pax_global_header00006660000000000000000000000064145625626350014530gustar00rootroot0000000000000052 comment=7088afe04dc83395af599b6c224b020a142e64d8 backends-1.3.0/000077500000000000000000000000001456256263500133035ustar00rootroot00000000000000backends-1.3.0/.editorconfig000066400000000000000000000006701456256263500157630ustar00rootroot00000000000000# .editorconfig -- settings for consistent coding style conventions # Copyright (C) 2017 Olaf Meeuwissen # # License: GPL-3.0+ # Your editor may need a plugin for this configuration to take effect. # See http://editorconfig.org/#download for details. root = true ; look no further [*] charset = utf-8 insert_final_newline = true trim_trailing_whitespace = true [backend/escl/*] indent_size = 4 indent_style = space backends-1.3.0/.gitignore000066400000000000000000000016341456256263500152770ustar00rootroot00000000000000# files generated by building the project *.a *.la *.lo *.o *.po~ .deps/ .libs/ .version # files generated during the configure step Makefile autom4te.cache/ config.cache libtool # files generated during autogen.sh step Makefile.in /INSTALL /aclocal.m4 /ar-lib /compile /config.guess /config.log /config.rpath /config.sub /config.status /configure /depcomp /install-sh /ltmain.sh /ltmain.sh.orig /m4/ !/m4/byteorder.m4 /missing /mkinstalldirs /test-driver # Gettext stuff /ABOUT-NLS /po/* !/po/*.po /po/en@*quot.po !/po/LINGUAS !/po/Makevars !/po/POTFILES.in # `make check` artifacts test-suite.log /testsuite/sanei/sanei_*_test /testsuite/sanei/sanei_*_test.log /testsuite/sanei/sanei_*_test.trs /testsuite/sanei/test_wire /testsuite/sanei/test_wire.log /testsuite/sanei/test_wire.out /testsuite/sanei/test_wire.trs /testsuite/tools/*.res # `make dist` artifacts /sane-backends-*.tar.gz # editor temp files *~ \#*\# backends-1.3.0/.gitlab-ci.yml000066400000000000000000000100161456256263500157350ustar00rootroot00000000000000# .gitlab-ci.yml -- to test some source code build scenarios # Copyright (C) 2016-2020 Olaf Meeuwissen # # License: GPL-3.0+ variables: REGISTRY_HUB: "registry.gitlab.com/sane-project/ci-envs" CONFIGURE_MINI: "--enable-silent-rules" CONFIGURE_FULL: "--with-usb --with-usb-record-replay --with-avahi --enable-pnm-backend --with-libcurl --with-poppler-glib" stages: - tarball - compile - snapshot - release # This job creates the source tarball that is the *sole* input to our # compile stage. The job is meant to be run on the stable release of # Debian GNU/Linux. make-dist: image: $REGISTRY_HUB:debian-bullseye-mini stage: tarball script: - git ls-files | xargs ./tools/style-check.sh - ./autogen.sh - ./tools/create-changelog.sh - ./configure - make dist artifacts: paths: - sane-backends-*.tar.gz expire_in: 1 day .compile_template: &compile_definition stage: compile script: - mkdir build - cd build - tar xzf ../sane-backends-*.tar.gz --strip-components=1 - (set -x; ./configure $CONFIGURE_OPTS) - eval "(set -x; make -j2 -k $MAKE_FLAGS)" debian-10-full: image: $REGISTRY_HUB:debian-buster-full variables: CONFIGURE_OPTS: "$CONFIGURE_MINI $CONFIGURE_FULL" MAKE_FLAGS: "CFLAGS=-Werror CXXFLAGS=-Werror" <<: *compile_definition debian-11-mini: image: $REGISTRY_HUB:debian-bullseye-mini variables: CONFIGURE_OPTS: "$CONFIGURE_MINI" MAKE_FLAGS: "CFLAGS=-Werror CXXFLAGS=-Werror" <<: *compile_definition # In addition to the regular compile check, the full Debian stable # environment is used to keep some of the HTML documentation that's # available from our website up-to-date. debian-11-full: image: $REGISTRY_HUB:debian-bullseye-full variables: CONFIGURE_OPTS: "$CONFIGURE_MINI $CONFIGURE_FULL" MAKE_FLAGS: "CFLAGS=-Werror CXXFLAGS=-Werror" <<: *compile_definition after_script: - make -C build/doc html-pages - rm -rf lists && mkdir lists && mv build/doc/*.html lists/ - cd build/doc && doxygen doxygen-sanei.conf && mv sanei-html ../../doc artifacts: paths: - sane-backends-*.tar.gz - lists - doc/sanei-html expire_in: 1 day fedora-36-clang: image: $REGISTRY_HUB:fedora-36-clang variables: CONFIGURE_OPTS: "$CONFIGURE_MINI $CONFIGURE_FULL" MAKE_FLAGS: "CFLAGS=-Werror CXXFLAGS=-Werror" <<: *compile_definition alpine-3.15-musl: image: $REGISTRY_HUB:alpine-3.15-musl variables: CONFIGURE_OPTS: "$CONFIGURE_MINI $CONFIGURE_FULL" MAKE_FLAGS: "CFLAGS='-Werror -Wno-pedantic' CXXFLAGS=-Werror" <<: *compile_definition ubuntu-22.04-lts: image: $REGISTRY_HUB:ubuntu-jammy-dist variables: CONFIGURE_OPTS: "$CONFIGURE_MINI $CONFIGURE_FULL" MAKE_FLAGS: "CFLAGS=-Werror CXXFLAGS=-Werror" <<: *compile_definition # This snapshot stage job makes sure that the source tarball has all # it needs to rebuild itself, install everything built and cleans up # without leaving any droppings behind when uninstalling. The build # result will be available as a snapshot for a limited time period. # People that prefer a source tarball to work with should use this # snapshot. # Some HTML documentation derived from this project's source is also # uploaded for use by our website so it uses the latest information. # It gets these artifacts from the full compile job on Debian stable, # hence the dependency. make-distcheck: image: $REGISTRY_HUB:debian-bullseye-full stage: snapshot dependencies: - debian-11-full script: - tar xzf sane-backends-*.tar.gz --strip-components=1 - rm sane-backends-*.tar.gz - ./configure - make distcheck artifacts: paths: - sane-backends-*.tar.gz - lists - doc/sanei-html expire_in: 90 days # For release tags only, this manual job handles putting all of the # releasables on the Project Releases page. See the script for more # details. upload: image: alpine stage: release before_script: - apk --no-cache add curl git jq script: - ./tools/create-release.sh only: - tags when: manual variables: GIT_DEPTH: "3" allow_failure: false backends-1.3.0/AUTHORS000066400000000000000000000233761456256263500143660ustar00rootroot00000000000000Backends: abaton: David Huggins-Daines agfafocus: Karl Anders Øygard apple: Milon Firikis artec: Chris Pinkham artec_eplus48u:Sergey Vlasov, Andreas Nowack, David Stevenson, and Michael Herder as6e: Eugene S. Weiss avision: Meino Christian Cramer, René Rebe bh: Tom Martone canon: Helmut Koeberle, Manuel Panea, and Markus Mertinat Mitsuru Okaniwa, Ulrich Deiters canon630u: Nathan Rutman canon_dr: m. allan noah (*) canon_lide70: Juergen Ernst, pimvantend (*) canon_pp: Matthew Duggan, Simon Krix cardscan: m. allan noah (*) coolscan: Didier Carlier, Andreas Rick coolscan2: Andras Major coolscan3: Alessandro Zummo dc25: Peter Fales dc210: Brian J. Murrell dc240: Peter Fales dell1600n_net: Jon Chambers dll: David Mosberger dmc: Dianne Skoll epjitsu: m. allan noah (*) epson: Karl Heinz Kremer epson2: Alessandro Zummo, Wolfram Sang (*) epsonds: Alessandro Zummo escl: Touboul Nathane, Thierry HUCHARD (*) fujitsu: Randolph Bentson, Frederik Ramm, Oliver Schirrmeister, m. allan noah (*) genesys: Henning Geinitz, Gerhard Jaeger (*), Stéphane Voltz, Pierre Willenbrock, Povilas Kanapickas (*) gphoto2: Peter Fales gt68xx: Sergey Vlasov, Andreas Nowack, David Stevenson, and Henning Geinitz hp: Peter Kirchgessner Geoffrey Dairiki hp3500: Troy Rollo hp3900: Jonathan Bravo Lopez hp4200: Adrian Perez Jorge, Andrew John Lewis, Arnar Mar Hrafnkelsson, Frank Zago, Henning Geinitz hp5400: Martijn van Oosterhout, Thomas Soumarmon hp5590: Ilia Sotnikov (*) hpljm1005: Philippe Rétornaz hpsj5s: Max Vorobiev hs2p: Jeremy Johnson ibm: M.F., Henning Geinitz kodak: m. allan noah (*) kodakaio: Paul Newall (*) kvs1025: Tao Zhang kvs20xx: Panasonic Russia Ltd kvs40xx: Panasonic Russia Ltd leo: Frank Zago lexmark: Fred Odendaal, Stéphane Voltz lexmark_x2600: Benoit Juin ma1509: Henning Geinitz magicolor: Reinhold Kainhofer(based on epson2 backend) matsushita: Frank Zago microtek: Matthew Marjanovic microtek2: Bernd Schroeder, Karsten Festag mustek: Andreas Bolsch, David Mosberger, Andreas Czechanowski, Henning Geinitz, and James Perry. mustek_pp: Jochen Eisinger, Eddy De Greef mustek_usb: Tom Wang and Henning Geinitz mustek_usb2: Roy Zhou, Jack Xu, Vinci Cen, and Henning Geinitz nec: Kazuya Fukuda net: Andreas Beck, David Mosberger, Julien Blache niash: Ullrich Sigwanz, Bertrik Sikken p5: Stéphane Voltz pie: Simon Munton pieusb: Jan Vleeshouwers, Michael Rickmann, Klaus Kämpf (*) pint: Gordon Matzigkeit pixma: Wittawat Yamwong Nicolas Martin Louis Lagendijk (*) Dennis Lou Rolf Bensch Povilas Kanapickas (*) plustek: Gerhard Jaeger (*) plustek_pp: Rick Bronson (former pp driver-code), Gerhard Jaeger (*) pnm: Andreas Beck, Gordon Matzigkeit, David Mosberger, Michael Herder, and Henning Geinitz qcam: Scott Laird (original driver), David Mosberger (SANE backend), despeckling filter by Patrick Reynolds, B&W fixes by Andrew Kuchling ricoh: Feico W. Dillema ricoh2: Stanislav Yuzvinsky (*) rts8891: Stéphane Voltz s9036: Ingo Schneider sceptre: Frank Zago sharp: Kazuya Fukuda and Abel Deuring sm3600: Marian Eichholz and Glenn Ramsey sm3840: Earle F. Philhower III snapscan: Kevin Charter, Franck Schneider, Michel Roelofs, Sebastien 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 teco3: Frank Zago test: Henning Geinitz u12: Gerhard Jaeger (*) umax: Oliver Rauch and Michael K. Johnson umax1220u: Marcio L. Teixeira, Patrick Lessard umax_pp: Stéphane Voltz v4l: Juergen G. Schimmer, and Henning Geinitz xerox_mfp: Alex Belkin (*) Frontends: jscanimage: Jeff Freedman and Guido Muesch saned: Andreas Beck, David Mosberger, Julien Blache scanimage: Andreas Beck, David Mosberger, Gordon Matzigkeit, m. allan noah (*), Julien Blache, Stéphane Voltz tstbackend: Frank Zago Sanei internal code: Adrian Perez Jorge, Andreas Beck, Andreas Czechanowski, Christian Bucher, David Mosberger-Tang, Frank Zago, Henning Geinitz, Jeff Freedman, Jochen Eisinger, Marcio Teixeira, Yuri Dario, Gerhard Jaeger (*), m. allan noah (*) Java API: Jeff Freedman Miscellaneous coding: Julien Blache, Mattias Ellert, Petter Reinholdtsen, Chris Bagwell, Olaf Meeuwissen (*) Translators: Giuseppe Sacco (*) (*) Maintainer with Git repository write access Please also read the file PROJECTS for projects that are planned or not yet included into the SANE distribution. Email addresses: Abel Deuring Alessandro Zummo Alex Belkin Andras Major Andreas Beck Andreas Bolsch Andreas Czechanowski Andreas Nowack Andreas Rick Andrew Kuchling Benoit Juin Bernd Schroeder Bertrik Sikken Brian J. Murrell Chris Bagwell Chris Pinkham Christian Bucher David Etherton David Huggins-Daines David Mosberger David Stevenson Dianne Skoll Didier Carlier Earle F. Philhower III Eddy De Greef Eugene S. Weiss Feico W. Dillema Franck Schneider Frank Zago Fred Odendaal Frederik Ramm Gerard Klaver Gerhard Jaeger Giuseppe Sacco Glenn Ramsey Gordon Matzigkeit Guido Muesch Helmut Koeberle Henning Geinitz Henrik Johansson Ilia Sotnikov Ingo Schneider Ingo Wilken James Perry Jeff Freedman Jochen Eisinger Jon Chambers Jonathan Bravo Lopez Juergen G. Schimmer Julien Blache Jan Vleeshouwers Karl Anders Øygard Karl Heinz Kremer Karsten Festag Kazuhiro Sasayama Kazuya Fukuda Kevin Charter Klaus Kämpf Louis Lagendijk M.F. Manuel Panea Marcio Teixeira Marian Eichholz Markus Mertinat Martijn van Oosterhout Matthew Duggan Matthew Marjanovic Mattias Ellert Max Vorobiev Meino Christian Cramer Michael Herder Michael K. Johnson Michael Rickmann Michel Roelofs Milon Firikis Mitsuru Okaniwa Nathan Rutman Nicolas Martin Olaf Meeuwissen Oliver Rauch Oliver Schirrmeister Oliver Schwartz Patrick Lessard Patrick Reynolds Paul Newall Peter Fales Peter Kirchgessner Petter Reinholdtsen Pierre Willenbrock Randolph Bentson Reinhold Kainhofer Rene Rebe Roger Wolff Rolf Bensch Roy Zhou Sebastien Sable Sergey Vlasov Simon Krix Simon Munton Stéphane Voltz Thierry HUCHARD Thomas Soumarmon Tom Martone Tom Wang Touboul Nathane Tristan Tarrant Troy Rollo Ullrich Sigwanz Ulrich Deiters Wittawat Yamwong Wolfram Sang m. allan noah backends-1.3.0/COPYING000066400000000000000000000432541456256263500143460ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. backends-1.3.0/ChangeLog000066400000000000000000000003501456256263500150530ustar00rootroot00000000000000This file is automatically created at build time using the git commit messages and included in our source tarballs. When you see this text you're using a `git checkout` and should use the `git log` command to see what has changed. backends-1.3.0/ChangeLogs/000077500000000000000000000000001456256263500153155ustar00rootroot00000000000000backends-1.3.0/ChangeLogs/ChangeLog-1.0.0000066400000000000000000004355041456256263500175340ustar00rootroot000000000000001998-11-21 David Mosberger-Tang * Version 1.0 released * From Brian J. Murrell: * backend/dll.conf: Mention dc210. * backend/cderror.h: New file. * backend/jinclude.h: Ditto. * backend/djpeg.c: Ditto. * backend/dc210.desc: Ditto. * backend/dc210.h: Ditto. * backend/dc210.c: Ditto. * backend/cdjpeg.h: Ditto. * doc/Makefile.in (SECT5): Mention sane-abaton.5. * doc/sane-abaton.man: New file. * backend/abaton.c: Ditto. (wait_ready): Explicitly cost tv_sec member to (long) in order to print it (avoids warning on platforms where tv_sec is not a long). * backend/abaton.h: Ditto. * backend/abaton.conf: Ditto. * backend/abaton.desc: Ditto. * backend/dll.conf: Mention abaton. * Apollo/DomainOS fixes by Paul Walker: * sanei/sanei_DomainOS.h: Add this missing file. * sanei/sanei_config2.c: Include to get u_char defined if necessary. * sanei/sanei_DomainOS.c (upper_string): Delete. (do_help): Ditto. * include/sane/config.h.in: Add #undef HAVE_SYS_TIME_H. * lib/usleep.c [HAVE_SYS_TIME_H]: Include only if we have it. * configure.in (CPPFLAGS): Check for sys/time.h. * frontend/gtkglue.c: Move include of in front of include of . * config.sub: Apply patch to recognize -sys5.3. * backend/microtek.c: Applied Matto's latest patch to upgrade backend from v0.9 to v0.10. 1998-11-04 David Mosberger-Tang * backend/microtek2.desc: Mention ScanMaker X6 and Phantom 636. * backend/microtek2.c (check_inquiry): Mention Phantom 636 in model string. 1998-11-03 David Mosberger-Tang * backend/microtek2.c (check_inquiry): Added patch by Sebastian Erdmann to recognize ScanMaker X6. 1998-11-02 David Mosberger-Tang * backend/artec.c: Print size_t as recommended in backend/GUIDE. 1998-11-02 Chris Pinkham * backend/artec.c: changes to correct bugs when using AT12. (sense_handler): New function (by Dick Bruijn). (wait_ready): Ditto (by Dick Bruijn). (abort_scan): Ditto (by Dick Bruijn). Support added to read capability data from scanner if the model. Supports this command (by Dick Bruijn). Added call to sanei_scsi_close() if detected scanner is not a Artec/Ultima model (by Francois Ouellet). Added Negative option. Added Halftone Pattern option. Added Filter Type option for mono scans. Added Quality Calibration option. Moved test_unit_ready command till AFTER we check for ULTIMA scanner. 1998-11-02 David Mosberger-Tang * config.guess: Upgrade to libtool v1.2. * config.sub: Ditto. * ltconfig: Ditto. * ltmain.sh: Ditto. 1998-10-29 David Mosberger-Tang * backend/mustek.c (encode_resolution): If MUSTEK_FLAG_DOUBLE_RES is set, use resolution encoding reported by (attach): Set MUSTEK_FLAG_DOUBLE_RES for MSF-06000CZ. Do other models need this, too? (calibration): Declare NUM as size_t, not int. #ifdef out this unused routine. (reader_process): Print size_t values as %lu with explicit cast to u_long. * backend/mustek.h (MUSTEK_FLAG_DOUBLE_RES): New flag. * backend/mustek.c (init_options): Initialize resolution with 18 dpi, not 100dpi so that "scanimage" without options gives a preview-quality image that doesn't take much space or time. * backend/mustek.c: Updated with Andreas Bolsch's version (adds support for SE models). * backend/mustek.h: Ditto. * backend/mustek.spec: Ditto. * backend/pnm.c (sane_read): Replace C++ comment with C comment. * backend/microtek2.c: Updated with Bernd's v0.5. * backend/microtek2.h: Ditto. * backend/microtek2.conf: Ditto. * backend/umax.c: Updated with Oliver's latest version. * backend/umax.h: Ditto. * backend/umax-scsidef.h: Ditto. * backend/umax-scanner.h: Ditto. * backend/umax-uc630.h: Ditto. * backend/umax-uc840.h: Ditto. * backend/umax-ug630.h: Ditto. * backend/umax-ug80.h: Ditto. * backend/umax-uc1200s.h: Ditto. * backend/umax-uc1200se.h: Ditto. * backend/umax-uc1260.h: Ditto. * backend/umax.conf: Ditto. * include/sane/saneopts.h: Ditto. * doc/sane-umax-doc.html: Ditto. * doc/sane-umax-doc.tex: Ditto. * doc/sane-umax.man: Ditto. 1998-10-28 David Mosberger-Tang * doc/sane.tex (subsection{sane_control_option}): Clarify that parameter "v" is ignored for SET_AUTO calls. * frontend/saned.c (init): Initialize w->version. * backend/net.c (connect_dev): Accept version code 2 and set peer's version code in dev->wire.version. * include/sane/sanei_wire.h: New member "version". * sanei/sanei_net.c (sanei_w_control_option_req): Encode option value only if peer_version is < 3 or if action is not SANE_ACTION_SET_AUTO. * include/sane/sanei_net.h (SANEI_NET_PROTOCOL_VERSION): Up protocol version to version 3. * sanei/sanei_net.c (sanei_w_control_option_req): Transcode value_type, value_size, and value only if req->action != SANE_ACTION_SET_AUTO. Reported by Petter Reinholdtsen. Patches by Petter Reinholdtsen : * backend/dmc.c (attach_one): Don't use C++-style comments. * backend/dc25.h (DEFAULT_TTY_BAUD): New macro. Move SPEEDS macro to dc25.c. * backend/dc25.c: Initialize tty_baud to DEFAULT_TTY_BAUD. Use B57600 and B115200 only if defined. * doc/saned.man: Mention that hostname matching no longer is case significant. * configure.in (lib/Makefile.in,lib/strcasecmp.c,frontend/saned.c): Use strcasecmp() instead of strcmp to compare DNS hostnames. Case is irrelevant when using DNS. * backend/dll.c: Add support for HP-UX 10.xx style shared libs. * configure.in include/sane/config.h.in backends/dll.c: Use dhl_load() family for DLL support on HP/UX. * backend/snapscan.c (sane_snapscan_open): Don't cast lvalue (illegal according to ANSI C, I think). * backend/apple.c (init_options): Add explicit "break" after "default:" to make HP-UX compiler happy (?). (sane_read): Make stuff after #endif a comment. * backend/agfafocus.c: New file (by Karl Anders Oygard ). * backend/agfafocus.h: Ditto. * doc/sane-agfafocus.man: Ditto. * doc/sane-net.man: Fix typo: network service is called "sane", not "saned" (by Andreas Dilger ). * backend/hp-scl.c (sanei_hp_scsi_pipeout): Use SIGPWR only if its defined (by Peter Kirchgessner). * frontend/Makefile.in (INCLUDES): Mention @GTK_CFLAGS@ as part of INCLUDES macro instead of CFLAGS. Despite its name, @GTK_CFLAGS@ is guaranteed to contain CPP include directives only. (Reported by Kevin Dalley .) 1998-10-27 David Mosberger-Tang * sanei/sanei_scsi.c (sanei_scsi_open): Return SANE_STATUS_ACCESS_DENIED if open() fails with errno==EACCES. * backend/ricoh-scsi.c: #ifdef out unused code and print values of type size_t as prescribed in the file backend/GUIDE. * backend/dll.conf: Mention Ricoh backend. * backend/ricoh.c: New file (by Ricoh Dillema). * backend/ricoh.h: Ditto.. * backend/ricoh-scsi.c: Ditto.. * backend/microtek.c: Apply Aug 7 patch by Matto Marjanovic. * backend/dc25.c: Apply patch by Peter Fales (v1.1). * backend/dc25.desc: Ditto. * doc/sane-dc25.man: Ditto. * backend/pnm.c: Applied patch by Goran Thyni that allows scanning parts of a PNM image. * backend/GUIDE: Add Matto's comment on .desc files. * backend/hp.c: New file from Peter Kirch (formerly known as xhp). * backend/hp.h: Ditto. * backend/hp-accessor.h: Ditto. * backend/hp-device.h: Ditto. * backend/hp-handle.h: Ditto. * backend/hp-option.h: Ditto. * backend/hp-scl.h: Ditto. * backend/hp-scsi.h: Ditto. * backend/hp-accessor.c: Ditto. * backend/hp-device.c: Ditto. * backend/hp-handle.c: Ditto. * backend/hp-hpmem.c: Ditto. * backend/hp-option.c: Ditto. * backend/hp-scl.c: Ditto. * backend/Makefile.in (libsane-hp.la): Make libsane-hp.la dependent upon hp-accessor.lo, hp-device.lo, hp-handle.lo, hp-hpmem.lo, hp-option.lo, and hp-scl.lo. 1998-10-22 David Mosberger-Tang * doc/sane-hp.man (Model): Mention HP6200 as per report by Tom Martone . 1998-07-30 David Mosberger-Tang * include/sane/config.h.in: Apply Cory Kempf's FreeBSD CAM support patch. * sanei/sanei_scsi.c: Ditto. * tools/find-scanner.c: Ditto. * configure.in: Ditto. 1998-07-27 David Mosberger-Tang * Version 0.74 released. * configure.in (CPPFLAGS): Save old CPPFLAGS in saved_CPPFLAGS and then set CPPFLAGS (not saved_CPPFLAGS!) to include GTK_CFLAGS. This should get GIMP detected again. * frontend/xscanimage.c (init): Add missing newline. * backend/net.c (sane_init): Look for service "sane", not "saned". * configure.in (V_MINOR): Make it 74. * tools/sane-desc.el: Hack a bit so sane-backends.html can be generated some place other than the working directory (which may not be writable). * tools/sane-desc.el: New file by Matto. * doc/Makefile.in (sane-backends-html): New rule to generate sane-backends.html. * backend/apple.desc: List sane-apple man page. * backend/umax.c: Updated with Oliver's latest version (with Irix 4MB workaround). * backend/microtek.c: Updated with Matto's v0.9. * backend/microtek.h: Ditto. * backend/microtek.conf: Ditto. * backend/microtek.desc: Ditto. * doc/sane-microtek.man: Ditto. * backend/apple.h: Updated with Milon's v0.3. * backend/apple.c: Ditto. * doc/sane-apple.man: New file (contributed by Milon Firirkis). 1998-07-23 David Mosberger-Tang * backend/canon-scsi.c (reserve_unit): Don't compile unused reserve_unit(). * sanei/sanei_scsi.c: Apply Yuri's patch to add sanei_scsi_find_devices() support to OS/2. * sanei/sanei_pio.c: Include instead of just so we pick up #define's for inb and out. * include/sane/config.h.in: Mention HAVE_OS2_H and HAVE_SYS_TYPES_H. * configure.in (AC_CHECK_HEADERS): Mention sys/hw.h and sys/types.h. (AC_CHECK_LIB): Check for syslog. (AC_CHECK_FUNCS): Check for _portaccess. (CPPFLAGS): Don't disable qcam backend if we have _portaccess. These patches contributed by Yuri for the benefit of OS/2. * backend/qcam.c (qc_unlock) [!F_SETLK]: Fall back to convention UNIX-style locking using O_EXCL (not ideal, but better than nothing at all). (qc_lock_wait): Ditto. * sanei/sanei_scsi.c (KillDomainServer): Add missing paren. * lib/usleep.c [HAVE_SYS_SELECT_H]: Include . * include/sane/sanei_backend.h: Define i/o-port access compatibility macros (by Yuri Dario). * backend/Makefile.in (CONFIGS): Mention dummy saned.conf (this really ought to go in frontend/Makefile, but I'm too lazy to add a configuration-install rule there...). * backend/coolscan.c: Update with Didier's latest version. (send_one_LUT): Merge in alloca() fix from 1998-04-04. 1998-07-22 David Mosberger-Tang * backend/dll.conf: Mention artec backend. * backend/mustek.c (sane_start): Remove duplicate status test. 1998-07-21 David Mosberger-Tang * frontend/saned.c (main): Look for service name "sane", not "saned". 1998-07-20 David Mosberger-Tang * backend/epson.c (epson_cmd): Add patch by Holger Frahm to support GT9500. * backend/microtek.c: Update with latest version (v0.8). * backend/artec.c (PREFER_PIXEL_MODE): Apply Chris Pinkham's 6/4/98 patch to add preliminary support for AT6 and AT12 scanner models. * backend/epson.c: Apply Christian Bucher's patch. * backend/dc25.h: New file (by Peter Fales ) * backend/dc25.c: Ditto. * backend/dc25.conf: Ditto. * doc/sane-dc25.man: Ditto. * backend/microtek2.c: Replace dynamically sized array with alloca() to be ANSI C compliant. * backend/epson.c: Replace zero-sized byte arrays with 1 byte arrays to be ANSI C compliant. * doc/sane-hp.man: Mention ScanJet 3P (reported by Patrick Debois ). * backend/umax.c: Updated with Oliver's 5/7/98 patch. 1998-07-20 Chris Pinkham * backend/artec.c: Added code to default to using millimeters instead of pixels for measurements, can revert to pixels by defining PREFER_PIXEL_MODE at compilation time. Added code to cause sane_read to block until data available to return, also buffers as much data as possible before returning. Added code to cause sane_read to read as much data from scanner as possible for each call, previously would read 1 line max from scanner. Added code to attempt to detect scanner capabilities from scanner if scanner supports this command, it not program has defaults coded in for certain scanner models. Separated horizontal and vertical resolution settings with ability to bind them together. This still has a bug, so the option is is disabled by default until I can get it working correctly. Bug-fixes to get backend working properly with xscanimage. Added preview mode option. Added threshold option. * backend/artec.h: Added options codes to go along with options added in artec.c file. 1998-07-20 David Mosberger-Tang * backend/epson.c (scsi_write): Apply patch by Thomas Bogendoerfer to get Epson backend to work for SCSI scanners again. 1998-06-05 David Mosberger-Tang * backend/mustek.c (attach): Move dev_wait_ready() after the check for a Mustek scanner. This is to make sure we don't issue any command other than INQUIRY to SCSI devices that are not known to be Mustek scanners. 1998-05-22 David Mosberger-Tang * backend/dmc.c: Updated with Dianne Skoll's latest patch. * sanei/sanei_scsi.c (sanei_scsi_find_devices): Declare missing findtype arg for dummy sanei_scsi_find_devices(). 1998-05-16 David Mosberger-Tang * backend/hp.c (attach): Return right after INQUIRY if the device doesn't look like an HP scanner. 1998-05-15 David Mosberger-Tang * Version 0.73 released. * backend/mustek.c (attach): Initialize *devp to 0. * configure.in: Use AM_PATH_GTK to figure out if/how to compile with gtk libs. * aclocal.m4: Include gtk.m4 from gtk-1.0.1 distribution. * backend/Makefile.in (install): Change install rule so there is guaranteed to be a (symlink) of the form libsane-$(BACKEND).so.$(V_MAJOR) pointing to the right version of a dll. * backend/dll.c (load): Get rid of .la parsing non-sense. Instead, always attempt to open libsane-$(BACKEND).so.$(V_MAJOR). * backend/mustek.c (sane_init): Make sure empty lines really are ignored. 1998-05-13 David Mosberger-Tang * backend/microtek2.conf: Add line "scsi * * Scanner". * backend/microtek.conf: Ditto. * sanei/sanei_scsi.c (get_devicename): New function. * sanei/sanei_config2.c: New file. * sanei/Makefile.in (LIBSANEI_OBJS): Mention sanei_config2. (LIBSANEI_LTOBJS): Ditto. * sanei/sanei_scsi.c (sanei_scsi_find_devices): New function (comes in two flavors: one for Linux, one for the remaining platforms). * include/sane/sanei_scsi.h: New function. * doc/sane-dll.man: Fix to make it work with man2html (use \- instead of -). * doc/sane-dmc.man: Ditto. * doc/sane-epson.man: Ditto. * doc/sane-hp.man: Ditto. * doc/sane-microtek.man: Ditto. * doc/sane-microtek2.man: Ditto. * doc/sane-mustek.man: Ditto. * doc/sane-pint.man: Ditto. * doc/sane-scsi.man: Ditto. * doc/sane-umax.man: Ditto. * doc/saned.man: Ditto. * doc/scanimage.man: Ditto. * doc/xscanimage.man: Ditto. * doc/Makefile.in (MAN2HTML): New macro. (html-man): New rule to generate HTML version of man-pages. * configure.in (V_MINOR): Up to 73. * backend/umax.conf: Add lines "scsi UMAX" and "scsi LinoHell Office". * backend/microtek2.h: New file (by Bernd Schroeder ). * backend/microtek2.c: Ditto. * backend/microtek2.conf: Ditto. * backend/dll.conf: Mention "microtek2". * backend/apple.conf: Add line "scsi APPLE". * backend/mustek.conf: Add lines for "scsi MUSTEK" and "scsi SCANNER". * backend/apple.c (attach_one): New function. (sane_init): Use sanei_config_attach_matching_devices(). * backend/artec.c: Like for apple.c. * backend/canon.c: Ditto. * backend/coolscan.c: Ditto. * backend/dmc.c: Ditto. * backend/epson.c: Ditto. * backend/hp.c: Ditto. * backend/microtek.c: Ditto. * backend/mustek.c: Ditto. * backend/s9036.c: Ditto. * backend/snapscan.c: Ditto. * backend/tamarack.c: Ditto. 1998-05-11 David Mosberger-Tang * backend/snapscan.c (sane_snapscan_get_parameters): Always set depth of 8. * configure.in: Check for gtk_tooltips_set_tips _after_ gtk libraries have been located. (Patch by Jake E. Hamby ). 1998-05-09 David Mosberger-Tang * sanei/Makefile.in (.c.o): Patch by Kaz Sasayama : invoke $(COMPILE) if libtool doesn't give us a regular .o file. * lib/Makefile.in (.c.o): Ditto. * frontend/preview.c (preview_update): Call update_selection() so selection is updated when scanwindow geometry changes. The patch from 1998/4/2 never worked satisfactorily (and indeed was never enabled in any of the SANE releases). 1998-05-06 David Mosberger-Tang * backend/mustek.c (sane_init): Use sanei_config_skip_whitespace() to skip whitespace. 1998-05-05 David Mosberger-Tang * frontend/xscanimage.c (init): Allow -g as an option. * sanei/sanei_config.c (sanei_config_get_string): New function. (sanei_config_skip_whitespace): Ditto. * backend/umax.c: Updated with Oliver's latest patch. * Makefile.in (INSTALLED_INCLUDES): Mention sanei_readproc.h. * backend/microtek.h: Updated with Matto's 0.7 patch. * doc/sane-microtek.man: Ditto. * backend/microtek.c: Ditto. Added precalibration code: tries to intelligently have scanner run a calibration only once when necessary --- this shaves a solid six seconds off every scan! Added model code/vendor name for ScanMaker 600ZS. Added model codes/vendor name for Agfa Arcus II, StudioScan, StudioScan II. Patched up 3-pass scanning (forgot to switch color planes). Corrected IIHR to 600dpi base resolution. Check so that 0x0 regions are recognized as invalid. Fixed subtle buffering bug in color scans. Fixed-up active/inactive dependencies among options. Make sure shadow <= midtone <= highlight. Let shadow/midtone/highlight be active during LineArt and Halftone. "Halftoning" option moved into Scan Mode Group (from Enhancement). Removed some illegal "TEST_UNIT_READY" commands (during scan). Removed "MODE_SENSE_1", since it didn't quite work anyway. 1998-05-03 David Mosberger-Tang * frontend/xscanimage.c (scan_preview): Don't register preview_window_destroyed as a delete_event handler. 1998-05-02 David Mosberger-Tang * backend/Makefile.in: Remove unused LIBLIB macro. 1998-04-30 David Mosberger-Tang Patches by Matto Marjanovic: * frontend/gtkglue.c (gsg_close_dialog_callback): Remove client_data pointer arg. * frontend/xscanimage.c (scan_win_delete): Remove GdkEvent arg. (preview_window_destroyed): Ditto. (input_available): Include bad_depth handling code only if HAVE_LIBGIMP_GIMP_H is defined. (scan_dialog): Remove client_data pointer arg. 1998-04-29 David Mosberger-Tang * frontend/gtkglue.c (gsg_get_filename): Use gtk_events_pending() instead of gdk_events_pending(). * frontend/preview.c (input_available): Ditto. * frontend/xscanimage.c (quit_xscan): Ditto. (input_available): Ditto. 1998-04-08 David Mosberger-Tang * sanei/sanei_pio.c (sanei_pio_open): Fix prototype. 1998-04-07 David Mosberger-Tang * Version 0.72 released. * backend/epson.c (sane_init): Fix up config file reading to be more like that of other backends. (PATH_MAX): Use PATH_MAX instead of FILENAME_MAX. * backend/canon.c (sane_init): Read & process canon.conf (patch by Yuri Dario ). 1998-04-06 David Mosberger-Tang * sanei/sanei_init_debug.c (sanei_init_debug): Move up #endif so NULL-test works for OS/2, too. * frontend/saned.c: Include . * backend/snapscan.c: Don't include non-ANSI . (sane_snapscan_start): Use INT_MAX instead of MAXINT. (PATH_MAX): Define PATH_MAX as 1024 if not defined otherwise. 1998-04-05 David Mosberger-Tang * backend/umax.c: Updated with Oliver's latest version (0.72pre-a). 1998-04-04 David Mosberger-Tang * backend/coolscan.c: Do lalloca.h spiel instead of including alloca.h. * configure.in: Look for glib/include directory in /usr/local/lib and /usr/lib. Add $C_SWITCH_X_SITE to CPPFLAGS, not DEFS. * doc/sane-mustek.man: Mention MFS-1200SP v1.07 as working as reported by ehramm@dk3uz.hh.provi.de (Edmund H. Ramm). 1998-04-03 David Mosberger-Tang * backend/canon-scsi.c (request_sense): ifdef out to quiet down gcc. * backend/artec.c (read_data): Print size_t as (u_long) to make it work right and without compiler warning both on 32 and 64 bit platforms. (artec_get_status): Ditto. (init_and_start_scan): Ditto. (sane_start): Ditto. (sane_read): Ditto. (artec_buffer_line_offset): Declare LEN as size_t. * backend/epson.c (sane_read): Ditto. * sanei/sanei_pio.c (pio_wait): Initialize STAT with 0 to quiet down gcc. * backend/Makefile.in (libsane-epson.la): Mention sanei_pio.lo. (libsane-dll.la libsane.la): Ditto. * sanei/Makefile.in (LIBSANEI_OBJS): Mention sanei_pio.o. (LIBSANEI_LTOBJS): Mention sanei_pio.lo. * frontend/xcam.c (input_available): Undo braindamage regarding break out of scan loop (the SANE docs _do_ specify that in blocking mode, *len==0 implies end of scan). * frontend/xscanimage.c: Ditto. * doc/sane-dmc.man: New file by Dianne Skoll. * sanei/sanei_DomainOS.c: New file by Paul Walker. * backend/snapscan.c (DL_INFO, DL_MAJOR_ERROR): Up error code to 1 to avoid printing anything unless the user specifically asked for this. * include/sane/sanei_debug.h (DBG_LEVEL): Define debug level variable as macro DBG_LEVEL. * README.solaris (NOTE): Emphasize that generic scsi driver needs to be installed before running configure. * backend/s9036.c: Don't depend on GNU C dynamically sized arrays. * backend/coolscan.c (send_one_LUT): Use alloca() instead of depending on GNU C dynamically sized arrays. Include . * frontend/xscanimage.c (input_available): Break out of the loop only if LEN==0 _and_ we have a non-negative input tag. * frontend/xcam.c (input_available): Ditto. * backend/microtek.c (parse_inquiry): Handle for ScanMaker 35t+. (id_microtek): Ditto. 1998-04-02 David Mosberger-Tang * configure.in (CPPFLAGS): Check for libXi. * frontend/gtkglue.c (panel_destroy): Clear dialog elements after destroying the panel. Patches by Christian Bucher : * include/sane/sanei_pio.h: New file. * sanei/sanei_pio.c: Ditto. * backend/epson.c, backend/epson.h, backend/epson.conf: Updated with Christian's version adds support for parallel port interface. * sanei/sanei_scsi.c (sanei_scsi_req_wait): Patch by Matto to let sense-handler decide whether a non-zero sense_buffer[0] really should be considered an error (needed for Microtek backend). * doc/sane.tex (\subsubsection{Option Value Unit}): Document new unit SANE_UNIT_MICROSECOND. Patches by Oliver Rauch: * frontend/gtkglue.c (unit_string): Handle SANE_UNIT_MICROSECOND. * frontend/scanimage.c (print_unit): Ditto. (parse_scalar): Ditto. * include/sane/sane.h: Add SANE_UNIT_MICROSECOND. * Upgrade to Kevin's latest SnapScan backend (0.4). * doc/sane-hp.man: Mention ScanJet IIp C1790A as working (reported by Ronald.Vogelaar@nl.origin-it.com). * frontend/xscanimage.c (init): Issue an error message when we see option -g as this is most likely due to someone invoking xscanimage through GIMP when GIMP support is missing. (scan_done): Add sanity check. (scan_start): Ditto. * Upgraded to Matto's latest Microtek backend (0.6). * Upgraded to Oliver's latest UMAX backend (0.71h). * frontend/preview.c (draw_selection): Fix so that selection is updated when sliders are moved. Patch by Mikko Tyo"la"ja"rvi. * backend/snapscan.c (DL_INFO): Increase from 0 to 1. (DL_MAJOR_ERROR): Ditto. This avoids printing error messages unless the user specifically requests them. * frontend/gtkglue.c: Include . * include/sane/config.h.in: Ignore HAVE_USLEEP under Apollo Domain. Patches by Paul Walker: * sanei/sanei_scsi.c: New Domain OS code. * tools/find-scanner.c: Include . * lib/usleep.c (usleep): Use time_$wait() to avoid broken usleep() implementation in Domain Sys5.3 environment. * config.sub: Translate sys5.3 into sysv3 for the benefit of Apollo Domain/OS. * sanei/sanei_init_debug.c (sanei_init_debug): Use DosScanEnv() instead of getenv() under OS/2. (Patch by Yuri Dario ) 1998-03-02 David Mosberger-Tang * frontend/xcam.c (main): Remove obsolete call to gdk_set_debug_level (0). * backend/mustek.c (attach): Print debug message when discovering unknown model. * doc/sane-scsi.man: Mention Adaptec 1505. * tools/find-scanner.c (main): List FreeBSD specific device names. * tools/Makefile.in (LIBS): Define. (find-scanner): Mention $(LIBS). * backend/epson.conf: New file. Patches by Yuri Dario : * backend/epson.c (sane_init): Support config file. * sanei/sanei_ab306.c (outb): Add missing parens. * backend/snapscan.c: Don't include * backend/net.c: Include netdb.h after in.h to appease OS/2. Include . * sane-0.71.spec (%files): Mention sane-dmc.5. * doc/Makefile.in (SECT5): Ditto. * backend/dmc.c, backend/dmc.h: Update with Feb 26 patch from Dianne Skoll. * doc/dmc.man: New file by Dianne Skoll. 1998-02-25 David Mosberger-Tang * backend/dmc.c (DMCSetMode): Add missing field name (patch by Martin Huber). * tools/find-scanner.c (main) [__sun]: Add missing command (patch by Martin Huber. * sanei/sanei_scsi.c (sanei_scsi_cmd): Fix typo: fd_Info->fd_info (patch by Jeff Freedman). * configure.os2: Update with patch by Jeff Freedman. * backend/snapscan.c (sane_snapscan_start): Replace non-standard MAXINT by INT_MAX from . (PATH_MAX): Define PATH_MAX as 1024 if not defined by headers. 1998-02-23 David Mosberger-Tang * Version 0.71 released. * backend/dmc.c (DMCAttach): Close scsi fd before returning. * backend/microtek.c: Default to no_dump. (sane_init): Change "nodump" into "dump" option. * doc/sane-microtek.man: Document this change. * backend/snapscan.c: Include . (sane_snapscan_init): Use sanei_config_open() instead of fopen(). * backend/snapscan.c: Upgrade to latest version (0.3 patch 7). * backend/snapscan.h: Ditto. * configure.in (V_MINOR): Up version to 0.71. * sanei/sanei_scsi.c (scsi_cmd) [USE == SOLARIS_INTERFACE]: "or" in lun instead of overwriting cdb[1] (patch by Martin Huber ). * backend/snapscan.c: Replace with . * backend/mustek.c (fix_line_distance_mfs): Add back missing multiplication by bpl (patch by Stefano Garavaglia ). * backend/coolscan.c: Updated with Didier's latest version. (COOLSCAN_CONFIG_FILE): Rename from PATH_COOLSCAN_CONFIG and define as "coolscan.conf". Include . (sanei_init): Use sanei_config_open() instead of fopen(). * backend/coolscan-scsidef.h: Ditto. * backend/coolscan.h: Ditto. * backend/hp.c (sane_close): Change s to s->next (patch by Dianne Skoll ). 1998-02-17 David Mosberger-Tang * backend/Makefile.in (PRELOADABLE_BACKENDS): Mention apple & coolscan. * backend/coolscan.c: New file (by Didier Carlier ).. * backend/coolscan.h: Ditto. * backend/coolscan-scsidef.h: Ditto. * backend/apple.c: Include (APPLE_CONFIG_FILE): Rename from PATH_APPLE_CONFIG. (sane_init): Use sanei_config_open(). * backend/apple.conf: New file. * backend/dll.conf: Mention apple & coolscan backends. * backend/apple.c: New file (by Milon Firikis ). * backend/apple.h: Ditto. * sane-0.70.spec: Make symlink for libsane.so.0. * doc/sane-epson.man: Mention GT-5500 scanner as working (as reported by Umberto Zanatta ). 1998-02-12 David Mosberger-Tang * sanei/sanei_scsi.c (DOMAINOS_INTERFACE): Define. Add Apollo Domain/OS support contributed by Paul Walker. * configure.in (AC_CHECK_HEADERS): Mention apollo/scsi.h. Add #undef of HAVE_APOLLO_SCSI_H. Add type checks for u_char, u_int, u_long (DomainOS reportedly needs these). * sanei/sanei_scsi.c (sanei_scsi_req_wait) [USE == LINUX_INTERFACE]: Always check for a non-zero error code in the sense-buffer. The Linux sg driver guarantees that the sense buffer is clear to zero when no sense code has been requested, so this is safe. 1998-01-28 David Mosberger-Tang * Version 0.7 released. 1998-01-27 David Mosberger-Tang * frontend/xscanimage.c (quit_xscan): Exit with status 0, not 1. * tools/xerox (scale): Added improvements contributed by Joachim Woll . * Upgrade to Oliver's latest UMAX backend. * include/Makefile.in (distclean): Add empty all rule. Add distclean rule to delete Makefile. * Makefile.in (distclean): Delete japi/Makefile as well (this command should be removed once japi gets added to SUBDIRS). * backend/Makefile.in (EXTRA): Remove ../lib/usleep.lo and ../lib/strndup.lo. (libsane.la): Make dependent on $(LIBOBJS). * frontend/xscanimage.c (preview_window_destroyed): Declare second arg (added in some version of gtk?). * tools/find-scanner.c (main): Change __sgi__ to __sgi. 1998-01-26 David Mosberger-Tang * backend/microtek.c: Don't declare strdup()---you're bound to get it wrong for some platforms! * doc/sane-scsi.man: Say explicitly that generic SCSI support needs to be enabled. * doc/sane-hp.man (Problems): Document PhotoSmart problems as reported Peter Kirchgessner . * backend/snapscan.c: Upgrade to v0.3 from http://www.cs.ualberta.ca/~charter/snapscan.html. * backend/snapscan.h: Ditto. 1998-01-22 David Mosberger-Tang SnapScan backend by Franck Schnefra, Michel Roelofs and Kevin Charter: * backend/snapscan.c: New file. * backend/snapscan.h: Ditto. * backend/snapscan.conf: Ditto. * backend/umax-scanner.h (scanner_str): Add missing comma. * sanei/sanei_scsi.c (sanei_scsi_cmd): Pass sense_handler_arg to sense handler for BSD_INTERFACE, HPUX_INTERFACE, OPENSTEP_INTERFACE, DECUNIX_INTERFACE, SCO_OS5_INTERFACE, OS2_INTERFACE, IRIX_INTERFACE, AIX_GSC_INTERFACE, and SOLARIS_INTERFACE. * include/sane/config.h.in: Add missing #undef of HAVE_SYS_SCSI_TARGETS_SCGIO_H. Ditto for HAVE_SYS_SCSI_SGDEFS_H. 1998-01-20 David Mosberger-Tang * Version 0.69 released. * doc/sane-scsi.man: Limit ncr810 patch to kernel versions < 2.0.33. * tools/xerox (HEIGHT): Add A4 paper size as comment. 1998-01-19 David Mosberger-Tang * frontend/preview.c (make_preview_image_path): New function. (preview_destroy): Save scan surface parameters as a comment in the preview image. (restore_preview_image): Read scan surface parameters from preview image file and restore only if the parameters match the currently selected surface. (paint_image): Gracefully handle NULL image_data. (event_handler): Don't call restore_preview_image(). (preview_update): Detect if the scan surface changed. If so, establish a new preview widget size, preview area size, and restore a preview image, if available. * backend/mustek.c (sense_handler): Declare closure argument. * sanei/sanei_scsi.c (sanei_scsi_open): Fix typos. * backend/umax-scsi.c (umax_open_scanner): Pass us as sense_arg. (umax_open_scanner): Ditto. (umax_open_scanner): Ditto. * backend/umax.c (sane_start): Ditto. * backend/canon.c (attach): Pass 0 as sense_arg to sanei_scsi_open(). (sane_start): Ditto. * backend/epson.c (attach): Ditto. (sane_start): Ditto. * backend/tamarack.c (sense_handler): Ditto. * backend/s9036.c (sane_start): Ditto. * backend/mustek.c (dev_open): Ditto. * backend/hp.c (attach): Ditto. (sane_start): Ditto. * backend/mustek.c (dev_open): Ditto. * tools/find-scanner.c (main): Ditto. * include/sane/sanei_scsi.h (SANEI_SCSI_Sense_Handler): Declare closure arg. (sanei_scsi_open): Ditto. * doc/sane-hp.man (Model): Remove obsolete comment regardign 5P. 1998-01-17 David Mosberger-Tang * backend/mustek.c (dev_open): Fix debug message. * Solaris related patches by Martin Huber: * backend/umax-scsi.c (umax_wait_scanner): Sleep for 1 second instead of 100ms on Sun platforms---the SCHILYscg driver prints a warning message each time a device is busy. Duh. (umax_get_data_buffer_status): Don't do umax_get_data_buffer_status() on Sun's. This should be fixed... * sanei/sanei_scsi.c (sanei_scsi_open): Call unit_ready() towards the end of this function. (CCS_SENSE_LEN): Define as 18 if not defined by any header files. (sanei_scsi_cmd): Use sensebuffer to collect sense info. (scsi_cmd): Better error handling when using SCHILYscg driver. 1998-01-14 David Mosberger-Tang * frontend/xscanimage.c (scan_start): Turn off dialog sensitivity. (scan_done): Restore dialog sensitivity. * backend/qcam.c (init_options): Turn on SANE_CAP_ALWAYS_SETTABLE. * frontend/preview.c (scan_done): Enable dialog sensitivity. (scan_start): Disable dialog sensitivity. * include/sane/sane.h (SANE_CAP_ACTIVE_WHILE_SCANNING): New manifest constant. 1998-01-12 David Mosberger-Tang * frontend/gtkglue.c (panel_destroy): Call gtk_tooltips_unref() instead of gtk_tooltips_destroy() as suggested by Ben Gertzfield . 1997-12-25 David Mosberger-Tang * backend/canon.c: New file by Helmut Koeberle . (sane_start): Print variables of type size_t as %lu and cast to (u_long), to make it compile without warning on all platforms. (attach): Remove extraneous semicolon. * backend/canon.h (canon_h): Ditto * backend/canon-scsi.c: Ditto. 1997-12-24 David Mosberger-Tang * sanei/sanei_ab306.c (sanei_ab306_cmd): Write the first 6 bytes of a SCSI command only (suggested by Andreas, but is this really correct??). * backend/mustek.c (gamma_correction): Add patch by Andreas Czechanowski to fix lineart scanning for Paragon II 600 N scanner. 1997-12-23 David Mosberger-Tang * tools/find-scanner.c (scanner_identify_scanner): Print info on all SCSI devices when --verbose is in effect. (main): Add Sun device names. * include/sane/config.h.in: Define _POSIX_SOURCE and __EXTENSIONS__ when compiling on a Sun with GCC. * configure.in (AC_CHECK_HEADERS): Mention sys/scsi/sgdefs.h and sys/scsi/targets/scgio.h. 1997-12-17 David Mosberger-Tang * frontend/xscanimage.c (device_dialog): Suggestion by Matt: set window auto-shrink by calling gtk_window_set_policy(). * japi/ImageCanvas.java: Updated with Jeff's latest patch. * japi/Jscanimage.java: Ditto. * japi/Makefile.in: Ditto. * japi/README.JAVA: Ditto. * japi/ScanIt.java: Ditto. * japi/ImageCanvasClient.java: New file. 1997-12-16 David Mosberger-Tang * frontend/gtkglue.c (panel_build): Patch by Matt: a) For all individual options flagged as "advanced", panel_build will only display the option if the "Show advanced" button is toggled. Group identifiers flagged as "advanced" are handled the same as before. b) panel_rebuild is called whenever the "Show advanced" button is toggled on or off. * doc/sane-scsi.man: Mention sane-epson(5) and sane-microtek(5). * doc/scanimage.man: Ditto. * doc/xscanimage.man: Ditto. * doc/sane-microtek.man: New file by Matt. * backend/microtek.c: Updated with Matt's version 0.4. * backend/microtek.h: Ditto. * backend/microtek.conf: Ditto. * doc/Makefile.in (SECT5): Mention sane-microtek.5. 1997-12-15 Fred Hucht & Michael Staats <{fred|michael}@thp.Uni-Duisburg.DE> * Added support for generic SCSI under AIX 4.1.x using the device driver gsc written by Matthew Jacob (nice piece of work!). Find this driver under ftp://ftp.feral.com/pub/aix/gsc.tar.gz or ftp://ftp.thp.Uni-Duisburg.DE/pub/source/gsc.tar.gz. Changed files: sanei/sanei_scsi.c, include/sane/config.h.in, configure.in. * Moved '#include ' to very beginning in all source files as AIX needs it there. * Added define OUTFILENAME to frontend/xscanimage.c. * Fixed wrong environment reference in /doc/sane-scsi.man. * Several changes in tools/find-scanner.c to run using above mentioned driver. Fixed wrong IN_periph_devtype_cpu (was 1, should be 3). 1997-12-14 David Mosberger-Tang * doc/sane-hp.man: Mention ScanJet 4P (reported to work by Adam Sjoegren ). 1997-12-09 David Mosberger-Tang * frontend/saned.c (check_host): Change len from size_t to int to match getpeername() as per Single Unix Spec (as opposed to POSIX drafts...). (start_scan): Ditto for getsockname(). * backend/net.c (sane_start): Ditto. * backend/tamarack.c (TAMARACK_CONFIG_FILE): Delete. (TAMARACK_CONFIG_FILE): New macro. Include . (sane_init): Use sanei_config_open() instead of fopen(). (read_data): Declare nbytes as size_t---makes a difference on 64-bit platforms. 1997-12-07 David Mosberger-Tang * backend/s9036.c (sane_init): Run through "indent -gnu". Use sanei_config_open() instead of fopen(). Include . (read_more_data): Print size_t variables by casting them to (u_long) and using %lu format (some platforms have size_t as u_long, others as u_int, which does make a difference if sizeof(long) > sizeof(int). (sane_start): Ditto. Add English translation for comments that were in German only. * backend/s9306.c: New file by Ingo Schneider. * backend/s9306.h: New file by Ingo Schneider. 1997-12-04 David Mosberger-Tang * backend/mustek.c (sane_cancel): Collect child process status after killing it (avoids accumulating zombie processes). Reported by Mike Sweet. * backend/umax.c (sane_cancel): Ditto. * backend/qcam.c (sane_close): Ditto. (sane_cancel): Ditto. 1997-12-03 David Mosberger-Tang * Version 0.68 released. 1997-12-02 David Mosberger-Tang * frontend/scanimage.c (window_val_user): New variable. (fetch_options): Don't overwrite window_val[i] if it's user-specified. (main): Set window_val_user[i] as necessary. * backend/mustek.c (send_gamma_table): New function. (sane_start): Send gamma table both before and after start_scan(). The MFS-06000CX is reported to need the gamma-table before start_scan() and the Mustek docs do indeed indicate downloading the gamma-table twice. (init_options): Make --custom-gamma option active by default (since gray-scale mode is the default mode). 1997-11-30 David Mosberger-Tang * doc/sane-mustek.man: Mention MFC-08000CZ. * backend/mustek.c (attach): Reduce y_range.max for MFC-08000CZ from 300 to 292mm as reported by Jeroen Steenblik . Ditto for MFC-06000CZ. 1997-11-28 David Mosberger-Tang * backend/umax-scanner.h (scanner_str): Add "Astra 610S". (known_scanners): Increment from 13 to 14. PP fixes by Andreas Czechanowski: * backend/mustek.c (EXTRA_SAVE_LINES): New macro. (fix_line_distance_pp): Use EXTRA_SAVE_LINES instead of hardcoded constant. Various fixes to make it actually work. (reader_process): Initialize s->ld_ld_line to zero. * backend/mustek.h (struct Mustek_Scanner): Add member ld_line. 1997-11-26 David Mosberger-Tang * tools/Makefile.in (.c.o): Add this rule. * tools/find-scanner.c: Include sanei_scsi.h and sanei_debug.h via <> quotes (note ""). 1997-11-25 David Mosberger-Tang * sanei/sanei_ab306.c (sanei_ab306_exit): Fix by Andreas Czechanowski: output 0x00 at port[i].base + 1 instead. 1997-11-22 David Mosberger-Tang * doc/sane-mustek.man (Model): Clarify that parallel port != printer port. * backend/mustek.c (fix_line_distance_mfs): Add missing SANE_UNFIX() calls for x_range.max and dpi_range.max. (fix_line_distance_pp): Correct based on Andreas' feedback. 1997-11-18 David Mosberger-Tang * configure.in (CFLAGS): Don't specify -ansi---with older libcs, it's causing more problems than it's worth. * Patch by Jeff Freedman: * japi/Sane.c: Limit string length to option size. * japi/SaneOption.java: Fix indentation. (unitString): New function. * japi/ImageCanvas.java: New file. * japi/Jscanimage.java: Ditto. * japi/ScanIt.java: Ditto. * japi/Makefile.in (CLASSES): Mention ScanIt.class and ImageCanvas.class. (all): Mention Jscanimage.class. * japi/Scan.c: Include . (String_length): New function. 1997-11-16 David Mosberger-Tang * frontend/gtkglue.c (scale_update): In recent versions of GTK, "value_changed" callbacks no longer return a value. Change this function to type "void" accordingly. * sanei/sanei_scsi.c: Declare cam_fd only if USE == DECUNIX_INTERFACE. * sanei/sanei_load_values.c (sanei_load_values): Detect errors while reading the option name (first call to sanei_w_string()). Reported by Geoffrey T. Dairiki. * sanei/sanei_ab306.c [HAVE_UNISTD_H]: Include . Fix by Geoffrey T. Dairiki. * lib/alloca.c: Enclose in #ifndef HAVE_ALLOCA bracket. Fix by Geoffrey T. Dairiki. * include/lalloca.h: Declare alloca() as returning void* when __STDC__ is in effect so declaration matches definition in lib/alloca.c. Reported by Geoffrey T. Dairiki. * frontend/preview.c (update_selection): Set coord[] values to rounded-to-nearest-int of float values. This avoids the jumping selection box effect. Fix by Geoffrey T. Dairiki. * frontend/gtkglue.c (scale_update): Patch by Geoffrey T. Dairiki : emit value_changed signal if the backend changed the value. * backend/umax.c: Upgraded to Oliver's latest UMAX version. * backend/mustek.c (sane_init): Ignore white space in front of an option/device-name. 1997-11-12 David Mosberger-Tang * sanei/sanei_ab306.c (sanei_ab306_get_io_privilege): (struct port): Remove member HAVE_IO_PRIVS. (sanei_ab306_get_io_privilege): Get ioperm()issions independent of HAVE_IO_PRIVS. * backend/mustek.c (do_stop): Always send STOP command before closing the device. (dev_read_req_enter): For parallel-port scanner, set *idp to 0. 1997-11-10 David Mosberger-Tang Bug reported by Matto Marjanovic : * frontend/gtkglue.c (panel_build): Create button only after we know the option's value. (button_new): Add VAL argument and initialize button state to correct value before connecting the toggled callback. Bugs reported by Petter Reinholdtsen : * sanei/sanei_config.c (sanei_config_open): Add missing cast to (char *). * sanei/sanei_scsi.c (sanei_scsi_cmd) [USE == IRIX_INTERFACE]: Fix (u_char) cast to (u_char *). 1997-11-06 David Mosberger-Tang * backend/qcam.c (init_options): Set the type of OPT_NUM_OPTS to SANE_TYPE_INT (suggested by Guido Muesch ). * backend/mustek.c (init_options): Ditto. 1997-11-04 David Mosberger-Tang * frontend/xcam.c (xcam_exit): Protect against recursive invocation. (main): Turn on preferences.advanced as no graphical geometry selection exists right now. Include . preferences: New variable. (pref_toggle_advanced): New function. (pref_toggle_tooltips): Ditto. (build_preferences_menu): Add button to control advanced & tooltips preferences. * doc/sane.tex (subsection{sanecontrol_option}): Make it illegal for SANE_INFO_RELOAD_OPTIONS to be set needlessly. * backend/hp.c (sane_control_option): Only turn on RELOAD_PARAMS when value changed. * backend/qcam.c (sane_control_option): Only turn on RELOAD_PARAMS when value changed. * backend/mustek.c (sane_control_option): Only turn on RELOAD_OPTIONS and/or RELOAD_PARAMS when value changes. * Integrate b&w quickcam fixes by Guido Muesch : * backend/qcam.c (bw_x_range, odd_bw_x_range, bw_y_range, odd_bw_y_range): New constants. (sane_open): Disable despeckle, black-level, hue, saturation, resolution & test options for b&w camera (either not useful or not supported at this point). (sane_start): Use QC_MONO_SET_CONTRAST instead of QC_BW_SET_CONTRAST. (sane_start): Call qc_reset() for b&w camera to avoid hangs (color camera doesn't need this and is faster that way). (sane_start): Multiply undecimated_width by s->val[OPT_DEPTH].w, not 4. * backend/qcam.h: Remove obsolete QC_BW_SET_CONTRAST, QC_BW_AUTO_ADJUST_OFFSET, QC_BW_GET_OFFSET, and QC_SET_CONTRAST macros. 1997-11-02 David Mosberger-Tang * doc/sane-hp.man (Model): Mention ScanJet IIcx. Johannes Geiger reported it to work fine under OS/2 with sane-0.66 * backend/umax.c: Apply patch by Oliver Rauch that avoid umax hangs. 1997-11-01 David Mosberger-Tang * Version 0.67 released. * sanei/sanei_ab306.c: New file. * backend/mustek.h (MUSTEK_FLAG_PP): New macro. (struct Mustek_Scanner): Add members ld.index and ld.lmod3 for parallel-port scanner line-distance correction. * backend/mustek.c (color_seq): Move to global level from fix_line_distance_normal(). (fix_line_distance_pp): New function. (scsi_wait_ready): Renamed from wait_ready(). (pp_wait_ready): New function. (dev_wait_ready): Ditto. (dev_open): Ditto. (dev_cmd): Ditto. (dev_req_wait): Ditto. (dev_read_start): Ditto. (dev_read_req_enter): Ditto. (dev_close): Ditto. (attach): Modify to use dev_open(). Set minimum scan resolution to 51 dpi for parallel-port scanners. (scan_area_and_windows): Call dev_cmd() instead of sanei_scsi_cmd(). (mode_select): Ditto. (gamma_correction): Ditto. (start_scan): Ditto. (stop_scan): Ditto. (line_distance): Ditto. (get_image_status): Ditto. (backtrack_and_adf): Ditto. (gamma_correction): Return immediately if custom-gamma is turned off or if not doing a multibit scan. Add support for sending all three gamma tables with a single command. (line_distance): Initialize additional line-distance correction state needed by parallel-port scanner. (read_req_enter): Remove. (send_data): Modify to support parallel-port scanner line-distance correction. (reader_process): Require I/O-privilege if dealing with parallel port scanner and call dev_read_start(). Use dev_read_req_enter() instead of read_req_enter(), dev_req_wait() instead of sanei_scsi_req_wait(). (sane_start): For parallel-port scanner, send gamma table with a single gamma_correction() call. * doc/sane-mustek.man: Update with info regarding parallel port scanner. * backend/dll.c (add_backend): When a backend is present already, move it to the head of the backend list, so pre-loaded backends appear in the same order as if they had been loaded dynamically * backend/mustek.c (pp_mode_list): New constant. * backend/mustek.c (init_options): Use pp_mode_list for parallel port type scanners (no color lineart/halftone modes). * sanei/sanei_codec_ascii.c (ascii_w_string): free(*s) only if *s is non-NULL (reported by Geoffrey T. Dairiki ). * backend/mustek.c (attach): Turn on MUSTEK_FLAG_LD_NONE for MFS-12000SP with firmware 1.02 or newer. Reported by Henning Busacker . 1997-10-25 David Mosberger-Tang * doc/sane.tex (chapter{Contact Information}): Update mailing list and home page address. * backend/dll.c: Include . Call sanei_config_open() instead of fopen(). * backend/hp.c: Ditto. * backend/mustek.c: Ditto. * backend/net.c: Ditto. * backend/pint.c: Ditto. * backend/qcam.c: Ditto. * backend/umax.c: Ditto. * backend/Makefile.in (EXTRA): Mention ../sanei/sanei_config.lo. * doc/sane-dll.man: Document SANE_CONFIG_DIR. * doc/sane-hp.man: Ditto. * doc/sane-mustek.man: Ditto. * doc/sane-net.man: Ditto. * doc/sane-pint.man: Ditto. * doc/sane-qcam.man: Ditto. * doc/sane-scsi.man: Ditto. * doc/sane-umax.man: Ditto. * doc/saned.man: Ditto. * backend/microtek.c: Updated with v0.3 from Matt. Include (MICROTEK_CONFIG_FILE): Define as "microtek.conf". (PATH_MICROTEK_CONFIG): Delete. (sane_init): Replace fopen() with sanei_config_open(). * backend/microtek.h: Ditto. 1997-10-24 David Mosberger-Tang * sanei/sanei_config.c (sanei_config_open): Add multiple-directory support for SANE_CONFIG_DIR. * sanei/sanei_config.c: New file by Jeff Freedman. * include/sane/sanei_config.h: Ditto. 1997-10-23 David Mosberger-Tang * Fixes for OS/2 by Jeff Freedman: * frontend/Makefile.in (SBINPROGS): Set to @SANED@. * configure.os2 (LN_S): Add --sysconfdir=. * configure.in: Set up SANED depending on . * backend/Makefile.in (EXTRA): Mention usleep.lo and strndup.lo. * backend/pnm.c (rgblength, rgbbuf, rgbleftover): Declare as static. * ltconfig: Upgrade to 1.0d. * ltmain.sh: Ditto. * sanei/sanei_constrain_value.c: Include . 1997-10-22 David Mosberger-Tang * frontend/gtkglue.h (struct GSGDialog): Remove idle_id member. * frontend/gtkglue.c (idle_handler): Remove. GTK's reference counting appears to have been fixed (?). * backend/umax_scanner.h: Update with Oliver's latest version. * backend/umax-scsi.c: Ditto. * backend/umax-scsidef.h: Ditto. * backend/umax-struct.h: Ditto. * backend/umax.c: Ditto. * backend/umax.h: Ditto. 1997-10-18 David Mosberger-Tang * sanei/sanei_scsi.c (sanei_scsi_req_enter): Set cdb.hdr.pack_id to unique id number. 1997-10-17 David Mosberger-Tang * japi/Makefile.in: New file (by Jeff Freedman ). * japi/README.JAVA: Ditto. * japi/Sane.c: Ditto. * japi/Sane.java: Ditto. * japi/SaneDevice.java: Ditto. * japi/SaneOption.java: Ditto. * japi/SaneParameters.java: Ditto. * japi/SaneRange.java: Ditto. * japi/Test.java: Ditto. * configure.in (AC_OUTPUT): Mention japi/Makefile. * sanei/sanei_scsi.c (sanei_scsi_cmd) [USE == IRIX_INTERFACE]: Add data buffer alignment fix by Michael Sweet. 1997-10-24 Jeff Freedman * sanei_open_config() added. Backends call it to open .conf files. 1997-10-14 David Mosberger-Tang * Version 0.66 released. * README: Update with new URLs and latest GIMP/GTK info. 1997-10-11 David Mosberger-Tang * include/sane/saneopts.h: Add #defines for TEN_BIT_MODE, WARMUP, RGB_PREVIEW_PATCH, and START_SCAN_PATCH. * README: Remove umax-specific configure options (they're now runtime options). * configure.in (CPPFLAGS): Ditto. * include/sane/config.h.in: Ditto. * backend/epson.c: Updated 1997-10-09 David Mosberger-Tang * backend/umax.c (sane_exit): Remove bogus call to free() (reported by Peter). Thu Oct 9 20:20:32 1997 Kazuhiro Sasayama * epson.c (sane_start): Fix lcount computation. Thu Oct 9 11:44:03 1997 Kazuhiro Sasayama * epson.c (sane_start): Use byte-interleaved mode if available. (sane_start): Set line counter for byte-interleaved mode. (sane_read): Handle byte-interleaved mode. (sane_init) [PACKAGE && VERSION]: Debug out PACKAGE and VERSION. Tue Oct 7 18:44:36 1997 Kazuhiro Sasayama * epson.c (set_lcount): New function. (sane_start): Use block mode for monochrome if available. (sane_read): Handle block mode. * epson.h (struct Epson_Scanner): Add block. Tue Oct 7 15:48:11 1997 Kazuhiro Sasayama * epson.c (set_speed): New function. (sane_start): Use set_speed. Tue Oct 7 15:44:33 1997 Kazuhiro Sasayama * README: Add info about a mailing list. * Makefile.am (libsane_epsonx_la_LDFLAGS): Increment the revision. * epson.c (identify): Debug output the command level. 1997-10-08 David Mosberger-Tang * backend/mustek.conf: Turn on linedistance-fix and lineart-fix by default. * include/sane/config.h.in: Remove NEED_MUSTEK_LINE_DISTANCE_WORKAROUND. * doc/sane-mustek.man (CONFIGURATION): Document option linedistance-fix and lineart-fix. * configure.in: Remove test for --enable-ld-fix. * backend/mustek.h (MUSTEK_FLAG_LD_FIX): New flag. (MUSTEK_FLAG_LINEART_FIX): Ditto. * backend/mustek.c (line_distance): Replace NEED_MUSTEK_LINE_DISTANCE_WORKAROUND with runtime test for same. (reader_process): If MUSTEK_FLAG_LINEART_FIX is on, delay 200ms when scanning in lineart mode. (sane_init): New variable. Add linedistance-fix and lineart-fix support. * backend/Makefile.in (install): Fix install rule so all symlinks necessary for libsane.so are installed. * backend/dll.c (load): Change from LIBNAME to LIBPATH (as per libtool-1.0c documentation). * sanei/sanei_scsi.c (issue): Declare `static'. * aclocal.m4: Update with contents of libtool.m4. * configure.in (AC_PROG_RANLIB): Remove (once again!). * config.guess: Update from libtool-1.0c. * config.sub: Ditto. * ltconfig: Ditto. * ltmain.sh: Ditto. * backend/epson.c: Update with Kazuhiro's latest epson version (1.1.6). 1997-10-07 David Mosberger-Tang * frontend/gtkglue.c (panel_destroy): Clear elem->menu after freeing it (reported by Kazuhiro Sasayama ). 1997-10-06 David Mosberger-Tang * backend/epson.c: Updated with Kazuhiro Sasayama latest version (1.1.5). 1997-10-04 David Mosberger-Tang * Version 0.65 released. * sanei/sanei_scsi.c (close_aspi): Rename from sanei_close_aspi (static functions don't need ugly sanei_ prefix...). (open_aspi): Ditto. * configure.os2: New file (from Jeff's config.os2). * backend/umax-scsi.c: Mmove include of and into !def UMAX_TO_SANE bracket. * backend/pnm.c (getparmfromfile): Open with mode "rb" to get binary file on platforms where this is meaningful. 1997-10-02 David Mosberger-Tang * doc/sane-epson.man: Mention that backend is known to work with GT-5000. 1997-10-01 David Mosberger-Tang * doc/sane-epson.man: New file (based on Kzuhiro's README file). * backend/Makefile.in (PRELOADABLE_BACKENDS): Mention epson. (libsane-epson.la): Add dependencies for Epson backend. * backend/epson.c: New file by Kazuhiro Sasayama . * backend/epson.h: Ditto. * backend/microtek.c: Updated with latest version from http://www.mir.com/mtek/ by Matt Marjanovic . * backend/microtek.h: Ditto. 1997-10-3 Jeff Freedman * sanei/sanei_scsi.c: Minor fixes for OS/2 support. * configure.in: Check for presence of sys/socket.h to set @NET@. * backend/Makefile.in: net -> @NET@ 1997-09-30 David Mosberger-Tang * sanei/sanei_scsi.c: Integrate OS/2 support by Jeff Freedman: (OS2_INTERFACE) Define. (open_aspi,close_aspi): New OS/2-specific functions. (sanei_scsi_open): Add OS/2 support. [USE = OS2_INTERFACE]: OS/2 version of sanei_scsi_cmd(). (sanei_scsi_cmd, sanei_scsi_req_wait) [STUBBED_INTERFACE]: Return proper value. * include/sane/config.h.in (HAVE_STRNCASECMP, HAVE_OS2_H): Add #undef. If !HAVE_STRNCASECMP, define strncasecmp macro as alias for strnicmp. * configure.in (AC_CHECK_FUNCS): Check for strncasecmp(). (AC_CHECK_HEADERS): Check for os2.h. 1997-09-30 David Mosberger-Tang * sanei/sanei_scsi.c: Integrate OS/2 support by Jeff Freedman: (OS2_INTERFACE) Define. (open_aspi,close_aspi): New OS/2-specific functions. (sanei_scsi_open): Add OS/2 support. [USE = OS2_INTERFACE]: OS/2 version of sanei_scsi_cmd(). (sanei_scsi_cmd, sanei_scsi_req_wait) [STUBBED_INTERFACE]: Return proper value. * include/sane/config.h.in (HAVE_STRNCASECMP, HAVE_OS2_H): Add #undef. If !HAVE_STRNCASECMP, define strncasecmp macro as alias for strnicmp. * configure.in (AC_CHECK_FUNCS): Check for strncasecmp(). (AC_CHECK_HEADERS): Check for os2.h. 1997-09-24 David Mosberger-Tang * Version 0.64 released. * PROJECTS (Backend): Update microtek entry. Add entry for sgivl. * backend/microtek.conf: New file. * backend/dll.conf: Mention microtek. * backend/Makefile.in (PRELOADABLE_BACKENDS): Mention microtek. (libsane-microtek.la): Mention microtek dependencies. * backend/microtek.c: New file by Matthew Marjanovic. * backend/microtek.h: Ditto. 1997-09-23 David Mosberger-Tang * sanei/sanei_scsi.c (sanei_scsi_cmd): Integrate Michael Sweet's latest Irix implementation of sanei_scsi_cmd(). Sat Sep 6 08:59:24 1997 David Mosberger-Tang * Version 0.63 released. * configure.in (V_MINOR): Bump up to 63. * configure.in (AM_PROG_RANLIB): Go back to AC_PROG_RANLIB. The former causes a bad configure script. Fri Aug 29 16:08:05 1997 David Mosberger-Tang * doc/sane-umax.man: Change status of "Astra 1200S" to "all modes ok" as Oliver tells me that this has been tested and is working now. Wed Aug 20 17:23:04 1997 David Mosberger-Tang * sane-0.63.lsm: Update email addresses etc. Integrate Oliver Rauch's UMAX improvements: * README: Document --enable-umax-preview-fix, --enable-umax-start-scan-fix. * configure.in (UMAX_RGB_PREVIEW, UMAX_START_SCAN_PATCH): Define if necessary. * include/sane/config.h.in (UMAX_RGB_PREVIEW, UMAX_START_SCAN_PATCH): Add #undef. * include/sane/saneopts.h: Add manifest constants for options resolution-bind, negative, quality-cal, threshold, analog-gamma, analog-gamma-r, analog-gamma-g, analog-gamma-b, analog-gamma-bind, smear, white-bind, black-bind. * AUTHORS (Frontends): Update email addresses of Oliver Rauch and myself. 1997-08-07 David Mosberger-Tang * backend/mustek.c (attach): Print Mustek scanner info at debug level 2, not 3. 1997-08-06 David Mosberger-Tang * frontend/preview.c (XSERVER_WITH_BUGGY_VISUALS): Make conditional on #ifdef __alpha__ (Michael Sweet reports that the old code broke SGI IRIX 6.3). 1998-08-01 David Mosberger-Tang * include/sane/config.h.in (HAVE_SYS_SCSICMD_H, HAVE_SYS_DSREQ_H): Undefine. * configure.in (AC_CHECK_HEADERS): Mention sys/scsicmd.h and sys/dsreq.h. * sanei/sanei_scsi.c (SCO_OS55_INTERFACE, IRIX_INTERFACE, SOLARIS_INTERFACE): Define. 1997-07-27 David Mosberger-Tang * frontend/xscanimage.c (quit_xscan) [HAVE_LIBGIMP_GIMP_H]: When running as a GIMP extension, call gimp_quit(). (init) [HAVE_LIBGIMP_GIMP_H]: Set GDK's xshm flag based on GIMP's flag. * doc/sane-mustek.man (SCSI ADAPTER TIPS): Try to make complete sentences. 1997-07-25 David Mosberger-Tang * Version 0.62 released. * configure.in (CPPFLAGS): Test for gtk_gamma_curve_new() to see whether appropriate version of gtk is installed. * backend/mustek.c (sane_init): Add parsing support for option `strip-height'. (strip_height): New variable to limit scan strip height. (reader_process): If strip_height is greater than 0.0, limit lines_per_buffer so that no more than strip_height inches are scanned with a single SCSI read command. * doc/sane-mustek.man (CONFIGURATION): Describe strip-height option. 1997-07-23 David Mosberger-Tang * doc/sane-hp.man (Model): Added ScanJet 3c info. 1997-07-22 David Mosberger-Tang * frontend/scanimage.README: Remove file. * doc/sane-scsi.man: Add info on generic AM53C974 driver. 1997-07-19 David Mosberger-Tang * sanei/sanei_scsi.c [USE == LINUX_INTERFACE]: Include . 1997-07-18 David Mosberger-Tang * sanei/sanei_scsi.c (sanei_scsi_req_flush_all) [WE_HAVE_ASYNC_SCSI]: Implement stub. * scripts/xerox: New file (simple xeroxing script). * frontend/scanimage.c (main): When opening a device fails, also print reason for failure. * frontend/preview.c (preview_new): Register expose_event handler. (preview_destroy): Call scan_done() if called when preview scanning in progress. * backend/mustek.c (attach): Add argument MAY_WAIT. If it's TRUE, wait for scanner to become ready before sending inquiry command. (do_cancel): Rename to do_stop. Ignore child's exist status when we're really cancelling a scan. Issue stop_scan() command only if the scan got cancelled and do a wait_ready() before issuing the stop_scan(). (read_data): Delete. (sane_init): Call attach() with MAY_WAIT set to SANE_FALSE. (sane_open): Call attach() with MAY_WAIT set to SANE_TRUE. * frontend/preview.c (display_partial_image): Use gtk_preview_put() to update the preview window. Much faster than hiding/showing the widget. (expose_handler): New function. * frontend/progress.c (progress_new): Make "Cancel" button a toggle-button so user can see when it was pressed down while the backend cancels its operation. * backend/mustek.c (do_cancel): Call wait_ready() before attempting to stop scanner. (read_req_enter): New function. (send_data): Ditto. (sigterm_handler): New function. (reader_process): Reimplement using asynchronous SCSI command interface. (wait_ready): Use gettimeofday() to implement timeout. The scsi command itself may take a considerable amount of time (1 second or more) so we can't just loop for a fixed number of times. * include/sane/sanei_scsi.h: Declre sanei_scsi_req_enter, sanei_scsi_req_wait, and sanei_scsi_req_flush_all. * sanei/sanei_scsi.c [WE_HAVE_ASYNC_SCSI]: Define. (issue): New function. (sanei_scsi_req_flush_all): New function. (sanei_scsi_req_enter): Ditto. (sanei_scsi_req_wait): Ditto. (sanei_scsi_cmd): Reimplement in terms of enter/wait. (sanei_scsi_req_enter) [!WE_HAVE_ASYNC_SCSI]: Stub that simply calls sanei_scsi_cmd(). (sanei_scsi_req_wait): Empty stub. 1997-07-16 David Mosberger-Tang * doc/sane-scsi.man: Add Tekram DC390 info (contributed by kawk@Home.Yo.COM (Kolja Waschk)) 1997-07-15 David Mosberger-Tang * sanei/sanei_scsi.c (sanei_scsi_cmd) [USE==LINUX_INTERFACE]: Return SANE_STATUS_NO_MEM if write() of SCSI command fails. * backend/mustek.c (do_cancel): If reader_process exited, use exit status as return value. (reader_process): Return SANE_STATUS_IO_ERROR instead of 1. Return SANE_STATUS_NO_MEM instead of 2. Return STATUS instead of 3. Return SANE_STATUS_GOOD instead of 0. (sane_read): If do_cancel() returns anything but SANE_STATUS_CANCELLED or SANE_STATUS_GOOD, something bad has happened and the return status should be returned. * doc/sane-scsi.man: Fix typo: sg.h is in /usr/include/scsi, not /usr/include. * PROBLEMS: Add warning about updating Linux kernel after increasing SG_BIG_BUFF. 1997-07-14 David Mosberger-Tang * frontend/xscanimage.c (quit_xscan): Destroy preview window before quitting, so preview image gets saved if necessary. * frontend/Makefile.in (LIBX11): New macro. (LIBGTK): Ditto. (xscanimage): Use $(LIBGTK) instead of $(LIBS). (xcam): Use $(LIBGTK) instead of $(LIBS). * configure.in (LIBX11): Define as all libraries necessary when using X11. (LIBGTK): Define as all libraries necessary when using GTK+. * backend/mustek.c (sane_start): Don't send gamma in lineart and halftone mode! * doc/sane-scsi.man: Add info on how to setup BT958 card (contributed by Jeremy ). 1997-07-13 David Mosberger-Tang * doc/sane-scsi.man: Add info on FreeBSD. 1997-07-12 David Mosberger-Tang * ltmain.sh: Installed libtool-1.0-nomode patch. * configure.in (AC_PROG_RANLIB): Change to AM_PROG_RANLIB. 1997-07-11 David Mosberger-Tang * ltmain.sh: Upgrade to libtool-1.0. * ltconfig: Ditto. * config.guess: Ditto. * config.sub: Ditto. * aclocal.m4: Ditto. 1997-07-08 David Mosberger-Tang * Version 0.61 released. * backend/Makefile.in (LIBLIB_FUNCS): Mention snprintf. * backend/umax-struct.h (LINEART, HALFTONE, GREYSCALE, RGB): Move the pound sign to the beginning of the line. * backend/umax-scsidef.h: Ditto for all indendent #defines in this file. * backend/umax-scsi.c: Ditto for all # directives in here. * frontend/saned.c [HAVE_LIBC_H]: Include . 1997-07-07 David Mosberger-Tang * backend/net.c [HAVE_LIBC_H]: Include . * lib/usleep.c: New file (adapted from glibc). * lib/strndup.c: Move #ifndef HAVE_STRNDUP up so nothing except gets included unless really needed. Include to get size_t defined. * lib/snprintf.c (vsnprintf): Rename from vplp_snprintf. (snprintf): Rename from plp_snprintf. * include/sane/config.h.in: Add #undef HAVE_USLEEP. Add #undef HAVE_IO_CAM_CAM_H. * configure.in: Test ${ac_cv_prog_gcc} instead of $CC to find out whether we're dealing with gcc. Test for io/cam/cam.h header. (AC_C_INLINE): Add. (AC_CHECK_FUNCS): Mention usleep. Check for Xext after checking for X11 since on some platforms, linking against Xext requires functions from X11. * backend/umax.c (PATH_MAX): Define as 1024 if undefined. * backend/Makefile.in (CONFIGS): Don't add $(srcdir) prefix. (LIBLIB_FUNCS): Mention usleep. (install): Invoke $(LIBTOOL) $(MINST) --finish $(libdir) in a final step. Correct installation rule for config files. 1997-07-06 David Mosberger-Tang * sanei/sanei_scsi.c (sanei_scsi_cmd): New variable RESULT. 1997-07-05 David Mosberger-Tang * sanei/sanei_scsi.c (sanei_scsi_cmd): Improve BSD version based on patch by Amancio Hasty . (BSD_INTERFACE): Rename from NETBSD_INTERFACE (FreeBSD uses the same interface). * configure.in (CPPFLAGS): Check for scsireq_enter in libscsi.a. * doc/Makefile.in (SECT5): Mention sane-scsi.5. * doc/sane-scsi.man: New file. * doc/sane-umax.man: Add SEE ALSO section mentioning sane-scsi(5). * doc/sane-mustek.man: Ditto. * doc/scanimage.man: Mention sane-umax(5). * doc/xscanimage.man: Ditto. * doc/saned.man: Ditto. * configure.in (AC_TYPE_PID_T): Add. * include/sane/config.h.in: Add undef of pid_t. 1997-07-04 David Mosberger-Tang * lib/sigprocmask.c (sigprocmask): Use hardcoded values instead of SIG_BLOCK, SIG_UNBLOCK, and SIG_SETPROCMASK. 1997-07-03 David Mosberger-Tang * configure.in (CPPFLAGS): Remove -D_POSIX_SOURCE. We can't define it safely since it causes select() related typedefs and defines to go away under FreeBSD, for example. 1997-07-02 David Mosberger-Tang * configure.in (CPPFLAGS): Put -D_GNU_SOURCE and -D_POSIX_SOURCE in CPPFLAGS, not CFLAGS. * doc/sane-umax.man: Change SG_BIG_BUFF example from 128KB to 127.5KB. * doc/sane-mustek.man: Ditto. Mention DTC3181E together with DTCT436. * frontend/gtkglue.c (gsg_create_dialog): Initialize dialog->advanced to TRUE so "dumb" frontends will show all options. * frontend/xscanimage.c (device_dialog): Turn off advanced options by default. * configure.in (AC_CHECK_TYPE): Check for u_long and u_int. Check for scsireq_t. * sanei/sanei_scsi.c: Don't define scsireq_t here. * include/sane/config.h.in: Add undef of u_long, u_int, and scsireq_t. 1997-07-01 David Mosberger-Tang * frontend/xscanimage.c (quit_xscan): No longer call preview_destroy() since that now gets called as part of the "destroy" callback. * backend/hp.c (attach) [PREFER_DEVICEPIXEL]: Use device-pixel unit commands instead of decipoint commands so things work on a 5p as well (contributed by NOGAYA Shigeki ). (sane_start) [PREFER_DEVICEPIXEL]: Convert from mm to pixels. * frontend/preview.c (input_available): Break out of per-bit loop when done with one line (fix contributed by NOGAYA Shigeki ). * frontend/xscanimage.c (input_available): Ditto. * doc/sane-umax.man: Rename SCSI id to Product id to reduce confusion. * doc/sane-hp.man: Ditto. * doc/sane-mustek.man: Ditto. * backend/mustek.c (wait_ready): Ignore errors during wait_ready(). With 3-pass scanners, ncr810 driver returns EIO while the scanner is getting ready for pass 2 or 3. 1997-06-30 David Mosberger-Tang * backend/mustek.c (sense_handler): Print debug info if we find unknown sense result. * sanei/sanei_scsi.c (sanei_scsi_cmd): Consider sense_buffer[] only when result != 0. * frontend/scanimage.c (sighandler): Change return value type from void to RETSIGTYPE (from ). * doc/sane-mustek.man: Mention problems with MSFS-12000SP. * backend/umax.c (sane_start): Don't use C++ style line comments. * backend/umax-scsi.c (umax_do_scsi_cmd): Use sigemptyset() to clear out signal set. Include . * frontend/saned.c [HAVE_SYS_SELECT_H]: Include . * include/sane/config.h.in: Mention HAVE_SYS_SELECT_H. Thu Jun 26 10:19:50 1997 David Mosberger-Tang * README: Mention permissions on generic SCSI device. Tue Jun 24 17:35:52 1997 David Mosberger-Tang * doc/xscanimage.man: Fix spacing. * doc/sane-dll.man: Mention how list of pre-loaded backends can be changed. Sun Jun 22 08:29:47 1997 David Mosberger-Tang * PROBLEMS: Add note on SCSI device names under OpenStep. * sanei/sanei_scsi.c (sanei_scsi_cmd): Fix various minor bugs. (sanei_scsi_open): Add support for OpenStep. * backend/Makefile.in (install): Replace 'ln -f -s' with `rm -f', `ln -s' sequence. OpenStep doesn't support the -f option at all. Fri Jun 20 07:30:18 1997 David Mosberger-Tang * doc/sane-qcam.man: Updated B&W status. * sanei/sanei_save_values.c [HAVE_LIBC_H]: Include . * sanei/sanei_load_values.c: Ditto. * lib/sigprocmask.c: New file. * lib/strdup.c: Ditto. * lib/Makefile.in (LIBLIB_OBJS): Mention strdup.o and sigprocmask.o. (LIBLIB_LTOBJS): Mention strdup.lo and sigprocmask.lo. (liblib.a): Use $(RANLIB) instead of -ranlib. * include/sane/sanei_backend.h [!HAVE_SIGPROCMASK]: Define compatibility macros sigset_t, sigemptyset(), sigfillset(), sigaddset(), sigdelset(), and sigaction(). Define macro SIGACTION. * frontend/saned.c (HAVE_VARARG_MACROS): Don't define unless we have at least gcc 2.6. * include/sane/sanei_debug.h Ditto. Use HAVE_VARARG_MACROS instead of __GNUC__. * sanei/sanei_init_debug.c: Ditto. * configure.in (AC_PROG_RANLIB): Mention. (CFLAGS): Move up near the beginning and add -D_POSIX_SOURCE. (AC_CHECK_HEADERS): Test for libc.h. (AC_TYPE_SIGNAL): Mention. (AC_CHECK_FUNCS): Check for sigprocmask. * backend/net.c (sane_init): Honor SANE_NET_HOSTS even if net.conf does not exist. * backend/mustek.c (sane_start): Use "struct SIGACTION" so it's easier to make code work on platforms that don't support sigprocmask(). * backend/umax.c (sane_start): Ditto. * ltmain.sh: Upgrade to libtool-0.9h. * ltconfig: Ditto. * config.guess: Ditto. * config.sub: Ditto. * aclocal.m4: Ditto. Thu Jun 19 08:26:33 1997 David Mosberger-Tang * configure.in (V_MINOR): Update forgotten version number... * backend/qcam.c (reader_process): Scale 4 and 6 bpp to 8 bpp. * backend/mustek.h (MUSTEK_FLAG_LD_NONE): New flag. * backend/mustek.c (attach): Turn on MUSTEK_FLAG_LD_NONE for MSF-06000SP (this fixes the problems for Andreas Gaumann ). (attach): Print debug message when MUSTEK_FLAG_LD_NONE is on. (line_distance): Fix up ld correction only if MUSTEK_FLAG_LD_NONE is not on. Wed Jun 18 15:42:25 1997 David Mosberger-Tang * doc/sane.tex (section{Image Data Format}}): Clarify what valid bit-depths are. Tue Jun 17 07:47:51 1997 David Mosberger-Tang * backend/qcam.c (sane_get_parameters): Test for != QC_COLOR instead of == QC_MONO. Apparently, there are several versions of the monochrome camera. (qc_setscanmode): Ditto. * lib/snprintf.c: Update with latest version (also includes license information now). * sane-0.6.lsm (Author): Update Tristan Tarrant's email address. * AUTHORS: Ditto. * frontend/xscanimage.c: Ditto. Sun Jun 15 10:08:44 1997 David Mosberger-Tang * doc/xscanimage.man: Add section on how to run xscanimage under the GIMP. * frontend/xscanimage.c (init) [HAVE_LIBGIMP_GIMP_H]: Call gtk_rc_parse() with gimp_gtk_rc() to get space-saving layout of The GIMP. (Suggested by Sven Neumann.) * sanei/sanei_scsi.c (sanei_scsi_cmd): Add NeXTStep/OpenStep implementation. (sanei_scsi_cmd): Adjust NetBSD version to make it more like NeXTStep/OpenStep version. * configure.in (AC_CHECK_HEADERS): Mention bsd/dev/scsireg.h. Thu Jun 19 15:19:38 1997 Gordon Matzigkeit * backend/umax-scsi.c: Parameterize slightly more so that the build succeeds on non-Linux platforms. * Makefile.in, backend/Makefile.in, doc/Makefile.in, frontend/Makefile.in (install): Fail immediately if any installation fails. Use mkinstalldirs to create all installation directories. Sat Jun 14 11:07:13 1997 David Mosberger-Tang * Version 0.6 released. * frontend/preview.c (preview_new): Connect "destroy" signal so we notice if window manager closes our window. (top_destroyed): New function. * frontend/xscanimage.c (scan_preview): Make it robust against preview_new() returning NULL and connect "destroy" signal so we notice when the window manager closes the preview window. (preview_window_destroyed): New function. * LICENSE: New file. * backend/dll.c: Change license from GPL to relaxed GPL. * backend/hp.c: Ditto. * backend/hp.h: Ditto. * backend/mustek.c: Ditto. * backend/mustek.h: Ditto. * backend/net.c: Ditto. * backend/net.h: Ditto. * backend/pint.c: Ditto. * backend/pint.h: Ditto. * backend/pnm.c: Ditto. * backend/pnm.h: Ditto. * backend/qcam.c: Ditto. * backend/qcam.h: Ditto. * sanei/sanei_codec_ascii.c: Ditto. * sanei/sanei_codec_bin.c: Ditto. * sanei/sanei_constrain_value.c: Ditto. * sanei/sanei_init_debug.c: Ditto. * sanei/sanei_load_values.c: Ditto. * sanei/sanei_net.c: Ditto. * sanei/sanei_save_valus.c: Ditto. * sanei/sanei_scsi.c: Ditto. * sanei/sanei_wire.c: Ditto. Fri Jun 13 18:35:47 1997 David Mosberger-Tang * lib/snprintf.c: New file (Patrick Powell). * frontend/xcam.c: Use guint32 and guint32 instead of u_int16_t and u_int32_t (Tristan Tarrant). * doc/Makefile.in (SECT5): Mention sane-umax.5. * configure.in (PACKAGE_VERSION): Check for snprintf (Tristan Tarrant). * backend/dll.conf: Mention umax. * backend/umax-scanner.h: New file (from umax-0.5.5). * backend/umax-scsi.c: Ditto. * backend/umax-scsidef.h: Ditto. * backend/umax-struct.h: Ditto. * backend/umax-uc630.h: Ditto. * backend/umax-ug630.h: Ditto. * backend/umax-umax.c: Ditto. * backend/umax.conf: Ditto. * backend/umax.h: Ditto. * doc/sane-umax.5: Ditto. * backend/Makefile.in (PRELOADABLE_BACKENDS): Mention umax. (libsane-umax.la): List dependencies for umax library. * COPYING: New file. * include/sane/config.h.in: Add #undef HAVE_SNPRINTF. Tue Jun 10 21:00:34 1997 David Mosberger-Tang * ltmain.sh: Upgrade to libtool-0.9g. Sun Jun 8 21:24:27 1997 David Mosberger-Tang * backend/dll.c (load): Also check SHLIB_PATH (HP-UX) and LIBPATH (AIX). * include/sane/config.h.in: Remove HAVE_GTK_GTKCURVE_H. Sat Jun 7 11:19:57 1997 David Mosberger-Tang * frontend/xscanimage.c (pref_device_save): Call gsg_sync() before saving the values. * backend/Makefile.in (libsane-%.la): Add -export-dynamic flag. * frontend/xscanimage.c (query): Rename "/Extensions/" to "/Xtns/" to reflect gimp-0.99.10 changes. (query): Don't put in separator---it confuses gimp. (main): Temporarily install a null print handler so gimp_main() doesn't produce any ugly messages when program gets invoked in stand-alone mode. * ltconfig: Upgrade libtool to version 0.9f. * ltmain.sh: Ditto. * config.guess: Ditto. * config.sub: Ditto. * frontend/Makefile.in (install): Define MKDIR as $(top_srcdir)/mkinstallldirs. Use $(MKDIR) instead of mkdir. * backend/Makefile.in (install): Ditto. * doc/Makefile.in (install): Ditto. * mkinstalldirs: New file (from maint-0.1g). Tue Jun 3 10:29:17 1997 David Mosberger-Tang * LEVEL2: New file. Sun Jun 1 13:26:40 1997 David Mosberger-Tang * doc/Makefile.in (LATEX, DLH): New macros. (sane.ind): Prefix source filename with $(srcdir). (ps): Ditto. (html): Ditto. * doc/net.tex (subsection{SANE_NET_START}): Document member byte_order. * sanei/sanei_net.c (sanei_w_start_reply): (De-)code BYTE_ORDER member. * frontend/saned.c (byte_order): New union. (process_request): Initialize byte_order member. * include/sane/sanei_net.h (SANE_Net_Byte_Order): New enum type. (SANE_Start_Reply): New member BYTE_ORDER. Wed May 28 17:13:43 1997 David Mosberger-Tang * doc/Makefile.in (install): Create $(mandir)/man1 and $(mandir)/man5 if necessary. Tue May 27 09:31:39 1997 David Mosberger-Tang * backend/hp.c: Remove s7_range and s1_range. (attach): Add inquiry of min/max brightness/contrast and fill in dev->brightness_range and dev->contrast_range accordingly. (init_options): Use s->hw->brightness_range instead of s7_range. Use s->hw->contrast_range instead of s7_range. Disable contrast option if min value is same as max value. * backend/hp.h (struct HP_Device): Add members BRIGHTNESS_RANGE and CONTRAST_RANGE. * sanei/sanei_scsi.c (sanei_scsi_open): Add more debug info. Mon May 26 10:43:51 1997 David Mosberger-Tang * sanei/Makefile.in: Add definitions for VPATH, srcdir, top_srcdir, and top_builddir. (INCLUDES): Move -I directives from CPPFLAGS to this macro. Also search in $(srcdir), $(top_builddir)/include, and $(top_srcdir)/include. * lib/Makefile.in: Add definitions for VPATH, srcdir, top_srcdir, and top_builddir. (INCLUDES): Move -I directives from CPPFLAGS to this macro. Also search in $(srcdir), $(top_builddir)/include, and $(top_srcdir)/include. * include/Makefile.in: New file. * frontend/Makefile.in: Add definitions for VPATH, srcdir, top_srcdir, and top_builddir. (INCLUDES): Also search in $(srcdir), $(top_builddir)/include, and $(top_srcdir)/include. (install): Add $(srcdir) prefix to sane-style.rc. * doc/Makefile.in: Add definitions for VPATH, srcdir, top_srcdir, and top_builddir. (%.1 %.5): Use $^ instead of $* so source file can be found even when in a different directory. * configure.in (AC_OUTPUT): Create (dummy) include/Makefile to ensure include directory exists in build tree. * backend/Makefile.in: Add definitions for VPATH, srcdir, top_srcdir, and top_builddir. (INCLUDES): Also search in $(srcdir), $(top_builddir)/include, and $(top_srcdir)/include. (CONFIGS): Add $(srcdir) prefix. (%-s.c): Ditto. * Makefile.in (INSTALLED_INCLUDES): Mention include/sane/config.h. Add definitions for VPATH, srcdir, top_srcdir, and top_builddir. (INSTALLED_INCLUDES): Change include/sane to $(top_srcdir)/include/sane and use "addprefix" function to add prefix. Thu May 15 07:26:06 1997 David Mosberger-Tang * doc/sane-mustek.man (Model): Add entry for MFC-600CD. (Model): Mention AHA-2940. * frontend/xscanimage.c (scan_dialog): Move gsg_sync() to here (from scan_start()). * backend/mustek.c (init_options): Disable brightness/contrast for 1-pass scanners only (in color mode). Wed May 14 09:17:02 1997 David Mosberger-Tang * backend/mustek.c: Add ax_brightness_range, ax_contrast_range. (init_options): Use ax_brightness_range/ax_contrast_range for brightness/contrast option when using a 3-pass scanner. (encode_percentage): Remove extraneous division by 100.0. * doc/sane-mustek.man (Model): Add MFS-12000CX entry. Mon May 12 07:58:34 1997 David Mosberger-Tang * frontend/scanimage.c (main): Shorten help string so it fits in 80 characters. * frontend/preview.c (paint_image): Only increment height if height < p->image_height. Sat May 10 14:43:05 1997 David Mosberger-Tang * Version 0.57 released. * backend/mustek.c (attach): Fix range to 220x300mm. (init_options): Initialize OPT_PREVIEW and OPT_GRAY_PREVIEW. (sane_control_option): Handle OPT_PREVIEW and OPT_GRAY_PREVIEW. Do not allow OPT_NUM_OPTS to be set. (sane_get_parameters): Except for s->params.format and s->params.last_frame, do not change any parameters when scanning is in progress. (sane_start): Handle mono-chrome previewing. * backend/mustek.h (Mustek_Option): Add OPT_PREVIEW and OPT_GRAY_PREVIEW. * frontend/progress.c (progress_new): Fix position of progress bar window to (progress_x,progress_y). * frontend/gtkglue.c (panel_build): Skip Preview option. Thu May 8 19:39:48 1997 David Mosberger-Tang * doc/scanimage.man: New file. * doc/Makefile.in (SECT1): Mention scanimage.1. * backend/dll.conf: Add hp to dll.conf. Wed May 7 07:28:18 1997 David Mosberger-Tang * include/sane/sanei_scsi.h (SANEI_SCSI_Sense_Handler): New type. (sanei_scsi_close): Add SENSE_HANDLER argument (NULL means no special handler needed). * backend/hp.c (sane_start): Pass 0 as SENSE_HANDLER argument. (attach): Ditto. * backend/mustek.c (sense_handler): Rename from request_sense and simplify. Remove all explicit request-sense calls. * sanei/sanei_scsi.c: Include . (num_handlers_alloced): New variable. (sense_handler): Ditto. (sanei_scsi_open): Add SENSE_HANDLER argument. Stash it away in SENSE_HANDLER array if non-NULL. (sanei_scsi_cmd) [HAVE_SYS_SG]: Invoke SENSE_HANDLER[FD] if it exists and is non-NULL and we have a valid sense-buffer. (sanei_scsi_cmd) [HAVE_SYS_SCSI]: Ditto. (sanei_scsi_close): Reset SENSE_HANDLER[FD] to NULL if it exists. * frontend/xcam.c: Include . * configure.in: Add a big fat warning that PROBLEMS should be read before running any programs. * PROBLEMS: Add info on how to avoid damaging Mustek scanners. Mon May 5 07:06:54 1997 David Mosberger-Tang * doc/sane.tex (chapter{Contact Information}): Fix URL. * doc/net.tex: Add missing sections. Make into chapter. * backend/pint.README: Remove. * backend/dll.c (ASSERT_SPACE): Call malloc() when devlist is NULL. SunOS realloc() returns NULL otherwise. * backend/net.c (ASSERT_SPACE): Ditto. Sun May 4 09:21:48 1997 David Mosberger-Tang * frontend/saned.c (EXIT_SUCCESS): Define as 0 if not defined by stdlib.h. * frontend/scanimage.c: Include . * backend/qcam.c: Define PATH_MAX as 1024 if limits.h doesn't define it. * backend/dll.c: Ditto. * backend/hp.c: Ditto. * backend/mustek.c: Ditto. * backend/net.c: Ditto. * backend/pint.c: Ditto. * backend/qcam.c: Ditto. * frontend/xscanimage.c: Ditto. * frontend/xcam.c: Ditto. * frontend/preview.c: Ditto. * sanei/sanei_wire.c: Include . * sanei/sanei_net.c: Ditto. * sanei/sanei_codec_ascii.c: Ditto. * sanei/sanei_codec_bin.c: Ditto. * sanei/sanei_save_values.c: Ditto. * backend/net.c: Ditto. * frontend/saned.c: Ditto. * configure.in (AC_CHECK_HEADERS): Mention /usr/src/linux/include/scsi/sg.h. * include/sane/config.h.in: Add #undef HAVE__USR_SRC_LINUX_INCLUDE_SCSI_SG_H. * sanei/sanei_scsi.c [HAVE__USR_SRC_LINUX_INCLUDE_SCSI_SG_H]: Include "/usr/src/linux/include/scsi/sg. (sanei_scsi_cmd): Use Linux version if either HAVE_SCSI_SG_H or HAVE__USR_SRC_LINUX_INCLUDE_SCSI_SG_H are defined. * doc/xscanimage.man: Rename --no-shm to --no-xshm. Mon Apr 28 09:55:02 1997 David Mosberger-Tang * Version 0.56 released. * backend/qcam.c (sane_open): Move disabling of despeckle-filter for b&w cameras to here (from init_options()). * frontend/preferences.c: Add missing default value for FILENAME. * backend/qcam.h: Add QC_BW_SET_CONTRAST, QC_BW_AUTO_ADJUST_OFFSET, and QC_BW_GET_OFFSET. * backend/qcam.c (sane_start): Use QC_BW_SET_CONTRAST when dealing with B&W camera. Fix computation of QC_SET_NUM_H value. [Based on Guido Muensch's patches.] * frontend/scanimage.c (advance): Move out-of-memory error message from scan_it() to here. (scan_it): When height is known a priori, allocate one extra line to avoid realloc() when image has been filled. * backend/hp.c (BACKEND_NAME): Define as hp (reported by Henryk Paluch). * backend/mustek.c (request_sense): Increase result size from 4 to 16 bytes to shut up aha1542 driver (reported by Mathias Lautner). * frontend/scanimage.c (advance): Fix memory allocation logic (bug reported by Matthias Lautner). * doc/sane-dll.man: Clarify that pre-loaded backends are not affected by dll.conf. * doc/sane-pint.man: New file (contributed by Gordon Matzigkeit). * backend/dll.c (RTLD_NOW): Define as 0 if dlfcn.h doesn't define it. Wed Apr 23 23:03:47 1997 Gordon Matzigkeit * backend/Makefile.in, frontend/Makefile.in: Specify full filenames, rather than just the directory name for INSTALL destinations. This Automakeism helps debugging when the target directory doesn't exist. * include/sane/config.h.in (HAVE_SYS_SCANIO_H): Define, for PINT's sake. * backend/pint.c: Prefix sane includes with . * backend/dll.c (RTLD_NOW): Paramaterize, so that RTLD_LAZY is used if RTLD_NOW is not defined. (load): Try looking up the symbol with a leading underscore, if we can't find it the first time. Make dynamic loading conditional on HAVE_DLOPEN rather than the library and header file. * configure.in (enable_dynamic): Disable dynamic loading if the system doesn't have dlopen. Tue Apr 22 00:17:41 1997 David Mosberger-Tang * Version 0.55 released. * frontend/xscanimage.c (browse_filename_callback): Initialize FILENAME before calling gsg_get_filename(). * doc/sane.tex: Change obsolete \driver{DVIps} into DVIps document style option. * frontend/xscanimage.c (query): Update to new command name length. Mon Apr 21 08:27:28 1997 David Mosberger-Tang * doc/Makefile.in (install): Add rule to install man-pages. (%.1 %.5): Add rule to generate man-pages (by substituting actual paths). (SECT1): New variable. (SECT5): Ditto. (MANPAGES): Ditto. * doc/sane-pnm.man: New file. * backend/pnm.README: Deleted. * doc/sane-hp.man: New file. * backend/hp.README: Deleted. * doc/saned.man: New file. * frontend/saned.README: Deleted. * doc/sane-qcam.man: New file. * backend/qcam.README: Deleted. * doc/sane-mustek.man: New file. * backend/mustek.README: Deleted. * doc/sane-net.man: New file. * backend/net.README: Deleted. * doc/sane-dll.man: New file. * backend/dll.README: Deleted. * doc/xscanimage.man: New file. * frontend/xscanimage.README: Deleted. * frontend/preview.c (event_handler): Restore preview image only if preferences.preserve_preview is TRUE. * frontend/xscanimage.c (preview_options_dialog): Add an option to control whether the preview image should be preserved. * sanei/sanei_load_values.c (sanei_load_values): Copy string value into temporary buffer that is opt->size bytes long. Then pass copy to sane_control_option(). * AUTHORS (Backends): Updated according to recent changes. * doc/sane.tex (The SANE Application Programmer Interface (API)): Fix typo: replace () by {}. * PROJECTS (Backend): Update PINT entry. * frontend/xscanimage.c (prog_name): New variable. (input_available): Use prog_name instead of hardcoding "xscan". (preview_options_ok_callback): Ditto. (preview_options_dialog): Ditto. (usage): Ditto. (interface): Ditto. (main): Initialize prog_name. * frontend/scanimage.c: Update for new name (scanimage instead of scan). * frontend/Makefile.in (SCAN_OBJS): Rename sources to xscanimage.c and scanimage.c as well. Sun Apr 20 09:09:06 1997 David Mosberger-Tang * Version 0.54 released. * sanei/Makefile.in (LIBSANEI_LTOBJS): Remove @LTALLOCA@. (LIBSANEI_OBJS): Remove @ALLOCA@. * lib/Makefile.in: Invoke $(LIBTOOL) with --mode arg to avoid depending on command names. Prefix $(LIBTOOL) invocation with @-sign to reduce verbosity. * frontend/Makefile.in: Ditto. * backend/Makefile.in: Ditto. * sanei/Makefile.in: Ditto. * frontend/saned.c (_PATH_HEQUIV): Define if not defined yet.. * configure.in: Check for libgimp/gimp.h instead of -lgimp since -lgimp cannot be linked by itself (it has an unresolved reference to PLUG_IN_INFO). * lib/strndup.c (strndup): Include . * frontend/xscanimage.README: Update. * sanei/sanei_scsi.c: Add missing "int" type in front of sanei_scsi_max_request_size. * configure.in: Check for -lintl, -lsocket, and -lnsl for braindamaged SVR4 systems. * frontend/scan.c (scan_it): Initialize min with 0xff, not ~0 (Sun CC complains otherwise). * backend/Makefile.in: Use "ln -f -s", not "ln -sf", Solaris doesn't like the former. Grrr... * lib/alloca.c: Don't use xmalloc(). * frontend/saned.c (store_reclen): Declare BUF as a pointer to SANE_Byte (not char). (do_scan): Ditto. * frontend/scan.c (fetch_options): Cast opt->name to (char *) to suppress (spurious) warning. * backend/dll.c (sane_init): Skip preloaded backends with no name. * backend/hp.c (inquire): Cast BUF to (char *) to avoid warning. (sane_read): Change type of NREAD to size_t (from ssize_t). * include/sane/sanei_backend.h (u_int16_t): Define u_int8_t, u_int16_t, and u_int32_t if necessary. * include/sane/sane.h (sane_strstatus): Remove non-ANSI "const". * backend/sane_strstatus.c (sane_strstatus): Ditto * include/sane/sanei_debug.h (DBG): Define alternate version for non-GNU C compilers. * sanei/sanei_init_debug.c (sanei_debug) [!__GNUC__]: New function. (max_level) [!__GNUC__]: New variable. * include/sane/sanei_wire.h: Fix typo in prototype for sanei_w_value_type. * configure.in: Test for gtk_check_button_new() in libgtk. * lib/Makefile.in (CPPFLAGS): Add -I../include/sane. * frontend/scan.c (STRIP_HEIGHT): New macro. (advance): Use above macro instead of hard-coding value. (scan_it): Ditto. (scan_it): Print error when out of memory. * frontend/sane-style.rc: Make the curve in the graph red. * frontend/preview.c (XSERVER_WITH_BUGGY_VISUALS): Define for now. (preview_new): Avoid push_visual()/get_visual() since it seems to cause problems with some XFree86 servers (and may be others as well). * frontend/preferences.h: Add member ADVANCED. * frontend/preferences.c: Initialize member ADVANCED and add an option descriptor for it. * frontend/gtkglue.c (panel_build): Layout panel different: all options are now in one column (advanced options are at the bottom). The graph widget (if any), will be shown in a separate column to the right of the options. Display a group only when there is at least one option in it. Create graph widget if there is at least one vector-valued option. (gsg_sync): New function. (gsg_set_advanced): Ditto. (gsg_message_dialog_active): Rename from SHOWING_MESSAGE and make global. * frontend/gtkglue.h (GSGDialog): Rename member NOTEBOOK to MAIN_HBOX. Add members ADVANCED_VBOX and ADVANCED. * frontend/gtkglue.c (group_new): New function. (curve_new): Ditto. (vector_new): Ditto. * backend/mustek.c (init_options): Initialize OPT_CUSTOM_GAMMA. (sane_control_option): Handle OPT_CUSTOM_GAMMA. * backend/mustek.h (OPT_CUSTOM_GAMMA): New option. * configure.in (AC_CONFIG_HEADER): Update for new location. * include/sane/config.h.in: Move to here from include/config.h. * *.[ch]: Replace with (except for files in lib. * frontend/saned.README: saned moved from bin/saned to sbin/saned. * configure.in (AC_CHECK_HEADERS): Check for gtk/gtkcurve.h. * backend/mustek.c (init_options): Undo changes of April 18th. * frontend/Makefile.in (install): Install $(BINPROGS) in $(bindir) and $(SBINPROGS) in $(sbindir). (SBINPROGS): New variable. (BINPROGS): Ditto. (PROGRAMS): Define as concatenation of the two vars above. (install): Create $(bindir) and $(sbindir) if necessary. Sat Apr 19 18:21:02 1997 David Mosberger-Tang * frontend/Makefile.in (scanimage): Rename "scan" binary to "scanimage" to avoid name-collision with MH's scan command. * backend/mustek.c (gamma_correction): Update according to new gamma-table handling. * backend/mustek.h: Declare gamma_table as 4 arrays of 256 words each. * backend/mustek.c (sane_control_option): Add missing return after setting OPT_GAMMA_VECTOR. * frontend/progress.h: Protect against multiple inclusion using macro progress_h, not __PROGRESS_H__ (names starting with underscores are reserved for libc/system). * frontend/gtkglue.c: Remove include of . * frontend/xcam.c: Ditto. * frontend/xscan.c: Ditto. * frontend/gtkglue.h: Include instead of . * frontend/progress.c: Ditto. * Makefile.in (install): Install headerfiles. (INSTALLED_INCLUDES): List of header files that need to be installed. Sat Apr 19 18:15:03 1997 Michael K. Johnson * */*.[ch]: Prefix sane-includes with . include/sane*: Move to include/sane subdirectory. Sat Apr 19 08:25:36 1997 David Mosberger-Tang * frontend/gtkglue.c (set_option): Add missing return and use snprintf instead of sprintf. * frontend/xscan.c (update_param): Multiply size by 3 if we're dealing with a frame from a 3-pass image. Fri Apr 18 07:28:46 1997 David Mosberger-Tang * backend/mustek.c (init_options): Mark all vector options as SANE_CAP_ADVANCED. * frontend/scan.c (scan_it): Fail if advance() fails. Thu Apr 17 08:59:00 1997 David Mosberger-Tang * backend/mustek.c (sane_read): Add debug statement. (reader_process): Remove extraneous if-statement that had the effect of not passing along any data in 3-pass mode. Wed Apr 16 09:16:01 1997 David Mosberger-Tang * frontend/preview.h: Add member CANCEL. * frontend/preview.c (scan_start): Make p->cancel sensitive. (scan_done): Make p->cancel insensitive. (preview_new): Set p->cancel insensitive. * frontend/preferences.h (Preferences): Add member "filename". * frontend/preferences.c: Initialize new member "filename". Declare member "filename" as a string. * frontend/xscan.c (device_dialog): Rename "Files" to "File". (quit_xscan): Call pref_xscan_save() before exiting. Replace global variable filename by preferences.filename. * frontend/xcam.c (main): Ditto. * PROBLEMS: New file. * frontend/gtkglue.c (scale_new): Set the adjustment page size to 0.0. Make horizontal scale 200 pixels wide. * frontend/xscan.c (device_dialog): Move gtk_widget_show (ScanWin.shell) after pref_device_restore (0, 0). (pref_device_restore): Make arg-less since callback arguments must not be used. (device_dialog): Remove dummy args from call to pref_device_restore(). * include/saneopts.h (SANE_TITLE_SCAN_TL_X, SANE_TITLE_SCAN_TL_Y, SANE_TITLE_SCAN_BR_X, SANE_TITLE_SCAN_BR_Y): Shorten titles. * frontend/xcam.c (main): Move gtk_widget_show(win.shell) to end of function. * backend/mustek.c (attach): Turn on MUSTEK_FLAG_USE_EIGHTS for MFS-12000CX. (sane_get_parameters): Must not depend on s->mode in this routine, since s->mode is valid only once scanning has begun. Tue Apr 15 23:06:17 1997 David Mosberger-Tang * backend/mustek.c (sane_start): Issue SCAN_AREA and BACKTRACK commands even during second and third pass. (sane_get_parameters): Be sure to properly update the params.format member during second and third pass. Sat Apr 12 11:40:52 1997 David Mosberger-Tang * frontend/scan.c (STRIP_HEIGHT): New macro. (scan_it): Fix typo: test parm.lines instead of image.height. * backend/pnm.c (hand_scanner): New variable. (sane_control_option): Handle setting/reading of HAND_SCANNER option. (getparmfromfile): Set parms.lines to -1 when simulating hand-scanner. (sod): Add hand-scanner option descriptor. * frontend/gtkglue.c (panel_build): Move gtk_widget_show (notebook) to the very of this function. This works around the notebook redrawing problems. Define WORKING_NOTEBOOK. * frontend/xscan.c (interface): Call quit_xscan() if device_dialog() did not end up with a non-NULL dialog. (quit_xscan): Add missing exit() call. * backend/mustek.c (attach): Print more debug info on detected scanner (1 vs 3-pass, ADF & TA support). (start_scan): Fix typo in code selecting the three-pass color filter. Sun Apr 13 23:53:02 1997 Gordon Matzigkeit * backend/pint.c, backend/pint.h, backend/pint.README: Added to distribution. Fri Apr 11 22:04:17 1997 David Mosberger-Tang * Version 0.53 released. * frontend/xscan.c: Default to "out.pnm" instead of "out.ppm". * backend/mustek.c (get_image_status): Add debug message. (sane_start): Only divide pixels_per_line by three if we're doing a one-pass color scan. (do_eof): New function. (sane_read): Call do_eof() when done with a frame. (do_cancel): Call do_eof(). * frontend/scan.c (write_pnm_header): New function. (advance): Ditto. (scan_it): Modify to support three-pass scanning and scanning of images whose height is not known a priori (hand-held scanners). Wed Apr 9 14:39:59 1997 David Mosberger-Tang * frontend/xscan.c (ok_choose_dialog_callback): Make argument-free. (select_device_callback): Declare event as second argument and call ok_choose_dialog_callback() if the callback was invoked through a double-click. (choose_device): Register select_device_callback() as "button_press_event" handler instead of as "clicked" so we can catch double-clicks. Tue Apr 8 10:31:47 1997 David Mosberger-Tang * frontend/xscan.c (init): Enable gtk_rc_parse() code (GTK-0.99.7 requires patch for gtkstyle.c). * backend/hp.c (attach): Fix parenthization to shut up gcc. Mon Apr 7 13:08:45 1997 David Mosberger-Tang * frontend/xscan.c (preview_options_dialog): Rename "changed_text" to "changed" to reflect latest GTK version. (device_dialog): Ditto. * frontend/gtkglue.c (text_entry_new): Ditto. * Version 0.52 released. * AUTHORS: Various updates. * backend/mustek.README: Add Adaptec info. * backend/mustek.c (sane_start): Call wait_ready() before any other scanner command. (wait_ready): New function. (attach): Call wait_ready() instead of open-coding it. * doc/sane.tex (subsection{\code{sane\_strstatus}}): Include net.tex. * doc/net.tex: New file. Thu Apr 3 08:51:13 1997 David Mosberger-Tang * frontend/xscan.c (update_param): Change type of SIZE to u_long (from long) since under NetBSD there seem to be some odd sign problems otherwise (compiler bug?). Wed Apr 2 07:15:41 1997 David Mosberger-Tang * backend/mustek.c (sane_read): Always deliver EOF after reading 0 bytes. * backend/qcam.c (reader_process): Fine-tune pixel interpolation. (despeckle32): Ignore last red pixel in every line (it's always black). Switch red and blue pixel location (the Connectix docs are wrong). * backend/mustek.c (start_scan): Fix typo that caused start[4] to be set incorrectly. * frontend/preview.c: Include to get PATH_MAX. Tue Apr 1 18:45:14 1997 David Mosberger-Tang * backend/qcam.c (despeckle): New function. (despeckle32): Ditto. (init_options): Initialize OPT_DESPECKLE. (reader_process): Add despeckling support. (sane_control_option): Ditto. (sane_start): Initialize req.despeckle. * backend/qcam.h: New member DESPECKLE. * frontend/preview.c (restore_option): Fix typo: it should be SANE_ACTION_SET_VALUE (not _get_). * backend/mustek.c (attach): Turn on MUSTEK_FLAG_USE_EIGHTS for MFS-06000CX. * backend/hp.c (attach): Allow both TYPE_PROCESSOR (0x03) and TYPE_SCANNER (0x06) as the first inquiry byte---some scanners use the former, others the latter. * Version 0.51 released. * configure.in: Check for gtk_tooltips_new in libgtk. Mon Mar 31 14:42:56 1997 Gordon Matzigkeit * frontend/scan.c (main): Added full_optstring, to allow getopt to do full parsing of the option arguments. * backend/pnm.c (getparmfromfile): Need to round up the number of bytes per line when scanning bitmaps. Mon Mar 31 07:48:50 1997 David Mosberger-Tang * backend/qcam.c (attach): Send a dummy black-level command. The first black-level command aftera camera reset doesn't appear to "take." * lib/strndup.c: Correct typo: HAVE_STRDUP -> HAVE_STRNDUP. * frontend/xscan.c (pref_set_tooltips): New function. (pref_build_menu): Build tooltip submenu. * frontend/preferences.h: New member TOOLTIPS_ENABLED. * frontend/preferences.c: Add entry for TOOLTIPS_ENABLED. * frontend/gtkglue.h: Add members TOOLTIPS, TOOLTIPS_FG, and TOOLTIPS_BG. * frontend/gtkglue.c (set_tooltip): New function. (autobutton_new): Add argument TOOLTIPS and add button-description via set_tooltip(). (button_new): New args TOOLTIPS & DESC. Set DESC as tooltip help string. (scale_new): Ditto. (option_menu_new): Ditto. (text_entry_new): Ditto. (panel_destroy): Destroy dialog tooltips as well. (panel_build): Create tooltips and allocate colors for it; pass new args to above functions. (gsg_set_tooltips): New function. * Version 0.5 released. * backend/Makefile.in (install): Add missing double-quotes to $(LIBS) and $(CONFIGS). Also create a symlink for libsane.a. * ltconfig: Revert back to version 0.9. * ltmain.sh: Ditto. * frontend/scan.c (print_option): Substitute our own help string for -x and -y options. (main): Remove BROKEN_GETOPT code. * frontend/Makefile.in (CPPFLAGS): Undo yesterday's change. * lib/getopt1.c: Ditto. * lib/getopt.c: Ditto. * frontend/scan.c (main): Add some clever getopt () hackery to work around multi-scan getopt () bug (patch by Gord Matzigkeit). * backend/qcam.README: Correct path of configuration file. Sun Mar 30 21:42:12 1997 David Mosberger-Tang * frontend/Makefile.in (CPPFLAGS): Add back -DBROKEN_GETOPT. * frontend/scan.c (main): Reinstantiate BROKEN_GETOPT code. getopt_long() is _STILL_ broken. ;-( * lib/getopt.c: Use this code if __GNU_LIBRARY__ <= 6. Early versions of glibc had a buggy getopt(). * lib/getopt1.c: Ditto. * frontend/Makefile.in (xscan): Mention $(LIBSANEI). * frontend/xscan.c: Include , not . * Makefile.in (distclean): Mention libtool. * configure.in (AC_CHECK_FUNCS): Ditto. * include/config.h.in: Add undef of STRSEP. * sanei/sane_strstatus.c: Move to backend directory to minimize chance of confusing this with a SANE-internal routine. * sanei/Makefile.in (LIBSANEI_OBJS): Remove sane_strstatus.o. (LIBSANEI_LTOBJS): Remove sane_strstatus.lo. * backend/Makefile.in (libsane-%.la): Mention sane_strstatus.lo explicitly (every SANE backend library MUST define this function so we can switch backends at will). * frontend/Makefile.in (SCAN_OBJS): Remove getopt.o, getopt1.o. * Makefile.in (SUBDIRS): Mention lib as first directory. * configure.in (AC_OUTPUT): Mention lib/Makefile. * lib/strsep.c: New file. * lib/strndup.c: Ditto. * lib/Makefile.in: Ditto. * sanei/test_wire.c (main): Initialize codecs through sanei_w_init(). * sanei/sanei_codec_bin.c (sanei_codec_bin_init): Ditto. * sanei/sanei_codec_ascii.c (sanei_codec_ascii_init): Remove w->buffer initalization---this function should be called through sanei_w_init () only. * sanei/sanei_scsi.c: Rename _sanei_scsi_max_request_size to sanei_scsi_max_request_size. * backend/mustek.c (reader_process): Rename macro SANEI_SCSI_MAX_REQUEST_SIZE to variable sanei_scsi_max_request_size. * include/sanei_scsi.h: Rename _sanei_scsi_max_request_size to sanei_scsi_max_request_size (names with a leading underscore are reserved for libc/system). (SANEI_SCSI_MAX_REQUEST_SIZE): Remove macro. * frontend/scan.c (main): Add a brief description of the program. * frontend/getopt.c: Move to lib. * frontend/getopt1.c: Ditto. * backend/pnm.c (enum ppm_type): Remove non-ANSI trailing comma. Correct struct indentation. * include/lalloca.h: Isolate alloca cruft in this file. * frontend/gtkglue.c: Include lalloca.h in place of alloca hack. * frontend/dll.c: Ditto. * frontend/scan.c: Ditto. * sanei/sanei_load_values.c: Ditto. * sanei/sanei_scsi.c: Ditto. Sat Mar 29 00:14:37 1997 Gordon Matzigkeit * backend/pnm.c (sane_read): Slurp in RGB files all at once, rather than limping along three bytes at a time. * frontend/xscan.c (advance): Only define if HAVE_LIBGIMP. * backend/pnm.c (sane_read, sane_start, getparmfromfile): Handle grayscale and monochrome bitmapped PNM files. * frontend/scan.c (main): Remove BROKEN_GETOPT workaround---the new getopt() works properly when making multiple passes over the argument list. Fri Mar 28 21:33:36 1997 Gordon Matzigkeit * configure.in: Make the QuickCam device conditional on whether or not the ioperm function exists. * sanei/sanei_scsi.c: created a stubbed version of sanei_scsi_cmd. * frontend/scan.c (main): Only print error messages to stderr. Use fputs or fputc when it is possible to avoid printf. * sanei/sanei_wire.c (sanei_w_array, sanei_w_ptr, sanei_w_init), sanei/sanei_save_values.c (sanei_save_values), sanei/sanei_codec_ascii.c (ascii_w_string): Return an error if malloc fails. * sanei/test_wire.c (main): Added --readonly flag to help test wire robustness. * backend/Makefile.in (LIBSANEI): Deleted the `EXTRA' variable, and used the convenience library, ../sanei/libsanei.a. Sat Mar 29 10:19:58 1997 David Mosberger-Tang * doc/sane.tex: Change version from 0.42 to 0.5. * README: Add pointer to frontend & backend specific README files. * backend/net.c (connect_dev): Ensure dev->ctl gets closed and reset to -1 in case of failure. * configure.in: Look for gtk_preview_new() in libgtk so we can ignore old GTK versions against which we can't build. * (config.sub): Replace with version that comes with autoconf-2.12. The version that came with libtool-0.9 doesn't like i686-unknown-linux. Fri Mar 28 13:12:17 1997 David Mosberger-Tang * doc/sane.tex (subsection{sane_get_select_fd}): Remove extraneous backslash. * backend/stubs.c (sane_cancel): Don't return anything. * include/sane.h: Remove trailing comma (for -pedantic). * frontend/xscan.c (interface): Complain when trying to call a device that's unavailable. (select_device_callback): Correct prototype. * frontend/xcam.c (main): Remove unused variable `label.' * backend/net.c (sane_cancel): Call SANE_NET_CANCEL. (sane_start): Handle failure of SANE_NET_START. * frontend/saned.c (process_request): Implement SANE_NET_CANCEL. * frontend/gtkglue.h (GSGDialogElement): Remove member "label". * frontend/gtkglue.c: Update accordingly. (button_new): Use gtk_check_button_new_with_label () to simplify this function. * frontend/gtkglue.c (unit_string): New function. (panel_build): Append unit_string () to option's title if the unit is not SANE_UNIT_NONE. Use "title" instead of opt->title when creating widgets. * frontend/preview.c (preview_update): Compute width/height as (max - min + eps) where eps is 1 for integer coordinates and SANE_UNFIX(1) for fixed-point coordinates. (display_partial_image): Support different scaling in x/y direction. In the case where surface_unit == SANE_UNIT_PIXEL, compute xscale/yscale based on the ratio between the _expected_ width/height and the actual width/height. If they're not equal, the backend must be doing some scaling (e.g., transfer-scale in the qcam backend). * frontend/xscan.c (init): Disable gtk_rc_parse () calls for now since gtk styles seem to be a bit buggy right now. (scan_done): Remove input_tag if it is >= 0. * frontend/gtkglue.c: Include . (panel_build): For fixed-point range-constraints scale min/max/quant based on the preferred metric. (scale_update): Ditto. (gsg_update_scan_window): Ditto. * backend/mustek.c (sane_control_option): For OPT_GAMMA_VECTOR_* options, fail with SANE_STATUS_INVAL if the vector has zero length (i.e., hasn't been set yet). * frontend/progress.c (progress_new): Don't make Cancel the default-button---it's too easy to cancel by mistake otherwise. * frontend/sane-style.rc: New file. * frontend/xscan.c (init): New function. Parse gtk_rc_parse () on /usr/local/share/sane-style.rc and ~/.sane/sane-style.rc if those files exist. * configure.in (CPPFLAGS): Add PATH_SANE_DATA_DIR. * frontend/gtkglue.c (gsg_warning): New function. (gsg_message): New function. (gsg_error): Implement in terms of gsg_message (). Thu Mar 27 16:52:27 1997 David Mosberger-Tang * frontend/gtkglue.c (gsg_make_path): Terminate buffer with '\0' before attempting mkdir(). * frontend/xcam.c (main): Use gtk_menu_item_new_label () instead of gtk_label_new ()/gtk_menu_item_new (). * frontend/preview.c (establish_selection): Invoke param_change_callback only if non-NULL. (preview_update): Convert scale range to inches if that's user's preference. * frontend/xcam.c (delayed_switch): New function. (device_activate_callback): Call delayed_switch() rather than executing that code directly. Pete tells me GTK is not designed to have a widget rebuilt while a submenu is active. * frontend/gtkglue.c (panel_build): Avoid using notebook widget until GTK+ has been fixed. (idle_handler): New function: rebuild dialog if dialog->rebuild is set. (set_option): Set dialog->rebuild to TRUE and schedule idle_handler() rather than directly calling panel_rebuild(). Pete tells me GTK is not designed to have a widget rebuilt while a submenu is active. (gsg_refresh_dialog): Ditto. Thu Mar 27 14:54:23 1997 Gordon Matzigkeit * backend/Makefile.in: Converted all for statements that might be empty to use a temporary list variable. * frontend/Makefile.in (scan): Added getopt.o, getopt1.o to scan dependencies. * frontend/getopt.c, frontend/getopt1.c, include/getopt.h: Added to the distribution. * backend/net.c (connect_dev), frontend/saned.c (main): Don't use TCP_NODELAY if it has not been defined. Look up tcp protocol number if SOL_TCP is not defined. * sanei/test_wire.c (main): lseek after changing direction, or the direction change ends up at the end of the output file. Added option parsing, a usage message, and other cleanups. * sanei/sanei_codec_ascii.c (sanei_codec_ascii_init), sanei/sanei_codec_bin.c (sanei_codec_bin_init): Initialize the wire if it has a 0-length buffer. * sanei/sanei_wire.c (sanei_w_init): Only call the codec_init_func if it is not NULL. * sanei/sanei_scsi.c: Parameterize inclusion of generic SCSI headers based on HAVE_SCSI_SG_H, HAVE_SYS_SCSI_H, or HAVE_SYS_SCSIIO_H. * configure.in (AC_CHECK_HEADERS): Check for scsi/sg.h, sys/scsi.h, and sys/scsiio.h. * sanei/sanei_load_values.c, sanei/sanei_scsi.c, backend/dll.c: Replaced unconditional alloca.h include with the cruft suggested by the Autoconf manual. * sanei/alloca.c: Add to distribution. * sanei/Makefile.in: Eliminate GNU make dependencies, and use @ALLOCA@ and @LTALLOCA@ as appropriate. * configure.in (AM_FUNC_ALLOCA): Use it. * aclocal.m4 (AM_FUNC_ALLOCA): Define it. Wed Mar 26 17:25:49 1997 David Mosberger-Tang * sanei/sanei_wire.c (sanei_w_status): Update *v only when decoding. (sanei_w_bool): Ditto. (sanei_w_constraint_type): Ditto. (sanei_w_value_type): Ditto. (sanei_w_unit): Ditto. (sanei_w_action): Ditto. (sanei_w_frame): Ditto. * frontend/xscan.c (quit_xscan): Always invoke gtk_main_quit() (not just in standalone mode). (quit_callback): Correct prototype. * backend/pnm.c (sod): Fix doc-string for option "three-pass". * frontend/xscan.c (advance): New function. (input_available): Support 1 bpp frames. Call advance() instead of open-coding it. Add RED/GREEN/BLUE frame support in GIMP mode. Complain when encountering unknown depth. (ScanWin): add member first_frame. (scan_start): Initialize first_frame and frame_offset in GIMP mode. * Integrate force unidirectional mode patch by Guido Muesch : * backend/qcam.c (attach): Force uni-directional mode if port address starts with `u'. (attach): Add debug statement to print QCam version. * Integrate 3-pass patches by Heiko : * backend/mustek.cb (scan_area_and_windows): Use 1/8" mode when MUSTEK_FLAG_USE_EIGHTS is on (instead of pixel unit). (mode_select): Set bit 3 in mode byte only if MUSTEK_FLAG_USE_EIGHTS is off. (encode_resolution): Don't do funky encoding for 3-pass scanners in 10% resolution mode. It seems the docs are wrong and the actual scanners require a much simpler setup. * frontend/preview.h: Add include of . Sat Mar 22 16:37:34 1997 David Mosberger-Tang * frontend/xscan.c (input_available): Reset ScanWin.x to zero at the end of each line. (input_available): Reset tile_offset to zero after updating a tile. (scan_done): Call gimp_display_new() to display the newly acquired image. Draw final few lines of image if image height is not a multiple of the tile-height. * frontend/saned.c (process_request): quit when receiving unexpected procedure number---ignoring is dangerous. * backend/dll.c (load): Fix off-by-one bug in allocation of libname. * frontend/xcam.c (prompt_for_device_name): Move grab-default call behind signal_connect since otherwise the default action won't work. * frontend/xscan.c (choose_device): Let "Ok" button be the one that gets activated by default (by hitting the Return key). * frontend/preview.c (preview_destroy): Rename save_preview_image to preserve_preview_image. * frontend/preview.h: Ditto. * frontend/preview.c (restore_preview_image): New function. (preview_destroy): Save preview image if it exists. (preview_scan): New parameter SELECTION_ONLY. If true, preview-scanning is limited to the selected area. * frontend/xscan.c (scan_done): Call quit_xscan() to exit. (quit_xscan): Destroy preview if ScanWin.preview is non-NULL. * frontend/preview.c (draw_selection): Don't draw if p->gc is NULL (i.e., if windows aren't mapped yet). * frontend/xscan.c: Move HAVE_LIBGIMP-specific code further down (past the type & global variable decls). * configure.in (CPPFLAGS): Make --disable-shared impy --enable-preload. * include/config.h.in: Add HAVE_STRNDUP. Fri Mar 21 00:01:00 1997 David Mosberger-Tang * sanei/sanei_scsi.c (sanei_scsi_cmd): Implemented FreeBSD version (untested). * sanei/sanei_load_values.c: Include * frontend/preview.c (update_selection): New function to set selection based on current scan-area option values. (preview_update): Call update_selection (). * sanei/sanei_save_values.c: Add copyright notice. * sanei/sanei_load_values.c: Add copyright notice and explanation of algorithm. (BITS_PER_LONG): New macro. (SET): Ditto. (IS_SET): Ditto. (sanei_load_values): Add variables caused_reload and keep_going to keep track of what options caused a reload_option and whether the whole option-value setting process should be repeated. * include/config.h.in: Add #undef HAVE_LIBGIMP. * frontend/xscan.c: Put all GIMP-dependencies inside #ifdef HAVE_LIBGIMP. Include . * sanei/sanei_wire.c (sanei_w_init): Initialize status to 0. (sanei_w_free): Preserve existing direction and do not flush i/o. (sanei_w_space): Return EINVAL if reading 0 bytes. Thu Mar 20 09:11:21 1997 David Mosberger-Tang * frontend/gtkglue.h (dev_name): New member. * frontend/gtkglue.c (gsg_destroy_dialog): free dialog->dev_name. (gsg_create_dialog): Keep copy of device name in dev_name member. * include/sanei_backend.h: Move stubs into backend/stubs.c and define redirection macros only if STUBS is not defined. * backend/stubs.c: New file. * configure.in (AC_PROG_RANLIB): Removed. No longer needed with libtool. * (frontend/Makefile.in): Adapt to libtool. * (backend/Makefile.in): Ditto. * (sanei/Makefile.in): Ditto. * backend/mustek.c (attach): Check result[0] == 0x06 to find out whether we're dealing with a scanner (rather than attempting to find the string "SCANNER" ). * backend/hp.c (sane_read): Initialize nread with max_len, not *len. (sane_get_parameters): Add missing `== 0' comparison for strcmp() calls. (inquire): Generalize so we can inquire any quantity and improve parsing. (attach): Use reasonable default value if inquiry of a quantity fails. (sane_start): Report error if inquiry of a scan parameter fails. Wed Mar 19 18:03:38 1997 David Mosberger-Tang * frontend/xcam.c (prompt_for_device_name): grab default for OK button (doesn't seem to work, though). * frontend/progress.c (progress_new): Connect progress_cancel() via signal_connect(), not signal_connect_object() and pass P, not P->SHELL. (progress_cancel): Don't free progress structure (must be done by app). * frontend/xscan.c (input_available): New function. (scan_done): Ditto. (scan_start): Ditto. (progress_cancel): Simply call sane_cancel(). (scan_dialog): Moved most of the code to above functions. * frontend/gtkglue.c (get_filename_button_clicked): New function. (gsg_get_filename): Ditto. * frontend/xscan.c (scan_dialog): Do not quit xscan when in standalone mode. (scan_dialog): Prompt for output filename in standalone mode. (filename): New variable. * backend/pnm.c (sane_control_option): Remove "pnm." prefix in debug messages. * backend/qcam.c (sane_cancel): Remove "qcam." prefix in debug messages. * backend/mustek.c: Remove "mustek." prefix in debug messages. Tue Mar 18 17:30:29 1997 David Mosberger-Tang * backend/mustek.c (sane_close): Remove superfluous "[mustek]" in debug string. * sanei/sanei_scsi.c (sanei_scsi_cmd): Implement HP-UX version of this function (untested). * backend/qcam.c (reader_process): Enable code to handle 4 bpp and 6 bpp cases. Thu Mar 13 13:15:21 1997 David Mosberger-Tang * doc/sane.tex (subsection{Scan Area Options}): Attempt to clarify the meaning of "top-left" corner. Tue Mar 11 07:12:53 1997 David Mosberger-Tang * frontend/gtkglue.c (panel_build): Fix typo in matching of "resolution" option (missing == 0). Mon Mar 10 20:19:06 1997 David Mosberger-Tang * Announced pre0.5 snapshot. * backend/qcam.h: Make transfer-scale part of the "mode" group. * backend/qcam.c (sane_read): Moved return of SANE_STATUS_EOF behind test for delivering EOF. * frontend/xscan.c (device_dialog): Relabel "Cancel" button to "Quit". * backend/qcam.c (init_options): Set SANE_CAP_ADVANCED for geometry group. * backend/mustek.c (init_options): Ditto. * backend/pnm.c (pass, three_pass): New variables. (sod): Add option "three-pass" to simulate a three-pass scanner. (sane_control_option): Add support for three_pass option. (getparmfromfile): In three-pass mode, set format and last_frame based on `pass'. (sane_start): If input file is open already, fail unless we're scanning the next frame of a three-pass image. (sane_read): Handle three-pass case. Sun Mar 9 13:20:19 1997 David Mosberger-Tang * frontend/gtkglue.c (text_entry_callback): Only call gtk_entry_set_text() if the backend modified the string. Otherwise, infinite recursion occurs on any change (since the change results in a change...). * frontend/xcam.c (next_frame): Reset win.remaining to zero. * backend/qcam.c (init_options): Set size of "resolution" option to sizeof("High"). * doc/sane.tex (subsection{Scan Area Options}): Allow word-list as a constraint. * configure.in (CFLAGS): Add -D_GNU_SOURCE to CFLAGS since we now depend on snprintf (). * frontend/xscan.c (device_dialog): Fix typo: PixelHeight should be > 0, not == 0 in order for us to create the preview window. (device_dialog): Ditto. (device_dialog): Add ScanWin.preview to frame container. Sat Mar 8 10:19:41 1997 David Mosberger-Tang * doc/sane.tex (subsection{sane_get_select_fd}): Clarify what must happen when the end of a frame has been reached. * frontend/xcam.c (input_available): Stop when sane_read() returns 0 bytes. Fix typo: win.data needs to be set to dst, not src, on exit. * backend/qcam.h (read_fd): New member. * backend/qcam.c (sane_get_select_fd): Return read_fd instead of from_child. (sane_open): Initialize s->read_fd to -1. (sane_close): Close s->select_fd if >= 0. (sane_start): Make read_fd dup() of s->from_child. (sane_cancel): Close s->read_fd if >= 0. (sane_set_io_mode): Modify s->read_fd instead of s->from_child. * frontend/scan.c: Include for NELEMS macro. * frontend/xcam.c: Ditto. * configure.in (CPPFLAGS): Add defines for V_MAJOR and V_MINOR. (V_MAJOR, V_MINOR): New variables. * sanei/Makefile.in (SANEI_OBJS): Mention sanei_net.o. * include/sanei_net.h: New file (split from sanei_wire.h). * sanei/sanei_net.c: New file (split from sanei_wire.c). * configure.in: Add -Wall to CFLAGS when using gcc. * frontend/Makefile.in (saned): List -lsanei before -lsane so saned never depends on sanei_*-routines that may be present in -lsane (the only dependencies for that library must be on sane_* names). * backend/Makefile.in (LIBS): Remove -ldl (configure will add it if available). (SHLDFLAGS): Use -usanei_init instead of --whole-archive option. (SHLDPOSTFLAGS): Remove --no-whole-archive. * backend/dll.c: Add include of . (DYNAMIC): New macro. Defined it dynamic loading support is available and enabled. Include only when DYNAMIC is defined. (load): Make no-op when DYNAMIC is not defined. (sane_exit): Never call dlclose() if DYNAMIC is not defined. * include/sanei_scsi.h: Rename from genscsi.h and rename prefix from `genscsi_' to `sanei_scsi_'. * sanei/sanei_scsi.c: Ditto. * backend/mustek.c: Rename `genscsi_' to `sanei_scsi_'. * configure.in: Check for availability of -ldl and . Fri Mar 7 15:21:38 1997 David Mosberger-Tang * sanei/sanei_wire.c: New file. * sanei/sanei_codec_bin.c: Ditto. * sanei/sanei_codec_ascii.c: Ditto. * backend/dll.c (sane_exit): Call dlclose() only if handle is non-NULL. (sane_init): Chain together preloaded backends. * backend/qcam.c (attach): Make static. (sighandler): Ditto. (qc_getstatus): Ditto. * backend/mustek.c (attach): Ditto. * backend/dll.c (op_name): Remove `sane_' prefix. * sanei/sanei_init_debug.c (sanei_init_debug): Upcase backend name when building environment variable name. * backend/genscsi.c: Make use of sanei_debug.h. * backend/mustek.c: Adapt to use sanei_backend.h. * backend/net.c: Ditto. * backend/pnm.c: Ditto. * backend/qcam.c: Ditto. * backend/dll.c (sane_exit): Fix typo that resulted in the last backend not being exited. Thu Mar 6 21:25:27 1997 David Mosberger-Tang * doc/sane.tex (subsection{\code{sane\_init}}): Allow authorization callbacks during sane_start() but not during sane_read(). Tue Mar 4 21:04:16 1997 David Mosberger-Tang * backend/mustek.c (sane_close): Print debug message when trying to close an unknown handle. Thu Feb 27 18:10:20 1997 David Mosberger-Tang * doc/sane.tex (chapter{Contact Information}\label{chap:contact}): Add `listserv.' to mailing list address---seems to be needed for subscriptions etc. Sun Feb 9 12:54:20 1997 David Mosberger-Tang * backend/mustek.README: Add pointer to Ingmar's 53c400 patch. Sat Feb 8 11:49:24 1997 David Mosberger-Tang * backend/mustek.c (line_distance): Add more debug output. Fri Feb 7 19:21:36 1997 David Mosberger-Tang * backend/mustek.c (line_distance): Remove erroneous #ifdef around FACTOR and COLOR. * doc/sane.tex (subsection{sane_init}): Make username/password fixed length arrays so no malloc/free magic is needed. (subsection{Preview Mode Option}): New section. * include/sane.h: Ditto. * include/sane.h (SANE_Char): New type. Tue Feb 4 18:47:52 1997 David Mosberger-Tang * backend/mustek.c (sane_get_parameters): Correct typo: replace s->pass with s->params.format. Fri Jan 31 16:16:26 1997 David Mosberger-Tang * backend/mustek.c (attach): Print debug message when using special line-distance algorithm. Enable special line-distance algorithm only for firmware revisions < 1.02. Determine fw revision based on INQUIRY result. Thu Jan 30 21:22:38 1997 David Mosberger-Tang * doc/sane.tex (section{Attaching to a SANE backend}): Rename metanet -> net, netproxy -> saned, metadl -> dll and update hierarchy figure as well. Added autolum meta backend as a filter example. * TODO: Rename metanet -> net, metadl -> dll. * AUTHORS (Frontends): Rename metanet -> net, metadl -> dll, mention saned and xcam and sort entries alphabetically. * backend/net.c (sane_control_option): Replace all occurrences of metanet with net. * backend/net.README: Ditto. Rename env var METANETHOST to SANE_NET_HOST. * backend/dll.README: Replace all occurrences of metadl with dll. * backend/dll.c: Ditto. * backend/dll.conf: Comment out metanet since it's a bit of a security risk (no authentication just yet). * backend/Makefile.in (CONFIGS): New macro (list of config files) (install): Change install rule so existing configuration files are not overwritten. Replace all occurrences of metanet with net. (V_MINOR): Bump up to 43. Tue Jan 28 08:17:18 1997 David Mosberger-Tang * frontend/Makefile (saned): Mention -lsanei before -lsane so saned does not incorrectly depend on sanei_printf/sanei_scanf in -lsane (if it happens to define those symbols). * backend/metanet.c (sane_init): Rename SANE_Authorization_Callback to SANE_Auth_Callback. * backend/mustek.c (sane_init): Ditto. * backend/pnm.c (sane_init): Ditto. * backend/metadl.c (sane_init): Ditto. * frontend/saned.c (main): Put output socket in TCP_NODELAY mode. Include of * backend/metanet.c (openit): Ditto. Mon Jan 27 19:32:29 1997 David Mosberger-Tang * doc/sane.tex (subsection{Device Descriptor Type}): Change vendor name "None" to "Noname". * backend/pnm.c (dev): Change vendor name from "noname" to "Noname". * include/sane.h (SANE_STATUS_ACCESS_DENIED): New status value. * sanei/sane_strstatus.c (sane_strstatus): Return appropriate status string. * frontend/xscan.c (scan_dialog): Don't depend on first_frame when creating image---the old code didn't work for R/G/B-only frames anyhow, so there is no point pretending. * include/sane.h (SANE_Parameters): Rename first_frame to last_frame. * backend/metanet.c (sane_get_parameters): Update accordingly. * backend/qcam.c (sane_get_parameters): Ditto. * frontend/saned.c (readcmd): Ditto. * frontend/test.c (testsane): Ditto. * backend/mustek.c (sane_get_parameters): Set last_frame unless the current pass is acquiring a red- or green-only frame. * doc/sane.tex (subsection{sane_get_parameters)): Change FIRST_FRAME into LAST_FRAME. * backend/metanet.c: Initialize PEER with -1. (sane_get_select_fd): Return PEER and succeed if not negative. * include/netsane.h (ns_printf, ns_scanf, netfd_wr, netfd_rd): Remove (obsolete). * frontend/saned.c: Increase STRBUF size to 32KB to get better scan_read() throughput. * include/sane.h (SANE_Athorization_Callback): Return void (instead of int) and make USERNAME and PASSWORD (pointers to) return values. * frontend/saned.c (netsane_auth_callback): Update prototype. * backend/metanet.c (sane_init): Move assignments out of if-conditional for clarity (and to avoid gcc warning). * sanei/sanei_printf.c: Include , . * backend/Makefile.in (libsane-mustek.a): Replace ../sanei/sane_strstatus.o with appropriately defined EXTRA macro. * backend/mustek.c (attach): Initialize ld.mode to MLD_NORMAL for all scanners but MFS-12000SP. (line_distance): In MD_MFS correction mode, do not issue LD command. (sane_close): Free s->ld.red_buf if non-NULL. (fix_line_distance_mfs): New function. (fix_line_distance_normal): Rename from fix_line_distance(). (reader_process): In MLD_MFS line-distance mode, call the mfs-specific routine, not the normal one. * backend/mustek.h (Mustek_Scanner): New members ld.red_buf, ld.green_buf. (MUSTEK_FLAG_LD_MFS): New flag to indicate when MFS-specific line-distance correction is needed. * backend/metanet.c: Fix up some minor formatting problems due to indent. * Merge in Andy's netsane diffs. Sun Jan 26 00:04:22 1997 David Mosberger-Tang * frontend/xscan.c (zoom_in_preview): Ignore selections of zero or negative width/height. (set_max_selection): Use SANE_UNFIX() instead of open coding. Adapted to use gtkglue. * backend/metadl.c (sane_init): Grab authorization callback. (authorization_callback): New variable. (load): Pass authorization callback to init function. * frontend/gtkglue.h (gsg_destroy_dialog): Add param_callback. Sat Jan 25 17:35:52 1997 David Mosberger-Tang * frontend/Makefile.in (distclean): Mention $(TESTPROGRAMS). * backend/metanet.c (copy_opt): Fix constraint-length computation. * backend/metadl.c (sane_get_devices): Pass along LOCA_ONLY arg to backend. * frontend/xcam.c (main): Remove extraneous calls to gtk_exit() and sane_exit(). * frontend/scan.c (main): Install sane_exit() as atexit handler. * backend/metadl.c (sane_get_devices): Ignore NULL BE_LIST. * frontend/saned.c (quit): New function. (main): catch SIGALRM, and SIGPIPE. * backend/metanet.c (sane_get_devices): Return empty list of LOCAL_ONLY is TRUE. * frontend/saned.c (readcmd): Don't take LOCAL arg from request---always pass SANE_TRUE! * doc/netprotocol.doc (sane_get_devices): Don't need LOCAL arg. * include/sanei.h: Include . * frontend/test.c (testsane): Print status message instead of status code and make robust against failed calls. Use fprintf(stderr instead of printf. * backend/qcam.c (reader_process): Remove extraneous arg. (reader_process): Declare `static' to make setjmp() safe. * frontend/saned.c (readcmd): Delete unused variable schr_hlp. (peerfp): New variable. Rename ns_printf() to sanei_printf() and pass peerfp arg. Rename ns_scanf() to sanei_scanf() and pass peerfp arg. (say_helo): Make peerfp refer to fd 0. * backend/metanet.c (openit): Return FILE*, not socket. (openit): Return 0, not -1 in case of failure. (peerfp): New variable. Rename ns_printf() to sanei_printf() and pass peerfp arg. Rename ns_scanf() to sanei_scanf() and pass peerfp arg. * sanei/sane_strstatus.c: New file (in lieu of backend/sane.c) * sanei/sanei_constrain_value.c: Ditto. * sanei/sanei_init_debug.c: Ditto. * sanei/sanei_scanf.c (ns_scanf): New file (based on Andy's ns_scanf). Use int8_t for 'c', int32_t for 'd'. Include . * sanei/sanei_printf.c (sanei_printf): New file (based on Andy's ns_printf). Use int8_t for 'c', int32_t for 'd'. Include . * include/netsane.h (CMD_SANE_STRS): Remove. * backend/metanet.c: Include (sane_strstatus): Remove. * backend/Makefile.in (BACKENDS): Mention libsane-metanet. * backend/metanet.c (openit): Use memcpy instead of bcopy. Use inet_aton() instead of inet_addr(), don't prototype. (sane_get_devices): Use strdup() instead of opencoding it. (sane_get_devices): Don't append " (NS)" to modelname! Metanet already prefixes the devicename with netsane, which is good enough. * backend/sanei.h (IF_DBG): New macro. * PROJECTS (Backend): Mention saned, metanet, and autoadj. (Frontend): New info. * AUTHORS: Update to reflect sanescan -> xscan namechange. Credit Andy for netsane. Fri Jan 24 00:01:45 1997 David Mosberger-Tang * doc/sane.tex (subsection{\code{sane\_get\_option\_descriptor}}): Define how long the returned option descriptor must remain valid. * frontend/gtkglue.c (autobutton_update): Check for SANE_INFO_RELOAD_OPTIONS. (button_update): Ditto. * frontend/xcam.c (main): Add device info label. * backend/mustek.c (init_options): Make OPT_BRIGHTNESS and OPT_CONTRAST inactive by default. * frontend/xcam.c (exit_callback): Only call gsg_destroy_dialog() if dialog is non-null. Thu Jan 23 00:17:46 1997 David Mosberger-Tang * backend/mustek.c (sane_control_option): Return SANE_STATUS_DEVICE_BUSY when attempting to set option during scanning. (sane_close): Call do_cancel(), not sane_cancel(). (sane_start): Ditto. * backend/pnm.c (sod): Rename "File" group to "Source Selection". * frontend/xcam.c (device_name_dialog_cancel): New function. (device_name_dialog_ok): Ditto. (prompt_for_device_name): Ditto. (build_device_menu): Move "Rescan devices..." from build_files_menu() to here. Add "Specify device name..." entry that invokes prompt_for_device_name. (play_stop_button): Ignore call if there is no device_dialog yet. (main): Don't attempt to set preview size if there is no device dialog yet. * frontend/gtkglue.c (text_entry_callback): Don't ignore tab key---it's used to switch focus! * backend/qcam.c (qc_wait): Return read value so optimizer won't be able to get rid of it easily. (qc_lock_wait): Use same lockname as qcam-0.7d for interoperability. (reader_process): Explicitly do an enable_ports() in the reader process. Linux/x86 doesn't inherit i/o port access rights. * frontend/xcam.c (stop_camera): New function. (input_available): Use stop_camera(). (next_frame): Ditto. * backend/genscsi.c (genscsi_open): Add missing DBG_INIT(GENSCSI). (genscsi_cmd): Return SANE_STATUS_DEVICE_BUSY if EBUSY is returned. * frontend/xcam.c (input_available): Fix line wrap for SANE_FRAME_GRAY. * include/sane.h: Remove old SANE_STATUS_AGAIN. * backend/sane.c (sane_strstatus): Ditto. * backend/mustek.c (sane_read): Return SANE_STATUS_GOOD, not AGAIN when no data available. (sane_start): Add debug printing. (sane_set_io_mode): Fix typo: return SANE_STATUS_GOOD on success. * backend/mustek.c (sane_control_option): Add support for OPT_BACKTRACK. (attach): Add lots of debug printing. * backend/qcam.README: New file. * backend/qcam.c (sane_control_option): Allow changing resolution while scan is in progress, but set RELOAD_OPTIONS only if not scanning already. * frontend/xcam.c (canvas_events): Get rid of old debug messages. * backend/qcam.c (sane_get_parameters): Update parameters only if not scanning. This makes it possible to change certain options (such as width/height while a scan is in progress). (sane_control_option): Allow changing of image corners, transfer-scale, and depth while scan is in progress. Set RELOAD_PARAMS only if scan is not in progress. (sane_start): Return DEVICE_BUSY if scan is in progress. * configure.in (PACKAGE_VERSION): Version 0.41 released. * backend/qcam.c (brightness_range): New variable. (init_options): Use brightness_range instead of u8_range to avoid going into bulb-mode accidentally. Wed Jan 22 09:16:21 1997 David Mosberger-Tang * backend/qcam.c (reader_process): Correct typo: it's buf, not buffer that we want to write out in high-res mode. (qc_reset): New function. (reader_process): Send at least one byte of data after receiving a signal. (sighandler): New function. (sane_cancel): If a read request is outstanding, cancel request by (a) sending SIGINT to reader process, (b) reading one byte of data in blocking mode, and then (c) reading whatever is left in the pipe in non-blocking mode. (sane_read): Return SANE_STATUS_CANCELLED if SCANNING is not set. * backend/qcam.h (QC_Scanner): Remove unused x/y fields. * backend/qcam.c (sane_close): Call disable_ports(). (sane_init): Replace isblank() with isspace() (former is a GNU extension). (sane_control_option): Scale corner coordinates when switching resolution and ensure they're always legal values. (sane_close): Check for reader_pid >= 0 not != 0! (sane_exit): Move disable_ports () from sane_close to here. (sane_start): Keep track of when we're holding lock. (sane_read): Release lock if s->holding_lock is TRUE and we read a few bytes. (sane_cancel): Release lock if it's being held. * backend/qcam.c: Clean up unused variables. Tue Jan 21 13:49:00 1997 David Mosberger-Tang * backend/qcam.c (sane_control_option): Keep track of changes to option values. (sane_get_select_fd): Implemented. (sane_set_io_mode): Ditto. (reader_process): New function. (sane_start): Modify to use reader_process(). * backend/qcam.h: (struct QC_Scanner): New field value_changed. Mon Jan 20 22:11:11 1997 David Mosberger-Tang * backend/metadl.c (sane_open): Correctly handle case where backend name is empty. Sun Jan 19 10:29:43 1997 David Mosberger-Tang * backend/qcam.c (attach): Add qc_wait() calls after each write_lpcontrol() call to ensure reliable communication. (attach): Add debug output in case of failure. * Version 0.4 released. Sat Jan 18 10:12:40 1997 David Mosberger-Tang * frontend/xscan.c (get_scan_size): Handle case where scan width/height is of type SANE_TYPE_INT. (get_scan_size): If width/height unit is pixels and the height is less than 500 pixels, use a ratio of 1. (device_dialog): Call update_complete_dialog() to ensure inactive options do not show (there probably is a better solution to this). * frontend/scan.c (main): Compute bottom-right position as top-left + width/height - 1 (the - 1 is new and necessary and works both for fixed and integer values). (print_option): Insert line-break when column >= 79 (not 80) to leave last column blank (better when running inside Emacs, for example). (main): Fix typos: case 't': window[2] -> window[3], switch short option names for top/left. (print_option): Print current option values (except for vectors). (fetch_options): Another off by one bug: width is br_x|y-tl_x|y+1. Thu Jan 16 20:26:29 1997 David Mosberger-Tang * backend/sane.c (sanei_constrain_value): Fix so it properly handles strings that are longer than any of the strings in a string list. * frontend/scan.c (main): Subtract value of top-left-x/top-left-y from width/height, respectively after all options have been read in. (main): Print help message _after_ options have been processed. (fetch_options): New function. (set_option): Call fetch_options() if SANE_INFO_RELOAD_OPTIONS is set. (main): Call fetch_options() instead of open-coding same. * doc/sane.tex (subsection{Device Descriptor Type}): Add index entry for device-name. Wed Jan 15 19:55:03 1997 David Mosberger-Tang * backend/metadl.c (sane_init): Call DBG_INIT() and declare debug level variable with DBG_DECL. Added various debug statements. * backend/pnm.c (sane_init): Ditto. * backend/mustek.c (sane_init): Ditto. * frontend/scan.c (parse_scalar): Default to mm again (instead of cm). Using `mm' as the default is counter-intuitive since the help info is printed with `mm' as the unit. * frontend/xscan.c (main): Pass local_only==SANE_FALSE as second argument to sane_get_devices(). Sun Jan 12 13:12:45 1997 David Mosberger-Tang * backend/sane.c (sanei_constrain_value): For string-lists, ignore case and allow prefixes as long as they remain unique. * frontend/scan.c (print_option): Print "auto" as a legal option value if the option has capability SANE_CAP_AUTOMATIC set. (process_backend_option): If option has SANE_CAP_AUTOMATIC set, let option value of "auto" turn on automatic mode. Wed Jan 8 19:55:31 1997 David Mosberger-Tang * backend/Makefile.in (install): Modify install rule so that $(libdir) gets added to /etc/ld.so.conf if it isn't there already. (COMPILE): Use $(SHCFLAGS) instead of $(CFLAGS). (LINK): Remove (unused). Wed Dec 25 20:21:39 1996 David Mosberger-Tang * backend/mustek.README: Add section "SCSI Adapter Tips". Fri Dec 13 20:02:03 1996 David Mosberger-Tang * backend/mustek.c (line_distance): Rename LINE_DISTANCE_WORKAROUND to NEED_MUSTEK_LINE_DISTANCE_WORKAROUND. Include . Thu Dec 12 20:19:26 1996 David Mosberger-Tang * backend/mustek.h (next): New member. Forms linked list of open handles. * backend/mustek.c (first_handle): New variable. Root of list of open handles. (sane_open): Insert handle into list of open handles. (sane_close): Remove handle from list of open handles. Wed Dec 4 18:37:45 1996 David Mosberger-Tang * backend/mustek.c (attach): Added back MFS-12000SP, MFS-08000SP, and MFS-06000SP. At least the first one really does exist. I don't know how the differ from the other scanners, but it looks as if line-distance correction might be different. Tue Dec 3 21:22:54 1996 David Mosberger-Tang * backend/sane.c (sane_strstatus): Remove trailing dot from error message. Mon Dec 2 19:12:53 1996 David Mosberger-Tang * backend/mustek.c (max_string_size): New function to determine maximum size of a string constraint list. (init_options): Use max_string_size () to initialize size of string-valued options. Sun Dec 1 15:44:53 1996 David Mosberger-Tang * Version 0.1 released. * backend/mustek.c (attach): Fix misspelled/bad model names: MFS-12000SP -> MSF-12000SP MFS-08000SP -> MSF-08000SP MFS-06000SP -> MSF-06000SP MFS-08000CZ -> MFS-06000CZ -> Tue Nov 26 19:11:18 1996 David Mosberger-Tang * frontend/scan.c (print_option): Separate option name from option values by blank, not equal so the syntax is correct even for short options. (parse_scalar): Added missing newline to error message. Sat Nov 16 17:26:19 1996 David Mosberger-Tang * frontend/scan.c (main): Created (by Andy). * backend/pnm.c: Adapted from Andy's pnm.c (just minor updates). * backend/mustek.c: Created. * backend/metadl.c: Ditto. backends-1.3.0/ChangeLogs/ChangeLog-1.0.1000066400000000000000000000367661456256263500175440ustar00rootroot000000000000001999-04-19 David Mosberger-Tang * Version 1.0.1 released. * backend/artec.c (attach): Applied patch by Petter to fix URL in debug output. * backend/microtek2.c (chunky_proc_data): Apply Bernd's latest patch to fix some color problems. 1999-04-17 David Mosberger-Tang * Patch by Mikko Työläjärvi: * sanei/sanei_scsi.c: Check for cam_devices[fd]==NULL, not !=NULL. * Patch from Petter: * config.guess: Update to latest version. * config.sub: Update to latest version. * config.install-sh: Update to latest version. * mkinstalldirs: Update to latest version. 1999-04-12 David Mosberger-Tang * doc/sane-dc25.man: Apply fixes by Peter Fales . * backend/dc25.c: Apply patch by Peter Fales. 1999-04-11 David Mosberger-Tang * include/sane/config.h.in: Declare return type of strdup(), strndup(), and strsep() if these functions are not part of the standard libc. This breaks the GNU convention of not declaring standard C library functions but is required in these cases because the functions return pointers. On a 64 bit platform, bad code is generated if a function is (implicitly) declared to return an "int" and then returns a pointer. To minimize the risk of compile errors due to conflicting prototypes, we don't specify a prototype though (just the return type). * backend/dll.conf: Comment out dc25 and dc210 backends as those are VERY slow to initialize when no camera is attached. * backend/epson.desc: Add Epson GT-7000. 1999-04-06 David Mosberger-Tang * backend/sharp.c (sane_read_shuffled): Declare as "static". (sane_read_direct): Ditto. Applied latest patch by Kazuya and Abel. * sanei/sanei_scsi.c [USE == FREEBSD_CAM_INTERFACE]: Fix bugs (patch by Mikko Tyo"la"ja"rvi ). * backend/snapscan.c (start_reader): Don't redirect scanner file descriptor to fd 0 (needlessly breaks FreeBSD and other platforms that don't use real file descriptors for the scanner. * backend/microtek2.c: Apply Bernd's patch to fix "green-ness" bug. * backend/umax.BUGS: Applied Oliver's latest patch. * backend/umax.CHANGES: Ditto. * backend/umax.FAQ: Ditto. * backend/umax.TODO: Ditto. * backend/umax.c: Ditto. * backend/umax.desc: Ditto. * doc/sane-umax-doc.html: Ditto. * doc/sane-umax-doc.tex: Ditto. * doc/sane-umax-doc.man: Ditto. 1999-04-06 Petter Reinholdtsen * backend/snapscan.c (sane_exit sane_get_devices): Removed memory leak. (sane_open): Give more sensible error message when unable to open temp file. Open temp file in /var/tmp, not in current directory. (mini_inquiry add_device): Make sure to only match listed models. Earlier, substrings would also match. * backend/snapscan.c (sane_snapscan_*): Changed API entries from sane_snapscan_* to sane_*. * backend/snapscan.c (add_device init_options inquiry sane_snapscan_get_parameters sane_snapscan_start sane_snapscan_set_io_mode sane_snapscan_read) backend/snapscan.h backend/snapscan.desc: Rewrote scanner detection code to loop over array of supported SCSI names. Added AGFA SnapScan 1236s support. It seems to be compatible with SnapScan 600. Make sure to not add the same device more then once to the device list. Bugfix in sane_snapscan_read() triggered on EOF. 1999-04-04 David Mosberger-Tang * PROBLEMS: Note that /proc/sys/kernel/sg-big-buff is available since Linux kernel v2.2. * backend/agfafocus.desc: Changed status from "new" to "alpha". * backend/hp.desc: Ditto. * backend/microtek2.desc: Increase version number to 0.6. 1999-04-04 Petter Reinholdtsen * PROBLEMS: Updated note on SG_BIG_BUF. * tools/README: Corrected typo. Added some notes on mustek600iin-off. * tools/mustek600iin-off.c: Remove hardcoded path to mustek.conf. 1999-04-03 David Mosberger-Tang * include/sane/sanei_debug.h: Define sanei_debug_BACKEND_NAME only if STUBS is not defined. * backend/microtek2.h: #ifdef out do_dummy_scan. * backend/Makefile.in (V_REV): New variable. * configure.in (V_REV): New variable. (VERSION): Include ${V_REV} in version number. Substitute V_REV. * backend/umax.c (umax_do_request_sense): Make "static". * backend/canon.h: Move array "option_name"... * backend/canon-sane.c: ...to here and make "static". * backend/pnm.c: Rolled back to version that shipped with sane-0.74. The scan area options were not well implemented. If someone wants to fix that code, the previous (unreliable) version of the PNM backend is in backend/pnm.c-bad. * doc/sane.tex (subsection{Resolution Option}: Remove section 4.5.5---it was a duplicate of section 4.5.2 (Scan Resolution Option)! * backend/microtek2.c (chunky_proc_data): Merge in latest changes from Bernd. 1999-04-02 David Mosberger-Tang * backend/net.c (sane_control_option): Ignore the option size of BUTTONs and GROUPs as required by section 4.2.9. (Bug reported by Nick Lamb). * frontend/xscanimage.c (null_print_func): Declare MSG param as "const" to match declaration of glib-1.2.1. * backend/ricoh.c (attach_one): return SANE_STATUS_GOOD. * backend/microtek2.c (do_dummy_scan): #ifdef out since it seems to be used for debugging only. * backend/hp-option.c (_probe_int): Needed only if HP_EXPERIMENTAL is defined. * backend/coolscan.c: Updated with Didier's latest patch. * backend/mustek.c: Applied Andreas's latest patch. * doc/sane.tex (subsection{Device Descriptor Type}): Add AGFA. * doc/net.tex (subsection{Primitive Data Types}): Clarify that SANE_Char is encoded as ISO LATIN-1 and describe SANE_String encoding (patch by Petter). * frontend/saned.c (DBG): Remove extraneous parens around "msg". * AUTHORS (Frontends): Update Dianne Skoll's email address. * frontend/saned.c (get_free_handle): Check for out of memory situations and initialize newly allocated memory. (auth_callback): Fail when we can _not_ authorize (i.e., !can_authorize), not the other way round. (quit): Make sure quit() gets executed only once. * backend/microtek2.c: Updated with Bernd's latest version. * backend/microtek2.h: Ditto. * backend/st400.desc: New file. * backend/epson.c (sane_open): Check for NULL or empty dummy_dev.sane.name to avoid segfaults. (Suggested by Yuri Dario). * sanei/sanei_scsi.c [USE = LINUX_INTERFACE]: Make "cdb" last member in "struct req" and change size of "data" to 1 byte to allow dynamic sizing of data buffer based on the value of sanei_scsi_max_request_size. (sanei_scsi_req_enter): Calculate size of request based on sanei_scsi_max_request_size. A similar patch was proposed by Petter Reinholdtsen. 1999-03-13 Petter Reinholdtsen * doc/saned.man: More info on how to use tcp_wrapper for access control. 1999-03-08 David Mosberger-Tang * backend/microtek2.c (sane_get_select_fd): Remove bogus cast. Reported by Petter Reinholdtsen. * sanei/sanei_config.c: Include . * frontend/saned.c: Include . (MAXHOSTNAMELEN): Define MAXHOSTNAMELEN if necessary. * configure.os2 (LN_S): Updated with Yuri's latest version. * backend/Makefile.in (install): Use $(LN_S) instead of ln -s. (libsane-dll.la): Make dependent on $(DLL_PRELOAD_EXTRAS). (libsane-hp.la): Make dependent on sanei_pio.lo. * backend/hp.c (PATH_MAX): Define PATH_MAX if necessary. * backend/hp-scl.c (sanei_hp_scsi_pipeout): Add SIGXCPU, SIGXFSZ, and SIGVTALRM only if defined. * backend/dc25.c (PATH_MAX): Define PATH_MAX if necessary. (sane_init): Move up fclose(fp) to avoid segfault when fp==NULL. * backend/dc210.c (sane_init): Ditto. * backend/pnm.c: Apply Oliver's 4-liner to make TLX, TLY, BRX, and BRY advanced options. * backend/umax.c: Updated with Oliver's latest version. * doc/sane-sharp.man: New file (by Kazuya Fukuda). * backend/sharp.c: Updated with Kazuya's latest version. * backend/sharp.h: Ditto. * backend/sharp.desc: Ditto. 1999-03-04 David Mosberger-Tang * backend/canon.c (adjust_hilo_points): Print values of type size_t as described in backend/GUIDE.. * backend/canon-scsi.c: Comment out unused code. * backend/canon.c: Updated with Manuel Panea latest version. * Irix patches from Michael Sweet: * sanei/sanei_scsi.c (sanei_scsi_find_devices): Update with Michael's latest version. * configure.in (CPPFLAGS): Don't check for libnsl or libsocket on Irix. (CPPFLAGS): Check for cfmakeraw. * backend/sharp.h (mode_select_param): Remove double semicolon (some compiler's don't grok those in structure declarations). * backend/Makefile.in (libsane-abaton.la): Make dependent on sanei_config2.lo. * backend/dll.conf: Enable net backend, disable pnm backend by default to lessen risk of confusing novice users. * backend/abaton.c (sane_read): Change type of "size" from ssize_t to size_t. * backend/apple.c (sane_read): Ditto. 1999-03-02 David Mosberger-Tang * backend/Makefile.in (EXTRA_dc210): New macro. * sanei/sanei_ab306.c: Apply usleep() patch by Andreas Czechanowski. * backend/hp.c: Updated with Peter's latest version (v.082). 1999-03-01 David Mosberger-Tang * include/sane/sanei_net.h: Changed copyright on this file (and this file only!) to be public domain (same as sane.h). This helps ensuring that independent network implementations remain compatible. 1999-02-28 David Mosberger-Tang * backend/sharp.c: New file by FUKUDA Kazuya . * backend/sharp.h: Ditto * backend/sharp.conf: Ditto. * backend/sharp.desc: Ditto. * include/sane/sanei_debug.h: Define sanei_debug_BACKEND_NAME only if BACKEND_NAME is defined. * tools/find-scanner.c (main): Add NetBSD device names (patch by Taniguchi Shinya ). * backend/epson.c (attach): Recognize Perfection636 scanner. Suggested by Svend Daugaard Pedersen . * backend/abaton.c (attach_one): Move past attach() to avoid forward-reference. * backend/umax.c (umax_do_request_sense): Make static. * backend/snapscan-310.c (rgb_buf_can_get_line): Make static. (rgb_buf_init): Ditto. (rgb_buf_clean): Ditto. (rgb_buf_push_line): Ditto. (rgb_buf_get_line): Ditto. (rgb_buf_set_diff): Ditto. (transfer_data_diff): Ditto. * backend/snapscan.c (gamma_8): Make static. (start_reader): Ditto. * backend/microtek.c (do_precalibrate): Make static. (finagle_precal): Ditto. (pack_flat_data): Ditto. (pack_goofyrgb_data): Ditto. (pack_into_ring): Ditto. (pack_into_dest): Ditto. (pack_seqrgb_data): Ditto. (ring_alloc): Ditto. (ring_expand): Ditto. (ring_free): Ditto. (set_pass_parameters): Ditto. * backend/epson.c (epson_cmd): Make static. * backend/dc25.c (pp): Make static. (tmpname): Ditto. (tmpnamebuf): Ditto. (tty_baud): Ditto. * backend/artec.c (artec_get_status): Make static. (attach_one): Ditto. (cap_data): Ditto. * backend/abaton.c (mode_update): Make mode_update() static. * backend/apple.c (mode_update): Ditto. (gamma_update): Make static. (xquant): Ditto. (yquant): Ditto. * backend/microtek2.c: Updated with Bernd's v0.6. Add missing include of . * backend/microtek2.h: Ditto. * backend/microtek2.conf: Ditto. 1999-02-27 David Mosberger-Tang * frontend/preview.c (preview_scan): Fix dpi calculation by replacing gwidth by gheight. Reported by Manuel Panea. * doc/sane-net.man: Change service name from "saned" to "sane". * sanei/sanei_scsi.c (MAX_DATA) [FREEDBSD_CAM_INTERFACE]: Change MAX_DATA from MAXPHYS to (DFLTPHYS - PAGE_SIZE). Patch by Parag Patel . * AUTHORS (Backends): Updated Peter's email address. * PROJECTS: Updated Peter's home page URL. * backend/snapscan.c (EXPECTED_MAJOR): Update EXPECTED_MAJOR to 1. * include/sane/sane.h (SANE_CURRENT_MAJOR): SANE_CURRENT_MAJOR and V_MAJOR HAVE to be in sync, so I'm forced to fix SANE_CURRENT_MAJOR now. Embarrassing, but fortunately only the snapscan backend seems affected by this change. * backend/dll.c (load): Use SANE_CURRENT_MAJOR, not V_MAJOR (the latter is used for shared lib versioning). * tools/find-scanner.c (main): Make verbose output a bit friendlier (patch by Petter). * backend/umax.c: Updated with Oliver's latest version (v1.0c). * Patch by Mike Sweet: * backend/Makefile.in (libsane-ricoh.la): Make dependent on sanei_config2.lo. * sanei/sanei_scsi.c (sanei_scsi_find_devices): New function. (WE_HAVE_FIND_DEVICES): Define. * frontend/saned.c (check_host): A "+" in saned.conf now matches any remote host (may be a bit dangerous, so use this judiciously). * frontend/gtkglue.c: Include . * backend/hp-accessor.c (hp_data_alloc): Round size up to a multiple of sizeof(long) to avoid alignment problems. * backend/abaton.h: Remove extraneous comma in enums. * backend/artec.conf: Add "scsi ULTIMA" line. * backend/abaton.conf: Add "scsi ABATON" line. * backend/epson.conf: Add "scsi EPSON" line. * backend/ricoh.conf: Add "scsi RICOH IS60" line. * backend/snapscan.conf: Add "scsi AGFA" and "scsi COLOR" line. * backend/tamarack.conf: Add "scsi TAMARACK" line. * backend/abaton.c (sane_init): Fix fall back code to attach /dev/scanner when ABATON_CONFIG_FILE doesn't exist. (sane_init): Use sanei_config_attach_matching_devices(). (attach_one): New function. * backend/hp-device.c (sanei_hp_device_probe): Mention model 6250C. * backend/saned.conf: Remove the stale comments about hostname matching being case-sensitive. * backend/microtek.h: Updated with Matto's v0.10.1. * backend/mustek.c (send_gamma_se): Apply patch by Wilco Oelen to clip gamma values to range 0..255. * backend/artec.desc: Updated with Chris Pinkham's latest version. * backend/umax.c (PATH_MAX): Do conditional define of PATH_MAX _after_ including all the headers to avoid "duplicate define" warning message. * backend/ricoh.h: Include to get #define for "inline" if necessary. * backend/dc210.c (init_dc210): To send break, use tcsendbreak() if available or TCSBKRP or TCKSBRK ioctl() if available or fall back to doing nothing if none of these are supported. This may cause the backend to work incorrectly on platforms that do not support one of the above methods for sending a break, but at least it will compile that way. 1998-12-12 David Mosberger-Tang * backend/hp.desc: Updated with Peter's latest version. 1998-11-30 David Mosberger-Tang * LICENSE (terms): Add a pointer to backend/djpeg.README.gz. * backend/djpeg.README.gz: New file (required to comply with JPG licensing restriction). * backend/snapscan.c (sane_snapscan_start): Use INT_MAX instead of non-portable MAXINT. Don't include Linux-specific . 1998-11-26 David Mosberger-Tang * doc/sane.tex (subsection{Device Descriptor Type}): Consistently use "Noname" (instead of None/Noname) to indicate the absence of a vendor. (subsection{sane_strstatus}): Define how long the returned string is valid. (section{Code Flow}): Clarify that sane_cancel() has to be called even if read returns SANE_STATUS_EOF. 1998-11-22 David Mosberger-Tang * backend/ricoh.h (_4btol): Replace "__inline" by "inline". * backend/abaton.c (sane_control_option): Assign word values to s->val[foo].w not s->val[foo]. The latter compiles on gcc-2.7.2.3 without warning but causes an error on other compilers (e.g., egcs). backends-1.3.0/ChangeLogs/ChangeLog-1.0.10000066400000000000000000001127101456256263500176040ustar00rootroot00000000000000****** Release of sane-backends 1.0.10. End of code freeze ****** 2003-02-01 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Artec AM12e+. 2003-01-31 Henning Meier-Geinitz * configure configure.in: New version: 1.0.10. Disabled warnings for release. * doc/Makefile.in: Added plustek to list of directories. 2003-01-31 Gerhard Jaeger * doc/plustek/Plustek-USB.txt: Added comment about scanner.o * backend/plustek.c backend/plustek-devs.c backend/plustek-usb.h backend/plustek-usbshading.c: Fixed major bug, that avoid proper shading on CCD-devices like HP 2200C. Fixed also CanoScan 1220 settings, as the current ones didn't work 2003-01-30 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Canon, Fijitsu and Prolink scanners. 2003-01-29 Henning Meier-Geinitz * frontend/saned.c: Fixed severe bug in saned that allowed a remote frontend to crash saned (null pointer dereference) if no scanners are connected. ---- CODE FREEZE FOR SANE 1.0.10 --- -- snapshot 1.0.10-pre3 2003-01-26 Henning Meier-Geinitz * po/sane-backends.de.po po/sane-backends.es.po po/sane-backends.fr.po po/sane-backends.pt.po po/sane-backends.ru.po po/sane-backends.sv.po: Minor updates due to backend changes. * TODO: Added icc problem. * NEWS: Updated. 2003-01-25 Jochen Eisinger * sanei/sanei_pa4s2.c: added some #ifdefs to make it compile with the Intel C++ Compiler (icc). * backend/mustek_pp.c: fixed some character encoding issues in debug messages * README.linux: added a note about the Intel C++ Compiler 2003-01-24 Jochen Eisinger * backend/mustek_pp_ccd300.c: continued on the CCD low-level driver. There is still some bug in it, that keeps it from actually scanning something... 2003-01-24 Jochen Eisinger * doc/descriptions/mustek_pp.desc: added supported scanners 2003-01-24 Henning Meier-Geinitz * backend/bh.c backend/coolscan.c: Minor conversion fixes (from Andrea Suatoni ). * backend/test.c: Use "Color pattern" consistently. * acinclude.m4 aclocal.m4 configure configure.in include/sane/config.h.in: Declare prototypes for all functions that may miss on some platforms. * doc/descriptions/unsupported.desc: Added Mustek parport scanners. Added more information for Primascan Colorado 2600u. * TODO: Updated Irix patches and desc section. 2003-01-24 Gerhard Jaeger * doc/plustek/Plustek-USB-TODO.txt doc/plustek/Plustek-USB.txt Documentation update * doc/descriptions/plustek.desc: status updates * backend/plustek.conf fixed options * backend/plustek.c backend/plustek-devs.c backend/plustek-usb.c backend/plustek-usbhw.c backend/plustek-usbimg.c backend/plustek-usbscan.c backend/plustek-usbshading.c backend/plustek.h backend/plustek-usb.h Minor fixes and prototype description updates, code cleanup 2003-01-23 Karl Heinz Kremer * doc/descriptions/epson.desc: Added Perfection 3200/GT-9800 2003-01-23 Andras Major * backend/doc/sane-coolscan2.man: added a .TP that mysteriously vanished 2003-01-23 Andras Major * backend/coolscan2.c: hack for IR readout with LS-4000. 2003-01-23 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Primax/Visioneer Primascan Colorado 2600u and UMAX Astra 4500. 2003-01-22 Karl Heinz Kremer * doc/descriptions/epson.desc: Corrected syntax for GT-6000 entry 2003-01-22 Henning Meier-Geinitz * sanei/Makefile.in: Fixed regeneration of libsanei.a when the object files were changed. * sanei/sanei_usb.c: Scanning for devices is now done in sanei_usb_init instead of every time when sanei_usb_find_devices is called. Also only devices that really exist are opened. These changes fix the slow detection of devices on systems using devfs or old scanner drivers. 2003-01-21 Karl Heinz Kremer * doc/descriptions/epson.desc: Added "SCSI" to GT-6000 entry 2003-01-21 Eddy De Greef * backend/mustek_pp_cis.c: compile warning fix and minor debug message fixes. * doc/sane-mustek_pp.man: updated supported scanner list and typo fixes. 2003-01-21 Henning Meier-Geinitz * configure configure.in: Adjusted test for sparc64 to disable SCSI generic v3. 2003-01-20 Karl Heinz Kremer * doc/descriptions/epson.desc: Replace :new with :alpha 2003-01-19 Karl Heinz Kremer * backend/epson_usb.c: Added USB product IDs for MFDs. * doc/descriptions/epson.desc: Updated version number, added entries for MFDs. * doc/sane-epson.man: Added MFDs. -- snapshot 1.0.10-pre2 2003-01-18 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx_devices.c doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Removed wrong "untested" flag from Mustek AE USB and Lexmark X70/X73 scanners. * po/sane-backends.de.po po/sane-backends.es.po po/sane-backends.fr.po po/sane-backends.pt.po po/sane-backends.ru.po po/sane-backends.sv.po: Regenerated. 2003-01-17 Henning Meier-Geinitz * configure configure.in include/sane/config.h.in sanei/sanei_scsi.c: Disable the new Linux SG interface for sparc64 to work-around 32 bit userland / 64 bit kernel problems. * doc/descriptions/unsupported.desc: Added HP ScanJet 3500c, 3530c, 3570c, 3570cxi, 5500C and Microtek ScanMaker 3800. Updated link for ScanJet 4470C. * TODO: Added links to hp4200 backend. Removed libusb entry, parport compilation problems, SCSI on sparc64 issue, USB on BSD problems. 2003-01-16 Frank Zago * backend/leo.c doc/descriptions/leo.desc doc/leo/leo.txt: Added another scsi string for the fs-1130. 2003-01-16 Henning Meier-Geinitz * backend/microtek2.c backend/microtek2.h: Bugfix for the X12USL (solved problems >600dpi), one for lineart mode with a 6400XL and one for Scanmaker 5 (crashes at sendgamma). From Karsten Festag . 2003-01-15 Henning Meier-Geinitz * sanei/sanei_usb.c: Don't call usb_clearhalt and usb_set_altinterface to avoid errors on Mac OS X. * doc/descriptions/unsupported.desc: Added HP 5490c. 2003-01-14 Henning Meier-Geinitz * NEWS: Updated list of backends. * doc/sane-usb.man: Added remark about old name of usbfs. * doc/descriptions/unsupported.desc: Added Artec Ultima 2000 (0x4001 model), Canon CanoScan D660U, Compaq S200, Epson Perfection 660, HP ScanJet 4570C, Microtek ScanMaker 3630. Updated ScanJet 5400C and 5470C URLs, Canon FB1210 information. 2003-01-13 Jochen Eisinger * backend/mustek_pp*, backend/Makefile: merged all mustek_pp related files into one compile unit and removed illegal symbols 2003-01-13 Gerhard Jaeger * doc/descriptions/unsupported.desc: Added HP ScanJet 2300C and various chipset information for the unsupported Plustek devices * doc/descriptions/plustek.desc: cleanup ---- FEATURE FREEZE FOR SANE 1.0.10 --- -- snapshot 1.0.10-pre1 2003-01-12 Jochen Eisinger * doc/sane-mustek_pp.man, doc/descriptions/mustek_pp.desc, backend/mustek_pp*: replaced the old mustek_pp backend with version 12-alpha. This version fully supports CIS scanners but removes support for 600dpi CCD scanners and the 300dpi CCD support isn't yet working. In contrary to the old backend, this version is based on information provided by Mustek. For now, the backend exports lots of illegal symbols, this will be fixed before code freeze. Also the .desc file is empty and will be updated soon. * AUTHORS: added Eddy De Greef who wrote the CIS hardware driver part 2003-01-12 Abel Deuring * backend/sharp.*: fixed a segfault, when the SCAN command returned an error; optional "relaxed" handling of transparency unit errors. 2003-01-12 Gerhard Jaeger * doc/descriptions/plustek.desc: Removed the unsupported stuff * doc/descriptions/unsupported.desc: Removed OpticPro 16B. 2003-01-12 Michael Herder * doc/descriptions/artec_eplus48u.desc: Added new models. 2002-01-11 Jochen Eisinger * sanei/sanei_pa4s2.h: prepared sanei for use with fork()ing backends 2003-01-11 Gerhard Jaeger * doc/descriptions/plustek.desc: Update to reflect the currently supported decvices. * doc/descriptions/unsupported.desc: Added some Plustek devices. CanoScan N1220U is now supported by the Plustek backend. * doc/plustek: Added this directory to store various Plustek information files. 2003-01-11 Henning Meier-Geinitz * doc/descriptions/unsupported.desc doc/descriptions-external/primax.desc: Some Primax scanners (and clones) are now supported by the primax backend. * backend/microtek.c backend/microtek2.conf backend/microtek2.h: OPT_THRESHOLD fix. Avoid using "_" in option names. Set more options in microtek2.conf to on by default. From Karsten Festag . * tools/sane-desc.c: Fixed overwrite warnings. Minor DBG message fixes. Fixed priority handling (backend and manpage were overwritten even if a model had a lower priority). * tools/README: Updated sane-find-scanner and sane-desc.el entries. * doc/sane-gt68xx.man doc/sane-mustek_usb.man: Updated status for the BSDs. 2003-01-10 Henning Meier-Geinitz * po/sane-backends.pt.po: Even more Portuguese translations from Pedro Morais . * sanei/sanei_usb.c: Comment out resetep at sane_close. This function sets th USB data toggle only on the host side. * TODO: Removed microtek2 problem, updated Irix patch section and Linux/Sparc section. * NEWS: Updated for 1.0.10. * backend/mustek.c: Removed hardcoded option size. 2003-01-10 Gerhard Jaeger * TODO: Plustek backend now supports libusb, moved appropriate line to done section. 2003-01-10 Gerhard Jaeger * doc/sane-plustek.man: update to reflect version change * doc/descriptions/plustek.desc: Added some Canon entries, status updates * backend/plustek.conf updated options * backend/plustek.c backend/plustek-devs.c backend/plustek-usb.c backend/plustek-usbhw.c backend/plustek-pp.c backend/plustek-usbmap.c backend/plustek-usbimg.c backend/plustek-usbscan.c backend/plustek-usbshading.c backend/plustek.h backend/plustek-share.h backend/plustek-usb.h New version + support for libusb 2003-01-09 Peter Fales * backend/dc210.c, backend/dc240.c: Fix compile failure on OS/2. 2003-01-09 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx.conf backend/gt68xx_devices.c backend/gt68xx_high.c doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Updated manpage and .desc concerning Lexmark X70. Updated manpage concerning kernel 2.4.21-pre3 containing the 1-endpoint fix. Fixed A3 USB resolution setting. Updated A3 USB margins (Pedro Morais ). Added detection of Genius Colorpage Vivid3x. It's unknown if it works. Updated .desc file. Don't print calib line warnings by default. * doc/descriptions/microtek2.desc: Updated (From Karsten Festag ). * po/Makefile.in po/sane-backends.pt.po: Added Portuguese translation for the gt68xx backend (from Pedro Morais ). * po/sane-backends.de.po: Added some missing translations. Removed some (wrong) fuzzy translations. 2003-01-08 Oliver Schwartz * doc/descriptions/snapscan.desc: Added entries for Acer/Benq 310U, 320U, 340U and Mitsubishi Diamondview 650U * backend/snapscan.c backend/snapscan.h backend/snapscan-scsi.c backend/snapscan.conf: Backend version 1.4.18, added support for Acer/Benq310U, fixed color correction for Agfa SnapScan 300 2003-01-08 Henning Meier-Geinitz * doc/descriptions/hpsj5s.desc doc/descriptions-external/hp4200.desc: Use uniform model names for HP scanners. * doc/descriptions/unsupported.desc: Added Canon CanoScan D1250U2 and HP ScanJet 4400C. * po/artec_eplus48u.de.po po/epson.de.po po/epson.ru.po po/epson.sv.po po/gt68xx.de.po po/matsushita.fr.po po/matsushita.ru.po po/mustek.de.po po/mustek.ru.po po/mustek_usb.de.po po/mustek_usb.ru.po po/plustek.de.po po/plustek.es.po po/plustek.ru.po po/pnm.de.po po/pnm.ru.po po/sceptre.fr.po po/sceptre.ru.po po/snapscan.de.po po/snapscan.ru.po po/teco1.fr.po po/teco1.ru.po po/umax.de.po po/umax.fr.po po/umax_pp.de.po po/umax_pp.fr.po po/umax_pp.ru.po po/umax.ru.po: Remove per-backend translations. The translations are now in the per-language sane-backends.??.po files. * po/saneopts.de.po po/saneopts.es.po po/saneopts.fr.po po/saneopts.ru.po po/saneopts.sv.po: Remove global saneopts translations. The translations are now in the per-language sane-backends.??.po files. * TODO: Updated .desc file, mapages, sanei_usb and OS/2 entries. Removed entry about po files. * po/Makefile.in: Remove sane-backends.pot from list of distributed files. Add microtek2 files. * backend/microtek2.c backend/microtek2.h: Improved support for Scanmaker X12USL. Alpha support for Scanmaker 9800XL. Some bugfixes. (From Karsten Festag ). * po/sane-backends.de.po: Added translation for microtek2 backend. (From Karsten Festag ). 2003-01-07 Peter Fales * TODO: Marked dc210/dc240 items as "done" 2003-01-07 Oliver Rauch * backend/umax.c: update to build 39 2003-01-07 Peter Fales * backend/dc210.c, backend/dc240.c: Use a more portable way of sending "break" on serial ports. 2003-01-07 Peter Kirchgessner * doc/descriptions/hp.desc: Change HP ScanJet to ScanJet 2003-01-07 Stéphane Voltz * doc/sane-umax_pp.man: updates and fixes * doc/descriptions/umax_pp.desc: change model form HP3200C to Scanjet 3200C 2003-01-07 Matthew Duggan * backend/canon_pp-dev.c: Improve/fix detection logic. * doc/descriptions/canon_pp.desc: Bump version number. 2003-01-06 Peter Fales * doc/descriptions/dc25.desc, doc/descriptions/dc210.desc, doc/descriptions/dc240.desc, doc/descriptions/gphoto2.desc: Update author information * backend/dc25.c: Fix compiler warning * backend/dc210.c, backend/dc240.c, backend/dc210.conf, backend/dc240.conf, backend/gphoto2.conf, backend/dc25.conf: Irix fixes from mailing list 2003-01-05 Peter Fales * backend/dc25.c: Mailing list fix. Change = to == dc25/dc20 tests. 2003-01-05 Henning Meier-Geinitz * TODO: Added link to Irix patches. Updated .desc section. 2003-01-05 Stéphane Voltz * backend/umax_pp_low.c: small image quality improvement * backend/umax_pp.conf: ppdev device is now default option 2003-01-04 Henning Meier-Geinitz * frontend/saned.c: Print version number. * doc/descriptions/unsupported.desc: Added Genius HR7X Slim, Visioneer 6200 and Onetouch 8920. 2002-12-31 Karl Heinz Kremer * backend/epson.c: Removed one '//' comment. 2002-12-31 Henning Meier-Geinitz * doc/descriptions-external/onetouch8600.desc: Added (from PROJECTS). * doc/descriptions/unsupported.desc: Added list of (known) scanners that are not supported by a SANE backend. There may be information about them or even a standaalone program to which the entries point. * PROJECTS: Removed projects that are now in the .desc files. 2002-12-30 Henning Meier-Geinitz * Makefile.in: Removed configure.os2. * PROJECTS: Removed HP 2200c project (now supported by plustek backend). Added "NIASH" keyword to hp3300backend. * README.darwin: Removed comment about dynamic loading. Added sm3600 and fork problems. * acinclude.m4 aclocal.m4 config.guess config.sub configure configure.in ltmain.sh: Update to latest libtool (1.4.3), config.sub and config.gues. This should fix the build problems on MacOS X concerning dynamic loading. * backend/Makefile.in: Backends shouldn't be linked to sanei_config2 if they aren't also linked to sanei_scsi. sanei_config2 needs sanei_scsi. * backend/gt68xx.c backend/gt68xx_devices.c backend/gt68xx_gt6801.c backend/gt68xx_gt6816.c backend/gt68xx_high.c backend/gt68xx_high.h backend/gt68xx_low.h doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Updated ScanExpress 2400 USB values. Added implementation of gt6801 lamp control for CCD scanners. Added flag for SE 2400 USB, added some quirks in gt68xx_gt6801.c. Fixed gt68xx_gt6801 -> lamp control. Adjusted Plustek 1248U geometry and afe values. CCD gross calibration changed: start with the default value from module struct. Is faster now for most cases. Only 2.5 mm of calibration for GT-6801 scanners. Added "afe" option for gt68xx.conf for selecting default afe values. Removed AFE and exposure SANE options. Added documentation for afe option in gt68xx.conf. New version: 1.0-35. * doc/descriptions-external/hp4200.desc doc/descriptions-external/hpoj.desc: Use Hewlett-Packard consitantly (instead of HP). * doc/descriptions-external/scanwit.desc doc/descriptions-external/viceo.desc: Added description files for scanwit and viceo backends. * doc/descriptions-external/tevion9693usb.desc: Removed (now included). * backend/test.c doc/descriptions/test.desc: Make sure that frontend bugs concerning the select fd are detected. * doc/sane-scsi.man: Minor formatting change. * sanei/sanei_usb.c: Explicitly set first configuration. When claiming the interface, use the interface number from bInterfaceNumber. Explicitly set the alternative setting. When closing, call clear_halt, and reset_ep, and release_interface explicitley. On errors in write/read, call clear_halt. * TODO: Added sm3600 issue, htmla manpage issue, sanei_scsi stuff. Updated desc file, sanei_usb, and MacOSX entries. Deleted sane.tex stuff (now TODO for SANE2). 2002-12-28 Karl Heinz Kremer * Added option to display short resolution list to. 2002-12-07 Henning Meier-Geinitz * doc/sane-scsi.man: The "scsi ..." way of specifying devices is not Linux-only. Some minor formatting updates. * TODO: Added entries for "missing" scanners in desc files: snapscan and epson. Added sanei_usb issues. Updated MacOS X and OS/2 porting issues. * README.darwin: Updated. * backend/gt68xx.c backend/gt68xx.conf backend/gt68xx_devices.c backend/gt68xx_high.c backend/gt68xx_high.h backend/gt68xx_low.c backend/gt68xx_low.h doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Coarse calibration for first scan only" is off by default now. Added gamma table factor option (SANE_CAP_EMULATED). Setup gamma to 2.0 by default. Fixed (hopefully) sigpipe when cancelling. No geometry quantization anymore (not necessary). Plustek 1248U: do linemode for every resolution, fix color correction. Added comments for every Model member in gt68xx_devices. Added Plustek UT16B and Genius Vivid3x to .desc. Added implementation for inverted offset for CCD scanners. Added Mustek ScanExpress 2400 USB. Well, at least it's detected. Added NEW marker to .desc. * doc/descriptions/artec_eplus48u.desc doc/descriptions/hpsj5s.desc doc/descriptions/teco2.desc doc/descriptions/teco3.desc: Updated NEW! markers. 2002-12-07 Abel Deuring * sanei/sanei_scsi.c: improved error handling 2002-12-07 Karl Heinz Kremer * backend/epson.c: Fixed problem with dropout color 2002-12-06 Peter Kirchgessner * backend/hp.h backend/hp.c backend/hp-scl.c backend/hp.conf * doc/sane-hp.man doc/descriptions/hp.desc Add option dumb-read to work around problem with Buslogic SCSI driver 2002-12-06 Henning Meier-Geinitz * configure aclocal.m4 acinclude.m4: Fixed MacOS X LD_FLAGS bug. * doc/sane-scsi.man: Removed link to ACard problem. * doc/sane.tex: Updated contact information. * LEVEL2: Removed. Contents is either in the SANE2 standard or in the SANE2 API TODO. * Makefile.in doc/Makefile.in: Removed LEVEL2. 2002-12-05 Henning Meier-Geinitz * doc/sane-mustek.man doc/sane-mustek_usb.man doc/descriptions/mustek.desc doc/descriptions/mustek_usb.desc: Updated links. * backend/test.c po/sane-backends.de.po: Minor spelling and translation fixes. 2002-12-04 Henning Meier-Geinitz * backend/epson_scsi.c backend/sm3600.c: Move config.h include to the top of the include list to avoid compilation errors on platforms not defining u_char. Added AIX lalloca check. * aclocal.m4 acinclude.m4 configure configure.in include/sane/config.h.in: Cleanup. Added some missing quotes. Better structure and comments. Tried to keep similar tests in one group. Moved more complicated tests to acinclude.m4. Added explicit test for build and host system type. When checking for programs, used $EXEEXT (hopefully fixes OS/2 gettext misdetection). Removed obsolete macros. Removed PTAL check. Used autoconf for asm/io.h check. Shortened --enable-foo output and formatted it correctly. Trans- lations were installed even if --disable-translations was set. Don't test for msgcat anymore. Tell why translations aren't installed. Used autoconf 2.5 syntax for AC_OUTPUT and AC_CONFIG_FILES. Make sane-config executable. Print flags and installation directories near the end of configure output. * po/Makefile.in: Added autoconf comment, removed MSGCAT variable. * backend/dll.c doc/sane-dll.man doc/descriptions/dll.desc: Ignore comments that are in the same line as the backend entries. Minor manpage fixes. Indented according GNU coding standards. * configure.os2: Removed. No longer necessary. 2002-12-04 Matthew Duggan * backend/canon_pp-dev.c: Don't compare firmware version numbers - multiple versions do actually exist. * doc/descriptions/canon_pp.desc: Bump version number. 2002-12-03 Peter Fales * backend/gphoto2.c: Don't display the "low resolution" option when taking a picture, because it doesn't actually do anything. 2002-12-02 Henning Meier-Geinitz * po/Makefile.in po/README sane-backends.de.po sane-backends.es.po sane-backends.fr.po sane-backends.ru.po sane-backends.sv.po: Use only one file of translated messages per language. Merged all backend files. Uses UTF-8 everywhere. Updated all files from the marked source code. 2002-12-01 Stéphane Voltz * backend/umax_pp.c: unmarked from SANE_I18N 2 options names 2002-12-01 Rene Rebe * backend/avision.h backend/avision.c backend/avision.conf doc/descriptions/avision.desc: updated the Avision backend - including auto-generated .desc file listing all devices also present in the avision.c device-list. * doc/descriptions/avision.desc: syntax fix * doc/sane-avision.man: updated to reflect the current backend development. 2002-12-01 Henning Meier-Geinitz * configure configure.in backend/dll.c: Add dynamic loading support for MacOS X/Darwin (from Peter O'Gorman ). * TODO: Added sp15c inclusion and low-level buffer prints. Removed avision warning, hpsj5s freeze, hp/libusb, and manpages issues. Updated MacOS X list. * configure configure.in backend/Makefile.in: Use libtool -module when linking shared libs that will be loaded dynamically. 2002-11-30 Henning Meier-Geinitz * po/Makefile.in po/epson.sv.po po/saneopts.sv.po: Added Swedish translation (from Dennis Björklund ). 2002-11-29 Henning Meier-Geinitz * configure configure.in backend/canon630u.c backend/dll.c backend/gt68xx.c backend/mustek_usb.c backend/net.c backend/pnm.c backend/test.c backend/umax1220u.c backend/v4l.c doc/Makefile.in include/sane/config.h.in tools/sane-desc.c: Fixed PACKAGE_VERSION breakage. PACKAGE_VERSION was redefined by autoconf 2.5 with a different meaning. Use PACKAGE_STRING instead. * backend/mustek.c doc/descriptions/mustek.desc doc/mustek/mustek.CHANGES: PACKAGE_VERSION fix. Fixed debug message in do_stop: really print exit status of reader process. * doc/sane-usb.man: PACKAGEVERSION fix. Removed HP exception. Added MacOS X. * doc/gamma4scanimage.man doc/sane-artec_eplus48u.man doc/sane-config.man doc/sane-find-scanner.man doc/sane-gt68xx.man doc/sane-mustek.man doc/sane-mustek_usb.man doc/sane.man doc/saned.man doc/scanimage.man: Added missing quotes around PACKAGEVERSION * doc/sane-abaton.man doc/sane-agfafocus.man doc/sane-apple.man doc/sane-artec.man doc/sane-as6e.man doc/sane-avision.man doc/sane-bh.man doc/sane-canon.man doc/sane-canon630u.man doc/sane-canon_pp.man doc/sane-coolscan.man doc/sane-coolscan2.man doc/sane-dc210.man doc/sane-dc240.man doc/sane-dc25.man doc/sane-dll.man doc/sane-dmc.man doc/sane-epson.man doc/sane-fujitsu.man doc/sane-gphoto2.man doc/sane-hp.man doc/sane-hpsj5s.man doc/sane-leo.man doc/sane-matsushita.man doc/sane-microtek.man doc/sane-microtek2.man doc/sane-mustek_pp.man doc/sane-nec.man doc/sane-net.man doc/sane-pie.man doc/sane-pint.man doc/sane-plustek.man doc/sane-pnm.man doc/sane-qcam.man doc/sane-ricoh.man doc/sane-s9036.man doc/sane-sceptre.man doc/sane-scsi.man doc/sane-sharp.man doc/sane-sm3600.man doc/sane-snapscan.man doc/sane-sp15c.man doc/sane-st400.man doc/sane-tamarack.man doc/sane-teco1.man doc/sane-teco2.man doc/sane-teco3.man doc/sane-test.man doc/sane-umax.man doc/sane-umax1220u.man doc/sane-umax_pp.man doc/sane-v4l.man: Changed "-" to "\-" in .SH NAME section (required by man 7 man). Added PACKAGEVERSION and "SANE Scanner Access Now Easy" to header. 2002-11-29 Oliver Rauch * frontend/stiff.c: added fclose(icc_file) 2002-11-25 Peter Kirchgessner * backend/hp-device.h backend/hp-accessor.c backend/hp-scl.c backend/hp.c backend/hp-device.c backend/hp-option.c backend/hp.h backend/hp.conf backend/Makefile.in doc/sane-hp.man doc/descriptions/hp.desc Added libusb support for hp-backend 2002-11-25 Henning Meier-Geinitz * PROJECTS: Removed projects for scanners that are now supported by the plustek backend. Removed lhii backend (dead?). Added more details for other projects. Added 5400C / 5470C project. 2002-11-23 Rene Rebe * backend/avision.h backend/avision.c backend/avision.conf doc/sane-avision.man: updated the Avisin backend to my todays snapshot. It know handles more scanners as well as ADF and A3 size devices. 2002-11-23 Max Vorobiev * backend/hpsj5s.c: Removed default port probing. 2002-11-22 Henning Meier-Geinitz * doc/sane.man: Formatting fixes. Updates some backend entries and sane-find-scanner. Some clarifications. * doc/scanimage.man: Use "--option arg" format for all options. Other formatting fixes. * doc/gamma4scanimage.man doc/sane-config.man doc/sane-find-scanner.man doc/saned.man: Formatting updates. 2002-11-22 Michael Herder Fixed header in artec_eplus48u.de.po. *po/artec_eplus48u.de.po 2002-11-21 Henning Meier-Geinitz * doc/descriptions/mustek_usb.desc doc/sane-mustek_usb.man doc/mustek_usb/mustek_usb.CHANGES doc/mustek_usb/mustek_usb.TODO: Minor manpage fixes. Added links to similar backends. More information about non-Linux OS. * doc/sane-mustek.man: Minor fixes. Added links to the backends providing support for Mustek scanners. * backend/gt68xx.c backend/gt68xx_devices.c backend/gt68xx_gt6816.c backend/gt68xx_low.c backend/gt68xx_low.h doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Changed 150 to 200 dpi for Mustek BearPaw 2400 TA. Fixed warnings in gt68xx_low.c. Used DBG instead of XDBG ing gt68xx_low.c. Minor manpage fixes. Added man page links to other backends. gt68xx_low.h: Fixed __FUNCTION__ bug on non-gcc compilers. Added more details about the BSDs in the manpage. Fixed color order for ScanExpress A3 USB. * TODO: Removed tevion9693usb and umax1220u warnings entries. Added avsion warnings. Little bit of restructuring. 2002-11-21 Michael Herder Added german translation for artec_eplus48u backend. *po/artec_eplus48u.de.po *po/Makefile.in 2002-11-20 Henning Meier-Geinitz * doc/Makefile.in: Don't generate symlinks for libsane.so/.a, the files are installed anyway. Fix symlink creation when only static libraries are created. 2002-11-20 Michael Herder Added artec_eplus48u backend for the scanner Artec E+ 48U and re-badged models like Tevion/Medion MD 9693, Medion MD 9705 and Trust Easy Webscan 19200. * backend/Makefile.in backend/dll.conf backend/artec_eplus48u.c backend/artec_eplus48u.conf backend/artec_eplus48u.h * doc/.cvsignore doc/Makefile.in doc/sane-artec_eplus48u.man doc/sane.man doc/descriptions/artec_eplus48u.desc * AUTHORS: Updated concerning artec_eplus48u. 2002-11-19 Henning Meier-Geinitz * doc/sane-usb.man: Added quick start section. New structure/order. Added list of backends that don't use sanei_usb. Added headers and fixed \- bug. * doc/sane-find-scanner.man: Minor adjustments, added headers, fixed \- bug. 2002-11-18 Frank Zago * backend/umax-usb.c: the usb support for the umax 2200 wasn't using the correct sanei USB API, which broke the support for that scanner. Also fixed a couple warnings and a typo. * backend/sceptre.c backend/teco1.c backend/teco3.c backend/matsushita.c backend/leo.c: fixed hexdump() declaration. * backend/dll.c: fixed some warnings. 2002-11-07 Stéphane Voltz * backend/umax_pp_low.c: disable test facility 2002-11-17 Henning Meier-Geinitz * TODO: Added manpage issues and hpsj5s freeze problem. Removed canon.c model problem. * doc/Makefile.in: Added variable @PACKAGEVERSION@ for usage in manpages. 2002-11-14 Henning Meier-Geinitz * doc/sane-mustek_usb.man doc/sane-usb.man: Fixed wrong .TH headers. 2002-11-13 Henning Meier-Geinitz * configure configure.in acinclude.m4 aclocal.m4 include/sane/config.h.in: Fix compilation on Tru64 by readding the sys/bitypes.h work-around. Minimum autoconf version is 2.50 now. * backend/gt68xx.c backend/gt68xx.conf backend/gt68xx_devices.c backend/gt68xx_gt6801.c backend/gt68xx_gt6801.h doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES po/gt68xx.de.po: Added basic support for Plustek OpticPro 1248U (from Gerhard Jaeger ). Removed 50 and 1200 dpi from Lexmark X73. Backend status is BETA now. 2002-11-13 Ulrich Deiters * modified the Canon SCSI backend (now version 1.10): * backend/canon.c, canon-scsi.c: Added support of focus control for the FS2710 film scanner. backend/canon-sane.c: Set handler_arg in sanei_scsi_open() calls to zero to avoid crashes with Mandrake Linux 9.0 2002-11-12 Oliver Rauch * added several russian translations to directory po. The translations have been created by Vitaly Lipatov 2002-11-07 Stéphane Voltz * backend/umax_pp.c: revert a experimental change that went into cvs by mistake. * po/umax_pp.de.po: switch to iso-8858-1 2002-11-07 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx_devices.c backend/gt68xx_high.h backend/gt68xx_low.h doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES po/Makefile.in po/gt68xx.de.po: Changed option titles and descriptions to make them better readable. Added translation markers for all options. Added German translation for backend options. Updated manpage and .desc file concerning scanner status. Added untested flag for Mustek ScanEpress A3 USB and Lexmark X73. * backend/mustek.c doc/sane-mustek.man doc/descriptions/mustek.desc doc/mustek/mustek.CHANGES: Upload linear gamma table for Pro models if custom gamma is off instead of uploading the composed gamma table. That avoids applying gamm twice. Minor man page update. 2002-11-05 Stéphane Voltz * backend/umax_pp.c backend/umax_pp_mid.c : OS/2 compile fix, indent correction 2002-11-03 Karl Heinz Kremer * sanei/sanei_usb.c: Use endpoint address with direction information instead of the pipe reference. This caused problems with Darwin systems. 2002-11-03 Karl Heinz Kremer * backend/epson.c: Full support for libusb 2002-11-03 Henning Meier-Geinitz * README configure.in configure po/Makefile.in po/README: Translations are now enabled by default if the gettext tools are found. Use msgcat if available. Don't update the .po files automatically. * TODO: Added entries about canon.c model extraction bug, and sanei_usb issue. Updated entries about MAcOS X support. Removed po files entry. 2002-10-31 Henning Meier-Geinitz * configure configure.in: Print installation directories. * backend/gt68xx.c backend/gt68xx_devices.c backend/gt68xx_high.c backend/gt68xx_low.c backend/gt68xx_low.h doc/gt68xx/gt68xx.CHANGES: Faster AFE calibration. Minor fixes. New version: 1.0-28. 2002-10-31 Stéphane Voltz * backend/umax_pp.c backend/umax_pp.h backend/umax_pp_low.h backend/umax_pp_low.c tools/umax_pp.c: add UTA detection, version number updates 2002-10-30 Henning Meier-Geinitz * backend/dll.conf: Removed commented out gt68xx entry. * PROJECTS: Updated viceo entry. 2002-10-27 Peter Fales * configure, acinclude.m4, aclocal.m4 - Remove checks for specific version numbers of gphoto2 - that's not good for long term maintenance, and the API has settled down enough that all recent versions (both releases and CVS) are workable for SANE. It is still necessary to specify --with-gphoto2 if you want to use the gphoto2 backend. 2002-10-27 Henning Meier-Geinitz * configure configure.in: Added a warning if sysconfdir is set to /usr/etc. * backend/test.c: Explicitly close the pipe and wait until reader_process is killed. 2002-10-25 Henning Meier-Geinitz * backend/Makefile.in backend/dll.conf backend/gt68xx.c backend/gt68xx.conf backend/gt68xx.h backend/gt68xx_devices.c backend/gt68xx_generic.c backend/gt68xx_generic.h backend/gt68xx_gt6801.c backend/gt68xx_gt6801.h backend/gt68xx_gt6816.c backend/gt68xx_gt6816.h backend/gt68xx_high.c backend/gt68xx_high.h backend/gt68xx_low.c backend/gt68xx_low.h backend/gt68xx_mid.c backend/gt68xx_mid.h backend/gt68xx_shm_channel.c backend/gt68xx_shm_channel.h: Added gt68xx backend for scanners based on the Grandtech GT-6801 and GT-8616 chips like The Mustek BearPaw CU, and TA scanners and some Artec Ultima 2000 clones. * doc/.cvsignore doc/Makefile.in doc/sane-gt68xx.man doc/sane.man doc/descriptions/gt68xx.desc doc/descriptions-external/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Added documentation for gt68xx backend. * AUTHORS PROJECTS TODO: Updated concerning gt68xx. * Makefile.in backend/Makefile.in doc/Makefile.in: Added some missing files to DISTFILES. 2002-10-24 Peter Fales * backend/gphoto2.c: Improve checks for values specified in config file and generate errors when invalid values are specified. 2002-10-24 Henning Meier-Geinitz * configure configure.in: Warnings enabled again. Used extra version -cvs. Older entries can be found in ChangeLog-1.0.9. backends-1.3.0/ChangeLogs/ChangeLog-1.0.11000066400000000000000000000007071456256263500176070ustar00rootroot00000000000000****** Release of sane-backends 1.0.12. End of code freeze ****** 2003-02-09 Henning Meier-Geinitz * frontend/saned.c sanei/sanei_codec_bin.c sanei/sanei_wire.c: Check the IP address of the remote host before any communication occurs. Check for a errors before trsuting values that came from remote. Make sure that strings are 0-terminated. Older entries can be found in ChangeLog-1.0.10. backends-1.3.0/ChangeLogs/ChangeLog-1.0.12000066400000000000000000001201771456256263500176140ustar00rootroot00000000000000****** Release of sane-backends 1.0.12. End of code freeze ****** 2003-05-25 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added DigitDia 3600. * configure configure.in: New version: 1.0.12. 2003-05-22 Henning Meier-Geinitz * doc/descriptions/mustek.desc: Primax Compact 4800 SCSI is reported to work. * doc/descriptions/unsupported.desc: Added more information for Artec and Visioneer/Primax scanners. * TODO: Added saned + configure issue. 2003-05-21 Stéphane Voltz * backend/umax_pp_low.c: backed out clearing epp timeout. It prevented most common settings to use the umax_pp backend. 2003-05-18 Gerhard Jaeger * doc/sane-plustek.man: Update. * backend/plustek-devs.c backend/plustek-usbshading.c backend/plustek-usb.h: Fixed critical bug in CIS device calibration and settings which causes the CanoScan 650, 1220 and 1240 not to work anymore * TODO: removed line "check status of Umax 3450...". ---- CODE FREEZE FOR SANE 1.0.12 --- -- snapshot 1.0.12-pre2 2003-05-18 Henning Meier-Geinitz * NEWS: Updated. * doc/descriptions/unsupported.desc: Added Mustek ScanMagic 9636P. * TODO: Added epson/saned/xsane problem. * configure configure.in: Disabled compilation warnings. * tools/Makefile.in: Added sane-po.awk to DISTFILES. 2003-05-16 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Minor fix for Spot scanners. 2003-05-15 Gerhard Jaeger * TODO: status of UMAX 3450 is fixed now in .desc * descriptions/plustek.desc: update * doc/plustek/Plustek-USB-TODO.txt doc/plustek/Plustek-USB.txt doc/plustek/Plustek.changes: update * backend/plustek-devs.c backend/plustek-pp.c backend/plustek-usb.c backend/plustek-usbhw.c backend/plustek-usbimg.c backend/plustek-usbio.c backend/plustek-usbmap.c backend/plustek-usbscan.c backend/plustek-usbshading.c backend/plustek.c backend/plustek.h backend/plustek-share.h backend/plustek-usb.h: Major bugfix release, cancel should work now, calibration for CIS devices is now fully functional 2003-05-15 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Updates for Canon scanners. Added Spot scanners. * aclocal.m4 acinclude.m4 configure: Fixed MacOS X build (patch from Mattias Ellert ). * doc/sane-gt68xx.man: Fixed wrong example. 2003-05-13 Henning Meier-Geinitz * backend/Makefile.in: Added missing $(DESTDIR). * TODO: Minor updates. 2003-05-12 Henning Meier-Geinitz * po/sane-backends.fr.po: Updated french translation (from Yann E. MORIN ). * frontends/saned.c: Replaces a strncmp() call by a macro detecting V4-mapped address. This is a lot safer than the strncmp, and doesn't break anything. Patch from Julien BLACHE . 2003-05-11 Eddy De Greef * backend/mustek_pp.[ch]: Offset correction for Mustek 600CP and added some debugging code. 2003-05-09 Rene Rebe * doc/sane-avision.man: fix tiny typo 2003-05-08 Rene Rebe * backend/avision.c doc/sane-avision.man doc/descriptions/avision.desc: update to the latest avision backend including the fix for segmentation fault when no config file is present. 2003-05-08 Henning Meier-Geinitz * acinclude.m4 aclocal.m4 configure include/sane/config.h.in: Added missing HAVE_LIBIEEE1284 macro. The test was there, but the macro wasn't set. * po/sane-backends.de.po: Added some missing translations. * po/sane-backends.es.po po/sane-backends.fr.po po/sane-backends.nl.po po/sane-backends.no.po po/sane-backends.pt.po po/sane-backends.ru.po po/sane-backends.sv.po: Updated for latest backend changes. * backend/hp5400.c backend/hp5400.h backend/hp5400_internal.c backend/hp5400_sanei.c backend/hp5400_xfer.h: Changed license to GPL + SANE exception. 2003-05-07 Henning Meier-Geinitz * po/Makefile.in po/sane-backends.no.po: Added Norwegian translation (from Sigurd Stordal ). 2003-05-06 Michael Herder * doc/descriptions/artec_eplus48u.desc: Added comment about different product ID of MEM 48U * backend/artec_eplus48u.conf: Added new model Memorex MEM 48U 2003-05-05 Oliver Rauch * backend/umax.c: solved bug that broke compilation for OS2 2003-05-05 Henning Meier-Geinitz * po/sane-backends.fr.po: Updates for the French translation from Yann E. MORIN . * tools/check-po.awk: Added script to check the translations for completeness (from Yann E. MORIN). * tools/README: Added description of check-po.awk. Minor reordering. ---- FEATURE FREEZE FOR SANE 1.0.12 --- -- snapshot 1.0.12-pre1 2003-05-04 Henning Meier-Geinitz * frontend/saned.c: Added implementation of poll() for systems that don't provide it (patch from Julien BLACHE ). * TODO: Removed obsololete entries. * NEWS: Updated for 1.0.12. * backend/ibm.c backend/ibm-scsi.c: Only disable object position command for Ricoh IS-420. 2003-05-04 Oliver Rauch * backend/umax.c umax.h applied changes that replace fork() by thread for OS2 2003-05-03 Henning Meier-Geinitz * include/md5.h: Set fixed alignment because otherwise the MacOS X gcc doesn't like it. * backend/ibm.c backend/ibm-scsi.c doc/descriptions/ibm.desc: Disabled object_position. That should fix the Ricoh IS-420. * frontend/saned.c: Try to get the correct port number when /etc/services doesn't list "sane" by asking for "6566". Check result of socket(). Print ip faimily and port. * configure configure.in frontend/Makefile.in tools/Makefile.in: Remove -all_load again and try to use the correct link order for libraries. 2003-05-02 Henning Meier-Geinitz * configure configure.in frontend/saned.c include/sane/config.h.in: Test for poll() and disable IPV6 if not found. * include/lalloca.h: Remove test for MacOS X. That one breaks some MacOS X systems. * configure configure.in frontend/Makefile.in tools/Makefile.in: Add -all_load to linker flags on MacOS X to avoid linker errors. * backend/Makefile.in: Create directory for gt68xx firmware. 2003-05-01 Oliver Rauch * backend/ umax-scanner.c umax-scsidef.h umax-uc1200se.c umax-uc630.c umax-ug630.c umax-usb.c umax.conf umax.h umax-scanner.h umax-uc1200s.c umax-uc1260.c umax-uc840.c umax-ug80.c umax.c umax.desc doc/sane-umax.man doc/umax/* update of sane-umax backend 2003-04-30 Oliver Schwartz * backend/snapscan.c backend/snapscan.h backend/snapscan-options.c backend/snapscan-scsi.c doc/descriptions/snapscan.desc: SnapScan backend 1.4.26, added support for Agfa Arcus 1200, better support for Benq 5000. 2003-04-30 Henning Meier-Geinitz * lib/getopt.c: Removed #include again, breaks compilation on OS/2. * config.guess config.sub: New upstream versions. * acinclude.m4 aclocal.m4 configure ltmain.sh: New libtool upstream release: 1.5. * acinclude.m4 aclocal.m4 configure configure.in: Added support for -framework option and check for IOKit for MacOS X. Check for latex tools. Minimum autoconf version is 2.54. * ltmain.sh: Add support for -framework option for MacOS X (from Mattias Ellert ). * sanei/sanei_scsi.c: Added SCSI support for MacOS X (from Mattias Ellert ). * include/sane/config.h.in: Added test for IOKit. * README.darwin: Updated concerning SCSI support. * backend/Makefile.in: Disable manual links for shared libraries. This breaks MacOS X and doesn't seem to be necessary on other platforms any more. * include/lalloca.h: Don't use builtin alloca on MacOS X. * tools/Makefile.in: Build sane-desc by default. * tools/sane-desc.c: Fixed segfault. * doc/Makefile.in: Only use latex if available. * doc/descriptions/unsupported.desc: Updated info on Artec, Canon and HP scanners. 2003-04-27 Henning Meier-Geinitz * backend/hp5400_internal.c backend/canon630u-common.c backend/epson_scsi.c: Minor compilation fixes for MacOS X and OS/2. * backend/gt68xx.c backend/gt68xx.conf backend/gt68xx_devices.c doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Added Genius 3x as working scanner, removed testing flag. Added Artec 2000 e+ to manpage. Added detection for the Plustek OpticPro U16B. * doc/descriptions/unsupported.desc: Added more information for the HP Scanjet 2300c. * lib/getopt.c: Add missing includes. * po/Makefile.in po/sane-backends.de.po po/sane-backends.es.po po/sane-backends.fr.po po/sane-backends.nl.po po/sane-backends.pt.po po/sane-backends.ru.po po/sane-backends.sv.po: Added hp5400 backend. Updated po files. * TODO: Added entry about MacOS X SCSI patches. 2003-04-26 Henning Meier-Geinitz * README.netbsd: Some additions concerning uscanner driver. * TODO: Removed hp4200 link. Added hp5400 GPL/SANE issue. Added avision hpusbscsi documentation issue. Removed scanimage/OS/2 problem. * doc/sane-mustek.man: Added some more details for SCSI adapters. * doc/descriptions/ibm.desc: Ricoh IS-420 doesn't seem to work out-of-the-box. * doc/descriptions/unsupported.desc: Added Visioneer OneTouch 4800 USB. Added more information for the Genius ColorPage HR7X Slim and Microtek Scanport 3000. 2003-04-23 Frank Zago * backend/teco2.c backend/teco2.h doc/sane-teco2.man doc/teco/teco2.txt descriptions/teco2.desc: new calibration algorithm. 2003-04-20 Henning Meier-Geinitz * README: Removed link to (non-working) anonymous CVS. Mention CVS snapshots and beta releases instead. Rewrite the paragraph about frontends. Mention that libusb is necessary for some backends. Mention more operating systems. Added contact section (mostly copying from sane(7)). Minor formatting updates. * README.freebsd: Automatic SCSI detection does work, so this part was removed. libusb is necessary for some backends. Some parport scanners seem to work. * README.linux: Minor updates. * README.openbsd: libusb is necessary for some backends. * Makefile.in doc/Makefile.in doc/sane.man: The lists of supported devices are now generated and installed by default. * NEWS: Updated for 1.0-12. * doc/descriptions/unsupported.desc: Removed Epson Perfection 660 (now supported by snapscan backend), Primax Jewel 4800 (teco2). Added Enhans/E-Lux j-6121 and Visioneer OneTouch 5300 USB. * sanei/sanei_usb.c: Removed Frank's memleak fix. The memory is allocated only once in sanei_usb_init, not in sanei_usb_open. So releasing it in sanei_usb_close breaks backends that open devices more than once. * po/sane-backends.nl.po: Updated Dutch translation (from Martin Kho and Bertrik Sikken ). 2003-04-18 Frank Zago * backend/teco2.c backend/teco2.conf backend/teco2.h doc/sane-teco2.man doc/descriptions/teco2.desc: added support for Relisys AVEC II S3 (VM3564) and Primax Jewel 4800 (VM356A). Patch from Gerard Klaver. * sanei/sanei_usb.c: fixed a memleak. * sanei/sanei_pv8630.c: added some debug traces. 2003-04-18 Stéphane Voltz * configure configure.in include/sane/config.h.in: added detection of dev/ppbus/ppi.h 2003-04-18 Stéphane Voltz * backend/umax_pp_low.c backend/umax_pp_low.h backend/umax_pp.conf: added support for /dev/ppi0 on *BSD, and a * backend/umax_pp.c: configuration parsing fix 2003-04-17 Henning Meier-Geinitz * AUTHORS backend/Makefile.in backend/dll.conf backend/hp5400.c backend/hp5400.conf backend/hp5400.h backend/hp5400_internal.c backend/hp5400_sanei.c backend/hp5400_xfer.h doc/.cvsignore doc/Makefile.in doc/sane-hp5400.man doc/sane.man: Added hp5400 backend from Martijn van Oosterhout and Thomas Soumarmon . Changed to use sanei_usb instead of direct /dev/usb/scanner access. Added manual page. Fixed some portablility issues and some warnings. Added SANE headers. * doc/descriptions/hp5400.desc doc/descriptions-external/hp5400.desc: Moved file as the backend is included now. Added new marker. * doc/descriptions-external/lhii.desc: Removed. Website has been dead for years. No response from author. * doc/descriptions-external/onetouch8600.desc: Removed. Website is dead. No response from author. * doc/descriptions-external/hp4200.desc doc/descriptions-external/hpoj.desc doc/descriptions-external/niash.desc doc/descriptions-external/primax.desc doc/descriptions-external/scanwit.desc doc/descriptions-external/v4l2.desc doc/descriptions-external/viceo.desc: Added comments to explain the reasons for not including these backends into SANE. 2003-04-16 Henning Meier-Geinitz * doc/scanimage.man: Added EXAMPLES section. * doc/descriptions/unsupported.desc: Added information about Hewlett-Packard and Mustek scanners. 2003-04-15 Henning Meier-Geinitz * AUTHORS backend/Makefile.in backend/dll.conf backend/ibm-scsi.c backend/ibm.c backend/ibm.conf backend/ibm.h doc/Makefile.in doc/sane.man doc/sane-ibm.man doc/.cvsignore doc/descriptions/ibm.desc: Added IBM backend for the IBM 2456, the Ricoh IS-420 and maybe the IS-410 and IS-430 from mf . Added manual page, fixed some warnings, Added detection for IS-410 and IS-430. * doc/descriptions-external/ibm.desc: Removed, now included in SANE distribution. * include/md5.h: Don't use __attribute__ for compilers that don't support it. * doc/sane-ibm.man doc/descriptions/ibm.desc: Mention alpha quality. Add maintainer address. 2003-04-14 Henning Meier-Geinitz * tools/sane-desc.c: Fixed some HTML bugs. * doc/desccriptions/unsupported.desc: Updated HP Scanjet 35XX, Medion MD 6228, and Umax Astra 4700 entries. Added some Mustek and Primax scanners. 2003-04-14 Peter Kirchgessner * backend/hp.c, hp-option.c: Check pointers received in sane_control_option(). Caused saned to crash on scanimage --help -d net:localhost:hp:... 2003-04-13 Henning Meier-Geinitz * doc/sane.man: Added SEE ALSO section. * backend/Makefile.in: Fixed warning for dll-preload.c. * bakend/dll.c: Add casts to function calls. Without this fix, the dll backend didn't work on the m68k platform because data and addresses are stored in different registers so the return values of the sane api functions were wrong. Patch from Dave Huang . Fixed some compilation warnings. 2003-04-11 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx_devices.c doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Updated Mustek BearPaw 1200 CU Plus white strip value. Set Mustek ScanExpress A3 USB to CCD. Black mark may be still wrong. Minor formatting changes in the manpage. Decreased ScanExpress A3 USB status to alpha because of CIS/CCD change. * backend/ma1509.c: Remove test for multi-pass. * doc/sane.man: Added information about #sane IRC channel. Formatting changes. * doc/descriptions/unsupported.desc: Added Mustek P 3600 A3 Pro. * TODO: Removed license issue. Updated desc files and global variables entries. Updated v4l entry. * doc/scanimage.man: --batch format doesn't work, only --batch=format. 2003-04-10 Henning Meier-Geinitz * backend/test.c backend/test.h doc/descriptions/test.desc: Added support for OS/2 (from Franz Bakan ). 2003-04-05 Nathan Rutman * backend/canon630u-common.c: Removed last change in offset/gain calculation, which croaked for some cases. Give up early if we can't talk on the USB. * backend/canon630u.c: Added option to force scanner recalibration. 2003-04-04 Henning Meier-Geinitz * backend/dll.c: Added some missing debug messages. Fixed a warning. 2003-04-03 Eddy De Greef * TODO: removed mustek_pp entry (Check that global variables are initialized properly 2003-04-03 Henning Meier-Geinitz * lib/Makefile.in: Recompile all targets if necessary. * sanei/sanei_scsi.c: Fixed some warnings. * include/md5.h: Declare md5_buffer. * sanei/sanei_constrain_value.c: Removed unnecessary variable. * backend/agfafocus.c: Fixed some warnings. * backend/as6e.c: Fixed some warnings. * backend/coolscan.c backend/coolscan-scsi.h: Fixed a bunch of compilation warnings. * backend/djpeg.c: Fixed some warnings. * backend/dmc.c: Fixed some warnings. 2003-04-03 Eddy De Greef * backend/mustek_pp.c: Fixed a potential crash bug that could be triggered when calling sane_init/sane_get_devices/sane_exit multiple times, and fixed some minor bugs to pass the frontend/tstbackend.c tests. 2003-04-03 Oliver Schwartz * backend/snapscan.c backend/snapscan.h backend/snapscan-scsi.c backend/snapscan-options.c backend/snapscan.conf doc/descriptions/snapscan.desc: SnapScan backend 1.4.25 - added support for Epson Perfection 660, enhanced support for Acer 5000 2003-04-02 Henning Meier-Geinitz * doc/sane-as6e.man: Added info about $PATH. Minor formatting fixes. * doc/sane.man: Reorganization. Updated DEVELOPER'S DOCUMENTATION. Rewrote PROBLEMS in a more user-centric way. Added "HOW CAN YOU HELP" section. Added paragraph about SANE device lists. Updated CONTACT section. Minor additions to the gt68xx and plustek entries. Minor spelling fixes. * backend/sm3600-color.c backend/sm3600-gray.c backend/sm3600-homerun.c backend/sm3600-scanmtek.c backend/sm3600-scantool.h backend/sm3600-scanusb.c backend/sm3600-scanutil.c backend/sm3600.c backend/sm3600.h: Fixed comment headers. They were copied verbatim from the dll backend. * AUTHORS backend/artec.c doc/sane-artec.man: Remove Chris Pinkham as active maintainer because his primary email address bounces, and he doesn't respond to mails to other addresses. Marked email addresses and website as dead. Fixed AT3 misdectection that was on the TODO list for some time. Initialize global variables in sane_init. * acinclude.m4 aclocal.m4 configure configure.in include/sane/config.h.in frontend/saned.c: Added a switch to disable libusb. Removed switch to enable ipv6 uncondinionally. Check for sys/poll.h. * sanei/sanei_usb.c: Added a function to print the buffer contents for the read and write functions. Fixed some DBGs. Fixed a wrong return value. * TODO: Removed sm3600, artec and libusb configure switch entries. Updated v4l, sanei buffer print and html manpages entries. Added OS/2 crash entry. * include/getopt.h lib/getopt.c lib/getopt1.c: Update to glibc 2.3.1 version. This fixes a compilation warning. Further more, the code is now LGPLed. * include/md5.h lib/md5.c: Update to glibc 2.3.1 version. The code is now LGPLed. Changed the K&R style and added prototypes to avoid warnings. 2003-03-31 Henning Meier-Geinitz * frontend/saned.c: Include poll.h only if new saned code is used. Fixes compilation on OS/2. Patch from Julien BLACHE . 2003-03-30 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Medion MD 6228, Microtek ScanPort 3000, and PIE Primefilm 1800u. * acinclude.m4 aclocal.m4 configure configure.in backend/net.c backend/net.h doc/sane-net.man doc/saned.man frontend/saned.c include/sane/config.h.in: Added support for IPv6. Updated manpages. Patch from Julien BLACHE . * TODO: Updated net sections. Removed Irix patch entry. Added entry about libusb switch. 2003-03-28 Oliver Schirrmeister * backend/fujitsu.c backend/fujitsu-scsi.h: now really supports the fi-4120C 2003-03-26 Karl Heinz Kremer * backend/epson.c: Added workaround for GT-8000 scanners, fixed two warnings reported by der Mouse. 2003-03-24 Rene Rebe * backend/Makefile.in backend/avision.c backend/avision.conf backend/avision.h doc/descriptions/avision.desc: updated avision backend to latest build. This includes user-space USB support and OS/2 thread handling. 2003-03-24 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Canon CanoScan FB620U. Added more info for HP ScanJet 3500c. 2003-03-24 Rene Rebe * include/sane/sanei_usb.h sanei/sanei_usb.c: support for interrupt endpoint reads 2003-03-23 Rene Rebe * doc/sane-usb.man fix typo 2003-03-22 Ulrich Deiters * backend/canon.c: Modified the option "eject after each scan" - Eject is suppressed for film scanners after preview scans. 2003-03-20 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Canon LIDE 50, IBM ADF Color Scanner, Plustek Spectra ADF. 2003-03-17 Henning Meier-Geinitz * backend/as6e.c: Fixed segmentation fault when $PATH is not set. 2003-03-16 Henning Meier-Geinitz * sanei/sanei_scsi.c: OS/2 specific changes: memory for SRB now is allocated using _tcalloc() instead of allocating on stack to avoid crossing 64k borders due to restrictions of 16-bit device-driver. Cosmetic changes and some casts to reduce compiler-warnings. Patch from Franz Bakan . * sanei/sanei_thread.c: Increased stacksize for thread. Patch from Franz Bakan . 2003-03-10 Oliver Schirrmeister * backend/fujitsu.c backund/fujitsu-scsi.h backend/fujitsu.h * patch from Ron Cemer fixes the broken "ADF empty" detection on the Fujitsu scanners when connected via USB. It also fixes a sense-request issues, which were required in order to determine whether the ADF is empty. It also eliminates some duplicate code blocks. * renamed some functions 2003-03-13 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx_devices.c backend/gt68xx_low.h doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Added Packard Bell Diamond 1200 Plus to .desc and manpage. New flag to avoid running stop_scan before doing a scan. The Windows firmware of the BearPaw 1200 CU Plus doesn't seem to like that. * doc/descriptions/unsupported.desc: Added some Boeder, Brother, HP and Trust scanners. * TODO: Removed fujitsu and ma1509 entries. Updated .desc files and global variables entry. 2003-03-10 Oliver Schirrmeister * backend/fujitsu.c backund/fujitsu-scsi.h displays the offending byte when something is wrong in the window descriptor block. 2003-03-09 Eddy De Greef * backend/mustek_pp_cis.c backend/mustek_pp_cis.h: Fixed two calibration bugs: one occasionally caused overexposed images at all resolutions; the other one occasionally caused color inbalances when scanning narrow areas at high resolutions. 2003-03-09 Henning Meier-Geinitz * sanei/sanei_scsi.c: Wait for the completion of all SCSI commands in sanei_scsi_flush_all_extended (Linux). Without this fix, especially long running commands and command queuing would cause memory corruption if the buffer was invalid after the flush command. * doc/descriptions/abaton.desc: Removed link to everex (timeout). 2003-03-07 Henning Meier-Geinitz * backend/mustek.c doc/descriptions/mustek.desc doc/mustek/mustek.CHANGES: Minor debug message updates. Fixed a little bug in sane_control_option (gamma control). Removed the signal blocking again. That only hides the segfault in sanei_scsi. Check if there are documents in ADF for Paragon 2. Otherwise return error. Fix some minor issues in reading the config file. 2003-03-07 Peter Fales * doc/descriptions/dc210.desc, doc/descriptions/dc240.desc: Fix broken URLs to the Kodak web pages 2003-03-06 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx_gt6801.c backend/gt68xx_gt6816.c backend/gt68xx_low.c doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Small requests are done with a response buffer of 8 bytes (instead of 64) now. Looks like GT-6816 based scanners return 8 bytes if the firmware is loaded and 64 bytes if it's not loaded. Changed GT-6816 firmware check to ignore errors from above change. GT-6816 scanners work with the BSDs now (at least once). Updated manpage concerning the BSDs. 2003-03-05 Henning Meier-Geinitz * backend/apple.c: Added missing option size (avoids crash on network scanning). Patch from Milon Firikis . 2003-03-05 Oliver Schirrmeister * backend/fujitsu.c, backend/fujitsu.h: renamed some variables * backend/fujitsu.c: gray duplex scanning now works with the 4097D when disconnect is enabled in the scsi-controller * doc/descriptions/unsupported.desc: remove fujitsu fi4120C * doc/descriptions/fujitsu.desc added fi4120C, ScanParter 93GX 2003-03-04 Henning Meier-Geinitz * include/sane/sanei.h sanei/sanei_constrain_value.c: Added function sanei_check_value in addition to the existing sanei_constrain_value. It checks if the value of a sane option fits into the constraint but doesn't try to do any fixing/rounding. * doc/sane-scsi.man: Changed the example to use /dev/sg0 instead of /dev/sge. * doc/descriptions/unsupported.desc: Removed Umax AstraSlim (now supported by artec_eplus48u backend). 2003-03-03 Gerhard Jaeger * doc/plustek/Plustek-USB-TODO.txt: update * backend/plustek-devs.c backend/plustek-usb.c backend/plustek-usbhw.c backend/plustek-usbimg.c backend/plustek-usbscan.c backend/plustek.c: Fixed some bugs, that avoid proper function of Genius devices and the HP2100c. Fixed also a problem, that causes permanent warmup cylces on EPSON Photo devices. 2003-03-02 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx_devices.c backend/gt68xx_gt6801.c doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Make sure that the firmware for plustek-like scanners is loaded. Didn't work for Genius vivid3xe. Added both Genius scanners to man page. Removed warning from Genius Vivid3xe. 2003-03-02 Michael Herder * doc/descriptions/artec_eplus48u.desc: * backend/artec_eplus48u.conf: Added new model Umax AstraSlim SE 2003-03-01 Henning Meier-Geinitz * doc/descriptions-external/ibm.desc: Added Ricoh IS-430. * doc/descriptions/unsupported.desc: Updated Microtek scanners. Added Umax AstraSlim SE. 2003-02-28 Ulrich Deiters * backend/canon-sane.c: Adjusted to use the new sanei_constrain_value function; made some cosmetic changes to reduce the number of compiler warnings 2003-02-27 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx.conf backend/gt68xx_devices.c backend/gt68xx_low.c backend/gt68xx_low.h doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Added Genius Colorpage Vivid3xe. Added request_type and request fields to the command set. Request is 0x01 or 0x04. * doc/descriptions/unsupported.desc: Added Compaq S4 100 and Microtek Scanmaker 4800. 2003-02-26 Henning Meier-Geinitz * doc/descriptions-external/hp5400.desc: Added description for hp5400 backend. It supports the Hewlett-Packard 5400, 5470 and (maybe) 5490 scanners. * doc/descriptions-external/viceo.desc: Added Visioneer OneTouch 8600. * doc/descriptions/unsupported.desc: Removed hp5400 series (now in hp5400.conf). Added Lexmark, Medion and Primax scanners. * doc/descriptions/ma1509.desc: Added :new token. * sanei/sanei_constrain_value.c: Round a word list value to the nearest entry (if necessary). Code was taken from canon backend. 2003-02-26 Ulrich Deiters * modified the Canon SCSI backend (now version 1.12): * backend/canon.c, canon-sane.c, canon.h: Added support for the FB1200S flatbed scanner. * backend/canon.c: Made the sense handler generate meaningful status information for new scanner models (solved the Mandrake crash problem). * backend/canon.c, canon-sane.c, canon.h: Moved model-dependent declarations and decisions to a common location. * backend/canon-sane.c: Fixed the out-of-memory problem of xscanimage when doing previews with scanners using hardware scan resolutions only. 2003-02-25 Henning Meier-Geinitz * po/sane-backends.fr.po: Updated French translation (from Yann E. MORIN ). 2003-02-23 Stéphane Voltz * backend/umax_pp_low.c backend/umax_pp_low.h backend/umax_pp_mid.c backend/umax_pp_mid.h backend/umax_pp.c backend/umax_pp.h backend/umax_pp.conf tools/umax_pp.c doc/sane-umax_pp.man: changed highlight option to contrast, and gain to brightness. 2003-02-24 Henning Meier-Geinitz * backend/Makefile.in backend/dll.conf backend/ma1509.c backend/ma1509.conf backend/ma1509.h: Added new backend ma1509 for Mustek BearPaw 1200F scanners. * doc/Makefile.in doc/descriptions/ma1509.desc doc/sane-ma1509.man doc/sane.man doc/.cvsignore: Added documentation for ma1509 backend. * doc/descriptions-external/ma1509.desc: Removed. * AUTHORS: Added ma1509. * po/Makefile.in po/sane-backends.de.po po/sane-backends.es.po po/sane-backends.fr.po po/sane-backends.nl.po po/sane-backends.pt.po po/sane-backends.ru.po po/sane-backends.sv.po: Updated for ma1509. * doc/backend-writing.txt: Some parts concerning PRJECTS/.desc files are rewritten. Added information about initializing global variables and sizeof (void *) != sizeof (int) (from Martijn van Oosterhout ). 2003-02-23 Simon Munton * backend/pie.c: increment BUILD (was missed in last change) 2003-02-23 Simon Munton * backend/pie.c: fixed segmentation faults when sane_init/sane_exit is run more than once. 2003-02-23 Stéphane Voltz * backend/umax_pp_low.c backend/umax_pp_low.c: Added global vars reset. Fixed a segmentation bug when using "" as opened device name. 2003-02-22 Frank Zago * doc/sane.tex: added Relisys and Mitsubishi to the list of vendors. Bumped the revision date. * frontend/tstbackend: added more sane_init/sane_exit tests. Added Fujistu and Relisys to the list of valid vendor names. 2003-02-22 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx.conf doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Added vid/pid 0x07b3/0x0400 to gt68xx.conf also. Added indormation about Medion 4394. Mention ma1509 backend. Fix segfault when calling sane_open with an empty device name. Explicitly initialize global variables to avoid segfaults when calling sane_init/sane_exit more than once. * backend/net.c doc/descriptions/net.desc: Initialize global variables in sane_init to avoid segmentation faults when sane_init/sane_exit is run more than once. * doc/descriptions-external/ibm.desc: Add Ricoh IS-420. * sanei/sanei_usb.c: Call sanei_usb_init only once. * backend/mustek_usb.c doc/descriptions/mustek_usb.desc doc/mustek_usb/mustek_usb.CHANGES doc/sane-mustek_usb.man: Iniatialize global variables explicitly. Mention ma1509. * backend/mustek.c doc/mustek/mustek.CHANGES: Initialize global variables in sane_init. * TODO: Add ma1509 backend. Add segfault when running sane_init more than once. Removed sanei_usb interface issue. 2003-02-20 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Removed Fujitsu fi-4220C (now supported by fujitsu backend). Added Syscan Travelscan 464. Updated HP scanners. 2003-02-20 Oliver Schirrmeister * backend/fujitsu.c: make option RIF available for 3091 and 3092, bugfix * backend/fujitsu.c: set availability of options THRESHOLD und VARIANCE 2003-02-20 Oliver Schirrmeister * backend/fujitsu.c, backend/fujitsu.h: patch from : fi4220 support USB support for scanners which send SCSI commands over usb 2003-02-19 Henning Meier-Geinitz * doc/descriptions-external/ma1509.desc: Added description of the ma1509 backend for Mustek BearPaw 1200F scanners. * doc/descriptions/unsupported.desc: Added Avision, Brother, and Umax scanners. Removed Mustek BearPaw 1200F. * sanei/sanei_usb.c: Check all acceptable interfaces instead of using the first one. * tools/sane-find-scanner.c: Check all interfaces. Print a more definite message when a scanner was found to avoid confusion. 2003-02-18 Stéphane Voltz * backend/umax_pp_low.c: icc compile fixes, and EPP mode setting fix for direct hardware access 2003-02-18 Henning Meier-Geinitz * po/sane-backends.fr.po: Added more French translations (from Yann E. MORIN ). * sanei/sanei_usb.c: usb_clear_halt should only be called in libusb mode. 2003-02-17 Henning Meier-Geinitz * backend/mustek.c doc/descriptions/mustek.desc doc/mustek/mustek.CHANGES: Make sure that the result of the reader_process is interpreted correctly. Block signals when waiting for a SCSI request to finish. Otherwise we may get a segfault if the reader_process is terminated while waiting. 2003-02-16 Henning Meier-Geinitz * doc/doxygen-sanei.conf: Updated version. * include/sane/sanei_usb.h: Fixed link to USB spec. * doc/descriptions/unsupported.desc: Added Artec scanners. * TODO: Updated desc file entry. * backend/gt68xx.c backend/gt68xx_devices.c doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: List BearPaw 2400 CS Plus as supported. Make fast preview the default (otherwise BP 2400 TA Plus wouldn't work with 12/16 bits). Added vid/pid 0x07b3/0x0400, that's another Plustek OpticPro 1248U. 2003-02-16 René Rebe * backend/avision.h backend/avision.c doc/sane.man doc/descriptions/avision.desc: update to the latest Avision backend release. It includes feature and stability improvements and bug fixes. The sane man-page now mentions that the avision backend supports more than the AV 630 CS ... 2003-02-15 Karl Heinz Kremer * backend/epson.c backend/epson_usb.[ch]: Fix problem with "usb syntax in config file * backend/epson.c: Move sanei_init_usb() to sane_init(). Thanks to Ron Cemer for the patch. 2003-02-13 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Updated Canon, Fujitsu, and Plustek entries. * TODO: Added entries about v4l, fujitsu, snapscan. 2003-02-11 Peter Fales * backend/dll.conf, doc/sane-gphoto2.man: Added a commented out gphoto2 entry in dll.conf, and mentioned it in the man page. * backend/gphoto2.c: Bug fixes. Don't coredump if the gphoto2.conf file is missing, and don't require a speed to be set if the usb port is used. 2003-02-11 Peter Kirchgessner * backend/hp-handle.c: Fix problem with ifdef/ifndef for threads 2003-02-11 Henning Meier-Geinitz * aclocal.m4 acinclude.m4 configure configure.in: Check for MacOS X native dynamic loading first before lookinf for libdl. 2003-02-09 Henning Meier-Geinitz * frontend/saned.c sanei/sanei_codec_bin.c sanei/sanei_wire.c: Check the IP address of the remote host before any communication occurs. Check for a errors before trsuting values that came from remote. Make sure that strings are 0-terminated. * po/Makefile.in po/sane-backends.nl.po: Added dutch (nl) translations for sane-backends (from Bertrik Sikken ). * doc/saned.man: More warnings about security issues. Make more clear that -s and -d can't be used in inetd mode. * PROBLEMS: Moved "memory exhausted" and missing scsi headers problems to README.linux. Removed SG_BIG_BUF problem. Rewrote Mustek "exceed the physical scan area" problem as it applies to most flatbed scanners. Added info about test backend. Added info about saned security issues. * README.linux: Moved "memory exhausted" and missing scsi headers problems from PROBLEMS. * TODO: Added saned info. Added artec at3 misdetection. * configure configure.in NEWS: Updated version number. 2003-02-09 Stéphane Voltz * backend/umax_pp.c: fixed big bug related to color plane synchronization, which gave blur effect at > 75 dpi * tools/umax_pp.c: change version number 2003-02-08 Peter Fales * backend/dc210.c, backend/dc240.c: Fixes for Irix from Andrea Suatoni. 2003-02-06 Henning Meier-Geinitz * Makefile.in: Added ChangeLog-1.0.10 to DISTFILES. * po/Makefile.in po/template.desc po/README: Don't generate po files with every make. That avoids recreation just because of date changes. Automatically create new languages on make update. Automatically generate list of DISTFILES. Added template for header used in new po files. Updated documentation. * doc/descriptions/unsupported.desc: Added Genius Colorpage Vivid III. Added more info for Plustek OpticPro 1212U and U12/UT12. 2003-02-05 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx_devices.c backend/gt68xx_high.c backend/gt68xx_high.h doc/descriptions/gt68xx.desc doc/descriptions/unsupported.desc doc/gt68xx/gt68xx.CHANGES: Fixed resolutions for BearPaw 1200 TA. Increase the lowest black for CCD coarse calibration. Adjusted internal gamma correction. CCD scanners use color mode for coarse+quality cal now. Preview isn't always in 8 bit mode now (can be selected by option). Upper and lower limits for coarse calibration are unified now. Added options for adjusting gain and offset. * po/sane-backends.de.po po/sane-backends.es.po po/sane-backends.fr.po po/sane-backends.pt.po po/sane-backends.ru.po po/sane-backends.sv.po: Adjusted for new gt68xx options. Updated German translation for gt68xx. 2003-02-04 Michael Herder * doc/descriptions/artec_eplus48u.desc: Unified manufacturer names according to Hennings suggestion. 2003-02-03 Henning Meier-Geinitz * doc/sane-backends.pt.po: Fixed UTF character. 2003-02-02 Peter Kirchgessner * backend/hp.c backend/hp-handle.c: Add support for OS/2 doc/descriptions/hp.desc: Version number changed 2003-02-02 Jochen Eisinger * doc/descriptions/mustek_pp.desc: cleaned up the file a little 2003-02-02 Henning Meier-Geinitz * doc/descriptions/gt68xx.desc doc/descriptions/unsupported.desc: Unified manufacturer names. Minor bugfixes. 2003-02-01 Henning Meier-Geinitz * configure configure.in: Warnings enabled again. Used extra version -cvs. Older entries can be found in ChangeLog-1.0.11. backends-1.3.0/ChangeLogs/ChangeLog-1.0.13000066400000000000000000001523671456256263500176230ustar00rootroot00000000000000****** Release of sane-backends 1.0.13. End of code freeze ****** 2003-11-13 Henning Meier-Geinitz * configure configure.in: New version: 1.0.13. 2003-11-20 Oliver Schwartz * doc/descriptions/snapscan.desc: Added model "SnapScan" (Bugtrack #300286) 2003-11-17 Henning Meier-Geinitz * po/sane-backends.sv.po: Updated Swedish translation (from Mattias Ellert ). 2003-11-16 Henning Meier-Geinitz * configure configure.in acinclude.m4 aclocal.m4: Changed check for linux/videodev.h. If that file is not compilable, we don't build the v4l backend. That's to avoid compilation errors if videov.h from Linux kernel 2.6.* is used. Include all the headers we also include in backend/v4l.c to make sure we find all problems. * backend/v4l.c: Try to not include linux/videodev2.h to avoid compilation errors. -- snapshot 1.0.13-pre3 ---- CODE FREEZE FOR SANE 1.0.13 --- 2003-11-16 Henning Meier-Geinitz * NEWS: Updated. * configure configure.in: Disabled warnings for release. 2003-11-15 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Brother DCP 8020 and UMAX Astra 3600. 2003-11-14 Eddy De Greef * doc/sane-mustek_pp.man: added the new libieee1284 style parport names. 2003-11-14 Gerhard Jaeger * backend/plustek.c backend/plustek-usb.cal backend/plustek-usbshading.c backend/plustek-usbhw.c: Endianness fixes. 2003-11-13 Eddy De Greef * backend/mustek_pp_cis.c: accept old style parport names to ensure backward compatibility with existing mustek_pp.conf files. 2003-11-13 Peter Fales * backend/gphoto2.h: Line beginning with "static static" was breaking compiles on some platforms. 2003-11-10 Gerhard Jaeger * backend/Makefile.in: Linking pie backend now against sanei_thread lib. * backend/pie.c: Switched backend over to sanei_thread usage, to make it also work with OS/2. 2003-11-08 Oliver Schwartz * backend/snapscan-options.c backend/snapscan.c Disabled quality calibration for Epson Perfection 1670 since it does not work reliably yet 2003-11-09 Henning Meier-Geinitz * doc/descriptions-external/lhii.desc: Added (back) the links to the external lhii backend that supports old handscanners. -- snapshot 1.0.13-pre2 2003-11-09 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Removed Epson Perfection 1670. It's now supported by the snapscan backend. 2003-11-08 Oliver Schwartz * backend/snapscan-options.c backend/snapscan-usb.c Fix TPO range for Epson 1670, fix compiler warnings on gcc 3.3 2003-11-08 Oliver Schwartz * backend/snapscan-scsi.c backend/snapscan.c doc/descriptions/snapscan.desc: Final bugfixes for Epson 1670 2003-11-07 Henning Meier-Geinitz * README.openbsd: Added info about setting permissions (Bug #300311). * doc/descriptions/unsupported.desc: Added Genius and Lexmark scanners. 2003-11-07 Oliver Schirrmeister * backend/fujitsu.c: Bugfix. If a scanner returned a color image in format rr...r gg.g bb...b the reader process crashed. * backend/fujitsu.[ch] Bugfix. The option gamma was enabled for the fi-4120. The result was an 'invalid field in parm list'-error. * doc/descriptions/fujitsu.desc: added model "fi-4530C" 2003-11-06 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Microtek and Visioneer scanners. 2003-11-04 m. allan noah * doc/descriptions/fujitsu.desc: added model "fi-4110EOX2" 2003-11-04 Gerhard Jaeger * backend/Makefile.in: Linking coolscan now against sanei_thread lib. * backend/coolscan.c backend/coolscan.h: Switched backend over to sanei_thread usage, to make it work at least with OS/2. * backend/plustek-pp_misc.c backend/plustek-pp_hwdefs.h: Fixed bug in parport setup routine. * sanei/sanei_pp.c: Fixed problem in sanei_pp_setmode(). Fixed parport mode setting in direct mode. 2003-11-02 Julien Blache * frontend/saned.c: fixed endianness issues in check_v{4,6}_in_range(), also fixed portability issues to other UNIX platforms due to different representations of IPv6 addresses. 2003-11-02 Gerhard Jaeger * doc/descriptions/unsupported.desc: Added Plustek OpticSlim 2400. * include/sanei/sanei_thread.h: Removed OS/2 porting section. * sanei/sanei_pp.c include/sanei/sanei_pp.h: Fixed udelay stuff, documentation update. 2003-11-02 Henning Meier-Geinitz * backend/Makefile.in: sanei_pp must be linked if we preload backends. ---- FEATURE FREEZE FOR SANE 1.0.13 --- -- snapshot 1.0.13-pre1 2003-11-02 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added PIE Powerslide 3600. * NEWS: Updated for 1.0.13. 2003-10-30 Gerhard Jaeger * sanei/lib/sanei_pp.c: Made sanei_pp_set_datadir work in no ieee1284 mode. Fixed a minor bug in delay calculation. * include/sanei/sanei_pp.h: Added some control-port definitions. * backend/plustek_pp.c backend/plustek-pp*: Made PS/2 bidirectional mode work. * doc/plustek/PLUSTEK-PARPORT.txt: Update. 2003-10-29 Henning Meier-Geinitz * backend/Makefile.in backend/mustek.c backend/mustek.h doc/descriptions/mustek.desc doc/mustek/mustek.CHANGES: Added support for sanei_thread. Removed OS/2 specific code. Added more debug messages. * doc/saned.man: Added more info about firewall trouble. 2003-10-29 Gerhard Jaeger * sanei/lib/sanei_pp.c include/sanei/sanei_pp.h: Added functions sanei_pp_set_datadir() and sanei_pp_uses_directio(). Added some documentation. * doc/plustek/PLUSTEK-PARPORT.txt doc/plustek/PLUSTEK-PARPORT-TODO.txt doc/plustek/PLUSTEK-USB-TODO.txt: Updates. * backend/plustek_pp.c backend/plustek-pp_*: Made usage of the new sanei_pp functions, made EPP modes work with libieee1284. Also some cleanup work. 2003-10-28 Henning Meier-Geinitz * doc/sane-scsi.man: Added hint for setting device permissions for /dev/xpt0 for FreeBSD users. * AUTHORS backend/artec.c backend/artec.h doc/sane-artec.man: Fixed Chris Pinkham's email address. * doc/descriptions/unsupported.desc: Added Microtek ScanMaker 4900. 2003-10-28 Rene Rebe * backend/avision.h backend/avision.c doc/descriptions/avision.desc backend/avision.conf AUTHORS: merge of the current Subversion revision of SANE/Avision. The fixes include: endianness fixes, calibration code, ADF detection, correct lamp warmup, gamma table for new ASICs, some workarounds for ASIC variations and the conversion to use OPT_SOURCE instead of separate OPT_ADF and OPT_TRANS. Also fixed Bugs #300288 and #300196 and removed the conflicting Option_Valu now present in sanei_backend.h * doc/sane-avision.man: made requested clarifications (Bugs #300290, #300291) 2003-10-28 Gerhard Jaeger * doc/sane-plustek.man backend/plustek.conf backend/plustek.c: Changed configuration file back to remain compatible with the old format. 2003-10-27 Karl Heinz Kremer * backend/epson.c: Replaced all DBG(0, statements with DBG(1, ... to avoid logging messages the user should not see anyways. 2003-10-27 Gerhard Jaeger * sanei/lib/sanei_pp.c: Fixed OS/2 compilation problems. * doc/descriptions/plustek_pp.desc: Updated status of Primax Compact 4800 Direct 30bit. 2003-10-26 Gerhard Jaeger * configure.in configure include/sanei/config.in.h: Added check for limits.h - HAVE_LIMITS_H. * sanei/lib/sanei_pp.c include /sanei/sanei_pp.h: Added sanei_pp_init() and sanei_pp_udelay(). * backend/plustek_pp.c backend/plustek-pp_misc.c backend/plustek-pp_scan.h: Fixed compiler warnings, using now sanei_pp_udelay() and made the backend work with libieee1284. ---- BACKEND FREEZE FOR SANE 1.0.13 --- 2003-10-24 Peter Kirchgessner * backend/hp.c, backend/hp-handle.c, backend/Makefile.in: second check-in: use new sanei_thread-interface for hp-backend 2003-10-24 Henning Meier-Geinitz * doc/descriptions-external/hp_rts88xx.desc: Added description file of new hp_rts88xx backend for HP 44x0 scanners (from Johannes Hub ). * doc/descriptions/unsupported.desc: Added Minolta DiMAGE Scan Elite 5400. Removed HP 44x0 (now supported by hp_rts88xx backend). Updated Epson Perfection 3170. Updated Plustek S24/ST24. * doc/descriptions/avision.desc doc/descriptions/canon.desc doc/descriptions/sharp.desc: Updated to new status values. Bugs: #300146, #300147, #300154. * include/sane/sanei_thread.h: Updated doxygen documentation for new sanei_thread interface. 2003-10-23 Frank Zago * README.windows: added note about libusb port for cygwin. 2003-10-23 Gerhard Jaeger * backend/plustek.c backend/plustek-usb.c: fixed bug, that causes CanoScan devices to use the wrong calibration functions, sanei_usb_get_vendor_product() fails. * sanei/sanei_pp.c: Added sanei_pp_getmode, fixed conditional compilation stuff. * include/sanei/sanei_pp.h: Added sanei_pp_getmode and SANEI_PP_MODE definitions. * backend/plustek_pp.c backend/plustek-pp.h backend/plustek-pp_misc.c: Added parport-mode detection. 2003-10-22 Peter Kirchgessner * backend/hp.c, backend/hp-handle.c, backend/Makefile.in: use new sanei_thread-interface for hp-backend 2003-10-22 Gerard Klaver * doc/descriptions/teco2.desc backend/teco2.conf doc/sane-teco2.man: Mustek ScanMagic 4830S added. * doc/sane-teco2.man: maintainer Gerard Klaver added, text update. * doc/teco/teco2.txt: data VM3564 Relisys AVEC II S3 added. 2003-10-22 Julien Blache * frontend/saned.c: replaced in_addr_t mask; by u_int32_t mask; in check_v4_in_range(). in_addr_t doesn't seem to exist on OS/2, and it's nothing more than an unsigned 32 bits integer. 2003-10-22 Gerhard Jaeger * configure configure.in include/sane/config.in.h: added checks for iopl. * sanei/sanei_pp.c sanei/Makefile.in include/sanei/sanei_pp.h include/sanei/Makefile.in: added generic parallel-port function lib. * backend/plustek-pp* backend/plustek_pp.c: major update, sanei_pp integration, compilation fixes. * backend/plustek.c: fixed, the "generic disable" switch 2003-10-21 Oliver Schwartz * backend/snapscan.h backend/snapscan.c backend/snapscan-option.c backend/snapscan-scsi.c backend/snapscan.conf: Bugfixes, hopefully better support for Epson 1670. 2003-10-21 Henning Meier-Geinitz * doc/descriptions-external/epkowa.desc: Added Perfection 1670 as "unsupported". Changes status of 1260 PHOTO to :basic because of broken TPU support. Fixed missing quotation mark in Perfection 1200S entry. * AUTHORS: Oliver Schirrmeister has a CVS account now. * doc/sane-mustek_usb.man: Added workaround for hardware bug. 2003-10-21 Stéphane Voltz * doc/sane-umax_pp.man: added help for new configuration option * tools/umax_pp.c: uppded version number 2003-10-20 Frank Zago * README.aix doc/sane-leo.man doc/sane-matsushita.man doc/sane-sceptre.man doc/sane-teco1.man doc/sane-teco2.man doc/sane-teco3.man doc/descriptions/leo.desc doc/descriptions/matsushita.desc doc/descriptions/sceptre.desc doc/descriptions/teco1.desc doc/descriptions/teco2.desc doc/descriptions/teco3.desc doc/descriptions-external/hp4200.desc: fixed urls. 2003-10-19 m. allan noah * backend/fujitsu.c: reverse previous patch. Dont blindly trust code from the backend maintainer :) 2003-10-19 Julien Blache * frontend/saned.c: added subnet support to saned ; added check_v4_in_range and check_v6_in_range functions. * backend/saned.conf: updated the config file. * doc/saned.man: updated the manpage for saned. 2003-10-18 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added HP ScanJet 3670. * doc/descriptions-external/epkowa.desc: Added description file for the external Epson Kowa backend. 2003-10-17 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Reflecta iScan 1800. Updated information for Epson Perfection 1670. * README.linux: Added information about compilation problems when Linux 2.6 is installed. * tools/sane-desc.c: In error and warning messages print the name of the description file. Print warning if the status is not set for a device. * backend/test.c: Reindented. * AUTHORS: Nathan Ruthman has CVS access now. 2003-10-17 Karl Heinz Kremer * doc/descriptions/epson.desc: Fixed bug #30149 (added :status information for all scanners) 2003-10-17 Gerhard Jaeger * acinclude.m4 aclocal.m4 configure: Added -D_REENTRANT to compiler flags, when using pthread support. * sanei/sanei_thread.c: Added SIGPIPE handling, when compiled for pthread support. * backend/test.c: Removed blocking SIGPIPE, as this is now handled in sanei_thread library. * backend/plustek.h backend/plustek.c backend/plustek-usbhw.c: Added checks for intervaltimer, to make it compile on OS/2. 2003-10-16 m. allan noah * backend/fujitsu.c: memset the device struct and the scsi buff when a new device is attached. Submitted by Chris Chesney . Initialized a couple vars in the 3091 init routines. 2003-10-15 Henning Meier-Geinitz * AUTHORS: Marked maintainers that have CVS access with a (*). Added m. allan noah to fujitsu backend. Added Julien Blache and Petter Reinholdtsen to "Miscellaneous coding". Removed Petter Reinholdtsen from "CVS repository maintainer". * doc/sane-usb.man: sane-find-scanner is in section 1, not 5. 2003-10-15 Gerhard Jaeger * include/sanei/config.h.in configure.in configure: Added test for setitimer. 2003-10-14 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Pentax, Sicos, Syscan, and UMAX scanners. 2003-10-13 Henning Meier-Geinitz * backend/gt68xx_low.c: Fixed compilation bug when debugging is disabled. * backend/test.c doc/descriptions/test.desc: Fixed cancelling when threads are used. Only SIGPIPE is blocked now. Sleep indefinetly after the reader_process has finished. Minor fixes of debug messages. 2003-10-13 Gerhard Jaeger * sanei/sanei_thread.c include/sane/sanei_thread.h: Fixed OS/2 compilation * sanei/sanei_lm983x.c include/sane/sanei_lm983x.h: Copyright update 2003-10-12 Gerhard Jaeger * sanei/sanei_thread.[ch]: Improved OS/2 support * backend/test.c: removed OS/2 section 2003-10-12 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Memorex, TCE and Optrox scanners. 2003-10-10 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Canon DR-2080C. Updated some links. * backend/mustek_pp_ccd.conf doc/sane-mustek_pp_ccd.man: Explained that libieee1284 device names like "parport0" can also be used. 2003-10-10 Gerhard Jaeger * sanei/sanei_thread.c: Fixed some compilation errors on Darwin and OS/2 Fixed segfault condition in sanei_thread_waitpid 2003-10-09 Peter Kirchgessner * backend/hp.c backend/hp-option.c backend/hp-scl.c: Bug #300241: fix inverse image on 3c/4c/6100C at 10 bit depth Redo when TEST UNIT READY failed Redo when read returns with 0 bytes (non-SCSI only) * doc/sane-hp.man: Add environment SANE_HP_RDREDO, SANE_HOME_HP 2003-10-09 Stéphane Voltz * backend/umax_pp.c backend/umax_pp_low.h backend/umax_pp.h backend/umax_pp_mid.c backend/umax_pp_low.c backend/umax_pp_mid.h backend/umax_pp.conf: added automatic parallel port detection, and special keyword in configuration file to use it. 2003-10-08 Gerhard Jaeger * backend/Makefile.in : linking plustek_pp- and test-backend against sanei_thread * backend/test.[ch]: added sanei_thread support * backend/test-picture.ch: fixed compiler warnings * backend/plustek.[ch] backend/plustek_pp.c backend/plustek-pp.h: changes due to the sanei_thread-lib work * sanei/sanei_thread.c include/sane/sanei_thread.h: improved support of pthreads, changed behaviour of sanei_thread_kill(), added functions sanei_thread_sendsig() and sanei_thread_get_status() changed behaviour of sanei_thread_waitpid(), changed parameters of sanei_thread_begin() 2003-10-08 Henning Meier-Geinitz * backend/canon630u.c: Workaround for bug in canon630u backend: The backend assumes that the id it gets back from sanei_usb_open is a file descriptor. It isn't. So sane_get_select_fd returns just a random number. That breaks at least saned, other frontends may not work either. This is a workaround for bug #300257. 2003-10-07 m. allan noah * backend/fujitsu.c: removed ~25 '_' from option names (#300139) * backend/fujitsu.conf: added lines for known usb scanners 2003-10-07 Oliver Schwartz * backend/snapscan.h backend/snapscan-scsi.c Updates for Epson Perfection 1670 2003-10-07 Oliver Schwartz * backend/snapscan.h backend/snapscan.c backend/snapscan-option.c Initial support for Epson Perfection 1670, minor bugfix (#300247) 2003-10-07 Henning Meier-Geinitz * acinclude.m4 aclocal.m4 configure.in configure include/sane/config.h.in sanei/sanei_thread.c: More verbose help messages for --enable-fork-process. Print message about pthread support after all the tests. If libpthread isn't available, try if libc supports pthreead_*. Use macro USE_PTHREAD to check if threads are used in the code. * backend/Makefile.in: Added sanei_pthread.lo to list of object files needed for preloading. 2003-10-07 Gerhard Jaeger * aclocal.m4 acinlcude.m4 configure configure.in: modified checks for pthread support, added switch --enable-fork-process which toggles between the usage of fork and pthread, set the default behaviour to pthread usage on Darwin * include/sane/config.h: added the HAVE_LIBPTHRAD stuff * sanei/sanei_thread.c include/sane/sanei_thread.h: fixed compilation issue on OS/2, added support for fork too, so that we can use sanei_thread functions in any case. * backend/plustek.[ch]: changes due to the sanei_thread-lib work 2003-10-06 m. allan noah * backend/fujitsu.[ch]: Added support for color modes of fi-4x20C and fi-4340C, maybe others as well. 2003-10-06 Peter Kirchgessner * backend/hp.c, backend/hp-option.h: Bug #300248 fixed (correct "Negatives" in option description to "Negative") 2003-10-06 Henning Meier-Geinitz * doc/Makefile.in: Remove doxygen-sanei.conf in clean target. * sanei/sanei_pa4s2.c: Made some internal functions static. 2003-10-06 Thomas Soumarmon * backend/hp5400_*: fixed bug #300252 : added static to all non SANE specific functions + changed #include into #include "../include/sane/..." as mentioned in the backend-writing.txt file 2003-10-05 Gerhard Jaeger * aclocal.m4 configure configure.in: added checks for pthread.h and added linker option -lpthread if pthread.h is found * include/sane/config.in.h: added HAVE_PTHREAD_H * sanei/sanei_thread.c include/sane/sanei_thread.h: added pthread_ functions to library, so every backend is able to use either fork or pthread for its reader-process - see plustek backend for example * backend/plustek.h backend/plustek.c: added sanei_thread stuff to support threading on MacOS X * backend/plustek_pp*: some cleanup 2003-10-05 Henning Meier-Geinitz * backend/microtek2.h backend/test.c include/sane/saneopts.h: Fixed some missing or duplicated spaces in option descriptions. * backend/canon630u.c: Option names shouldn't be translated. * backend/mustek.c: Use the same title for brightness as in other backends. * po/sane-backends.de.po: Fixed minor bugs. * po/sane-backends.sv.po: Updated (from Mattias Ellert ). * po/sane-backends.bg.po po/sane-backends.cs.po po/sane-backends.es.po po/sane-backends.fr.po po/sane-backends.it.po po/sane-backends.nl.po po/sane-backends.no.po po/sane-backends.pt.po po/sane-backends.ru.po: Regenerated. * doc/descriptions/unsupported.desc: Added Epson Perfection 3170, Packard Bell Diamond 1200, and Visioneer PaperPort 3100b. Updated Epson Perfection 1670. 2003-10-04 Karl Heinz Kremer * backend/epson.[ch]: Fixed bug 300246 - Use SANE_TITLE_SCAN_SPEED (and DESC and NAME) from saneopts.h instead of my own strings. Fixed typo in gamma correction description. Start two descriptions with a capital character. 2003-10-04 Henning Meier-Geinitz * AUTHORS configure configure.in backend/Makefile.in backend/dll.conf backend/mustek_pp_ccd.c backend/mustek_pp_ccd.conf backend/mustek_pp_ccd.h doc/Makefile.in doc/sane.man doc/sane-mustek_pp_ccd.man doc/descriptions/mustek_pp_ccd.desc: Added back old mustek_pp backend. That backend supports Mustek CCD scanners. The backend was renamed to mustek_pp_ccd to avoid conflicts with the new mustek_pp backend for CIS scanners. The code was taken from sane-backends 1.0.9. This is only a temporary solution. The goal is to incooperate CCD and CIS support into mustek_pp. * doc/sane-mustek_pp.man doc/descriptions/mustek_pp.desc: Added links to the mustek_pp_ccd backend. Removed scanners supported by mustek_pp_ccd. 2003-10-03 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Microtek ScanMaker 5900. * doc/descriptions/umax1220u.desc: Converted to new format (bug #300155). Commented out unsupported UMAX Astra 2200U to avoid confusion. * doc/Makefile.in: Cleanup. Removed install target for mostang.com. * doc/descriptions/microtek2.desc: Converted to new format (bug #300153). Commented out ScanMaker 9600XL (was mentioned twice?). * doc/descriptions/hpsj5s.desc: Converted to new format (bug #300151). Added comment about only gray mode. * doc/descriptions/fujitsu.desc: Converted to new format (bug #300150). * doc/descriptions/canon630u.desc: Converted to new format (bug #300148). 2003-10-03 Karl Heinz Kremer * doc/descriptions/epson.desc: Changed "status" information 2003-10-02 Gerhard Jaeger * backend/plustek_pp* backend/plustek-usb*: fixed some OS/2 compiling issues * doc/plustek: fixed module compilation stuff and increase version number 2003-10-02 Peter Fales * backend/gphoto2.c backend/gphoto2.h: Try to handle the case where the camera has a corrupt file that can't be decoded as a JPEG image. 2003-10-01 Eddy De Greef * doc/sane-mustek_pp.man, doc/descriptions/mustek_pp.desc: Added Mustek 96 CP to list of supported scanners. 2003-09-30 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Fixed links to messages in the mailing list archive. * configure configure.in doc/Makefile.in doc/doxygen-sanei.conf doc/doxygen-sanei.conf.in: Automatically update the version number of SANE in the doxygen documentation. * doc/backend-writing.txt doc/sane.man: Updated links to sanei documentation. * doc/.cvsignore include/sane/sanei.h include/sane/sanei_scsi.h: Minor fixes for sanei documentation. * backend/gt68xx.c backend/gt68xx.conf backend/gt68xx_devices.c backend/gt68xx_high.c doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Added detection of Genius Vivid 4x. It won't work out-of-the-box, however. CCD coarse calib shouldn't run into an endless loop anymore. Plustek 1248U is much faster now. 2003-09-30 Gerhard Jaeger * doc/descriptions/unsupported.desc: removed OpticSlim entry * doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES doc/sane-gt68xx.man backend/gt68xx.conf: added Plustek OpticSlim entry * backend/gt68xx_devices.c: added settings for Plustek OpticSlim 1200 2003-09-29 Gerhard Jaeger * doc/sane-plustek.man: minor update * backend/plustek.c: changed version number, cleanup * backend/plustek-usb*.c: cleanup * backend/plustek-pp.*: cleanup * backend/plustek-share.h: removed, no longer needed * backend/Makefile.in: removed reference to plustek-share.h 2003-09-28 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Enhans, Genius and UMAX scanners. * backend/test.c doc/descriptions/test.desc: Some options had wrong constraints or didn't match their descriptions (bug #300235). 2003-09-25 Frank Zago * README.windows: updated status of xscanimage and XSane. 2003-09-25 Henning Meier-Geinitz * po/Makefile.in: Added plustek_pp.c. * po/sane-backends.nl.po: Updated Dutch translation (from Martin Kho ). * po/sane-backends.bg.po po/sane-backends.cs.po po/sane-backends.de.po po/sane-backends.es.po po/sane-backends.fr.po po/sane-backends.it.po po/sane-backends.no.po po/sane-backends.pt.po po/sane-backends.ru.po po/sane-backends.sv.po: Regenerated. * doc/Makefile.in: Fixed link to image in HTML manpages. 2003-09-24 Oliver Schwartz * backends/snapscan.c: check second argument of sanei_config_get_string (Bug #300198) 2003-09-24 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Changed links to point to sane-project.org. * TODO: Removed. Please use the bug tracking system at http://www.sane-project.org/bugs.html instead. * README README:os2 configure configure.in: Updated contact information (website, mailing lists, bug tracker). Minor fixes. * backend/mustek.c: Fixed sane-devel address. * doc/backend-writing.txt doc/releases.txt: Updated contact information. Spelling fixes. Minor updates. * doc/sane.man: Updated contact information. Removed contributing section (now on homepage). Spelling fixes, general updates. * sane-abaton.man sane-agfafocus.man sane-apple.man sane-bh.man sane-dc210.man sane-dc240.man sane-dc25.man sane-gphoto2.man sane-hp.man sane-microtek2.man sane-microtek.man sane-mustek.man sane-mustek_pp.man sane-pint.man sane-st400.man sane-usb.man sane-v4l.man: Updated links to sane-devel and SANE homepage. * sane.tex: Changed homepage and sane-devel-request address. * doc/Makefile.in: Added README.windows. * doc/canon/canon.install2700F.txt: Updated links. * include/sane/sanei.h include/sane/sanei_usb.h: Updated links. * tools/sane-desc.c: Changed links to sane-project.org. Mentioned bug tracker. Added contact link. Removed link to special USB and Parport lists (now on static web pages). * tools/sane-config.in: Updated links. 2003-09-24 Gerhard Jaeger * doc/sane.man, AUTHORS: added plustek_pp information * doc/backend-writing.txt: Updated CHECKLIST information * doc/sane-plustek.man: removed parport sections * doc/sane-plustek_pp.man: removed usb sections * backend/plustek.c: changed version number * backend/plustek-usb*.c: minor fixes, esp. CanoScan LiDE30 gray/color bug * backend/plustek-pp.*: changed file headers and copyright info applied some minor fixes * doc/unsupported.desc: Added Plustek OpticPro S28 and S48 * Makefile.in doc/Makefile.in: removed TODO, as this file does no longer exist * doc/plustek/*: changed links to www.sane-project.org (Bug #300215) 2003-09-23 Frank Zago * Makefile.in NEWS configure configure.in backend/Makefile.in backend/dll.c include/sane/config.h.in sanei/sanei_scsi.c tools/sane-find-scanner.c: cygwin port. 2003-09-23 Gerhard Jaeger * configure.in: added new backend plustek_pp * doc/descriptions/plustek_pp.desc : added * doc/plustek/*: added/updated various doc-files * doc/sane-plustek_pp.man doc/Makefile.in : added new man page * backend/dll.conf : added plustek_pp * backend/Makefile.in : added plustek_pp files, updated plustek files * backend/plustek-devs.c backend/pustek-pp.c : removed * backend/plustek-usbcalfile.c backend/plustek-usbcal.c backend/plustek-usbdevs.c: new files for the plustek usb backend * backend/plustek_pp.c backend/plustek_pp.conf * backend/plustek-pp.h backend/plustek-pp_dac.c backend/plustek-pp_dbg.h backend/plustek-pp_detect.c backend/plustek-pp_genericio.c backend/plustek-pp_hwdefs.h backend/plustek-pp_image.c backend/plustek-pp_io.c backend/plustek-pp_map.c backend/plustek-pp_misc.c backend/plustek-pp_models.c backend/plustek-pp_motor.c backend/plustek-pp_p12.c backend/plustek-pp_p12ccd.c backend/plustek-pp_p48xx.c backend/plustek-pp_p9636.c backend/plustek-pp_procfs.c backend/plustek-pp_procs.h backend/plustek-pp_ptdrv.c backend/plustek-pp_scale.c backend/plustek-pp_scan.h backend/plustek-pp_scandata.h backend/plustek-pp_sysdep.h backend/plustek-pp_tpa.c backend/plustek-pp_types.h backend/plustek-pp_wrapper.c : new added, contains all the code necessary for controlling various Plustek ASIC 9600x/9800x based parallelport scanner 2003-09-21 Henning Meier-Geinitz * tools/sane-desc.c: PATH_MAX is too small for long comments on win32. Actually return 0 if there is no second quotation mark. Patch from Frank Zago . Check for !word everywhere to avoid segfaults when a quotation mark is missing. * sanei/sanei_config.c: Actually return 0 if there is no second quotation mark. * backend/ma1509.c backend/mustek.c backend/mustek_usb.c backend/test.c doc/descriptions/ma1509.desc doc/descriptions/mustek.desc doc/descriptions/mustek_usb.desc doc/descriptions/test.desc doc/mustek/mustek.CHANGES doc/mustek_usb/mustek_usb.CHANGES: Check if sanei_config_get_string fails because of a single quotation mark in the configuration file. Update version numbers. 2003-09-19 Henning Meier-Geinitz * doc/descriptions/microtek.desc: Used new :status scheme. Added Vobis Highscreen Realscan and Genius Colorpage-SP2. * doc/descriptions/unsupported.desc: Added HP, Microtek and UMAX scanners. 2003-09-18 Stéphane Voltz * backend/sane-umax_pp.c: fix default port address handling (patch from Malcolm Parsons ) * configure.in configure include/sane/config.h.in: added detection of machine/cpufunc.h and i386_set_ioperm() for FreeBSD support. 2003-09-16 Stéphane Voltz * doc/sane-umax_pp_low.man: updated man page 2003-09-15 Simon Munton * doc/sane-pie.man doc/descriptions/pie.desc: Updated status of ScanAce III to 'good' based on feedback from Brian Wood. 2003-09-14 Karl Heinz Kremer * backend/epson.c: put Henning's #include change back in. 2003-09-14 Stéphane Voltz * backend/umax_pp_low.c: reworked inb/out functions. The backend now works on FreeBSD. 2003-09-12 Karl Heinz Kremer * backend/epson.c: Increment only once in loop to find USB scanners, fix rounding error when calculating number of lines to scan. 2003-09-12 Eddy De Greef * AUTHORS, backend/mustek_pp_cis.c, backend/mustek_pp_cis.h, doc/sane-mustek_pp.man: Updated e-mail address. 2003-09-12 Henning Meier-Geinitz * doc/.cvsignore sanei/.cvsignore: Added .libs. Sorted. * doc/descriptions/unsupported.desc: Updated HP ScanJet 2300c and 2400c. * backend/abaton.c backend/abaton.h backend/agfafocus.c backend/agfafocus.h backend/apple.c backend/apple.h backend/artec.h backend/artec_eplus48u.h backend/as6e.c backend/as6e.h backend/avision.h backend/bh.c backend/bh.h backend/canon.c backend/canon.h backend/coolscan.h backend/dmc.c backend/dmc.h backend/epson.c backend/epson.h backend/fujitsu.h backend/gt68xx.c backend/gt68xx_high.h backend/ibm.c backend/ibm.h backend/leo.h backend/ma1509.c backend/ma1509.h backend/matsushita.h backend/microtek.c backend/microtek.h backend/microtek2.c backend/microtek2.h backend/mustek.c backend/mustek.h backend/mustek_pp.c backend/mustek_pp.h backend/mustek_usb.c backend/mustek_usb_high.h backend/nec.c backend/nec.h backend/pie.c backend/pint.c backend/pint.h backend/plustek.h backend/qcam.c backend/qcam.h backend/ricoh.c backend/ricoh.h backend/sceptre.h backend/sharp.c backend/sharp.h backend/sm3600.c backend/sm3600.h backend/snapscan.c backend/snapscan.h backend/tamarack.c backend/tamarack.h backend/teco1.h backend/teco2.h backend/teco3.h backend/test.h backend/umax.c backend/umax.h backend/umax_pp.c backend/umax_pp.h backend/v4l.c backend/v4l.h include/sane/sanei_backend.h: Moved union Option_Value from backend header files to sanei_backend.h. No need to copy it over and over again. Changed header inclusion order in backend files to include backend.h after sanei_backend.h. Based on a patch from stef . 2003-09-09 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Canon Lide 80, updated Canon and HP information. 2003-08-27 Henning Meier-Geinitz * backend/gt68xx.c doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Added Mustek ScanMagic 1200 UB Plus. Minor bugfix. * doc/descriptions/unsupported.desc: Added UMAX Astra 4450 and Mustek BearPaw 2448 TA Pro. Updated Canon CanoScan 5000F. 2003-08-22 Henning Meier-Geinitz * po/Makefile.in po/sane-backends.it.po: Added Italien translation (from Luca Clemente ). 2003-08-22 Karl Heinz Kremer * backend/epson*.[ch]: Fixed compile problem on Linux Code cleanup to get rid of compiler warnings 2003-08-21 Karl Heinz Kremer * backend/epson.c: Removed '//' comments - again ... Added EPSON KOWA copyright 2003-08-21 Henning Meier-Geinitz * README.darwin sanei/sanei_scsi.c: Added support for the MacOS X IOKit SCSI Architecture Model API. Added support for Firewire scanners. Patch from Guy Brooker . * TODO: Added entries for mustek patch, sanei_usb devfs trouble, and moving of Option_Value. Updated .desc file section. * doc/descriptions/unsupported.desc: Updated sections about Canon, Epson, HP, Medion, Microtek, Mustek, and Xerox scanners. 2003-07-22 Oliver Schwartz * backend/snapscan.c backend/snapscan.h backend/snapscan-scsi.c backend/snapscan-options.c backend/snapscan.conf: Backend version 1.4.27 (ID cleanup, fix for firmware download of Acer 310/320, new USB ID for Acer 310) 2003-08-17 Stéphane Voltz * backend/umax_pp.c backend/umax_pp.h: revert change for UTA 2003-08-15 Karl Heinz Kremer * backend/epson.[ch]: Added support for GT-30000, with support for the ADF in simplex mode (used some code from the EPSON Kowa IScan version of the backend) * backend/epson_scsi.c: Use sanei_scsi_cmd2() to send commands to fix a problem with SBP-2 under FreeBSD 2003-08-15 Stéphane Voltz * backend/umax_pp.c backend/umax_pp.h: added a check of values passed in gamma table. Fixed a bug which prevented custom gamm and UTA to be active 2003-08-14 Gerhard Jaeger * doc/sane-plustek.man: Added debug description * backend/plustek.c backend/plustek-usbhw.c backend/plustek-usbshading.c backend/plustek-usbscan.c backend/plustek-devs.c Fixed warmup bug Fixed EPSON12x0 and CanoScan hangs during init step Some fine-tuning for the CanoScan devices 2003-08-10 Gerhard Jaeger * doc/descriptions/plustek.desc: Update to new keywords, added Genius Colorpage HR6X EPP * TODO: moved Plustek topics to done section 2003-08-07 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Plustek OpticSlim 1200. Updated Mustek Paragon 600 II ED/EP information. 2003-07-31 Peter Kirchgessner * doc/descriptions/hp.desc: Add status by model 2003-07-31 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: All the unsupported entries that pointed to .txt files have links to HTML pages now. 2003-07-29 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Minolta and Visioneer scanners. Updated Avision, Medion, and UMAX scanners. * tools/check-usb-chip.c: Added test for Genesys Logig GL660 + GL646 combination. Added some more messages about what's going on. * TODO: Added frontend linking issue and color management discussion. Removed sanei_jpeg move and avision patch entries. Updated global init issue and desc file entry. * po/sane-backends.bg.po: Updated Bulgarian translation (from Pavel Constantinov ). 2003-07-29 Matthew Duggan * backend/canon_pp.c: Reset globals in sane_exit. 2003-07-28 Andras Major * doc/descriptions/coolscan2.desc: changed IEEE1394 to IEEE-1394 and changed to new status keyword scheme. 2003-07-28 Henning Meier-Geinitz * po/Makefile.in po/sane-backends.bg.po: Added Bulgarian translation (from Pavel Constantinov ). 2003-07-28 Thomas Soumarmon * doc/descriptions/hp5400.desc : changed status to basic 2003-07-28 Thomas Soumarmon * backend/hp5400_* : moved constant initialization to sane_init and sane_exit + removed some compilation warnings. 2003-07-27 Henning Meier-Geinitz * Makefile.in backend/Makefile.in doc/Makefile.in frontend/Makefile.in include/Makefile.in sanei/Makefile.in: Minor cleanup from the jpeg move. Reverted frontend link order. 2003-07-26 Oliver Schwartz * backend/snapscan-usb.c backend/snapscan-usb.h: Changed license to GPL + SANE exception. 2003-07-26 Peter Fales * sanei/Makefile.in, backend/cderror.h, backend/jinclude.h, backend/dc210.c, backend/dc240.c, backend/ gphoto2.c, backend/djpeg.c, backend/cdjpeg.h, backend/Makefile.in, configure configure.in, frontend/Makefile.in: Move jpeg support file to sanei directofiles to include/sane, and associated configure changes 2003-07-26 Henning Meier-Geinitz * include/sane/sanei.h include/sane/sanei_codec_ascii.h include/sane/sanei_codec_bin.h include/sane/saneopts.h: Changed license to GPL + SANE exception. * AUTHORS frontend/scanimage.c: Updated email address of Andreas Beck. * include/lalloca.h include/lassert.h: Changed license to GPL + SANE exception. * LICENSE: Clarify that not all backends us GPL + SANE exception. Fixed pointer to README.djpeg. * sanei/linux_sg3_err.h: Added GPL + SANE exception license. 2003-07-25 Peter Fales * include/sane/sanei_cderror.h, include/sane/sanei_jinclude.h include/sane/sanei_jpeg.h sanei/sanei_jpeg.c: Install these files as copies of the ones currently in backend. (The old files will be deleted later) * Moved backend/djpeg.README to README.djpeg and added usage info 2003-07-25 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx.conf doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Added Fujitsu 1200CUS to man page, .conf and .desc. Changed .conf file to be more readable. Added RevScan 19200i to .conf file. Added Plustek U16B to .desc and .conf, updated UT16B. * po/sane-backends.de.po: Added some translations for the HP backend. Minor header updates. * po/sane-backends.cs.po po/sane-backends.es.po po/sane-backends.fr.po po/sane-backends.nl.po po/sane-backends.no.po po/sane-backends.pt.po po/sane-backends.ru.po po/sane-backends.sv.po: Regenerated to include recent changes in the backends. * include/sane/sanei_scsi.h include/sane/sanei_wire.h: Changed license to GPL + SANE exception. * TODO: Added canon630u SANE_I18N and include file license problem. Updated .desc file section. 2003-07-24 Peter Kirchgessner * backend/hp.c, backend/hp-option.h, backend/hp-option.c po/Makefile.in: Add support for Internationalization for hp-backend. 2003-07-24 Henning Meier-Geinitz * tools/check-usb-chip.c: Added support for detecting Genesys Logic GL646. Fixed indentation. 2003-07-23 Henning Meier-Geinitz * include/sane/sanei_thread.h include/sane/sanei_usb.h: Changed license to GPL + SANE exception. 2003-07-22 Henning Meier-Geinitz * doc/descriptions/mustek_pp.desc: Tag CCD scanners as unsupported and mention that they work in 1.0.9 or earlier. 2003-07-22 Oliver Schwartz * doc/descriptions/snapscan.desc: Updated to use new status values. 2003-07-22 Matthew Duggan * doc/descriptions/canon_pp.desc: Updated to use new status values. 2003-07-21 Henning Meier-Geinitz * TODO: Added mustek_pp CCD support and avision patch entry. Updated .desc file section. * doc/descriptions/unsupported.desc: Added more information for Canon 9900F. 2003-07-21 Stéphane Voltz * doc/descriptions/umax_pp.desc: removed unwanted status line for backend 2003-07-20 Stéphane Voltz * doc/descriptions/umax_pp.desc: updated forgotten status to use new keyword. 2003-07-20 Oliver Rauch * backend/umax.c: changes for Umax UC630 3pass color scan * doc/descriptopns/umax.desc: changed status 2003-07-19 Frank Zago * backend/teco2.c backend/teco2.h backend/teco2.conf: (from gerard Klaver and Michael Holler) Better detection of the scanner. Added white level. More work on some color resolutions. * updated several backend descriptions file. 2003-07-19 Peter Fales * doc/descriptions/gphoto2.c: Remove "status" (not used for APIs) and correct typo in comment. 2003-07-19 Henning Meier-Geinitz * doc/descriptions/as6e.desc doc/descriptions/bh.desc doc/descriptions/coolscan.desc doc/descriptions/nec.desc doc/descriptions/sm3600.desc doc/descriptions/st400.desc: Converted to use new :status values. * doc/descriptions/unsupported.desc: Sorted (more or less) alphabetically. Added some Canon and Hewlett-Packard scanners. * frontends/scanimage.c: When parsing string options whose arguments are too short, don't overwrite the remaining command line arguments. Print "[inactive]" after every inactive option. * TODO: Added fujitsu patch, description file updates, close_on_exec problem. Removed scanimage bugs. 2003-07-18 Henning Meier-Geinitz * doc/sane-find-scanner.man tools/Makefile.in tools/check-usb-chip.c tools/sane-find-scanner.c: Added support for detecting USB chipsets to sane-find-scanner. 2003-07-17 Abel Deuring * sanei/sanei_scsi.c: improved SCSI error handling in sanei_scsi_cmd2 for FREEBSD_CAM_INTERFACE 2003-07-17 Henning Meier-Geinitz * sanei/sanei_usb.c: Enable close_on_exec in sanei_usb_open. 2003-07-16 Henning Meier-Geinitz * TODO: Added fujitsu option and libtool problem. Updated desc file entries. 2003-07-15 Stéphane Voltz * doc/descriptions/umax_pp.desc: updated to use new :status keyword. 2003-07-10 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Corex and Relisys scanners. Updated Canon and HP scanners. * doc/descriptions-external/primax.desc: Fixed typo. 2003-07-02 Thomas Soumarmon * backend/hp5400_internal.c: Added version string for v0.87 which has been reported to work ok. Use of a list of versions instead of several constants. 2003-07-02 Peter Fales * backend/gphoto2.c: Suppress the "bad parameter" message printed when a camera is not connected, by using Debug level 1 2003-07-01 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Memorex, Visioneer and Xerox scanners. Updated information for Visioneer scanners. 2003-06-28 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Visioneer OneTouch 5800 USB. * doc/doxygen-sanei.conf: Update version. 2003-06-25 Henning Meier-Geinitz * README.darwin: Mention some more working scanners. * doc/descriptions/unsupported.desc: Added Brother and Microtek scanners. 2003-06-20 Henning Meier-Geinitz * tools/sane-desc.c: Mention backend name in warnings and errors. 2003-06-20 Michael Herder * doc/descriptions/artec_eplus48u.desc: Updated status fields 2003-06-19 Peter Fales * doc/descriptions/dc25.desc, dc240.desc: Updated status fields 2003-06-19 Simon Munton * doc/descriptions/pie.desc: Updated status fields 2003-06-19 Eddy De Greef * doc/descriptions/mustek_pp.desc: Updated status fields of CIS type scanners. 2003-06-19 Henning Meier-Geinitz * doc/descriptions.txt: Changed unmaintained tag to lowercase. * doc/descriptions/template.desc. doc/descriptions-external/template.desc.: Updated according to descriptions.txt. * doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Added RevScan 19200i. Updated device status entries. * doc/descriptions/abaton.desc doc/descriptions/agfafocus.desc doc/descriptions/apple.desc doc/descriptions/artec.desc doc/descriptions/dc210.desc doc/descriptions/dll.desc doc/descriptions/dmc.desc doc/descriptions/ibm.desc doc/descriptions/ma1509.desc doc/descriptions/mustek.desc doc/descriptions/mustek_usb.desc doc/descriptions/net.desc doc/descriptions/pint.desc doc/descriptions/pnm.desc doc/descriptions/qcam.desc doc/descriptions/ricoh.desc doc/descriptions/s9036.desc doc/descriptions/sp15c.desc doc/descriptions/tamarack.desc doc/descriptions/test.desc doc/descriptions/v4l.desc: Updated to new status system. Added "unmaintained" where appropriate. The status values are sometimes guessed from the documentation. * doc/descriptions/unsupported.desc: Added Benq parport scanners. * doc/descriptions-external/hp4200.desc doc/descriptions-external/hpoj.desc doc/descriptions-external/niash.desc doc/descriptions-external/primax.desc doc/descriptions-external/scanwit.desc doc/descriptions-external/v4l2.desc doc/descriptions-external/viceo.desc: Updated to new status system. The status values are sometimes guessed from the documentation. 2003-06-18 Henning Meier-Geinitz * tools/sane-desc.c: Changed to use the new status system for devices: unsupported, untested, minimal, basic, good, complete. The old backend and device statuses are translated. Removed the option to generate one big table of backends (wasn't used anyway). * doc/descriptions.txt: Explained the new status values. Removed reference to emacs-lisp code for generation of HTML files. Explained unsupported.desc. * tools/sane-desc.el.in tools/sane-desc-ext.el: Removed because these emacs-lisp scripts haven't been used for some time now. Use sane-desc.c instead. * configure configure.in tools/.cvsignore tools/Makefile.in tools/README: Removed references to sane-desc.el. * Makefile.in: Added ChangeLog-1.0.12 to DISTFILES. 2003-06-15 Henning Meier-Geinitz * README.darwin: Minor updates. * TODO: Added v4l2 problem, desc file issues. Removed sm3600 MacOS X problem + printing backends in configure. * doc/descriptions/unsupported.desc: Added Canon D2400UF and IS 12. 2003-06-13 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added link for HP 5550c. * doc/descriptions/hp5400.desc doc/descriptions/ma1509.desc: Removed :new marker. * doc/descriptions/tamarack.desc: Removed link to www.tamarack.net. Hostname is no longer in DNS. 2003-06-11 Oliver Schirrmeister * backend/fujitsu.c: fixed bug in that code when a scanner is disconnected (anoah at pfeiffer dot edu) 2003-06-10 Michael Herder * po/Makefile.in: Added Czech translation * po/sane-backends.cs.po: Added Czech translation provided by Josef 2003-06-09 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Canon 8000F. More info for Avision/Umax scanners. 2003-05-27 Frank Zago * backend/teco2.c: fixed a bug regarding color shifting above X max resolution. Added more working resolutions for the VM3575. 2003-06-05 Thomas Soumarmon * hp5400 backend : synchronized sourceforge CVS with SANE CVS, details below added : backend/hp5400_debug.c backend/hp5400_debug.h backend/hp5400_sane.c backend/hp5400_sanei.h backend/hp5400_internal.h modified : backend/hp5400.c backend/hp5400.h backend/hp5400_internal.c backend/hp5400_sanei.c backend/hp5400_xfer.h to compile and run (on linux 2.4.21) as SANE backend and in the corresponding hp5400backend sourceforge project 2003-06-06 Henning Meier-Geinitz * TODO: Removed saned issues. * doc/descriptions/unsupported.desc: Added Microtek 336 CX. * configure configure.in backend/Makefile.in: The list of backends that are built is now in configure.in. Print backends that are built. Allow manually setting the backends. 2003-06-05 Jochen Eisinger * include/sane/sanei_pa4s2.h, sanei/sanei_pa4s2.c: New sanei_pa4s2 version supporting both raw IO and libieee1284. If you don't have libieee1284 installed, you need to enable raw IO when running configure 2003-06-06 Oliver Schirrmeister * backend/fujitsu.h backend/fujitsu.c doc/descriptions/fujitsu.desc: remove SP15 code sane_open actually opens the device you request from (anoah at pfeiffer dot edu) 2003-06-05 Henning Meier-Geinitz * backend/gt68xx.c doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Don't check if the firmware is loaded. The check seems to confuse the USB system on some UHCI chips. 2003-06-04 Henning Meier-Geinitz * sanei/sanei_scsi.c: Fixed C++-like code for MacOS X (patch from Mattias Ellert ). * acinclude.m4 aclocal.m4 configure frontend/saned.c include/sane/config.h.in: Check if ss_family or __ss_family is in struct sockaddr_storage. Otherwise diasbale ipv6. Patch from Julien BLACHE . 2003-06-03 Oliver Schirrmeister * backend/fujitsu.c, backend/fujitsu.h: separated the 4x20 into another model and color support for the 4x20 2003-06-03 Henning Meier-Geinitz * backend/Makefile.in: hp5400 doesn't need sanei_config2. 2003-05-30 Henning Meier-Geinitz * po/sane-backends.fr.po: Fixed two wrong codings of accented characters. * backend/net.c: Fixed bug concerning hosts that have both IPv6 and IPv4 addresses but only accept connections on IPv4. Patch from Julien BLACHE . * doc/descriptions/net.desc: Mention IPv6. Bump version number. 2003-05-27 Frank Zago * PROJECTS: Changed entry for win32 SANE project. 2003-05-28 Henning Meier-Geinitz * doc/descriptions/unsupported.html: Added link for Umax astra 4700. 2003-05-27 Henning Meier-Geinitz * configure configure.in: Don't disable IPv6 if poll is not found. * frontend/saned.c: Use u_int_* instead of uint_*. That fixes compilation for MacOS X (hopefully). * doc/Makefile.in: Add option -nodepage to man2html to avoid missing lines in the html output. * doc/descriptions/unsupported.html: Added link for Spot scanners. 2003-05-25 Henning Meier-Geinitz * configure configure.in: Warnings enabled again. Added -cvs to version. Older entries can be found in ChangeLog-1.0.12. backends-1.3.0/ChangeLogs/ChangeLog-1.0.14000066400000000000000000001066011456256263500176120ustar00rootroot00000000000000****** Release of sane-backends 1.0.14. End of code freeze ****** 2004-04-30 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added link to more info about Microtek ScanMaker 3630. * NEWS: Updated for release. * backend/sm3600.h: Added last minute fix to avoid compilation trouble with gcc 3.4. * configure configure.in: New version: 1.0.14. 2004-04-29 Oliver Schwartz * backend/snapscan.conf doc/descriptions/snapscan.desc Added Benq 640BT, changed status of Epson 1670 to "good" 2004-04-24 Henning Meier-Geinitz * po/sane-backends.sv.po: Updated Swedish translation (from Mattias Ellert ). ---- CODE FREEZE FOR SANE 1.0.14 --- -- snapshot 1.0.14-pre3 2004-04-24 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Lexmark X5130, UMAX Astra 4000 and 4600. * doc/descriptions-external/genesys.desc: Removed comments from some scanners as the chipset is known now. * configure configure.in: Disabled compilation warnings. 2004-04-24 Jochen Eisinger * doc/sane-usb.man: added comment about devmode not working with current (2.6.3-2.6.5) 2.6 kernels 2004-04-23 Henning Meier-Geinitz * backend/dll.c: Fixed dynamic loading of backend libraries on OS/2. Patch from Franz Bakan . 2004-04-23 Gerhard Jaeger * doc/sane-u12.man doc/plustek/Plustek-USB.changes: updates. 2004-04-19 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx_devices.c doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Minor fixes for Mustek BearPaw 2448 TA Plus. * doc/descriptions/unsupported.desc: Added Canon IS-32. 2004-04-17 Henning Meier-Geinitz * acinclude.m4 aclocal.m4 configure: Don't use -ansi on MacOS X. 2004-04-17 Henning Meier-Geinitz * acinclude.m4 aclocal.m4 configure: If the test for gphoto2-config fails, don't call it later. -- snapshot 1.0.14-pre2 2004-04-17 Henning Meier-Geinitz * NEWS: Added some more updated backends and danish translation. * tools/hotplug/libsane.usermap: Added Epson Stylus CX5400 and Nikon Coolscan 5000 ED. Minor documentation updates. 2004-04-15 Oliver Schwartz * backend/snapscan-mutex.c: Fixed compilation problem on OS/2 (Thanks to Franz Bakan). * configure.in configure: SnapScan backend now compiled in by default on OS/2 2004-04-15 Marian Eichholz * backend/sm3600.h backend/sm3600-scanmtek.c backend/sm3600-color.c backend/sm3600-gray.c backend/sm3600-homerun.c: Many fixes for MacOS-X problems pointed out by Mattias Ellert, thanks: Waiting longer for DoJog() command completion, new function for transmitting the line gain correction data with correct endianness, Chassis-Gray-Level lowered. * backend/sm3600.c : Fixed off-by-one-error in p->lines backend/sm3600-scanmtek: Fixed just broken UploadGainCorrection() 2004-04-14 Karl Heinz Kremer * backend/epson.conf: Added "usb" entry for libusb 2004-04-14 Rene Rebe * backend/avision.h, backend/avision.c: the current development state, including cleanups, code refactoring, dynamic color and scan source list creation, implemented ADF mirroring needed for some ASICs , some scan window scaliing fixes for some ASICs, added 16bit gray and color support, fixed a tiny big-endian issue, fixed command error reporting for some ASICs, improved inquiry logging and some tiny fixes for film-scanners. 2004-04-14 Gerhard Jaeger * backend/plustek_pp.c backend/plustek-pp_sysdep.h backend/plustek-pp_scan.h backend/plustek-pp_ptdrv.c backend/plustek-pp_misc.c: Fixed Kernel 2.6 compilation problems. * doc/plustek/Makefile.kernel24 doc/plustek/Makefile.kernel26 doc/plustek/MakeModule.sh: added to make kernel-module compilation easier. * doc/plustek/BUILD doc/plustek/VERSION0 doc/plustek/Plustek-PARPORT.txt doc/sane-plustek.man: update. * backend/u12.c backend/u12-hw.c backend/u12-if.c: minor fixes. * doc/u12/U12.changes doc/u12/U12.todo: update. * doc/descriptions/u12.desc: added RevScan Orange R48Ti. * sanei/sanei_thread.c: fixed some warning conditions. 2004-04-14 Henning Meier-Geinitz * po/Makefile.in po/sane-backends.da.po: Added Danish translation (from Mogens Jaeger ). 2004-04-12 Henning Meier-Geinitz * README.windows: Mentioned C++ misdetection trouble. * tools/Makefile.in: In make distclean also clean subdirectories. 2004-04-11 Karl Heinz Kremer * backend/epson_usb.c: Added missing device IDs for current MFDs ---- FEATURE FREEZE FOR SANE 1.0.14 --- -- snapshot 1.0.14-pre1 2004-04-10 Henning Meier-Geinitz * japi/.cvsignore: Updated. * japi/Jscanimage.java japi/Sane.c: Fixes from David Neary to make japi work with current jdks. More work and documentation is still needed. * backend/gt68xx.c backend/gt68xx.conf backend/gt68xx_devices.c doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Minor fixes for Mustek A3 USB, Genius ColorPage Vivid4 and Mustek BearPaw 2448 CS/TA Plus. * Makefile.in tools/Makefile.in: Added some missing files to DISTFILES. 2004-04-10 Jochen Eisinger * tools/sane-find-scanner.c, doc/sane-mustek_pp.man, doc/sane-find-scanner.man: added support for detecting mustek parallel port scanners. 2004-04-09 Gerhard Jaeger * backend/plustek.c: Fixes bug #300620. * sanei_thread.c: Fixes bug-reports #300617 & #300618, all reports and patches or patch-ideas by Mattias Ellert. 2004-04-09 Oliver Schwartz * backend/snapscan-usb.c backend/snapscan-mutex.c backend/Makefile.in: Use own file for mutex implementation to allow for easier porting to other platforms backend/snapscan-sources.c backend/snapscan.c: Bugfixes for pthread implementation 2004-04-08 Oliver Schwartz * backend/snapscan.c backend/snapscan-sources.c backend/snapscan.h backend/Makefile.in: Use sanei_thread functions instead of forking * backend/snapscan-options.c: Change title and description for OPT_HIGHQUALITY to use existing i18n translations * backend/snapscan-usb.c: Use urb counting (thanks to Jose Alberto Reguero) 2004-04-06 Gerard Klaver * backend/test.c changed some option group headers to advanced option menu. 2004-04-06 Jochen Eisinger * sanei/sanei_pa4s2.c include/sane/sanei_pa4s2.h, backend/mustek_pp.c: fixed compiler warnings 2004-04-04 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx.conf backend/gt68xx_devices.c backend/gt68xx_mid.c doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Added BGR color order for pixel mode. Plustek OpticPro U16B works in color mode now. Made 600 dpi mode for Mustek BearPaw 2400 TA Plus a bit faster. Changed Mustek ScanExpress A3 linedistance correction. Added detection of Visioneer Onetouch 7300. * doc/descriptions/unsupported.desc: Removed Canon D660. Now supported by plustek backend. 2004-04-04 Gerhard Jaeger * doc/descriptions/plustek.desc: added Canon D660U. * doc/plustek/Plustek-USB.changes doc/plustek/Plustek-USB-TODO.txt doc/plustek/Plustek-USB.txt: update * po/sane-backends.*.po: new strings, as the plustek backend has changed. * backend/plustek.[ch], plustek-usb.[ch], plustek-usb.cal.c, plustek-usbdevs.c, plustek-usbhw.c, plustek-usbimg.c, plustek-usbshading.c: added Canon D660U support, cleanup, bug-fixing. * backend/plustek.conf: added red_lampoff, green_lampoff and blue_lampoff parameters. 2004-04-02 Oliver Schwartz * backend/snapscan.c backend/snapscan-options.c backend/snapscan-scsi.c: Various bugfixes for gamma correction (Thanks to Robert Tsien) * po/sane-backends.de.po: Fix translation for "gamma table" 2004-04-02 Henning Meier-Geinitz * NEWS: Updated for sane-backends 1.0.14. * README.linux: Added some hints about setting permissions with libusb on Linux 2.6. Removed some obsolete paragraphs. * config.guess config.sub: Updated from stable libtool. * doc/descriptions/unsupported.desc: Added Relisys Episode scanner. 2004-03-29 Henning Meier-Geinitz * sanei/sanei_scsi.c: Increased SCSI buffer size on MacOS X. Patch from Mattias Ellert, bug #300601. 2004-03-28 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx_devices.c backend/gt68xx_gt6801.c backend/gt68xx_gt6816.c backend/gt68xx_high.c doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Plustek U16B: Added GT68XX_FLAG_SE_2400. Fixed color order.Increased debug level for setup_scan. Fixed CCD ocarse calibration. * doc/descriptions/microtek.desc: Added Agfa Studiostar. * doc/descriptions/unsupported.desc: Added Avigramm Minidoc, Medion MD 41260, and Nikon LS 50 ED. Removed Genius Colorpage Vivid III as it may work with the u12 backend. 2004-03-27 Peter Kirchgessner * backend/hp.c, hp-scl.c, hp.h, doc/sane-hp.man, doc-descriptions/hp.desc: Fix problem with USB-connections on Linux 2.6.x. Add environment SANE_HP_KEPPOPEN... and don't close connections for USB. 2004-03-21 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Tevion MD 40420. Moved Plustek OpticPro S(T)28 to genesys.desc. * doc/descriptions-external/genesys.desc: Moved Plustek OpticPro S(T)28 to genesys.desc. 2004-03-22 Oliver Schwartz * backend/snapscan.h Added detection for Epson 660 by USB ID since new models use new ID strings. 2004-03-21 Henning Meier-Geinitz * backend/mustek.c doc/descriptions/mustek.desc doc/mustek/mustek.CHANGES: Added detection of Mustek ScanExpress 1200 FS. * doc/descriptions/unsupported.desc: Added some Microtek and Agfa scanners. 2004-03-19 Karl Heinz Kremer * doc/descriptions/epson.desc: Added Perfection 4870 and CX-6400 * doc/descriptions/unsupported.desc: Removed CX-6400 2004-03-19 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx.conf backend/gt68xx_devices.c backend/gt68xx_high.c backend/gt68xx_low.h doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Genius Vivid 4xe now has color order RGB. Removed 50 dpi for Mustek BearPaw 2448 as that resolution doesn't work. Make sure that the lamp of the Mustek A3 USB gets warmed up. It's a CIS scanner with a lamp. That code is untested. Full scan starts scanning from the start position of the white strip now. Added detection for Genius Vivid 1200 EX. Fixed product id of Mustek BearPaw 2448 TA Plus. Other minor updates. 2004-03-15 Oliver Rauch * backend/umax: bugfix for sense_handler and do_calibration 2004-03-15 Stéphane Voltz * backend/umax_pp_low.c backend/umax_pp_low.h backend/umax_pp_mid.c backend/umax_pp.c tools/umax_pp.c: Added ppdev support for 610P detection. Fixed scan area overflow at >= 600 dpi. Fixed ppdev data direction setting. Fixed direct hardware ECP mode for 1220P. 2004-03-14 Henning Meier-Geinitz * tools/README tools/hotplug/README tools/hotplug/libsane.usermap tools/hotplug/libusbscanner: Added scanner ids (from Marcel Pol). Added more documentation. Added "chmod a+rw" alternative. * doc/descriptions-external/genesys.desc: Added Plustek OpticPro ST48. * doc/descriptions/unsupported.desc: Added Mustek A3 EP. Removed Plustek OpticPro ST48. 2004-03-13 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Vantas 3000. Added some links. 2004-03-09 Stéphane Voltz * backend/umax_pp_low.c backend/umax_pp_low.h backend/umax_pp_mid.c doc/sane-umax_pp.man doc/descriptions/umax_pp.desc tools/umax_pp.c: finished ECP support for 1220P/2000P. Added scanner detection and low-level communication for 610P. 2004-03-08 Henning Meier-Geinitz * tools/check-usb-scanner.c: GL841 max packet size can be 512 bytes on USB 2.0. * doc/sane-usb.man: The Linux scanner module was removed. The devmode trick in fstab doesn't work anymore. We now have a hotplug script. * doc/descriptions/unsupported.desc: Moved all scanners that may be supported by the genesys backend to genesys.desc. Added Agfa 1212 P. * doc/descriptions-external/genesys.desc: Moved all scanners that may be supported by the genesys backend to genesys.desc. Added links to scanner pages. 2004-03-04 Oliver Schirrmeister * backend/fujitsu.c enabled dropoutcolor for fi4x20C 2004-03-04 Oliver Schirrmeister * backend/fujitsu.c enabled dropoutcolor for fi4530C 2004-03-03 Gerhard Jaeger * doc/descriptions/unsupported.desc: Updated Plustek section. * doc/descriptions-external/genesys.desc: updated list of scanners that should be supported by the genesys backend. 2004-03-03 Henning Meier-Geinitz * frontend/saned.c: Added checks for bad handles for SANE_NET_CLOSE and SANE_NET_CANCEL. This is intended to stop saned from segfaulting when a bad handle is used. 2004-03-02 Karl Heinz Kremer * backend/epson.c: Added D8 function level for RX-500 2004-03-02 Henning Meier-Geinitz * backend/mustek_usb.c backend/mustek_usb_low.c backend/mustek_usb_low.h doc/sane-mustek_usb.man doc/mustek_usb/mustek_usb.CHANGES doc/mustek_usb/mustek_usb.TODO: Added workaround for toggle = data0 chipset bug. Fixes bug #300323. * doc/descriptions/unsupported.desc: Added link for HP ScanJet 8290. Added Logitec PageScan Color. 2004-03-01 Frank Zago * doc/descriptions-external/genesys.desc: created list of scanners that should be supported by the genesys backend. * doc/leo/leo.txt: doc update. 2004-03-01 Karl Heinz Kremer * doc/sane-epson.man: Corrected default function level from B5 to B3 2004-02-28 Frank Zago * tools/check-usb-chip.c: Added test for GL646 used in HP scanners. 2004-02-28 Julien Blache * tools/hotplug: added the hotplug script used by the Debian package, with a quick README. * tools/README: updated to mention the hotplug/ directory. 2004-02-27 Matthew Duggan * backend/canon_pp.c: Add more debug output to help resolve bug #300524 2004-02-22 Henning Meier-Geinitz * doc/scanimage.man frontend/scanimage.c: Added support for asking for pressing a key before scanning a page in a batch scan. Patch from Dominik Fischer . 2004-02-20 Oliver Schirrmeister * backend/fujitsu.c backend/fujitsu.h backend/fujitsu-scsi.h: merged the 3092 and the 3091 functions inverted the image in mode color and grayscale jpg hardware compression support (fi-4530C) 2004-02-20 Stéphane Voltz * backend/umax_pp_low.h backend/umax_pp_low.c backend/umax_pp.conf backend/umax_pp.c tools/umax_pp.c: added initial ECP support for scanner, laid groundwork for 610P support. 2004-02-14 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added HP ScanJet 4670 and Tamarack Artiscan 9600 pro 36. Removed Plustek OpticSlim 2400. * doc/descriptions/umax1220u.desc: Added UMAX Astra 1600U as this scanner is reported to work as "well" as the 2000U. 2004-02-11 Frank Zago * backend/matsushita.c backend/matsushita.conf doc/sane-matsushita.man doc/descriptions/matsushita.desc: added partial support for models KV-SS55EX, KV-S2025C, KV-S2045C and KV-S2065L. 2004-02-08 Karl Heinz Kremer * backend/epson*.[ch]: Reformat source code to get rid of different coding styles used over the years (and by different authors). 2004-02-08 Frank Zago * README.windows: added info about USB scanners. * backend/teco1.c backend/teco1.conf backend/teco1.h doc/sane-teco1.man doc/descriptions/teco1.desc: added new scanner Relisys RELI 4816, marked Relisys AVEC 2412 as tested. 2004-02-08 Oliver Rauch * backend/umax: bugfix for DOR mode, sane-umax-build-43 * doc/umax/umax.CHANGES updated 2004-02-07 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx.conf backend/gt68xx_devices.c backend/gt68xx_high.c doc/sane-gt68xx.man doc/descriptions/gt68xx.desc: Added basic support for Plustek OpticSlim 2400. Fixed calibration debug output. 2004-02-07 Karl Heinz Kremer * backend/epson.h, backend/epson_usb.h: Increase timeout for lamp warmup from 60 to 120 seconds 2004-02-07 Eddy De Greef * backend/mustek_pp_cis.c: cleaned up debug code * backend/mustek_pp_decl.h, backend/mustek_pp_drivers.h: bumped version number. * backend/mustek_pp.conf: fixed mistakes in examples 2004-02-07 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Genius Colorpage Vivid 1200EX and Mustek Paragon 800 II EP. 2004-02-04 Henning Meier-Geinitz * sanei/sanei_usb.c: Fixed inconsistent error message (patch from Mattias Ellert ). 2004-02-01 Gerhard Jaeger * sanei/sanei_thread.c: added Mattias Ellerts' patch to make sanei_thread_kill work on MacOSX. 2004-02-01 Karl Heinz Kremer * backend/epson.c: Added D7 function level as copy of D1 for CX-6400 * backend/epson_usb.c: Added IDs for CX-6400 and Perfection 4870 2004-02-01 Oliver Schwartz * backend/snapscan-options.c: Remove non-working resolutions for Epson Perfection 1670 * doc/descriptions/snapscan.desc: Add Guillemot Maxi A4 36 bit 2004-01-31 Matthew Duggan * backend/canon_pp.c: Remove more compile warnings. 2004-01-30 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Mustek BearPaw 4800TA Pro II. 2004-01-29 Henning Meier-Geinitz * backend/mustek_scsi_pp.c: Use gettimeofday() instead of ftime(). Bug #300482. 2004-01-21 Gerhard Jaeger * backend/test.c: reader_process terminates now, when running as thread. * backend/u12.c backend/u12-hw.c backend/u12-if.c backend/u12-io.c backend/u12-map.c backend/u12-motor.c backend/u12-shading.c backend/u12-tpa.c backend/u12.h backend/u12.conf: major update, compilation fixes. 2004-01-19 Gerhard Jaeger * doc/u12/U12.changes: update. * doc/u12/U12.todo: initial checkin. * backend/plustek-usb.c: cleanup. * backend/u12.c backend/u12-ccd.c backend/u12-hw.c backend/u12-if.c backend/u12-image.c backend/u12-io.c: activated cancel function, added lamp off timer function, cleanup, stability fixes. 2004-01-19 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx.conf backend/gt68xx_devices.c doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Debug options are only enabled if the debug level is 1 and higher. Added product id 0x0402 for Plustek U16B. 2004-01-17 Henning Meier-Geinitz * doc/gamma4scanimage.man: scanimage is in section 1. * doc/descriptions/unsupported.desc: Added Lexmark X74. 2004-01-15 Eddy De Greef * backend/mustek_pp.c, backend/mustek_pp_cis.c: Bug fix: make sure that the child process releases the parallel port under all circumstances. Otherwise, the parent process may no longer be able to control the scanner and the lamp stays on and doesn't return home. 2004-01-15 Gerard Klaver * include/sane/saneopts.h: Added options WHITE_LEVEL_R, WHITE_LEVEL_G, and WHITE_LEVEL_B. * doc/sane-teco2.man: changed --white-level to white-level-r, -g, -b update text * backend/teco2.c: changed WHITE_LEVEL to WHITE_LEVEL_R, _G and _B, changed calibration part for the VM3564, VM356A and VM3575 (subtract highest and lowest value and then divide). * backend/teco2.h: changed WHITE_LEVEL to WHITE_LEVEL_R, _G, _B. 2004-01-14 Gerhard Jaeger * backend/u12.c: Fixed preview bug. 2004-01-14 Gerhard Jaeger * descriptions/u12.desc: fixed wrong man-page reference, status updates. * backend/u12.c backend/u12.h backend/u12-hwdef.h backend/u12-scanner.h backend/u12-*.c: bump up version, fixed device autodetection and corrupted pictures, improved model-detection. 2004-01-13 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added several Canon, HP, Plustek, and UMAX scanners. * doc/descriptions-external/epkowa.desc: Epson 1670 is supported by SnapScan backend now. * doc/descriptions/gt68xx.desc doc/sane-gt68xx.man: Added Packard Bell Diamond 2450. 2004-01-13 Karl Heinz Kremer * backend/epson-usb.c: Added product ID for RX-500 2004-01-13 Gerhard Jaeger * doc/sane-plustek_pp.man: added missing explanation. Bug #300441. * include/sane/sanei_thread.h: updated copyright info. * sanei/sanei_thread.c: fixed return value handling for sanei_wait_pid(). 2004-01-12 Karl Heinz Kremer * backend/epson.c: Fixed bug # 300444 - Changed OPT_CCT_( desc. string. 2004-01-12 Oliver Rauch * backend/umax backend/Makefile.in: repleaced fork routines by sanei_thread * doc/umax/umax.CHANGES updated 2004-01-12 Matthew Duggan * backend/canon_pp-dev.c backend/canon_pp-io.c backend/canon_pp.c backend/canon_pp.h: Fix various compile warnings (Bug #300407). Also fix bug in parsing config file. * doc/descriptions/canon_pp.desc: Bump version number. 2004-01-11 Henning Meier-Geinitz * tools/sane-desc.c: When generating HTML anchor names, make sure they are unique. Bug #300439. * doc/descriptions/qcam.desc doc/descriptions/unsupported.desc: Fixed some broken links. 2004-01-10 Julien BLACHE * sanei/sanei_pa4s2.c: libieee1284 support for scsi_pp functions. This is untested but should work. 2004-01-10 Oliver Rauch * doc/umax: corrected links in umax documentations 2004-01-09 Henning Meier-Geinitz * doc/sane-config.man: Only one option can be used at the same time. * doc/descriptions/unsupported.desc: Added HP Scanjet 4600 and Quato scanners. Updated Epson 1270. 2004-01-09 Gerhard Jaeger * doc/u12/U12.changes: initial checkin. * doc/sane-u12.man doc/sane-plustek.man doc/sane-plustek_pp: added ENVIRONMENT information and did some minor updates. * descriptions/plustek_pp.desc descriptions/u12.desc: status updates. * po/sane-backends.de.po: fixed wrong translation. * doc/plustek/Plustek-USB.changes: update. * doc/plustek/Plustek-PARPORT-TODO.txt: update. * doc/Makefile.in: added u12 subdirectory. * backend/plustek.[ch] backend/plustek-usb*.[ch]: copyright updates and some changes concerning the CanoScan lamp calibration. * backend/u12.[ch] backend/u12-*.[ch]: copyright updates. * backend/plustek_pp.[ch] backend/plustek-pp*.[ch]: copyright updates and minor fixes. 2004-01-08 Oliver Rauch * backend/umax.c: changed order of includes because of compilation error 2004-01-07 Oliver Rauch * backend/umax.c: disabled quality calibration for Astra 2200 SU via USB 2004-01-05 Gerhard Jaeger * configure configure.in: added new u12 backend. * AUTHORS: added myself as u12 backend author. * backend/Makefile.in: added new backend u12. * backend/u12.c backend/u12.h backend/u12-scanner.h backend/u12-hwdef.h backend/u12.conf backend/u12-shading.c backend/u12-tpa.c backend/u12-ccd.c backend/u12-hw.c backend/u12-if.c backend/u12-image.c backend/u12-io.c backend/u12-map.c backend/u12-motor.c: initial checkin. * backend/dll.conf: added u12 backend. * doc/sane-u12.man: initial checkin. * doc/sane.man doc/Makefile.in: added man-page for u12 backend. * doc/descriptions/u12.desc: added descitpion for u12 backend. * doc/descriptions/unsupported.desc: removed Plustek OpticPro U12, UT12, 1212U, Genius Colorpage Vivid III, as they should be supported now. 2004-01-05 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx.conf backend/gt68xx_devices.c backend/gt68xx_gt6801.c doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Fixed a bug in the stop scan function for Plustek U16b. Added support for Genius Vivid4xe. * doc/descriptions/unsupported.desc: Added Visioneer 8820 and TCE S450. 2003-12-31 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added HP and Epson scanners. 2003-12-29 Henning Meier-Geinitz * tools/check-usb-chip.c: Added test for Genesys Logic GL841. 2003-12-27 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Updated/added several Medion scanners. * backend/as6e.c backend/coolscan.c backend/dll.c backend/ibm.c backend/microtek2.c backend/mustek_scsi_pp.c backend/mustek_usb_high.c backend/mustek_usb_low.c backend/mustek_usb_mid.c backend/nec.c backend/net.c backend/qcam.c backend/ricoh-scsi.c backend/s9036.c backend/sp15c-scsi.h backend/sp15c.c backend/sp15c.h backend/st400.c backend/tamarack.c backend/tamarack.h sanei/sanei_scsi.c tools/check-usb-chip.c: Silenced some compilation warnings. 2003-12-26 Henning Meier-Geinitz * README.darwin: Mentioned libusb problems. Updated fork section. 2003-12-25 Henning Meier-Geinitz * doc/descriptions-external/niash.desc: Added information that this backend will be included soon. Fixed "Snapscan" to "SnapScan" (bug #300394). * backend/mustek_scsi_pp.c backend/mustek_scsi_pp.h backend/Makefile.in backend/mustek.c backend/mustek.conf backend/mustek.h doc/sane-mustek.man doc/sane.man doc/descriptions/mustek.desc include/sane/sanei_pa4s2.h sanei/sanei_pa4s2.c: Added support for Mustek Paragon 600 II EP (SCSI-over-parallel port). This is based on a patch from James Perry. Support for libiee1284 is missing until now. Closes bug #300143. * AUTHORS: Added James Perry. * doc/descriptions/unsupported.desc: Removed Mustek Paragon 600 II EP/ED. Added Genius Colorpage-EP. * README: Updated concerning libieee1284. 2003-12-23 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added more information about the Benq Scanprisa 640P, Boder SmartSCan Office, and Trust CombiScan 19200. 2003-12-22 Gerard Klaver * tools/check-usb-chip.c: Added patch for test ICM532B. 2003-12-20 Henning Meier-Geinitz * backend/test.c doc/descriptions/test.desc: Fixed initial value for --fixed-constraint-range (bug #300388). * include/sane/sanei_usb.h: Include stdlib.h for size_t. * sanei/sanei_constrain_value.c: Fixed overflow (bug #300389). * acinclude.m4 aclocal.m4 configure configure.in backend/v4l.c backend/v4l.h doc/descriptions/v4l.desc: Don't include linux/videodev.h kernel header anymore. Use own defines instead. Fixes bug #300324. 2003-12-18 Gerhard Jaeger * doc/sane-plustek.man: new version, minor fixes * doc/descriptions/plustek.desc: new backend version * backend/plustek-pp*: cleanup work * backend/plustek-usb*: new version 0.47, added support for model override when one manufacturer uses the same model-id for various devices. 2003-12-16 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Canon 3200F. * backend/gt68xx_gt6801.h: Made internal function static. 2003-12-15 Oliver Schirrmeister * backends/fujitsu.[hc]: Bugfix: The options pagewidth and pageheight were disabled for the fi4530 2003-12-15 Eugene Weiss * backend/as6e.c: fixed bugs 300123 and 300133. 2003-12-15 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added some scanners. * doc/sane-mustek_pp_ccd.man: Removed (now again in mustek_pp backend). 2003-12-15 Oliver Schirrmeister * backends/fujitsu.[hc]: - 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 with x=100 and y=100 dpi with an fi4120 resulted in an image with 100,75 dpi. - Bugfix: Set the default value of gamma to 0x80 for all scanners that don't have build in gamma patterns. - Bugfix: fi-4530 and fi-4210 don't support standard paper size spezification. Disabled this option for these scanners. 2003-12-14 Henning Meier-Geinitz * AUTHORS: Eugene S. Weiss has CVS write access now. * doc/descriptions/unsupported.desc: Added and updated some scanners. * tools/sane-find-scanner.c: Actually use the number of the alt setting when printing descriptors. Fixes bug #300383 found by Gerard Klaver. * backend/gt68xx.c backend/gt68xx_devices.c doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Updated code for Genius Vivid 4x and Mustek ScanExpress A3 USB. Added Genius Colorpage Vivid3 V2 and Googlegear 2000 as supported devices. * backend/artec.c: Check for NULL before accessing a pointer. This may fix a segmentation fault mentioned in bug #300339. Also a compilation warning was fixed. 2003-12-13 Julien BLACHE * fronted/saned.c: use strcasecmp() instead of strcmp() when matching IPv6 addresses. 2003-12-12 Michael Herder * backend/artec_eplus48u.c backend/artec_eplus48u.h backend/artec_eplus48u.conf: added experimental support for the Artec E+ Pro fixed sane-Bugs-300261 (doesn't compile when NDEBUG is defined) 2003-12-09 Allan Noah * doc/desc/{fujitsu|ma1509}.desc: move fujitsu fi-4110eox2 section 2003-12-09 Allan Noah * backend/fujitsu.c: count USB packets to prevent timeouts, perhaps due to DATA0/1 toggle problems in certain scanners 2003-12-07 Peter Fales * acinclude.m4, aclocal.m4, configure: remove "enabling GPHOTO2" messages printed by configure 2003-12-07 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added some scanners. 2003-12-06 Peter Fales * acinclude.m4, aclocal.m4, configure: gphoto2 backend is now enabled by default if gphoto2-config is found. Use "--with-gphoto2=no" to disable it. 2003-12-06 Henning Meier-Geinitz * configure configure.in include/sane/config.h.in sanei/sanei_scsi.c: sanei_scsi.c should now compile with the new and old SCSI API of MacOS X. Patch from Mattias Ellert . * doc/descriptions/unsupported.desc: Added several scanners. * AUTHORS: Matthew Marjanovic and Marian Eichholz both have CVS access now. * sanei/sanei_usb.c: Check for /dev/uscanner* for NetBSD and OpenBSD. Patch from bug tracker #300373. 2003-12-01 Nathan Rutman * backend/canon630u.c: Change default scan size to full platen, removed compiler warnings, made HMG's fix for bug #300257 permanent. * doc/sane-canon630u.man: cleanup 2003-12-01 Jochen Eisinger * backend/mustek_pp_ccd300.c: Fixed horizontal offset of scan area and scaling for color scans 2003-11-27 Karl Heinz Kremer * doc/descriptions/epson.desc: Changed status for CX3200 and CX5200 from untested to good 2003-11-28 Oliver Schwartz * backend/snapscan.c: Download gamma table twice for Epson Perfection 1670 2003-11-27 Gerhard Jaeger * doc/plustek/Plustek.changes: moved to Plustek-USB.changes * doc/plustek/Plustek-PARPORT.changes: added * doc/plustek/Plustek-USB.txt doc/sane-plustek.man doc/sane-plustek_pp.man: Update 2003-11-26 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Epson CX6400. Removed Packard Bell Diamond 1200. Updated HP ScanJet 8200. * Makefile.in doc/descriptions/releases.txt: Generate md5 hash when making the a release. 2003-11-24 Henning Meier-Geinitz * AUTHORS: Michael Herder has CVS access now. * doc/descriptions/unsupported.desc: Added Biolux 654. Updated HP Photosmart entries. Removed Mustek BearPaw 2448 TA Plus (now listed by gt68xx.desc). 2003-11-23 Jochen Eisinger * backend/mustek_pp_ccd300.c: forgot parameters to wait_bank_change * include/sane/sanei_pa4s2.h: fixed function prototype 2003-11-23 Jochen Eisinger * doc/descriptions/mustek_pp.desc: changed :beta to :good 2003-11-23 Jochen Eisinger * backend/mustek_pp.c, sanei/sanei_pa4s2.c: added support for auto probing of the port * configure{.in}, backend/mustek_pp_ccd.*, backend/Makefile.in, backend/dll.conf, doc/sane-mustek_pp_ccd.man, doc/sane.man, doc/Makefile.in, doc/descriptions/mustek_pp_ccd.desc: mustek_pp_ccd backend removed * backend/mustek_pp.conf, mustek_pp_ccd300.[ch], doc/sane-mustek_pp.man, doc/descriptions/mustek_pp.desc: fixed CCD support for mustek_pp backend * backend/mustek_pp_null.c: beautified debug scanner name 2003-11-23 René Rebe * backend/avision.c backend/avision.h: the current development state, including cleanups, code refactoring and nearly complete HP 53xx/75xx ADF support 2003-11-23 Henning Meier-Geinitz * configure configure.in: Warnings enabled again. Added -cvs to version. * doc/releases.txt: Updated information about diffs. * doc/descriptions/unsupported.desc: Added HP and Mustek scanners. * backend/gt68xx.c backend/gt68xx.conf backend/gt68xx_devices.c backend/gt68xx_gt6801.c backend/gt68xx_gt6801.h backend/gt68xx_low.c backend/gt68xx_low.h doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Added Mustek BearPaw 2448 TA Plus. Added special command set for Plustek U16B. Added Plug-n-Scan 2400 M(T) to list of devices. No positive report yet. Used request for memory writes and reads instead of a fixed value of 0x04. Set Genius Vivid4x to GT6816. Untested. All changes have not been tested on their respective scanners yet. New version: 1.0.50. Older entries can be found in ChangeLog-1.0.13. backends-1.3.0/ChangeLogs/ChangeLog-1.0.15000066400000000000000000001126461456256263500176210ustar00rootroot00000000000000****** Release of sane-backends 1.0.15. End of code freeze ****** 2004-11-07 Henning Meier-Geinitz * doc/descriptions-external/brother.desc: One more report about a working scanner. Added link. * doc/descriptions/unsupported.desc: Added some scanners. * configure configure.in: New version: 1.0.15. 2004-11-07 Giuseppe Sacco * New italian translation update 2004-11-01 Oliver Schwartz * doc/descriptions/snapscan.desc: Status update for Epson scanners ---- CODE FREEZE FOR SANE 1.0.15 --- -- snapshot 1.0.15-pre2 2004-10-31 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx_high.h doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Removed unused gain and offset options. * NEWS: Added (planned) release date. * configure configure.in: Disabled compilation warnings. 2004-10-31 Gerhard Jaeger * backend/plustek.c: Additional debug output. * backend/plustek-usbscan.c: Fixed a warning condition. 2004-10-30 Gerhard Jaeger * backend/plustek.c: Bumped release number. * backend/plustek-usbscan.c: Fixed a bug in buffer calculation for CIS devices. * doc/plustek/Plustek-USB.changes: Update. 2004-10-30 Henning Meier-Geinitz * doc/descriptions/unsupported.desc doc/descriptions-external/hp_rts88xx.desc: Moved HP 35xx scanners to hp_rts88x backend. Updated status settings and URL (from johanneshub@foni.net). Updated link to Tamarack 9600 project. * doc/descriptions-external/brother.desc: Seems to work in version 0.0.12 at least for one scanner. 2004-10-27 Ullrich Sigwanz * backend/niash_core.c backend/niash.c: moving critical timing for backward movement to niash_core 2004-10-24 Henning Meier-Geinitz * doc/descriptions/unsupported.desc doc/descriptions-external/genesys.desc: Moved Plustek OpticFilm 7200 to genesys.desc (GL841 chipset). Added UMAX Astra 3400 (0x50 model). Added Xerox WorkCentre M15i. 2004-10-23 Giuseppe Sacco * Updated italian translation. 2004-10-21 Mattias Ellert * backend/gt68xx.c backend/mustek.c: Remove an extra "in". * po/*.po: Fixing fuzzy translations (and some others). 2004-10-21 Rene Rebe * backend/avision.h backend/avision.c: update of the scanner ID table, wait_4_light bugfixes, fixed color packing, fixed 16bit modes (especially for big-endian systems), avoid 16bit modes for default modes, fixed gamma-table for some models as well as spelling on the way 2004-10-20 Rene Rebe * backend/avision.c doc/descriptions/avision.desc doc/sane-avision.man: substituted my mail address and web-site to a new one - due to leaving the rocklinux project 2004-10-19 Gerhard Jaeger * backend/plustek.c: Using now the same strings for gain and offset like the umax_pp backend (bug #300962). * doc/plustek/Plustek-USB.changes: Update. * po/*.po: Update due to changes in the Plustek backend. * po/sane-backends.de.po: Fixed gain translation. 2004-10-18 Henning Meier-Geinitz * tools/sane-find-scanner.c: When getting string descriptors, ask for the length of the descriptor first (bug #301001). 2004-10-18 Ullrich Sigwanz * backend/niash_core.c: * backend/niash.c: correcting vertical scanning start point for changed max. page height 2004-10-17 Ullrich Sigwanz * backend/niash_core.c: using exact number of lines for a scan ---- FEATURE FREEZE FOR SANE 1.0.15 --- -- snapshot 1.0.15-pre1 2004-10-17 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Olivetti Job-Jet M400. 2004-10-17 Julien Blache * tools/hotplug/libsane.usermap: Added Epson Perfection 2480. 2004-10-16 Karl Heinz Kremer * backend/epson_usb.c: Added USB ID for Expression 10000XL 2004-10-16 Mattias Ellert * ltmain.sh: backport -framework support from libtool 2.0 * README.darwin: sane-find-scanner now works for SCSI, so removed statement that said it didn't. More libusb info, and link to libusb patch 2004-10-16 Henning Meier-Geinitz * frontend/saned.c: Fixed NULL string crash. * README.linux: Added some more details about hotplug and Gentoo problems. * NEWS: Updated for 1.0.15. 2004-10-17 Ullrich Sigwanz * backend/niash_core.c: rewrote buffer portioning * backend/niash_core.c (2): updated debug info * backend/niash.c: Enabling support of full DIN A4 size 2004-10-16 Oliver Rauch * backend/umax.c: added default options for Linotype OPAL2 2004-10-15 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added various scanners. * po/sane-backends.da.po: Updated Danish translation (from Mogens Jaeger ). 2004-10-14 Henning Meier-Geinitz * include/sane/sanei_wire.h sanei/sanei_wire.c: Limit the total amount of memory used for arrays and pointers while decoding the wire to 1 MB (bug #300158). Run "make clean" before "make"! 2004-10-14 Ullrich Sigwanz * backend/niash.c: removing a non-ANSI conform comma. adapting the gammma conversion. 2004-10-14 Gerhard Jaeger * sanei/sanei_thread.c: Added missing pthread_detach() so the thread resources could be reused again. 2004-10-13 Ullrich Sigwanz * backend/niash.c: Proper resource handling in sane_cancel. 2004-10-12 Gerhard Jaeger * backend/plustek.c: Fixed warning condition. * backend/plustek-usbhw.c: Cleanup. 2004-10-12 Giuseppe Sacco * Updated italian translation. * Added a "translators" section in AUTHORS 2004-10-11 Henning Meier-Geinitz * acinclude.m4 config.guess config.sub configure ltmain.sh: Update to libtool 1.5.10. * doc/descriptions/unsupported.desc: Added link to Microtek Filmscan 35. * backend/gt68xx.c backend/gt68xx.conf backend/gt68xx_high.c doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Fixed typos. Added Packard Bell Diamond 2450 to .conf and changed status to "good". Avoid error message when closing scanner. Don't print max_white warning when debugging is not enabled. * po/sane-backends.bg.po po/sane-backends.cs.po po/sane-backends.da.po po/sane-backends.de.po po/sane-backends.es.po po/sane-backends.fi.po po/sane-backends.fr.po po/sane-backends.it.po po/sane-backends.nl.po po/sane-backends.no.po po/sane-backends.pt.po po/sane-backends.ru.po po/sane-backends.sv.po: Recreated. Fixed some fuzzy texts and added some translations to .de.po. * tools/check-usb-chip.c: Workaround for GT6816 detection problem on BSDs. 2004-10-11 Oliver Schirrmeister (oschirr@abm.de) * backend/fujitsu.c: bugfix: 3091 did not work (15.12.2003) M4099 supported (bw only) enables brightness 2004-10-10 Henning Meier-Geinitz * backend/test.c: Fixed typo. * README.solaris: Added details on building SANE on Solaris/x86 (from Tomasz Orlinski ). 2004-10-10 Mattias Ellert * backend/Makefile.in: fix niash dependencies 2004-10-08 Stéphane Voltz * backend/umax_pp.c backend/umax_pp_low.c: 610P shading calibration improvements. 1220P offset and gain fixes. Code cleanups. 2004-10-08 Ullrich Sigwanz * backend/niash.c: Added grayscale and lineart support * backend/niash_core.c: corrected bug in line-weight in function _UnScrambleLine * doc/descriptions/niash.desc: changed status to complete removed the color only comment 2004-10-06 Gerhard Jaeger * backend/plustek.c backend/u12.c backend/plustek_pp.c: Using now the well known MODE definitions. * backend/plustek.h backend/plustek-usb.h: Cleanup. * doc/plustek/Plustek-USB.changes doc/plustek/Plustek-Parport.changes doc/u12/U12.changes: Update. * po/*.po: Update. 2004-10-06 Henning Meier-Geinitz * backend/microtek2.h doc/descriptions/microtek2.desc: New version: 0.96. Added Genius ColorPage-EP (from Karsten Festag ). * doc/descriptions/unsupported.desc: Added link to artiscan 9600 project. Removed Genius ColorPage-EP. * configure.in include/sane/config.h.in: Removed dangling quotation mark. * backend/sp15c.c: Don't eject medium twice after each page. 2004-10-05 Henning Meier-Geinitz * tools/sane-find-scanner.c: Fixed compilation problem when compiled without libusb support. Print sane-backends version number. Print if built without libusb. * backend/microtek2.c backend/microtek2.h: Added backend version 200410042220 from Karsten Festag . While attaching devices only read attributes from source 0 (= MD_SOURCE_FLATBED), others give wrong results. Better handling of different shading depths. Including model ColorPage-EP. Workaround for firmware bug for V300 (FW < 2.70). Workaround for firmware bug with odd pixel numbers. Bugfix for lamp switching when using LightLid35 Transparency Adaptor. * tools/check-usb-chip.c: Added test for interface 1 and altsetting 2 for GT-8911. Unified ouitput. Formatting updates. 2004-10-04 Henning Meier-Geinitz * doc/sane-find-scanner.man tools/check-usb-chip.c tools/sane-find-scanner.c: sane-find-scanner can now load USB descriptors from /proc/bus/usb/devices dumps (e.g. from the unsupported scanner web pages). Minor modifications to some of the chipset tests. 2004-10-04 Peter Kirchgessner * backend/hp.h backend/hp.c backend/hp-scl.c: Fixed bug #300973 (renamed global function hp_init_openfd to sanei_hp_init_openfd 2004-10-04 Gerhard Jaeger * backend/plustek.c: Fixed bug #300963. * doc/plustek/Plustek-USB.changes: Update. * po/*.po: Update. 2004-10-03 Thomas Soumarmon * backend/hp5400_debug.c backend/hp5400_debug.h backend/hp5400_internal.c backend/hp5400_sane.c: removing more hp5400 compilation warnings 2004-10-03 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Nikon LS-5000 ED (bug #300972). * include/sane/saneopts.h: Added option for turning off the scanner's lamp on exit (bug #300963). * backend/gt68xx.c backend/gt68xx_high.h: Used new lamp option from saneopts.h (bug #300963). * backend/net.c doc/descriptions/net.desc: Check for (size_t) -1 doesn't work on 64 bit platforms as size_t is 64 bits there while the length of a data block is defined as 32 bits in the SANE network standard (bug #300837). * backend/microtek2.c backend/microtek2.h: Fixed some warnings (bug #300823). Fixed Microtek Phantom C6 scanning on big endian platforms. Based on patch from Matijs van Zuijlen in Debian bug tracking system (#274523). * sanei/sanei_auth.c sanei/sanei_lm983x.c sanei/sanei_wire.c backend/artec.c backend/artec_eplus48u.c backend/as6e.c backend/avision.c backend/canon-sane.c backend/canon-scsi.c backend/canon.c backend/canon630u-common.c backend/dc25.c backend/epson.c backend/fujitsu.c backend/gt68xx_low.c backend/hp5400_internal.c backend/hp5400_sanei.c backend/ma1509.c backend/microtek.c backend/microtek2.c backend/mustek.c backend/mustek_scsi_pp.c backend/nec.c backend/net.c backend/pie.c backend/sharp.c backend/snapscan-scsi.c backend/snapscan-sources.c backend/snapscan-usb.c backend/snapscan.c backend/teco3.c backend/test.c backend/umax-usb.c backend/umax1220u-common.c: 64 bit platform fixes (bug #300799). * backend/Makefile.in tools/Makefile.in: Fixed DESTFILES. 2004-10-02 Thomas Soumarmon * backend/hp5400_internal.c backend/hp5400_internal.h backend/hp5400_sanei.c backend/hp5400_sanei.h backend/hp5400_sane.c: removing some compilation warnings 2004-10-02 Thomas Soumarmon * backend/hp5400_internal.c: hp5400 version matching test has been removed by default. To enable it : CFLAGS="-DSTRING_VERSION_MATCH" ./configure 2004-10-02 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added several scanners. Removed Genius Vivid 1200 XE (actually supported by gt68xx backend). * backend/gt68xx.c backend/gt68xx.conf backend/gt68xx_devices.c doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Updates of supported scanners. 2004-10-01 Oliver Rauch * backend/umax-scanner.c, umax.conf and umax.desc: added "LinoHell", "OPAL2 " as supported device 2004-09-28 Mattias Ellert * po/sane-backends.sv.po: Updated Swedish translation file 2004-09-27 Oliver Schwartz * doc/descriptions/unsupported.desc doc/descriptions/snapscan.desc: Changed entry for Epson 2580 (should work with SnapScan backend). 2004-09-21 Gerhard Jaeger * doc/descriptions/unsupported.desc: fixed Plustek entries. 2004-09-19 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Compeye Simplex 1236C. 2004-09-14 Karl Heinz Kremer * backend/epson_usb.c: add USB device ID for CX6400 back in * backend/epson.c: disable "feed" command for Perfection 1640 w/ ADF 2004-09-08 Gerhard Jaeger * doc/plustek/Plustek-USB.changes: Update. * doc/descriptions/unsupported.desc: Added Microtek Scanmaker 5700, ArtixScan 4000tf and Medion MD41985. 2004-09-06 Henning Meier-Geinitz * configure: Regenerated. * doc/descriptions/unsupported.desc doc/descriptions-external/genesys.desc: Moved HP 35xx series to unsupported.desc because these scanners use a RTS8801 chip (no Genesys chipset). 2004-09-06 Jochen Eisinger * backend/mustek_pp.c: clarify error message * configure.in: only build the mustek_pp backend, if parallel port support of any kind is present 2004-09-06 Gerhard Jaeger * backend/plustek.c: Bumped build number. * backend/plustek-usbdevs.c: Fixed bug #300913. 2004-09-05 Julien Blache * More auth_callback() fixes, although they're not critical. >>>>>>> 1.2283 2004-09-03 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Lexmark X1130, Dell 1600n. Removed Canon LiDE 35 (now in genesys.desc) and Epson Perfection 2480 (now in snapscan.desc). * doc/descriptions-external/genesys.desc: Added Canon LiDE 35. * backend/gt68xx.c backend/gt68xx.conf backend/gt68xx_devices.c backend/gt68xx_low.h backend/gt68xx_low.h backend/gt68xx_shm_channel.c doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Fixed firmware name for Mustek 1248 UB. Changed status to basic. Removed "unsupported" warning. Added workaround for shared memory compilation problem on Windows. It's now possible to add a new vendor/product id line to gt68xx.conf to test yet unsupported scanners without changing the source code. * frontend/.cvsignore: Added tstbackend. 2004-09-02 Oliver Schwartz * backend/snapscan.c backend/snapscan-scsi.c backend/snapscan-options.c backend/snapscan.h backend/snapscan.conf doc/descriptions/snapscan.desc: Added support for Epson 2480 2004-09-01 Julien Blache * frontend/saned.c: auth_callback(): arrays are passed as pointers, declaring parameters as arrays of fixed size is useless. memset() the correct length, not sizeof(pointer). Caught while testing splint on the SANE sources. 2004-08-30 Gerhard Jaeger * doc/descriptions/unsupported.desc: Added Canon LiDE35. * sanei/sanei_usb.c: Fixed memory leak. 2004-08-29 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Epson 2580 and Nikon LS-50. * doc/descriptions-external/brother.desc: Added more clear comments. 2004-08-28 Stéphane Voltz * backend/umax_pp.c: option parsing fix * backend/umax_pp_low.c: minor 1220P calibration fixes 2004-08-25 Stéphane Voltz * tools/umax_pp.c backend/umax_pp.c backend/umax_pp.conf backend/umax_pp.h backend/umax_pp_low.c backend/umax_pp_low.h backend/umax_pp_mid.c backend/umax_pp_mid.h doc/sane-umax_pp.man: rename 'contrast' to 'offset' and 'highlight' to 'gain'. Translations will need to be updated. Beginning of 610/1220P codepath merge. 2004-08-24 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Epson 2480 and Microtek Filmscan 35. 2004-08-23 Henning Meier-Geinitz * tools/sane-find-scanner.c: Worked around a cygwin libusb compatibility problem (patch from Giuseppe Sacco eppesuig at users.alioth.debian.org). 2004-08-19 Henning Meier-Geinitz * doc/descriptions/gt68xx.desc doc/descriptions/unsupported.desc: Updated. 2004-08-18 Julien Blache * tools/hotplug/libsane.usermap: committed patch from Aurélien Jarno adding USB IDs for Epson Stylus CX6400 (and doing s/EPSON/Epson/ on one entry to maintain consistency). 2004-08-14 Frank Zago * frontend/tstbackend: fixed a couple bugs. 2004-08-14 Henning Meier-Geinitz * po/Makefile.in po/sane-backends.fi.po: Added Finnish translation (from Harri Järvi ). 2004-08-08 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx.conf backend/gt68xx_devices.c doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES tools/hotplug/libsane.usermap: Added Mustek ScanExpress 1248 UB and new version of Mustek BearPaw 1200 CU Plus. * doc/descriptions/unsupported.desc: Added Lexmark and Xerox scanners. 2004-08-05 Gerard Klaver * backend/teco2.c backend/teco2.h: changed to SANE_VALUE_SCAN_MODE_LINEART, _GRAY, and _COLOR, * backend/teco2.c: changed use of %d to %ld (when bytes values are displayed) 2004-08-04 Julien Blache * tools/hotplug/libsane.usermap: added HP ScanJet 5300C. 2004-08-04 Gerard Klaver * AUTHORS: corrrection for teco2 * doc/descriptions/teco2.desc: url and status change * doc/sane-teco2.man: update info * backend/teco2.c: - added for the VM6575 a WHITE_LEVEL_R, _G an _B slider option -changed for the VM656A and VM6575 and VM6586 the calibration part (subtract highest and lowest value and then divide). -default SANE_TECO_CAL_ALGO value is now 1 for the VM3564 and VM6575. - preview value is now 75 dpi for the VM6575 2004-08-04 Oliver Rauch * doc/umax/umax.FAQ: corrected bug about dtc3181e scsi controller 2004-08-03 Henning Meier-Geinitz * aclocal.m4 configure include/sane/config.h.in: Regenerated to include updates from niash and resource manager inclusion. * doc/sane.man doc/sane-niash.man: Minor documentation updates. * doc/descriptions-external/niash.desc: Removed (backend now included). 2004-08-03 Gerhard Jaeger * AUTHORS: Added niash backend maintainer and author. * configure.in: Added niash backend. * backend/dll.conf: Added niash backend. * backend/Makefile.in: Added niash backend files and target. * backend/niash.c backend/niash_core.c backend/niash_core.h backend/niash-xfer.c backend/niash-xfer.h backend/niash-types.h: Niash backend files, initial checkin. * doc/sane-niash.man, doc/descriptions/niash.desc, doc/niash/niash.TODO: Initial checkin. * doc/Makefile.in: Added niash documentation stuff. * doc/sane-man: Added niash manpage. * po/Makefile.in: Added niash source file. * po/sane-backends.de.po: Updated and completed niash backend translation. * po/sane-backends.*.po: Added niash strings. * backend/plustek.c: Small cleanup. 2004-07-31 Julien Blache * frontend/scanimage.c: Added the possibility to cleanly stop a batch by pressing Ctrl+D when using --batch-prompt. 2004-07-28 Gerard Klaver * doc/descriptions/unsupported.desc: Added Logitech PageScan USB and Grandtek Scopecam 2004-07-26 Gerhard Jaeger * sanei/sanei_usb.c sanei/sanei configure configure.in: Added resource manager library support. * backend/plustek.[ch] backend/plustek-usbdevs.c backend/plustek-usbhw.c backend/plustek-usbshading.c backend/plustek.conf: Added speedup parameters and TPA autodetection for UMAX3400/3450, added disableSpeedup option. * doc/plustek/Plustek-USB-TODO.txt doc/plustek/Plustek-USB.changes doc/descriptions/plustek.desc: Updated. 2004-07-21 Stéphane Voltz * backend/umax_pp_low.c backend/umax_pp.c tools/umax_pp.c: fixed a 610P initialization bug that shown up when changing from color to grey mode. Changed version numbers. 2004-07-18 Karl Heinz Kremer * doc/descriptions/epson.desc: Added CX-5400, RX-500 and RX-600; updated version * doc/descriptions/unsupported.desc: Removed RX-500 2004-07-16 Henning Meier-Geinitz * doc/descriptions-external/epkowa.desc: Added Epson Perfection 3170 Photo as it's reported to work with that backend. * doc/descriptions/unsupported.desc: Added some scanners. Removed Epson Perfection 3170 (now in epkowa). 2004-07-16 Gerhard Jaeger * doc/plustek/Makefile.module doc/plustek/BUILD doc/plustek/VERSION0 doc/plustek/VERSION1: Removed, no longer needed. * doc/plustek/Makefile.kernel2x doc/plustek/MakeModule.sh: Getting version information now out of the backends main file. * doc/plustek/Plustek_PARPORT.changes: Updated. * doc/descriptions/plustek_pp.desc: Updated. * doc/sane-plustek.man: Fixed typo. * backend/plustek-pp.[ch], backend/plustek-pp_*.[ch]: Added DevFS support for kernel 2.6, removed floating point operations (Thanx to Rafal Rzepecki), bumped up build number, cleanup work. * doc/plustek/Plustek_USB.changes: Updated. * backend/plustek.c: Bumped up build number * backend/plustek-usbshading.c: Improved autowarmup, cleanup work. * backend/plustek-usb.h backend/plustek-usbdevs.c backend/plustek-usbhw.c backend/plustek-usnscan.c: Improved fastforward stuff, cleanup work. * sanei/sanei_lm983x.c: Cleanup work. 2004-07-15 Rene Rebe * include/sane/sane.h: added extern "C" for compilation with a C++ compiler 2004-07-12 Henning Meier-Geinitz * doc/sane-coolscan2.man doc/sane-microtek2.man: Fixed manual page problems. 2004-07-10 Gerard Klaver * tools/check-usb-chip.c: Added check for the GT-8911. 2004-07-10 Henning Meier-Geinitz * tools/check-usb-chip.c: Also check the number of interfaces for the GT-6816 to avoid conflicts with other GT chips. 2004-07-09 Henning Meier-Geinitz * doc/descriptions-external/brother.desc: New file. Added the scanners listed on the brother backend page. Used "untested" status as we don't have confirmation that the backend actually works. * tools/hotplug/libsane.usermap: Added Mustek ScanExpress 1248UB. * tools/sane-desc.c: XML mode updates (patch from Jose Gato ). * doc/descriptions/ma1509.desc: Removed dead link. 2004-07-05 Gerhard Jaeger * doc/descriptions/plustek.desc: Changed status of CanoScan D660U. * doc/plustek/Plustek-USB*: Update. * backend/plustek.c: Bumped up build number. * backend/plustek-usb.[ch] backend/plustek-usbdevs.c backend/plustek-usbhw.c backend/plustek-usbimg.c backend/plustek-usbmap.c backend/plustek-usbscan.c: Added support for binary scanning for the CanoScan D660U, cleanup work. 2004-07-03 Peter Fales * acinclude.m4, aclocal.m4, configure, tools/sane-config.in: When using pkg-config to get library flags for gphoto2, any extra flags (such as -L) must be passed to sane-config.in so that it can find the libraries in a non-standard location. (Bug #300686) 2004-07-02 Gerhard Jaeger * po/*.po: Updated according to changes in the plustek backend. * backend/plustek.c backend/plustek-usb*: Major update, see doc/plustek/Plustek-USB.changes. * doc/sane-plustek.man: Update. * doc/plustek/Plustek-USB.changes: Update. * doc/descriptions/plustek.desc: Removed unsupported devices, updated some states. * doc/descriptions/plustek_pp.desc: URL update. * doc/descriptions/u12.desc: URL update, changed state of backend. * doc/sane-plustek_pp.man: URL update. * doc/sane-u12.man: URL update. 2004-06-30 Frank Zago * backend/leo.c doc/descriptions/leo.desc: added support for Genius FS-1130 Colorpage Scanner. 2004-06-30 Gerhard Jaeger * doc/plustek/Makefile.kernel26: fixed floating point issues for SuSE kernels. 2004-06-28 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Microtek and Xerox scanners. * tools/sane-desc.c: Added xml output (patch from jose ). Other minor fixes. 2004-06-28 m. allan noah * backend/fujitsu.c: use model code instead of string compare submitted by: stan at saticed.me.uk 2004-06-21 Gerhard Jaeger * doc/descriptions/unsupported.desc: removed Compac S4 100 (supported by the Plustek backend), added various Plustek devices, changed OpticPro m12 to OpticSlim M12 2004-06-22 Henning Meier-Geinitz * backend/microtek2.c: Fixed some (but not all) MIN related compilation warnings (bug #300823). * tools/sane-find-scanner.c: Look for NetBSD uscanner devices (bug #300815). 2004-06-22 Rene Rebe * backend/avision.c, backend/avision.h, doc/sane-avision.man: Fixed compilation warning (Bug #300399) and added a force-a3 option, needed for A3 scanner returning bogus scan area definitions 2004-06-21 Gerhard Jaeger * po/*.po: Updated according to upcoming changes in the plustek backend. 2004-06-20 Mattias Ellert * aclocal.m4, configure, sane/config.h.in, backend/Makefile.in, backend/agfafocus.c, backend/artec_eplus48u.c, backend/avision.c, backend/coolscan.c, backend/fujitsu.c, backend/pie.c, backend/plustek.c, backend/plustek_pp.c, backend/sp15c.c, backend/tamarack.c, backend/u12.c * Fixing bug #300602 for the following backends: agfafocus, artec_eplus48u, avision, coolscan, fujitsu, pie, plustek, plustek_pp, sp15c, tamarack and u12 * Migrating the avision backend to sanei_threads (bug #300631) 2004-06-19 Mattias Ellert * backend/gt68xx.c po/sane-backends.bg.po po/sane-backends.cs.po po/sane-backends.da.po po/sane-backends.de.po po/sane-backends.es.po po/sane-backends.fr.po po/sane-backends.it.po po/sane-backends.nl.po po/sane-backends.no.po po/sane-backends.pt.po po/sane-backends.ru.po po/sane-backends.sv.po * Option names should not be tagged for localization * Fixing some typos * New localizable strings from the gt68xx backend (pofiles regenerated) * Swedish translation updated 2004-06-19 Mattias Ellert * sanei/sanei_scsi.c, tools/sane-find-scanner.c * SCSI scanners can now be selected by LUN on MacOS X * sane-find-scanner now finds SCSI scanners on MacOS X * making some sanei_scsi internal functions static 2004-05-30 Peter Fales * acinclude.m4, aclocal.m4, configure.in, configure In addition to using pkg-config to find the gphoto2 library flags, we should check to see whether it's actually possible to link a program using those flags. 2004-06-18 Henning Meier-Geinitz * doc/gt68xx/gt68xx.TODO: New file. Lots of bugs and missing features for the gt68xx backend. * backend/gt68xx.c backend/gt68xx_devices.c backend/gt68xx_generic.c backend/gt68xx_gt6801.c backend/gt68xx_gt6801.h backend/gt68xx_gt6816.c backend/gt68xx_high.c backend/gt68xx_high.h backend/gt68xx_low.c backend/gt68xx_low.h backend/gt68xx_mid.c doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Updated to backend version 61. Lots of fixes for coarse calibration, scanning speed and several scanners. For details, see gt68xx.CHANGES. 2004-06-17 Henning Meier-Geinitz * doc/.cvsignore: Added *.8. 2004-06-17 Stéphane Voltz * backend/umax_pp_low.c doc/descriptions/umax_pp.desc doc/sane-umax_pp.man: fixed overflows in 610P shading calibration coefficients, minor man update, 610P status change from minimal to good 2004-06-16 Oliver Schwartz * backend/snapscan.h backend/snapscan.c backend/snapscan-usb.c: Don't enforce even number of URB packages on 1212u_2 since it causes problems. See bug #300753. 2004-06-15 Henning Meier-Geinitz * backend/snapscan.c: Only use __attribute__ if gcc is used for compilation. Some other compilers don't know __attribute__ and therefore can't compile sane-backends without this fix. See bug #300803. 2004-06-15 Stéphane Voltz * backend/umax_pp_low.h backend/umax_pp_low.c backend/umax_pp_mid.c: 610P gray level shading calibration fix. Added timer to let 610P ASIC to settle down after probing. 2004-06-13 Julien Blache * doc/Makefile.in: saned is in /usr/sbin, its manpage should go to section 8. Fixed everything referring to saned(1) to refer to saned(8). 2004-06-13 Mattias Ellert * doc/descriptions/unsupported.desc: Added NEC Petiscan as unsupported 2004-06-13 Karl Heinz Kremer * backend/sane_usb.c: Added Perfection 1650 back in that was removed by mistake and finally removed Perfection 1250. 2004-06-12 Stéphane Voltz * backend/umax_pp_low.c tools/umax_pp.c: final fixes for 610P color scanning, parallel port autodetection for the umax_pp tool. 2004-06-10 Henning Meier-Geinitz * doc/descriptions/mustek_usb.desc: Fixed version number. 2004-06-08 Gerhard Jaeger * backend/plustek-pp_misc.c: fixed multiple parport problem for kernel 2.6.x. * backend/plustek_pp.c: bumped up version number. * doc/plustek/BUID: bumped up build number. 2004-06-08 Henning Meier-Geinitz * tools/RenSaneDlls.cmd: Fixed newlines. * README.netbsd: Mention SCSI buffer size problems and uk/ss files issue. * backend/mustek.c doc/descriptions/mustek.desc doc/mustek/mustek.CHANGES: Reduced scan area of Mustek Paragon 1200 A3 Pro. Removed warning message. Increased support level to complete. * tools/hotplug/libsane.usermap: Added another variant of a Plustek OpticPro 1248U. * doc/descriptions/unsupported.desc: Added Syscan TravelScan FS-531. 2004-06-06 Karl Heinz Kremer * backend/epson_usb.c: remove product IDs for Perfection 1250 and 1260 2004-06-06 Oliver Schwartz * backend/snapscan-usb.c: Don't use shared memory on OS/2 and when using pthreads. 2004-06-06 Henning Meier-Geinitz * tools/hotplug/libsane.usermap: Added Mustek BearPaw 2448 Plus and Plustek OpticPro U16B. 2004-06-05 Henning Meier-Geinitz * tools/README tools/RenSaneDlls.cmd: Added REXX script to convert backend-DLL-filenames according to 8.3 naming convention necessary for DLLs on OS/2 (from Franz Bakan). 2004-06-02 Henning Meier-Geinitz * backend/net.c doc/sane-net.man doc/saned.man frontend/saned.c: Changed service name from "sane" to "sane-port". This is the IANA registered service name for port 6566 (bug #300758). 2004-06-02 Oliver Schirrmeister * fujitsu.c bugfix: It is possible to read duplex color now. 2004-05-31 Henning Meier-Geinitz * AUTHORS: Mattias Ellert has CVS write access now. 2004-05-30 Peter Fales * acinclude.m4, aclocal.m4, configure.in, configure Use pkg-config rather than gphoto2-config to get gphoto build parameters (bug #300686) 2004-05-30 Henning Meier-Geinitz * backend/v4l.c: Used SANE_VALUE_SCAN_MODE_* constants. * doc/descriptions-external/viceo.desc: Mention special kernel patch. 2004-05-29 Henning Meier-Geinitz * backend/sp15c.c: Fixed the fix of the sanei_thread fix (from Mattias Ellert). 2004-05-28 Henning Meier-Geinitz * README.hp-ux: Mention trouble with higher optimization levels (from Ulrich Deiters ). * doc/descriptions/unsupported.desc: Added Dell A920, Microtek 1850S and Plustek OpticPro m12. Removed HP,Scanjet 2300 (already in genesys.desc). * backend/sp15c.c: Fixed sanei_thread fix (bug #300634, by Mattias Ellert). 2004-05-27 Oliver Schwartz * backend/snapscan.c backend/snapscan-usb.c: Use shared memory for urb counters 2004-05-24 Henning Meier-Geinitz * backend/dll.c doc/descriptions/dll.desc: Work around 8 char limit for dynamic loading on OS/2 (patch from Franz Bakan ). 2004-05-24 m. allan noah * backend/fujitsu.[ch]: apply Mattias Ellert's thread patch split packet counter into r and w 2004-05-23 Henning Meier-Geinitz * tools/check-usb-scanner.c: Detect GL660+GL646 on USB2 also. Fixed Mustek MA1017 scanner freeze problem. * backend/Makefile.in backend/agfafocus.c backend/agfafocus.h backend/microtek2.c backend/microtek2.h backend/sp15c.c backend/sp15c.h backend/tamarack.c backend/tamarack.h: Use sanei_thread instead of fork() in the unmaintained backends. Patches from Mattias Ellert (bugs: 300635, 300634, 300633, 300629). 2004-05-21 Gerhard Jaeger * sanei/sanei_pp.c: fixed compilation problem on HP-UX. 2004-05-21 Ulrich Deiters * backend/canon.c, canon-sane.c, canon.h: removed an option (OPT_PAGE) that conflicted with some frontends 2004-05-18 Ulrich Deiters * backend/canon.c, canon-sane.c: memory leak and bug fixed in slide scanner code 2004-05-18 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added more Canon cartridge scanners. Updated Visioneer Paperport 3100b. 2004-05-18 Michael Herder * backend/Makefile.in: adjusted for use with sanei_thread and artec_eplus48u backend (thanks Mattias Ellert) 2004-05-16 Oliver Rauch * sanei/sanei_config.c: added DIR_SEP=";" and PATH_SEP="\\" for windows (when windows.h) is available * backend/dll.c: added DIR_SEP definitions from sanei_config.c and replaced relevant ":" by DIR_SEP 2004-05-15 Gerhard Jaeger * doc/plustek/BUID: bumped up build number. * doc/plustek/Plustek-PARPORT.changes: update * backend/plustek-pp_misc.c: fixed kernel 2.6 issue. fixed also Bug #300698. 2004-05-15 Michael Herder * backend/artec_eplus48u.c backend/artec_eplus48u.h: applied patch from Mattias Ellert (thanks), which adds support for sanei_thread 2004-05-15 Stéphane Voltz * backend/umax_pp_low.c backend/umax_pp.c tools/umax_pp.c: fixed origin shift bug for 610P. Added on guard against configuration that can put several 'port' option in conf file. 2004-05-13 Stéphane Voltz * backend/umax_pp_low.c backend/umax_pp.c tools/umax_pp.c: fixed data lines reordering for 610P 2004-05-12 Henning Meier-Geinitz * sane-backends.lsm: Updated FTP server link to ftp.sane-project.org. 2004-05-11 Henning Meier-Geinitz * Makefile.in: Remove autoconf temp files and some japi stuff in distclean target. 2004-05-10 Stéphane Voltz * backend/umax_pp_low.c : fixed 300x600 dpi scans, direct hardware access and timing issues for 610P 2004-05-06 Jochen Eisinger * tools/hotplug/libusbscanner: latest hotplug doesn't set DEVICE on 2.6.x kernels. Added a workaround 2004-05-05 Matthew Duggan * include/sane/saneopts.h: Added SANE_VALUE_SCAN_MODE_* strings. * backend/canon_pp.c: Used them. 2004-05-01 Jochen Eisinger * tools/hotplug/libsane.usbmap: removed empty lines, latest hotplug cannot cope with them 2004-03-15 Stéphane Voltz * backend/umax_pp_low.c backend/umax_pp_low.h backend/umax_pp_mid.c backend/umax_pp_mid.h backend/umax_pp.c backend/umax_pp.h tools/umax_pp.c: added support for 610P 2004-05-01 Henning Meier-Geinitz * configure configure.in: Warnings enabled again. Added -cvs to version. * Makefile.in: Added Changelog-1.0.14 to DISTFILES. Older entries can be found in ChangeLog-1.0.14. backends-1.3.0/ChangeLogs/ChangeLog-1.0.16000066400000000000000000001062021456256263500176110ustar00rootroot00000000000000****** Release of sane-backends 1.0.16. End of code freeze ****** 2005-08-07 Henning Meier-Geinitz * configure configure.in: New version 1.0.16. 2005-08-05 St�hane Voltz * backend/genesys.c: Fixed endianness issue in slope table generation. Tested ok on x86 architecture. 2005-08-02 Henning Meier-Geinitz * po/sane-backends.da.po: Updated Danish translation (from Mogens Jaeger * po/sane-backends.sv.po: Update Swedish translation 2005-08-01 Eddy De Greef * doc/sane-mustek_pp.man: updated URL for additional CIS driver info. 2005-07-31 Henning Meier-Geinitz * NEWS: Updated. ---- CODE FREEZE FOR SANE 1.0.16 --- 2005-07-31 Henning Meier-Geinitz * configure configure.in: Disabled compilation warnings. 2005-07-30 Karl Heinz Kremer * backend/epson_usb.c: Applied Olaf Meuwissen's patch to add new device IDs. 2005-07-30 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Xerox Workcentre 470cx. Added link for Canon DR-2080C. * backend/Makefile.in: libsane and documentation is installed correctly again. * acinclude.m4 configure configure.in: Avoid error messages in locking test. Make sure that io/cam/cam.h header file will be detected correctly. 2005-07-29 Gerard Klaver * doc/sane-teco2.man: text update doc/descriptions/teco2.desc: update status VM3564 doc/teco/teco2.txt: text added backend/teco2.c: removed teco_request_sens command for VM3564 (bug) backend/teco2.h: text update 2005-07-29 Julien Blache * tools/hotplug/libsane.usermap: Added various Epson IDs from Olaf Meeuwissen; reodered some misplaced Epson entries. 2005-07-29 Julien Blache * tools/hotplug/libsane.usermap: Added Visioneer OneTouch 7300 (0x0444,0x0211), from Derek J Frye. 2005-07-25 Gerhard Jaeger * doc/descriptions-external/epkowa.desc: Updated according to the info provided by Olaf Meeuwissen 2005-07-24 Henning Meier-Geinitz * doc/descriptions-external/brother2.desc: Brother DCP 7025 is reported to work (bug #301960). * doc/descriptions-external/hp_rts88xx.desc: Moved unsupported devices to unsupported.desc. * doc/descriptions-external/hp3770.desc doc/descriptions-external/hp8200.desc: Added external hp3770 and hp8200 backends. * doc/descriptions/unsupported.desc: Added unsupported scanners from hp_rts88xx.desc. Removed HP ScanJet 3770 (now in hp3770 backend). Added Lexmark X75 PrinTrio. 2005-07-22 Giuseppe Sacco * italian translation update 2005-07-22 Julien Blache * tools/hotplug/libsane.usermap: Added HP ScanJet 7400C (0x03f0, 0x0801), from Thomas Nadolny. 2005-07-21 Gerhard Jaeger * doc/plustek/Plustek-USB.changes: Update. * backend/plustek.c: Bumped build number. * backend/plustek-usbcal.c: Fixed lampsettings for CIS devices. 2005-07-15 Oliver Schwartz * backend/snapscan-options.c: Change TPO range for Epson 2480/2580 to 55x80mm^2 which is the working range for the 2580. Re-enabled 2400 DPI for 2480/2580. 2005-07-15 Oliver Schwartz * backend/snapscan-mutex.c backend/snapscan-usb.c: Changes to support SANE on ZETA. * backend/snapscan-scsi.c: Fix compiler warnings. ---- FEATURE FREEZE FOR SANE 1.0.16 --- 2005-07-17 Henning Meier-Geinitz * Makefile.in backend/Makefile.in tools/Makefile.in: Make sure that all the necessary files will be in the distribution .tar.gz file. * sanei/sanei_scsi.c: Removed "const" to make gcc on OS/2 happy. * NEWS: Updated. 2005-07-15 Henning Meier-Geinitz * backend/sp15c.c: Better 4->8 bit depth expansion algorithm (from Mattias Ellert ). * sanei/sanei_usb.c: Fixed comment (we still use 30 seconds USB timeout by default). * README.zeta configure configure.in Makefile.in backend/Makefile.in backend/artec_eplus48u.h backend/dll.c backend/plustek-pp_scan.h doc/Makefile.in frontend/saned.c include/sane/config.h.in po/Makefile.in sanei/sanei_config.c sanei/sanei_config2.c sanei/sanei_init_debug.c sanei/sanei_pio.c sanei/sanei_thread.c sanei/sanei_usb.c: Changes to support SANE on ZETA. Not all patches have been applied yet, but it's a start. The backends are not installed as shared libs but as add-ons, which are shared libs but in a specific folder in [/system|~]/config/add-ons/ named SANE/. The install target has been changed to account for this, Some files don't exist in BeOS/ZETA (ipc.h, ...). Backend function names get a prefix, I suppose to avoid namespace clashes. Though we do have a libdl to implement dl_open, the native way is preferred. Added a --with-docdir= configure arg. BeOS has a broken get[name|addr]info() from bind. This is fixed in ZETA R1. libtool needs -no-undefined. Various VPATH fixes. Check for , but not used yet. No S_IFSOCK (sockets are fds to /dev/net/api). Stub pio code, untested. Patch from Fran�is Revol . 2005-07-15 Oliver Schwartz * doc/descriptions/snapscan.desc: updated comments for Epson 1670, 2480 and 2580 2005-07-15 Rene Rebe * backend/avision.h, backend/avision.c, doc/sane-avision.man, doc/descriptions/avision.desc: updated the Avision backend with the latest off-site maintained version: fixed segmentation faults for some modes, many many new devices, two different duplex modes are supported, vast speedup of multi page batch scans, buttons as well a 7-segment LED readout and some quirks for old scanners not filling all fields 2005-07-15 Rene Rebe * include/sane/sanei_usb.h sanei/sanei_usb.c: Added support to set the USB timeout. 2005-07-10 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx_devices.c backend/gt68xx_generic.c backend/gt68xx_high.c doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES doc/gt68xx/gt68xx.TODO: Changed resolution from 75 to 100 dpi and changed 16 bit/color to 12 based on a user report for the Visioneer OneTouch 7300. Updated .desc. Moved check for stable lamp to its own function. Mustek ScanExpress A3 USB: Use CIS calibration. Use lamp warmup. Fixed CCD on GT6801 scanning (color scanning was broken due to a change in motormode_2. 2005-07-09 Henning Meier-Geinitz * po/Makefile.in po/sane-backends.bg.po po/sane-backends.cs.po po/sane-backends.da.po po/sane-backends.de.po po/sane-backends.es.po po/sane-backends.fi.po po/sane-backends.fr.po po/sane-backends.it.po po/sane-backends.nl.po po/sane-backends.no.po po/sane-backends.pl.po po/sane-backends.pt.po po/sane-backends.ru.po po/sane-backends.sv.po: Updated Danish translation (from Mogens Jaeger ). Made sure that no errors occur because xgettext/xmsgfmt think that "%" is a C format string. Regenerated all po files. * configure configure.in include/sane/config.h.in: Make sure that getopt is not built on OS/2 (from Franz Bakan ). 2005-07-07 Frank Zago * AUTHORS backend/leo.c backend/leo.h backend/matsushita.c backend/matsushita.h backend/sceptre.c backend/sceptre.h backend/teco1.c backend/teco1.h backend/teco2.c backend/teco2.h backend/teco3.c backend/teco3.h frontend/tstbackend.c: new email address. 2005-07-07 Gerhard Jaeger * doc/plustek/Plustek-USB.changes: Update. * backend/plustek.c: Fixed scanarea calculation. * backend/plustek-usbshading.c: Fixed fine-white calibration. 2005-07-05 Gerhard Jaeger * doc/descriptions-external/epkowa.desc: Updated according to the info provided by Olaf Meeuwissen 2005-07-04 Gerhard Jaeger * doc/plustek/Plustek-USB.changes: Update. * backend/plustek.[ch] backend/plustek-usbscan.c backend/plustek-usbshading.c backend/plustek-usbdevs.c backend/plustek-usbdevs.c backend/plustek-usbimg.c backend/plustek-usbmap.c backend/plustek-usbhw.c backend/plustek-usbcal.c backend/plustek-usb.[ch]: Added button support, did some cleanup, added IPC between reader- and parent-process. 2005-07-04 ULrich Deiters * inserted a definition for SSIZE_MAX in backend/canon.h 2005-07-03 Henning Meier-Geinitz * backend/sm3600.c: Don't check the result of usb_find_busses(). Based on patch from Julien BLACHE . * doc/descriptions/genesys.desc: Used "Medion/Lifetec/Tevion/Cytron" as manufacturer as in the other backends. Used "ScanJet 2300C" as model name as in other backends. * doc/descriptions/unsupported.desc: Added links to scanners with GL646 and GL841 chipsets. Moved all unsupported scanners from doc/descriptions-external/genesys.desc to unsupported.desc. Minor fixes for other scanners. * doc/descriptions-external/genesys.desc: Removed. Scanners have been moved to doc/descriptions/genesys.desc and doc/descriptions/unsupported.desc. * doc/sane-genesys.man: Typo/formatting fixes. * NEWS: Updated for sane-backends 1.0.16. 2005-07-01 Ulrich Deiters * restored the actual version of the Canon-SCSI backend (backend/canon.c, canon-sane.c canon-scsi.c canon.h) 2005-06-30 St�hane Voltz * configure.in AUTHORS backend/dll.conf doc/sane.man doc/Makefile.in po/Makefile.in : Fixed forgotten modifications when adding the genesys backend 2005-06-30 Gerhard Jaeger * tools/RenSaneDlls.cmd backend/dll.c: Fixed OS/2 restriction for dlopening DLLs on OS/2, as it only works for 7.3 filenames for some reason (patches by Franz Bakan . 2005-06-27 St�hane Voltz * configure doc/sane-genesys.man doc/sane-genesys.man doc/Makefile.in doc/descriptions/genesys.desc doc/descriptions/unsupported.desc backend/genesys_low.h backend/genesys_devices.c backend/genesys_gl841.c backend/genesys_gl646.c backend/genesys.h backend/genesys.conf backend/genesys.c backend/Makefile.in 2005-06-27 Gerhard Jaeger * backend/plustek.c: Fixed "double free" problem in sane_exit. * doc/plustek/Plustek-USB.changes: Update. 2005-06-26 Mattias Ellert * po/sane-backends.sv.po: Update Swedish translation * tools/Makefile.in: link sane-desc to $(LIBLIB) 2005-06-12 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Removed Samsung SCX-4216F (supported by samsung backend). Dell 1600n has also network connection. * doc/descriptions-external/brother.desc: Removed misleading comment. * doc/descriptions-external/samsung.desc: Added several models, updated others. * doc/descriptions.txt: Added "Ethernet" to list of possible interfaces. 2005-06-12 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Canon CanoScan LiDE 500F. 2005-06-04 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Genius ColorPage-SF600. * doc/descriptions-external/brother2.desc: New. Listed scanners supported by the new external brother2 backend. * doc/descriptions-external/brother.desc: Fixed comments. 2005-05-30 Karl Heinz Kremer * doc/descriptions/epson.desc: Added CX-4600 2005-05-29 Henning Meier-Geinitz * doc/descriptions-external/samsung.desc: Status of Samsung SCX-4100 is "good" asit's reported to work. * doc/sane-usb.man doc/sane.man: Updated, mostly concerning kernel scanner driver (deprecated). Minor fixes. * backend/gt68xx.c backend/gt68xx_devices.c backend/gt68xx_high.c backend/gt68xx_low.h doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES doc/gt68xx/gt68xx.TODO: Start exposure with the settings from the scanner entry. CIS Calibration and exposure uses a broader spectrum now. Expsoure limit is increased to 50 loops. Genius Vivid 4x is reported to work so the UNTESTED label is removed now. Visioneer 7300 is reported to work so the UNTESTED label is removed now. Genius ColorPage Vivid 1200XE: Removed UNTESTED label. Fixed resolutions. Adjusted margins. Fixed color order. Fixed linedistance. Adjusted AFE. Mustek ScanExpress 1248 UB: Fixed margins. Adjusted AFE. Adjusted exposure. Adjusted gamma. Changed status to "complete". Mustek BearPaw 2400 CU Plus: Adsusted exposure and AFE parameters. Hopefully stripes and similar trouble are gone now. Mustek BearPaw 1200 CU: Increased exposure to avoid vertical lines. Updated manual page. 2005-05-28 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Avision DS310F. Added link for the UMAX Astra 4000. Fixed missing link for Genius ColorPage-Slim 1200 USB2. 2005-05-26 Henning Meier-Geinitz * doc/net.tex doc/sane.tex: Mention RPC codes explicitly and explain what request and reply means. based on patch from Johannes Berg . 2005-05-25 Jochen Eisinger * doc/descriptions/mustek_pp.desc: added scanner 2005-05-23 Julien Blache * doc/sane-find-scanner.man, doc/gamma4scanimage.man: spelling fixes from A Costa. 2005-05-22 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Konica Minolta Dimage 5400 2. Fixed bus type for Mustek 800 II EP. * doc/descriptions-external/genesys.desc: Changed status of Medion MD 6471 to "basic". 2005-05-22 Oliver Schwartz * backend/snapscan-options.c: Disabled 2400 DPI for Epson 2480 due to user report of broken scanner 2005-05-20 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx_devices.c backend/gt68xx_generic.c backend/gt68xx_generic.h backend/gt68xx_gt6801.c backend/gt68xx_gt6801.h backend/gt68xx_gt6816.c backend/gt68xx_gt6816.h backend/gt68xx_high.c backend/gt68xx_high.h backend/gt68xx_low.c backend/gt68xx_low.h backend/gt68xx_mid.c doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES doc/gt68xx/gt68xx.TODO: Mustek BearPaw 1200 CU Plus model 0x21b works now. Fixed firmware and offset settings. Calibration is not completely ok yet, however. Code cleanup for setup_scan (only one function for all chipsets). Simplified line mode/pixel mode setting Updated TODO. Mustek BearPaw 2448 TA Plus is now only "basic". Too much trouble for "good". Removed option "fast preview". By default for preview mode 8 bits/pixel are used. Fixed indentation. Full scan really scans from y=0 on gt6816 scanners now. * po/sane-backends.bg.po po/sane-backends.cs.po po/sane-backends.da.po po/sane-backends.de.po po/sane-backends.es.po po/sane-backends.fi.po po/sane-backends.fr.po po/sane-backends.it.po po/sane-backends.nl.po po/sane-backends.no.po po/sane-backends.pl.po po/sane-backends.pt.po po/sane-backends.ru.po po/sane-backends.sv.po: Regenerated translation files. Updated German translation. 2005-05-16 Henning Meier-Geinitz * doc/backend-writing.txt: Minor updates and line-wrap fixes. * doc/descriptions/unsupported.desc: Added Kodak i30 and Minolta Dual Scan IV. Updated link to Konica Minolta website. 2005-05-15 frank * tools/Makefile.in: remove superfluous libraries for sane-desc * doc/descriptions/matsushita.desc: changed some scanner status from good to minimal * doc/descriptions/unsupported.desc: added a few Panasonic scanners. 2005-05-13 Julien Blache * tools/hotplug/libsane.usermap: Added Epson Stylus RX620 (04b8/0811). From Mike Talbot. 2005-05-11 Gerhard Jaeger * backend/plustek-pp_misc.c: Applied patch (see bug #301605). * doc/plustek/Plustek-PARPORT.changes: Update. * backend/plustek-pp.c: Bumped build number. 2005-05-10 Gerhard Jaeger * sanei/sanei_access.c: Added PATH_MAX, in case it's not defined by any header (i.e. OS/2). 2005-05-07 Mattias Ellert * README.darwin: updated the section about libusb, since all known bugs have now been fixed in the libusb CVS. 2005-05-05 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx_devices.c doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES doc/gt68xx/gt68xx.TODO: Plustek OpticSlim 2400: Fixed list of resolutions. That fixed the preview. Removed "untested" warning. Adjusted scan area. Updated gt68xx.TODO. Find firmwares independent of capitalization (bug #301580). 2005-05-07 Julien Blache * tools/hotplug/libsane.usermap: Added Microtek ScanMaker 3700 (05da/40cb). From Ian Beckwith. 2005-05-06 Gerard Klaver * backend/qcam.c -indent -gnu, bugreport 300128 free (devlist) added, DEBUG output added and some small fixes. 2005-05-05 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Dell 922. * backend/mustek.c backend/mustek.h doc/descriptions/mustek.desc doc/mustek/mustek.CHANGES: Added options to set the time after which the lamp of the A3 Pro is shut off and to shut it off immediately. 2005-05-02 Peter Fales * backend/gphoto2.c: Fix core dump when no port is specified in the gphoto2 config file. (bug #301552) 2005-04-29 Henning Meier-Geinitz * doc/descriptions/sp15c.desc: Added ScanPartner 600C (bug #301528). * doc/descriptions/unsupported.desc: Removed several Brother scanner which are supported by the brother backend now. * doc/descriptions-external/brother.desc: Fixed link. 2005-04-28 Julien Blache * backend/v4l.c: duplicate capability.name in attach() so that each device is listed with its actual name, not with the name of the last discovered device. Reported by Andreas Hartmann. 2005-04-28 Mattias Ellert * frontend/scanimage.c: Added missing braces 2005-04-27 Gerhard Jaeger * doc/descriptions/plustek.desc: Added UMAX Astranet ia101. 2005-04-26 Julien Blache * tools/hotplug/libsane.usermap: Added Epson Stylus CX3650 (04b8/080e), from Daniel Sobe. 2005-04-25 Gerhard Jaeger * include/sane/sanei_pp.h, sanei/sanei_pp.c: Added outb_eppdata functionality (thanks to Anderson Lizardo). * backend/plustek-pp_procs.h, backend/plustek-pp_io.c, backend/plustek-pp_misc.c: Fixed compilation problem for kernel module with gcc-3.4 compiler. * doc/plustek/Plustek-PARPORT.changes: Update. * backend/plustek-pp.c: Bumped build number. 2005-04-24 Henning Meier-Geinitz * tools/hotplug/libsane.usermap: Fixed Minolta Scan Dual III entry (bug #301512). * doc/descriptions/mustek.desc: Added Mustek TwainScan II SP (bug #301217). * doc/descriptions-external/viceo.desc: Added Primax Colorado 2200 USB. * doc/descriptions/unsupported.desc: Added Canon PIXMA MP110. * AUTHORS: Marked Kazuya Fukuda as active. 2005-04-23 Julien Blache * LICENSE: Add an FAQ section. * tools/hotplug/libsane.usermap: Added Genius ColorPage Vivid3xe (0458/2017), from Ramiro Aceves through Debian BTS. 2005-04-18 Gerhard Jaeger * backend/plustek-usbhw.c, backend/plustek-usbscan.c: Fixed problem, that has been reported in conjunction with backtracking and sensor-speedup option. * doc/plustek/Plustek-USB.changes: Update. * backend/plustek.c: Bumped build number. 2005-04-16 Henning Meier-Geinitz * doc/descriptions/unsupported.desc doc/descriptions-external/genesys.desc: Added and updated several scanners. 2005-04-15 Gerard Klaver * sanei/sanei_usb.c More debug info for control and isochronous mode Detection added for endpoints for configurations, interface and altsetting numbers > 0 Routine added for changing configuration, interface or altsetting number sanei_usb_set_configuration sanei_usb_claim_interface sanei_usb_release_interface sanei_usb_set_altinterface * include/sane/sanei_usb.h headers and text added for sanei_usb_set_configuration sanei_usb_claim_interface sanei_usb_release_interface sanei_usb_set_altinterface * frontend/scanimage.c added -B option buf size split up printf command to remove warning ISO C89 patch for bugreport 300160, ranges for width and height are wrong * doc/scanimage.man added text -B option buf size 2005-04-13 Mattias Ellert * backend/hp-option.c: Add missing SANE_I18N * po/sane-backends.*.po: Regenerate .po files accordingly * po/sane-backends.sv.po: Update Swedish translations 2005-04-12 Mattias Ellert * Various man page fixes: sane-agfafocus.man, sane-apple.man, sane-artec.man, sane-artec_eplus48u.man, sane-avision.man, sane-bh.man, sane-canon_pp.man, sane-coolscan.man, sane-coolscan2.man, sane-dll.man, sane-epson.man, sane-fujitsu.man, sane-hp.man, sane-leo.man, sane-matsushita.man, sane-microtek2.man, sane-mustek.man, sane-mustek_pp.man, sane-mustek_usb.man, sane-nec.man, sane-niash.man, sane-pie.man, sane-plustek.man, sane-plustek_pp.man, sane-sceptre.man, sane-scsi.man, sane-sharp.man, sane-sm3600.man, sane-sm3840.man, sane-sp15c.man, sane-teco1.man, sane-teco2.man, sane-teco3.man, sane-test.man, sane-u12.man, sane-umax.man, sane-umax1220u.man, sane-umax_pp.man, sane.man, saned.man 2005-04-11 Gerhard Jaeger * acinclude.m4, configure.in, configure: the locking feature will now be disabled, when the selected group could not be set. The configuration script should not fail any longer. 2005-04-10 Julien Blache * backend/hp5400_xfer.h, backend/hp5400_sanei.c, backend/hp5400.c: make non-SANE-standard functions static. 2005-04-10 Jochen Eisinger * backend/mustek_pp.c, backend/mustek_pp.conf, sanei/sanei_pa4s2.c, include/sane/sanei_pa4s2.h, doc/sane-mustek_pp.man: added global option "no_epp" to work around a known bug in the linux parport code. 2005-04-02 Julien Blache * doc/sane.man: typo fix from A Costa. 2005-04-01 Gerhard Jaeger * README: Added some info about the --enable-locking switch. * doc/descriptions/unsupported.desc doc/descriptions-external/genesys.desc: Moved some Plustek devices over to the genesys descriptions. * tools/hotplug/libsane.usermap: Added some Plustek devices. * mkinstalldirs: Added -o and -g options for changing the owner and group of an installed directory. * acinclude.m4, configure.in, configure: Added --enable-locking and --with-group to enable device locking via sanei_access-lib and to specify the group of the locking directory. * include/sane/config.h.in: ENABLE_LOCKING has been added by automake/autoconf. * backend/Makefile.in: Added installation of $(localstatesanedir)/lock/sane, were the lockfile should go to. Also added linking of sanei_access to the plustek backend. * include/Makefile.in: Added sane/sanei_access.h. * sanei/Makefile.in: Added sanei_access.c. * include/sane/sanei_access.h sanei/sanei_access.c: Initial checkin. 2005-03-31 Julien Blache * doc/scanimage.man: fix typos, patch from A Costa. 2005-03-20 Earle F. Philhower III * backend/sm3840_scan.c: Big-endian graymode fix 2005-03-17 Gerard Klaver * doc/descriptions-external/samsung.desc added 2005-03-16 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Removed Microtek ScanMaker 4800 (supported by sm3840 now). 2005-03-16 Earle F. Philhower III * backend/sm3840.c backend/sm3840_scan.c backend/sm3840.conf backend/Makefile.in doc/descriptions/sm3840.desc doc/sane-sm3840.man: Added ScanMaker 4800 USB ID and documentation, fixed PPC endianness problems, fixed Makefile dependencies for sm3840 driver 2005-03-13 Henning Meier-Geinitz * doc/descriptions/unsupported.desc doc/descriptions-external/genesys.desc: Added and updated various scanners. 2005-03-04 Gerard Klaver * doc/sane-teco2.man: text update backends/teco2: use of __unused__ 2005-03-03 Julien Blache * tools/hotplug-ng/libsane.hotplug: add missing quote in the grep line. 2005-03-02 Julien Blache * tools/hotplug-ng/libsane.hotplug: allow for several spaces between fields in the db file. 2005-02-28 Julien Blache * tools/hotplug-ng/libsane.hotplug: replace \t by [[:space:]] when grepping for the device in the db file. 2005-02-21 Julien Blache * tools/hotplug-ng: added new hotplug/hotplug-ng hook, with scripts and documentation. 2005-02-20 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Removed Genius ColorPage-Vivid Pro II, now in external backend. Added Visioneer 9000. * AUTHORS configure configure.in backend/Makefile.in backend/dll.conf backend/sm3840.c backend/sm3840.conf backend/sm3840.h backend/sm3840_lib.c backend/sm3840_lib.h backend/sm3840_params.h backend/sm3840_scan.c doc/Makefile.in doc/sane-sm3840.man doc/sane.man doc/descriptions/sm3840.desc: Added sm3840 backend (from "Earle F. Philhower, III" ). * doc/descriptions-external/sm3840.desc: Removed (backend included in SANE). * NEWS: Updated. 2005-02-14 Gerhard Jaeger * doc/descriptions-external: Added new external backend geniusvp2. 2005-02-10 Karl Heinz Kremer * doc/descriptions/epson.desc: Added CX-3600 and 3650 doc/sane-epson.man, backend/epson.conf: Added information about GT-6500 (bug #301100) 2005-02-08 Oliver Schwartz * backend/snapscan.h backend/snapscan.conf Added IDs for Benq 5250C and 5000S 2005-02-08 Gerhard Jaeger * sanei/sanei_pp.c: Cleanup, uses now static array for parport management instead of dynamically allocated. Fixes at least a potential memory leak. 2005-02-06 Oliver Schwartz * doc/descriptions/unsupported.desc doc/descriptions/snapscan.desc: Renamed "Benq" to "Benq (Acer)" for better access through the scanner search engine. Fixes bug #301158 2005-02-06 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added several scanners. Minor fixes. * doc/descriptions-external/sm3840.desc: Added new external backend sm3840. * doc/descriptions-external/brother.desc: Updated status. * po/Makefile.in po/sane-backends.pl.po: Added Polish translation (from Jerzy Szczudlowski ). See bug #301054. 2005-02-04 Gerhard Jaeger * tools/hotplug/libsane.usermap: Added Epson CX6600 based on patch submitted by Aurelien Jarno . 2005-02-03 Ulrich Sigwanz * backend/niash_xfer.c: * tools/hotplug/libsane.usermap: added ID for Silitek-HP-ScanJet-3400c-Clone 2005-01-19 Oliver Rauch * backend/coolscan.c: added initialization of dev->scanning = SANE_FALSE 2005-01-18 Oliver Schwartz * backend/snapscan.h: Added ID for Benq 5250C 2005-01-16 Karl Heinz Kremer * backend/epson_usb.c: Added IDs for RX-425 (from bug report #301114) * doc/descriptsions/epson.desc: Added RX-425 2005-01-16 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added UMAX Astraslim 6000. Added Trust SCSI Scan 19200 -Excellence Series-. Added ColorPage-Vivid Pro II. * tools/hotplug/libsane.usermap: Added Epson Stylus RX425 based on bug report #301114. * doc/sane-agfafocus.man doc/sane-artec.man: Fixed some macros (patch from esr@thyrsus.com). 2005-01-12 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Genius ColorPage-Slim 1200 USB2. * doc/descriptions-external/genesys.desc: Added Canon Lide 40. * doc/descriptions-external/brother.desc: Added several scanners and updated the status of others based on user comments. 2005-01-12 Julien Blache * tools/hotplug/libusbscanner: use sysfs to get the device number, instead of guessing (it ended up being wrong in a number of cases...). Fix from Ubuntu, transmitted by Martin Pitt. 2005-01-12 Gerhard Jaeger * doc/plustek/Plustek-USB.changes: Update. * backend/plustek.c: Bumped build number. Copyright Update. * backend/plustek-usbscan.c backend/plustek-usbshading.c: Using now PhyDpi.y as selector for the motor MCLK setting. Copyright Update. * backend/plustek-usbdevs.c: Tweaked motor settings for EPSON and CANON1200. Copyright Update. * backend/plustek-*.[ch]: Copyright Update. 2005-01-09 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Guillemot scanner. 2005-01-09 Karl Heinz Kremer * backend/epson.c: "flaming hack to get USB scanners working without timeouts under linux" submitted by "Steve" (in comment to bug #300830) * doc/descriptions/epson.desc, doc/sane-epson.man: Added SCSI version of GT-5000 to supported list. 2004-12-29 Julien Blache * tools/sane-find-scanner.c: Added a hint about switching the scanner on and connecting it to the computer before doing anything. 2004-12-26 Gerard Klaver * doc/descriptions-external/hpaio.desc join two lines so html conversion possible doc/descriptions-external/stv680.desc added two webcams with different usb vid and pid 2004-12-26 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added several scanners. 2004-12-18 Karl Heinz Kremer * backend/epson.c backend/epson_usb.c: Added USB IDs for CX-3650 and CX-4600 and updated version number. 2004-12-15 Gerard Klaver * doc/descriptions-external/hpaio.desc stv680.desc Info new external backends 2004-12-01 Oliver Schwartz * backend/snapscan.c backend/snapscan-scsi.c backend/snapscan-options.c: Added quality calibration for Epson 2480 (implemented by Simon Munton) 2004-12-07 Henning Meier-Geinitz * backend/gt68xx_devices.c: Disabled stop_scan command for Mustek BearPaw 2448 TA Plus. 2004-12-05 Henning Meier-Geinitz * doc/sane-gphoto2.man doc/sane-microtek2.man: Fixed minor glitches based on patch from Eric S. Raymond . * doc/descriptions/unsupported.desc: Added Benq 320p. 2004-12-04 Julien Blache * tools/hotplug/libsane.usermap: Added Epson Perfection 4870 Photo, vendor 0x04b8 product 0x0128. 2004-12-01 Oliver Schwartz * backend/snapscan.c: Applied fix for allocation of gamma tables (thanks to Simon Munton) 2004-12-01 Oliver Schwartz * doc/descriptions/unsupported.desc: removed Epson Perfection 1270 2004-12-01 Oliver Schwartz * backend/snapscan.h backend/snapscan.c backend/snapscan-scsi.c backend/snapscan-options.c doc/descriptions/snapscan.desc: Added support for Epson Perfection 1270 2004-12-01 Henning Meier-Geinitz * doc/descriptions/umax1220u.desc doc/descriptions/unsupported.desc doc/descriptions-external/genesys.desc: Added several scanners. Moved UMAX 2100U to umax1220u.desc. Moved Plustek S(T)12 to unsupported.desc. 2004-11-28 Henning Meier-Geinitz * doc/descriptions-external/brother.desc. Updated comments. 9160 works. 2004-11-21 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Updated/added several scanners. 2004-11-21 Gerhard Jaeger * backend/plustek-usb.c: Fixed problem, when builing supported and connected device list. 2004-11-19 Rene Rebe * sanei/sanei_usb.c configure.in: use usb_interrupt_read instead of usb_bulk_read for sanei_usb_read_int. Fixes bug #300878. Added a missing DBG output in sanei_usb_read_int. Check for usb_interrupt_read to make sure, we have at least version 0.1.8. 2004-11-19 Rene Rebe * backend/avison.c doc/descriptions/avision.desc doc/descriptions/unsupported.desc: just added new IDs including moving the HP 82xx to the avision backend 2004-11-19 Gerhard Jaeger * backend/plustek.c backend/plustek-usb*: Major update, see doc/plustek/Plustek-USB.changes. Esp. improved support for multiple devices and autodetection. * doc/plustek/Plustek-USB.changes: Update. * doc/descriptions/plustek.desc: Updated release number. 2004-04-15 St�hane Voltz * backend/umax_pp_low.c: non i386 linux compilation fix, 1220P gray levels scan hang fix, 1220P color calibration tuning. 2004-11-14 Oliver Schwartz * snapscan-sources.c: Applied patch by Julien Blache to change ch_past_init from SANE_Int to SANE_Bool 2004-11-14 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx_devices.c backend/gt68xx_gt6801.c backend/gt68xx_gt6816.c doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Fixed test to move home sensor at the start of scan. Fixed gray mode linemode test. That stopped e.g. the Plustek OpticPro U 16 B from working in gray mode. 2004-11-13 Frank Zago * include/sane/sanei_backend.h: Added compiler attribute __unused__ for unused variables/functions. * backend/leo.c backend/teco3.c backend/matsushita.c backend/sceptre.c backend/teco1.c: use __unused__ * sanei/sanei_wire.c sanei/sanei_pa4s2.c sanei/sanei_scsi.c: fixes for 64 bits platforms. * backend/sp15c-scsi.h backend/sp15c.h backend/dc210.c backend/dc210.h: fixes some warnings. 2004-11-13 Henning Meier-Geinitz * backend/v4l.c: 64 bit fix from Frank Zago . 2004-11-13 Julien Blache * tools/hotplug/libsane.usermap: Added Epson Stylus Photo RX500, Epson Stylus Photo RX600 and Minolta Scan Dual II. 2004-11-10 Oliver Schwartz * snapscan-sources.c: First implementation of deinterlacer for epson scanners at high resolutions (thanks to Brad Johnson) 2004-11-07 Henning Meier-Geinitz * configure configure.in: Updated version. Enabled compilation warnings. * doc/releases.txt: Updated according to current practice. Older entries can be found in ChangeLog-1.0.15. backends-1.3.0/ChangeLogs/ChangeLog-1.0.17000066400000000000000000001176341456256263500176250ustar00rootroot00000000000000****** Release of sane-backends 1.0.17. End of code freeze ****** 2005-12-18 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added link to Lexmark X6170. * doc/descriptions/gt68xx.desc: Added Trust Direct WebScan 19200 (reported on sane-devel). * backend/Makefile.in: Added missing genesys_conv.c and genesys_conv_hlp.c to DISTFILES. Without this change the files wouldn't be part of the archive. * configure configure.in: Disabled compilation warnings. Increased version number. 2005-12-17 Karl Heinz Kremer * doc/descriptions/epson.desc: Added a few new scanners based on messages to the sane-devel mailing list. 2005-12-11 Henning Meier-Geinitz * NEWS: Updated for release. ---- CODE FREEZE FOR SANE 1.0.17 --- 2005-12-10 Henning Meier-Geinitz * doc/descriptions-external/hp3500.desc: Added .desc file for new external backend hp3500 which provides support for the HP ScanJet 3500 series (from Troy Rollo ). * doc/descriptions/unsupported.desc: Removed HP 3500, 3530, and 3570 (now in hp3500.desc). 2005-12-09 Henning Meier-Geinitz * doc/descriptions-external/brother-mfc4600.desc: Added .desc file for (older) external backend for Brother MFC 4600 (USB version). * doc/descriptions/unsupported.desc: Added Lexmark X6170. Removed Brother MFC 4600 USB. * backend/gt68xx.c backend/gt68xx_devices.c backend/gt68xx_high.c backend/gt68xx_low.c doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES doc/gt68xx/gt68xx.TODO: Silenced non-fatal warnings/error messages. Several fixes for avoiding freezes/timeouts after cancelling a scan. Several fixes for warming up of lamp. Decreased scan width of Plustek OpticPro 1248U. 2005-12-08 Gerhard Jaeger * backend/plustek-pp_tpa.c backend/u12-tpa.c: Fixed nasty compiler warnings. 2005-12-07 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Brother MFC-7300c. Removed Primax Colorado 2400U (now in primascan.desc). * doc/descriptions-external/primascan.desc: New external backend for Primax Colorado 2400U. 2005-12-07 Gerhard Jaeger * sanei/sanei_acces.c: Fixed problem, when the device name contains one or more path-separators. These characters are now converted. The problem occurs on 2.4 based installations as well as on OpenBSD. 2005-12-06 Stephane Voltz * backend/genesys_gl646.c: removed forgotten left-over of an experiment . 2005-12-06 Stephane Voltz * backend/genesys_gl646.c: added AF init in powersave, fixed long-standing bug related to a data read timeout between distinct scanning sessions 2005-12-06 Henning Meier-Geinitz * backend/genesys.conf: Enabled Canon LiDE 60 which was disabled for safety reasons. Bugs are fixed now. * doc/descriptions/genesys.desc: Changed status of Canon LiDE 50 and 60 to "good". Added comment for untested LiDE 40 to report any success/failure. * AUTHORS: Marked Fred Odendaal as active maintainer. * tools/hotplug/libsane.usermap: Added Epson Perfection 3490. 2005-12-05 Rene Rebe * backend/avision.c backend/avision.conf doc/sane-avision.man: fixed more typos in comments and debug output, removed obsolete options from the parser, example avision.conf and manual page. Fixed 12 Bit gray and color modes to actually work as well as software CCD line-difference correction. Minimal scan resolutions have been slightly adapted for some ASICs. 2005-12-05 Pierre Willenbrock * backend/genesys.c: removed usage of current_setup in functions where it is not necessarily initialized. Modified shading calibration to not use fixed stripe sizes. Fixed memory corruption while calculating shading data. Fixed read sizes to be multiples of 256. Fixed stagger/line distance correction. Fixed line shrinking to correctly update data buffers. Bumped BUILD number. * backend/genesys.c backend/genesys_conv.c: Added gray to lineart conversion. * backend/genesys.c backend/genesys_conv_hlp.c: Modified calling parameters to stagger/line distance correction filter to better describe its inner working. * backend/genesys_devices.c: Slowed down motor timings for LiDE 35/40/50/60. Removed untested message for LiDE 35/40/50/60. * backend/genesys_gl646.c: Fixed bug for odd pixel count. Added support for gray to lineart conversion. * backend/genesys_gl841.c: Fixed words_per_line calculation. Added support for gray to lineart conversion(not used). Fixed problem with scanners not backtracking while scanning calibration area leading to scanning part of document area. 2005-12-05 Oliver Schwartz * backend/snapscan.c backend/snapscan-scsi.c: small bugfix for Benq5150 2005-12-05 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Canon PIXMA MP170. 2005-12-04 Rene Rebe * backend/avision.c backend/avision.h: updated the Avision backend from BUILD 167 to 179, including support for HP 5370, AV600U, AV210C2, AV220C2, et. al. The USB i/o paths got hardened, and a lot of fixes as well es enhancements and optimizations where added and a lot of typos, mostly in debug output and comments, got fixed. The "Line Art" mode was renamed to "Lineart" to match the other backends. 2005-12-03 Karl Heinz Kremer * backend/epson_usb.c: Added id for CX4200 2005-12-04 Oliver Schwartz * backend/snapscan.h backend/snapscan.c backend/snapscan-scsi.c backend/snapscan-options.c: Some fixes for Benq 5150 2005-12-03 Peter Fales * backend/gphoto2.c: Cosmetic changes to debug messages 2005-12-02 Oliver Schwartz * backend/snapscan-sources.c: Another fix for lineart mode for the Epson 3490 @ 3200 DPI - this time tested * backend/snapscan.c: Change version number to 1.4.50 2005-12-01 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added link for Corex Cardscan 500. 2005-11-30 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added UMAX Astra 4100. * tools/hotplug/libsane.usermap: Added Epson Stylus CX4200. 2005-11-28 Jochen Eisinger * sanei/sanei_pa4s2.c: incorrect test of a bit mask. 2005-11-28 Stephane Voltz * backend/umax_pp_low.c: ifdef'ed forgottent debug statements 2005-11-28 Stephane Voltz * backend/umax_pp_low.c: fixed 'blue tint' on scanning area border by increasing shading coefficient on dark areas 2005-11-26 Oliver Schwartz * backend/snapscan-sources.c: Fix lineart mode for Epson 3490 * doc/descriptions/snapscan.desc: Update status for Benq 5000 2005-11-26 Oliver Schwartz * backend/snapscan-scsi.c: Fix for Benq 5000 * backend/snapscan.conf: Replace esfw52.bin with dummy filename entry 2005-11-25 Mattias Ellert * backend/artec_eplus48u.conf, backend/snapscan.conf: Use default firmware dirs in config files * doc/sane-artec_eplus48u.man, doc/sane-snapscan.man: Use default firmware dirs in man pages 2005-11-25 Ulrich Deiters * backend/canon-sane: fixed usage of an uninitialized variable 2005-11-25 Henning Meier-Geinitz * po/sane-backends.da.po: Updated Danish translation (from Mogens Jaeger ). 2005-11-25 Oliver Schwartz * backend/snapscan-sources.c: Fix for grayscale / linart for Epson 3490 at 3200 DPI 2005-11-25 Mattias Ellert * po/sane-backends.ru.po: Fixing some fuzzies 2005-11-24 Mattias Ellert * doc/gamma4scanimage.man, doc/sane-abaton.man, doc/sane-agfafocus.man, doc/sane-apple.man, doc/sane-artec.man, doc/sane-avision.man, doc/sane-bh.man, doc/sane-canon.man, doc/sane-canon630u.man, doc/sane-config.man, doc/sane-coolscan.man, doc/sane-coolscan2.man, doc/sane-dc210.man, doc/sane-dc240.man, doc/sane-dc25.man, doc/sane-dmc.man, doc/sane-epson.man, doc/sane-find-scanner.man, doc/sane-fujitsu.man, doc/sane-genesys.man, doc/sane-gphoto2.man, doc/sane-gt68xx.man, doc/sane-hp.man, doc/sane-hp4200.man, doc/sane-hp5400.man, doc/sane-ibm.man, doc/sane-leo.man, doc/sane-lexmark.man, doc/sane-ma1509.man, doc/sane-matsushita.man, doc/sane-microtek.man, doc/sane-microtek2.man, doc/sane-mustek.man, doc/sane-mustek_pp.man, doc/sane-mustek_usb2.man, doc/sane-nec.man, doc/sane-net.man, doc/sane-niash.man, doc/sane-pint.man, doc/sane-plustek.man, doc/sane-plustek_pp.man, doc/sane-ricoh.man, doc/sane-s9036.man, doc/sane-sceptre.man, doc/sane-sharp.man, doc/sane-snapscan.man, doc/sane-sp15c.man, doc/sane-st400.man, doc/sane-tamarack.man, doc/sane-teco1.man, doc/sane-teco2.man, doc/sane-teco3.man, doc/sane-u12.man, doc/sane-umax1220u.man, doc/sane-umax_pp.man, doc/sane-usb.man, doc/sane-v4l.man, doc/sane.man, doc/saned.man, doc/scanimage.man: man page fixes 2005-11-23 Oliver Schwartz * backend/snapscan-options.c: Disable bilevel colour / halftoning for Epson 3490 2005-11-23 Mattias Ellert * backend/Makefile.in: added "artec_eplus48u" to FIRMWARE_DIRS 2005-11-22 Mattias Ellert * po/sane-backends.sv.po: Updated Swedish translation * sanei/sanei_scsi.c: Added some debugging. Fixed some warnings ---- FEATURE FREEZE FOR SANE 1.0.17 --- 2005-11-20 Henning Meier-Geinitz * backend/genesys.c backend/genesys_devices.c backend/genesys.conf: Bumped build number. Changed scanner's name to Canon LiDE 35/40/50. Added Canon LiDE LiDE 60. This scanner is still commented out in genesys.conf. Removed comment sign for Canon LiDE 35/40/50 in genesys.conf. * doc/descriptions/genesys.desc: Added Canon LiDE 35, 40, 50, 60. * doc/sane-genesys.man: Updated concerning gl841 scanners. * doc/descriptions/unsupported.desc: Moved Canon LiDE 35/40/50/60 to genesys.desc. Added Canon Pixma MP150, Tevion MD 90070 and Primax Colorado 1200p. * tools/hotplug/libsane.usermap: Added Plustek Opticslim 2400 ids (from Jan Matousek). * NEWS: Updated. 2005-11-19 Pierre Willenbrock * backend/genesys_gl841.c backend/genesys_devices.c: Added support for Canon LiDE 35/40/50 * backend/genesys.c backend/genesys_low.h backend/genesys_gl646.c: Reworked data conversion process to convert CIS data, added new slope generation variant * backend/genesys_conv.c backend/genesys_conv_hlp.c: Moved conversion filter functions out of backend/genesys.c 2005-11-18 Oliver Schwartz * backend/snapscan-options.c: Disable 2400 DPI for Epson 3490, use 1600 DPI instead 2005-11-17 Gerhard Jaeger * doc/plustek/Plustek-PARPORT.changes: Update. * backend/plustek_pp.c backend/plustek-pp.h backend/plustek-pp_ptdrv.c backend/plustek-pp_wrapper.c: Fixed sizeof(long) issue for 64bit platforms, see bugreport #302195. * backend/plustek_pp.conf: Default config now only tries to access parport using libieee1284. 2005-11-15 Oliver Schwartz * backend/snapscan.c backend/snapscan-options.c backend/snapscan-scsi.c: Enabled quality calibration for the Epson 3490 * doc/descriptions/snapscan.desc: Changed status for Epson Perfection 3490 (good) and 3590 (basic) 2005-11-15 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added HP Scanjet 4890. Added several Optrox scanners. * doc/descriptions-external/brother.desc: Marked status of MFC-9880 as "unsupported" based on a user's report. 2005-11-13 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added IOmagic MobileScan USB. Mentioned chips used in the Artec AM12E+. * doc/descriptions-external/hp3770.desc doc/descriptions-external/hp8200.desc: Added clarification concerning status of these backends. 2005-11-10 Oliver Schwartz * backend/snapscan.c backend/snapscan-sources.c: Added deinterlacing for Epson 3490 * backend/Makefile.in: added "snapscan" to FIRMWARE_DIRS 2005-11-07 m. allan noah * backend/fujitsu.c: M3091/2 lie about gamma dl capability 2005-11-07 Henning Meier-Geinitz * doc/descriptions/microtek2.desc: Fixed "unmaintained" marker. Added Microtek Scanmaker V6UPL (bug #302464). * tools/check-usb-chip.c: Added detection of rts8822l-01h chipset (patch from Jonathan Bravo Lopez ). 2005-11-06 Henning Meier-Geinitz * doc/descriptions-external/epkowa.desc: Disabled man page link (points to nowhere). Bug #302463. * doc/descriptions/unsupported.desc: Added Canon PIXMA MP130. * doc/sane-hp.man doc/sane-microtek2.man: Fixed links to ppscsi. 2005-11-02 Oliver Schwartz * sanei/sanei_usb.c: Fixed output of transfer buffer for usb_read_bulk in OS/2. * backend/snapscan.c backend/snapscan-scsi.c: Fixes for Benq 5000 * backend/snapscan-usb.c: Avoid recursive calls of usb_sense_handler 2005-11-01 Henning Meier-Geinitz * sanei/sanei_usb.c: Added support for detecting vendor and product id with FreeBSD kernel scanner driver (based on patch in FreeBSD ports). * README.freebsd: Updated information about USB scanners. 2005-10-31 Oliver Schwartz * backend/snapscan.c backend/snapscan-scsi.c backend/snapscan.h backend/snapscan-options.c doc/descriptions/snapscan.desc: Distinguish between 5000/5000E/5000U * backend/snapscan-sources.c: Enable deinterlacer for 5000E/5000U for 1200 DPI * backend/snapscan.conf: Fix names for 5000/5000E/5000U 2005-10-30 Giuseppe Sacco * po/*it.po: Updated italian translation 2005-10-29 Henning Meier-Geinitz * backend/artec_eplus48u.conf doc/descriptions/artec_eplus48u.desc: Added support for Trust 240H Easy Webscan Gold to artec_eplus48u backend. * Makefile.in acinclude.m4 configure configure.in backend/Makefile.in frontend/Makefile.in lib/Makefile.in sanei/Makefile.in tools/Makefile.in: Run "makedepend" if it's available. This way "make" builds backends correctly even if only included files (e.g. headers) are changed. Don't be too verbose when running makedepend. Create links libsane-*.so.1 to the real library files if the links are not there. This fixes dynamic loading on OpenBSD. The links are not created for MacOS X as they don't work there. Remove any libsane.* links in /usr/local/lib/sane. Such links are created by libtool. As they point to libsane-v4l.so, scanimage -L doesn't find any scanner in case of ld.so misconfigurations. The install target is much quiter now and prints the libtool message only once now. * README.openbsd: Removed comment about broken library links. 2005-10-28 Gerhard Jaeger * po/*.po: Updated po files, corrected/updated german translation. * doc/plustek/Plustek-USB.changes: Update. * backend/plustek.c backend/plustek.h: Added OPTION_SPEEDUP. * backend/plustek-usbdevs.c: Changed high-speed setting for UMAX 3400, due to bugreport #302317. Fixed CanoScan N650U settings (bugreport #302433). 2005-10-25 Gerhard Jaeger * doc/plustek/Plustek-USB.changes: Update. * backend/plustek.c: Bumped build number. * backend/plustek-usbdevs.c: Tweaked LiDE25 settings. * backend/plustek-usb.c: Let CIS devices use green-channel for gray scans. * backend/plustek-usbcal.c: Fixed segfault condition in fine calibration for gray scanmodes. 2005-10-24 Oliver Schwartz * backend/snapscan.c backend/snapscan-scsi.c backend/snapscan.h backend/snapscan-options.c: Fix transparency range for Epson 2480/2580, fix preview for Epson 2580. 2005-10-24 Henning Meier-Geinitz * doc/descriptions/umax1220u.desc: Marked backend as unmaintained. Used non-broken link. 2005-10-23 Oliver Schwartz * backend/snapscan.c backend/snapscan-scsi.c: Fixes for buffer size in high-res modes by Simon Munton, small changes to delay code. 2005-10-23 Henning Meier-Geinitz * tools/hotplug/libsane.usermap: Added Epson Stylus DX3850 (from niels_kalle ). 2005-10-22 Eddy De Greef * backend/mustek_pp_cis.c: Decreased the maximum number of pixels on a line for CIS scanners a bit to avoid border artifacts. 2005-10-22 Eddy De Greef * backend/mustek_pp_cis.c backend/mustek_pp_cis.h doc/sane-mustek_pp.man: Added an optional engine_delay parameter to work around potential engine instability problems for CIS models. 2005-10-21 Gerhard Jaeger * doc/plustek/Plustek-USB.changes: Update. * backend/plustek.c: Bumped build number. * backend/plustek-usbdevs.c: Fixed high-speed feature of Canoscan D660U. 2005-10-20 Oliver Schwartz * backend/snapscan-scsi.c: Fixes for 16 bit quality calibration by Simon Munton 2005-10-18 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added HP ScanJet 7650 and UMAX Astra 2500. * doc/descriptions-external/brother2.desc: MFC-210C is reported to work. * NEWS: Created entry for sane-backends 1.0.17. 2005-10-17 Henning Meier-Geinitz * acinclude.m4 configure: Fixed check for libpthread functions. * tools/hotplug/libsane.usermap: Added Brother MFC 210C (from Benjamin Mirza ). * doc/descriptions-external/epkowa.desc: Updated for iscan 1.17.0 (patch from Olaf Meeuwissen ). Bug #302183. 2005-10-16 Henning Meier-Geinitz * AUTHORS: Marked Michael Herder as not active. No activity since some time, most email addresses bounce, no response to pings. Changed contact address to the only one that doesn't directly bounce. * backend/artec_eplus48u.conf: Added support for UMAX AstraSlim 1200 SE (from Harq al-Ada ). * doc/descriptions/artec_eplus48u.desc: UMAX AstraSlim 1200 SE is supported. Backend is unmaintained. * doc/descriptions/unsupported.desc: Added HP ScanJet 4850C. Removed UMAX AstraSlim 1200 SE. * backend/gt68xx.c backend/gt68xx_generic.c doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Minor modifications to sheet-fed scanner support. * backend/mustek_usb2.c backend/mustek_usb2.h backend/mustek_usb2_asic.c backend/mustek_usb2_asic.h backend/mustek_usb2_high.c backend/mustek_usb2_high.h backend/mustek_usb2_reflective.c backend/mustek_usb2_transparent.c doc/descriptions/mustek_usb2.desc doc/mustek_usb2/mustek_usb2.CHANGES: Removed typedefs for DWORD, WORD and so on. Used int, unsigned short, ... directly in the code. * AUTHORS configure configure.in backend/Makefile.in backend/dll.conf backend/lexmark-x1100.c backend/lexmark.c backend/lexmark.conf backend/lexmark.h doc/Makefile.in doc/sane-lexmark.man tools/hotplug/libsane.usermap: Added lexmark backend (from Fred Odendaal ). * frontend/tstbackend.c: Added vendor "Lexmark". * doc/sane.tex: Added vendor "Lexmark". Used current date. * doc/descriptions-external/lexmark.desc doc/descriptions/lexmark.desc: Moved to doc/descriptions. Added "new" marker. Added manpage link. Updated version. Marked X1180 as "basic" according to man page. * doc/sane.man: Added lexmark backend. * backend/Makefile.in doc/Makefile.in tools/Makefile.in: Make "make dist" work again. * acinclude.m4 configure include/sane/config.h.in: Check for pthread_cancel() and pthread_testcancel(). 2005-10-15 Jochen Eisinger * doc/descriptions/mustek_pp.desc: add Medion MD9806 scanner as supported 2005-10-14 Oliver Schwartz * backend/snapscan-options.c backend/snapscan-scsi.c backend/snapscan.c backend/snapscan-sources.c: Fixes for 16 bit scan mode from Simon Munton 2005-10-11 Oliver Schwartz * backend/snapscan-options.c backend/snapscan-scsi.c backend/snapscan.c backend/snapscan-sources.c: Fixes for Epson 3490/3590 and 16 bit scan mode 2005-10-11 Stephane Voltz * backend/umax_pp.c: change sane_start semantic to allow for batch scanning 2005-10-08 Henning Meier-Geinitz * doc/descriptions-external/mustek_a3p1.desc: Downgraded status to minimal. * doc/descriptions/mustek_usb2.desc: Added "new" marker. 2005-10-06 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added HP ScanJet 3800c. * frontend/scanimage.c: Use correct size for fgets. Patch from Antoine Jacoutot . 2005-10-05 Mattias Ellert * backend/microtek2.c: Off-by-one error 2005-10-03 Henning Meier-Geinitz * AUTHORS acinclude.m4 configure configure.in backend/Makefile.in backend/dll.conf backend/mustek_usb2.c backend/mustek_usb2.h backend/mustek_usb2_asic.c backend/mustek_usb2_asic.h backend/mustek_usb2_high.c backend/mustek_usb2_high.h backend/mustek_usb2_reflective.c backend/mustek_usb2_transparent.c doc/Makefile.in doc/sane-mustek_usb2.man doc/mustek_usb2/mustek_usb2.CHANGES doc/mustek_usb2/mustek_usb2.TODO: Added mustek_usb backend which supports the Mustek BearPaw 2448 TA Pro. Changes of the code before inclusion to CVS can be found in doc/mustek_usb2/mustek_usb2.CHANGES. * doc/sane.man: Added mustek_usb2. Added description for u12 backend. Added missing links to other backends. * doc/descriptions/mustek_usb2.desc: Moved from descriptions-external. Updated. * PROJECTS: Removed link to BeOS (link goes to nowhere, SANE website has links to projects). Removed link to wi32 port (included in SANE). Removed link to SaneTwain (listed on SANE website). * doc/doxygen-sanei.conf.in: Updated to current doxygen version. Output more C-friendly format. * include/sane/sanei.h include/sane/sanei_backend.h include/sane/sanei_debug.h include/sane/sanei_pp.h include/sane/sanei_usb.h: Fixed some bugs in doxygen documentation. Added some comments. 2005-10-02 Gerhard Jaeger * doc/plustek/Plustek-USB.changes: Update. * backend/plustek.c: Bumped build number. * backend/plustek-usbdevs.c backend/plustek-usb.h: Updated motor settings for Canoscan LiDE25, thanks to Stephan February for providing these values. 2005-10-02 Henning Meier-Geinitz * doc/descriptions/as6e.desc: Added Trust Easy Scan 9600 Plus (bug #301000). * backend/sharp.c: Fixed some compilation warnings (bug #300404). * AUTHORS: Fixed Rene Rebe's email address. * doc/descriptions/unsupported.desc: Removed Epson Perfection 4180 (supported by epkowa backend). 2005-10-01 Henning Meier-Geinitz * doc/descriptions-external/mustek_a3p1.desc doc/descriptions/unsupported.desc: Added external mustek_a3p1 backend that supports Mustek P 3600 A3 Pro. * tools/Makefile.in: Added udev to list of directories that are part of the distribution. * doc/descriptions.txt: Yet another "itsself". * backend/sp15c.c: Fixed some warnings (bug #302290). 2005-10-01 Julien Blache * doc/sane-find-scanner.man: Typo fix, s/itsself/itself/ from Alfie Costa. 2005-09-29 Eddy De Greef * backend/mustek_pp_cis.c: Minor bug fix: wrong model name was reported for 1200CP+ driver. 2005-09-29 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx.conf backend/gt68xx_devices.c backend/gt68xx_generic.c backend/gt68xx_generic.h backend/gt68xx_high.c backend/gt68xx_low.c backend/gt68xx_low.h doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES doc/gt68xx/gt68xx.TODO: Added support for Plustek OpticSlim M12 (untested). Based on patch from Gerhard Jaeger . Fixed gt68xx homepage links in man page. Changed minimum version of libusb to 0.1.8. * doc/descriptions/unsupported.desc: Removed Plustek OpticSlim M12. 2005-09-28 Oliver Schwartz * backend/snapscan-options.c backend/snapscan-scsi.c backend/snapscan.c backend/snapscan.h: Added 16 bit option for Epson scanners, untested. Re-enabled enhanced inquiry command for epson scanners. 2005-09-28 Julien Blache * tools/udev/convert-usermap.sh: Don't print 0x when matching VID and PID. 2005-09-28 Julien Blache * tools/udev/convert-usermap.sh: Added script to convert hotplug/libsane.usermap to a udev rules file. udev 070 + linux 2.6.14 will deprecate hotplug. * tools/README: updated. 2005-09-28 Henning Meier-Geinitz * doc/descriptions-external/brother.desc: Removed duplicate entry. * doc/descriptions/genesys.desc: Added link to genesys homepage. 2005-09-26 Henning Meier-Geinitz * tools/sane-find-scanner.c: Print more clear output if no USB scanners are found. Point to manual page. Warn if libusb support hasn't been built. 2005-09-25 Henning Meier-Geinitz * doc/descriptions-external/lexmark.desc doc/descriptions/unsupported.desc: Added external lexmark backend. Moved Lexmark X11?? devices to lexmark.desc. Based on patch from Fred Odendaal . 2005-09-25 Oliver Schwartz * backend/snapscan-scsi.c: Removed debugging code for Epson scanners until a working solution has been found. 2005-09-23 Henning Meier-Geinitz * README: Mention in addition to the listed libraries, their header files are also necessary. A missing usb.h is a common problem when building SANE (--> no USB support). * configure configure.in: Added warning message that's printed when libusb or its header file is not available. * frontend/scanimage.c doc/scanimage.man: Added progress indicator to scanimage (based on patch from Mario Goppold ). Updated copyright information, added links to sane-devel mailing list. * sanei/sanei_usb.c: Ignore EBUSY from usb_set_configuration. This function fails, if a different interface of the same device is claimed (e.g. by usblp). 2005-09-22 Mattias Ellert * backend/hp4200.c backend/umax.c backend/umax1220u.c include/sane/sanei_pv8630.h sanei/sanei_pv8630.c: Fix SANE_DEBUG_SANEI_PV8630 2005-09-19 Frank Zago * backend/dc210.c backend/leo.c backend/matsushita.c backend/sceptre.c backend/sp15c-scsi.h backend/sp15c.h backend/teco1.c backend/teco2.c backend/teco3.c include/sane/sanei_backend.h: Replaced __unused__ with __sane_unused__ to avoid a namespace conflict. 2005-09-18 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx_high.c doc/descriptions/gt68xx.descdoc/gt68xx/gt68xx.CHANGES doc/gt68xx/gt68xx.TODO: Fixed segfault that may happen with Mustek ScanExpress A3 USB. * doc/backend-writing.txt: Added some hints about number of files. * backend/mustek.conf doc/sane-mustek.man doc/descriptions/mustek.desc: Mustek Paragon 600 II EP works. Mentioned "parport0". * doc/descriptions/unsupported.desc: Added Canon PIXMA MP750. 2005-09-17 Mattias Ellert * README.darwin: New date for good libusb from CVS 2005-09-07 Mattias Ellert * acinclude.m4, configure: fix disabling of locking when the locking user group does not exist 2005-09-07 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Genius ColorPage Slim 1200. 2005-09-07 Oliver Schirrmeister * backend/fujitsu.c: enabled dropoutcolor for all fi-* scanners. Applied patch from Mario Goppold: Bugfixes for the M3092DCd 2005-09-05 Henning Meier-Geinitz * po/sane-backends.ru.po: Updated Russian translation (from Vitaly Lipatov ). 2005-09-03 Oliver Schwartz * backend/snapscan.h backend/snapscan-scsi.c: (Hopefully) fixed some debugging code for Epson scanners that only works after firmware upload. 2005-09-02 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Epson Perfection 4490 Photo. * tools/hotplug/libsane.usermap: Added HP PSC 750. 2005-09-01 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx_devices.c backend/gt68xx_generic.c backend/gt68xx_high.c doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Genius ColorPage Vivid 1200 X is reported to work. Genius ColorPage Vivid 4 XE seems to be the same as 4 X, it just doesn't have buttons. Cleanup of .desc file. Fixed gain setting. Mustek ScanExpress A3 USB 600 dpi color scanning works now. * tools/check-usb-chip.c: Added detection for SQ113 chip (e.g. Mustek BearPaw 2448 TA Pro). 2005-08-31 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Removed Mustek 1800 A3 Pro (this is actually a P3600 A3 Pro). * doc/descriptions-external/mustek_usb2.desc: Mustek BearPaw 2448 TA Pro has status "good" now. 2005-08-30 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Removed Mustek BearPaw 2448 TA Pro (now supported by external Mustek USB2 backend). Added comments about that backend to some other scanners that may be supported later. * doc/descriptions-external/mustek_usb2.desc: New external Backend for Mustek BearPaw 2448 TA Pro. 2005-08-29 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Canon LiDE 60 is GL841 based. 2005-08-28 Henning Meier-Geinitz * doc/descriptions/canon630u.desc: Changed status of Canon 636u too "good". 2005-08-27 Henning Meier-Geinitz * doc/descriptions-external/brother2.desc: Brother MFC-5440CN is reported to work (bug #302105). 2005-08-24 St~hane Voltz * backend/umax_pp.c backend/umax_pp_low.c tools/umax_pp.c: Added EPP support for 610P, revision number changes 2005-08-23 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Canon LiDE 60. Reflecta Digitdia 3600 is not GL841-based. 2005-08-22 St~hane Voltz * tools/check-usb-chip.c: Added rts8858c detection (Lexmark X1100 series, Dell A920). 2005-08-22 Henning Meier-Geinitz * tools/hotplug/libsane.usermap: Added some Genius scanners. Fixed Visioneer Onetouch 7300 USB. Added Plustek OpticSlim 2400. * README.openbsd: Mention problems with library names. 2005-08-22 Gerhard Jaeger * doc/plustek/Plustek-USB.changes: Update. * backend/plustek.c: Bumped build number. * backend/plustek-usb.c backend/plustek-usbscan.c: Fixed problem, when trying to scan at resolutions beyond the optical one (sensor stops too early). * tools/hotplug/libsane.usermap: Added USB ID for LiDE25. 2005-08-21 Henning Meier-Geinitz * doc/descriptions/microtek2.desc doc/descriptions/unsupported.desc: Marked microtek2 backend as unsupported. Changed link to point to old website directly. Listed Microtek ScanMaker 9800XL as supported (bug #301515). 2005-08-21 Mattias Ellert * ltmain.sh acinclude.am configure: Updated from libtool 1.5.18. 2005-08-20 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx.conf backend/gt68xx_devices.c backend/gt68xx_low.h doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES doc/gt68xx/gt68xx.TODO: Increased number limit of scanners that can work with this backend to 50. Added support for Genius ColorPage Vivid 1200 X (untested). 2005-08-19 Henning Meier-Geinitz * configure configure.in: Fixed check for usbcalls.h on OS/2 (patch from Paul Smedley ). * backend/gt68xx.c backend/gt68xx_devices.c doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Enabled GT68XX_FLAG_NO_STOP for Mustek BearPaw 2400 CU Plus. Some of these scanners don't seem to like that command. * doc/descriptions/artec_eplus48u.desc: Disabled link to backend homepage which doesn't seem to contain anything sane-related. 2005-08-19 Gerhard Jaeger * doc/plustek/Plustek-USB.changes: Update. * backend/plustek-usb.h backend/plustek-usbdevs.c: Removed obsolete _WAF_BLACKFINE. LiDE20 does not seem to have a reliable black calibration area, so the devices now will switch off the lamp for dark calibration. * backend/plustek-usbcal.c backend/plustek-usbscan.c: Cleanup. * backend/plustek-usbshading.c: Fixed line statistics and added calibration data output. * backend/plustek.c: Bumped build number. 2005-08-18 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx_low.c backend/gt68xx_low.h doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES doc/gt68xx/gt68xx.TODO: Fixed the problem concerning scanning on *BSD. Scanning only worked once (bug #300597). Fixed compilation warnings. 2005-08-17 Henning Meier-Geinitz * README: Min. libusb version is 0.1.8. 2005-08-17 Julien Blache * tools/hotplug/libsane.usermap: Added USB IDs for the sm3600-supported scanners. If you know the exact model name corresponding to the above IDs, please mail sane-devel: 0x05da/0x40b3, 0x05da/0x40b8, 0x05da/0x40ca, 0x05da/0x40dd, 0x05da/0x40ff 2005-08-16 Oliver Schwartz * backend/snapscan-scsi.c: Make compileable ;-) * backend/snapscan-options.c: Removed //-style comment 2005-08-16 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Canon PIXMA MP780 and Lexmark P6250. * tools/sane-find-scanner.c: Don't print anything else but the found messages in "-q" mode. * tools/check-usb-chip.c: Added check for combination of a PowerVision PV8630 (USB->parport bridge) and National Semiconductor LM9830 as used in the HP 4200. Fixed compilation warning. Added check for Toshiba M011 chips as used in Microtek ScanMaker 3600, 3700, and 3750. * configure configure.in backend/Makefile.in backend/sm3600-scanusb.c backend/sm3600-scanutil.c backend/sm3600.c backend/sm3600.h doc/sane-sm3600.man doc/sane-usb.man: Removed direct dependence of sm3600 on libusb. Used sanei_usb instead. Based on patch from Fran~is Revol . Updated documentation accordingly. Fixed compilation warnings. 2005-08-15 Oliver Schwartz * backend/snapscan.c: Bumped version number * backend/snapscan-scsi.c: Added temporary debug code for 2480/2580 distinction 2005-08-15 Oliver Schwartz * backend/snapscan.c backend/snapscan-options.c backend/snapscan-scsi.c backend/snapscan.h doc/descriptions/snapscan.desc backend/snapscan.conf: Added support for Epson Perfection 3490/3590 (thanks to Matt Judge). 2005-08-15 Gerhard Jaeger * doc/plustek/Plustek-USB.changes: Update. * doc/descriptions/plustek.desc: Updated version number. * backend/plustek.h. backend/plustek-usb.h backend/plustek-usbcalfile.c backend/plustek-usbimg.c backend/plustek-usbmap.c backend/plustek-usbdevs.c backend/plustek-usbscan.c: Cleanup. * backend/plustek.c: Bumped version and build number. Activated IPC between reader-process and parent. * backend/plustek-usbio.c: usbio_DetectLM983x() now returns error if register could not be red, usbio_ResetLM983x() checks for reg7 value before writing. * backend/plustek-usbhw.c: Added button support for Plustek/Genius devices. Changed behaviour of usb_IsScannerReady(). Added special misc I/O setup for CIS devices (usb_ResetRegisters). * backend/plustek-usb.c: Minor fix for startup reset. Removed unnecessary calls to usbio_ResetLM983x(). * backend/plustek-usbshading.c: Readded kCIS670 to add 5% extra to LiDE20 fine calibration. * backend/plustek-usbcal.c: Tried to use the settings from SANE-1.0.13. Added _TWEAK_GAIN to allow increasing GAIN during lamp coarse calibration. Added call to speedtest function. 2005-08-14 Henning Meier-Geinitz * Makefile.in: Added Changelog-1.0.16 to DISTFILES. * backend/hp4200.c doc/sane-hp4200.man doc/descriptions/hp4200.desc: Enabled backtracking by default. This is slower but avois bumping the scan head at the end of the scan area and also missing parts of the scanned image. Increased safety margin for backtracking. This fixes the "garbled image" bug. Set default gamma value to 2. Manpage update. Status set to "basic". * frontend/scanimage.c: Don't exit with error when trying to set inactive options. This especially happens in connection with geometry options and the v4l backend (bugs #300321, #301977). 2005-08-13 Henning Meier-Geinitz * doc/descriptions/niash.desc doc/descriptions/sm3840.desc: Removed ":new". * AUTHORS configure configure.in backend/Makefile.in backend/dll.conf backend/hp4200.c backend/hp4200.conf backend/hp4200.h backend/hp4200_lm9830.c backend/hp4200_lm9830.h doc/Makefile.in doc/sane-hp4200.man doc/sane.man doc/descriptions/hp4200.desc: Added hp4200 backend. Code from Julien BLACHE's sane-backends-extras debian package, based on Frank Zago's patches based on Adrian Perez Jorge's code. Fixed compilation warnings. Fixed bug when no sane device was given. Code indented by indent -gnu. Added man page. * doc/descriptions-external/hp4200.desc: Removed (backend is now included). * configure configure.in: Moved CPPFLAGS definition down to avoid problems with libtool tests for -fPIC. 2005-08-10 Gerhard Jaeger * backend/plustek_pp.c: Bumped build number. * backend/plustek_pp-genericio.c: Fixed bug, that causes ASIC96003/1 based devices to move their sensors too fast in lineart mode at 200 and 300dpi. * doc/plustek/Plustek-PARPORT.changes: Update. 2005-08-09 Henning Meier-Geinitz * sanei/sanei_usb.c: Minor fixes for usbcalls interface (patch from Paul Smedley ). * doc/descriptions/unsupported.desc: Added Hewlett-Packard Scanjet 4370. 2005-08-08 Gerhard Jaeger * acinclude.m4 configure: Locking feature will be disabled on OS/2 per default (according to Paul Smedley ). * doc/plustek/Plustek-USB.changes: Update. * backend/plustek.c: Bumped build number. * doc/plustek/Plustek-USB.txt doc/descriptions/plustek.desc doc/sane-plustek.man backend/plustek-usb.c backend/plustek-usbdevs.c: Added support for CanoScan LiDE25. 2005-08-07 Oliver Schwartz * backend/snapscan.c: Use first known device if no explicit device name is specified 2005-08-07 Henning Meier-Geinitz * configure configure.in: Updated version. Enabled compilation warnings. * doc/releases.txt: Typo fix. * config.guess config.sub: Updated from libtool 1.5.18. * configure configure.in include/sane/config.h.in sanei/sanei_usb.c: Added support for usb functionality on OS/2 using the usbcalls interface (patch from Paul Smedley ). * sanei/sanei_usb.c: Fixed some compilation warnings. Older entries can be found in ChangeLog-1.0.16. backends-1.3.0/ChangeLogs/ChangeLog-1.0.18000066400000000000000000001150701456256263500176160ustar00rootroot00000000000000****** Release of sane-backends 1.0.18. End of code freeze ****** 2006-07-03 Henning Meier-Geinitz * configure configure.in: Increased version number. 2006-07-02 Gerhard Jaeger * backend/plustek.c: Fixed the fix (problem when trying to select bit-depth). 2006-07-01 Wittawat Yamwong * doc/sane-pixma.man doc/descriptions/pixma.desc: Updated status of MP760 and MP780 2006-06-30 Gerhard Jaeger * doc/plustek/Plustek-USB.changes: Update * backend/plustek.c: Fixed problem when trying to select bit-depth. 2006-06-29 Henning Meier-Geinitz * NEWS: Added dell1600n_net backend. * doc/releases.txt: Typo fix. * aclocal.m4 configure configure.in include/sane/config.h.in: Disabled compilation warnings for release. ---- CODE FREEZE FOR SANE-BACKENDS 1.0.18 --- 2006-06-26 Henning Meier-Geinitz * NEWS: Updated for 1.0.18. 2006-06-24 m. allan noah * backend/fujitsu.c: update to v1.0.33, add 5900 usb id * doc/descriptions/fujitsu.desc: usb id and version update 2006-06-22 Henning Meier-Geinitz * doc/descriptions/epson.desc: Fixed USB id of CX6600 (patch from Olaf Meeuwissen ). * doc/descriptions-external/epkowa.desc: Updated (patch from Olaf Meeuwissen ). * doc/descriptions/unsupported.desc: Added Xerox WorkCentre PE120i. 2006-06-19 Henning Meier-Geinitz * doc/sane-usb.man: Fixed typo (bug #303571). 2006-06-19 Giuseppe Sacco * corrected sane-usb manual page. 2006-06-15 Ulrich Deiters * backend/canon.c, canon.h, canon-scsi.c, canon-sane.c: got rid of some compiler warnings 2006-06-14 m. allan noah * backend/fujitsu.c: update to v1.0.32, add 4220c2 usb id * doc/descriptions/fujitsu.desc: usb id and version update 2006-06-14 Jon Chambers * backend/dell1600n_net.c: working backend added 2006-06-13 m. allan noah * backend/fujitsu.[ch]: update to v1.0.31, add 5220c usb id, don't show ink level buttons if no imprinter, gather button presses more frequently * doc/descriptions/fujitsu.desc: usb id and version update 2006-06-12 Jochen Eisinger * doc/descriptions/mustek_pp.desc: Added Micromaxx MM-0851 ---- FEATURE FREEZE FOR SANE 1.0.18 --- 2006-06-11 Henning Meier-Geinitz * backend/coolscan2.c doc/descriptions/coolscan2.desc: Added (minimal) support for Nikon LS 50 ED, Coolscan V ED and (probably) Super Coolscan LS-5000 ED (patch from Giuseppe Sacco ). * backend/pixma.h: Fixed compilation problem on *BSD (ENODATA not defined). * doc/descriptions/unsupported.desc: Added Canon 9950. Removed Epson 4490 (supported by epkowa). Removed Nikon scanners (supported by coolscan2 backend). Updated PLANon DocuPen R700. 2006-06-11 Eddy De Greef * AUTHORS backend/mustek_pp_cis.c backend/mustek_pp_cis.h doc/sane-mustek_pp.man: e-mail address update. 2006-06-11 Henning Meier-Geinitz (patch applied by Karl Heinz Kremer * doc/descriptions/epson.desc: Added CX-4800. Added Perfection 4990 (bug #301795). * backend/epson_usb.c: Added CX-4800 (bug #303341). * backend/epson.c: Fixed segfault when a scanner isn't recognized. Added "Flatbed" as the only possible source option for MOD_SOURCE in this case (bug #303340). Epson CX4800 and CX6400 were detected as "unknown model". This changes request_identity2 to 0 in the EpsonCmdRec for the "D7" level, which skips the call to get_identity2_information(). The second bug was that the return status of get_identity2_information() was not checked in attach(). This actually allowed the scanner to be used but with inaccurate parameters (e.g. "Unknown model", default color depth, etc) because the scanner was already closed before probing for the rest of the parameters (bug #301478, #303342). The Epson Perfection 4990 photo/GT-X800 wrongly return 3200 dpi as their maximum resolution. This workaround enables the full 4800 dpi (bug #302090, patch from Claus Boje ). * backend/epson.conf.in: Detect some more epson SCSI scanners (GT-6000, GT-9000) which report "EPSON SC" instead of "EPSON" as SCSI manufacturer ID (bug #303269). * sanei/sanei_pio.c: Don't do a busy loop when looking for scanners. This froze the backend when the scanner was switched off. Also CPU time is much reduced (bug #301926). 2006-06-11 Gerard Klaver * include/Makefile.in font_6x11.h added 2006-06-10 Wittawat Yamwong * backend/pixma_mp750.c doc/sane-pixma.man /doc/descriptions/pixma.desc: Removed experimental flag from MP750, MP760 and MP780. 2006-06-09 Gerard Klaver * doc/sane-stv680.man update text 2006-06-09 m. allan noah * frontend/scanimage.c: move sane_cancel() out of scan_it(). more like scanadf. fixes issues with duplex adf scanning. 2006-06-08 Wittawat Yamwong * backend/pixma.h: Upgraded to version 0.11.3 * backend/pixma_common.c backend/pixma_common.h: Added a work-around for the buffer underrun problem. * backend/pixma_io_sanei.c: Set minimum timeout to 10ms. * backend/pixma_mp150.c: Added 2 commands for MP800. * backend/pixma_mp730.c: Added debug info. * backend/pixma_mp750.c: Added support for buttons and a work-around for the lockup problem. * backend/pixma.c: Removed SANE_I18N from backend type string. * doc/sane-pixma.man: Updated * doc/descriptions/pixma.desc: Updated status of MP750. 2006-06-08 Gerhard Jaeger * doc/plustek/Plustek-USB.changes: Update * backend/plustek.c backend/plustek-usbcal.c backend/plustek-usbcalfile.c backend/plustek-usbhw.c: Fixed calibration cache issues (coarse calibration for all modes, correct fine calibration for sheetfed devices). 2006-06-08 Mattias Ellert * po/sane-backends.*.po: updated with new translation keys from genesys backend. 2006-06-07 Pierre Willenbrock * backend/genesys.c backend/genesys.h backend/genesys_low.h: added options for enabling/disabling horizontal interpolation at high resolutions and selection of color filter in grayscale and lineart modes. * backend/genesys_gl841.c: implemented color filter selection. 2006-06-07 Gerhard Jaeger * configure, configure.in, backend/Makefile.in include/sane/sanei_access.h sanei/Makefile.in sanei/sanei_access.c: Added patch created by Vitaly Lipatov : Add --with-lockdir to configure for set path to lock dir Set PATH_SANE_LOCK_DIR really to lock dir (instead localstatedir) * po/sane-backends.de.po: Update. 2006-06-06 m. allan noah * backend/fujitsu.[ch]: update to v1.0.30, init more models, M3091/2 duplex support, flatbed bug fixes, minor refactoring * doc/descriptions/fujitsu.desc: status and version update * doc/sane-fujitsu.man: remove note about broken M3091/2 duplex 2006-06-06 Rene Rebe * backend/avision.c backend/avision.h backend/avision.conf.in doc/descriptions/avision.desc doc/sane-avision.man: updated the Avision backend (Build 182 -> 201), including a lot fixes, some new features and support for a bunch of new devices and updates to the status marks, including: control of the lamp power-save time, retrieval of NVRAM data such as the scan counts and serial number, correction of the quality vs. preview scan bit (was inverted), fixes for the logic deciding whether the cached window parameters are valid, yet another deinterlacing method (for new scanner models), rewritten main loop to be able to read with larger USB buffers for enhanced scan speed, fixes of BGR -> RGB mirroring (was off-by-one), attaching without a config by probing all known SCSI vendor/product IDs and some fixed typos on the way 2006-06-06 Mattias Ellert * po/Makefile.in, po/sane-backends.*.po: updated with translation keys from stv680 backend * backend/Makefile.in: Fix stv680 dependencies 2006-06-05 Gerard Klaver * backend/stv680.c backend/stv680.h backend/stv680.conf.in added * doc/sane-stv680.man doc/descriptions/stv680.desc added * AUTHORS: added stv680 Gerard Klaver * configure configure.in doc/Makefile.in doc/sane.man backend/Makefile.in backend/dll.conf.in: added stv680 backend * doc/descriptions-external/stv680.desc: removed because the backend is now a part of sane-backends. 2006-06-04 m. allan noah * backend/fujitsu.[ch]: update to v 1.0.29, option cleanups, M3091/2 color mode support, sloppy buffer supports saned/scanimage -T * backend/fujitsu-scsi.c: proper casting in macro * doc/descriptions/fujitsu.desc: status and version update * doc/sane-fujitsu.man: remove note about broken M3091/2 color 2006-06-03 Wittawat Yamwong * backend/pixma_sane_options.c: Added SANE_I18N for "Flatbed" and "Color" * backend/pixma_common.[ch] backend/pixma_mp150.c backend/pixma_mp730.c backend/pixma_mp750.c backend/pixma_rename.h backend/pixma.h: Added pixma_get_device_status() * backend/pixma.c: Fixed a bug in sane_open(). State wasn't initiallized properly. * doc/sane-pixma.man doc/descriptions/pixma.desc: Updated device status. 2006-06-01 m. allan noah * backend/fujitsu.[ch]: update to v 1.0.28 * doc/descriptions/fujitsu.desc: status and version update 2006-05-30 m. allan noah * backend/fujitsu.c: DBG speedup from abel deuring, duplex bugfix, split scsi/usb data into new DBG level, stop including or calling sanei_thread * doc/descriptions/fujitsu.desc: minor update * doc/sane-fujitsu.man: minor text changes, note DBG levels 2006-05-28 Gerhard Jaeger * doc/sane-plustek_pp.man: Fixed typo. * doc/sane-plustek.man: Update. * doc/descriptions/plustek.desc: Update. * backend/plustek.c backend/plustek.h backend/plustek-usb.c backend/plustek-usbcal.c backend/plustek-usbcalfile.c backend/plustek-usbdevs.c backend/plustek-usb.h backend/plustek-usbhw.c backend/plustek-usbimg.c backend/plustek-usbio.c backend/plustek-usbmap.c backend/plustek-usbscan.c backend/plustek-usbshading.c: Added support for CIS-based sheetfed scanners, namely TravelScan662. Added support for saving calibration data for CIS devices - should speedup CanoScan devices. 2006-05-28 Wittawat Yamwong * backend/pixma.h: Added #define ENODATA and EPROTO to let the backend compiles under OS/2 with gcc 3.3.5. 2006-05-27 Giuseppe Sacco * Updated italian translation 2006-05-27 Mattias Ellert * tools/sane-desc.c: Added plist output 2006-05-26 Mattias Ellert * po/Makefile.in, po/sane-backends.*.po: updated with translation keys from pixma backend 2006-05-26 Wittawat Yamwong * AUTHORS: added Wittawat Yamwong * configure configure.in doc/Makefile.in doc/sane.man doc/descriptions/pixma.desc backend/Makefile.in backend/dll.conf.in backend/pixma*.[ch]: added pixma backend * doc/descriptions-external/pixma.desc: removed because the backend is now a part of sane-backends. * sanei/sanei_usb.c: Modified sanei_read_int(): call usb_clear_halt() if and only if the endpoint is halted. 2006-05-24 Mattias Ellert * Updated Swedish translation 2006-05-23 m. allan noah * backend/fujitsu.c: don't send scsi cmd F1 if unsupported. * doc/descriptions/fujitsu.desc: update version number. 2006-05-23 Gerhard Jaeger * doc/plustek/Makefile.kernel26: Fixed extraction of version string. 2006-05-21 m. allan noah * backend/fujitsu.c backend/fujitsu.conf.in doc/descriptions/fujitsu.desc: added usb ids for fi-5110EOX 2006-05-21 Henning Meier-Geinitz * README.linux: Updated concerning USB permissions (really, this time). * doc/descriptions/unsupported.desc: Removed models that are now in pixma.desc. Updated comment for Plustek OpticPro S24. * doc/descriptions-external/hp_rts88xx.desc: Added reason for not including the backend. * backend/gt68xx.c backend/gt68xx_devices.c doc/gt68xx/gt68xx.CHANGES doc/descriptions/gt68xx.desc: Added support for Plustek OpticPro S24 (some models). Added .conf entry for Plustek OpticPro S12. 2006-05-19 m. allan noah * backend/fujitsu*: use sanei_scsi_open_extended() change config file var to "buffer-size", increase default * doc/sane-fujitsu.man: buffer-size mentioned 2006-05-18 Henning Meier-Geinitz * doc/descriptions-external/pixma.desc: Added some models (from Wittawat Yamwong ). 2006-05-14 m. allan noah * backend/fujitsu.c: sane_read() - only send EOF with len=0 do_cmd() - simplify timeout handling * backend/fujitsu-scsi.h: increase default timeoutes 2006-05-15 Stephane Voltz * backend/genesys_devices.c backend/genesys_gl646.c: tuned HP 2300 geometry description and added a safeguard against failed origin detection 2006-05-14 m. allan noah * backend/fujitsu*: rewritten backend, supports many more scanners with much better usb support and less model-specific code. * doc/sane-fujitsu.man: updated to match new backend. 2006-05-12 m. allan noah * doc/descriptions/fujitsu.desc: updated with all known scanners by fujitsu. Most now supported as 'basic'. 2006-05-10 Henning Meier-Geinitz * tools/hotplug-ng/convert-usermap.sh tools/udev/convert-usermap.sh: Removed. No longer needed as hotplug-ng and udev files are generated by sane-desc now. * tools/README tools/hotplug/README tools/hotplug-ng/README doc/sane-usb.man: Updated documentation concerning udev. * doc/descriptions/unsupported.desc: Added Epson Stylus CX-5800 and some PLANon scanners. 2006-05-06 Henning Meier-Geinitz * doc/descriptions/lexmark.desc: X1150 is reported to work. * doc/descriptions-external/brother.desc: MFC-3820CN is reported to work. 2006-04-27 Henning Meier-Geinitz * doc/descriptions-external/brother.desc doc/descriptions-external/brother2.desc: Network models are now supported (bug #303386). 2006-04-23 Pierre Willenbrock * backend/genesys_devices.c: Increase length of acceleration slopes for Canon LiDE 35/40/50/60 2006-04-21 Stephane Voltz * backend/genesys.c: add workraound with issue related to asic reset and data scan amount 2006-04-20 Henning Meier-Geinitz * backend/artec_eplus48u.conf.in: Made config file more readable. Instead of commenting every single line, the explanations are given once at the top. Don't repeat options that aren't changed anyway. * doc/descriptions/unsupported.desc: Added Microtek Scanmaker 3880. 2006-04-18 Giuseppe Sacco * Updated italian translation 2006-04-18 Pierre Willenbrock * backend/genesys_gl841.c backend/genesys_gl646.c: Actually use the endian converted slope table. * backend/genesys_conv_hlp.c: Fix interpolation code to emit all pixels of a line. * doc/descriptions/unsupported.desc: Added missing scanners from libsane.usermap. 2006-04-18 Henning Meier-Geinitz * doc/descriptions/epson.desc doc/descriptions/fujitsu.desc doc/descriptions/hp.desc doc/descriptions/u12.desc doc/descriptions/umax.desc: Added most USB vendor and product ids. Minor cleanup. * doc/descriptions/avision.desc: Used avision-desc.sh to update the .desc file to current SANE CVS avision.c code. Added USB ids. * doc/descriptions/plustek.desc: Added USB ids. Split some entries which have two different USB ids. Clarified comment about UMAX 3400. * tools/hotplug/libsane.usermap: Removed. Will be created automatically by sane-desc. * tools/Makefile.in: Create libsane.usermap (for hotplug), libsane.db (for hotplug-ng) and libsane.rules (for udev) automatically with sane-desc based on the :usbid tokens in the description files. * doc/Makefile.in: Don't print warnings when creating the HTML scanner tables. The warnings are already printed in the tools directory. * doc/descriptions-external/brother.desc doc/descriptions-external/brother2.desc: Point to current lists on Brother website. * tools/hotplug/.cvsignore tools/hotplug-ng/.cvsignore tools/udev/.cvsignore: Added. 2006-04-17 Henning Meier-Geinitz * backend/umax1220u-common.c backend/umax1220u.c doc/descriptions/umax1220u.desc doc/sane-umax1220u.man: Make UMAX 2100U (and probably 1600U and 2000U) work. Code from Patrick Lessard . Updated manual page and .desc file. * AUTHORS: Added Patrick Lessard and Pierre Willenbrock. * doc/descriptions/unsupported.desc doc/descriptions-external/pixma.desc: Moved scanners supported by the external pixma backend to pixma.desc. Added Canon PIXMA MP730. * doc/sane-mustek_usb2.man: Removed description of non-existing configuration file. 2006-04-12 Julien Blache * tools/sane-desc.c: Fix/optimize udev rules; tests on the same line are ANDed together, not ORed together, causing the rules to trigger more often than necessary. 2006-04-09 Henning Meier-Geinitz * frontend/saned.c: If saned does not find any config file, it calls fclose with a NULL FILE*. The code may also leak open file descriptors and FILE's if multiple config files are found. (bug #303339). Fixed possible segfault on close_handle() (bug #303338). * doc/descriptions/umax1220u.desc: Added links to Patrick Lessard's patch. * backend/qcam.c: Fixed undefined symbols in the qcam backend on GNU/kFreeBSD (patch from Aurelien Jarno ). * doc/descriptions/unsupported.desc: Added Lexmark X5150. 2006-04-03 Henning Meier-Geinitz * doc/descriptions-external/brother2.desc: Added several scanners from Brother website. * doc/descriptions/unsupported.desc: Added C-Channel scanners. * tools/hotplug/libsane.usermap: Added some scanners. 2006-03-29 Henning Meier-Geinitz * backend/sp15c.c: Fixed ADF support. Patch from Andreas Degert . * doc/descriptions-external/cs3200f.desc: Listed new cs3200f backend for Canon 3200F. * doc/descriptions/unsupported.desc: Added Canon PIXMA760, Tevion MD 90090, Microtek ScanMaker 4600, and Visioneer Onetouch 9320. Removed Canon CanoScan 3200F (now in external backends). 2006-03-27 Pierre Willenbrock * backend/genesys.c backend/genesys.h backend/genesys_low.h backend/genesys_conv.c backend/genesys_gl841.c: Added extended option "Threshold" for lineart mode(Thanks to Laurent Charpentier). 2006-03-24 Julien Blache * doc/*: Another batch of spelling fixes from A. Costa. 2006-03-24 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Iris Iriscan and Xerox Documate 510. 2006-03-21 Julien Blache * doc/*.man: More spelling fixes from A. Costa (through Debian). 2006-02-02 Oliver Schwartz * backend/snapscan-scsi.c: Limit inquiry length to 120 bytes if firmware is not yet loaded 2006-03-18 Julien Blache * doc/sane-apple.man: Applied patch from A Costa (through Debian); english/spelling fixes for sane-apple(5). 2006-03-16 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Microtek ScanMaker 4850 II and Plustek OpticPro ST 64+. 2006-03-12 Stephane Voltz * backend/genesys_gl646.c: removed now unneeded #ifdef in gl646_send_gamma_table. 2006-03-12 Pierre Willenbrock * backend/genesys_gl646.c: fixed silly thinko in gl646_send_gamma_table. 2006-03-12 Stephane Voltz * backend/genesys_gl646.c: fixed gl646_send_gamma_table after endianness changes. Put again a little fix for power saving 2006-03-11 Henning Meier-Geinitz * sanei/sanei_usb.c tools/sane-find-scanner.c: Added USB support for DragonFlyBSD (bug #303183). 2006-03-09 Pierre Willenbrock * backend/genesys.c backend/genesys_low.h: Removed little endian conversions in slope table creation functions * backend/genesys_gl646.c backend/genesys_gl841.c: Added little endian conversions to gl*_send_slope_table functions 2006-03-09 Pierre Willenbrock * backend/genesys.c: Some annotations about endianness of scanned data * backend/genesys_gl646.c: Convert gamma tables to little endian 2006-03-06 Stephane Voltz * backend/genesys_gl646.c: power saving fixes 2006-03-05 Henning Meier-Geinitz * doc/descriptions-external/samsung.desc: Added USB ids for SCX-4100 (bug #303166). * doc/descriptions/unsupported.desc: Added Brother MFC 9600, Canon PIXMA MP450, and Plustek ScanCopy 115. 2006-02-16 Gerhard Jaeger * doc/plustek/Plustek-PARPORT.changes: Update. * backend/plustek_pp.c: Bumped build number. * backend/plustek-pp_ptdrv.c backend/plustek-pp_detec.c: Fixed bug, that prevents backend from working, when the device is another parport than parport0 - spotted by Christoph Steinbruchel. 2006-02-14 Gerhard Jaeger * doc/u12/U12.changes: Update. * backend/u12.c: Bumped build number. * backend/u12-hw.c: SoftwareReset is now disabled in cancelSequence when the device is a Genius scanner. 2006-02-13 Stephane Voltz * backend/genesys_gl646.c: disabled power saving for gl646. 2006-02-12 Henning Meier-Geinitz * README.solaris: Added link to installation report. * doc/descriptions/unsupported.desc: Removed HP ScanJet 8200 (supported by Avision backend). Minor updates. 2006-02-04 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Removed Trust Office Scan USB 19200 (now in niash.desc), added Xerox 4800 One Touch. * include/sane/sanei_usb.h sanei/sanei_usb.c: Workaround for compilation problem on Windows/cygwin. * backend/dll.c: With cygwin, use ":" as DIR_SEP (patch from Philip Aston ). 2006-02-04 Ullrich Sigwanz * backend/niash_xfer.c: Added Trust OfficeScan 19200 * doc/descriptions/niash.desc: Added Trust OfficeScan 19200 * backend/niash.c: Removed Reload on X,Y settings 2006-02-02 Oliver Schwartz * backend/snapscan.h: Corrected USB ID for Benq 310 2006-01-31 Gerhard Jaeger * doc/plustek/Plustek-USB.changes: Update. * backend/plustek.c: Bumped build number. * backend/plustek-usbdevs.c: Fixed CanoScan N1220U settings. 2006-01-29 Henning Meier-Geinitz * doc/descriptions-external/brother2.desc: Changed status of Brother MFC-410CN to "basic" (bug #302961). * doc/descriptions/unsupported.desc: Added Benq 7400UT and Genius Colorpage CS. 2006-01-26 Henning Meier-Geinitz * backend/snapscan-usb.c: Added #defines for SHM_R/W for cygwin (patch from Philip Aston ). * AUTHORS: Troy Rollo is now active. 2006-01-22 Henning Meier-Geinitz * doc/descriptions-external/epkowa.desc: Updated to match iscan-1.18.0. Added usb ids (patch from Olaf Meeuwissen ). * doc/descriptions/unsupported.desc: Added Scanshell 800N and Canon Smartbase MP360. * sanei/sanei_usb.c tools/sane-find-scanner.c: Added checks for altsetting =! 0. 2006-01-22 Mattias Ellert * backend/dll.c, backend/plustek-usbhw.c, backend/plustek-usbscan.c, sanei/sanei_pio.c: Fixed warnings about C++ style comments. * backend/hp5400_xfer.h: Fixed compilation warnings. 2006-01-21 Mattias Ellert * backend/canon-sane.c: Fixed variable redeclaration. 2006-01-05 Stephane Voltz * tools/hotplug/libsane.usermap: Added Dell A920 (rebranded X1100) 2006-01-19 Gerhard Jaeger * doc/plustek/Plustek-USB.changes: Update. * backend/plustek.c: Bumped build number. Fixed constraint_type for OPT_BUTTON. * backend/plustek-usbdevs.c: Added high-speed setting for HP2200. Cleanup. 2006-01-18 Gerhard Jaeger * doc/plustek/Plustek-USB.changes: Update. * backend/plustek.c: Bumped build number. * backend/plustek-usbdevs.c: Fixed CanoScan N670U settings, see (bugreport #302738). 2006-01-15 Henning Meier-Geinitz * doc/descriptions/canon630u.desc doc/descriptions/coolscan2.desc doc/descriptions/hp5400.desc doc/descriptions/niash.desc doc/descriptions/unsupported.desc: Added USB vendor and product ids. Minor cleanup. * backend/artec_eplus48u.conf.in doc/sane-artec_eplus48u.man doc/descriptions/artec_eplus48u.desc: Added Yakumo Scan50. Minor cleanup. * doc/descriptions/gt68xx.desc: Added Mustek BearPaw 1248 CU. * doc/descriptions/lexmark.desc: Added USB vendor and product ids. Changed status of X1150 and X1170 according to reports on sane-devel. 2006-01-15 Ulrich Deiters * backend/canon-sane.c: fixed byte-swapping problem for little-endian platforms (affects raw-mode scanning with FS2710 slide scanner) 2006-01-14 Henning Meier-Geinitz * tools/check-usb-chip.c: Improved rts8822l-01h chipset detection by adding check for descriptor.bcdUSB == 0x110 reported by some scanners and accepting any value different to 0, read from 0xfe11 register. Patch from Jonathan Bravo Lopez . * tools/sane-desc.c: Added "ignore" parameter to :usbid token. Print warning if a backend defines USB models without defining usbid. Print warning if a model doesn't have an interface. * doc/descriptions.txt: Added "ignore" parameter to :usbid token. * doc/descriptions-external/hpaio.desc doc/descriptions-external/hpoj.desc doc/descriptions-external/samsung.desc: Updated comments. Added usb ids or "ignore". 2006-01-10 Oliver Schwartz * doc/descriptions/snapscan.desc: Changed entries for duplicate USB IDs, added Epson Stylus CX-1500 * backend/snapscan.c backend/snapscan-scsi.c: Added support for 12 bit gamma tables for Epson CX-1500 * backend/snapscan.conf.in: Added Epson Stylus CX-1500 2006-01-10 Gerard Klaver * doc/descriptions-external/stv680.desc: Added USB id. added Creative webcam to list 2006-01-10 Henning Meier-Geinitz * doc/descriptions-external/epkowa.desc: Film area guide mode of 4990 not supported (bug #302728). * doc/descriptions-external/brother-mfc4600.desc doc/descriptions-external/brother.desc doc/descriptions-external/brother2.desc doc/descriptions-external/hp3770.desc doc/descriptions-external/hp3900.desc doc/descriptions-external/hp8200.desc doc/descriptions-external/hp_rts88xx.desc doc/descriptions-external/mustek_a3p1.desc doc/descriptions-external/primascan.desc doc/descriptions-external/template.desc. doc/descriptions-external/viceo.desc: Added several USB ids. Added some more scanners. General cleanup. 2006-01-09 Henning Meier-Geinitz * doc/descriptions/gt68xx.desc: Added USB id. * doc/descriptions/unsupported.desc: Added interfaces to Panasonic scanners. 2006-01-08 Oliver Schwartz * doc/descriptions/snapscan.desc: Added USB IDs 2006-01-08 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Xerox 4800. * doc/descriptions.txt tools/sane-desc.c: Added support for keyword :usbid. All output modes now print the usb vendor and product modes. Added usermap, db, and udev output modes for generating USB vendor/product lists for hotplug (ng) and Linux udev. Added version information to html modes. * doc/descriptions/gt68xx.desc: Added vendor and product ids for all scanners. * doc/descriptions/artec_eplus48u.desc doc/descriptions/genesys.desc doc/descriptions/hp3500.desc doc/descriptions/hp4200.desc doc/descriptions/ma1509.desc doc/descriptions/microtek2.desc doc/descriptions/mustek_usb.desc doc/descriptions/mustek_usb2.desc doc/descriptions/sm3600.desc doc/descriptions/sm3840.desc doc/descriptions/template.desc. doc/descriptions/umax1220u.desc doc/descriptions/unsupported.desc doc/descriptions-external/template.desc.: Added USB vendor and product ids. Minor cleanups. 2006-01-07 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Medion MD 85264 finger print sensor and Trust Office Scan USB 19200. * doc/descriptions-external/brother2.desc tools/hotplug/libsane.usermap: Brother DCP-7010 is reported to work (bug #302862). * tools/check-usb-chip.c: Print version information when detecting chip sets. * sanei/sanei_usb.c: Added warning if libusb support is not built in and some more debug messages. * doc/descriptions-external/hp3900.desc: Status of HP ScanJet 4070 Photosmart is now minimal. * tools/sane-desc.c: Added new output mode: statistics. This mode prints overal statistics about known devices. Added support for more than one search directory. Removed support for deprecated backend status ("alpha", "beta" ...). Ran indent -gnu. HTML output (backends and mfgs) prints statistics now. Unify status and device type strings. Simplified status and device type string printing. Removed any remaining links of backend status. 2006-01-06 Oliver Schwartz * backend/snapscan-scsi.c backend/snapscan-options.c backend/snapscan.c: Some fixes for the Epson Stylus CX1500 2006-01-05 Stephane Voltz * backend/umax_pp_low.c: removed unused local variable 2006-01-05 Henning Meier-Geinitz * backend/Makefile.in backend/abaton.conf backend/abaton.conf.in backend/agfafocus.conf backend/agfafocus.conf.in backend/apple.conf backend/apple.conf.in backend/artec.conf backend/artec.conf.in backend/artec_eplus48u.conf backend/artec_eplus48u.conf.in backend/avision.conf backend/avision.conf.in backend/bh.conf backend/bh.conf.in backend/canon.conf backend/canon.conf.in backend/canon630u.conf backend/canon630u.conf.in backend/canon_pp.conf backend/canon_pp.conf.in backend/coolscan.conf backend/coolscan.conf.in backend/coolscan2.conf backend/coolscan2.conf.in backend/dc210.conf backend/dc210.conf.in backend/dc240.conf backend/dc240.conf.in backend/dc25.conf backend/dc25.conf.in backend/dll.conf backend/dll.conf.in backend/dmc.conf backend/dmc.conf.in backend/epson.conf backend/epson.conf.in backend/fujitsu.conf backend/fujitsu.conf.in backend/genesys.conf backend/genesys.conf.in backend/gphoto2.conf backend/gphoto2.conf.in backend/gt68xx.conf backend/gt68xx.conf.in backend/hp.conf backend/hp.conf.in backend/hp4200.conf backend/hp4200.conf.in backend/hp5400.conf backend/hp5400.conf.in backend/hpsj5s.conf backend/hpsj5s.conf.in backend/ibm.conf backend/ibm.conf.in backend/leo.conf backend/leo.conf.in backend/lexmark.conf backend/lexmark.conf.in backend/ma1509.conf backend/ma1509.conf.in backend/matsushita.conf backend/matsushita.conf.in backend/microtek.conf backend/microtek.conf.in backend/microtek2.conf backend/microtek2.conf.in backend/mustek.conf backend/mustek.conf.in backend/mustek_pp.conf backend/mustek_pp.conf.in backend/mustek_usb.conf backend/mustek_usb.conf.in backend/nec.conf backend/nec.conf.in backend/net.conf backend/net.conf.in backend/pie.conf backend/pie.conf.in backend/plustek.conf backend/plustek.conf.in backend/plustek_pp.conf backend/plustek_pp.conf.in backend/qcam.conf backend/qcam.conf.in backend/ricoh.conf backend/ricoh.conf.in backend/s9036.conf backend/s9036.conf.in backend/saned.conf backend/saned.conf.in backend/sceptre.conf backend/sceptre.conf.in backend/sharp.conf backend/sharp.conf.in backend/sm3840.conf backend/sm3840.conf.in backend/snapscan.conf backend/snapscan.conf.in backend/sp15c.conf backend/sp15c.conf.in backend/st400.conf backend/st400.conf.in backend/tamarack.conf backend/tamarack.conf.in backend/teco1.conf backend/teco1.conf.in backend/teco2.conf backend/teco2.conf.in backend/teco3.conf backend/teco3.conf.in backend/test.conf backend/test.conf.in backend/u12.conf backend/u12.conf.in backend/umax.conf backend/umax.conf.in backend/umax1220u.conf backend/umax1220u.conf.in backend/umax_pp.conf backend/umax_pp.conf.in backend/v4l.conf backend/v4l.conf.in: Renamed backend configuration files from *.conf to *.conf.in. The configuration files are preprocessed by backend/Makefile now and variables like @DATADIR@ are substituted by the appropriate directories. This is especially useful for firmware locations. For the firmware paths for the artec_eplus48u, gt68xx, and snapscan backends variables were used. Bug #302590. Regenerate .conf files only if .conf.in were changed. * backend/.cvsignore: Added *.conf. * README.linux: Mentioned udev. * doc/backend-writing.txt: New chapters about the build system and the files and directories of sane-backends. Fixed building instructions for tstbackend. Mentioned that sane_* symbols should be only used for API symbols. * testsuite/Makefile.in testsuite/README testsuite/testfile.pnm: Changed testsuite to use the test backend instead of the pnm backend. This actually works with current sane-backends. * PROBLEMS: Removed pnm problem description. It's not relevant for normal users and explained in the sane-pnm man page anyway. * backend/genesys.c: Fixed some compilation warnings. * tools/hotplug/libsane.usermap: Added Hewlett-Packard PSC 1210. 2006-01-03 Henning Meier-Geinitz * doc/sane.tex: Corrected formula for calculation of bytes_per_line in chapter 4.3.8 sane_get_parameters. New version: 1.04. 2006-01-02 Henning Meier-Geinitz * backend/microtek2.c backend/microtek2.h: Fixed problem with option "resolution" which exists twice at the same time. Bug #302466. * tools/hotplug/libsane.usermap: Added Canon LiDE 60 (bug #302830). * backend/gt68xx.c backend/gt68xx.conf backend/gt68xx_devices.c backend/gt68xx_high.c doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES doc/gt68xx/gt68xx.TODO: Added Plustek OpticPro S(T)12 (product id 0x040b). Mostly untested. Print warmup message 5 seconds after starting the warm-up. * doc/descriptions/unsupported.desc: Added Visioneer 9450 USB. Updated comments for Plustek S(T)12. * backend/test.c backend/test.conf backend/test.h doc/sane-test.man doc/descriptions/test.desc: Added option "source" which can be used to simulate an Automatic Document Feeder (ADF). Added copyright header to test.h. * frontend/scanimage.c: Don't create and delete a file in batch mode if the document feeder is empty or if CTRL-D is pressed in prompt mode. This change avoids deleting existing files. For an explanation, see bug #302797. 2006-01-01 Oliver Schwartz * backend/snapscan-data.c backend/snapscan-options.c backend/snapscan.c doc/descriptions/snascan.desc: Added (static) calibration data for Benq 5150 / 5250 * backend/snapscan.h backend/snapscan.conf: Added preliminary support for Epson Stylus CX 1500 * backend/Makefile.in: Added snapscan-data.c 2005-12-29 Henning Meier-Geinitz * doc/descriptions-external/scanwit.desc: Added "(Acer)" to manufacturer name (bug #302801). * doc/descriptions/unsupported.desc: Added/updated several old Acer (Benq) and Agfa parport scanners. 2005-12-28 Henning Meier-Geinitz * tools/hotplug/libsane.usermap: Added Brother MFC 8440. * Makefile.in: make libcheck now also checks for non standard sane symbols (sane_*). * doc/descriptions/unsupported.desc: HP Scanjet 3690 uses GL646_HP chipset. 2005-12-27 Henning Meier-Geinitz * README.linux: Mention ld.so.conf issue. * doc/descriptions/unsupported.desc doc/descriptions-external/hp3900.desc: Added external hp3900 backend. Moved (possibly) supported scanners from unsupported.desc. * doc/backend-writing.txt: Added some hints about what to do to add a new backend to the existing sane-backends code. 2005-12-24 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Lexmark X2330. 2005-12-23 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added Canon Pixma MP500. * README.openbsd: Added hint about CPPFLAGS. * doc/descriptions/artec.desc: Changed status of Artec AT6 to "good" as reported in bug #302749. * tools/hotplug/libsane.usermap: Added Brother MFC-7420. 2005-12-20 Henning Meier-Geinitz * backend/dll.c doc/descriptions/dll.desc: Fixed dynamic loading on current HP/UX systems (bug #302732). * AUTHORS ChangeLog configure configure.in backend/Makefile.in backend/dll.conf backend/hp3500.c doc/Makefile.in doc/sane-hp3500.man doc/sane.man doc/descriptions/hp3500.desc: Added hp3500 backend which supports the HP ScanJet 3500 series, including the 3500C, 3530C and 3570C (patch from Troy Rollo ) (bug #302687). * doc/descriptions-external/hp3500.desc: Deleted. * doc/descriptions/hp4200.desc doc/descriptions/lexmark.desc doc/descriptions/mustek_usb2.desc: Removed "NEW!" markers. 2005-12-18 Henning Meier-Geinitz * Makefile.in: List ChangeLog-1.0.17 in DISTFILES. * configure configure.in: Changed version to 1.0.17-cvs. Enabled compilation warnings. * doc/descriptions-external/brother2.desc: Added Brother MFC-215C. Older entries can be found in ChangeLog-1.0.17. backends-1.3.0/ChangeLogs/ChangeLog-1.0.19000066400000000000000000001303711456256263500176200ustar00rootroot00000000000000****** Release of sane-backends 1.0.19. End of code freeze ****** 2008-02-10 m. allan noah * Makefile.in: correct DISTFILES * configure.in, configure: alpha sort backend names * backend/Makefile.in: correct/sort DISTFILES and .la: targets * include/Makefile.in: correct/sort SANE_INCLUDES * doc/releases.txt: minor updates 2008-02-10 m. allan noah * config.guess, config.sub: updated to latest versions * configure.in, configure: updated sane version number * doc/releases.txt: minor updates * sane-backends.lsm: updated maintainer and keyword info * NEWS: added last 18+ months of updates to 1.0.19 section 2008-02-09 m. allan noah * backend/hpljm1005.c, doc/descriptions/hpljm1005.desc: add usb ID for LaserJet M1120 2008-02-09 Rene Rebe * backend/avision.ch: fixed device list matching for entries with partial matches on USB ID, but vendor / product string matches as mostly only the HP5300 vs. HP5370, fixed non-color calibration for devices not filling all RGB fields of calibration format information, do not send 3x3 color matrix to older devices (ASIC versions) as some HP53xx does not correctly handle it, improved calibration accuracy and fixed transparency adapter detection to not use non-zero as present, but just 1 (some devices now set 0xff as (-1) - not present ... 2008-02-07 m. allan noah * backend/hpljm1005.c, doc/descriptions/hpljm1005.desc: add usb ID for LaserJet M1120n * doc/descriptions-external/brother2.desc: add usb ID for DCP-120C 2008-02-03 m. allan noah * backend/hpljm1005.c: better sane_cancel handling * backend/agfafocus.c backend/coolscan.c, backend/coolscan2.c, backend/hpljm1005.c, backend/ibm.c, backend/lexmark_low.c, backend/microtek.c, backend/microtek2.c, backend/nec.c, backend/pie.c, backend/ricoh.c, backend/s9036.c, backend/st400.c, backend/umax.c: reduce gcc pedantic/ansi warnings from 690 to 280 lines, mostly via casts in str*() calls. 2008-02-03 Mattias Ellert * Makefile.in, */Makefile.in, tools/sane-config.in: Fix warnings about ignored --datarootdir * backend/hp5590_low.c: Fix incompatible pointer type warning * backend/genesys_gl646.c: Fix incompatible pointer type warning * backend/dc25.c, backend/coolscan2.c, backend/epson2.c, backend/epson2-commands.c, backend/epson2-io.c, backend/epson2_net.c, backend/genesys.c, backend/genesys_gl841.c, backend/hp3900_usb.c, backend/lexmark_low.c, backend/plustek-usbhw.c, backend/sm3840.c: Fix format warnings * backend/pixma.c: Add missing #include 2008-02-02 Mattias Ellert * configure, configure.in, include/sane/config.h.in, backend/hp3900_debug.c: make tiffio.h optional * backend/Makefile.in: remove sanei_config2 from epjitsu deps * backend/epson2.c, backend/hpljm1005.c: add missing #includes 2008-02-01 Gerhard Jaeger * po/sane-backends.no.po: renamed to sane-backends.nb.po * po/Makefile.in: norwegian bokmål locale is nb and not no 2008-01-30 Mattias Ellert * backend/pixma.c: Fix initialization of the reader taskid 2008-01-30 m. allan noah * backend/hpljm1005.c: call sane_get_devices if required 2008-01-29 m. allan noah * backend/snapscan.c: fix bug #310538 * doc/descriptions/unsupported.desc: add Canon LiDE 600F 2008-01-23 Alessandro Zummo * doc/descriptions/epson2.desc: cloned from epson.desc with minor modifications. 2008-01-22 m. allan noah * backend/hpljm1005.c, doc/descriptions-external/hpljm1005.desc: Update backend with DBG macro calls, remove external .desc file, from author- couriousous at mandriva dot org * backend/fujitsu.c: disable compression option arg until sane 1.1.0 * doc/descriptions/dell1600n_net|hp3500|pixma.desc: remove 'new' flag 2008-01-17 m. allan noah * backend/hpljm1005.c, doc/descriptions/hpljm1005.desc, doc/sane-hpljm1005.man, AUTHORS, configure, configure.in, backend/Makefile.in, backend/dll.conf.in, doc/Makefile.in, doc/sane.man: add hpljm1005 backend, couriousous at mandriva dot org * tools/check-po.awk, po/sane-backends.fr.po: updates from Yann E. MORIN 2008-01-16 m. allan noah * po/sane-backends.fr.po: updated translation from Yann E. MORIN * backend/fujitsu.c, backend/fujitsu.conf.in, doc/descriptions/fujitsu.desc: add usb id for S500M 2008-01-16 Jonathan Bravo Lopez * backend/hp3900_usb.c: fixed compilation warning related to an argument type in Read_Bulk function. 2008-01-14 m. allan noah * tools/check-po.awk: support different use of 'fuzzy' * po/sane-backends.fr.po: updated translation both updates from Yann E. MORIN 2008-01-09 Gerard Klaver * backend/teco2.c changed routine teco_request_sens add init value to size (bugreport https://bugzilla.novell.com/show_bug.cgi?id=205451) 2008-01-09 Alessandro Zummo * changed functions prefix (esci_ for device commands, e2_ for driver's functions), better handling of tpu area, fixed tpu detection, changed freeing of line buffers, fixed some XXXs. 2008-01-09 Alessandro Zummo * sanei_tcp.c, fix compilation with cygwin. 2008-01-02 m. allan noah * AUTHORS, configure, configure.in, backend/Makefile.in, backend/dll.conf.in, backend/hs2p-saneopts.h, backend/hs2p-scsi.c, backend/hs2p-scsi.h, backend/hs2p.c backend/hs2p.conf.in, backend/hs2p.h doc/Makefile.in, doc/sane-hs2p.man, doc/sane.man, doc/descriptions/hs2p.desc: add hs2p backend for jazz_johnson a t verizon d o t net 2007-12-29 m. allan noah * backend/epjitsu.c: let io_error fall thru usb command function * backend/epjitsu.conf.in: use @DATADIR@ for holding firmware * backend/Makefile.in: add epjitsu to FIRMWARE_DIRS 2007-12-29 m. allan noah * include/sane/sane.h, frontend/scanimage.c, backend/fujitsu.[ch]: commented/removed/deactivated all new SANE_FRAME code 2007-12-26 Alessandro Zummo * backend/epson2.c: added network scanner autodiscovery 2007-12-26 Alessandro Zummo * sanei/sanei_udp.c: added udp support functions 2007-12-24 Julien Blache * configure.in, configure: Do not build plustek_pp on Hurd. Patch from Samuel Thibault . Add missing closing paren to error message. 2007-12-21 Jonathan Bravo Lopez * backend/hp3900.c, backend/hp3900_rts8822.c: Fixed bug which made slide/negative scans unusable. * backend/hp3900_config.c: Changed area constrains of slide/negative scans. 2007-12-19 m. allan noah * backend/epjitsu.c: backend v1.0.10, fix missing function 2007-12-19 Jonathan Bravo Lopez * po/sane-backends.es.po: Updated translation. * tools/check-usb-chip.c: fixed compilation warning and "RTS8822L-01H" strings renamed to "RTS8822" 2007-12-19 Mattias Ellert * po/Makefile.in, po/sane-backends.*.po: New translation keys for hp3900 backend. Updated Swedish translation. 2007-12-17 m. allan noah * backend/epjitsu*, backend/Makefile.in, backend/dll.conf.in, doc/sane-epjitsu.man, doc/Makefile.in, doc/sane.man, doc/descriptions/epjitsu.desc, configure, configure.in, AUTHORS: add backend for Epson-based Fujitsu scanners (fi-60F and S300) 2007-12-17 Gerhard Jaeger * backend/plustek-usb.[ch] backend/plustek-usbcal.c backend/plustek-usbimg.c backend/plustek-usbio.c backend/plustek-usbshading.c: Fixed ARM/Xscale issues. * backend/plustek.c: Bumped build number * doc/plustek/Plustek-USB-TODO.txt doc/plustek/Plustek-USB.changes: Update 2007-12-15 Alessandro Zummo * backend/epson2.c: fixed model detection, depth detection and removed some unused variables. 2007-12-13 Jonathan Bravo Lopez * Added files for 'hp3900' backend which supports HP Scanjet 3800/3970/4070/4370/G3010 * doc/descriptions-external/hp3900.desc: moved to doc/descriptions 2007-12-10 Alessandro Zummo * backend/epson2.c: use epson2_model where possible, fixed segmentation fault. 2007-12-08 Giuseppe Sacco * Added esperanto translation, per Antonio Codazzi. 2007-11-23 Gerhard Jaeger * po/sane-backends.*.po: Update * backend/plustek-usb.[ch] backend/plustek-usbcal.c backend/plustek-usbdevs.c backend/plustek.[ch]: Tweaked TravelScan464 settings. Improved AFE gain calculation for CIS devices. This should avoid stripes in the scanned images. * doc/sane-plustek.man doc/plustek/Plustek-USB.changes: Update 2007-11-22 Pierre Willenbrock * backend/genesys.c, backend/genesys_devices.c, backend/genesys_low.h, backend/genesys_gl646.c, backend/genesys_gl841.c: add infrastructure for multiple motor power modes 2007-11-21 Pierre Willenbrock * backend/genesys_gl841.c: add internal flag for disabling lamp during scan(useful for black level calibration) 2007-11-18 Mattias Ellert * backend/mustek.c: Add protection for a double free (#306775) * backend/agfafocus.c, backend/artec_eplus48u.c, backend/avision.c, backend/coolscan.c, backend/hp3500.c, backend/microtek2.c, backend/mustek.c, backend/pie.c, backend/pixma.c, backend/plustek.c, backend/plustek_pp.c, backend/snapscan.c, backend/sp15c.c, backend/tamarack.c, backend/test.c, backend/u12.c, backend/umax.c: Fix handling of valid "negative" PIDs. 2007-11-18 Alessandro Zummo * backend/epson2.c: removed quick-format option. it's the job of a frontend to provide such a commodity. removed confusing parameters (preview-speed, speed). when a preview is requested, the scanner will be set to high speed (if possible). removed references to the never implemented zoom function. the function that shortened the list was actually missing the first entry. more resolutions added for networked scanners. added support for Perfection 4990 (Claus Boje). 2007-11-17 m. allan noah * backend/fujitsu.c, backend/fujitsu.conf.in, doc/descriptions/fujitsu.desc: add usb id for S510 2007-11-17 Rene Rebe * backend/avision.[ch]: Implemented support for latest Avision ASICs and features, including overscan, background raster, software scaling for cheaper ASICs used in HP scanners. Changed types used to more portable uint* ones, without underscore (_), removed in-file ChangeLog history, as the files became big enough. Implemented early calibration for easier handling of Avision-based film scanners and implemented support for 2-pass duplex scanners. 2007-11-16 Mattias Ellert * include/sane/sanei_thread.h, include/sane/sanei_usb.h: Fixing doxygen warnings. 2007-11-16 Gerhard Jaeger * backend/plustek-usb.[ch] backend/plustek-usbcal.c backend/plustek-usbdevs.c backend/plustek-usbshading.c backend/plustek.[ch] backend/plustek.conf.in: Tweaked TravelScan464 settings. Added possibility to disable dark-calibration with lamp on (touches devices like CanoScan1220 etc.) Use attribute packed for data access structs * doc/sane-plustek.man doc/plustek/Plustek-USB.changes: Update * include/sane/sanei_usb.h sanei/sanei_usb.c: Added function sanei_usb_get_descriptor() to retrieve some infos about a connected device 2007-11-16 Mattias Ellert * backend/artec_eplus48u.c, backend/coolscan.c, backend/mustek.c, backend/pie.c, backend/plustek.c, backend/plustek_pp.c, backend/snapscan.c, backend/test.c, backend/u12.c, backend/umax.c: Correct the test of the return value from sanei_thread_begin. 2007-11-12 Julien Blache * doc/descriptions-external/epkowa.desc: Update epkowa.desc for iScan! 2.10.0. Patch provided by Olaf Meeuwissen. 2007-11-11 Pierre Willenbrock * backend/genesys_gl841.c: add check for low brightness 2007-11-11 Pierre Willenbrock * backend/genesys.c, backend/genesys_gl646.c, backend/genesys_gl841.c, backend/genesys_low.h: change bulk_write_register to take number of registers instead of byte size of register set 2007-05-08 Mattias Ellert * acinclude.m4: NSLinkModule is deprecated in favour of dlopen - only look for NSLinkModule if dlopen is not available * acinclude.m4, m4/libtool.m4: include libtool.m4 using m4_include * ltmain.sh, m4/libtool.m4: newer versions. * configure.in: move the byteorder test to after AC_GNU_SOURCE et al. to avoid warnings when running autoconf, add AC_SUBST(CROSS_COMPILING), add a check for IOKit/scsi/SCSICommandOperationCodes.h, add a check for SCSITaskSGElement (for darwin 64 bit support) * doc/Makefile.in, tools/Makefile.in: disable things that need the built binaries to run when doing cross-compilations * sanei/sanei_scsi.c: IOKit/scsi-commands has moved to IOKit/scsi - support both locations of headers, use SCSITaskSGElement if available * aclocal.m4, config.guess, config.sub, configure, include/sane/config.h.in: update autogenerated files * backends/Makefile.in: remove sanei_config2 from cardscan deps * backends/dll.c: add MacOS X naming convention for dlopen * tools/sane-find-scanner.c: IOKit/scsi-commands has moved to IOKit/scsi - support both locations of headers, fix some warnings 2007-11-08 Gerhard Jaeger * doc/plustek/Plustek-USB.changes doc/sane-plustek.man: Update * backend/plustek.c: Bumped build number * doc/descriptions/unsupported.desc: Updated some entries * backend/plustek-usb.c backend/plustek-usbimg.c backend/plustek-usbmap.c backend/plustek-usbscan.c backend/plustek-usbshading.c: Fixed copyright * backend/plustek-usb.h backend/plustek-usbdevs.c doc/descriptions/plustek.desc: Added support for TravelScan464 2007-10-28 Julien Blache * doc/descriptions/epson.desc: Added the Epson DX-6000 (04b8:082e) based on user report. 2007-10-27 Julien Blache * backend/net.c: Fix IPv4 legacy code after last changes to the net backend on 2007-10-24. 2007-10-26 Julien Blache * backend/abaton.c, backend/agfafocus.c, backend/apple.c, backend/artec.c, backend/canon.c, backend/cardscan.c, backend/coolscan.c, backend/epson.c, backend/epson2.c, backend/fujitsu.c, backend/hp3500.c, backend/ibm.c, backend/pint.c, backend/ricoh.c, backend/s9036.c, backend/sm3600.c, backend/sp15c.c, backend/tamarack.c: OPT_NUM_OPTS must be of type SANE_TYPE_INT. 2007-10-25 Pierre Willenbrock * backend/genesys.c: Removed some more hardcoded sizeof(Genesys_Register_Set) 2007-10-25 Julien Blache * sanei/sanei_ab306.c: Fix boundary checking after the for() loop in sanei_ab306_open(). From Johannes Meixner at SuSE. * backend/as6e.c: Fix strncpy()/strncat() usage in check_for_driver(). From Johannes Meixner at SuSE. 2007-10-25 Pierre Willenbrock * backend/genesys_gl841.c: Hopefully removed the remaining hardcoded sizeof(Genesys_Register_Set) 2007-10-25 Pierre Willenbrock * backend/genesys_gl841.c: One more instance of sizeof(Genesys_Register_Set) vs 2 * backend/genesys.c: Add check for small register set in sanei_genesys_get_address 2007-10-24 Julien Blache * backends/net.c: Add an optional connection timeout for the initial connection to saned. Based on a patch from Ryan Duryea . Bump net backend version to 1.0.14. * backends/net.conf.in: Add the new connect_timeout option and adjust comments accordingly. * doc/sane-net.man: Document the connect_timeout option and the SANE_NET_TIMEOUT environment variable. 2007-10-19 Stephane Voltz * tools/check-usb-chip.c: added detection of rts8801 and rts8891 ASICs 2007-10-13 Pierre Willenbrock * backend/genesys_gl841.c backend/genesys_gl646.c: use sizeof(Genesys_Register_Set) instead of 2 2007-10-07 Bertrik Sikken * doc/descriptions/unsupported.desc: updated with info from 'FormularDaten' e-mails up to 2007/9/29 2007-10-02 Julien Blache * doc/descriptions-external/brother2.desc: add DCP-117C USB IDs and mark support as good, based on user report. 2007-10-01 Stephane Voltz * backend/lexmark.c backend/lexmark_low.c: fixed compilation warnings 2007-10-01 Stephane Voltz * doc/sane-lexmark.man doc/descriptions/lexmark.desc backend/Makefile.in backend/lexmark.c backend/lexmark_low.c backend/lexmark_sensors.c backend/lexmark_models.c backend/lexmark.conf.in: moved experimental version to current tree 2007-10-01 Stephane Voltz * doc/sane-umax_pp.man doc/descriptions/umax_pp.desc: added Genius ColorPage-Life Pro as supported scanner by the umax_pp backend 2007-09-28 Bertrik Sikken * doc/descriptions/unsupported.desc: updated with info from 'FormularDaten' e-mails from 2007/7/3 to 2007/7/30 2007-09-27 Julien Blache * doc/backend-writing.txt: fix typos, patch from . 2007-09-26 Julien Blache * doc/descriptions/unsupported.desc: Added pointer to http://code.google.com/p/kvss905c/ for the Panasonic KV-SS905C and Panasonic KV-S3105C scanners (note that the non-SANE driver at that URL supports other scanners of the KV-SS905C family). 2007-09-25 Bertrik Sikken * doc/descriptions/unsupported.desc: updated with info from 'FormularDaten' e-mails from 2007/5/1 to 2007/6/24 2007-09-23 Bertrik Sikken * doc/descriptions/unsupported.desc: updated with info from 'FormularDaten' e-mails from 2007/2/11 to 2007/4/30 2007-09-17 Gerhard Jaeger * doc/descriptions/unsupported.desc: #305009 removed UMAX3400, as it is supported by the Plustek backend 2007-08-28 Giuseppe Sacco * Italian translation update * Corrected a typo in backend/canon.c 2007-08-27 Stephane Voltz * backend/genesys.h backend/genesys_gl841.c backend/genesys_low.h backend/umax_pp.c backend/umax_pp.h backend/umax_pp_mid.c backend/umax_pp.h AUTHORS doc/sane-umax_pp.man doc/sane-genesys.man: mail address update, minor man update 2007-08-26 Stephane Voltz * backend/genesys.c backend/genesys_gl646.c backend/genesys_devices.c: HP2400 warmup fix by Luke 2007-08-19 Henning Geinitz * backend/gt68xx.c backend/gt68xx.conf.in backend/gt68xx_devices.c backend/gt68xx_generic.c backend/gt68xx_gt6801.c backend/gt68xx_gt6816.c backend/gt68xx_high.c backend/gt68xx_high.h backend/gt68xx_low.c backend/gt68xx_low.h backend/gt68xx_mid.c doc/sane-gt68xx.man doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Added Artec Ultima 2000 e+, Nortek Myscan 1200, NeatReceipts Scanalizer Professional, Genius Colorpage Slim-1200. Name and email address changes. * AUTHORS: Name and email address change. 2007-08-18 Julien Blache * doc/descriptions/epson.desc: Added Stylus CX-5000 (04b8:082b). * backend/epson_usb.c: Added various USB IDs for CX-6000, DX-5050, DX-5000, CX-5000, DX-4050. 2007-08-12 Henning Geinitz * doc/descriptions-external/hpljm1005.desc: Added. 2007-08-08 m. allan noah * frontend/scanimage.c: bugfix: don't round up negative user values * doc/descriptions/fujitsu.desc: add/consolidate new models 2007-08-03 Julien Blache * doc/descriptions/epson.desc: add the Epson Stylus Photo RX-700 (04b8:0810), based on user report. Add the Epson Stylus CX-6600 (04b8:0813), based on user report. 2007-07-26 m. allan noah * frontend/scanimage.c: add default: blocks to FRAME switch code * backend/fujitsu.[ch]: update to version 1.0.52: - remove unused jpeg function - reactivate look-up-table based brightness and contrast options - change range of hardware brightness/contrast to match LUT versions - call send_lut() from sane_control_option instead of sane_start 2007-07-31 Julien Blache * doc/descriptions/epson.desc: add the Epson Stylys DX-5050 (04b8:082b). 2007-07-30 Julien Blache * doc/descriptions/epson.desc: add the Epson Stylus DX-4050 (04b8:082f), based on several reports. Add the Epson Stylus DX-5000 (04b8:082b) on the same grounds. * backend/canon.c: apply patch from Nils Philippsen, turning 3 logical AND into bitwise AND (SANE bug #304363). 2007-07-29 Julien Blache * tools/sane-desc.c: Add a RUN rule to the udev rules to automatically disable USB suspend for all known scanners. Works only with kernels >= 2.6.22 where /sys/bus/usb/devices/*/power/level exists. 2007-07-26 m. allan noah * backend/fujitsu.c: update to version 1.0.51, fix bug in jpeg code 2007-07-26 Julien Blache * tools/sane-desc.c: revert my last commit and rework the one before to produce a backward compatible udev rules file. Yay. 2007-07-26 Gerhard Jaeger * doc/plustek/Plustek-USB.changes: Update * backend/plustek.c: Bumped build number * backend/plustek-usbhw.c: Force output bit set on misc I/O, when lamp is switched * doc/descriptions/unsupported.desc: Updated some Plustek entries 2007-07-25 Julien Blache * tools/sane-desc.c: Update udev rules for use with Linux >= 2.6.22 and CONFIG_USB_DEVICE_CLASS=n. 2007-07-20 Wittawat Yamwong * backend/pixma.c backend/pixma.h backend/pixma_mp150.c doc/sane-pixma.man doc/descriptions/pixma.desc: upgraded to version 0.13.1 added PIXMA MP960 2007-07-15 m. allan noah * doc/descriptions/fujitsu.desc, doc/sane-fujitsu.man: update website and fi-60F status * Authors, backend/dll.conf.in: added cardscan backend 2007-07-14 m. allan noah * doc/sane-cardscan.man,doc/Makefile.in,doc/descriptions/cardscan.desc, backend/cardscan.*,backend/Makefile.in,configure.in: add new v1.0.0 backend for Corex CardScan 800c 2007-07-14 m. allan noah * doc/sane-fujitsu.man: add more known models, fix bug #304450 * backend/Makefile.in: libsane-fujitsu.la does not use sanei_thread.lo 2007-07-11 Gerhard Jaeger * tools/check-usb-chip.c: Try to distinguish the various GenesysLogic GeneScan ASICS - GL841, GL842 and GL843 2007-07-10 m. allan noah * backend/fujitsu.[ch], backend/fujitsu-scsi.h: update to v1.0.50, add JPEG support, usb command sending and scan params cleanups * include/sane/sane.h: add SANE_FRAME_JPEG * frontend/scanimage.c: don't crash on unknown frame types 2007-07-10 Gerhard Jaeger * doc/descriptions/plustek.desc: Fixed web entries * backend/plustek-usb.h backend/plustek-usbdevs.c backend/plustek.c: Added flag to allow only 1- and 8-bit scanmodes. The Q-Scan does not seem to support 14-bit modes. 2007-06-29 Gerhard Jaeger * backend/plustek-usb*.[ch] backend/plustek.[ch]: Added sheetfed device Q-Scan USB001 from Portable Peripherals Fixed Mustek Bearpaw and made some speedup (bugreports #304343 and #301763) Fixed calibration for senororders other that RGB * doc/descriptions/plustek.desc: Bumped version and added Q-Scan * doc/descriptions/unsupported.desc: Removed Q-Scan * doc/sane-plustek.man doc/plustek/Plustek-USB.changes: Update 2007-06-28 m. allan noah * backend/fujitsu.c: update to v1.0.49, fi-5750C usb ID and color mode * backend/fujitsu.conf.in: add fi-5750C * doc/descriptions/fujitsu.desc: bump version, add fi-5750C and S510 2007-06-21 Julien Blache * sanei/sanei_scsi.c: Switch sanei_scsi to the SG_IO ioctl interface, instead of the asynchronous SG3 read/write interface. Makes it possible to use SCSI scanners in 32/64bit mixed environments, thanks to the ioctl 32bit compatibility layer, which is NOT possible using the SG3 interface. 2007-06-18 Gerhard Jaeger * doc/plustek/Plustek-PARPORT.changes: Update. * backend/plustek_pp.c: Bumped build number. * backend/plustek-pp_drv.c backend/plustek_pp_sysdep.h: Make the Kernelmodule work with Kernels > 2.6.15 w/o DEVFS 2007-06-12 Ilia Sotnikov * Use libtool instead of ar/ranlib, which correctly handles dependencies (eg. for parallel makes) 2007-05-08 Mattias Ellert * po/Makefile.in, po/sane-backends.*.po: New translation keys for hp3500 and hp5590 backends. Updated Swedish translation. * backend/hp5590.c: Do not localize option names 2007-04-29 Ilia Sotnikov * backend/hp5590_low.c: don't use libusb structs directly, define necessary of them by ourselves ('struct usb_ctrl_setup' -> 'struct usb_in_usb_ctrl_setup') * backend/hp5590_low.c: renamed 'struct usb_bulk_setup' to 'struct usb_in_usb_bulk_setup' to show its internal usage 2007-04-24 Ilia Sotnikov + configure: added check for header + Added files for 'hp5590' backend which supports HP ScanJet 5550/5590/7650 scanners + tools/check-usb-chip.c: added HP ScanJet 5550/5590/7650 detection routine 2007-04-21 Troy Rollo * backend/hp3500.c: Improve speed and reduce noise of most resolutions; deal with an escape code discovered in the scanner's protocol; use hardware detailed calibrations for resolutions up to 300; use improved software detailed calibration for other resolutions; ddd more debug information; drop the 25dpi resolution; make 200dpi the default (the same as the Windows frontend; Add code (not used yet) to deal partially with grayscale and lineart scanning; use I18N strings where appropriate. 2007-04-21 Mattias Ellert * doc/descriptions-external/hp5590.desc, doc/descriptions/unsupported.desc: Added description for new external backend hp5590 See: http://alioth-lists.debian.net/pipermail/sane-devel/2007-April/018977.html 2007-04-21 Julien Blache * backend/epson.c: remove bogus check in get_identity2_information(), causing the identification of various Stylus CX5xxx models (among others) to fail. Olaf Meeuwissen from Epson confirmed the check was bogus. 2007-04-17 Julien Blache * backend/microtek.c: add missing braces. * tools/sane-desc.c: use mode 0664 for usbfs device nodes; allows lsusb to still work for everybody on the system. 2007-04-13 m. allan noah * backend/fujitsu.c: update to v1.0.48, re-enable brightness/contrast for models with built-in support 2007-04-15 Wittawat Yamwong * doc/descriptions/unsupported.desc: Remove Canon PIXMA MP160 and Canon PIXMA MP600 (now supported by pixma backend) 2007-04-13 Mattias Ellert * doc/descriptions-external/hp3900.desc, doc/descriptions/unsupported.desc: Moved 3 scanners from unsupported to hp3900 See: http://alioth-lists.debian.net/pipermail/sane-devel/2007-April/018980.html 2007-04-13 m. allan noah * backend/fujitsu.[ch]|fujitsu.conf.in: update to v1.0.47, change gamma determination, add support/usbid for fi-5650C * doc/descriptions/fujitsu.desc: version number update * tools/hal/.cvsignore: ignore .fdi file 2007-04-09 Wittawat Yamwong * backend/pixma*.[ch] doc/sane-pixma.man doc/descriptions/pixma.desc: Updated to version 0.13.0 Added MP160, MP180, MP460, MP510 and MP600 Fixed a buffer-overflow bug in sane_read() 2007-04-5 Jochen Eisinger * README.openbsd, tools/README, tools/openbsd/attach, tools/openbsd/detach: add notes about device permissions on OpenBSD and provide some example scripts for hotplugd(8). 2007-03-02 m. allan noah * doc/sane.tex: update to 1.05, fix description of SANE_Parameters, from 2007-03-21 Julien Blache * tools/Makefile.in: Remove leftovers from a previous experiment. 2007-03-18 Julien Blache * tools/hal: New directory. * tools/Makefile.in: Add rules to build hal/10-libsane.fdi. * tools/sane-desc.c: Add output mode 'hal', from David Zeuthen . 2007-03-17 Oliver Rauch * Bug #303752: Maybe faulty icc-profile-file length detection: corrected icc profile length calculation 2007-03-08 Earle F. Philhower, III * backend/sm3840*.[ch]: Add 1-bpp modes (lineart, halftone) * doc/sane-sm3840.man: Add 1-bpp mode options 2007-03-05 Gerhard Jaeger * doc/plustek/Plustek-USB.changes: Update. * backend/plustek.c: Bumped build number. * backend/plustek-usb.c: Fixed typo. * backend/plustek-usbdevs.c: Added CanoScan to all Canon device strings, identified one more Plustek device as U24. * backend/plustek-usbhw.c: Fixed button handling for Plustek/ KYE devices and added some more debug messages. 2007-02-24 Giuseppe Sacco * Italian translation update * Second Italian translation update 2007-02-11 Jochen Eisinger * doc/sane-mustek_pp.man: clean up markup, bug #304392 2007-02-11 Henning Meier-Geinitz * po/sane-backends.pl.po: Polish translation fix (from Jakub Bogusz , bug #304410). 2007-02-07 Gerhard Jaeger * sanei/sanei_scsi.c: Fixed retrieval of HZ. 2007-01-28 Henning Meier-Geinitz * backend/gt68xx.c backend/gt68xx_devices.c doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Added .conf entry for Pluste OpticPro S12 and S24. Updated .desc file. * doc/descriptions/unsupported.desc: Added UMAX Astra 2850. Added Canon Imagerunner series. 2007-01-28 m. allan noah * backend/fujitsu.[ch]: update to v1.0.45, update overscan code to extend max scan area * doc/descriptions/fujitsu.desc: version number update 2007-01-27 oliver rauch * backend/umax.c removed bug in highlight blue using green value * doc/umax/umax.CHANGES 2007-01-26 m. allan noah * AUTHORS, doc/sane-fujitsu.man, backend/fujitsu.conf.in, backend/fujitsu.c: updated email address * backend/fujitsu-scsi.h, backend/fujitsu.[ch]: update to v1.0.44, set SANE_CAP_HARD_SELECT on all buttons/sensors. disable sending gamma LUT, quality errors reported. support MS overscan. clamp the scan area to the pagesize on ADF. * doc/descriptions/fujitsu.desc: version number update 2006-01-21 Jochen Eisinger * doc/descriptions/mustek_pp.desc: Cybercom is a CIS scanner 2007-01-20 Mattias Ellert * backend/canon.c, backend/canon-sane.c: Fixed the "three-valued boolean" bug 2007-01-20 Alessandro Zummo * sanei/sanei_tcp.c: added a missing include * backend/epson2.c: fixed a bug while moving scanner data, removed support for line mode (block or ext modes will be used). 2007-01-20 Mattias Ellert * acinclude.m4, aclocal.m4, configure, include/sane/config.h.in: Fix autogenerated files * backend/canon.c, backend/epson.c, backend/epson2.c: String harmonization * po/Makefile.in, po/sane-backends.*.po: New translation keys from canon and epson2 backends Updated Swedish translation * backend/dll.c, backend/microtek.c, backend/umax1220.c, sanei/sanei_wire.c: Fixing compiler warnings 2007-01-19 Mattias Ellert * doc/descriptions/unsupported.desc: Added Visioneer Strobe Pro USB 2007-01-15 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added and updated several scanners. 2007-01-08 Julien Blache * doc: fix man warnings. * backend/microtek2.c: add missing return status check in sane_start(), preventing an ugly segfault later on. 2006-12-18 Alessandro Zummo * epson2: restructured code once more, split in multiple files, added networking support, added FS G extended handshaking mode, make proper use of extended commands when possible * sanei/sanei_tcp.c: read now wait until all the requested data is available. 2006-12-13 Alessandro Zummo * coolscan2: fixed coolscan2 infrared to work as advertised in the man page. RGBA format will come soon. 2006-12-12 Alessandro Zummo * epson2: removed calls to alloca(), code reorganization fixed a bug with request_extended_status (reply length is 33 on older scanners). * include/.cvsignore: added two more entries 2006-12-10 Pierre Willenbrock * backend/genesys_devices.c: reduced height of calibration area * backend/genesys_gl841.c: fixed bug in offset calibration(offset values were not clamped to 0..255) 2006-12-10 Ulrich Deiters * canon: disentangled some pointer arithmetics in canon-sane.c 2006-12-07 Alessandro Zummo * epson2: reordered includes, replaced __FUNCTION__, use the new byteorder macros * moved acbyteorder.m4 to m4/byteorder.m4, added m4/stdint.m4 2006-12-06 Alessandro Zummo * Added acbyteorder.m4 macro for endianness conversion 2006-12-02 Pierre Willenbrock * backend/genesys_devices.c: fixed gamma settings(1.0 now) * backend/genesys.c backend/genesys_gl841.c: improved calibration for dark shades 2006-12-02 Alessandro Zummo * Added missing bits for epson2 driver. 2006-12-01 Alessandro Zummo * Added (experimental) epson2 driver. 2006-12-01 Alessandro Zummo * Added sanei_tcp interface. 2006-11-27 Henning Meier-Geinitz * doc/descriptions-external/samsung.desc: SCX-4200 is reported to work. 2006-11-22 Gerhard Jaeger * doc/plustek/Plustek-USB.changes: update. * backend/plustek.c: bumped build number, fixed option descriptors, see bug #303786. 2006-11-22 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added several scanners, fixed the USB id for Microtek 4850 (bug #304151). * doc/descriptions/umax1220u.desc: Updated status of UMAX 2000. * doc/descriptions-external/brother2.desc: Updated status of Brother MFC-7420. 2006-11-06 m. allan noah * doc/descriptions-external/epkowa.desc: backend v2.3.0 2006-10-31 Henning Meier-Geinitz * doc/descriptions/unsupported.desc: Added HP Photosmart C5100 and ScanJet 8390. * doc/descriptions-external/hpoj.desc: Project is unmaintained. 2006-10-24 Henning Meier-Geinitz * po/sane-backends.pl.po: Major bugfix and few small changes (noticed by Jaroslaw Gorny) (bug #303962). * po/sane-backends.es.po: Updated (from Jonathan Bravo Lopez ). * doc/descriptions-external/hp3900.desc doc/descriptions-external/samsung.desc: Updated based on sane-devel information. * doc/descriptions/unsupported.desc: Added Canon Canoscan 4400F, LiDE 70, Corex Cardscan 700 C, Umax Astra 4900, Visioneer Onetouch 7700. Updated other scanners. 2006-10-03 Henning Meier-Geinitz * doc/descriptions-external/hp3900.desc: Changed status of HP Scanjet 4370 to "minimal" (bug #303839). * doc/scanimage.man frontend/scanimage.c: Added examples on how to set the scan area in the manual page (bug #303802). Mention how to separate parameters from options in --help and manpage (bug #303819). * doc/descriptions/unsupported.desc: Added EDT BizCardReader 900C. Moved Genius ColorPage-SF600 to gt68xx.desc. Minor updates. * backend/gt68xx.c backend/gt68xx_devices.c doc/descriptions/gt68xx.desc doc/gt68xx/gt68xx.CHANGES: Added Support for Genius Colorpage SF600. 2006-09-24 Giuseppe Sacco * Updated italian translation 2006-09-18 m. allan noah * backend/fujitsu.[ch], backend/fujitsu-scsi.h: update to v1.0.43, add model-specific code to init_vpd for M3099, clean some noise * doc/descriptions/fujitsu.desc: version number update 2006-09-18 Stephane Voltz * backend/umax_pp.c backend/umax_pp_mid.c backend/umax_pp.c_low: color calibration fix. Mail address update. 2006-09-14 Henning Meier-Geinitz * doc/descriptions/unsupported.desc doc/descriptions-external/samsung.desc doc/descriptions-external/hpaio.desc: Added several scanners. * po/sane-backends.pl.po: Updated Polish translation (from Jakub Bogusz, bug #303769). 2006-08-31 Oliver Schwartz * backend/snapscan-scsi.c: Bugfix for firmware download from Paul Smedley 2006-08-31 m. allan noah * backend/fujitsu.c: update to v1.0.42, fix bug in get_hardware_status (#303798) * doc/descriptions/fujitsu.desc: version number update 2006-08-29 Gerhard Jaeger * doc/plustek/Plustek-PARPORT.changes: update. * backend/plustek-pp.h backend/plustek-pp_ptdrv.c backend/plustek_pp.c: fixed "not homing" problem, the sensor did not return when the driver gets the _IOCTL_STOP command fixed compilation issue for kernels > 2.6.15 fixed compiler warning conditions 2006-08-28 m. allan noah * backend/fujitsu.c backend/fujitsu-scsi.h: update to v1.0.41, do_usb_cmd() returns io error on cmd/out/status/rs EOF, fix bug in MS buffer/prepick scsi data block * doc/descriptions/fujitsu.desc: version number update 2006-08-28 Stephane Voltz * backend/genesys_gl646.c: changes in gl646_init_regs_for_warmup to add support for HP2400 model. 2006-08-27 Wittawat Yamwong * backend/pixma*.[ch]: update to 0.12.2, use own error codes e.g. PIXMA_EPROTO instead of EPROTO, add MP530, MP800R and MP360, add grayscale mode for MP700 and MP730, add work-around for lock-up ("hang") problem of MP760 and MP780, fix line correction (color alignment) for MP760 * doc/sane-pixma.man: update * doc/descriptions/pixma.desc: add MP360,MP530 and MP800R product ID * doc/descriptions/unsupported.desc: remove Smartbase MP360 (supported by pixma backend) 2006-08-26 m. allan noah * backend/fujitsu.[ch] backend/fujitsu-scsi.h: update to v1.0.40, add 5530C usb id, rewrite brightness/contrast/gamma functions, do_*_cmd functions handle short reads, updated init functions, add MS buffer and prepick support for newer scanners * doc/descriptions/fujitsu.desc backend/fujitsu.conf.in: add 5530C usb id, version number update * doc/descriptions/sane-fujitsu.man: note support for newer scanner models 2006-08-22 Karl Heinz Kremer * backend/epson_usb.c: Add product ID for CX3800/3810, V700/V750 2006-06-11 Eddy De Greef * doc/sane-mustek_pp.man: URL update. 2006-08-21 Karl Heinz Kremer * backend/epson.c: Fix buffer overflow error (submitted by Johannes Meixner) 2006-08-21 Jon Chambers * TIFF tweaks for pickier libtiff under cygwin * backend/Makefile.in: remove explicit $(srcdir) path from .conf source paths to allow support for kdevelop-style build dirs. * backend/dell1600n_net.c: fix compile warnings 2006-08-12 Jon Chambers * backend/dell1600n_net.c: update for dell1600n_net.conf + reduce memory footprint * backend/dell1600n_net.conf.in: added * backend/dell1600n_net.c: update for dell1600n_net.conf.in 2006-08-11 Gerhard Jaeger * doc/plustek/Plustek-USB.changes: update. * backend/plustek.c: bumped build number. * backend/plustek-usbdevs.c: lowered speed for LiDE20/30 when using low resolutions. * backend/plustek-usb.c: fixed warning condition. 2006-08-09 Stephane Voltz * backend/genesys_gl646.c backend/genesys.c backend/genesys_devices.c: improved sanei_genesys_search_reference_point to get more reliable detection for HP2300 and MD6345. Slight tune up for HP2400 model. 2006-08-09 Gerhard Jaeger * doc/plustek/Plustek-PARPORT.changes doc/plustek/Plustek-USB.changes doc/u12/U12.changes: update. * backend/artec_eplus48u.c backend/plustek.c backend/plustek_pp.c backend/u12.c: changed sane.type to "flatbed scanner" only. * backend/plustek-usbdevs.c: fixed LiDE20/30 motor settings 2006-08-06 Stephane Voltz * backend/genesys_gl646.c backend/genesys.c: fix y scan area offset detection for HP2300, one more fix related to bug #303681. 2006-08-01 Henning Meier-Geinitz * doc/descriptions-external/epkowa.desc: Updated info about plugins and other comments (patch from Olaf Meeuwissen ). 2006-07-30 Pierre Willenbrock * backend/genesys_gl841.c: use an intermediate u_int8_t buffer for register write (bug #303681). 2006-07-30 Stephane Voltz * backend/genesys_gl646.c: use an intermediate char buffer for register bulk write (bug #303681). 2006-07-25 Henning Meier-Geinitz * tools/libtool-get-dll-ext: Fixed compilation problem with newer tail programs which don't understand "-1" (bug #303630). 2006-07-17 m. allan noah * backend/fujitsu.c: backend v1.0.39, rewrite contrast slope code for readability, portability to other table widths * doc/descriptions/fujitsu.desc: version number update 2006-07-16 Julien Blache * tools/sane-desc.c: split very long comment lines in the generated udev rules file. Some comment lines very overly long, and udev produced warning messages while loading the rules file. 2006-07-15 m. allan noah * backend/fujitsu.c: backend v1.0.38, add 'useless noise' debug level (35), for mode sense errors * doc/sane-fujitsu.man: minor text changes, add new debug level * doc/descriptions/fujitsu.desc: version number update 2006-07-14 m. allan noah * backend/fujitsu.[ch] backend/fujitsu-scsi.h: backend v1.0.37, add support for mode sense command, use it to detect various page codes instead of hardcoding. add support for send cmd, use it to enable 8 or 10 bit LUT for brightness/contrast. minor global variable and option description cleanups. * doc/descriptions/fujitsu.desc: version number/status update 2006-07-06 m. allan noah * backend/fujitsu.[ch]: backend v1.0.36, less verbose debugging, fi-5900 needs even number of bytes per scanline * doc/descriptions/fujitsu.desc: version number update 2006-07-05 m. allan noah * backend/fujitsu.[ch] backend/fujitsu-scsi.h: backend v1.0.35, allow double feed detection, minor cleanups * doc/descriptions/fujitsu.desc: version number update 2006-07-04 m. allan noah * backend/fujitsu.[ch] backend/fujitsu-scsi.h: update to v1.0.34, add S500 usb id, get more inq and vpd data, allow background color setting for some scanners * doc/descriptions/fujitsu.desc: version number update 2006-07-03 Julien Blache * tools/sane-desc.c: Fix a typo in the udev header. 2006-07-03 Henning Meier-Geinitz * configure configure.in: Changed version to 1.0.18-cvs. Enabled compilation warnings. * Makefile.in: Added ChangeLog-1.0.18 to DISTFILES. Older entries can be found in ChangeLog-1.0.18. backends-1.3.0/ChangeLogs/ChangeLog-1.0.2000066400000000000000000000502331456256263500175260ustar00rootroot000000000000002000-03-06 Petter Reinholdtsen * configure.in configure: New version is 1.0.2. 2000-03-01 Abel Deuring * doc/sane-scsi.man: fixed an ambiguity and a typo. 2000-02-27 Petter Reinholdtsen * backend/v4l*.desc: Changed description from v4l to Video For Linux. * backend/lhii.desc: Updated with URL to LHII drivers home page. * README.unixware7: Added compile information for SCO UnixWare 7 from Jens Scheithauer. 2000-02-26 Karl Heinz Kremer * doc/sane-epson.man: Updated documentation for EPSON backend. 2000-02-26 Chris Pinkham * backend/artec.desc: updated statuses, added link to a webpage for AS6E parallel models, added manpage info. 2000-02-26 Abel Deuring * Sharp man page updated; man page added to doc/Makefile.in 2000-02-26 Petter Reinholdtsen * configure.in: Don't use -ansi and -pedantic unless --enable-warnings is used. Warnings are now disabled by default to prepare for the next release. 2000-02-24 Petter Reinholdtsen * tools/sane-desc.el: In backend web page, packends not included in the distribution have the version number in parentheses. 2000-02-20 Karl Heinz Kremer * sanei/sanei_scsi.c: Make sure the file is valid ANSI C. * configure: Regenerated based on current configure.in. * include/sane/sanei_backend.h: Typedef u_int{8,16,32}_t. Defining them gave bogus code in on Irix 6.5. * backend/lhii.desc backend/musteka4s2.desc backend/nec.desc backend/plustek.desc backend/sagitta.desc backend/st400.desc backend/v4l.desc backend/v4l2.desc: Added info on existing backends. Placed version number in () to indicate that these backend are distributed separately. * backend/sharp.desc backend/pint.desc backend/canon.desc backend/s9036.desc: Fixed typo and updated with more information. * AUTHORS: Marked active maintainers with '(*)'. * sanei/sanei_pio.c: Removed some warnings. * configure.in: New flag --enable-warnings to turn on or off more GCC warnings. Default should be 'on' for development snapshots, and 'off' for releases. 2000-02-19 Karl Heinz Kremer * backend/epson.*: Updated desc file with new version number and some more supported scanners. Removed OPT_PREVIEW_RESOLUTION so that the frontend can handle the preview resolution. Changed the OPT_RESOLUTION data from a range to a word list, so that the frontends can display the correct list of available resolutions. 2000-02-19 Chris Pinkham * backend/artec.c: Updated backend version to v0.5.13. Corrected matrix of which features/enhancements should be active for each scan mode. Fixed bug which causing Contrast to be INactive at startup instead of Threshold. * backend/artec.desc: updated backend version information. 2000-02-19 Petter Reinholdtsen * sanei/sanei_thread.c include/sane/sanei_thread.h: These files declares a _proposed_ internal SANE interface. It was proposed by Yuri Dario to wrap UNIX functions fork(), kill(), waitpid() and wait(), which are missing or not working on OS/2. * frontend/saned.c: OS/2 send socket on param list. Patch from Yuri Dario. * sanei/os2_srb.h sanei/sanei_scsi.c: Include OS/2 SCSI header file. * include/sane/sanei_config.h sanei/sanei_config.c: New function sanei_config_read(). Use this instead of fgets() when reading config files to remove line ending chars on all known platforms. Patch from Yuri Dario. * configure.os2: Updated configure param list from Yuri Dario. 2000-02-19 Oliver Rauch * updated umax backend to version 1.0 build 18 for details read backend/umax.CHANGES 2000-02-19 Oliver Rauch * corrected spelling error in umax.c (devive -> device) 2000-02-18 Abel Deuring * sharp.c / sharp.h: Options for resolution now conform to the Sane API; fixed and initialisation bug. Updated backend version number in sharp.desc. 2000-02-18 Oliver Rauch * updated umax backend to 1.0 build 17 (added support for sanei_scsi_open_extended) for details take a look at backend/umax.CHANGES 2000-02-15 Petter Reinholdtsen * backend/tamarack.desc: Updated info on request from Rogier Wolff. 2000-02-14 Petter Reinholdtsen * backend/m3096g-scsi.h backend/sp15c-scsi.h: Make lint_catcher static to avoid link problems with duplicate symbols. 2000-02-13 Petter Reinholdtsen * README.unixware2: Information from Wolfgang Rapp on how to build SANE on UnixWare 2. * ltconfig ltmain.sh: Upgraded to libtool 1.3.4 and added Oliver Rauch's soname patch. * backend/hp-accessor.c backend/hp-device.c backend/hp-handle.c backend/hp-hpmem.c backend/hp-option.c backend/hp-option.h backend/hp-scl.c backend/hp-scsi.h backend/hp.c backend/hp.desc backend/hp.h doc/sane-hp.man: Updated HP backend to v0.86 from Peter Kirchgessner. Enable scan depths > 8 and add option enable-image-buffering to config file. * AUTHORS: Added author for backend s9036. * backend/qcam.desc: Updated with more information. 2000-02-11 Chris Pinkham * backend/artec.c backend/artec.h: Updated backend to version 0.5.11. Removed mono_adjust function which handled bit offset in 1bpp modes, this is now handled by forcing all scans to be wide enough to fill a full byte. Reinserted code to set brightness on A6000C model as this appears to be the only model that accepts that setting. Bug-fixes for ADF & Transparency option settings. * backend/artec.desc: updated backend version information. 2000-02-11 Karl Heinz Kremer * backend/epson.c - Fixed default scan source when option equipment is installed. The default is now always "Flatbed". 2000-02-08 Oliver Rauch * Added (again) missing file: doc/sane-logo2.jpg 2000-02-08 Oliver Rauch * Added missing umax doc files: - doc/sane-umax-old-scanners-doc.html - doc/sane-umax-not-listed-doc.html - doc/sane-umax-speed-doc.html * Updated: - doc/sane-umax-doc.html - doc/sane-umax-scanners-doc.html 2000-02-08 Matto Marjanovic * backend/microtek.c backend/microtek.h backend/microtek.conf backend/microtek.desc doc/sane-microtek.man: Updated microtek backend to v0.12.0. Changes: - LUT entry size should match bit depth of scan. - Free LUT's in sane_close(). - Fixed really dumb type typos in microtek.h. - Added all "mystery" model codes (and an auto-warning). - Added all additional document size codes. - Made function (description) of "scan speed" more obvious. - Added entries for Color PageWiz. - Added entries for Agfa DuoScan. - A few miscellaneous cleanups to quiet gcc warnings. 2000-02-07 Petter Reinholdtsen * frontend/saned.c sanei/sanei_pio.c sanei/sanei_scsi.c: Some OS/2 patches from Yuri Dario. * frontend/scanimage.c: Avoid sprintf("%s", NULL). Bugreport and fix from Yuri Dario. 2000-02-06 Petter Reinholdtsen * configure: Regenerated based on current configure.in. doc/Makefile.in doc/sane-logo.gif doc/sane-logo.png doc/sane.gif doc/sane.png: Convert .gif to .png. * backend/canon.c backend/snapscan-sources.c: Port to HP/UX. Stop using GCC specific features. 2000-02-05 Rene Rebe * backedn/avision.? only minor updates and the new home-page url ... 2000-02-05 Abel Deuring * fixed wrong buffer size handling for old Linux SG drivers in sanei_scsi.c * updated sane-scsi.man: How to change the buffer size, if the new Linux SG driver is being used. * fixed a type on sharp.c 2000-02-03 Karl Heinz Kremer * backend/epson.[ch] Reworked the gamma correction stuff 2000-02-02 Karl Heinz Kremer * backend/epson.[ch] Finally put the USB fix into the CVS version. Removed version number from epson.h so that this file does not need to be updated every time epson.c changes. 2000-02-01 Karl Heinz Kremer * backend/epson.[ch] More changes in the attach function to recognize USB scanners again. * doc/sane-epson.man More updates to the man page. 2000-01-29 Oliver Rauch * updated umax backend to version 1.0-build-16 for details take a look at backend/umax.CHANGES 2000-01-31 Petter Reinholdtsen * backend/hp-accessor.c backend/hp-accessor.h backend/hp-device.c backend/hp-device.h backend/hp-handle.c backend/hp-hpmem.c backend/hp-option.c backend/hp-option.h backend/hp-scl.c backend/hp-scl.h backend/hp-scsi.h backend/hp.TODO backend/hp.c backend/hp.desc doc/sane-hp.man: Updated to v0.85 from Peter Kirchgessner. 2000-01-30 Chris Pinkham * backend/artec.c backend/artec.h: (v0.5.9) changes to get lineart and halftone modes working on AT12 and AM12S models. bugfixes to eliminate hanging on some AT12 models. * backend/artec.desc: version and model status updates 2000-01-30 Oliver Rauch * corrected missing "/" in include/sane/config.h.in line 251 2000-01-30 Rene Rebe * backend/avision.c fixed to compile on HP/UX * backend/avision.desc fixed typo * AUTHORS changed my eMail address 2000-01-30 Petter Reinholdtsen * backend/dll.c backend/dll.aliases doc/sane-dll.man: Aliased and hidden backend patch from Ingo Wilken. * config.sub configure.in include/sane/config.h.in sanei/sanei_scsi.c: UnixWare 2.x and UnixWare 7 port by Wolfgang Rapp. 2000-01-29 Karl Heinz Kremer * backend/epson.[ch] Moved gamma "stuff" from advanced to standard options section to fix core dump in xscanimage Removed pragma pack() to make it easier to compile on non-gcc systems 2000-01-29 Oliver Rauch * preview (xscanimage): added GDK_INPUT_EXCEPTION to gdk_input_add * updated umax-backend to build 15 (for more see umax.CHANGES * xscanimage: added GDK_INPUT_EXCEPTION to gdk_input_add 2000-01-26 Karl Heinz Kremer * backend/epson.[ch] Bugfix for resolution selection via menu Fixed coredump when no scanner found merged Christian Bucher's newest version * AUTHORS ChangeLog 2000-01-26 Petter Reinholdtsen * backend/m3096g-scsi.h backend/m3096g.c backend/m3096g.desc backend/m3096g.h backend/sp15c-scsi.h backend/sp15c.c backend/sp15c.desc backend/sp15c.h: Updated backends sp15c (v1.12) and m3096g (v1.11) from Randolph Bentson. 2000-01-25 Chris Pinkham * backend/artec.h backend/artec.c backend/artec.desc installed new version (0.5.6) of artec backend with support for Artec AM12S and Plustek 19200S models and numerous bugfixes. * doc/sane-artec.man updated manpage to reflect new list of supported models and backend status. 2000-01-25 Abel Deuring * frontend/scanimage.c: minor modifications to the new batch mode If an error occurs in batch mode, the corresponding output file is now deleted. (Should make post-processing easier, because otherwise we would have an empty output file every time the ADF is empty or paper is jammed.) 2000-01-25 Kazuya Fukuda and Abel Deuring * backend/sharp.c: Fix of possible segfault in sane_init * backend/sharp.conf: contains now a complete sample configuration 2000-01-25 Petter Reinholdtsen * frontend/scanimage.c: Return SANE_STATUS_NO_MEM if advance() fails. Add return value to test_it(). * include/sane/sanei_debug.h: Add fflush() after fprintf() in DBG macro. * AUTHORS backend/Makefile.in backend/m3096g.h backend/m3096g.c backend/m3096g-scsi.h backend/m3096g.desc backend/m3096g.conf backend/sp15c.h backend/sp15c.c backend/sp15c-scsi.h backend/sp15c.conf backend/sp15c.desc backend/dll.conf doc/Makefile.in doc/sane-fujitsu.man: Added backends sp15c (v1.11) and m3096g (v1.10). All patches from Randolph Bentson. 2000-01-24 Abel Deuring * backend/sharp.c, backend/sharp.h: update to version 0.30 (sorry, forgot to make this note some days earlier) 2000-01-24 Oliver Rauch * Added test for libcam to configure.in it is needed by FreeBSD 3+ 2000-01-21 Rene Rebe: * ChangeLog: updated (oversaw last time, sorry) * backend/avision.c: fixed some compiling warnings 2000-01-21 Petter Reinholdtsen * sanei/sanei_scsi.c: Bugfix: Added return type for dummy sanei_scsi_open_extended() call. Bugreport for Solaris from Martin Spott. 2000-01-20 Petter Reinholdtsen * sanei/sanei_ab306.c: Removed compiler warnings. * frontend/scanimage.c: Adds a "batch mode" to the software, that can be used in conjunction with an ADF device, so that one can e.g. scan a complete document consisting of multiple pages with just one call to scanimage. This modification was created by Christian Bucher so that his Epson scanner could use the ADF. Patch from Karl Heinz Kremer. 2000-01-19 Rene Rebe * backend/avision.*: added - the (very) basic avision backend * backend/Makefile.in: updated for the avision backend * AUTHORS: updated 2000-01-18 Karl Heinz Kremer * fixed typo in epson.desc and fixed the dates for two of my earlier submissions in ChangeLog file 2000-01-16 Karl Heinz Kremer * backend/epson.desc updated with URL * backend/epson.c added support for Expression family of scanners * doc/sane-epson.man updated 2000-01-10 Karl Heinz Kremer * backend/epson.desc fixed/updated. Now shows a link to the alternative FilmScan 200 backend. 2000-01-09 Karl Heinz Kremer * backend/epson.* updated. This is a new backend still based on the original Sane 1.0.1 version, but with lots of work done: Support for ADF/TPU, more function levels (up to B9 and initial work for F5), parallel scanner support and initial support for USB scanners (don't use this yet, it still needs a lot of work to be functional and usable). 2000-01-05 Oliver Rauch * backend/umax* updated to backend version 1.0 build 14 for detailed info take a look at umax.CHANGES 2000-01-05 Petter Reinholdtsen * sanei/sanei_scsi.c include/sane/sanei_scsi.h configure.in linux_sg3_err.h: Updated Linux SCSI Generic (SG) driver as follows: - new handling of sanei_scsi_max_request_size, if an SG driver >= 2 is installed; - SCSI command queueing on SG level for SG drivers >= 2.1.35; - usage of the new interface in SG version 3, if available - new configure option --enable-scsibuffersize=N Patch from Abel Deuring . 2000-01-02 Petter Reinholdtsen * configure.in tools/Makefile.in tools/sane-config.in: New script sane-config to help frontend developers find the sane libraries. 1999-12-29 Petter Reinholdtsen * configure.in: Add gcc options '-W -Wall -Wpointer-arith -Wcast-qual' to at least get warnings on illegal C code which would fail to compile on HP/UX native compiler. 1999-12-20 Petter Reinholdtsen * include/sane/config.h.in backend/dc25.desc backend/dc25.c: Updated DC-25 backend to v1.2 from Peter Fales . - Updated dc25.desc - Retry capability for missing data or bad checksums in the data from the camera. This has resulted in a dramatic reduction in download failures. - Include HAVE_CFMAKERAW in config.h.in and dc25.c. * backend/dc210.c: Corrected HAVE_cfmakeraw to HAVE_CFMAKERAW. * frontend/xscanimage.c: Change GIMP_EXTENSION to SANE_GIMP_EXTENSION to avoid name conflict with gimp. Patch from Stanislav Brabec . 1999-12-12 Petter Reinholdtsen * backend/ricoh.h backend/ricoh.c: Add support for Ricoh IS50 scanner. Limit IS50 resolution to 400 dpi. Reverse IS50 contrast direction. Patch from Dick Streefland . * backend/microtek.c backend/microtek.h backend/microtek.conf backend/microtek.desc doc/sane-microtek.man: Updated Microtek backend to v0.11.0 from Matthew Marjanovic . * backend/microtek2.c backend/microtek2.h: Updated Microtek2 backend to v0.8 from Bernd Schroeder . 1999-11-20 Oliver Rauch * ltmain.sh: changed variable soname from "libsane-backendname.*" to "libsane.*" by sed command. * ltmain.sh: changed again the sed command, forgot to remove "0-9" 1999-11-14 Petter Reinholdtsen * backend/artec.c backend/artec.h backend/artec.desc doc/sane-artec.5 doc/Makefile.in: Updated Artec backend to v0.5 from Chris Pinkham . 1999-11-11 Petter Reinholdtsen * backend/microtek2.c backend/microtek2.h backend/microtek2.desc: Updated Microtek2 backend to vpre0.8.161099 from Bernd Schroeder * backend/canon-* NEWS doc/canon.*: Updated Canon backend to v991108 from Manuel Panea . 1999-09-14 Oliver Rauch * removed sane-umax-*.gif and umaxlogo.gif, the files were replaced by jpg format. 1999-09-13 Oliver Rauch * backend/umax* and doc/sane-umax* updated to version 1.0-build-12 for detailed info take a look at umax.CHANGES 1999-09-12 Petter Reinholdtsen * include/sane/config.h.in: Add missing HAVE_SYS_SOCKET_H. * backend/hp-handle.c backend/hp-option.c backend/hp-option.h backend/hp-scl.c backend/hp-scl.h backend/hp-scsi.h backend/hp.c backend/hp.desc backend/hp.h doc/sane-hp.man: Updated backend to v0.83 from Peter Kirchgessner . News: - Reset scanner before downloading parameters (fixes problem with sleep mode of scanner) - Fix problem with coredump if non-scanner HP SCSI devices are connected (CDR) - Option scan-from-adf replaced by scantype normal/adf/xpa. Transparency adapter scans (XPA) allow scanning with internal light source switched off. - PhotoScanner: Allow scanning of slides/negatives only at multiple of 300 dpi. This fixes problem with preview which scanned at arbitrary resolutions. - (by Marian Szebenyi): close pipe (coredump on Digital UNIX) * backend/Makefile.in: Correct install target to also work on platforms where dll endings isn't .so (like HP/UX). Extract endings from libsane-dll.la. 1999-09-07 Petter Reinholdtsen * tools/find-scanner.c: Checks (Linux only so far) for SCSI Generic support in the OS kernel. If NOT present it tells the user they may need SG drivers. Patch from Nick Lamb . * sanei/sanei_scsi.c [sanei_scsi_cmd sanei_scsi_open]: Digital UNIX 4.0D patches from Marian Szebenyi . 1999-08-16 Petter Reinholdtsen * configure.in: Turn on more gcc warnings. * acinclude.m4 configure.in: Separate GTK_LIBS into LIBS and LDFLAGS to get the X programs to link on HP/UX. 1999-08-12 Petter Reinholdtsen * aclocal.m4 configure.in configure: Generate configure and aclocal.m4 with autoconf 2.13. Adding AC_PROG_LD, trying to get this compiling on CygWin32. Add AM_PROG_CC_STDC to get it to compile out of the box on HP/UX. * lib/dll.c: Use calloc() instead of malloc() and memset(). Use dlerror() instead of strerror() when dlopen fails (bugfix from Ingo Wilkens). 1999-08-11 Petter Reinholdtsen * config.guess config.sub: Updated to latest versions from FSF. Better support for BeOS, WinNT and others. 1999-08-09 Petter Reinholdtsen * Started CVS branch DEVEL_1_9. * install-sh ltconfig ltmain.sh mkinstalldirs: Updated from automake 1.4 and libtool 1.3.3. backends-1.3.0/ChangeLogs/ChangeLog-1.0.20000066400000000000000000002662011456256263500176120ustar00rootroot000000000000002009-05-03 Chris Bagwell * Makefile.in, aclocal.m4, configure, */Makefile.in: Regenerated with newer automake. * backend/Makefile.am, tools/sane-config.in: Add missing $GPHOTO2_LDFLAGS so library can be found. * tools/Makefile.am: Add missing $SCSI_LIBS to sane-find-scanner. ****** Release of sane-backends 1.0.20. End of code freeze ****** 2009-05-03 m. allan noah * config.guess, config.sub: updated to latest versions * NEWS, configure.in, configure: updated for 1.0.20 * sane-backends.lsm, doc/releases.txt: minor tweaks * doc/descriptions/coolscan3.desc, doc/descriptions/rts8891.desc: add :new marker, correct manpage link 2009-05-01 m. allan noah * backend/fujitsu.c: copy_buffer() needs to count lines, or M309[12] cannot scan in duplex 2009-04-30 m. allan noah * backend/fujitsu.c: ignore errors in scanner_control(), M3091 has not worked since sane 1.0.19, due to this. 2009-04-30 m. allan noah * acinclude.m4, backend/gphoto2.c, configure, include/sane/config.h.in: deal with upcoming gphoto2 interface change (patch by Chris Bagwell) * po/sane-backends.nl.po: updates from Martin Kho 2009-04-29 m. allan noah * po/sane-backends.nl.po: updates from Martin Kho * po/sane-backends.de.po: updates from Burkhard Luck * po/Makefile.am, po/Makefile.in, po/sane-backends.en_GB.po: new translation from Andrew Coles * po/*.po: rebuilt with new strings 2009-04-29 Stéphane Voltz * doc/sane-genesys.man doc/sane-rts8891.man: documentation updates and cleanups 2009-04-28 Julien Blache * sanei/sanei_scsi.c: /proc/scsi is being deprecated in the Linux kernel; use sysfs for SCSI device enumeration in sanei_scsi_find_devices() by default, keep sanei_proc_scsi_find_devices() as a fallback option. 2009-04-27 Gerhard Jaeger * backend/plustek-usbdevs.c: Tweaked highspeed settings for Epson 1260 2009-04-27 Stéphane Voltz * backend/rts8891.c backend/rts8891.h backend/rts8891_devices.c backend/rts8891_low.c backend/rts8891_low.h backend/rts88xx_lib.c backend/rts88xx_lib.h: turn off scanner sharing option to off by default to keep on the safe side, copyright and internal version updates. 2009-04-27 Stéphane Voltz * backend/genesys.c backend/genesys_gl646.c backend/genesys_devices.c: shading calibration fixes for HP2300 2009-04-25 Alessandro Zummo * backend/epson2.c: fixed TPU warmup retry 2009-04-23 Stéphane Voltz * backend/genesys.c backend/genesys_gl646.c backend/genesys_devices.c: y scan position and shading calibration fixes for MD5345/MD6471 2009-04-23 m. allan noah * doc/descriptions/fujitsu.desc: update S1500 status to complete 2009-04-22 m. allan noah * include/sane/sane.h: convert new frame and status to #define 2009-04-21 Nicolas Martin * doc/sane-pixma.man, doc/descriptions/pixma.desc, backand/pixma_mp150.c: Updated documentation for Pixma MP240, reported to work fine by Nik. 2009-04-21 Alex Belkin * backend/xerox_mfp.c: update version number. 2009-04-20 m. allan noah * backend/fujitsu.c, backend/fujitsu.conf.in, doc/descriptions/fujitsu.desc: add S1500 usb ids and status * backend/canon_dr.c: update credits * doc/sane-fujitsu.man, doc/sane-canon_dr.man: update version numbers, dates, credits and known issues * doc/saned.man: remove section about uncontrolled data port range 2009-04-20 Alessandro Zummo * backend/coolscan3.c: temporarily disable infrared. 2009-04-17 Nicolas Martin * doc/sane-pixma.man, doc/descriptions/pixma.desc: Updated documentation for Pixma MP540, reported to work fine by Rogge. 2009-04-17 Pierre Willenbrock * backend/genesys.c, backend/genesys_gl646.c, backend/genesys_gl841.c, backend/genesys_low.h: Always do shading calibration in color mode on gl841. * backend/genesys.c, backend/genesys_gl841.c: Fix bugs introduced above. 2009-04-16 Stéphane Voltz * backend/genesys.h backend/genesys.c backend/genesys_gl646.c backend/genesys_devices.c: y scan position fixes - added a 'clear calibration cache button' 2009-04-15 Stéphane Voltz * backend/genesys.c backend/genesys_gl646.c: calibration cache activation for gl646 based scanners 2009-04-15 m. allan noah * backend/fujitsu.c, doc/descriptions/fujitsu.desc: backend v93 - return cmd status for reads of sensor options * backend/canon_dr.c, doc/descriptions/canon_dr.desc: backend v26 - return cmd status for reads of sensor options - allow rs to adjust read length for all bad status responses 2009-04-14 Stéphane Voltz * backend/genesys.c backend/genesys_devices.c backend/genesys_gl646.c: shading calibration rework for GL646 based scanners 2009-04-13 m. allan noah * tools/sane-desc.c: fix double summing of untested column 2009-04-13 Stéphane Voltz * backend/rts8891.c: minor version change to test commit scripts 2009-04-13 Stéphane Voltz * backend/pnm.c: conditional handling of STATUS_HW_LOCKED and STATUS_WARMING_UP 2009-04-12 m. allan noah * doc/doxygen-sanei.conf.in: update to recent version of doxygen 2009-04-12 m. allan noah * configure.in: change version to 1.0.20cvs * configure: rebuild from configure.in * include/sane/sane.h: hide API changes (minor, frame and status) * backend/canon_dr.c, backend/fujitsu.c: SANE_FRAME_JPEG * backend/coolscan3.c: SANE_FRAME_RGBI * backend/genesys_gl646.c, backend/genesys_gl841.c: STATUS_HW_LOCKED * backend/rts8891.c, frontend/scanimage.c: STATUS_WARMING_UP * backend/pixma_io_sanei.c, backend/xerox_mfp.c, backend/sane_strstatus.c: STATUS_HW_LOCKED & STATUS_WARMING_UP * doc/releases.txt: minor updates for new build system 2009-04-07 Nicolas Martin * doc/sane-pixma.man, doc/descriptions/pixma.desc: Updated documentation for i-SENSYS MF4018 support. 2009-04-07 Julien Blache * doc/descriptions-external/epkowa.desc: update for iScan 2.19.0, from Olaf Meeuwissen. 2009-04-05 Nicolas Martin * backend/pixma_io.h, backend/pixma_io_sanei.c, doc/sane-pixma.man: Increased timeouts for read bulk and write bulk operations to 10s Changed minimum timeout for interrupt read to 100ms For MAC OS X : added a wrapper to sanei_usb_read_int as darwin libusb does not handle timeouts in interrupt reads. This disables button scan for MAC OS X, updated man page with this info. * backend/pixma_mp150.c: Added 2 new Canon Pixma models but usb pid/vid yet unknown. 2009-04-05 m. allan noah * backend/canon_dr.[ch], backend/canon_dr.conf.in: backend v24 - fix DR-2510C duplex deinterlacing code - rewrite sane_read helpers to read until EOF - update sane_start for scanners that don't use object_position - don't call sanei_usb_clear_halt() if device is not open - increase default buffer size to 4 megs - set buffermode on by default - hide modes and resolutions that DR-2510C lies about - read_panel() logs front-end access to sensors instead of timing - rewrite do_usb_cmd() to use remainder from RS info * doc/descriptions/canon_dr.desc: backend v24, update DR-2510C comment 2009-03-31 Louis Lagendijk * backend/pixma_bjnp.c: - silenced some debug statements during scanner detections - increased timeouts to 20 seconds as 10 seconds is apparently - not enough for the combination of a Mac and an MX850. 2009-03-21 Alex Belkin * backend/xerox_mfp.conf.in: add Xerox Phaser 6110MFP * backend/xerox_mfp.c: compatibility with scanners w/o feeder. 2009-03-28 Julien Blache * backend/avision.c: fix typos in error messages in sense_handler(). 2009-03-27 m. allan noah * backend/canon_dr.[ch], backend/canon_dr-cmd.h: backend v23 - rewrite all image data processing code - handle more image interlacing formats - re-enable binary mode on some scanners - limit some machines to full-width scanning * doc/descriptions/canon_dr.desc: v23, improved comments 2009-03-25 m. allan noah * backend/canon_dr.[ch]: backend v22 - add deinterlacing code for DR-2510C in duplex and color 2009-03-25 Pierre Willenbrock * backend/genesys.c, backend/genesys_gl646.c, backend/genesys_gl841.c, backend/genesys_low.h: Add calibration cache 2009-03-24 m. allan noah * backend/canon_dr.[ch], backend/canon_dr-cmd.h: backend v21 - correct rgb padding macro - skip send_panel and ssm_df commands for DR-20xx scanners * doc/descriptions/microtek2.desc: add ScanMaker 6400XL 2009-03-24 Chris Bagwell * doc/Makefile.am: Don't include any template.desc. into generated HTML files. 2009-03-23 m. allan noah * backend/canon_dr.[ch], backend/canon_dr-cmd.h: backend v20 - improved macros for inquiry and set window - shorten inquiry vpd length to match windows driver - remove status-length config option - add padded-read config option - rewrite do_usb_cmd to pad reads and calloc/copy buffers * backend/canon_dr.conf.in: s/status-length/padded-read/g 2009-03-22 m. allan noah * backend/canon_dr.[ch]: backend v19 - pad gray deinterlacing area for DR-2510C - override tl_x and br_x for fixed width scanners * doc/descriptions/canon_dr.desc: backend v19, update comments 2009-03-21 m. allan noah * backend/canon_dr.[ch], backend/canon_dr-cmd.h: backend v15 thru v18 - add byte-oriented duplex interlace code - add RRGGBB color interlace code - add basic support for DR-2580C, DR-2050C, DR-2080C, DR-2510C - add more unknown setwindow bits - add support for 16 byte status packets - clean do_usb_cmd error handling (call reset more often) - set status packet size from config file - rewrite config file parsing to reset options after each scanner - add config options for vendor, model, version - don't call inquiry if those 3 options are set - remove default config file from code - add initial gray deinterlacing code for DR-2510C - rename do_usb_reset to do_usb_clear * doc/descriptions/canon_dr.desc: backend v18, update model status * backend/canon_dr.conf.in: added better comments and new options 2009-03-21 Pierre Willenbrock * backend/genesys_devices.c: Enable Motor again for combined dark/bright calibration, fix calculation of pixel number used in calibration * backend/genesys_devices.c, backend/genesys_gl646.c, backend/genesys_gl841.c, backend/genesys_low.h: Remove park_head * backend/genesys_devices.c, backend/genesys_gl841.c: Calculate shading coefficients using maximum input resolution available 2009-03-21 m. allan noah * backend/fujitsu.[ch]: backend v91 - remove unused temp file code 2009-03-20 m. allan noah * backend/hpljm1005.c: use private function instead of round() 2009-03-19 Pierre Willenbrock * backend/genesys_devices.c, backend/genesys.conf.in: Add basic support for Visioneer XP100 rev 3 and USB IDs for Syscan DocketPort 465 2009-03-06 Louis Lagendijk * backend/pixma_bjnp.c backend/pixma_bjnp.h backend/pixma_bjnp_private.h backend/pixma_io_sanei.c: Make bjnp protocol more resilient against packet loss and corruption Changed timeout for all responses to be at least 10 seconds Send all broadcasts for scanner detection 5 times Made sure scanners are added to device list only once Changed device-id for bjnp so it uses scanner hostname/ip-address instead of mac address as this is more human friendly. To make room, use scanner model instead of USB-id (which is bogus for network scanners 2009-03-17 m. allan noah * doc/desc/gt68xx.desc, backend/gt68xx.conf.in: add NeatReceipts Mobile Scanner (from Kelly Price) 2009-03-13 m. allan noah * frontend/scanimage.c, doc/scanimage.man: make -B (buffer-size) selectable, and make long name consistent. (#309672 by Johannes Berg) 2009-03-13 m. allan noah * tools/sane-desc.c: remove 8859-1 chars from string cleaner, enforce only printable ASCII on output 2009-03-13 Ilia Sotnikov * frontend/saned.c: - Remove unnecessary 'res' variable assignment in check_host() (AF-indep version) 2009-03-13 Julien Blache * doc/descriptions-external/epkowa.desc: update for iScan 2.18.0, from Olaf Meeuwissen. * frontend/saned.c: rework Ilia's changes in check_host(). Explicitly bind IPv6 addresses first, introduce do_bindings_family() split off of do_bindings(). 2009-03-12 Ilia Sotnikov * frontend/saned.c: - Allow host checking to proceed if no local name was found 2009-03-12 Ilia Sotnikov * frontend/saned.c: - Use hstrerror (h_errno) instead of strerror(errno) on gethostbyname() errors 2009-03-12 Nicolas Martin * doc/sane-pixma.man, doc/descriptions/pixma.desc: Added ImageClass MF4010 support in pixma backend docs. * backend/pixma_imageclass.c: Removed ADF capability to ImageClass models without ADF * backend/pixma.h, backend/pixma_common.c, backend/pixma_io_sanei.c: Added a PIXMA_EOF error return code 2009-03-09 Stéphane Voltz * backend/genesys.c backend/genesys.h backend/genesys_devices.c backend/genesys_gl646.c doc/descriptions/lexmark.desc: cleanups for genesys backend. Change in document detection for gl646 sheetfed scanners. - lexmark backend description update 2009-03-09 Stéphane Voltz * backend/genesys.c backend/genesys_devices.c backend/genesys_gl646.c: 1200 dpi scan fix and head positioning fixes 2009-03-07 m. allan noah * backend/canon_dr.c, backend/canon_dr.conf.in: backend version 14 - remove HARD_SELECT from counter (Legitimate, but API violation) - attach to CR-series scanners as well 2009-03-06 Louis Lagendijk * backend/pixma_bjnp_private.h: disabled experimental/incomplete button handling for bjnp protocol as it seems to cause problems with xsane preview 2009-03-06 m. allan noah * backend/canon_dr.c, backend/canon_dr.conf.in, doc/descriptions/canon_dr.desc: backend version 13 - new vendor ID for recent machines - add usb ids for several new machines - DR-4010C reported good 2009-03-05 m. allan noah * backend/umax1220u.c: set initial scan area dimensions to maximum 2009-03-05 Chris Bagwell * configure.in: Allow user to disable latex support; original patch from Johnson Earls. 2009-03-05 m. allan noah * doc/descriptions-external/epkowa.desc: update all broken urls 2009-03-04 m. allan noah * doc/descriptions/coolscan3.desc, doc/sane-coolscan3.man, doc/Makefile.in|am: add missing coolscan3 docs * backend/dll.conf.in: # out epson and coolscan2, remove hpoj text * doc/descriptions/epson2.desc: add Epson RX-620 2009-03-03 Pierre Willenbrock * backend/genesys_gl841.c: Fix the generated generic gamma table. 2009-02-28 Chris Bagwell * tools/sane-config.in: Add space mistakenly removed during last update. 2009-03-03 Julien Blache * doc/descriptions/epson.desc: add Epson RX-620 (0x04b8 0x0811), reported by Heikki Kantola. 2009-02-28 Chris Bagwell * acinclude.m4, tools/sane-config.in: Add in missing libraries to sane-config missed during fix to stop using LIBS to link everything. Add back in code to move LDFLAGS out of $GPHOTO2_LIBS and into GPHOTO2_LDFLAGS. 2009-03-02 Stéphane Voltz * backend/genesys_devices.c, backend/genesys_gl646.c: fix 400, 1200 and 2400 dpi scan modes for MD5345/MD6471 2009-03-02 Julien Blache * frontend/saned.c: work around backends that can't keep their dirty fingers off stdin/stdout/stderr when run through inetd, breaking the network dialog and crashing the remote net backend. 2009-02-28 Chris Bagwell * m4/byteorder.m4: Delete temporary file in all cases. * backend/Makefile.am, japi/Makefile.am: Use BUILT_SOURCES instead of dependency so that files are not compiled during "dist" target. 2009-02-28 Pierre Willenbrock * backend/genesys.conf.in, backend/genesys_devices.c, backend/genesys_gl841.c, backend/genesys_low.h: Add support for Ambir/Syscan DocketPORT 665 * backend/genesys.conf.in, backend/genesys_devices.c, backend/genesys_low.h: Add support for Visioneer Roadwarrior * backend/genesys_gl841.c: Adjust gl841 part to recent changes 2009-02-27 Stéphane Voltz * backend/genesys.c backend/genesys.conf.in backend/genesys.h backend/genesys_devices.c backend/genesys_gl646.c backend/genesys_gl841.c backend/genesys_low.h: rewrite of gl646 internals to enable easy scanner addition and better calibration process. Untested support for hp3670. Use of id in genesys_devices structures to have more robust descriptions. 2009-02-25 Chris Bagwell * acinclude.m4, backend/*.[ch], tools/*.[ch], frontend/*.[ch]: Global replace of u_int??_t with C9x standard based uintxx_t in order to remove some tricky and overlapping portability logic from acinclude.m4 and leave it in only m4/stdint.m4. 2009-02-24 Chris Bagwell * configure.in, m4/stdint.m4: Update to latest version to get latest fixes and use logic to not recreate _stdint.h if no changes (to prevent unneeded recompiles). * m4/byteorder.m4: Added support to reuse existing byteorder.h if no changes to prevent timestamp causing a recompile. 2009-02-24 m. allan noah * frontend/scanimage.c: improved comments, simplified x/y option code, removed buggy -1/+1 x/y code (#311172), expose non-settable options, check for invalid caps on options, handle option descs with \n * doc/releases.txt: add note about cvs checkout on alioth. 2009-02-24 m. allan noah * backend/*.[ch]: more consistent #include "../include/sane/config.h" 2009-02-24 m. allan noah * backend/Makefile.am/in, backend/xerox_mfp.conf.in: should use conf.in 2009-02-23 Chris Bagwell * backend/Makefile.am: Add back in deletion of $(sanelibdir)/libsane.* for buggy libtools. Add some minor documentation. * doc/backend-writing.txt: Add minor document updates to reflect conversions to automake. 2009-02-23 m. allan noah * backend/fujitsu.c, backend/fujitsu.conf.in, doc/descriptions/fujitsu.desc, doc/sane-fujitsu.man: backend v90, add S510M usb ID's, add fi-6010N to .desc * backend/epjitsu.conf.in, doc/descriptions/epjitsu.desc, doc/sane-epjitsu.man: add S300M usb ID's 2009-02-23 Pierre Willenbrock * backend/genesys_devices.c, backend/genesys_gl841.c, backend/genesys_low.h: Add post_scan and eject_feed to struct Genesys_Model * backend/genesys.c, backend/genesys_gl646.c, backend/genesys_gl841.c: Move sanei_genesys_{start,stop}_motor into chip specific sources backend/genesys_gl841.c: Make start/stop_(motor, now)action actually work, various warning cleanups 2009-02-23 Jonathan Bravo Lopez backend/hp3900_config.c: Fix area of negative/slide scans for HP ScanJet G3110. 2009-02-22 Chris Bagwell * japi/Makefile.am: Add back -version-number to java library but also add V_REV. * backend/Makefile.am: Use variables to add libraries to backends instead of direct substitution. Easier to touch up variables on problem platforms then substitution. Also, add back in creating dll.d directory; missed during conversion. * backend/genesys_gl841.c: Portablity fixes for strict C compilers. 2009-02-21 Chris Bagwell * po/Makefile.am, configure.in: convert remaining directory, po, to use automake. * doc/Makefile.am: Get rid of unneeded gnu extensions to quieten down autotools warnings. * lib/getopt.c, lib/getopt1.c: Fix disabling getopt compile again. 2009-02-20 m. allan noah * backend/avision.c: backend v290, fix reader_pid and NVRAM option issues, by Mattias Ellert. * backend/fujitsu.c: backend v89, fi-4750 has no serial number support * doc/descriptions/fujitsu.desc: backend v89, remove 'MAC/TWAIN' text * doc/sane-fujitsu.man: backend v89 * backend/.cvsignore: ignore *.loT 2009-02-01 Mattias Ellert * tools/Makefile.am: Add missing liblib dependencies * frontend/scanimage.c: Restore alloca include order 2009-02-19 Chris Bagwell * configure.in, Makefile.am, backend/Makefile.am: Have configure define configdir so all makefiles can use it (fixes bug introduce with sanei converted to automake). Enable running testsuite during distcheck. * japa/Makefile.am: Convert japi to automake. 2009-02-19 Julien Blache * configure.in: add --enable-libusb_1_0 and check for libusb-1.0 using pkg-config. * sanei/sanei_usb.c: add support for libusb-1.0. * tools/sane-find-scanner.c: add support for libusb-1.0. * tools/check-usb-chip.c: compile as an empty file if libusb-1.0 is used. 2009-02-18 Chris Bagwell * configure.in, tools/Makefile.am: convert tools to automake. Add tools/openbsd to distribution package. * include/Makefile.am: include files should have been installed under sane/ directory when converted to automake. * doc/Makefile.am: Cleanup. Use automake's built in support to install docs within subdirectories. * Makefile.am, testsuite: Convert testsuite to automake. Waiting for test backend bugfix before enabling this to run during "distcheck". 2009-02-18 Julien Blache * configure.in, configure: raise avahi-client dependency to 0.6.24, following a needed bugfix in this release. * backend/net.c: do not lock the Avahi thread before stopping it. It looks like it's no longer necessary to do so. * frontend/saned.c: fix handling of Avahi server restart so it actually works. 2009-02-14 Chris Bagwell * configure.in, doc/Makefile.am, frontend/Makefile.am, include/Makefile.am, sanei/Makefile.am, tools/Makefile.am: convert frontend, include, and sanei directories to use automake. Only reference libsanei.la now. Use am_conditional to compile sanei_jpeg.lo. distcheck now runs sanei/wire_test. 2009-02-13 Chris Bagwell * Makefile.am, configure.in, doc/Makefile.am: Convert doc/Makefile to use automake. Main difference is that no longer installs man pages for backends that are not compiled. Moved install ownership of $top_srcdir docs to $top_srcdir's Makefile. 2009-02-06 Chris Bagwell * configure.in, backend/Makefile.am: Add back change to use -version-number even though its known not to work on some platforms (OS/2). -version-info results in a variety of version numbers in soname based on platform and would not be easily mappable back to values currently return by each backend's sane_init(). ltmain.sh will need to be hand patched for any known issues. 2009-02-08 Pierre Willenbrock * backend/genesys_gl841.c: Remove "init device" usb request. * backend/genesys_gl841.c, backend/genesys_devices.c, backend/genesys_low.h: Add support for uncalibrated scans in all modes for Visioneer Strobe XP300. Front- and backside are side-by-side, backside mirrored horizontally. * doc/descriptions/genesys.desc: Added XP300 to the genesys desc file. 2009-02-06 Chris Bagwell * configure.in, backend/Makefile.am: Add back in support for optionally linking in sanei_jpeg.lo under backend directory; but using configure and not with GNU make extensions. Move back to libtool's -version-info instead of -version-number because the later has known bugs on platforms such as OS/2. 2009-02-04 Chris Bagwell * configure.in, acinclude.ac: Cleanup autoconf 2.63 warnings. add cv_ prefix to cached variable. Remove AC_ARG_PROGRAM as automake already invokes this. When preferred AC_USE_SYSTEM_EXTENSIONS exists, use that instead of AC_GNU_SOURCE/AC_AIX/AC_MINUX. Cleanup socket detection more by creating only a single SOCKET_LIBS that contains list of all optional libraries for any socket related function. Make sure that checks for socket related functions use SOCKET_LIBS when searching. Check for getopt_long() and getopt.h. Make OS/2 use -no-undefined (same as windows). * ltmain.sh, m4/libtool.m4: Upgrade to libtool 1.5.26. * Makefile.am: Cleanups to match configure updates. * frontend/scanimage.c, include/Makefile.in, lib/getopt.c lib/getopt1.c, toosl/sane-desc.c, include/lgetopt.h: Rename internal getopt.h to lgetopt.h to allow using external getopt.h when it exists. This allows to go back to optionally compiling getopt()/getopt_long() and its prototypes and not have conflicts with external headers/symbols. 2009-02-04 Nicolas Martin * doc/sane-pixma.man doc/descriptions/pixma.desc: Added ImageClass MF4120 support in pixma backend docs. 2009-02-03 Julien Blache * tools/sane-desc.c: filter out unsupported/unknown models from output (udev/hal). 2009-02-02 Nicolas Martin * backend/pixma_common.c backend/pixma_common.h /backend/pixma_imageclass.c backend/pixma_mp150.c backend/pixma_mp730.c backend/pixma_mp750.c: Change ALIGN macro name to ALIGN_SUP for PPC compatibility. Some cosmetic changes to source code alignment. 2009-02-01 Alex Belkin * backend/xerox_mfp.c backend/xerox_mfp.h: Proper handling of parameters (to fix xsane crash). * backend/xerox_mfp.conf doc/descriptions/xerox_mfp.desc: Added Dell MFP 1815dn. 2009-02-01 Chris Bagwell * aclocalm4: fix prototype of internal strcasestr and usleep. * configure.in: Group all USB logic together and all SCSI logic together to aid understanding of what's no longer valid. Allow sharing enable/disable options between all USB drivers on multiple platforms. Combined CAM_LIBS and SCSI_LIBS since they are mutually exclusive. Skip some USB/SCSI checks when previous tests show it will always fail. * backend/Makefile.am, tools/Makefile.in: Combine CAM_LIBS and SCSI_LIBS. Add missing SOCKET_LIBS to epson2. Add USB_LIB. * saned.c: Add limits.h for PATH_MAX. 2009-02-01 Mattias Ellert * acinclude.m4, backend/Makefile.am: Restore the removed DYNAMIC_FLAG configuration. * acinclude.m4, sanei/sanei_jpeg.c: Don't compile sanei_jpeg if libjpeg is unavailable. * backend/canon_dr.c, backend/hs2p-scsi.c, backend/xerox_mfp.c: format fixes. * backend/hs2p.c: fix missing return. * backend/canon630u-common.c: avoid redefinition warning. 2009-01-31 Chris Bagwell * backend/Makefil.am: Add missing math library to coolscan2 and coolscan3. * acinclude.m4, configure.in: Define JPEG objects based on existence of JPEG library and not on the dc* backends alone since more backends then that use JPEG support. 2009-01-30 Chris Bagwell * frontend/saned.c: Replace usage of getgrouplist() with getgrent() and friends. getgrouplist is not posix and not on several platforms including cygwin. * acinclude.m4, configure.in: Put all libraries into their own *_LIB variables instead of $LIB so that we do not have to link in the world to all executables. Modified SANE_CHECK_U_TYPES to be a little more portable to platforms that use #define for u_* types. Create SANE_CHECK_BACKENDS macro so that PRELOADABLE_BACKENDS can also be valided. Auto-populated PRELAODABLE_BACKENDS when detect dlopen() won't work. Various protability cleanups. * backend/dll.c: Make dll-preload.c a .h since its an include and not compilable byitself. * frontend/Makefile.in, frontend/scanimage.c, include/laaloca.h, lib/Makefile.am, lib/alloca.c, strcasestr.c, tools/Makefile.in, tools/sane-desc.c: Convert lib/ to automake. Create a liblib.la for everyone to use and a libfelib.la for only frontend programs. Make all internal programs be prefixed with sanei_ as not to conflict with other programs libsane is linked in with that will also most likely create similar internal utils on problem platforms. * include/getopt.h, lib/getopt.c, lib/getopt1.c: Always compile and link in getopt_long() but prefix it with sanei_. Its easier to always use internal version then try to figure out what platforms support getopt_long() and what header files to use. * backend/Makefile.am: Convert backend makefile to automake. Initial version that is feature parity with original but uses specific rules instead of wildcards and only links in libraries/objs really required. Room for more cleanup of what's linked in once all makefiles have been converted to automake. 2009-01-29 Chris Bagwell * backend/epson2.c backend/pixma_bjnp.c, include/sane/sanei_backend.h, sanei/sanei_tcp.c, sanie/sanei_udp.c: Improve portablity by removing usage of MSG_WAITALL since not all platforms support that (cygwin). Default is to be blocking anyways. Changed usage of MSG_NOTWAIT to use fcntl() function as needed as well. * configure.in, lib/Makefile.in, lib/strcasestr.c: Add internal strcasestr() for platforms missing it (cygwin). * pixma_common.c: Make source match header prototype for picky compilers (cygwin). * backend/umax_pp_mid.c: Allow BACKEND_NAME to be filename yet debug to be SANE_DEBUG_UMAX_PP to match man page. 2009-01-28 Julien Blache * doc/descriptions/epson.desc: added :scsi data for the GT-7000 and Perfection1200S. Patch from Dieter Jurzitza. 2009-01-27 Louis Lagendijk * backend/pixma_bjnp.c: fixed bug that caused scanner discovery to fail when it encountered a point to point link (check data returned by getifaddrs() 2009-01-26 Chris Bagwell * configure.in: Add AM_MAINTAINER_MODE to disable regenerating configure files. This was behavior of Makefiles before automake and works around various timestamp issues. 2009-01-25 Pierre Willenbrock * backend/genesys_gl841.c: Change status code for locked head to SANE_STATUS_HW_LOCKED 2009-01-21 m. allan noah * doc/descriptions/canon_dr.desc: correct version, status = new * doc/descriptions/epjitsu.desc, doc/descriptions/fujitsu.desc: version * doc/descriptions/umax1220u.desc: correct status of 1600U * doc/descriptions/xerox_mfp.desc: status = new 2009-01-23 Julien Blache * doc/descriptions-external/epkowa.desc: Update for iScan 2.16.0, from Olaf Meeuwissen. 2009-01-22 Chris Bagwell * Makefile.in: Submit Makefile.in from Makefile.am submitted previously. * backend/epson.c, backend/epson_scsi.c, backend/epson2_net.c, backend/epson2_scsi.c, backend/genesys_gl646.c, backend/genesys_gl841.c, backend/lexmark_low.c, backend/rts88xx_lib.c, backend/umax_pp.c, backend/umax_pp_low.c, backend/umax_pp_mid.c, doc/Makefile.in, doc/sane-epson.man, doc/sane-epson2.man: Updated backends with EXTRA_* files to allow Makefile's to always pass in BACKEND_NAME to be fixed to backend name instead of filename; while still making sure that SANE_DEBUG_${BACKEND}* exist as documented in man pages. Add references to epson and epson2 man pages about SANE_DEBUG_EPSONx_SCSI and _NET options. Created an epson2 man page; based mostly on epson page. * backend/stubs.c: Currently, compiling stubs.c requires its own compile rule simply to pass in -DSTUBS. Since its always required, just define it in stubs.c 2009-01-22 Stéphane Voltz * doc/Makefile.in doc/descriptions/genesys.desc : added the rts8891 man page to the generation and install . added XP200 to the genesys desc file. 2009-01-21 Chris Bagwell * Makefile.am, lib/Makefile.in: Run all libcheck tests before failure and look for both static and shared libraries. Fixed $srcdir typo in lib/Makefile.in. 2009-01-21 m. allan noah * backend/canon_dr.[ch], backend/epjitsu.[ch], backend/fujitsu.[ch]: - don't export private symbols 2009-01-19 m. allan noah * doc/descriptions-external/brother2.desc: add MFC-7840W 2009-01-19 Nicolas Martin * pixma_imageclass.c: Fixed select_source message length, and typo bug in last update. Set MP4600 series for inverted checksumming. 2009-01-19 Stéphane Voltz * backend/genesys.c backend/genesys.conf.in backend/genesys.h backend/genesys_devices.c backend/genesys_gl646.c backend/genesys_gl841.c backend/genesys_low.h: add support for uncalibrated scans in all modes for Visioneer Strobe XP200. Add support for buttons for MD5345/HP2300 and XP200 2009-01-18 m. allan noah * doc/descriptions/unsupported.desc: everything reported from 2008-07 to 2009-01 2009-01-18 Pierre Willenbrock * backend/genesys_low.h: Fix prototype of update_hardware_sensors. Make half-ccd mode optional. * backend/genesys_gl841.c: Make dpihw depend on sensor pixel count instead of sensor resolution. Make some Canon LiDE 35 specific gpio handling conditional, add missing SCANMOD shift. Make half-ccd mode optional. Re-enable clock register setup from sensor struct, set SCANMOD. * backend/genesys_devices.c: Make half-ccd mode optional. Re-enable clock register setup from sensor struct, set SCANMOD. 2009-01-17 Nicolas Martin * pixma_mp150.c: Fixed "Busy mode" exit processing. 2009-01-17 Julien Blache * doc/descriptions/hp.desc: add SCSI identifiers for the ScanJet IIc, from Daniel Golle (sane-devel, 20061105). * frontend/scanimage.c: make batch mode create output files atomically. Patch by Simon Matter . 2009-01-16 Chris Bagwell * .cvsignore, Makefile.in, aclocal.m4, config.sub, configure, configure.in, backend/cvsignore, include/Makefile.in, include/sane/config.h.in, INSTALL, Makefile.am, missing: Convert top-level Makefile to be generated by automake. "dist" target now includes m4 directory. Updated "libcheck" target to look at dynamic libraries instead of static. 2009-01-16 Chris Bagwell * doc/Makefile.in, frontend/Makefile.in, include/Makefile.in, japi/Makefile.in, lib/Makefile.in, po/Makefile.in, sanei/Makefile.in, testsuite/Makefile.in, tools/Makefile.in, backend/Makefile.in: Updates to Makefiles to prepare for transition to automake. Fix a few bugs with "all" and "uninstall" targets to work when $(builddir) != $(srcdir). Added $(DESTDIR) to uninstall target. Made $(distdir) related to current directory to match automake. 2009-01-16 Pierre Willenbrock * backend/genesys.c, backend/genesys.h, backend/genesys_devices.c, backend/genesys_gl646.c, backend/genesys_gl841.c, backend/genesys_low.h: Add support for buttons on Canon LiDE 35/40/50. 2009-01-15 Nicolas Martin * doc/sane-pixma.man, doc/descriptions/pixma.desc, pixma_mp150.c, AUTHORS: Updated docs and comments in pixma backend. Added Dennis Lou to pixma backend authors 2009-01-13 Louis Lagendijk * AUTHORS: added myself as co-responsible for pixma backend 2009-01-15 Alessandro Zummo * backend/epson2.c: do not use request_identity2 with networked scanners, fixed generation of resolution list. 2009-01-13 Louis Lagendijk * backend/pixma_bjnp.c: send broadcasts from bjnp port to make firewalling easier 2009-01-13 Julien Blache * tools/sane-desc.c: replace opencoded device permissions and ownership by proper definitions. Group USB devices by vendor in the HAL FDI output. Replace obsolete SYSFS{} key by the newer ATTR{} key in the udev output. Add a new :scsi keyword for SCSI devices, add support for SCSI devices in the udev and HAL FDI outputs. * doc/descriptions/hp.desc, doc/descriptions/epson.desc: add :scsi keyword to a handful of SCSI scanners known to advertise themselves as type "Processor". All of the above based on a patch contributed by Dieter Jurzitza. 2009-01-10 m. allan noah * sanei/sanei_usb.c: rescan usb every time sanei_usb_init() is called - remove missing devices, add new devices to global lists - based on code from stef.dev@free.fr 2009-01-10 m. allan noah * backend/canon_dr.[ch], backend/canon_dr-cmd.h: backend v11 - send_panel() can disable too - add cancel() to send d8 command - call cancel() only after final read from scanner - stop button requests cancel 2009-01-10 Jeremy Johnson * backend/hs2p.c: replaced ulong with u_long, added static keyword for SANE_Status update_hs2p_data() 2009-01-06 Jonathan Bravo Lopez * backend/hp3900_sane.c, backend/hp3900_config.c, backend/hp3900_types.c, backend/hp3900.conf.in, doc/sane-hp3900.man, doc/descriptions/hp3900.desc: Added support for HP Scanjet G3110 scanner. 2009-01-05 Jeremy Johnson Added code to read_data() to pad image data to requested length and to zero out any garbage using information from sense data command. Added new MAINTENANCE_DATA struct and options to display scanner's maintenance/calibration statistics. *backend/hs2p.h - added enum CONNECTION_TYPES - added HS2P_DATA struct - added SENSE_DATA struct to struct HS2P_Device - added SANE_String_Const orientation_list[] - added macros isset_ILI() and isset_EOM() *backend/hs2p-scsi.h - replaced request_sense struct with SENSE_DATA struct - added #define DATA_TYPE_EOL (va_list sentinel) - added MAINTENANCE_DATA struct *backend/hs2p-saneopts.h - added OPT_ORIENTATION - added MAINTENANCE_DATA options *backend/hs2p.c - added MAINTENANCE_DATA options - added unused connType to attach() - added update_hs2p_data() to fill in options - added hs2p_open() and hs2p_close() - added get_hs2p_data() to read scanner data - added print_maintenance_data() - modified sane_control_options() to accommodate new maintenance options and to handle Portrait/Landscape option replaced adf_status macro with s->data.adf_status - modified sane_read() to correctly zero out the missing bytes on an incomplete read and to pad to the requested length *backend/hs2p-scsi.c - replaced request_sense() with get_sense_data() - added print_sense_data() - added SENSE_DATA *sdp to sense_handler() - modified read_data() to handler other data type codes/qualifiers - read_adf_status() now is replaced with call to read_data(fd,*buf,*bufsize,dtc,dtq) 2009-01-04 Nicolas Martin * doc/sane-pixma.man, doc/descriptions/pixma.desc: Updated doc for pixma backend (ImageClass) 2008-12-31 Stéphane Voltz * backend/genesys.c: restored warming up broken by a previuos commit 2008-12-28 Nicolas Martin * backend/pixma_imageclass.c: Change to include different checksumming for MF41xx and MF42xx series 2008-12-26 Alessandro Zummo * backend/epson2.c: changed version code, added protection from wrong settings. 2008-12-21 m. allan noah * backend/fujitsu.[ch]: backend v87 - accept null pointer as empty device name - track frontend reading sensor/button values to reload - deactivate double feed options if df-action == default 2008-12-21 Nicolas Martin * doc/descriptions/pixma.desc, doc/sane-pixma.man, backend/pixma_mp150.c: MP800/MP800R: Fixed bug and color planes shift issues, added TPU support for 48 bits images depth up to 1200 dpi. MP980: following a user feedback, declared as working. Doc and man pages updated. 2008-12-18 m. allan noah * backend/fujitsu.[ch]: backend v86 - get_pixelsize() sets back window ID for back side scans * doc/sane-fujitsu.man, doc/descriptions/fujitsu.desc: backend v86 2008-12-17 Julien Blache * backend/v4l.c: unmap mapped buffer in sane_cancel(). Use libv4l1 for v4l device access, buys us some support for v4l2 devices for free thanks to libv4l1, libv4l2 and libv4lconvert. * configure.in, configure: check for libv4l1 availability. 2008-12-15 Alex Belkin * AUTHORS, configure, configure.in, backend/Makefile.in, backend/xerox_mfp.c, backend/xerox_mfp.h, doc/Makefile.in, doc/descriptions/xerox_mfp.desc, doc/sane-xerox_mfp.man: Initial commit to xerox_mfp backend. 2008-12-12 Julien Blache * frontend/saned.c: add a data_portrange config file option to saned to specify a port range for the data connection. Based on a patch contributed by Oren Held. * backend/saned.conf.in: add the data_portrange option to the config file and rework the comments. * doc/saned.man: document the data_portrange option. 2008-12-11 Stéphane Voltz * backend/rts8891.c doc/sane-rts8891.man doc/descriptions/rts8891.desc: scan register setting fix, documentation update 2008-12-10 m. allan noah * backend/fujitsu.[ch]: backend v85 - round pixels_per_line down to arbitrary limits for fi-4990 & fi-4860 - fi-4860 returns random garbage to serial number queries - initialize *info to 0 in sane_control_option() * doc/descriptions/fujitsu.desc: backend v85, improve status/comments * frontend/scanimage.c: initialize *info, fix buffer overwalk 2008-12-10 m. allan noah * backend/canon_dr.c: backend v10 - add all documented request sense codes to sense_handler() - fix color jpeg (remove unneeded BGR to RGB swapping code) - add macros for LUT data * backend/canon_dr.conf.in: add DR-2080C usb ID * doc/descriptions/canon_dr.desc: backend v10, add DR 3020 2008-12-08 m. allan noah * backend/canon_dr.[ch], backend/canon_dr-cmd.h: backend v9 - add rollerdeskew and stapledetect options - add rollerdeskew and stapledetect bits to ssm_df() * doc/descriptions/canon_dr.desc: backend v9 2008-12-08 m. allan noah * backend/avision.c: backend v289 - fix sending SIGTERM when reader_pid == 0 2008-12-07 m. allan noah * backend/canon_dr.[ch], backend/canon_dr-cmd.h: backend v8 - rename read/send_counter to read/send_panel - enable control panel during init - add options for all buttons - call TUR twice in wait_scanner(), even if first succeeds - disable rif - enable brightness/contrast/threshold options * doc/descriptions/canon_dr.desc, doc/sane-canon_dr.man: backend v8 2008-12-06 Louis Lagendijk * backend/pixma_bjnp.c pixma_bjnp_private.h: replaced getlogin/getenv by getpwuid(geteuid) 2008-12-06 Louis Lagendijk * backend/pixma_bjnp.c pixma_bjnp_private.h: On Ubuntu getlogin() returns NULL. So we now first try getlogin() and if that fails, we try getenv("USER") and if that fails we use a default user string 2008-12-03 Stéphane Voltz * backend/rts8891.c backend/rts8891_low.c: possible fix for failed scan at 300 and 1200 dpi for HP4400, sensor type 2. 2008-12-01 Julien Blache * sanei/sanei_usb.c: fix assignment in if clause, reported on sane-devel. 2008-11-30 Nicolas Martin * doc/descriptions/pixma.desc, doc/sane-pixma.man: MP620 supported by pixma backend confirmed, doc and man pages updated. 2008-11-29 m. allan noah * backend/canon_dr.[ch]: backend v7 - jpeg support (size rounding and header overwrite) - call object_position(load) between pages even if buffering is on - use request sense info bytes on short scsi reads - byte swap color BGR to RGB - round image width down, not up - round image height down to even # of lines - always transfer even # of lines per block - scsi and jpeg don't require reading extra lines to reach EOF - rename buffer option to buffermode to avoid conflict with scanimage - send ssm_do and ssm_df during sane_start - improve sense_handler output * doc/descriptions/canon_dr.desc: backend v7 2008-11-29 m. allan noah * backend/canon_dr.[ch], backend/canon_dr-cmd.c: backend v6 - fix adf simplex - rename ssm_duplex to ssm_buffer - add --buffer option - reduce inter-page commands when buffering is enabled - improve sense_handler output - enable counter option - drop unused code * doc/descriptions/canon_dr.desc: backend v6 2008-11-26 m. allan noah * backend/*.[ch]: nearly every backend used V_MAJOR instead of SANE_CURRENT_MAJOR in sane_init() * backend/snapscan.c: remove EXPECTED_VERSION check since new SANE standard is forward compatible 2008-11-25 m. allan noah * backend/canon_dr.[ch], backend/canon_dr-cmd.c, doc/descriptions/canon_dr.desc: backend v5 - remove EOF ejection code - add SSM and GSM commands - add dropout, doublefeed, and jpeg compression options - disable adf backside - fix adf duplex - read two extra lines (ignore errors) at end of image - only send scan command at beginning of batch - fix bug in hexdump with 0 length string - DR-7580 support 2008-11-25 Nicolas Martin * backend/pixma.c: Fixed regression bug at end of scan for all PIXMA, noticeable in multipage ADF scan. 2008-11-23 Nicolas Martin * backend/pixma_mp150.c, sane-backends/po/sane-backends.fr.po, doc/descriptions/pixma.desc, doc/sane-pixma.man: Support for MP630 in pixma backend. Updated docs for MP630, and network interface to various PIXMA models in pixma.desc. Fixed some typos in French translations. 2008-11-20 Stéphane Voltz * backend/rts8891.c: fixes 'commit scan value' for HP4400. 2008-11-20 Stéphane Voltz * backend/rts8891.c: calibration fixes for HP4400 sensor type 3. 2008-11-18 Alessandro Zummo * backend/epson2.c: fixed bug when an extended command was used with D8 level scanners. bug reported by Kare Sars 2008-11-17 Gerhard Jaeger * doc/descriptions-external/epkowa.desc: iscan 2.14.0 updates on behalf of olaf.meeuwissen@avasys.jp 2008-11-12 Stéphane Voltz * backend/rts8891.c: sensor detection fix. Lamp setting change for HP4400 sensor type 3. 2008-11-11 m. allan noah * backend/canon_dr.c: backend v4 - eject document when sane_read() returns EOF 2008-11-09 m. allan noah * backend/canon_dr.[ch], backend/canon_dr-cmd.h, doc/sane-canon_dr.man, backend/canon_dr.conf.in, doc/descriptions/canon_dr.desc: - New Canon DR-series backend v3 - support all modes and resolutions of DR-9080C - advanced options (MF detection, compression) not yet supported - other larger models believed similar, smaller ones unknown * doc/descriptions/unsupported.desc: remove Canon DR-series machines 2008-11-07 m. allan noah * backend/fujitsu.c: backend v84 - round lines down to even number to get even # of total bytes - round binary bpl and Bpl down to byte boundary 2008-11-06 m. allan noah * backend/fujitsu.c: backend v83 - round binary bpl and Bpl up to byte boundary - use s->params instead of user data in set_window() - read_from_scanner() only grabs an even number of lines 2008-10-31 Louis Lagendijk * Pixma backend (pixma.c/pixma_common.c/pixma_io_sanei.c/pixma_bjnp.c): bjnp tcp connection is now de-activated after initialization and re-activated at start of scanning. If we do not do this, the scanner closes its side of the connection after 30 secods of inactivity. Added a maximum to the retry of select calls when signals are received Added blocksize detection for tcp read_bulk call 2008-11-04 m. allan noah * sanei/sanei_usb.c, include/sane/sanei_usb.h: - shorten debug lines to fit in 80 chars - add sanei_usb_clear_halt() required by upcoming canon_dr backend 2008-10-31 m. allan noah * backend/fujitsu.[ch], backend/fujitsu-scsi.h, backend/fujitsu.conf.in, doc/descriptions/fujitsu.desc: backend v82 - improved front-side endorser vpd detection - send scanner_control_ric during sane_read of each side - add fi-6770A and fi-6670A USB ID's 2008-10-31 Louis Lagendijk * backend/pixma_bjnp.c: corrected small typo 2008-10-31 Louis Lagendijk * backend/pixma_bjnp.c: Added retry to select() calls when they get interrupted by a signal. This solves problems in xsane after a first scan completed. There is still an issue that scanner closes tcp connection after 30 seconds of inactivity backend/pixma_bjnp.c backend/bjnp_private.h Marked local functions as static and removed them from ..private.h 2008-10-31 Stéphane Voltz * backend/rts8891.c backend/rts8891_low.c backend/rts8891_low.h: added 600 and 1200 dpi scan for HP4400 and HP4470 with 'xpa' sensor. 2008-10-25 Nicolas Martin * backend/pixma_mp150.c, doc/sane-pixma.man doc/descriptions/pixma.desc: Tweaks for TPU scan at 4800 dpi, and MP810 at 2400 dpi (now works). Updated docs around that. 2008-10-25 Louis Lagendijk * Added configuration file support for pixma backend Currently used only for configuration of network scanners. 2008-10-22 Nicolas Martin * backend/pixma.c, backend/pixma_io_sanei.c, backend/pixma_mp150.c: TPU scanning in 48 bits mode now works fine on MP970, with Xsane or scanimage. Changed also minimum to 300 dpi for TPU scan, so that preview now works in Xsane. Added Legal paper dimensions (14") to ADF scan for MX850 and MX7600. The rest is cosmetic only. 2008-10-21 Louis Lagendijk * Added #ifdef around definitions of MIN and MAX in pixma_common.h - Re-enable includes in pixma_bjnp.c as they are needed on Freebsd - Replaced getaddrinfo by gethostbyname in pixma_bjnp.c as this is supported on OS/2 as well 2008-10-21 m. allan noah * doc/descriptions/fujitsu.desc: backend v81, fix fi-6240 usb id 2008-10-20 m. allan noah * backend/fujitsu.[ch], backend/fujitsu-scsi.h: backend v81 - increase USB timeouts - enable get_pixelsize() to update scan params after set_window() - remove even_scan_line hack 2008-10-19 Nicolas Martin * configure.in, configure, backend/pixma_bjnp.c, backend/pixma_bjnp.h, backend/pixma_bjnp_private.h, pixma.h, doc/descriptions/pixma.desc: From Louis Lagendijk for bjnp protocol on pixma backend, add a header check for ifaddrs.h (necessary on different platforms BeOS, OS/2, ...) Increment pixma backend version number. 2008-10-15 Stéphane Voltz * backend/rts8891.c: added 300 dpi scan for HP4400 2008-10-15 Stéphane Voltz * backend/rts8891.c: fixed HP4400 sensor detection, added 150 dpi scan mode for HP4400 2008-10-14 Nicolas Martin * backend/pixma*.c, backend/pixma*.h (all pixma backend files), doc/sane-pixma.man, doc/description/pixma.desc, backend/Makefile.in Added 3 new files backend/pixma_bjnp.c, backend/pixma_bjnp.h, backend/pixma_bjnp_private.h: - Thanks to Louis Lagendijk, added an implementation of Canon's BJNP network protocol to the pixma backend, for scanning with PIXMA devices over a LAN network. - Fix for ADF scan reset session when ADF is loaded with more pages to scan than the frontend requests. - Updated pixma backend documentation and file headers. 2008-10-09 m. allan noah * doc/descriptions/fujitsu.desc, doc/sane-fujitsu.man: backend v80 2008-10-08 m. allan noah * backend/fujitsu.c: backend v80 - front-side endorser uses data ID 0x80 2008-10-06 Stéphane Voltz * backend/rts8891.c backend/rts8891.h: fixed configuration option bug 2008-10-04 Nicolas Martin * backend/pixma.c, backend/pixma.h, backend/pixma_common.c, backend/pixma_io_sanei.c, backend/pixma_mp150.c, doc/sane-pixma.man, doc/description/pixma.desc: MP970 scanning improvements, up to 4800 dpi. On the way soon, network BJNP protocol designed by Louis Lagendijk to be added to CVS. MX7600 reported to work fine with the backend. ADF scanning: - improved for latest PIXMAs like MX850, MX310. - bug fix in Sane_start, when scanning several pages with ADF. ADF DUPLEX scanning: - new code for ADF Duplex, (to be tested) based on a MX850 Snoop. Changes might fit also MP830 (To be confirmed). TPU scanning: - MP970 TPU scanning: Protocol works, get scanned TPU images with 48 bits to 24 bits conversion, full 48 bit version yet to be debugged. 2008-10-03 m. allan noah * backend/epjitsu.[ch]: backend v17: - increase scan height ~1/2 inch due to head offset - change page length autodetection condition 2008-10-03 Stéphane Voltz * include/sane/sane.h: added a SANE_CURRENT_MINOR define 2008-10-02 Stéphane Voltz * backend/rts8891.c backend/rts8891.h backend/rts8891_low.h: add a 'sensornumber' option to override detected sensor model 2008-10-02 m. allan noah * doc/descriptions-external/epkowa.desc: - iscan 2.12.0 updates from olaf.meeuwissen@avasys.jp 2008-10-01 m. allan noah * doc/descriptions/fujitsu.desc, backend/fujitsu.conf.in, backend/fujitsu.[ch], backend/fujitsu-scsi.h: backend v79 - add usb ids for several models - print additional hardware capability bits - detect front-side endorser - disable endorser-side controls if only one side installed - add quirks for fi-6x70 2008-10-01 m. allan noah * backend/epjitsu.[ch]: backend v16: - split fill_frontback_buffers_S300 into 3 functions - enable threshold-curve option - add 1-D dynamic binary thresholding code - remove y-resolution option - pad 225x200 data to 225x225 2008-10-01 Gerhard Jaeger * doc/descriptions/plustek.desc: Added entry for UT12 devid 0x0013 * doc/descriptions/unsupported.desc: Removed entry for UT12 devid 0x0013 * doc/descriptions/gt68xx.desc: Added entry for OpticSlim 2400plus * backends/gt68xx.conf.in: Added entry for OpticSlim 2400plus 2008-09-26 Stéphane Voltz * backend/rts8891_low.c: fix HP4400 head parking 2008-09-25 m. allan noah * backend/epjitsu.[ch], backend/epjitsu-cmd.h: backend v 13, 14 and 15: - add working page-height control - add working brightness, contrast and threshold controls - add disabled threshold curve and geometry controls - move initialization code to sane_get_devices, for hotplugging - support S300 on USB power - support S300 225x200 and 600x600 scans - support for automatic paper length detection (parm.lines = -1) - expose hardware buttons/sensors as options for S300 * doc/descriptions/epjitsu.desc: change S300 status to good 2008-09-25 m. allan noah * include/sane/saneopts.h: underscore not allowed in option names * frontend/scanimage.c: fix broken unknown length (-1) binary scans, fix invalid tiff height with unknown length scans, fix compiler warn 2008-09-20 Stéphane Voltz * backend/rts8891.c: fix typo in HP4400 variant detection 2008-09-20 Stéphane Voltz * backend/rts8891.c backend/rts8891_low.c backend/rts8891_low.h: more fix for HP4400 head parking, 75 dpi support for another HP4400 sensor variant, better variant detector and initial setting 2008-09-13 Nicolas Martin * backend/pixma_mp150.c, doc/sane-pixma.man, doc/description/pixma.desc: Changes in pixma backend for PIXMA MP970 at 4800 dpi (to be continued). Declare MX7600 but yet untested. 2008-09-11 Stéphane Voltz * backend/rts8891.c backend/rts8891_low.c backend/rts8891_low.h: fix parking for HP4400, tune dark calibration for HP4400 2008-09-07 Nicolas Martin * backend/pixma_mp150.c backend/pixma_imageclass.c backend/pixma_common.h doc/sane-pixma.man doc/descriptions/pixma.desc: - Updated doc for MF4140, MF4150 and MP810 - Remove white lines previously padded to image bottom for CCD sensors - Modified scan area adjustments to enhance frontend area selection fit but to be confirmed that it does not bring backward compatibility issues. 2008-09-03 Stéphane Voltz * backend/rts8891.c backend/rts8891_low.c backend/rts8891_low.h backend/rts88xx_lib.c: minor changes for hp4400 support 2008-09-02 Nicolas Martin * backend/pixma_mp150.c: Some tweaks to have PIXMA MP810 supported by the pixma backend. 2008-08-29 Stéphane Voltz * backend/genesys.c backend/genesys_gl646.c backend/genesys_devices.c: - move to configuration framework - fix GL646 GPO value usage - HP2400/G2410 tidbits - don't reset ASIC anymore 2008-08-27 m. allan noah * doc/descriptions/fujitsu.desc: mark fi-6230 and fi-6240 complete 2008-08-26 m. allan noah * backend/fujitsu.c, doc/descriptions/fujitsu.desc: backend v78 - recent model names (fi-6xxx) don't end in 'C' - simplify flatbed area overrides - call scanner_control to change source during sane_start 2008-08-26 m. allan noah * backend/fujitsu.c, backend/fujitsu-scsi.h: backend v77 - override flatbed maximum area for fi-6230C and fi-6240C - set PF bit in all mode_select(6) CDB's - set SANE_CAP_INACTIVE on all disabled options - fix bug in mode_select page for sleep timer 2008-08-25 Gerhard Jaeger * backend/plustek-usb.c backend/plustek-usbdevs.c: Added preliminary support for Visioneer XP100 Tweaked entry for LM9831 version of Plustek UT12 * backend/plustek.c: Bumped build number * doc/sane-plustek.man doc/plustek/Plustek-USB.changes: Update * doc/descriptions/plustek.desc: Added entry for Strobe XP100 * doc/descriptions/unsupported.desc: Removed entry for Strobe XP100 2008-08-21 Stéphane Voltz * backend/rts8891.c: calibration updating and 75 dpi scan for hp400 2008-08-23 m. allan noah * doc/descriptions/fujitsu.desc: set lots of scanners to complete :) 2008-08-21 Stéphane Voltz * backend/rts8891.c backend/rts8891_devices.c backend/rts8891_low.c backend/rts88xx_lib.c: add sensor type field and use it in geometry detection. 2008-08-14 Stéphane Voltz * backend/rts8891.c backend/rts88xx_lib.c: fix for hp4400 init 2008-08-13 m. allan noah * backend/fujitsu.[ch]: backend v76 - add independent maximum area values for flatbed - override said values for fi-4220C, fi-4220C2 and fi-5220C * doc/descriptions/fujitsu.desc: bump version, add 2 untested scanners 2008-08-07 Rene Rebe * backend/fujitsu.c, backend/fujitsu.conf.in, doc/descriptions/fujitsu.desc: backend v75 - added fi-6230 ID, updated fi-6130, fi-6230 status 2008-08-05 Julien Blache * frontend/saned.c: initialize runas_{u,g}id to 0 instead of -1; initialize ngroups to 0. 2008-08-04 Julien Blache * frontend/scanimage.c: fetch_options(), actually get option descriptor 0 before attempting to get the value of option 0. 2008-08-04 Stéphane Voltz * backend/umax_pp.c backend/umaxp_pp.h backend/umax_pp.conf.in: use configuration framework 2008-08-02 m. allan noah * backend/fujitsu.c, backend/fujitsu-scsi.h: backend v74 - replace global scsi blocks with local ones in each function 2008-07-31 Julien Blache * frontend/saned.c: set supplemental group list in addition to setting euid and egid. Reported by Cameron Hutchison. 2008-07-28 Julien Blache * frontend/saned.c: call setegid() before seteuid(), aka while we're still root. Patch from Nick Andrew . 2008-07-27 m. allan noah * backend/fujitsu.c, backend/fujitsu.conf.in, doc/descriptions/fujitsu.desc: backend v73 - add fi-6130 usb ID - correct overscan dimension calculation - provide correct overscan size overrides for fi-5110C and fi-4x20C2 - fi-5750C can't handle 10 bit LUT via USB 2008-07-25 m. allan noah * AUTHORS: Earle has commit perms * doc/descriptions/unsupported.desc: add Lexmark X7170 * include/sane/sanei_config.h: remove typedef so doxygen will shutup 2008-07-25 m. allan noah * include/sane/saneopts.h: separate x-resolution from resolution * backend/abaton.c, backend/artec.c, backend/canon-sane.c, backend/epjitsu.c, backend/fujitsu.c, backend/microtek2.c, backend/sp15c.c, backend/umax.c: use SANE_NAME_SCAN_RESOLUTION instead of SANE_NAME_SCAN_X_RESOLUTION, to avoid ui change 2008-07-20 Julien Blache * backend/net.c: sane_control_option: when the frontend doesn't care (info == NULL) and the remote backend asks for a reload, perform the reload. Hopefully this brings the behaviour back in line with standard backends. 2008-07-15 Nicolas Martin * backend/pixma_mp150.c: Changed page scan end condition to handle correctly PIXMA ADF. 2008-07-14 Julien Blache * doc/*.man: Fix hyphen vs. minus sign issues in the manpages, breaking copy/paste of command-lines and options in UTF-8 environments. 2008-07-13 m. allan noah * backend/fujitsu.[ch]: backend v72, - use mode_sense to determine background color support - remove fi-5900 background color override 2008-07-13 m. allan noah * backend/fujitsu.[ch]: backend v71, - disable overscan option if vpd does not tell overscan size - fi-5110EOX crops scan area based on absolute maximum, not paper - fi-5330C and fi-5650C can't handle 10 bit LUT via USB - fi-5900 has background color, though it reports otherwise 2008-07-10 Stéphane Voltz * sanei/sanei_config.c: handle cases where config or callback function is NULL * backend/rts8891.c: use new configuration parsing framework 2008-07-10 Stéphane Voltz * include/sane/sanei_config.h: doxygen fix for new function 2008-07-10 Stéphane Voltz * backend/sanei_constrain_value.c: add support for SANE_Bool in sanei_check_value * backend/Makefile.in: add sanei_constrain_value.lo to libsane 2008-07-10 Stéphane Voltz * backend/sanei_constrain_value.c: add support for SANE_Bool in sanei_check_value * backend/Makefile.in: add sanei_constrain_value.lo to libsane dependencies * sanei/Makefile.in: add makedepend for .lo files * include/sane/sanei_config.h sanei/sanei_config.c: add configuration parsing framework 2008-07-05 m. allan noah * backend/fujitsu.[ch]: backend v70, - fix bug in sane_get_parameters (failed to copy values) - autodetect jpeg duplex interlacing mode by inspecting scan width 2008-07-05 Nicolas Martin * backend/pixma.c: Fix in sane_control_option () for PIXMA ADF scan. Change malformed test conditions for ADF/Duplex scan source. Updated PIXMA MX850 description. 2008-07-03 m. allan noah * backend/fujitsu.[ch]: backend v69, support hot-unplugging scanners 2008-07-02 m. allan noah * backend/fujitsu.[ch], backend/fujitsu-scsi.h: backend v68, - add halftone type and pattern options - support M3097G with IPC and CMP options via modified VPD response * doc/descriptions/fujitsu.desc, doc/sane-fujitsu.man: backend v68, - manpage cleanup 2008-07-01 m. allan noah * backend/fujitsu.[ch], backend/fujitsu-scsi.h: backend v67, - add IPC/DTC/SDTC options - call check_for_cancel() in sane_cancel, unless reading flag is set 2008-06-28 Nicolas Martin * backend/pixma_mp150.c: Removed "experimental" flag for PIXMA MX850. 2008-06-26 m. allan noah * backend/fujitsu.[ch], backend/fujitsu-scsi.h: backend v66, - restructure double feed detection options for finer-grained control - add endorser side option - prevent init_interlace() from overriding init_model() - simplify sane_start() and fix interlaced duplex jpeg support - simplify sane_read() and add non-interlaced duplex jpeg support - removed unused code 2008-06-26 m. allan noah * backend/test.[ch]: check for options being controlled before loading 2008-06-25 Nicolas Martin * backend/pixma_imageclass.c: Patch from Sam Varshavchik to deal with libusb 64 read issue, for ImageClass MFPs. 2008-06-24 m. allan noah * backend/fujitsu.[ch], backend/fujitsu-scsi.h, doc/descriptions/fujitsu.desc, doc/sane-fujitsu.man: backend version 65, add endorser support, add quirks for fi-4990 2008-06-24 Julien Blache * backend/net.c: do not reload options behind the frontend's back in sane_control_option; rather return SANE_STATUS_INVAL and warn that the frontend is buggy. Fetch option descriptors in sane_open() so a GET_VALUE on option 0 can still work without having to get option descriptor 0 beforehand. * frontend/saned.c: rework error/exit path in process_request() to properly terminate the Avahi thread when in debug mode. 2008-06-22 Julien Blache * frontend/saned.c: terminate child processes before exiting in debug mode. 2008-06-19 Ilia Sotnikov * backend/hp5590.c: Fixed segmentation fault on invalid option passed to sane_get_option_descriptor(), sane_control_option() (thanks to Albert Cervera i Areny) 2008-06-19 Ilia Sotnikov * backend/hp5590.c, backend/hp5590_cmds.c, backend/hp5590_cmds.h * backend/hp5590_low.c, backend/hp5590_low.h * doc/descriptions/hp5590.desc, doc/sane-hp5590.man: Added support for HP ScanJet 4570c (thanks to Markham Thomas) 2008-06-15 Nicolas Martin * doc/sane-pixma.man, * backend/pixma.c, backend/pixma_mp150.c, backend/pixma_sane_options.c * sane-backends/po/sane-backends.??.po: Remove "experimental" in pixma buttons options and text. Fix some French translation typos, and update man page. 2008-06-10 Stéphane Voltz * backend/sanei_constrain_value.c: add support for arrays of SANE_Word in sanei_constrain_value * backend/umax_pp.c: remove now unneede 'hand made' constrain on gamma tables 2008-06-09 Stéphane Voltz * backend/rts8891.c: fix for model with 'XPA' sensor 2008-06-06 Julien Blache * backend/net.c: lock the Avahi thread before stopping it and tearing down the Avahi objects. 2008-06-03 Stéphane Voltz * backend/genesys_gl646.c: fixed wrong offset for start of scanarea for MD6471 scanner. 2008-05-30 m. allan noah * backend/fujitsu.[ch], backend/fujitsu-scsi.h, doc/descriptions/fujitsu.desc: backend version 64, use model and serial to build sane.name (idea from Ryan Duryea), allow both serial_name and device_name to sane_open scanner, simulate missing VPD data for M3097G, probe scanner for color interlacing mode instead of hardcoding, other minor cleanups 2008-05-29 Nicolas Martin * doc/sane-pixma.man: man update. * backend/pixma_io_sanei.c: update status types, and take into account new ones. 2008-05-15 Mattias Ellert * backend/hp3900_sane.c: Correct typos. * po/Makefile.in: add backend/rts8891.c to localization. * po/sane-backends.sv.po: update Swedish localization. 2008-05-27 m. allan noah * frontend/scanimage.c: add %n for newline to -f option 2008-05-26 Jonathan Bravo Lopez * po/sane-backends.es.po: added spanish translation about status strings * po/Makefile.in: added backend/sane_strstatus.c to SRC_FILES section. 2008-05-26 Rene Rebe * backend/avision.{c,h}: merged latest upstream SVN changes, bug fixes and device ID updates, including fixing uninitialized variables due former defect merge and exposure control for film / dia scanners 2008-05-26 Stéphane Voltz * backend/sane_strstatus.c: added new status and internationalization support * frontend/scanimage.c, frontend/tstbackend.c: added handling of status SANE_STATUS_WARMING_UP * backend/pnm.c: added 2 more virtual devices, one that is hardware locked, and another that always do warming before scan. 2008-05-26 Stéphane Voltz * backend/rts8891.[ch], backend/rts8891_low.[ch], backend/rts88xx_lib.[ch]: renamed per frontend struct, made use of new well-known option groups. Added warming-up handling in sane_start(). 2008-05-22 Stéphane Voltz * backend/genesys.c: changed top of scan area detection for MD6228/MD6471 models 2008-05-23 Jonathan Bravo Lopez * backend/hp3900_sane.c: setting appropriate capabilities to some options. 2008-05-22 Jonathan Bravo Lopez * backend/hp3900_sane.c: use 1.1.0 well-known option group strings. * backend/hp3900_config.c, backend/hp3900_debug.c, backend/hp3900_rts8822.c, backend/hp3900_types.c, backend/hp3900_usb.c: Fixed reference position detection for HP3800/HPG2710 scanners. Code clean up. 2008-05-22 Stéphane Voltz * backend/rts8891.c: minor tweak for scanjet 4470cx 2008-05-21 m. allan noah * include/sane/saneopts.h: add sane 1.1.0 well-known button options * backend/fujitsu.c: v1.0.63, use sane 1.1.0 well-known option names for some buttons, remove 'button-' from others 2008-05-20 m. allan noah * backend/fujitsu.[ch], backend/fujitsu-scsi.h: v1.0.62, check for all supported scsi commands, use well-known option group strings from saneopts.h, rename pagewidth to page-width to meet sane 1.1.0 spec, same for height, add unused get_window() * doc/descriptions/fujitsu.desc: increase version to 1.0.62. 2008-05-20 m. allan noah * include/sane/sane.h: add SANE_STATUS_WARMING_UP and SANE_STATUS_HW_LOCKED, remove SANE_CAP_ALWAYS_SETTABLE, add SANE_FRAME_XML, improve comments on other new frame types. * include/sane/saneopts.h: add common option groups, add new page-width and page-height well-known options 2008-05-20 m. allan noah * backend/qcam.c, backend/v4l.c, frontend/tstbackend.c: remove undocumented and otherwise unused SANE_CAP_ALWAYS_SETTABLE 2008-05-19 Stéphane Voltz * backend/rts8891.c: 150/300 dpi shading calibration fixes for 'XPA' sensor HP4470 models 2008-05-16 m. allan noah * backend/fujitsu.c: inspect correct bool to enable prepick mode 2008-05-15 m. allan noah * doc/descriptions/cardscan.desc, doc/descriptions/epjitsu.desc, doc/descriptions/hp3900.desc, doc/descriptions/hp5590.desc, doc/descriptions/hpljm1005.desc, doc/descriptions/hs2p.desc: remove 'new' tag from sane 1.0.19 backends * doc/descriptions/fujitsu.desc, backend/fujitsu.conf.in: add fi-5530C2 usb ID * backend/cardscan.c: correct version number * backend/epjitsu.conf.in: improved firmware directions * backend/fujitsu.[ch]: update to backend v 1.0.61 2008-05-15 Mattias Ellert * doc/sane-genesys.man, doc/sane-lexmark.man, doc/sane-microtek2.man, doc/sane-mustek_pp.man, doc/sane-pixma.man, doc/sane-rts8891.man, doc/sane-umax_pp.man: Man page fixes * doc/descriptions-external/hp_rts88xx.desc: removed since its successor rts8891 is now included in the SANE distribution 2008-05-15 Mattias Ellert * backend/agfafocus.h, backend/artec_eplus48u.c, backend/artec_eplus48u.h, backend/avision.h, backend/coolscan.h, backend/hp-handle.c, backend/hp3500.c, backend/microtek2.h, backend/mustek.c, backend/mustek.h, backend/pie.c, backend/pixma.c, backend/plustek-pp.h, backend/plustek.c, backend/plustek.h, backend/plustek_pp.c, backend/snapscan.c, backend/snapscan.h, backend/sp15c.h, backend/tamarack.h, backend/test.c, backend/test.h, backend/u12.c, backend/u12.h, backend/umax.c, backend/umax.h, include/sane/sanei_thread.h, sanei/sanei_thread.c: Fix for bug #306751: sanei-thread with pthreads on 64 bit 2008-05-12 Stéphane Voltz * backend/rts8891.c 150/300 dpi mode fixes for 'XPA' sensor HP4470 models 2008-05-08 Alessandro Zummo * AUTHORS: added myself :) 2008-05-07 Julien Blache * doc/sane-config.man: Fix man warning. * backend/dll.c: look for dll.conf snippets in $configdir/dll.d; this is a facility for external backends to automatically add their backends to the dll backend configuration without mucking with dll.conf. * backend/Makefile.in: create $configdir/dll.d. 2008-05-07 Nicolas Martin * backend/pixma_imageclass.c, doc/descriptions/pixma.desc: USB IDs updates in imageClass backend (pixma), by Dennis Lou. 2008-05-07 Jochen Eisinger * doc/sane-mustek_pp.man: fix catman warning. Reported by Raymond Chen. 2008-05-05 Earle Philhower * doc/sane-sm3840.man: Add lineart and halftone options to page * backend/sm3840*: Remove 64-bit compile warnings 2008-04-05 Stéphane Voltz * backend/rts8891.c backend/rts8891_low.c doc/descriptions/rts8891.desc: 150 dpi mode fix for 'XPA' sensor HP4470 models, description update 2008-05-05 Nicolas Martin * doc/sane-pixma.man, doc/descriptions/pixma.desc: Description and man updates for PIXMA MX300. 2008-05-04 Nicolas Martin * backend/pixma_imageclass.c, doc/descriptions/pixma.desc: Code cleanup patch to imageClass backend (pixma) by Dennis Lou. 2008-05-03 Julien Blache * backend/epson2.c: initialize s->fd to -1 once the struct Epson_Scanner is allocated and zeroed out in attach(). Otherwise the backend sends inquiry data to fd 0, which it obviously shouldn't do; this breaks scanning over saned. * doc/net.tex: replace occurrences of SANE_STATUS_SUCCESS with SANE_STATUS_GOOD, as SANE_STATUS_SUCCESS doesn't exist. 2008-04-28 m. allan noah * backend/epjitsu.[ch]: backend v1.0.12, fix double free bug 2008-04-27 m. allan noah * sanei/sanei_usb.c: allow sanei_usb_init() to run once per second * backend/fujitsu.c: backend v1.0.60, relocate call to sanei_usb_init(), free sane_devArray before calloc'ing a new one 2008-04-22 m. allan noah * backend/fujitsu.c, backend/fujitsu.conf.in: backend v1.0.59, add fi-6140 PID, and fi-6x40 color mode * doc/descriptions/fujitsu.desc: add fi-6140 PID, mark as 'good' * doc/descriptions/epjitsu.desc: add S300M, mark as 'untested' 2008-04-22 Nicolas Martin * backend/pixma_mp150.c: Updated pixma backend to have MP970 (tested), and probably other CCD sensor MPs, working for Grayscale scan. 2008-04-21 Nicolas Martin * backend/pixma.c, backend/pixma_common.c, backend/pixma_rename.h doc/sane-pixma.man, doc/descriptions/pixma.desc (new) backend/pixma_imageclass.c, backend/Makefile.in: Thanks to Dennis Lou, who adapted the pixma backend to add support for Canon ImageCLASS series, fully tested for MF4270, and includes PIDs declarations for other ImageCLASS devices, yet to be tested. Fixes also a bug for ADF and ADF Duplex scan source selection. 2008-04-20 m. allan noah * backend/fujitsu.h: remove #define SANE_FRAME_JPEG 2008-04-19 m. allan noah * backend/fujitsu.[ch], backend/fujitsu-scsi.h: backend v1.0.58, rename page code 32 to 'unknown', compile if NDEBUG is set, proper async sane_cancel support, re-enable JPEG support various functions rewritten (shorter, more clear) * doc/descriptions/fujitsu.desc: add new fi-6xxx machines 2008-04-18 Alessandro Zummo * backend/epson2.c: fixed attach() error path. 2008-04-14 Nicolas Martin * backend/pixma_mp150.c, backend/pixma.h, backend/pixma_mp150.c doc/sane-pixma.man, doc/descriptions/pixma.desc: With feedback from MP970 owner, updated pixma backend for MP970 CCD sensor support (yet in color only), and more generally, support for other CCD sensor PIXMA: MP800, MP810, MP830, MP960, but yet untested, which produce shifted color planes scanned images. Current trim based on sample images provided in bug reports and other web pages, but might require some few and simple final tweaks. Also fixed a bug for MP220 at 1200 dpi, MP220 is now reported to work fine. 2008-04-13 Julien Blache * frontend/saned.c: fix typo. * backend/net.c: plug an information leak in the net backend. When sending out a SANE_NET_CONTROL_OPTION RPC for the SANE_ACTION_GET_VALUE action (and SANE_ACTION_SET_AUTO for the network protocol versions < 3), the backend was not clearing the memory area for the value argument before sending it over the network, resulting in an information leak for the SANE_ACTION_GET_VALUE case. 2008-04-12 Mattias Ellert * backend/rts8891.c, backend/rts88xx_lib.c: fix format warning * doc/sane-rts8891.man: man page fixes * AUTHORS: e-mail update 2008-04-11 Julien Blache * frontend/saned.c: announce the _sane-port._tcp service via mDNS (Avahi) when running in standalone or debug mode. A separate process is responsible for the announcement through Avahi. * backend/net.c: look for _sane-port._tcp service announcements via mDNS (Avahi). A separate thread listens to announcements through Avahi. Start the thread as early as possible in sane_init() so as to get as much data as possible until sane_get_devices() is called. * aclocal.m4, configure, configure.in, include/sane/config.h.in: add autofoo stuff for Avahi support, disabled by default. 2008-04-10 Julien Blache * frontend/saned.c: do not use daemon(), as it's a 4.4BSD/glibc function; OS/2 for instance does not have it. Use an open-coded equivalent. Add a PID file. saned -a username now drops privileges 2008-04-22 Nicolas Martin * backend/pixma.c, backend/pixma_common.c, backend/pixma_rename.h doc/sane-pixma.man, doc/descriptions/pixma.desc (new) backend/pixma_imageclass.c, backend/Makefile.in: Thanks to Dennis Lou, who adapted the pixma backend to add support for Canon ImageCLASS series, fully tested for MF4270, and includes PIDs declarations for other ImageCLASS devices, yet to be tested. Fixes also a bug for ADF and ADF Duplex scan source selection. 2008-04-20 m. allan noah * backend/fujitsu.h: remove #define SANE_FRAME_JPEG 2008-04-19 m. allan noah * backend/fujitsu.[ch], backend/fujitsu-scsi.h: backend v1.0.58, rename page code 32 to 'unknown', compile if NDEBUG is set, proper async sane_cancel support, re-enable JPEG support various functions rewritten (shorter, more clear) * doc/descriptions/fujitsu.desc: add new fi-6xxx machines 2008-04-18 Alessandro Zummo * backend/epson2.c: fixed attach() error path. 2008-04-14 Nicolas Martin * backend/pixma_mp150.c, backend/pixma.h, backend/pixma_mp150.c doc/sane-pixma.man, doc/descriptions/pixma.desc: With feedback from MP970 owner, updated pixma backend for MP970 CCD sensor support (yet in color only), and more generally, support for other CCD sensor PIXMA: MP800, MP810, MP830, MP960, but yet untested, which produce shifted color planes scanned images. Current trim based on sample images provided in bug reports and other web pages, but might require some few and simple final tweaks. Also fixed a bug for MP220 at 1200 dpi, MP220 is now reported to work fine. 2008-04-13 Julien Blache * frontend/saned.c: fix typo. * backend/net.c: plug an information leak in the net backend. When sending out a SANE_NET_CONTROL_OPTION RPC for the SANE_ACTION_GET_VALUE action (and SANE_ACTION_SET_AUTO for the network protocol versions < 3), the backend was not clearing the memory area for the value argument before sending it over the network, resulting in an information leak for the SANE_ACTION_GET_VALUE case. 2008-04-12 Mattias Ellert * backend/rts8891.c, backend/rts88xx_lib.c: fix format warning * doc/sane-rts8891.man: man page fixes * AUTHORS: e-mail update 2008-04-11 Julien Blache * frontend/saned.c: announce the _sane-port._tcp service via mDNS (Avahi) when running in standalone or debug mode. A separate process is responsible for the announcement through Avahi. * backend/net.c: look for _sane-port._tcp service announcements via mDNS (Avahi). A separate thread listens to announcements through Avahi. Start the thread as early as possible in sane_init() so as to get as much data as possible until sane_get_devices() is called. * aclocal.m4, configure, configure.in, include/sane/config.h.in: add autofoo stuff for Avahi support, disabled by default. 2008-04-10 Julien Blache * frontend/saned.c: do not use daemon(), as it's a 4.4BSD/glibc function; OS/2 for instance does not have it. Use an open-coded equivalent. Add a PID file. saned -a username now drops privileges and runs as the given user (and group). * doc/saned.man: document -a username. 2008-04-06 Nicolas Martin * backend/pixma_mp150.c: Modifications for PIXMA MP970, to manage the CCD sensor with PIXMA generation 3 protocol. To be tested. 2008-04-06 Julien Blache * frontend/saned.c: add standalone daemon mode, building upon the AF-indep/IPv6 debug mode. Reorganize code by splitting the main() function into a series of functions. Factorize common code between the old network code and the AF-indep code. There's now only one version of main(). * doc/saned.man: document new -a flag, reorganize manpage sections (separate inetd configuration under the INETD CONFIGURATION section). 2008-04-05 Stéphane Voltz * backend/genesys.c backend/genesys.h backend/genesys_devices.c backend/genesys_gl646.c backend/genesys_low.h: Fixed double free problems, made OPT_COLOR_FILTER work for GL646 scanners, added OPT_CUSTOM_GAMMA and OPT_GAMMA_VECTOR* options, split sane_control_options to make it more readable, fixed 'reset stream' problem for MD6471. 2008-04-05 Stéphane Voltz * backend/rts8891.c: split sane_control_option() to make it more readable 2008-04-02 Nicolas Martin * backend/pixma_mp150.c, backend/pixma.c, doc/sane-pixma.man, doc/descriptions/pixma.desc: Added a 10s tempo before "NO PAPER" fail message appears in ADF scan, declared PIXMA MX850, moved MP970 to Generation 3 protocol, but yet untested. 2008-03-28 Julien Blache * sanei/sanei_usb.c, include/sane/sanei_usb.h: add sanei_usb_get_vendor_product_byname(). * backend/snapscan-mutex.c: do not use ftok() in snapscani_mutex_open(); ftok() will fail with anything for which the devicename is not a filename. Construct an IPC key based on the product ID, bus number and device number for libusb devices, otherwise fallback to ftok() and check its return value. * tools/sane-desc.c: HAL 0.5.11-rc2 does not support the info.bus property anymore; superseded by info.subsystem. Unfortunately, this is not backward-compatible, so add a new "hal-new" mode to sane-desc. 2008-03-28 m. allan noah * backend/hp-scl.c: add usleep to improve usb performance, from jim a t meyering d o t net 2008-03-28 m. allan noah * sanei/sanei_usb.c: add usb class 6 (imaging) to detected devices * sanei/sanei_scsi.c: fix bug in sanei_scsi_find_devices() which caused early return if attach callback returned error. 2008-03-22 Stéphane Voltz * AUTHORS configure configure.in backend/dll.conf.in backend/Makefile.in backend/rts8891.c backend/rts8891.conf.in backend/rts8891_devices.c backend/rts8891.h backend/rts8891_low.c backend/rts8891_low.h backend/rts88xx_lib.c backend/rts88xx_lib.h doc/descriptions/rts8891.desc doc/descriptions/unsupported.desc doc/sane-rts8891.man: rts8891 backend inclusion 2008-03-20 Julien Blache * include/Makefile.in: remove _stdint.h and byteorder.h in distclean target, autogenerated cruft. * frontend/saned.c: fix debug messages around the getservbyname() call in the IPv4 code as it still referred to the "sane" port instead of "sane-port". 2008-03-16 Nicolas Martin * backend/pixma_mp150.c, backend/pixma.h, doc/sane-pixma.man, doc/descriptions/pixma.desc: Changes to have new Canon PIXMA MX700 working with pixma backend Flatbed and ADF scan are supported. 2008-03-15 Julien Blache * include/sane/sanei_usb.h: typo fix. 2008-03-02 Stéphane Voltz * doc/descriptions/lexmark.desc: added X1195 * doc/descriptions/unsupported.desc: removed X11xx, X12xx and Dell A920 which are to be handled by the lexmark backend * backend/umax_pp.h backend/umax_pp.c backend/umax_pp_mid.h backend/umax_pp_mid.c backend/umax_pp_low.h backend/umax_pp_low.c: copyright notice update 2008-03-01 Gerard klaver * doc/descriptions/as6e.desc added Dexxa 4800 2008-02-26 Jonathan Bravo Lopez * backend/hp3900_sane.c: Fixed optional grayscale emulation * backend/hp3900_debug.c: Silence gcc warnings when tiffio.h is not used 2008-02-25 m. allan noah * backend/fujitsu.[ch], backend/fujitsu-scsi.h: add mode sense for page code 32 (color interlacing?) and more debug output in init_ms(), for fi-5900 2008-02-24 Nicolas Martin * backend/pixma_mp150.c, doc/sane-pixma.man, doc/descriptions/pixma.desc: Changes to have Canon PIXMA MP140 now working with pixma backend 2008-02-23 Pierre Willenbrock * backend/genesys_gl841.c: Make LEDs go really dark when "lamp" is off 2008-02-22 Nicolas Martin * backend/pixma_mp150.c, backend/pixma_mp730.c, doc/sane-pixma.man, doc/descriptions/pixma.desc: Changed MP140 to experimental cause not working yet Changed MP710 to non-ADF scanner 2008-02-22 Pierre Willenbrock * backend/genesys_gl841.c: Fix debugging output of gl841_bulk_write_register 2008-02-21 Pierre Willenbrock * backend/genesys.c, backend/genesys_gl646.c, backend/genesys_gl841.c, backend/genesys_low.h: Add LED exposure control to gl841_set_lamp_power 2008-02-20 Pierre Willenbrock * backend/genesys_gl841.c: Send 32 registers at once instead of 1 2008-02-20 Mattias Ellert * backend/Makefile.in, ltmain.sh: Library version definition fixes * backend/coolscan3.c: Fix format warnings 2008-02-18 Alessandro Zummo * backend/coolscan3.c: added new coolscan3 backend with improved stability and infrared support. * configure.in, include/sanei.h: bumped version number to 1.1.0 and enabled 1.1 frame types. doc/sane-pixma.man, doc/descriptions/pixma.desc, doc/descriptions-external/canon_mfp.desc: > New models changes: - added: Pixma MP210, MP470, MP520, MP610, MP710 - declared but untested: Pixma MP140, MP220, MP740 - declared experimental and untested: MP970 > Bugs and fixes - multipage documents scan no more fails in non-ADF scan - IO Fail bug when scanning large pages e.g. with Xsane - implement send_time message form MP710, MP730 family - run indent -gnu on all pixma_* source files. 2008-02-14 m. allan noah * backend/cardscan.c, backend/epjitsu.c, backend/fujitsu.c: sanei_read_config has already cleaned string, fix bug #310597 2008-02-13 Pierre Willenbrock Patch by Stefan Lucke * backend/genesys_gl841.c: Fixes 3 possibilities for infinite loops and adjusts loop threshold to given comments 2008-02-13 Jonathan Bravo Lopez * backend/hp3900.c, backend/hp3900_debug.c, backend/hp3900_rts8822.c, backend/hp3900_usb.c, backend/hp3900_sane.c, backend/hp3900_types.c, backend/hp3900_config.c, backend/hp3900.conf.in, /doc/sane-hp3900.man, doc/descriptions/hp3900.desc: Minor changes and added support for HP ScanJet G2710 scanner. 2008-02-10 m. allan noah * configure, configure.in: Changed version to 1.0.19-cvs. Enabled compilation warnings. * Makefile.in: Added ChangeLog-1.0.19 to DISTFILES. Older entries can be found in ChangeLog-1.0.19. backends-1.3.0/ChangeLogs/ChangeLog-1.0.21000066400000000000000000001137301456256263500176110ustar00rootroot00000000000000****** Release of sane-backends 1.0.21. End of code freeze ****** 2010-04-25 Chris Bagwell * tools/sane-config.in: Fix typo: REGMGR=RESMGR. 2010-04-21 m. allan noah * tools/.gitignore: add sane-backends.pc * po/uk.po: updated translation from Yuri Chornoivan * po/nl.po: updated translation from Martin Kho 2010-04-20 Chris Bagwell * doc/descriptions-external/epkowa.desc: Updated based on values that will be in next release of iscan. Updated 4 pre-existing SCSI models with commonly used model names. This allows wider range of Epson SCSI scanners to be correctly detected using auto-generated hal and udev files. This benefits epkowa, epson2, and epson backends. 2010-04-18 m. allan noah * tools/sane-desc.c: Update the sane-devel contact suggestion 2010-04-16 m. allan noah * backend/kvs1025*, kvs1025.desc, sane-kvs1025.man: backend v3 - invert usb status, add 0x to usbids, add missing KV-S1045 usbid 2010-04-12 Nicolas Martin * backend/pixma_mp150.c, backend/pixma_io_sanei.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: pixma: added changes from Gernot Hassenpflug to fully support Canoscan 8800F. debug code clean-up in pixma_mp150.c 2010-04-12 Chris Bagwell * backend/p5.c, backend/p5_device.c, backend/p5.h: Remove references to libc.h in p5.h. On OSX, it was including socket.h and causing compile fail with conflicting connect() functions. Since backend doesn't support network connects, should be fine to use name connect. 2010-04-07 Chris Bagwell * acinclude.m4: cygwin and mingw is now hiding M_PI_2 defines with -ansi flag. Remove to fix cygwin compile. Mingw doesn't support fork() so default to pthreads. Add u_short to complete definition of u_* types which helps out ming when we define _BSDTYPES_DEFINED. 2010-03-25 Stéphane Voltz * backend/p5.c backend/p5_device.[ch] backend/genesys_gl646.c: replace u_int8_t and u_int16_t by uint8_t and uint16_t 2010-04-06 Chris Bagwell * configure.in, tools/sane-backends.pc.in: Add stub for future pkg-config support to configure. This allows distributes that are patching in pkg-config support to not rerun autoconf and wipe out our custom libtool. * tools/sane-config.in: Fix LDFLAGS bug related to GPHOTO2 support. 2010-04-02 Chris Bagwell * acinclude.m4: Add --enable-rpath option to configure. Ported from Fedora patches. 2010-04-02 Chris Bagwell * backend/kvs1024*: Remove includes of usb.h directly into backend. Complete prototype for sane_exit(). 2010-04-02 Chris Bagwell * po/Makefile.am: Remove unneeded distclean-local target. 2010-03-25 Stéphane Voltz * backend/kvs1025.c: compile fix 2010-03-25 m. allan noah * backend/kvs1025*: convert backend from libusb to sanei_usb * doc/descriptions/kvs1025.desc, doc/sane-kvs1025.man: update docs * backend/dll.conf.in, configure, configure.in: enable kvs1025 backend 2010-03-16 Stéphane Voltz * backend/rts8891.c: change register 0x11 settings for sensor type 1 2010-03-15 Nicolas Martin * backend/pixma_mp150.c, doc/descriptions/pixma.desc: pixma: fix color shifts for MP990 up to 2400 dpi updated and reorderd descs for several models. 2010-03-15 Stéphane Voltz * backend/genesys.[ch] backend/genesys_devices.c backend/genesys_low.h backend/genesys_gl646.[ch] backend/genesys_gl841.c: full transparency adaptor support for gl646 based scanners 2010-03-11 Stéphane Voltz * backend/rts8891.c: further register tweaking for 600 dpi scans 2010-03-11 Stéphane Voltz * backend/lexmark.c: minor bugfix when assigning possible dpi list 2010-03-07 Stéphane Voltz * backend/rts8891.c: tweak register settings at 600 dpi for HP4470 2010-03-05 Stéphane Voltz * backend/rts8891.c: fix HP4470 sensor detection 2010-03-04 Stéphane Voltz * backend/rts8891.c backend/rts8891.h: add debug traces for sensor type handling 2010-03-04 Stéphane Voltz * doc/desc/genesys.desc: fix HP2400/G2410 USB id, update HP3670 status 2010-03-01 Stéphane Voltz * doc/sane-p5.man doc/sane-umax_pp.man doc/sane-rts8891.man doc/sane-lexmark.man: fix author name accentuation and capitalization 2010-03-01 Stéphane Voltz * backend/genesys.c backend/genesys_gl646.c backend/genesys_devices.h: enable warming up and calibration cache for HP3670 2010-02-28 Stéphane Voltz * backend/genesys_devices.c backend/genesys_gl646.c backend/genesys_gl646.h: tune HP2400/G2410 settings 2010-02-27 Chris Bagwell * Rerun autoconf/automake to align files to single version of tools. 2010-02-27 m. allan noah * */Makefile.in: update with (unfortunately) slightly older automake * po/*: standard gettext layout by Adi Roiban * doc/descriptions/fujitsu.desc, backend/fujitsu.conf.in: add S1500M entry by Harald Weis * tools/sane-desc.c: Condense warnings about half-baked .desc files 2010-02-27 Stéphane Voltz * backend/genesys.c backend/genesys_devices.c backend/genesys_gl646.c backend/genesys_gl646.h: final work for HP3670 full support 2010-02-25 Stéphane Voltz * backend/genesys_gl646.c: fix regression for gl646 CCD scanners due to incorrect color filter setting. 2010-02-23 Stéphane Voltz * backend/genesys.c backend/genesys_devices.c backend/genesys_gl646.c backend/genesys_gl646.h: XP200 calibration rework. Some HP3670 improvements. 2010-02-22 m. allan noah * backend/fujitsu.c: force enable flatbed for M3092, by Jochen Hepp * doc/Makefile.*, doc/descriptions-external/kodak-twain: new backend 2010-02-22 Julien Blache * doc/descriptions-external/epkowa.desc: Updated for iscan 2.24.0. 2010-02-16 Stéphane Voltz * backend/genesys.c backend/genesys_devices.c backend/genesys_gl646.c backend/genesys_gl646.h backend/genesys_low.h: buttons and basic XPA support for HP3670. 2010-02-15 Stéphane Voltz * backend/p5.c backend/p5.h backend/p5_device.c backend/p5_device.h backend/p5.conf.in doc/descriptions/p5.desc doc/sane-p5.man: add new p5 backend for Primax PagePartner * backend/dll.conf.in backend/Makefile.* configure* doc/Makefile.* doc/sane.man: p5 backend integration changes 2010-02-13 m. allan noah * backend/avision.[ch]: updated settings for Visioneer 9450, based on code from Andyz Smith * doc/descriptions/avision.desc: added AV220-G and 6080E 2010-02-12 m. allan noah * backend/kvs1025*: add new backend from Panasonic Russia, Ltd. * backend/Makefile.*, configure*, doc/Makefile.*, doc/sane.man: backend integration changes * doc/descriptions/kvs1025.desc, doc/sane-kvs1025.man: the parts Panasonic forgot 2010-02-12 m. allan noah * backend/fujitsu.c: disable bg_color for S1500 2010-02-12 Stéphane Voltz * backend/genesys_gl646.c: fixed MD5345 calibration 2010-02-12 Stéphane Voltz * doc/descriptions/genesys.desc backend/genesys.c backend/genesys_devices.c backend/genesys_gl646.c backend/genesys_gl646.h: added support for hp3670, uncalibrated scans only 2010-02-10 m. allan noah * include/sane/saneopts.h: add three new SANE_VALUE macros * backend/*.[ch]: use mode name macros from saneopts.h by Robert Ancell 2010-02-10 m. allan noah * doc/descriptions/hpaio.desc: updated from hplip v3.9.12 * po/sane-backends.nl.po: updates from Armijn Hemel 2010-02-10 m. allan noah * backend/canon_dr.c, backend/cardscan.c, backend/epjitsu.c, backend/fujitsu.c, backend/kodak.c, po/Makefile.*: - enable translations of all static strings via SANE_I18N macro - don't fail if sanei_scsi_open gives smaller buffer than asked for * doc/descriptions/kodak.desc, doc/sane-canon_dr.man, doc/sane-fujitsu.man, doc/sane-kodak.man: - minor version number and text updates 2010-02-10 m. allan noah * acinclude.m4, configure: missing $ in variable expansion * frontend/scanimage.c: exit with error if stdout isatty() * doc/descriptions/unsupported.desc, doc/descriptions/lexmark.desc: - add Dell A940 and correct manpage name * backend/gt68xx_high.c: maximum exposure 245 not 248, see bug #312113 * po/Makefile.*, po/sane-backends.ja.po: - New Japanese translation from Hiroshi Miura 2010-02-10 m. allan noah * backend/cardscan.[ch], backend/cardscan.conf.in, doc/descriptions/cardscan.desc, doc/sane-cardscan.man: - add lines_per_block config option - add has_cal_buffer config option - basic support for 600C - clean #include lines * doc/sane.man: add missing backends 2010-02-10 Stéphane Voltz * doc/descriptions/genesys.desc: added G2410 2010-02-10 m. allan noah * backend/gt68xx_devices.c: add GT68XX_FLAG_NO_STOP to SF600 * doc/.gitignore, doc/Makefile.am, doc/Makefile.in: - added new targets for scanner search engine db * doc/releases.txt: improved doc * doc/saned.man: clean typos * tools/sane-desc.c: removed NAME= from udev rules (deprecated) 2010-02-09 m. allan noah * backend/fujitsu.c, doc/descriptions/fujitsu.desc, backend/canon_dr.c, doc/descriptions/canon_dr.desc, backend/epjitsu.c, doc/descriptions/epjitsu.desc, doc/sane-fujitsu.man, doc/sane-canon_dr.man, doc/sane-epjitsu.man, backend/epjitsu.conf.in: - cleanup #include lines, copyrights, manual, and supported scanners. 2010-02-08 Stéphane Voltz * backend/genesys.c backend/genesys.h backend/genesys_conv.c backend/genesys_devices.c backend/genesys_gl646.c backend/genesys_gl646.h: working uncalibrated 150/300 dpi modes for HP3670. Enable custom gamma for gl841 based scanners. Turned dynamic line-art to be default. 2010-02-03 Stéphane Voltz * backend/genesys_conv.c: add safeguards when doing gray normalization 2010-02-02 Nicolas Martin * doc/sane-pixma.man, doc/descriptions/pixma.desc: pixma: updated docs for supported model ImageCLASS MF4350d. 2010-01-31 Stéphane Voltz * backend/genesys_conv.c: normalize gray data when doing dynamic lineart. 2010-01-30 Stéphane Voltz * backend/genesys.conf.in backend/genesys_devices.c doc/descriptions/genesys.desc doc/sane-genesys.man: add Xerox Travel Scanner 100, work by Andrey Loginov 2010-01-29 Nicolas Martin * backend/pixma_io.h, backend/pixma_imageclass.c, doc/sane-pixma.man, doc/descriptions/pixma.desc: pixma: changed usb timeouts to 20 sec to support ImageClass MF3240. updated source code comments and documentation. 2010-01-28 Oliver Schwartz * backend/snapscan-usb.c: Use hash of device string instead of parsing it to create a semaphore id. The new code is less platform dependent and should also work on FreeBSD 8.0. 2010-01-27 Stéphane Voltz * backend/genesys.c backend/genesys.h backend/genesys_conv.c backend/genesys_low.h backend/genesys_gl841.c: add dynamic lineart scanning based on epjitsu code by m. allan noah. 2010-01-19 Nicolas Martin * backend/pixma.h, backend/pixma_mp150.c: pixma: changes for cropped area enhancement, supplied by Christian Scholtz for generation 3+ devices. 2010-01-19 Stéphane Voltz * backend/genesys.c backend/genesys.conf.in backend/genesys.h backend/genesys_conv.c backend/genesys_devices.c backend/genesys_gl646.c backend/genesys_gl841.c backend/genesys_low.h doc/descriptions/genesys.desc doc/sane-genesys.man: add Visioneer OneTouch 7100 (patch by Jack McGill). Add software lineart option. 2010-01-17 Stéphane Voltz * backend/genesys.c backend/genesys_gl646.c: gray shading calibration fix for CCD GL646 based scanners. 2010-01-17 Mattias Ellert * po/sane-backends.sv.po: Updated Swedish translations. 2010-01-16 Stéphane Voltz * backend/genesys.c backend/genesys_gl646.c: re-enable threshold option, fix wpl computing for HP2400 2010-01-13 Stéphane Voltz * backend/genesys.c backend/genesys_low.c backend/genesys_gl646.c backend/genesys_gl841.c doc/sane-genesys.man : add true gray scanning for CIS based scanners 2010-01-08 Nicolas Martin * doc/sane-pixma.man, doc/descriptions/pixma.desc: pixma: updated documentation for MP490 and MP550 (supported models). 2009-12-30 Stéphane Voltz * backend/lexmark_low.c: improve init time debug messages 2009-12-26 Nicolas Martin * doc/sane-pixma.man, backend/pixma.c, backend/pixma_mp150.c: pixma: some routine maintenance updates to backend and documentation. 2009-12-24 Julien Blache * frontend/saned.c: fix a bug in the standalone polling code. 2009-12-05 Stéphane Voltz * backend/genesys.c backend/genesys_gl841.c backend/genesys_low.h: fixed amount of bytes left to read at document end for gl841 based sheetfed scanners 2009-12-09 Nicolas Martin * doc/descriptions/pixma.desc, doc/sane-pixma.man, backend/pixma_imageclass.c, backend/pixma_mp150.c: pixma: updated backend, docs and desc for support to MP560 and MF4330d/ADF 2009-12-06 Nicolas Martin * doc/descriptions/pixma.desc, doc/sane-pixma.man, backend/pixma.h, backend/pixma.c, backend/pixma_mp150.c: pixma: bumped version, added compilation type debug message. updated doc and desc (support for MX330) 2009-12-05 Stéphane Voltz * backend/genesys.c backend/genesys_gl841.c backend/genesys_devices.c backend/genesys_low.h backend/genesys.conf.in document/descriptions/genesys.desc: add full support for Ambir/Syscan DS685. Use maximum height to handle the case when document height isn't known before scan for sheetfed scanners 2009-12-04 Nicolas Martin * backend/pixma_mp150.c: pixma: fix pixma backend bug for compilation with USE_PTHREAD/-lpthread 2009-12-01 Stéphane Voltz * backend/genesys_gl841.c: take amount of data in scanner's buffer into account when detecting end of document 2009-11-30 Stéphane Voltz * backend/gt68xx.c backend/gt68xx_high.c backend/gt68xx_high.h backend/gt68xx_low.h backend/gt68xx_gt6816.h backend/gt68xx_gt6816.c: implement request '0x59' which detects document presence for 6816. Add a sensor option to reports document presence, and test for document before scan for 6816 sheetfed scanners. 2009-11-29 Stéphane Voltz * backend/gt68xx.c backend/gt68xx_high.c backend/gt68xx_high.h: added 'clear calibration' button option and 'need calibration' sensor option. 2009-11-28 Nicolas Martin * backend/pixma.c, backend/pixma_mp1580.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: pixma: fix pixma backend (end of scan), when compiled with USE_PTHREAD/-lpthread set support to complete for MP990 in documentation, as reported by Jean-Michel Poure. 2009-11-28 Stéphane Voltz * backend/gt68xx_generic.h backend/gt68xx_generic.c backend/gt68xx_low.h backend/gt68xx_high.c backend/gt68xx_high.h backend/gt68xx_devices.c backend/gt68xx.c doc/descriptions/gt68xx.desc: added sheetfed calibration 2009-11-25 Stéphane Voltz * backend/genesys_gl841.c: use only signed vars in gl841_detect_document_end with a few more debug traces 2009-11-25 Stéphane Voltz * backend/genesys.conf.in backend/genesys_gl841.c backend/genesys_devices.c: added docketport 467 model and add debug traces in gl841_detect_document_end 2009-11-23 Stéphane Voltz * backend/gt68xx_generic.h backend/gt68xx_generic.c backend/gt68xx.conf.in backend/gt68xx_low.h backend/gt68xx_high.c backend/gt68xx_devices.c backend/gt68xx.c: fixed gt6816 based sheetfed scanners positioning by implementing and using a move paper request. Added Iriscan Express 2 model based on reports seen in sane-devel list. 2009-11-19 Nicolas Martin * doc/descriptions/pixma.desc, doc/sane-pixma.man: pixma: updated Pixma MP250 status to supported. Changed desc status from good to complete for well supported models. 2009-11-16 Stéphane Voltz * backend/genesys.conf.in backend/genesys_gl841.c doc/descriptions/genesys.desc : applied fixes and status update from Jack McGill. 2009-11-16 Stéphane Voltz * backend/genesys.c backend/genesys_devices.c backend/genesys_gl646.c backend/genesys_gl841.c: add needed flags for DP485/487 and XP100 scanners. Use different thresholds in strip detection function. Do led, coarse and gain calibration at sensor's max resolution when doing sheetfed calibration. 2009-11-14 Julien Blache * doc/descriptions/epson.desc: add the Perfection636 SCSI scanner, from Frédéric Brière . 2009-11-06 Stéphane Voltz * backend/genesys.c backend/genesys.h backend/genesys_devices.c backend/genesys_gl646.c backend/genesys_gl646.h backend/genesys_gl841.c backend/genesys_low.h doc/descriptions/genesys.desc doc/sane-genesys.man: sheetfed calibration work merge. All genesys supported sheetfed scanners can now be calibrated with a special sheet. 2009-10-28 Nicolas Martin * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: pixma: declared Pixma MP550 (yet untested), updated docs. 2009-10-22 Louis Lagendijk * doc/sane-pixma.man: Added networking details to doc/sane-pixma.man 2009-10-21 Nicolas Martin * backend/pixma_imageclass.c, doc/sane-pixma.man: pixma: added I-SENSYS MF4320d and updated doc, for this model and MP390. 2009-10-20 Louis Lagendijk * backend/pixma_io_sanei.c: Allow for ranges in model returned from device (allow -) 2009-10-18 Nicolas Martin * doc/descriptions/pixma.desc, doc/sane-pixma.man: pixma: updated doc to include support for MP270 and ImageClass MF3110 2009-10-18 Nicolas Martin * backend/pixma_mp150.c, backend/pixma_imageclass.c, backend/pixma.h, doc/descriptions/pixma.desc, doc/sane-pixma.man: pixma: - bumped backend version to 0.16.1 - Some error condition fixes to Generation 4 (MP640, ...) XML code - Added ImageClass MF4360dn-MF4390dn 2009-10-18 Troy Rollo * backend/hp3500.c: Stop backed from calling exit(), which is not helpful when using threads instead of processes. 2009-10-16 Nicolas Martin * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: pixma: Changes to include new XML dialog for new 2009 PIXMA models, like MP270 or MP640. 2009-10-03 Ilia Sotnikov * backend/epjitsu.c: use own private function instead of roundf() 2009-10-02 Stéphane Voltz * backend/genesys_gl646.c: honor GENESYS_NO_CALIBRATION device flag when setting registers for final scan 2009-10-01 Jonathan Bravo Lopez * backend/hp3900_sane.c: fix bug 311991: "Bad option sizes let frontend (e.g. xsane) crash" 2009-10-01 Stéphane Voltz * backend/lexmark_low.c: fix a memleak in find_start_line(), check success of memory allocation after calloc() 2009-09-29 Chris Bagwell * backend/Makefile.am, backend/Makefile.in: Add missing sanei_pio to qcam backend. Fixes compile error on embedded Linux boxes. Added new genesys_gl646.h to backend's SOURCES so its included in tar file. * doc/Makefile.am, doc/Makefile.in: Remove manual deletion of $docdir. Modern automake cleans up fine after itself and this fix is required for distcheck target to pass on automake-1.11. 2009-09-27 Nicolas Martin * backend/pixma_bjnp.c, backend/pixma_mp730.c: Print size_t values as %lu with cast to unsigned long as prescribed in the file doc/backend_writing.txt, to avoid compile warnings. Removed unused debug code. 2009-09-26 Julien Blache * backend/genesys.c: add missing check when logging raw data, patch by Tollef Fog Heen . 2009-09-16 Nicolas Martin * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: pixma backend: Updated documentation for: - Pixma MP260 support - descriptions for recently added new models 2009-09-14 m. allan noah * sanei/sanei_usb.c: reset the libusb device handle after bus scan. patch from Hans Petter Selasky * backend/fujitsu.*, backend/Makefile.*: backend v97, use sanei_magic to provide software deskew, autocrop and despeckle * sanei/sanei_magic.c, include/sane/sanei_magic.h, Makefile.*: new image processing lib for deskew, despeckle, autocrop. 2009-09-13 Nicolas Martin * backend/pixma_mp150.c, backend/pixma_mp730.c: pixma backend: Declared new Pixma models MP250, MP490, MP560, MP640, and MP990, all yet untested, and modified some debug code. 2009-09-11 Mattias Ellert * tools/sane-find-scanner.c: Fix false warning about USB not supported when linking with libusb-1.0. * po/sane-backends.sv.po: Updated Swedish translations. 2009-09-07 Nicolas Martin * backend/pixma_mp730.c: pixma backend: added debug info for MF3110 and changed default params. 2009-09-06 Nicolas Martin * backend/pixma_mp730.c, backend/pixma_imageclass.c, backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: pixma backend: - Fix usb interrupt messages handling for MF3110 - New support for ImageClass MF4660 - Documentation updates 2009-09-03 Nicolas Martin * backend/pixma_mp730.c: pixma backend: change for MF3110 image encoding. 2009-08-29 Nicolas Martin * backend/pixma_imageclass.c, backend/pixma_mp730.c: pixma backend: moved MF3110 declaration to mp730 part of backend. 2009-08-27 Nicolas Martin * doc/descriptions/pixma.desc, backend/pixma_mp150.c: - pixma backend: Updated description for MP830 reported to be fully supported. 2009-08-13 m. allan noah * tools/sane-desc.c: add closedir call to fix bug #311880, patch from Johannes Meixner 2009-08-08 Julien Blache * frontend/saned.c: run_standalone(), explicitly check for errors on fds. 2009-08-07 m. allan noah * backend/fujitsu.[ch], backend/fujitsu-scsi.h: Backend v96 - split sane_get_parameters into two functions - remove unused code from get_pixelsize - support hardware based auto length detection * doc/descriptions/fujitsu.desc: various status and comment updates 2009-08-07 Stéphane Voltz * backend/genesys_gl646.c backend/genesys_devices.c: - small fixes for hp3670 support 2009-08-05 Stéphane Voltz * backend/genesys.c: - fixed shading calibration for hp2300 when dpi <= 300 2009-08-04 Stéphane Voltz * backend/genesys.c backend/genesys_gl646.c backend/genesys_devices.c: - fixed document end detection for gl646 cis scanners - disable color filter for gray mode for gl646 cis scanners 2009-08-03 Nicolas Martin * doc/descriptions/pixma.desc, doc/sane-pixma.man: - pixma backend: few cosmetic documentation updates. 2009-08-02 Stéphane Voltz * frontend/tstbackend.c: remove WARMING_UP_STATUS usage 2009-07-30 Jonathan Bravo Lopez * backend/hp3900_config.c: - Fixed bug [#311856] possible for loop overflow 2009-07-29 m. allan noah * backend/canon_dr.[ch]: Backend v34 - add simplified Hough transform based deskewing code - add extremity detecting cropping code - use per-model background color to fill corners after deskew - request and chop extra scanlines instead of rounding down - remove padding dumb scanners add to top of front side - sane_get_params uses intermediate struct instead of user struct - if scanner stops, clone the last line until the end of buffer - reset some intermediate params between duplex sides 2009-07-28 Nicolas Martin * doc/descriptions/pixma.desc: - Updated pixma backend desc for Pixma MX860 2009-07-25 Nicolas Martin * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: - Fix in declarations for Pixma MX320, MX330 and MX860, - Updated docs for MX860, confirmed to be supported. 2009-06-23 m. allan noah * backend/canon_dr.[ch]: Backend v33 - add software brightness/contrast for dumb scanners - add blocking mode to allow full-page manipulation options to run - add swdespeck option and support code - add swdeskew and swcrop options (disabled) 2009-06-22 m. allan noah * backend/canon_dr.[ch]: Backend v32 - crop/resample image data before buffering, not after - shink image buffers to size of output image, not input - correct some debug message - better handling of EOF - add intermediate param struct to existing user and scan versions 2009-07-17 Alex Belkin * backend/xerox_mfp.c: more sanity checking for debug mode. 2009-07-15 Stéphane Voltz * backend/lexmark.h: disable deep debugging 2009-07-10 Stéphane Voltz * backend/genesys.c backend/genesys_gl646.c backend/genesys_low.h backend/genesys_gl646.h backend/genesys_gl841.c backend/genesys_devices.c backend/genesys.h: - add shading calibration for sheetfed scanners - shading calibration fix for all gl646 based scanners 2009-07-06 Stéphane Voltz * backend/lexmark.c backend/lexmark.h backend/lexmark_low.h: do not use resolution higher than sensor resolution when doing calibation. 2009-07-02 Nicolas Martin * backend/pixma.h, backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: - Declared new models (yet untested) Pixma MX330 and MX860, updated docs. - Bump backend minor version number. 2009-07-01 Alex Belkin * backend/xerox_mfp.conf.in: usbid for Samsung SCX-4500W 2009-06-29 m. allan noah * po/Makefile.am, po/Makefile.in, po/sane-backends.uk.po: - Ukrainian translation from yurchor@ukr.net * backend/avision.c: set flag for Fujitsu SP620C * doc/descriptions/unsupported.desc: minor updates to Canon scanners * doc/descriptions/canon_dr.desc: set status complete on 5 machines * backend/canon_dr.c: reduce default buffer size to 2 megs 2009-06-26 Julien Blache * po/Makefile.am, po/Makefile.in, po/sane-backends.gl.po: added new gl (galician/galego) translation. * po/sane-backends.es.po: update es translation. Both translations courtesy of Miguel Bouzada * po/README: update instructions for po file addition. 2009-06-22 m. allan noah * backend/kodak.*: new backend for big Kodak SCSI/Firewire scanners * doc/descriptions/kodak.desc, doc/sane-kodak.man: docs for same 2009-06-22 Nicolas Martin * backend/pixma_imageclass.c, backend/pixma_mp730.c, doc/descriptions/pixma.desc: - Added duplex ADF scan for Canon ImageCLASS D480. - Fixed ImageCLASS MF5770 grayscale scanning. - Moved ImageCLASS MF5730 and MF5750 to pixma_mp730.c (similarity with MF5770). - Updated documentation. 2009-06-20 Mattias Ellert * po/sane-backends.sv.po: Updated Swedish translations. * backend/hp5400_internal.h, backend/hp5590_cmds.c, backend/hp5590_low.c, backend/net.c: Add missing includes. 2009-06-20 Nicolas Martin * backend/pixma_imageclass.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: Added Canon imageCLASS D480 support to the pixma backend. 2009-06-17 Stéphane Voltz * backend/lexmark_low.c: f3/f4 registers assignment cleanup 2009-06-18 Nicolas Martin * backend/pixma_mp150.c: Fixed typo in pixma backend for MX320 declaration. 2009-06-17 m. allan noah * backend/canon_dr.[ch], backend/canon_dr-cmd.h: backend v30 - add fine cal support for machines with internal buffer (2050/2080) - support fixed-width machines that require even bytes per scanline - pad end of scan with gray if scanner stops prematurely - better handling of errors during calibration - cleanup canceling debug messages - remove old cancel() prototype - small sleep before clearing usb halt condition 2009-06-17 Stéphane Voltz * backend/lexmark_low.c: cleanups, better logging in is_home_line() and possible fix for bug #311862 by using f4 & f5 registers instead of f3 & f4 2009-06-16 Nicolas Martin * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: Declared Pixma MX320 in the pixma backend. Yet untested. 2009-06-16 Julien Blache * frontend/saned.c: fix strict aliasing issues, now problematic with gcc 4.4. 2009-06-14 Nicolas Martin * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: Thanks to Vladimir Andreev patch, fix network scan for MP800R. Documentation updates for latest MF5770 changes. 2009-06-13 Alex Belkin * backend/xerox_mfp.conf.in, doc/descriptions/xerox_mfp.desc: usbids for some Samsung devices. * backend/xerox_mfp.c: backend version increment. 2009-06-09 Chris Bagwell * sanei/sanie_tcp.c, backend/epson2_net.c: Update sanei_tcp_read() to better simulate old behavior of MSG_WAITALL. recv() tends to return MTU sized chunks of data without that option. And at least cygwin doesn't support MSG_WAITALL. Re-enable epson2 using that function for big receives. * libgetopt.c, libgetopt1.c: Revert some logic meant to be used only when we were always compiling internal getopt() functions. Fix header filename. 2009-06-09 Stéphane Voltz * backend/lexmark.h backend/lexmark.c backend/lexmark_low.c: cleanups, better 'fake usb' and possible fix for bug #311862 2009-06-08 m. allan noah * backend/canon_dr.c: backend v29 - split coarse and fine cal to run independently - add side option - reset scan params to user request if calibration fails - better handling of sane_cancel - better handling of errors during sane_start and sane_read 2009-06-08 Nicolas Martin * backend/pixma_mp730.c, backend/pixma_imageclass.c: Modifications to support Canon ImageClass MF5770 2009-06-04 Pierre Willenbrock * backend/genesys_gl841.c: Make calibration steps always send registers before trying to acquire a line. 2009-05-29 Pierre Willenbrock * doc/descriptions/genesys.desc: Added description for Visioneer XP100r3, Visioneer Roadwarrior, demoted DocketPort 465 to "untested" 2009-05-28 m. allan noah * backend/pnm.c: hide 'warming up' and 'hw locked' 'devices' 2009-05-26 m. allan noah * backend/canon.c: clamp film type to allowed range (bug #311685) 2009-05-25 m. allan noah * backend/fujitsu.[ch]: backend v94 - add side option to show which duplex image is being transferred - convert front and simplex buffers to use much less ram - add lowmemory option which makes duplex back buffer small too - refactor image handling code to track eof's instead of lengths - do color deinterlacing after reading from scanner, before buffering 2009-05-25 Nicolas Martin * sanei/sanei_usb.c: Adds debug verbosity to last sanei_usb.c patch. 2009-05-23 Nicolas Martin * doc/sane-pixma.man, doc/descriptions/pixma.desc: Updated documentation for Pixma MultiPass MP730. 2009-05-22 Alex Belkin * backend/xerox_mfp.c: - additional error check to (stop scanadf) + fix of it. 2009-05-22 Nicolas Martin * backend/pixma_imageclass.c, backend/pixma_mp730.c, sanei/sanei_usb.c: - pixma_imageclass.c: supported devices list cleanup. - pixma_mp730.c: fixed scan area width adjust and end of scan protocol for MP730. - sanei/sanei_usb.c: in sanei_usb_open(), changed "endpoints seek loop" to take into account only interfaces detected by sanei_usb_init() Fixes Pixma MP730 usb wrong endpoints selection. 2009-05-19 Pierre Willenbrock * backend/genesys_devices.c, backend/genesys_low.h: Add Pentax DSmobile 600, Syscan 485, DCT 487 2009-05-18 Stéphane Voltz * backend/genesys_gl646.h: fixed missing part of the include 2009-05-13 Stéphane Voltz * backend/genesys_gl646.c backend/genesys_gl646.h: created a genesys_gl646.h header file, moved all declarations from genesys_gl646.c to this file. 2009-05-13 Nicolas Martin * backend/pixma_mp150.c: Fixed Pixma MP600R for network scan. Changes apply to MP600R _and_ MP600, to use now a strict clone of Canon's driver protocol. 2009-05-09 Chris Bagwell * */*.c: standardize including local sane include files. 2009-05-09 Nicolas Martin * backend/pixma_mp150.c, doc/descriptions/pixma.desc: Fixed Pixma MP600 and MP600R for 2400 dpi scan, updated their descriptions. 2009-05-08 m. allan noah * backend/canon_dr*: backend v27 - bug fix in read_panel() - initialize vars in do_usb_cmd() - set buffermode off by default - clear page counter during init and sane_start() - eject previous page during init and sane_start() - improved SSM_BUFF macros - moved set_window() to after ssm-*() - add coarse calibration (AFE offset/gain & per-channel exposure) - add fine calibration (per-cell offset/gain) - free image and fine cal buffers in sane_close() - compare page counter of small scanners only in non-buffered mode - add back-side gray mirroring code for DR-2580C 2009-05-06 Nicolas Martin * doc/descriptions/pixma.desc: Updated descriptions in pixma backend for ImageClass MF4010 and MF4018. 2009-05-06 Ilia Sotnikov * doc/descriptions/unsupported.desc: HP ScanJet 4500C/4570C/5500C is supported by the 'hp5590' backend. * doc/descriptions/hp5590.desc: Updated backend version, added HP ScanJet 4500C as completely supported, added HP ScanJet 5500C as untested, HP ScanJet 4570C/5550C/5590/7650 marked as completely supported * tools/check-usb-chip.c: Updated HP ScanJet model names displayed during the check_hp5590(). * backend/hp5590.c, backend/hp5590_cmds.c, backend/hp5590_cmds.h, backend/hp5590_low.c, backend/hp5590_low.h, doc/sane-hp5590.man: Updated the list of supported devices (added HP 5500C which is similar to 4570C, 5500C which is similar to 4500C) - only strings/comments, no code was changed. Updated backend version. Updated man page. 2009-05-06 Julien Blache * tools/sane-desc.c: unbreak udev rules, use ATTRS instead of ATTR for USB devices. * frontend/saned.c: pass the network fds to saned_avahi() so the Avahi process can close them. Fixes a hang possible hang of the net backend when saned is run in debug mode. 2009-05-05 Julien Blache * doc/backend-writing.txt, doc/releases.txt: update for the switch to git. 2009-05-04 Chris Bagwell * acinclude.m4: Rename --enable-fork-process to --enable-pthreads to match internal usage. Stop linking in pthread when disabled. * backend/Makefile.am: Add $PTHREAD_LIBS to all backends that include sanei_thread. * Makefile.am: Add new ChangeLog-1.0.20 to distribution list. 2009-05-03 m. allan noah * ChangeLog-1.0.20, Makefile.in: ChangeLog from 1.0.20 release * configure, configure.in: version 1.0.21cvs * doc/releases.txt: minor updates for new build system * doc/descriptions/canon_dr.desc, doc/descriptions/coolscan3.desc, doc/descriptions/rts8891.desc, doc/descriptions/xerox_mfp.desc: remove :new tag 2009-05-03 Chris Bagwell * Makefile.in, aclocal.m4, configure, */Makefile.in: Regenerated with newer automake. * backend/Makefile.am, tools/sane-config.in: Add missing $GPHOTO2_LDFLAGS so library can be found. * tools/Makefile.am: Add missing $SCSI_LIBS to sane-find-scanner. Older entries can be found in ChangeLog-1.0.20. backends-1.3.0/ChangeLogs/ChangeLog-1.0.22000066400000000000000000000542111456256263500176100ustar00rootroot00000000000000****** Release of sane-backends 1.0.22. End of code freeze ****** 2011-02-10 m. allan noah * po/nl.po: updates from Martin Kho * po/uk.po: updates from Yuri Chornoivan 2011-02-04 Stéphane Voltz * backend/genesys.c: disable image processing options at 16 bits since they can't handle it. 2011-02-02 Stéphane Voltz * backend/genesys.c backend/genesys_low.c backend/genesys_gl843.c: more asynchronous parking issues 2011-01-31 Stéphane Voltz * backend/genesys.c backend/genesys_low.[ch]: don't return EOF too early when applying image processing functions, handle writing of lineart data, improve asynchronous head parking 2011-01-31 m. allan noah * backend/epjitsu.c, doc/descriptions/epjitsu.desc: - comment changes - added new models * backend/fujitsu.c, doc/descriptions/fujitsu.desc, doc/sane-fujitsu.man: fujitsu backend v106, - added new models - don't call mode_select with a page code the scanner does not support 2011-01-28 Mike Kelly * backends/avision.[ch]: - Bumped build number to 294. - Various minor code cleanups. - Fixed initialization and duplexing for AV220-G - Added ADF support for HP8200 series scanner (flipping duplexers). - Return copied values in sane_get_parameters() - Added paper-length option to prevent double feeds - Added a Misc option group to hold misc features 2011-01-26 m. allan noah * backend/canon_dr.[ch], doc/descriptions/canon_dr.desc, backend/canon_dr.conf.in, doc/sane-canon_dr.man: canon_dr backend v37: - don't center window when using flatbed - improve request sense error messages - enable flatbed for all known models - ad usb ids for DR-6030C, CR-135i & CR-190i * doc/descriptions/unsupported.desc: moved DR-2020U from canon_dr.desc 2011-01-20 Stéphane Voltz * backend/genesys_devices.c backend/genesys_gl646.h: tune resolution list and add internal values matching them 2011-01-18 m. allan noah * backend/canon_dr.[ch], doc/desc/canon_dr.desc, doc/sane-canon_dr.man: canon_dr backend v36: - initial support for DR-3080 and DR-5060 - add code to clamp scan width to an arbitrary byte width boundary - add code to prevent setting of brightness/threshold/contrast - don't send dropout color command on non-color scanners - initial support for DR-7090C - update credits * po/pl.po: updated translation from Jakub Bogusz * po/de.po: updated translation from Matthias Mailander * backend/canon.conf.in: improved detection of scsi scanners 2011-01-17 Stéphane Voltz * doc/descriptions/genesys.desc: add DSmobile variant 2011-01-15 Reinhold Kainhofer * sanei/sanei_usb.c: Add function sanei_usb_set_endpoint to change the endpoints for usb communications. * backend/magicolor.c: Add new "magicolor" backend for KONICA MINOLTA magicolor 1690MF devices 2011-01-13 Stéphane Voltz * backend/genesys_devices.c backend/genesys_gl124.c backend/genesys_gl124.h doc/descriptions/genesys.desc: add 2400 dpi mode for LiDE 110 and 210 2011-01-12 Stéphane Voltz * backend/genesys.c backend/genesys_devices.c backend/genesys_gl124.c backend/genesys_gl124.h backend/genesys_low.c backend/genesys_low.h : prepare 2400 dpi modes for LiDE 210/110, add a wait for head to park function and use it in sane_start(). 2011-01-06 Stéphane Voltz * backend/genesys.c backend/genesys_devices.c backend/genesys_gl847.c: fix lineart issue when yres is higher than xres and dynamic lineart is enabled, make gl847 wait for head to park between scans, wait for head to park in sane_close() 2011-01-05 Nicolas Martin * backend/pixma_mp730.c: pixma: Added patch proposed by Klaus Stengel for Canon imageRunner 1020/1024/1025 support. 2011-01-04 Nicolas Martin * backend/pixma_mp150.c, backend/pixma.c, backend/pixma.h backend/pixma_common.c, backend/pixma_common.h: pixma: several updates for MP830, MP990, memmove bug fix, and 64 bits image size. 2011-01-04 Stéphane Voltz * backend/genesys.c backend/genesys_devices.c backend/genesys_gl124.c: fix 1200 dpi lineart issues for gl124, re-enable low resolutions for lineart for gl124, and geometry fine tuning for LiDE 110/210 2011-01-03 Stéphane Voltz * backends/lexmark_low.c: fix compilation issue due to incorrect place for a DBG statement. 2011-01-03 Chris Bagwell * backends/Makefile.am: Add SOCKET_LIBS to xerox_mfp for platforms such as solaris since it uses sanei_tcp.k * doc/sane.tex: removed reference to changelog package since its not used and not installed by Macports (#312596) * doc/Makefile.am: put quotes around TEXINPUTS in case paths have spaces in name. This is so latex can find html.sty package that is referenced by sane.tex. 2011-01-01 Chris Bagwell * */Makefile.in, configure, aclocal.m4: Generate from latest autoconf (2.63 to 2.66). * m4/*: Added new libtool helper files. * ltmain.sh: updated to latest libtool and added back special sane soname work around. * po/Makefile.am: Allow "make dist" to work when translations disabled. * sanei/Makefile.am: allow "make check" to work on cygwin. 2010-12-28 Stéphane Voltz * backend/genesys.conf.in backend/genesys_devices.c backend/genesys_gl646.c backend/genesys_gl847.c backend/genesys_gl847.h: fix HP2300 warming up problem, add LiDE 700F and 5600F to gl847 devices, smooth DSmobile 600 moves. 2010-12-27 Stéphane Voltz * backend/genesys.c backend/genesys.conf.in backend/genesys_devices.c backend/genesys_gl841.c backend/genesys_low.h doc/descriptions/genesys.desc doc/descriptions/unsupported.desc doc/sane-genesys.man: add Plustek OpticBook 3600 support by Chris Berry and Michael Rickmann 2010-12-27 Stéphane Voltz * backend/genesys_devices.c backend/genesys_gl124.c backend/genesys.c doc/descriptions/genesys.desc doc/sane-genesys.man: enable 16 bit gamma for gl124 scanners, models and credits update. 2010-12-24 Stéphane Voltz * backend/genesys_devices.c backend/genesys_gl124.c backend/genesys.c doc/descriptions/genesys.desc: GL847 shading area size fix, GL124 shading area tuning, descriptions update. 2010-12-24 Stéphane Voltz * backend/lexmark*.[ch]: applied X74 support patch by Torsten Houwaart 2010-12-23 Mike Kelly * backends/avision.[ch]: - Bumped build number to 293. - Removed "regularly tested" from various scanners. - Fixed USB id for Avision FB2080E. - Added support for the Avision AV210D2+ scanner. - Moved device init code to sane_get_devices(). - Rewrote indirect "adf_mode" code to be more direct. - Fixed indentation. - Changed AVISION_FILTER_* defines to use final values. - Wrap duplicate USB id entries with ifdefs. - Removed a duplicate entry for Avision AV210C2. - Print out USB ids as four digit hex numbers. - Enhanced sane_get_option_descriptor() debug message. * doc/descriptions/avision.desc: - (Re)generated from avision.c. 2010-12-19 Stéphane Voltz * backend/genesys_gl124.h: LiDE 210 GPIO fix 2010-12-16 Stéphane Voltz * backend/genesys.c backend/genesys.conf.in backend/genesys_devices.c backend/genesys_gl124.h: LiDE 210 support (GL124 based) 2010-12-14 Stéphane Voltz * backend/genesys*.[ch] backend/genesys.conf.in backend/Makefile.am backend/Makefile.in tools/check-usb-chip.c doc/desc/genesys.desc: LiDE 110 support (GL124 based) 2010-12-08 Mike Kelly * doc/descriptions/avision.desc: - (Re)generated from avision.c. * doc/sane-avision.man: - Added myself as maintainer. * backends/avision.desc: - Removed this obsolete file (again). * backends/avision.c: - Bumped version to 292. - Added myself as maintainer. - Added support for DocuMate262i. - Added support entries for HP 8300 series. * backends/avision.conf.in: - Added myself as maintainer. 2010-12-06 Julien Blache * doc/descriptions/espon.desc, doc/descriptions/epson2.desc: add SCSI IDs for the Perfection 3200 (GT-9800) connected through FireWire. Report from Colin Kincaid Williams . Update Perfection 2450 interface list. 2010-12-02 m. allan noah * backend/fujitsu.[ch], backend/fujitsu-scsi.h: Fujitsu backend version 105 - backup and restore image params around image processing code - cache software crop/deskew parameters for use on backside of duplex - fi-6110 does not support bgcolor or prepick 2010-11-29 Olaf Meeuwissen * backend/epson2-ops.c: fix list of supported commands for levels D1 and D2. 2010-11-24 m. allan noah * backend/fujitsu.c, backend/fujitsu.conf.in, doc/descriptions/fujitsu.desc, doc/sane-fujitsu.man: Fujitsu backend versions 103 and 104 - remove compiled-in default config file - initial support for new fi-6xxx machines - never request more than s->buffer_size from scanner - silence noisy set_window() calls from init_interlace() 2010-11-19 Julien Blache * doc/descriptions-external/epkowa.desc: update for iScan 2.26.1, from Olaf Meeuwissen . 2010-11-17 Stéphane Voltz * backend/genesys.c: don't write scan data unless specified by debug level. 2010-11-08 Marc Deslauriers * backend/v4l.c: convert v4l1 BGR data to RGB; v4l1 always returns BGR for RGB due to a coding error way back. 2010-11-07 Marc Deslauriers * backend/v4l.c: loop through the read buffers in the v4l backend so we don't get stale frames. 2010-11-06 Stéphane Voltz * frontend/scanimage.c doc/scanimage.man: add -A/--all-options to list all control options exposed by a backend. 2010-11-01 Stéphane Voltz * frontend/scanimage.c: don't print readonly device options since they can't be set on command line 2010-11-01 Stéphane Voltz * backend/genesys.c backend/genesys_devices.c backend/genesys_gl843.c backend/genesys_gl843.h backend/genesys_gl847.c: up build number to 41, rework head parking, fixed clear-calibration option reading, make calibration cache expire for non sheetfed gl847 scanners, tuned KV-SS080 timings to improve image quality, raised LiDE 100/200 default gamma to 1.7 2010-10-31 Julien Blache * doc/descriptions/hp.desc: add SCSI IDs for ScanJet 4c. 2010-10-30 Julien Blache * backend/xerox_mfp.conf.in, doc/descriptions/xerox_mfp.desc: add Samsung SCX 4824 & 4825FN (Debian #601748). Resync desc file with config file. 2010-10-13 Stéphane Voltz * backend/genesys_devices.c backend/genesys_gl843.c backend/genesys_gl843.h: added 100, 150, 400 and 600 dpi modes for G4050 and G4010. Added 500 and 400 dpi modes for KV-SS080 2010-09-30 Alex Belkin * Makefile.in backend/Makefile.am backend/Makefile.in backend/xerox_mfp-tcp.c backend/xerox_mfp-usb.c backend/xerox_mfp.c backend/xerox_mfp.conf.in backend/xerox_mfp.h doc/Makefile.in doc/descriptions/xerox_mfp.desc doc/sane-xerox_mfp.man frontend/Makefile.in include/Makefile.in japi/Makefile.in lib/Makefile.in po/Makefile.in sanei/Makefile.in testsuite/Makefile.in tools/Makefile.in: Added Samsung SCX-4500W scan over network support for xerox_mfp backend (by Alexander Kuznetsov). autoconf and automake reconfigure to support new files. 2010-09-28 Stéphane Voltz * backend/genesys.c backend/genesys.conf.in backend/genesys_devices.c backend/genesys_gl843.c backend/genesys_gl843.h: add minimal support (200 and 300 dpi) for HP G4050. Add a device entry for G4010. 2010-09-18 Reinhold Kainhofer * include/sane/sanei_config.h sanei/sanei_config.c: Add function sanei_config_get_paths to obtain all configuration paths (from env var SANE_CONFIG_DIR and default paths); fix pointers to invalid/freed strings when SANE_CONFIG_DIR is set. * backend/dll.c: When searching for the dll.d/ directory, also use the SANE_CONFIG_DIR env variable. 2010-07-12 Julien Blache * doc/descriptions-external/epkowa.desc: update for iScan 2.26.0, from Alesh Slovak . 2010-09-17 Stéphane Voltz * backend/genesys.c backend/genesys_low.h sanei/sanei_magic.c backend/genesys_devices.c backend/genesys_gl843.c: do asynchronous head parking for flatbed scanners to save scan time. Fix a couple of memory overwrites detected by valgrind. Optimize shading data writing and fine tune scaneara for gl843. 2010-09-16 Julien Blache * frontend/saned.c: exit Avahi process on error and when the poll loop terminates. 2010-09-13 Stéphane Voltz * doc/descriptions/unsupported.desc: update status of scanners now supported by the genesys backend. 2010-09-13 Stéphane Voltz * doc/sane-genesys.man doc/descriptions/genesys.desc backend/genesys.con.in: update HP3690 status to supported 2010-09-12 Stéphane Voltz * backend/genesys_conv.c backend/genesys.c doc/sane-genesys.man sanei/sanei_magic.c backend/genesys.h backend/genesys_low.h backend/Makefile.in backend/Makefile.am: add software deskep, crop and despeckle for the genesys backend. 2010-09-08 Nicolas Martin * backend/pixma_mp150.c, backend/pixma_mp730.c doc/descriptions/pixma.desc, doc/sane-pixma.man: pixma: several updates for MP375R, MP390, MP460 and docs by Gernot Hassenpflug. 2010-09-07 Nicolas Martin * backend/pixma_mp750.c: pixma: fix for 2400 dpi striping on MP760/770/780/790 by Gernot Hassenpflug. 2010-09-05 Nicolas Martin * doc/sane-pixma.man: pixma: man page update for devices as reported by Gernot Hassenpflug. 2010-09-05 Stéphane Voltz * backend/genesys_gl841.c: end of document detection rework for sheetfed scanners. 2010-09-04 Stéphane Voltz * backend/genesys_devices.c backend/genesys.c backend/genesys_gl843.c: 1200 dpi resolution support for KV-SS080. 2010-09-01 Stéphane Voltz * backend/genesys_devices.c backend/genesys.c backend/genesys_gl843.c: support for scan button, faster move to scan area and calibration fine tuning. 2010-09-01 Nicolas Martin * backend/pixma.c, backend/pixma_mp730.c, backend/pixma_mp150.c doc/descriptions/pixma.desc: pixma: changes supplied by Gernot Hassenpflug for Lineart support to some Pixma devices, and desc updates. 2010-08-31 Stéphane Voltz * backend/genesys_devices.c backend/genesys.conf.in backend/genesys.c backend/genesys_gl843.[ch] backend/Makefile.am backend/Makefile.in: support for gl843 based scanners, starting with KV-SS080. disable true gray for LIDE35/50 since it breaks scanning 2010-08-02 Stéphane Voltz * backend/genesys_devices.c backend/genesys.conf.in: add a device entry for the Canoscan 5600f. 2010-07-25 Nicolas Martin * backend/pixma.c, backend/pixma.h, backend/pixma_common.c, backend/pixma_imageclass.c: pixma: changes for future Lineart scan mode support. some MF8030 settings, but device not yet supported. 2010-07-22 Nicolas Martin * backend/pixma_mp150.c, backend/pixma_imageclass.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: pixma: add support for PIXMA MX870. declared PIXMA MF8030, but not yet working. 2010-07-14 Julien Blache * doc/descriptions/epson.desc, doc/descriptions/epson2.desc: add :scsi keyword for the Perfection 2450 connected through FireWire. Courtesy of Brian Denheyer . 2010-07-12 Julien Blache * doc/descriptions-external/epkowa.desc: update for iScan 2.25.0, from Alesh Slovak . 2010-07-09 Julien Blache * backend/dll.c: allow symlinks under SANE_CONFIG_DIR/dll.d. 2010-07-04 Nicolas Martin * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: pixma: changes for PIXMA MX350 support, usb and ethernet. 2010-07-03 Nicolas Martin * doc/descriptions/pixma.desc, backend/pixma.c: pixma: fix ImageClass MF6550 description. fix end of line crop buffer size. 2010-07-02 Nicolas Martin * doc/descriptions/pixma.desc, doc/sane-pixma.man: pixma: update doc to include ImageClass MF6550 support. 2010-07-02 m. allan noah * backend/avision.c: patch for AV122-C2 from Ori Koren 2010-07-01 Nicolas Martin * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: pixma: thanks to Dan McGee patch, add support for PIXMA MX340. 2010-06-23 m. allan noah * backend/fujitsu.c: Fix compilation bug when jpeg support is enabled. 2010-06-23 Julien Blache * various: corrected initialization, printing, etc. in many backends 2010-06-21 Julien Blache * tools/sane-desc.c: udev rules: set libsane_matched=yes for SCSI devices too. Used by udev-acl later on. 2010-06-21 Stéphane Voltz * backend/genesys.c backend/genesys_devices.c backend/genesys_gl847.c backend/genesys_low.h: improve scan quality by using double x resolution internally at low resolution. Scan area geometry fine tuning. 2010-06-18 Stéphane Voltz * backend/genesys.c backend/genesys_devices.c backend/genesys_gl847.c: add 400, 200 and 100 dpi resolution to LiDE 100 and LiDE 200 . Disable true gray until it really works. 2010-06-15 Stéphane Voltz * backend/genesys.c backend/genesys_devices.c backend/genesys_gl847.c backend/genesys_gl847.h: Canon LiDE 200 support up to 1200 DPI 2010-06-15 m. allan noah * doc/descriptions/xerox_mfp.desc, backend/xerox_mfp.conf.in: Add Samsung SCX-4600 USB IDs * backend/kvs20xx.h: use sys/param.h instead of endian.h 2010-06-10 Stéphane Voltz * backend/genesys.c: calibration file name double free fix 2010-06-10 m. allan noah * backend/niash.c: patch for HP3300 from Yves Jeanrenaud * po/fr.po: updates from Yann E. MORIN 2010-06-10 Stéphane Voltz * backend/genesys_devices.c backend/genesys_gl847.c: LiDE 100 motor settings rework 2010-06-09 m. allan noah * backend/kvs20xx*, Makefiles, confs and docs: - Add new kvs20xx backend from Panasonic, for KV-S202xC and KV-S204xC 2010-06-09 m. allan noah * backend/fujitsu.[ch]: backend v100: - store more Request Sense data in scanner struct - clear Request Sense data at start of every do_cmd() call - track per-side ILI and global EOM flags - set per-side EOF flag if ILI and EOM are set 2010-06-09 m. allan noah * backend/Makefile.{am|in}: add genesys_gl847 files * po/Makefile.{am|in}: add LINGUAS POTFILES to EXTRA_DIST * Makefile.am: add Changelog-1.0.21 to EXTRA_DIST 2010-06-08 Nicolas Martin * backend/pixma_imageclass.c: pixma: set #1 of modifications for ImageClass MF65xx series. 2010-06-07 Stéphane Voltz * backend/genesys.c backend/genesys_devices.c backend/genesys_gl847.c: LiDE 100 motor fine tuning 2010-06-03 Stéphane Voltz * backend/genesys.c backend/genesys_devices.c backend/genesys_gl847.c doc/descriptions/genesys.desc: final bits for full LiDE 100 support 2010-05-31 Stéphane Voltz * backend/genesys.c backend/genesys_devices.c backend/genesys_gl847.c: shading calibration is working and led calibration has been tuned 2010-05-30 Stéphane Voltz * backend/genesys.c backend/genesys_devices.c backend/genesys_gl847.c backend/genesys_low.h: Canon LiDE 100 working without shading calibration which is the final bit to fix 2010-05-24 m. allan noah * backend/avision.c, doc/descriptions/avision.desc: add Fujitsu fi-5015C 2010-05-20 Stéphane Voltz * backend/genesys.c backend/genesys_devices.c backend/genesys_gl847.c: make led and shading calibration work for LiDE 100 2010-05-16 Chris Bagwell * README.freebsd: Add suggested hint that configure needs to be told location of external libraries installed through Ports. 2010-05-16 Stéphane Voltz * backend/genesys.[ch] backend/genesys_devices.c backend/genesys.conf.in backend/genesys_gl847.[ch] backend/Makefile.in: start of LiDE 100 and LIDE 200 support 2010-05-16 Stéphane Voltz * sanei/sanei_usb.c include/sane/sanei_usb.h: add a sanei_usb_reset() function. 2010-05-15 m. allan noah * backend/fujitsu.c: backend v99 - sense_handler(): collect rs_info for any ILI, not just EOM - do_usb_cmd(): use rs_info whenever set, not just EOF - read_from_*(): better handling of EOF from lower level functions - sane_read(): improve duplexing logic 2010-05-03 Pierre Willenbrock * Change status of CanoScan LiDE 40 from untested to good 2010-04-27 m. allan noah * po/Makefile.in: include POTFILES and LINGUAS in DIST * doc/releases.txt: more git notes 2010-04-25 Julien Blache * tools/sane-backends.pc.in: syntax fixes. 2010-04-25 m. allan noah * ChangeLog-1.0.21, Makefile.in: ChangeLog for 1.0.21 release * configure, configure.in: version 1.0.22git * doc/descriptions/kodak.desc, doc/descriptions/kvs1025.desc, doc/descriptions/p5.desc: remove :new tag * doc/releases.txt: minor updates due to new git hooks Older entries can be found in ChangeLog-1.0.21. backends-1.3.0/ChangeLogs/ChangeLog-1.0.23000066400000000000000000000727731456256263500176260ustar00rootroot00000000000000****** Release of sane-backends 1.0.23. End of code freeze ****** 2012-08-18 Rolf Bensch * po/nl.po: Updated Dutch translation from Martin Kho. 2012-08-17 Chris Bagwell * backend/kvs40xx*: Fix scan() symbol name that was still conflicting the epjistu and snapscan backends. 2012-08-16 Rolf Bensch * backend/pixma.[ch], backend/pixma_common.[ch], backend/pixma_imageclass.c, backend/pixma_mp*.c: Copyright updated. 2012-08-11 Rolf Bensch * backend/pixma.c, backend/pixma_mp150.c, backend/pixma_mp810.c: Lineart fix for generation 1+2 scanners. 2012-08-09 Paul Newall * /backend/kodakaio.c: calling of poll tidied up, may fix problems with repeated scans. 2012-07-30 Stéphane Voltz * doc/sane-genesys.man backend/genesys_low.h backend/genesys*.c: rewrite lineart emulation du to bugs exhibited by the use of the genesys backend through saned. Fixed an option delcartion that led to saned crash and fixed batch scanning with sheet-fed scanners. 2012-08-07 Chris Bagwell * backend/kvs20xx*, backend/kvs40xx*: Fix duplicate symbols caused by copy&pasting between related backends. This allows prelinking of backends to work. * backend/hp5590_low.c: Fix unresolved symbols error by including byteorder.h header. * backend/dll.c: Use correct function prototype for prelink version of DLL backend. Helps some 64-bit compilers. 2012-07-31 Rolf Bensch * doc/descriptions/pixma.desc, doc/sane-pixma.man: doc updates for all PIXMA scanners * backend/pixma.h, doc/descriptions/pixma.desc, doc/sane-pixma.man: - Pixma backend version 0.17.0 - date updated in Pixma man page 2012-07-30 m. allan noah * backend/kvs1025.h: Increase max paper size (Matthew Wild) * doc/*.man, doc/*.html: Typo fixes (Yuri Chornoivan) 2012-07-30 Stéphane Voltz * backend/genesys.c backend/genesys_gl843.c backend/genesys_gl646.c: fix batch scanning for gl646 scanners 2012-07-29 Paul Newall * /doc/descriptions/kodakaio.desc: advent AW10 added. 2012-07-28 Paul Newall * /doc/descriptions/kodakaio.desc: usbids added and version. 2012-07-28 Rolf Bensch * doc/descriptions/pixma.desc: scanners resorted by name * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: new scanners reported by DMoeller - Canon PIXMA MX410 as untested - Canon PIXMA MX420 as complete - Canon PIXMA E500, E600, MX370 Series, MX430 Series, MX710 Series as untested and experimental * backend/pixma_mp150.c, doc/descriptions/pixma.desc: capabilities updated for Canon PIXMA MX510 and MX890 * backend/pixma_mp150.c, doc/sane-pixma.man: all 2012 untested new devices marked as experimental 2012-07-26 Rolf Bensch * README.linux: New detailed description for the installation of SANE backend. 2012-07-24 m. allan noah * backend/kvs40xx_opt.c: Oops- it is new * doc/descriptions/kvs40xx.desc: Fix typo (Yuri Chornoivan) * po/uk.po: Updated (Yuri Chornoivan) * tools/Makefile.am, tools/Makefile.in, tools/sane-config.in: sane-config fixes (Ruediger Meier) 2012-07-15 Ilia Sotnikov * backend/hp5590.c, backend/hp5590_low.c: Fix hp5590 backend on big-endian platforms provided by Nhan Ngo Dinh 2012-07-13 Stéphane Voltz * backend/genesys.c backend/genesys_devices.c backend/genesys_gl124.c backend/genesys_gl646.c backend/p5.c backend/p5.h backend/p5_device.h backend/rts8891.c backend/rts8891.h backend/rts8891_devices.c backend/rts8891_low.c backend/rts8891_low.h backend/rts88xx_lib.c backend/rts88xx_lib.h backend/umax_pp.c backend/umax_pp.h backend/umax_pp_low.c backend/umax_pp_low.h backend/umax_pp_mid.c backend/umax_pp_mid.h doc/sane-genesys.man sanei/sanei_magic.c : misc cleanups and doc updates to prepare release 2012-06-28 Paul Newall * backend/kodakaio.c backend/kodakaio.conf.in backend/kodakaio.h doc/sane-kodakaio.man doc/descriptions/kodakaio.desc configure.in makefile.am dllconf.in doc/makefile.am: Added new backend kodakaio for kodak ESP nnnn, Cnnn, hero AiOs, detection of cups added to configure since cups is used for network auto detection. 2012-06-27 Rolf Bensch * backend/pixma.[ch], backend/pixma_sane_options.c, doc/descriptions/pixma.desc: - Pixma backend version 0.16.4 - new scan modes for 48 bit flatbed scanners: PIXMA_SCAN_MODE_COLOR_48, PIXMA_SCAN_MODE_GRAY_16, enabled by capability PIXMA_CAP_48BIT * backend/pixma_mp150.c: unused capability PIXMA_CAP_48BIT removed * backend/pixma_mp810.c: - new capability for CS9000F: PIXMA_CAP_48BIT - functions for scan mode detection improved - lowest resolution for 48 bit flatbed scan modes is 150 dpi * po/de.po: German translations for new scan modes 2012-06-04 Stéphane Voltz * backend/genesys.c backend/genesys.h backend/genesys_low.h backend/genesys_devices.c backend/genesys_gl124.[ch] : LiDE 110/210 led calibration improvements, add a 'lamp off during scan' option, add 2400x4800 mode to LiDE 100, 110 and 210, improve remove/add scanner detection. 2012-06-01 Stéphane Voltz * backend/genesys.c backend/genesys_gl841.c: apply led calibration fix 2012-06-01 Stéphane Voltz * backend/genesys.c: fix get_device to handle scanner plugging and unplugging 2012-05-31 Stéphane Voltz * doc/descriptions/genesys.desc doc/sane-genesys.man doc/descriptions/unsupported.desc: updated Xerox onetouch 2400 status to supported 2012-05-30 Stéphane Voltz * backend/genesys.c backend/genesys_gl124.c backend/genesys_gl124.h: align gl124 code on latest gl847 improvements 2012-05-29 Stéphane Voltz * backend/genesys.c backend/genesys_devices.c backend/genesys_gl847.c backend/genesys_gl847.h doc/descriptions/genesys.desc: add 4800 dpi for LiDE 700F - improve fedd/move to scan area for gl847 based scanners - remove non working 400 dpi mode 2012-05-28 Stéphane Voltz * backend/genesys.c backend/genesys_devices.c backend/genesys_gl124.c backend/genesys_gl847.c backend/genesys_gl847.h backend/genesys_low.h doc/descriptions/genesys.desc doc/sane-genesys.man: add support for LiDE 700F up to 2400 dpi 2012-05-10 m. allan noah * backend/fujitsu.c: backend v111 - call send_* and mode_select_* from sane_start - split read payloads into new debug level - add paper-protect, staple-detect and df-recovery options 2012-05-09 m. allan noah * backend/fujitsu.[ch], backend/fujitsu-scsi.h: backend v110 - correct max_y_fb for fi-62x0 series - add must_fully_buffer helper routine - add hwdeskewcrop option, with fallback to software versions - add 'actual' param to get_pixelsize for post-scan - add recent model VPD params - only set params->lines = -1 when using ald without buffering - fix bugs in background color when using software deskew * sanei/sanei_magic.c: Update deskew algo - allow paper to be +/- 1 inch from top of image - correct integer overflow - improve (disabled) debug logs 2012-05-03 Rolf Bensch * backend/pixma_io_sanei.c, backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: Patch for Canon Pixma MP280 from Daniel Beer. - PIXMA_EOF mapped to PIXMA_ETIMEDOUT. - Maximum resolution reduced to 600dpi. - Scanner added to doc files. 2012-04-23 Rolf Bensch * AUTHORS: Pixma backend and email addresses updates. 2012-04-20 Rolf Bensch * tools/sane-desc.c: tools/udev/libsane.rules supports scanner group access to any scanner, with and without acl support. 2012-04-17 Rolf Bensch * backend/pixma.c, backend/pixma_sane_options.c, po/de.po: New description for device specific option "source". 2012-04-17 Rolf Bensch * backend/pixma.c: select first entries of dynamic dpi list and dynamic mode list as default values after changing the scan source 2012-04-04 Rolf Bensch * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: New scanners Canon Pixma MX510 and Canon Pixma MX890. Both are untested. 2012-04-04 Rolf Bensch * backend/pixma.[ch], backend/pixma_mp810.c, po/de.po: color and grayscale negatives scan in TPU mode, for CS8800F and CS9000F. 2012-04-04 Rolf Bensch * backend/pixma_mp810.c: cropping y and h to scanable area in TPU mode, for CS8800F and CS9000F. 2012-03-29 Stéphane Voltz * backend/genesys_gl124.c backend/genesys_devices.c: use feed earlier at high resolution. Tune LiDE 110/210 geometry. 2012-03-26 Stéphane Voltz * doc/descriptions/unsupported.desc: removed G4010/G4050, patch by Martin Michlmayr . 2012-03-25 Mike Kelly * backends/avision.[ch]: - Added Xerox Documate 632. - Added firmware checking for HP5370c scanners. - Reverted AV610 USB IDs and removed AV_INT_STATUS. - Fixed warning about printf and size_t. - Fixed bug using wrong enum in x/y range check. 2012-03-22 Rolf Bensch * po/de.po: New German translations for pixma backend. 2012-03-21 Mike Kelly * backends/avision.[ch]: - Added Gray mode support for Kodak i30 and i40. - Skip post-processing when caching flipping duplex. - Moved a comment nearer its code. - Added sane_reload_devices() to sane_init(). - Corrected the logic to set lines = -1 for ADF mode. - Fix calculations when flipping back of duplex page. - Track the number of lines to flip as a negative number. 2012-03-20 Stéphane Voltz * backend/genesys_*.[ch]: gl841 sheetfed scanners calibration improvement, fix document end detection when doing dynamic lineart. Copyrights updates. 2012-03-19 Gerhard Jaeger * backend/plustek.c: Fix batch scanning with Plustek backend Patch submitted and tested by Elias Oltmanns 2012-03-19 Stéphane Voltz * backend/genesys_devices.c backend/genesys_gl843.c: update HP4850 geometry and fix initial state 2012-03-06 Rolf Bensch * backend/pixma_bjnp.c: Replace index() with strchr(). Bug #313563. 2012-02-29 Rolf Bensch * backend/pixma.[ch], backend/pixma_common.c, backend/pixma_sane_options.[ch]: New device specific option 'threshold-curve'. Can be used to optimize 1 bit B/W lineart scans. 2012-02-27 Rolf Bensch * backend/pixma_imageclass.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: New scanner Canon imageCLASS MF4410 from Vasiliy Olekhov. 2012-02-16 Rolf Bensch * backend/pixma.c, backend/pixma_sane_options.[ch]: New device specific option 'gamma'. 2012-02-09 Rolf Bensch * backend/pixma.[ch], backend/pixma_common.[ch], backend/pixma_sane_options.[ch], backend/pixma_mp{150,810}.c: 1 bit B/W lineart for pixma_mp150 and pixma_810 subdrivers. 2012-02-06 Rolf Bensch * backend/pixma_mp150, doc/descriptions/pixma.desc, doc/sane-pixma.man: New scanner Canon PIXMA MX360. 2012-02-03 Rolf Bensch * backend/pixma.h: set PIXMA_VERSION_{MAJOR,MINOR,BUILD} to 0.16.3. 2012-02-03 Rolf Bensch * backend/scripts/pixma_gen_options.py: script to generate backend/pixma_sane_options.c and backend/pixma_sane_options.h from pixma.c. Found in old pixma project archive file http://home.arcor.de/wittawat/pixma/mp150-0.13.1.tar.bz2. 2012-01-31 Rolf Bensch * backend/pixma.[ch], backend/pixma_imageclass.c, backend/pixma_mp[17]50.c, backend/pixma_mp730.c, backend/pixma_mp810.c: modify dpi_list dependent on different scanner capabilities for normal (flatbed) and ADF/TPU modes. 2012-01-23 Rolf Bensch * backend/pixma_mp150, doc/descriptions/pixma.desc, doc/sane-pixma.man: New scanner PIXMA MX880 Series. 2012-01-21 Ruediger Meier * backend/canon_dr.h, backend/cardscan.h, backend/dll.c, backend/epjitsu.h, backend/fujitsu.h, backend/gt68xx.c, backend/kodak.h, backend/microtek2.h, sanei/sanei_access.[hc]: fix and cleanup portable PATH_SEP and DIR_PATH defines * sanei/sanei_scsi.c, tools/sane-find-scanner.c, configure.in, sane/config.h.in: use the right scsi header on win32. 2012-01-17 Rolf Bensch * backend/pixma_mp810.c: Flatbed mode supports max. 4800 dpi. 2012-01-14 m. allan noah * backend/Makefile.{am,in}, backend/pixma.[ch], backend/pixma_common.c, backend/pixma_mp[17]50.c, backend/pixma_mp810.c, backend/pixma_rename.h, doc/descriptions/pixma.desc, doc/sane-pixma.man: Updated Pixma backend from Gernot Hassenpflug. 2012-01-03 Stéphane Voltz * backend/genesys_gl646.c backend/genesys_gl847.c backend/genesys_gl43.[ch]: fixd scan line number for gl646 CCD scanners, improved led calbration for gl847 CIS scanners and G4050 XPA work progress. 2011-12-30 Alex Belkin * backend/xerox_mfp.conf.in doc/descriptions/xerox_mfp.desc: usb id for Samsung SCX-3205W, reported by sane tester. 2011-12-27 Chris Bagwell * configure.in, configure, config.h.in, sanei_usb.c, check-usb-chip.c, sane-find-scanner.c: Add check for libusb-win32 which is API compatible with libusb-0.1 but has different header file name to prevent conflicts with Windows own usb.h. Since libusb-1.0 is scheduled to support Windows (not a forked version), it will probably work as-is if user has pkg-config installed under mingw. 2011-12-20 m. allan noah * backend/fujitsu*, doc/descriptions/fujitsu.desc: backend v109 - added some MS and INQ information - increased default buffer size for later machines in config file - renamed new fi-6xx0Z models 2011-12-18 Chris Bagwell * doc/descriptions-external/epkowa.desc: update for new iScan 2.28.1 release from Olaf Meeuwissen . 2011-11-29 Stéphane Voltz * backend/genesys.c backend/genesys_low.h backend/genesys_devices.c backend/genesys_gl847.c: genesys backend build 67, gl847 led and shadingcalibration improvements. 2011-11-21 m. allan noah * backend/fujitsu.[ch]: backend v107 and v108 - M3091 does not support scanner_control(adf) - Correct buffer overflow in read_from_3091duplex() - sane_read() now always calls read_from_*() - read_from_*() are callable when there is no data, and read to eof - sane_read() will keep alternate duplex reads to similar length - Added debugging statements - Corrected comments - Updated Copyright - merged x/y resolution options - moved page width/height to start of geometry group - use mode to pick resolution list v/s range - improved M3091 resolution choices 2011-11-20 Chris Bagwell * epson2-commands.c: Include to resolve u_long. * epson2.c, magicolor.c, xerox_mfp-tcp.c: Include and to for anyone using setsockopt(). * sanei_tcp.h: Include since ssize_t is referenced. * sanei_usb.c: FreeBSD version checks. All changes in this batch come from FreeBSD ports patches. Bug #312503. 2011-11-20 Stéphane Voltz * backend/genesys_*.[ch] doc/descriptions/genesys.desc: build 66. Add hp N6310 and 4850C devices. Fix gl847 calibration. GL843 XPA support groundwork. 2011-11-14 Chris Bagwell * doc/descriptions-external/epkowa.desc: update for new iScan release from Olaf Meeuwissen . 2011-11-10 Chris Bagwell * include/sane/sanei_thread.h, sanei/sanei_thread.c: Make SANE_Pid map to pthread_t to be compilable on platforms where pthread_t is not a integer; namely mingw. * configure, configure.in, include/sane/config.h.in, lib/Makefile.am, lib/Makefile.in, lib/sleep.c: Add a sleep() replacement function; mostly for mingw. * backend/epson2-ops.c, backend/epson2.c, umax_pp_low.c: Revert some broken sleep()->usleep() conversions. 2011-11-09 Chris Bagwell * configure, configure.in, acinclude.m4: Convert enable_dynamic to "auto" behavior. Previous attempt was resulting in it always defaulting to "yes" and user had to use --disable-dynamic on platforms that couldn't support it. * backend/epson2-ops.c, backend/epson2.c, backend/epson2.h: minor portability changes to epson2. #ifdef some optional headers and use usleep() instead of sleep(). Switch to sanei_udb_set_noblock(). 2011-11-08 Chris Bagwell * backend/Makefile.*: Finish preload linking fix. dll.c was being linked in with convenience library. If it needs to have two behaviors then we need to create two libraries. Now preloading is working in libsane.so again and disabled in libsane-dll.so. * configure.in, configure, README, */Makefile.*: Update README to describe use of BACKENDS and PRELOADABLE_BACKENDS to limit backend compiles. Also, mark those in configure.in as variables so they show up in "configure --help". Add back the useful --disable-dynamic and --disable-preload that appear to have been removed at some point but are documented in README. * README.windows: Update info to include mingw references. 2011-11-07 Chris Bagwell * ltmain.sh: Disable sane's soname libtool hack for mingw platform so that DLL's will be created for each backin built; just like on unix platforms. * backend/Makefile.*: Add missing sanei_magic.lo to libsane backend. * sanei/sanei_tcp.c sanei/sanei_udp.c include/sane/sanei_udp.h: Add WSAStartup()/WSACleanup() calls on mingw to get ws2_32 working. Make util function to set sockets to nonblocking and make work in unix and mingw platforms. 2011-11-06 Chris Bagwell * INSTALL, Makefile.am, */Makefile.in, compile, config.guess, config.sub, configure, depcomp, include/sane/config.h.in, install-sh, ltmain.sh, libtool.m4, ltversion.m4, missing, mkinstalldirs: Update files using autoconf 2.68 and libtool 2.4. * configure.in, frontend/scanimage.c, include/sane/sanei_tcp.h, include/sane/sanei_udp.h, lib/inet_ntop.c, lib/inet_pton.c, sanei/sanei_tcp.c, sanei/sanei_udp.c, tools/sane-find-scanner.c: Add check for winsock2. Add ws2_32 library when found. Look for getuid and getpass since not on mingw. * lib/sigprocmask.c: Comment out logic on windows for now since it doesn't work. At least it will compile. * lib/syslog.c: Add a replacement syslog for at least mingw. * testsuite/Makefile.*: Use $(EXEEXT) so that scanimage can be run on windows. * backend/Makefile.*, backend/dll.c: Modify dll backend so that libsane-dll does not reference preloaded backends symbols since its not linking them in. Only libsane references preloaded backend symbols and also links them in now. 2011-11-05 Chris Bagwell * lib/vsyslog.c, frontend/scanimage.c, include/sane/sanei_tcp.h include/sane/sanei_udp.h, lib/inet_ntop.c, lib/inet_pton.c, sanei/sanie_init_debug.c, sanei/sanei_scsi.c, sanei/sanei_tcp.c, sanei_udp.c, sanei_usb.c: mingw32 compile fixes. Mostly its not including header files that windows doesn't have and add winsock.h as needed. Also, do not use signals windows doesn't have as well. 2011-11-02 Stéphane Voltz * backend/genesys_*.[ch] backend/Makefile.am backend/Makefile.in: genesys_gl841.h creation and genesys backend code cleanup 2011-11-01 m. allan noah * backend/canon_dr.[ch]: backend v39 - DR-2580C pads the backside of duplex scans 2011-11-01 Stéphane Voltz * backend/genesys_gl124.c backend/genesys_gl646.c backend/genesys_gl841.c backend/genesys_gl843.c backend/genesys_gl847.c backend/genesys_low.c backend/genesys_low.h: do include in a clean way 2011-10-30 Mattias Ellert * backend/kvs40xx.h: Use portable endian macro from config.h * backend/kodak.c: Use more precise path to internal headers * backend/canon630u.c, backend/genesys_gl124.c, backend/genesys_gl646.c, backend/genesys_gl841.c, backend/genesys_gl843.c, backend/genesys_gl847.c, backend/genesys_low.c, backend/hp3900_rts8822.c, backend/rts88xx_lib.c, backend/xerox_mfp.c, sanei/sanei_pio.c: Include for definition of u_long on MacOS X * po/sv.po: Update Swedish translation 2011-10-21 Stéphane Voltz * backend/genesys.c backend/genesys.h backend/genesys_devices.c backend/genesys_gl*.c backend/genesys_gl843.h backend/genesys_low.[ch], doc/sane.man doc/sane-genesys.man doc/descriptions/genesys.desc: 1200 and 2400 dpi support for G4010/G4050 2011-10-19 Alex Belkin * backend/xerox_mfp.conf.in doc/descriptions/xerox_mfp.desc: usb id for Samsung CLX 3185, reported by John Dignum. 2011-10-01 Alex Belkin * backend/xerox_mfp.conf.in doc/descriptions/xerox_mfp.desc: usb id for Samsung SCX-4828FN or SCX-4x28 Series, reported by Patrice Levesque. 2011-09-16 Nils Philippsen * doc/sane-hpljm1005.man doc/sane-p5.man: use groff escape sequences in man pages * AUTHORS NEWS doc/descriptions.txt doc/*/*.CHANGES doc/plustek/Plustek-*.txt doc/u12/U12.* doc/umax/sane-umax-parport-doc.html: encode to UTF-8 * tools/sane-config.in: use pkg-config * doc/descriptions/epson2.desc backend/epson_usb.c: add USB id for Epson Stylus SX125 2011-09-07 Stéphane Voltz * backend/genesys_devices.c backend/genesys_low.h backend/genesys_gl124.c: fix button mapping for LiDE 210 2011-08-25 Stéphane Voltz * backend/genesys_gl646.c backend/genesys_low.c backend/genesys_low.h backend/genesys.c: add a no move during shading calibratiob flag, and use it for MD5345 2011-08-23 Stéphane Voltz * backend/genesys_gl847.c backend/genesys_low.c backend/genesys_low.h backend/genesys.c backend/genesys_gl124.c backend/genesys_gl843.c: make sure to use the fatest speed when parking, use lowest sensor dpi as default resolution 2011-08-22 Stéphane Voltz * backend/genesys*.[ch]: enable calibration for G4050/G4010 and minor code refactors 2011-08-05 Stéphane Voltz * backend/genesys.c backend/genesys_low.c backend/genesys_conv.c: fixed generic calibration cache for CCD case and when yres is higher than sensor's maximum one. Fixed lineart data enlarging when yres is higher than xres. 2011-07-31 Stéphane Voltz * backend/genesys_gl646.c backend/genesys_gl841.c backend/genesys_gl843.c backend/genesys_gl847.c backend/genesys_gl124.c backend/genesys_low.[ch]: is_compatible cache refactor for gl843, gl847 and gl124 2011-07-31 Stéphane Voltz * backend/genesys_gl124.[ch] backend/genesys_gl843.[ch]: use sanei_genesys_compute_dpihw instead of specific function. 2011-07-15 Stéphane Voltz * backend/genesys_devices.c backend/genesys_gl646.c backend/genesys_gl847.c: re-tune scan area of MD5345/MD6228. parking function clean up. 2011-07-15 Stéphane Voltz * backend/lexmark.c backend/lexmark_low.c: increase tolerance when detecting the 'home' dot. 2011-07-07 Stéphane Voltz * backend/genesys.c: fixed incorrect image height for sheetfed scanners 2011-07-07 m. allan noah * po/pt.po: updated translation from cncsolutions.com.br 2011-07-06 m. allan noah * backend/canon_dr.[ch]: backend version 38 - initial support for DR-5020 - use ppl_mod instead of Bpl_mod, apply to all modes - invert logic of read_panel tracking - add ability to disable read_panel() - automatically disable read/send_panel if unsupported * doc/descriptions/canon_dr.desc: status of DR-5020 2011-07-06 Stéphane Voltz * backend/genesys_low.h backend/genesys_gl847.c backend/genesys.c backend/genesys_gl124.c backend/genesys_gl843.c backend/genesys_gl646.c backend/genesys_gl841.c: improve 4800 dpi quality for LiDE 200 by increasing the number of lines scan for shading 2011-07-05 Stéphane Voltz * backend/genesys_low.h backend/genesys_gl847.c backend/genesys_devices.c backend/genesys.c backend/genesys_gl124.c backend/genesys_gl843.c backend/genesys_gl646.c backend/genesys_gl841.c: make 4800 pdi work for Lide200. Calibration cache file leak fix. 2011-06-30 Olaf Meeuwissen * frontend/scanimage.c: plug a memory leak in batch mode. 2011-06-24 Julien Blache * tools/sane-desc.c: add a wildcard rule for Epson SCSI scanners with a model string beginning with "SCANNER". Idea from Olaf Meeuwissen. 2011-06-21 Julien Blache * doc/descriptions/epson2.desc: added SCSI IDs for the GT-10000+, reported by Simon Becherer. 2011-06-16 Alex Belkin * backend/xerox_mfp.conf.in doc/descriptions/xerox_mfp.desc: usb id for Samsung CLX-216x Series, tested with CLX-2160, reported by Malte Starostik 2011-06-15 Stéphane Voltz * backend/genesys.c backend/genesys_gl847.c: lineart fix 2011-06-13 Stéphane Voltz * backend/genesys_devices.c backend/genesys_gl646.c backend/genesys_gl646.h doc/descriptions/genesys.desc doc/sane-genesys.man: add full HP2400 support thanks a patch from Alexey Osipov 2011-06-13 Stéphane Voltz * backend/genesys*.[ch] : rework of gl847 to reach 2400 dpi for LiDE 100 and 4800 dpi for LiDE 200 2011-06-10 Stéphane Voltz * backend/lexmark_models.c: fix missing motor initialization 2011-06-07 Stéphane Voltz * backend/genesys.c backend/genesys.h backend/genesys_conv.c: add blank page skipping and rotation detection options 2011-06-06 m. allan noah * docs/*kvs40xx*, backend/kvs40xx*: New Panasonic KV-S40xx/70xx backend, originally by Panasonic Russia. * acinclude.m4, */Makefile.am, configure*: build new kvs40xx backend * po/POTFILES: add kvs* backends * po/.gitignore: ignore sane-backends.pot * include/sane/sanei_magic.h, sanei/sanei_magic.c: add new blank detection and rotation detection routines * backend/kvs1025*, backend/Makefile*: add support for sanei_magic 2011-06-02 Julien Blache * tools/sane-desc.c: add udev+acl output mode, udev rules using ACLs for the scanner group instead of making the device root:scanner 0664. This is designed to help with MFPs and play nice with ConsoleKit. 2011-04-20 Stéphane Voltz * backend/genesys_low.c backend/genesys_devices.c backend/genesys.conf.in: add Xerox 2400 onetouch model and improve big endian handling 2011-03-19 Julien Blache * tools/sane-desc.c: move away from using power/level for disabling USB autosuspend/power management and use power/control if available. 2011-03-18 Stéphane Voltz * backend/genesys_low.c backend/genesys.c : rewrite big endian fixes for gl847/gl124 based scanners. Improve calibration cache file handling. 2011-03-17 Stéphane Voltz * backend/genesys_low.c: big endian fixes for gl847/gl124 based scanners. Patch by Olaf Zimmermann . 2011-03-15 Stéphane Voltz * backend/Makefile.in backend/canon_dr.[ch]: fixes to allow full static build under cygwin 2011-03-12 Troy Rollo * backend/hp3500.c: Remove interdependency between contrast and brightness. 2011-03-12 Julien Blache * doc/scanimage.man: batch-start defaults to 1 if not specified, not 0. Reported by Jakub Wilk . 2011-03-12 Troy Rollo * backend/hp3500.c: Add grayscale and line art scanning. Add contrast and brightness controls which influence the calibration data provided to the scanner. 2011-03-06 Ilia Sotnikov * backend/hp5590.c, backend/hp5590_cmds.{c,h}: in ADF modes the device can scan up to 14", which is usually bigger than what scanner reports back during initialization * backend/hp5590.c, backend/hp5590_cmds.{c,h}, backend/hp5590_low.{c,h}: fixed detection of HP4500 devices (tested) and HP5550 (untested) - these devices need no reading USB-in-USB acknowledgement after each command. To achieve that, proto_flags are passed down to low-level functions. These flags are taken from device descriptions * backend/hp5590_low.{c,h}: fixed up get_status command - index should be 0x00, not 0x20 * backend/hp5590.c: bump up the backend version 2011-03-04 Julien Blache * frontend/saned.c: define PATH_MAX if needed, fixes build on HURD. Patch from Pino Toscano . 2011-02-16 Julien Blache * backend/v4l.c, backend/v4l.h: fix build with libv4l 0.8.3+. 2011-02-13 m. allan noah * doc/releases.txt: minor updates Older entries can be found in ChangeLog-1.0.22. backends-1.3.0/ChangeLogs/ChangeLog-1.0.24000066400000000000000000000635531456256263500176230ustar00rootroot00000000000000****** Release of sane-backends 1.0.24. End of code freeze ****** 2013-09-30 m. allan noah * po/nl.po: updated translation from Martin Kho. 2013-09-30 Rolf Bensch * backend/pixma_mp810.c: fix button support for Canon CS8800F. 2013-09-24 Rolf Bensch * backend/pixma_imageclass.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: New scanner Canon imageCLASS MF4770n reported by Ralph Little. 2013-09-18 Alex Belkin * backend/xerox_mfp.conf.in doc/descriptions/xerox_mfp.desc: usb id for dell 1235cn, reported by Jean-Francois Labrousse. 2013-09-17 m. allan noah * sanei/sanei_init_debug.c: do our own upcasing to allow backend debugging in Turkish. Revert prior set_locale() patch. * po/uk.po: updated by Yuri Chornoivan * backend/microtek.c: add missing break (#314408) 2013-09-16 Gerhard Jaeger * backend/plustek-usbdevs.c: Tweaked motor settings for CanoScan N650U * backend/plustek.c: Bumped build number 2013-09-13 Rolf Bensch * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: New scanners Canon Pixma MG2400, MG2500, MG3500, MG5500, MG64000, MG6500 and MG7100. All scanners need further testing. * backend/pixma_imageclass.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: New scanners Canon imageCLASS MF4570dw and imageRUNNER 1133. All scanners need further testing. * backend/pixma.h, doc/descriptions/pixma.desc: Pixma backend version 0.17.4 2013-09-08 paul newall * kodakaio.desc: Added hero 4.1 model, marked some scanners as status basic 2013-08-27 m. allan noah * acinclude.m4, configure: enable pthread on Linux by default * sanei/sanei_init_debug.c: use C locale when building debug env vars, to allow backend debugging in Turkish. Patch from Olaf Meeuwissen. 2013-08-26 Chris Bagwell * sanei/sanei_usb.c: Treat errno of EACCES as access denied. This is what libusb is return on my Linux box. This will help give more helpful error messages to user during debugging. * testsuite/sanie/sanie_usb_test.c: Do not treat open failures because of permission errors or already opened by other processes as test failures since this is expected in some cases (such as network devices that use vendor specific class). 2013-08-16 Chris Bagwell * testsuite/sanei/Makefile.am: Add missing data files to distribution. Pass in $srcdir to sanei_config_test so it knows were data files are regardless of were run from. * testsuite/sanei/sanei_config_test.c: Use full path for config file. This allows "make distcheck" to work for this testcase since it uses a different build directory then source directory and also runs it from build directory. * testsuite/sanei/sanei_usb_test.c: add a fake name string when creating MAX_DEVICE fake devices so that strcmp() won't crash. * testsuite/tools/Makefile.am: Add missing data files to distribution. Make use of $srcdir so test case can base when build directory is not same as source directory. 2013-08-15 Chris Bagwell * sanei/sanei_thread.c: Add support for winpthreads used by mingw64 in addition to preexisting support for pthread-win32 used by mingw32. Based on work from Michael Cronenworth for Fedora's mingw64 sane-backends package. 2013-08-15 Chris Bagwell * configure.in: Make snmp detection work better when cross conmpiling; such as mingw builds on Fedora host. Most likely, net-snmp-config values are for local host so ignore them if matching library isn't also found. Required updated all autofoo's generated files to newer version by running 'autoreconf -i -f' and repatching ltmain.sh. * test-driver: Added missing test-driver that automake installs. Its expected to exist by the Makefile.in that we also checkin. 2013-08-11 Stéphane Voltz * backend/genesys_low.[ch] backend/genesys_conv.c backend/genesys.c backend/genesys_gl841.c backend/genesys_gl646.c: make use of hardware gamma tables to implement contrast and brightness correction. 2013-08-07 Stéphane Voltz * backend/genesys.[ch] backend/genesys_conv.c doc/sane-genesys.man po/fr.po: add digital brightness and contrast options. Add an option to set calibration cache file name. 2013-08-07 Stéphane Voltz * frontend/tstbackend.c: add argument to get verbose messages during checks. 2013-08-04 Stéphane Voltz * sanei/sanei_constrain_value.c testsuite/sanei/sanei_constrain_test.c: fixed the case where the rounding in sanei_constrain_value computes a value higher than the range maximum, spotted and proposed by viresh_shirol@yahoo.co.uk . Added testcase for the bug in testsuite. 2013-08-02 Stéphane Voltz * configure configure.in testsuite/Makefile.* testsuite/tools/*: add a testsuite for sane-desc, then merge hwdb support for sane-desc by Tom Gundersen . 2013-07-31 m. allan noah * backend/canon_dr*: backend version 41 - initial P-208 and P-215 support - bug fix for calibration of scanners with duplex_offset - allow duplex_offset to be controlled from config file 2013-07-30 Stéphane Voltz * backend/genesys.c: fixed typo when defining OPT_SWDEROTATE cap (#314339). 2013-07-19 Rolf Bensch * backend/pixma_mp150.c: Canon Pixma MX920 has duplex ADF and supports max. 600 dpi @ ADF. 2013-07-18 Rolf Bensch * backend/pixma_mp810.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: Canon PIXMA MG8200 is working now. * backend/pixma.[ch], backend/pixma_mp150.c, backend/pixma_mp810.c, backend/pixma_sane_options.[ch], doc/sane-pixma.man, po/de.po: - New button option --scan-resolution. Scan resolution is provided from some multi function devices. - Add some detailed button option description to the pixma man page. * backend/pixma.h, doc/descriptions/pixma.desc: Pixma backend version 0.17.3 2013-06-26 Stéphane Voltz * backend/genesys.[ch] backend/genesys_devices.c backend/genesys_gl124.[ch] backend/genesys_low.[ch]: improve scanning speed and quality for GL124 based scanner by implementing half ccd mode below 600 dpi, thanks for an hardware donation from Dany Qumsiyeh. 2013-07-03 Rolf Bensch * doc/descriptions/pixma.desc, doc/sane-pixma.man: Canon PIXMA MG5200 Series support reported by Dimitri Fellous. * .gitignore: ignore all .* files 2013-06-27 Rolf Bensch * backend/pixma.c: fix mode list for infrared scans 2013-06-27 m. allan noah * backend/fujitsu*: backend v117 - default buffer-mode to off - improved error handling in sane_start - image width must be multiple of 8 when swcrop is used before binarization (iX500) - check hopper sensor before calling object_position(load) on iX500 * .gitignore: ignore .deps directories 2013-06-26 Stéphane Voltz * backend/genesys.c backend/genesys.conf.in backend/genesys_devices.c backend/genesys_gl846.c backend/genesys_gl846.h backend/genesys_low.c backend/genesys_low.h: add experimental description and code for the GL845 based Opticbook 3800. 2013-06-22 Alex Belkin * backend/xerox_mfp.conf.in doc/descriptions/xerox_mfp.desc: multiple usb ids for Samsung models, reported by Mikhail Elhimov. 2013-06-21 Rolf Bensch * doc/descriptions/pixma.desc: Canon PIXMA MG3100 Series WLAN support reported by Torben Nielsen. 2013-06-19 Stéphane Voltz * frontend/scanimage.c: protect for group option with incorrect capabilities. * frontend/tstbackend.c: add test to check is group option are settable. Fix test_getdevices() function. 2013-06-18 Stéphane Voltz * backend/genesys.c: add missing cap to 2 option group tp fix long argument option bug in scanimage. 2013-06-15 Rolf Bensch * backend/pixma_mp810.c: fix test mode 2013-06-10 Stéphane Voltz * tools/check-usb-chip.c: add genesys GL128, GT-68xx usb chip detection. 2013-06-10 Rolf Bensch * backend/pixma_mp810.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: New scanner Canon CanoScan 9000F Mark II. * backend/pixma_mp810.c: Disable TPU negatives scan for CS8800F and CS9000F. Negatives scan has no benefit for the user and disabling avoids confusion. 2013-06-03 Stéphane Voltz * tools/sane-find-scanner.c tools/check-usb-chip.c: add genesys usb chip detection when compiled for libusb-1.0 . 2013-06-03 Stéphane Voltz * backend/genesys_gl646.c: #314293 fix 2013-05-29 Rolf Bensch * doc/sane-pixma.man: - HTML parseable name section (bug #314283) - typo in device names 2013-05-27 Stéphane Voltz * backend/genesys_gl646.c: #314261 fix 2013-05-11 Rolf Bensch * README.linux: some distros need libusb-1_0-devel. 2013-05-11 Jonathan Bravo Lopez * backend/hp3900_rts8822.c: fix compilation warnings. 2013-04-26 Rolf Bensch * backend/pixma.[ch], backend/pixma_common.[ch], backend/pixma_imageclass.c, backend/pixma_io_sanei.c, backend/pixma_mp150.c, backend/pixma_mp7[35]0.c, backend/pixma_mp810.c: update copyright. 2013-04-24 Rolf Bensch * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: New scanners Canon Pixma E510, E610 and MX720. All scanners need further testing. 2013-04-19 Rolf Bensch * backend/pixma_imageclass.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: New scanner Canon i-SENSYS MF3010 reported by Nathan Stewart. 2013-04-11 Rolf Bensch * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: New scanners Canon Pixma MG2200, MG3200, MG5400, MX390, MX450, MX520 and MX920. All scanners need further testing. 2012-04-03 Alex Belkin * doc/descriptions/xerox_mfp.desc: add Samsung SCX-4835FD networked support, reported by Dave Lewis. USB mode doesn't work. 2013-04-02 Stéphane Voltz * backend/lexmark.c backend/lexmark.h: fix device detection to handle hotplugging and unplugging of USB devices. USe sanei_usb_exit(). 2013-03-29 Stéphane Voltz * backend/gt68xx.c backend/gt68xx_low.h: fix device detection to handle hotplugging and unplugging of USB devices. * frontend/tstbackend.c: a test to loop over snae_get_devices to check if a backend handle hotplugging correctly 2013-03-27 Stéphane Voltz * backend/genesys.c backend/genesys_low.h backend/genesys_gl846.c: warning fixes, SANE option correctness fixes. 2013-03-25 Rolf Bensch * backend/pixma_mp150.c: fix button support for Canon Pixma MG6300. 2013-03-24 Stéphane Voltz * sanei/test_wire.c, testsuite/sanei/sanei_*test.c: move test program to testsuite/sanei. Add unit test programs for sanei_check_*, sanei_constrain_*, sanei_config_* and sanei_usb_* functions. 2013-03-24 Stéphane Voltz * backend/genesys.c: make use of the new sanei_usb_scan_devices function in sane_get_devices. 2013-03-24 Stéphane Voltz * sanei/sanei_usb.c include/sane/sanei_usb.h: separate device search logic from usb init by creating a sanei_usb_scan_devices. Add a sanei_usb_exit function to free allocated resources by sanei_usb_init. 2013-03-21 Rolf Bensch * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: New scanner Canon Pixma MP230 reported by Petar Dodev. 2013-03-18 Stéphane Voltz * backend/hp5590.c backend/nec.c backend/sharp.c backend/stv680: apply use after free fixes, bugs #314035, #314036, #314037 and #314038 * backend/hp4200.c: fix a potential division by O, bug #314042 All five patches for these bugs were provided by Nickolai Zeldovich 2013-03-15 Rolf Bensch * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: New scanner Canon Pixma MG6300 reported by Theo van Rijn. 2013-02-28 Stéphane Voltz * configure.in configure: restored HAVE_USBCALLS like initially committed for OS/2 support 2013-03-04 Oliver Schwartz * backend/snapscan-options.c backend/snapscan-scsi.c backend/snapscan.c backend/snapscan-sources.c backend/snapscan.h doc/descriptions/snapscan.desc: Added support for Acer Scanwit 2720S implemented by Andrew Goddbody. 2013-02-28 Stéphane Voltz * sanei/sanei_usb.c: only free devanme when reusing a device slot in device list 2013-02-28 Rolf Bensch * README.linux: ease installation procedure. 2013-02-27 Stéphane Voltz * doc/descriptions/genesys.desc: update G2410 status to basic 2013-02-27 Stéphane Voltz * backend/rts8891.c backend/rts88xx_lib.c backend/lexmark.c backend/lexmark_low.c backend/rts8891_low.c: cppcheck errors and warnings fixes 2013-02-27 Rolf Bensch * README.linux: fix installation path for libsane.la. 2013-02-22 Gerhard Jaeger * tools/sane-find-scanner.c: Fix another bug in the libusb1 part of function check_libusb_device() that prevents sanner detection for certain devices. 2013-02-20 Gerhard Jaeger * tools/sane-find-scanner.c: Setup libusb1 debugging after initialization otherwise a segfault appears in triple -v mode * sanei/sanei_usb.c: Free heap memory, acquired via strdup for devname, in sanei_usb_close() * po/de.po: Fixed typo 2013-02-18 Rolf Bensch * backend/pixma.[ch], backend/pixma_common.[ch], backend/pixma_imageclass.c, backend/pixma_mp150.c, backend/pixma_mp7[35]0.c, backend/pixma_mp810.c, doc/descriptions/pixma.desc, doc/sane-pixma.man, po/de.po: - add infrared scan support for Canon Canoscan 9000F - Pixma backend version 0.17.2 2013-02-13 Stéphane Voltz * backend/genesys*.[ch]: cppcheck errors fixes 2013-02-13 Gerhard Jaeger * backend/plustek_pp.c: Bumped build number * backend/plustek-pp*: Fixed kernel module build for recent kernels Defined internal "ULong" types to be in fact 32 bit wide, this should fix issues on 64 bit machines. * doc/plustek/Plustek-PARPORT.changes: Update 2013-02-13 Gerhard Jaeger * backend/plustek.c: Bumped build number * backend/plustek-usbscan.c backend/plustek-usbshading.c: Cleanup * backend/plustek-usb.h backend/plustek-usbdevs.c backend/plustek-usbhw.c: Added support for Q-Scan A6 portable scanner * doc/descriptions/plustek.desc: Added entry for Q-Scan A6 Q-Scan A6 Patch submitted and tested by Hiroshi Miura. Bug #312073. * doc/sane-plustek.man doc/plustek/Plustek-USB.changes: Update 2013-02-11 Rolf Bensch * backend/pixma_common.c, backend/pixma_imageclass.c, doc/descriptions/pixma.desc: fixed adf paper empty for Canon i-SENSYS MF4550d. Bug #313674. 2013-02-11 Stéphane Voltz * tools/sane-desc.c: fix minor memleaks and whitespace at end of line. 2013-02-09 Rolf Bensch * backend/pixma.c, backend/pixma_mp750.c, backend/pixma_mp810.c, backend/pixma_sane_options.c: fixed compiler warnings. * backend/pixma.c, backend/pixma_io_sanei.c, backend/pixma_mp150.c, backend/pixma_mp7[35]0.c, backend/pixma_mp810.c: fixed cppcheck warnings. 2013-02-06 Rolf Bensch * backend/pixma_mp150.c: fixed button support for Canon Pixma MG6200 by Matthias Thon. 2013-02-01 Stéphane Voltz * backend/genesys_low.c backend/genesys_gl843.c backend/genesys_gl846.c: minor usb firmware setup changes 2013-01-31 Stéphane Voltz * backend/genesys.[ch] backend/genesys_devices.c backend/genesys_gl124.c backend/genesys_low.h: add support for LIDE 210 fifth button. 2013-01-29 Stéphane Voltz * backend/genesys*.[ch] : ASIC init refactor. Groundwork for GL846 support. 2013-01-24 Rolf Bensch * backend/pixma_mp150.c: fixed 1200 dpi scan for Canon Pixma MG2100. * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: Canon Pixma MG2100 support reported by David Durgee. * backend/pixma_imageclass.c: fixed usb checksums for MF4100. 2013-01-19 Paul Newall * backends/kodakaio.c: Hero 4.1 added. 2013-01-18 Rolf Bensch * backend/pixma.c, backend/pixma_mp810.c: fixed button support for Canon Canoscan CS9000F. * doc/sane-pixma-man: additional description for button support. 2013-01-16 Paul Newall * doc/descriptions/kodakaio.desc: Desc file updated. 2013-01-14 Rolf Bensch * backend/pixma_imageclass.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: - new scanner Canon i-SENSYS MF4550d. Bug #313674. - new scanner Canon i-SENSYS MF5880dn. Bug #313613. - new scanner Canon i-SENSYS MF6680dn. Bug #313922. 2013-01-11 Rolf Bensch * backend/pixma_mp810.c: - formatted white space - Canon Pixma MP970 & MP990: fix for broken post_process_image_data. * doc/descriptions/pixma.desc, doc/sane-pixma.man: Canon Pixma MG5100 support reported by Patrick Viola. 2013-01-10 Louis Lagendijk * Changed the pixma_bjnp.c implementation to use its own debug environment variable (SANE_DEBUG_BJNP) so debugging for the bjnp protocol and the backend proper can be controlled independently. Updated manpage for sane-pixma to document the above changes as well as the changed scannner buttjn options and the fact the bjnp protocol has added support for IPv6 2013-01-10 Louis Lagendijk * Added optional support in sanedi.c for systemd socket activation. This will allow for using backend debugging using SANE_DEBUG_xxx to be used for saned controlled scanning when using socket activation. When only the socket is received we use the existing code path. 2013-01-08 Rolf Bensch * backend/pixma_mp810.c: fix for broken tpu scan. 2013-01-03 Paul Newall * backend/kodakaio.c kodakaio.h: Detecting end of paper when using adf is now an option. Alternatively the image is padded with the background colour to make up the specified image size. 2012-12-31 Paul Newall * backend/kodakaio.c kodakaio.h kodakaio.conf kodakaio.conf.in: better fix for bug where adf could not scan more than 1 page with net connection end of paper now detected with adf, if it is less than the selected size ESP 2170 and Hero 9.1 can now do 1200dpi 2012-12-31 Rolf Bensch * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: New scanner Canon Pixma MG4200 reported by nuxer. 2012-12-28 Alex Belkin * backend/xerox_mfp.conf.in doc/descriptions/xerox_mfp.desc: usb id for Samsung CLX-3300 Series, reported by Jarkko A-L. 2012-12-23 Paul Newall * backend/kodakaio.c: fix bug where adf could not scan more than 1 page with net connection 2012-12-13 Rolf Bensch * README.linux: Installation description moved to top and some editing. 2012-12-09 Paul Newall * backend/kodakaio.c: add adf to ESP2170. Reduce the default adf y length to 11.3 inch 2012-12-09 Stéphane Voltz * backend/genesys*.c backend/genesys_low.h: reworked gamma table handling. Compute gamma table outside send_gamma_table so default gamma table use device specific gamma values defined in device description. 2012-12-07 Rolf Bensch * backend/pixma.h: Pixma backend version 0.17.1 * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: - Canon Pixma MG3100 support reported by Madis Lõhmus. - Canon Pixma MG6200 support reported by Matthias Thon. - Canon Pixma MX890 support reported by Henry Ptasinski. * backend/pixma_imageclass.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: - Canon imageCLASS MF4430 support reported by Michael Mik. This device is a MF4410 with ADF. 2012-12-05 Stéphane Voltz * backend/genesys.c backend/genesys_gl124.c: add binary data logging for gl124 based scanners. Issue park command as early as possible to speed up batch scans 2012-11-28 Paul Newall * backend/kodakaio.c backend/kodakaio.h backend/kodakaio.conf.in backend/Makefile.am backend/Makefile.in docs/descriptions/kodakaio.desc: Use avahi instead of cups for network autodetection 2012-11-24 Stéphane Voltz * backend/rts9918.c backend/rts8891_low.c backend/rts8891_low.h: fix batch scan by adding asynchronous head parking. Fix 600 and 1200 dpi scan mode for sensor type 1 2012-11-11 Stéphane Voltz * backend/genesys.c backend/genesys_gl124.c: improve calibration for gl124 based scanners. Fix head parking issue at 1200 dpi. 2012-11-02 Stéphane Voltz * doc/descriptions/unsupported.desc: add USB ID 0x07b3, 0x0802 2012-11-02 Stéphane Voltz * configure configure.in doc/Makefile.am doc/Makefile.in: add optional doxygen documentation generation for genesys backend. 2012-10-31 Louis Lagendijk * backend/scripts/pixma_gen_options.py Updated formatting of produced files to match pixma.c formatting without need for manual editing * pixma.[ch] pixma_mp810.c pixma_mp150.c Rewrite of button option handling. Button options can now be read without triggering through the button-update option. Buton-update options is left for ease of use with xsane Options are cached so that only when an option is read again all options are re-read. 2012-10-29 Alex Belkin * backend/xerox_mfp.conf.in doc/descriptions/xerox_mfp.desc: usb id for Samsung SCX-4833FD, reported by Frank Pennycook; description for SCX-3405W working in networking mode, reported by Patrick Mackinlay. 2012-10-24 Stéphane Voltz * backend/genesys_*.[ch]: Lide 35 improvements, doxygen fixes, experimental 100 dpi support for Canoscan 4400F and 8400F 2012-10-24 Mattias Ellert * configure.in, configure: Unset VERSION during the SCSI header check (Check fails on MacOS X if VERSION is defined) 2012-10-22 Mattias Ellert * po/sv.po: Updated Swedish translation * backend/pixma_bjnp.c: Fix arguments to IPv6 macros 2012-10-18 m. allan noah * backend/canon*: IX-4015 support by Ondrej Zary 2012-10-17 Alex Belkin * backend/xerox_mfp.conf.in doc/descriptions/xerox_mfp.desc: usb id for Samsung SCX-4100, reported by Antonello Lobianco. 2012-09-18 Alex Belkin * backend/xerox_mfp.conf.in doc/descriptions/xerox_mfp.desc: usb id for Samsung SCX-4729FD, reported by wom balton. 2012-09-12 Rolf Bensch * doc/descriptions/pixma.desc, doc/sane-pixma.man: Canon imageCLASS MF5730 support reported by Ondrej Zary. 2012-09-10 Rolf Bensch * doc/descriptions/unsupported.desc: Removed Canon Multipass 390 from unsupported device list. 2012-09-08 Louis Lagendijk * backend/pixma_bjnp.c, backend/pixma_bjnp_private.h bulk reading rewritten and solved the case where the scanner returns a 0 length payload. Use MDL string instead of DES as it is mre fool proof when matching the model string from the backend 2012-09-06 Stéphane Voltz * backend/genesys_gl646.c backend/genesys.c: enable hardware lineart for Strobe XP200 2012-09-05 Rolf Bensch * backend/pixma_imageclass.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: New scanner reported by Eric Schwartz. Bug #313761. * backend/pixma_mp150.c: 14" (ADF) / A4 (Flatbed) exception for Pixma MX420 reported by D.Möller. Same exception also added for Pixma MX410. 2012-09-04 Rolf Bensch * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: Pixma MG5300 support reported by Gernot Hassenpflug. 2012-09-03 Rolf Bensch * README.linux: Installation description updated for sane-backends 1.0.24git. * doc/descriptions/pixma.desc, doc/sane-pixma.man: Canon imageRunner 1020/1024/1025 support reported by Yuri Kirin. Bug #313203. * backend/pixma_mp150.c: Use xdpi to check for 14" / A4 scan. * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: Pixma MX370 support reported by Daniel Lehmann. Bug #313750. * backend/pixma_mp150.c: PIXMA_CAP_EXPERIMENT removed from new scanners. 2012-08-27 Louis Lagendijk * pixma_bjnp.c pixma_bjnp_private.h Added working scan-button support Added IPv6 support Some clean up and refactoring 2012-08-27 Rolf Bensch * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: Pixma MG6100 support reported by Yann Bonnamy. Bug #313471. 2012-08-26 m. allan noah * ChangeLog*, configure*, Makefile.in, doc/desc/{kodakaio,kvs40xx}.desc: Various updates related to starting 1.0.24git Older entries can be found in ChangeLog-1.0.23. backends-1.3.0/ChangeLogs/ChangeLog-1.0.25000066400000000000000000000545071456256263500176230ustar00rootroot00000000000000****** Release of sane-backends 1.0.25. End of code freeze ****** 2015-09-30 Rolf Bensch * doc/descriptions/pixma.desc, doc/sane-pixma.man: Canon PIXMA MG5600 Series is working, reported by Gavin Falconer. 2015-09-26 Rolf Bensch * backend/pixma.h, doc/descriptions/pixma.desc: Pixma backend version 0.17.23 2015-06-22 Louis Lagendijk * doc/saned.man: updated to clarify systemd integration and configuration for thecase where saned is compiled with systemd glue. 2015-09-18 Rolf Bensch * backend/pixma_imageclass.c: for all adf scanners restrict maximum page height at flatbed scans 2015-09-16 Stéphane Voltz * backend/niash.c: merged pu/protect-niash-option-access-315132 and ifdef'ed unused code 2015-09-16 Stéphane Voltz * backend/pieusb.c: include sane/config.h first 2015-09-14 Stéphane Voltz * backend/bh.c: replace mktemp by mkstemp #300134 2015-09-13 Paul Newall

* backend/kodakaio.c: redundant variable bitposn and redundant function kodakaio_rxflush commented out. 2015-09-09 Stéphane Voltz * doc/sane-genesys.man, doc/descriptions/genesys.desc: No LiDE 120 support. 2015-09-08 m. allan noah * backend/artec_eplus48u.c: fix HOME env bug #315071 (from Jörg Frings-Fürst) 2015-09-03 Rolf Bensch * backend/pixma_mp810.c: Canon Pixma MP990 needs specific reordering pixels for 4800 dpi, patch from Guillaume Courtois. * backend/pixma.h, doc/descriptions/pixma.desc: Pixma backend version 0.17.22 2015-08-20 Rolf Bensch * README.linux: add mandatory and optional development environment to install description * frontend/scanimage.c: remove atexit() function. In some cases atexit() doesn't work with libusb-compat. 2015-08-10 Rolf Bensch * backend/pixma_io_sanei.c: EOF is a bjnp timeout error. * backend/pixma.h, doc/descriptions/pixma.desc: Pixma backend version 0.17.21 2015-08-10 Stéphane Voltz * backend/abaton.c, backend/agfafocus.c, backend/apple.c, backend/bh.c, backend/coolscan.c, backend/dmc.c, backend/ricoh.c, backend/s9036.c, backend/sp15c.[ch], backend/tamarack.c: fix for memleak in sane_get_devices (bugs #300132 #300131 #300130 #300129 #300127 #300126 #300124 #300122 #300121 #300120) 2015-08-10 Klaus Kämpf * backend/pieusb.c, doc/descriptions/pieusb.desc, doc/sane-pieusb.man, backend/pieusb_buffer.c, backend/pieusb_scancmd.c, backend/pieusb_specific.c, backend/pieusb_usb.c: New scanners PIE PowerSlide 3600,3650,4000,5000 / Reflecta DigitDia 3600, 4000, 5000, 6000; ProScan 7200; CrystalScan 7200. These scanners need further testing. 2015-08-08 Stéphane Voltz * backend/test.c: fix memleaks (#313553) * configure.in, configure: don not hardcode -I/usr/local/include (#315060) open/close tests. 2015-08-07 Rolf Bensch * README.linux: update development packages list. OpenSuse needs libusb-compat-devel. Sane doesn't support libusb 1.0. * backend/pixma_io_sanei.c: EOF isn't an usb timeout error; this fixes usb communication problems, e.g. with OpenSuse. * backend/pixma.h, doc/descriptions/pixma.desc: Pixma backend version 0.17.20 2015-08-06 Stéphane Voltz * backend/genesys_devices.c, backend/genesys_gl841.c, backend/genesys_low.c: fix GPIO setup for LiDE 33/40/50, add more usleep() for GL847 scanning issues. Some GL841 LEDADD improvements. 2015-07-31 Stéphane Voltz * frontend/tstbackend.c: add an option to do tests scans during open/close tests. * backend/genesys.c, backend/genesys_low.c: fixes for - #315104 - #315105 And a timing issue on USB3 hardware debugged and tracked by John S. Weber 2015-07-29 Rolf Bensch * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: New scanner Canon PIXMA MG3600 Series. This scanner needs further testing. * backend/pixma_mp150.c: Canon PIXMA MG7500 Series doesn't need special image format post processing. * doc/descriptions/pixma.desc, doc/sane-pixma.man: Canon PIXMA MG7500 Series is working, reported by Jonathan Anderson. * backend/pixma.h, doc/descriptions/pixma.desc: Pixma backend version 0.17.19 2015-07-24 Rolf Bensch * doc/descriptions/pixma.desc, doc/sane-pixma.man: Canon PIXMA MX470 Series is working, bug #314894. 2015-06-27 Louis Lagendijk * backend/pixma_bjnp.c: added missing return in case of no error 2015-06-27 Louis Lagendijk * backend/pixma_bjnp.c Added backend version in bjnp debug output 2015-06-11 Stéphane Voltz * backend/genesys.c backend/genesys_gl124.h: LiDE 120 support improvement. 2015-05-18 Rolf Bensch * backend/pixma_imageclass.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: New scanner Canon i-SENSYS MF8300 Series, reported by Florian Nierhaus. * backend/pixma.h, doc/descriptions/pixma.desc: Pixma backend version 0.17.18 2015-04-12 Stéphane Voltz * doc/sane-genesys.man backend/genesys.[ch] backend/genesys_low.c: fix calibration cleared when calibration file loaded, add expiration time option for cache. 2015-04-07 Rolf Bensch * backend/pixma_mp810.c: Canon Pixma MP990 TPU scan patch from Guillaume Courtois. * doc/descriptions/pixma.desc: remove unmaintained websites * backend/pixma.h, doc/descriptions/pixma.desc: Pixma backend version 0.17.17 2015-04-03 Rolf Bensch * backend/pixma_imageclass.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: New Canon scanners i-SENSYS MF210 Series, MF220 Series, MF5900 Series, MF6100 Series and imageCLASS MF810/820. All scanners need further testing. * backend/pixma.h, doc/descriptions/pixma.desc: Pixma backend version 0.17.16 * backend/pixma.[ch], backend/pixma_common.[ch], backend/pixma_imageclass.c, backend/pixma_io_sanei.c, backend/pixma_mp150.c, backend/pixma_mp7[35]0.c, backend/pixma_mp810.c: update copyright. 2015-03-17 Stéphane Voltz * backend/genesys.c, backend/genesys.conf.in, backend/genesys_devices.c, backend/genesys_gl124.c, backend/genesys_gl124.h, backend/genesys_low.h, doc/descriptions/genesys.desc, doc/sane-genesys.man: add support for Canon LiDE 120 2015-03-14 René Rebe * backend/avision.h backend/avision.c: fixed 32-bit build regression due new feature_type and reverted 4d38523bdacd18186857ee8a93daaa0f8721c89b 2015-03-13 René Rebe * doc/sane-avision.man, backend/avision.h, backend/avision.c: fixed newer avision scanner duplex scanning, and removed unnecessary feature_type2 clutter by using a uint64_t to share all bits 2015-03-12 René Rebe * doc/descriptions/avision.desc, backend/avision.c: fixed multiple regressions (range initilaizer, clobbering paper-length window bits, ...) introduced over the past years, added support for newer ASIC gamma table size, added AV220D2+ ID 2015-03-04 Stéphane Voltz * doc/descriptions/genesys.desc: mark LiDE 120 as unsupported. 2015-03-02 Stéphane Voltz * backend/genesys_low.c, backend/genesys_gl124.[ch]: improve parking reliability for LiDE 210 2015-03-01 Stéphane Voltz * backend/genesys_devices.c, backend/genesys_gl124.c: final tuning for Canon LiDE 220 2015-02-26 Stéphane Voltz * backend/genesys.c, backend/genesys.conf.in, backend/genesys_devices.c, backend/genesys_gl124.c, backend/genesys_gl124.h, backend/genesys_low.h, doc/descriptions/genesys.desc, doc/sane-genesys.man: add support for Canon LiDE 220 2015-02-21 Rolf Bensch * backend/pixma_mp150.c: Canon PIXMA MG6400 Series doesn't need special image format post processing. * doc/descriptions/pixma.desc, doc/sane-pixma.man: Canon PIXMA MG6400 Series is working, reported by Christian Spielberger. * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: New scanners Canon PIXMA MX490 Series and E480 Series. All scanners need further testing. * backend/pixma.h, doc/descriptions/pixma.desc: Pixma backend version 0.17.15 2015-02-09 Rolf Bensch * doc/descriptions/pixma.desc: Canon PIXMA MX920 Series supports Ethernet and WiFi interfaces. 2015-02-06 Rolf Bensch * doc/descriptions/pixma.desc, doc/sane-pixma.man: Canon PIXMA MG2500 Series is working, reported by Brenda Ruch. 2015-01-20 Ilia Sotnikov * backend/hp5590.c: Invert pixels in case of TMA Negatives source has been selected. Thanks for Will Kranz who reported this. 2015-01-17 Rolf Bensch * doc/descriptions/pixma.desc, doc/sane-pixma.man: Canon PIXMA MG2200 Series is working (bug #314944). 2014-12-31 Paul Newall

* backend/kodakaio.c: connection to device moved from sane_open to sane_start disconnection move from sane_close to sane_cancel in order to fix bug where scanner disconnected after 30s idle. 2014-12-19 Paul Newall

* backend/kodakaio.c: param.depth corrected for lineart mode, was 8 now 1 sequence of modes changed to make the color mode the default. 2014-12-16 m. allan noah * sanei/sanei_usb.c: add calls to sanei_usb_set_altinterface in sanei_usb_close and sanei_usb_clear_halt- hopefully work around USB3/xhci problems in Linux. 2014-11-18 Rolf Bensch * backend/pixma_imageclass.c: - Canon i-SENSYS MF8200C and MF8500C use generation 2 protocols. - Also use has_paper() for generation 2 scanners. * backend/pixma_imageclass.c, doc/descriptions/pixma.desc: Canon i-SENSYS MF8200C: ADF supports max. 300DPI. * backend/pixma.h, doc/descriptions/pixma.desc: Pixma backend version 0.17.14 2014-11-15 Rolf Bensch * backend/avision.c: - Remove firmware checking for HP5370c scanners. Launchpad bug #1080787, reported by Night Train. - Build version 297. 2014-10-29 Rolf Bensch * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: New Canon scanners MAXIFY MB5000, MB5300, MB2000 and MB2300; PIXMA MG7500, MG6600, MG5600, MG2900 and E460. All scanners need further testing. * backend/pixma.h, doc/descriptions/pixma.desc: Pixma backend version 0.17.13 * doc/descriptions/pixma.desc, doc/sane-pixma.man: Canon PIXMA MG2900 Series is working, reported by Yannick Dirou. 2014-10-27 Rolf Bensch * backend/pixma_mp150.c: Canon PIXMA MG3500 Series doesn't need special image post processing. * doc/descriptions/pixma.desc, doc/sane-pixma.man: Canon PIXMA MG3500 Series is working. * backend/pixma.h, doc/descriptions/pixma.desc: Pixma backend version 0.17.12 2014-10-20 Rolf Bensch * backend/pixma_imageclass.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: New Scanner Canon imageCLASS D530. * backend/pixma_mp150.c: - increase calibration timeout - calibration can finish with 0x01 or 0x02 2014-09-18 Rolf Bensch * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: Canon Pixma MG5500 Series is working, bug #314819. 2014-09-16 Rolf Bensch * doc/descriptions/pixma.desc, doc/sane-pixma.man: Canon Pixma MG2400 Series is working, reported by Marcus Wellnitz. * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: Canon Pixma MX530 Series is working, reported by Stephen Weston. * backend/pixma.h, doc/descriptions/pixma.desc: Pixma backend version 0.17.11 2014-09-13 Paul Newall

* backend/kodakaio.c, backend/kodakaio.h: * Added lineart mode with threshold option. 2014-09-07 Paul Newall

* backend/kodakaio.c: * Fixed error where the colour compensation curves did not have enough elements and this caused speckles in areas of very low or high colour. 2014-08-23 Louis Lagendijk * backend/pixma_bjnp.c: * Keep TCP-connection closed until we really need it. This avoids the scanner closing the TCP-connection when idle * Add an explicit error message when we detect that the scanner closed the TCP-connection 2014-08-16 Rolf Bensch * backend/pixma_imageclass.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: New scanners Canon i-SENSYS MF8200C Series and MF8500C Series. MF8500C Series need further testing. * doc/sane-pixma.man: add up to now undocumented verbose debug-level * backend/pixma.h, doc/descriptions/pixma.desc: Pixma backend version 0.17.10 * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: Canon PIXMA MX720 Series scanner is working (bug #314787). 2014-07-25 Rolf Bensch * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: New scanners Canon Pixma E400, E560, MX470 and MX530. All scanners need further testing. 2014-07-14 m. allan noah * frontend/scanimage.c: batch-print option and exit value changes from Jeff Breidenbach jeff@jab.org 2014-07-01 Stéphane Voltz * testsuite/sanei/sanei_config_test.c: fix #314694 by exporting a specific SANE_CONFIG_DIR env dir for tests 2014-06-29 Stéphane Voltz * backend/genesys_gl646.c: fix regression in coarse gain calibration 2014-06-05 Tom Callaway * lib/snprintf.c: update to newer version from LPRng. The old version is licensed under the Artistic License 1.0 which isn't compatible with the GPL and arguably non-free. 2014-06-02 Stéphane Voltz * backend/genesys_*.[ch]: add internal ini_scan_regs_api. Add proper target speed for LiDE 110/210 grey mode motor tables. 2014-05-29 Paul Newall * backend/kodakaio.c: change to comment on ./configure parameters 2014-05-26 Rolf Bensch * backend/pixma_mp150.c: fix ADF paper empty @ start scanning for Canon Pixma MP150 based ADF scanners * backend/pixma_common.c: remove unused variable * backend/pixma.h, doc/descriptions/pixma.desc: Pixma backend version 0.17.9 2014-05-08 Rolf Bensch * backend/pixma_mp150.c: fix MG7100 image data post processing @ high resolutions > 600dpi. * doc/descriptions/pixma.desc, doc/sane-pixma.man: Canon Pixma MG7100 Series scanner is working, reported by Gert Cauwenberg. * backend/pixma.h, doc/descriptions/pixma.desc: Pixma backend version 0.17.8 2014-05-13 Stéphane Voltz * backend/genesys_gl646.[ch] backend/genesys.c backend/genesys_low.c: fix bug #314663. Set gamma for gl646 devices only at scan time. Reset endpoint each time before leaving. 2014-05-08 Rolf Bensch * backend/pixma.h, doc/descriptions/pixma.desc: Pixma backend version 0.17.7 * backend/pixma_mp150.c: - all Canon Pixma MP150 based ADF scanners support 14" legal paper size - fix ADF paper empty bug for Canon Pixma MP150 based ADF scanners * doc/descriptions/pixma.desc: remove comments for Canon Pixma MP150 based ADF scanners: adf empty buggy * doc/sane-pixma.man: Canon Pixma MX360 shouldn't hang anymore (@ ADF paper empty) 2014-04-07 Rolf Bensch * doc/descriptions/pixma.desc, doc/sane-pixma.man: Canon Pixma MX410 is working, reported from Javier Gorostiza. 2014-03-21 Rolf Bensch * doc/descriptions/pixma.desc, doc/sane-pixma.man: Canon Pixma MG3200 is working, reported in ubuntuforums.org/showthread.php?t=2072162. * doc/descriptions/pixma.desc: rename WLAN to WiFi * backend/pixma_mp150.c: - Canon Pixma MX520 Series supports 14" legal paper size in ADF mode. - MX520 has button support. * backend/pixma_common.c: Clamp minimum image size to 16 px x 16 px. Some new scanners need minimum 16 px height. * doc/descriptions/pixma.desc, doc/sane-pixma.man: Canon Pixma MX520 Series is working, reported by Trygve Flathen. 2014-02-28 Rolf Bensch * backend/pixma_imageclass.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: Rename Canon i-SENSYS MF4550d to Canon i-SENSYS MF4500 Series. This fixes ethernet problems. Patch from chrysn. 2014-02-24 Stéphane Voltz * doc/sane-genesys.man backend/genesys.[ch]: avoid calibration file name collision in case of several identical scanners 2014-02-10 Louis Lagendijk * backend/pixma_bjnp.c backend/pixma_bjnp.h backend/pixma_bjnp_private.h: - Added support for Canon laser multi-functionals using the MFNP protocol over port 8610 - Improved debug-logging to be more readable 2014-02-08 Rolf Bensch * backend/pixma_mp150.c: - Canon Pixma MX510 Series supports 14" legal paper size in ADF mode. * doc/descriptions/pixma.desc, doc/sane-pixma.man: - Canon Pixma MX510 Series is working, reported by sebastien_gd. 2014-01-29 m. allan noah * backend/umax-uc630.c: updates from Ondrej Zary 2014-01-21 Stéphane Voltz * doc/descriptions/unsupported.desc doc/descriptions/genesys.desc: update status of the HP Scanjet 4850C 2014-01-17 Rolf Bensch * doc/descriptions/pixma.desc: Update specification file for Canon i-SENSYS MF4800 Series: - Ethernet is buggy. - ADF is working. 2014-01-09 Gerhard Jaeger * tools/sane-find-scanner.c tools/check-usb-chip.c: Add LM983x usb chip detection when compiled for libusb-1.0. 2014-01-02 Rolf Bensch * doc/descriptions/pixma.desc: ADF is working for Canon i-SENSYS MF4800 Series, reported by Yvan L. Gélinas. 2014-01-02 Rolf Bensch * backend/pixma.[ch], backend/pixma_common.[ch], backend/pixma_imageclass.c, backend/pixma_io_sanei.c, backend/pixma_mp150.c, backend/pixma_mp7[35]0.c, backend/pixma_mp810.c: update copyright. * backend/pixma_imageclass.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: bug #314411, Canon imageCLASS MF4570dw is working. 2013-12-27 Rolf Bensch * backend/pixma_imageclass.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: - Canon i-SENSYS MF4800 Series has ADF and supports 14" legal paper size in ADF mode. - Canon i-SENSYS MF4800 Series is working, reported by Yvan L. Gélinas. 2013-12-23 Rolf Bensch * backend/pixma_common.c: fix usb cmd transaction timeout 2013-12-23 Stéphane Voltz * backend/genesys_devices.c backend/genesys.c backend/genesys_low.h backend/genesys_gl841.[ch]: color calibration improvement for LiDE80, working LEDADD for gl841 CIS scanners 2013-12-16 Rolf Bensch * backend/pixma_imageclass.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: - rename Canon imageCLASS MF4770n to i-SENSYS MF4700 Series - rename Canon i-SENSYS MF4890dw to MF4800 Series * backend/pixma_mp150.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: - Canon MX920 reported as working; ADF is still buggy - MX920 has 14" ADF paper length - MX920 has button support - fix vertical stripe shifting for MX920 - tidy function calc_shifting() * backend/pixma.h, doc/descriptions/pixma.desc: Pixma backend version 0.17.6 2013-12-16 Stéphane Voltz * doc/descriptions/genesys.desc: update LiDE 80 * backend/genesys_devices.c backend/genesys.c backend/genesys_low.h backend/genesys_gl841.[ch]: basic support for LiDE 80 2013-12-10 m. allan noah * backend/fujitsu*: fujitsu backend v118 - support fi-7160, fi-7260, fi-7180 and fi-7280 - remove unused var from do_scsi_cmd() - added more request_sense options - add adv_paper_protect option - enable paper protection by default - increase max_x_fb for fi-6240 and fi-6230 2013-12-09 m. allan noah * backend/canon_dr*: canon_dr backend v42 - initial DR-G1100 support - add support for paper sensors (P-215 & P-208) - add initial support for card reader (P-215) - removed unused var from do_scsi_cmd() 2013-11-21 Stéphane Voltz * testsuite/tools/Makefile.*: handle sane git version change in tests. 2013-10-19 Stéphane Voltz * doc/descriptions/genesys.desc doc/Descriptions/unsupported.desc: update LiDE 80 and HP2400 information * backend/genesys_devices.c backend/genesys.conf.in backend/genesys.c backend/genesys_low.h backend/genesys_gl841.[ch]: add first element of LiDE 80 support. 2013-10-15 Paul Newall * configure.in: detection of cups was commented out. Avahi is now used for autodetection in kodakaio so the detection of cups is no longer required. 2013-10-14 Paul Newall * backend/kodakaio.c: fix bug #314301, Segfault when unexpected data returned by avahi auto discovery. 2013-10-11 Rolf Bensch * backend/pixma_imageclass.c: Use generation variable instead of single defines of scanner pid's. Now new scanners should work without extensive code editing. * backend/pixma_imageclass.c, doc/descriptions/pixma.desc, doc/sane-pixma.man: New scanner Canon i-SENSYS MF4890dw. This scanner needs further testing. * backend/pixma.h, doc/descriptions/pixma.desc: Pixma backend version 0.17.5 2013-10-02 Rolf Bensch * README.linux: Update Installation description for 1.0.25git. 2013-09-30 m. allan noah * ChangeLog*, configure*, doc/releases.txt: Various updates related to starting 1.0.25git Older entries can be found in ChangeLog-1.0.24. backends-1.3.0/ChangeLogs/ChangeLog-1.0.27000066400000000000000000004007571456256263500176270ustar00rootroot00000000000000commit 82cd8f245cf26879b3f94348d4069dcec8d49f21 Author: m. allan noah Date: 2017-05-22 11:16:40 -0400 SANE-backends release 1.0.27 Moved old changelogs to new directory, and excluded them from release tarball. Improved release docs. Updated config.* files Bumped version numbers. Wrote release notes. commit 16f3060061a17cbc54f3415a7e2abc1c35f3fcd2 Author: Rolf Bensch Date: 2017-05-20 14:13:19 +0200 pixma: i-SENSYS MF240 Series is working commit ad5fd9edc83f2c6b6e27e9a366cd8ff5e00376b8 Author: m. allan noah Date: 2017-05-19 20:24:14 -0400 Updated NL translation from Martin Kho commit baf786742f0382bed3f1b58d9977229826e3df5f Author: Olaf Meeuwissen Date: 2017-05-15 21:37:34 +0900 po/*.po: sync with source code commit 05553c679fd02648fc5b6dd3a4abf75fe67e1d27 Author: m. allan noah Date: 2017-05-14 21:34:36 -0400 updated uk.po translation Update from Yuri Chornoivan commit 479ccaedddcb0a491b351e20cac22595196df02a Author: Olaf Meeuwissen Date: 2017-05-14 19:48:41 +0900 autotools: Sync derived files This was done by running autoreconf --force --install on Debian GNU/Linux 8.8 and reverting the removal of SANE specific tweaks to ltmain.sh. commit 02037adb134a2b533bb3201833444aed7bedc8f6 Author: Olaf Meeuwissen Date: 2017-05-14 19:47:31 +0900 Fix unused parameter compiler warnings in check tools commit 85b91bcd248d2a40300b9fda76bdb88853fbcd0b Author: Jörg Frings-Fürst Date: 2017-05-13 18:11:40 +0900 doc: fix typos, formatting and stylistic issues commit 1808a63b0091f0e5562586278b13d7ae63184f43 Author: Jörg Frings-Fürst Date: 2017-05-13 18:07:26 +0900 Replace the startup gamma with the same from the standalone version Fixes https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=629470 commit 5c0812c51b6d48516acb9dd9efcfbe2b34f04a79 Author: Jörg Frings-Fürst Date: 2017-05-13 18:05:43 +0900 genesys.conf: fix comment typo commit e21bb28e5051d8fdd64df3c4ea2625b9f45538c8 Author: Jörg Frings-Fürst Date: 2017-05-13 18:03:42 +0900 Source code string typo fixes commit 09cf70563a1129116ab6888e944bfcb44fec8dd0 Author: Olaf Meeuwissen Date: 2017-05-13 16:33:23 +0900 configure.ac: Fix condition commit 9230e0c251f2351ce1c7b8d7fca08dc39c320925 Author: Olaf Meeuwissen Date: 2017-05-13 13:12:06 +0900 saned: Link against Avahi library commit da5ce7216cf63a454411fa781bc2ea30fa881974 Author: Olaf Meeuwissen Date: 2017-05-13 13:09:25 +0900 Add a header to dll.conf Document dll.conf in a header, point to the dll.d directory for external backends and document the net backend w/ pointers to the manpages. Based on a patch by Julien BLACHE commit 77dcf26860b239dafb3094c863691aaef304805a Author: Christopher James Halse Rogers Date: 2017-05-13 13:07:01 +0900 Fix potential assert in avahi backend avahi_browser contains a reference to an AvahiClient, which may be avahi_client. If it does, then freeing avahi_browser references avahi_client, resulting in an assert() in Avahi's linked list macro. Fix this by freeing avahi_browser before avahi_client. See https://bugs.launchpad.net/bugs/831867 commit e6711c377f266d33a3d8bc28d7a508e3c4c29c79 Author: Rolf Bensch Date: 2017-05-10 19:10:26 +0200 remove unused entry from ChangeLog commit 0cf531e44528a541d76c3571bca72f395b7892ae Author: Klaus Kämpf Date: 2017-05-09 10:52:12 +0200 Update pieusb.conf and pieusb.desc commit 135b4a3909e0fe0d14062dc009bfba8085ebf167 Author: Rolf Bensch Date: 2017-05-10 11:51:35 +0200 pixma.desc: fix version of backend commit 5b10b0b635157cdb9766197a5f2ea7f1d24a402a Author: René Rebe Date: 2017-05-05 07:05:30 +0200 epson2: fixed network transport for new scanners increased command buffer due memory overrun, and always read receive status, even for zero length on network scanenrs, should fix #315552 commit 6ffeb9097386268e425ae2390ed2835853dbd439 Author: Olaf Meeuwissen Date: 2017-05-06 20:40:25 +0900 Fix test for API specification generation If none of the formats can be generated but API specs are requested, bomb. The original test aimed to do so but was testing against "no", a value that was never assigned. commit f3f8afb08c2098d9659757a8e7b8c212a09ddf79 Author: Olaf Meeuwissen Date: 2017-05-06 12:24:13 +0900 kvs20xx.c: don't assume sane_get_devices() has been called Nor that that function is called with a non-NULL argument. This aligns the implementation with that of kvs40xx.c. Fixes 315625. commit 8995b1fc81e4378d2b5b0b6b0d354735403210ed Author: Martin Husemann Date: 2016-03-15 18:07:00 +0900 sanei_usb: fix number of bytes read for 64bit big-endian systems Fixes 315337. commit b1a2c6105443c5279684f0c6827c268594aa234d Author: Louis Lagendijk Date: 2017-05-05 19:46:28 +0200 pixma: backend version 0.17.37 commit 09c467deab5ca4301ab7da37241c3fc072b94399 Author: Klaus Kaempf Date: 2017-05-03 17:52:15 +0200 pieusb: Support ProScan 10T and Reflecta CrystalScan 3600 Neither of these scanner have an automated slide transport and fail on respective SCSI commands. - Add flags parameter to control if automatic slide transport is available - Reflect flags in pieusb.conf.in - rename SLIDE_LAMP_ON to SLIDE_INIT it fails on scanners without automatic slide transport, so it has nothing to do with the lamp. - run SLIDE_INIT only FLAG_SLIDE_TRANSPORT is set - pieusb.conf.in: Add Reflecta CrystalScan 3600 commit 801558f959a03b5042d6df288cb1fd2c87035798 Author: Aaron Muir Hamilton Date: 2017-05-01 20:15:23 +0000 Write density (resolution) JFIF header information with JPEG files. commit b219bc2c73113fd9d8f7c5795bd662f03a8610ea Merge: f624a9f72527 92e59791d2e4 Author: Olaf Meeuwissen Date: 2017-05-04 18:26:55 +0900 Merge branch 'icc-profiles' commit 92e59791d2e478484409f5f2e634196863d7dafb Author: Olaf Meeuwissen Date: 2017-04-30 20:05:12 +0900 Fix [-Werror=implicit-function-declaration] compiler error The fileno() is only available if its feature test macro passes. The various symbols are defined appropriately in ../include/sane/config.h during configuration of the build. commit adebd37b5cb3ec0cab3025300db5cdd03c2173cc Author: Olaf Meeuwissen Date: 2017-04-30 20:00:28 +0900 Fix [-Wpointer-arith] compiler warnings commit c3903cc476909884ace9fedbea5f69b3a4085b70 Author: Aaron Muir Hamilton Date: 2017-04-17 12:07:40 +0000 Avoid attaching grayscale ICC profiles to rgb images, or vice-versa. If the ICC profile added does not match the colour format of the image, libpng will abort. This can leave the scanner motors in an incorrect state and possibly cause permanent damage. commit a907b61bd58f00fd6b1d8468d9d28d6d39232c79 Author: Aaron Muir Hamilton Date: 2017-04-17 12:07:39 +0000 Add ICC profile embedding for PNG output. commit 2c653a926af83d7bb910307f441055a6d7603f0b Author: Aaron Muir Hamilton Date: 2017-04-16 15:13:06 +0000 Separate ICC profile loading into a separate file. This cuts out some duplicate code, and enables us to reuse this logic for PNG, JPEG, and any other future output format. sanei_load_icc_profile also allows us to know that an ICC profile file is not long enough before we start to write it to the output; this should prevent poorly-written software from overflowing into image data when they read the bad profile based on its length. commit f624a9f72527fc2896562e338f9b7c07549af0f0 Author: Olaf Meeuwissen Date: 2017-05-01 09:39:59 +0900 utsushi.desc: sync with upstream commit 0ca2fc46136dbaa30da25f4b79b67871c7e2d39b Author: Louis Lagendijk Date: 2017-05-03 13:52:06 +0200 fix 1200 dpi scanning for MB5000 series commit ff5c11622dafe58d1025f34a3672c7a059e963a7 Author: Louis Lagendijk Date: 2017-05-03 13:48:20 +0200 Fix scanbutton detection for MB5000 series commit 6af0e32bd6bd9e2c1b2adc5cb2a0561902eef526 Author: Paul Newall Date: 2017-05-01 19:49:41 +0100 kodakaio.c patched to change avahi callback variable from global to local. Bug 315705 commit e0741f71e289b878f0efad6cbb56b15d73b49f4e Author: Paul Newall Date: 2017-04-30 18:09:59 +0100 deleted a line in configure.ac. This was a commented out line left from when the backend kodakaio used CUPS to autodetect printers. commit 611388db1d56b0ffd74af05c4a51fe518269c90d Author: Rolf Bensch Date: 2017-04-27 21:59:24 +0200 pixma.desc: fix usbid for PIXMA TS5000 Series commit 5389301a40d67ed627b1998e19a1af60da26e93f Author: Olaf Meeuwissen Date: 2017-04-27 22:26:01 +0900 utsushi.desc: PID 0156 is a ES-400 See Alioth issue 315695. commit a0321d2a92286afbc9557fa44e2de4f706cf2407 Author: Aaron Muir Hamilton Date: 2017-04-16 09:06:48 +0000 Include physical resolution (pHYs section) in PNG output. commit 469dc738aafe42b231ecae778e393263028ab1b9 Author: Olaf Meeuwissen Date: 2017-04-27 21:17:34 +0900 Only swap 16-bit PNG bytes on machines that are not big-endian The SANE API specifies that image bytes are in machine native order. commit 28eb09eec4c64d9070b5747c93c25134395cab53 Author: Aaron Muir Hamilton Date: 2017-04-16 00:27:54 +0000 Swap bytes in 16-bit PNGs, because PNG is big-endian. commit 5fa4be5ae4f9937d79f076f7004a4b48ba765e8a Author: Rolf Bensch Date: 2017-04-26 11:06:00 +0200 pixma: backend version 0.17.36 commit 879565cefcac61e371581d09696ef6d738c2a720 Author: Rolf Bensch Date: 2017-04-26 11:43:30 +0200 new scanners Canon i-SENSYS MF230 and MF240 Series commit ddccf956b44509fc21cdcd4c52b6750e2ddab37a Author: Rolf Bensch Date: 2017-04-26 11:02:46 +0200 pixma_mp150: MG5400 Series has button scan support commit f14285dba30404f23fc9de13d632e69f23f7cb75 Author: Rolf Bensch Date: 2017-04-06 11:20:40 +0200 pixma_mp150: MG5400 Series doesn't need special image format post processing @ high dpi commit 0960bf9c72d25a0f8686314fbe9a6a0f9002ea3c Author: Olaf Meeuwissen Date: 2017-04-18 20:12:31 +0900 utsushi.desc: sync with upstream commit dcddb9505c88485bda6cdcf77927d14008b449ee Author: m. allan noah Date: 2017-04-11 21:22:05 -0400 epjitsu backend v31 - hardware gray support for fi-60F/65F (disabled pending calibration) - merge fi-60F/65F settings - improve desc, conf and man files commit 34e4dbf6c35946ca52678f6a7a61ef002fddb1c2 Author: Olaf Meeuwissen Date: 2017-04-10 20:23:03 +0900 Bump Alpine and Fedora builder versions commit 9712e2aa2595f440252d40c26b680c3cf2b8ae30 Author: m. allan noah Date: 2017-04-08 21:44:27 -0400 fujitsu backend v133 2017-04-08, MAN - initial support for fi-7600/7700 - autodetect various double feed capabilities using VPD - call send_lut if we are using a downloaded gamma table commit 5edb87f9ac950473f8c3a57b4071c970887d8440 Author: Olaf Meeuwissen Date: 2017-04-08 21:08:12 +0900 genesys: Trigger reallocation of gamma tables on next sane_open() Fixes 315491. commit 6eb4a1f164ef116a3ed10ee42883a15c268ea01e Author: Robert Ancell Date: 2017-04-04 11:36:05 +1200 kodakaio: Fix crash when Avahi search fails commit ccb47215e653724f317f7d40138098762578a6f9 Author: Olaf Meeuwissen Date: 2017-04-07 20:28:02 +0900 Add missing double quote Accidentally removed in 38ce7fc086a6ecbee94ef31901a6eb874fb611b1. Thanks to Jon Nelson for pointing this out. commit 38ce7fc086a6ecbee94ef31901a6eb874fb611b1 Author: Olaf Meeuwissen Date: 2017-04-05 18:29:41 +0900 Update SANE developer and backend maintenance status commit cbcab6dffadffdb40f87ddffaf2aa7e1618bb72b Author: Rolf Bensch Date: 2017-04-04 11:28:18 +0200 pixma: backend version 0.17.35 commit 66ce95a5a1a716a92260701c76c0989abf4196bb Author: Rolf Bensch Date: 2017-04-04 11:59:56 +0200 new scanners Canon PIXMA G3000, E470, G3000, TS5000, TS6000, TS8000 and TS9000 [#315658] commit a2e26572729d62123b2f167fee9e671bf26b8987 Author: Rolf Bensch Date: 2017-04-04 11:27:38 +0200 pixma: signal scan cancel from the frontend with new parameter s->param->frontend_cancel commit c5bfbc5a3a43a81776841289cd164a584c2e8ec2 Author: Rolf Bensch Date: 2017-04-04 11:25:53 +0200 pixma: implement adf-wait for imageclass scanners commit 17f3899b1583971351ea7ac40e92660f6558268d Author: Rolf Bensch Date: 2017-04-04 11:23:03 +0200 pixma: new option adf-wait commit 3ef45ec3c3d56bfc60b339dac8a2f19543387ef3 Author: Rolf Bensch Date: 2017-04-04 11:16:20 +0200 pixma: update docs for MG5400 Series commit e6c8ea6f0bcb2c6b6f7004feadc7db6cbfc3ee2b Author: m. allan noah Date: 2017-03-22 12:33:27 -0400 epjitsu backend v30 again - typo in previous commit broke low resolution scans on fi-65F commit a7fde11e15e5604c7b37bc9553cbd08086c08e53 Author: m. allan noah Date: 2017-03-21 21:35:59 -0400 epjitsu backend v30 - bump build number commit 891e3984d0410fa8c698d99d79948df3237e258d Author: m. allan noah Date: 2017-03-21 20:39:47 -0400 epjitsu v30 - fix image truncation when using 150 DPI in Y direction - add 200 and 400 DPI Y direction support for fi-60F/65F commit 42896939822b44f44ecd1b6d35afdfa4473ed35d Author: Olaf Meeuwissen Date: 2017-02-19 16:45:45 +0900 Address memory corruption and information leakage. Addresses CVE-2017-6318, Alioth#315576 and Debian's BTS#854804. commit eaa4d4407d96585b7cdbe194d8ac295eee3e6299 Author: m. allan noah Date: 2017-03-18 19:15:03 -0400 epjitsu backend v29 - fix infinite loop when scaling in Y direction commit 2f9ea038b157fc6d08ce882a1d9984d37b46d809 Author: Olaf Meeuwissen Date: 2017-03-11 10:48:35 +0900 Fix typo See http://alioth-lists.debian.net/pipermail/sane-devel/2017-March/035099.html commit 1e028b72cae0f4caaf48868504bd2cd1667d6da7 Author: Olaf Meeuwissen Date: 2017-03-06 20:54:59 +0900 pixma: updated status for MG5400 Series See http://alioth-lists.debian.net/pipermail/sane-devel/2017-March/035072.html and http://alioth-lists.debian.net/pipermail/sane-devel/2017-March/035082.html commit 928b803ef1f1bc1007ca3fa83418f103c6a7bdd7 Author: Olaf Meeuwissen Date: 2017-03-05 15:35:06 +0900 utsushi.desc: sync with upstream commit f450049bccc1e331790cb2546c2c1440fa3d24c0 Author: Olaf Meeuwissen Date: 2017-02-14 18:52:25 +0900 utsushi.desc: sync with upstream commit e3ed043500e786724cc77fdd0565993e878a51fc Author: Olaf Meeuwissen Date: 2016-12-26 08:13:33 +0900 Update backend maintenance status Following the project member list cleanup, only backends that list a project member with git write access in the AUTHORS file are now considered maintained. commit 7a0e1126b3eebd38944b8b281e6a481b95a35bbd Author: Louis Lagendijk Date: 2017-01-06 23:30:31 +0100 Revert "pixma_mp150.c: Fix scanbutton detection for MB5000 series" This reverts commit 53aa78df3a6980791ce250c471d019c5c59b83ec. commit 306f8f16e78b52948a6e09fa0957b3e364e0189f Author: Louis Lagendijk Date: 2017-01-06 23:29:59 +0100 Revert "pixma_mp150.c: fix 1200 dpi scanning for MB5000 series" This reverts commit dfc5487571ed2915bda7bdedbd4c79015004361b. commit 9bc734042f6a2af159772eef412efe7c65ebb02f Author: Louis Lagendijk Date: 2017-01-06 23:28:56 +0100 Revert "pixma_mp150.c: scan param 0x26-0x29 should contain localtime HHMM, also for MB5000 series" This reverts commit eb712c6bd8ca6a42a1d3941583fa18b9981b8712. commit 6c439bce791522aa9cd3fc04cf62087bdd6c6875 Author: Louis Lagendijk Date: 2017-01-06 23:28:31 +0100 Revert "pixma_mp150.c: replace multiple if statements with case statement in handle_interrupt" This reverts commit 28dcd0aef4ac91b87527c17c8aa89e879f39f20d. commit 06b4b27ecfad1aa30c772fa0fac15d1c144c1e7a Author: Louis Lagendijk Date: 2017-01-06 23:28:09 +0100 Revert "pixma_mp150.c: fix parameter settings for MB5000 in send_scan_param" This reverts commit 1d2a40ec96aea0ba183415efea570301159d4159. commit 589e011992a21ae1edb4684eb89582350c47333f Author: Louis Lagendijk Date: 2017-01-06 22:15:24 +0100 Update the sane-pixma man-page for the new timeout setting option. commit 1b442ce1ea7068bda1129c061071dc94bfd6a8d2 Author: Louis Lagendijk Date: 2017-01-06 21:37:52 +0100 Added examples for new bjnp timer settings in the pixma config file commit cfc7a069d2696942bfeac90e0ff7aa50f83d0ec1 Author: Louis Lagendijk Date: 2016-12-31 17:56:06 +0100 Added timeout options for the bjnp protocol commit c2898cb6021fac6e2a526c2ce4692783ca69b18d Author: Louis Lagendijk Date: 2016-12-30 21:36:26 +0100 Remove incorrect comment left from old version commit 526f642f6487cd348b39a6e138c2bffa9a88d0f6 Merge: 32be261254a7 68cd2744d190 Author: Louis Lagendijk Date: 2016-12-30 20:04:53 +0100 Merge branch 'master' of ssh://git.debian.org/git/sane/sane-backends commit 68cd2744d1906ebf00b2bfdc90662992f8e5385c Author: Olaf Meeuwissen Date: 2016-12-18 16:53:26 +0900 Sort backends with LC_COLLATE=C commit d47957075ff0859f5bd52c683f5b6408c63d1e46 Author: Olaf Meeuwissen Date: 2016-12-17 17:03:32 +0900 Update git repositor write access info commit 078435eaf19d00e6b863a7802073863604c9e9dc Author: Olaf Meeuwissen Date: 2016-09-12 19:58:42 +0900 Fix typo commit 755e829caa23b9781863e4b1e1d24c3cfee9e8bf Author: Dave Platt Date: 2016-12-15 23:12:07 -0800 avision.c: abort "wait for light" loop if interrupted Adds a "cancelled" flag to the Avision device structure, cleared in sane_start() and set in sane_cancel(). Test this flag in wait_4_light() and bail out of the 90-second "wait for the light to warm up" loop if the scan is cancelled. commit 28963459a4ac43100b8e377d65332d81a2660c0d Author: Dave Platt Date: 2016-12-15 23:02:42 -0800 avision.c: Recognize "backlight on" status Avision scanners can return an additional light-status value meaning "Transparent Media Adapter back-light is on". Accept this as equivalent to "on" during the lamp-warmup check prior to starting a scan. Re-check the light status after sending the window command, as the light selection might have been changed and a further warm-up may be required. commit 6585b6f60d07a196e42c99b935e2754bbc80b3c8 Author: Dave Platt Date: 2016-12-15 22:29:16 -0800 avision.c: Add "skip-adf" option The code which tries aggressively to reset/initialize HP 82xx/83xx Automatic Document Feeders doesn't work well if there's no ADF attached, because the scanner reports a non-zero ADF model type in this case. The retry code makes several attempts to initialize the ADF, and then bails out with an "Operation not supported" error. Add a new "option skip-adf" which will cause the backend to believe the "ADF not present" status if that's what the scanner reports. Fixes #313751 commit f58ce8f9201364bd0fe28ef736d4b1345f3f71b2 Author: Troy Rollo Date: 2016-12-02 01:37:23 +1100 HP3500: Always enable the second set of CCDs. This prevents ghosting that occurs when only enabling the first set of CCDs, even though at some resolutions we will not get any data from the second set. commit 77986b1d14db5ba285d595ab7630748b3c6d9284 Author: Troy Rollo Date: 2016-12-02 01:20:43 +1100 HP3500: Move to scan area more quickly if it is a long way from the home position commit b1f3815014f11bbadd74d255ee4343e16ffee6b8 Author: Troy Rollo Date: 2016-11-29 21:11:06 +1100 Add missing math library to HP3500 backend link flags commit fd7a3505f644573e832f417c7f0f62ca8ddeb7c8 Author: Troy Rollo Date: 2016-11-29 20:47:59 +1100 HP3500 backend: fix #314811, greatly improve calibration, support hardware calibration in 120DPI and 400DPI, and add support for hardware gamma correction. commit 6484ff8fc368db24b567e1d2fbb44cd86e0f9e43 Author: Alex Belkin Date: 2016-11-06 23:03:23 +0300 Forgotten comma in backends man pages list. commit 69a008830b84437701022cbd96a8863ac61d3ddf Author: Alex Belkin Date: 2016-11-06 22:28:03 +0300 Pre-create temporary file for Jpeg scanning. Jpeg mode (as submitted by Samsung developer) uses temporary file with fixed name. This opens attacks on reading its content or overwriting any other user-writable file (with symlink). This change pre-creates temporary file with O_CREAT|O_EXCL and mode 0600. Assuming usual +t permissions for tmp this should mitigate issue. commit 226c2b9deb2f4f4b1d7c51ad4bb83d181e5e31b5 Author: Alex Belkin Date: 2016-11-06 21:50:06 +0300 Update header and copyright statements for xerox_mfp. commit 58c1e43b83712899162030d83df3ecac151d69d3 Author: Alex Belkin Date: 2016-11-06 21:38:47 +0300 Indent of xerox_mfp-tcp.c and xerox_mfp-usb.c This is purely indent commit without any code changes, in follow-up to 83bbcfe7d546b4d0cd28269b647fb558c6a1e00a. Indent command used: astyle -A8 -U -H -k3 -W3 -o xerox_mfp-{usb,tcp}.c Proof that code is not changed: git log -w -p xerox_mfp-{usb,tcp}.c commit 610533a9a66d7232a8a0ecd360871e3a6fa67980 Author: Alex Belkin Date: 2016-11-06 21:24:38 +0300 Update xerox_mfp man page. commit ba6a5710b508e73f4cb7152d034adc865f5201d7 Author: Alex Belkin Date: 2016-11-06 21:00:58 +0300 Update sane.man to index xerox_mfp backend. commit 83bbcfe7d546b4d0cd28269b647fb558c6a1e00a Author: Alex Belkin Date: 2016-11-06 20:24:02 +0300 Run indent: astyle -A8 -U -H -k3 -W3 -o xerox_mfp.[ch] This is purely indent commit w/o any code changes. To prove run `git diff -w`. commit e4aed53a1e46b9d46a76f814d414ef8d2c2276e3 Author: m. allan noah Date: 2016-10-08 19:32:43 -0400 fujitsu backend v132 - remove ipc_mode option and variables - set ipc mode based on other options - cleanup inverted logic DTC options - fixes threshold option reported in #315069 commit cfa98fd0d1271dd426ff2d6c565790d87cf3986d Author: Rolf Bensch Date: 2016-10-08 14:25:33 +0200 pixma: backend version 0.17.34 commit 7e20475c8b45249d05315d5a889551924210666d Author: Rolf Bensch Date: 2016-10-08 14:24:53 +0200 pixma: new scanner Canon PIXMA MG3000 Series see bug report #315521 commit 3b63fdab2b94f14765f8ba165546f6d69fc23ea9 Author: Rolf Bensch Date: 2016-10-05 20:05:37 +0200 pixma: backend version 0.17.33 commit c751a9ca22934ef93ca6b7afd3d0aaea4969c5fc Author: Rolf Bensch Date: 2016-10-05 19:51:35 +0200 fix ADF scanning for Canon MF57x0 devices - ignore result from calibrate(): don't interrupt @ PIXMA_STATUS_BUSY - MF57x0 devices don't require abort_session() after the last page - reported by Dmitry Deshevoy commit a9806de08ea65acb8a1109b480147a4a7ec76cb5 Author: Olaf Meeuwissen Date: 2016-09-25 17:06:15 +0900 AWARE: Treat compiler warnings as errors on debian-8-mini too! commit 490c4501c7ca59568bd9611665386c512163b51b Author: Olaf Meeuwissen Date: 2016-09-25 17:04:23 +0900 Sync autofoo commit da49686efe16bc5f7b4ddef18ea0dcb5f7c18d77 Author: Olaf Meeuwissen Date: 2016-09-25 17:00:54 +0900 sane-find-scanner: disable USB if not supported This fixes a [-Wpedantic] compiler warning about ISO C forbidding empty translation units. The check-usb-chip.c file reduces to such a file in the case USB support was explicitly disabled or no usable library found. commit 471e8ac0c1b565e5aea92f35ae173e167f3b61c7 Author: Olaf Meeuwissen Date: 2016-09-25 16:28:11 +0900 sanei_pp: fix [-Wcpp] compiler warning The `#warning` directive is turned into an error by `-Werror`. The directive triggers is guaranteed to trigger with __GNUC__ when no parallel support is available. This would prevent strict CI builds. Output a message with all compilers that understand the pragma. Those that don't should ignore it, according to C99. commit 7d7030a40986d97d3cf7a5313bf889bce4af8131 Author: Olaf Meeuwissen Date: 2016-09-25 15:53:46 +0900 sanei_usb: : fix [-Wunused-parameter] compiler warnings These were issued in the absence of libusb. commit e6faafcdc94e253f7f4fe793fec6110ce37e8014 Author: Olaf Meeuwissen Date: 2016-09-25 15:44:36 +0900 xerox_mfp: fix [-Wunused-parameter] compiler warnings These are issued when libjpeg was not found during configuration. commit 32be261254a78d1f314b3ee86fd5e5b40ab91a86 Merge: ec8bf64396b3 97d2a3d22d9c Author: Louis Lagendijk Date: 2016-09-21 22:35:10 +0200 Merge branch 'master' of ssh://git.debian.org/git/sane/sane-backends commit 97d2a3d22d9c6a6d0a277de0066c73e9059abd92 Author: Olaf Meeuwissen Date: 2016-09-21 22:11:09 +0900 AWARE: Treat compiler warnings as errors on debian-8-full This build became compiler warning free in ee550e5d. Let's keep it that way! commit 4119f05d5f400803a9c952896125e9feabb07393 Author: Olaf Meeuwissen Date: 2016-09-21 21:54:39 +0900 Fix USB ID typo commit ee550e5de8e636642b9895d54f6740aa38ee6ff8 Author: Olaf Meeuwissen Date: 2016-09-19 12:52:31 +0900 pieusb: fix [-Wunused-parameter] compiler warnings The functions that trigger these warnings were not used. commit bf088a6ed15733237df446a84d6659c4e0145c9a Author: Olaf Meeuwissen Date: 2016-09-19 17:45:35 +0900 Merge comments This gets rid of a missing :usbid warning. commit e01f574b19be74081650e9be9bcb16a3420068ef Author: Olaf Meeuwissen Date: 2016-09-19 17:44:48 +0900 Merge duplicate entry commit 1a328e412fb7bf3872de20fe6e57b00ab913a75d Author: Olaf Meeuwissen Date: 2016-09-19 15:26:14 +0900 Add missing USB IDs Taken from http://www.linux-usb.org/usb.ids commit dd940a1f612f56d2acb929afd54614ebc6dfb309 Author: Olaf Meeuwissen Date: 2016-09-17 20:43:44 +0900 utsushi.desc: sync with upstream commit fb0464d9e3e39a93306e98602ff879c43276de5c Author: Olaf Meeuwissen Date: 2016-09-12 20:12:28 +0900 niash: fix [-Wunused-but-set-variable] compiler warning This makes explicit what the compiler has effectively been doing all along anyway since at least 727dd4ab (2004-08-03). commit 6f4b88b6173745f09e4ce1fa465db534eca85751 Author: Olaf Meeuwissen Date: 2016-09-12 20:05:42 +0900 epson2: fix [-Wunused-but-set-variable] compiler warning This makes explicit what the compiler has effectively been doing all along anyway since at least 728e032a (2009-06-01). commit 4dce101c05515200a6cd337451da664c1978f115 Author: Olaf Meeuwissen Date: 2016-09-19 10:04:35 +0900 Keep generated *.eps and *.pdf files out of the repository commit 46bc0ff1c89a6759fd22f80fd1ceb9551268da4f Author: Olaf Meeuwissen Date: 2016-09-18 21:41:03 +0900 Sync autofoo commit 2b973d75fed73771664958ea6cb5b4b8fd7b1307 Author: Olaf Meeuwissen Date: 2016-09-18 21:21:35 +0900 Add fig2dev as prerequisite for API HTML docs The target for sane-html depends on sane.dvi which requires fig2dev. Thanks to: - https://lists.debian.org/debian-user/2016/08/msg00484.html - http://superuser.com/questions/1101439/error-building-sane-backend-on-debian commit db22a031d130d533f5fd41710ddabdc61280d313 Author: Olaf Meeuwissen Date: 2016-09-18 21:19:12 +0900 Fix typo introduced in fa41b180. Fixes 315435 commit 9d388c35937b75cf5f47410ead07ef2226ac8005 Author: m. allan noah Date: 2016-09-11 19:03:23 -0400 canon_dr v56 - add initial support for P-150M (and probably P-150) commit 39ceeae616a2e1638c2760d4364adcaa210a413b Author: Olaf Meeuwissen Date: 2016-09-07 21:03:30 +0900 md5: fix [-Wstrict-aliasing] compiler warnings commit 27a4da41b1761acba33e99aab4d782e726934c04 Author: Olaf Meeuwissen Date: 2016-09-07 21:01:26 +0900 Prefer *our* `include/` files This fixes discrepancies in build results for VPATH builds. commit 737977199ccd2419a51e120c472981b1a1263ab0 Author: Olaf Meeuwissen Date: 2016-09-05 23:40:39 +0900 umax_pp: fix [-Wunused-but-set-variable] compiler warning commit 50fa8efdcff0dd159d3ff51e219a3c08020ca54c Author: Olaf Meeuwissen Date: 2016-09-04 14:39:52 +0900 gphoto2: fix libgphoto2 logging integration In libgphoto2-2.5.0 (tagged for release on 2012-07-10) the GPLogFunc no longer takes a va_list. Instead, it passes a formatted message. commit 23f052113b5567033da0d9c4679451d97c484220 Author: Olaf Meeuwissen Date: 2016-09-04 12:50:19 +0900 saned: fix [-Wunused-parameter] compiler warning The `__sane_unused__` attribute has been moved from sanei_backend.h to sanei.h to make it generally useful instead of limited to backends. commit 0d527f6659e06175d855aaa1a70fc2be52aad1a7 Author: Olaf Meeuwissen Date: 2016-09-04 12:13:16 +0900 Drop build environments Adding them did not quite have the effect I thought it would. I had thought it would allow for easy linking to build logs by environment name. Alas, no such luck. This reverts commits: - 9c44317289f1be0a72089ffd030be84caf75b58c - 6209fecf3a11e90e2e5efc057cf650f4ab792b50 commit 74b4a34e82124621616c699ac0d5ba0d5e93b6a1 Author: Olaf Meeuwissen Date: 2016-09-04 12:12:14 +0900 dll: fix pedantic compiler warning commit 246673715d9abdbfbea349de4636322b242f9700 Author: Olaf Meeuwissen Date: 2016-09-03 14:36:49 +0900 utsushi.desc: sync with upstream commit a65e74fe83ae96d5d00af6a05c751086cdae8d38 Author: Olaf Meeuwissen Date: 2016-08-28 15:49:44 +0900 umax_pp: fix [-Werror=unused-but-set-parameter] compiler warnings commit 4eaefe43713036f73ab322e8d6b519589055540d Author: Olaf Meeuwissen Date: 2016-08-28 15:27:50 +0900 magicolor: fix [-Werror] compiler warning This casts away const-ness in the hope that libsnmp doesn't change the session.peername. commit 31ac0db73672347732dc80cf8f2afe8e6f3d466b Author: Olaf Meeuwissen Date: 2016-08-28 15:25:43 +0900 magicolor: fix [-Werror=pointer-sign] compiler warnings The libsnmp API requires a u_char *session.community :-( commit c32d48ef5fd4a47a93933e6ffba5c8a93d26e43f Author: Olaf Meeuwissen Date: 2016-08-28 14:18:48 +0900 kodakaio: fix [-Weror=format=] compiler warning commit 0337514b8f0e484c11b668d8ca0e0af998d3dfbc Author: Olaf Meeuwissen Date: 2016-08-28 14:18:00 +0900 kodakaio: fix [-Werror=sign-compare] compiler warning commit 763816814bc072b19f1178e0d92031217d7f5f06 Author: Olaf Meeuwissen Date: 2016-08-28 13:55:30 +0900 hp5400: fix [-Werror=overflow] compiler warning This explicitly implements what gcc and clang assign. commit 9c44317289f1be0a72089ffd030be84caf75b58c Author: Olaf Meeuwissen Date: 2016-08-28 13:24:17 +0900 Fix environment name (periods are not allowed) commit 984fefd55173f30f4cde3ed7acb462d660c676eb Author: Olaf Meeuwissen Date: 2016-08-28 13:21:11 +0900 plustek_pp: fix [-Werror=sign-compare] compiler warning commit f0153bc7fe7fd79ef2b1b41d259adf3d20b9d641 Author: Olaf Meeuwissen Date: 2016-08-28 12:57:17 +0900 v4l: fix [-Werror=sign-compare] compiler warning commit 6209fecf3a11e90e2e5efc057cf650f4ab792b50 Author: Olaf Meeuwissen Date: 2016-08-28 12:17:46 +0900 Make referencing builds easier commit 235de98784897edb63d1ed1a32609b7786d799d0 Author: Olaf Meeuwissen Date: 2016-08-28 11:58:16 +0900 md5: fix [-Wpointer-arith] compiler warnings This reverts commits 9aecb00947e6bf7ef1cf412aff2ab41202617b30 and cdad5961f3888f0a3c0c525d0efab33174c11a24 which fixed a different compiler warning that seems to have disappeared (at least on the debian-8-full builds). commit bf53c714348dbf4654de887ae7e649fbad6b2288 Author: Olaf Meeuwissen Date: 2016-08-04 22:47:01 +0900 Sync with upstream commit ffd93a9fad10ced31de037f2c6180f328a38b547 Author: Olaf Meeuwissen Date: 2016-08-03 22:43:06 +0900 Sync autofoo commit cb3090cb5472b63d5cdfb5110652f568bd9477ad Author: Olaf Meeuwissen Date: 2016-08-03 22:16:26 +0900 pixma: Fix include guard typo This was introduced in f573865 on 2008-10-18. commit 23cc2e1ba043ea8309923f5f1dcc5acaeee38a95 Author: Olaf Meeuwissen Date: 2016-08-03 22:08:49 +0900 Fix include guard typo This seems to have been present since the beginning of sanei/sanei_scsi.c. commit 63b55e2b591ba977a285a4c91fde0118c25246e7 Author: Olaf Meeuwissen Date: 2016-08-03 22:05:03 +0900 Drop check for sys/bitypes.h It was obsoleted in 70f3fdf on 2009-02-26. commit d263f25911a0b359b7edec0faa9320d5d13928da Author: Olaf Meeuwissen Date: 2016-08-03 21:48:54 +0900 Fix typo Appears to have been introduced in 0330a4d on 2009-02-02. There is no evidence that a scdds.h file has ever existed. commit 970464f7332a1995bfd825de285425f0c957b1da Author: Olaf Meeuwissen Date: 2016-08-03 20:53:06 +0900 Drop unused CPP define The last reference to it was removed in 5dcf165 on 2007-06-21. commit 5389808029f0f81d204630f6e78abe11dfd61122 Author: Olaf Meeuwissen Date: 2016-08-02 21:56:09 +0900 Fix memory leak commit 1006445982877970d376c0f657e04fc1481e2459 Author: Olaf Meeuwissen Date: 2016-08-02 21:38:43 +0900 Use DIR_SEP instead of hard-coding ":" Fixes 313540 commit b021c3bee8e9edc7854050a3975cef003586b2e5 Author: Troy Rollo Date: 2016-07-31 22:47:25 +1000 Fix bug 314571 - hp3500 backend does not set correct size for string option. commit 1859803563f8baadeb6c0926610b2f782350d1e4 Author: Olaf Meeuwissen Date: 2016-07-27 21:17:08 +0900 Add a Fedora 24 Clang builder commit f6c8be1380c2f12080cdbd7a81eb2d5156eaa154 Author: Olaf Meeuwissen Date: 2016-07-18 20:55:14 +0900 kvs40xx: fix [-Wunused-function] compiler warning This rather unceremoniously removed the function. Note that a near copy can be found in the kvs20xx backend code. commit effc26e4028f2d21a5bd9fec0458a78c0f6dc330 Author: Olaf Meeuwissen Date: 2016-07-18 20:53:51 +0900 kvs20xx: fix [-Wunused-function] compiler warning This rather unceremoniously removed the function. Note that a near copy can be found in the kvs40xx backend code. commit 2d468852288b5d80e9e8b6831467081fe3ec874e Author: Olaf Meeuwissen Date: 2016-07-18 17:32:21 +0900 pnm: fix [-Wformat=] compiler warning commit 2f39c7227021e85b8fdde8161358858797bd7461 Author: Olaf Meeuwissen Date: 2016-07-18 17:11:26 +0900 v4l: fix [Wunused-parameter] compiler warnings commit 880992739020eee8cc0e005e6bdf853f2ff3572f Author: Olaf Meeuwissen Date: 2016-07-18 17:10:56 +0900 sm3600: fix [-Wunused-parameter] compiler warning commit 99c14f2d9503cd9a6c77d71d590e434e504052c6 Author: Olaf Meeuwissen Date: 2016-07-18 16:51:08 +0900 Use a single symbol to flag unused parameters The __sane_unused__ symbol was used slightly more often then UNUSEDARG and, more importantly, defined in include/sane/sanei_backend.h. That makes it readily available to virtually all backends. commit b73a6e5017f6d4d2d0ef7a61bf6e2113f848be94 Author: Olaf Meeuwissen Date: 2016-07-18 16:14:20 +0900 niash: fix [-Wunused-parameter] compiler warning The attribute needs to go before the pointer designation for it to have any effect. commit 61719e0a131a6f0d4bf53aa98ecf510ce7081ca7 Author: Olaf Meeuwissen Date: 2016-07-18 16:11:39 +0900 dc240: fix [-Wunused-parameter] compiler warning The attribute needs to go before the pointer designation for it to have any effect. commit ef8e258165526175c9ac2d262448f16b551d19e9 Author: Olaf Meeuwissen Date: 2016-07-14 21:12:38 +0900 Sync autofoo commit 57350969f6f46195c5fd5e255b3d1906df401d19 Author: Olaf Meeuwissen Date: 2016-07-14 20:51:42 +0900 sane.tex: mark up email as a URL commit 707baa0d160ff13331bed6063d2502b09f902f38 Author: Olaf Meeuwissen Date: 2016-07-14 20:50:14 +0900 doc/sane.tex: drop html.sty in favour of url.sty The latter is part of the core TeX Live distribution which is readily available on all major operating systems. commit bfd1626c4b119404429a847aaa92df77ca3fbb40 Author: Olaf Meeuwissen Date: 2016-07-13 23:44:26 +0900 Sync autofoo commit 8598f81d0b9542b6f4928b8b870a26b50f440379 Author: Olaf Meeuwissen Date: 2016-07-13 23:43:35 +0900 Fix figure orientation commit 00bc3e9b451f8dfb89553305cce89172be520bb1 Author: Olaf Meeuwissen Date: 2016-07-13 23:40:19 +0900 Generate EPS figures too It's good to know how this is done so that we can modify any original figure if need be. commit cc8f99b900d5ae44248677f6881f1b74d7885bfb Author: Olaf Meeuwissen Date: 2016-07-13 23:24:29 +0900 Generate PDF figures explicitly with fig2dev This utility has less dependencies and supports conversion to a pile of other formats that may come in handy as well. Clean up of generated PDF figures has been moved to the clean target as well. commit d7e1995d001b0d16b9920e751be923f6a8393bf1 Author: Olaf Meeuwissen Date: 2016-07-13 22:37:44 +0900 doc/sane.tex: Replace epsfig with graphicx The graphicx package allows for file specification without a hardcoded extension. This provides more freedom in choosing how to prepare images in *.eps and *.pdf formats. Moreover, epsfig is considered mostly obsolete in the TeX community and graphicx is the "current 'preferred' solution" to embed figures. See https://www.ctan.org/pkg/epsfig. commit 0aab12b934a9f9620fb1236e13dd2b71a7426000 Author: Olaf Meeuwissen Date: 2016-07-11 20:41:54 +0900 Bump standard minor version This is to acknowledge the SANE_STATUS_GOOD fixes in net.tex that were committed in 4daf488 and 1100883. commit 1ed3b9fa86e6bc6875a72a9b347164a5879ffe8b Author: Olaf Meeuwissen Date: 2016-07-11 20:37:08 +0900 Reclassify doc/html.sty It is a package used by doc/sane.tex. commit 2481e1e98b02e927cdb9c2e6e404252ef9e49041 Author: Olaf Meeuwissen Date: 2016-07-10 15:43:42 +0900 Sync autofoo commit fa41b18001f74e8941813709f15a15cc9ea299e1 Author: Olaf Meeuwissen Date: 2016-07-10 20:35:04 +0900 Refactor API spec conversion support Unless explicitly asked to do without, all formats for which required tools are found will be built. Checks cover PostScript, PDF and HTML. All logic and files involved are now collected in a single "section" and grouped in such a way that their function should be clearer. fixup! Refactor API spec conversion support commit d7c69d141ffb4f456d659b443aa4c3d3be82793e Author: Olaf Meeuwissen Date: 2016-07-10 19:40:04 +0900 Add test for the TeX to HTML converter commit fe2fbe06e1b7db42ea2578e8d853cf83d7bb72a9 Author: Olaf Meeuwissen Date: 2016-07-10 19:29:55 +0900 Refactor TEXINPUTS handling commit f322cbc7f950b087e969ac115a5b4e14735881e4 Author: Olaf Meeuwissen Date: 2016-07-10 15:41:08 +0900 Add support for PDF API spec output Note that pdflatex internally uses epstopdf which in turn requires a working ghostscript. commit 5a73fb24d1e7a5dcdd287ce7654050b1703cc9c6 Author: Olaf Meeuwissen Date: 2016-07-10 14:48:43 +0900 Be quiet but do not hide errors commit e0fd693bb170813f95241898a53ba3f9c261c22a Author: Olaf Meeuwissen Date: 2016-07-10 13:32:22 +0900 Fix API spec generation for VPATH builds commit cb04102a20bc5b1e9cbb990805de3586a0a940c0 Author: Olaf Meeuwissen Date: 2016-07-09 15:10:00 +0900 Add GitLab CI musl build job commit be7af6835741c18ea93babd421c083c377b1f88d Author: Ruslan Nabioullin Date: 2011-10-29 20:27:47 -0400 Fixed scanning status messages. Fixes 313411 commit d62d0a2e452e9ed1ae74cc20c01488543cda8ef9 Author: Olaf Meeuwissen Date: 2016-07-06 20:23:12 +0900 check-po.awk: fix awk invocation The awk utility has been moved to /usr/bin and the script relies on features present in gawk but not in mawk. commit ddbdfd6206e30e513509720dd5137c26c9efc248 Author: Olaf Meeuwissen Date: 2016-07-03 21:11:04 +0900 Fix configure time test commit c17821e3609080309bcb8a3173cb071e1493a62c Author: Olaf Meeuwissen Date: 2016-07-02 19:23:01 +0900 Fix configure time warning during CI build commit c96236862635f74d8a1c01654430d93bed83f756 Author: Olaf Meeuwissen Date: 2016-07-01 23:45:43 +0900 Sync autofoo commit a9c813944e9aa0ceb6c153d06dff286ba855b90e Author: Olaf Meeuwissen Date: 2016-07-01 23:37:49 +0900 Make libusb-1.0 the default for USB support When libusb-1.0 is not found, libusb-0.1 will be tried. On BeOS and OS2, nothing should have changed in terms of detection of USB support. On all systems the --enable-libusb* flags are now ignored. Instead, the --with-usb and --without-usb flags now control support. When neither is given USB support will be enable if possible and disabled otherwise. If --with-usb is requested but not possible, ./configure will fail. There is no support to prefer libusb-0.1 over libusb-1.0. commit 418fbb987d731ed2468f48a6fb162f47ea7da55e Author: Olaf Meeuwissen Date: 2016-06-30 21:50:33 +0900 Sync autofoo, once again ... commit 602182931dceeedddcc6be3b144cbc1423bd9b4a Author: Olaf Meeuwissen Date: 2016-06-30 21:49:25 +0900 Improve test shell builtin invocation portability Looks like I missed this when committing 309a8140. commit 828cd013e9a4411158af8816a63b133b6da19ed5 Author: Olaf Meeuwissen Date: 2016-06-30 21:38:04 +0900 Compile getopt replacement functions only if needed This ought to get rid of the last empty compilation unit warnings. This merges libfelib.la with liblib.la. The former was always used together with the latter. The latter was only ever used without the former when linking backends and tools. Tools might want to use the getopt functions, backends might just want to use MD5. commit 89dc05006ec93e8fd8a103504701db720814669a Author: Olaf Meeuwissen Date: 2016-06-29 22:21:10 +0900 Sync autofoo Looks like the sorting of the DIST_COMMON file list is not stable. commit a298bfe5a8c65236ac13130e891a93bf3cf58976 Author: Olaf Meeuwissen Date: 2016-06-29 22:20:51 +0900 Fix AS_IF condition commit f03f1f66d4a7704c20d221897665509d7f1a626a Author: Olaf Meeuwissen Date: 2016-06-29 22:15:06 +0900 Sync autofoo files commit 1c946f7c194869966f6d3af9df499184d3035c20 Author: Olaf Meeuwissen Date: 2016-06-28 22:42:09 +0900 Compile replacement functions only if needed This ought to get rid of most empty compilation warnings. commit 0c2b86ac0b56ee7cb8b962102c54855f05887b08 Author: Olaf Meeuwissen Date: 2016-06-27 22:33:28 +0900 Fix distcheck issues in tools/ Automake is smart enough these days to compile the *.lo files that are used when linking. The umax_pp_CPPFLAGS are useless because backend/umax_pp_low.c undefines BACKEND_NAME unconditionally and sets it to what was passed here ;-) commit 81f9557966ad335a76deff57a112607d9813ba7a Author: Olaf Meeuwissen Date: 2016-06-27 22:08:20 +0900 Clean up .gitignore file content Removes empty lines and duplicates (note that .gitignore patterns are recursive unless anchored with a leading /). commit 61f3fec68e14944be7ecf219ec604e599141cbdf Author: Olaf Meeuwissen Date: 2016-06-27 22:05:50 +0900 Remove references to SANE CVS The references in in-file changelogs and the ChangeLog files have not been modified. Only those references that refer to current practices or locations. commit c23791cbd71d58b2627cfc65bbf2cb20aeb51bba Author: Olaf Meeuwissen Date: 2016-06-27 21:51:43 +0900 Sync AUTHORS content with current status We've been using git for a while ;-). Also updates the write access info and fixes encoding issues. commit cb4924f210bf758558b583220a14ed5a62f178dc Author: Olaf Meeuwissen Date: 2016-06-26 10:22:35 +0900 Stop distclean from removing files willy-nilly The distclean target should only remove files that make creates. If there are any that need taking care of, they should be added in on a per file basis in the Makefile.am that creates it. commit 0065aaa1c8cb823d20b1e0ff32b3f6197e73db3f Author: Olaf Meeuwissen Date: 2016-06-26 10:11:42 +0900 Remove overly zealous ignore patterns Text editor backup and temporary files like *~ are a per developer issue, add them to your global .gitignore. Other dot-files should be ignored on a case-by-case basis. commit 8274a52eec3c20becf9901acaab4837b6b61c2f7 Author: Olaf Meeuwissen Date: 2016-06-26 10:07:26 +0900 Sort .gitignore files commit b25ef62c2bb163fffea56000ef25544a236d2096 Author: Olaf Meeuwissen Date: 2016-06-26 10:05:12 +0900 Fix sorting of ignore files We haven't been using CVS since ... 2009-05-04 :-| commit 0bc7b6aed7e3ac8ee78190f0e795ce9c62c3f36e Author: Olaf Meeuwissen Date: 2016-06-26 09:16:54 +0900 Sync *.po files with previous changes This changes the file/line references in all files (and picks up any new messages as a side effect). commit bdade6bb07a0ec27e3930c0b3b86c28a5c2a5bd9 Author: Olaf Meeuwissen Date: 2016-06-26 09:15:24 +0900 Sync derived files with previous changes commit ec4fe7ec7200a881feba5b539256dc58ae1edbaf Author: Olaf Meeuwissen Date: 2016-06-26 08:50:04 +0900 Prefer gettext's --disable-nls option commit c676acbe472f2a57763d0ce198e50b048d37bbda Author: Olaf Meeuwissen Date: 2016-06-26 08:49:14 +0900 Switch to using the gettext build machinery This adds two automatically maintained English variants that handle quote substitution in onrder to provide smart quotes. commit 0286bf46fc2992dfd2e9539fb939c243db13e91f Author: Olaf Meeuwissen Date: 2016-06-26 08:30:45 +0900 Add gettext build machinery files for I18N/L10N support These are courtesy of autopoint and target gettext-0.18.1 because that is the version in Debian 7 (which is what Alioth is running). The one warning this produces on autoreconf is fixed in gettext-0.18.2, BTW. commit eecee6d1014537cabe427d44168fce9ac9894301 Author: Olaf Meeuwissen Date: 2016-06-25 15:23:05 +0900 Fix VPATH build issue The source *.po files weren't found when you configured outside of the top source directory. commit 00984cd1119bb9bb5b794bfc0f1ef77369978d68 Author: Yurii Kolesnykov Date: 2016-06-25 03:34:12 +0300 Fix compilation of saned.c under OS X commit ad176ba9fe1f7608d8ad8032407cf1f7dc66dad6 Author: Alessandro Zummo Date: 2016-06-21 00:50:02 +0200 epsonds: remove unused var commit 1dbabdce5d34e686608bb997245c49a823e19fb5 Author: Alessandro Zummo Date: 2016-06-21 00:49:08 +0200 epsonds: fix compilation commit 9b389a61c5e698e8bffb186bdbc005739128b427 Author: Alessandro Zummo Date: 2016-06-21 00:43:39 +0200 epsonds: added support for network scanners commit f13ca02b86a4d27951149723ca2ae81491991c18 Author: Olaf Meeuwissen Date: 2016-06-20 21:42:56 +0900 Fix variable name typo and other typographic nit-picks commit e7100e275d58d67b903e883e7a02e45d18f81ef7 Author: Olaf Meeuwissen Date: 2016-06-15 20:47:28 +0900 Update documentation to reflect recent changes commit 5695d01077c7e575808a37a66df37d1f9fafa637 Author: Olaf Meeuwissen Date: 2016-06-15 20:23:33 +0900 Silence overly verbose targets This only takes effect with silent rules enabled. commit 43cdc1056af899520797b09f3f78ba64703ed74d Author: Olaf Meeuwissen Date: 2016-06-15 20:18:25 +0900 Do not duplicate Warning on output commit 40bfec7a6b4a804201bad7f0517f16b0c6ae4110 Author: Olaf Meeuwissen Date: 2016-06-15 20:17:29 +0900 GitLab-CI: fix configure option commit f75e271853f40c6cb00ce59cbb4e9d62d083a1b2 Author: Olaf Meeuwissen Date: 2016-06-14 21:24:53 +0900 Tweak GitLab CI builds The idea is to build as much as possible for the *-full flavour build, make compiler warnings stand out and muddle on after errors so that we get all the problems in a single log. commit ef7deff36c759efa8d371251febef6d37d4eb94b Author: Olaf Meeuwissen Date: 2016-06-14 21:20:48 +0900 Add missing autofoo file This should have gone in with d853463e. commit d853463ef1b97082d657a0be32d1b27a04940ea2 Author: Olaf Meeuwissen Date: 2016-06-14 06:30:48 +0900 Sync derived files This was done with AUTOMAKE="automake --add-missing --copy" autoreconf --force on Debian GNU/Linux 8.5. commit 17d78ab7c326069c3a596f17ded6418ef42930bc Author: Olaf Meeuwissen Date: 2016-02-12 21:09:32 +0900 Fix warnings of "newer" automake versions commit a7376ffa6dd1796c1678b33bd03bf19766e227de Author: Olaf Meeuwissen Date: 2016-02-12 20:52:57 +0900 Fix libtoolize warning commit 7675cb7c3eb5b6e97fb87adc464b6bcc67cfd7dd Author: Olaf Meeuwissen Date: 2016-02-11 17:43:14 +0900 Drop Makefile.in from EXTRA_DIST Automake is smart enough to include this on its own. commit 7bfa818c57485310994650528124a7036894eb36 Author: Olaf Meeuwissen Date: 2016-01-14 21:22:00 +0900 Set up compiler and linker early and in one place commit f80cf1dbbdd85f2a0b595aef83cc63faa0c91c04 Author: Olaf Meeuwissen Date: 2016-01-14 20:21:49 +0900 Remove unused SANE_LINKER_RPATH macro commit e4ad1f58f31ec7110df716494b13cf96156b6a7c Author: Olaf Meeuwissen Date: 2016-01-14 18:50:47 +0900 Enable ISO C99 support for more compilers commit 444b572d1a559d93a6ad172f86bdcc9197377e75 Author: Olaf Meeuwissen Date: 2016-01-09 18:43:36 +0900 Use autoconf provided --docdir commit f1f4180e5b6f71bf3a674bbfcbf7932b401d7a96 Author: Olaf Meeuwissen Date: 2016-01-09 18:24:20 +0900 Collect autoconf bits in one place commit 35f1936aeba18b34628d35938529e0d2af0e58c7 Author: Olaf Meeuwissen Date: 2016-01-09 18:11:53 +0900 Determine version bits and release programmatically commit 16497dc13838e14784778883fb2837fbcf9e288e Author: Olaf Meeuwissen Date: 2016-01-09 17:59:20 +0900 Replace convenience variable by its expansion commit 9a52b5bc27c2a50e14161b4dcaba43f825cf91a4 Author: Olaf Meeuwissen Date: 2016-01-09 17:48:27 +0900 Fix variable name typo commit 79c623628a28b6c28321cad6518bf98cf7a705a4 Author: Olaf Meeuwissen Date: 2016-01-09 17:48:03 +0900 Fix new automake warnings commit 97fed76717367289a8e160dab4341d79586c1079 Author: Olaf Meeuwissen Date: 2016-01-09 17:22:31 +0900 Replace obsolescent macro with LT_INIT option commit c868a2e8d48e3c31051b6a8978c9b1df494bc36b Author: Olaf Meeuwissen Date: 2016-01-09 17:22:07 +0900 Remove commented out code commit 4894b1513d829957e109e5c49b812dc688fe78b3 Author: Olaf Meeuwissen Date: 2016-01-09 17:11:19 +0900 Drop a pile of unnecessary macro invocations These are already taken care of by other macros. commit ae89c8f98362a4e50d4b1a0ce8f3586b77a81989 Author: Olaf Meeuwissen Date: 2016-01-09 15:59:35 +0900 Update to automake-1.11.6 This turns on additional warnings and adds support for silent make rules (so compiler warning really stand out ;-). commit 87e12bbf2e4312bf0bd18656afe579919c8788e1 Author: Olaf Meeuwissen Date: 2016-06-13 22:24:21 +0900 Sync derived files with autoreconf --force For the record, this was done on Debian GNU/Linux 8.5. commit 309a8140e7285f87d5fc5ee24b942f1713e80be8 Author: Olaf Meeuwissen Date: 2015-10-28 21:59:42 +0900 Improve test shell builtin invocation portability POSIX 2008 obsoletes the -a and -o operands (according to the autoconf documentation). commit d9e2f441139c0514c3d388976d1f8fc78a04de95 Author: Olaf Meeuwissen Date: 2015-10-28 20:59:22 +0900 Double quote code passed to AC_LANG_PROGRAM Upon re-reading the autoconf documentation, this seemed the right thing to do. The AC_LANG_PROGRAM macro does not quote its arguments so any occurrence of [ and ] will get stripped out if not double quoted. Current code snippets passed do not use them but it is better to use a consistent quoting approach for such snippets. In other places in the configure.ac file double quoting is used already. commit d3d5dc3da04cce842f7fa8d941676f2f5abc3bbd Author: Olaf Meeuwissen Date: 2015-10-22 22:18:19 +0900 Update to autoconf-2.69 This gets rid of a number of obsoleted macros and replaces RETSIGTYPE with a void literal which can be assumed for C89 and later. This changeset was prepared using autoupdate and modified to get rid of whitespace and doubled [] changes. Changes to acinclude.m4 were added by hand. commit 2d6fad58b4c61d59bec3879dab9ccf0c1b88dd11 Author: Olaf Meeuwissen Date: 2015-10-22 20:15:45 +0900 Respect GNU Standards user variables Several variables are considered "user variables" by the GNU Standards. This means that the user can pass these to their ./configure and make invocations as they see fit. These variables include CPPFLAGS, CFLAGS and LDFLAGS. This changeset pushes our use of these variables to the automake shadow variables, AM_CPPFLAGS, AM_CFLAGS and AM_LDFLAGS, so that user variables will no longer clobber flags required in order to build. commit 7a2b112d20e08337528128745bf7cc1d6096a1d8 Author: Olaf Meeuwissen Date: 2015-10-21 22:33:53 +0900 Prefer make variables over literal substitutions The AC_SUBST and PKG_CHECK_MODULE invocations in configure.ac as well as acinclude.m4 are sufficient to trigger automake to insert initialization logic for the variables these introduce. There is no need to do this by hand. Wherever these values are needed, it is better to use a variable. This allows for one-off overrides on the make command-line (without the need to run ./configure) for one thing. commit 2745528b60617d737a7a597c4ee5397359a6ddfd Author: Olaf Meeuwissen Date: 2015-10-19 21:52:47 +0900 Fix non-POSIX automake file variable warning This addresses warnings about the use of the $(wildcard) function. It has been replaced by a plain shell glob. There is one case where this will result in an error, when there are no matches, but that is when the targets that use it make no sense. It is probably good to get an error in that case. commit d81836bd2332e42136a15a72961c0b9295d94b58 Author: Olaf Meeuwissen Date: 2015-10-19 21:04:53 +0900 Add automake subdir-objects option This addresses autoreconf output such as: tools/Makefile.am:37: warning: source file '../backend/sane_strstatus.c' is in a subdirectory, tools/Makefile.am:37: but option 'subdir-objects' is disabled The option was introduced in automake version 1.9 and is slated for unconditional activation with automake-2.0. commit d38a098ec6fce0aa18dd757677c5a07ff1a44163 Author: Olaf Meeuwissen Date: 2015-10-19 20:20:56 +0900 Rename configure.in to configure.ac This name has been preferred since autoconf-2.52 and automake-1.5. Both were released in the summer of 2001. Support for configure.in is slated for removal in automake-2.0. The configure.ac file itself requires autoconf-2.54 or later (and does not care about the automake version). commit c9027378a12a6f67b22ee5fe203df1739486e3ad Author: Olaf Meeuwissen Date: 2016-06-13 20:35:34 +0900 xerox_mpf: Make JPEG support an option commit 359bb9c3d48c87324b2ead602de76e0e35daadba Author: Olaf Meeuwissen Date: 2016-06-12 20:32:30 +0900 Add CI support to test selected build scenarios commit 5ba37467e88ca8052973b37128ce8fd36ad5d61d Author: m. allan noah Date: 2016-06-09 21:51:18 -0400 fujitsu backend v131 - hide compression-arg option when jpeg disabled - add Send/SC/GHS macros for recent scanners - add initial support for fi-74x0 - add initial support for fi-7030 - set has_MS_lamp=0 for fi-71x0 - add I18N macros to all option titles and descriptions - add usb ids for many newer scanners commit ec8bf64396b394c4d483b811f9948871f12882b4 Merge: 1d2a40ec96ae 1e013654cc3a Author: Louis Lagendijk Date: 2016-05-21 22:06:57 +0200 Merge branch 'master' of ssh://git.debian.org/git/sane/sane-backends commit 1e013654cc3af09f4731ab9ec8d8324d03a7de4a Author: Alessandro Zummo Date: 2016-05-09 11:10:35 +0200 pieusb: mkostemp -> mkstemp commit 926bfade544de4a4fd5f1a8082b85a97e2443770 Author: Alex Belkin Date: 2016-05-07 22:33:03 +0300 Color scanning for Samsung models, which support JPEG Lossy compression. Patch is submitted by Laxmeesh Onkar Markod Patch to code is applied almost verbatim, except, insignificant formatting fixes and making new functions static. Also, new USB ids added and scanner support is changed as reported. commit 1d2a40ec96aea0ba183415efea570301159d4159 Author: Louis Lagendijk Date: 2016-04-30 15:32:55 +0200 pixma_mp150.c: fix parameter settings for MB5000 in send_scan_param commit 28dcd0aef4ac91b87527c17c8aa89e879f39f20d Author: Louis Lagendijk Date: 2016-04-27 18:45:22 +0200 pixma_mp150.c: replace multiple if statements with case statement in handle_interrupt commit eb712c6bd8ca6a42a1d3941583fa18b9981b8712 Author: Louis Lagendijk Date: 2016-04-27 17:55:52 +0200 pixma_mp150.c: scan param 0x26-0x29 should contain localtime HHMM, also for MB5000 series commit c8169b1e656f7f95c67946298da5a0e1c143f8e8 Author: Louis Lagendijk Date: 2016-04-22 23:58:23 +0200 pixma.h: step version of the backend to 0.17.32 sane-pixma.man pixma.desc: mark Maxify MB5000 as complete (but ADF does not work) commit dfc5487571ed2915bda7bdedbd4c79015004361b Author: Louis Lagendijk Date: 2016-04-22 22:42:48 +0200 pixma_mp150.c: fix 1200 dpi scanning for MB5000 series commit d5e1323738a4ed2edc96b566138c7cc6c55f3f5e Author: Louis Lagendijk Date: 2016-04-22 21:05:40 +0200 pixma.h: step pixma backend version to 0.17.31 commit c2985f0fd60279bca0dcfed3d3fb60cc33cfe4dc Author: Louis Lagendijk Date: 2016-04-22 21:03:48 +0200 pixma_bjnp_private.h: change minimum timeout value for the bjnp protocol to 1 sec as 500 ms gives errors on scanimage -A (it workked for scanbd) commit 4b2f171a13248a8e3d79379e368c54fb71ed97e2 Author: Louis Lagendijk Date: 2016-04-20 11:19:32 +0200 pixma_bjnp.c pixma_bjnp_private.h: set minimum timeout for network operations to 500ms commit affe1ac821b17813188bd91045ced32fe4fcfe56 Author: Louis Lagendijk Date: 2016-04-20 10:58:54 +0200 pixma_bjnp.c pixma_bjnp_private.h: make bjnp protocol follow timeouts set by backend commit 53aa78df3a6980791ce250c471d019c5c59b83ec Author: Louis Lagendijk Date: 2016-04-18 22:00:15 +0200 pixma_mp150.c: Fix scanbutton detection for MB5000 series commit 1452cf2e0d9f56602a5ca9b07e52f8d8f6b9ec8a Author: Louis Lagendijk Date: 2016-04-16 21:37:25 +0200 pixma_bjnp: Remove dead code in polling commit 19c128a23e27c1ab5a030fa6ff74da1b740629bb Author: Rolf Bensch Date: 2016-04-12 18:30:24 +0200 README.linux: move installation description into new document INSTALL.linux commit 51943537c314c9060138df8b37307377c307ba3a Author: Rolf Bensch Date: 2016-04-12 18:14:55 +0200 pixma: backend version 0.17.30 commit 270a8cd3bebfb2a630a1b74d5abd0b6fe2fc2e5d Author: Rolf Bensch Date: 2016-04-12 18:13:36 +0200 pixma: Canon PIXMA MG7700 Series is working commit 2556ffc2c593c3408cc554ecc97e07db457c975d Author: Rolf Bensch Date: 2016-03-31 11:21:27 +0200 pixma_mp150: MG7700 Series doesn't need special image format post processing @ high dpi commit e6b6ad9d4847e86aed8be0837a19bfada881f52d Author: m. allan noah Date: 2016-04-05 20:30:10 -0400 canon_dr backend v55 fixed-width scanners were calculating left-side offset incorrectly in color initial support for DR-F120 rename all DUPLEX_INTERLACE_* to indicate start and end of line commit 41a416e4afcf6cada69193dc408ef184d0e5f678 Author: Alessandro Zummo Date: 2016-03-21 18:59:47 +0100 epsonds: fixed resolution setting bug, DS-60000 workaround is needed no more commit 52bc4d241c9587e99be2ae4566ad01469a3ecab9 Author: Rolf Bensch Date: 2016-03-21 10:53:29 +0100 pixma: backend version 0.17.29 commit bc1637bb39794b415a2e73ee990e4763c73be3f9 Author: Rolf Bensch Date: 2016-02-16 16:20:58 +0100 pixma_mp150: MG3600 Series doesn't need special image format post processing @ 1200dpi commit 23eb95582da718791103b83ea002e947caa0f5fc Author: Rolf Bensch Date: 2016-03-04 13:38:12 +0100 pixma: backend version 0.17.28 commit eba9c9e2391d59459e5fa3f362d8dc08d14c7b9c Author: Rolf Bensch Date: 2016-03-04 13:36:08 +0100 pixma_imageclass: software lineart for all imageCLASS, imageRUNNER, i-SENSYS and laserBase scanners, supported by this sub-backend imageCLASS scanners: D420, D480, D530, MF810/820, MF3240, MF4010, MF4150, MF4270, MF4320, MF4360, MF4410, MF4570dw, MF4690, MF5630, MF6500, MF8030, MF8170c i-SENSYS scanners: MF210, MF220, MF3010, MF4500, MF4700, MF4800, MF5880dn, MF5900, MF6100, MF6680dn, MF8200C, MF8300, MF8500C imageRUNNER scanners: iR1133 laserBase scanners: MF5650 commit 38d6f1ba6dd48d8139b3ef504b4c7920d0e4736b Author: Rolf Bensch Date: 2016-03-04 13:08:31 +0100 pixma_imageclass: MF6100 Series document feeder does not have 600DPI capability commit 328e9b93d71d1f9b031c4066e4cba00893703c4a Author: Stéphane Voltz Date: 2016-03-03 21:59:24 +0100 raise build number commit 8ad2c60d82797cc3495f63f8230bdf6f999067a1 Author: Stéphane Voltz Date: 2016-03-03 21:58:55 +0100 use half step mode for LiDE 110 600 dpi scan commit 52ea420d44a26832f662c837bbbe4d2f7002875a Author: Stéphane Voltz Date: 2016-03-03 21:46:43 +0100 use half step for LiDE 210 600 dpi scans commit bd0b0cd218504868f32962a5558449956c8ce242 Author: Stéphane Voltz Date: 2016-03-03 21:39:25 +0100 use rewind instead of slow_back_home - if required by flags, do a rewind instead a slow_back_home that pollutes shading settings commit 3dee0f8d48e26e3aceb0243d03199af5870f30e2 Author: Stéphane Voltz Date: 2016-03-03 21:31:01 +0100 implement rewind for gl847 and gl124 commit de635a32f9638f5fad5806ab5de9498f5fa47ca9 Author: Stéphane Voltz Date: 2016-03-03 21:12:29 +0100 add rewind function to command set commit 270d6a4242c95d6d376f442ca701e067e3fc74d1 Author: Stéphane Voltz Date: 2016-02-29 21:14:30 +0100 fix Lide 110 gray mode max speed commit 2f891c13bb5cb1728479ee08ba45b756f68a3287 Author: Rolf Bensch Date: 2016-03-03 13:40:46 +0100 pixma: backend version 0.17.27 commit bbd327f5e7c973db331f3f309788d852367bc79a Author: Rolf Bensch Date: 2016-03-03 13:38:33 +0100 pixma_imageclass: reduce timeout for failed first usb command to 2 seconds commit c41640f36433e646989665bac0ac5da460f2d9a9 Author: Rolf Bensch Date: 2016-03-03 13:36:44 +0100 pixma: new global parameter to set duration of receive timeout loop The default value for receive timeout loop duration has not been changed. The loop still takes 8 seconds if a timeout occurs. commit 9897357727b65d13167b89cd1d1fe816f4d72563 Author: Stéphane Voltz Date: 2016-02-28 21:26:27 +0100 enable pixel averaging when below 600 for all models commit d784dec60b096b8e8fe18f5f7b5c1921249d6ed2 Author: Stéphane Voltz Date: 2016-02-28 12:26:12 +0100 motor table fixes - add target speed for 600 dpi gray - add slope table for 4800/2400 dpi, gray and color commit 759729331cc60a86d884518064b2dc92736f7abd Author: Stéphane Voltz Date: 2016-02-28 12:18:56 +0100 fix maximum speed compute in gray mode commit e1b1ba8aeecf2eefa2855ec8c77604a8070027ab Author: Stéphane Voltz Date: 2016-02-26 05:57:35 +0100 fix expiration time option reading commit 61d6f5c2140e6e659db20916937cc95c5c7256f3 Author: Stéphane Voltz Date: 2016-02-25 21:37:55 +0100 mode set fixes commit fa2c9dc9b216fa7b7f1baf013b7cbe8eb3df8597 Author: Stéphane Voltz Date: 2016-02-25 21:37:00 +0100 fix GPIO during scan commit c5117ed0f1b522eab10fd2248f140b2acad2a708 Author: Stéphane Voltz Date: 2016-02-24 21:43:29 +0100 doc and version update commit 747275a00f03e4ab643461448985ce20879e1d12 Author: Stéphane Voltz Date: 2016-02-24 21:15:26 +0100 add 1200 and 2400 dpi mode for LiDE 120 commit 2a18600792c00f9ca37ff5b52d2cc1c8202d8b4b Author: Stéphane Voltz Date: 2016-02-24 06:35:46 +0100 make 1200 dpi scan mode work for LiDE 120 commit b11a69d27572aa938cedff303ba7560ca9793943 Author: Stéphane Voltz Date: 2016-02-24 06:17:50 +0100 make 600 dpi mode work for LiDE 120 - add override for registers 0x16 and 0x70 since 120's sensor is set differently according to resolution commit 15e203de89abe53f732aa431548645c5c9838237 Author: m. allan noah Date: 2016-02-23 21:11:00 -0500 Various copyright comment updates commit 1465543cc0dd9e4fb4b3706ca6793e90a35ce389 Author: m. allan noah Date: 2016-02-23 21:07:06 -0500 fujitsu backend v130 run init_model before init_ms so some scanners can override set all M309x and M409x scanners s->broken_diag_serial = 1 commit 963de58dfc4498693aeeec35a9ff23363af6ac67 Author: m. allan noah Date: 2016-02-23 20:42:52 -0500 Enable padded read for DR-3080CII commit 1207ce5a40664c04b934bd0a6babbc1575361356 Author: m. allan noah Date: 2016-02-23 20:36:32 -0500 Support SANE_USB_WORKAROUND env var Here, we add a new env var (SANE_USB_WORKAROUND) to sanei_usb.c. This allows end users to conditionally enable the workaround added in commit 014b45d920f1fb630e1a31bb01f1da02ea2a6a87. The default is to not enable the workaround, because more recent Linux kernels no longer seem to need it. Some users have even reported failure with the workaround enabled. commit 219198838be069d7e3d48b53762727f1bbfb93e5 Author: Stéphane Voltz Date: 2016-02-23 21:39:22 +0100 LiDE 120 working [75-300] dpi - fix sensor pixel number - enable calibration - default to 16 pixel DUMMY commit 4fd1d10228047a740f28b1cd25579b7c40c93b1f Author: Stéphane Voltz Date: 2016-02-22 21:45:02 +0100 tune scanarea geometry commit a74ebe551daf8750821b1ab57324e54141a84461 Author: Stéphane Voltz Date: 2016-02-21 22:14:48 +0100 first almost correct scan at 300 dpi commit 8f938f2d00d27070e826f88a77264ee36d0bbedf Author: Stéphane Voltz Date: 2016-02-21 09:48:26 +0100 GL124 DAC and SENSOR setup changes commit f56ffa63b99db4767b35d294fef96476b228f993 Author: Stéphane Voltz Date: 2016-02-21 09:47:47 +0100 doxygne fix commit 2e1102dc3ab1ee4d7bfec3e7cbf6e3a1527f5264 Author: Stéphane Voltz Date: 2016-02-18 21:41:38 +0100 add dedicated reference slope tables for LiDE 120 commit 214dfad1d5aa841b597187004747ec88c507cf06 Author: Stéphane Voltz Date: 2016-02-17 21:43:05 +0100 use dedicated LiDE120 GPIO commit 387cea7d6ce75af9441c5841221267bef7b7924c Author: Stéphane Voltz Date: 2016-02-17 21:04:55 +0100 define dedicated motor/sensor/gpio for LiDE 120 commit e90f8cb8a1e62f6865d9e237fd1f0e7f773e5094 Author: Stéphane Voltz Date: 2016-02-12 08:11:20 +0100 latin1 -> utf8 commit d74d3bcd887d2a3d59ee96e04eb68f15c0a3b882 Author: Rolf Bensch Date: 2016-02-18 22:34:16 +0100 README.linux: add some hints for individual SANE installations commit 926a7e67b6f81ffdb1b4f67a55bf84862efcf6b9 Author: Rolf Bensch Date: 2016-02-17 21:24:18 +0100 pixma: fix scanner names commit 235dc80b5f3faaf195eef1e177a1f38284ca3bee Author: Rolf Bensch Date: 2016-02-17 21:10:44 +0100 pixma: Canon i-SENSYS MF6100 Series is working commit 6d89f7ef25266cc9a53440bbf68ff2e1a37d4b86 Author: Rolf Bensch Date: 2016-02-17 21:06:04 +0100 pixma: backend version 0.17.26 commit 7a227dcaf2771b31feb852e6a83336976966a0ab Author: Rolf Bensch Date: 2016-02-17 21:03:18 +0100 pixma_imageclass: wait for 8sec to throw last page out of ADF feeder @ ADFDUP commit 23efb9f55f466179df86848912acbed1a94c10ff Author: Rolf Bensch Date: 2016-02-17 21:01:48 +0100 pixma_imageclass: PIXMA_ENO_PAPER check also for ADFDUP scans commit 29ceb0e377009cd6829d8f68c58a2197ac19a20d Author: Rolf Bensch Date: 2016-02-17 20:59:32 +0100 pixma_imageclass: fix for failed first usb command (timeout) Sometimes the first usb command to the scanner fails after a previous scan. A workaround was to reconnect the usb cable. This fix simply resends the failed command. commit 8f1202d6537568e56b4886ebe5d41d0210ffacd9 Author: Rolf Bensch Date: 2016-02-17 20:55:49 +0100 pixma_imageclass: fixes for MF6100 Series - special ADF settings - the MF6100 Series is a Generation 2+ scanner, but uses the old read image command commit 02855737ee84e3c72bceff9ba06fddc5a5d99860 Author: Rolf Bensch Date: 2016-02-17 20:32:00 +0100 pixma: use usb timeout instead of sleep timer while waiting for usb response commit 4771fab9c16b477dc376bd2e5fdd8d0dc7f2c478 Author: Rolf Bensch Date: 2016-02-17 20:30:47 +0100 pixma: reduce global usb read and write timeouts to 1 sec commit 99f7e7872f2619c3ebec2590b91de3fb0ca28a1a Author: Olaf Meeuwissen Date: 2016-02-13 17:37:34 +0900 Disable use of pthreads if pthread.h cannot be found commit 04d5e4a0cf775f6483873a3f1e70a173587546b6 Author: Olaf Meeuwissen Date: 2016-02-13 17:34:46 +0900 Move #include to sanei_thread.h The sanei header file may need pthread_t. If it does, make sure it is available. commit af35e9ba749cce6e735d685ec4495e8c8d85778c Author: Rolf Bensch Date: 2016-02-11 12:06:34 +0100 pixma: Canon Pixma MG3600 Series is working Reported by Rajib Bandopadhyay commit 7bd773ade5ff1bc4170d2a1773428efe759aaa75 Author: Stéphane Voltz Date: 2016-02-09 09:55:26 +0100 LiDE 120 support wip commit 056f590f2d147099554d97a89dd5e0ddfa8d6dda Author: Alessandro Zummo Date: 2016-01-23 00:55:31 +0100 epsonds: fixed comment commit cf2dfea0008d16cb70f8baeaae5a414e9bdafda3 Author: Alessandro Zummo Date: 2016-01-22 20:44:36 +0100 epsonds: extend the bug fix to all depths commit 8fd6151acd1a1b313b1c9fabb5e2691ba45de9d6 Author: Alessandro Zummo Date: 2016-01-22 17:38:00 +0100 epsonds: fix stripes on DS-60000 at 1bpp/300dpi commit 4fa7f4f6f8b69f41fd5d03abc12d16e0f2e876b3 Author: Alessandro Zummo Date: 2016-01-22 17:01:57 +0100 epsonds: clear endpoints on startup in order to fix some strange scanner / xhci combo commit f78e85cad666492fadd5612af77fa7c84e270a12 Author: Rolf Bensch Date: 2016-01-08 18:35:20 +0100 pixma: backend version 0.17.25 commit b08c324223732407d736a79ab8c59f01b3774736 Author: Rolf Bensch Date: 2016-01-08 18:34:27 +0100 pixma: Canon PIXMA MG5700 Series is working Reported by Andrew Reginato, bug #315244 commit 0f47b47b759908e7b11800404d269087d60d87ad Author: Rolf Bensch Date: 2016-01-08 17:32:36 +0100 pixma_mp[150|810]: disable send_time() The function send_time() makes trouble with a lot of scanners. We send a warning to the debug output instead of sending the time. fix for [#315248] Canon MP250 with pixma backend is not working commit b040b150a08b0b5394ad3bc91a45966d97f58152 Author: Rolf Bensch Date: 2016-01-08 16:54:30 +0100 pixma: fix for [#315244] Add support for Canon PIXMA MG5700, MG6800, MG6900 and MG7700 These scanners are new and need further testing! commit 81c429082c3c139f8616de4a73733953cd5aa33f Author: Rolf Bensch Date: 2016-01-08 16:43:46 +0100 pixma: update copyright commit 9aecb00947e6bf7ef1cf412aff2ab41202617b30 Author: Olaf Meeuwissen Date: 2016-01-08 21:26:31 +0900 md5: Avoid the need for casting This also modifies the code slightly to follow the original array access more closely. commit cdad5961f3888f0a3c0c525d0efab33174c11a24 Author: Volker Diels-Grabsch Date: 2015-12-06 01:23:51 +0100 Introduce md5_set_uint32 This fixes the following GCC warning: warning: dereferencing type-punned pointer will break strict-aliasing rules commit f3471c2ca6ea22846dd6619aff7913610722a6c0 Author: Olaf Meeuwissen Date: 2016-01-02 17:50:34 +0900 saned: Use system header IPv6 test macros if available This should fix [-Wstrict-aliasing] compiler warnings on those systems where the IN6_IS_ADDR_LOOPBACK and IN6_IS_ADDR_V4MAPPED macros exist. POSIX.1-2001 states that they shall be defined in netinit/in.h so most systems will probably have them. commit 0de5416a9bafa1ade52722c509817276bbf87d8a Author: Olaf Meeuwissen Date: 2016-01-02 16:06:34 +0900 umax_pp: fix memory leak Found courtesy of [-Wunused-but-set-variable] compiler warning. commit 9d78ace3b7e60afa2b3cb829ac23e5f8b4ccdc07 Author: Olaf Meeuwissen Date: 2016-01-02 16:06:00 +0900 umax_pp: fix [-Wunused-but-set-variable] compiler warnings commit 3f0c3df2fcde8d0cf30ab68c70cb5cad984dda6f Author: Olaf Meeuwissen Date: 2015-12-31 17:51:00 +0900 kvs40xx: fix [-Wstrict-aliasing] compiler warnings commit b1f886a2e36555fb0b01c8eb22a0041aebe25795 Author: Olaf Meeuwissen Date: 2015-12-31 17:47:40 +0900 kvs20xx: fix [-Wstrict-aliasing] compiler warnings commit 0e3a5c4197fe36eae739a6086b484ff16b83d663 Author: Olaf Meeuwissen Date: 2015-12-31 17:43:25 +0900 kodakaio: fix [-Wformat=] compiler warnings This uses the ANSI C approach that is used in a lot of existing code rather than what ISO C99 allows for consistency. commit cadb4b0fff00540159625320416e5601c4704627 Author: Olaf Meeuwissen Date: 2015-12-26 17:45:56 +0900 saned: fix [-Wmaybe-uninitialized] compiler warning commit 572e61d39a10ac7299d95005fa7543afe4957d80 Author: Olaf Meeuwissen Date: 2015-12-26 14:56:12 +0900 p5: fix [-Wunused-but-set-variable] compiler warnings commit f9e44963801be2940c71e49d7c76a4e59b28dfc7 Author: Olaf Meeuwissen Date: 2015-12-26 14:51:21 +0900 xerox_mfp: fix [-Wunused-but-set-variable] compiler warning commit f276e3e05503c5f44890df5a8105959ea07dbb6d Author: Olaf Meeuwissen Date: 2015-12-26 14:49:25 +0900 umax1220: fix [-Wunused-but-set-variable] compiler warning commit e28bdadae98f203188253c5b0a8825a284c8c066 Author: Olaf Meeuwissen Date: 2015-12-26 13:27:47 +0900 u12: fix [-Wunused-but-set-variable] compiler warning commit 70c9e1d13ce705249ddbbee2dad5e6307fe77ffa Author: Olaf Meeuwissen Date: 2015-12-26 13:25:18 +0900 sm3840: fix [-Wunused-but-set-variable] compiler warnings commit 9d4f2a72098436e4a65d2047a151ba3c0af82907 Author: Olaf Meeuwissen Date: 2015-12-26 13:18:13 +0900 sm3600: fix [-Wunused-but-set-variable] compiler warning commit 82720a2023d8e75480036a9e86f8a57d0bfe6a10 Author: Olaf Meeuwissen Date: 2015-12-24 19:01:09 +0900 sharp: fix [-Wunused-but-set-variable] compiler warning commit da010d6088a75ed85472c46e9a29ace18cd7eda9 Author: Olaf Meeuwissen Date: 2015-12-24 17:44:04 +0900 niash: fix [-Wunused-but-set-variable] compiler warning commit 86c5d6d7bd52dcb4208c7d5a1ba8844e8c8011e5 Author: Olaf Meeuwissen Date: 2015-12-24 17:38:41 +0900 nec: fix [-Wunused-but-set-variable] compiler warning commit 220429ff9ebc78e9869e431125ffd9a235efa93d Author: Olaf Meeuwissen Date: 2015-12-23 22:27:47 +0900 mustek_usb2: fix [-Wunused-but-set-variable] compiler warnings commit 7efb05627273266a8171d3f9244bbd7a433d6e44 Author: Olaf Meeuwissen Date: 2015-12-23 22:19:01 +0900 mustek_pp: fix [-Wunused-but-set-variable] compiler warning commit 664398cc311b85647a804bee0f8f230a0e3a3f49 Author: Olaf Meeuwissen Date: 2015-12-23 22:12:07 +0900 mustek: fix [-Wunused-but-sed-variable] compiler warnings commit 8b75c6d45002ffafae6cfbf2edd64b3de35d3b04 Author: Olaf Meeuwissen Date: 2015-12-23 22:17:40 +0900 microtek2: fix another [-Wunused-but-set-variable] compiler warning This allows for the fact that the backend may be compiled with and without support for authorization. commit c1458770955b5476eedc89755bb424744b05b055 Author: Olaf Meeuwissen Date: 2015-12-23 22:11:31 +0900 microtek2: fix [-Wunused-but-set-variable] compiler warnings commit 26aa8b52f60adf7553b2872f54a85cfd7b20dd98 Author: Olaf Meeuwissen Date: 2015-12-26 17:41:09 +0900 magicolor: fix [-Wunused-function] compiler warning This function is only used when SNMP support is activated. commit 292dd42bb6585e2801623d1f33497581626d9d72 Author: Olaf Meeuwissen Date: 2015-12-23 21:50:42 +0900 magicolor: fix [-Wunused-but-set-variable] compiler warning commit d0e85e9d5e775f143f3db98e762d72861f60a82b Author: Olaf Meeuwissen Date: 2015-12-23 21:48:09 +0900 ma1509: fix [-Wunused-but-set-variable] compiler warning commit 7c0f5ec66c7fd673d27876b538e52440a897cc1f Author: Olaf Meeuwissen Date: 2015-12-26 17:36:57 +0900 lexmark: fix [-Wmaybe-uninitialized] compiler warning This adds a default handler to switch statement so low_get_start_loc() bails before the start_600 variable is used. commit 068c3bb75d3972dd2218caa84a6de761c5ca0db8 Author: Olaf Meeuwissen Date: 2015-12-26 17:28:34 +0900 kodakaio: fix [-Wunused-function] compiler warning The function is only used when Avahi support is enabled and has been put in a suitable compilation scope. commit 610dc9ceb4d9eb061209d935e2b1966dfca5a74c Author: Olaf Meeuwissen Date: 2015-12-23 21:38:08 +0900 kodak: fix [-Wunused-but-set-variable] compiler warning commit 2724f85552def3fa0c92d88389854c283eac38d3 Author: Olaf Meeuwissen Date: 2015-12-23 21:36:54 +0900 hpsj5s: fix [-Wunused-but-set-variable] compiler warning commit aabb63dbbfdfea9f444019a19171bdcace08b0dd Author: Olaf Meeuwissen Date: 2015-12-23 21:35:39 +0900 hp4200: fix [-Wunused-but-set-variable] compiler warnings commit a82798d5e1d8352daa1d519d77a7a22771c8439e Author: Olaf Meeuwissen Date: 2015-12-23 21:33:40 +0900 hp3500: fix [-Wunused-but-set-variable] compiler warning commit 51277aec4a6959f88d46d7ca14cfb94166c328eb Author: Olaf Meeuwissen Date: 2015-12-23 21:24:10 +0900 hp: fix [-Wunused-but-set-variable] compiler warnings commit a97b1738dfcc16cd0a7fd68f95df8b013f3cb206 Author: Olaf Meeuwissen Date: 2015-12-23 21:19:46 +0900 gphoto2: fix [-Wunused-but-set-variable] compiler warning commit cbeb98daeceb262b3a868c1b2ea2102b98760e61 Author: Olaf Meeuwissen Date: 2015-12-23 21:10:29 +0900 epson: fix [-Wunused-but-set-variable] compiler warning commit cbc72a017123b81a21c6b69ffcb6f828cc810f03 Author: Olaf Meeuwissen Date: 2015-12-23 21:08:46 +0900 dmc: fix [-Wunused-but-set-variable] compiler warning commit fb4147c4f13f7f39c00a807ecc327b5824df21f5 Author: Olaf Meeuwissen Date: 2015-12-23 20:58:07 +0900 dc240: fix [-Wunused-but-set-variable] compiler warning commit bcfa3f6591744e43a0aa7be228505423b25f7731 Author: Olaf Meeuwissen Date: 2015-12-23 20:56:49 +0900 dc210: fix [-Wunused-but-set-variable] compiler warning commit c31cf621062ae4ef69a80d51879d242978853859 Author: Olaf Meeuwissen Date: 2015-12-23 20:55:36 +0900 dc25: fix [-Wunused-but-set-variable] compiler warnings commit 43b296471bc5a902de470f57df681d0c04964b91 Author: Olaf Meeuwissen Date: 2015-12-23 20:51:28 +0900 coolscan: fix [-Wunused-but-set-variable] compiler warnings commit 50ca4bc97670fed1083a5677ddfa71f74bd495dc Author: Olaf Meeuwissen Date: 2015-12-23 20:49:17 +0900 cardscan: fix [-Wunused-but-set-variable] compiler warning commit c2b7b1af748527dd8ba0fcabc4310fcac1bdf491 Author: Olaf Meeuwissen Date: 2015-12-23 20:46:13 +0900 canon636u: fix [-Wunused-but-set-variable] compiler warning commit e8818437456f36c721f12d311bdb65f3d45606c7 Author: Olaf Meeuwissen Date: 2015-12-23 20:39:15 +0900 canon: fix [-Wunused-but-set-variable] compiler warnings commit 592430016c97b4362314c63a1ac7c96f23d71e4d Author: Olaf Meeuwissen Date: 2015-12-23 20:30:02 +0900 avision: fix [-Wunused-but-set-variable] compiler warning The variable seems to have been superseded by conv_out_size in the else branch of set_calib_data(). commit 5b8fdd317f1bb4076b8fe661db78c230af21004b Author: Olaf Meeuwissen Date: 2015-12-23 20:27:20 +0900 artec_eplus48u: fix [-Wunused-but-set-variable] compiler warning This leaves the sane_close() implementation just as broken as it has been from the initial commit sometime in 2002. commit ec1936713e67b192d057b2f552a25622f88844a1 Author: Olaf Meeuwissen Date: 2015-12-23 20:26:21 +0900 artec_eplus48u: fix [-Wunused-but-set-variable] compiler warning The variable was only used to silence a [-Wunused-parameter] warning. commit 5b21a24312f2cf2ced5d5c70b0f7d2c1f0c58f35 Author: Olaf Meeuwissen Date: 2015-12-23 19:57:13 +0900 artec: fix [-Wunused-but-set-variable] compiler warning The variable is used to assign return values but is not referenced by anything itself. commit 57ae115eafa5b52b84b8e2878982cdad7c1a9f13 Author: Olaf Meeuwissen Date: 2015-12-26 17:12:41 +0900 sanei_thread: fix [-Wmaybe-uninitialized] compiler warning commit cd3453948dd812aaa77d921b198351b340f8c748 Author: Olaf Meeuwissen Date: 2015-12-23 18:53:06 +0900 sanei_usb: const_cast data buffer The libusb_bulk_transfer() API caters to both reads and writes and as a result of that will not take a const pointer. commit be084e0d7bb7933e1826e6e48b42f43bc3cb077a Author: Olaf Meeuwissen Date: 2015-12-26 17:05:17 +0900 sanei_usb: Move variable declarations to scope of usage Fixes an [-Wunused-variable] when compiling without libusb. commit 0d7882e34b5f5625261258a3a341a58a04beb304 Author: Olaf Meeuwissen Date: 2015-12-26 16:51:44 +0900 Change compile time warning policy The -Wall flag is now always enabled (when using gcc). The -Wextra flag (previously known as -W) and -pedantic flags are, by default, enabled during development but disabled for releases. All other flags have been removed. commit 5136e664b8608604f54a2cc1d466019922b311e6 Author: Olaf Meeuwissen Date: 2015-12-20 21:58:45 +0900 Document ChangeLog policy change commit 674b75fc913187415b4f151a52e4bc7004c5d5b0 Author: Olaf Meeuwissen Date: 2015-12-20 17:33:55 +0900 Improve readability of condition This also fixes the following GCC warning: warning: logical not is only applied to the left hand side of comparison commit 58052a7ce6b205e25a2fc85f31158595cad00258 Author: Volker Diels-Grabsch Date: 2015-12-05 21:33:48 +0100 Change GCC mode from ISO C90 to ISO C99 Add GCC flag "-std=c99". Also remove GCC flag "-ansi" which is an alias for "-std=c90" and would make the flag "-std=c99" ineffective. This fixes all occurrences of the following GCC warning: warning: ISO C90 does not support '__func__' predefined identifier commit baec5e039504de390cae41557b0b8323343721c3 Author: Volker Diels-Grabsch Date: 2015-12-05 10:48:59 +0100 Use consistently __func__ instead of __FUNCTION__ Some parts of SANE used __FUNCTION__ while other parts used __func__. Now, __func__ is used consistently throughout the SANE sources. This fixes all occurrences of the following GCC warning: warning: ISO C does not support '__FUNCTION__' predefined identifier commit 93298674d02b1127efdf4e726e92e797a0081c9e Author: Volker Diels-Grabsch Date: 2015-12-05 20:44:14 +0100 Merge all compatibility macros around __func__ and __FUNCTION__ Various parts of SANE introduced their own compatibility macros for old compilers that don't support __func__. Most of these definitions are identical or have minor differences. This patch replaces them with a single instance in the central header file for backends. commit 7c8c8e29beed2c4e6f96db08031c12c9001ca784 Author: Volker Diels-Grabsch Date: 2015-12-06 03:32:51 +0100 Remove dead code due to unused variables This fixes some occurrences of the following GCC warning: warning: variable '...' set but not used commit aba87a4582d594cf953b4bda5b5d42e37e13f21c Author: Volker Diels-Grabsch Date: 2015-12-06 01:30:30 +0100 Mark internal function toupper_ascii as static This fixes the following GCC warning: warning: no previous prototype for 'toupper_ascii' commit 13b6faee2a0696eb02a61f22121b7f4854987f2e Author: Volker Diels-Grabsch Date: 2015-12-06 03:03:06 +0100 Fix interface of helper function write_many This fixes two occurrences of the following GCC warning: warning: cast discards 'const' qualifier from pointer target type commit badd7025b21b2207cfb95001aeb324597e24b301 Author: Volker Diels-Grabsch Date: 2015-12-06 02:37:10 +0100 Bugfix: On error, return the actual error code in sanei_magic_rotate Up to now, on failure the sanei_magic_rotate function returned a success status. This fixes the following GCC warning: warning: variable 'ret' set but not used commit 6aced758f4a95fd858906f3e898ee967a9718641 Author: Volker Diels-Grabsch Date: 2015-11-29 16:02:01 +0100 Fix typos in comments While reading through the SANE sources I noticed some annoying typos in the comments. This patch is my contribution to make the SANE sources slightly more pleasant to read. commit 6d8b8d5aa6e8da2b24e1caa42b9ea75e9624b45d Author: Alessandro Zummo Date: 2015-12-15 16:17:53 +0100 epsonds: fix duplex w/ double feed detection commit e9b52639e8dca938b5569b1697c36796ab92174a Author: m. allan noah Date: 2015-12-15 07:23:42 -0500 Add note about changelogs commit 06d876f74e5d07cb82d02ca1222926a61a3e458b Author: Stéphane Voltz Date: 2015-12-15 06:05:07 +0100 fix for #315050 - handle case where linesel is rounded to 0 when scan y resolution is below minimal scan resolution but higher than half of the minimum commit 753d123f36d08cdb8047adac7c89a9fe015c4b25 Author: m. allan noah Date: 2015-11-28 08:34:58 -0500 canon_dr desc update add note about mode switch on P-208 commit f1593204c72124f3077b0b29f3c128d30be415b5 Author: Olaf Meeuwissen Date: 2015-11-27 21:26:46 +0900 Drop execute permission Source code, description files and configuration templates should never need to be executed. commit e094c5b43b6d8d8bec84bd5b1ab2c6009f38e14b Author: Olaf Meeuwissen Date: 2015-11-13 22:06:30 +0900 Invert sense of sanei_thread validity check Double negatives suck. commit fe12d31e6873f5c3de330f1de4c4fbc851df1e9f Author: Olaf Meeuwissen Date: 2015-11-13 21:41:56 +0900 Fix SANE_Pid related -Wsign-compare warnings This may fix threading related issues on WIN32 platforms, BTW. commit ed59056d40a143b3cf9243ea0068448a88ce3108 Author: m. allan noah Date: 2015-11-23 21:26:09 -0500 canon_dr backend v54 - br_x and br_y locked to page_width/height until changed commit d0ea697ab83360bf0ccddcd02565f85ea0f96b50 Author: m. allan noah Date: 2015-11-23 21:08:30 -0500 fujitsu backend v129 - br_x and br_y locked to page_width/height until changed - add USB ID's for fi-6xxxLA and fi-6xxxZLA scanners - add USB ID's for unsupported SP11xx scanners (genesys based) commit e6a249e0424f6854b7ed12f8272633a0edecfa90 Author: m. allan noah Date: 2015-11-12 21:10:49 -0500 revert mustek_usb2 backend upgrade Instead of 100+ revert commits, I've lumped everything into a single commit. This removes all of dedf0ac30 through db7f038f1, plus the parts of 497d59180 which were specific to mustek_usb2 commit 21c5f4d814f93a363377798635c7f1cbc1c79342 Author: Olaf Meeuwissen Date: 2015-11-11 22:38:56 +0900 Add missing #include for malloc() commit 7dc416b7ae3f0db7c5ce97b5bce0f05f869d877f Author: Olaf Meeuwissen Date: 2015-11-11 22:38:32 +0900 Remove unnecessary cast commit fff1c5962a94ab410dba4fed541312554c8edf8d Author: Olaf Meeuwissen Date: 2015-11-11 22:35:34 +0900 Fix "discards 'const' qualifier from pointer target type" warnings The line variable is not (and cannot be) declared 'const'. The string variable points to a location in line, hence it is safe to cast away 'const'ness of the sanei_config_skip_whitespace() return value. commit 38153b33e1657137e6956e63a9fcb76d665f86f2 Author: Olaf Meeuwissen Date: 2015-11-11 22:33:34 +0900 Fix "discards 'const' qualifier from pointer target type" warning The device2 variable is not modified so let's make that clear. commit d2576bbb948dd9d8e06f66dc4cede774c5518751 Author: Olaf Meeuwissen Date: 2015-11-11 22:30:10 +0900 Fix "discards 'const' qualifier from pointer target type" warnings The cs3_xfree() functions is really just a checked call to free() so its signature has been changed to match that. The warnings that causes have been fixed in the same way and on the same grounds as the previous commit. commit e65fd1b4cb5e9463d16a6fb4cd8eeb129ba58e63 Author: Olaf Meeuwissen Date: 2015-11-11 22:28:17 +0900 Fix "discards 'const' qualifier from pointer target type" warnings The resources pointed to have been acquired by the backend. It is safe to cast away the 'const' qualifier when releasing these resources. commit 497d59180649a831691220aefcc8ec81d5f90960 Author: m. allan noah Date: 2015-11-08 17:17:37 -0500 automake for recent changes updates to mustek_usb2, canon_dr and umax_pp require automake run commit db7f038f108ca12966b947dd80f3772e2133495e Author: Jan Hauffa Date: 2012-07-14 23:15:02 +0200 Fix a bug introduced during refactoring that caused discolored stripes to appear at the left and right sides of the scan area. commit a64477bf1a381458e0500ef7c62da521d6c4d97f Author: Jan Hauffa Date: 2012-07-14 16:44:28 +0200 Remove USB 2.0 mode, as it causes horrible backtracking for resolutions > 300 dpi. commit 98248f6088850045579a1e46f2e8aa16302f9dd9 Author: Jan Hauffa Date: 2012-06-30 22:37:21 +0200 properly add new handle to linked list commit 3b48a6d10560e855cef815ca5c1ecbb50fd02d00 Author: Jan Hauffa Date: 2012-06-30 00:04:06 +0200 fix integer overflow bug in image data conversion commit d0ea79f00059a4a8c3a2ac710fd0443c8bbb3d3a Author: Jan Hauffa Date: 2012-05-27 12:23:53 +0200 Add model-specific functions for computing the number of motor steps. commit 15fbbf60a6d8c9956a6b6a874f2d0b734ab1b935 Author: Jan Hauffa Date: 2011-05-15 22:42:17 +0200 Split SetMotorSteps into a function that computes the number of steps for the various motor states and and a function that writes the step counts to the controller's registers. commit 70dcfa4cedf3619575d413126d3530b28e490fd3 Author: Jan Hauffa Date: 2011-05-15 21:16:00 +0200 Simplify SetMotorStepTable*. commit 27b636d294876c7aac0b53922bacac627ba873c9 Author: Jan Hauffa Date: 2011-05-15 19:58:09 +0200 Provide model-specific function for computing the motor current. commit 8f405f5a937fd05a67a7554138c047fb5abe5e2c Author: Jan Hauffa Date: 2011-05-15 18:13:44 +0200 Update motor table generation according to new dumps. commit 2fa054456e723d7741be4eeb09bb879b802fad9d Author: Jan Hauffa Date: 2011-05-15 15:09:21 +0200 Detect USB controller type. commit d8a203a69373bb6f207de9ddc9c30a60da4a2cc9 Author: Jan Hauffa Date: 2011-05-15 14:35:06 +0200 Add function to compute scan motor table for 4800H48U, rearrange code in Asic_SetWindow. commit ceb7d29690f2ce8f32576caa4b7197fc91b024c1 Author: Jan Hauffa Date: 2011-05-08 17:39:31 +0200 Fix some misuse of SENSOR_DPI constant. commit 30e39071560412b362e03a105e20996093074d81 Author: Jan Hauffa Date: 2011-05-08 16:44:15 +0200 Allow for model-specific parameters in mustek_usb2_high.c. commit 47c56e3f19b38c97db4f2d14f780343adca9024f Author: Jan Hauffa Date: 2011-05-08 14:55:42 +0200 Add some motor-related code for the Microtek 4800H48U. commit 7b26e0e3fa48742b66bd4632772d70d140058c70 Author: Jan Hauffa Date: 2011-05-08 13:56:17 +0200 Simplify SimpleMotorMove. commit 0a1125895b3f1ed0058729fcc29d6858ac6d5e56 Author: Jan Hauffa Date: 2011-05-07 13:58:24 +0200 Add some information about the values retrieved by GetChipStatus. commit c691b0224130763239966c1d7856e96810289238 Author: Jan Hauffa Date: 2011-05-07 13:28:52 +0200 Remove restriction to 8-bit color in negative mode. The only reason for that restriction was that the histogram stretching code, which has been removed earlier, could only handle 8-bit color. commit 4ced7a900a6463c460121eaed7583f4249ef74c2 Author: Jan Hauffa Date: 2011-05-07 12:45:50 +0200 Improve debug logging and fix a bug in TestDRAM. commit e1c00bd9ab9d3f4b568d5bb6d09e0812cbef7b7f Author: Jan Hauffa Date: 2011-05-04 23:30:14 +0200 Introduce SetMotorCurrentAndPhase for Microtek scanner, fix old bug in simplification of Mustek variant. commit 96b4950499d0cf44369fc8e50099925de2ac3c4f Author: Jan Hauffa Date: 2011-04-25 20:25:02 +0200 Register related fixes. commit 5d556cd5e65ba9d1df30e3a3beb0322549d040ff Author: Jan Hauffa Date: 2011-04-25 19:26:32 +0200 Introduce structure for specifying model-specific parameters on the ASIC level. commit 529b6a1d6dc3ff52945f8bf3323bf571596e8088 Author: Jan Hauffa Date: 2011-04-24 21:01:10 +0200 Fix bug in device list handling. commit 877f250b5c32f8403ab321d14fd76afd7e2c2d1d Author: Jan Hauffa Date: 2011-04-24 20:54:19 +0200 Resolve issue with debug logging. commit 272c1d6fdeb8af9d6d4b61f85b4a77ca2ebbb645 Author: Jan Hauffa Date: 2011-04-24 20:30:19 +0200 Add preliminary model entry for the Microtek 4800H48U. commit 7fda9bbf93f4565e1334ebafd0b6caf13d462de4 Author: Jan Hauffa Date: 2011-04-24 20:21:41 +0200 Introduce support for multiple scanners. Keep all state local and ensure that all resources are freed by sane_exit. Ensure that each function returns only those error codes that are valid according to the SANE standard. commit 6865eaf6edef0cc5ea30bcc0d72b52189132f4d6 Author: Jan Hauffa Date: 2011-04-24 14:49:06 +0200 Simplify sane_read, other fixes to mustek_usb2.c. commit 63793907e2c2120511603a504396f7053fdc5149 Author: Jan Hauffa Date: 2011-04-24 14:15:59 +0200 Minor fixes to image processing. commit bc203e8aa125f276ce84498091185482a22d3527 Author: Jan Hauffa Date: 2011-04-24 13:55:35 +0200 Sanitize option handling and add support for scanner buttons. commit b2b3cf767a9a91677b0ac13a6645ed918ca71d55 Author: Jan Hauffa Date: 2011-04-24 00:59:19 +0200 Use standard macros for byte swapping. commit 281f53aa1829a61286232efed7a9d6253cec32d4 Author: Jan Hauffa Date: 2011-04-24 00:39:59 +0200 Remaining endian safety fixes. commit 80b7249ebf9c57b60cbc0a25288cc04ba2302b56 Author: Jan Hauffa Date: 2011-04-23 22:42:45 +0200 Resolve minor thread safety problem. commit de84f51270f05ead6758d2811a2d36160c412cb1 Author: Jan Hauffa Date: 2011-04-23 19:31:50 +0200 Simplify row stride calculation in Asic_SetWindow(). commit 7cd88d41222c85bd262fbf681401ae8c2dab1bce Author: Jan Hauffa Date: 2011-04-23 19:21:52 +0200 Remove Scanner_ScanSuggest. commit 3f5592ef8e7eedc0a9ff52b8091f9b41b9c2d56f Author: Jan Hauffa Date: 2011-04-23 19:04:31 +0200 Move global state variables of mustek_usb2_high.c into scanner structure. commit 60a244ed1e1a4e4f8c4dbd83f12141c0c35202ab Author: Jan Hauffa Date: 2011-04-23 18:08:28 +0200 Store size of shading table in ASIC structure. commit ed1997b22f5f11ccc96262ed841f03b705982737 Author: Jan Hauffa Date: 2011-04-23 17:22:15 +0200 Do not test for NULL before calling free(). commit 35c6a52834271e41960030b0c24f8cda2245230d Author: Jan Hauffa Date: 2011-04-23 15:28:26 +0200 Fix error propagation in mustek_usb2_high.c and a layering violation. commit babee5168909c8c3b8d01c36ab509ea4f7092c4f Author: Jan Hauffa Date: 2011-04-21 23:25:34 +0200 Fix indentation of debug macros. commit c0e44738a41c2cd772125d0774772a541dbac7c5 Author: Jan Hauffa Date: 2011-04-21 23:23:18 +0200 Simplify debug output in mustek_usb2.c. commit c377a0245332df3f5e07c3cbd5ead889f18e0549 Author: Jan Hauffa Date: 2011-04-21 23:10:08 +0200 Simplify debug output in mustek_usb2_high.c. commit eebbe92c4e73922f1fa1368852a77697c23a1ad1 Author: Jan Hauffa Date: 2011-04-21 22:45:21 +0200 Simplify debug logging in mustek_usb2_asic.c. commit 2c712f27c90109e2106d7e8d2f5eeb5af6314260 Author: Jan Hauffa Date: 2011-04-21 00:12:26 +0200 Remove uninformative function name prefixes, move gamma table creation into separate function. commit c1dc16d709459e054f50604f86f1ee41c0e7177f Author: Jan Hauffa Date: 2011-04-20 23:10:53 +0200 Extensive changes to reduce the amount of global variables in mustek_usb2_high.c. Might have made MustScanner_SetupScan less readable, though. commit 9444bf1156f0ab52937d101ba6120ae8685b645b Author: Jan Hauffa Date: 2011-04-17 22:54:14 +0200 Reorganize some MustScanner_* functions. commit bd7107ca057b1f45313d96114c6376bb5ee7c200 Author: Jan Hauffa Date: 2011-04-17 22:29:32 +0200 Remove some unneeded global variables from mustek_usb2_high.c. commit 589d6e15eee356121582eae90d7a938ff094c1e6 Author: Jan Hauffa Date: 2011-04-17 21:57:18 +0200 Endian-safe macros; moved remaining ASIC state into structure. commit f89a7962deafc3b015700469bc2976ac1b931165 Author: Jan Hauffa Date: 2011-04-17 21:00:26 +0200 Fix indentation. commit df045634cb6a9667c625fb88ecb36bcb58310cda Author: Jan Hauffa Date: 2011-04-17 19:14:53 +0200 Misc fixes. commit 86f399bd21d7794ba07265b503fea39baa1bb859 Author: Jan Hauffa Date: 2011-04-17 19:00:30 +0200 Code in mustek_usb2.c should not use global state of mustek_usb2_high.c. commit 74effd61fd8c9663b994459e8f5a738cc95c4932 Author: Jan Hauffa Date: 2011-04-17 18:25:00 +0200 Remove histogram stretching for negative images. commit 4962b35abd7426b7b51a00d907a538a2a38dc859 Author: Jan Hauffa Date: 2011-04-17 15:56:40 +0200 Use prefix 'p' instead of 'lp', fix other style issues. commit 9e2aabc97690baae4dd5a7d79d5e7d18df2cf697 Author: Jan Hauffa Date: 2011-04-17 15:27:14 +0200 If g_isCanceled is true, the thread has already been canceled. commit a75bd3e8f789435ac5d86ef100dd04e8bd155f76 Author: Jan Hauffa Date: 2011-04-17 15:18:58 +0200 Improve error handling in mustek_usb2_high.c. commit 1d0f3ca42ee9d84bd4afc2f379e46bc6a376de96 Author: Jan Hauffa Date: 2011-04-17 14:51:01 +0200 Fix remaining comments. commit 9226162537706c982405a7469e7003c4d61808e0 Author: Jan Hauffa Date: 2011-04-17 14:23:46 +0200 Normalize naming of constants, structures, and enums. commit d7ebb1553b8e28a9291493aa94003737f0c87298 Author: Jan Hauffa Date: 2011-04-17 13:48:45 +0200 Remove USB host type enum. commit 41ac627e5f4dbd84c082d1756414ef9922c294e7 Author: Jan Hauffa Date: 2011-04-17 13:45:42 +0200 Get rid of unnecessary forward declarations. commit da64a1fc1efd8a2e8b8245d6101deb103eea1dc4 Author: Jan Hauffa Date: 2011-04-16 21:37:20 +0200 First hack to build mustek_usb2* source files separately. commit 3479acf538335e1290ca00a5e5fef3097a835579 Author: Jan Hauffa Date: 2011-04-16 20:24:26 +0200 Merge mustek_usb2_reflective/transparent.c into mustek_usb2_high.c. commit a45931dd11e5fd270d963c5d4e1b882fa3755c90 Author: Jan Hauffa Date: 2011-04-15 20:12:28 +0200 Remove MustScanner_GetScannerState. commit c9516d53656ed5a7e45dc63b1f2202c96269a7ef Author: Jan Hauffa Date: 2011-04-13 00:22:06 +0200 Merge Reflective_Reset, Transparent_Reset, and MustScanner_Prepare into new function MustScanner_Reset. commit b2f52afa321331bf5214cdfe00d15ef479b88651 Author: Jan Hauffa Date: 2011-04-10 18:53:37 +0200 Further cleanup of mustek_usb2.c. commit ccce2d4a9a9e2762f8802af051db3f9ed2922ac8 Author: Jan Hauffa Date: 2011-04-10 18:42:05 +0200 Further cleanup of mustek_usb2.c. commit 39141a4c0265d97fe4d77084a5a63eaaafd8b8c4 Author: Jan Hauffa Date: 2011-03-30 20:57:54 +0200 Return SANE_STATUS_UNSUPPORTED unconditionally in sane_set_io_mode and sane_get_select_fd. commit d9c1df0bb84bffb23ee54be2fbd977e2a18ff4ed Author: Jan Hauffa Date: 2011-03-30 19:41:05 +0200 Remove vestigial support for custom gamma tables. commit acf84e0c9474382d78ae85120c2671b5525bd079 Author: Jan Hauffa Date: 2011-03-29 18:25:19 +0200 Clean up function StopScan. commit 9158d10df92b7d96bda66d9e601f248c5474259f Author: Jan Hauffa Date: 2011-03-19 14:55:33 +0100 Further simplification of MustScanner_Get*Line functions. commit 1f6b405451fda5fe349df1653e3423e8fc360e3b Author: Jan Hauffa Date: 2011-03-19 14:14:30 +0100 First attempt at simplifying the Get*Line functions. commit a6b15f18ccc430db08c638510bcd603d569723ee Author: Jan Hauffa Date: 2011-03-14 00:50:30 +0100 Break remaining long lines. commit 25242ff9c85f06275ca190896fe37b6d8902e122 Author: Jan Hauffa Date: 2011-03-14 00:21:44 +0100 Clean up mustek_usb2_high.c. commit c8982a99392cb7c982f12201a978fb4ae5e9e15a Author: Jan Hauffa Date: 2011-03-13 21:13:26 +0100 Do not use prefix "by" for byte variable names. commit d7b4e02780acd916ebbdd1cf8a9cc99945ea1acd Author: Jan Hauffa Date: 2011-03-13 21:07:55 +0100 Use SANE_Status and SANE_TRUE/SANE_FALSE consistently. commit 8b3c78838ae099d0099b54fbd640c0db1bb3b88c Author: Jan Hauffa Date: 2011-03-13 20:06:24 +0100 Create common helper function for Asic_CarriageHome and Asic_MotorMove, remove MotorBackHome. commit ed5dc8a7cdfb7c32faff37e7ea23446b5de9fc41 Author: Jan Hauffa Date: 2011-03-13 19:41:40 +0100 Further refactoring of Asic_SetWindow. commit 82a40d5dca84990f41c15998ae89425eaa576ff6 Author: Jan Hauffa Date: 2011-03-13 19:24:17 +0100 Refactoring of Asic_SetWindow. commit 93a79af5d3322fd06369c59a7e72f6773f9bed52 Author: Jan Hauffa Date: 2011-03-13 15:54:02 +0100 Fix indentation. commit b43f0688dbb309b7841e89074aab00d4aa61c3a2 Author: Jan Hauffa Date: 2011-03-13 15:15:47 +0100 Use symbolic constants in more places. commit 3c87b646b5831b24e6d9d13f26f2a08ec83b6cff Author: Jan Hauffa Date: 2011-03-13 14:48:40 +0100 First attempt at merging Asic_SetCalibrate into Asic_SetWindow. commit 033ebe6d9b6db266fb8146a4ac17d82043b5636a Author: Jan Hauffa Date: 2011-03-12 19:25:59 +0100 Made error handling in mustek_usb2_asic.c more consistent. commit 96c6ee1dae3dffe2eae9cf3466a0d246d9e3317d Author: Jan Hauffa Date: 2011-03-12 16:20:36 +0100 Cleaned up firmware state handling. commit c1f22ef9a9a3be1db26fc03721156a72156870b5 Author: Jan Hauffa Date: 2011-03-12 14:18:48 +0100 Clean up Asic_SetCalibrate, make Asic_SetCalibrate and Asic_SetWindow more similar. commit 3cd05daccad6bb1e50cc4030c0b471ed00897367 Author: Jan Hauffa Date: 2011-03-12 02:13:34 +0100 Refactoring the smaller high level ASIC functions. commit de5b647c4a47938f186b4661615dfbbd3315636d Author: Jan Hauffa Date: 2011-03-11 23:48:21 +0100 Refactored medium level ASIC functions. commit 693da258ce3bbcaf5c8ea31b9a08b77af615c0f2 Author: Jan Hauffa Date: 2011-03-11 18:36:07 +0100 Move some definitions from mustek_usb2_asic.h to more appropriate headers. commit 2776b336d15b0e8094271c69dfda7055cd1d896b Author: Jan Hauffa Date: 2011-03-11 18:17:54 +0100 Simplify LLFSetMotorCurrentAndPhase. commit dea59ee824157d33c64df719869ee937749f5428 Author: Jan Hauffa Date: 2011-03-11 17:44:11 +0100 Refactored all low-level motor functions except LLFSetMotorCurrentAndPhase. commit e47fee8dcc88ef4fc3e68ed65def29aacb56d17d Author: Jan Hauffa Date: 2011-03-11 16:27:03 +0100 Simplified computation of motor current, removed a redundant register assignment. commit 5688a78ace501c9a0ca36cee5f67a5929d652e81 Author: Jan Hauffa Date: 2011-03-11 16:08:35 +0100 Simplify low-level ASIC functions, add missing error checks. commit 1d8a008856d049df910b1d68599a622986ce85c4 Author: Jan Hauffa Date: 2011-03-11 14:25:09 +0100 Simplify SetRWSize, use symbolic constants for registers whenever possible. commit 794b15ff9e1daf87b80898aade0dc401e1f597d8 Author: Jan Hauffa Date: 2011-03-11 13:38:40 +0100 Simplyfied Asic_SetShadingTable. commit 6d5a5123ea3c41ada76e984670b988f847de2145 Author: Jan Hauffa Date: 2011-03-11 13:12:31 +0100 Remove whitespace at the end of debug output lines. commit d418094b64ad7ae2558860568c3e1e28a5a6cbc7 Author: Jan Hauffa Date: 2011-03-11 13:08:03 +0100 Some motor table related fixes. commit 3d1b409a1b526025c52c314f143cedd4d2d5d05f Author: Jan Hauffa Date: 2011-03-11 02:28:14 +0100 Fix a few comments. commit 88cac5b4ade1ad4ace0da5be843c879e9256c71c Author: Jan Hauffa Date: 2011-03-11 02:25:43 +0100 Begin simplifying LLFSetMotorCurrentAndPhase, ensure all fields of LLF_MOTOR_CURRENT_AND_PHASE are initialized. commit fb55a9260404049e00f13e627d7e3c8e06b397b8 Author: Jan Hauffa Date: 2011-03-11 02:08:46 +0100 Remove even more unused code from mustek_usb2_asic.c. commit 77ea9b01c374833e061ecee271d81a37f38dad77 Author: Jan Hauffa Date: 2011-03-11 00:07:43 +0100 Improved readability of functions in mustek_usb2_high.c. commit ca1e0db16f6aeb5c92e2ddf08700981f6799fc4e Author: Jan Hauffa Date: 2011-03-10 01:39:01 +0100 Remove unused code and fix indentation in mustek_usb2_high.c. commit 3f7cf8a65f48447fbc70808229ef6d2f50c3e412 Author: Jan Hauffa Date: 2011-03-09 01:56:04 +0100 Remove DISABLE bit flag constants. commit 073fe96611477986de970ab0c444f73024ecea33 Author: Jan Hauffa Date: 2011-03-09 01:26:09 +0100 LLF_MOTOR_CURRENT_AND_PHASE: only first byte of arrays MotorCurrentTableA/B is ever used commit 4b242a71394221a2155379b8e89340610a8ed725 Author: Jan Hauffa Date: 2011-03-09 01:18:10 +0100 Remove unused parameter isOrderInvert from MustScanner_GetMono*Line. commit 78863d13deacf657ef15b6078d0926fadbd9991a Author: Jan Hauffa Date: 2011-03-09 01:05:41 +0100 Remove redundant typecasts. commit cf6c88bfd1debd288ca563b54120e8ff3903f634 Author: Jan Hauffa Date: 2011-03-09 00:41:21 +0100 Try to reduce differences between mustek_usb2_reflective.c and mustek_usb2_transparent.c, part 1. commit 972fc54793d767a6cd18c722ae36feb408f45f9f Author: Jan Hauffa Date: 2011-03-09 00:02:06 +0100 Move some code that was duplicated in mustek_usb2_transparent.c and mustek_usb2_reflective.c to mustek_usb2_high.c. commit db37c13bb337c153ddd83902b8c9ad0b4a445c2a Author: Jan Hauffa Date: 2011-03-08 23:10:57 +0100 Remove information-less comments, break long lines, fix indentation. commit fa8101e05e0432a385e782acd663ce56e0c00367 Author: Jan Hauffa Date: 2011-03-08 18:37:55 +0100 Remove unused enum FS_NULL. commit ccde6b063555892ae55ac6c7bfeb8a7d44e02a4e Author: Jan Hauffa Date: 2011-03-08 18:29:13 +0100 Remove unused fields of struct GETPARAMETERS. commit 2be2be76aec39dbef8bbe0501eabaaf0838b3cbc Author: Jan Hauffa Date: 2011-03-08 18:26:32 +0100 Unified style of struct and enum definitions. commit ce6d7010d3fc0bfc5a54cd3411660db43e382bac Author: Jan Hauffa Date: 2011-03-08 18:15:30 +0100 Pixel flavor is never PF_WhiteIs0, so the associated code can be removed. commit 76a23c3bc4c80f91f223f29ae8cc5c91a1a8e4f8 Author: Jan Hauffa Date: 2011-03-06 20:31:48 +0100 Remove dead "auto level" code, sanitize key handling. commit e7c44fe6799e5c1d4bce272b539927337b7a2aca Author: Jan Hauffa Date: 2011-03-06 20:19:27 +0100 Cleanup of Asic_SetWindow commit 16485d47da8b2d0a829e378afa1443c1ef4839f8 Author: Jan Hauffa Date: 2011-03-06 19:37:25 +0100 Optimize motor table calculation. commit 8821c2882f51d309a7832b362407c46ff11ccd4a Author: Jan Hauffa Date: 2011-03-06 19:16:16 +0100 First attempt at cleaning up mustek_usb2_asic.c. commit 30087b3f43f4e6f2dd83373145dbe0a533b27419 Author: Jan Hauffa Date: 2011-03-06 17:06:14 +0100 Avoid some unorthodox language constructs. commit 030c7fa122dccca2f47c949ca46a5972efca20b8 Author: Jan Hauffa Date: 2011-03-06 16:55:39 +0100 Remove redundant code from mustek_usb2_high.c. commit a554d61c17206e6b1585bacdfd41fe4aacea1a95 Author: Jan Hauffa Date: 2011-03-06 16:18:18 +0100 Remove redundant code from mustek_usb2_asic.c, add a missing error check. commit a49bff1a60386c88168d9aa8a94cdc0ccecac7fd Author: Jan Hauffa Date: 2011-03-06 15:39:23 +0100 Simplify DRAM test. commit bc9252c09464b7ed460bf726dcca3d67f97cd74d Author: Jan Hauffa Date: 2011-03-06 15:27:19 +0100 Remove bogus automatic warmup and power saving functions. commit 0f63d16121ea84f99f75331582c3bdd0cd0d4e1f Author: Jan Hauffa Date: 2011-03-06 15:13:46 +0100 Remove redundant code from mustek_usb2.c. commit 694a69dcab0dddf2ab12eebba49c66c7ac20fdad Author: Jan Hauffa Date: 2011-03-06 14:48:40 +0100 Remove unused definitions from mustek_usb2.h. commit 21be236e840be4f8ca4bead69844f7cd691a8353 Author: Jan Hauffa Date: 2011-03-06 13:57:10 +0100 Remove unused definitions from mustek_usb2_asic.h. commit 2404326ddeaf7552e094062b1ed57521e9a21cb4 Author: Jan Hauffa Date: 2011-03-06 02:41:42 +0100 Remove unused definitions from mustek_usb2_high.h and ensure that the remaining ones are used consistently. commit d1851a6512a113d1dc4f20beb86a8be29310b627 Author: Jan Hauffa Date: 2011-03-06 01:50:53 +0100 Remove some particularly pointless comments, reduce size of block comments. commit 047631050c262142a0f52ed4e1ecc053b03869ad Author: Jan Hauffa Date: 2011-03-06 01:15:12 +0100 Remove pointless indirection. commit dedf0ac30d341b9bcbab7fb32050ff953cc73eff Author: Jan Hauffa Date: 2011-03-06 01:03:14 +0100 Remove unused global variable g_pDeviceFile and associated logic. commit 3b73e1e8a3b0085ad5db38837f0bf44f5a5b8919 Author: m. allan noah Date: 2015-11-08 09:24:37 -0500 fujitsu backend v128 do not ask fi-4340 for serial number commit 72ecee97ce9db13f148aedf52eb46911212d58e4 Author: Thomas Klausner Date: 2015-10-27 20:06:00 +0900 Add DragonFly BSD support (fixes 315205) commit a4cc05f677f110c9361f3274fc5d15087a2f8906 Author: Thomas Klausner Date: 2015-10-27 20:09:00 +0900 Add DragonFly BSD support to sane-find-scanner (fixes 315206) commit 1baab222e418275ec6fd14afad91b64d0110fdbd Author: Olaf Meeuwissen Date: 2015-11-08 18:49:58 +0900 Add missing include (fixes 315207) Both Linux and NetBSD mention this header for use of setsocketopt(). DragonFly BSD needs it for the SOL_SOCKET symbol. commit b0a99cb48938cae5a88c5f37a9a91b60589bad4e Author: Olaf Meeuwissen Date: 2015-11-08 18:36:57 +0900 Fix non-portable endian.h include issue (315209) commit d7516a11ebd6a8d96380c5ee256b171be1cb5e35 Author: Olaf Meeuwissen Date: 2015-11-08 18:16:17 +0900 Fix sane-desc testsuite logic This fixes test failures for release tarballs (pointed out by Mike Frysinger), clamps down on wildcards and prints a failure message when a test does not succeed. commit 358cbd7f319c64055cd76fedf62a92500b0c5cf5 Author: m. allan noah Date: 2015-11-06 21:39:35 -0500 canon_dr v53 continued reorder geometry group options use bg_color to fill missing image data commit 398610336b6aadf1a0c37390a0a5e2bd926bdfdf Author: m. allan noah Date: 2015-11-06 12:19:20 -0500 canon_dr backend v53 add swskip option commit b4bc0eb518ce74ffbfdbe0ce1a14bb404804cd57 Author: m. allan noah Date: 2015-11-06 11:46:17 -0500 canon_dr backend v53 replace image processing methods with sanei_magic commit 666c9a74ff26fd530b40db28f03b447209cf419e Author: m. allan noah Date: 2015-11-05 21:42:29 -0500 canon_dr backend v52 improve dropout option handling add software dropout implementation for downsampled modes commit 9dc79245291f6d4312c5656fff4232b07f70b673 Author: m. allan noah Date: 2015-11-04 13:49:02 -0500 canon_dr backend v52 add must_downsample and must_fully_buffer commit 01063a769d5431ca83069a9cf240894f893d01f0 Author: m. allan noah Date: 2015-11-04 13:05:05 -0500 canon_dr backend v52 set can_color=1 by default (recent models don't have 'C' in name) enable jpeg for DR-6080 commit 0b822359b3e1798294e30a7ea9cfd6fd9a1e3e85 Author: m. allan noah Date: 2015-11-04 13:00:10 -0500 cardscan backend v3 add USB IDs for newer model 800c commit 33495ef9b42a783c8f5f2c056ac5699481d3cc7e Author: Luiz Angelo Daros de Luca Date: 2015-10-15 12:35:00 +0900 Add missing includes commit 4f803bff0872460433dae1ac2a2954ad1016b678 Author: Olaf Meeuwissen Date: 2015-10-22 22:23:47 +0900 Escape [] for configure help strings commit 471453d2b825a569d467d60160b0eaaf6746bf67 Author: Olaf Meeuwissen Date: 2015-10-19 20:51:04 +0900 Drop unused INCLUDES substitution variable This addresses the following autoreconf output: configure.ac:85: warning: 'INCLUDES' is the old name for 'AM_CPPFLAGS' (or '*_CPPFLAGS') The INCLUDES variable is not referenced anywhere in configure.ac or configure. In addition, none of the Makefile.am files use it. commit 32c25b8b8235762d9feb5dbd0e81496a56e15569 Author: Olaf Meeuwissen Date: 2015-10-19 21:11:53 +0900 Fix duplicate automake file variable initialization issue This was introduced in 74c00494. commit f8d35b8d2fdcacc9b664594d141102c2c80ac076 Author: m. allan noah Date: 2015-10-26 08:34:17 -0400 Update canon_dr.desc We had a few scanners that were reported by email or in tracker tickets, which had not been updated in the desc file. commit 716340e7320e7f946329ad7a3cd911d676c92f6d Author: Olaf Meeuwissen Date: 2015-10-17 13:55:17 +0900 Include sys/types.h for u_long. Fixes compile on OS X commit 03d90ac2edf287bb0358044d7abdd9f6008dffc5 Author: Alessandro Zummo Date: 2015-10-14 21:55:31 +0200 epsonds: do not enable double feed detection by default commit a90d7c91b36b06bd002af890863c1d608dcdd7d4 Author: Rolf Bensch Date: 2015-10-14 19:05:03 +0200 changelog for recent work commit 4a83d9c8a4f4c396f5261d62bf7d2f9b8684eba9 Author: Rolf Bensch Date: 2015-10-14 19:04:27 +0200 Pixma backend version 0.17.24 commit 3095ab46c45e004163f5fb36efa00ce378770a9e Author: Rolf Bensch Date: 2015-10-14 19:02:43 +0200 new scanners Canon PIXUS MP5/SmartBase MPC190/imageCLASS MPC190 and Canon MP10/SmartBase MPC200/imageCLASS MPC200 commit 06b865cfd24f1e6c71506684d73ef79b43edc3cf Author: Olaf Meeuwissen Date: 2015-10-14 20:39:39 +0900 Prevent possible buffer overflows [-Wstrncat-size]. Fixes 315198 commit 18e4c4a08622e2ee4536dcb423d4548a4bc7a7e3 Author: Olaf Meeuwissen Date: 2015-10-13 23:32:40 +0900 Revert "Prevent use of uninitialized variable" This change introduces a variable that shadows the file scope one. This reverts commit 45e66aee952dcdada88293901580a111262fc1e9. commit 36876a83663832cf89fa945737d004ce9b46fb01 Author: Olaf Meeuwissen Date: 2015-10-12 20:55:19 +0900 saned: minor improvement of help message wording commit 32986192bd9ce14276e1ae144d71d42461e591b9 Author: Olaf Meeuwissen Date: 2015-10-12 20:42:50 +0900 fix: declaration-after-statement warning commit 42aa01e4e558e0019df00702579c928f9fd126e6 Author: Olaf Meeuwissen Date: 2015-10-12 20:42:01 +0900 fix: missing-field-initializers warning commit fa001c2193045844dd3318f9538b309e11089c9c Author: Olaf Meeuwissen Date: 2015-10-12 20:49:07 +0900 Follow getopt_long usage in scanimage.c This is on the off chance that getopt_long is not in the system's standard library. commit 45e66aee952dcdada88293901580a111262fc1e9 Author: Olaf Meeuwissen Date: 2015-10-12 20:47:43 +0900 Prevent use of uninitialized variable commit eab8fd457d2b8f38f9425f87258cf4fe42dac15c Author: Matteo Croce Date: 2015-10-11 19:50:04 +0200 saned: add '-b' option to bind to a specific address commit fed2a2cf60511d87e413148f1b9063f9ea4e6917 Author: Matteo Croce Date: 2015-10-11 19:50:03 +0200 saned: use getopt_long() for option parsing The help message has been adjusted to match getopt_long conventions. commit 418d1ecea7571d10acd08df2edc7967a91e14b0d Author: Matteo Croce Date: 2015-09-20 19:55:37 +0200 saned: move help message to usage() function commit a79cd0abe7daed1a749c8ad2cd7e2a89cc6153e4 Author: Matteo Croce Date: 2015-09-20 19:55:36 +0200 saned: parse inetd args in main() move argument parsing logic from run_inetd() to main() commit 2c3cb206ce55a7ac9ff9b479de452a332ba2aa1a Author: Matteo Croce Date: 2015-09-20 19:55:35 +0200 saned: parse standalone args in main() move argument parsing logic from run_standalone() to main() commit 2239d2aaff16964a5d67293f5e4220d235e2c84d Author: Alexander Hofmann Date: 2015-10-06 17:42:05 +0200 Replace obsolete interface with standard include files commit 56e69f0998005db432b4f7dd38693a870f663520 Author: m. allan noah Date: 2015-10-05 08:21:39 -0400 added kvs1025.conf.in and utsushi.desc commit 69a9a3116eb18f8d0c618217e033a1c134215954 Author: m. allan noah Date: 2015-10-04 21:25:08 -0400 minor release doc update commit 5e1d45cad7d7933b7120646ae5174ccdc8a9b49f Author: m. allan noah Date: 2015-10-04 21:13:39 -0400 reopen sane-backends 1.0.26git backends-1.3.0/ChangeLogs/ChangeLog-1.0.28000066400000000000000000004403311456256263500176200ustar00rootroot00000000000000commit 5aa523289f82d1c7f01dde601356214920646542 Author: Olaf Meeuwissen Date: 2019-07-31 20:39:43 +0900 NEWS: Document changes for upcoming release commit 7799a072b4ee31f7848bac7b52b916dd8d627817 Author: Olaf Meeuwissen Date: 2019-07-30 22:40:11 +0900 CI: Add a scripted release process commit a8d4d4b7786e0cca227ab716f9d865bc9c288b25 Author: Rolf Bensch Date: 2019-07-29 22:34:13 +0200 pixma: Canon PIXMA TS8200 Series is working https://alioth-lists.debian.net/pipermail/sane-devel/2019-July/036877.html commit 9dce0eaf74facef88fae83ba4d7f9fe886307d4c Merge: 05bdcbd81a5a a4e122add55e Author: Rolf Bensch Date: 2019-07-29 17:27:07 +0200 Merge branch 'update_de_translation' commit a4e122add55e4b315d96183ced77acc76ab74737 Author: Rolf Bensch Date: 2019-07-28 23:04:23 +0200 update German translation commit ef11a785404c9f9bb663b2cc3d14d956c7fec4d8 Author: Rolf Bensch Date: 2019-07-27 13:11:28 +0200 1st bunch of updated German translation commit 05bdcbd81a5a92da12e1d4e1559d3f7faf7782ac Merge: 8eb5850f2749 d4164a7320e3 Author: Olaf Meeuwissen Date: 2019-07-27 06:49:23 +0000 Merge branch 'en_GB_translation' into 'master' Updated en_GB translations See merge request sane-project/backends!89 commit d4164a7320e3b73547ae624817e974e1fb2086e3 Author: Ralph Little Date: 2019-07-26 23:20:29 -0700 Fix trailing whitespace and update translator commit 78359453e1c21af2055559be99040d979aececdb Author: Ralph Little Date: 2019-07-26 10:05:41 -0700 Updated en_GB translations commit 8eb5850f2749ba8b8c7e1d8e4d7aed18d3adf67a Author: Olaf Meeuwissen Date: 2019-07-26 13:55:23 +0900 po/nl.po: Update Dutch translations [skip ci] commit 3a0f0df8fb0bf63c2980b80950b4314b93830ba7 Author: Olaf Meeuwissen Date: 2019-07-23 21:15:24 +0900 po/*.po: Sync with latest source commit 97f04e16e462f61cb87a0acc19868c3357c259eb Merge: 8ae2569a879c a8a5f58f2155 Author: Povilas Kanapickas Date: 2019-07-22 23:13:51 +0000 Merge branch 'genesys-fix-calibration' into 'master' genesys: Fix crash when deserializing calibration files Closes #97 See merge request sane-project/backends!88 commit a8a5f58f21554af7c4c945fa07ed33fa03fc0421 Author: Povilas Kanapickas Date: 2019-07-23 02:00:44 +0300 genesys: Add tests for serialization of calibration data commit 25ed10029cea095e5e757f12b5dccf46eb4ca973 Author: Povilas Kanapickas Date: 2019-07-23 02:00:43 +0300 genesys: Make set_calibration_value() more robust commit b9fb97e0f77efba9059834096845a1d1a49d20e6 Author: Povilas Kanapickas Date: 2019-07-23 02:00:42 +0300 genesys: Make serialization of calibration data less error prone commit 8ae2569a879c39f520120f33bda94991b1dd10ab Author: Olaf Meeuwissen Date: 2019-07-17 18:26:59 +0900 INSTALL.linux: Document need to run autogen.sh for clean checkouts Fixes #102. commit cb8171734110fd3f42a24b57be735163f903076b Author: Olaf Meeuwissen Date: 2019-07-14 17:09:33 +0900 .gitignore: more build artifacts commit dc328a8d5fdf3daf4aa7deaecb46bd69797a99c6 Author: Olaf Meeuwissen Date: 2019-07-14 17:08:08 +0900 ChangeLog: Fix awkward grammar commit c52eef6e215cb9f3bfab19a64dbcd96d39aaca25 Author: Olaf Meeuwissen Date: 2019-07-14 14:03:56 +0900 CI: Drop Debian 8, add Debian 10 This uses the updated Debian images which now use codenames instead of version numbers. Note that `CFLAGS=-Werror` has been disabled until such time as all new warnings have been addressed. commit 3fb0f020154eecb95a81e0ab32a441709ead361f Author: Olaf Meeuwissen Date: 2019-07-10 22:58:40 +0900 utsushi.desc: Sync with upstream commit 7b2d2cd0b3b6a2052635a35bdf4d0eeab10a3b5d Merge: 6f6a4928741c 4742f213185c Author: Olaf Meeuwissen Date: 2019-07-07 02:16:49 +0000 Merge branch 'fix-warnings-memset-size' into 'master' Fix invocations of memset with incorrect size See merge request sane-project/backends!87 commit 4742f213185c9c18f5a194a133adc2752b97660d Author: Povilas Kanapickas Date: 2019-07-06 22:14:11 +0300 Fix invocations of memset with incorrect size commit 6f6a4928741ce99dd6c4d5fe4eed5357d11e9964 Author: Olaf Meeuwissen Date: 2019-07-03 22:19:52 +0900 Raise the bar for archiving of source tarballs They now have to pass a `make distcheck`. The compile stage takes care of compiler errors/warnings. We are interested in all checks passing and the source archive being self-consistent. commit 14e00fa360839fbb0222c742e8f5f31c530998a4 Author: Olaf Meeuwissen Date: 2019-06-30 21:58:57 +0900 Drop Linux Software Map file; it has outlived its purpose Source and binary packages are readily available from most major Linux distributions. commit 02b0a7e4b6a1ba02b566d96f5aa477e32a8efdb0 Author: Olaf Meeuwissen Date: 2019-06-30 17:48:34 +0900 m4: Move byteorder.m4 back to m4/ directory There is little value in putting this in its own directory just because it is a non-generated file. commit 2f14ac2fcc6124c7170fdc2bbb868813c5bdfeb7 Merge: 72d68c7367e4 640fa9dbbaaa Author: Povilas Kanapickas Date: 2019-07-05 23:39:04 +0000 Merge branch 'genesys-error-handling' into 'master' genesys: Improve error handling See merge request sane-project/backends!86 commit 640fa9dbbaaab7f03b7aff0e9d6abcc17ce4689e Author: Povilas Kanapickas Date: 2019-07-06 02:10:08 +0300 genesys: Always initialize status variable commit 0454f42c36422c1d7b3c3595be0a10bc13da4be1 Author: Povilas Kanapickas Date: 2019-07-06 02:10:07 +0300 genesys: Report USB errors via exceptions to reduce code duplication commit b9cd547aea749ee1064fec6a8a3bafba88761efa Author: Povilas Kanapickas Date: 2019-07-06 02:10:06 +0300 genesys: Support printf-like messages in debug helper commit 22b7fb5105e8e32936e540c8f4276d167ee9508d Author: Povilas Kanapickas Date: 2019-07-06 02:10:05 +0300 genesys: Move more error functionality to genesys_error.{h,cc} commit 942f3fbbf7083d77f5af96455f523c577148f4d7 Author: Povilas Kanapickas Date: 2019-07-06 02:10:04 +0300 genesys: Move error-related functions to separate header commit 9d07e2108a5fdc45136cb73be7617ba80d448431 Author: Povilas Kanapickas Date: 2019-07-06 02:10:03 +0300 genesys: Support message argument to exceptions commit 72d68c7367e4713a340ea1ab109360ce9788266d Merge: ec8017a8c023 14bbe802dbdd Author: Povilas Kanapickas Date: 2019-06-30 23:16:47 +0000 Merge branch 'genesys-canoscan-8600f' into 'master' genesys: Preparation for infrared channel support on 8600F (part 2) See merge request sane-project/backends!84 commit 14bbe802dbdd748d439bb54c62800ad73141880f Author: Povilas Kanapickas Date: 2019-06-30 14:05:29 +0300 genesys: Move move_to_ta() calls out of *_init_regs_for_*() commit a92187edadb28c071c092ce17ffdd13759723d91 Author: Povilas Kanapickas Date: 2019-06-30 14:05:28 +0300 genesys: Move moving to home out of *_init_regs_for_scan() commit 442ffd048663994bb4b7a1a884e6abd49f65709e Author: Povilas Kanapickas Date: 2019-06-30 14:05:27 +0300 genesys: Move waiting for motor stop out of *_init_regs_for_scan() commit bba75702b6d8b136562bc6d13c466d5d8232716a Author: Povilas Kanapickas Date: 2019-06-30 14:05:26 +0300 genesys: Add utility to print debug messages upon function exit commit 397994b215605814713f008a474f8ff12607bd07 Author: Povilas Kanapickas Date: 2019-06-30 14:05:25 +0300 genesys: Make exposure configuration more consistent commit 1e7da8638e34168ad2ba656e7be51ac7c088c50c Author: Povilas Kanapickas Date: 2019-06-30 14:05:24 +0300 genesys: Use a more descriptive filename for coarse gain debug images commit 6d11f6df25a5fc764781c097a592366d33c01d9c Author: Povilas Kanapickas Date: 2019-06-30 14:05:23 +0300 genesys: Fix support for large exposures on GL843 commit f41440ef9fd7ffc377939725e9454ef931860909 Author: Povilas Kanapickas Date: 2019-06-30 14:05:22 +0300 genesys: Extract debug_dump for Genesys_Current_Setup commit fe8383543dbf3d8abb3addbdc3d144213954390a Author: Povilas Kanapickas Date: 2019-06-30 14:05:21 +0300 genesys: Remove code for G4050 CCD that's unused and likely incorrect commit 7bd68b5225f4feeccd61c54a11441087846dc82f Author: Povilas Kanapickas Date: 2019-06-30 14:05:20 +0300 genesys: Fix crash in sane_open_impl due to uninitialized variable commit 616c86de2fa70d852ebed3659b3c224127fdbf93 Author: Povilas Kanapickas Date: 2019-06-30 14:05:19 +0300 genesys: Fix incorrect use of TGTIME for pixel coordinates commit 89d06c9a3aa3f6013ee9fc94e2d30e61a2484d6c Author: Povilas Kanapickas Date: 2019-06-30 14:05:18 +0300 genesys: Fix error handling in the presence of exceptions commit 3050f50b256dd7f902fd02549959a168772113c5 Author: Povilas Kanapickas Date: 2019-06-30 14:05:17 +0300 genesys: Add a macro to convert status return to exception commit 1685e6e8636dbfe77e01f865c0ab50e8ccc51feb Author: Povilas Kanapickas Date: 2019-06-30 14:05:16 +0300 genesys: Return bool out of *_is_compatible_calibration() commit eaa4cb77664122251a9085a7fd974c323359c641 Author: Povilas Kanapickas Date: 2019-06-30 14:05:15 +0300 genesys: Return void out of *_calculate_current_setup() commit ec8017a8c0238ba319b87d94fa7214cd2cb92c86 Author: Alex Belkin Date: 2019-06-30 18:54:46 +0300 xerox_mfp: Mark SCX-4x16 as unsupported As reported by Andrew Sotnikov in issue #95 Samsung SCX-4216F is not really supported. Change SCX-4x16 status from untested to unsupported. Closes #95. commit 7a07f47b4796f4d4c4f853b8bf76edd5c424f291 Author: Olaf Meeuwissen Date: 2019-06-30 14:53:00 +0900 Include po/README in the source tarball Makefile.in.in ignores EXTRA_DIST. commit 2ea6e8eaaa214da7a551070763ef8e4f370018db Author: Olaf Meeuwissen Date: 2019-06-30 11:52:33 +0900 po: Fix inconsistent word wrapping when using custom width This fixes test failures run during a `make distcheck` for generated message catalogs for LINGUAS such as `en@boldquot` and `en@quot`. commit f29e1c88def371a227d0dbd9de7dcb64eae863d5 Author: Olaf Meeuwissen Date: 2019-06-30 11:51:55 +0900 Make potential issues stick out when running `make distcheck` commit 9b109a7bd70892f69228d916fc19c2bbd3594f61 Author: Olaf Meeuwissen Date: 2019-06-30 11:17:59 +0900 Fix typo commit 7af0d47e2957c7c53884904656ef8b9f39b75b53 Author: Olaf Meeuwissen Date: 2019-06-30 11:15:20 +0900 Keep warning out of redirected image data. Fixes testsuite commit adc64520eedbdea6946c6a7b05ba2d9da15917a3 Author: Olaf Meeuwissen Date: 2019-06-29 17:31:25 +0900 Fix test suite, following bf00b9f49c1ed91e898e5c6f4bc93f7c483c651e commit 02d33a0e6d8712c5e1fa18ec5c549cde8bcd3792 Author: Olaf Meeuwissen Date: 2019-06-29 17:08:52 +0900 Take "advantage" of CI environment changes The mini variant now has everything needed to roll a source tarball. It also has git installed so we don't need a convoluted find for our style check anymore. commit 1017d790b61fce2944ea57f2fe4a1a767c09b505 Author: Olaf Meeuwissen Date: 2019-06-29 17:29:15 +0900 Always use the latest upstream config.{guess,sub} in source tarballs This was one of the release tasks but we may as well find out as soon as possible whether these break anything ;-) commit b596f9887998fc2f2e30ce904bb42e37e428ddfd Author: Olaf Meeuwissen Date: 2019-06-29 15:57:04 +0900 Include an up-to-date ChangeLog with every build commit 8b2fa76497f195208623298d129a2c3fd7917c1b Merge: bf00b9f49c1e 0dd1feec2d62 Author: Olaf Meeuwissen Date: 2019-06-27 20:00:38 +0900 Merge branch 'yurchor:uk_update' commit 0dd1feec2d62caddbe7e9702a213cf87c3e5b027 Author: Olaf Meeuwissen Date: 2019-06-27 20:00:05 +0900 po/uk.po: Remove trailing blank line commit d976a5124fdc300bebf67ff09b93a5cea64c2027 Author: Yuri Chornoivan Date: 2019-06-26 18:18:47 +0300 Update Ukrainian translation commit bf00b9f49c1ed91e898e5c6f4bc93f7c483c651e Author: Olaf Meeuwissen Date: 2019-06-25 21:54:37 +0900 Use git to put a version in configure CI has been using this for a long time. Now that generated files are no longer in the repository, new clones require autotools already and adding a git requirement doesn't seem like a big burden. commit 2ab39228b9120b0aba4e71269edf8fdbc5a45def Author: Olaf Meeuwissen Date: 2019-06-25 21:52:48 +0900 Ignore generated English message catalogs w/ quotation marks commit 9bbb2b37a189bc275bc11a655e832bb781b7d998 Author: Olaf Meeuwissen Date: 2019-06-20 20:02:15 +0900 Bump Alpine build from 3.9 to 3.10 commit f3577168d2bb8ed9fd7bdf53997ad511ac800870 Author: Olaf Meeuwissen Date: 2019-06-20 19:47:32 +0900 autogen.sh: Fix out-of-tree invocation The check for unsubstituted macros on configure would silently pass if configure was not created in the current working directory. commit ec1a614d6b73214a3510e9654c0f33d4a876bc3e Merge: f901462f9937 9173e5dbddf5 Author: Povilas Kanapickas Date: 2019-06-20 08:58:33 +0000 Merge branch 'genesys-canoscan-8600f' into 'master' genesys: Preparation for infrared channel support on 8600F See merge request sane-project/backends!82 commit 9173e5dbddf5f616ae2343222315310edfad2ccf Author: Povilas Kanapickas Date: 2019-06-08 14:04:30 +0300 genesys: Merge XPA lamp setup to a single function commit 4adb96b7afa8935c663279c5b5caccb60a7241b8 Author: Povilas Kanapickas Date: 2019-06-08 14:04:29 +0300 genesys: Merge XPA motor setup to a single function commit 4d4b3be12d566ed3eb3bc81b9f8db915d81ed5e8 Author: Povilas Kanapickas Date: 2019-06-08 14:04:28 +0300 genesys: Cache logical lamp and XPA state in register set commit d11971b220c8c780b043ed08c58d2cb8b527c5f4 Author: Povilas Kanapickas Date: 2019-06-08 14:04:27 +0300 genesys: Only ever use sanei_genesys_set_lamp_power() to turn on lamp commit 6d9e783a517f2481ff0a661ebc2c7f8b8918daf1 Author: Povilas Kanapickas Date: 2019-06-08 14:04:26 +0300 genesys: Only ever use sanei_genesys_set_motor_power() to turn on motor commit 5b788022dc37d5dd259e784dcc23b6354d3e6991 Author: Povilas Kanapickas Date: 2019-06-08 14:04:25 +0300 genesys: Extract sanei_genesys_set_motor_power() commit 57480021dd6853267e4af266afc9d55dac04f3e7 Author: Povilas Kanapickas Date: 2019-06-08 14:04:24 +0300 genesys: Extract sanei_genesys_set_lamp_power() commit 8d5ff56ee97969671ca3ea9e7e6cb20508b9c2df Author: Povilas Kanapickas Date: 2019-06-08 14:04:23 +0300 genesys: Use scan method out of setup params for cache comparison commit bf0ed8ed09f4aa710dfb6c5e543928a89a42c184 Author: Povilas Kanapickas Date: 2019-06-08 14:04:22 +0300 genesys: Store scan method within params struct commit be19edfd7241d00f2fc8990f50dbbb9c8a1c6fe3 Author: Povilas Kanapickas Date: 2019-06-08 14:04:21 +0300 genesys: Store setup params to current setup struct commit 9f3c86cd57a61cc841bec66849ffe255c2b96fec Author: Povilas Kanapickas Date: 2019-06-08 14:04:20 +0300 genesys: Wrap data into SetupParams in *_calculate_current_setup() commit 6796315cc10f41033d4666942e2600abb1406624 Author: Povilas Kanapickas Date: 2019-06-08 14:04:19 +0300 genesys: Remove useless condition in lineart setup check The flag in the remaining condition can only be set if the second condition is true. commit 65bb8724c67619c57ed25fcbf98c21d3f0b37eb9 Author: Povilas Kanapickas Date: 2019-06-08 14:04:18 +0300 genesys: Use scan params to compute scan geometry, not settings commit 2a8f642787b8ebd33c82727a5e2f518f1f80760a Author: Povilas Kanapickas Date: 2019-06-08 14:04:17 +0300 genesys: Fix sign comparison warnings commit bf395270e807e50a89bf98bcdf14bde1e9e58f1c Author: Povilas Kanapickas Date: 2019-06-08 14:04:16 +0300 genesys: Refactor color filter into an enum commit 0ffbd6c0238aee9f6eed676c94478bb9938da375 Author: Povilas Kanapickas Date: 2019-06-08 14:04:15 +0300 genesys: Specify underlying types of enums commit cd7186965b3418a4c4c21d1f0b6d49f07137a0b2 Author: Povilas Kanapickas Date: 2019-06-08 14:04:14 +0300 genesys: Don't use floating point numbers in SetupParams unnecessarily commit ca070fb5697124f0a92432fcb7c87b73048098fe Author: Povilas Kanapickas Date: 2019-06-08 14:04:13 +0300 genesys: Extract function to dump SetupParams to debug commit a8b61d0501fffa2eff1fcdd841c7c4fd11dba048 Author: Povilas Kanapickas Date: 2019-06-08 14:04:12 +0300 genesys: Update gl646_setup_registers() doc commit e47a6dfb56468d19bb1482258d9947aee3039458 Author: Povilas Kanapickas Date: 2019-06-08 14:04:11 +0300 genesys: Represent color as channels on GL646 commit 6ae640cac17c504c060676e099851e9514a9a896 Author: Povilas Kanapickas Date: 2019-06-08 14:04:10 +0300 genesys: Simplify channel setup in gl646_setup_registers() commit a843704cc198fcbc10d1eeb0ba537b68cf4438c4 Author: Povilas Kanapickas Date: 2019-06-08 14:04:09 +0300 genesys: Move color filter setup to gl646_setup_registers() commit 8184699bbfac3a6ded6f4dfb26a6adc6534a5c77 Author: Povilas Kanapickas Date: 2019-06-08 14:04:08 +0300 genesys: Allocate GL646 slope tables on stack commit e129bdd1e16de8556ec458dbf50a3f76a6f20481 Author: Povilas Kanapickas Date: 2019-06-08 14:04:07 +0300 genesys: Move low-level computations to gl646_setup_registers() commit fb8014d77e3de4fe0188d9ef0e554d7df1ae1838 Author: Povilas Kanapickas Date: 2019-06-08 14:04:06 +0300 genesys: Store scan setup data in SetupParams struct commit 42ae7ea2d8fef1141f7f110051ee2f94d13c555c Author: Povilas Kanapickas Date: 2019-06-08 14:04:05 +0300 genesys: Reuse SetupParams on GL847 code commit 1841e5b276c21578b2296f382861a08bf5ffb31a Author: Povilas Kanapickas Date: 2019-06-08 14:04:04 +0300 genesys: Reuse SetupParams on GL846 code commit 6719f885da49dc616a80e8bd317e6ad08bc11cfd Author: Povilas Kanapickas Date: 2019-06-08 14:04:03 +0300 genesys: Reuse SetupParams on GL841 code commit 50f5007b3d0d1f0d4713d767590c6a54e2f582b6 Author: Povilas Kanapickas Date: 2019-06-08 14:04:02 +0300 genesys: Reuse SetupParams on GL124 code commit 319a5082ac3cda6c70bca9c568fd7f24ed9fab90 Author: Povilas Kanapickas Date: 2019-06-08 14:04:01 +0300 genesys: Remove unused command set API commit d92bfd1e9e831e3044f24e7d430d03a9273ffe39 Author: Povilas Kanapickas Date: 2019-06-08 14:04:00 +0300 genesys: Remove unused fields out of Genesys_Settings struct commit efd7723a4f16c20b31fc874a1792bf3dd52cc920 Author: Povilas Kanapickas Date: 2019-06-08 14:03:59 +0300 genesys: Deduplicate dumping of Genesys_Current_Setup to logs commit 7020001ba77cec3a2d4368ba4bbd1da7bc7c9bca Author: Povilas Kanapickas Date: 2019-06-08 14:03:58 +0300 genesys: Remove unused ScanColorMode::COLOR_MULTI_PASS commit 6e657e3eac1f878d881d058ca272646afcb4542d Author: Povilas Kanapickas Date: 2019-06-08 14:03:57 +0300 genesys: Refactor scan color mode into an enum commit 723426d78bd90802199301ef7d75febb8766cc74 Author: Povilas Kanapickas Date: 2019-06-08 14:03:56 +0300 genesys: Don't use dev->calib_reg directly when possible commit c09370eab49b1c1522964d418c6e1344adc07ede Author: Povilas Kanapickas Date: 2019-06-08 14:03:55 +0300 genesys: Add support for selecting infrared channel commit a63c8a5f8c8422cd51aea00efc30fd11c68fb780 Author: Povilas Kanapickas Date: 2019-06-08 14:03:54 +0300 genesys: Support more than two scan methods commit f1e583aea8f5e6a4ef1e74071ef1f41390c447ad Author: Povilas Kanapickas Date: 2019-06-08 14:03:53 +0300 genesys: Refactor scan method into an enum commit 374a3043fafcae61a64f601a3329afc04f748060 Author: Povilas Kanapickas Date: 2019-06-08 14:03:52 +0300 genesys: Use STR_ prefix to macros yielding to strings commit 06157f753cf9a5c1114d63604b2710131cb58a6a Author: Povilas Kanapickas Date: 2019-06-08 14:03:51 +0300 genesys: Extract remaining options out of option list commit 38ccdc67881de4c5e0e7fc19467229ea8ab56bf8 Author: Povilas Kanapickas Date: 2019-06-08 14:03:50 +0300 genesys: Extract resolution and bit_depth options out of option list commit a7a2e0abd6c9d58dc9782add70384fe707b48da4 Author: Povilas Kanapickas Date: 2019-06-08 14:03:49 +0300 genesys: Extract string options out of options list commit 3301e0fbea7bb731f70d3420fdcb088400700c45 Author: Povilas Kanapickas Date: 2019-06-08 14:03:48 +0300 genesys: Extract position options out of option list commit 3f184b795f302aa63c331982113b0f0427a8928d Author: Povilas Kanapickas Date: 2019-06-08 14:03:47 +0300 genesys: Simplify sensor handling commit f901462f9937e884c4da7bfe9e96f6d12dfdccf1 Author: Olaf Meeuwissen Date: 2019-06-19 21:12:51 +0900 Fix AS_CASE clause matching See https://alioth-lists.debian.net/pipermail/sane-devel/2019-June/036809.html commit 46e98476acc831c43cc60fb82756aad19b214e5d Merge: 6d95bc191f79 8aa5318f90a0 Author: Povilas Kanapickas Date: 2019-06-16 15:40:29 +0000 Merge branch 'genesys-canoscan-8600f' into 'master' genesys: Add initial support for 2400 and 4800 dpi transparency scanning on CanoScan 8600F See merge request sane-project/backends!81 commit 8aa5318f90a084ae7fb0c27258a456723540f552 Author: Povilas Kanapickas Date: 2019-06-02 11:48:04 +0300 genesys: Make frontend register list generic commit 24853e657a3a57d18d8c10005074159ed7882547 Author: Povilas Kanapickas Date: 2019-06-02 11:48:03 +0300 genesys: Refactor frontend table to initialize data explicitly commit 63054332102eb4c4f1b8b56d442c436aa8a3f82a Author: Povilas Kanapickas Date: 2019-06-02 11:48:02 +0300 genesys: Cache the initial frontend values in the device commit ba778a7d84e555c58843d1664db65f683c1f5d4c Author: Povilas Kanapickas Date: 2019-06-02 11:48:01 +0300 genesys: Add a way to set custom FE registers depending on scan mode commit c05b0c15983b223dc43957f569def6609f7ee165 Author: Povilas Kanapickas Date: 2019-06-02 11:48:00 +0300 genesys: Add initial support for 8600F 4800dpi transparency scanning commit 91a37b65588a23eef009283d307b0650c91be34d Author: Povilas Kanapickas Date: 2019-06-02 11:47:59 +0300 genesys: Add a way to calibrate partial width of the scanner commit a2f451526c5d0b05a60f5407923e79061b5788fe Author: Povilas Kanapickas Date: 2019-06-02 11:47:58 +0300 genesys: Extract GenesysRegisterSettingSet::{fread,fwrite}() commit ac624433bbacdcf3c1cfe5cbcfd16803baae2097 Author: Povilas Kanapickas Date: 2019-06-02 11:47:57 +0300 sanei: Print the number of read bytes on bulk USB read error commit a9689de4784deb240073a964c85fbee14ea125b0 Author: Povilas Kanapickas Date: 2019-06-02 11:47:56 +0300 genesys: Don't hardcode ccd size divisor for start position commit 3a8f1ddf6c8d3f8bdf7ddeee4af5d3734cf6b390 Author: Povilas Kanapickas Date: 2019-06-02 11:47:55 +0300 genesys: Support 2400dpi transparency scans on 8600F commit 8520b810fdc7901db6e5fa5fbf773723ba077f22 Author: Povilas Kanapickas Date: 2019-06-02 11:47:54 +0300 genesys: Add support for mixed half/quarter-ccd mode on the same scanner commit 36872e57518f22da7e8588b697b3ac24ea56597d Author: Povilas Kanapickas Date: 2019-06-02 11:47:53 +0300 genesys: Add support for half-ccd and quarter-ccd modes on the same chip Previously we hardcoded half-ccd mode to be actually quarter-ccd mode on GL843. commit 8a9a4e3f719d2d7fdf3c41b1431dd9dd29d5d54f Author: Povilas Kanapickas Date: 2019-06-02 11:47:52 +0300 genesys: Move GL843 sensor profiles to global sensor database Note that gl843_setup_sensor() did not write certain registers and they weren't written to the scanner anywhere else, thus they have been excluded from the new definitions. commit 723aa2b73cf213b58310e74d4baca78ff03e4a61 Author: Povilas Kanapickas Date: 2019-06-02 11:47:51 +0300 genesys: Don't store current sensor in device as it depends on the scan commit f8e8243b78fa3a13d366bbdacdc2b13158a66c36 Author: Povilas Kanapickas Date: 2019-06-02 11:47:50 +0300 genesys: Don't modify sensor with gamma data commit dafd2a150b908b37065c5d5817e36c044ae6c675 Author: Povilas Kanapickas Date: 2019-06-02 11:47:49 +0300 genesys: Extract gamma creation into a single function commit ea74f8e6ef3fe496a75d3f7fc7165c4dd3b4adca Author: Povilas Kanapickas Date: 2019-06-02 11:47:48 +0300 genesys: Pass gamma tables as vectors commit dd2884ede7dbe619bd1b845708d4e74440289a73 Author: Povilas Kanapickas Date: 2019-06-02 11:47:47 +0300 genesys: Don't read registers to get data available from model flags commit 5adaea3e2f22c53108f062d3763ba4f34e4ec72b Author: Povilas Kanapickas Date: 2019-06-02 11:47:46 +0300 genesys: Pass sensor to genesys_coarse_calibration() as param commit 60289d58e20ded371dee4f82eae786ac4afb53c5 Author: Povilas Kanapickas Date: 2019-06-02 11:47:45 +0300 genesys: Pass sensor to genesys_average_white() as param commit 35e45196b950227057b9858d1e62614fcb6fc696 Author: Povilas Kanapickas Date: 2019-06-02 11:47:44 +0300 genesys: Pass sensor to sanei_genesys_search_reference_point() as param commit 43c86ecd819307b076aecba880d6bf55012b98bf Author: Povilas Kanapickas Date: 2019-06-02 11:47:43 +0300 genesys: Only ever name Genesys_Sensor as "sensor" to reduce confusion commit 773841251710c8933aecab8e9ff7002ffe888245 Author: Povilas Kanapickas Date: 2019-06-02 11:47:42 +0300 genesys: Remove vim indent settings commit 6fdabd817477b5d69733d18e9244fc05a7af2f8a Author: Povilas Kanapickas Date: 2019-06-02 11:47:41 +0300 genesys: Remove unused sensor config commit d3f13839af4a985308c1cdf67d2307966ca01e1c Author: Povilas Kanapickas Date: 2019-06-02 11:47:40 +0300 genesys: Remove no longer needed explicit initialization and copying commit 4269f67c4a04862627795ec17559fb8d75c00aaf Author: Povilas Kanapickas Date: 2019-06-02 11:47:39 +0300 genesys: Always initialize Genesys_Frontend commit 68df437faf1d025f6a3e16a47816ed261707f172 Author: Povilas Kanapickas Date: 2019-06-02 11:47:38 +0300 genesys: Always initialize Genesys_Motor commit 52f73ffd8d673641176379aa75879b2a7154f294 Author: Povilas Kanapickas Date: 2019-06-02 11:47:37 +0300 genesys: Always initialize Genesys_Gpo commit d39189e424dcda05c295a60e01ffff25a97f374e Author: Povilas Kanapickas Date: 2019-06-02 11:47:36 +0300 genesys: Always initialize Genesys_Settings commit 03ecbf1bb0af74f857172a2792ebfc55d40c76f8 Author: Povilas Kanapickas Date: 2019-06-02 11:47:35 +0300 genesys: Always initialize Genesys_Current_Setup commit a287f4945cb63e425b37aa9b6d0682de8570a8d8 Author: Povilas Kanapickas Date: 2019-06-02 11:47:34 +0300 genesys: Extract computation of session setup to callers commit d688429ccd3558fb6eb5948efce9e812d7a23776 Author: Povilas Kanapickas Date: 2019-06-02 11:47:33 +0300 genesys: Fix use of wrong dpi when computing output pixel count commit 1a9e05b2918b831c4e3afa5d05ee32c61793ddc6 Author: Povilas Kanapickas Date: 2019-06-02 11:47:32 +0300 genesys: Extract params to gl843_init_scan_regs to a struct commit 836a78bef5713d6e7ade520e23c4959ef3a31386 Author: Povilas Kanapickas Date: 2019-06-02 11:47:31 +0300 genesys: Extract exposure to separate variable in sensor definition commit e9419d5e33c0bda22e7f358e9d1ef6a33127a699 Author: Povilas Kanapickas Date: 2019-06-02 11:47:30 +0300 genesys: Make per-sensor register override list generic commit 7f22e35e2c6ec20a552e588decc000996385a3df Author: Povilas Kanapickas Date: 2019-06-02 11:47:29 +0300 genesys: Use internal sleep wrapper for remaining sleep calls commit 90814ac7d3c30bdc1a4c4b19d8822e908aedefdc Author: Povilas Kanapickas Date: 2019-06-02 11:47:28 +0300 genesys: Move half CCD property to sensor definition commit 7359412548cfc662b913a79ee014aa8e30fb19c1 Author: Povilas Kanapickas Date: 2019-06-02 11:47:27 +0300 genesys: Remove dead code commit 04c5b4afde1a88454df1682e00dcb8a1b360eb49 Author: Povilas Kanapickas Date: 2019-06-02 11:47:26 +0300 genesys: Improve type safety of Genesys_Register_Set commit 6d95bc191f796eef07da3f5334d840e8b31bf4cf Author: Olaf Meeuwissen Date: 2019-06-16 21:40:39 +0900 Drop unneeded C++ compiler flag additions to AM_CXXFLAGS. Re #92 The AX_CXX_COMPILE_STDCXX_11 macro takes care of doing so if needed already. commit a4862d8526ca238fb667658a4bcd83de27915cb8 Author: Olaf Meeuwissen Date: 2019-06-16 21:39:45 +0900 Predicate genesys compilation on documented variable. Re #92 commit 424bb7d923965327a28e303d1c424d5fc00e977c Author: Rolf Bensch Date: 2019-06-16 13:13:48 +0200 INSTALL.linux: add missing development environment packages commit 4c05fb474282c64c8e700ab9852b465eda32617e Merge: cadf5a9b1a9e f111032e7f05 Author: Olaf Meeuwissen Date: 2019-06-15 06:50:27 +0000 Merge branch 'backend/as6e' into 'master' as6e: Avoid out of bound access See merge request sane-project/backends!31 commit f111032e7f05e735422d02a88b1d04cb7b5da1c2 Author: Martin Güthle Date: 2018-10-25 08:53:58 +0200 as6e: Avoid out of bound access This fixes a crash due to a stack corruption. To reproduce the bug, set a path within the PATH variable, to something, which exceeds 128 chars. Maybe more chars are needed, to reach the stack corruption. commit cadf5a9b1a9e62829fe630b474d91c5a94c4e68a Author: Olaf Meeuwissen Date: 2019-06-15 14:45:36 +0900 Prevent segfault in case strndup cannot allocate memory This complements 18f9e5598c224e90554d333b7f9f05ba8fa14ad0 commit f9eb32317abf2f109fe21478358e637750df7f32 Merge: d22516c76ac5 0b5ab0b5e47f Author: Olaf Meeuwissen Date: 2019-06-15 05:43:06 +0000 Merge branch 'sanei-usb-testing-mode-prep' into 'master' sanei: Preparation to support capture and replay of USB data for testing See merge request sane-project/backends!74 commit 0b5ab0b5e47f74ddf7e6a609bf91c3f4b36c0369 Author: Povilas Kanapickas Date: 2019-04-27 12:16:10 +0300 sanei_usb: Fall through to the end of sanei_usb_get_descriptor() commit 422e0831757681e1ff3cd6d6c93b80c38a8d1e67 Author: Povilas Kanapickas Date: 2019-04-27 12:16:09 +0300 sanei_usb: Fall through the end in sanei_usb_control_msg() commit d865705ef13a5d9f261abf1ed39b02319d414004 Author: Povilas Kanapickas Date: 2019-04-27 12:16:08 +0300 sanei_usb: Don't change input size variable in USBCALLS bulk code paths commit 18f9e5598c224e90554d333b7f9f05ba8fa14ad0 Author: Povilas Kanapickas Date: 2019-04-27 12:16:07 +0300 dll: Don't unnecessarily use alloca commit d22516c76ac52c3b5208b62fc82312b223efc7ad Merge: 767cbfaa7f8d 9c42d6ac11fc Author: Olaf Meeuwissen Date: 2019-06-12 10:48:14 +0000 Merge branch 'remove-autoconf-generated-files' into 'master' Remove autoconf generated files See merge request sane-project/backends!72 commit 9c42d6ac11fc0ef71d4712607381d19636829d9c Author: Olaf Meeuwissen Date: 2019-06-08 13:00:25 +0900 Check AX_CXX_COMPILE_STDCXX macro expansion in configure If not expanded, the user is informed how to deal with this. commit c0b07792d948e46ef9cde8910a5b78f5f9e958fc Author: Olaf Meeuwissen Date: 2019-06-08 12:37:49 +0900 Ignore harmless patch backups When patching `ltmain.sh` the `patch` utility creates this file if the patch did not apply cleanly, e.g. when it applied with an offset. Any hunks that *failed* to apply end up in `ltmain.sh.rej`. This file you probably want to see in `git status` so it is not ignored. It should not be committed, of course. commit 8b51e449f67aa220b515364bceae960e8b1090e8 Author: Olaf Meeuwissen Date: 2019-06-08 12:36:06 +0900 Add ignore patterns These files were removed in 1c143f630e3c8ef4b649a369260eebebf10c869c. commit b35e0184027c36e51fd73e1f52f8705f525cc4d8 Author: Olaf Meeuwissen Date: 2019-06-08 12:34:58 +0900 Remove files provided/created by Gettext. Re !72 commit ee604e33653537db8825e76bcc8560dd8cb53910 Author: Olaf Meeuwissen Date: 2019-06-07 20:48:40 +0900 Update autotools related information. Re !72 commit 165aad31bcc115013658683a53195279122c0800 Author: Olaf Meeuwissen Date: 2019-06-06 20:50:29 +0900 Make tar invocation work with Busybox tar version commit 03591a4c4a1014cac0cd39aff3daf8dde927563a Author: Olaf Meeuwissen Date: 2019-06-06 20:37:08 +0900 Fix current working directory assumptions commit 103be60e24a07e14b3c8bd98e53472f3822d8a16 Author: Olaf Meeuwissen Date: 2019-06-05 23:44:43 +0900 Really make it a no-op script. Re !72 commit a367df0994ec7fb1e43a0e6e191096bca71d143a Author: Olaf Meeuwissen Date: 2019-06-05 23:39:44 +0900 Add no-op script to satisfy GitLab CI requirements. Re !72 commit 4ddd89dc16700cbe968c7627870be583bf6264d5 Author: Olaf Meeuwissen Date: 2019-06-05 23:34:56 +0900 Move source tarball creation to prepare stage. Re !72 The build jobs use this tarball to exercise the build and only if all builds pass will it be "archived". commit b6f8053af8f2c2ef301c23fbf9e24fe75b4463d7 Author: Olaf Meeuwissen Date: 2019-06-04 18:18:59 +0900 Carry forward configure.ac tweak only. Re !72 commit 1c143f630e3c8ef4b649a369260eebebf10c869c Author: Olaf Meeuwissen Date: 2019-06-02 21:33:22 +0900 Remove more generated files. Re !72 commit 93be23ccf5b0a29024d619f6cc13191c9088b32b Author: Olaf Meeuwissen Date: 2019-06-02 21:21:42 +0900 Fix libtool requirement for autogen.sh. Re !72 commit 3b2ecec808b54543ec341a8028055b36d650e17e Author: Povilas Kanapickas Date: 2019-06-01 14:41:32 +0300 style-check: Ignore patch files commit 61bff8b60153f0189a9bcc5cb70d4a2b042b3f13 Author: Povilas Kanapickas Date: 2019-05-25 09:12:18 +0300 autotools: Restore patch to libtool to change libsane soname commit 72320897d8f1b5b8bfc296791d114de01b4b4bd1 Author: Povilas Kanapickas Date: 2019-05-25 09:12:17 +0300 autotools: Move byteorder.m4 to prevent mixing with generated files commit 9e6a83860b2f8644a739ba837f6434d3815105a6 Author: Povilas Kanapickas Date: 2019-05-25 09:12:16 +0300 autotools: Add autogen.sh to run autotools in appropriate way commit 1c3e1aa1846ec04bf8a48bd918a2ad62ac77dd2e Author: Povilas Kanapickas Date: 2019-05-25 09:12:15 +0300 autotools: Remove files generated by autotools from version control commit 767cbfaa7f8d190648345a97637d76120f3aa1c9 Merge: 1f847e4128a3 1f383b379540 Author: Ilia Sotnikov Date: 2019-06-03 06:10:26 +0000 Merge branch 'hp5590-devel' into 'master' HP5590: Add options for reading out user settings from scanner panel. See merge request sane-project/backends!66 commit 1f383b379540bc8a4b2300b066fe9ed6cfc7f550 Author: Bernard B Badeer Date: 2019-06-03 06:10:26 +0000 Add options for reading LCD counter and LED indicator. * Add command line parameters for readling LCD counter and LED indicator. * Code refactoring: Use static parameter strings instead of dynamically allocated string in order to avoid memory leak. commit 1f847e4128a3f1f2d425b5ebb5509f64f2d32f2b Author: Rolf Bensch Date: 2019-06-01 18:23:41 +0200 INSTALL.linux: add missing development package commit 6b4759252043534f74caa18918ce6f2d91b52651 Merge: 26b3d8aaa085 916d4e0db9e6 Author: Povilas Kanapickas Date: 2019-06-01 02:40:24 +0000 Merge branch 'genesys-fix-read-set-reg-from-set' into 'master' genesys: Fix sanei_genesys_{read,set}_reg_from_set() See merge request sane-project/backends!80 commit 916d4e0db9e62a444cdcbef6c6512b7ecf6dd238 Author: Povilas Kanapickas Date: 2019-06-01 05:24:58 +0300 genesys: Fix sanei_genesys_{read,set}_reg_from_set() There can be registers in the register set with zero address. In particular, the 0x0b register's address is set to zero on most sub-backends to prevent writes to it. This leads to the functions in question not do anything in most scenarios. commit 26b3d8aaa0854f87dd39dce1dbe5af3ae1adbfcc Merge: ceec219cecb4 1df6952788f1 Author: Povilas Kanapickas Date: 2019-05-30 21:17:10 +0000 Merge branch 'genesys-canoscan-8600f' into 'master' genesys: Add basic support for CanoScan 8600F See merge request sane-project/backends!79 commit 1df6952788f1724fe63bb17c588e56d98d0ce23f Author: Povilas Kanapickas Date: 2019-05-31 00:07:44 +0300 doc: Update description for CanoScan 8600F commit c536f3cf7094a597fe389cfdb1acf2c7753522f2 Author: Povilas Kanapickas Date: 2019-05-18 14:56:12 +0300 genesys: Fix calibration size calculation for 8600F The current approach of marking the scanner as GENESYS_FLAG_FULL_HWDPI_MODE is counter-productive, because only the register value is always full DPI, the actual resolution is different. For now, let's just work around this by having a special case for the scanner. commit 0a0a14042338ca26c77910ed08e1248578e16d30 Author: Povilas Kanapickas Date: 2019-05-18 14:56:11 +0300 genesys: Correctly adjust pixels per line during calibration commit 190361b724b0bea6ec253b9296e02e17b8767387 Author: Povilas Kanapickas Date: 2019-05-18 14:56:10 +0300 genesys: Work around first line having artifacts during calibration commit 3a4f67feefe8c262a6da95b9c64057ff1548e089 Author: Povilas Kanapickas Date: 2019-05-18 14:56:09 +0300 genesys: Fix lockups on GL843 during calibration commit ed582c585841279827cd0a3117dfdb3fc1848e44 Author: Povilas Kanapickas Date: 2019-05-18 14:56:08 +0300 genesys: Fix gain calibration on GL843 commit b2205121721306db13b75fa9400c93f92b20a27a Author: Povilas Kanapickas Date: 2019-05-18 14:56:07 +0300 genesys: Add a separate shading line count param for TA mode q commit b7ef75d63f8acc8fb71d8fa74429dcc52eddb012 Author: Povilas Kanapickas Date: 2019-05-18 14:56:06 +0300 genesys: Implement transparency support for 8600F commit 126e98524a3543f83e79dd1a15e3cd6674dc6324 Author: Povilas Kanapickas Date: 2019-05-18 14:56:05 +0300 genesys: Remove special ADF handling for G4050 commit 091fdbe6d66315430cf227b5e92d30639d214f01 Author: Povilas Kanapickas Date: 2019-05-18 14:56:04 +0300 genesys: Use correct offsets for transparency scan commit d0c4f87ac793a7dafcf35796edfee9f23878fdde Author: Povilas Kanapickas Date: 2019-05-18 14:56:03 +0300 genesys: Don't set exposure to zero on 8600F commit 33a60dcc7bae671f63bbacc684c996167c5307df Author: Povilas Kanapickas Date: 2019-05-18 14:56:02 +0300 genesys: Implement support for 8600F commit 41e2029e4bcb25371cc0b89170dfab8e47a40b96 Author: Povilas Kanapickas Date: 2019-05-18 14:56:01 +0300 genesys: Initialize 0x7e register on GL843 commit 966ef337100c4143408a68a609bef8054442b65c Author: Povilas Kanapickas Date: 2019-05-18 14:56:00 +0300 genesys: Fix pixel calculation during calibration on gl843 commit df889c96f5d979369ba702a87ae0cafcda9aba9a Author: Povilas Kanapickas Date: 2019-05-18 14:55:59 +0300 genesys: Deduplicate pixel count computation during calibration commit 695addaafd0d7de46309384587d4d864261083cb Author: Povilas Kanapickas Date: 2019-05-18 14:55:58 +0300 genesys: Fix pixel count when scanning in half-ccd mode on GL843 commit d2b4a1b838eb5df1a27f5a0fde8e2297fd17b1e9 Author: Povilas Kanapickas Date: 2019-05-18 14:55:57 +0300 genesys: Extract gl843_compute_physical_params() commit 76bf1ad072bc5b50743273060f3c4b3cd67e0950 Author: Povilas Kanapickas Date: 2019-05-18 14:55:56 +0300 genesys: Improve documentations for registers in various places commit ceec219cecb467e9d2cd082f88096f0a1686200b Merge: dc42318d905e aeefb370724c Author: Povilas Kanapickas Date: 2019-05-30 20:51:27 +0000 Merge branch 'genesys-fix-gl843-calib-pixel-count' into 'master' genesys: Fix read pixel count calculation during calibration on GL843 See merge request sane-project/backends!78 commit aeefb370724cd0bfa085f728d3bf7132a9134863 Author: Povilas Kanapickas Date: 2019-05-30 23:33:13 +0300 genesys: Fix read pixel count calc during calibration on GL843 commit dc42318d905e35f09a5e5d0a9560d296eb43cf06 Merge: 9f461060aac2 50b27fe8367c Author: Povilas Kanapickas Date: 2019-05-29 21:28:43 +0000 Merge branch 'genesys-use-containers' into 'master' genesys: Use C++ containers to remove chances of memory leaks See merge request sane-project/backends!76 commit 50b27fe8367cbe5f1e426d709be00aae478c6b15 Author: Povilas Kanapickas Date: 2019-05-25 11:15:42 +0300 genesys: Use std::string for Genesys_Device::calib_file commit 38986c2c6f39da0f326c922b3607b78c1591b5e9 Author: Povilas Kanapickas Date: 2019-05-25 11:15:41 +0300 genesys: Use std::vector for Genesys_Device::img_buffer commit eb93267fa9af5632d1cff5cf221b3ea2916c69d0 Author: Povilas Kanapickas Date: 2019-05-25 11:15:40 +0300 genesys: Use std::vector instead of custom vector implementation commit 69485e9f11963cee51ff10fd63d069bd2fea06a8 Author: Povilas Kanapickas Date: 2019-05-25 11:15:39 +0300 genesys: Remove unused new device list commit 3096dcee0ee25ea93123801e56a1d75cd153aca1 Author: Povilas Kanapickas Date: 2019-05-25 11:15:38 +0300 genesys: Use s_devices.size() for num_devices commit 9c2fdeeb3d91d581d55fa8972b583e7d7cb8b765 Author: Povilas Kanapickas Date: 2019-05-25 11:15:37 +0300 genesys: Use proper containers for device lists commit 5a90b7bb8fb72ba9fed235d2ff974c8e26dfba30 Author: Povilas Kanapickas Date: 2019-05-25 11:15:36 +0300 genesys: Convert Genesys_Buffer to C++ class commit 247658f23ed81be58a5bfea6f62f9d229b163ff4 Author: Povilas Kanapickas Date: 2019-05-25 11:15:35 +0300 genesys: Return std::vector from GL646 simple_scan() commit e48b6c4e2dc7065ead61015673fed89d16f1c81a Author: Povilas Kanapickas Date: 2019-05-25 11:15:34 +0300 genesys: Store global Genesys_Scanner instances in std::list commit 15b373a5cc388aed628d5514e00f979cf8df6222 Author: Povilas Kanapickas Date: 2019-05-25 11:15:33 +0300 genesys: Use std::vector for calibration cache average data commit 64ca298d02d03dbd2818d8c887f7c94c350ac42e Author: Povilas Kanapickas Date: 2019-05-25 11:15:32 +0300 genesys: Store calibration class using std::list commit 52b1989edaa99dea1d1ab6bbcc9486d64ae28360 Author: Povilas Kanapickas Date: 2019-05-25 11:15:31 +0300 genesys: Remove no longer used RIEF and RIEF2 macros commit 3d44c89f1e7a3ef548275730127d956c415773ba Author: Povilas Kanapickas Date: 2019-05-25 11:15:30 +0300 genesys: Use std::vector for Genesys_Device::{dark,white}_average_data commit 2dc4e5d1fc3c48cf1cd7ae1010dd5cc3328754dc Author: Povilas Kanapickas Date: 2019-05-25 11:15:29 +0300 genesys: Use std::vector for Genesys_Sensor::gamma_table commit 555be1c3eb5e3becac770170687713d73756511b Author: Povilas Kanapickas Date: 2019-05-25 11:15:28 +0300 genesys: Use Genesys_Device as C++ class commit cb189cfe2d9ec1004e83c285612f6ee386f44100 Author: Povilas Kanapickas Date: 2019-05-25 11:15:27 +0300 genesys: Rewrite sensor table to use std::vector commit e5eff5d76f98170d6b583aa19e9c6ebd58407eb4 Author: Povilas Kanapickas Date: 2019-05-25 11:15:26 +0300 genesys: Add facility for auto releasing static data on backend exit commit 9cea33b53c4fb351e8983c0a1f6e2694220f8127 Author: Povilas Kanapickas Date: 2019-05-25 11:15:25 +0300 genesys: Use std::vector instead of malloc for local allocations commit 9f461060aac2c6517e471699a7efa7e254b8048d Merge: 5d7fc4e0ed34 896385a082f0 Author: Povilas Kanapickas Date: 2019-05-28 01:21:44 +0000 Merge branch 'genesys-catch-exceptions' into 'master' genesys: Catch C++ exceptions when returning to C code See merge request sane-project/backends!75 commit 896385a082f0d44a4fa4abb21c4c5fd110d51d8e Author: Povilas Kanapickas Date: 2019-05-25 10:03:15 +0300 genesys: Ensure that functions passed to C API don't throw commit 23f3ebf612456f3328c925764abfcaaa31c80e39 Author: Povilas Kanapickas Date: 2019-05-25 10:03:14 +0300 genesys: Wrap exported functions to always catch exceptions commit 5d7fc4e0ed3489982082be3c1845fa3f2eba42d9 Merge: 216e8b28a0b7 58ee13d057f0 Author: Povilas Kanapickas Date: 2019-05-24 18:25:58 +0000 Merge branch 'genesys-fix-warning' into 'master' genesys: Fix warning See merge request sane-project/backends!71 commit 58ee13d057f0db1c07edb6627aaf973e6a3d6566 Author: Povilas Kanapickas Date: 2019-05-24 21:13:37 +0300 genesys: Fix warning commit 216e8b28a0b792276ede8a46618a713ad5ef4b9d Author: Olaf Meeuwissen Date: 2019-05-22 21:48:32 +0900 Turn C++ compiler warning into error on the right job. Re #85 Fixes 8d67531c70e8cc346d8705477ac9c53d0a4571d8 where the flags were set for a job that runs in an environment *without* a C++ compiler. commit 199a685d0b174ce618d23decd0565b591be67b91 Author: Olaf Meeuwissen Date: 2019-05-22 21:35:08 +0900 Restore availability of config.status for archive stage. Re #85 commit 8d67531c70e8cc346d8705477ac9c53d0a4571d8 Author: Olaf Meeuwissen Date: 2019-05-22 21:31:13 +0900 Turn C++ compiler warnings into errors. See #85 commit 57ee89a6760621d92fa8564bdc066c36c17618d7 Author: Olaf Meeuwissen Date: 2019-05-22 21:19:54 +0900 Drop caching of untracked files. Re #85 commit be9736020f48119a1060088d95ec1ac8eef8ac07 Author: Olaf Meeuwissen Date: 2019-05-21 22:11:50 +0900 autofoo: Sync generated files The AX_CXX_COMPILE_STDCXX* macros that were embedded in aclocal.m4 have been replaced with the versions found in the autoconf-archive package from Debian 9. commit e2d89c1ab3b06cb81634a869ed94620d91af6d68 Author: Olaf Meeuwissen Date: 2019-05-21 22:10:12 +0900 Expand AM_CXXFLAGS in our Makefiles. Fixes #84 commit 16a3945ce6b585c97a90461419b573a2d389d58f Merge: f498e5fa6671 0a66ed1dbcb3 Author: Povilas Kanapickas Date: 2019-05-20 09:47:38 +0000 Merge branch 'genesys-sleep' into 'master' genesys: Add wrapper for sleep that can be turned off when testing See merge request sane-project/backends!70 commit 0a66ed1dbcb3f85bedab81581cb84320a8c3c2c1 Author: Povilas Kanapickas Date: 2019-04-28 17:47:45 +0300 genesys: Use a function to sleep ms to reduce chance of errors commit d9f65eefc0fca4a45fb3ac1b944d55fb33fd319a Author: Povilas Kanapickas Date: 2019-04-28 17:47:44 +0300 genesys: Fix a bug in sleep duration calculation commit baaa934dce8db53bd1c4f0e0308f7dac1000201f Author: Povilas Kanapickas Date: 2019-04-28 17:47:43 +0300 genesys: Use a wrapper of usleep() commit f498e5fa66718a76adbcd60be8228a50cc903ab1 Author: Olaf Meeuwissen Date: 2019-05-20 18:32:48 +0900 genesys: Add missing pointer cast on malloc call Fixes build on Fedora 29 and 30. commit 2bb5ed655ade84281435d80cf133403f8e11bb29 Merge: 2a11622967ff 185303a18d18 Author: Olaf Meeuwissen Date: 2019-05-20 09:10:53 +0000 Merge branch 'genesys-enable-cxx' into 'master' Use C++ for the genesys backend See merge request sane-project/backends!61 commit 185303a18d18e855df38b85a0647d595a1c0462f Author: Povilas Kanapickas Date: 2019-05-19 13:50:11 +0300 Enable warnings in C++ source commit 42fb5a3e038d1344534e5ff1b175dc4c7125ff0d Author: Povilas Kanapickas Date: 2019-05-19 13:39:42 +0300 Don't compile genesys backend when C++11 is not available commit ca051cc22736909c7eaddbb1ba549eb718ea36f8 Author: Povilas Kanapickas Date: 2019-05-10 21:16:11 +0300 genesys: Compile library as C++ code commit 72121bc7d88bbd6577cd3ea88b301d91ac35d675 Author: Povilas Kanapickas Date: 2019-05-10 21:16:10 +0300 sanei: Use C linkage for internal functions in C++ mode commit 9e3b5d6381b5c8d49b71753e7b28b65f45feebee Author: Povilas Kanapickas Date: 2019-05-10 21:16:09 +0300 Update generated files commit b93340b8623de865c73b7e12a5b448732f9a5f68 Author: Povilas Kanapickas Date: 2019-05-11 00:02:47 +0300 Enable C++ in autoconf commit 2a11622967ff2f5f09d4b47ffc3e1817645f814a Merge: 758553859589 7a8ae928626a Author: Stanislav Yuzvinsky Date: 2019-05-19 18:18:57 +0000 Merge branch 'teardown' into 'master' ricoh2: Rearrange init/deinit code of ricoh2 to correctly support "code flow" See merge request sane-project/backends!65 commit 7a8ae928626a2ccecc5dd40dd4564c47d8dcb9a4 Author: Stanislav Yuzvinsky Date: 2019-05-08 11:03:15 +0300 ricoh2: Rearrange init/deinit code of ricoh2 to correctly support "code flow" The backend worked incorrectly when scanning process was interrupted before the current page was scanned completely. For example, scanimage tool could not scan the next page if the previous one was interrupted. Now this issue is fixed. See also "4.4. Code Flow" in SANE API specification v.1.06. Also debug levels was changed to have better control over what should be logged. commit 758553859589a1c5ec0a7ff688fe4621e81b62db Author: Olaf Meeuwissen Date: 2019-05-19 21:41:16 +0900 epson2: Add XP-255 as supported. Fixes #81 commit 71bf2daec6584db1228975c88cb737375244e57e Merge: 8cdd5ae22e3a a4ab69376c3f Author: Povilas Kanapickas Date: 2019-05-19 12:02:50 +0000 Merge branch 'genesys-remove-static' into 'master' genesys: Remove uses of GENESYS_STATIC See merge request sane-project/backends!68 commit a4ab69376c3f05a37d51953776a938731c277683 Author: Povilas Kanapickas Date: 2019-05-19 14:49:44 +0300 genesys: Remove uses of GENESYS_STATIC commit 8cdd5ae22e3ac4c3b059edba56e174eafcfc404d Merge: e13b80fa6ff3 4c2c4eb6397b Author: Povilas Kanapickas Date: 2019-05-19 11:56:38 +0000 Merge branch 'genesys-debug-cleanup' into 'master' genesys: Use the same debug identifier for the whole backend See merge request sane-project/backends!67 commit 4c2c4eb6397b974c90a20c1f4f355730b700be27 Author: Povilas Kanapickas Date: 2019-05-19 14:46:46 +0300 genesys: Remove duplicate definition of DBGSTART and DBGCOMPLETED commit 6f617e9889be1c35f5960b2a00dcc702aa3787f2 Author: Povilas Kanapickas Date: 2019-05-19 14:46:45 +0300 genesys: Don't define different backend names in source files commit 873e82c0a06a6a240cdb8e0178f4bc6f5d87c1c2 Author: Povilas Kanapickas Date: 2019-05-19 14:46:44 +0300 genesys: Remove logging of the build number A git commit would be much more useful in log messages commit e13b80fa6ff333cede132dc29377eec9b6c02f48 Merge: ae871333ef09 0273c05c9504 Author: Olaf Meeuwissen Date: 2019-05-12 03:11:00 +0000 Merge branch 'add_sg3100' into 'master' Add support for Aficio SG3100SNw See merge request sane-project/backends!57 commit 0273c05c95040b0cd3141117d67bbd08e4a03e5b Author: Stanislav Yuzvinsky Date: 2019-05-11 16:17:40 +0300 Fix review comments; add the backend to dll.conf commit 3514d06156424373b832f2e46a7d7e3311f6d15b Author: Stanislav Yuzvinsky Date: 2019-05-01 01:14:45 +0300 Add support for Aficio SG3100SNw commit ae871333ef09d867c6d95edd6c3adf3bd05c2fea Merge: 4df63ef54801 ee6d6da33945 Author: Olaf Meeuwissen Date: 2019-05-12 02:36:36 +0000 Merge branch 'scanimage-output-path' into 'master' scanimage: Allow specification of the output path via option See merge request sane-project/backends!46 commit ee6d6da339459bc8393547005e087e97d3d4987b Author: Povilas Kanapickas Date: 2019-04-28 23:28:21 +0300 scanimage: Update manual page commit 728de89d7166708331907726571c7027b4921d82 Author: Povilas Kanapickas Date: 2019-04-28 23:28:20 +0300 scanimage: Guess --format from --output-file if possible commit 86e917b04b9bd1fbeb53d58323b118f30c8efc15 Author: Povilas Kanapickas Date: 2019-04-28 23:28:19 +0300 scanimage: Prevent --output-file and --batch to be used together commit 7f4944f0a7d1c7ce90d5f4f79078112bc5d52dff Author: Povilas Kanapickas Date: 2019-04-28 23:28:18 +0300 scanimage: Warn when output format is not set commit 877cc29d88529f9af8e898a52b4be99863a20eea Author: Povilas Kanapickas Date: 2019-04-28 23:28:17 +0300 scanimage: Raise an error if --format option is an unknown format commit 5dbd51d1ec36ba4389a7ec18ff041fd04ee3c5da Author: Povilas Kanapickas Date: 2019-03-20 23:09:03 +0200 scanimage: Allow specification of the output path via option commit 4df63ef5480103edf5c2a92a1641afdc0eddca04 Merge: 3c8b9b916794 988a91aec476 Author: Povilas Kanapickas Date: 2019-05-11 09:20:45 +0000 Merge branch 'genesys-debug-improvements' into 'master' genesys: Miscellaneous debug improvements See merge request sane-project/backends!64 commit 988a91aec47697f1ec37ecc492819223df40b18f Author: Povilas Kanapickas Date: 2019-05-11 12:12:29 +0300 genesys: Write all offset calibration debug to a single file on gl843 commit 36bc194405ed82e56ec12b68b5cc9491ed3ef199 Author: Povilas Kanapickas Date: 2019-05-11 12:12:28 +0300 genesys: Add a small C array implementation commit 5321427e55b67a1d7274f3b0851e89dced66e1a0 Author: Povilas Kanapickas Date: 2019-05-11 12:12:27 +0300 genesys: Don't write identical debug pnm files commit 882741d78ae5ebffbd128104d850eec95a9e5a50 Author: Povilas Kanapickas Date: 2019-05-11 12:12:26 +0300 genesys: Prepend file identifier to debug image filenames commit d3b25a59664a2779481329d4c5627f9018af3f83 Author: Povilas Kanapickas Date: 2019-05-11 12:12:25 +0300 genesys: Don't write identical debug pnm files commit 3c8b9b9167947def05544160153d8f3bc5312548 Merge: 74fffa64791f 8de5a1fdb104 Author: Povilas Kanapickas Date: 2019-05-11 09:14:06 +0000 Merge branch 'genesys-reduce-duplication' into 'master' genesys: Reduce duplication in low level functions See merge request sane-project/backends!63 commit 8de5a1fdb10498c85a998ad8eeb4e6b228019fb2 Author: Povilas Kanapickas Date: 2019-05-11 12:05:26 +0300 genesys: Remove unused code commit da81a523fa231858a18232186c878730c849aa6f Author: Povilas Kanapickas Date: 2019-05-11 12:05:25 +0300 genesys: Reuse sanei_genesys_bulk_write_register() on GL841 commit 2b1f13fddb9fdfe16523c855242a768f39a64f57 Author: Povilas Kanapickas Date: 2019-05-11 12:05:24 +0300 genesys: Reuse sanei_genesys_bulk_write_register() on GL646 commit b683a40fdda7eae26ca80995935d473a272521e6 Author: Povilas Kanapickas Date: 2019-05-11 12:05:23 +0300 genesys: Reuse common genesys register functions on GL646 commit 58e1eb8ac4692423ce4795a815d0d7c2ef2b25cf Author: Povilas Kanapickas Date: 2019-05-11 12:05:22 +0300 genesys: Reuse sanei_genesys_bulk_write_data() on GL646 commit 80efacab1022351597dac95f6183204fb3f3e800 Author: Povilas Kanapickas Date: 2019-05-11 12:05:21 +0300 genesys: Reuse sanei_genesys_bulk_write_data() on GL841 commit d62d4f0d164b1ee3a5a3debebd307b6484fa5a8f Author: Povilas Kanapickas Date: 2019-05-11 12:05:20 +0300 genesys: Use sanei_genesys_get_bulk_max_size() for max bulk out size commit 7ee139bd4bc0dbc6740df0d9fbe0bc89794c18da Author: Povilas Kanapickas Date: 2019-05-11 12:05:19 +0300 genesys: Pass device to sanei_genesys_write_ahb() as ptr not number commit d7bff42cba003c99197ce7bb2384d08383dd4ef9 Author: Povilas Kanapickas Date: 2019-05-11 12:05:18 +0300 genesys: Add generic sanei_genesys_bulk_write_data() out of GL843 impl commit 98258549cb48955aadba7fb6e2ed266aeec3eb68 Author: Povilas Kanapickas Date: 2019-05-11 12:05:17 +0300 genesys: Extract sanei_genesys_get_bulk_max_size() commit 5b9d0145927b0974cb49d4f042e7d2927abdd97d Author: Povilas Kanapickas Date: 2019-05-11 12:05:16 +0300 genesys: Remove commented out code for buffer size alignment commit 9dc8bbfac95c2106aabe2833dbd7ccf191aee62a Author: Povilas Kanapickas Date: 2019-05-11 12:05:15 +0300 genesys: Reuse sanei_genesys_bulk_read_data() on GL847 commit 4af3557bbf622b4937278273daef782baaf42157 Author: Povilas Kanapickas Date: 2019-05-11 12:05:14 +0300 genesys: Reuse sanei_genesys_bulk_read_data() on GL846 commit 216c18f9ff02b2984c55b65b4050caef70956a7b Author: Povilas Kanapickas Date: 2019-05-11 12:05:13 +0300 genesys: Reuse sanei_genesys_bulk_read_data() on GL843 commit 55e5c7b7ac745ada19dc95dd59929a6a03313431 Author: Povilas Kanapickas Date: 2019-05-11 12:05:12 +0300 genesys: Reuse sanei_genesys_bulk_read_data() on GL841 commit 8e8511a49e8bfb4aae993a64bc8845f82e26d513 Author: Povilas Kanapickas Date: 2019-05-11 12:05:11 +0300 genesys: Reuse sanei_genesys_bulk_read_data() on GL646 commit 69435b3bb2a35ba11ac1a1e79ee91bd4a5c2f9a5 Author: Povilas Kanapickas Date: 2019-05-11 12:05:10 +0300 genesys: Revert 512-byte bulk transfer alignment workaround It should be handled by libusb. commit 4a899d26f33e337b56e0d5c8ad834bee7cfb132d Author: Povilas Kanapickas Date: 2019-05-11 12:05:09 +0300 genesys: Create generic sanei_genesys_bulk_read_data out of gl124 impl commit 74fffa64791f6e670942604eac27bc330afc339b Merge: 15fd9bc9ce70 887759192bbf Author: Povilas Kanapickas Date: 2019-05-11 09:08:11 +0000 Merge branch 'genesys-force-calibration' into 'master' genesys: Add option to force calibration ignoring caches See merge request sane-project/backends!62 commit 887759192bbf2f8265f5897ba43acdcf5a10cbe5 Author: Povilas Kanapickas Date: 2019-05-11 12:00:25 +0300 genesys: Add option to force calibration ignoring caches commit 15fd9bc9ce70953d70fd3fa2f48bf725cb27a37d Merge: 856391100f00 16a8d5548091 Author: Olaf Meeuwissen Date: 2019-05-11 08:13:18 +0000 Merge branch 'log-messages-no-git-conflict-markers' into 'master' Don't use strings that are similar to conflict markers in debug msgs See merge request sane-project/backends!60 commit 16a8d554809171156ed2727c6e1f4e2fdff5b5e3 Author: Povilas Kanapickas Date: 2019-05-10 21:05:51 +0300 Don't use strings that are similar to conflict markers in debug msgs commit 856391100f00b49bdf245930cd68c52f3116b6f7 Merge: 56c01c005a8d 47e7f087c622 Author: Povilas Kanapickas Date: 2019-05-10 18:23:32 +0000 Merge branch 'genesys-explicit-casts' into 'master' genesys: Perform type casts explicitly See merge request sane-project/backends!59 commit 47e7f087c62234c540cac2cdcf86d4334a4026ea Author: Povilas Kanapickas Date: 2019-05-10 21:04:15 +0300 genesys: Perform type casts explicitly commit 56c01c005a8ddda04d24e81a337e724f673faece Merge: f6038a70ec2c 296542905e84 Author: Olaf Meeuwissen Date: 2019-05-05 09:27:29 +0000 Merge branch '24-hp-scanjet-8250-duplex-broken-avision-backend' into 'master' avision: Fix threaded ADF flipping duplex handling Closes #24 See merge request sane-project/backends!58 commit 296542905e84c5607e3dc5da5766fd574015f66d Author: Olaf Meeuwissen Date: 2019-05-04 23:39:33 +0900 avision: Fix threaded ADF flipping duplex handling. Re #24 commit f6038a70ec2c4492d7488e81021fed04891f5d06 Author: Olaf Meeuwissen Date: 2019-05-04 09:19:26 +0900 Add a Fedora 30 build, drop the Fedora 28 one commit 1b8f7926fe18b2d5c646480a4fa691f1984114a3 Merge: fa940e86bbac 25feb1be0c89 Author: Olaf Meeuwissen Date: 2019-05-02 06:16:55 +0000 Merge branch 'debian-bts-869673-genesys-usb-mode-initialization' into 'master' genesys: Fix use of uninitialized variable See merge request sane-project/backends!56 commit 25feb1be0c89f14d4cbca09e5e44b50ac1ebb8ed Author: Olaf Meeuwissen Date: 2017-08-03 18:50:05 +0900 genesys: Fix use of uninitialized variable See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=869673 commit fa940e86bbac2ac21c969b80722d8cf341eac389 Merge: 916ce82f007e 0921d3661812 Author: Povilas Kanapickas Date: 2019-04-28 21:02:16 +0000 Merge branch 'sanei-endpoint-refactor' into 'master' sanei_usb: Deduplicate endpoint setup code See merge request sane-project/backends!48 commit 0921d3661812615b4cb12d16d0754af49445bd05 Author: Povilas Kanapickas Date: 2019-03-22 15:56:57 +0200 sanei_usb: Deduplicate endpoint setup code commit 916ce82f007e0eb297dadda9a0577482f0fb2dc0 Author: Rolf Bensch Date: 2019-04-25 21:31:27 +0200 pixma: backend version 0.23.0 commit 28d87cb6e5400df519c703bc4769808a37b6f990 Merge: 392beeec74b9 9fe7d990891c Author: Rolf Bensch Date: 2019-04-25 21:27:53 +0200 Merge remote-tracking branch 'origin/master' commit 9fe7d990891cd53ec6295610507be0238ad868c6 Merge: d50f33306f9c a3c64bc0cad1 Author: Rolf Bensch Date: 2019-04-25 19:26:50 +0000 Merge branch 'master' into 'master' Added Imageclass MF634Cdw See merge request sane-project/backends!49 commit a3c64bc0cad104d09ba14f833828ae963dbc72f9 Author: MackPi Date: 2019-04-03 16:37:03 -0700 Added Imageclass MF634Cdw Added MF634Cdw using MF733Cdw as reference commit 392beeec74b97cd712469953962c2b3a6bf2b31f Author: Rolf Bensch Date: 2019-04-25 21:24:40 +0200 pixma: new scanners Canon i-SENSYS MF110, MF260, MF420, MF510, MF520, MF640 and MF740 Series commit d50f33306f9c9ae0d976dce247c316c071ae3715 Merge: a569bfbf02c1 a814df334311 Author: Povilas Kanapickas Date: 2019-04-22 22:07:19 +0000 Merge branch 'genesys-numeric-model-ids' into 'master' genesys: Add numeric model IDs and prefer them to string comparisons See merge request sane-project/backends!54 commit a814df3343110b0329dfaab83d4e4e31e1ad27e5 Author: Povilas Kanapickas Date: 2019-04-23 00:55:46 +0300 genesys: Prefer numeric model IDs instead of string comparisons commit ad164ce9c6a7b924ec88d6496fa89f7093272581 Author: Povilas Kanapickas Date: 2019-04-23 00:55:45 +0300 genesys: Fix a typo in LiDE 120 gpio setup commit f240a77e92d0557996ed37b650ddfaf3ae64342e Author: Povilas Kanapickas Date: 2019-04-23 00:55:44 +0300 genesys: Add numeric model IDs to model tables commit a569bfbf02c1d85b440db0b00f5e3a6564991564 Merge: 595e1fc754cf 3cd8a4cd0157 Author: Povilas Kanapickas Date: 2019-04-22 21:38:29 +0000 Merge branch 'genesys-misc' into 'master' genesys: Miscellaneous changes/improvements See merge request sane-project/backends!53 commit 3cd8a4cd015769d275816252b256cc0e97b9a847 Author: Povilas Kanapickas Date: 2019-04-23 00:14:49 +0300 genesys: Use hex to print USB vendor and product IDs to debug log commit d7c17f75403aa5619c711b7d19e81932fa9a47b1 Author: Povilas Kanapickas Date: 2019-04-23 00:14:48 +0300 genesys: Limit maximum bulk transfer size to allow data capture commit f04119b12626d0bb7c71e46a8a82837501da7ee9 Author: Povilas Kanapickas Date: 2019-04-23 00:14:47 +0300 genesys: Remove support for fake USB mode WIP implement support for 8600F commit 902610132b04d1adc279f11cc2cb7541a28b0ba7 Author: Povilas Kanapickas Date: 2019-04-23 00:14:46 +0300 genesys: Include genesys_low.h commit 595e1fc754cfa463f771f25daff5c7bea79715d8 Merge: c9ebf2b5605e 23416187205c Author: Povilas Kanapickas Date: 2019-04-22 21:15:44 +0000 Merge branch 'genesys-bugs' into 'master' Several genesys bugfixes See merge request sane-project/backends!52 commit 23416187205c4d7ef4ec9c1a4b1eaa3f0e03a4c4 Author: Povilas Kanapickas Date: 2019-04-23 00:05:35 +0300 genesys: Work around reads from non-initialized memory in gamma setup commit 88c22e38beef49e2722656f47f39e33b329bd967 Author: Povilas Kanapickas Date: 2019-04-23 00:05:34 +0300 genesys: Fix double free in sanei_genesys_send_gamma_table() commit 621c9799fe535d296218f80701a9ea06e538974d Author: Povilas Kanapickas Date: 2019-04-23 00:05:33 +0300 genesys: Fix use of uninitialized memory commit c9ebf2b5605e263d8d93f1132cb4df73ebee4516 Merge: 4f5eb745ac68 2fd3b5622b39 Author: Povilas Kanapickas Date: 2019-04-22 20:56:38 +0000 Merge branch 'genesys-remove-unneeded-function-names-in-dbg-args' into 'master' genesys: Remove unneeded function name in DBG macro args See merge request sane-project/backends!51 commit 2fd3b5622b395bc60beb903b5e5539d23ca9b6eb Author: Povilas Kanapickas Date: 2019-04-22 23:48:23 +0300 genesys: Remove unneeded function name in DBG macro args commit 4f5eb745ac680479ed23eff0741911903090d21c Author: Olaf Meeuwissen Date: 2019-04-17 21:00:47 +0900 epson2_usb.c: Sync USB product IDs with epson2.desc content commit ddbcb2b28588068f845d6ef3feaa32c60f6e57d3 Author: Olaf Meeuwissen Date: 2019-04-17 21:00:10 +0900 epson2.desc: Add the EPSON L380 as supported. Re #23 commit 3f6f9f829eca56791cd3f28e755eaaef5761357a Author: Olaf Meeuwissen Date: 2019-04-06 17:11:47 +0900 utsushi.desc: Sync with upstream commit 155248dde28b2a9ec4114d59dc294b69b3260142 Author: Louis Lagendijk Date: 2019-03-27 19:09:43 +0100 pixma_bjnp: change default timeout from 1 to 10 seconds (10000) commit d15aa248c78c6f296fe1dbef189f54052c80386b Author: Louis Lagendijk Date: 2019-03-27 18:46:17 +0100 pixma_bjnp.c: corrected url-rewrite as it set bjnp:// unconditionally rather than honor the received method function renamed to add_default_timeout() to clarify its purpose commit 4ac6550e7ca64a4f6c7ece2156c3d43033600b52 Author: Olaf Meeuwissen Date: 2019-03-23 14:09:24 +0900 utsushi.desc: Sync with upstream commit 276670f3c249e4a59267bc5c45e170c8e7be7286 Merge: e92a3d6ebaca 2653cbaec799 Author: Povilas Kanapickas Date: 2019-03-22 14:27:53 +0000 Merge branch 'rerun-autoreconf' into 'master' Rerun autoreconf and automake See merge request sane-project/backends!44 commit 2653cbaec799fbfd365362c45e61796ce90b6466 Author: Povilas Kanapickas Date: 2019-03-20 23:09:07 +0200 Run autoreconf and automake commit e92a3d6ebaca4fcedb262e7861155b9fd49e5bf0 Merge: de5c63ee7cff d866998f102f Author: Povilas Kanapickas Date: 2019-03-22 14:25:39 +0000 Merge branch 'remove-unused-unit-testing' into 'master' Remove uses of unused UNIT_TESTING ifdef See merge request sane-project/backends!45 commit d866998f102f242ed421ff4b4480aa2a4073c166 Author: Povilas Kanapickas Date: 2019-03-20 23:09:05 +0200 genesys: Remove uses of unused UNIT_TESTING ifdef commit b7b5ca79c8f7a92d488d96d0b1d0963d01d86423 Author: Povilas Kanapickas Date: 2019-03-20 23:09:04 +0200 rts8891: Remove uses of unused UNIT_TESTING ifdef commit de5c63ee7cff16115fa04ed998c988fb79087bda Merge: 13350ba4ba0a 265f4a96ea0d Author: Povilas Kanapickas Date: 2019-03-22 14:24:48 +0000 Merge branch 'genesys-enums' into 'master' genesys: Prefer enums to #defines See merge request sane-project/backends!47 commit 265f4a96ea0d3ba898b65adb56f7c7b83ae18b30 Author: Povilas Kanapickas Date: 2019-03-22 14:50:03 +0200 genesys: Prefer enums to #defines commit 13350ba4ba0a40e0fae4567bb23566575a0317e2 Author: Rolf Bensch Date: 2019-03-11 17:46:18 +0100 pixma: backend version 0.22.0 commit dfc5f5ed760b7922c98280e03099aed5bcc719ad Author: Rolf Bensch Date: 2019-03-11 17:45:27 +0100 pixma: new scanners Canon PIXUS XK50, XK70, XK80 Series commit 6f4440152f58507f5a4d4f915f41a5de7c0a5e2e Author: Rolf Bensch Date: 2019-03-11 17:44:19 +0100 pixma: new scanners PIXMA TS6130, TS6180, TS6230, TS6280, TS8100, TS8130, TS8180, TS8200, TS8230, TS8280, TS9180, TS9580 Series commit adef5537994948794ca6db3369b71269ffaa1d45 Author: Rolf Bensch Date: 2019-03-11 17:26:29 +0100 pixma: new scanners Canon PIXMA TR4500, TR7350, TR8530, TR8580, TR9530 Series commit 83ddbd0fc4ae64c694bf8f9ba5f695aa5f7d3bd8 Author: Rolf Bensch Date: 2019-03-11 17:23:20 +0100 pixma: new scanners Canon PIXMA G3010, G4010 Series commit 96e6eff1ae18d9d34d24a5e9ae4684da80c70165 Author: Rolf Bensch Date: 2019-03-11 17:21:36 +0100 pixma: fix some descriptions names commit 953562ae8e6e1c215e1b3818911edc35c6e8aec7 Author: Rolf Bensch Date: 2019-03-11 17:17:45 +0100 pixma: new scanner Canon PIXMA E4200 Series commit 0678ce8e3bd8f73efc3189df225b25d6de368679 Author: Rolf Bensch Date: 2019-03-11 17:15:54 +0100 pixma: fix some interface descriptions commit 960a6d5d697bac72dd5d068eefe12741a5d68de5 Merge: 9d69d94f0219 2915756ae426 Author: Rolf Bensch Date: 2019-03-08 20:56:26 +0000 Merge branch 'master' into 'master' Add Driver Canon Pixma TS 6200 Series. See merge request sane-project/backends!43 commit 2915756ae4262b6a87490b5d96959f58c498307b Author: Thierry Date: 2019-03-05 08:43:31 +0100 Add Driver Canon Pixma TS 6200 Series. commit 9d69d94f0219fa71b8e16968f08293f902d0016f Author: Rolf Bensch Date: 2019-03-08 17:28:36 +0100 add missing development packages to INSTALL.linux commit 9e4344b33a2d83aa7663b65d6d8a49c7306d72c7 Author: Rolf Bensch Date: 2019-02-27 19:37:58 +0100 remove canon_mfp from doc/sane-mfgs-external.html all listed scanners are supported by the pixma backend commit 9c4ac1f07f67b9f07f53a380897b3af11601a240 Author: m. allan noah Date: 2019-02-24 20:37:55 -0500 canon_dr backend v57 - complete support for X-10, including hardware cropping (by manuarg) - minor comments and move a bit of code for consistency commit 81faeb46f28c4b6c251123aabac118ff0262d9e7 Merge: 2d0912afdcdb 60dffda6036a Author: m. allan noah Date: 2019-02-25 01:37:03 +0000 Merge branch 'canon_drx10c' into 'master' Add support for Canon DR-X10C scanner See merge request sane-project/backends!12 commit 60dffda6036ae15d89b2a94af269d32dcb300786 Author: Manuel Argüelles Date: 2019-02-08 22:40:15 -0500 Add mention of company contributing to canon_dr.c commit 1bc6ade107d294d93ad4c50727bd9fd77caf63ec Author: Manuel Argüelles Date: 2018-11-30 10:50:13 -0500 Fix image displacement for lineart/halftone during hardware crop Increases the width of the image if the reported size is less than the byte boundary. commit ecac81eee2f79fc3f089f3a3d3509a2860e3f4c0 Author: Manuel Argüelles Date: 2018-11-12 14:01:24 -0500 Set scan area to maximum when hardware crop is enable commit d61a59ca01b638474b6a847bf26e6dfb9420e064 Author: Manuel Argüelles Date: 2018-07-30 17:26:13 -0500 Add support for Canon DR-X10C scanner Added initial support for DR-X10C SSM2 scanner with dropout color, hardware deskew and hardware crop. commit 2d0912afdcdb174721dbad0cb2e200880e5ddb15 Author: Rolf Bensch Date: 2019-02-24 12:40:16 +0100 pixma: backend version 0.21.1 commit 0c00dac37adbf61db5f64a5bcb67be039a19bd58 Author: Rolf Bensch Date: 2019-02-24 12:39:54 +0100 pixma: update doc files for MF731/733 commit d0e4f0fa5268294993ec640a18bcd8893a4d1e51 Author: Rolf Bensch Date: 2019-02-24 12:38:37 +0100 pixma: add comment for MF733C commit 73645abba08a0df68c43f798dee9cf28451b785d Merge: 5ae6f698aedd b327c3d780a4 Author: Rolf Bensch Date: 2019-02-24 11:22:07 +0000 Merge branch 'mikef-MF733C' into 'master' Mikef mf733 c See merge request sane-project/backends!42 commit b327c3d780a486b3069f2f17c589fcc083289788 Author: Mike Ferrara Date: 2019-02-22 11:01:51 -0800 Typo. fixed. commit eadd663676c17a30ba27854e82ad8dfebefb4aa6 Merge: 7277cec027c8 ff04ede4adb4 Author: Mike Ferrara Date: 2019-02-22 10:40:29 -0800 Merge branch 'master' into mikef-MF733C commit 7277cec027c80386f90193b330aacf1b555d0c96 Author: Mike Ferrara Date: 2019-02-22 10:30:18 -0800 Mods to support imageCLASS MF733Cdw USA color laser MFP commit 5ae6f698aedd58ad83802b0ecfe87d171f30ee72 Author: m. allan noah Date: 2019-02-23 15:46:59 -0500 remove trailing whitespace commit 156f7c9bd442105038adeb9962a60fb1b56b6349 Author: m. allan noah Date: 2019-02-23 15:16:32 -0500 fujitsu backend v134 rewrite init_vpd for scanners which fail to report overscan correctly update description and man page to match rebuild po files with updated line numbers commit 057a10e9842997aeb7166fcd7a255390d047d3d9 Author: m. allan noah Date: 2019-02-23 15:14:34 -0500 move three scanners to unsupported.desc These three machines are unsupportable by fujitsu backend. Remove them from fujitsu backend files. commit 941f6bb3808fd45760ab0cf0fa0cf53a6f197986 Author: m. allan noah Date: 2019-02-23 14:54:48 -0500 remove nonexistent scanners the fi-6125 and 6135 were never released, though they showed in some old versions of the windows driver. here, we remove them. commit b608751b82cad09a82d816dcb2210c5352996bcd Author: m. allan noah Date: 2019-02-23 14:50:49 -0500 Fix a few typos commit ff04ede4adb4f0267351150122fa63fea354f0bb Author: Rolf Bensch Date: 2019-02-20 19:51:57 +0100 pixma: Canon PIXMA G2000 is working reported by William Bombardelli da Silva commit 7521ff5b339c1513560014f1885742f57cfa5b36 Author: Rolf Bensch Date: 2019-02-20 19:25:46 +0100 pixma: Canon imageRUNNER 1133 is working See issue sane-project/website#15 commit c9c0d956c60a9e7d36bdfbdcdfdf1340499d2edd Author: Rolf Bensch Date: 2019-02-20 19:14:19 +0100 pixma: backend version 0.21.0 commit 7bbc9bf1c76c9ce1a36d3c771e38d8585c724d86 Author: Rolf Bensch Date: 2019-02-20 19:12:55 +0100 pixma: new scanner CanoScan LIDE 300 See issue sane-project/website#18 commit 0d193b838b49335c848ad2b6e0d5edee95796837 Author: Rolf Bensch Date: 2019-02-20 19:00:32 +0100 pixma: fix manpage section commit ab1b746c69e2490102fd2cec36f7489b797b4bd4 Author: Rolf Bensch Date: 2019-02-20 18:47:24 +0100 pixma: add button support for CanoScan LiDE400 commit c784e82d65689fd1e315bb814e381959a56d2b98 Merge: 2b0c7a3170a6 d9784940968d Author: Rolf Bensch Date: 2019-02-20 17:44:44 +0000 Merge branch 'master' into 'master' pixma: Added Canon LiDE 400 See merge request sane-project/backends!36 commit d9784940968d4dec9ffc4b1810b01729bb9270a2 Author: pekhterev Date: 2019-02-20 17:44:44 +0000 pixma: new scanner CanoScan LiDE 400 See merge request sane-project/backends!36 commit 2b0c7a3170a6ef0ab74bbf652069aa06a069e8c6 Author: Alex Belkin Date: 2019-02-05 15:19:27 +0300 xerox_mfp: Add Samsung C480W usb id Reported in #54 by Lambrigts walter. commit 78ffe3235db28a1048561d32267dc69fdc3b79d1 Merge: 66fed7c4bfb2 fd18d197f834 Author: Alex Belkin Date: 2019-02-05 15:08:17 +0300 Merge branch 'Piraty/backends-samsung-m2070' commit fd18d197f8346ebee2841fc0ddbd5bbca6e8649e Author: Piraty Date: 2019-02-03 15:06:59 +0100 xerox_mfp: Add Samsung Xpress M2070 Device is tested with latest sane (1.0.27) on several Linux distributions (Void Linux + Ubuntu), using the proposed addition. Frontends used to test are: simple-scan and xsane commit 66fed7c4bfb28fb0a97abb470751636ada871c82 Author: Olaf Meeuwissen Date: 2019-02-02 10:38:35 +0900 Bump Alpine build from 3.8 to 3.9 commit 4354fc7f6da2ee65cbe940a715f11abecdad3f49 Merge: 93340afddfbc 00d31e14f8a5 Author: Olaf Meeuwissen Date: 2019-01-21 11:51:48 +0000 Merge branch '7-discolored-bar-on-scan-canolide-200-from-1-0-25-onwards' into 'master' Restore slow_back_home use, disable rewind for gl847 Closes #7 See merge request sane-project/backends!34 commit 00d31e14f8a5339a2756af29697ec3c2ff6e7d4d Author: Olaf Meeuwissen Date: 2018-12-16 22:25:12 +0900 genesys: fix [-Wunused-function] compiler warning commit d609de285b710ca0a9c76c653abee4b8f4315f57 Author: Olaf Meeuwissen Date: 2018-12-16 21:56:52 +0900 Restore slow_back_home use, disable rewind for gl847 See #7. commit 93340afddfbc4085a5297fe635b65dd7f7f3ef05 Author: Bernhard Übelacker Date: 2018-12-17 00:05:43 +0100 mustek_usb2: Avoid stack smashing. Fixes #35 Use a properly sized variable in call to sanei_usb_{read,write}_bulk. Debian-Bug: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=886777 Debian-Bug: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=907972 commit a8fb090f560aa8f15df137c4162090524fd31323 Author: Rolf Bensch Date: 2019-01-03 12:12:28 +0100 add missing development package to INSTALL.linux commit 99abf804109b330a77a4bf7c74e37df965698138 Author: Rolf Bensch Date: 2019-01-01 14:25:23 +0100 pixma: backend version 0.20.0 commit 8fdc6f79eaa050d60257f9f541382ffa1ebe0aa4 Author: Rolf Bensch Date: 2019-01-01 14:24:41 +0100 Revert "pixma: backend version 0.19.0" This reverts commit dd6e2632418d46baaed6d6f5aec1d4c8fc625d9f. commit 46ef5ffa4198847e306aae8047fe04bf53ab5aa7 Author: Rolf Bensch Date: 2019-01-01 14:24:27 +0100 pixma: update copyright commit 107f334d7f0693756dfc148e05265e78da75816a Author: Rolf Bensch Date: 2019-01-01 14:20:46 +0100 pixma: update copyright commit dd6e2632418d46baaed6d6f5aec1d4c8fc625d9f Author: Rolf Bensch Date: 2019-01-01 14:14:19 +0100 pixma: backend version 0.19.0 commit ff021ddcf919039a95d75f1fb87c05332264274e Author: Rolf Bensch Date: 2019-01-01 14:13:58 +0100 pixma: new scanner Canon imageCLASS D550 commit ac83c8110c47659993266dae3de6aafb29308716 Merge: 36648cba52c9 d71f5e4bc875 Author: Gerhard Jäger Date: 2018-12-31 14:13:26 +0000 Merge branch 'plustek-fix-color-banding-64bit' into 'master' backend/plustek: fix "color-banding" on 64bit Closes #42 See merge request sane-project/backends!33 commit d71f5e4bc87500c2de8e4297e140bab09e3f6272 Author: Michał Wróbel Date: 2018-12-09 22:21:18 +0100 backend/plustek: fix "color-banding" on 64bit Fixes https://gitlab.com/sane-project/backends/issues/42 commit 36648cba52c90df11ee5a6c3d3a4a7506b0cc7cd Merge: 2d14283d0c44 47f9b1eeed2d Author: Gerhard Jäger Date: 2018-12-31 14:09:56 +0000 Merge branch 'lide700' into 'master' Fix a bug with Canon LiDE 700 where bright areas overflow See merge request sane-project/backends!32 commit 47f9b1eeed2deb1f0d0f63c25f83dab5aeea0c8d Author: Mark Hills Date: 2018-12-01 21:01:57 +0000 Fix a bug with Canon LiDE 700 where bright areas overflow On this scanner, white areas are easily seen to become yellow, implying some kind of integer overflow in the blue channel first, followed by others. I can't be 100% sure this is the correct fix without knowing more about the design or having a data sheet; this "target_code" was established experimentally. This is a fix to issue #43. commit 4423421806d727885918b81f94fde1d21409059e Author: Mark Hills Date: 2018-12-01 21:00:47 +0000 A switch statement prepares us for additional entries Avoid the negative 'if' case which is harder to reason about. Preparing for new entries to be added here. commit 2d14283d0c443cfd505088f2503b6e59d47b192d Author: Rolf Bensch Date: 2018-12-20 12:57:14 +0100 pixma: backend version 0.19.0 commit 2f3cdcb58089ca62e36151d687bf29839882f748 Author: Rolf Bensch Date: 2018-12-20 12:56:47 +0100 pixma: new scanner Canon PIXMA TS9500 Series commit 08a638dc319a9f10810f2653002c7f348dd492d6 Author: Nico R Date: 2018-10-06 17:50:47 +0200 xerox_mfp: Add Samsung Xpress C1860FW Device status is “untested”, because it was not really tested _in depth_. Basic scanning of a number of pages using the automated feeder worked. See https://alioth-lists.debian.net/pipermail/sane-devel/2018-October/036411.html commit dc3e6e6e41f4698c0676bb8d0119777ed91d287a Author: Olaf Meeuwissen Date: 2018-12-08 20:33:20 +0900 .gitignore: `make check` artifacts commit 866c451edb420855c6d9dff6b7a6c21a0a2d96ce Author: Olaf Meeuwissen Date: 2018-12-08 20:29:22 +0900 Add a Fedora 29 build, drop the Fedora 27 one commit b72e2bc758a9d6a93b25083f8f4ac73dadff1e86 Author: Olaf Meeuwissen Date: 2018-12-08 20:24:00 +0900 utsushi.desc: Sync with upstream commit dc8b27c1eff4617596f7b9304ef743d415791c26 Author: Olaf Meeuwissen Date: 2018-11-12 21:14:18 +0900 Add new maintainer to genesys backend commit 5c048d58df598c31e6918ad19179fa7131f56b43 Author: Olaf Meeuwissen Date: 2018-11-09 20:31:29 +0900 Mention commit access for ricoh2 backend maintainer. [skip ci] commit 0af6149aa8799f0d0b08570af34a99c3de63eaba Merge: 6f797d94e572 9cf0de7559fe Author: Olaf Meeuwissen Date: 2018-11-04 11:33:33 +0000 Merge branch 'master' into 'master' Add new backend ricoh2 See merge request sane-project/backends!20 commit 9cf0de7559fe68a40db8e9f7421696e6d8c90c03 Author: Stanislav Yuzvinsky Date: 2018-11-02 23:12:44 +0300 Address review comments commit d62ba623b2118ca009a8491e40daedbbd643f267 Author: Stanislav Yuzvinsky Date: 2018-09-22 01:00:06 +0300 Add new backend ricoh2 commit 6f797d94e572e333341d11a32aa37a6b36018584 Author: Rolf Bensch Date: 2018-10-28 12:29:36 +0100 pixma: backend version 0.18.2 commit 1675697366e0d69edf1e413265c6fc15e6f3ab97 Author: Rolf Bensch Date: 2018-10-28 12:28:11 +0100 pixma: MP490 Series doesn't need special image formatting @ high dpi commit 5fee88415b175054f38a9a31967c007c8b36d20f Author: Rolf Bensch Date: 2018-10-28 12:11:49 +0100 pixma: Canon imageCLASS MF8030 is working commit 54aa154d918245d5090fc342d4d1b00cf12a2477 Author: Rolf Bensch Date: 2018-10-24 20:25:40 +0200 pixma: backend version 0.18.1 commit 8818879297155abb8f91b242b98b62a4d0024331 Author: Rolf Bensch Date: 2018-10-24 20:24:00 +0200 pixma: Canon PIXMA E510 is working commit e41090725370cf0abaf15cd9c34ad7e711fece3f Merge: ef85977ec4ba 1846b0381e06 Author: Rolf Bensch Date: 2018-10-24 20:05:00 +0200 Merge branch 'kapcake/backends-patch-1' commit 1846b0381e06359033f122986f3296afa3445677 Author: Laurent Kap Date: 2018-10-17 21:30:17 +0000 Adds button support for Pixma MG5350 commit ef85977ec4ba55254dba31752c84b4e160b63923 Author: Rolf Bensch Date: 2018-10-12 12:33:46 +0200 pixma: Canon PIXMA TS3100 Series is working commit 54c42b04a8ccfce2c140782007a11d2b3a1627fe Merge: f633bad6ec1d 05400ad69b80 Author: Gerhard Jäger Date: 2018-10-11 07:36:54 +0000 Merge branch 'lide80-increase-bright-point' into 'master' backend/genesys: Increase excessively low bright point on Lide 80 calib See merge request sane-project/backends!10 commit 05400ad69b8047f0a2320a120519941fa99faf36 Author: Povilas Kanapickas Date: 2018-07-21 03:37:45 +0300 backend/genesys: Increase excessively low bright point on Lide 80 calib commit f633bad6ec1dd4b306ea245dc10a602020df7bba Merge: 030153b39b82 92c40c44b9a6 Author: Gerhard Jäger Date: 2018-10-11 07:34:14 +0000 Merge branch 'lide80-reduce-dark-area-offset' into 'master' backend/genesys_gl841: Reduce extra dark area offset for Lide 80 See merge request sane-project/backends!9 commit 92c40c44b9a6c7ce7a5cc865db1f4b61fb1bd3c0 Author: Povilas Kanapickas Date: 2018-07-21 03:37:44 +0300 backend/genesys_gl841: Reduce extra dark area offset for LIDE 80 commit 030153b39b82aca45b724b940aa107fdfe0c4b37 Merge: 6abb072ded1d 065855e1da5c Author: Gerhard Jäger Date: 2018-10-11 07:33:24 +0000 Merge branch 'lide80-disable-ledadd' into 'master' backend/genesys_gl841: Disable LEDADD on Lide 80 since it's broken there (fixes issue #12) Closes #12 See merge request sane-project/backends!7 commit 065855e1da5c088e561580c26d96ad2b051685c4 Author: Povilas Kanapickas Date: 2018-07-21 03:37:46 +0300 backend/genesys_gl841: Disable LEDADD on Lide 80 since it's broken there commit 6abb072ded1daea860c64375b140287fb180bfbd Merge: c980c73259de a64f20964389 Author: Gerhard Jäger Date: 2018-10-11 07:29:59 +0000 Merge branch 'master' into 'master' genesys: Add buttons to LiDE 60 See merge request sane-project/backends!6 commit a64f2096438970ca0c1b9a86f363a0f3ce8476ef Author: iosabi Date: 2018-07-02 22:48:02 +0200 genesys: Add buttons to LiDE 60 Canon LiDE 60 has four buttons. These are already supported in the GL841, we just needed to add them to the config. Tested it with scanbd. commit c980c73259de59b5af1785068b21c5417173e47e Author: Olaf Meeuwissen Date: 2018-10-11 00:46:55 +0900 Remove cached lists of supported devices Caching was introduced in e7d9779dfc3748f8f205585fa09ebc7181874642. Fixes sane-project/website#7. commit e2bfb2e00ad5f87d4f905218daa7d066f908c28b Author: Olaf Meeuwissen Date: 2018-10-11 00:30:20 +0900 Bump Alpine build from 3.7 to 3.8 commit b31ef5e4aafdc02eb019039a7a5f1b4e80e874ef Author: Rolf Bensch Date: 2018-10-08 21:25:47 +0200 pixma: backend version 0.18.0 commit e8bdbe1f50fd44a87f6f95c40577ac1ea462b01f Author: Rolf Bensch Date: 2018-10-08 21:24:51 +0200 pixma: new scanners Canon MAXIFY MB5100, MB5400 Series commit b5e92b8b8de83985f25484adcdeb5b2a4ecd0df9 Author: Rolf Bensch Date: 2018-10-08 21:24:04 +0200 pixma: new scanners Canon PIXMA TR7500, TR8500, TS3100, TS5100, TS9100 Series commit 8ba4c51bae5df65b67b692da9ade5e7808cee9eb Author: Olaf Meeuwissen Date: 2018-10-02 22:09:18 +0900 utsushi.desc: SYnc with upstream commit 30579b0f54dc079549bdf8190e59477814852799 Merge: 428dc5657d4e de8ab7560898 Author: Olaf Meeuwissen Date: 2018-10-02 11:27:47 +0000 Merge branch 'for-upstream/backend_avision_gitignore' into 'master' add .po~ to gitignore See merge request sane-project/backends!24 commit de8ab7560898550cbb6e3dcac655b46c52adaeaf Author: Michael Niewoehner Date: 2018-09-20 19:26:43 +0200 add .po~ to gitignore commit 428dc5657d4e02d8eefabd0231e8117e73c17ccd Merge: 6bdb2486aeba 37bbbed3cbe8 Author: Olaf Meeuwissen Date: 2018-10-01 21:17:45 +0000 Merge branch 'check-po.awk-typos2' into 'master' check-po.awk: Fix minor typos See merge request sane-project/backends!27 commit 37bbbed3cbe8ae80035f356d41e144c2e4255b3a Author: Simon J Mudd Date: 2018-09-30 20:29:16 +0200 check-po.awk: Fix minor typos commit 6bdb2486aeba08a4d75964b325da5a75b14c68bf Merge: 04fc0ca8bd7b cfbc744826f5 Author: Rolf Bensch Date: 2018-09-29 20:39:05 +0200 Merge remote-tracking branch 'origin/master' commit cfbc744826f5ca29936fd00ae05b385819ce9988 Author: Alex Belkin Date: 2018-09-21 21:35:03 +0300 xerox_mfp: Change support status of SCX-4623FW after user feedback Also fix spelling of previous commit. commit 22af4c8e251e34d28c13db79da8b5743e330a030 Author: Alex Belkin Date: 2018-09-20 15:11:16 +0300 xerox_mfp: Update xerox_mfp.desc with useful comments commit 9ee0d41f4a0529fc2dd17b976337f6902053acff Merge: 8bc331761a88 46e59346ea4f Author: Olaf Meeuwissen Date: 2018-09-17 00:36:35 +0000 Merge branch 'master' into 'master' Add Hebrew translation by Elishai Shkury See merge request sane-project/backends!17 commit 46e59346ea4f24210f50d82052e83486287004b0 Author: Yuri Chornoivan Date: 2018-09-16 22:57:27 +0300 Add Hebrew translation by Elishai Shkury commit 04fc0ca8bd7bd64a1d4fbee6790d4353a79275a4 Author: Rolf Bensch Date: 2018-09-29 20:37:59 +0200 pixma: TS6100 Series is working commit 3437b6e48ed235d5fcec6a4625eddf6bb52b77df Author: Rolf Bensch Date: 2018-09-22 12:54:05 +0200 pixma: i-SENSYS MF730 Series is working commit 8bc331761a88491c80524cd7d4b1ae64c1fccc92 Merge: 0b811391a9b3 eceeaa80afcf Author: Olaf Meeuwissen Date: 2018-09-16 13:07:31 +0900 Merge branch 'master' of https://gitlab.com/bellaperez/backends commit eceeaa80afcfacb541f653380d4fbadba6c94e9c Author: Antoni Bella Pérez Date: 2018-09-15 22:28:48 +0200 Add a new transtalion for Valencian Language commit 92dc8789be48297c4dca68b1150dfb0c83603c90 Author: Antoni Bella Pérez Date: 2018-09-10 23:30:40 +0200 Sync source code commit 4bea45b632e68d7b6aa2972523d664fe42140a08 Author: Antoni Bella Pérez Date: 2018-09-10 00:43:34 +0200 Fixes from Softcatalà.org mailing list commit f0f349b0ce2088dac9c8762959f9bf2f93313e9b Author: Antoni Bella Pérez Date: 2018-09-07 15:22:17 +0200 Last fixes reported commit 36d1618b76d692e58523e02e869d8eab0834c413 Author: Antoni Bella Pérez Date: 2018-09-07 14:52:27 +0200 100% translated, whaiting mailing list fixes... ;-) commit 210ea90b107337a79e4505c372124db4a6ad41b8 Author: Antoni Bella Pérez Date: 2018-09-04 23:26:43 +0200 More updates commit 7f551a79e33df168b5faaa73ea0bcb7d5b8f7269 Author: Antoni Bella Pérez Date: 2018-09-03 01:23:23 +0200 Updates and fixes for initial translation commit 617037837aff3952dae719265250f4a1e3d657d6 Author: Antoni Bella Pérez Date: 2018-09-02 21:33:36 +0200 Fixes from SoftCatalà.org commit ceb6b2787e5f904c6a652988ef8f77f44ef4d826 Author: Antoni Bella Pérez Date: 2018-09-02 21:06:41 +0200 Last fixes for initial translation commit 4a73b1cb0c0184cb7340fb398d39c63f649341cb Author: Antoni Bella Pérez Date: 2018-09-01 23:26:55 +0200 KDE fixes (pology) for initial translation commit 7459debf0ed2de477111f2246f15c24758e6b19e Author: Antoni Bella Pérez Date: 2018-09-01 20:52:05 +0200 Update initial translation commit a1f3fbcef869e865aef7a6817a12aba3f8cda58e Author: Antoni Bella Pérez Date: 2018-08-28 19:55:01 +0200 Sync all languages (test Catalan translations) commit 2143773b063e58754c9fe56a6cc5c9cfbf676964 Author: Antoni Bella Pérez Date: 2018-08-28 19:31:57 +0200 Update initial translation commit 158898f2a4e26467f84ff15d01e0da8eeec38c7f Author: Antoni Bella Pérez Date: 2018-08-28 00:52:26 +0200 More updates for initial translation commit c187b6c36334789f9d71f1e74f89615725a9f068 Author: Antoni Bella Pérez Date: 2018-08-27 02:05:11 +0200 Updates for initial translation commit a76b0b21543f38996fb6a516f7d8e25cb80d08c7 Author: Antoni Bella Pérez Date: 2018-08-21 14:10:54 +0200 Add an initial translation commit 0b811391a9b3bacfe17f8760da16b96663ba647e Author: Rolf Bensch Date: 2018-09-13 19:07:42 +0200 pixma: new scanner i-SENSYS MF410 Series commit e8b11ec2eb948d5b13bc05287ddd58aad24bc413 Author: Rolf Bensch Date: 2018-09-13 18:43:55 +0200 pixma: backend version 0.17.51 commit 3b5223b16b043bba1c74a70df87047c889c7bc88 Author: Rolf Bensch Date: 2018-09-13 18:43:31 +0200 pixma: new scanner imageCLASS D570 commit 01533e2e3e99e72ec29272a039ef6997360e99e6 Author: Rolf Bensch Date: 2018-09-12 22:09:06 +0200 pixma: backend version 0.17.50 commit dbecf846d5c040eb0688eba2af21a168dbe29114 Author: Rolf Bensch Date: 2018-09-12 22:08:41 +0200 pixma: new scanner TS6100 Series commit 4c562f8142b0b67e8646975f07a3d0ebb588703f Author: Olaf Meeuwissen Date: 2018-09-09 09:54:38 +0900 Document that Ethernet works for the Canon MB5300 Series See https://alioth-lists.debian.net/pipermail/sane-devel/2018-September/036346.html commit fd8aedea4c475f1c8c51c3efe28bc81495ece61c Author: Olaf Meeuwissen Date: 2018-09-08 20:15:15 +0900 List Canon MB5300 Series as supported See https://alioth-lists.debian.net/pipermail/sane-devel/2018-September/036340.html commit 3ee997194a7026085b3c390289bfec89fb81e40c Merge: 2f944738803f 1484b47582d2 Author: Olaf Meeuwissen Date: 2018-09-06 12:30:34 +0000 Add support for Plustek OpticSlim 500+ See merge request sane-project/backends!4 commit 1484b47582d20eab6a78a1f0461f5e90ceb7faa8 Author: Mikhail Strizhov Date: 2018-09-06 12:36:26 +0300 add support of Plustek OpticSlim 500+ commit 2f944738803fbaf90a37e3960b06f565274c1174 Merge: 1d58e23c91bc 32d05417c898 Author: Olaf Meeuwissen Date: 2018-09-04 12:24:52 +0000 Merge branch 'master' into 'master' Update Ukrainian translation See merge request sane-project/backends!14 commit 32d05417c898fad4985f3cb87b465826ec99fd60 Author: Yuri Chornoivan Date: 2018-09-02 20:23:47 +0300 Update Ukrainian translation commit 1d58e23c91bce08693ae75ae18d66c938927b6b7 Author: Olaf Meeuwissen Date: 2018-09-02 20:52:51 +0900 utsushi.desc: Sync with upstream commit 5230430a572c078605cf9cd3894de2ebb80b1956 Author: Rolf Bensch Date: 2018-08-27 21:57:24 +0200 pixma: backend version 0.17.49 commit eb11c2fc94ccc7ae0e002a6a488546dd31f17171 Author: Rolf Bensch Date: 2018-08-27 21:56:48 +0200 pixma: new scanner i-SENSYS MF731/733 commit 082cf4cdf3cad1f97f4d1a5c0ffb1ad2c69ab9df Merge: 1a2a8ee744ce d6aace7d5167 Author: Olaf Meeuwissen Date: 2018-07-22 13:01:55 +0000 Merge branch 'hp5590-fix-compile-error' into 'master' hp5590: Fix sanei_net.h include path. See merge request sane-project/backends!8 commit d6aace7d5167b39d474f57c5671f0a400cad0bc0 Author: Povilas Kanapickas Date: 2018-07-21 03:37:43 +0300 hp5590: Fix compile error commit 1a2a8ee744ceacdb2a46906e626327c5d9496af6 Author: Ilia Sotnikov Date: 2018-07-15 14:09:49 +0300 backend/hp5590.c, backend/hp5590_cmds.c: + Files header have been expanded to mention of scanbd integration by Damiano Scaramuzza and Bernard Badr commit bfc63709a796704005642edfacf0b4bb92732597 Merge: fc88e5251130 1649d5bfea36 Author: Ilia Sotnikov Date: 2018-07-15 11:00:18 +0000 Merge branch 'master' into 'master' HP5590: scanbd integration is ready. See merge request sane-project/backends!5 commit 1649d5bfea36bc2209a7c673df333ac6c71ac376 Author: Bernard B Badr Date: 2018-07-15 11:00:18 +0000 HP5590: scanbd integration is ready. commit fc88e5251130ef39a7e56930c3323aa1d8d7762d Author: Olaf Meeuwissen Date: 2018-06-13 21:23:43 +0900 utsushi.desc: Sync with upstream commit a80f3b575ea7f2ef90efb1368949a7d0cf451366 Author: Ilia Sotnikov Date: 2018-06-11 11:11:53 +0300 backend/hp5590.c, backend/hp5590_cmds.c: + Files header have been expanded to mention of ADF page detection and high DPI fixes by Bernard Badr * Use C-style comments instead of C++ ones commit 8c912284299d37dd32ea8e3216d16cada180928e Merge: 85efea28aeb1 b1da51f7fe04 Author: Ilia Sotnikov Date: 2018-06-11 07:56:28 +0000 Merge branch 'master' into 'master' HP5590: Fixed ADF handling, fixed 48bit color mode, fixed 2400 dpi color layer shift. See merge request sane-project/backends!3 commit b1da51f7fe04f6186dc0f1826bd7c0e67f1e41f8 Author: Bernard Badr Date: 2018-06-11 07:56:28 +0000 HP5590: Fixed ADF handling, fixed 48bit color mode, fixed 2400 dpi color layer shift. commit 85efea28aeb14c0f769a25d527055636aa9ec915 Author: Olaf Meeuwissen Date: 2018-05-31 22:15:04 +0900 Add a Fedora 28 build, drop the Fedora 26 one commit ad20558bb23195f3a5c7cbcc9cb81af3e05dcd19 Author: Alex Belkin Date: 2018-05-18 01:51:01 +0300 xerox_mfp: Blacklist Samsung M288x Series from jpeg mode Fix issue #315839 reported by Jan Christoph Ebersach https://alioth.debian.org/tracker/?func=detail&group_id=30186&aid=315839&atid=410366 commit 54a55700f66eaeef827de3ab2dbf802dd32ea697 Author: Alex Belkin Date: 2018-05-18 01:31:46 +0300 xerox_mfp: blacklist SCX-4500W from jpeg mode Fix issue #315876 reported by Bernard Cafarelli https://alioth.debian.org/tracker/?func=detail&atid=410366&aid=315876&group_id=30186 commit 4accdae4edbaf4ab88dcf458282c82189f8e39a3 Author: Rolf Bensch Date: 2018-05-16 21:29:50 +0200 pixma: backend version 0.17.48 commit 15aebab5149a767e02e25b47abb7145f7fa6f668 Author: Rolf Bensch Date: 2018-05-16 21:29:14 +0200 pixma: Canon MAXIFY MB2100 and MB2700 Series support 1200 dpi commit 6a7e4141eb53876511829b5bb87aae311d828402 Merge: 9d315bc2f71c 5e5183c84efe Author: Rolf Bensch Date: 2018-05-16 18:08:42 +0000 Merge branch 'mb2100_adf_oop' into 'master' Return NO_DOCS after last ADF page, not IO_ERROR See merge request sane-project/backends!2 commit 5e5183c84efe4d71675a3d4cc968a4eb09511a7d Author: Earle F. Philhower, III Date: 2018-05-15 20:16:44 -0700 Return NO_DOCS after last ADF page, not IO_ERROR When scanning from ADF in MP150 based scanners, after the last page is scanned the XML session-close was sent. However, when using the ADF, if the frontend called sane_start/sane_read(), it would try and send an XML command that was not valid after the session abort. This would give an IO error up through the stack. Now, check if ADF scanning is happening and on reads after the last page return SANE_STATUS_NO_DOCS on read. and abort. Finally, minor fix of max DPI for Pixma MAXIFY MB21xx/27xx to 1200 DPI. commit 9d315bc2f71c48622511c49fdeeb756b71a69e38 Author: Robert A. Schmied Date: 2018-05-14 21:28:40 +0900 Fix typo in debugging statement commit a1c42247ab36682d9664affb1dab5933dbd3fd20 Author: Rolf Bensch Date: 2018-05-12 20:54:38 +0200 pixma: backend version 0.17.47 commit 72f9411257345c941459703f3e08f79d8ea8f46e Author: Rolf Bensch Date: 2018-05-12 20:55:16 +0200 pixma: new scanners Canon Maxify MB2100 and MB2700 Series commit 8c340a34a49312a29f959e22286c3b83be9bf19b Merge: c6bfe88c31f3 d90f9054c6fd Author: Rolf Bensch Date: 2018-05-12 20:35:41 +0200 Merge branch 'earlephilhower/backends-pixma_jpeg' merge request #1 commit d90f9054c6fd2f4ecad78142411bef91c449c19f Author: Earle F. Philhower, III Date: 2018-05-09 13:40:10 -0700 Add JPEG scanning, MB2100/2700 to PIXMA The Maxify scanners seem to only be able to return JPEG data for ADF sources. Attempting to send a gamma LUT will result in an error on sane_start when trying to use the ADF. Flatbed scanning is unaffected and runs fine with LUTs like prior models. This patch adds support to the PIXMA backend for returned JPEG scans and keys it off of the new capability, ADF_JPEG. Tested on a Maxify MB2120 using the ADF and the flatbed scanner. This may also fix other Maxify MB* models where it seems the ADF does not with with the prior code, but I only have the MB2120 to test. commit c6bfe88c31f3f582fdc0bea08199282efceb3914 Author: Olaf Meeuwissen Date: 2018-04-18 21:36:00 +0900 Update mailing lists addresses to new location commit 4ade295ab1c1d3bbd23ae3fe2118424c2bb5c799 Author: Olaf Meeuwissen Date: 2018-04-18 21:12:44 +0900 Update mail archive links to point to new location commit d22f36f8cc759d9ee978a438be08b1bd57bfe170 Author: Olaf Meeuwissen Date: 2018-04-10 19:41:24 +0900 utsushi.desc: Sync with upstream commit f17585e1595e50ee0bbdd36210bc141e1a137f53 Author: Olaf Meeuwissen Date: 2018-03-26 21:31:57 +0900 *.desc: Mark backends that have become unmaintained commit 872a7521f5e0177cacfd3f800105237fd596fd9e Author: Olaf Meeuwissen Date: 2018-03-26 21:30:57 +0900 AUTHORS: Update git access status following project member changes commit 07f1351df510ea9453811b9137e71b8c26b4ac38 Author: Olaf Meeuwissen Date: 2018-03-20 21:48:24 +0900 sane-p5.man: Point to new issue tracker location commit 2d374b0a690fe4e8116fa2714ef426cc3fd536d1 Author: Olaf Meeuwissen Date: 2018-03-16 21:55:35 +0900 ltmain.sh: revert removal of SANE specific tweaks commit a78969384b2b7ac233694e7e21add44ad055dbea Author: Olaf Meeuwissen Date: 2018-03-16 21:54:42 +0900 autotools: Sync derived files This was done by running libtoolize --copy autoreconf --force --install on Debian GNU/Linux 9.3. commit c8d8f05256523cb0aafe24309ea2b3c93bea20f2 Author: Olaf Meeuwissen Date: 2018-03-16 20:56:47 +0900 Bump autofoo dependencies to match those present in Debian 9 This gets rid of the warning that AM_PROG_MKDIR_P is deprecated and should be replaced with AC_PROG_MKDIR_P warning. commit f162fad98c55f792828acc05ff32c3e036490ea7 Author: Olaf Meeuwissen Date: 2018-03-15 09:22:53 +0900 Bump the "canonical" build environment to Debian 9 commit 5944c15d03b6aadaa8691cd0b7615b551f60d196 Author: Olaf Meeuwissen Date: 2018-03-15 09:41:58 +0900 Bump Alpine build from 3.6 to 3.7 commit 080702ae7df21e882d2e135fc35a0bbde04aca8b Author: Olaf Meeuwissen Date: 2018-03-15 09:04:31 +0900 Add a Fedora 27 build, drop the Fedora 25 one commit a5ae2dad1d4202911be927933b8f8d6a8dde6413 Author: Rolf Bensch Date: 2018-03-11 12:08:18 +0100 pixma: backend version 0.17.46 commit 12dbae0fc5d08c597deb85150521945c5b806488 Author: Rolf Bensch Date: 2018-03-11 12:06:51 +0100 pixma: MB2300 Series has no duplex document scanner commit 4f156894db02d3232a04ab17ca49da84540e7d13 Author: Rolf Bensch Date: 2018-03-11 11:13:43 +0100 pixma: add button support for MB2300 Series commit 393cf040b950b274090372b58693227a4455aa16 Author: Olaf Meeuwissen Date: 2018-03-10 10:14:06 +0900 Add doxygen generated sanei HTML documentation to archive commit fadb3d809ae850b5f260550d149713dde763945d Author: Olaf Meeuwissen Date: 2018-03-09 22:36:39 +0900 Add doxygen generated sanei HTML documentation to artifacts commit 82b9d215fc6d943cb9245c8eded11f51f4f1f154 Author: Olaf Meeuwissen Date: 2018-03-09 22:23:12 +0900 Update configuration files to match newer doxygen version The comments have been dropped to cut down on future diff chatter. Please refer to the doxygen documentation instead. commit 9b0db823ede07e1bb0cb867b64b87d019cff5741 Author: Olaf Meeuwissen Date: 2018-03-07 21:38:56 +0900 Make absolute URLs in the sane-desc HTML output site-relative commit 8849137abb3f7ac2836609c69ee2c89af7538ee8 Author: Olaf Meeuwissen Date: 2018-03-05 20:21:58 +0900 Make root-relative hrefs/src properties site-relative This allows for hosting content on any level below a domain. commit 48f950b95a1a851e8656c0da53ddd553f5f397c9 Author: Olaf Meeuwissen Date: 2018-02-27 21:38:06 +0900 utsushi.desc: Sync with upstream commit bb56d44eb99cc8fc122728995fc8a2aa68aa83f5 Author: Olaf Meeuwissen Date: 2018-02-25 17:01:10 +0900 CI: Actually move the *html files commit ef0610ce2e8155b1edfca45dc03e653f80cf66c3 Author: Olaf Meeuwissen Date: 2018-02-21 21:09:59 +0900 CI: Generate device support status HTML in after_script commit 936a45b4aa3b818c5c963109e06d7cece5c19af1 Author: Olaf Meeuwissen Date: 2018-02-21 20:50:07 +0900 Include device support status HTML pages in archive target This is meant to make it easier for the website to get the latest information up. commit a84290a8f96f2a93ab4ac4cdc36e7e771d4a3e48 Author: Olaf Meeuwissen Date: 2018-02-20 21:24:56 +0900 sane.man: Fix typo in stv680 backend name commit f38a1731a643e0c54f27f065ff35a4d66635f68d Author: Olaf Meeuwissen Date: 2018-02-09 18:37:21 +0900 Point to new GitLab.com repository location commit c32c78fe8e19835cde4bf7b3742d488e3ec9092b Author: Olaf Meeuwissen Date: 2018-02-08 21:58:45 +0900 epson2.desc: Add ET-2650/L495 as supported Information courtesy of Ed Hamrick. commit 1b2e4b0b3bef6dc07e750bdb9288f593faf146a2 Author: Olaf Meeuwissen Date: 2018-02-08 21:53:02 +0900 include/sane/sanei_ir.h: Adjust doc params to match code commit 0e7b3b79251274cff6c136cb54785e7ccc047966 Author: Olaf Meeuwissen Date: 2018-02-08 21:52:14 +0900 utsushi.desc: sync with upstream commit 3c6a3d499236e7f8ad9e9f7d43d2b22f26d17480 Author: Rolf Bensch Date: 2018-02-04 13:12:34 +0100 pixma: Canon MAXIFY MB2300 Series is working - reported by Patrick Roncagliolo commit b487db3af0251171e43d8a5458f9200954cf79a9 Author: Rolf Bensch Date: 2018-01-04 19:52:16 +0100 pixma: Canon i-SENSYS MF620 Series is working reported by Markus Heiser commit 74898c4330cc25521ddbf008aa18db75fc00af1b Author: Olaf Meeuwissen Date: 2018-01-03 16:13:16 +0900 Fix array indexing This fixes a glaring oversight in 39ceeae6. Thanks to James Ring for reporting this. commit b6873cfa5a5e57e1ba60f90b137cfa4400bfb99a Author: Rolf Bensch Date: 2018-01-03 21:01:32 +0100 pixma: backend version 0.17.45 commit a873a5e444a067c6d2ac7f813b2e97e31e98c582 Author: Rolf Bensch Date: 2018-01-03 21:00:51 +0100 pixma: new scanner Canon i-SENSYS MF630 Series [#315892] commit e6db7a8f14a107bcdc1edbb068cee15a34f2d030 Author: Rolf Bensch Date: 2018-01-03 20:52:39 +0100 pixma: new scanner Canon i-SENSYS MF630 Series commit 463e010f1a83063610b43b7aae6f378b8c532a07 Author: Rolf Bensch Date: 2018-01-03 20:07:33 +0100 pixma: new scanner Canon i-SENSYS MF620 Series many thanks to Markus Heiser commit 781c4ad080d1f240e763fc87f193a9349e23a23d Author: Rolf Bensch Date: 2018-01-02 14:14:05 +0100 pixma: Canon MAXIFY MB2000 Series is working, except ADF (#315538) commit e895ee55bec8a3320a0e972b32c05d35b47fe226 Author: Edward Betts Date: 2017-12-01 00:49:00 +0900 sane-find-scanner: Recognize --help as a valid option commit 214f82e36a3bbcac21053cc282dfe67ecef9a0f9 Author: Gerhard Jaeger Date: 2017-11-18 15:07:25 +0100 [#315864] CanoScan LiDE25 -> CanoScan LiDE 25 Change LiDE descriptions and split as suggested in the ticket. While here remove outdated URL. commit d6a93b83a7dc3a3871db31ecf11adebe470b52a4 Author: Olaf Meeuwissen Date: 2017-11-18 17:35:30 +0900 epson2: Add Expression 12000XL / DS-G20000 as supported Also removed stale comment in epson2.desc. See https://lists.alioth.debian.org/pipermail/sane-devel/2017-November/035744.html commit 76600e397459e237af2a174664fe7fc1e3b5c110 Author: Olaf Meeuwissen Date: 2017-11-18 16:42:39 +0900 utsushi.desc: sync with upstream commit c51af8fd996f124151f82e13d6a6e94fcd906b94 Author: Rolf Bensch Date: 2017-11-14 22:16:40 +0100 pixma: fix typos commit 77722d754f2eb06d1a26380989dd098fcb8da533 Author: Olaf Meeuwissen Date: 2017-10-11 22:09:43 +0900 utsushi.desc: sync with upstream commit 99a98e448b603fccb438aea94a7c28ea2ca2059a Author: Olaf Meeuwissen Date: 2017-09-29 21:49:08 +0900 saned: Brush up manual page's OPTIONS section following 5288dd5f commit f9a76395da768f2bd4ee803a78ca5e5604df0c8d Author: Olaf Meeuwissen Date: 2017-09-29 21:48:14 +0900 saned: Fix typo in help message commit 5288dd5f61b32da2f4bc18950c247dc3b6e1579d Author: Luiz Angelo Daros de Luca Date: 2017-09-18 04:25:37 -0300 saned: reorganize flags, remove run_mode SANED_RUN_DEBUG Flags like -a, -d and -s have many overlap effects. This patch restricts the effect of flags to a simple action. New -u (user) flag replaces -a optional argument for running saned as a different user. The code that retrieve the user info and drop privileges migrated to runas_user(). As a side effect, PID file can be created even if getting user info fails. New -l (listen) flag sets run_mode to standalone. New -D (daemonize) flag daemonizes saned after bind. New -o (once) make saned exit after the first client disconnects. Flag -s (syslog) is gone. Previous behavior can be reproduced with '-a -d level -o -f'. New -e (stderr) flag for redirecting output to stderr, instead of syslog. Flag -d (debug) now only sets the debug level and argument is required. Previous behavior can be reproduced with '-a -d level -o -f -e'. The run_mode SANED_RUN_DEBUG and SANED_RUN_ALONE shared most of its code path. With the new flags dealing with their difference, SANED_RUN_DEBUG is gone. Flag '-a' still works as before but it can be replaced by '-l -D -u user'. Current uses of -d (debug) or -s (syslog) will break. Signed-off-by: Luiz Angelo Daros de Luca commit c9356cb184c0babeb9a8b525728e33d01e2a878c Author: Olaf Meeuwissen Date: 2017-09-09 15:37:13 +0900 saned: Fix incorrect claim about passing options to inetd etc. commit 1f68d8d8c43ff049b0c6921d3823f6c32fdece41 Author: Farid Benamrouche Date: 2017-07-22 21:34:00 +0900 saned: Cancel scan if data connection appears to have gone away The data connection timeout default to 4000ms but is configurable in saned.conf. See Alioth 315765 commit 0a62202e95983493598d3b94718bb277f5e71b2f Author: Rolf Bensch Date: 2017-09-01 12:15:40 +0200 pixma: backend version 0.17.44 commit 055c3ffa8643da6dad885bb69b08a7d4f45d1cc0 Author: Rolf Bensch Date: 2017-09-01 12:15:10 +0200 pixma: new scanners Canon PIXMA E410 Series, E3100 Series, G2000 and G3000 commit 61ca162fc069dff85964ad5352b1f2faaca692c5 Author: Rolf Bensch Date: 2017-09-01 12:07:57 +0200 pixma: fix definitions for PIXMA G4000 commit d78013afc6943b6cd86d468b045fe16fe495350d Author: Rolf Bensch Date: 2017-09-01 11:52:39 +0200 pixma_mp150: fix comments for generation 5 scanners commit fef19d7740fc1dd1c3109efe47040b1eca046fe6 Author: Rolf Bensch Date: 2017-08-31 20:34:32 +0200 pixma: backend version 0.17.43 commit 450e55c2309d372a088ef963082252fd2f27c524 Author: Rolf Bensch Date: 2017-08-31 13:27:16 +0200 pixma_mp150: send XML end of scan dialog also for generation 5 scanners commit 9b95970b917a74d4c973dc536192b7546603c9b7 Author: Olaf Meeuwissen Date: 2017-08-20 14:08:33 +0900 utsushi.desc: Sync with upstream commit 6eb2a0d10a9f5e116b8ef3aff508dfec51ca273a Author: Rolf Bensch Date: 2017-08-18 10:06:17 +0200 pixma: fix device interface info for PIXMA MG7100 Series commit 5a0e30b5e31586bf3ec5488876a7744341e16331 Author: Olaf Meeuwissen Date: 2017-08-17 21:24:14 +0900 epson2: Add the Epson XP-243 245 247 Series as supported See https://alioth.debian.org/tracker/?func=detail&atid=410366&aid=315818&group_id=30186 commit 1e87016dca2593e4d595654435fe0bbaf6311fd3 Author: Olaf Meeuwissen Date: 2017-08-12 14:14:05 +0900 epson2: Add the Epson XP-427 as supported See https://alioth.debian.org/tracker/index.php?func=detail&aid=315805&group_id=30186&atid=410366 commit 519ff57bed9391c9c178f660648669f4c77b7d3a Author: Olaf Meeuwissen Date: 2017-08-12 15:03:51 +0900 install: Make sure configuration files get created before install commit f19a41e6f7b693e55fd4e26b988e55e3a30b6eb8 Author: Luiz Angelo Daros de Luca Date: 2017-07-22 05:43:17 -0300 saned: update man for option -b commit fc57a5a6a5b9d1039915145223571a2211ce6173 Author: Luiz Angelo Daros de Luca Date: 2017-07-22 05:43:16 -0300 saned: fix --debug help message (output is stderr) Man page was correct. Signed-off-by: Luiz Angelo Daros de Luca commit f482b498a25326ae380c16f22dfdfc34ec6edfe3 Author: Jeremy Bicha Date: 2017-07-26 18:32:36 -0400 po: Fix Plural-Forms header for es and gl Fixes 315801. commit f88b5ea2b6bccf7dc356fa2c2944428f0a67d41d Author: Olaf Meeuwissen Date: 2017-07-22 15:31:42 +0900 umax_pp: Fix [-Wsign-compare] compiler warnings commit 14e701438dd87dc47b500c504702c4b5b98b5c3d Author: Olaf Meeuwissen Date: 2017-07-22 14:19:02 +0900 umax_pp: Fix [-Wunused-but-set-variable] compiler warnings commit 772f31d4a0624c00b698fc4873feafb5820c2141 Author: Olaf Meeuwissen Date: 2017-07-22 14:02:10 +0900 umax_pp: Fix [-Wunused-variable] compiler warnings The variable declaration section has been completely rewritten to follow the #ifdef structure of the function's implementation. commit 56398ead607d8bcb9deef7ca3f4d7f78478f1b4c Author: Olaf Meeuwissen Date: 2017-07-22 11:48:14 +0900 sane-usb: Document SANE_USB_WORKAROUND variable Fixes Alioth#315792. Thanks Zdenek Dohnal commit 1f8bfd69a253fa0e3af39e30d1a5ce00891550d2 Author: Olaf Meeuwissen Date: 2017-07-18 21:02:43 +0900 dell1600n_net: Fix [-Wincompatible-pointer-types] compiler warnings This casts the pointers to the type expected by the functions as per POSIX stipulations. See http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/netinet_in.h.html commit bdba3cc073055ac852bc93e126ae97f938b2d06f Author: Olaf Meeuwissen Date: 2017-07-18 20:46:01 +0900 tools/umax_pp: Fix [-Wunused-variable] compiler warning The file descriptor is only used in code conditionalised on the HAVE_LINUX_PPDEV_H preprocessor define. commit 3258b70de42f0abf891b139b01fc0a731f2974e8 Author: Rolf Bensch Date: 2017-07-16 11:46:27 +0200 pixma: fix device status for i-SENSYS MF240 Series and add comment for image width workaround (see: 611647b) commit 8f61317f00529ecb0b1e44a726266e33512759ad Author: Olaf Meeuwissen Date: 2017-07-15 22:52:00 +0900 autofoo: Sync generated files commit f18ab2e2a29058c098d7709cbad6169064b70da2 Author: Olaf Meeuwissen Date: 2017-07-15 22:48:29 +0900 plustek-pp: Replace sys/signal.h include with signal.h All other backends already `#include ` directly, without any configure time checking or problems. Fixes [-Wcc] compiler warning on Alpine. commit 68ec98d7b1ef636f4d230c81c8e1e8de7e62c66d Author: Olaf Meeuwissen Date: 2017-07-15 22:15:18 +0900 hp3500: Fix [-Wunused-function] compiler warning The sigtermHandler is only used when _POSIX_SOURCE is defined. commit d94c29a726667604f3ba1cf18185a48ad7355966 Author: Olaf Meeuwissen Date: 2017-07-15 22:06:05 +0900 autofoo: Sync generated files commit 54627bdeacf5f8bff8d622469f3c02f6f9c8ef79 Author: Olaf Meeuwissen Date: 2017-07-15 21:58:51 +0900 lib/isfdtype.c: Remove badly broken substitute The substitute completely ignores the fdtype argument and only works correctly for S_IFSOCK values. This happened to be the only way the function was invoked but for safety's sake this has been replaced by a local implementation that does pretty much the same thing, without the misleading bit. Found courtesy of a [-Wunused-parameter] compiler warning on Alpine. commit c754d440eaec35b231537a26ee6c6d230c489733 Author: Olaf Meeuwissen Date: 2017-07-15 20:27:28 +0900 sanei_thread: Fix [-Wunused-function] compiler warning The sanei_thread_set_invalid static function is only used by code that exists if USE_PTHREAD is defined. commit f3334500860115aab264de850db1c0cb6411099e Author: Olaf Meeuwissen Date: 2017-07-15 17:19:38 +0900 CI: Add a Fedora 26 Clang build commit 2c1f3d696f1c5ba6f410fca6e103da51c6a814a4 Author: Olaf Meeuwissen Date: 2017-07-15 17:17:21 +0900 autofoo: Sync generated files This pulls in the changes from 756d286f and e7d9779d. commit 756d286f3605143b26471eb7e1e7a45bc7ba356a Author: Olaf Meeuwissen Date: 2017-07-15 17:13:34 +0900 pthread: Disable support if pthread_t is *not* an integer type The sanei_thread implementation assumes an integer type in case of pthread based thread support. As anything else is unlikely to work correctly, it's safer to just fall back to forked processes. commit 73861ea91cd046c1b42b6c1270ff387c34d10fa4 Author: Olaf Meeuwissen Date: 2017-07-15 17:11:06 +0900 pixma: Fix [-Wint-conversion] and [-Wpedantic] compiler warnings These are also related to the SANE_Pid type issues. commit ddd7d821f68ab6f4389563c19d1915097f9c5a19 Author: Olaf Meeuwissen Date: 2017-07-15 17:07:13 +0900 sanei_thread: Fix [-Wformat=] compiler warnings commit ad09bbdd1860899f0d8badb359c26be4848b64c9 Author: Olaf Meeuwissen Date: 2017-07-15 17:06:20 +0900 sanei_thread: Fix [-Wint-conversion] compiler warnings commit 3caf05c0335858cbf22dedff3fbac47f3b9f7a91 Author: Olaf Meeuwissen Date: 2017-07-15 17:00:08 +0900 hp: Fix [-Wint-conversion] compiler warnings Don't do yourself what sanei_thread does for you already ;-) commit df1aba21bc318dffc316fc28bd7eee82531a707c Author: Olaf Meeuwissen Date: 2017-07-15 14:47:54 +0900 SANE_Pid: Fix [-Wint-conversion] issues Not all pthreads implementations use an integer type for pthread_t. As a matter of fact, POSIX has explicitly withdrawn the requirement that it must be an arithmetic type. The musl C library uses a `struct __pthread *` which triggered the warnings. As of this change, sanei_thread.h works around this by providing two new macros to help keep this issue out of sight. All backends have been changed to use these macros. commit 3f8db8e2d0ee2b0282f94ea108db5ac4462334a8 Author: Olaf Meeuwissen Date: 2017-07-12 21:31:31 +0900 AWARE: Treat compiler warnings as errors on the debian-9-* builds commit 3eb3d6b9bf827be6ee62ab7e155f71ba9b09d633 Author: Olaf Meeuwissen Date: 2017-07-12 21:20:12 +0900 plustek-pp: Fix [-Wmisleading-indentation] compiler warnings commit 48cb209f2a780fd83a497201e79b9c4147e7b4a9 Author: Rolf Bensch Date: 2017-07-11 14:02:11 +0200 pixma: backend version 0.17.42 commit 611647b61ca3dbb2dd720f67cd219f68f6dea1d1 Author: Rolf Bensch Date: 2017-07-11 14:00:40 +0200 pixma_imageclass.c: restrict image width to 215mm for i-SENSYS MF240 Series this scanner produces black stripes in 216mm wide images @ 600dpi commit 62fcf80c0b733211a602c9dd2073551dfc36361a Author: Rolf Bensch Date: 2017-07-10 23:34:01 +0200 pixma: PIXMA TS5000 Series (#315764) and TS8000 Series (#315763) are working commit 02a4ec11bfae075e0ab8d3d87e37d17c9ef19ff0 Author: Rolf Bensch Date: 2017-07-10 23:07:35 +0200 pixma: backend version 0.17.41 commit 862f265631386ef8531106d6cd04f6e0c0e9ccec Author: Rolf Bensch Date: 2017-07-10 23:12:22 +0200 pixma: remove unreachable and depreciated websites from man page commit 49ae62618cd0b5d0063c884ce8521b165bae7c22 Author: Rolf Bensch Date: 2017-07-10 23:06:29 +0200 pixma_mp150.c: new generation 5 for scanners without special image format @ high dpi commit 890aa30a7cc7e9ac43d379e38c280679f78ad3d7 Author: Rolf Bensch Date: 2017-07-10 22:01:26 +0200 pixma: i-SENSYS MF210 Series is working commit fbbb6cabf3f69334837413d6d4e4822efe23f476 Author: Rolf Bensch Date: 2017-06-25 10:02:43 +0200 pixma: fix capabilities for TS* Series commit b5f78dd081c41a91b381943726412e4142bc5338 Author: Rolf Bensch Date: 2017-07-10 21:46:57 +0200 pixma_imageclass.c: fix image block size calculation for particular scanners commit a08f5630d83328a38602b47f74f2728489656377 Author: Olaf Meeuwissen Date: 2017-07-08 17:58:49 +0900 sanei_scsi: Fix [-Wdeprecated-declarations] compiler warning The readdir_r() using loop in sanei_scsi_find_devices() has been modified to use readdir(). With the exception of sane_cancel(), the SANE API is not re-entrant. The sanei_scsi_find_devices() function is referenced neither directly nor indirectly from any of the sane_cancel() implementations so there is no inherent need to use readdir_r(). commit 0bf4595273b89b660bf1cef1c2752cdbf164b27d Author: Olaf Meeuwissen Date: 2017-06-26 19:13:51 +0900 plustek: Fix [-Wpedantic] compiler warning The warning reads: enumerator value for '_PS_INP_MIO6' is not an integer constant expression and that comes about as _PS_INP_MIO6 evaluates to 0x8000000, which does not fit in the *signed* integer range which has been allotted to enumeration constants by ISO C99. Seeing that the _PS_INP* values appear to be bitflags (despite the arithmetic additions in backend/plustek-usbdev.c!) it is safer to use #defines instead of an enum. commit a74cb99c1aa3dbddf0bb8250a9b2af72584d9b42 Author: Olaf Meeuwissen Date: 2017-06-26 18:19:27 +0900 u12: Fix [-Wmisleading-indentation] compiler warning It appears that what the compiler has to do is what was intended. Fixing the indentation to make that clearer. commit 6ad9e8479d9dc1fbf72ed7ae1d275753288365b9 Author: Olaf Meeuwissen Date: 2017-06-26 18:07:11 +0900 pixma: Fix [-Wmisleading-indentation] compiler warning There is no point in falling through the case branch so the break should be at the same level as the if. commit 08e1ba12866eb5592e27f879479a878fa9d21c70 Author: Olaf Meeuwissen Date: 2017-06-26 18:01:34 +0900 pieusb: Fix [-Wmisleading-indentation] compiler warning The indentation has been aligned what is used inside the while loop. The function appears to have been copied from elsewhere and slightly modified. The msg variable is set to NULL inside the if branch for efficiency only. commit 3191056a4c0b4f21b9b31ae0bccc68d40934954d Author: Olaf Meeuwissen Date: 2017-06-26 17:58:44 +0900 microtek2: Fix [-Wmisleading-indentation] compiler warnings The case branches have been made to follow other case branches in the same switch. commit e04261673f60dd9797d41528297c95fae8e24ecb Author: Olaf Meeuwissen Date: 2017-06-26 17:57:14 +0900 magicolor: Fix [-Wmisleading-indentation] compiler warning The assumption is that the failure should be returned to the caller. commit 0f90a5c2e33e7b893e432d1c096198951829598c Author: Olaf Meeuwissen Date: 2017-06-26 17:55:30 +0900 hp: Fix [-Wmisleading-indentation] compiler warning Indentation based on the style used in the immediate neighbourhood. The file uses a mix of styles though. commit 2ff1d7da59eeca5a83e1f83d32382daf07914d95 Author: Olaf Meeuwissen Date: 2017-06-26 17:52:08 +0900 genesys: Fix [-Wmisleading-indentation] compiler warning The fix is based on similar code in backend/genesys_gl846.c and fixes a potential infinite loop as well. commit 5098b9bfbb8707f55930864fbbcaccc8356a1efe Author: Olaf Meeuwissen Date: 2017-06-26 17:51:40 +0900 genesys: Fix [-Wmisleading-indentation] compiler warnings commit a1133aa194fbf407998e62c9d20c0ecc2a90c937 Author: Olaf Meeuwissen Date: 2017-06-26 12:28:54 +0900 pnm: Fix [-Wshift-negative-value] compiler warning commit deb856cb279bd5a31b5e8b72b2a65f6a66a16122 Author: Olaf Meeuwissen Date: 2017-06-26 12:09:54 +0900 umax: Fix [-Wshift-negative-value] compiler warning commit ff535ef1ead749526ff41c56b24bd72ea0b16705 Author: Olaf Meeuwissen Date: 2017-06-26 12:09:39 +0900 u12: Fix [-Wshift-negative-value] compiler warning commit 0c6fc8b1745465c2071f8346406aed8d86c7eef2 Author: Olaf Meeuwissen Date: 2017-06-26 12:09:23 +0900 snapscan: Fix [-Wshift-negative-value] compiler warning commit 582a3b89877c149f5122f414abc6a75127eb553a Author: Olaf Meeuwissen Date: 2017-06-26 12:09:06 +0900 s9036: Fix [-Wshift-negative-value] compiler warning commit 73e1d3568dabb1ef7e66985c94b716909df34c5d Author: Olaf Meeuwissen Date: 2017-06-26 12:08:51 +0900 plustek_pp: Fix [-Wshift-negative-value] compiler warning commit 1a1c808c15c78ec63ecb66a9f4538ddb6d5a5761 Author: Olaf Meeuwissen Date: 2017-06-26 12:08:37 +0900 plustek: Fix [-Wshift-negative-value] compiler warning commit f8eb12b49d43c69a4ab7259b201158f1f2dba793 Author: Olaf Meeuwissen Date: 2017-06-26 12:08:17 +0900 mustek: Fix [-Wshift-negative-value] compiler warning commit 02ce2b5d46d1aa352d1444ac61c67c865b4ae962 Author: Olaf Meeuwissen Date: 2017-06-26 12:07:24 +0900 agfafocus: Fix [-Wshift-negative-value] compiler warning commit 702a8e27b61a4bb5de0da2c4f84326e96b9dde10 Author: Olaf Meeuwissen Date: 2017-06-26 11:43:01 +0900 p5: Fix [-Wunused-const-variable=] compiler warning The u8_range and threshold_percentage_range constants have not been used for anything ever since the backend's addition. They probable ended up in the code as a result of copy-and-paste. commit aa5468552bbe036aed5d8a4df1b61ff5b42c751b Author: Olaf Meeuwissen Date: 2017-06-26 11:39:50 +0900 teco2: Fix [-Wunused-const-variable=] compiler warning The default_dpi_color_adjust variable hasn't been used ever since the backend was added. commit a2083538e97b6419e70414ab0c42aa42a78fbce4 Author: Olaf Meeuwissen Date: 2017-06-26 11:35:06 +0900 tamarack: Fix [-Wunused-const-variable=] compiler warning The u8_range has been `#if 0`'d to match the gamma options' have been disabled (since the initial revision). The options refer to u8_range. commit 2442ddf6d17b2721192d311cd961f29eaa0b55cd Author: Olaf Meeuwissen Date: 2017-06-26 11:28:10 +0900 sp15c: Fix [-Wunused-const-variable=] compiler warning The RCSid* variables have never been used for anything by the backend code. commit 86ab41d8f1bd85d75af1e8e2cd1ed387b8ab9fc0 Author: Olaf Meeuwissen Date: 2017-06-26 11:19:55 +0900 mustek_usb: Fix [-Wunused-const-variable=] compiler warning The brightness option that used the char_range variable was renamed to threshold and changed to use u8_range in c95e4638. commit 39849809d126de710318e4c70f93728c3d4ade1f Author: Olaf Meeuwissen Date: 2017-06-26 11:16:55 +0900 mustek: Fix [-Wunused-const-variable=] compiler warning The const variables' definition has been #ifdef'd to prevent loss of protocol info. Neither scsi_area_and_windows nor scsi_lookup_table appear to have been used for anything. commit 398d5850fa5a1d3ce6a7f5eb13b3cfb7e1c11296 Author: Olaf Meeuwissen Date: 2017-06-26 11:08:03 +0900 avision: Fix [-Wunused-const-variable=] compiler warning The const variable's definition has been #ifdef'd to prevent loss of protocol info. commit 0f70ce9aac2e1d7f198b8d73524657b404a99f2d Author: Olaf Meeuwissen Date: 2017-06-26 11:05:01 +0900 avision: Fix [-Wunused-const-variable=] compiler warning The threshold option which used the abs_percentage_range was removed in e2169ec4. commit 2f5c9e14990b90b2716a12fcb449527e0fb1ffb4 Author: Olaf Meeuwissen Date: 2017-06-26 10:57:50 +0900 kvs1025: Fix [-Wunused-const-variable=] compiler warning The go_paper_max_width value hasn't been used for anything since the initial revision of the file. commit ed0062d474530d0dd95f9152ee7c2bb88abe010d Author: Olaf Meeuwissen Date: 2017-06-26 10:50:40 +0900 gt68xx: Fix [-Wunused-const-variable=] compiler warning The options that used offset_range were removed in a3a8808b. commit fa4497047352583cd3113d2962d919589f0044ae Author: Olaf Meeuwissen Date: 2017-06-26 10:45:42 +0900 epson2: Fix [-Wunused-const-variable=] compiler warning The s8_range variable appears to be a left-over from the epson backend's "fork" and became superfluous after refactoring the colour correction support in the epson2 backend. commit 754a7164f35aa6331f2f5a02076fcb5082b521d8 Author: Olaf Meeuwissen Date: 2017-06-26 10:38:44 +0900 dc25: Fix [-Wunused-const-variable=] compiler warning The percentage_range hasn't been used for anything since the initial revision of the file. This also gets rid of a [-Wshift-negative-value] warning as well as a "initializer element is not a constant expression" pedantic warning. commit a52d843da40a945c4e2a55b59e92f362b6d77479 Author: Olaf Meeuwissen Date: 2017-06-26 10:34:14 +0900 canon: Fix [-Wunused-const-variable=] compiler warning The papersize_list hasn't been used for anything since the initial revision of the file. commit fe3299a9b069865aef81aa50a0bca0b7a114d74f Author: Olaf Meeuwissen Date: 2017-06-22 20:36:59 +0900 Add minimal and full CI compile tests for Debian 9 commit e7d9779dfc3748f8f205585fa09ebc7181874642 Author: Olaf Meeuwissen Date: 2017-06-17 12:38:21 +0900 CI: Build `git describe` versioned snapshot tarballs This: - modifies `configure.ac` to get a version number courtesy of `git` when `autoconf` is run - runs a `make dist` when all compilation targets succeed - makes the resulting tarball available as a build artifact - renames the various pipeline stages commit a06e9a40751dd3c4c87d35a821f7c9281fa71644 Author: Olaf Meeuwissen Date: 2017-06-12 20:53:53 +0900 Script updating of upstream files commit baec9c4e264329a13899e28090b59fe7182b80dd Author: Olaf Meeuwissen Date: 2017-06-07 22:50:56 +0900 autofoo: Sync with configure.ac, Makefile.am changes commit a2f1cc1c82e1ca4dfb04c386a6d51cfad3934d33 Author: Olaf Meeuwissen Date: 2017-06-07 22:06:05 +0900 libsane: Only depend on and link in the kitchen sink if needed There is no need to depend on and link in all the various dependencies for whatever backends *might* be preloaded if none are. Distributions habitually rip these out, rightfully so, to reduce the list of package dependencies. This will achieve the same while still doing the "right thing" for builds that do preload one or more backens. commit 2284c6e03438c489bd3f077810e3e24771fed294 Author: Jerome Duval Date: 2017-06-04 12:22:31 +0200 epsonds: add missing includes Fixes build failure on Haiku. Alioth Tracker: 315750. commit fbe2f6d332702c7878483d24d1daf66fe83633d4 Author: Olaf Meeuwissen Date: 2017-06-02 21:08:44 +0900 CI: add a "lint" stage before the builds This is mostly meant for "cheap" policy checks that do not require the sources to be compiled. commit 828f1a39495198aace07d1991e7a785d3a7ce291 Author: Olaf Meeuwissen Date: 2017-06-02 20:52:12 +0900 style-check: skip directories, sanitize file names When generating lists of files with `find`, directories may get output if you try to prune paths and it has a habit of prefixing paths with a leading `./`. commit 75e271444ae76b3a6c845943a765a17664987e4e Author: Rolf Bensch Date: 2017-06-01 14:11:48 +0200 pixma: backend version 0.17.40 commit 80445f9e5775b976dce1f6162974839197b8a10d Author: Rolf Bensch Date: 2017-06-01 14:09:21 +0200 pixma_imageclass.c: MF240 Series restricts adf scans to max. 300dpi commit c2594e949bc77b2fc23988062af67c2fa9e988d5 Author: Rolf Bensch Date: 2017-06-01 14:06:43 +0200 Revert "pixma_imageclass.c: MF240 Series supports only 300dpi for adf scans" This reverts commit 60e0c31bb7a080385fba0e9acf0afe55f293526a. Truly this scanner restricts adf scans to max. 300dpi. commit ba8e76d937c520a6841cf838867760f58f7758f9 Author: Olaf Meeuwissen Date: 2017-05-31 21:28:45 +0900 .editorconfig: add script to check and fix style issues The various checks cover all settings in the `.editorconfig` file. The `--fix` support, however, does not attempt to correct charset issues because the encoding cannot be determined automatically. Note that image files as well as generated files in the repository are exempted from all style checks. commit 01c5dbc82b78db865cd88c0044d22199babb3e6f Author: Olaf Meeuwissen Date: 2017-05-30 22:39:30 +0900 .editorconfig: Fix sane-desc tool to match cleaned up references Changes to the test reference files should not, famous last words ;-), have any effect on the use of the tool's actual outputs. Note that the tests use a "concocted" `testfile.desc` as their input. commit fc4b250a090eef934b9c77afedcebaf146f119c0 Author: Olaf Meeuwissen Date: 2017-05-27 15:22:15 +0900 .editorconfig: use utf-8 charset throughout commit f0f187f995c45226e95dfbeb3231a316f76e11ea Author: Olaf Meeuwissen Date: 2017-05-27 15:37:14 +0900 .editorconfig: trim trailing blank lines commit ffa573f65eade56a7c76c91ca4a584692c8bfb97 Author: Olaf Meeuwissen Date: 2017-05-27 13:57:01 +0900 .editorconfig: insert a newline at end-of-file This keeps `git` from "complaining" about this when doing a diff. It also keeps your prompt at the left edge when you `cat` a file. commit 23891a2646c171f7396bc399d25079f9887c10a0 Author: Olaf Meeuwissen Date: 2017-05-27 14:27:22 +0900 .editorconfig: trim trailing whitespace commit 45a7aabc80b0bc18e6fec1c3015466e1a2debca4 Author: Olaf Meeuwissen Date: 2017-05-27 13:52:59 +0900 Add .editorconfig to maintain a more consistent coding style This does not set anything yet. See http://editorconfig.org/ for more information. commit 55257bea66c8d0b48cb3e306d5863b190752cfed Author: Olaf Meeuwissen Date: 2017-05-29 21:02:53 +0900 Bump Alpine builder to 3.6 commit 7893b77d926139452be1162a4bea4dc2b595face Author: Rolf Bensch Date: 2017-05-26 23:37:28 +0200 pixma: backend version 0.17.39 commit bcf1f4398eb967cc4db313a3b15104fff5587eb3 Author: Rolf Bensch Date: 2017-05-26 23:36:52 +0200 pixma_imageclass.c: reduce max. scan width to 216mm for particular scanners commit 2b0028b4af61761420fbff4459310b799bc13c7b Merge: 35dc4426d2da 2bba2739937b Author: Rolf Bensch Date: 2017-05-23 21:16:52 +0200 Merge branch 'pixma/mf240_adf_only_300dpi' commit 2bba2739937ba26059701eec1ae5e4372ff9de2a Author: Rolf Bensch Date: 2017-05-19 11:21:59 +0200 pixma: backend version 0.17.38 commit 60e0c31bb7a080385fba0e9acf0afe55f293526a Author: Rolf Bensch Date: 2017-05-19 11:26:00 +0200 pixma_imageclass.c: MF240 Series supports only 300dpi for adf scans commit 35dc4426d2dad67167af40e6c136feb229b4f3ac Author: m. allan noah Date: 2017-05-22 21:08:33 -0400 Open repo for 1.0.28 development backends-1.3.0/ChangeLogs/ChangeLog-1.0.29000066400000000000000000007270711456256263500176320ustar00rootroot00000000000000commit e52a5bf71979365a028bbf8aa5bd5e5e6b983b7f Author: Olaf Meeuwissen Date: 2020-02-02 20:19:37 +0900 NEWS: Update with changes committed to the release/1.0.29 branch commit f35aab0de76a720615da845e04b9f8863ed01bd7 Author: Olaf Meeuwissen Date: 2020-02-02 20:07:39 +0900 po/*.po: Update Project-Id-Version of several translations This only affects those languages for which translatable messages have been updated since the last release (1.0.28). commit 0c90e6bdef277bdc497d875f31d2dee6d5a71301 Merge: 3c714b48afb7 8bc98d0f7d37 Author: Povilas Kanapickas Date: 2020-01-19 13:09:24 +0000 Merge branch '225-fix-genesys-testsuite-compiler-warning' into 'release/1.0.29' Resolve "genesys test utility generates a compiler warning on Debian 10" See merge request sane-project/backends!309 commit 8bc98d0f7d37bec2dc77d8fea5d57e95ba1534a8 Author: Olaf Meeuwissen Date: 2020-01-13 18:37:21 +0900 testsuite/backend/genesys: Fix include path for out-of-tree builds commit ba84a8f69c5277723df6e77c53521365a7645558 Author: Olaf Meeuwissen Date: 2020-01-13 18:35:33 +0900 testsuite/backend/genesys: Fix [-Wcatch-value=] compiler warning Exceptions ought to be caught by reference. commit 3c714b48afb7eeaff54a4e6212c111647a803153 Merge: 3825e0ca6496 fe38a70d73c0 Author: Rolf Bensch Date: 2020-01-18 19:59:38 +0000 Merge branch 'de-minimal-translation-updates' into 'release/1.0.29' Minimal German translation updates See merge request sane-project/backends!312 commit fe38a70d73c0f677fb4b10b865be464252c6c32a Author: Olaf Meeuwissen Date: 2020-01-18 12:13:30 +0900 po/de.po: Cherry pick fixes for fuzzies and untranslated messages commit 3825e0ca6496b1de08b847c802125c655ad025f2 Merge: f22dc84b71a9 b91dca43d504 Author: Olaf Meeuwissen Date: 2020-01-18 02:53:48 +0000 Merge branch 'bellaperez/cat_translations' into 'release/1.0.29' Update Catalan and Valencian translations See merge request sane-project/backends!311 commit b91dca43d504e2019521e49b3abc8d259d92c9aa Author: Antoni Bella Pérez Date: 2020-01-18 02:53:48 +0000 Update Catalan and Valencian translations Adapted and tested: * msgmerge --silent --previous --width=75 --lang= * msgfmt -vc commit f22dc84b71a95afb01a41ad38b87b10f9e2baa83 Merge: 77c92f49af02 f72862ddf8b4 Author: Olaf Meeuwissen Date: 2020-01-14 13:05:02 +0000 Merge branch 'undefined' into 'release/1.0.29' Update Ukrainian translation See merge request sane-project/backends!305 commit f72862ddf8b4dac5857cc9858f2d3ee6efca63ff Author: Yuri Chornoivan Date: 2020-01-14 13:05:02 +0000 Update Ukrainian translation commit 77c92f49af02a9cb4389ddc2473e68dbed32642a Author: Gerhard Jäger Date: 2020-01-13 09:38:30 +0100 Issue#113: Fix applied (thanks David Binderman). commit d36a9ba786d10b302a40a92cdeb55da74a363809 Author: Rolf Bensch Date: 2020-01-12 21:07:18 +0100 INSTALL.linux: add missing development packages these packages are mandatory for building escl backend commit 4a9c723646cfbd7a6df976e24284f24986f28038 Merge: ca63c6cf9a38 cc31c110c94d Author: Ralph Little Date: 2020-01-12 19:37:39 +0000 Merge branch 'uk-english-translations' into 'release/1.0.29' Update British English translations See merge request sane-project/backends!306 commit cc31c110c94d03f0895cd94bf82e139ba89b9e92 Author: Ralph Little Date: 2020-01-12 10:39:27 -0800 Updated British English translations commit ca63c6cf9a387c594e8995cfc3fdf94d1858a86c Author: Olaf Meeuwissen Date: 2020-01-12 21:28:21 +0900 NEWS: Add most relevant changes for 1.0.29 commit 7b45c89b023dd902cd6d8d81713e977df4caa444 Merge: 0f394f55ed5d 055cd9f28211 Author: Olaf Meeuwissen Date: 2020-01-12 08:26:50 +0000 Merge branch 'dll-conf-sync' into 'master' Synchronize dll configuration file See merge request sane-project/backends!304 commit 055cd9f28211cc72436827f264a87d645f9212ad Author: Olaf Meeuwissen Date: 2020-01-12 17:11:40 +0900 dll: Sort dll.conf.in for ease of maintenance Only the `net` backend is treated special to match the file comment. commit 4f77e6dcfa4532741370096f2d517cdc9d0fe8d8 Author: Olaf Meeuwissen Date: 2020-01-12 17:10:38 +0900 dll: Add kvs40xx and pieusb backends to dll.conf.in commit 0f394f55ed5daf742f62f4139b0f5c89572aa6e5 Merge: 42b256247061 2b927f165b08 Author: Olaf Meeuwissen Date: 2020-01-12 07:44:10 +0000 Merge branch 'i18n-update' into 'master' I18n update See merge request sane-project/backends!303 commit 2b927f165b08dcef3c207e36597a45878c956676 Author: Olaf Meeuwissen Date: 2020-01-12 16:26:15 +0900 po: Synchronize message catalogs with latest code commit f094033990ee1f1106299dbc70e5491328ed3700 Author: Olaf Meeuwissen Date: 2020-01-12 15:10:32 +0900 po: Regenerate POTFILES.in The file now contains only those backend files that mention SANE_I18N and contains all of them. commit 8ae5ba0522c139f4958d91620fd5c94cc94905d6 Author: Olaf Meeuwissen Date: 2020-01-12 14:46:14 +0900 escl: Use standardized, translated option group titles commit 42b256247061aedf0c4adb3e324d122329b09118 Merge: 75162d0f005a d40a8ff90da7 Author: Olaf Meeuwissen Date: 2020-01-12 03:37:34 +0000 Merge branch 'escl-debug' into 'master' Log message improvements. See merge request sane-project/backends!302 commit d40a8ff90da71193762dc46518f2eba58799707b Author: Thierry HUCHARD Date: 2020-01-11 09:15:51 +0100 Delete unused file. commit d8f983bbd9cd1e02ecf5a80b9250cd314ae4b541 Author: Thierry HUCHARD Date: 2020-01-11 08:56:46 +0100 Log message improvements. commit 75162d0f005a5e89f8815dce27abc9a2e0cbbc01 Merge: d9188a7699a6 3e49c3af0b3a Author: Olaf Meeuwissen Date: 2020-01-12 03:19:00 +0000 Merge branch '201-old-incorrect-link-in-sane-man' into 'master' Resolve "Old, incorrect link in sane.man" Closes #201 See merge request sane-project/backends!291 commit 3e49c3af0b3a4034dd62df4ee46f7c959c1f929f Author: Olaf Meeuwissen Date: 2020-01-04 15:37:51 +0900 sane.man: Drop outdated link for coolscan2 backend commit 70cfd20c206e0b248d08b8cc9ebd666fe620d7c7 Author: Rolf Bensch Date: 2020-01-03 18:32:01 +0100 update last edited date commit 59bd48ea7275c94b1d0590af7a72f334dad204c2 Author: Rolf Bensch Date: 2020-01-03 18:31:31 +0100 update pixma backend description commit d9188a7699a6db1f5925c1ac80c0ea7a880283ce Merge: 0830b8391590 c821f277f314 Author: Olaf Meeuwissen Date: 2020-01-12 03:16:45 +0000 Merge branch '153-is-pthread_t-check-for-non-integers-still-neccesary' into 'master' Resolve "Is pthread_t check for non-integers still necessary?" Closes #153 See merge request sane-project/backends!289 commit c821f277f3149a0ab57940e2512e893c6f886a20 Author: Olaf Meeuwissen Date: 2019-12-31 12:33:13 +0900 build: Rename *THREAD_LIBS parameters This aims to clarify the purpose of each parameter: SANEI_THREAD_LIBS for any code that uses sanei/sanei_thread.c and a plain PTHREAD_LIBS for code that uses the pthread library directly. commit 9050ae3b6a3489a78c791ca0c3aa71d13e68d966 Author: Olaf Meeuwissen Date: 2019-12-31 12:14:46 +0900 mustek_usb2: Remove unneeded linking arguments commit 378a85fe0960e7969e552325f9cf774fdfc45852 Author: Olaf Meeuwissen Date: 2019-12-31 11:55:31 +0900 Don't disable backends using pthreads directly. Fixes #153 The use of pthreads was disabled in case of a non-integral pthread_t type in 756d286f3605143b26471eb7e1e7a45bc7ba356a as the sanei_thread implementation assumes an integral type. This also disabled building of the kvs40xx and mustek_usb2 backends which use pthreads directly. This re-enables the build of these backends. commit 0830b83915900bb1f936372adaafd00f814a5eae Merge: aedebecef790 c4a88e6bf4a2 Author: Povilas Kanapickas Date: 2020-01-11 15:50:06 +0000 Merge branch '188-hp-scanjet-3670-fails-to-scan-at-75dpi' into 'master' Resolve "HP ScanJet 3670 fails to scan at 75dpi" Closes #188 See merge request sane-project/backends!298 commit c4a88e6bf4a2e153ec667f811dfdeb35ef2e9464 Author: Ralph Little Date: 2020-01-07 22:52:14 -0800 genesys: renamed HP ScanJet 3670C to 3670. Model has no C suffix commit 88ea0d48e7357799e33a7352e73694967857e502 Author: Ralph Little Date: 2020-01-07 22:36:09 -0800 genesys: added 50dpi to HP ScanJet 3670 backend Windows driver supports 50dpi as the lowest mode. commit aedebecef790867c390e97e05741965f53a8e3e0 Merge: 28264dcfd825 913be9367248 Author: Olaf Meeuwissen Date: 2020-01-09 12:27:11 +0000 Merge branch 'escl-integration-fixes' into 'master' Escl integration fixes See merge request sane-project/backends!299 commit 913be9367248966c2f346244d8968fa71787a73c Author: Olaf Meeuwissen Date: 2020-01-09 21:09:47 +0900 escl: Mark backend as new for upcoming release commit e5e601379cef583a5541c9eeaa43cbbe5438f071 Author: Olaf Meeuwissen Date: 2020-01-09 21:09:06 +0900 dll: Enable loading of escl backend commit 28264dcfd825d7c2f84e3cd7e117ee7e831ba11d Merge: 8b611a252d40 74131ede6c64 Author: Olaf Meeuwissen Date: 2020-01-08 11:29:40 +0000 Merge branch 'escl-Securing-libcurl' into 'master' Escl securing libcurl See merge request sane-project/backends!297 commit 74131ede6c6438d215b324a5ee23c09a0efc0a6a Author: Ordissimo Date: 2020-01-07 14:14:19 +0000 Add header curl. commit 94daddc6bd15c1975dc66dcd97d5fb2ca4567c92 Author: thierry1970 Date: 2020-01-07 15:01:47 +0100 cURL global init/cleanup issues. commit 8b611a252d40b5bdab3c6b3135fe3300b63088b7 Merge: bc804f56410c 66ec4dda2601 Author: Olaf Meeuwissen Date: 2020-01-08 11:28:08 +0000 Merge branch 'escl-Memory-and-file-handle-leak' into 'master' Escl memory and file handle leak See merge request sane-project/backends!296 commit 66ec4dda26016457d145f1438e8494c5f872f7c1 Author: thierry1970 Date: 2020-01-07 14:07:29 +0100 Buffer overflow. commit 7e192f8ba7f99fddb33862efad6d7ac8790f6296 Author: thierry1970 Date: 2020-01-07 14:06:11 +0100 Memory and file handle leak. commit bc804f56410c2eb4347650733eda8a3c415d6b32 Merge: dad063068dc9 bd2d5f5e7d6d Author: Louis Lagendijk Date: 2020-01-07 16:07:46 +0000 Merge branch 'pixma_bjnp_model_not_found' into 'master' pixma_bjnp: Report that scanner model is not supported as suggested in issue #69 See merge request sane-project/backends!293 commit bd2d5f5e7d6dc2047448e5e027d94d753dc7baef Author: Louis Lagendijk Date: 2020-01-07 16:07:46 +0000 pixma_bjnp: Report that scanner model is not supported as suggested in issue #69 pixma: Improved logging for search for scanner model Moved lookup_scanner from pixma_io_sanei.c to pixma_bjnp.c pixma_io_sanei.c: fix whitespace commit dad063068dc9738143d417223e8780f7b6a480ba Author: Rolf Bensch Date: 2020-01-05 12:49:20 +0100 pixma: backend version 0.27.0 commit 4a0d222e88b6dd0699d7d98b660a3c6672d0a382 Merge: ba2f14fde17e b7e0d9afae23 Author: Rolf Bensch Date: 2020-01-05 11:45:51 +0000 Merge branch 'pixma/move-to-subfolder' into 'master' move pixma source files to subfolder See merge request sane-project/backends!290 commit b7e0d9afae230a8ae6b552297a58bc8b7efdf44b Author: Rolf Bensch Date: 2020-01-02 13:56:05 +0100 pixma: fix comment commit 7b228be5e7dad536640abb91ec6db605c956e1f5 Author: Rolf Bensch Date: 2020-01-02 12:36:49 +0100 pixma: move source files to subfolder commit ba2f14fde17ebff30261551d1fad1aa8c311ca40 Author: Rolf Bensch Date: 2020-01-04 12:39:59 +0100 pixma: backend version 0.26.1 commit c2e512db8b438f40627c7d2de2b5fdbf9cb64729 Merge: b30784a73015 d910d52dabb9 Author: Rolf Bensch Date: 2020-01-04 11:23:36 +0000 Merge branch '183-add-support-for-canon-mf743c' into 'master' Resolve "Add support for Canon MF743C" Closes #183 See merge request sane-project/backends!258 commit d910d52dabb983fa190ac92864a1069895572ed8 Author: Rolf Bensch Date: 2019-12-19 20:07:16 +0100 pixma: restrict ADFDUP scans to 300dpi for i-SENSYS MF741C/743C commit cf506daab35626c10c690ee36ad0935f8da0d658 Author: Rolf Bensch Date: 2019-12-15 11:56:47 +0100 pixma: new scanner Canon i-SENSYS MF741/743 commit b30784a73015fd00a5edf95628188f577158666a Author: Jakub Benda Date: 2020-01-03 19:48:37 +0300 xerox_mfp: Fix Samsung CLX-3175FW color scan "Not a JPEG file" Exclude "Samsung CLX-3170 Series" from JPEG mode. Closes #200. Reference: https://gitlab.com/sane-project/backends/issues/200 commit 8d022c29b25f9529d0c090070f37e37b66aa9e10 Merge: 3a1d6dbb96de 5927ecfa2a52 Author: Olaf Meeuwissen Date: 2020-01-02 10:57:08 +0000 Merge branch 'escl-png' into 'master' Added PNG and TIFF decoder. See merge request sane-project/backends!283 commit 5927ecfa2a5212eb71731926fea7e57d10e3a5e3 Author: thierry1970 Date: 2020-01-02 09:39:16 +0100 Using strcmp instead of strncmp, strncmp is not justified. commit 160b63a9c2c5fee2703c1e14fc8d5a77904cb055 Author: Thierry HUCHARD Date: 2019-12-29 10:52:11 +0100 Using strcmp instead of strncmp, strncmp is not justified. commit 925bf77419967cbd1fa667214cee31f7675dc76f Author: Thierry HUCHARD Date: 2019-12-29 10:50:43 +0100 Removing the png and tiff test from the header. commit d527110a52569beddb5a631756bccf807a0ebf4e Merge: f956fd00ba42 5275b3bbd65a Author: thierry1970 Date: 2019-12-27 15:23:02 +0100 Merge commit '5275b3bb' into escl-png commit f956fd00ba42da9202225aa1622d923a8a980033 Merge: 5372f21810ce 45c670255273 Author: thierry1970 Date: 2019-12-27 15:22:38 +0100 Merge commit '45c67025' into escl-png commit 5372f21810ce6251b8753c694c25c579f3233f50 Author: Ordissimo Date: 2019-12-26 14:11:09 +0000 Fix typo. commit 1a95f30210c15f03e9fc54a4bd6ae5af492a98c2 Author: Ordissimo Date: 2019-12-26 14:08:15 +0000 Fixing the comparison. commit 2ea6552ed3220c0ff9495863c79bb0e366375a51 Author: thierry1970 Date: 2019-12-26 14:03:33 +0100 Fix style. commit 65470e95aad9ed66d8344343ac7619d964aa794b Author: thierry1970 Date: 2019-12-26 14:01:00 +0100 Addition of the TIFF decoder. commit a23dfbfff569da2a02fa0d48f4b87f112a974c29 Author: thierry1970 Date: 2019-12-26 11:55:00 +0100 Standardization of method signatures. commit df59815895846505c5fccb84dd309405395dbccb Author: Ordissimo Date: 2019-12-25 18:43:56 +0000 Remove unused function. commit 296a4b68f907013ddf43eed89ea5e06a5af73612 Author: Ordissimo Date: 2019-12-25 18:34:57 +0000 Fix syntaxe. commit d3efc20a0bf5815ccf0393d57bb7a766d4f92bcd Author: Thierry HUCHARD Date: 2019-12-25 18:36:23 +0100 Decoding files in one phase. commit c6e8d381e070cfc8775401a7f35a0dcae23ed01c Author: Thierry HUCHARD Date: 2019-12-25 00:49:52 +0100 Fix syntax. commit 5c15d372488bc4e5d6f08f232433ae3cbfde4bd4 Author: Thierry HUCHARD Date: 2019-12-25 00:39:04 +0100 Adding png support. The decompressors (Jpeg and Png) are in separate files. commit 3a1d6dbb96def4e7be4058a19ee29f0d0256a425 Author: Olaf Meeuwissen Date: 2019-12-31 12:49:40 +0900 .gitignore: .dirstamp files and *-s.cpp files [skip CI] commit fa882855be117ae74e49e72ac7d483209e414788 Merge: 75310001f176 adaef0435a77 Author: Olaf Meeuwissen Date: 2019-12-30 07:06:02 +0000 Merge branch '120-fix-all-compiler-warnings-on-the-debian-10-builds' into 'master' CI: Turn compiler warnings into errors on Debian 10. Fixes #120 Closes #120 See merge request sane-project/backends!288 commit adaef0435a77b48c777f720036f288611762cb5f Author: Olaf Meeuwissen Date: 2019-12-30 15:48:56 +0900 CI: Turn compiler warnings into errors on Debian 10. Fixes #120 commit 75310001f1765f6e9c083c89c3b894e6f97abf3a Merge: b2c83de8a412 b4ce719973a4 Author: Olaf Meeuwissen Date: 2019-12-30 06:19:39 +0000 Merge branch 'fix-kvs40xx-cast-function-type-warning' into 'master' kvs40xx: Fix [-Wcast-function-type] compiler warning. Re #120 See merge request sane-project/backends!229 commit b4ce719973a45aeaf55d732c5847d5e460943bb5 Author: Olaf Meeuwissen Date: 2019-10-30 20:28:33 +0900 kvs40xx: Fix [-Wcast-function-type] compiler warning. Re #120 The changes are meant to make explicit what the run-time or compiler is thought to be doing anyway. commit b2c83de8a41286faee798b1300aa48de6f43a203 Merge: a9ab9df99738 4c8e4dd934ef Author: Olaf Meeuwissen Date: 2019-12-30 03:11:15 +0000 Merge branch '196-fix-compiler-warning-for-pieusb-backend' into 'master' pieusb: fix compiler warnings Closes #196 See merge request sane-project/backends!287 commit 4c8e4dd934efe3d204c3dd9b0bf15463fb640d4b Author: Klaus Kämpf Date: 2019-12-23 19:43:43 +0100 pieusb: fix compiler warnings fixes #196 commit a9ab9df99738d74759a9e8775b3aa2ffa2023f49 Merge: 6f34396ab493 7f977124917b Author: Ralph Little Date: 2019-12-29 19:08:01 +0000 Merge branch '198-fix-compiler-warning-in-scanimage-frontend' into 'master' Resolve "Fix compiler warning in scanimage frontend" Closes #198 See merge request sane-project/backends!286 commit 7f977124917b4b9d65ce0e8ececd93777fbe07a1 Author: Olaf Meeuwissen Date: 2019-12-29 17:37:17 +0900 scanimage: Combine conditions to reduce nesting commit 4fbb5d2f1b7068ed8b312c43d72e88f69356a757 Author: Olaf Meeuwissen Date: 2019-12-28 18:52:59 +0900 scanimage: Use destination size to limit strncpy() commit df114df05f8fdb8b3086608ed2d0bf5012b236b5 Author: Olaf Meeuwissen Date: 2019-12-28 18:46:02 +0900 scanimage: Replace string length computations by temporary strings This creates temporary strings that correspond to the colon-delimited username and password that are read from file in `auth_callback()`. The null-terminated strings are used directly to determine lengths. commit 6682223b260f00ebe936307997e90e79712cd94a Author: Olaf Meeuwissen Date: 2019-12-28 18:26:43 +0900 scanimage: Improve readability of auth_callback implementation Rather than repeatedly calling `strchr`, use descriptive variables to hold the result. commit 5e6a111ffa3c8bd1218dd36284ddcb3537ecfe97 Author: Olaf Meeuwissen Date: 2019-12-28 18:03:00 +0900 scanimage: Un-word-wrap auth_callback to improve readability This is one of those cases where word-wrapping to less than 80 or so characters isn't helpful in understanding the code. commit 6f34396ab4938bfabfbe3b7db1e24eb1b45706ab Author: Rolf Bensch Date: 2019-12-28 13:58:11 +0100 pixma: backend version 0.26.0 commit eae4137ae1ee19fc2743ae62558f9a62cf8be7b5 Author: Rolf Bensch Date: 2019-12-28 13:55:26 +0100 pixma: update date of man page commit a7c78f41d6ba81d746384b3b43a15286b14ae578 Author: Rolf Bensch Date: 2019-12-28 13:45:02 +0100 pixma: reorder scanner defines commit c83b1a17d23512cd43fd1425f81923f0666c51e9 Merge: b1ad53fff568 18670166a68c Author: Rolf Bensch Date: 2019-12-28 12:53:52 +0000 Merge branch 'master' into 'master' Add model 2019: G6000, G6080, TS5300, TS5380, TS6300, TS6380, TS7330, TS8300,... See merge request sane-project/backends!226 commit 18670166a68c90394de1366e3d7fffcf69ed5136 Author: Ordissimo Date: 2019-12-28 12:53:52 +0000 pixma: Add model 2019: G6000, G6080, TS5300, TS5380, TS6300, TS6380, TS7330, TS8300, TS8380, TS8330, XK60, TS6330, TS3300 and E3300 commit b1ad53fff5687df439b0cc2a3f50ffa731226e43 Merge: bac0c4abcef1 a297073bc9d9 Author: Rolf Bensch Date: 2019-12-28 13:34:47 +0100 Merge branch 'perillamint/backends-add-canon-pixma-g2010-support' pixma: Add support for Canon PIXMA G2010 Series see merge request sane-project/backends!223 commit a297073bc9d91aea9ebbfff721c6dd27dd415c0d Author: Rolf Bensch Date: 2019-12-28 13:26:54 +0100 pixma: Canon PIXMA G2010 Series is working commit aa76967917e5118362f21f4fa9c67f4da2b8473d Author: Rolf Bensch Date: 2019-12-28 13:26:11 +0100 pixma: fix device define for Canon PIXMA G2010 Series commit 66c17ebf846406ee68ba9dcd202c197fcdd086ff Author: perillamint Date: 2019-10-22 15:34:04 +0900 pixma: Add support for Canon PIXMA G2010 Series This commit adds support for Canon PIXMA G2010 Series. commit bac0c4abcef14bb6e777cf2a5294cc5ec0838593 Merge: e80034a6e0d5 5275b3bbd65a Author: Olaf Meeuwissen Date: 2019-12-28 05:55:59 +0000 Merge branch 'escl-discovery' into 'master' Solves the problem of HP device discovery. See merge request sane-project/backends!285 commit 5275b3bbd65a56eaf0e5e27f48f7d44b7dd28620 Author: thierry1970 Date: 2019-12-27 15:07:10 +0100 Solves the problem of HP device discovery. commit 45c6702552733e04a3b2439172c842cdbf160b94 Author: thierry1970 Date: 2019-12-27 15:04:57 +0100 Formatting of the code to not exceed 80 characters. commit e80034a6e0d5f4cf5efccc64c9a986cfe15d5d8d Merge: 7a76f21ccdaa adf9bb837fc8 Author: Olaf Meeuwissen Date: 2019-12-27 11:22:05 +0000 Merge branch '199-hpsj5s-backend-generates-compiler-warnings-in-debian-10' into 'master' Resolve "hpsj5s backend generates compiler warnings in Debian 10" Closes #199 See merge request sane-project/backends!281 commit adf9bb837fc85016ee8e83e8c300910fa43ac400 Author: Ralph Little Date: 2019-12-27 11:22:05 +0000 Resolve "hpsj5s backend generates compiler warnings in Debian 10" commit 7a76f21ccdaa27ca493dfdbb88c04d02c6bd7e5a Merge: d190c559c24f 53efe5822dcc Author: Povilas Kanapickas Date: 2019-12-26 15:31:53 +0000 Merge branch 'genesys-lide-fixes' into 'master' genesys: Various fixes affecting LiDE 100,110,120,200,210,220 See merge request sane-project/backends!284 commit 53efe5822dcc47d77e418d17218914ef8ac0c6ab Author: Povilas Kanapickas Date: 2019-12-26 17:13:34 +0200 genesys: Remove excessive debug logs during data read commit 2b00ef39950e32447080687c4373bda1fae0e955 Author: Povilas Kanapickas Date: 2019-12-26 17:13:33 +0200 genesys: Improve calibration speed by making convergence faster commit fd13c10b5cebb6a2375a4ba40fd17748b97e66c4 Author: Povilas Kanapickas Date: 2019-12-26 17:13:32 +0200 genesys: Fix invalid memory access in ImagePipelineNodeDesegment commit cacc68a6d45ccd656a71ca6b0593a9eeefda22ae Author: Povilas Kanapickas Date: 2019-12-26 17:13:31 +0200 genesys: Fix initial register values on gl124 commit 66ac8c62032829da854ff5a5b29957ee94c25160 Author: Povilas Kanapickas Date: 2019-12-26 17:13:30 +0200 genesys: Fix sensor table for LiDE 120 commit 2661115251be1bb7dd53382773ce2db0066a59d9 Author: Povilas Kanapickas Date: 2019-12-26 17:13:29 +0200 genesys: Fix sensor table for LiDE 110 commit d7d227a3f7b58bd6cbae4a019042c3ea9230d798 Author: Povilas Kanapickas Date: 2019-12-26 17:13:28 +0200 genesys: Fix sensor table for LiDE 200 commit 09a84d11d80b0b7cd18c440701ce15f2640c9526 Author: Povilas Kanapickas Date: 2019-12-26 17:13:27 +0200 genesys: Fix sensor table for LiDE 100 commit 01bdc63d7d3c73465340c19075d5af5b3981d7f3 Author: Povilas Kanapickas Date: 2019-12-26 17:13:26 +0200 genesys: Fix step selector masks on gl124 commit 40a2a0d55491fae4b592b596ac2088b594eecfd3 Author: Povilas Kanapickas Date: 2019-12-26 17:13:25 +0200 genesys: Remove unnecessary model check in calibration init commit 47431a8776881613a993e198c5a1b6330b2eae81 Author: Povilas Kanapickas Date: 2019-12-26 17:13:24 +0200 genesys: Fix exception on LiDE 210 when going back from unknown position commit e2bb81ef01e37cad50a4fde4cf11e24ab0476180 Author: Povilas Kanapickas Date: 2019-12-26 17:13:23 +0200 genesys: Add extra sleep during scan stop for gl646 commit d190c559c24f0d984a4fba7394a60e2801572722 Merge: 0ae128d1ab64 d1940cf9b843 Author: Olaf Meeuwissen Date: 2019-12-26 10:27:47 +0000 Merge branch 'epjitsu-backend-generates-compiler-warning-in-debian-10' into 'master' Resolve "epjitsu backend generates compiler warning in Debian 10" Closes #193 See merge request sane-project/backends!267 commit d1940cf9b843d871a8eae32a44d2967d9b2c92da Author: Ralph Little Date: 2019-12-21 21:44:39 -0800 epjitsu: strncpy() compiler warning suppressed. commit 0ae128d1ab64bfacead805ab5f8177b672ad9f9d Merge: 6d9cb35b1faf 3d1242496118 Author: Olaf Meeuwissen Date: 2019-12-24 12:03:09 +0000 Merge branch 'gt68xx-backend-generates-compiler-warnings-on-debian-10' into 'master' Resolve "gt68xx backend generates compiler warnings on Debian 10" Closes #194 See merge request sane-project/backends!268 commit 3d1242496118536af32cd1865ffd177be26ffe83 Author: Ralph Little Date: 2019-12-21 22:53:16 -0800 gt68xx: suppressed strncpy compiler warnings. commit a4360b0a75fe730e78bf6722d7630eba4941a94a Author: Ralph Little Date: 2019-12-21 22:07:39 -0800 gt68xx: suppress snprintf warning with return length check. commit 6d9cb35b1faf808f8da602617d0cc739e6deba49 Merge: c77f89de6250 b02a208a943a Author: Olaf Meeuwissen Date: 2019-12-24 09:05:30 +0000 Merge branch 'as6e-generates-compiler-warnings-in-debian-10' into 'master' Resolve "as6e generates compiler warnings in Debian 10" Closes #192 See merge request sane-project/backends!266 commit b02a208a943a1a128d3b27bd9a63e33f316ef4a9 Author: Ralph Little Date: 2019-12-21 22:02:50 -0800 as6e: data type correction. size_t is unsigned so -1 error from snprintf would not be properly detected. commit 2230614edec80c81868d1c0335b914e2fcd1d75c Author: Ralph Little Date: 2019-12-21 21:24:19 -0800 as6e: compiler warnings suppressed for problematic use of strncat. Replaced with snprintf which simplifies the code and suppresses the warning. commit c77f89de62502c4e4b86afb68729226d8787723d Merge: f506aceb33d3 3bb5e94d654a Author: Olaf Meeuwissen Date: 2019-12-24 09:01:13 +0000 Merge branch 'artec-backend-produces-compiler-warnings-on-debian-10' into 'master' Resolve "artec backend produces compiler warnings on Debian 10" Closes #191 See merge request sane-project/backends!265 commit 3bb5e94d654acae88aef0efef908459a054cd45a Author: Ralph Little Date: 2019-12-21 20:57:48 -0800 artec: strncpy replaced with memcpy() This was a cheeky strncpy hiding what is really the job of memcpy(). commit f506aceb33d348b6cfca3149bc3deee19696e1d0 Merge: 92308130f4ec 821efb262692 Author: Olaf Meeuwissen Date: 2019-12-24 08:59:27 +0000 Merge branch 'ibm-backend-produces-compiler-warnings-in-debian-10' into 'master' Resolve "IBM & Ricoh backends produces compiler warnings in Debian 10" Closes #190 See merge request sane-project/backends!264 commit 821efb262692b3782a948f8ad7cafdc744c1c502 Author: Ralph Little Date: 2019-12-21 20:21:52 -0800 ibm/ricoh: suppress warning by using snprintf instead of strncat commit 92308130f4ec5d978f629a6b78b13184c9407884 Merge: 9527e72a53cd 328a5d654bef Author: Ralph Little Date: 2019-12-23 17:04:19 +0000 Merge branch '197-fix-hp3900-backend-compiler-warning' into 'master' hp3900: Fix [-Wtautological-compare] compiler warning Closes #197 See merge request sane-project/backends!280 commit 328a5d654bef3b8367eb696e3f5e0a9f169d88e0 Author: Olaf Meeuwissen Date: 2019-12-23 21:57:36 +0900 hp3900: Fix [-Wtautological-compare] compiler warning Although the fix changes the backend's logic, it probably matches what was the original code's intent. commit 9527e72a53cdef9f1922c588f4e4213876132efc Merge: 37f96cc800f2 70ef8db5d6f5 Author: Ralph Little Date: 2019-12-23 16:49:11 +0000 Merge branch '195-fix-hp-backend-compiler-warning' into 'master' hp: Fix [-Wstringop-truncation] compiler warning Closes #195 See merge request sane-project/backends!279 commit 70ef8db5d6f5a640093a06d733c82fd56c6df429 Author: Olaf Meeuwissen Date: 2019-12-23 21:36:13 +0900 hp: Fix [-Wstringop-truncation] compiler warning commit 37f96cc800f2705acf5ca1a6fad3ff059f455cc4 Merge: 74f601188b4e fd407cc58bce Author: Ralph Little Date: 2019-12-23 16:39:34 +0000 Merge branch 'fix-dll-compiler-warnings' into 'master' Address dll compiler warnings See merge request sane-project/backends!269 commit fd407cc58bce4162a8329fb4ec587837f50c1fd0 Author: Olaf Meeuwissen Date: 2019-12-22 18:34:45 +0900 dll: Make previous change backwardly compatible Older GCC do not know the cast-function-pragma and flag that as a warning. That results in an error on our Debian 9 build. commit e7f6c6e864200fc51a05c8eb5963a78b3251adbd Author: Olaf Meeuwissen Date: 2019-12-22 17:58:39 +0900 dll: Ignore [-Wcast-function-type] compiler warnings commit a993e6438eff1e5b71f1dcc97df478ca954aae9f Author: Olaf Meeuwissen Date: 2019-12-22 16:05:51 +0900 dll: Fix [-Wformat-truncation=] compiler warning Per POSIX, `readdir` returns filenames that are at most `NAME_MAX` long. commit 74f601188b4ee0dc0737dfbc1418c36f748448e2 Merge: 4bf176c73efc 2788328e1926 Author: Olaf Meeuwissen Date: 2019-12-23 11:57:10 +0000 Merge branch 'escl-force-formatext' into 'master' indicator of the presence of the DocumentFormatExt field See merge request sane-project/backends!261 commit 2788328e1926da293261c0410321565baf1cfe1b Author: Thierry HUCHARD Date: 2019-12-22 13:48:55 +0100 Fix: better readability. commit afc9b62c7e43b99c0e951da9b2aec9484afd7471 Author: Thierry HUCHARD Date: 2019-12-22 09:50:04 +0100 Fix: better readability. commit 6506cd9595843d6a2eab0628dfd4b39c9dc4db3f Author: thierry1970 Date: 2019-12-19 14:30:35 +0100 indicator of the presence of the DocumentFormatExt field, forces for certain printer the output format. commit 4bf176c73efc4f01a519e74d18800ef894b7f0bb Merge: a0e978d4aee3 538b34f418f9 Author: Povilas Kanapickas Date: 2019-12-23 00:42:01 +0000 Merge branch 'genesys-fix-gl646-scanning' into 'master' genesys: Work around invalid acceleration curves on certain gl646 scanners See merge request sane-project/backends!278 commit 538b34f418f9bf2a2b6f603a715ab9b30f5520a4 Author: Povilas Kanapickas Date: 2019-12-23 02:27:20 +0200 genesys: Fix testing mode in gl646 commit 5d83753b07871fd6e204c44eb74011f06795e0c6 Author: Povilas Kanapickas Date: 2019-12-23 02:27:05 +0200 genesys: Work around bad acceleration curves on certain gl646 scanners commit a0e978d4aee319578aee0e2f8a393836d1ed089b Merge: e8f6a508ca2a fb1dea325cdb Author: Povilas Kanapickas Date: 2019-12-22 23:59:05 +0000 Merge branch 'genesys-fix-gl646-scanning' into 'master' genesys: Fix completely broken scanning on GL646 See merge request sane-project/backends!277 commit fb1dea325cdbdb8e1f72ee85a31f817d59395408 Author: Povilas Kanapickas Date: 2019-12-23 01:43:39 +0200 genesys: Fix crash when moving back hove on gl646 commit 9f4cabb55c778446a3220e7bdf51128860f70afe Author: Povilas Kanapickas Date: 2019-12-23 01:43:38 +0200 genesys: Fix random lock ups when waiting for motor to stop on gl646 commit fffcb6254410c9da9841447d50e1d7c58528702e Author: Povilas Kanapickas Date: 2019-12-23 01:43:37 +0200 genesys: Fix completely broken backtracing calculation on gl646 commit e8f6a508ca2a022512d5f1a55f058928158606ce Merge: b10c15595b26 e437f17b40ec Author: Povilas Kanapickas Date: 2019-12-22 21:37:51 +0000 Merge branch 'genesys-8600f-enable-ir-resolutions' into 'master' genesys: Enable additional IR resolutions on CanoScan 8600F See merge request sane-project/backends!276 commit e437f17b40ece55bd34b242d638057b21f444c05 Author: Povilas Kanapickas Date: 2019-12-22 23:23:18 +0200 genesys: Enable more infrared resolutions on 8600F commit d1a0c1589a5c22a5e0c36a18123bed3e6c797744 Author: Povilas Kanapickas Date: 2019-12-22 23:23:17 +0200 genesys: Sort 8600F sensor tables commit 216250361fd9639a6938c297c36a265545bc0dc6 Author: Povilas Kanapickas Date: 2019-12-22 23:23:16 +0200 genesys: Fix uninitialized variable warning commit b10c15595b26d5cd49ff67f25cfefc7f7c240b31 Merge: 408329e8cfc7 6fb588648c9b Author: Povilas Kanapickas Date: 2019-12-22 15:55:53 +0000 Merge branch 'genesys-use-fast-moving-home' into 'master' genesys: Use fast moving when returning home See merge request sane-project/backends!275 commit 6fb588648c9bf57f7c03e2d19759671b00a1ec3d Author: Povilas Kanapickas Date: 2019-12-14 10:20:27 +0200 genesys: Fix infinite loop when we overestimate distance to home sensor commit 01ca9c9030cb1af5303c5d98ecb5ff044c5bd210 Author: Povilas Kanapickas Date: 2019-12-14 10:20:20 +0200 genesys: Use fast moving for most of the distance when moving back home commit 2397d8879a8208c00ad87b1becd8d0a730e53ef1 Author: Povilas Kanapickas Date: 2019-12-14 10:20:18 +0200 genesys: Fix collection of scanhead movement information commit 29166d9b3b2b22b6f812192dae95bdcac112d014 Author: Povilas Kanapickas Date: 2019-12-14 10:20:16 +0200 genesys: Remove rewind() function due to unreliability commit 408329e8cfc7d7025c0798d78aafb5b615887e54 Merge: 01bae1305f65 fd29cae64aad Author: Povilas Kanapickas Date: 2019-12-22 15:24:48 +0000 Merge branch 'genesys-fix-motor-acceleration-calculations' into 'master' genesys: Fix calculations of scan head acceleration tables See merge request sane-project/backends!274 commit fd29cae64aad017b09c3bc9e19e2a9b0988d6c1f Author: Povilas Kanapickas Date: 2019-12-14 10:20:26 +0200 genesys: Correctly set LINESEL on 4400F commit 69067f2f7f0f48cf2ae7c20277c18b166f7babce Author: Povilas Kanapickas Date: 2019-12-14 10:20:25 +0200 genesys: Remove no longer used final_exposure commit 35385625deaa0b07c849ce536de4d4be88fb9223 Author: Povilas Kanapickas Date: 2019-12-14 10:20:24 +0200 genesys: Delete unused code commit 38f988dc39afa95d673f3db8caa8bcf28a036467 Author: Povilas Kanapickas Date: 2019-12-14 10:20:23 +0200 genesys: Use correct SHDAREA setting on 4400F commit 67638f55b21f6551252079dd853d21291a3dac29 Author: Povilas Kanapickas Date: 2019-12-14 10:20:22 +0200 genesys: Use a more reasonable backtract distance on gl843 commit 2a6081affe9e8b84d648dd6c1b4079351783b5d0 Author: Povilas Kanapickas Date: 2019-12-14 10:20:21 +0200 genesys: Fix scanning table length calculation on gl843 commit 33bfa923d5dfc5c3ec2a9c8520a146eb0ec74ac5 Author: Povilas Kanapickas Date: 2019-12-14 10:20:19 +0200 genesys: Add a way to create fastest motor acceleration table commit 83fa276b907cfbfbf65ef6bafa4e16fdd963dea6 Author: Povilas Kanapickas Date: 2019-12-14 10:20:17 +0200 genesys: Fix bug in setting up motor tables 3,4,5 on gl843 commit 48fd80d20829f36b2db43a6c9d5a5749be18b514 Author: Povilas Kanapickas Date: 2019-12-14 10:20:15 +0200 genesys: Simplify handling of motor step multiplier commit 1d7adfcfc19aaf64f8c192941f18b4b4fc219060 Author: Povilas Kanapickas Date: 2019-12-14 10:20:14 +0200 genesys: Fix naming of step multiplier commit 194920159d299c3450e335f378e7993823d62bc4 Author: Povilas Kanapickas Date: 2019-12-14 10:20:13 +0200 genesys: Remove support for legacy curves commit 3d9113997f30de42066727e81d0cd24c51335b65 Author: Povilas Kanapickas Date: 2019-12-14 10:20:12 +0200 genesys: Switch remaining legacy curve uses to physical curves commit 9a245a1325a16da6a483d17f12905d3c55ebc71e Author: Povilas Kanapickas Date: 2019-12-14 10:20:11 +0200 genesys: Switch unused motor curves to physical format commit 7d477da4047886526e8a69f25ff8e22059ca3cb5 Author: Povilas Kanapickas Date: 2019-12-14 10:20:10 +0200 genesys: Use MotorSlope in gl646 code commit dec996277331dbed6dfa6cb6e2267129a6c0e759 Author: Povilas Kanapickas Date: 2019-12-14 10:20:09 +0200 genesys: Make the size of slope tables configurable by asic type commit c685ac075be5cdc9771a8e36bb5a65ff54742b6f Author: Povilas Kanapickas Date: 2019-12-14 10:20:08 +0200 genesys: Return MotorSlopeTable out of *_generate_slope_table() commit 8731a6cc5d0f025ba3800872eb8bc3c83f595d2c Author: Povilas Kanapickas Date: 2019-12-14 10:20:07 +0200 genesys: Return MotorSlopeTable out of *_create_slope_table3() commit 7c6229f272bace0d022f08842afeffb9ef28e958 Author: Povilas Kanapickas Date: 2019-12-14 10:20:06 +0200 genesys: Rename MotorSlopeTable::{scan_steps -> steps_count} commit 54034c3fae484974898ec162cc9825b608c5983a Author: Povilas Kanapickas Date: 2019-12-14 10:20:05 +0200 genesys: Fix Opticfilm 7200i and 7500i motor curves commit 868c4378962f1dade54a6ef2ea1fe97dd97348da Author: Povilas Kanapickas Date: 2019-12-14 10:20:04 +0200 genesys: Convert hardcoded motor slopes to physical slope config commit 465077c6cb6c832708f1da28563864d910e64fac Author: Povilas Kanapickas Date: 2019-12-14 10:20:03 +0200 genesys: Remove manual override of the first value in motor tables commit f605e7c2297e6d26c8d3521301206b25c3ae40a8 Author: Povilas Kanapickas Date: 2019-12-14 10:20:02 +0200 genesys: Fix testing mode recording of scanner_slow_back_home_ta() commit c1d791f11cdbfbf2090570b68be17bf3749204a2 Author: Povilas Kanapickas Date: 2019-12-14 10:20:01 +0200 genesys: Raise error when TA scan head takes too long to return home commit 01bae1305f656794f82b58a78751220efde4fec6 Merge: e0679c316e85 5cfb44366bb6 Author: Povilas Kanapickas Date: 2019-12-22 14:48:28 +0000 Merge branch 'genesys-remove-broken-resolutions' into 'master' genesys: Disable broken resolutions on LiDE 50 and 80 See merge request sane-project/backends!273 commit 5cfb44366bb6e2405a573c03c980bd85445fe3bd Author: Povilas Kanapickas Date: 2019-12-14 10:13:09 +0200 genesys: Disable broken resolutions on LiDE 50 and 80 commit e0679c316e85c001c15a2afc0fb13bfa6dec8c18 Merge: 053a2c10d33a 49934f067884 Author: Povilas Kanapickas Date: 2019-12-22 14:44:01 +0000 Merge branch 'genesys-fix-4850c' into 'master' genesys: Fix black scans on HP 4850C See merge request sane-project/backends!272 commit 49934f0678843df5c4b1543fade1b60902682c10 Author: Povilas Kanapickas Date: 2019-12-14 10:11:37 +0200 genesys: Fix black side calibration on HP 4850c commit 8d8157b96edee51392084b56480000d5dc0cb5bd Author: Povilas Kanapickas Date: 2019-12-14 10:11:36 +0200 genesys: Fix scan area Y position on HP 4850c commit 355f2ddd4db6c51168f20111ffdafd0d836152ad Author: Povilas Kanapickas Date: 2019-12-14 10:11:35 +0200 genesys: Fix black scans on HP 4850c commit 053a2c10d33aac78f863fb24dfa882b354bac576 Merge: 42070a606cd5 8c99dc4819d6 Author: Povilas Kanapickas Date: 2019-12-22 14:38:58 +0000 Merge branch 'genesys-fix-stagger-config' into 'master' genesys: Fix staggerred CCD configuration on OpticFilm 7200i/7300/7500i and Canon 8400F/8600F scanners See merge request sane-project/backends!271 commit 8c99dc4819d6ef68330a043e8617519df3a938cc Author: Povilas Kanapickas Date: 2019-12-14 09:42:58 +0200 genesys: Fix stagger configuration on 8600F commit a8df8201e9d8c083fcc65c5836d231d8eb95e4f3 Author: Povilas Kanapickas Date: 2019-12-14 09:42:57 +0200 genesys: Fix stagger configuration on 8400F commit 8a2af6d4b2458a155e1bacc932f0b66bfed6710b Author: Povilas Kanapickas Date: 2019-12-14 09:42:56 +0200 genesys: Fix stagger configuration on OpticFilm scanners commit 42070a606cd5c5df73ac1d5d095d6a7c8716c676 Merge: e0d669acfe01 02b0f0d474fd Author: Povilas Kanapickas Date: 2019-12-22 14:23:17 +0000 Merge branch 'genesys-stagger-convert' into 'master' genesys: Convert old broken stagger configuration to a per-sensor configuration See merge request sane-project/backends!270 commit 02b0f0d474fddffa0097a549336f8e389d6bd50c Author: Povilas Kanapickas Date: 2019-12-14 09:12:29 +0200 genesys: Convert old broken stagger configuration to per-sensor config commit 4c0f63052b6c91b51c9ef2d57ffccb62ec6c6fa7 Author: Povilas Kanapickas Date: 2019-12-14 09:12:28 +0200 genesys: Add a way to configure per-sensor stagger config commit e0d669acfe01e26c619cbbdf342b6ad475c0b9b3 Merge: 3b0ab4f09716 809ccfe695cc Author: Olaf Meeuwissen Date: 2019-12-22 03:49:59 +0000 Merge branch 'bump-alpine-to-3.11' into 'master' CI: Bump Alpine from 3.10 to 3.11 See merge request sane-project/backends!263 commit 809ccfe695ccad9607207140829f86a4626fb4aa Author: Olaf Meeuwissen Date: 2019-12-22 12:33:27 +0900 CI: Bump Alpine from 3.10 to 3.11 commit 3b0ab4f097166725ff1f2f718a9a9437371453ca Merge: 3a4d5a002c1c 790bdd822a29 Author: Olaf Meeuwissen Date: 2019-12-22 02:05:53 +0000 Merge branch 'plustek-silence_compiler_warning' into 'master' Resolve "Plustek backend generates compiler warnings on Debian 10" Closes #189 See merge request sane-project/backends!262 commit 790bdd822a29779a90e7f903b5daf655bc5258cf Author: Ralph Little Date: 2019-12-22 02:05:53 +0000 plustek: Fix [-Wformat-overflow=] compiler warnings on Debian 10 Also fixes a format truncation warning observed elsewhere. commit 3a4d5a002c1c5db5055974bed11b6f8edffb3a76 Merge: 02251c6489f9 97dea9495982 Author: Olaf Meeuwissen Date: 2019-12-19 12:14:16 +0000 Merge branch '120-fix-all-compiler-warnings-on-the-debian-10-builds' into 'master' Fix umax_pp compiler warnings See merge request sane-project/backends!259 commit 97dea949598220dba43fd1dcadf0f8930f81260f Author: Ralph Little Date: 2019-12-19 12:14:16 +0000 Revert "plustex: suppressed format truncated compiler warning." This reverts commit cc7fbd35e535e22cd13cd58979921e0c01fb38b4 commit 02251c6489f9923a9492bf0eedbce402bf342789 Merge: 277194d5347d 8de208c24c56 Author: Olaf Meeuwissen Date: 2019-12-19 11:57:25 +0000 Merge branch 'add_sp112' into 'master' Add SP-112SU model See merge request sane-project/backends!260 commit 8de208c24c5618009e99dc5cb44b7a98a17649ed Author: Stanislav Yuzvinsky Date: 2019-12-19 11:57:25 +0000 Add SP-112SU model commit 277194d5347ddc978ad76f10f76156126a2deab9 Author: Thierry HUCHARD Date: 2019-12-17 20:00:19 +0100 Revert "uniformise signature." This reverts commit 2eff3e82bad5b03dd5af6cf491fe206c48b6e928. commit 2eff3e82bad5b03dd5af6cf491fe206c48b6e928 Author: Thierry HUCHARD Date: 2019-12-17 19:58:14 +0100 uniformise signature. commit f95fcf9d56e44869e4185a595c0a2ad08d13a2be Author: Rolf Bensch Date: 2019-12-15 11:37:41 +0100 pixma: Canon imageCLASS D570 is working See issue sane-project/backends#182 commit 8270a82bc0423df7739813c734307e3a2ac87052 Merge: fdcb2fa5e8d6 7c34046a3960 Author: Olaf Meeuwissen Date: 2019-12-14 08:13:54 +0000 Merge branch 'escl-2019' into 'master' See merge request sane-project/backends!242 commit 7c34046a396045c8e042d80674c6ef1ea880d97d Author: Touboul Nathane Date: 2019-12-14 08:13:54 +0000 Add escl backend commit fdcb2fa5e8d6a1a4856c88fbf8787ecc4cfc768c Author: m. allan noah Date: 2019-12-11 21:10:07 -0500 Update fujitsu.desc status for iX1500 commit 58b4371bb50ee68c5eafcf4757a9d25d525e4e4f Merge: ae732126b94a 4ef2e3987234 Author: Povilas Kanapickas Date: 2019-12-10 23:39:25 +0000 Merge branch 'genesys-remove-sensor-profile' into 'master' genesys: Remove sensor profile struct See merge request sane-project/backends!257 commit 4ef2e3987234f03e765a5290c34823dcb9aef693 Author: Povilas Kanapickas Date: 2019-12-08 11:42:32 +0200 genesys: Remove remaining uses of SensorProfile commit 7971ee56934e300ebd564fdf0045eaaeaf232193 Author: Povilas Kanapickas Date: 2019-12-08 11:42:31 +0200 genesys: Use sensor profile information directly from profiles commit 524c9de1fa0cc953fc3b441db22ca141cf2d52d4 Author: Povilas Kanapickas Date: 2019-12-08 11:42:30 +0200 genesys: Fix calibration exposure in testing mode commit 791433c895a0ed67aebd0696bf857cdbf0cfd938 Author: Povilas Kanapickas Date: 2019-12-08 11:42:29 +0200 genesys: Duplicate SensorProfile information into Genesys_Sensor struct commit 716689161dd3013b093c75e3a18dea95efc03f0d Author: Povilas Kanapickas Date: 2019-12-08 11:42:28 +0200 genesys: Use correct sensor for scanning on gl124 commit 1f33bafe5d561660d0a414a45b6788e5e9e32f88 Author: Povilas Kanapickas Date: 2019-12-08 11:42:27 +0200 genesys: Remove unused fallback sensor profile code commit a7fd1d6ae1fc499605cba9bea5bb8aad0b5113dd Author: Povilas Kanapickas Date: 2019-12-08 11:42:26 +0200 genesys: Simplify sensor profile definition commit 99101d43194999dfe032e473351aea46dd9a833b Author: Povilas Kanapickas Date: 2019-12-08 11:42:25 +0200 genesys: Move ResolutionFilter definition above SensorProfile commit 7a2d3df0e3c9f0c2a9a99d08a4c90d44ba4fde77 Author: Povilas Kanapickas Date: 2019-12-08 11:42:24 +0200 genesys: Use correct sensor for calibration on gl847 commit 6c8c4bb74f48f01a46d5f80ac94abf035164ca91 Author: Povilas Kanapickas Date: 2019-12-08 11:42:23 +0200 genesys: Use correct sensor for calibration on gl846 commit 4dfa7d25a327ea84225700545ec81556c3c16115 Author: Povilas Kanapickas Date: 2019-12-08 11:42:22 +0200 genesys: Use correct sensor for calibration on gl124 commit 059da17ac06a880c329af6ab16713c83689bc092 Author: Povilas Kanapickas Date: 2019-12-08 11:42:21 +0200 genesys: Simplify register definitions on gl846 commit cb64243e5dcf4132abb24238104735eea2fbee3b Author: Povilas Kanapickas Date: 2019-12-08 11:42:20 +0200 genesys: Simplify register definitions on gl847 commit 06742ab7d44a8764a4f760da806e20a1c71405d9 Author: Povilas Kanapickas Date: 2019-12-08 11:42:19 +0200 genesys: Simplify register definitions on gl124 commit ae732126b94a867ba59d7e55e67c4d4d48722bd1 Merge: 037f06712ecb 3d068a9eee49 Author: Povilas Kanapickas Date: 2019-12-09 17:33:39 +0000 Merge branch 'genesys-motors-refactor' into 'master' genesys: Extract move chip-specific motor handling code to the common area See merge request sane-project/backends!256 commit 3d068a9eee49cae318aa9b14e7d69f26d219e896 Author: Povilas Kanapickas Date: 2019-12-01 10:43:12 +0200 genesys: Fix TA support in scanner_move() commit 6a5fd2d36c288c396be576612c00e4db0c5d6e17 Author: Povilas Kanapickas Date: 2019-12-01 10:43:11 +0200 genesys: Support multiple scan methods in scanner_move() commit 27aea235b47c7ba6c98f6788f57d1d87c104e330 Author: Povilas Kanapickas Date: 2019-12-01 10:43:10 +0200 genesys: Rename slow_back_home() to move_back_home() commit 35c8cb84f81102dc8c9483a9f7175291c500d0b1 Author: Povilas Kanapickas Date: 2019-12-01 10:43:09 +0200 genesys: Extract slow_back_home_ta() to common code commit 037f06712ecbba2033ae514b18f54b32e529a02e Merge: 4156fae159d7 5b7c19a7d58f Author: Povilas Kanapickas Date: 2019-12-09 17:06:05 +0000 Merge branch 'genesys-reduce-duplication' into 'master' genesys: Reduce duplication in scan head movement routines See merge request sane-project/backends!255 commit 5b7c19a7d58f5fac6752fd2b53b1cf376db1e003 Author: Povilas Kanapickas Date: 2019-12-01 10:08:59 +0200 genesys: Reduce duplication of *_stop_action() across the ASICs commit 9e092f9d67651b5db82500098b1b5c7f96d1547a Author: Povilas Kanapickas Date: 2019-12-01 10:08:58 +0200 genesys: Extract scanner_is_motor_stopped() to use across the asics commit c2863d6ba14cccd615eb43a371c22d3d2cac23f3 Author: Povilas Kanapickas Date: 2019-12-01 10:08:57 +0200 genesys: Fix inconsistent check of scanner status in *_stop_action() commit a90ab552a9e78b6b0aec318998865b0659483eb5 Author: Povilas Kanapickas Date: 2019-12-01 10:08:56 +0200 genesys: Extract scanner_stop_action_no_move() to use across the asics commit 6913513dc26c98a0965429086831f306d3279c91 Author: Povilas Kanapickas Date: 2019-12-01 10:08:55 +0200 genesys: Extract regs_set_optical_off() to use across the ASICs commit 052aa0162608fdb435f6f90733ca6d34911a2df9 Author: Povilas Kanapickas Date: 2019-12-01 10:08:54 +0200 genesys: Reduce duplication of *_feed() across different ASICs commit e435036b97fb0876804b06be7b043e280f2c8caf Author: Povilas Kanapickas Date: 2019-12-01 10:08:53 +0200 genesys: Use single function to set exposure on all ASICs commit 4156fae159d707a145ac9728f21d7adb7a7573f0 Merge: 560af93a391f 738444cd1ca2 Author: Rolf Bensch Date: 2019-12-04 16:10:27 +0000 Merge branch 'debug_add_timestamp' into 'master' add timestamp to debug outputs See merge request sane-project/backends!228 commit 738444cd1ca29d0bfe4633f17f1183d5c8e3f1ad Author: Rolf Bensch Date: 2019-11-26 17:33:07 +0100 add µsec to timestamp commit d9f35d1b5e582b99a1e286cff80d6d22906fd885 Author: Rolf Bensch Date: 2019-10-19 22:00:35 +0200 add timestamp to debug outputs commit 560af93a391f94bf57b202f5f95b88b00659e638 Author: Rolf Bensch Date: 2019-12-04 17:07:20 +0100 pixma: update manpage commit 34922d957413688469627fb9d9761fe2df4bd8bc Author: Louis Lagendijk Date: 2019-12-04 00:56:37 +0100 pixma: fixed MB5400 ADF scan. MB5400 works upto 1200DPI and with ADF or ADF-duplex commit 0bbb7ef3ba837162929f946595aa177963c3de19 Author: Louis Lagendijk Date: 2019-12-03 22:05:13 +0100 tstbackend: make it compile again commit 46eacadc78a3aa34aaa68a4ecd9e2b9316a2399a Author: Louis Lagendijk Date: 2019-12-03 22:01:08 +0100 pixma_bjnp.c: fix parsing of mfnp in pixma config file commit 22c1bceda2c4bd1992a279ebc35f3f1c93567a10 Merge: fa75eff7b4df 3a1c1bad32f9 Author: Olaf Meeuwissen Date: 2019-12-03 11:02:34 +0000 Merge branch 'update_uk' into 'master' Update Ukrainian translation See merge request sane-project/backends!254 commit 3a1c1bad32f983037bc537d10007c691b61a7558 Author: Yuri Chornoivan Date: 2019-12-03 11:02:34 +0000 Update Ukrainian translation commit fa75eff7b4df9bf98f64567697487b9a9a9e902b Merge: f41bd003d065 311b9ff5bfb3 Author: Olaf Meeuwissen Date: 2019-12-02 11:59:50 +0000 Merge branch '178-ci-requires-remote-resources-to-succeed-in-tools-update-upstreams-sh' into 'master' Resolve "CI requires remote resources to succeed in ./tools/update-upstreams.sh" Closes #178 See merge request sane-project/backends!253 commit 311b9ff5bfb3ca2a14d48c5c00a08597b65fb40a Author: Olaf Meeuwissen Date: 2019-12-02 20:40:20 +0900 Remove dependency on external resources during CI. Fixes #178 The update-upstreams.sh script is kept for the convenience of those who want or need to use it. It has been slightly modified to provide more feedback in case of error conditions. commit f41bd003d06505c4d4cf6b10f103a143b1fdfa58 Merge: 1c4cff20050f f98bec2afb2a Author: Povilas Kanapickas Date: 2019-12-01 13:22:45 +0000 Merge branch 'genesys-use-full-scan-ta' into 'master' genesys: Use full scan initialization in slow_back_home_ta() See merge request sane-project/backends!251 commit f98bec2afb2a093cfbc5b86cabcea478e907cb3e Author: Povilas Kanapickas Date: 2019-11-30 19:19:21 +0200 genesys: Use full scan initialization in slow_back_home_ta() commit 66a95875d402ef27ef49771f663f06a5a8cc4d60 Author: Povilas Kanapickas Date: 2019-11-30 19:19:20 +0200 genesys: Remove duplicate operation a register The same value is set just several lines above. commit 4743a0235359ef9f5749aa9a38850b199a437368 Author: Povilas Kanapickas Date: 2019-11-30 19:19:19 +0200 genesys: Remove duplicate operation on a register init_regs_for_scan_session() already sets the value of the register to what we expect. commit 8aee1f0982dbca641b5553766d692c0626b3d8f8 Author: Povilas Kanapickas Date: 2019-11-30 19:19:18 +0200 genesys: Expose slow_back_home_ta() to the cmd set commit 1c4cff20050fcfe9238a32b9ad4fdd2d919e081d Merge: 6b4be81e9002 c24a956685cc Author: Olaf Meeuwissen Date: 2019-12-01 08:11:28 +0000 Merge branch 'pixma-fix-usbid-case-warning' into 'master' canon_pixma: Use lowercase in usbid description to avoid warning See merge request sane-project/backends!249 commit c24a956685cc13b17a435514f25d5651722d162a Author: Povilas Kanapickas Date: 2019-11-30 17:51:12 +0200 canon_pixma: Use lowercase in usbid description to avoid warning commit 6b4be81e9002006b9c675bfa40f73e0490b183ce Merge: 072487ee7bcd 97ae3fc94621 Author: Povilas Kanapickas Date: 2019-11-30 23:40:52 +0000 Merge branch 'genesys-motors-refactor' into 'master' genesys: Motors refactor See merge request sane-project/backends!250 commit 97ae3fc94621ecbf6e01d0ddbefcf2bc176155fa Author: Povilas Kanapickas Date: 2019-11-30 19:06:52 +0200 genesys: Reduce duplication of slow_back_home() across different asics commit cba6086ce79fcc0debce0a47925b8b73c61ed29b Author: Povilas Kanapickas Date: 2019-11-30 19:06:51 +0200 genesys: Expose init_regs_for_scan_session() to the cmd set commit 79b4d1560ad0121dcd58edb4273fa5a05b502839 Author: Povilas Kanapickas Date: 2019-11-30 19:06:50 +0200 genesys: Remove no longer needed global settings modification commit a355df168a6f0990559dd6eb574f89c5fa451257 Author: Povilas Kanapickas Date: 2019-11-30 19:06:49 +0200 genesys: Expose update_home_sensor_gpio() in the cmd set commit 326733a479888716a4d690b04cc6f288e8570a95 Author: Povilas Kanapickas Date: 2019-11-30 19:06:48 +0200 genesys: Simplify slow_back_home() on gl646 commit 98dbd206eebda52f704b06b032f9cd8032c682a9 Author: Povilas Kanapickas Date: 2019-11-30 19:06:47 +0200 genesys: Reduce duplication of slope upload on gl646 commit 66264538048e89679cc17bc0cea0b37b1f1f39ce Author: Povilas Kanapickas Date: 2019-11-30 19:06:46 +0200 genesys: Reduce duplication of scanner status printing commit df8a411de386ed632b6cda98ec8e9bef0fce121f Author: Povilas Kanapickas Date: 2019-11-30 19:06:45 +0200 genesys: Extract reliable reading of home sensor to separate function commit 2ca325c63854c3287800d82bc9abdab9cd2d4e50 Author: Povilas Kanapickas Date: 2019-11-30 19:06:44 +0200 genesys: Simplify scanner status handling commit 7385e0d305ecac52e5fff85079d2e60928d0488c Author: Povilas Kanapickas Date: 2019-11-30 19:06:43 +0200 genesys: Simplify get_gain4_bit() implementation commit 3ada79c35ab6fcb5cea56595573510bc5781ec6c Author: Povilas Kanapickas Date: 2019-11-30 19:06:42 +0200 genesys: Move motor direction register adjustment to low level functions commit 50149e90642d6ba99e04af04eccc68888e2cc630 Author: Povilas Kanapickas Date: 2019-11-30 19:06:41 +0200 genesys: Use enum class for motor flags commit 072487ee7bcd0489e722d59df8e09a2a7410f144 Author: Louis Lagendijk Date: 2019-11-30 23:51:03 +0100 pixma: made backend obey local_only setting in sane_get_devices commit 6c0282e06f190ffcc023f6ba145d54c052853bf4 Author: Rolf Bensch Date: 2019-11-30 17:52:19 +0100 INSTALL.linux: add missing dev package "libusb-1.0.0-dev" commit 0b4165b27f43b646063ca89c956b8dbefd1a266c Merge: 3dff8085e771 837ec33c7fe0 Author: Povilas Kanapickas Date: 2019-11-30 15:04:14 +0000 Merge branch 'genesys-motors-refactor' into 'master' genesys: Motors refactor See merge request sane-project/backends!248 commit 837ec33c7fe0babb020c1b56aa7029fa310ccf89 Author: Povilas Kanapickas Date: 2019-11-23 12:38:51 +0200 genesys: Make scan flags an enum commit 05e3091d5fb12a853c71e9eb189a4203ea284554 Author: Povilas Kanapickas Date: 2019-11-23 12:38:50 +0200 genesys: Remove duplication of motor startup commit 9cd373fe6c5e4b9d1aa68e54ba72aa1bddf3bc2c Author: Povilas Kanapickas Date: 2019-11-23 12:38:49 +0200 genesys: Return MotorSlopeTable out of sanei_genesys_slope_table() commit 63b0366b43cb256b9e7b83df29fe8884c335dce7 Author: Povilas Kanapickas Date: 2019-11-23 12:38:48 +0200 genesys: Store motor profile tables in std::vector commit cbe45396feb3796e3d9e7a0aac34490832fff9e0 Author: Povilas Kanapickas Date: 2019-11-23 12:38:47 +0200 genesys: Move static initialization functionality to a separate file commit f6f27e053953661c7bbf3261a91ca71553772945 Author: Povilas Kanapickas Date: 2019-11-23 12:38:46 +0200 genesys: Implement building of motor tables based on physical slope commit 34bba237b5c0ed6b29fd93150b4be6d3f341aaf6 Author: Povilas Kanapickas Date: 2019-11-23 12:38:45 +0200 genesys: Don't select motor profile twice during setup of same scan commit b43a21d25a4ae4d4fdeb37d7f5156e90a6cdf17d Author: Povilas Kanapickas Date: 2019-11-23 12:38:44 +0200 genesys: Add new motor slope type commit 15819a7d12eef1e10921b0f4b1f1d10746786bcb Author: Povilas Kanapickas Date: 2019-11-23 12:38:43 +0200 genesys: Add tests for sanei_genesys_create_slope_table3() commit ddcce5295faa5cfdc822f308391d917d496a3656 Author: Povilas Kanapickas Date: 2019-11-23 12:38:42 +0200 genesys: Use StepType enum more commit c6784d2f1db40a65721766b6fc6442a93bc6eab8 Author: Povilas Kanapickas Date: 2019-11-23 12:38:41 +0200 genesys: Simplify API of sanei_genesys_create_slope_table3() commit ce59a69224cb24a07f27f6f8ccd4a38f54476e9e Author: Povilas Kanapickas Date: 2019-11-23 12:38:40 +0200 genesys: Record motor slope tables in the config test commit d8e8899a760fc24e175f2641f69af7ba3eb674af Author: Povilas Kanapickas Date: 2019-11-23 12:38:39 +0200 genesys: Remove unused code commit 3dff8085e771ca945fc46fd93cdf2eb8454e8470 Merge: 9bee993c3cd2 acc5fd318c3d Author: Povilas Kanapickas Date: 2019-11-24 12:19:20 +0000 Merge branch 'genesys-remove-macros' into 'master' genesys: Use enums instead of macro constants See merge request sane-project/backends!246 commit acc5fd318c3d3010f001cf72a16e4f3cc6c99370 Author: Povilas Kanapickas Date: 2019-11-18 22:10:29 +0200 genesys: Replace unneeded uses of macros with enums commit 10662d297336c712ac7499cf293e723ab268d550 Author: Povilas Kanapickas Date: 2019-11-18 22:10:28 +0200 genesys: Remove uses of SETREG and INITREG macros commit 185fdcc9ce0b45e93fff54df24ce35b61c9ccc97 Author: Povilas Kanapickas Date: 2019-11-18 22:10:27 +0200 genesys: Replace defines with constants in register lists commit 6d9259bd4853fa1299816910a7bbaad89cc66604 Author: Povilas Kanapickas Date: 2019-11-18 22:10:26 +0200 genesys: Put chip-specific functionality into namespaces commit 9bee993c3cd25dac13481a2d641838df77e1b1e7 Merge: f1cf1ff1d97a 9472dce5f7cb Author: Olaf Meeuwissen Date: 2019-11-24 01:59:29 +0000 Merge branch 'fix-scanimage-overflow' into 'master' scanimage: Fix 32-bit overflow when calculating total bytes to transfer See merge request sane-project/backends!234 commit 9472dce5f7cbd4446fc1bc2bc4a580f3e0a981a4 Author: Povilas Kanapickas Date: 2019-11-09 22:58:30 +0200 scanimage: Fix 32-bit overflow when calculating total bytes to transfer commit f1cf1ff1d97ac7387c4fac22c6e27406b7741e6c Merge: 5cd65e6e1954 f366073b0835 Author: Povilas Kanapickas Date: 2019-11-23 13:10:15 +0000 Merge branch 'genesys-canon-4400f' into 'master' genesys: Add initial support for flatbed mode on Canon 4400F See merge request sane-project/backends!245 commit f366073b0835c4005f4f4fc891f407cc4c25a6c3 Author: Povilas Kanapickas Date: 2019-11-23 14:54:10 +0200 genesys: Add initial support for flatbed mode on Canon 4400F commit 5cd65e6e19544d4421bee51336eb61d7841347fe Merge: 7e7f02773e1e e46b6927dd34 Author: Povilas Kanapickas Date: 2019-11-23 13:09:27 +0000 Merge branch 'correct-man' into 'master' Mention correct man file for ricoh2 See merge request sane-project/backends!243 commit e46b6927dd344c8b0aa1ab4d5021c2b7163885a5 Author: Stanislav Yuzvinsky Date: 2019-11-17 17:08:42 +0300 Mention correct man file for ricoh2 commit 7e7f02773e1e62ef3ae16229863fa8561e78d3e2 Author: Alex Belkin Date: 2019-11-05 22:01:53 +0300 xerox_mfp: Fix crash in test mode Do not decode JPEG into output buffer if it's NULL. Also, I add assert() for `dev->decData` size, which is never checked anywhere. Thanks to Michal Nowak for report and testing. Fixes #128. References: https://gitlab.com/sane-project/backends/issues/128 Tested-by: Michal Nowak commit 3c863c2bd9a2f06b3a92b93bc1031fe03c44c0a0 Author: Louis Lagendijk Date: 2019-11-11 13:22:59 +0100 sane-pixma.man: fixed typos commit 2cffe0e7a6a836091b47d301f3018f42d6678b69 Merge: aeadb8ef1eaf e2e72535b5ca Author: Povilas Kanapickas Date: 2019-11-10 20:36:01 +0000 Merge branch 'genesys-smoke-tests' into 'master' genesys: Add smoke tests See merge request sane-project/backends!241 commit e2e72535b5caa86707fa0ff37e929a45a44a41c5 Author: Povilas Kanapickas Date: 2019-10-26 12:42:50 +0200 genesys: Record data that is important for shading data upload commit 67588e46ef343839795933d22bf6e1299d0cbd6a Author: Povilas Kanapickas Date: 2019-10-26 12:42:49 +0200 genesys: Add checkpoints to record internal state commit eadfdb57a460f7197cf6cc9aef7e50d3e799ebac Author: Povilas Kanapickas Date: 2019-10-26 12:42:48 +0200 genesys: Add a way to record backend internals using test scanner iface commit 95366b1f1b7d640e1a512546b4a622dfe4a5cc13 Author: Povilas Kanapickas Date: 2019-10-26 12:42:47 +0200 genesys: Add a way to record arbitrary data for debugging commit 3ac009abe207873627b709544c63a775982ab7b3 Author: Povilas Kanapickas Date: 2019-10-26 12:42:46 +0200 genesys: Add more debug printers commit 38c32ff7029553aadf0e17689a27fe605fe4635a Author: Povilas Kanapickas Date: 2019-10-26 12:42:45 +0200 genesys: Extract formatting functionality out of debug_dump() commit 105f7fbfaddd762455430bb787b2283d9b13899a Author: Povilas Kanapickas Date: 2019-10-26 12:42:44 +0200 genesys: Add utility to restore stream state commit aeadb8ef1eaf9853d9a40dc2dc0dc692633ef5c8 Merge: fa01471c9287 391d4721273e Author: Povilas Kanapickas Date: 2019-11-10 19:45:16 +0000 Merge branch 'genesys-scanner-interface' into 'master' genesys: Low-level scanner interface See merge request sane-project/backends!240 commit 391d4721273eeeb94b5b111d396c5f085c1c1fde Author: Povilas Kanapickas Date: 2019-10-26 11:38:48 +0200 genesys: Rename record_{test => progress}_message commit e2e3ea094770b888b6cdad8866fae622b403b66b Author: Povilas Kanapickas Date: 2019-10-26 11:38:47 +0200 genesys: Route sleep calls through low level scanner interface commit 521c0428892fd001a4db9e00c5e8c5e836422cdd Author: Povilas Kanapickas Date: 2019-10-26 11:38:46 +0200 genesys: Route low-level status debug messages through scanner interface commit 779725ab55cebb0a9f3c2800f2ce411c91ba4584 Author: Povilas Kanapickas Date: 2019-10-26 11:38:45 +0200 genesys: Extract attach_usb_device() commit 908053751f32faf162d44dd4dc098f94ad1ad091 Author: Povilas Kanapickas Date: 2019-10-26 11:38:44 +0200 genesys: Use generic USB device interface througout the backend commit 978ad5e52bdafbf4c713e005b86c4174efa06b7e Author: Povilas Kanapickas Date: 2019-10-26 11:38:43 +0200 genesys: Add interface for USB devices to allow mocking of them commit 4e9b47de56a8ce97e069ea9cbbf9c7eaa4c672e0 Author: Povilas Kanapickas Date: 2019-10-26 11:38:42 +0200 genesys: Move low level data writing API to scanner interface commit 0fed92b0ff541ccccad04bd8afbe133cd303181c Author: Povilas Kanapickas Date: 2019-10-26 11:38:41 +0200 genesys: Remove unneeded wrapper for scanner interface functions commit 0582b5448c1943ea73ba51f7c2e8a9b19c3f566c Author: Povilas Kanapickas Date: 2019-10-26 11:38:40 +0200 genesys: Move low-level protocol implementation to ScannerInterfaceUsb commit c6e60013eff280689e44f3febde0fce622784a17 Author: Povilas Kanapickas Date: 2019-10-26 11:38:39 +0200 genesys: Add low level scanner test interface commit 0ea8ec115cf69dce5735700dfd4b68726f78f436 Author: Povilas Kanapickas Date: 2019-10-26 11:38:38 +0200 genesys: Add low level interface for interaction with the scanner commit 655c6b14b654585bb65d43bb2bbdf2256a3018c7 Author: Povilas Kanapickas Date: 2019-10-26 11:38:37 +0200 genesys: Extract low level register set functionality to separate class commit 309ee441de2875f7e6d498dcad7c3d78262b1166 Author: Povilas Kanapickas Date: 2019-10-26 11:38:36 +0200 genesys: Remove trivial bulk_{read,write}_data() wrapper commit fa01471c9287663bba02225e2b9c0c300271ec67 Author: m. allan noah Date: 2019-11-10 10:46:50 -0500 fujitsu backend v135 - set has_MS_lamp=0 for fi-72x0, bug #134 commit 8c4879c835256a5d02f15f0fbdabd3b3d73304de Merge: 99d1abe1e7fd 9d9552575d6c Author: Povilas Kanapickas Date: 2019-11-10 14:56:00 +0000 Merge branch 'genesys-misc-cleanup' into 'master' genesys: Miscellaneous cleanup See merge request sane-project/backends!239 commit 9d9552575d6c578cda0a3892403158db33d0e00e Author: Povilas Kanapickas Date: 2019-11-02 17:13:20 +0200 genesys: Extract MethodResolutions::get_resolutions() commit 42a58387b6847ab820dbd80ab1a54597eb04db96 Author: Povilas Kanapickas Date: 2019-11-02 17:13:19 +0200 genesys: Extract conversion of ScanColorMode to separate functions commit 3ad5a9241486b8fa785d37c92892919d1cda8ec0 Author: Povilas Kanapickas Date: 2019-11-02 17:13:18 +0200 genesys: Move conversion of ScanMethod to option strings to enums.cpp commit 6fe1db1c734b9df8afe3c7a5c46203a7c858ece4 Author: Povilas Kanapickas Date: 2019-11-02 17:13:17 +0200 genesys: Use std where needed commit 429d8e4d5b811fa70383ab5d3f8d414733181158 Author: Povilas Kanapickas Date: 2019-11-02 17:13:16 +0200 genesys: Remove unused code commit a7ca16a415e526bae47d9e98eae282408875c001 Author: Povilas Kanapickas Date: 2019-11-02 17:13:15 +0200 genesys: Add file with class forward declarations commit f5410b7738e46689a64e95279ea17b5178356785 Author: Povilas Kanapickas Date: 2019-11-02 17:13:14 +0200 genesys: Add an easy way to override SANE API linkage commit 99d1abe1e7fd210570e3b55503114efe87f6d678 Author: m. allan noah Date: 2019-11-10 09:40:47 -0500 fujitsu backend: add iX1500 IDs - updated per bug #131 commit 5c0838e42383d37256dc775c246d3f476234b95a Merge: 779d09a0b56c 7213738359a7 Author: Povilas Kanapickas Date: 2019-11-10 14:32:41 +0000 Merge branch 'genesys-fix-calibration' into 'master' genesys: Fix calibration issues See merge request sane-project/backends!238 commit 7213738359a7b54018711f2300ae4f2bf128a9a9 Author: Povilas Kanapickas Date: 2019-11-10 10:32:11 +0200 genesys: Make sure correct session data is used for saving calibration commit 68f388eb6a9427d340a21a7aeb6bb6082d054aeb Author: Povilas Kanapickas Date: 2019-11-10 10:32:10 +0200 genesys: Remove is_compatible_calibration() wrappers commit ced1ee03066edcc4513ff8364940e238c20ce930 Author: Povilas Kanapickas Date: 2019-11-10 10:32:09 +0200 genesys: Use common impl of is_compatible_calibration() on gl646 commit 8659536e1b8b820e281a5a8f8fc264fa6bbffb9d Author: Povilas Kanapickas Date: 2019-11-10 10:32:08 +0200 genesys: Implement calculate_scan_session() on gl646 commit 84e4ababa517063514cfc4395fe8d35df787f5f0 Author: Povilas Kanapickas Date: 2019-11-10 10:32:07 +0200 genesys: Correctly serialize GenesysFrontendLayout commit 8557e0c2204e4d1ffae17b05341e3e0a60fb31ca Author: Povilas Kanapickas Date: 2019-11-10 10:32:06 +0200 genesys: Don't modify global state in *_is_compatible_calibration() commit f4f58f6c185123f907e0fd71d067ae009e61a816 Author: Povilas Kanapickas Date: 2019-11-10 10:32:05 +0200 genesys: Remove no longer used Genesys_Current_Setup commit 8c6987e5a1002bbe40a5b52b1bd761e090cd3947 Author: Povilas Kanapickas Date: 2019-11-10 10:32:04 +0200 genesys: Rewrite is_compatible_calculation() to use session data commit 9662a501a9bc5347884644a9aa746c9ee3192678 Author: Povilas Kanapickas Date: 2019-11-10 10:32:03 +0200 genesys: Use data from session directly when possible commit 779d09a0b56c65cfdd776c16b46d4efec0fe256b Author: m. allan noah Date: 2019-11-10 09:01:06 -0500 canon_dr backend v58 - adjust wait_scanner to set runRS only as a last resort, bug #154 commit a15d960228c028154766a0018a4dcffa304da95d Merge: e4a908d88b17 d2aaa822f36c Author: Povilas Kanapickas Date: 2019-11-10 12:12:41 +0000 Merge branch 'genesys-logging-improvements' into 'master' genesys: Miscellaneous logging improvements See merge request sane-project/backends!237 commit d2aaa822f36c0643323eb6abe6f2c7c04591c990 Author: Povilas Kanapickas Date: 2019-10-27 11:48:36 +0200 genesys: Allow use of TIE() macro outside the genesys namespace commit 87b5a78e377b5febc2acb79974e30029586bb974 Author: Povilas Kanapickas Date: 2019-10-27 11:48:35 +0200 genesys: Add more logging commit 1faa3223089cad06d99061cc8c9706079aa08e24 Author: Povilas Kanapickas Date: 2019-10-27 11:48:34 +0200 genesys: Add a way to log messages using DBG_HELPER commit 9ca0a109381ebe7d9a4d737bee8cd3b5020811d3 Author: Povilas Kanapickas Date: 2019-10-27 11:48:33 +0200 genesys: Improve class method identifiers in debug messages commit 6d7365ba897890ec75eaa83c9981393677239fac Author: Povilas Kanapickas Date: 2019-10-27 11:48:32 +0200 genesys: Return errors as exceptions commit 46876064d7ccff723d422a8c42fb2da94f61f3fe Author: Povilas Kanapickas Date: 2019-10-27 11:48:31 +0200 genesys: Improve error logging commit e4a908d88b1736902c30f23caa6a3ace032a71aa Merge: 05f313cc5310 a1bc0d33cd02 Author: Povilas Kanapickas Date: 2019-11-10 12:04:51 +0000 Merge branch 'genesys-session-cleanup' into 'master' genesys: More cleanup for ScanSession See merge request sane-project/backends!236 commit a1bc0d33cd0283f015fd5663f5fe6764b028803e Author: Povilas Kanapickas Date: 2019-10-27 10:42:01 +0200 genesys: Move line-distance correction data to session commit 0fa25a233b4306a9d8f3e78a80f900974179fd6c Author: Povilas Kanapickas Date: 2019-10-27 10:42:00 +0200 genesys: Merge chip-specific compute_session functions commit 05f313cc53106de0a284f157aac646bf4ce9205d Merge: fae6080630ee c2479ed0e938 Author: Povilas Kanapickas Date: 2019-11-10 11:22:41 +0000 Merge branch 'genesys-improve-8400f' into 'master' genesys: Add support for 3200 dpi resolution on 8400F See merge request sane-project/backends!235 commit c2479ed0e938b54b115b84679f81132ab417acd7 Author: Povilas Kanapickas Date: 2019-10-27 10:29:56 +0200 genesys: Add support for 3200dpi flatbed resolution on 8400F commit 7f49bd2a2ef39c09a2a75db7fb7dbc0f2513d3b5 Author: Povilas Kanapickas Date: 2019-10-27 10:29:55 +0200 genesys: Enable all transparency resolutions for infrared scans on 8400F commit 7336ec8969015fb77ad2e8e5a25217af82473335 Author: Povilas Kanapickas Date: 2019-10-27 10:29:54 +0200 genesys: Fix 1600 dpi flatbed / 3200 dpi TA support on 8400F commit fae6080630ee1c03186e5fb1e36e74d7e3778b65 Merge: 71b817cef8e2 44812981295b Author: Povilas Kanapickas Date: 2019-11-09 21:09:17 +0000 Merge branch 'genesys-misc-fixes' into 'master' genesys: Miscellaneous fixes See merge request sane-project/backends!233 commit 44812981295b0300b65aadd9e7958c0c6cd81a6f Author: Povilas Kanapickas Date: 2019-11-09 22:51:42 +0200 genesys: Fix invalid memory access on DSMobile 600 and DocketPort 467 commit c6b85390323fa357573ef0f604494bfcd1c4bfe1 Author: Povilas Kanapickas Date: 2019-11-09 22:51:41 +0200 genesys: Disable support for Canon Image Formula 101 commit 3632d4ec3795ce498be0285991285c92d57a6573 Author: Povilas Kanapickas Date: 2019-11-09 22:51:40 +0200 genesys: Fix out of bounds write during initialization of shading data commit e967d98aabe326bb1d6d043edd55adf8f5b7e060 Author: Povilas Kanapickas Date: 2019-11-09 22:51:39 +0200 genesys: Disable broken support for 4800 dpi on LiDE 210 and 220 commit cb7521e7c11e63abcc4c72ff580987805541b568 Author: Povilas Kanapickas Date: 2019-11-09 22:51:38 +0200 genesys: Remove currently unsupported resolutions on 8600F commit 18206d845d92ea036384cc8b20140cf6012ae541 Author: Povilas Kanapickas Date: 2019-11-09 22:51:37 +0200 genesys: Fix crash on startup on Canon 4400F commit a5a03dfce2dc1e7237180ff17b710de65ad4385b Author: Povilas Kanapickas Date: 2019-11-09 22:51:36 +0200 genesys: Remove incorrect assert on gl841 commit a7be52a5a11aaa602fdd782af11b4e98156fff4d Author: Povilas Kanapickas Date: 2019-11-09 22:51:35 +0200 genesys: Fix crash on gl646 when sensor matches any resolution commit ddb8685755783b59053ae072c68755abfeb6cce3 Author: Povilas Kanapickas Date: 2019-11-09 22:51:34 +0200 genesys: Fix null name of the "extras group" option leading to crashes commit 54edeaf69d187c5301204b8ac40152a4023af8f6 Author: Povilas Kanapickas Date: 2019-11-09 22:51:33 +0200 genesys: Inherit from std::exception publicly commit ecb1d899b936f83638adbf704ef4b8f91435c799 Author: Povilas Kanapickas Date: 2019-11-09 22:51:32 +0200 genesys: Fix typo which prevented calibration data from being used commit cac57c3e9fa1f38ec8e6b886639c08005d69c535 Author: Povilas Kanapickas Date: 2019-11-09 22:51:31 +0200 genesys: Remove duplicate handling of document end This duplicates work done in genesys_fill_read_buffer() commit 71b817cef8e28c3c4a8aa1a7cbc23478e66ce783 Author: Olaf Meeuwissen Date: 2019-11-07 20:24:31 +0900 hpaio.desc: Sync with hplip-3.19.11 Re #166 commit ee3f9b47ece844262f4614ebedfd9f21bebc3f24 Author: Rolf Bensch Date: 2019-11-06 18:08:41 +0100 pixma: backend version 0.25.1 commit eca978c258fb4e6e1f24dd47ff77dbaa36b80230 Author: Louis Lagendijk Date: 2019-11-05 23:03:27 +0100 pixma_bjnp: added options to completely disable networking or skip auto detection. commit 0f493e155aae4b3411fef89f7cdc98eb373c7a7f Author: Louis Lagendijk Date: 2019-11-05 20:09:42 +0100 Fixed pixma_bjnp.c commit a1ecd8c15887c62c052f02952f4f90e32b38f197 Author: Louis Lagendijk Date: 2019-11-05 19:45:30 +0100 Fixed completely messed up pixma_bjnp.c commit bda9ae39bad41b32992a84ddec8fddc24ce7ee74 Author: Louis Lagendijk Date: 2019-11-05 19:22:11 +0100 pixma_bjnp.c sane-pixma.doc Fix trailing spaces commit 462df7d6ad72b1c35e155987ec9f173f45a1ee91 Author: Louis Lagendijk Date: 2019-11-05 18:31:39 +0100 usane-pixma: - Document that Canon seems to be remving the supported MFNP and BJNP protocols in new printers/scanners - Added an mfnp example in the pixma.conf file commit 739cfc8d83f6b1903c6694a7e5d24e081dd12a18 Author: Louis Lagendijk Date: 2019-11-05 17:16:09 +0100 pixma_bjnp.c: redsign of timeout code commit 5dd50be523375c888ac0c16a32bb046c256e43ca Author: Louis Lagendijk Date: 2019-11-05 14:35:11 +0100 xma_bjnp.c: fixed parsing of model in parse_IEEE1284_to_model commit 766b4937c3bad766c422951b07125e424bdf5efb Author: Louis Lagendijk Date: 2019-11-05 13:56:23 +0100 pixma_bjnp.c Retry TCP connect in case the scanner is not yet ready commit c9a2b3bd23cbe6496aca2f0d977c60341cb4c056 Author: Olaf Meeuwissen Date: 2019-11-04 11:17:44 +0900 utsushi.desc: Sync with upstream commit 572c5e6e6100da229dfcc6aef0a10d5551703298 Author: Rolf Bensch Date: 2019-11-01 19:53:41 +0100 pixma: backend version 0.25.0 commit 27cac478e72ca6dbdd035f33d2eefb4574d284c5 Author: Rolf Bensch Date: 2019-11-01 19:52:57 +0100 pixma: CanoScan LiDE 300 & 400 support all scan resolutions commit 053e5fd7526b08795e9a4e20bc9b03d887e532db Merge: 69e40e9a62a4 e16a2b05b1a6 Author: Rolf Bensch Date: 2019-11-01 19:30:53 +0000 Merge branch 'pixma/lide400_low_dpi' into 'master' CanoScan LiDE400 scans below 300dpi See merge request sane-project/backends!231 commit e16a2b05b1a6584b3ef9fd0506f0645accb70a2d Author: Rolf Bensch Date: 2019-11-01 19:46:16 +0100 pixma_mp150: activate image down-scaling for LiDE300 & 400 we don't need to force >=300 dpi scans anymore commit cffeb11ee63905ae44fb1f4454081674cd21cbb3 Author: Rolf Bensch Date: 2019-11-01 19:44:27 +0100 pixma_mp150: special image format handling only needed for existing sub-images commit 95e208527a1eadc5c4e11e2c083990fe1cde11ea Author: Rolf Bensch Date: 2019-11-01 19:42:16 +0100 pixma_mp150: new function shrink_image() call shrink_image() from post processing image data commit 452b28a81fba249d4afd2071ad7e79b849c37e17 Author: Rolf Bensch Date: 2019-11-01 19:21:50 +0100 pixma_mp150: use mp->scale to calculate scan parameters for down-scaling images the values for xdpi, ydpi, x, xs, y and wx must be expanded to minimum image size commit b4cf3343bd0b253ad5df00772983e390336fcfda Author: Rolf Bensch Date: 2019-11-01 19:16:14 +0100 pixma_mp150: add comments and some debug outputs commit 5d64760f0f61092c2f18b04414bd41a75d163e63 Author: Rolf Bensch Date: 2019-11-01 19:06:53 +0100 pixma_mp150: new sub-driver parameter scale commit bd811cb0b0b7d4e41910193608a8a7c3b02c178c Author: Rolf Bensch Date: 2019-11-01 19:02:01 +0100 pixma: new device option: min_xdpi used for scaling scanned images commit 69e40e9a62a4157165c9865dc3653f293ccaeaab Merge: e97112fa9649 1bd2dcc9c81c Author: Olaf Meeuwissen Date: 2019-10-31 12:03:33 +0000 Merge branch 'ci-fedora-31' into 'master' CI: Bump Fedora from 30 to 31 See merge request sane-project/backends!230 commit 1bd2dcc9c81c9d930cf943a4024deb3716d18817 Author: Olaf Meeuwissen Date: 2019-10-31 18:23:15 +0900 CI: Bump Fedora from 30 to 31 commit e97112fa9649a4a7f9f5fda57ca9eac94d564447 Merge: fe30fbe893a9 ffcc232b6cc2 Author: Olaf Meeuwissen Date: 2019-10-29 11:28:57 +0000 Merge branch 'patch-1' into 'master' Added the list of devices supported by scangearmp2. See merge request sane-project/backends!227 commit ffcc232b6cc2ec5e4fd4079742ece7558cd8c48b Author: Ordissimo Date: 2019-10-29 11:28:57 +0000 Add list of devices supported by scangearmp2 commit fe30fbe893a9842470768bfbc3fd204bacb74e97 Merge: 99ae5490a677 82e971a7862a Author: Povilas Kanapickas Date: 2019-10-25 20:22:38 +0000 Merge branch 'glibcxx-assertions-abort' into 'master' genesys: Fix #156 - [Fedora] Abort due out-of-bound access Closes #156 See merge request sane-project/backends!224 commit 82e971a7862a9c87c3f49e62e39a34da67d0b069 Author: Zdenek Dohnal Date: 2019-10-25 13:06:15 +0200 Fix #156 - [Fedora] Abort due out-of-bound access commit 99ae5490a677c3210e23c770b2032dc235bcd8b7 Author: Rolf Bensch Date: 2019-10-24 15:10:23 +0200 pixma: backend version 0.24.1 commit 7b388d14da4ef9cf76cd987c575494fe6256fdc5 Author: Rolf Bensch Date: 2019-10-24 15:04:09 +0200 pixma_mp150: fix scan height calculation this fixes: pixma_mp150: remove (ccd) color shifting (fc300be) commit 63ce5153760cddfc6b15a1f2fc20bfa69d80fc82 Author: Rolf Bensch Date: 2019-10-22 20:09:00 +0200 pixma_mp800: remove double PIXMA_CAP_CCD settings commit 2f2bce2ae9f259646512ce7b1be3791001e08245 Author: Rolf Bensch Date: 2019-10-22 19:41:49 +0200 pixma: backend version 0.24.0 commit 7db4449fa044e8ee06b7c7f83a54a7725f861da4 Merge: 2b1ce918aef1 ab0314399a26 Author: Rolf Bensch Date: 2019-10-22 19:40:06 +0200 Merge branch 'pixma/separate_ccd_from_cis' commit ab0314399a26b470d7f601a69649f7ce7975d1c0 Author: Rolf Bensch Date: 2019-10-22 19:32:07 +0200 pixma_mp810: rename => pixma_mp800 1st ccd scanner in pixma_mp810 is MP800 commit fc300be715eeee3b719824b694a660672de99d78 Author: Rolf Bensch Date: 2019-10-22 19:23:18 +0200 pixma_mp150: remove (ccd) color shifting commit 1b8fe029a256d6ef4202838a1c69fc2e9307daa7 Author: Rolf Bensch Date: 2019-10-22 19:10:53 +0200 pixma_mp150: remove unused code unused scanner functions: ccd, tpu commit 83a0a1c55f973db2d4fb7ce9d664477e46516651 Author: Rolf Bensch Date: 2019-10-22 18:03:33 +0200 pixma_mp150: move ccd scanners => pixma_mp810 commit 657f1d8e26f0259f380c11aa16cdcfc00249648a Author: Rolf Bensch Date: 2019-10-22 17:46:19 +0200 pixma_mp810: all scanners have ccd commit 97cd2e10bb6149cbdaab8fc306ea2761aa83f972 Author: Rolf Bensch Date: 2019-10-22 17:46:01 +0200 pixma_mp750: all scanners have ccd commit 2b1ce918aef19061bf5f0cc3cba625314bb6f1c3 Author: Louis Lagendijk Date: 2019-10-18 19:45:37 +0200 pixma_bjnp.c: Fixed compile warnings on My Fedora build commit 6b0f3ca124fc3e31db1d4750f9e6e006f135976d Merge: 9ee689c6f67e 0b4a5f941ad2 Author: Povilas Kanapickas Date: 2019-10-17 19:44:38 +0000 Merge branch 'genesys-misc-fixes' into 'master' genesys: Miscellaneous fixes for numerous scanners See merge request sane-project/backends!222 commit 0b4a5f941ad2550e90d06ff1c6ba7f7e909312d5 Author: Povilas Kanapickas Date: 2019-10-17 22:30:27 +0300 genesys: Use correct min resolution for feeding commit abec99006c78353a300747e34ae39f9be7d5673d Author: Povilas Kanapickas Date: 2019-10-17 22:30:26 +0300 genesys: Add missing includes commit 2d0a1a34709670d2e7946e8f0695af4b73b75847 Author: Povilas Kanapickas Date: 2019-10-17 22:30:25 +0300 genesys: Enable 1200 Y resolution on HP ScanJet 2300 commit 2a8be5e191550dadc44ca8eaf9fafe45171bb76d Author: Povilas Kanapickas Date: 2019-10-17 22:30:24 +0300 genesys: Use motor base dpi for shading calibration on gl847 commit 87969174bd9d68983f6b4b47aae51f813e64054e Author: Povilas Kanapickas Date: 2019-10-17 22:30:23 +0300 genesys: Fix shading target for Canon LiDE 110, 120, 210, 220 commit 36f0685d0a5db1c2b361ad250b8235b5e07aaafd Author: Povilas Kanapickas Date: 2019-10-17 22:30:22 +0300 genesys: Improve robustness of calibration on gl847 commit 67d6a194e588fffdea15e5f3c0e2279de519f5f8 Author: Povilas Kanapickas Date: 2019-10-17 22:30:21 +0300 genesys: Fix geometry for Canon LiDE 110 commit 0096d56cb61dbeb7426c6699cea4c0bf0eb7f2de Author: Povilas Kanapickas Date: 2019-10-17 22:30:20 +0300 genesys: Fix X resolution list for Canon LiDE 100 and 120 commit dab38144a03cd1da055cc9ab06defb6d0a79872e Author: Povilas Kanapickas Date: 2019-10-17 22:30:19 +0300 genesys: Fix geometry of HP G4050 commit 661fa1d590c5ab7ef1d8f85513c55aa63d3c78a5 Author: Povilas Kanapickas Date: 2019-10-17 22:30:18 +0300 genesys: Add missing resolutions for Canon 5600F commit 2c7f7fcac283c1f3e073908601f5da0444fc3819 Author: Povilas Kanapickas Date: 2019-10-17 22:30:17 +0300 genesys: Fix sensor gamma of LiDE 100, 110, 120, 200, 210, 220 scanners commit b391b35fd9180af4222ed0c9ff71e7aa62baca4b Author: Povilas Kanapickas Date: 2019-10-17 22:30:16 +0300 genesys: Fix uninitialized variable warning in SaneException c-tor commit 1ac04c6266dd1bf721b9b950ebe3540112af0beb Author: Povilas Kanapickas Date: 2019-10-17 22:30:15 +0300 genesys: Don't enable gamma when not needed commit 9ee689c6f67e48b3e94a2ff0d129c28476ff855b Merge: 680ebd9f3f65 93e980d0c1d3 Author: Povilas Kanapickas Date: 2019-10-17 19:20:11 +0000 Merge branch 'genesys-median-shading' into 'master' genesys: Use median filter during to average columns during shading calculations See merge request sane-project/backends!221 commit 93e980d0c1d3b03bea1d2db45ea588f2272c94ab Author: Povilas Kanapickas Date: 2019-10-17 22:07:01 +0300 genesys: Use median instead of mean when computing shading calib data commit f9d36d1a1b5150866d2a714380c7e7579e4e1189 Author: Povilas Kanapickas Date: 2019-10-17 22:07:00 +0300 genesys: Add utility to compute percentille of columns in image commit 051ff59e0c21e512823093a125e1a1da4a70a79a Author: Povilas Kanapickas Date: 2019-10-17 22:06:59 +0300 testsuite: Add a way to test exception throwing commit 680ebd9f3f6576686a25e721138f01c5ae8d9c72 Merge: efb693e0b7fa bc2f40b274ce Author: ABC Date: 2019-10-16 15:48:10 +0000 Merge branch 'samsungc460-no-jpeg-support' into 'master' xerox_mfp: Samsung C460 does not have the JPEG mode So blacklist it from jpeg parser. See merge request sane-project/backends!218 https://gitlab.com/sane-project/backends/merge_requests/218 Downstream references: https://bugzilla.redhat.com/show_bug.cgi?id=1760916 commit bc2f40b274ceb9292c3665ea55755e43d0b99487 Author: Zdenek Dohnal Date: 2019-10-15 12:49:48 +0200 Samsung C460 does not have the JPEG mode commit efb693e0b7fa1ad020bf7e0c0d1cf06b7a9dd61e Merge: 82b1a8922ef9 885eb8280120 Author: Povilas Kanapickas Date: 2019-10-13 15:49:07 +0000 Merge branch 'genesys-per-scan-method-resolutions' into 'master' genesys: Support different resolutions depending on scan method See merge request sane-project/backends!217 commit 885eb8280120fc9e41c151d15ee01973125df515 Author: Povilas Kanapickas Date: 2019-10-13 18:30:28 +0300 genesys: Update resolutions specific to scan method in model list commit a5fe12a37fbc200475a03c7644ec429e213107da Author: Povilas Kanapickas Date: 2019-10-13 18:30:27 +0300 genesys: Allow supported resolutions to vary depending on scan method commit 24230aea7d1865b0a1fac649f53c7fdcac0aee51 Author: Povilas Kanapickas Date: 2019-10-13 18:30:26 +0300 genesys: Simplify option setup commit b716c1a45760a79ea8a31d4296270d0605f669b7 Author: Povilas Kanapickas Date: 2019-10-13 18:30:25 +0300 genesys: Remove duplicate definitions of SCAN_FLAG_* commit 82b1a8922ef981ae03126d492389bdc897fc7243 Merge: f04ab61eaab8 3037ba1eafb5 Author: Povilas Kanapickas Date: 2019-10-13 10:41:13 +0000 Merge branch 'genesys-searchable-register-names' into 'master' genesys: Use a searchable name for register enums See merge request sane-project/backends!216 commit 3037ba1eafb551524b30865323900bc5afa1dd6a Author: Povilas Kanapickas Date: 2019-10-02 14:28:59 +0300 genesys: Use a searchable name for register enums commit f04ab61eaab8335866cce7c48796c080b2729db2 Merge: 9fe2026accee 0ab82e9f86d4 Author: Povilas Kanapickas Date: 2019-10-13 10:15:12 +0000 Merge branch 'genesys-fix-warnings' into 'master' genesys: Fix floating-point conversion warnings See merge request sane-project/backends!215 commit 0ab82e9f86d4b1d94e6e1eb1e501498570c89cb2 Author: Povilas Kanapickas Date: 2019-10-13 06:18:51 +0300 genesys: Resolve various floating-point conversion warnings commit f4a295754159acb6d4df0868f02b69531667cd92 Author: Povilas Kanapickas Date: 2019-10-13 06:18:50 +0300 genesys: Use unsigned for resolution in various places commit 59e356ac1340c930bd8a2f2ffc79ac36cd3d837d Author: Povilas Kanapickas Date: 2019-10-13 06:18:49 +0300 genesys: Don't use variants of M_PI that aren't guaranteed to be present commit a694002364761f179db8d0694b46afe5a3509750 Author: Povilas Kanapickas Date: 2019-10-13 06:18:48 +0300 genesys: Switch session startx and starty to unsigned The sources and destinations are integer in most cases for these variables. commit 0840c6e9d9e1a878b625ba6547958e5bc41645d8 Author: Povilas Kanapickas Date: 2019-10-13 06:18:47 +0300 genesys: Fix float conversion warnings in the tables commit 9fe2026accee3bdfcfd81fbb158934d2a2f5d37c Merge: 41ce1eb20413 68e5f8a91206 Author: Povilas Kanapickas Date: 2019-10-12 20:06:18 +0000 Merge branch 'genesys-misc-cleanup' into 'master' genesys: Miscellaneous cleanup See merge request sane-project/backends!214 commit 68e5f8a912067731af262c7218d9eaf88e032721 Author: Povilas Kanapickas Date: 2019-10-02 12:01:28 +0300 genesys: Put the backend code into a separate namespace commit e32a45fef814c36756bedb259c2baa692da25c39 Author: Povilas Kanapickas Date: 2019-10-02 12:01:27 +0300 genesys: Retrieve depth from session in genesys_warmup_lamp() commit 17740df924c692d8585bc24258a357cc5f3d17b2 Author: Povilas Kanapickas Date: 2019-10-02 12:01:26 +0300 genesys: Remove support for binary logging commit 41ce1eb204130a974e15c63cec418b6197cf41c4 Author: Rolf Bensch Date: 2019-10-12 18:49:10 +0200 pixma: backend version 0.23.5 commit a89ffe8f2c02d45cbd7a26608c02535b972d1dfa Merge: d5c0240f43a9 eb2f8364818d Author: Povilas Kanapickas Date: 2019-10-12 16:09:29 +0000 Merge branch 'genesys-remove-1bit-support' into 'master' genesys: Remove support for scanning 1 bit output See merge request sane-project/backends!212 commit eb2f8364818d5fbd30e06e7a2b296699a1ad5af8 Author: Povilas Kanapickas Date: 2019-10-01 18:11:52 +0300 genesys: Remove low-level support for 1-bit depth scans commit 0180c4c5d871c55ecd38c8ad9891c4a51dc70c1a Author: Povilas Kanapickas Date: 2019-10-01 18:11:51 +0300 genesys: Remove SCAN_FLAG_DYNAMIC_LINEART commit 8be96ecd6072a9199ab2cb6f105b79efab97b9e2 Author: Povilas Kanapickas Date: 2019-10-01 18:11:50 +0300 genesys: Simplify code as dynamic_lineart is always enabled for lineart commit 3263b986ce7afc802c96276d4b876632bcdf3146 Author: Povilas Kanapickas Date: 2019-10-01 18:11:49 +0300 genesys: Remove support of --disable-dynamic-lineart option commit bff9f2fdc4a31f1f498223144814168f64aaf802 Author: Povilas Kanapickas Date: 2019-10-01 18:11:48 +0300 genesys: Fix crash during sane_exit() on high debug level commit 158cc81e206a03f4c25a66b2e3147c1c4c31abf8 Author: Povilas Kanapickas Date: 2019-10-01 18:11:47 +0300 genesys: Simplify setup of session depth parameter commit d5c0240f43a9186f3149fc57b8c2ebcd30725208 Merge: f6c9b83dcd7b 6638f00b8496 Author: Rolf Bensch Date: 2019-10-12 18:07:03 +0200 Merge remote-tracking branch 'origin/master' commit 6638f00b8496977d02ceda8f90208e029a6616f2 Merge: 9581f29f135d 09336e0286ce Author: Povilas Kanapickas Date: 2019-10-12 16:05:18 +0000 Merge branch 'genesys-fix-warnings' into 'master' genesys: Fix warnings See merge request sane-project/backends!211 commit 09336e0286ce828c5288ac361ae3bd83e671cffb Author: Povilas Kanapickas Date: 2019-10-01 15:41:27 +0300 genesys: Remove code that has no effect commit cccd1e949ab53f6463bb8f97431dde9a0bc5e632 Author: Povilas Kanapickas Date: 2019-10-01 15:41:26 +0300 genesys: Fix unused exception warning commit 60b6465115eb391b93e84a237cabdfa7e88e2cb4 Author: Povilas Kanapickas Date: 2019-10-01 15:41:25 +0300 genesys: Emit single vtable of ImagePipelineNodeCallableSource commit c8f2a815c03478bd61b16ad35f34cab7edf7ed30 Author: Povilas Kanapickas Date: 2019-10-01 15:41:24 +0300 genesys: Don't use std types from global namespace commit 1423db661858e566dbf17430c82e5471a69c0b2a Author: Povilas Kanapickas Date: 2019-10-01 15:41:23 +0300 genesys: Include missing includes commit 9581f29f135db2e4c23056d37997fe35416a3d5f Merge: 5ca0acd19a2a 42cace02c52e Author: Povilas Kanapickas Date: 2019-10-12 15:43:04 +0000 Merge branch 'genesys-fix-warnings' into 'master' genesys: Fix warnings not enabled in regular build See merge request sane-project/backends!209 commit 42cace02c52e19a9520dcafaf56dfeb442eb7c50 Author: Povilas Kanapickas Date: 2019-10-01 12:09:51 +0300 genesys: Simplify version logging commit 4c90bfcaeea4c470c96cf7bc6c4b0f277742ec17 Author: Povilas Kanapickas Date: 2019-10-01 12:09:50 +0300 genesys: Remove uses of old-style casts where possible commit 8537ba21b7fa9dfca25068735d11916b1b8869e2 Author: Povilas Kanapickas Date: 2019-10-01 12:09:49 +0300 genesys: Don't emit symbols of internal objects commit 9c7199c09ec88832b285add6178272e158f6101d Author: Povilas Kanapickas Date: 2019-10-01 12:09:48 +0300 genesys: Don't use std identifiers from global scope commit bb84d6d9e4b7c6de55cb432892af26ed89750354 Author: Povilas Kanapickas Date: 2019-10-01 12:09:47 +0300 genesys: Remove unused code commit e20e73f43465e79336a30792c16adb0dffc6084b Author: Povilas Kanapickas Date: 2019-10-01 12:09:46 +0300 genesys: Don't use C-style casts where possible commit 5ca0acd19a2a8958c4de78eb0ccffe520ca7d1b1 Merge: ebfb939647c3 f8623e8f5380 Author: Povilas Kanapickas Date: 2019-10-12 15:41:59 +0000 Merge branch 'genesys-plustek-7300-7500i' into 'master' genesys: Implement support for Plustek OpticFilm 7300 and 7500i scanners See merge request sane-project/backends!210 commit f8623e8f538063ba209b8b5fd235e3a5f9f866c3 Author: Povilas Kanapickas Date: 2019-10-12 18:23:56 +0300 genesys: Add support for Plustek OpticFilm 7500i commit 20da3df5014abfc6b380a65853d5b522f02ded7e Author: Povilas Kanapickas Date: 2019-10-12 18:23:55 +0300 genesys: Add support for Plustek Opticfilm 7300 commit ebfb939647c301bf2b4d9af87321c3e57596fa7d Merge: 6959c2d14e7f d3d86a457ed7 Author: Povilas Kanapickas Date: 2019-10-12 15:39:20 +0000 Merge branch 'genesys-move-to-dir' into 'master' genesys: Move the backend to a separate directory See merge request sane-project/backends!207 commit d3d86a457ed71884eb42efd2003599ea9beceec8 Author: Povilas Kanapickas Date: 2019-10-12 17:43:32 +0300 po: Sync with latest source commit b5e4d86eb2699ef61c79e81835db0d0598267735 Author: Povilas Kanapickas Date: 2019-10-01 08:42:09 +0300 genesys: Use separate translation unit for conversion functions commit 9f44bab6e094527a323d4c4bf057fd65d399d39b Author: Povilas Kanapickas Date: 2019-10-01 08:42:08 +0300 genesys: Move backend files to a separate directory commit f6c9b83dcd7b6d9afcfcf337ec30d2ba5d8966e9 Merge: 6959c2d14e7f ba9bc24fd204 Author: Rolf Bensch Date: 2019-10-12 18:00:51 +0200 Merge branch 'pixma/fix-debian10-compiler-warning_format-truncation' See issue sane-project/backends#120 commit ba9bc24fd2040351f5d00b9a6d4624bab57b0d4c Author: Rolf Bensch Date: 2019-10-12 17:59:13 +0200 pixma: replace snprintf() by strftime() strftime() uses format specifiers with fixed lengths. commit 6959c2d14e7fff2c61bee5972304782dc84e07ea Merge: 9ce4cb089f1a 497c9aeba2f4 Author: Povilas Kanapickas Date: 2019-10-12 15:06:44 +0000 Merge branch 'genesys-misc-cleanup' into 'master' genesys: Miscellaneous cleanup See merge request sane-project/backends!208 commit 497c9aeba2f4dc2a7a472b643b884fd4a7b3753c Author: Povilas Kanapickas Date: 2019-10-01 10:52:44 +0300 genesys: Wrap SANE_Fixed into class which automatically converts values commit b176366dcf375499868eb54057e5f4c8cbbf4924 Author: Povilas Kanapickas Date: 2019-10-01 10:52:43 +0300 genesys: Increase wait for home timeout to 200 seconds commit 8a1bc90521e205f7747cf2f12e0849ca7c0772aa Author: Povilas Kanapickas Date: 2019-10-01 10:52:42 +0300 genesys: Simplify sanei_genesys_wait_for_home() commit 9ce4cb089f1ad10e91a5ce5d9bd222a06f764c41 Merge: 9ccf586575ed 61e1f1bf08f6 Author: Povilas Kanapickas Date: 2019-10-12 14:13:44 +0000 Merge branch 'genesys-misc-cleanup' into 'master' genesys: Miscellaneous cleanup See merge request sane-project/backends!206 commit 61e1f1bf08f6483608f876ffb085eb6c2d2a7317 Author: Povilas Kanapickas Date: 2019-10-01 08:18:47 +0300 genesys: Simplify usage of sanei_genesys_get_status() commit 26e418537ff694e1b92074d7032fa93aeaa6dc72 Author: Povilas Kanapickas Date: 2019-10-01 08:18:46 +0300 genesys: Reduce duplication of waiting for non-empty buffer state commit bf9d69d20df3c0b80a683cada2c23999699e6d5d Author: Povilas Kanapickas Date: 2019-10-01 08:18:45 +0300 genesys: Replace uses of SANE_Bool with bool commit 6da4e69d95f47db0afe8f020d4fc0dd926281069 Author: Povilas Kanapickas Date: 2019-10-01 08:18:44 +0300 genesys: Mark overridden functions consistently commit 44ac90d309e68d447026e375b02996fa54800219 Author: Povilas Kanapickas Date: 2019-10-01 08:18:43 +0300 genesys: Fix misuses of comma operator commit 7ac53bce10264644fe70b55af2a5fd13d4b16f82 Author: Povilas Kanapickas Date: 2019-10-01 08:18:42 +0300 genesys: Replace uses of nullptr commit 9ccf586575ed274fa9a11084dd831709e8b1fd08 Merge: 0e4a2d107a95 0781f3fd3960 Author: Povilas Kanapickas Date: 2019-10-12 00:13:59 +0000 Merge branch 'genesys-fix-gl841-init-crash' into 'master' genesys: Make sure calib_reg are available before writing into them Closes #136 See merge request sane-project/backends!205 commit 0781f3fd3960ae30d1b1f8ecfd842b82bc7df2bb Author: Povilas Kanapickas Date: 2019-10-12 02:59:11 +0300 genesys: Make sure calib_reg are available before writing into them commit 0e4a2d107a957e7476cee994c3373cdc7e1d7354 Merge: 6933315e16a0 fc699d2bf573 Author: Olaf Meeuwissen Date: 2019-10-06 02:47:36 +0000 Merge branch 'hh' into 'master' uClibc-ng fixes See merge request sane-project/backends!198 commit fc699d2bf573444a55ada47ad2317dc47b67c6d7 Author: Rosen Penev Date: 2019-10-02 18:41:07 -0700 treewide: Replace bzero with memset bzero was removed in POSIX 2008. It is optionally unavailable with uClibc-ng. Signed-off-by: Rosen Penev commit 428bec49bfda8bf6502b97bb02e1eb292aa6e4e6 Author: Rosen Penev Date: 2019-10-02 18:27:36 -0700 sanei_scsi: Replace bcopy with memcpy bcopy was deprecated in POSIX 2008 and is optionally unavailable with uClibc-ng. Signed-off-by: Rosen Penev commit 5c0f0eb12896b26553de88401605391aefc84d02 Author: Rosen Penev Date: 2019-10-02 18:25:02 -0700 pieusb_buffer: Define L_tmpnam if not available uClibc-ng does not make it available for some reason. Signed-off-by: Rosen Penev commit c320a72e32eb6651bd6f1918b83a7a44ca6fcc87 Author: Rosen Penev Date: 2019-10-02 18:21:59 -0700 treewide: Replace mktemp by mkstemp mktemp has been removed in POSIX 2008. uClibc-ng optionally does not make it available. Signed-off-by: Rosen Penev commit 6933315e16a04ba2a5170aeccc29e5de694d8889 Merge: f8131fd6fe36 eb7d173916b6 Author: Rolf Bensch Date: 2019-10-05 21:29:57 +0200 Merge remote-tracking branch 'origin/master' commit eb7d173916b6dc56139d10f56f243eff75a8dbe1 Merge: 9e8f7805742d ec8dadc0a065 Author: Povilas Kanapickas Date: 2019-10-05 08:14:36 +0000 Merge branch 'genesys-use-consistent-enum-names' into 'master' genesys: Use consistent enum names See merge request sane-project/backends!204 commit ec8dadc0a065e038821ba27cd079e5eb504fd6a0 Author: Povilas Kanapickas Date: 2019-09-30 13:52:07 +0300 genesys: Use consistent names for enums of Canon scanners commit 6a1055143f32bd0bef1cef3ae91594f496894bf1 Author: Povilas Kanapickas Date: 2019-09-30 13:52:06 +0300 genesys: Use consistent names for enums of Plustek scanners commit 9e8f7805742db9318125ce77838cb61109a8632b Merge: 290d19a0974f a42a177a7ad5 Author: Povilas Kanapickas Date: 2019-10-05 07:06:51 +0000 Merge branch 'genesys-strongly-typed-enums' into 'master' genesys: Convert weakly typed enums to strongly typed enums See merge request sane-project/backends!203 commit a42a177a7ad54eb26e079b80f61a9bc7739212c0 Author: Povilas Kanapickas Date: 2019-09-30 13:52:05 +0300 genesys: Bump calibration version commit 98b4742aab09eaa58c232b434fdd3f46a3c02b35 Author: Povilas Kanapickas Date: 2019-09-30 13:52:04 +0300 genesys: Use strong enum for motor step type commit 719a0cf55ce7ad84d2fc561fb9859c823113b783 Author: Povilas Kanapickas Date: 2019-09-30 13:52:03 +0300 genesys: Use strong enum for motor id commit 45aec261047cdb7a68e85bba93ced441cfb434ce Author: Povilas Kanapickas Date: 2019-09-30 13:52:02 +0300 genesys: Use strong enum for gpio type commit d4ca0177a13cf21fce320a876ddcfa66c2490acb Author: Povilas Kanapickas Date: 2019-09-30 13:52:01 +0300 genesys: Use strong enum for ADC id commit df14234297f969ccab005227760bfb2bedc168ec Author: Povilas Kanapickas Date: 2019-09-30 13:52:00 +0300 genesys: Use strong enum for sensor id commit 66937f646827fe558a96828980552f94fe6edde9 Author: Povilas Kanapickas Date: 2019-09-30 13:51:59 +0300 genesys: Remove invalid comment commit 6971301590e5cbd7c8932ea9b2246c0df8253c33 Author: Povilas Kanapickas Date: 2019-09-30 13:51:58 +0300 genesys: Rename Genesys_Sensor::{CCD -> ccd}_start_xoffset commit 591309a29e4667ea9e5bb27b6535b0400e8738da Author: Povilas Kanapickas Date: 2019-09-30 13:51:57 +0300 genesys: Use strongly typed enum for model id commit 290d19a0974f68a5037fb6a17e44af8f95fe9793 Merge: 7a7009a79d86 7fb6582157ae Author: Povilas Kanapickas Date: 2019-10-05 06:27:44 +0000 Merge branch 'genesys-model-flag-inverted-output' into 'master' genesys: Introduce a model flag for inverted 16-bit output See merge request sane-project/backends!202 commit 7fb6582157ae2b55f38936e58b1ed8bf174b6055 Author: Povilas Kanapickas Date: 2019-10-05 09:01:32 +0300 genesys: Introduce a model flag for inverted 16-bit output commit 7a7009a79d86b772de70113b305ce9595537fe07 Merge: 303cc7dedd3d 32dea7695672 Author: Povilas Kanapickas Date: 2019-10-05 06:21:12 +0000 Merge branch 'genesys-virtual-functions-for-cmdset' into 'master' genesys: Use virtual functions for command set actions See merge request sane-project/backends!201 commit 32dea7695672d89245dff98448b0e697e83086f9 Author: Povilas Kanapickas Date: 2019-10-05 09:01:31 +0300 genesys: Use virtual functions for command set actions commit 303cc7dedd3d1fb8b4cd64b53c1a40065f931835 Merge: 65d9497ff30d 1face9299d86 Author: Povilas Kanapickas Date: 2019-10-05 05:30:18 +0000 Merge branch 'fix-memory-errors' into 'master' Fix memory errors in various places See merge request sane-project/backends!200 commit 1face9299d866996e0d30d8692e7345c2388b254 Author: Povilas Kanapickas Date: 2019-10-05 01:01:32 +0300 genesys: Fix uninitialized option group names commit 6b3ba8af8a62545c90051b0dbf30b955d2462841 Author: Povilas Kanapickas Date: 2019-10-05 01:01:31 +0300 sanei_usb: Fix memory leak in USB testing mode commit 643ac8eab272cf1fe528bf79a94c873227d84614 Author: Povilas Kanapickas Date: 2019-10-05 01:01:30 +0300 genesys: Fix memory leaks in global data commit f8131fd6fe3652ce8930275898578ba56fc3ddc6 Author: Rolf Bensch Date: 2019-10-05 21:29:14 +0200 pixma: i-SENSYS MF640 Series networking tested commit 65d9497ff30dcc4748a9a5940d1c73b2a8a47c5c Author: Rolf Bensch Date: 2019-10-04 21:29:13 +0200 pixma: fix network scanner model for Canon i-SENSYS MF640 Series commit 67c732f2f03fa6528099990065a073c02d404f27 Author: Rolf Bensch Date: 2019-10-04 15:46:30 +0200 pixma: PIXMA MG3000 Series is working commit 8841b4e593141f70532379ec83c64c62855b7cce Author: Rolf Bensch Date: 2019-10-04 12:32:56 +0200 pixma: i-SENSYS MF640 Series is working commit d78dc1a30be2fb8ac740f9f19a348ae134f5627d Merge: 4239428c87d0 3c449a75154e Author: Rolf Bensch Date: 2019-10-04 11:22:09 +0200 Merge remote-tracking branch 'origin/master' commit 3c449a75154e9f7cdd2c599b2c2c81ec8300c1fe Merge: 899d6968fada 0146888d79e7 Author: Povilas Kanapickas Date: 2019-10-04 04:16:07 +0000 Merge branch 'genesys-plustek-opticfilm-7200i' into 'master' genesys: Implement infrared scanning support on OpticFilm 7200i See merge request sane-project/backends!199 commit 0146888d79e7abfb81d977df1dd6a75e65bb0e11 Author: Povilas Kanapickas Date: 2019-09-19 01:28:17 +0300 genesys: Implement infrared scanning support on OpticFilm 7200i commit 4239428c87d021825f554ca5a9c376df5a03136c Author: Rolf Bensch Date: 2019-10-04 11:21:43 +0200 pixma: backend version 0.23.4 commit 14851d780d9c4743e8d5df55ebcc668a603b1a22 Author: Rolf Bensch Date: 2019-10-04 11:21:14 +0200 pixma: CanoScan LiDE 300 supports max. 2400 dpi commit 899d6968fada5caabde4f4c81a701354f6fbe666 Author: Rolf Bensch Date: 2019-10-01 22:58:54 +0200 pixma: backend version 0.23.3 commit af09cf3ac8e33ca17875d6f3f6b5f05cf8c8e9f5 Author: Rolf Bensch Date: 2019-10-01 22:57:18 +0200 pixma: add document feeder support for i-SENSYS MF260 Series commit cc6afc02ea9095204f364ad5231dcb922824dcfb Author: Rolf Bensch Date: 2019-10-01 22:41:19 +0200 pixma: uncomment and edit experimental scanners list in sane-pixma.man We have no experimental scanners yet. But environment variable PIXMA_EXPERIMENT is specified and may be used again in future. commit 607eacaa7104c62cd6082c02f4fcf83c4523ddd9 Author: Rolf Bensch Date: 2019-10-01 22:37:15 +0200 pixma: reorder scanners in pixma.desc commit 9824dfe8b027de2aa8d09ad11eb05e5a3f14922f Merge: 1dc1da59428e ef29ed60c2a5 Author: Povilas Kanapickas Date: 2019-09-29 22:15:02 +0000 Merge branch 'genesys-opticfilm-7200i' into 'master' genesys: Implement support for Plustek Opticfilm 7200i See merge request sane-project/backends!197 commit ef29ed60c2a5928d6be916fc7e85efc692cb8a2f Author: Povilas Kanapickas Date: 2019-09-19 00:30:23 +0300 genesys: Implement support for Plustek Opticfilm 7200i commit 1dc1da59428ec6715dae67f0bc8a99f150fc73b3 Merge: 4c9f9f0847ee 1bab4037f56f Author: Povilas Kanapickas Date: 2019-09-29 19:18:59 +0000 Merge branch 'genesys-misc-cleanup' into 'master' genesys: Miscellaneous cleanup See merge request sane-project/backends!196 commit 1bab4037f56fa36c59a32b809cbe8407890db2fb Author: Povilas Kanapickas Date: 2019-09-18 23:31:47 +0300 genesys: Remove unused command set name property commit 60534b89ae5217e55ea23eaf1b56d025d9764173 Author: Povilas Kanapickas Date: 2019-09-18 23:31:46 +0300 genesys: Store the calibration session into device struct commit 17fe9d8393147f4af94463c6212bba299724f0b1 Author: Povilas Kanapickas Date: 2019-09-18 23:31:45 +0300 genesys: Print debug image of raw data coming from the scanner commit 4c9f9f0847ee6d2e9ebc406a28f63889473068c4 Merge: 7df7acbafd58 323f37753a66 Author: Povilas Kanapickas Date: 2019-09-29 18:44:12 +0000 Merge branch 'genesys-host-side-calibration' into 'master' genesys: Implement support for host-side calibration See merge request sane-project/backends!195 commit 323f37753a66a8f1ebe35fff8a12fd6e3c6a6963 Author: Povilas Kanapickas Date: 2019-09-18 03:54:52 +0300 genesys: Implement support for host-side calibration commit 7df7acbafd584bb041d0ef73220edcedafd4cc31 Merge: cf5f30dec309 5e2a0f572407 Author: Povilas Kanapickas Date: 2019-09-29 18:31:46 +0000 Merge branch 'genesys-gl843-image-calibration' into 'master' genesys: Use image pipeline for calibration on gl843 See merge request sane-project/backends!194 commit 5e2a0f5724070d8813657812c54c82c2f5e2292c Author: Povilas Kanapickas Date: 2019-09-17 17:21:06 +0300 genesys: Use Image in gl843_search_strip() commit 08a4d8455f8248577324e2441f29db83589c3d9e Author: Povilas Kanapickas Date: 2019-09-17 17:21:05 +0300 genesys: Use Image in gl843_coarse_gain_calibration() commit 074603598085a1850005076dde3b8793772aae3d Author: Povilas Kanapickas Date: 2019-09-17 17:21:04 +0300 genesys: Use Image in gl843_offset_calibration() commit ac54f7f9bd511c1dd545037d49390f5046384014 Author: Povilas Kanapickas Date: 2019-09-17 17:21:03 +0300 genesys: Use Image in gl843_led_calibration() commit 4a6216501416bd8b1d84e90c5072b91be0329f1e Author: Povilas Kanapickas Date: 2019-09-17 17:21:02 +0300 genesys: Use Image in gl843_search_start_position commit 93395bd24e21d9555fbe69d7bae89f9e22c49d33 Author: Povilas Kanapickas Date: 2019-09-17 17:21:01 +0300 genesys: Add wrapper to retrieve raw channel value from image commit 4f74dceaba9e02e967c0b94390642ace399b16e3 Author: Povilas Kanapickas Date: 2019-09-17 17:21:00 +0300 genesys: Add wrapper that reads Image from the scanner commit a5006703a33c4a14a6a65bcdda608175cdc7d096 Author: Povilas Kanapickas Date: 2019-09-17 17:20:59 +0300 genesys: Implement wrapper that writes Image to pnm file commit 093439153e553d10824598ba935097307ef4c807 Author: Povilas Kanapickas Date: 2019-09-17 17:20:58 +0300 genesys: Add a way to retrieve full output of pipeline as an image commit cf5f30dec30902be8172721eb8130ec299080346 Merge: b3445a1803ef 3c99d405e0ce Author: Povilas Kanapickas Date: 2019-09-29 18:23:12 +0000 Merge branch 'genesys-fix-depth-option' into 'master' genesys: Fix handling of the `--depth` option See merge request sane-project/backends!193 commit 3c99d405e0ced32f9cca9fb5de0f6362ec48ccbf Author: Povilas Kanapickas Date: 2019-09-16 12:44:01 +0300 genesys: Fix the default value of the depth option commit a93d80651f5bf2658a412eb31013f7052140972d Author: Povilas Kanapickas Date: 2019-09-16 12:44:00 +0300 genesys: Don't disable depth option even if one value is possible commit b3445a1803ef372240485b0de8a7c14bbe02fb19 Merge: a30daa96d150 b09aa6584d8e Author: Povilas Kanapickas Date: 2019-09-29 18:09:59 +0000 Merge branch 'genesys-move-register-lists-to-separate-files' into 'master' genesys: Move register address lists to separate files See merge request sane-project/backends!192 commit b09aa6584d8e52a3565b7c775cad37842c039885 Author: Povilas Kanapickas Date: 2019-09-16 11:36:02 +0300 genesys: Add include guards to headers where missing commit dc1350781f7874cb580b25397dceb4e779f58b7e Author: Povilas Kanapickas Date: 2019-09-16 11:36:01 +0300 genesys: Move register address lists to separate files commit a30daa96d150b1c79fac765a7f01be5bc45407d6 Merge: 7bd4a1db49b9 11c80b129ee9 Author: Povilas Kanapickas Date: 2019-09-29 18:00:13 +0000 Merge branch 'genesys-misc-cleanup' into 'master' genesys: Miscellaneous cleanup See merge request sane-project/backends!191 commit 11c80b129ee92b2d80dab4e895cc4e9463075b8c Author: Povilas Kanapickas Date: 2019-09-16 09:50:12 +0300 genesys: Extract wait_until_has_valid_words() commit b58192493e33a54ae53a209f8a8d181795e17b44 Author: Povilas Kanapickas Date: 2019-09-16 09:50:11 +0300 genesys: Properly use 16-bit data when calibrating commit 7bd4a1db49b94ed4c72aa445626cadda0d7ef30d Merge: 32c49e5ec1e4 599443bf72aa Author: Povilas Kanapickas Date: 2019-09-29 17:49:38 +0000 Merge branch 'genesys-image-limit-output-sheetfed-scanners' into 'master' genesys: Fix output limiting for sheetfed scanners See merge request sane-project/backends!190 commit 599443bf72aac832d4602c56556d996b5127651e Author: Povilas Kanapickas Date: 2019-09-16 09:34:15 +0300 genesys: Remove uses of dev->read_bytes_left_after_deseg commit 5e60cc3636b85d01f26a731194c9ec443149e98a Author: Povilas Kanapickas Date: 2019-09-16 09:34:14 +0300 genesys: Simplify calculations when on early document end commit 20ad0c8c2efa34d65fc5da887892ffc6c12dcc96 Author: Povilas Kanapickas Date: 2019-09-16 09:34:13 +0300 genesys: Add output_line_bytes_requested to the session struct commit 88adea9cd6697674bace583a48e05019bd4a4c90 Author: Povilas Kanapickas Date: 2019-09-16 09:34:12 +0300 genesys: Add the number of total bytes to the session struct commit c3e7411aca85515ae6c0f20e7e372a764f272788 Author: Povilas Kanapickas Date: 2019-09-16 09:34:11 +0300 genesys: Allow limiting the amount of data read from pipeline sources commit 663e3a99ed9008cd1eb8650f15b6211104d558c9 Author: Povilas Kanapickas Date: 2019-09-16 09:34:10 +0300 genesys: Add a way to report persistent eof() status from pipeline commit 2e10f4ac172b3431dbf852668589533bfda8a8f8 Author: Povilas Kanapickas Date: 2019-09-16 09:34:09 +0300 genesys: Report failures to produce full lines in pipeline commit f2b1b4449e5d4af6b852877fcf7a22afd76e420f Author: Povilas Kanapickas Date: 2019-09-16 09:34:08 +0300 genesys: Add a way to use an image as a source for a pipeline commit 32c49e5ec1e4b204f1da2320b6323abb9c6293f1 Merge: b6eef0861081 e53058e8b085 Author: Povilas Kanapickas Date: 2019-09-29 17:25:50 +0000 Merge branch 'genesys-image-full-ownership' into 'master' genesys: Add class that handles full ownership of image data See merge request sane-project/backends!188 commit e53058e8b085351ac907fe72897f32483cd77126 Author: Povilas Kanapickas Date: 2019-09-15 15:27:11 +0300 genesys: Add class that handles data ownership for full image commit 6ced865d53570f04a66855fb324817fa24e5fb50 Author: Povilas Kanapickas Date: 2019-09-15 15:27:10 +0300 genesys: Move pixel-related functionality to separate file commit b6eef08610810a9b38f98fc5c233f4138c230358 Merge: 28b216513e86 ab62a1448cb1 Author: Povilas Kanapickas Date: 2019-09-29 04:33:06 +0000 Merge branch 'genesys-session-calib-pixel-offsets' into 'master' genesys: Use session for pixel coordinates during calibration See merge request sane-project/backends!189 commit ab62a1448cb11a2dfb6fd9b3393850f087acc202 Author: Povilas Kanapickas Date: 2019-09-15 15:40:31 +0300 genesys: Use session for pixel coordinates during calibration commit 28b216513e86ae57f9baea5bce464e825245313a Merge: 7b66a8f732b0 050b0c1d8af5 Author: Povilas Kanapickas Date: 2019-09-28 18:22:42 +0000 Merge branch 'genesys-misc-cleanup' into 'master' genesys: Miscellaneous cleanup See merge request sane-project/backends!185 commit 050b0c1d8af5930b9f3faa64b7de1f15abfaeb1e Author: Povilas Kanapickas Date: 2019-09-15 14:46:48 +0300 genesys: Invert meaning of GENESYS_FLAG_LAZY_INIT commit c7bb34c998a08802e5eba1e2d5a87686d1e0e8e5 Author: Povilas Kanapickas Date: 2019-09-15 14:46:47 +0300 genesys: Simplify calibration calculations on GL843 commit 079fa25b0d6e864124fe2bb8c198cebab202836f Author: Povilas Kanapickas Date: 2019-09-15 14:46:46 +0300 genesys: Allow full value range of the frontend registers to be set commit 2bb31ed5d8c2f107bb32235d7d2442f30a30a92f Author: Povilas Kanapickas Date: 2019-09-15 14:46:45 +0300 genesys: Remove duplicate logs of information included into session commit 7b66a8f732b01f6366694c941180dab7bd2e6735 Merge: f2019befd755 b9346f0f79b8 Author: Povilas Kanapickas Date: 2019-09-28 18:19:58 +0000 Merge branch 'genesys-override-dpi' into 'master' genesys: Add a way to override logical dpi and physical pixel counts See merge request sane-project/backends!187 commit b9346f0f79b87a25a58814dddfc9b6331feda5b4 Author: Povilas Kanapickas Date: 2019-09-15 15:14:18 +0300 genesys: Add a way to multiply the pixel counts sent to the scanner commit 00b8441dfab058e01d5b9168755f241d16e3deeb Author: Povilas Kanapickas Date: 2019-09-15 15:14:17 +0300 genesys: Add a way to override logical HW dpi commit 34c7a4cb2910848da5beeace0ceefe165f274d6c Author: Povilas Kanapickas Date: 2019-09-15 15:14:16 +0300 genesys: Rename {dpihw => register_dpihw}_override commit f2019befd7558421ae13022d0e92798fd032f145 Merge: 345be52f9e32 db36dd81036a Author: Povilas Kanapickas Date: 2019-09-28 18:02:33 +0000 Merge branch 'genesys-session-pixel-offsets' into 'master' genesys: Move pixel coordinate calculation to a single place See merge request sane-project/backends!186 commit db36dd81036ac7ee9d5c15efc7f117805546f81d Author: Povilas Kanapickas Date: 2019-09-15 10:36:58 +0300 genesys: Move pixel coordinate calculation to a single place commit 345be52f9e322b30a28910238492a9ea30631096 Author: Olaf Meeuwissen Date: 2019-09-28 20:33:26 +0900 nec: Fix [-Werror=implicit-fallthrough] warning The fallthrough would log a message that is probably misleading. commit b0b8e8f980692c67ba8aff760344c9305ea02013 Author: Olaf Meeuwissen Date: 2019-09-28 18:07:27 +0900 Revert "CI: Speed up compile stage jobs" The additional variables: section in the template hides any variables set for each job that uses the template! This reverts commit 14e7ba47dda2ee7389e8db21268cd49e80ea968a. commit e00d5462dee98bd6fa7d8672fba730af9874db87 Author: Olaf Meeuwissen Date: 2019-09-28 17:43:06 +0900 Fix libusb related [-Werror=deprecated-declarations] warnings See http://libusb.sourceforge.net/api-1.0/group__libusb__lib.html commit 29580b08b55a60209c03ce4bf17a834f0904ead3 Merge: bf3e170dba7a be4c3c1f699c Author: Povilas Kanapickas Date: 2019-09-28 05:49:11 +0000 Merge branch 'genesys-deduplicate-shading-calibration' into 'master' genesys: Deduplicate shading calibration See merge request sane-project/backends!184 commit be4c3c1f699cef1aa1f169fba9a736f4c51467cb Author: Povilas Kanapickas Date: 2019-09-14 13:49:23 +0300 genesys: Reduce duplication of shading calculations commit 580b9db56238ee8f98a9d6e1b4cbc7c7cad1ce04 Author: Povilas Kanapickas Date: 2019-09-14 13:49:22 +0300 genesys: Don't duplicate actions when DARK_CALIBRATION flag is off commit 8790940cc343b892a1a4c4912da6ed09e22bd8df Author: Povilas Kanapickas Date: 2019-09-14 13:49:21 +0300 genesys: Remove code that has no effects commit 154ab4578b1f87d9791e359473f2003caa4ba5e6 Author: Povilas Kanapickas Date: 2019-09-14 13:49:20 +0300 genesys: Extract calculation of dummy dark shading commit 6f29caef0d7125c91e488b13e826c115a6ab8998 Author: Povilas Kanapickas Date: 2019-09-14 13:49:19 +0300 genesys: Extract post-shading repark out of white calibration function commit bf3e170dba7a1c0a01ae8e207247a368f75fa00c Merge: f3d0a7c17cc2 bee1179e0efa Author: Povilas Kanapickas Date: 2019-09-28 05:48:28 +0000 Merge branch 'genesys-use-image-pipeline' into 'master' genesys: Use new image operations pipeline for image data reading and conversion See merge request sane-project/backends!183 commit bee1179e0efa7a00d45bddaedd74f5819ee1ad74 Author: Povilas Kanapickas Date: 2019-09-13 17:04:05 +0300 genesys: Print debug images of intermediate pipeline output commit 41addb973065b0b8e68f975ae060df49ebe57ded Author: Povilas Kanapickas Date: 2019-09-13 17:04:04 +0300 genesys: Fix debug identifiers commit 9cd1de5c5268a89b20c21e2777229bde08f8e3d6 Author: Povilas Kanapickas Date: 2019-09-13 17:04:03 +0300 genesys: Add a way to debug image pipelines commit cd712f9f9942c424ee1c554f81ba8fa038ed41c7 Author: Povilas Kanapickas Date: 2019-09-13 17:04:02 +0300 genesys: Use new image pipeline for row scaling commit bf7e890fa4d6c17d2313d3eaf0f90d68c9ae98cd Author: Povilas Kanapickas Date: 2019-09-13 17:04:01 +0300 genesys: Use new image pipeline for CCD line shifts and unstagger commit 0b1bfa3f1201b5cfa9e93ecbc4fcf00a57d6b63d Author: Povilas Kanapickas Date: 2019-09-13 17:04:00 +0300 genesys: Swap 16-bit pixel endian on big endian machines commit cae3015b66ab340cf585fb224745e73ae6c53ae6 Author: Povilas Kanapickas Date: 2019-09-13 17:03:59 +0300 genesys: Use new image pipeline for format and CIS reordering commit f3d0a7c17cc2244059773116091e481ed5ee116b Merge: d1badffa886b 8cf5735278af Author: Povilas Kanapickas Date: 2019-09-28 05:34:34 +0000 Merge branch 'genesys-fix-pow-ambiguity' into 'master' genesys: Fix ambiguity of std::pow() on old compilers See merge request sane-project/backends!182 commit 8cf5735278af26afd6c0b563915f3be879ad14c4 Author: Povilas Kanapickas Date: 2019-09-28 06:56:03 +0300 genesys: Fix ambiguity of std::pow() on old compilers commit d1badffa886b1065322978a8ea8e5b94d88abde5 Merge: 9eefac82ca98 802a62ee2cd1 Author: Povilas Kanapickas Date: 2019-09-28 03:54:56 +0000 Merge branch 'genesys-fix-output-line-bytes-raw' into 'master' genesys: Fix calculation of session output_line_bytes_raw See merge request sane-project/backends!181 commit 802a62ee2cd13336ef0889281aa2da1a7bf2bbe7 Author: Povilas Kanapickas Date: 2019-09-13 16:40:42 +0300 genesys: Fix incorrect calculation of output_line_bytes_raw on gl646 commit 40b2d8928f22e5885dedc8eb424bd5590ce57647 Author: Povilas Kanapickas Date: 2019-09-13 16:40:41 +0300 genesys: Fix definition of output_line_bytes_raw on gl841 CIS scanners commit 434b03d4986febf22bd65c06521540a0296fe2c9 Author: Povilas Kanapickas Date: 2019-09-13 16:40:40 +0300 genesys: Fix definition of output_line_bytes_raw on gl124 commit 9eefac82ca98faf28bb56271b8526331413a2c75 Merge: a500f577c04a 97387e208900 Author: Olaf Meeuwissen Date: 2019-09-28 03:09:07 +0000 Merge branch '103-add-old-changelogs-to-source-tarball' into 'master' Resolve "Source tarball does not include older ChangeLogs" Closes #103 See merge request sane-project/backends!127 commit 97387e20890035b4685b939415af46b21958903f Author: Olaf Meeuwissen Date: 2019-08-28 21:21:07 +0900 Install ChangeLogs/ files in $docdir/ChangeLogs/ This makes the trailing comment in ChangeLog correct for both the repository and installed documentation. Complements ee2653b02b75e6b99a0ff77745a4a738049d58db. commit 8f3b84f6f4e166cfbae73220d888eaa9d7d7025d Author: Olaf Meeuwissen Date: 2019-08-26 21:53:09 +0900 Adjust ChangeLog trailer comment to match Makefile.am comment commit 2baee4914d0d31aac01e939d91885b6695bcc47d Author: Olaf Meeuwissen Date: 2019-08-26 21:48:52 +0900 umax_pp: Drop reference to ChangeLog in code comment commit eb5382c7535caf2deedfe54bc5e455de4dd55b11 Author: Olaf Meeuwissen Date: 2019-08-26 21:48:09 +0900 mustek_pp: Drop unused ChangeLog: references from comments commit fe6bac727f6c557df01f0385a56b8b5133561ea0 Author: Olaf Meeuwissen Date: 2019-08-26 21:43:52 +0900 doc: Drop moving of ChangeLog files with each release. See #103 commit 47f0b5dee2af853c6f024edc4d8affd9f225656d Author: Olaf Meeuwissen Date: 2019-08-26 21:43:25 +0900 doc: Update ChangeLog related documentation commit 2a9d98be6e9ec22507c13b3c5cceba3bd234ec8a Author: Olaf Meeuwissen Date: 2019-08-24 17:24:23 +0900 Resurrect ChangeLog-1.0.11 It was missing in the ChangeLogs/ directory. Content has been dug up from the ChangeLog file for the RELEASE_1_0_11 tag. commit ee2653b02b75e6b99a0ff77745a4a738049d58db Author: Olaf Meeuwissen Date: 2019-08-24 17:10:27 +0900 Include ChangeLogs/ directory content in source tarball. Re #103 The files are installed next to the ChangeLog file in $docdir. There will be *no* $docdir/ChangeLogs/ directory. commit a500f577c04a300cddcacccffd52de59c3f80909 Merge: c9e95999f350 efbfcb0a7fc8 Author: Povilas Kanapickas Date: 2019-09-27 22:43:07 +0000 Merge branch 'genesys-calibration-variable-names' into 'master' genesys: Improve clarity of calibration offset variable names See merge request sane-project/backends!180 commit efbfcb0a7fc88fdfda0aeb13d0b32379d7e9100d Author: Povilas Kanapickas Date: 2019-09-13 16:03:21 +0300 genesys: Improve clarity of calibration offset variable names commit c9e95999f350a2145797954adcc770da91d4ffd7 Merge: da745b2f3007 661aa3bcdb42 Author: Povilas Kanapickas Date: 2019-09-27 22:38:55 +0000 Merge branch 'genesys-generic-register-set' into 'master' genesys: Make GenesysRegisterSettingSet generic See merge request sane-project/backends!179 commit 661aa3bcdb42a30ff02ffbe7d3f74ad09573a4a6 Author: Povilas Kanapickas Date: 2019-09-13 15:56:44 +0300 genesys: Make GenesysRegisterSettingSet generic commit da745b2f3007d3450845af1fde87265f23366aa3 Merge: dc52dc84f047 d4e4bf35c127 Author: Povilas Kanapickas Date: 2019-09-27 22:35:42 +0000 Merge branch 'genesys-image-pipeline-segmented-sensors' into 'master' genesys: Use the new image pipeline for segmented sensor desegmentation See merge request sane-project/backends!178 commit d4e4bf35c1275448bb212e33ee5eebe9afa505cf Author: Povilas Kanapickas Date: 2019-09-13 15:37:33 +0300 genesys: Fix a bug in segmented sensors when scanning 16-bit data commit af17797d3ce91aad2b7ff9cb604955716a1195ad Author: Povilas Kanapickas Date: 2019-09-13 15:37:32 +0300 genesys: Use new image pipeline for handling segmented sensors commit 7f852e0417a7524b6abb7bc6e428cee87dbef20d Author: Povilas Kanapickas Date: 2019-09-13 15:37:31 +0300 genesys: Fix depth calculation for segmented sensors in lineart mode commit e89c6a0aab188938b77c885946ab163ba1cbfa64 Author: Povilas Kanapickas Date: 2019-09-13 15:37:30 +0300 genesys: Remove accidental uses of C++14 APIs commit dc52dc84f047749abf5e226f2d4c23c0c8fa131c Merge: 7a8da5c40bf4 9a844e1eded5 Author: Povilas Kanapickas Date: 2019-09-27 21:56:17 +0000 Merge branch 'genesys-misc-cleanup' into 'master' genesys: Miscellaneous cleanup See merge request sane-project/backends!177 commit 9a844e1eded54687aa4094359fdabe5f233f24d2 Author: Povilas Kanapickas Date: 2019-09-13 11:36:56 +0300 genesys: Compute what image processing will be needed in session commit a5efb969789f1ea8f663267ed5e1012ac7c35831 Author: Povilas Kanapickas Date: 2019-09-13 11:36:55 +0300 genesys: Replace uses of Genesys_Color_Order with ColorOrder commit 05ed63fa34edd1e3c842b996f85d554bee8d8df1 Author: Povilas Kanapickas Date: 2019-09-13 11:36:54 +0300 genesys: Move setup of read buffer to a single function commit 28d15784e4e3ee5750203f7bb401d716037df436 Author: Povilas Kanapickas Date: 2019-09-13 11:36:53 +0300 genesys: Remove unused code commit 8ed2cd991b24f64ff6945ee67adaa93e40dcd43a Author: Povilas Kanapickas Date: 2019-09-13 11:36:52 +0300 genesys: Initialize frontend state from the outset commit 7a8da5c40bf4c4e08f7da21a4a4f33e0ac2bd228 Merge: 0440123bdc5b d73915ba197a Author: Povilas Kanapickas Date: 2019-09-27 21:32:02 +0000 Merge branch 'genesys-analog-devices-adc-calibration' into 'master' genesys: Implement gain calibration for Analog-Devices ADC See merge request sane-project/backends!176 commit d73915ba197a9d33dc9e3e8131660c01d2c6b833 Author: Povilas Kanapickas Date: 2019-09-13 11:01:48 +0300 genesys: Implement gain calibration for Analog-Devices ADC commit e45e668326a4499a8586ab94b6a7bb2a2380efa3 Author: Povilas Kanapickas Date: 2019-09-13 11:01:47 +0300 genesys: Extract gain computation into a separate function commit 94c1cf1d5a640aa6a40b93d3f37dba3a60816477 Author: Povilas Kanapickas Date: 2019-09-13 11:01:46 +0300 genesys: Add clamp() utility function commit 0440123bdc5b6104a44098c4d066d8c3404cd413 Merge: 4cd9dbb73d74 ad1067ad03b4 Author: Povilas Kanapickas Date: 2019-09-27 21:21:09 +0000 Merge branch 'genesys-gl843-segmented-sensors' into 'master' genesys: Add support for segmented sensors on gl843 See merge request sane-project/backends!175 commit ad1067ad03b4a043372ce6c2ec6408448e65e4ff Author: Povilas Kanapickas Date: 2019-09-13 10:52:35 +0300 genesys: Add support for segmented sensors on gl843 commit 4cd9dbb73d745891732f3ba3c2b48268f7b1a713 Merge: c59f9a037bc5 1c7ef4b55ee9 Author: Povilas Kanapickas Date: 2019-09-27 21:06:44 +0000 Merge branch 'genesys-image-library' into 'master' genesys: Implement a streaming image library See merge request sane-project/backends!174 commit 1c7ef4b55ee998af7612a68e50de97a5fbc5a0e8 Author: Povilas Kanapickas Date: 2019-09-13 10:38:05 +0300 genesys: Implement image data source that mimics current reading from USB commit 7d7a3952773258027c92d331f9d7e6e9d6ba6f69 Author: Povilas Kanapickas Date: 2019-09-13 10:38:04 +0300 genesys: Implement library for image manipulation commit c59f9a037bc5a53ad4dfd23f28c602a8e0bac557 Merge: 17dc1aee81b2 2f3d9e9d7682 Author: Povilas Kanapickas Date: 2019-09-27 20:55:51 +0000 Merge branch 'genesys-default-scan-method' into 'master' genesys: Add a way to specify default scan method See merge request sane-project/backends!173 commit 2f3d9e9d7682bce0cd87bdf3e7322943a27742ab Author: Povilas Kanapickas Date: 2019-09-12 23:48:16 +0300 genesys: Don't crash when default scan method is not supported commit 4c18ef27a247c4a1d04ee34d2d7f00c7063683e7 Author: Povilas Kanapickas Date: 2019-09-12 23:48:15 +0300 genesys: Raise exception when device structs are not found commit c2115543db126832450bbf4d2a66b82d53ce4a85 Author: Povilas Kanapickas Date: 2019-09-12 23:48:14 +0300 genesys: Implement a way to set default scan method used internally commit c0010fcd9f26f9af34de7b49f3aef90dc938896e Author: Povilas Kanapickas Date: 2019-09-12 23:48:13 +0300 genesys: Fix incorrect scan method in move function on gl646 commit 17dc1aee81b247c973d82a045e66fec871d5af9b Merge: 60ce6b5c0128 9b7ab9372786 Author: Povilas Kanapickas Date: 2019-09-27 20:44:47 +0000 Merge branch 'genesys-desegmentation-refactor' into 'master' genesys: Simplify desegmentation state (part 6) See merge request sane-project/backends!172 commit 9b7ab9372786c63a90c01817f25ad17dbb12808e Author: Povilas Kanapickas Date: 2019-09-12 22:39:22 +0300 genesys: Add unit test for genesys_fill_segmented_buffer() commit 502478a40d66030895abcc351250fc040bfe41a9 Author: Povilas Kanapickas Date: 2019-09-12 22:39:21 +0300 genesys: Reindent genesys_fill_segmented_buffer() commit c2c65f29c9a2fa6695141532f104f00d1d7bfd17 Author: Povilas Kanapickas Date: 2019-09-12 22:39:20 +0300 genesys: Simplify genesys_fill_segmented_buffer() commit 60ce6b5c0128676b6d5aed75ee6ddd73e91776c8 Merge: 015252e4a828 e0974ab74ac9 Author: Povilas Kanapickas Date: 2019-09-27 20:37:08 +0000 Merge branch 'stv-werror-fix-constant-redefinition' into 'master' stv: Fix macro constant redefinition See merge request sane-project/backends!171 commit e0974ab74ac90b47975739d69b04ff8e2b86a7cb Author: Povilas Kanapickas Date: 2019-09-20 17:29:41 +0300 stv: Fix macro constant redefinition commit 015252e4a8285c9b39cb0e8ba9aaf883ef7187e5 Merge: 3618a244af39 0290a263946e Author: Povilas Kanapickas Date: 2019-09-27 20:35:13 +0000 Merge branch 'sanei-usb-optimize-test-mode-bulk-transfers' into 'master' sanei_usb: Optimize bulk USB transfers in testing mode See merge request sane-project/backends!170 commit 0290a263946e233c743b4721e56fdc98b1624b1f Author: Povilas Kanapickas Date: 2019-09-27 23:19:52 +0300 sanei_usb: Optimize bulk USB transfers in testing mode commit 3618a244af39ea26cf7c9cbfc914d9ccd2706e69 Merge: 86b9768dd323 0267eeeeae4b Author: Povilas Kanapickas Date: 2019-09-27 19:44:15 +0000 Merge branch 'genesys-desegmentation-refactor' into 'master' genesys: Simplify desegmentation state (part 5) See merge request sane-project/backends!169 commit 0267eeeeae4be4b2dbf45d8be3e1d0be5efb0e12 Author: Povilas Kanapickas Date: 2019-09-12 22:22:00 +0300 genesys: Remove the desegmentation state struct commit 3d023060a35fb8b1cf2311746340f83d1118c59e Author: Povilas Kanapickas Date: 2019-09-12 22:21:59 +0300 genesys: Move desegmentation skip_bytes to session struct commit 192dd886cd9e7fd368c339f4ef3526b082a18c83 Author: Povilas Kanapickas Date: 2019-09-12 22:21:58 +0300 genesys: Use common code path to compute ..._segment_pixel_group_count commit 4870a9f97529b09fa9cf013c76b3e40329078024 Author: Povilas Kanapickas Date: 2019-09-12 22:21:57 +0300 genesys: Properly set conseq_pixel_dist_bytes on gl124 commit 95eb6a72ddf52a1d95b5427a6d441d9992bd9ce1 Author: Povilas Kanapickas Date: 2019-09-12 22:21:56 +0300 genesys: Use single code path to compute session output_line_bytes_raw commit 1f4a5311d74b518b25411bc12da4e7d5974b7d20 Author: Povilas Kanapickas Date: 2019-09-12 22:21:55 +0300 genesys: Rename DesegmentationState::raw_{channel => line}_bytes commit 61d4779402c7c79852187f7e8916ff661d8963f2 Author: Povilas Kanapickas Date: 2019-09-12 22:21:54 +0300 genesys: Remove erroneously named raw_line_bytes The raw_channel_bytes is what we store the line information to. Turns out raw_line_bytes is unnecessary and only ever used by error. commit 4df8872ab3559e7162f99fab4d1078f5db1827e4 Author: Povilas Kanapickas Date: 2019-09-12 22:21:53 +0300 genesys: Simplify raw_channel_bytes computation more On the chipsets in question, session.hwdpi_divisor == sensor.optical_res / sensor.get_register_hwdpi(...), so the divisor cancels out with the rest of the expression. commit 95a3836f1a08252d5d7f32748389d1175bf93d1c Author: Povilas Kanapickas Date: 2019-09-12 22:21:52 +0300 genesys: Simplify raw_channel_bytes calculation commit 98320f5b8126e638e0e0064d43555b63b80f9515 Author: Povilas Kanapickas Date: 2019-09-12 22:21:51 +0300 genesys: Don't overflow in multiply_by_depth_ceil commit 86b9768dd3238b9238751e4a969edb476f084b93 Merge: 16b0033439fd eb0882ecc445 Author: Povilas Kanapickas Date: 2019-09-27 19:22:08 +0000 Merge branch 'genesys-desegmentation-refactor' into 'master' genesys: Simplify desegmentation state (part 4) See merge request sane-project/backends!168 commit eb0882ecc4459d6e8982e97446b34f1f9357b517 Author: Povilas Kanapickas Date: 2019-09-12 22:11:20 +0300 genesys: Use common code path to compute session optical_pixels_raw commit 3a323a751a60e1091d8bde2a569a7daadd072db1 Author: Povilas Kanapickas Date: 2019-09-12 22:11:19 +0300 genesys: Use common code path to compute session conseq_pixel_dist_bytes commit 16b0033439fd56280abd124a4d94852f3e033b0e Merge: 4f1890f19536 693ff3e77a46 Author: Povilas Kanapickas Date: 2019-09-27 00:32:05 +0000 Merge branch 'genesys-fix-cxx11-build' into 'master' genesys: Remove accidental use of C++14 features See merge request sane-project/backends!166 commit 693ff3e77a46ed2318b4528e94c595fddf1b537f Author: Povilas Kanapickas Date: 2019-09-27 03:10:53 +0300 genesys: Remove accidental use of C++14 features commit 4f1890f19536637f0b697dcb21a3998171cb2e04 Merge: a4729cff19aa b856c2cb6f89 Author: Povilas Kanapickas Date: 2019-09-24 22:46:08 +0000 Merge branch 'genesys-desegmentation-refactor' into 'master' genesys: Simplify desegmentation state (part 3) See merge request sane-project/backends!165 commit b856c2cb6f8946a2560959783d6c85bbbb766fed Author: Povilas Kanapickas Date: 2019-09-12 21:24:32 +0300 genesys: Remove used_pixels to simplify segment setup commit 0a10bcef59bc1cd021cc6b97fb561e551f468845 Author: Povilas Kanapickas Date: 2019-09-12 21:24:31 +0300 genesys: Refactor the segment setup code to expose chip similarities commit 41b5e5f5ff3932758348e2d673fa9ab492617dda Author: Povilas Kanapickas Date: 2019-09-12 21:24:30 +0300 genesys: Use common code path to compute session segment_count commit ae784d9927b9c0c5395d3eb7b7189971a146fdc2 Author: Povilas Kanapickas Date: 2019-09-12 21:24:29 +0300 genesys: Simplify segment count calculation commit a4729cff19aa736fa5c0fb1c2941774bfee2e3e2 Merge: e4e1a02733dd a830fca943fc Author: Povilas Kanapickas Date: 2019-09-24 21:11:09 +0000 Merge branch 'genesys-misc-cleanup' into 'master' genesys: Miscellaneous cleanup See merge request sane-project/backends!164 commit a830fca943fcf6c2a30669392b8dea3ab68e65ca Author: Povilas Kanapickas Date: 2019-09-12 22:40:57 +0300 genesys: Remove GL123 enum commit 322a9a6810f18a45055eae2c90e73c457ee02880 Author: Povilas Kanapickas Date: 2019-09-12 22:40:56 +0300 genesys: Don't modify session in *_init_scan_regs() commit 80d0129f4415a55325f53f3622802f05d4e8f6e5 Author: Povilas Kanapickas Date: 2019-09-12 22:40:55 +0300 genesys: Pass start pixel to *_init_optical_regs_scan() via session commit c0e465784e7fd85ce811b15159ee8b849b68f158 Author: Povilas Kanapickas Date: 2019-09-12 22:40:54 +0300 genesys: Deduplicate different get_sensor_profile implementations commit e4e1a02733dd3c7733e747bb6e2f7b30db7c9fcf Merge: 37b60ada834d 52baae02dca2 Author: Povilas Kanapickas Date: 2019-09-24 15:08:50 +0000 Merge branch 'genesys-session-hwdpi-divisor' into 'master' genesys: Use common code path to compute session hwdpi divisor See merge request sane-project/backends!163 commit 52baae02dca2f0bd346d9f9453b6afeff03874da Author: Povilas Kanapickas Date: 2019-09-12 21:55:30 +0300 genesys: Use common code path to compute session hwdpi_divisor commit f480521a231596f5c3ce35dbc7466b6913aa34af Author: Povilas Kanapickas Date: 2019-09-12 21:55:29 +0300 genesys: Pass sensor profile directly to *_setup_sensor() where possible commit 37b60ada834d707618e494d6b94f4f7b5d0217f9 Merge: 8539f350fd3b 676831dc7bf9 Author: Povilas Kanapickas Date: 2019-09-24 14:41:17 +0000 Merge branch 'genesys-desegmetation-refactor' into 'master' genesys: Simplify desegmentation state (part 2) See merge request sane-project/backends!162 commit 676831dc7bf9cab36fe72786e8a89d935ea8da0b Author: Povilas Kanapickas Date: 2019-09-12 21:26:58 +0300 genesys: Move Genesys_Device::curr to DesegmentationState commit f92253adc86b425abc228ddbed6de1dac67f40c3 Author: Povilas Kanapickas Date: 2019-09-12 21:26:57 +0300 genesys: Move Genesys_Device::segnb to DesegmentationState commit 15996983ba34868927750665dd49b3d13b76a2c8 Author: Povilas Kanapickas Date: 2019-09-12 21:26:56 +0300 genesys: Rename SensorProfile::segment_{count => size} commit 31820384de76a71e83b6fa003b2d2c95d2a85272 Author: Povilas Kanapickas Date: 2019-09-12 21:26:55 +0300 genesys: Move Genesys_Device::skip to DesegmentationState commit 9bd14f535059e674016a96b1f04a38533bc15a4b Author: Povilas Kanapickas Date: 2019-09-12 21:26:54 +0300 genesys: Move Genesys_Device::len to DesegmentationState commit 07d8db5423cae48cbe247f3a3b6f42a1c33aa6ca Author: Povilas Kanapickas Date: 2019-09-12 21:26:53 +0300 genesys: Move Genesys_Device::dist to DesegmentationState commit dd702968279b4328d0650853a15cc4bf96542494 Author: Povilas Kanapickas Date: 2019-09-12 21:26:52 +0300 genesys: Move desegmentation line width data to a separate struct commit 827c3a049db4391501dda81f2999df8e4d011403 Author: Povilas Kanapickas Date: 2019-09-12 21:26:51 +0300 genesys: Rename ScanSession::output_{line_channel => channel}_bytes commit 8539f350fd3bfe654a5fd680428fa97cea352034 Author: Olaf Meeuwissen Date: 2019-09-24 21:22:13 +0900 sanei_wire: Fix sanei_w_void function signature This function needs to have the same number of arguments as any other functions passed to sanei_w_array() via its w_element argument, i.e. sanei_w_char and sanei_w_word, in the w_option_value() implementation in sanei/sanei_net.c. Fixes [-Werror=cast-function-type] compiler warning. commit cad4085565437579884698e42c38eb0a685ad4a7 Merge: ce2d9840a741 cbb7e953505e Author: Povilas Kanapickas Date: 2019-09-22 20:47:19 +0000 Merge branch 'genesys-session-buffer-sizes' into 'master' genesys: Use common code path for computing session buffer sizes See merge request sane-project/backends!161 commit cbb7e953505e09f049e142f8cf30d903fb1d1d50 Author: Povilas Kanapickas Date: 2019-09-12 20:28:46 +0300 genesys: Use common code path to compute session buffer sizes commit a0cead1cd5d43bba44fc400676ee52b86fc8c186 Author: Povilas Kanapickas Date: 2019-09-12 20:28:45 +0300 genesys: Pass asic type to sanei_genesys_get_bulk_max_size() commit ce2d9840a741906844de36e5cf96575e33d02f4d Merge: f44c0bb6802a 88d6cdcc32c1 Author: Povilas Kanapickas Date: 2019-09-22 18:29:02 +0000 Merge branch 'genesys-desegmentation-refactor' into 'master' genesys: Simplify desegmentation state (part 1) See merge request sane-project/backends!160 commit 88d6cdcc32c13114531b9879ece1410b56e3de09 Author: Povilas Kanapickas Date: 2019-09-12 20:06:38 +0300 genesys: Rename read_bytes_left -> read_bytes_left_after_deseg commit 96a893bb0d9c46ae2ce0a4824d7fc63f863e8c0d Author: Povilas Kanapickas Date: 2019-09-12 20:06:37 +0300 genesys: Simplify words_per_line calculations commit 463f5f4364b8e702369af10f28fb78bc7b253633 Author: Povilas Kanapickas Date: 2019-09-12 20:06:36 +0300 genesys: Don't duplicate data passed to *_init_optical_regs_scan() commit f44c0bb6802a6263ff692d0bcdebef21f2c64ea8 Merge: 37734c880d46 ed4419e73d63 Author: Povilas Kanapickas Date: 2019-09-22 17:39:15 +0000 Merge branch 'genesys-session-line-sizes' into 'master' genesys: Use common code path to compute session line sizes See merge request sane-project/backends!159 commit ed4419e73d631f04e9f6b2da07ea1adc2429f3fc Author: Povilas Kanapickas Date: 2019-09-12 19:49:51 +0300 genesys: Use common code path to calculate session line sizes commit 9915608309a803c4cc306158c896117cb64ccf76 Author: Povilas Kanapickas Date: 2019-09-12 19:49:50 +0300 genesys: Extract multiplication by depth to a separate function commit 37734c880d467cc14c143a43e963e746ab36ca09 Merge: 0cf4c1d55567 991eec63d57e Author: Povilas Kanapickas Date: 2019-09-22 17:17:29 +0000 Merge branch 'genesys-session-output-line-count' into 'master' genesys: Use common code path to compute session output_line_count See merge request sane-project/backends!158 commit 991eec63d57ef58a6c67af7e18f182edf13aaf7c Author: Povilas Kanapickas Date: 2019-09-12 19:34:43 +0300 genesys: Use common code path to compute session output_line_count commit 0cf4c1d555671058ef9e3a77167891822199b56e Merge: 6f51393bd7a9 605fcf97626f Author: Povilas Kanapickas Date: 2019-09-22 16:58:14 +0000 Merge branch 'genesys-misc-cleanup' into 'master' genesys: Miscellaneous cleanup in gl646 motor code See merge request sane-project/backends!157 commit 605fcf97626fcc66fd3f7b0c9e2e1aa9e29afa1a Author: Povilas Kanapickas Date: 2019-09-12 19:11:07 +0300 genesys: Assume correct resolution is passed from high-level commit 73951b4bf80a498ec238a6a9ef951fc598b3b7ae Author: Povilas Kanapickas Date: 2019-09-12 19:11:06 +0300 genesys: Simplify motor code on gl646 by retining real motor dpi prop commit 6f51393bd7a935086989f1c10a012d4fccd91141 Merge: d0365bd7e4d3 ce144ed46fe7 Author: Povilas Kanapickas Date: 2019-09-21 15:59:41 +0000 Merge branch 'genesys-session-max-color-shift-lines' into 'master' genesys: Use common code path to compute session max_color_shift_lines See merge request sane-project/backends!155 commit ce144ed46fe7b28b4e1753753651796af70fc47d Author: Povilas Kanapickas Date: 2019-09-12 18:27:45 +0300 genesys: Use common code path to compute session max_color_shift_lines commit d0365bd7e4d37c45668c7aa7da3c11da5fbabd8a Merge: 9afaf9a2cf37 c69e22eb766d Author: Povilas Kanapickas Date: 2019-09-21 15:56:34 +0000 Merge branch 'sanei-fix--usb-testing-mode-in-release' into 'master' sanei_usb: Fix uninitialized device struct in USB testing code See merge request sane-project/backends!156 commit c69e22eb766d9cb1505099f5075c44a24dd412c8 Author: Povilas Kanapickas Date: 2019-09-21 18:42:56 +0300 sanei_usb: Fix uninitialized device struct in USB testing code commit 9afaf9a2cf373f40fafd26b3c69414ec15294076 Merge: da9307c7a63b 77e0acde229f Author: Povilas Kanapickas Date: 2019-09-20 14:37:24 +0000 Merge branch 'genesys-fix-total-bytes-to-read' into 'master' genesys: Fix computation of total_bytes_to_read See merge request sane-project/backends!154 commit 77e0acde229f89efaf3fbea1e990b90ae1d9f932 Author: Povilas Kanapickas Date: 2019-09-12 18:22:57 +0300 genesys: Fix computation of total_bytes_to_read commit da9307c7a63b8c05c5275e3768de2760325f2f29 Merge: bd35742b789a 0961c5d32fc1 Author: Povilas Kanapickas Date: 2019-09-19 22:51:01 +0000 Merge branch 'genesys-session-output-pixels' into 'master' genesys: Use common code path to compute session output_pixels See merge request sane-project/backends!153 commit 0961c5d32fc173bb55a30cdd55d7b28ac6c5a1ab Author: Povilas Kanapickas Date: 2019-09-12 18:23:32 +0300 genesys: Use common code path to compute session output_pixels commit bd35742b789ac1ea1b528880afca75b0489020bc Merge: c1b8cb5c31b3 50e5c804346b Author: Povilas Kanapickas Date: 2019-09-19 22:32:54 +0000 Merge branch 'genesys-handle-non-uniform-scans-at-high-level' into 'master' genesys: handle non-uniform scans at high level See merge request sane-project/backends!152 commit 50e5c804346bf92528b7aad384dbc4749dec0f98 Author: Povilas Kanapickas Date: 2019-09-12 18:21:18 +0300 genesys: Remove low-level handling of xdpi < ydpi case The high-level code ensures that the xdpi is always natively supported by the scanner. commit 548eff4a329b9ee0ff860cc0edb332f20b3ab201 Author: Povilas Kanapickas Date: 2019-09-12 18:21:17 +0300 genesys: Fix support for scans when xdpi Date: 2019-09-12 18:21:16 +0300 genesys: Use Sensor::optical_res directly where possible commit c1b8cb5c31b39f375881f12f73d91a61a610d50f Merge: 1e2f64dc3dee 453a6b75be86 Author: Povilas Kanapickas Date: 2019-09-19 22:17:43 +0000 Merge branch 'genesys-session-num-staggered-lines' into 'master' genesys: Use common code path to compute session num_staggered_lines See merge request sane-project/backends!151 commit 453a6b75be86522aa03d666ceddd77c0950880dd Author: Povilas Kanapickas Date: 2019-09-12 17:59:47 +0300 genesys: Use common code path to compute session num_staggered_lines commit 1e2f64dc3dee2f53cf9a8eb5f9201660cff5cd6c Author: Rolf Bensch Date: 2019-09-12 22:09:22 +0200 pixma: backend version 0.23.2 commit aacdd88a274e2ebaf702ce161292529f41946a35 Author: Rolf Bensch Date: 2019-09-12 22:08:49 +0200 pixma: update doc files commit fd94a7e60c24d7564cbf1bf49c99fedda377f036 Merge: 7e3a68c9e7ce 7f491519d106 Author: Rolf Bensch Date: 2019-09-12 19:42:12 +0000 Merge branch 'MF633_635_645' into 'master' pixma: new scanners Canon i-SENSYS MF633C/MF635C, MF645C See merge request sane-project/backends!126 commit 7f491519d106ecd672e5ef2bb16693056e21ea1d Author: Mikael Vallerie Date: 2019-08-27 15:34:47 +0200 Removing MF635_PID, using MF630_PID instead commit d08e4c9000808aba840b7f8e7e5b26f2af2326c1 Author: Mikael Vallerie Date: 2019-08-27 15:33:58 +0200 Removing MF633C/MF635C from docs, as it's just a variant of MF630 commit 13008723cd9f6954ecc86f51fb612171ec183570 Author: Mikael Vallerie Date: 2019-08-26 18:41:29 +0200 Right PIDs for MF633C/MF635C and MF645C commit f38f0b868117eff208460ba0e77a6270247c71fd Author: Mikael Vallerie Date: 2019-08-26 18:41:06 +0200 Right pid for MF645C commit 62284c2ed5739e51accaa4e3c8b96e0047425996 Author: Mikael Vallerie Date: 2019-08-19 20:21:23 +0200 Docs for MF633C/MF635C and MF645C commit 5d21800b51f86b7d6e4d8be8c89b7e2186a7daa7 Author: Mikael Vallerie Date: 2019-08-18 18:53:08 +0200 pixma: new scanners Canon i-SENSYS MF633C/MF635C, MF645C commit 7e3a68c9e7ceee244ad7cbe1ed0842ae5dd7cf2e Merge: 489a80088abc f2ce27f44130 Author: Povilas Kanapickas Date: 2019-09-09 21:54:13 +0000 Merge branch 'sanei-usb-fix-build-without-usb' into 'master' sanei_usb: Fix build when USB support is disabled Closes #123 See merge request sane-project/backends!150 commit f2ce27f441307ae42c888a033328c8007e62bc36 Author: Povilas Kanapickas Date: 2019-09-10 00:39:08 +0300 sanei_usb: Fix build when USB support is disabled commit 489a80088abc289be23c2ab6f44840030b8f8293 Merge: 6b7052c4cf24 8f1615f3337d Author: Stanislav Yuzvinsky Date: 2019-09-07 18:20:19 +0000 Merge branch 'ricoh2' into 'master' ricoh2: Add support for Aficio SG3110SFNw See merge request sane-project/backends!149 commit 8f1615f3337d401961094cd707d2271c5b281f12 Author: Vladislav Bogdanov Date: 2019-09-04 11:07:26 +0300 ricoh2: Add support for Aficio SG3110SFNw commit 6b7052c4cf24c3a01d1871bdeb90c6627c083e51 Merge: fc85e7d15d4a 2757475dd998 Author: Povilas Kanapickas Date: 2019-08-31 18:19:57 +0000 Merge branch 'genesys-misc-cleanup' into 'master' genesys: Miscellaneous cleanup See merge request sane-project/backends!148 commit 2757475dd99863eeb5e59ea38333c362bfd26604 Author: Povilas Kanapickas Date: 2019-08-24 10:13:44 +0300 genesys: Use session.params.xres directly where possible commit ba3013b1aae35b57e35dc38c4027e182f2fb519f Author: Povilas Kanapickas Date: 2019-08-24 10:13:43 +0300 genesys: Handle full sensor scans on gl841 consistently to other chips commit 48251cf8b22dec638861cd8832bbf1fe0d1e38bc Author: Povilas Kanapickas Date: 2019-08-24 10:13:42 +0300 genesys: Simplify start pixel computation on gl841 commit 6ca11bfd719fdf6e08fd5c8711356af0f9bca97d Author: Povilas Kanapickas Date: 2019-08-24 10:13:41 +0300 genesys: Always dump session after computing it commit fc85e7d15d4a8631e9777cf7704d3115fdc8058a Merge: 3db2278bea1a 5226a163ab60 Author: Povilas Kanapickas Date: 2019-08-31 18:02:54 +0000 Merge branch 'genesys-session-optical-pixels' into 'master' genesys: Use common code path to compute session optical_pixels See merge request sane-project/backends!147 commit 5226a163ab606178474bcbdc12466689a53a2604 Author: Povilas Kanapickas Date: 2019-08-24 10:05:53 +0300 genesys: Use common code path to compute session optical_pixels commit 3db2278bea1af5d74923ff6b746ed390a16f769a Merge: 66c804a7ea40 ec5225e1f8a1 Author: Povilas Kanapickas Date: 2019-08-31 17:44:21 +0000 Merge branch 'genesys-misc-simplify' into 'master' genesys: Miscellaneous simplifications See merge request sane-project/backends!146 commit ec5225e1f8a165fd50786459383b16f5c0c39524 Author: Povilas Kanapickas Date: 2019-08-24 10:00:52 +0300 genesys: Assume correct resolution is passed from high-level commit 9a21a8335fadc7f5722b85dc0172ee24f221b3b1 Author: Povilas Kanapickas Date: 2019-08-24 10:00:51 +0300 genesys: Remove dead code related to SCAN_FLAG_USE_OPTICAL_RES commit 21f949bd8736ecffe0ed0ad1409bbe01d882d148 Author: Povilas Kanapickas Date: 2019-08-24 10:00:50 +0300 genesys: Remove *_compute_exposure() wrappers commit 66c804a7ea40f30fe771421a9eab4ccac90ac8be Merge: 6fcfab3fdad1 a2ade508700a Author: Povilas Kanapickas Date: 2019-08-31 17:18:37 +0000 Merge branch 'genesys-ensure-correct-resolution-high-level' into 'master' genesys: Ensure that correct resolution is passed from high level See merge request sane-project/backends!145 commit a2ade508700adab5ecffca3301170fa0f3ed8ca0 Author: Povilas Kanapickas Date: 2019-08-18 11:13:01 +0300 genesys: Ensure that resolution is set to a supported one at high-level Previously we relied on low-level implementation to detect when the requested resolution is higher than one supported by the scanner and seamlessly patch around so that the high-level code does not notice. This complicates the low level implementation and results to duplicate code. commit 61a055a8cfb8fb8874ba077cb602a9447dd5f128 Author: Povilas Kanapickas Date: 2019-08-18 11:13:00 +0300 genesys: Pass the line width requested by the frontend separately commit 0b2840a3ff61a791f8a19c0f9e5d0343f6d9a229 Author: Povilas Kanapickas Date: 2019-08-18 11:12:59 +0300 genesys: Clarify frontend param processing a little commit ec0ceaebb5fa61390f38655c2e497063aa021608 Author: Povilas Kanapickas Date: 2019-08-18 11:12:58 +0300 genesys: Use correct resolution to select sensor during option parsing commit 6fcfab3fdad1783765fa48c280d586bce03ac3ca Merge: dc7c1f81f384 f48239419ab3 Author: Povilas Kanapickas Date: 2019-08-31 17:02:03 +0000 Merge branch 'genesys-session-optical-resolution' into 'master' genesys: Use common code path to compute session optical_resolution See merge request sane-project/backends!144 commit f48239419ab3756fd84e51e05625d54b142341df Author: Povilas Kanapickas Date: 2019-08-18 10:39:36 +0300 genesys: Use common code path to compute session optical_resolution commit dc7c1f81f384bf23b8275d56229d689258eb42c6 Merge: da5d0bc91698 687b549f0ad1 Author: Povilas Kanapickas Date: 2019-08-31 16:45:04 +0000 Merge branch 'genesys-session-ccd-size-divisor' into 'master' genesys: Use ScanSession to compute ccd_size_divisor on all chips See merge request sane-project/backends!143 commit 687b549f0ad1f25d31d045d9a10098a4001444c9 Author: Povilas Kanapickas Date: 2019-08-18 10:37:49 +0300 genesys: Use ScanSession to compute ccd_size_divisor on all chips commit da5d0bc91698dd4f923398b1b1fad7534201283a Merge: 1a02dfc8cb07 510b6d0d86be Author: Povilas Kanapickas Date: 2019-08-31 16:22:45 +0000 Merge branch 'genesys-gl646-simplify-setup-registers' into 'master' genesys: Simplify gl646 register setup See merge request sane-project/backends!142 commit 510b6d0d86be7892e284ddb7a0e592faa5a8bc12 Author: Povilas Kanapickas Date: 2019-08-26 00:29:18 +0300 genesys: Remove gl646-specific get_ccd_size_divisor commit e0efbd37b80022f1451b19aa558775d22300bdf5 Author: Povilas Kanapickas Date: 2019-08-25 23:40:39 +0300 genesys: Use sensor functionality instead of compute_ccd_size_divisor() commit a6a785e28d95d644c48a03f201519712f5cc7c1c Author: Povilas Kanapickas Date: 2019-08-25 23:34:38 +0300 genesys: Use common path to compute ccd_size_divisor on gl646 commit e2e1efae804fb3f1e2fb2a2c083e332fd6878048 Author: Povilas Kanapickas Date: 2019-08-25 23:24:19 +0300 genesys: Use the sensor passed to gl646_setup_registers() commit 1a02dfc8cb072bde4cde5f46ff297da0cacd623f Merge: ab9f5531c1f2 ceb56a8b3b5d Author: Povilas Kanapickas Date: 2019-08-31 15:44:23 +0000 Merge branch 'genesys-gl646-common-sensor-table' into 'master' genesys: Use common sensor table on gl646 See merge request sane-project/backends!141 commit ceb56a8b3b5d4ebf8515a2e2d4d496d9b9264344 Author: Povilas Kanapickas Date: 2019-08-17 12:02:45 +0300 genesys: Move gl646 sensor settings to the common sensor table commit a31da79e076155b1ea2d743b5f7f06687f4125db Author: Povilas Kanapickas Date: 2019-08-17 12:02:44 +0300 genesys: Compute cksel from register descriptions on gl646 commit ab9f5531c1f23e36bd891c90b32f5be442079b2c Merge: 0c6d7cd4a40d 5532f2f277f5 Author: Povilas Kanapickas Date: 2019-08-31 15:23:06 +0000 Merge branch 'genesys-identify-sensor-channel-count' into 'master' genesys: Identify sensor by supported channel count See merge request sane-project/backends!140 commit 5532f2f277f5fdaad17b870138555e125c89f415 Author: Povilas Kanapickas Date: 2019-08-17 11:51:35 +0300 genesys: Add channel count to the parameters that identify a sensor commit f2871ca81dd29512be9af19dcad5ad6e191f788e Author: Povilas Kanapickas Date: 2019-08-17 11:51:34 +0300 genesys: Reduce duplication in channel count and depth calculation commit 0c6d7cd4a40d29dfb9139cf0739cf6149dd739ef Merge: 3e2b6cead083 eb4fb4404868 Author: Povilas Kanapickas Date: 2019-08-31 14:56:23 +0000 Merge branch 'genesys-fix-missing-return-after-refactor' into 'master' genesys: Fix missing return from a previous refactor See merge request sane-project/backends!138 commit eb4fb440486846cdca3c14811c12c324f9ddb9ef Author: Povilas Kanapickas Date: 2019-08-17 11:30:33 +0300 genesys: Fix missing return from a previous refactor commit 3e2b6cead083f441ec68e32e62e31c1bd1acf80d Merge: 6216ada2e7ca d9fab3a7d8fd Author: Povilas Kanapickas Date: 2019-08-31 14:55:57 +0000 Merge branch 'genesys-gl646-simplify-sensor' into 'master' genesys: Simplify sensor setup on GL646 See merge request sane-project/backends!139 commit d9fab3a7d8fd4b5a9ce196f0125c1e6648eea13f Author: Povilas Kanapickas Date: 2019-08-17 11:49:10 +0300 genesys: Fix incorrect channel count when fetching ccd_size_divisor All current scanners use the same divisor on both 1 and 3 channels, so this commit does not change behavior. commit e8d9b6ff5dde80f69e80632ce715e0ad7e9902b6 Author: Povilas Kanapickas Date: 2019-08-17 11:49:09 +0300 genesys: Reduce duplication in the Sensor_Master struct list commit f228e427a958c4aa80d6c9fd4d35cdad20d557d1 Author: Povilas Kanapickas Date: 2019-08-17 11:49:08 +0300 genesys: Store sensor settings in std::vector on gl646 commit a8ed3df0fd486400d14927b5ec53684bcacacf1f Author: Povilas Kanapickas Date: 2019-08-17 11:49:07 +0300 genesys: Merge Sensor_Settings struct to Sensor_Master commit c001662c0506c9e6bc4a0ee94b31e8b515c8b5c8 Author: Povilas Kanapickas Date: 2019-08-17 11:49:06 +0300 genesys: Reorder the components on Sensor_Master struct commit 71a467ddf34d61f8abc9e7edef4898ad222490be Author: Povilas Kanapickas Date: 2019-08-17 11:49:05 +0300 genesys: Remove unused data from Sensor_Master on gl646 commit b19720cfa17047c2030225e59174dd1bace6eda8 Author: Povilas Kanapickas Date: 2019-08-17 11:49:04 +0300 genesys: Simplify sensor register desc by matching ccd_size_divisor commit e36d6b8fb5ad09ab1b70616b0a79223fb273f099 Author: Povilas Kanapickas Date: 2019-08-17 11:49:03 +0300 genesys: Simplify exposure setting on gl646 commit a4a1003ffc8e5e0133592e0830978597ac9932a7 Author: Povilas Kanapickas Date: 2019-08-17 11:49:02 +0300 genesys: Rename Sensor_Master::{exposure->exposure_lperiod} commit a3a3647006882f8671395d107df791064d9086f4 Author: Povilas Kanapickas Date: 2019-08-17 11:49:01 +0300 genesys: Compute dpiset instead of storing it in tables on gl646 commit d2d43d5ed8e564d74c2529c1fda12cc56c2cbd55 Author: Povilas Kanapickas Date: 2019-08-17 11:49:00 +0300 genesys: Make sensor table initialization more generic on gl646 commit 1330d2289ba081d1ac11e83ef5644332ba1e5884 Author: Povilas Kanapickas Date: 2019-08-17 11:48:59 +0300 genesys: Correctly initialize Sensor_Master struct commit 6216ada2e7cabb2620aa947980152476f224ffa8 Merge: 5b99a428d2cd 86ac877d8225 Author: Povilas Kanapickas Date: 2019-08-31 14:43:05 +0000 Merge branch 'genesys-fix-sensor-write' into 'master' genesys: Fix writing to sensors See merge request sane-project/backends!137 commit 86ac877d8225722fa2ec3fc52ad518a9c5f812e4 Author: Povilas Kanapickas Date: 2019-08-17 11:05:38 +0300 genesys: Remove unused code commit eb1c4fadb9497992691b4407c93cf77fa2a31512 Author: Povilas Kanapickas Date: 2019-08-17 11:05:37 +0300 genesys: Pick correct sensors during option parsing commit 1902c9fbcbb108e1fe7fa4588fa140ac29acfc84 Author: Povilas Kanapickas Date: 2019-08-17 11:05:36 +0300 genesys: Fix sensor const safety in *_search_start_position() commit f019f9e7b80f3a7c2aa6f4099e2fda1529b17365 Author: Povilas Kanapickas Date: 2019-08-17 11:05:35 +0300 genesys: Don't modify sensor in *_led_calibration() commit 5b99a428d2cdf45b35daa5ecbeb9eaddb30f2f0f Merge: 6e950209b688 0cd26a8adf3d Author: Povilas Kanapickas Date: 2019-08-31 14:39:01 +0000 Merge branch 'genesys-resolution-filter-class' into 'master' genesys: Extract resolution filtering to separate class See merge request sane-project/backends!136 commit 0cd26a8adf3d4a7190b8565897df236bfd466632 Author: Povilas Kanapickas Date: 2019-08-17 11:56:45 +0300 genesys: Extract resolution filtering to separate class commit 6e950209b688cd82dd6804d108ad2a4eb8b2b6ad Merge: c34cd7cd8661 107404dcd05e Author: Povilas Kanapickas Date: 2019-08-31 14:09:52 +0000 Merge branch 'genesys-session-ccd-size-divisor' into 'master' genesys: Use common code path for ccd_size_divisor computation See merge request sane-project/backends!135 commit 107404dcd05e442068f112e339bc996e8c82be7f Author: Povilas Kanapickas Date: 2019-08-17 11:11:15 +0300 genesys: Move ccd_size_divisor computation to compute_session() commit 92412a90b25f11a866f6c68c132a47edc7d9e459 Author: Povilas Kanapickas Date: 2019-08-17 11:11:14 +0300 genesys: Create compute_session() for common session computation commit c34cd7cd8661e7b046e1974c2f9eda55cadd0f42 Merge: 302932c1dbb5 53d447b6dcbb Author: Povilas Kanapickas Date: 2019-08-31 13:25:34 +0000 Merge branch 'genesys-common-sensor-profiles' into 'master' genesys: Use the sensor description list for sensor profiles See merge request sane-project/backends!134 commit 53d447b6dcbb8d4fc2cc4653888e4b36febb0b6a Author: Povilas Kanapickas Date: 2019-08-17 11:02:39 +0300 genesys: Move GL846 sensor profiles to a common sensor array commit 4978eceda330ae361e79d301f591c475fe6cf204 Author: Povilas Kanapickas Date: 2019-08-17 11:02:38 +0300 genesys: Move GL847 sensor profiles to a common sensor array commit 63cc11bce15428fd76918a6e91ece1d742949d00 Author: Povilas Kanapickas Date: 2019-08-17 11:02:37 +0300 genesys: Move GL124 sensor profiles to a common sensor array commit 4348451e47fdd1e006b121786e4f065710ef56f3 Author: Povilas Kanapickas Date: 2019-08-17 11:02:36 +0300 genesys: Add sensor profile to the sensor struct This will allow moving per-chip sensor profile tables to a common configuration commit ba546d5370e4df85dd6cd002c03f67e890b0bef9 Author: Povilas Kanapickas Date: 2019-08-17 11:02:35 +0300 genesys: Store segment order as a std::vector commit 302932c1dbb592797e15042c61c79eba5f91360c Merge: 4a9738b71958 7872f87475c6 Author: Povilas Kanapickas Date: 2019-08-31 13:20:00 +0000 Merge branch 'genesys-gl843-single-gpio-setting-list' into 'master' genesys: Move all gpio settings to a single struct on GL843 See merge request sane-project/backends!133 commit 7872f87475c6aeb1e6b7152247f7abf66e7eb802 Author: Povilas Kanapickas Date: 2019-08-17 10:38:58 +0300 genesys: Move all gpio settings to a single struct on GL843 commit 4a9738b71958e81704c5f257ef9eb82bc29520d5 Merge: 2896c21ffe9d 759e450b6b0a Author: Povilas Kanapickas Date: 2019-08-31 13:04:53 +0000 Merge branch 'genesys-simplify-reg-setting-set' into 'master' genesys: Reduce duplication in GenesysRegisterSettingSet See merge request sane-project/backends!132 commit 759e450b6b0aaf5d68456ec2397d5d73a426243c Author: Povilas Kanapickas Date: 2019-08-17 10:02:40 +0300 genesys: Reduce logical duplication in GenesysRegisterSettingSet commit 543ba367bcaeb2e9c28a79d8fabe83f89404319a Author: Povilas Kanapickas Date: 2019-08-17 10:02:39 +0300 genesys: Rename GenesysRegisterSettingSet::{regs_->registers_} commit 2896c21ffe9d75b0b0a36f25b7de273ef3172986 Merge: 562f900337be 8fce6b624c03 Author: Povilas Kanapickas Date: 2019-08-31 12:58:50 +0000 Merge branch 'genesys-frontend-type' into 'master' genesys: Add frontend type to the frontend description See merge request sane-project/backends!131 commit 8fce6b624c03db44b8cd5b1c56739f408b0802c3 Author: Povilas Kanapickas Date: 2019-08-17 10:27:42 +0300 genesys: Don't perform offset and gain calibration on unknown frontends commit 5580fc0db1c85902867761db90aeaf5575a194dc Author: Povilas Kanapickas Date: 2019-08-17 10:27:41 +0300 genesys: Add frontend type to the layout struct commit 562f900337be1a94eb980b960288cba83f7e2185 Merge: e452e869c267 a76a2a096e37 Author: Povilas Kanapickas Date: 2019-08-31 12:54:14 +0000 Merge branch 'genesys-override-dpiset' into 'master' genesys: Implement a way to override dpiset See merge request sane-project/backends!130 commit a76a2a096e377e887357ddf0754fea9205bc7ff6 Author: Povilas Kanapickas Date: 2019-08-17 10:21:04 +0300 genesys: Implement a way to override DPISET on GL124 and GL843 commit db15a755044cdc723ebbd4febd7b2fbcf3d75334 Author: Povilas Kanapickas Date: 2019-08-17 10:21:03 +0300 genesys: Add a way to override dpihw commit 458e31a0c0918a2a824fb878673a936b3859fe71 Author: Povilas Kanapickas Date: 2019-08-17 10:21:02 +0300 genesys: Extract setting of DPIHW to a separate function commit e452e869c267452756c11704adeffb058e371e9c Merge: c73a7b5b0601 503797c2480b Author: Povilas Kanapickas Date: 2019-08-31 12:46:46 +0000 Merge branch 'genesys-simplify-shading-calibration' into 'master' genesys: Simplify shading calibration See merge request sane-project/backends!129 commit 503797c2480b046aefe27043467ab000efd7d34e Author: Povilas Kanapickas Date: 2019-08-17 10:53:56 +0300 genesys: Simplify shading computation by using enum for color order commit 3a1163480a28214cb2f92c395e6b8a7b5dc96d86 Author: Povilas Kanapickas Date: 2019-08-17 10:53:55 +0300 genesys: Store calibration data using correct type internally commit c73a7b5b0601016ee5b75b1d962879891ad1d4fd Merge: e6b278638076 5e5a1a6fc3ab Author: Povilas Kanapickas Date: 2019-08-31 12:24:34 +0000 Merge branch 'genesys-fix-std-accumulate' into 'master' genesys: Use correct type for std::plus See merge request sane-project/backends!128 commit 5e5a1a6fc3abe9418090438b235ffa7b2f4f8dc2 Author: Povilas Kanapickas Date: 2019-08-31 15:11:40 +0300 genesys: Use correct type for std::plus commit e6b27863807654e24c0d2c6169bb506be68d4f2d Merge: b4c95a8ade91 f18ef0e282f1 Author: Stanislav Yuzvinsky Date: 2019-08-30 14:16:41 +0000 Merge branch 'macos-malloc-fix' into 'master' ricoh2_buffer: fix malloc header location on macOS See merge request sane-project/backends!90 commit f18ef0e282f1a5437adce032b735f47b00f91168 Author: Caleb Xu Date: 2019-08-01 23:51:20 -0400 ricoh2_buffer: fix malloc header location on macOS commit b4c95a8ade91463a9f2d5c9f51daad9f6548d0e5 Author: Rolf Bensch Date: 2019-08-20 13:30:25 +0200 pixma: backend version 0.23.1 commit f64489bd7179e148f50d9586be2c5f064a4c1804 Author: Rolf Bensch Date: 2019-08-20 13:16:32 +0200 pixma: fix ADF scanning for Canon MAXIFY MB2000 and MB2300 Series commit 8446baf4f9eb7f11d56b5b0771bc452dca7e6507 Author: Rolf Bensch Date: 2019-08-20 13:11:57 +0200 pixma: fix ADF scanning for Canon MAXIFY MB5000 Series commit 5e74e1b7940e32b2a6f387410755149d21672a4f Author: Olaf Meeuwissen Date: 2019-08-19 22:06:33 +0900 genesys: Make std::plus<> template type instantiation explicit This fixes compile errors on older versions of gcc and possibly other C++ compilers. commit f37326784dcf71e498383fbe3317e45b1f669caf Merge: 21bb96d61e5b 740f013292fd Author: Olaf Meeuwissen Date: 2019-08-12 09:16:37 +0000 Merge branch 'ci-tweaks' into 'master' CI tweaks See merge request sane-project/backends!125 commit 740f013292fdda3d45726a6a9595396f2d26c7f5 Author: Olaf Meeuwissen Date: 2019-08-12 18:01:35 +0900 CI: Document the various jobs commit 5aa84ed9ead0c1d24757ea18a7782bf6ce0a092b Author: Olaf Meeuwissen Date: 2019-08-12 17:39:54 +0900 CI: Drop Debian 9 mini build The "mini" build scenario is sufficiently covered by Debian 10 mini. Considering the Debian 9 is now oldstable, there is not much value in keeping it around. commit 69044bdffe6d6ff1b8ce8181abe75b4074eed006 Author: Olaf Meeuwissen Date: 2019-08-12 17:38:54 +0900 CI: Drop Fedora 29 compile job It's package version make up is too similar to Debian 10 for it to be worth the CPU cycles. commit 08bff060f8cbf12c4299f46eaf233522956893af Author: Olaf Meeuwissen Date: 2019-08-11 17:20:47 +0900 CI: Fix make distclean target Implicit compile dependencies prevent automake from adding dependency tracking files to the list of files to clean up. commit 1f1b9dcd56e67c9b7dac1164505f60859da7d076 Author: Olaf Meeuwissen Date: 2019-08-11 12:59:07 +0900 CI: Only keep snapshots for 90 days The snapshots aren't meant to stay around forever, that's for releases. commit 8cb53a191ea04f32075a447da26b72a6d3f390a6 Author: Olaf Meeuwissen Date: 2019-08-11 12:52:03 +0900 CI: Move after_script: from stretch to buster This follows c90e9674e74f15b0839ea944de47f887566d07ee. commit 14e7ba47dda2ee7389e8db21268cd49e80ea968a Author: Olaf Meeuwissen Date: 2019-08-11 12:49:19 +0900 CI: Speed up compile stage jobs commit 68d70def14542af1862eeb6edbd07c50c2626f95 Author: Olaf Meeuwissen Date: 2019-08-11 12:22:19 +0900 autofoo: Fix ltmain.sh.patch offsets to match new version commit 041ea5f53c3330b84a9462b42032783ed770b51d Author: Olaf Meeuwissen Date: 2019-08-11 12:10:07 +0900 japi: Fix BUILT_SOURCES dependency Newer automake versions have the dist target depend on BUILT_SOURCES so that variable should not heavy build dependencies. This fixes the dist target sufficiently, for CI purposes at least, in that it leads to inclusion of the same list of files. Note that CI does not build anything in the japi/ directory. commit c90e9674e74f15b0839ea944de47f887566d07ee Author: Olaf Meeuwissen Date: 2019-08-11 12:08:50 +0900 CI: Build our source tarball on Debian's Buster This bumps the build distribution's version, up from Stretch. commit 6fdbc95bf3386492f0787e730a2404c222ab08ba Author: Olaf Meeuwissen Date: 2019-08-06 22:36:57 +0900 CI: Fix broken dependency after job renames commit 2874d215146924b2cf8aab279c770a41c1aeae76 Author: Olaf Meeuwissen Date: 2019-08-06 21:42:08 +0900 CI: Rename stages and jobs for clarity It drops the common `compile:` prefix because the web UI ellipsizes job names. This leads to names that are hard to tell apart when the prefix is applied. The archive stage has been renamed to snapshot to differentiate it more from the release stage. The prepare moniker did not clearly describe the stage's intent and has been renamed to tarball. The job names for these stages now reflect the `make` invocations used in an attempt to be more descriptive. commit 21bb96d61e5b9f1507f6d973c51230ce7b591d78 Merge: f80cb86a1f67 5cba3bd465f1 Author: Olaf Meeuwissen Date: 2019-08-11 01:48:16 +0000 Merge branch 'fix-warnings' into 'master' Fix -Wimplicit-fallthrough warnings See merge request sane-project/backends!85 commit 5cba3bd465f128784306214f07c9ee334f723518 Author: Olaf Meeuwissen Date: 2019-08-11 10:37:10 +0900 saned: Fix misleading log messages caused by fall through commit e42e3bc849ab98a45c27b30c4a44fe10cdf462a3 Author: Olaf Meeuwissen Date: 2019-08-11 10:23:29 +0900 scanimage: Fix misleading fall through annotation The `scanimage_exit()` call exits the program. commit 9344a957ed2e67cf1cd285eac66ed7dd159d8d13 Author: Olaf Meeuwissen Date: 2019-08-11 10:16:00 +0900 apple: Fix [-Wimplicit-fallthrough] compiler warning It doesn't make sense to log a value as both `Fixed` *and* `Int`. commit 797cd1fb37ee87185ad79fd4bf87648b33599e83 Author: Olaf Meeuwissen Date: 2019-08-10 21:05:48 +0900 epson2, epsonds, kodakaio, magicolor: Don't set invalid value If the value to be set is not valid, it should not change the sval structure (even if doing so has no negative side effects). This also makes the fallthrough less confusing by removing the first cast of two for the br-x and br-y values. commit fcd537e2077eea1c8e00a1875a7fed56852bfb9b Author: Olaf Meeuwissen Date: 2019-08-10 20:49:09 +0900 sm3600: Fix [-Wimplicit-fallthrough] warning This fixes the warning for both #define SM3600_SUPPORT_EXPOSURE values. commit 3dde8f28f094f8440050c773720312383ea73933 Author: Povilas Kanapickas Date: 2019-06-30 11:58:33 +0300 niash: Fix an implicit fallthrough bug in handling of threshold option commit e40596c2a59e188a0a44f44877aa91e41a5698aa Author: Povilas Kanapickas Date: 2019-06-30 11:58:32 +0300 Fix -Wimplicit-fallthrough warnings by adding standard annotations commit f80cb86a1f677f32a9f612db0fbc99d73bf01247 Merge: 7159bbee3296 6912400cf48a Author: Povilas Kanapickas Date: 2019-08-10 15:53:39 +0000 Merge branch 'genesys-split-tables' into 'master' genesys: Split tables into separate files See merge request sane-project/backends!124 commit 6912400cf48ab027dd17569003d9385fd161a5f0 Author: Povilas Kanapickas Date: 2019-08-04 18:21:08 +0300 genesys: Move motor profile tables to a single file commit aa4ee9c49e7e603c283ca48d41e75f979550c470 Author: Povilas Kanapickas Date: 2019-08-04 18:21:07 +0300 genesys: Split genesys_devices.cc file to separate files for each table commit 6592692448f8b4f35e2eb8fc9d6e31375a6a4ae3 Author: Povilas Kanapickas Date: 2019-08-04 18:21:06 +0300 genesys: Compile genesys_devices.cc as a separate translation unit commit 7159bbee3296fbe13e82293e8fd1e3fb61edd31f Merge: 8a48e8774109 8cb6130cfe7d Author: Povilas Kanapickas Date: 2019-08-10 15:33:38 +0000 Merge branch 'genesys-cleanup-gpo' into 'master' genesys: Cleanup the gpo setup See merge request sane-project/backends!123 commit 8cb6130cfe7d08a4c8bea6d6d8cb528aa1039c44 Author: Povilas Kanapickas Date: 2019-08-04 14:01:50 +0300 genesys: Cleanup the gpo setup commit 8a48e8774109d62642404729ee5917331188e45e Merge: b5ff3b4f4372 ca54462bd287 Author: Povilas Kanapickas Date: 2019-08-10 12:28:50 +0000 Merge branch 'genesys-simplify-gl843-dpi' into 'master' genesys: Simplify GL843 DPI calculation See merge request sane-project/backends!122 commit ca54462bd287cbf8b48d04e703cddac18f8ffc37 Author: Povilas Kanapickas Date: 2019-08-04 13:17:52 +0300 genesys: Simplify shading upload on gl843 commit 2df86f551f128248550485ec26dbe07f6fdb64dc Author: Povilas Kanapickas Date: 2019-08-04 13:17:51 +0300 genesys: Fix incorrect dpi computation for G4050 commit d365637dfc7687313c39375d4140462875e95ce9 Author: Povilas Kanapickas Date: 2019-08-04 13:17:50 +0300 genesys: Simplify optical regs setup on gl843 commit 3e09ba18fa4e7e5c7818faeb6e85b9a354548d5f Author: Povilas Kanapickas Date: 2019-08-04 13:17:49 +0300 genesys: Move pixel coordinate calculation to gl843_compute_session() commit 6a0f17cecca0536c5689aafaaee46bf658b2d5b0 Author: Povilas Kanapickas Date: 2019-08-04 13:17:48 +0300 genesys: Raise an exception if input resolution is out of bounds commit b5ff3b4f43722cf8cee420d824daa5a979ae1071 Merge: 608680f8b80f ae525fb91b69 Author: Povilas Kanapickas Date: 2019-08-10 12:23:20 +0000 Merge branch 'genesys-cleanup-motors' into 'master' genesys: Cleanup motor definitions See merge request sane-project/backends!121 commit ae525fb91b69e8956298d9a3b0cc59667f5464b2 Author: Povilas Kanapickas Date: 2019-08-10 15:10:31 +0300 genesys: Remove trailing whitespace commit 1c61d3692a236d3d86903c1d6f00ca72e004d3b6 Author: Povilas Kanapickas Date: 2019-08-04 12:01:52 +0300 genesys: Rewrap extremely long lines in motor step definitions commit ea602f7ebc38d84858d18727621af91d6748b667 Author: Povilas Kanapickas Date: 2019-08-04 12:01:51 +0300 genesys: Rewrite and document sanei_genesys_calculate_zmod() commit 6f20e6d341c67fe394c860de716d2771f97e81ca Author: Povilas Kanapickas Date: 2019-08-04 12:01:50 +0300 genesys: Use std::vector for the motor slope tables commit 510e8123f3826938cfe3fdda68d4eef0a9519976 Author: Povilas Kanapickas Date: 2019-08-04 12:01:49 +0300 genesys: Remove support for multiple motor power modes commit 239eab6ea661654fc7134684b8c8bb07b2478fc6 Author: Povilas Kanapickas Date: 2019-08-04 12:01:48 +0300 genesys: Simplify motor setup to make it easier to expand commit 608680f8b80fc7b8f684beeac6568da3673be47e Merge: 7d6897ecb4bd b85a83f6af03 Author: Povilas Kanapickas Date: 2019-08-10 12:10:27 +0000 Merge branch 'genesys-remove-optical-flags' into 'master' genesys: Remove optical flags See merge request sane-project/backends!120 commit b85a83f6af03b5300a1808cef0b68330c70f1d9a Author: Povilas Kanapickas Date: 2019-08-04 10:52:58 +0300 genesys: Remove no longer used optical flags variables commit 306b160f00661f488337b906e5a83eda67f6eeaa Author: Povilas Kanapickas Date: 2019-08-04 10:52:57 +0300 genesys: Pass OPTICAL_FLAG_DISABLE_SHADING flag via session commit 86d4828ece1db3e42cef876c344bd7d5b91cd010 Author: Povilas Kanapickas Date: 2019-08-04 10:52:56 +0300 genesys: Pass OPTICAL_FLAG_DISABLE_GAMMA flag via session commit d503701fb754c9d152866440f7382aa6f9fd650e Author: Povilas Kanapickas Date: 2019-08-04 10:52:55 +0300 genesys: Pass OPTICAL_FLAG_USE_XPA flag via session commit 773170af094193209992739a07c9f220f25a4290 Author: Povilas Kanapickas Date: 2019-08-04 10:52:54 +0300 genesys: Pass OPTICAL_FLAG_DISABLE_LAMP flag via session commit 8618a49182ea59bd6d25e282f877437d5d3d7f63 Author: Povilas Kanapickas Date: 2019-08-04 10:52:53 +0300 genesys: Pass OPTICAL_FLAG_STAGGER flag via session commit a4b551be0c07f26b54c1cda8c631f920dcc9eac9 Author: Povilas Kanapickas Date: 2019-08-04 10:52:52 +0300 genesys: Pass OPTICAL_FLAG_ENABLE_LEDADD flag via session commit 7d6897ecb4bd23e90bf106aa0d8f945a2157cce3 Merge: 6bf49297c215 e5a90e800537 Author: Povilas Kanapickas Date: 2019-08-10 11:55:49 +0000 Merge branch 'genesys-fix-search-reference-point' into 'master' genesys: Fix search reference point to update all sensors See merge request sane-project/backends!118 commit e5a90e8005375577fc795f3f71c111e2467c70a8 Author: Povilas Kanapickas Date: 2019-08-04 02:48:29 +0300 genesys: Update all sensors when searching for reference point commit 91b20fb268e649249ce062cbca7afc835874a5d0 Author: Povilas Kanapickas Date: 2019-08-04 02:48:28 +0300 genesys: Make sanei_genesys_search_reference_point() non-destructive commit d0dbfa6ec33c04e3f1973b478ce912966411deb3 Author: Povilas Kanapickas Date: 2019-08-04 02:48:27 +0300 genesys: Print traces of more data commit 824f922e49de40fe4bb4cac263732c6ad09c0ac9 Author: Povilas Kanapickas Date: 2019-08-04 02:48:26 +0300 genesys: Don't do additional scans in case of debug commit 6bf49297c21538462323efef2d27c2d68475e1f9 Merge: 8897f787535b e733f669601f Author: Povilas Kanapickas Date: 2019-08-10 11:55:03 +0000 Merge branch 'genesys-cleanup' into 'master' genesys: Miscellaneous cleanups and refactors See merge request sane-project/backends!119 commit e733f669601f0ae2184680107e46ec70b3b19d02 Author: Povilas Kanapickas Date: 2019-08-04 01:11:08 +0300 genesys: Add a way to customize per-sensor divisor management commit ea78c00882a1db9322062d4efa271d5ecaedebff Author: Povilas Kanapickas Date: 2019-08-04 01:11:07 +0300 genesys: Fix duplicate hwdpi conversion on gl124 commit 1ba4018a6e71090d5d92c8ba30f0987442b0056d Author: Povilas Kanapickas Date: 2019-08-04 01:11:06 +0300 genesys: Pass session directly to gl843_init_optical_regs_scan() commit 8897f787535bca06de3c9d40cbd07639dba05f06 Merge: a18ec0faac2c cafb97d2935a Author: Povilas Kanapickas Date: 2019-08-10 11:40:53 +0000 Merge branch 'genesys-session-settings' into 'master' genesys: Use session struct to pass data to the setup functions See merge request sane-project/backends!117 commit cafb97d2935a58d86976cbd7c73d57c1849cc2ff Author: Povilas Kanapickas Date: 2019-08-03 22:42:20 +0300 genesys: Use session struct to pass data to the setup functions commit a18ec0faac2c1cf7fe26d780dacaeb116b87e806 Merge: 0df7e1ab5021 38b97f1402e9 Author: Povilas Kanapickas Date: 2019-08-10 11:19:29 +0000 Merge branch 'genesys-session-settings' into 'master' genesys: Session settings refactor See merge request sane-project/backends!116 commit 38b97f1402e9def3ce0536e187cb439661781e53 Author: Povilas Kanapickas Date: 2019-07-27 05:12:31 +0300 genesys: Include full session description into the device struct commit f4401d501fb28da8045bfa6b3b2d42ff89cf56bd Author: Povilas Kanapickas Date: 2019-07-27 05:12:30 +0300 genesys: Make asic type an enum commit e34814aef85d11fe09d0fd86745fc1717d86d9ae Author: Povilas Kanapickas Date: 2019-07-27 05:12:29 +0300 genesys: Remove duplicate fields out of settings struct commit 808cc5737d4abe5d229cf31c767f7f25fd15a7d1 Author: Povilas Kanapickas Date: 2019-07-27 05:12:28 +0300 genesys: Move ScanSession to genesys_settings.h commit d0ed3795f625e39f56f2d4ad2e316fe911a62805 Author: Povilas Kanapickas Date: 2019-07-27 05:12:27 +0300 genesys: Remove unused code commit 0df7e1ab5021e5e79ee22d90443dc220763bf0ce Merge: 6ef288855ed5 408469d94e67 Author: Olaf Meeuwissen Date: 2019-08-10 07:08:56 +0000 Merge branch 'patch-1' into 'master' Update hp3900_rts8822.c See merge request sane-project/backends!92 commit 408469d94e67725fb3f9a87f4e1a5527fdca49bf Author: Cor Date: 2019-08-08 14:58:09 +0000 Update hp3900_rts8822.c After increase the timeout from 10 to 60 seconds, the hp3970 can scan black and white 35mm films using the parameters Gray, 16 bit and 2400 dpi. More information in https://gitlab.com/sane-project/backends/issues/77 commit 6ef288855ed5e0068902ce9cb2772b8ed6a32158 Merge: 1267a9a5cc09 d2653efbd9db Author: Olaf Meeuwissen Date: 2019-08-10 06:06:38 +0000 Merge branch 'sanei-usb-capture-debug-msg' into 'master' sanei_usb: Add a way to record debug messages to USB captures See merge request sane-project/backends!93 commit d2653efbd9dbe8b8f6611c10209864eeb5c5be58 Author: Povilas Kanapickas Date: 2019-08-08 20:51:24 +0300 sanei_usb: Add a way to record debug messages to USB captures commit 1267a9a5cc09c424cc2f1f95c7d5ffb7a33a8785 Merge: a2085a973ff1 df580b9480ea Author: Rolf Bensch Date: 2019-08-09 20:18:55 +0200 Merge remote-tracking branch 'origin/master' commit df580b9480eaacf02348742df064ab22daebf100 Merge: 9085260541c2 cdb14d9fef9e Author: Povilas Kanapickas Date: 2019-08-09 11:10:43 +0000 Merge branch 'genesys-simplify-register-set-access' into 'master' genesys: Simplify register set access See merge request sane-project/backends!115 commit cdb14d9fef9e2f9137c9be6cb210649b516a2760 Author: Povilas Kanapickas Date: 2019-07-21 18:41:50 +0300 genesys: Remove uses of sanei_genesys_set_reg_from_set() commit 36714a781044f431f078fc269ad21a41f2a0b643 Author: Povilas Kanapickas Date: 2019-07-21 18:41:49 +0300 genesys: Remove uses of sanei_genesys_read_reg_from_set() commit e6dbfd64a352e8d7702d73f5be3e1a4465a4b871 Author: Povilas Kanapickas Date: 2019-07-21 18:41:48 +0300 genesys: Remove uses of sanei_genesys_set_triple() commit 08c98e13b2c59bf192f122a8714363bd6a4a2be6 Author: Povilas Kanapickas Date: 2019-07-21 18:41:47 +0300 genesys: Remove uses of sanei_genesys_set_double() commit 98546ecff05239ab1c864b246092312d499e09e0 Author: Povilas Kanapickas Date: 2019-07-21 18:41:46 +0300 genesys: Remove uses of sanei_genesys_get_triple() commit fa9fcc31a13cc0400cacb5c5cbf01683af6c98da Author: Povilas Kanapickas Date: 2019-07-21 18:41:45 +0300 genesys: Remove uses of sanei_genesys_get_double() commit 9085260541c288e765af7183ab2abf3826e66a6a Merge: 041389e57f74 598aa99778fc Author: Povilas Kanapickas Date: 2019-08-09 10:51:06 +0000 Merge branch 'genesys-remove-half-ccd' into 'master' genesys: Improve code clarity by retiring half_ccd See merge request sane-project/backends!114 commit 598aa99778fc178fd6dd62f8aa42deca31f0cb66 Author: Povilas Kanapickas Date: 2019-08-03 01:31:07 +0300 genesys: Improve code clarity by retiring half_ccd commit 041389e57f748ce6e38f11c2db836e3cc12b7f0c Merge: 035951933b19 16c55bf330f8 Author: Povilas Kanapickas Date: 2019-08-09 10:46:36 +0000 Merge branch 'genesys-cleanup-cmd-set' into 'master' genesys: Cleanup cmd_set implementation See merge request sane-project/backends!113 commit 16c55bf330f80db64f886cd5bc9ceda588e00ba0 Author: Povilas Kanapickas Date: 2019-08-02 22:39:05 +0300 genesys: Init cmd_set directly instead of going through a function commit a58b20d2c8663ae75356663b68a1734936f4d0dd Author: Povilas Kanapickas Date: 2019-08-02 22:39:04 +0300 genesys: Move cmd_set from model to device struct commit 035951933b19472f0f548afed4cd8df113db847b Merge: a3e492d6d59a f9a9d3f84a45 Author: Povilas Kanapickas Date: 2019-08-09 10:28:10 +0000 Merge branch 'genesys-physical-register-state' into 'master' genesys: Cache physical register state See merge request sane-project/backends!111 commit f9a9d3f84a45d58b2e4d94901ff74c1b0597787c Author: Povilas Kanapickas Date: 2019-08-02 22:39:01 +0300 genesys: Cache physical register state in the device on writes commit faab7b02e0ee513834d35e313f7186245b2584a1 Author: Povilas Kanapickas Date: 2019-08-02 22:39:00 +0300 genesys: Cache physical register state in the device on bulk writes commit 7836ea5778cbaba908a6cdb27014e723fdce8e6c Author: Povilas Kanapickas Date: 2019-08-02 22:38:59 +0300 genesys: Remove bulk_write_register from the cmd_set structures commit 60146456640985f46c8319f8acdc2f535f66e9cb Author: Povilas Kanapickas Date: 2019-08-02 22:38:58 +0300 genesys: Cache physical register state in the device on reads commit a3e492d6d59a75e869d8f35122a2d1044d4dc579 Merge: ccd40d396980 f9b87de44a6a Author: Povilas Kanapickas Date: 2019-08-09 10:23:15 +0000 Merge branch 'genesys-remove-manual-alloc' into 'master' genesys: Remove manual allocations See merge request sane-project/backends!112 commit f9b87de44a6ab4ae74c10be6eb242798a2b88fc3 Author: Povilas Kanapickas Date: 2019-08-02 22:38:54 +0300 genesys: Remove manual allocations from calibration functions commit 6b2919db9a14d6b7ddffb7fe504060a3eec9df65 Author: Povilas Kanapickas Date: 2019-08-02 22:38:53 +0300 genesys: Remove manual allocations in device class commit ccd40d3969806c67b973a7e7466c76690de395c0 Merge: 5bd9ab4941f7 fc9159b4075c Author: Povilas Kanapickas Date: 2019-08-09 10:15:53 +0000 Merge branch 'genesys-improve-readability-model-struct' into 'master' genesys: Improve readability of model struct setup See merge request sane-project/backends!110 commit fc9159b4075ca16ae41090f55ca785757762b65b Author: Povilas Kanapickas Date: 2019-08-02 22:36:12 +0300 genesys: Improve readability of model struct setup commit 5bd9ab4941f749047944e2748f9c711cf047b17d Merge: 239147d4c71a 49b94dc09e1d Author: Povilas Kanapickas Date: 2019-08-09 09:57:02 +0000 Merge branch 'genesys-split-genesys-low' into 'master' genesys: Split genesys_low.h into several files See merge request sane-project/backends!109 commit 49b94dc09e1dd2c9af99e85d88a0a5dacd1b9f2a Author: Povilas Kanapickas Date: 2019-08-02 22:35:16 +0300 genesys: Split genesys_low.h into several files commit 239147d4c71ae2d39798172e2752318bf9c228bf Merge: c4468f998ace 3c6635559f32 Author: Povilas Kanapickas Date: 2019-08-09 08:45:38 +0000 Merge branch 'genesys-cleanup' into 'master' genesys: Cleanups See merge request sane-project/backends!106 commit 3c6635559f3221b6c839532ceeb18121a76002a0 Author: Povilas Kanapickas Date: 2019-08-02 22:31:58 +0300 genesys: Remove unused code commit 13b2d09a42e22e35993cad5e170310d0b81c07ea Author: Povilas Kanapickas Date: 2019-08-02 22:31:57 +0300 genesys: Fix printing of errors on exceptions commit 025ec317e32dba0b269a5f5e9dea0e056b6d40b1 Author: Povilas Kanapickas Date: 2019-08-02 22:31:56 +0300 genesys: Move logical and register dpi computation to sensor struct commit c4468f998acedb2ebf6b80aa37171435c15ce1c5 Merge: ebac8aee75bc faa078cd213a Author: Povilas Kanapickas Date: 2019-08-09 08:26:52 +0000 Merge branch 'genesys-return-errors-as-exceptions' into 'master' genesys: Return errors as exceptions instead of error codes See merge request sane-project/backends!105 commit faa078cd213a2c27f43a4c52599b65302d1f49f2 Author: Povilas Kanapickas Date: 2019-07-27 10:12:36 +0300 genesys: Return errors as exceptions from remaining functions commit c0360248108e39604a0a0378a62b60e1a5641869 Author: Povilas Kanapickas Date: 2019-07-27 10:12:35 +0300 genesys: Return errors as exceptions from genesys_buffer_image() commit 748acbbb818919ed78b7084c5a287e5fef108442 Author: Povilas Kanapickas Date: 2019-07-27 10:12:34 +0300 genesys: Simplify code by removing uses of RIE() macro commit c6bbc5f7739711100ed6ec3a4a861c14af8fd2ff Author: Povilas Kanapickas Date: 2019-07-27 10:12:33 +0300 genesys: Return errors as exceptions from genesys_read_ordered_data() commit 1c030da1c29963ad815db4c4a5e2ea6e28092af1 Author: Povilas Kanapickas Date: 2019-07-27 10:12:32 +0300 genesys: Return errors as exceptions from image manipulation functions commit 39e5daa7b452b422b5135e7016ea6582fc1950f2 Author: Povilas Kanapickas Date: 2019-07-27 10:12:31 +0300 genesys: Remove unused code commit db2bb793d4a25aa900117d82bb15c9ac2a97b4f0 Author: Povilas Kanapickas Date: 2019-07-27 10:12:30 +0300 genesys: Remove obsolete comments commit 107019c5de73d3efc7f23285b4ce3dd2ca32d3bb Author: Povilas Kanapickas Date: 2019-07-27 10:12:29 +0300 genesys: Return errors as exceptions from init_options() commit 1fd16cd16bd61b2d578cb4ccbcaee6b5517e8406 Author: Povilas Kanapickas Date: 2019-07-27 10:12:28 +0300 genesys: Return errors as exceptions from calc_parameters() commit 10947b94f115ccb0016408ba4a28bd2a534d56f2 Author: Povilas Kanapickas Date: 2019-07-27 10:12:27 +0300 genesys: Return errors as exceptions from pixel format conv functions commit f727f1a01ba2d2e630c4f1c878290f712667ec81 Author: Povilas Kanapickas Date: 2019-07-27 10:12:26 +0300 genesys: Return errors as exceptions from genesys_fill_read_buffer() commit a6855efb575e6fb40413d6f36c9c33413f48ac05 Author: Povilas Kanapickas Date: 2019-07-27 10:12:25 +0300 genesys: Return errors as exceptions from genesys_fill_segmented_buffer() commit a18ef2e17ffd0963b12e10caa8f64d70ddb89ef9 Author: Povilas Kanapickas Date: 2019-07-27 10:12:24 +0300 genesys: Return errors as exceptions from genesys_fill_line_interp_buffer() commit a2085a973ff1fcdf5e0a3021eb819710d331c9a5 Author: Rolf Bensch Date: 2019-08-09 20:16:59 +0200 po/de.po: add/fix some German translations commit 97dd074595622948e456a9d8f20fb8457bd39b19 Author: Rolf Bensch Date: 2019-08-09 20:11:05 +0200 po/de.po: recreate German translations file commit 453b48180d22012b65fa9712be47d24c3e75cf93 Author: Rolf Bensch Date: 2019-08-09 19:51:18 +0200 pixma: Canon PIXMA TS6200 Series is working (#109) commit ebac8aee75bc231a9b6975562e1208100ba86fcd Merge: 153602308cd8 c6dacdf01490 Author: Povilas Kanapickas Date: 2019-08-09 07:51:05 +0000 Merge branch 'genesys-return-errors-as-exceptions' into 'master' genesys: Return errors as exceptions instead of error codes See merge request sane-project/backends!104 commit c6dacdf0149063057dc9446abd9393155d86df4b Author: Povilas Kanapickas Date: 2019-07-20 11:02:41 +0300 genesys: Return errors as exceptions from accurate_line_read() commit 53450b681051b5c71746af5d4bfeb53f7c25d5df Author: Povilas Kanapickas Date: 2019-07-20 11:02:40 +0300 genesys: Return errors as exceptions from genesys_start_scan() commit 1ad6aca5018dd8c5f204f6db9bafd5ceef701095 Author: Povilas Kanapickas Date: 2019-07-20 11:02:39 +0300 genesys: Return errors as exceptions from genesys_load_lut() commit 7c70d30f63e62f23c69845e2c2a1354bc041c831 Author: Povilas Kanapickas Date: 2019-07-20 11:02:38 +0300 genesys: Return errors as exceptions from genesys_*_calibration() commit b41ad140ad64c878342edb07136b5b6ff88534bf Author: Povilas Kanapickas Date: 2019-07-20 11:02:37 +0300 genesys: Return errors as exceptions from *genesys_warmup_lamp() commit fa07b4806c08af6f2dfbb5a45bdf4d9fc868b532 Author: Povilas Kanapickas Date: 2019-07-20 11:02:36 +0300 genesys: Return errors as exceptions from genesys_save_calibration() commit e3d4b8fb9138c7c08ad6fade46c79330f0fb399a Author: Povilas Kanapickas Date: 2019-07-20 11:02:35 +0300 genesys: Return errors as exceptions from genesys_send_shading_coefficient() commit 82afcfae2d9e6fd76b904cf06f84ac6f2001a27e Author: Povilas Kanapickas Date: 2019-07-20 11:02:34 +0300 genesys: Return errors as exceptions from genesys_*_shading_calibration() commit edb442afe8b9f2890295c70e772b078b5113b0ae Author: Povilas Kanapickas Date: 2019-07-20 11:02:33 +0300 genesys: Return errors as exceptions from genesys_dummy_dark_shading() commit cd87e4e6626a33feacaa45cdd389a78301a83539 Author: Povilas Kanapickas Date: 2019-07-20 11:02:32 +0300 genesys: Return errors as exceptions from genesys_average_white() commit 05a9a0837458cf62e38626381b70d1b811344d14 Author: Povilas Kanapickas Date: 2019-07-20 11:02:31 +0300 genesys: Return errors as exceptions from *rewind() commit 579df0d0b75c6519982e19466aa1e55069bf4648 Author: Povilas Kanapickas Date: 2019-07-20 11:02:30 +0300 genesys: Return errors as exceptions from *move_to_ta() commit 92d1817c2454a528c2f5b714f7b0d3193d51f838 Author: Povilas Kanapickas Date: 2019-07-20 11:02:29 +0300 genesys: Return errors as exceptions from *search_strip() commit 4247ced84ae6a3315a9c24a4b19e97de71d2ec3b Author: Povilas Kanapickas Date: 2019-07-20 11:02:28 +0300 genesys: Return errors as exceptions from *detect_document_end() commit 3862f53f24c06988a0be6b60931053b761279085 Author: Povilas Kanapickas Date: 2019-07-20 11:02:27 +0300 genesys: Return errors as exceptions from *load_document() commit 24625dcc2ca173728f89b037d19c8e4393da6c75 Author: Povilas Kanapickas Date: 2019-07-20 11:02:26 +0300 genesys: Return errors as exceptions from *update_hardware_sensors() commit 0db604d4dd2113639eb9a2a82cfc63bbfc6f05fc Author: Povilas Kanapickas Date: 2019-07-20 11:02:25 +0300 genesys: Return errors as exceptions from *led_calibration() commit dd29e02457bbaf5b10b539d866fb8a318e8f0ab3 Author: Povilas Kanapickas Date: 2019-07-20 11:02:24 +0300 genesys: Return errors as exceptions from *coarse_gain_calibration() commit faea78eae6ea9b1f491e5e327117754dfb5c0351 Author: Povilas Kanapickas Date: 2019-07-20 11:02:23 +0300 genesys: Return errors as exceptions from *offset_calibration() commit 1eaa88963285026bb7b3be57efeac4688b4acee1 Author: Povilas Kanapickas Date: 2019-07-20 11:02:22 +0300 genesys: Return errors as exceptions from *search_start_position() commit d46d5cd0277dcc3be13bde79c2fededca2bf9a6f Author: Povilas Kanapickas Date: 2019-07-20 11:02:21 +0300 genesys: Return errors as exceptions from *init_regs_for_scan() commit 8837f8adcc55d65bd093c560851a553d8d53c087 Author: Povilas Kanapickas Date: 2019-07-20 11:02:20 +0300 genesys: Return errors as exceptions from *init_regs_for_shading() commit 61f74af2498081206cef56733a71a71686d2441e Author: Povilas Kanapickas Date: 2019-07-20 11:02:19 +0300 genesys: Return errors as exceptions from *init_regs_for_warmup() commit 75144683328632702bfd6d0cb63a4a6b22e870b0 Author: Povilas Kanapickas Date: 2019-07-20 11:02:18 +0300 genesys: Return errors as exceptions from *coarse_calibration() commit 88e7268bbe0525388bad97b3d350d65d2d029195 Author: Povilas Kanapickas Date: 2019-07-20 11:02:17 +0300 genesys: Return errors as exceptions from *for_coarse_calibration() commit 7019901aa57ca630fd0ac09f29275a802e1eae97 Author: Povilas Kanapickas Date: 2019-07-20 11:02:16 +0300 genesys: Return errors as exceptions from *simple_scan() commit 94abc7f964d1b97ce7bfd2d26cdea8da099d91c4 Author: Povilas Kanapickas Date: 2019-07-20 11:02:15 +0300 genesys: Return errors as exceptions from *read_data_from_scanner() commit 867d907bb9fed29cee3408ae5d4b8ed4c66ad0dc Author: Povilas Kanapickas Date: 2019-07-20 11:02:14 +0300 genesys: Return errors as exceptions from *init() commit 67f187bceb609d3981299edcb7f4ae5023bd91f6 Author: Povilas Kanapickas Date: 2019-07-20 11:02:13 +0300 genesys: Return errors as exceptions from *end_scan() commit eaeb05ead5ab691f2a40e4f682442487814506d7 Author: Povilas Kanapickas Date: 2019-07-20 11:02:12 +0300 genesys: Return errors as exceptions from *search_reference_point() commit 196e0824549b84c9c1a186050987e49c39077bca Author: Povilas Kanapickas Date: 2019-07-20 11:02:11 +0300 genesys: Return errors as exceptions from *write_file() commit 241a4ea913c5796506c807b3f469c1eb919c2f61 Author: Povilas Kanapickas Date: 2019-07-20 11:02:10 +0300 genesys: Return errors as exceptions from *write_pnm_file() commit a6cbb980699e385168f954d665bdce1d41c054cf Author: Povilas Kanapickas Date: 2019-07-20 11:02:09 +0300 genesys: Return errors as exceptions from *eject_document() commit 0bc253de07abc3e2348b16dcd1a105e4648dfd4c Author: Povilas Kanapickas Date: 2019-07-20 11:02:08 +0300 genesys: Return errors as exceptions from *start_motor() commit 3477b827ee178f22098da4b8a9389f2119eae091 Author: Povilas Kanapickas Date: 2019-07-20 11:02:07 +0300 genesys: Return errors as exceptions from *get_paper_sensor() commit 5d2d8e9509f5459fba74fca0409ac7738a4c25b3 Author: Povilas Kanapickas Date: 2019-07-20 11:02:06 +0300 genesys: Return errors as exceptions from *send_gamma_table() commit e614c9e102629bcf34547eee2082dcfbd384adf6 Author: Povilas Kanapickas Date: 2019-07-20 11:02:05 +0300 genesys: Return errors as exceptions from *repark_head() commit d8d1638fdedec78832cb926bce56ea1614843a64 Author: Povilas Kanapickas Date: 2019-07-20 11:02:04 +0300 genesys: Return errors as exceptions from *asic_test() commit a0aa335d90cb74fcfbf4c9817f5219d2cb0a7901 Author: Povilas Kanapickas Date: 2019-07-20 11:02:03 +0300 genesys: Return errors as exceptions from *set_powersaving() commit fd12b4052f9933247288474ea3f3f5d5016740e3 Author: Povilas Kanapickas Date: 2019-07-20 11:02:02 +0300 genesys: Return errors as exceptions from *save_power() commit d75aa89c9f12502cf30402da84a72c495db0be27 Author: Povilas Kanapickas Date: 2019-07-20 11:02:01 +0300 genesys: Return errors as exceptions from *slow_back_home() commit 075d733613bbe0ee4ba82f5267d5367dcb241eea Author: Povilas Kanapickas Date: 2019-07-20 11:02:00 +0300 genesys: Return errors as exceptions from *feed() commit 104ad9b44877563718d93023ef2e42b418d623af Author: Povilas Kanapickas Date: 2019-07-20 11:01:59 +0300 genesys: Return errors as exceptions from setup_for_scan() commit 32a3faa3b56635fe54b24b452bc604c0e69e0ab8 Author: Povilas Kanapickas Date: 2019-07-20 11:01:58 +0300 genesys: Return errors as exceptions from *setup_registers() and deps commit 06367877202dfff9a1f92302c9c3c04febe5b8f4 Author: Povilas Kanapickas Date: 2019-07-20 11:01:57 +0300 genesys: Return errors as exceptions from *stop_motor() commit e5e9b5dc0d5797fb1e387e8dd3eeb8d942a06a83 Author: Povilas Kanapickas Date: 2019-07-20 11:01:56 +0300 genesys: Return errors as exceptions from *begin_scan() commit 02efbdee43054a02c21ebf2640486ab04fcb3166 Author: Povilas Kanapickas Date: 2019-07-20 11:01:55 +0300 genesys: Return errors as exceptions from *xpa_motor_power() commit 8a3b5a88a5ee356fb95402ac5c6f69656c6c9c80 Author: Povilas Kanapickas Date: 2019-07-20 11:01:54 +0300 genesys: Return errors as exceptions from *stop_action() commit fa6e4ab4c3961e4377091aa99bda80ca672bb319 Author: Povilas Kanapickas Date: 2019-07-20 11:01:53 +0300 genesys: Return errors as exceptions from *setup_scan_gpio() commit 86a151a4f2c021c3d0433f060f89e04a1b733591 Author: Povilas Kanapickas Date: 2019-07-20 11:01:52 +0300 genesys: Return errors as exceptions from *boot() and friends commit 67ecc10b81030d6d95addd5ad58fff8972d29b1c Author: Povilas Kanapickas Date: 2019-07-20 11:01:51 +0300 genesys: Return errors as exceptions from *init_gpio() commit 8e9253e301458fd299c9ba77b1976edeea391211 Author: Povilas Kanapickas Date: 2019-07-20 11:01:50 +0300 genesys: Return errors as exceptions from *genesys_init_shading_data() commit 24775d6e0eb398a3f02f781c09882d2ae57f6830 Author: Povilas Kanapickas Date: 2019-07-20 11:01:49 +0300 genesys: Return errors as exceptions from *homsnr_gpio() commit 08a1ae562543c842b8fafe2df43690e2a80af9c1 Author: Povilas Kanapickas Date: 2019-07-20 11:01:48 +0300 genesys: Return errors as exceptions from *start_action() commit c843e3e83db31f0d42f48f21b9c46b4e689ee95f Author: Povilas Kanapickas Date: 2019-07-20 11:01:47 +0300 genesys: Return errors as exceptions from *init_scan_regs() commit 153602308cd87e99b19c8f2b339c633daa95e4c6 Merge: b30567c271a4 bd6afe373684 Author: Povilas Kanapickas Date: 2019-08-09 07:33:28 +0000 Merge branch 'genesys-refactor-model-list' into 'master' genesys: Refactor model list See merge request sane-project/backends!103 commit bd6afe373684672d3e50503b8e40726f86fa6325 Author: Povilas Kanapickas Date: 2019-07-20 09:16:36 +0300 genesys: Store available DPI into proper array commit bca1ab1d99ac48138881f7dc3c57abebd9d4012f Author: Povilas Kanapickas Date: 2019-07-20 09:16:35 +0300 genesys: Store available resolutions into proper array commit 341061f3df51a0c2339aea1112b026c6832cfdd2 Author: Povilas Kanapickas Date: 2019-07-20 09:16:34 +0300 genesys: Correctly initialize Genesys_Model commit 7d4e7efa4ef9c9e2b36bddd2c5cf948a94814f73 Author: Povilas Kanapickas Date: 2019-07-20 09:16:33 +0300 genesys: Initialize usb device list in a function commit b30567c271a4fb3a1f6855fb356f11bec9a7e833 Merge: 326b2a04f7c1 847a3a6e7c8e Author: Povilas Kanapickas Date: 2019-08-09 07:12:47 +0000 Merge branch 'genesys-canoscan-8400f-infrared' into 'master' genesys: Add infrared channel support for CanoScan 8400F See merge request sane-project/backends!102 commit 847a3a6e7c8e85bd26f7d3bb67699c6877788910 Author: Povilas Kanapickas Date: 2019-07-20 21:17:31 +0300 genesys: Fix 8400F 2400 dpi scans commit b29bb66a36481bc906b2ad09403102f08b177a49 Author: Povilas Kanapickas Date: 2019-07-20 21:17:30 +0300 genesys: Add support for infrared scanning on 8400F commit ce713f95c0470e1afb20148e1c886310bcc09eb4 Author: Povilas Kanapickas Date: 2019-07-20 21:17:29 +0300 genesys: Refactor XPA lamp setup function to be more extensible commit 326b2a04f7c1d5d6bf3d06540e096039bcf3e2c2 Merge: 7d1617e4c067 57d8eb565ffb Author: Povilas Kanapickas Date: 2019-08-09 06:53:42 +0000 Merge branch 'genesys-return-errors-as-exceptions' into 'master' genesys: Return errors as exceptions instead of error codes See merge request sane-project/backends!101 commit 57d8eb565ffbaacb5e23d8b02d659775f21e8650 Author: Povilas Kanapickas Date: 2019-08-09 09:40:54 +0300 genesys: Add missing dependencies to the unit tests commit c19ab1fce57481b061e1b829bf4461d4cea4fef2 Author: Povilas Kanapickas Date: 2019-07-14 23:41:05 +0300 genesys: Return errors as exceptions from *init_motor_regs_scan() commit 9c9a118e12fcc9986d73824ccca40f917b924c78 Author: Povilas Kanapickas Date: 2019-07-14 23:41:04 +0300 genesys: Return errors as exceptions from *send_slope_table() commit 7d9f4d2c58e2d94521e7e6901572b4e2a2f164f3 Author: Povilas Kanapickas Date: 2019-07-14 23:41:03 +0300 genesys: Return errors as exceptions from *init_optical_regs_scan() commit 876b7bba4e19f33a775a91a5f0b63d8710167cba Author: Povilas Kanapickas Date: 2019-07-14 23:41:02 +0300 genesys: Return errors as exceptions from *set_fe() commit ce06801d5537cdfef728438d22841a6cca266cf9 Author: Povilas Kanapickas Date: 2019-07-14 23:41:01 +0300 genesys: Support variadic format string in SaneException commit 290f71d6c1f9e1579efcac6a9dfe9e7494bf6ae7 Author: Povilas Kanapickas Date: 2019-07-14 23:41:00 +0300 genesys: Move SaneException implementation to source file commit f258158ff3ab84d127170dd7873d46b4c8a3978d Author: Povilas Kanapickas Date: 2019-07-14 23:40:59 +0300 genesys: Return errors as exceptions from *send_offset_and_shading() commit 2f6052046fdbb908381f4118a1538197b8a981f9 Author: Povilas Kanapickas Date: 2019-07-14 23:40:58 +0300 genesys: Return errors as exceptions from gl843_set_buffer_address() commit 4303877059c1f05f5493faf43012c4f3088e7fea Author: Povilas Kanapickas Date: 2019-07-14 23:40:57 +0300 genesys: Return errors as exceptions from *init_cmd_set() commit d29f6862c63d401dff116103498c6546021b3e22 Author: Povilas Kanapickas Date: 2019-07-14 23:40:56 +0300 genesys: Return errors as exceptions from *wait_for_home() commit 2a9764b4fd50ca3ea15f206c6587efc6c98e5aae Author: Povilas Kanapickas Date: 2019-07-14 23:40:55 +0300 genesys: Return errors as exceptions from *send_shading_data() commit 99b5f1e94e355e2cb0ae626f203a49ca957a90c1 Author: Povilas Kanapickas Date: 2019-07-14 23:40:54 +0300 genesys: Return errors as exceptions from *gamma_buffer() commit e78cfa750feab63b4796392c3087196e4da0b5dc Author: Povilas Kanapickas Date: 2019-07-14 23:40:53 +0300 genesys: Return errors as exceptions from *fe_read_data() commit ad231613db7ad1e0cd183ebb04aebe504ed6fba2 Author: Povilas Kanapickas Date: 2019-07-14 23:40:52 +0300 genesys: Return errors as exceptions from *read_feed_steps() commit 612ff9487c9ab72e722892236c5d726ded07b977 Author: Povilas Kanapickas Date: 2019-07-14 23:40:51 +0300 genesys: Return errors as exceptions from *fe_write_data() commit 32a2eeaa5f163afd3e25c0c29c62acfb0befd1da Author: Povilas Kanapickas Date: 2019-07-14 23:40:50 +0300 genesys: Return errors as exceptions from *write_ahb() commit 693d42c0250d1e7d43ded9e7eb141bef5d808cee Author: Povilas Kanapickas Date: 2019-07-14 23:40:49 +0300 genesys: Return errors as exceptions from *read_valid_words() commit 478ddd5d01853a135830c36284e577447a9621a6 Author: Povilas Kanapickas Date: 2019-07-14 23:40:48 +0300 genesys: Return errors as exceptions from *read_scancnt() commit 5c0a382bc9cde7f1d409d020db9563df6b8fa76a Author: Povilas Kanapickas Date: 2019-07-14 23:40:47 +0300 genesys: Return errors as exceptions from *test_buffer_empty() commit f153fb109e82db4ef0821af69cbf7408933e1c8c Author: Povilas Kanapickas Date: 2019-07-14 23:40:46 +0300 genesys: Return errors as exceptions from *get_status() commit e388fc505884819a5168495c05b9c05706b6dd98 Author: Povilas Kanapickas Date: 2019-07-14 23:40:45 +0300 genesys: Return errors as exceptions from *bulk_write_register() commit 0dc3342be12be1c99660b4a8f608397faa98736e Author: Povilas Kanapickas Date: 2019-07-14 23:40:44 +0300 genesys: Return errors as exceptions from *bulk_write_data() commit 1c5960f9aa568c167fc68e7a10d9d1f8b9d6d180 Author: Povilas Kanapickas Date: 2019-07-14 23:40:43 +0300 genesys: Replace explicit DBG messages with DBG_HELPER commit 3a47a9ed4da8aa5f9c5505d879e9ad4ece02d38d Author: Povilas Kanapickas Date: 2019-07-14 23:40:42 +0300 genesys: Return errors as exceptions from *set_buffer_address() commit 17a619a927b11671d331dc63723d5f9ee6b18a3e Author: Povilas Kanapickas Date: 2019-07-14 23:40:41 +0300 genesys: Return errors as exceptions from *write_register() commit d1cffab8f4500247e3f865c3ce5ea4faec8a3083 Author: Povilas Kanapickas Date: 2019-07-14 23:40:40 +0300 genesys: Return errors as exceptions from *write_gl847_register() commit 3d17645fade5945e5aefa7a73ef9fa9425937e0c Author: Povilas Kanapickas Date: 2019-07-14 23:40:39 +0300 genesys: Return errors as exceptions from *read_register() commit bfef12cc4941f61700fb2e9563bf2d8c6b3253b4 Author: Povilas Kanapickas Date: 2019-07-14 23:40:38 +0300 genesys: Return errors as exceptions from *read_gl847_register() commit 9e15623411b81a345aeac8e12d409190bdac48d2 Author: Povilas Kanapickas Date: 2019-07-14 23:40:37 +0300 genesys: Return errors as exceptions from *write_0x8c() commit 1e5a7bd4d88d830a59d748272df575adda0ea035 Author: Povilas Kanapickas Date: 2019-07-14 23:40:36 +0300 genesys: Return errors as exceptions from *write_hregister() commit f856a3dacfff8eefc9468b4b73c12020d7aa55b7 Author: Povilas Kanapickas Date: 2019-07-14 23:40:35 +0300 genesys: Return errors as exceptions from *read_hregister() commit a79f1404f3bd504ad51a5dc4b24a2287ff6ee668 Author: Povilas Kanapickas Date: 2019-07-14 23:40:34 +0300 genesys: Return errors as exceptions from *bulk_read_data() commit 7d1617e4c067704704b5b828fc192a534ee09d1d Merge: 9a8eb90dc369 5c32aefd0f4d Author: Povilas Kanapickas Date: 2019-08-08 20:09:54 +0000 Merge branch 'genesys-canoscan-8400f' into 'master' genesys: Implement regular and transparency scans for Canon CanoScan 8400F See merge request sane-project/backends!100 commit 5c32aefd0f4d79a43b4f20bc23dc80a7416a7612 Author: Povilas Kanapickas Date: 2019-07-20 03:11:24 +0300 genesys: Implement transparency scanning support for 8400F commit ab2952d1df2acd1935d8db451984621e05ee7017 Author: Povilas Kanapickas Date: 2019-07-20 03:11:23 +0300 genesys: Add support for regular scans on CanoScan 8400F commit 0c1dded259055a5136556875d982fd2bf0da5c50 Author: Povilas Kanapickas Date: 2019-07-20 03:11:22 +0300 genesys: Enable support for CanoScan 8400F commit 9a8eb90dc3694d259a82853fa82be368457f59ed Merge: 820ff5293aca 6d6dbd81d32d Author: Povilas Kanapickas Date: 2019-08-08 19:51:06 +0000 Merge branch 'genesys-canoscan-8600f-infrared' into 'master' genesys: Add infrared support to Canon CanoScan 8600F See merge request sane-project/backends!99 commit 6d6dbd81d32d1754208512c09d6bdeb9fc6c8fda Author: Povilas Kanapickas Date: 2019-07-13 04:14:47 +0300 genesys: Improve documentation of Genesys_Model struct commit 644314bdad83cc0213c88b899343e2f9f3da35a5 Author: Povilas Kanapickas Date: 2019-07-13 04:14:46 +0300 genesys: Put 0x72/0x73 registers to sensor definition on GL843 commit 1a5e3944b3ae64ab4d95d189a978d2ceb999f17f Author: Povilas Kanapickas Date: 2019-07-13 04:14:45 +0300 genesys: Add option to get a raw scan ignoring offsets commit 144ed1f29b5d6869d40dc495edb5fda813a0de4c Author: Povilas Kanapickas Date: 2019-07-13 04:14:44 +0300 genesys: Implement infrared channel support for 8600F commit 78cdb347bea6e0fbfacd10a73537c6d680a99258 Author: Povilas Kanapickas Date: 2019-07-13 04:14:43 +0300 genesys: Fix lperiod on transparency scans on 8600F commit 3137639aca481b8a3257c0e78d07c8469b4b602f Author: Povilas Kanapickas Date: 2019-07-13 04:14:42 +0300 genesys: Make scan method explicit when selecting sensor commit c96a34f4a8b65f3390fac427124fffca619328df Author: Povilas Kanapickas Date: 2019-07-13 04:14:41 +0300 genesys: Fix partial width during calibration support commit 7e2a1eed97d2cf77830d282acc522a878c2a7b2f Author: Povilas Kanapickas Date: 2019-07-13 04:14:40 +0300 genesys: Fix alignment of lamp and sensor during of transparency scan The calibration area does not necessarily need to be at the zero position. We may need to drive sensor to the lamp and then drive both to the calibration. commit 7d32dca50e2f7ae3c9b5d795dae3c938e7a93b7e Author: Povilas Kanapickas Date: 2019-07-13 04:14:39 +0300 genesys: Sync GPIO with window driver on transparency scans on 8600F commit 0db759d996c1bd6f05e867a687f4de30f7055c19 Author: Povilas Kanapickas Date: 2019-07-13 04:14:38 +0300 genesys: Don't enable buggy behavior on newly supported scanners commit 820ff5293acaafe1ca218c7943d6cdc2a9c258be Merge: 45646240bf43 ac6ccfc1844f Author: Povilas Kanapickas Date: 2019-08-08 18:51:09 +0000 Merge branch 'genesys-repark-before-setting-shading-regs' into 'master' genesys: Repark before regs are setup in white_shading_calibration() See merge request sane-project/backends!98 commit ac6ccfc1844f7212ed6bb274874046e16edcae81 Author: Povilas Kanapickas Date: 2019-07-02 23:30:53 +0300 genesys: Repark before regs are setup in white_shading_calibration() Reparking after registers have been setup is very brittle approach, as it must preserve everything intact. commit 45646240bf4322702d0f270e44b33f684734ef77 Merge: 8d3369f68ecf af0f8e7e460e Author: Povilas Kanapickas Date: 2019-08-08 18:33:41 +0000 Merge branch 'genesys-fix-gl843-gain-calibration' into 'master' genesys: Improve gain calibration on GL843 See merge request sane-project/backends!97 commit af0f8e7e460ef61571ef347ec7242a6c61643b98 Author: Povilas Kanapickas Date: 2019-07-02 23:30:50 +0300 genesys: Improve gain calibration on GL843 commit 8d3369f68ecf171c8a4e6dc319b8b5d501206dc5 Merge: 1491a1067e6c 74854a85aab4 Author: Povilas Kanapickas Date: 2019-08-08 18:13:30 +0000 Merge branch 'genesys-reinit-regs-before-shading' into 'master' genesys: Reinit registers before each shading calibration step See merge request sane-project/backends!96 commit 74854a85aab4d5c35e9570fcfc1c86544cd174a1 Author: Povilas Kanapickas Date: 2019-07-02 23:30:48 +0300 genesys: Reinit registers for shading again before white shading commit a5c5ea56ada665769f4819a59953ceae0820666d Author: Povilas Kanapickas Date: 2019-07-02 23:30:47 +0300 genesys: Add debug markers to data captured during calibration commit 1491a1067e6cdf39d0088ed788fabf66b2216a75 Merge: 0fc4eb80e366 aa6bdba74d07 Author: Povilas Kanapickas Date: 2019-08-08 17:49:06 +0000 Merge branch 'genesys-usb-capture-debug-msg' into 'master' genesys: Add a way to record debug messages to USB captures See merge request sane-project/backends!95 commit aa6bdba74d07fa140c60cd354d8cab6faa086edb Author: Povilas Kanapickas Date: 2019-06-28 18:18:28 +0300 genesys: Add a way to record debug messages to USB captures commit 0fc4eb80e3665af8aecedcdab2cd5110325f52cf Merge: eae96342d838 34d378347c93 Author: Povilas Kanapickas Date: 2019-08-08 16:51:43 +0000 Merge branch 'genesys-cleanup' into 'master' genesys: Various cleanups See merge request sane-project/backends!94 commit 34d378347c93976f37e985f273b1f37a863c51bd Author: Povilas Kanapickas Date: 2019-07-06 21:02:28 +0300 genesys: Remove unused register read commit 8c9d6d7208ab7f88f479411153cf33add777cae1 Author: Povilas Kanapickas Date: 2019-07-06 21:02:27 +0300 genesys: Read the value of CKSEL from the sensor definition directly commit 1a641316b0921abdc6882f9df7b5692968d6a406 Author: Povilas Kanapickas Date: 2019-07-06 21:02:26 +0300 genesys: Remove duplicate entry of Canon 5600F in config file commit f84f1b9d4252cc0686c45c824478f9f977cf3fc8 Author: Povilas Kanapickas Date: 2019-07-06 21:02:25 +0300 genesys: Sort the initial register map by register on GL843 commit a6a09f5dc5941a67ea0809064c7086846432cc1c Author: Povilas Kanapickas Date: 2019-07-06 21:02:24 +0300 genesys: Don't reuse non-applicable macro just because its value matches commit f4d5fd4ad6520ac4d54cbf8dd1f9bcdde1680867 Author: Povilas Kanapickas Date: 2019-07-06 21:02:23 +0300 genesys: Add a way to dump Genesys_Register_Set to debug commit eae96342d838347479565e8870e0686dca5be2a3 Merge: 1be824f2c5bd fdc23d129ab2 Author: Ralph Little Date: 2019-08-08 16:44:19 +0000 Merge branch 'text_corrections' into 'master' Some fixes to US messages in source. See merge request sane-project/backends!91 commit fdc23d129ab2746ba19b2c2d405b8d9965cd53cb Author: Ralph Little Date: 2019-08-05 20:54:18 -0700 1) Correct some spelling mistakes 2) Convert some British spellings to US 3) Modified some longer sentences to improve readability. 4) Modified en_GB to suit commit 1be824f2c5bdf81998dc875cd43c28ce617fc871 Merge: 953ed4c2b0b2 f8f90f8b98c2 Author: Olaf Meeuwissen Date: 2019-08-08 14:01:30 +0000 Merge branch 'sanei-usb-testing-mode' into 'master' sanei: Support capture and replay of USB data for testing See merge request sane-project/backends!55 commit f8f90f8b98c29c6a823b14a71be411b3efd63bda Author: Povilas Kanapickas Date: 2019-06-30 22:31:14 +0300 configure: Make --with-usb-record-replay opt-in rather than opt-out commit baa4eb30483c59e0b34c045a1476562286ffa25b Author: Povilas Kanapickas Date: 2019-06-30 22:18:11 +0300 testsuite: Add missing libs to `make check` programs commit 599bfe519c4d3e835e9825ea9ceb9d0360d0166a Author: Povilas Kanapickas Date: 2019-06-16 21:12:42 +0300 sanei_usb: Fix build when USB replay-record mode is disabled commit f3df7b9db14eeaae3915fb96d25877e1f61ff897 Author: Povilas Kanapickas Date: 2019-06-16 21:12:41 +0300 genesys: Don't sleep during tests commit 4edfb7a287635c28fde1201f57e183efbf1b89b9 Author: Povilas Kanapickas Date: 2019-04-29 23:18:38 +0300 sanei_usb: Add function to check whether we are using fake data commit fd6adf2a46e8776ea0f133481bb7f14e3a1277d8 Author: Povilas Kanapickas Date: 2019-05-09 00:38:45 +0300 sanei_usb: Don't crash when recording failed reads commit b30406873c81ebd922f805747a58aa4ffdce2ed5 Author: Povilas Kanapickas Date: 2019-04-27 12:16:13 +0300 sanei_usb: Add support for record testing mode commit 7de8efd395375f84629a8d8c980009f5292841da Author: Povilas Kanapickas Date: 2019-04-27 12:16:12 +0300 sanei_usb: Add support for "development mode" replay testing commit 21ed18b230bd542055a4f3c8e7271777f71b893f Author: Povilas Kanapickas Date: 2019-04-27 12:16:11 +0300 dll: Report when testing backend is unknown commit 0122c060b7dad349584cbe11a1cc348a92f65bc1 Author: Povilas Kanapickas Date: 2019-04-27 12:16:10 +0300 sanei_usb: Add support for replay testing commit a88b6241cd7fc849619d81063663a5e609dfe951 Author: Povilas Kanapickas Date: 2019-05-25 09:12:30 +0300 configure: Add option to enable USB record-replay commit 953ed4c2b0b2927516382927e1cb1f8f8863ba8d Author: Olaf Meeuwissen Date: 2019-08-08 21:41:22 +0900 mustek_usb2: Fix sprintf invocation. Fixes #110 commit 90684c53bb646e6db98b4547cde9aeb2d41cf602 Author: Olaf Meeuwissen Date: 2019-08-01 21:14:08 +0900 doc: Remove :new markers in external backend descriptions External backends are per definition not new sane-backends. Removing the commented out info makes for fewer false hits when searching. commit 85674f12138882ef34f493749cb1731b07f26b9e Author: Olaf Meeuwissen Date: 2019-08-01 21:13:33 +0900 doc: Update as per post-release instructions commit 9a16b62e33ac55ecc5c4fdfd9f17f407541ab6ab Author: Olaf Meeuwissen Date: 2019-08-01 21:13:19 +0900 doc: Update post-release instructions This reflects the post-release changes that have been taken care of and are still considered somewhat meaningful. MD5 checksums have been replaced by SHA512 and SHA256 checksum files on the Releases page. The descriptions.db was used by the no longer existant Search Engine on the website. backends-1.3.0/ChangeLogs/ChangeLog-1.0.3000066400000000000000000000754431456256263500175410ustar00rootroot000000000000002000-08-12 Petter Reinholdtsen * configure.in aclocal.m4 configure NEWS: New version 1.0.3. Updated release date and removed freeze markers. 2000-08-12 Oliver Rauch * changed tools/sane-desc.el sane.gif->sane.png 2000-08-10 Henning Meier-Geinitz * backend/avision.c: Changed // comment to /* */. This broke compilation on AIX cc, Irix cc and Sun cc. 2000-08-10 Rene Rebe * backend/avision.h: some updates + cleanups * backend/avision.c: use of DB () and sane_config_read () general cleanups (sorry for the big commit - I used Emacs auto-ident for some regions ...) 2000-08-07 Henning Meier-Geinitz * README.irix: Removed text about library version problems. * README.linux: Added comment about idescsi problems. * ltmain.sh: Changed Irix library version system (from Oliver Rauch). This should fix the library version problems under Irix. * backend/Makefile.in: Added all files in lib/ to LIBLIB. This fixes a bug concerning linking with external frontends. 2000-08-06 Henning Meier-Geinitz * backend/plustek.c: Removed "const" from function header. This fixes a compilation problem on aix/cc. * configure configure.in: Additional warnings are now disabled by default for the 1.0.3 release. 2000-08-06 Henning Meier-Geinitz * backend/hp4200.desc: Added description file for HP 4200 USB scanner. 2000-08-05 Henning Meier-Geinitz * AUTHORS: Added link to PROJECTS. * PROJECTS: Removed frontends that are included in the SANE distribution. Added frontends: sanecgi, scanadf. Removed backends Kodak DS-20 (old camera, no specs available, not interested anymore), Logitech (old handscanner, author not interested anymore). Added backends as6e (Artec AS6E), bh (Bell and Howell Copiscan), lhii (handheld scanner support), Mustek USB, nec (NEC PC-IN500/4C), v4l2 (Video for Linux 2). Added/updated information about SANE ports: BeOS, OS/2, win32. Added Information about WinSANE and TWAIN interface for xsane-win32. Added link to AUTHORS and README. Changed format. Added explanation for statuses. * README: Added link to PROJECTS. * README.irix: New file. Information about the library version and jpeg library problems. * backend/as6e.desc: New description file for the Artec AS6E. * backend/bh.desc: New description file for Bell and Howell Copiscan scanners. 2000-08-03 Henning Meier-Geinitz * PROJECTS: Removed backends that are included in SANE. Updated info for Primax. Added HP 4200. 2000-08-02 Henning Meier-Geinitz * NEWS: Updated backend versions. * backend/hp*.c: Changed include statements from #include to #include "sane/...". 2000-08-02 Petter Reinholdtsen * backend/v4l.c: Check return value of ioctl() calls. Use DBG() instead of syslog() report progress. Removed redundant check for trailing newline in config file as we are now using sanei_config_read(). Remove ioctl(VIDIOCSYNC), as it hangs on my v4l2 device. 2000-07-31 Henning Meier-Geinitz * backend/mustek.*: Update to Mustek backend 1.0-96. Fixed Problem with detecting some three-pass scanners. 2000-07-31 Henning Meier-Geinitz * doc/sane.tex: Chapter 4 said, that this was standard version 0 (draft). Changed to 1 and removed "(draft)". 2000-07-31 Henning Meier-Geinitz * sanei/sanei_DomainOS.c sanei/sanei_ab306.c sanei/sanei_codec_ascii.c sanei/sanei_codec_bin.c sanei/sanei_config.c sanei/sanei_config2.c sanei/sanei_constrain_value.c sanei/sanei_init_debug.c sanei/sanei_load_values.c sanei/sanei_net.c sanei/sanei_pa4s2.c sanei/sanei_pio.c sanei/sanei_save_values.c sanei/sanei_scsi.c sanei/sanei_thread.c sanei/sanei_wire.c sanei/test_wire.c: Changed include statements from #include to #include "sane...". * PROBLEMS: Updated Mustek entry. * TODO: Updated some entries that are done. * backend/avision.c backend/plustek.c: Changed include statements from #include to #include "sane...". 2000-07-30 Peter Kirchgessner * backend/hp.desc: Change Parallel to Parport in description 2000-07-30 Oliver Rauch * changed all to "config.h" in lib/*.c 2000-07-30 Henning Meier-Geinitz * backend/mustek.*: Update to Mustek backend 1.0-95. Changed from wait() to waitpid() and removed unused code. * configure configure.in backend/m3096g.c backend/sp15c.c: Reverted the V_REV patch. V_REV should not be used in backends. 2000-07-30 Henning Meier-Geinitz * configure configure.in: Add V_REV to CPPFLAGS (only V_MAJOR and V_MINOR were defined until now). * doc/.cvsignore: Added sane-coolscan.5. 2000-07-29 Henning Meier-Geinitz * backend/sp15c.c backend/m3096g.c: Replace fgets with sanei_config_read, return V_REV as part of version_code string (patch from Randolph Bentson). 2000-07-29 Chris Pinkham * backend/artec.c: Changed include statements from #include to #include "sane...". 2000-07-29 Henning Meier-Geinitz * backend/GUIDE: Added some comments about portability and documentation. * backend/abaton.c backend/agfafocus.c backend/apple.c backend/canon.c backend/coolscan.c backend/dc210.c backend/dc25.c backend/dll.c backend/dmc.c backend/microtek.c backend/microtek2.c backend/microtek2.c backend/mustek_pp.c backend/net.c backend/pint.c backend/pnm.c backend/qcam.c backend/ricoh.c backend/s9036.c backend/sane_strstatus.c backend/sharp.c backend/snapscan.c backend/st400.c backend/stubs.c backend/tamarack.c backend/v4l.c: Changed include statements from #include to #include "sane...". * backend/avision.c backend/dc25.c: Use DBG(0, ...) instead of fprintf (stderr, ...) * backend/avision.c backend/canon-sane.c backend/coolscan.c backend/dc25.c backend/microtek.c backend/microtek2.c backend/st400.c: Use sanei_config_read() instead of fgets(). * backend/coolscan.desc backend/microtek.desc backend/microtek2.desc backend/st400.desc: Added :interface and :manpage entries. * backend/nec.desc: Status is beta now (was: new). Fixed typo. * doc/canon.README: Removed, because the information is included in the manpage now. * doc/Makefile.in: Added sane-coolscan to list of mapages to install. * README: Added Link to coolscan manpage. * backend/mustek.*: Update to Mustek backend 1.0-94. Fixed the #include bug. 2000-07-29 Karl Heinz Kremer * backend/epson.c: Changed the include statements for SANE includes from #include <...> to #include "..." 2000-07-28 Chris Pinkham * backend/aretc.c: Corrected sane_close() bug. Converted to use sanei_config_read() instead of fgets(). * backend/artec.desc: Changed interface entry to "Parport" vs "Parallel" 2000-07-28 Henning Meier-Geinitz * ltmain.sh: Added underscore to sed expression for removing backendname from soname (needed for libsane-mustek_pp). * sane.lsm: Added keywords for new backends. * lib/isfdtype.c: Added implementation for platforms that lack isfdtype() (mostly from Olly Betts). * backend/v4l.desc: Activated man page entry. * backend/mustek.* doc/sane-mustek.man: Update to Mustek backend 1.0-93. Fixed color scanning for Paragon 600 II N firmware 1.02. Fixed possible segfault in sane_control_option() when info is 0. Removed some compiler warnings. * NEWS: Updated Mustek backend version, added Artec, fixed typo * doc/Makefile.in doc/sane-snapscan.man doc/sane-canon.man README backend/snapscan.desc backend/canon.desc backend/.cvsignore: Added manpage for canon and snapscan backends. Added :interface entry for canon. * backend/canon-sane.c: Fixed possible segfault in sane_control_option() when info is 0. 2000-07-22 Petter Reinholdtsen * backend/v4l.c: Bugfix: Avoid crashing in sane_control_option() when last param is NULL. 2000-07-25 Chris Pinkham * backend/artec.c: Updated to v0.5.14. Added "Negative" mode as option even when in color/greyscale modes. Fix sane_close() bug. Fixed bug in sane_get_option_descriptor that allowed invalid option number. Changed numerous int variables to size_t to eliminate warnings. Changed various elements in ARTEC_Scanner structure to SANE_Int instead of int. Replaced all fprintf() statements with DBG(). * backend/artec.desc Added interface entries. 2000-07-26 Karl Heinz Kremer * backend/epson.c: Fixed problem with Perfection610 scanner. The variable s->color_shuffle_line was not correctly initialized. * backend/epson.desc: Updated version number 2000-07-26 Oliver Rauch * added info about variable scsi buffer sizes defined by backends umax, mustek, sharp (via sanei_scsi_open_extended()) to man sane-scsi 2000-07-25 Henning Meier-Geinitz * backend/snapscan.c: Use DBG(0, ...) instead of fprintf (stderr, ...) * backend/abaton.c backend/agfafocus.c backend/apple.c backend/dc210.c backend/dll.c backend/dmc.c backend/microtek2.c backend/pint.c backend/qcam.c backend/ricoh.c backend/s9036.c backend/snapscan.c backend/tamarack.c: Use sanei_config_read instead of fgets. * backend/dc210.c backend/microtek.c backend/pnm.c: Added #include . * backend/dc25.c backend/m3096.c backend/m3096g.c backend/sp15.c backend/st400.c: Moved #include to the beginning. * AUTHORS: Changed agfa to agfafocus. 2000-07-25 Peter Kirchgessner * backend/hp.c hp-scl.c hp-option.c hp-handle.c hp-accessor.c remove inline stuff 2000-07-25 Henning Meier-Geinitz * configure.in configure include/sane/config.h.in include/sane/sanei_backend.h: Moved test for u_int* to configure. * include/sane/sanei_debug.h: Remove #warning (Irix compiler doesn't like this) * backend/mustek.* doc/sane-mustek.man: Update to Mustek backend 1.0-92. Changed linedistance correction for MFS-8000SP. Fixed margin positions and removed warning for MFS 6000CX. Warning is printed in debug level 0 again. Removed test for gamma length. Fixed (partly) ADF handling. Man page update. More details in mustek.CHANGES. * NEWS: updated for new Mustek backend version. 2000-07-20 Peter Kirchgessner * backend/hp.c: use sanei_config_read() instead of fgets * backend/hp-scl.c: Dont write chars < 32 to DBG 2000-07-20 Abel Deuring * backend/sharp.c: removed fgets-call + minor bugfix * backend/sharp.desc: added entry ":manpage" * sanei/sanei_scsi.c: added missing dummy function sanei_scsi_req_flush_all_extended 2000-07-18 Henning Meier-Geinitz * configure.in configure: Fixed --enable-warnings to work as advertised. 2000-07-18 Petter Reinholdtsen * configure.in configure include/sane/config.h.in lib/Makefile.in lib/getenv.c lib/isfdtype.c lib/vsyslog.c sanei/sanei_init_debug.c: OS/2's getenv() is useless, OS/2, Solaris and AIX is missing isfdtype() and AIX is missing vsyslog(). Implement replacement functions. 2000-07-17 Henning Meier-Geinitz * backend/snapscan.c backend/snapscan-scsi.c: Replace C++ comment with C comment. 2000-07-17 Henning Meier-Geinitz * include/sane/sanei_backend.h: Replace C++ comment with C comment. 2000-07-17 Henning Meier-Geinitz * backend/musteka4s2.desc: Removed. Now that the mustek_pp backend is part of SANE musteka4s2 is no longer necessary. If somebody really wants to use the old musteka4s2 sources he will find a link on the mustek_pp WWW page. 2000-07-16 Jochen Eisinger * backend/mustek_pp.c: Replaced fgets() with sanei_config_read() 2000-07-16 Petter Reinholdtsen * include/sane/sanei_debug.h: Fix typo. 2000-07-15 Karl Heinz Kremer * backend/epson.c: Replaced fprintf() with DBG() * backend/epson.desc: updated version number 2000-07-15 Henning Meier-Geinitz * backend/dc210.c: Replaced fprintf (stderr, ...) by DBG (). 2000-07-15 Henning Meier-Geinitz * doc/Makefile.in doc/sane-dc210.man doc/sane-v4l.man doc/.cvsignore: Added manpages for dc210 and v4l. These are just standard manpages. Please check and update them. * README: Added dc210, v4l and mustek_pp entries. 2000-07-15 Henning Meier-Geinitz * backend/mustek.* doc/sane-mustek.man: Update to Mustek backend 1.0-90. Fixed linedistance-handling for Paragon 8000SP. Fixed mustek.conf port entry. Updated man page (parallel port scanners and 600 II N). * NEWS: Updated (mustek backend version 1.0-90) * backend/dll.conf: Commented out mustek_pp on behalf of the maintainer * doc/.cvsignore: added sane-mustek_pp.5, sane-plustek.5 and sane-st400.5 2000-07-15 Andreas Rick * backend/coolscan.c: added missing initialization in coolscan_read_data_block() 2000-07-13 Petter Reinholdtsen * AUTHORS backend/dll.conf backend/Makefile.in backend/mustek_pp.c backend/mustek_pp.desc backend/mustek_pp.h backend/mustek_pp.conf doc/Makefile.in doc/sane-mustek_pp.man: New backend for ScanExpress 6000 P, ScanMagic 4800 P, 600 III EP Plus, ScanExpress 600 SEP and MD9848 from Jochen Eisinger. * backend/snapscan-310.c backend/snapscan-scsi.c backend/snapscan-sources.c backend/snapscan-sources.h backend/snapscan-utils.c backend/snapscan.c backend/snapscan.desc backend/snapscan.h: New snapscan backend version dated 20000514 from Steve Underwood. * backend/microtek2.h backend/microtek2.c backend/microtek2.desc: Add support for ScanMaker X6USB. Patch from Oliver Neukum. * README.os2 configure.os2 include/sane/sanei_backend.h sanei/os2_srb.h sanei/sanei_init_debug.c backend/dll.c backend/net.c frontend/saned.c : Some of the OS/2 patches from Yuri Dario. * frontend/saned.c: Correct cancel handling in saned. Patch from Jochen Eisinger. 2000-07-12 Ingo Wilken * frontend/scanimage.c, doc/scanimage.man: Support for environment variable SANE_DEFAULT_DEVICE. 2000-07-11 Ingo Wilken * backend/st400.c, backend/st400.conf, backend/st400.h, doc/sane-st400.man: new files * backend/st400.desc: updated information * README, backend/Makefile.in, doc/Makefile.in: added st400 backend * AUTHORS: added myself as maintainer of st400 backend 2000-07-09 Peter Kirchgessner * doc/sane-hp.man: Updated manual page (scantype --> scan source) 2000-07-09 Peter Kirchgessner * backend/hp-handle.c, hp-scl.c, hp-scl.h, hp.c, hp.h, hp-option.c hp-option.h: Add wait for front panel button * backend/hp.desc: Changed version to 0.88 2000-07-09 Andreas Rick * AUTHORS: added myself as maintainer of the coolscan backend * README: added Coolscan page link * doc/sane-coolscan.man: first version of Coolscan manpage 2000-07-09 Gerhard Jaeger * AUTHORS: added myself as maintainer of the Plustek backend * README: added Plustek manpage entry * backend/plustek.desc: updated description * backend/plustek.c backend/plustek.h backend/plustek-share.h backend/plustek.conf: added Plustek backend code * backend/Makefile.in doc/Makefile.in backend/dll.conf: added plustek entries * doc/sane-plustek.man: added manpage for Plustek backend 2000-07-09 Petter Reinholdtsen * sanei/sanei_thread.c: Add required include files to get this to compile almost without warnings on Unix. * frontend/Makefile.in frontend/scanimage.c frontend/stiff.h frontend/stiff.c doc/scanimage.man: Add 16 bit support and new option --format to change file format. Makes it possible to save uncompressed TIFF images. Patch from Peter Kirchgessner. * backend/microtek2.c: Add ScanMaker X6USB identifier. Patch from Oliver Neukum. 2000-07-07 Petter Reinholdtsen * acinclude.m4 configure.in aclocal.m4 configure: New autoconf test SANE_V4L_VERSION. * AUTHORS: Added myself as CVS repository maintainer. * include/sane/sanei_debug.h sanei/sanei_init_debug.c: Send debug messages to syslog if stderr is a socket. 2000-07-06 Petter Reinholdtsen * configure configure.in: Change "you're" to "you are" to avoid confusing emacs font-lock mode. Regenerated configure. * backend/v4l.c: Fix sane_close() bug. 2000-07-02 Peter Kirchgessner * backend/hp.desc added interface entries * backend/hp.c, backend/hp-handle.c ADF-support for ScanJet IIp Return error SANE_STATUS_NO_DOCS if no paper in ADF 2000-06-30 Henning Meier-Geinitz * backend/mustek.* Update to Mustek backend 1.0-89. Fixed "scan slider doesn't return to start" bug. Details in backend/mustek.CHANGES. * backend/nec.desc backend/dc25.desc backend/m3096g.desc: Added :interface entries. Updated email adderess. 2000-06-28 Karl Heinz Kremer * backend/epson.c Fixed sane_close() - when the scanner still had data to deliver at the time the scanner was closed, it was impossible to restart any frontend software. This also caused scanimge -T to work just once. 2000-06-28 Henning Meier-Geinitz * backend/mustek.* Update to Mustek backend 1.0-88. Small bugfixes. Details in backend/mustek.CHANGES. * backend/sp15.desc backend/dc25.desc backend/m3096g.desc: Added :interface entries. 2000-06-28 Oliver Rauch * Updated umax backend to version 1.0.21 take a look at backend/umax.CHANGES for details - updated umax.desc, umax manpage 2000-06-28 Abel Deuring * backend/sharp.esc: added "interface" entries 2000-06-28 Henning Meier-Geinitz * backend/pint.c: fixed sane_close bug * backend/microtek.c: fixed compilation problem with NDEBUG defined 2000-06-27 Henning Meier-Geinitz * backend/lhii.desc: added :interface * README.linux: added reference to Linux SCSI documentation (from Abel Deuring) 2000-06-26 Henning Meier-Geinitz * backend/musteka4s2.desc: Added all more scanners and :interface entries * backend/sagitta.desc: Removed on behalf of the author. Nobody has showed interest in this very old (and rare) scanner over the years. It's difficult to keep it up-to-date because a kernel module is necessary. Nobody volunteered to take over maintainership. * backend/tamarack.c: Changed all fprintf to DBG calls. 2000-06-26 Abel Deuring * sanei/sanei_scsi.c and include/sanei/sanei_scsi.h: new functions sanei_scsi_cmd2 and sanei_scsi_req_enter2 for proper handling of "unusual" SCSI commands lengths * backend/canon-scsi.c: call sanei_scsi_cmd2, where necessary 2000-06-25 René Rebe * backend/avision.c: fixed sane_close () bug * backend/avision.desc: added some more scanners 2000-06-25 Henning Meier-Geinitz * backend/mustek.* doc/sane-mustek.man: Update to Mustek backend 1.0-87. Fixed gamma correction for 1200 A3 Pro. Fixed dependency of mustek.h on sane-backends.h. Fixed debug output bug in fix_linedistance_se. Updated documentation and mustek.conf. * README: Added v4l and fixed typo. 2000-06-25 Karl Heinz Kremer * backend/epson.desc - Changed Parallel to Parport 2000-06-25 Henning Meier-Geinitz * doc/Makefile.in doc/sane-s9036.man doc/sane-tamarack.man doc/sane-ricoh.man doc/sane-avision.man doc/xcam.man .cvsignore: Added manpages for these backends and xcam. * frontend/xcam.README: removed because manpage now exists. * backend/plustek.desc backend/qcam.desc backend/ricoh.desc backend/s9036.desc backend/tamarack.desc backend/avision.desc backend/st400.desc: Updated :interface and :manpage information. * README: Updated information regarding documentation. * backend/tamarack.c: Changed printf(...) to fprintf(stderr,...) on behalf of the maintainer. 2000-06-25 Petter Reinholdtsen * backend/Makefile.in backend/v4l.desc backend/v4l.c: V4L backend corrections. Corrected dependencies. Removed () in version number, as this backend is now included in the distribution. More debug info and use sanei_config_read() instead of fgets() in backend. 2000-06-23 Karl Heinz Kremer * backend/epson.desc - added :interface information 2000-06-23 Petter Reinholdtsen * AUTHORS: Added Juergen G. Schimmer as author of the v4l backend. * configure.in backend/Makefile.in backend/dll.conf backend/v4l-frequencies.h backend/v4l-grab.h backend/v4l.c backend/v4l.conf backend/v4l.desc backend/v4l.h: Added Video4Linux backend from Juergen G. Schimmer. 2000-06-22 Henning Meier-Geinitz * include/sane/sanei_pa4s2.h sanei/sanei_pa4s2.c Makefile.in backend/Makefile.in sanei/Makefile.in: Added interface for Mustek parallel port scanners (from Jochen Eisinger ). This will be used by the Mustek parallel port backend mustek_pp (coming soon). * tools/sane-desc.el backend/template.desc.: Added support for "interface" column in sane-desc.el. The new keyword is ":interface", examples in "template.desc.". All maintainers should update their *.desc files. * backend/mustek.desc abaton.desc agfafocus.desc apple.desc dc210.desc dmc.desc qcam.desc ricoh.desc s9036.desc snapscan.desc tamarack.desc: added interface entry for the Mustek and unmaintained backends. * backend/abaton.c apple.c qcam.c tamarack.c: fixed sane_close () bug for the unmaintained backends. 2000-06-18 Henning Meier-Geinitz * backend/mustek.* doc/sane-mustek.man: Update to Mustek backend 1.0-86. Added support for Mustek Paragon 1200 A3 pro (1, 8, and 24 bit/pixel; calibration doesn't work yet). Removed some remainings of color lineart and halftone support. More debugging output. Fixed bug in sane_control_option (option was not checked for negative values). Fixed bug in sane_close. General code cleanup. Details in backend/mustek.CHANGES. 2000-06-13 Karl Heinz Kremer * backend/epson.*: Invert image when scanning negative off the TPU. Initialize optial_res to 0. Fix sane_close() bug. Make threshold only active when halftoning is off and scan depth is 1. Make film type only active when TPU is selected. Scanner based color correction. More constraints for GUI. Cleanup in option handling. 2000-05-21 Henning Meier-Geinitz * backend/mustek.*: Update to Mustek backend 1.0-85. Added double buffering. This may improve scan performance a bit. Removed dead code (in dev_read_start). Changed linedistance correction handling. Paragon 12000 SP color mode might work now with all firmware versions. Details in backend/mustek.CHANGES. 2000-05-27 Karl Heinz Kremer * backend/epson.[ch] Support for multiple EPSON scanners added Fixed Perfection 610 color problem 2000-05-26 Abel Deuring * sanei/sanei_scsi.c: Fixed a bug in the Linux queue handling, as suggested by Simon Munton 2000-05-22 Oliver Rauch * ltconfig: Added option "-X" to ldconfig for linux-gnu this solves the problem with link /usr/local/libs/sane/libsane.so.1 => libsane-umax.so.1 2000-05-21 Henning Meier-Geinitz * README: Added quick install. Removed list of supported platforms because it is unknown how accurate it is at the moment. Added description for --enable-scsibuffersize (partly from Abel Deuring). Removed operating system specific information. See README.* files instead. Added comment to read the PROBLEMS file. Added/changed list of available documentation. Added comment on possible causes for the frontends not detecting scanners. Added comment on tools directory. Headings should be easier to find now. * README.aix: New file. Extracted from README. * README.hp-ux: New file. Extracted from README. * README.linux: New file. What is needed to get SCSI scanners to work under Linux. Workaround for Adaptec 1542 users (from Abel Deuring) 2000-05-18 Henning Meier-Geinitz * backend/mustek.* doc/sane-mustek.man: Update to Mustek backend 1.0-82. Added shrunk image fix to ScanExpress 12000SP models <= v2.0. Fixed remaining sane_cancel problems (in non_blocking mode). Fixed color stripes and segmentation fault for Paragon MFS-12000SP 1.00 (at least for me). Some minor debug output additions and changes. Look at backend/mustek.CHANGES for more details. * doc/.cvsignore: Added missing sane-sharp.5 2000-05-07 Henning Meier-Geinitz * backend/mustek.* doc/sane-mustek.man: Update to Mustek backend 1.0-81. Added transparency adapter (TA) support for all Mustek scanners. Fixed shrunk image bug for the ScanExpress 6000SP. Removed unused options (3-pass scanners and 600 II N). Scan source selection now shows only the options currently available. Fixed a bug concerning the ADF handling. sane_init () now uses sanei_config_read () and sanei_config_get_string () instead of its own functions. Man page and .desc update. Look at backend/mustek.CHANGES for more details. 2000-04-27 Karl Heinz Kremer * backend/epson.* Some code cleanup, added support for GRB images and did some Gamma correction fixes for the Perfection 610. 2000-04-27 Petter Reinholdtsen * include/sane/sanei_debug.h: Bugfix. Make sure the source compiles even if NDEBUG is defined. 2000-04-23 Henning Meier-Geinitz * backend/mustek.* doc/sane-mustek.man: Update to Mustek backend 1.0-80. Adjusted scan areas for most scanners. Added transparency adapter support for all Paragon legal size scanners. Fixed color mode for the Paragon MFS-8000SP (gamma problems). New system to set buffersize using sanei_scsi_open_extended. Removed (maybe temporarily) support for SCSI queue. Fixed shrunk image bug with the ScanExpress 12000SP. Fixed positional options detection (from mustek.conf). Added option "buffersize". Adjusted mustek.conf template to more reasonable defaults. Lots of small fixes and more complete debugging output. Look at backend/mustek.CHANGES for more details. 2000-04-15 Andreas Rick * backend/coolscan.* Update coolscan backend to version 0.4.3. This version includes support for the newer Nikon Coolscan models LS-30 and LS-2000. Dustremoval is not yet included. 2000-04-09 Henning Meier-Geinitz * backend/mustek.* doc/sane-mustek.man: Update to Mustek backend 1.0-79. Changed version system. Linedistance correction for the 600 II N is fixed. Added output of scanning time. Rewrite of the resolution encoding for the 3-pass scanners. Look at backend/mustek.CHANGES for details. 2000-04-05 Oliver Rauch * Updated umax backend to version 1.0.2-build-20 take a look at backend/umax.CHANGES for details 2000-04-05 Henning Meier-Geinitz * frontend/xscanimage.c, frontend/preview.c: Fixed a small bug concerning 3-pass scanners. gdk_input_add() was called three times without a preceding gdk_input_remove(). * frontend/scanimage.c: Fixed a bug where the scan was cancelled after a test for low mem even if enough memory is available. This probably only affected 3-pass scanners. 2000-04-02 Henning Meier-Geinitz * backend/mustek.*: Update to Mustek backend 0.78. Fixed several small bugs and spelling errors. Adjusted scanner names to Mustek style. Inquiry is even more verbose now. Look at backend/mustek.CHANGES for details. 2000-03-27 Karl Heinz Kremer * backend/epson.c backend/epson.desc: Removed status request for push button. This caused an error on older scanners. Fixed a few typos in the .desc file and added GT-9000 scanner. 2000-03-23 Oliver Rauch * updated umax backend to version 1.0 build 19 for details read backend/umax.CHANGES 2000-03-22 Karl Heinz Kremer * backend/epson.*: Several fixes for minor problems. Fixed function level A4 scanners (line mode instead of byte mode). Removed unnecessary free() calls. Use extended status for recognize warm up. This fixes problems with TPU and occasional crashes during startup. Added D1 level for Perfection 610. Added first version of support for set threshold and set zoom. 2000-03-19 Henning Meier-Geinitz * backend/mustek.c: Updated backend to new version 0.76. Fixes for 600 II N (color stripes, cancel handling), ScanExpress ("XC06" is now recognized), MFS-1200SP (linedistance correction in high resolutions, patch from Andreas Beck), STORE macros (patch from Norbert Mueller), ADF (patch from Joerg Anders), firmware identification (patch from Marco G. Salvagno), sane_cancel (scan will now be cancelled immediately, no new sane_read necessary), sane_get_option_descriptor (test for option < 0). Added a more verbose inquiry output. Adjusted debug levels. Changed the model names (sane.model) to reflect the real scanner names. Removed some compiler warnings. Added new maintainer. * backend/mustek.desc: Added link to backend homepage. Added all the different scanner names used for the same model. Added Trust scanners known to work. Removed firmware versions and scanner ids. * backend/mustek.CHANGES: New file. More detailed changes for the Mustek backend. * doc/sane-mustek.man: Documentation for the 600 II N linedistance options. Added warning from PROBLEMS. Added link to backend homepage. Changed supported scanners list (see mustek.desc). Added more bug entries. * AUTHORS: Added new maintainer and email address 2000-03-18 Petter Reinholdtsen * backend/Makefile.in tools/libtool-get-dll-ext: New script to detect shared library endings without using 'rev' which is missing on some platforms. 2000-03-14 Petter Reinholdtsen * sanei/sanei_scsi.c: Test for 'USE == LINUX_INTERFACE' instead of defined(__linux__). 2000-03-14 Abel Deuring * new version of the Sharp backend (0.31): added support for the JX350 (Thanks to Shuhei Tomita for providing the patch) 2000-03-07 Petter Reinholdtsen * Makefile.in (lsm) sane.lsm sane-1.0.1.lsm: New target to generate Linux Software Map (LSM) entry from template. Remove old file. backends-1.3.0/ChangeLogs/ChangeLog-1.0.30000066400000000000000000000147371456256263500176200ustar00rootroot00000000000000commit d5187355f6e0de529b562569509a1851dda7ad84 Author: Olaf Meeuwissen Date: 2020-05-17 20:16:28 +0900 NEWS: Document changes for 1.0.30 release commit 1fe94e6674d0572d2408361903730f012c60fc6c Merge: 898ab1834864 5104b80fc8f0 Author: Olaf Meeuwissen Date: 2020-05-17 16:26:00 +0900 Merge branch '279-confidential-issue' into release/1.0.30 commit 5104b80fc8f0d6528b856233a52846a414ae6616 Merge: f38c9f0d64a5 30b1831a28f2 Author: Olaf Meeuwissen Date: 2020-05-14 09:36:25 +0000 Merge branch 'mitigate-epsonds-net-security-issue' into '279-confidential-issue' epsonds: Mitigate potential network related security issues. Re #279 See merge request paddy-hack/backends!9 commit 30b1831a28f24ab2921b9f717c66d37f02bb81cc Author: Olaf Meeuwissen Date: 2020-05-11 21:07:12 +0900 epsonds: Mitigate potential network related security issues. Re #279 This pre-empts the possibility of triggering GHSL-2020-079, GHSL-2020-080 and GHSL-2020-081. commit f38c9f0d64a52697562abdfbf9c9044cb1b7e897 Merge: 3d005c2570a7 b9b0173409df Author: Olaf Meeuwissen Date: 2020-05-07 09:42:42 +0000 Merge branch 'issue09-esci2-img-buffer-size-check' into '279-confidential-issue' epsonds: Prevent possible buffer overflow when reading image data See merge request paddy-hack/backends!8 commit b9b0173409df73e235da2aa0dae5edd21fb55967 Author: Olaf Meeuwissen Date: 2020-04-27 18:48:29 +0900 epsonds: Prevent possible buffer overflow when reading image data Addresses GHSL-2020-084, re #279. commit 3d005c2570a71fe93a63192d9c47ee54cb39049b Merge: 226d9c92899f 27ea994d23ee Author: Olaf Meeuwissen Date: 2020-05-06 04:06:49 +0000 Merge branch 'issue05-out-of-bounds-read-decode_binary' into '279-confidential-issue' epsonds: Do not read beyond the end of the token See merge request paddy-hack/backends!5 commit 27ea994d23ee52fe1ec1249c92ebc1080a358288 Author: Olaf Meeuwissen Date: 2020-04-30 21:15:45 +0900 epsonds: Do not read beyond the end of the token Addresses GHSL-2020-082, re #279. commit 226d9c92899facf4b22b98c73be6ad2cd0effc4a Merge: 02b5d33b7a7c db9480b09ea8 Author: Olaf Meeuwissen Date: 2020-05-06 04:05:59 +0000 Merge branch 'issue07-out-of-bounds-read-in-esci2_check_header' into '279-confidential-issue' epsonds: Read only up to seven hexdigits to determine payload size See merge request paddy-hack/backends!6 commit db9480b09ea807e52029f2334769a55d4b95e45b Author: Olaf Meeuwissen Date: 2020-04-27 18:24:56 +0900 epsonds: Read only up to seven hexdigits to determine payload size Addresses GHSL-2020-083, re #279. commit 02b5d33b7a7c0b72137f5b968c46a1d52a75aa63 Merge: 4c9e4efd4a82 8682023faa27 Author: Olaf Meeuwissen Date: 2020-05-06 04:04:18 +0000 Merge branch 'issue08-integer-overflow-sanei_tcp_read' into '279-confidential-issue' sanei: Integer overflow sanei tcp read See merge request paddy-hack/backends!7 commit 8682023faa27c61156a354955c89617a3304d66f Author: Olaf Meeuwissen Date: 2020-05-04 11:54:35 +0900 sanei_tcp: Address possible integer overflow. Re #279, issue 8 commit fe08bbee6b238ea0be73af67b560ffc2c47562fd Author: Olaf Meeuwissen Date: 2020-05-04 11:48:46 +0900 epsonds: Handle error condition. Re #279, issue 8 commit 4c9e4efd4a82214719eeb1377a900e3a85c1c369 Merge: 2b4aa45bad61 fff83e7eacd0 Author: Olaf Meeuwissen Date: 2020-05-06 04:03:19 +0000 Merge branch 'issue01-null-pointer-deref-sanei_epson_net_read' into '279-confidential-issue' epson2: Rewrite network I/O See merge request paddy-hack/backends!3 commit fff83e7eacd0f27bb2d71c42488e0fd735c15ac3 Author: Olaf Meeuwissen Date: 2020-04-30 18:24:51 +0900 epson2: Rewrite network I/O This addresses GHSL-2020-075 as well as all other problematic code uncovered as a result of investigating that. This includes: - buffer overflows due to use of unchecked lengths - integer overflows due to type conversions - potential memory leaks - checking for memory allocation failures Re #279. commit 2b4aa45bad61d5e34996645581a606fd8795a48c Merge: 37b142494bf6 07e3834127f8 Author: Olaf Meeuwissen Date: 2020-05-04 08:24:19 +0000 Merge branch 'issue11-read_of_uninitialized_data' into '279-confidential-issue' magicolor: Added security mediation to device discovery See merge request paddy-hack/backends!2 commit 07e3834127f8bcd9dac02b91c17127dc41fbfb5b Author: Ralph Little Date: 2020-04-30 23:21:00 -0700 magicolor: Added security mediation to device discovery Extraction of values from the SNMP response were not checked. Also fixed a bug that mistakenly matched any SNMP OIDs with the first model in the model list, in function mc_get_device_from_identification(). commit 37b142494bf659d8147b6f0fcb8629408717d14d Merge: e52a5bf71979 af0442f15cc9 Author: Olaf Meeuwissen Date: 2020-05-04 05:28:37 +0000 Merge branch 'issue10-SIGFPE-in-mc_setup_block_mode' into '279-confidential-issue' magicolor: Added security remediation for pixels_per_line. See merge request paddy-hack/backends!1 commit af0442f15cc966bbc3d7d9322380005ea0ee8340 Author: Ralph Little Date: 2020-04-26 13:04:41 -0700 magicolor: Added security remediation for pixels_per_line. This implements a security issue reported by GitHub Security Lab. The details are disclosed in GitLab issue #279. The issue relates to an invalid scan parameter block being sent to the backend containing 8 bytes of 0x00 which leads to pixels_per_line being set to 0. Later arithmetic involves the division by this value which causes a div by zero crash. commit 898ab1834864e3b813f0d0ae234f38ac05813756 Author: Olaf Meeuwissen Date: 2020-02-06 20:56:21 +0900 Really remove libxml2 linker/loader flags from dependencies. Re #239 commit 76bf742aba32ec1ed4ae641285f8e6a0b038326d Author: Olaf Meeuwissen Date: 2020-02-05 21:30:11 +0900 Remove libxml2 linker/loader flags from dependencies. Re #239 backends-1.3.0/ChangeLogs/ChangeLog-1.0.31000066400000000000000000006616211456256263500176210ustar00rootroot00000000000000commit 871813a22db1b03adcbf4f5dc4ea9eb29f12a36d Author: Olaf Meeuwissen Date: 2020-08-23 20:25:14 +0900 NEWS: Document changes for 1.0.31 release commit b21f1cfd2748979b4e036575293a18eba28bf685 Author: Olaf Meeuwissen Date: 2020-08-23 15:15:13 +0900 Revert spurious source code file reference changes in !512 commit a2075317e38502dd2eb9058d54015e9faecf8a56 Merge: 8f19b77f45e9 d65d121e6d2a Author: Oliver Schwartz Date: 2020-08-20 19:55:50 +0000 Merge branch '38-macos-device-0x03f0-0x1305-at-020-002-is-not-configured' into 'release/1.0.31' Resolve "[macOS] device 0x03f0/0x1305 at 020:002 is not configured" See merge request sane-project/backends!504 commit d65d121e6d2ab48832e1b4f9d2172a500cfabbfa Author: Oliver Schwartz Date: 2020-08-08 09:55:47 +0000 Use SANEI_ALLOW_UNCONFIGURED_DEVICES macro to skip test of unconfigured devices instead of platform test. commit 8f19b77f45e955345da7cfc2ae48777009203ec7 Merge: 80643db457d1 48fee0b33f4b Author: Olaf Meeuwissen Date: 2020-08-20 10:07:25 +0000 Merge branch 'release/1.0.31' into 'release/1.0.31' Italian translation update See merge request sane-project/backends!512 commit 48fee0b33f4b9b998be27857d9239da5049820c4 Author: Vincenzo Reale Date: 2020-08-19 12:07:30 +0200 Italian translation update commit 80643db457d1893cd39bd333ff3bb08fb145f3e6 Author: Rolf Bensch Date: 2020-08-15 14:29:18 +0200 pixma: MP495 is working Pixma backend supports only symmetric scan resolutions, here 600dpi. See issue sane-project/backends#32 commit 7ac9296a1940b81372e46ac2e94126792fa87985 Author: Rolf Bensch Date: 2020-08-14 19:58:15 +0200 pixma: backend version 0.28.5 commit 3113d546c893ad9c8b4a3d7245e083272db8f6db Author: Rolf Bensch Date: 2020-08-14 20:27:06 +0200 Revert "pixma: MP490 Series doesn't need special image formatting @ high dpi" This reverts commit 1675697366e0d69edf1e413265c6fc15e6f3ab97. See issue sane-project/backends#338 commit e76b1e4863a23de7d740a46738af762c66935349 Merge: 0d5ea59d4378 743290300a22 Author: Olaf Meeuwissen Date: 2020-08-12 00:36:57 +0000 Merge branch 'for-upstream/avision_fix_fastfeed' into 'release/1.0.31' backend/avision: fix lock-up of scanners not supporting fastfeed See merge request sane-project/backends!505 commit 743290300a2269398b2c6986eaa603ae73c75526 Author: Michael Niewöhner Date: 2020-08-10 19:26:24 +0200 backend/avision: fix lock-up of scanners not supporting fastfeed de19ebc introduced a regression, where some scanners would lock-up completely and require a full power cycle. To fix that, do not call release_unit for any scanners not supporting fastfeed for now, since that leads to lock-ups on at least one device. Currently, it isn't clear what exactly is causing the problems, so it maybe be enabled in a correct way again, later. Resolves issue !337. Signed-off-by: Michael Niewöhner commit 0d5ea59d43780e5fc22238f3929e5ccd968a5587 Merge: 4a4199c33033 0082a9af569a Author: Rolf Bensch Date: 2020-08-10 21:09:40 +0000 Merge branch 'german-translation-revision' into 'release/1.0.31' Update German translation See merge request sane-project/backends!487 commit 0082a9af569ad8f5a7ae719ac0c4d54c3dbd5469 Author: Rolf Bensch Date: 2020-08-10 22:52:45 +0200 some translations need some rework commit c1297cddfb54cca6a6cb506a1abbf106fb35f66a Author: Rolf Bensch Date: 2020-08-10 22:51:38 +0200 rework some translations commit 85fcab62f5a0bf140e2fd9f85cf4af4cd26d597c Author: Rolf Bensch Date: 2020-08-10 22:46:30 +0200 translations start in capital letters commit 66e3e318afc5452c7c9d3f7b4e4589cb6d8770b8 Author: Rolf Bensch Date: 2020-08-10 22:41:33 +0200 remove outdated translations commit 5e07b8ad0be1df462439ecfea5227913cf5f9865 Merge: 3ab937a3f171 4a4199c33033 Author: Rolf Bensch Date: 2020-08-08 14:15:23 +0200 Merge branch 'release/1.0.31' into german-translation-revision commit 4a4199c33033e786260f88866245b4322266dee1 Author: Rolf Bensch Date: 2020-08-08 13:51:25 +0200 pixma: E410 Series is working See issue sane-project/backends#334 commit 3f1fea952c46d0a07222a5154c8a5f0d45cb325a Merge: f23107f43d97 5447fd3d375d Author: Oliver Schwartz Date: 2020-08-08 11:50:35 +0000 Merge branch '293-epson-perfection-3590-photo' into 'release/1.0.31' Resolve "Epson Perfection 3590 Photo" See merge request sane-project/backends!503 commit 5447fd3d375d40b1e589171f2619f347974f7637 Author: Oliver Schwartz Date: 2020-08-08 09:25:01 +0000 Added "PHOTO" suffix to some Epson scanners to have unified model names across backends. commit f23107f43d974c692c2c6750db910e8c6f82b97c Author: Olaf Meeuwissen Date: 2020-08-02 21:46:21 +0900 I18N: Update metadata (version, date, last translator) commit e7ab1b620fbdef94268c7d5af8911fde97457874 Author: Olaf Meeuwissen Date: 2020-08-02 21:45:42 +0900 I18N: Sync message catalogs with current code. commit ddf30425462a97c7928d4b8608e7e5062e50e1ef Merge: d289b42ad736 823176c0fe99 Author: Olaf Meeuwissen Date: 2020-08-02 12:15:21 +0000 Merge branch 'undefined' into 'release/1.0.31' Update Ukrainian translation See merge request sane-project/backends!493 commit 823176c0fe9957cfa0c71915f99937af73379691 Author: Yuri Chornoivan Date: 2020-07-22 12:37:35 +0000 Update Ukrainian translation commit d289b42ad736ca68a7f2e89ae23fa5217c293417 Merge: 7a3e090efcc2 72d09e0bfa01 Author: m. allan noah Date: 2020-08-01 12:10:17 +0000 Merge branch 'p208ii' into 'master' canon_dr: update P-208II status, add second mode See merge request sane-project/backends!499 commit 72d09e0bfa015e2ff899cc5ff53c4da737a90b00 Author: Florian Klink Date: 2020-07-29 15:01:19 +0200 canon_dr: update P-208II status, add second mode commit 7a3e090efcc2682a14d476cec8b198e97cd70b1c Merge: 7056b4826b28 d90b9e0292ef Author: Olaf Meeuwissen Date: 2020-08-01 06:15:30 +0000 Merge branch 'master' into 'master' Mark PLANon DocuPen RC800 as supported by gPhoto2. See merge request sane-project/backends!489 commit d90b9e0292ef8347d7ec3c0d805a20d77fd48fc0 Author: Ondrej Zary Date: 2020-07-04 13:38:07 +0200 Mark PLANon DocuPen RC800 as supported by gPhoto2. Docupen RC800 support was merged into libgphoto: https://github.com/gphoto/libgphoto2/pull/533 commit 7056b4826b28d9b509a60f52f9bfe11d4932d761 Merge: a277ea5ff1f4 9793d4f64eaf Author: Olaf Meeuwissen Date: 2020-08-01 06:14:15 +0000 Merge branch 'mingw-fixes' into 'master' MinGW build fixes See merge request sane-project/backends!483 commit 9793d4f64eaffff57e02c2247830b4eaf595d7b5 Author: Michael Cronenworth Date: 2020-06-17 17:24:55 -0500 MinGW build fixes - Fix define check on S_IFSOCK, looked like a typo - Need a define check around syslog usage - libdll_preload already has sanei_usb symbols and linking fails with duplicate symbol errors if this is left in commit a277ea5ff1f407ff208c413c7e619f3e9b32093e Merge: e44672cdf8d4 8e21bd58063f Author: Olaf Meeuwissen Date: 2020-08-01 03:45:15 +0000 Merge branch '279-issue02-null-pointer-deref-epsonds_net_read' into 'master' Resolve "memory corruption bugs in libsane" See merge request sane-project/backends!500 commit 8e21bd58063fa3c2807025b6d7c932283788158a Author: Olaf Meeuwissen Date: 2020-05-08 21:02:32 +0900 epsonds: Read whole payload of welcome message commit 6c07abf763bcb99d1fa419b999b1e4551990dea6 Author: Olaf Meeuwissen Date: 2020-04-30 19:45:42 +0900 epsonds: Rewrite network I/O This follows the changes made to the epson2 backend and addresses GHSL-2020-079, GHSL-2020-080 and GHSL-2020-081. Re #279. commit e44672cdf8d4d8c2636ceca6b0f73710dbd22195 Merge: be666bb03762 df451265125a Author: m. allan noah Date: 2020-08-01 01:10:17 +0000 Merge branch 'canon_dr/fix_get_pixelsize' into 'master' canon_dr: Report early error See merge request sane-project/backends!333 commit df451265125a0ed8eb0c58690f8bb537a4694a27 Author: Denis West Date: 2020-02-13 11:06:55 -0500 canon_dr: Report early error Exits get_pixelsize function when error is detected on previous operation, avoiding unnecessary retries and reporting the correct error code. commit be666bb03762116932e4c66c9af60c1b22ba5e70 Merge: 08790ef7cb7f 5dd9eeb0837b Author: Ralph Little Date: 2020-07-27 14:38:12 +0000 Merge branch '6-gt68xx-scanimage-batch-segfault' into 'master' Resolve "gt68xx scanimage --batch segfault" Closes #6 See merge request sane-project/backends!498 commit 5dd9eeb0837b8fdef3c3f75a5d518616680fdcc4 Author: Ralph Little Date: 2020-07-26 11:19:31 -0700 gt68xx: Restore cancel "stop_scan" call and fix whitespace issue The original issue that I had with this patch was in the cancel function where the patch author had commented out the "stop scan" call for cancel. This made no sense to me. I have put it back in for testing. It might have been accidentally left out. commit 462419cc68e1469ca5426f6c82dc17d9b1bd2a88 Author: Ralph Little Date: 2020-07-26 11:02:40 -0700 gt68xx: Initial check-in of proposed patch for crash. Ulf and I will review and test this patch on this branch. It does seem to fix the issue, but I want to check it out a little more before we commit it to master. commit 08790ef7cb7f15f2c925b22af8fd14fa5c631bef Author: pimvantend Date: 2020-07-27 12:45:07 +0200 lide 600 support promised in man-page commit 1b9e7016053e2b5dbb96ad255b8d16b3f4d5fd5d Merge: f29bbab5facd 1bd4fc283a08 Author: Ralph Little Date: 2020-07-26 17:53:48 +0000 Merge branch 'en_GB-translations-update' into 'master' po: Updated en_GB translations. See merge request sane-project/backends!497 commit 1bd4fc283a08117bceb51e6613831fa6d4412dfd Author: Ralph Little Date: 2020-07-26 10:35:04 -0700 po: Updated en_GB translations. commit f29bbab5facd85f376b6e60fb20db83444064932 Merge: 21a3bfad71eb 37bc4598a249 Author: Ralph Little Date: 2020-07-26 17:19:15 +0000 Merge branch 'hp5400-fix-option-warning-bug' into 'master' hp5400: initialize options bit mask to avoid compiler warning and glitchy operation See merge request sane-project/backends!496 commit 37bc4598a24993e8aa69aa9e9381c8b5382a34d7 Author: Ralph Little Date: 2020-07-26 10:00:48 -0700 hp5400: initialize options bit mask to avoid compiler warning and glitchy operation This was picked up as a compiler warning in Ubuntu build and was a real functional issue. commit 21a3bfad71eb28f61815d9cc4935d7de38e500db Merge: 890eb452b9d3 7f8acc85341a Author: Olaf Meeuwissen Date: 2020-07-26 12:20:04 +0000 Merge branch 'for-upstream/backend_avision_i1120' into 'master' backend/avision: support for Kodak i1120 See merge request sane-project/backends!19 commit 7f8acc85341a8228b53991e63fee106e223f8ed1 Author: Michael Niewöhner Date: 2018-10-28 16:05:08 +0100 backend/avision: i1120: add description entry Signed-off-by: Michael Niewöhner commit e8ae2ca16f57cc13af2efe2a2c7c9c84c36a20ac Author: Michael Niewöhner Date: 2018-10-28 14:48:55 +0100 backend/avision: fix: use fabs instead of abs for double values Signed-off-by: Michael Niewöhner commit fa6dfcc44e770fee8ad3e772ab88fdbea9990ec7 Author: Michael Niewöhner Date: 2018-10-28 11:38:01 +0100 backend/avision: i1120: finally set correct offsets for duplex Signed-off-by: Michael Niewöhner commit 7d7184a209007731b41985b91de716f9fd177302 Author: Michael Niewöhner Date: 2018-10-28 09:39:08 +0100 backend/avision: add missing offsets for bry Signed-off-by: Michael Niewöhner commit 6a6927b198b3b6352e33165bebce808ed8a6900c Author: Michael Niewöhner Date: 2018-10-27 18:43:39 +0200 backend/avision: implement ADF first-sheet offset compensation i1120 has another offset. When scanning in ADF multi-page mode (the default), the very first sheet is moved 1.5mm down. That leads to 1.5mm being cut off at the bottom. This implements the (hopefully last) offset setting for multi-sheet ADF scans. Signed-off-by: Michael Niewöhner commit e96ce8887e64b396789aacba73493ff642f6caa2 Author: Michael Niewöhner Date: 2018-10-21 11:35:19 +0200 backend/avision: add ADF front-only offset compensation This adds offset compensation for ADF front-only scans, which may have different offsets than ADF duplex scans. Signed-off-by: Michael Niewöhner commit 58a3cdd042a7668ced79f007235a519dbb471c52 Author: Michael Niewöhner Date: 2018-10-14 18:54:40 +0200 backend/avision: Rewrite offset compensation This is a complete rewrite of the offset compensation using a combination of overscan and line skipping. It replaces the option AV_REAR_OFFSET by specifying exact offset values for each scanner. This was needed for Kodak i1120 which needs bottom line skipping. The old implementation could not skip bottom lines since we could not know how many lines we will get in the end from ADF. The new implementation redirects the output file descriptor to a temporary file. After the current page is completely scanned, offsets are applied and the output is passed to the original output file descriptor. Signed-off-by: Michael Niewöhner commit b4716f626435c2eb71ef31bbf977c1040399dff8 Author: Michael Niewoehner Date: 2018-10-03 14:45:58 +0200 backend/avision: i1120: Add option AV_MULTI_SHEET_SCAN Kodak i1120 has single-sheet and multi-sheet scan modes. This option sets bitset3[7] which enables multi-sheet scan by default so there is no pause of 1s between two sheets in ADF scan mode. This also fixes some offsets when scanning multiple sheets. Signed-off-by: Michael Niewöhner commit e54c9602e9fdd75611b8f0904d640168cd6414c4 Author: Michael Niewoehner Date: 2018-10-01 18:10:09 +0200 backend/avision: i1120: add option AV_GAMMA_UINT16 Kodak i1120 has a different gamma table format that looks like a uint16/double array. Implement it and add an option for it. Signed-off-by: Michael Niewöhner commit d8bf3924244899ba0eb0af1aeafb949eb9ba67a6 Author: Michael Niewoehner Date: 2018-09-30 18:35:31 +0200 backend/avision: i1120: add option AV_GAMMA_10 Kodak i1120 needs gamma to be set to 1.0 to give decent scan results. Add an option for this. Signed-off-by: Michael Niewöhner commit 886bf85d880d1eac4add0bc43da3768c2af6a217 Author: Michael Niewoehner Date: 2018-09-30 18:18:41 +0200 backend/avision: i1120: add option AV_NO_QCALIB_MODE i1120 does not have an explicit "quality-calibration" mode. Add an option to disable this mode. Signed-off-by: Michael Niewöhner commit de19ebc61aff535edfb3e465a4a4d89f9b9e9dc2 Author: Michael Niewoehner Date: 2018-09-29 21:35:55 +0200 backend/avision: i1120: add option AV_FASTFEED_ON_CANCEL Some scanners like i1120 support fast feed-out of the sheet when cancelling a running scan. Add a new option for this release type. Signed-off-by: Michael Niewöhner commit 8b467b248ef8307d71b01d55aea868be35ec530c Author: Michael Niewoehner Date: 2018-09-29 21:58:24 +0200 backend/avision: i1120: set AV_NO_REAR Kodak broke rear-only scan (bitset3[3]) when they modified the Avision firmware. It would be possible to add a work-around that drops front data and writes rear data only. Because of deinterlacing, offsets etc. this is way too complicated. The simpliest solution to scan rear-only is to turn around the paper stack and use front scan instead. Signed-off-by: Michael Niewöhner commit 692f87389b5e023d4b427c50762afb74cb6882c8 Author: Michael Niewoehner Date: 2018-09-29 21:56:43 +0200 backend/avision: i1120: add option AV_OVERSCAN_OPTDPI i1120 uses optical DPI for overscan calculation. Add an option for it. Signed-off-by: Michael Niewöhner commit 74e94e69016c9b14f9ddde5789679c451d826980 Author: Michael Niewoehner Date: 2018-09-21 21:25:11 +0200 backend/avision: i1120: add option AV_NO_QSCAN_MODE i1120 does not have an explicit quality-scan mode. Add an option to disable it. Signed-off-by: Michael Niewöhner commit aff30cbb21d36bc43bf5a522c2c02dc6a0574c37 Author: Michael Niewoehner Date: 2018-09-21 20:49:46 +0200 backend/avision: ability[3] seems to be a typo since ability[2] is dark cal indicator Signed-off-by: Michael Niewöhner commit de1a873cb7020a7faa382e5f6b74cabe2c56dbd0 Author: Michael Niewoehner Date: 2018-09-20 20:13:53 +0200 backend/avision: i1120: use AV_SOFT_SCALE Signed-off-by: Michael Niewöhner commit c1c8ee8ea603652fee85c6dba824a82107e7ca7c Author: Michael Niewoehner Date: 2018-09-20 18:03:59 +0200 backend/avision: i1120: Enable gray filter to match windows driver Signed-off-by: Michael Niewöhner commit f393f8f24688b0bdca7d14286728382455c31fb5 Author: Michael Niewoehner Date: 2018-09-16 21:32:51 +0200 backend/avision: i1120: add option AV_FORCE_CALIB Add option AV_FORCE_CALIB to force calibration when scanner claims it is not needed but it is. Signed-off-by: Michael Niewöhner commit 196ce0b046e4eb5960c7c108f57cce3562b0e88e Author: Michael Niewoehner Date: 2018-09-20 18:43:31 +0200 backend/avision: Add basic support for KODAK i1120 Signed-off-by: Michael Niewöhner commit 890eb452b9d3eed5aba317c203d160f8607c3ccd Merge: f06c18b7aebf 5683aab66bc7 Author: Olaf Meeuwissen Date: 2020-07-26 09:39:23 +0000 Merge branch 'Add-gamma-tables-to-test-backend' into 'master' Add gamma test options See merge request sane-project/backends!301 commit 5683aab66bc704a884c2aa10589840d4ec020a56 Author: Kåre Särs Date: 2020-07-26 09:39:23 +0000 Add gamma test options commit f06c18b7aebf44de0aaa732a43e2163bd680187b Merge: e7fc728e2867 8bb62701c1e8 Author: Olaf Meeuwissen Date: 2020-07-26 09:10:21 +0000 Merge branch 'french-translation-revision' into 'master' Update French translation See merge request sane-project/backends!494 commit 8bb62701c1e841836b1ac3b038bacff33368eaec Author: Ordissimo Date: 2020-07-24 21:53:26 +0000 Update fr.po commit 0ca69fdc2e3e3d2cb864ee8aefb1b2d42d63e583 Author: Thierry HUCHARD Date: 2020-07-24 12:33:25 +0200 Update French translation commit e7fc728e2867a25abdafe20ae7c57a17193eca18 Author: m. allan noah Date: 2020-07-18 21:12:52 -0400 fujitsu: add USB IDs for fi-7800 and fi-7900 commit 8bdd27d149113a7e8cbea878829bb8adb3c09fab Author: Rolf Bensch Date: 2020-07-14 19:36:17 +0200 pixma: remove auto generated files @ 'make clean' See merge request sane-project/backends!491 commit e390b351a019b34c70b752d50bf97ea32c266070 Author: Rolf Bensch Date: 2020-07-14 18:02:11 +0200 pixma: backend version 0.28.3 commit f1ebc352ae46730c8b31cfe4a4244217dc7c3f68 Author: Rolf Bensch Date: 2020-07-14 18:01:22 +0200 pixma: update man page commit bc814909961465e044f198f4581937865ece25e3 Merge: 7481254f1bed 1da5ae889a2a Author: Rolf Bensch Date: 2020-07-14 15:57:31 +0000 Merge branch '302-pixma-status-of-pixma-tr-4500-series' into 'master' Resolve "pixma: Status of PIXMA TR 4500 Series" Closes #302 See merge request sane-project/backends!485 commit 1da5ae889a2a389cdfdd0c7f4ecf8f4335d1d777 Author: Rolf Bensch Date: 2020-07-11 00:28:39 +0200 pixma: add new button options some scanners return more options from push-button: document-type, adf-status and adf-orientation commit b8cf6b4b49ff08111b153790fbd09608b995b219 Author: Rolf Bensch Date: 2020-07-07 19:56:55 +0200 pixma: TR4500 Series is working commit ec918e5c74ab4e787ea576135cd1a119513a8a29 Author: Rolf Bensch Date: 2020-07-07 19:43:10 +0200 pixma: TR4500 Series supports max. 600dpi commit 0febc8166584d3ecf58c3c39973408aeee67b658 Author: Rolf Bensch Date: 2020-06-23 23:00:36 +0200 pixma: add button support for TR4500 Series this scanner provides additional button information commit 1943d34d606d1a0aae22661d2517f57406692a3f Author: Rolf Bensch Date: 2020-06-23 22:49:56 +0200 pixma: TR4500 Series returns JPEG images from ADF scans commit 7481254f1bed0311fe25596421771a02f5b7ba59 Author: Rolf Bensch Date: 2020-07-14 17:13:06 +0200 pixma: auto generate options files with python[23] see issue sane-project/backends#327 commit 7525fda505d3d12bc103954f4f4873c5d4d2ee94 Author: Rolf Bensch Date: 2020-07-11 20:15:43 +0200 INSTALL.linux: add python to mandatory develop environment list See merge request sane-project/backends!491 commit 819cc8cd611f1e2670cfeb1e18bfa9f5cc7c5de8 Author: Rolf Bensch Date: 2020-07-11 20:04:46 +0200 pixma: add comment how to generate new pixma_sane_options.[ch] commit 684efcef23283706ee69c80f8b2870d4e84ab382 Author: Rolf Bensch Date: 2020-07-11 19:51:26 +0200 pixma: add "do not edit" comment on top of generated files commit 8ad42d2967f3321a880c38a7e1dfbc9228022f95 Author: Rolf Bensch Date: 2020-07-11 19:46:56 +0200 pixma: PIXMA TS5100 Series is working see issue sane-project/backends#325 commit ff1e8b85c9d21192e9c1f302272d8196109881ec Merge: 537fbc9bf652 4a2cabe3be65 Author: Olaf Meeuwissen Date: 2020-07-11 08:42:36 +0000 Merge branch 'pixma-autogen_options' into 'master' pixma: auto generate sane options files See merge request sane-project/backends!491 commit 4a2cabe3be6593effa1004279e39a44f39f8e13f Author: Olaf Meeuwissen Date: 2020-07-11 16:51:23 +0900 pixma: Fix sane options file generation This now generates files in the source tree and includes them in the source tarball together with the script used. Explicit dependencies are added to trigger generation. Rules have been rewritten to use implicit variables for brevity. commit b957674f65d65536458fd2ccb9e65294f4399f86 Author: Rolf Bensch Date: 2020-07-07 22:24:35 +0200 pixma: auto generate sane options files This prevents from fixing stuff in generated files. Generated from pixma.c, bottom comment area. commit 537fbc9bf652d2aad2eee1a70023682f6febe868 Author: Rolf Bensch Date: 2020-07-06 23:34:07 +0200 pixma: backend version 0.28.2 commit 0b22eecf867c9d3701d96a19e2a6d8e19167336d Merge: 37c597d8df18 8f93b4cc622c Author: Rolf Bensch Date: 2020-07-06 21:38:06 +0000 Merge branch '318-canoscan-lide-400-potentially-supports-48-bit-depth' into 'master' Resolve "CanoScan LiDE 400 potentially supports 48-bit depth" Closes #318 See merge request sane-project/backends!486 commit 8f93b4cc622c002b8b1dea63b1a210fc6dbfa210 Author: Rolf Bensch Date: 2020-07-06 22:36:25 +0200 pixma_mp800: remove TODO for 16-bit scans mp800 scanners need min. 150dpi for 16-bit scans commit 3a18629f1190d78c48241e061448d8d500a50c41 Author: Rolf Bensch Date: 2020-07-06 22:31:48 +0200 pixma: in dpi list set min. scan resolution for 16-bit scans commit 0f1850fe4fbb019f87aa62e94168b41a8faf459d Author: Rolf Bensch Date: 2020-07-06 22:30:28 +0200 pixma: new parameter to set min. xdpi for 16-bit scans 16-bit scans have different min. scan resolutions: 75 and 150dpi commit b0efdd89cbb4a6869d03629a2b5fb6cee16f955a Author: Rolf Bensch Date: 2020-07-06 23:04:59 +0200 pixma_mp150: scan 48bit color and convert commit c4f0d4505e7d35185f675eadd90f8c8e1680eea8 Author: Rolf Bensch Date: 2020-06-24 16:58:36 +0200 pixma_mp150: enable 48bit support for LiDE 400 commit 3ab937a3f171a40ab117198074d726e7bda9036c Author: Rolf Bensch Date: 2020-06-27 13:42:04 +0200 fix some translations commit 463f833aa98da010ddf5b42f79e04f5bcf6599a2 Author: Rolf Bensch Date: 2020-06-27 13:35:08 +0200 add missing translations commit 6cc9fbdbbb98ef6780b25dc0f826224b5adc05ce Author: Rolf Bensch Date: 2020-06-27 12:19:34 +0200 update German translation from source commit 37c597d8df18601e4b8f374cc99275b0aad85f99 Merge: 7e2361d42e2a b0c42c0f5f52 Author: Povilas Kanapickas Date: 2020-06-26 22:04:27 +0000 Merge branch 'genesys-lide-90' into 'master' genesys: Add support for Canon CanoScan LiDE 90 See merge request sane-project/backends!488 commit b0c42c0f5f525660dfc5ebe861ae0abb03ed744d Author: Povilas Kanapickas Date: 2020-06-27 00:47:20 +0300 genesys: Implement support for LiDE 90 commit ea15fb29b73a3d90b0dc003e307a666caa3c5f3e Author: Povilas Kanapickas Date: 2020-06-27 00:47:19 +0300 genesys: Simplify motor table upload on gl842 commit d948f5eb697de719183a445faacc22949a18f0f0 Author: Povilas Kanapickas Date: 2020-06-27 00:47:18 +0300 genesys: Fix cis scanner support on gl842 commit 02782651a36bc43b2dd009dbb440bd2f41eec60e Author: Povilas Kanapickas Date: 2020-06-27 00:47:17 +0300 genesys: Add model option to disable fast feeding commit 86c15ff4a027163fc7955c8ee4cbeb507bb5d1b1 Author: Povilas Kanapickas Date: 2020-06-27 00:47:16 +0300 genesys: Add option to fill dark calibration with constant commit 6fdc5e6e1fd0b569cda494776b3d3054ee843d5f Author: Povilas Kanapickas Date: 2020-06-27 00:47:15 +0300 genesys: Throw an exception when motor speed is too low commit f6f891bd9387445f8176fffd02c60d1ad5114c77 Author: Povilas Kanapickas Date: 2020-06-27 00:47:14 +0300 genesys: Call update_home_sensor_gpio() for all chip types commit 7e2361d42e2af44d29b395394abab9ebce0ad835 Merge: 278d2c236eda c5c9c6a9405c Author: Rolf Bensch Date: 2020-06-26 22:16:32 +0200 Merge branch 'german-translation-revision' commit c5c9c6a9405c126a3a26b1f80e3f53bb83c06e65 Author: Ulf Zibis Date: 2020-01-13 23:34:08 +0100 po/de.po: First batch of full blown revision (15% done) commit 278d2c236eda1279d824ea458d0f8f58bbeeb0ea Author: Rolf Bensch Date: 2020-06-26 21:37:21 +0200 pixma: i-SENSYS MF4700 Series support is complete see issue sane-project/backends#262 commit 1a429257c136b066353c56658891790a9a616620 Author: Alex Belkin Date: 2020-06-25 19:07:59 +0300 xerox_mfp: Mark M288x Series as unsupported Device is reported by @dominicjaeger. Also, reference sane-airscan as suggested in #317 by @alexpevzner. commit 621e7dbf29fe18ad5e5bde4d24eb192ca5dec0b3 Author: Olaf Meeuwissen Date: 2020-06-24 20:34:13 +0900 utsushi.desc: Sync with upstream commit 7efeb82f16376a8f796a6ca516884de953595365 Merge: 8c9002772caf ce03cd0ad360 Author: Ordissimo Date: 2020-06-21 19:41:32 +0000 Merge branch 'escl-add-hp-officejet-pro-8610' into 'master' Add HP officeJet Pro 8610 See merge request sane-project/backends!484 commit ce03cd0ad360f7c6ffd373bb1d82674065fc52bd Author: Thierry HUCHARD Date: 2020-06-21 21:19:24 +0200 Add HP officeJet Pro 8610 commit 8c9002772caf7012383ea8e7bc7c9ef878b371cf Merge: fa0d3886641b 6bb87fdf1f3d Author: Olaf Meeuwissen Date: 2020-06-17 10:05:03 +0000 Merge branch '314-sane-1-0-30-unable-to-compile-without-libusb' into 'master' Resolve "sane 1.0.30: unable to compile without libusb" Closes #314 See merge request sane-project/backends!482 commit 6bb87fdf1f3dc190cfc4b7d64b0c8c8c3d10151b Author: Ralph Little Date: 2020-06-14 08:09:44 -0700 sanei: added missing include file stdint.h commit fa0d3886641b5afd05c039156daf0ec801accbc3 Merge: 67a2fe57717a 5a8ee2bd5b1d Author: Ordissimo Date: 2020-06-08 21:08:46 +0000 Merge branch 'escl-add-model-hp' into 'master' Add 2 models HP. See merge request sane-project/backends!481 commit 5a8ee2bd5b1d8ac41ebd6f6436b078b0fa253bf8 Author: Thierry HUCHARD Date: 2020-06-08 22:30:16 +0200 Add 2 models HP. commit 67a2fe57717a896a5d4d01cbc038f94868c4c76d Merge: af3acaf0303a 58c755d37400 Author: Ordissimo Date: 2020-06-07 13:56:32 +0000 Merge branch 'add-model-working' into 'master' escl : Add model working See merge request sane-project/backends!480 commit 58c755d37400e6674fff12070fd6e9d63e6d8662 Author: Thierry HUCHARD Date: 2020-06-07 15:38:11 +0200 Add model and mfg. commit 76dfd392e3da6458ebe771c1f6b9467a94a604fa Author: Thierry HUCHARD Date: 2020-06-07 13:46:47 +0200 Fix style. commit 7bd186acf0dad312991464cb52ae7007c0f8f901 Author: Thierry HUCHARD Date: 2020-06-07 13:39:19 +0200 Ajout des premiers modèles identifiés et soutenus par l'escl. commit af3acaf0303adadc27a65faacf327873c33a95a2 Author: Rolf Bensch Date: 2020-06-06 14:42:09 +0200 pixma: G4000 Series is working See issue sane-project/backends#245 commit e859ea108ae4b0f3180c34cfc3043cb774345a98 Author: Rolf Bensch Date: 2020-06-06 13:16:54 +0200 pixma: backend version 0.28.1 commit e58ccba918d0e9307941b0661f21226ced620abf Author: Rolf Bensch Date: 2020-06-06 13:16:16 +0200 pixma: TR8500 Series returns JPEG images from ADF scans See issue sane-project/backends#270 commit dc25c9f48dc0bf4d3c3ab6035fcda1cbc5cdab89 Author: Rolf Bensch Date: 2020-06-05 23:20:41 +0200 pixma: backend version 0.28.0 commit d2ae0ee6970a9a3d57d8dd7d86fd3d947d7bdcb5 Author: Rolf Bensch Date: 2020-06-05 23:17:51 +0200 pixma: update coryright commit 190f2874dbbce6b087fb78b2b57cc8d2e8569e29 Author: Rolf Bensch Date: 2020-06-05 23:17:24 +0200 pixma man page: refer to scanimage and gamma4scanimage man pages commit edace214b25766860dc3f71a4201fca9f052e5c4 Merge: b9b82f25e1e5 4b9dddb76365 Author: Rolf Bensch Date: 2020-06-05 20:58:07 +0000 Merge branch 'pixma/gamma_table' into 'master' Pixma: fix internal generated gamma table See merge request sane-project/backends!295 commit 4b9dddb76365165070711e4b4f262a02dd9eefe1 Author: Rolf Bensch Date: 2020-06-05 22:41:32 +0200 remove debug outputs from send gamma table commit 0118111c6fa935374800d1cf5cbb5f41d443712c Author: Rolf Bensch Date: 2020-06-05 22:07:19 +0200 use capability to select gamma table size commit 16ddd3b9722f628ae82d7fb742718b54b2d6daa7 Author: Rolf Bensch Date: 2020-05-30 16:21:05 +0200 get 1024 and 4096 size gamma tables from frontend Default gamma table has 1024 entries of 16 bits values. Only generation 1 scanners need a gamma table with 4096 entries of 8 bits values. commit 16ff7c0bba68e9c39a90ab4394a1adcebcfa8adf Author: Rolf Bensch Date: 2020-05-30 16:17:15 +0200 generate gamma table from control option we need to generate gamma table only once, after getting gamma from the frontend commit 057aa02b34567ae51d828fdf9d054eec0aac1c58 Author: Rolf Bensch Date: 2020-05-30 16:04:25 +0200 new capability for scanner generation 1 4096 bytes gamma table commit 11126468ef880bf7f0ee6520247ac97ecdd59a9f Author: Rolf Bensch Date: 2020-05-30 16:02:01 +0200 calculate 16-bit gamma table Generation 1 scanners need a 8-bit gamma table, with 4096 bytes size. All other scanners need a 16-bit gamma table, with 1024 bytes size. For these scanners values of the generated gamma table are close by the values used from the manufacturers proprietary driver. commit 103c77ddee320d38620dcbfe933fb341dc08a657 Author: Rolf Bensch Date: 2020-05-30 15:26:20 +0200 new global variable for gamma get gamma value from 'opt_gamma' commit 24c016f40836dad746174711a7675833c8b9d278 Author: Rolf Bensch Date: 2020-05-30 15:12:46 +0200 pixma_gen_options.py: fix print whitespace commit b9b82f25e1e595260003e272ad0d354ffeeada08 Merge: 1486b81dd9e1 f52f4917e549 Author: Ordissimo Date: 2020-06-05 10:03:25 +0000 Merge branch 'corrupted-data-image-with-scanimage' into 'master' Replacement of printf by DBG See merge request sane-project/backends!479 commit f52f4917e54941f4dcaf924afaf09e35dbf4ac6d Author: thierry1970 Date: 2020-06-05 11:45:21 +0200 Error syntax commit 641c736c37ce8033d7084106893c58c2c2e7e122 Author: thierry1970 Date: 2020-06-05 10:20:21 +0200 Replacement of printf by DBG. commit 1486b81dd9e187f8b4ac893d0cdf55f344a71b26 Author: Rolf Bensch Date: 2020-05-30 16:21:44 +0200 pixma: fix description of adf-wait commit 4a5c7d6889c5a7361ea892ec1264a545e249c5a0 Merge: 9165864eef83 db20a613c37b Author: Olaf Meeuwissen Date: 2020-05-30 04:20:44 +0000 Merge branch 'ci-updates' into 'master' CI updates See merge request sane-project/backends!478 commit db20a613c37b4f4cd3fe109d93d2d9015eb2d06a Author: Olaf Meeuwissen Date: 2020-05-30 13:04:03 +0900 CI: Bump Alpine from 3.11 to 3.12 commit 299af98cb2bd21ac3aded75a2ad3ad03b7f94c67 Author: Olaf Meeuwissen Date: 2020-05-30 13:02:14 +0900 CI: Sync full set of configure options with ci-envs settings commit 9165864eef83dd86e176c5ab266f06c0cf9ce95c Merge: e1be18fbef07 96f60115dfee Author: Povilas Kanapickas Date: 2020-05-25 23:10:01 +0000 Merge branch 'genesys-5600f' into 'master' genesys: Add support for 5600F See merge request sane-project/backends!477 commit 96f60115dfee4b9795b5d3101a3517271dcac2ea Author: Povilas Kanapickas Date: 2020-05-26 01:51:23 +0300 genesys: Bump calibration version commit 970b831ffe94a4b2445aa749c35e49c40f4c35ed Author: Povilas Kanapickas Date: 2020-05-26 01:51:22 +0300 genesys: Remove a couple of no longer relevant links commit 069d8a7db06379c185ce0d44931ea91c8f1bc00e Author: Povilas Kanapickas Date: 2020-05-26 01:51:21 +0300 genesys: Mark 5600F support as complete commit 23079e00612bc513a3414c80ad1d0853cfa255f0 Author: Povilas Kanapickas Date: 2020-05-26 01:51:20 +0300 genesys: Increase timeout in wait_until_has_valid_words() commit 3cca2593fc5bf1fc8afcf72c078ae14ef9d26eb4 Author: Povilas Kanapickas Date: 2020-05-26 01:51:19 +0300 genesys: Add support for Canon 5600F commit b9e4113ef083f6d272a53a3d25055df5b66e0228 Author: Povilas Kanapickas Date: 2020-05-26 01:51:18 +0300 genesys: Add functions to write to register according to mask commit e1be18fbef070f0eebd1e4311c5065d06594f189 Merge: 7c378476e856 7c74cb7ce973 Author: Povilas Kanapickas Date: 2020-05-25 22:49:33 +0000 Merge branch 'genesys-cleanup' into 'master' genesys: Miscellaneous cleanup See merge request sane-project/backends!476 commit 7c74cb7ce9730315bf762a6b9619cc0f662f63a9 Author: Povilas Kanapickas Date: 2020-05-26 01:32:45 +0300 genesys: Remove no longer used code commit 8ec6bc2e5d1e2a8c18a293a553887ed288ab8280 Author: Povilas Kanapickas Date: 2020-05-26 01:32:44 +0300 genesys: Deduplicate move_to_ta() commit c4135d63a7d6f35b95b9bfab9f521b900460ea26 Author: Povilas Kanapickas Date: 2020-05-26 01:32:43 +0300 genesys: Simplify move_to_ta() on gl843 commit bc973a21c25159e8d4f19a9b916602e645d6bba5 Author: Povilas Kanapickas Date: 2020-05-26 01:32:42 +0300 genesys: Simplify move_to_ta() on gl842 commit b8a24ee409e42dfce728d9f83de1f564418d8a9a Author: Povilas Kanapickas Date: 2020-05-26 01:32:41 +0300 genesys: Remove useless debug statements commit 57a398888d04f288e3c167e13a6a154c0a66ae80 Author: Povilas Kanapickas Date: 2020-05-26 01:32:40 +0300 genesys: Deduplicate init_regs_for_scan() commit 66ba92cfb6ea70a7cc4ec628365b67cd0c941462 Author: Povilas Kanapickas Date: 2020-05-26 01:32:39 +0300 genesys: Don't throw exception in non-exceptional circumstances commit 7c378476e856d739f9797c2b7e70b48ba1f71823 Merge: 64b69331d351 f3b04b374a7c Author: Povilas Kanapickas Date: 2020-05-25 22:30:48 +0000 Merge branch 'genesys-gl847-transparency' into 'master' genesys: Implement transparency support on gl847 See merge request sane-project/backends!475 commit f3b04b374a7c5f7814fa3822de22cd89c024a7ad Author: Povilas Kanapickas Date: 2020-05-26 01:14:43 +0300 genesys: Implement transparency support on gl847 commit 64b69331d351921f2d40ea662211f74b60b831b8 Merge: 786442d9c7b1 8a66829057e4 Author: Povilas Kanapickas Date: 2020-05-25 22:29:03 +0000 Merge branch 'genesys-misc-fixes' into 'master' genesys: Miscellaneous fixes See merge request sane-project/backends!474 commit 8a66829057e47cb6dd6ea5e00d7a097c9dd069a0 Author: Povilas Kanapickas Date: 2020-05-26 01:13:20 +0300 genesys: Simplify access to nodes that are newly added to image pipeline commit c9182dc6067279cf3cb153d97f5a5105d1a35308 Author: Povilas Kanapickas Date: 2020-05-26 01:13:19 +0300 genesys: Ensure ImagePipelineStack nodes are destroyed in reverse order commit 3282c5a65410055c7047d5642cc95da07ca50722 Author: Povilas Kanapickas Date: 2020-05-26 01:13:18 +0300 genesys: Fix TIFF file writing commit 786442d9c7b1ac14b5b2b030cae3664195772335 Merge: 6bf26b3882a9 ba91fc844d0c Author: Povilas Kanapickas Date: 2020-05-25 22:26:23 +0000 Merge branch 'genesys-pixel-offsets' into 'master' genesys: Pixel offset calculation adjustments See merge request sane-project/backends!473 commit ba91fc844d0c6aa5c2775e3a4add937368ba4a0c Author: Povilas Kanapickas Date: 2020-05-26 01:10:52 +0300 genesys: Fix start pixel calculation when optical resolution is not max commit 56db2b988aed604036df855dcb9c201309d56a5e Author: Povilas Kanapickas Date: 2020-05-26 01:10:51 +0300 genesys: Adjust host side calibration by shading pixel offset commit 8a10e33a13929345ff32080199d8a4853bd01526 Author: Povilas Kanapickas Date: 2020-05-26 01:10:50 +0300 genesys: Move all pixel adjustment calculations to a single function commit 6bf26b3882a9e386a7b6ec095292c00754a084f5 Merge: 5e8583bc3151 b569e6cbb949 Author: Povilas Kanapickas Date: 2020-05-25 22:25:47 +0000 Merge branch 'genesys-host-side-calibration' into 'master' genesys: Reimplement simplier host-side calibration See merge request sane-project/backends!472 commit b569e6cbb9492c90b9514db162689bd08702a4e1 Author: Povilas Kanapickas Date: 2020-05-26 01:09:23 +0300 genesys: Reimplement simplier host-side calibration commit 5e8583bc3151dfab92eb1ae20d23844baf4ee43a Merge: b7ec6da949b6 08613aad2677 Author: Olaf Meeuwissen Date: 2020-05-25 12:05:14 +0000 Merge branch 'remove-package-version-from-config' into 'master' gt68xx: Remove @PACKAGEVERSION@ from configuration filey See merge request sane-project/backends!471 commit 08613aad26776ef4bea41e5554a369ef9ed9403e Author: Olaf Meeuwissen Date: 2020-05-25 20:48:35 +0900 gt68xx: Remove @PACKAGEVERSION@ from configuration filey commit b7ec6da949b6667199ffb9e164f0f2bea5e70e94 Merge: 3084c412695c 92bf0c6623c7 Author: Povilas Kanapickas Date: 2020-05-24 17:59:40 +0000 Merge branch 'genesys-cleanup' into 'master' genesys: Miscellaneous cleanup See merge request sane-project/backends!470 commit 92bf0c6623c765fa5d7b3fe504d3c6b1ecf5b99a Author: Povilas Kanapickas Date: 2020-05-24 20:44:26 +0300 genesys: Remove broken interpolation support commit 6a495f97253f6771f53ac0e9ff6bbe9e12745d77 Author: Povilas Kanapickas Date: 2020-05-24 20:44:25 +0300 genesys: Remove duplicate align_int_up() commit e7e532200308d73dcc4cd7a3bede945f4944b5fb Author: Povilas Kanapickas Date: 2020-05-24 20:44:24 +0300 genesys: Remove dead code commit 3084c412695c25108536c866ab883f0e4b95d3c6 Merge: 44ee9bf373b8 33fc7cfb29dd Author: Povilas Kanapickas Date: 2020-05-24 15:52:51 +0000 Merge branch 'genesys-fix-parameters' into 'master' genesys: Retrieve output parameters out of the actual pipeline See merge request sane-project/backends!469 commit 33fc7cfb29dd153a71275bb6246405db8fc070e6 Author: Povilas Kanapickas Date: 2020-05-24 18:18:15 +0300 genesys: Retrieve output parameters out of the actual pipeline commit 44ee9bf373b8643bd93dd643e5ad1999d326215a Merge: 745a5c8b3870 a12083c8c44b Author: Povilas Kanapickas Date: 2020-05-24 15:35:41 +0000 Merge branch 'genesys-remove-lineart' into 'master' genesys: Remove support for generating lineart within the backend See merge request sane-project/backends!468 commit a12083c8c44b7296fa98ecb83e8932d0f6bdeb05 Author: Povilas Kanapickas Date: 2020-05-24 17:48:33 +0300 genesys: Remove support for generating lineart within the backend commit 745a5c8b387043eee461e7be683b0a72c0e4fe57 Merge: ddeae7e8b0f3 a23d61c26bea Author: Povilas Kanapickas Date: 2020-05-24 15:19:16 +0000 Merge branch 'genesys-simplify-pipeline' into 'master' genesys: Allow pipeline to be created without mutating device See merge request sane-project/backends!467 commit a23d61c26bea07f54f8f442c26f14decfef98203 Author: Povilas Kanapickas Date: 2020-05-24 16:42:41 +0300 genesys: Allow pipeline to be created without mutating device commit ddeae7e8b0f34e6822f83a4072e88dbfefe88e9b Merge: d680724f9e1c 28e6ad89b8ca Author: Povilas Kanapickas Date: 2020-05-24 13:52:19 +0000 Merge branch 'genesys-simplify-buffers' into 'master' genesys: Simplify read buffer management See merge request sane-project/backends!466 commit 28e6ad89b8ca150db2f8a159be8c83b2288d466d Author: Povilas Kanapickas Date: 2020-05-23 10:39:12 +0300 genesys: Simplify read buffer size calculation commit ec5af182398c94d01741cd9d8d1ce0109903cfe1 Author: Povilas Kanapickas Date: 2020-05-23 10:39:11 +0300 genesys: Remove no longer used ImageBufferGenesysUsb commit d172b9cc4d0f8d07c0075b3a0b29a2e97ebe6975 Author: Povilas Kanapickas Date: 2020-05-23 10:39:10 +0300 genesys: Remove read buffer after image processing commit f5af63326381a64aafe6e888fef564ae22b513ee Author: Povilas Kanapickas Date: 2020-05-23 10:39:09 +0300 genesys: Simplify data buffering in main image pipeline commit 9873fdf92270b1e8a6cf688c8366583e350f6e1a Author: Povilas Kanapickas Date: 2020-05-23 10:39:08 +0300 genesys: Don't read too much data when segment count is more than one commit 55691ece2c651468b2f21b371037d2827c359b3c Author: Povilas Kanapickas Date: 2020-05-23 10:39:07 +0300 genesys: Merge ImagePipelineNodeBuffered{CallableSource and GenesysUsb} commit 8981e583e20559c7e5dccbbeea7fbae114f826a8 Author: Povilas Kanapickas Date: 2020-05-23 10:39:06 +0300 genesys: Move math utilities to utilities.h commit fe323f19cb9de8f02b7f8482cf9d1e6d437ed535 Author: Povilas Kanapickas Date: 2020-05-23 10:39:05 +0300 genesys: Add a way to push constructed nodes to pipeline commit afa798d71a440032dcab309468c7e6dcb0b5b852 Author: Povilas Kanapickas Date: 2020-05-23 10:39:04 +0300 genesys: Simplify ImagePipelineNodeArraySource commit d680724f9e1cc7b68b8520f8a69fe5bea3e28bfd Merge: 51725d406fa6 d770012132e9 Author: Povilas Kanapickas Date: 2020-05-23 11:18:34 +0000 Merge branch 'genesys-debug-tiff' into 'master' genesys: Use TIFF files for debugging See merge request sane-project/backends!464 commit d770012132e9aa80f51b0f4cc3272d242b9df025 Author: Povilas Kanapickas Date: 2020-05-23 14:03:01 +0300 genesys: Use TIFF files for debugging commit 51725d406fa6464d3a4c35e80eb10dd9afae4507 Merge: 7fbe79220084 aa937c5b4c98 Author: Povilas Kanapickas Date: 2020-05-23 11:18:09 +0000 Merge branch 'genesys-stagger-alignment' into 'master' genesys: Align input pixel positions for unstaggering See merge request sane-project/backends!465 commit aa937c5b4c985943018565a39b7052a0caaaa97a Author: Povilas Kanapickas Date: 2020-05-23 14:02:32 +0300 genesys: Simplify pixel positioning for unstaggering commit 5a455e741bf7eef66457ac27fa5f13f4b0b2fa96 Author: Povilas Kanapickas Date: 2020-05-23 14:02:31 +0300 genesys: Fix align_multiple_{floor,ceil} for zero multiple commit bb05afdbfdab501e3496e887f63a5c9fd6e27157 Author: Povilas Kanapickas Date: 2020-05-23 14:02:30 +0300 genesys: Fix erroneous multiplication by channels on gl646 cis commit 7fbe79220084dd40dc37062d5cdca4488f90c34f Merge: 4ee035983cc8 5f0abce90fb9 Author: Povilas Kanapickas Date: 2020-05-21 22:40:34 +0000 Merge branch 'genesys-stagger' into 'master' genesys: Improve stagger handling See merge request sane-project/backends!463 commit 5f0abce90fb9545e71dee00ec9f23311cf6e00eb Author: Povilas Kanapickas Date: 2020-05-22 01:23:50 +0300 genesys: Add support for unstaggering in X direction commit 95d7196fca0f50c3223f7e4638da10cb5ad4909f Author: Povilas Kanapickas Date: 2020-05-22 01:23:49 +0300 genesys: Implement image pipeline node to shift columns commit 1bae94fd1ee8e8a5e33a23dfec4e010f2eac954e Author: Povilas Kanapickas Date: 2020-05-22 01:23:48 +0300 genesys: Support more than 2 columns in ImagePipelineNodePixelShiftLines commit d980da1ff7141e7b433689489dbc9a1fb02957d5 Author: Povilas Kanapickas Date: 2020-05-22 01:23:47 +0300 genesys: Improve vertical staggering to support more than 2 lines commit 4ee035983cc8af3ba39b7a7d75ab613e751872fa Merge: 2e4a3ddd0696 4b0f3ed02c32 Author: Povilas Kanapickas Date: 2020-05-21 20:57:48 +0000 Merge branch 'genesys-simplify-param-calculation' into 'master' genesys: Simplify scan parameter calculation See merge request sane-project/backends!462 commit 4b0f3ed02c32a180d3d8305caf1fa9506456e67e Author: Povilas Kanapickas Date: 2020-05-21 23:40:59 +0300 genesys: Warn if output and requested widths don't match commit a6f023fee180cbdfd834b98605f2b8d6e1459814 Author: Povilas Kanapickas Date: 2020-05-21 23:40:58 +0300 genesys: Reuse Genesys_Settings::requested_pixels for bytes per line commit c50bfb8493c94b306132620e9f071c8bd1d7d500 Author: Povilas Kanapickas Date: 2020-05-21 23:40:57 +0300 genesys: Simplify calc_parameters() by making data flow explicit commit 90a2386c01f5632981b0e0accd7329c5f090c74b Author: Povilas Kanapickas Date: 2020-05-21 23:40:56 +0300 genesys: Fix const safety of sensor retrieval functions commit 0957103cc3d5cb9bb1395652fbedf108596a070a Author: Povilas Kanapickas Date: 2020-05-21 23:40:55 +0300 genesys: Prefer to have a local `dev` variable for Genesys_Device* commit b6eb01cd0feb93171503bd3e57549702e4111b49 Author: Povilas Kanapickas Date: 2020-05-21 23:40:54 +0300 genesys: Update session test to include scan params commit 2e4a3ddd069626a3613de78a51626b864f536cab Merge: 3cd0914f678b 83f7520fe783 Author: Povilas Kanapickas Date: 2020-05-21 18:07:20 +0000 Merge branch 'genesys-simplify-buffers' into 'master' genesys: Simplify buffer handling See merge request sane-project/backends!461 commit 83f7520fe783a03929a8dd28a760903459aa1d35 Author: Povilas Kanapickas Date: 2020-05-21 20:51:17 +0300 genesys: Remove obsolete comment commit 0d97c3d19587388d14f1828957797ef4a938592c Author: Povilas Kanapickas Date: 2020-05-21 20:51:16 +0300 genesys: Simplify genesys_read_ordered_data() commit 1eccbf5aefa70ac3e6b2972f74813ca42a17e1da Author: Povilas Kanapickas Date: 2020-05-21 20:51:15 +0300 genesys: Remove no longer used buffers commit 3cd0914f678bfd96b7c144a5f0bf9910537698b4 Merge: d7f930252b2c 657de7d72a67 Author: Povilas Kanapickas Date: 2020-05-21 17:38:18 +0000 Merge branch 'genesys-remove-image-enhancement' into 'master' genesys: Remove image enhancement functionality See merge request sane-project/backends!460 commit 657de7d72a672c23670b810dd83ec5784779278d Author: Povilas Kanapickas Date: 2020-05-21 20:16:36 +0300 genesys: Remove image enhancement functionality from the backend commit 59abadd06558354e9d41c86bd4f0181d94ec9579 Author: Povilas Kanapickas Date: 2020-05-21 20:16:35 +0300 genesys: Fix debug statement logging directly to stderr commit d7f930252b2c333a62d0c0b0c3fec2de4f66449e Merge: 92552129acfa b42ed0343af2 Author: Povilas Kanapickas Date: 2020-05-21 17:37:23 +0000 Merge branch 'genesys-crash-bugs' into 'master' genesys: Fix several bugs that can potentially crash the backend See merge request sane-project/backends!459 commit b42ed0343af27d50d42724cf20e461960f0b7252 Author: Povilas Kanapickas Date: 2020-05-21 20:21:05 +0300 genesys: Fix uninitialized variable commit 78ed723f388b4e049d220e155349a98ad12b873d Author: Povilas Kanapickas Date: 2020-05-21 20:21:04 +0300 genesys: Fix invalid writes in ImagePipelineNodeInvert for 1-bit data commit 2986d95ee7464d36f1ef2ff16b72dcba7ed372af Author: Povilas Kanapickas Date: 2020-05-21 20:21:03 +0300 genesys: Fix potential out of bounds access in line shift pipeline nodes commit 92552129acfa26ef225a83d9e2c1d5ec8d51d99e Merge: 757b7cf46371 5a727324f917 Author: Povilas Kanapickas Date: 2020-05-20 19:35:48 +0000 Merge branch 'genesys-enums-human-readable' into 'master' genesys: Update enum formatters See merge request sane-project/backends!458 commit 5a727324f917365b77fde1cc0c77dc3c8867087f Author: Povilas Kanapickas Date: 2020-05-20 22:00:16 +0300 genesys: Update enum formatters for OpticFilm 7200 commit 4fea56dfe85ddef814d963ad5b99da4809afc107 Author: Povilas Kanapickas Date: 2020-05-20 22:00:15 +0300 genesys: Add function to format SensorId commit 757b7cf463718de9b4f7b29b0cd61eb9bde10f86 Merge: 0415fb4b7df1 bfea8a1a8235 Author: Olaf Meeuwissen Date: 2020-05-18 12:23:49 +0000 Merge branch 'release/1.0.30' into 'master' Merge changes released with 1.0.30 Closes #279 See merge request sane-project/backends!457 commit bfea8a1a82356f73101572d39bb33ca185692ed4 Merge: d5187355f6e0 0415fb4b7df1 Author: Olaf Meeuwissen Date: 2020-05-18 12:08:28 +0000 Merge branch 'master' into 'release/1.0.30' # Conflicts: # NEWS commit 0415fb4b7df148647692f5c7e1766bd214c12bfe Merge: 42954251826b e62f13052374 Author: Povilas Kanapickas Date: 2020-05-18 03:50:52 +0000 Merge branch 'genesys-sensor-optical-resolution' into 'master' genesys: Add real optical resolution to sensor structs See merge request sane-project/backends!456 commit e62f13052374d6a6e699d864dd34653fedf1497a Author: Povilas Kanapickas Date: 2020-05-18 06:35:20 +0300 genesys: Replace ccd_size_divisor with explicit optical resolution commit b5430cc039cda3d942488a80b3d49bc7e5c18f0a Author: Povilas Kanapickas Date: 2020-05-18 06:35:19 +0300 genesys: Rename Genesys_Sensor::{optical_res => full_resolution} commit 42954251826ba66271ea98ae0b30f2b7e23f9faa Merge: 42fe911fcd39 c2851ff61f22 Author: Povilas Kanapickas Date: 2020-05-18 01:49:47 +0000 Merge branch 'genesys-fix-motors-gl841' into 'master' genesys: Motor fixes and cleanups on gl841 See merge request sane-project/backends!455 commit c2851ff61f22c856e774a5168ca45ce6f8dbf80b Author: Povilas Kanapickas Date: 2020-05-18 03:53:29 +0300 genesys: Slow fast movements on higher resolutions on LiDE 35,50 commit b35345ae7a15347faa6f1107596df2e939f5f433 Author: Povilas Kanapickas Date: 2020-05-18 03:53:28 +0300 genesys: Use quarter step for high-resolution scans on LiDE 35,50 commit cf8f9c75f96472bc2077c1a2f34d8d16389d2154 Author: Povilas Kanapickas Date: 2020-05-18 03:53:27 +0300 genesys: Add missing register writes on gl841 commit 96a5f963a3e017c8d2be6026a9e31f4fd03e73c7 Author: Povilas Kanapickas Date: 2020-05-18 03:53:26 +0300 genesys: Make sure full-step tables are used on gl841 commit e5fa8dd3cc7da002f038392090e4057a814f4c17 Author: Povilas Kanapickas Date: 2020-05-18 03:53:25 +0300 genesys: Simplify motor mode selection on gl841 commit 69202b2b566cdb0fbd61bef77ab6c6b2fb591a9f Author: Povilas Kanapickas Date: 2020-05-18 03:53:24 +0300 genesys: Add separate sensor definition for LiDE 60 commit 341fdbbee7f3e7f55097a4291147933bcf7c47fa Author: Povilas Kanapickas Date: 2020-05-18 03:53:23 +0300 genesys: Add missing register writes on LiDE 60 commit bb0410199061520e9ffcfdf33396674300c581b3 Author: Povilas Kanapickas Date: 2020-05-18 03:53:22 +0300 genesys: Use scan table for back table on gl841 commit 5820e1e0bd098ea9916c7e69f50d6ef3340df744 Author: Povilas Kanapickas Date: 2020-05-18 03:53:21 +0300 genesys: Restore full motor table writes on gl841 commit 51f97b1b9719821f01462abca3822d4606e8f065 Author: Povilas Kanapickas Date: 2020-05-18 03:53:20 +0300 genesys: Fix invalid step count when feeding on gl841 commit 8435be213fc4687b3376b9488ba911534699453c Author: Povilas Kanapickas Date: 2020-05-18 03:53:19 +0300 genesys: Support non-FULL steps for fast tables on gl841 commit 4f0bab0bfa295f58ac24555680864339e5cff8e1 Author: Povilas Kanapickas Date: 2020-05-18 03:53:18 +0300 genesys: Don't write zero values to step count tables commit 1633022fda15bd4be1688494e8e74b4962773b0e Author: Povilas Kanapickas Date: 2020-05-18 03:53:17 +0300 genesys: Simplify motor setup on LiDE 80 commit 7932c68d03aa0dd32f208e6546af3b2e97c24ffb Author: Povilas Kanapickas Date: 2020-05-18 03:53:16 +0300 genesys: Simplify slope table size calculations commit 288dd82a6f22262eb823556f1a7744c809170b4b Author: Povilas Kanapickas Date: 2020-05-18 03:53:15 +0300 genesys: Pass motor profile directly to exposure calculation utils commit 049e69aa7953a893039903cc18f5189f7d125f09 Author: Povilas Kanapickas Date: 2020-05-18 03:53:14 +0300 genesys: Fix step multiplier handling in motor table manipulation utils commit b8ced2861a6fd52491ea550067bdfc4fcf5707e3 Author: Povilas Kanapickas Date: 2020-05-18 03:53:13 +0300 genesys: Always write the same set of tables on gl841 commit 12641bd6ca4b6f486cd035a8894be492f7783e85 Author: Povilas Kanapickas Date: 2020-05-18 03:53:12 +0300 genesys: Don't send empty tables unnecessarily on gl841 commit 88b49704b24806effd074bc8db2cbb31d4672f96 Author: Povilas Kanapickas Date: 2020-05-18 03:53:11 +0300 genesys: Use create_slope_table() directly on gl841 commit 16fa00c13b3db470ef56d55a75575a43f5158a62 Author: Povilas Kanapickas Date: 2020-05-18 03:53:10 +0300 genesys: Extract scanner_send_slope_table() commit 11c3a67e47354c3d0ada281233c019492337a8a7 Author: Povilas Kanapickas Date: 2020-05-18 03:53:09 +0300 genesys: Remove redundand argument out of *_send_slope_table() commit 253bd0ffe42b2961236a767d1abb0fef7d668359 Author: Povilas Kanapickas Date: 2020-05-18 03:53:08 +0300 genesys: Regenerate motor table total duration when it is updated commit 9c4f84f91afcd3ce993db0f527f617f16ea46f53 Author: Povilas Kanapickas Date: 2020-05-18 03:53:07 +0300 genesys: Don't generate excessive amount of steps for the motor tables commit 8e76d4ede9855cf3c7ee37786fc38f1a40876b77 Author: Povilas Kanapickas Date: 2020-05-18 03:53:06 +0300 genesys: Upload only required part of motor slope table on gl841 commit 412beaeb19f22c18572fc0af0337af4c62a035b6 Author: Povilas Kanapickas Date: 2020-05-18 03:53:05 +0300 genesys: Simplify motor setup on gl646 commit 42fe911fcd3931aa81225e0e0d9cf1c1358573fc Merge: 86d2b1e52fd8 800cf3beaf29 Author: Povilas Kanapickas Date: 2020-05-16 22:19:29 +0000 Merge branch 'genesys-cleanup-disable-calibration' into 'master' genesys: Cleanup the way calibration is enabled/disabled See merge request sane-project/backends!454 commit 800cf3beaf29e9126f32f9284b0e7f0b4242214a Author: Povilas Kanapickas Date: 2020-05-17 01:02:28 +0300 genesys: Use ModelFlag::DISABLE_*_CALIBRATION instead of NO_CALIBRATION commit 04b91dc4c9308223ee76973b28e3249b11ce6cc5 Author: Povilas Kanapickas Date: 2020-05-17 01:02:27 +0300 genesys: Implement a way to selectively disable calibration steps commit 71eeeb0ca6c16a68052fbd5a27929ba17d6a5d76 Author: Povilas Kanapickas Date: 2020-05-17 01:02:26 +0300 genesys: Remove dead code commit 86d2b1e52fd8f7f68845611221b33292f6030a14 Merge: 41facc7dd405 d6570c86ffb9 Author: Povilas Kanapickas Date: 2020-05-16 20:59:29 +0000 Merge branch 'genesys-remove-obsolete-debug' into 'master' genesys: Remove debug messages that are no longer useful See merge request sane-project/backends!453 commit d6570c86ffb917316d3a64d4f11dea1ae05d973e Author: Povilas Kanapickas Date: 2020-05-16 23:43:10 +0300 genesys: Remove debug messages that are no longer useful commit 41facc7dd405478340fe24435efa399ef1480156 Merge: 5d441bdd3ea3 14db69fb76d5 Author: Povilas Kanapickas Date: 2020-05-16 20:24:00 +0000 Merge branch 'genesys-image-logging' into 'master' genesys: Make image data logging more convenient See merge request sane-project/backends!452 commit 14db69fb76d52f13cdc2d588d2992b861da3f9d6 Author: Povilas Kanapickas Date: 2020-05-16 23:08:10 +0300 doc/genesys: Add documentation of SANE_DEBUG_GENESYS_IMAGE commit c0b49d0d3e651a6ec5e2ed80f68e48b1ca2dd8f6 Author: Povilas Kanapickas Date: 2020-05-16 23:07:08 +0300 doc/genesys: Remove no longer existing debug options commit 6e398bc7bf0e2314c819a460ad45887c1c9c3fba Author: Povilas Kanapickas Date: 2020-05-16 23:05:21 +0300 genesys: Write debug images only if step was enabled commit 6092e2d682886b44f6cba3a2b8570d34a705c90d Author: Povilas Kanapickas Date: 2020-05-16 23:05:20 +0300 genesys: Enable image data logging as a separate environment variable commit 5d441bdd3ea33b089ad2a47ca710abd2c7965fbe Merge: 10b126a65efc 9f46845f957d Author: Povilas Kanapickas Date: 2020-05-16 19:51:50 +0000 Merge branch 'genesys-print-error-when-config-file-unavailable' into 'master' genesys: Print debug message if configuration file can't be accessed Closes #101 See merge request sane-project/backends!451 commit 9f46845f957d3d1cf85e13e6dbaf60212c32bf4e Author: Povilas Kanapickas Date: 2020-05-16 22:35:17 +0300 genesys: Print debug message if configuration file can't be accessed commit 10b126a65efc5f69d8b085ece268d58e86e2847c Merge: 909a78a0c7dd ea8beca57dd6 Author: Povilas Kanapickas Date: 2020-05-16 19:39:23 +0000 Merge branch 'genesys-lide60-abrt' into 'master' Fix abort in genesys backend during sending slope table See merge request sane-project/backends!346 commit ea8beca57dd66d44e03f77dd60f1ee25bcbbe278 Author: Zdenek Dohnal Date: 2020-03-23 09:47:02 +0100 Fix assertion error in gl841 asic type by lowering steps for fast/back/slow tables commit 909a78a0c7dd25ecbeded78ab5de79a1be069166 Merge: 4bd76d35c6f4 d3b9178d6bda Author: Povilas Kanapickas Date: 2020-05-16 19:36:33 +0000 Merge branch 'genesys-simplify-gpio-gl847' into 'master' genesys: Simplify GPIO setup on gl847 See merge request sane-project/backends!450 commit d3b9178d6bdabaf11549cdf401c35bc56496bd12 Author: Povilas Kanapickas Date: 2020-05-16 22:21:13 +0300 genesys: Simplify GPIO setup on gl847 commit 4bd76d35c6f42f7a6326d70713de22a509884cdb Merge: c9beb6b0ba43 69a249e00076 Author: Povilas Kanapickas Date: 2020-05-16 19:21:39 +0000 Merge branch 'genesys-fix-adc-definition-gl847' into 'master' genesys: Fix ADC definitions on gl847 See merge request sane-project/backends!449 commit 69a249e0007636f4db9ecc18c482876aaca34296 Author: Povilas Kanapickas Date: 2020-05-16 22:04:52 +0300 genesys: Simplify ADC setup on gl847 commit 0d2a4b5244744c2bb8935db78a9f7f764cb49427 Author: Povilas Kanapickas Date: 2020-05-16 22:04:51 +0300 genesys: Fix incorrect ADC definition on gl847 commit c9beb6b0ba433b2d25eb07537fa7307f15010a1b Merge: fbb60d4d5255 4691b92d8047 Author: Povilas Kanapickas Date: 2020-05-16 18:52:33 +0000 Merge branch 'genesys-simplify-motor-profile-setup-gl841' into 'master' genesys: Simplify motor profile setup on gl841 See merge request sane-project/backends!448 commit 4691b92d80478c97701ed04b9817f92a268d2235 Author: Povilas Kanapickas Date: 2020-05-09 10:38:42 +0300 genesys: Simplify motor profile selection on gl841 commit ae8cb155efc65000d4454d2499e9408b0d372097 Author: Povilas Kanapickas Date: 2020-05-09 10:38:41 +0300 genesys: Simplify register setup on gl841 commit fbb60d4d525572f42cf78c5850756f7f10f2da57 Merge: a3284ab5e0bd 16a9bddf59f2 Author: Povilas Kanapickas Date: 2020-05-16 18:46:21 +0000 Merge branch 'genesys-simplify-gl646' into 'master' genesys: Simplify miscellaneous gl646 code See merge request sane-project/backends!447 commit 16a9bddf59f2a95a9bff4dd5a11fe6d2cb9ff89a Author: Povilas Kanapickas Date: 2020-05-10 22:28:01 +0300 genesys: Remove dead code on gl646 commit edb375fbccfa158be2bdc7ec670be6ba38f4fc1b Author: Povilas Kanapickas Date: 2020-05-10 22:28:00 +0300 genesys: Remove duplicated coarse calibration on gl646 commit 256a90e29d777de3b2865627b3f8030ef7635857 Author: Povilas Kanapickas Date: 2020-05-10 22:27:59 +0300 genesys: Simplify coarse_gain_calibration() on gl646 commit a4145e2edaebbdd43dc64fa41dc8f07f38e20bb7 Author: Povilas Kanapickas Date: 2020-05-10 22:27:58 +0300 genesys: Only set requested_pixels when it's not default on gl646 commit a3284ab5e0bdef4f5d20f3ce428af841b153b150 Merge: 35ffd5758617 b1b61b043b90 Author: Povilas Kanapickas Date: 2020-05-16 18:42:21 +0000 Merge branch 'genesys-simplify-motor-setup' into 'master' genesys: Simplify motor setup See merge request sane-project/backends!446 commit b1b61b043b90c5fd933db4bad3a4fd51576973f1 Author: Povilas Kanapickas Date: 2020-05-09 10:34:41 +0300 genesys: Pass whole motor struct to create_slope_table() commit fe3677b68f8517bcb04912303e3b3341d4b7bd4d Author: Povilas Kanapickas Date: 2020-05-09 10:34:40 +0300 genesys; Rename create_slope_table{=>_speed}() commit 35ffd575861726952b53efad944802e6269c5041 Merge: 4605d379bbbb dcb90dc042ac Author: Povilas Kanapickas Date: 2020-05-16 18:29:17 +0000 Merge branch 'genesys-use-flags-to-setup-scan-gl646' into 'master' genesys: Use flags to setup scans on gl646 See merge request sane-project/backends!445 commit dcb90dc042ac577106659f072f91893ad8ec4fb8 Author: Povilas Kanapickas Date: 2020-05-09 04:10:17 +0300 genesys: Simplify handling of output line count on gl646 commit 46d3ea07d49c6169d6f57537da802f55f70507c9 Author: Povilas Kanapickas Date: 2020-05-09 04:10:16 +0300 genesys: Use DISABLE_GAMMA fla to control GMMENB bit on gl646 commit 35544b0c363ba203059b0e6909737118908a9489 Author: Povilas Kanapickas Date: 2020-05-09 04:10:15 +0300 genesys: Simplify init_regs_for_shading() on gl646 commit 3dff3a97b34d9385064c03079a3c5b4f8a9b5479 Author: Povilas Kanapickas Date: 2020-05-09 04:10:14 +0300 genesys: Fix invalid movement in simple_move() on gl646 commit 84d4d0b15c94b4f232c8338928eea14b86ac7651 Author: Povilas Kanapickas Date: 2020-05-09 04:10:13 +0300 genesys: Use AUTO_GO_HOME flag to control AGOHOME bit on gl646 commit 4b5b2b2f0bdd11c79c44137f78977498efb33566 Author: Povilas Kanapickas Date: 2020-05-09 04:10:12 +0300 genesys: Simplify init_regs_for_warmup() on gl646 commit 0bc0f893b2188ca2be5263b0bfb606ce1cc05f47 Author: Povilas Kanapickas Date: 2020-05-09 04:10:11 +0300 genesys: Simplify invocation of simple_scan() on gl646 commit 4605d379bbbbcfd2097599a873727c98e0b92b61 Merge: ccf1cbd3ab51 aee51c9dec9e Author: Povilas Kanapickas Date: 2020-05-16 18:13:05 +0000 Merge branch 'genesys-fix-led-calibration-crash-on-black-image' into 'master' genesys: Fix crash in led_calibration() when image is completely black See merge request sane-project/backends!444 commit aee51c9dec9ed4de75eec79bb170372ecf00c333 Author: Povilas Kanapickas Date: 2020-05-11 23:48:21 +0300 genesys: Fix crash in led_calibration() when image is completely black commit ccf1cbd3ab5182b31abe45bcf15d469c151d1b27 Merge: e4d0b7b357e6 644f4b0c9461 Author: Povilas Kanapickas Date: 2020-05-16 20:56:19 +0300 Merge branch 'genesys-simplify-gl646' into master commit 644f4b0c946184bd97009ef77fd36d8a681557a4 Author: Povilas Kanapickas Date: 2020-05-06 01:05:45 +0300 genesys: Simplify session setup in ad_fe_offset_calibration() on gl646 commit 4183f37b4610bb907c802d50a3b699119d08e18d Author: Povilas Kanapickas Date: 2020-05-06 01:05:44 +0300 genesys: Simplify session setup in offset_calibration() on gl646 commit e32ca11346697db8cb0bb60c367d9a7de1187089 Author: Povilas Kanapickas Date: 2020-05-06 01:05:43 +0300 genesys: simplify session setup in ad_fe_coarse_gain_calibration() commit 1f78a2e64c7d93c37368400f31dfb3ea791acbd0 Author: Povilas Kanapickas Date: 2020-05-06 01:05:42 +0300 genesys: Simplify session setup in coarse_gain_calibration() on gl646 commit e876379f3181d37e12a10fdcc1db3fef78dab1fb Author: Povilas Kanapickas Date: 2020-05-06 01:05:41 +0300 genesys: Simplify session in simple_move() on gl646 commit 5a2e81b633811c6d5f4de4e168f8cd11ccae1eee Author: Povilas Kanapickas Date: 2020-05-06 01:05:40 +0300 genesys: Simplify session setup in led_calibration on gl646 commit 09eb07561fc0fdc38ee240308954f2a818fee09e Author: Povilas Kanapickas Date: 2020-05-06 01:05:39 +0300 genesys: Split simple_scan() into version that accepts ScanSession commit 4f1dad8ca91a6ea3db6df64dfc120b8d38c4253e Author: Povilas Kanapickas Date: 2020-05-06 01:05:38 +0300 genesys: Simplify session setup on gl646 commit 99a59d9871ecbfd7668759aca3d4050d86fa7811 Author: Povilas Kanapickas Date: 2020-05-06 01:05:37 +0300 genesys: Remove out of date comment commit e4d0b7b357e60612124fb1cd34bdcbd222f127ee Merge: 076faf8b8425 09eaba9606e9 Author: Povilas Kanapickas Date: 2020-05-16 15:15:49 +0000 Merge branch 'genesys-pipeline-invert' into 'master' genesys: Add pipeline to invert pixel values See merge request sane-project/backends!442 commit 09eaba9606e9f8894acb87879cf644747af59274 Author: Povilas Kanapickas Date: 2020-05-09 13:29:53 +0300 genesys: Add model flag for inverted output commit ab1d86d74a230add6ca328d3f3a769ef29e31634 Author: Povilas Kanapickas Date: 2020-05-09 13:29:52 +0300 genesys: Rename {INVERTED->SWAP}_16_BIT_DATA model flag commit 52cd02d78ca89e26277a2a908874cf6a4ab5a418 Author: Povilas Kanapickas Date: 2020-05-09 13:29:51 +0300 genesys: Add image pipeline to invert pixel values commit 076faf8b84256d624b399e102cc89e98b247b1d3 Merge: a05983ba28d7 06ad1dd43f5b Author: Povilas Kanapickas Date: 2020-05-16 15:09:31 +0000 Merge branch 'genesys-simplify-gl646' into 'master' genesys: Simplify gl646 scan setup code See merge request sane-project/backends!441 commit 06ad1dd43f5b23aa764be1d4eb4e61294b41e3b4 Author: Povilas Kanapickas Date: 2020-05-06 01:29:10 +0300 genesys: Inline setup_for_scan() into gl646_repark_head() commit 1b5c31bb01459e6e7980707e4d373b7dec86699a Author: Povilas Kanapickas Date: 2020-05-06 01:29:09 +0300 genesys: Inline setup_for_scan() into init_regs_for_warmup() on gl646 commit d7bf809a6cfc15975ed51ed6ec6efdc22aa28730 Author: Povilas Kanapickas Date: 2020-05-06 01:29:08 +0300 genesys: Inline setup_for_scan() into init_regs_for_shading() on gl646 commit 7ba20e43a31ab9c4d7c4076e1e30f5289c494b5c Author: Povilas Kanapickas Date: 2020-05-06 01:29:07 +0300 genesys: Simplify simple_scan() on gl646 commit f1d6c2c0cddc919ad005663c9bc5995620e9a8fc Author: Povilas Kanapickas Date: 2020-05-06 01:29:06 +0300 genesys: Inline setup_for_scan into simple_scan on gl646 commit a05983ba28d7db886b42cdd23193dd7d6823f2ae Merge: cf5c40b8e872 0abdfa483f54 Author: Ordissimo Date: 2020-05-15 08:59:36 +0000 Merge branch 'fix-statis-no-data' into 'master' If no data then the status is NO-DOCS. See merge request sane-project/backends!440 commit 0abdfa483f54dc5ce876a870fc9335d6dc3e6bfe Author: thierry1970 Date: 2020-05-15 10:21:56 +0200 If no data then the status is NO-DOCS. commit cf5c40b8e872d43d2b080fd44ef6b94b289e52bc Merge: 9e8326a3bcd3 1027e8aae09c Author: Ordissimo Date: 2020-05-14 14:23:51 +0000 Merge branch 'fix-status-start-and-stop' into 'master' Fix status start and stop See merge request sane-project/backends!439 commit 1027e8aae09c8044574841af2f146adc7bc8d3cd Author: thierry1970 Date: 2020-05-14 15:31:10 +0200 Fix build. commit bd8cb0e6aa40ed1cbaa7ee4b4a63a2eeb8077c89 Author: thierry1970 Date: 2020-05-14 14:03:20 +0200 Code cleanup. commit 287a6bb88a648a3a15b33e450bb3958eb164bb1f Author: thierry1970 Date: 2020-05-14 13:05:08 +0200 Test status device before scan and after. commit 9e8326a3bcd3842354a7dd0a7687594e7dae275f Merge: 0615d21718df d9c377cf0926 Author: Ordissimo Date: 2020-05-13 20:52:56 +0000 Merge branch 'fix-offset' into 'master' Fix offset See merge request sane-project/backends!438 commit d9c377cf0926e13c4ac3a963a7a884112c2537a0 Author: Thierry HUCHARD Date: 2020-05-13 22:24:51 +0200 Fix style commit c66efc0fafa3feecd567c4264f55ec25249e0096 Author: Thierry HUCHARD Date: 2020-05-13 22:20:55 +0200 Allows the X and Y offset to be set to 0. commit 0615d21718df1dc0d393082552ddd0d7e05f678d Merge: 3eb72d7485fd 29866b0b1975 Author: Ordissimo Date: 2020-05-13 18:49:57 +0000 Merge branch 'fix-return-status-adf' into 'master' Fix status sane_read. See merge request sane-project/backends!437 commit 29866b0b1975ca1d81a68d41315bbd42607e1ca7 Author: Thierry HUCHARD Date: 2020-05-13 20:33:01 +0200 Fix status sane_read. commit 3eb72d7485fd0fa7eac33acfc6e7fa0e1cd69a6a Merge: 080ac870ee9c c6a411a29fe0 Author: Louis Lagendijk Date: 2020-05-11 16:50:27 +0000 Merge branch 'pixma_mf4700n_not_buggy' into 'master' doc: ADF not now buggy on imageClass MF4770n. See merge request sane-project/backends!436 commit c6a411a29fe0faec788eb7c5c54c5e8aaf564363 Author: Ralph Little Date: 2020-05-11 09:16:07 -0700 doc: ADF not now buggy on imageClass MF4770n. Due to recent work on the pixma backend. commit 080ac870ee9c2382786e3abaf7e0a34d4c03a437 Author: Louis Lagendijk Date: 2020-05-11 17:12:31 +0200 pixma_bjnp.c: fix incorrect return from SANE_STATUS_INVAL to BJNP_STATUS_INVAL as pointed out on F32 compile in CI commit 5ad7708381e462d0f76bed42e6c0382e6aa264ac Merge: 767a289fd2dc 36ece1de84e5 Author: Ordissimo Date: 2020-05-11 14:58:38 +0000 Merge branch 'escl-fix-status-adf' into 'master' Refactoring of the adf status. See merge request sane-project/backends!434 commit 36ece1de84e5507bb456dee4fa1f0d1946a904dc Author: thierry1970 Date: 2020-05-11 10:25:27 +0200 Fix status for all vendors commit dc2b9aeadeaa183ee7a3f16b05230d622dc55970 Author: Thierry HUCHARD Date: 2020-05-10 22:20:30 +0200 JobState, if it exists, decides whether to proceed to the next page. commit 70daed513298e600db6a52334925d1ce96972558 Author: Thierry HUCHARD Date: 2020-05-10 15:11:30 +0200 Fix error name variable. commit 64c1c8a86559f76d4222305b76ff7f71ebe77a13 Author: Thierry HUCHARD Date: 2020-05-10 01:28:49 +0200 Logic correction. commit c1d302f3d71c8fcf1ce230d87de413fb4e90d2f7 Author: Thierry HUCHARD Date: 2020-05-09 23:48:48 +0200 Adjuste status adf. commit 296e73ae6bc7fde984cb5b110fdd5332e18c970b Author: Thierry HUCHARD Date: 2020-05-09 15:42:33 +0200 escl get sane_cancel output commit f2337927027a8f529c948ba8f5be146a11077194 Author: Thierry HUCHARD Date: 2020-05-09 10:03:39 +0200 Update Adf status. commit c4d8cc424e8d9a21fefe5e00647ce2d9828f9e6a Author: Thierry HUCHARD Date: 2020-05-09 09:50:15 +0200 Update Adf status. commit a931e0df574ae4b7047c19852c7add8f25e760df Author: thierry1970 Date: 2020-05-07 19:19:55 +0200 Refactoring of the adf status. commit 767a289fd2dc815e0b546dd8a7ca5d19802264fd Author: Louis Lagendijk Date: 2020-05-11 16:42:33 +0200 pixma_bjnp: fix ADF scanning for laser/mfnp scanners. mfnp devices require a single TCP session for ADF scans, while bjnp requires a TCP session per page. Squashed commit of the following: commit 8ee0a23d6b2920bc344bf76fd39f4b556222190b Author: Louis Lagendijk Date: Sat May 9 16:47:23 2020 +0200 Make pixma_bjnp work for both bjnp and mfnp commit 3df6d0d5903ccf4de0d27301e65025209888d3c7 Author: Louis Lagendijk Date: Sat May 2 19:50:23 2020 +0200 Move opening tcp connection to bjnp_open/close commit 10408cfa99e3f9965ea7b350a554a6ccf715b385 Merge: 6689104f01d9 73bf5533f978 Author: Ordissimo Date: 2020-05-08 07:01:30 +0000 Merge branch 'escl-clean' into 'master' escl - release memory and close descriptor. See merge request sane-project/backends!435 commit 73bf5533f97846d94034df809cfe6c6a5ff7d295 Author: Thierry HUCHARD Date: 2020-05-08 08:35:50 +0200 release memory and close descriptor. commit 6689104f01d9db0cf0da763b216c0c4b5ab990bf Merge: e8e9577c1200 878197a00e5f Author: Ordissimo Date: 2020-05-06 18:29:07 +0000 Merge branch 'fix-status-returned-by-sane-start' into 'master' escl: Add support for SANE_STATUS_NO_DOCS and SANE_STATUS_DEVICE_BUSY to sane_start Closes #283 See merge request sane-project/backends!433 commit 878197a00e5f72a5bb2486c20e8c98b8bdc70387 Author: Bartosz Kosiorek Date: 2020-05-06 18:25:44 +0200 escl: Add support for SANE_STATUS_NO_DOCS and SANE_STATUS_DEVICE_BUSY to sane_start Fixes: #283 commit e8e9577c1200dee3a8c10e4e97250875b055ae84 Merge: 20f7026371e7 adbbdc8a69f4 Author: Ordissimo Date: 2020-05-06 18:10:33 +0000 Merge branch 'add-more-logs' into 'master' Make curl error message more informative, to help debug issues See merge request sane-project/backends!431 commit adbbdc8a69f40d384576f055b8cb3cde6da92623 Author: Bartosz Kosiorek Date: 2020-05-06 16:01:01 +0200 Make curl error message more informative, to help debug issues commit 20f7026371e7db5badf42be8429f5ff743a3c699 Merge: 050e73d07ec8 fc14edf69ee0 Author: Ordissimo Date: 2020-05-06 14:44:24 +0000 Merge branch 'escl-reduces-number-entries-offer' into 'master' Escl reduces number entries offer See merge request sane-project/backends!432 commit fc14edf69ee0993fdb1f5c5c8faeb3ff5afdbc3c Author: thierry1970 Date: 2020-05-06 16:22:11 +0200 Fix build. commit 646ee990d39e04984354dc394153fb74d8d94200 Author: thierry1970 Date: 2020-05-06 16:08:53 +0200 Reduces the number of entries on offer. commit 050e73d07ec8f53ac2ddc4c38469a72cde4c7713 Merge: 833c25c3ae68 c9fdb62060ce Author: Ordissimo Date: 2020-05-06 09:21:28 +0000 Merge branch 'escl-fix-feeder-not-duplex' into 'master' Escl fix feeder not duplex See merge request sane-project/backends!429 commit c9fdb62060cecb3fbc44cfe0f31facfbf9b5a668 Author: thierry1970 Date: 2020-05-06 10:44:31 +0200 The duplex tag is only sent to the device if the device supports AdfDuplex. commit 9b3d7dc8b69590508d4eb9bd23dbaf3d83eb86a8 Author: thierry1970 Date: 2020-05-06 10:12:52 +0200 Fix name of sources. commit 833c25c3ae681820f7e1d41f2a0bac9a18fd3e80 Merge: b5013094f8bc e18242108e93 Author: Olaf Meeuwissen Date: 2020-05-06 05:58:02 +0000 Merge branch 'inspiration-from-freebsd-patches' into 'master' Inspiration from freebsd patches Closes #291, #290, #289, and #286 See merge request sane-project/backends!425 commit e18242108e93c2442306b061b6b781df41e192be Author: Olaf Meeuwissen Date: 2020-05-05 16:15:13 +0900 v4l: Drop kernel interface from v4l.h It has been included in libv4l1.h since 0.8.3. commit 95b43691661bacf47c8985a0ebb0f4ad1eb6d440 Author: Olaf Meeuwissen Date: 2020-05-05 16:14:11 +0900 v4l: Prefer C99 standard type over asm __u32. Re #291 commit 9e1acea49766e69b1782676ff11bfced7f35c4bf Author: Olaf Meeuwissen Date: 2020-05-05 13:02:35 +0900 umax_pp: Fix typo. Fixes #290 commit 1a201b28ab1bb6e7dacb3ed19226fbfb97df0e04 Author: Olaf Meeuwissen Date: 2020-05-05 12:55:19 +0900 ricoh2: Use C99 standard headers Inspired by a FreeBSD patch. Fixes #289. commit 9232792e0fb544eed9e6930cd7c282680cc1e305 Author: Olaf Meeuwissen Date: 2020-05-05 12:31:34 +0900 epsonds: Add missing header files for socket use. Fixes #286 commit b5013094f8bc0f58bc401589eeee93ef6cb2357a Merge: 7f1741d54d1f 0efee9ddd6ab Author: Ordissimo Date: 2020-05-05 22:31:58 +0000 Merge branch 'add-vendor' into 'master' Escl - Add vendor See merge request sane-project/backends!428 commit 0efee9ddd6ab331ef7848cd852f6418b1d074cd2 Author: Thierry HUCHARD Date: 2020-05-06 00:14:12 +0200 Fix model name cropping. commit cd18ba69dec1922501ff6f34b5ecbfb1b4bcf2e5 Author: Thierry HUCHARD Date: 2020-05-06 00:13:37 +0200 Add vendor commit 7f1741d54d1fbc405a8132fe192599f641faf16e Merge: bd482b6365fe 8e2d33565340 Author: Ordissimo Date: 2020-05-05 20:48:02 +0000 Merge branch 'escl-fix-vendor' into 'master' Put the vendor corresponding to the device used. See merge request sane-project/backends!426 commit 8e2d3356534003fa6b16516e7a310774284a38bc Author: Thierry HUCHARD Date: 2020-05-05 22:29:03 +0200 Put the vendor corresponding to the device used. commit bd482b6365fe0a6526982674ea0e870540c026f1 Merge: 0f657faf7cf2 2ed753b3fe47 Author: Ordissimo Date: 2020-05-04 11:39:19 +0000 Merge branch 'escl-fixe-descriptor-size' into 'master' Fixe descriptor size. See merge request sane-project/backends!424 commit 2ed753b3fe47911d2e87a71f6d4e1f32eaf826da Author: thierry1970 Date: 2020-05-04 13:19:03 +0200 Fixe descriptor size. commit 0f657faf7cf2038031e163bfa889c5e043f3c023 Merge: a8ce7a99ceb1 855cc408d3c1 Author: Ordissimo Date: 2020-05-02 09:23:17 +0000 Merge branch 'escl-cancel-scan' into 'master' Fix the status of the scanner in case of failure. See merge request sane-project/backends!422 commit 855cc408d3c16db695374c729b0f35a604ffa600 Author: Thierry HUCHARD Date: 2020-05-02 11:05:49 +0200 Fix the status of the scanner in case of failure. commit a8ce7a99ceb1bcde22bbbb3e42860e76da1d9e67 Merge: bc6e04f47791 a0ef09c456d6 Author: Olaf Meeuwissen Date: 2020-05-02 05:29:17 +0000 Merge branch 'ci-fedora-32' into 'master' CI: Bump Fedora from 31 to 32 See merge request sane-project/backends!421 commit a0ef09c456d6197cec5a919f117576636fa7ea9e Author: Olaf Meeuwissen Date: 2020-05-02 14:12:33 +0900 CI: Bump Fedora from 31 to 32 commit bc6e04f477912a70e8d244e47bf170ce95694333 Merge: ea053c1a6460 2190739b3108 Author: Ordissimo Date: 2020-05-01 10:37:17 +0000 Merge branch 'escl-correction-label-source' into 'master' escl : Fixed source argument. See merge request sane-project/backends!420 commit 2190739b310885e3c42be6adf27fbd1a4be27f10 Author: Thierry HUCHARD Date: 2020-05-01 11:59:58 +0200 Fixed source argument. commit ea053c1a6460af108bae8f3a66a4dc35d7f75d8f Merge: 9aad355032fd 24ffce93fd95 Author: Ordissimo Date: 2020-05-01 05:04:33 +0000 Merge branch 'capabilities-logs' into 'master' Don't use the uninitialized data during getting capabilities and print capabilities See merge request sane-project/backends!419 commit 24ffce93fd9557b7396d0588694c76ef43358418 Author: Bartosz Kosiorek Date: 2020-04-30 22:36:00 +0200 Don't use the uninitialized data during getting capabilities and print capabilities commit 9aad355032fd9653cf74d6c1ece890809d08799f Merge: 812497969ca3 dabd9270099f Author: Olaf Meeuwissen Date: 2020-04-30 01:34:12 +0000 Merge branch 'update-epkowa-documentation' into 'master' Sync doc/descriptions-external/epkowa.desc with latest upstream See merge request sane-project/backends!408 commit dabd9270099f31cb7238ecfb98617872bb85f30d Author: Bartosz Kosiorek Date: 2020-04-15 18:11:54 +0200 Sync doc/descriptions-external/epkowa.desc with latest upstream iscan-data_1.39.1-2.tar.gz archive downloaded from: http://support.epson.net/linux/src/scanner/iscan/ commit 812497969ca3a68615b71162b6560d4fb890b3dd Merge: 867aeb7d0908 08b41c052e46 Author: Povilas Kanapickas Date: 2020-04-23 18:53:20 +0000 Merge branch 'genesys-plustek-7200' into 'master' genesys: Add support for Plustek OpticFilm 7200 See merge request sane-project/backends!418 commit 08b41c052e4652c66d0906948eacd8dc3cf4e25b Author: Povilas Kanapickas Date: 2020-04-23 21:17:03 +0300 genesys: Add support for Plustek OpticFilm 7200 commit 867aeb7d0908a495b1118ceb012bbbc2143f5baa Merge: a70beba79c9a c88ba0dff246 Author: Oliver Schwartz Date: 2020-04-23 14:13:27 +0000 Merge branch 'master' into 'master' sanei_usb: allow non-configured devices on macOS for libusb-1.x See merge request sane-project/backends!358 commit c88ba0dff246b19a817a35c88c2a62bf8524de3f Author: Brandon Beck Date: 2020-03-13 17:54:51 -0500 Allow unconfigured USB scanners on macOS. Based on findings from Oliver Schwartz (https://alioth-lists.debian.net/pipermail/sane-devel/2012-June/029890.html) this change allows USB scanners to be unconfigured on macOS. This fixes the issue of scanners being unable to be discovered because they look to be not configured but end up getting configured later. commit 011d0f9bacab126fb2ae09d29abdd6eb88f1333d Author: Brandon Beck Date: 2020-03-13 19:26:54 -0500 Fix build on macOS. commit a70beba79c9a2e7f26e7958db76a19f5e1b8fadd Merge: 40a451b1ac28 e5c371b24b35 Author: Povilas Kanapickas Date: 2020-04-22 20:09:23 +0000 Merge branch 'genesys-cleanup-motor-dpi' into 'master' genesys: Merge motor optical_ydpi to base_ydpi See merge request sane-project/backends!417 commit e5c371b24b35f70e2f082078a6f2e0b5aaf30e6f Author: Povilas Kanapickas Date: 2020-04-22 08:28:20 +0300 genesys: Merge motor optical_ydpi to base_ydpi commit 40a451b1ac28f75c69f56f1c62d573b4727ee3b8 Author: Alex Belkin Date: 2020-04-22 16:15:33 +0300 xerox_mfp: Mark Xerox WorkCentre 3225 as supported Reported by Darodaros in issue sane-project/website#26. commit 5cf347d34d7e470d3376e89daef0e9fbd0e78ed5 Merge: 27e3d213512c 32083172ea58 Author: Ordissimo Date: 2020-04-21 13:06:27 +0000 Merge branch 'escl-ADF-only-scans-first-page' into 'master' Moves to the next page. See merge request sane-project/backends!416 commit 32083172ea58ce44a402ef2372afc1c82f8d19e9 Author: thierry1970 Date: 2020-04-21 14:49:40 +0200 Allows you to move to the next page. commit 27e3d213512c72bc94fdc1d33c673996a74b0f62 Merge: 5eec2b4a0c44 111d8d2aabd7 Author: Ordissimo Date: 2020-04-21 11:05:21 +0000 Merge branch 'escl-invalide-image-with-scanimage' into 'master' eSCL : Fix invalide image with scanimage. See merge request sane-project/backends!415 commit 111d8d2aabd72db62a3a8bc67475f1045b050d5b Author: thierry1970 Date: 2020-04-21 12:47:07 +0200 Fix invalide image with scanimage. commit 5eec2b4a0c444e0a56c1b1a2a383b211574a14a7 Merge: a00b8079ee68 53162b4cad32 Author: Povilas Kanapickas Date: 2020-04-19 23:35:56 +0000 Merge branch 'genesys-cleanup-gl841' into 'master' genesys: Cleanup gl841 code See merge request sane-project/backends!414 commit 53162b4cad323316678c340cfca9f13461dab03f Author: Povilas Kanapickas Date: 2020-04-20 00:35:57 +0300 genesys: Remove no longer used motor action commit af28c2d56c612cfdb21e3884fc26e436c8a8e21d Author: Povilas Kanapickas Date: 2020-04-20 00:35:56 +0300 genesys: Reuse scanner_move_back_home() on gl841 commit d9b49e567c0ccbe8d11794a39f3b7be9fc3ca8af Author: Povilas Kanapickas Date: 2020-04-20 00:35:55 +0300 genesys: Simplify motor direction setup on gl841 commit 8412861a662b13976ec36808b772d761ae490d6c Author: Povilas Kanapickas Date: 2020-04-20 00:35:54 +0300 genesys: Support reverse scan direction on gl841 commit f965c2ef255af3cb84dd8ab79a6f61576ab58017 Author: Povilas Kanapickas Date: 2020-04-20 00:35:53 +0300 genesys: Reuse scanner_stop_action() on gl841 commit 98ee100237f50a4cfadea060caf84cb763d50781 Author: Povilas Kanapickas Date: 2020-04-20 00:35:52 +0300 genesys: Fix scanner_is_motor_stopped() on gl841 commit a00b8079ee68cc020a4b0ad10a5a775adac2c687 Merge: 1938bcfbeb68 b68fd960e302 Author: Povilas Kanapickas Date: 2020-04-19 20:37:19 +0000 Merge branch 'genesys-cleanup-gl646' into 'master' genesys: Cleanup gl646 code See merge request sane-project/backends!413 commit b68fd960e302806dc2c2edd4be477c9f5fb9f1b2 Author: Povilas Kanapickas Date: 2020-04-17 06:19:22 +0300 genesys: Simplify sensor setup on gl646 commit a3ad02c8bfbff31eb431aee2da562f21fce4e193 Author: Povilas Kanapickas Date: 2020-04-17 06:19:21 +0300 genesys: Remove sensor setting duplication on gl646 commit 1938bcfbeb6869b1c324c4ba59315ffd8de5d68f Merge: ff2ba89b068c ebfa38a73293 Author: Povilas Kanapickas Date: 2020-04-19 19:04:52 +0000 Merge branch 'genesys-cleanup' into 'master' genesys: Miscellaneous cleanup See merge request sane-project/backends!412 commit ebfa38a732930006ded4601de769ea986d195cee Author: Povilas Kanapickas Date: 2020-04-17 05:38:47 +0300 genesys: Map motor flags to scan flags commit 4c95e94f9b73575da697e7e1b5dfa42414ca002d Author: Povilas Kanapickas Date: 2020-04-17 05:38:46 +0300 genesys: Move private functionality out of headers commit afa96a227df1d44da1bfe6e41e49b997b059f00e Author: Povilas Kanapickas Date: 2020-04-17 05:38:45 +0300 genesys: Reduce duplication of sensor setup functionality commit d0855e513bfcc8f04d951753e61579ef257c9546 Author: Povilas Kanapickas Date: 2020-04-17 05:38:44 +0300 genesys: Remove unused #defines commit edda9bff25930455a039335c218c9738b713d3ef Author: Povilas Kanapickas Date: 2020-04-17 05:38:43 +0300 genesys: Simplify creation of command set commit c0deb9b2c4e4d492cb7749a13df46e11aa1c7c68 Author: Povilas Kanapickas Date: 2020-04-17 05:38:42 +0300 genesys: Use consistent model name for Canon CanoScan 5600F commit ff2ba89b068cb1e7d545fe5e697ee398292f527f Merge: 59743d7f296f ad4c8f5453a3 Author: Povilas Kanapickas Date: 2020-04-19 18:45:03 +0000 Merge branch 'genesys-cleanup' into 'master' genesys: Reduce duplication in led_calibration See merge request sane-project/backends!411 commit ad4c8f5453a3b46cf5697cea517eb5db0098f445 Author: Povilas Kanapickas Date: 2020-04-17 05:23:07 +0300 genesys: Reduce duplication in led_calibration commit 59743d7f296f439752c12ec9fbe334eeb3e7c548 Merge: 3be9ac99a9f6 4aace34caa99 Author: Povilas Kanapickas Date: 2020-04-19 18:21:13 +0000 Merge branch 'genesys-cleanup-gl841' into 'master' genesys: Cleanup gl841 code See merge request sane-project/backends!410 commit 4aace34caa99b3199e4a5754a9dc28c57b5d00b3 Author: Povilas Kanapickas Date: 2020-04-17 05:18:01 +0300 genesys: Simplify led calibration on gl841 commit f6b606503c1127e0a4fea1ccc7e6c736f099f4d8 Author: Povilas Kanapickas Date: 2020-04-17 05:18:00 +0300 genesys: Remove out of date comments on gl841 commit 6b49c6656c826ceec505de5faccc0edc69e2ff7b Author: Povilas Kanapickas Date: 2020-04-17 05:17:59 +0300 genesys: Simplify motor setup on gl841 commit 2a9254c9988426b0df7c3968f6607eb69ce84056 Author: Povilas Kanapickas Date: 2020-04-17 05:17:58 +0300 genesys: Verify that all sensors have required registers on gl841 commit 608cd18282317b58341afc297a9fca39800906d5 Author: Povilas Kanapickas Date: 2020-04-17 05:17:57 +0300 genesys: Rewrap custom register lists in sensor definitions on gl841 commit 75f5ec405849ff2689ea20ba00cc90ee8b0de416 Author: Povilas Kanapickas Date: 2020-04-17 05:17:56 +0300 genesys: Remove duplicate initialization during scanner setup on gl841 commit f37c3c55e4aeda487c739c2c5fb0f995ba9a7c84 Author: Povilas Kanapickas Date: 2020-04-17 05:17:55 +0300 genesys: Simplify sensor setup on gl841 commit 3be9ac99a9f6adae8adee4a72ee022ccf7745536 Merge: 3ba284eeec8f 6fcf2c3f1342 Author: Olaf Meeuwissen Date: 2020-04-18 04:15:20 +0000 Merge branch 'document-canon-lide70-addition' into 'master' Document the addition of the canon_lide70 backend See merge request sane-project/backends!409 commit 6fcf2c3f13428290f5ce540fd7eb2a5061f94f52 Author: Olaf Meeuwissen Date: 2020-04-18 12:59:07 +0900 Document the addition of the canon_lide70 backend commit 3ba284eeec8faecae4cf45b0d2efc755b08b76e3 Author: Rolf Bensch Date: 2020-04-15 21:50:05 +0200 pixma: backend version 0.27.7 commit 598b836c05c9b5a671eda3a096823aad0b7690e2 Merge: 4d01195d2270 e17646a869b7 Author: Rolf Bensch Date: 2020-04-15 19:47:27 +0000 Merge branch 'k3a-pixma' into 'master' pixma: parse XML response sent by the scanner and present tips to the user Closes issue sane-project/backends#70 See merge request sane-project/backends!386 commit e17646a869b7a0b1f3c80f9690f8b834d5788d99 Author: Mario Hros Date: 2020-04-01 18:01:24 +0200 pixma: parse XML response sent by the scanner and present tips to the user commit 4d01195d22704502d860fe51a0b171c435ac799d Merge: 549e10314f9e 0638e84d1358 Author: Povilas Kanapickas Date: 2020-04-14 21:37:03 +0000 Merge branch 'genesys-hp-4850' into 'master' genesys: Fix shading offsets on HP 4850 See merge request sane-project/backends!406 commit 0638e84d1358f120c049289a3cef8bdc5732b802 Author: Povilas Kanapickas Date: 2020-04-15 00:21:05 +0300 genesys: Fix shading offsets on HP 4850 commit 549e10314f9e4828b6358e3f91b69ace8cec78e5 Merge: ea7f8a58f224 6fc6fb7ea2e6 Author: Povilas Kanapickas Date: 2020-04-14 21:35:50 +0000 Merge branch 'genesys-calibration' into 'master' genesys: Fix multiple issues in calibration code paths See merge request sane-project/backends!407 commit 6fc6fb7ea2e68da4443dc44fba06757452e3f82b Author: Povilas Kanapickas Date: 2020-04-15 00:16:01 +0300 genesys: Load calibration data in sane_start() not in sane_init() commit a595875d8748668f4c21eca0e003db148d2bc4f8 Author: Povilas Kanapickas Date: 2020-04-15 00:15:21 +0300 genesys: Bump calibration version commit 7304a4bb783c8e37d01cd796915f48d09b55b0ee Author: Povilas Kanapickas Date: 2020-04-15 00:15:10 +0300 genesys: Fix out of bounds access during calibration on gl843 commit ea7f8a58f224ed3eae3563575fc11eb502725531 Merge: 5d08b64bc6f0 617e83b4e502 Author: Povilas Kanapickas Date: 2020-04-13 15:21:23 +0000 Merge branch 'genesys-descriptions' into 'master' doc: Update descriptions of supported genesys scanners See merge request sane-project/backends!405 commit 617e83b4e502d79aa0541141f1d5191dccca2d7a Author: Povilas Kanapickas Date: 2020-04-13 18:05:36 +0300 doc: Update descriptions of supported genesys scanners commit 5d08b64bc6f031bbb58e6ccaf54fbb3400bb3395 Merge: 0932b751e0ee eec083f46a1e Author: Povilas Kanapickas Date: 2020-04-13 15:09:55 +0000 Merge branch 'genesys-4400f-8400f-8600f-bugs' into 'master' genesys: Fix various bugs on 4400F, 8400F, 8600F See merge request sane-project/backends!404 commit eec083f46a1e7ccc63441642096d2b35c238ede7 Author: Povilas Kanapickas Date: 2020-04-13 07:38:08 +0300 genesys: Remove startup position hack on 4400F 4800dpi scans commit 1125cf338a11991b7e6b6284ea90f5fb1bcbdd3c Author: Povilas Kanapickas Date: 2020-04-13 07:38:07 +0300 genesys: Update pixel start offsets on 4400F commit 60da130402d8616d18244b32dc13390e05e882e0 Author: Povilas Kanapickas Date: 2020-04-13 07:38:06 +0300 genesys: Fix Y scan area offset on 4400F commit ea781b6b07956b0aa26cb190674d9e20fd85ec8d Author: Povilas Kanapickas Date: 2020-04-13 07:38:05 +0300 genesys: Improve head positioning during calibration on 8600F TA scans commit b788f2de8f948aff02d13043d7801ff1d7efa9d9 Author: Povilas Kanapickas Date: 2020-04-13 07:38:04 +0300 genesys: Remove startup position hack on 8600F 4800dpi scans commit aec867d1e8edc47c726453b40d349d1eeeb6356b Author: Povilas Kanapickas Date: 2020-04-13 07:38:03 +0300 genesys: Support negative output_pixel_offset commit f03b7ad29f4d6d7d4b15ab3e7d9addf22eb06d80 Author: Povilas Kanapickas Date: 2020-04-13 07:38:02 +0300 genesys: Fix alignment of transparency scans of different dpi on 8600F commit 82b42862abfb8f0b2473198486c416fe2aef9d25 Author: Povilas Kanapickas Date: 2020-04-13 07:38:01 +0300 genesys: Fix calibration on 8400F commit 6fea625ad6030e78218f089ff344a93baec4efaf Author: Povilas Kanapickas Date: 2020-04-13 07:38:00 +0300 genesys: Support custom shading offset on gl843 commit 4b796205254c0b41370e806b07990117d1ea4444 Author: Povilas Kanapickas Date: 2020-04-13 07:37:59 +0300 genesys: Fix shading resolution on 8400F 400dpi commit 664c07c01a4502546ac89211211dcb8261c8877f Author: Povilas Kanapickas Date: 2020-04-13 07:37:58 +0300 genesys: Add a way to configure shading pixel offset commit df0fccf899d13269bd24952da30e17543f38dd78 Author: Povilas Kanapickas Date: 2020-04-13 07:37:57 +0300 genesys: Fix incorrect handling of start position on gl843 commit 0932b751e0ee962650eb1bdea2b5fab79b019dc4 Merge: 1fc00f2b962f 922cabc0befe Author: Povilas Kanapickas Date: 2020-04-13 14:04:43 +0000 Merge branch 'genesys-output-pixel-offset' into 'master' genesys: Allow to specify output pixel offset within model configuration See merge request sane-project/backends!403 commit 922cabc0befe832a965e67c866d3862f172978ba Author: Povilas Kanapickas Date: 2020-04-13 07:23:43 +0300 genesys: Remove no longer used ccd_start_xoffset commit 87f5637a47f52499a2d87b12b5903db8b401c20d Author: Povilas Kanapickas Date: 2020-04-13 07:23:42 +0300 genesys: Cleanup pixel position handling on gl124 commit bae938b4be6d74659201a68d40b0977df45de1d9 Author: Povilas Kanapickas Date: 2020-04-13 07:23:41 +0300 genesys: Use output_pixel_offset for CCD offsets on gl847 commit eca7a5ab91ef24fb172ffc683be4dcae1eeb5128 Author: Povilas Kanapickas Date: 2020-04-13 07:23:40 +0300 genesys: Use output_pixel_offset for CCD offsets on gl845/gl846 commit e5c46aee41083d5f1449dad6c1558560f3503755 Author: Povilas Kanapickas Date: 2020-04-13 07:23:39 +0300 genesys: Use output_pixel_offset for CCD offsets on gl843 commit dc00ab867bc80d84c5e2691e2a0f85158b480401 Author: Povilas Kanapickas Date: 2020-04-13 07:23:38 +0300 genesys: Use output_pixel_offset for CCD offsets on gl841 commit 648a24830f04ea6f972094828bdb9335b6246ac4 Author: Povilas Kanapickas Date: 2020-04-13 07:23:37 +0300 genesys: Use output_pixel_offset for CCD offsets on gl646 commit 1fc00f2b962fcda4fbe3f87df9ce43961a360421 Merge: db828453fae7 3693f4b12d3d Author: Povilas Kanapickas Date: 2020-04-13 13:33:50 +0000 Merge branch 'genesys-cleanup-pixel-position-handling' into 'master' genesys: Cleanup pixel position handling See merge request sane-project/backends!401 commit 3693f4b12d3dcbc47a6d60ed648c178f8018c4f4 Author: Povilas Kanapickas Date: 2020-04-13 07:19:59 +0300 genesys: Simplify use of pixel_endx on gl124 commit cdefc1cae501503fed0aee43036ee4bb37386690 Author: Povilas Kanapickas Date: 2020-04-13 07:19:58 +0300 genesys: Simplify pixel position handling on gl646 commit e920d1bbcea1dd6c2daf2014c775c3d472df42ed Author: Povilas Kanapickas Date: 2020-04-13 07:19:57 +0300 genesys: Add a way to specify output pixel offset commit 94f130cdfa8c56bef77f24cce2a6d587fc426292 Author: Povilas Kanapickas Date: 2020-04-13 07:19:56 +0300 genesys: Simplify pixel position calculation on gl841 commit 8b37d4b15e9a51d9d0412331b2ec74ac54195e24 Author: Povilas Kanapickas Date: 2020-04-13 07:19:55 +0300 genesys: Remove handling of XCORRECTION on gl646 commit 2bec01cf60a487f0752205d2e44586d18434b83d Author: Povilas Kanapickas Date: 2020-04-13 07:19:54 +0300 genesys: Simplify optical pixel calculations on gl646 commit f4b60c48bba4d2c9b83b1d47e9115c2b90177a2f Author: Povilas Kanapickas Date: 2020-04-13 07:19:53 +0300 genesys: Remove unnecessary pixel averaging handling code on gl841 commit 1f43e6a7f89e299e72f3b19f4ec42a8a2259ac65 Author: Povilas Kanapickas Date: 2020-04-13 07:19:52 +0300 genesys: Remove unused handling of partial resolutions commit 71f3dd8643667cf05bbc48203a31aefe18643e3c Author: Povilas Kanapickas Date: 2020-04-13 07:19:51 +0300 genesys: Make definition of optical_resolution more consistent on gl646 commit 6db2f94598a4d03d444bd08f5834f0afa8938648 Author: Povilas Kanapickas Date: 2020-04-13 07:19:50 +0300 genesys: Remove unused configuration of ccd_size_divisor commit 12269c5ad47471706259a0222561df9b90ff05e7 Author: Povilas Kanapickas Date: 2020-04-13 07:19:49 +0300 genesys: Prefer not to use ccd_size_divisor on gl841 commit 48c377b5f0a84887338fdfee2c74ead6cc08271f Author: Povilas Kanapickas Date: 2020-04-13 07:19:48 +0300 genesys: Improve description of SetupParams::pixels commit db828453fae7d9d8973caa0d582e61cc4bd05b4a Merge: 4c254e3c3d4b 901827c8c72a Author: Povilas Kanapickas Date: 2020-04-13 13:31:04 +0000 Merge branch 'genesys-canoscan-700f' into 'master' genesys: Fix support for CanoScan 700F See merge request sane-project/backends!402 commit 901827c8c72aef642e025d054546e80bacf15bd5 Author: Povilas Kanapickas Date: 2020-04-13 07:19:29 +0300 genesys: Disable currently unsupported resolutions on LiDE 700F commit 35e48da4a89355750449730c62fd1da1a9d129de Author: Povilas Kanapickas Date: 2020-04-13 07:19:28 +0300 genesys: Fix invalid home position reporting on LiDE 700F commit 4c254e3c3d4b1567066a4ec0f5e84509e936a6f3 Merge: 19d7b02d4adf 6a0e0eb5683f Author: Ordissimo Date: 2020-04-12 09:01:51 +0000 Merge branch 'escl-adf-support' into 'master' Escl adf support See merge request sane-project/backends!300 commit 6a0e0eb5683f5a0654ab697ec96cdcf60e6c7b60 Author: Thierry HUCHARD Date: 2020-04-12 10:28:55 +0200 Fix build commit 3d02d2f1be192831b48c5abf686e10feaebba8e7 Author: Thierry HUCHARD Date: 2020-04-12 10:17:33 +0200 Changes the status when the trays are empty. commit ba5f99273799e7408e919f627f68523c0ec3be13 Author: Thierry HUCHARD Date: 2020-04-04 17:11:16 +0200 Fix all conflicts. commit 4ed0b0fd59e5fd363967526de84b17fdeab690fc Author: Thierry HUCHARD Date: 2020-04-04 16:29:41 +0200 Fix conflicts commit fa75c94b241743c48c66145c0e426883ad20420f Merge: 59824f3d8343 f8193494ad23 Author: Ordissimo Date: 2020-04-04 14:04:18 +0000 Merge branch 'master' into 'escl-adf-support' # Conflicts: # backend/escl/escl.c # backend/escl/escl.h # backend/escl/escl_capabilities.c # backend/escl/escl_crop.c # backend/escl/escl_newjob.c # backend/escl/escl_scan.c # backend/escl/escl_status.c commit 59824f3d8343410151eb8d3664d0f3b69935b698 Author: thierry1970 Date: 2020-04-03 09:37:49 +0200 Fix style. commit 8f5f815be107d6dcbf15430b654395e7e6cf20ad Author: thierry1970 Date: 2020-04-03 09:33:55 +0200 Moves to the next page for the ADF. commit 4f8a6991a97606a30f6187dcff030366e5d738aa Author: thierry1970 Date: 2020-04-03 09:23:49 +0200 Calculates the cropping offset with the ratio. commit dd9e30c8148afc91573080e1bde08765c6f64544 Author: thierry1970 Date: 2020-04-03 09:22:51 +0200 Adjusts the cropping offset. commit 81325dc773ef9f0aab8ea40bc81e05036cfbe25a Author: Thierry HUCHARD Date: 2020-03-22 16:41:53 +0100 Fix build. commit f4e6677fc1f1fa3d4862ac5b45f3428d434dd2a3 Author: Thierry HUCHARD Date: 2020-03-22 16:29:34 +0100 Fix build. commit 79f7463366aaaf8b9736833a63916931884ebf9c Author: Thierry HUCHARD Date: 2020-03-22 16:14:23 +0100 Refactoring the ADF mode. commit 7caf9287971b1f59c478e38e87ff7e1750f55e46 Author: Thierry HUCHARD Date: 2020-03-17 23:42:53 +0100 Correction of the return value of the scanner status commit 136024355ddd87fa462dad840c44da2cfdd4b384 Author: Thierry HUCHARD Date: 2020-03-17 11:04:18 +0100 Fix open device. commit 505a2c220893963f85f11d5385c6ce9faf05f096 Author: Thierry HUCHARD Date: 2020-03-17 09:51:34 +0100 Fix typo. commit 41541da8ff2990cf6124c61a74ed679a2773ae46 Author: Thierry HUCHARD Date: 2020-03-16 00:37:28 +0100 Fix few arguments to function 'print_xml_s' commit 9315ad2ce6843eabfe3afcdaa667ca94d81e40ef Author: Thierry HUCHARD Date: 2020-03-16 00:29:37 +0100 Fix build. commit f62f9be1dd3b5a98300910e1c2ab8c0a8a4d4736 Author: Thierry HUCHARD Date: 2020-03-16 00:21:49 +0100 Fix variable type. commit 682d238c08722e2c34c6b31a336cc293d563b444 Author: Thierry HUCHARD Date: 2020-03-16 00:13:42 +0100 Fix build. commit 0440234c36a031b9a3f76d72b89d169faa42908e Author: Thierry HUCHARD Date: 2020-03-16 00:01:58 +0100 Fix function proto. commit 54136b8deb18dcefb8b80029f4f1823c58eed315 Author: Thierry HUCHARD Date: 2020-03-15 23:50:40 +0100 If Adf, acquisition of the next page, if it exists. commit cf94e585f54cee1b0e9f91290cf5f26525106e3c Author: Thierry HUCHARD Date: 2020-03-15 16:57:14 +0100 Full definition of ADF support. commit 22416dece7aa6e7dd4aa563416a048fcc447f668 Author: Thierry HUCHARD Date: 2020-03-14 16:42:15 +0100 Fix style. commit beaba443855b40d58ac1c5263a064cafee492d5a Merge: 749fe19febba 93df3050c45d Author: Thierry HUCHARD Date: 2020-03-14 16:38:40 +0100 Merge branch 'escl-adf-support' of gitlab.com:sane-project/backends into escl-adf-support commit 93df3050c45da64747e8f35712fe679809b87d9a Author: Thierry HUCHARD Date: 2020-01-12 13:48:59 +0100 Resolv conflict. commit adc38bce0ebdbb73d406996208aaf0b956a47b9c Merge: 899d9e90bc18 7b45c89b023d Author: Thierry HUCHARD Date: 2020-01-12 13:23:02 +0100 Merge branch 'master' into escl-adf-support commit 899d9e90bc1870125ca0fd0520dd903db9187488 Merge: b2aea45ca416 aedebecef790 Author: Thierry HUCHARD Date: 2020-01-10 21:00:56 +0100 Merge branch 'master' into escl-adf-support commit b2aea45ca416c64b77a5dbfdd59dcc6ce82be282 Merge: 3c73e7031dae 7ae6ccfb3201 Author: Thierry HUCHARD Date: 2020-01-10 20:58:32 +0100 Merge branch 'escl-adf-support' of gitlab.com:sane-project/backends into escl-adf-support commit 7ae6ccfb32010eaa87efe6f158f927e8852c8701 Author: thierry1970 Date: 2020-01-09 16:58:12 +0100 Call explicitly cancel. commit 3c73e7031daeb24866a7e7e08a0537376ebfc440 Merge: 579e79da148c 28264dcfd825 Author: Thierry HUCHARD Date: 2020-01-08 20:55:28 +0100 Merge branch 'master' into escl-adf-support commit 579e79da148c85ac965cea01d15944c55f08292f Author: Ordissimo Date: 2020-01-07 19:25:38 +0000 Remove debug. commit 4f7fc379daa0552bc10a3a12b5b3e0560ad431bd Author: thierry1970 Date: 2020-01-07 16:09:38 +0100 Add support adf. commit 749fe19febbaaf79558f508b1c117d3cdc4761a6 Author: Thierry HUCHARD Date: 2020-03-14 16:19:18 +0100 Fix build commit 1b3f0c9bc0e3e90fa39cb2598bc1c4d31c10806d Author: thierry1970 Date: 2020-01-09 16:58:12 +0100 Call explicitly cancel. commit 2c12635b2360a757d4180a08a7222d5d388c8767 Author: Ordissimo Date: 2020-01-07 19:25:38 +0000 Remove debug. commit fd0a8a002602a8d480827210956b70c03f601f40 Author: Thierry HUCHARD Date: 2020-03-14 15:41:15 +0100 Add support adf. commit 19d7b02d4adf02e4fafd51e56f377d216cbb95ba Merge: 3b1d7d59c146 7fbc7dda5457 Author: Povilas Kanapickas Date: 2020-04-11 13:16:48 +0000 Merge branch 'genesys-fix-test-mode' into 'master' genesys: Fix test mode (part 2) See merge request sane-project/backends!400 commit 7fbc7dda5457247f966108c2393b3884d80b58c9 Author: Povilas Kanapickas Date: 2020-04-11 15:50:03 +0300 genesys: Readd missed comment commit 3b1d7d59c1460146dd581d9ac659c9b918ab731e Merge: 13f943ec12c0 db50c9113235 Author: Povilas Kanapickas Date: 2020-04-11 12:38:19 +0000 Merge branch 'genesys-reg-get-address' into 'master' genesys: Remove uses of sanei_genesys_get_address() See merge request sane-project/backends!399 commit db50c91132354e90c9fca3eddd10724da99d5bd8 Author: Povilas Kanapickas Date: 2020-04-05 06:19:38 +0300 genesys: Remove uses of sanei_genesys_get_address() commit 13f943ec12c0c9b25e3b3fb6bfdb9beae4a87a1a Merge: db6b862d7071 5ffd4039de04 Author: Povilas Kanapickas Date: 2020-04-11 12:37:30 +0000 Merge branch 'genesys-fix-test-mode' into 'master' genesys: Fix test mode See merge request sane-project/backends!398 commit 5ffd4039de04b4a47ad0a5b6a7639bab11e14493 Author: Povilas Kanapickas Date: 2020-04-11 15:20:30 +0300 genesys: Fix crash when exact usb device is not specified commit 55972a3efaa180bafb5852124f6d6ecf05f2dd0b Author: Povilas Kanapickas Date: 2020-04-11 15:20:29 +0300 genesys: Fix crash during testing mode commit db6b862d7071d983d23b80c910bd634eabb3e552 Merge: 11cace00a8ac f48f0bf7d161 Author: Povilas Kanapickas Date: 2020-04-09 22:50:46 +0000 Merge branch 'genesys-plustek' into 'master' genesys: Fix scan area size on OpticFilm 8200i and 7400 See merge request sane-project/backends!397 commit f48f0bf7d161be018ce0f41eeec58f45f3fc564e Author: Povilas Kanapickas Date: 2020-04-10 01:14:14 +0300 genesys: Fix scan area size on OpticFilm 8200i and 7400 commit 11cace00a8acb40b2ee2630c6db198d8cae5ed5a Merge: 33b605279048 ea94970b6a2d Author: Povilas Kanapickas Date: 2020-04-09 21:13:27 +0000 Merge branch 'genesys-warmup' into 'master' genesys: Fix and enable lamp warmup before first scan See merge request sane-project/backends!396 commit ea94970b6a2d22cec3af797bbce75120ef065259 Author: Povilas Kanapickas Date: 2020-04-09 23:57:13 +0300 genesys: Enable lamp warmup on scanners that need it commit f22dd4d7b204e1b1e328bc2234b7a022f88ede57 Author: Povilas Kanapickas Date: 2020-04-09 23:57:12 +0300 genesys: Move lamp to transparency adapter if needed during lamp warmup commit 21d9079eb760bceba9e95e9afb957def63ffcecb Author: Povilas Kanapickas Date: 2020-04-09 23:57:11 +0300 genesys: Invert meaning of ModelFlag::SKIP_WARMUP commit 8b653560476b5c74cc793880432de410c40a20ed Author: Povilas Kanapickas Date: 2020-04-09 23:57:10 +0300 genesys: Fix calculation of averages during lamp warmup commit c2480ac0649aaa5af1540695e38e9b78a2ba18d5 Author: Povilas Kanapickas Date: 2020-04-09 23:57:09 +0300 genesys: Simplify calculation of warmup success criteria commit 40cb193cc37d1a348fb621511b86509280d63d2e Author: Povilas Kanapickas Date: 2020-04-09 23:57:08 +0300 genesys: Fix debug messages during lamp warmup on 16-bit scans commit 0ab6e786579441ac0ad87b4b67580a886c4ec151 Author: Povilas Kanapickas Date: 2020-04-09 23:57:07 +0300 genesys: Compare all subsequent scans during lamp warmup commit fa594d965d3903f258728ebd6eaaa7d3c9235332 Author: Povilas Kanapickas Date: 2020-04-09 23:57:06 +0300 genesys: Correctly initialize warmup registers in transparency scans commit 6420e00a38bbd239f33d407ad66e0b96e9178ad8 Author: Povilas Kanapickas Date: 2020-04-09 23:57:05 +0300 genesys: Don't hardcode depth during warmup commit 4586ae4e563edba12fed630815360593a72a6ce5 Author: Povilas Kanapickas Date: 2020-04-09 23:57:04 +0300 genesys: Increase consistency of lamp warmup handling commit 33b605279048ef72354efb2ef5f065b889c95289 Merge: 5cbb9eaae5d8 15b664fa3235 Author: Povilas Kanapickas Date: 2020-04-09 21:11:29 +0000 Merge branch 'genesys-8600f-ta-position' into 'master' genesys: Fix position of transparency area on 8600F See merge request sane-project/backends!395 commit 15b664fa3235007130ad396161d9d3892c97e674 Author: Povilas Kanapickas Date: 2020-04-09 23:55:57 +0300 genesys: Fix position of transparency area on 8600F commit 5cbb9eaae5d833b1c7d7898d2df2bd8239b9b832 Merge: 14e9fb18773c 40b4852eb710 Author: Povilas Kanapickas Date: 2020-04-09 21:09:03 +0000 Merge branch 'genesys-cleanup-device-init' into 'master' genesys: Cleanup device initialization See merge request sane-project/backends!394 commit 40b4852eb710673cd3d26c607a93e0a12f60376b Author: Povilas Kanapickas Date: 2020-04-09 23:53:30 +0300 genesys: Remove unused argument out of sanei_genesys_asic_init commit 0992b379c88d3f6cb740460c26dad91fca9d82ad Author: Povilas Kanapickas Date: 2020-04-09 23:53:29 +0300 genesys: Remove unnecessary scan out of gl841 initialization code commit 57b0873a8303a0696a2b6416725a19f78cde2a47 Author: Povilas Kanapickas Date: 2020-04-09 23:53:28 +0300 genesys: Don't move scanner head forward on startup on gl843 commit 14e9fb18773ce96603dd9c0f98e9e61aecf7eb75 Merge: f8193494ad23 0d1542cbe6f3 Author: Povilas Kanapickas Date: 2020-04-09 21:06:42 +0000 Merge branch 'genesys-fix-usb-id-init' into 'master' genesys: Fix initialization of USB IDs See merge request sane-project/backends!393 commit 0d1542cbe6f3c85693e3495f032f1887cbda6c8e Author: Povilas Kanapickas Date: 2020-04-09 23:50:48 +0300 genesys: Fix bcdDevice filtering commit bb318cf8f31ea84540f80b32ef4558c35a494cf5 Author: Povilas Kanapickas Date: 2020-04-09 23:50:47 +0300 genesys: Simplify USB id retrieval commit 13dffa0915fb76ad730fb558e451589fc6c7194f Author: Povilas Kanapickas Date: 2020-04-09 23:50:46 +0300 genesys: Defer evaluation of bcdDevice until open() commit 0e8f9c935eb764b5f7095ff4d9cdc82a8a5811c1 Author: Povilas Kanapickas Date: 2020-04-09 23:50:45 +0300 genesys: Correctly initialize test USB device commit f8193494ad23dce78a2e2bdfa7825715ec29d2e0 Merge: 9723a83b2c35 bd6a2551a5db Author: Olaf Meeuwissen Date: 2020-04-04 04:35:45 +0000 Merge branch 'newyearsbranch' into 'master' canon_lide70-sequel See merge request sane-project/backends!315 commit bd6a2551a5db9f771ace2362dbb80991ac86a3a9 Author: pimvantend Date: 2020-03-10 21:00:03 +0100 canon_lide70-common.c eliminate two spaces near new io_error commit a0ddf165a77e08f970428d34e1b6bd1869e670b0 Author: pimvantend Date: 2020-03-10 20:48:10 +0100 canon_lide70-common.c adding empty line commit 775a26471cfff2a80ad45104b2c90658fe001f3b Author: pimvantend Date: 2020-03-10 20:42:31 +0100 canon_lide70-common.c eliminate u_long commit 04fc970b2ed55df3ad1d0d253f4594c114088089 Merge: e6b1647a1b9b 70070f7be2b5 Author: pimvantend Date: 2020-03-10 20:09:49 +0100 Merge branch 'master' of https://gitlab.com/sane-project/backends into newyearsbranch resolve po/POTFILES.in commit e6b1647a1b9bfe7c9b0974404c9358a737736a90 Author: pimvantend Date: 2020-03-10 19:08:27 +0100 canon_lide70 extended canon_lide70.desc commit 70b654239e213eb261d890866b9dfed1f3323bb9 Author: pimvantend Date: 2020-03-10 19:03:33 +0100 canon_lide70 shortened canon_lide70.desc commit d3a8d9ef89d3b209762cf325c9ded54ee4c3a3c4 Author: Pim van Tend Date: 2020-02-04 19:20:40 +0000 Update po/POTFILES.in to the current mainstream sane version, adding canon_lide70.c commit ebd45d4cf87955c5eb59e49d2a7190fd60d5f3ef Author: pimvantend Date: 2020-01-04 16:18:57 +0100 canon_lide70 adding/modifying twelve files to provide the new canon_lide70 backend commit d2d878d023bb65ad8ca2f9bd3c798961f7b36381 Author: pimvantend Date: 2020-01-01 13:00:21 +0100 precursor to the canon_lide70 backend left by @jrernst at http://www.juergen-ernst.de/info_sane.html this version of the backend does not work commit 9723a83b2c35b1591198ee809518e48c1a5ba076 Merge: 911e40ba4eda f39a43953b57 Author: Olaf Meeuwissen Date: 2020-04-02 11:05:58 +0000 Merge branch 'zeroheure-master-patch-67644' into 'master' document JPEG compression level See merge request sane-project/backends!389 commit f39a43953b5700ac7dd31cc97d04a65c62524e3d Author: Xavier Brochard Date: 2020-04-01 20:44:49 +0000 document JPEG compression level commit 911e40ba4eda964927f3adacb766ed45e60729be Merge: 6b318d808229 7f10387ab089 Author: Ordissimo Date: 2020-04-02 09:08:24 +0000 Merge branch 'escl-fix-ratio-crop' into 'master' Escl fix ratio crop See merge request sane-project/backends!390 commit 7f10387ab089b35e042d51b82afbbef913c9b179 Author: thierry1970 Date: 2020-04-02 10:41:59 +0200 Calculates the cropping offset with the ratio. commit 6c0c360ce7b53fb195d9462649c734d83d13a42a Author: thierry1970 Date: 2020-04-02 10:40:27 +0200 Adjusts the cropping offset. commit 6b318d80822955cb2aafb443652ee17aa15ebb71 Merge: 101d27e8db7b 8725559d4bc7 Author: Povilas Kanapickas Date: 2020-04-01 18:45:49 +0000 Merge branch 'genesys-plustek' into 'master' genesys: Update Plustek model status See merge request sane-project/backends!388 commit 8725559d4bc7a37122bbdc2250eb7a5c67e9b1b2 Author: Povilas Kanapickas Date: 2020-04-01 21:30:01 +0300 doc: Update plustek model status commit 101d27e8db7bd4665ab366079a1b7561ed74ce4d Merge: 726e3e540768 0d5f5b54ed35 Author: Povilas Kanapickas Date: 2020-04-01 18:17:03 +0000 Merge branch 'genesys-plustek' into 'master' genesys: Enable support for several Plustek OpticFilm scanner models See merge request sane-project/backends!387 commit 0d5f5b54ed35ae21b1da5a14f725dba569ff998f Author: Povilas Kanapickas Date: 2020-04-01 20:57:01 +0300 genesys: Enable bcdDevice filtering commit 54f3b8efd17d2be6831f519c22247347a6ddb1ca Author: Povilas Kanapickas Date: 2020-04-01 20:57:00 +0300 genesys: Add support for early version of Plustek OpticFilm 7600i commit bfd0b82d2b4f2770e8cc72af0cbde12d0f3210f4 Author: Povilas Kanapickas Date: 2020-04-01 20:56:59 +0300 genesys: Add support for Plustek OpticFilm 8100 commit a2a121ab8c53bc3f2e1523f633ddbb1bc6e04815 Author: Povilas Kanapickas Date: 2020-04-01 20:56:58 +0300 genesys: Add support for late version of Plustek OpticFilm 7600i commit ea128674a65213fb9850ca7d2c6a4f594709f525 Author: Povilas Kanapickas Date: 2020-04-01 20:56:57 +0300 genesys: Add support for early version of Plustek OpticFilm 7400 commit 4a1667865a50d6f34a70e2891e89830588b1a6ff Author: Povilas Kanapickas Date: 2020-04-01 20:56:56 +0300 genesys: Add support for Plustek OpticFilm 7200 (v2) commit 726e3e540768279edad11a2b5d7985cb018d3511 Author: Rolf Bensch Date: 2020-03-31 22:54:02 +0200 pixma: backend version 0.27.6 commit 05459de5e8e9ad1b25aa84453d2277d7c950b729 Author: Rolf Bensch Date: 2020-03-11 23:55:21 +0100 pixma: print characters @ pixma_hexdump() commit e58641a430a7f1b70edb40c2fe495923e02a35bc Merge: 2fe8e635970e c47e1c0bd5fd Author: Rolf Bensch Date: 2020-03-31 20:46:48 +0000 Merge branch '258-fake-usb-playback-fails-with-pixma-backend' into 'master' Resolve "Fake USB Playback fails with pixma backend" Closes issue sane-project/backends#258 See merge request sane-project/backends!353 Need merge requests sane-project/backends!380 and sane-project/backends!381 commit c47e1c0bd5fdb953a6d0b3ba171f7c236dd95eeb Author: Rolf Bensch Date: 2020-03-31 18:45:35 +0200 sanei_usb: don't read data from timeouted interrupt commit d2a43afce04943a40a43e8d6b57a069bcf5acb78 Author: Rolf Bensch Date: 2020-03-31 13:26:38 +0200 pixma: get device id if we replay a xml file commit fa9d240e7040bf0b4ddb7c00334febf49307ea9d Author: Rolf Bensch Date: 2020-03-10 16:22:30 +0100 pixma: call sanei_usb_exit() on sane_exit() see: sane-project/backends#108 commit 2fe8e635970e9ef21a26a78f69eb58e5332fad1c Merge: d6d3c0566fec d6fa8668183e Author: Povilas Kanapickas Date: 2020-03-31 17:55:02 +0000 Merge branch 'sanei-usb-record-replay-descriptor' into 'master' sanei_usb: Add support for record/replay of get descriptor calls See merge request sane-project/backends!381 commit d6fa8668183e82232d82d1058d96b446e07b4904 Author: Povilas Kanapickas Date: 2020-03-28 22:08:41 +0200 sanei_usb: Add support for record/replay of get descriptor calls commit 77886e63874a38736ccdf4cb90f4cdf856d4064b Author: Povilas Kanapickas Date: 2020-03-28 22:08:40 +0200 sanei_usb: Return correct error code in replay code path commit d6d3c0566fec15197a43ac0daf2a8a19c9650cb2 Merge: 22b548bf2247 770b204702d0 Author: Povilas Kanapickas Date: 2020-03-31 10:03:01 +0000 Merge branch 'sanei-usb-record-replay-multiple-open' into 'master' sanei_usb: Support recording conversations within multiple open-close calls See merge request sane-project/backends!380 commit 770b204702d00d243d4739ad9b0d68d5800c1469 Author: Povilas Kanapickas Date: 2020-03-28 22:08:43 +0200 sanei_usb: Support devices that are being opened multiple times commit 7d97a1aaf46b8adf5808fdb4204e99af7a095c6a Author: Povilas Kanapickas Date: 2020-03-28 22:08:42 +0200 sanei_usb: Reset all testing data on exit commit 62614a46609d85f03d9b73a826f8c94a3554e2b1 Author: Povilas Kanapickas Date: 2020-03-28 22:08:39 +0200 sanei_usb: Use internal linkage for private static variables commit 22b548bf2247d544493a825999f748af7e3a7f8c Merge: b291a1793488 d5fdbb0c7dcb Author: Olaf Meeuwissen Date: 2020-03-30 11:42:02 +0000 Merge branch 'escl-fix-capabilities' into 'master' escl - fix capabilities See merge request sane-project/backends!384 commit d5fdbb0c7dcbaa26e29b114f09844abdb0d52a52 Author: Thierry HUCHARD Date: 2020-03-29 21:37:35 +0200 Revert "Offset correction" This reverts commit d7df235a7e74602c51b002a6962eb72937f623c3. commit 062fb40cf42220d9ab440b185afbfe023d85a650 Author: Thierry HUCHARD Date: 2020-03-29 21:37:25 +0200 Revert "Correction of the zone cropping." This reverts commit 67c7ef9c4e1463d853db190db22e7f652119d664. commit 67c7ef9c4e1463d853db190db22e7f652119d664 Author: Thierry HUCHARD Date: 2020-03-29 21:35:28 +0200 Correction of the zone cropping. commit d7df235a7e74602c51b002a6962eb72937f623c3 Author: Thierry HUCHARD Date: 2020-03-29 21:30:21 +0200 Offset correction commit 6c855b7662fd2d8d4c3688176d74843207b56d55 Author: Thierry HUCHARD Date: 2020-03-29 16:20:16 +0200 Fix build. commit b4b1f2c7489912ec1243c576a6a6af1074565977 Author: Thierry HUCHARD Date: 2020-03-29 16:12:19 +0200 Fix build. commit 9afdd20776d06ff5cceae9087eae038cc6dfc285 Author: Ordissimo Date: 2020-03-29 13:53:42 +0000 Fix initialisation variable. commit 3066241e9037b2fb9b1a76c8ff02d97e94e345c5 Author: Thierry HUCHARD Date: 2020-03-29 15:47:44 +0200 Sets the capabilities of the device according to the output format. commit b291a1793488e3eaba7a42938f600869f358058e Merge: ee54d3935f5c d06e42b08c8a Author: Olaf Meeuwissen Date: 2020-03-30 11:40:07 +0000 Merge branch 'fix-croping' into 'master' escl - Fix croping See merge request sane-project/backends!385 commit d06e42b08c8a6d4812af08f7ac81def33f3ffe1b Author: Thierry HUCHARD Date: 2020-03-29 21:42:38 +0200 Correction of the zone cropping. commit c6a8a541110a5cc946d949d32206710d9a21027b Author: Thierry HUCHARD Date: 2020-03-29 21:42:10 +0200 Offset correction commit ee54d3935f5c58dfdb36daf5bc7c8e82923bfbb3 Merge: 07f28a1ca63d 8ecd54e8d2d1 Author: Olaf Meeuwissen Date: 2020-03-29 12:32:33 +0000 Merge branch 'escl-formatext-pdf' into 'master' Add FormatExt for PDF. See merge request sane-project/backends!356 commit 8ecd54e8d2d18d2a539d3625e7cf55df43570b0b Author: Thierry HUCHARD Date: 2020-03-14 22:17:34 +0100 Add FormatExt for PDF. commit 07f28a1ca63d384d365a04a67990e72c7843eaa5 Merge: 47689131a474 ce97560e56b4 Author: Olaf Meeuwissen Date: 2020-03-29 12:29:56 +0000 Merge branch 'openbsd-hotplugd' into 'master' OpenBSD hotplugd(8) See merge request sane-project/backends!375 commit ce97560e56b450764dedf52b35344818d2d77f76 Author: Raf Czlonka Date: 2020-03-25 20:15:37 +0000 Remove double quotes, use curly braces where appropriate, and fix the $BUSNAME device node path commit 6fed5be5f359b4f242feeee827c10827bddcf6ae Author: Raf Czlonka Date: 2020-03-25 20:12:53 +0000 $BUSNAME is a full device path, not just the device node name commit 2c9a411b12a7272654ac49f0a20b579a91a4a292 Author: Raf Czlonka Date: 2020-03-25 20:05:04 +0000 Remove superfluous double quotes Device nodes are always comprised of alphanumeric characters so there's no need to quote them. That being said, where appropriate, use curly braces if the variable doesn't terminate the string - technically, this isn't necessary as the character which follows is a dot ('.'), which isn't a legal character in variable names so there's no ambiguity but it makes the variable "stand out" at the very least. commit d24a5171c2f22cfc384442872077baae14b303df Author: Raf Czlonka Date: 2020-03-25 20:03:41 +0000 chown -> chmod commit 6b1b071b771cbf98684336194eb1f9f26a9f020f Author: Raf Czlonka Date: 2020-03-25 03:02:56 +0000 Group commands and add conditionals commit 0d0c03ab686cacafc739677048d9fde741e7c212 Author: Raf Czlonka Date: 2020-03-25 02:48:53 +0000 Change the owner as well as the group - missed earlier commit 9687ff8aa0edf2b4f0207682eab395fcbbbefa6f Author: Raf Czlonka Date: 2020-03-25 02:47:54 +0000 Additionally to user:group, modes also need changing commit 5a842d8dd6611bc283e78b3d1a4f9f489cc14774 Author: Raf Czlonka Date: 2020-03-25 02:44:37 +0000 Remove useless comments commit 0b978ea7422cc067f986a428e4e2d0c581bda8bc Author: Raf Czlonka Date: 2020-03-25 02:25:50 +0000 Simplify - use test(1) and && instead of 'if ... then ... fi" commit a129b7767de601969e0ada6fdb836701e9662bc6 Author: Raf Czlonka Date: 2020-03-25 02:07:05 +0000 Incorporate hotplugd(8) patches from the OpenBSD port https://cvsweb.openbsd.org/~checkout~/ports/graphics/sane-backends/patches/patch-tools_openbsd_attach?rev=1.3 https://cvsweb.openbsd.org/~checkout~/ports/graphics/sane-backends/patches/patch-tools_openbsd_detach?rev=1.2 While there, use "$()" instead of "``" for running commands in a subshell (this is to match `attach` change commited earlier). More whitespace fixes. commit 61881417faf2ce8a67428d177f00a749d00622e3 Author: Raf Czlonka Date: 2020-03-25 01:59:59 +0000 Provide a working hotplugd(8) attach example Current example not only doesn't work but also hard-codes `ugen0` device name. Fix whitespace while there. commit 47689131a4741511221bbddd4d47a35fe58f0985 Merge: 59df3942f87a 9698bfece709 Author: Povilas Kanapickas Date: 2020-03-28 21:31:29 +0000 Merge branch 'genesys-select-by-bcddevice' into 'master' genesys: Implement device filtering by bcdDevice value See merge request sane-project/backends!383 commit 9698bfece709ae429b17c3da40b91ae906af60cb Author: Povilas Kanapickas Date: 2020-03-28 23:15:48 +0200 genesys: Add support for matching devices by bcdDevice commit 0bdce7928f55b2706a29418b363c184ff9f8ba2e Author: Povilas Kanapickas Date: 2020-03-28 23:15:47 +0200 genesys: Turn Genesys_USB_Device_Entry into a class commit b89751605f1f677b86cc903e2ecff6f8d3b5e0be Author: Povilas Kanapickas Date: 2020-03-28 23:15:46 +0200 genesys: Add support for reading bcdDevice value commit 59df3942f87a9c95ba85b6355b9f9078c899d78b Merge: 7508a2174102 45dfd889e67f Author: Povilas Kanapickas Date: 2020-03-28 20:42:38 +0000 Merge branch 'sanei-usb-record-indent-configuration' into 'master' sanei_usb: Indent the configuration section in recorded xml See merge request sane-project/backends!382 commit 45dfd889e67f80ef55f1fdd62d66c8c15c41f2d4 Author: Povilas Kanapickas Date: 2020-03-28 22:08:44 +0200 sanei_usb: Indent the configuration section in recorded xml commit 7508a21741023ba08749901bdc656e74dc5a5aee Merge: 4a960e960aca 5c1643291a15 Author: Povilas Kanapickas Date: 2020-03-27 22:04:15 +0000 Merge branch 'lide-210-220-4800dpi' into 'master' genesys: Add 4800dpi support for Canon LiDE 210 & 220 Closes #204 See merge request sane-project/backends!374 commit 5c1643291a1542c7aaddbfacd18b569436580d17 Author: Matthew Petroff Date: 2020-03-24 21:32:11 -0400 genesys: Add 4800dpi support for Canon LiDE 220. commit 93f6bb19a9d83785b0447c2775e4e1a31ceaebf4 Author: Matthew Petroff Date: 2020-03-24 20:50:50 -0400 genesys: Simplify GL124 shading data segment iteration. commit 74f2af604bee59139b2277f916526271ae88977c Author: Matthew Petroff Date: 2020-03-24 19:44:17 -0400 genesys: Add 4800dpi support for Canon LiDE 210. Based on USB logs from Windows driver. commit 4a960e960aca62860e4346569fc036bf766d58cf Merge: bed29187f7bf e592a0c6f2de Author: Povilas Kanapickas Date: 2020-03-27 21:24:08 +0000 Merge branch 'genesys-plustek' into 'master' genesys: Fix x position of scan window on Plustek 7400 See merge request sane-project/backends!378 commit e592a0c6f2de345c161695d0f1b9b77ec17c9c09 Author: Povilas Kanapickas Date: 2020-03-27 23:05:49 +0200 genesys: Fix x position of scan window on Plustek 7400 commit bed29187f7bf340a04d94c285dd5ba9e7787fcd7 Merge: 1fdb7021dfee 8068a958b221 Author: Povilas Kanapickas Date: 2020-03-27 20:20:51 +0000 Merge branch 'genesys-plustek' into 'master' genesys: Add support for Plustek OpticFilm 7400 and 8200i See merge request sane-project/backends!377 commit 8068a958b221ee8cb6e9b76703175cc502b778fd Author: Povilas Kanapickas Date: 2020-03-27 21:29:30 +0200 genesys: Fix setup of motor vref on gl846 commit 982d7d18aac3f24928d662ee9597caebb76eabfb Author: Povilas Kanapickas Date: 2020-03-27 21:29:29 +0200 genesys: Fix log message when device is not supported commit 6cb39e0a29142a832a6fe2ca772ff99be92d6a27 Author: Povilas Kanapickas Date: 2020-03-27 21:29:28 +0200 genesys: Add support for Plustek Opticfilm 8200i commit adaac29d90179e2e639c08ed0760d2125c4f9c19 Author: Povilas Kanapickas Date: 2020-03-27 21:29:27 +0200 genesys: Add support for Plustek Opticfilm 7400 commit ef67ab08352604811462d55e766c85873d1f9d4c Author: Povilas Kanapickas Date: 2020-03-27 21:29:26 +0200 genesys: Fix support for transparency scanners on gl846 commit 0fc25ed2d4a06465a2dcd9190a282846394b15b5 Author: Povilas Kanapickas Date: 2020-03-27 21:29:25 +0200 genesys: Fix motor setup on gl846 commit c3d7a02b0c2bacd54e82daf61dd3442c1c6f0ecf Author: Povilas Kanapickas Date: 2020-03-27 21:29:24 +0200 genesys: Fix bulk data read from USB on gl845 commit 69bfaa725686ede10f67dd1ab784807cf24258eb Author: Povilas Kanapickas Date: 2020-03-27 21:29:23 +0200 genesys: Print MotorId as string instead of a numeric id commit 71c81423fe5a591876cebedc63fcb3c6885ff977 Author: Povilas Kanapickas Date: 2020-03-27 21:29:22 +0200 genesys: Print GpioId as string instead of a numeric id commit 40d0641940c712cb3de488e3c749d04b98a5af1c Author: Povilas Kanapickas Date: 2020-03-27 21:29:21 +0200 genesys: Print AdcId as string instead of a numeric id commit 75da1523b89007ab142629c5f436436407f95146 Author: Povilas Kanapickas Date: 2020-03-27 21:29:20 +0200 genesys: Print ScanSession::pixel_count_ratio when serializing to stream commit 1fdb7021dfeefa6a59a0c265ae7a22990404bef1 Author: Rolf Bensch Date: 2020-03-25 20:22:27 +0100 pixma: Canon PIXMA TS3300 Series is working see merge request sane-project/backends!376 commit dd785110726fe69c999c362bece11c51835b303f Merge: 03a135cafd3c 48bc45f46be2 Author: Olaf Meeuwissen Date: 2020-03-24 12:22:38 +0000 Merge branch 'escl-unix-socket' into 'master' Add unix socket support to ESCL See merge request sane-project/backends!348 commit 48bc45f46be27c94dfe099f424683256c17fbb73 Author: Benjamin Gordon Date: 2020-03-05 14:53:42 -0700 escl: Add unix socket support Curl can pass HTTP traffic through a unix socket instead of TCP with the CURLOPT_UNIX_SOCKET_PATH option. This is useful if you have a proxy listening on a local socket between your computer and the scanner. Now that URL parsing is centralized, take advantage of this to support an extended name scheme: escl:unix:/path/to/socket:URL These names won't be picked up on the network when avahi scans, but they can be passed manually with -d or added to /etc/sane.d/escl.conf. If the existing escl:URL syntax is used, the socket is not set and the curl option is not used, so existing network connections work the same as before. Since local sockets are optional, the current conf parsing scheme becomes order dependent. To avoid this, also add an additional "device" line type to the config parser. If a line starts with "device", the remainder of the line is treated as a complete device name/URL and parsed with the URL parsing added in the previous commit. commit f6832f2a897d98f15c94a2c684e8c9d272caa594 Author: Benjamin Gordon Date: 2020-03-05 14:32:14 -0700 escl: Refactor name handling Scanners are currently passed around by name, which is actually a URL. Each function parses the URL a little bit, depending on what it needs, and then sets the same curl options. This refactors parsing out into a single escl_parse_name() function called from sane_open and then passes around the resulting ESCL_Device instead of the raw name. To set up the curl handle, the new escl_curl_url() reconstructs a URL from the parsed ESCL_Device and sets the appropriate options on the handle. Each place that makes a curl call is updated to use the new function. commit 03a135cafd3c16abec90a8a05f7900217a05f370 Author: Rolf Bensch Date: 2020-03-24 13:05:19 +0100 pixma: backend version 0.27.5 commit 86a8780c0c08479f918a0eef3af31c53a5633314 Merge: a309a2a58d37 6e52b3f01ab5 Author: Rolf Bensch Date: 2020-03-24 12:03:18 +0000 Merge branch 'pixma/use-BT.709-luma' into 'master' pixma: for grayscale conversion use BT.709 luma See merge request sane-project/backends!373 commit 6e52b3f01ab5bbdc7b192895d3fd4819e49a7864 Author: Rolf Bensch Date: 2020-03-24 12:37:24 +0100 pixma: for grayscale conversion use BT.709 luma Before we used linear grayscale conversion. This is not state of the art. commit a309a2a58d37efa538e770e9b59f2d0d04948aea Merge: 199b9eeef6ec e5e63f306af6 Author: Olaf Meeuwissen Date: 2020-03-24 11:30:46 +0000 Merge branch '111-sane-backends-1-0-28-backend-gt68xx_high-c-100-bad-expression-testpicture' into 'master' Resolve "sane-backends-1.0.28/backend/gt68xx_high.c:100: bad expression ?" Closes #111 See merge request sane-project/backends!369 commit e5e63f306af6c2f4d5ff4c458c1e5cae91963d86 Author: Ralph Little Date: 2020-03-22 12:40:17 -0700 test: extra brackets to silence cppcheck warning. commit 199b9eeef6ecc4ec411b4feaf8deef63d45a1c21 Merge: 2583158a5b04 56522b6a3585 Author: Olaf Meeuwissen Date: 2020-03-23 13:17:23 +0000 Merge branch '243-really-fix-build-failure-on-ubuntu-16.04' into 'master' Resolve "cannot build escl backend for Ubuntu 16.04 LTS" Closes #243 See merge request sane-project/backends!372 commit 56522b6a35853025c46bdf14a2a252f8748d3dc6 Author: Olaf Meeuwissen Date: 2020-03-23 22:00:16 +0900 Fix references to function check results commit 00a7fd8f00b7ff671574557b4638451d1141b8b1 Author: Olaf Meeuwissen Date: 2020-03-23 21:59:41 +0900 Add linker flags for JPEG library when checking for functions commit 2583158a5b04a48db4391f6df4a8bb95f1b39363 Merge: 487b4a3a3c41 afdc2a159e38 Author: Ralph Little Date: 2020-03-22 17:10:37 +0000 Merge branch '111-sane-backends-1-0-28-backend-gt68xx_high-c-100-bad-expression' into 'master' Resolve "sane-backends-1.0.28/backend/gt68xx_high.c:100: bad expression ?" See merge request sane-project/backends!363 commit afdc2a159e38359f7c71ace91f7aa7960281f702 Author: Ralph Little Date: 2020-03-20 20:27:52 -0700 gt68xx: corrected | character to || to fix warning. commit db9e4d4729ec562e1592659cba41baee757274a3 Author: Ralph Little Date: 2020-03-20 19:37:35 -0700 mustek: added clarifying brackets to expression. commit bc57c7ce5fc38859cb3c26540e2961b0a051a4c5 Author: Ralph Little Date: 2020-03-20 19:37:00 -0700 kvs20xx: added clarifying brackets to expression. commit 487b4a3a3c410bad61fc824cdc0c3ee87a627439 Merge: c99580b81195 28abffad3cb1 Author: Ralph Little Date: 2020-03-22 17:09:45 +0000 Merge branch '116-sane-backends-1-0-28-backend-dmc-c-many-redundant-assignments' into 'master' Resolve "sane-backends-1.0.28/backend/dmc.c: many redundant assignments ?" Closes #116 See merge request sane-project/backends!364 commit 28abffad3cb1c7f934dc3f33b9e12f370d613517 Author: Ralph Little Date: 2020-03-20 20:43:12 -0700 dmc: corrected tl-x, tl-y, br-x and br-y option ranges, to eliminate compiler warnings commit c99580b811959996f79a1f7278afddba20cfc69b Merge: dab5594a5052 ef583d54f209 Author: Povilas Kanapickas Date: 2020-03-22 13:01:07 +0000 Merge branch 'genesys-simplify-dpiset' into 'master' genesys: Simplify DPISET register calculations See merge request sane-project/backends!367 commit ef583d54f209b89d296b4678c7b880f6c47129cc Author: Povilas Kanapickas Date: 2020-03-21 00:11:32 +0200 genesys: Rename dpiset_override to register_dpiset commit 5bcb5f3e80e16583c447d10fb544d22d42f47ea1 Author: Povilas Kanapickas Date: 2020-03-21 00:11:31 +0200 genesys: Simplify shading resolution calculation on gl646 commit c8c9deaf6def7ea921ece64f490037d96bfa72ff Author: Povilas Kanapickas Date: 2020-03-21 00:11:30 +0200 genesys: Simplify dpiset calculation on gl646 commit 989782bd72431118457cb5c0262dc80acd7c73b3 Author: Povilas Kanapickas Date: 2020-03-21 00:11:29 +0200 genesys: Simplify dpiset calculation on gl847 commit a43d362b7b35b7caa87d6ae70f9a7c577291db77 Author: Povilas Kanapickas Date: 2020-03-21 00:11:28 +0200 genesys: Simplify dpiset calculation on gl845/gl846 commit 4a5f65d2d2b488eb2c1792a14dac1a863e6f0e0a Author: Povilas Kanapickas Date: 2020-03-21 00:11:27 +0200 genesys: Verify that dpiset_override is specified commit 94cfdbfdc699ae99468d0493f659f37328398399 Author: Povilas Kanapickas Date: 2020-03-21 00:11:26 +0200 genesys: Simplify dpiset calculation on gl843 commit d9cd672a43faa5ae861e31c42a6d1496de5e0b4e Author: Povilas Kanapickas Date: 2020-03-21 00:11:25 +0200 genesys: Simplify shading data upload on gl841 commit 459c62c131b4794d115c38b7a555d2415f7e9870 Author: Povilas Kanapickas Date: 2020-03-21 00:11:24 +0200 genesys: Simplify dpiset calculation on gl841 commit cce3cdeaabc137aafdc8cd401242a96c98d8d2b6 Author: Povilas Kanapickas Date: 2020-03-21 00:11:23 +0200 genesys: Update all sensors with calibrated exposure on gl841 commit 38cf079e99b011e78523c48fd3fbc66a128a6491 Author: Povilas Kanapickas Date: 2020-03-21 00:11:22 +0200 genesys: Simplify dpihw register setup on gl841 commit 692178eade0e176fe28d56278140af0cd39e8675 Author: Povilas Kanapickas Date: 2020-03-21 00:11:21 +0200 genesys: Simplify dpiset calculation on gl124 commit 79e403bb89b0f108488aab87d0d688528a714a3c Author: Povilas Kanapickas Date: 2020-03-21 00:11:20 +0200 genesys: Simplify register setup on gl841 commit f1368cb34c29c14d0b1cc660f241a71a55c14832 Author: Povilas Kanapickas Date: 2020-03-21 00:11:19 +0200 genesys: Add comments with asic types of sensors to the sensor tables commit 127e56978c9052a0900b4f16af2979f8d01c0bdd Author: Povilas Kanapickas Date: 2020-03-21 00:11:18 +0200 genesys: Remove dead code in gl841 sensor setup impl commit 8808fe777929650519532abc5fc34e5e4398c3da Author: Povilas Kanapickas Date: 2020-03-21 00:11:17 +0200 genesys: Remove no longer used logical_dpihw_override commit dab5594a5052faee59a8a25dc68d738bb054b889 Merge: 6ab010939ce1 1e116f12231c Author: Povilas Kanapickas Date: 2020-03-22 12:42:23 +0000 Merge branch 'genesys-remove-hwdpi' into 'master' genesys: Simplify hwdpi calculation See merge request sane-project/backends!366 commit 1e116f12231cb1935140693433b61431899a09e3 Author: Povilas Kanapickas Date: 2020-03-21 00:03:47 +0200 genesys: Verify that sensor tables are setup correctly commit 4cc233cde8071b1ed1b1672a3ee06dc982749ade Author: Povilas Kanapickas Date: 2020-03-21 00:03:46 +0200 genesys: Add missed settings for HP N6310 commit eb5a643c0adf894d4df5b19caa8aedde202038ce Author: Povilas Kanapickas Date: 2020-03-21 00:03:45 +0200 genesys: Rename register_dpihw_override to register_dpihw commit c24886d7d7e97b0d82ead92b12b350bf27790f5d Author: Povilas Kanapickas Date: 2020-03-21 00:03:44 +0200 genesys: Remove no longer used hwdpi calculation functions commit 815d36c9c304d4f35f15fda568bee8f74552efbc Author: Povilas Kanapickas Date: 2020-03-21 00:03:43 +0200 genesys: Simplify shading resolution computation commit 163776a47910e0199ccda6aaaf4c0d4e5b5a9262 Author: Povilas Kanapickas Date: 2020-03-21 00:03:42 +0200 genesys: Simplify dpihw and shading resolution calculation on gl841 commit 88989a1616ee53cb20587d4ce6242cb3a0b1a480 Author: Povilas Kanapickas Date: 2020-03-21 00:03:41 +0200 genesys: Pass resolution setting to sanei_genesys_set_dpihw() directly commit 2547f681f3056c960e37dfe76624b5706a1f9e7e Author: Povilas Kanapickas Date: 2020-03-21 00:03:40 +0200 genesys: Remove no longer used ModelFlag::FULL_HWDPI_MODE commit fd90ddefdc2d123f7316420696cef1266ac3c270 Author: Povilas Kanapickas Date: 2020-03-21 00:03:39 +0200 genesys: Simplify motor setup to use only model id for vref on gl843 commit ca3ae12acfe1ef10641538efa3f8d3ae54e629a0 Author: Povilas Kanapickas Date: 2020-03-21 00:03:38 +0200 genesys: Simplify sensor setup to use only model id on gl843 commit 2d7758f5f22f64e112adc221f119499928f31e51 Author: Povilas Kanapickas Date: 2020-03-21 00:03:37 +0200 genesys: Simplify dpihw calculation on gl843 commit cf4ce94fc48ae6e4fb92d9386c85aac9bffd77ac Author: Povilas Kanapickas Date: 2020-03-21 00:03:36 +0200 genesys: Simplify computation of SHDAREA setting on gl843 commit 0fca1185f57e3cc5d1e35aae868d88ea47f581ef Author: Povilas Kanapickas Date: 2020-03-21 00:03:35 +0200 genesys: Simplify shading resolution computation on gl843 commit f06238c97c568fc41f90e5c9187cbc8f557af57e Author: Povilas Kanapickas Date: 2020-03-21 00:03:34 +0200 genesys: Simplify shading resolution computation on gl847 commit 3f6dcf84feff96ecb82bcc2f78fc3fda982cd680 Author: Povilas Kanapickas Date: 2020-03-21 00:03:33 +0200 genesys: Simplify shading resolution computation on gl846 commit 0c0b4847283c9995c1b05c863163ae697adb30f6 Author: Povilas Kanapickas Date: 2020-03-21 00:03:32 +0200 genesys: Simplify dpihw calculation on gl846 commit dd16101d44680ebe0096f175b7635eeed83f0b86 Author: Povilas Kanapickas Date: 2020-03-21 00:03:31 +0200 genesys: Simplify dpihw calculation on gl847 commit a41ffe3c334d133edba624a224a6929b1ae4c975 Author: Povilas Kanapickas Date: 2020-03-21 00:03:30 +0200 genesys: Simplify motor setup on gl846 commit fe8ffefe1b3e2c76ccef3f735340ec70b078ff10 Author: Povilas Kanapickas Date: 2020-03-21 00:03:29 +0200 genesys: Simplify lamp warmup setup on gl843 commit 3c8b103d821b8e6c5b3f1e8bff7a690061973b1d Author: Povilas Kanapickas Date: 2020-03-21 00:03:28 +0200 genesys: Simplify shading resolution computation on gl124 commit c0a94b9288ecd53d81d7ce70c4d178dd19f8b327 Author: Povilas Kanapickas Date: 2020-03-21 00:03:27 +0200 genesys: Simplify dpihw calculation on gl124 commit 028c9d398f6b8c88bd72ed5cb5b7d3c012f04eac Author: Povilas Kanapickas Date: 2020-03-21 00:03:26 +0200 genesys: Simplify calculation of shading upload parameters on gl845/846 commit 6e3713c87d4725526cca0e04dbba45f40a0ed029 Author: Povilas Kanapickas Date: 2020-03-21 00:03:25 +0200 genesys: Simplify calculation of shading upload parameters on gl847 commit e54f15bcc8eb1abc648f966b8aa95f094dbafc6e Author: Povilas Kanapickas Date: 2020-03-21 00:03:24 +0200 genesys: Simplify calculation of shading upload parameters on gl124 commit 6ab010939ce15a4cd403e97cf2f90aa251b4ce0b Merge: 115bbf6a245f 7a86b487bda7 Author: Povilas Kanapickas Date: 2020-03-22 12:34:05 +0000 Merge branch '114-sane-backends-1-0-28-sanei-sanei_pio-c-3-bad-test' into 'master' Resolve "sane-backends-1.0.28/sanei/sanei_pio.c: 3 * bad test ?" Closes #114 See merge request sane-project/backends!362 commit 7a86b487bda7f568812c6a77abfcd631716eecdc Author: Ralph Little Date: 2020-03-20 19:28:56 -0700 sanei: corrected fd validity check, which always used to fail. commit 115bbf6a245f0a76ad5cd3aa3f8851128bef8582 Merge: 1ed4c7d8f558 94efec957eed Author: Povilas Kanapickas Date: 2020-03-22 12:18:46 +0000 Merge branch 'genesys-remove-hwdpi-divisor' into 'master' genesys: Remove hwdpi divisor See merge request sane-project/backends!365 commit 94efec957eed2b1e53491d7fd1d24f34d6de8b8d Author: Povilas Kanapickas Date: 2020-03-20 23:29:10 +0200 genesys: No longer specify per-sensor hwdpi divisor commit 5a106f9e9ca479c37449ba6da007f252c608a0f6 Author: Povilas Kanapickas Date: 2020-03-20 23:29:09 +0200 genesys: Remove remaining uses of hwdpi_divisor commit 31d5382b2b5a906edd0467ec6939255706a67c71 Author: Povilas Kanapickas Date: 2020-03-20 23:29:08 +0200 genesys: Don't use hwdpi_divisor for motor vref computation commit ceb13f77d73dc2fcf0ab6a10eb6fe0e796091315 Author: Povilas Kanapickas Date: 2020-03-20 23:29:07 +0200 genesys: Simplify physical pixel count computation on gl843 commit f34176edf28e6cdc173c037083b14ede918e8546 Author: Povilas Kanapickas Date: 2020-03-20 23:29:06 +0200 genesys: Simplify physical pixel count computation on gl845 commit 5807e5d2981eb460c3a3822acbf460b41cf36cc9 Author: Povilas Kanapickas Date: 2020-03-20 23:29:05 +0200 genesys: Simplify physical pixel count computation on gl846 commit a1f10bf2bce3b0103e58d81dad8e32d40581f89f Author: Povilas Kanapickas Date: 2020-03-20 23:29:04 +0200 genesys: Simplify physical pixel count computation on gl847 commit 576d1fcdbac394c3220a1467c67e8477251ebe35 Author: Povilas Kanapickas Date: 2020-03-20 23:29:03 +0200 genesys: Simplify physical pixel count computation on gl124 commit e2fc129b567aad2bb26f4b79479cac1228996d9e Author: Povilas Kanapickas Date: 2020-03-20 23:29:02 +0200 genesys: Make handling of stagger more uniform across chipsets commit 75d227f9c4dfe7395e49e2bb36b8a155b7891dce Author: Povilas Kanapickas Date: 2020-03-20 23:29:01 +0200 genesys: Represent pixel count ratio as a class commit b2b569a7436dd9e53362db0c53d80a430b686c73 Author: Povilas Kanapickas Date: 2020-03-20 23:29:00 +0200 genesys: Add a way to override pixel coordinate divisor commit 559b2e314fb75ac27fa7a975b1617ff73c78a3bb Author: Povilas Kanapickas Date: 2020-03-20 23:28:59 +0200 genesys: Make handling of hwdpi_divisor uniform across chipsets commit 1ed4c7d8f5585de74a22bc0931a94d40b419e0d5 Author: Rolf Bensch Date: 2020-03-19 16:11:06 +0100 pixma: backend version 0.27.4 commit b113ef1285ee0ae59071aec6da05685d0c23ed90 Author: Rolf Bensch Date: 2020-03-19 16:09:38 +0100 pixma: for Canon LiDE300 and LiDE400 map target 06 to cancel/end button see issue sane-project/backends#259 commit 120d8c0bd3227f25eb31b146f61908b6ed24d254 Author: Olaf Meeuwissen Date: 2020-03-18 20:24:21 +0900 README: Mention need for C++11 compiler for genesys backend commit 787b89daeab2796845d6d24b9cd91859470b9a5e Merge: e12b1e26aabe 4e9cbda3babc Author: Ralph Little Date: 2020-03-17 15:51:35 +0000 Merge branch '236-red-and-yellow-overlay-after-3rd-scan-with-simple-scan-epson-perfection-1260' into 'master' Resolve "Red and Yellow overlay after 3rd scan with simple-scan Epson Perfection 1260" Closes #236 See merge request sane-project/backends!359 commit 4e9cbda3babc3d008a36d0097090e8ca5a2d6a71 Author: Ralph Little Date: 2020-03-15 20:21:16 -0700 plustek: fix for gain loop computation to prevent buffer overrun commit e12b1e26aabe1cea265210ec8f999dec4a69220b Merge: a1888ae3bd28 db4cbd4185ee Author: Olaf Meeuwissen Date: 2020-03-17 12:16:38 +0000 Merge branch '243-cannot-build-escl-backend-for-ubuntu-16.04-lts' into 'master' Resolve "cannot build escl backend for Ubuntu 16.04 LTS" Closes #243 See merge request sane-project/backends!360 commit db4cbd4185eee2c00bbaefee7896c11e21f99888 Author: Olaf Meeuwissen Date: 2020-03-17 20:46:34 +0900 Add a build to prove that the previous commit fixes #243 commit f5af34e75f3c946ba2aa935d09036dc99a1ecebf Author: Olaf Meeuwissen Date: 2020-03-17 20:43:16 +0900 Add checks for newer JPEG API used by the escl backend. Fixes #243 The backend is excluded from the build if this API is not available by SANE_CHECK_BACKENDS. commit a1888ae3bd285e94f0dbb982e2611a3960faa623 Merge: c7c5b1a602e4 3da269b7de53 Author: Povilas Kanapickas Date: 2020-03-15 16:13:53 +0000 Merge branch 'genesys-duplication' into 'master' genesys: Reduce code duplication See merge request sane-project/backends!357 commit 3da269b7de5349b024c25edb1876ed22979bc345 Author: Povilas Kanapickas Date: 2020-03-14 23:19:38 +0200 genesys: Use common code path for memory layouts commit 718550e2d47b5f6168d256897efef6162ac09cf9 Author: Povilas Kanapickas Date: 2020-03-14 23:19:37 +0200 genesys: Add a way to flush GenesysRegisterSettingSet without read back commit cb442169e4beb4bce2ae7f0008739e6171361644 Author: Povilas Kanapickas Date: 2020-03-14 23:19:36 +0200 genesys: Implement a way to serialize ModelId commit c1d4369410263fe5014ab41c4201f32e8375c31d Author: Povilas Kanapickas Date: 2020-03-14 23:19:35 +0200 genesys: Reuse generic value filtering code for scan method filtering commit c234ce6b77e623f7adfcf5ddd80b7d77ea102ecc Author: Povilas Kanapickas Date: 2020-03-14 23:19:34 +0200 genesys: Add generic value filter class for resolutions filtering commit ee2e026f2a5e86dd9188abc1277861cfd39d8cb0 Author: Povilas Kanapickas Date: 2020-03-14 23:19:33 +0200 genesys: Don't overwrite RAM settings commit ea5047466ce0ade9bd5176caef18cbbe4260ce7b Author: Povilas Kanapickas Date: 2020-03-14 23:19:32 +0200 genesys: Reuse common gpio code on gl846 commit 31fecb81407dcdbf10715b3f7f3667cf49e00c42 Author: Povilas Kanapickas Date: 2020-03-14 23:19:31 +0200 genesys: Clean up motor setup on gl847 commit b307eba5cca35f247175fd66e68a1c6a075d2177 Author: Povilas Kanapickas Date: 2020-03-14 23:19:30 +0200 genesys: Clean up motor setup on gl846 commit 884de5f9fe5f7303950d82fb1fb0d90197ccdd20 Author: Povilas Kanapickas Date: 2020-03-14 23:19:29 +0200 genesys: Clean up motor setup on gl843 commit cd43109966d8655a3f49f3a0f0e96866cf16b76e Author: Povilas Kanapickas Date: 2020-03-14 23:19:28 +0200 genesys: Deduplicate gain calibration commit 233cb4bd5a5050c2867447d9c9c3e0f17c7c6eb4 Author: Povilas Kanapickas Date: 2020-03-14 23:19:27 +0200 genesys: Use single code path for gain code calculation commit e0bec6723d893cc1d0624d5cdae3bf0b82fdeff1 Author: Povilas Kanapickas Date: 2020-03-14 23:19:26 +0200 genesys: Deduplicate offset calibration commit 255da97fcb9862e3b8fbea8b73ead3241b8501c8 Author: Povilas Kanapickas Date: 2020-03-14 23:19:25 +0200 genesys: Remove get_closest_resolution() All gl646 scanners have sensors for all supported resolutions commit 428293c1e26c5f3c1a7ab87ef6717264d572bf24 Author: Povilas Kanapickas Date: 2020-03-14 23:19:24 +0200 genesys: Reflow sensor tables commit c7c5b1a602e44f0bf4132f4259ca0481a53b3a25 Merge: 93fd01866e07 82515511a60c Author: Rolf Bensch Date: 2020-03-14 13:08:41 +0000 Merge branch '224-enable-avahi-support-by-default' into 'master' Enable Avahi support by default if possible. Re #224 Closes #224 See merge request sane-project/backends!355 commit 82515511a60cc3fa6d3e7f2453ffce0cab62da7a Merge: 0f857f94cd84 168e22a9416f Author: Olaf Meeuwissen Date: 2020-03-14 09:03:31 +0000 Merge branch 'master' into '224-enable-avahi-support-by-default' # Conflicts: # .gitlab-ci.yml commit 0f857f94cd84141e80eab816b49d016d7066da25 Author: Olaf Meeuwissen Date: 2020-03-14 16:02:10 +0900 .gitlab-ci.yml: Follow Avahi configure option rename commit f8c4bb77de38df28151008f03916ba9e01f22151 Author: Olaf Meeuwissen Date: 2020-03-14 15:48:58 +0900 kodakaio: Update Avahi configure time option documentation It has been removed from the command-line examples as the default is to enable when possible and the Ubuntu example says to install the necessary libraries. The avahi library reference has been fixed. commit cf7227b59848ce7d124f7494c607b8c924a7b153 Author: Olaf Meeuwissen Date: 2020-03-14 15:48:19 +0900 Pass Avahi related preprocessor flags where needed commit 53875a52388f2477cef67b4cc13359caa99fff8b Author: Olaf Meeuwissen Date: 2020-03-14 15:46:03 +0900 Fix WITH_AVAHI preprocessor conditionals The macro is now always defined to either 0 or 1. commit f4b7e22bc0baec8819dcaf8659866d17d00c2cc7 Author: Olaf Meeuwissen Date: 2020-03-14 14:54:10 +0900 Enable Avahi support by default if possible. Re #224 This eases building of the escl backend and aligns configure time behaviour with other --with-* options. commit 93fd01866e07097c8eca596034c1fef14cf40890 Author: Rolf Bensch Date: 2020-03-14 13:53:56 +0100 INSTALL.linux: add missing development package commit 168e22a9416f03cf2c1133e34ca50005b9ede8a7 Merge: 0033357120d8 5dcd770e5e22 Author: Olaf Meeuwissen Date: 2020-03-14 09:01:17 +0000 Merge branch 'escl-lineart' into 'master' Escl lineart See merge request sane-project/backends!339 commit 5dcd770e5e223a2099a6ea6d69e5560432355144 Author: Ordissimo Date: 2020-03-14 09:01:17 +0000 .gitlab-ci.yml: Revert poppler related changes The Docker images used by CI have been updated to include the required packages. commit 0033357120d8d6506e8eaece09cdb4e98ec3739e Author: Rolf Bensch Date: 2020-03-11 13:21:12 +0100 pixma: backend version 0.27.3 commit dd3515ce3f1e6f95b9b2b786a0a877df1426d966 Author: Rolf Bensch Date: 2020-03-11 13:20:43 +0100 sane-pixma.man: update document date commit b956a3cef04cdb7f7e50658b3143d425c762b2a5 Author: Rolf Bensch Date: 2020-03-11 13:19:31 +0100 pixma: update copyright commit dbc966e97ed0b8f0038f563298c625f374dea823 Merge: 7322b5aff046 c4af1318487f Author: Rolf Bensch Date: 2020-03-11 12:14:42 +0000 Merge branch 'pixma/new_scanners' into 'master' Pixma: new scanners See merge request sane-project/backends!354 commit c4af1318487f798fccc6010e248094c6d9952865 Author: Rolf Bensch Date: 2020-03-11 12:40:21 +0100 pixma: new scanners Canon PIXMA G7000 and GM4000 Series commit 2b076de25f624811f29f75baeab2316b7760305c Author: Rolf Bensch Date: 2020-03-11 10:27:17 +0100 pixma: new scanner Canon i-SENSYS MF720 Series commit 7322b5aff046623140694b2ba1f04de5efa73524 Author: Rolf Bensch Date: 2020-03-11 12:53:05 +0100 pixma: restrict ADF scans to 300dpi for Canon i-SENSYS MF440 Series commit e83c56658e9d7e99ef36dc08bcb7d2f467646e37 Author: Rolf Bensch Date: 2020-03-11 12:49:53 +0100 sane-pixma.man: align table of supported scanners commit b9a19d0cef9de8cebc472f6c434ab88174670a16 Merge: 70070f7be2b5 20419f9815d3 Author: Rolf Bensch Date: 2020-03-11 11:43:49 +0000 Merge branch 'patch-1' into 'master' pixma: detect MF440. Fix #257 Closes #257 See merge request sane-project/backends!347 commit 20419f9815d392ceae01264fa272a6b74c1b58f6 Author: Jérémy Lal Date: 2020-03-07 17:45:31 +0100 pixma: detect MF440. Fix #257 commit 70070f7be2b587b85f8d058b2b6769aa5dbd2d1e Merge: 9f9536709c28 5441ea1c518e Author: Ralph Little Date: 2020-03-08 17:04:26 +0000 Merge branch '233-additional-sensors' into 'master' Resolve "Regression tests: HP Scanjet 5400C" See merge request sane-project/backends!351 commit 5441ea1c518e18c06b6ff0fca4a277eb896787a3 Author: Ralph Little Date: 2020-03-07 19:55:52 -0800 hp5400: use standard defines for "color" and "grey" texts commit 19d033837e43f18bb1474851ae0e8ce713c478d5 Author: Ralph Little Date: 2020-03-07 15:37:14 -0800 hp5400: fix cancel bug that caused a crash when cancelling a scan or a preview commit 28bcb9179d2ac2fb506fe570e14a0f8a7de42189 Author: Ralph Little Date: 2020-03-07 14:36:27 -0800 hp5400: added support for editable colour/bw switch and copy count sensors. commit 9f9536709c28a3cbd72cf37263b58b5f7cad02a1 Merge: 090973fa94f4 10620841b0a6 Author: Olaf Meeuwissen Date: 2020-03-08 13:11:41 +0000 Merge branch '228-remove-sane-desc-timestamping' into 'master' Resolve "Make sane reproducible" Closes #228 See merge request sane-project/backends!352 commit 10620841b0a6b4be524ed50cf639097b3e9c9e93 Author: Olaf Meeuwissen Date: 2020-03-08 21:56:10 +0900 Run through all sane-desc modes before signalling failures commit ea65e1b2ae692cef9d5dbd71e4c76b22951c3869 Author: Olaf Meeuwissen Date: 2020-03-08 21:38:45 +0900 Drop diff ignore pattern from sane-desc check. Re #228 commit b9651d5e19ccdc178707944a74d7267178c081d0 Author: Olaf Meeuwissen Date: 2020-03-08 21:36:06 +0900 Update test reference outputs to match new sane-desc format. Re #228 commit 1f9590ab5a2467d8ac4bf5db143ab61df7d1b93e Author: Olaf Meeuwissen Date: 2020-03-08 21:29:29 +0900 Remove timestamps from sane-desc output. Re #228 commit 090973fa94f47ff6994b068d33b1d02d527df0a7 Merge: 960394b1e4ab 6d2dda4a9860 Author: Olaf Meeuwissen Date: 2020-03-07 09:35:18 +0000 Merge branch 'for-upstream/backend_avision_nonfilm_noexposure' into 'master' backend/avision: Disable exposure option for non-filmscanners See merge request sane-project/backends!23 commit 6d2dda4a986070c8fea68ac2df9b6527a045e1ab Author: Michael Niewoehner Date: 2018-08-26 13:47:44 +0200 backend/avision: Disable exposure option for non-filmscanners commit 960394b1e4aba8130d3d2b4d0acd6038ce31da33 Merge: 6fe58ec210c4 c16ff3ffe0de Author: Olaf Meeuwissen Date: 2020-03-07 09:25:59 +0000 Merge branch 'for-upstream/backend_avision_fix_threading' into 'master' backend/avision: fix thread cancellation See merge request sane-project/backends!21 commit c16ff3ffe0de9770a7af25368aaac443d3db03dc Author: Michael Niewoehner Date: 2018-09-29 19:01:01 +0200 backend/avision: fix thread cancellation sigprocmask does not work for threads but only for forked processes. Even though a thread-safe version, pthread_sigmask, we do not use it since using singals with threads is a bad practice. Instead implement pthread's own cancellation method. Because sane_read blocks when reader_process is terminated, read_fds is closed and invalidated to make sane_read abort read and return gracefully. commit 6fe58ec210c418b674af42c9c6e752f66eaed2f8 Merge: 953bea63bb1b 67cb40d0f5fa Author: Olaf Meeuwissen Date: 2020-03-07 05:14:11 +0000 Merge branch '254-remove-sane-standard-documentation' into 'master' Resolve "Remove SANE Standard documentation" Closes #254 See merge request sane-project/backends!350 commit 67cb40d0f5fab9c58a4486b915b3a19fada3e684 Author: Olaf Meeuwissen Date: 2020-03-07 13:58:27 +0900 NEWS: Document SANE Standard removal commit 11d59a6d75b930cd40f2e65d13d7b78783553946 Author: Olaf Meeuwissen Date: 2020-03-05 21:20:38 +0900 doc/.gitignore: Remove SANE Standard related files and patterns commit 599186fdb23ddaf24f1eaf573660e5668fbd62fc Author: Olaf Meeuwissen Date: 2020-03-05 21:18:49 +0900 kodakaio: Remove reference to removed --without-api-spec option commit d4d5d76e83aaf1f841bb41122ea040bce2591d46 Author: Olaf Meeuwissen Date: 2020-03-05 21:14:34 +0900 Update references to the SANE standard The authors have been added to SANE standard itself, pointers to the standard now point to the published version and/or the project that isnow used for its maintenance as appropriate. References to input files and installation locations have been removed. commit 2db06a272e0c455f6764e18ed090aa65b5640c6e Author: Olaf Meeuwissen Date: 2020-03-02 20:19:07 +0900 Remove SANE Standard build support commit 73db94b706aaff99734965f0bf3822109ff2e96b Author: Olaf Meeuwissen Date: 2020-03-02 20:03:37 +0900 Delete SANE Standard input files from the repository commit 953bea63bb1b0d66d8c660ef8303c7ee7e9bc596 Merge: 0a5f2f98ffed 2739969661e6 Author: Olaf Meeuwissen Date: 2020-03-07 04:36:08 +0000 Merge branch 'escl-fix-parsing-location' into 'master' Fix error parsing Location job. See merge request sane-project/backends!349 commit 2739969661e64484465a175f087b6d2d306d51d2 Author: thierry1970 Date: 2020-03-06 10:58:31 +0100 Fix error parsing Location job. commit 0a5f2f98ffed60e2762ad2064190a47d33cd7993 Merge: 5da64adadff6 a34382c67b2e Author: Ralph Little Date: 2020-03-06 17:19:44 +0000 Merge branch '233-scanimage-mods' into 'master' Resolve "Regression tests: HP Scanjet 5400C" - scanimage See merge request sane-project/backends!345 commit a34382c67b2e73a9ad466b4c337c935d2ade7ab5 Author: Ralph Little Date: 2020-03-05 09:19:53 -0800 hp5400: some small corrections to button descriptions. commit 180ecf7b414ab229dc3d17b657a4e1e9f759b578 Author: Ralph Little Date: 2020-03-05 09:18:20 -0800 scanimage: revert previous range change. It is not settled that this change is favourable and is probably out of scope for this issue. I will leave it for the moment. commit dd078cd17df04f3075962ee3b061529f44ffecb5 Author: Ralph Little Date: 2020-03-05 08:58:54 -0800 hp5400: Added support for all buttons. Support to read copy counter display to be done shortly. commit 4f6b7665a8815e196e004e18e3711c562b22e934 Author: Ralph Little Date: 2020-03-03 09:56:39 -0800 hp5400: small enhancement from review to reduce code and increase clarity. commit 4948130f7c8cae85d2073f5f1be40f709ce2dae9 Author: Ralph Little Date: 2020-03-01 23:31:23 -0800 scanimage: various small corrections and little changes to enhance clarity. commit 2868e9e346356743927259f6c50c6524e99287c9 Author: Ralph Little Date: 2020-03-01 23:30:03 -0800 hp5400: corrected source for default values. Just correcting a mistake from the previous check in. Also substituted value variable for clarity. commit 385543519b1d8e3971bb97641ff878d80a19bdc8 Author: Ralph Little Date: 2020-03-01 22:53:48 -0800 scanimage,hp5400: added option checking for hp5400 and rationalised scanimage help h5400 now checks validity of -l, -t, -x and -y and auto selects a supported resolution. scanimage now shows the full range for -x and -y despite other selections. commit 5da64adadff6f28452384de1ae545b668982fe46 Merge: f24001e46eaa acfab5b7022a Author: Olaf Meeuwissen Date: 2020-03-03 10:00:17 +0000 Merge branch 'escl-load-conf-segfault' into 'master' Reshaping the loading of the device configuration from escl.conf. See merge request sane-project/backends!340 commit acfab5b7022a3d1813aeefc9273e6221a0a32d95 Author: Thierry HUCHARD Date: 2020-03-01 14:54:00 +0100 renaming the function with a more descriptive name. commit d572db20444376d5005486a7d3e13f78a6ca34d7 Author: Thierry HUCHARD Date: 2020-03-01 14:48:08 +0100 code readability. commit 8870081e5731644d54d24e2a848a5894695ac9b9 Merge: 5503bf8e676a d4aa676f7296 Author: Thierry HUCHARD Date: 2020-03-01 14:44:57 +0100 Merge remote-tracking branch 'origin/master' into escl-load-conf-segfault commit 5503bf8e676a062922b7b939e8a1370521ac4649 Author: Thierry HUCHARD Date: 2020-02-21 22:50:23 +0100 Fix the 0x0 geometry problem. commit c23cc87e526e821dac19f88c67529a518d895f8d Author: thierry1970 Date: 2020-02-20 09:25:42 +0100 Reshaping the loading of the device configuration from escl.conf. commit f24001e46eaa9c3dba39f1003371012907307a80 Author: Rolf Bensch Date: 2020-03-02 16:14:42 +0100 pixma: backend version 0.27.2 commit 3c0f8189ddf29c539a4e2b5153be4057e9a7b737 Author: Rolf Bensch Date: 2020-03-02 16:13:46 +0100 pixma: fix device define for Canon i-SENSYS MF110/910 Series commit d4aa676f729623e5fce6dc98f0096edafad2dd95 Author: Rolf Bensch Date: 2020-02-28 15:36:35 +0100 INSTALL.linux: add missing development package commit dd59aea71b0a0c696dd139e2d7835501d05584c1 Merge: 993a79b594a5 93a0971576da Author: Ralph Little Date: 2020-02-25 18:20:41 +0000 Merge branch '233-regression-tests-hp-scanjet-5400c' into 'master' Resolve "Regression tests: HP Scanjet 5400C - scan defaults" See merge request sane-project/backends!321 commit 93a0971576da97aaaf7c62f6ad3c70e9e1ad94c9 Author: Ralph Little Date: 2020-02-01 11:48:28 -0800 hp5400: Change defaults for -x, -y, -l, -t for full page scan. Official doc says that the max length is 297 not 300 (which scans a bit of the plastic case at the bottom) commit 993a79b594a51991e2e00fed8f25b629429504fb Merge: 0d0c9f76b432 d8f09656b237 Author: Povilas Kanapickas Date: 2020-02-22 14:36:17 +0000 Merge branch 'genesys-duplication' into 'master' genesys: Reduce code duplication See merge request sane-project/backends!344 commit d8f09656b23756e7a6dccb35216c23a982bcecde Author: Povilas Kanapickas Date: 2020-02-22 11:02:57 +0200 genesys: Deduplicate strip searching functionality commit 5854246e88b053a7a77ead87491f5e5e88df0fec Author: Povilas Kanapickas Date: 2020-02-22 11:02:56 +0200 genesys: Remove duplicate way to retrieve register dpihw commit 01bd9b3cb227763f4096fb0f660b1a40e3ada118 Author: Povilas Kanapickas Date: 2020-02-22 11:02:55 +0200 genesys: Use register_dpihw_override to override dpihw commit 4da92aaa507e81e90675ad6a0a45d59c71498e38 Author: Povilas Kanapickas Date: 2020-02-22 11:02:54 +0200 genesys: Reuse scanner_clear_scan_and_feed_counts() commit 8735e965ee34acbb72129f4d8bed74c18ed996c9 Author: Povilas Kanapickas Date: 2020-02-22 11:02:53 +0200 genesys: Reduce duplication in init_regs_for_scan() commit e0d7d74cdd60fe4604b14cb1f32cb5a313bdc24a Author: Povilas Kanapickas Date: 2020-02-22 11:02:52 +0200 genesys: Inline sanei_gl841_repark_head() commit 0d0c9f76b4326b09cf91641f3a2c891d68d434a1 Merge: 881b17c65f3a 793703c93e29 Author: Povilas Kanapickas Date: 2020-02-22 14:18:04 +0000 Merge branch 'genesys-remove-search-start' into 'master' genesys: Remove start search functionality See merge request sane-project/backends!342 commit 793703c93e29f672c97e1a6e8b32bae9eea06f86 Author: Povilas Kanapickas Date: 2020-02-22 10:28:19 +0200 genesys: Update scan geometry on ScanJet 2300 commit 6653ec0ae65eda268033d40287f6f7e0084f1c8b Author: Povilas Kanapickas Date: 2020-02-22 10:28:18 +0200 genesys: Remove support for shading calibration without moving commit 2cf05553c017e6ddef48fbd292d7a63ed927ed77 Author: Povilas Kanapickas Date: 2020-02-22 10:28:17 +0200 genesys: Remove no longer used code related to start position search commit 5f8129b2d006a8ea9e7aa39da5b807bea8b25639 Author: Povilas Kanapickas Date: 2020-02-22 10:28:16 +0200 genesys: Disable SEARCH_START on gl646 scanners commit 511969ded07d8f6e55fd20a13cc3c296422215b0 Author: Povilas Kanapickas Date: 2020-02-22 10:28:15 +0200 genesys: Remove dead code on gl841 commit 881b17c65f3ab28f0203edba358c9b03b088eed5 Merge: fff74519f665 37297fdad3aa Author: Povilas Kanapickas Date: 2020-02-22 14:10:46 +0000 Merge branch 'genesys-sensor-width' into 'master' genesys: Define sensor width in mm See merge request sane-project/backends!343 commit 37297fdad3aa5746bd9bfabe79a2934ae9e5e397 Author: Povilas Kanapickas Date: 2020-02-22 10:48:27 +0200 genesys: Simplify definitions of x_size_calib_mm commit 9d7aa61544a749d76c8435f8b8f68165416600bd Author: Povilas Kanapickas Date: 2020-02-22 10:48:26 +0200 genesys: Define the width of the sensor in mm instead of pixels commit fff74519f665b9da129e21d316607c51d7f9d506 Merge: 50f02fc3f261 94ab4e2891e2 Author: Povilas Kanapickas Date: 2020-02-21 21:14:42 +0000 Merge branch 'genesys-canon-4400f' into 'master' Fix flatbed scans on Canon 4400F See merge request sane-project/backends!341 commit 94ab4e2891e24c069cdfb68b7402f27d68da49b1 Author: Povilas Kanapickas Date: 2020-02-21 22:40:34 +0200 genesys: Fix shading calibration start position on 4400F commit fa9f5370b2c2babfd63e8442ecff0f567053898f Author: Povilas Kanapickas Date: 2020-02-21 22:40:15 +0200 genesys: Turn off offset calibration on 4400F commit 50f02fc3f261ea582ed599cc5124c85cae2ae4c2 Merge: 494aa15141d5 4c395182be62 Author: Povilas Kanapickas Date: 2020-02-16 23:05:26 +0000 Merge branch 'genesys-remove-unused-code' into 'master' genesys: Remove unused code See merge request sane-project/backends!338 commit 4c395182be6214899b57991ef7b5c3c5081f99c9 Author: Povilas Kanapickas Date: 2020-02-17 00:49:14 +0200 genesys: Remove no longer used coarse calibration ced path commit 9e3bf1e1e26ed466071055809058379506e902ea Author: Povilas Kanapickas Date: 2020-02-17 00:49:13 +0200 genesys: Remove OFFSET_CALIBRATION as it's used on all scanners commit 494aa15141d52b526839453fa3b99af94caf33d4 Merge: 00d528b28cbf fff395928144 Author: Povilas Kanapickas Date: 2020-02-16 22:21:05 +0000 Merge branch 'genesys-calibration-area-config' into 'master' genesys: Improve calibration area configuration See merge request sane-project/backends!337 commit fff3959281448a6cd788340904d9d6fdefeb9f71 Author: Povilas Kanapickas Date: 2020-02-16 10:46:19 +0200 genesys: Increase low-brightness gain threshold commit 88d9c086456c76290d6c9b696016a6c6be659e3a Author: Povilas Kanapickas Date: 2020-02-16 10:46:18 +0200 genesys: Use scanner_move() on gl841 commit 4261549e2d4a0ddee0d7e48a51c7b67a02736725 Author: Povilas Kanapickas Date: 2020-02-16 10:46:17 +0200 genesys: Use separate settings for dark-white calibration area geometry commit aaf8a2fd49bd1afd79caa20e6d26484b8c2331a8 Author: Povilas Kanapickas Date: 2020-02-16 10:46:16 +0200 genesys: Simplify model table by not definition optional fields commit 4a11453b06e7c17733db0521097740d4c1ce56bd Author: Povilas Kanapickas Date: 2020-02-16 10:46:15 +0200 genesys: Use matching xdpi and ydpi when calibrating shading on gl841 commit 300bd3e645e6d2cb5648605dadc68d68262aca5a Author: Povilas Kanapickas Date: 2020-02-16 10:46:14 +0200 genesys: Fix incorrect calibration position due to rounding on gl841 commit 63adf8eb803afa8b061ba159977919295894073f Author: Povilas Kanapickas Date: 2020-02-16 10:46:13 +0200 genesys: Fix geometry of calibration strip on LiDE 60 commit 1e817987aa01ed2a427bc75b8bfee5d8af81f10a Author: Povilas Kanapickas Date: 2020-02-16 10:46:12 +0200 genesys: Fix geometry of calibration strip on LiDE 50 commit dcd5982de1273f3be5d7425634bd25ecd0e2c3c6 Author: Povilas Kanapickas Date: 2020-02-16 10:46:11 +0200 genesys: Improve LiDE 200 motor tables to fix lock-ups commit 38b921e844c2d7c6d92d167662fe57696fe2ca0c Author: Povilas Kanapickas Date: 2020-02-16 10:46:10 +0200 genesys: Improve LiDE 100 motor tables to fix lock-ups commit 55000d82ac9f7e22a04d200742ae595ea45f6787 Author: Povilas Kanapickas Date: 2020-02-16 10:46:09 +0200 genesys: Simplify shading line calculation by defining distance in mm commit 1a55cc99e4bc064544513d7fd5ca95a6a93ce9be Author: Povilas Kanapickas Date: 2020-02-16 10:46:08 +0200 genesys: Don't ignore stagger or color offsets when acquiring calib data commit 00d528b28cbf831a41006117a4f4d6f231d5cd29 Merge: 1fa3086cab4c 3f313376a7bd Author: Povilas Kanapickas Date: 2020-02-16 22:00:12 +0000 Merge branch 'genesys-misc-fixes' into 'master' genesys: Miscellaneous fixes and improvements See merge request sane-project/backends!336 commit 3f313376a7bda879e86923440791f524d8e57606 Author: Povilas Kanapickas Date: 2020-02-16 10:41:30 +0200 genesys: Remove unnecessary register write during init on gl843 commit b70b305d27e54e30ed806dba6af5942cea12bf35 Author: Povilas Kanapickas Date: 2020-02-16 10:41:29 +0200 genesys: Use fast motor tables on all gl843 models commit 2a3d5150cb9e207cad8ad201bd2555179091381e Author: Povilas Kanapickas Date: 2020-02-16 10:41:28 +0200 genesys: Simplify handling of fe register writes on gl843 commit 5b9aad649e43d572a98be01229adbdc5c968e289 Author: Povilas Kanapickas Date: 2020-02-16 10:41:27 +0200 genesys: Stop scanner after moving head on gl843 commit 498b52fe98847e34fcf59c65b11e85c8a719a6fe Author: Povilas Kanapickas Date: 2020-02-16 10:41:26 +0200 genesys: Use same buffer and gamma write method on all chipsets commit aca291906c08d649f1ba9ef4502405e97fe3af46 Author: Povilas Kanapickas Date: 2020-02-16 10:41:25 +0200 genesys: Remove unused debug code commit dcfa45c5cdca7b18a929b8a5f00ce952f278317b Author: Povilas Kanapickas Date: 2020-02-16 10:41:24 +0200 genesys: Move gamma address reset to scanner interface on gl843 commit 9eb13d5a10b7a317b7f90512dc02c9958643fa12 Author: Povilas Kanapickas Date: 2020-02-16 10:41:23 +0200 genesys: Remove reads from registers that are immediately overwritten commit 7619e95eaef2fc3c3a35a44c9319ab2ee61d9264 Author: Povilas Kanapickas Date: 2020-02-16 10:41:22 +0200 genesys: Cleanup progress message setup during calibration commit 537569926076537b5e02a91395f31c3027230033 Author: Povilas Kanapickas Date: 2020-02-16 10:41:21 +0200 genesys: Use same method to clear scan and feed counts commit de7375c7bb0961c642f020704815f502ceea3573 Author: Povilas Kanapickas Date: 2020-02-16 10:41:20 +0200 genesys: Remove legacy buffer handling code commit 000b28dd301e8a37874bdcace39b24609cf31ce0 Author: Povilas Kanapickas Date: 2020-02-16 10:41:19 +0200 genesys: Only ensure full exposure value is nonzero, not each byte commit ddd72058b65f3412c03abe4d8db40921155058bb Author: Povilas Kanapickas Date: 2020-02-16 10:41:18 +0200 genesys: Reuse sanei_genesys_fixup_exposure() commit b2d378d413f6f481818054af9670acf47853e390 Author: Povilas Kanapickas Date: 2020-02-16 10:41:17 +0200 genesys: Make sure we don't set exposure to zero commit 054a73c02c4372561604b88a7a9df3a94ed1b50d Author: Povilas Kanapickas Date: 2020-02-16 10:41:16 +0200 genesys: Fix calculation of secondary head position on gl843 commit b8fbdd346adda536d807e735d3b21c600b8d7a42 Author: Povilas Kanapickas Date: 2020-02-16 10:41:15 +0200 genesys: Fix incorrect register definition on Canon 8600F commit 51b09eec5c5f9e308b129215efb324ad14d79fb6 Author: Povilas Kanapickas Date: 2020-02-16 10:41:14 +0200 genesys: Don't set powersaving when setting cache expiration time commit 9bb7ed64b6eca1417cfd42ebce10eab647b82b8c Author: Povilas Kanapickas Date: 2020-02-16 10:41:13 +0200 genesys: Fix initialization of shading data when scanning in gray mode commit 6ab142155c4ed755355e8a086806b8b7419dea7a Author: Povilas Kanapickas Date: 2020-02-16 10:41:12 +0200 genesys: Correctly shutdown lamp on OpticFilm 7200i commit b78ea2913087d37f477dee78014fc533bd9b9b92 Author: Povilas Kanapickas Date: 2020-02-16 10:41:11 +0200 genesys: Don't try to turn off XPA lamp when shutting down regular lamp commit 2b584095ad8edb962b03502ee6615826579b236f Author: Povilas Kanapickas Date: 2020-02-16 10:41:10 +0200 genesys: Remove no longer needed head movement workaround for LiDE 210 commit 883114f722441c310450eb9ea3651a76499f03f0 Author: Povilas Kanapickas Date: 2020-02-16 10:41:09 +0200 genesys: Use similar scan session for head movements commit b54d12bc4daed7d5f8617aead4c3ed78ff71aff8 Author: Povilas Kanapickas Date: 2020-02-16 10:41:08 +0200 genesys: Define SetupParams::startx in terms of xres commit 1fa3086cab4c9a1df1737de527b3f1726e4697d0 Merge: d72e07c6714c 2428efd6af75 Author: Povilas Kanapickas Date: 2020-02-15 15:49:17 +0000 Merge branch 'genesys-canon-4400f' into 'master' genesys: Implement transparency support for Canon 4400F See merge request sane-project/backends!335 commit 2428efd6af7586358809a5d9704fda32687c9396 Author: Povilas Kanapickas Date: 2020-02-15 14:12:44 +0200 genesys: Fix host-side calibration when scan start position is not zero commit 6e7fc639f5b05899928966eee722976addf6266c Author: Povilas Kanapickas Date: 2020-02-15 14:12:43 +0200 genesys: Add initial support for transparency on 4400F commit aec9d7484532f1db374313b7d8048a4ad6513f45 Author: Povilas Kanapickas Date: 2020-02-15 14:12:42 +0200 genesys: Improve 4400F motor tables commit 207f67e30350621ed3d36a6dbc5c7e90e9f5fd41 Author: Povilas Kanapickas Date: 2020-02-15 14:12:41 +0200 genesys: Use fast tables on 4400F commit a1e292b8e3069a2eca68bcba11e39d45053eed13 Author: Povilas Kanapickas Date: 2020-02-15 14:12:40 +0200 genesys: Reflow sensor tables to take less space commit b2ebbba41a8455d0237c42fe34b30e5447f5e18f Author: Povilas Kanapickas Date: 2020-02-15 14:12:39 +0200 genesys: Move host-side calibration setting to the sensor commit d72e07c6714c8648f995ea67c413eef13d87a4d4 Merge: 60dc64f8f861 294db2a6166e Author: Povilas Kanapickas Date: 2020-02-14 18:16:23 +0000 Merge branch 'genesys-host-side-calibration' into 'master' genesys: Fix host side calibration See merge request sane-project/backends!334 commit 294db2a6166e096eca2bb979e175fae013986f70 Author: Povilas Kanapickas Date: 2020-02-10 06:28:36 +0200 genesys: Fix host-side calibration when calibration data is shifted commit 7f454b3e0eb683e815ed28a391f55307e74715a9 Author: Povilas Kanapickas Date: 2020-02-10 06:28:35 +0200 genesys: Observe DISABLE_SHADING flag during host-side calibration commit 60dc64f8f861ec400f2bb816a0df6b753a9dde06 Author: Rolf Bensch Date: 2020-02-13 18:41:18 +0100 pixma: backend version 0.27.1 commit 7f2f54bddce0e870434e6732dd348fb6ec310869 Merge: 06cd16a0ea1d c3d58edb08c8 Author: Rolf Bensch Date: 2020-02-13 18:28:14 +0100 Merge branch '242-canon-mb-5150' Closes #242 commit c3d58edb08c860dc64e398ff6f921d66fa25e67b Author: Rolf Bensch Date: 2020-02-13 18:27:04 +0100 pixma: Canon MAXIFY MB5100 Series sends ADF scans as JPEG image commit 06cd16a0ea1de45aa7055cb60e9ff650792a65ac Merge: 36f068a0ced4 f650f470b260 Author: Olaf Meeuwissen Date: 2020-02-12 11:27:41 +0000 Merge branch 'escl-crop-fix' into 'master' Fix convert size. See merge request sane-project/backends!332 commit f650f470b260d129a2ac0449f077bf900847da51 Author: thierry1970 Date: 2020-02-12 12:12:15 +0100 Replacing roundl by round. commit 87ff6c494a4e4ef25758b1bee370d14be8ddb2e6 Author: thierry1970 Date: 2020-02-12 11:51:02 +0100 Fix convert size. commit 36f068a0ced483d78e9a59d0c8c27226e6782eb2 Merge: 3508f3208fc7 4466d5192497 Author: Olaf Meeuwissen Date: 2020-02-11 07:38:28 +0000 Merge branch 'escl-page-crop' into 'master' Escl page crop Closes #230 See merge request sane-project/backends!316 commit 4466d51924975c3f34f2fee5f02419db24d63d5a Author: thierry1970 Date: 2020-02-11 08:22:43 +0100 Fixed variable name. commit 23cd9f5d6104c22cbd254445618bc627f6e16ba3 Author: Olaf Meeuwissen Date: 2020-02-11 01:45:11 +0000 Apply suggestion to backend/escl/escl_jpeg.c commit dd4fb84de81bcfe0caf570958ee1d04ee63484ab Author: Olaf Meeuwissen Date: 2020-02-11 01:45:08 +0000 Apply suggestion to backend/escl/escl.c commit a771be0c6a1f11a1263f466be2ca4fb2749dc9a2 Author: Ordissimo Date: 2020-02-10 08:45:38 +0000 Apply suggestion to backend/escl/escl_png.c commit bae9f0a3d3bc83928e95e0cf5abb7c63b9552e75 Author: Ordissimo Date: 2020-02-10 08:45:21 +0000 Apply suggestion to backend/escl/escl.h commit c3adeed4c46b30ce1f8185ee600ebb7b68b29d0f Author: Thierry HUCHARD Date: 2020-02-09 09:36:16 +0100 Fix style. commit 6a9a0beee89e80bb5fb13cfc7177354a16c061f2 Author: Thierry HUCHARD Date: 2020-02-09 09:30:43 +0100 Clarification and factoring of the code commit af6f6d46559efc0ebdc017d485573449a5d6347b Author: Thierry HUCHARD Date: 2020-02-08 22:36:49 +0100 Clarification of variable names. commit 7ea6af06243c307660138c3e01fde85dd7d32297 Author: Thierry HUCHARD Date: 2020-02-08 22:16:16 +0100 Replacing unit conversion functions with macros. commit 0ca9390279415d9da084a1bae3e39d96f84c4f6f Author: Thierry HUCHARD Date: 2020-01-31 18:32:32 +0100 Fix style. commit 5298b0e0186eb74f16996fa962271f0b6c91e54d Author: Thierry HUCHARD Date: 2020-01-31 18:28:04 +0100 Setting up the cut-out of the received image. commit d42521dfd4875f54b089b60de2866146a03d0a9b Author: Thierry HUCHARD Date: 2020-01-31 17:42:52 +0100 Geometry correction and use of the milimeter as a unit of measurement. commit 8c15724b432d6b60c30de04cf9ad8fa825f3b7da Author: Thierry HUCHARD Date: 2020-01-31 17:19:42 +0100 Added milimeter-to-pixel and pixel-to-milimeter conversion function. commit 58df3795ee7f6504b65e7fac35c5fe5a6d53e1ec Author: Thierry HUCHARD Date: 2020-01-27 20:47:56 +0100 Fix geometry. commit 3508f3208fc7597c1dbd49f0e610336bcb3eb1bb Merge: 336cbdfd4ca4 e1934720c687 Author: Povilas Kanapickas Date: 2020-02-10 07:42:52 +0000 Merge branch 'fix_genesys_bigendian' into 'master' genesys: fix bigendian build Closes #238 See merge request sane-project/backends!329 commit e1934720c687ed8c6125c75ac658f55b4e1513ce Author: Luiz Angelo Daros de Luca Date: 2020-02-02 21:19:15 -0300 genesys: fix bigendian build Signed-off-by: Luiz Angelo Daros de Luca commit 336cbdfd4ca408ef0d8935608a044cad64696c51 Author: m. allan noah Date: 2020-02-07 21:06:50 -0500 fujitsu backend v136 - add support for fi-800R - add support for card scanning slot (Return Path) - fix bug with reading hardware sensors on first invocation commit 03ca64a88598d5e286a5557907762fcb51886bd8 Merge: f7c01360cbb0 7415a9f5c97a Author: Olaf Meeuwissen Date: 2020-02-06 12:14:13 +0000 Merge branch '239-1-0-29-common_libs-in-libsane-la-dependencies-causes-potential-make-error' into 'master' Really remove libxml2 linker/loader flags from dependencies. Re #239 Closes #239 See merge request sane-project/backends!331 commit 7415a9f5c97acc655dae357dbffe7ff6b69ef82d Author: Olaf Meeuwissen Date: 2020-02-06 20:56:21 +0900 Really remove libxml2 linker/loader flags from dependencies. Re #239 commit f7c01360cbb04d5e704c1c7a217dddd312e61919 Merge: 20be9af3d7f3 17d760a5f848 Author: Olaf Meeuwissen Date: 2020-02-05 12:48:20 +0000 Merge branch '239-1-0-29-common_libs-in-libsane-la-dependencies-causes-potential-make-error' into 'master' Remove libxml2 linker/loader flags from dependencies. Re #239 Closes #239 See merge request sane-project/backends!330 commit 17d760a5f848f4034fbaa19657567fae52f8c150 Author: Olaf Meeuwissen Date: 2020-02-05 21:30:11 +0900 Remove libxml2 linker/loader flags from dependencies. Re #239 commit 20be9af3d7f3a6b0ca577ff6701b51f1f8accdd4 Merge: afa1c5440880 dc082d9066d1 Author: Povilas Kanapickas Date: 2020-02-02 23:16:29 +0000 Merge branch 'genesys-cleanup-calibration-regs' into 'master' genesys: Cleanup data used for calibration See merge request sane-project/backends!328 commit dc082d9066d1142ae0cdd69d1cd9e495a5e9d9c5 Author: Povilas Kanapickas Date: 2020-02-03 00:42:06 +0200 genesys: Add a note about calibration size calculation commit 9a142d6acc3de2bf160e7cde44046ae49d283020 Author: Povilas Kanapickas Date: 2020-02-03 00:42:05 +0200 genesys: Remove uses of calib_total_bytes_to_read commit 328f39e39e6b40d0bcdaa28371ebb13b5113dcea Author: Povilas Kanapickas Date: 2020-02-03 00:42:04 +0200 genesys: Remove uses of calib_lines commit 53d51a6537c1dd4005a1596d08e0de7b1a5b7746 Author: Povilas Kanapickas Date: 2020-02-03 00:42:03 +0200 genesys: Fix wrong line count computation on gl646 It actually does not matter, because we overwrite LINCNT below anyway and don't use any information affected by this variable when computing how much data to retrieve. commit 26eec0ba574ef66f6283d3f0a1d82ff4f1cc9fd1 Author: Povilas Kanapickas Date: 2020-02-03 00:42:02 +0200 genesys: Remove uses of calib_channels commit 9ddc64826c014638cd3422441c0e70431bf90fa8 Author: Povilas Kanapickas Date: 2020-02-03 00:42:01 +0200 genesys: Remove uses of calib_resolution commit 10fb42cd68eddd928a38d576258135db89ca08e8 Author: Povilas Kanapickas Date: 2020-02-03 00:42:00 +0200 genesys: Use calib_session to retrieve pixel counts commit 87b27136289ecf117d67475d1163ae294ce2bd28 Author: Povilas Kanapickas Date: 2020-02-03 00:41:59 +0200 genesys: Implement serialization of ScanSession commit fe02dc36d8d231d9764693284626862d5460ff93 Author: Povilas Kanapickas Date: 2020-02-03 00:41:58 +0200 genesys: Implement comparison of ScanSession commit 9b1fe59c2a7efa5520b379714c0483757f65a30e Author: Povilas Kanapickas Date: 2020-02-03 00:41:57 +0200 genesys: Set calib_session on all chipsets commit 3b27241e0740959bdbebab2399ff47685801b617 Author: Povilas Kanapickas Date: 2020-02-03 00:41:56 +0200 genesys: Remove no longer needed conversions to single precision float commit 325aba25f1f7b30bc3f4a05e78b914b778851c39 Author: Povilas Kanapickas Date: 2020-02-03 00:41:55 +0200 genesys: Use float to store tl_x and tl_y scan settings commit 0a49fa10ee1fa490432b016cabbf8f428aaeff1d Author: Povilas Kanapickas Date: 2020-02-03 00:41:54 +0200 genesys: Extract register initialization out of init_regs_for_shading() commit c1791e6c839a61abc7d3ef803872f81440b408ef Author: Povilas Kanapickas Date: 2020-02-03 00:41:53 +0200 genesys: Pass the register set to modify to init_regs_for_scan() commit 5fb5da4f788630ae44ab91741c137b9c93307ff7 Author: Povilas Kanapickas Date: 2020-02-03 00:41:52 +0200 genesys: Remove duplicate way to check for transparency support commit 4cfc150bdcb8d0206ae822622ae00f550c72f9e5 Author: Povilas Kanapickas Date: 2020-02-03 00:41:51 +0200 genesys: Add a way to check whether a model supports scan method commit afa1c54408806480893643f1a85b777cd3232ba8 Merge: 34c15b47d9fd cce5e21829b1 Author: Povilas Kanapickas Date: 2020-02-02 17:01:26 +0000 Merge branch 'genesys-cleanup-calibration-regs' into 'master' genesys: Cleanup register data that is used during calibration See merge request sane-project/backends!327 commit cce5e21829b1efbfdc22c2357ec9bf4927620c77 Author: Povilas Kanapickas Date: 2020-02-02 18:45:32 +0200 genesys: Remove several unnecessary initializations of dev->initial_regs commit 32041bc7fedb64a031e3e75c01c9edd9aa94fda4 Author: Povilas Kanapickas Date: 2020-02-02 18:45:31 +0200 genesys: Rename calib_reg to initial_regs commit 16e8ca35119963846dda94ae0cf13636aa337a60 Author: Povilas Kanapickas Date: 2020-02-02 18:45:30 +0200 genesys: Don't modify calib_reg when calibrating commit 86fe2f5f6d32bf78a9242ecae5733dcd9e83c600 Author: Povilas Kanapickas Date: 2020-02-02 18:45:29 +0200 genesys: Move modification of calib_reg out of shading reg init on gl646 commit 87473910cf85c638b9efafd027990ec956d3d0bb Author: Povilas Kanapickas Date: 2020-02-02 18:45:28 +0200 genesys: Use regular registers when computing what coefficient to send commit 8e4c332ea69fdb674ebf371f4fe274645de2d6ca Author: Povilas Kanapickas Date: 2020-02-02 18:45:27 +0200 genesys: Pass registers from callers to shading calibration commit 2a3ae40d8ddc4003508034a10a32f8de0925fd1f Author: Povilas Kanapickas Date: 2020-02-02 18:45:26 +0200 genesys: Pass registers from callers to dark-white shading calibration commit 25f097b44597c920535bb333259acdd231e96748 Author: Povilas Kanapickas Date: 2020-02-02 18:45:25 +0200 genesys: Pass registers from callers in coarse calibration commit d80db65c5864f5eccc8baecd0780c507b23df7c7 Author: Povilas Kanapickas Date: 2020-02-02 18:45:24 +0200 genesys: Move writing to registers to coarse calibration functions commit b1b57026ddaf8a04a68590c6f745cad1463d21c5 Author: Povilas Kanapickas Date: 2020-02-02 18:45:23 +0200 genesys: Move writing to registers to calibration functions commit dff52ad7136c5d5f1e83f888d660820fbff85b94 Author: Povilas Kanapickas Date: 2020-02-02 18:45:22 +0200 genesys: Don't write registers in init_regs_for_warmup() commit 55e32001583ddee2da0a911dfe2ecd1a593fcc91 Author: Povilas Kanapickas Date: 2020-02-02 18:45:21 +0200 genesys: Don't write registers in init_regs_for_coarse_calibration() commit bdc671b58be6a74e944d9c88119f25f3c381682a Author: Povilas Kanapickas Date: 2020-02-02 18:45:20 +0200 genesys: Don't perform register writes in init_regs_for_shading() commit 34c15b47d9fd839a5334665a869bc0944a073534 Merge: 03d50b5e64fd 9fc3b38a9b0e Author: Povilas Kanapickas Date: 2020-02-02 13:38:19 +0000 Merge branch 'genesys-use-float-for-fixed' into 'master' genesys: Use float precision for data that was previously saved in fixed format See merge request sane-project/backends!326 commit 9fc3b38a9b0e92e5e74bd3b49c292f231c9e14c3 Author: Povilas Kanapickas Date: 2020-02-02 15:23:21 +0200 genesys: Use fixed float utilities instead of SANE_{FIX,UNFIX} commit 7ee775e983911577b74a64db40809933284e44f8 Author: Povilas Kanapickas Date: 2020-02-02 15:23:20 +0200 genesys: Remove the FixedFloat class commit 62c6959df4b6834e98a86b8c7085ffd0b7081ef6 Author: Povilas Kanapickas Date: 2020-02-02 15:23:19 +0200 genesys: Add utilities for fixed floats commit 03d50b5e64fd781540ac259649d312bd179e8cae Merge: f913ed58b688 e59401ed6b92 Author: Povilas Kanapickas Date: 2020-02-02 12:25:16 +0000 Merge branch 'genesys-model-flag-enum' into 'master' genesys: Move model flags to separate enum See merge request sane-project/backends!325 commit e59401ed6b923ebb16123d3da5cd4f696582bdd1 Author: Povilas Kanapickas Date: 2020-02-02 14:08:52 +0200 genesys: Move model flags to a separate enum commit de10ace2e6b0d635ed56b9213dfe17586835576a Author: Povilas Kanapickas Date: 2020-02-02 14:08:51 +0200 genesys: Remove invalid usage of GENESYS_HAS_NO_BUTTONS commit 1c3d1e25ecc763e011819a72c3370c089d5f8010 Author: Povilas Kanapickas Date: 2020-02-02 14:08:50 +0200 genesys: Remove unused model flags commit f913ed58b6882aabbf87f1893b74cf90b26029a2 Author: Olaf Meeuwissen Date: 2020-02-02 21:11:31 +0900 escl: Remove :new marker from backend's description file commit 5e5cdad7dd611faf39d50301bd614fa68e1bc4c9 Merge: 9e9bbb5a802c e52a5bf71979 Author: Olaf Meeuwissen Date: 2020-02-02 21:06:46 +0900 Merge branch 'release/1.0.29' commit 9e9bbb5a802ca2a078e4d84c50dd7414e85138b8 Merge: 3d0a626944f3 fee5b7e04f54 Author: Povilas Kanapickas Date: 2020-02-02 11:29:04 +0000 Merge branch 'genesys-improve-debug' into 'master' genesys: Improve debug output slightly See merge request sane-project/backends!324 commit fee5b7e04f54b650a9e774ccbb84f2192e46bc4b Author: Povilas Kanapickas Date: 2020-02-02 13:11:49 +0200 genesys: Condense SetupParams debug dump output commit 9d7f7abdcd009fcc108a4e7523461cb62a9cb19d Author: Povilas Kanapickas Date: 2020-02-02 13:11:48 +0200 genesys: Remove extraneous debugging information commit 3d0a626944f3b818ef055bf5c4b0779eff19c420 Merge: 12e113ab5225 9d1788cf5d82 Author: Povilas Kanapickas Date: 2020-02-02 11:25:16 +0000 Merge branch 'genesys-fix-active-area-calib' into 'master' genesys: Fix issues in active area calibration See merge request sane-project/backends!323 commit 9d1788cf5d821b3d05cbf2f060648381c76788ee Author: Povilas Kanapickas Date: 2020-02-02 13:08:50 +0200 genesys: Fix loss of precision in during active area offset calibration commit 91d9d8b2dcad6171ac5f258181b4c6f95696523d Author: Povilas Kanapickas Date: 2020-02-02 13:08:49 +0200 genesys: Fix incorrect resolution being used in active area calibration commit 6bbdb7afc8a24dbab33189549fb5c8a9c5cc2b53 Author: Povilas Kanapickas Date: 2020-02-02 13:08:48 +0200 genesys: Extract function to enable active area shading commit 12e113ab52254ebbf86eab4902df798e67545967 Merge: 07e3d5d4d6f6 6e2e885ec564 Author: Povilas Kanapickas Date: 2020-02-01 21:43:09 +0000 Merge branch 'genesys-motor-fixes' into 'master' genesys: Motor table fixes See merge request sane-project/backends!322 commit 6e2e885ec564ada7cb5295d9e7bbafe1a88ed4f5 Author: Povilas Kanapickas Date: 2020-02-01 23:28:24 +0200 genesys: Use same step type in motor tables on 8600F commit 06cee81dbe7d65f9a399aef19a0d92bb0d51b11c Author: Povilas Kanapickas Date: 2020-02-01 23:28:23 +0200 genesys: Fix incorrect selection of step type for fast table on gl843 commit 07e3d5d4d6f69cf63209bdb705253d42fea1e4b9 Merge: 50788b382739 ac5e086eb7c7 Author: Povilas Kanapickas Date: 2020-02-01 15:13:57 +0000 Merge branch 'genesys-improve-motor-tables' into 'master' genesys: Improve motor tables on 8400F See merge request sane-project/backends!320 commit ac5e086eb7c73954f05bfc5d781d9def6bdc06f1 Author: Povilas Kanapickas Date: 2020-02-01 16:57:12 +0200 genesys: Improve motor tables on 8400F commit 50788b382739eb0403b5c7aea11fa265ef896c36 Merge: 6f9a3cfa2cde d19e48d5ab7d Author: Povilas Kanapickas Date: 2020-02-01 12:47:38 +0000 Merge branch 'genesys-improve-motor-tables' into 'master' genesys: Improve motor tables See merge request sane-project/backends!319 commit d19e48d5ab7df65aaa829ebb1fd4c103d874072f Author: Povilas Kanapickas Date: 2020-02-01 14:23:04 +0200 genesys: Improve motor tables on OpticFilm 7500i commit f3f0143500eb1ed9da5d08697a552aaa401f01e4 Author: Povilas Kanapickas Date: 2020-02-01 14:23:03 +0200 genesys: Improve motor tables on OpticFilm 7300 commit 8b3b85c160a762b6f05ed5a4c75e5534f740502f Author: Povilas Kanapickas Date: 2020-02-01 14:23:02 +0200 genesys: Improve motor tables on OpticFilm 7200i commit 0b75ffdf367695bc28cbf2e474adf23d0973f05c Author: Povilas Kanapickas Date: 2020-02-01 14:23:01 +0200 genesys: Improve motor tables on 8400F commit 6d15a0da5a65a1d9729b8a992e58831144edb256 Author: Povilas Kanapickas Date: 2020-02-01 14:23:00 +0200 genesys: Improve motor tables on 8600F commit 6f9a3cfa2cde00fe35c3ea99d272aa48e1d24636 Merge: 26e3d6c1f37f a5d63baf3d19 Author: Povilas Kanapickas Date: 2020-01-31 22:58:52 +0000 Merge branch 'genesys-improve-motor-support' into 'master' genesys: Improve motor support See merge request sane-project/backends!318 commit a5d63baf3d194ceac6cc882acbe127f832204a0c Author: Povilas Kanapickas Date: 2020-01-31 20:13:18 +0200 genesys: Implement support for fast movement curves in the motor table commit 91d5235d4b38ebde70eb43eb0a8d431c7e35a640 Author: Povilas Kanapickas Date: 2020-01-31 20:13:17 +0200 genesys: Make get_motor_profile* reusable for different profile sets commit 247a6b7882d03df19fa0c9b376a5b9f2a0e5e2fb Author: Povilas Kanapickas Date: 2020-01-31 20:13:16 +0200 genesys: Add support for per-resolution or per-method motor profiles commit 479718a6b5543c4cc15ba6b46611e830019312e0 Author: Povilas Kanapickas Date: 2020-01-31 20:13:15 +0200 genesys: Add motor vref to motor profiles commit 002d2ff2db43291a3435ff6356baac394a4cac08 Author: Povilas Kanapickas Date: 2020-01-31 20:13:14 +0200 genesys: Use motor profiles from motor tables commit e5e8ea3d3f7d32fb6507d9e6171c2953eab9a9f9 Author: Povilas Kanapickas Date: 2020-01-31 20:13:13 +0200 genesys: Add motor profile information to motors table commit 879587f303334e4fb334b8f45b297490de203482 Author: Povilas Kanapickas Date: 2020-01-31 20:13:12 +0200 genesys: Rename Genesys_Motor::{get_slope -> get_slope_with_step_type} commit 7dca191b14d7660545e8c37f3c0228362ccda130 Author: Povilas Kanapickas Date: 2020-01-31 20:13:11 +0200 genesys: Merge chip-specific motor tables into one commit 2d0fc3ce5543dc4ba36ba08c021353bcf519dc39 Author: Povilas Kanapickas Date: 2020-01-31 20:13:10 +0200 genesys: Don't rely on motor profile fallbacks commit 26e3d6c1f37f50a023b838d31b929b8c22a32892 Merge: 1942c5fd32a7 0a7813177bb7 Author: Povilas Kanapickas Date: 2020-01-31 22:32:04 +0000 Merge branch 'genesys-reduce-duplication' into 'master' genesys: Reduce duplication in session setup code See merge request sane-project/backends!317 commit 0a7813177bb7ec5753900820c473591246e26523 Author: Povilas Kanapickas Date: 2020-01-25 11:28:30 +0200 genesys: Reduce code duplication in gl841 session setup code commit 374578abce229f742040eddedaf0ddb6870ad4ab Author: Povilas Kanapickas Date: 2020-01-25 11:28:29 +0200 genesys: Reduce code duplication in gl843 session setup code commit 1942c5fd32a7623351e4f73dc7590854debe4698 Merge: e820ed0f6cee 76726491d901 Author: Olaf Meeuwissen Date: 2020-01-26 10:56:34 +0000 Merge branch 'escl-simple-scan' into 'master' Displays SSL if the device uses https. Closes #229 See merge request sane-project/backends!313 commit 76726491d901ca0e789cdd52d3b95ac89821281b Author: Thierry HUCHARD Date: 2020-01-26 10:16:06 +0100 Billing for cleaning. commit bb5b109560794296ab8c2dc46215910fa425908d Author: Thierry HUCHARD Date: 2020-01-26 08:28:19 +0100 Fix memory leak. commit bb9c4ead76a08fb99cc64aa8d18849c00846575e Merge: 70ae518fc621 e820ed0f6cee Author: Thierry HUCHARD Date: 2020-01-25 17:32:10 +0100 Merge remote-tracking branch 'origin/master' into escl-simple-scan commit e820ed0f6ceeb49d3064e7e0a5afd26cca14eeac Merge: 5747ffa9fb91 99fde7b12ee0 Author: Povilas Kanapickas Date: 2020-01-25 13:27:10 +0000 Merge branch 'genesys-bugs' into 'master' genesys: Fix various bugs on 8400F and 8600F See merge request sane-project/backends!314 commit 99fde7b12ee0d7c8c80f7a1aa559ba410d59a9d2 Author: Povilas Kanapickas Date: 2020-01-25 11:28:12 +0200 genesys: Slightly tweak X TA offset on 8600F commit 0c84e1951a01a69614eaf886163ea47585f44f6c Author: Povilas Kanapickas Date: 2020-01-25 11:28:11 +0200 genesys: Fix incorrect X TA offset on 8600F commit 9c10d59ce81816c843d89b194bac5b852f8e9157 Author: Povilas Kanapickas Date: 2020-01-25 11:28:10 +0200 genesys: Fix 3200 dpi flatbed support on 8400F commit b9a4054775cd32ef294480dfc0b6f106f55b1dac Author: Povilas Kanapickas Date: 2020-01-25 11:28:09 +0200 genesys: Fix sensor tables on 8400F commit 15bbc3d0b30b278a817e870bf6ee2674a8fa7726 Author: Povilas Kanapickas Date: 2020-01-25 11:28:08 +0200 genesys: Rewrap sensor settings for 8400F commit 0abb83ead4a146581f861bab3a2800be639f74fc Author: Povilas Kanapickas Date: 2020-01-25 11:28:07 +0200 genesys: Fix incorrect number of sensor pixels on 8400F commit 75b2bd6445ab75fb693902cb9b44f8ebc8a7e041 Author: Povilas Kanapickas Date: 2020-01-25 11:28:06 +0200 genesys: Fix incorrect calculation of X offsets on 8400F commit 8532e6319411ed6f9a72162f1686538102791386 Author: Povilas Kanapickas Date: 2020-01-25 11:28:05 +0200 genesys: Fix incorrect calculation of X offsets on 8600F commit 1748e625f0ccfe8f846ade83493a2973a1b3ca52 Author: Povilas Kanapickas Date: 2020-01-25 11:28:04 +0200 genesys: Fix recovery from interrupted transparency scans on 8600F commit 70ae518fc621b7fa72c27919d199cc71c6af7a18 Author: thierry1970 Date: 2020-01-23 13:02:38 +0100 Fix break build. commit a0845a83222cf867d367915f6a5a150298cd3a33 Author: thierry1970 Date: 2020-01-23 12:20:48 +0100 Verify allocation and add debug. commit 51b9883603d8196ee189a7e8c66351062fbaff11 Author: thierry1970 Date: 2020-01-23 08:53:19 +0100 Displays SSL if the device uses https. commit 5747ffa9fb914b88c469a7c1d6d5357f85a94577 Author: Gerhard Jäger Date: 2020-01-13 09:38:30 +0100 Issue#113: Fix applied (thanks David Binderman). commit 8d07515bea266994e03aedeb4f770c9bd8e1a52c Merge: 6be33accf284 c5f397e778b6 Author: Povilas Kanapickas Date: 2020-01-12 19:26:35 +0000 Merge branch 'genesys-refactor-motor-handling' into 'master' genesys: Refactor motor handling See merge request sane-project/backends!308 commit c5f397e778b6eeae3cabf202a67d3918710191f3 Author: Povilas Kanapickas Date: 2020-01-12 10:57:11 +0200 genesys: Extract handle_motor_position_after_hove_back_home_ta() commit e8e56e33d30394db30e04d2d267e3f461b2c0021 Author: Povilas Kanapickas Date: 2020-01-12 10:57:10 +0200 genesys: Make XPA motor mode handling more generic commit 23f3f80e3843549174cb01e19963687eb977db95 Author: Povilas Kanapickas Date: 2020-01-12 10:57:09 +0200 genesys: Remove duplicate head position reset commit 645820bc4d3bf7e4e33ece892f607c2ad8db99b1 Author: Povilas Kanapickas Date: 2020-01-12 10:57:08 +0200 genesys: Improve set_head_pos_unknown() to set status per head commit a676810f5da18f077ec533221387f0bc391ae1d1 Author: Povilas Kanapickas Date: 2020-01-12 10:57:07 +0200 genesys: Implement function to apply settings with backup commit 05978e1c8b84d384852fd80f0e68bfe04a11a760 Author: Povilas Kanapickas Date: 2020-01-12 10:57:06 +0200 genesys: Move *_set_xpa_lamp_power() to common command set commit cbb3d98e838224a970a33775e8f97d0c271086fb Author: Povilas Kanapickas Date: 2020-01-12 10:57:05 +0200 genesys: Move *_set_xpa_motor_power() to common command set commit 3753716e04e5d339df70a444cc7891506b9f19eb Author: Povilas Kanapickas Date: 2020-01-12 10:57:04 +0200 genesys: Add a class to move common command set functions to commit 8b129b32ff1117bd08e8d7278376bd0411ae2d40 Author: Povilas Kanapickas Date: 2020-01-12 10:57:03 +0200 genesys: Simplify handling of xpa motor state on gl843 commit 6be33accf284e1aca1c0f5abbd946df57a91f4ff Merge: 7b45c89b023d 45952fdd69b6 Author: Povilas Kanapickas Date: 2020-01-12 19:16:57 +0000 Merge branch 'genesys-refactor-scanner-read' into 'master' genesys: Refactor data reading from scanner See merge request sane-project/backends!307 commit 45952fdd69b698e2674ddb7bae375c88f8e36a0a Author: Povilas Kanapickas Date: 2020-01-04 12:09:29 +0200 genesys: Reduce duplication of scanner read methods on gl847 commit 8bd8174cc9c030ad835f8025f80b25595b90f843 Author: Povilas Kanapickas Date: 2020-01-04 12:09:28 +0200 genesys: Reduce duplication of scanner read methods on gl846 commit d74c4b6b6830f183413650a34c010cc08829c1f8 Author: Povilas Kanapickas Date: 2020-01-04 12:09:27 +0200 genesys: Reduce duplication of scanner read methods on gl841 commit 67d3c134a9ce465571866b7223a3fb1949c5f09f Author: Povilas Kanapickas Date: 2020-01-04 12:09:26 +0200 genesys: Reduce duplication of scanner read methods on gl124 commit c39cb0d4b99591bf71de8c3a77d218642cdffac1 Author: Povilas Kanapickas Date: 2020-01-04 12:09:25 +0200 genesys: Fix definition of session's output_segment_pixel_group_count commit 2269e05facaf07abeb00a5f59c3a31b4abdd9d2f Author: Povilas Kanapickas Date: 2020-01-04 12:09:24 +0200 genesys: Fix pipeline setup in read_unshuffled_image_from_scanner() commit 8d2081cda8eeb28eaeb04d25306b5aa4f192ec79 Author: Povilas Kanapickas Date: 2020-01-04 12:09:23 +0200 genesys: Deduplicate calculation of scan line count commit b5b50758f5583396a635366fcf421fb7d35469df Author: Povilas Kanapickas Date: 2020-01-04 12:09:22 +0200 genesys: Remove out of date note commit eb45a1b709c9dcfbd4270ece7c628a81eb33df74 Author: Povilas Kanapickas Date: 2020-01-04 12:09:21 +0200 genesys: Handle segmented sensors in read_unshuffled_image_from_scanner commit 444f25b96fc0953a82c539e40dd860f44202ec2b Author: Povilas Kanapickas Date: 2020-01-04 12:09:20 +0200 genesys: Remove duplicate computations of line length backends-1.3.0/ChangeLogs/ChangeLog-1.0.32000066400000000000000000002201371456256263500176130ustar00rootroot00000000000000commit c1c567c49b3bc9d86a5796b7fd9b17816700f75c Author: Olaf Meeuwissen Date: 2021-02-14 20:00:00 +0900 NEWS: Document changes for 1.0.32 release commit eda0a770bf52c4ae446779d8ad8af005a8a974f9 Author: Olaf Meeuwissen Date: 2021-02-14 16:24:24 +0900 po: Re-sync *.po files to pick up latest source code changes Lines number have changes due to the FSF address change. commit edcfdbc0d801547d3c1e0ab8bd62dad74295c54e Author: Olaf Meeuwissen Date: 2021-02-13 20:21:45 +0900 saned: Add new -p, --port option to the manual page This complements !549. commit 3408eef8ed04bbffd2cf601850ccee16e596e4b1 Author: Olaf Meeuwissen Date: 2021-02-13 20:20:14 +0900 escl.desc: Add entries for models newly supported since 1.0.31 These were mentioned in the commit logs and merge requests but missing from the descriptions file. commit e0b5a7e40748490bccfe10fbc2cd2d5a469cfd90 Merge: 102288c22eb3 9c46d6bcaccf Author: Olaf Meeuwissen Date: 2021-02-14 09:31:59 +0000 Merge branch 'disable-canoscan-4400f' into 'release/1.0.32' genesys: Disable support for the CanoScan 4400F See merge request sane-project/backends!592 commit 9c46d6bcaccf38df870f22b5bc78ca8cc2eba692 Author: Olaf Meeuwissen Date: 2021-02-14 10:46:36 +0900 genesys: Disable support for the CanoScan 4400F. Re #436 commit 102288c22eb33e2becb38ff866fdb3260f58cefb Merge: ef8e403572d9 e3962ad18434 Author: Olaf Meeuwissen Date: 2021-02-14 07:18:00 +0000 Merge branch 'add-files-to-source-tarball' into 'release/1.0.32' Add extra documentation and utility file to the source tarball See merge request sane-project/backends!593 commit e3962ad1843432bdf11c7ac31338491407a41960 Author: Olaf Meeuwissen Date: 2021-02-14 16:01:01 +0900 Add extra documentation and utility file to the source tarball These might be useful for someone starting from one of our release or snapshot source tarballs. commit ef8e403572d91cb8bac4ef63ba9cae21fc46f5dc Merge: 1532520fa14b 85f4ef106577 Author: Ralph Little Date: 2021-02-13 20:20:01 +0000 Merge branch '433-issues-in-sane-man-pages' into 'release/1.0.32' Resolve "Issues in SANE man pages" See merge request sane-project/backends!581 commit 85f4ef10657735b0322c9f545b426b857a28b502 Author: Ralph Little Date: 2021-02-13 12:02:54 -0800 doc: final pass through the man pages. Mostly just tidy up, catching pages that I missed the first time round. A small number of pages were given a bit more reformat. A bit beyond the remit of this merge request but not too much has been changed. commit 0eaae38d1e546ddd67b811ef81791194cf5c4687 Author: Ralph Little Date: 2021-02-12 18:01:30 -0800 doc: remaining man pages done. I will quickly review them all and check in corrections. I know that there are a few things that I did wrong in the first pass and I will fix them up. commit 30eaec1f00f6d5edfec8f7ce79baa6fb5c0541bc Author: Ralph Little Date: 2021-02-02 12:58:54 -0800 doc: additional format tweaks to man pages hpljm1005-kodakaio commit 460898b18ff0657550a06d1bdfba1a4f393441f8 Author: Ralph Little Date: 2021-01-31 23:19:48 -0800 doc: hp manpages tweaked. commit 6a51099e9fe313a29a92cc60bd7189e7b67a59cf Author: Ralph Little Date: 2021-01-31 22:06:39 -0800 doc: some more consistency changes to format. commit 79b866619b6292da00d385fdd3c7d182a0634d0f Author: Ralph Little Date: 2021-01-31 19:44:13 -0800 doc: corrections for trailing space and a batch of man pages commit cfa36aa47a7d768d7db694ed5702c70bf8448565 Author: Ralph Little Date: 2021-01-31 17:44:58 -0800 doc: implemented some reformatting and wording suggestions Partly through suggestions from translators but also from canonical man page references for consistency and conformity. I have also removed some unnecessary wordiness from some of the backend supporting lists which contained verbose repetition. commit 742989b65807e27bb9d6a1343e949094ddca1649 Author: Ralph Little Date: 2021-01-30 12:25:09 -0800 man: grammar and formatting for gamma4scanimage.man and sane.man commit ab945b1a89cb193a28ea2aa50c084c28d1be0ca0 Merge: b90ecabf8c62 a7a8cb44d4f1 Author: Olaf Meeuwissen Date: 2021-01-25 11:09:24 +0000 Merge branch 'ci-bump-alpine-fedora-versions' into 'master' CI: Bump alpine fedora versions See merge request sane-project/backends!579 commit a7a8cb44d4f1e2f31fc09825fed846ba31a2b195 Author: Olaf Meeuwissen Date: 2021-01-24 21:22:34 +0900 CI: Bump Fedora from 32 to 33 commit 8462b2d75ae83a6930eccfe014fded249572a774 Author: Olaf Meeuwissen Date: 2021-01-24 21:22:07 +0900 CI: Bump Alpine from 3.12 to 3.13 commit 1532520fa14b43972c708282efbb19b436dd4ce8 Merge: 9ec6ec40a926 4c8ac5e0a6bd Author: Olaf Meeuwissen Date: 2021-02-12 09:52:08 +0000 Merge branch '320-incorrect-fsf-address-in-license-and-copying' into 'release/1.0.32' Resolve "Incorrect FSF address in LICENSE and COPYING" See merge request sane-project/backends!591 commit 4c8ac5e0a6bd565a59099426aa7212eb780175d5 Author: Olaf Meeuwissen Date: 2021-02-12 18:23:53 +0900 COPYING: Sync with current plain text version on the FSF site This updates the Free Software Foundation's postal address. It replaces references to the Library General Public License (LGPL-2) with references to the GNU Lesser Public License (LGPL-2.1) as well. These licenses differ slightly, the latter adds section 6b, but the references are *outside* the TERMS AND CONDITIONS section. Moreover, the first reference is in connection with clarifying the licensing of Free Software Foundation software and the second reference is merely a suggestion to use the LGPL in case you want to allow linking your code with proprietary software. In view of that, these changes in COPYING do *not* have an effect on the licensing status of the SANE Backends. Re #320. commit e79adde8b2d713448f3022632f410d4a9de0fb91 Author: Olaf Meeuwissen Date: 2021-02-12 17:41:38 +0900 Use URL to refer users to copies of the GPL and LGPL With the exception of use in three quotes as well as our inlcuded copy of the GPL, all use of the Free Software Foundation's postal address has been removed. Re #320. commit 9ec6ec40a926cc78cdba7e02168c54c68a5944f9 Merge: a21f4713da86 51a7abd1fb94 Author: Olaf Meeuwissen Date: 2021-02-09 09:14:43 +0000 Merge branch 'update_uk3' into 'release/1.0.32' Update Ukrainian translation See merge request sane-project/backends!589 commit 51a7abd1fb940c299cbad31c6da57217e5b8ef57 Author: Yuri Chornoivan Date: 2021-02-08 15:33:42 +0200 Update Ukrainian translation commit a21f4713da86f355941307c91d03cc64c211d045 Merge: 5db03a05935f ae2c4068428a Author: Olaf Meeuwissen Date: 2021-02-08 13:15:43 +0000 Merge branch 'sync-po-files-with-source' into 'release/1.0.32' I18N: Sync po/*.po files with sources slated for next release See merge request sane-project/backends!588 commit ae2c4068428a9345670b0b9902555a00bc868555 Author: Olaf Meeuwissen Date: 2021-02-08 21:58:34 +0900 I18N: Sync po/*.po files with sources slated for next release commit 5db03a05935f06fe31035a08ff6cc81007d83efe Merge: 335edb9ed87f a83126fc2373 Author: Olaf Meeuwissen Date: 2021-02-07 12:25:51 +0000 Merge branch '124-configure-disable-shared-errors-out-linking-scanimage' into 'release/1.0.32' Resolve "./configure --disable-shared errors out linking scanimage" See merge request sane-project/backends!586 commit a83126fc2373e838cae4a7cc41b5ad572fcc877d Author: Olaf Meeuwissen Date: 2021-02-07 21:06:38 +0900 Work around missing C++ symbols for static linking scenarios commit fe7b1a8f2d9e1d63ccd685263fb7a7bcf4dd642d Author: Olaf Meeuwissen Date: 2021-02-07 19:33:11 +0900 Add missing backend dependencies for static linking. Re #124 commit 4cb49c1f0819c0dffb38e63c28f6cd3dbc12adee Author: Olaf Meeuwissen Date: 2021-02-07 19:32:24 +0900 Disambiguate symbols for static linking scenarios. Re #124 commit 335edb9ed87f4367f5aed91cb8c95830b3af752d Merge: 68312608d13a fea0ccca1ea8 Author: Olaf Meeuwissen Date: 2021-02-07 07:42:06 +0000 Merge branch '354-genesys-testsuite-built-even-when-backend-isnt' into 'release/1.0.32' Build genesys testsuite only when backend is build See merge request sane-project/backends!585 commit fea0ccca1ea879ccdedcfc8feb1de82905a37d98 Author: Olaf Meeuwissen Date: 2021-02-07 16:25:30 +0900 Build genesys testsuite only when backend is build. Re #354 commit 68312608d13adcabea808f2239f52c4183dd493e Merge: 5ff049699846 e86771c21bb2 Author: Olaf Meeuwissen Date: 2021-02-07 06:23:25 +0000 Merge branch '248-generated-source-tar-balls-cant-be-built' into 'release/1.0.32' Resolve "Generated source tar balls can't be built" See merge request sane-project/backends!584 commit e86771c21bb29cc7a082c96f935e2c77c45ad22d Author: Olaf Meeuwissen Date: 2021-02-07 15:02:38 +0900 Fix ignore pattern when comparing sane-desc outputs in testsuite The old versions used plain `git describe` output leading to versions like 1.0.32-265-g5ff049. The `git-version-gen` scripts replaces the first dash, `-`, with a period, `.`, for the benefit of other version comparison tools and strips the `g` to save a byte. So that leads to versions like 1.0.32.265-5ff049. commit bb463d642a464e11610abcfc1aa9f68b34ecf178 Author: Olaf Meeuwissen Date: 2021-02-07 14:49:52 +0900 Make it easy to update the git-version-gen script commit c28c9843ed12f0fe2697d66c3dad724f68d0e576 Author: Olaf Meeuwissen Date: 2021-02-07 14:46:47 +0900 Use git-version-gen to inject a version This fixes builds from tarballs without a .git directory. Fixes #248 commit 3ca641c0185124d471e4186b77c1411d6aba280c Author: Olaf Meeuwissen Date: 2021-02-07 14:44:50 +0900 autogen.sh: Fix out-of-tree invocation commit 5ff049699846dc59424aaacb76b7eafee0bddf5d Author: Olaf Meeuwissen Date: 2021-01-31 19:44:18 +0900 pixma: Downcase USB product ID commit 5ae713180a335306398e2e0db0be2d26dd10778b Merge: 1229b5a3d1d5 7acfdead7038 Author: Olaf Meeuwissen Date: 2021-01-31 08:20:48 +0000 Merge branch '122-autogen-sh-script-complains-about-obsolete-macros' into 'release/1.0.32' Resolve "autogen.sh script complains about obsolete macro" See merge request sane-project/backends!582 commit 7acfdead7038ee8f1482f11676dac1e15f1935bb Author: Olaf Meeuwissen Date: 2021-01-31 16:51:01 +0900 Collect patches in one place and distribute them commit 129b53bbca254914b6b7d716bd63b30d0f3e2f70 Author: Olaf Meeuwissen Date: 2021-01-31 16:44:49 +0900 Suppress obsolete macro warnings. Fixes #122 commit 1229b5a3d1d5daf626b61da94010505f158db167 Merge: b90ecabf8c62 7946a14cfbae Author: Ralph Little Date: 2021-01-24 18:36:03 +0000 Merge branch 'ukGB_translation' into 'release/1.0.32' Added en_GB translations. See merge request sane-project/backends!578 commit 7946a14cfbae0303562663f5acd756eef7d0c2fb Author: Ralph Little Date: 2021-01-24 10:04:47 -0800 po: Added en_GB translations. Note: also fixed a minor grammar point in the pixma backend. commit b90ecabf8c62e92bec8284559dee2bf0dd11158b Merge: 63d938de61c3 a0570828010e Author: Olaf Meeuwissen Date: 2021-01-24 11:42:36 +0000 Merge branch 'update_uk1' into 'master' Update Ukrainian translation See merge request sane-project/backends!576 commit a0570828010e061fda2bae0fab4a569ead47fee3 Author: Yuri Chornoivan Date: 2021-01-18 13:36:37 +0200 Update Ukrainian translation commit 63d938de61c3be99dbc9474f48740ccbb143ea80 Merge: 420e09214a58 1042a61c30e5 Author: Olaf Meeuwissen Date: 2021-01-24 08:24:47 +0000 Merge branch 'fix-udev-rules' into 'master' Trigger udev rules parsing on all kernel events except of "remove". See merge request sane-project/backends!541 commit 1042a61c30e5ed3131f134290dea3820a70a3185 Author: Marcin Kocur Date: 2020-10-28 18:09:33 +0000 Trigger udev rules parsing on all kernel events except of "remove" - fix tests - udev.ref commit 968114e7e408cf5d261ec7004e1e8a75142f193c Author: Marcin Kocur Date: 2020-10-28 17:52:59 +0000 Trigger udev rules parsing on all kernel events except of "remove" - fix test udev+hwdb commit b5bbcb2ef7c2928b60371c559fda18dd908df458 Author: Marcin Kocur Date: 2020-10-28 17:29:19 +0000 Trigger udev rules parsing on all kernel events except of "remove" - fix tests. commit a9c181a211870ca8c129370156be813be84cf556 Author: Marcin Kocur Date: 2020-10-26 21:15:29 +0000 Trigger udev rules parsing on all kernel events except of "remove". commit 420e09214a5833f0040881d3e2036ff03cea1f5b Merge: c3a29fb9843c 6f9044264179 Author: Ordissimo Date: 2021-01-24 07:17:36 +0000 Merge branch 'escl-fix-sleep-mode' into 'master' Fix sleep mode See merge request sane-project/backends!577 commit 6f9044264179dc8f2503cd4e73a1f7eff82543c8 Author: Ordissimo Date: 2021-01-24 07:17:36 +0000 Fix sleep mode commit c3a29fb9843ccbf479b8127fff4ce8a1c568cf20 Merge: fc74de7c5ec6 7ddbefd103df Author: Olaf Meeuwissen Date: 2021-01-24 06:24:53 +0000 Merge branch 'readme1' into 'master' Add Ubuntu pre-built section to README See merge request sane-project/backends!518 commit 7ddbefd103dfd9d724065d5b7aa951a80a554c65 Author: Gábor Lipták Date: 2020-09-07 09:07:30 -0400 Add Ubuntu pre-built section to README Signed-off-by: Gábor Lipták commit fc74de7c5ec6043542b028e6a0f39ec8c01358e3 Merge: ece6d7d5e721 9dd8319a0192 Author: Ordissimo Date: 2021-01-17 16:52:58 +0000 Merge branch 'escl-complete-hack-scanjob-request' into 'master' Use MakeAndModel to apply the hack to the device See merge request sane-project/backends!575 commit 9dd8319a01923feabfbbe0cb97965fce19f2b75f Author: Thierry HUCHARD Date: 2021-01-17 17:23:56 +0100 Utilise MakeAndModel pour le device au hack. commit ece6d7d5e7212e8d8d59175be9c1e49a5f324c3c Merge: 7b602c1d6b12 af445c2e9c78 Author: Ordissimo Date: 2021-01-16 22:27:14 +0000 Merge branch 'escl-hack-scanjob-request' into 'master' Escl hack scanjob request See merge request sane-project/backends!574 commit af445c2e9c78adbfe33551f5262b4ddcd24981c2 Author: Ordissimo Date: 2021-01-16 22:11:36 +0000 Fix syntax in escl.conf.in commit 4b6418476e1aa675af0e55fa25a2127a1f5d8aa7 Author: Thierry HUCHARD Date: 2021-01-16 23:01:31 +0100 Allows to activate the hack from the configuration file, only the first form supports it. commit cb8b6d2ace5316ad6379018358e788590f3fef4f Author: Thierry HUCHARD Date: 2021-01-16 09:06:01 +0100 Fixed variable initialization. commit b24907c8a9f8bdfecaeb33175653a49f7a0f650a Author: Thierry HUCHARD Date: 2021-01-16 09:03:14 +0100 Fix scanjob for LaserJet FlowMFP M578 and LaserJet MFP M630. commit 7b602c1d6b12ab48ce0dea5095f2359fa051c0f2 Merge: c26e9acd79ee 957a4fcb8f1a Author: Olaf Meeuwissen Date: 2021-01-16 06:30:56 +0000 Merge branch 'sync-utsushi-desc-with-upstream' into 'master' utsushi.desc: Sync with upstream See merge request sane-project/backends!573 commit 957a4fcb8f1a374c54c8061f2fe5b74807328431 Author: Olaf Meeuwissen Date: 2021-01-16 15:14:22 +0900 utsushi.desc: Sync with upstream commit c26e9acd79ee7072c9803ced5107eb5e3424e6cc Merge: d46fbd9d794c 891ccac3b62b Author: Ordissimo Date: 2021-01-15 15:16:45 +0000 Merge branch 'escl-fix-alloc' into 'master' escl: fix char_to_array memory allocation calculations Closes #425 See merge request sane-project/backends!571 commit 891ccac3b62b2677623696b8dbdf03a27611bf1b Author: Timo Teräs Date: 2021-01-15 16:45:12 +0200 escl: fix char_to_array memory allocation calculations Too little memory allocated due to incorrect calculation. Fixes #425 commit d46fbd9d794cd7acefba2f491582319a6c4750cd Merge: b0655dc03e67 0293e662cff9 Author: Ordissimo Date: 2021-01-14 12:46:22 +0000 Merge branch 'escl-check-poppler-glib' into 'master' Fix check poppler-glib, (see issue #422). See merge request sane-project/backends!570 commit 0293e662cff93fcd6da8c0ce41f77a47a4f4a0f1 Author: thierry1970 Date: 2021-01-14 13:20:17 +0100 Fix check poppler-glib, (see issue #422). commit b0655dc03e67de902bf98850b4460b188503c186 Author: Gerhard Jaeger Date: 2021-01-07 12:34:34 +0100 Fix issue with '--enable-locking': Do not require sub-directory for locking. commit f0f7e43d7fb06ee4e1587ec3b84852bd4dcce957 Merge: adbfcd16960c fefc6f1818a2 Author: Ordissimo Date: 2021-01-04 19:07:25 +0000 Merge branch 'escl-fix-orders' into 'master' The order of the parameters does not change the result. See merge request sane-project/backends!568 commit fefc6f1818a286a23217420a74c29ad2da825496 Author: Thierry HUCHARD Date: 2021-01-04 19:47:08 +0100 The order of the parameters does not change the result. commit adbfcd16960ca535c6426005f632076d2feddcdb Merge: 2e41482e0142 17023afb99fb Author: Ordissimo Date: 2021-01-04 18:40:43 +0000 Merge branch 'escl-devices-list-properties' into 'master' Adds to the list of detected devices the different supported trays See merge request sane-project/backends!564 commit 17023afb99fbbcbff73b7e534c1ee3ea147331f0 Author: Thierry HUCHARD Date: 2020-12-29 11:33:55 +0100 Get device informations with avahi. commit 2e41482e0142b63ce998ab9401f2ce826eab5d3c Merge: 0c2fa6bea6ef 445b5bd7e192 Author: Olaf Meeuwissen Date: 2021-01-01 07:19:07 +0000 Merge branch '409-autoconf-2.70-causes-resulting-configure-file-to-fail' into 'master' Drop use of obsoleted autoconf AC_HEADER_STDC macro. Fixes #409 Closes #409 See merge request sane-project/backends!566 commit 445b5bd7e19245c08de6a0e669dd9211d2597bfa Author: Olaf Meeuwissen Date: 2021-01-01 15:53:11 +0900 Drop use of obsoleted autoconf AC_HEADER_STDC macro. Fixes #409 Use of the STDC_HEADER pre-processor macro that the autoconf macro used to define has been removed. Conditionalized code is now used unconditionally. This should be fine as the macro checks for ANSI C, i.e. C89 (ISO C90), compliant headers. We already require C99. commit 0c2fa6bea6ef07c5e2909b5c404db37361b15e4a Merge: 422425d740ea b4be10c5a2d8 Author: Povilas Kanapickas Date: 2020-12-26 22:19:45 +0000 Merge branch 'fix_crash' into 'master' Fix crash in scanimage when must_buffer is true Closes #408 See merge request sane-project/backends!563 commit b4be10c5a2d8fe4105ee476b40cf636a24d79444 Author: James Ring Date: 2020-12-26 13:55:54 -0800 Fix crash in scanimage when must_buffer is true Fixes https://gitlab.com/sane-project/backends/-/issues/408. commit 422425d740ea1abfc3cec7a8bb1c53c7623ddc62 Merge: 5af7850ba6b6 ea50fb683c1b Author: Povilas Kanapickas Date: 2020-12-26 17:26:42 +0000 Merge branch 'port' into 'master' allow saned to listen on specified port, or an ephemeral port See merge request sane-project/backends!549 commit ea50fb683c1b8bbf904d51cad6766b1dfae84ae9 Author: James Ring Date: 2020-12-05 09:16:18 -0800 allow saned to listen on specified port, or an ephemeral port commit 5af7850ba6b6970a8330c15ecface09e8ba81190 Merge: ee7f913974ef e9d2cdf67bb4 Author: Povilas Kanapickas Date: 2020-12-26 14:16:19 +0000 Merge branch 'sanei-usb-configure-record-open-clear' into 'master' sanei_usb: Allow backends to specify when USB recording is cleared See merge request sane-project/backends!392 commit e9d2cdf67bb4dd8b7cd6b5df06d6e724d708d0a9 Author: Povilas Kanapickas Date: 2020-04-09 00:47:41 +0300 sanei_usb: Allow backends to modify when USB recording is cleared commit ee7f913974efb0e08fc1456f0096ff49e3b155b4 Merge: 9c00d111e678 ffc2bbb1ffd6 Author: Povilas Kanapickas Date: 2020-12-26 13:56:49 +0000 Merge branch 'pixma-add-models' into 'master' Add models See merge request sane-project/backends!553 commit ffc2bbb1ffd617f2f50e6f5d4eba805c289d24a3 Author: Thierry HUCHARD Date: 2020-12-15 07:21:21 +0100 pixma: Add models commit 9c00d111e678da3eba892264605174f59614a68d Merge: e0be6fc89201 aa228faa1370 Author: Povilas Kanapickas Date: 2020-12-26 13:41:52 +0000 Merge branch 'fix_leak' into 'master' fix small memory leak in scanimage option processing See merge request sane-project/backends!551 commit aa228faa137072ebd69913efbe1dfc3e7706e7fe Author: James Ring Date: 2020-12-07 14:55:32 -0800 fix small memory leak in scanimage option processing commit e0be6fc892013b849ed69e42c3ac7daaa72e15ef Merge: 1ae3197f004f 4d347512ebdc Author: Olaf Meeuwissen Date: 2020-12-26 05:17:54 +0000 Merge branch '395-add-epson-et-2600-support' into 'master' Resolve "Epson ET-2600 works with libusb configuration" Closes #395 See merge request sane-project/backends!562 commit 4d347512ebdca44f986753ee6331e0ded9e08647 Author: Olaf Meeuwissen Date: 2020-12-26 13:44:42 +0900 epson2.desc: Fix formatting error commit be8b84105771de6f43b7eae04e4faf89ec3695d8 Author: Olaf Meeuwissen Date: 2020-12-26 12:04:11 +0900 utsushi.desc: Sync with upstream commit 77a3173e9094a79fe743f1dff3b7397531b09c2d Author: Olaf Meeuwissen Date: 2020-12-26 12:03:15 +0900 epson2: Add ET-2600 as supported See https://gitlab.com/sane-project/backends/-/issues/395 commit 1ae3197f004f7cb4d3d095452f25892455fe219f Merge: 87d9a86742ff 1dbc31632709 Author: Ordissimo Date: 2020-12-22 15:58:19 +0000 Merge branch 'escl-fix-messages-and-variable' into 'master' Fix messages and variable. See merge request sane-project/backends!561 commit 1dbc316327093508a357ed72388732a7e816118e Author: thierry1970 Date: 2020-12-22 16:14:44 +0100 Fix messages and variable. commit 87d9a86742ff0efc6c7c43a8290dcd4d64980bbf Merge: ae06e017f442 4aa767327d72 Author: Ordissimo Date: 2020-12-21 20:54:27 +0000 Merge branch 'escl-fix-documentformat' into 'master' Choose the format according to availability and options. See merge request sane-project/backends!560 commit 4aa767327d72d3df92f968346b47dfeb46a9e157 Author: Ordissimo Date: 2020-12-21 20:17:03 +0000 Choose the format according to availability and options. commit ae06e017f4423b02fd7e85e22660e2c7447c847e Merge: c8e1c9595ee5 c0dae7e8f5a8 Author: Ordissimo Date: 2020-12-21 19:42:45 +0000 Merge branch 'remotes/origin/escl-fix-adv-options' into 'master' refactoring options code See merge request sane-project/backends!559 commit c0dae7e8f5a8466855dcdf9d6d25bc7a8edac776 Author: thierry1970 Date: 2020-12-21 18:16:31 +0100 refactoring options code commit c8e1c9595ee5489766d7e411ee1fe6b13c528634 Merge: 6652aa42a8bd e111c485627b Author: Ralph Little Date: 2020-12-20 03:35:57 +0000 Merge branch '402-hp-scanjet-5370c-produces-double-height-image-and-cannot-preview' into 'master' Resolve "HP Scanjet 5370C produces double height image and cannot preview" Closes #402 See merge request sane-project/backends!558 commit e111c485627b540ec14609f48cf3123ff7290543 Author: Ralph Little Date: 2020-12-19 17:28:36 -0800 avision: added increment of the line variable in non-interpolated case commit 15e200a981bf2605e445ec642a070dbd5a7289de Author: Ralph Little Date: 2020-12-19 17:27:23 -0800 avision: added some brackets to silence warnings about precedence ambiguity commit 6652aa42a8bd3ca676d638433099c0e5da6a335e Merge: a03e0106a119 a5181be71bee Author: Ordissimo Date: 2020-12-19 21:22:20 +0000 Merge branch 'escl-fix-segfault' into 'master' Fix a crash when asking for the list of options: scanimage -A See merge request sane-project/backends!557 commit a5181be71bee8b0433f7b9916a2801d5eee0f227 Author: Thierry HUCHARD Date: 2020-12-19 22:04:30 +0100 Fix a crash when asking for the list of options: scanimage -A commit a03e0106a119008410786b75174ad8e6e907fd00 Merge: fc1c5667da48 3ad2f011ca9c Author: Ordissimo Date: 2020-12-18 08:16:26 +0000 Merge branch 'update-external-model-scangearmp2' into 'master' Add model version scangearmp2 4.0 and 4.10. See merge request sane-project/backends!556 commit 3ad2f011ca9c8162c4c7742d2fbefca17c92ceb5 Author: thierry1970 Date: 2020-12-18 08:48:27 +0100 Add model version scangearmp2 4.0 and 4.10. commit fc1c5667da48cbc8024f2613a3b343c3335bcc34 Merge: bb84652c3c34 6ad635234281 Author: Olaf Meeuwissen Date: 2020-12-17 10:57:02 +0000 Merge branch 'escl-device-crash-without-flatbed' into 'master' Escl device crash without flatbed See merge request sane-project/backends!554 commit 6ad6352342812305e575fba632f8b83c0cb9c7d1 Author: thierry1970 Date: 2020-12-16 13:50:24 +0100 Add model HP Deskjet 3760. commit 841680f6108abaa1ebb032137fa5a4850011a8a3 Author: thierry1970 Date: 2020-12-16 13:41:31 +0100 Fix : Fixes the crash of devices that do not have a tray, discovered on the HP Deskjet 3760 device. commit bb84652c3c345bd8bbd724e1b057392688281b9b Merge: f1154375f59c a388c3abc158 Author: Povilas Kanapickas Date: 2020-12-07 13:21:30 +0000 Merge branch 'origin/pixma_bug_fixes' into 'master' General pixma backend fixes See merge request sane-project/backends!548 commit a388c3abc1584ca80eed435a3063839c4c3b4752 Author: Ralph Little Date: 2020-11-28 19:34:52 -0800 pixma: avoid structure copy on every read by using cinfo address. commit 410f954f3b962368d218ac8ad154c97049089797 Author: Ralph Little Date: 2020-11-28 19:29:06 -0800 pixma: assignment must be inside the loop to be effective. commit f1154375f59c12b4a0286e6b847289082255b722 Merge: 7f7a4778bee3 cbf9627fd8d2 Author: Olaf Meeuwissen Date: 2020-11-30 11:07:40 +0000 Merge branch 'master' into 'master' drop CVS keywords See merge request sane-project/backends!547 commit cbf9627fd8d20b7b35ee8376ad8cd36f8aa8bb51 Author: Mikolaj Kucharski Date: 2020-11-29 09:19:26 +0000 final Revision CVS keyword removal commit 4196d204f108be532ab9ad10d1624f8aaf3d46d6 Author: Mikolaj Kucharski Date: 2020-11-29 09:17:33 +0000 drop more tricky Revision CVS keyword commit 7de7c051fa5afbf6765bac91b8df2a5fd2f0aec3 Author: Mikolaj Kucharski Date: 2020-11-29 09:06:48 +0000 more CVS keywords removed, Log and Revision commit 65f11315414d44417ae453e940387cd3b869deb0 Author: Mikolaj Kucharski Date: 2020-11-28 10:01:56 +0000 drop CVS keywords commit 7f7a4778bee37c88510d8f6b6af4297a54aceeb2 Author: m. allan noah Date: 2020-11-28 18:48:50 -0500 canon_dr backend v60 - add new gray and color interlacing options for DR-C120 - initial support for DR-C120 and C130 - enable fine calibration for P-208 (per @sashacmc in !546) commit 08eaf1a22e6441ecc446d0a5b34f21c08709a770 Author: Ralph Little Date: 2020-11-28 19:19:26 -0800 pixma: MX490 series returns JPEG from ADF scans. commit e5bd31c88284881f156bbca3d42384711e89a34d Author: Olaf Meeuwissen Date: 2020-11-25 20:05:28 +0900 utsushi.desc: Sync with upstream commit 8a8595808de7b43aabe86a4b1d5c514ad8853ef7 Merge: a7f42db900a0 862ca098ae38 Author: Olaf Meeuwissen Date: 2020-11-23 01:39:29 +0000 Merge branch 'master' into 'master' Fixed Artec AstraSlim SE configuration. See merge request sane-project/backends!545 commit 862ca098ae38c31f4a1489203eadc66424bbdb22 Author: RICCIARDI-Adrien Date: 2020-11-21 11:58:17 +0100 Fixed Artec AstraSlim SE configuration. commit a7f42db900a02955ac030408e460d7b7b79909c0 Merge: eae1e9068af0 c7dd86c7e758 Author: Ordissimo Date: 2020-11-08 19:31:12 +0000 Merge branch 'add-new-modele' into 'master' Add model Canon TS-5351 See merge request sane-project/backends!544 commit c7dd86c7e75853bbd7d3bd6622747795e01d1651 Author: Thierry HUCHARD Date: 2020-11-08 20:16:05 +0100 Add model Canon TS-5351 commit eae1e9068af08f3db20bf56ff38ea53d191c7a6d Author: Thierry HUCHARD Date: 2020-11-08 20:11:50 +0100 Revert "Add model Canon wqTS-5351." This reverts commit 9ec62a4780dd5db444a573c055d2558739e63881. commit 9ec62a4780dd5db444a573c055d2558739e63881 Author: Thierry HUCHARD Date: 2020-11-08 20:02:55 +0100 Add model Canon wqTS-5351. commit 7699e1a09873df90185ca65f162812f987a87395 Merge: e4ef0548bf86 d0695c147065 Author: Povilas Kanapickas Date: 2020-11-02 22:07:21 +0000 Merge branch 'pixma-calibrate' into 'master' pixma: Add --calibrate option See merge request sane-project/backends!542 commit d0695c14706575d765c770cfbea63372ff85bd45 Author: Iskren Chernev Date: 2020-11-02 21:44:08 +0200 pixma: Add --calibrate option Feature was discussed in https://gitlab.com/sane-project/backends/-/issues/321 commit e4ef0548bf86a0690c2b373d3ece5aae52300036 Merge: 016271ebdcf5 6fe222ed2924 Author: Olaf Meeuwissen Date: 2020-10-20 09:01:15 +0000 Merge branch 'leak_fix' into 'master' Fix memory leaks in dll and test backends See merge request sane-project/backends!537 commit 6fe222ed29240e039ccaf462eeaa20a919ae9da9 Author: Fletcher Woodruff Date: 2020-10-12 11:20:49 -0600 test: do not leak initial values of string options The default values for string options are set using static (i.e. non-malloced) strings. Later, if new values are loaded from the config file, those SANE_Strings will then point to dynamically allocated memory which is eventually leaked. Change the initial values for the string options to NULL, and initialize them to the proper values within sane_init() using strdup(). This way, whenever the value for the string is changed, we can safely free() the previous value. This eliminates the other main source of memory leaks in the test backend. This patch also updates sane_init for the test backend to properly cleanup memory if it fails. commit f71ac6126f7834c400fc9ced96325d1a61fc94d9 Author: Fletcher Woodruff Date: 2020-10-12 09:14:35 -0600 test: free memory used for string options Inside Test_Device, track whether we've already called init_options(). If we have, don't call it again when calling sane_open() again. Add a function cleanup_options() which frees the memory used for these options, and call it in sane_exit() if we previously called init_options(). Change Test_Devices to be initialized with calloc instead of malloc, so that we can assume that uninitialized fields are NULL, and can safely call free() on them unconditionally. This eliminates some larger memory leaks within the test backend. commit de903b84c49f80a973f48ea47e46e5e47a186b7e Author: Fletcher Woodruff Date: 2020-10-12 16:22:40 -0600 dll: fix memory leak in load In the case where we are unable to get a path for loading libs, and instead just use LIBDIR, we set 'src = strdup(LIBDIR)'. However, we never update orig_src to point to src before tokenizing the path with strsep(), so the memory is never freed. Update load to always set orig_src to src so that we don't leak memory. commit 016271ebdcf52351051ddd41fe71841aa03203b2 Merge: 519c300ff064 bc64c8f5492b Author: Ralph Little Date: 2020-10-18 17:36:24 +0000 Merge branch '362-lexmark-mc3200-mc3224adwe-network-scanner-crash-due-to-null-constraints' into 'master' Resolve "Lexmark MC3200 / MC3224adwe Network scanner crash due to NULL constraints" Closes #362 See merge request sane-project/backends!534 commit bc64c8f5492bd9ada7800a89a8fdaaf5777ed7e3 Author: Ralph Little Date: 2020-10-18 09:51:35 -0700 scanimage: check also for negative word_list length which is nonsensical. commit 968add6706b88218e2cb91a9a26ef6928868b793 Author: Ralph Little Date: 2020-10-06 20:16:13 -0700 scanimage: added checks for null constraints. We have seen this from buggy backends that present NULL for constraint string_lists. This is probably illegal behaviour from backends but we don't want to crash. commit 519c300ff0644f49d1a0a520aa4272aa9a36b685 Merge: 730982bd6b00 b1b412d5f82e Author: Olaf Meeuwissen Date: 2020-10-18 01:47:29 +0000 Merge branch 'brightness_on_12000xl' into 'master' epson2: fix lower brightness limit for DS-G20000/12000XL See merge request sane-project/backends!529 commit b1b412d5f82e682119517c52addc37a36de7cf0a Author: Wolfram Sang Date: 2020-09-18 20:37:40 +0200 epson2: fix lower brightness limit for DS-G20000/12000XL I get -EINVAL with -4 (ROM Version 1.02). -3 works. Signed-off-by: Wolfram Sang commit 730982bd6b00176c52a88a467543775dce6b1b6d Merge: 7f38f0f71c77 014c307e024c Author: Povilas Kanapickas Date: 2020-10-15 21:20:20 +0000 Merge branch '364-pixma-mx320-cannot-do-1200dpi-in-adf' into 'master' Resolve "Pixma MX320 cannot do 1200dpi in ADF" Closes #364 See merge request sane-project/backends!538 commit 014c307e024ce85fe1a5509109ed97f88fbac176 Author: Ralph Little Date: 2020-10-13 09:03:45 -0700 pixma: MX320 machine will not perform 1200 dpi scan from the ADF 600 dpi maximum from ADF. commit 7f38f0f71c7779281bc26dde40bab0456e60f43d Merge: 7e6231fe5714 6af85ec669f7 Author: Gerhard Jäger Date: 2020-10-12 09:14:04 +0000 Merge branch 'canoscan_n650u_plustek_usb_workaround' into 'master' [plustek] Add 1ms delay before writing to register 0x59 during reseting registers Closes #137 See merge request sane-project/backends!535 commit 6af85ec669f7a201360f3dcc48f6b47a06812914 Author: Zdenek Dohnal Date: 2020-10-09 08:55:41 +0200 plustek-usbhw.c: Add 1ms sleep before writing to register 0x59 Works around discovery problem for CanoScan N650U, fixes #137. commit 7e6231fe571429d0b23654f24eac2333349381be Merge: b75e39af8cad 42520aeaa9ea Author: Ralph Little Date: 2020-10-10 19:22:41 +0000 Merge branch '358-gt68xx-1-0-31-breaks-mustek-1248ub-after-first-scan' into 'master' Resolve "gt68xx: 1.0.31 breaks Mustek 1248UB after first scan" Closes #358 See merge request sane-project/backends!533 commit 42520aeaa9ea9f4e55b4025d6f04c7497bfa5852 Author: Ralph Little Date: 2020-10-10 12:01:33 -0700 gt68xx: added flag to fix stop scan bug Some scanners (notably the Mustek 1248UB) doesn't like receiving a STOP scan command when it is not scanning. Flag prevents it my making sure that START/STOP commands are only ever paired up. commit 6886cf6e8015e5e7c452add4907d7a486ee8ccb1 Author: Ralph Little Date: 2020-10-03 10:38:50 -0700 gt68xx: Added temporary fix to avoid stop scan during cancel. Calling this function upsets some scanners in this family. This temporary sticking plaster permits the Mustek 1248UB scanner to scan after cancelling. We need to look into this much more when a scanner becomes available for investigation. commit b75e39af8cade19d3658e5fbdc5b09bba76af8fd Merge: 9ac6ee6cd625 8885312711ec Author: Ordissimo Date: 2020-10-10 16:51:52 +0000 Merge branch 'escl-avahi-resolve' into 'master' Fixes device discovery See merge request sane-project/backends!536 commit 8885312711ec7a228c5e60fde9b6447bc2569ba6 Author: Thierry HUCHARD Date: 2020-10-10 18:01:55 +0200 Fixes device discovery commit 9ac6ee6cd6252e7c4e63a21715497295c5684a55 Author: pimvantend Date: 2020-10-07 15:06:07 +0200 desc and news lide 600 commit 81227d1d408af53105cd26cecefd3963729b92d9 Merge: efd4767d13a8 5ef217b19dca Author: Olaf Meeuwissen Date: 2020-09-29 09:38:15 +0000 Merge branch 'avision_add_av186plus_av188_support' into 'master' add ID information for Avision AV186+ and AV188 sheetfed USB scanners See merge request sane-project/backends!532 commit 5ef217b19dcad89c3eb4587a0480e89008ca8961 Author: Nikolai Kostrigin Date: 2020-06-25 18:13:41 +0300 add ID information for Avision AV186+ and AV188 sheetfed USB scanners backend/avision.c: add AV186+ as 'good', AV188 as 'untested' doc/descriptions/avision.desc: add AV186+ as 'good', AV188 as 'untested' commit efd4767d13a8c49634187180662c3e0cacf5d5de Merge: 9e9dc5104ea2 7139d6decf08 Author: Olaf Meeuwissen Date: 2020-09-28 09:16:31 +0000 Merge branch 'epson2/rework_focus' into 'master' Epson2/rework focus See merge request sane-project/backends!531 commit 7139d6decf0860b868314d6bdae4bf4e493cf066 Author: Wolfram Sang Date: 2020-09-26 13:22:57 +0200 epson2: add autofocus Enable autofocus support on my Epson 12000XL. The middle of the scan area will be used as the focus point. An arbitrary X,Y is possible but not implemented yet Signed-off-by: Wolfram Sang commit 5d706c2c3573f21438c39020782cbdfa4923076d Author: Wolfram Sang Date: 2020-09-19 11:24:37 +0200 epson2: implement full range manual focus settings Allow the whole range of manual focus settings as an expert option. This is also now independent of a TPU being installed or not. The old default values are applied if the user changes the source type (either FLATBED or TPU). Adding a focus group here because autofocus options will come with later patches. Signed-off-by: Wolfram Sang commit f9f75ca068f66e3d9f8c442789b7582595b947b4 Author: Wolfram Sang Date: 2020-09-19 10:13:43 +0200 saneopts: add entries for focus/autofocus coolscan2 has focus entries already reused in coolscan3. Because I want to add the same options to epson2, it makes sense to make them available globally. Add i18n while here. Signed-off-by: Wolfram Sang commit a0e68f7de9679a0fe5f6ae61f9487d5fd9c8a86b Author: Wolfram Sang Date: 2020-09-26 11:32:49 +0200 epson2: enable focus for all scanner types Even with USB based scanners, we need to send the esci command to set the focus. There is no byte in the extended scanning parameters for it. So, move the code to a place where it is called for all cases. Signed-off-by: Wolfram Sang commit 1653b0f80c296477387101774ee3d0ea2c696cb8 Merge: 429cc0efc005 94506b17874d Author: Wolfram Sang Date: 2020-09-26 11:47:57 +0200 Merge branch 'zdohnal/esci_set_focus_retval' into focus_on_12000XL commit 9e9dc5104ea206e79d04c64a2b9eaca49cb1a172 Merge: 429cc0efc005 94506b17874d Author: Olaf Meeuwissen Date: 2020-09-27 08:01:36 +0000 Merge branch 'esci_set_focus_retval' into 'master' epson2: check for return value of e2_esc_cmd See merge request sane-project/backends!526 commit 94506b17874dfdbea0f02c6ecd0b58dcb6a71b84 Author: Zdenek Dohnal Date: 2020-09-22 11:34:56 +0200 epson2: check for return value of e2_esc_cmd commit 429cc0efc0055a8f231199260e0b52e18169be5f Author: Rolf Bensch Date: 2020-09-25 16:40:07 +0200 pixma: backend version 0.28.6 commit 9db8fc305e7b5547fe508096895fe94245373813 Author: Rolf Bensch Date: 2020-09-25 16:38:38 +0200 pixma: add comment for last merge commit e80612275eaa5ea61434e81b2dcb0e23330c8804 Merge: 72fd68cb56ee 4a76bbd16211 Author: Rolf Bensch Date: 2020-09-25 14:35:40 +0000 Merge branch 'patch-1' into 'master' Added support for Canon Pixma MX340 buttons and ADF status See merge request sane-project/backends!513 commit 4a76bbd162110f6a3944ebc7a6f08259724e4618 Author: Asela Fernando Date: 2020-08-20 00:32:49 +0000 Added support for Canon Pixma MX340 buttons buf[7] and ADF status buf[8] commit 72fd68cb56eeaf29dea6ea8af6656a9514fb25f0 Author: Rolf Bensch Date: 2020-09-25 16:20:58 +0200 introduce Povilas Kanapickas as pixma maintainer commit e46adab4f28b0f65df9fb8e4c0181512d0d0c710 Author: m. allan noah Date: 2020-08-11 19:28:11 -0400 canon_dr v59: fine calibration updates - restructure fine calibration code - initial support for uploading fine calibration payloads - improve DR-C225 support Most canon scanners require that the driver apply fine (per-cell) calibration information. But a few require that the info be loaded into the scanner. Here, we add initial support for that, though the needed values are hardcoded instead of calculated. commit 588f01526e7cc9459739d88fdf57961b021ffec7 Author: m. allan noah Date: 2020-09-23 21:03:45 -0400 fujitsu v137b: brightness/contrast improvements - change window_gamma init (fixes bright/contrast for iX1500) - only call send_lut after set_window (remove late_lut) commit 5b92a367c386d2a576c5aa4f4b95106252ce62a1 Author: m. allan noah Date: 2020-09-23 20:57:26 -0400 fujitsu v137a: fix JPEG duplex memory corruption commit 6d7fbc83200e4c0ae4fb5bdad89714e1f84604e6 Merge: b155955b0e24 cd768b3692e1 Author: Ordissimo Date: 2020-09-18 14:03:22 +0000 Merge branch 'escl-check-scan-options' into 'master' Check options scan and fix xml. See merge request sane-project/backends!528 commit cd768b3692e1fa18e97bb5565299ba2a2c83a354 Author: thierry1970 Date: 2020-09-18 15:47:45 +0200 Check options scan and fix xml. commit b155955b0e24ab5ff40f3ca9cdcad4b676f1ac4d Merge: ddcfa6a12fce e281dfb0cd5f Author: Ordissimo Date: 2020-09-17 20:29:28 +0000 Merge branch 'escl-add-scan-options' into 'master' Added options: Brightness, Threshold, Sharpen and Contrast. See merge request sane-project/backends!527 commit e281dfb0cd5fb15acc1ee80766d22c26ac0e6e53 Author: thierry1970 Date: 2020-09-17 18:55:03 +0200 Fix style. commit fcbeeccbf08b302e2944b5e912daaaf99bbbdc1a Author: thierry1970 Date: 2020-09-17 18:50:33 +0200 Added options: Brightness, Threshold, Sharpen and Contrast. commit ddcfa6a12fce64f41ba0f40746a6487207fdfb95 Merge: b89b9b135003 924dcf073176 Author: Olaf Meeuwissen Date: 2020-09-17 11:41:31 +0000 Merge branch 'master' into 'master' Do not use fixed python executable name and use AM_PROG_PYTHON See merge request sane-project/backends!525 commit 924dcf073176bc130750056f5e2b2943d287d382 Author: Your Name Date: 2020-09-14 15:44:36 +0100 Lower minimum python version required to 2.7 commit 0f7ce3fe7a22c2bf89caa7e7297d2af27e69f772 Author: Your Name Date: 2020-09-13 20:17:43 +0100 Do not use fixed python executable name and use AM_PROG_PYTHON On many systems now python executable it is python3. On my system it the case and by this muild failed with: make[2]: Leaving directory '/home/tkloczko/rpmbuild/BUILD/backends-1.0.31/backend' make[2]: Entering directory '/home/tkloczko/rpmbuild/BUILD/backends-1.0.31/backend' Generating pixma/pixma_sane_options.h from pixma/pixma.c /bin/sh: python: command not found make[2]: *** [Makefile:7317: pixma/pixma_sane_options.h] Error 127 make[2]: Leaving directory '/home/tkloczko/rpmbuild/BUILD/backends-1.0.31/backend' make[2]: *** Waiting for unfinished jobs.... Using AM_PROG_PYTHON() nmacro cllows avoide tha by hecks current version of the python and allw to use custom python executable by execute configure by: $ PYTHON= ./configure AM_PROG_PYTHON aclocal macro propagates to automake files $(PYTHON) variable which could be used on geberate instaleable scripts on duting build process like it is now in backend/Makefile.am. Currently minum version of required vesion of the python is python >= 3. commit b89b9b1350035a579757b2b11b34db112bc920ad Merge: 2c5f3e181449 a380fb089499 Author: Olaf Meeuwissen Date: 2020-09-13 10:38:24 +0000 Merge branch '345-fix-pixma-libxml-build-dependency' into 'master' pixma: Restore old behaviour in case XML support is missing Closes #345 See merge request sane-project/backends!524 commit a380fb089499f6b5192c07ae2feaa3376997382f Author: Olaf Meeuwissen Date: 2020-09-13 18:47:32 +0900 pixma: Restore old behaviour in case XML support is missing Fixes #345. commit 2c5f3e181449451daa4999e8eb5bc60cb2d134d5 Merge: 3ac8ca4e87aa 4846d36ad1e9 Author: Olaf Meeuwissen Date: 2020-09-13 09:12:58 +0000 Merge branch '344-generate-sane-backends-pot-inputs' into 'master' po: Make sure all sane-backends.pot input are available Closes #344 See merge request sane-project/backends!523 commit 4846d36ad1e9ca709cb3a2eaa839c8c79effe34b Author: Olaf Meeuwissen Date: 2020-09-13 17:56:40 +0900 po: Make sure all sane-backends.pot input are available commit 3ac8ca4e87aabcc432fe629d905bcfdee4c0a199 Merge: e69284695a75 1666e3916db2 Author: Olaf Meeuwissen Date: 2020-09-13 08:44:21 +0000 Merge branch 'epson_perfection_1640' into 'master' Add SCSI ids for Epson Perfection 1640SU See merge request sane-project/backends!509 commit 1666e3916db23aa1a8f82c32e9156d8ca994d783 Author: StefanBruens Date: 2020-08-18 19:13:19 +0000 Add SCSI ids for Epson Perfection 1640SU See: https://sane-devel.alioth.debian.narkive.com/fNt0Hkzn/epson-perfection-1640su-with-adf-or-filmadapter commit e69284695a75b124533b411fdffd6f8dd8727b24 Merge: 2539a13dfbdc 0077988e83f1 Author: Olaf Meeuwissen Date: 2020-09-13 08:38:26 +0000 Merge branch '350-remove-automake-maintainer-mode' into 'master' Resolve "1.0.31: build fails" Closes #350 See merge request sane-project/backends!522 commit 0077988e83f1ba3704e5d8fc296e030563b2dd91 Author: Olaf Meeuwissen Date: 2020-09-13 16:24:46 +0900 po/README: Make the documentation use consistent notation commit e22b393e38afb4db00adb73466ad87eda1631fb1 Author: Olaf Meeuwissen Date: 2020-09-13 16:24:17 +0900 po/README: Update documentation that refers to maintainer mode commit 262e32ce591feb79ba5be9b21396b9dd71f1f067 Author: Olaf Meeuwissen Date: 2020-09-13 16:21:03 +0900 configure.ac: Remove maintainer mode Now that we no longer include generated files in our git repository builds should always have maintainer mode enabled so checkouts pick up on changes in Makefile.am, configure.ac, etc. between builds. See #350. commit 30b7e31eacd14675c373a5d44833f606c46ca894 Author: Olaf Meeuwissen Date: 2020-09-13 15:56:04 +0900 configure.ac: Remove stale release versioning comment commit 2539a13dfbdc71c6c8becd7973c22c5da26b6354 Author: Olaf Meeuwissen Date: 2020-09-13 16:31:41 +0900 Fix spelling differences in testsuite reference data Fix up of ffff088641e3dcd5d3a3b92d533c2eb0a02a6219. commit 404e1500c2851055f0544fef5b5ac2cd52961d45 Merge: 5d3ce7806858 b5cf71318b7c Author: Olaf Meeuwissen Date: 2020-09-13 06:07:39 +0000 Merge branch 'usb_udev_attrs' into 'master' Speedup ATTR match for USB devices Closes #341 See merge request sane-project/backends!510 commit b5cf71318b7c34635df41178a7f1666ffb035df9 Author: Olaf Meeuwissen Date: 2020-09-13 14:50:15 +0900 sane-desc: Update test reference files This follows ad66f79c43c1c1cd809ad6f77fb16ba94c71bea9. commit 124c16fe6f7731f6e4cf35d870f3aafe8bf15094 Author: Olaf Meeuwissen Date: 2020-09-13 14:34:21 +0900 sane-desc: Fail build if testsuite fails commit ad66f79c43c1c1cd809ad6f77fb16ba94c71bea9 Author: StefanBruens Date: 2020-08-18 19:23:09 +0000 Speedup ATTR match for USB devices Fixes #341 commit 5d3ce78068580e5e9fe0e2142caddb883312fecc Merge: 17ab6caa383c 2a238f83e0d1 Author: Ordissimo Date: 2020-09-11 08:17:35 +0000 Merge branch 'escl-add-models' into 'master' Added HP "DeskJet 2710" and "DesJet 2723" models as supported. See merge request sane-project/backends!519 commit 2a238f83e0d1bf6b7d6a7d70ee025689fc20163a Author: thierry1970 Date: 2020-09-11 09:58:09 +0200 Added HP "DeskJet 2710" and "DesJet 2723" models as supported. commit 17ab6caa383ceec41930ae95956b34197d183c5a Author: pimvantend Date: 2020-09-07 15:15:21 +0200 canon_lide70 simplified going back commit cea2a83ea2c006a6347e8926b6b9163d20e77874 Merge: f59ba7003d4f 33d1aecc7c5a Author: Olaf Meeuwissen Date: 2020-09-07 09:24:15 +0000 Merge branch 'master-spelling' into 'master' spelling fixes See merge request sane-project/backends!516 commit 33d1aecc7c5a3e0604c1a24903e9d22d10081eb1 Author: Peter Marschall Date: 2020-09-06 07:34:40 +0000 Apply 1 suggestion(s) to 1 file(s) commit 950aded610368dd4e87e96ba5cffd3dd2cb3164e Author: Peter Marschall Date: 2020-09-06 07:29:49 +0000 Apply 1 suggestion(s) to 1 file(s) commit 6e16f45b76d50530c66c1e2e6dfc295b50f9aaa3 Author: Peter Marschall Date: 2020-09-06 07:29:44 +0000 Apply 1 suggestion(s) to 1 file(s) commit 76ab18f064eb282a0708039b63ff3ebd9acfb086 Author: Peter Marschall Date: 2020-09-06 07:29:33 +0000 Apply 1 suggestion(s) to 1 file(s) commit 408fb55b026ba681ca992d02d20db83694f21af8 Author: Peter Marschall Date: 2020-09-06 07:20:32 +0000 Apply 1 suggestion(s) to 1 file(s) commit d78954552157675237ff0f5904830ade388c0a6d Author: Peter Marschall Date: 2020-09-06 07:17:00 +0000 Apply 1 suggestion(s) to 1 file(s) commit c0ab7e950e15aae16f05b3003b9d401b9239136e Author: Peter Marschall Date: 2020-09-06 07:16:41 +0000 Apply 1 suggestion(s) to 1 file(s) commit 2ad63d4e1ee942a0289b4986fd015ae426868a44 Author: Peter Marschall Date: 2020-08-29 08:47:52 +0200 spelling fixes for sane core commit 7cbcc9ceac565e44e2990ba1de78c26b1a85721c Author: Peter Marschall Date: 2020-08-29 08:46:45 +0200 spelling fixes for docs, READMEs, ... commit b4c6cbeb1ff9a12b25ac0c047c17570c5d40babf Author: Peter Marschall Date: 2020-08-29 08:44:47 +0200 spelling fixes for tools commit ffff088641e3dcd5d3a3b92d533c2eb0a02a6219 Author: Peter Marschall Date: 2020-08-29 08:44:33 +0200 spelling fixes for testsuite commit cd8c018585c36391e58ebc8409facf033f57c645 Author: Peter Marschall Date: 2020-08-29 08:43:03 +0200 spelling fixes for xerox_mfp backend commit 77406ed606607f509cac9fcfb6ece998505cd191 Author: Peter Marschall Date: 2020-08-29 08:43:03 +0200 spelling fixes for sceptre backend commit 44755f6fc59a72b871ffc34c36081e097fef1972 Author: Peter Marschall Date: 2020-08-29 08:42:15 +0200 spelling fixes for dmc backend commit 5515db02724e08f8fbe4afcfe2619f3d91cfe85a Author: Peter Marschall Date: 2020-08-29 08:42:15 +0200 spelling fixes for leo backend commit 5137304bb62313a5202ba2d68abed54f19fdf498 Author: Peter Marschall Date: 2020-08-29 08:42:15 +0200 spelling fixes for ma1509 backend commit f8d08816a83067a63ed816930f512b7ca65f7a98 Author: Peter Marschall Date: 2020-08-29 08:42:15 +0200 spelling fixes for sp15c backend commit 25af0a7403f2451b0668c610275ca1332f2efc82 Author: Peter Marschall Date: 2020-08-29 08:42:15 +0200 spelling fixes for tamarack backend commit 66443f37e6c14b73b251e2080d7d97ff6a746b5b Author: Peter Marschall Date: 2020-08-29 08:40:56 +0200 spelling fixes for ibm backend commit f6059bd3cbe8fa2c613e884839a4b0e5f037d590 Author: Peter Marschall Date: 2020-08-29 08:40:56 +0200 spelling fixes for gphoto2 backend commit a36cf4afda01c2d871aa8a01d7078a5d8bfc4d9c Author: Peter Marschall Date: 2020-08-29 08:40:56 +0200 spelling fixes for dell1600n backend commit 9d368fd7a884103362fa73914795a21ae82e305d Author: Peter Marschall Date: 2020-08-29 08:40:56 +0200 spelling fixes for dc25 backend commit 37bee66d055b364530289410bd512b5e88454f6c Author: Peter Marschall Date: 2020-08-29 08:39:02 +0200 spelling fixes for s9036 backend commit 56cbbdad5d6f8fe01708785bbac88485fdbf8c1e Author: Peter Marschall Date: 2020-08-29 08:39:02 +0200 spelling fixes for sharp backend commit 4984bedb6e39ee24fd48d9f95cd0ccf02a282538 Author: Peter Marschall Date: 2020-08-29 08:39:02 +0200 spelling fixes for ricoh backend commit 35fe083519e49cfe87ce41d9384fa1e8b5766dbc Author: Peter Marschall Date: 2020-08-29 08:39:02 +0200 spelling fixes for qcam backend commit 95eda03dd12de41972407c4943f2f6e28f289a60 Author: Peter Marschall Date: 2020-08-29 08:39:02 +0200 spelling fixes for nec backend commit 340f7e7329a87af240b5de647375c13f84a8c60f Author: Peter Marschall Date: 2020-08-29 08:37:18 +0200 spelling fixes for niash backend commit 56be6cf59af13523ac9696121f119d477269b2f6 Author: Peter Marschall Date: 2020-08-29 08:36:19 +0200 spelling fixes for lexmark backend commit fc24607b3165a8df897a3d71455f1e47a19ebd65 Author: Peter Marschall Date: 2020-08-29 08:35:16 +0200 spelling fixes for agfafocus backend commit b834e472876f47639c87bda8ae9996b06f409754 Author: Peter Marschall Date: 2020-08-29 08:33:32 +0200 spelling fixes for kodak backend commit af9f9d62cd97830aac9b417f7af3c673c2dd0fcb Author: Peter Marschall Date: 2020-08-29 08:33:13 +0200 spelling fixes for kodakaio backend commit 269a7c0413c0b6e9b9a1703a414860587b6dfeab Author: Peter Marschall Date: 2020-08-29 08:32:00 +0200 spelling fixes for avision backend commit f611cab816eec5df32c26b110148b41aad95fdff Author: Peter Marschall Date: 2020-08-29 08:31:46 +0200 spelling fixes for v4l backend commit 9a6ef1ceb046367d5b48ea57bb9af27f9e51f91b Author: Peter Marschall Date: 2020-08-29 08:31:33 +0200 spelling fixes for hs2p backend commit e0bc8e9ba7aeb04e20f507c10b9ef64b2ece124c Author: Peter Marschall Date: 2020-08-29 08:29:25 +0200 spelling fixes for net backend commit 15d95a5b1b40b96e4df92c32b7cd27218ab9512d Author: Peter Marschall Date: 2020-08-29 08:26:33 +0200 spelling fixes for matsushita backend commit 452f21c1e2c460aee14d35e819e29f801d60aa43 Author: Peter Marschall Date: 2020-08-29 08:24:51 +0200 spelling fixes for microtek2 backend commit 16acc1c2335358883ffae78526237894e5c9db71 Author: Peter Marschall Date: 2020-08-29 08:23:03 +0200 spelling fixes for artec backend commit 917c8b2c5fcb5d4a12f35357bf5598dbb9adfb0d Author: Peter Marschall Date: 2020-08-29 08:22:53 +0200 spelling fixes for artec_eplus48u backend commit 7e984d479a8aacd7b838e01d9fae951bf84a38c5 Author: Peter Marschall Date: 2020-08-29 08:22:37 +0200 spelling fixes for apple backend commit ffb741852ad46017926b81bf24a466a56ef8f4f7 Author: Peter Marschall Date: 2020-08-29 08:20:08 +0200 spelling fixes for hp backend commit 7dcb5dc096e5eff45ffbd1f7ac55ed3d5a4c7ebe Author: Peter Marschall Date: 2020-08-29 08:19:45 +0200 spelling fixes for hp3500 backend commit f4fb21e33410c9d246a4028573efd95f26cd8ad3 Author: Peter Marschall Date: 2020-08-29 08:19:31 +0200 spelling fixes for hp3900 backend commit 32aa1ef82607ffd130a747dd4325624841981b2d Author: Peter Marschall Date: 2020-08-29 08:19:15 +0200 spelling fixes for hp5400 backend commit b9ca020ef60bd2553c9eab092c9277d0683ac269 Author: Peter Marschall Date: 2020-08-29 08:18:26 +0200 spelling fixes for hpljm1005 backend commit bbf7d068eda56ef7506f1c7d83903ce7af9cac34 Author: Peter Marschall Date: 2020-08-29 08:17:09 +0200 spelling fixes for hpsj5s backend commit 33f413e790ac203e1550440274915e03e2110c54 Author: Peter Marschall Date: 2020-08-28 19:10:36 +0200 spelling fixes for epson backend commit b2ce3110c6ebdcfbc228e32ed187a918ee0b9c99 Author: Peter Marschall Date: 2020-08-28 19:10:25 +0200 spelling fixes for epson2 backend commit 3d1a5da4e1363942b6d1a878b43711e964b53954 Author: Peter Marschall Date: 2020-08-28 19:08:59 +0200 spelling fixes for fujitsu backend commit 79a1aa27234ebaf7d44f23e02979d1e9eb7bfb06 Author: Peter Marschall Date: 2020-08-28 19:07:54 +0200 spelling fixes for stv680 backend commit d2f2d9965e974f1094cbb3c2b2c639dac4e63e41 Author: Peter Marschall Date: 2020-08-28 19:07:33 +0200 spelling fixes for st400 backend commit b7676f3ec1b89b73b0f66fd16ff2e0d54478265f Author: Peter Marschall Date: 2020-08-28 19:05:10 +0200 spelling fixes for canon backend commit fbe99c09149450389a6e2d0d282e83ee7dee1caa Author: Peter Marschall Date: 2020-08-28 19:04:55 +0200 spelling fixes for canon_dr backend commit 0e8b1d0e89fb5e0b40bd6651d4e8a7d662e23c15 Author: Peter Marschall Date: 2020-08-28 19:04:45 +0200 spelling fixes for canon_pp backend commit ec07e73d04cc98aae22a47fdaabbd2a003d6a473 Author: Peter Marschall Date: 2020-08-28 19:04:25 +0200 spelling fixes for canon630u backend commit e1ea09c8344a1a990f54a0d89bf3cbb1a3dc873a Author: Peter Marschall Date: 2020-08-28 19:01:50 +0200 spelling fixes for coolscan backend commit e78667e32d12bb01f98fe1459b090e15a2b57885 Author: Peter Marschall Date: 2020-08-28 19:01:44 +0200 spelling fixes for coolscan2 backend commit 27e5078c39cc86bcb7876221fcd9f744da3d5e61 Author: Peter Marschall Date: 2020-08-28 19:01:36 +0200 spelling fixes for coolscan3 backend commit dde1ad7a82689e247818672aeae621c8ae87a44e Author: Peter Marschall Date: 2020-08-28 19:00:07 +0200 spelling fixes for kvs40xx backend commit 5d606d9b56126b19dd425f73cd80a06cdd787449 Author: Peter Marschall Date: 2020-08-28 18:59:58 +0200 spelling fixes for kvs20xx backend commit bf5ddfe38912be356d662364da08344b5b5bf86f Author: Peter Marschall Date: 2020-08-28 18:59:42 +0200 spelling fixes for kvs1025 backend commit 0ff0d7d25bbe66d79126eb8f97cbb0c8ed0bebe2 Author: Peter Marschall Date: 2020-08-28 18:58:19 +0200 spelling fixes for epjitsu backend commit f69854bb4843756c630b49e2b7e9b126b340156f Author: Peter Marschall Date: 2020-08-28 18:57:23 +0200 spelling fixes for rts8891 backend commit 78dd1892abe7bef46672ce7260f2b2022af3832e Author: Peter Marschall Date: 2020-08-28 18:55:39 +0200 spelling fixes for sm3840 backend commit a94c17175f2bd3bf541fc62ff1036351c44eefa0 Author: Peter Marschall Date: 2020-08-28 18:55:26 +0200 spelling fixes for sm3600 backend commit 3e33c9664aebda7b87db0e77b0e533e6f4039e6c Author: Peter Marschall Date: 2020-08-28 18:52:53 +0200 spelling fixes for pixma backend commit 4f4278ab2dc80dbc893eb8b24daf28939875390e Author: Peter Marschall Date: 2020-08-28 18:51:48 +0200 spelling fixes for escl backend commit 13b56dd1223fa3fc09019f45142faf022ac22884 Author: Peter Marschall Date: 2020-08-28 18:50:36 +0200 spelling fixes for pie backend commit 63c686b9295983f3196f0043956eff7da43f42b9 Author: Peter Marschall Date: 2020-08-28 18:50:24 +0200 spelling fixes for pieusb backend commit 064d73ed5a653998f518e5d9fecde2c27e104e04 Author: Peter Marschall Date: 2020-08-28 18:48:30 +0200 spelling fixes for umax backend commit 0d862ba5753183e14f01f63270857e63c3ad7786 Author: Peter Marschall Date: 2020-08-28 18:48:12 +0200 spelling fixes for umax1220u backend commit e8ba89fdccf0d87eac731cdf3d8f283423b0e615 Author: Peter Marschall Date: 2020-08-28 18:46:31 +0200 spelling fixes for umax_pp backend commit b8cc6ce3a9ff9fe535d3a888703988536b57d47d Author: Peter Marschall Date: 2020-08-28 18:45:02 +0200 spelling fixes for cardscan backend commit 192262a2774df19a70233a3f158b76482ac7257d Author: Peter Marschall Date: 2020-08-28 18:43:42 +0200 spelling fixes for p5 backend commit 0b2e89db18bddfca70a1aa00271a2ffaf9104907 Author: Peter Marschall Date: 2020-08-28 18:42:16 +0200 spelling fixes for snapscan backend commit cbbfcd1121af9cc8322cd17aed8afe43287e430a Author: Peter Marschall Date: 2020-08-28 18:39:49 +0200 spelling fixes for gt68xx backend commit f4385e21c34d77626569376f56290a04611b5992 Author: Peter Marschall Date: 2020-08-28 18:35:49 +0200 spelling fixes for teco3 backend commit 78bf42e04e8761b7a2a3b1ec246c354e188f6868 Author: Peter Marschall Date: 2020-08-28 18:35:41 +0200 spelling fixes for teco2 backend commit fe71899cc83747ee8c4e052401eb3197f14fe94f Author: Peter Marschall Date: 2020-08-28 18:35:33 +0200 spelling fixes for teco1 backend commit bd081d3bbe0a512cab1df9a336648a0376449f18 Author: Peter Marschall Date: 2020-08-28 18:32:54 +0200 spelling fixes for mustek_usb backend commit 6cb28ca627e556082fc434b377d16a54444a2612 Author: Peter Marschall Date: 2020-08-28 18:32:42 +0200 spelling fixes for mustek_pp backend commit a1239c5601b67841c4cf6021486578182a4aa6e9 Author: Peter Marschall Date: 2020-08-28 18:32:10 +0200 spelling fixes for mustek backend commit 493ed0a533ca7702b75788aa40514e31aeba0cf4 Author: Peter Marschall Date: 2020-08-28 18:29:16 +0200 spelling fixes for mustek_usb2 backend commit 9afda64c421c24e1a42cf6803faff3f759cdbebf Author: Peter Marschall Date: 2020-08-28 18:23:32 +0200 spelling fixes for plustek backend commit e3f20a932c368906f50124db4c434044a05f545a Author: Peter Marschall Date: 2020-08-28 18:21:06 +0200 spelling fixes for plustek_pp backend commit 0cd9de9befaca49ff8717233f4a0a01b338ea56d Author: Peter Marschall Date: 2020-08-28 18:17:08 +0200 spelling fixes for genesys backend commit 94a9ef8250564b916bf2c1bb74c556c1862d1506 Author: Peter Marschall Date: 2020-08-28 18:13:03 +0200 spelling fixes for ChangeLogs commit a307ba8ccf3d9ac5fb92ada268169c40234c08c0 Author: Peter Marschall Date: 2020-08-28 18:12:00 +0200 spelling fixes for u12 backend commit f59ba7003d4f5cdaddaba722aff01ddae23d6bbf Merge: 7884311d4a20 e946d14e6cd9 Author: Ordissimo Date: 2020-09-07 08:40:34 +0000 Merge branch 'escl-add-models' into 'master' Addition of two models See merge request sane-project/backends!517 commit e946d14e6cd9dcc678610b7c7d81a92ca6d10174 Author: thierry1970 Date: 2020-09-07 10:15:03 +0200 Addition of two models commit 7884311d4a207c20feca30d2a229459c83a1e5e4 Author: thierry1970 Date: 2020-09-07 10:10:40 +0200 Revert "Addition of two models" This reverts commit 059296b287774f68e3379bb9c63e6c706a7770a0. commit 059296b287774f68e3379bb9c63e6c706a7770a0 Author: thierry1970 Date: 2020-09-07 10:07:06 +0200 Addition of two models commit c946aa5a84297f33b30cddb0c89fcf375ed65d10 Merge: 8894364c5bbc 0f9b481fee97 Author: Olaf Meeuwissen Date: 2020-09-04 11:48:13 +0000 Merge branch 'dell1600n_net_return_eaccess_if_local_only' into 'master' dell1600n_net: return ACCESS_DENIED when local devices are requested See merge request sane-project/backends!506 commit 0f9b481fee97c05adaceaeb9291a73380372d6d9 Author: pobrn Date: 2020-08-20 15:39:27 +0200 dell1600n_net: return empty list when local devices are requested !502 made the dell1600n_net backend return GOOD status when local devices are requested. This is problematic since in that case the caller may assume that the passed device list has been populated. However, this is not the case for this backend, since the device list is not modified in any way in that case. The caller trying to access the device list may lead to undesired consequences. Fix that by returning an empty list if 'local_only' is true. commit 8894364c5bbcbb893ca22d3bfda693c857157c35 Merge: f7ad04168227 f089aeac8c28 Author: Olaf Meeuwissen Date: 2020-09-03 12:10:51 +0000 Merge branch 'master' into 'master' backend/avision.c adf_reset(): In debug message "adf_reset: read ... failed... See merge request sane-project/backends!515 commit f089aeac8c2863def8540eb6452cd86720156888 Author: Adam Richter Date: 2020-08-24 03:56:36 -0700 backend/avision.c adf_reset(): In debug message "adf_reset: read ... failed ...", fix arithmetic and compiler warning about printf integer format type mismatch. commit f7ad04168227ae37ddc0497129688ef2d22eaff1 Author: pimvantend Date: 2020-08-28 14:30:48 +0200 canon_lide70 eliminating old cmd_buffer commit 0544e420029938fb2f3afb9737b58644c3490310 Author: pimvantend Date: 2020-08-27 15:16:05 +0200 lide 70 init problem solved commit e97919d53b63158ae15c70a67db8b6fa6d363190 Author: pimvantend Date: 2020-08-26 15:25:24 +0200 canon_lide70 small changes commit ead06704ea4bc5e8963a72c4d02e66d26a70fd80 Author: pimvantend Date: 2020-08-24 17:14:40 +0200 removed trailing whitespace commit 5bfbb13b08cfcc69c330d1b6132b1ff82db56e19 Author: pimvantend Date: 2020-08-24 15:23:00 +0200 renaming startblobs, modernizing gamma procedures commit bfab54e3913f3f7fb80f7a3d4532efe1b4dad072 Merge: ed43548de752 871813a22db1 Author: Olaf Meeuwissen Date: 2020-08-24 09:19:44 +0000 Merge branch 'release/1.0.31' into 'master' Release/1.0.31 See merge request sane-project/backends!514 commit ed43548de752d2f02fcd42158f1f5bfac6f39ec8 Author: pimvantend Date: 2020-08-22 11:15:44 +0200 first support for the lide600. 1200dpi dropped. commit d00af49e365e2cbad363b2faf27bbed1410ce570 Author: pimvantend Date: 2020-08-20 16:49:17 +0200 preparing for the lide600 commit 245564dffe96ddf0fd1c703c48973cd35fddd268 Merge: 65db792a14c1 45f84a569c1f Author: Olaf Meeuwissen Date: 2020-08-16 09:55:14 +0000 Merge branch 'respect_local_only' into 'master' Respect 'local_only' parameter of sane_get_devices() Closes #140 and #130 See merge request sane-project/backends!502 commit 45f84a569c1f15fd76f36199061eed41f5f1587f Author: pobrn Date: 2020-08-12 12:29:36 +0200 respect 'local_only' parameter of sane_get_devices() Certain backends do network scans even if the 'local_only' parameter for 'sane_get_devices()' is true. Fix that. Changes: 1. Modify 'sanei_configure_attach()' so that backend specific data may be passed to the 'attach()' function. 2. Use this in certain backends to pass the value of the 'local_only' parameter so that network related activities are only carried out when the value is false. commit 65db792a14c1db609c4d5ae57b4c9272282ea5ca Author: Rolf Bensch Date: 2020-08-15 14:33:48 +0200 pixma: backend version 0.28.5 commit 311bf867f9119ba72c92f321356fdba9a3a2f176 Author: Rolf Bensch Date: 2020-08-15 14:32:58 +0200 pixma: MP495 is working Pixma backend supports only symmetric scan resolutions, here 600dpi. See issue sane-project/backends#32 commit d1fea9269d118942bbc52123e9c4378fb9c5ca70 Author: Rolf Bensch Date: 2020-08-14 20:27:06 +0200 Revert "pixma: MP490 Series doesn't need special image formatting @ high dpi" This reverts commit 1675697366e0d69edf1e413265c6fc15e6f3ab97. See issue sane-project/backends#338 commit 5207b12d5d35e9fe76ffbbbab3264dbd7199ac10 Merge: d289b42ad736 34c25369e5c7 Author: Rolf Bensch Date: 2020-08-14 19:58:51 +0200 Merge branch '333-canon-mf264dw-fails-to-scan' See issue sane-project/backends#333 commit 34c25369e5c78a84eec0d6b2ef32200325dc4ae3 Author: Rolf Bensch Date: 2020-08-14 19:58:15 +0200 pixma: backend version 0.28.4 commit 584099c9c17b8cbcd2772679becab0ccdc37f8db Author: Rolf Bensch Date: 2020-08-14 19:47:11 +0200 pixma: enable PIXMA_CAP_JPEG for Canon i-SENSYS MF260 Series commit a384de5aeacda7d822ecd80ff3cb668d9d92f352 Author: Rolf Bensch Date: 2019-11-20 22:42:52 +0100 pixma: new capability PIXMA_CAP_JPEG backends-1.3.0/ChangeLogs/ChangeLog-1.0.4000066400000000000000000000707211456256263500175340ustar00rootroot00000000000000************************ Release of sane-backends 1.0.4 ********************** 2000-12-22 Henning Meier-Geinitz * configure configure.in: Changed version to 1.0.4 and package to sane-backends (from Oliver Rauch * TODO: Some points about OS/2 compilation problems. 2000-12-17 Henning Meier-Geinitz * TODO: More details for net/saned problems. 2000-12-16 Jochen Eisinger * TODO: removed entry about net.c seg-faulting when saned isn't loaded or timed-out 2000-12-16 Henning Meier-Geinitz * configure configure.in: Disable warnings by default for release. * sanei/sanei_wire.c: Fixed typo. 2000-12-16 Jochen Eisinger * sanei/sanei_wire.c: added test for negative parameter to memcpy() 2000-12-15 Gerhard Jaeger * fixed wrong options in sane-plustek.man 2000-12-12 Oliver Rauch * corrected backend version in umax.desc ************************** Code freeze for SANE 1.0.4 ** ********************* 2000-12-12 Henning Meier-Geinitz * NEWS: Updated/corrected version numbers of backends. 2000-12-10 Henning Meier-Geinitz * doc/sane-scsi.man: Small changes/updates concerning Linux 2.2. Updated entries about Adaptec cards, NCR53c400/Domex 3181 cards, NCR 810 cards. Removed direct links to some backends. * doc/sane-snapscan.man: Added link to new snapscan website. 2000-12-10 Henning Meier-Geinitz * backend/mustek.c backend/mustek.desc backend/mustek.CHANGES: Fixed wrong comment in encode_resolution. Added missing test for return status in sane_start. Fixed wrong calculation of tlx and tly for Pro series scanners. Increased version number. 2000-12-09 Karl Heinz Kremer * backend/epson.c: Fixed user defined gamma curve for inverted images. When scanning negatives the gamma curve was applied incorrectly. Fixed debug output for user defined gamma curves - no [epson] in between values anymore. 2000-12-09 Peter Kirchgessner * doc/sane-hp.man, backend/hp.desc Add HP ScanJet 6390C to list of supported scanners 2000-12-07 Henning Meier-Geinitz * TODO: Updated. Removed entries about plustek comment problembelm and libsane.la. Added entry about disabling local scanning. 2000-12-07 Gerhard Jaeger * backend/plustek-share.h backend/plustek.h backend/plustek.c removed warning conditions fixed a bug that causes wrong frontend displays fixed problem when driver returns error during read 2000-12-05 Henning Meier-Geinitz * NEWS: Updated backend versions. Added point about compilation fixes on Irix and FreeBSD. * backend/mustek.c backend/mustek.desc backend/mustek.CHANGES: Fixed problem with lamp control of SE scanners and reduced length of scan area for Paragon 1200 SP PRO. Removed warning for this scanner. Increased version number. 2000-12-05 Henning Meier-Geinitz * TODO: Updated. Added point about plustek-share.h using c++ comment and reminder to check config.in.h. Removed entriy about as6e.c stat result check. * backend/as6e.c backend/as63.h: Committed patch from Eugene Weiss . Fixes: Use only DBG (no printfs). Check result of stat. Add GPL header. 2000-12-05 Gerhard Jaeger * backend/plustek-share.h cleanup 2000-12-05 Peter Kirchgessner * backend/hp-handle.c - Change SCL_UNLOAD to SCL_CHANGE_DOC (bug from copying code) 2000-12-04 Henning Meier-Geinitz * TODO: Updated. Added entries about as6e stat problems and addition to sanei_scsi.c from FreeBSD ports. ************************* snapshot-2000-12-04 ******************************* 2000-12-04 Peter Kirchgessner * backend/hp.c, hp-handle.c, hp.desc - fix problem with ADF support on 6350C (and maybe others) 2000-12-04 Oliver Rauch * removed usage of gettext, problem with -lintl should be solved: - removed usage of gettext from lib/getopt.c - removed test for libintl/gettext in configure[.in] - removed intllib from frontend/Makfile.in * updated umax.desc 2000-12-03 Karl Heinz Kremer * backend/epson.*: Version 0.1.38 Removed changes regarding 12/14 bit support because of SANE feature freeze for 1.0.4. The following fixes are in the software compared to the version prior to the feature freeze: - refresh UI after a change in the scan mode setting (was not updated when going from Binary to Gray or vice versa) - Read values for "line distance" from the scanner instead of using hardcoded values. This makes sure the backend always uses the correct values regardless of firmware version. - Fixed an "off-by-one" error in the color reordering routine that caused weird artifacts in some instances. 2000-12-03 Karl Heinz Kremer * backend/epson.c: Fixed off-by-one in color reordering 2000-12-02 Karl Heinz Kremer * backend/epson.*: Read information about optical resolution and line distance from scanner instead of hardcoded values. Add support for color depth > 8 bits per channel. (can use 12, 14 and 16 bits per channel) EPSON backend is now version 0.36 2000-11-30 Oliver Rauch * changed backend/Makefile.in: wrong old installation of libsane.la $(INSTALL_PROGRAM) libsane.la $(libdir)/libsane.la to this: $(LIBTOOL) $(MINST) $(INSTALL_PROGRAM) libsane.la $(libdir)/libsane.la 2000-11-30 Henning Meier-Geinitz * README.irix: Removed. Problem with libjpeg is solved. * TODO: Updated. Removed hpoj addition. Added snapscan link. Added entry about net backend segfaulting if using net:localhost. Removed artec O_SYNC problem. Removed Irix jpeg problem. Removed PTAL addition. * doc/saned.man: Added paragraph about xinetd (from Matt Mozur ). Removed links to backends, added sane-"backendname" instead. * doc/sane-net.man: Added comment about segfaults if scanning on localhost. * sanei/sanei_scsi.c: Added missing DBG_INIT (from FreeBSD ports). 2000-11-30 Gerhard Jaeger * backend/plustek.c backend/plustek.desc backend/plustek-share.h backend/plustek.h doc/sane-plustek.man: changed to version 0.38 - document update, minor bugfixes 2000-11-30 Chris Pinkham * artec.c: removed O_SYNC for posix compatibility, converted variable to unsigned char to get rid of overflow error. Changed version to 0.5.15. 2000-11-30 Oliver Rauch * changed sane-config.in again: sane-config --libs has to print all libs that the sane libs depend on. This is not needed for shared libs and that was the reason why I removed all libs but "-lsane". But when linking against static sane libs we need to know about all necessary libs. ************************* snapshot-2000-11-28 ******************************* 2000-11-28 Oliver Rauch * added SANE_CHECK_JPEG to aclocal.m4 and configure.in (test routine by henning Meier Geinitz) ************************* Feature freeze for SANE 1.0.4 ********************* 2000-11-27 Peter Kirchgessner * NEWS: hp-backend version is 0.92 2000-11-27 Oliver Rauch * backend/umax.c: corrected wrong BUILD (25->24) 2000-11-26 Henning Meier-Geinitz * AUTHORS: Added Karsten Festag as maintainer of microtek2. * backend/mustek.* doc/sane-mustek.man: Update to Mustek backend 1.0-100. Added option fast-preview that tries to use the fastest mode available for preview. This is supported for the 3-pass scanners (was option "preview in gray") and some of the ScanExpress scanners (6000 SP, 12000 SP Plus). Use #include "../include/sane/...". Put option force-wait into mustek.conf (disabled). Removed block mode for Paragon 6000 SP and 8000 SP. Code cleanup. More debug output. Details in backend/mustek.CHANGES. * doc/.cvsignore: Added sane-as6e.5 and sane-nec.5. 2000-11-26 Peter Kirchgessner * backend/hp.desc, doc/sane-hp.man Add descriptions for PTAL support * backend/hp.c If PTAL-support requested, write warning about unsupported PTAL with DBG(0,... instead of DBG(1,... 2000-11-25 Henning Meier-Geinitz * configure configure.in acinclude.m4 aclocal.m4 include/sane/config.h.in: Added test for PTAL library and headers (patch from David Paschal ). Some reformatting of the output of configure --help. * backend/GUIDE: Updated entry about #include. 2000-11-24 Henning Meier-Geinitz * AUTHORS: Removed authors of xscanimage and xcam (now in sane-frontends). * README.unixware2, README.unixware7: X-frontends are no longer included. Removed comments and sourcecode of "rev" (not supported/used in backends/Makefile anymore). * sane.lsm: X-frontends are no longer included. Used blanks instead of tabs. * doc/scanimage.man: Removed links to backends, added generic link instead. * doc/.cvsignore frontend/.cvsignore: X-frontends are no longer included. 2000-11-24 Oliver Rauch * changed configure.in, configure frontend/Makefile.in and backend/Makfile.in: The shared libraries required by the backends are linked to the backends (they have been linked to the frontends before). Only libintl is linked to the frontend. * changed tool/sane-config.in, sane-config --libs => "-lsane", all other libraries are linked to the backend libs now 2000-11-24 Jochen Eisinger * doc/scanimage.man doc/saned.man: updated description of password file * sanei/sanei_auth.c frontend/saned.c frontend/scanimage.c: replaced index() by strchr(). Changed style of password file * backend/net.c: the net backend now prepends net:host: to the resource before calling the auth_callback function 2000-11-23 Oliver Rauch * removed doc/xcam.man, doc/xscanimage.man and updated doc/Makefile.in * changed configure.in and frontend/Makefile.in, "-lintl" is not added to @LIBS@ any more, @INTLLIBS@ is defined instead now, this is necessary because "-lintl" has to be added to the frontends and not to a backend and sane-config --libs must not list it. * Added "Introduction" to README that tells about the new X-frontend package * removed gimp and gtk parts from include/sane/config.h.in * removed gtk test from aclocal.m4 * removed xcam.c xscanimage.c sane-style.rc progress.c progress.h preview.c preview.h preferences.c preferences.h gtkglue.c gtkglue.h from frontend/ * removed parts for xcam and xscanimage from frontend/Makefile.in, configure.in and updated confiugre 2000-11-23 Henning Meier-Geinitz * NEWS: Updated for SANE 1.0.4. Please check. * README.aix: Removed paragraph about shared libs not working. * README.linux: Added paragraph about excessive warnings due to glibc bug (from Peter Kirchgessner). * TODO: Removed entry about shared libs not working on AIX. Added entry about PTAL checks. Removed entry about DBG and microtek2. Added entry about dc210 and jpeg functions. Added entry about using strchr() instead of index(). * sane.lsm: Added NEC. * backend/microtek2.c: (from karsten.festag@t-online.de (Karsten Festag)) Use DBG instead of printf/fprintf. Use #include "../sane/include/..." instead of #include "sane/...". Fixed warnings. 2000-11-23 Karl Heinz Kremer * backend/epson.c: Display "Set Focus" control only for scanners that can actually handle the command. 2000-11-22 Oliver Rauch * changed configure and configure.in, removed test for string.h/strings.h this is not needed because we use strchr instead (always in string.h) of index (sometimes in string.h, sometimes in strings.h) * changed ltconfig for aix4*, repleace -o $objdir/$soname by -o $lib, the shared libraries are handled correct now 2000-11-21 Abel Deuring * backend/sharp.c, backend/sharp.desc, doc/sharp.man: Support for JX320 added 2000-11-21 Henning Meier-Geinitz * AUTHORS: Added FUKUDA Kazuya for the nec Backend. Used spaces instead of tabs. * PROJECTS: Removed entry about NEC. Added/updated entries about Mustek USB scanners. * README: Added link to sane-nec(5). * TODO: Removed entries about sanei_authorize and plain text passwords. * backend/Makefile.in backend/dll.conf backend/nec.conf backend/nec.c backend/nec.desc backend/nec.h doc/Makefile.in doc/sane-nec.man: Added nec backend for the NEC scanners PC-IN500/4C (from Kazuya Fukuda ) 2000-11-20 Henning Meier-Geinitz * AUTHORS: Added Eugene S. Weiss. * PROJECTS: Removed Artec as6e backend. * README: Added link to man sane-as6e.5. * TODO: Removed as6e. * doc/Makefile.in: Added generation of as6e manpage. * backend/Makefile.in: Added entries for as6e. * backend/dll.conf: Added as6e. * backend/as6e.c backend/as6e.h backend/as6e.desc doc/sane-as6e.man: Added backend for Artec AS6E parallel port scanner (from Eugene S. Weiss ). 2000-11-20 Jochen Eisinger * configure configure.in: Added test for /dev/urandom and index() in string.h or strings.h * backend/net.c: Fixed SANE_NET_AUTHORIZE call handling * doc/net.tex: Clarified definition of SANE_NET_AUTHORIZE, added definition of MD5 authorization * doc/sane.tex: Upgraded to version 1.0.2, changed definition of SANE_MAX_USERNAME_LEN & SANE_MAX_PASSWORD_LEN to 128 chars * doc/saned.man: added description of SANE_CONFIG_DIR/saned.users * doc/scanimage.man: added description of ~/.sane/pass and --accept-md5-only * frontend/saned.c: implemented user authorization on a per backend basis * frontend/scanimage.c: fixed bug (option -b wasn't present) implemented auth_callback * lib/md5.c include/md5.h: added md5 functions from GNU textutils * include/sane/sane.h: changed definition of SANE_MAX_USERNAME_LEN & SANE_MAX_PASSWORD_LEN to 128 chars * include/sane/sanei_auth.h sanei/sanei_auth.c: implemented new SANE interface for user authorization using MD5 digest * lib/Makefile.in sanei/Makefile.in: updated Makefiles 2000-11-19 Karl Heinz Kremer * backend/epson.c: Removed one debug output statement. 2000-11-19 Karl Heinz Kremer * backend/epson.[ch]: Added support for "set focus position" command. This command is necessary to scan via the TPU with the Expression1600. 2000-11-19 Henning Meier-Geinitz * TODO: Removed entry about stiff.*. Updated entry about testing saned. 2000-11-19 Jochen Eisinger * backend/mustek_pp.[ch]: replaced #include "sane/.." by #include "../include/sane/.." 2000-11-19 Peter Kirchgessner * frontend/stiff.c: Fix problem with writing Tag bits per sample for color tiff files. 2000-11-18 Jochen Eisinger * doc/sane-mustek_pp.man backend/mustek_pp.*: updated mustek_pp backend to version 0.9-devel 2000-11-15 Henning Meier-Geinitz * PROJECTS: Added entry about Visioneer OneTouch 8600. * TODO: Added entries about net backend crashing after timeout, stiff.c problems, and more details about Irix cc jpeg problems. 2000-11-15 Rene Rebe * backend/avision.[c,h]: some fixes for brightness and contrast via the gamma-table. New config-option to force the backend to use DIN A4 ("option force-a4"). Clean up. 2000-11-14 Rene Rebe * backend/avision.[h,c]: Added gamma table support in hardware. Brightness and contrast emulation via the hardware gamma table. Code cleanup. * AUTHORS: Changed my e-mail address. 2000-11-13 Henning Meier-Geinitz * Makefile.in: Install sane.h and saneopts.h only. * PROJECTS: Removed sane-pie. * TODO: Removed sane-pie. Added possible portability problem for artec. Removed entry about installed headers. Added entry about gettext and libdl. * doc/.cvsignore: Added sane-pie.5. 2000-11-13 Simon Munton * backend/pie.c backend/pie-scsidef.c backend/pie.conf backend/pie.desc doc/sane-pie.man: Added Pacific Image Electronics backend for ScanAce range of scanners (also supports DevCom BlackWidow scanners and ADLIB JetScan scanners). * README backend/Makefile.in backend/dll.conf doc/Makefile.in: Added pie backend. * sane.lsm: Added keywords for pie backend. * AUTHORS: Added myself as maintainer of pie backend. 2000-11-12 Henning Meier-Geinitz * doc/sane.tex: Fixed a typo. * doc/.cvsignore: Added the files produced by make ps. * README: Removed X-frontends. Point to sane-frontends and website. Some reformatting. * TODO: Updated concerning frontends split. * PROJECTS: Removed entries about frontends (now in sane-frontends/README) * PROBLEMS: Removed entries about frontends (now in sane-frontends/PROBLEMS) 2000-11-10 Henning Meier-Geinitz * sanei/sanei_constrain_value.c: If constraint_type is SANE_CONSTRAINT_RANGE and quantization is used, the results may be outside the limits of the range because quantization uses the original value. Fixed by doing quantization with the already checked value. 2000-11-10 Gerhard Jaeger * backend/plustek.c backend/plustek.desc backend/plustek-share.h backend/plustek.h doc/sane-plustek.man: updated to version 0.37 - support for xsane, new models document update, minor bugfixes 2000-11-08 Henning Meier-Geinitz * TODO: Updated. Added entries for viceo backend and addition to hp backend. Updated entries about the include issue. Added point about additional options for pnm backend, install problem of libsane.la, and some build problems and the --without-x problem. Some more details for detecting older SANE versions. * LEVEL2: Added entry about sending messages from backend to frontend. Removed entry about image polarity. * backend/mustek.c backend/mustek.h backend/mustek.CHANGES backend/mustek.desc: Update to Mustek backend 1.0-99. Better transparency adapter support for ScanExpress scanners. Changed color support for ScanExpress scanners (faster at some resolutions now). Paragon 1200 SP color mode should work now without garbage at the end of an image. Some bugfixes. Details in backend/mustek.CHANGES. 2000-11-08 Jochen Eisinger * include/sane/sanei_debug.h: fixed typo 2000-10-30 Henning Meier-Geinitz * TODO: Updated. Added points about --without-x and the #include stuff. Added xcam problem. Changed entry for Relisys scanner. 2000-10-30 Petter Reinholdtsen * frontend/xscanimage.c: Define GIMP_ENABLE_COMPAT_CRUFT when including gimp.h to use the old API. Eventually we should convert the frontends to use the new Gimp API. Patch from Kevin Dalley. 2000-10-29 Henning Meier-Geinitz * TODO: Updated. 2000-10-23 Peter Kirchgessner *backend/hp-accessor.c hp-device.c hp-handle.c hp-hpmem.c hp.h hp.c hp-option.c hp-scl.c: Change sanei_debug-interface Allocate accessors once (for fixed size accessors) Close connection in some error conditions 2000-10-23 Jochen Eisinger * backend/dll.c: added test for ``dll'' in dll.conf 2000-10-23 Jochen Eisinger * sanei/sanei_init_debug.c include/sane/sanei_debug.h frontend/saned.c: removed vararg macros 2000-10-17 Henning Meier-Geinitz * doc/sane.tex: Added paragraph about image polarity as discussed on sane-devel. This is the version from Nick Lamb. 2000-10-15 Jochen Eisinger * include/sane/sanei_debug.h: sanei_debug_init was only declared if VARARG macros are supported. Fixed this. 2000-10-08 Jochen Eisinger * sanei/sanei_pa4s2.c: fixed all but one compiler warning 2000-10-03 Henning Meier-Geinitz * tools/find-scanner.c: Explicitly state that find-scanner won't find non-SCSI scanners. * doc/scanimage.man: Add examples for SANE device names. * frontend/scanimage.c: Output message when no devices are found. Add example for devicename to --help message. Print warning when a Unix devicename is used instead of a SANE device. Removed compiler warning. * TODO: Removed entries about the fgets and include issues in umax. Removed entries about scanimage device name documentation. Added a point about xscanimage problems with the new GIMP API. * backend/mustek.* doc/sane-mustek.man: Update to Mustek backend 1.0-98. Fixed segmentation fault in sane_init. Enabled double buffering. Clear inquiry buffer before using. Paragon 1200 SP now uses LD_BLOCK color correction. Removed LD MFS (wasn't used anymore). Fix sane_cancel for Paragon series II scanners. Fix halftone and grain for Paragon series II scanners. Backtracking is used automatically by all scanners now. Some safety checks, code cleanup and more debug output. Details are in backend/mustek.CHANGES. 2000-10-02 Oliver Rauch * Updated umax backend to sane-umax-1.0.3-build-24 take a look at backend/umax.CHANGES for details 2000-10-02 Oliver Rauch * sanei/sanei_constrain_value.c: if checked option is a range and the value is out of range the value is now corrected to the minimum or maximum allowed value and SANE_INFO_INEXACT is set. The old version returned with an error. This caused problems when the value was a little bit out of range because of rounding errors. 2000-09-30 Henning Meier-Geinitz * backend/pnm.c: Fixed three segfaults due to buffer overruns: maximum length of filename wasn't checked; maximum length in sane_read wasn't always checked; ppm_type wasn't always set in getparmfromfile. Added some checks for safety and removal of warnings. Added DBG lines for every SANE API call. * TODO: Added entries about the backend:devicename issue. Add a comment about fronends not to crash when length is -1. Added entry about sane-pie. Added point about fronends not checking if too much data is delivered with sane_read. * PROJECTS: Added entry about sane-pie. 2000-09-24 Henning Meier-Geinitz * backend/mustek.*: Update to Mustek backend 1.0-97. This is a development version and is only tested for three-pass scanners. Added support for Paragon 1200 SP Pro and ScanExpress A3 SP. Removed detection of " C04" and " C12" (don't seem to exist). Changed SCSI request scheme. Sane_read can read more than 4096 bytes from pipe. Minimum dpi is 30 now. Fixed LD correction for Paragon 1200SP 1.06 and 1.11. Read SCSI buffer into big block and do LD after that for Paragon one-pass scanners. New option "blocksize" in mustek.conf. Better detection of the scanner type (three-pass, Paragon I, ...). Fixed possible segmentation faults in Paragon 600 II N LD code, scsi_sense_wait_ready and sense_handler. Decreased maximum scan size of Paragon 800 II SP. For three-pass scanners: fixed stop_scan and speed code, scan area is in pixels now, added RGB brightness and contrast, use +-100% for brightness and contrast. Return SANE_STATUS_CANCELLED when scan was cancelled. Option "force backtracking" is enabled by default, "scan speed" is set to "fastest". Added option "force-warn" for mustek.conf. Rewrote halftone mode. Better error handling and more debug output. * TODO: Clarified point about image data polarity. Moved point about xscanimage not updating the progress bar for 3pass scanners to frontend section. Removed umax entry in "frontends". Removed entry about alpha channel (was added to LEVEL2 file). * LEVEL2: Add point about image data polarity. * frontend/scanimage.c: Removed some warnings. 2000-09-19 Gerhard Jaeger * backend/plustek.desc doc/sane-plustek.man: updated information about supported scanners 2000-09-18 Henning Meier-Geinitz * README: Removed comment about xscanimage error message. Added comment about find-scanner (may find scanners not supported by SANE). * tools/README: Added comment about find-scanner and some lines about sane-desc.el and sane-config. * tools/find-scanner.c: Added comment about scanners not supported by SANE. Removed some warnings. * TODO LEVEL2: Removed xscanimage error comment from TODO and moved ideas for SANE 2.0 from TODO to LEVEL2. 2000-09-18 Petter Reinholdtsen * backend/v4l.c: Remove X11 dependency. 2000-09-17 Henning Meier-Geinitz * frontend/xscanimage.c: Fixed gtk_main_quit bug (based on patch from ). An error message was printed when no sane device was found. 2000-09-17 Petter Reinholdtsen * config.guess config.sub : Upgraded to latest version from . * doc/Makefile.in: New target 'install-docs' to install documentation in $(prefix)/doc/sane-/. * japi/Makefile.in: Move javac and javah to make variables. 2000-09-16 Henning Meier-Geinitz * TODO: Added xscanimage bug and information about versioning problems. Some other minor changes. 2000-09-15 Gerhard Jaeger * backend/plustek.c added some code to support timing measurement in debug sessions 2000-09-10 Peter Kirchgessner * backend/hp-handle.c Special handling of sane_cancel for OfficeJet K series 2000-09-09 Peter Kirchgessner * backend/hp.h hp-scsi.h hp-scl.h hp-option.h hp-option.c hp-device.h hp-scl.c hp.c hp-device.c hp-handle.c David Paschal (paschal@rcsis.com) Added support for flatbed HP OfficeJets. fix problem with cancel preview fix timing problem between killing child and writing to pipe change fprintf(stderr,...) to DBG change include to "sane.." in hp.h change handling of options that have global effects. i.e. if option scanmode is received (has global effect), all options that "may change" are send to the scanner again. This fixes a problem that --resolution specified infront of --mode on command line of scanimage was ignored. NOTE: This change does not allow to specify --depth 12 infront of --mode color, because --depth is only enabled with --mode color. add depth greater 8 bits for mode grayscale add option for 8 bit output but 10/12 bit scanning 2000-08-24 Gerhard Jaeger * Updated plustek backend to support multiple devices and 12bit color-depth affected files: backend/plustek.c backend/plustek.h backend/plustek-share.h backend/plustek.conf * backend/plustek.desc: updated information about supported scanner * doc/sane-plustek.man: updated manpage according to the changes 2000-08-23 Abel Deuring * sanei/sanei_scsi.c: Second attempt to fix the errno bug in the Linux command queue handling. 2000-08-19 Henning Meier-Geinitz * TODO: Included TODO file for 1.0.4. * sanei/sanei_ab306.c: Removed some comiler warnings. * doc/sane.tex: Set \date to the date of the last change (not the date of running latex (\today)) * frontend/xscanimage.c: Fixed image size overrun (>ca. 400 MB). Removed some compiler warnings. 2000-08-15 Henning Meier-Geinitz * configure configure.in: Default to --enable-warnings again. 2000-08-15 Jochen Eisinger * backend/mustek_pp.c: fixed bug in config_ccd_1013 that prevents ASIC 1013 scanners from working backends-1.3.0/ChangeLogs/ChangeLog-1.0.5000066400000000000000000001353031456256263500175330ustar00rootroot000000000000002001-07-01 Oliver Rauch ************************ end of code freeze *********************************** ************************ Release of sane-backends 1.0.5 ********************** 2001-06-30 Henning Meier-Geinitz * configure configure.in: Disabled warnings by default. * NEWS: Updated version numbers, added dc25. 2001-06-25 Oliver Rauch * configure.in/configure: changed version to 1.0.5 2001-06-25 ********** CODE FREEZE FOR SANE-1.0.5 snapshot sane-backends-pre1-1.0.5 planned release on 2001-07-01 2001-06-24 Oliver Rauch * backend/umax*: bugfixes: * bugfix for DOR: - bottom-right edge keeps in position if possible when DOR is switched on * bugfix for Astra 610S color mode: - scale_y is set to 0.5 if y_resolutions is smaller than optical_res/2 this does fix the color scan problems for Astra 610S. Not tested for Astra 600S. 2001-06-20 Peter Fales * backend/dc25.c - Backed out the previously added mkdtemp() patch, as mkdtemp is not available on all platforms. * TODO: Removed entry about dc25 bug 2001-06-20 Henning Meier-Geinitz * TODO: Updated documentation entries. Added compilation bug in dc25.c. 2001-06-20 Stéphane Voltz * doc/sane-umax_pp.man: corrections according to the TODO list --- cvs snapshot-2001-06-17 --- 2001-06-17 Oliver Rauch * backend/umax*: bugfixes: - removed "#include " from umax.c: assert() is never called - bugfix for 3 pass scanning: reposition_scanner is called in sane_start for 2nd and 3rd pass of 3 pass scan - bugfix for UTA and DOR geometry offsets 2001-06-14 Peter Fales * backend/dc25.c - Added a patch from Tim Waugh to use mkdtemp() instead of mktemp(). Also cleaned up some compiler warnings. 2001-06-14 Karl Heinz Kremer * doc/sane-epson.man: Removed reference to sane-devel list 2001-06-14 Simon Munton * doc/sane-pie.man: Fixed formatting problems 2001-06-14 Gerhard Jaeger * doc/sane-plustek.man: Corrected URL to Plustek mailing list 2001-06-14 Henning Meier-Geinitz * TODO: Added link to libieee1284. 2001-06-13 Karl Heinz Kremer * backend/epson.desc: Added GT-9500 (same scanner as Expression 636) * backend/epson.c: Finally updated version # 2001-06-13 Henning Meier-Geinitz * TODO: Updated the entries about documentation. 2001-06-13 Oliver Rauch * sanei/sanei-scsi.c: Corrected bug in sanei_scsi_cmd2 for IRIX: - scsi_req.ds_senselen has been set to sizeof pointer, is set to 128 bytes now (1024 bytes does not work) - sensebuf is cleared before scsi command is executed - sensehandle always called when it exists and an error occurs 2001-06-11 Henning Meier-Geinitz * tools/umax_pp.c: Also use sanei_umax_pp_SetLamp() (patch from Tim Waugh ). 2001-06-11 Stéphane Voltz * backend/umax_pp_low.c backend/umax_pp_low.h backend/umax_pp_mid.c: make use of PPGETMODES only when available in ppdev.h. Corrected OS/2 typo in Outb() and rename sanei_umax_pp_Lamp() to sanei_umax_pp_SetLamp(). 2001-06-05 Peter Fales * backend/doc/sane-dc25.man, sane-dc210.man, sane-dc240.man - Added reference to web page for information about sane mailing list 2001-06-10 Jochen Eisinger * doc/sane-mustek_pp.man: added pointer and note to list subscription page --- cvs snapshot-2001-06-10 --- 2001-06-10 Henning Meier-Geinitz * doc/sane-artec.man doc/sane-coolscan.man doc/sane-umax.man: Fixed whatis/apropos output (don't put an empty line after the NAME section). Added SEE ALSO entry to sane(7). * doc/sane-dll.man: Fixed output bug and added SEE ALSO entry. * doc/sane-scsi.man: Rephrased the paragraphs about Linux sg buffer to make it clear, that kernel changes are really only necessary with older kernels. Fixed torque.net URL. Added comment, that usually every SCSI adapter should work. Added paragraph about problems with Acard adapters. Rephrased NCR810 entry. * doc/sane-net.man: Updated BUGS section. Added SEE ALSO. * doc/saned.man doc/sane-abaton.man doc/sane-agfafocus.man doc/sane-apple.man doc/sane-as6e.man doc/sane-avision.man doc/sane-bh.man doc/sane-dc210.man doc/sane-dc240.man doc/sane-dc25.man doc/sane-dmc.man doc/sane-epson.man doc/sane-fujitsu.man doc/sane-hp.man doc/sane-microtek.man doc/sane-mustek_pp.man doc/sane-nec.man doc/sane-pie.man doc/sane-pint.man doc/sane-plustek.man doc/sane-pnm.man doc/sane-qcam.man doc/sane-ricoh.man doc/sane-s9036.man doc/sane-sharp.man doc/sane-sm3600.man doc/sane-st400.man doc/sane-tamarack.man doc/sane-umax_pp.man doc/sane-v4l.man: Added SEE ALSO entry for sane(7). * doc/sane-coolscan.man: Used @@ macros instead of fixed paths. * doc/sane-dc210.man doc/sane-dc240.man doc/sane-dc25.man: Used @@ macros instead of fixed paths. Fixed library paths. * doc/Makefile.in doc/sane-coolscan.man doc/sane-dc210.man doc/sane-dc240.man doc/sane-dc25.man: Set the correct library path for all backends by changing @LIBDIR@ to $(libdir)/sane. * doc/sane-as6e.man doc/sane-canon.man doc/sane-microtek.man doc/sane-microtek2.man: Some minor formatting issues. * doc/sane-mustek.man doc/sane-v4l.man: Added link to mostang mail page. Fixed some formatting issues. * doc/backend-writing.txt: Added paragraph about @LIBDIR@ and friends in manual pages. * TODO: Added several entries about manual pages. Removed entry about sane(7). 2001-06-09 Oliver Rauch * sanei/sanei_scsi.c: applied patch by Douglas Gilbert that solves compilation problem about: storage size of `ssid' isn't known 2001-06-09 Karl Heinz Kremer * backend/epson.c: Changed debug level for for an informational message in the sense_handler from 0 to 2. 2001-06-09 Henning Meier-Geinitz * README.os2: Added link to Franz Bakan's web page. * configure configure.in backend/Makefile.in include/sane/config.h.in: Check for sys/sem.h and disable snapscan if it isn't available. Removed "//" (C++-style comment) from config.h.in. Link to djpeg only once. * TODO: Added comment on command line option for timeout in saned. 2001-06-09 Petter Reinholdtsen * acinclude.m4 include/sane/config.h.in: Fix configure/compile problem on Alpha/Tru64 Unix. 2001-06-07 Stéphane Voltz * backend/umax_pp_low.c: make use of PPGETMODES to get ppdev capabilities. 2001-06-05 Peter Fales * backend/dc240.c - Fixed bug where attempting to change the "image number" in an empty camera causes a core dump. 2001-06-06 Stéphane Voltz * backend/umax_pp_low.c: OS/2 compile fixes (again). Backend successfully built on Warp with EMX 0.9d. 2001-06-06 Stéphane Voltz * backend/umax_pp_low.c: OS/2 compile fixes 2001-06-05 Oliver Rauch * changed my email address in sane-backends.lsm --- feature freeze for sane-1.0.5 is active --- --- cvs snapshot-2001-06-05 --- 2001-06-04 Oliver Rauch * update of umax backend to version 1.0 build 27 2001-06-04 Henning Meier-Geinitz * TODO: Updated the entries concerning net+saned. Removed the exporting symbols entry. Added entry about lassert.h. * include/lassert.h sanei/sanei_scsi.c: Use local assert.h file to fix problems with AIX and shared libraries. * frontend/test.c: Changed order of headers to get test.c compiling on OS/2. * frontend/.cvsignore: Added test. * frontend/saned.c: Increase timeout to one hour. * backend/coolscan.c: Declared all internal functions static. * tools/Makefile.in: Fixed typo that prevented make from removing mustek600iin-off. * sanei/sanei_scsi.c: Fixed some small bugs concerning error reporting. * frontend/scanimage.c: Fixed small bug in output for batch mode. 2001-06-03 Peter Fales * backend/dc240.c - Batch scanning of multiple images with scanimage was failing if subsequent images have a different size than from the first image. 2001-06-03 Karl Heinz Kremer * doc/sane-epson.man: Updated documentation 2001-06-03 Stéphane Voltz * backend/umax_pp_low.c: Fixed compile problem on archs that have inb/outb and not insb/outsb. Cleaned up and simplified probing code. 2001-06-03 Peter Fales * backend/dc240.c - Add "auto increment" option to allow all images to be downloaded with the --batch option to scanimage. 2001-06-03 Henning Meier-Geinitz * backend/mustek.c: Added some debug messages. * backend/net.c: Fixed yet another segfault. Added debug messages. * frontend/scanimage.c doc/scanimage.man: Added more options for batch-scanning (patch from Caskey Dickson ). 2001-06-02 Chris Pinkham * backend/artec.desc: updated version number to match artec.c. 2001-06-02 Oliver Rauch * corrected umax version number in umax.desc (1.0.24->1.0.26) 2001-06-02 Henning Meier-Geinitz * NEWS: Updated concerning backend versions. * TODO: Removed nec entries. Added entry about ADF extension to scanimage. * backend/qcam.c: Another occurrence where struct flock must be defined for OS/2. * AUTHORS: Fixed Kazuya Fukuda's name and email address. * backend/nec.c backend/nec.h backend/nec.desc: Update to backend version 0.12 (from Kazuya Fukuda ). Removed references to sharp backend (grep for "JX"). Check for HAVE_SYS_SHM_H before including sys/shm.h and disable shared memory support if necessary. Free devlist allocated in sane_get_devices() in sane_exit() resolution setting bug fixed (PC-IN500/4C 10dpi step). Removed resolution list. 2001-06-02 Oliver Rauch * changed minimum scsi buffer size from 64KB to 32KB 2001-06-01 Henning Meier-Geinitz * backend/umax_pp_low.c: Fixed the FreeBSD problems by checking /dev/io before accessing io ports (patch from Stéphane Voltz ). * backend/snapscan.c: Fixed two bugs: pss->devname must be checked for 0. Return total number of lines in sane_get_parameters (patch from Ben Stanley ). 2001-05-31 Henning Meier-Geinitz * backend/mustek.c: Updated some comments. * TODO: Removed the entry about the config_line[len-1] bug. * configure configure.in include/sane/config.h.in backend/qcam.c: Check for struct flock and define it if necessary. * tools/Makefile.in: Make sure that backend/umax_pp_low.o exists when umax_pp.o is linked. * backend/bh.c backend/m3096g.c backend/sp15c.c: Fixed config_line[len-1] bug which could generate an access violation if len==0. * lib/alloca.c lib/getnev.c lib/getopt.c lib/getopt1.c lib/isfdtype.c lib/md5.c lib sigprocmask.c lib/snprintf.c lib strdup.c lib/strndup.c lib/strsep.c lib/usleep.c lib/vsyslog.c: Used #include "../include/sane/config.h". * sanei/sanei_scsi.c: Fixed compilation issue for OS/2 in sanei_scsi_find_devices. --- cvs snapshot-2001-05-30 --- 2001-05-30 Marian Eichholz * backend sm3600 with better gain calibration incorporated. fixed a superfluceous export. 2001-05-30 Oliver Rauch * Problem with SOLARIS_INTERFACE (change from 2001-05-27) had an other reason than the scsi buffer size (MAX_DATA). Changed scsi buffer size for solaris sg driver (USE == SOLARIS_INTERFACE in sanei_scsi.c) again from 32 KB to 128KB. 2001-05-30 Henning Meier-Geinitz * tools/Makefile.in: Fixed umax_pp compilation problem with Solaris and other operating systems. 2001-05-29 Henning Meier-Geinitz * backend/canon.conf doc/sane-canon.man: Added config file and some documentation for it. * AUTHORS: Added Chris Bagwell and Oliver Schwartz for snapscan. * TODO: Added entry about possible sanei_usb.*. 2001-05-29 Peter Fales * backend/dc240.c - Fixed a fairly obscure problem that can result in core dump. (Deleting pictures with gphoto, then launching xscanimage without power cycling the camera left bad data in the camera status table.) 2001-05-29 Oliver Rauch * added entry about sanei_parport in TODO 2001-05-27 Henning Meier-Geinitz * doc/releases.txt: Removed comment about net.* and dll.* version updating. Added info about make install-mostang. * TODO: Updated concerning backend bugs (export and config_line). * backend/umax_pp_low.c: Fixed bus error with FreeBSD. 2001-05-27 Oliver Rauch * changed scsi buffer size for solaris sg driver (USE == SOLARIS_INTERFACE in sanei_scsi.c) to 32 KB again because it does not work for at least one system with 64KB. 2001-05-27 Petter Reinholdtsen * doc/releases.txt: Added points on tagging the source and generating a diff from the last release. --- cvs snapshot-2001-05-27 --- 2001-05-27 Oliver Rauch * sanei/sanei_scsi.c: changed scsi buffer size (MAX_DATA) to 128KB for: SOLARIS_INTERFACE, SOLARIS_SG_INTERFACE, SOLARIS_USCSI_INTERFACE and IRIX_INTERFACE (some scanners have problems with the predefined 32KB buffer size, these systems should work with 128KB but we need to test if no problems do occur). 2001-05-27 Henning Meier-Geinitz * doc/sane-microtek2.man: Include updates from backend version 0.9 (from karsten.festag@t-online.de (Karsten Festag)). * backend/sm3600.c: setResolutions has only 5 instead of 6 resolutions. 2001-05-26 Jochen Eisinger * backend/mustek_pp.c: declared function comp() static * TODO: removed entry regarding this problem 2001-05-26 Chris Pinkham * backend/artec.c: Added code in sane_exit() to free memory malloced in sane_get_devices(). Declared several functions 'static' so they are not exported. * TODO: removed 'artec' backend from list mentioned in describing above problems. 2001-05-26 Petter Reinholdtsen * TODO: Updated entry on library search path, sanei API documentation and sane_init() return value. * Makefile.in:New target 'libcheck' making sure libraries export only sane_* and sanei_* symbols. 2001-05-26 Henning Meier-Geinitz * TODO: Removed snapscan and microtek2 updates. Removed status change of as6e. Removed True64 Unix and HP/UX compilation problems. Added entry about exporting symbols. Reformatted some entries. * backend/microtek2.c backend/microtek2.desc backend/microtek2.h: Updated to version 0.9 (from Karsten Festag ). * doc/sane-snapscan.man: Added new scanners and authors. * AUTHORS: Added Sebastien Sable and Henrik Johansson. * backend/snapscan-scsi.c backend/snapscan-sources.c backend/snapscan-sources.h backend/snapscan-usb.c backend/snapscan-usb.h backend/snapscan.c backend/snapscan.conf backend/snapscan.desc backend/snapscan.h: Updated snapscan backend to version 1.2 (from Sebastien Sable ). * doc/backend-writing.txt: Added "make libcheck" info. * backend/sm3600-color.c backend/sm3600-gray.c backend/sm3600-homerun.c backend/sm3600-scanmtek.c backend/sm3600-scanusb.c backend/sm3600-scanutil.c backend/sm3600.c backend/sm3600.h: Made all non-sane-api functions static. Fixed some warnings. * backend/sm3600.desc: Changed status to new. * backend/as6e.desc: Changed status to alpha because backend isn't new any more. * acinclude.m4 aclocal.m4 configure configure.in include/sane/config.h.in: Fixed u_int* bug for Tru64 by including sys/bitypes.h. * PROJECTS: Added hpoj project. 2001-05-25 Jochen Eisinger * doc/sane-mustek_pp.man, backend/mustek_pp.desc: Changed URL of the homepage to home.nexgo.de/jochen.eisinger/sane/ 2001-05-25 Karl Heinz Kremer * backend/epson.c: Allow more than 8 bit color depth even for preview mode since Xsane can handle this. Some code cleanup. Removed ancient code that was used to determine the resolution back when the backend still had a slider for the resolution selection. * backend/epson.desc: Updated version number, added new scanners. 2001-05-24 Henning Meier-Geinitz * sane-mustek.man: Removed some bug descriptions and added sane(7) to SEE ALSO. * sane-find-scanner.man sane-scsi.man scanimage.man: Added sane(7) to SEE ALSO. * PROJECTS: Removed entry about Polaroid SprintScan 35LE (now in microtek). * TODO: Removed umax from lists of backends with sane_get_devices and config_line bug. Removed entry about make uninstall and sanei_wire.c bug. Removed entry about microtek update. Added point about adding sane(7) to any manual page. 2001-05-23 Matto Marjanovic * backend/microtek.c backend/microtek.h: o Added support for Agfa StudioStar (weird pixel packing) o A couple of small fixes (missing 'static' declarations) o Upped version to 0.12.3 2001-05-23 Matto Marjanovic * backend/microtek.c backend/microtek.h: Small fixes: o Use 'expanded contrast/exposure settings' inquiry data to dynamically set ranges for those options. o Patches for Polaroid SprintScan 35LE (from Dick Bruijn) - added "Polaroid" vendor string - screwed around with 'brightness' setting - attached 'negative scan' button o sane_exit() now frees the devlist allocated by sane_get_devices() o upped version to 0.12.2 2001-05-23 Stéphane Voltz * backend/umax_pp.c: changed option names to use lower case characters and dashes instead of spaces. * backend/umax_pp_low.c: commented out a useless command sequence. Added 55 AA pattern escaping in data block sending. 2001-05-22 Karl Heinz Kremer * backend/epson.c: Added sense handler to support the GT-8000 scanner. Also added experimental (and #ifdef protected) USB scanner probing code - requires patch to USB scanner driver. 2001-05-21 Henning Meier-Geinitz * README: Added "man sane" to quick install. Added paragraph about old versions. Added paragraph about "make clean" and "make distclean". Added section about removing SANE. * Makefile.in backend/Makefile.in doc/Makefile.in lib/Makefile.in sanei/Makefile.in tools/Makefile.in: Added target "uninstall". 2001-05-20 Oliver Rauch * removed a bug in saned (patch by Matthias Trute ) calculation of the reader buffer index was not correct: if (reader >= sizeof (buf)) - reader = 0; + reader -= sizeof(buf); 2001-05-19 Karl Heinz Kremer * backend/epson.c: fixed dates in change log entries and finally fixed the TPU bug with older scanners 2001-05-17 Oliver Rauch * removed bug in umax backend sane_exit 2001-05-17 Henning Meier-Geinitz * include/lalloca.h: Used a more suitable comment. * doc/backend-writing.txt: Reformated. Added point about headers and libraries that are not available on every system and inclusion of backends into CVS. Some minor fixes and clarifications. * NEWS: Updated concerning sane man page and new backends. 2001-05-17 Oliver Rauch * updated umax backend to version 1.0 build 26 2001-05-15 Peter Fales * backend/dc25.c, backend/dc240.c - Added DBG(1,..) to print version string 2001-05-16 Henning Meier-Geinitz * doc/.cvsignore: Added sane-sm3600.5. * PROJECTS: Removed sm3600. * TODO: Updates concerning memory leaks and access violations. * AUTHORS doc/Makefile.in: doc/sane-sm3600.man doc/sane.man: Updated documentation concerning sm3600 backend and added man page (from from Marian Eichholz ). * configure configure.in include/sane/config.h.in: Detect presence of libusb and enable sm3600 backend. * backend/Makefile.in backend/dll.c backend/sm3600-color.c backend/sm3600-homerun.c backend/sm3600-scantool.h backend/sm3600-scanutil.c backend/sm3600.desc backend/sm3600-gray.c backend/sm3600-scanmtek.c backend/sm3600-scanusb.c backend/sm3600.c backend/sm3600.h: Added sm3600 backend (from Marian Eichholz ). 2001-05-16 Chris Pinkham * backend/artec.c: changed #include to use "../include/sane/config.h" instead of "sane/config.h". 2001-05-16 Stéphane Voltz * backend/umax_pp.c: fix memory leak in sane_exit * backend/umax_pp_low.c backend/umax_pp_low.h : added /proc parsing when available to get I/O addr. Added pauses in 610P probe sequence. 2001-05-14 Henning Meier-Geinitz * frontend/scanimage.c: scanimage_exit: use the handle (not its address) for sane_close. Fixed yet another warning. 2001-05-14 Gerhard Jaeger * backend/plustek.c: Free memory malloced in sane_get_devices() in sane_exit() 2001-05-14 Petter Reinholdtsen * backend/lhii.desc: Updated with current version and contact information. * frontend/scanimage.c tools/sane-find-scanner.c: Remove some compiler warnings. 2001-05-13 Peter Fales * backend/dc25.c: Remove DBG(0,...) line that was getting printed for non-dc25 users. 2001-05-13 Karl Heinz Kremer * backend/epson.c: Removed check for '\n' before end of line Free memory malloced in sane_get_devices() in sane_exit() again Check first if the scanner does support the set film type and set focus position before the GUI elements are displayed. This caused problems with older (B4 level) scanners when a TPU was connected. 2001-05-13 Henning Meier-Geinitz * doc/sane.man: Added path to sane.ps. Added comments about how somebody can help the SANE project. Added comments about possible problems with old installaitions of SANE and /etc/ld.so.conf. Added section "REPORTING BUGS". * tools/Makefile.in tools/.cvsignore: Added mustek600iin-off. * sanei/sanei_codec_ascii.c tools/mustek600iin-off.c tools/sane-find-scanner.c frontend/stiff.c: Used #include "../include/sane/...". * backend/net.c backend/net.h backend/net.desc: Fixed some memory leaks. A bit of reformatting. Added more debug output. Used #include "../include/sane/...". * sanei_codec_bin.c sanei/sanei_net.c sanei/sanei_wire.c: Fixed some memory leaks. Used #include "../include/sane/...". * frontend/scanimage.c: Added exit handler to call sane_close and sane_exit in any case and to free allocated memory. A bit of reformatting. Used #include "../include/sane/...". Fixed some warnings. * frontend/saned.c: Fixed some memory leaks. Fixed some warnings. Some reformatting. Used #include "../include/sane/...". * README.hp-ux: Use gmake if make doesn't work. * LEVEL2: Added point about sane_init results != SANE_STATUS_GOOD. * TODO: Added entries about memory leaks, sane_init return values, and HP-UX problems in isfdtype.c. 2001-05-11 Jochen Eisinger * TODO, backend/mustek_pp.c: fixed line-end handling, removed entry from TODO list 2001-05-11 Henning Meier-Geinitz * TODO: Removed pie from lists of bugs. Removed sane manual page. Added some context to the Tru64 problems. 2001-05-09 Henning Meier-Geinitz * doc/.cvsignore: Added sane.7. * README: Removed content that's now in the sane maual page. Added reference to man sane. * doc/Makefile.in doc/sane.man: Added SANE manual page. * doc/sane.tex: Clarified the paragraph concerning sane_read. If *len != 0 the status must be SANE_STATUS_GOOD. 2001-05-08 Simon Munton * backend/pie.c: Free devlist in sane_exit. Removed redundant check for trailing newline in config file. 2001-05-07 Peter Fales * backend/dc25.[ch]: Cleaned up compiler warnings and fixed (very minor) memory leak. 2001-05-07 Oliver Rauch * applied a patch by Douglas Gilbert to sanei_scsi.c it solves the Problem that the scanner is not found on linux systems when the order of the scsi devices is not coverd by the order of devices listed by /proc/scsi/scsi, it prefers new kernel 2.4 methods when available 2001-05-06 Henning Meier-Geinitz * backend/mustek.c doc/mustek/mustek.CHANGES: Free devlist in sane_exit. * TODO: Updated concerning config_line[len - 1] stuff. Added some (most) backends to list of sane_get_devices leak. * backend/dll.c backend/dll.desc: Removed access to uninitialized memory. Increased version number. * backend/net.c backend/net.desc: Removed access to uninitialized memory. Changed htons to ntohs in DBG statement. Free devlist in sane_exit. Use "../include/sane/...". Increased version number. 2001-05-04 Gerhard Jaeger * backend/plustek.c: Applied Petters' patch to avoid an illegal memory access 2001-05-04 Henning Meier-Geinitz * NEWS: Updated. * PROJECTS: Removed umax_pp. Added Canon FB620S and FS2710 backend. * TODO: Removed entry about umax_pp and comment about testing for existing sane installations and plustek memory access violation. Added Canon FB620S and FS2710 backend and entries about Tru64 compilation problems and installation procedure for translations. * doc/backend-writing.txt: Added entries about SANE_I18N() and documentation directories. 2001-05-03 Henning Meier-Geinitz * AUTHORS README: Updated concerning umax_pp. * doc/Makefile.in doc/sane-umax_pp.man .cvsignore: Added manpage for umax_pp (patch from Stéphane Voltz ). * tools/README tools/Makefile.in tools/umax_pp.c tools/.cvsignore: Added umax_pp test program (patch from Stéphane Voltz ). * configure configure.in include/sane/config.h.in: Test for linux/ppdev.h (for umax_pp backend). * backend/umax_pp.c backend/umax_pp.h backend/umax_pp_low.c backend/umax_pp_low.h backend/umax_pp_mid.c backend/umax_pp_mid.h backend/umax_pp.conf backend/umax_pp.desc backend/Makefile.in dll.conf: Added umax_pp backend (patch from Stéphane Voltz ). 2001-05-02 Henning Meier-Geinitz * configure configure.in: Don't use -ansi for Solaris. On some Solaris installations compilation breaks with -ansi. * doc/Makefile.in tools/sane-desc.el doc/.cvsignore: Install sane.ps and sane.dvi if latex/makeindex/dvips is available. Generate sane-backends.html in doc/. New target: install-mostang. Installs the sane standard (.ps, .ps.gz and html), the html manpages, and sane-backends.html on www.mostang.com. 2001-05-01 Henning Meier-Geinitz * configure configure.in include/sane/config.h.in README.solaris sanei/sanei_scsi.c: Added support for Solaris sgen SCSI driver (patch from Juergen Keil ). * acinclude.m4 aclocal.m4 configure: Added Irix to the list of operating systems that can use -Wl,-rpath. * doc/Makefile.in: Install all the READMEs and other available documentation in $docdir. Print which manpage is generated. Don't remove files/dirs that doesn't exist. Remove the html manpages in distclean. Fixed spelling mistake in .PHONY. * TODO: Added entries about possible segfaults and memory leaks. Added entry about AIX problems. Removed entry about installing documentation. * PROJECTS: Added Canon FB330P, FP630P. 2001-04-30 Jochen Eisinger * doc/sane-mustek_pp.man: Added information about CIS patches by Eddy De Greef 2001-04-29 Chris Pinkham * backend/artec.c: Fixed bug causing possible dereference of a NULL pointer. * backend/artec.desc: Modified Parport entry to say AS6E scanner supported by SANE AS6E backend. 2001-04-29 Petter Reinholdtsen * sanei/sanei_config.c sanei/sanei_codec_bin.c: Avoid reading uninitialized memory. * sanei/sanei_config.c: Fix memleak when using env SANE_CONFIG_DIR. * backend/net.c sanei/sanei_wire.c include/sane/sanei_wire.h: Make sure net backend release all memory when finished. * README: Added info about the CVS repository. 2001-04-29 Henning Meier-Geinitz * sanei/sanei_wire.c: Don't free memory that's not alocced. * sanei/sanei_config.c: Avoid reading uninitialized memory when checking SANE_CONFIG_DIR. * backend/mustek.c: Fixed several memory leaks. * frontend/scanimage.c: Fixed memory leak. * sanei/sanei_config2.c: Fixed memory leak: vendor wasn't freed. * PROJECTS: Added entry for the Fujitsu M3091DCd. Updated my email address. * TODO: Added entry for the Fujitsu M3091DCd and a combined Fujitsu backend. Clarified that "printf" also shouldn't be used. * backend/dll.desc: Added my email as contact address. * backend/m3091.desc: New file for the Fujitsu M3091DCd. * doc/sane-mustek.man: Updated URL. 2001-04-28 Henning Meier-Geinitz * backend/mustek.CHANGES backend/umax.CHANGES backend/umax.FAQ backend/umax.TODO: Moved to doc/mustek respectively doc/umax. * doc/canon.CHANGES doc/canon.install2700F.txt doc/canon: Moved canon-specific documentation to doc/canon. * doc/mustek: New subdirectory for mustek-specific documentation. * doc/sane-umax*.html doc/sane-umax*.jpg doc/sane-umax-doc.tex doc/sane-umax-doc.dvi doc/umax: Moved all the umax-specific documentation to new subdirectory umax. 2001-04-28 Peter Kirchgessner * backend/hp.desc, doc/sane-hp.man: Add support for new models through PTAL-library 2001-04-27 Henning Meier-Geinitz * backend/agfafogus.c: Moved PATH_MAX after sanei_backend.h include to avoid warning. Changed printf to DBG. * backend/mustek.c backend/mustek.CHANGES backend/mustek.desc: Added support for translating options. Used only PACKAGE_VERSION for output. Updated email address. New version: 1.0-107. * include/sane/sanei_backend.h include/sane/saneopts.h: Added macro SANE_I18N. It's used to mark strings that can be translated and used by a frontend. Usually these are option titles and descriptions as well as the contents of string lists. Used this macro to mark the translatable strings in saneopts.h. More translation support will follow. 2001-04-22 Henning Meier-Geinitz * PROBLEMS: Point to pnm/saned security risks. * TODO: Removed the entries about epson usb mis-detection and check for installed sane versions. * configure configure.in backend/Makefile.in: Added detection of older versions of SANE. The pnm backend is now disabled by default. * backend/dll.c backend/dll.desc: Fixed file descriptor leak (found by Douglas Gilbert). New version: 1.0.2. * backend/net.desc: Added Jochen Eisinger's email address as contact for the net backend. * doc/sane-pnm.man: Point to pnm/saned security risks. 2001-04-21 Karl Heinz Kremer * backend/epson.conf: Commented out the usb config entry, this can block other USB scanners from being recognized. 2001-04-19 Henning Meier-Geinitz * AUTHORS: Added Michael Herder and me for pnm. Updated my email address. * TODO: Removed pnm entry about adding more options. Added entry about epson backend freezing when trying to detect non-epson USB scanner. Added entry about adding contact addresses to .desc files. Added comment about me working on a sane man page. Removed entry about OS/2 problems in sanei_scsi.c. * configure configure.in: Removed a newline in --help message. * backend/pnm.c: Added several options: gamma tables (-> arrays), resolution option (word list), read only test option (SANE_CAP_SOFT_DETECT), several options for returning status codes in sane_read (all of this is from Michael Herder ). Added build version info, print version information in sane_init. Use ../include/sane/ for includes. Version is 1.0.1. * backend/pnm.desc: Updated contact info and version. 2001-04-19 Gerhard Jaeger * backend/plustek.c: removed again that old fgets() call - sorry 2001-04-19 Petter Reinholdtsen * tools/sane-config.in: Fix typo in previous commit. 2001-04-17 Karl Heinz Kremer * doc/sane-epson.man: Fixed types, some reformatting and got rid of a duplicate paragraph. 2001-04-17 Henning Meier-Geinitz * backend/dc210.desc backend/dc240.desc backend/microtek2.desc: Update contact information for backends. dc240 status is :NEW. * TODO: added entries about as6e status shouldn't be :new, and installing all documentation files. 2001-04-16 Petter Reinholdtsen * Makefile.in: New target 'sort-cvsignore' to keep all .cvsignore files sorted. * acinclude.m4 configure.in tools/sane-config.in: Try to guess how to set runtime link path, and make it easier to extend. * acinclude.m4 configure.in: Clean up warning flag handling. 2001-04-16 Henning Meier-Geinitz * README: Updated xsane link. * configure configure.in backend/dll.c backend/dll.desc backend/net.c backend/net.desc include/sane/config.h.in Yet another change in the version system for dll and net. For sane_init return the version of the package but use own internal version number in the source (printed with debugging enabled) and in *.desc. The internal versions of both net and dll are set to 1.0.1. Please increase the build number with every change on the source files. * doc/releases.txt -> doc/releases.txt. Fixed spelling error. Added entries about sane-backends.html, sane.ps, man pages, platforms page. 2001-04-14 Gerhard Jaeger * backend/plustek.c backend/plustek.desc backend/plustek.h backend/plustek-share.h minor fixes and additional scanner models * doc/sane-plustek.man added info about slowIO switch 2001-04-13 Oliver Rauch * changed my email address in AUTHORS 2001-04-13 Henning Meier-Geinitz * PROJECTS TODO: Added entry about E3 USB chipset. * backend/dll.c backend/dll.desc: New version is 1.0.5. Version and PACKAGE_VERSION is printed with debug level set to 1 or higher. The version numbers of all backends that are loaded are printed at debug level 3 or higher. Print error if backend with wrong major version number is loaded. Some standardization changes concerning the DBG messages. * backend/net.c backend/net.desc: New version is 1.0.5. Version and PACKAGE_VERSION is printed with debug level set to 1 or higher. Some standardization changes concerning the DBG messages. * backend/pnm.c: Return SANE_INFO_RELOAD_PARAMS even if the pnm file doesn't exist or can't be loaded (from mh ). * doc/backend-writing.txt: Added AUTHORS to the list of things that should be updated when including a new backend. Added points about avoiding printf and exit in backends. * doc/releases.txt: New file. This text summarizes some points to pay attention to when a new release of sane-backends is made. Additions and corrections are appreciated. * doc/scanimage.man: Add some more information about the -V option. * frontend/scanimage.c: The option -V now also prints the version of the backend (usually dll). 2001-04-11 Abel Deuring * sanei/sanei_scsi.c: fixed a bug in error handling for the Linux SG driver version 3.x * sanei/sanei_scsi.c, configure.in, configure: Usage of direct IO disabled by default. * README.linux: Added a note, how to enable direct IO 2001-04-08 Henning Meier-Geinitz * backend/mustek.c backend/mustek.desc backend/mustek.CHANGES Added detection of the TA for the Paragon 1200 SP Pro. Added warning about not supported Trust scanners. Changed description of contrast and brightness settings. Backend status is "stable" now. New version: 1.0-106. * acinclude.m4 aclocal.m4: Fixed comment concerning dc240. * configure configure.in include/sane/config.h.in: Added test for socklen_t in sys/socket.h. This fixes the compilation problem on Irix. * doc/sane-avision.man doc/sane-ricoh.man doc/sane-s9036.man doc/sane-tamarack.man: find-scanner is now called sane-find-scanner and installed on the system so it's not necessary to point to the tools directory. * frontend/scanimage.c: Fixed too long line in output. * sane-config.in: Name is sane-config again (was sane-backends-config due to the renaming of the package). * TODO: Removed entry about net.c not compiling on Irix and references to find-scanner in the man pages. Updated numbers of the compilation warnings. Still too many, however. 2001-04-07 Henning Meier-Geinitz * backend/sc240.c backend/dc240.h backend/dc240.conf backend/dc240.desc backend/dll.conf doc/sane-dc240.man: Added dc240 backend for the Kodak DC240 Digital camera (from Peter Fales ). * doc/Makefile.in: Added sane-dc240 in section 5. * backend/Makefile.in: Added dc240. * configure configure.in: If the jpeg libs are not available, dc240 is also disabled. * AUTHORS: Added entry for dc240 and updated email address of Peter Fales. * NEWS: First version of the news entry for 1.0.5. * PROJECTS: Removed dc240. Added Microrec ScanMaker 3600. * README: Added dc240. * TODO: Removed dc240. Added entry about keep-alive mechanism in saned. * sane-backends.lsm: Added dc240. * testsuite/.cvsignore: New file. Added entry for Makefile. 2001-03-31 Karl Heinz Kremer * backend/epson.*: Next attempt to get the reported number of lines correct for the "color shuffling" part. Added more comments. 2001-03-31 Henning Meier-Geinitz * backend/mustek.c backend/mustek.desc backend/mustek.h backend/mustek.CHANGES: Fixed grascale gamma for the 600 II N. Fixed ADF problems when selecting windows smaller than the whole scan area. Fixed linedistcnace corrections issues concerning ADF and concerning 1200SP V 1.00. New version: 1.0-105. 2001-03-31 Henning Meier-Geinitz * PROJECTS: Updated entry about Canon 1220U and Umax 1220P / HP3200C. Added entry about Kodak DC-240. * TODO: Added Umax 1200P/2000P & HP 320C + Kodak DC-240 as backends to be included. Added net.c compilation problem. 2001-03-10 Jochen Eisinger * frontend/saned.c: fixed bug that allowed access to a scanner without being prompted for a password 2001-03-04 Oliver Rauch * umack backend updated to version 1.0-build-25 2001-02-26 Abel Deuring * sanei/sanei_scsi.c: removed a possible null pointer dereference in the Solaris/Schilling driver part. (Bug was found and fixed by William L. Sebok) * backend/tamarack.c: inconsistent call to sanei_scsi_cmd fixed 2001-02-22 Petter Reinholdtsen * TODO: Add comment on how link problems should really be fixed. * TODO: List memleak problem for sane_get_devices(). * configure.in include/sane/config.h.in: Really move definition of SCSIBUFFERSIZE from CFLAGS to config.h. * configure.in testsuite/*: Started on runtime test suite. Currently only tests if PNM test scanning works. * sanei/sanei_constrain_value.c sanei/sanei_load_values.c sanei/sanei_save_values.c: Include "sane/sanei.h" to make sure the prototype match the function. * backend/pnm.c: Write more sensible error message when failing to open PNM file. * include/sane/sanei_backend.h: Include before testing for O_NONBLOCK and before testing for PATH_MAX to increase the chances of getting the real value. 2001-02-12 Petter Reinholdtsen * backend/dll.c backend/v4l.c: Get rid of small memory leak when using 'scanimage -L'. 2001-02-10 Tom Martone * backend/bh.c backend/bh.h doc/sane-bh.man added support for Copiscan 8000 series by Mark Temple added options deskew and rotation inquiry compares product to COPISCAN rather than COPISCAN II 2001-02-04 Peter Kirchgessner * hp-backend V0.94, backend/hp.c hp-handle.c hp-scl.h: Switch off lamp after scan 2001-01-31 Abel Deuring * sanei/linux_sg3_err.h: New version provided by Douglas Gilbert. Does not longer #include and #include * sanei/sanei_scsi.c (Linux part): fixed some format errors in debug output; added debug: system("cat /proc/scsi/sg/debug 1>&2"), if debug level >= 255. (Suggestion by Douglas Gilbert) 2001-01-23 Oliver Rauch * TODO: sane-config changes 2001-01-21 Tom Martone * backend/dll.conf: Added bh 2001-01-21 Henning Meier-Geinitz * AUTHORS: Added Tom Martone. * PROJECTS: Removed bh (now included in sane). * README: Added entry for sane-bh(5). * TODO: Removed bh. Updated BearPaw entry. Added entry about v4l problem. * backend/v4l.c: Changed comment as v4l is part of the sane package. 2001-01-20 Tom Martone * doc/.cvsignore: Added sane-bh.5. 2001-01-20 Tom Martone * backend/bh.c backend/bh.conf backend/bh.h doc/sane-bh.man Added new backend Bell and Howell Copiscan scanners * backend/bh.desc Updated version from (0.1) to 1.0-4 Referenced manpage * backend/Makefile.in Added bh to PRELOADABLE_BACKENDS Added libsane-bh.la dependencies * doc/Makefile.in Added sane-bh.5 to SECT5 2001-01-17 Henning Meier-Geinitz * backend/mustek.c backend/mustek.desc backend/mustek.h backend/mustek.CHANGES doc/sane-mustek.man: Added 36 bit mode for Pro models. Removed support for "special" modes, used options instead. Code cleanup. Updated manpage concerning sane-find-scanner. New version: 1.0-104. * configure: regenerated from configure.in. 2001-01-14 Petter Reinholdtsen * sanei/sanei_scsi.c: Make sure to initialize uninitialized variable 'defined' to zero. * include/sane/sanei_wire.h: Add missing prototype for sanei_w_ptr() and sanei_w_bool(). * configure.in include/sane/config.h.in: Move definition of HAVE_DEV_URANDOM and SCSIBUFFERSIZE from CFLAGS to config.h. 2001-01-07 Abel Deuring * sanei/sanei_scsi-c: (Linux part) timeout for SCSI commands sent with the SG driver version increased; debug output of device status, host status, driver status and sense buffer added. 2001-01-09 Henning Meier-Geinitz * PROJECTS TODO: Added/updated entries about backends for Mustek BearPaw and 1200 UB scanners. 2001-01-07 Abel Deuring * sanei/sanei_scsi-c: (Linux part) improved detection of sanei_scsi_max_request_size. Setting SANE_SG_BUFFERSIZE for low drivers which don't support scatter-gather should no longer be necessary. * sanei/sanei_scsi-c: (Linux part) Added support for the new devfs in sanei_scsi_find_devices- * backend/sharp.c: Test for existence of shm.h added. * TODO: Removed entry about missing detection of shm.h in sharp backend. 2001-01-03 Henning Meier-Geinitz * PROJECTS: Added Umax 1220P / HP3200C. 2000-12-31 Henning Meier-Geinitz * tools/find-scanner.c: Removed. Now called sane-find-scanner. * tools/sane-find-scanner: New file (was find-scanner). * tools/.cvsignore tools/Makefile.in tools/README: Updated because of name change. sane-find-scanner is installed now. * README README.linux: Updated because of sane-find-scanner. * frontend/scanimage.man: Updated because of sane-find-scanner. * doc/sane-find-scanner.man: New file. * doc/.cvsignore doc/Makefile.in: Added sane-find-scanner(1). * TODO: Updated concerning sane-find-scanner. Added entry about qcam /tmp file handling. Some spelling corrections. 2000-12-31 Henning Meier-Geinitz * backend/mustek.CHANGES backend/mustek.c backend/mustek.desc backend/mustek.h doc/sane-mustek.man: Added calibration for Pro scanners and the SE 12000 SP Plus. Added option to use the full legal size of the Paragon 1200 LS scanner. Added fast preview for Pro scanners. Bug fixes. New version: 1.0-103. 2000-12-25 Karl Heinz Kremer * TODO: Removed entry about net/saned crashes. * backend/net.c: Fixed segfault when using SANE_TYPE_STRING options (e.g. scanimage on localhost). Added checks to prevent other segfaults. Fixed some warnings. * backend/snapscan.desc: Changed to use new homepage. 2000-12-23 Henning Meier-Geinitz * NEWS: Added date of release. * TODO: Removed entries about PATH_MAX, sanei_scsi_find_devices, and shm.h. Added entry about the necessity to check HAVE_SYS_SHM_H. * configure configure.in include/sane/config.h.in: Default to --enable-warnings again. Don't set "-ansi" for OS/2. Test for sys/shm.h. * backend/GUIDE: Moved to doc/backend-writing.txt. * doc/backend-writing.txt: New file (moved from backend/GUIDE). Added comments about sanei-backend.h and PATH_MAX. * include/sane/sanei_backend.h: Added define for PATH_MAX (if necessary). * sanei/sanei_scsi.c: Implemented sanei_scsi_find_devices for FreeBSD (from ports@FreeBSD.org). * tools/Makefile.in: Use INSTALL_SCRIPT instead of INSTALL_PROGRAM for sane-config (from ports@FreeBSD.org). backends-1.3.0/ChangeLogs/ChangeLog-1.0.6000066400000000000000000000602401456256263500175310ustar00rootroot00000000000000****** Release of sane-backends 1.0.6. End of code freeze ****** 2001-11-04 Henning Meier-Geinitz * sane-backends.lsm: Added keyword gphoto. 2001-10-31 Peter Fales * acinclude.m4, aclocal.m4, configure - Fix build failure due to renaming of gphoto2 header files 2001-10-30 Henning Meier-Geinitz * backend/apple.desc: Updated URL. * backend/canon_pp.desc: Fixed two wrong scanner names (from "Matthew Duggan" ). * backend/m3091.desc backend/m3096.desc backend/sp15c.desc: Updated URL. 2001-10-29 Peter Fales * backend/dc25.desc - Update URLs 2001-10-19 Oliver Rauch * set correct backend version number in backend/umax.desc ****** code freeze sane-1.0.6 / sane-pre1-1.0.6 ***** 2001-10-29 Henning Meier-Geinitz * backend/microtek2.desc: Added missing scanner descriptions (from karsten.festag@t-online.de (Karsten Festag)). * NEWS: Updated version numbers. Added release date. * configure configure.in: New version: 1.0.6. Disabled warnings by default. 2001-10-28 Peter Fales * backend/gphoto2.c, backend/gphoto2.conf: the ghoto2 people made some pretty radical changes to the API and broke the gphoto2 backend. Made the changes to allow the gphoto2 backend to work with the latest changes from the gphoto2 library CVS. 2001-10-28 Karl Heinz Kremer * backend/epson.c: Fixed bug in recognition of faulty Perfection 1650 2001-10-28 Henning Meier-Geinitz * TODO: Removed entry about @LIBDIR@ and @CONFIGDIR@ in man pages. Added Entry about sanei_scsi return values and timeout. * backend/microtek2.desc backend/microtek2.h doc/sane-microtek2.man: Increased version number to 0.95. Updated man page. Patch from karsten.festag@t-online.de (Karsten Festag). 2001-10-27 Peter Kirchgessner *backend/hp-accessor.c hp-device.c hp-handle.c hp-hpmem.c hp-option.c hp-scl.c: Use ../include/lassert.h 2001-10-17 Oliver Rauch * umax backend bugfixes (new version number: 1.0 build 31): man page uses @LIBDIR@ and @CONFIGDIR@, calibration fix for Supervista S-12 and compatible scanners directory backend: umax.c umax-scanner.c umax-scsidef.h directory doc: sane-umax.man /umax/sane-umax-advanced-options-doc.html umax/sane-umax-standard-options-doc.html umax/umax.CHANGES 2001-10-27 Oliver Schwartz * backend/Makefile.in backend/snapscan-usb.c backend/snapscan-usb.h backend/snapscan.c backend/snapscan.h Check USB vendor ID to avoid USB hangup Fix dither matrix computation bug ****** snapshot 2001-10-27 ***** 2001-10-26 Oliver Schwartz * backend/snapscan.conf Make /dev/sga the default device to avoid USB hangup 2001-10-26 Henning Meier-Geinitz * TODO: Removed net/saned cancel problem. Added entry about clarifying sane_get_select_fd/sane_cancel in sane.tex. Added entry about sanei_usb and control messages. * sanei/sanei_usb.c: Fixed vendor/device detection for Linux versions >= 2.4.13. 2001-10-25 Oliver Schwartz * NEWS backend/snapscan.c backend/snapscan.desc Change snapscan backend version number to 1.4.0 2001-10-24 Henning Meier-Geinitz * backend/microtek2.c backend/microtek2.desc backend/microtek2.h doc/sane-microtek2.man: Updated microtek2 backend to build 20011023 (from karsten.festag@t-online.de (Karsten Festag)). Bugfix for scsi_read_image_status - supports now the old and the new method. Improved backend-calibration supports brightness and contrast settings and color balancing. Enable 12 bit per color scanning with Scanmaker 9600XL (and other types that use the segreg pixel transfer format and support 12 bit, untested). 10 bit does not work yet. Make C6 USB work. 2001-10-24 Oliver Schwartz * doc/sane-snapscan.man Documentation update. 2001-10-23 Oliver Schwartz * backend/snapscan-scsi.c Limit number of scan lines for quality calibration to fit in SCSI buffer. 2001-10-21 Peter Kirchgessner * backend/hp.desc, doc/sane-hp.man Add support for OfficeJet PSC 900 series 2001-10-20 Karl Heinz Kremer * backend/epson.[ch]: Fix for older Perfection 1650 scanners - their firmware only reports half of the vertical scanning area and they need to swap two color channels. 2001-10-19 Henning Meier-Geinitz * PROJECTS: Added Coolscan2 backend. 2001-10-15 Peter Fales * backend/gphoto2.c, backend/gphoto2.h: Cosmetic cleanup ****** Feature freeze for sane-1.0.6 is active ***** ****** snapshot 2001-10-15 ***** 2001-10-15 Henning Meier-Geinitz * TODO: Removed some entries about net/saned. * doc/saned.man: Clear up that a FQDN must be used in saned.conf and that localhost is always granted access. * NEWS: Updated. 2001-10-14 Jochen Eisinger * backend/mustek_pp.conf: added comment about changes in the naming of ports in 2.4.x 2001-10-13 Oliver Rauch * backend/umax.c, bachend/umax-scanner.c, backend/umax.desc, doc/umax/sane-umax-config-doc.html, doc/umax/umax.CHANGES, doc/sane-umax.man: update to sane-umax-1.0 build 30: - added EDGE KTX-9600US as supported scanner - Changing scansource does not change lower left selection edge if not necessary 2001-10-12 Oliver Schwartz * backend/snapscan-scsi.c backend/snapscan.h backend/snapscan.c Update to snapscan-20011012 2001-10-12 Henning Meier-Geinitz * sanei/sanei_wire.c: Added debug output (based on patch from Jochen Eisinger ). * backend/net.c backend/net.desc: Better and more debug output (based on patch from Jochen Eisinger ). Fixed some debug output and formatting issues of the byte order patch. Increased version number to 1.0.5. * frontend/saned.c: More debug output. Used DBG instead of fprintf/perror. Fixed some formatting issues. 2001-10-10 Jochen Eisinger * sanei/sanei_wire.c: replaced memcpy with memmove 2001-10-10 Jochen Eisinger * PROJECTS: added URL to HP ScanJet 2200c project 2001-10-10 Henning Meier-Geinitz * backend/m3096g-scsi.h backend/m3096g.c backend/m3096g.h doc/sane-fujitsu.man: Update (from Oliver Schirrmeister ). Added: Support for ipc2/3 and cmp2 options; support for duplex-scanners m3093DG, m4097DG; constraint checking for m3093; support EVPD (virtual product data); support ADF paper size spezification. * AUTHORS: Added Oliver Schirrmeister for m3096g, removed the active sign (*) from Randolph Bentson. 2001-10-09 Peter Fales * backend/dc210.c, backend/dc210.h: Fix compiler warnings 2001-10-09 Oliver Schwartz * backend/snapscan-scsi.c backend/snapscan-usb.c backend/snapscan.c Fix compiler warnings 2001-10-09 Peter Fales * backend/gphoto2.desc: Yet another try at getting a format that generates the right web page. 2001-10-09 Henning Meier-Geinitz * backend/ibm.desc: Removed old entry. * backend/sm3600.desc: Changed status to :alpha. * doc/sane-agfafocus.man: Fixed some small mistakes. * doc/sane-apple.man: Fixed some small mistakes. Added link to mostang.com mailing list archive. * doc/sane-artec.man: Fixed spelling mistake and formatting issues. * doc/sane-nec.man: Fixed problem with table. * doc/sane-sharp.man: Fixed problem with table. * backend/sharp.desc: Added link to sane-sharp manual page. * doc/sane_mustek_pp.man: Removed link to sane-musteka4s2 (doesn't exist). * doc/sane-abaton.man doc/sane-agfafocus.man doc/sane-apple.man doc/sane-bh.man doc/sane-microtek.man doc/sane-pint.man doc/sane-st400.man: Added link to mostang.com mailing list page. * backend/net.c backend/net.desc: Fixed byte order problems for 16 bits per color image data (from mh ). * TODO: Removed entries about .desc files, man pages and net.c byte-order problem. 2001-10-09 Oliver Schwartz * backend/snapscan-scsi.c backend/snapscan-sources.c backend/snapscan-sources.h backend/snapscan-usb.c backend/snapscan-usb.h backend/snapscan.c backend/snapscan.conf backend/snapscan.desc backend/snapscan.h Update to snapscan-20011008 2001-10-08 Peter Fales * backend/gphoto2.desc: Another try at getting a format that generates the right web page. 2001-10-08 Henning Meier-Geinitz * backend/ibm.desc: New file. Backend for the IBM 2456 and the Ricoh IS-410. * PROJECTS: Added IBM backend. Removed gphoto2 (now included in distribution). 2001-10-07 Peter Fales * backend/gphoto2.desc: Cleaned up syntax errors doc/sane.man: Added gphoto2 backend to digital camera section AUTHORS: Added gphoto2 backend/author 2001-10-07 Henning Meier-Geinitz * backend/mustek_usb.c backend/mustek_usb.conf backend/mustek_usb.desc backend/mustek_usb_high.c backend/mustek_usb_high.h backend/mustek_usb_low.c backend/mustek_usb_low.h backend/mustek_usb_mid.c backend/mustek_usb_mid.h doc/mustek_usb/mustek_usb.CHANGES: Fixed problem with 1200UB sensor detection. Started work on 1200 USB. New version: 1.0-7. 2001-10-07 Oliver Rauch * created directory "po" added files po/epson.de.po and po/umax.de.po we have to define how we will handle (include+install) translations, in the moments the files are only added to the directory structure without any function 2001-10-06 Henning Meier-Geinitz * backend/canon_pp.desc: New file (from Matthew Duggan . 2001-10-05 Henning Meier-Geinitz * TODO: Removed entries about hp and plustek backends. * backend/microtek2.c backend/microtek2.h doc/sane-microtek2.man: Updated Microtek2 backend to version 0.9 build 20010828 (from Karsten Festag ). * NEWS: Updated. 2001-10-04 Peter Kirchgessner * backend/hp-accessor.c hp-device.c hp-handle.c hp-hpmem.c hp-option.c hp-scl.c Use include "../include/assert.h" doc/sane-hp.man Tell about subscribing to sane-devel 2001-10-03 Henning Meier-Geinitz * README.hp-ux: Lots of new and updated information (from Michael Piotrowski ). 2001-10-02 Henning Meier-Geinitz * backend/dll.c backend/dll.desc: HP/UX uses .sl for shared libs. Changed status to "stable". Increased version number. * tools/sane-find-scanner.c: Check for __hpux instead of __hpux__. 2001-10-01 Henning Meier-Geinitz * TODO: Added possible saned problem with (not) fully qualified domain names. 2001-09-29 Peter Fales * backend/gphoto2.c, backend/gphoto2.h, backend/gphoto2.desc: Cosmetic cleanup and minor fixes. Added list of gphoto2 supported cameras to gphoto2.desc, even though most of them are not tested and probably not working yet. 2001-09-24 Peter Fales * backend/gphoto2.c, backend/gphoto2.h, backend/gphoto2.conf: Cosmetic cleanup and changes to reflect changes to the gphoto2 API. 2001-09-23 Henning Meier-Geinitz * TODO: Added SCSI problem with Linux/Sparc. 2001-09-21 Henning Meier-Geinitz * sanei/sanei_scsi.c: Fixed detection of SCSI devices that return device strings (vendor, product) consisting of spaces. sanei_scsi_req_enter2 was changed to copy the CDB and data before sending it to the device. Both patches are from Abel Deuring. * sanei/sanei_config2.c: Removed some remainings of the USB merge. 2001-09-18 Henning Meier-Geinitz * backend/umax1220u.desc: New file (from Marcio Luis Teixeira ). * PROJECTS: Updated Winsane address. * TODO: Added net/saned and .desc file entries. Removed scanimage/adf extension entry. * backend/pnm.c backend/pnm.desc: Make sure that *length=0 in sane_read(). Added more debug output. 2001-09-18 Stéphane Voltz * backend/umax_pp_low.c backend/umax_pp.desc: corrected compile problem when no ppdev available, changed status from :new to :beta. 2001-09-17 Peter Fales * backend/gphoto2.c: Converted from test code using pipes to the command line program to using the camera API. Currently only supportes cameras that natively generate jpeg files. 2001-09-17 Tom Martone * backend/bh.desc changed status from :new to :beta 2001-09-17 Simon Munton * backend/pie.c backend/pie.conf: Changed ADLIB to AdLib as this is what the scanner returns and the comparison is case sensitive * doc/sane-pie.man: Update the status of the AdLib JetScan 630 2001-09-17 Henning Meier-Geinitz * PROJECTS: Removed (very) old entries. Removed backends which are included in the distribution now. Updated bearpaw and OS/2 entries. * NEWS: Created preliminary entry for 1.0.6. 2001-09-16 Peter Fales * backend/dc240.desc: Change status from new to alpha acinclude.m4, aclocal.m4, configure: Cosmetic changes to help text and change function used in gphoto2 lib check due to API change 2001-09-16 Stéphane Voltz * backend/umax_pp_low.c backend/umax_pp_mid.c tools/umax_pp.c: change ppdev support to compile and run on linux kernel prior to 2.4.5 2001-09-13 Henning Meier-Geinitz * backend/mustek_usb.c backend/mustek_usb.desc backend/mustek_usb_high.c backend/mustek_usb_high.h backend/mustek_usb_mid.c backend/mustek_usb_mid.h doc/sane-mustek_usb.man doc/mustek_usb/mustek_usb.CHANGES doc/mustek_usb/mustek_usb.TODO: Update to mustek_usb backend 1.0-6. Increased resolution to the maximum hardware supports. No fixed resolution any more. Gamma table support. Bugfixes. 2001-09-12 Peter Fales * acinclude.m4 aclocal.m4 configure configure.in: Changes to start using gphoto2 libraries in gphoto2 backend backend/gphoto2.c backend/gphoto2.h: Use gphoto2 headers and library functions for list management 2001-09-12 Peter Fales * backend/doc/.cvsignore: Added gphoto2 man page to .cvsignore 2001-09-11 Peter Fales * backend/doc/Makefile.in: Added man page for gphoto2 backend 2001-09-11 Henning Meier-Geinitz * backend/Makefile.in: Added mustek_usb again. 2001-09-10 Peter Fales * backend/gphoto2.c, backend/Makefile.in, configure.in, configure, acinclude.m4, aclocal.m4: Add gphoto2 backend to Makefiles. It's disable if the gphoto2 program is not present. 2001-09-10 Henning Meier-Geinitz * backend/mustek.c backend/mustek.desc doc/mustek/mustek.CHANGES: Fixed problem with gamma correction. New version: 1.0-113. * backend/mustek_usb.c backend/mustek_usb.desc backend/mustek_usb_high.c backend/mustek_usb_low.c backend/mustek_usb_low.h backend/mustek_usb_mid.c backend/mustek_usb_mid.h doc/mustek_usb/mustek_usb.CHANGES: 200 dpi support for 600 CU. Some internal reorganizations. New version: 1.0-5. 2001-09-09 Henning Meier-Geinitz * backend/mustek_usb.c backend/mustek_usb.conf backend/mustek_usb.desc backend/mustek_usb.h backend/mustek_usb_high.c backend/mustek_usb_high.h backend/mustek_usb_low.c backend/mustek_usb_low.h backend/mustek_usb_mid.c backend/mustek_usb_mid.h: Added backend mustek_usb for Mustek USB scanners. Currently the 600 CU, 1200 CU, 1200 CU Plus and 1200 UB are supported. * backend/Makefile.in dll.conf: Added entries for mustek_usb. * doc/Makefile.in doc/mustek_usb/mustek_usb.CHANGES doc/mustek_usb/mustek_usb.TODO doc/sane-mustek_usb.man: Added mustek_usb documentation. * doc/sane.man: Updated concerning mustek_usb. * PROJECTS TODO: Updated concerning mustek_usb. * doc/.cvsignore: Added sane-mustek_usb.5. * AUTHORS: Added mustek_usb authors. * backend/pnm.c: Fixed SANE-standard compliance bug in sane_read: length must be set to 0 if status != SANE_STATUS_GOOD. 2001-09-08 Oliver Rauch * added information for device filesystem permissions in doc/sane-scsi.man 2001-09-06 Peter Fales * backend/gphoto2.c, backend/gphoto2.h, backend/gphoto2.conf, backend/gphoto2.desc, doc/sane-gphoto2.man: Initial files for gphoto2 backend (interface to cameras supported by the gphoto2 commands/libraries). Makefile changes coming soon. 2001-09-06 Stéphane Voltz * backend/umax_pp_low.c: made CmdSetDataBuffer static * backend/Makefile.in: removed sanei_config2 from umax_pp dependencies 2001-09-05 Jochen Eisinger * backend/net.c: ignoring size of parameter value for SANE_ACTION_SET_AUTO (as described in the SANE standard) 2001-09-05 Henning Meier-Geinitz * sanei/sanei_ab306.c: Added FreeBSD support. * include/sane/sanei_usb.h sanei/sanei_config2.c sanei/sanei_usb.c: Moved the function to check the "usb VENDOR PRODUCT" config lines to sanei_usb to avoid linking problems. * backend/Makefile.in: Fixed spelling mistakes concerning st400. * Makefile.in: make libcheck now also prints the name(s) of the `illegal' functions. 2001-09-04 Henning Meier-Geinitz * PROJECTS: Added m3096g updates and website. Added 600CU to list of supported Mustek USB scanners. 2001-09-04 Jochen Eisinger * backend/net.desc doc/saned.man doc/sane-net.man: added pointer to new saned homepage (http://home.nexgo.de/jochen.eisinger/saned/) 2001-09-04 Peter Kirchgessner * backend/hp-device.[hc] hp-handle.c hp-scl.[hc] hp-option.[hc] hp.[hc] hp.desc doc/sane-hp.man: Add support for active XPA Check if paper in ADF for ADF scan Add option lamp off Remove some really unused parameters 2001-09-04 Peter Fales * backend/PROJECTS: Added gphoto2 project to provide support for some or all of the cameras supported by gphoto2. (See http://www.gphoto.org) 2001-09-03 Stéphane Voltz * backend/umax_pp_low.c backend/umax_pp_mid.c backend/umax_pp_mid.h backend/umax_pp.c tools/umax_pp.c: Make use of ppdev char device when available, so that the backend does not require root privileges anymore. 2001-08-30 Henning Meier-Geinitz * backend/Makefile.in: Added sanei_usb dependency. 2001-08-26 Henning Meier-Geinitz * backend/dll.c doc/sane.man frontend/scanimage.c sanei/sanei_usb.c tools/sane-find-scanner.c : Fixed minor bugs in debug output and documentation. * backend/mustek.c backend/mustek.desc doc/sane-mustek.man doc/mustek/mustek.CHANGES: Update to backend version 1.0-112. Some minor fixes for returning the scan slider and OS/2. 2001-08-26 Oliver Rauch * backend/umax.c backend/umax.desc doc/umax/umax.CHANGES: umax scsi backend update to version 1.0 build 29 2001-08-19 Stéphane Voltz * backend/umax_pp_low.c tools/umax_pp.c: Finished probe code cleaning, increased version number 2001-08-16 Henning Meier-Geinitz * configure configure.in: Fixed bug concerning libusb that broke compilation on NetBSD. * doc/sane-find-scanner.man doc/sane-usb.man sanei/sanei_usb.c tools/sane-find-scanner.c: Added (limited) support for FreeBSD. 2001-08-11 Henning Meier-Geinitz * include/sane/sanei_thread.h sanei/sanei_thread.c: Updated sanei_thread functions (from Yuri Dario) and made clear that they shouldn't be used directly. * include/sane/sanei_usb.h sanei/Makefile.in sanei/sanei_usb.c doc/Makefile.in doc/sane-usb.man: Added sane USB interface. * include/sane/sanei_config.h sanei/sanei_config2.c: Added support for searching USB devices ("usb vendor product"). * doc/sane-find-scanner.man tools/Makefile.in tools/sane-find-scanner.c: Added USB support. * doc/sane.man: Added information for USB scanners. * TODO: Removed USB entries. Removed entry about checking for /etc/ld.so.conf, this shouldn't be necessary any longer. Removed entry about saned freezing, no bug reports about this for a long time. Removed entry about moving .desc files, this doesn't seem to be necessary. Added proposal to include libieeee1284. * doc/.cvsignore: Updated concerning sane-usb.5. 2001-08-05 Peter Fales * backend/dc240.c - Previously, the backend always assumed that the images were stored in the 100DC240 directory in the camera. It now determines the directory automatically. If multiple directories are present, it will eventually let you pick the directory, but that feature hasn't been completed yet. 2001-08-05 Henning Meier-Geinitz * backend/mustek_usb.desc: New file. This backend supports the Mustek 1200UB, 1200CU and 1200CU Plus. * PROJECTS TODO: Updated concerning mustek_usb backend. 2001-08-01 Henning Meier-Geinitz * PROJECTS TODO: Added UMAX Astra 1220U backend. 2001-08-01 Stéphane Voltz * doc/sane-umax_pp.man backend/umax_pp_low.c: One more step toward cleaner scanner probing. Added message about EPP mode not being available. Slight man update to emphasize EPP mode setting. 2001-07-24 Stéphane Voltz * backend/umax_pp.c backend/umax_pp_low.c: Fixed highlight parsing bug in conf file. Fixed 'greenish' looking scans bug. Started probe sequence rewrite/cleanup. 2001-07-18 Henning Meier-Geinitz * sanei/sanei_init_debug.c: Use syslog.h instead of sys/syslog.h. 2001-07-18 Henning Meier-Geinitz * backend/mustek.c backend/mustek.desc doc/mustek/mustek.CHANGES: Call stop_scan only when scan was cancelled. Some more debug messages. New version: 1.0-111. 2001-07-11 Peter Fales * backend/dc240.c - Using scanimage with the "thumbnails" option and attempting to connect to a powered off camera was causing a core dump. 2001-07-11 Henning Meier-Geinitz * backend/mustek.c backend/mustek.desc backend/mustek.h doc/mustek/mustek.CHANGES: Fixed bug in fix_line_distance_block that resulted in too much data transferred to the frontend. New version: 1.0-110. 2001-07-09 Henning Meier-Geinitz * backend/mustek.c backend/mustek.conf backend/mustek.desc backend/mustek.h doc/sane-mustek.man doc/mustek/mustek.CHANGES: Fixed problem with SCSI command queuing. Fixed problem that resulted in black images on some Paragon 6000SP scanners. Added option to disable backtracking. New version: 1.0-109. 2001-07-07 Henning Meier-Geinitz * doc/Makefile.in: Use 6 lines as top/bottom margin for man2html to avoid losing 2 lines at every page break. * backend/mustek.c backend/mustek.conf backend/mustek.desc doc/sane-mustek.man doc/mustek/mustek.CHANGES: Added option for disabling double-buffering. Updated man page. New version: 1.0-108. 2001-07-02 Henning Meier-Geinitz * configure configure.in: Enabled warnings by default. Older entries can be found in ChangeLog-1.0.5. backends-1.3.0/ChangeLogs/ChangeLog-1.0.7000066400000000000000000001055511456256263500175370ustar00rootroot00000000000000****** Release of sane-backends 1.0.7. End of code freeze ****** 2002-02-03 Henning Meier-Geinitz * acinclude.m4 aclocal.m4 configure: Added a missing "test" command in check for gphoto2. This fixes error messages while testing for gphoto2 and a false positive result. * TODO: Added entries about config.h, sane-backends.html, device file types, the HPUX -ansi problem, and make distclean. * backend/Makefile.in backend/coolscan2.desc: Added description file for coolscan2 backend (from Major Andras ). * backend/gphoto2.desc: gphoto2 is no longer "NEW". 2002-02-02 Henning Meier-Geinitz * PROJECTS: Added info about Canon N670U project. Added info about Sceptre S1200 project. * backend/sceptre.desc backend/Makefile.in: New file (from Frank Zago ). * backend/microtek2.desc doc/sane-microtek2.man: Updated microtek2 documentation (from karsten.festag@t-online.de (Karsten Festag)). * Makefile.in README.netbsd: New file. Information about problems with shared libs. * configure configure.in: Include and also when checking for struct semun in . This fixes a compilation problem on FreeBSD. 2002-01-31 Peter Fales * configure, acinclude.m4, aclocal.m4 - Latest gphoto2 updates changed the version number, breaking the gphoto2 backend. This is a one line change to correct that. 2002-01-31 Henning Meier-Geinitz * PROJECTS: Added info about GT-68xx backend. 2002-01-29 Henning Meier-Geinitz * configure configure.in: New version: 1.0.7. Disabled extra version. Disabled warnings by default. * TODO: Added entry about adding well known types and manufacturers to sane.tex. ---- CODE FREEZE FOR SANE 1.0.7 --- ---- sane-backends-1.0.7-beta2 ---- 2002-01-27 Gerhard Jaeger * backend/plustek-usbhw.c: Fixed LM9831 problem... 2002-01-27 Henning Meier-Geinitz * backend/microtek.c backend/microtek2.h: Fixed undefined behaviour problem and Scanmaker X12 problems. New version: 20020127. Code from karsten.festag@t-online.de (Karsten Festag). * lib/inet_ntop.c: Fixed problem with header include order on Irix. * configure configure.in backend/snapscan-usb.c include/sane/config.h.in: Only define union semun if not already defined in . Fixes compilation bugs on Irix and FreeBSD. 2002-01-26 Henning Meier-Geinitz * NEWS: Updated for sane-backends 1.0.7. * TODO: Added point about sane-find-scanner. Added details about NetBSD linking problem. 2002-01-25 Karl Heinz Kremer * backend/epson.desc, doc/sane-epson.man: Added Perfection 1650 and 2450 scanners to supported scanners list. 2002-01-25 Abel Deuring * frontend/scanimage.c: illegal placeholders in the -f format string are now replaced by "%%". (Bug found by Peter Fales) 2002-01-25 Gerhard Jaeger * backend/plustek-usb.h: changed according to avoid some gcc-3 warnings * docb/sane-plustek.man: Update 2002-01-24 Henning Meier-Geinitz * po/Makefile.in: Added SHELL variable. Fixes po generation on OS/2. * po/README: Added some comments on what the Makefile actually does. * doc/backend-writing.txt doc/sane.man: Added note about doxygen configuration for sanei. 2002-01-24 Stéphane Voltz * backend/umax_pp_low.c: corrected 2000P mis-detection by adding pauses in epat wake-up. 2002-01-23 Oliver Schwartz * backend/snapscan.c backend/snapscan-scsi.c backend/snapscan.h Fix recognition of Acer 320U, Fix for spaces in model ID strings, Change snapscan version to 1.4.7 2002-01-23 Henning Meier-Geinitz * backend/mustek.c backend/mustek_usb.c doc/mustek/mustek.CHANGES doc/mustek_usb/mustek_usb.CHANGES: Fixed undefined operation warnings. 2002-01-22 Oliver Schwartz * backend/snapscan.c: Do not use quantization for scan areas * backend/snapscan-scsi.c: Add sense handling for sense code 0x0b to avoid infinite loops, Change snapscan version to 1.4.6 * backend/snapscan.conf Use /dev/sg0 as default instead of the more uncommon /dev/sga ---- sane-backends-1.0.7-beta1 ---- 2002-01-21 Henning Meier-Geinitz * TODO: Made entry about TL_X < BR_X, jpeg->sanei, and sanei_scsi more exact. Added entry about new version of libtool. Removed entry about exit(). * backend/Makefile.in po/Makefile.in: Added missing files to DISTFILES. Fixed incompatibilities with older versions of gmake. 2002-01-21 Oliver Rauch * doc/sane-umax.man, backend/umax.conf: added new umax.conf options and explanation 2002-01-20 Gerhard Jaeger * backend/plustek.c: change the options names to match SANE standard * backend/plustek-usbhw.c backend/plustek-usbio.c backend/plustek-usbscan.c: Minor fixes, according to the National Sources 2002-01-20 Abel Deuring * sanei/sanei_scsi.c: set timeout value for all operating systems to 1 minute. The timeout value is now derived from the macro SANE_SCSICMD_TIMEOUT. 2002-01-20 Henning Meier-Geinitz * backend/mustek.c backend/mustek.desc doc/mustek/mustek.CHANGES: Fixed calibration bug for the 1200 Pro. New version: 1.0-118. * include/sane/sanei_thread.h: Removed */. * backend/tamarack.c: Used return SANE_STATUS_INVAL instead of exit. 2002-01-19 Peter Fales * backend/gphoto2.c - Fixed a bug that can cause driver errors following image capture, and bug that was incorrectly disabling thumbail previews. 2002-01-19 Oliver Rauch * updated umax.de.po 2002-01-19 Henning Meier-Geinitz * TODO: Removed entries about kill(-1,...), wrong .desc links, sanei documentation, added entries about exit(), and sanei doxygen documentation. * backend/mustek.c doc/mustek/mustek.CHANGES: Fixed some option name issues. * doc/sane-mustek-usb.man: Fixed typo. * po/Makefile.in po/README po/epson.de.po po/mustek.de.po po/mustek_usb.de.po po/plustek.de.po po/pnm.de.po po/saneopts.de.po po/umax.de.po: Moved the options contained in saneopts.h into a separate file. So the backend po files are much smaller and easier to read and the saneopts translations are consistent through backends. * backend/microtek2.c backend/microtek2.h: Update to backend 0.95-20020112 (from karsten.festag@t-online.de (Karsten Festag)). * include/sane/sanei.h include/sane/sanei_config.h include/sane/sanei_thread.h sanei/sanei_config.c: Made documentation doxygen-compatible. Move comments from sanei_config.c to sanei_config.h. 2002-01-18 Karl Heinz Kremer * backend/epson.c: Also recognize the GT-xxxx scanners when connected via SCSI or IEEE-1394 interface 2002-01-17 Peter Fales * configure, acinclude.m4, aclocal.m4 - Update gphoto2 version check * backend/gphoto2.c - Improved support for options not supported by camera. Cosmetic cleanup. 2002-01-18 Rene Rebe * docs/sane-avision.man: added the new disable-gamma-table option to the sane-avision man-page. 2002-01-18 Rene Rebe * backend/avision.h, backend/avision.c, backend/avision.conf: many cleanups, fixed sane_cancel to not hang - but perform the cancel, added a disable-gamma-table option and added the possible options into the avision.conf file 2002-01-18 Henning Meier-Geinitz * doc/Makefile.in: Added doxygen-sanei.conf to DIST_FILES. 2002-01-17 Henning Meier-Geinitz * include/sanei/sanei_usb.h: Made documentation doxygen compatible. 2002-01-17 Peter Fales * backend/gphoto2.c, doc/sane-gphoto2.man Remove calls to exit() Support cameras which store all files in top-level directory 2002-01-17 Peter Fales * configure, acinclude.m4, aclocal.m4 - (Really) disable gphoto2 backend unless specifically requested by the user (using --with-gphoto2) and the right CVS version of gphoto2 is found 2002-01-17 Rene Rebe * backend/avision.c removed to call exit in a debug case 2002-01-17 Gerhard Jaeger * backend/plustek.c backend/plustek.h Fixed conditional compilation problem Updated localization strings * doc/sane-plustek.man: Updated * po/plustek.de.po: Updated * doc/doxygen-sanei.conf: Added doxygen configuration file for the sane libs * include/sane/sanei.h include/sane/sanei_ab306.h include/sane/sanei_lm983x.h sanei/sanei_lm983x.c: Updated to support doxygen comments 2002-01-17 Peter Fales * configure.in, configure, acinclude.m4, aclocal.m4 - Disable gphoto2 backend unless specifically requested by the user (using --with-gphoto2) and the right CVS version of gphoto2 is found 2002-01-17 Rene Rebe * backend/avision.h backend/avision.c backend/avision.desc doc/sane-avision.man: Merged the Avision v0.3.0 backend. It includes overall cleanups, improved supported device detection, memory-leak fixes, new config-options, initial USB device and color calibration support. Also updated the homepage-url and the sane-avision man-page. 2002-01-16 Henning Meier-Geinitz * backend/abaton.desc backend/dmc.desc backend/m3096g.desc backend/mustek.c backend/sp15c.desc backend/v4l.desc: Updated broken URLs for which I could find new ones. Commented out all the others. 2002-01-15 Peter Fales * backend/gphoto2.c: Following yet another gphoto2 API change - converted gp_debug_printf to gp_log configure.in, configure, acinclude.m4, aclocal.m4 - Improvements to the way gphoto2 is detected and used based on using gphoto2-config. 2002-01-15 Henning Meier-Geinitz * backend/pnm.c backend/pnm.desc po/pnm.de.po: Removed descripions of options of type SANE_TYPE_GROUP. Fixed some translations. Fixed header. New version: 1.0.4. * backend/plustek.c backend/plustek.h po/Makefile.in po/plusetk.de.po: Added German translation of Plustek backend options. Marked translatable text in backend sources. 2002-01-14 Oliver Schwartz * backend/snapscan.c backend/snapscan-usb.c: Added workaround for bug in semctl() on PPC; backend version 1.4.5 2002-01-14 Peter Fales * backend/gphoto2.c: Was requiring the device to have the IMAGE_CAPTURE capability - changed this to a warning instead of an error so the "Directory Browse" camera can be used. Don't require a port to be specified for the "Directory Browse" camera. Fixed a bug that can result in extra data at the end of the image. Allow the returned image image to be larger than the "estimate" specified in the configuration file. 2002-01-14 Henning Meier-Geinitz * frontend/scanimage.c: Added help message for --formatted-device-list. Added va_end() and #include . Used fprintf to print errors. Fixed some typos. 2002-01-13 Abel Deuring * frontend/scanimage.c, doc/scaniamge.man: Added the option -f / --formatted-device-list to allow arbirtarily formatted device lists. Suggested by Klaas Freitag. 2002-01-13 Henning Meier-Geinitz * acinclude.m4 aclocal.m4 configure: Included AC_PROG_LIBTOOL into acinclude.m4 to avoid trouble with newer versions of libtool. * backend/niash.desc: New file for the niash backend (from Bertrik Sikken ). 2002-01-12 Peter Fales * backend/gphoto2.c - Fixed breakage due to gphoto2 API changes (Added context parameter to functions calls) 2002-01-12 Gerhard Jaeger * doc/sane-plustek.man: Update * backend/plustek-devs.c backend/plustek-usb.c backend/plustek-usbhw.c backend/plustek-usbio.c backend/plustek-usbscan.c backend/plustek-usbshading.c backend/plustek.c: Updated to reflect the current feedback upon the code and added some minor fixes * backend/plustek.desc: Added Mustek and HP entry 2002-01-12 Gerhard Jaeger * include/sane/sanei_lm983x.h sanei/sanei_lm983x.c: added sanei_lm983x_init(), updated comments and fixed the u_long * AUTHORS: Added myself to the list of sanei authors. 2002-01-12 Henning Meier-Geinitz * doc/sane.man doc/scanimage.man: Added link to scanadf. 2002-01-11 Henning Meier-Geinitz * backend/qcam.c: Create lockfile with O_EXCL even when using fcntl locking. Remove it in any case after exiting. Log file handling is still buggy but this has to be done by someone who has a qcam. * sanei/sanei_lm983x.c: Added #include (OS/2 complains otherwise). * backend/pnm.c backend/pnm.desc po/pnm.de.po: hand_scanner and three_pass options need SANE_INFO_RELOAD_PARAMETERS. All options have a symbolic name now. Removed bogus translation from po. New version: 1.0.3. * frontend/saned.c: Check that the IP addresses of the peer are the same for control and data connections. Exit, if this is not the case. Changed some debug levels. * PROJECTS: Updated some URLs. Removed m3096g and Mustek BearPaw (both are included now in SANE). Added test backend and SaneTwain. * TODO: Removed primax (not a backend), fixed viceo URL, updated qcam entries, added entries about ipv6 support, removed saned address validation entry. Added entry about scanimage + NLS. * backend/mustek_usb.c backend/mustek_usb.desc backend/mustek_usb_high.c backend/mustek_usb_high.h backend/mustek_usb_mid.c backend/mustek_usb_mid.h doc/mustek_usb/mustek_usb.CHANGES po/mustek_usb.de.po: Removed some warnings. Fixed segfault while exiting. Using empty devicename works now. New version: 1.0-10. 2002-01-10 Tom Martone * backend/bh.c addressed security issue with tempfile 2002-01-10 Oliver Schwartz * backend/snapscan.c backend/snapscan.h backend/snapscan.desc: Update to snapscan-20020110 (Version 1.4.4) - add support for SnapScan e42 2002-01-10 Gerhard Jaeger * include/sane/sanei_lm983x.h sanei/sanei_lm983x.c sanei/Makefile.in: Added to support for the National Semiconductor LM9831/2/3 chipsets (read/write functions) * backend/plustek.desc: Updated, added missing, now supported devices * backend/plustek.c backend/plustek-share.h backend/plustek.h backend/plustek.conf backend/plustek-usb.c backend/plustek-devs.c backend/plustek-pp.c backend/plustek-usbhw.c backend/plustek-usbimg.c backend/plustek-usbio.c backend/plustek-usbmap.c backend/plustek-usbscan.c backend/plustek-usbshading.c backend/plustek-usb.h: Major changes to use the new sanei_lm983x functions Changed the configuration file to reflect all the possible config-options * backend/Makefile.in: Changed to compile the Plustek backend 2002-01-10 Henning Meier-Geinitz * po/pnm.de.po: Added German translation for pnm backend. * po/mustek.de.po po/mustek_usb.de.po: Updated German translations. They should be complete now. * po/Makefile.in README: Made Makefile more intelligent. Targets are only updated if necessary. No manual copying necessary. Added pnm.de.po to DISTFILES. Added comment about DISTFILES. * backend/mustek_usb.desc: New status: beta. * README: Added description of --enable-translations. * doc/sane.man: Added comment about translations. * Makefile.in: Added Changelogs to DISTFILES. test/Makefile is removed in make distclean now. * doc/backend-writing.txt: Added paragraph about getting started. Added comments about ANSI C, compilation warnings, testing and keeping manpages up-to-date. * backend/pnm.c: Added SANE_I18N() marks. * TODO: Cleanup. Moved several entries to doc/backend-writing.txt. Moved some entries to more appropriate paragraphs. Added entry about missing or defective links in the .desc files. Removed entry about translations. * lib/inet_ntop.c: Changed order of includes for OS/2 compatibility. * frontend/scanimage.c: Added output of bytes scanned in total. Added warning that's printed if scanimage gets more data then expected from the parameters. * include/Makefile.in: Added sanei_lm983x.h to DISTFILES. * backend/qcam.c: Actually close the lock file when unlocking. 2002-01-09 Henning Meier-Geinitz * ChangeLog ChangeLog-1.0.0 ChangeLog-1.0.1 ChangeLog-1.0.2 ChangeLog-1.0.3 ChangeLog-1.0.4 ChangeLog-1.0.5: Split ChangeLog to limit filesize. Editing became slow on smaller systems. 2002-01-08 Marcio Teixeira * backend/umax1220u.c: fixed compile warnings * backend/umax1220u-common.c: fixed compile warnings * backend/umax1220u.desc: added info about 2000U, 2200U, 2200US 2002-01-08 Henning Meier-Geinitz * configure configure.in: Added V_EXTRA version number to mark CVS versions (-cvs) and snapshots/betas (e.g. -beta1). V_EXTRA=-pre1 would result in "sane-backends-1.0.7-pre1(.tar.gz)". For release, V_EXTRA is empty. Added NUMBER_VERSION to avoid confusing sane-config and frontends that rely on numbered versions. * tools/sane-config.in: Use @NUMBER_VERSION@. * sane-backends.lsm: Made more compatible with LSM standard. Fixed mostang.com directory. Added OpenBSD. * include/Makefile.in: Added target "dist". Moved include targets from root dir to this file. Added standard variables and .PHONY. * backend/Makefile.in doc/Makefile.in frontend/Makefile.in japi/Makefile.in lib/Makefile.in po/Makefile.in sanei/Makefile.in testsuite/Makefile.in tools/Makefile.in: Added target "dist". Fixed .PHONY. * Makefile.in: Added targets "dist" and "sane-backends". make dist creates sane-backends-x.y.z.tar.gz and make sane-backends also creates the appropriate sane-x-y-z.lsm. Moved distclean to include/. Fixed .PHONY. Made sane-backands-*.lsm look nicer. * doc/backend-writing.txt: Point to po/README for the I18N details. * doc/releases.txt: Added "make sane-backends" procedure. * po/Makefile.in: make now creates all the files, make install does nothing but installation. * README: Updated. Fixed minor bugs. * AUTHORS: Added sanei authors. * doc/saned.man: Added info about data connection. * po/mustek.de.po: Updated German translation for the Mustek SCSI backend. * frontend/.gdbinit: Removed unnecessary file. 2002-01-08 Stéphane Voltz * configure.in: added help text for --enable-parport-directio 2002-01-07 Henning Meier-Geinitz * PROJECTS: Added link to backend for HP Scanjet 3300C / 3400C and 4300C scanners. Removed umax1220u project (now included in sane-backends). * po/Makefile.in po/README: Added basic support for translating option descriptions and titles of SANE backends. This is really alpha! * po/epson.de.po po/umax.de.po: Updated by make update-po. * po/mustek.de.po mustek_usb.de.po: Added German translations for Mustek SCSI and USB backends. Translations for well-known options are missing. * Makefile.in configure configure.in: Added option --enable-translations to configure. I18N support is disabled by default. * po/.cvsignore: New file. * doc/sane-mustek_usb.man doc/mustek_usb/mustek_usb.CHANGES: Added info about BearPaw scanners and plustek backend. * backend/mustek.c backend/mustek.desc doc/sane-mustek.man doc/mustek/mustek.CHANGES: Small fix for Paragon 1200 Pro. Added some test code for this scanner. Added Trust Imagery 1200 to man page and .desc. New version: 1.0-117. * backend/Makefile.in backend/dll.conf backend/umax1220u-common.c backend/umax1220u.c backend/umax1220u.conf backend/umax1220u.desc: Added new backend umax1220u for UMAX Astra 1220U and 2000U scanners (patch from Marcio Luis Teixeira ). * doc/Makefile.in doc/sane.man doc/sane-umax1220u.man: Added documentation for umax1220u backend (patch from from Marcio Luis Teixeira ). * AUTHORS: Added Marcio Luis Teixeira. * TODO: Removed umax1220u project (now included in sane-backends). Added HP Scanjet 3300C / 3400C and 4300C project. Added entry about saned data port documentation. Removed saned race condition bug. * doc/.cvsignore: Added sane-umax1220u.5. 2002-01-06 Henning Meier-Geinitz * configure configure.in include/sane/config.h.in: Added check for inet_ntop(). * lib/inet_ntop.c lib/Makefile.in backend/Makefile.in: Added implementation of inet_ntop() for platforms without this function based on inet_ntoa. * backend/saned.conf frontend/saned.c doc/saned.man: Changed access control (function check host). Now IP addresses are used for comparison, not host names as before. It's possible to put hostnames, FQDNs and IP addresses into saned.conf. Limitations: Only the first IP address in struct hostent is checked. IPv6 is untested. The change is based on a patch by Petter Reinholdtsen . Return SANE_STATUS_ACCESS_DENIED if access was not granted instead of SANE_STATUS_IO_ERROR. Removed isfdtype to avoid compilation problems. Added more debug output. Print function name in every DBG statement. Updated manual pages and saned.conf. * net.c: Return SANE_STATUS_ACCESS_DENIED if access was not granted instead of SANE_STATUS_IO_ERROR. 2001-01-06 Karl Heinz Kremer * backend/epson.c: Undefine TEST_IOCTL again, which was enabled by accident. 2002-01-06 Stéphane Voltz * backend/umax_pp_low.c: improved warm up and color calibration of much better quality now. Manual settings now work correctly. Corrected parport mode being set before being claimed. * backend/umax_pp.c: changed build number. * tools/umax_pp.c: changed version. 2002-01-05 Oliver Rauch * backend/umax-scanner.c, umax.c, umax.conf, umax.desc, umax.h: update to backend version 1.0 build 32 2002-01-05 Karl Heinz Kremer * backend/epson.[ch]: Check for (and set) s->fd to -1 when scanner is closed. Removed black gamma table - only RGB is used, even for grayscale scans. Do not call access() when running on OS/2 or when using a parallel port scanner. 2002-01-05 Henning Meier-Geinitz * backend/net.c backend/net.desc: Fixed race condition between saned sending NET_OPEN reply and net.c flushing the wire. New version: 1.0.6. * doc/saned.man frontend/saned.c: Changed debug handling. All debug messages use DBG now. If in inetd mode or daemon mode with "-s", the messages are printed to syslog as before. If option "-d" is used, the messages are printed to stderr. Updated manual page. 2002-01-04 Henning Meier-Geinitz * sanei/sanei_wire.c: Added DBG message that warns if the read buffer is going to be deleted (data loss). 2002-01-03 Henning Meier-Geinitz * README.os2: Updated URL of Franz Bakan's web site. * include/sane/sanei_usb.h sanei/sanei_usb.c: Added support for USB control messages (patch from Marcio Luis Teixeira ). * doc/sane-usb.man: Updated concerning backends supporting sanei_usb and OpenBSD. Other minor fixes. * include/sane/sanei_pv8630.h sanei/sanei_pv8630.h sanei/Makefile.in: Added support for the PowerVision 8630 chip, a USB to parallel converter used in many scanners (patch from from Marcio Luis Teixeira ). Fixed sanei_thread Makefile.in entry. * TODO: Added entries about saned/net freezes and parport-directio doc. Removed entries about sanei_usb control msg, sanei_pv8630 inclusion, OS/2 libsocket problem, and splitting --enable-directio. 2001-12-30 Henning Meier-Geinitz * frontend/scanimage.c: Fixed endless loop in sighandler. * frontend/saned.c: Fixed sigpipe race in connection with select_fd. Added debug output. 2001-12-30 Gerhard Jaeger * backend/plustek_usbmap.c backend/plustek-usbimg.c backend/plustek_usb.c backend/plustek_usb.h: Fixed bug in lineart scaling and in brightness setting 2001-12-29 Gerhard Jaeger * backend/plustek.c backend/plustek-share.h backend/plustek.h backend/plustek.conf: Changed to support the LM983x based USB scanner * backend/plustek.desc: Updated, added missing, now supported devices * backend/plustek-usb.c backend/plustek-devs.c backend/plustek-pp.c backend/plustek-usbhw.c backend/plustek-usbimg.c backend/plustek-usbio.c backend/plustek-usbmap.c backend/plustek-usbscan.c backend/plustek-usbshading.c backend/plustek-usb.h: New files - added to support the Plustek USB devices (LM9831/2 based) and other devices based on this chipset * backend/Makefile.in: Changed to compile the Plustek backend * doc/sane-plustek.man: Added some USB information 2001-12-28 Henning Meier-Geinitz * configure configure.in: Changed order of tests for libsocket and libsyslog to avoid problems with OS/2. Small spelling and format fixes. 2001-12-28 Abel Deuring * configure.in, configure, sanei/sanei_scsi.c, umax_pp_low.c, README.linux: replaced --enable-directio and ENABLE_DIRECTIO with --enable-scsi-directio / --enable-parport-directio resp. ENABLE_SCSI_DIRECTIO / ENABLE_PARPORT_DIRECTIO 2001-12-27 Henning Meier-Geinitz * PROJECTS TODO: Updated info about "Relisys Scorpio Super 3 SCSI II". 2001-12-25 Henning Meier-Geinitz * README.linux: Used "sane-find-scanner". Some minor changes. * config.guess config.sub: Updated from http://savannah.gnu.org/projects/config. * TODO: New section: "platform-specific". Added some problems with *BSD. Some minor fixes and additions. * tools/sane-find-scanner.c: Added OpenBSD device files. * README.openbsd: New file. Some information on how to get SANE running on top of OpenBSD. 2001-12-22 Henning Meier-Geinitz * backend/mustek_usb.c backend/mustek_usb.conf backend/mustek_usb.desc backend/mustek_usb_low.c backend/mustek_usb_low.h doc/sane-mustek_usb.man doc/mustek_usb/mustek_usb.CHANGES: Made usb_low_read_rows more robust. Limited maximum block size and added option for this. New version: 1.0-9. 2001-12-21 Oliver Schwartz * backend/snapscan.c backend/snapscan.h Remove tmpfname var 2001-12-20 Peter Fales * backend/dc25.c - Fixed a potential security problem due to a race condition involving temporary file creation. Also did some cosmetic cleanup and removed some un-needed code. 2001-12-19 Henning Meier-Geinitz * backend/dll.conf: Added (commented out) st400 entry. 2001-12-18 Oliver Schwartz * backend/snapscan.c backend/snapscan.h Remove temporary file 2001-12-18 Ingo Wilken * backend/st400.c backend/st400.conf Security fix: Dump inquiry data to $HOME/st400.dump instead of /tmp/st400.dump. 2001-12-17 Oliver Schwartz * backend/snapscan-scsi.c backend/snapscan.h backend/snapscan-usb.h backend/snapscan-usb.c backend/snapscan-sources.h backend/snapscan-sources.c backend/snapscan.c backend/snapscan.desc Update to snapscan-20011212 (snapscan backend 1.4.3): - Use sense handler for USB scanners - Correct color alignment for Snapscan 600 - Fix dither matrix computation - Add support for Snapscan e26 and e52 - Guard for TL_X < BR_X and TL_Y < BR_Y 2001-12-16 Peter Kirchgessner * frontend/stiff.c Add fillorder tag for b/w-TIFF-files Include stiff.h to avoid warning "no previous prototype" 2001-12-15 Henning Meier-Geinitz * backend/mustek.c backend/mustek.desc doc/mustek/mustek.CHANGES: Fixed a possible segfault in sane_control_option. Added more debug output for SCSI commands. New version: 1.0-116. 2001-12-01 Henning Meier-Geinitz * sanei/sanei_ab306: Removed warning: `dev_io_fd' defined but not used. Variable used only by FreeBSD. Added test for FreeBSD. Patch from Stephen Torri . 2001-11-30 Henning Meier-Geinitz * testsuite/Makefile.in testsuite/README: Added comment about the need to enable pnm. Made output easier to read. 2001-11-29 Henning Meier-Geinitz * backend/mustek.c backend/mustek.desc doc/mustek/mustek.CHANGES: Added check for TL_X > BR_X or TL_Y > BR_Y. New version: 1.0-115. * TODO: Added entry about TL_X < BR_X problems and semaphores for OS/2. Removed entry about BearPaw scanners. 2001-11-26 Abel Deuring * backend/sharp.c: added an additional check to prevent OS/2 from define "#define USE_FORK" 2001-11-24 Abel Deuring * backend/sharp.c: removed an unconditional "#define USE_FORK" 2001-11-22 Stéphane Voltz * backend/umax_pp_low.c: fixed color calibration bug. Scans are of much better quality now. Manual settings now work correctly. * tools/umax_pp.c: changed version. 2001-11-22 Henning Meier-Geinitz * config.guess config.sub: Fetched new versions from http://savannah.gnu.org/projects/config. * mkinstalldirs: Updated for OS/2 (from "Franz Bakan" ). * backend/mustek.c backend/mustek.desc backend/mustek.h doc/mustek/mustek.CHANGES: Added OS/2 compatibility fixes (from "Franz Bakan" ). New version: 1.0-114. * sanei/sanei_scsi.c: OS/2 changes: Removed semaphore code and increased MAX_DATA to 64k (from "Franz Bakan" ). * tools/sane-config.in: Also return -ldl (necessary for IA64?). * TODO: Added entry about .exe and similar file extensions. Removed sane-config.in -ldl issue. 2001-11-22 Stéphane Voltz * backend/umax_pp_low.c: fixed build problem on non i386 linux when there is no . 2001-11-21 Henning Meier-Geinitz * TODO: Added entries about the fork/kill(-1,...) issue, the inb/outb problems, config.guess and sane-config.in. * sanei/Makefile.in: Added sanei_thread for OS/2 portability. 2001-11-20 Henning Meier-Geinitz * doc/scanimage.man frontend/scanimage.c: Use out%d.tif by default if --format tiff was selected. Update documentation about default formats. Fixed spelling mistakes. * AUTHORS: I think this was planned in alphabetical order... 2001-11-18 Karl Heinz Kremer * backend/epson.c: Fixed Wait for Button functionality. Don't call access() for par port scanners. 2001-11-18 Karl Heinz Kremer * backend/epson.h: Added missing defines for USB IOCTLs 2001-11-18 Henning Meier-Geinitz * backend/mustek.desc doc/sane-mustek.man doc/mustek/mustek.CHANGES: Added enries for Primax Compact 4800. 2001-11-15 Henning Meier-Geinitz * backend/mustek_usb.c backend/mustek_usb.conf backend/mustek_usb.desc backend/mustek_usb.h backend/mustek_usb_high.c backend/mustek_usb_high.h backend/mustek_usb_low.c backend/mustek_usb_low.h backend/mustek_usb_mid.c backend/mustek_usb_mid.h doc/sane-mustek_usb.man doc/mustek_usb/mustek_usb.CHANGES doc/mustek_usb/mustek_usb.TODO: FreeBSD is supported to some degree now. Started 1200 USB support. Documentation updates. New version: 1.0-8. 2001-11-12 Peter Fales * gphoto2.c - Debug message was not getting printed because DBG_INIT was not called first. 2001-11-12 Henning Meier-Geinitz * doc/sane-scsi.man: More details and examples for the "scsi * ..." directive for config files. Added information about debug level 255 printing (Linux) kernel debug messages. Added link to sane-usb(5). 2001-11-10 Stéphane Voltz * backend/umax_pp_low.c: correct PPC compile problem by removing buggy extra ifdef 2001-11-11 Peter Fales * gphoto2.c - Number of images was not getting updated after capturing a new image 2001-11-11 Henning Meier-Geinitz * backend/dll.c backend/dll.desc doc/sane-dll.man: Added some debug messages (e.g. if dll.conf isn't found). Some adjustments concerning debug levels. Removed some compilation warnings. New version: 1.0.5. 2001-11-11 Abel Deuring * sanei/sanei_scsi.c (Linux part): - improved handling of status codes returned by the SG driver. Device status INTERMEDIATE GOOD and CONDITION MET are now mapped to SANE_STATUS_GOOD; device status BUSY and a few driver status codes are mapped to SANE_STATUS_DEVICE_BUSY - On SCSI command completion, the (sometimes bogus) residual count is no longer subtracted from *req->dstlen * backend/sharp.c: if a READ command fails with SANE_STATUS_DEVICE_BUSY, retry a few times. 2001-11-10 Karl Heinz Kremer * backends/epson.[ch]: all strings tagged with I18N macro Added support for scan button, scanner waits for button if the option "wait-for-button" is activated. 2001-11-10 Stéphane Voltz * backend/umax_pp_low.c backend/umax_pp_low.h backend/umax_pp_mid.c backend/umax_pp_mid.h backend/umax_pp.c backend/umax_pp.h: use ENABLE_DIRECTIO to compile in direct I/O. Corrected the long standing scan area origin detection bug. Improved parport mode negotiation. 2001-11-07 Henning Meier-Geinitz * sanei/sanei_usb.c: Fixed debug messages. 2001-11-06 Peter Fales * dc240.c - fix a bug that causes the backend's internal directory to get out of sync with the camera directory when a file is deleted * gphoto2.c - include the gphoto2 port name as part of the SANE device name and improve reliability when using the gphoto2 serial port libs. 2001-11-04 Henning Meier-Geinitz * configure configure.in: Enabled warnings by default. * sane-backends.lsm: Made more compatible with template. Added myself as maintainer to avoid problems when uploading to tsx-11 and sunsite in future. Older entries can be found in ChangeLog-1.0.6. backends-1.3.0/ChangeLogs/ChangeLog-1.0.8000066400000000000000000001235111456256263500175340ustar00rootroot00000000000000****** Release of sane-backends 1.0.8. End of code freeze ****** 2002-05-27 Henning Meier-Geinitz * backend/plustek-usbscan.c: Critical bugfix to avoid bumping the scan slider at the end of the scan. Committed on behalf of g-jaeger@t-online.de (G. Jaeger). 2002-05-26 Karl Heinz Kremer * doc/descriptions/epson.desc doc/sane-epson.man backend/epson.conf: Updated man page, added one more comment to the conf file and adjusted the version in the desc file. 2002-05-26 Frank Zago * doc/descriptions-external/teco2.desc doc/descriptions-external/teco3.desc: new backends descriptions. * AUTHORS: changed my email address * doc/descriptions/teco1.desc: added the missing connection type for the vm3520 2002-05-26 Henning Meier-Geinitz * backend/Makefile.in: Added workaround for GNU make 3.79. This version of make insisted on at least one argument for "basename" which broke compilation. * doc/descriptions/leo.desc doc/descriptions/teco1.desc: Changed status to ":new" for release. * doc/descriptions/umax1220u.desc: Changed status from :new to :alpha as the baceknd was already in SANE 1.0.7. 2002-05-25 Andras Major * doc/descriptions/coolscan2.desc: changed status to :new. 2002-05-25 Matthew Duggan * doc/descriptions/canon_pp.desc: Changed status to :new. 2002-05-25 Henning Meier-Geinitz * AUTHORS backend/microtek2.c doc/sane-microtek2.man doc/descriptions/microtek2.desc: Updated Karsten Festag's email address and website. * NEWS: Added OPENSTEP to the list of portability fixes. * doc/descriptions/test.desc: Changed status to :new. * configure configure.in: Set version to 1.0.8. Disabled warnings by default. 2002-05-23 Oliver Rauch * doc/umax/sane-umax-powerlook-doc.html * doc/descriptions/umax.desc update ---- CODE FREEZE FOR SANE 1.0.8 --- --- snapshot 1.0.8-pre1 2002-05-22 Henning Meier-Geinitz * sanei/sanei_scsi.c: Fixed sanei_scsi_cmd2() for OPENSTEP (from Oliver Schirrmeister ). 2002-05-21 Petter Reinholdtsen * include/Makefile.in (install): Make it possible to install when building in a subdirectory. 2002-05-14 Oliver Schwartz * backend/snapscan.conf: Added additional USB IDs for Acer 320U and Acer 620U 2002-05-09 Henning Meier-Geinitz * configure configure.in frontend/scanimage.c include/sane/config.h.in: Fixed 16 bit pnm output. The byte-order was wrong for little-endian systems. Based on code from Roland Roberts . * NEWS: Minor updates. * TODO: Removed entry about 16 bit scanimage problem. Added entry about scanimage and width/height ranges. 2002-05-07 Frank Zago * doc/descriptions-external/tevion9693usb.desc: new backend, from mh . 2002-05-06 Andras Major * doc/sane.man: coolscan2 entry updated. * doc/sane-usb.man: added coolscan2 to sanei_usb users' list. 2002-05-05 Frank Zago * backend/umax-usb.c backend/teco1.c backend/matsushita.c backend/matsushita.h backend/leo.c backend/sceptre.c: minor fixes. 2002-05-05 Andras Major * backend/coolscan2.c: version number replaces "CVS" * doc/coolscan2.man doc/descriptions/coolscan2.desc: added man page for coolscan2. 2002-05-05 Henning Meier-Geinitz * backend/test.c doc/descriptions/test.desc: Check return value of waitpid. Don't evaluate status of children if waitpid wasn't successful. * backend/mustek.c doc/descriptions/mustek.desc: Check return value of waitpid. Don't evaluate status of children if waitpid wasn't successful. * TODO doc/backend-writing.txt: Add an entry about the return value of wait/waitpid. ---- FEATURE FREEZE FOR SANE 1.0.8 --- 2002-05-02 Oliver Schwartz * doc/description/snapscan.desc: Fix URL 2002-05-02 Oliver Schwartz * backend/snapscan.c backend/snapscan-options.c backend/snapscan-scsi.c backend/snapscan-usb.c po/snapscan.de.po: Snapscan backend version 1.4.13 - Support for ADF - Fixed status handling after cancel * doc/description/snapscan.desc: - Add Guillemot Scan@home 1248 USB - Fix vendor URLs 2002-04-21 Oliver Rauch * sane-umax: updated umax.CHANGES 2002-04-30 Jochen Eisinger * doc/saned.man doc/sane-net.man doc/descriptions/net.desc: Updated URL of the sane-net homepage to http://www.penguin-breeder.org/?page=sane-net * doc/sane-mustek_pp.man doc/descriptions/mustek_pp.desc: Updated URL of the mustek_pp homepage to http://www.penguin-breeder.org/?page=mustek_pp * TODO: Added entry to saned section: - Add support for IP ranges in saned.conf (like 10.0.0.0/8) 2002-04-28 Frank Zago * tools/Makefile.in tools/sane-find-scanner.c: bug fixes, better error reporting and display the inquiry in verbose mode. 2002-04-29 Kazuya Fukuda * backend/nec.c: fixed a compile problem for Dec Unix v4 and probably other 64 bit platform. * AUTHORS: Update email address for Kazuya Fukuda 2002-04-27 Oliver Schwartz * backend/snapscan.c backend/snapscan.h backend/snapscan-options.c backend/snapscan-scsi.c backend/snapscan-usb.c po/snapscan.de.po: Snapscan backend version 1.4.12 - Removed SCSI debug options - Fixed option handling (errors found by tstbackend) 2002-04-27 Henning Meier-Geinitz * frontend/scanimage.c: Fixed scanimage SANE_CAP_AUTOMATIC bug (from David Paschal ). 2002-04-26 Peter Fales * backend/dc240.c backend/gphoto2.c: Various minor bug fixes for problems found by tstbackend. Fix a core dump when debugging is enabled. 2002-04-26 Jochen Eisinger * backend/mustek_pp.c: fixed a typo, thanks to Henning for pointing this one out to me 2002-04-26 Andras Major * backend/coolscan2.c doc/descriptions/coolscan2.desc: update to release 0.1.5, various saned-related problems fixed. 2002-04-25 Henning Meier-Geinitz * TODO: Updated backend list. Added entry for sanei_scsi/Mac OS X. 2002-04-24 Henning Meier-Geinitz * backend/mustek.c backend/mustek.h doc/descriptions/mustek.desc doc/mustek/mustek.CHANGES: Print useful DBG messages for options without a name. Undef MIN and MAX macros before defining them. * backend/mustek_usb.c backend/mustek_usb_low.h doc/descriptions/mustek_usb.desc doc/mustek_usb/mustek_usb.CHANGES: Free devlist on exit. Undef MIN and MAX macros before defining them. * backend/Makefile.in frontend/Makefile.in: Added missing files to DISTFILES. * config.guess: Updated from ftp.gnu.org. 2002-04-24 Oliver Schwartz * backend/snapscan.c backend/snapscan.h backend/snapscan-options.c backend/snapscan-scsi.c: Snapscan backend version 1.4.11 - Improve scan area option setting - Cleanup of DBG messages - Improve config file reading ---- BACKEND FREEZE FOR SANE 1.0.8 --- 2002-04-23 Frank Zago * backend/coolscan2.c: Fixed the version reporting problem. * backend/dll.conf: added coolscan2 entry. 2002-04-23 Peter Fales * backend/dc240.c backend/gphoto2.c: Various minor bug fixes for problems found by tstbackend * AUTHORS: Update email address for Peter Fales 2002-04-22 Frank Zago * AUTHORS PROJECTS backend/Makefile.in backend/coolscan2.c backend/coolscan2.conf doc/descriptions/coolscan2.conf doc/sane.man: added coolscan2 backend 2002-04-22 Abel Deuring * backend/sharp.c: fixed a compile problem for Dec Unix v4 and probably other 64 bit platform. 2002-04-22 Henning Meier-Geinitz * doc/backend-writing.txt: More details for the exported symbols issue. Used test.c instead of pnm.c as example. Fixed paths for .desc files. * doc/sane.man: Added paragraph about testing with the test backend. * TODO: Removed several avision bug entries. * NEWS: First version of entry for 1.0.8. 2002-04-21 Frank Zago * backend/teco1.c doc/descriptions/teco1.desc: updates. 2002-04-21 Frank Zago * backend/sceptre.c doc/descriptions/sceptre.desc: updates. 2002-04-21 Frank Zago * AUTHORS PROJECT backend/Makefile.in backend/leo.c backend/leo.h backend/leo.conf doc/sane.man doc/sane-leo.man doc/descriptions/leo.desc doc/Makefile.in: added leo backend 2002-04-22 Rene Rebe * backend/avision.c next backend version, several new scsi id's, and bug fixes 2002-04-22 Karl Heinz Kremer * backend/epson.c: Declare close_scanner() and open_scanner() before they are used 2002-04-21 Oliver Rauch * sane-umax backend update to version 1.0 build 34 2002-04-21 Henning Meier-Geinitz * backend/test.c doc/descriptions/test.desc: Check if sane_init was called before any other SANE function. * backend/dll.c doc/descriptions/dll.desc: Don't call sane_exit twice. Call sane_init after sane_exit. Try to load from $LD_LIBRARY_PATH ($SHLIB_PATH, $LIBPATH) first and only check LIBDIR if opening failed. New version: 1.0.6. * backend/v4l.c: Fixed some warnings. * backend/artec.c: Don't export cap_data. * backend/canon.h: Don't export option_name. * TODO: Updated entries about compilation warnings for various backends. Removed entries about canon update and dll sane_exit problems. * PROJECTS: Removed canon update. 2002-04-21 Abel Deuring * backend/sharp.c: added a "free(devlist)" call to sane_exit 2002-04-21 Abel Deuring * backend/canon-sane.c, backend/canon-scsi.c, backend/canon.c, backend/canon.h, doc/sane-canon.man, doc/descriptions/canon.desc: added support for FB620S and and FS2700, by Mitsuru Okaniwa and Ulrich Deiters 2002-04-19 Frank Zago * AUTHORS: fixed typos, formatting and added the tstbackend frontend. * frontend/Makefile.in frontend/tstbackend.c: a frontend to test backends * doc/backend-writing.txt: added info about tstbackend 2002-04-19 Henning Meier-Geinitz * backend/test.c doc/descriptions/test.desc: Added missing include. 2002-04-18 Henning Meier-Geinitz * backend/net.c doc/descriptions/net.desc: Fixed bug in sane_read that cause garbled data to be sent to the frontend. Fixed some long lines. 2002-04-18 Marian Eichholz * backend/sm3600.c : compiles without warnings now. * backend/sm3600.h : FakeCalibration prototype conditionalised, too. 2002-04-17 Frank Zago * TODO: changed Relisys Scorpio Super 3 contact info. 2002-04-17 Marian Eichholz * doc/descriptions/sm3600.desc : specific models listed * doc/sane-sm3600.man : warning for libusb-versions. * backend/sm3600-homerun.c : FakeCalibration() conditionalised. * backend/sm3600.c : Improved portability, less warnings 2002-04-17 Henning Meier-Geinitz * backend/fujitsu-scsi.h backend/fujitsu.c backend/fujitsu.conf backend/fujitsu.h doc/sane-fujitsu.man doc/descriptions/fujitsu.desc: Added new fujitsu backend (from Oliver Schirrmeister ). This backend supersedes the m3096g backend and also includes the m3091 backend. * backend/m3096g.c backend/m3096g.h backend/m3096g.conf backend/m3096g-scsi.h: Removed, support is now in fujitsu-backend. * backend/Makefile.in backend/dll.conf: Adjusted for new fujitsu backend. * doc/sane-sp15c.c: Added new manpage for sp15c. Extracted from the old sane-fujitsu manpage. * doc/.cvsignore doc/Makefile.in doc/sane.man: Adjusted for fujitsu and sp15c manpages. * AUTHORS: Updated for fujitsu backend. * doc/descriptions-external/m3091.desc doc/descriptions/m3096g.desc: Removed, now in doc/descriptions/fujitsu.desc. * PROJECTS: Removed m3091 project. * TODO: Added coolscan2 backend, bh and coolscan warnings, scanimage 16 bit problem. Removed fujitsu m391 entries, sm3600 non-static symbol, plustek-backend OS/2 problem. 2002-04-15 Marian Eichholz * sm3600 imported from sm3600.sf.net featuring infrastructure for various models and the (new) SM 3750i. backend/sm3600.h backend/sm3600.c backend/sm3600-color.c backend/sm3600-homerun.c backend/sm3600-scantool.h backend/sm3600-scanutil.c backend/sm3600-gray.c backend/sm3600-scanmtek.c backend/sm3600-scanusb.c doc/sane-sm3600.man 2002-04-15 Henning Meier-Geinitz * doc/descriptions/mustek_usb.desc: Commented out 1200 USB as it is not really supported yet. 2002-04-14 Gerhard Jaeger * backend/plustek.[ch], backend/plustek-usbhw.c, backend/plustek-usbio.c, backend/plustek-usbscan.c, backend/plustek-usbshading.c backend/plustek-usb.c, backend/plustek-devs.c backend/plustek-usb.h backend/plustek-share.h: Code cleanup, fixed OS/2 compilation breakage fixed problem that causes non LM983x based devices to crash, minor fixes added CANON N650U device structure 2002-04-13 Karl Heinz Kremer * backend/epson.[ch]: Added new product IDs for Perfection 1650 and 2450. Check if scanner needs to be opened for the reset() call. 2002-04-13 Henning Meier-Geinitz * backend/canon630u-common.c: Added #include to fix compilation on OS/2. * sanei/sanei_scsi.c: Use O_NONBLOCK when opening an sg device under Linux. Return SANE_STATUS_DEVICE_BUSY if EBUSY. Check for buffer==0 for OS/2. * doc/sane-scsi.man: Added more information about NCR/Symbios 810 and Tekram DC315 controllers under Linux. * backend/Makefile.in backend/test.c backend/test.conf backend/test.h backend/test-picture.c: Added new test backend. * doc/Makefile.in: Added sane-test man page. Added teco doc directory. * doc/sane.man doc/sane-test.man doc/.cvsignore: Added sane-test manual page. * doc/descriptions-external/test.desc doc/descriptions/test.desc: Moved test.desc to doc/descriptions and updated this file. * PROJECTS: Removed test backend. * AUTHORS: Added myself for test backend. * TODO: Added entries about SANE_CAP_ADVANCED in groups, a wip marker for sorted sane-backends.html, and saneopts.??.po problem. Removed backends from list of inclusion because of lack of response: v4l2, lhii, viceo (they stay in PROJECTS). Removed snapscan from exported symbols bug list. Updated doxygen list. 2002-04-12 Frank Zago * doc/descriptions-external/leo.desc PROJECTS: Added leo backend info. 2002-04-12 Matthew Duggan * backend/canon_pp-io.c: Updated for new libieee1284 interface (version 0.1.5) * acinclude.m4 aclocal.m4 configure configure.in: Added check for libieee1284 > 0.1.5 2002-04-08 Rene Rebe * backend/avision.h backend/avision.c backend/avision.conf: bug- fixes 2002-04-11 Henning Meier-Geinitz * include/sane/sanei_pa4s2.h include/sane/sanei_scsi.h: Added/adjusted documentation for doxygen. 2002-04-11 Stéphane Voltz * backend/umax_pp_low.c: fixed 8 bits I/O support 2002-04-10 Oliver Schwartz * backend/snapscan-scsi.c Removed illegal character * backend/snapscan-usb.h Removed declaration of bqelements 2002-04-10 Oliver Schwartz * backend/snapscan-usb.c make bqelements static * backend/snapscan-scsi.c disable send_diagnostic() for SnapScan 1236 2002-04-10 Henning Meier-Geinitz * backend/mustek_usb.c doc/descriptions/mustek_usb.desc doc/mustek_usb/mustek_usb.CHANGES: Cleanup in sane_control_option, sane_set_io_mode, sane_get_select_fd and sane_exit. New version: 1.0-13. * backend/mustek_usb.c backend/mustek_usb_high.c backend/mustek_usb_high.h backend/mustek_usb_low.c backend/mustek_usb_low.h backend/mustek_usb_mid.c backend/mustek_usb_mid.h: Fixed coding-style. * backend/pnm.c doc/descriptins/pnm.desc: sane_set_io_mode checks for !non_blocking and scanning now. Fixed coding-style. New version: 1.0.8. * TODO: Added dll init/exit problem. Added non-static symbol problem. Added info about missing definition of 1-bit modes in sane.tex. Added entry about sane-find-scanner searching directories. * backend/mustek.c backend/mustek.h doc/desacriptions/mustek.desc doc/mustek/mustek.CHANGES: Set freed variables to 0 in sane_exit. Fixed coding style. New version: 1.0-121. 2002-04-09 Petter Reinholdtsen * sanei/sanei_pv8630.c (sanei_pv8630_bulkwrite): Avoid warning on Solaris. Correct type of second argument to sanei_usb_write_bulk() from (char*) to (SANE_Byte*). 2002-04-08 Frank Zago * backend/teco1.c backend/teco1.conf backend/teco1.h doc/sane-teco1.man doc/descriptions/teco1.desc doc/teco/teco1.txt doc/.cvsignore doc/Makefile.in doc/sane.man po/Makefile.in po/teco1.fr.po backend/Makefile.in backend/dll.conf sane-backends/AUTHORS sane-backends/ChangeLog sane-backends/PROJECTS: added teco1 backend 2002-04-08 Rene Rebe i * doc/descriptions/avision.desc: fixed syntax 2002-04-08 Henning Meier-Geinitz * Makefile.in: make libcheck now also checks for non-static variables. * v4l.c: Some variables haven't been static. sane_set_io_mode must return SANE_STATUS_GOOD if non_blocking == SANE_FALSE. 2002-04-08 Rene Rebe * backend/avision.c backend/avision.h doc/descriptions/avision.desc doc/sane-avision.man: updated to Avision backend build 25. More stable for HP usb scanners, support for Misubishi scanners and various cleanups. - And corrected the avision.desc location. 2002-04-07 Henning Meier-Geinitz * backend/canon_pp.c backend/canon_pp-dev.c backend/canon_pp-io.c backend/canon_pp-dev.h: Changed timeouts and added scanner sleeps to improve reliability of 6x0P models. Also corrected typo which caused full bed scans to fail. Disabled problematic detect for now, will make detection slightly slower. Patch from Matthew Duggan . * doc/descriptions/canon_pp.desc: Added man page, incremented version. Patch from Matthew Duggan . * doc/sane-canon_pp.man: Added more hints on getting canon_pp driver working. Patch from Matthew Duggan . * backend/Makefile.in backend/canon630u-common.c backend/canon630u.c backend/canon630u.conf backend/dll.conf backend/lm9830.h: Added canon630u backend. This backend supports the CanoScan 630u and CanoScan 636u (hopefully). Patch from Nathan Rutman . * doc/.cvsignore doc/Makefile.in doc/sane-canon630u.man doc/descriptions/canon630u.desc: Added documentation and description of canon630u backend. Patch from Nathan Rutman . * PROJECTS doc/sane.man: Updated for canon630u backend. * AUTHORS: Updated for canon630u backend. * doc/saned.man: The path in the xinetd example pinted to /usr/local unconditionally. * backend/canon630u-common.c: Fixed compilation on non-Linux systems. * configure configure.in: Added work-around for asm/io.h problems. 2002-04-06 Henning Meier-Geinitz * backend/mustek.c doc/descriptions/mustek.desc doc/mustek/mustek.CHANGES: Fixed color three-pass scanning for some scanners. * PROJECTS: Added Hewlett-Packard ScanJet 2200c project. * sanei/sabei_constrain_value.c: Check that a SANE_Bool variable can only be SANE_TRUE or SANE_FALSE. 2002-04-02 Peter Fales * configure, acinclude.m4, aclocal.m4 - Another tweak to the allowed gphoto2 version numbers 2002-04-02 Henning Meier-Geinitz * acinclude.m4 aclocal.m4 configure configure.in: Added checks for libieee1284. Enable canon_pp backend if found. Patch from "Matthew Duggan" . * backend/Makefile.in backend/canon_pp-dev.c backend/canon_pp-dev.h backend/canon_pp-io.c backend/canon_pp-io.h backend/canon_pp.c backend/canon_pp.conf backend/canon_pp.h backend/dll.conf: Added new canon_pp backend for the CanoScan FB330P, FB630P, N340P, and N640P scanners. Patch from "Matthew Duggan" . * AUTHORS doc/Makefile.in doc/sane-canon_pp.man doc/sane.man doc/descriptions/canon_pp.desc: Updated for canon_pp backend. Patch from "Matthew Duggan" . * PROJECTS TODO: Removed canon_pp. * doc/.cvsignore: Added sane-canon_pp.5. * doc/descriptions-external/canon_pp.desc: Removed. 2002-03-30 Henning Meier-Geinitz * doc/descriptions-external/test.desc: Added description of the test backend. * PROJECTS: Added homepage of the test backend. 2002-03-29 Henning Meier-Geinitz * backend/pnm.c doc/descriptions/pnm.desc: Check if option is settable when automatically setting it. New version: 1.0.7. * backend/Makefile.in doc/Makefile.in frontend/Makefile.in include/Makefile.in po/Makefile.in tools/Makefile.in: Support for variable DESTDIR. If set, all files are installed to that location. Also print the filename of the installed file, not only the path. Both changes are mostly for package creators/maintainers. * configure configure.in: Another try to get the DISTCLEAN_FILES working. 2002-03-28 Henning Meier-Geinitz * doc/sane-scsi.man: Updated info about SCSI-Howto and some SCSI adapters. 2002-03-27 Oliver Rauch * removed unused definitions in include/sane/saneopts.h: SMEAR, TEN_BIT_MODE, TWELVE_BIT_MODE, RGB_PREVIEW_PATCH, START_SCAN_PATCH 2002-03-26 Henning Meier-Geinitz * PROJECTS: Updated canon_pp entry. Added Canon FB630U and Canon N650U USB entries. Updated test backend entry. 2002-03-26 Frank Zago * PROJECTS: Updated Relisys Scorpio Super 3 infos. 2002-03-26 Gerhard Jaeger * AUTHORS, sanei_lm983x.[ch], entire Plustek backend: Updated to new mail-address and backend URL 2002-03-24 Oliver Schwartz * backend/snapscan.c: Fix segfault in sane_exit if no devices were found. 2002-03-24 Henning Meier-Geinitz * acinclude.m4 aclocal.m4 configure configure.in ltmain.sh: Update to libtool 1.4.2. Included local changes: use soname "libsane" for all os but AIX. Use "normal" shared libs (.so) instead of archives on AIX. Use 1 instead of 2 as major number with Irix. * Makefile.in ltconfig: Removed ltconfig as it is no longer used by libtool. * TODO: More status indicators for backends to include. Removed entry about the grand Fujitsu reunification. Updated entry about the DBG warnings. Removed entry about split sane-backends.html. Removed entries about libtool problems. Added entry about plustek on OS/2 problem. * backend/pnm.c doc/descriptions/pnm.desc: If fread returns 0, check for EOF and other errors and return appropriately. * backend/mustek_usb_mid.h: Updated SANE header. * backend/net.c doc/descriptions/net.desc: Fixed 16-bit byte-order handling in sane_read() (patch from Michael Herder ). New version: 1.0.7. 2002-03-24 Oliver Schwartz * backend/snapscan-options.c: New file (option functions moved from snapscan.c) * backend/snapscan-utils.c: Removed file * backend/Makefile.in: Added snapscan-options.c, removed snapscan-utils.c * backend/snapscan.c backend/snapscan.h backend/snapscan-scsi.c backend/snapscan-usb.c backend/snapscan.h backend/snapscan.conf: Snapcan-backend ver. 1.4.9 - Moved option functions to snapscan-options.c - Autodetect USB scanners on Linux - Better error reporting 2002-03-21 Henning Meier-Geinitz * doc/backend-writing.txt: Updated concerning split .desc directories. * doc/Makefile.in: Updated install-mostang concerning split HTML pages. * configure configure.in README: Use only shared libraries by default. Fixed file patterns for distclean targets. * doc/Makefile.in: Remove backup etc. files also in subdirs when using make distclean. * backend/pnm.c doc/descriptions/pnm.desc: Don't allow to set options that don't have SANE_CAP_SOFT_SELECT and don't allow read and write for options that are inactive. New version: 1.0.5. 2002-03-20 Henning Meier-Geinitz * sanei_wire.c: Set allocated memory to 0 to avoid delivering garbage to the frontend. * backend/mustek.c backend/mustek.desc backend/mustek.h doc/mustek/mustek.CHANGES: Fixed halftone pattern handling. The buffer was way too small. Option 0 has an empty name. Better debug output for dev_cmd. Set size for group options to 0. Set size of halftone pattern to non 0. * backend/mustek_usb.c backend/mustek_usb.desc doc/mustek_usb/mustek_usb.CHANGES: Option 0 has an empty name now. Group options have size 0 now. Check also for SANE_ACTION_SET_AUTO. Removed buggy output in sane_control_option. Added more debug output in sane_control_option. * backend/net.c backend/net.desc backend/net.h: Use copies of option descriptors to make sure their addresses aren't changed until sane_close. New version: 1.0.7. * doc/descriptions/abaton.desc doc/descriptions/agfafocus.desc doc/descriptions/apple.desc doc/descriptions/artec.desc doc/descriptions/as6e.desc doc/descriptions/avision.desc doc/descriptions/bh.desc doc/descriptions/canon.desc doc/descriptions/coolscan.desc doc/descriptions/dc210.desc doc/descriptions/dc240.desc doc/descriptions/dc25.desc doc/descriptions/dll.desc doc/descriptions/dmc.desc doc/descriptions/epson.desc doc/descriptions/gphoto2.desc doc/descriptions/hp.desc doc/descriptions/m3096g.desc doc/descriptions/matsushita.desc doc/descriptions/microtek2.desc doc/descriptions/microtek.desc doc/descriptions/mustek.desc doc/descriptions/mustek_pp.desc doc/descriptions/mustek_usb.desc doc/descriptions/nec.desc doc/descriptions/net.desc doc/descriptions/pie.desc doc/descriptions/pint.desc doc/descriptions/plustek.desc doc/descriptions/pnm.desc doc/descriptions/qcam.desc doc/descriptions/ricoh.desc doc/descriptions/s9036.desc doc/descriptions/sceptre.desc doc/descriptions/sharp.desc doc/descriptions/sm3600.desc doc/descriptions/snapscan.desc doc/descriptions/sp15c.desc doc/descriptions/st400.desc doc/descriptions/tamarack.desc doc/descriptions/template.desc. doc/descriptions/umax1220u.desc doc/descriptions/umax.desc doc/descriptions/umax_pp.desc doc/descriptions/v4l.desc: Moved descriptions of included backends from backend/*.desc. * doc/descriptions-external/canon_pp.desc doc/descriptions-external/coolscan2.desc doc/descriptions-external/hp4200.desc doc/descriptions-external/hpoj.desc doc/descriptions-external/ibm.desc doc/descriptions-external/lhii.desc doc/descriptions-external/m3091.desc doc/descriptions-external/niash.desc doc/descriptions-external/teco.desc doc/descriptions-external/v4l2.desc doc/descriptions-external/template.desc.: Moved descriptions of external backends from backend/*.desc. * doc/Makefile.in: Updated for separated lists of backends. * tools/Makefile.in tools/sane-desc.el.in tools/sane-desc-ext.el: Updated for separated lists of backends. Use package version in internal list. Don't use version and man page in external list. * tools/sane-desc.el: Removed. * configure configure.in: Added tools/sane-desc.el to output files. * backend/Makefile.in: Updated DISTFILES. * backend/*.desc backend/template.desc.: Removed (now in doc/). * tools/.cvsignore: Added sane-desc.el. 2002-03-19 Frank Zago * matsushita backend: updates and fixes. * sceptre backend: updates and fixes. 2002-03-19 Henning Meier-Geinitz * TODO: Marked backends that really should be included. Clarified comment about sane-backends.html. Added comment about sort order this list. Add comment about better linking of external libs. 2002-03-17 Henning Meier-Geinitz * backend/hpoj.desc: New file (from David Paschal ). * backend/mustek_usb.c backend/mustek_usb.desc backend/mustek_usb.h backend/mustek_usb_high.c backend/mustek_usb_high.h backend/mustek_usb_low.c backend/mustek_usb_low.h backend/mustek_usb_mid.c doc/mustek_usb/mustek_usb.CHANGES: Fixed segfault when opening device again after closing and possible segfault when name="". Type for option 0 must be set to SANE_TYPE_INT explicitly. Updated GPL/SANE headers. * TODO: Added entry about auto-loading SCSI drivers. Removed entry about new SANE types. Moved entry about config.guess to doc/releases.txt. Added more info about libtool problems. * doc/releases.txt: Added info about config.guess and config.sub. * config.guess config.sub: Updated from upstream. 2002-03-17 Peter Fales * backend/gphoto2.c - Minor bug fixes for problems found by tstbackend 2002-03-16 Gerhard Jaeger * backend/plustek.[ch]: fixed a bug, that causes segfaulting the backend when using the USB autodetection stuff 2002-03-15 Stéphane Voltz * backend/umax_pp_low.c: fixed 1200 dpi mode * backend/umax_pp: changes for translations support * po/Makefile.in po/umax_pp.fr.po: create french translations for umax_pp backend 2002-03-12 Frank Zago * PROJECTS backend/teco.desc: new project 2002-03-11 Henning Meier-Geinitz * README: Some more information on where to find the config files and a hint to make a backup. * doc/sane.man: Updated mustek_usb and plustek backend entries. Added FILES section. Minor fixes and updates. 2002-03-10 Frank Zago * PROJECTS: removed matsushita project. * backend/sceptre.desc backend/matsushita.desc: updated the backend url. 2002-03-10 Abel Deuring * configure.in, configure: Added a second test for HAVE_SG_TARGET_STATUS: check /usr/src/linux/include/scsi/sg.h; added conditionals so that this test is done only for Linux 2002-03-10 Gerhard Jaeger * doc/sane-plustek.man: Update * backend/plustek-usbhw.c backend/plustek-usbimg.c backend/plustek-usbio.c backend/plustek-usbmap.c backend/plustek-usbscan.c backend/plustek-usbshading.c backend/plustek.c backend/plustek-devs.c backend/plustek-pp.c backend/plustek.usb backend/plustek.h backend/plustek-share.h backend/plustek-usb.h: Added custom gamma tables, added patches to support EPSON1250, UMAX3400 and HP2100C devices, added warmup and timed lamp-off features, minor bug-fixes * backend/plustek.desc: Added Umax entry * backend/plustek.conf: Update 2002-03-10 Abel Deuring * configure.in, configure, include/sanei/config.h.in, sanei/sanei_scsi.c: Added HAVE_SG_TARGET_STATUS for compatibility with old Linux sg.h versions 2002-03-10 Gerhard Jaeger * po/plustek.de.po: Update * po/plustek.es.po, po/saneopts.es.po: Added spanish translation thanks to Gustavo D. Vranjes 2002-03-10 Stéphane Voltz * backend/umax_pp_low.c: fast and adaptative scanner probe function. Improved CCD calibration. * tools/umax_pp.c: revision change 2002-03-08 Oliver Rauch * backend/Makfile.in: added missing dependencies for new umax backends 2002-03-08 Henning Meier-Geinitz * doc/sane.tex: Added "multi-function peripheral" to list of types in SANE standard. 2002-03-07 Henning Meier-Geinitz * include/sane/config.h.in: Fix comment for HAVE_USB_H. * TODO: Added entry about md5/getopt license issues. 2002-03-07 Abel Deuring * sanei/sanei_scsi.c: fixed a typo (Sg_scsi_id -> SG_scsi_id) 2002-03-07 Oliver Rauch * updated umax backend to version 1.0.7-build-33 new file: umax-usb.c (by Frank Zago) 2002-03-06 Frank Zago * po/Makefile,in doc/sane.man doc/Makefile.in backend/matsushita.desc backend/dll.conf backend/Makefile.in sane-backends.lsm AUTHORS po/matsushita.fr.po doc/matsushita/matsushita.txt doc/matsushita/matsushita10_trc.txt doc/matsushita/matsushita11_trc.txt doc/matsushita/matsushita12_trc.txt doc/matsushita/matsushita13_trc.txt doc/matsushita/matsushita14_trc.txt doc/matsushita/matsushita1_trc.txt doc/matsushita/matsushita2_trc.txt doc/matsushita/matsushita3_trc.txt doc/matsushita/matsushita4_trc.txt doc/matsushita/matsushita5_trc.txt doc/matsushita/matsushita6_trc.txt doc/matsushita/matsushita7_trc.txt doc/matsushita/matsushita8_trc.txt doc/matsushita/matsushita9_trc.txt doc/sane-matsushita.man backend/matsushita.h backend/matsushita.conf backend/matsushita.c: Addition of the Matsushita / Panasonic backend * doc/.cvsignore: added sane-matsushita.5 2002-03-03 Frank Zago * doc/.cvsignore: added sane-sceptre.5 2002-03-03 Frank Zago * doc/sane-sceptre.5: removed (auto-generated) 2002-03-03 Frank Zago * doc/sceptre/s1200.txt: doc updates * backend/sceptre.desc: increased version * backend/sceptre.c backend/sceptre.h: fixed a gamma table bug, fixed some color shifting problems, some cleanups. * doc/sceptre.man: doc updates 2002-02-24 Henning Meier-Geinitz * backend/Makefile.in: Removed getopt.o getopt1.o and md5.o from LIBLIB_FUNCS as they are not needed in backends. 2002-02-22 Henning Meier-Geinitz * PROJECTS: Added matsushita backend. * backend/matsushita.desc: New file. Description for the matsushita backend. 2002-02-21 Henning Meier-Geinitz * backend/Makefile.in backend/dll.conf backend/sceptre.c backend/sceptre.conf backend/sceptre.desc backend/sceptre.h: Added sceptre backend for the Sceptre VividScan 1200 (patch from Frank Zago ). * doc/Makefile.in doc/sane-sceptre.5 doc/sane-sceptre.man doc/sane.man doc/sceptre/preview_trace.txt doc/sceptre/s1200.txt doc/sceptre/scan_trace.txt: Added documentation for sceptre backend (patch from Frank Zago ). * po/Makefile.in po/sceptre.fr.po: Added french translation for sceptre backend (patch from Frank Zago ). * AUTHORS PROJECTS sane-backends.lsm: Updated concerning sceptre backend (patch from Frank Zago ). 2002-02-20 Stéphane Voltz * backend/umax_pp_low.c backend/umax_pp.c: corrected few bugs due to changing default model to 'none'. * tools/umax_pp.c: fixed compile problem 2002-02-19 Henning Meier-Geinitz * lib/inet_pton.c: Use u_int32_t instead of in in_addr_t which isn't defined at least for OS/2. * TODO: Added entry about DBG warnings. * doc/sane.tex: Added some vendors (Abaton, Acer, Apple, Avision, CANON, Fujitsu, IBM, NEC, Nikon, Plustek, Polaroid, Ricoh, Sharp, Siemens, Tamarack) and device types (film scanner, sheetfed scanner) to the SANE standard. Updated date. * doc/Makefile.in: Remove sanei-html in make distclean. * PROJECTS: Added Acer ScanWit 2720S. * backend/template.desc.: Added explanation for backend version, fixed typo. 2002-02-16 Abel Deuring : * frontend/scanimage.c / part for the "-f" command line option: replaced the vprintf call with a loop of printf calls; fixed a "too stingy" malloc 2002-02-16 Peter Fales * backend/dc240.c - Fix mismatches between format and parameters in debug statements 2002-02-15 Henning Meier-Geinitz * include/sane/sanei.h include/sane/sanei_ab306.h include/sane/sanei_auth.h include/sane/sanei_backend.h include/sane/sanei_codec_ascii.h include/sane/sanei_codec_bin.h include/sane/sanei_config.h include/sane/sanei_debug.h include/sane/sanei_lm983x.h include/sane/sanei_thread.h include/sane/sanei_usb.h: Added, fixed and updated documentation for sanei using doxygen. * doc/.cvsignore: Added sanei-html. * TODO: Updated entry about missing sanei documentation. Removed entry about make distclean issues. 2002-02-15 Stéphane Voltz * backend/umax_pp_low.c backend/umax_pp_low.h backend/umax_pp_mid.h backend/umax_pp_mid.c backend/umax_pp.c backend/umax_pp.h backend/umax_pp.conf: added ppdev character device name passing from conf file. Allow model override from conf option. DBG macros clean-up. Fixed color inversion for 1660P models. Fixed potential crash when custom dump files could not be opened for writing. * doc/sane-umax_pp.man: updates to match backned new parameter and behaviour * tools/umax_pp.c: added device name argument 2002-02-14 Jochen Eisinger * sanei/sanei_auth.h: remove strange line-ending handling code 2002-02-14 Henning Meier-Geinitz * configure configure.in include/sane/config.h.in lib/Makefile.in lib/inet_pton.c: Added wrapper for inet_pton(). If this function is not available, try first inet_aton() and then inet_addr(). * frontend/saned.c: Use inet_pton() instead of inet_aton() to avoid compilation errors on e.g. OS/2. * include/Sane/sanei_debug.h: Added warnings for format problems in DBG messages (from Frank Zago ). * include/sane/sanei.h sanei/Makefile.in sanei/load_values.c sanei/save_values.c: Removed load_values and save_values as they are only used in sane_frontends. * backend/mustek.c backend/mustek_usb_low.c backend/mustek_usb_high.c backend/net.c doc/mustek/mustek.CHANGES doc/mustek_usb/mustek_usb.CHANGES sanei/sanei_usb.c: Fixed some DBG format warnings. * lib/inet_ntop.c: Only use inet_ntoa if it's available. 2002-02-13 Peter Fales * configure, acinclude.m4, aclocal.m4 - Another tweak to the allowed gphoto2 version numbers 2002-02-13 Henning Meier-Geinitz * configure configure.in Makefile.in backend/Makefile.in doc/Makefile.in frontend/Makefile.in include/Makefile.in japi/Makefile.in lib/Makefile.in sanei/Makefile.in tools/Makefile.in testsuite/Makefile.in po/Makefile.in: Added global list of file patterns for "make distclean". Some "make clean" and "make distclean" cleanup and additions. 2002-02-12 Henning Meier-Geinitz * tools/sane-config.in: Avoid printing "-I/usr/include" as this changes the default include order (from Tim Waugh ). Really check for entries in $cflags that are also in $includedir. * frontend/saned.c: DNS queries for remote hosts are only done if necessary. It's now possible to use "+" without hosts/DNS entries for the connecting host. * TODO: Removed entries for config.h, OpenBSD shared libs, and -ansi on HP-UX. Updated device type entry. 2002-02-10 Henning Meier-Geinitz * configure configure.in: Link to functions in lib/ only if they are not available on the system. Use AC_PROG_LIBTOOL instead of the deprecated AM_PROG_LIBTOOL. * backend/Makefile.in lib/Makefile.in: Link to functions in lib/ only if they are not available on the system. Avoid duplicating list of functions. * sanei/sanei_usb.c: If get_vendor_product fails, don't try again for every device file. 2002-02-10 Oliver Schwartz * po/Makefile.in: Added snapscan.de.po to DISTFILES 2002-02-09 Abel Deuring : * sanei/sanei_scsi.c: Added checks to the Linux part of sanei_scsi_open_extended, if an SG device file is being opened 2002-02-09 Oliver Schwartz * po/Makefile.in po/snapscan.de.po backend/snapscan.c: Added language translation support for snapscan backend, added german translations. 2002-02-09 Henning Meier-Geinitz * po/Makefile.in po/saneopts.fr.po po/umax.fr.po: Added french translation (from Frank Zago ). 2002-02-08 Henning Meier-Geinitz * PROJECTS: Added info about HP Scanjet 5S. * lib/inet_ntop.c: Removed OS/2 kludge: it's not necessary. 2002-02-05 Stéphane Voltz * backend/umax_pp_low.c backend/umax_pp_low.h backend/umax_pp_mid.h backend/umax_pp_mid.c backend/umax_pp.c backend/umax_pp.h: tuned down duration of pauses in scanner ringing. Indent'ing all files. Include header files according to HAVE_XXX_H defines in config.h . * tools/umax_pp.c: updated version and added printing of config options 2002-02-05 Henning Meier-Geinitz * configure configure.in: Version is 1.0.7-cvs. Enable warnings by default. Don't use "-ansi" for HPUX. * sanei/sanei_thread.c: Make sure that waitpid returns something useful on OS/2. Avoids the "Unknown SANE status code 128" errors. Older entries can be found in ChangeLog-1.0.7. backends-1.3.0/ChangeLogs/ChangeLog-1.0.9000066400000000000000000001273241456256263500175430ustar00rootroot00000000000000****** Release of sane-backends 1.0.9. End of code freeze ****** 2002-10-23 Henning Meier-Geinitz * doc/descriptions-external/gt68xx.conf: Updated status of scanners. * doc/doxygen.conf: Increased version to 1.0.9. * configure configure.in: New version: 1.0.9. Disabled compilation warnings. 2002-10-23 Stéphane Voltz * backend/umax_pp_low.c: added O_NONBLOCK to flags when opening ppdev character device, to get around hangs with 2.4.19 linux kernels in pure EPP mode. 2002-10-20 Henning Meier-Geinitz * README.linux: Added comment about DEC cc on Linux Alpha. 2002-10-19 Peter Fales * backend/dc25.c - Moved use of UNUSEDARG macro to fix compilation failure on OS/X 2002-10-19 Henning Meier-Geinitz * doc/descriptions-external/tevion9693usb.desc: Added Trust Easy Webscan 19200. ---- CODE FREEZE FOR SANE 1.0.9 --- -- snapshot 1.0.9-pre2 2002-10-17 Henning Meier-Geinitz * backend/dll.conf: Added commented out entries for hpoj and gt68xx. * NEWS: Updated release date and backend list. * TODO: Added scanimage short string-list options problem. 2002-10-17 Gerhard Jaeger * backend/plustek.c: removed internationalization stuff * backend/plustek-usb.c: removed obsolete definitions * backend/plustek-usbhw.c: fixed typos * backend/plustek-usnshading.c: fixed bug that produces files > 700M in debug mode. Added some debug output. 2002-10-16 Oliver Rauch * backend/umax.c, umax-scanner.c: added Power Look 2000 as supported device, build 38 2002-10-15 Gerhard Jaeger * doc/sane-plustek.man: Major update * doc/descriptions/plustek.desc: Added EPSON 1260 entry * backend/plustek.conf updated options * backend/plustek.c backend/plustek-devs.c backend/plustek-usb.c backend/plustek-usbhw.c backend/plustek-pp.c backend/plustek-usbmap.c backend/plustek-usbimg.c backend/plustek-usbscan.c backend/plustek-usbshading.c backend/plustek.h backend/plustek-share.h backend/plustek-usb.h Bug fixes and workarounds 2002-10-15 Henning Meier-Geinitz * doc/sane.tex: Backend behaviour is undefined if sane_init doesn't return SANE_STATUS_GOOD. Added SANE_STATUS_INVAL to return codes for sane_start. Description of sane_set_io_mode is more precise. * sanei/sanei_usb.c: Really check for bulk_out ep when writing. * TODO: Added microtek2 problem. Updated sane.tex and desc sections. Added scanimage width/height problem. Added possible solution to po file mess. * doc/Makefile.in doc/sane-config.man: Added man page for sane-config. * doc/.cvsignore: Added sane-config.1. 2002-10-15 Peter Kirchgessner * backend/hp.conf: Added comment for USB-scanner 2002-10-14 Peter Fales * backend/dc240.h,backend/dc240.c: Use portable code for camera structures that does not depend on gcc or little-endian byte order 2002-10-14 Oliver Schwartz * backend/snapscan.h backend/snapscan.c backend/snapscan.conf doc/descriptions/snapscan.desc: SnapScan backend 1.4.17 - added ID string for SnapScan e10, added Mitsubishi Diamandview 648UT in desc file. 2002-10-11 Peter Fales * backend/dc240.h - Add message to #error as required by ANSI 2002-10-11 Henning Meier-Geinitz * backend/mustek.c doc/mustek/mustek.CHANGES: Fixed shutting off the lamp for the Paragon 1200 A3 Pro. * doc/descriptions/mustek.desc: Updated version number and A3 Pro information. 2002-10-10 Petter Reinholdtsen * backend/canon630u-common.c: Change type of size parameter from 'unsigned int' to 'size_t' for gl640WriteBulk() and gl640ReadBulk() to avoid crash on platforms where 'sizeof(size_t) > sizeof(int)'. * backend/coolscan.c: Remove semicolon at the end of functions. 2002-10-09 Frank Zago * backend/teco1.c backend/teco1.conf doc/descriptions/teco1.desc: Added entry for AVEC color 2412. 2002-10-09 Henning Meier-Geinitz * doc/descriptions/microtek2.desc: Added per-scanner status (from Karsten Festag ). 2002-10-09 Stéphane Voltz * backend/umax_pp.c: disable scanner protocol debug facility 2002-10-08 Karl Heinz Kremer * backend/epson.c: force color channel re-order for GT-2200 scanner 2002-10-08 Stéphane Voltz * backend/umax_pp.c: use macros from saneopts.h to fix translations problems 2002-10-08 Gerhard Jaeger * po/plustek.de.po: Cleanup 2002-10-08 Henning Meier-Geinitz * doc/saned.man doc/sane-net.man: Removed comment about saned port not officially assigned. * po/plustek.de.po po/umax_pp.fr.po: Fixed double msgids. 2002-10-05 Karl Heinz Kremer * backend/epson.c: Fixed problem with incorrect response to sane_get_parameters() in certain situations. 2002-10-04 Jochen Eisinger * doc/descriptions/mustek_pp.desc: add :status tags to each scanner 2002-10-04 Gerhard Jaeger * backend/plustek.conf fixed option enableTPA * po/plustek.de.po po/plustek.es.po removed "Negative" * backend/plustek.c backend/plustek-devs.c backend/plustek-usb.c backend/plustek-usbhw.c backend/plustek-usbimg.c backend/plustek-usbmap.c backend/plustek-usbscan.c backend/plustek-usbshading.c Minor bug fixes 2002-10-04 Karl Heinz Kremer * doc/sane-epson.man: Added quotes around halftoning options. 2002-10-02 Henning Meier-Geinitz * TODO: Updated list of backends scheduled for inclusion. Updated v4l bug list. Added entry about vendor/product ids for .desc. Removed TL_X > BR_X (->TODO), dll ltdl rewrite, wait return value (->TODO), sane.tex comment about 1 bit color mode, sane-plustek.man issue. 2002-10-02 Stéphane Voltz * tools/umax_pp.c: changed version and added handling of 4 digits port address * po/umax_pp.de.po: removed redundant entry * backend/umax_pp.conf: comment fixings * backend/umax_pp_low.c backend/umax_pp.c: added handling of ppdev ioctl return code, allow 4 digits port number 2002-10-02 Matto Marjanovic * doc/descriptions/microtek.desc: Updated version number. * backend/microtek.c: Fixed bug in sane_read() [had failed to set return length to zero in non-STATUS_GOOD conditions]. Fixed bug in sane_set_io_mode() [had returned SANE_STATUS_UNSUPPORTED if blocking mode was requested]. Bumped version up to 0.13.1. 2002-10-01 Henning Meier-Geinitz * doc/descriptions-external/gt68xx.desc: Added new scanners. Updated status of supported scanners. * doc/sane.tex: Added explanation of bit order for 1 bit modes. Added comment about 1 bit color modes. Fixed missing \code command. New version: 1.03. * doc/backend-writing.txt: Added paragraph about checking geometry (from TODO). 2002-09-30 Peter Fales * configure, acinclude.m4, aclocal.m4 - Update the allowed gphoto2 version numbers 2002-09-30 Oliver Rauch * doc/descriptions/umax.desc: removed remark that Astra 2200U via USB only works on linux (because need of USB control messages) 2002-09-30 Matthew Duggan * backend/canon_pp.c backend/canon_pp-dev.c backend/canon_pp-dev.h: Finally fixed cancelling, which I broke some time ago in the quest for speed. FB620P still doesn't abort nicely (firmware bug?). * doc/sane-canon_pp.man: Added notes on FB620P problems. * doc/descriptions/canon_pp.desc: Bumped version number. 2002-09-30 Henning Meier-Geinitz * backend/test.c backend/test-picture.c doc/descriptions/test.desc: Fixed bytes_per_line in 1 bit mode. Fixed 1 bit color three-pass mode (grid). Changed bit order for 1 bit color to most significant bit is first pixel. 2002-09-29 Peter Fales * backend/dc25.[ch]: Fix compiler warnings, and correct errors found by tstbackend. ---- FEATURE FREEZE FOR SANE 1.0.9 --- -- snapshot 1.0.9-pre1 2002-09-29 Henning Meier-Geinitz * doc/descriptions/mustek_usb.desc: Added "ScanExpress" for all models to avoid confusion with BearPaw models. * Makefile.in: Added ChangeLog-1.0.6 ChangeLog-1.0.7 ChangeLog-1.0.8 to DISTFILES. 2002-09-29 Karl Heinz Kremer * doc/sane-epson.man: Updated information, cleanup * doc/descriptions/epson.desc: Added 1660 and 2400 models 2002-09-28 Oliver Rauch * backend/umax.c: update to build 37 2002-09-28 Gerhard Jaeger * sanei/sanei_lm983x.c: cleanup in reset function * doc/descriptions/plustek.desc: Update according to the new entry definitions * doc/sane-plustek.man: Fixed format and updated info * backend/plustek.c backend/plustek-devs.c backend/plustek-pp.c backend/plustek-share.h backend/plustek-usb.c backend/plustek-usb.h backend/plustek-usbhw.c backend/plustek-usbimg.c backend/plustek-usbmap.c backend/plustek-usbscan.c backend/plustek-usbshading.c backend/plustek.conf: Major update to support CIS based devices Added register dump and raw picturue/calibration data dump Added support for Canon N650U and N670U (currently 8-bit color mode only) 2002-09-28 Andras Major * backends/coolscan2.c doc/descriptions/coolscan2.desc doc/sane-coolscan2.man: update to 0.1.8 2002-09-26 Henning Meier-Geinitz * config.sub config.guess: Updated to current versions. * doc/releases.txt: Added paragraph about the different freeze types and a timetable. 2002-09-26 Oliver Schwartz * doc/snapscan.desc backend/snapscan.c backend/snapscan.h backend/snapscan.conf backend/snapscan-scsi.c: SnapScan backend 1.4.16 (added support for Acer/Benq 5000) 2002-09-24 Peter Fales * backend/dc25.c: Backend returning too much data for image size 2002-09-24 Henning Meier-Geinitz * doc/descriptions-external/tevion9693usb.desc: Added MD9693, updated Artec E+ 48U (from Michael Herder ). 2002-09-23 Henning Meier-Geinitz * doc/descriptions/fujitsu.desc: Changed status to beta and added URLs. * doc/descriptions-external/gt68xx.desc: Added some untested scanners. Updated Mustek entries. * TODO: Removed hpsj5s .desc file problem. 2002-09-22 Peter Fales * doc/sane-dc25.man, backend/dc25.c, backend/dc25.h, backend/gphoto2.c, backend/gphoto2.h, backend/dc240.c, backend/dc240.h: Update authors email address 2002-09-19 Frank Zago * backend/leo.c backend/leo.conf doc/sane-leo.man doc/descriptions/leo.desc: added support for the leoscan S3. * backend/teco2.c backend/teco2.h: added some stuff. 2002-09-18 Henning Meier-Geinitz * doc/descriptions/hpsj5s.desc: Used "Hewlett-Packard" instead of "HP". 2002-09-17 Henning Meier-Geinitz * doc/descriptions-external/gt68xx.desc: Updated Mustek information. Added Plustek OpticPro 1248U. * backend/mustek.c: Fixed version number. * NEWS: Updated backend list. 2002-09-17 Oliver Schirrmeister * backend/fujitsu.c fujitsu.h fujitsu-scsi.h: added m3092 support * doc/descriptions/fujitsu doc/sane-fujitsu added description 2002-09-17 Matthew Duggan * backend/canon_pp.c backend/canon_pp.h backend/canon_pp.conf: Added init_mode option to conf file for faster starts. * doc/sane-canon_pp.man : Added description of init_mode option. 2002-09-16 Matthew Duggan * backend/canon_pp-dev.c backend/canon_pp-dev.h backend/canon_pp-io.c backend/canon_pp-io.h backend/canon_pp.c backend/canon_pp.h: Fix init problem, add changes to facilitate configurable wakeup mode (for faster starts on FB320P and FB620P). * doc/sane-canon_pp.man doc/descriptions/canon_pp.desc: Updates regarding FB310P and FB610P (rebadged Avisions) 2002-09-16 Henning Meier-Geinitz * backend/.cvsignore frontend/.cvsignore tools/.cvsignore: Added .libs. Patch from Sylvain Petreolle . * TODO: Added gt68xx project and viceo.desc entry. * PROJECTS doc/descriptions-external/gt68xx.desc: Added gt68xx information. 2002-09-15 Karl Heinz Kremer * doc/sane-epson.man: Fixed typo (PIP->PIO) 2002-09-15 Henning Meier-Geinitz * tools/sane-find-scanner.c: Format of output for devices supported by libusb and scanner module is the same now. Minor output fixes. * doc/sane-find-scanner.man: Updated -v description. * doc/sane-usb.man: Added info about "unable to access minor data" messages. 2002-09-13 Oliver Rauch * backend/umax-scanner.c, backend/umax.conf doc/descriptions/umax.desc: added Linotype Hell SAPHIR3 as supported scanner 2002-09-12 Henning Meier-Geinitz * README.beos Makefile.in doc/Makefile.in: Added BeOS platform information. Updated Makefiles concerning READMEs. 2002-09-11 Oliver Rauch * doc/descriptions/umax.desc: corrected some urls 2002-09-11 Henning Meier-Geinitz * doc/descriptions/canon630u.desc: Added fb636u as unsupported. Added status information. * frontend/scanimage.c: Added option --dont-scan. Resorted usage output. * doc/scanimage.man: Reordered options. Added info about ICC profiles, gamma4scanimage, and --dont-scan option. Fixed example. * doc/gamma4scanimage.man: Formatting updates. Added default values. Minor fixes. * TODO: Updated backend list. Added comments for additions to sane.tex. Updated .desc file bug list. Removed scanimage entries. Added plustek man page issue. 2002-09-10 Peter Kirchgessner * doc/sane-hp.man: Added hints about special USB device names 2002-09-07 Frank Zago * backend/teco1.c backend/teco1.h teco1.desc: added threshold option 2002-09-07 Oliver Rauch * backend/umax.c, umax.h, umax-scanner.c, umax.conf doc/descriptions/umax.desc Update to sane-umax build 36 2002-09-07 Matthew Duggan * backend/canon_pp-dev.c backend/canon_pp-dev.h: Changes to improve calibration quality on FB620P. 2002-09-06 Frank Zago * README.aix: added mention of the AIX 5.1 version of the passthru driver. 2002-09-06 Henning Meier-Geinitz * tools/sane-config.in: Removed "function" keyword (bourne shell compatibility). 2002-09-03 Abel Deuring * sanei/sanei_scci.c doc/sanei-scsi.man: default timeout for SCSI commands is now 120 seconds; added the environment variable SANE_SCSICMD_TIMEOUT to override the default value. 2002-09-01 Matthew Duggan * doc/sane-canon_pp.man doc/descriptions/canon_pp.desc: Added FB320P to supported scanners after report of successful test. Added FB310P and FB610P to desc as specifically not supported yet. 2002-09-01 Karl Henz Kremer * backend/epson_scsi.c: Fixed alloca compile problem. 2002-09-01 Karl Henz Kremer * backend/epson.[ch] backend/epson_scsi.[ch] backend/epson_usb.[ch] backend/Makefile.in: USB scanners are now using sanei_usb_ functions, split out SCSI related functions to their own source/header file, added source/header file for USB interface 2002-09-01 Henning Meier-Geinitz * TODO: Updated desc and SANE standard sections. Removed ptal entry. * doc/Makefile.in: Make sure that sane-desc is compiled when generating HTML pages. Fixed typo. * doc/sane-usb.man: Minor updates and fixes. 2002-09-01 Peter Kirchgessner * configure.in, doc/sane-hp.man, doc/descriptions/hp.desc applied patch by David Paschal to remove PTAL-support from hp-backend 2002-09-01 Matthew Duggan * backend/canon_pp.c backend/canon_pp-dev.c backend/canon_pp-dev.h backend/canon_pp-io.c: Support for FB620P. * doc/sane-canon_pp.man doc/descriptions/canon_pp.desc: Added notes on support of FB620P 2002-08-31 Frank Zago * PROJECTS AUTHORS TODO NEWS backend/dll.conf backend/Makefile.in backend/teco2.conf backend/teco2.c backend/teco2.h doc/.cvsignore doc/sane.man doc/Makefile.in doc/sane-teco2.man doc/teco/teco2.txt doc/descriptions-external/teco2.desc doc/descriptions/teco2.desc: added teco2 backend. 2002-08-30 Frank Zago * backend/teco1.c backend/teco1.h backend/teco1.conf doc/descriptions/teco1.desc doc/teco/teco1.txt doc/sane-teco1.man: Added support for another museum piece, the Dextra DF-600P, a rebadged VM3510. 2002-08-30 Rene Rebe * backend/avision.h backend/avision.c backend/avision.conf doc/sane-avision.man descriptions/avision.desc: latest avision backend 2002-08-29 Henning Meier-Geinitz * backend/v4l.c backend/v4l.h doc/descriptions/v4l.desc: Big clean up of Video for Linux backend. Removed old unused stuff from qcam and others including unused options and variables concerning direct port access. Removed wcam license (no "substantial portions of code" are used any more). Removed unuesed headers. Made lots of global variables local. Lots of checks for null pointer dereferences and errors when opening files or using ioctl/mmap. Added option to select grey or color mode. Added support for different channels. Fixed lots of options. Made sure ioctls are only used when necessary. Used vendor == "Noname". Added ??? comments for suspicious code. Lots of minor fixes and cleanup. Added version number: 1.0-2. * backend/v4l-grab.h: Deleted (unused). * backend/Makefile.in: Removed v4l-grab.h. * AUTHORS: Added myself as active maintainer of v4l. * doc/sane-v4l.man: Added warning about ALPHA software and bugs. Minor updates. * doc/descriptions/bh.desc doc/descriptions/canon630u.desc doc/descriptions/dmc.desc doc/descriptions/sm3600.desc doc/descriptions/umax1220u.desc: Minor fixes and updates for the new .desc file format. * TODO: Updated .desc and v4l section. Added entries for scanimage. * NEWS: First draft for 1.0.9. 2002-08-18 Oliver Rauch * frontend/scanimage.c, frontend/stiff.h, frontend/stiff.c: Added option "-i filename"/"--icc-profile filename": When image is saved in tiff format then this icc-profile is added to the tiff file as tiff tag 2002-08-25 Henning Meier-Geinitz * tools/Makefile.in: Fixed gamma4scanimage target for OS/2. * backend/v4l.c: Fixed device list. Added more verbose error output. Unified DBG messages. Fixed depth option. Reordered options in init_options to reflect real order. Added some markers (/* ??? */) for suspicious code. Used GNU indent style. * backend/v4l.h backend/v4l-frequencies.h backend/v4l-grab.h: Used GNU indent style. 2002-08-22 Frank Zago * backend/ricoh.c backend/ricoh.h backend/apple.c backend/agfafocus.c backend/abaton.c sanei/sanei_pv8630.c: Fixed some warnings. 2002-08-22 Frank Zago * doc/sane-teco1.man: Added Dextra scanner to the list of untested, but potentially supported, scanners. * doc/teco/teco1.txt: Added RELI 4830 inquiry string. * doc/descriptions/teco3.desc: Increased version number. * backend/: teco3.c, teco3.h: Added threshold option for black & white. * doc/descriptions/teco1.desc: Added 3 new (untested) scanners. * backend/teco1.conf: Fixed the scsi inquiry string for the VM4542. * backend/teco1.c backend/teco1.h: Enlarged the gamma table for the VM4542 (1024 entries as opposed to 256 for the previous scanners). 2002-08-22 Andras Major * backend/coolscan2.c doc/descriptions/coolscan2.desc doc/sane-coolscan2.man: updated to 0.1.7. 2002-08-21 Henning Meier-Geinitz * doc/.cvsignore: Added gamma4scanimage.1 2002-08-20 Henning Meier-Geinitz * PROJECTS: Added Hewlett-Packard ScanJet 4470C project. * tools/.cvsignore: Added gamma4scanimage. * tools/MAkefile.in: Added gamma4scanimage.c to DISTFILES. 2002-08-18 Oliver Rauch * tools/gamma4scanimage.c: Bugfix (atof->atoi) * tools/README: added gamma4scanimage * doc/Makefile.in: added manpage for gamma4scanimage * doc/gamma4scanimage.man: added manpage for gamma4scanimage 2002-08-17 Karl Heinz Kremer * backend/epson.[ch]: Fixed typo in variable name. Fixed IEEE-1394 problem with Perfection-2450. Fixed problem with older B3 level SCSI scanners that do not support the extended status request. 2002-08-17 Oliver Rauch * tools/gamma4scanimage.c: NEW: tool to create gamma table for scanimage * tools/Makefile.in: changed file to compile and install gamma4scanimage 2002-08-16 Gerhard Jaeger * doc/descriptions/plustek.desc: Update according to the new entry definitions * po/saneopts.es.po: More complete spanish translation (Thanks to Gustavo Vranjes) * backend/plustek.c backend/plustek-devs.c backend/plustek-pp.c backend/plustek-share.h backend/plustek-usb.c backend/plustek-usb.h backend/plustek-usbhw.c backend/plustek-usbimg.c backend/plustek-usbmap.c backend/plustek-usbscan.c backend/plustek-usbshading.c backend/plustek.conf: Major update to support better control of all LM983x based devices HP2200C, Mustek Bearpaw 1200 and 2400 are now working correctly 2002-08-15 Peter Fales * configure, acinclude.m4, aclocal.m4 - Another tweak to the allowed gphoto2 version numbers 2002-08-15 Stéphane Voltz * tools/umax_pp.c: upped version number * backend/umax_pp_low.c backend/umax_pp.c: corrected ppdev usage, minor option fix 2002-08-14 Henning Meier-Geinitz * AUTHORS: Made Ulrich Deiters the active maintainer of the canon backend. * TODO: Updated concerning HP backend and manufacturer names. 2002-08-07 Matthew Duggan * backend/canon_pp.c backend/canon_pp-dev.c backend/caon_pp-io.c backend/canon_pp-dev.h: Many changes: Bug fixes, less memory leaks (none left now?), more reliable, faster. Biggest changes are speculative reads (ask scanner to read more while data processing occurs) and more reliable sending of commands. Slight performance increase over previous version. * doc/sane-canon_pp.man: Added discussion noting that scanning greyscale in green is bad for colour. 2002-08-07 Peter Kirchgessner * backend/hp.h: Add missing structure member got_connect_type 2002-08-06 Peter Kirchgessner * doc/descriptions/hp.desc: Use Hewlett-Packard instead of HP 2002-08-01 Matthew Duggan * backend/canon_pp-dev.c: Misc bug fixes for fast machines, more useful debug messages, more reliable file reads/writes. 2002-07_31 Peter Kirchgessner * backend/hp.c, backend/hp-scl.c: hp-backend V0.96 Check device names to prevent USB-devices to be opened as SCSI. 2002-07-30 Henning Meier-Geinitz * po/Makefile.in: Use awk to filter out duplicate .po file headers. * po/umax_pp.de.po po/umax_pp.fr.po: Avoided including duplicate msgids. Made sure that header is not marked fuzzy. * Makefile.in doc/Makefile.in japi/Makefile.in tools/Makefile.in: Minor DISTFILES and distclean updates. * backend/mustek.c doc/descriptions/mustek.desc doc/mustek/mustek.CHANGES: Fixed some warnings. * TODO: Removed entries about "echo -e" and po problems. 2002-07-28 Henning Meier-Geinitz * tools/sane-config.in: Avoid using "echo -e" for compatibility reasons. 2002-07-27 Henning Meier-Geinitz * sanei/sanei_usb.c: Made libusb_timeout static. * TODO: Updated .desc entries. Removed entry about scanimage i18n support. 2002-07-25 Matthew Duggan * backend/canon_pp.c: Now always uses as much scanner buffer as possible, then feeds data to frontend. Results in large speed increase in ECP mode. Also fixed problems with saned compatibility. * backend/canon_pp.h: Added bytes_sent to scanner data structure. * doc/sane-canon_pp.man: Discuss hardware problems at high resolutions. 2002-07-25 Frank Zago * doc/sane.man doc/teco/teco3.txt doc/.cvsignore doc/sane-teco3.man doc/Makefile.in doc/descriptions-external/teco3.desc doc/descriptions/teco3.desc backend/teco3.c backend/teco3.h backend/teco3.conf backend/Makefile.in sane-backends/TODO sane-backends/AUTHORS backend/dll.conf: teco3 integration * sane-backends/Makefile.in: added an etags rule to create an emacs tag file * doc/backend-writing.txt: Enlarged the "SUBMITTING A NEW BACKEND" section. 2002-07-25 Matthew Duggan * backend/canon_pp.c: Now uses mm instead of pixel measurements, better behaved in xsane etc as a result. * backend/canon_pp.h: Tweaked up max buffer size to size available in hardware. * doc/descriptions/canon_pp.desc: increment version no. 2002-07-24 Frank Zago * doc/backend-writing.txt: Added a SUBMITTING CHECKLIST section. 2002-07-24 Henning Meier-Geinitz * frontend/stiff.c: Fixed 16 bit tiff output. * backend/test.c backend/test.conf backend/test.h backend/test-picture.c doc/descriptions/test.desc doc/sane-test.man: Added option invert-endianess to test 16 bit modes. 2002-07-24 Kazuya Fukuda * doc/descriptions/nec.desc: Remove unsupported scanner and update url. 2002-07-24 Matthew Duggan * src/canon_pp-io.c: Minor bugfix checking return of read 2002-07-23 Henning Meier-Geinitz * TODO: Updated desc file entries. Removed entries about microtek fprintf issue and fujitsu mem leak. 2002-07-23 Simon Munton * backend/pie.c: added consistency check for TL_X < BR_X and TL_Y < BR_Y 2002-07-23 Peter Fales * doc/descriptions/gphoto2.desc: Comment out :interface line 2002-07-23 Stéphane Voltz * doc/descriptions/umax_pp.desc: tagged 610P as unsupported 2002-07-22 Frank Zago * TODO: scheduled teco2 and teco3 backends for 1.0.9 2002-07-22 Matto Marjanovic * doc/descriptions/microtek.desc: Updated (status keywords, mostly). * backend/microtek.c: * backend/microtek.h: Replaced fprintf()/printf() with various DBG() statements. Removed #include ! Eliminated a few "unused parameter" compiler warnings. Updated the copyright date. Bumped version up to lucky 0.13.0 2002-07-23 Matthew Duggan * doc/sane-canon_pp.man: Added N640Pex to the man page too.. 2002-07-22 Chris Pinkham * doc/descriptions/artec.desc: Changed status of AT6 model to alpha. Deleted entries for USB & parallel port scanners which are unsupported. 2002-07-23 Matthew Duggan * doc/descriptions/canon_pp.desc: Added "N640P ex" model to supported scanners after report of successful test. Also, "EPP" really should have been "ECP". 2002-07-22 Henning Meier-Geinitz * frontend/scanimage.c: Fail if there are arguments without a preceding option on the command line. Fixed indenting. * TODO: Updated concerning .desc files. Added comment about USB problems on *BSD. 2002-07-21 Henning Meier-Geinitz * doc/descriptions/template.desc. doc/descriptions-external/template.desc.: Updated and added more examples. 2002-07-19 Oliver Rauch * doc/descriptions/umax.desc: added per scanner status 2002-07-17 Henning Meier-Geinitz * tools/sane-desc.c: Models are sorted alphanumerically now. Added support for priorities if one model is mentioned by two backends. Don't print tables when no suitable backend was found. More comments. Fixed some warnings. Code cleanup. * doc/Makefile.in: Added support for sane-desc.c. * doc/backend-writing.txt: Updated concerning sane-desc.c. * backend/mustek_usb.c backend/mustek_usb_high.h backend/mustek_usb_low.c doc/descriptions/mustek_usb.desc doc/mustek_usb/mustek_usb.CHANGES: Added manufacturer comment in mustek_usb.desc. Changed brightness to threshold. Fixed some warnings. Really check the status in usb_low_open. * sanei/sanei_usb.c: Check if file is already closed. 2013-07-15 Frank Zago * doc/descriptions/leo.desc doc/descriptions/sceptre.desc doc/descriptions/teco1.desc doc/descriptions/matsushita.desc: added new per backend status, added more scanners. 2002-07-15 Henning Meier-Geinitz * README.darwin: New file. Added some basic information for Darwin (Mac OS X) users. * Makefile.in: Added the new README files to DISTFILES. * tools/sane-find-scanner.c: Print USB descriptors (libusb) in -v -v mode. Print vendor and product names (when available). Rewrote libusb heuristics. * backend/Makefile.in: Removed deleted files from DISFILES. 2002-07-14 Henning Meier-Geinitz * include/sane/sanei_usb.h: Made buffer const SANE_Byte * in sanei_usb_bulk_write(). Try to call usb_find_busses only once. Make sure that already opened devices aren't opened again. Exit libusb scan loop if one device is found. Make error values more expressive. Check for missing config descriptors before using them to avoid segfaults. * README.openbsd: Updated concerning USB scanners. * README.openbsd README.netbsd: New files. Added some information about SCSI and USB problems. * tools/sane-find-scanner.c: Made verbosity option more flexible and added quiet option. In quiet mode, print only the devices we have found. Don't check USB devices with SCSI functions and vice versa if option force is not given. Don't do the libusb scan if an explicit device name was given. * doc/sane-usb.man: Updated supported platforms list. Minor wording improvements. * doc/sane-find-scanner.man: Documentation for the new options. Split USB and SCSI parts. * doc/descriptions/hpsj5s.desc: Added missing space in ":new :yes" line. * TODO: Added entry about desc files. Removed entries about sane-backends.html, sanei_usb.c libusb support, and the OS/2 strncasecmp problem. Updated entries about checking device file types and OS X in sanei_scsi.c. 2002-07-13 Simon Munton * doc/descriptions/pie.desc: removed unsupported scanner types, use :status :untested for untested models, added AdLib scanners that are supported 2002-07-13 Oliver Schwartz * backend/snapscan.c backend/snapscan.h backend/snapscan-options.c backend/snapscan-usb.c backend/snapscan-scsi.c backend/snapscan.conf: Snapscan backend version 1.4.15 - Improve support for Acer / Benq 5300 - use usb_sanei_read_bulk() and usb_sanei_write_bulk() - correct scan area for e52 with TPO * doc/description/snapscan.desc: - Adapted file to new syntax - Mark most scanners as "stable" 2002-07-12 Henning Meier-Geinitz * tools/sane-desc.c: Added backends mode with split tables. Fixed HTML colors. Spelling and other minor fixes. * doc/descriptions-external/hp4200.desc doc/descriptions-external/hpoj.desc doc/descriptions-external/ibm.desc doc/descriptions-external/lhii.desc doc/descriptions-external/niash.desc doc/descriptions-external/teco2.desc doc/descriptions-external/teco3.desc doc/descriptions-external/tevion9693usb.desc doc/descriptions-external/v4l2.desc: Minor fixes for the new sane-desc.c. Commented out version numbers and manpages. Added :status :untested if the comment suggests this. Fixed double and bad device types. * tools/sane-find-scanner.c: Added support for libusb. Made comments better readable and positioned after the output of the devices. Shorter output to fit into 80 chars. 2002-07-08 Karl Heinz Kremer * doc/descriptions/epson.desc: fixed typo in URL added :status lines to the scanner entries 2002-07-09 Abel Deuring * doc/descriptions/sharp.desc: Added ":status" lines to the scanner entries 2002-07-08 Henning Meier-Geinitz * doc/descriptions/sp15c.desc: Added missing slash in URL. * doc/descriptions/fujitsu.desc: Changed status to :beta. Added some URLs. * frontend/Makefile.in: Use LIBS = @LIBS@ to make sure that -lsyslog and -lsocket are linked for OS/2. * doc/descriptions/abaton.desc: Added :status :untested for Scan 300/S. * doc/descriptions/agfafocus.desc: Added status and comments (from the manpage). * doc/descriptions/ricoh.desc: Removed unused version and comment tokens. * doc/descriptions/s9036.desc: Changed :devicetype to :scanner. I guess :vidcam is not appropriate for a flatbed scanner :-) * doc/descriptions/net.desc doc/descriptions/test.desc: No bugs for quite some time, changed status to stable (knocking on wood). Updated comments. * doc/descriptions.txt doc/descriptions/template.desc. doc/descriptions-external/template.desc.: Updated concerning model status. * tools/sane-desc.c: Added support for :status :unsupported. 2002-07-07 Henning Meier-Geinitz * tools/sane-desc.c: Added status per model. Added quicklist for manufacturers. All manufacturer URLs are printed now. Code cleanup. Added status column for non-hardware devices. Fixed legends. Minor output fixes. * backend/mustek.c doc/descriptions/mustek.desc doc/mustek/mustek.CHANGES: Increased width and height of Paragon 800 and 600. * doc/descriptions/mustek_usb.desc: Changed status to stable (knocking on wood...). Added more Mustek URLs, fixed the existing one. Added per-model status. Added "compatible" comments. * doc/descriptions/mustek.desc: Fixed Mustek URL. Added Trust comment about similar names. Added comments about compatible scanners. Fixed Primax URL. * doc/descriptions/sp15c.desc: Fixed manpage link. Use "Fujitsu" consistently. Updated manufacturer links. 2002-07-06 Henning Meier-Geinitz * tools/sane-desc.c: Added
after backend name. Colors can be changed by macros now. Centered entries. Code cleanup. Manufacturer page split up into separate tables. Added links and comment display on the top of each table. Manufacturers are case-insensitive. OS/2 fix. * configure: Regenerated from configure.in. * doc/sane-usb.man: Mentioned control messages, usbfs, and more methods for permission adjustment. * configure configure.in include/sane/config.h.in: Added define for strcasecmp on OS/2. 2002-07-06 Max Vorobiev * backend/hpsj5s.c: Rewrote to depend on libieee1284. * backend/hpsj5s.h: Added register description defines * backend/hpsj5s.conf: Default config set for parport0 * backend/hpsj5s_int.h: Removed. All important stuff moved to hpsj5s.h * backend/Makefile.in: hpsj5s backend build depend on libieee1284 present * doc/hpsj5s.man: Comments about libieee1284 added * configure.in: hpsj5s backend will be compiled if libieee1284 detected 2002-07-05 Henning Meier-Geinitz * tools/.cvsignore tools/Makefile.in rools/README tools/sane-desc.c: Added new file sane-desc.c. sane-desc is inended as an replacement for the Emacs Lisp file sane-desc.el. It can generate ASCI and HTML lists from the backends' .desc files. * doc/sane.man: Added security to the list of topics that can be reported to me. * doc/descripions/template.desc. doc/descriptions-external/template.desc.: Updated concerning ":new". 2002-07-03 Henning Meier-Geinitz * configure configure.in include/sane/config.h.in: Added HAVE_LIBUSB #define. Check for usb_get_busses to make sure, we have at least version 0.1.6. Added OS/2 substitution for strncasecmp. * include/sane/sanei_usb.h sanei/sanei_usb.c: Added support for libusb. * README.netbsd: Removed. Shared libraries are supported since 1.0.8. * README.openbsd: Updated for libusb. * README: Added section about libraries like libiee1284 and libusb. * doc/doxygen-sanei.conf: Updated version number. * include/sane/sanei_usb.h sanei/sanei_usb.c: Used dn (device number) instead of fd (file descriptor) to avoid confusion. Updated doxygen documentation. * doc/sane-usb.man: Updated concerning libusb. Added tips for access via the kernel scanner drivers. 2002-06-29 Matthew Duggan * backend/canon_pp.c: Workaround for ppdev bug on <= Linux 2.4.19rc1. * backend/canon_pp-io.c: Fixed reading on hardware with small buffers. * doc/descriptions/canon_pp.desc: Status changed from new to alpha. 2002-06-28 Henning Meier-Geinitz * tools/sane-find-scanner.c: For HP-UX, check the complete /dev/rscsi/ directory for SCSI sscanners, not only the device files for the first two host adapters. * TODO: Added entries about sanei_usb/ibusb, config.h/OS/2, echo -e in shell scrips, and japi. Updated dll entry. Removed entry about sane-find-scanner. 2002-06-27 Henning Meier-Geinitz * doc/sane-fujitsu.man doc/sane-tamarack.man: Added newline at the end to keep Solaris' sed happy. * sanei/sanei_scsi.c: Set buffer size to 64 kB for SOLARIS_USCSI_INTERFACE (reported by Ed Randall ). 2002-06-25 Henning Meier-Geinitz * tools/sane-find-scanner.c: Added support for scanning whole directories for SCSI and USB device files. Moved SCSI and USB detection routines to their own functions. Renamed functions related to SCSI for clarity. Fixed indentation. 2002-06-21 Max Vorobiev * backend/hpsj5s.c: added fcntl.h include directive. 2002-06-15 Andras Major * doc/sane-coolscan2.man: corrected stupid mistake. 2002-06-14 Andras Major * backend/coolscan2.c doc/sane-coolscan2.man doc/descriptions/coolscan2.desc: updated to 0.1.6. 2002-06-11 Henning Meier-Geinitz q * README.solaris: Added comment about SCSI-only support. * doc/sane-usb.man: Updated list of backends that use sanei_usb. Fixed some minor spelling issues. Really used decimal numbers in example. 2002-06-09 Henning Meier-Geinitz * doc/descriptions.txt: New file. Specification of the format of backend description files (*.desc). * configure configure.in: Added some sanity checks. Configure now stops if one of the essential headers isn't found. * backend/dll.conf: Added entry for test backend (commented out). * doc/backend-writing.txt: Mentioned descriptions.txt. * bachend/test.c backend/test.conf backend/test.h backend/test-picture.c doc/descriptions/test.desc doc/sane-test.man: Added support for lineart mode with pixels_per_line not divisible by 8. Added support for padded image formats. Added documentation for all options. 2002-06-07 Henning Meier-Geinitz * backend/mustek.c doc/descriptions/mustek.desc doc/mustek/mustek.CHANGES: Fixed problem for Paragon 6000SP (color and gray modes produced solid black images). Minor cleanups. New version: 1.0-125. * config.guess config.sub: Updated to latest versions (from ftp://ftp.gnu.org/pub/gnu/config/). * configure configure.in: Print CPPFLAGS, CFLAGS, LDFLAGS and LIBS for easier debugging. Moved AC_SUBST to the variable definitions they belong to. 2002-06-05 Abel Deuring * doc/descriptions/sharp.desc: added "interface" parameter 2002-06-05 Stéphane Voltz * backend/umax_pp.c: reverted a change in color reordering for 1600P * po/Makefile.in po/umax_pp.de.po: added german translation courtesy of Heiko Schaefer 2002-06-04 Rene Rebe * backend/avision.c backend/avision.h backend/avision.conf: next backend version. Should work for the HP 5300 and HP 5370 now. Several other minor fixes. 2002-06-04 Stéphane Voltz * backend/umax_pp_low.c: relaxed scanner detection not to fail on some parports. 2002-06-02 Henning Meier-Geinitz * configure configure.in backend/Makefile.in: Added option --disable-local-backends to configure. Only net and dll will be compiled if set. Also a warning is printed. * TODO: Removed entries about config.h.in macros and disabling local backends. 2002-06-01 Henning Meier-Geinitz * backend/pnm.c-bad: Removed because it was out-of-date and buggy. The features of this modified pnm backend are now in test.c. 2002-05-30 Oliver Rauch * corrected wrong version number in umax.desc 2002-05-30 Stéphane Voltz * backend/umax_pp_mid.c backend/umax_pp.c: software lineart for real this time. 2002-05-30 Henning Meier-Geinitz * doc/saned.man: Added explicit path to tcpd example. * sanei/sanei_usb.c: Explicitly initialize vendorID and productID. * sanei/sanei_config.c sanei/sanei_config2.c: Fixed possible segfault. 2002-05-30 Stéphane Voltz * backend/umax_pp_low.c backend/umax_pp.c: fixed 1600P detection, IRIX compile fixes, final bits of software lineart 2002-05-29 Henning Meier-Geinitz * AUTHORS backend/Makefile.in backend/dll.conf backend/hpsj5s.c backend/hpsj5s.conf backend/hpsj5s.h backend/hpsj5s_int.h doc/sane-hpsj5s.man doc/descriptions/hpsj5s.desc doc/.cvsignore doc/Makefile.in doc/sane.man: Added hpsj5s backend for the Hewlett-Packard ScanJet 5S parport scanner (from Max Vorobiev ). * PROJECTS: Removed hpsj5s. 2002-05-29 Frank Zago * configure.in: Added test for long long. Fixed declaration for HAVE_ASM_IO_H * configure include/sane/config.h.in: regenerated * frontend/tstbackend: add test for long long. 2002-05-28 Frank Zago * po/matsushita.fr.po po/saneopts.fr.po po/sceptre.fr.po po/teco1.fr.po po/umax.fr.po backend/leo.c backend/leo.h backend/matsushita.c backend/matsushita.h backend/sceptre.c backend/sceptre.h backend/teco1.c backend/teco1.h frontend/tstbackend.c: new email address. 2002-05-28 Henning Meier-Geinitz * configure configure.in: Warnings enabled by default. * sanei/sanei_scsi.c: Fixed request-sense handling for OpenStep (from Oliver Schirrmeister ). * backend/mustek.c doc/descriptions/mustek.desc doc/mustek/mustek.CHANGES: Set X minimum value to 0 for Paragon 6000SP. 2002-05-27 Oliver Rauch * configure.in/configure: V_EXTRA=-cvs Older entries can be found in ChangeLog-1.0.8. backends-1.3.0/ChangeLogs/ChangeLog-1.1.1000066400000000000000000002015611456256263500175300ustar00rootroot00000000000000commit 332edc8b7ce642bb06132cf204a8c2dd57720bce Author: Povilas Kanapickas Date: 2022-01-19 18:34:04 +0200 Release 1.1.1 Note that there is no 1.1.0 release as make distcheck was broken there. The failure depended on the exact release number, thus it was not noticed earlier. commit 1c805281f2b93b2ea9f2ee98a1d5a00775a5a53a Author: Povilas Kanapickas Date: 2022-01-19 18:33:50 +0200 NEWS: Update release version commit 2d75530344eb2d853e066fdfa93e09ffa4fc7c39 Merge: 2cd4058942f6 c2a77be45b31 Author: Povilas Kanapickas Date: 2022-01-19 15:45:49 +0000 Merge branch 'release-1.1.x-fix-distcheck' into 'release-1.1.x' [Release 1.1.x] testsuite: Allow any 1.x.y version when comparing sane-desc results See merge request sane-project/backends!685 commit c2a77be45b317f542bf3215407c0aeeccd9af45b Author: Povilas Kanapickas Date: 2022-01-19 17:29:59 +0200 testsuite: Allow any 1.x.y version when comparing sane-desc results commit 2cd4058942f668397c29d13c445515c7c3cc27ca Author: Povilas Kanapickas Date: 2022-01-18 23:04:46 +0200 Release 1.1.0 commit 44d038345897d6f348fe2706665bf841ab733e00 Merge: b18fb290a975 a00a5f2f2cb6 Author: Povilas Kanapickas Date: 2022-01-18 20:58:44 +0000 Merge branch 'release-1.1.x-notes-1.1.0' into 'release-1.1.x' [Release 1.1.x] Release notes for 1.1.0 See merge request sane-project/backends!684 commit a00a5f2f2cb63871aed5a6952353221f3ccfbd5b Author: Povilas Kanapickas Date: 2022-01-18 22:40:39 +0200 NEWS: Release notes for 1.1.0 commit b18fb290a975c7ce6ebb47a85aca50342deaeee2 Merge: 3f62c9132a55 bb0bee92f668 Author: Povilas Kanapickas Date: 2022-01-18 20:33:08 +0000 Merge branch 'release-1.1.0-releasing-procedure' into 'release-1.1.x' [Release 1.1.0] Update releasing procedure See merge request sane-project/backends!683 commit bb0bee92f6686429af64c210ae21115f410d0a70 Author: Povilas Kanapickas Date: 2022-01-18 22:12:19 +0200 doc: Update the releasing documentation commit 3738eb9e42012df03e2ff22261107374b717b9e5 Author: Povilas Kanapickas Date: 2022-01-18 22:12:18 +0200 newsfragments: Document the misc news fragment type commit 5f08348068d3a8e047466a61240af0df49dc06b9 Author: Povilas Kanapickas Date: 2022-01-18 22:12:17 +0200 Setup release notes build using towncrier commit 3f62c9132a55b129b0159c0cc9ffd6e4e03ea093 Merge: 5e463f4c91ad a3031367fec2 Author: Povilas Kanapickas Date: 2021-12-30 18:39:57 +0000 Merge branch 'release-1.0.33-genesys-remove-gl847-unsupported-resolutions' into 'release-1.0.33' [1.0.33] genesys: Remove unsupported resolutions on LiDE 100 and 200 See merge request sane-project/backends!681 commit a3031367fec2c1137b156fd93771a900b70af7e3 Author: Povilas Kanapickas Date: 2021-12-30 18:51:09 +0200 newsfragments: Add release note for MR !675 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/675 (cherry picked from commit 2f31fea9d351ed99d8e0a7bf700261926a7e88c7) commit 773f0e506189bb87f441619eef0e7c84e61d773f Author: Povilas Kanapickas Date: 2021-12-28 02:49:59 +0200 genesys: Remove unsupported 75 and 100 dpi resolutions on LiDE 200 (cherry picked from commit eca148dbf44611b357bd01b4672b83737c114a28) commit fe6fe45098d067c070f95e9508c8a11e82eafdbd Author: Povilas Kanapickas Date: 2021-12-28 02:49:58 +0200 genesys: Remove unsupported 75 and 100 dpi resolutions on LiDE 100 (cherry picked from commit c86564a3e39c5407c770dca48ce37dee3c142b3c) commit 0dc467a3348fff930f09d628df0d3e1e0cc3cb0d Author: Povilas Kanapickas Date: 2021-12-28 02:49:57 +0200 genesys: Log the target motor speed in case it can't be acquired (cherry picked from commit f5d5928346e3a49914a6cd8cdd00d3e6f4556282) commit 5e463f4c91ad06b1e81f0e923760b7a46c1f47d3 Merge: a45a29d9487c e0425c737d5e Author: Povilas Kanapickas Date: 2021-12-30 18:28:12 +0000 Merge branch 'release-1.0.33-backport-genesys-remove-gpl-exception' into 'release-1.0.33' [1.0.33] genesys: Remove exception for the GPL license See merge request sane-project/backends!679 commit e0425c737d5e7cf79c9b29a5f42290d30722796a Author: Povilas Kanapickas Date: 2021-12-30 19:16:54 +0200 genesys: Remove exception for the GPL license Several files already don't have the exception that allows uses of the code that are additional to the GPL license. I'm no longer comfortable granting this exception for my subsequent contributions, thus the exception has been removed. (cherry picked from commit 59506f866d3ac4c2fc2bdadf66865e38f8e86ac4) commit a45a29d9487c31eb369e22cf9911ee0e7824e7df Merge: 87f27311f751 2b6fa1056d00 Author: Povilas Kanapickas Date: 2021-12-30 18:21:01 +0000 Merge branch 'release-1.0.33-backports' into 'release-1.0.33' [1.0.33] Backport release notes See merge request sane-project/backends!678 commit 2b6fa1056d00137f6fd8df9c7ea946d6b0d8265b Author: Povilas Kanapickas Date: 2021-12-30 20:05:22 +0200 newsfragments: Add release note for MR !673 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/673 (cherry picked from commit 9887b162d90f3763fcfa9954c4636804d56c9a1d) commit ef45427c53f7d0b9786c935618a065da29b98c0a Author: Povilas Kanapickas Date: 2021-12-30 20:05:21 +0200 newsfragments: Add release note for MR !622 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/622 (cherry picked from commit fec5e8917feb04167b5d497dce20415bba3e74ba) commit 4a2ebc7feef7632d595e190cff43973baf387075 Author: Povilas Kanapickas Date: 2021-12-30 20:05:20 +0200 newsfragments: Add release note for MR !659 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/659 (cherry picked from commit cb6a7d0a90ce1c41b59bcc817daf911a6b2c8206) commit d1690a044de986a27be742f29c26ebb6746a3b59 Author: Povilas Kanapickas Date: 2021-12-30 20:05:19 +0200 newsfragments: Add release note for MR !669 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/669 (cherry picked from commit 77baf258b6305666409c54f9e051afe4fb1ac1b2) commit 78c188542a78c416c6254dcbea99922ef68e430b Author: Povilas Kanapickas Date: 2021-12-30 20:05:18 +0200 newsfragments: Add release note for MR !667 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/667 (cherry picked from commit 3b91ab76e6438cec380c4bf2d946347e0909f403) commit 9864172a11caef7bc70ee2ba07e67fbe6852dcb2 Author: Povilas Kanapickas Date: 2021-12-30 20:05:17 +0200 newsfragments: Add release note for MR !621 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/621 (cherry picked from commit 8f0e1d2b483ee83b8675f62ae19f0eea631e2a57) commit c13555df6f3fb24b672e4a7a569534434c98f56e Author: Povilas Kanapickas Date: 2021-12-30 20:05:16 +0200 newsfragments: Add release note for MR !620 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/620 (cherry picked from commit 37603742d52c8b3226ec7f4fb54cb2362ff0f68c) commit 53609d6d09e90c6dec5e1a12df501e079cb4f226 Author: Povilas Kanapickas Date: 2021-12-30 20:05:15 +0200 newsfragments: Add release note for MR !619 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/619 (cherry picked from commit f02ee333b7f75d495ea9cdf4ec2724adb64794c4) commit c0f0c8246751939ca399f44ac7cb6d886b9e1efc Author: Povilas Kanapickas Date: 2021-12-30 20:05:14 +0200 newsfragments: Add release note for MR !618 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/618 (cherry picked from commit 69e70e898624880da18be48d6cf22eacf3eababb) commit c7b3ffc5bbb7c490f5804440b4482376e09787c1 Author: Povilas Kanapickas Date: 2021-12-30 20:05:13 +0200 newsfragments: Add release note for MR !617 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/617 (cherry picked from commit c6601d96059b1137bf620fe7b3a13426c56607c0) commit 465e5867cb7ccfebc79468010440cb5c8da149f6 Author: Povilas Kanapickas Date: 2021-12-30 20:05:12 +0200 newsfragments: Add release note for MR !613 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/613 commit 156eba8dea3ceed80952f7434344a7cbbc10b928 Author: Povilas Kanapickas Date: 2021-12-30 20:05:11 +0200 newsfragments: Add release note for MR !612 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/612 (cherry picked from commit a1c0b4e36cfb356674988c56e478218399fcd583) commit 4931efc5c7564269bd30306ff975611f0fad3667 Author: Povilas Kanapickas Date: 2021-12-30 20:05:10 +0200 newsfragments: Add release note for MR !609 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/609 (cherry picked from commit 295352c529047a0696a87b74d64d2b2996210a93) commit edfe281aa7699eaf696eaa182f1189ae0cfca36b Author: Povilas Kanapickas Date: 2021-12-30 20:05:09 +0200 newsfragments: Add release note for MR !607 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/607 (cherry picked from commit 10ab653c07f62363e1a7b206a7292d235dfeaf77) commit c3d537c586d99d7f26320e23d5d169406287706d Author: Povilas Kanapickas Date: 2021-12-30 20:05:08 +0200 newsfragments: Add release note for MR !605 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/605 (cherry picked from commit 41ba2922c72ced8ef130bd6dcf5b6afbacfca4e8) commit a0fc5c871cbc5e84489a84bcc2cf6db331fbc649 Author: Povilas Kanapickas Date: 2021-12-30 20:05:07 +0200 newsfragments: Add release note for MR !604 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/604 (cherry picked from commit d0484425b497f2b698cedfd2f607ed17efc63baf) commit 68fddf92f3b30f8969a785455a48bd0dadd73e0f Author: Povilas Kanapickas Date: 2021-12-30 20:05:06 +0200 newsfragments: Add release note for MR !651 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/651 (cherry picked from commit bda6796748e08100e27bc17d9004555e4614f1c6) commit 9bd3d6ddf248968978249cb85aab2aa919e2018a Author: Povilas Kanapickas Date: 2021-12-30 20:05:05 +0200 newsfragments: Add release note for MR !650 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/650 (cherry picked from commit 007c457bb6798377f7767e3e1b1174216cef366a) commit 6545aa5ea8b1ac99a5e0578b701e61ecbb4b1b6f Author: Povilas Kanapickas Date: 2021-12-30 20:05:04 +0200 newsfragments: Add release note for MR !550 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/550 (cherry picked from commit 7f28c1fa00cbd6f571de737d46f46c8c9f01dd9b) commit dcfbf4f6d564842de9b406fa6aa087ab6bc83044 Author: Povilas Kanapickas Date: 2021-12-30 20:05:03 +0200 newsfragments: Add release note for MR !647 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/647 (cherry picked from commit 2c4ebcd9241d96fbc0950baa02a41b5a70be52ce) commit f5ed3eaf71d64ce012ab6ba6f6adfd384ad14ab0 Author: Povilas Kanapickas Date: 2021-12-30 20:05:02 +0200 newsfragments: Add release note for MR !645 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/645 (cherry picked from commit 494e2b55587beacb99d9fb09efaaf588aafa15fc) commit 4634c4bde3a5891f25ecdddd7edf3e275be8dd11 Author: Povilas Kanapickas Date: 2021-12-30 20:05:01 +0200 newsfragments: Add release note for MR !643 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/643 (cherry picked from commit 24a5d6f4c7bfd999a4e88f0efecc32a8ec36e705) commit 094750b1dfbdd044fb1dd409e6e00387a8b63bb8 Author: Povilas Kanapickas Date: 2021-12-30 20:05:00 +0200 newsfragments: Add release note for MR !642 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/642 (cherry picked from commit 885574ccc2a98ac034455f3c2dc391e261238da8) commit 506f6f5085ab8835ec60d7c72fe84086272ae052 Author: Povilas Kanapickas Date: 2021-12-30 20:04:59 +0200 newsfragments: Add release note for MR !641 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/641 (cherry picked from commit 692ac5d8d96d36d9f7e49996d6c8bd8c48410887) commit 9a06a54452b97279b9b12a38723f4a09fae87beb Author: Povilas Kanapickas Date: 2021-12-30 20:04:58 +0200 newsfragments: Add release note for MR !640 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/640 (cherry picked from commit f5e6f926392be74cda787a8d5248a5b7a03f745c) commit d9b1139927b0267c8cc7c6e0ce4f20ef8819067c Author: Povilas Kanapickas Date: 2021-12-30 20:04:57 +0200 newsfragments: Add release note for MR !639 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/639 (cherry picked from commit 01614368715e93956bcc116b55e80c88dbc285f5) commit 0a8e8a3d4d557a971291c3fdb1d951eda2631167 Author: Povilas Kanapickas Date: 2021-12-30 20:04:56 +0200 newsfragments: Add release notes for MR !638 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/638 (cherry picked from commit ab5b9b2293e0b689ce51baa951a1859f95fa3455) commit 6fc8dc5a020b833bb3fcb57e16fb8e8665166902 Author: Povilas Kanapickas Date: 2021-12-30 20:04:55 +0200 newsfragments: Add release note for MR !635 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/635 (cherry picked from commit 77c3626c4b5b108b66f8764940d92f8da3e5a545) commit 33ec34cfe3b047f04eddad51520a640f5d653209 Author: Povilas Kanapickas Date: 2021-12-30 20:04:54 +0200 newsfragments: Add release note for MR !634 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/634 (cherry picked from commit f89a4be7049f498e0fc5e7a698cc6751bc86495a) commit 247fc90a39f26d70e10ecccc7484dfd01c530a2f Author: Povilas Kanapickas Date: 2021-12-30 20:04:53 +0200 newsfragments: Add release note for MR !629 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/629 (cherry picked from commit 10fea431c39c118996f3d8624ea04b24c43792d3) commit cd0b7ed82f5c4eb5f5bb983b17bce5755fac5a6b Author: Povilas Kanapickas Date: 2021-12-30 20:04:52 +0200 newsfragments: Add release note for MR !627 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/627 (cherry picked from commit 0e20fd74dee4c0ae9140d1c023ae66e6a2df621d) commit c21f16c11aefad41cd21bfc0b8fba919319b75ce Author: Povilas Kanapickas Date: 2021-12-30 20:04:51 +0200 NEWS: Move unreleased release notes to newsfragments directory (cherry picked from commit 8ca61ed2dae709810a91919347d7890bcda55998) commit 0169fe6f842ce543d1e2caa40dc2bcec5a8262a1 Author: Povilas Kanapickas Date: 2021-12-30 20:04:50 +0200 newsfragments: Document how to provide release notes (cherry picked from commit 74cd7d0a7bb4d228f72c4957e9914634c2033cbc) commit 87f27311f75182ea9a431e25d75485b263abbcc3 Merge: a38919bf7f14 7353cd598ba2 Author: Povilas Kanapickas Date: 2021-12-27 21:26:53 +0000 Merge branch 'release-1.0.33-backports' into 'release-1.0.33' [1.0.33] Miscellaneous backports See merge request sane-project/backends!674 commit 7353cd598ba2a22262268dd6ef1649530ff05433 Author: Povilas Kanapickas Date: 2021-12-27 22:21:18 +0200 genesys: Remove unneeded per-scanner register setup These register values are overwritten later. commit 022f63161102fc00c377b5b4a2754095bb7a0707 Author: Povilas Kanapickas Date: 2021-12-27 22:21:17 +0200 genesys: Fixed offset calibration on certain gl841 devices commit 374ba39e46d26aca072b529c9028f51cbdaed959 Author: Povilas Kanapickas Date: 2021-12-27 22:21:16 +0200 genesys: Use consistent initial exposure for led calibration on gl841 commit 9527a9e6231fb88d796e5b3840c2ec73bfd52e37 Author: Povilas Kanapickas Date: 2021-12-27 22:21:15 +0200 genesys: Fix inconsistent exposure values in led calib in testing mode commit 8eb2c0e4044faf150f166e32a81837a192bcde61 Author: Povilas Kanapickas Date: 2021-12-27 22:21:14 +0200 genesys: Ensure sensor exposure is up-to-date when scanning on gl841 commit dd1abe938031be3948c3deeb6fa26f2afc082ce4 Author: Povilas Kanapickas Date: 2021-12-27 22:21:13 +0200 genesys: Reuse gl124 led calibration acceptability criteria for gl841 commit 9611b9d757ed33bdbe72b404d88973f271e80d18 Author: Povilas Kanapickas Date: 2021-12-27 22:21:12 +0200 genesys: Remove no longer used led calibration fallback code on gl841 This was used only on scanners which did not have calibration target intensity value. All gl841 scanners now have such setting, so the code can be removed. commit b3f6637ae2eca2635f0e7a82ac1cf8014fd73572 Author: Povilas Kanapickas Date: 2021-12-27 22:21:11 +0200 genesys: Specify target white level for all gl841 devices commit 4d1246b5070548ccfffbae34f9ae4f978bbee774 Author: Povilas Kanapickas Date: 2021-12-27 22:21:10 +0200 genesys: Remove duplicate register write during led calibration on gl841 commit 408ee7a770964229b987399526b1eb7c88e53de8 Author: Povilas Kanapickas Date: 2021-12-27 22:21:09 +0200 genesys: Reset registers after move during led calibration on gl841 commit 9669c6c33584cdae82ed341bfa36c5716e814c51 Author: Povilas Kanapickas Date: 2021-12-27 22:21:08 +0200 genesys: Remove erroneous register write on LiDE 80 commit bafd6e134a1a3599a71319379963ea8a280ebf10 Author: Povilas Kanapickas Date: 2021-12-27 22:21:07 +0200 genesys: Clean up exposure calibration on gl841 commit 4fce71b55b158283c8e369f21f9752b1e3f61b92 Author: Povilas Kanapickas Date: 2021-12-27 22:21:06 +0200 genesys: Use more robust gain calibration on gl841 commit cc3bf08055601ec8f3896351cfefbd1b30601d89 Author: Povilas Kanapickas Date: 2021-12-27 22:21:05 +0200 genesys: Remove duplicate register writes in gl841 calibration commit 7dcffcf75f6633acb6f1f3da6d50b75bdb42bf88 Author: Povilas Kanapickas Date: 2021-12-27 22:21:04 +0200 genesys: Use host-side gray instead of device-side true-gray on gl841 True gray mode is not handled correctly by devices: they have bugs that lead to incorrect LED color being emitted (e.g. dark red) and thus this feature is completely unusable. At least LiDE 35/40/50, LiDE 60 and LiDE 80 are affected. Simpy disabling true-gray unfortunately leads to even worse outcome because the scanner then simply proceeds to perform a color scan. To work around these problems we do a normal color scan and then produce gray output based on the color data. This will satisfy the use cases when correct gray is needed. In cases when it is sufficient to construct gray from a single color channel, the color filter setting could be used. commit 440ec4f80cdc4e37c034042c135b531a7f69fe38 Author: Povilas Kanapickas Date: 2021-12-27 22:21:03 +0200 genesys: Implement host-side gray support commit 6bec3fcfa69d3ec63fe53cb4f156d0a22c774878 Author: Povilas Kanapickas Date: 2021-12-27 22:21:02 +0200 genesys: Implement image pipeline for merging color channels to gray commit b7b37b4f4502c61f67a6ada6b5520d59435a298c Author: Povilas Kanapickas Date: 2021-12-27 22:21:01 +0200 genesys: Rename ImagePipelineNodeMergeMono{Lines -> LinesToColor} commit e64aad283f240755e1e9d049a5589541f251afef Author: Povilas Kanapickas Date: 2021-12-27 22:21:00 +0200 genesys: Remove true_gray variable by deriving its value directly commit 72d0c39656a30fe18b24bc23679344377a80d86e Author: Povilas Kanapickas Date: 2021-12-27 22:20:59 +0200 genesys: Fix incorrect scan exposure setup on gl841 When sensor exposure values are significantly different from each other the total scan exposure will be too low leading to the device becoming confused and significantly commit 7b25c36c1544ab5988fe17ca8ca544cfdb21cc1f Author: Povilas Kanapickas Date: 2021-12-27 22:20:58 +0200 genesys: Inline gl841_get_led_exposure() commit 341b2ff6e6ec4a1e4b39bda6170972e0d4781621 Author: Povilas Kanapickas Date: 2021-12-27 22:20:57 +0200 genesys: Remove broken two-table feeding support from gl841 At least on LiDE 50 two-table feeding caused unexplained motor spin-up failures on certain motor exposures. Various register modifications showed that there's high likelihood on device-side bug. Even the official drivers don't use proper two-table feeding. commit df20a7a46e19848307b025c9450a82bab67aff9e Author: Povilas Kanapickas Date: 2021-12-27 22:20:56 +0200 genesys: Remove unused two-table feeding support from gl847 commit 3787dfdb4b846473fa0e2c224655e1273ad33037 Author: Povilas Kanapickas Date: 2021-12-27 22:20:55 +0200 genesys: Remove unused two-table feeding support from gl846 commit 491a08ba96cabfe682e0fdd9c2248ea47ecc0bb3 Author: Povilas Kanapickas Date: 2021-12-27 22:20:54 +0200 genesys: Remove unused two-table feeding support from gl124 commit c6df5fd69a6417a2a668733bf4af9a799865f8c6 Author: Povilas Kanapickas Date: 2021-12-26 14:53:15 +0200 genesys: Enable gamma setting when contrast or brightness is adjusted Brightness and contrast adjustments are handled via gamma tables and thus gamma functionality is required for these settings to have any effect. Previously if the device has a sensor with identity gamma (gamma = {1, 1, 1}), then gamma tables were turned off, consequently brightness and contrast settings had no effect. The underlying issue was identified by Gunnar Hjalmarsson and STK. commit 2cc2f8a26b815698e7152bff6d51ed93eb7af050 Author: Povilas Kanapickas Date: 2021-12-26 14:53:14 +0200 genesys: Store contrast and brightness adjustments in session params commit a38919bf7f14ca50f43e425bd3db8f698cfdd0cc Merge: 65b66849849c eb097b632c70 Author: Povilas Kanapickas Date: 2021-12-25 02:23:57 +0000 Merge branch 'release-1.0.33-backports' into 'release-1.0.33' [1.0.33] Miscellaneous backports See merge request sane-project/backends!668 commit eb097b632c7067f02d270df8ac85d36900d4dd4a Author: Povilas Kanapickas Date: 2021-12-15 23:51:51 +0200 genesys: Address wrong indentation warning This is separate commit because the previous commit introducing this warning is completely unrelated and would not benefit from this change being included. (cherry picked from commit 75801bffd30105511e76a74ef6db8714ba1b3c7f) commit 944183c3796d4b57e129a39c76f4e53040890ae6 Author: Ralph Little Date: 2021-12-07 22:16:13 -0800 genesys: Added transparency button option This is introduced primarily for the ScanJet G4010 which has "Scan Film" buttons. (cherry picked from commit 8347191b5fd597bd2b34e74cc57b2d9849b653f8) commit b83f7d3cadd13bd19d5dfb2a0f5b0ffbf0f0d9a5 Author: Ralph Little Date: 2021-05-05 12:37:11 -0700 genesys: updated button support for HP Scanjet G4010 (cherry picked from commit fa414e46b1c724721eabef690fa622c2c3d0478a) commit 0114403ac58f1a773510ca17a14b70ef9d3d5e76 Author: Peter Seiderer Date: 2021-03-15 20:53:55 +0100 genesys: fix gcc-4.8 compile Fixes: genesys/utilities.h:229:23: error: invalid initialization of non-const reference of type 'std::basic_ios&' from an rvalue of type '' stream_{stream} ^ genesys/image_pipeline.cpp:715:19: error: invalid initialization of non-const reference of type 'genesys::ImagePipelineNode&' from an rvalue of type '' source_{source} ^ Signed-off-by: Peter Seiderer (cherry picked from commit 1db13a713f281c66d3eaebab06a33d735ea9c20e) commit 63fffd74038e712cc05e879f33c03b111bfce0f7 Author: Povilas Kanapickas Date: 2021-12-25 03:06:29 +0200 genesys: Improve gray scan quality on LiDE 110 The fix has been suggested by Matthew Petroff (cherry picked from commit ea0e57a05e728a2d8efe38494cfd9a6ae0591b3f) commit cf8835625a42c28937099e3609cb309ab3b9fd91 Author: Povilas Kanapickas Date: 2021-12-25 03:06:28 +0200 genesys: Improve gray scan quality on LiDE 120 The fix has been suggested by Matthew Petroff (cherry picked from commit 8ceb1dde8da201e6cccd4cc64d2247993b6b2cc8) commit 3a890c7100fbbcb274a912546db5fd103362166e Author: Povilas Kanapickas Date: 2021-12-25 03:06:27 +0200 genesys: Improve gray scan quality on LiDE 210 The fix has been suggested by Matthew Petroff (cherry picked from commit 711a3c5c143fdf6b5b05a874c0a522b7796af874) commit b16baa642b0e00928faf480b57709c42114faa80 Author: Povilas Kanapickas Date: 2021-12-25 03:06:26 +0200 genesys: Improve gray scan quality on LiDE 220 The fix has been suggested by Matthew Petroff (cherry picked from commit 723eaa5917c197cad2c4621f5d2a3f83451c0ecc) commit 8414a1db9bcab7f16b79d67dbefa652f0e420b10 Author: Povilas Kanapickas Date: 2021-12-25 03:06:25 +0200 genesys: Move gl124 0x0c reg definition to sensor tables (cherry picked from commit bd0f15f5855b01c698f3a2da623ebc759401ed66) commit bffda89bc7893fac96d0ac1f65fe7d90c5af215a Author: Povilas Kanapickas Date: 2021-12-13 22:38:49 +0200 backend/Makefile: Reduce excessive line lengths (cherry picked from commit ed51223cfb9d1bfac30bd21fc5f567e335875d58) commit 1c8bb97b69b4987f1f5d1a854739323bf7e0d3a0 Author: Wolfram Sang Date: 2021-11-21 21:11:27 +0100 epson2: add driver to main sane manpage Reported missing by A E Lawrence via mail. Thanks! (cherry picked from commit bb941829822cb9a22989434e59be2009105e1975) commit d6189aa7894e46ab1fe698c9d38358e2d876b1ef Author: Wolfram Sang Date: 2021-12-03 12:34:17 +0100 epson2/epsonds: add new IDs provided by Epson Sent by Nakamura Iwao via sane-devel mailing list on Nov, 29th. I only sorted the epson2 descriptions alphabetically. Subject: [sane-devel] Additional model support for epson2 / epsonds backend Message-ID: (cherry picked from commit 91c24b29550e252dad2dace10ccc1ddcfa62f4b4) commit 5152108769dca77f5a75e2965b2852a4bd76eb2e Author: Ordissimo Date: 2021-10-20 22:25:10 +0200 Fix segfault sane-escl. (cherry picked from commit 72992f4e25ddbc000b79a2a66f41e5be98dc2120) commit 59b669ae3c774cca2da2487cd87762fc8ca1b707 Author: Jindřich Makovička Date: 2021-02-14 13:16:48 +0100 escl: Follow the 302 redirects Recent ipp-usb redirects from http://127.0.0.1 to http://localhost (cherry picked from commit 1b5344ba56a0d49ff583e734705acae5a88088d9) commit 65b66849849cc7f64e49f10a8ebe79d96acffbe6 Merge: 66f2673d714d 88934578ec51 Author: Ordissimo Date: 2021-09-26 06:35:31 +0000 Merge branch 'pdf-on' into 'master' The name of the device, allows to disable the use of the PDF format. See merge request sane-project/backends!651 commit 88934578ec5150cbd479978c51f85843a5bb9d54 Author: Ordissimo Date: 2021-09-26 06:35:31 +0000 The name of the device, allows to disable the use of the PDF format. commit 66f2673d714d7f0562f194b2d4dbc7843fc06988 Merge: 55ac7b306024 d25f5db73924 Author: Ordissimo Date: 2021-09-22 15:14:15 +0000 Merge branch 'add-format-pdf' into 'master' Add format pdf. See merge request sane-project/backends!648 commit d25f5db73924dcc89647ae9f7890ebb34653b5b9 Author: Ordissimo Date: 2021-09-22 15:14:15 +0000 Add format pdf. commit 55ac7b3060243e80e416e30824600877cfd5de0c Merge: 8ad3b1127f58 d12533fced66 Author: Wolfram Sang Date: 2021-09-22 10:18:26 +0000 Merge branch 'kkaempf/add_Reflecta_RPS_10M' into 'master' Add Reflecta RPS 10M to pieusb See merge request sane-project/backends!650 commit d12533fced6660a34043950ec14c63b80cac0219 Author: Klaus Kämpf Date: 2021-09-21 14:06:56 +0200 Add Reflecta RPS 10M to pieusb (aka Pacific Image PrimeFilm XAs) Thanks to Thomas Wilshaw for testing. Signed-off-by: Klaus Kämpf commit 8ad3b1127f5875b31c29162d198375b0940eca04 Author: Ralph Little Date: 2021-09-20 20:31:33 -0700 po: en_Gb translation. Nothing much to do. Just a couple of new msgs from the epsonds backend update. commit c540a3a354edb9accb09c035be81a2894c7fa8c0 Author: Ralph Little Date: 2021-09-20 20:09:29 -0700 po: update po files prior to translations for release 1.0.33 commit 66d65ba37d3b07b256645b8433cbebfc9fc817b6 Merge: a3558af0fc08 5698be8cce8a Author: Ralph Little Date: 2021-09-21 03:05:57 +0000 Merge branch 'uk_update1' into 'master' po: Update Ukrainian translation See merge request sane-project/backends!649 commit 5698be8cce8a1b01bf2dacd05058509f1d2af07e Author: Yuri Chornoivan Date: 2021-09-20 19:47:14 +0300 po: Update Ukrainian translation commit a3558af0fc08e40a4b7ee0d9d565df6451a11bed Merge: 54e3fb2094b7 0400e23d30bd Author: Wolfram Sang Date: 2021-09-18 18:38:12 +0000 Merge branch 'epson2-uninitialized' into 'master' epson2: fix up uninitialized variables See merge request sane-project/backends!636 commit 0400e23d30bd8b321e2904b89ca8a1980242f7a7 Author: Adriaan de Groot Date: 2021-06-14 13:42:25 +0200 uninitialized: set *status on all returns out of e2_ssend() commit 54e3fb2094b7c406913d85a7eb653c27abbed052 Author: pimvantend Date: 2021-09-06 16:19:04 +0200 Added the lide70 news to NEWS, in view of the upcoming release. Removed gamma remark from lide70 manpage: default color quality is getting better, though still not perfect. commit 1d191ab8017737f5c24109a29a07503275a169fe Merge: 563afbe0fa41 e1b5c43b5636 Author: Ordissimo Date: 2021-08-31 20:09:33 +0000 Merge branch 'escl-add-models' into 'master' Escl add models Epson ET-6100 See merge request sane-project/backends!647 commit e1b5c43b56360b2d05d3a2a20ab4c983ab8a335c Author: Ordissimo Date: 2021-08-31 20:09:33 +0000 Escl add models Epson ET-6100 commit 563afbe0fa410d52b99dc2a4672faa17e57890da Merge: c3cdf4102c09 e84dc47f73d9 Author: Ralph Little Date: 2021-08-29 23:16:42 +0000 Merge branch '505-issues-in-man-pages' into 'master' Resolve "Issues in man pages" Closes #505 See merge request sane-project/backends!646 commit e84dc47f73d956a5d81db124c20bd328643aee44 Author: Ralph Little Date: 2021-08-29 16:00:23 -0700 doc: errors corrected by translator. Many thanks to translator hmartink for raising these issues. commit c3cdf4102c0951cfd2e7115eec713d0cb0ca8064 Author: Ordissimo Date: 2021-08-28 09:03:42 +0000 Update pixma.desc commit 2ad66b6bec313805b5dc4eba71a7b2b2621a4caf Merge: c798d85eafe5 fc99bbe78491 Author: Ordissimo Date: 2021-08-27 19:00:26 +0000 Merge branch 'add-models' into 'master' Add models See merge request sane-project/backends!645 commit fc99bbe7849102cff6d7604e03c7e4b963131163 Author: Ordissimo Date: 2021-08-27 19:00:26 +0000 Add models commit c798d85eafe51597d45d72b0a97c892b5ecc37c5 Merge: 26749cee5348 94f25af5d846 Author: Ordissimo Date: 2021-08-27 14:53:31 +0000 Merge branch 'cleaning-doc-escl' into 'master' Alphabetical classification by make and model See merge request sane-project/backends!644 commit 94f25af5d8460b3b12fd06b946a0d87c1fde9e23 Author: thierry1970 Date: 2021-08-27 16:09:09 +0200 Alphabetical classification by make and model commit 26749cee5348dbeb4708eb40087d867cd191df78 Merge: 1ba63878e40a 4929387246d7 Author: Ordissimo Date: 2021-08-23 07:57:32 +0000 Merge branch 'fix-redirection-curl' into 'master' Fix redirect request. See merge request sane-project/backends!643 commit 4929387246d70ca5b812886dd3d1839112614407 Author: Ordissimo Date: 2021-08-23 07:57:32 +0000 Fix redirect request. commit 1ba63878e40a74e7863a3f2fab53c40d47dbaf0b Merge: f5b4e914c675 47963e059ed7 Author: Ordissimo Date: 2021-08-10 20:32:29 +0000 Merge branch 'canon-pixma-new-model' into 'master' Canon pixma new model See merge request sane-project/backends!642 commit 47963e059ed76042984d19de2563fde75b3519aa Author: Ordissimo Date: 2021-08-10 20:32:29 +0000 Canon pixma new model commit f5b4e914c67551ef38d6810dde05f12ce2f504ff Merge: c6fd7de5f10c 6ea78a79ca43 Author: Wolfram Sang Date: 2021-08-03 20:21:38 +0000 Merge branch 'epson-xp-455' into 'master' epson2: Add XP-452 455 Series as supported See merge request sane-project/backends!641 commit 6ea78a79ca43ef5fd0d8c98132de0811d128c788 Author: John Keeping Date: 2021-07-31 18:03:33 +0100 epson2: Add XP-452 455 Series as supported Tested via simple-scan with a device branded as XP-455 but which identifies in the device descriptor's product string as "XP-452 455 Series". commit c6fd7de5f10c8b4a55d4e6fa0eb6d6f1f34225ad Merge: 9a0fa0da86d4 4c99352dda05 Author: Ordissimo Date: 2021-07-29 15:34:30 +0000 Merge branch 'escl-support-TR7500' into 'master' Escl support tr4520 and tr7500 See merge request sane-project/backends!640 commit 4c99352dda057319ae3586b89d8babafd63531da Author: Ordissimo Date: 2021-07-29 15:34:29 +0000 Escl support tr4520 and tr7500 commit 9a0fa0da86d4efde5d3441222be3de414165a96f Merge: 7b2c4a0bf733 3bbf104ea0b2 Author: Ralph Little Date: 2021-07-17 22:40:54 +0000 Merge branch '231-canon-lide-120-wrong-size-with-y-x-after-update-to-sane-backends-1-0-28-1078-g5747ffa9' into 'master' Resolve "Canon LiDE 120: wrong size with -y -x after update to sane-backends 1.0.28-1078-g5747ffa9" Closes #231 See merge request sane-project/backends!423 commit 3bbf104ea0b28f543bb38e104862e7b4692bb262 Author: Ralph Little Date: 2020-05-02 11:12:22 -0700 genesys: Corrected motor slope for LiDE 120 commit 7b2c4a0bf7336ad819bada7837004da23af1aef6 Merge: 1e88e1504ef7 bb8c0b088c39 Author: Ralph Little Date: 2021-07-03 15:59:39 -0700 Merge branch 'epsonds_updates' commit bb8c0b088c395f77b5f3e618fb46fe7f51db777a Author: Ralph Little Date: 2021-07-02 09:31:12 -0700 epsonds: Correction from Epson for unused parameter fail in pipeline. commit b74dfb300489b37e6fa697a1d19caaa0a0ac920a Author: Ralph Little Date: 2021-06-22 12:41:44 -0700 epsonds: corrections from Epson following first review. Contains some rework to address concerns from pipeline build. commit 9282aac9bee8ad141765fa30ee502bbfbcfe5251 Author: Ralph Little Date: 2021-06-20 14:57:08 -0700 epsonds: Fix up white space issues in patch.; commit bc6035624d7b9bd32600e214d80ab10e17abbd2b Author: Ralph Little Date: 2021-06-20 14:49:56 -0700 epsonds: major product support update from Epson. Patch provided by Nakamura Iwao at Epson Japan. commit 1e88e1504ef77f09d49da95d393a0af09d20f7fa Author: Ralph Little Date: 2021-07-03 15:55:25 -0700 epsonds: Merge to master commit 911be8af68ebd3b53ef91422bf5e3c9c34a8d9f5 Merge: 1a95dc7cde56 eb42ad08d571 Author: Wolfram Sang Date: 2021-06-19 19:43:38 +0000 Merge branch 'cleanup_major_minor' into 'master' treewide: use SANE_CURRENT_* macros consistently See merge request sane-project/backends!632 commit eb42ad08d571043d61fc7a88655642aaff241871 Author: Wolfram Sang Date: 2021-06-08 17:13:51 +0200 treewide: use SANE_CURRENT_* macros consistently Backends use a mixture of SANE_CURRENT_{MAJOR | MINOR} and V_{MAJOR | MINOR} with all kind of permutations. I was confused by this and one comment in pieusb.c tells me I was not alone. Some items in old changelogs suggest to use the SANE_CURRENT_ macros in backends, so let's switch to do that with the exception of net.c and dll.c. Done with: $ find backend -name '*.[ch]' | xargs sed -i '/nearly every/ ! { s/\ Date: 2021-06-18 22:00:16 +0000 Merge branch 'gt68xx/proper_free' into 'master' gt68xx: fix use-after-free and two mem leaks See merge request sane-project/backends!634 commit 63942f7a7473496d1160f02f5c1da3620525690d Author: Wolfram Sang Date: 2021-06-10 11:32:04 +0200 gt68xx: fix use-after-free and two mem leaks The config file argument needs to be freed when a device is not set. That was missed for two occasions. The other occasion was freeing it unconditionally leading to a use-after-free for the regular use case. Fixes: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=980311 This is the least intrusive fix. The code really wants to be refactored. Tested with a Mustek 1200 UB Plus. commit 026430c293d14d531af807174d3bd9fbb6523015 Merge: e86175e726ef 7a28e66a162c Author: Ralph Little Date: 2021-06-18 21:59:54 +0000 Merge branch 'libera_chat' into 'master' docs/sane.man: Move to libera.chat See merge request sane-project/backends!637 commit 7a28e66a162cbcbafb9f8777c63614ea3ddc8f9b Author: Zdenek Dohnal Date: 2021-06-17 08:58:44 +0200 docs/sane.man: Move to libera.chat commit e86175e726ef913e96145aafbca08f1ce449b817 Merge: 980bb29cb85d b1021e09e435 Author: Ralph Little Date: 2021-06-18 19:08:15 +0000 Merge branch 'ps-devel-fix-microtek-va_list-001' into 'master' Fix uclibc compile (include stdarg.h for va_list/va_start/va_end), fix NDEBUG compile (PDBG/pixma_dbg) See merge request sane-project/backends!638 commit b1021e09e435e6c727303b9bfa2f69eb8fcf958a Author: Peter Seiderer Date: 2021-06-18 20:10:02 +0200 backend/pixma: fix compile with NDEBUG Fixes: .../host/opt/ext-toolchain/bin/../lib/gcc/x86_64-buildroot-linux-uclibc/9.3.0/../../../../x86_64-buildroot-linux-uclibc/bin/ld: ./.libs/libpixma.a(libpixma_la-pixma_common.o): in function `sanei_pixma_read_image': pixma_common.c:(.text+0xc05): undefined reference to `DBG' .../host/opt/ext-toolchain/bin/../lib/gcc/x86_64-buildroot-linux-uclibc/9.3.0/../../../../x86_64-buildroot-linux-uclibc/bin/ld: pixma_common.c:(.text+0xc38): undefined reference to `DBG' .../host/opt/ext-toolchain/bin/../lib/gcc/x86_64-buildroot-linux-uclibc/9.3.0/../../../../x86_64-buildroot-linux-uclibc/bin/ld: pixma_common.c:(.text+0xcbf): undefined reference to `DBG' Signed-off-by: Peter Seiderer commit 7dafc52dda96fa68f39058b10eec3d822fd5ea9d Author: Peter Seiderer Date: 2021-06-18 19:56:56 +0200 backend/sm3600-scanutil: fix uclibc compile (include stdarg.h for va_list/va_start/va_end) Fixes: sm3600-scanutil.c: In function ‘debug_printf’: sm3600-scanutil.c:69:3: error: unknown type name ‘va_list’ 69 | va_list ap; | ^~~~~~~ sm3600-scanutil.c:48:1: note: ‘va_list’ is defined in header ‘’; did you forget to ‘#include ’? 47 | #include "sm3600-scantool.h" +++ |+#include 48 | sm3600-scanutil.c:75:3: warning: implicit declaration of function ‘va_start’; did you mean ‘sane_start’? [-Wimplicit-function-declaration] 75 | va_start(ap,szFormat); | ^~~~~~~~ | sane_start sm3600-scanutil.c:76:28: warning: passing argument 3 of ‘vfprintf’ makes pointer from integer without a cast [-Wint-conversion] 76 | vfprintf(stderr,szFormat,ap); | ^~ | | | int In file included from ../include/sane/sanei_config.h:50, from sm3600.c:70: .../host/x86_64-buildroot-linux-uclibc/sysroot/usr/include/stdio.h:339:23: note: expected ‘__va_list_tag *’ but argument is of type ‘int’ 339 | __gnuc_va_list __arg); | ~~~~~~~~~~~~~~~^~~~~ In file included from sm3600.c:94: sm3600-scanutil.c:77:3: warning: implicit declaration of function ‘va_end’ [-Wimplicit-function-declaration] 77 | va_end(ap); | ^~~~~~ Signed-off-by: Peter Seiderer commit 145e16008e7479ea58278e55f71d6dfcd4db714b Author: Peter Seiderer Date: 2021-06-18 19:51:45 +0200 backend/microtek: fix uclibc compile (include stdarg.h for va_list/va_start/va_end) Fixes: microtek.c: In function ‘MDBG_INIT’: microtek.c:163:3: error: unknown type name ‘va_list’ 163 | va_list ap; | ^~~~~~~ microtek.c:78:1: note: ‘va_list’ is defined in header ‘’; did you forget to ‘#include ’? 77 | #include "microtek.h" +++ |+#include 78 | microtek.c:164:3: warning: implicit declaration of function ‘va_start’; did you mean ‘sane_start’? [-Wimplicit-function-declaration] 164 | va_start(ap, format); | ^~~~~~~~ | sane_start microtek.c:165:54: warning: passing argument 4 of ‘vsnprintf’ makes pointer from integer without a cast [-Wint-conversion] 165 | vsnprintf(_mdebug_string, MAX_MDBG_LENGTH, format, ap); | ^~ | | | int In file included from ../include/sane/sanei_config.h:50, from microtek.c:70: .../host/x86_64-buildroot-linux-uclibc/sysroot/usr/include/stdio.h:359:57: note: expected ‘__va_list_tag *’ but argument is of type ‘int’ 359 | const char *__restrict __format, __gnuc_va_list __arg) | ~~~~~~~~~~~~~~~^~~~~ microtek.c:166:3: warning: implicit declaration of function ‘va_end’ [-Wimplicit-function-declaration] 166 | va_end(ap); | ^~~~~~ Signed-off-by: Peter Seiderer commit 980bb29cb85dff9d1018ca82fd7ed24944fe580f Merge: a78abbbdbadf 667d8f7acc98 Author: Ordissimo Date: 2021-06-12 12:41:05 +0000 Merge branch 'full-location-header' into 'master' The job URL is adapted to the information provided by the value of the Location property. See merge request sane-project/backends!635 commit 667d8f7acc985a0c25970b67e52cd4185113a412 Author: thierry1970 Date: 2021-06-12 00:57:07 +0200 The job URL is adapted to the information provided by the value of the Location property. commit a78abbbdbadfe5ab2ea0208c390e1e5b209785f5 Merge: cd1733c345ca cadc4de1a744 Author: Wolfram Sang Date: 2021-06-08 16:58:30 +0000 Merge branch 'epson2/cleanup-desc' into 'master' epson2: cleanup desc and remove BUILD versioning See merge request sane-project/backends!631 commit cadc4de1a74435ee3d569f7a82444ea23b93c9e6 Author: Wolfram Sang Date: 2021-06-08 09:01:54 +0200 descriptions: remove unneeded false :new flags It is the default commit be5b2471800d70d74c46c01b5af4bb1e19e0f527 Author: Wolfram Sang Date: 2021-06-08 08:59:26 +0200 epson2: desc: remove unneeded URL No need for a generic URL at a specific scanner commit 65b7e317a6d90bdf0950328c05f695824038d7e2 Author: Wolfram Sang Date: 2021-04-09 22:10:50 +0200 epson2: remove BUILD versioning Makes no sense these days. Sane version is good enough. commit cd1733c345ca4f4e69026f88ac4efaa2866337c1 Merge: ffa7a82900bf 53641f834238 Author: Wolfram Sang Date: 2021-06-08 05:14:40 +0000 Merge branch 'epson2-new-usbids' into 'master' epson2: improve sorting of usbids and add new ones provided by Epson See merge request sane-project/backends!630 commit 53641f834238a9f200d00c3d2bdf6ed0400ab83a Author: Wolfram Sang Date: 2021-05-29 12:33:09 +0200 epson2: add usb ids provided by Epson This patch is based on the file "epson2_fix.patch" from the archive "SANE-patch-for-epsonds-epson2-20210518-5.zip" sent to the sane-devel mailing list. The following changes have been made on top of the original patch: * in epson2.desc, the upstream entry for ET-2600 has been dropped because a better version was provided in the new additions * the seperate entry for XP-240 has been merged into the combined entry for XP-243/245/247 * the new entries have been sorted into the existing entries * original changes to epson2_usb.c have been dropped and were recreated using the updated epson2.desc and epson2usb.pl commit db7a285e7425879e2a5c67536b46c1b4d7dab743 Author: Wolfram Sang Date: 2021-05-29 12:16:44 +0200 epson2: use 4 digits for USB IDs Use the output of the updated epson2usb helper. Affects only the sorting, no functional change. Also, fix minor whitespace issues at the end of the file. commit 28176df499d7772733623cff6c4401416f26466e Author: Wolfram Sang Date: 2021-05-29 12:13:35 +0200 tools: epson2usb: always use 4 digits for USB IDs Otherwise the sorting is too confusing when 3- and 4-digits are mixed. commit ffa7a82900bf3ce05581a1e6cba50a49cb9ac31f Author: Ralph Little Date: 2021-05-31 21:57:37 -0700 po: remove trailing blank line, breaks pipeline commit 19b9b41e4d3df24624a99727bb6f4158784a7028 Author: Ralph Little Date: 2021-05-31 21:54:23 -0700 po: Add zh_CN locale to build. commit 257529d39ed5e3e829b5601a459e697460f4493b Merge: 075bd9b7e94a b7055f48593d Author: Ralph Little Date: 2021-06-01 04:45:06 +0000 Merge branch 'master' into 'master' Added SANE backend translation for Simplified Chinese. See merge request sane-project/backends!629 commit b7055f48593de252ba0c17155cab1b9ead7bb865 Author: Tyson Tan Date: 2021-05-21 13:55:05 +0000 Corrected some entries from Ustushi's S-Chinese translations. commit 99f378360838e457f5350d719f6ef606f2446cd6 Author: Tyson Tan Date: 2021-05-21 08:34:57 +0000 Added SANE backend translation for Simplified Chinese. commit 075bd9b7e94aab0c6b7996bccca298460b3ca2a0 Author: pimvantend Date: 2021-05-25 14:25:28 +0200 canon_lide70: faster and brighter scanning at 600dpi for the lide600 commit 2b0e4bc4979412ff70d899b5cefa9e8e0c092039 Merge: 8e85691dd9e4 28ad82bd423a Author: Ralph Little Date: 2021-05-13 15:44:34 +0000 Merge branch 'scanimage_man_patch_update' into 'master' doc: add man change clarification to deal with debian patch. See merge request sane-project/backends!627 commit 28ad82bd423a55f35c1a67253411f801a22747ac Author: Ralph Little Date: 2021-05-13 15:44:33 +0000 doc: add man change clarification to deal with debian patch. commit 8e85691dd9e48bd773fad69c29c9d5f6cd1a3b23 Merge: e81f6b5a57b1 4fef6d6da1af Author: Ralph Little Date: 2021-05-12 16:57:41 +0000 Merge branch 'reformat-scanimage-options' into 'master' doc: reformatted the main options of scanimage See merge request sane-project/backends!626 commit 4fef6d6da1af99339a813409f5cba6419101bf4c Author: Ralph Little Date: 2021-05-12 09:29:04 -0700 doc: reformatted the main options of scanimage Following the update of saned's man page, I have reformatted the scanimage options section for consistency and to correct some omissions and improve readability and grammar. commit e81f6b5a57b140f2c06c0de034dcf24905dbac0c Author: Ralph Little Date: 2021-05-07 11:05:48 -0700 doc: fix trailing whitespace issue commit 37fe11acd268adb1ad663e27e4f61a51eb8f2f5e Merge: 28f7bbfb87a1 7f21e4cac8c5 Author: Ralph Little Date: 2021-05-07 16:25:40 +0000 Merge branch 'saned_man_option_update' into 'master' doc: updated saned manpage to reflect saned. See merge request sane-project/backends!624 commit 7f21e4cac8c54147bd040b55660a03a8baa2bd9c Author: Ralph Little Date: 2021-05-07 09:23:28 -0700 doc: updated saned manpage to reflect saned. saned supports long options and some clarification was necessary for correctness. Also I reformatted the options section to be more standard and consistent with other pages. commit 28f7bbfb87a1cbf49a61c213c9283f82020ce5a0 Merge: 3f80ebae9381 dde45eed5efc Author: Ralph Little Date: 2021-05-07 16:07:36 +0000 Merge branch 'master' into 'master' Fix manpage typo See merge request sane-project/backends!623 commit dde45eed5efc7c3853e7e5ee78dc4e0dba315da8 Author: Rodrigo Osorio Date: 2021-05-07 10:18:22 +0200 Fix manpage typo There is no such switch as -B in scanned commit 3f80ebae93815e602361e0ffad6b766df9aae189 Author: pimvantend Date: 2021-04-18 17:47:49 +0200 lide70 and 600: many simplifications commit 0943bf00bd4928dbc46da4ec34d6c7693f86c4cc Merge: 75575e0888b3 a6964435552b Author: Wolfram Sang Date: 2021-04-09 19:36:06 +0000 Merge branch 'epson2/better_timeouts' into 'master' epson2: better timeouts See merge request sane-project/backends!605 commit a6964435552b0dfd79838db016d12b11d9f0a080 Author: Wolfram Sang Date: 2021-03-04 19:51:22 +0100 epson2: remove double semicolon commit 85d804760190fad2e05fc0013643821fcfcdcbc2 Author: Wolfram Sang Date: 2021-03-04 19:49:58 +0100 epson2: bail out if device is still initializing The comment says it all. commit c70426fde28d6b8581a2db98af8ea8b7b7d93fd7 Author: Wolfram Sang Date: 2021-03-04 18:51:36 +0100 epson2: use longer USB timeout With my 12000XL, using autofocus takes ~35 sec before scanning starts. So, extend the default USB timeout. commit 75575e0888b3f3d6f60da1592676c0f5a3a3ad93 Author: Ralph Little Date: 2021-04-08 09:37:24 -0700 epson2: Wolfram Sang is now the maintainer for this backend. commit 7b7b36e593cf1c66d5a69eac19a6e1c5cd39c069 Merge: d9412740acb1 730e4ada3746 Author: Ralph Little Date: 2021-04-07 16:24:08 +0000 Merge branch '464-genesys-backend-crashes-during-exception-handling' into 'master' Resolve "genesys backend crashes during exception handling" Closes #464 See merge request sane-project/backends!621 commit 730e4ada37463f7f8e5116affbc3a16f48e5169e Author: Ralph Little Date: 2021-04-07 09:06:39 -0700 genesys: fixed indentation again. Hopefully, I will get it right this time. Uses 4 space indents rather than 2 as used elsewhere in the code base. commit 5702046a9c1825a717ee307ee3320271de45bf1f Author: Ralph Little Date: 2021-04-07 09:01:29 -0700 genesys: fix indentation commit 7ce23d05e2067b023ee397b5f6cea39a776ad6fe Author: Ralph Little Date: 2021-04-06 08:41:41 -0700 genesys: ensure that vector ptr is initialized commit d9412740acb1f9d09cdfa0f5761047938f2e271a Merge: c5d84e5f1766 a6ffa6c8f8d3 Author: Ralph Little Date: 2021-04-07 03:24:46 +0000 Merge branch 'mention-advanced-in-scanimage' into 'master' scanimage: mention when an option is advanced See merge request sane-project/backends!613 commit a6ffa6c8f8d398e090c68c1a561547c917d9f6eb Author: Andrew Sayers Date: 2021-03-30 11:47:59 +0100 scanimage: mention when an option is advanced commit c5d84e5f1766760377994c4a9af9678499060f13 Merge: df34fa403a5c 696ccd2d7b04 Author: Klaus Kämpf Date: 2021-04-06 13:39:11 +0000 Merge branch 'digitdia4000_slide' into 'master' Enable Slide advance on DigitDia 4000 and correct misspelling See merge request sane-project/backends!620 commit 696ccd2d7b04c4dc2621cfcefbfdc1d0c73f12eb Author: grmpl Date: 2021-04-03 18:41:06 +0200 Enable Slide on DigitDia 4000, correct misspelling commit df34fa403a5c3714eab436aee1b9a3304109bf70 Author: pimvantend Date: 2021-04-04 16:15:10 +0200 canon_lide70 approximate slope tables with formula a/(1+b*(x-c)) commit e16d023b2b8ef716885b77d7ddcdbe4db9d25eaf Merge: eb702d33c484 691d774f75ed Author: Povilas Kanapickas Date: 2021-03-31 23:54:18 +0000 Merge branch 'ps-devel-fix-thread-less-compile-001' into 'master' genesys: use usleep instead of std::this_thread::sleep_for to re-enable thread less compile See merge request sane-project/backends!619 commit 691d774f75ed904e7a1a1a532f9de963a5d2739e Author: Peter Seiderer Date: 2021-03-31 22:51:03 +0200 genesys: use usleep instead of std::this_thread::sleep_for to re-enable thread less compile Fixes: genesys/scanner_interface_usb.cpp: In member function ‘virtual void genesys::ScannerInterfaceUsb::sleep_us(unsigned int)’: genesys/scanner_interface_usb.cpp:484:10: error: ‘std::this_thread’ has not been declared 484 | std::this_thread::sleep_for(std::chrono::microseconds{microseconds}); | ^~~~~~~~~~~ Signed-off-by: Peter Seiderer commit eb702d33c4842f2224a5d0ae7632c783f6e06a0e Merge: de5b27c99548 f99687b3cc93 Author: Ordissimo Date: 2021-03-31 16:12:51 +0000 Merge branch 'fix-escl-resolution-with-jpeg' into 'master' Fix scan all resolution with JPEG. See merge request sane-project/backends!617 commit f99687b3cc93a5da9408765ba6d22fc0355aa42d Author: Thierry HUCHARD Date: 2021-03-30 22:37:28 +0200 Fix scan all resolution with JPEG. commit de5b27c9954822768318760be3c6135854dd1735 Merge: 69df00d00c91 9539027f2492 Author: Ordissimo Date: 2021-03-31 16:11:24 +0000 Merge branch 'add-escl-models' into 'master' Add models Canon TR455X See merge request sane-project/backends!618 commit 9539027f24925415f6036617e0d818bc4581dca8 Author: thierry1970 Date: 2021-03-31 14:46:20 +0200 Add models Canon TR455X commit 69df00d00c9123aa251e0670709ff4297726c7b3 Merge: 0ef485d18959 0bbbf85528e9 Author: Povilas Kanapickas Date: 2021-03-30 16:00:15 +0000 Merge branch 'restore_plustek_opticfilm_v2' into 'master' genesys: add back Plustek Optifilm 7200 V2 config. See merge request sane-project/backends!612 commit 0bbbf85528e9d6eade8eefdb4e6d52d8bb9131a6 Author: Ralph Little Date: 2021-03-28 21:07:21 -0700 genesys: add back Plustek Optifilm 7200 V2 config. This has support in the backend and user reports works. I think this might just be an oversight. commit 0ef485d18959b873f6a3bac693fdc872e514a207 Author: pimvantend Date: 2021-03-25 13:55:13 +0100 lide70: more white balance, less slope tables commit 1d30150c3c6f6b9e98eb0d1d7f02775ebfb5d2a4 Merge: 82117153f58f 580c278dcafe Author: Ralph Little Date: 2021-03-20 16:51:18 +0000 Merge branch 'epson2/disable_focus_properly' into 'master' epson2: disable focus properly See merge request sane-project/backends!604 commit 580c278dcafe4159213406b4307ee8598fe08fe7 Author: Wolfram Sang Date: 2021-03-04 08:30:46 +0100 epson2: disable focus properly The old epson driver disabled the focus by erasing the setFocus command. The epson2 driver turned it into a seperate flag. However, scanners which disabled focus in post_init were not converted to use the new flag. Because we meanwhile set focus during init, these scanners could not start anymore. Convert them to use the new flag. While here, update the comment how we handle focus detection and update the debug strings accordingly. Bug reported by Hans Meir and debugged by Zdenek Dohnal. Many thanks! commit 82117153f58f931c50f358ac3bea5bad7e7878c2 Merge: bb90aa9d950a 39ced5545418 Author: Ralph Little Date: 2021-03-20 16:46:52 +0000 Merge branch 'epson2/fix-descs' into 'master' epson2: improve descs See merge request sane-project/backends!606 commit 39ced55454184135c88f8b8b0544a237dbd356b0 Author: Wolfram Sang Date: 2021-03-12 20:32:46 +0100 epson2: 12000XL and 1640SU have been tested with TPU So we can mark them as "complete" now. commit 6e85a57e64b7df36c1fbc9ec0f8da86acee6583a Author: Wolfram Sang Date: 2021-03-12 14:53:40 +0100 epson2: remove desc entries which are supported by other backends Only the epson2.desc has entries pointing to other backends. Remove them and update the backends which really support them instead. commit bb90aa9d950a8a3b93c944a51d6a67206b6e86fe Merge: 5819a79ab025 acc5ca499f67 Author: Ralph Little Date: 2021-03-14 18:41:17 +0000 Merge branch '454-hp-scanjet-4200c-backend-causes-segmentation-violation' into 'master' Resolve "HP ScanJet 4200C backend causes segmentation violation" Closes #454 See merge request sane-project/backends!607 commit acc5ca499f67ed1c8c42242fcf87358e7968e71d Author: Ralph Little Date: 2021-03-13 17:05:13 -0800 hp4200: memset incorrectly sized. registro[0] is pointer not int. commit 5819a79ab025bcdc01c8fcc7dd16a301eccfa55b Author: m. allan noah Date: 2021-02-28 20:41:21 -0500 canon_dr v62b - rewrite do_cmd() timeout handling - remove long timeout TUR from v61 (did not help) - allow config file to set initial tur timeout for DR-X10C (#142) commit 6d990e73e9571a9fc5cccfd92f1c89a8585d04ff Author: m. allan noah Date: 2021-02-28 16:13:36 -0500 canon_dr v62a - allow config file to set inq and vpd lengths for DR-M1060 (#263) commit e55ea7c06d2d91103dff236be8c5e97465f82e34 Author: pimvantend Date: 2021-02-24 14:01:10 +0100 added lide 600 to central manpage commit dd9d90223e7c4581bac36f458c073fd2ff8e43ae Merge: 44a085d685da b9edb46542bf Author: Ralph Little Date: 2021-02-24 02:16:05 +0000 Merge branch '433-issues-in-sane-man-pages' into 'master' Resolve "Issues in SANE man pages" Closes #433 See merge request sane-project/backends!598 commit b9edb46542bfa644d11d9fb37aa9c984b0c99d49 Author: Ralph Little Date: 2021-02-23 18:00:18 -0800 doc: final corrections indicated by translators. commit 7413e3131be2a0df182c5d6692e02764a0c130ba Author: Ralph Little Date: 2021-02-21 09:01:26 -0800 doc: some small man page corrections. commit 44a085d685dac1e270dda73f6344e23ef4e9d337 Merge: 12c81e2f1925 325c77503692 Author: Ralph Little Date: 2021-02-24 01:21:13 +0000 Merge branch 'debug-minolta-dimage-scan-dual-ii' into 'master' Add function to debug avision command See merge request sane-project/backends!595 commit 325c775036928f27ec4147352e08742801d141f2 Author: Giovanni Cappellotto Date: 2021-02-15 02:42:21 -0500 Add function to debug avision command Define a new enum that contains Avision data type codes and use those instead of pure values. I also added a couple of helper functions for debugging the command operation and data type code if read or send. commit 12c81e2f1925a03ca389207e2b4fdb1a32794d76 Author: pimvantend Date: 2021-02-23 14:28:56 +0100 some color and geometry changes commit 13d1cb694ee80dcbbee7568c0e12c544b23e1785 Merge: 00df971593ee 5d749ac79662 Author: Olaf Meeuwissen Date: 2021-02-16 11:49:48 +0000 Merge branch 'update-release-documentation' into 'master' Update release documentation See merge request sane-project/backends!596 commit 5d749ac796621c1b988e6302871c9facb135f1dc Author: Olaf Meeuwissen Date: 2021-02-16 20:30:46 +0900 doc: Update the release procedure and convert to Markdown commit 7ac30dced0b89396ca707129aa303dd17083b042 Author: Olaf Meeuwissen Date: 2021-02-16 19:46:57 +0900 NEWS: Add placeholder for user-visible changes in the next release commit 00df971593ee8b0c6bc23f2574291ac5f653408f Merge: c85185bd412b c1c567c49b3b Author: Olaf Meeuwissen Date: 2021-02-14 12:24:09 +0000 Merge branch 'release/1.0.32' into 'master' Release/1.0.32 Closes #122 and #248 See merge request sane-project/backends!594 commit c85185bd412be937766bf993c8c50a97b75f211c Author: m. allan noah Date: 2021-02-13 21:08:40 -0500 update AUTHORS for kvs* backends (#59) commit 7dbfccb53295901672682074a3cc1b1f3c7c76f8 Author: m. allan noah Date: 2021-02-13 15:15:09 -0500 canon_dr backend v61 - treat DR-P208 like P-208 (#356) - treat DR-P215 like P-215 (#356) - adjust wait_scanner to try one TUR with a long timeout (#142) commit 5205d1d09d0d0d09d48d085b476958832066d4bd Author: m. allan noah Date: 2021-02-13 13:58:33 -0500 Various fujitsu and canon_dr doc updates - update the copyright dates - update dates and version numbers in man pages - add iX1600 to supported list - set duplex-offset for P-208 commit b1a2b5fdb687435aede05a580641333294fb99b8 Merge: 678495f59687 23a4c86b32de Author: Ralph Little Date: 2021-02-12 03:11:27 +0000 Merge branch '438-hp-scanjet-3400c-incorrect-maximum-scan-area' into 'master' Resolve "HP ScanJet 3400C incorrect maximum scan area" Closes #438 See merge request sane-project/backends!590 commit 23a4c86b32de7fa9ca7be8e89d9694c113493b96 Author: Ralph Little Date: 2021-02-11 18:54:24 -0800 niash: increased the maximum Y extent to 297mm 297mm is advertised in the official specs for this machine family. Tested against the ScanJet 3300C and it doesn't bottom out or anything. Issue came about because A4 is 297mm in height and 296mm too short. Probably just a typo or an oversight. commit 678495f59687c5de20e2d79a731401a4d7007500 Merge: ab945b1a89cb 5f2209ffd993 Author: Ordissimo Date: 2021-01-31 15:18:09 +0000 Merge branch 'escl-fix-header-hp' into 'master' Use header to activate hack localhost. See merge request sane-project/backends!583 commit 5f2209ffd9935fb2f0612cf1fafb85276bf4e02e Author: Thierry HUCHARD Date: 2021-01-30 20:32:00 +0100 Use header to activate hack localhost. backends-1.3.0/ChangeLogs/ChangeLog-1.2.1000066400000000000000000003134121456256263500175300ustar00rootroot00000000000000commit b9fbe0b82eff0217c0303b2a7f556483929f3ffc Author: Ralph Little Date: 2023-02-04 16:16:54 -0800 scanimage: handle backends that provide options with null attributes HPAIO backend in some instances provides options that have NULL name. It might be that it is using SETTABLE flag to disable options rather than removing them from the options list. Or it might just be a backend bug. commit e3c1c0e000d894fcf07bb5aef81cb0fe6a10ab16 Author: Ralph Little Date: 2023-02-04 09:40:07 -0800 doc: Updates to en_GB translations. Not much to add, just confirming new messages. commit 6b588b0d9312117f98e0e6a95cd7aa9974d7151b Merge: 573ca4b45359 6220b21429f5 Author: Ralph Little Date: 2023-02-01 17:19:56 +0000 Merge branch 'uk_update2' into 'master' Update Ukrainian translation See merge request sane-project/backends!779 commit 6220b21429f51d2f0b5eeab8582c0022031505b2 Author: Yuri Chornoivan Date: 2023-02-01 18:53:17 +0200 Update Ukrainian translation commit 573ca4b45359ee408dab71adaccde69ca2ff35d6 Author: m. allan noah Date: 2023-01-27 18:09:57 -0500 update canon_dr.desc for #552 Several canon scanners can operate as a mass storage device, holding the windows software. They have a different USB PID in that case. commit 7ee6327d3c08584af7c42f8134246a21979897ee Merge: 4033ea23b692 370547fc6006 Author: Ralph Little Date: 2023-01-07 07:48:17 +0000 Merge branch '634-llvm-15-on-freebsd-support-for-sane-backends-1-1-1' into 'master' Resolve "LLVM 15 on FreeBSD support for sane-backends 1.1.1" Closes #634 See merge request sane-project/backends!777 commit 370547fc6006bdc4fe59b53f22c09c86d7e88d13 Author: Ralph Little Date: 2023-01-06 23:36:43 -0800 scanimage, jpegtopdf: fixes for FreeBSD header definitions. commit 4033ea23b6929265ccbee65bc26c7c654077cfc6 Merge: 7e85f737d090 109a82f0feb7 Author: Ordissimo Date: 2023-01-06 13:57:39 +0000 Merge branch 'escl-add-espon-L3160' into 'master' escl: add Epson EcoTank L3160 support. Closes #632 See merge request sane-project/backends!776 commit 109a82f0feb7d8b0b3bc609fbec72dfe405b8e23 Author: Ordissimo Date: 2023-01-06 14:42:11 +0100 escl: add Epson EcoTank L3160 support. commit 7e85f737d090ed01ea82fbb938fb96e291b6f5e8 Merge: fea8ad2b993b e840ad7fa3af Author: Ralph Little Date: 2022-12-28 18:11:42 +0000 Merge branch 'master' into 'master' Update Russian translation See merge request sane-project/backends!775 commit e840ad7fa3afaf087d0d3c97bf22d13d3c32bbcf Author: Mariya Shikunova Date: 2022-12-28 08:50:11 +0300 update translation commit fea8ad2b993bc28a647834e41f37b1d0ec79e8ac Merge: f9fe6d0ced3e 6352b00d6c74 Author: Ralph Little Date: 2022-12-21 17:37:30 +0000 Merge branch 'mg5700_buttons' into 'master' pixma: MG5700 button-controlled scan See merge request sane-project/backends!774 commit 6352b00d6c7411d263e08e5f4e3798b8a2a7939c Author: András Wacha Date: 2022-12-21 17:12:42 +0100 Made scan buttons work for Canon Pixma MG5700 commit f9fe6d0ced3e62872ba53633654ee166f76b3e0f Merge: 15e1d83b9d8a 506f539edaff Author: Ralph Little Date: 2022-12-04 00:54:15 +0000 Merge branch 'it-translation' into 'master' Update 2 IT translations See merge request sane-project/backends!772 commit 506f539edaff8202bb7032b2b598d4c7db011023 Author: Giovanni Cappellotto Date: 2022-12-03 16:23:38 -0500 Update 2 IT translations "Brightness" is "Luminosità", I'm 99.9% sure. "Quality calibration" should be translated as "Calibrazione della qualità" instead of "Qualità della calibrazione" because the subject is "quality". commit 15e1d83b9d8a9b87dbdd1693f8f94cb20fbb6deb Merge: ba1b68e6e9f0 b3b44f1188f9 Author: Ralph Little Date: 2022-11-30 18:19:16 +0000 Merge branch 'no_embedded_ldflags' into 'master' sane-backends.pc.in: Don't embed LDFLAGS into libs.private See merge request sane-project/backends!768 commit b3b44f1188f902a959a7b8d71cf70eef8abb63da Author: Zdenek Dohnal Date: 2022-11-30 15:31:19 +0100 sane-backends.pc.in: Drop ldflags from the file The ldflags variable is no longer used in the file because it embedded LDFLAGS from build environment and LibGPhoto2 LDFLAGS, where the former contains flags from the environment and can cause problems, and the latter is empty. With the state above, we can drop ldflags from pkgconfig file completely. commit ba1b68e6e9f04ba25c7f6d6f03617dc9ab460a7a Author: m. allan noah Date: 2022-11-18 21:14:28 -0500 canon_dr backend v64 - add complete support for imprinters on X10C (#585) The vast majority of the work here was originally done in !706 by Charles Quarra. I had some merge conflicts, and it was easier to pull all his work into a patch and apply it myself. The only changes I made were minor updates to comments, and adding an imprinter group option. Sorry for the loss of attribution, and thanks Charles! commit bab852c5bbf49ad1904f530795937588b243f117 Author: m. allan noah Date: 2022-11-18 19:23:10 -0500 canon_dr backend v63 - minor tweaks to support for reading the total and roller counters commit b9e11b11493d57220def869fd9e7c5a5545dd70b Merge: 9144eada4483 933b53608dca Author: m. allan noah Date: 2022-11-18 23:40:02 +0000 Merge branch 'canon-dr-x10c-counters' into 'master' Adds support for total and roller counters See merge request sane-project/backends!738 commit 933b53608dcaec5ffac09ea9caa1cb334d960cfb Author: Charles Quarra Date: 2022-11-18 23:40:01 +0000 Adds support for total and roller counters commit 9144eada4483e4550d1242d6137f23c849f0aafc Author: m. allan noah Date: 2022-11-18 14:19:23 -0500 add fujitsu ScanSnap iX1400, fixes #555 commit 87e9be4be5d31feb5f3f46c4eb1c7a832a1b31a8 Author: m. allan noah Date: 2022-11-17 21:24:45 -0500 add ScanSnap iX1300 to fujitsu desc and conf commit 190fa52619994f378bf8aee6bc232fbd9ebf439d Author: m. allan noah Date: 2022-11-17 20:52:29 -0500 epjitsu backend v33 - S1300i: fix color plane offset at 225 and 330 dpi (fixes #538) commit 6acd5366cbbfa298d2becf341b1ca3d32ab73f84 Author: m. allan noah Date: 2022-11-15 22:01:19 -0500 epjitsu backend v32 - fix hanging scan when using source = ADF Back (fixes #601) - minor improvements to .desc and manpage commit eb52b437627c77361d4ed30fc0995459a6b80907 Author: m. allan noah Date: 2022-11-15 19:36:53 -0500 fujitsu documentation updates - minor tweaks to text of manpage - update the desc file status of some unsupported scanners commit fa29906201639e2bca683118fc8fcff81aaa736f Author: m. allan noah Date: 2022-11-15 18:26:20 -0500 fujitsu backend v139 - move updated window_gamma logic to set_window - use internal gamma table if possible (fixes #618) commit 7547ee04e162f29227499fb83cf3720e5d14e31b Author: m. allan noah Date: 2022-11-09 23:34:19 -0500 more fujitsu desc and conf for fi-8xxx commit 642160ef978899ecb701d2c9d174573a0794b5e4 Author: m. allan noah Date: 2022-11-09 20:44:14 -0500 fujitsu desc and conf updates - add fi-81xx scanners - clean up desc file comments commit 436c507adec64eb742a63a7f5d875568ee6a2d90 Merge: b2a5a41be951 388d75715fa0 Author: Ralph Little Date: 2022-10-22 01:44:52 +0000 Merge branch '616-bug-in-current-release-libsane-for-ubuntu-22-04-1-with-samsung-scx-4828-fn' into 'master' Resolve "BUG in current release libsane for Ubuntu 22.04.1 with Samsung SCX-4828 FN" Closes #616 See merge request sane-project/backends!760 commit 388d75715fa0869090ed25b245cea7104f1f33f1 Author: Ralph Little Date: 2022-10-21 18:32:30 -0700 xerox_mfp: Added 4x28 to JPEG blacklist. User reports the common JPEG problem with his Samsung SCX-4828 FN. Machine announces itself as "Samsung SCX-4x28 Series" commit b2a5a41be951bcb7c49180afda07143c6bec56b1 Merge: 6b99447f5b12 6740b47bca42 Author: Ralph Little Date: 2022-10-06 19:37:58 +0000 Merge branch 'ir1022a' into 'master' pixma: add support for imageRUNNER 1018/1022/1023 See merge request sane-project/backends!759 commit 6740b47bca426bb9521f29bd787b516e9e60ff40 Author: Michał Kopeć Date: 2022-10-06 10:43:31 +0200 pixma: add support for imageRUNNER 1018/1022/1023 Add support for Canon imageRUNNER 1018/1022/1023 models, which all share the same USB PID. Add it as a copy of MF6500. Tested with an iR1022A model. Flatbed and ADF both work correctly. Signed-off-by: Michał Kopeć commit 6b99447f5b12758ff015b5c360a6dcbcf9b0a72d Merge: 9afa6f6e74dd edfc90450ee0 Author: Povilas Kanapickas Date: 2022-10-04 05:29:20 +0000 Merge branch '597-failed-to-build-from-source-with-gcc-12' into 'master' Resolve "Failed to build from source with GCC-12" Closes #597 See merge request sane-project/backends!757 commit edfc90450ee06149537fadb3095ba4b215c5c4fa Author: Ralph Little Date: 2022-10-02 18:14:25 -0700 genesys: corrections to include file order. minigtest.h has items that require the definitions in tests_printers.h. Pre-GCC-12, this didn't seem to matter but GCC12 seems to have a problem with this and requires the template definitions to have already appeared. commit 9afa6f6e74dd64da0a3f9c51ebd2c96bd25fe777 Merge: 1da0970a7efc ac11086cec34 Author: Pim van Tend Date: 2022-10-03 12:25:00 +0000 Merge branch 'lide70_corrections' into 'master' canon_lide70: Some small corrections. See merge request sane-project/backends!758 commit ac11086cec349e26baf5476a1d67e699beb6e18d Author: Ralph Little Date: 2022-10-02 18:27:45 -0700 canon_lide70: Some small corrections. commit 1da0970a7efc4bb052c1c439293b6abbf31bd169 Merge: b5e7e78312a1 0d7543ec659f Author: Ralph Little Date: 2022-09-21 17:23:46 +0000 Merge branch 'sane_canon_show_modelname' into 'master' [canon] Use the common model name instead of string from the device See merge request sane-project/backends!756 commit 0d7543ec659fc5f8f2750a8d8cc47e6cf939f029 Author: Zdenek Dohnal Date: 2022-09-21 17:57:35 +0200 backend/canon.c: Use the common model name instead internal string commit b5e7e78312a1b218273c89a8a22a381dfb957c2c Merge: 4873304efe2d e0c25d5a674a Author: Ralph Little Date: 2022-09-09 16:41:57 +0000 Merge branch 'ambir_externals' into 'master' doc_external: Added external driver entry for Ambir ImageScan Pro 820ix See merge request sane-project/backends!755 commit e0c25d5a674a8a6151cc2ffa9650edc26af6171c Author: Ralph Little Date: 2022-09-09 09:29:54 -0700 doc_external: Added external driver entry for Ambir ImageScan Pro 820ix Also corrected a prior error in the Kyocera external file. commit 4873304efe2d2a4e64fb502e50709811ed0b9836 Author: m. allan noah Date: 2022-09-06 21:18:31 -0400 fujitsu.desc: Remove trailing whitespace commit 84675354484361ba582c47ee42f7c420f64a0ce6 Author: m. allan noah Date: 2022-06-01 20:06:59 -0400 fujitsu backend 138 - update FCPA Inc to PFU America Inc - note support for fi-7300NX (#594) commit 693572d616edad74c6a57e74e508e2614cfa86d8 Merge: 9330bfdd830b 807d13de6db7 Author: Ralph Little Date: 2022-09-02 16:18:37 +0000 Merge branch 'desc_fix' into 'master' sane-canon: update broken link in desc file. See merge request sane-project/backends!752 commit 807d13de6db72df5f5e175f1a3838c3b35e790b4 Author: Ralph Little Date: 2022-09-02 09:06:55 -0700 sane-canon: update broken link in desc file. commit 9330bfdd830b40d920c45af9c6b81245ac84e5fb Merge: 1d2253e21f8d 16eed3e5267e Author: Ralph Little Date: 2022-08-25 18:49:35 +0000 Merge branch 'kyocera_external' into 'master' doc: Added external report of Kyocera SANE driver. See merge request sane-project/backends!749 commit 16eed3e5267e9c38ea4b10da48746222edc3ed29 Author: Ralph Little Date: 2022-08-25 18:49:35 +0000 doc: Added external report of Kyocera SANE driver. commit 1d2253e21f8def1de356c0fa91fda17ba0074da3 Merge: f1413cbc163b cbffffbd7b2f Author: Ralph Little Date: 2022-08-23 04:08:22 +0000 Merge branch 'new_epsonds' into 'master' epsonds: Added new model XP-2200 series. See merge request sane-project/backends!748 commit cbffffbd7b2f314fbfa593a6da10febf279313cb Author: Ralph Little Date: 2022-08-22 20:55:59 -0700 epsonds: Added new model XP-2200 series. Patch supplied by Nakamura Iwao from Epson Japan. commit f1413cbc163b76807e98b6f91ab42d6b506a694c Merge: ba463b896681 7ec4074fbd75 Author: Povilas Kanapickas Date: 2022-08-16 17:28:50 +0000 Merge branch 'pixma-adding-models' into 'master' pixma: add 2 models See merge request sane-project/backends!745 commit 7ec4074fbd7500f295777615a7cf8c33bf3a96a8 Author: thierry1970 Date: 2022-08-16 11:20:02 +0200 pixma: add 2 models commit ba463b89668176921704ac31ee63cbf9406e6f05 Merge: 8cd1488e3cd9 32a86107cdb4 Author: Ordissimo Date: 2022-08-16 09:43:03 +0000 Merge branch 'canon_pixma-adding-models' into 'master' scangearmp2: add 2 models See merge request sane-project/backends!746 commit 32a86107cdb4e63743c6377bdf9b221f99fe4080 Author: thierry1970 Date: 2022-08-16 11:31:59 +0200 scangearmp2: add 2 models commit 8cd1488e3cd9b46cc372ee8752133c232d2f7c07 Merge: b51a2e9cf272 23c72909310d Author: Ordissimo Date: 2022-08-05 15:51:49 +0000 Merge branch 'escl-fix-version' into 'master' escl: get the eSCL version in the xml See merge request sane-project/backends!744 commit 23c72909310ddd287afd42e4acf86d95a35efd4a Author: thierry1970 Date: 2022-08-05 17:40:20 +0200 Get the eSCL version in the xml commit b51a2e9cf2720280c2e60c9a20adccf7a37c532a Merge: 97a0c90fd1e0 a417cb8edf34 Author: Ralph Little Date: 2022-07-24 17:41:16 +0000 Merge branch 'pixma_support' into 'master' pixma: user reports that their TS6420a (TS6400 family) works with USB See merge request sane-project/backends!743 commit a417cb8edf3458f2320bb866d25c76f870074c44 Author: Ralph Little Date: 2022-07-24 10:28:51 -0700 pixma: user reports that their TS6420a (TS6400 family) works with USB commit 97a0c90fd1e00cd98220bcdde7ffc1f6ba61129c Merge: 409c23a7454b c0e966b21e92 Author: Ralph Little Date: 2022-07-22 16:23:56 +0000 Merge branch 'umax_pp_tool_build' into 'master' configure: corrected issue in Makefile.am and removed noinst build of umax_pp tool See merge request sane-project/backends!741 commit c0e966b21e92e1a48087061f053aa26265064c55 Author: Ralph Little Date: 2022-07-22 09:10:46 -0700 configure: corrected issue in Makefile.am and removed noinst build of umax_pp tool Currently, there is no way to disable the build of the umax_pp low level sources because the tool umax_pp is *always* built. Some platforms cannot build the umax_pp low level code so this causes problems. commit 409c23a7454bb35112ef1eb6773adf95871ee8e6 Merge: 948c1c5a40dc 12f91442c0e8 Author: Ralph Little Date: 2022-07-15 04:09:23 +0000 Merge branch 'upstream_avision_AD345F_basic' into 'master' avision: add AD345F support as "basic" See merge request sane-project/backends!740 commit 12f91442c0e823a536678088d27881e092159dfc Author: Nikolai Kostrigin Date: 2022-06-27 13:32:57 +0300 avision: add AD345F support as "basic" Tested with Avision AD345F model number DL-1802B. Flatbed and ADF duplex scanning available. Known limitation: ADF scanning is only capable to scan one sheet at a time via automatic feeder. Providing 2+ sheets breaks backend and scanner needs to be rebooted. Signed-off-by: Nikolai Kostrigin Tested-by: Mikhail Chernonog commit 948c1c5a40dc887a77eb675fbaa40224adc0fc6d Merge: 3f45d4d19e54 a698f003fee4 Author: Ordissimo Date: 2022-07-05 06:36:44 +0000 Merge branch 'escl-fix-segfault' into 'master' Escl: fix segfault See merge request sane-project/backends!739 commit a698f003fee4a7a56a207b65fed371f7c6a3aae9 Author: Ordissimo Date: 2022-07-05 06:36:44 +0000 Escl: fix segfault commit 3f45d4d19e5447e32f4e707f6610b1bcc8f60d58 Author: Ralph Little Date: 2022-06-19 16:02:18 -0700 test: fix type issue. proper type for usleep() is useconds_t. commit 1e68bfc7fe5899e591cb1479126a84eaa58a3ebb Merge: adcbf249f948 6ad2b593fef7 Author: Ralph Little Date: 2022-06-17 17:56:39 +0000 Merge branch 'changelogs' into 'master' ChangeLogs: Fix formatting issues, apply to 1.0.27 and 1.0.28, and add missing releases See merge request sane-project/backends!736 commit 6ad2b593fef74cc7d1d3672b9fb0212a1b599ec0 Author: David Ward Date: 2022-05-14 17:47:08 -0400 ChangeLogs: Reformat 1.0.27 and 1.0.28, and add missing releases Apply formatting changes to the existing ChangeLogs for the 1.0.27 and 1.0.28 releases. (This includes re-ordering some entries so that merge requests always appear together.) Manual formatting changes to entries in these ChangeLogs have been preserved. Add missing ChangeLogs for later releases. commit 7ca518027b14fb67603cf51548027256c8b1fad6 Author: David Ward Date: 2022-05-14 17:35:43 -0400 tools: Determine starting commit when generating a new ChangeLog file This was still hard-coded to 1.0.28. Detect this automatically instead by finding the most recent release tag. commit 7467a9da22cb5b16ccdd79c9bee2e1682ee09aa8 Author: David Ward Date: 2022-05-14 17:35:43 -0400 tools: Use topological commit ordering when generating ChangeLog files This means that commits from the same merge request will always appear together in the ChangeLog, instead of appearing shuffled together with other commits that were authored around the same time. commit 4dbfa03ec462f0fb8b06aae6357ccb022b3cb5eb Author: David Ward Date: 2022-05-14 17:35:43 -0400 tools: Fix inconsistent formatting when generating ChangeLog files Use 12-digit short hashes (which appear in merge commits). The number of digits required to unambiguously identify a commit increases as the Git repository grows. The ChangeLog for the 1.0.27 release has 7-digit short hashes, which are no longer meaningful: 9 digits are needed now. Forcing 12 digits to display here is the solution in the Linux kernel. Do not "decorate" the log with branch or tag names. It is understood that each file starts at a specific release tag (e.g. 1.0.27) and ends at the next tag (1.0.28), or at HEAD for development snapshots. Topic branch names, or the refs "master" and "HEAD", do not need labeling. commit adcbf249f94891a4b3376304a99b3e10bd251929 Author: Ralph Little Date: 2022-06-01 20:48:55 -0700 avision: fix whitespace tool check problem commit ba2c1f92f15b91c5e59ae108799af74f7c9fdfbf Merge: 1d259c749a0f 999a52f69130 Author: Ralph Little Date: 2022-06-01 20:34:20 +0000 Merge branch 'test-high-res' into 'master' Test backend - scanning high resolution images See merge request sane-project/backends!737 commit 999a52f69130f57d34acd086a4d16db1a7da05ba Author: Christian Theis Date: 2022-06-01 18:04:29 +0100 discussed amendments commit 1aead16faf57a092b5cf69627827339fdbcb5d3f Author: Christian Theis Date: 2022-05-29 20:52:51 +0100 enable test backend to scan high res images commit 39f9083debcdc750a02d94cf0c46df15bf9f7c8b Author: Christian Theis Date: 2022-05-21 17:28:21 +0100 compile avision with -Wconversion -Wtype-limits commit 1d259c749a0f1ff433e111af4dcd8979f647780f Merge: 8daae197a0be 108716c840ec Author: Ralph Little Date: 2022-05-13 20:58:05 +0000 Merge branch 'ci-bump-versions' into 'master' CI: Bump to Ubuntu 22.04 LTS and Fedora 36 See merge request sane-project/backends!735 commit 108716c840eccf2f37604f211b489d5371bf6c50 Author: David Ward Date: 2022-05-12 21:51:28 -0400 CI: Bump to Ubuntu 22.04 LTS and Fedora 36 commit 8daae197a0be4532a211001d8303284f427df1cb Merge: 6b30245844bd df048b16f00c Author: Ordissimo Date: 2022-05-11 13:42:52 +0000 Merge branch 'escl-fix-version-xml-job' into 'master' escl: fix version job. See merge request sane-project/backends!734 commit df048b16f00cc73bc0a6333772a1b5717c1f39f4 Author: thierry1970 Date: 2022-05-11 15:23:34 +0200 escl: fix version job. commit 6b30245844bdedd748b0cfde5593b314357ddfe7 Merge: 6acddfe7f0dc 7431b65974fe Author: Ordissimo Date: 2022-05-11 13:18:18 +0000 Merge branch 'escl-set-option-if-necessary' into 'master' Escl: set option if necessary See merge request sane-project/backends!733 commit 7431b65974fe9214905d300e1a83d1f9c4a474dc Author: Ordissimo Date: 2022-05-11 13:18:17 +0000 Escl: set option if necessary commit 6acddfe7f0dcbda7cb1311343fbda5297dffebf2 Merge: f68b57378d6b 024ae3b5fad8 Author: Ordissimo Date: 2022-05-11 09:59:20 +0000 Merge branch 'escl-normalize-spec' into 'master' Escl: normalize spec See merge request sane-project/backends!731 commit 024ae3b5fad8d41a6d836b125141adc01f222741 Author: Ordissimo Date: 2022-05-11 09:59:20 +0000 Escl: normalize spec commit f68b57378d6bfc5d0e47153fd80d16eb1b3c971d Merge: f012ae84e5a1 aa032f458d9b Author: Ordissimo Date: 2022-05-11 06:59:40 +0000 Merge branch 'escl-delete-obselete-tag' into 'master' escl : Remove obselete tag. See merge request sane-project/backends!730 commit aa032f458d9bf6a30209ff8cca7d36c475f9a480 Author: thierry1970 Date: 2022-05-11 08:28:35 +0200 escl : Remove obselete tag. commit f012ae84e5a148666599f64792617dbc6df4cabb Merge: b8722a14ed97 1d502a288ac2 Author: Ralph Little Date: 2022-05-08 17:41:51 +0000 Merge branch 'fix-scanjet-8250' into 'master' set correct minimum dpi for AV_ASIC_C6 on avision See merge request sane-project/backends!728 commit 1d502a288ac272a55c7db436efaf09c70327b486 Author: Christian Theis Date: 2022-05-08 15:08:16 +0100 set correct minimum dpi for AV_ASIC_C6 on avision commit b8722a14ed97b0db4b24fba6444885ae8987edbd Merge: a45e79bce6a7 f85e2fb9c252 Author: Ralph Little Date: 2022-05-01 22:02:55 +0000 Merge branch '149-fix-mkstemp-error-handling' into 'master' Resolve "Fix `mkstemp` error handling" Closes #149 See merge request sane-project/backends!725 commit f85e2fb9c252cd70e7630366e1886ba367c739ae Author: Ralph Little Date: 2022-05-01 22:02:55 +0000 Resolve "Fix `mkstemp` error handling" commit a45e79bce6a7d44fc87641bfed4436f7abb614cb Merge: e0aad8e814b4 a13cc4c1ff8f Author: Ralph Little Date: 2022-05-01 21:56:52 +0000 Merge branch 'clang-warnings' into 'master' Fix Clang warnings See merge request sane-project/backends!727 commit a13cc4c1ff8fc18365727d86042c963273dd6150 Author: David Ward Date: 2022-04-26 07:22:51 -0400 CI: Do not allow build job failures for Fedora 35 The CI build jobs targeting Fedora 35 pass (without compiler warnings). Enforce this going forward. commit 9e1819c3f7c1fc23675af9affcb657a642fe283b Author: David Ward Date: 2022-04-26 02:12:25 -0400 configure: Use PKG_CHECK_MODULES to detect Net-SNMP The output of "net-snmp-config --cflags" can contain optimization flags. These might not be applicable to the current compiler, causing warnings. Use PKG_CHECK_MODULES to check for Net-SNMP and obtain the compiler and linker flags instead, in the same way as for libcurl or poppler-glib. commit 4b15f35e75179fa44f08733f27e47147e82556a3 Author: David Ward Date: 2022-04-26 01:18:01 -0400 mustek: Remove unused array variables Introduce a CDB_SIZE() macro similar to that found in sanei/sanei_scsi.c. This avoids warnings from Clang about unused variables. commit e0aad8e814b4576f1597e4095b92a9fc9551a178 Merge: ac42d6e684c9 76aa009316f0 Author: Ralph Little Date: 2022-04-25 14:17:15 +0000 Merge branch 'backend-built-sources' into 'master' backend/Makefile: Fix handling of built sources See merge request sane-project/backends!726 commit 76aa009316f09e66c4fda98fd39fd672c4d07a3e Author: David Ward Date: 2022-04-24 23:02:04 -0400 backend/Makefile: Include dll-preload.h in sources commit adac4f23e672ce7a2d6e601024bda190a12e0df5 Author: David Ward Date: 2022-04-24 22:52:55 -0400 backend/Makefile: Improve rules for pixma/pixma_sane_options.{c,h} Ensure these files are updated after pixma/pixma.c is changed. Do not remove them during "make clean" or "make distclean", because they are part of the source distribution (generated with "make dist"). Display the relative paths in the build output. commit 28b5aa7deec6f3d7f98c8ddd2c64d75cb6d71930 Author: David Ward Date: 2022-04-24 20:12:23 -0400 Revert "backend/Makefile: Remove unneeded references to $(srcdir)" This breaks "make dist" when run locally in the CI container. commit ac42d6e684c90c94812d01f7104afed7fb9cb749 Merge: e0e9a614be92 a1fbbae636c6 Author: Ralph Little Date: 2022-04-24 17:10:18 +0000 Merge branch 'sp30' into 'master' Fujitsu / PFU SP30 See merge request sane-project/backends!716 commit a1fbbae636c698363c2eb9333475061cc7adcfd1 Author: Aaron Jackson Date: 2022-04-14 22:25:29 +0100 Update fujitsu description to include SP30 as good commit 140100bb232cacc8f96f64ec144cb957cd4127d0 Author: Aaron Jackson Date: 2022-04-14 22:13:20 +0100 Fujitsu / PFU SP30 Tested and working against the Fujitsu driver. commit e0e9a614be923cebcc2403185b89a2d0cd8ce411 Merge: 5f27cc71c8b4 8bbcdf8de353 Author: Ralph Little Date: 2022-04-24 17:08:20 +0000 Merge branch 'avision-fixes' into 'master' avision: Fix issues found by code inspection See merge request sane-project/backends!710 commit 8bbcdf8de35316b9318cc464b0bf5e74a64d9cb0 Author: David Ward Date: 2022-03-27 16:51:30 -0400 avision: Invert conditional for software scaling Place the alternative calls to fwrite() near each other in the code, for better readability. commit c7a33643f3243d8ed58f8eabfb317e58547d203c Author: David Ward Date: 2022-03-27 16:51:30 -0400 avision: Fix line numbers in debug message for software scaling Print line numbers that are relative to the entire output, rather than the current stripe. commit 1277612c5b937d57e2f956d68a5be8ad1c59e5ac Author: David Ward Date: 2022-03-27 16:51:30 -0400 avision: Cancel scanning before closing device commit bd6ad2e092789c3fa5e07d8088a103e6d51e543c Author: David Ward Date: 2022-03-27 16:51:30 -0400 avision: Correctly handle failure to open file commit 5f27cc71c8b4fd4204af314a1e83b05721160e77 Merge: dd822eda010d b217e7b00274 Author: Ralph Little Date: 2022-04-24 17:02:54 +0000 Merge branch '584-avision-reading-unexpected-length-is-not-handled-correctly' into 'master' Resolve "avision: Reading unexpected length is not handled correctly" Closes #584 See merge request sane-project/backends!724 commit b217e7b002743f96f7edfef6aed8eb8b77e51e7a Author: Ralph Little Date: 2022-04-24 09:49:51 -0700 avision: enhance return size checks to generate an appropriate error code. Some of the status code checks also check that the returned data is of the expected size. However, if they are not, it is possible to return SANE_STATUS_GOOD in error. We should generate an appropriate error code other than SANE_STATUS_GOOD for this case. commit dd822eda010d44565808f71e3c20a30347069b0c Merge: 137a8f88c7c8 ff4114a79572 Author: Ralph Little Date: 2022-04-22 17:51:06 +0000 Merge branch 'thread-kvs40xx' into 'master' kvs40xx: Return NULL from read_data() See merge request sane-project/backends!722 commit ff4114a79572ce56af05cf8e853508ad9a102673 Author: David Ward Date: 2022-03-08 19:00:00 -0500 kvs40xx: Return NULL from read_data() Functions called by pthread_create() have the return type (void *). However, the return statements in read_data() use a value of type SANE_Status rather than a pointer, which causes a compiler warning. This return value is never actually used, so return NULL instead. commit 137a8f88c7c83dffb70040cc4b26e94dcfff4378 Merge: 31cefeb65919 12560890a6e2 Author: Ralph Little Date: 2022-04-22 17:48:42 +0000 Merge branch 'poll-header' into 'master' Fix header file used for poll() See merge request sane-project/backends!723 commit 12560890a6e298091bd63b8093a35604416eb92a Author: David Ward Date: 2022-04-21 23:37:33 -0400 Fix header file used for poll() POSIX specifies the header to include is , not . This results in a compiler warning with musl libc (on Alpine Linux). commit 31cefeb65919ed85b4a4e081aeb668e03be47977 Merge: 99dba99442f1 0286b132b7e5 Author: Ralph Little Date: 2022-04-21 14:24:31 +0000 Merge branch 'ci-debian-11' into 'master' CI: Do not allow build job failures for Debian 11 See merge request sane-project/backends!721 commit 0286b132b7e5367efbf94bf313105c6a941f8e5f Author: David Ward Date: 2022-04-21 00:57:24 -0400 CI: Do not allow build job failures for Debian 11 With the changes in commit a519a3529aad30524125b103b9362d30af694733, the CI build jobs targeting Debian 11 pass (without compiler warnings). Enforce this going forward. commit 944bb49e56c1e0d5768f14cfacb2e04310ee3642 Author: David Ward Date: 2022-04-21 00:48:22 -0400 CI: Use Debian stable again to build docs and run "make distcheck" This reverts commit b458cb14c6c7e92818d5ed2aae925c7686a63393, now that the CI build jobs targeting Debian 11 pass (without compiler warnings). commit 99dba99442f1a3f42014d18e57fd3c24ed05d713 Merge: cc0d729e1f1d 172e16136910 Author: Ralph Little Date: 2022-04-21 04:52:28 +0000 Merge branch 'lockpath-group-check' into 'master' configure: Remove --with-group option for device locking See merge request sane-project/backends!713 commit 172e161369102e5e6013f576ac5c6c62920235ca Author: David Ward Date: 2022-04-06 23:56:29 -0400 configure: Remove --with-group option for device locking This option is not used anywhere in the code or the build system, except that ./configure will not build device locking support if this is set to a non-existent group. (It chooses uucp by default.) However, that check does not actually work. It attempts to run the chgrp command on a temporary file; but since ./configure is run without root privileges, that always fails unless the user running ./configure is a member of the group. Remove both the option and the broken check. It is assumed that device locking support was typically disabled in builds because of this issue, and it will now be restored except where --disable-locking is specified. commit cc0d729e1f1d952114c40e3911e5dfb145b545e7 Merge: 71152f017118 356f1c9b2725 Author: Ralph Little Date: 2022-04-21 04:40:26 +0000 Merge branch 'pixma-add-TS3400' into 'master' pixma:Add TS3400. See merge request sane-project/backends!704 commit 356f1c9b272593d9bec9f0cc576f181f9ab278a3 Author: thierry1970 Date: 2022-03-09 09:02:41 +0100 pixma:Add TS3400. commit 71152f017118401f9b6806cdc77463aa8e314eb4 Merge: 33f0751670a8 03bfd4232c7b Author: Ralph Little Date: 2022-04-21 01:23:44 +0000 Merge branch 'make-silent-rules-output' into 'master' Utilize Automake macros for silent rules output See merge request sane-project/backends!718 commit 03bfd4232c7bf7a9951a20a38d256a02581cff90 Author: David Ward Date: 2022-04-17 20:16:19 -0400 Makefile: Utilize Automake macros for silent rules output Building with --enable-silent-rules makes it easier to identify errors or warnings in the build output. Automake provides macros for custom rules to use, so that they will print a similar message to rules which run the compiler or linker: ... CC libpieusb_la-pieusb.lo CCLD libpieusb.la CCLD libsane-pieusb.la CC libsane_pixma_la-pixma-s.lo GEN pixma/pixma_sane_options.h GEN pixma/pixma_sane_options.c CC pixma/libpixma_la-pixma.lo ... This does not change the current output if silent rules are disabled. commit 939d62f83642b5ff2016549918b570299681c13d Author: David Ward Date: 2022-04-17 20:14:15 -0400 backend/Makefile: Remove unneeded references to $(srcdir) Adjust these rules to use relative pathnames, like the other rules. commit 33f0751670a822798db41d466e2ebbbd919f08ef Merge: f1e3a410370c b458cb14c6c7 Author: Ralph Little Date: 2022-04-21 01:22:50 +0000 Merge branch 'ci-bump-versions' into 'master' CI: Bump all distributions to stable versions See merge request sane-project/backends!705 commit b458cb14c6c7e92818d5ed2aae925c7686a63393 Author: David Ward Date: 2022-03-27 19:57:08 -0400 CI: Continue using Debian 10 to build docs and run "make distcheck" While outstanding issues that cause compiler warnings are being fixed, continue using Debian 10 (oldstable) rather than Debian 11 (stable) to build HTML documentation and run "make distcheck". commit 60209994a1cb3101f912f194df5fdcb420f57dc9 Author: David Ward Date: 2022-03-19 19:10:40 -0400 CI: Temporarily allow build job failures Specify -Werror in both CFLAGS and CXXFLAGS for all build jobs. For the updated build targets, temporarily allow the build job to fail while outstanding issues that cause compiler warnings are being fixed. commit 6a14383e8461f932ad96d416fc5d1d6486bf752c Author: David Ward Date: 2022-03-19 19:06:18 -0400 CI: Bump all distributions to stable versions Use Alpine 3.15.0; Debian 10.11 and 11.2; Fedora 35; and Ubuntu 20.04. commit f1e3a410370ca0a62f0a117413ac649c4d17266c Merge: f71b32c63287 a519a3529aad Author: Ralph Little Date: 2022-04-21 01:22:09 +0000 Merge branch 'mustek-usb2-status' into 'master' mustek_usb2: Replace STATUS type with SANE_Status See merge request sane-project/backends!720 commit a519a3529aad30524125b103b9362d30af694733 Author: David Ward Date: 2022-03-02 21:16:47 -0500 mustek_usb2: Replace STATUS type with SANE_Status In some cases, a return value with type SANE_Status was assigned to a variable with type STATUS. It is easiest to remove the redundant type here. commit f71b32c63287220fe743c8011bb815e3484dc267 Merge: 602b4f8d3dad b2c2b748581c Author: Ordissimo Date: 2022-04-19 12:11:24 +0000 Merge branch 'escl-pdf-mmap' into 'master' escl: convert get_PDF_data() to use GMappedFile and GBytes See merge request sane-project/backends!719 commit b2c2b748581c6649fde770458eed89e3ac529691 Author: David Ward Date: 2022-04-19 12:11:24 +0000 escl: convert get_PDF_data() to use GMappedFile and GBytes commit 602b4f8d3dad3c6f35a63ec5735d38715515e57d Author: Ralph Little Date: 2022-04-15 11:50:56 -0700 genesys: add full button support for the Canon 5600F This model spreads their GPIO buttons lines over 3 different registers: 0x6c, 0xa6 and 0x6d commit 785a935e9e8ece9beff08659611fd38732677f66 Merge: ea8d0eb91e45 877871443dc6 Author: Ordissimo Date: 2022-04-14 13:50:58 +0000 Merge branch 'FixEsclTiffSupport' into 'master' Fix inversed logic in escl_tiff.c:get_TIFF_data See merge request sane-project/backends!715 commit 877871443dc6258bfecbcacb7f7cd91896fd9952 Author: Raphael Isemann Date: 2022-04-14 07:15:39 +0200 Fix inversed logic in escl_tiff.c:get_TIFF_data malloc returns NULL on error, but this code errors out on the non-NULL pointer indicating success (which essentially makes this function always fail). Signed-off-by: Raphael Isemann commit ea8d0eb91e4564c71a30f417c161440cfd795852 Merge: 804e936a65c4 b3a04eae1a13 Author: Ralph Little Date: 2022-04-13 18:24:57 +0000 Merge branch 'genesys_8400f_sensors' into 'master' Genesys 8400f sensors See merge request sane-project/backends!712 commit b3a04eae1a131ad86836a67cf06d1587017a3dde Author: Ralph Little Date: 2022-04-06 10:13:02 -0700 genesys: added special PDF function definitions for Canon 4400f commit 54766358b50197bf818fa3bec4c9e18fb0c9004b Author: Ralph Little Date: 2022-04-03 17:37:49 -0700 genesys: corrections for 4400f buttons. Additional special PDF buttons not yet included. We need a strategy to deal with them. commit ab7b3a38e28c2b03c30d8e0115c2fb585f38adf8 Author: Ralph Little Date: 2022-04-03 14:47:51 -0700 genesys: added button definitions for Canoscan 8400F. commit 804e936a65c44d48009625fed55a7b4c31727922 Merge: 586ff11af54d b5d5b01886fa Author: Ordissimo Date: 2022-04-09 15:49:47 +0000 Merge branch 'escl-fix-type-ip' into 'master' escl: Fix ipv6 detection. See merge request sane-project/backends!714 commit b5d5b01886fa60ac4538e548e9fad8437bfe5144 Author: Ordissimo Date: 2022-04-09 17:31:36 +0200 escl: Fix ipv6 detection. commit 586ff11af54dac860d5bbb7f4e013a6f709351c1 Merge: 49dfefe8c093 baa26ffd9555 Author: Ralph Little Date: 2022-03-29 05:45:03 +0000 Merge branch 'pixma-sa-size' into 'master' pixma: fix broadcast_sa size calculation Closes #426 See merge request sane-project/backends!572 commit baa26ffd955559b544da006c2c17c8602397f0b1 Author: Timo Teräs Date: 2021-01-15 16:49:21 +0200 pixma: fix broadcast_sa size calculation The sa_size was incorrectly used on the destination buffer which likely contains wrong address family. Fixes #426 commit 49dfefe8c093e70de69868f522d96c497789fe4a Merge: c1c77436f424 732fc8cad715 Author: Ralph Little Date: 2022-03-27 22:44:16 +0000 Merge branch 'portable-filenames' into 'master' Rename doc/*/template.desc. for Windows compatibility See merge request sane-project/backends!711 commit 732fc8cad71523e24a12d20ced8808d128649baa Author: David Ward Date: 2022-03-27 17:11:56 -0400 Rename doc/*/template.desc. for Windows compatibility Windows does not allow filenames ending in a period ("."), preventing this git repository from even being checked out on a Windows system. Drop the trailing period in these two filenames. Both files will still be skipped during the build, since they are not included in DESC_FILES or DESC_EXT_FILES in doc/Makefile.am. commit c1c77436f4245983ff0d06c1c28f94715dd26bd6 Merge: e87bd848dfbd 3513af029e51 Author: Ralph Little Date: 2022-03-25 03:49:57 +0000 Merge branch '578-remove-linux-kernel-driver-for-plustek_pp-backend' into 'master' Resolve "Remove Linux kernel driver for plustek_pp backend" Closes #578 See merge request sane-project/backends!708 commit 3513af029e512a08ea0a4dd7700ea173079222ec Author: Ralph Little Date: 2022-03-23 13:26:53 -0700 plustek-pp: Removed module build files from dist make rules. commit 27a7c4518e8a2d7e31fcc2e35474a428862936b0 Author: Ralph Little Date: 2022-03-23 13:15:27 -0700 plustek-pp: removed conditionally compiled code for Linux driver. Mainly consists of removing code for #ifdef __KERNEL__ In order to keep it really clear what code has been removed, I have not corrected any formatting issues. commit 3635176eb9c926ae71ed2ebced00e01d545a40ea Author: Ralph Little Date: 2022-03-15 18:57:51 -0700 plustek_pp: Removed references to the kernel driver from doc. commit e87bd848dfbd038c8795df6172d4e63a9bdafc0d Merge: 6054620c6999 aba8c30c9275 Author: Povilas Kanapickas Date: 2022-03-21 20:41:50 +0000 Merge branch 'merge-release-1.1.x' into 'master' Merge release-1.1.x branch to master. See merge request sane-project/backends!709 commit aba8c30c92759d7479bd0e87a60be4adb932437b Merge: 6054620c6999 b68172cbd205 Author: Povilas Kanapickas Date: 2022-03-21 22:26:33 +0200 Merge branch 'release-1.1.x' into merge-release-1.1.x commit b68172cbd205aff56d91766b3bc8d46222e7669f Merge: 332edc8b7ce6 1a8a86bade20 Author: Povilas Kanapickas Date: 2022-01-28 20:57:55 +0000 Merge branch 'release-1.1.x-genesys-gl845-crash' into 'release-1.1.x' [Release 1.1.x] genesys: GL845 has vector size 257 as well See merge request sane-project/backends!689 commit 1a8a86bade20ed66ce7d68a9c8ca4de83dc7d56e Author: Povilas Kanapickas Date: 2022-01-28 22:28:04 +0200 genesys: Simplify gamma buffer setup This also makes sure that we never access the source gamma tables out of bounds which was possible previously. commit fb41d3ca04410c7901e54067068f27e6292412f4 Author: Povilas Kanapickas Date: 2022-01-28 22:19:06 +0200 genesys: Simplify interface of generate_gamma_buffer() commit aeb60735c177657ef6a4ba93a1d185c204c07506 Author: Zdenek Dohnal Date: 2022-01-20 10:55:07 +0100 genesys: GL845 has vector size 257 as well Fixes https://bugzilla.redhat.com/show_bug.cgi?id=2042316 commit 6054620c6999920b74562c268c7f6d09f9ec9fe5 Merge: ad08e768be88 2cf41870b9a0 Author: Ralph Little Date: 2022-03-19 16:20:34 +0000 Merge branch '583-samsung-scx-4824fn-support' into 'master' Resolve "Samsung SCX-4824FN Support" Closes #583 See merge request sane-project/backends!707 commit 2cf41870b9a0ce42af2db396f0246bf5c464401c Author: Ralph Little Date: 2022-03-14 16:35:19 -0700 xerox_mfp: added fix for Samsung SCX-4824FN and friends for broken JPEg support commit ad08e768be8842e134cb8334fa0f841a692ba3c8 Merge: 95e2498fe995 9cd0b1809c6e Author: Ralph Little Date: 2022-03-19 16:19:54 +0000 Merge branch 'resolve-compiler-warnings' into 'master' Resolve compiler warnings (including fixes for actual bugs) See merge request sane-project/backends!701 commit 9cd0b1809c6eec83b4e5cd88dd39db1cdaddb492 Author: David Ward Date: 2022-03-08 19:00:00 -0500 lexmark_low: Adjust calculation of bytes available in read buffer GCC warns that an unsigned value is passed to abs(), which may not be intended. To resolve this warning, adjust the code so abs() is not used and the behavior is more explicit. commit 2efdd5a3340379c49f4914ec22f1397bb2f4d61a Author: David Ward Date: 2022-03-08 19:00:00 -0500 plustek: Adjust gain calculation to avoid calling labs() labs() has a signed parameter. However the argument to it here was the difference between two unsigned values, which itself remains unsigned. GCC warned that using this in labs() might not have the intended result. By definition though, dwInc >= m_dwIdealGain >= dwDec, so labs() is not even needed in this expression. commit 5576d03afdba9360db5f215e52216ac226c739c5 Author: David Ward Date: 2022-03-08 19:00:00 -0500 genesys: Fix forward declaration for type UsbDeviceEntry This did not match the definition, causing a compiler warning. commit 6048724f22106f39d8be0cd750c4e8a64349676c Author: David Ward Date: 2022-03-08 19:00:00 -0500 coolscan2, coolscan3: Fix initialization of enum members in struct GCC warns when setting an enum member to 0 without an explicit cast. Use the corresponding value in the enum type instead. commit 47fd7538bc325cb2af90aa5dd034d44ff9bad16a Author: David Ward Date: 2022-03-08 19:00:00 -0500 Remove set but unused variables which cause compiler warnings commit a82a312d8541b92b0c34403b8723c9caaec89ab7 Author: David Ward Date: 2022-03-08 19:00:00 -0500 Remove unused functions which cause compiler warnings commit 0d032cd982ca8ee01df44584bba82a5d89d9788d Author: David Ward Date: 2022-03-08 19:00:00 -0500 Remove useless conditionals which cause compiler warnings These always evaluate to true in the context of the surrounding code. commit b3d105cba5c7f95052827a81b52ac92841865ae9 Author: David Ward Date: 2022-03-08 19:00:00 -0500 Use snprintf() instead of strncpy() for space-padded strings The output from the SCSI inquiry command uses fixed-length space-padded strings, which are copied into null-terminated strings before use. This is currently done using strncpy(), with the count parameter set to the string's fixed length. Because a null terminator is not encountered in the input, strncpy() does not write one in the output, and GCC warns about potential string truncation. A null terminator is added manually, but this is error prone (as shown by the fix for the microtek backend). Use snprintf() instead, which guarantees a null-terminated result and resolves the warnings from GCC. commit 6a16e78788967a8f4508ddd46540958274520cc4 Author: David Ward Date: 2022-03-08 19:00:00 -0500 Use strdup()/strndup() instead of direct allocation and string copy Otherwise, GCC warns about possible string truncation. This simplifies the code here as well. commit 3c3a247d256d6bc2f90150a2ff234e7ce0d6d5ae Author: David Ward Date: 2022-03-08 19:00:00 -0500 Replace deprecated uint32 with uint32_t commit db573b74ed24c04f557eaba0ecc79aa7c9f20e2a Author: David Ward Date: 2022-03-08 19:00:00 -0500 Fix format specifiers to match arguments' data types commit 1a933bba7ee24fd39eb36c793d9f5450074010e8 Author: David Ward Date: 2022-03-08 19:00:00 -0500 Cast pointer arguments if needed when using "%p" format specifier Pointer arguments must have type (void *) when printed with the format specifier "%p". commit f3162bafa837d43d0e987c060523b1566e7be0a7 Author: David Ward Date: 2022-03-08 19:00:00 -0500 Replace variable self-assignments to avoid Clang warnings These were intended to suppress GCC warnings about unused variables. However, this leads to different warnings from Clang instead. Use another approach that suppresses warnings from both compilers. commit 6be83f469b3572b9bcaf79d4f67ec217a8102cc8 Author: David Ward Date: 2022-03-08 19:00:00 -0500 Resolve compiler warnings about misleading indentation This change is targeted at specific lines of code reported by GCC or Clang as potentially causing unintended behavior. commit 6cc15e2b813e5d0cad54e76a39186f07182f8788 Author: David Ward Date: 2022-03-08 19:00:00 -0500 Adjust conditional expressions to resolve compiler warnings commit 2955089cd9782f3b79f488590be633b85c2590e7 Author: David Ward Date: 2022-03-08 19:00:00 -0500 Fix conditional expressions with unintended behavior commit 1458d23c4229a9d9fa46f93a3859d501ec7aa149 Author: David Ward Date: 2022-03-08 19:00:00 -0500 umax: Fix unintended string concatenation in array definition This causes the wrong error message to be used in the log. commit bfa69c26e666e25e8f019a20665c9045ecc3e5e4 Author: David Ward Date: 2022-03-08 19:00:00 -0500 snapscan: Fix debug logging in sense_handler() Remove an extraneous log message. Ensure that a separate log message is only printed when the preceding if-statement is true (and its string argument has actually been set). Both issues were identified from GCC warnings about indentation. commit 23addf5590718d605db4a56632789611dc903920 Author: David Ward Date: 2022-03-08 19:00:00 -0500 sm3840: Fix argument types for variadic function Values of type unsigned char are automatically promoted to a larger integer type, so they cannot be used as variadic function arguments. commit 4cf3b09e49d07d520ddce2ca2f60cdad94d8b1eb Author: David Ward Date: 2022-03-08 19:00:00 -0500 rts8822, rts8891: Replace abs() with fabs() where needed abs() has an integer parameter and returns an integer value. Floating- point values should be passed to fabs() instead. In particular, this affected calculations for gain and offset, where a value with floating-point precision is clearly expected. commit e859ea4d895e2afb76bd1742c954485da7f840f7 Author: David Ward Date: 2022-03-08 19:00:00 -0500 pieusb: Add missing calls to sanei_pieusb_convert_status() Fix cases where values of type PIEUSB_Status were used or returned when the expected type was SANE_Status. commit f8785fb6a81453ce8eb7cb9172d2a155ec4d7f76 Author: David Ward Date: 2022-03-08 19:00:00 -0500 mustek_pp: Handle status argument correctly in pa4s2_init() Currently the pointer itself is overwritten, when it is intended that the pointer be derefenced first. commit f651d6f098caf6ad7f42cc002a802f3a34be6ce1 Author: David Ward Date: 2022-03-08 19:00:00 -0500 microtek: Fix null termination of string, and adjust buffer lengths The Product Revision Level field in the SCSI inquiry data has a fixed length of 4 bytes. When copying it as a null-terminated string, place the null terminator in the correct position; currently the string has an extra character that is never initialized in memory. Reduce the length of each string buffer, so it does not extend beyond the null terminator. commit 95e2498fe995f1f5e98b18bc00f6d3a3ef612025 Merge: 7cdd2ad268a1 0c70fd560cf3 Author: Ralph Little Date: 2022-03-14 00:25:02 +0000 Merge branch 'canon_pp-weights-file' into 'master' canon_pp: Avoid buffer overflow if pathname exceeds PATH_MAX See merge request sane-project/backends!702 commit 0c70fd560cf351fb2460a0d9ab0883acb6f098de Author: David Ward Date: 2022-03-08 19:00:00 -0500 canon_pp: Avoid buffer overflow if pathname exceeds PATH_MAX If the weights file pathname is longer than PATH_MAX, it may be written past the end of the buffer on the stack; or it may be truncated when it is written to allocated memory (such that it is not null-terminated). Adjust the code to fix both issues. Dynamically allocate memory for the actual length of the pathname so that it is not constrained by PATH_MAX. commit 7cdd2ad268a149ca2d42c0ecf8c78aec36104923 Merge: 2d49a2af6500 41866df6e1b0 Author: Ralph Little Date: 2022-03-10 01:31:01 +0000 Merge branch '580-canoscan-lide-220-aborts-before-end-of-4800dpi-colour-scan' into 'master' Resolve "Canoscan LiDE 220 aborts before end of 4800dpi colour scan" Closes #580 See merge request sane-project/backends!697 commit 41866df6e1b0eb633d0a40b7e2bd13cc49ad4275 Author: Ralph Little Date: 2022-02-27 18:55:26 -0800 genesys: fix for computation of total file size exceeding unsigned Large scans that exceed 32-bits need cast to allow 64-bit size. commit 2d49a2af6500a8a55be9dece632f41ddda9ac3ba Merge: 84e907f87c95 e5ff5d06e5ec Author: Ralph Little Date: 2022-03-10 01:29:59 +0000 Merge branch 'pixma-fix-max-resolution' into 'master' pixma:Fix max resolution See merge request sane-project/backends!703 commit e5ff5d06e5ec278ffc41ffbb41bd071efb4d54a4 Author: Ordissimo Date: 2022-03-10 01:29:59 +0000 pixma:Fix max resolution commit 84e907f87c95634f7376e1984afc5f69235f5a2e Merge: 9cd8f475eb0c e1a319e72946 Author: Ralph Little Date: 2022-03-09 03:51:08 +0000 Merge branch 'resolve-sane-desc-warnings' into 'master' descriptions: Resolve multiple warnings from sane-desc See merge request sane-project/backends!698 commit e1a319e7294614075b50119727cbd7bbb67c714e Author: David Ward Date: 2022-03-02 21:16:33 -0500 descriptions: Resolve multiple warnings from sane-desc Fix the USB VID/PID in device descriptions to use lowercase hex digits. Remove an extraneous description, having no name or USB VID, from the epsonds file. Remove an extraneous interface in the description for the Brother MFC-J1300DW. Additionally, fix mispellings of "Flatbed" and "WiFi". commit 9cd8f475eb0c0a780cd6f80d54c0b13329b0af0c Merge: 4091619e07a0 84a39145e4d3 Author: Ordissimo Date: 2022-03-08 08:59:43 +0000 Merge branch 'escl-add-canon-ts-3400-series' into 'master' escl: Add canon TS-3400 series See merge request sane-project/backends!700 commit 84a39145e4d38bf69f7510922b5e34c672d37cfd Author: Ordissimo Date: 2022-03-08 08:59:43 +0000 escl: Add canon TS-3400 series commit 4091619e07a014de2621d1caa7de4d00de761eba Merge: aa153e52c85c 9512d05fe65d Author: Ralph Little Date: 2022-02-25 02:31:18 +0000 Merge branch '577-add-plustek' into 'master' Resolve "Issues making on osx" Closes #577 See merge request sane-project/backends!696 commit 9512d05fe65db493f195875f675ea7114b0b41b4 Author: Ralph Little Date: 2022-02-22 16:22:23 -0800 genesys: added conf entry for Plustek OpticFilm 7600i User reports scanner works fine, but entry missing from config. commit aa153e52c85c458b022e4e79e2877b9897651c01 Merge: f6594ed040af 047fa8498f1a Author: Ralph Little Date: 2022-02-22 17:37:48 +0000 Merge branch '577-issues-making-on-osx' into 'master' Resolve "Issues making on osx" Closes #577 See merge request sane-project/backends!694 commit 047fa8498f1a343778951693c4a281a05f8a30dc Author: Ralph Little Date: 2022-02-22 17:37:48 +0000 Resolve "Issues making on osx" commit f6594ed040af8155e6682001ce61ac5d720e8756 Merge: 312edd6d5f89 a0ca5a4fc4ec Author: Ralph Little Date: 2022-02-21 22:13:28 +0000 Merge branch '573-hp5590-read-only-values-aren-t-accessible-via-cli-options-unrecognized-option' into 'master' Resolve "[hp5590] Read only values aren't accessible via CLI options (unrecognized option)" Closes #573 See merge request sane-project/backends!693 commit a0ca5a4fc4ec3e6874eda96e5d2d65fc9a2e6fa7 Author: Ralph Little Date: 2022-02-21 22:13:28 +0000 Resolve "[hp5590] Read only values aren't accessible via CLI options (unrecognized option)" commit 312edd6d5f89ac627a6d8cd935d1e9952f215751 Author: Ralph Little Date: 2022-02-19 14:25:13 -0800 doc: Added Plustek OpticSlim 550 description, unsupported as yet commit b701c499c9ce56c6bbea14b2fac2feea37f2410f Merge: 3c3e898c5e0e 9daadb8512b4 Author: Ralph Little Date: 2022-02-17 16:48:23 +0000 Merge branch 'sane-find-scanner-output' into 'master' sane-find-scanner: Improve output for possible USB scanners Closes #575 See merge request sane-project/backends!691 commit 9daadb8512b41731a586f04414f60b7ac281c7cf Author: David Ward Date: 2022-02-17 09:16:30 -0500 sane-find-scanner: Improve output for possible USB scanners Closes #575 commit 3c3e898c5e0e6c58cc2de092f29a2726ec4c88e2 Merge: e1f1273d23ff ba07a8a7f53e Author: Ralph Little Date: 2022-02-07 20:58:45 +0000 Merge branch 'fix-install-exec-target' into 'master' backend/Makefile: Fix installation of backend libraries See merge request sane-project/backends!690 commit ba07a8a7f53ec7b2e7ddb030bb37df2099a09de5 Author: David Ward Date: 2022-02-06 23:02:01 -0500 backend/Makefile: Fix installation of backend libraries When using Automake variables like sanelib* to install files in a custom directory, the files are assumed to be platform-independent, unless these variables contain "exec" in the name. This affects whether the files are installed during "make install-data" or "make install-exec". It does not matter whether a suffix like _DATA or _LTLIBRARIES is added to this name. The packaging scripts for Debian call those Makefile targets separately and are affected by this behavior. Since the backend libraries themselves are platform-dependent files, rename these variables to execsanelib*. commit e1f1273d23ff179678f1b4960092518ab0833506 Merge: e3b5a9686fd2 2f17613dbb5e Author: Povilas Kanapickas Date: 2022-01-28 20:54:52 +0000 Merge branch 'genesys_gl845_crash' into 'master' genesys: GL845 has vector size 257 as well See merge request sane-project/backends!688 commit 2f17613dbb5e538adc12417e3132845e92c65e96 Author: Povilas Kanapickas Date: 2022-01-28 22:28:04 +0200 genesys: Simplify gamma buffer setup This also makes sure that we never access the source gamma tables out of bounds which was possible previously. commit 188cf636271fe02a1555a59925af13b34e3a409f Author: Povilas Kanapickas Date: 2022-01-28 22:19:06 +0200 genesys: Simplify interface of generate_gamma_buffer() commit d8ebd5a4d70ea727f1abdc8171f79230a26db9fa Author: Zdenek Dohnal Date: 2022-01-20 10:55:07 +0100 genesys: GL845 has vector size 257 as well Fixes https://bugzilla.redhat.com/show_bug.cgi?id=2042316 commit e3b5a9686fd224e843ef1aedefd2d6e9c7a38fe7 Merge: 156337e5cd31 c46f00620997 Author: Povilas Kanapickas Date: 2022-01-28 20:02:25 +0000 Merge branch 'release-notes-1.1.1' into 'master' NEWS: Release notes for 1.1.1 See merge request sane-project/backends!687 commit c46f00620997401eece36f238ee8fb0f1fc69dc6 Author: Povilas Kanapickas Date: 2022-01-18 22:40:39 +0200 NEWS: Release notes for 1.1.1 (cherry picked from commit a00a5f2f2cb63871aed5a6952353221f3ccfbd5b) commit 156337e5cd31e7b4c3dcce76507a0cba21990072 Merge: b09575f6be67 5529eb73d757 Author: Povilas Kanapickas Date: 2022-01-19 15:47:01 +0000 Merge branch 'fix-distcheck' into 'master' testsuite: Allow any 1.x.y version when comparing sane-desc results See merge request sane-project/backends!686 commit 5529eb73d75767c4c761d92e6d04d6900032c31a Author: Povilas Kanapickas Date: 2022-01-19 17:29:59 +0200 testsuite: Allow any 1.x.y version when comparing sane-desc results commit b09575f6be67b06f4c57a4c8455e39cff6ff72e7 Merge: 3f955dd532e9 0ac693c2c74f Author: Povilas Kanapickas Date: 2022-01-18 20:28:06 +0000 Merge branch 'releasing-procedure' into 'master' Update releasing procedure See merge request sane-project/backends!682 commit 0ac693c2c74facb9cbd7b89365779f9889cd20c5 Author: Povilas Kanapickas Date: 2022-01-18 22:12:19 +0200 doc: Update the releasing documentation commit e3b847e981c9f70fd7951dd2fc861946d2fa8800 Author: Povilas Kanapickas Date: 2022-01-18 22:12:18 +0200 newsfragments: Document the misc news fragment type commit 3e1de72bcbbc808a100ae9090b2d9c7f069dbfd0 Author: Povilas Kanapickas Date: 2022-01-18 22:12:17 +0200 Setup release notes build using towncrier commit 3f955dd532e98c4795cc43fb13e86aa83591346d Merge: 64bfc68a398b 1a66f8c55325 Author: Povilas Kanapickas Date: 2021-12-30 18:50:56 +0000 Merge branch 'make-jpeg-compression-optional' into 'master' xerox_mfp: make JPEG compression user-configurable See merge request sane-project/backends!615 commit 1a66f8c553251ef46eec06029b97bfd3bbe5e184 Author: Povilas Kanapickas Date: 2021-12-30 20:35:03 +0200 Add newsfragment commit a336f5ed2f8eb7994db2ea9d55d6b6b8dda67ece Author: Andrew Sayers Date: 2021-03-30 12:07:24 +0100 xerox_mfp: make JPEG compression user-configurable JPEG compression improves scan time, and some scanners require it for high DPI values. But it loses some data, and we already support toggling it at compile-time. Add an advanced option so users can decide at scan-time. commit 64bfc68a398b713c48eb138aaaaf25016fa870c4 Merge: 8c5ea25efab9 cf7dde3c4b43 Author: Povilas Kanapickas Date: 2021-12-30 18:37:30 +0000 Merge branch 'genesys-remove-gpl-exception' into 'master' genesys: Remove several accidental edits See merge request sane-project/backends!680 commit cf7dde3c4b4356029a91d985c56a5c8a9d174e76 Author: Povilas Kanapickas Date: 2021-12-30 20:20:40 +0200 genesys: Remove several accidental edits Fixes 59506f866d3ac4c2fc2bdadf66865e38f8e86ac4. commit 8c5ea25efab98771b023fdf3f9017ac1342e91dc Merge: 23fb2a213944 59506f866d3a Author: Povilas Kanapickas Date: 2021-12-30 17:32:07 +0000 Merge branch 'genesys-remove-gpl-exception' into 'master' genesys: Remove exception for the GPL license See merge request sane-project/backends!677 commit 59506f866d3ac4c2fc2bdadf66865e38f8e86ac4 Author: Povilas Kanapickas Date: 2021-12-30 19:16:54 +0200 genesys: Remove exception for the GPL license Several files already don't have the exception that allows uses of the code that are additional to the GPL license. I'm no longer comfortable granting this exception for my subsequent contributions, thus the exception has been removed. commit 23fb2a213944c9788db420e99866052c88cc1f5e Merge: 5591ce7c1b79 fec5e8917feb Author: Povilas Kanapickas Date: 2021-12-30 17:22:36 +0000 Merge branch 'release-notes' into 'master' Add release notes for all notable changes since 1.0.32 See merge request sane-project/backends!676 commit fec5e8917feb04167b5d497dce20415bba3e74ba Author: Povilas Kanapickas Date: 2021-12-30 18:51:21 +0200 newsfragments: Add release note for MR !622 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/622 commit a27f807608a3a98ea7816357a2a1810c2d5cbc31 Author: Povilas Kanapickas Date: 2021-12-30 18:51:20 +0200 newsfragments: Add release note for MR !621 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/621 commit 2f7da02d943df3bda9b0c43e66263102f150d0b4 Author: Povilas Kanapickas Date: 2021-12-30 18:51:19 +0200 newsfragments: Add release note for MR !620 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/620 commit 6832c5f3999972f93a7081ff61f66e0fb44a5f42 Author: Povilas Kanapickas Date: 2021-12-30 18:51:18 +0200 newsfragments: Add release note for MR !619 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/619 commit 27598c788905b9c824752293e9fd4e040e86f192 Author: Povilas Kanapickas Date: 2021-12-30 18:51:17 +0200 newsfragments: Add release note for MR !618 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/618 commit 451761272ec87906c3c35783e0c21fe10c3842fb Author: Povilas Kanapickas Date: 2021-12-30 18:51:16 +0200 newsfragments: Add release note for MR !617 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/617 commit 86e138bea47f87d09e4736a2944938b58a0fbb61 Author: Povilas Kanapickas Date: 2021-12-30 18:51:15 +0200 newsfragments: Add release note for MR !613 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/613 commit ebee3c297090de74776ae1f23970bcdb32614ed9 Author: Povilas Kanapickas Date: 2021-12-30 18:51:14 +0200 newsfragments: Add release note for MR !612 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/612 commit bdb4aa1585576dd4e398b62a179129f6fbc83818 Author: Povilas Kanapickas Date: 2021-12-30 18:51:13 +0200 newsfragments: Add release note for MR !609 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/609 commit b06e024be75f2374528e3e2af6fe8d76184b34c0 Author: Povilas Kanapickas Date: 2021-12-30 18:51:12 +0200 newsfragments: Add release note for MR !607 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/607 commit 0feacf6f581f2100bc695ab1f2d169e0809ca39d Author: Povilas Kanapickas Date: 2021-12-30 18:51:11 +0200 newsfragments: Add release note for MR !605 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/605 commit 6773f79086b7887a59b24f683760a521d39e3c70 Author: Povilas Kanapickas Date: 2021-12-30 18:51:10 +0200 newsfragments: Add release note for MR !604 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/604 commit 2f31fea9d351ed99d8e0a7bf700261926a7e88c7 Author: Povilas Kanapickas Date: 2021-12-30 18:51:09 +0200 newsfragments: Add release note for MR !675 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/675 commit 9887b162d90f3763fcfa9954c4636804d56c9a1d Author: Povilas Kanapickas Date: 2021-12-30 18:51:08 +0200 newsfragments: Add release note for MR !673 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/673 commit 85b6cd3320e2371ff92a7bb42f6bafde8fcdc0bb Author: Povilas Kanapickas Date: 2021-12-30 18:51:07 +0200 newsfragments: Add release note for MR !671 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/671 commit 77baf258b6305666409c54f9e051afe4fb1ac1b2 Author: Povilas Kanapickas Date: 2021-12-30 18:51:06 +0200 newsfragments: Add release note for MR !669 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/669 commit 3b91ab76e6438cec380c4bf2d946347e0909f403 Author: Povilas Kanapickas Date: 2021-12-30 18:51:05 +0200 newsfragments: Add release note for MR !667 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/667 commit 87778eef50c8d07ac94ad995e65309b8eb0611d3 Author: Povilas Kanapickas Date: 2021-12-30 18:51:04 +0200 newsfragments: Add release note for MR !666 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/666 commit 58007470e033d2a79ad78dfa349d18b2a3aed8a1 Author: Povilas Kanapickas Date: 2021-12-30 18:51:03 +0200 newsfragments: Add release note for MR !665 commit 5b1b01204798857e12779d0c4c028b3db0292828 Author: Povilas Kanapickas Date: 2021-12-30 18:51:02 +0200 newsfragments: Add release note for MR !663 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/663 commit cb6a7d0a90ce1c41b59bcc817daf911a6b2c8206 Author: Povilas Kanapickas Date: 2021-12-30 18:51:01 +0200 newsfragments: Add release note for MR !659 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/659 commit 5695b15dfcdbf502ffd9a3cd6af853d4100dc6bb Author: Povilas Kanapickas Date: 2021-12-30 18:51:00 +0200 newsfragments: Add release note for MR !658 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/658 commit 3dfaea18bb7661dd464fe7289919a47112981ea5 Author: Povilas Kanapickas Date: 2021-12-30 18:50:59 +0200 newsfragments: Add release note for MR !657 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/657 commit 2dff4aa09ac8bf6508097da198d5a59229304cf7 Author: Povilas Kanapickas Date: 2021-12-30 18:50:58 +0200 newsfragments: Add release note for MR !654 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/654 commit 034fa489b650fa2f5789c9adc34b7e04276e4c2b Author: Povilas Kanapickas Date: 2021-12-30 18:50:57 +0200 newsfragments: Add release note for MR !651 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/651 commit 33b6e0ae479af7c0918bf0686c6d0935f4fc8c68 Author: Povilas Kanapickas Date: 2021-12-30 18:50:56 +0200 newsfragments: Add release note for MR !650 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/650 commit bed836adad66684c3bd949d38815d29b70fdede6 Author: Povilas Kanapickas Date: 2021-12-30 18:50:55 +0200 newsfragments: Add release note for MR !550 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/550 commit 39d1f6a80ad4ad04b9229ea698e0fc3ed067c13d Author: Povilas Kanapickas Date: 2021-12-30 18:50:54 +0200 newsfragments: Add release note for MR !647 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/647 commit dc3146a31c12a8a3d1bb3bc616ab478c3d1e0360 Author: Povilas Kanapickas Date: 2021-12-30 18:50:53 +0200 newsfragments: Add release note for MR !645 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/645 commit 7cbb3ae1d4a2c3fc5b16ece9006b13f531af1e92 Author: Povilas Kanapickas Date: 2021-12-30 18:50:52 +0200 newsfragments: Add release note for MR !643 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/643 commit e9b3e39536c4d85131c18723a3d6ffd8ab516d9b Author: Povilas Kanapickas Date: 2021-12-30 18:50:51 +0200 newsfragments: Add release note for MR !642 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/642 commit 520a0df0f78cd6196e1600f83c17a8f8218f5fa2 Author: Povilas Kanapickas Date: 2021-12-30 18:50:50 +0200 newsfragments: Add release note for MR !641 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/641 commit d666430f7576f0ad69400daacbf3634cf662b2da Author: Povilas Kanapickas Date: 2021-12-30 18:50:49 +0200 newsfragments: Add release note for MR !640 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/640 commit 94933f881dfabe79de68abbc8ca0a5147b38b1eb Author: Povilas Kanapickas Date: 2021-12-30 18:50:48 +0200 newsfragments: Add release note for MR !639 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/639 commit 7e2d4efd55ee7f7947d9a38b7c6dd2657ce2da36 Author: Povilas Kanapickas Date: 2021-12-30 18:50:47 +0200 newsfragments: Add release notes for MR !638 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/638 commit 7c215610c4e293f7b5b9b8ffd93f62d1b96fe26d Author: Povilas Kanapickas Date: 2021-12-30 18:50:46 +0200 newsfragments: Add release note for MR !635 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/635 commit d9e722af63a71eb7e89f44a8a622cf9b5e285397 Author: Povilas Kanapickas Date: 2021-12-30 18:50:45 +0200 newsfragments: Add release note for MR !634 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/634 commit cc88c73ac9c2e0f69b31b15c973675c5f63814a8 Author: Povilas Kanapickas Date: 2021-12-30 18:50:44 +0200 newsfragments: Add release note for MR !633 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/633 commit 35de63ca99b34574779ce88df71d7f89d8b23541 Author: Povilas Kanapickas Date: 2021-12-30 18:50:43 +0200 newsfragments: Add release note for MR !629 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/629 commit 5b143760f38ee0f3c584882bc07e02851c4d8ad8 Author: Povilas Kanapickas Date: 2021-12-30 18:50:42 +0200 newsfragments: Add release note for MR !627 Full link: https://gitlab.com/sane-project/backends/-/merge_requests/627 commit c94a3319165512c766179dc8a7e0419e0e33f6bf Author: Povilas Kanapickas Date: 2021-12-30 18:50:41 +0200 newsfragments: Add release note for MR !628 https://gitlab.com/sane-project/backends/-/merge_requests/628 commit 141964f6529a880db27d92d0e413ffaa2c209358 Author: Povilas Kanapickas Date: 2021-12-30 18:50:40 +0200 NEWS: Move unreleased release notes to newsfragments directory commit 0b31b2c1ed6fee5beb41ee725f467e276ec80401 Author: Povilas Kanapickas Date: 2021-12-30 18:50:39 +0200 newsfragments: Document how to provide release notes commit 5591ce7c1b7979f18a6e6b1445c0be66152527f1 Merge: 4d2a6eb5137b eca148dbf446 Author: Povilas Kanapickas Date: 2021-12-28 01:29:03 +0000 Merge branch 'genesys-remove-gl847-unsupported-resolutions' into 'master' genesys: Remove unsupported resolutions on LiDE 100 and 200 Closes #383 See merge request sane-project/backends!675 commit eca148dbf44611b357bd01b4672b83737c114a28 Author: Povilas Kanapickas Date: 2021-12-28 02:49:59 +0200 genesys: Remove unsupported 75 and 100 dpi resolutions on LiDE 200 commit c86564a3e39c5407c770dca48ce37dee3c142b3c Author: Povilas Kanapickas Date: 2021-12-28 02:49:58 +0200 genesys: Remove unsupported 75 and 100 dpi resolutions on LiDE 100 commit f5d5928346e3a49914a6cd8cdd00d3e6f4556282 Author: Povilas Kanapickas Date: 2021-12-28 02:49:57 +0200 genesys: Log the target motor speed in case it can't be acquired commit 4d2a6eb5137bff1c8b0b221c7866801492d1a214 Merge: f4552acf8d62 75ac76f87a78 Author: Povilas Kanapickas Date: 2021-12-27 21:08:39 +0000 Merge branch 'genesys-gl841-fixes' into 'master' genesys: Various fixes for GL841 scanners Closes #357 See merge request sane-project/backends!673 commit 75ac76f87a78b4662ea37313e250d96e284aab86 Author: Povilas Kanapickas Date: 2021-12-27 22:21:18 +0200 genesys: Remove unneeded per-scanner register setup These register values are overwritten later. commit f06e507de738cf5bf6e4ec5eb1abc3015c4315d6 Author: Povilas Kanapickas Date: 2021-12-27 22:21:17 +0200 genesys: Fixed offset calibration on certain gl841 devices commit 334e4bd8d123a13e8cb8283b9f430d10da83de47 Author: Povilas Kanapickas Date: 2021-12-27 22:21:16 +0200 genesys: Use consistent initial exposure for led calibration on gl841 commit af175f6d4c10e52bc2b1e07b156c4bd5e8ac04e0 Author: Povilas Kanapickas Date: 2021-12-27 22:21:15 +0200 genesys: Fix inconsistent exposure values in led calib in testing mode commit 3e9431d079c320a81a3e1a3d3b4925317d03aafc Author: Povilas Kanapickas Date: 2021-12-27 22:21:14 +0200 genesys: Ensure sensor exposure is up-to-date when scanning on gl841 commit c11a0e7ab6889d5ba1cf89592bf901aa8304de19 Author: Povilas Kanapickas Date: 2021-12-27 22:21:13 +0200 genesys: Reuse gl124 led calibration acceptability criteria for gl841 commit d19ccd2fad4efc2785a57e172f17749db57011e7 Author: Povilas Kanapickas Date: 2021-12-27 22:21:12 +0200 genesys: Remove no longer used led calibration fallback code on gl841 This was used only on scanners which did not have calibration target intensity value. All gl841 scanners now have such setting, so the code can be removed. commit e540778cfaad9d5936baea6bf841db8be30d21b1 Author: Povilas Kanapickas Date: 2021-12-27 22:21:11 +0200 genesys: Specify target white level for all gl841 devices commit c1f731a7e099ec303ec0e1f667f77d973e2534bd Author: Povilas Kanapickas Date: 2021-12-27 22:21:10 +0200 genesys: Remove duplicate register write during led calibration on gl841 commit f1910ae58cdeb948189e5cd878ff5378a7134fdd Author: Povilas Kanapickas Date: 2021-12-27 22:21:09 +0200 genesys: Reset registers after move during led calibration on gl841 commit 86fe51e224daadf80577d887127c51611a53223d Author: Povilas Kanapickas Date: 2021-12-27 22:21:08 +0200 genesys: Remove erroneous register write on LiDE 80 commit 0f341bf5a0e3bc3dd2951b0dc74dd808ccc57879 Author: Povilas Kanapickas Date: 2021-12-27 22:21:07 +0200 genesys: Clean up exposure calibration on gl841 commit 4ee36a2e31edd9ffc059306233d70f3f91ab86b8 Author: Povilas Kanapickas Date: 2021-12-27 22:21:06 +0200 genesys: Use more robust gain calibration on gl841 commit bbc15435258e479cf9a5178fa79a05eecea7deab Author: Povilas Kanapickas Date: 2021-12-27 22:21:05 +0200 genesys: Remove duplicate register writes in gl841 calibration commit 675497fcd818b2dcc716ce052bb90913091f8bc2 Author: Povilas Kanapickas Date: 2021-12-27 22:21:04 +0200 genesys: Use host-side gray instead of device-side true-gray on gl841 True gray mode is not handled correctly by devices: they have bugs that lead to incorrect LED color being emitted (e.g. dark red) and thus this feature is completely unusable. At least LiDE 35/40/50, LiDE 60 and LiDE 80 are affected. Simpy disabling true-gray unfortunately leads to even worse outcome because the scanner then simply proceeds to perform a color scan. To work around these problems we do a normal color scan and then produce gray output based on the color data. This will satisfy the use cases when correct gray is needed. In cases when it is sufficient to construct gray from a single color channel, the color filter setting could be used. commit 7bbb6d8fdb8358f14be8e331c4b8a15b2b552855 Author: Povilas Kanapickas Date: 2021-12-27 22:21:03 +0200 genesys: Implement host-side gray support commit 2f030f04e296cf4e7fae49387c8bc989b7731df8 Author: Povilas Kanapickas Date: 2021-12-27 22:21:02 +0200 genesys: Implement image pipeline for merging color channels to gray commit ad842841862f7090e9062bc98b999e9f7389b786 Author: Povilas Kanapickas Date: 2021-12-27 22:21:01 +0200 genesys: Rename ImagePipelineNodeMergeMono{Lines -> LinesToColor} commit bd4f00912218fceab4818a9fe66f584af8740cf8 Author: Povilas Kanapickas Date: 2021-12-27 22:21:00 +0200 genesys: Remove true_gray variable by deriving its value directly commit 1e7502504842471498e825c8ff65b231e646d511 Author: Povilas Kanapickas Date: 2021-12-27 22:20:59 +0200 genesys: Fix incorrect scan exposure setup on gl841 When sensor exposure values are significantly different from each other the total scan exposure will be too low leading to the device becoming confused and significantly commit 601705fa3ae0c9a84f6e906fa50f1810cabbef6f Author: Povilas Kanapickas Date: 2021-12-27 22:20:58 +0200 genesys: Inline gl841_get_led_exposure() commit 6c0fafac7985c829b4be39d15fa166ac0e65bb65 Author: Povilas Kanapickas Date: 2021-12-27 22:20:57 +0200 genesys: Remove broken two-table feeding support from gl841 At least on LiDE 50 two-table feeding caused unexplained motor spin-up failures on certain motor exposures. Various register modifications showed that there's high likelihood on device-side bug. Even the official drivers don't use proper two-table feeding. commit e27d991fdfc61fad704508b98cd021d882810a8b Author: Povilas Kanapickas Date: 2021-12-27 22:20:56 +0200 genesys: Remove unused two-table feeding support from gl847 commit d4dc13f5275beaf4d93f26fd46d712b9c976566c Author: Povilas Kanapickas Date: 2021-12-27 22:20:55 +0200 genesys: Remove unused two-table feeding support from gl846 commit 137a2d676f0be466e1c707053a0c275fc172658c Author: Povilas Kanapickas Date: 2021-12-27 22:20:54 +0200 genesys: Remove unused two-table feeding support from gl124 commit f4552acf8d623e1ff6f7ffe3448d589bf8992c45 Merge: 8d028fe39883 584f16e2ee25 Author: Povilas Kanapickas Date: 2021-12-27 14:49:08 +0000 Merge branch 'pixma_new_models' into 'master' pixma: add 2021 models. See merge request sane-project/backends!671 commit 584f16e2ee25456718d6344e654008d64d992aef Author: Ordissimo Date: 2021-12-26 15:49:21 +0100 pixma: add 2021 models. Fix white space Fix build Add descriptions Reduces the length of the line commit 8d028fe398831c6b94ec4627387850cd1dfc82d6 Merge: 53c1be6ae8a1 020030080c09 Author: Ordissimo Date: 2021-12-27 13:43:20 +0000 Merge branch 'scangearmp2_add_2021_modeles' into 'master' Scangearmp2: add 2021 models See merge request sane-project/backends!672 commit 020030080c090803dca2c4922e2a219eaff988c5 Author: Ordissimo Date: 2021-12-27 14:23:37 +0100 Scangearmp2 add 2021 models commit 53c1be6ae8a1cec2c94ec6d259d72e279200b2a2 Merge: 7bc998340739 b668e92047d0 Author: Povilas Kanapickas Date: 2021-12-26 14:53:11 +0000 Merge branch 'genesys-cleanup' into 'master' genesys: Miscellaneous cleanups See merge request sane-project/backends!670 commit b668e92047d03a93fb056d2b82065ba2785a4221 Author: Povilas Kanapickas Date: 2021-12-26 16:09:17 +0200 genesys: Use {uint,int}{8,16,32,64} from std namespace This is not strictly necessary as all known C++ compilers also inject these types to the global namespace. However this is not guaranteed by the C++ standard and accordingly some code completion tools don't support this without additional configuration. commit 7c76892b9809b28b2ace8d852b0817919679ba66 Author: Povilas Kanapickas Date: 2021-12-26 16:09:16 +0200 genesys: Remove empty file commit 3752d11c34f7d03ce808a5338f93ecb76395ca61 Author: Povilas Kanapickas Date: 2021-12-26 16:09:15 +0200 genesys: Wrap very long lines commit 7bc998340739618e1f3a9181a2789d4a7f922081 Merge: fcda028e4ac8 80f6d2117047 Author: Povilas Kanapickas Date: 2021-12-26 13:15:18 +0000 Merge branch 'genesys-fix-contrast-brightness' into 'master' genesys: Enable gamma setting when contrast or brightness is adjusted Closes #271 See merge request sane-project/backends!669 commit 80f6d211704703dae299599af62fd4c93908e854 Author: Povilas Kanapickas Date: 2021-12-26 14:53:15 +0200 genesys: Enable gamma setting when contrast or brightness is adjusted Brightness and contrast adjustments are handled via gamma tables and thus gamma functionality is required for these settings to have any effect. Previously if the device has a sensor with identity gamma (gamma = {1, 1, 1}), then gamma tables were turned off, consequently brightness and contrast settings had no effect. The underlying issue was identified by Gunnar Hjalmarsson and STK. commit bf4614b76e9c54e204d65c5e42fd1ac8cdfbfaf4 Author: Povilas Kanapickas Date: 2021-12-26 14:53:14 +0200 genesys: Store contrast and brightness adjustments in session params commit fcda028e4ac861249a68b19e59bb3bb991eeacbb Merge: 4b4cc5d019ef ea0e57a05e72 Author: Povilas Kanapickas Date: 2021-12-25 01:57:10 +0000 Merge branch 'genesys-lide-gray' into 'master' genesys: Improve gray scan quality on LiDE 220 Closes #52 and #106 See merge request sane-project/backends!667 commit ea0e57a05e728a2d8efe38494cfd9a6ae0591b3f Author: Povilas Kanapickas Date: 2021-12-25 03:06:29 +0200 genesys: Improve gray scan quality on LiDE 110 The fix has been suggested by Matthew Petroff commit 8ceb1dde8da201e6cccd4cc64d2247993b6b2cc8 Author: Povilas Kanapickas Date: 2021-12-25 03:06:28 +0200 genesys: Improve gray scan quality on LiDE 120 The fix has been suggested by Matthew Petroff commit 711a3c5c143fdf6b5b05a874c0a522b7796af874 Author: Povilas Kanapickas Date: 2021-12-25 03:06:27 +0200 genesys: Improve gray scan quality on LiDE 210 The fix has been suggested by Matthew Petroff commit 723eaa5917c197cad2c4621f5d2a3f83451c0ecc Author: Povilas Kanapickas Date: 2021-12-25 03:06:26 +0200 genesys: Improve gray scan quality on LiDE 220 The fix has been suggested by Matthew Petroff commit bd0f15f5855b01c698f3a2da623ebc759401ed66 Author: Povilas Kanapickas Date: 2021-12-25 03:06:25 +0200 genesys: Move gl124 0x0c reg definition to sensor tables commit 4b4cc5d019efecc7b94865aed39b16824539ad79 Merge: 98869ebedffc 0a22da26c42f Author: Ordissimo Date: 2021-12-23 09:03:08 +0000 Merge branch 'escl-add-new-models' into 'master' escl:Added of 3 tested models Closes #540 See merge request sane-project/backends!666 commit 0a22da26c42ffb2fe2ba57ac1d494e6723fe7cd9 Author: thierry1970 Date: 2021-12-23 09:02:08 +0100 escl:Added of 3 tested models Rename model commit 98869ebedffcecacd3540a981c23f10a9e7a1943 Merge: 7394329b6904 d77743b2433b Author: Ordissimo Date: 2021-12-17 12:13:07 +0000 Merge branch 'fix-old-tls-connections' into 'master' Fix old tls connections See merge request sane-project/backends!663 commit d77743b2433b67a1b28a9a6b1af1a1a27b32d52b Author: thierry1970 Date: 2021-12-13 18:28:45 +0100 Search for the TLS version used by the device, then force the TLS version if necessary. Check constant curl_ssl Fix build commit 7394329b6904fb7c461cdc994b60fcb2212b716b Merge: c8f42d6bd5bd 4752a9c3791f Author: Ordissimo Date: 2021-12-16 11:09:33 +0000 Merge branch 'escl-ipv6' into 'master' Escl ipv6 See merge request sane-project/backends!565 commit 4752a9c3791f27e80801398e92b5bfcadecfd574 Author: Thierry HUCHARD Date: 2020-12-29 12:37:38 +0100 added support for ipv6. Fixes a possible memory overflow. commit c8f42d6bd5bd1bf2bc4cc9ec25f868ca8762f8c0 Merge: b72ac67a7025 b49deae017ea Author: Povilas Kanapickas Date: 2021-12-16 11:08:40 +0000 Merge branch 'devel/avision' into 'master' Added support for Avision FB2280E See merge request sane-project/backends!657 commit b49deae017ea893f0ffbca44fa1c26b54ee4fb11 Author: Paul Wolneykien Date: 2021-11-19 14:05:56 +0300 avision: Added support for Avision FB2280E It seems to be the same as FB2080E. Signed-off-by: Paul Wolneykien commit b72ac67a70251049ef2c682ee9d819368529cf18 Merge: dd0599ca5244 b8df4a0cf6d6 Author: Povilas Kanapickas Date: 2021-12-16 10:56:23 +0000 Merge branch 'R40' into 'master' canon_dr: Add basic support for Canon R40 scanner See merge request sane-project/backends!665 commit b8df4a0cf6d6511e6a019ce490e7742af1414f0e Author: genkn <8407030-genkn@users.noreply.gitlab.com> Date: 2021-03-15 08:11:13 +0100 canon_dr: Add basic support for Canon R40 scanner Initial support for Canon R40 SSM scanner with Letter-size ADF. Hardware provides: gray/color, simplex/duplex, full-width, 300/600dpi horizontal, with front mirrored horizontally. commit dd0599ca5244f35aa54b1e72d8b6dd8be85ee35c Merge: bfa3e39de254 40f4885ab857 Author: Povilas Kanapickas Date: 2021-12-16 10:15:41 +0000 Merge branch 'fix-dev_acquire' into 'master' xerox_mfp: return correct value in dev_acquire() See merge request sane-project/backends!614 commit 40f4885ab8570cfe7dee965fb0e6d32e3933733d Author: Andrew Sayers Date: 2021-03-30 12:16:08 +0100 xerox_mfp: return correct value in dev_acquire() dev_acquire() is expected to return 1 on success and 0 on failure. Fix the cases where it returned non-zero on error. commit bfa3e39de254ccf7db170b62cb0dc3bf055f6327 Merge: db6e4fd77fa0 75801bffd301 Author: Ralph Little Date: 2021-12-16 05:00:36 +0000 Merge branch 'scanjet_g4010' into 'master' Updated button support for HP Scanjet G4010 See merge request sane-project/backends!622 commit 75801bffd30105511e76a74ef6db8714ba1b3c7f Author: Povilas Kanapickas Date: 2021-12-15 23:51:51 +0200 genesys: Address wrong indentation warning This is separate commit because the previous commit introducing this warning is completely unrelated and would not benefit from this change being included. commit 8347191b5fd597bd2b34e74cc57b2d9849b653f8 Author: Ralph Little Date: 2021-12-07 22:16:13 -0800 genesys: Added transparency button option This is introduced primarily for the ScanJet G4010 which has "Scan Film" buttons. commit fa414e46b1c724721eabef690fa622c2c3d0478a Author: Ralph Little Date: 2021-05-05 12:37:11 -0700 genesys: updated button support for HP Scanjet G4010 commit db6e4fd77fa0f959c5dd0622b94023e4c117e5d8 Merge: 7b19eb21159c 9ed68732920f Author: Povilas Kanapickas Date: 2021-12-15 22:21:53 +0000 Merge branch 'xerox_workcentre_3025' into 'master' Add Xerox WorkCentre 3025 See merge request sane-project/backends!654 commit 9ed68732920fdcc3486e9d7093804afb980fa195 Author: Andrii Pravorskyi Date: 2021-10-26 03:42:37 +0300 xerox_mfp: Add Xerox WorkCentre 3025 commit 7b19eb21159cfe61aa55fbbc086936e440a45446 Merge: dc32df1001d9 0a038376a6ff Author: Povilas Kanapickas Date: 2021-12-15 22:17:12 +0000 Merge branch 'pixma-mf56x0' into 'master' pixma: move MF56x0 to MP730 backend See merge request sane-project/backends!628 commit 0a038376a6ffae955de95796989dafa50a327fed Author: Konstantin Tcepliaev Date: 2021-05-15 16:03:48 +0000 pixma: move MF56x0 to MP730 backend commit dc32df1001d985130b25b5833a62533a4706e925 Merge: 9ad2aa54b902 1db13a713f28 Author: Povilas Kanapickas Date: 2021-12-15 22:12:13 +0000 Merge branch 'ps-devel-fix-gcc-4-8-compile-001' into 'master' genesys: fix gcc-4.8 compile See merge request sane-project/backends!609 commit 1db13a713f281c66d3eaebab06a33d735ea9c20e Author: Peter Seiderer Date: 2021-03-15 20:53:55 +0100 genesys: fix gcc-4.8 compile Fixes: genesys/utilities.h:229:23: error: invalid initialization of non-const reference of type 'std::basic_ios&' from an rvalue of type '' stream_{stream} ^ genesys/image_pipeline.cpp:715:19: error: invalid initialization of non-const reference of type 'genesys::ImagePipelineNode&' from an rvalue of type '' source_{source} ^ Signed-off-by: Peter Seiderer commit 9ad2aa54b902379289fae7686a60cfee5c1d4521 Merge: 2e77ac40520d d32e6c411530 Author: Povilas Kanapickas Date: 2021-12-15 21:39:12 +0000 Merge branch '519-imageRUNNER1133A-support' into 'master' Added support for imageRUNNER1133A See merge request sane-project/backends!658 commit d32e6c411530bf2579539d3dc3f77c4537b09d2f Author: Mikhail Remnev Date: 2021-11-27 17:59:43 +0700 Added iclass_device for imageRUNNER1133A commit 2e77ac40520d3e5297e2a63d85edb8455e75b10e Merge: 7f06490c931e 8090d3844b15 Author: Povilas Kanapickas Date: 2021-12-15 20:55:28 +0000 Merge branch 'add-sanei-directio' into 'master' Factoring of umax_pp_low functions concerning sys/io.h See merge request sane-project/backends!521 commit 8090d3844b1555cbf51a3ec7d3c6dce8028bd492 Author: Thierry HUCHARD Date: 2021-12-15 22:37:03 +0200 tools: Reuse sanei_directio commit c7575b36791694f835433bec70ed42d38ab0c8a3 Author: Thierry HUCHARD Date: 2021-12-15 22:37:02 +0200 plustek-pp: Reuse sanei_directio for inb/outb commit 8a2a81a83c4ce4007fd59bfd373dad48e1f3093c Author: Thierry HUCHARD Date: 2021-12-15 22:37:01 +0200 sanei: Remove no longer used code from sanei_backend.h commit ab11e428940e02f46f95734cff839b0ad4bcba10 Author: Thierry HUCHARD Date: 2021-12-15 22:37:00 +0200 sanei: Reuse sanei_directio in sanei_pio commit 7d75dbad0c915500785144fa717bdbe6fdbdde33 Author: Thierry HUCHARD Date: 2021-12-15 22:36:59 +0200 sanei: Remove BEOS support from sanei_pio commit 3e8885f937ffeda9f340970c953f7fc463bad093 Author: Thierry HUCHARD Date: 2021-12-15 22:36:58 +0200 sanei: Reuse sanei_directio in sanei_pa4s2 commit abbc93df1aefa8c33d80fed2141244fda6ee8e0c Author: Thierry HUCHARD Date: 2021-12-15 22:36:57 +0200 qcam: Reuse sanei_directio commit 79e470cf9a77a4256a4b365724b11722695f3aef Author: Thierry HUCHARD Date: 2021-12-15 22:36:56 +0200 sanei: Reuse sanei_directio in sanei_ab306 commit db83d554957086a64a968e0f03f228ea8707920f Author: Thierry HUCHARD Date: 2021-12-15 22:36:55 +0200 sanei: Reuse sanei_directio in sanei_pp commit 7b5386ff6eb0a651cec293ceaae8f2f727afafe1 Author: Thierry HUCHARD Date: 2021-12-15 22:36:54 +0200 Factoring of umax_pp_low functions concerning sys/io.h to sanei lib commit 374521c4b6d7dfd2a0b53207372792dd4d447b4e Author: Thierry HUCHARD Date: 2021-12-15 22:36:53 +0200 p5_device: Rename inb/out/INB to prevent name clashes commit 7f06490c931e7bc35578334905a29d8c255c31ed Merge: 96277ac05277 ed51223cfb9d Author: Povilas Kanapickas Date: 2021-12-13 21:47:00 +0000 Merge branch 'makefile-no-long-lines' into 'master' backend/Makefile: Reduce excessive line lengths See merge request sane-project/backends!664 commit ed51223cfb9d1bfac30bd21fc5f567e335875d58 Author: Povilas Kanapickas Date: 2021-12-13 22:38:49 +0200 backend/Makefile: Reduce excessive line lengths commit 96277ac0527713a12e93c4eae14b70b0ec039a1c Merge: b183f6a05588 d829ff835afd Author: Povilas Kanapickas Date: 2021-12-08 14:53:34 +0000 Merge branch 'epjitsu-cleanup' into 'master' A few cleanups & fixes to the epjitsu backend See merge request sane-project/backends!662 commit d829ff835afd196265c49c5c5b2e86e31035ac2f Author: Peter Marschall Date: 2014-09-21 14:45:49 +0200 epjitsu: read page_width pixels from scanner This commit fixes a regression introduced in 1.0.25 for ADF scanners: the scanning page-width was always set to a fixed value, ignoring the page-width provided by the frontend. This commit restores the behaviour that allows changing the page-width. Signed-off-by: Peter Marschall commit 79e76258cb52929e715474b861db145df179364f Author: Peter Marschall Date: 2014-05-24 13:33:24 +0200 epjitsu: enforce range.max >= range.min in option descriptors In the option descriptor definitions of tl_x, tl_y, br_x & br_y make sure the the maximal value of the range is not smaller than the minimal value. Signed-off-by: Peter Marschall commit 1dab1f01210c4ddc1201d61fc4b8a32339f5ad34 Author: Peter Marschall Date: 2014-03-16 14:17:22 +0100 epjitsu: fix start & finish DBG() calls in coarsecal*() Adapt levels for start & finish logging to match the documentation and the other functions. Signed-off-by: Peter Marschall commit 16cdaf16e5ef238ddb933f5e51aca868b2fc74a0 Author: Peter Marschall Date: 2014-03-16 13:26:35 +0100 epjitsu: instrument finecal_send_cal() & finecal_get_line() with DBG() calls No functional changes, but allow for easier debugging. Signed-off-by: Peter Marschall commit 36b59bd2c1d8835de36af7f98b812fd28c1a72ac Author: Peter Marschall Date: 2014-03-04 12:09:17 +0100 epjitsu: introduce & use MAX() & MIN() This change simplifies some expressions, and hence allows for easier understanding of the intention. Signed-off-by: Peter Marschall commit b183f6a055884cfbf4a62ff840b038da180c6fa3 Merge: 952976c6a605 bb941829822c Author: Wolfram Sang Date: 2021-12-08 08:48:45 +0000 Merge branch 'epson2/add_to_main_manpage' into 'master' epson2: add driver to main sane manpage See merge request sane-project/backends!660 commit bb941829822cb9a22989434e59be2009105e1975 Author: Wolfram Sang Date: 2021-11-21 21:11:27 +0100 epson2: add driver to main sane manpage Reported missing by A E Lawrence via mail. Thanks! commit 952976c6a6056d4205a974869e6ee5a679c25ba4 Merge: 5ecd4a9d3edc 91c24b29550e Author: Wolfram Sang Date: 2021-12-08 08:47:29 +0000 Merge branch 'epson2/more_ids' into 'master' epson2/epsonds: add new IDs provided by Epson See merge request sane-project/backends!659 commit 91c24b29550e252dad2dace10ccc1ddcfa62f4b4 Author: Wolfram Sang Date: 2021-12-03 12:34:17 +0100 epson2/epsonds: add new IDs provided by Epson Sent by Nakamura Iwao via sane-devel mailing list on Nov, 29th. I only sorted the epson2 descriptions alphabetically. Subject: [sane-devel] Additional model support for epson2 / epsonds backend Message-ID: commit 5ecd4a9d3edc5c5b5553670749488c5d10b54172 Merge: 3b47d1d26a73 0bbf1155af51 Author: Ralph Little Date: 2021-12-07 20:54:54 +0000 Merge branch 'canon_xk90_ts8030' into 'master' pixma: Added testing information for Canon XK90 and added large image interleave. See merge request sane-project/backends!661 commit 0bbf1155af513b98e122a7d65dd832190ae1db18 Author: Ralph Little Date: 2021-12-07 20:54:54 +0000 pixma: Added testing information for Canon XK90 and added large image interleave. commit 3b47d1d26a73054b7138759fa7af57ed6c8e8e35 Merge: 3440ef60b3d0 72992f4e25dd Author: Ordissimo Date: 2021-10-21 06:58:39 +0000 Merge branch 'escl-segfault' into 'master' Fix segfault sane-escl. See merge request sane-project/backends!653 commit 72992f4e25ddbc000b79a2a66f41e5be98dc2120 Author: Ordissimo Date: 2021-10-20 22:25:10 +0200 Fix segfault sane-escl. commit 3440ef60b3d0ab64823534d83218e6978bda92ee Merge: 0c1faae1de3b 1b5344ba56a0 Author: Ordissimo Date: 2021-10-18 11:45:22 +0000 Merge branch 'master' into 'master' escl: Follow the 302 redirects See merge request sane-project/backends!652 commit 1b5344ba56a0d49ff583e734705acae5a88088d9 Author: Jindřich Makovička Date: 2021-02-14 13:16:48 +0100 escl: Follow the 302 redirects Recent ipp-usb redirects from http://127.0.0.1 to http://localhost commit 0c1faae1de3b0cd85b955d33887666e97315b36e Merge: 65b66849849c 3a5f128e8a79 Author: Ralph Little Date: 2021-10-14 14:47:51 +0000 Merge branch 'test_int_inexact_option' into 'master' Add int_inexact option to test SANE_INFO_INEXACT. See merge request sane-project/backends!633 commit 3a5f128e8a79252c24803c218812ac5ef5929c16 Author: Ralph Little Date: 2021-10-14 14:47:51 +0000 Add int_inexact option to test SANE_INFO_INEXACT. ---------------------------------------------------------------------- Older entries are located in the ChangeLogs/ directory, which contains a separate file for each release. (Please note: 1.0.26 and 1.1.0 were skipped as release numbers.) backends-1.3.0/INSTALL.linux000066400000000000000000000116101456256263500154710ustar00rootroot00000000000000Installation Instructions ************************* 1. Quick install: ================= $ ./autogen.sh # only if you just cloned the git repository $ ./configure $ make $ make install 2. Step by step install on Linux >=2.6, with udev: ================================================== 2.1. Install with your preferred package manager: (a) the development environment for your Linux distribution - mandatory: gcc, make, kernel header files, autoconf, autoconf-archive, python(>=2.7) - optional: git (b) missing development packages - libusb-dev or libusb-devel or libusb-compat-devel - libusb-1.0.0-dev or similar - libjpeg-dev or libjpeg8-dev or libjpeg-turbo-devel or turbojpeg-devel - libpng-dev or similar - libcurl4-gnutls-dev or similar - libxml2-dev or similar - libsnmp-dev or similar - libpoppler-glib-dev or similar 2.2. Get the latest SANE backend from git: You can download "daily git snapshot" from here: http://www.sane-project.org/snapshots/ If you prefer to use git, you can fetch a read-only copy of the git tree with this command: $ git clone https://gitlab.com/sane-project/backends.git If you already are using a self compiled SANE backend and just fetched a new copy of the git tree or if you patched some source files, there is no need to follow the installation procedure again. You only need to make and install libsane: $ make && sudo make install 2.3. Configure, make and install latest SANE backend: We install a new SANE dynamically linked shared object library in /usr/local/lib beside your system's SANE library. $ ./autogen.sh # only if you just cloned the git repository $ ./configure $ make && sudo make install If you want to change some settings for your SANE installation, please read the documentation on the website, the man pages and: $ ./configure --help Maybe you don't want to compile all scanners on your system, then you can select the backends you need, e.g. epson2 and fujitsu: $ ./configure BACKENDS="epson2 fujitsu" You can search for your scanners backend names here: http://sane-project.org/lists/sane-backends-cvs.html 2.3.1. SANE library: Register new installed SANE dynamically linked shared object library. $ sudo ldconfig -v | grep libsane libsane.so.1 -> libsane.so.1.0.25 libsane.so.1 -> libsane.so.1.0.23 This example shows that the system first find version 1.0.25 and then 1.0.23. This is the correct order. If your system first find the old version and then the new installed one, then you must change the order for library paths in /etc/ld.so.conf or you must create the new configuration file /etc/ld.so.conf.d/1-sane.conf. $ echo "/usr/local/lib" | sudo tee -a /etc/ld.so.conf.d/1-sane.conf Then you must repeat this step. 2.3.2. Localization file: $ cd /usr/share/locale//LC_MESSAGES $ sudo ln -sf /usr/local/share/locale//LC_MESSAGES/sane-backends.mo . $ cd - 2.3.3. udev rules file: $ sudo cp tools/udev/libsane.rules /etc/udev/rules.d Reconnect your scanner to the USB bus to activate the new rules file. 2.3.4. Use the scanner with normal user rights: Your user must be a member of the group scanner. $ cat /etc/group | grep scanner scanner:x:107: Create a new group scanner, if it doesn't exist. $ sudo addgroup scanner Add an existing user to group scanner. $ sudo adduser scanner After this you must logoff and login again. 2.4. Test your scanner: 2.4.1. Check the used backend version: The programs must use the installed SANE backend version, e.g. 1.0.25. $ scanimage -V scanimage (sane-backends) 1.0.25git; backend version 1.0.25 This example shows that backend and scanimage are version 1.0.25. $ /usr/bin/scanimage -V scanimage (sane-backends) 1.0.23; backend version 1.0.25 This example shows that an old scanimage (1.0.23) uses the backend 1.0.25. If you want to use xsane, start xsane and check the used version with CTRL - i. 2.4.2. Access scanner with normal user rights: $ scanimage -L If your scanner isn't recognised here, try this: $ sudo scanimage -L If this works, your user doesn't have the rights to access the scanner. However, please check and redo the installation described above. If this doesn't help, you can ask the mailing list for further support. 2.4.3. Testscan: $ cd ~ $ scanimage > test.pnm 3. Advanced Installation Information ==================================== Please read the documents INSTALL, README and README.linux. backends-1.3.0/LICENSE000066400000000000000000000041341456256263500143120ustar00rootroot00000000000000Sat Apr 23 2005 This files attempts to clarify the licensing situation for the SANE distribution. In case of doubt, the copyright information contained in each file overrides what is said here. SANE consists of three parts each of which has its own licensing terms: * The frontend programs. These programs are generally protected by the GNU General Public License. (See file COPYING.) * The backend libraries. Most backend libraries are protected by the GNU General Public License (see file COPYING), but as an exception, it is permissible to link against such a library without affecting the licensing status of the program that uses the libraries. For details, see the copyright notice at the head of the backend files (e.g., backend/dll.c). Note that not all of the backends apply the exception and that some have additional licensing constraints. E.g., the DC210 backend uses JPG code that is licensed as described in README.djpeg. * The SANE API and network protocol as put forth in the standard document. The standard is considered to be in the public domain. Anyone is free to implement SANE interface conforming applications or libraries in any way he or she sees fit. The standard is maintained at https://gitlab.com/sane-project/standard and published at https://sane-project.gitlab.io/standard/. Frequently Asked Questions about the SANE licensing: * Why don't you use the GNU LPGL ? The reason SANE isn't licensed under the GNU LGPL is very simple: the GNU LGPL didn't exist at the time SANE was first released. So, the SANE Exception was added to the GNU GPL. * Why don't you relicense SANE, now that the GNU LGPL is available ? To relicense the various pieces of code composing SANE, each and every copyright holder needs to agree with the relicensing. Unfortunately, some of the (original) backend authors cannot be contacted any longer, for various reasons; not to mention each and every contributor who sent in a patch. This effectively makes it impossible for the SANE Project to relicense SANE. backends-1.3.0/Makefile.am000066400000000000000000000123571456256263500153470ustar00rootroot00000000000000## Makefile.am -- an automake template for Makefile.in file ## Copyright (C) 2009 Chris Bagwell and Sane Developers. ## ## This file is part of the "Sane" build infra-structure. See ## included LICENSE file for license information. SUBDIRS = include lib sanei backend frontend tools doc po testsuite DIST_SUBDIRS = include lib sanei backend frontend tools doc po japi testsuite dist_doc_DATA = AUTHORS COPYING LICENSE NEWS PROBLEMS PROJECTS \ README README.aix README.beos README.darwin README.djpeg README.freebsd \ README.hp-ux README.linux README.netbsd README.openbsd README.os2 \ README.solaris README.unixware2 README.unixware7 README.windows \ README.zeta dist_doc_DATA += ChangeLog changelogsdir = $(docdir)/ChangeLogs dist_changelogs_DATA = ChangeLogs/ChangeLog-1.0.28 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.27 ## sane-backends-1.0.26 was skipped dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.25 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.24 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.23 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.22 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.21 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.20 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.19 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.18 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.17 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.16 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.15 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.14 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.13 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.12 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.11 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.10 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.9 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.8 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.7 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.6 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.5 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.4 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.3 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.2 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.1 dist_changelogs_DATA += ChangeLogs/ChangeLog-1.0.0 EXTRA_DIST = .editorconfig EXTRA_DIST += INSTALL.linux EXTRA_DIST += autogen.sh EXTRA_DIST += po/README # Just in case these come in handy during development started from a # regular source tarball. They are not needed otherwise as they are # already applied to the files in the source tarball. EXTRA_DIST += patches/ltmain.sh.patch EXTRA_DIST += patches/Rules-quot.patch EXTRA_DIST += patches/ax_create_stdint_h.19-20.m4.patch EXTRA_DIST += patches/ax_create_stdint_h.20-21.m4.patch EXTRA_DIST += tools/git-version-gen EXTRA_DIST += $(top_srcdir)/.version BUILT_SOURCES = $(top_srcdir)/.version $(top_srcdir)/.version: echo $(VERSION) > $@-t && mv $@-t $@ dist-hook: echo $(VERSION) > $(distdir)/.tarball-version # # Keep the .gitignore files sorted, and use this target to do it. # Note that git's negated ignore patterns introduce order dependency # and should therefore be sorted with much more care than we do here. # Until that has been taken care of, such files are best left alone. # PERL=perl sort-gitignore: for f in `find . -name .gitignore`; do \ if test -n "`sed -n '/^\!/p' $$f`"; then \ echo "$$f: skipping file with negated pattern" >&2; \ fi; \ $(PERL) -e 'print sort <>;' < $$f > $$f.tmptmp; \ mv $$f.tmptmp $$f; \ done # # Check to make sure only sane_ and sanei_ symbols are exported from # the libraries # libcheck: @echo "Libraries exporting 'illegal' symbols:" @for lib in backend/.libs/*.so backend/.libs/*.a; do \ lines=`nm -g $$lib|grep '\( T \)\|\( D \)'|egrep -v ' _fini| _init'|egrep -v ' sane_| sanei_'`; \ if test -n "$$lines" ; then \ echo -e "*** $$lib:\n$$lines"; \ found_errors="yes"; \ fi \ done; \ echo; \ echo "Libraries exporting 'non-standard sane_*' symbols:"; \ for lib in backend/.libs/*.so backend/.libs/*.a; do \ lines=`nm -g $$lib|grep '\( T \)\|\( D \)'|egrep ' sane_' | egrep -v 'sane_.*init|sane_.*exit|sane_.*get_devices|sane_.*open|sane_.*close|sane_.*get_option_descriptor|sane_.*control_option|sane_.*get_parameters|sane_.*start|sane_.*read|sane_.*cancel|sane_.*set_io_mode|sane_.*get_select_fd|sane_strstatus'` ; \ if test -n "$$lines" ; then \ echo -e "*** $$lib:\n$$lines"; \ found_errors="yes"; \ fi \ done; if test ! -z $$found_errors ; then \ exit 1; \ fi # Verify that backends do not contain any unwanted public symbols # anytime check or distcheck is run. # TODO: Currently this doesn't pass. Uncomment once symbols have # been cleaned up. #check-am: libcheck # Run testsuite any time installcheck or distcheck is run. # Must run testsuite after install or else shared library backends # will not be found (including test backend). installcheck-local: cd testsuite && $(MAKE) test.local md5: dist md5sum $(distdir).tar.gz > $(distdir).tar.gz.md5 sane-backends: md5 # Clean files created by custom targets in this file. clean-local: rm -f $(distdir).tar.gz.md5 # Clean files created by configure distclean-local: rm -f include/_stdint.h rm -f include/byteorder.h ACLOCAL_AMFLAGS = -I m4 # Make potential issues stick out when running `make distcheck`. DISTCHECK_CONFIGURE_FLAGS = --enable-silent-rules backends-1.3.0/NEWS000066400000000000000000002114321456256263500140050ustar00rootroot00000000000000 ## New with 1.3.0 (released 2024-02-12) ### Backends - `avision`: - Correction for ADF presence detect which caused a hang. - Internationalised some text items in the backend to make them available for translation. - `epsonds`: - Some small changes to option documentation. - Added support for models: ES-C220, DS-C330, and DS-C490. - `escl`: - Fix for pwf:Version item. - Fix for crash in libjpeg when cropping image. - Fix for issue with configured device when AVAHI not running. - Fix for build without poppler-glib. - `fujitsu`: - Added support for Ricoh scanners: FI-8040, FI-70F. - `genesys`: - Added missing configuration for Plustek OpticFilm 7500i and Plustek OpticFilm 8100. - `hpljm1005`: - Fixes to maximum geometries and various cropping and geometry details specifically for the HP LaserJet M1005. - `lexmark_X2670`: - Added new backend for the Lexmark X26xx series of scanners. - `pixma`: - Fix to prevent mode change when source is selected. Mode should not change if still valid for source. - Added support for models: TS8630 Series, XK110 Series, GX3000 Series, GX4000 Series, G3070 Series, G2070 Series, G4070 Series, G3030 Series, G2030 Series, TC-20M, TC-5200M, TR4500 Series, TS8700 series, XK120 series, GX1000 series, GX2000 series, TS6630 series, TS7700 series, TS7600i series, TS6730 series, TR7800 series, TS7700i series, TS7700A series, GX6500 series, i-SENSYS MF750 Series - `snapscan`: - Corrections for Agfa ARCUS 1200 scan area. - `test`: - Fix for a crash in the backend. - `xerox_mfp`: - Added support for Dell 1135n, which is a clone of the Samsung SCX-4623F. - Added support for Xerox WorkCentre 3335. - Added JPEG mitigation for modules that don't properly support JPEG output: Xerox WorkCentre 3225 - Fix to remove USB CLEAR HALT during initialisation which can potentially lock up devices. - General fixes to support the SCX-4521F model. ### Frontends - `saned`: - Default scan buffer is now 1MB. -B option added to permit the buffersize to be set. - Documentation fixes. - `scanimage`: - Fix for keyboard prompt processing - Default scan buffer is now 1MB. --buffer-size and -B options now require an argument to be provided. - Fix for PDF generation, missing newline in generated file. ### Build - Fixes for clang-16 compiler - Fixes for FreeBSD build. - Fixes for escl build option involving poppler-glib. - Optionally take build version from ChangeLog if not other version source is available. - Fixes to autoconf files to remove unnecessary warnings from test logs. - Fix for macOS builds related to threading support: SANE in macOS doesn't work for multi-process with libusb. ### Tools - sane-find-scanner: Added some Linux SCSI device names since some later potential names are missing. Better solution in the pipeline. ### Documentation - Fixes for a number of man pages. - Added documentation for a number of Ricoh scanners which are rebranded products acquired from Fujitsu. ### Miscellaneous - sanei_config: Fix for a potential crash. - New Georgian translation added. - Translations updated for: British English, Ukrainian, French and Simplified Chinese. ## New with 1.2.1 (released 2023-02-05) ### Backends - `avision`: - Minor fix for protocol packet length. - Added “basic” support for Avision AD345F. Note that multi-sheet scanning is problematic. - Fix for 32-bit limitation on scan line size which caused early abort of scan. - Fix for minimum resolution for devices with AV_ASIC_C6. - Various small logic errors addressed. - `canon`: Use the common model name instead of string from the device. - `canon_lide70`: Some small corrections to option support. - `canon_pp`: Potential buffer overrun issue addressed. - `canon-dr`: Added support for lifecycle counters for dr-x10c scanners. - `epjitsu`: Hang fix for Scansnap S1300i. - `epsonds`: Added support for Epson XP-2200 Series devices. - `escl`: - Fixes related to support for HP M277dw. - Fix for Canon TS-3400 series resolution support. - Added support for Epson EcoTank L3160. - Fix for segfault crash. - Performance and memory use enhancements through the use of memory-mapped I/O. - Fix for TIFF file type generation failure. - IPv6 detection improvements. - `fujitsu`: - Contrast/brightness fixes for Fujitsu ix500. - Support confirmed for Fujitsu fi-7300NX. - Support added for ScanSnap ix1400. - Added support for ScanPartner SP30. - `genesys`: - Fixes for unit test build errors related to GCC 12. - Fix for 32-bit limitation on scan line size which caused early abort. of scan. - Build issue fix for OSX. - Button support for Canon 8400f scanner corrected. - Support added back in for Plustek OpticFilm 7600i: config file entry was missing. - `kvs40xx`: Compiler warnings fixed. - `mustek_usb2`: Compiler warnings fixed. - `pixma`: - Fix for Canon TS-3400 series resolution support. - Button support for Canon MG5700 series corrected. - Added support for Canon imageRUNNER 1018/1022/1023. - Added support for Canon PIXMA TS2400 Series. - Added support for Canon PIXMA TS2600 Series. - Testing confirmed for Canon TS6400 series devices. - Testing confirmed for Canon TS3400 series devices. - Various devices’ max resolutions fixed. - Minor fix for broadcast buffer size error. - `plustek_pp`: Support removed for deprecated Linux kernel driver build. - `test`: Fix for 32-bit limitation on scan line size which caused early abort of scan. - `xerox_mfp`: - JPEG support fix for Samsung SCX-4828 FN and similar. - JPEG support fix for Samsung SCX-4824 FN and similar. ### Frontends - `sane-find-scanner`: Alteration to output to indicate that indicated devices are potentially scanners. - `saned`: Fix for poll.h header file, which caused problems on Alpine Linux. - `scanimage`: - Attempts to set readonly options will give a warning about readonly option set attempt now, instead of the misleading "unknown option" error. - Header file build fixes for FreeBSD. - Fix for backends presenting NULL attributes in deactivated options. ### Miscellaneous - Updated translation to Russian, Italian, British English and Ukraine. - Compiler warnings addressed for a number of backends. - A number of fixes for the build system: - Fix to ignore LDFLAGS environment variable in build. - Fix to not build umax_pp tool when disabled. - Fix for CLANG compiler warnings. - Various small improvements to dependency checking and CI builds. - General corrections for backends’ use of mkstemp() function. - Added support for autotools’ silent build option. - Remove –with-group configure option, since it is no longer used. - Autotools fixes for backend library installation. ## New with 1.1.1 (released 2022-01-18) ### Backends - `epson2`: Fixed support for many scanners that don't support focus command. - `epson2`: Improve reliability of long scans. - `epsonds`: Implemented support for the following Epson scanners: - DS-1610 - DS-1630 - DS-1660W - DS-310 - DS-320 - DS-360W - DS-410 - DS-530 - DS-530II - DS-531 - DS-535 - DS-535H - DS-535II - DS-570W - DS-570WII - DS-571W - DS-575W - DS-575WII - DS-70 - DS-80W - ES-200 - ES-300W - ES-300WR - ES-400 - ES-400II - ES-50 - ES-500W - ES-500WII - ES-500WR - ES-55R - ES-580W - ES-60W - ES-60WB - ES-60WW - ES-65WR - ET-2700 Series - ET-2710 Series - ET-2810 Series - ET-M2140 Series - ET-M3140 Series - EW-052A Series - FF-680W - L3150 Series - L3200 Series - L3210 Series - L3250 Series - L4150 Series - M2140 Series - M3140 Series - RR-60 - RR-600W - RR-70W - XP-2100 Series - XP-2150 Series - `epson2`: Marked XP-452 455 series as supported in documentation. - `escl`: Fixed scanning problems on certain scanners due to incorrect URL being used (https://gitlab.com/sane-project/backends/-/issues/479) - `escl`: Fixed support for different resolutions when using JPEG format. - `escl`: Fixed handling of ipp-usb redirects to localhost which previously caused certain scanners to be always reported as busy. - `escl`: Added support for Brother DCP-J772DW and Epson ET-2750 scanners. - `escl`: Marked the following scanners as supported in documentation: - Canon PIXMA G4511 - Canon PIXMA TR4550 Series - Canon PIXMA TR4551 Series - Epson ET-6100 - `escl`: Implemented support for disabling PDF output on scanners where it's broken (https://gitlab.com/sane-project/backends/-/issues/510) - `escl`: Implemented support for Canon PIXME TR4520 and TR7500 scanners. - `genesys`: Improved scan quality on Canon LiDE 35/40/50/60 by using brighter LED illumination. - `genesys`: Fixed control of contrast and brigthness on certain scanners (https://gitlab.com/sane-project/backends/-/issues/271). - `genesys`: Fixed crashes when handling slightly unexpected conditions (https://gitlab.com/sane-project/backends/-/issues/464). - `genesys`: Fixed support for Plustek Opticfilm 7200 v2 scanner. - `genesys`: Fixed button support on HP ScanJet G4010. - `genesys`: Fixed compilation on gcc-4.8. - `genesys`: Fixed incorrect LED exposure calculation leading to wrong color balance on certain resolutions on gl841 scanners - `genesys`: Improved gray scan quality on Canon LiDE 110, 120, 210, 220 (https://gitlab.com/sane-project/backends/-/issues/106, https://gitlab.com/sane-project/backends/-/issues/52). - `genesys`: Fixed issue of motor becoming stuck at certain resolutions on Canon LiDE 50 and possibly other gl841 scanners (https://gitlab.com/sane-project/backends/-/issues/357) - `genesys`: Fixed periodic black lines in gray scans on Canon LiDE 80. - `genesys`: Removed support for broken 75 and 100 dpi hardware resolutions to fix preview scans (https://gitlab.com/sane-project/backends/-/issues/383). These resolutions did not have any benefit compared to next smallest 150 dpi resolution. - `genesys`: Add support for running in embedded environments that don't support threads. - `genesys`: Fixed gray scans to use white color for illumination instead of red on Canon LiDE 35/40/50/60 and potentially other gl841 scanners. Old behavior can be restored via the color filter setting to select specific color component for the gray scan. - `genesys`: The genesys backend is now distributed under GPL v2 or later license. Previously there existed an exception that allowed additional uses of the backend. - `gt68xx`: Fixed several memory issues that can potentially lead to crashes or increased memory use. - `hp4200`: Fixed crash when using HP ScanJet 4200C (https://gitlab.com/sane-project/backends/-/issues/454). - `microtek`: Fixed support for embedded platforms using `uclibc`. - `pieusb`: Implemented support for Reflecta RPS 10M scanner. - `pieusb`: Fixed support for automatically advancing slides on DigitDia 4000. - `pixma`: Fixed compliation with `NDEBUG` macro defined. - `pixma`: Marked Canon PIXMA G4511 as supported in documentation - `scangearmp2`: Mark GX6000, GX7000, TS5400 and MX455 series as supported. - `sm3600-scanutil`: Fixed support for embedded platforms using `uclibc`. ### Frontends - Improved documentation of `scanimage` concerning options provided by backends. - `scanimage`: Improved help to specify which options are advanced. - `scanimage`: Implemented support for PDF output format. ### Miscellaneous - Added translation to Simplified Chinese. ## New with 1.0.32 (released 2021-02-14) ### Backends - all backends now respect the `local_only` parameter when `true` is passed to `sane_get_devices()` in that they do not actively go out looking for networked devices (!502) - `artec_eplus48u`: fixes configuration for AstraSlim SE (!545) - `avision`: adds the AV186+ and AV188 as supported (!532) - `avision`: fixes doubled height issue (#402) - `avision`: fixes a debug message and compiler warning (!515) - `canon_dr`: adds support for the DR-C120 and DR-C130 (#175) - `canon_dr`: adds support for uploading of fine calibration uploads - `canon_dr`: enables fine calibration for the P-208 - `canon_dr`: improves DR-C225 support (#431) - `canon_lide70`: adds support for document scanning on the Canon LiDE 600(F), thanks to a hardware donation by Georg Sauthoff - `dll`: fixes a memory leak (!537) - `epson2`: adds support for the ET-2600 (#395) - `epson2`: adds autofocus support for devices that support it (!531) - `epson2`: fixes brightness support for DS-G20000/12000XL (!529) - `epson2`: fixes an unchecked return value issue (!526) - `escl`: adds support for brightness, threshold, sharpen and contrast options (!527, !528) - `escl`: adds support for LaserJet FLowMFP M578 and MFP M630 (#424) - `escl`: adds support for DeskJet 2710, 2723 (!519) and 3760 (!554) - `escl`: adds support for the PIXMA TS-5351 (!544) and MG5765 (!517) - `escl`: adds support for the Brother HL-L258DW (!517) - `escl`: fixes Avahi device discovery (!536) - `escl`: fixes crashes for devices without a flatbed (!554) - `escl`: fixes segfaults in option handling (!557) - `escl`: fixes sleep mode (!577) - `escl`: fixes builds without libpoppler-glib-dev (#422) - `escl`: fixes a memory issue in its capability handling (#425) - `fujitsu`: fixes brightness/contrast for the iX500 - `fujitsu`: fixes memory corruption for duplex scans - `genesys`: disables support for CanoScan 4400F to prevent possible physical damage (#436) - `gt68xx`: fixes scan cancellation logic (#356) - `pixma`: adds untested support for models released in 2020 (!553) - `pixma`: adds support for ADF scans on the i-SENSYS MF260 - `pixma`: adds support for PIXMA M340 buttons and ADF status (!513) - `pixma`: adds an option to control when to calibrate (#321) - `pixma`: fixes support for the MX492 (!548) - `pixma`: fixes ADF support for the MX490 Series - `pixma`: fixes max resolution for ADF scans on the PIXMA M320 (#364) - `pixma`: fixes compile errors when libxml2 is not available (#345) - `plustek`: fixes CanoScanN650u discovery (#137) - `test`: fixes several memory leaks (!537) ### Frontends - `saned`: add support for listening on a custom or ephemeral port via a new `-p`/`--port` option (!549) - `scanimage`: fixes crashes for multi-pass and handheld scans (#408) - `scanimage`: fixes a memory leak (!551) - `scanimage`: fixes option handling for non-compliant backends (#362) ### Documentation - updates our copy of the GPL with the FSF's current postal address. This changes references to the Library GPL (LGPL-2.0) into refs to the Lesser GPL (LGPL-2.1) but does *not* affect the licensing of our backends (#320). - source code now points to the Free Software Foundation's website for copies of the GPL and LGPL (#320) - updates translations for British English and Ukrainian - adds a pointer to our Ubuntu PPA for pre-built binaries - adds SCSI IDs for the EPSON Perfection 1640SU (!509) - fixes a boat-load of spelling mistakes (!516, thanks @marschap) - updates description files for `scangearmp2` and `utsushi` external backends ### Build - removes support for `automake` maintainer mode (!522) - removes `sane` subdirectory from the lock directory to fix issues when the lock directory is on `tmpfs` (#363) - adds support for builds using GitLab "source" tarballs (#248) - fixes static link scenarios (#124) - fixes `python` invocations to use the detected program (!525) - disables `genesys` testsuite when that backend is not built (#354) - suppresses warnings about obsolete autoconf macros (#122) - fixes availability of sane-backends.pot file inputs (#344) - fixes `configure.ac` for use with `autoconf-2.70` (#409) - removes CVS keywords from the source to ease use by downstreams that still use CVS (!547) ### Miscellaneous - `udev` rules now trigger on all events other than `remove` (!541). This aims to address a kernel API change introduced in 4.14. The new behaviour may slow down `udev`'s processing of kernel events when still using our plain `udev` or `udev+acl` rules, even though these have been optimized slightly (#341). If that bothers you, now is a good time to switch to our `udev+hwdb` rules in combination with the `hwdb` database if you have not done so already. ## New with 1.0.31 (released 2020-08-23) ### Backends - adds the `canon_lide70` backend - `avision`: adds support for the KODAK i1120, fixes thread cancellation issues, disables exposure option for non-filmscanners - `canon_dr`: improves error reporting - `dmc`: fixes compiler warnings on the scan area options - `epsonds`: rewrites the network I/O following changes made to the `epson2` backend in 1.0.30 to fix security issues. Network support is still unsupported. - `escl`: adds ADF support, fixes many minor issues and improves error handling. Now also supports devices behind a proxy (needs explicit configuration). - `fujitsu`: adds support for the fi-800R and a card scanning slot, fixes a bug when reading hardware sensors on the first invocation. Adds USB IDs for fi-7800 and fi-7900. - `genesys`: adds support for the Canon 5600F, Canon LiDE 90, Plustek OpticFilm 7200 and 7200 (v2), 7400, 7600i, 8100 and 8200i. Fixes several issues with the Canon 8600F. Adds 4800dpi support for the Canon LiDE 210 and fixes 3200dpi flatbed support on the Canon 8400F. Adds an option to fill dark calibration with a constant. Adds transparency support for gl847 chipset based devices. Fixes CIS scanner support for gl842 chipset based devices. Removes lineart and image enhancement emulation support. - `gphoto`: supports the PLANon DocuPen RC800 (with a recent enough version of `gphoto2`) - `gt68xx`: modifies scan cancellation behaviour - `hp5400`: adds button support, fixes a scan cancellation crash issue - `pixma`: add supports for the i-SENSYS MF440 Series and untested support for the PIXMMA G7000 Series and GM4000 Series as well as the i-SENSYS MF720 Series. - `plustek`: fixes a potential buffer overrun - `test`: adds gamma options ### Documentation - removes the SANE Standard. This is now maintained as a separate project at https://gitlab.com/sane-project/standard. HTML and PDF versions can be found at https://sane-project.gitlab.io/standard/. - adds and updates information for multiple supported and unsupported devices - translation updates for British English, French, German, Italian and Ukrainian - `scanimage`: documents the JPEG compression level - minor improvements documenting build requirements ### Build - removes the `--with-api-spec` option from `configure` - replaces the `--enable-avahi` option with an `--with-avahi` that defaults to enabling if possible. If the option is given and the required support is not available, `configure` will exit with an error. - improves checking for libjpeg version dependent requirements - has been made reproducible (#228) - fixes build issues on MinGW ### Miscellaneous - fixes OpenBSD hotplugd scriptlets (!375) - fixes support for unconfigured USB scanners on MacOS (#38) - various fixes and improvements in the USB record/replay support - fixes a file descriptor validation test in sanei_pio - adds missing include in sanei_usb ## New with 1.0.30 (released 2020-05-17) This release fixes several security related issues and a build issue. ### Backends - `epson2`: fixes CVE-2020-12867 (GHSL-2020-075) and several memory management issues found while addressing that CVE - `epsonds`: addresses out-of-bound memory access issues to fix CVE-2020-12862 (GHSL-2020-082) and CVE-2020-12863 (GHSL-2020-083), addresses a buffer overflow fixing CVE-2020-12865 (GHSL-2020-084) and disables network autodiscovery to mitigate CVE-2020-12866 (GHSL-2020-079), CVE-2020-12861 (GHSL-2020-080) and CVE-2020-12864 (GHSL-2020-081). Note that this backend does not support network scanners to begin with. - `magicolor`: fixes a floating point exception and uninitialized data read - fixes an overflow in `sanei_tcp_read()` ### Build - fixes a build issue where linker flags would become link time dependencies (#239) ## New with 1.0.29 (released 2020-02-02) ### Backends - adds an `escl` backend (theoretically supporting *all* AirPrint devices with a scan unit) - adds support for 23 new scanner models via existing backends - significantly changes `genesys` and `pixma` backends - fixes bugs in `canon_dr`, `fujitsu`, `hp3900`, `mustek_usb2`, `plustek` and `xerox_mfp` backends - fixes *all* compiler warnings on Debian 10 (#120) - fixes portability issues for uClibc-ng and MacOS builds - adds support to record and replay USB I/O traffic - adds timestamps to debug logs ### Frontends - fixes a 32-bit arithmetic overflow issue in `scanimage` ### Documentation - updates translations for British English, Catalan, German, Ukrainian, Valencian - adds `scangearmp2` external backend descriptions - updates `hpaio` and `utsushi` external backend descriptions - adds the `ChangeLogs/` directory to the source tarball (#103) ### Build - additionally requires `libcurl` and `libxml2` to build the `escl` backend - requires `libxml2` for USB I/O recording and replay functionality - re-enables pthread support for backends that use its API directly, irrespective of the `pthread_t` type (#153) - moves the `genesys` and `pixma` backends to a directory of their own ## New with 1.0.28 (released 2019-07-31) ### Backends - adds a `ricoh2` backend - adds support for 40+ new scanner models - significantly changes `canon_dr`, `fujitsu`, `genesys`, `gt68xx`, `hp5590` and, `pixma` backends - fixes bugs in `as6e`, `avision`, `mustek` and `plustek` backends - fixes scores of compiler warnings ### Frontends - adds, changes and removes options for `saned` (see `man 8 saned` for details) - adds: `-l` (listen), `-D` (daemonize), `-o` (once), `-e` (stderr), `-u` (user) - removes: `-s` (syslog). Use `-a -d level -o -f` for the old behavior - changes: `-d` (debug). Use `-a -d level -o -f -e` for the old behavior - `saned` now cancels scans if the data connections appears to have gone away (after 4 seconds by default, configurable in `saned.conf`) - adds `--output-file` option to `scanimage` - adds `--help` option to `scan-find-scanner` - changes `sane-desc` to create relative hyperlinks ### Documentation - adds translations for Catalan, Hebrew and Valencian - updates translations for British English, Dutch, German and Ukrainian - marks unmaintained backends as such - documents USB workaround in `sane-usb` manual page ### Build - requires a C++11 compatible compiler for the `genesys` backend - requires a JPEG library when building the `pixma` backend - fixes unneeded linking when not using preloadable backend support - disables pthread support for systems with non-integral `pthread_t` type - fixes USB support detection on BeOS and OS/2 at `./configure` time - normalizes trailing whitespace in source code files. If you have local patches that no longer apply, have a look at the whitespace ignoring options of `patch` and `git apply`. New with 1.0.27 (see Note 1), released 2017-05-22: * Significant enhancements to canon_dr, epjitsu, epsonds, fujitsu, genesys, hp3500, pixma and xerox-mfp backends. * Minor updates, bugfixes or scanners added in several backends. * 30+ new scanner models supported. * Made libusb-1.0 the default for USB support (see Note 2). * Switched code from C90 to C99 (Thanks to Volker Diels-Grabsch). * Updated Linux USB3 workaround (see Note 3). * Documentation and translation updates. * Bugfixes (Avahi, threading, USB, ICC/PNG/JPEG, etc). * Reduced compiler warnings, improved code (see Note 4). Note 1: There is no 1.0.26 release of sane-backends. That number was skipped so that our development version numbers would more clearly be an extension of the prior release. This should make packaging a little easier for our downstream users. Note 2: On all systems, the --enable-libusb* flags are now ignored. Instead, the --with-usb and --without-usb flags now control support. When neither is given, USB support will be enabled if possible and disabled otherwise. If --with-usb is requested but not possible, ./configure will fail. There is no support to prefer libusb-0.1 over libusb-1.0. When libusb-1.0 is not found, libusb-0.1 will be tried. Note 3: The Linux USB3 workaround which was added in version 1.0.25 is now disabled by default. If you have difficulty using a scanner which previously worked, or intermittent scanner availability, try setting the new environment variable SANE_USB_WORKAROUND=1 before starting your frontend. Note 4: A special thanks goes to Olaf Meeuwissen for picking up the janitorial duties in sane-backends. He has found and fixed a large number of long-standing bugs, curated our bug tracker, improved our build scripts and docs, and provided user support. Thank you Olaf! New with 1.0.25, released 2015-10-03: * New backends: epsonds (Epson DS, PX and WF series), pieusb (PIE and Reflecta film/slide scanners). * Support for JPG and PNG output format in scanimage. * Significant enhancements to avision, canon_dr, epjitsu, fujitsu, genesys, kodakaio and pixma backends. * Minor updates, bugfixes or scanners added in several backends. * 300 new scanner models supported. * Workaround for USB3 problems in Linux kernel. * Added code for IR functionality. * Documentation and translation updates. * Bugfixes (threading, networking, udev rules). New with 1.0.24, released 2013-09-30: * Significant enhancements to pixma, genesys, kodakaio, fujitsu, canon_dr. * Minor updates, bugfixes or scanners added in several backends. * Added new testsuite * 51 new scanner models supported. * USB support improvements. * Improved build system (mingw64, bug fixes, default pthread on Linux). * Documentation updates. * Bugfixes. New with 1.0.23, released 2012-08-19: * New backends: kvs40xx (Panasonic KV-S40xx), kodakaio (Kodak AiO ESP and Hero) * Significant enhancements to several backends. * 30 new scanner models supported. * V4L support improvements. * Improvements for builds on multiple platforms. * Improved udev rules. * Updated software deskew algorithm. * Documentation updates. * Bugfixes. New with 1.0.22, released 2011-02-13: * New backends: kvs20xx (Panasonic KV-S20xx), magicolor (Konica-Minolta ) * Significant enhancements to several backends. * More than 80 new scanner models supported. * Support many more networked scanners. * Added -A option to scanimage * Improved build system. * Improved USB support. * Improved udev rules. * Documentation updates. * Bugfixes. New with 1.0.21, released 2010-04-25: * New backends: kodak (Kodak i18xx), kvs1025 (Panasonic KV-S10xx), p5 (Primax PagePartner) * 224 more scanner models supported. * Many backends updated. * Improved compilation on uncommon platforms. * More consistent option naming. * Scanimage no longer writes image to tty. * Modern translation infrastructure. * Improved saned network daemon. * Internal SCSI, USB, threading, TCP & UDP code updates. * Updated HAL and udev support. * New sanei_magic image processing library. * Documentation updates. * Bugfixes. New with 1.0.20, released 2009-05-03: * New backends: canon_dr (Canon DR-series), coolscan3 (Nikon LS & Coolscan), rts8891 (Umax & HP 44xx), xerox_mfp (Samsung/Xerox/Dell MFPs) * Many backends updated. * 75 more scanner models supported. * New build system (thanks Chris and Olaf). * Avahi support (thanks Julien). * More network scanners supported * Epson backend deprecated, use epson2 * Coolscan2 backend deprecated, use coolscan3 * Internal SCSI, USB, threading, TCP & UDP code updates. * Updated HAL and udev support. * Improved saned configuration. * Scanimage updates. * Updated translations. * Documentation updates. * Bugfixes. New with 1.0.19, released 2008-02-10: * New backends: cardscan (Corex Cardscan 800c), epjitsu (Epson-based Fujitsu), epson2 (various Epson scanners), hp3900 (HP ScanJet 3970 and more), hp5590 (HP ScanJet 5590 and more), hpljm1005 (HP LaserJet M1005 and more), hs2p (Ricoh IS400 series) * Updated backends: abaton, agfafocus, apple, artec, artec_eplus48u, as6e, avision, canon, coolscan, coolscan2, dc25, dell1600n_net, dll, epson, fujitsu, genesys, gt68xx, hp3500, ibm, lexmark, microtek, microtek2, mustek, nec, net, pie, pint, pixma, plustek, plustek_pp, ricoh, s9036, sm3600, sm3840, snapscan, sp15c, st400, tamarack, teco2, test, u12, umax, umax1220u, umax_pp * Scanimage detects more chipsets. * Internal scsi, usb, tcp and udp code updates. * Basic HAL .fdi creation support * Build system updates. * Updated translations. * Documentation updates. * Bugfixes. New with 1.0.18, released 2006-07-02: * New backends: dell1600n_net (Dell 1600n), hp3500 (HP ScanJet 3500 series), pixma (Canon Pixma series), stv680 (Aiptek Pencam and similar). * Updated backends: avision, canon, dll, epson, fujitsu, genesys, gt68xx, microtek2, mustek_pp, niash, plustek, snapscan, sp15c, test, u12, umax1220u. * Scanimage batch mode fixes. * Scanner lists contain USB vendor/product ids and some statistics now. * Udev/hotplug lists are generated automatically from the .desc files now. * Portability fixes (especially for DragonFlyBSD and HP/UX). * Build system updates. * Updated translations. * Documentation updates. * Bugfixes. New with 1.0.17, released 2005-12-18: * New backends: hp4200 (HP ScanJet 4200), lexmark (Lexmark X1100 series), mustek_usb2 (Mustek BearPaw 2448 TA Pro) * Updated backends: artec_eplus48u, avision, canon, epson, fujitsu, genesys, gt68xx, microtek2, mustek_pp, plustek, plustek_pp, sm3600, snapscan, umax_pp. * Hotplug/udev updates. * scanimage fixes. * sane-find-scanner finds more chipsets. * Build system fixes/updates. make now calls makedepend. * Several fixes in SANE internal functions (sanei). * Portability fixes (especially for MacOSX, FreeBSD, OpenBSD, OS/2). * Updated translations. * Documentation updates. * Bugfixes. New with 1.0.16, released 2005-08-07: * New backends: genesys, sm3840 * Updated backends: avision, canon, coolscan, epson, gphoto2, gt68xx, mustek, mustek_pp, niash, plustek, plustek_pp, qcam, sm3600, snapscan, teco2, umax_pp, v4l. * More functionality/fixes for sanei_usb and sanei_pp. * Added sanei_access to allow locking of SANE devices. * Hotplug updates. * scanimage updates. * Portability fixes (especially for OS/2, 64bit platforms, BeOS+ZETA). * Added Polish translation. Updated other translations. Fixed format strings. * Documentation updates. * Bugfixes. New with 1.0.15, released 2004-11-07: * New backends: niash. * Updated backends: agfafocus, artec_eplus48u.c, avision, canon, canon_pp, coolscan, dll, epson, fujitsu, gt68xx, hp, hp5400, leo, microtek2, mustek, mustek_pp, net, pie, plustek, plustek_pp, snapscan, sp15c, tamarack, teco2, umax, umax_pp, u12, v4l. * Portability fixes (especially for MacOS X, NetBSD, OS/2, 64 bit platforms). * sane-find-scanner detects more chipsets now. * Added Finnish translation. Updated other translations. * Several saned fixes. * Build system updates. * Documentation updates. * Bugfixes. New with 1.0.14, released 2004-05-01: * New backends: u12 * Updated backends: artec, artec_eplus48u, as6e, avision, canon630u, canon_pp, epson, fujitsu, gphoto2, gt68xx, hp, matsushita, mustek, mustek_pp, mustek_usb, plustek, plustek_pp, sm3600, snapscan, teco1, teco2, u12, umax, umax_pp, v4l. * Added scripts for USB hotplugging (Linux) * Added Danish translation. Updated other translations. * sane-find-scanner knows about more chipsets now. * Portability fixes (especially for MacOS X, NetBSD, OpenBSD, and OS/2). * Build system fixes. * Documentation updates. * Bugfixes. New with 1.0.13, released 2003-11-22: * New backends: mustek_pp_ccd (for Mustek parport CCD scanners), plustek_pp (for Plustek parport scanners, split from the plustek backend). * Updated backends: avision, canon630u, coolscan, epson, fujitsu, gphoto2, gt68xx, hp, hp5400, mustek, mustek_pp, pie, plustek, snapscan, teco2, test, umax, umax_pp. * New internal interface for parport scanners. * Networking (IPv6 + subnet support) fixes + updates. * sane-find-scanner can detect some USB scanner chipsets now. * Better support for threading. * Port to MS Windows (Cygwin). * Portability fixes (especially for FreeBSD, MacOS X, OS/2). * Build system improvements. * Added new (Bulgarian, Czech and Italian) and updated existing translations. * The level of support for scanners is now: unsupported, minimal, basic, good, complete or untested (instead of alpha, beta, stable). * Documentation updates. * Bugfixes. New with 1.0.12, released 2003-05-25: * New backends: hp5400 (Hewlett Packard 54XX series), ibm (IBM 2456, Ricoh IS-410, IS-420, and IS-430), and ma1509 (Mustek BearPaw 1200F). * Updated backends: apple, artec_eplus48u, as6e, avision, canon (FB1200S), canon630u, dc210, dc240, dll, epson, fujitsu (fi4220), gphoto2, gt68xx, hp, mustek, mustek_pp, mustek_usb, net, pie, plustek, snapscan (Agfa Arcus 1200, Epson Perfection 660), teco2 (Relisys AVEC II S3, Relisys APOLLO Express 3 and Primax Jewel 4800), test, umax, umax_pp. * Added IPv6 support for saned and net backend. * Added SCSI support for MacOS X. * Build system improvements. * USB access improvements. * Portability fixes (especially for Irix, MacOS X, OS/2 and Linux/m68k). * Added new (nl, no) and updated existing translations. * Documentation updates. * Bugfixes. New with 1.0.11, released 2003-02-09: * Security fixes for saned. New with 1.0.10, released 2003-02-01: * New backends: artec_eplus48u (Artec E+ 48U, Tevion/Medion MD 9693, Medion MD 9705, Trust Easy Webscan 19200), gt68xx (Artec Ultima 2000, some Mustek BearPaws and other GT6801/GT6816 based scanners). * Updated backends: avision, canon, coolscan2, canon_pp, dc210, dc240, dc25, dll, epson, gphoto2, hp, hpsj5s, microtek2, mustek, mustek_pp, plustek, sharp, snapscan, test, umax, umax_pp. * Improved SCSI and USB handling. * Portability fixes (especially for Linux/sparc64, MacOS X, OS/2, Tru64). * New translation system. All backends have translations for the standard options now. Added translations for Portuguese, Russian and Swedish. * Build system updates. * Various documentation updates. * Bugfixes. New with 1.0.9, released 2002-10-24: * New backends: hpsj5s (Hewlett-Packard ScanJet 5S parport scanner), teco2 (some Relisys/Primax SCSI flatbed scanners, models VM3575, VM6565, VM6586 and VM6575), teco3 (some Relisys/Piotech/Trust/Plustek SCSI flatbed scanners, model VM3552). * Updated backends: avision, canon_pp, coolscan2, dc240, dc25, epson, fujitsu, hp, leo, microtek, mustek, mustek_usb, pie, plustek, snapscan, teco1, test, umax, umax_pp, v4l. * sane-find-scanner finds more scanners and has more verbosity options now. * New tool to create gamma tables (gamma4scanimage). * Support for ICC profiles for scanimage. * USB scanner should work on all systems supported by libusb now. * Portability fixes (especially for big-endian systems, Irix, OpenStep, OS/2, Solaris). * New and updated translations for various backends. * New system for generating lists of manufacturers/backends. * Various documentation updates. * Bugfixes. New with 1.0.8, released 2002-05-27: * New backends: canon_pp (Canon parport scanners), canon630u (CanoScan 630u + 636u), coolscan2 (Nikon Coolscan LS-30, LS-40 ED, LS-4000 ED, and LS-8000 ED), fujitsu (M3091, M3093, M3096 and similar scanners), leo (Leo/Across FS-1130 scanner), matsushita (Panasonic KV-SS scanners), sceptre (Sceptre VividScan S1200), teco1 (some Teco/Relisys flatbed scanners), test (test frontends and SANE installation). * Updated backends: avision, canon, dc240, dll, epson, gphoto2, mustek, mustek_pp, mustek_usb, nec, net, plustek, pnm, sharp, sm3600, snapscan, umax, umax_pp, v4l. * New translations for several backends (de, es, fr). * Networking supports hosts without working DNS now. * SCSI system ignores USB files and doesn't block (Linux). * New test frontend and backend for testing SANE. * Several build system improvements (e.g., new libtool, better make distclean, build only shared libs by default). * Portability fixes (especially for AIX, Linux (2.2, hppa, sparc), HP-UX, NetBSD, OPENSTEP, OS/2). * Better documentation of internal functions (sanei). * Various Documentation updates. * Bugfixes. New with 1.0.7, released 2002-02-04: * New backend: umax1220u (for UMAX Astra 1220U and 2000U scanners). * Updated backends: avision (v 1.0.15), dc240 (v 0.0), dc25 (v 1.2), dll (v 1.0.5), epson (v 1.0.219), gphoto2 (v 0.0), microtek2 (v 0.95), mustek (1.0.118), mustek_usb (v 1.0.10), plustek (v 0.41), pnm (v 1.0.4), sharp (v 0.32), snapscan (v 1.4.7), st400 (v 1.6), umax (v 1.0-32), umax_pp (v 1.0.6). * Added support for translating backend options. * Added support for USB control messages and lm983x and pv6830 chipsets. * Better documentation of internal functions (sanei). * Fixed several security issues with temporary files. * Fixed several issues in the networking code. * Portability fixes (especially for OS/2, FreeBSD, and OpenBSD). * Scanimage fixes concerning TIFF output and signal handling. * Documentation updates. * Bugfixes. New with 1.0.6, released 2001-11-05: * New backends: gphoto2 (backend for gphoto2 supported cameras), mustek_usb (for Mustek USB scanners 600CU, 1200CU(+), 1200UB). * Updated backends: dc240 (v 0.0), epson (v 0.2.12), hp (0.95), m3096g (v 1.11), microtek2 (v 0.95), mustek (v1.0-113), net (v 1.0.5), pie (1.0-7), pnm (1.0-2), snapscan (v 1.4.0), umax (1.0-31), umax_pp (1). * Added generic support for USB devices. * Added USB support to sane-find-scanner. * Code and documentation updates for HP UX compatibility. * Documentation updates. * Bugfixes. New with 1.0.5, released 2001-07-01: * New backends: bh (Bell+Howell Copiscan II series), dc240 (Kodak DC240 Digital Camera), sm3600 (Microtek ScanMaker 3600), umax_pp (Umax parallel port scanners). * Updated backends: artec (v0.5.16), dc25 (v1.2), dll (v1.0.3), epson (v0.2.08), hp (v0.94), microtek (v0.12.3), microtek2(v0.9), mustek (v1.0-107), nec (v0.12), net (v1.0.3), plustek (v0.39), pnm (v1.0.1), snapscan (v1.2), tamarack (v0.5), umax (v1.0-28). * Fixed security bug in saned.c that allowed access to scanner without password. * pnm isn't installed by default anymore due to security risks * Linux-part of sanei_scsi.c was updated concerning buffersizes, devfs, and kernel 2.4. More debug output. * sanei_scsi_find_devices works on FreeBSD now. * Added support for Solaris sgen SCSI driver. * find-scanner was renamed to sane-find-scanner and is installed now. Further more it has a man page now. * There is a testsuite for sane, but only one test is implemented yet. * Better output about versions of backends and frontends. * There is a man page for SANE now (sane(7)). * Install all available documentation. * Bugfixes. New with 1.0.4, released 2000-12-21: * New backends: as6e, nec, pie. * Updated backends: artec (v0.5.15), avision (v0.2.1), epson (v0.1.39), hp (v0.93), mustek (v1.0-102), mustek_pp (v0.9-devel), plustek (v0.38), sharp (v0.32), umax (v1.0-21). * Moved frontends xscanimage and xcam to a new package (sane-frontends). * Added authentication with encrypted passwords. * Installation of shared libraries on AIX is supported now. * Fixed compilation bugs on Irix and FreeBSD. * Better debug output on platforms not supporting vararg macros. * Better documentation about SANE devicenames. * Bugfixes. New with 1.0.3, released 2000-08-12: * New backends: mustek_pp, plustek, st400, v4l. * Updated backends: Artec (v0.5.14), Coolscan (v0.4.3), Epson (v0.1.33), HP (v0.88), Mustek (v1.0-96), Sharp (v0.31), UMAX (v1.0-21). * Removed backend info sagitta.desc due to lack of interest. * Installing no longer depends on 'rev'. * Added and structured documentation. * Every backend has a man page now. * Debug messages are now sent to syslog if stderr is a socket. * New 16 bit support to scanimage. * Bugfixes. New with 1.0.2, released 2000-03-06: * Aliased and hidden backend support. * Port to UnixWare 2.x and UnixWare 7. * New script 'sane-config' to help frontend developers. * Added Ricoh IS50 support. * Updated backends: Artec (v0.5.13), CANON (v991108), DC-25 (1.2), HP (v0.86), Microtek (0.12.0), Microtek2 (v0.8), UMAX (v1.0-build-18), Epson (v0.1.15), Sharp (v0.30), sp15c (1.12) and m3096g (1.11). * Automatic document feeder support for scanimage. * New backend for Avision AV 630 CS. * New Linux SCSI Generic driver. * Upgraded to libtool 1.3.4 * GIMP compatibility patch. * OS/2 patches. * Bugfixes. New with 1.01: * Miscellaneous o configure --disable-shared works again o scanimage -T testing option added (by Nick Lamb) o in dll.conf, the pnm, dc25, and dc210 backends are now disabled (commented out) by default. The net backend is now enabled by default. The pnm backend has been disabled as it often proved confusing to novice users. The dc25 and dc210 backends were disabled because they initialize very slowly if no camera is attached. o Downgraded PNM backend to the version shipped with SANE 0.74. The scan area options as implemented in SANE 1.00 were incorrectly implemented which made the backend unreliable. * New tool to turn off Mustek 600 II N scanner (by Andreas Czechanowski) o the new binary tools/mustk600iin-off allows to turn off a Mustek 600 II N scanner (note: you'll need to install this tool manually if you want it) * New backend for Sharp scanners (by FUKUDA Kazuya & Abel Deuring) o supports the Sharp JX-610, JX-250, and JX-330 SCSI scanner o preview/lineart/haftone/threshold are untested * Updated Kodak DC25 backend (by Peter Fales). * Updated HP backend (by Peter Kirchgessner) o New features: - Option 'unload after scan' for PhotoScanner - Manual page sane-hp updated - hp.desc updated o Bugfixes: - Adjustable scan size for scan from ADF - No blanks in command line options - Segmentation fault for starting scanimage -d hp:/dev/sg0 without /dev/sgo in hp.conf Scanning from the ADF therefore should work now. * Updated Microtek backend (by Matto Marjanovic) o Fixed off-by-1 error in revision-number parsing. o dump_inquiry() now prints the "Vendor Specific String". o Added model code/vendor name for the other 600ZS. o Made get_scan_status() retry delay increase with each try (will this help the poor 600ZS?). o Corrected base resolution for Agfa StudioScan II/IIsi (400dpi). o Disabled backend's clever aspect-ratio correction code for expanded-mode scans with StudioScan II/IIsi (scanner handles it internally - smart scanner.) o Fixed gamma LUT code parsing stupidity: now correctly determines maximum allowed LUT size, and uses it. (Especially necessary for StudioScan II/IIsi.) o The E6 claims to only support 256-byte LUT's, but the backend now ignores that misinformation (and uses 1024). o Now ignores denial of expanded-mode by older E6's (model code 0x63), in addition to newer E6's. * Updated Microtek2 backend (by Bernd Schroeder) o Added support for non-blocking I/O. o Preliminary support for a strip and a slide mode. o For people, who are annoyed, if the the scanhead moves back and fourth during a scan (backtracking): There is a new option 'disable backtracking'. At present this option must be activated by uncommenting the respective entry in the included microtek2.conf file. This is an advanced option. o The X6EL should no longer produce pixel junk after one successful scan. * Updated Mustek backend (by Andreas Bolsch) o larger buffer acquired (speeds up scanning slightly) o for SE models minimal resolution increased from 60 dpi to 75 dpi o for SE models 75 dpi in color mode is no longer used as the newer firmware version seems to be even more buggy o bug in calculation of gamma table corrected (applies only to SE models, NOT related to the overflow problem which was corrected via CLIP( ) ) o default resolution is now set according to the minimal resolution (firmware dependent) o for SE models all unsupported options (speed, ... ) explicitly disabled * Updated Nikon Coolscan backend (by Didier Carlier) o Reverse the pixel order for the LS-1000. * Updated snapscan backend to v0.6 (by Kevin Charter and Petter Reinholdtsen) o Fixes the versioning problem. o Add support for SnapScan 1236s (should also work for SnapScan 600). * Updated UMAX backend (by Oliver Rauch) o Added support for Linotype Hell Saphir 2 o Changed bug in gray gamma data conversion o Added support for Nikon AX-210 o Added "Vista S-6E" as supported o Added PowerLook-II (PL-II) as supported o Added Astra 600S as supported New with 1.00: * Upgraded to libtool-1.2. This reportedly gets SANE to build on Solaris versions with a broken printf. * saned Matching of hostnames is no longer case-sensitive. * New Abaton backend (by David Huggins-Daines) Supports "Scan 300/GS" and may work with "Scan 300/S" but the latter is untested. * New Agfa Focus backend (by Karl Anders Øygard) Supports: o AGFA Focus GS (6 bit gray scale) (untested) o AGFA Focus Lineart (lineart) (untested) o AGFA Focus II (8 bit gray scale) (untested) o Siemens S9036 (8 bit gray scale) (untested) o AGFA Focus Color (24 bit color 3-pass) o AGFA Focus Color Plus (24 bit color 3-pass) * New Kodak DC210 still camera backend (by Brian J. Murrell) * New Ricoh backend (by Feico Dillema). * New HP backend. The backend formerly known as "xhp" is now the default HP backend. This backend should support HP scanners much better and was contributed by Geoffrey Dairiki and Peter Kirchgessner. - Added support for HP 6200C - Suppress halftone mode on photosmart (it is not supported) - Show calibrate button on photoscanner only for print media - Add media selection for photoscanner - Cleanup hp_scsi_s structure * Updated apple backend (by Milon Firikis). Now you should be able to scan from ColorOneScanners (in non color modes) and maybe from OneScanners (untested). * Updated Artec backend (by Chris Pinkham). * Updated Kodak DC25 backend (by Peter Fales). * Updated Microtek backend (by Matto Marjanovic). - Fix segfault on exit due to unnecessary free() in sane_close(). - Fix to red channel shift bug (which crept in during rewrite of color-handling code). - Addition of "patch level" to version codes. * Updated Microtek2 backend - Added support for ScanMaker 330, ScanMaker 636, ScanMaker E3plus, ScanMaker X6 and Phantom 636. - Other improvements (this includes support for automatic document feeders and transparency adapters, missing option descriptions were added). - Updated the manual page. * Updated Mustek backend (patches by Andreas Bolsch and Heiko Schroeder) - Heiko's patch should make resolutions >300dpi work for MFS-6000CX. Andreas's patches: - Should work with ScanExpress 6000SP, 12000SP as well as older models - Bug with MFS-12000SP corrected (poined out by Daniel Deckers) - Bug which caused xscanimage to crash after color preview corrected - Improvement(?) in high resolution Important Notes for ScanExpress models: - Resolutions below 60 dpi don't work (firmware problem). - Resolutions >300 dpi (6000 SP) or >600 dpi (12000 SP) result in different x/y-resolution as 6000 SP and 12000 SP have in fact only 300 dpi and 600 dpi CCD sensors, respectively. - Resolutions >300dpi in color mode sometimes result in corrupted images (parts duplicated/shifted *HORIZONTALLY*) depending on hardware configuration. Killing all other applications and disabling swap (if sufficient physical memory available) may help. I added some code which writes to every page of the buffer prior to scanning to fool the memory management and scanned a full page color scan with 600dpi * 1200dpi. Very slow but image seemed ok after pnmscale. - Max. scan area: 0-216mm (hor.), 2.5-294.5mm (ver.) The scanners can't scan the first 2.5mm (ver.), so you *MUST* specify the scan area according to this restriction! - The scanners support only lineart (1 bpp), gray (8 bpp), color (24 bpp). Although the scanners have 10 bit (6000 SP) or 12 bit (12000 SP) A/D-converters, they can send only 8 bit values to the host. The higher resolution may only be used via gamma table. - For compatibility only 256 entry gamma tables may be specified, the actual gamma table sent to the scanner is linearly interpolated from it. - In lineart mode only brightness may be adjusted. - Cover open/closed reported only via debug output and ignored otherwise. - Use of SCSI card supplied with scanner is *NOT* recommended. (Don't forget termination!) * Updated UMAX backend (by Oliver Rauch) - added output of umax-backend-version in sane_init - added Linotype Hell Jade2 (Vobis ScanBoostar Premium) SCSI-ID "LinoHell","Office2" as supported scanner - changed base of calculation of scanwidth and scanlegth from pixel to resolutionbase (1/300, 1/600 or 1/1200 inch) - changed calculation for width in pixels for lineart mode - changed selection for calibration mode - added inquiry for UMUX UC1200SE - corrected 12/36-bit-mode in send_gamma_data and array in umax.h * Updated SnapScan backend to v0.5 (by Kevin Charter) - bug fixes (Wolfgang, David) - addition of threshold control for lineart mode (Mikko) - Vuego 310S support (Wolfgang) - default scan area now maximal (Kevin) New with 0.74: * GIMP auto-detection should work again. * Service name for remote scanning changed from "saned" to "sane". Be sure to update your /etc/services and /etc/inetd.conf if you use remote scanning! We generally try to avoid such incompatible changes, but the name "saned" was definitely wrong, so it seemed better to change it now. * Thanks to Matto Marjanovic work, each backend now comes with a .desc file that describes exactly what devices it supports. These description files can be automatically translated into various other formats, such as HTML. See: http://www.mostang.com/sane/sane-backends.html for an example as to what this can do for you. * New backend for Kodak DC25 digital cameras (by Peter Fales). * Updated Artec backend (by Chris Pinkham). * Updated Microtek backend (by Matthew Marjanovic) o Complete rewrite of bit-shuffling, buffering, and color-handling code. o Improved rescaling algorithm for expanded resolution scans. o Support for 600GS (and maybe ZS, too) (thanks to Oliver Neukum). o Support for document autofeeder and IIG (thanks to Ross Crawford). o Fixed sane_cancel. o sane_get_parameters size estimates are now accurate to the last bit. o get_scan_status timeout increased (for 600GS), status code fixed. o Fixed parsing of 3-pass bit in INQUIRY data. o Stopped sending gamma tables to scanners that don't want them. o Made precalibration a bit more robust (always attempt at least one scan line now). o Much, much code clean-up. o Tested & working with saned. (Atrocious hack so sane_read handles buffers smaller than one scanline.) o Auto-expand pre/post hold buffers as necessary (fixes some problems with single-pass color scans). o Added configuration file option to disable precalibration logic. o Fixed document size calculations. o Added more informative scsi-II note. o Remove misnomer "Highscan" from manpage. o Updated man-page. * Updated Microtek2 backend (by Bernd Schroeder) o changed the code for gamma calculation and added a custom gamma table to the options. In some cases this requires an additional memcpy(), but the code is now leaner and cleaner. o fixed the bugs that the backend didn't compile with non gcc compilers. o added an option to control exposure times. o rewrote the code that processes the data that is received from the scanner and transfers it into the frontend buffer. The smallest unit of data that can be copied into the frontend buffer is now a pixel, no longer a complete line. o added (a yet) undocumented option "strip-height" that allows to control the number of lines that is read from the scanner with one "read image". o fixed a bug that caused scanimage to sigsegv when calling sane_exit() without having written all data into the output file. o added code to support scanners with more than 8 bit output per color. Due to the lack of such a scanner this functionality is COMPLETELY UNTESTED and there are some potential sources of bugs, but at least one could give it try. o added sanei_config_attach_matching_devices() o improved the code for the check if it really is Microtek SCSI-II scanner o fixed the "lineart inverted" bug o The threshold option in lineart mode is now in the enhancement group o changed the default for the resolution o The values for the SANE_Params struct are calculated more precisely o dito the number of scan lines that fit into the frontend buffer o changed some return statuses ( replaced SANE_STATUS_INVAL with SANE_STATUS_IO_ERROR where the first one is not allowed) o completely rewrote the end of scan and cancel scan handling o fixed another bug that caused xscanimage to crash with sigsegv under some circumstances (check of the inquiry result was wrong) o added model code for a Vobis Highscan o support for new format of configuration file (including "option ") * Updated Nikon Coolscan backend (by Didier Carlier). * Updated UMAX backend (by Oliver Rauch): o Cancelling a scan now works, cancelling a preview still makes problems! o Preview fix is activated for Astra 600S, Astra 610S and Vista S6E scanners. (calibration by driver still does not work for these scanners). - removed button for preview patch o Quality calibration / calibration by driver now works for same scanners. - added selection for type of shading data calculation o Solved problem with black area at top of the image (happened only with some SCSI-adapters). o Added gamma download format type 0 and type 1 for older scanners. o Added Astra 1220S as supported scanner: - added 36 gamma input bits support for Astra 1220S - added 36 output bits support, but there is no frontend that can handle it! o Added inquiry for Escom Image Scanner 256 (UMAX UG80). * OS/2 (by Yuri Dario): o Updated SCSI support so sanei_scsi_find_devices is supported now. o Epson parallel support added (by Rocco Caputo ) New with 0.73: * In xscanimage, the selection box now gets updated again when changing the geometry option sliders---really! * On Linux, it is now possible to specify SCSI device names by entering their SCSI ids. For example, instead of "/dev/scanner", one can now specify "scsi MUSTEK" to get all scanners manufactured by Mustek. This feature is currently supported by the all SCSI backends except microtek2. This feature is courtesy of Oliver Rauch. * Backend libraries are now installed in $(libdir)/sane (/usr/local/lib/sane/, by default). * Updated Microtek backend (Matto Marjanovic): - ScanMaker 600ZS, Agfa Arcus II, StudioScan, and StudioScan II now supported (or recognized, at least ;-). - Fixed 3-pass scanning. - Various bug-fixes (see ChangeLog for details). * New Microtek2 backend (Bernd Schroeder) This backend supports the ScanMaker 630 and possibly other newer scanners that are not supported by the "old" Microtek backend. Additional info by Bernd: This is the very first release of this backend, so consider this software to be in alpha state. The backend was developed on a Pentium (60 Mhz) with Linux 2.0.29 and a ScanMaker 630 attached to the Adaptec AHA1505E that shipped with the scanner. As frontend xscanimage was used. It is intended to work with other models, too, but there are no experiences yet. The following options are supported: - 8-bit color, 8-bit grayscale, halftone and lineart scans. - brightness, contrast, shadow, midtone and highlight control - scalar gamma correction. Options that are not yet supported include: - 3-pass scanners - more than 8 bit per color output data. Provisions are made here and there in the code, to support more than 8 bit, but that's incomplete. * configure --disable-static should now work as expected. New with 0.72: * New backend for Artec scanners. This backend is known to work with Artec AT3, but others may work too (A6000C should work). * Updated DMC and Microtek backend. * Updated UMAX backend: ** added exposure time adjustment (for high-end scanners) ** added lamp density control (for high-end scanners) ** UMAX Astra 6X0S works in color-mode now, you have to enable preview_patch! ** added support for UMAX UC1200S and UC1260, but it will not work fine! ** enabled x-resolution greater than 600 dpi if supported by scanner (e.g. UMAX S12) but it sometimes still does not work right! * Updated SnapScan backend: There is now support for the SnapScan 310 and 600 scanner models. * OS/2 and FreeBSD support should be working again. * New backend writer's guide to SANE File backend/GUIDE outlines a few rules that should help in writing a new backend. New with 0.71: * Polaroid Digital Microscope Camera (DMC) backend Written by Dianne Skoll * Apple scanner backend Written by Milon Firikis . This backend supports AppleScanner and has preliminary support for OneScanner and ColorOneScanner. * Nikon CoolScan backend Written by Didier Carlier . * Apollo Domain/OS support Contributed by Paul Walker . New with 0.70: * Preliminary version of AGFA SnapScan backend is now included. This backend is know to work with AGFA SnapScan scanners but is unlikely to work with any other AGFA See scanner.http://www.cs.ualberta.ca/~charter/snapscan.html for details. * Various minor bug fixes that prevented SANE from building on non-Linux platforms. * xscanimage now honors WM_DELETE message. * Updated UMAX backend. New between 0.6 and 0.69: * Mustek backend now supports the Transparency Adapter on the Paragon 1200SP (MFS-12000SP). * New backend for Canon scanners. This backend was written by Helmut Koeberle . It is known to work with the CanonScan 600 though not all features are supported yet. * Solaris SCSI support now exists. Thanks to Martin Huber , the SCSI backends are now usable under Solaris, too. See README.solaris for details. * AIX SCSI support now exists. Thanks to Fred Hucht & Michael Staats, the SCSI backends are now usable under AIX, too. * New backend for Tamarack and ESCOM scanners. This backend was written by Roger Wolff of BitWizard. * New backend for Siemens S9036 scanner. This backend was written by Ingo Schneider . * find-scanner (by Oliver Rauch) SANE now comes with a program called find-scanner (in the tools subdirectory) that can be used to find the device name of attaches SCSI scanners. Note that this program is not normally installed as part of the normal SANE installation as this program is not something an end-user should ever have to use. * The Mustek backend has preliminary support for the Paragon 600 II N scanner. This scanner attaches directly to a Mustek-supplied ISA card which implements a funky parallel port. For details, see the section entitled PARALLEL PORT SCANNERS in sane-mustek(5). Use at your own risk! * The location of the configuration files can now be overridden with environment variable SANE_CONFIG_DIR (see also man-pages for the backends and programs). * When preloading backends into dll, they now appear in the same order as if they had been loaded dynamically (i.e., in reverse order in which they're listed in dll.conf). * Java frontend (by Jeff Freedman) SANE now includes a Java frontend for SANE. However, the Java files are not built by default. See japi/README.JAVA for details. * There is a Java API for SANE now. See japi/README.JAVA for details. This code is courtesy of Jeff Freedman . * UMAX updates (by Oliver Rauch): - the umax backend is now fully runtime configuable---there are no longer any build-time configuration options. - Umax T630, Astra 610S, and Linotype Hell Office scanners are now supported - gamma-data now works on Astra 1200 S with 30 bits/pixel Note: None of the SANE frontends presently support 30 bits/pixel. If you're interested in fixing this, send mail to sane-devel@mostang.com. * The Mustek backend is now fully runtime configurable---there are no longer any build-time configuration options. To this end, the mustek.conf configuration file now supports options linedistance-fix, lineart-fix, and strip-height (see sane-mustek(5) for details). * New backend for Epson scanners An alpha-quality backend for Epson scanners is now included with SANE thanks to the efforts of Kazuhiro Sasayama . * OS/2 Support Thanks to Jeff Freedman SANE now supports OS/2. * New backend for Microtek scanners Thanks to the excellent work of Matthew Marjanovic , the Microtek is now taking shape. * Irix SCSI support Thanks to the work of Michael Sweet , there is now SCSI support for Irix! * Improvements to the UMAX backend (by Oliver Rauch): ** workaround for preview-bit-problem in RGB-mode (UMAX S6E ...) ** unsupported options are disabled ** now three_pass_scan should work ** new supported scanners: *** UC840 *** Astra 1200S * The Mustek configuration file (mustek.conf) now supports a configuration option to limit the height of the strip that is scanned with a single SCSI read command. The syntax is: option strip-height HEIGHT where HEIGHT is a floating point number that gives the maximum strip height in inches. This option should be set to a small value (e.g., 1 inch) when the scanner is connected to a SCSI bus shared with other devices or when using a broken SCSI driver whose timeouts expire prematurely. For best scan performance, the strip-height should be set to a large value or the option should be removed completely. See the sane-scsi(5) man-page for details on how drivers with premature timeouts can be fixed (the Linux ncr810 driver is the only known driver with this problem at this point). * The preview window now properly draws the initial window-selection. * Mustek backend now uses a SCSI command queue to avoid performance problems with three pass scanners. This can reduce scantimes from 15 minutes down to 3 minutes! * Mustek backend will now wait for up to 1 minute for a scanner to become ready if the scanner name is specified explicitly. E.g., "scanimage -d mustek" will timeout almost right away (since the Mustek device name is not specified explicitly) whereas "scanimage -d mustek:/dev/scanner" will wait for up to a minute. * HP backend now uses pixel-unit commands to accommodate ScanJet 5P. * Platform-specific SCSI setup info is now in sanei-scsi(5). * xscanimage(1) now has a section on how to run it under GIMP. * B&W qcam support should now work (reports on how well it works are welcome). * Exiting xscanimage with preview window open should no longer cause an error. * Support for OpenStep/NeXTStep added (xscanimage and xcam require an X server and the GTK+ libraries, though). User-level SCSI is supported. * SCSI support for NetBSD and FreeBSD should work now. Thanks to NOGAYA Shigeki and Amancio Hasty for relevant patches. * New man-page sane-scsi(5) with platform-specific SCSI tips and tricks. * SANE now builds on HP-UX (SCSI support untested) and IRIX (no SCSI support), too. New in 0.6: * UMAX scanners are now supported! Kudos to Oliver Rauch and Michael K. Johnson . * scan got renamed to scanimage to avoid a nameclash with an MH program by the same name. For consistency, xscan also got renamed to xscanimage. * Man-pages! There finally are at least a few man-pages. At present, the following is covered: saned.1 scanimage.1 xscanimage.1 sane-dll.5 sane-hp.5 sane-mustek.5 sane-net.5 sane-pint.5 sane-pnm.5 sane-qcam.5 sane-umax.5 * SANE no longer insists on using GCC. GCC works best, but other ANSI C compilers will now also produce usable executables. * xscanimage now supports vector options (such as gamma-tables which are also known as intensity or tonemaps). * The gamma-table (intensity/tone-map) handling changed in the Mustek backend. As a result, when using scanimage it is now necessary to specify option --custom-gamma before gamma-tables can be specified. Also, the handling of the intensity table is now handled better in color mode (it no longer overrides the color tables; instead the composition of the intensity and the color channel table is used). * The SANE header files are now isolated in the include/sane directory and those files get now installed as part of "make install". Thanks to Michael K. Johnson for this patch! * xscanimage now displays the options' documentation strings as tooltips (can be disabled via the "Preferences" menu). * scanimage now supports three-pass scanning and scanning of images whose height is not known a priori (e.g., hand-held scanners). * The Mustek backend now supports an option to force preview scans to be monochrome (good to save time on three-pass scanners). * configure can now be run from any directory, as it's supposed to (makes it easier to build SANE for multiple platforms from the same source tree). * xcam and xscanimage should now build on Solaris, too (thanks to Tristan Tarrant). * copyright info in various files have been adjusted. See LICENSE for the general idea behind SANE licensing. * Many, many bugfixes. New in 0.5: * The same xscan binary can now function as a standalone frontend or as a gimp extension. If installed as a GIMP extension, xscan will attach itself as Extensions->Acquire Image. * The pnm backend now has an option to simulate a three-pass scanner. Good for testing. * xscan now supports previewing and (persistent) preferences. * The build process should be much more robust now. It requires GNU make and gcc but should be completely unproblematic otherwise. A simple "configure" should result in a working SANE environment even on systems where dynamic loading is unavailable. Various options are available to tailor the SANE setup. See README for details. * A first implementation of the HP backend now exists (for ScanJet scanners). * A first implementation of the net backend and saned (network daemon) now exists. So it is now possible to scan across the network! See backend/net.README and frontend/saned.README for details. * xcam, a camera frontend is now included. See frontend/xcam.README for details. * Renamed metadl to dll. New in 0.4: * A first implementation of the Connectix quickcam backend now exists. At present, only color cameras are known to work, though it shouldn't be too hard to get the b&w versions to work as well. * Improvements for the command-line frontend scan: ** Option settings are now applied _before_ the help info is printed. This allows to see what the available options are with certain options in effect. ** It can now deal with SANE_INFO_RELOAD_OPTIONS. ** It now prints the current option values in the help message (except for vectors). New in 0.33: * sane_get_devices() now takes a second argument of boolean type. If it's SANE_TRUE, then the backend must return local (non-remote) devices only. * scan now uses the default-unit of `mm' (millimeters) again for lengths. Using `cm' (centimeter) proved confusing since the help messages print length values in millimeters. * Debugging can now be controlled on a per-backend basis. The debug-level of backend is set by environment variable SANE_DEBUG_. For example, to see all debug messages of the metadl backend, set SANE_DEBUG_METADL to a large value (say 128). The sanei.h file provides three macros to assist in using this debug facility: DBG_DECL to declare the integer variable that holds the debug level, DBG_INIT to initialize debugging, and DBG to print a debug message. See backend/sanei.h and backend/metadl.c for details and examples. * scan now supports setting options to "auto" mode (e.g., --brightness=auto would ask the backend to select brightness automatically if that option supports automatic mode * scan now allows abbreviating the values of a string-lists. Case is ignored and the best matches is used (either longest unique match or exact match when ignoring case) New in 0.32: * xscan improved much. See frontend/xscan.README and frontend/xscan.BUGS for details. New in 0.31: * xscan has improved much. See frontend/xscan.CHANGES for details. New in 0.3: * The location of the SANE configuration files moved from /etc/saneconf to ${prefix}/etc/sane.d. This normally expands into /usr/local/etc/sane.d. * Real build environment. It's GNU autoconf based so all you should have to say is: ./configure make make install backends-1.3.0/PROBLEMS000066400000000000000000000021321456256263500144470ustar00rootroot00000000000000- Avoiding damage on flatbed scanners Most flatbed scanners have no protection against exceeding the physical scan area height. That is, if a scan is attempted with a height that exceeds the height of the scan surface, the scanner begins making loud noises and the scan mechanism may be damaged. Thus, if you hear such a noise, IMMEDIATELY turn off the scanner. Normally, the backend will ensure that the maximum scan height is not exceeded. However, if your scanner model has not been tested yet this safety-guard may not work. Please report errors like this to the author of the backend or the sane-devel mailing list. - Security problems with saned (SANE network scanning daemon) saned does not provide confidentiality when communicating with clients. If saned is exposed directly on the network, other users may be able to intercept scanned images, or learn passwords for connecting to saned, with little effort. Client systems should connect to saned through a secure tunnel to the server instead. saned is not a trusted program and should not run with root privileges. backends-1.3.0/PROJECTS000066400000000000000000000017771456256263500144730ustar00rootroot00000000000000PROJECTS (2005-10-03) Here is a list of projects that various people have expressed interest in. Before starting working on one of these projects, it may be a good idea to get in touch with the relevant people. Please also read the files AUTHORS and README for projects that are included into the SANE-package. For frontends, look at the sane-frontends package. For backends or stand-alone drivers, add a .desc file or add an entry to unsupported.desc. Status description --------- ---------------------------------------------------------------- wanted Somebody would like to have this feature but can't do it himself. planned Only plans but no code yet. wip Work in progress. There is (more or less) working code. For details, look at the web page or contact the author. SANE on non-Unix platforms: ---------------------------- OS/2 support (wip) http://home.tiscalinet.de/fbakan/sane-os2.htm Misc: ----- TWAIN interface for xsane-win32 (planned) Oliver Rauch backends-1.3.0/README000066400000000000000000000203251456256263500141650ustar00rootroot00000000000000How to configure, build, and install SANE. Introduction: ============= SANE stands for Scanner Access Now Easy. This package contains the SANE libraries (this means backends and network scanning parts) and the command line frontend scanimage. You always find the most recent version of SANE on: http://www.sane-project.org/ At the same location there are also links to snapshots of the Git server and sometimes beta releases of sane-backends. These are unstable development versions, so be careful when using them. Please report any problems to us. See contact section for details. There are several graphical frontends available for SANE, see the list at http://www.sane-project.org/sane-frontends.html. Quick install: ============== ./autogen.sh # only if you just cloned the git repository ./configure make make install man sane Pre-built binaries: =================== In addition to versions included in releases, some platforms have more current versions available. Ubuntu PPA https://launchpad.net/~sane-project/+archive/ubuntu/sane-release sudo add-apt-repository ppa:sane-project/sane-release sudo apt update sudo apt install libsane libsane-common sane-utils Prerequisites ============= In order to build SANE, the following tools and libraries are required: - GNU make: version 3.70 or newer - ISO C99 compiler: GNU C (gcc) is recommended for best results, but any ISO C99 compliant compiler should do Some more libraries are not strictly necessary to compile SANE, but some functionality may be lost if they are not available. Make sure that these libraries and their respective header files are available before running configure. On some Linux distributions the header files are part of separate packages (e.g. usb.h in libusb-devel or libusb-dev). These must also be installed. - libusb: Strongly recommended if you use a USB scanner. Some backends won't work without libusb at all. - libjpeg (>=6B): For the dc210, dc240, and gphoto2 backends. - libieee1284 (>=0.1.5): For some parallel port backends. - libgphoto2 (>=2.0): For the gphoto2 backend. - a C++11 compliant C++ compiler for the genesys backend. If you got the source straight from the git repository, as opposed to a source tarball, you will need a few more utilities. These utilities should normally *not* be needed for source archives downloaded from the project's website at http://www.sane-project.org/. - autoconf (>= 2.69) as well as the autoconf-archive (for the AX_CXX_COMPILE_STDCXX and AX_CXX_COMPILE_STDCXX_11 macros) - automake (>=1.15) - libtool (>=2.4.6) - gettext (>=0.19.8) - git (>= 2.1.4) SANE should build on most Unix-like systems. Support for OS/2, MacOS X, BeOS, and Microsoft Windows is also available. For more details look at the operating system specific README.* files. For a detailed support matrix, see: http://www.sane-project.org/sane-support.html This table may be out of date. Please tell us about any corrections or additions. Please mention your operating system and platform and all the other details mentioned in the table. See also the contact section. Please check that there aren't any older versions of SANE installed on your system. Especially if SANE libraries are installed in a different prefix directory (e.g. /usr/lib/) this may cause problems with external frontends. Please remove these libraries (libsane.*, sane/libsane-*) by using your package manager or manually before installing this version of SANE. Configuration ============= Simply invoke configure in the top-level directory. Besides the usual GNU configure options, there are the following SANE specific options: --disable-shared Don't use shared libraries. Useful for debugging or when there is a problem building shared libraries. This implicitly turns on --disable-dynamic --enable-static as well. --disable-dynamic Disable dynamic loading of backends (in the dll backend). configure normally turns on dynamic loading when it can find the appropriate header files and libraries ( and -ldl). --enable-static Use static libraries (turned off by default). --enable-preload Preload backends into DLL backend. This is useful for debugging, when dynamic loading is unavailable, to reduce runtime linking overheads, or when you only want to distribute a single DLL with all backends available. If dynamic loading or shared libraries are unavailable or disabled, this option is turned on automatically. --enable-scsibuffersize=N Specify the buffer size of the buffer for SCSI commands. The default value is 131072 bytes (128 kb). This may be changed at runtime by setting the environment variable SANE_SG_BUFFERSIZE to the desired value. The option is Linux-only at this time. --enable-scsibuffersize and SANE_SG_BUFFERSIZE have no effect for the Mustek, Umax and Sharp backends. For these backends, the buffer size is set automatically and/or can be specified in the backend's configuration file. Please refer to the backend's man pages for details. --enable-locking Means, that some backends will use a lockfile for allowing multiple access to one scanner. This is useful, i.e. one frontend is scanning the button status and another one will scan. The path to the lock files is defined by --localstatedir at the configure step and is $localstatedir/lock. The default group is uucp and can be changed by using --with-group=newgroup. If you do not want any backend to use a lockfile, simply use --disable-locking. Note: The Plustek backend is currently the only backend that makes use of this feature. To limit the backends that are compiled, set the variable BACKENDS to the list of backends to compile. The following will limit compiling to the epson2 and fujitsu backends: ./configure BACKENDS="epson2 fujitsu" To limit the backends that are preloaded into the DLL, set the variable PRELOADABLE_BACKENDS. The following will limit compiling to the epson2 and fujitsu backends but only preloads the epson2 backend: ./configure BACKENDS="epson2 fujitsu" PRELOADABLE_BACKENDS="epson2" In addition to these configuration options, there are some more SANE-specific options and many standard-options. To get a description of available options, invoke configure with option --help. If you plan on debugging one of the SANE programs, we recommend to run configure like this: CFLAGS="-g -O -Wall" ./configure --disable-shared For operating system specific information, look at the README.* files. Build ===== To build SANE, simply type "make" in the top-level directory. To clean up the executables and libraries in the source directory, type "make clean". To restore everything to the status after unpacking the package, type "make distclean". Installation and Configuration ============================== Once the build has finished, install SANE with "make install". By default, this will place the SANE libraries in /usr/local/lib/, the configuration files in /usr/local/etc/sane.d/, and the manual pages in /usr/local/man/. The location of these directories can be overridden with configure options; see "configure --help" for details. Before running any SANE program, read the PROBLEMS file in this directory. For information on configuring and trouble-shooting the various SANE components, please refer to the manual page sane(7). The tools/ directory contains some small programs that may be helpful. They are described in tools/README. Removing ======== Type "make uninstall" to remove SANE from your system. This will also remove older versions of SANE if they have been installed at the same prefix. Warning: Your configuration files will be deleted also so make sure you have a backup. By default the configuration files are located in the directory /usr/local/etc/sane.d/. Contact ======= For questions and general discussion about SANE contact the sane-devel mailing list. You must be subscribed to the list to send mail. See http://www.sane-project.org/mailing-lists.html for details. If you want to submit a bug report or feature request please use our bug tracking system. See http://www.sane-project.org/bugs.html for details. You may also contact the author of a specific backend directly. See the AUTHORS file for a list of addresses. backends-1.3.0/README.aix000066400000000000000000000006721456256263500147500ustar00rootroot00000000000000Under AIX, you'll need the generic SCSI device driver gsc written by Matthew Jacob (nice piece of work!). When you install this driver, copy the header file gscdds.h to /usr/include, as we look for it there. Find this driver under: ftp://ftp.feral.com/pub/aix/gsc.tar.gz ftp://ftp.thp.Uni-Duisburg.DE/pub/source/gsc.tar.gz A version for AIX 4.3.3 and AIX 5.1 is also available. http://www.zago.net/aix/gsc-1.1.tar.gz backends-1.3.0/README.beos000066400000000000000000000015711456256263500151160ustar00rootroot00000000000000SANE and BeOS ------------- The information in this text is from an article of Philippe Houdoin on sane-devel (29 Aug 2002). SANE 1.0.3 was first ported to BeOS, under the name BeSANE, by Massimiliano Origgi: http://www.intuiware.com/Products/BeSANE/index.html SANE 1.0.5 BeOS port and fixes to some issues with original BeSANE port were made by Philippe Houdoin but aren't released until now. BeOS GUI SANE frontend project (by Philippe Houdoin): http://philippe.houdoin.free.fr/phil/beos/sanity/index-en.html Updates of Sanity from Kaya Memisoglu: http://www.bebits.com/app/2847 SANE was reported to work with the following configuration and features: Platform: BeOS R5.0.x ix86 Latest SANE version tested: 1.0.5 Compiler: gcc 2.95.3 User-level SCSI support: yes USB support: yes Shared library support: yes Dynamic loading support: yes X11 clients: no backends-1.3.0/README.darwin000066400000000000000000000040601456256263500154460ustar00rootroot00000000000000SANE and Darwin (Mac OS X) -------------------------- Building: --------- You may get the message: "gcc: Internal compiler error: program cc1obj got fatal signal 11". That's not a bug in SANE. Probably a compiler or hardware problem. The error usually occurs in canon.c because that's the most complex backend (at least concerning compilation). If you can't update your compiler, try to reduce optimization (e.g. CFLAGS="-O1" ./configure). Another work-around: Disable the canon backend in backend/Makefile.in (look at PRELOADABLE_BACKENDS) and rerun configure. General: -------- If scanimage -L (or any other frontend) stops with a segmentation fault in the sm3600 backend, disable sm3600 in dll.conf if you don't need it. Backends that use the function "fork" may not work at least with USB scanners. That's a limitation of MacOS X (doesn't use file descriptors for USB access). Most backends have been modified to use threads on MacOS X and should work. Others don't use fork at all. Both types of backends should work with MacOS X. Changing the backends to use threads is an ongoing effort. SCSI-scanners and Firewire scanners: ------------------------------------ There is support for SCSI and Firewire scanners but hasn't had much testing. Please send failure and success reports to the sane-devel mailing list. At least the Epson Perfection 1640SU and the CANON IX-06015C (CanoScan 600) are reported to work. USB-scanners: ------------- Work with libusb. Try "sane-find-scanner -v -v" and report success or failure to the SANE mailing list. At least the following scanners are known to work: UMAX Astra 1220U and 2000U, Epson Perfection 1640SU and 2450 Photo. Use libusb version 0.1.11 or later (or a cvs checkout from 2005-09-01 or later). If you use earlier versions the libusb code needs to be patched. Parport-scanners: ----------------- I don't have any information about these. Please contact me or the SANE mailing list if you succeeded in using one of these. 2003-12-26 Henning Meier-Geinitz 2005-05-07 Mattias Ellert backends-1.3.0/README.djpeg000066400000000000000000000473001456256263500152570ustar00rootroot00000000000000The JPEG decoder in SANE is taken mostly without change from The Independent JPEG Group's JPEG software, release 6a. Their "djpeg.README" file is included below. The only changes are to file names, e.g. djpeg.c -> sanei_jpeg.c, and function names, e.g. sanei_jpeg_start_output_ppm() The Independent JPEG Group's JPEG software ========================================== README for release 6a of 7-Feb-96 ================================= This distribution contains the sixth public release of the Independent JPEG Group's free JPEG software. You are welcome to redistribute this software and to use it for any purpose, subject to the conditions under LEGAL ISSUES, below. Serious users of this software (particularly those incorporating it into larger programs) should contact IJG at jpeg-info@uunet.uu.net to be added to our electronic mailing list. Mailing list members are notified of updates and have a chance to participate in technical discussions, etc. This software is the work of Tom Lane, Philip Gladstone, Luis Ortiz, Jim Boucher, Lee Crocker, Julian Minguillon, George Phillips, Davide Rossi, Ge' Weijers, and other members of the Independent JPEG Group. IJG is not affiliated with the official ISO JPEG standards committee. DOCUMENTATION ROADMAP ===================== This file contains the following sections: OVERVIEW General description of JPEG and the IJG software. LEGAL ISSUES Copyright, lack of warranty, terms of distribution. REFERENCES Where to learn more about JPEG. ARCHIVE LOCATIONS Where to find newer versions of this software. RELATED SOFTWARE Other stuff you should get. FILE FORMAT WARS Software *not* to get. TO DO Plans for future IJG releases. Other documentation files in the distribution are: User documentation: install.doc How to configure and install the IJG software. usage.doc Usage instructions for cjpeg, djpeg, jpegtran, rdjpgcom, and wrjpgcom. *.1 Unix-style man pages for programs (same info as usage.doc). wizard.doc Advanced usage instructions for JPEG wizards only. change.log Version-to-version change highlights. Programmer and internal documentation: libjpeg.doc How to use the JPEG library in your own programs. example.c Sample code for calling the JPEG library. structure.doc Overview of the JPEG library's internal structure. filelist.doc Road map of IJG files. coderules.doc Coding style rules --- please read if you contribute code. Please read at least the files install.doc and usage.doc. Useful information can also be found in the JPEG FAQ (Frequently Asked Questions) article. See ARCHIVE LOCATIONS below to find out where to obtain the FAQ article. If you want to understand how the JPEG code works, we suggest reading one or more of the REFERENCES, then looking at the documentation files (in roughly the order listed) before diving into the code. OVERVIEW ======== This package contains C software to implement JPEG image compression and decompression. JPEG (pronounced "jay-peg") is a standardized compression method for full-color and gray-scale images. JPEG is intended for compressing "real-world" scenes; line drawings, cartoons and other non-realistic images are not its strong suit. JPEG is lossy, meaning that the output image is not exactly identical to the input image. Hence you must not use JPEG if you have to have identical output bits. However, on typical photographic images, very good compression levels can be obtained with no visible change, and remarkably high compression levels are possible if you can tolerate a low-quality image. For more details, see the references, or just experiment with various compression settings. This software implements JPEG baseline, extended-sequential, and progressive compression processes. Provision is made for supporting all variants of these processes, although some uncommon parameter settings aren't implemented yet. For legal reasons, we are not distributing code for the arithmetic-coding variants of JPEG; see LEGAL ISSUES. We have made no provision for supporting the hierarchical or lossless processes defined in the standard. We provide a set of library routines for reading and writing JPEG image files, plus two sample applications "cjpeg" and "djpeg", which use the library to perform conversion between JPEG and some other popular image file formats. The library is intended to be reused in other applications. In order to support file conversion and viewing software, we have included considerable functionality beyond the bare JPEG coding/decoding capability; for example, the color quantization modules are not strictly part of JPEG decoding, but they are essential for output to colormapped file formats or colormapped displays. These extra functions can be compiled out of the library if not required for a particular application. We have also included "jpegtran", a utility for lossless transcoding between different JPEG processes, and "rdjpgcom" and "wrjpgcom", two simple applications for inserting and extracting textual comments in JFIF files. The emphasis in designing this software has been on achieving portability and flexibility, while also making it fast enough to be useful. In particular, the software is not intended to be read as a tutorial on JPEG. (See the REFERENCES section for introductory material.) Rather, it is intended to be reliable, portable, industrial-strength code. We do not claim to have achieved that goal in every aspect of the software, but we strive for it. We welcome the use of this software as a component of commercial products. No royalty is required, but we do ask for an acknowledgement in product documentation, as described under LEGAL ISSUES. LEGAL ISSUES ============ In plain English: 1. We don't promise that this software works. (But if you find any bugs, please let us know!) 2. You can use this software for whatever you want. You don't have to pay us. 3. You may not pretend that you wrote this software. If you use it in a program, you must acknowledge somewhere in your documentation that you've used the IJG code. In legalese: The authors make NO WARRANTY or representation, either express or implied, with respect to this software, its quality, accuracy, merchantability, or fitness for a particular purpose. This software is provided "AS IS", and you, its user, assume the entire risk as to its quality and accuracy. This software is copyright (C) 1991-1996, Thomas G. Lane. All Rights Reserved except as specified below. Permission is hereby granted to use, copy, modify, and distribute this software (or portions thereof) for any purpose, without fee, subject to these conditions: (1) If any part of the source code for this software is distributed, then this README file must be included, with this copyright and no-warranty notice unaltered; and any additions, deletions, or changes to the original files must be clearly indicated in accompanying documentation. (2) If only executable code is distributed, then the accompanying documentation must state that "this software is based in part on the work of the Independent JPEG Group". (3) Permission for use of this software is granted only if the user accepts full responsibility for any undesirable consequences; the authors accept NO LIABILITY for damages of any kind. These conditions apply to any software derived from or based on the IJG code, not just to the unmodified library. If you use our work, you ought to acknowledge us. Permission is NOT granted for the use of any IJG author's name or company name in advertising or publicity relating to this software or products derived from it. This software may be referred to only as "the Independent JPEG Group's software". We specifically permit and encourage the use of this software as the basis of commercial products, provided that all warranty or liability claims are assumed by the product vendor. ansi2knr.c is included in this distribution by permission of L. Peter Deutsch, sole proprietor of its copyright holder, Aladdin Enterprises of Menlo Park, CA. ansi2knr.c is NOT covered by the above copyright and conditions, but instead by the usual distribution terms of the Free Software Foundation; principally, that you must include source code if you redistribute it. (See the file ansi2knr.c for full details.) However, since ansi2knr.c is not needed as part of any program generated from the IJG code, this does not limit you more than the foregoing paragraphs do. The configuration script "configure" was produced with GNU Autoconf. It is copyright by the Free Software Foundation but is freely distributable. It appears that the arithmetic coding option of the JPEG spec is covered by patents owned by IBM, AT&T, and Mitsubishi. Hence arithmetic coding cannot legally be used without obtaining one or more licenses. For this reason, support for arithmetic coding has been removed from the free JPEG software. (Since arithmetic coding provides only a marginal gain over the unpatented Huffman mode, it is unlikely that very many implementations will support it.) So far as we are aware, there are no patent restrictions on the remaining code. WARNING: Unisys has begun to enforce their patent on LZW compression against GIF encoders and decoders. You will need a license from Unisys to use the included rdgif.c or wrgif.c files in a commercial or shareware application. At this time, Unisys is not enforcing their patent against freeware, so distribution of this package remains legal. However, we intend to remove GIF support from the IJG package as soon as a suitable replacement format becomes reasonably popular. We are required to state that "The Graphics Interchange Format(c) is the Copyright property of CompuServe Incorporated. GIF(sm) is a Service Mark property of CompuServe Incorporated." REFERENCES ========== We highly recommend reading one or more of these references before trying to understand the innards of the JPEG software. The best short technical introduction to the JPEG compression algorithm is Wallace, Gregory K. "The JPEG Still Picture Compression Standard", Communications of the ACM, April 1991 (vol. 34 no. 4), pp. 30-44. (Adjacent articles in that issue discuss MPEG motion picture compression, applications of JPEG, and related topics.) If you don't have the CACM issue handy, a PostScript file containing a revised version of Wallace's article is available at ftp.uu.net, graphics/jpeg/wallace.ps.gz. The file (actually a preprint for an article that appeared in IEEE Trans. Consumer Electronics) omits the sample images that appeared in CACM, but it includes corrections and some added material. Note: the Wallace article is copyright ACM and IEEE, and it may not be used for commercial purposes. A somewhat less technical, more leisurely introduction to JPEG can be found in "The Data Compression Book" by Mark Nelson, published by M&T Books (Redwood City, CA), 1991, ISBN 1-55851-216-0. This book provides good explanations and example C code for a multitude of compression methods including JPEG. It is an excellent source if you are comfortable reading C code but don't know much about data compression in general. The book's JPEG sample code is far from industrial-strength, but when you are ready to look at a full implementation, you've got one here... The best full description of JPEG is the textbook "JPEG Still Image Data Compression Standard" by William B. Pennebaker and Joan L. Mitchell, published by Van Nostrand Reinhold, 1993, ISBN 0-442-01272-1. Price US$59.95, 638 pp. The book includes the complete text of the ISO JPEG standards (DIS 10918-1 and draft DIS 10918-2). This is by far the most complete exposition of JPEG in existence, and we highly recommend it. The JPEG standard itself is not available electronically; you must order a paper copy through ISO or ITU. (Unless you feel a need to own a certified official copy, we recommend buying the Pennebaker and Mitchell book instead; it's much cheaper and includes a great deal of useful explanatory material.) In the USA, copies of the standard may be ordered from ANSI Sales at (212) 642-4900, or from Global Engineering Documents at (800) 854-7179. (ANSI doesn't take credit card orders, but Global does.) It's not cheap: as of 1992, ANSI was charging $95 for Part 1 and $47 for Part 2, plus 7% shipping/handling. The standard is divided into two parts, Part 1 being the actual specification, while Part 2 covers compliance testing methods. Part 1 is titled "Digital Compression and Coding of Continuous-tone Still Images, Part 1: Requirements and guidelines" and has document numbers ISO/IEC IS 10918-1, ITU-T T.81. Part 2 is titled "Digital Compression and Coding of Continuous-tone Still Images, Part 2: Compliance testing" and has document numbers ISO/IEC IS 10918-2, ITU-T T.83. Extensions to the original JPEG standard are defined in JPEG Part 3, a new ISO document. Part 3 is undergoing ISO balloting and is expected to be approved by the end of 1995; it will have document numbers ISO/IEC IS 10918-3, ITU-T T.84. IJG currently does not support any Part 3 extensions. The JPEG standard does not specify all details of an interchangeable file format. For the omitted details we follow the "JFIF" conventions, revision 1.02. A copy of the JFIF spec is available from: Literature Department C-Cube Microsystems, Inc. 1778 McCarthy Blvd. Milpitas, CA 95035 phone (408) 944-6300, fax (408) 944-6314 A PostScript version of this document is available at ftp.uu.net, file graphics/jpeg/jfif.ps.gz. It can also be obtained by e-mail from the C-Cube mail server, netlib@c3.pla.ca.us. Send the message "send jfif_ps from jpeg" to the server to obtain the JFIF document; send the message "help" if you have trouble. The TIFF 6.0 file format specification can be obtained by FTP from sgi.com (192.48.153.1), file graphics/tiff/TIFF6.ps.Z; or you can order a printed copy from Aldus Corp. at (206) 628-6593. The JPEG incorporation scheme found in the TIFF 6.0 spec of 3-June-92 has a number of serious problems. IJG does not recommend use of the TIFF 6.0 design (TIFF Compression tag 6). Instead, we recommend the JPEG design proposed by TIFF Technical Note #2 (Compression tag 7). Copies of this Note can be obtained from sgi.com or from ftp.uu.net:/graphics/jpeg/. It is expected that the next revision of the TIFF spec will replace the 6.0 JPEG design with the Note's design. Although IJG's own code does not support TIFF/JPEG, the free libtiff library uses our library to implement TIFF/JPEG per the Note. libtiff is available from sgi.com:/graphics/tiff/. ARCHIVE LOCATIONS ================= The "official" archive site for this software is ftp.uu.net (Internet address 192.48.96.9). The most recent released version can always be found there in directory graphics/jpeg. This particular version will be archived as graphics/jpeg/jpegsrc.v6a.tar.gz. If you are on the Internet, you can retrieve files from ftp.uu.net by standard anonymous FTP. If you don't have FTP access, UUNET's archives are also available via UUCP; contact help@uunet.uu.net for information on retrieving files that way. Numerous Internet sites maintain copies of the UUNET files. However, only ftp.uu.net is guaranteed to have the latest official version. You can also obtain this software in DOS-compatible "zip" archive format from the SimTel archives (ftp.coast.net:/SimTel/msdos/graphics/), or on CompuServe in the Graphics Support forum (GO CIS:GRAPHSUP), library 12 "JPEG Tools". Again, these versions may sometimes lag behind the ftp.uu.net release. The JPEG FAQ (Frequently Asked Questions) article is a useful source of general information about JPEG. It is updated constantly and therefore is not included in this distribution. The FAQ is posted every two weeks to Usenet newsgroups comp.graphics.misc, news.answers, and other groups. You can always obtain the latest version from the news.answers archive at rtfm.mit.edu. By FTP, fetch /pub/usenet/news.answers/jpeg-faq/part1 and .../part2. If you don't have FTP, send e-mail to mail-server@rtfm.mit.edu with body send usenet/news.answers/jpeg-faq/part1 send usenet/news.answers/jpeg-faq/part2 RELATED SOFTWARE ================ Numerous viewing and image manipulation programs now support JPEG. (Quite a few of them use this library to do so.) The JPEG FAQ described above lists some of the more popular free and shareware viewers, and tells where to obtain them on Internet. If you are on a Unix machine, we highly recommend Jef Poskanzer's free PBMPLUS image software, which provides many useful operations on PPM-format image files. In particular, it can convert PPM images to and from a wide range of other formats. You can obtain this package by FTP from ftp.x.org (contrib/pbmplus*.tar.Z) or ftp.ee.lbl.gov (pbmplus*.tar.Z). There is also a newer update of this package called NETPBM, available from wuarchive.wustl.edu under directory /graphics/graphics/packages/NetPBM/. Unfortunately PBMPLUS/NETPBM is not nearly as portable as the IJG software is; you are likely to have difficulty making it work on any non-Unix machine. A different free JPEG implementation, written by the PVRG group at Stanford, is available from havefun.stanford.edu in directory pub/jpeg. This program is designed for research and experimentation rather than production use; it is slower, harder to use, and less portable than the IJG code, but it is easier to read and modify. Also, the PVRG code supports lossless JPEG, which we do not. FILE FORMAT WARS ================ Some JPEG programs produce files that are not compatible with our library. The root of the problem is that the ISO JPEG committee failed to specify a concrete file format. Some vendors "filled in the blanks" on their own, creating proprietary formats that no one else could read. (For example, none of the early commercial JPEG implementations for the Macintosh were able to exchange compressed files.) The file format we have adopted is called JFIF (see REFERENCES). This format has been agreed to by a number of major commercial JPEG vendors, and it has become the de facto standard. JFIF is a minimal or "low end" representation. We recommend the use of TIFF/JPEG (TIFF revision 6.0 as modified by TIFF Technical Note #2) for "high end" applications that need to record a lot of additional data about an image. TIFF/JPEG is fairly new and not yet widely supported, unfortunately. The upcoming JPEG Part 3 standard defines a file format called SPIFF. SPIFF is interoperable with JFIF, in the sense that most JFIF decoders should be able to read the most common variant of SPIFF. SPIFF has some technical advantages over JFIF, but its major claim to fame is simply that it is an official standard rather than an informal one. At this point it is unclear whether SPIFF will supersede JFIF or whether JFIF will remain the de-facto standard. IJG intends to support SPIFF once the standard is frozen, but we have not decided whether it should become our default output format or not. (In any case, our decoder will remain capable of reading JFIF indefinitely.) Various proprietary file formats incorporating JPEG compression also exist. We have little or no sympathy for the existence of these formats. Indeed, one of the original reasons for developing this free software was to help force convergence on common, open format standards for JPEG files. Don't use a proprietary file format! TO DO ===== In future versions, we are considering supporting some of the upcoming JPEG Part 3 extensions --- principally, variable quantization and the SPIFF file format. Tuning the software for better behavior at low quality/high compression settings is also of interest. The current method for scaling the quantization tables is known not to be very good at low Q values. As always, speeding things up is high on our priority list. Please send bug reports, offers of help, etc. to jpeg-info@uunet.uu.net. backends-1.3.0/README.freebsd000066400000000000000000000034111456256263500155730ustar00rootroot00000000000000SANE and FreeBSD ---------------- Building: --------- Don't forget to use GNU make (gmake). E.g. "MAKE=gmake ./configure". SANE should compile and install out-of-the-box. If you've installed some of the optional libraries that sane-backends can make use of (see README for list) using Ports then you will need to let configure know about their locations: MAKE=gmake CPPFLAGS=-I/usr/local/include LDFLAGS=-L/usr/local/lib ./configure SCSI-scanners: -------------- If the SCSI host adapter is supported, there are no known problems. USB-scanners: ------------- USB-Scanners are supported by FreeBSD. The USB scanner driver "uscanner" and access over libusb can be used. For some backends (e.g. gt68xx) you MUST use libusb. The uscanner driver won't work because it doesn't support control messages or interrupt endpoints. If you want to use libusb, your scanner should *not* be claimed by the uscanner driver. Make sure, that /dev/ugen* and /dev/usb* devices are available. Use /dev/MAKEDEV to create them, if necessary. Make sure that the user has write access to the appropriate device files. To get your scanner detected by the uscanner driver, it may be necessary to add its vendor and device ids to the kernel and recompile. Use MAKEDEV to generate the /dev/uscanner* files if they are not already there. Use sane-find-scanner to find your scanner. scanimage -L should find it automatically. Make sure that the user has read access to /dev/usb* and read and write access to /dev/uscanner*. Parport-scanners: ----------------- I don't have much information about these. The Mustek 600 II N scanner seems to work, others may or may not work. Please contact me or the SANE mailing list if you succeeded in using a parport scanner. 2005-11-01 Henning Meier-Geinitz backends-1.3.0/README.hp-ux000066400000000000000000000076461456256263500152400ustar00rootroot00000000000000Building and Installing SANE on HP-UX ------------------------------------- This file contains some notes on building and installing SANE on HP-UX. It tells you which compiler switches to use, how to find out to which controller card your scanner is connected, and how to create a device file for it. Building SANE ------------- On HP-UX, SANE can be built using either the HP ANSI C compiler or GCC. For GCC, no special arguments to configure are necessary. For the HP compiler, invoke configure like this: CC=cc CFLAGS="-Ae -O" ./configure Higher optimization levels than -O (equivalent to +O2) may fail to compile correctly. If you're using the HP compiler on 64-bit HP-UX 11, you must build 64-bit executables: CC=cc CFLAGS="-Ae +DA2.0W -O" ./configure Some of the make files use GNU extensions, so you have to use gmake (GNU make). Gmake binaries are available from the HP-UX Porting and Archive Centre and its mirror sites. Installing SANE --------------- The SCSI pass-through driver (sctl) must be enabled in your kernel, but it is by default. Naturally, the scanner must have a non-conflicting SCSI address and it must be connected to the right SCSI bus to work. You'll probably need to create a device file for your scanner. To do this, you'll need to know its SCSI address, and, if your machine has multiple SCSI controllers, the number of the one it's connected to. As root, you can use ioscan -f to find this information. For example, here's the partial ioscan output for a C200: Class I H/W Path Driver S/W State H/W Type Description ============================================================================== [...] ext_bus 0 8/0/19/0 c720 CLAIMED INTERFACE Ultra Wide SCSI target 0 8/0/19/0.6 tgt CLAIMED DEVICE disk 0 8/0/19/0.6.0 sdisk CLAIMED DEVICE IBM DDYS-T09170N target 1 8/0/19/0.7 tgt CLAIMED DEVICE ctl 0 8/0/19/0.7.0 sctl CLAIMED DEVICE Initiator [...] ext_bus 1 8/16/5 c720 CLAIMED INTERFACE Built-in SCSI target 4 8/16/5.1 tgt CLAIMED DEVICE unknown -1 8/16/5.1.0 UNCLAIMED UNKNOWN EPSON Perfection1640 target 2 8/16/5.2 tgt CLAIMED DEVICE disk 1 8/16/5.2.0 sdisk CLAIMED DEVICE TOSHIBA CD-ROM XM-5401TA target 3 8/16/5.7 tgt CLAIMED DEVICE ctl 1 8/16/5.7.0 sctl CLAIMED DEVICE Initiator [...] You can see that there are two SCSI controllers, Ultra Wide SCSI and Built-in SCSI (narrow single-ended). The I column shows the number of the controller card. Our scanner, an Epson Perfection 1640, is connected to controller 1, and has SCSI address 1 (that's the 1 in the H/W Path number). You can now create the device file using mknod(1M). In this example, the command would be: mknod /dev/rscsi/c1t1d0 c 203 0x011000 In the filename, c1 specifies controller 1, t1 is target 1 (i.e., the SCSI address), d0 (device 0) because it's the only device at that address. 203 is the major number of the sctl driver. In the minor number (0x011000), 01 again means controller 1, and the second one means SCSI address 1. See scsi_ctl(7) for details. For ease of use with SANE, I'd recommend to create a link to /dev/scanner, e.g., ln -s /dev/rscsi/c1t1d0 /dev/scanner To allow normal users to access the scanner, the best approach is probably to create a new group, say, "scanner", and make the scanner device file readable and writable for the group, e.g., chown bin:scanner /dev/rscsi/c1t1d0 chmod g+rw /dev/rscsi/c1t1d0 You would then add all users that are allowed to use the scanner to that group. If you haven't already done so, you should do ln -s /etc/group /etc/logingroup so that users are automatically in all groups to which they belong (and don't have to use newgrp(1)). backends-1.3.0/README.linux000066400000000000000000000154331456256263500153270ustar00rootroot00000000000000Information about USB scanners: ================================ With Linux 2.4.* you could either use the kernel scanner module or libusb to access USB scanners. In Linux 2.6.4 the kernel scanner module was removed. Therefore with this and later kernels libusb must be used. Permissions: ------------ While SANE automatically uses libusb when the library and its header file were present during the build of sane-backends, setting permissions will require some attention. So if scanimage -L lists your scanner as root but not as normal user read on this text. Most distributions support setting permissions without much manual configuration. Usually you must just add the users that are allowed to access the scanner to group "scanner". To make that change active, the user must login again. For more details, see your distribution's documentation e.g. for Debian: README.debian.gz. If this doesn't work you you want to know more, read on. The device files used by libusb are located in /proc/bus/usb/ (e.g. /proc/bus/usb/001/003) or in /dev/bus/usb/ (e.g. /dev/bus/usb/001/003), if you use udev. The exact file name can be found out by running sane-find-scanner which would print "libusb:001:003" in this case. While setting permissions with e.g. "chmod a+rw /proc/bus/usb/001/003" works, this change is not permanent. The permissions will be reset when the scanner is replugged or Linux is rebooted. One solution to set permissions on-the-fly is Linux udev which comes with current distributions. SANE comes with a udev rules file in the tools/udev directory which may be used by distributions or can be copied to /etc/udev/rules.d manually. The file format is explained on top of the file itself. Either you need libusb 0.1.12 or newer or USB_DEVFS_PATH=/dev/bus/usb must be exported as a system-wide environment variable. Older distributions may use the Linux hot-plug tools (or hotplug-ng). Your distribution should have set up the scripts to automatically change permissions correctly. Look for "libsane.usermap" and "libusbscanner" in /etc/hotplug/usb. If you build SANE from source you can use the hotplug script that comes with SANE. See the tools/hotplug/ directory in the source distribution. Please refer to the README in that directory for the details. Gentoo information: ------------------- Gentoo users: If your USB scanner is not detected at all check that USE=usb is set when emerging. Information about SCSI scanners: ================================ Under Linux, your kernel must have generic SCSI support (sg) as well as a driver for your SCSI adapter. You may want to increase the SCSI buffer size to increase scan speed. Details on all of the above can be found in sane-scsi(5). If your SCSI and sg driver are build as moduls you will need to load them with modprobe: # modprobe your-driver-name # modprobe sg You may find error messages in /var/log/messages. Look at the documentation for your SCSI driver. Maybe you need to add options like the io port. Now the SCSI adapter and your scanner should be visible at /proc/scsi/scsi. Example: # cat /proc/scsi/scsi Host: scsi0 Channel: 00 Id: 06 Lun: 00 Vendor: SCANNER Model: Rev: 2.02 Type: Scanner ANSI SCSI revision: 01 CCS In this case the real vendor and scanner name are not shown (Mustek Scannexpress 12000SP) but SANE will detect it nevertheless. If your scanner is supported by SANE, scanimage -L will list it now: # scanimage -L device mustek:/dev/scanner' is a Mustek ScanExpress 12000SP flatbed scanner If this doesn't work you may have to add the right SCSI generic device name to the configuration file. This should be documented in the man page for your backend. To find out about the right SCSI device use sane-find-scanner: # sane-find-scanner found SCSI scanner "SCANNER 2.02" at /dev/scanner found SCSI scanner "SCANNER 2.02" at /dev/sg0 found SCSI scanner "SCANNER 2.02" at /dev/sga It may help to set a symbolic link /dev/scanner to the respective device if automatic detection does not work. If you need more information on the Linux SCSI subsystem, look at http://www.torque.net/scsi/linux_scsi_24/index.html. Although this documentation is about the 2.4 kernels, large parts are also valid for older kernels. One important exception is the section on "Device Names in devfs". Adaptec 1542 SCSI adapter: Using buffer sizes of more than 32768 bytes with the aha1542 driver can lead to kernel panic with older kernels. To avoid this, run configure with the option --enable-scsibuffersize or set the environment variable SANE_SG_BUFFERSIZE to 32768 before running scanimage or another frontend, or download and install the SG driver 2.1.37 or newer from http://www.torque.net/sg. idescsi: The Linux kernel "Emulation of a SCSI host adapter for IDE ATAPI devices" (idescsi) is reported to cause problems in connection with SANE. If your scanner isn't found or you encounter segmentation faults try to disable idescsi. SCSI Direct IO: Recent versions of the Linux SG driver for the 2.4 kernels support direct IO, i.e., the SCSI adapter's DMA chip copies data directly to/from user memory. Direct IO reduces memory usage, but it can lead to access conflicts, if a backend uses shared memory. SANE does not use direct IO by default. If you want to use it, run configure --enable-scsi-directio=yes Very old Linux distributions are missing the /usr/include/scsi directory. In such a case, it is necessary to copy the relevant files from the kernel distribution. Normally, the command: cp -a /usr/src/linux/include/scsi /usr/include should fix this problem. Don't do this if you don't get compilation errors about missing SCSI headers. Other Information ================= ld.so.conf configuration: "/usr/local/lib/sane" or "/usr/lib/sane" MUST NOT be listed in /etc/ld.so.conf. If your scanner is not detected or only Video for Linux devices are found, check for one of the above lines in ld.so.conf. A line "/usr/local/lib" or "/usr/lib" in ld.so.conf is ok, however. Excessive warnings "pointer of type `void *' used in arithmetic": Some older versions of glibc generate these warnings not related to SANE source code. To suppress these warnings do export CFLAGS="-g -O2 -D__NO_STRING_INLINES" and rerun configure. If you use DEC cc on Linux Alpha, you may need to set LDFLAGS="-N" to be able to build sane-backends. The Intel C++ Compiler for IA32 and IA64 isn't supported yet. If you want to try nevertheless, you will experience undefined references to inb and outb functions. To avoid those replace #include with #if defined(__ICC) && __ICC >= 700 # define __GNUC__ 2 #endif #include #if defined(__ICC) && __ICC >= 700 # undef __GNUC__ #elif defined(__ICC) && defined(HAVE_ASM_IO_H) # include #endif Have a lot of fun with the latest SANE backend. backends-1.3.0/README.netbsd000066400000000000000000000041051456256263500154410ustar00rootroot00000000000000SANE and NetBSD ---------------- Building: --------- Don't forget to use GNU make (gmake). E.g. "MAKE=gmake ./configure". SANE should compile and install out-of-the-box. SCSI-scanners: -------------- SANE only supports the generic /dev/uk? devices. /dev/ss? won't work. If your scanner is detected by the ss driver, disable the driver with the "config" utility or compile a new kernel without ss. Set a link /dev/scanner to /dev/uk0 (or whatever you use) and/or edit your backend's config file appropriately. Don't forget to set up permissions to the device file correctly for access by a non-root user (read/write). Auto-configuration using the "scsi *" lines in the config files doesn't work. Scanners with higher resolutions need bigger SCSI buffers. Therefore, edit sanei/sanei_scsi.c and look for these lines: #ifndef MAX_DATA # define MAX_DATA (32*1024) #endif Increase the buffer size to e.g. 128 * 1024. Recompile. USB-scanners: ------------- USB-Scanners are supported in principle. Using libusb is recommended. Problems when using libusb: Older kernels may need to be compiled WITHOUT DIAGNOSTIC. Disable it in the config file or use a GENERIC kernel without DIAGNOSTIC. If it is, you will get errors like "ugenioctl: USB_SET_TIMEOUT, no pipe". When using libusb, make sure, that /dev/ugen* and /dev/usb* devices are available. Use /dev/MAKEDEV to create them, if necessary. If you want to use libusb, disable the uscanner driver. Problems with uscanner driver: The uscanner driver also works (tested with the plustek backend). As the uscanner driver can't detect the vendor and product ids automatically, it's necessary to add the name of the device file to the backends's configuration file. Some backends also need the vendor and product id of the scanner. Some backends won't work with the uscanner driver because they need USB control messages. Use libusb instead. Parport-scanners: ----------------- I don't have any information about these. Please contact me or the SANE mailing list if you succeeded in using one of these. 2003-04-24 Henning Meier-Geinitz backends-1.3.0/README.openbsd000066400000000000000000000047611456256263500156240ustar00rootroot00000000000000SANE and OpenBSD ---------------- Building: --------- Don't forget to use GNU make (gmake). E.g. "MAKE=gmake ./configure". SANE should compile and install out-of-the-box. If some libraries and headers (like libjpeg) are installed in /usr/local/, adding "-I/usr/local/include/ -L/usr/local/lib/" to the CPPFLAGS environment variable before running configure may be necessary to get them detected. Since release 3.9, SANE is available from OpenBSD ports. SCSI-scanners: -------------- SANE only supports the generic /dev/uk? devices. /dev/ss? won't work. If your scanner is detected by the ss driver, disable the driver with the "config" utility or compile a new kernel without ss. Set a link /dev/scanner to /dev/uk0 (or whatever you use) and/or edit your backend's config file appropriately. Don't forget to set up permissions to the device file correctly for access by a non-root user (read/write). USB-scanners: ------------- USB-Scanners are supported in principle since OpenBSD 2.9. The USB scanner driver "uscanner" and access over libusb is supported. To get your scanner detected by the uscanner driver, it may be necessary to add its vendor and device ids to the kernel and recompile (sys/devs/usb/uscanner.c). With OpenBSD 3.0 there is also a bug concerning uscanner that prevents accessing the /dev/uscanner devices. Get a newer kernel in this case. If your MAKEDEV won't make /dev/uscanner0 do it manually: "mknod /dev/uscanner0 c 77 0" for the first scanner. Edit your backend's configuration file appropriately. If you want to use libusb, your scanner should *not* be claimed by the uscanner driver. Also make sure that the ugen driver is NOT compiled with DIAGNOSTIC. If it is, you will get errors like "ugenioctl: USB_SET_TIMEOUT, no pipe". Make sure, that /dev/ugen* and /dev/usb* devices are available. Use /dev/MAKEDEV to create them, if necessary. For some backends you must use libusb. The uscanner driver won't work because automatic detection, control messages, or interrupt endpoints are needed. When using libusb, you need read/write permissions to ugen(4) (/dev/ugen*.*) and the usb(4) controller (/dev/usb*) your scanner is connected to. Consider adding a group usb and setting the device permissions using the hotplugd(8) attach and detach scripts. See tools/openbsd for an example. Parport-scanners: ----------------- I don't have any information about these. Please contact me or the SANE mailing list if you succeeded in using one of these. 2005-12-23 Henning Meier-Geinitz backends-1.3.0/README.os2000066400000000000000000000172631456256263500146760ustar00rootroot000000000000002002-01-03 Look at http://home.tiscalinet.de/fbakan/sane-os2.htm for information about compiling SANE on OS/2 (from Franz Bakan). The following text was for 1.0.3, but it may help nevertheless. SANE 1.03 for OS/2 - build 1 ---------------------------- 09 Jul 2000, Yuri Dario WARNING: -------- This release of has been compiled with EMX 0.9D, so it doesn't work with the previous runtime. You must upgrade to the latest runtime before running SANE (actually EMX 0.9D fix 03). SANE (Scanner Access Now Easy) ------------------------------ For more info about the SANE Project, please visit http://www.sane-project.org/ You should also check the main site for an updated list of supported scanners. Note that I can't say you if your scanner is supported; check SANE homepage instead. SUPPORT ------- I'm sorry to tell you that it is hard for me to help you: if your scanner doesn't work or your problem can't be reproduced on my pc, there are high choices that I can't fix the bug. Most problems are specific to same PC/scanner combinations, and can't be solved without having a full development environment and programming experience. I have setup a mailing list for users, where I hope most people can find an answer to their questions. You will find that I answer only sometimes, because I'm too busy. To subscribe please visit http://www.egroups.com and look for sane-os2, or fill the form available on my site. DOCUMENTATION ------------- To read docs with correct format, you should type more < doc\sane-epson.txt If someone has more ideas on how to get a correct .txt starting from a .man file, he is welcome (actually I'm using groff). INSTALLATION ------------ If you already didn't it, install the correct driver for your scsi adapter. The drivers has usually a .ADD extension and looks like BASEDEV=AHA154X.ADD Then you need the ASPI driver: this driver is already in your OS2\BOOT directory, so you have only to add BASEDEV=OS2ASPI.DMD /ALL The /ALL switch is required only if you need to use other devices with aspirout.sys, e.g. if you have a CD-RW and CDRecord/2 installed. Last, add aspirout.sys to your config.sys DEVICE=D:\OS2\BOOT\ASPIROUT.SYS N.B. you can change the path to other directories. Now reboot. To run scanimage.exe you need also to download the latest EMX runtime, available on Hobbes or Leo as emxrt.zip. find-scanner.exe can be used to recognize a scanner on the scsi bus. Then you should edit your xxx.conf file and add the scanner address, a string like b0t4l0. This can be simplified using autodetection: with autodetection you can use a string like scsi EPSON in your xxx.conf file; the autodection will enable the backend that recognize 'EPSON' in the scanner id. To test scanimage, run scanimage --help you should see your scanner following the list of internal devices: List of available devices: pnm:0 pnm:1 If it is not present, check your scsi driver, the aspi driver OS2ASPI.DMD and aspirout.sys; on Warp4 you can use the Hardware Manager to check scanner presence. Then look at your xxx.conf, maybe there is something wrong in the text. USAGE: ------ Once configured your system, create a proper .conf file (edit one of the supplied templates) and run scanimage -L The output should be like device `umax:b0t3l0' is a UMAX Vista-S8 flatbed scanner device `pnm:0' is a Noname PNM file reader virtual device device `pnm:1' is a Noname PNM file reader virtual device The pnm devices are always available, while on the first line you should read the id of your scanner. A simple scan scanimage -d umax > test.pnm will do a scan with default parameters. Run scanimage -d umax --help to get a full list of available switches. SANED: ------ In this release the SANE network daemon can be used (with a limit). First edit your \mptn\etc\services and add sane 6566/tcp # SANE network scanner daemon (maybe a reboot is required to apply that change). Create a saned.conf in the same directory of saned.exe and add a list of valid client addresses; the clients are allowed to access the local scanner. Now run the server in debug mode saned -d On the client side, you need to create a net.conf file in the same directory of scanimage.exe: here you have to add a list of valid saned servers. Run scanimage -L The output is like this device `umax:b0t3l0' is a UMAX Vista-S8 flatbed scanner device `pnm:0' is a Noname PNM file reader virtual device device `pnm:1' is a Noname PNM file reader virtual device device `net:pippo.intranet:umax:b0t3l0' is a UMAX Vista-S8 flatbed scanner device `net:pippo.intranet:pnm:0' is a Noname PNM file reader virtual device device `net:pippo.intranet:pnm:1' is a Noname PNM file reader virtual device Then saned will quit. To run it forever, you have to configure the INETD daemon: create a \mptn\etc\inetd.lst with the following line sane tcp /rd/sane/sane-1.0.1/frontend/saned.exe Be aware that this configuration shouldn't work: I have been unable to get it working on my PC without running saned under the debugger PMGDB. The correct syntax for running scanimage over the net is scanimage -d net:HOSTNAME:umax:b0t3l0 > test.pnm where HOSTNAME is a valid DNS host name. You can log server access using syslogd: if syslogd is running, saned will send to it all messages. HOW TO COMPILE: --------------- To compile SANE with emx, you need - EMX 0.9D - GNU Make 3.75 (rename to make.exe) - GNU Patch 2.5 - KSH 5.2.13 (rename to sh.exe) - GNU Bison - GNU File utilities - GNU SED (stream editor) - Autoconf 2.13 Steps: - untar the original Unix distribution; - unzip this file somewhere; - copy src\* into sane-1.0.1; - enter sane-1.0.1 directory; - apply patches to original distribution: [...\sane-1.0.1]gnupatch -p 0 < patch.os2 - run autoconf to rebuild the configure script - edit configure and change autoconf default optimization flags; search for -O2 -m486 and change to your preferred settings; - add the following line after :${LDFLAGS="...."} : ${LIBS="-lsocket"} - run configure.os2 to build all installation files; - add -Zexe to LDFLAGS in frontend\Makefile and tools\Makefile; - run make (compiler warnings are ok); - wait to complete all makes; Remember to install a Unix like shell in your path; I use ksh.exe renamed to sh.exe, and saved in d:\bin (that's because most unix scripts uses SHELL=/bin/sh) You need also a compatible dl.a and syslog.a library; look in .\contrib for a couple of simple sources. Flags needed under EMX: -Zsysv-signals enables signal management as in SystemV, otherwise emx convention is used (and it is different from sysv). -Zcrtdll link dynamically with EMXLIBCM.DLL; PARALLEL PORT ------------- This release has support for parallel port enabled: actually only the Epson backend makes uses of such support; the Epson GT-300 can be recognized, but scanning can't be completed. Also HP-5100C parallel scanner can be run under OS/2: you need to download the EPST driver from Shuttle Tech. homepage (www.shuttletech.com); follow the instruction for installation. Then the scanner is seen as a scsi HP, so the HP backend can work with it. The Quickcam backend is also included with lpt support, but I can't say how it is working. History: -------- 09 Jul 2000 release 1.03 20 Jun 1999 release 1.01 - changed runtime to EMX 0.9D - fixed: find-scanner should now work correctly. - added: how to compile instructions. 07 Mar 1999 release 1.01 pre-test3 no OS/2 specific fixes 20 Oct 1998 release 0.74 fixed fork()ing backends with new sanei_thread api. fixed scsi autodetection added parallel port support =============================================================================== Yuri Dario http://www.quasarbbs.com/yuri backends-1.3.0/README.solaris000066400000000000000000000163611456256263500156450ustar00rootroot00000000000000This is a report on how to build SANE on Solaris/x86 using a USB scanner. See below for SCSI. Another report for SANE on OpenSolaris can be found here: http://ginfo.egim-mrs.fr/article.php3?id_article=44 From: Tomasz Orlinski To: sane-devel@lists.alioth.debian.org Subject: [sane-devel] USB scanners DO work on Solaris 10 x86 Date: Thu, 7 Oct 2004 20:26:50 +0200 I would like to inform you, that I have compiled SANE on Sun Solaris 10 x86 with built-in USB scanner support. It was possible, because Sun had prepared libusb library wrapper for Solaris 10. I know, that Solaris 10 Software Express Release 08/04 is required. I have done it in that way: LD_LIBRARY_PATH=/usr/sfw/lib:$LD_LIBRARY_PATH CFLAGS="-I/usr/sfw/include" CPPFLAGS="-I/usr/sfw/include" LDFLAGS="-L/usr/sfw/lib -R/usr/sfw/lib -lusb" export LD_LIBRARY_PATH CFLAGS CPPFLAGS LDFLAGS ./configure --prefix=/opt/sane --disable-fork-process make make install It was also required to add a generic USB kernel driver. I had to look for my scanner device name in output of prtconf -D -v command. My scanner is Plustek UT24 and the appropriate part of output looked like this: name='compatible' type=string items=8 value='usb7b3,17.100' + ... I had to remove the not used kernel driver rem_drv ugen And add it again: add_drv -i 'usb7b3,17.100' ugen Then, /opt/sane/bin/sane-find-scanner detected my scanner and everything worked fine. I to add " " signs around usb7b3,17.100 name in /etc/driver_aliases to have the scanner working after reboot. To compile this packages SUNWlibusb and SUNWlibusbugen were needed. I used Solaris Software Companion CD gcc 2.95.3 compiler and Sun's /usr/ccs/bin/ld linker. USB support DOES NOT work in Solaris 9 and earlier versions. I hope, that this information would be useful for other USB scanner users. From: Tomasz Orlinski To: sane-devel@lists.alioth.debian.org Subject: Re: [sane-devel] USB scanners DO work on Solaris 10 x86 Date: Fri, 8 Oct 2004 16:06:14 +0200 [...] Sun in Driver Development Kit v. 0.9 writes how to compile SANE, but it does not really work :) They write about compiling with gcc and give options for Sun Forte compiler (cc) and forget about attaching a kernel driver. But they write, that versions earlier than 1.0.14 cannot be used. They want also to build SANE with Posix threads enabled. So I think that can be true. Sun writes also, that Solaris Software Express release at least S10_62 is needed to use libusb. Release number can be checked in /etc/release. The newest version can be downloaded from www.sun.com/solaris for free. In my opinion it is important to be cautious when using scanner or just libusb on important Solaris machines (especially multi-processor ones), because unlike in other systems, Solaris kernel is fully preemtible, what means, that many instances of the same driver can run simultaneously - it's dangerous, when drivers are not perfect. And Solaris USB framework is absolutely new, so it can contain bugs. I tried to crash my Solaris using USB subsystem and SANE, I didn't manage to, but it doesn't mean, it's impossible. SANE frontends work without any problems with Xsun and GIMP included in Solaris Software Companion CD. --------------------------------------------------------------------------- The following text describes, how to use a SCSI scanner (2002-06-11). You need a generic SCSI driver to run SANE on Solaris. There are at least three such drivers: the scg driver by Joerg Schillig, the sg driver by Kevin Sheehan, and starting with Solaris 8 Sun's own sgen(7D) driver. NOTE: You should install the SCSI generic driver BEFORE you run `configure' in the sane directory---otherwise configure won't set up SANE to work with the generic scsi driver. *** scg driver The SCSI general driver scg is Copyright 1986-1995 Joerg Schilling, It is supplied binary in pkgadd(1m) format and is tested from Solaris 2.3 to Solaris 2.6 (sparc) and Solaris 2.3 to Solaris 2.5.1 (x86). It can be found in ftp://ftp.fokus.gmd.de/pub/unix/kernel/scg/ To install it on a SPARC do: cd /tmp get SCHILYscg.sparc.tar.Z uncompress SCHILYscg.sparc.tar.Z tar -xvf SCHILYscg.sparc.tar as root: pkgadd -d . NOTE: Be very careful with pkgadd as it does not check for the correct target architecture. Do not install drivers for x86 on sparc and vice versa. You will get a corrupt system otherwise. For Solaris on an x86 do: cd /tmp get SCHILYscg.i386.tar.Z uncompress SCHILYscg.i386.tar.Z tar -xvf SCHILYscg.i386.tar as root: pkgadd -d . Then do a reboot --r Once the system has rebooted, there should be a device node /dev/scgXX for each of your SCSI adapters (/dev/scg0 for the first adapter, /dev/scg1 for the second, and so on...) Because the device name specifies a SCSI adapter, you need to use a special device naming syntax so SANE can tell which device you want to talk to. If the device is at SCSI id 0, the character 'a' should be appended, character 'b' should be used for SCSI id 1, and so on (see also the Solaris section in sane-scsi(5)). E.g., to configure an HP scanner, configuration file /opt/local/etc/sane.d/hp.conf might contain: /dev/scg0c if the scanner has the SCSI target id 2 WARNING! Everybody who can read/write a generic SCSI device can do with all your disks whatever he/she wants. It takes only a few lines of code to send a FORMAT control block... Rather than giving users access to the SCSI adapter special device, it may be a better idea to install scanimage/xscanimage setgid to a special "scanner" group and then turn on write permission for the scanner group. *** sg driver Another solution to the permission problem is to use the generic SCSI driver sg by Kevin Sheehan. This driver is not free but uses separate device node for each SCSI target: /dev/sg/0, /dev/sg/1, ... This allows to control device access on a per-device basis. *** sgen driver The solaris 8 sgen driver must be configured before it can be used. See /kernel/drv/sgen.conf and the manual page sgen(7D). The minimal config includes defining the correct "device-type-config-list" property in /kernel/drv/sgen.conf. The typical SCSI device type for a scanner is either "scanner" or "processor". You can optionally restrict the devices sgen attaches to, by defining the "inquiry-config-list" property. You also have to uncomment the scanner's "target/lun" entry, so that the sgen driver is allowed to attach to the SCSI scanner hardware. After the configuration file /kernel/drv/sgen.conf is set up for the scanner, run the command "devfsadm -v -i sgen" to create sgen device nodes for the scanner. In case you need to rerun devfsadm to incorporate changes to the sgen.conf file, make sure the sgen driver is unloaded from the kernel before you re-run devfsadm. The driver is unloaded using the "modunload -i {id}" command; the {id} of the sgen driver can be determined with the modinfo command. The device nodes use names of the following form: /dev/scsi/device-type/cXtXdX Example: A HP Scanjet 4c (SCSI device type: "processor") on controller #1, target #6, lun #0 uses the device name /dev/scsi/processor/c1t6d0 /kernel/drv/sgen.conf contains: device-type-config-list="processor"; name="sgen" class="scsi" target=6 lun=0; If you have questions or problems with the Solaris support in SANE, send mail to: hu@garfield.m.isar.de backends-1.3.0/README.unixware2000066400000000000000000000075321456256263500161150ustar00rootroot00000000000000UNIXWARE PORT of SANE - HOWTO build and what you need to run You need a sane SCSI driver to run SANE on Unixware 2.x. There is at least one such driver: the sane driver by R=I+S. The Unixware sane driver is a binary loadable module driver for UW2.x. Please send Email to wolfgang@rapp-informatik.de to receive. What you need to build xane on Unixware 2.x - gnu make You don't need gcc. All is build with the standard Unixware cc and libs!! For the X-frontends xscanimage and xsane (separately distributed) you need libs like libgtk libgimp and the image libs for jpeg, tiff and png. It is also good to have gettext with libintl installed. Most of the libs are available on the Skunkware CD's from SCO in pkgadd format. If you build the frontends with this libs gimp plugin is also supported by xscanimage and xsane. Latest xsane version tested was 0.48. With the following configure should run an build shared libs with libtool 1.3.4 LD_RUN_PATH=/usr/local/lib CPPFLAGS="-I/usr/local/include -I/isr/X/include" CFLAGS="-I/usr/local/include -I/usr/X/include -I/usr/ucbinclude -L/usr/local/lib -L/usr/ucblib -l ucb" LIBS="-lsocket -lnsl -lgen -I/opt/include -L/usr/ucblib -lucb" ./configure After running configure you have to edit the generated file libtool Append to the line archive_cmds="\$LD -G -o \$lib \$libobjs \$deplibs \$linkopts so that it looks like this. archive_cmds="\$LD -G -o \$lib \$libobjs \$deplibs \$linkopts -B bind_now -L/usr/local/lib -lintl -L/usr/ucblib -lucb -lm" This is a must to link the libucb.a , libintl.a in when the sane libs are build.It's also necessary for mathematic lib -lm . Not doing this will bring runtime errors like missimg usleep or pow function. ATTENTION:!!! libucb.a of Unixware 2.x has a buggy readdir function so it is recommended to remove readdir.o from libucb.a with ar -d readdir.o libucb.a Often used and needed functions from libucb.a are strncasecmp or getpagesize. If you use -I/usr/ucbinclude it is also a good idea to move /usr/ucbinlcude/unistd.h to another file, because it will be included first and some sources will not compile. Then you can run make with GNU make. After this say su and then it's good to say LD_RUN_PATH=/usr/local/lib export LD_RUN_PATH after this you can install with make install Tested on Unixware 2.0.x with Umax Astra 1220S and HP C5100A with sane-1.0.1. With Microtek backend earlier on sane-0.74. If you use the net backend you do not need the Unixware sanedrv. The driver is a shareware product. The driver is supplied binary in pkgadd(1m) format and is tested from UW2.0x. Please send Email to wolfgang@rapp-informatik.de to get the driver in binary pkgadd package. To install it on a Unixware do: cd /tmp get sanedrv.pkg.tgz gunzip sanedrv.pkg.tgz tar xvf sanedrv.pkg.tar as root: pkgadd -d /tmp/sanedrv Once the driver is installed, there should be a device nodes /dev/sane and /dev/scanner /dev/scanner1 for the second, and so on...) The scanner my be connected to any SCSI controller in the system, also if you have additional devices like disks or cdroms on the same controller. The UW2.x sane driver do not block the controller. The scanner must be switched on to be recognized by UW, if the low level driver p.e. adsl is loaded. To configure an UMAX scanner, configuration file /usr/local/etc/sane.d/umax.conf might contain: /dev/scanner The driver is configured to recognice all scsi scanners with ID SCANNER and all HP Scsi scanners with ID Processor and Inquiry string starting with "HP" string. For details have a look to /etc/conf/pack.d/sane/space.c. If you change this then you must unload the sane driver modadmin -U sane, build the new module by /etc/conf/bin/idbuild -M sane and the install the driver with modadmin -l sane. If you have questions or problems with the Unixware support in SANE, send mail to: wolfgang@rapp-informatik.de backends-1.3.0/README.unixware7000066400000000000000000000054021456256263500161140ustar00rootroot00000000000000SANE on UnixWare 7 ================== The easiest way to get SANE working on a UnixWare 7.1.X system (UW 7.0.0 and 7.0.1 are not supported) is to download and install the SANE binary distribution from SCOs Skunkware web site. The URL is: ftp://ftp.sco.com/skunkware/uw7/Packages Use the pkgadd command to install the files, e.g.: # pkgadd -d `pwd`/sane-1.0.2.pkg A web page with further information specific to SANE on UnixWare 7 can be found here: http://www.sco.com/skunkware/sane/index.html However, please note that the SANE package that is available from the Skunkware server will most likely contain the latest stable version of SANE. Since SANE is continuously being worked on, you might want to download the latest source from the Git repository and build the source yourself. To build SANE on a UnixWare 7.1.X system the following packages are required: * make (GNU make) * glibs (contains gtk, glib, libjpeg, libpnm, libtiff etc.; only necessary for frontends xscanimage, xcam, and xsane which are distributed separately) The packages can be downloaded from the Skunkware ftp server (see URL above). I've used the following versions to build sane-1.0.2: * make-3.78.1.pkg * glibs-2.0.3.pkg If you want to include support for The Gimp (this is optional), you need to install the gimp package, too. This is the version I've used: * gimp-1.0.4.pkg GCC is not required. SANE compiles quite happily (and faster) with UnixWares native C compiler. Set the LD_RUN_PATH variable to add /usr/local/lib to the list of directories to be searched by the dynamic linker: $ LD_RUN_PATH=/usr/local/lib $ export LD_RUN_PATH Run the configure script as following: $ LIBS="-lsdi" ./configure (The sdi library contains the SCSI passthrough functions the UW7 port of SANE uses to access the scanner from user space.) Run gmake to build SANE: $ gmake SCSI access from user space requires special privileges. If you want users without root perms to be able to use the scanner you can use the filepriv command to change the privileges that are associated with the SANE applications: # /sbin/filepriv -f driver -f sysops /usr/local/bin/scanimage # /sbin/filepriv -f driver -f sysops /usr/local/bin/xscanimage # /sbin/filepriv -f sysops /usr/local/bin/xcam If you have built SANE with support for The Gimp you might want to create a symlink from the plug-in directory to the xscanimage and xcam binaries, e.g.: # ln -s /usr/local/bin/xscanimage \ /usr/local/lib/gimp/1.0/plug-ins/xscanimage # ln -s /usr/local/bin/xcam /usr/local/lib/gimp/1.0/plug-ins/xcam Questions? Comments? Please send e-mail to jenss@sco.com or skunkware@sco.com! backends-1.3.0/README.windows000066400000000000000000000034641456256263500156630ustar00rootroot00000000000000SANE on Windows Prerequisites ============= To be able to compile sane-backends, you need to have either Cygwin or Mingw compilers and a suitable POSIX compatible environment. You can get the Cygwin POSIX compatible environment for Windows Windows and the Cygwin gcc compiler at http://www.cygwin.com You can get the MSYS POSIX compatible environment for Windows and the MinGW gcc compiler at http://www.mingw.org/wiki/MSYS The scanner must be detected by Windows and not be disabled. Check with the hardware manager. Building ======== See general README for build basics. If a given backend fails to compile, you can use the BACKENDS variable to limit compilation to backends your interested in: ./configure BACKENDS=epson2 Configuring =========== If you have more than one scanner, you should do the following: - run sane-find-scanner to get the device name of the scanner. The name for scsi devices is something like h0b0t6l0, meaning hba 0, bus 0, scsi id 6 and lun 0. - edit the config file for the backend (/path/to/sane/etc/sane.d/xxxx.conf) and add the scanner device name on an empty line. - Set environment variable SANE_CONFIG_DIR to point to the directory where the config files are located. Run "scanimage > out.pnm" to get a scan. xscanimage and XSane have been reported to compile and run in the past under Cygwin. Notes ===== - Only SCSI, USB (with libusb-win32), and network scanners may work. No FireWire/Parallel. The Cygwin libusb port can be installed with Cygwin setup.exe or can be compiled manually under cygwin or mingw using the libusb-win32 port: http://sourceforge.net/apps/trac/libusb-win32/wiki - Tested on Windows XP, 7, and using Wine on Linux. - Some scanners' backend may not work because of requirement not supported by Cygwin or MinGW. 2011/10/08 backends-1.3.0/README.zeta000066400000000000000000000014411456256263500151250ustar00rootroot00000000000000SANE and Zeta ------------- The Zeta port is based on existing BeOS port, with some changes to accommodate yellowTAB's internal build procedure, and some new conventions brought up by Zeta. Cf. README.beos for more historical stuff. SANE 1.0.9 was updated for Zeta by Ithamar Adema for yellowTAB. SANE 1.0.15 was updated for BeOS and Zeta by François Revol for yellowTAB. Zeta uses a modified version of Philippe Houdoin's Sanity GUI frontend. http://philippe.houdoin.free.fr/phil/beos/sanity/index-en.html SANE was reported to work with the following configuration and features: Platform: Zeta R1 (== BeOS R6.x) ix86 Latest SANE version tested: 1.0.15 Compiler: gcc 2.95.3 User-level SCSI support: yes USB support: yes Shared library support: yes Dynamic loading support: yes X11 clients: no backends-1.3.0/acinclude.m4000066400000000000000000000512511456256263500155000ustar00rootroot00000000000000dnl dnl Contains the following macros dnl SANE_SET_AM_CFLAGS(is_release) dnl SANE_CHECK_MISSING_HEADERS dnl SANE_SET_AM_LDFLAGS dnl SANE_CHECK_DLL_LIB dnl SANE_EXTRACT_LDFLAGS(LIBS, LDFLAGS) dnl SANE_CHECK_JPEG dnl SANE_CHECK_IEEE1284 dnl SANE_CHECK_PTHREAD dnl SANE_CHECK_LOCKING dnl JAPHAR_GREP_AM_CFLAGS(flag, cmd_if_missing, cmd_if_present) dnl SANE_CHECK_U_TYPES dnl SANE_CHECK_GPHOTO2 dnl SANE_CHECK_IPV6 dnl SANE_CHECK_BACKENDS dnl SANE_PROTOTYPES dnl AC_PROG_LIBTOOL dnl # SANE_SET_AM_CFLAGS(is_release) # Set default AM_CFLAGS if gcc is used. Enable/disable additional # compilation warnings. The extra warnings are enabled by default # during the development cycle but disabled for official releases. # The argument is_release is either yes or no. AC_DEFUN([SANE_SET_AM_CFLAGS], [ if test "${ac_cv_c_compiler_gnu}" = "yes"; then DEFAULT_WARNINGS="\ -Wall" EXTRA_WARNINGS="\ -Wextra \ -pedantic" for flag in $DEFAULT_WARNINGS; do JAPHAR_GREP_AM_CFLAGS($flag, [ AM_CFLAGS="$AM_CFLAGS $flag" ]) JAPHAR_GREP_AM_CXXFLAGS($flag, [ AM_CXXFLAGS="$AM_CXXFLAGS $flag" ]) done AC_ARG_ENABLE(warnings, AS_HELP_STRING([--enable-warnings], [turn on tons of compiler warnings (GCC only)]), [ if eval "test x$enable_warnings = xyes"; then for flag in $EXTRA_WARNINGS; do JAPHAR_GREP_AM_CFLAGS($flag, [ AM_CFLAGS="$AM_CFLAGS $flag" ]) JAPHAR_GREP_AM_CXXFLAGS($flag, [ AM_CXXFLAGS="$AM_CXXFLAGS $flag" ]) done fi ], [if test x$1 = xno; then # Warnings enabled by default (development) for flag in $EXTRA_WARNINGS; do JAPHAR_GREP_AM_CFLAGS($flag, [ AM_CFLAGS="$AM_CFLAGS $flag" ]) JAPHAR_GREP_AM_CXXFLAGS($flag, [ AM_CXXFLAGS="$AM_CXXFLAGS $flag" ]) done fi]) fi # ac_cv_c_compiler_gnu ]) dnl SANE_CHECK_MISSING_HEADERS dnl Do some sanity checks. It doesn't make sense to proceed if those headers dnl aren't present. AC_DEFUN([SANE_CHECK_MISSING_HEADERS], [ MISSING_HEADERS= if test "${ac_cv_header_fcntl_h}" != "yes" ; then MISSING_HEADERS="${MISSING_HEADERS}\"fcntl.h\" " fi if test "${ac_cv_header_sys_time_h}" != "yes" ; then MISSING_HEADERS="${MISSING_HEADERS}\"sys/time.h\" " fi if test "${ac_cv_header_unistd_h}" != "yes" ; then MISSING_HEADERS="${MISSING_HEADERS}\"unistd.h\" " fi if test "${MISSING_HEADERS}" != "" ; then echo "*** The following essential header files couldn't be found:" echo "*** ${MISSING_HEADERS}" echo "*** Maybe the compiler isn't ANSI C compliant or not properly installed?" echo "*** For details on what went wrong see config.log." AC_MSG_ERROR([Exiting now.]) fi ]) # SANE_SET_AM_LDFLAGS # Add special AM_LDFLAGS AC_DEFUN([SANE_SET_AM_LDFLAGS], [ # Define stricter linking policy on GNU systems. This is not # added to global LDFLAGS because we may want to create convenience # libraries that don't require such strick linking. if test "$GCC" = yes; then case ${host_os} in linux* | solaris*) STRICT_LDFLAGS="-Wl,-z,defs" ;; esac fi AC_SUBST(STRICT_LDFLAGS) case "${host_os}" in aix*) #enable .so libraries, disable archives AM_LDFLAGS="$AM_LDFLAGS -Wl,-brtl" ;; darwin*) #include frameworks LIBS="$LIBS -framework CoreFoundation -framework IOKit" ;; esac ]) # SANE_CHECK_DLL_LIB # Find dll library AC_DEFUN([SANE_CHECK_DLL_LIB], [ dnl Checks for dll libraries: dl DL_LIBS="" if test "${enable_dynamic}" = "auto"; then # default to disabled unless library found. enable_dynamic=no # dlopen AC_CHECK_HEADERS(dlfcn.h, [AC_CHECK_LIB(dl, dlopen, DL_LIBS=-ldl) saved_LIBS="${LIBS}" LIBS="${LIBS} ${DL_LIBS}" AC_CHECK_FUNCS(dlopen, enable_dynamic=yes,) LIBS="${saved_LIBS}" ],) # HP/UX DLL handling AC_CHECK_HEADERS(dl.h, [AC_CHECK_LIB(dld,shl_load, DL_LIBS=-ldld) saved_LIBS="${LIBS}" LIBS="${LIBS} ${DL_LIBS}" AC_CHECK_FUNCS(shl_load, enable_dynamic=yes,) LIBS="${saved_LIBS}" ],) if test -z "$DL_LIBS" ; then # old Mac OS X/Darwin (without dlopen) AC_CHECK_HEADERS(mach-o/dyld.h, [AC_CHECK_FUNCS(NSLinkModule, enable_dynamic=yes,) ],) fi fi AC_SUBST(DL_LIBS) DYNAMIC_FLAG= if test "${enable_dynamic}" = yes ; then DYNAMIC_FLAG=-module fi AC_SUBST(DYNAMIC_FLAG) ]) # # Separate LIBS from LDFLAGS to link correctly on HP/UX (and other # platforms who care about the order of params to ld. It removes all # non '-l..'-params from passed in $1(LIBS), and appends them to $2(LDFLAGS). # # Use like this: SANE_EXTRACT_LDFLAGS(PKGNAME_LIBS, PKGNAME_LDFLAGS) AC_DEFUN([SANE_EXTRACT_LDFLAGS], [ tmp_LIBS="" for param in ${$1}; do case "${param}" in -l*) tmp_LIBS="${tmp_LIBS} ${param}" ;; *) $2="${$2} ${param}" ;; esac done $1="${tmp_LIBS}" unset tmp_LIBS unset param ]) # # # Checks for ieee1284 library, needed for canon_pp backend. AC_DEFUN([SANE_CHECK_IEEE1284], [ AC_CHECK_HEADER(ieee1284.h, [ AC_CACHE_CHECK([for libieee1284 >= 0.1.5], sane_cv_use_libieee1284, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ struct parport p; char *buf; ieee1284_nibble_read(&p, 0, buf, 1); ]])], [sane_cv_use_libieee1284="yes"; IEEE1284_LIBS="-lieee1284" ],[sane_cv_use_libieee1284="no"]) ],) ],) if test "$sane_cv_use_libieee1284" = "yes" ; then AC_DEFINE(HAVE_LIBIEEE1284,1,[Define to 1 if you have the `ieee1284' library (-lcam).]) fi AC_SUBST(IEEE1284_LIBS) ]) # # Checks for pthread support AC_DEFUN([SANE_CHECK_PTHREAD], [ case "${host_os}" in linux* | darwin* | mingw*) # enabled by default on Linux, MacOS X and MINGW use_pthread=yes ;; *) use_pthread=no esac have_pthread=no # # now that we have the systems preferences, we check # the user AC_ARG_ENABLE([pthread], AS_HELP_STRING([--enable-pthread], [use pthread instead of fork (default=yes for Linux/MacOS X/MINGW, no for everything else)]), [ if test $enableval = yes ; then use_pthread=yes else use_pthread=no fi ]) if test $use_pthread = yes ; then AC_CHECK_HEADERS(pthread.h, [ AC_CHECK_LIB(pthread, pthread_create, PTHREAD_LIBS="-lpthread") have_pthread=yes save_LIBS="$LIBS" LIBS="$LIBS $PTHREAD_LIBS" AC_CHECK_FUNCS([pthread_create pthread_kill pthread_join pthread_detach pthread_cancel pthread_testcancel], ,[ have_pthread=no; use_pthread=no ]) LIBS="$save_LIBS" ],[ have_pthread=no; use_pthread=no ]) fi # Based on a similar test for pthread_key_t from the Python project. # See https://bugs.python.org/review/25658/patch/19209/75870 AC_MSG_CHECKING(whether pthread_t is integer) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[#include ]], [[pthread_t k; k * 1;]])], [ac_pthread_t_is_integer=yes], [ac_pthread_t_is_integer=no] ) AC_MSG_RESULT($ac_pthread_t_is_integer) if test "$ac_pthread_t_is_integer" = yes ; then AC_DEFINE(PTHREAD_T_IS_INTEGER, 1, [Define if pthread_t is integer.]) else case "$host_os" in darwin*) # Always use pthreads on macOS use_pthread=yes ;; *) # Until the sanei_thread implementation is fixed. use_pthread=no ;; esac fi if test "$have_pthread" = "yes" ; then AM_CPPFLAGS="${AM_CPPFLAGS} -D_REENTRANT" fi AC_SUBST(PTHREAD_LIBS) if test $use_pthread = yes ; then AC_DEFINE_UNQUOTED(USE_PTHREAD, "$use_pthread", [Define if pthreads should be used instead of forked processes.]) SANEI_THREAD_LIBS=$PTHREAD_LIBS else SANEI_THREAD_LIBS="" fi AC_SUBST(SANEI_THREAD_LIBS) AC_MSG_CHECKING([whether to enable pthread support]) AC_MSG_RESULT([$have_pthread]) AC_MSG_CHECKING([whether to use pthread instead of fork]) AC_MSG_RESULT([$use_pthread]) ]) # # 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, [ AC_CHECK_HEADER(jconfig.h, [ AC_MSG_CHECKING([for jpeglib - version >= 61 (6a)]) AC_EGREP_CPP(sane_correct_jpeg_lib_version_found, [ #include #if JPEG_LIB_VERSION >= 61 sane_correct_jpeg_lib_version_found #endif ], [sane_cv_use_libjpeg="yes"; JPEG_LIBS="-ljpeg"; AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)]) ],) ],) if test "$sane_cv_use_libjpeg" = "yes" ; then AC_DEFINE(HAVE_LIBJPEG,1,[Define to 1 if you have the libjpeg library.]) fi AC_SUBST(JPEG_LIBS) ]) # 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"; TIFF_LIBS="-ltiff"],) ],) AC_SUBST(TIFF_LIBS) ]) AC_DEFUN([SANE_CHECK_PNG], [ AC_CHECK_LIB(png,png_init_io, [ AC_CHECK_HEADER(png.h, [sane_cv_use_libpng="yes"; PNG_LIBS="-lpng"],) ],) if test "$sane_cv_use_libpng" = "yes" ; then AC_DEFINE(HAVE_LIBPNG,1,[Define to 1 if you have the libpng library.]) fi AC_SUBST(PNG_LIBS) ]) # # Checks for device locking support AC_DEFUN([SANE_CHECK_LOCKING], [ use_locking=yes case "${host_os}" in os2* ) use_locking=no ;; esac # # we check the user AC_ARG_ENABLE( [locking], AS_HELP_STRING([--enable-locking], [activate device locking (default=yes, but only used by some backends)]), [ if test $enableval = yes ; then use_locking=yes else use_locking=no fi ]) if test $use_locking = yes ; then INSTALL_LOCKPATH=install-lockpath AC_DEFINE([ENABLE_LOCKING], 1, [Define to 1 if device locking should be enabled.]) else INSTALL_LOCKPATH= fi AC_MSG_CHECKING([whether to enable device locking]) AC_MSG_RESULT([$use_locking]) AC_SUBST(INSTALL_LOCKPATH) ]) dnl dnl JAPHAR_GREP_AM_CFLAGS(flag, cmd_if_missing, cmd_if_present) dnl dnl From Japhar. Report changes to japhar@hungry.com dnl AC_DEFUN([JAPHAR_GREP_AM_CFLAGS], [case "$AM_CFLAGS" in "$1" | "$1 "* | *" $1" | *" $1 "* ) ifelse($#, 3, [$3], [:]) ;; *) $2 ;; esac ]) dnl dnl JAPHAR_GREP_AM_CXXFLAGS(flag, cmd_if_missing, cmd_if_present) dnl AC_DEFUN([JAPHAR_GREP_AM_CXXFLAGS], [case "$AM_CXXFLAGS" in "$1" | "$1 "* | *" $1" | *" $1 "* ) ifelse($#, 3, [$3], [:]) ;; *) $2 ;; esac ]) dnl dnl SANE_CHECK_U_TYPES dnl AC_DEFUN([SANE_CHECK_U_TYPES], [ dnl Use new style of check types that doesn't take default to use. dnl The old style would add an #undef of the type check on platforms dnl that defined that type... That is not portable to platform that dnl define it as a #define. AC_CHECK_TYPES([u_char, u_short, u_int, u_long],,,) ]) # # Checks for gphoto2 libs, needed by gphoto2 backend AC_DEFUN([SANE_CHECK_GPHOTO2], [ AC_ARG_WITH(gphoto2, AS_HELP_STRING([--with-gphoto2], [include the gphoto2 backend @<:@default=yes@:>@]), [# If --with-gphoto2=no or --without-gphoto2, disable backend # as "$with_gphoto2" will be set to "no"]) # If --with-gphoto2=yes (or not supplied), first check if # pkg-config exists, then use it to check if libgphoto2 is # present. If all that works, then see if we can actually link # a program. And, if that works, then add the -l flags to # GPHOTO2_LIBS and any other flags to GPHOTO2_LDFLAGS to pass to # sane-config. if test "$with_gphoto2" != "no" ; then AC_CHECK_TOOL(HAVE_GPHOTO2, pkg-config, false) if test ${HAVE_GPHOTO2} != "false" ; then if pkg-config --exists libgphoto2 ; then with_gphoto2="`pkg-config --modversion libgphoto2`" GPHOTO2_CPPFLAGS="`pkg-config --cflags libgphoto2`" GPHOTO2_LIBS="`pkg-config --libs libgphoto2`" saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${GPHOTO2_CPPFLAGS}" saved_LIBS="${LIBS}" LIBS="${LIBS} ${GPHOTO2_LIBS}" # Make sure we an really use the library AC_CHECK_FUNCS(gp_camera_init, HAVE_GPHOTO2=true, HAVE_GPHOTO2=false) if test "${HAVE_GPHOTO2}" = "true"; then AC_CHECK_FUNCS(gp_port_info_get_path) fi CPPFLAGS="${saved_CPPFLAGS}" LIBS="${saved_LIBS}" else HAVE_GPHOTO2=false fi if test "${HAVE_GPHOTO2}" = "false"; then GPHOTO2_CPPFLAGS="" GPHOTO2_LIBS="" else SANE_EXTRACT_LDFLAGS(GPHOTO2_LIBS, GPHOTO2_LDFLAGS) if pkg-config --atleast-version=2.5.0 libgphoto2; then AC_DEFINE([GPLOGFUNC_NO_VARGS], [1], [Define if GPLogFunc does not take a va_list.]) fi fi fi fi AC_SUBST(GPHOTO2_CPPFLAGS) AC_SUBST(GPHOTO2_LIBS) AC_SUBST(GPHOTO2_LDFLAGS) ]) # # Check for AF_INET6, determines whether or not to enable IPv6 support # Check for ss_family member in struct sockaddr_storage AC_DEFUN([SANE_CHECK_IPV6], [ AC_MSG_CHECKING([whether to enable IPv6]) AC_ARG_ENABLE(ipv6, AS_HELP_STRING([--disable-ipv6],[disable IPv6 support]), [ if test "$enableval" = "no" ; then AC_MSG_RESULT([no, manually disabled]) ipv6=no fi ]) if test "$ipv6" != "no" ; then AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #define INET6 #include #include #include ]], [[ /* AF_INET6 available check */ if (socket(AF_INET6, SOCK_STREAM, 0) < 0) exit(1); else exit(0); ]])],[ AC_MSG_RESULT(yes) AC_DEFINE([ENABLE_IPV6], 1, [Define to 1 if the system supports IPv6]) ipv6=yes ],[ AC_MSG_RESULT([no (couldn't compile test program)]) ipv6=no ]) fi if test "$ipv6" != "no" ; then AC_MSG_CHECKING([whether struct sockaddr_storage has an ss_family member]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #define INET6 #include #include #include ]], [[ /* test if the ss_family member exists in struct sockaddr_storage */ struct sockaddr_storage ss; ss.ss_family = AF_INET; exit (0); ]])], [ AC_MSG_RESULT(yes) AC_DEFINE([HAS_SS_FAMILY], 1, [Define to 1 if struct sockaddr_storage has an ss_family member]) ], [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #define INET6 #include #include #include ]], [[ /* test if the __ss_family member exists in struct sockaddr_storage */ struct sockaddr_storage ss; ss.__ss_family = AF_INET; exit (0); ]])], [ AC_MSG_RESULT([no, but __ss_family exists]) AC_DEFINE([HAS___SS_FAMILY], 1, [Define to 1 if struct sockaddr_storage has __ss_family instead of ss_family]) ], [ AC_MSG_RESULT([no]) ipv6=no ]) ]) fi ]) # # Verifies that values in $BACKENDS and updates FILTERED_BACKEND # with either backends that can be compiled or fails the script. AC_DEFUN([SANE_CHECK_BACKENDS], [ if test "${user_selected_backends}" = "yes"; then DISABLE_MSG="aborting" else DISABLE_MSG="disabling" fi FILTERED_BACKENDS="" for be in ${BACKENDS}; do backend_supported="yes" case $be in plustek_pp) case "$host_os" in gnu*) echo "*** $be backend not supported on GNU/Hurd - $DISABLE_MSG" backend_supported="no" ;; esac ;; dc210|dc240|pixma) if test "${sane_cv_use_libjpeg}" != "yes"; then echo "*** $be backend requires JPEG library - $DISABLE_MSG" backend_supported="no" fi ;; canon_pp|hpsj5s) if test "${sane_cv_use_libieee1284}" != "yes"; then echo "*** $be backend requires libieee1284 library - $DISABLE_MSG" backend_supported="no" fi ;; genesys) if test "${HAVE_CXX11}" != "1"; then echo "*** $be backend requires C++11 support - $DISABLE_MSG" backend_supported="no" fi ;; mustek_pp) if test "${sane_cv_use_libieee1284}" != "yes" && test "${enable_parport_directio}" != "yes"; then echo "*** $be backend requires libieee1284 or parport-directio libraries - $DISABLE_MSG" backend_supported="no" fi ;; dell1600n_net) if test "${sane_cv_use_libjpeg}" != "yes" || test "${sane_cv_use_libtiff}" != "yes"; then echo "*** $be backend requires JPEG and TIFF library - $DISABLE_MSG" backend_supported="no" fi ;; epsonds) if test "${sane_cv_use_libjpeg}" != "yes"; then echo "*** $be backend requires JPEG library - $DISABLE_MSG" backend_supported="no" fi ;; escl) if test "x${with_avahi}" != "xyes"; then echo "*** $be backend requires AVAHI library - $DISABLE_MSG" backend_supported="no" fi if test "x${with_libcurl}" != "xyes"; then echo "*** $be backend requires cURL library - $DISABLE_MSG" backend_supported="no" fi if test "x${have_libxml}" != "xyes"; then echo "*** $be backend requires XML library - $DISABLE_MSG" backend_supported="no" fi # FIXME: Remove when PNG and/or PDF support have been added. if test "x${sane_cv_use_libjpeg}" != "xyes"; then echo "*** $be backend currently requires JPEG library - $DISABLE_MSG" backend_supported="no" else if test "x${ac_cv_func_jpeg_crop_scanline}" != "xyes" \ || test "x${ac_cv_func_jpeg_skip_scanlines}" != "xyes"; then echo "*** $be backend requires a newer JPEG library - $DISABLE_MSG" backend_supported="no" fi fi ;; gphoto2) if test "${HAVE_GPHOTO2}" != "true" \ || test "${sane_cv_use_libjpeg}" != "yes"; then echo "*** $be backend requires gphoto2 and JPEG libraries - $DISABLE_MSG" backend_supported="no" fi ;; pint) if test "${ac_cv_header_sys_scanio_h}" = "no"; then echo "*** $be backend requires sys/scanio.h - $DISABLE_MSG" backend_supported="no" fi ;; qcam) if ( test "${ac_cv_func_ioperm}" = "no" || test "${sane_cv_have_sys_io_h_with_inb_outb}" = "no" )\ && test "${ac_cv_func__portaccess}" = "no"; then echo "*** $be backend requires (ioperm, inb and outb) or portaccess functions - $DISABLE_MSG" backend_supported="no" fi ;; v4l) if test "${have_linux_ioctl_defines}" != "yes" \ || test "${have_libv4l1}" != "yes"; then echo "*** $be backend requires v4l libraries - $DISABLE_MSG" backend_supported="no" fi ;; net) if test "${ac_cv_header_sys_socket_h}" = "no"; then echo "*** $be backend requires sys/socket.h - $DISABLE_MSG" backend_supported="no" fi ;; mustek_usb2|kvs40xx) if test "${have_pthread}" != "yes"; then echo "*** $be backend requires pthread library - $DISABLE_MSG" backend_supported="no" fi ;; esac if test "${backend_supported}" = "no"; then if test "${user_selected_backends}" = "yes"; then exit 1 fi else FILTERED_BACKENDS="${FILTERED_BACKENDS} $be" fi done ]) # # Generate prototypes for functions not available on the system AC_DEFUN([SANE_PROTOTYPES], [ AH_BOTTOM([ #if defined(__MINGW32__) #define _BSDTYPES_DEFINED #endif #ifndef HAVE_U_CHAR #define u_char unsigned char #endif #ifndef HAVE_U_SHORT #define u_short unsigned short #endif #ifndef HAVE_U_INT #define u_int unsigned int #endif #ifndef HAVE_U_LONG #define u_long unsigned long #endif /* Prototype for getenv */ #ifndef HAVE_GETENV #define getenv sanei_getenv char * getenv(const char *name); #endif /* Prototype for inet_ntop */ #ifndef HAVE_INET_NTOP #define inet_ntop sanei_inet_ntop #include const char * inet_ntop (int af, const void *src, char *dst, size_t cnt); #endif /* Prototype for inet_pton */ #ifndef HAVE_INET_PTON #define inet_pton sanei_inet_pton int inet_pton (int af, const char *src, void *dst); #endif /* Prototype for sigprocmask */ #ifndef HAVE_SIGPROCMASK #define sigprocmask sanei_sigprocmask int sigprocmask (int how, int *new, int *old); #endif /* Prototype for snprintf */ #ifndef HAVE_SNPRINTF #define snprintf sanei_snprintf #include int snprintf (char *str,size_t count,const char *fmt,...); #endif /* Prototype for strcasestr */ #ifndef HAVE_STRCASESTR #define strcasestr sanei_strcasestr char * strcasestr (const char *phaystack, const char *pneedle); #endif /* Prototype for strdup */ #ifndef HAVE_STRDUP #define strdup sanei_strdup char *strdup (const char * s); #endif /* Prototype for strndup */ #ifndef HAVE_STRNDUP #define strndup sanei_strndup #include char *strndup(const char * s, size_t n); #endif /* Prototype for strsep */ #ifndef HAVE_STRSEP #define strsep sanei_strsep char *strsep(char **stringp, const char *delim); #endif /* Prototype for usleep */ #ifndef HAVE_USLEEP #define usleep sanei_usleep unsigned int usleep (unsigned int useconds); #endif /* Prototype for vsyslog */ #ifndef HAVE_VSYSLOG #include void vsyslog(int priority, const char *format, va_list args); #endif ]) ]) m4_include([m4/byteorder.m4]) backends-1.3.0/autogen.sh000077500000000000000000000050541456256263500153100ustar00rootroot00000000000000#!/bin/bash test -n "$srcdir" || srcdir=`dirname "$0"` test -n "$srcdir" || srcdir=. # When repos are forked on GitLab tags aren't copied thus making # git-version-gen producing incorrect version ("UNKNOWN") which in turn causes # CI build failures. To workaround this reconstruct version from ChangeLogs # files (handy updated on every release). If git describe is not working and we # are not in dist package - take version from the top-most ChangeLog file. if [ ! -e .tarball-version ] && ! git describe >/dev/null 2>&1; then ls ChangeLogs \ | sort -Vr \ | grep -m1 -P -o '(?<=ChangeLog-).*' > .tarball-version read v < .tarball-version echo >&2 "Package version reconstructed from ChangeLog: $v" fi patchdir="$srcdir/patches" # Suppress warnings about obsolete macros if still needed (#122) ac_dir=$(aclocal --print-ac-dir) if test -r "$ac_dir/ax_create_stdint_h.m4"; then serial=$(awk '/#serial/{ print $2 }' "$ac_dir/ax_create_stdint_h.m4") if test "$serial" -lt 21; then m4_dir=$(cd $srcdir; autoconf -t 'AC_CONFIG_MACRO_DIR:$%') target="$srcdir/$m4_dir/ax_create_stdint_h.m4" echo "Copying file to $target" cp "$ac_dir/ax_create_stdint_h.m4" "$srcdir/$m4_dir" if test "$serial" -lt 20; then echo "patching file $target to #serial 20" patch --quiet $target \ "$patchdir/ax_create_stdint_h.19-20.m4.patch" fi echo "patching file $target to #serial 21" patch --quiet "$target" \ "$patchdir/ax_create_stdint_h.20-21.m4.patch" fi fi autoreconf --force --install --verbose --warnings=all "$srcdir" patch "$srcdir/ltmain.sh" "$patchdir/ltmain.sh.patch" patch "$srcdir/po/Rules-quot" "$patchdir/Rules-quot.patch" autoreconf "$srcdir" # Taken from https://gitlab.com/utsushi/utsushi/blob/master/bootstrap # # Sanity check the result to catch the most common errors that are # not diagnosed by autoreconf itself (or could use some extra help # explaining what to do in those cases). if grep AX_CXX_COMPILE_STDCXX "$srcdir/configure" >/dev/null 2>&1; then cat <> $@; \ done; \ echo "static struct backend preloaded_backends[] = {" >> $@; \ sep=""; \ list="$(PRELOADABLE_BACKENDS)"; \ if test -z "$${list}"; then \ echo { 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }} >> $@; \ else \ for be in $$list; do \ echo "$${sep}PRELOAD_DEFN($$be)" >> $@; \ sep=","; \ done; \ fi; \ echo "};" >> $@ # TODO: This really belongs together with the saned sources and # should be installed there as well. EXTRA_DIST += saned.conf.in # Backends are not required to have a config file. Any backend # that wants to install a config file should list it here. BACKEND_CONFS= abaton.conf agfafocus.conf apple.conf artec.conf \ artec_eplus48u.conf avision.conf bh.conf \ canon630u.conf canon.conf canon_dr.conf \ canon_lide70.conf \ canon_pp.conf cardscan.conf coolscan2.conf coolscan3.conf \ coolscan.conf dc210.conf dc240.conf dc25.conf \ dell1600n_net.conf dmc.conf epjitsu.conf epson2.conf \ epson.conf epsonds.conf escl.conf fujitsu.conf genesys.conf \ gphoto2.conf gt68xx.conf hp3900.conf hp4200.conf hp5400.conf \ hp.conf hpsj5s.conf hs2p.conf ibm.conf kodak.conf kodakaio.conf\ kvs1025.conf leo.conf lexmark.conf lexmark_x2600.conf \ ma1509.conf magicolor.conf \ matsushita.conf microtek2.conf microtek.conf mustek.conf \ mustek_pp.conf mustek_usb.conf nec.conf net.conf \ p5.conf \ pie.conf pieusb.conf pixma.conf plustek.conf plustek_pp.conf \ qcam.conf ricoh.conf rts8891.conf s9036.conf sceptre.conf \ sharp.conf sm3840.conf snapscan.conf sp15c.conf \ st400.conf stv680.conf tamarack.conf \ teco1.conf teco2.conf teco3.conf test.conf \ u12.conf umax1220u.conf umax.conf umax_pp.conf v4l.conf \ xerox_mfp.conf dll.conf saned.conf # Although ./configure sets up BACKEND_CONFS_ENABLED it does not take # into account the fact that some backends don't have a configuration # file. The becfg should depend on BACKEND_CONFS. The install-becfg # target uses BACKEND_CONFS_ENABLED and silently skips missing files. becfg: $(BACKEND_CONFS) SUFFIXES = .conf.in .conf .conf.in.conf: $(AM_V_GEN) @if $(AM_V_P); then echo Generating $@ from $^; fi @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' $? > $@ install-data-hook: install-becfg install-firmware-path $(INSTALL_LOCKPATH) # Custom install target to install config files. Do not overwrite # files that have been previously installed so that user modifications # are not lost. install-becfg: becfg @# Libtool has a bug where it will sometimes symlink the last @# installed library in $(execsanelibdir) to libsane.*, which @# causes a conflict with the actual libsane.* in $(libdir). -rm -f $(DESTDIR)$(execsanelibdir)/libsane.* test -z "$(configdir)" || $(MKDIR_P) "$(DESTDIR)$(configdir)" test -z "$(configdir)/dll.d" || $(MKDIR_P) "$(DESTDIR)$(configdir)/dll.d" @list="$(BACKEND_CONFS_ENABLED) saned.conf dll.conf"; for cfg in $$list; do \ if test ! -r $${cfg}; then continue; fi; \ if test -f $(DESTDIR)$(configdir)/$${cfg}; then \ echo NOT overwriting $${cfg} in $(configdir)...; \ else \ echo installing $${cfg} in $(configdir)/$${cfg}...; \ $(INSTALL_DATA) $${cfg} $(DESTDIR)$(configdir)/$${cfg} \ || exit 1; \ fi; \ done install-firmware-path: for dir in $(FIRMWARE_DIRS) ; do \ $(mkinstalldirs) $(DESTDIR)$(datadir)/sane/$${dir} ; \ done install-lockpath: $(mkinstalldirs) -m 775 $(DESTDIR)$(locksanedir) uninstall-hook: rm -rf $(DESTDIR)$(libdir)/sane $(DESTDIR)$(configdir) $(DESTDIR)$(locksanedir) rm -f $(DESTDIR)$(libdir)/libsane.* -for dir in $(FIRMWARE_DIRS) ; do \ rmdir $(DESTDIR)$(datadir)/sane/$${dir} ; \ done CLEANFILES += $(BACKEND_CONFS) $(be_convenience_libs) clean-local: find . -type l -name \*-s.c | xargs rm -f # Backends # # All possible backends should be listed here. As a first step, we create # a convenience library containing all files needed to link a backend # directly into libsane.la. Convenience library should have the # form of lib${backend}.la to match what configure will list to # build. # Occasionally, this approach will have name conflicts with external # libraries that need to be linked in. See libgphoto2_i.la for # example of working around that issue. be_convenience_libs = libabaton.la libagfafocus.la \ libapple.la libartec.la libartec_eplus48u.la \ libas6e.la libavision.la libbh.la \ libcanon.la libcanon630u.la libcanon_dr.la \ libcanon_lide70.la \ libcanon_pp.la libcardscan.la libcoolscan.la \ libcoolscan2.la libcoolscan3.la libdc25.la \ libdc210.la libdc240.la libdell1600n_net.la \ libdmc.la libdll.la libdll_preload.la libepjitsu.la libepson.la \ libepson2.la libepsonds.la libescl.la libfujitsu.la libgenesys.la \ libgphoto2_i.la libgt68xx.la libhp.la \ libhp3500.la libhp3900.la libhp4200.la \ libhp5400.la libhp5590.la libhpljm1005.la \ libhpsj5s.la libhs2p.la libibm.la libkodak.la libkodakaio.la\ libkvs1025.la libkvs20xx.la libkvs40xx.la \ libleo.la liblexmark.la liblexmark_x2600.la libma1509.la libmagicolor.la \ libmatsushita.la libmicrotek.la libmicrotek2.la \ libmustek.la libmustek_pp.la libmustek_usb.la \ libmustek_usb2.la libnec.la libnet.la \ libniash.la libp5.la \ libpie.la libpieusb.la libpint.la libpixma.la \ libplustek.la libplustek_pp.la libpnm.la \ libqcam.la libricoh.la libricoh2.la librts8891.la \ libs9036.la libsceptre.la libsharp.la \ libsm3600.la libsm3840.la libsnapscan.la \ libsp15c.la libst400.la libstv680.la \ libtamarack.la libtest.la libteco1.la \ libteco2.la libteco3.la libu12.la libumax.la \ libumax1220u.la libumax_pp.la libv4l.la \ libxerox_mfp.la # Each stand-alone backend that's possible to be built should be listed # here. There are the libraries that are installed under $(libdir)/sane. # Format is libsane-${backend}.la. be_dlopen_libs = libsane-abaton.la libsane-agfafocus.la \ libsane-apple.la libsane-artec.la libsane-artec_eplus48u.la \ libsane-as6e.la libsane-avision.la libsane-bh.la \ libsane-canon.la libsane-canon630u.la libsane-canon_dr.la \ libsane-canon_lide70.la \ libsane-canon_pp.la libsane-cardscan.la libsane-coolscan.la \ libsane-coolscan2.la libsane-coolscan3.la libsane-dc25.la \ libsane-dc210.la libsane-dc240.la libsane-dell1600n_net.la \ libsane-dmc.la libsane-epjitsu.la libsane-epson.la \ libsane-epson2.la libsane-epsonds.la libsane-escl.la libsane-fujitsu.la \ libsane-genesys.la libsane-gphoto2.la libsane-gt68xx.la libsane-hp.la \ libsane-hp3500.la libsane-hp3900.la libsane-hp4200.la \ libsane-hp5400.la libsane-hp5590.la libsane-hpljm1005.la \ libsane-hpsj5s.la libsane-hs2p.la libsane-ibm.la libsane-kodak.la libsane-kodakaio.la\ libsane-kvs1025.la libsane-kvs20xx.la libsane-kvs40xx.la \ libsane-leo.la \ libsane-lexmark.la libsane-lexmark_x2600.la libsane-ma1509.la libsane-magicolor.la \ libsane-matsushita.la libsane-microtek.la libsane-microtek2.la \ libsane-mustek.la libsane-mustek_pp.la libsane-mustek_usb.la \ libsane-mustek_usb2.la libsane-nec.la libsane-net.la \ libsane-niash.la libsane-p5.la \ libsane-pie.la libsane-pieusb.la libsane-pint.la libsane-pixma.la \ libsane-plustek.la libsane-plustek_pp.la libsane-pnm.la \ libsane-qcam.la libsane-ricoh.la libsane-ricoh2.la libsane-rts8891.la \ libsane-s9036.la libsane-sceptre.la libsane-sharp.la \ libsane-sm3600.la libsane-sm3840.la libsane-snapscan.la \ libsane-sp15c.la libsane-st400.la libsane-stv680.la \ libsane-tamarack.la libsane-test.la libsane-teco1.la \ libsane-teco2.la libsane-teco3.la libsane-u12.la libsane-umax.la \ libsane-umax1220u.la libsane-umax_pp.la libsane-v4l.la \ libsane-xerox_mfp.la EXTRA_LTLIBRARIES = $(be_convenience_libs) $(be_dlopen_libs) lib_LTLIBRARIES = libsane.la # The libraries in $(libdir)/sane are platform-dependent files, so they # should be installed during "make install-exec". For that reason, the # variable names here must contain "exec". execsanelibdir = $(libdir)/sane execsanelib_LTLIBRARIES = $(BACKEND_LIBS_ENABLED) libsane-dll.la COMMON_LIBS = ../lib/liblib.la $(XML_LIBS) # Each backend should define a convenience library that compiles # all related files within backend directory. General guideline # is to have a ${backend}.c and ${backend}.h. Some backends also # add a few support source files to convenience library. # Note: automake doesn't really use header files listed here. # They are indications that they need to be distributed only. libabaton_la_SOURCES = abaton.c abaton.h libabaton_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=abaton # Each backend should define a stand-alone library that gets installed. # This will need to link in a special file ${backend}-s.c that allows # the backend to be stand-alone and contain all SANE API functions. # Also, it will need to link in related convenience library as well as # any external libraries required to resolve symbols. # # All backends should include $(DIST_SANELIBS_LDFLAGS) so that # library is correctly versioned. # # If a backend has a config file, it must be listed here to get distributed. nodist_libsane_abaton_la_SOURCES = abaton-s.c libsane_abaton_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=abaton libsane_abaton_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_abaton_la_LIBADD = $(COMMON_LIBS) \ libabaton.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ $(SCSI_LIBS) $(RESMGR_LIBS) EXTRA_DIST += abaton.conf.in libagfafocus_la_SOURCES = agfafocus.c agfafocus.h libagfafocus_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=agfafocus nodist_libsane_agfafocus_la_SOURCES = agfafocus-s.c libsane_agfafocus_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=agfafocus libsane_agfafocus_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_agfafocus_la_LIBADD = $(COMMON_LIBS) \ libagfafocus.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_thread.lo \ ../sanei/sanei_scsi.lo \ $(SCSI_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS) EXTRA_DIST += agfafocus.conf.in libapple_la_SOURCES = apple.c apple.h libapple_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=apple nodist_libsane_apple_la_SOURCES = apple-s.c libsane_apple_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=apple libsane_apple_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_apple_la_LIBADD = $(COMMON_LIBS) \ libapple.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ $(SCSI_LIBS) $(RESMGR_LIBS) EXTRA_DIST += apple.conf.in libartec_la_SOURCES = artec.c artec.h libartec_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=artec nodist_libsane_artec_la_SOURCES = artec-s.c libsane_artec_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_artec_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=artec libsane_artec_la_LIBADD = $(COMMON_LIBS) \ libartec.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ $(SCSI_LIBS) $(RESMGR_LIBS) EXTRA_DIST += artec.conf.in libartec_eplus48u_la_SOURCES = artec_eplus48u.c artec_eplus48u.h libartec_eplus48u_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=artec_eplus48u nodist_libsane_artec_eplus48u_la_SOURCES = artec_eplus48u-s.c libsane_artec_eplus48u_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=artec_eplus48u libsane_artec_eplus48u_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_artec_eplus48u_la_LIBADD = $(COMMON_LIBS) \ libartec_eplus48u.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_thread.lo \ $(MATH_LIB) $(USB_LIBS) $(SANEI_THREAD_LIBS) $(RESMEG_LIBS) EXTRA_DIST += artec_eplus48u.conf.in libas6e_la_SOURCES = as6e.c as6e.h libas6e_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=as6e nodist_libsane_as6e_la_SOURCES = as6e-s.c libsane_as6e_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=as6e libsane_as6e_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_as6e_la_LIBADD = $(COMMON_LIBS) libas6e.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo libavision_la_SOURCES = avision.c avision.h libavision_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=avision nodist_libsane_avision_la_SOURCES = avision-s.c libsane_avision_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=avision libsane_avision_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_avision_la_LIBADD = $(COMMON_LIBS) \ libavision.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_thread.lo \ ../sanei/sanei_scsi.lo \ $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS) EXTRA_DIST += avision.conf.in libbh_la_SOURCES = bh.c bh.h libbh_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=bh nodist_libsane_bh_la_SOURCES = bh-s.c libsane_bh_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=bh libsane_bh_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_bh_la_LIBADD = $(COMMON_LIBS) libbh.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ $(SCSI_LIBS) $(RESMGR_LIBS) EXTRA_DIST += bh.conf.in libcanon_la_SOURCES = canon.c canon.h libcanon_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon nodist_libsane_canon_la_SOURCES = canon-s.c libsane_canon_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon libsane_canon_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_canon_la_LIBADD = $(COMMON_LIBS) libcanon.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ $(MATH_LIB) $(SCSI_LIBS) $(RESMGR_LIBS) EXTRA_DIST += canon.conf.in # TODO: Why are these distributed but not compiled? EXTRA_DIST += canon-sane.c canon-scsi.c libcanon630u_la_SOURCES = canon630u.c libcanon630u_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon630u nodist_libsane_canon630u_la_SOURCES = canon630u-s.c libsane_canon630u_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon630u libsane_canon630u_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_canon630u_la_LIBADD = $(COMMON_LIBS) \ libcanon630u.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += canon630u.conf.in # TODO: Why are this distributed but not compiled? EXTRA_DIST += canon630u-common.c lm9830.h libcanon_dr_la_SOURCES = canon_dr.c canon_dr.h canon_dr-cmd.h libcanon_dr_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon_dr nodist_libsane_canon_dr_la_SOURCES = canon_dr-s.c libsane_canon_dr_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon_dr libsane_canon_dr_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_canon_dr_la_LIBADD = $(COMMON_LIBS) \ libcanon_dr.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_scsi.lo \ ../sanei/sanei_magic.lo \ $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += canon_dr.conf.in libcanon_lide70_la_SOURCES = canon_lide70.c libcanon_lide70_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon_lide70 nodist_libsane_canon_lide70_la_SOURCES = canon_lide70-s.c libsane_canon_lide70_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon_lide70 libsane_canon_lide70_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_canon_lide70_la_LIBADD = $(COMMON_LIBS) \ libcanon_lide70.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += canon_lide70.conf.in # TODO: Why are this distributed but not compiled? EXTRA_DIST += canon_lide70-common.c libcanon_pp_la_SOURCES = canon_pp.c canon_pp.h canon_pp-io.c canon_pp-io.h canon_pp-dev.c canon_pp-dev.h libcanon_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon_pp nodist_libsane_canon_pp_la_SOURCES = canon_pp-s.c libsane_canon_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=canon_pp libsane_canon_pp_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_canon_pp_la_LIBADD = $(COMMON_LIBS) \ libcanon_pp.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ $(IEEE1284_LIBS) EXTRA_DIST += canon_pp.conf.in libcardscan_la_SOURCES = cardscan.c cardscan.h libcardscan_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=cardscan nodist_libsane_cardscan_la_SOURCES = cardscan-s.c libsane_cardscan_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=cardscan libsane_cardscan_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_cardscan_la_LIBADD = $(COMMON_LIBS) \ libcardscan.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += cardscan.conf.in libcoolscan_la_SOURCES = coolscan.c coolscan.h coolscan-scsidef.h libcoolscan_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=coolscan nodist_libsane_coolscan_la_SOURCES = coolscan-s.c libsane_coolscan_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=coolscan libsane_coolscan_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_coolscan_la_LIBADD = $(COMMON_LIBS) \ libcoolscan.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_thread.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_scsi.lo \ $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS) EXTRA_DIST += coolscan.conf.in libcoolscan2_la_SOURCES = coolscan2.c libcoolscan2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=coolscan2 nodist_libsane_coolscan2_la_SOURCES = coolscan2-s.c libsane_coolscan2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=coolscan2 libsane_coolscan2_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_coolscan2_la_LIBADD = $(COMMON_LIBS) \ libcoolscan2.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_scsi.lo \ $(SCSI_LIBS) $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += coolscan2.conf.in libcoolscan3_la_SOURCES = coolscan3.c libcoolscan3_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=coolscan3 nodist_libsane_coolscan3_la_SOURCES = coolscan3-s.c libsane_coolscan3_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=coolscan3 libsane_coolscan3_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_coolscan3_la_LIBADD = $(COMMON_LIBS) \ libcoolscan3.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_scsi.lo \ $(SCSI_LIBS) $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += coolscan3.conf.in libdc25_la_SOURCES = dc25.c dc25.h libdc25_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dc25 nodist_libsane_dc25_la_SOURCES = dc25-s.c libsane_dc25_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dc25 libsane_dc25_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_dc25_la_LIBADD = $(COMMON_LIBS) \ libdc25.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ $(MATH_LIB) EXTRA_DIST += dc25.conf.in libdc210_la_SOURCES = dc210.c dc210.h libdc210_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dc210 nodist_libsane_dc210_la_SOURCES = dc210-s.c libsane_dc210_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dc210 libsane_dc210_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_dc210_la_LIBADD = $(COMMON_LIBS) \ libdc210.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ $(SANEI_SANEI_JPEG_LO) $(JPEG_LIBS) EXTRA_DIST += dc210.conf.in libdc240_la_SOURCES = dc240.c dc240.h libdc240_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dc240 nodist_libsane_dc240_la_SOURCES = dc240-s.c libsane_dc240_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dc240 libsane_dc240_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_dc240_la_LIBADD = $(COMMON_LIBS) \ libdc240.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ $(SANEI_SANEI_JPEG_LO) $(JPEG_LIBS) EXTRA_DIST += dc240.conf.in libdell1600n_net_la_SOURCES = dell1600n_net.c libdell1600n_net_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dell1600n_net nodist_libsane_dell1600n_net_la_SOURCES = dell1600n_net-s.c libsane_dell1600n_net_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dell1600n_net libsane_dell1600n_net_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_dell1600n_net_la_LIBADD = $(COMMON_LIBS) \ libdell1600n_net.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ $(TIFF_LIBS) $(JPEG_LIBS) $(SOCKET_LIBS) EXTRA_DIST += dell1600n_net.conf.in libdmc_la_SOURCES = dmc.c dmc.h libdmc_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dmc nodist_libsane_dmc_la_SOURCES = dmc-s.c libsane_dmc_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dmc libsane_dmc_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_dmc_la_LIBADD = $(COMMON_LIBS) \ libdmc.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ $(SCSI_LIBS) $(RESMGR_LIBS) EXTRA_DIST += dmc.conf.in if have_libavahi if have_libcurl if have_libxml2 libescl_la_SOURCES = escl/escl.c \ escl/escl_capabilities.c \ escl/escl_devices.c \ escl/escl.h \ escl/escl_newjob.c \ escl/escl_reset.c \ escl/escl_scan.c \ escl/escl_status.c \ escl/escl_jpeg.c \ escl/escl_png.c \ escl/escl_tiff.c \ escl/escl_pdf.c \ escl/escl_crop.c libescl_la_CPPFLAGS = $(AM_CPPFLAGS) $(JPEG_CFLAGS) $(PNG_CFLAGS) $(TIFF_CFLAGS) $(POPPLER_GLIB_CFLAGS) $(XML_CFLAGS) $(libcurl_CFLAGS) $(AVAHI_CFLAGS) -DBACKEND_NAME=escl nodist_libsane_escl_la_SOURCES = escl-s.c libsane_escl_la_CPPFLAGS = $(AM_CPPFLAGS) $(JPEG_CFLAGS) $(PNG_CFLAGS) $(TIFF_CFLAGS) $(POPPLER_GLIB_CFLAGS) $(XML_CFLAGS) $(libcurl_CFLAGS) $(AVAHI_CFLAGS) -DBACKEND_NAME=escl libsane_escl_la_CFLAGS = $(AM_CFLAGS) $(JPEG_CFLAGS) $(PNG_CFLAGS) $(TIFF_CFLAGS) $(POPPLER_GLIB_CFLAGS) $(XML_CFLAGS) $(libcurl_CFLAGS) $(AVAHI_CFLAGS) -DBACKEND_NAME=escl libsane_escl_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_escl_la_LIBADD = $(COMMON_LIBS) \ libescl.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ $(MATH_LIB) $(JPEG_LIBS) $(PNG_LIBS) $(TIFF_LIBS) $(POPPLER_GLIB_LIBS) \ $(XML_LIBS) $(libcurl_LIBS) $(AVAHI_LIBS) endif endif endif EXTRA_DIST += escl.conf.in libepjitsu_la_SOURCES = epjitsu.c epjitsu.h epjitsu-cmd.h libepjitsu_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=epjitsu nodist_libsane_epjitsu_la_SOURCES = epjitsu-s.c libsane_epjitsu_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=epjitsu libsane_epjitsu_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_epjitsu_la_LIBADD = $(COMMON_LIBS) \ libepjitsu.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += epjitsu.conf.in libepson_la_SOURCES = epson.c epson.h epson_scsi.c epson_scsi.h epson_usb.c epson_usb.h libepson_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=epson nodist_libsane_epson_la_SOURCES = epson-s.c libsane_epson_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=epson libsane_epson_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_epson_la_LIBADD = $(COMMON_LIBS) \ libepson.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_scsi.lo \ ../sanei/sanei_pio.lo \ ../sanei/sanei_directio.lo \ $(SCSI_LIBS) $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += epson.conf.in libepson2_la_SOURCES = epson2.c epson2.h epson2_scsi.c epson2_scsi.h epson2_usb.c epson2_net.c epson2_net.h epson2-io.c epson2-io.h epson2-commands.c epson2-commands.h epson2-ops.c epson2-ops.h epson2-cct.c libepson2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=epson2 nodist_libsane_epson2_la_SOURCES = epson2-s.c libsane_epson2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=epson2 libsane_epson2_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_epson2_la_LIBADD = $(COMMON_LIBS) \ libepson2.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_scsi.lo \ ../sanei/sanei_pio.lo \ ../sanei/sanei_tcp.lo \ ../sanei/sanei_udp.lo \ ../sanei/sanei_directio.lo \ $(SCSI_LIBS) $(USB_LIBS) $(SOCKET_LIBS) $(MATH_LIB) $(RESMGR_LIBS) EXTRA_DIST += epson2.conf.in libepsonds_la_SOURCES = epsonds.c epsonds.h epsonds-usb.c epsonds-usb.h epsonds-io.c epsonds-io.h \ epsonds-cmd.c epsonds-cmd.h epsonds-ops.c epsonds-ops.h epsonds-jpeg.c epsonds-jpeg.h \ epsonds-net.c epsonds-net.h libepsonds_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=epsonds nodist_libsane_epsonds_la_SOURCES = epsonds-s.c libsane_epsonds_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=epsonds libsane_epsonds_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) if have_libavahi libsane_epsonds_la_LIBADD = $(COMMON_LIBS) libepsonds.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo \ ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo \ ../sanei/sanei_tcp.lo ../sanei/sanei_udp.lo \ $(SANEI_SANEI_JPEG_LO) $(JPEG_LIBS) $(USB_LIBS) $(MATH_LIB) $(RESMGR_LIBS) $(SOCKET_LIBS) $(AVAHI_LIBS) else libsane_epsonds_la_LIBADD = $(COMMON_LIBS) libepsonds.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo \ ../sanei/sanei_usb.lo ../sanei/sanei_scsi.lo \ ../sanei/sanei_tcp.lo ../sanei/sanei_udp.lo \ $(SANEI_SANEI_JPEG_LO) $(JPEG_LIBS) $(USB_LIBS) $(MATH_LIB) $(RESMGR_LIBS) $(SOCKET_LIBS) endif EXTRA_DIST += epsonds.conf.in libfujitsu_la_SOURCES = fujitsu.c fujitsu.h fujitsu-scsi.h libfujitsu_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=fujitsu nodist_libsane_fujitsu_la_SOURCES = fujitsu-s.c libsane_fujitsu_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=fujitsu libsane_fujitsu_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_fujitsu_la_LIBADD = $(COMMON_LIBS) \ libfujitsu.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_scsi.lo \ ../sanei/sanei_magic.lo \ $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += fujitsu.conf.in libgenesys_la_SOURCES = genesys/genesys.cpp genesys/genesys.h \ genesys/calibration.h \ genesys/command_set.h \ genesys/command_set_common.h genesys/command_set_common.cpp \ genesys/device.h genesys/device.cpp \ genesys/enums.h genesys/enums.cpp \ genesys/error.h genesys/error.cpp \ genesys/fwd.h \ genesys/gl646.cpp genesys/gl646.h genesys/gl646_registers.h \ genesys/gl124.cpp genesys/gl124.h genesys/gl124_registers.h \ genesys/gl841.cpp genesys/gl841.h genesys/gl841_registers.h \ genesys/gl842.cpp genesys/gl842.h genesys/gl842_registers.h \ genesys/gl843.cpp genesys/gl843.h genesys/gl843_registers.h \ genesys/gl846.cpp genesys/gl846.h genesys/gl846_registers.h \ genesys/gl847.cpp genesys/gl847.h genesys/gl847_registers.h \ genesys/row_buffer.h \ genesys/image_buffer.h genesys/image_buffer.cpp \ genesys/image_pipeline.h genesys/image_pipeline.cpp \ genesys/image_pixel.h genesys/image_pixel.cpp \ genesys/image.h genesys/image.cpp \ genesys/motor.h genesys/motor.cpp \ genesys/register.h \ genesys/register_cache.h \ genesys/scanner_interface.h genesys/scanner_interface.cpp \ genesys/scanner_interface_usb.h genesys/scanner_interface_usb.cpp \ genesys/sensor.h genesys/sensor.cpp \ genesys/settings.h genesys/settings.cpp \ genesys/serialize.h \ genesys/static_init.h genesys/static_init.cpp \ genesys/status.h genesys/status.cpp \ genesys/tables_frontend.cpp \ genesys/tables_gpo.cpp \ genesys/tables_memory_layout.cpp \ genesys/tables_model.cpp \ genesys/tables_motor.cpp \ genesys/tables_sensor.cpp \ genesys/test_scanner_interface.h genesys/test_scanner_interface.cpp \ genesys/test_settings.h genesys/test_settings.cpp \ genesys/test_usb_device.h genesys/test_usb_device.cpp \ genesys/usb_device.h genesys/usb_device.cpp \ genesys/low.cpp genesys/low.h \ genesys/value_filter.h \ genesys/utilities.h libgenesys_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=genesys nodist_libsane_genesys_la_SOURCES = genesys-s.cpp libsane_genesys_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=genesys libsane_genesys_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_genesys_la_LIBADD = $(COMMON_LIBS) libgenesys.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ $(MATH_LIB) $(TIFF_LIBS) $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += genesys.conf.in libgphoto2_i_la_SOURCES = gphoto2.c gphoto2.h libgphoto2_i_la_CPPFLAGS = $(AM_CPPFLAGS) $(GPHOTO2_CPPFLAGS) -DBACKEND_NAME=gphoto2 nodist_libsane_gphoto2_la_SOURCES = gphoto2-s.c libsane_gphoto2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=gphoto2 libsane_gphoto2_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_gphoto2_la_LIBADD = $(GPHOTO2_LDFLAGS) $(COMMON_LIBS) \ libgphoto2_i.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ $(SANEI_SANEI_JPEG_LO) $(GPHOTO2_LIBS) $(JPEG_LIBS) EXTRA_DIST += gphoto2.conf.in libgt68xx_la_SOURCES = gt68xx.c gt68xx.h libgt68xx_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=gt68xx nodist_libsane_gt68xx_la_SOURCES = gt68xx-s.c libsane_gt68xx_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=gt68xx libsane_gt68xx_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_gt68xx_la_LIBADD = $(COMMON_LIBS) \ libgt68xx.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += gt68xx.conf.in # TODO: Why are this distributed but not compiled? EXTRA_DIST += gt68xx_devices.c gt68xx_generic.c gt68xx_generic.h gt68xx_gt6801.c gt68xx_gt6801.h gt68xx_gt6816.c gt68xx_gt6816.h gt68xx_high.c gt68xx_high.h gt68xx_low.c gt68xx_low.h gt68xx_mid.c gt68xx_mid.h gt68xx_shm_channel.c gt68xx_shm_channel.h libhp_la_SOURCES = hp.c hp.h hp-accessor.c hp-accessor.h hp-device.c hp-device.h hp-handle.c hp-handle.h hp-hpmem.c hp-option.c hp-option.h hp-scl.c hp-scl.h hp-scsi.h libhp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp nodist_libsane_hp_la_SOURCES = hp-s.c libsane_hp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp libsane_hp_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_hp_la_LIBADD = $(COMMON_LIBS) \ libhp.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_scsi.lo \ ../sanei/sanei_pio.lo \ ../sanei/sanei_thread.lo \ ../sanei/sanei_directio.lo \ $(SCSI_LIBS) $(USB_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS) EXTRA_DIST += hp.conf.in # TODO: These should be moved to ../docs/hp; don't belong here. EXTRA_DIST += hp.README hp.TODO libhp3500_la_SOURCES = hp3500.c libhp3500_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp3500 nodist_libsane_hp3500_la_SOURCES = hp3500-s.c libsane_hp3500_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp3500 libsane_hp3500_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_hp3500_la_LIBADD = $(COMMON_LIBS) \ libhp3500.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_thread.lo \ $(MATH_LIB) $(USB_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS) libhp3900_la_SOURCES = hp3900.c libhp3900_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp3900 nodist_libsane_hp3900_la_SOURCES = hp3900-s.c libsane_hp3900_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp3900 libsane_hp3900_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_hp3900_la_LIBADD = $(COMMON_LIBS) \ libhp3900.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ $(MATH_LIB) $(TIFF_LIBS) $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += hp3900.conf.in # TODO: Why are these distributed but not compiled? EXTRA_DIST += hp3900_config.c hp3900_debug.c hp3900_rts8822.c hp3900_sane.c hp3900_types.c hp3900_usb.c libhp4200_la_SOURCES = hp4200.c hp4200.h libhp4200_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp4200 nodist_libsane_hp4200_la_SOURCES = hp4200-s.c libsane_hp4200_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp4200 libsane_hp4200_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_hp4200_la_LIBADD = $(COMMON_LIBS) \ libhp4200.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_pv8630.lo \ $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += hp4200.conf.in # TODO: Why are these distributed but not compiled? EXTRA_DIST += hp4200_lm9830.c hp4200_lm9830.h libhp5400_la_SOURCES = hp5400.c hp5400.h libhp5400_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp5400 nodist_libsane_hp5400_la_SOURCES = hp5400-s.c libsane_hp5400_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp5400 libsane_hp5400_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_hp5400_la_LIBADD = $(COMMON_LIBS) \ libhp5400.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += hp5400.conf.in # TODO: Why are these distributed but not compiled? EXTRA_DIST += hp5400_debug.c hp5400_debug.h hp5400_internal.c hp5400_internal.h hp5400_sane.c hp5400_sanei.c hp5400_sanei.h hp5400_xfer.h libhp5590_la_SOURCES = hp5590.c libhp5590_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp5590 nodist_libsane_hp5590_la_SOURCES = hp5590-s.c libsane_hp5590_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hp5590 libsane_hp5590_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_hp5590_la_LIBADD = $(COMMON_LIBS) \ libhp5590.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ $(USB_LIBS) $(RESMGR_LIBS) # TODO: Why are these distributed but not compiled? EXTRA_DIST += hp5590_cmds.c hp5590_cmds.h hp5590_low.c hp5590_low.h libhpljm1005_la_SOURCES = hpljm1005.c libhpljm1005_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hpljm1005 nodist_libsane_hpljm1005_la_SOURCES = hpljm1005-s.c libsane_hpljm1005_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hpljm1005 libsane_hpljm1005_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_hpljm1005_la_LIBADD = $(COMMON_LIBS) \ libhpljm1005.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS) libhpsj5s_la_SOURCES = hpsj5s.c hpsj5s.h libhpsj5s_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hpsj5s nodist_libsane_hpsj5s_la_SOURCES = hpsj5s-s.c libsane_hpsj5s_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hpsj5s libsane_hpsj5s_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_hpsj5s_la_LIBADD = $(COMMON_LIBS) \ libhpsj5s.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ $(IEEE1284_LIBS) EXTRA_DIST += hpsj5s.conf.in libhs2p_la_SOURCES = hs2p.c hs2p.h hs2p-saneopts.h libhs2p_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hs2p nodist_libsane_hs2p_la_SOURCES = hs2p-s.c libsane_hs2p_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=hs2p libsane_hs2p_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_hs2p_la_LIBADD = $(COMMON_LIBS) \ libhs2p.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ $(SCSI_LIBS) $(RESMGR_LIBS) EXTRA_DIST += hs2p.conf.in # TODO: Why are these distributed but not compiled? EXTRA_DIST += hs2p-scsi.c hs2p-scsi.h libibm_la_SOURCES = ibm.c ibm.h libibm_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=ibm nodist_libsane_ibm_la_SOURCES = ibm-s.c libsane_ibm_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=ibm libsane_ibm_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_ibm_la_LIBADD = $(COMMON_LIBS) \ libibm.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ $(SCSI_LIBS) $(RESMGR_LIBS) EXTRA_DIST += ibm.conf.in # TODO: Why are these distributed but not compiled? EXTRA_DIST += ibm-scsi.c libkodak_la_SOURCES = kodak.c kodak.h kodak-cmd.h libkodak_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kodak nodist_libsane_kodak_la_SOURCES = kodak-s.c libsane_kodak_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kodak libsane_kodak_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_kodak_la_LIBADD = $(COMMON_LIBS) \ libkodak.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += kodak.conf.in libkodakaio_la_SOURCES = kodakaio.c kodakaio.h libkodakaio_la_CPPFLAGS = $(AM_CPPFLAGS) $(AVAHI_CFLAGS) -DBACKEND_NAME=kodakaio nodist_libsane_kodakaio_la_SOURCES = kodakaio-s.c libsane_kodakaio_la_CPPFLAGS = $(AM_CPPFLAGS) $(AVAHI_CFLAGS) -DBACKEND_NAME=kodakaio libsane_kodakaio_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_kodakaio_la_LIBADD = $(COMMON_LIBS) \ libkodakaio.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_scsi.lo \ ../sanei/sanei_tcp.lo \ ../sanei/sanei_udp.lo \ $(USB_LIBS) $(SOCKET_LIBS) $(AVAHI_LIBS) $(MATH_LIB) $(RESMGR_LIBS) EXTRA_DIST += kodakaio.conf.in libkvs1025_la_SOURCES = kvs1025.c kvs1025_low.c kvs1025_opt.c kvs1025_usb.c \ kvs1025.h kvs1025_low.h kvs1025_usb.h kvs1025_cmds.h libkvs1025_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kvs1025 nodist_libsane_kvs1025_la_SOURCES = kvs1025-s.c libsane_kvs1025_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kvs1025 libsane_kvs1025_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_kvs1025_la_LIBADD = $(COMMON_LIBS) \ libkvs1025.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_magic.lo \ $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += kvs1025.conf.in libkvs20xx_la_SOURCES = kvs20xx.c kvs20xx_cmd.c kvs20xx_opt.c \ kvs20xx_cmd.h kvs20xx.h libkvs20xx_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kvs20xx nodist_libsane_kvs20xx_la_SOURCES = kvs20xx-s.c libsane_kvs20xx_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kvs20xx libsane_kvs20xx_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_kvs20xx_la_LIBADD = $(COMMON_LIBS) \ libkvs20xx.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_scsi.lo \ $(SCSI_LIBS) $(USB_LIBS) $(RESMGR_LIBS) libkvs40xx_la_SOURCES = kvs40xx.c kvs40xx_cmd.c kvs40xx_opt.c \ kvs40xx.h libkvs40xx_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kvs40xx nodist_libsane_kvs40xx_la_SOURCES = kvs40xx-s.c libsane_kvs40xx_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=kvs40xx libsane_kvs40xx_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_kvs40xx_la_LIBADD = $(COMMON_LIBS) \ libkvs40xx.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_scsi.lo \ $(SCSI_LIBS) $(USB_LIBS) $(PTHREAD_LIBS) $(RESMGR_LIBS) libleo_la_SOURCES = leo.c leo.h libleo_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=leo nodist_libsane_leo_la_SOURCES = leo-s.c libsane_leo_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=leo libsane_leo_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_leo_la_LIBADD = $(COMMON_LIBS) \ libleo.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ $(SCSI_LIBS) $(RESMGR_LIBS) EXTRA_DIST += leo.conf.in liblexmark_la_SOURCES = lexmark.c lexmark.h lexmark_low.c liblexmark_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=lexmark nodist_libsane_lexmark_la_SOURCES = lexmark-s.c libsane_lexmark_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=lexmark libsane_lexmark_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_lexmark_la_LIBADD = $(COMMON_LIBS) \ liblexmark.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += lexmark.conf.in # TODO: Why are these distributed but not compiled? EXTRA_DIST += lexmark_models.c lexmark_sensors.c liblexmark_x2600_la_SOURCES = lexmark_x2600.c lexmark_x2600.h liblexmark_x2600_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=lexmark_x2600 nodist_libsane_lexmark_x2600_la_SOURCES = lexmark_x2600-s.c libsane_lexmark_x2600_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=lexmark_x2600 libsane_lexmark_x2600_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_lexmark_x2600_la_LIBADD = $(COMMON_LIBS) \ liblexmark_x2600.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += lexmark_x2600.conf.in libma1509_la_SOURCES = ma1509.c ma1509.h libma1509_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=ma1509 nodist_libsane_ma1509_la_SOURCES = ma1509-s.c libsane_ma1509_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=ma1509 libsane_ma1509_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_ma1509_la_LIBADD = $(COMMON_LIBS) \ libma1509.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += ma1509.conf.in libmagicolor_la_SOURCES = magicolor.c magicolor.h libmagicolor_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=magicolor $(SNMP_CFLAGS) nodist_libsane_magicolor_la_SOURCES = magicolor-s.c libsane_magicolor_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=magicolor libsane_magicolor_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_magicolor_la_LIBADD = $(COMMON_LIBS) \ libmagicolor.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_scsi.lo \ ../sanei/sanei_tcp.lo \ ../sanei/sanei_udp.lo \ $(USB_LIBS) $(SOCKET_LIBS) $(MATH_LIB) $(RESMGR_LIBS) $(SNMP_LIBS) EXTRA_DIST += magicolor.conf.in libmatsushita_la_SOURCES = matsushita.c matsushita.h libmatsushita_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=matsushita nodist_libsane_matsushita_la_SOURCES = matsushita-s.c libsane_matsushita_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=matsushita libsane_matsushita_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_matsushita_la_LIBADD = $(COMMON_LIBS) \ libmatsushita.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ $(SCSI_LIBS) $(RESMGR_LIBS) EXTRA_DIST += matsushita.conf.in libmicrotek_la_SOURCES = microtek.c microtek.h libmicrotek_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=microtek nodist_libsane_microtek_la_SOURCES = microtek-s.c libsane_microtek_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=microtek libsane_microtek_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_microtek_la_LIBADD = $(COMMON_LIBS) \ libmicrotek.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ $(MATH_LIB) $(SCSI_LIBS) $(RESMGR_LIBS) EXTRA_DIST += microtek.conf.in libmicrotek2_la_SOURCES = microtek2.c microtek2.h libmicrotek2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=microtek2 nodist_libsane_microtek2_la_SOURCES = microtek2-s.c libsane_microtek2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=microtek2 libsane_microtek2_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_microtek2_la_LIBADD = $(COMMON_LIBS) \ libmicrotek2.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ ../sanei/sanei_thread.lo \ $(MATH_LIB) $(SCSI_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS) EXTRA_DIST += microtek2.conf.in libmustek_la_SOURCES = mustek.c mustek.h libmustek_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek nodist_libsane_mustek_la_SOURCES = mustek-s.c libsane_mustek_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek libsane_mustek_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_mustek_la_LIBADD = $(COMMON_LIBS) \ libmustek.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ ../sanei/sanei_thread.lo \ ../sanei/sanei_ab306.lo \ ../sanei/sanei_pa4s2.lo \ ../sanei/sanei_directio.lo \ $(IEEE1284_LIBS) $(SCSI_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS) EXTRA_DIST += mustek.conf.in # TODO: Why are these distributed but not compiled? EXTRA_DIST += mustek_scsi_pp.c mustek_scsi_pp.h libmustek_pp_la_SOURCES = mustek_pp.c mustek_pp.h mustek_pp_decl.h mustek_pp_drivers.h libmustek_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek_pp nodist_libsane_mustek_pp_la_SOURCES = mustek_pp-s.c libsane_mustek_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek_pp libsane_mustek_pp_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_mustek_pp_la_LIBADD = $(COMMON_LIBS) \ libmustek_pp.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_pa4s2.lo \ ../sanei/sanei_directio.lo \ $(MATH_LIB) $(IEEE1284_LIBS) EXTRA_DIST += mustek_pp.conf.in # TODO: Why are these distributed but not compiled? EXTRA_DIST += mustek_pp_ccd300.c mustek_pp_ccd300.h mustek_pp_cis.c mustek_pp_cis.h mustek_pp_null.c libmustek_usb_la_SOURCES = mustek_usb.c mustek_usb.h libmustek_usb_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek_usb nodist_libsane_mustek_usb_la_SOURCES = mustek_usb-s.c libsane_mustek_usb_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek_usb libsane_mustek_usb_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_mustek_usb_la_LIBADD = $(COMMON_LIBS) \ libmustek_usb.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += mustek_usb.conf.in # TODO: Why are these distributed but not compiled? EXTRA_DIST += mustek_usb_high.c mustek_usb_high.h mustek_usb_low.c mustek_usb_low.h mustek_usb_mid.c mustek_usb_mid.h libmustek_usb2_la_SOURCES = mustek_usb2.c mustek_usb2.h libmustek_usb2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek_usb2 nodist_libsane_mustek_usb2_la_SOURCES = mustek_usb2-s.c libsane_mustek_usb2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=mustek_usb2 libsane_mustek_usb2_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_mustek_usb2_la_LIBADD = $(COMMON_LIBS) \ libmustek_usb2.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ $(MATH_LIB) $(PTHREAD_LIBS) $(USB_LIBS) $(RESMGR_LIBS) # TODO: Why are these distributed but not compiled? EXTRA_DIST += mustek_usb2_asic.c mustek_usb2_asic.h mustek_usb2_high.c mustek_usb2_high.h mustek_usb2_reflective.c mustek_usb2_transparent.c libnec_la_SOURCES = nec.c nec.h libnec_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=nec nodist_libsane_nec_la_SOURCES = nec-s.c libsane_nec_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=nec libsane_nec_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_nec_la_LIBADD = $(COMMON_LIBS) \ libnec.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ $(MATH_LIB) $(SCSI_LIBS) $(RESMGR_LIBS) EXTRA_DIST += nec.conf.in libnet_la_SOURCES = net.c net.h libnet_la_CPPFLAGS = $(AM_CPPFLAGS) $(AVAHI_CFLAGS) -DBACKEND_NAME=net nodist_libsane_net_la_SOURCES = net-s.c libsane_net_la_CPPFLAGS = $(AM_CPPFLAGS) $(AVAHI_CFLAGS) -DBACKEND_NAME=net libsane_net_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_net_la_LIBADD = $(COMMON_LIBS) \ libnet.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_net.lo \ ../sanei/sanei_wire.lo \ ../sanei/sanei_codec_bin.lo \ $(AVAHI_LIBS) $(SOCKET_LIBS) EXTRA_DIST += net.conf.in libniash_la_SOURCES = niash.c libniash_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=niash nodist_libsane_niash_la_SOURCES = niash-s.c libsane_niash_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=niash libsane_niash_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_niash_la_LIBADD = $(COMMON_LIBS) \ libniash.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS) # TODO: Why are these distributed but not compiled? EXTRA_DIST += niash_core.c niash_core.h niash_xfer.c niash_xfer.h libpie_la_SOURCES = pie.c pie-scsidef.h libpie_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pie nodist_libsane_pie_la_SOURCES = pie-s.c libsane_pie_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pie libsane_pie_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_pie_la_LIBADD = $(COMMON_LIBS) \ libpie.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ ../sanei/sanei_thread.lo \ $(SCSI_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS) EXTRA_DIST += pie.conf.in libpieusb_la_SOURCES = pieusb.h pieusb_buffer.c pieusb_buffer.h pieusb_scancmd.c pieusb_scancmd.h pieusb_specific.c pieusb_specific.h pieusb_usb.c pieusb_usb.h pieusb.c libpieusb_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pieusb nodist_libsane_pieusb_la_SOURCES = pieusb-s.c libsane_pieusb_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pieusb libsane_pieusb_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_pieusb_la_LIBADD = $(COMMON_LIBS) \ libpieusb.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ ../sanei/sanei_thread.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_ir.lo \ ../sanei/sanei_magic.lo \ $(SANEI_THREAD_LIBS) $(RESMGR_LIBS) $(USB_LIBS) $(MATH_LIB) EXTRA_DIST += pieusb.conf.in libp5_la_SOURCES = p5.c p5.h p5_device.h libp5_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=p5 nodist_libsane_p5_la_SOURCES = p5-s.c libsane_p5_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=p5 libsane_p5_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_p5_la_LIBADD = $(COMMON_LIBS) \ libp5.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo EXTRA_DIST += p5.conf.in p5_device.c libpint_la_SOURCES = pint.c pint.h libpint_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pint nodist_libsane_pint_la_SOURCES = pint-s.c libsane_pint_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pint libsane_pint_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_pint_la_LIBADD = $(COMMON_LIBS) \ libpint.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo libpixma_la_SOURCES = \ pixma/pixma_sane_options.h \ pixma/pixma.c \ pixma/pixma.h \ pixma/pixma_io_sanei.c \ pixma/pixma_io.h \ pixma/pixma_common.c \ pixma/pixma_common.h \ pixma/pixma_mp150.c \ pixma/pixma_mp730.c \ pixma/pixma_mp750.c \ pixma/pixma_mp800.c \ pixma/pixma_imageclass.c \ pixma/pixma_bjnp.c \ pixma/pixma_bjnp.h \ pixma/pixma_bjnp_private.h \ pixma/pixma_rename.h libpixma_la_CPPFLAGS = $(AM_CPPFLAGS) $(XML_CFLAGS) -DBACKEND_NAME=pixma $(srcdir)/pixma/pixma_sane_options.h: $(srcdir)/pixma/pixma.c @if $(AM_V_P); then \ echo "Generating pixma/$(@F) from pixma/$(^F)"; \ else \ echo " GEN pixma/$(@F)"; \ fi @$(PYTHON) $(srcdir)/pixma/scripts/pixma_gen_options.py h < $^ > $@ $(srcdir)/pixma/pixma_sane_options.c: $(srcdir)/pixma/pixma.c @if $(AM_V_P); then \ echo "Generating pixma/$(@F) from pixma/$(^F)"; \ else \ echo " GEN pixma/$(@F)"; \ fi @$(PYTHON) $(srcdir)/pixma/scripts/pixma_gen_options.py < $^ > $@ BUILT_SOURCES += pixma/pixma_sane_options.c BUILT_SOURCES += pixma/pixma_sane_options.h EXTRA_DIST += pixma/pixma_sane_options.c EXTRA_DIST += pixma/scripts/pixma_gen_options.py nodist_libsane_pixma_la_SOURCES = pixma-s.c libsane_pixma_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pixma libsane_pixma_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_pixma_la_LIBADD = $(COMMON_LIBS) \ libpixma.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_thread.lo \ $(SANEI_SANEI_JPEG_LO) $(JPEG_LIBS) $(XML_LIBS) $(MATH_LIB) $(SOCKET_LIBS) $(USB_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS) EXTRA_DIST += pixma.conf.in libplustek_la_SOURCES = plustek.c plustek.h libplustek_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=plustek nodist_libsane_plustek_la_SOURCES = plustek-s.c libsane_plustek_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=plustek libsane_plustek_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_plustek_la_LIBADD = $(COMMON_LIBS) \ libplustek.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_thread.lo \ ../sanei/sanei_lm983x.lo \ ../sanei/sanei_access.lo \ $(MATH_LIB) $(USB_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS) EXTRA_DIST += plustek.conf.in EXTRA_DIST += plustek-usb.c plustek-usb.h plustek-usbcal.c plustek-usbcalfile.c plustek-usbdevs.c plustek-usbhw.c plustek-usbimg.c plustek-usbio.c plustek-usbmap.c plustek-usbscan.c plustek-usbshading.c libplustek_pp_la_SOURCES = plustek_pp.c plustek-pp.h libplustek_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=plustek_pp nodist_libsane_plustek_pp_la_SOURCES = plustek_pp-s.c libsane_plustek_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=plustek_pp libsane_plustek_pp_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_plustek_pp_la_LIBADD = $(COMMON_LIBS) \ libplustek_pp.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_pp.lo \ ../sanei/sanei_thread.lo \ ../sanei/sanei_directio.lo \ $(MATH_LIB) $(IEEE1284_LIBS) $(SANEI_THREAD_LIBS) EXTRA_DIST += plustek_pp.conf.in # TODO: Why are these distributed but not compiled? EXTRA_DIST += plustek-pp_dac.c plustek-pp_dbg.h plustek-pp_detect.c plustek-pp_genericio.c plustek-pp_hwdefs.h plustek-pp_image.c plustek-pp_io.c plustek-pp_map.c plustek-pp_misc.c plustek-pp_models.c plustek-pp_motor.c plustek-pp_p12.c 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_wrapper.c libpnm_la_SOURCES = pnm.c libpnm_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pnm nodist_libsane_pnm_la_SOURCES = pnm-s.c libsane_pnm_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=pnm libsane_pnm_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_pnm_la_LIBADD = $(COMMON_LIBS) \ libpnm.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo libqcam_la_SOURCES = qcam.c qcam.h libqcam_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=qcam nodist_libsane_qcam_la_SOURCES = qcam-s.c libsane_qcam_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=qcam libsane_qcam_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_qcam_la_LIBADD = $(COMMON_LIBS) \ libqcam.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_pio.lo \ ../sanei/sanei_directio.lo EXTRA_DIST += qcam.conf.in libricoh_la_SOURCES = ricoh.c ricoh.h libricoh_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=ricoh nodist_libsane_ricoh_la_SOURCES = ricoh-s.c libsane_ricoh_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=ricoh libsane_ricoh_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_ricoh_la_LIBADD = $(COMMON_LIBS) \ libricoh.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ $(SCSI_LIBS) $(RESMGR_LIBS) EXTRA_DIST += ricoh.conf.in # TODO: Why are these distributed but not compiled? EXTRA_DIST += ricoh-scsi.c libricoh2_la_SOURCES = ricoh2.c libricoh2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=ricoh2 nodist_libsane_ricoh2_la_SOURCES = ricoh2-s.c libsane_ricoh2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=ricoh2 libsane_ricoh2_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_ricoh2_la_LIBADD = $(COMMON_LIBS) \ libricoh2.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ $(USB_LIBS) EXTRA_DIST += ricoh2_buffer.c librts8891_la_SOURCES = rts8891.c rts8891.h rts88xx_lib.c rts88xx_lib.h librts8891_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=rts8891 nodist_libsane_rts8891_la_SOURCES = rts8891-s.c libsane_rts8891_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=rts8891 libsane_rts8891_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_rts8891_la_LIBADD = $(COMMON_LIBS) \ librts8891.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ ../sanei/sanei_usb.lo \ $(SCSI_LIBS) $(USB_LIBS) $(RESMGR_LIBS) $(RESMGR_LIBS) EXTRA_DIST += rts8891.conf.in # TODO: Why are these distributed but not compiled? EXTRA_DIST += rts8891_devices.c rts8891_low.c rts8891_low.h libs9036_la_SOURCES = s9036.c s9036.h libs9036_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=s9036 nodist_libsane_s9036_la_SOURCES = s9036-s.c libsane_s9036_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=s9036 libsane_s9036_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_s9036_la_LIBADD = $(COMMON_LIBS) \ libs9036.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ $(SCSI_LIBS) $(RESMGR_LIBS) EXTRA_DIST += s9036.conf.in libsceptre_la_SOURCES = sceptre.c sceptre.h libsceptre_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sceptre nodist_libsane_sceptre_la_SOURCES = sceptre-s.c libsane_sceptre_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sceptre libsane_sceptre_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_sceptre_la_LIBADD = $(COMMON_LIBS) \ libsceptre.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ $(SCSI_LIBS) $(RESMGR_LIBS) EXTRA_DIST += sceptre.conf.in libsharp_la_SOURCES = sharp.c sharp.h libsharp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sharp nodist_libsane_sharp_la_SOURCES = sharp-s.c libsane_sharp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sharp libsane_sharp_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_sharp_la_LIBADD = $(COMMON_LIBS) \ libsharp.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ $(MATH_LIB) $(SCSI_LIBS) $(RESMGR_LIBS) EXTRA_DIST += sharp.conf.in libsm3600_la_SOURCES = sm3600.c sm3600.h libsm3600_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sm3600 nodist_libsane_sm3600_la_SOURCES = sm3600-s.c libsane_sm3600_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sm3600 libsane_sm3600_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_sm3600_la_LIBADD = $(COMMON_LIBS) \ libsm3600.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ $(USB_LIBS) $(RESMGR_LIBS) # TODO: Why are these distributed but not compiled? EXTRA_DIST += sm3600-color.c sm3600-gray.c sm3600-homerun.c sm3600-scanmtek.c sm3600-scantool.h sm3600-scanusb.c sm3600-scanutil.c libsm3840_la_SOURCES = sm3840.c sm3840.h sm3840_params.h libsm3840_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sm3840 nodist_libsane_sm3840_la_SOURCES = sm3840-s.c libsane_sm3840_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sm3840 libsane_sm3840_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_sm3840_la_LIBADD = $(COMMON_LIBS) \ libsm3840.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += sm3840.conf.in # TODO: Why are these distributed but not compiled? EXTRA_DIST += sm3840_lib.c sm3840_lib.h sm3840_scan.c libsnapscan_la_SOURCES = snapscan.c snapscan.h libsnapscan_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=snapscan nodist_libsane_snapscan_la_SOURCES = snapscan-s.c libsane_snapscan_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=snapscan libsane_snapscan_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_snapscan_la_LIBADD = $(COMMON_LIBS) \ libsnapscan.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_thread.lo \ ../sanei/sanei_scsi.lo \ $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS) EXTRA_DIST += snapscan.conf.in # TODO: Why are these distributed but not compiled? EXTRA_DIST += snapscan-data.c snapscan-mutex.c snapscan-options.c snapscan-scsi.c snapscan-sources.c snapscan-sources.h snapscan-usb.c snapscan-usb.h libsp15c_la_SOURCES = sp15c.c sp15c.h sp15c-scsi.h libsp15c_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sp15c nodist_libsane_sp15c_la_SOURCES = sp15c-s.c libsane_sp15c_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=sp15c libsane_sp15c_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_sp15c_la_LIBADD = $(COMMON_LIBS) \ libsp15c.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_thread.lo \ ../sanei/sanei_scsi.lo \ $(SCSI_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS) EXTRA_DIST += sp15c.conf.in libst400_la_SOURCES = st400.c st400.h libst400_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=st400 nodist_libsane_st400_la_SOURCES = st400-s.c ../sanei/sanei_scsi.lo libsane_st400_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=st400 libsane_st400_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_st400_la_LIBADD = $(COMMON_LIBS) \ libst400.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ $(SCSI_LIBS) $(RESMGR_LIBS) EXTRA_DIST += st400.conf.in libstv680_la_SOURCES = stv680.c stv680.h libstv680_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=stv680 nodist_libsane_stv680_la_SOURCES = stv680-s.c libsane_stv680_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=stv680 libsane_stv680_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_stv680_la_LIBADD = $(COMMON_LIBS) \ libstv680.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += stv680.conf.in libtamarack_la_SOURCES = tamarack.c tamarack.h libtamarack_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=tamarack nodist_libsane_tamarack_la_SOURCES = tamarack-s.c libsane_tamarack_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=tamarack libsane_tamarack_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_tamarack_la_LIBADD = $(COMMON_LIBS) \ libtamarack.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_thread.lo \ ../sanei/sanei_scsi.lo \ $(SCSI_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS) EXTRA_DIST += tamarack.conf.in libtest_la_SOURCES = test.c test.h libtest_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=test nodist_libsane_test_la_SOURCES = test-s.c libsane_test_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=test libsane_test_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_test_la_LIBADD = $(COMMON_LIBS) \ libtest.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_thread.lo \ $(SANEI_THREAD_LIBS) EXTRA_DIST += test.conf.in # TODO: Why are these distributed but not compiled? EXTRA_DIST += test-picture.c libteco1_la_SOURCES = teco1.c teco1.h libteco1_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=teco1 nodist_libsane_teco1_la_SOURCES = teco1-s.c libsane_teco1_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=teco1 libsane_teco1_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_teco1_la_LIBADD = $(COMMON_LIBS) \ libteco1.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ $(SCSI_LIBS) $(RESMGR_LIBS) EXTRA_DIST += teco1.conf.in libteco2_la_SOURCES = teco2.c teco2.h libteco2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=teco2 nodist_libsane_teco2_la_SOURCES = teco2-s.c libsane_teco2_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=teco2 libsane_teco2_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_teco2_la_LIBADD = $(COMMON_LIBS) \ libteco2.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ $(SCSI_LIBS) $(RESMGR_LIBS) EXTRA_DIST += teco2.conf.in libteco3_la_SOURCES = teco3.c teco3.h libteco3_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=teco3 nodist_libsane_teco3_la_SOURCES = teco3-s.c libsane_teco3_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=teco3 libsane_teco3_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_teco3_la_LIBADD = $(COMMON_LIBS) \ libteco3.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_scsi.lo \ $(SCSI_LIBS) $(RESMGR_LIBS) EXTRA_DIST += teco3.conf.in libu12_la_SOURCES = u12.c u12.h libu12_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=u12 nodist_libsane_u12_la_SOURCES = u12-s.c libsane_u12_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=u12 libsane_u12_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_u12_la_LIBADD = $(COMMON_LIBS) \ libu12.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_thread.lo \ $(MATH_LIB) $(USB_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS) EXTRA_DIST += u12.conf.in # TODO: Why are these distributed but not compiled? EXTRA_DIST += u12-ccd.c u12-hw.c u12-hwdef.h u12-if.c u12-image.c u12-io.c u12-map.c u12-motor.c u12-scanner.h u12-shading.c u12-tpa.c libumax_la_SOURCES = umax.c umax.h libumax_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=umax nodist_libsane_umax_la_SOURCES = umax-s.c libsane_umax_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=umax libsane_umax_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_umax_la_LIBADD = $(COMMON_LIBS) \ libumax.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_config2.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_thread.lo \ ../sanei/sanei_scsi.lo \ ../sanei/sanei_pv8630.lo \ $(MATH_LIB) $(SCSI_LIBS) $(USB_LIBS) $(SANEI_THREAD_LIBS) $(RESMGR_LIBS) EXTRA_DIST += umax.conf.in # TODO: Why are these distributed but not compiled? EXTRA_DIST += 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 libumax1220u_la_SOURCES = umax1220u.c libumax1220u_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=umax1220u nodist_libsane_umax1220u_la_SOURCES = umax1220u-s.c libsane_umax1220u_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=umax1220u libsane_umax1220u_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_umax1220u_la_LIBADD = $(COMMON_LIBS) \ libumax1220u.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_pv8630.lo \ $(MATH_LIB) $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += umax1220u.conf.in # TODO: Why are these distributed but not compiled? EXTRA_DIST += umax1220u-common.c libumax_pp_la_SOURCES = umax_pp.c umax_pp.h umax_pp_low.c umax_pp_low.h umax_pp_mid.c umax_pp_mid.h libumax_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=umax_pp nodist_libsane_umax_pp_la_SOURCES = umax_pp-s.c libsane_umax_pp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=umax_pp libsane_umax_pp_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_umax_pp_la_LIBADD = $(COMMON_LIBS) \ libumax_pp.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ ../sanei/sanei_directio.lo \ sane_strstatus.lo \ $(MATH_LIB) EXTRA_DIST += umax_pp.conf.in libv4l_la_SOURCES = v4l.c v4l.h v4l-frequencies.h libv4l_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBV4L_CFLAGS) -DBACKEND_NAME=v4l nodist_libsane_v4l_la_SOURCES = v4l-s.c libsane_v4l_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=v4l libsane_v4l_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_v4l_la_LIBADD = $(COMMON_LIBS) \ libv4l.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ $(LIBV4L_LIBS) EXTRA_DIST += v4l.conf.in libxerox_mfp_la_SOURCES = xerox_mfp.c xerox_mfp-usb.c xerox_mfp-tcp.c xerox_mfp.h libxerox_mfp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=xerox_mfp nodist_libsane_xerox_mfp_la_SOURCES = xerox_mfp-s.c libsane_xerox_mfp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=xerox_mfp libsane_xerox_mfp_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_xerox_mfp_la_LIBADD = $(COMMON_LIBS) \ libxerox_mfp.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ @SANEI_SANEI_JPEG_LO@ $(JPEG_LIBS) ../sanei/sanei_usb.lo \ ../sanei/sanei_tcp.lo \ $(MATH_LIB) $(SOCKET_LIBS) $(USB_LIBS) $(RESMGR_LIBS) EXTRA_DIST += xerox_mfp.conf.in nodist_libdll_preload_la_SOURCES = dll-preload.h libdll_preload_la_SOURCES = dll.c libdll_preload_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dll -DENABLE_PRELOAD libdll_preload_la_LIBADD = ../sanei/sanei_usb.lo \ $(USB_LIBS) $(XML_LIBS) libdll_la_SOURCES = dll.c libdll_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dll libdll_la_LIBADD = ../sanei/sanei_usb.lo \ $(USB_LIBS) $(XML_LIBS) BUILT_SOURCES += dll-preload.h CLEANFILES += dll-preload.h nodist_libsane_dll_la_SOURCES = dll-s.c libsane_dll_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dll libsane_dll_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS) libsane_dll_la_LIBADD = $(COMMON_LIBS) \ libdll.la \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ sane_strstatus.lo \ $(DL_LIBS) EXTRA_DIST += dll.conf.in # TODO: Why is this distributed but not installed? EXTRA_DIST += dll.aliases # libsane.la and libsane-dll.la are the same thing except for # the addition of backends listed by PRELOADABLE_BACKENDS that are # statically linked in. # Also, libsane.la goes into $(libdir) where as all libsane-* # (including libsane-dll.la) go into $(libdir)/sane # FIXME: Since we are throwing in the kitchen sink, might as # well link in ../sanei/libsanei.la instead. But currently, # libsanei.la is linking in sanei_auth which requires md5. # Shipping md5 could cause symbol conflicts with commonly used # md5 external libraries. Either need to prefix md5 with sanei_ # (see liblib.la and snprintf), or move sanei_auth outside # of libsanei. # # FIXME: This is using every possibly needed library and dependency # when the user is using any PRELOADABLE_BACKENDS, irrespective of # what backends are preloaded. It should include what is needed by # those backends that are actually preloaded. if preloadable_backends_enabled PRELOADABLE_BACKENDS_LIBS = \ ../sanei/sanei_config2.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_scsi.lo \ ../sanei/sanei_pv8630.lo \ ../sanei/sanei_pp.lo \ ../sanei/sanei_thread.lo \ ../sanei/sanei_lm983x.lo \ ../sanei/sanei_access.lo \ ../sanei/sanei_net.lo \ ../sanei/sanei_wire.lo \ ../sanei/sanei_codec_bin.lo \ ../sanei/sanei_pa4s2.lo \ ../sanei/sanei_ab306.lo \ ../sanei/sanei_pio.lo \ ../sanei/sanei_tcp.lo \ ../sanei/sanei_udp.lo \ ../sanei/sanei_magic.lo \ ../sanei/sanei_directio.lo \ $(LIBV4L_LIBS) $(MATH_LIB) \ $(IEEE1284_LIBS) \ $(TIFF_LIBS) \ $(JPEG_LIBS) \ $(GPHOTO2_LIBS) \ $(SOCKET_LIBS) \ $(USB_LIBS) \ $(AVAHI_LIBS) \ $(SCSI_LIBS) \ $(SANEI_THREAD_LIBS) \ $(RESMGR_LIBS) \ $(PNG_LIBS) \ $(POPPLER_GLIB_LIBS) \ $(XML_LIBS) \ $(libcurl_LIBS) \ $(SNMP_LIBS) PRELOADABLE_BACKENDS_DEPS = ../sanei/sanei_config2.lo \ ../sanei/sanei_usb.lo \ ../sanei/sanei_scsi.lo \ ../sanei/sanei_pv8630.lo \ ../sanei/sanei_pp.lo \ ../sanei/sanei_thread.lo \ ../sanei/sanei_lm983x.lo \ ../sanei/sanei_access.lo \ ../sanei/sanei_net.lo \ ../sanei/sanei_wire.lo \ ../sanei/sanei_codec_bin.lo \ ../sanei/sanei_pa4s2.lo \ ../sanei/sanei_ab306.lo \ ../sanei/sanei_pio.lo \ ../sanei/sanei_tcp.lo \ ../sanei/sanei_udp.lo \ ../sanei/sanei_magic.lo \ ../sanei/sanei_directio.lo \ $(SANEI_SANEI_JPEG_LO) endif nodist_libsane_la_SOURCES = dll-s.c libsane_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=dll libsane_la_LDFLAGS = $(DIST_LIBS_LDFLAGS) libsane_la_LIBADD = $(COMMON_LIBS) \ $(PRELOADABLE_BACKENDS_ENABLED) libdll_preload.la \ sane_strstatus.lo \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ $(PRELOADABLE_BACKENDS_LIBS) $(DL_LIBS) $(XML_LIBS) # WARNING: Automake is getting this wrong so have to do it ourselves. libsane_la_DEPENDENCIES = ../lib/liblib.la \ $(PRELOADABLE_BACKENDS_ENABLED) libdll_preload.la \ sane_strstatus.lo \ ../sanei/sanei_init_debug.lo \ ../sanei/sanei_constrain_value.lo \ ../sanei/sanei_config.lo \ $(PRELOADABLE_BACKENDS_DEPS) backends-1.3.0/backend/abaton.c000066400000000000000000001146601456256263500163120ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1998 David Huggins-Daines, heavily based on the Apple scanner driver (since Abaton scanners are very similar to old Apple scanners), which is (C) 1998 Milon Firikis, which is, in turn, based on the Mustek driver, (C) 1996-7 David Mosberger-Tang. 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, see . 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 Abaton flatbed scanners. */ #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "../include/_stdint.h" #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_scsi.h" #define BACKEND_NAME abaton #include "../include/sane/sanei_backend.h" #ifndef PATH_MAX # define PATH_MAX 1024 #endif #include "../include/sane/sanei_config.h" #define ABATON_CONFIG_FILE "abaton.conf" #include "abaton.h" static const SANE_Device **devlist = 0; static int num_devices; static Abaton_Device *first_dev; static Abaton_Scanner *first_handle; static SANE_String_Const mode_list[5]; static const SANE_String_Const halftone_pattern_list[] = { "spiral", "bayer", 0 }; static const SANE_Range dpi_range = { /* min, max, quant */ 72, 300, 1 }; static const SANE_Range enhance_range = { 1, 255, 1 }; static const SANE_Range x_range = { 0, 8.5 * MM_PER_INCH, 1 }; static const SANE_Range y_range = { 0, 14.0 * MM_PER_INCH, 1 }; #define ERROR_MESSAGE 1 #define USER_MESSAGE 5 #define FLOW_CONTROL 50 #define VARIABLE_CONTROL 70 #define DEBUG_SPECIAL 100 #define IO_MESSAGE 110 #define INNER_LOOP 120 /* SCSI commands that the Abaton scanners understand: */ #define TEST_UNIT_READY 0x00 #define REQUEST_SENSE 0x03 #define INQUIRY 0x12 #define START_STOP 0x1b #define SET_WINDOW 0x24 #define READ_10 0x28 #define WRITE_10 0x2b /* not used, AFAIK */ #define GET_DATA_STATUS 0x34 #define INQ_LEN 0x60 static const uint8_t inquiry[] = { INQUIRY, 0x00, 0x00, 0x00, INQ_LEN, 0x00 }; static const uint8_t test_unit_ready[] = { TEST_UNIT_READY, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* convenience macros */ #define ENABLE(OPTION) s->opt[OPTION].cap &= ~SANE_CAP_INACTIVE #define DISABLE(OPTION) s->opt[OPTION].cap |= SANE_CAP_INACTIVE #define IS_ACTIVE(OPTION) (((s->opt[OPTION].cap) & SANE_CAP_INACTIVE) == 0) /* store an 8-bit-wide value at the location specified by ptr */ #define STORE8(ptr, val) (*((uint8_t *) ptr) = val) /* store a 16-bit-wide value in network (big-endian) byte order */ #define STORE16(ptr, val) \ { \ *((uint8_t *) ptr) = (val >> 8) & 0xff; \ *((uint8_t *) ptr+1) = val & 0xff; \ } /* store a 24-bit-wide value in network (big-endian) byte order */ #define STORE24(ptr, val) \ { \ *((uint8_t *) ptr) = (val >> 16) & 0xff; \ *((uint8_t *) ptr+1) = (val >> 8) & 0xff; \ *((uint8_t *) ptr+2) = val & 0xff; \ } /* store a 32-bit-wide value in network (big-endian) byte order */ #define STORE32(ptr, val) \ { \ *((uint8_t *) ptr) = (val >> 24) & 0xff; \ *((uint8_t *) ptr+1) = (val >> 16) & 0xff; \ *((uint8_t *) ptr+2) = (val >> 8) & 0xff; \ *((uint8_t *) ptr+3) = val & 0xff; \ } /* retrieve a 24-bit-wide big-endian value at ptr */ #define GET24(ptr) \ (*((uint8_t *) ptr) << 16) + \ (*((uint8_t *) ptr+1) << 8) + \ (*((uint8_t *) ptr+2)) static SANE_Status wait_ready (int fd) { #define MAX_WAITING_TIME 60 /* one minute, at most */ struct timeval now, start; SANE_Status status; gettimeofday (&start, 0); while (1) { DBG (USER_MESSAGE, "wait_ready: sending TEST_UNIT_READY\n"); status = sanei_scsi_cmd (fd, test_unit_ready, sizeof (test_unit_ready), 0, 0); switch (status) { default: /* Ignore errors while waiting for scanner to become ready. Some SCSI drivers return EIO while the scanner is returning to the home position. */ DBG (ERROR_MESSAGE, "wait_ready: test unit ready failed (%s)\n", sane_strstatus (status)); /* fall through */ case SANE_STATUS_DEVICE_BUSY: gettimeofday (&now, 0); if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME) { DBG (ERROR_MESSAGE, "wait_ready: timed out after %ld seconds\n", (long) (now.tv_sec - start.tv_sec)); return SANE_STATUS_INVAL; } usleep (100000); /* retry after 100ms */ break; case SANE_STATUS_GOOD: return status; } } return SANE_STATUS_INVAL; } static SANE_Status sense_handler (int scsi_fd, u_char * result, void *arg) { (void) scsi_fd; /* silence gcc */ (void) arg; /* silence gcc */ switch (result[2] & 0x0F) { case 0: DBG (USER_MESSAGE, "Sense: No sense Error\n"); return SANE_STATUS_GOOD; case 2: DBG (ERROR_MESSAGE, "Sense: Scanner not ready\n"); return SANE_STATUS_DEVICE_BUSY; case 4: DBG (ERROR_MESSAGE, "Sense: Hardware Error. Read more...\n"); return SANE_STATUS_IO_ERROR; case 5: DBG (ERROR_MESSAGE, "Sense: Illegal request\n"); return SANE_STATUS_UNSUPPORTED; case 6: DBG (ERROR_MESSAGE, "Sense: Unit Attention (Wait until scanner " "boots)\n"); return SANE_STATUS_DEVICE_BUSY; case 9: DBG (ERROR_MESSAGE, "Sense: Vendor Unique. Read more...\n"); return SANE_STATUS_IO_ERROR; default: DBG (ERROR_MESSAGE, "Sense: Unknown Sense Key. Read more...\n"); return SANE_STATUS_IO_ERROR; } return SANE_STATUS_GOOD; } static SANE_Status request_sense (Abaton_Scanner * s) { uint8_t cmd[6]; uint8_t result[22]; size_t size = sizeof (result); SANE_Status status; memset (cmd, 0, sizeof (cmd)); memset (result, 0, sizeof (result)); cmd[0] = REQUEST_SENSE; STORE8 (cmd + 4, sizeof (result)); sanei_scsi_cmd (s->fd, cmd, sizeof (cmd), result, &size); if (result[7] != 14) { DBG (ERROR_MESSAGE, "Additional Length %u\n", (unsigned int) result[7]); status = SANE_STATUS_IO_ERROR; } status = sense_handler (s->fd, result, NULL); if (status == SANE_STATUS_IO_ERROR) { /* Since I haven't figured out the vendor unique error codes on this thing, I'll just handle the normal ones for now */ if (result[18] & 0x80) DBG (ERROR_MESSAGE, "Sense: Dim Light (output of lamp below 70%%).\n"); if (result[18] & 0x40) DBG (ERROR_MESSAGE, "Sense: No Light at all.\n"); if (result[18] & 0x20) DBG (ERROR_MESSAGE, "Sense: No Home.\n"); if (result[18] & 0x10) DBG (ERROR_MESSAGE, "Sense: No Limit. Tried to scan out of range.\n"); } DBG (USER_MESSAGE, "Sense: Optical gain %u.\n", (unsigned int) result[20]); return status; } static SANE_Status set_window (Abaton_Scanner * s) { uint8_t cmd[10 + 40]; uint8_t *window = cmd + 10 + 8; int invert; memset (cmd, 0, sizeof (cmd)); cmd[0] = SET_WINDOW; cmd[8] = 40; /* Just like the Apple scanners, we put the resolution here */ STORE16 (window + 2, s->val[OPT_X_RESOLUTION].w); STORE16 (window + 4, s->val[OPT_Y_RESOLUTION].w); /* Unlike Apple scanners, these are pixel values */ STORE16 (window + 6, s->ULx); STORE16 (window + 8, s->ULy); STORE16 (window + 10, s->Width); STORE16 (window + 12, s->Height); STORE8 (window + 14, s->val[OPT_BRIGHTNESS].w); STORE8 (window + 15, s->val[OPT_THRESHOLD].w); STORE8 (window + 16, s->val[OPT_CONTRAST].w); invert = s->val[OPT_NEGATIVE].w; if (!strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART)) { STORE8 (window + 17, 0); } else if (!strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_HALFTONE)) { STORE8 (window + 17, 1); } else if (!strcmp (s->val[OPT_MODE].s, "Gray256") || !strcmp (s->val[OPT_MODE].s, "Gray16")) { STORE8 (window + 17, 2); invert = !s->val[OPT_NEGATIVE].w; } else { DBG (ERROR_MESSAGE, "Can't match mode %s\n", s->val[OPT_MODE].s); return SANE_STATUS_INVAL; } STORE8 (window + 18, s->bpp); if (!strcmp (s->val[OPT_HALFTONE_PATTERN].s, "spiral")) { STORE8 (window + 20, 0); } else if (!strcmp (s->val[OPT_HALFTONE_PATTERN].s, "bayer")) { STORE8 (window + 20, 1); } else { DBG (ERROR_MESSAGE, "Can't match haftone pattern %s\n", s->val[OPT_HALFTONE_PATTERN].s); return SANE_STATUS_INVAL; } /* We have to invert these ones for some reason, so why not let the scanner do it for us... */ STORE8 (window + 21, invert ? 0x80 : 0); STORE16 (window + 22, (s->val[OPT_MIRROR].w != 0)); return sanei_scsi_cmd (s->fd, cmd, sizeof (cmd), 0, 0); } static SANE_Status start_scan (Abaton_Scanner * s) { SANE_Status status; uint8_t start[7]; memset (start, 0, sizeof (start)); start[0] = START_STOP; start[4] = 1; status = sanei_scsi_cmd (s->fd, start, sizeof (start), 0, 0); return status; } static SANE_Status attach (const char *devname, Abaton_Device ** devp, int may_wait) { char result[INQ_LEN]; const char *model_name = result + 44; int fd, abaton_scanner; Abaton_Device *dev; SANE_Status status; size_t size; for (dev = first_dev; dev; dev = dev->next) if (strcmp (dev->sane.name, devname) == 0) { if (devp) *devp = dev; return SANE_STATUS_GOOD; } DBG (USER_MESSAGE, "attach: opening %s\n", devname); status = sanei_scsi_open (devname, &fd, sense_handler, 0); if (status != SANE_STATUS_GOOD) { DBG (ERROR_MESSAGE, "attach: open failed (%s)\n", sane_strstatus (status)); return SANE_STATUS_INVAL; } if (may_wait) wait_ready (fd); DBG (USER_MESSAGE, "attach: sending INQUIRY\n"); size = sizeof (result); status = sanei_scsi_cmd (fd, inquiry, sizeof (inquiry), result, &size); if (status != SANE_STATUS_GOOD) { DBG (ERROR_MESSAGE, "attach: inquiry failed (%s)\n", sane_strstatus (status)); sanei_scsi_close (fd); return status; } status = wait_ready (fd); sanei_scsi_close (fd); if (status != SANE_STATUS_GOOD) return status; /* check that we've got an Abaton */ abaton_scanner = (strncmp (result + 8, "ABATON ", 8) == 0); model_name = result + 16; /* make sure it's a scanner ;-) */ abaton_scanner = abaton_scanner && (result[0] == 0x06); if (!abaton_scanner) { DBG (ERROR_MESSAGE, "attach: device doesn't look like an Abaton scanner " "(result[0]=%#02x)\n", result[0]); return SANE_STATUS_INVAL; } dev = malloc (sizeof (*dev)); if (!dev) return SANE_STATUS_NO_MEM; memset (dev, 0, sizeof (*dev)); dev->sane.name = strdup (devname); dev->sane.vendor = "Abaton"; dev->sane.model = strndup (model_name, 16); dev->sane.type = "flatbed scanner"; if (!strncmp (model_name, "SCAN 300/GS", 11)) { dev->ScannerModel = ABATON_300GS; } else if (!strncmp (model_name, "SCAN 300/S", 10)) { dev->ScannerModel = ABATON_300S; } DBG (USER_MESSAGE, "attach: found Abaton scanner model %s (%s)\n", dev->sane.model, dev->sane.type); ++num_devices; dev->next = first_dev; first_dev = dev; if (devp) *devp = dev; return SANE_STATUS_GOOD; } static SANE_Status attach_one (const char *devname) { return attach (devname, 0, SANE_FALSE); } static SANE_Status calc_parameters (Abaton_Scanner * s) { SANE_String val = s->val[OPT_MODE].s; SANE_Status status = SANE_STATUS_GOOD; SANE_Int dpix = s->val[OPT_X_RESOLUTION].w; SANE_Int dpiy = s->val[OPT_Y_RESOLUTION].w; double ulx, uly, width, height; DBG (FLOW_CONTROL, "Entering calc_parameters\n"); if (!strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) || !strcmp (val, SANE_VALUE_SCAN_MODE_HALFTONE)) { s->params.depth = 1; s->bpp = 1; } else if (!strcmp (val, "Gray16")) { s->params.depth = 8; s->bpp = 4; } else if (!strcmp (val, "Gray256")) { s->params.depth = 8; s->bpp = 8; } else { DBG (ERROR_MESSAGE, "calc_parameters: Invalid mode %s\n", (char *) val); status = SANE_STATUS_INVAL; } /* in inches */ ulx = (double) s->val[OPT_TL_X].w / MM_PER_INCH; uly = (double) s->val[OPT_TL_Y].w / MM_PER_INCH; width = (double) s->val[OPT_BR_X].w / MM_PER_INCH - ulx; height = (double) s->val[OPT_BR_Y].w / MM_PER_INCH - uly; DBG (VARIABLE_CONTROL, "(inches) ulx: %f, uly: %f, width: %f, height: %f\n", ulx, uly, width, height); /* turn 'em into pixel quantities */ s->ULx = ulx * dpix; s->ULy = uly * dpiy; s->Width = width * dpix; s->Height = height * dpiy; DBG (VARIABLE_CONTROL, "(pixels) ulx: %d, uly: %d, width: %d, height: %d\n", s->ULx, s->ULy, s->Width, s->Height); /* floor width to a byte multiple */ if ((s->Width * s->bpp) % 8) { s->Width /= 8; s->Width *= 8; DBG (VARIABLE_CONTROL, "Adapting to width %d\n", s->Width); } s->params.pixels_per_line = s->Width; s->params.lines = s->Height; s->params.bytes_per_line = s->params.pixels_per_line * s->params.depth / 8; DBG (VARIABLE_CONTROL, "format=%d\n", s->params.format); DBG (VARIABLE_CONTROL, "last_frame=%d\n", s->params.last_frame); DBG (VARIABLE_CONTROL, "lines=%d\n", s->params.lines); DBG (VARIABLE_CONTROL, "depth=%d (%d)\n", s->params.depth, s->bpp); DBG (VARIABLE_CONTROL, "pixels_per_line=%d\n", s->params.pixels_per_line); DBG (VARIABLE_CONTROL, "bytes_per_line=%d\n", s->params.bytes_per_line); DBG (VARIABLE_CONTROL, "Pixels %dx%dx%d\n", s->params.pixels_per_line, s->params.lines, 1 << s->params.depth); DBG (FLOW_CONTROL, "Leaving calc_parameters\n"); return status; } static SANE_Status mode_update (SANE_Handle handle, char *val) { Abaton_Scanner *s = handle; if (!strcmp (val, SANE_VALUE_SCAN_MODE_LINEART)) { DISABLE (OPT_BRIGHTNESS); DISABLE (OPT_CONTRAST); ENABLE (OPT_THRESHOLD); DISABLE (OPT_HALFTONE_PATTERN); } else if (!strcmp (val, SANE_VALUE_SCAN_MODE_HALFTONE)) { ENABLE (OPT_BRIGHTNESS); ENABLE (OPT_CONTRAST); DISABLE (OPT_THRESHOLD); ENABLE (OPT_HALFTONE_PATTERN); } else if (!strcmp (val, "Gray16") || !strcmp (val, "Gray256")) { ENABLE (OPT_BRIGHTNESS); ENABLE (OPT_CONTRAST); DISABLE (OPT_THRESHOLD); DISABLE (OPT_HALFTONE_PATTERN); } /* End of Gray */ else { DBG (ERROR_MESSAGE, "Invalid mode %s\n", (char *) val); return SANE_STATUS_INVAL; } calc_parameters (s); return SANE_STATUS_GOOD; } /* find the longest of a list of strings */ static size_t max_string_size (const SANE_String_Const strings[]) { size_t size, max_size = 0; int i; for (i = 0; strings[i]; ++i) { size = strlen (strings[i]) + 1; if (size > max_size) max_size = size; } return max_size; } static SANE_Status init_options (Abaton_Scanner * s) { int i; memset (s->opt, 0, sizeof (s->opt)); memset (s->val, 0, sizeof (s->val)); for (i = 0; i < NUM_OPTIONS; ++i) { s->opt[i].size = sizeof (SANE_Word); s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* "Mode" group: */ s->opt[OPT_MODE_GROUP].title = "Scan Mode"; s->opt[OPT_MODE_GROUP].desc = ""; s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MODE_GROUP].cap = 0; s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; mode_list[0]=SANE_VALUE_SCAN_MODE_LINEART; switch (s->hw->ScannerModel) { case ABATON_300GS: mode_list[1]=SANE_VALUE_SCAN_MODE_HALFTONE; mode_list[2]="Gray16"; mode_list[3]="Gray256"; mode_list[4]=NULL; break; case ABATON_300S: default: mode_list[1]=NULL; break; } /* scan mode */ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; 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].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_MODE].size = max_string_size (mode_list); s->opt[OPT_MODE].constraint.string_list = mode_list; s->val[OPT_MODE].s = strdup (mode_list[0]); /* resolution - horizontal */ s->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_X_RESOLUTION; s->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_X_RESOLUTION; s->opt[OPT_X_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_X_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_X_RESOLUTION].constraint.range = &dpi_range; s->val[OPT_X_RESOLUTION].w = 150; /* resolution - vertical */ s->opt[OPT_Y_RESOLUTION].name = SANE_NAME_SCAN_Y_RESOLUTION; s->opt[OPT_Y_RESOLUTION].title = SANE_TITLE_SCAN_Y_RESOLUTION; s->opt[OPT_Y_RESOLUTION].desc = SANE_DESC_SCAN_Y_RESOLUTION; s->opt[OPT_Y_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_Y_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_Y_RESOLUTION].constraint.range = &dpi_range; s->val[OPT_Y_RESOLUTION].w = 150; /* constrain resolutions */ s->opt[OPT_RESOLUTION_BIND].name = SANE_NAME_RESOLUTION_BIND; s->opt[OPT_RESOLUTION_BIND].title = SANE_TITLE_RESOLUTION_BIND; s->opt[OPT_RESOLUTION_BIND].desc = SANE_DESC_RESOLUTION_BIND; s->opt[OPT_RESOLUTION_BIND].type = SANE_TYPE_BOOL; s->opt[OPT_RESOLUTION_BIND].unit = SANE_UNIT_NONE; s->opt[OPT_RESOLUTION_BIND].constraint_type = SANE_CONSTRAINT_NONE; /* until I fix it */ s->val[OPT_RESOLUTION_BIND].w = SANE_FALSE; /* preview mode */ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; s->val[OPT_PREVIEW].w = SANE_FALSE; /* halftone pattern */ s->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN; s->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN; s->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN; s->opt[OPT_HALFTONE_PATTERN].size = max_string_size (halftone_pattern_list); s->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING; s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; s->opt[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_HALFTONE_PATTERN].constraint.string_list = halftone_pattern_list; s->val[OPT_HALFTONE_PATTERN].s = strdup (halftone_pattern_list[0]); /* "Geometry" group: */ s->opt[OPT_GEOMETRY_GROUP].title = "Geometry"; s->opt[OPT_GEOMETRY_GROUP].desc = ""; s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* top-left x */ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->opt[OPT_TL_X].type = SANE_TYPE_INT; s->opt[OPT_TL_X].unit = SANE_UNIT_MM; s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_X].constraint.range = &x_range; s->val[OPT_TL_X].w = 0; /* top-left y */ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->opt[OPT_TL_Y].type = SANE_TYPE_INT; s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_Y].constraint.range = &y_range; s->val[OPT_TL_Y].w = 0; /* bottom-right x */ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->opt[OPT_BR_X].type = SANE_TYPE_INT; s->opt[OPT_BR_X].unit = SANE_UNIT_MM; s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_X].constraint.range = &x_range; s->val[OPT_BR_X].w = x_range.max; /* bottom-right y */ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->opt[OPT_BR_Y].type = SANE_TYPE_INT; s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_Y].constraint.range = &y_range; s->val[OPT_BR_Y].w = y_range.max; /* "Enhancement" group: */ s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement"; s->opt[OPT_ENHANCEMENT_GROUP].desc = ""; s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_ENHANCEMENT_GROUP].cap = 0; s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* brightness */ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT; s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE; s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BRIGHTNESS].constraint.range = &enhance_range; s->val[OPT_BRIGHTNESS].w = 150; /* contrast */ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST; s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST; s->opt[OPT_CONTRAST].type = SANE_TYPE_INT; s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE; s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CONTRAST].constraint.range = &enhance_range; s->val[OPT_CONTRAST].w = 150; /* 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_INT; s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE; s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_THRESHOLD].constraint.range = &enhance_range; s->val[OPT_THRESHOLD].w = 150; /* negative */ s->opt[OPT_NEGATIVE].name = SANE_NAME_NEGATIVE; s->opt[OPT_NEGATIVE].title = SANE_TITLE_NEGATIVE; s->opt[OPT_NEGATIVE].desc = SANE_DESC_NEGATIVE; s->opt[OPT_NEGATIVE].type = SANE_TYPE_BOOL; s->opt[OPT_NEGATIVE].unit = SANE_UNIT_NONE; s->opt[OPT_NEGATIVE].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_NEGATIVE].w = SANE_FALSE; /* mirror-image */ s->opt[OPT_MIRROR].name = "mirror"; s->opt[OPT_MIRROR].title = "Mirror Image"; s->opt[OPT_MIRROR].desc = "Scan in mirror-image"; s->opt[OPT_MIRROR].type = SANE_TYPE_BOOL; s->opt[OPT_MIRROR].unit = SANE_UNIT_NONE; s->opt[OPT_MIRROR].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_MIRROR].w = SANE_FALSE; return SANE_STATUS_GOOD; } SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { char dev_name[PATH_MAX]; size_t len; FILE *fp; (void) authorize; /* silence gcc */ DBG_INIT (); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); fp = sanei_config_open (ABATON_CONFIG_FILE); if (!fp) { /* default to /dev/scanner instead of insisting on config file */ attach ("/dev/scanner", 0, SANE_FALSE); return SANE_STATUS_GOOD; } while (sanei_config_read (dev_name, sizeof (dev_name), fp)) { if (dev_name[0] == '#') /* ignore line comments */ continue; len = strlen (dev_name); if (!len) continue; /* ignore empty lines */ if (strncmp (dev_name, "option", 6) == 0 && isspace (dev_name[6])) { const char *str = dev_name + 7; while (isspace (*str)) ++str; continue; } sanei_config_attach_matching_devices (dev_name, attach_one); } fclose (fp); return SANE_STATUS_GOOD; } void sane_exit (void) { Abaton_Device *dev, *next; for (dev = first_dev; dev; dev = next) { next = dev->next; free ((void *) dev->sane.name); free ((void *) dev->sane.model); free (dev); } if (devlist) free (devlist); } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { Abaton_Device *dev; int i; (void) local_only; /* silence gcc */ if (devlist) free (devlist); devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); if (!devlist) return SANE_STATUS_NO_MEM; i = 0; for (dev = first_dev; i < num_devices; dev = dev->next) devlist[i++] = &dev->sane; devlist[i++] = 0; *device_list = devlist; return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { Abaton_Device *dev; SANE_Status status; Abaton_Scanner *s; if (devicename[0]) { for (dev = first_dev; dev; dev = dev->next) if (strcmp (dev->sane.name, devicename) == 0) break; if (!dev) { status = attach (devicename, &dev, SANE_TRUE); if (status != SANE_STATUS_GOOD) return status; } } else /* empty devicname -> use first device */ dev = first_dev; if (!dev) return SANE_STATUS_INVAL; s = malloc (sizeof (*s)); if (!s) return SANE_STATUS_NO_MEM; memset (s, 0, sizeof (*s)); s->fd = -1; s->hw = dev; init_options (s); /* set up some universal parameters */ s->params.last_frame = SANE_TRUE; s->params.format = SANE_FRAME_GRAY; /* insert newly opened handle into list of open handles: */ s->next = first_handle; first_handle = s; *handle = s; return SANE_STATUS_GOOD; } void sane_close (SANE_Handle handle) { Abaton_Scanner *prev, *s; /* remove handle from list of open handles: */ prev = 0; for (s = first_handle; s; s = s->next) { if (s == (Abaton_Scanner *) handle) break; prev = s; } if (!s) { DBG (ERROR_MESSAGE, "close: invalid handle %p\n", handle); return; /* oops, not a handle we know about */ } if (prev) prev->next = s->next; else first_handle = s->next; free (handle); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { Abaton_Scanner *s = handle; if ((unsigned) option >= NUM_OPTIONS) return NULL; return s->opt + option; } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { Abaton_Scanner *s = handle; SANE_Status status; SANE_Word cap; if (option < 0 || option >= NUM_OPTIONS) return SANE_STATUS_INVAL; if (info != NULL) *info = 0; if (s->scanning) return SANE_STATUS_DEVICE_BUSY; cap = s->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) return SANE_STATUS_INVAL; if (action == SANE_ACTION_GET_VALUE) { switch (option) { /* word options: */ case OPT_NUM_OPTS: case OPT_X_RESOLUTION: case OPT_Y_RESOLUTION: case OPT_RESOLUTION_BIND: case OPT_PREVIEW: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_BRIGHTNESS: case OPT_CONTRAST: case OPT_THRESHOLD: case OPT_NEGATIVE: case OPT_MIRROR: *(SANE_Word *) val = s->val[option].w; return SANE_STATUS_GOOD; /* string options */ case OPT_MODE: case OPT_HALFTONE_PATTERN: status = sanei_constrain_value (s->opt + option, s->val[option].s, info); strcpy (val, s->val[option].s); return SANE_STATUS_GOOD; } } else if (action == SANE_ACTION_SET_VALUE) { if (!SANE_OPTION_IS_SETTABLE (cap)) return SANE_STATUS_INVAL; status = sanei_constrain_value (s->opt + option, val, info); if (status != SANE_STATUS_GOOD) return status; switch (option) { /* resolution should be uniform for previews, or when the user says so. */ case OPT_PREVIEW: s->val[option].w = *(SANE_Word *) val; if (*(SANE_Word *) val) { s->val[OPT_Y_RESOLUTION].w = s->val[OPT_X_RESOLUTION].w; if (info) *info |= SANE_INFO_RELOAD_OPTIONS; } /* always recalculate! */ calc_parameters (s); if (info) *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case OPT_RESOLUTION_BIND: s->val[option].w = *(SANE_Word *) val; if (*(SANE_Word *) val) { s->val[OPT_Y_RESOLUTION].w = s->val[OPT_X_RESOLUTION].w; calc_parameters (s); if (info) *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; } return SANE_STATUS_GOOD; case OPT_X_RESOLUTION: if (s->val[OPT_PREVIEW].w || s->val[OPT_RESOLUTION_BIND].w) { s->val[OPT_Y_RESOLUTION].w = *(SANE_Word *)val; if (info) *info |= SANE_INFO_RELOAD_OPTIONS; } s->val[option].w = *(SANE_Word *) val; calc_parameters (s); if (info) *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case OPT_Y_RESOLUTION: if (s->val[OPT_PREVIEW].w || s->val[OPT_RESOLUTION_BIND].w) { s->val[OPT_X_RESOLUTION].w = *(SANE_Word *)val; if (info) *info |= SANE_INFO_RELOAD_OPTIONS; } s->val[option].w = *(SANE_Word *) val; calc_parameters (s); if (info) *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; /* these ones don't have crazy side effects */ case OPT_TL_X: case OPT_TL_Y: case OPT_BR_Y: s->val[option].w = *(SANE_Word *) val; calc_parameters (s); if (info) *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; /* this one is somewhat imprecise */ case OPT_BR_X: s->val[option].w = *(SANE_Word *) val; calc_parameters (s); if (info) *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_INEXACT; return SANE_STATUS_GOOD; /* no side-effects whatsoever */ case OPT_BRIGHTNESS: case OPT_CONTRAST: case OPT_THRESHOLD: case OPT_NEGATIVE: case OPT_MIRROR: s->val[option].w = *(SANE_Word *) val; return SANE_STATUS_GOOD; /* string options */ case OPT_HALFTONE_PATTERN: if (info) *info |= SANE_INFO_RELOAD_OPTIONS; if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); return SANE_STATUS_GOOD; case OPT_MODE: status = mode_update (s, val); if (status != SANE_STATUS_GOOD) return status; if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; } /* End of switch */ } /* End of SET_VALUE */ return SANE_STATUS_INVAL; } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { Abaton_Scanner *s = handle; DBG (FLOW_CONTROL, "Entering sane_get_parameters\n"); calc_parameters (s); if (params) *params = s->params; return SANE_STATUS_GOOD; } SANE_Status sane_start (SANE_Handle handle) { Abaton_Scanner *s = handle; SANE_Status status; /* First make sure we have a current parameter set. Some of the parameters will be overwritten below, but that's OK. */ calc_parameters (s); if (s->fd < 0) { /* this is the first (and maybe only) pass... */ status = sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, 0); if (status != SANE_STATUS_GOOD) { DBG (ERROR_MESSAGE, "open: open of %s failed: %s\n", s->hw->sane.name, sane_strstatus (status)); return status; } } status = wait_ready (s->fd); if (status != SANE_STATUS_GOOD) { DBG (ERROR_MESSAGE, "open: wait_ready() failed: %s\n", sane_strstatus (status)); goto stop_scanner_and_return; } status = request_sense (s); if (status != SANE_STATUS_GOOD) { DBG (ERROR_MESSAGE, "sane_start: request_sense revealed error: %s\n", sane_strstatus (status)); goto stop_scanner_and_return; } status = set_window (s); if (status != SANE_STATUS_GOOD) { DBG (ERROR_MESSAGE, "open: set scan area command failed: %s\n", sane_strstatus (status)); goto stop_scanner_and_return; } s->scanning = SANE_TRUE; s->AbortedByUser = SANE_FALSE; status = start_scan (s); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; return SANE_STATUS_GOOD; stop_scanner_and_return: s->scanning = SANE_FALSE; s->AbortedByUser = SANE_FALSE; return status; } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { Abaton_Scanner *s = handle; SANE_Status status; uint8_t get_data_status[10]; uint8_t read[10]; uint8_t result[12]; size_t size; SANE_Int data_av = 0; SANE_Int data_length = 0; SANE_Int offset = 0; SANE_Int rread = 0; SANE_Bool Pseudo8bit = SANE_FALSE; *len = 0; /* don't let bogus read requests reach the scanner */ /* this is a sub-optimal way of doing this, I'm sure */ if (!s->scanning) return SANE_STATUS_EOF; if (!strcmp (s->val[OPT_MODE].s, "Gray16")) Pseudo8bit = SANE_TRUE; memset (get_data_status, 0, sizeof (get_data_status)); get_data_status[0] = GET_DATA_STATUS; /* This means "block" for Apple scanners, it seems to be the same for Abaton. The scanner will do non-blocking I/O, but I don't want to go there right now. */ get_data_status[1] = 1; STORE8 (get_data_status + 8, sizeof (result)); memset (read, 0, sizeof (read)); read[0] = READ_10; do { size = sizeof (result); /* this isn't necessary */ /* memset (result, 0, size); */ status = sanei_scsi_cmd (s->fd, get_data_status, sizeof (get_data_status), result, &size); if (status != SANE_STATUS_GOOD) return status; if (!size) { DBG (ERROR_MESSAGE, "sane_read: cannot get_data_status.\n"); return SANE_STATUS_IO_ERROR; } /* this is not an accurate name, but oh well... */ data_length = GET24 (result); data_av = GET24 (result + 9); /* don't check result[3] here, because that screws things up somewhat */ if (data_length) { DBG (IO_MESSAGE, "sane_read: (status) Available in scanner buffer %u.\n", data_av); if (Pseudo8bit) { if ((data_av * 2) + offset > max_len) rread = (max_len - offset) / 2; else rread = data_av; } else if (data_av + offset > max_len) { rread = max_len - offset; } else { rread = data_av; } DBG (IO_MESSAGE, "sane_read: (action) Actual read request for %u bytes.\n", rread); size = rread; STORE24 (read + 6, rread); status = sanei_scsi_cmd (s->fd, read, sizeof (read), buf + offset, &size); if (Pseudo8bit) { SANE_Int byte; SANE_Int pos = offset + (rread << 1) - 1; SANE_Byte B; for (byte = offset + rread - 1; byte >= offset; byte--) { B = buf[byte]; /* don't invert these! */ buf[pos--] = B << 4; /* low (right) nibble */ buf[pos--] = B & 0xF0; /* high (left) nibble */ } /* putting an end to bitop abuse here */ offset += size * 2; } else offset += size; DBG (IO_MESSAGE, "sane_read: Buffer %u of %u full %g%%\n", offset, max_len, (double) (offset * 100. / max_len)); } } while (offset < max_len && data_length != 0 && !s->AbortedByUser); if (s->AbortedByUser) { s->scanning = SANE_FALSE; if (status != SANE_STATUS_GOOD) { DBG (ERROR_MESSAGE, "sane_read: request_sense revealed error: %s\n", sane_strstatus (status)); return status; } status = sanei_scsi_cmd (s->fd, test_unit_ready, sizeof (test_unit_ready), 0, 0); if (status != SANE_STATUS_GOOD && status != SANE_STATUS_INVAL) return status; return SANE_STATUS_CANCELLED; } if (!data_length) { s->scanning = SANE_FALSE; DBG (IO_MESSAGE, "sane_read: (status) No more data..."); if (!offset) { /* this shouldn't happen */ *len = 0; DBG (IO_MESSAGE, "EOF\n"); return SANE_STATUS_EOF; } else { *len = offset; DBG (IO_MESSAGE, "GOOD\n"); return SANE_STATUS_GOOD; } } DBG (FLOW_CONTROL, "sane_read: Normal Exiting, Aborted=%u, data_length=%u\n", s->AbortedByUser, data_av); *len = offset; return SANE_STATUS_GOOD; } void sane_cancel (SANE_Handle handle) { Abaton_Scanner *s = handle; if (s->scanning) { if (s->AbortedByUser) { DBG (FLOW_CONTROL, "sane_cancel: Already Aborted. Please Wait...\n"); } else { s->scanning = SANE_FALSE; s->AbortedByUser = SANE_TRUE; DBG (FLOW_CONTROL, "sane_cancel: Signal Caught! Aborting...\n"); } } else { if (s->AbortedByUser) { DBG (FLOW_CONTROL, "sane_cancel: Scan has not been initiated yet." "we probably received a signal while writing data.\n"); s->AbortedByUser = SANE_FALSE; } else { DBG (FLOW_CONTROL, "sane_cancel: Scan has not been initiated " "yet (or it's over).\n"); } } return; } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { (void) handle; /* silence gcc */ (void) non_blocking; /* silence gcc */ DBG (FLOW_CONTROL, "sane_set_io_mode: Don't call me please. " "Unimplemented function\n"); return SANE_STATUS_UNSUPPORTED; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { (void) handle; /* silence gcc */ (void) fd; /* silence gcc */ DBG (FLOW_CONTROL, "sane_get_select_fd: Don't call me please. " "Unimplemented function\n"); return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/abaton.conf.in000066400000000000000000000000311456256263500174040ustar00rootroot00000000000000scsi ABATON /dev/scanner backends-1.3.0/backend/abaton.h000066400000000000000000000067361456256263500163230ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1998 David Huggins-Daines, heavily based on the Apple scanner driver (since Abaton scanners are very similar to old Apple scanners), which is (C) 1998 Milon Firikis, which is, in turn, based on the Mustek driver, (C) 1996-7 David Mosberger-Tang. 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, see . 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 abaton_h #define abaton_h #include enum Abaton_Modes { ABATON_MODE_LINEART=0, ABATON_MODE_HALFTONE, ABATON_MODE_GRAY }; enum Abaton_Option { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_MODE, OPT_X_RESOLUTION, OPT_Y_RESOLUTION, OPT_RESOLUTION_BIND, OPT_PREVIEW, OPT_HALFTONE_PATTERN, OPT_GEOMETRY_GROUP, OPT_TL_X, /* top-left x */ OPT_TL_Y, /* top-left y */ OPT_BR_X, /* bottom-right x */ OPT_BR_Y, /* bottom-right y */ OPT_ENHANCEMENT_GROUP, OPT_BRIGHTNESS, OPT_CONTRAST, OPT_THRESHOLD, OPT_NEGATIVE, OPT_MIRROR, /* must come last: */ NUM_OPTIONS }; enum ScannerModels { ABATON_300GS, ABATON_300S }; typedef struct Abaton_Device { struct Abaton_Device *next; SANE_Int ScannerModel; SANE_Device sane; SANE_Range dpi_range; unsigned flags; } Abaton_Device; typedef struct Abaton_Scanner { /* all the state needed to define a scan request: */ struct Abaton_Scanner *next; SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; SANE_Bool scanning; SANE_Bool AbortedByUser; SANE_Parameters params; /* The actual bpp, before "Pseudo-8-bit" fiddling */ SANE_Int bpp; /* window, in pixels */ SANE_Int ULx; SANE_Int ULy; SANE_Int Width; SANE_Int Height; int fd; /* SCSI filedescriptor */ /* scanner dependent/low-level state: */ Abaton_Device *hw; } Abaton_Scanner; #endif /* abaton_h */ backends-1.3.0/backend/agfafocus.c000066400000000000000000001545451456256263500170120ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. This file (C) 1997 Ingo Schneider (C) 1998 Karl Anders Øygard This file is part of the SANE package. SANE 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. SANE 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 sane; see the file COPYING. If not, see . This file implements a SANE backend for AGFA Focus flatbed scanners. */ #include "../include/sane/config.h" #include #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/sanei_config.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_scsi.h" #include "../include/sane/sanei_thread.h" #define BACKEND_NAME agfafocus #include "../include/sane/sanei_backend.h" #include "agfafocus.h" #ifndef PATH_MAX # define PATH_MAX 1024 #endif #undef Byte #define Byte SANE_Byte static const SANE_Device **devlist = 0; static int num_devices; static AgfaFocus_Device *agfafocus_devices; static const SANE_String_Const focus_mode_list[] = { "Lineart", "Gray (6 bit)", 0 }; static const SANE_String_Const focusii_mode_list[] = { "Lineart", "Gray (6 bit)", "Gray (8 bit)", 0 }; static const SANE_String_Const focuscolor_mode_list[] = { "Lineart", "Gray (6 bit)", "Gray (8 bit)", "Color (18 bit)", "Color (24 bit)", 0 }; static const SANE_String_Const halftone_list[] = { "None", "Dispersed dot 4x4", "Round (Clustered dot 4x4)", "Diamond (Clustered dot 4x4)", 0 }; static const SANE_String_Const halftone_upload_list[] = { "None", "Dispersed dot 4x4", "Round (Clustered dot 4x4)", "Diamond (Clustered dot 4x4)", 0 }; static const SANE_String_Const source_list[] = { "Opaque/Normal", "Transparency", 0 }; static const SANE_String_Const quality_list[] = { "Low", "Normal", "High", 0 }; static size_t max_string_size (const SANE_String_Const strings[]) { size_t size, max_size = 0; int i; DBG (11, ">> max_string_size\n"); for (i = 0; strings[i]; ++i) { size = strlen (strings[i]) + 1; if (size > max_size) max_size = size; } DBG (11, "<< max_string_size\n"); return max_size; } /* sets loc_s bytes long value at offset loc in scsi command to value size */ static void set_size (Byte * loc, int loc_s, size_t size) { int i; for (i = 0; i < loc_s; i++) { loc[loc_s - i - 1] = (size >> (i * 8)) & 0xff; } } /* gets loc_s bytes long value from loc in scsi command */ static int get_size (Byte * loc, int loc_s) { int i; int j = 0; for (i = 0; i < loc_s; i++) { j = (j << 8) + (loc[i] & 0xff); } return j; } static long reserve_unit (int fd) { struct { /* Command */ Byte cmd; Byte lun; Byte res[2]; Byte tr_len; Byte ctrl; } scsi_reserve; memset (&scsi_reserve, 0, sizeof (scsi_reserve)); scsi_reserve.cmd = 0x16; /* RELEASE */ DBG (3, "reserve_unit()\n"); return sanei_scsi_cmd (fd, &scsi_reserve, sizeof (scsi_reserve), 0, 0); } static long release_unit (int fd) { struct { /* Command */ Byte cmd; Byte lun; Byte res[2]; Byte tr_len; Byte ctrl; } scsi_release; memset (&scsi_release, 0, sizeof (scsi_release)); scsi_release.cmd = 0x17; /* RELEASE */ DBG (3, "release_unit()\n"); return sanei_scsi_cmd (fd, &scsi_release, sizeof (scsi_release), 0, 0); } static SANE_Status test_ready (int fd) { SANE_Status status; int try; struct { /* Command */ Byte cmd; Byte lun; Byte res[2]; Byte tr_len; Byte ctrl; } scsi_test_ready; memset (&scsi_test_ready, 0, sizeof (scsi_test_ready)); scsi_test_ready.cmd = 0x00; /* TEST UNIT READY */ for (try = 0; try < 1000; ++try) { DBG (3, "test_ready: sending TEST_UNIT_READY\n"); status = sanei_scsi_cmd (fd, &scsi_test_ready, sizeof (scsi_test_ready), 0, 0); switch (status) { case SANE_STATUS_DEVICE_BUSY: usleep (100000); /* retry after 100ms */ break; case SANE_STATUS_GOOD: return status; default: DBG (1, "test_ready: test unit ready failed (%s)\n", sane_strstatus (status)); return status; } } DBG (1, "test_ready: timed out after %d attempts\n", try); return SANE_STATUS_IO_ERROR; } static SANE_Status sense_handler (int scsi_fd, u_char *result, void *arg) { (void) scsi_fd; /* silence gcc */ (void) arg; /* silence gcc */ if (result[0]) { DBG (0, "sense_handler() : sense code = %02x\n", result[0]); return SANE_STATUS_IO_ERROR; } else { return SANE_STATUS_GOOD; } } static SANE_Status stop_scan (int fd) { (void) fd; /* silence gcc */ /* XXX don't know how to stop the scanner. To be tested ! */ #if 0 const Byte scsi_rewind[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }; DBG (1, "Trying to stop scanner...\n"); return sanei_scsi_cmd (fd, scsi_rewind, sizeof (scsi_rewind), 0, 0); #else return SANE_STATUS_GOOD; #endif } static SANE_Status start_scan (int fd, SANE_Bool cont) { struct { /* Command */ Byte cmd; Byte lun; Byte res[2]; Byte tr_len; Byte ctrl; /* Data */ Byte wid; } scsi_start_scan; memset (&scsi_start_scan, 0, sizeof (scsi_start_scan)); scsi_start_scan.cmd = 0x1b; /* SCAN */ scsi_start_scan.tr_len = 1; scsi_start_scan.wid = 0; scsi_start_scan.ctrl = (cont == SANE_TRUE) ? 0x80 : 0x00; DBG (1, "Starting scanner ...\n"); return sanei_scsi_cmd (fd, &scsi_start_scan, sizeof (scsi_start_scan), 0, 0); } static void wait_ready (int fd) { struct { Byte bytes[2]; /* Total # of bytes */ Byte scan[2]; /* ms to complete - driver sleep time */ } result; size_t size = sizeof (result); SANE_Status status; struct { Byte cmd; Byte lun; Byte data_type; Byte re1[3]; Byte tr_len[3]; Byte ctrl; } cmd; memset (&cmd, 0, sizeof (cmd)); cmd.cmd = 0x28; /* READ */ cmd.data_type = 0x80; /* get scan time */ set_size (cmd.tr_len, 3, sizeof (result)); while (1) { status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), &result, &size); if (status != SANE_STATUS_GOOD || size != sizeof (result)) { /* Command failed, the assembler code of the windows scan library ignores this condition, and so do I */ break; } else { /* left is the amount of seconds left till the scanner is ready * 100 */ int left = get_size (result.scan, 2); DBG (1, "wait_ready() : %d left...\n", left); if (!left) break; /* We delay only for half the given time */ else if (left < 200) usleep (left * 5000); else sleep (left / 200); } } return; } static SANE_Status get_read_sizes (int fd, int *lines_available, int *bpl, int *total_lines) { struct { Byte reserved1[8]; Byte line_width[2]; Byte total_lines[2]; Byte cur_line[2]; Byte lines_this_block[2]; Byte reserved[8]; } read_sizes; const Byte scsi_read[] = { 0x28, 0x00, /* opcode, lun */ 0x81, /* data type 81 == read time left */ 0x00, 0x00, 0x00, /* reserved */ 0x00, 0x00, sizeof (read_sizes), /* transfer length */ 0x00, /* control byte */ }; size_t size = sizeof (read_sizes); SANE_Status status; status = sanei_scsi_cmd (fd, scsi_read, sizeof (scsi_read), &read_sizes, &size); if (status != SANE_STATUS_GOOD || size != sizeof (read_sizes)) { /* Command failed */ return SANE_STATUS_IO_ERROR; } else { *lines_available = get_size (read_sizes.lines_this_block, 2); *bpl = get_size (read_sizes.cur_line, 2); if (total_lines) *total_lines = get_size (read_sizes.total_lines, 2); } DBG (1, "get_read_sizes() : %d of %d, %d\n", *lines_available, total_lines ? *total_lines : -1, *bpl); return SANE_STATUS_GOOD; } static SANE_Status set_window (AgfaFocus_Scanner * s) /* This function sets and sends the window for scanning */ { double pixels_per_mm = (double) s->val[OPT_RESOLUTION].w / MM_PER_INCH; SANE_Bool auto_bright = s->val[OPT_AUTO_BRIGHTNESS].b; SANE_Bool auto_contr = s->val[OPT_AUTO_CONTRAST].b; /* ranges down 255 (dark) down to 1(bright) */ int brightness = auto_bright ? 0 : (SANE_UNFIX (s->val[OPT_BRIGHTNESS].w) * -1.27 + 128.5); /* ranges from 1 (little contrast) up to 255 (much contrast) */ int contrast = auto_contr ? 0 : (SANE_UNFIX (s->val[OPT_CONTRAST].w) * 1.27 + 128.5); int width; /* ranges from 40 (dark) down to 0 (bright) */ int bright_adjust = (SANE_UNFIX (s->val[OPT_BRIGHTNESS].w) * -20.0) / 100.0 + 20.0; /* ranges from 20 (little contrast) down to -20 = 235 (much contrast) */ int contr_adjust = (SANE_UNFIX (s->val[OPT_CONTRAST].w) * -20.0) / 100.0; /* Warning ! The following structure SEEMS to be a valid SCSI-2 SET_WINDOW command. But e.g. the limits for the window are only 2 Bytes instead of 4. The scanner was built at about 1990, so SCSI-2 wasn't available for development... */ struct { Byte cmd; Byte lun; Byte re1[4]; Byte tr_len[3]; Byte ctrl; Byte re2[6]; Byte wd_len[2]; struct { Byte wid; /* Window ID */ Byte autobit; /* Window creation */ Byte x_axis_res[2]; /* X resolution */ Byte y_axis_res[2]; /* X resolution */ Byte x_axis_ul[2]; /* X upper left */ Byte y_axis_ul[2]; /* Y upper left */ Byte wwidth[2]; /* Width */ Byte wlength[2]; /* Length */ Byte contrast; /* Contrast */ Byte dummy1; Byte intensity; /* Intensity */ Byte image_comp; /* Image composition (0, 2, 5) */ Byte bpp; /* Bits per pixel */ Byte tonecurve; /* Tone curve (0 - 8) */ Byte ht_pattern; /* Halftone pattern */ Byte paddingtype; /* Padding type */ Byte bitordering[2]; /* Bit ordering (0 = left to right) */ Byte comprtype; /* Compression type */ Byte comprarg; /* Compression argument */ Byte dummy2[6]; Byte edge; /* Sharpening (0 - 7) */ Byte dummy3; Byte bright_adjust; /* */ Byte contr_adjust; /* */ Byte imagewidthtruncation; /* */ Byte dummy4; Byte quality_type; /* 0 normal, 1 high, 255 low */ Byte red_att; Byte green_att; Byte blue_att; Byte dummy5[5]; Byte color_planes; Byte orig_type; Byte fixturetype; Byte exposure[2]; Byte defocus[2]; Byte dummy6[4]; Byte descreen_factor; Byte packing_word_length; Byte packing_number_of_pixels; Byte packing_color_mode; Byte strokenab; Byte rotatenab; Byte autostrokenab; Byte dummy7; } wd; } cmd; memset (&cmd, 0, sizeof (cmd)); cmd.cmd = 0x24; /* SET WINDOW PARAMETERS */ switch (s->hw->type) { case AGFAGRAY64: case AGFALINEART: case AGFAGRAY256: set_size (cmd.tr_len, 3, 36 + 8); set_size (cmd.wd_len, 2, 36); break; case AGFACOLOR: set_size (cmd.tr_len, 3, 65 + 8); set_size (cmd.wd_len, 2, 65); break; } /* Resolution. Original comment in German: Aufloesung */ set_size (cmd.wd.x_axis_res, 2, s->val[OPT_RESOLUTION].w); set_size (cmd.wd.y_axis_res, 2, s->val[OPT_RESOLUTION].w); /* Scan window position/size. Original comment in German: Fensterposition / Groesse */ set_size (cmd.wd.x_axis_ul, 2, SANE_UNFIX (s->val[OPT_TL_X].w) * pixels_per_mm + 0.5); set_size (cmd.wd.y_axis_ul, 2, SANE_UNFIX (s->val[OPT_TL_Y].w) * pixels_per_mm + 0.5); width = (SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w) * pixels_per_mm) + 0.5; if (s->bpp == 1 && width % 8) width += 8 - width % 8; set_size (cmd.wd.wwidth, 2, width); set_size (cmd.wd.wlength, 2, SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w) * pixels_per_mm + 0.5); cmd.wd.bpp = s->bpp; if (s->mode == COLOR18BIT || s->mode == COLOR24BIT) { cmd.wd.paddingtype = 3; cmd.wd.ht_pattern = 3; cmd.wd.red_att = s->r_att; cmd.wd.blue_att = s->g_att; cmd.wd.green_att = s->b_att; cmd.wd.color_planes = 0x0e; set_size (cmd.wd.exposure, 2, s->exposure); cmd.wd.packing_word_length = 1; cmd.wd.packing_number_of_pixels = 1; cmd.wd.packing_color_mode = 2; if (s->bpp == 6) cmd.wd.edge = s->edge; DBG (3, "Setting parameters: imc %d, bpp %d, res %d, exp %d, attenuation [%d, %d, %d], edge %d\n", s->image_composition, s->bpp, s->val[OPT_RESOLUTION].w, s->exposure, cmd.wd.red_att, cmd.wd.blue_att, cmd.wd.green_att, s->edge); } else { if (s->bpp == 1) cmd.wd.ht_pattern = s->halftone; else cmd.wd.ht_pattern = 3; cmd.wd.intensity = brightness; cmd.wd.contrast = contrast; cmd.wd.contr_adjust = contr_adjust; cmd.wd.bright_adjust = bright_adjust; cmd.wd.tonecurve = s->tonecurve; cmd.wd.paddingtype = 3; cmd.wd.edge = s->edge; if (s->lin_log) cmd.wd.dummy3 = 0x02; DBG (3, "Setting parameters: imc %d, bpp %d, res %d, bri %d, con %d, bad %d, cad %d, ht %d, edge %d\n", s->image_composition, s->bpp, s->val[OPT_RESOLUTION].w, brightness, contrast, bright_adjust, contr_adjust, s->halftone, s->edge); } cmd.wd.image_comp = s->image_composition; cmd.wd.quality_type = s->quality; cmd.wd.orig_type = s->original; return sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), 0, 0); } /* Tell scanner to scan more data. */ static SANE_Status request_more_data (AgfaFocus_Scanner * s) { SANE_Status status; int lines_available; int bytes_per_line; status = start_scan (s->fd, SANE_TRUE); if (status != SANE_STATUS_GOOD) return status; if (!s->hw->disconnect) wait_ready (s->fd); status = get_read_sizes (s->fd, &lines_available, &bytes_per_line, 0); if (!lines_available) return SANE_STATUS_INVAL; s->lines_available = lines_available; return SANE_STATUS_GOOD; } static SANE_Status upload_dither_matrix (AgfaFocus_Scanner * s, int rows, int cols, int *dither_matrix) { struct { Byte cmd; Byte lun; Byte data_type; Byte re1[3]; Byte tr_len[3]; Byte ctrl; struct { Byte nrrows[2]; Byte nrcols[2]; struct { Byte data[2]; } element[256]; } wd; } cmd; SANE_Status status; int i; memset (&cmd, 0, sizeof (cmd)); cmd.cmd = 0x2a; /* WRITE */ cmd.data_type = 0x81; /* upload dither matrix */ set_size (cmd.tr_len, 3, 4 + (2 * rows * cols)); set_size (cmd.wd.nrrows, 2, rows); set_size (cmd.wd.nrcols, 2, cols); for (i = 0; i < cols * rows; ++i) set_size (cmd.wd.element[i].data, 2, dither_matrix[i]); status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), 0, 0); if (status != SANE_STATUS_GOOD) /* Command failed */ return SANE_STATUS_IO_ERROR; DBG (1, "upload_dither_matrix(): uploaded dither matrix: %d, %d\n", rows, cols); return SANE_STATUS_GOOD; } #if 0 static SANE_Status upload_tonecurve (AgfaFocus_Scanner * s, int color_type, int input, int output, int dither_matrix[256]) { struct { Byte cmd; Byte lun; Byte re1[4]; Byte tr_len[3]; Byte ctrl; Byte re2[6]; Byte wd_len[2]; struct { Byte color_type[2]; Byte nrinput[2]; Byte nroutput[2]; struct { Byte data[2]; } outputval[256]; } wd; } cmd; SANE_Status status; int i, j; memset (&cmd, 0, sizeof (cmd)); cmd.cmd = 0x80; set_size (cmd.tr_len, 3, sizeof (cmd.wd)); set_size (cmd.wd.nrrows, 2, rows); set_size (cmd.wd.nrrows, 2, cols); for (i = 0; i < cols; ++i) for (j = 0; j < rows; ++j) set_size (cmd.wd.element[j + i * rows].data, 2, dither_matrix[j + i * rows]); status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), 0, 0); if (status != SANE_STATUS_GOOD) /* * Command failed * */ return SANE_STATUS_IO_ERROR; DBG (1, "upload_dither_matrix(): uploaded dither matrix\n"); return SANE_STATUS_GOOD; } #endif /* May only be called when there is at least one row of data to be read. Original comment in German: Darf nur aufgerufen werden, wenn wirklich noch Zeilen zu scannen/lesen sind ! */ static SANE_Status read_data (AgfaFocus_Scanner * s, SANE_Byte *buf, int lines, int bpl) { struct { Byte cmd; Byte lun; Byte re1[4]; Byte tr_len[3]; Byte ctrl; } cmd; SANE_Status status; size_t size; unsigned int i; memset (&cmd, 0, sizeof (cmd)); cmd.cmd = 0x28; /* READ */ set_size (cmd.tr_len, 3, lines); size = lines * bpl; status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), buf, &size); if (status != SANE_STATUS_GOOD) { DBG (1, "sanei_scsi_cmd() = %d\n", status); return SANE_STATUS_IO_ERROR; } if (size != ((unsigned int) lines * bpl)) { DBG (1, "sanei_scsi_cmd(): got %lu bytes, expected %d\n", (u_long) size, lines * bpl); return SANE_STATUS_INVAL; } DBG (1, "Got %lu bytes\n", (u_long) size); /* Reverse: */ if (s->bpp != 1) { if (s->bpp != 6) for (i = 0; i < size; i++) buf[i] = 255 - buf[i]; else for (i = 0; i < size; i++) buf[i] = 255 - ((buf[i] * 256.0f) / 64.0f); } s->lines_available -= lines; return SANE_STATUS_GOOD; } static SANE_Status attach (const char *devname, AgfaFocus_Device ** devp) { #define ATTACH_SCSI_INQ_LEN 55 const Byte scsi_inquiry[] = { 0x12, 0x00, 0x00, 0x00, ATTACH_SCSI_INQ_LEN, 0x00 }; Byte result[ATTACH_SCSI_INQ_LEN]; int fd; AgfaFocus_Device *dev; SANE_Status status; size_t size; int i; for (dev = agfafocus_devices; dev; dev = dev->next) if (strcmp (dev->sane.name, devname) == 0) { if (devp) *devp = dev; return SANE_STATUS_GOOD; } DBG (3, "attach: opening %s\n", devname); status = sanei_scsi_open (devname, &fd, sense_handler, 0); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: open failed (%s)\n", sane_strstatus (status)); return SANE_STATUS_INVAL; } DBG (4, "attach: sending INQUIRY\n"); size = sizeof (result); status = sanei_scsi_cmd (fd, scsi_inquiry, sizeof (scsi_inquiry), result, &size); if (status != SANE_STATUS_GOOD || size != ATTACH_SCSI_INQ_LEN) { DBG (1, "attach: inquiry failed (%s)\n", sane_strstatus (status)); sanei_scsi_close (fd); return status; } status = test_ready (fd); sanei_scsi_close (fd); if (status != SANE_STATUS_GOOD) return status; /* The structure send by the scanner after inquiry is not SCSI-2 compatible. The standard manufacturer/model fields are no ASCII strings, but ? At offset 36 my SIEMENS scanner identifies as an AGFA one ?! */ if (result[0] != 6 || strncmp ((char *)result + 36, "AGFA0", 5)) { DBG (1, "attach: device doesn't look like a Siemens 9036 scanner\n"); return SANE_STATUS_INVAL; } DBG (4, "Inquiry data:\n"); DBG (4, "-----------\n"); for (i = 5; i < 55; i += 10) DBG (4, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", result[i], result[i + 1], result[i + 2], result[i + 3], result[i + 4], result[i + 5], result[i + 6], result[i + 7], result[i + 8], result[i + 9]); dev = malloc (sizeof (*dev)); if (!dev) return SANE_STATUS_NO_MEM; memset (dev, 0, sizeof (*dev)); dev->sane.name = strdup (devname); if (!strncmp ((char *)result + 36, "AGFA01", 6)) { dev->sane.vendor = "AGFA"; dev->sane.model = "Focus GS Scanner (6 bit)"; dev->upload_user_defines = SANE_TRUE; dev->type = AGFAGRAY64; } else if (!strncmp ((char *)result + 36, "AGFA02", 6)) { dev->sane.vendor = "AGFA"; dev->sane.model = "Focus Lineart Scanner"; dev->upload_user_defines = SANE_FALSE; dev->type = AGFALINEART; } else if (!strncmp ((char *)result + 36, "AGFA03", 6)) { dev->sane.vendor = "AGFA"; dev->sane.model = "Focus II"; dev->upload_user_defines = SANE_TRUE; dev->type = AGFAGRAY256; } else if (!strncmp ((char *)result + 36, "AGFA04", 6)) { dev->sane.vendor = "AGFA"; dev->sane.model = "Focus Color"; dev->upload_user_defines = SANE_TRUE; dev->type = AGFACOLOR; } else { free (dev); DBG (1, "attach: device looks like an AGFA scanner, but wasn't recognised\n"); return SANE_STATUS_INVAL; } dev->sane.type = "flatbed scanner"; dev->transparent = result[45] & 0x80 ? SANE_TRUE : SANE_FALSE; dev->analoglog = result[46] & 0x80 ? SANE_TRUE : SANE_FALSE; dev->tos5 = result[46] & 0x05 ? SANE_TRUE : SANE_FALSE; dev->quality = result[47] & 0x40 ? SANE_TRUE : SANE_FALSE; dev->disconnect = result[47] & 0x80 ? SANE_TRUE : SANE_FALSE; DBG (4, "\n"); DBG (4, "scan modes:\n"); DBG (4, "-----------\n"); DBG (4, "three pass color mode: %s\n", dev->type >= AGFACOLOR ? "yes" : "no"); DBG (4, "8 bit gray mode: %s\n", dev->type >= AGFAGRAY64 ? "yes" : "no"); DBG (4, "uploadable matrices: %s\n", dev->upload_user_defines ? "yes" : "no"); DBG (4, "transparency: %s\n", dev->transparent ? "yes" : "no"); DBG (4, "disconnect: %s\n", dev->disconnect ? "yes" : "no"); DBG (4, "quality calibration: %s\n", dev->quality ? "yes" : "no"); dev->handle = 0; DBG (3, "attach: found AgfaFocus scanner model\n"); ++num_devices; dev->next = agfafocus_devices; agfafocus_devices = dev; if (devp) *devp = dev; return SANE_STATUS_GOOD; } static SANE_Status do_eof (AgfaFocus_Scanner *s) { if (s->pipe >= 0) { close (s->pipe); s->pipe = -1; } return SANE_STATUS_EOF; } static SANE_Status do_cancel (AgfaFocus_Scanner * s) { s->scanning = SANE_FALSE; s->pass = 0; do_eof (s); if (sanei_thread_is_valid (s->reader_pid)) { int exit_status; /* ensure child knows it's time to stop: */ sanei_thread_kill (s->reader_pid); sanei_thread_waitpid (s->reader_pid, &exit_status); sanei_thread_invalidate(s->reader_pid); } if (s->fd >= 0) { stop_scan (s->fd); release_unit (s->fd); sanei_scsi_close (s->fd); s->fd = -1; } return SANE_STATUS_CANCELLED; } static SANE_Status init_options (AgfaFocus_Scanner * s) { int i; /* Hardware Limitations: must be static ! */ static const SANE_Int dpi_list[] = {8, 100, 200, 300, 400, 500, 600, 700, 800}; static const SANE_Range percentage_range = { SANE_FIX(-100), /* minimum */ SANE_FIX(100), /* maximum */ SANE_FIX(1) /* quantization */ }; static const SANE_Range sharpen_range = {0, 7, 1}; static const SANE_Range exposure_range = {0, 100, 0}; static const SANE_Range attenuation_range = { SANE_FIX(0), /* minimum */ SANE_FIX(100), /* maximum */ SANE_FIX(1) /* quantization */ }; static const SANE_Range x_range = {0, SANE_FIX (8.27 * MM_PER_INCH), 0}; static const SANE_Range y_range = {0, SANE_FIX (12.72 * MM_PER_INCH), 0}; /* ------ */ memset (s->opt, 0, sizeof (s->opt)); memset (s->val, 0, sizeof (s->val)); for (i = 0; i < NUM_OPTIONS; ++i) { s->opt[i].size = sizeof (SANE_Word); s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* "Mode" group: */ s->opt[OPT_MODE_GROUP].title = "Scan Mode"; s->opt[OPT_MODE_GROUP].desc = ""; s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MODE_GROUP].cap = 0; s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* scan mode */ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; 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; switch (s->hw->type) { case AGFACOLOR: s->opt[OPT_MODE].size = max_string_size (focuscolor_mode_list); s->opt[OPT_MODE].constraint.string_list = focuscolor_mode_list; s->val[OPT_MODE].s = strdup (focuscolor_mode_list[0]); break; case AGFAGRAY256: s->opt[OPT_MODE].size = max_string_size (focusii_mode_list); s->opt[OPT_MODE].constraint.string_list = focusii_mode_list; s->val[OPT_MODE].s = strdup (focusii_mode_list[0]); break; default: s->opt[OPT_MODE].size = max_string_size (focus_mode_list); s->opt[OPT_MODE].constraint.string_list = focus_mode_list; s->val[OPT_MODE].s = strdup (focus_mode_list[0]); break; } s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; /* resolution */ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_RESOLUTION].constraint.word_list = dpi_list; s->val[OPT_RESOLUTION].w = 100; s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE; s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE; s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE; s->opt[OPT_SOURCE].type = SANE_TYPE_STRING; s->opt[OPT_SOURCE].unit = SANE_UNIT_NONE; if (!s->hw->transparent) s->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE; else s->opt[OPT_SOURCE].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_SOURCE].constraint.string_list = source_list; s->opt[OPT_SOURCE].size = max_string_size (source_list); s->val[OPT_SOURCE].s = strdup (source_list[0]); /* "Geometry" group: */ s->opt[OPT_GEOMETRY_GROUP].title = "Geometry"; s->opt[OPT_GEOMETRY_GROUP].desc = ""; s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* top-left x */ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; s->opt[OPT_TL_X].unit = SANE_UNIT_MM; s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_X].constraint.range = &x_range; s->val[OPT_TL_X].w = 0; /* top-left y */ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_Y].constraint.range = &y_range; s->val[OPT_TL_Y].w = 0; /* bottom-right x */ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; s->opt[OPT_BR_X].unit = SANE_UNIT_MM; s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_X].constraint.range = &x_range; s->val[OPT_BR_X].w = x_range.max; /* bottom-right y */ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_Y].constraint.range = &y_range; s->val[OPT_BR_Y].w = y_range.max; /* "Enhancement" group: */ s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement"; s->opt[OPT_ENHANCEMENT_GROUP].desc = ""; s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_ENHANCEMENT_GROUP].cap = 0; s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* exposure */ s->opt[OPT_EXPOSURE].name = "exposure"; s->opt[OPT_EXPOSURE].title = "Exposure"; s->opt[OPT_EXPOSURE].desc = "Analog exposure control."; s->opt[OPT_EXPOSURE].type = SANE_TYPE_INT; s->opt[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE; s->opt[OPT_EXPOSURE].unit = SANE_UNIT_PERCENT; s->opt[OPT_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_EXPOSURE].constraint.range = &exposure_range; s->val[OPT_EXPOSURE].w = 23; /* brightness automatic correct */ s->opt[OPT_AUTO_BRIGHTNESS].name = "adjust-bright"; s->opt[OPT_AUTO_BRIGHTNESS].title = "Automatic brightness correction"; s->opt[OPT_AUTO_BRIGHTNESS].desc = "Turns on automatic brightness correction of " "the acquired image. This makes the scanner do a two pass scan to analyse the " "brightness of the image before it's scanned."; s->opt[OPT_AUTO_BRIGHTNESS].type = SANE_TYPE_BOOL; s->val[OPT_AUTO_BRIGHTNESS].b = SANE_FALSE; /* contrast automatic correct */ s->opt[OPT_AUTO_CONTRAST].name = "adjust-contr"; s->opt[OPT_AUTO_CONTRAST].title = "Automatic contrast correction"; s->opt[OPT_AUTO_CONTRAST].desc = "Turns on automatic contrast correction of " "the acquired image. This makes the scanner do a two pass scan to analyse " "the contrast of the image to be scanned."; s->opt[OPT_AUTO_CONTRAST].type = SANE_TYPE_BOOL; s->val[OPT_AUTO_CONTRAST].b = SANE_FALSE; /* brightness */ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].desc = "Controls the brightness of the acquired image. " "When automatic brightness is enabled, this can be used to adjust the selected brightness."; s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_FIXED; s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT; s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BRIGHTNESS].constraint.range = &percentage_range; s->val[OPT_BRIGHTNESS].w = 0; /* contrast */ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST; s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; s->opt[OPT_CONTRAST].desc = "Controls the contrast of the acquired image. " "When automatic contrast is enabled, this can be used to adjust the selected contrast."; s->opt[OPT_CONTRAST].type = SANE_TYPE_FIXED; s->opt[OPT_CONTRAST].unit = SANE_UNIT_PERCENT; s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CONTRAST].constraint.range = &percentage_range; s->val[OPT_CONTRAST].w = 0; /* halftone patterns */ s->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN; s->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN; s->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN; s->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING; s->opt[OPT_HALFTONE_PATTERN].size = 32; s->opt[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_STRING_LIST; if (s->hw->upload_user_defines) { s->opt[OPT_HALFTONE_PATTERN].constraint.string_list = halftone_upload_list; s->val[OPT_HALFTONE_PATTERN].s = strdup (halftone_upload_list[0]); } else { s->opt[OPT_HALFTONE_PATTERN].constraint.string_list = halftone_list; s->val[OPT_HALFTONE_PATTERN].s = strdup (halftone_list[0]); } /* red-attenuation */ s->opt[OPT_ATTENUATION_RED].name = "red-attenuation"; s->opt[OPT_ATTENUATION_RED].title = "Red attenuation"; s->opt[OPT_ATTENUATION_RED].desc = "Controls the red attenuation of the acquired image. " "Higher values mean less impact on scanned image."; s->opt[OPT_ATTENUATION_RED].type = SANE_TYPE_FIXED; s->opt[OPT_ATTENUATION_RED].cap |= SANE_CAP_INACTIVE; s->opt[OPT_ATTENUATION_RED].unit = SANE_UNIT_PERCENT; s->opt[OPT_ATTENUATION_RED].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_ATTENUATION_RED].constraint.range = &attenuation_range; s->val[OPT_ATTENUATION_RED].w = SANE_FIX (50.0); /* green-attenuation */ s->opt[OPT_ATTENUATION_GREEN].name = "green-attenuation"; s->opt[OPT_ATTENUATION_GREEN].title = "Green attenuation"; s->opt[OPT_ATTENUATION_GREEN].desc = "Controls the green attenuation of the acquired image. " "Higher values mean less impact on scanned image."; s->opt[OPT_ATTENUATION_GREEN].type = SANE_TYPE_FIXED; s->opt[OPT_ATTENUATION_GREEN].cap |= SANE_CAP_INACTIVE; s->opt[OPT_ATTENUATION_GREEN].unit = SANE_UNIT_PERCENT; s->opt[OPT_ATTENUATION_GREEN].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_ATTENUATION_GREEN].constraint.range = &attenuation_range; s->val[OPT_ATTENUATION_GREEN].w = SANE_FIX (50.0); /* blue-attenuation */ s->opt[OPT_ATTENUATION_BLUE].name = "blue-attenuation"; s->opt[OPT_ATTENUATION_BLUE].title = "Blue attenuation"; s->opt[OPT_ATTENUATION_BLUE].desc = "Controls the blue attenuation of the acquired image. " "Higher values mean less impact on scanned image."; s->opt[OPT_ATTENUATION_BLUE].type = SANE_TYPE_FIXED; s->opt[OPT_ATTENUATION_BLUE].cap |= SANE_CAP_INACTIVE; s->opt[OPT_ATTENUATION_BLUE].unit = SANE_UNIT_PERCENT; s->opt[OPT_ATTENUATION_BLUE].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_ATTENUATION_BLUE].constraint.range = &attenuation_range; s->val[OPT_ATTENUATION_BLUE].w = SANE_FIX (50.0); /* quality-calibration */ s->opt[OPT_QUALITY].name = SANE_NAME_QUALITY_CAL; s->opt[OPT_QUALITY].title = SANE_TITLE_QUALITY_CAL; s->opt[OPT_QUALITY].desc = "Controls the calibration that will be done in the " "scanner. Less calibration result in faster scanner times."; s->opt[OPT_QUALITY].type = SANE_TYPE_STRING; s->opt[OPT_QUALITY].size = 32; if (!s->hw->quality) s->opt[OPT_QUALITY].cap |= SANE_CAP_INACTIVE; else s->opt[OPT_QUALITY].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_QUALITY].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_QUALITY].constraint.string_list = quality_list; s->val[OPT_QUALITY].s = strdup (quality_list[1]); /* sharpening */ s->opt[OPT_SHARPEN].name = "sharpen"; s->opt[OPT_SHARPEN].title = "Sharpening"; s->opt[OPT_SHARPEN].desc = "Controls the sharpening that will be " "done by the video processor in the scanner."; s->opt[OPT_SHARPEN].type = SANE_TYPE_INT; s->opt[OPT_SHARPEN].unit = SANE_UNIT_NONE; s->opt[OPT_SHARPEN].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_SHARPEN].constraint.range = &sharpen_range; s->val[OPT_SHARPEN].w = 1; return SANE_STATUS_GOOD; } static SANE_Status attach_one (const char *dev) { attach (dev, 0); return SANE_STATUS_GOOD; } SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { char dev_name[PATH_MAX]; size_t len; FILE *fp; (void) authorize; /* silence gcc */ DBG_INIT (); sanei_thread_init (); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); fp = sanei_config_open ("agfafocus.conf"); if (!fp) { /* default to /dev/scanner instead of insisting on config file */ attach ("/dev/scanner", 0); return SANE_STATUS_GOOD; } while (sanei_config_read (dev_name, sizeof (dev_name), fp)) { if (dev_name[0] == '#') /* ignore line comments */ continue; len = strlen (dev_name); if (!len) continue; /* ignore empty lines */ sanei_config_attach_matching_devices (dev_name, attach_one); } fclose (fp); return SANE_STATUS_GOOD; } void sane_exit (void) { AgfaFocus_Device *dev, *next; for (dev = agfafocus_devices; dev; dev = next) { next = dev->next; if (dev->handle) sane_close (dev->handle); free (dev); } if (devlist) free (devlist); } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { AgfaFocus_Device *dev; int i; (void) local_only; /* silence gcc */ if (devlist) free (devlist); devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); if (!devlist) return SANE_STATUS_NO_MEM; for (dev = agfafocus_devices, i = 0; i < num_devices; dev = dev->next) devlist[i++] = &dev->sane; devlist[i++] = 0; *device_list = devlist; return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { AgfaFocus_Device *dev; SANE_Status status; AgfaFocus_Scanner *s; if (devicename[0]) { status = attach (devicename, &dev); if (status != SANE_STATUS_GOOD) return status; } else { /* empty devicname -> use first device */ dev = agfafocus_devices; } if (!dev) return SANE_STATUS_INVAL; if (dev->handle) return SANE_STATUS_DEVICE_BUSY; s = malloc (sizeof (*s)); if (!s) return SANE_STATUS_NO_MEM; memset (s, 0, sizeof (*s)); s->scanning = SANE_FALSE; s->fd = -1; s->hw = dev; s->hw->handle = s; init_options (s); *handle = s; return SANE_STATUS_GOOD; } void sane_close (SANE_Handle handle) { AgfaFocus_Scanner *s = handle; if (s->scanning) do_cancel (handle); s->hw->handle = 0; free (handle); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { AgfaFocus_Scanner *s = handle; if ((unsigned) option >= NUM_OPTIONS) return 0; return s->opt + option; } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { AgfaFocus_Scanner *s = handle; SANE_Status status; if (info) *info = 0; if (s->scanning) return SANE_STATUS_DEVICE_BUSY; if (option >= NUM_OPTIONS || !SANE_OPTION_IS_ACTIVE (s->opt[option].cap)) return SANE_STATUS_UNSUPPORTED; if (action == SANE_ACTION_GET_VALUE) { switch (option) { case OPT_RESOLUTION: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_NUM_OPTS: case OPT_BRIGHTNESS: case OPT_CONTRAST: case OPT_SHARPEN: case OPT_EXPOSURE: case OPT_ATTENUATION_RED: case OPT_ATTENUATION_GREEN: case OPT_ATTENUATION_BLUE: *(SANE_Word *) val = s->val[option].w; break; case OPT_AUTO_BRIGHTNESS: case OPT_AUTO_CONTRAST: *(SANE_Bool *) val = s->val[option].b; break; case OPT_MODE: case OPT_HALFTONE_PATTERN: case OPT_QUALITY: case OPT_SOURCE: strcpy (val, s->val[option].s); return (SANE_STATUS_GOOD); default: return SANE_STATUS_UNSUPPORTED; } } else if (action == SANE_ACTION_SET_VALUE) { if (!SANE_OPTION_IS_SETTABLE (s->opt[option].cap)) return SANE_STATUS_UNSUPPORTED; status = sanei_constrain_value (s->opt + option, val, info); if (status != SANE_STATUS_GOOD) return status; switch (option) { case OPT_RESOLUTION: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: if (info) *info |= SANE_INFO_RELOAD_PARAMS; // fall through case OPT_SHARPEN: case OPT_EXPOSURE: case OPT_ATTENUATION_RED: case OPT_ATTENUATION_GREEN: case OPT_ATTENUATION_BLUE: case OPT_BRIGHTNESS: case OPT_CONTRAST: s->val[option].w = *(SANE_Word *) val; break; case OPT_AUTO_BRIGHTNESS: case OPT_AUTO_CONTRAST: s->val[option].b = *(SANE_Bool *) val; break; case OPT_MODE: if (strcmp (s->val[option].s, (SANE_String) val)) { if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); if (strcmp (s->val[option].s, "Gray (6 bit)") == 0) s->mode = GRAY6BIT; else if (strcmp (s->val[option].s, "Gray (8 bit)") == 0) s->mode = GRAY8BIT; else if (strcmp (s->val[option].s, "Color (18 bit)") == 0) s->mode = COLOR18BIT; else if (strcmp (s->val[option].s, "Color (24 bit)") == 0) s->mode = COLOR24BIT; else s->mode = LINEART; switch (s->mode) { case LINEART: s->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_SHARPEN].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE; s->opt[OPT_ATTENUATION_RED].cap |= SANE_CAP_INACTIVE; s->opt[OPT_ATTENUATION_GREEN].cap |= SANE_CAP_INACTIVE; s->opt[OPT_ATTENUATION_BLUE].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_AUTO_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_AUTO_CONTRAST].cap &= ~SANE_CAP_INACTIVE; break; case GRAY6BIT: s->opt[OPT_SHARPEN].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE; s->opt[OPT_ATTENUATION_RED].cap |= SANE_CAP_INACTIVE; s->opt[OPT_ATTENUATION_GREEN].cap |= SANE_CAP_INACTIVE; s->opt[OPT_ATTENUATION_BLUE].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_AUTO_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_AUTO_CONTRAST].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; break; case GRAY8BIT: s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_AUTO_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_AUTO_CONTRAST].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE; s->opt[OPT_ATTENUATION_RED].cap |= SANE_CAP_INACTIVE; s->opt[OPT_ATTENUATION_GREEN].cap |= SANE_CAP_INACTIVE; s->opt[OPT_ATTENUATION_BLUE].cap |= SANE_CAP_INACTIVE; s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; s->opt[OPT_SHARPEN].cap |= SANE_CAP_INACTIVE; break; case COLOR18BIT: s->opt[OPT_EXPOSURE].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_ATTENUATION_RED].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_ATTENUATION_GREEN].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_ATTENUATION_BLUE].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; s->opt[OPT_AUTO_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; s->opt[OPT_AUTO_CONTRAST].cap |= SANE_CAP_INACTIVE; s->opt[OPT_SHARPEN].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; break; case COLOR24BIT: s->opt[OPT_EXPOSURE].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_ATTENUATION_RED].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_ATTENUATION_GREEN].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_ATTENUATION_BLUE].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; s->opt[OPT_AUTO_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; s->opt[OPT_AUTO_CONTRAST].cap |= SANE_CAP_INACTIVE; s->opt[OPT_SHARPEN].cap |= SANE_CAP_INACTIVE; s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; break; } } break; case OPT_SOURCE: case OPT_QUALITY: case OPT_HALFTONE_PATTERN: if (info && strcmp (s->val[option].s, (SANE_String) val)) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); break; default: return SANE_STATUS_UNSUPPORTED; } } else { return SANE_STATUS_UNSUPPORTED; } return SANE_STATUS_GOOD; } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { AgfaFocus_Scanner *s = handle; if (!s->scanning) { double width, height, dpi; const char *quality; const char *original; memset (&s->params, 0, sizeof (s->params)); width = SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w); height = SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w); dpi = s->val[OPT_RESOLUTION].w; /* make best-effort guess at what parameters will look like once scanning starts. */ if (dpi > 0.0 && width > 0.0 && height > 0.0) { double dots_per_mm = dpi / MM_PER_INCH; s->params.pixels_per_line = width * dots_per_mm + 0.5; s->params.lines = height * dots_per_mm + 0.5; } /* Should we specify calibration quality? */ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_QUALITY].cap)) { DBG(3, " -------------- setting quality\n"); quality = s->val[OPT_QUALITY].s; if (strcmp (quality, "Low") == 0 ) s->quality = 255; else if (strcmp (quality, "High") == 0) s->quality = 1; else s->quality = 0; } else s->quality = 0; /* Should we select source type? */ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_SOURCE].cap)) { DBG(3, " -------------- setting source\n"); original = s->val[OPT_SOURCE].s; if (strcmp (original, "Transparency") == 0) s->original = 0; else s->original = 1; } else s->original = 0; s->exposure = ((s->val[OPT_EXPOSURE].w * (255.0f - 80.0f)) / 100.0f) + 80.0f; s->r_att = (SANE_UNFIX (s->val[OPT_ATTENUATION_RED].w) * 20.0f) / 100.0f; s->g_att = (SANE_UNFIX (s->val[OPT_ATTENUATION_GREEN].w) * 20.0f) / 100.0f; s->b_att = (SANE_UNFIX (s->val[OPT_ATTENUATION_BLUE].w) * 20.0f) / 100.0f; s->tonecurve = 0; switch (s->mode) { case LINEART: { const char *halftone; s->image_composition = 0; /* in 1 bpp mode, lines need to be 8 pixel length */ if (s->params.pixels_per_line % 8) s->params.pixels_per_line += 8 - (s->params.pixels_per_line % 8); s->params.format = SANE_FRAME_GRAY; s->params.bytes_per_line = s->params.pixels_per_line / 8; s->bpp = s->params.depth = 1; halftone = s->val[OPT_HALFTONE_PATTERN].s; if (strcmp (halftone, "1") == 0 ) s->halftone = 1; else if (strcmp (halftone, "Dispersed dot 4x4") == 0) s->halftone = 2; else if (strcmp (halftone, "Round (Clustered dot 4x4)") == 0) s->halftone = 3; else if (strcmp (halftone, "Diamond (Clustered dot 4x4)") == 0) s->halftone = 4; else if (strcmp (halftone, "User defined") == 0) s->halftone = 5; else s->halftone = 0; s->edge = s->val[OPT_SHARPEN].w; } break; case GRAY6BIT: s->image_composition = 2; s->params.format = SANE_FRAME_GRAY; s->params.bytes_per_line = s->params.pixels_per_line; s->bpp = 6; s->params.depth = 8; s->edge = s->val[OPT_SHARPEN].w; break; case GRAY8BIT: s->image_composition = 2; s->params.format = SANE_FRAME_GRAY; s->params.bytes_per_line = s->params.pixels_per_line; s->bpp = s->params.depth = 8; break; case COLOR18BIT: s->image_composition = 5; s->params.format = SANE_FRAME_RED; s->params.bytes_per_line = s->params.pixels_per_line; s->bpp = 6; s->params.depth = 8; s->edge = s->val[OPT_SHARPEN].w; break; case COLOR24BIT: s->image_composition = 5; s->params.format = SANE_FRAME_RED; s->params.bytes_per_line = s->params.pixels_per_line; s->bpp = s->params.depth = 8; break; } s->pass = 0; /*s->params.bytes_per_line = (s->params.pixels_per_line + (8 - s->params.depth)) / (8 / s->params.depth);*/ } else if (s->mode == COLOR18BIT || s->mode == COLOR24BIT) s->params.format = SANE_FRAME_RED + s->pass; s->params.last_frame = (s->params.format != SANE_FRAME_RED && s->params.format != SANE_FRAME_GREEN); if (params) *params = s->params; return SANE_STATUS_GOOD; } /* This function is executed as a child process. The reason this is executed as a subprocess is because some (most?) generic SCSI interfaces block a SCSI request until it has completed. With a subprocess, we can let it block waiting for the request to finish while the main process can go about to do more important things (such as recognizing when the user presses a cancel button). WARNING: Since this is executed as a subprocess, it's NOT possible to update any of the variables in the main process (in particular the scanner state cannot be updated). */ static int reader_process (void *scanner) { AgfaFocus_Scanner *s = (AgfaFocus_Scanner *) scanner; int fd = s->reader_pipe; SANE_Status status; SANE_Byte *data; int lines_read = 0; int lines_per_buffer; int bytes_per_line = 0, total_lines = 0; int i; sigset_t sigterm_set; sigset_t ignore_set; struct SIGACTION act; if (sanei_thread_is_forked()) close (s->pipe); sigfillset (&ignore_set); sigdelset (&ignore_set, SIGTERM); #if defined (__APPLE__) && defined (__MACH__) sigdelset (&ignore_set, SIGUSR2); #endif sigprocmask (SIG_SETMASK, &ignore_set, 0); memset (&act, 0, sizeof (act)); sigaction (SIGTERM, &act, 0); sigemptyset (&sigterm_set); sigaddset (&sigterm_set, SIGTERM); if (!s->hw->disconnect) wait_ready (s->fd); status = get_read_sizes (s->fd, &s->lines_available, &bytes_per_line, &total_lines); if (status != SANE_STATUS_GOOD) { DBG (1, "open: get_read_sizes() failed: %s\n", sane_strstatus (status)); do_cancel (s); close (fd); return 1; } if (!s->lines_available || !bytes_per_line || !total_lines || bytes_per_line < s->params.bytes_per_line) { DBG (1, "open: invalid sizes: %d, %d, %d\n", s->lines_available, bytes_per_line, total_lines); do_cancel (s); close (fd); return 1; } lines_per_buffer = sanei_scsi_max_request_size / bytes_per_line; if (!lines_per_buffer) { close (fd); return 2; /* resolution is too high */ } data = malloc (lines_per_buffer * bytes_per_line); if (!data) { DBG (1, "open malloc(%lu) failed.\n", (u_long) lines_per_buffer * bytes_per_line); do_cancel (s); close (fd); return 1; } while (lines_read < s->params.lines) { int lines = lines_per_buffer; if (s->lines_available == 0) { /* No lines in scanner? Scan some more */ status = request_more_data (s); if (status != SANE_STATUS_GOOD) { close (fd); return 1; } } /* We only request as many lines as there are already scanned */ if (lines > s->lines_available) lines = s->lines_available; DBG (1, "Requesting %d lines, in scanner: %d, total: %d\n", lines, s->lines_available, s->params.lines); status = read_data (s, data, lines, bytes_per_line); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_read: read_data() failed (%s)\n", sane_strstatus (status)); do_cancel (s); close (fd); return 1; } /* Sometimes the scanner will return more bytes per line than requested, so we copy only what we wanted. */ for (i = 0; i < lines; i++) if (write (fd, data + i * bytes_per_line, s->params.bytes_per_line) != s->params.bytes_per_line) { do_cancel (s); close (fd); return 1; } lines_read += lines; } close (fd); return 0; } SANE_Status sane_start (SANE_Handle handle) { AgfaFocus_Scanner *s = handle; SANE_Status status; int fds[2]; /* First make sure we have a current parameter set. Some of the parameters will be overwritten below, but that's OK. */ status = sane_get_parameters (s, 0); if (status != SANE_STATUS_GOOD) return status; /* don't initialise scanner if we're doing a three-pass scan */ if (s->pass == 0) { if (s->fd < 0) { status = sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, 0); if (status != SANE_STATUS_GOOD) { DBG (1, "open: open of %s failed: %s\n", s->hw->sane.name, sane_strstatus (status)); s->fd = -1; return status; } } status = test_ready (s->fd); if (status != SANE_STATUS_GOOD) { DBG (1, "open: test_ready() failed: %s\n", sane_strstatus (status)); sanei_scsi_close (s->fd); s->fd = -1; return status; } status = reserve_unit (s->fd); if (status != SANE_STATUS_GOOD) { DBG (1, "open: reserve_unit() failed: %s\n", sane_strstatus (status)); sanei_scsi_close (s->fd); s->fd = -1; return status; } status = set_window (s); if (status != SANE_STATUS_GOOD) { DBG (1, "open: set_window() failed: %s\n", sane_strstatus (status)); release_unit (s->fd); sanei_scsi_close (s->fd); s->fd = -1; return status; } { int matrix[256] = { 2, 60, 16, 56, 3, 57, 13, 53, 34, 18, 48, 32, 35, 19, 45, 29, 10, 50, 6, 63, 11, 51, 7, 61, 42, 26, 38, 22, 43, 27, 39, 23, 4, 58, 14, 54, 1, 59, 15, 55, 36, 20, 46, 30, 33, 17, 47, 31, 12, 52, 8, 62, 9, 49, 5, 63, 44, 28, 40, 24, 41, 25, 37, 21 }; status = upload_dither_matrix (s, 8, 8, matrix); if (status != SANE_STATUS_GOOD) { DBG (1, "open: upload_dither_matrix() failed: %s\n", sane_strstatus (status)); release_unit (s->fd); sanei_scsi_close (s->fd); s->fd = -1; return status; } } s->scanning = SANE_TRUE; status = start_scan (s->fd, SANE_FALSE); if (status != SANE_STATUS_GOOD) { DBG (1, "open: start_scan() failed: %s\n", sane_strstatus (status)); do_cancel (s); return status; } } else { /* continue three-pass scan */ status = start_scan (s->fd, SANE_TRUE); if (status != SANE_STATUS_GOOD) { DBG (1, "open: start_scan() failed: %s\n", sane_strstatus (status)); do_cancel (s); return status; } } if (pipe (fds) < 0) return SANE_STATUS_IO_ERROR; s->pipe = fds[0]; s->reader_pipe = fds[1]; s->reader_pid = sanei_thread_begin (reader_process, (void *) s); if (sanei_thread_is_forked()) close (s->reader_pipe); return SANE_STATUS_GOOD; } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { AgfaFocus_Scanner *s = handle; ssize_t nread; *len = 0; nread = read (s->pipe, buf, max_len); DBG (3, "read %ld bytes\n", (long) nread); if (!s->scanning) return do_cancel (s); if (nread < 0) { if (errno == EAGAIN) { return SANE_STATUS_GOOD; } else { do_cancel (s); return SANE_STATUS_IO_ERROR; } } *len = nread; if (nread == 0) { s->pass++; return do_eof (s); } return SANE_STATUS_GOOD; } void sane_cancel (SANE_Handle handle) { AgfaFocus_Scanner *s = handle; if (sanei_thread_is_valid (s->reader_pid)) sanei_thread_kill (s->reader_pid); s->scanning = SANE_FALSE; } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { AgfaFocus_Scanner *s = handle; if (!s->scanning) return SANE_STATUS_INVAL; if (fcntl (s->pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0) return SANE_STATUS_IO_ERROR; return SANE_STATUS_GOOD; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { AgfaFocus_Scanner *s = handle; if (!s->scanning) return SANE_STATUS_INVAL; *fd = s->pipe; return SANE_STATUS_GOOD; } backends-1.3.0/backend/agfafocus.conf.in000066400000000000000000000000151456256263500201000ustar00rootroot00000000000000/dev/scanner backends-1.3.0/backend/agfafocus.h000066400000000000000000000060151456256263500170030ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. This file (C) 1997 Ingo Schneider (C) 1998 Karl Anders Øygard This file is part of the SANE package. SANE 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. SANE 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 sane; see the file COPYING. If not, see . */ #ifndef agfafocus_h #define agfafocus_h enum AgfaFocus_Scanner_Type { AGFAGRAY64, AGFALINEART, AGFAGRAY256, AGFACOLOR }; typedef enum { LINEART, GRAY6BIT, GRAY8BIT, COLOR18BIT, COLOR24BIT } AgfaFocus_Scanner_Mode; enum AgfaFocus_Option { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_MODE, OPT_HALFTONE_PATTERN, /* halftone matrix */ OPT_RESOLUTION, OPT_SOURCE, OPT_QUALITY, /* quality calibration */ OPT_GEOMETRY_GROUP, OPT_TL_X, /* top-left x */ OPT_TL_Y, /* top-left y */ OPT_BR_X, /* bottom-right x */ OPT_BR_Y, /* bottom-right y */ OPT_ENHANCEMENT_GROUP, OPT_EXPOSURE, OPT_BRIGHTNESS, OPT_CONTRAST, OPT_AUTO_BRIGHTNESS, OPT_AUTO_CONTRAST, OPT_ATTENUATION_RED, OPT_ATTENUATION_BLUE, OPT_ATTENUATION_GREEN, OPT_SHARPEN, /* sharpening */ /* must come last: */ NUM_OPTIONS }; typedef struct AgfaFocus_Device { struct AgfaFocus_Device *next; SANE_Device sane; SANE_Handle handle; SANE_Word type; SANE_Bool transparent; SANE_Bool analoglog; SANE_Bool tos5; SANE_Bool quality; SANE_Bool disconnect; SANE_Bool upload_user_defines; } AgfaFocus_Device; typedef struct AgfaFocus_Scanner { /* all the state needed to define a scan request: */ SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; /* Parsed option values and variables that are valid only during actual scanning: */ SANE_Bool scanning; SANE_Int pass; SANE_Parameters params; AgfaFocus_Scanner_Mode mode; SANE_Int image_composition; SANE_Int bpp; SANE_Int halftone; SANE_Int original; SANE_Int exposure; SANE_Int r_att; SANE_Int g_att; SANE_Int b_att; SANE_Int tonecurve; SANE_Int quality; SANE_Bool edge; SANE_Bool lin_log; int lines_available; /* Lines in scanner memory */ int fd; /* SCSI filedescriptor */ SANE_Pid reader_pid; /* process id of reader */ int pipe; /* pipe to reader process */ int reader_pipe; /* pipe from reader process */ /* scanner dependent/low-level state: */ AgfaFocus_Device *hw; } AgfaFocus_Scanner; #endif /* agfafocus_h */ backends-1.3.0/backend/apple.c000066400000000000000000002235351456256263500161510ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1998 Milon Firikis based on David Mosberger-Tang previous Work on mustek.c file from the SANE package. 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, see . 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 Apple flatbed scanners. */ #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "../include/_stdint.h" #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_scsi.h" /* SCSI commands that the Apple scanners understand: */ #define APPLE_SCSI_TEST_UNIT_READY 0x00 #define APPLE_SCSI_REQUEST_SENSE 0x03 #define APPLE_SCSI_INQUIRY 0x12 #define APPLE_SCSI_MODE_SELECT 0x15 #define APPLE_SCSI_RESERVE 0x16 #define APPLE_SCSI_RELEASE 0x17 #define APPLE_SCSI_START 0x1b #define APPLE_SCSI_AREA_AND_WINDOWS 0x24 #define APPLE_SCSI_READ_SCANNED_DATA 0x28 #define APPLE_SCSI_GET_DATA_STATUS 0x34 #define INQ_LEN 0x60 #define ENABLE(OPTION) s->opt[OPTION].cap &= ~SANE_CAP_INACTIVE #define DISABLE(OPTION) s->opt[OPTION].cap |= SANE_CAP_INACTIVE #define IS_ACTIVE(OPTION) (((s->opt[OPTION].cap) & SANE_CAP_INACTIVE) == 0) #define XQSTEP(XRES,BPP) (SANE_Int) (((double) (8*1200)) / ((double) (XRES*BPP))) #define YQSTEP(YRES) (SANE_Int) (((double) (1200)) / ((double) (YRES))) /* Very low info, Apple Scanners only */ /* TODO: Ok I admit it. I am not so clever to do this operations with bitwised operators. Sorry. */ #define STORE8(p,v) \ { \ *(p)=(v); \ } #define STORE16(p,v) \ { \ *(p)=(v)/256; \ *(p+1)=(v-*(p)*256); \ } #define STORE24(p,v) \ { \ *(p)=(v)/65536; \ *(p+1)=(v-*(p)*65536)/256; \ *(p+2)=(v-*(p)*65536-*(p+1)*256); \ } #define STORE32(p,v) \ { \ *(p)=(v)/16777216; \ *(p+1)=(v-*(p)*16777216)/65536; \ *(p+2)=(v-*(p)*16777216-*(p+1)*65536)/256; \ *(p+3)=(v-*(p)*16777216-*(p+1)*65536-*(p+2)*256);\ } #define READ24(p) *(p)*65536 + *(p+1)*256 + *(p+2) #include "../include/sane/sanei_backend.h" #ifndef PATH_MAX # define PATH_MAX 1024 #endif #include "../include/sane/sanei_config.h" #define APPLE_CONFIG_FILE "apple.conf" #include "apple.h" static const SANE_Device **devlist = 0; static int num_devices; static Apple_Device *first_dev; static Apple_Scanner *first_handle; static SANE_String_Const mode_list[6]; static SANE_String_Const SupportedModel[] = { "3", "AppleScanner 4bit, 16 Shades of Gray", "OneScanner 8bit, 256 Shades of Gray", "ColorOneScanner, RGB color 8bit per band", NULL }; static const SANE_String_Const graymap_list[] = { "dark", "normal", "light", 0 }; #if 0 static const SANE_Int resbit4_list[] = { 5, 75, 100, 150, 200, 300 }; static const SANE_Int resbit1_list[] = { 17, 75, 90, 100, 120, 135, 150, 165, 180, 195, 200, 210, 225, 240, 255, 270, 285, 300 }; #endif static const SANE_Int resbit_list[] = { 5, 75, 100, 150, 200, 300 }; static const SANE_String_Const speed_list[] = { "normal", "high", "high wo H/S", 0 }; static SANE_String_Const halftone_pattern_list[6]; static const SANE_String_Const color_sensor_list[] = { "All", "Red", "Green", "Blue", 0 }; /* NOTE: This is used for Brightness, Contrast, Threshold, AutoBackAdj and 0 is the default value */ static const SANE_Range byte_range = { 1, 255, 1 }; static const SANE_Range u8_range = { 0, /* minimum */ 255, /* maximum */ 0 /* quantization */ }; /* NOTE: However I can select from different lists during the hardware probing time. */ static const uint8_t inquiry[] = { APPLE_SCSI_INQUIRY, 0x00, 0x00, 0x00, INQ_LEN, 0x00 }; static const uint8_t test_unit_ready[] = { APPLE_SCSI_TEST_UNIT_READY, 0x00, 0x00, 0x00, 0x00, 0x00 }; #if 0 SANE_Int xqstep (unsigned int Xres, unsigned int bpp) { return (SANE_Int) ((double) (8 * 1200)) / ((double) (Xres * bpp)); } SANE_Int yqstep (unsigned int Yres, unsigned int bpp) { return (SANE_Int) ((double) (1200)) / ((double) (Yres)); } #endif /* The functions below return the quantized value of x,y in scanners dots aka 1/1200 of an inch */ static SANE_Int xquant (double x, unsigned int Xres, unsigned int bpp, int dir) { double tmp; unsigned int t; tmp = (double) x *Xres * bpp / (double) 8; t = (unsigned int) tmp; if (tmp - ((double) t) >= 0.1) if (dir) t++;; return t * 8 * 1200 / (Xres * bpp); } static SANE_Int yquant (double y, unsigned int Yres, int dir) { double tmp; unsigned int t; tmp = (double) y *Yres; t = (unsigned int) tmp; if (tmp - ((double) t) >= 0.1) if (dir) t++;; return t * 1200 / Yres; } static SANE_Status wait_ready (int fd) { #define MAX_WAITING_TIME 60 /* one minute, at most */ struct timeval now, start; SANE_Status status; #ifdef NEUTRALIZE_BACKEND return SANE_STATUS_GOOD; #else gettimeofday (&start, 0); while (1) { DBG (USER_MESSAGE, "wait_ready: sending TEST_UNIT_READY\n"); status = sanei_scsi_cmd (fd, test_unit_ready, sizeof (test_unit_ready), 0, 0); switch (status) { default: /* Ignore errors while waiting for scanner to become ready. Some SCSI drivers return EIO while the scanner is returning to the home position. */ DBG (ERROR_MESSAGE, "wait_ready: test unit ready failed (%s)\n", sane_strstatus (status)); /* fall through */ case SANE_STATUS_DEVICE_BUSY: gettimeofday (&now, 0); if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME) { DBG (ERROR_MESSAGE, "wait_ready: timed out after %lu seconds\n", (u_long) now.tv_sec - start.tv_sec); return SANE_STATUS_INVAL; } usleep (100000); /* retry after 100ms */ break; case SANE_STATUS_GOOD: return status; } } return SANE_STATUS_INVAL; #endif /* NEUTRALIZE_BACKEND */ } static SANE_Status sense_handler (int scsi_fd, u_char * result, void *arg) { (void) scsi_fd; /* silence gcc */ (void) arg; /* silence gcc */ switch (result[2] & 0x0F) { case 0: DBG (USER_MESSAGE, "Sense: No sense Error\n"); return SANE_STATUS_GOOD; case 2: DBG (ERROR_MESSAGE, "Sense: Scanner not ready\n"); return SANE_STATUS_DEVICE_BUSY; case 4: DBG (ERROR_MESSAGE, "Sense: Hardware Error. Read more...\n"); return SANE_STATUS_IO_ERROR; case 5: DBG (ERROR_MESSAGE, "Sense: Illegall request\n"); return SANE_STATUS_UNSUPPORTED; case 6: DBG (ERROR_MESSAGE, "Sense: Unit Attention (Wait until scanner " "boots)\n"); return SANE_STATUS_DEVICE_BUSY; case 9: DBG (ERROR_MESSAGE, "Sense: Vendor Unique. Read more...\n"); return SANE_STATUS_IO_ERROR; default: DBG (ERROR_MESSAGE, "Sense: Unknown Sense Key. Read more...\n"); return SANE_STATUS_IO_ERROR; } return SANE_STATUS_GOOD; } static SANE_Status request_sense (Apple_Scanner * s) { uint8_t cmd[6]; uint8_t result[22]; size_t size = sizeof (result); SANE_Status status; memset (cmd, 0, sizeof (cmd)); memset (result, 0, sizeof (result)); #ifdef NEUTRALIZE_BACKEND return SANE_STATUS_GOOD; #else cmd[0] = APPLE_SCSI_REQUEST_SENSE; STORE8 (cmd + 4, sizeof (result)); sanei_scsi_cmd (s->fd, cmd, sizeof (cmd), result, &size); if (result[7] != 14) { DBG (ERROR_MESSAGE, "Additional Length %u\n", (unsigned int) result[7]); status = SANE_STATUS_IO_ERROR; } status = sense_handler (s->fd, result, NULL); if (status == SANE_STATUS_IO_ERROR) { /* Now we are checking for Hardware and Vendor Unique Errors for all models */ /* First check the common Error conditions */ if (result[18] & 0x80) DBG (ERROR_MESSAGE, "Sense: Dim Light (output of lamp below 70%%).\n"); if (result[18] & 0x40) DBG (ERROR_MESSAGE, "Sense: No Light at all.\n"); if (result[18] & 0x20) DBG (ERROR_MESSAGE, "Sense: No Home.\n"); if (result[18] & 0x10) DBG (ERROR_MESSAGE, "Sense: No Limit. Tried to scan out of range.\n"); switch (s->hw->ScannerModel) { case APPLESCANNER: if (result[18] & 0x08) DBG (ERROR_MESSAGE, "Sense: Shade Error. Failed Calibration.\n"); if (result[18] & 0x04) DBG (ERROR_MESSAGE, "Sense: ROM Error.\n"); if (result[18] & 0x02) DBG (ERROR_MESSAGE, "Sense: RAM Error.\n"); if (result[18] & 0x01) DBG (ERROR_MESSAGE, "Sense: CPU Error.\n"); if (result[19] & 0x80) DBG (ERROR_MESSAGE, "Sense: DIPP Error.\n"); if (result[19] & 0x40) DBG (ERROR_MESSAGE, "Sense: DMA Error.\n"); if (result[19] & 0x20) DBG (ERROR_MESSAGE, "Sense: GA1 Error.\n"); break; case ONESCANNER: if (result[18] & 0x08) DBG (ERROR_MESSAGE, "Sense: CCD clock generator failed.\n"); if (result[18] & 0x04) DBG (ERROR_MESSAGE, "Sense: LRAM (Line RAM) Error.\n"); if (result[18] & 0x02) DBG (ERROR_MESSAGE, "Sense: CRAM (Correction RAM) Error.\n"); if (result[18] & 0x01) DBG (ERROR_MESSAGE, "Sense: ROM Error.\n"); if (result[19] & 0x08) DBG (ERROR_MESSAGE, "Sense: SRAM Error.\n"); if (result[19] & 0x04) DBG (ERROR_MESSAGE, "Sense: CPU Error.\n"); break; case COLORONESCANNER: if (result[18] & 0x08) DBG (ERROR_MESSAGE, "Sense: Calibration cirquit cannot " "support normal shading.\n"); if (result[18] & 0x04) DBG (ERROR_MESSAGE, "Sense: PSRAM (Correction RAM) Error.\n"); if (result[18] & 0x02) DBG (ERROR_MESSAGE, "Sense: SRAM Error.\n"); if (result[18] & 0x01) DBG (ERROR_MESSAGE, "Sense: ROM Error.\n"); if (result[19] & 0x10) DBG (ERROR_MESSAGE, "Sense: ICP (CPU) Error.\n"); if (result[19] & 0x02) DBG (ERROR_MESSAGE, "Sense: Over light. (Too bright lamp ?).\n"); break; default: DBG (ERROR_MESSAGE, "Sense: Unselected Scanner model. Please report this.\n"); break; } } DBG (USER_MESSAGE, "Sense: Optical gain %u.\n", (unsigned int) result[20]); return status; #endif /* NEUTRALIZE_BACKEND */ } static SANE_Status attach (const char *devname, Apple_Device ** devp, int may_wait) { char result[INQ_LEN]; const char *model_name = result + 44; int fd, apple_scanner, fw_revision; Apple_Device *dev; SANE_Status status; size_t size; for (dev = first_dev; dev; dev = dev->next) if (strcmp (dev->sane.name, devname) == 0) { if (devp) *devp = dev; return SANE_STATUS_GOOD; } DBG (USER_MESSAGE, "attach: opening %s\n", devname); #ifdef NEUTRALIZE_BACKEND result[0]=0x06; strcpy(result + 8, "APPLE "); if (APPLE_MODEL_SELECT==APPLESCANNER) strcpy(result + 16, "SCANNER A9M0337 "); if (APPLE_MODEL_SELECT==ONESCANNER) strcpy(result + 16, "SCANNER II "); if (APPLE_MODEL_SELECT==COLORONESCANNER) strcpy(result + 16, "SCANNER III "); #else status = sanei_scsi_open (devname, &fd, sense_handler, 0); if (status != SANE_STATUS_GOOD) { DBG (ERROR_MESSAGE, "attach: open failed (%s)\n", sane_strstatus (status)); return SANE_STATUS_INVAL; } if (may_wait) wait_ready (fd); DBG (USER_MESSAGE, "attach: sending INQUIRY\n"); size = sizeof (result); status = sanei_scsi_cmd (fd, inquiry, sizeof (inquiry), result, &size); if (status != SANE_STATUS_GOOD) { DBG (ERROR_MESSAGE, "attach: inquiry failed (%s)\n", sane_strstatus (status)); sanei_scsi_close (fd); return status; } status = wait_ready (fd); sanei_scsi_close (fd); if (status != SANE_STATUS_GOOD) return status; #endif /* NEUTRALIZE_BACKEND */ /* check for old format: */ apple_scanner = (strncmp (result + 8, "APPLE ", 8) == 0); model_name = result + 16; apple_scanner = apple_scanner && (result[0] == 0x06); if (!apple_scanner) { DBG (ERROR_MESSAGE, "attach: device doesn't look like an Apple scanner" "(result[0]=%#02x)\n", result[0]); return SANE_STATUS_INVAL; } /* get firmware revision as BCD number: */ fw_revision = (result[32] - '0') << 8 | (result[34] - '0') << 4 | (result[35] - '0'); DBG (USER_MESSAGE, "attach: firmware revision %d.%02x\n", fw_revision >> 8, fw_revision & 0xff); dev = malloc (sizeof (*dev)); if (!dev) return SANE_STATUS_NO_MEM; memset (dev, 0, sizeof (*dev)); dev->sane.name = strdup (devname); dev->sane.vendor = "Apple"; dev->sane.model = strndup (model_name, 16); dev->sane.type = "flatbed scanner"; dev->x_range.min = 0; dev->x_range.max = SANE_FIX (8.51 * MM_PER_INCH); dev->x_range.quant = 0; dev->y_range.min = 0; dev->y_range.max = SANE_FIX (14.0 * MM_PER_INCH); dev->y_range.quant = 0; dev->MaxHeight = 16800; if (strncmp (model_name, "SCANNER A9M0337 ", 16) == 0) { dev->ScannerModel = APPLESCANNER; dev->dpi_range.min = SANE_FIX (75); dev->dpi_range.max = SANE_FIX (300); dev->dpi_range.quant = SANE_FIX (1); dev->MaxWidth = 10208; } else if (strncmp (model_name, "SCANNER II ", 16) == 0) { dev->ScannerModel = ONESCANNER; dev->dpi_range.min = SANE_FIX (72); dev->dpi_range.max = SANE_FIX (300); dev->dpi_range.quant = SANE_FIX (1); dev->MaxWidth = 10200; } else if (strncmp (model_name, "SCANNER III ", 16) == 0) { dev->ScannerModel = COLORONESCANNER; dev->dpi_range.min = SANE_FIX (72); dev->dpi_range.max = SANE_FIX (300); dev->dpi_range.quant = SANE_FIX (1); dev->MaxWidth = 10200; } else { DBG (ERROR_MESSAGE, "attach: Cannot found Apple scanner in the neighborhood\n"); free (dev); return SANE_STATUS_INVAL; } DBG (USER_MESSAGE, "attach: found Apple scanner model %s (%s)\n", dev->sane.model, dev->sane.type); ++num_devices; dev->next = first_dev; first_dev = dev; if (devp) *devp = dev; return SANE_STATUS_GOOD; } static size_t max_string_size (const SANE_String_Const strings[]) { size_t size, max_size = 0; int i; for (i = 0; strings[i]; ++i) { size = strlen (strings[i]) + 1; if (size > max_size) max_size = size; } return max_size; } static SANE_Status scan_area_and_windows (Apple_Scanner * s) { uint8_t cmd[10 + 8 + 42]; #define CMD cmd + 0 #define WH cmd + 10 #define WP WH + 8 #ifdef NEUTRALIZE_BACKEND return SANE_STATUS_GOOD; #else /* setup SCSI command (except length): */ memset (cmd, 0, sizeof (cmd)); cmd[0] = APPLE_SCSI_AREA_AND_WINDOWS; if (s->hw->ScannerModel == COLORONESCANNER) { STORE24 (CMD + 6, 50); STORE16 (WH + 6, 42); } else { STORE24 (CMD + 6, 48); STORE16 (WH + 6, 40); } /* Store resolution. First X, the Y */ STORE16 (WP + 2, s->val[OPT_RESOLUTION].w); STORE16 (WP + 4, s->val[OPT_RESOLUTION].w); /* Now the Scanner Window in Scanner Parameters */ STORE32 (WP + 6, s->ULx); STORE32 (WP + 10, s->ULy); STORE32 (WP + 14, s->Width); STORE32 (WP + 18, s->Height); /* Now The Enhansment Group */ STORE8 (WP + 22, s->val[OPT_BRIGHTNESS].w); STORE8 (WP + 23, s->val[OPT_THRESHOLD].w); STORE8 (WP + 24, s->val[OPT_CONTRAST].w); /* The Mode */ if (!strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART)) STORE8 (WP + 25, 0) else if (!strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_HALFTONE)) STORE8 (WP + 25, 1) else if (!strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY) || !strcmp (s->val[OPT_MODE].s, "Gray16")) STORE8 (WP + 25, 2) else if (!strcmp (s->val[OPT_MODE].s, "BiColor")) STORE8 (WP + 25, 3) else if (!strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR)) STORE8 (WP + 25, 5) else { DBG (ERROR_MESSAGE, "Cannot much mode %s\n", s->val[OPT_MODE].s); return SANE_STATUS_INVAL; } STORE8 (WP + 26, s->bpp) /* HalfTone */ if (s->hw->ScannerModel != COLORONESCANNER) { if (!strcmp (s->val[OPT_HALFTONE_PATTERN].s, "spiral4x4")) STORE16 (WP + 27, 0) else if (!strcmp (s->val[OPT_HALFTONE_PATTERN].s, "bayer4x4")) STORE16 (WP + 27, 1) else if (!strcmp (s->val[OPT_HALFTONE_PATTERN].s, "download")) STORE16 (WP + 27, 1) else if (!strcmp (s->val[OPT_HALFTONE_PATTERN].s, "spiral8x8")) STORE16 (WP + 27, 3) else if (!strcmp (s->val[OPT_HALFTONE_PATTERN].s, "bayer8x8")) STORE16 (WP + 27, 4) else { DBG (ERROR_MESSAGE, "Cannot much haftone pattern %s\n", s->val[OPT_HALFTONE_PATTERN].s); return SANE_STATUS_INVAL; } } /* Padding Type */ STORE8 (WP + 29, 3); if (s->hw->ScannerModel == COLORONESCANNER) { if (s->val[OPT_VOLT_REF].w) { STORE8(WP+40,s->val[OPT_VOLT_REF_TOP].w); STORE8(WP+41,s->val[OPT_VOLT_REF_BOTTOM].w); } else { STORE8(WP+40,0); STORE8(WP+41,0); } return sanei_scsi_cmd (s->fd, cmd, sizeof (cmd), 0, 0); } else return sanei_scsi_cmd (s->fd, cmd, sizeof (cmd) - 2, 0, 0); #endif /* NEUTRALIZE_BACKEND */ } static SANE_Status mode_select (Apple_Scanner * s) { uint8_t cmd[6 + 12]; #define CMD cmd + 0 #define PP cmd + 6 /* setup SCSI command (except length): */ memset (cmd, 0, sizeof (cmd)); cmd[0] = APPLE_SCSI_MODE_SELECT; /* Apple Hardware Magic */ STORE8 (CMD + 1, 0x10); /* Parameter list length */ STORE8 (CMD + 4, 12); STORE8 (PP + 5, 6); if (s->val[OPT_LAMP].w) *(PP+8) |= 1; switch (s->hw->ScannerModel) { case APPLESCANNER: if (!strcmp (s->val[OPT_GRAYMAP].s, "dark")) STORE8 (PP + 6, 0) else if (!strcmp (s->val[OPT_GRAYMAP].s, "normal")) STORE8 (PP + 6, 1) else if (!strcmp (s->val[OPT_GRAYMAP].s, "light")) STORE8 (PP + 6, 2) else { DBG (ERROR_MESSAGE, "Cannot mach GrayMap Function %s\n", s->val[OPT_GRAYMAP].s); return SANE_STATUS_INVAL; } /* And the auto background threshold */ STORE8 (PP + 7, s->val[OPT_AUTOBACKGROUND_THRESHOLD].w) break; case ONESCANNER: if (s->val[OPT_LED].w) *(PP+7) |= 4; if (s->val[OPT_CCD].w) *(PP+8) |= 2; if (!strcmp (s->val[OPT_SPEED].s, "high")) *(PP+8) |= 4; else if (!strcmp (s->val[OPT_SPEED].s, "high wo H/S")) *(PP+8) |= 8; else if (!strcmp (s->val[OPT_SPEED].s, "normal")) { /* Do nothing. Zeros are great */} else { DBG (ERROR_MESSAGE, "Cannot mach speed selection %s\n", s->val[OPT_SPEED].s); return SANE_STATUS_INVAL; } break; case COLORONESCANNER: if (s->val[OPT_LED].w) *(PP+7) |= 4; if (!s->val[OPT_CUSTOM_GAMMA].w) *(PP+7) |= 2; if (!s->val[OPT_CUSTOM_CCT].w) *(PP+7) |= 1; if (s->val[OPT_MTF_CIRCUIT].w) *(PP+8) |= 16; if (s->val[OPT_ICP].w) *(PP+8) |= 8; if (s->val[OPT_POLARITY].w) *(PP+8) |= 4; if (s->val[OPT_CCD].w) *(PP+8) |= 2; if (!strcmp (s->val[OPT_COLOR_SENSOR].s, "All")) STORE8 (PP + 9, 0) else if (!strcmp (s->val[OPT_COLOR_SENSOR].s, "Red")) STORE8 (PP + 9, 1) else if (!strcmp (s->val[OPT_COLOR_SENSOR].s, "Green")) STORE8 (PP + 9, 2) else if (!strcmp (s->val[OPT_COLOR_SENSOR].s, "Blue")) STORE8 (PP + 9, 3) else { DBG (ERROR_MESSAGE, "Cannot mach Color Sensor for gray scans %s\n", s->val[OPT_COLOR_SENSOR].s); return SANE_STATUS_INVAL; } break; default: DBG(ERROR_MESSAGE,"Bad Scanner.\n"); break; } #ifdef NEUTRALIZE_BACKEND return SANE_STATUS_GOOD; #else return sanei_scsi_cmd (s->fd, cmd, sizeof (cmd), 0, 0); #endif /* NEUTRALIZE_BACKEND */ } static SANE_Status start_scan (Apple_Scanner * s) { SANE_Status status; uint8_t start[7]; memset (start, 0, sizeof (start)); start[0] = APPLE_SCSI_START; start[4] = 1; switch (s->hw->ScannerModel) { case APPLESCANNER: if (s->val[OPT_WAIT].w) start[5]=0x80; /* NOT TODO NoHome */ break; case ONESCANNER: if (!s->val[OPT_CALIBRATE].w) start[5]=0x20; break; case COLORONESCANNER: break; default: DBG(ERROR_MESSAGE,"Bad Scanner.\n"); break; } #ifdef NEUTRALIZE_BACKEND return SANE_STATUS_GOOD; #else status = sanei_scsi_cmd (s->fd, start, sizeof (start), 0, 0); return status; #endif /* NEUTRALIZE_BACKEND */ } static SANE_Status calc_parameters (Apple_Scanner * s) { SANE_String val = s->val[OPT_MODE].s; SANE_Status status = SANE_STATUS_GOOD; SANE_Bool OutOfRangeX, OutOfRangeY, Protect = SANE_TRUE; SANE_Int xqstep, yqstep; DBG (FLOW_CONTROL, "Entering calc_parameters\n"); if (!strcmp (val, SANE_VALUE_SCAN_MODE_LINEART)) { s->params.last_frame = SANE_TRUE; s->params.format = SANE_FRAME_GRAY; s->params.depth = 1; s->bpp = 1; } else if (!strcmp (val, SANE_VALUE_SCAN_MODE_HALFTONE)) { s->params.last_frame = SANE_TRUE; s->params.format = SANE_FRAME_GRAY; s->params.depth = 1; s->bpp = 1; } else if (!strcmp (val, "Gray16")) { s->params.last_frame = SANE_TRUE; s->params.format = SANE_FRAME_GRAY; s->params.depth = 8; s->bpp = 4; } else if (!strcmp (val, SANE_VALUE_SCAN_MODE_GRAY)) { s->params.last_frame = SANE_TRUE; s->params.format = SANE_FRAME_GRAY; s->params.depth = 8; s->bpp = 8; } else if (!strcmp (val, "BiColor")) { s->params.last_frame = SANE_TRUE; s->params.format = SANE_FRAME_RGB; s->params.depth = 24; s->bpp = 3; } else if (!strcmp (val, SANE_VALUE_SCAN_MODE_COLOR)) { s->params.last_frame = SANE_FALSE; s->params.format = SANE_FRAME_RED; s->params.depth = 24; s->bpp = 24; } else { DBG (ERROR_MESSAGE, "calc_parameters: Invalid mode %s\n", (char *) val); status = SANE_STATUS_INVAL; } s->ulx = SANE_UNFIX (s->val[OPT_TL_X].w) / MM_PER_INCH; s->uly = SANE_UNFIX (s->val[OPT_TL_Y].w) / MM_PER_INCH; s->wx = SANE_UNFIX (s->val[OPT_BR_X].w) / MM_PER_INCH - s->ulx; s->wy = SANE_UNFIX (s->val[OPT_BR_Y].w) / MM_PER_INCH - s->uly; DBG (VARIABLE_CONTROL, "Desired [%g,%g] to +[%g,%g]\n", s->ulx, s->uly, s->wx, s->wy); xqstep = XQSTEP (s->val[OPT_RESOLUTION].w, s->bpp); yqstep = YQSTEP (s->val[OPT_RESOLUTION].w); DBG (VARIABLE_CONTROL, "Quantization steps of [%u,%u].\n", xqstep, yqstep); s->ULx = xquant (s->ulx, s->val[OPT_RESOLUTION].w, s->bpp, 0); s->Width = xquant (s->wx, s->val[OPT_RESOLUTION].w, s->bpp, 1); s->ULy = yquant (s->uly, s->val[OPT_RESOLUTION].w, 0); s->Height = yquant (s->wy, s->val[OPT_RESOLUTION].w, 1); DBG (VARIABLE_CONTROL, "Scanner [%u,%u] to +[%u,%u]\n", s->ULx, s->ULy, s->Width, s->Height); do { OutOfRangeX = SANE_FALSE; OutOfRangeY = SANE_FALSE; if (s->ULx + s->Width > s->hw->MaxWidth) { OutOfRangeX = SANE_TRUE; Protect = SANE_FALSE; s->Width -= xqstep; } if (s->ULy + s->Height > s->hw->MaxHeight) { OutOfRangeY = SANE_TRUE; Protect = SANE_FALSE; s->Height -= yqstep; } DBG (VARIABLE_CONTROL, "Adapting to [%u,%u] to +[%u,%u]\n", s->ULx, s->ULy, s->Width, s->Height); } while (OutOfRangeX || OutOfRangeY); s->ulx = (double) s->ULx / 1200; s->uly = (double) s->ULy / 1200; s->wx = (double) s->Width / 1200; s->wy = (double) s->Height / 1200; DBG (VARIABLE_CONTROL, "Real [%g,%g] to +[%g,%g]\n", s->ulx, s->uly, s->wx, s->wy); /* TODO: Remove this ugly hack (Protect). Read to learn why! NOTE: I hate the Fixed Sane type. This type gave me a terrible headache and a difficult bug to find out. The xscanimage frontend was looping and segfaulting all the time with random order. The problem was the following: * You select new let's say BR_X * sane_control_option returns info inexact (always for BR_X) but does not modify val because it fits under the constrained quantization. Hm... Well sane_control doesn't change the (double) value of val but the Fixed interpatation may have been change (by 1 or something small). So now we should protect the val if the change is smaller than the quantization step or better under the SANE_[UN]FIX accuracy. Looks like for two distinct val (Fixed) values we get the same double. How come ? This hack fixed the looping situation. Unfortunately SIGSEGV remains when you touch the slice bars (thouhg not all the time). But it's OK if you select scan_area from the preview window (cool). */ if (!Protect) { s->val[OPT_TL_X].w = SANE_FIX (s->ulx * MM_PER_INCH); s->val[OPT_TL_Y].w = SANE_FIX (s->uly * MM_PER_INCH); s->val[OPT_BR_X].w = SANE_FIX ((s->ulx + s->wx) * MM_PER_INCH); s->val[OPT_BR_Y].w = SANE_FIX ((s->uly + s->wy) * MM_PER_INCH); } else DBG (VARIABLE_CONTROL, "Not adapted. Protecting\n"); DBG (VARIABLE_CONTROL, "GUI [%g,%g] to [%g,%g]\n", SANE_UNFIX (s->val[OPT_TL_X].w), SANE_UNFIX (s->val[OPT_TL_Y].w), SANE_UNFIX (s->val[OPT_BR_X].w), SANE_UNFIX (s->val[OPT_BR_Y].w)); /* NOTE: remember that AppleScanners quantize the scan area to be a byte multiple */ s->params.pixels_per_line = s->Width * s->val[OPT_RESOLUTION].w / 1200; s->params.lines = s->Height * s->val[OPT_RESOLUTION].w / 1200; s->params.bytes_per_line = s->params.pixels_per_line * s->params.depth / 8; DBG (VARIABLE_CONTROL, "format=%d\n", s->params.format); DBG (VARIABLE_CONTROL, "last_frame=%d\n", s->params.last_frame); DBG (VARIABLE_CONTROL, "lines=%d\n", s->params.lines); DBG (VARIABLE_CONTROL, "depth=%d (%d)\n", s->params.depth, s->bpp); DBG (VARIABLE_CONTROL, "pixels_per_line=%d\n", s->params.pixels_per_line); DBG (VARIABLE_CONTROL, "bytes_per_line=%d\n", s->params.bytes_per_line); DBG (VARIABLE_CONTROL, "Pixels %dx%dx%d\n", s->params.pixels_per_line, s->params.lines, 1 << s->params.depth); DBG (FLOW_CONTROL, "Leaving calc_parameters\n"); return status; } static SANE_Status gamma_update(SANE_Handle handle) { Apple_Scanner *s = handle; if (s->hw->ScannerModel == COLORONESCANNER) { if ( !strcmp(s->val[OPT_MODE].s,SANE_VALUE_SCAN_MODE_GRAY) || !strcmp(s->val[OPT_MODE].s,"Gray16") ) { ENABLE (OPT_CUSTOM_GAMMA); if (s->val[OPT_CUSTOM_GAMMA].w) { ENABLE (OPT_DOWNLOAD_GAMMA); if (! strcmp(s->val[OPT_COLOR_SENSOR].s,"All")) { ENABLE (OPT_GAMMA_VECTOR_R); ENABLE (OPT_GAMMA_VECTOR_G); ENABLE (OPT_GAMMA_VECTOR_B); } if (! strcmp(s->val[OPT_COLOR_SENSOR].s,"Red")) { ENABLE (OPT_GAMMA_VECTOR_R); DISABLE(OPT_GAMMA_VECTOR_G); DISABLE (OPT_GAMMA_VECTOR_B); } if (! strcmp(s->val[OPT_COLOR_SENSOR].s,"Green")) { DISABLE (OPT_GAMMA_VECTOR_R); ENABLE (OPT_GAMMA_VECTOR_G); DISABLE (OPT_GAMMA_VECTOR_B); } if (! strcmp(s->val[OPT_COLOR_SENSOR].s,"Blue")) { DISABLE (OPT_GAMMA_VECTOR_R); DISABLE (OPT_GAMMA_VECTOR_G); ENABLE (OPT_GAMMA_VECTOR_B); } } else /* Not custom gamma */ { goto discustom; } } else if (!strcmp(s->val[OPT_MODE].s,SANE_VALUE_SCAN_MODE_COLOR)) { ENABLE (OPT_CUSTOM_GAMMA); if (s->val[OPT_CUSTOM_GAMMA].w) { ENABLE (OPT_DOWNLOAD_GAMMA); ENABLE (OPT_GAMMA_VECTOR_R); ENABLE (OPT_GAMMA_VECTOR_G); ENABLE (OPT_GAMMA_VECTOR_B); } else /* Not custom gamma */ { goto discustom; } } else /* Not Gamma capable mode */ { goto disall; } } /* Not Gamma capable Scanner */ else { disall: DISABLE (OPT_CUSTOM_GAMMA); discustom: DISABLE (OPT_GAMMA_VECTOR_R); DISABLE (OPT_GAMMA_VECTOR_G); DISABLE (OPT_GAMMA_VECTOR_B); DISABLE (OPT_DOWNLOAD_GAMMA); } return SANE_STATUS_GOOD; } static SANE_Status mode_update (SANE_Handle handle, char *val) { Apple_Scanner *s = handle; SANE_Bool cct=SANE_FALSE; SANE_Bool UseThreshold=SANE_FALSE; DISABLE(OPT_COLOR_SENSOR); if (!strcmp (val, SANE_VALUE_SCAN_MODE_LINEART)) { if (s->hw->ScannerModel == APPLESCANNER) ENABLE (OPT_AUTOBACKGROUND); else DISABLE (OPT_AUTOBACKGROUND); DISABLE (OPT_HALFTONE_PATTERN); UseThreshold=SANE_TRUE; } else if (!strcmp (val, SANE_VALUE_SCAN_MODE_HALFTONE)) { DISABLE (OPT_AUTOBACKGROUND); ENABLE (OPT_HALFTONE_PATTERN); } else if (!strcmp (val, "Gray16") || !strcmp (val, SANE_VALUE_SCAN_MODE_GRAY)) { DISABLE (OPT_AUTOBACKGROUND); DISABLE (OPT_HALFTONE_PATTERN); if (s->hw->ScannerModel == COLORONESCANNER) ENABLE(OPT_COLOR_SENSOR); } /* End of Gray */ else if (!strcmp (val, "BiColor")) { DISABLE (OPT_AUTOBACKGROUND); DISABLE (OPT_HALFTONE_PATTERN); UseThreshold=SANE_TRUE; } else if (!strcmp (val, SANE_VALUE_SCAN_MODE_COLOR)) { DISABLE (OPT_AUTOBACKGROUND); DISABLE (OPT_HALFTONE_PATTERN); cct=SANE_TRUE; } else { DBG (ERROR_MESSAGE, "Invalid mode %s\n", (char *) val); return SANE_STATUS_INVAL; } /* Second hand dependencies of mode option */ /* Looks like code doubling */ if (UseThreshold) { DISABLE (OPT_BRIGHTNESS); DISABLE (OPT_CONTRAST); DISABLE (OPT_VOLT_REF); DISABLE (OPT_VOLT_REF_TOP); DISABLE (OPT_VOLT_REF_BOTTOM); if (IS_ACTIVE (OPT_AUTOBACKGROUND) && s->val[OPT_AUTOBACKGROUND].w) { DISABLE (OPT_THRESHOLD); ENABLE (OPT_AUTOBACKGROUND_THRESHOLD); } else { ENABLE (OPT_THRESHOLD); DISABLE (OPT_AUTOBACKGROUND_THRESHOLD); } } else { DISABLE (OPT_THRESHOLD); DISABLE (OPT_AUTOBACKGROUND_THRESHOLD); if (s->hw->ScannerModel == COLORONESCANNER) { ENABLE (OPT_VOLT_REF); if (s->val[OPT_VOLT_REF].w) { ENABLE (OPT_VOLT_REF_TOP); ENABLE (OPT_VOLT_REF_BOTTOM); DISABLE (OPT_BRIGHTNESS); DISABLE (OPT_CONTRAST); } else { DISABLE (OPT_VOLT_REF_TOP); DISABLE (OPT_VOLT_REF_BOTTOM); ENABLE (OPT_BRIGHTNESS); ENABLE (OPT_CONTRAST); } } else { ENABLE (OPT_BRIGHTNESS); ENABLE (OPT_CONTRAST); } } if (IS_ACTIVE (OPT_HALFTONE_PATTERN) && !strcmp (s->val[OPT_HALFTONE_PATTERN].s, "download")) ENABLE (OPT_HALFTONE_FILE); else DISABLE (OPT_HALFTONE_FILE); if (cct) ENABLE (OPT_CUSTOM_CCT); else DISABLE (OPT_CUSTOM_CCT); if (cct && s->val[OPT_CUSTOM_CCT].w) { ENABLE(OPT_CCT); ENABLE(OPT_DOWNLOAD_CCT); } else { DISABLE(OPT_CCT); DISABLE(OPT_DOWNLOAD_CCT); } gamma_update (s); calc_parameters (s); return SANE_STATUS_GOOD; } static SANE_Status init_options (Apple_Scanner * s) { int i; memset (s->opt, 0, sizeof (s->opt)); memset (s->val, 0, sizeof (s->val)); for (i = 0; i < NUM_OPTIONS; ++i) { s->opt[i].size = sizeof (SANE_Word); s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* Hardware detect Information group: */ s->opt[OPT_HWDETECT_GROUP].title = "Hardware"; s->opt[OPT_HWDETECT_GROUP].desc = "Detected during hardware probing"; s->opt[OPT_HWDETECT_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_HWDETECT_GROUP].cap = 0; s->opt[OPT_HWDETECT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_MODEL].name = "model"; s->opt[OPT_MODEL].title = "Model"; s->opt[OPT_MODEL].desc = "Model and capabilities"; s->opt[OPT_MODEL].type = SANE_TYPE_STRING; s->opt[OPT_MODEL].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_MODEL].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_MODEL].size = max_string_size (SupportedModel); s->val[OPT_MODEL].s = strdup (SupportedModel[s->hw->ScannerModel]); /* "Mode" group: */ s->opt[OPT_MODE_GROUP].title = "Scan Mode"; s->opt[OPT_MODE_GROUP].desc = ""; s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MODE_GROUP].cap = 0; s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; halftone_pattern_list[0]="spiral4x4"; halftone_pattern_list[1]="bayer4x4"; halftone_pattern_list[2]="download"; halftone_pattern_list[3]=NULL; switch (s->hw->ScannerModel) { case APPLESCANNER: mode_list[0]=SANE_VALUE_SCAN_MODE_LINEART; mode_list[1]=SANE_VALUE_SCAN_MODE_HALFTONE; mode_list[2]="Gray16"; mode_list[3]=NULL; break; case ONESCANNER: mode_list[0]=SANE_VALUE_SCAN_MODE_LINEART; mode_list[1]=SANE_VALUE_SCAN_MODE_HALFTONE; mode_list[2]="Gray16"; mode_list[3]=SANE_VALUE_SCAN_MODE_GRAY; mode_list[4]=NULL; halftone_pattern_list[3]="spiral8x8"; halftone_pattern_list[4]="bayer8x8"; halftone_pattern_list[5]=NULL; break; case COLORONESCANNER: mode_list[0]=SANE_VALUE_SCAN_MODE_LINEART; mode_list[1]="Gray16"; mode_list[2]=SANE_VALUE_SCAN_MODE_GRAY; mode_list[3]="BiColor"; mode_list[4]=SANE_VALUE_SCAN_MODE_COLOR; mode_list[5]=NULL; break; default: break; } /* scan mode */ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; 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].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_MODE].size = max_string_size (mode_list); s->opt[OPT_MODE].constraint.string_list = mode_list; s->val[OPT_MODE].s = strdup (mode_list[0]); /* resolution */ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; /* TODO: Build the constraints on resolution in a smart way */ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_RESOLUTION].constraint.word_list = resbit_list; s->val[OPT_RESOLUTION].w = resbit_list[1]; /* preview */ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; s->val[OPT_PREVIEW].w = SANE_FALSE; /* "Geometry" group: */ s->opt[OPT_GEOMETRY_GROUP].title = "Geometry"; s->opt[OPT_GEOMETRY_GROUP].desc = ""; s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* top-left x */ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; s->opt[OPT_TL_X].unit = SANE_UNIT_MM; s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_X].constraint.range = &s->hw->x_range; s->val[OPT_TL_X].w = 0; /* top-left y */ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range; s->val[OPT_TL_Y].w = 0; /* bottom-right x */ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; s->opt[OPT_BR_X].unit = SANE_UNIT_MM; s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_X].constraint.range = &s->hw->x_range; s->val[OPT_BR_X].w = s->hw->x_range.max; /* bottom-right y */ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range; s->val[OPT_BR_Y].w = s->hw->y_range.max; /* "Enhancement" group: */ s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement"; s->opt[OPT_ENHANCEMENT_GROUP].desc = ""; s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_ENHANCEMENT_GROUP].cap = 0; s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* brightness */ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT; s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE; s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BRIGHTNESS].constraint.range = &byte_range; s->val[OPT_BRIGHTNESS].w = 128; /* contrast */ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST; s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST " This option is active for halftone/Grayscale modes only."; s->opt[OPT_CONTRAST].type = SANE_TYPE_INT; s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE; s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CONTRAST].constraint.range = &byte_range; s->val[OPT_CONTRAST].w = 1; /* 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_INT; s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE; s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_THRESHOLD].constraint.range = &byte_range; s->val[OPT_THRESHOLD].w = 128; /* AppleScanner Only options */ /* GrayMap Enhance */ s->opt[OPT_GRAYMAP].name = "graymap"; s->opt[OPT_GRAYMAP].title = "GrayMap"; s->opt[OPT_GRAYMAP].desc = "Fixed Gamma Enhancing"; s->opt[OPT_GRAYMAP].type = SANE_TYPE_STRING; s->opt[OPT_GRAYMAP].constraint_type = SANE_CONSTRAINT_STRING_LIST; if (s->hw->ScannerModel != APPLESCANNER) s->opt[OPT_GRAYMAP].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GRAYMAP].constraint.string_list = graymap_list; s->opt[OPT_GRAYMAP].size = max_string_size (graymap_list); s->val[OPT_GRAYMAP].s = strdup (graymap_list[1]); /* Enable auto background adjustment */ s->opt[OPT_AUTOBACKGROUND].name = "abj"; s->opt[OPT_AUTOBACKGROUND].title = "Use Auto Background Adjustment"; s->opt[OPT_AUTOBACKGROUND].desc = "Enables/Disables the Auto Background Adjustment feature"; if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) || (s->hw->ScannerModel != APPLESCANNER)) DISABLE (OPT_AUTOBACKGROUND); s->opt[OPT_AUTOBACKGROUND].type = SANE_TYPE_BOOL; s->val[OPT_AUTOBACKGROUND].w = SANE_FALSE; /* auto background adjustment threshold */ s->opt[OPT_AUTOBACKGROUND_THRESHOLD].name = "abjthreshold"; s->opt[OPT_AUTOBACKGROUND_THRESHOLD].title = "Auto Background Adjustment Threshold"; s->opt[OPT_AUTOBACKGROUND_THRESHOLD].desc = "Selects the automatically adjustable threshold"; s->opt[OPT_AUTOBACKGROUND_THRESHOLD].type = SANE_TYPE_INT; s->opt[OPT_AUTOBACKGROUND_THRESHOLD].unit = SANE_UNIT_NONE; s->opt[OPT_AUTOBACKGROUND_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; if (!IS_ACTIVE (OPT_AUTOBACKGROUND) || s->val[OPT_AUTOBACKGROUND].w == SANE_FALSE) s->opt[OPT_AUTOBACKGROUND_THRESHOLD].cap |= SANE_CAP_INACTIVE; s->opt[OPT_AUTOBACKGROUND_THRESHOLD].constraint.range = &byte_range; s->val[OPT_AUTOBACKGROUND_THRESHOLD].w = 64; /* AppleScanner & OneScanner options */ /* Select HalfTone Pattern */ s->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN; s->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN; s->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN; s->opt[OPT_HALFTONE_PATTERN].size = max_string_size (halftone_pattern_list); s->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING; s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_AUTOMATIC; s->opt[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_HALFTONE_PATTERN].constraint.string_list = halftone_pattern_list; s->val[OPT_HALFTONE_PATTERN].s = strdup (halftone_pattern_list[0]); if (s->hw->ScannerModel!=APPLESCANNER && s->hw->ScannerModel!=ONESCANNER) s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; /* halftone pattern file */ s->opt[OPT_HALFTONE_FILE].name = "halftone-pattern-file"; s->opt[OPT_HALFTONE_FILE].title = "Halftone Pattern File"; s->opt[OPT_HALFTONE_FILE].desc = "Download and use the specified file as halftone pattern"; s->opt[OPT_HALFTONE_FILE].type = SANE_TYPE_STRING; s->opt[OPT_HALFTONE_FILE].cap |= SANE_CAP_INACTIVE; s->opt[OPT_HALFTONE_FILE].size = 256; s->val[OPT_HALFTONE_FILE].s = "halftone.pgm"; /* Use volt_ref */ s->opt[OPT_VOLT_REF].name = "volt-ref"; s->opt[OPT_VOLT_REF].title = "Volt Reference"; s->opt[OPT_VOLT_REF].desc ="It's brightness equivalent."; s->opt[OPT_VOLT_REF].type = SANE_TYPE_BOOL; if (s->hw->ScannerModel!=COLORONESCANNER) s->opt[OPT_VOLT_REF].cap |= SANE_CAP_INACTIVE; s->val[OPT_VOLT_REF].w = SANE_FALSE; s->opt[OPT_VOLT_REF_TOP].name = "volt-ref-top"; s->opt[OPT_VOLT_REF_TOP].title = "Top Voltage Reference"; s->opt[OPT_VOLT_REF_TOP].desc = "I really do not know."; s->opt[OPT_VOLT_REF_TOP].type = SANE_TYPE_INT; s->opt[OPT_VOLT_REF_TOP].unit = SANE_UNIT_NONE; if (s->hw->ScannerModel!=COLORONESCANNER || s->val[OPT_VOLT_REF].w==SANE_FALSE) s->opt[OPT_VOLT_REF_TOP].cap |= SANE_CAP_INACTIVE; s->opt[OPT_VOLT_REF_TOP].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_VOLT_REF_TOP].constraint.range = &byte_range; s->val[OPT_VOLT_REF_TOP].w = 255; s->opt[OPT_VOLT_REF_BOTTOM].name = "volt-ref-bottom"; s->opt[OPT_VOLT_REF_BOTTOM].title = "Bottom Voltage Reference"; s->opt[OPT_VOLT_REF_BOTTOM].desc = "I really do not know."; s->opt[OPT_VOLT_REF_BOTTOM].type = SANE_TYPE_INT; s->opt[OPT_VOLT_REF_BOTTOM].unit = SANE_UNIT_NONE; if (s->hw->ScannerModel!=COLORONESCANNER || s->val[OPT_VOLT_REF].w==SANE_FALSE) s->opt[OPT_VOLT_REF_BOTTOM].cap |= SANE_CAP_INACTIVE; s->opt[OPT_VOLT_REF_BOTTOM].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_VOLT_REF_BOTTOM].constraint.range = &byte_range; s->val[OPT_VOLT_REF_BOTTOM].w = 1; /* Misc Functions: Advanced */ s->opt[OPT_MISC_GROUP].title = "Miscallaneous"; s->opt[OPT_MISC_GROUP].desc = ""; s->opt[OPT_MISC_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MISC_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_MISC_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* Turn On lamp during scan: All scanners */ s->opt[OPT_LAMP].name = "lamp"; s->opt[OPT_LAMP].title = "Lamp"; s->opt[OPT_LAMP].desc = "Hold the lamp on during scans."; s->opt[OPT_LAMP].type = SANE_TYPE_BOOL; s->val[OPT_LAMP].w = SANE_FALSE; /* AppleScanner Only options */ /* Wait for button to be pressed before scanning */ s->opt[OPT_WAIT].name = "wait"; s->opt[OPT_WAIT].title = "Wait"; s->opt[OPT_WAIT].desc = "You may issue the scan command but the actual " "scan will not start unless you press the button in the front of the " "scanner. It is a useful feature when you want to make a network scan (?) " "In the mean time you may halt your computer waiting for the SCSI bus " "to be free. If this happens just press the scanner button."; s->opt[OPT_WAIT].type = SANE_TYPE_BOOL; if (s->hw->ScannerModel != APPLESCANNER) s->opt[OPT_WAIT].cap |= SANE_CAP_INACTIVE; s->val[OPT_WAIT].w = SANE_FALSE; /* OneScanner Only options */ /* Calibrate before scanning ? */ s->opt[OPT_CALIBRATE].name = "calibrate"; s->opt[OPT_CALIBRATE].title = "Calibrate"; s->opt[OPT_CALIBRATE].desc = "You may avoid the calibration before " "scanning but this will lead you to lower image quality."; s->opt[OPT_CALIBRATE].type = SANE_TYPE_BOOL; if (s->hw->ScannerModel != ONESCANNER) s->opt[OPT_CALIBRATE].cap |= SANE_CAP_INACTIVE; s->val[OPT_CALIBRATE].w = SANE_TRUE; /* speed */ s->opt[OPT_SPEED].name = SANE_NAME_SCAN_SPEED; s->opt[OPT_SPEED].title = SANE_TITLE_SCAN_SPEED; s->opt[OPT_SPEED].desc = SANE_DESC_SCAN_SPEED; s->opt[OPT_SPEED].type = SANE_TYPE_STRING; if (s->hw->ScannerModel != ONESCANNER) s->opt[OPT_SPEED].cap |= SANE_CAP_INACTIVE; s->opt[OPT_SPEED].size = max_string_size (speed_list); s->opt[OPT_SPEED].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_SPEED].constraint.string_list = speed_list; s->val[OPT_SPEED].s = strdup (speed_list[0]); /* OneScanner & ColorOneScanner (LED && CCD) */ /* LED ? */ s->opt[OPT_LED].name = "led"; s->opt[OPT_LED].title = "LED"; s->opt[OPT_LED].desc ="This option controls the setting of the ambler LED."; s->opt[OPT_LED].type = SANE_TYPE_BOOL; if (s->hw->ScannerModel!=ONESCANNER && s->hw->ScannerModel!=COLORONESCANNER) s->opt[OPT_LED].cap |= SANE_CAP_INACTIVE; s->val[OPT_LED].w = SANE_TRUE; /* CCD Power ? */ s->opt[OPT_CCD].name = "ccd"; s->opt[OPT_CCD].title = "CCD Power"; s->opt[OPT_CCD].desc ="This option controls the power to the CCD array."; s->opt[OPT_CCD].type = SANE_TYPE_BOOL; if (s->hw->ScannerModel!=ONESCANNER && s->hw->ScannerModel!=COLORONESCANNER) s->opt[OPT_CCD].cap |= SANE_CAP_INACTIVE; s->val[OPT_CCD].w = SANE_TRUE; /* Use MTF Circuit */ s->opt[OPT_MTF_CIRCUIT].name = "mtf"; s->opt[OPT_MTF_CIRCUIT].title = "MTF Circuit"; s->opt[OPT_MTF_CIRCUIT].desc ="Turns the MTF (Modulation Transfer Function) " "peaking circuit on or off."; s->opt[OPT_MTF_CIRCUIT].type = SANE_TYPE_BOOL; if (s->hw->ScannerModel!=COLORONESCANNER) s->opt[OPT_MTF_CIRCUIT].cap |= SANE_CAP_INACTIVE; s->val[OPT_MTF_CIRCUIT].w = SANE_TRUE; /* Use ICP */ s->opt[OPT_ICP].name = "icp"; s->opt[OPT_ICP].title = "ICP"; s->opt[OPT_ICP].desc ="What is an ICP anyway?"; s->opt[OPT_ICP].type = SANE_TYPE_BOOL; if (s->hw->ScannerModel!=COLORONESCANNER) s->opt[OPT_ICP].cap |= SANE_CAP_INACTIVE; s->val[OPT_ICP].w = SANE_TRUE; /* Data Polarity */ s->opt[OPT_POLARITY].name = "polarity"; s->opt[OPT_POLARITY].title = "Data Polarity"; s->opt[OPT_POLARITY].desc = "Reverse black and white."; s->opt[OPT_POLARITY].type = SANE_TYPE_BOOL; if (s->hw->ScannerModel!=COLORONESCANNER) s->opt[OPT_POLARITY].cap |= SANE_CAP_INACTIVE; s->val[OPT_POLARITY].w = SANE_FALSE; /* Color Functions: Advanced */ s->opt[OPT_COLOR_GROUP].title = SANE_VALUE_SCAN_MODE_COLOR; s->opt[OPT_COLOR_GROUP].desc = ""; s->opt[OPT_COLOR_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_COLOR_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_COLOR_GROUP].constraint_type = SANE_CONSTRAINT_NONE; #ifdef CALIBRATION_FUNCTIONALITY /* OneScanner calibration vector */ s->opt[OPT_CALIBRATION_VECTOR].name = "calibration-vector"; s->opt[OPT_CALIBRATION_VECTOR].title = "Calibration Vector"; s->opt[OPT_CALIBRATION_VECTOR].desc = "Calibration vector for the CCD array."; s->opt[OPT_CALIBRATION_VECTOR].type = SANE_TYPE_INT; if (s->hw->ScannerModel!=ONESCANNER) s->opt[OPT_CALIBRATION_VECTOR].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CALIBRATION_VECTOR].unit = SANE_UNIT_NONE; s->opt[OPT_CALIBRATION_VECTOR].size = 2550 * sizeof (SANE_Word); s->opt[OPT_CALIBRATION_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CALIBRATION_VECTOR].constraint.range = &u8_range; s->val[OPT_CALIBRATION_VECTOR].wa = s->calibration_vector; /* ColorOneScanner calibration vector per band */ s->opt[OPT_CALIBRATION_VECTOR_RED].name = "calibration-vector-red"; s->opt[OPT_CALIBRATION_VECTOR_RED].title = "Calibration Vector for Red"; s->opt[OPT_CALIBRATION_VECTOR_RED].desc = "Calibration vector for the CCD array."; s->opt[OPT_CALIBRATION_VECTOR_RED].type = SANE_TYPE_INT; if (s->hw->ScannerModel!=COLORONESCANNER) s->opt[OPT_CALIBRATION_VECTOR_RED].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CALIBRATION_VECTOR_RED].unit = SANE_UNIT_NONE; s->opt[OPT_CALIBRATION_VECTOR_RED].size = 2700 * sizeof (SANE_Word); s->opt[OPT_CALIBRATION_VECTOR_RED].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CALIBRATION_VECTOR_RED].constraint.range = &u8_range; s->val[OPT_CALIBRATION_VECTOR_RED].wa = s->calibration_vector_red; /* ColorOneScanner calibration vector per band */ s->opt[OPT_CALIBRATION_VECTOR_GREEN].name = "calibration-vector-green"; s->opt[OPT_CALIBRATION_VECTOR_GREEN].title = "Calibration Vector for Green"; s->opt[OPT_CALIBRATION_VECTOR_GREEN].desc = "Calibration vector for the CCD array."; s->opt[OPT_CALIBRATION_VECTOR_GREEN].type = SANE_TYPE_INT; if (s->hw->ScannerModel!=COLORONESCANNER) s->opt[OPT_CALIBRATION_VECTOR].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CALIBRATION_VECTOR_GREEN].unit = SANE_UNIT_NONE; s->opt[OPT_CALIBRATION_VECTOR_GREEN].size = 2700 * sizeof (SANE_Word); s->opt[OPT_CALIBRATION_VECTOR_GREEN].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CALIBRATION_VECTOR_GREEN].constraint.range = &u8_range; s->val[OPT_CALIBRATION_VECTOR_GREEN].wa = s->calibration_vector_green; /* ColorOneScanner calibration vector per band */ s->opt[OPT_CALIBRATION_VECTOR_BLUE].name = "calibration-vector-blue"; s->opt[OPT_CALIBRATION_VECTOR_BLUE].title = "Calibration Vector for Blue"; s->opt[OPT_CALIBRATION_VECTOR_BLUE].desc = "Calibration vector for the CCD array."; s->opt[OPT_CALIBRATION_VECTOR_BLUE].type = SANE_TYPE_INT; if (s->hw->ScannerModel!=COLORONESCANNER) s->opt[OPT_CALIBRATION_VECTOR_BLUE].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CALIBRATION_VECTOR_BLUE].unit = SANE_UNIT_NONE; s->opt[OPT_CALIBRATION_VECTOR_BLUE].size = 2700 * sizeof (SANE_Word); s->opt[OPT_CALIBRATION_VECTOR_BLUE].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CALIBRATION_VECTOR_BLUE].constraint.range = &u8_range; s->val[OPT_CALIBRATION_VECTOR_BLUE].wa = s->calibration_vector_blue; #endif /* CALIBRATION_FUNCTIONALITY */ /* Action: Download calibration vector */ s->opt[OPT_DOWNLOAD_CALIBRATION_VECTOR].name = "download-calibration"; s->opt[OPT_DOWNLOAD_CALIBRATION_VECTOR].title = "Download Calibration Vector"; s->opt[OPT_DOWNLOAD_CALIBRATION_VECTOR].desc = "Download calibration vector to scanner"; s->opt[OPT_DOWNLOAD_CALIBRATION_VECTOR].type = SANE_TYPE_BUTTON; if (s->hw->ScannerModel!=ONESCANNER && s->hw->ScannerModel!=COLORONESCANNER) s->opt[OPT_DOWNLOAD_CALIBRATION_VECTOR].cap |= SANE_CAP_INACTIVE; /* custom-cct table */ s->opt[OPT_CUSTOM_CCT].name = "custom-cct"; s->opt[OPT_CUSTOM_CCT].title = "Use Custom CCT"; s->opt[OPT_CUSTOM_CCT].desc ="Determines whether a builtin " "or a custom 3x3 Color Correction Table (CCT) should be used."; s->opt[OPT_CUSTOM_CCT].type = SANE_TYPE_BOOL; s->opt[OPT_CUSTOM_CCT].cap |= SANE_CAP_INACTIVE; if (s->hw->ScannerModel!=COLORONESCANNER) s->opt[OPT_CUSTOM_CCT].cap |= SANE_CAP_INACTIVE; s->val[OPT_CUSTOM_CCT].w = SANE_FALSE; /* CCT */ s->opt[OPT_CCT].name = "cct"; s->opt[OPT_CCT].title = "3x3 Color Correction Table"; s->opt[OPT_CCT].desc = "TODO: Color Correction is currently unsupported"; s->opt[OPT_CCT].type = SANE_TYPE_FIXED; s->opt[OPT_CCT].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CCT].unit = SANE_UNIT_NONE; s->opt[OPT_CCT].size = 9 * sizeof (SANE_Word); s->opt[OPT_CCT].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CCT].constraint.range = &u8_range; s->val[OPT_CCT].wa = s->cct3x3; /* Action: custom 3x3 color correction table */ s->opt[OPT_DOWNLOAD_CCT].name = "download-3x3"; s->opt[OPT_DOWNLOAD_CCT].title = "Download 3x3 CCT"; s->opt[OPT_DOWNLOAD_CCT].desc = "Download 3x3 color correction table"; s->opt[OPT_DOWNLOAD_CCT].type = SANE_TYPE_BUTTON; if (s->hw->ScannerModel!=COLORONESCANNER) s->opt[OPT_DOWNLOAD_CCT].cap |= SANE_CAP_INACTIVE; /* custom-gamma table */ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL; s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE; /* red gamma vector */ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[0][0]; /* green gamma vector */ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[1][0]; /* blue gamma vector */ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[2][0]; /* Action: download gamma vectors table */ s->opt[OPT_DOWNLOAD_GAMMA].name = "download-gamma"; s->opt[OPT_DOWNLOAD_GAMMA].title = "Download Gamma Vector(s)"; s->opt[OPT_DOWNLOAD_GAMMA].desc = "Download Gamma Vector(s)."; s->opt[OPT_DOWNLOAD_GAMMA].type = SANE_TYPE_BUTTON; s->opt[OPT_DOWNLOAD_GAMMA].cap |= SANE_CAP_INACTIVE; s->opt[OPT_COLOR_SENSOR].name = "color-sensor"; s->opt[OPT_COLOR_SENSOR].title = "Gray scan with"; s->opt[OPT_COLOR_SENSOR].desc = "Select the color sensor to scan in gray mode."; s->opt[OPT_COLOR_SENSOR].type = SANE_TYPE_STRING; s->opt[OPT_COLOR_SENSOR].unit = SANE_UNIT_NONE; s->opt[OPT_COLOR_SENSOR].size = max_string_size (color_sensor_list); if (s->hw->ScannerModel!=COLORONESCANNER) s->opt[OPT_COLOR_SENSOR].cap |= SANE_CAP_INACTIVE; s->opt[OPT_COLOR_SENSOR].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_COLOR_SENSOR].constraint.string_list = color_sensor_list; s->val[OPT_COLOR_SENSOR].s = strdup(color_sensor_list[2]); mode_update (s, s->val[OPT_MODE].s); return SANE_STATUS_GOOD; } static SANE_Status attach_one (const char *dev) { attach (dev, 0, SANE_FALSE); return SANE_STATUS_GOOD; } SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { char dev_name[PATH_MAX]; size_t len; FILE *fp; (void) authorize; /* silence gcc */ DBG_INIT (); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); fp = sanei_config_open (APPLE_CONFIG_FILE); if (!fp) { /* default to /dev/scanner instead of insisting on config file */ attach ("/dev/scanner", 0, SANE_FALSE); return SANE_STATUS_GOOD; } while (sanei_config_read (dev_name, sizeof (dev_name), fp)) { if (dev_name[0] == '#') /* ignore line comments */ continue; len = strlen (dev_name); if (!len) continue; /* ignore empty lines */ if (strncmp (dev_name, "option", 6) == 0 && isspace (dev_name[6])) { const char *str = dev_name + 7; while (isspace (*str)) ++str; continue; } sanei_config_attach_matching_devices (dev_name, attach_one); } fclose (fp); return SANE_STATUS_GOOD; } void sane_exit (void) { Apple_Device *dev, *next; for (dev = first_dev; dev; dev = next) { next = dev->next; free ((void *) dev->sane.name); free ((void *) dev->sane.model); free (dev); } if (devlist) free (devlist); } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { Apple_Device *dev; int i; (void) local_only; /* silence gcc */ if (devlist) free (devlist); devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); if (!devlist) return SANE_STATUS_NO_MEM; i = 0; for (dev = first_dev; i < num_devices; dev = dev->next) devlist[i++] = &dev->sane; devlist[i++] = 0; *device_list = devlist; return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { Apple_Device *dev; SANE_Status status; Apple_Scanner *s; int i, j; if (devicename[0]) { for (dev = first_dev; dev; dev = dev->next) if (strcmp (dev->sane.name, devicename) == 0) break; if (!dev) { status = attach (devicename, &dev, SANE_TRUE); if (status != SANE_STATUS_GOOD) return status; } } else /* empty devicname -> use first device */ dev = first_dev; if (!dev) return SANE_STATUS_INVAL; s = malloc (sizeof (*s)); if (!s) return SANE_STATUS_NO_MEM; memset (s, 0, sizeof (*s)); s->fd = -1; s->hw = dev; for (i = 0; i < 3; ++i) for (j = 0; j < 256; ++j) s->gamma_table[i][j] = j; init_options (s); /* insert newly opened handle into list of open handles: */ s->next = first_handle; first_handle = s; *handle = s; return SANE_STATUS_GOOD; } void sane_close (SANE_Handle handle) { Apple_Scanner *prev, *s; /* remove handle from list of open handles: */ prev = 0; for (s = first_handle; s; s = s->next) { if (s == handle) break; prev = s; } if (!s) { DBG (ERROR_MESSAGE, "close: invalid handle %p\n", handle); return; /* oops, not a handle we know about */ } if (prev) prev->next = s->next; else first_handle = s->next; free (handle); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { Apple_Scanner *s = handle; if ((unsigned) option >= NUM_OPTIONS) return 0; return s->opt + option; } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { Apple_Scanner *s = handle; SANE_Status status; SANE_Word cap; DBG (FLOW_CONTROL, "(%s): Entering on control_option for option %s (%d).\n", (action == SANE_ACTION_GET_VALUE) ? "get" : "set", s->opt[option].name, option); if (val || action == SANE_ACTION_GET_VALUE) switch (s->opt[option].type) { case SANE_TYPE_STRING: DBG (FLOW_CONTROL, "Value %s\n", (action == SANE_ACTION_GET_VALUE) ? s->val[option].s : (char *) val); break; case SANE_TYPE_FIXED: { double v1, v2; SANE_Fixed f; v1 = SANE_UNFIX (s->val[option].w); f = *(SANE_Fixed *) val; v2 = SANE_UNFIX (f); DBG (FLOW_CONTROL, "Value %g (Fixed)\n", (action == SANE_ACTION_GET_VALUE) ? v1 : v2); break; } default: DBG (FLOW_CONTROL, "Value %u (Int).\n", (action == SANE_ACTION_GET_VALUE) ? s->val[option].w : *(SANE_Int *) val); break; } if (info) *info = 0; if (s->scanning) return SANE_STATUS_DEVICE_BUSY; if (option >= NUM_OPTIONS) return SANE_STATUS_INVAL; cap = s->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) return SANE_STATUS_INVAL; if (action == SANE_ACTION_GET_VALUE) { switch (option) { /* word options: */ case OPT_NUM_OPTS: case OPT_RESOLUTION: case OPT_PREVIEW: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_BRIGHTNESS: case OPT_CONTRAST: case OPT_THRESHOLD: case OPT_AUTOBACKGROUND: case OPT_AUTOBACKGROUND_THRESHOLD: case OPT_VOLT_REF: case OPT_VOLT_REF_TOP: case OPT_VOLT_REF_BOTTOM: case OPT_LAMP: case OPT_WAIT: case OPT_CALIBRATE: case OPT_LED: case OPT_CCD: case OPT_MTF_CIRCUIT: case OPT_ICP: case OPT_POLARITY: case OPT_CUSTOM_CCT: case OPT_CUSTOM_GAMMA: *(SANE_Word *) val = s->val[option].w; return SANE_STATUS_GOOD; /* word-array options: */ case OPT_CCT: case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: memcpy (val, s->val[option].wa, s->opt[option].size); return SANE_STATUS_GOOD; /* string options: */ case OPT_MODE: /* TODO: This is to protect the mode string to be ruined from the dll? backend. I do not know why. It's definitely an overkill and should be eliminated. status = sanei_constrain_value (s->opt + option, s->val[option].s, info); */ case OPT_MODEL: case OPT_GRAYMAP: case OPT_HALFTONE_PATTERN: case OPT_HALFTONE_FILE: case OPT_SPEED: case OPT_COLOR_SENSOR: strcpy (val, s->val[option].s); return SANE_STATUS_GOOD; /* Some Buttons */ case OPT_DOWNLOAD_CALIBRATION_VECTOR: case OPT_DOWNLOAD_CCT: case OPT_DOWNLOAD_GAMMA: return SANE_STATUS_INVAL; } } else if (action == SANE_ACTION_SET_VALUE) { if (!SANE_OPTION_IS_SETTABLE (cap)) return SANE_STATUS_INVAL; status = sanei_constrain_value (s->opt + option, val, info); if (status != SANE_STATUS_GOOD) return status; switch (option) { /* (mostly) side-effect-free word options: */ case OPT_RESOLUTION: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: s->val[option].w = *(SANE_Word *) val; calc_parameters (s); if (info) *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT; return SANE_STATUS_GOOD; /* fall through */ case OPT_BRIGHTNESS: case OPT_CONTRAST: case OPT_THRESHOLD: case OPT_AUTOBACKGROUND_THRESHOLD: case OPT_VOLT_REF_TOP: case OPT_VOLT_REF_BOTTOM: case OPT_LAMP: case OPT_WAIT: case OPT_CALIBRATE: case OPT_LED: case OPT_CCD: case OPT_MTF_CIRCUIT: case OPT_ICP: case OPT_POLARITY: s->val[option].w = *(SANE_Word *) val; return SANE_STATUS_GOOD; /* Simple Strings */ case OPT_GRAYMAP: case OPT_HALFTONE_FILE: case OPT_SPEED: if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); return SANE_STATUS_GOOD; /* Boolean */ case OPT_PREVIEW: s->val[option].w = *(SANE_Bool *) val; return SANE_STATUS_GOOD; /* side-effect-free word-array options: */ case OPT_CCT: case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: memcpy (s->val[option].wa, val, s->opt[option].size); return SANE_STATUS_GOOD; /* options with light side-effects: */ case OPT_HALFTONE_PATTERN: if (info) *info |= SANE_INFO_RELOAD_OPTIONS; if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); if (!strcmp (val, "download")) { return SANE_STATUS_UNSUPPORTED; /* TODO: ENABLE(OPT_HALFTONE_FILE); */ } else DISABLE (OPT_HALFTONE_FILE); return SANE_STATUS_GOOD; case OPT_AUTOBACKGROUND: if (info) *info |= SANE_INFO_RELOAD_OPTIONS; s->val[option].w = *(SANE_Bool *) val; if (*(SANE_Bool *) val) { DISABLE (OPT_THRESHOLD); ENABLE (OPT_AUTOBACKGROUND_THRESHOLD); } else { ENABLE (OPT_THRESHOLD); DISABLE (OPT_AUTOBACKGROUND_THRESHOLD); } return SANE_STATUS_GOOD; case OPT_VOLT_REF: if (info) *info |= SANE_INFO_RELOAD_OPTIONS; s->val[option].w = *(SANE_Bool *) val; if (*(SANE_Bool *) val) { DISABLE(OPT_BRIGHTNESS); DISABLE(OPT_CONTRAST); ENABLE(OPT_VOLT_REF_TOP); ENABLE(OPT_VOLT_REF_BOTTOM); } else { ENABLE(OPT_BRIGHTNESS); ENABLE(OPT_CONTRAST); DISABLE(OPT_VOLT_REF_TOP); DISABLE(OPT_VOLT_REF_BOTTOM); } return SANE_STATUS_GOOD; /* Actions: Buttons */ case OPT_DOWNLOAD_CALIBRATION_VECTOR: case OPT_DOWNLOAD_CCT: case OPT_DOWNLOAD_GAMMA: /* TODO: fix {down/up}loads */ return SANE_STATUS_UNSUPPORTED; case OPT_CUSTOM_CCT: s->val[OPT_CUSTOM_CCT].w=*(SANE_Word *) val; if (s->val[OPT_CUSTOM_CCT].w) { ENABLE(OPT_CCT); ENABLE(OPT_DOWNLOAD_CCT); } else { DISABLE(OPT_CCT); DISABLE(OPT_DOWNLOAD_CCT); } if (info) *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_CUSTOM_GAMMA: s->val[OPT_CUSTOM_GAMMA].w = *(SANE_Word *) val; gamma_update(s); if (info) *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_COLOR_SENSOR: if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); gamma_update(s); if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; /* HEAVY (RADIOACTIVE) SIDE EFFECTS: CHECKME */ case OPT_MODE: if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); status = mode_update (s, val); if (status != SANE_STATUS_GOOD) return status; if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; } /* End of switch */ } /* End of SET_VALUE */ return SANE_STATUS_INVAL; } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { Apple_Scanner *s = handle; DBG (FLOW_CONTROL, "Entering sane_get_parameters\n"); calc_parameters (s); if (params) *params = s->params; return SANE_STATUS_GOOD; } SANE_Status sane_start (SANE_Handle handle) { Apple_Scanner *s = handle; SANE_Status status; /* First make sure we have a current parameter set. Some of the parameters will be overwritten below, but that's OK. */ calc_parameters (s); if (s->fd < 0) { /* this is the first (and maybe only) pass... */ status = sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, 0); if (status != SANE_STATUS_GOOD) { DBG (ERROR_MESSAGE, "open: open of %s failed: %s\n", s->hw->sane.name, sane_strstatus (status)); return status; } } status = wait_ready (s->fd); if (status != SANE_STATUS_GOOD) { DBG (ERROR_MESSAGE, "open: wait_ready() failed: %s\n", sane_strstatus (status)); goto stop_scanner_and_return; } status = mode_select (s); if (status != SANE_STATUS_GOOD) { DBG (ERROR_MESSAGE, "sane_start: mode_select command failed: %s\n", sane_strstatus (status)); goto stop_scanner_and_return; } status = scan_area_and_windows (s); if (status != SANE_STATUS_GOOD) { DBG (ERROR_MESSAGE, "open: set scan area command failed: %s\n", sane_strstatus (status)); goto stop_scanner_and_return; } status = request_sense (s); if (status != SANE_STATUS_GOOD) { DBG (ERROR_MESSAGE, "sane_start: request_sense revealed error: %s\n", sane_strstatus (status)); goto stop_scanner_and_return; } s->scanning = SANE_TRUE; s->AbortedByUser = SANE_FALSE; status = start_scan (s); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; return SANE_STATUS_GOOD; stop_scanner_and_return: s->scanning = SANE_FALSE; s->AbortedByUser = SANE_FALSE; return status; } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { Apple_Scanner *s = handle; SANE_Status status; uint8_t get_data_status[10]; uint8_t read[10]; #ifdef RESERVE_RELEASE_HACK uint8_t reserve[6]; uint8_t release[6]; #endif uint8_t result[12]; size_t size; SANE_Int data_length = 0; SANE_Int data_av = 0; SANE_Int offset = 0; SANE_Int rread = 0; SANE_Bool Pseudo8bit = SANE_FALSE; #ifdef NEUTRALIZE_BACKEND *len=max_len; return SANE_STATUS_GOOD; #else *len = 0; if (!s->scanning) return SANE_STATUS_EOF; if (!strcmp (s->val[OPT_MODE].s, "Gray16")) Pseudo8bit = SANE_TRUE; /* TODO: The current function only implements for APPLESCANNER In order to use the COLORONESCANNER you have to study the docs to see how it the parameters get modified before scan. From this starting point it should be trivial to use a ONESCANNER int the gray256 mode but I don't have one from these pets in home. MF */ memset (get_data_status, 0, sizeof (get_data_status)); get_data_status[0] = APPLE_SCSI_GET_DATA_STATUS; get_data_status[1] = 1; /* Wait */ STORE24 (get_data_status + 6, sizeof (result)); memset (read, 0, sizeof (read)); read[0] = APPLE_SCSI_READ_SCANNED_DATA; #ifdef RESERVE_RELEASE_HACK memset (reserve, 0, sizeof (reserve)); reserve[0] = APPLE_SCSI_RESERVE; reserve[1]=CONTROLLER_SCSI_ID; reserve[1]=reserve[1] << 1; reserve[1]|=SETTHIRDPARTY; memset (release, 0, sizeof (release)); release[0] = APPLE_SCSI_RELEASE; release[1]=CONTROLLER_SCSI_ID; release[1]=reserve[1] << 1; release[1]|=SETTHIRDPARTY; #endif do { size = sizeof (result); status = sanei_scsi_cmd (s->fd, get_data_status, sizeof (get_data_status), result, &size); if (status != SANE_STATUS_GOOD) return status; if (!size) { DBG (ERROR_MESSAGE, "sane_read: cannot get_data_status.\n"); return SANE_STATUS_IO_ERROR; } data_length = READ24 (result); data_av = READ24 (result + 9); if (data_length) { /* if (result[3] & 1) Scanner Blocked: Retrieve data */ if ((result[3] & 1) || data_av) { DBG (IO_MESSAGE, "sane_read: (status) Available in scanner buffer %u.\n", data_av); if (Pseudo8bit) if ((data_av << 1) + offset > max_len) rread = (max_len - offset) >> 1; else rread = data_av; else if (data_av + offset > max_len) rread = max_len - offset; else rread = data_av; DBG (IO_MESSAGE, "sane_read: (action) Actual read request for %u bytes.\n", rread); size = rread; STORE24 (read + 6, rread); #ifdef RESERVE_RELEASE_HACK { SANE_Status status; DBG(IO_MESSAGE,"Reserving the SCSI bus.\n"); status=sanei_scsi_cmd (s->fd,reserve,sizeof(reserve),0,0); DBG(IO_MESSAGE,"Reserving... status:= %d\n",status); } #endif /* RESERVE_RELEASE_HACK */ status = sanei_scsi_cmd (s->fd, read, sizeof (read), buf + offset, &size); #ifdef RESERVE_RELEASE_HACK { SANE_Status status; DBG(IO_MESSAGE,"Releasing the SCSI bus.\n"); status=sanei_scsi_cmd (s->fd,release,sizeof(release),0,0); DBG(IO_MESSAGE,"Releasing... status:= %d\n",status); } #endif /* RESERVE_RELEASE_HACK */ if (Pseudo8bit) { SANE_Int byte; SANE_Int pos = offset + (rread << 1) - 1; SANE_Byte B; for (byte = offset + rread - 1; byte >= offset; byte--) { B = buf[byte]; buf[pos--] = 255 - (B << 4); /* low (right) nibble */ buf[pos--] = 255 - (B & 0xF0); /* high (left) nibble */ } offset += size << 1; } else offset += size; DBG (IO_MESSAGE, "sane_read: Buffer %u of %u full %g%%\n", offset, max_len, (double) (offset * 100. / max_len)); } } } while (offset < max_len && data_length != 0 && !s->AbortedByUser); if (s->AbortedByUser) { s->scanning = SANE_FALSE; status = sanei_scsi_cmd (s->fd, test_unit_ready, sizeof (test_unit_ready), 0, 0); if (status != SANE_STATUS_GOOD) return status; return SANE_STATUS_CANCELLED; } if (!data_length) /* If not blocked */ { s->scanning = SANE_FALSE; DBG (IO_MESSAGE, "sane_read: (status) Oups! No more data..."); if (!offset) { *len = 0; DBG (IO_MESSAGE, "EOF\n"); return SANE_STATUS_EOF; } else { *len = offset; DBG (IO_MESSAGE, "GOOD\n"); return SANE_STATUS_GOOD; } } DBG (FLOW_CONTROL, "sane_read: Normal Exiting (?), Aborted=%u, data_length=%u\n", s->AbortedByUser, data_length); *len = offset; return SANE_STATUS_GOOD; #endif /* NEUTRALIZE_BACKEND */ } void sane_cancel (SANE_Handle handle) { Apple_Scanner *s = handle; if (s->scanning) { if (s->AbortedByUser) { DBG (FLOW_CONTROL, "sane_cancel: Already Aborted. Please Wait...\n"); } else { s->scanning=SANE_FALSE; s->AbortedByUser = SANE_TRUE; DBG (FLOW_CONTROL, "sane_cancel: Signal Caught! Aborting...\n"); } } else { if (s->AbortedByUser) { DBG (FLOW_CONTROL, "sane_cancel: Scan has not been Initiated yet, " "or it is already aborted.\n"); s->AbortedByUser = SANE_FALSE; sanei_scsi_cmd (s->fd, test_unit_ready, sizeof (test_unit_ready), 0, 0); } else { DBG (FLOW_CONTROL, "sane_cancel: Scan has not been Initiated " "yet (or it's over).\n"); } } return; } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { DBG (FLOW_CONTROL,"sane_set_io_mode: Entering.\n"); (void) handle; /* silence gcc */ if (non_blocking) { DBG (FLOW_CONTROL, "sane_set_io_mode: Don't call me please. " "Unimplemented function\n"); return SANE_STATUS_UNSUPPORTED; } return SANE_STATUS_GOOD; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { (void) handle; /* silence gcc */ (void) fd; /* silence gcc */ DBG (FLOW_CONTROL, "sane_get_select_fd: Don't call me please. " "Unimplemented function\n"); return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/apple.conf.in000066400000000000000000000000301456256263500172400ustar00rootroot00000000000000scsi APPLE /dev/scanner backends-1.3.0/backend/apple.h000066400000000000000000000142531456256263500161510ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1998 Milon Firikis based on David Mosberger-Tang previous Work on mustek.c file from 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, see . 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 apple_h #define apple_h #include /* Warning: if you uncomment the next line you 'll get zero functionality. All the scanner specific function such as sane_read, attach and the others will return without doing anything. This way you can run the backend without an attached scanner just to see if it gets its control variables in a proper way. TODO: This could be a nice thing to do as a sane config option at runtime. This way one can debug the gui-ipc part of the backend without actually has the scanner. */ #if 0 #define NEUTRALIZE_BACKEND #define APPLE_MODEL_SELECT APPLESCANNER #endif #undef CALIBRATION_FUNCTIONALITY #undef RESERVE_RELEASE_HACK #ifdef RESERVE_RELEASE_HACK /* Also Try these with zero */ #define CONTROLLER_SCSI_ID 7 #define SETTHIRDPARTY 0x10 #endif #define ERROR_MESSAGE 1 #define USER_MESSAGE 5 #define FLOW_CONTROL 50 #define VARIABLE_CONTROL 70 #define DEBUG_SPECIAL 100 #define IO_MESSAGE 110 #define INNER_LOOP 120 /* mode values: */ enum Apple_Modes { APPLE_MODE_LINEART=0, APPLE_MODE_HALFTONE, APPLE_MODE_GRAY, APPLE_MODE_BICOLOR, EMPTY_DONT_USE_IT, APPLE_MODE_COLOR }; enum Apple_Option { OPT_NUM_OPTS = 0, OPT_HWDETECT_GROUP, OPT_MODEL, OPT_MODE_GROUP, OPT_MODE, OPT_RESOLUTION, OPT_PREVIEW, OPT_GEOMETRY_GROUP, OPT_TL_X, /* top-left x */ OPT_TL_Y, /* top-left y */ OPT_BR_X, /* bottom-right x */ OPT_BR_Y, /* bottom-right y */ OPT_ENHANCEMENT_GROUP, /* COMMON */ OPT_BRIGHTNESS, OPT_CONTRAST, OPT_THRESHOLD, /* AppleScanner only */ OPT_GRAYMAP, OPT_AUTOBACKGROUND, OPT_AUTOBACKGROUND_THRESHOLD, /* AppleScanner & OneScanner */ OPT_HALFTONE_PATTERN, OPT_HALFTONE_FILE, /* ColorOneScanner Only */ OPT_VOLT_REF, OPT_VOLT_REF_TOP, OPT_VOLT_REF_BOTTOM, /* misc : advanced */ OPT_MISC_GROUP, /* all */ OPT_LAMP, /* AppleScanner Only */ OPT_WAIT, /* OneScanner only */ OPT_CALIBRATE, OPT_SPEED, /* OneScanner && ColorOneScanner */ OPT_LED, OPT_CCD, /* ColorOneScanner only */ OPT_MTF_CIRCUIT, OPT_ICP, OPT_POLARITY, /* color group : advanced */ OPT_COLOR_GROUP, #ifdef CALIBRATION_FUNCTIONALITY /* OneScanner */ OPT_CALIBRATION_VECTOR, /* ColorOneScanner */ OPT_CALIBRATION_VECTOR_RED, OPT_CALIBRATION_VECTOR_GREEN, OPT_CALIBRATION_VECTOR_BLUE, #endif /* OneScanner && ColorOneScanner */ OPT_DOWNLOAD_CALIBRATION_VECTOR, /* ColorOneScanner */ OPT_CUSTOM_CCT, OPT_CCT, OPT_DOWNLOAD_CCT, OPT_CUSTOM_GAMMA, OPT_GAMMA_VECTOR_R, OPT_GAMMA_VECTOR_G, OPT_GAMMA_VECTOR_B, OPT_DOWNLOAD_GAMMA, OPT_COLOR_SENSOR, /* must come last: */ NUM_OPTIONS }; /* This is a hack to get fast the model of the Attached Scanner */ /* But it Works well and I am not considering in "fix" it */ enum SCANNERMODEL { OPT_NUM_SCANNERS = 0, APPLESCANNER, ONESCANNER, COLORONESCANNER, NUM_SCANNERS }; typedef struct Apple_Device { struct Apple_Device *next; SANE_Int ScannerModel; SANE_Device sane; SANE_Range dpi_range; SANE_Range x_range; SANE_Range y_range; SANE_Int MaxWidth; SANE_Int MaxHeight; unsigned flags; } Apple_Device; typedef struct Apple_Scanner { /* all the state needed to define a scan request: */ struct Apple_Scanner *next; SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; /* First we put here all the scan variables */ /* These are needed for converting back and forth the scan area */ SANE_Int bpp; /* The actual bpp, before scaling */ double ulx; double uly; double wx; double wy; SANE_Int ULx; SANE_Int ULy; SANE_Int Width; SANE_Int Height; /* TODO: Initialize this beasts with malloc instead of statically allocation. */ SANE_Int calibration_vector[2550]; SANE_Int calibration_vector_red[2700]; SANE_Int calibration_vector_green[2700]; SANE_Int calibration_vector_blue[2700]; SANE_Fixed cct3x3[9]; SANE_Int gamma_table[3][256]; SANE_Int halftone_pattern[64]; SANE_Bool scanning; SANE_Bool AbortedByUser; int pass; /* pass number */ SANE_Parameters params; int fd; /* SCSI filedescriptor */ /* scanner dependent/low-level state: */ Apple_Device *hw; } Apple_Scanner; #endif /* apple_h */ backends-1.3.0/backend/artec.c000066400000000000000000003104471456256263500161450ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1997 David Mosberger-Tang 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, see . 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 the Artec/Ultima scanners. Copyright (C) 1998-2000 Chris Pinkham Released under the terms of the GPL. *NO WARRANTY* Portions contributed by: David Leadbetter - A6000C (3-pass) Dick Bruijn - AT12 ********************************************************************* For feedback/information: cpinkham@corp.infi.net http://www4.infi.net/~cpinkham/sane/sane-artec-doc.html ********************************************************************* */ #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include "../include/_stdint.h" #include "../include/sane/sane.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_scsi.h" #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_config.h" #include #define BACKEND_NAME artec #define ARTEC_MAJOR 0 #define ARTEC_MINOR 5 #define ARTEC_SUB 16 #define ARTEC_LAST_MOD "05/26/2001 17:28 EST" #ifndef PATH_MAX #define PATH_MAX 1024 #endif #define ARTEC_CONFIG_FILE "artec.conf" #define ARTEC_MAX_READ_SIZE 32768 static int num_devices; static const SANE_Device **devlist = 0; static ARTEC_Device *first_dev; static ARTEC_Scanner *first_handle; static const SANE_String_Const mode_list[] = { SANE_VALUE_SCAN_MODE_LINEART, SANE_VALUE_SCAN_MODE_HALFTONE, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_COLOR, 0 }; static const SANE_String_Const filter_type_list[] = { "Mono", "Red", "Green", "Blue", 0 }; static const SANE_String_Const halftone_pattern_list[] = { "User defined (unsupported)", "4x4 Spiral", "4x4 Bayer", "8x8 Spiral", "8x8 Bayer", 0 }; static const SANE_Range u8_range = { 0, /* minimum */ 255, /* maximum */ 0 /* quantization */ }; #define INQ_LEN 0x60 static const uint8_t inquiry[] = { 0x12, 0x00, 0x00, 0x00, INQ_LEN, 0x00 }; static const uint8_t test_unit_ready[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static struct { SANE_String model; /* product model */ SANE_String type; /* type of scanner */ double width; /* width in inches */ double height; /* height in inches */ SANE_Word adc_bits; /* Analog-to-Digital Converter Bits */ SANE_Word setwindow_cmd_size; /* Set-Window command size */ SANE_Word max_read_size; /* Max Read size in bytes */ long flags; /* flags */ SANE_String horz_resolution_str; /* Horizontal resolution list */ SANE_String vert_resolution_str; /* Vertical resolution list */ } cap_data[] = { { "AT3", "flatbed", 8.3, 11, 8, 55, 32768, ARTEC_FLAG_CALIBRATE_RGB | ARTEC_FLAG_RGB_LINE_OFFSET | ARTEC_FLAG_RGB_CHAR_SHIFT | ARTEC_FLAG_OPT_CONTRAST | ARTEC_FLAG_GAMMA_SINGLE | ARTEC_FLAG_SEPARATE_RES | ARTEC_FLAG_SENSE_HANDLER | ARTEC_FLAG_SENSE_BYTE_19 | ARTEC_FLAG_ADF | ARTEC_FLAG_HALFTONE_PATTERN | ARTEC_FLAG_MBPP_NEGATIVE | ARTEC_FLAG_ONE_PASS_SCANNER, "50,100,200,300", "50,100,200,300,600" } , { "A6000C", "flatbed", 8.3, 14, 8, 55, 8192, /* some have reported that Calibration does not work the same as AT3 & A6000C+ ARTEC_FLAG_CALIBRATE_RGB | */ ARTEC_FLAG_OPT_CONTRAST | ARTEC_FLAG_OPT_BRIGHTNESS | ARTEC_FLAG_SEPARATE_RES | ARTEC_FLAG_SENSE_HANDLER | ARTEC_FLAG_ADF | ARTEC_FLAG_HALFTONE_PATTERN, "50,100,200,300", "50,100,200,300,600" } , { "A6000C PLUS", "flatbed", 8.3, 14, 8, 55, 8192, ARTEC_FLAG_CALIBRATE_RGB | ARTEC_FLAG_RGB_LINE_OFFSET | ARTEC_FLAG_RGB_CHAR_SHIFT | ARTEC_FLAG_OPT_CONTRAST | ARTEC_FLAG_GAMMA_SINGLE | ARTEC_FLAG_SEPARATE_RES | ARTEC_FLAG_SENSE_HANDLER | ARTEC_FLAG_SENSE_BYTE_19 | ARTEC_FLAG_ADF | ARTEC_FLAG_HALFTONE_PATTERN | ARTEC_FLAG_MBPP_NEGATIVE | ARTEC_FLAG_ONE_PASS_SCANNER, "50,100,200,300", "50,100,200,300,600" } , { "AT6", "flatbed", 8.3, 11, 10, 55, 32768, ARTEC_FLAG_CALIBRATE_RGB | ARTEC_FLAG_RGB_LINE_OFFSET | ARTEC_FLAG_RGB_CHAR_SHIFT | ARTEC_FLAG_OPT_CONTRAST | /* gamma not working totally correct yet. ARTEC_FLAG_GAMMA_SINGLE | */ ARTEC_FLAG_SEPARATE_RES | ARTEC_FLAG_SENSE_HANDLER | ARTEC_FLAG_ADF | ARTEC_FLAG_HALFTONE_PATTERN | ARTEC_FLAG_MBPP_NEGATIVE | ARTEC_FLAG_ONE_PASS_SCANNER, "50,100,200,300", "50,100,200,300,600" } , { "AT12", "flatbed", 8.5, 11, 12, 67, 32768, /* calibration works slower so disabled ARTEC_CALIBRATE_DARK_WHITE | */ /* gamma not working totally correct yet. ARTEC_FLAG_GAMMA | */ ARTEC_FLAG_OPT_CONTRAST | ARTEC_FLAG_SEPARATE_RES | ARTEC_FLAG_SENSE_HANDLER | ARTEC_FLAG_SENSE_ENH_18 | ARTEC_FLAG_SENSE_BYTE_22 | ARTEC_FLAG_SC_BUFFERS_LINES | ARTEC_FLAG_SC_HANDLES_OFFSET | ARTEC_FLAG_PIXEL_AVERAGING | ARTEC_FLAG_ENHANCE_LINE_EDGE | ARTEC_FLAG_ADF | ARTEC_FLAG_HALFTONE_PATTERN | ARTEC_FLAG_MBPP_NEGATIVE | ARTEC_FLAG_ONE_PASS_SCANNER, "25,50,100,200,300,400,500,600", "25,50,100,200,300,400,500,600,700,800,900,1000,1100,1200" } , { "AM12S", "flatbed", 8.26, 11.7, 12, 67, ARTEC_MAX_READ_SIZE, /* calibration works slower so disabled ARTEC_CALIBRATE_DARK_WHITE | */ /* gamma not working totally correct yet. ARTEC_FLAG_GAMMA | */ ARTEC_FLAG_RGB_LINE_OFFSET | ARTEC_FLAG_SEPARATE_RES | ARTEC_FLAG_IMAGE_REV_LR | ARTEC_FLAG_REVERSE_WINDOW | ARTEC_FLAG_SENSE_HANDLER | ARTEC_FLAG_SENSE_ENH_18 | ARTEC_FLAG_MBPP_NEGATIVE | ARTEC_FLAG_ONE_PASS_SCANNER, "50,100,300,600", "50,100,300,600,1200" } , }; /* store vendor and model if hardcoded in artec.conf */ static char artec_vendor[9] = ""; static char artec_model[17] = ""; /* file descriptor for debug data output */ static int debug_fd = -1; static char *artec_skip_whitespace (char *str) { while (isspace (*str)) ++str; return str; } static SANE_Status artec_str_list_to_word_list (SANE_Word ** word_list_ptr, SANE_String str) { SANE_Word *word_list; char *start; char *end; char temp_str[1024]; int comma_count = 1; if ((str == NULL) || (strlen (str) == 0)) { /* alloc space for word which stores length (0 in this case) */ word_list = (SANE_Word *) malloc (sizeof (SANE_Word)); if (word_list == NULL) return (SANE_STATUS_NO_MEM); word_list[0] = 0; *word_list_ptr = word_list; return (SANE_STATUS_GOOD); } /* make temp copy of input string (only hold 1024 for now) */ strncpy (temp_str, str, 1023); temp_str[1023] = '\0'; end = strchr (temp_str, ','); while (end != NULL) { comma_count++; start = end + 1; end = strchr (start, ','); } word_list = (SANE_Word *) calloc (comma_count + 1, sizeof (SANE_Word)); if (word_list == NULL) return (SANE_STATUS_NO_MEM); word_list[0] = comma_count; comma_count = 1; start = temp_str; end = strchr (temp_str, ','); while (end != NULL) { *end = '\0'; word_list[comma_count] = atol (start); start = end + 1; comma_count++; end = strchr (start, ','); } word_list[comma_count] = atol (start); *word_list_ptr = word_list; return (SANE_STATUS_GOOD); } static size_t artec_get_str_index (const SANE_String_Const strings[], char *str) { size_t index; index = 0; while ((strings[index]) && strcmp (strings[index], str)) { index++; } if (!strings[index]) { index = 0; } return (index); } static size_t max_string_size (const SANE_String_Const strings[]) { size_t size, max_size = 0; int i; for (i = 0; strings[i]; ++i) { size = strlen (strings[i]) + 1; if (size > max_size) max_size = size; } return (max_size); } /* DB added a sense handler */ /* last argument is expected to be a pointer to a Artec_Scanner structure */ static SANE_Status sense_handler (int fd, u_char * sense, void *arg) { ARTEC_Scanner *s = (ARTEC_Scanner *)arg; int err; err = 0; DBG(2, "sense fd: %d, data: %02x %02x %02x %02x %02x %02x %02x %02x " "%02x %02x %02x %02x %02x %02x %02x %02x\n", fd, sense[0], sense[1], sense[2], sense[3], sense[4], sense[5], sense[6], sense[7], sense[8], sense[9], sense[10], sense[11], sense[12], sense[13], sense[14], sense[15]); /* byte 18 info pertaining to ADF */ if ((s) && (s->hw->flags & ARTEC_FLAG_ADF)) { if (sense[18] & 0x01) { DBG (2, "sense: ADF PAPER JAM\n"); err++; } if (sense[18] & 0x02) { DBG (2, "sense: ADF NO DOCUMENT IN BIN\n"); err++; } if (sense[18] & 0x04) { DBG (2, "sense: ADF SWITCH COVER OPEN\n"); err++; } /* DB : next is, i think no failure, so no incrementing s */ if (sense[18] & 0x08) { DBG (2, "sense: ADF SET CORRECTLY ON TARGET\n"); } /* The following only for AT12, its reserved (zero?) on other models, */ if (sense[18] & 0x10) { DBG (2, "sense: ADF LENGTH TOO SHORT\n"); err++; } } /* enhanced byte 18 sense data */ if ((s) && (s->hw->flags & ARTEC_FLAG_SENSE_ENH_18)) { if (sense[18] & 0x20) { DBG (2, "sense: LAMP FAIL : NOT WARM \n"); err++; } if (sense[18] & 0x40) { DBG (2, "sense: NOT READY STATE\n"); err++; } } if ((s) && (s->hw->flags & ARTEC_FLAG_SENSE_BYTE_19)) { if (sense[19] & 0x01) { DBG (2, "sense: 8031 program ROM checksum Error\n"); err++; } if (sense[19] & 0x02) { DBG (2, "sense: 8031 data RAM R/W Error\n"); err++; } if (sense[19] & 0x04) { DBG (2, "sense: Shadow Correction RAM R/W Error\n"); err++; } if (sense[19] & 0x08) { DBG (2, "sense: Line RAM R/W Error\n"); err++; } if (sense[19] & 0x10) { /* docs say "reserved to '0'" */ DBG (2, "sense: CCD control circuit Error\n"); err++; } if (sense[19] & 0x20) { DBG (2, "sense: Motor End Switch Error\n"); err++; } if (sense[19] & 0x40) { /* docs say "reserved to '0'" */ DBG (2, "sense: Lamp Error\n"); err++; } if (sense[19] & 0x80) { DBG (2, "sense: Optical Calibration/Shading Error\n"); err++; } } /* These are the self test results for tests 0-15 */ if ((s) && (s->hw->flags & ARTEC_FLAG_SENSE_BYTE_22)) { if (sense[22] & 0x01) { DBG (2, "sense: 8031 Internal Memory R/W Error\n"); err++; } if (sense[22] & 0x02) { DBG (2, "sense: EEPROM test pattern R/W Error\n"); err++; } if (sense[22] & 0x04) { DBG (2, "sense: ASIC Test Error\n"); err++; } if (sense[22] & 0x08) { DBG (2, "sense: Line RAM R/W Error\n"); err++; } if (sense[22] & 0x10) { DBG (2, "sense: PSRAM R/W Test Error\n"); err++; } if (sense[22] & 0x20) { DBG (2, "sense: Positioning Error\n"); err++; } if (sense[22] & 0x40) { DBG (2, "sense: Test 6 Error\n"); err++; } if (sense[22] & 0x80) { DBG (2, "sense: Test 7 Error\n"); err++; } if (sense[23] & 0x01) { DBG (2, "sense: Test 8 Error\n"); err++; } if (sense[23] & 0x02) { DBG (2, "sense: Test 9 Error\n"); err++; } if (sense[23] & 0x04) { DBG (2, "sense: Test 10 Error\n"); err++; } if (sense[23] & 0x08) { DBG (2, "sense: Test 11 Error\n"); err++; } if (sense[23] & 0x10) { DBG (2, "sense: Test 12 Error\n"); err++; } if (sense[23] & 0x20) { DBG (2, "sense: Test 13 Error\n"); err++; } if (sense[23] & 0x40) { DBG (2, "sense: Test 14 Error\n"); err++; } if (sense[23] & 0x80) { DBG (2, "sense: Test 15 Error\n"); err++; } } if (err) return SANE_STATUS_IO_ERROR; switch (sense[0]) { case 0x70: /* ALWAYS */ switch (sense[2]) { case 0x00: DBG (2, "sense: Successful command\n"); return SANE_STATUS_GOOD; case 0x02: DBG (2, "sense: Not Ready, target can not be accessed\n"); return SANE_STATUS_IO_ERROR; case 0x03: DBG (2, "sense: Medium Error, paper jam or misfeed during ADF\n"); return SANE_STATUS_IO_ERROR; case 0x04: DBG (2, "sense: Hardware Error, non-recoverable\n"); return SANE_STATUS_IO_ERROR; case 0x05: DBG (2, "sense: Illegal Request, bad parameter in command block\n"); return SANE_STATUS_IO_ERROR; case 0x06: DBG (2, "sense: Unit Attention\n"); return SANE_STATUS_GOOD; default: DBG (2, "sense: SENSE KEY UNKNOWN (%02x)\n", sense[2]); return SANE_STATUS_IO_ERROR; } default: DBG (2, "sense: Unknown Error Code Qualifier (%02x)\n", sense[0]); return SANE_STATUS_IO_ERROR; } DBG (2, "sense: Should not come here!\n"); return SANE_STATUS_IO_ERROR; } /* DB added a wait routine for the scanner to come ready */ static SANE_Status wait_ready (int fd) { SANE_Status status; int retry = 30; /* make this tuneable? */ DBG (7, "wait_ready()\n"); while (retry-- > 0) { status = sanei_scsi_cmd (fd, test_unit_ready, sizeof (test_unit_ready), 0, 0); if (status == SANE_STATUS_GOOD) return status; if (status == SANE_STATUS_DEVICE_BUSY) { sleep (1); continue; } /* status != GOOD && != BUSY */ DBG (9, "wait_ready: '%s'\n", sane_strstatus (status)); return status; } /* BUSY after n retries */ DBG (9, "wait_ready: '%s'\n", sane_strstatus (status)); return status; } /* DB added a abort routine, executed via mode select */ static SANE_Status abort_scan (SANE_Handle handle) { ARTEC_Scanner *s = handle; uint8_t *data, comm[22]; DBG (7, "abort_scan()\n"); memset (comm, 0, sizeof (comm)); comm[0] = 0x15; comm[1] = 0x10; comm[2] = 0x00; comm[3] = 0x00; comm[4] = 0x10; comm[5] = 0x00; data = comm + 6; data[0] = 0x00; /* mode data length */ data[1] = 0x00; /* medium type */ data[2] = 0x00; /* device specific parameter */ data[3] = 0x00; /* block descriptor length */ data = comm + 10; data[0] = 0x00; /* control page parameters */ data[1] = 0x0a; /* parameter length */ data[2] = 0x02 | ((s->val[OPT_TRANSPARENCY].w == SANE_TRUE) ? 0x04 : 0x00) | ((s->val[OPT_ADF].w == SANE_TRUE) ? 0x00 : 0x01); data[3] = 0x00; /* reserved */ data[4] = 0x00; /* reserved */ DBG (9, "abort: sending abort command\n"); sanei_scsi_cmd (s->fd, comm, 6 + comm[4], 0, 0); DBG (9, "abort: wait for scanner to come ready...\n"); wait_ready (s->fd); DBG (9, "abort: resetting abort status\n"); data[2] = ((s->val[OPT_TRANSPARENCY].w == SANE_TRUE) ? 0x04 : 0x00) | ((s->val[OPT_ADF].w == SANE_TRUE) ? 0x00 : 0x01); sanei_scsi_cmd (s->fd, comm, 6 + comm[4], 0, 0); DBG (9, "abort: wait for scanner to come ready...\n"); return wait_ready (s->fd); } /* DAL - mode_select: used for transparency and ADF scanning */ /* Based on abort_scan */ static SANE_Status artec_mode_select (SANE_Handle handle) { ARTEC_Scanner *s = handle; uint8_t *data, comm[22]; DBG (7, "artec_mode_select()\n"); memset (comm, 0, sizeof (comm)); comm[0] = 0x15; comm[1] = 0x10; comm[2] = 0x00; comm[3] = 0x00; comm[4] = 0x10; comm[5] = 0x00; data = comm + 6; data[0] = 0x00; /* mode data length */ data[1] = 0x00; /* medium type */ data[2] = 0x00; /* device specific parameter */ data[3] = 0x00; /* block descriptor length */ data = comm + 10; data[0] = 0x00; /* control page parameters */ data[1] = 0x0a; /* parameter length */ data[2] = ((s->val[OPT_TRANSPARENCY].w == SANE_TRUE) ? 0x04 : 0x00) | ((s->val[OPT_ADF].w == SANE_TRUE) ? 0x00 : 0x01); data[3] = 0x00; /* reserved */ data[4] = 0x00; /* reserved */ DBG (9, "artec_mode_select: mode %d\n", data[2]); DBG (9, "artec_mode_select: sending mode command\n"); sanei_scsi_cmd (s->fd, comm, 6 + comm[4], 0, 0); DBG (9, "artec_mode_select: wait for scanner to come ready...\n"); return wait_ready (s->fd); } static SANE_Status read_data (int fd, int data_type_code, u_char * dest, size_t * len) { static u_char read_6[10]; DBG (7, "read_data()\n"); memset (read_6, 0, sizeof (read_6)); read_6[0] = 0x28; read_6[2] = data_type_code; read_6[6] = *len >> 16; read_6[7] = *len >> 8; read_6[8] = *len; return (sanei_scsi_cmd (fd, read_6, sizeof (read_6), dest, len)); } static int artec_get_status (int fd) { u_char write_10[10]; u_char read_12[12]; size_t nread; DBG (7, "artec_get_status()\n"); nread = 12; memset (write_10, 0, 10); write_10[0] = 0x34; write_10[8] = 0x0c; sanei_scsi_cmd (fd, write_10, 10, read_12, &nread); nread = (read_12[9] << 16) + (read_12[10] << 8) + read_12[11]; DBG (9, "artec_status: %lu\n", (u_long) nread); return (nread); } static SANE_Status artec_reverse_line (SANE_Handle handle, SANE_Byte * data) { ARTEC_Scanner *s = handle; SANE_Byte tmp_buf[32768]; /* max dpi 1200 * 8.5 inches * 3 = 30600 */ SANE_Byte *to, *from; int len; DBG (8, "artec_reverse_line()\n"); len = s->params.bytes_per_line; memcpy (tmp_buf, data, len); if (s->params.format == SANE_FRAME_RGB) /* RGB format */ { for (from = tmp_buf, to = data + len - 3; to >= data; to -= 3, from += 3) { *(to + 0) = *(from + 0); /* copy the R byte */ *(to + 1) = *(from + 1); /* copy the G byte */ *(to + 2) = *(from + 2); /* copy the B byte */ } } else if (s->params.format == SANE_FRAME_GRAY) { if (s->params.depth == 8) /* 256 color gray-scale */ { for (from = tmp_buf, to = data + len; to >= data; to--, from++) { *to = *from; } } else if (s->params.depth == 1) /* line art or halftone */ { for (from = tmp_buf, to = data + len; to >= data; to--, from++) { *to = (((*from & 0x01) << 7) | ((*from & 0x02) << 5) | ((*from & 0x04) << 3) | ((*from & 0x08) << 1) | ((*from & 0x10) >> 1) | ((*from & 0x20) >> 3) | ((*from & 0x40) >> 5) | ((*from & 0x80) >> 7)); } } } return (SANE_STATUS_GOOD); } #if 0 static SANE_Status artec_byte_rgb_to_line_rgb (SANE_Byte * data, SANE_Int len) { SANE_Byte tmp_buf[32768]; /* max dpi 1200 * 8.5 inches * 3 = 30600 */ int count, from; DBG (8, "artec_byte_rgb_to_line_rgb()\n"); /* copy the RGBRGBRGBRGBRGB... formatted data to our temp buffer */ memcpy (tmp_buf, data, len * 3); /* now copy back to *data in RRRRRRRGGGGGGGBBBBBBB format */ for (count = 0, from = 0; count < len; count++, from += 3) { data[count] = tmp_buf[from]; /* R byte */ data[count + len] = tmp_buf[from + 1]; /* G byte */ data[count + (len * 2)] = tmp_buf[from + 2]; /* B byte */ } return (SANE_STATUS_GOOD); } #endif static SANE_Status artec_line_rgb_to_byte_rgb (SANE_Byte * data, SANE_Int len) { SANE_Byte tmp_buf[32768]; /* max dpi 1200 * 8.5 inches * 3 = 30600 */ int count, to; DBG (8, "artec_line_rgb_to_byte_rgb()\n"); /* copy the rgb data to our temp buffer */ memcpy (tmp_buf, data, len * 3); /* now copy back to *data in RGB format */ for (count = 0, to = 0; count < len; count++, to += 3) { data[to] = tmp_buf[count]; /* R byte */ data[to + 1] = tmp_buf[count + len]; /* G byte */ data[to + 2] = tmp_buf[count + (len * 2)]; /* B byte */ } return (SANE_STATUS_GOOD); } static SANE_Byte **line_buffer = NULL; static SANE_Byte *tmp_line_buf = NULL; static SANE_Int r_buf_lines; static SANE_Int g_buf_lines; static SANE_Status artec_buffer_line_offset (SANE_Handle handle, SANE_Int line_offset, SANE_Byte * data, size_t * len) { ARTEC_Scanner *s = handle; static SANE_Int width; static SANE_Int cur_line; SANE_Byte *tmp_buf_ptr; SANE_Byte *grn_ptr; SANE_Byte *blu_ptr; SANE_Byte *out_ptr; int count; DBG (8, "artec_buffer_line_offset()\n"); if (*len == 0) return (SANE_STATUS_GOOD); if (tmp_line_buf == NULL) { width = *len / 3; cur_line = 0; DBG (9, "buffer_line_offset: offset = %d, len = %lu\n", line_offset, (u_long) * len); tmp_line_buf = malloc (*len); if (tmp_line_buf == NULL) { DBG (1, "couldn't allocate memory for temp line buffer\n"); return (SANE_STATUS_NO_MEM); } r_buf_lines = line_offset * 2; g_buf_lines = line_offset; line_buffer = malloc (r_buf_lines * sizeof (SANE_Byte *)); if (line_buffer == NULL) { DBG (1, "couldn't allocate memory for line buffer pointers\n"); return (SANE_STATUS_NO_MEM); } for (count = 0; count < r_buf_lines; count++) { line_buffer[count] = malloc ((*len) * sizeof (SANE_Byte)); if (line_buffer[count] == NULL) { DBG (1, "couldn't allocate memory for line buffer %d\n", count); return (SANE_STATUS_NO_MEM); } } DBG (9, "buffer_line_offset: r lines = %d, g lines = %d\n", r_buf_lines, g_buf_lines); } cur_line++; if (r_buf_lines > 0) { if (cur_line > r_buf_lines) { /* copy the Red and Green portions out of the buffer */ /* if scanner returns RRRRRRRRGGGGGGGGGBBBBBBBB format it's easier */ if (s->hw->flags & ARTEC_FLAG_RGB_CHAR_SHIFT) { /* get the red line info from r_buf_lines ago */ memcpy (tmp_line_buf, line_buffer[0], width); /* get the green line info from g_buf_lines ago */ memcpy (tmp_line_buf + width, &line_buffer[line_offset][width], width); } else { /* get the red line info from r_buf_lines ago as a whole line */ memcpy (tmp_line_buf, line_buffer[0], *len); /* scanner returns RGBRGBRGB format so we do a loop for green */ grn_ptr = &line_buffer[line_offset][1]; out_ptr = tmp_line_buf + 1; for (count = 0; count < width; count++) { *out_ptr = *grn_ptr; /* copy green pixel */ grn_ptr += 3; out_ptr += 3; } } } /* move all the buffered lines down (just move the ptrs for speed) */ tmp_buf_ptr = line_buffer[0]; for (count = 0; count < (r_buf_lines - 1); count++) { line_buffer[count] = line_buffer[count + 1]; } line_buffer[r_buf_lines - 1] = tmp_buf_ptr; /* insert the new line data at the end of our FIFO */ memcpy (line_buffer[r_buf_lines - 1], data, *len); if (cur_line > r_buf_lines) { /* copy the Red and Green portions out of the buffer */ /* if scanner returns RRRRRRRRGGGGGGGGGBBBBBBBB format it's easier */ if (s->hw->flags & ARTEC_FLAG_RGB_CHAR_SHIFT) { /* copy the red and green data in with the original blue */ memcpy (data, tmp_line_buf, width * 2); } else { /* scanner returns RGBRGBRGB format so we have to do a loop */ /* copy the blue data into our temp buffer then copy full */ /* temp buffer overtop of input data */ if (s->hw->flags & ARTEC_FLAG_IMAGE_REV_LR) { blu_ptr = data; out_ptr = tmp_line_buf; } else { blu_ptr = data + 2; out_ptr = tmp_line_buf + 2; } for (count = 0; count < width; count++) { *out_ptr = *blu_ptr; /* copy blue pixel */ blu_ptr += 3; out_ptr += 3; } /* now just copy tmp_line_buf back over original data */ memcpy (data, tmp_line_buf, *len); } } else { /* if in the first r_buf_lines, then don't return anything */ *len = 0; } } return (SANE_STATUS_GOOD); } static SANE_Status artec_buffer_line_offset_free (void) { int count; DBG (7, "artec_buffer_line_offset_free()\n"); free (tmp_line_buf); tmp_line_buf = NULL; for (count = 0; count < r_buf_lines; count++) { free (line_buffer[count]); } free (line_buffer); line_buffer = NULL; return (SANE_STATUS_GOOD); } #if 0 static SANE_Status artec_read_gamma_table (SANE_Handle handle) { ARTEC_Scanner *s = handle; char write_6[4096 + 20]; /* max gamma table is 4096 + 20 for command data */ char *data; char prt_buf[128]; char tmp_buf[128]; int i; DBG (7, "artec_read_gamma_table()\n"); memset (write_6, 0, sizeof (*write_6)); write_6[0] = 0x28; /* read data code */ /* FIXME: AT12 and AM12S use 0x0E for reading all channels of data */ write_6[2] = 0x03; /* data type code "gamma data" */ write_6[6] = (s->gamma_length + 9) >> 16; write_6[7] = (s->gamma_length + 9) >> 8; write_6[8] = (s->gamma_length + 9); /* FIXME: AT12 and AM12S have one less byte so use 18 */ if ((!strcmp (s->hw->sane.model, "AT12")) || (!strcmp (s->hw->sane.model, "AM12S"))) { data = write_6 + 18; } else { data = write_6 + 19; } /* FIXME: AT12 & AM12S ignore this, it's a reserved field */ write_6[10] = 0x08; /* bitmask, bit 3 means mono type */ if (!s->val[OPT_CUSTOM_GAMMA].w) { write_6[11] = 1; /* internal gamma table #1 (hope this is default) */ } DBG( 9, "Gamma Table\n" ); DBG( 9, "==================================\n" ); prt_buf[0] = '\0'; for (i = 0; i < s->gamma_length; i++) { if (DBG_LEVEL >= 9) { if (!(i % 16)) { if ( prt_buf[0] ) { strcat( prt_buf, "\n" ); DBG( 9, "%s", prt_buf ); } sprintf (prt_buf, "%02x: ", i); } sprintf (tmp_buf, "%02x ", (int) s->gamma_table[0][i]); strcat (prt_buf, tmp_buf ); } data[i] = s->gamma_table[0][i]; } if ( prt_buf[0] ) { strcat( prt_buf, "\n" ); DBG( 9, "%s", prt_buf ); } if ((!strcmp (s->hw->sane.model, "AT12")) || (!strcmp (s->hw->sane.model, "AM12S"))) { return (sanei_scsi_cmd (s->fd, write_6, 10 + 8 + s->gamma_length, 0, 0)); } else { return (sanei_scsi_cmd (s->fd, write_6, 10 + 9 + s->gamma_length, 0, 0)); } } #endif static SANE_Status artec_send_gamma_table (SANE_Handle handle) { ARTEC_Scanner *s = handle; char write_6[4096 + 20]; /* max gamma table is 4096 + 20 for command data */ char *data; char prt_buf[128]; char tmp_buf[128]; int i; DBG (7, "artec_send_gamma_table()\n"); memset (write_6, 0, sizeof (*write_6)); write_6[0] = 0x2a; /* send data code */ if (s->hw->setwindow_cmd_size > 55) { /* newer scanners support sending 3 channels of gamma, or populating all */ /* 3 channels with same data by using code 0x0e */ write_6[2] = 0x0e; } else { /* older scanners only support 1 channel of gamma data using code 0x3 */ write_6[2] = 0x03; } /* FIXME: AT12 & AM!2S ignore this, it's a reserved field */ write_6[10] = 0x08; /* bitmask, bit 3 means mono type */ if (!s->val[OPT_CUSTOM_GAMMA].w) { write_6[6] = 9 >> 16; write_6[7] = 9 >> 8; write_6[8] = 9; write_6[11] = 1; /* internal gamma table #1 (hope this is default) */ return (sanei_scsi_cmd (s->fd, write_6, 10 + 9, 0, 0)); } else { write_6[6] = (s->gamma_length + 9) >> 16; write_6[7] = (s->gamma_length + 9) >> 8; write_6[8] = (s->gamma_length + 9); DBG( 9, "Gamma Table\n" ); DBG( 9, "==================================\n" ); /* FIXME: AT12 and AM12S have one less byte so use 18 */ if ((!strcmp (s->hw->sane.model, "AT12")) || (!strcmp (s->hw->sane.model, "AM12S"))) { data = write_6 + 18; } else { data = write_6 + 19; } prt_buf[0] = '\0'; for (i = 0; i < s->gamma_length; i++) { if (DBG_LEVEL >= 9) { if (!(i % 16)) { if ( prt_buf[0] ) { strcat( prt_buf, "\n" ); DBG( 9, "%s", prt_buf ); } sprintf (prt_buf, "%02x: ", i); } sprintf (tmp_buf, "%02x ", (int) s->gamma_table[0][i]); strcat (prt_buf, tmp_buf ); } data[i] = s->gamma_table[0][i]; } data[s->gamma_length - 1] = 0; if ( prt_buf[0] ) { strcat( prt_buf, "\n" ); DBG( 9, "%s", prt_buf ); } /* FIXME: AT12 and AM12S have one less byte so use 18 */ if ((!strcmp (s->hw->sane.model, "AT12")) || (!strcmp (s->hw->sane.model, "AM12S"))) { return (sanei_scsi_cmd (s->fd, write_6, 10 + 8 + s->gamma_length, 0, 0)); } else { return (sanei_scsi_cmd (s->fd, write_6, 10 + 9 + s->gamma_length, 0, 0)); } } } static SANE_Status artec_set_scan_window (SANE_Handle handle) { ARTEC_Scanner *s = handle; char write_6[4096]; unsigned char *data; int counter; int reversed_x; int max_x; DBG (7, "artec_set_scan_window()\n"); /* * if we can, start before the desired window since we have to throw away * s->line_offset number of rows because of the RGB fixup. */ if ((s->line_offset) && (s->tl_y) && (s->tl_y >= (s->line_offset * 2))) { s->tl_y -= (s->line_offset * 2); } data = (unsigned char *)write_6 + 10; DBG (5, "Scan window info:\n"); DBG (5, " X resolution: %5d (%d-%d)\n", s->x_resolution, ARTEC_MIN_X (s->hw), ARTEC_MAX_X (s->hw)); DBG (5, " Y resolution: %5d (%d-%d)\n", s->y_resolution, ARTEC_MIN_Y (s->hw), ARTEC_MAX_Y (s->hw)); DBG (5, " TL_X (pixel): %5d\n", s->tl_x); DBG (5, " TL_Y (pixel): %5d\n", s->tl_y); DBG (5, " Width : %5d (%d-%d)\n", s->params.pixels_per_line, s->hw->x_range.min, (int) ((SANE_UNFIX (s->hw->x_range.max) / MM_PER_INCH) * s->x_resolution)); DBG (5, " Height : %5d (%d-%d)\n", s->params.lines, s->hw->y_range.min, (int) ((SANE_UNFIX (s->hw->y_range.max) / MM_PER_INCH) * s->y_resolution)); DBG (5, " Image Comp. : %s\n", s->mode); DBG (5, " Line Offset : %lu\n", (u_long) s->line_offset); memset (write_6, 0, 4096); write_6[0] = 0x24; write_6[8] = s->hw->setwindow_cmd_size; /* total size of command */ /* beginning of set window data header */ /* actual SCSI command data byte count */ data[7] = s->hw->setwindow_cmd_size - 8; /* x resolution */ data[10] = s->x_resolution >> 8; data[11] = s->x_resolution; /* y resolution */ data[12] = s->y_resolution >> 8; data[13] = s->y_resolution; if ( s->hw->flags & ARTEC_FLAG_REVERSE_WINDOW ) { /* top left X value */ /* the select area is flipped across the page, so we have to do some */ /* calculation here to get the real starting X value */ max_x = (int) ((SANE_UNFIX (s->hw->x_range.max) / MM_PER_INCH) * s->x_resolution); reversed_x = max_x - s->tl_x - s->params.pixels_per_line; data[14] = reversed_x >> 24; data[15] = reversed_x >> 16; data[16] = reversed_x >> 8; data[17] = reversed_x; } else { /* top left X value */ data[14] = s->tl_x >> 24; data[15] = s->tl_x >> 16; data[16] = s->tl_x >> 8; data[17] = s->tl_x; } /* top left Y value */ data[18] = s->tl_y >> 24; data[19] = s->tl_y >> 16; data[20] = s->tl_y >> 8; data[21] = s->tl_y; /* width */ data[22] = s->params.pixels_per_line >> 24; data[23] = s->params.pixels_per_line >> 16; data[24] = s->params.pixels_per_line >> 8; data[25] = s->params.pixels_per_line; /* height */ data[26] = (s->params.lines + (s->line_offset * 2)) >> 24; data[27] = (s->params.lines + (s->line_offset * 2)) >> 16; data[28] = (s->params.lines + (s->line_offset * 2)) >> 8; data[29] = (s->params.lines + (s->line_offset * 2)); /* misc. single-byte settings */ /* brightness */ if (s->hw->flags & ARTEC_FLAG_OPT_BRIGHTNESS) data[30] = s->val[OPT_BRIGHTNESS].w; data[31] = s->val[OPT_THRESHOLD].w; /* threshold */ /* contrast */ if (s->hw->flags & ARTEC_FLAG_OPT_CONTRAST) data[32] = s->val[OPT_CONTRAST].w; /* * byte 33 is mode * byte 37 bit 7 is "negative" setting */ if (strcmp (s->mode, SANE_VALUE_SCAN_MODE_LINEART) == 0) { data[33] = ARTEC_COMP_LINEART; data[37] = (s->val[OPT_NEGATIVE].w == SANE_TRUE) ? 0x0 : 0x80; } else if (strcmp (s->mode, SANE_VALUE_SCAN_MODE_HALFTONE) == 0) { data[33] = ARTEC_COMP_HALFTONE; data[37] = (s->val[OPT_NEGATIVE].w == SANE_TRUE) ? 0x0 : 0x80; } else if (strcmp (s->mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) { data[33] = ARTEC_COMP_GRAY; data[37] = (s->val[OPT_NEGATIVE].w == SANE_TRUE) ? 0x80 : 0x0; } else if (strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) == 0) { data[33] = ARTEC_COMP_COLOR; data[37] = (s->val[OPT_NEGATIVE].w == SANE_TRUE) ? 0x80 : 0x0; } data[34] = s->params.depth; /* bits per pixel */ if (s->hw->flags & ARTEC_FLAG_HALFTONE_PATTERN) { data[35] = artec_get_str_index (halftone_pattern_list, s->val[OPT_HALFTONE_PATTERN].s); /* halftone pattern */ } /* user supplied halftone pattern not supported for now so override with */ /* 8x8 Bayer */ if (data[35] == 0) { data[35] = 4; } /* NOTE: AT12 doesn't support mono according to docs. */ data[48] = artec_get_str_index (filter_type_list, s->val[OPT_FILTER_TYPE].s); /* filter mode */ if (s->hw->setwindow_cmd_size > 55) { data[48] = 0x2; /* DB filter type green for AT12,see above */ if (s->hw->flags & ARTEC_FLAG_SC_BUFFERS_LINES) { /* FIXME: guessing at this value, use formula instead */ data[55] = 0x00; /* buffer full line count */ data[56] = 0x00; /* buffer full line count */ data[57] = 0x00; /* buffer full line count */ data[58] = 0x0a; /* buffer full line count */ /* FIXME: guessing at this value, use formula instead */ data[59] = 0x00; /* access line count */ data[60] = 0x00; /* access line count */ data[61] = 0x00; /* access line count */ data[62] = 0x0a; /* access line count */ } if (s->hw->flags & ARTEC_FLAG_SC_HANDLES_OFFSET) { /* DB : following fields : high order bit (0x80) is enable */ /* scanner handles line offset fixup, 0 = driver handles */ data[63] = 0x80; } if ((s->hw->flags & ARTEC_FLAG_PIXEL_AVERAGING) && (s->val[OPT_PIXEL_AVG].w)) { /* enable pixel average function */ data[64] = 0x80; } else { /* disable pixel average function */ data[64] = 0; } if ((s->hw->flags & ARTEC_FLAG_ENHANCE_LINE_EDGE) && (s->val[OPT_EDGE_ENH].w)) { /* enable lineart edge enhancement function */ data[65] = 0x80; } else { /* disable lineart edge enhancement function */ data[65] = 0; } /* data is R-G-B format, 0x80 = G-B-R format (reversed) */ data[66] = 0; } DBG (50, "Set Window data : \n"); for (counter = 0; counter < s->hw->setwindow_cmd_size; counter++) { DBG (50, " byte %2d = %02x \n", counter, data[counter] & 0xff); /* DB */ } DBG (50, "\n"); /* set the scan window */ return (sanei_scsi_cmd (s->fd, write_6, 10 + s->hw->setwindow_cmd_size, 0, 0)); } static SANE_Status artec_start_scan (SANE_Handle handle) { ARTEC_Scanner *s = handle; char write_7[7]; DBG (7, "artec_start_scan()\n"); /* setup cmd to start scanning */ memset (write_7, 0, 7); write_7[0] = 0x1b; /* code to start scan */ /* FIXME: need to make this a flag */ if (!strcmp (s->hw->sane.model, "AM12S")) { /* start the scan */ return (sanei_scsi_cmd (s->fd, write_7, 6, 0, 0)); } else { write_7[4] = 0x01; /* need to send 1 data byte */ /* start the scan */ return (sanei_scsi_cmd (s->fd, write_7, 7, 0, 0)); } } static SANE_Status artec_software_rgb_calibrate (SANE_Handle handle, SANE_Byte * buf, int lines) { ARTEC_Scanner *s = handle; int line, i, loop, offset; DBG (7, "artec_software_rgb_calibrate()\n"); for (line = 0; line < lines; line++) { i = 0; offset = 0; if (s->x_resolution == 200) { /* skip ever 3rd byte, -= causes us to go down in count */ if ((s->tl_x % 3) == 0) offset -= 1; } else { /* round down to the previous pixel */ offset += ((s->tl_x / (300 / s->x_resolution)) * (300 / s->x_resolution)); } for (loop = 0; loop < s->params.pixels_per_line; loop++) { if ((DBG_LEVEL == 100) && (loop < 100)) { DBG (100, " %2d-%4d R (%4d,%4d): %d * %5.2f = %d\n", line, loop, i, offset, buf[i], s->soft_calibrate_data[ARTEC_SOFT_CALIB_RED][offset], (int) (buf[i] * s->soft_calibrate_data[ARTEC_SOFT_CALIB_RED][offset])); } buf[i] = buf[i] * s->soft_calibrate_data[ARTEC_SOFT_CALIB_RED][offset]; i++; if ((DBG_LEVEL == 100) && (loop < 100)) { DBG (100, " G (%4d,%4d): %d * %5.2f = %d\n", i, offset, buf[i], s->soft_calibrate_data[ARTEC_SOFT_CALIB_GREEN][offset], (int) (buf[i] * s->soft_calibrate_data[ARTEC_SOFT_CALIB_GREEN][offset])); } buf[i] = buf[i] * s->soft_calibrate_data[ARTEC_SOFT_CALIB_GREEN][offset]; i++; if ((DBG_LEVEL == 100) && (loop < 100)) { DBG (100, " B (%4d,%4d): %d * %5.2f = %d\n", i, offset, buf[i], s->soft_calibrate_data[ARTEC_SOFT_CALIB_BLUE][offset], (int) (buf[i] * s->soft_calibrate_data[ARTEC_SOFT_CALIB_BLUE][offset])); } buf[i] = buf[i] * s->soft_calibrate_data[ARTEC_SOFT_CALIB_BLUE][offset]; i++; if (s->x_resolution == 200) { offset += 1; /* skip every 3rd byte */ if (((offset + 1) % 3) == 0) offset += 1; } else { offset += (300 / s->x_resolution); } } } return (SANE_STATUS_GOOD); } static SANE_Status artec_calibrate_shading (SANE_Handle handle) { ARTEC_Scanner *s = handle; SANE_Status status; /* DB added */ u_char buf[76800]; /* should be big enough */ size_t len; SANE_Word save_x_resolution; SANE_Word save_pixels_per_line; int i; DBG (7, "artec_calibrate_shading()\n"); if (s->hw->flags & ARTEC_FLAG_CALIBRATE_RGB) { /* this method scans in 4 lines each of Red, Green, and Blue */ /* after reading line of shading data, generate data for software */ /* calibration so we have it if user requests */ len = 4 * 2592; /* 4 lines of data, 2592 pixels wide */ if ( DBG_LEVEL == 100 ) DBG (100, "RED Software Calibration data\n"); read_data (s->fd, ARTEC_DATA_RED_SHADING, buf, &len); for (i = 0; i < 2592; i++) { s->soft_calibrate_data[ARTEC_SOFT_CALIB_RED][i] = 255.0 / ((buf[i] + buf[i + 2592] + buf[i + 5184] + buf[i + 7776]) / 4); if (DBG_LEVEL == 100) { DBG (100, " %4d: 255.0 / (( %3d + %3d + %3d + %3d ) / 4 ) = %5.2f\n", i, buf[i], buf[i + 2592], buf[i + 5184], buf[i + 7776], s->soft_calibrate_data[ARTEC_SOFT_CALIB_RED][i]); } } if (DBG_LEVEL == 100) { DBG (100, "GREEN Software Calibration data\n"); } read_data (s->fd, ARTEC_DATA_GREEN_SHADING, buf, &len); for (i = 0; i < 2592; i++) { s->soft_calibrate_data[ARTEC_SOFT_CALIB_GREEN][i] = 255.0 / ((buf[i] + buf[i + 2592] + buf[i + 5184] + buf[i + 7776]) / 4); if (DBG_LEVEL == 100) { DBG (100, " %4d: 255.0 / (( %3d + %3d + %3d + %3d ) / 4 ) = %5.2f\n", i, buf[i], buf[i + 2592], buf[i + 5184], buf[i + 7776], s->soft_calibrate_data[ARTEC_SOFT_CALIB_GREEN][i]); } } if (DBG_LEVEL == 100) { DBG (100, "BLUE Software Calibration data\n"); } read_data (s->fd, ARTEC_DATA_BLUE_SHADING, buf, &len); for (i = 0; i < 2592; i++) { s->soft_calibrate_data[ARTEC_SOFT_CALIB_BLUE][i] = 255.0 / ((buf[i] + buf[i + 2592] + buf[i + 5184] + buf[i + 7776]) / 4); if (DBG_LEVEL == 100) { DBG (100, " %4d: 255.0 / (( %3d + %3d + %3d + %3d ) / 4 ) = %5.2f\n", i, buf[i], buf[i + 2592], buf[i + 5184], buf[i + 7776], s->soft_calibrate_data[ARTEC_SOFT_CALIB_BLUE][i]); } } } else if (s->hw->flags & ARTEC_FLAG_CALIBRATE_DARK_WHITE) { /* this method scans black, then white data */ len = 3 * 5100; /* 1 line of data, 5100 pixels wide, RGB data */ read_data (s->fd, ARTEC_DATA_DARK_SHADING, buf, &len); save_x_resolution = s->x_resolution; s->x_resolution = 600; save_pixels_per_line = s->params.pixels_per_line; s->params.pixels_per_line = ARTEC_MAX_X (s->hw); s->params.pixels_per_line = 600 * 8.5; /* ?this? or ?above line? */ /* DB added wait_ready */ status = wait_ready (s->fd); if (status != SANE_STATUS_GOOD) { DBG (1, "wait for scanner ready failed: %s\n", sane_strstatus (status)); return status; } /* next line should use ARTEC_DATA_WHITE_SHADING_TRANS if using ADF */ read_data (s->fd, ARTEC_DATA_WHITE_SHADING_OPT, buf, &len); s->x_resolution = save_x_resolution; s->params.pixels_per_line = save_pixels_per_line; } return (SANE_STATUS_GOOD); } static SANE_Status end_scan (SANE_Handle handle) { ARTEC_Scanner *s = handle; /* DB uint8_t write_6[6] = {0x1B, 0, 0, 0, 0, 0}; */ DBG (7, "end_scan()\n"); s->scanning = SANE_FALSE; /* if (s->this_pass == 3) */ s->this_pass = 0; if ((s->hw->flags & ARTEC_FLAG_RGB_LINE_OFFSET) && (tmp_line_buf != NULL)) { artec_buffer_line_offset_free (); } /* DB return (sanei_scsi_cmd (s->fd, write_6, 6, 0, 0)); */ return abort_scan (s); } static SANE_Status artec_get_cap_data (ARTEC_Device * dev, int fd) { int cap_model, loop; u_char cap_buf[256]; /* buffer for cap data */ DBG (7, "artec_get_cap_data()\n"); /* DB always use the hard-coded capability info first * if we get cap data from the scanner, we override */ cap_model = -1; for (loop = 0; loop < NELEMS (cap_data); loop++) { if (strcmp (cap_data[loop].model, dev->sane.model) == 0) { cap_model = loop; } } if (cap_model == -1) { DBG (1, "unable to identify Artec model '%s', check artec.c\n", dev->sane.model); return (SANE_STATUS_UNSUPPORTED); } dev->x_range.min = 0; dev->x_range.max = SANE_FIX (cap_data[cap_model].width) * MM_PER_INCH; dev->x_range.quant = 1; dev->width = cap_data[cap_model].width; dev->y_range.min = 0; dev->y_range.max = SANE_FIX (cap_data[cap_model].height) * MM_PER_INCH; dev->y_range.quant = 1; dev->height = cap_data[cap_model].height; artec_str_list_to_word_list (&dev->horz_resolution_list, cap_data[cap_model].horz_resolution_str); artec_str_list_to_word_list (&dev->vert_resolution_list, cap_data[cap_model].vert_resolution_str); dev->contrast_range.min = 0; dev->contrast_range.max = 255; dev->contrast_range.quant = 1; dev->brightness_range.min = 0; dev->brightness_range.max = 255; dev->brightness_range.quant = 1; dev->threshold_range.min = 0; dev->threshold_range.max = 255; dev->threshold_range.quant = 1; dev->sane.type = cap_data[cap_model].type; dev->max_read_size = cap_data[cap_model].max_read_size; dev->flags = cap_data[cap_model].flags; switch (cap_data[cap_model].adc_bits) { case 8: dev->gamma_length = 256; break; case 10: dev->gamma_length = 1024; break; case 12: dev->gamma_length = 4096; break; } dev->setwindow_cmd_size = cap_data[cap_model].setwindow_cmd_size; if (dev->support_cap_data_retrieve) /* DB */ { /* DB added reading capability data from scanner */ char info[80]; /* for printing debugging info */ size_t len = sizeof (cap_buf); /* read the capability data from the scanner */ DBG (9, "reading capability data from scanner...\n"); wait_ready (fd); read_data (fd, ARTEC_DATA_CAPABILITY_DATA, cap_buf, &len); DBG (50, "scanner capability data : \n"); strncpy (info, (const char *) &cap_buf[0], 8); info[8] = '\0'; DBG (50, " Vendor : %s\n", info); strncpy (info, (const char *) &cap_buf[8], 16); info[16] = '\0'; DBG (50, " Device Name : %s\n", info); strncpy (info, (const char *) &cap_buf[24], 4); info[4] = '\0'; DBG (50, " Version Number : %s\n", info); sprintf (info, "%d ", cap_buf[29]); DBG (50, " CCD Type : %s\n", info); sprintf (info, "%d ", cap_buf[30]); DBG (50, " AD Converter Type : %s\n", info); sprintf (info, "%d ", (cap_buf[31] << 8) | cap_buf[32]); DBG (50, " Buffer size : %s\n", info); sprintf (info, "%d ", cap_buf[33]); DBG (50, " Channels of RGB Gamma : %s\n", info); sprintf (info, "%d ", (cap_buf[34] << 8) | cap_buf[35]); DBG (50, " Opt. res. of R channel : %s\n", info); sprintf (info, "%d ", (cap_buf[36] << 8) | cap_buf[37]); DBG (50, " Opt. res. of G channel : %s\n", info); sprintf (info, "%d ", (cap_buf[38] << 8) | cap_buf[39]); DBG (50, " Opt. res. of B channel : %s\n", info); sprintf (info, "%d ", (cap_buf[40] << 8) | cap_buf[41]); DBG (50, " Min. Hor. Resolution : %s\n", info); sprintf (info, "%d ", (cap_buf[42] << 8) | cap_buf[43]); DBG (50, " Max. Vert. Resolution : %s\n", info); sprintf (info, "%d ", (cap_buf[44] << 8) | cap_buf[45]); DBG (50, " Min. Vert. Resolution : %s\n", info); sprintf (info, "%s ", cap_buf[46] == 0x80 ? "yes" : "no"); DBG (50, " Chunky Data Format : %s\n", info); sprintf (info, "%s ", cap_buf[47] == 0x80 ? "yes" : "no"); DBG (50, " RGB Data Format : %s\n", info); sprintf (info, "%s ", cap_buf[48] == 0x80 ? "yes" : "no"); DBG (50, " BGR Data Format : %s\n", info); sprintf (info, "%d ", cap_buf[49]); DBG (50, " Line Offset : %s\n", info); sprintf (info, "%s ", cap_buf[50] == 0x80 ? "yes" : "no"); DBG (50, " Channel Valid Sequence : %s\n", info); sprintf (info, "%s ", cap_buf[51] == 0x80 ? "yes" : "no"); DBG (50, " True Gray : %s\n", info); sprintf (info, "%s ", cap_buf[52] == 0x80 ? "yes" : "no"); DBG (50, " Force Host Not Do Shading : %s\n", info); sprintf (info, "%s ", cap_buf[53] == 0x00 ? "AT006" : "AT010"); DBG (50, " ASIC : %s\n", info); sprintf (info, "%s ", cap_buf[54] == 0x82 ? "SCSI2" : cap_buf[54] == 0x81 ? "SCSI1" : "Parallel"); DBG (50, " Interface : %s\n", info); sprintf (info, "%d ", (cap_buf[55] << 8) | cap_buf[56]); DBG (50, " Phys. Area Width : %s\n", info); sprintf (info, "%d ", (cap_buf[57] << 8) | cap_buf[58]); DBG (50, " Phys. Area Length : %s\n", info); /* fill in the information we've got from the scanner */ dev->width = ((float) ((cap_buf[55] << 8) | cap_buf[56])) / 1000; dev->height = ((float) ((cap_buf[57] << 8) | cap_buf[58])) / 1000; /* DB ----- */ } DBG (9, "Scanner capability info.\n"); DBG (9, " Vendor : %s\n", dev->sane.vendor); DBG (9, " Model : %s\n", dev->sane.model); DBG (9, " Type : %s\n", dev->sane.type); DBG (5, " Width : %.2f inches\n", dev->width); DBG (9, " Height : %.2f inches\n", dev->height); DBG (9, " X Range(mm) : %d-%d\n", dev->x_range.min, (int) (SANE_UNFIX (dev->x_range.max))); DBG (9, " Y Range(mm) : %d-%d\n", dev->y_range.min, (int) (SANE_UNFIX (dev->y_range.max))); DBG (9, " Horz. DPI : %d-%d\n", ARTEC_MIN_X (dev), ARTEC_MAX_X (dev)); DBG (9, " Vert. DPI : %d-%d\n", ARTEC_MIN_Y (dev), ARTEC_MAX_Y (dev)); DBG (9, " Contrast : %d-%d\n", dev->contrast_range.min, dev->contrast_range.max); DBG (9, " REQ Sh. Cal.: %d\n", dev->flags & ARTEC_FLAG_CALIBRATE ? 1 : 0); DBG (9, " REQ Ln. Offs: %d\n", dev->flags & ARTEC_FLAG_RGB_LINE_OFFSET ? 1 : 0); DBG (9, " REQ Ch. Shft: %d\n", dev->flags & ARTEC_FLAG_RGB_CHAR_SHIFT ? 1 : 0); DBG (9, " SetWind Size: %d\n", dev->setwindow_cmd_size); DBG (9, " Calib Method: %s\n", dev->flags & ARTEC_FLAG_CALIBRATE_RGB ? "RGB" : dev->flags & ARTEC_FLAG_CALIBRATE_DARK_WHITE ? "white/black" : "N/A"); return (SANE_STATUS_GOOD); } static SANE_Status dump_inquiry (unsigned char *result) { int i; int j; char prt_buf[129] = ""; char tmp_buf[129]; DBG (4, "dump_inquiry()\n"); DBG (4, " === SANE/Artec backend v%d.%d.%d ===\n", ARTEC_MAJOR, ARTEC_MINOR, ARTEC_SUB); DBG (4, " ===== Scanner Inquiry Block =====\n"); for (i = 0; i < 96; i += 16) { sprintf (prt_buf, "0x%02x: ", i); for (j = 0; j < 16; j++) { sprintf (tmp_buf, "%02x ", (int) result[i + j]); strcat( prt_buf, tmp_buf ); } strcat( prt_buf, " "); for (j = 0; j < 16; j++) { sprintf (tmp_buf, "%c", isprint (result[i + j]) ? result[i + j] : '.'); strcat( prt_buf, tmp_buf ); } strcat( prt_buf, "\n" ); DBG(4, "%s", prt_buf ); } return (SANE_STATUS_GOOD); } static SANE_Status attach (const char *devname, ARTEC_Device ** devp) { char result[INQ_LEN]; char product_revision[5]; char temp_result[33]; char *str, *t; int fd; SANE_Status status; ARTEC_Device *dev; size_t size; DBG (7, "attach()\n"); for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->sane.name, devname) == 0) { if (devp) *devp = dev; return (SANE_STATUS_GOOD); } } DBG (6, "attach: opening %s\n", devname); status = sanei_scsi_open (devname, &fd, sense_handler, NULL); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: open failed (%s)\n", sane_strstatus (status)); return (SANE_STATUS_INVAL); } DBG (6, "attach: sending INQUIRY\n"); size = sizeof (result); status = sanei_scsi_cmd (fd, inquiry, sizeof (inquiry), result, &size); if (status != SANE_STATUS_GOOD || size < 16) { DBG (1, "attach: inquiry failed (%s)\n", sane_strstatus (status)); sanei_scsi_close (fd); return (status); } /* * Check to see if this device is a scanner. */ if (result[0] != 0x6) { DBG (1, "attach: device doesn't look like a scanner at all.\n"); sanei_scsi_close (fd); return (SANE_STATUS_INVAL); } /* * The BlackWidow BW4800SP is actually a rebadged AT3, with the vendor * string set to 8 spaces and the product to "Flatbed Scanner ". So, * if we have one of these, we'll make it look like an AT3. * * For now, to be on the safe side, we'll also check the version number * since BlackWidow seems to have left that intact as "1.90". * * Check that result[36] == 0x00 so we don't mistake a microtek scanner. */ if ((result[36] == 0x00) && (strncmp (result + 32, "1.90", 4) == 0) && (strncmp (result + 8, " ", 8) == 0) && (strncmp (result + 16, "Flatbed Scanner ", 16) == 0)) { DBG (6, "Found BlackWidow BW4800SP scanner, setting up like AT3\n"); /* setup the vendor and product to mimic the Artec/Ultima AT3 */ memcpy (result + 8, "ULTIMA", 6); memcpy (result + 16, "AT3 ", 16); } /* * The Plustek 19200S is actually a rebadged AM12S, with the vendor string * set to 8 spaces. */ if ((strncmp (result + 8, " ", 8) == 0) && (strncmp (result + 16, "SCAN19200 ", 16) == 0)) { DBG (6, "Found Plustek 19200S scanner, setting up like AM12S\n"); /* setup the vendor and product to mimic the Artec/Ultima AM12S */ memcpy (result + 8, "ULTIMA", 6); memcpy (result + 16, "AM12S ", 16); } /* * Check to see if they have forced a vendor and/or model string and * if so, fudge the inquiry results with that info. We do this right * before we check the inquiry results, otherwise we might not be forcing * anything. */ if (artec_vendor[0] != 0x0) { /* * 1) copy the vendor string to our temp variable * 2) append 8 spaces to make sure we have at least 8 characters * 3) copy our fudged vendor string into the inquiry result. */ strcpy (temp_result, artec_vendor); strcat (temp_result, " "); strncpy (result + 8, temp_result, 8); } if (artec_model[0] != 0x0) { /* * 1) copy the model string to our temp variable * 2) append 16 spaces to make sure we have at least 16 characters * 3) copy our fudged model string into the inquiry result. */ strcpy (temp_result, artec_model); strcat (temp_result, " "); strncpy (result + 16, temp_result, 16); } /* are we really dealing with a scanner by ULTIMA/ARTEC? */ if ((strncmp (result + 8, "ULTIMA", 6) != 0) && (strncmp (result + 8, "ARTEC", 5) != 0)) { DBG (1, "attach: device doesn't look like a Artec/ULTIMA scanner\n"); strncpy (temp_result, result + 8, 8); temp_result[8] = 0x0; DBG (1, "attach: FOUND vendor = '%s'\n", temp_result); strncpy (temp_result, result + 16, 16); temp_result[16] = 0x0; DBG (1, "attach: FOUND model = '%s'\n", temp_result); sanei_scsi_close (fd); return (SANE_STATUS_INVAL); } /* turn this wait OFF for now since it appears to cause problems with */ /* AT12 models */ /* turned off by creating an "if" that can never be true */ if ( 1 == 2 ) { DBG (6, "attach: wait for scanner to come ready\n"); status = wait_ready (fd); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: test unit ready failed (%s)\n", sane_strstatus (status)); sanei_scsi_close (fd); return (status); } /* This is the end of the "if" that can never be true that in effect */ /* comments out this wait_ready() call */ } /* end of "if( 1 == 2 )" */ dev = malloc (sizeof (*dev)); if (!dev) return (SANE_STATUS_NO_MEM); memset (dev, 0, sizeof (*dev)); if (DBG_LEVEL >= 4) dump_inquiry ((unsigned char *) result); dev->sane.name = strdup (devname); /* get the model info */ str = malloc (17); memcpy (str, result + 16, 16); str[16] = ' '; t = str + 16; while ((*t == ' ') && (t > str)) { *t = '\0'; t--; } dev->sane.model = str; /* for some reason, the firmware revision is in the model info string on */ /* the A6000C PLUS scanners instead of in it's proper place */ if (strstr (str, "A6000C PLUS") == str) { str[11] = '\0'; strncpy (product_revision, str + 12, 4); } else if (strstr (str, "AT3") == str) { str[3] = '\0'; strncpy (product_revision, str + 8, 4); } else { /* get the product revision from it's normal place */ strncpy (product_revision, result + 32, 4); } product_revision[4] = ' '; t = strchr (product_revision, ' '); if (t) *t = '\0'; else t = "unknown revision"; /* get the vendor info */ str = malloc (9); memcpy (str, result + 8, 8); str[8] = ' '; t = strchr (str, ' '); *t = '\0'; dev->sane.vendor = str; DBG (5, "scanner vendor: '%s', model: '%s', revision: '%s'\n", dev->sane.vendor, dev->sane.model, product_revision); /* Artec docs say if bytes 36-43 = "ULTIMA ", then supports read cap. data */ if (strncmp (result + 36, "ULTIMA ", 8) == 0) { DBG (5, "scanner supports read capability data function\n"); dev->support_cap_data_retrieve = SANE_TRUE; } else { DBG (5, "scanner does NOT support read capability data function\n"); dev->support_cap_data_retrieve = SANE_FALSE; } DBG (6, "attach: getting scanner capability data\n"); status = artec_get_cap_data (dev, fd); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: artec_get_cap_data failed (%s)\n", sane_strstatus (status)); sanei_scsi_close (fd); return (status); } sanei_scsi_close (fd); ++num_devices; dev->next = first_dev; first_dev = dev; if (devp) *devp = dev; return (SANE_STATUS_GOOD); } static SANE_Status init_options (ARTEC_Scanner * s) { int i; DBG (7, "init_options()\n"); memset (s->opt, 0, sizeof (s->opt)); memset (s->val, 0, sizeof (s->val)); for (i = 0; i < NUM_OPTIONS; ++i) { s->opt[i].size = sizeof (SANE_Word); s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* "Mode" group: */ s->opt[OPT_MODE_GROUP].title = "Scan Mode"; s->opt[OPT_MODE_GROUP].desc = ""; s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MODE_GROUP].cap = 0; s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* scan mode */ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; 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 = max_string_size (mode_list); s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_MODE].constraint.string_list = mode_list; s->val[OPT_MODE].s = strdup (mode_list[3]); /* horizontal resolution */ s->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_X_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_X_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_X_RESOLUTION].constraint.word_list = s->hw->horz_resolution_list; s->val[OPT_X_RESOLUTION].w = 100; /* vertical resolution */ s->opt[OPT_Y_RESOLUTION].name = SANE_NAME_SCAN_Y_RESOLUTION; s->opt[OPT_Y_RESOLUTION].title = SANE_TITLE_SCAN_Y_RESOLUTION; s->opt[OPT_Y_RESOLUTION].desc = SANE_DESC_SCAN_Y_RESOLUTION; s->opt[OPT_Y_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_Y_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_Y_RESOLUTION].constraint.word_list = s->hw->vert_resolution_list; s->opt[OPT_Y_RESOLUTION].cap |= SANE_CAP_INACTIVE; s->val[OPT_Y_RESOLUTION].w = 100; /* bind resolution */ s->opt[OPT_RESOLUTION_BIND].name = SANE_NAME_RESOLUTION_BIND; s->opt[OPT_RESOLUTION_BIND].title = SANE_TITLE_RESOLUTION_BIND; s->opt[OPT_RESOLUTION_BIND].desc = SANE_DESC_RESOLUTION_BIND; s->opt[OPT_RESOLUTION_BIND].type = SANE_TYPE_BOOL; s->val[OPT_RESOLUTION_BIND].w = SANE_TRUE; if (!(s->hw->flags & ARTEC_FLAG_SEPARATE_RES)) s->opt[OPT_RESOLUTION_BIND].cap |= SANE_CAP_INACTIVE; /* Preview Mode */ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; s->opt[OPT_PREVIEW].unit = SANE_UNIT_NONE; s->opt[OPT_PREVIEW].size = sizeof (SANE_Word); s->val[OPT_PREVIEW].w = SANE_FALSE; /* Grayscale Preview Mode */ s->opt[OPT_GRAY_PREVIEW].name = SANE_NAME_GRAY_PREVIEW; s->opt[OPT_GRAY_PREVIEW].title = SANE_TITLE_GRAY_PREVIEW; s->opt[OPT_GRAY_PREVIEW].desc = SANE_DESC_GRAY_PREVIEW; s->opt[OPT_GRAY_PREVIEW].type = SANE_TYPE_BOOL; s->opt[OPT_GRAY_PREVIEW].unit = SANE_UNIT_NONE; s->opt[OPT_GRAY_PREVIEW].size = sizeof (SANE_Word); s->val[OPT_GRAY_PREVIEW].w = SANE_FALSE; /* negative */ s->opt[OPT_NEGATIVE].name = SANE_NAME_NEGATIVE; s->opt[OPT_NEGATIVE].title = SANE_TITLE_NEGATIVE; s->opt[OPT_NEGATIVE].desc = "Negative Image"; s->opt[OPT_NEGATIVE].type = SANE_TYPE_BOOL; s->val[OPT_NEGATIVE].w = SANE_FALSE; if (!(s->hw->flags & ARTEC_FLAG_MBPP_NEGATIVE)) { s->opt[OPT_NEGATIVE].cap |= SANE_CAP_INACTIVE; } /* "Geometry" group: */ s->opt[OPT_GEOMETRY_GROUP].title = "Geometry"; s->opt[OPT_GEOMETRY_GROUP].desc = ""; s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* top-left x */ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; s->opt[OPT_TL_X].unit = SANE_UNIT_MM; s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_X].constraint.range = &s->hw->x_range; s->val[OPT_TL_X].w = s->hw->x_range.min; /* top-left y */ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range; s->val[OPT_TL_Y].w = s->hw->y_range.min; /* bottom-right x */ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; s->opt[OPT_BR_X].unit = SANE_UNIT_MM; s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_X].constraint.range = &s->hw->x_range; s->val[OPT_BR_X].w = s->hw->x_range.max; /* bottom-right y */ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range; s->val[OPT_BR_Y].w = s->hw->y_range.max; /* Enhancement group: */ s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement"; s->opt[OPT_ENHANCEMENT_GROUP].desc = ""; s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* filter mode */ s->opt[OPT_FILTER_TYPE].name = "filter-type"; s->opt[OPT_FILTER_TYPE].title = "Filter Type"; s->opt[OPT_FILTER_TYPE].desc = "Filter Type for mono scans"; s->opt[OPT_FILTER_TYPE].type = SANE_TYPE_STRING; s->opt[OPT_FILTER_TYPE].size = max_string_size (filter_type_list); s->opt[OPT_FILTER_TYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_FILTER_TYPE].constraint.string_list = filter_type_list; s->val[OPT_FILTER_TYPE].s = strdup (filter_type_list[0]); s->opt[OPT_FILTER_TYPE].cap |= SANE_CAP_INACTIVE; /* contrast */ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST; s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST; s->opt[OPT_CONTRAST].type = SANE_TYPE_INT; s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE; s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CONTRAST].constraint.range = &s->hw->brightness_range; s->val[OPT_CONTRAST].w = 0x80; if (!(s->hw->flags & ARTEC_FLAG_OPT_CONTRAST)) { s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; } /* brightness */ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT; s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE; s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BRIGHTNESS].constraint.range = &s->hw->contrast_range; s->val[OPT_BRIGHTNESS].w = 0x80; if (!(s->hw->flags & ARTEC_FLAG_OPT_BRIGHTNESS)) { s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; } /* 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_INT; s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE; s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_THRESHOLD].constraint.range = &s->hw->threshold_range; s->val[OPT_THRESHOLD].w = 0x80; s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; /* halftone pattern */ s->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN; s->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN; s->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN; s->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING; s->opt[OPT_HALFTONE_PATTERN].size = max_string_size (halftone_pattern_list); s->opt[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_HALFTONE_PATTERN].constraint.string_list = halftone_pattern_list; s->val[OPT_HALFTONE_PATTERN].s = strdup (halftone_pattern_list[1]); s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; /* pixel averaging */ s->opt[OPT_PIXEL_AVG].name = "pixel-avg"; s->opt[OPT_PIXEL_AVG].title = "Pixel Averaging"; s->opt[OPT_PIXEL_AVG].desc = "Enable HardWare Pixel Averaging function"; s->opt[OPT_PIXEL_AVG].type = SANE_TYPE_BOOL; s->val[OPT_PIXEL_AVG].w = SANE_FALSE; if (!(s->hw->flags & ARTEC_FLAG_PIXEL_AVERAGING)) { s->opt[OPT_PIXEL_AVG].cap |= SANE_CAP_INACTIVE; } /* lineart line edge enhancement */ s->opt[OPT_EDGE_ENH].name = "edge-enh"; s->opt[OPT_EDGE_ENH].title = "Line Edge Enhancement"; s->opt[OPT_EDGE_ENH].desc = "Enable HardWare Lineart Line Edge Enhancement"; s->opt[OPT_EDGE_ENH].type = SANE_TYPE_BOOL; s->val[OPT_EDGE_ENH].w = SANE_FALSE; s->opt[OPT_EDGE_ENH].cap |= SANE_CAP_INACTIVE; /* custom-gamma table */ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL; s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE; /* grayscale gamma vector */ s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR; s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR; s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR; s->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE; s->val[OPT_GAMMA_VECTOR].wa = &(s->gamma_table[0][0]); s->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range; s->opt[OPT_GAMMA_VECTOR].size = s->gamma_length * sizeof (SANE_Word); /* red gamma vector */ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE; s->val[OPT_GAMMA_VECTOR_R].wa = &(s->gamma_table[1][0]); s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &(s->gamma_range); s->opt[OPT_GAMMA_VECTOR_R].size = s->gamma_length * sizeof (SANE_Word); /* green gamma vector */ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE; s->val[OPT_GAMMA_VECTOR_G].wa = &(s->gamma_table[2][0]); s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &(s->gamma_range); s->opt[OPT_GAMMA_VECTOR_G].size = s->gamma_length * sizeof (SANE_Word); /* blue gamma vector */ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE; s->val[OPT_GAMMA_VECTOR_B].wa = &(s->gamma_table[3][0]); s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &(s->gamma_range); s->opt[OPT_GAMMA_VECTOR_B].size = s->gamma_length * sizeof (SANE_Word); if (s->hw->flags & ARTEC_FLAG_GAMMA_SINGLE) { 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; } if (!(s->hw->flags & ARTEC_FLAG_GAMMA)) { 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; s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; } /* transparency */ s->opt[OPT_TRANSPARENCY].name = "transparency"; s->opt[OPT_TRANSPARENCY].title = "Transparency"; s->opt[OPT_TRANSPARENCY].desc = "Use transparency adaptor"; s->opt[OPT_TRANSPARENCY].type = SANE_TYPE_BOOL; s->val[OPT_TRANSPARENCY].w = SANE_FALSE; /* ADF */ s->opt[OPT_ADF].name = "adf"; s->opt[OPT_ADF].title = "ADF"; s->opt[OPT_ADF].desc = "Use ADF"; s->opt[OPT_ADF].type = SANE_TYPE_BOOL; s->val[OPT_ADF].w = SANE_FALSE; /* Calibration group: */ s->opt[OPT_CALIBRATION_GROUP].title = "Calibration"; s->opt[OPT_CALIBRATION_GROUP].desc = ""; s->opt[OPT_CALIBRATION_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_CALIBRATION_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_CALIBRATION_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* Calibrate Every Scan? */ s->opt[OPT_QUALITY_CAL].name = SANE_NAME_QUALITY_CAL; s->opt[OPT_QUALITY_CAL].title = "Hardware Calibrate Every Scan"; s->opt[OPT_QUALITY_CAL].desc = "Perform hardware calibration on every scan"; s->opt[OPT_QUALITY_CAL].type = SANE_TYPE_BOOL; s->val[OPT_QUALITY_CAL].w = SANE_FALSE; if (!(s->hw->flags & ARTEC_FLAG_CALIBRATE)) { s->opt[OPT_QUALITY_CAL].cap |= SANE_CAP_INACTIVE; } /* Perform Software Quality Calibration */ s->opt[OPT_SOFTWARE_CAL].name = "software-cal"; s->opt[OPT_SOFTWARE_CAL].title = "Software Color Calibration"; s->opt[OPT_SOFTWARE_CAL].desc = "Perform software quality calibration in " "addition to hardware calibration"; s->opt[OPT_SOFTWARE_CAL].type = SANE_TYPE_BOOL; s->val[OPT_SOFTWARE_CAL].w = SANE_FALSE; /* check for RGB calibration now because we have only implemented software */ /* calibration in conjunction with hardware RGB calibration */ if ((!(s->hw->flags & ARTEC_FLAG_CALIBRATE)) || (!(s->hw->flags & ARTEC_FLAG_CALIBRATE_RGB))) { s->opt[OPT_SOFTWARE_CAL].cap |= SANE_CAP_INACTIVE; } return (SANE_STATUS_GOOD); } static SANE_Status do_cancel (ARTEC_Scanner * s) { DBG (7, "do_cancel()\n"); s->scanning = SANE_FALSE; /* DAL: Terminate a three pass scan properly */ /* if (s->this_pass == 3) */ s->this_pass = 0; if ((s->hw->flags & ARTEC_FLAG_RGB_LINE_OFFSET) && (tmp_line_buf != NULL)) { artec_buffer_line_offset_free (); } if (s->fd >= 0) { sanei_scsi_close (s->fd); s->fd = -1; } return (SANE_STATUS_CANCELLED); } static SANE_Status attach_one (const char *dev) { DBG (7, "attach_one()\n"); attach (dev, 0); return (SANE_STATUS_GOOD); } SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { char dev_name[PATH_MAX], *cp; size_t len; FILE *fp; DBG_INIT (); DBG (1, "Artec/Ultima backend version %d.%d.%d, last mod: %s\n", ARTEC_MAJOR, ARTEC_MINOR, ARTEC_SUB, ARTEC_LAST_MOD); DBG (1, "http://www4.infi.net/~cpinkham/sane-artec-doc.html\n"); DBG (7, "sane_init()\n" ); devlist = 0; /* make sure these 2 are empty */ strcpy (artec_vendor, ""); strcpy (artec_model, ""); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); if (authorize) DBG (7, "sane_init(), authorize %s null\n", (authorize) ? "!=" : "=="); fp = sanei_config_open (ARTEC_CONFIG_FILE); if (!fp) { /* default to /dev/scanner instead of insisting on config file */ attach ("/dev/scanner", 0); return (SANE_STATUS_GOOD); } while (sanei_config_read (dev_name, sizeof (dev_name), fp)) { cp = artec_skip_whitespace (dev_name); /* ignore line comments and blank lines */ if ((!*cp) || (*cp == '#')) continue; len = strlen (cp); /* ignore empty lines */ if (!len) continue; DBG (50, "%s line: '%s', len = %lu\n", ARTEC_CONFIG_FILE, cp, (u_long) len); /* check to see if they forced a vendor string in artec.conf */ if ((strncmp (cp, "vendor", 6) == 0) && isspace (cp[6])) { cp += 7; cp = artec_skip_whitespace (cp); strcpy (artec_vendor, cp); DBG (5, "sane_init: Forced vendor string '%s' in %s.\n", cp, ARTEC_CONFIG_FILE); } /* OK, maybe they forced the model string in artec.conf */ else if ((strncmp (cp, "model", 5) == 0) && isspace (cp[5])) { cp += 6; cp = artec_skip_whitespace (cp); strcpy (artec_model, cp); DBG (5, "sane_init: Forced model string '%s' in %s.\n", cp, ARTEC_CONFIG_FILE); } /* well, nothing else to do but attempt the attach */ else { sanei_config_attach_matching_devices (dev_name, attach_one); strcpy (artec_vendor, ""); strcpy (artec_model, ""); } } fclose (fp); return (SANE_STATUS_GOOD); } void sane_exit (void) { ARTEC_Device *dev, *next; DBG (7, "sane_exit()\n"); for (dev = first_dev; dev; dev = next) { next = dev->next; free ((void *) dev->sane.name); free ((void *) dev->sane.model); free (dev); } if (devlist) free (devlist); } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { ARTEC_Device *dev; int i; DBG (7, "sane_get_devices( device_list, local_only = %d )\n", local_only ); if (devlist) free (devlist); devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); if (!devlist) return SANE_STATUS_NO_MEM; i = 0; for (dev = first_dev; i < num_devices; dev = dev->next) devlist[i++] = &dev->sane; devlist[i++] = 0; *device_list = devlist; return (SANE_STATUS_GOOD); } SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { SANE_Status status; ARTEC_Device *dev; ARTEC_Scanner *s; int i, j; DBG (7, "sane_open()\n"); if (devicename[0]) { for (dev = first_dev; dev; dev = dev->next) if (strcmp (dev->sane.name, devicename) == 0) break; if (!dev) { status = attach (devicename, &dev); if (status != SANE_STATUS_GOOD) return (status); } } else { /* empty devicname -> use first device */ dev = first_dev; } if (!dev) return SANE_STATUS_INVAL; s = malloc (sizeof (*s)); if (!s) return SANE_STATUS_NO_MEM; memset (s, 0, sizeof (*s)); s->fd = -1; s->hw = dev; s->this_pass = 0; s->gamma_length = s->hw->gamma_length; s->gamma_range.min = 0; s->gamma_range.max = s->gamma_length - 1; s->gamma_range.quant = 0; /* not sure if I need this or not, it was in the umax backend though. :-) */ for (j = 0; j < s->gamma_length; ++j) { s->gamma_table[0][j] = j * (s->gamma_length - 1) / s->gamma_length; } for (i = 1; i < 4; i++) { for (j = 0; j < s->gamma_length; ++j) { s->gamma_table[i][j] = j; } } init_options (s); /* insert newly opened handle into list of open handles: */ s->next = first_handle; first_handle = s; *handle = s; if (s->hw->flags & ARTEC_FLAG_CALIBRATE) { status = sanei_scsi_open (s->hw->sane.name, &s->fd, 0, 0); if (status != SANE_STATUS_GOOD) { DBG (1, "error opening scanner for initial calibration: %s\n", sane_strstatus (status)); s->fd = -1; return status; } status = artec_calibrate_shading (s); if (status != SANE_STATUS_GOOD) { DBG (1, "initial shading calibration failed: %s\n", sane_strstatus (status)); sanei_scsi_close (s->fd); s->fd = -1; return status; } sanei_scsi_close (s->fd); } return (SANE_STATUS_GOOD); } void sane_close (SANE_Handle handle) { ARTEC_Scanner *prev, *s; DBG (7, "sane_close()\n"); if ((DBG_LEVEL == 101) && (debug_fd > -1)) { close (debug_fd); DBG (101, "closed artec.data.raw output file\n"); } /* remove handle from list of open handles: */ prev = 0; for (s = first_handle; s; s = s->next) { if (s == handle) break; prev = s; } if (!s) { DBG (1, "close: invalid handle %p\n", handle); return; /* oops, not a handle we know about */ } if (s->scanning) do_cancel (handle); if (prev) prev->next = s->next; else first_handle = s->next; free (handle); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { ARTEC_Scanner *s = handle; DBG (7, "sane_get_option_descriptor()\n"); if (((unsigned) option >= NUM_OPTIONS) || (option < 0 )) return (0); return (s->opt + option); } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { ARTEC_Scanner *s = handle; SANE_Status status; SANE_Word w, cap; DBG (7, "sane_control_option()\n"); if (info) *info = 0; if (s->scanning) return SANE_STATUS_DEVICE_BUSY; if (s->this_pass) return SANE_STATUS_DEVICE_BUSY; if (option >= NUM_OPTIONS) return SANE_STATUS_INVAL; cap = s->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) return SANE_STATUS_INVAL; if (action == SANE_ACTION_GET_VALUE) { DBG (13, "sane_control_option %d, get value\n", option); switch (option) { /* word options: */ case OPT_X_RESOLUTION: case OPT_Y_RESOLUTION: case OPT_PREVIEW: case OPT_GRAY_PREVIEW: case OPT_RESOLUTION_BIND: case OPT_NEGATIVE: case OPT_TRANSPARENCY: case OPT_ADF: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_NUM_OPTS: case OPT_QUALITY_CAL: case OPT_SOFTWARE_CAL: case OPT_CONTRAST: case OPT_BRIGHTNESS: case OPT_THRESHOLD: case OPT_CUSTOM_GAMMA: case OPT_PIXEL_AVG: case OPT_EDGE_ENH: *(SANE_Word *) val = s->val[option].w; return (SANE_STATUS_GOOD); /* string options: */ case OPT_MODE: case OPT_FILTER_TYPE: case OPT_HALFTONE_PATTERN: strcpy (val, s->val[option].s); return (SANE_STATUS_GOOD); /* word array options: */ case OPT_GAMMA_VECTOR: case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: memcpy (val, s->val[option].wa, s->opt[option].size); return (SANE_STATUS_GOOD); } } else if (action == SANE_ACTION_SET_VALUE) { DBG (13, "sane_control_option %d, set value\n", option); if (!SANE_OPTION_IS_SETTABLE (cap)) return (SANE_STATUS_INVAL); status = sanei_constrain_value (s->opt + option, val, info); if (status != SANE_STATUS_GOOD) return (status); switch (option) { /* (mostly) side-effect-free word options: */ case OPT_X_RESOLUTION: case OPT_Y_RESOLUTION: case OPT_BR_X: case OPT_BR_Y: case OPT_TL_X: case OPT_TL_Y: if (info && s->val[option].w != *(SANE_Word *) val) *info |= SANE_INFO_RELOAD_PARAMS; /* fall through */ case OPT_PREVIEW: case OPT_GRAY_PREVIEW: case OPT_QUALITY_CAL: case OPT_SOFTWARE_CAL: case OPT_NUM_OPTS: case OPT_NEGATIVE: case OPT_TRANSPARENCY: case OPT_ADF: case OPT_CONTRAST: case OPT_BRIGHTNESS: case OPT_THRESHOLD: case OPT_PIXEL_AVG: case OPT_EDGE_ENH: s->val[option].w = *(SANE_Word *) val; return (SANE_STATUS_GOOD); case OPT_MODE: { if (s->val[option].s) free (s->val[option].s); s->val[option].s = (SANE_Char *) strdup (val); if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE; /* options INvisible by 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; s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; s->opt[OPT_SOFTWARE_CAL].cap |= SANE_CAP_INACTIVE; s->opt[OPT_EDGE_ENH].cap |= SANE_CAP_INACTIVE; /* options VISIBLE by default */ s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_FILTER_TYPE].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_NEGATIVE].cap &= ~SANE_CAP_INACTIVE; if (strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) == 0) { /* Lineart mode */ s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; /* OFF */ s->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE; if (s->hw->flags & ARTEC_FLAG_ENHANCE_LINE_EDGE) s->opt[OPT_EDGE_ENH].cap &= ~SANE_CAP_INACTIVE; } else if (strcmp (val, SANE_VALUE_SCAN_MODE_HALFTONE) == 0) { /* Halftone mode */ if (s->hw->flags & ARTEC_FLAG_HALFTONE_PATTERN) s->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE; } else if (strcmp (val, SANE_VALUE_SCAN_MODE_GRAY) == 0) { /* Grayscale mode */ if (!(s->hw->flags & ARTEC_FLAG_MBPP_NEGATIVE)) { s->opt[OPT_NEGATIVE].cap |= SANE_CAP_INACTIVE; } } else if (strcmp (val, SANE_VALUE_SCAN_MODE_COLOR) == 0) { /* Color mode */ s->opt[OPT_FILTER_TYPE].cap |= SANE_CAP_INACTIVE; s->opt[OPT_SOFTWARE_CAL].cap &= ~SANE_CAP_INACTIVE; if (!(s->hw->flags & ARTEC_FLAG_MBPP_NEGATIVE)) { s->opt[OPT_NEGATIVE].cap |= SANE_CAP_INACTIVE; } } } return (SANE_STATUS_GOOD); case OPT_FILTER_TYPE: case OPT_HALFTONE_PATTERN: if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); return (SANE_STATUS_GOOD); case OPT_RESOLUTION_BIND: if (s->val[option].w != *(SANE_Word *) val) { s->val[option].w = *(SANE_Word *) val; if (info) { *info |= SANE_INFO_RELOAD_OPTIONS; } if (s->val[option].w == SANE_FALSE) { /* don't bind */ s->opt[OPT_Y_RESOLUTION].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_X_RESOLUTION; s->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_X_RESOLUTION; } else { /* bind */ s->opt[OPT_Y_RESOLUTION].cap |= SANE_CAP_INACTIVE; s->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; } } return (SANE_STATUS_GOOD); /* side-effect-free word-array options: */ case OPT_GAMMA_VECTOR: case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: memcpy (s->val[option].wa, val, s->opt[option].size); return (SANE_STATUS_GOOD); /* options with side effects: */ case OPT_CUSTOM_GAMMA: w = *(SANE_Word *) val; if (w == s->val[OPT_CUSTOM_GAMMA].w) return (SANE_STATUS_GOOD); s->val[OPT_CUSTOM_GAMMA].w = w; if (w) /* use custom_gamma_table */ { const char *mode = s->val[OPT_MODE].s; if ((strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0) || (strcmp (mode, SANE_VALUE_SCAN_MODE_HALFTONE) == 0) || (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0)) { s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; } else if (strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) == 0) { s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; if (!(s->hw->flags & ARTEC_FLAG_GAMMA_SINGLE)) { s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; } } } else /* don't use custom_gamma_table */ { 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; } if (info) *info |= SANE_INFO_RELOAD_OPTIONS; return (SANE_STATUS_GOOD); } } return (SANE_STATUS_INVAL); } static void set_pass_parameters (SANE_Handle handle) { ARTEC_Scanner *s = handle; DBG (7, "set_pass_parameters()\n"); if (s->threepasscolor) { s->this_pass += 1; DBG (9, "set_pass_parameters: three-pass, on %d\n", s->this_pass); switch (s->this_pass) { case 1: s->params.format = SANE_FRAME_RED; s->params.last_frame = SANE_FALSE; break; case 2: s->params.format = SANE_FRAME_GREEN; s->params.last_frame = SANE_FALSE; break; case 3: s->params.format = SANE_FRAME_BLUE; s->params.last_frame = SANE_TRUE; break; default: DBG (9, "set_pass_parameters: What?!? pass %d = filter?\n", s->this_pass); break; } } else s->this_pass = 0; } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { ARTEC_Scanner *s = handle; DBG (7, "sane_get_parameters()\n"); if (!s->scanning) { double width, height; memset (&s->params, 0, sizeof (s->params)); s->x_resolution = s->val[OPT_X_RESOLUTION].w; s->y_resolution = s->val[OPT_Y_RESOLUTION].w; if ((s->val[OPT_RESOLUTION_BIND].w == SANE_TRUE) || (s->val[OPT_PREVIEW].w == SANE_TRUE)) { s->y_resolution = s->x_resolution; } s->tl_x = SANE_UNFIX (s->val[OPT_TL_X].w) / MM_PER_INCH * s->x_resolution; s->tl_y = SANE_UNFIX (s->val[OPT_TL_Y].w) / MM_PER_INCH * s->y_resolution; width = SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w); height = SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w); if ((s->x_resolution > 0.0) && (s->y_resolution > 0.0) && (width > 0.0) && (height > 0.0)) { s->params.pixels_per_line = width * s->x_resolution / MM_PER_INCH + 1; s->params.lines = height * s->y_resolution / MM_PER_INCH + 1; } s->onepasscolor = SANE_FALSE; s->threepasscolor = SANE_FALSE; s->params.last_frame = SANE_TRUE; if ((s->val[OPT_PREVIEW].w == SANE_TRUE) && (s->val[OPT_GRAY_PREVIEW].w == SANE_TRUE)) { s->mode = SANE_VALUE_SCAN_MODE_GRAY; } else { s->mode = s->val[OPT_MODE].s; } if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_LINEART) == 0) || (strcmp (s->mode, SANE_VALUE_SCAN_MODE_HALFTONE) == 0)) { s->params.format = SANE_FRAME_GRAY; s->params.bytes_per_line = (s->params.pixels_per_line + 7) / 8; s->params.depth = 1; s->line_offset = 0; /* round pixels_per_line up to the next full byte of pixels */ /* this way we don't have to do bit buffering, pixels_per_line is */ /* what is used in the set window command. */ /* SANE expects the last byte in a line to be padded if it's not */ /* full, so this should not affect scans in a negative way */ s->params.pixels_per_line = s->params.bytes_per_line * 8; } else if (strcmp (s->mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) { s->params.format = SANE_FRAME_GRAY; s->params.bytes_per_line = s->params.pixels_per_line; s->params.depth = 8; s->line_offset = 0; } else { s->params.bytes_per_line = s->params.pixels_per_line; s->params.depth = 8; if (s->hw->flags & ARTEC_FLAG_ONE_PASS_SCANNER) { s->onepasscolor = SANE_TRUE; s->params.format = SANE_FRAME_RGB; s->params.bytes_per_line *= 3; /* * line offsets from documentation. * (I don't yet see a common formula I can easily use) */ /* FIXME: figure out a cleaner way to do this... */ s->line_offset = 0; /* default */ if ((!strcmp (s->hw->sane.model, "AT3")) || (!strcmp (s->hw->sane.model, "A6000C")) || (!strcmp (s->hw->sane.model, "A6000C PLUS")) || (!strcmp (s->hw->sane.model, "AT6"))) { /* formula #1 */ /* ranges from 1 at 50dpi to 16 at 600dpi */ s->line_offset = 8 * (s->y_resolution / 300.0); } else if (!strcmp (s->hw->sane.model, "AT12")) { /* formula #2 */ /* ranges from 0 at 25dpi to 16 at 1200dpi */ /***********************************************************/ /* this should be handled in hardware for now, so leave it */ /* sitting at zero for now. */ /***********************************************************/ /* s->line_offset = 16 * ( s->y_resolution / 1200.0 ); */ } else if (!strcmp (s->hw->sane.model, "AM12S")) { /* formula #3 */ /* ranges from 0 at 50dpi to 8 at 1200dpi */ s->line_offset = 8 * (s->y_resolution / 1200.0); } } else { s->params.last_frame = SANE_FALSE; s->threepasscolor = SANE_TRUE; s->line_offset = 0; } } } if (params) *params = s->params; return (SANE_STATUS_GOOD); } SANE_Status sane_start (SANE_Handle handle) { ARTEC_Scanner *s = handle; SANE_Status status; DBG (7, "sane_start()\n"); if (debug_fd != -1) { close (debug_fd); debug_fd = -1; } if (DBG_LEVEL == 101) { debug_fd = open ("artec.data.raw", O_WRONLY | O_CREAT | O_TRUNC, 0666); if (debug_fd > -1) DBG (101, "opened artec.data.raw output file\n"); } /* First make sure we have a current parameter set. Some of the */ /* parameters will be overwritten below, but that's OK. */ status = sane_get_parameters (s, 0); if (status != SANE_STATUS_GOOD) return status; /* DAL: For 3 pass colour set the current pass parameters */ if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) == 0) && s->threepasscolor) set_pass_parameters (s); /* DAL: For single pass scans and the first pass of a 3 pass scan */ if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) != 0) || (!s->threepasscolor) || ((s->threepasscolor) && (s->this_pass == 1))) { if (s->hw->flags & ARTEC_FLAG_SENSE_HANDLER) { status = sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, (void *)s); } else { status = sanei_scsi_open (s->hw->sane.name, &s->fd, 0, 0); } if (status != SANE_STATUS_GOOD) { DBG (1, "open of %s failed: %s\n", s->hw->sane.name, sane_strstatus (status)); return status; } /* DB added wait_ready */ status = wait_ready (s->fd); if (status != SANE_STATUS_GOOD) { DBG (1, "wait for scanner ready failed: %s\n", sane_strstatus (status)); return status; } } s->bytes_to_read = s->params.bytes_per_line * s->params.lines; DBG (9, "%d pixels per line, %d bytes, %d lines high, xdpi = %d, " "ydpi = %d, btr = %lu\n", s->params.pixels_per_line, s->params.bytes_per_line, s->params.lines, s->x_resolution, s->y_resolution, (u_long) s->bytes_to_read); /* DAL: For single pass scans and the first pass of a 3 pass scan */ if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) != 0) || !s->threepasscolor || (s->threepasscolor && s->this_pass == 1)) { /* do a calibrate if scanner requires/recommends it */ if ((s->hw->flags & ARTEC_FLAG_CALIBRATE) && (s->val[OPT_QUALITY_CAL].w == SANE_TRUE)) { status = artec_calibrate_shading (s); if (status != SANE_STATUS_GOOD) { DBG (1, "shading calibration failed: %s\n", sane_strstatus (status)); return status; } } /* DB added wait_ready */ status = wait_ready (s->fd); if (status != SANE_STATUS_GOOD) { DBG (1, "wait for scanner ready failed: %s\n", sane_strstatus (status)); return status; } /* send the custom gamma table if we have one */ if (s->hw->flags & ARTEC_FLAG_GAMMA) artec_send_gamma_table (s); /* now set our scan window */ status = artec_set_scan_window (s); if (status != SANE_STATUS_GOOD) { DBG (1, "set scan window failed: %s\n", sane_strstatus (status)); return status; } /* DB added wait_ready */ status = wait_ready (s->fd); if (status != SANE_STATUS_GOOD) { DBG (1, "wait for scanner ready failed: %s\n", sane_strstatus (status)); return status; } } /* now we can start the actual scan */ /* DAL: For single pass scans and the first pass of a 3 pass scan */ if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) != 0) || (!s->threepasscolor) || (s->this_pass == 1)) { /* DAL - do mode select before each scan */ /* The mode is NOT turned off at the end of the scan */ artec_mode_select (s); status = artec_start_scan (s); if (status != SANE_STATUS_GOOD) { DBG (1, "start scan: %s\n", sane_strstatus (status)); return status; } } s->scanning = SANE_TRUE; return (SANE_STATUS_GOOD); } #if 0 static void binout (SANE_Byte byte) { SANE_Byte b = byte; int bit; for (bit = 0; bit < 8; bit++) { DBG (9, "%d", b & 128 ? 1 : 0); b = b << 1; } } #endif static SANE_Status artec_sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { ARTEC_Scanner *s = handle; SANE_Status status; size_t nread; size_t lread; size_t bytes_read; size_t rows_read; size_t max_read_rows; size_t max_ret_rows; size_t remaining_rows; size_t rows_available; size_t line; SANE_Byte temp_buf[ARTEC_MAX_READ_SIZE]; SANE_Byte line_buf[ARTEC_MAX_READ_SIZE]; DBG (7, "artec_sane_read( %p, %p, %d, %d )\n", handle, (void *) buf, max_len, *len); *len = 0; if (s->bytes_to_read == 0) { if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) != 0) || !s->threepasscolor || (s->threepasscolor && s->this_pass == 3)) { do_cancel (s); /* without this a 4th pass is attempted, yet do_cancel does this */ s->scanning = SANE_FALSE; } return (SANE_STATUS_EOF); } if (!s->scanning) return do_cancel (s); remaining_rows = (s->bytes_to_read + s->params.bytes_per_line - 1) / s->params.bytes_per_line; max_read_rows = s->hw->max_read_size / s->params.bytes_per_line; max_ret_rows = max_len / s->params.bytes_per_line; while (artec_get_status (s->fd) == 0) { DBG (120, "hokey loop till data available\n"); usleep (50000); /* sleep for .05 second */ } rows_read = 0; bytes_read = 0; while ((rows_read < max_ret_rows) && (rows_read < remaining_rows)) { DBG (50, "top of while loop, rr = %lu, mrr = %lu, rem = %lu\n", (u_long) rows_read, (u_long) max_ret_rows, (u_long) remaining_rows); if (s->bytes_to_read - bytes_read <= s->params.bytes_per_line * max_read_rows) { nread = s->bytes_to_read - bytes_read; } else { nread = s->params.bytes_per_line * max_read_rows; } lread = nread / s->params.bytes_per_line; if ((max_read_rows - rows_read) < lread) { lread = max_read_rows - rows_read; nread = lread * s->params.bytes_per_line; } if ((max_ret_rows - rows_read) < lread) { lread = max_ret_rows - rows_read; nread = lread * s->params.bytes_per_line; } while ((rows_available = artec_get_status (s->fd)) == 0) { DBG (120, "hokey loop till data available\n"); usleep (50000); /* sleep for .05 second */ } if (rows_available < lread) { lread = rows_available; nread = lread * s->params.bytes_per_line; } /* This should never happen, but just in case... */ if (nread > (s->bytes_to_read - bytes_read)) { nread = s->bytes_to_read - bytes_read; lread = 1; } DBG (50, "rows_available = %lu, params.lines = %d, bytes_per_line = %d\n", (u_long) rows_available, s->params.lines, s->params.bytes_per_line); DBG (50, "bytes_to_read = %lu, max_len = %d, max_rows = %lu\n", (u_long) s->bytes_to_read, max_len, (u_long) max_ret_rows); DBG (50, "nread = %lu, lread = %lu, bytes_read = %lu, rows_read = %lu\n", (u_long) nread, (u_long) lread, (u_long) bytes_read, (u_long) rows_read); status = read_data (s->fd, ARTEC_DATA_IMAGE, temp_buf, &nread); if (status != SANE_STATUS_GOOD) { end_scan (s); do_cancel (s); return (SANE_STATUS_IO_ERROR); } if ((DBG_LEVEL == 101) && (debug_fd > -1)) { write (debug_fd, temp_buf, nread); } if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) == 0) && (s->hw->flags & ARTEC_FLAG_RGB_LINE_OFFSET)) { for (line = 0; line < lread; line++) { memcpy (line_buf, temp_buf + (line * s->params.bytes_per_line), s->params.bytes_per_line); nread = s->params.bytes_per_line; artec_buffer_line_offset (s, s->line_offset, line_buf, &nread); if (nread > 0) { if (s->hw->flags & ARTEC_FLAG_RGB_CHAR_SHIFT) { artec_line_rgb_to_byte_rgb (line_buf, s->params.pixels_per_line); } if (s->hw->flags & ARTEC_FLAG_IMAGE_REV_LR) { artec_reverse_line (s, line_buf); } /* do software calibration if necessary */ if (s->val[OPT_SOFTWARE_CAL].w) { artec_software_rgb_calibrate (s, line_buf, 1); } memcpy (buf + bytes_read, line_buf, s->params.bytes_per_line); bytes_read += nread; rows_read++; } } } else { if ((s->hw->flags & ARTEC_FLAG_IMAGE_REV_LR) || ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) == 0) && (s->hw->flags & ARTEC_FLAG_RGB_CHAR_SHIFT))) { for (line = 0; line < lread; line++) { if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) == 0) && (s->hw->flags & ARTEC_FLAG_RGB_CHAR_SHIFT)) { artec_line_rgb_to_byte_rgb (temp_buf + (line * s->params.bytes_per_line), s->params.pixels_per_line); } if (s->hw->flags & ARTEC_FLAG_IMAGE_REV_LR) { artec_reverse_line (s, temp_buf + (line * s->params.bytes_per_line)); } } } /* do software calibration if necessary */ if ((s->val[OPT_SOFTWARE_CAL].w) && (strcmp (s->mode, SANE_VALUE_SCAN_MODE_COLOR) == 0)) { artec_software_rgb_calibrate (s, temp_buf, lread); } memcpy (buf + bytes_read, temp_buf, nread); bytes_read += nread; rows_read += lread; } } *len = bytes_read; s->bytes_to_read -= bytes_read; DBG (9, "artec_sane_read() returning, we read %lu bytes, %lu left\n", (u_long) * len, (u_long) s->bytes_to_read); if ((s->bytes_to_read == 0) && (s->hw->flags & ARTEC_FLAG_RGB_LINE_OFFSET) && (tmp_line_buf != NULL)) { artec_buffer_line_offset_free (); } return (SANE_STATUS_GOOD); } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { ARTEC_Scanner *s = handle; SANE_Status status; int bytes_to_copy; int loop; static SANE_Byte temp_buf[ARTEC_MAX_READ_SIZE]; static int bytes_in_buf = 0; DBG (7, "sane_read( %p, %p, %d, %d )\n", handle, (void *) buf, max_len, *len); DBG (9, "sane_read: bib = %d, ml = %d\n", bytes_in_buf, max_len); if (bytes_in_buf != 0) { bytes_to_copy = max_len < bytes_in_buf ? max_len : bytes_in_buf; } else { status = artec_sane_read (s, temp_buf, s->hw->max_read_size, len); if (status != SANE_STATUS_GOOD) { return (status); } bytes_in_buf = *len; if (*len == 0) { return (SANE_STATUS_GOOD); } bytes_to_copy = max_len < s->hw->max_read_size ? max_len : s->hw->max_read_size; bytes_to_copy = *len < bytes_to_copy ? *len : bytes_to_copy; } memcpy (buf, temp_buf, bytes_to_copy); bytes_in_buf -= bytes_to_copy; *len = bytes_to_copy; DBG (9, "sane_read: btc = %d, bib now = %d\n", bytes_to_copy, bytes_in_buf); for (loop = 0; loop < bytes_in_buf; loop++) { temp_buf[loop] = temp_buf[loop + bytes_to_copy]; } return (SANE_STATUS_GOOD); } void sane_cancel (SANE_Handle handle) { ARTEC_Scanner *s = handle; DBG (7, "sane_cancel()\n"); if (s->scanning) { s->scanning = SANE_FALSE; abort_scan (s); do_cancel (s); } } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { DBG (7, "sane_set_io_mode( %p, %d )\n", handle, non_blocking); return (SANE_STATUS_UNSUPPORTED); } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { DBG (7, "sane_get_select_fd( %p, %d )\n", handle, *fd ); return (SANE_STATUS_UNSUPPORTED); } backends-1.3.0/backend/artec.conf.in000066400000000000000000000000311456256263500172360ustar00rootroot00000000000000scsi ULTIMA /dev/scanner backends-1.3.0/backend/artec.h000066400000000000000000000202421456256263500161410ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1996 David Mosberger-Tang 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, see . 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 the Artec/Ultima scanners. Copyright (C) 1998,1999 Chris Pinkham Released under the terms of the GPL. *NO WARRANTY* ********************************************************************* For feedback/information: cpinkham@corp.infi.net http://www4.infi.net/~cpinkham/sane/sane-artec-doc.html ********************************************************************* */ #ifndef artec_h #define artec_h #include #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #define MAX(a,b) (((a) > (b)) ? (a) : (b)) #define ARTEC_MIN_X( hw ) ( hw->horz_resolution_list[ 0 ] ? \ hw->horz_resolution_list[ 1 ] : 0 ) #define ARTEC_MAX_X( hw ) ( hw->horz_resolution_list[ 0 ] ? \ hw->horz_resolution_list[ \ hw->horz_resolution_list[ 0 ] ] : 0 ) #define ARTEC_MIN_Y( hw ) ( hw->vert_resolution_list[ 0 ] ? \ hw->vert_resolution_list[ 1 ] : 0 ) #define ARTEC_MAX_Y( hw ) ( hw->vert_resolution_list[ 0 ] ? \ hw->vert_resolution_list[ \ hw->vert_resolution_list[ 0 ] ] : 0 ) typedef enum { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_MODE, OPT_X_RESOLUTION, OPT_Y_RESOLUTION, OPT_RESOLUTION_BIND, OPT_PREVIEW, OPT_GRAY_PREVIEW, OPT_NEGATIVE, OPT_GEOMETRY_GROUP, OPT_TL_X, /* top-left x */ OPT_TL_Y, /* top-left y */ OPT_BR_X, /* bottom-right x */ OPT_BR_Y, /* bottom-right y */ OPT_ENHANCEMENT_GROUP, OPT_CONTRAST, OPT_BRIGHTNESS, OPT_THRESHOLD, OPT_HALFTONE_PATTERN, OPT_FILTER_TYPE, OPT_PIXEL_AVG, OPT_EDGE_ENH, OPT_CUSTOM_GAMMA, /* use custom gamma table */ OPT_GAMMA_VECTOR, OPT_GAMMA_VECTOR_R, OPT_GAMMA_VECTOR_G, OPT_GAMMA_VECTOR_B, OPT_TRANSPARENCY, OPT_ADF, OPT_CALIBRATION_GROUP, OPT_QUALITY_CAL, OPT_SOFTWARE_CAL, /* must come last */ NUM_OPTIONS } ARTEC_Option; /* Some FLAGS */ #define ARTEC_FLAG_CALIBRATE 0x00000001 /* supports hardware calib */ #define ARTEC_FLAG_CALIBRATE_RGB 0x00000003 /* yes 3, set CALIB. also */ #define ARTEC_FLAG_CALIBRATE_DARK_WHITE 0x00000005 /* yes 5, set CALIB. also */ #define ARTEC_FLAG_RGB_LINE_OFFSET 0x00000008 /* need line offset buffer */ #define ARTEC_FLAG_RGB_CHAR_SHIFT 0x00000010 /* RRRRGGGGBBBB line fmt */ #define ARTEC_FLAG_OPT_CONTRAST 0x00000020 /* supports set contrast */ #define ARTEC_FLAG_ONE_PASS_SCANNER 0x00000040 /* single pass scanner */ #define ARTEC_FLAG_GAMMA 0x00000080 /* supports set gamma */ #define ARTEC_FLAG_GAMMA_SINGLE 0x00000180 /* yes 180, implies GAMMA */ #define ARTEC_FLAG_SEPARATE_RES 0x00000200 /* separate x & y scan res */ #define ARTEC_FLAG_IMAGE_REV_LR 0x00000400 /* reversed left-right */ #define ARTEC_FLAG_ENHANCE_LINE_EDGE 0x00000800 /* line edge enhancement */ #define ARTEC_FLAG_HALFTONE_PATTERN 0x00001000 /* > 1 halftone pattern */ #define ARTEC_FLAG_REVERSE_WINDOW 0x00002000 /* reverse selected area */ #define ARTEC_FLAG_SC_BUFFERS_LINES 0x00004000 /* scanner has line buffer */ #define ARTEC_FLAG_SC_HANDLES_OFFSET 0x00008000 /* sc. handles line offset */ #define ARTEC_FLAG_SENSE_HANDLER 0x00010000 /* supports sense handler */ #define ARTEC_FLAG_SENSE_ENH_18 0x00020000 /* supports enh. byte 18 */ #define ARTEC_FLAG_SENSE_BYTE_19 0x00040000 /* supports sense byte 19 */ #define ARTEC_FLAG_SENSE_BYTE_22 0x00080000 /* supports sense byte 22 */ #define ARTEC_FLAG_PIXEL_AVERAGING 0x00100000 /* supports pixel avg-ing */ #define ARTEC_FLAG_ADF 0x00200000 /* auto document feeder */ #define ARTEC_FLAG_OPT_BRIGHTNESS 0x00400000 /* supports set brightness */ #define ARTEC_FLAG_MBPP_NEGATIVE 0x00800000 /* can negate > 1bpp modes */ typedef enum { ARTEC_COMP_LINEART = 0, ARTEC_COMP_HALFTONE, ARTEC_COMP_GRAY, ARTEC_COMP_UNSUPP1, ARTEC_COMP_UNSUPP2, ARTEC_COMP_COLOR } ARTEC_Image_Composition; typedef enum { ARTEC_DATA_IMAGE = 0, ARTEC_DATA_UNSUPP1, ARTEC_DATA_HALFTONE_PATTERN, /* 2 */ ARTEC_DATA_UNSUPP3, ARTEC_DATA_RED_SHADING, /* 4 */ ARTEC_DATA_GREEN_SHADING, /* 5 */ ARTEC_DATA_BLUE_SHADING, /* 6 */ ARTEC_DATA_WHITE_SHADING_OPT, /* 7 */ ARTEC_DATA_WHITE_SHADING_TRANS, /* 8 */ ARTEC_DATA_CAPABILITY_DATA, /* 9 */ ARTEC_DATA_DARK_SHADING, /* 10, 0xA */ ARTEC_DATA_RED_GAMMA_CURVE, /* 11, 0xB */ ARTEC_DATA_GREEN_GAMMA_CURVE, /* 12, 0xC */ ARTEC_DATA_BLUE_GAMMA_CURVE, /* 13, 0xD */ ARTEC_DATA_ALL_GAMMA_CURVE /* 14, 0xE */ } ARTEC_Read_Data_Type; typedef enum { ARTEC_CALIB_RGB = 0, ARTEC_CALIB_DARK_WHITE } ARTEC_Calibrate_Method; typedef enum { ARTEC_FILTER_MONO = 0, ARTEC_FILTER_RED, ARTEC_FILTER_GREEN, ARTEC_FILTER_BLUE } ARTEC_Filter_Type; typedef enum { ARTEC_SOFT_CALIB_RED = 0, ARTEC_SOFT_CALIB_GREEN, ARTEC_SOFT_CALIB_BLUE } ARTEC_Software_Calibrate; typedef struct ARTEC_Device { struct ARTEC_Device *next; SANE_Device sane; double width; SANE_Range x_range; SANE_Word *horz_resolution_list; double height; SANE_Range y_range; SANE_Word *vert_resolution_list; SANE_Range threshold_range; SANE_Range contrast_range; SANE_Range brightness_range; SANE_Word setwindow_cmd_size; SANE_Word calibrate_method; SANE_Word max_read_size; long flags; SANE_Bool support_cap_data_retrieve; SANE_Bool req_shading_calibrate; SANE_Bool req_rgb_line_offset; SANE_Bool req_rgb_char_shift; /* info for 1-pass vs. 3-pass */ SANE_Bool onepass; SANE_Bool support_gamma; SANE_Bool single_gamma; SANE_Int gamma_length; } ARTEC_Device; typedef struct ARTEC_Scanner { /* all the state needed to define a scan request: */ struct ARTEC_Scanner *next; SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; SANE_Int gamma_table[4][4096]; double soft_calibrate_data[3][2592]; SANE_Int halftone_pattern[64]; SANE_Range gamma_range; int gamma_length; int scanning; SANE_Parameters params; size_t bytes_to_read; SANE_Int line_offset; /* scan parameters */ char *mode; SANE_Int x_resolution; SANE_Int y_resolution; SANE_Int tl_x; SANE_Int tl_y; /* info for 1-pass vs. 3-pass */ int this_pass; SANE_Bool onepasscolor; SANE_Bool threepasscolor; int fd; /* SCSI filedescriptor */ /* scanner dependent/low-level state: */ ARTEC_Device *hw; } ARTEC_Scanner; #endif /* artec_h */ backends-1.3.0/backend/artec_eplus48u.c000066400000000000000000003672671456256263500177320ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2002 Michael Herder ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ This backend is based on the "gt68xxtest" program written by the following persons: Sergey Vlasov - Main backend code. Andreas Nowack - Support for GT6801 (Mustek ScanExpress 1200 UB Plus). David Stevenson - Automatic AFE gain and offset setting. ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Please note: The calibration code from the gt68xxtest program isn't used here, since I couldn't get it working. I'm using my own calibration code, which is based on wild assumptions based on the USB logs from the windoze driver. ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ It also contains code from the plustek backend Copyright (C) 2000-2002 Gerhard Jaeger and from the mustek_usb backend Copyright (C) 2000 Mustek. Maintained by Tom Wang Updates (C) 2001 by Henning Meier-Geinitz. ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 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, see . 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. */ #define BUILD 12 #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #define BACKEND_NAME artec_eplus48u #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_config.h" #include "artec_eplus48u.h" #ifndef PATH_MAX # define PATH_MAX 1024 #endif #define _DEFAULT_DEVICE "/dev/usbscanner" #define ARTEC48U_CONFIG_FILE "artec_eplus48u.conf" #define _SHADING_FILE_BLACK "artec48ushading_black" #define _SHADING_FILE_WHITE "artec48ushading_white" #define _EXPOSURE_FILE "artec48uexposure" #define _OFFSET_FILE "artec48uoffset" #define _BYTE 3 #define _STRING 2 #define _FLOAT 1 #define _INT 0 /*for calibration*/ #define WHITE_MIN 243*257 #define WHITE_MAX 253*257 #define BLACK_MIN 8*257 #define BLACK_MAX 18*257 #define EXPOSURE_STEP 280 static Artec48U_Device *first_dev = 0; static Artec48U_Scanner *first_handle = 0; static SANE_Int num_devices = 0; static char devName[PATH_MAX]; static char firmwarePath[PATH_MAX]; static char vendor_string[PATH_MAX]; static char model_string[PATH_MAX]; static SANE_Bool cancelRead; static int isEPro; static int eProMult; static SANE_Auth_Callback auth = NULL; static double gamma_master_default = 1.7; static double gamma_r_default = 1.0; static double gamma_g_default = 1.0; static double gamma_b_default = 1.0; static SANE_Word memory_read_value = 0x200c; /**< Memory read - wValue */ static SANE_Word memory_write_value = 0x200b; /**< Memory write - wValue */ static SANE_Word send_cmd_value = 0x2010; /**< Send normal command - wValue */ static SANE_Word send_cmd_index = 0x3f40; /**< Send normal command - wIndex */ static SANE_Word recv_res_value = 0x2011; /**< Receive normal result - wValue */ static SANE_Word recv_res_index = 0x3f00; /**< Receive normal result - wIndex */ static SANE_Word send_small_cmd_value = 0x2012; /**< Send small command - wValue */ static SANE_Word send_small_cmd_index = 0x3f40; /**< Send small command - wIndex */ static SANE_Word recv_small_res_value = 0x2013; /**< Receive small result - wValue */ static SANE_Word recv_small_res_index = 0x3f00; /**< Receive small result - wIndex */ static SANE_String_Const mode_list[] = { SANE_VALUE_SCAN_MODE_LINEART, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_COLOR, NULL }; static SANE_Word resbit_list[] = { 6, 50, 100, 200, 300, 600, 1200 }; static SANE_Range brightness_contrast_range = { -127, 127, 0 }; static SANE_Range blacklevel_range = { 20, 240, 1 }; static SANE_Range gamma_range = { 0, /* minimum */ SANE_FIX (4.0), /* maximum */ 0 /* quantization */ }; static SANE_Range scan_range_x = { 0, /* minimum */ SANE_FIX (216.0), /* maximum */ 0 /* quantization */ }; static SANE_Range scan_range_y = { 0, /* minimum */ SANE_FIX (297.0), /* maximum */ 0 /* quantization */ }; static SANE_Word bitdepth_list[] = { 2, 8, 16 }; static SANE_Word bitdepth_list2[] = { 1, 8 }; static Artec48U_Exposure_Parameters exp_params; static Artec48U_Exposure_Parameters default_exp_params = { 0x009f, 0x0109, 0x00cb }; static Artec48U_AFE_Parameters afe_params; static Artec48U_AFE_Parameters default_afe_params = { 0x28, 0x0a, 0x2e, 0x03, 0x2e, 0x03 }; static SANE_Status download_firmware_file (Artec48U_Device * chip) { SANE_Status status = SANE_STATUS_GOOD; SANE_Byte *buf = NULL; int size = -1; FILE *f; XDBG ((2, "Try to open firmware file: \"%s\"\n", chip->firmware_path)); f = fopen (chip->firmware_path, "rb"); if (!f) { XDBG ((2, "Cannot open firmware file \"%s\"\n", firmwarePath)); status = SANE_STATUS_INVAL; } if (status == SANE_STATUS_GOOD) { fseek (f, 0, SEEK_END); size = ftell (f); fseek (f, 0, SEEK_SET); if (size == -1) { XDBG ((2, "Error getting size of firmware file \"%s\"\n", chip->firmware_path)); status = SANE_STATUS_INVAL; } } if (status == SANE_STATUS_GOOD) { XDBG ((3, "firmware size: %d\n", size)); buf = (SANE_Byte *) malloc (size); if (!buf) { XDBG ((2, "Cannot allocate %d bytes for firmware\n", size)); status = SANE_STATUS_NO_MEM; } } if (status == SANE_STATUS_GOOD) { int bytes_read = fread (buf, 1, size, f); if (bytes_read != size) { XDBG ((2, "Problem reading firmware file \"%s\"\n", chip->firmware_path)); status = SANE_STATUS_INVAL; } } if (f) fclose (f); if (status == SANE_STATUS_GOOD) { status = artec48u_download_firmware (chip, buf, size); if (status != SANE_STATUS_GOOD) { XDBG ((2, "Firmware download failed\n")); } } if (buf) free (buf); return status; } static SANE_Status init_calibrator (Artec48U_Scanner * s) { XDBG ((2, "Init calibrator size %d\n",30720 * s->dev->epro_mult)); s->shading_buffer_w = (unsigned char *) malloc (30720 * s->dev->epro_mult); /*epro*/ s->shading_buffer_b = (unsigned char *) malloc (30720 * s->dev->epro_mult); /*epro*/ s->shading_buffer_white[0] = (unsigned int *) malloc (5120 * s->dev->epro_mult * sizeof(unsigned int));/*epro*/ s->shading_buffer_black[0] = (unsigned int *) malloc (5120 * s->dev->epro_mult * sizeof (unsigned int));/*epro*/ s->shading_buffer_white[1] = (unsigned int *) malloc (5120 * s->dev->epro_mult * sizeof (unsigned int));/*epro*/ s->shading_buffer_black[1] = (unsigned int *) malloc (5120 * s->dev->epro_mult * sizeof (unsigned int));/*epro*/ s->shading_buffer_white[2] = (unsigned int *) malloc (5120 * s->dev->epro_mult * sizeof (unsigned int));/*epro*/ s->shading_buffer_black[2] = (unsigned int *) malloc (5120 * s->dev->epro_mult * sizeof (unsigned int));/*epro*/ if (!s->shading_buffer_w || !s->shading_buffer_b || !s->shading_buffer_white[0] || !s->shading_buffer_black[0] || !s->shading_buffer_white[1] || !s->shading_buffer_black[1] || !s->shading_buffer_white[2] || !s->shading_buffer_black[2]) { if (s->shading_buffer_w) free (s->shading_buffer_w); if (s->shading_buffer_b) free (s->shading_buffer_b); if (s->shading_buffer_white[0]) free (s->shading_buffer_white[0]); if (s->shading_buffer_black[0]) free (s->shading_buffer_black[0]); if (s->shading_buffer_white[1]) free (s->shading_buffer_white[1]); if (s->shading_buffer_black[1]) free (s->shading_buffer_black[1]); if (s->shading_buffer_white[2]) free (s->shading_buffer_white[2]); if (s->shading_buffer_black[2]) free (s->shading_buffer_black[2]); return SANE_STATUS_NO_MEM; } return SANE_STATUS_GOOD; } static void init_shading_buffer (Artec48U_Scanner * s) { unsigned int i, j; for (i = 0; i < 5120 * s->dev->epro_mult; i++) /*epro*/ { for (j = 0; j < 3; j++) { s->temp_shading_buffer[j][i] = 0; } } } static void add_to_shading_buffer (Artec48U_Scanner * s, unsigned int **buffer_pointers) { unsigned int i, j; for (i = 0; i < 5120 * s->dev->epro_mult; i++) /*epro*/ { for (j = 0; j < 3; j++) { s->temp_shading_buffer[j][i] += buffer_pointers[j][i]; } } } static void finish_shading_buffer (Artec48U_Scanner * s, SANE_Bool white) { unsigned int i, j, cnt, c, div; unsigned char *shading_buffer; cnt = 0; if (white) { shading_buffer = s->shading_buffer_w; div = s->dev->shading_lines_w; } else { shading_buffer = s->shading_buffer_b; div = s->dev->shading_lines_b; } for (i = 0; i < 5120 * s->dev->epro_mult; i++) /*epro*/ { for (j = 0; j < 3; j++) { int value = s->temp_shading_buffer[j][i] / (div); shading_buffer[cnt] = (SANE_Byte) (value & 0xff); ++cnt; shading_buffer[cnt] = (SANE_Byte) ((value >> 8) & 0xff); ++cnt; } } for (c = 0; c < (30720 * s->dev->epro_mult) - 5; c += 6) /*epro*/ { i = (int) shading_buffer[c] + ((int) shading_buffer[c + 1] << 8); i = (int) shading_buffer[c + 2] + ((int) shading_buffer[c + 3] << 8); i = (int) shading_buffer[c + 4] + ((int) shading_buffer[c + 5] << 8); } } static void finish_exposure_buffer (Artec48U_Scanner * s, int *avg_r, int *avg_g, int *avg_b) { unsigned int i, j, cnt, c, div; unsigned int max_r; unsigned int max_g; unsigned int max_b; unsigned char *shading_buffer; cnt = 0; shading_buffer = s->shading_buffer_w; div = s->dev->shading_lines_w; for (i = 0; i < 5120 * s->dev->epro_mult; i++) /*epro*/ { for (j = 0; j < 3; j++) { int value = s->temp_shading_buffer[j][i] / (div); shading_buffer[cnt] = (SANE_Byte) (value & 0xff); ++cnt; shading_buffer[cnt] = (SANE_Byte) ((value >> 8) & 0xff); ++cnt; } } max_r = 0; max_g = 0; max_b = 0; for (c = 0; c < (30720 * s->dev->epro_mult) - 5; c += 6) /*epro*/ { i = (int) shading_buffer[c] + ((int) shading_buffer[c + 1] << 8); if (i > max_r) max_r = i; i = (int) shading_buffer[c + 2] + ((int) shading_buffer[c + 3] << 8); if (i > max_g) max_g = i; i = (int) shading_buffer[c + 4] + ((int) shading_buffer[c + 5] << 8); if (i > max_b) max_b = i; } *avg_r = max_r; *avg_g = max_g; *avg_b = max_b; } static void finish_offset_buffer (Artec48U_Scanner * s, int *avg_r, int *avg_g, int *avg_b) { unsigned int i, j, cnt, c, div; unsigned int min_r; unsigned int min_g; unsigned int min_b; unsigned char *shading_buffer; cnt = 0; shading_buffer = s->shading_buffer_b; div = s->dev->shading_lines_b; for (i = 0; i < 5120 * s->dev->epro_mult; i++) /*epro*/ { for (j = 0; j < 3; j++) { int value = s->temp_shading_buffer[j][i] / (div); shading_buffer[cnt] = (SANE_Byte) (value & 0xff); ++cnt; shading_buffer[cnt] = (SANE_Byte) ((value >> 8) & 0xff); ++cnt; } } min_r = 65535; min_g = 65535; min_b = 65535; for (c = 0; c < (30720 * s->dev->epro_mult) - 5; c += 6) /*epro*/ { i = (int) shading_buffer[c] + ((int) shading_buffer[c + 1] << 8); if (i < min_r) min_r = i; i = (int) shading_buffer[c + 2] + ((int) shading_buffer[c + 3] << 8); if (i < min_g) min_g = i; i = (int) shading_buffer[c + 4] + ((int) shading_buffer[c + 5] << 8); if (i < min_b) min_b = i; } *avg_r = min_r; *avg_g = min_g; *avg_b = min_b; } static SANE_Status artec48u_wait_for_positioning (Artec48U_Device * chip) { SANE_Status status; SANE_Bool moving; while (SANE_TRUE) { status = artec48u_is_moving (chip, &moving); if (status != SANE_STATUS_GOOD) return status; if (!moving) break; usleep (100000); } return SANE_STATUS_GOOD; } static void copy_scan_line (Artec48U_Scanner * s) { /*For resolution of 1200 dpi we have to interpolate horizontally, because the optical horizontal resolution is limited to 600 dpi. We simply use the average value of two pixels. */ int cnt, i, j; int xs = s->params.pixel_xs; int interpolate = 0; int value; int value1; int value2; if ((s->reader->params.ydpi == 1200) && (s->dev->is_epro == 0)) /*epro*/ interpolate = 1; cnt = 0; if (s->params.color) { if (s->params.depth > 8) { for (i = xs - 1; i >= 0; i--) { for (j = 0; j < 3; j++) { value = s->buffer_pointers[j][i]; s->line_buffer[cnt] = LOBYTE (value); ++cnt; s->line_buffer[cnt] = HIBYTE (value); ++cnt; } if (interpolate == 1) /*1200 dpi */ cnt += 6; } if (interpolate == 1) { for (i = 0; i < (xs * 12) - 12; i += 12) { value1 = (int) s->line_buffer[i]; value1 += (int) (s->line_buffer[i + 1] << 8); value2 = (int) s->line_buffer[i + 12]; value2 += (int) (s->line_buffer[i + 13] << 8); value = (value1 + value2) / 2; if (value < 0) value = 0; if (value > 65535) value = 65535; s->line_buffer[i + 6] = LOBYTE (value); s->line_buffer[i + 7] = HIBYTE (value); value1 = (int) s->line_buffer[i + 2]; value1 += (int) (s->line_buffer[i + 3] << 8); value2 = (int) s->line_buffer[i + 14]; value2 += (int) (s->line_buffer[i + 15] << 8); value = (value1 + value2) / 2; if (value < 0) value = 0; if (value > 65535) value = 65535; s->line_buffer[i + 8] = LOBYTE (value); s->line_buffer[i + 9] = HIBYTE (value); value1 = (int) s->line_buffer[i + 4]; value1 += (int) (s->line_buffer[i + 5] << 8); value2 = (int) s->line_buffer[i + 16]; value2 += (int) (s->line_buffer[i + 17] << 8); value = (value1 + value2) / 2; if (value < 0) value = 0; if (value > 65535) value = 65535; s->line_buffer[i + 10] = LOBYTE (value); s->line_buffer[i + 11] = HIBYTE (value); } } } else { for (i = xs - 1; i >= 0; i--) { for (j = 0; j < 3; j++) { value = s->buffer_pointers[j][i]; s->line_buffer[cnt] = (SANE_Byte) (value / 257); cnt += 1; } if (interpolate == 1) /*1200 dpi */ cnt += 3; } if (interpolate == 1) { for (i = 0; i < (xs * 6) - 6; i += 6) { value1 = (int) s->line_buffer[i]; value2 = (int) s->line_buffer[i + 6]; value = (value1 + value2) / 2; if (value < 0) value = 0; if (value > 255) value = 255; s->line_buffer[i + 3] = (SANE_Byte) (value); value1 = (int) s->line_buffer[i + 1]; value2 = (int) s->line_buffer[i + 7]; value = (value1 + value2) / 2; if (value < 0) value = 0; if (value > 255) value = 255; s->line_buffer[i + 4] = (SANE_Byte) (value); value1 = (int) s->line_buffer[i + 2]; value2 = (int) s->line_buffer[i + 8]; value = (value1 + value2) / 2; if (value < 0) value = 0; if (value > 255) value = 255; s->line_buffer[i + 5] = (SANE_Byte) (value); } } } } else { if (s->params.depth > 8) { for (i = xs - 1; i >= 0; --i) { value = s->buffer_pointers[0][i]; s->line_buffer[cnt] = LOBYTE (value); ++cnt; s->line_buffer[cnt] = HIBYTE (value); ++cnt; if (interpolate == 1) /*1200 dpi */ cnt += 2; } if (interpolate == 1) { for (i = 0; i < (xs * 4) - 4; i += 4) { value1 = (int) s->line_buffer[i]; value1 += (int) (s->line_buffer[i + 1] << 8); value2 = (int) s->line_buffer[i + 4]; value2 += (int) (s->line_buffer[i + 5] << 8); value = (value1 + value2) / 2; if (value < 0) value = 0; if (value > 65535) value = 65535; s->line_buffer[i + 2] = LOBYTE (value); s->line_buffer[i + 3] = HIBYTE (value); } } } else { if (s->params.lineart == SANE_FALSE) { for (i = xs - 1; i >= 0; --i) { value = s->buffer_pointers[0][i]; s->line_buffer[cnt] = (SANE_Byte) (value / 257); ++cnt; if (interpolate == 1) /*1200 dpi */ ++cnt; } if (interpolate == 1) { for (i = 0; i < (xs * 2) - 2; i += 2) { value1 = (int) s->line_buffer[i]; value2 = (int) s->line_buffer[i + 2]; value = (value1 + value2) / 2; if (value < 0) value = 0; if (value > 255) value = 255; s->line_buffer[i + 1] = (SANE_Byte) (value); } } } else { int cnt2; int bit_cnt = 0; int black_level = s->val[OPT_BLACK_LEVEL].w; /*copy to lineart_buffer */ for (i = xs - 1; i >= 0; --i) { s->lineart_buffer[cnt] = (SANE_Byte) (s->buffer_pointers[0][i] / 257); ++cnt; if (interpolate == 1) /*1200 dpi */ ++cnt; } cnt2 = cnt - 1; cnt = 0; if (interpolate == 1) { for (i = 0; i < cnt2 - 2; i += 2) { value1 = (int) s->lineart_buffer[i]; value2 = (int) s->lineart_buffer[i + 2]; value = (value1 + value2) / 2; if (value < 0) value = 0; if (value > 255) value = 255; s->lineart_buffer[i + 1] = (SANE_Byte) (value); } } /* in this case, every value in buffer_pointers represents a bit */ for (i = 0; i < cnt2; i++) { SANE_Byte temp; if (bit_cnt == 0) s->line_buffer[cnt] = 0; /*clear */ temp = s->lineart_buffer[i]; if (temp <= black_level) s->line_buffer[cnt] |= 1 << (7 - bit_cnt); ++bit_cnt; if (bit_cnt > 7) { bit_cnt = 0; ++cnt; } } } } } } /*............................................................................. * attach a device to the backend */ static SANE_Status attach (const char *dev_name, Artec48U_Device ** devp) { SANE_Status status; Artec48U_Device *dev; XDBG ((1, "attach (%s, %p)\n", dev_name, (void *) devp)); if (!dev_name) { XDBG ((1, "attach: devname == NULL\n")); return SANE_STATUS_INVAL; } /* already attached ? */ for (dev = first_dev; dev; dev = dev->next) { if (0 == strcmp (dev->name, dev_name)) { if (devp) *devp = dev; XDBG ((3, "attach: device %s already attached\n", dev_name)); return SANE_STATUS_GOOD; } } XDBG ((3, "attach: device %s NOT attached\n", dev_name)); /* allocate some memory for the device */ artec48u_device_new (&dev); if (NULL == dev) return SANE_STATUS_NO_MEM; dev->fd = -1; dev->name = strdup (dev_name); dev->sane.name = strdup (dev_name); /* * go ahead and open the scanner device */ status = artec48u_device_open (dev); if (status != SANE_STATUS_GOOD) { XDBG ((3, "Could not open device!!\n")); artec48u_device_free (dev); return status; } /*limit the size of vendor and model string to 40 */ vendor_string[40] = 0; model_string[40] = 0; /* assign all the stuff we need for this device... */ dev->sane.vendor = strdup (vendor_string); XDBG ((3, "attach: setting vendor string: %s\n", vendor_string)); dev->sane.model = strdup (model_string); XDBG ((3, "attach: setting model string: %s\n", model_string)); dev->sane.type = "flatbed scanner"; dev->firmware_path = strdup (firmwarePath); dev->epro_mult = eProMult; dev->is_epro = isEPro; XDBG ((1, "attach eProMult %d\n", eProMult)); XDBG ((1, "attach isEPro %d\n", isEPro)); dev->optical_xdpi = 600 * dev->epro_mult; /*epro*/ dev->optical_ydpi = 1200 * dev->epro_mult; /*epro*/ dev->base_ydpi = 600 * dev->epro_mult; /*epro*/ dev->xdpi_offset = 0; /* in optical_xdpi units */ dev->ydpi_offset = 280 * dev->epro_mult; /* in optical_ydpi units */ dev->x_size = 5120 * dev->epro_mult; /*epro*/ /* in optical_xdpi units */ dev->y_size = 14100 * dev->epro_mult; /*epro*/ /* in optical_ydpi units */ dev->shading_offset = 10 * dev->epro_mult; dev->shading_lines_b = 70 * dev->epro_mult; dev->shading_lines_w = 70 * dev->epro_mult; dev->gamma_master = gamma_master_default; dev->gamma_r = gamma_r_default; dev->gamma_g = gamma_g_default; dev->gamma_b = gamma_b_default; dev->afe_params.r_offset = afe_params.r_offset; dev->afe_params.g_offset = afe_params.g_offset; dev->afe_params.b_offset = afe_params.b_offset; dev->afe_params.r_pga = default_afe_params.r_pga; dev->afe_params.g_pga = default_afe_params.g_pga; dev->afe_params.b_pga = default_afe_params.b_pga; dev->exp_params.r_time = exp_params.r_time; dev->exp_params.g_time = exp_params.g_time; dev->exp_params.b_time = exp_params.b_time; ++num_devices; dev->next = first_dev; first_dev = dev; if (devp) *devp = first_dev; status = artec48u_device_close (dev); return SANE_STATUS_GOOD; } static SANE_Status attach_one_device (SANE_String_Const devname) { Artec48U_Device *dev; SANE_Status status; status = attach (devname, &dev); if (SANE_STATUS_GOOD != status) return status; return SANE_STATUS_GOOD; } /** * function to decode an value and give it back to the caller. * @param src - pointer to the source string to check * @param opt - string that keeps the option name to check src for * @param what - _FLOAT or _INT * @param result - pointer to the var that should receive our result * @param def - default value that result should be in case of any error * @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) { char *tmp, *tmp2; const char *name; /* skip the option string */ name = (const char *) &src[strlen ("option")]; /* get the name of the option */ name = sanei_config_get_string (name, &tmp); if (tmp) { /* on success, compare with the given one */ if (0 == strcmp (tmp, opt)) { XDBG ((1, "Decoding option >%s<\n", opt)); if (_INT == what) { /* assign the default value for this option... */ *((int *) result) = *((int *) def); if (*name) { /* get the configuration value and decode it */ name = sanei_config_get_string (name, &tmp2); if (tmp2) { *((int *) result) = strtol (tmp2, 0, 0); free (tmp2); } } free (tmp); return SANE_TRUE; } else if (_FLOAT == what) { /* assign the default value for this option... */ *((double *) result) = *((double *) def); if (*name) { /* get the configuration value and decode it */ name = sanei_config_get_string (name, &tmp2); if (tmp2) { *((double *) result) = strtod (tmp2, 0); free (tmp2); } } free (tmp); return SANE_TRUE; } else if (_BYTE == what) { /* assign the default value for this option... */ *((SANE_Byte *) result) = *((SANE_Byte *) def); if (*name) { /* get the configuration value and decode it */ name = sanei_config_get_string (name, &tmp2); if (tmp2) { *((SANE_Byte *) result) = (SANE_Byte) strtol (tmp2, 0, 0); free (tmp2); } } free (tmp); return SANE_TRUE; } else if (_STRING == what) { if (*name) { /* get the configuration value and decode it */ sanei_config_get_string (name, &tmp2); if (tmp2) { strcpy ((char *) result, (char *) tmp2); free (tmp2); } } free (tmp); return SANE_TRUE; } } free (tmp); } return SANE_FALSE; } /** * function to retrieve the device name of a given string * @param src - string that keeps the option name to check src for * @param dest - pointer to the string, that should receive the detected * devicename * @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) { char *tmp; const char *name; if (0 == strncmp ("device", src, 6)) { name = (const char *) &src[strlen ("device")]; name = sanei_config_skip_whitespace (name); XDBG ((1, "Decoding device name >%s<\n", name)); if (*name) { name = sanei_config_get_string (name, &tmp); if (tmp) { strcpy (dest, tmp); free (tmp); return SANE_TRUE; } } } return SANE_FALSE; } #ifdef ARTEC48U_USE_BUTTONS static SANE_Status artec48u_check_buttons (Artec48U_Device * dev, SANE_Int * value) { SANE_Status status; Artec48U_Packet req; memset (req, 0, sizeof (req)); req[0] = 0x74; req[1] = 0x01; status = artec48u_device_small_req (dev, req, req); if (status != SANE_STATUS_GOOD) return status; *value = (SANE_Int) req[2]; return SANE_STATUS_GOOD; } #endif #define MAX_DOWNLOAD_BLOCK_SIZE 64 static SANE_Status artec48u_generic_start_scan (Artec48U_Device * dev) { Artec48U_Packet req; memset (req, 0, sizeof (req)); req[0] = 0x43; req[1] = 0x01; return artec48u_device_req (dev, req, req); } static SANE_Status artec48u_generic_read_scanned_data (Artec48U_Device * dev, SANE_Bool * ready) { SANE_Status status; Artec48U_Packet req; memset (req, 0, sizeof (req)); req[0] = 0x35; req[1] = 0x01; status = artec48u_device_req (dev, req, req); if (status != SANE_STATUS_GOOD) return status; if (req[1] == 0x35) { if (req[0] == 0) *ready = SANE_TRUE; else *ready = SANE_FALSE; } else return SANE_STATUS_IO_ERROR; return SANE_STATUS_GOOD; } static SANE_Status artec48u_download_firmware (Artec48U_Device * dev, SANE_Byte * data, SANE_Word size) { SANE_Status status; SANE_Byte download_buf[MAX_DOWNLOAD_BLOCK_SIZE]; SANE_Byte check_buf[MAX_DOWNLOAD_BLOCK_SIZE]; SANE_Byte *block; SANE_Word addr, bytes_left; Artec48U_Packet boot_req; SANE_Word block_size = MAX_DOWNLOAD_BLOCK_SIZE; CHECK_DEV_ACTIVE ((Artec48U_Device *) dev, (char *) "artec48u_device_download_firmware"); for (addr = 0; addr < size; addr += block_size) { bytes_left = size - addr; if (bytes_left > block_size) block = data + addr; else { memset (download_buf, 0, block_size); memcpy (download_buf, data + addr, bytes_left); block = download_buf; } status = artec48u_device_memory_write (dev, addr, block_size, block); if (status != SANE_STATUS_GOOD) return status; status = artec48u_device_memory_read (dev, addr, block_size, check_buf); if (status != SANE_STATUS_GOOD) return status; if (memcmp (block, check_buf, block_size) != 0) { XDBG ((3, "artec48u_device_download_firmware: mismatch at block 0x%0x\n", addr)); return SANE_STATUS_IO_ERROR; } } memset (boot_req, 0, sizeof (boot_req)); boot_req[0] = 0x69; boot_req[1] = 0x01; boot_req[2] = LOBYTE (addr); boot_req[3] = HIBYTE (addr); status = artec48u_device_req (dev, boot_req, boot_req); if (status != SANE_STATUS_GOOD) return status; return SANE_STATUS_GOOD; } static SANE_Status artec48u_is_moving (Artec48U_Device * dev, SANE_Bool * moving) { SANE_Status status; Artec48U_Packet req; memset (req, 0, sizeof (req)); req[0] = 0x17; req[1] = 0x01; status = artec48u_device_req (dev, req, req); if (status != SANE_STATUS_GOOD) return status; if (req[0] == 0x00 && req[1] == 0x17) { if (req[2] == 0 && (req[3] == 0 || req[3] == 2)) *moving = SANE_FALSE; else *moving = SANE_TRUE; } else return SANE_STATUS_IO_ERROR; return SANE_STATUS_GOOD; } static SANE_Status artec48u_carriage_home (Artec48U_Device * dev) { Artec48U_Packet req; memset (req, 0, sizeof (req)); req[0] = 0x24; req[1] = 0x01; return artec48u_device_req (dev, req, req); } static SANE_Status artec48u_stop_scan (Artec48U_Device * dev) { Artec48U_Packet req; memset (req, 0, sizeof (req)); req[0] = 0x41; req[1] = 0x01; return artec48u_device_small_req (dev, req, req); } static SANE_Status artec48u_setup_scan (Artec48U_Scanner * s, Artec48U_Scan_Request * request, Artec48U_Scan_Action action, SANE_Bool calculate_only, Artec48U_Scan_Parameters * params) { DECLARE_FUNCTION_NAME ("artec48u_setup_scan") SANE_Status status; SANE_Int xdpi, ydpi; SANE_Bool color; SANE_Int depth; SANE_Int pixel_x0, pixel_y0, pixel_xs, pixel_ys; SANE_Int pixel_align; SANE_Int abs_x0, abs_y0, abs_xs, abs_ys, base_xdpi, base_ydpi; SANE_Int scan_xs, scan_ys, scan_bpl; SANE_Int bits_per_line; SANE_Byte color_mode_code; /*If we scan a black line, we use these exposure values */ Artec48U_Exposure_Parameters exp_params_black = { 4, 4, 4 }; XDBG ((6, "%s: enter\n", function_name)); XDBG ((1,"setup scan is_epro %d\n",s->dev->is_epro)); XDBG ((1,"setup scan epro_mult %d\n",s->dev->epro_mult)); xdpi = request->xdpi; ydpi = request->ydpi; color = request->color; depth = request->depth; switch (action) { case SA_CALIBRATE_SCAN_WHITE: { /*move a bit inside scan mark - the value for the offset was found by trial and error */ pixel_y0 = s->dev->shading_offset; pixel_ys = s->dev->shading_lines_w; pixel_x0 = 0; pixel_xs = 5120 * s->dev->epro_mult; /*epro*/ xdpi = ydpi = 600 * s->dev->epro_mult; /*epro*/ color = SANE_TRUE; depth = 8; break; } case SA_CALIBRATE_SCAN_OFFSET_1: case SA_CALIBRATE_SCAN_OFFSET_2: { pixel_y0 = s->dev->shading_offset; pixel_ys = s->dev->shading_lines_b; pixel_x0 = 0; pixel_xs = 5120 * s->dev->epro_mult; /*epro*/ xdpi = ydpi = 600 * s->dev->epro_mult; /*epro*/ color = SANE_TRUE; depth = 8; break; } case SA_CALIBRATE_SCAN_EXPOSURE_1: case SA_CALIBRATE_SCAN_EXPOSURE_2: { pixel_y0 = s->dev->shading_offset; pixel_ys = s->dev->shading_lines_w; pixel_x0 = 0; pixel_xs = 5120 * s->dev->epro_mult; /*epro*/ xdpi = ydpi = 600 * s->dev->epro_mult; /*epro*/ color = SANE_TRUE; depth = 8; break; } case SA_CALIBRATE_SCAN_BLACK: { pixel_y0 = s->dev->shading_offset; pixel_ys = s->dev->shading_lines_w; pixel_x0 = 0; pixel_xs = 5120 * s->dev->epro_mult; /*epro*/ xdpi = ydpi = 600 * s->dev->epro_mult; /*epro*/ color = SANE_TRUE; depth = 8; break; } case SA_SCAN: { SANE_Fixed x0 = request->x0 + s->dev->xdpi_offset; SANE_Fixed y0; /*epro*/ if ((ydpi == 1200) && (s->dev->is_epro == 0)) xdpi = 600; y0 = request->y0 + s->dev->ydpi_offset; pixel_ys = SANE_UNFIX (request->ys) * ydpi / MM_PER_INCH + 0.5; pixel_x0 = SANE_UNFIX (x0) * xdpi / MM_PER_INCH + 0.5; pixel_y0 = SANE_UNFIX (y0) * ydpi / MM_PER_INCH + 0.5; pixel_xs = SANE_UNFIX (request->xs) * xdpi / MM_PER_INCH + 0.5; break; } default: XDBG ((6, "%s: invalid action=%d\n", function_name, (int) action)); return SANE_STATUS_INVAL; } XDBG ((6, "%s: xdpi=%d, ydpi=%d\n", function_name, xdpi, ydpi)); XDBG ((6, "%s: color=%s, depth=%d\n", function_name, color ? "TRUE" : "FALSE", depth)); XDBG ((6, "%s: pixel_x0=%d, pixel_y0=%d\n", function_name, pixel_x0, pixel_y0)); XDBG ((6, "%s: pixel_xs=%d, pixel_ys=%d\n", function_name, pixel_xs, pixel_ys)); switch (depth) { case 8: color_mode_code = color ? 0x84 : 0x82; break; case 16: color_mode_code = color ? 0xa4 : 0xa2; break; default: XDBG ((6, "%s: unsupported depth=%d\n", function_name, depth)); return SANE_STATUS_UNSUPPORTED; } base_xdpi = s->dev->optical_xdpi; base_ydpi = s->dev->base_ydpi; XDBG ((6, "%s: base_xdpi=%d, base_ydpi=%d\n", function_name, base_xdpi, base_ydpi)); abs_x0 = pixel_x0 * base_xdpi / xdpi; abs_y0 = pixel_y0 * base_ydpi / ydpi; /* Calculate minimum number of pixels which span an integral multiple of 64 * bytes. */ pixel_align = 32; /* best case for depth = 16 */ while ((depth * pixel_align) % (64 * 8) != 0) pixel_align *= 2; XDBG ((6, "%s: pixel_align=%d\n", function_name, pixel_align)); if (pixel_xs % pixel_align == 0) scan_xs = pixel_xs; else scan_xs = (pixel_xs / pixel_align + 1) * pixel_align; scan_ys = pixel_ys; XDBG ((6, "%s: scan_xs=%d, scan_ys=%d\n", function_name, scan_xs, scan_ys)); abs_xs = scan_xs * base_xdpi / xdpi; abs_ys = scan_ys * base_ydpi / ydpi; XDBG ((6, "%s: abs_xs=%d, abs_ys=%d\n", function_name, abs_xs, abs_ys)); bits_per_line = depth * scan_xs; if (bits_per_line % 8) /* impossible */ { XDBG ((1, "%s: BUG: unaligned bits_per_line=%d\n", function_name, bits_per_line)); return SANE_STATUS_INVAL; } scan_bpl = bits_per_line / 8; if (scan_bpl % 64) /* impossible */ { XDBG ((1, "%s: BUG: unaligned scan_bpl=%d\n", function_name, scan_bpl)); return SANE_STATUS_INVAL; } if (scan_bpl > 15600) { XDBG ((6, "%s: scan_bpl=%d, too large\n", function_name, scan_bpl)); return SANE_STATUS_INVAL; } XDBG ((6, "%s: scan_bpl=%d\n", function_name, scan_bpl)); if (!calculate_only) { Artec48U_Packet req; char motor_mode_1, motor_mode_2; switch (action) { case SA_CALIBRATE_SCAN_WHITE: motor_mode_1 = 0x01; motor_mode_2 = 0x00; break; case SA_CALIBRATE_SCAN_BLACK: motor_mode_1 = 0x04; motor_mode_2 = 0x00; break; case SA_SCAN: motor_mode_1 = 0x01; motor_mode_2 = 0x00; break; default: XDBG ((6, "%s: invalid action=%d\n", function_name, (int) action)); return SANE_STATUS_INVAL; } /* Fill in the setup command */ memset (req, 0, sizeof (req)); req[0x00] = 0x20; req[0x01] = 0x01; req[0x02] = LOBYTE (abs_y0); req[0x03] = HIBYTE (abs_y0); req[0x04] = LOBYTE (abs_ys); req[0x05] = HIBYTE (abs_ys); req[0x06] = LOBYTE (abs_x0); req[0x07] = HIBYTE (abs_x0); req[0x08] = LOBYTE (abs_xs); req[0x09] = HIBYTE (abs_xs); req[0x0a] = color_mode_code; req[0x0b] = 0x60; req[0x0c] = LOBYTE (xdpi); req[0x0d] = HIBYTE (xdpi); req[0x0e] = 0x12; req[0x0f] = 0x00; req[0x10] = LOBYTE (scan_bpl); req[0x11] = HIBYTE (scan_bpl); req[0x12] = LOBYTE (scan_ys); req[0x13] = HIBYTE (scan_ys); req[0x14] = motor_mode_1; req[0x15] = motor_mode_2; req[0x16] = LOBYTE (ydpi); req[0x17] = HIBYTE (ydpi); req[0x18] = 0x00; status = artec48u_device_req (s->dev, req, req); if (status != SANE_STATUS_GOOD) { XDBG ((3, "%s: setup request failed: %s\n", function_name, sane_strstatus (status))); return status; } if (action == SA_SCAN) { artec48u_calculate_shading_buffer (s, pixel_x0, pixel_xs + pixel_x0, xdpi, color); artec48u_generic_set_exposure_time (s->dev, &(s->dev-> artec_48u_exposure_params)); artec48u_generic_set_afe (s->dev, &(s->dev->artec_48u_afe_params)); } else if (action == SA_CALIBRATE_SCAN_BLACK) { artec48u_generic_set_exposure_time (s->dev, &exp_params_black); artec48u_generic_set_afe (s->dev, &(s->dev->afe_params)); } else if (action == SA_CALIBRATE_SCAN_WHITE) { artec48u_generic_set_exposure_time (s->dev, &(s->dev->exp_params)); artec48u_generic_set_afe (s->dev, &(s->dev->afe_params)); } } /* Fill in calculated values */ params->xdpi = xdpi; params->ydpi = ydpi; params->depth = depth; params->color = color; params->pixel_xs = pixel_xs; params->pixel_ys = pixel_ys; params->scan_xs = scan_xs; params->scan_ys = scan_ys; params->scan_bpl = scan_bpl; XDBG ((6, "%s: leave: ok\n", function_name)); return SANE_STATUS_GOOD; } static SANE_Status artec48u_generic_set_afe (Artec48U_Device * dev, Artec48U_AFE_Parameters * params) { Artec48U_Packet req; memset (req, 0, sizeof (req)); req[0] = 0x22; req[1] = 0x01; req[2] = params->r_offset; req[3] = params->r_pga; req[4] = params->g_offset; req[5] = params->g_pga; req[6] = params->b_offset; req[7] = params->b_pga; return artec48u_device_req (dev, req, req); } static SANE_Status artec48u_generic_set_exposure_time (Artec48U_Device * dev, Artec48U_Exposure_Parameters * params) { Artec48U_Packet req; memset (req, 0, sizeof (req)); req[0] = 0x76; req[1] = 0x01; req[2] = req[6] = req[10] = 0x04; req[4] = LOBYTE (params->r_time); req[5] = HIBYTE (params->r_time); req[8] = LOBYTE (params->g_time); req[9] = HIBYTE (params->g_time); req[12] = LOBYTE (params->b_time); req[13] = HIBYTE (params->b_time); return artec48u_device_req (dev, req, req); } static SANE_Status artec48u_device_new (Artec48U_Device ** dev_return) { DECLARE_FUNCTION_NAME ("artec48u_device_new") Artec48U_Device *dev; XDBG ((7, "%s: enter\n", function_name)); if (!dev_return) return SANE_STATUS_INVAL; dev = (Artec48U_Device *) malloc (sizeof (Artec48U_Device)); if (!dev) { XDBG ((3, "%s: couldn't malloc %lu bytes for device\n", function_name, (u_long) sizeof (Artec48U_Device))); *dev_return = 0; return SANE_STATUS_NO_MEM; } *dev_return = dev; memset (dev, 0, sizeof (Artec48U_Device)); dev->fd = -1; dev->active = SANE_FALSE; dev->read_buffer = NULL; dev->requested_buffer_size = 32768; XDBG ((7, "%s: leave: ok\n", function_name)); return SANE_STATUS_GOOD; } static SANE_Status artec48u_device_free (Artec48U_Device * dev) { DECLARE_FUNCTION_NAME ("artec48u_device_free") XDBG ((7, "%s: enter: dev=%p\n", function_name, (void *) dev)); if (dev) { if (dev->active) artec48u_device_deactivate (dev); if (dev->fd != -1) artec48u_device_close (dev); XDBG ((7, "%s: freeing dev\n", function_name)); free (dev); } XDBG ((7, "%s: leave: ok\n", function_name)); return SANE_STATUS_GOOD; } static SANE_Status artec48u_device_open (Artec48U_Device * dev) { DECLARE_FUNCTION_NAME ("artec48u_device_open") SANE_Status status; SANE_Int fd; XDBG ((7, "%s: enter: dev=%p\n", function_name, (void *) dev)); CHECK_DEV_NOT_NULL (dev, function_name); if (dev->fd != -1) { XDBG ((3, "%s: device already open\n", function_name)); return SANE_STATUS_INVAL; } status = sanei_usb_open (dev->sane.name, &fd); if (status != SANE_STATUS_GOOD) { XDBG ((3, "%s: sanei_usb_open failed: %s\n", function_name, sane_strstatus (status))); return status; } dev->fd = fd; XDBG ((7, "%s: leave: ok\n", function_name)); return SANE_STATUS_GOOD; } static SANE_Status artec48u_device_close (Artec48U_Device * dev) { DECLARE_FUNCTION_NAME ("artec48u_device_close") XDBG ((7, "%s: enter: dev=%p\n", function_name, (void *) dev)); CHECK_DEV_OPEN (dev, function_name); if (dev->active) artec48u_device_deactivate (dev); sanei_usb_close (dev->fd); dev->fd = -1; XDBG ((7, "%s: leave: ok\n", function_name)); return SANE_STATUS_GOOD; } static SANE_Status artec48u_device_activate (Artec48U_Device * dev) { DECLARE_FUNCTION_NAME ("artec48u_device_activate") CHECK_DEV_OPEN (dev, function_name); if (dev->active) { XDBG ((3, "%s: device already active\n", function_name)); return SANE_STATUS_INVAL; } XDBG ((7, "%s: model \"%s\"\n", function_name, dev->sane.model)); dev->xdpi_offset = SANE_FIX (dev->xdpi_offset * MM_PER_INCH / dev->optical_xdpi); dev->ydpi_offset = SANE_FIX (dev->ydpi_offset * MM_PER_INCH / dev->optical_ydpi); dev->active = SANE_TRUE; return SANE_STATUS_GOOD; } static SANE_Status artec48u_device_deactivate (Artec48U_Device * dev) { DECLARE_FUNCTION_NAME ("artec48u_device_deactivate") SANE_Status status = SANE_STATUS_GOOD; CHECK_DEV_ACTIVE (dev, function_name); if (dev->read_active) artec48u_device_read_finish (dev); dev->active = SANE_FALSE; return status; } static SANE_Status artec48u_device_memory_write (Artec48U_Device * dev, SANE_Word addr, SANE_Word size, SANE_Byte * data) { DECLARE_FUNCTION_NAME ("artec48u_device_memory_write") SANE_Status status; XDBG ((8, "%s: dev=%p, addr=0x%x, size=0x%x, data=%p\n", function_name, (void *) dev, addr, size, (void *) data)); CHECK_DEV_ACTIVE (dev, function_name); status = sanei_usb_control_msg (dev->fd, 0x40, 0x01, memory_write_value, addr, size, data); if (status != SANE_STATUS_GOOD) { XDBG ((3, "%s: sanei_usb_control_msg failed: %s\n", function_name, sane_strstatus (status))); } return status; } static SANE_Status artec48u_device_memory_read (Artec48U_Device * dev, SANE_Word addr, SANE_Word size, SANE_Byte * data) { DECLARE_FUNCTION_NAME ("artec48u_device_memory_read") SANE_Status status; XDBG ((8, "%s: dev=%p, addr=0x%x, size=0x%x, data=%p\n", function_name, (void *) dev, addr, size, (void *) data)); CHECK_DEV_ACTIVE (dev, function_name); status = sanei_usb_control_msg (dev->fd, 0xc0, 0x01, memory_read_value, addr, size, data); if (status != SANE_STATUS_GOOD) { XDBG ((3, "%s: sanei_usb_control_msg failed: %s\n", function_name, sane_strstatus (status))); } return status; } static SANE_Status artec48u_device_generic_req (Artec48U_Device * dev, SANE_Word cmd_value, SANE_Word cmd_index, SANE_Word res_value, SANE_Word res_index, Artec48U_Packet cmd, Artec48U_Packet res) { DECLARE_FUNCTION_NAME ("artec48u_device_generic_req") SANE_Status status; XDBG ((7, "%s: command=0x%02x\n", function_name, cmd[0])); CHECK_DEV_ACTIVE (dev, function_name); status = sanei_usb_control_msg (dev->fd, 0x40, 0x01, cmd_value, cmd_index, ARTEC48U_PACKET_SIZE, cmd); if (status != SANE_STATUS_GOOD) { XDBG ((3, "%s: writing command failed: %s\n", function_name, sane_strstatus (status))); return status; } memset (res, 0, sizeof (Artec48U_Packet)); status = sanei_usb_control_msg (dev->fd, 0xc0, 0x01, res_value, res_index, ARTEC48U_PACKET_SIZE, res); if (status != SANE_STATUS_GOOD) { XDBG ((3, "%s: reading response failed: %s\n", function_name, sane_strstatus (status))); return status; } return status; } static SANE_Status artec48u_device_req (Artec48U_Device * dev, Artec48U_Packet cmd, Artec48U_Packet res) { return artec48u_device_generic_req (dev, send_cmd_value, send_cmd_index, recv_res_value, recv_res_index, cmd, res); } static SANE_Status artec48u_device_small_req (Artec48U_Device * dev, Artec48U_Packet cmd, Artec48U_Packet res) { Artec48U_Packet fixed_cmd; int i; for (i = 0; i < 8; ++i) memcpy (fixed_cmd + i * 8, cmd, 8); return artec48u_device_generic_req (dev, send_small_cmd_value, send_small_cmd_index, recv_small_res_value, recv_small_res_index, fixed_cmd, res); } static SANE_Status artec48u_device_read_raw (Artec48U_Device * dev, SANE_Byte * buffer, size_t * size) { DECLARE_FUNCTION_NAME ("artec48u_device_read_raw") SANE_Status status; CHECK_DEV_ACTIVE (dev, function_name); XDBG ((7, "%s: enter: size=0x%lx\n", function_name, (unsigned long) *size)); status = sanei_usb_read_bulk (dev->fd, buffer, size); if (status != SANE_STATUS_GOOD) { XDBG ((3, "%s: bulk read failed: %s\n", function_name, sane_strstatus (status))); return status; } XDBG ((7, "%s: leave: size=0x%lx\n", function_name, (unsigned long) *size)); return SANE_STATUS_GOOD; } static SANE_Status artec48u_device_set_read_buffer_size (Artec48U_Device * dev, size_t buffer_size) { DECLARE_FUNCTION_NAME ("gt68xx_device_set_read_buffer_size") CHECK_DEV_NOT_NULL (dev, function_name); if (dev->read_active) { XDBG ((3, "%s: BUG: read already active\n", function_name)); return SANE_STATUS_INVAL; } buffer_size = (buffer_size + 63UL) & ~63UL; if (buffer_size > 0) { dev->requested_buffer_size = buffer_size; return SANE_STATUS_GOOD; } XDBG ((3, "%s: bad buffer size\n", function_name)); return SANE_STATUS_INVAL; } static SANE_Status artec48u_device_read_prepare (Artec48U_Device * dev, size_t expected_count) { DECLARE_FUNCTION_NAME ("artec48u_device_read_prepare") CHECK_DEV_ACTIVE (dev, function_name); if (dev->read_active) { XDBG ((3, "%s: read already active\n", function_name)); return SANE_STATUS_INVAL; } dev->read_buffer = (SANE_Byte *) malloc (dev->requested_buffer_size); if (!dev->read_buffer) { XDBG ((3, "%s: not enough memory for the read buffer (%lu bytes)\n", function_name, (unsigned long) dev->requested_buffer_size)); return SANE_STATUS_NO_MEM; } dev->read_active = SANE_TRUE; dev->read_pos = dev->read_bytes_in_buffer = 0; dev->read_bytes_left = expected_count; return SANE_STATUS_GOOD; } static void reader_process_sigterm_handler (int signal) { XDBG ((1, "reader_process: terminated by signal %d\n", signal)); _exit (SANE_STATUS_GOOD); } static void usb_reader_process_sigterm_handler (int signal) { XDBG ((1, "reader_process (usb): terminated by signal %d\n", signal)); cancelRead = SANE_TRUE; } static SANE_Status artec48u_device_read_start (Artec48U_Device * dev) { CHECK_DEV_ACTIVE (dev, "artec48u_device_read_start"); return SANE_STATUS_GOOD; } static SANE_Status artec48u_device_read (Artec48U_Device * dev, SANE_Byte * buffer, size_t * size) { DECLARE_FUNCTION_NAME ("artec48u_device_read") SANE_Status status; size_t byte_count = 0; size_t left_to_read = *size; size_t transfer_size, block_size, raw_block_size; CHECK_DEV_ACTIVE (dev, function_name); if (!dev->read_active) { XDBG ((3, "%s: read not active\n", function_name)); return SANE_STATUS_INVAL; } while (left_to_read > 0) { if (dev->read_bytes_in_buffer == 0) { block_size = dev->requested_buffer_size; if (block_size > dev->read_bytes_left) block_size = dev->read_bytes_left; if (block_size == 0) break; raw_block_size = (block_size + 63UL) & ~63UL; status = artec48u_device_read_raw (dev, dev->read_buffer, &raw_block_size); if (status != SANE_STATUS_GOOD) { XDBG ((3, "%s: read failed\n", function_name)); return status; } dev->read_pos = 0; dev->read_bytes_in_buffer = block_size; dev->read_bytes_left -= block_size; } transfer_size = left_to_read; if (transfer_size > dev->read_bytes_in_buffer) transfer_size = dev->read_bytes_in_buffer; if (transfer_size > 0) { memcpy (buffer, dev->read_buffer + dev->read_pos, transfer_size); dev->read_pos += transfer_size; dev->read_bytes_in_buffer -= transfer_size; byte_count += transfer_size; left_to_read -= transfer_size; buffer += transfer_size; } } *size = byte_count; if (byte_count == 0) return SANE_STATUS_EOF; else return SANE_STATUS_GOOD; } static SANE_Status artec48u_device_read_finish (Artec48U_Device * dev) { DECLARE_FUNCTION_NAME ("artec48u_device_read_finish") CHECK_DEV_ACTIVE (dev, function_name); if (!dev->read_active) { XDBG ((3, "%s: read not active\n", function_name)); return SANE_STATUS_INVAL; } XDBG ((7, "%s: read_bytes_left = %ld\n", function_name, (long) dev->read_bytes_left)); free (dev->read_buffer); dev->read_buffer = NULL; dev->read_active = SANE_FALSE; return SANE_STATUS_GOOD; } static SANE_Status artec48u_delay_buffer_init (Artec48U_Delay_Buffer * delay, SANE_Int pixels_per_line) { DECLARE_FUNCTION_NAME ("artec48u_delay_buffer_init") SANE_Int bytes_per_line; SANE_Int line_count, i; if (pixels_per_line <= 0) { XDBG ((3, "%s: BUG: pixels_per_line=%d\n", function_name, pixels_per_line)); return SANE_STATUS_INVAL; } bytes_per_line = pixels_per_line * sizeof (unsigned int); delay->line_count = line_count = 1; delay->read_index = 0; delay->write_index = 0; delay->mem_block = (SANE_Byte *) malloc (bytes_per_line * line_count); if (!delay->mem_block) { XDBG ((3, "%s: no memory for delay block\n", function_name)); return SANE_STATUS_NO_MEM; } delay->lines = (unsigned int **) malloc (sizeof (unsigned int *) * line_count); if (!delay->lines) { free (delay->mem_block); XDBG ((3, "%s: no memory for delay line pointers\n", function_name)); return SANE_STATUS_NO_MEM; } for (i = 0; i < line_count; ++i) delay->lines[i] = (unsigned int *) (delay->mem_block + i * bytes_per_line); return SANE_STATUS_GOOD; } static SANE_Status artec48u_delay_buffer_done (Artec48U_Delay_Buffer * delay) { if (delay->lines) { free (delay->lines); delay->lines = NULL; } if (delay->mem_block) { free (delay->mem_block); delay->mem_block = NULL; } return SANE_STATUS_GOOD; } #define DELAY_BUFFER_WRITE_PTR(delay) ( (delay)->lines[(delay)->write_index] ) #define DELAY_BUFFER_READ_PTR(delay) ( (delay)->lines[(delay)->read_index ] ) #define DELAY_BUFFER_STEP(delay) \ do { \ (delay)->read_index = ((delay)->read_index + 1) % (delay)->line_count; \ (delay)->write_index = ((delay)->write_index + 1) % (delay)->line_count; \ } while (SANE_FALSE) static inline void unpack_8_mono (SANE_Byte * src, unsigned int *dst, SANE_Int pixels_per_line) { XDBG ((3, "unpack_8_mono\n")); for (; pixels_per_line > 0; ++src, ++dst, --pixels_per_line) { *dst = (((unsigned int) *src) << 8) | *src; } } static inline void unpack_16_le_mono (SANE_Byte * src, unsigned int *dst, SANE_Int pixels_per_line) { XDBG ((3, "unpack_16_le_mono\n")); for (; pixels_per_line > 0; src += 2, dst++, --pixels_per_line) { *dst = (((unsigned int) src[1]) << 8) | src[0]; } } static SANE_Status line_read_gray_8 (Artec48U_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; unsigned int *buffer; XDBG ((3, "line_read_gray_8\n")); size = reader->params.scan_bpl; status = artec48u_device_read (reader->dev, reader->pixel_buffer, &size); if (status != SANE_STATUS_GOOD) return status; buffer = DELAY_BUFFER_READ_PTR (&reader->g_delay); buffer_pointers_return[0] = buffer; unpack_8_mono (reader->pixel_buffer, buffer, reader->pixels_per_line); return SANE_STATUS_GOOD; } static SANE_Status line_read_gray_16 (Artec48U_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; unsigned int *buffer; XDBG ((3, "line_read_gray_16\n")); size = reader->params.scan_bpl; status = artec48u_device_read (reader->dev, reader->pixel_buffer, &size); if (status != SANE_STATUS_GOOD) return status; buffer = DELAY_BUFFER_READ_PTR (&reader->g_delay); buffer_pointers_return[0] = buffer; unpack_16_le_mono (reader->pixel_buffer, buffer, reader->pixels_per_line); return SANE_STATUS_GOOD; } static SANE_Status line_read_bgr_8_line_mode (Artec48U_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; SANE_Int pixels_per_line; SANE_Byte *pixel_buffer = reader->pixel_buffer; XDBG ((3, "line_read_bgr_8_line_mode\n")); size = reader->params.scan_bpl * 3; status = artec48u_device_read (reader->dev, pixel_buffer, &size); if (status != SANE_STATUS_GOOD) return status; pixels_per_line = reader->pixels_per_line; unpack_8_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->b_delay), pixels_per_line); pixel_buffer += reader->params.scan_bpl; unpack_8_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->g_delay), pixels_per_line); pixel_buffer += reader->params.scan_bpl; unpack_8_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->r_delay), pixels_per_line); buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay); buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay); buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay); DELAY_BUFFER_STEP (&reader->r_delay); DELAY_BUFFER_STEP (&reader->g_delay); DELAY_BUFFER_STEP (&reader->b_delay); return SANE_STATUS_GOOD; } static SANE_Status line_read_bgr_16_line_mode (Artec48U_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; SANE_Int pixels_per_line; SANE_Byte *pixel_buffer = reader->pixel_buffer; XDBG ((3, "line_read_bgr_16_line_mode\n")); size = reader->params.scan_bpl * 3; status = artec48u_device_read (reader->dev, pixel_buffer, &size); if (status != SANE_STATUS_GOOD) return status; pixels_per_line = reader->pixels_per_line; unpack_16_le_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->b_delay), pixels_per_line); pixel_buffer += reader->params.scan_bpl; unpack_16_le_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->g_delay), pixels_per_line); pixel_buffer += reader->params.scan_bpl; unpack_16_le_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->r_delay), pixels_per_line); buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay); buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay); buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay); DELAY_BUFFER_STEP (&reader->r_delay); DELAY_BUFFER_STEP (&reader->g_delay); DELAY_BUFFER_STEP (&reader->b_delay); return SANE_STATUS_GOOD; } static SANE_Status artec48u_line_reader_init_delays (Artec48U_Line_Reader * reader) { SANE_Status status; if (reader->params.color) { status = artec48u_delay_buffer_init (&reader->r_delay, reader->params.pixel_xs); if (status != SANE_STATUS_GOOD) return status; status = artec48u_delay_buffer_init (&reader->g_delay, reader->params.pixel_xs); if (status != SANE_STATUS_GOOD) { artec48u_delay_buffer_done (&reader->r_delay); return status; } status = artec48u_delay_buffer_init (&reader->b_delay, reader->params.pixel_xs); if (status != SANE_STATUS_GOOD) { artec48u_delay_buffer_done (&reader->g_delay); artec48u_delay_buffer_done (&reader->r_delay); return status; } } else { status = artec48u_delay_buffer_init (&reader->g_delay, reader->params.pixel_xs); if (status != SANE_STATUS_GOOD) return status; } reader->delays_initialized = SANE_TRUE; return SANE_STATUS_GOOD; } static void artec48u_line_reader_free_delays (Artec48U_Line_Reader * reader) { if (!reader) { return; } if (reader->delays_initialized) { if (reader->params.color) { artec48u_delay_buffer_done (&reader->b_delay); artec48u_delay_buffer_done (&reader->g_delay); artec48u_delay_buffer_done (&reader->r_delay); } else { artec48u_delay_buffer_done (&reader->g_delay); } reader->delays_initialized = SANE_FALSE; } } static SANE_Status artec48u_line_reader_new (Artec48U_Device * dev, Artec48U_Scan_Parameters * params, Artec48U_Line_Reader ** reader_return) { DECLARE_FUNCTION_NAME ("artec48u_line_reader_new") SANE_Status status; Artec48U_Line_Reader *reader; SANE_Int image_size; SANE_Int scan_bpl_full; XDBG ((6, "%s: enter\n", function_name)); XDBG ((6, "%s: enter params xdpi: %i\n", function_name, params->xdpi)); XDBG ((6, "%s: enter params ydpi: %i\n", function_name, params->ydpi)); XDBG ((6, "%s: enter params depth: %i\n", function_name, params->depth)); XDBG ((6, "%s: enter params color: %i\n", function_name, params->color)); XDBG ((6, "%s: enter params pixel_xs: %i\n", function_name, params->pixel_xs)); XDBG ((6, "%s: enter params pixel_ys: %i\n", function_name, params->pixel_ys)); XDBG ((6, "%s: enter params scan_xs: %i\n", function_name, params->scan_xs)); XDBG ((6, "%s: enter params scan_ys: %i\n", function_name, params->scan_ys)); XDBG ((6, "%s: enter params scan_bpl: %i\n", function_name, params->scan_bpl)); *reader_return = NULL; reader = (Artec48U_Line_Reader *) malloc (sizeof (Artec48U_Line_Reader)); if (!reader) { XDBG ((3, "%s: cannot allocate Artec48U_Line_Reader\n", function_name)); return SANE_STATUS_NO_MEM; } memset (reader, 0, sizeof (Artec48U_Line_Reader)); reader->dev = dev; memcpy (&reader->params, params, sizeof (Artec48U_Scan_Parameters)); reader->pixel_buffer = 0; reader->delays_initialized = SANE_FALSE; reader->read = NULL; status = artec48u_line_reader_init_delays (reader); if (status != SANE_STATUS_GOOD) { XDBG ((3, "%s: cannot allocate line buffers: %s\n", function_name, sane_strstatus (status))); free (reader); return status; } reader->pixels_per_line = reader->params.pixel_xs; if (!reader->params.color) { XDBG ((2, "!reader->params.color\n")); if (reader->params.depth == 8) reader->read = line_read_gray_8; else if (reader->params.depth == 16) reader->read = line_read_gray_16; } else { XDBG ((2, "reader line mode\n")); if (reader->params.depth == 8) { XDBG ((2, "depth 8\n")); reader->read = line_read_bgr_8_line_mode; } else if (reader->params.depth == 16) { XDBG ((2, "depth 16\n")); reader->read = line_read_bgr_16_line_mode; } } if (reader->read == NULL) { XDBG ((3, "%s: unsupported bit depth (%d)\n", function_name, reader->params.depth)); artec48u_line_reader_free_delays (reader); free (reader); return SANE_STATUS_UNSUPPORTED; } scan_bpl_full = reader->params.scan_bpl; if (reader->params.color) scan_bpl_full *= 3; reader->pixel_buffer = malloc (scan_bpl_full); if (!reader->pixel_buffer) { XDBG ((3, "%s: cannot allocate pixel buffer\n", function_name)); artec48u_line_reader_free_delays (reader); free (reader); return SANE_STATUS_NO_MEM; } artec48u_device_set_read_buffer_size (reader->dev, scan_bpl_full /* 200 */ ); image_size = scan_bpl_full * reader->params.scan_ys; status = artec48u_device_read_prepare (reader->dev, image_size); if (status != SANE_STATUS_GOOD) { XDBG ((3, "%s: artec48u_device_read_prepare failed: %s\n", function_name, sane_strstatus (status))); free (reader->pixel_buffer); artec48u_line_reader_free_delays (reader); free (reader); return status; } XDBG ((6, "%s: leave: ok\n", function_name)); *reader_return = reader; return SANE_STATUS_GOOD; } static SANE_Status artec48u_line_reader_free (Artec48U_Line_Reader * reader) { DECLARE_FUNCTION_NAME ("artec48u_line_reader_free") SANE_Status status; XDBG ((6, "%s: enter\n", function_name)); if (!reader) { return SANE_STATUS_GOOD; } artec48u_line_reader_free_delays (reader); if (reader->pixel_buffer) { free (reader->pixel_buffer); reader->pixel_buffer = NULL; } status = artec48u_device_read_finish (reader->dev); if (status != SANE_STATUS_GOOD) { XDBG ((3, "%s: artec48u_device_read_finish failed: %s\n", function_name, sane_strstatus (status))); } if (reader) free (reader); XDBG ((6, "%s: leave\n", function_name)); return status; } static SANE_Status artec48u_line_reader_read (Artec48U_Line_Reader * reader, unsigned int **buffer_pointers_return) { return (*reader->read) (reader, buffer_pointers_return); } static SANE_Status artec48u_scanner_new (Artec48U_Device * dev, Artec48U_Scanner ** scanner_return) { DECLARE_FUNCTION_NAME ("artec48u_scanner_new") Artec48U_Scanner *s; *scanner_return = NULL; s = (Artec48U_Scanner *) malloc (sizeof (Artec48U_Scanner)); if (!s) { XDBG ((5, "%s: no memory for Artec48U_Scanner\n", function_name)); return SANE_STATUS_NO_MEM; } s->dev = dev; s->reader = NULL; s->scanning = SANE_FALSE; s->line_buffer = NULL; s->lineart_buffer = NULL; s->next = NULL; s->pipe_handle = NULL; s->buffer_pointers[0] = NULL; s->buffer_pointers[1] = NULL; s->buffer_pointers[2] = NULL; s->shading_buffer_w = NULL; s->shading_buffer_b = NULL; s->shading_buffer_white[0] = NULL; s->shading_buffer_white[1] = NULL; s->shading_buffer_white[2] = NULL; s->shading_buffer_black[0] = NULL; s->shading_buffer_black[1] = NULL; s->shading_buffer_black[2] = NULL; *scanner_return = s; return SANE_STATUS_GOOD; } static SANE_Status artec48u_scanner_read_line (Artec48U_Scanner * s, unsigned int **buffer_pointers, SANE_Bool shading) { DECLARE_FUNCTION_NAME ("artec48u_scanner_read_line") SANE_Status status; int i, j, c; status = artec48u_line_reader_read (s->reader, buffer_pointers); if (status != SANE_STATUS_GOOD) { XDBG ((5, "%s: artec48u_line_reader_read failed: %s\n", function_name, sane_strstatus (status))); return status; } if (shading != SANE_TRUE) return status; c = s->reader->pixels_per_line; if (s->reader->params.color == SANE_TRUE) { for (i = c - 1; i >= 0; i--) { for (j = 0; j < 3; j++) { int new_value; unsigned int value = buffer_pointers[j][i]; if (value < s->shading_buffer_black[j][i]) value = s->shading_buffer_black[j][i]; if (value > s->shading_buffer_white[j][i]) value = s->shading_buffer_white[j][i]; new_value = (double) (value - s->shading_buffer_black[j][i]) * 65535.0 / (double) (s->shading_buffer_white[j][i] - s->shading_buffer_black[j][i]); if (new_value < 0) new_value = 0; if (new_value > 65535) new_value = 65535; new_value = s->gamma_array[j + 1][s->contrast_array[s-> brightness_array [new_value]]]; new_value = s->gamma_array[0][new_value]; buffer_pointers[j][i] = new_value; } } } else { for (i = c - 1; i >= 0; i--) { int new_value; unsigned int value = buffer_pointers[0][i]; new_value = (double) (value - s->shading_buffer_black[1][i]) * 65535.0 / (double) (s->shading_buffer_white[1][i] - s->shading_buffer_black[1][i]); if (new_value < 0) new_value = 0; if (new_value > 65535) new_value = 65535; new_value = s->gamma_array[0][s-> contrast_array[s->brightness_array[new_value]]]; buffer_pointers[0][i] = new_value; } } return status; } static SANE_Status artec48u_scanner_free (Artec48U_Scanner * s) { DECLARE_FUNCTION_NAME ("artec48u_scanner_free") if (!s) { XDBG ((5, "%s: scanner==NULL\n", function_name)); return SANE_STATUS_INVAL; } if (s->reader) { artec48u_line_reader_free (s->reader); s->reader = NULL; } free (s->shading_buffer_w); free (s->shading_buffer_b); free (s->shading_buffer_white[0]); free (s->shading_buffer_black[0]); free (s->shading_buffer_white[1]); free (s->shading_buffer_black[1]); free (s->shading_buffer_white[2]); free (s->shading_buffer_black[2]); if (s->line_buffer) free (s->line_buffer); if (s->lineart_buffer) free (s->lineart_buffer); free (s); return SANE_STATUS_GOOD; } static SANE_Status artec48u_scanner_internal_start_scan (Artec48U_Scanner * s) { DECLARE_FUNCTION_NAME ("artec48u_scanner_internal_start_scan") SANE_Status status; SANE_Bool ready; SANE_Int repeat_count; status = artec48u_wait_for_positioning (s->dev); if (status != SANE_STATUS_GOOD) { XDBG ((2, "%s: artec48u_scanner_wait_for_positioning error: %s\n", function_name, sane_strstatus (status))); return status; } status = artec48u_generic_start_scan (s->dev); if (status != SANE_STATUS_GOOD) { XDBG ((2, "%s: artec48u_device_start_scan error: %s\n", function_name, sane_strstatus (status))); return status; } for (repeat_count = 0; repeat_count < 30 * 10; ++repeat_count) { status = artec48u_generic_read_scanned_data (s->dev, &ready); if (status != SANE_STATUS_GOOD) { XDBG ((2, "%s: artec48u_device_read_scanned_data error: %s\n", function_name, sane_strstatus (status))); return status; } if (ready) break; usleep (100000); } if (!ready) { XDBG ((2, "%s: scanner still not ready - giving up\n", function_name)); return SANE_STATUS_DEVICE_BUSY; } status = artec48u_device_read_start (s->dev); if (status != SANE_STATUS_GOOD) { XDBG ((2, "%s: artec48u_device_read_start error: %s\n", function_name, sane_strstatus (status))); return status; } return SANE_STATUS_GOOD; } static SANE_Status artec48u_scanner_start_scan_extended (Artec48U_Scanner * s, Artec48U_Scan_Request * request, Artec48U_Scan_Action action, Artec48U_Scan_Parameters * params) { DECLARE_FUNCTION_NAME ("artec48u_scanner_start_scan_extended") SANE_Status status; status = artec48u_wait_for_positioning (s->dev); if (status != SANE_STATUS_GOOD) { XDBG ((2, "%s: artec48u_scanner_wait_for_positioning error: %s\n", function_name, sane_strstatus (status))); return status; } if (action == SA_SCAN) status = artec48u_setup_scan (s, request, action, SANE_FALSE, params); if (status != SANE_STATUS_GOOD) { XDBG ((2, "%s: artec48u_device_setup_scan failed: %s\n", function_name, sane_strstatus (status))); return status; } status = artec48u_line_reader_new (s->dev, params, &s->reader); if (status != SANE_STATUS_GOOD) { XDBG ((2, "%s: artec48u_line_reader_new failed: %s\n", function_name, sane_strstatus (status))); return status; } status = artec48u_scanner_internal_start_scan (s); if (status != SANE_STATUS_GOOD) { XDBG ((2, "%s: artec48u_scanner_internal_start_scan failed: %s\n", function_name, sane_strstatus (status))); return status; } return SANE_STATUS_GOOD; } static SANE_Status artec48u_scanner_start_scan (Artec48U_Scanner * s, Artec48U_Scan_Request * request, Artec48U_Scan_Parameters * params) { return artec48u_scanner_start_scan_extended (s, request, SA_SCAN, params); } static SANE_Status artec48u_scanner_stop_scan (Artec48U_Scanner * s) { XDBG ((1, "artec48u_scanner_stop_scan begin: \n")); artec48u_line_reader_free (s->reader); s->reader = NULL; return artec48u_stop_scan (s->dev); } static void calculateGamma (Artec48U_Scanner * s) { double d; int gval; unsigned int i; double gamma = SANE_UNFIX (s->val[OPT_GAMMA].w); d = 65536.0 / pow (65536.0, 1.0 / gamma); for (i = 0; i < 65536; i++) { gval = (int) (pow ((double) i, 1.0 / gamma) * d); s->gamma_array[0][i] = gval; } } static void calculateGammaRed (Artec48U_Scanner * s) { double d; int gval; unsigned int i; double gamma = SANE_UNFIX (s->val[OPT_GAMMA_R].w); d = 65536.0 / pow (65536.0, 1.0 / gamma); for (i = 0; i < 65536; i++) { gval = (int) (pow ((double) i, 1.0 / gamma) * d); s->gamma_array[1][i] = gval; } } static void calculateGammaGreen (Artec48U_Scanner * s) { double d; int gval; unsigned int i; double gamma = SANE_UNFIX (s->val[OPT_GAMMA_G].w); d = 65536.0 / pow (65536.0, 1.0 / gamma); for (i = 0; i < 65536; i++) { gval = (int) (pow ((double) i, 1.0 / gamma) * d); s->gamma_array[2][i] = gval; } } static void calculateGammaBlue (Artec48U_Scanner * s) { double d; int gval; unsigned int i; double gamma = SANE_UNFIX (s->val[OPT_GAMMA_B].w); d = 65536.0 / pow (65536.0, 1.0 / gamma); for (i = 0; i < 65536; i++) { gval = (int) (pow ((double) i, 1.0 / gamma) * d); s->gamma_array[3][i] = gval; } } static SANE_Status artec48u_calculate_shading_buffer (Artec48U_Scanner * s, int start, int end, int resolution, SANE_Bool color) { int i; int c; int bpp; c = 0; bpp = 6; switch (resolution) { case 50: bpp = 72; break; case 100: bpp = 36; break; case 200: bpp = 18; break; case 300: bpp = 12; break; case 600: bpp = 6; break; case 1200: if(s->dev->is_epro == 0) bpp = 6; else bpp = 3; } for (i = start * bpp; i < end * bpp; i += bpp) { if (color) { s->shading_buffer_white[0][c] = (unsigned int) s->shading_buffer_w[i] + ((((unsigned int) s->shading_buffer_w[i + 1]) << 8)); s->shading_buffer_white[2][c] = (unsigned int) s->shading_buffer_w[i + 4] + ((((unsigned int) s->shading_buffer_w[i + 5]) << 8)); s->shading_buffer_black[0][c] = (unsigned int) s->shading_buffer_b[i] + ((((unsigned int) s->shading_buffer_b[i + 1]) << 8)); s->shading_buffer_black[2][c] = (unsigned int) s->shading_buffer_b[i + 4] + ((((unsigned int) s->shading_buffer_b[i + 5]) << 8)); } s->shading_buffer_white[1][c] = (unsigned int) s->shading_buffer_w[i + 2] + ((((unsigned int) s->shading_buffer_w[i + 3]) << 8)); s->shading_buffer_black[1][c] = (unsigned int) s->shading_buffer_b[i + 2] + ((((unsigned int) s->shading_buffer_b[i + 3]) << 8)); ++c; } return SANE_STATUS_GOOD; } 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; } static SANE_Status init_options (Artec48U_Scanner * s) { int i; XDBG ((5, "init_options: scanner %p\n", (void *) s)); XDBG ((5, "init_options: start\n")); XDBG ((5, "init_options: num options %i\n", NUM_OPTIONS)); memset (s->val, 0, sizeof (s->val)); memset (s->opt, 0, sizeof (s->opt)); for (i = 0; i < NUM_OPTIONS; ++i) { s->opt[i].size = sizeof (SANE_Word); s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } 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].type = SANE_TYPE_INT; 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->opt[OPT_MODE_GROUP].name = "scanmode-group"; s->opt[OPT_MODE_GROUP].title = SANE_TITLE_SCAN_MODE; s->opt[OPT_MODE_GROUP].desc = ""; s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MODE_GROUP].size = 0; s->opt[OPT_MODE_GROUP].unit = SANE_UNIT_NONE; s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_MODE_GROUP].cap = 0; s->opt[OPT_SCAN_MODE].name = SANE_NAME_SCAN_MODE; s->opt[OPT_SCAN_MODE].title = SANE_TITLE_SCAN_MODE; s->opt[OPT_SCAN_MODE].desc = SANE_DESC_SCAN_MODE; s->opt[OPT_SCAN_MODE].type = SANE_TYPE_STRING; s->opt[OPT_SCAN_MODE].size = max_string_size (mode_list); s->opt[OPT_SCAN_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_SCAN_MODE].constraint.string_list = mode_list; s->val[OPT_SCAN_MODE].s = strdup (mode_list[1]); s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT; s->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_NONE; s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_BIT_DEPTH].constraint.word_list = bitdepth_list; s->val[OPT_BIT_DEPTH].w = bitdepth_list[1]; /* black level (lineart only) */ s->opt[OPT_BLACK_LEVEL].name = SANE_NAME_BLACK_LEVEL; s->opt[OPT_BLACK_LEVEL].title = SANE_TITLE_BLACK_LEVEL; s->opt[OPT_BLACK_LEVEL].desc = SANE_DESC_BLACK_LEVEL; s->opt[OPT_BLACK_LEVEL].type = SANE_TYPE_INT; s->opt[OPT_BLACK_LEVEL].unit = SANE_UNIT_NONE; s->opt[OPT_BLACK_LEVEL].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BLACK_LEVEL].constraint.range = &blacklevel_range; s->opt[OPT_BLACK_LEVEL].cap |= SANE_CAP_INACTIVE; s->val[OPT_BLACK_LEVEL].w = 127; s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_RESOLUTION].constraint.word_list = resbit_list; s->val[OPT_RESOLUTION].w = resbit_list[1]; /* "Enhancement" group: */ s->opt[OPT_ENHANCEMENT_GROUP].name = "enhancement-group"; s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement"); s->opt[OPT_ENHANCEMENT_GROUP].desc = ""; s->opt[OPT_ENHANCEMENT_GROUP].size = 0; s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_ENHANCEMENT_GROUP].cap = 0; /* brightness */ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT; s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE; s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BRIGHTNESS].constraint.range = &brightness_contrast_range; s->val[OPT_BRIGHTNESS].w = 0; /* contrast */ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST; s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST; s->opt[OPT_CONTRAST].type = SANE_TYPE_INT; s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE; s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CONTRAST].constraint.range = &brightness_contrast_range; s->val[OPT_CONTRAST].w = 0; /* master analog gamma */ s->opt[OPT_GAMMA].name = SANE_NAME_ANALOG_GAMMA; s->opt[OPT_GAMMA].title = SANE_TITLE_ANALOG_GAMMA; s->opt[OPT_GAMMA].desc = SANE_DESC_ANALOG_GAMMA; s->opt[OPT_GAMMA].type = SANE_TYPE_FIXED; s->opt[OPT_GAMMA].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA].constraint.range = &gamma_range; s->val[OPT_GAMMA].w = SANE_FIX (s->dev->gamma_master); s->opt[OPT_GAMMA].size = sizeof (SANE_Word); /* red analog gamma */ s->opt[OPT_GAMMA_R].name = SANE_NAME_ANALOG_GAMMA_R; s->opt[OPT_GAMMA_R].title = SANE_TITLE_ANALOG_GAMMA_R; s->opt[OPT_GAMMA_R].desc = SANE_DESC_ANALOG_GAMMA_R; s->opt[OPT_GAMMA_R].type = SANE_TYPE_FIXED; s->opt[OPT_GAMMA_R].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_R].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_R].constraint.range = &gamma_range; s->val[OPT_GAMMA_R].w = SANE_FIX (s->dev->gamma_r); /* green analog gamma */ s->opt[OPT_GAMMA_G].name = SANE_NAME_ANALOG_GAMMA_G; s->opt[OPT_GAMMA_G].title = SANE_TITLE_ANALOG_GAMMA_G; s->opt[OPT_GAMMA_G].desc = SANE_DESC_ANALOG_GAMMA_G; s->opt[OPT_GAMMA_G].type = SANE_TYPE_FIXED; s->opt[OPT_GAMMA_G].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_G].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_G].constraint.range = &gamma_range; s->val[OPT_GAMMA_G].w = SANE_FIX (s->dev->gamma_g); /* blue analog gamma */ s->opt[OPT_GAMMA_B].name = SANE_NAME_ANALOG_GAMMA_B; s->opt[OPT_GAMMA_B].title = SANE_TITLE_ANALOG_GAMMA_B; s->opt[OPT_GAMMA_B].desc = SANE_DESC_ANALOG_GAMMA_B; s->opt[OPT_GAMMA_B].type = SANE_TYPE_FIXED; s->opt[OPT_GAMMA_B].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_B].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_B].constraint.range = &gamma_range; s->val[OPT_GAMMA_B].w = SANE_FIX (s->dev->gamma_b); s->opt[OPT_DEFAULT_ENHANCEMENTS].name = "default-enhancements"; s->opt[OPT_DEFAULT_ENHANCEMENTS].title = SANE_I18N ("Defaults"); s->opt[OPT_DEFAULT_ENHANCEMENTS].desc = SANE_I18N ("Set default values for enhancement controls."); s->opt[OPT_DEFAULT_ENHANCEMENTS].size = 0; s->opt[OPT_DEFAULT_ENHANCEMENTS].type = SANE_TYPE_BUTTON; s->opt[OPT_DEFAULT_ENHANCEMENTS].unit = SANE_UNIT_NONE; s->opt[OPT_DEFAULT_ENHANCEMENTS].constraint_type = SANE_CONSTRAINT_NONE; /* "Geometry" group: */ s->opt[OPT_GEOMETRY_GROUP].name = "geometry-group"; s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry"); s->opt[OPT_GEOMETRY_GROUP].desc = ""; s->opt[OPT_GEOMETRY_GROUP].size = 0; s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_GEOMETRY_GROUP].cap = 0; /* top-left x */ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; s->opt[OPT_TL_X].unit = SANE_UNIT_MM; s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_X].constraint.range = &scan_range_x; s->val[OPT_TL_X].w = SANE_FIX (0.0); /* top-left y */ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_Y].constraint.range = &scan_range_y; s->val[OPT_TL_Y].w = SANE_FIX (0.0); /* bottom-right x */ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; s->opt[OPT_BR_X].unit = SANE_UNIT_MM; s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_X].constraint.range = &scan_range_x; s->val[OPT_BR_X].w = SANE_FIX (50.0); /* bottom-right y */ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_Y].constraint.range = &scan_range_y; s->val[OPT_BR_Y].w = SANE_FIX (50.0); /* "Calibration" group: */ s->opt[OPT_CALIBRATION_GROUP].name = "calibration-group"; s->opt[OPT_CALIBRATION_GROUP].title = SANE_I18N ("Calibration"); s->opt[OPT_CALIBRATION_GROUP].desc = ""; s->opt[OPT_CALIBRATION_GROUP].size = 0; s->opt[OPT_CALIBRATION_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_CALIBRATION_GROUP].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_CALIBRATION_GROUP].cap = 0; /* calibrate */ s->opt[OPT_CALIBRATE].name = "calibration"; s->opt[OPT_CALIBRATE].title = SANE_I18N ("Calibrate before next scan"); s->opt[OPT_CALIBRATE].desc = SANE_I18N ("If enabled, the device will be calibrated before the " "next scan. Otherwise, calibration is performed " "only before the first start."); s->opt[OPT_CALIBRATE].type = SANE_TYPE_BOOL; s->opt[OPT_CALIBRATE].unit = SANE_UNIT_NONE; s->opt[OPT_CALIBRATE].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_CALIBRATE].w = SANE_FALSE; /* calibrate */ s->opt[OPT_CALIBRATE_SHADING].name = "calibration-shading"; s->opt[OPT_CALIBRATE_SHADING].title = SANE_I18N ("Only perform shading-correction"); s->opt[OPT_CALIBRATE_SHADING].desc = SANE_I18N ("If enabled, only the shading correction is " "performed during calibration. The default values " "for gain, offset and exposure time, " "either built-in or from the configuration file, " "are used."); s->opt[OPT_CALIBRATE_SHADING].type = SANE_TYPE_BOOL; s->opt[OPT_CALIBRATE_SHADING].unit = SANE_UNIT_NONE; s->opt[OPT_CALIBRATE_SHADING].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_CALIBRATE_SHADING].w = SANE_FALSE; #ifdef ARTEC48U_USE_BUTTONS s->opt[OPT_BUTTON_STATE].name = "button-state"; s->opt[OPT_BUTTON_STATE].title = SANE_I18N ("Button state"); s->opt[OPT_BUTTON_STATE].type = SANE_TYPE_INT; s->opt[OPT_BUTTON_STATE].unit = SANE_UNIT_NONE; s->opt[OPT_BUTTON_STATE].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_BUTTON_STATE].cap = SANE_CAP_SOFT_DETECT; s->val[OPT_BUTTON_STATE].w = 0; #endif return SANE_STATUS_GOOD; } static void calculate_brightness (Artec48U_Scanner * s) { long cnt; double bright; bright = (double) s->val[OPT_BRIGHTNESS].w; bright *= 257.0; for (cnt = 0; cnt < 65536; cnt++) { if (bright < 0.0) s->brightness_array[cnt] = (int) (((double) cnt * (65535.0 + bright)) / 65535.0); else s->brightness_array[cnt] = (int) ((double) cnt + ((65535.0 - (double) cnt) * bright) / 65535.0); if (s->brightness_array[cnt] > 65535) s->brightness_array[cnt] = 65535; if (s->brightness_array[cnt] < 0) s->brightness_array[cnt] = 0; } } static void calculate_contrast (Artec48U_Scanner * s) { int val; double p; int cnt; double contr; contr = (double) s->val[OPT_CONTRAST].w; contr *= 257.0; for (cnt = 0; cnt < 65536; cnt++) { if (contr < 0.0) { val = (int) (cnt > 32769) ? (65535 - cnt) : cnt; val = (int) (32769.0 * pow ((double) (val ? val : 1) / 32769.0, (32769.0 + contr) / 32769.0)); s->contrast_array[cnt] = (cnt > 32769) ? (65535 - val) : val; if (s->contrast_array[cnt] > 65535) s->contrast_array[cnt] = 65535; if (s->contrast_array[cnt] < 0) s->contrast_array[cnt] = 0; } else { val = (cnt > 32769) ? (65535 - cnt) : cnt; p = ((int) contr == 32769) ? 32769.0 : 32769.0 / (32769.0 - contr); val = (int) (32769.0 * pow ((double) val / 32769.0, p)); s->contrast_array[cnt] = (cnt > 32639) ? (65535 - val) : val; if (s->contrast_array[cnt] > 65535) s->contrast_array[cnt] = 65535; if (s->contrast_array[cnt] < 0) s->contrast_array[cnt] = 0; } } } /* The calibration function Disclaimer: the following might be complete crap :-) -Gain, offset, exposure time It seems, that the gain values are actually constants. The windows driver always uses the values 0x0a,0x03,0x03, during calibration as well as during a normal scan. The exposure values are set to 0x04 for black calibration. It's not necessary to move the scan head during this stage. Calibration starts with default values for offset/exposure. These values are increased/decreased until the white and black values are within a specific range, defined by WHITE_MIN, WHITE_MAX, BLACK_MIN and BLACK_MAX. -White shading correction The scanning head is moved some lines over the calibration strip. Some lines are scanned at 600dpi/16bit over the full width. The average values are used for the shading buffer. The normal exposure values are used. -Black shading correction Works like the white shading correction, with the difference, that the red-, green- and blue exposure time is set to 0x04 (the value is taken from the windoze driver). -Since we do this over the whole width of the image with the maximal optical resolution, we can use the shading data for every scan, independent of the size, position or resolution, because we have the shading values for every sensor/LED. Note: For a CIS device, it's sufficient to determine those values once. It's not necessary, to repeat the calibration sequence before every new scan. The windoze driver even saves the values to various files to avoid the quite lengthy calibration sequence. This backend can also save the values to files. For this purpose, the user has to create a hidden directory called .artec-eplus48u in his/her home directory. If the user insists on calibration before every new scan, he/she can enable a specific option in the backend. */ static SANE_Status calibrate_scanner (SANE_Handle handle) { Artec48U_Scanner *s = handle; unsigned int *buffer_pointers[3]; int avg_black[3]; int avg_white[3]; int exp_off; int c; int finish = 0; int noloop = 0; if ((s->val[OPT_CALIBRATE].w == SANE_TRUE) && (s->val[OPT_CALIBRATE_SHADING].w == SANE_FALSE)) { while (finish == 0) { finish = 1; /*get black values */ artec48u_carriage_home (s->dev); artec48u_wait_for_positioning (s->dev); s->reader = NULL; s->scanning = SANE_TRUE; init_shading_buffer (s); artec48u_setup_scan (s, &(s->request), SA_CALIBRATE_SCAN_BLACK, SANE_FALSE, &(s->params)); artec48u_scanner_start_scan_extended (s, &(s->request), SA_CALIBRATE_SCAN_OFFSET_1, &(s->params)); for (c = 0; c < s->dev->shading_lines_b; c++) { artec48u_scanner_read_line (s, buffer_pointers, SANE_FALSE); /* we abuse the shading buffer for the offset calculation */ add_to_shading_buffer (s, buffer_pointers); } artec48u_scanner_stop_scan (s); finish_offset_buffer (s, &avg_black[0], &avg_black[1], &avg_black[2]); s->scanning = SANE_FALSE; XDBG ((1, "avg_r: %i, avg_g: %i, avg_b: %i\n", avg_black[0], avg_black[1], avg_black[2])); /*adjust offset */ for (c = 0; c < 3; c++) { if (c == 0) { if (avg_black[c] < BLACK_MIN) { s->dev->afe_params.r_offset -= 1; finish = 0; XDBG ((1, "adjust offset r: -1\n")); } else if (avg_black[c] > BLACK_MAX) { s->dev->afe_params.r_offset += 1; finish = 0; XDBG ((1, "adjust offset r: +1\n")); } } if (c == 1) { if (avg_black[c] < BLACK_MIN) { s->dev->afe_params.g_offset -= 1; finish = 0; XDBG ((1, "adjust offset g: -1\n")); } else if (avg_black[c] > BLACK_MAX) { s->dev->afe_params.g_offset += 1; finish = 0; XDBG ((1, "adjust offset g: +1\n")); } } if (c == 2) { if (avg_black[c] < BLACK_MIN) { s->dev->afe_params.b_offset -= 1; finish = 0; XDBG ((1, "adjust offset b: -1\n")); } else if (avg_black[c] > BLACK_MAX) { s->dev->afe_params.b_offset += 1; finish = 0; XDBG ((1, "adjust offset b: +1\n")); } } } /*adjust exposure */ /*get white values */ artec48u_carriage_home (s->dev); artec48u_wait_for_positioning (s->dev); s->reader = NULL; s->scanning = SANE_TRUE; init_shading_buffer (s); artec48u_setup_scan (s, &(s->request), SA_CALIBRATE_SCAN_WHITE, SANE_FALSE, &(s->params)); artec48u_scanner_start_scan_extended (s, &(s->request), SA_CALIBRATE_SCAN_EXPOSURE_1, &(s->params)); for (c = 0; c < s->dev->shading_lines_w; c++) { artec48u_scanner_read_line (s, buffer_pointers, SANE_FALSE); /* we abuse the shading buffer for the exposure calculation */ add_to_shading_buffer (s, buffer_pointers); } artec48u_scanner_stop_scan (s); finish_exposure_buffer (s, &avg_white[0], &avg_white[1], &avg_white[2]); s->scanning = SANE_FALSE; XDBG ((1, "avg_r: %i, avg_g: %i, avg_b: %i\n", avg_white[0], avg_white[1], avg_white[2])); for (c = 0; c < 3; c++) { if (c == 0) { if (avg_white[c] < WHITE_MIN) { exp_off = ((WHITE_MAX + WHITE_MIN) / 2 - avg_white[c]) / EXPOSURE_STEP; if (exp_off < 1) exp_off = 1; s->dev->exp_params.r_time += exp_off; finish = 0; XDBG ((1, "adjust exposure r: ++\n")); } else if (avg_white[c] > WHITE_MAX) { exp_off = (avg_white[c] - (WHITE_MAX + WHITE_MIN) / 2) / EXPOSURE_STEP; if (exp_off < 1) exp_off = 1; s->dev->exp_params.r_time -= exp_off; finish = 0; XDBG ((1, "adjust exposure r: --\n")); } } else if (c == 1) { if (avg_white[c] < WHITE_MIN) { exp_off = ((WHITE_MAX + WHITE_MIN) / 2 - avg_white[c]) / EXPOSURE_STEP; if (exp_off < 1) exp_off = 1; s->dev->exp_params.g_time += exp_off; finish = 0; XDBG ((1, "adjust exposure g: ++\n")); } else if (avg_white[c] > WHITE_MAX) { exp_off = (avg_white[c] - (WHITE_MAX + WHITE_MIN) / 2) / EXPOSURE_STEP; if (exp_off < 1) exp_off = 1; s->dev->exp_params.g_time -= exp_off; finish = 0; XDBG ((1, "adjust exposure g: --\n")); } } else if (c == 2) { if (avg_white[c] < WHITE_MIN) { exp_off = ((WHITE_MAX + WHITE_MIN) / 2 - avg_white[c]) / EXPOSURE_STEP; if (exp_off < 1) exp_off = 1; s->dev->exp_params.b_time += exp_off; finish = 0; XDBG ((1, "adjust exposure b: ++\n")); } else if (avg_white[c] > WHITE_MAX) { exp_off = (avg_white[c] - (WHITE_MAX + WHITE_MIN) / 2) / EXPOSURE_STEP; if (exp_off < 1) exp_off = 1; s->dev->exp_params.b_time -= exp_off; finish = 0; XDBG ((1, "adjust exposure b: --\n")); } } } XDBG ((1, "time_r: %x, time_g: %x, time_b: %x\n", s->dev->exp_params.r_time, s->dev->exp_params.g_time, s->dev->exp_params.b_time)); XDBG ((1, "offset_r: %x, offset_g: %x, offset_b: %x\n", s->dev->afe_params.r_offset, s->dev->afe_params.g_offset, s->dev->afe_params.b_offset)); ++noloop; if (noloop > 10) break; } } XDBG ((1, "option redOffset 0x%x\n", s->dev->afe_params.r_offset)); XDBG ((1, "option greenOffset 0x%x\n", s->dev->afe_params.g_offset)); XDBG ((1, "option blueOffset 0x%x\n", s->dev->afe_params.b_offset)); XDBG ((1, "option redExposure 0x%x\n", s->dev->exp_params.r_time)); XDBG ((1, "option greenExposure 0x%x\n", s->dev->exp_params.g_time)); XDBG ((1, "option blueExposure 0x%x\n", s->dev->exp_params.b_time)); s->dev->artec_48u_afe_params.r_offset = s->dev->afe_params.r_offset; s->dev->artec_48u_afe_params.g_offset = s->dev->afe_params.g_offset; s->dev->artec_48u_afe_params.b_offset = s->dev->afe_params.b_offset; /*don't forget the gain */ s->dev->artec_48u_afe_params.r_pga = s->dev->afe_params.r_pga; s->dev->artec_48u_afe_params.g_pga = s->dev->afe_params.g_pga; s->dev->artec_48u_afe_params.b_pga = s->dev->afe_params.b_pga; s->dev->artec_48u_exposure_params.r_time = s->dev->exp_params.r_time; s->dev->artec_48u_exposure_params.g_time = s->dev->exp_params.g_time; s->dev->artec_48u_exposure_params.b_time = s->dev->exp_params.b_time; /******************************* *get the black shading values * *******************************/ artec48u_carriage_home (s->dev); artec48u_wait_for_positioning (s->dev); s->reader = NULL; s->scanning = SANE_TRUE; init_shading_buffer (s); artec48u_setup_scan (s, &(s->request), SA_CALIBRATE_SCAN_BLACK, SANE_FALSE, &(s->params)); artec48u_scanner_start_scan_extended (s, &(s->request), SA_CALIBRATE_SCAN_BLACK, &(s->params)); for (c = 0; c < s->dev->shading_lines_b; c++) { artec48u_scanner_read_line (s, buffer_pointers, SANE_FALSE); add_to_shading_buffer (s, buffer_pointers); } artec48u_scanner_stop_scan (s); finish_shading_buffer (s, SANE_FALSE); s->scanning = SANE_FALSE; /******************************* *get the white shading values * *******************************/ artec48u_carriage_home (s->dev); artec48u_wait_for_positioning (s->dev); s->reader = NULL; s->scanning = SANE_TRUE; init_shading_buffer (s); artec48u_setup_scan (s, &(s->request), SA_CALIBRATE_SCAN_WHITE, SANE_FALSE, &(s->params)); artec48u_scanner_start_scan_extended (s, &(s->request), SA_CALIBRATE_SCAN_WHITE, &(s->params)); for (c = 0; c < s->dev->shading_lines_w; c++) { artec48u_scanner_read_line (s, buffer_pointers, SANE_FALSE); add_to_shading_buffer (s, buffer_pointers); } artec48u_scanner_stop_scan (s); finish_shading_buffer (s, SANE_TRUE); s->scanning = SANE_FALSE; save_calibration_data (s); return SANE_STATUS_GOOD; } static SANE_Status close_pipe (Artec48U_Scanner * s) { if (s->pipe >= 0) { XDBG ((1, "close_pipe\n")); close (s->pipe); s->pipe = -1; } return SANE_STATUS_EOF; } static void sigalarm_handler (int __sane_unused__ signal) { XDBG ((1, "ALARM!!!\n")); cancelRead = SANE_TRUE; } static void sig_chldhandler (int signo) { XDBG ((1, "Child is down (signal=%d)\n", signo)); } static int reader_process (void * data) { Artec48U_Scanner * s = (Artec48U_Scanner *) data; int fd = s->reader_pipe; SANE_Status status; struct SIGACTION act; sigset_t ignore_set; ssize_t bytes_written = 0; XDBG ((1, "reader process...\n")); if (sanei_thread_is_forked()) close (s->pipe); sigfillset (&ignore_set); sigdelset (&ignore_set, SIGTERM); sigdelset (&ignore_set, SIGUSR1); #if defined (__APPLE__) && defined (__MACH__) sigdelset (&ignore_set, SIGUSR2); #endif sigprocmask (SIG_SETMASK, &ignore_set, 0); memset (&act, 0, sizeof (act)); sigaction (SIGTERM, &act, 0); sigaction (SIGUSR1, &act, 0); cancelRead = SANE_FALSE; if (sigemptyset (&(act.sa_mask)) < 0) XDBG ((2, "(child) reader_process: sigemptyset() failed\n")); act.sa_flags = 0; act.sa_handler = reader_process_sigterm_handler; if (sigaction (SIGTERM, &act, 0) < 0) XDBG ((2, "(child) reader_process: sigaction(SIGTERM,...) failed\n")); act.sa_handler = usb_reader_process_sigterm_handler; if (sigaction (SIGUSR1, &act, 0) < 0) XDBG ((2, "(child) reader_process: sigaction(SIGUSR1,...) failed\n")); XDBG ((2, "(child) reader_process: s=%p, fd=%d\n", (void *) s, fd)); /*read line by line into buffer */ /*copy buffer pointers to line_buffer */ XDBG ((2, "(child) reader_process: byte_cnt %d\n", (int) s->byte_cnt)); s->eof = SANE_FALSE; while (s->lines_to_read > 0) { if (cancelRead == SANE_TRUE) { XDBG ((2, "(child) reader_process: cancelRead == SANE_TRUE\n")); s->scanning = SANE_FALSE; s->eof = SANE_FALSE; return SANE_STATUS_CANCELLED; } if (s->scanning != SANE_TRUE) { XDBG ((2, "(child) reader_process: scanning != SANE_TRUE\n")); return SANE_STATUS_CANCELLED; } status = artec48u_scanner_read_line (s, s->buffer_pointers, SANE_TRUE); if (status != SANE_STATUS_GOOD) { XDBG ((2, "(child) reader_process: scanner_read_line failed\n")); return SANE_STATUS_IO_ERROR; } copy_scan_line (s); s->lines_to_read -= 1; bytes_written = write (fd, s->line_buffer, s->sane_params.bytes_per_line); if (bytes_written < 0) { XDBG ((2, "(child) reader_process: write returned %s\n", strerror (errno))); s->eof = SANE_FALSE; return SANE_STATUS_IO_ERROR; } XDBG ((2, "(child) reader_process: lines to read %i\n", s->lines_to_read)); } s->eof = SANE_TRUE; close (fd); return SANE_STATUS_GOOD; } static SANE_Status do_cancel (Artec48U_Scanner * s, SANE_Bool closepipe) { struct SIGACTION act; SANE_Pid res; XDBG ((1, "do_cancel\n")); s->scanning = SANE_FALSE; if (sanei_thread_is_valid (s->reader_pid)) { /*parent */ XDBG ((1, "killing reader_process\n")); /* tell the driver to stop scanning */ sigemptyset (&(act.sa_mask)); act.sa_flags = 0; act.sa_handler = sigalarm_handler; if (sigaction (SIGALRM, &act, 0) == -1) XDBG ((1, "sigaction() failed !\n")); /* kill our child process and wait until done */ alarm (10); if (sanei_thread_kill (s->reader_pid) < 0) XDBG ((1, "sanei_thread_kill() failed !\n")); res = sanei_thread_waitpid (s->reader_pid, 0); alarm (0); if (res != s->reader_pid) { XDBG ((1, "sanei_thread_waitpid() failed !\n")); } sanei_thread_invalidate (s->reader_pid); XDBG ((1, "reader_process killed\n")); } if (SANE_TRUE == closepipe) { close_pipe (s); XDBG ((1, "pipe closed\n")); } artec48u_scanner_stop_scan (s); artec48u_carriage_home (s->dev); if (s->line_buffer) { XDBG ((2, "freeing line_buffer\n")); free (s->line_buffer); s->line_buffer = NULL; } if (s->lineart_buffer) { XDBG ((2, "freeing lineart_buffer\n")); free (s->lineart_buffer); s->lineart_buffer = NULL; } return SANE_STATUS_CANCELLED; } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { static const SANE_Device **devlist = 0; Artec48U_Device *dev; SANE_Int dev_num; XDBG ((5, "sane_get_devices: start: local_only = %s\n", local_only == SANE_TRUE ? "true" : "false")); if (devlist) free (devlist); devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); if (!devlist) return SANE_STATUS_NO_MEM; dev_num = 0; for (dev = first_dev; dev_num < num_devices; dev = dev->next) { devlist[dev_num] = &dev->sane; XDBG ((3, "sane_get_devices: name %s\n", dev->sane.name)); XDBG ((3, "sane_get_devices: vendor %s\n", dev->sane.vendor)); XDBG ((3, "sane_get_devices: model %s\n", dev->sane.model)); ++dev_num; } devlist[dev_num] = 0; ++dev_num; *device_list = devlist; XDBG ((5, "sane_get_devices: exit\n")); return SANE_STATUS_GOOD; } static SANE_Status load_calibration_data (Artec48U_Scanner * s) { SANE_Status status = SANE_STATUS_GOOD; FILE *f = 0; size_t cnt; char path[PATH_MAX]; char filename[PATH_MAX]; s->calibrated = SANE_FALSE; path[0] = 0; /* return SANE_STATUS_INVAL if HOME environment variable is not set */ if (getenv ("HOME") == NULL) { XDBG ((1, "Environment variable HOME not set\n")); return SANE_STATUS_INVAL; } if (strlen (getenv ("HOME")) < (PATH_MAX - 1)) strcat (path, getenv ("HOME")); else return SANE_STATUS_INVAL; if (strlen (path) < (PATH_MAX - 1 - strlen ("/.artec_eplus48u/"))) strcat (path, "/.artec_eplus48u/"); else return SANE_STATUS_INVAL; /*try to load black shading file */ strcpy (filename, path); if (strlen (filename) < (PATH_MAX - 1 - strlen ("artec48ushading_black"))) strcat (filename, "artec48ushading_black"); else return SANE_STATUS_INVAL; XDBG ((1, "Try to read black shading file: \"%s\"\n", filename)); f = fopen (filename, "rb"); if (!f) return SANE_STATUS_INVAL; /*read values */ cnt = fread (s->shading_buffer_b, sizeof (unsigned char), 30720*s->dev->epro_mult, f); /*epro*/ if (cnt != (30720*s->dev->epro_mult)) /*epro*/ { fclose (f); XDBG ((1, "Could not load black shading file\n")); return SANE_STATUS_INVAL; } fclose (f); /*try to load white shading file */ strcpy (filename, path); if (strlen (filename) < (PATH_MAX - 1 - strlen ("artec48ushading_white"))) strcat (filename, "artec48ushading_white"); else return SANE_STATUS_INVAL; XDBG ((1, "Try to read white shading file: \"%s\"\n", filename)); f = fopen (filename, "rb"); if (!f) return SANE_STATUS_INVAL; /*read values */ cnt = fread (s->shading_buffer_w, sizeof (unsigned char), 30720*s->dev->epro_mult, f);/*epro*/ if (cnt != (30720*s->dev->epro_mult)) /*epro*/ { fclose (f); XDBG ((1, "Could not load white shading file\n")); return SANE_STATUS_INVAL; } fclose (f); /*try to load offset file */ strcpy (filename, path); if (strlen (filename) < (PATH_MAX - 1 - strlen ("artec48uoffset"))) strcat (filename, "artec48uoffset"); else return SANE_STATUS_INVAL; XDBG ((1, "Try to read offset file: \"%s\"\n", filename)); f = fopen (filename, "rb"); if (!f) return SANE_STATUS_INVAL; /*read values */ cnt = fread (&s->dev->artec_48u_afe_params, sizeof (Artec48U_AFE_Parameters), 1, f); if (cnt != 1) { fclose (f); XDBG ((1, "Could not load offset file\n")); return SANE_STATUS_INVAL; } fclose (f); /*load exposure file */ strcpy (filename, path); if (strlen (filename) < (PATH_MAX - 1 - strlen ("artec48uexposure"))) strcat (filename, "artec48uexposure"); else return SANE_STATUS_INVAL; XDBG ((1, "Try to read exposure file: \"%s\"\n", filename)); f = fopen (filename, "rb"); if (!f) return SANE_STATUS_INVAL; /*read values */ cnt = fread (&s->dev->artec_48u_exposure_params, sizeof (Artec48U_Exposure_Parameters), 1, f); if (cnt != 1) { fclose (f); XDBG ((1, "Could not load exposure file\n")); return SANE_STATUS_INVAL; } fclose (f); s->calibrated = SANE_TRUE; return status; } static SANE_Status save_calibration_data (Artec48U_Scanner * s) { SANE_Status status = SANE_STATUS_GOOD; FILE *f = 0; size_t cnt; char path[PATH_MAX]; char filename[PATH_MAX]; mode_t mode = S_IRUSR | S_IWUSR; path[0] = 0; /* return SANE_STATUS_INVAL if HOME environment variable is not set */ if (getenv ("HOME") == NULL) { XDBG ((1, "Environment variable HOME not set\n")); return SANE_STATUS_INVAL; } if (strlen (getenv ("HOME")) < (PATH_MAX - 1)) strcat (path, getenv ("HOME")); else return SANE_STATUS_INVAL; if (strlen (path) < (PATH_MAX - 1 - strlen ("/.artec_eplus48u/"))) strcat (path, "/.artec_eplus48u/"); else return SANE_STATUS_INVAL; /*try to save black shading file */ strcpy (filename, path); if (strlen (filename) < (PATH_MAX - 1 - strlen ("artec48ushading_black"))) strcat (filename, "artec48ushading_black"); else return SANE_STATUS_INVAL; XDBG ((1, "Try to save black shading file: \"%s\"\n", filename)); f = fopen (filename, "w"); if (!f) { XDBG ((1, "Could not save artec48ushading_black\n")); return SANE_STATUS_INVAL; } if (chmod (filename, mode) != 0) return SANE_STATUS_INVAL; /*read values */ cnt = fwrite (s->shading_buffer_b, sizeof (unsigned char), 30720*s->dev->epro_mult, f); /*epro*/ XDBG ((1, "Wrote %li bytes to black shading buffer \n", (u_long) cnt)); if (cnt != (30720*s->dev->epro_mult))/*epro*/ { fclose (f); XDBG ((1, "Could not write black shading buffer\n")); return SANE_STATUS_INVAL; } fclose (f); /*try to save white shading file */ strcpy (filename, path); if (strlen (filename) < (PATH_MAX - 1 - strlen ("artec48ushading_white"))) strcat (filename, "artec48ushading_white"); else return SANE_STATUS_INVAL; XDBG ((1, "Try to save white shading file: \"%s\"\n", filename)); f = fopen (filename, "w"); if (!f) return SANE_STATUS_INVAL; if (chmod (filename, mode) != 0) return SANE_STATUS_INVAL; /*read values */ cnt = fwrite (s->shading_buffer_w, sizeof (unsigned char), 30720*s->dev->epro_mult, f);/*epro*/ if (cnt != (30720*s->dev->epro_mult)) /*epro*/ { fclose (f); XDBG ((1, "Could not write white shading buffer\n")); return SANE_STATUS_INVAL; } fclose (f); /*try to save offset file */ strcpy (filename, path); if (strlen (filename) < (PATH_MAX - 1 - strlen ("artec48uoffset"))) strcat (filename, "artec48uoffset"); else return SANE_STATUS_INVAL; XDBG ((1, "Try to write offset file: \"%s\"\n", filename)); f = fopen (filename, "w"); if (!f) return SANE_STATUS_INVAL; if (chmod (filename, mode) != 0) return SANE_STATUS_INVAL; /*read values */ cnt = fwrite (&s->dev->artec_48u_afe_params, sizeof (Artec48U_AFE_Parameters), 1, f); if (cnt != 1) { fclose (f); XDBG ((1, "Could not write afe values\n")); return SANE_STATUS_INVAL; } fclose (f); /*try to write exposure file */ strcpy (filename, path); if (strlen (filename) < (PATH_MAX - 1 - strlen ("artec48uexposure"))) strcat (filename, "artec48uexposure"); else return SANE_STATUS_INVAL; XDBG ((1, "Try to write exposure file: \"%s\"\n", filename)); f = fopen (filename, "w"); if (!f) return SANE_STATUS_INVAL; if (chmod (filename, mode) != 0) return SANE_STATUS_INVAL; /*read values */ cnt = fwrite (&s->dev->artec_48u_exposure_params, sizeof (Artec48U_Exposure_Parameters), 1, f); if (cnt != 1) { fclose (f); XDBG ((1, "Could not write exposure values\n")); return SANE_STATUS_INVAL; } fclose (f); return status; } SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { SANE_Status status = SANE_STATUS_INVAL; Artec48U_Device *dev = 0; Artec48U_Scanner *s = 0; if (!devicename) return SANE_STATUS_INVAL; XDBG ((2, "sane_open: devicename = \"%s\"\n", devicename)); if (devicename[0]) { for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->sane.name, devicename) == 0) { XDBG ((2, "sane_open: found matching device %s\n", dev->sane.name)); break; } } if (!dev) { status = attach (devicename, &dev); if (status != SANE_STATUS_GOOD) XDBG ((2, "sane_open: attach failed %s\n", devicename)); } } else { /* empty devicename -> use first device */ XDBG ((2, "sane_open: empty devicename\n")); dev = first_dev; } if (!dev) return SANE_STATUS_INVAL; status = artec48u_device_open (dev); if (status != SANE_STATUS_GOOD) { XDBG ((3, "could not open device\n")); return status; } XDBG ((2, "sane_open: opening device `%s', handle = %p\n", dev->sane.name, (void *) dev)); XDBG ((1, "sane_open - %s\n", dev->sane.name)); XDBG ((2, "sane_open: try to open %s\n", dev->sane.name)); status = artec48u_device_activate (dev); if (status != SANE_STATUS_GOOD) { XDBG ((3, "could not activate device\n")); return status; } /* We do not check anymore, whether the firmware is already loaded */ /* because that caused problems after rebooting; furthermore, loading */ /* of the firmware is fast, therefore the test doesn't make much sense */ status = download_firmware_file (dev); if (status != SANE_STATUS_GOOD) { XDBG ((3, "download_firmware_file failed\n")); return status; } /* If a scan is interrupted without sending stop_scan, bad things happen. * Send the stop scan command now just in case. */ artec48u_stop_scan (dev); artec48u_wait_for_positioning (dev); artec48u_scanner_new (dev, &s); init_calibrator (s); s->next = first_handle; first_handle = s; *handle = s; status = init_options (s); if (status != SANE_STATUS_GOOD) return status; /*Try to load the calibration values */ status = load_calibration_data (s); return SANE_STATUS_GOOD; } void sane_close (SANE_Handle handle) { Artec48U_Scanner *s; XDBG ((5, "sane_close: start\n")); /* remove handle from list of open handles: */ for (s = first_handle; s; s = s->next) { if (s == handle) break; } if (!s) { XDBG ((5, "close: invalid handle %p\n", handle)); return; } artec48u_device_close (s->dev); artec48u_scanner_free (s); XDBG ((5, "sane_close: exit\n")); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { Artec48U_Scanner *s = handle; if ((unsigned) option >= NUM_OPTIONS) return 0; XDBG ((5, "sane_get_option_descriptor: option = %s (%d)\n", s->opt[option].name, option)); return s->opt + option; } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int * info) { Artec48U_Scanner *s = handle; #ifdef ARTEC48U_USE_BUTTONS SANE_Int button_state; #endif SANE_Status status; XDBG ((8, "sane_control_option: handle=%p, opt=%d, act=%d, val=%p, info=%p\n", (void *) handle, option, action, (void *) value, (void *) info)); if (info) *info = 0; if (option < 0 || option >= NUM_OPTIONS) return SANE_STATUS_INVAL; /* Unknown option ... */ if (!SANE_OPTION_IS_ACTIVE (s->opt[option].cap)) return SANE_STATUS_INVAL; switch (action) { case SANE_ACTION_SET_VALUE: if (s->scanning == SANE_TRUE) return SANE_STATUS_INVAL; if (!SANE_OPTION_IS_SETTABLE (s->opt[option].cap)) return SANE_STATUS_INVAL; status = sanei_constrain_value (s->opt + option, value, info); if (status != SANE_STATUS_GOOD) return status; switch (option) { case OPT_RESOLUTION: if(s->dev->is_epro != 0) { if((s->val[option].w == 1200) && (*(SANE_Word *) value < 1200)) { s->opt[OPT_BIT_DEPTH].constraint.word_list = bitdepth_list; *info |= SANE_INFO_RELOAD_OPTIONS; } else if((s->val[option].w < 1200) && (*(SANE_Word *) value == 1200)) { s->opt[OPT_BIT_DEPTH].constraint.word_list = bitdepth_list2; if(s->val[OPT_BIT_DEPTH].w > 8) s->val[OPT_BIT_DEPTH].w = 8; *info |= SANE_INFO_RELOAD_OPTIONS; } } s->val[option].w = *(SANE_Word *) value; if (info) { *info |= SANE_INFO_RELOAD_PARAMS; } break; /* fall through */ case OPT_BIT_DEPTH: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: s->val[option].w = *(SANE_Word *) value; if (info) *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; /* fall through */ case OPT_BLACK_LEVEL: case OPT_BRIGHTNESS: case OPT_CONTRAST: case OPT_GAMMA: case OPT_GAMMA_R: case OPT_GAMMA_G: case OPT_GAMMA_B: case OPT_CALIBRATE: case OPT_CALIBRATE_SHADING: s->val[option].w = *(SANE_Word *) value; return SANE_STATUS_GOOD; case OPT_DEFAULT_ENHANCEMENTS: s->val[OPT_GAMMA].w = SANE_FIX (s->dev->gamma_master); if (strcmp (s->val[OPT_SCAN_MODE].s, mode_list[2]) == 0) { s->val[OPT_GAMMA_R].w = SANE_FIX (s->dev->gamma_r); s->val[OPT_GAMMA_G].w = SANE_FIX (s->dev->gamma_g); s->val[OPT_GAMMA_B].w = SANE_FIX (s->dev->gamma_b); } s->val[OPT_BRIGHTNESS].w = 0; s->val[OPT_CONTRAST].w = 0; if (info) *info |= SANE_INFO_RELOAD_OPTIONS; break; case OPT_SCAN_MODE: if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (value); if (strcmp (s->val[OPT_SCAN_MODE].s, mode_list[0]) == 0) { s->opt[OPT_GAMMA_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_B].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BLACK_LEVEL].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; } else if (strcmp (s->val[OPT_SCAN_MODE].s, mode_list[1]) == 0) { s->opt[OPT_GAMMA_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_B].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BLACK_LEVEL].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE; } else { s->opt[OPT_GAMMA_R].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_G].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_B].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_BLACK_LEVEL].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE; } if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; } break; case SANE_ACTION_GET_VALUE: switch (option) { /* word options: */ case OPT_NUM_OPTS: case OPT_RESOLUTION: case OPT_BIT_DEPTH: case OPT_BLACK_LEVEL: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_BRIGHTNESS: case OPT_CONTRAST: case OPT_GAMMA: case OPT_GAMMA_R: case OPT_GAMMA_G: case OPT_GAMMA_B: case OPT_CALIBRATE: case OPT_CALIBRATE_SHADING: *(SANE_Word *) value = (SANE_Word) s->val[option].w; return SANE_STATUS_GOOD; /* string options: */ case OPT_SCAN_MODE: strcpy (value, s->val[option].s); return SANE_STATUS_GOOD; #ifdef ARTEC48U_USE_BUTTONS case OPT_BUTTON_STATE: status = artec48u_check_buttons (s->dev, &button_state); if (status == SANE_STATUS_GOOD) { s->val[option].w = button_state; *(SANE_Int *) value = (SANE_Int) s->val[option].w; } else { s->val[option].w = 0; *(SANE_Int *) value = 0; } return SANE_STATUS_GOOD; #endif } break; default: return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { Artec48U_Scanner *s = handle; SANE_Status status; SANE_Word resx; /* int scan_mode;*/ SANE_String str = s->val[OPT_SCAN_MODE].s; int tlx; int tly; int brx; int bry; int tmp; XDBG ((2, "sane_get_params: string %s\n", str)); XDBG ((2, "sane_get_params: enter\n")); tlx = s->val[OPT_TL_X].w; tly = s->val[OPT_TL_Y].w; brx = s->val[OPT_BR_X].w; bry = s->val[OPT_BR_Y].w; /*make sure, that tlx < brx and tly < bry this will NOT change the options */ if (tlx > brx) { tmp = tlx; tlx = brx; brx = tmp; } if (tly > bry) { tmp = tly; tly = bry; bry = tmp; } resx = s->val[OPT_RESOLUTION].w; str = s->val[OPT_SCAN_MODE].s; s->request.color = SANE_TRUE; if ((strcmp (str, mode_list[0]) == 0) || (strcmp (str, mode_list[1]) == 0)) s->request.color = SANE_FALSE; else s->request.color = SANE_TRUE; s->request.depth = s->val[OPT_BIT_DEPTH].w; if (strcmp (str, mode_list[0]) == 0) s->request.depth = 8; s->request.y0 = tly; /**< Top boundary */ s->request.x0 = SANE_FIX (216.0) - brx; /**< left boundary */ s->request.xs = brx - tlx; /**< Width */ s->request.ys = bry - tly; /**< Height */ s->request.xdpi = resx; /**< Horizontal resolution */ s->request.ydpi = resx; /**< Vertical resolution */ /*epro*/ if ((resx == 1200) && (s->dev->is_epro == 0)) s->request.xdpi = 600;/**< Vertical resolution */ status = artec48u_setup_scan (s, &(s->request), SA_SCAN, SANE_TRUE, &(s->params)); if (status != SANE_STATUS_GOOD) return SANE_STATUS_INVAL; /*DBG(1, "sane_get_params: scan_mode %i\n",scan_mode);*/ params->depth = s->params.depth; s->params.lineart = SANE_FALSE; if (s->params.color == SANE_TRUE) { params->format = SANE_FRAME_RGB; params->bytes_per_line = s->params.pixel_xs * 3; } else { params->format = SANE_FRAME_GRAY; params->bytes_per_line = s->params.pixel_xs; if (strcmp (str, mode_list[0]) == 0) { params->depth = 1; params->bytes_per_line = (s->params.pixel_xs + 7) / 8; s->params.lineart = SANE_TRUE; } } if ((resx == 1200) && (s->dev->is_epro == 0)) { if (params->depth == 1) params->bytes_per_line = (s->params.pixel_xs * 2 + 7) / 8; else params->bytes_per_line *= 2; } if (params->depth == 16) params->bytes_per_line *= 2; params->last_frame = SANE_TRUE; params->pixels_per_line = s->params.pixel_xs; if ((resx == 1200) && (s->dev->is_epro == 0)) params->pixels_per_line *= 2; params->lines = s->params.pixel_ys; return SANE_STATUS_GOOD; } SANE_Status sane_start (SANE_Handle handle) { Artec48U_Scanner *s = handle; SANE_Status status; int fds[2]; if (s->scanning) { return SANE_STATUS_DEVICE_BUSY; } if (sane_get_parameters (handle, &s->sane_params) != SANE_STATUS_GOOD) return SANE_STATUS_INVAL; if ((s->calibrated != SANE_TRUE) || (s->val[OPT_CALIBRATE].w == SANE_TRUE)) { XDBG ((1, "Must calibrate scanner\n")); status = calibrate_scanner (s); if (status != SANE_STATUS_GOOD) return status; s->calibrated = SANE_TRUE; } if (sane_get_parameters (handle, &s->sane_params) != SANE_STATUS_GOOD) return SANE_STATUS_INVAL; calculate_brightness (s); calculate_contrast (s); calculateGamma (s); calculateGammaRed (s); calculateGammaGreen (s); calculateGammaBlue (s); artec48u_carriage_home (s->dev); artec48u_wait_for_positioning (s->dev); s->reader = NULL; s->scanning = SANE_TRUE; s->byte_cnt = 0; s->lines_to_read = s->params.pixel_ys; /*allocate a buffer, that can hold a complete scan line */ /*If resolution is 1200 dpi and we are scanning in lineart mode, then we also allocate a lineart_buffer, which can hold a complete scan line in 8 bit/gray. This makes interpolation easier. */ if ((s->params.ydpi == 1200) && (s->dev->is_epro == 0)) { if (s->request.color == SANE_TRUE) { s->line_buffer = (SANE_Byte *) malloc (s->params.scan_bpl * 8); } else { s->line_buffer = (SANE_Byte *) malloc (s->params.scan_bpl * 4); /*lineart ? */ if (strcmp (s->val[OPT_SCAN_MODE].s, mode_list[0]) == 0) s->lineart_buffer = (SANE_Byte *) malloc (s->params.pixel_xs * 2); } } else { if (s->request.color == SANE_TRUE) s->line_buffer = (SANE_Byte *) malloc (s->params.scan_bpl * 4); else { s->line_buffer = (SANE_Byte *) malloc (s->params.scan_bpl * 2); /*lineart ? */ if (strcmp (s->val[OPT_SCAN_MODE].s, mode_list[0]) == 0) s->lineart_buffer = (SANE_Byte *) malloc (s->params.pixel_xs * 2); } } if (pipe (fds) < 0) { s->scanning = SANE_FALSE; XDBG ((2, "sane_start: pipe failed (%s)\n", strerror (errno))); return SANE_STATUS_IO_ERROR; } status = artec48u_scanner_start_scan (s, &s->request, &s->params); if (status != SANE_STATUS_GOOD) { XDBG ((2, "sane_start: could not start scan\n")); return status; } s->pipe = fds[0]; s->reader_pipe = fds[1]; s->reader_pid = sanei_thread_begin (reader_process, s); cancelRead = SANE_FALSE; if (!sanei_thread_is_valid (s->reader_pid)) { s->scanning = SANE_FALSE; XDBG ((2, "sane_start: sanei_thread_begin failed (%s)\n", strerror (errno))); return SANE_STATUS_NO_MEM; } signal (SIGCHLD, sig_chldhandler); if (sanei_thread_is_forked()) close (s->reader_pipe); XDBG ((1, "sane_start done\n")); return SANE_STATUS_GOOD; /* parent */ } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * data, SANE_Int max_length, SANE_Int * length) { Artec48U_Scanner *s = handle; ssize_t nread; *length = 0; /* here we read all data from the driver... */ nread = read (s->pipe, data, max_length); XDBG ((3, "sane_read - read %ld bytes\n", (long) nread)); if (cancelRead == SANE_TRUE) { return do_cancel (s, SANE_TRUE); } if (nread < 0) { if (EAGAIN == errno) { /* if we already had read the picture, so it's okay and stop */ if (s->eof == SANE_TRUE) { sanei_thread_waitpid (s->reader_pid, 0); sanei_thread_invalidate (s->reader_pid); artec48u_scanner_stop_scan (s); artec48u_carriage_home (s->dev); return close_pipe (s); } /* else force the frontend to try again */ return SANE_STATUS_GOOD; } else { XDBG ((4, "ERROR: errno=%d\n", errno)); do_cancel (s, SANE_TRUE); return SANE_STATUS_IO_ERROR; } } *length = nread; s->byte_cnt += nread; /* nothing read means that we're finished OR we had a problem... */ if (0 == nread) { if (0 == s->byte_cnt) { s->exit_code = sanei_thread_get_status (s->reader_pid); if (SANE_STATUS_GOOD != s->exit_code) { close_pipe (s); return s->exit_code; } } return close_pipe (s); } return SANE_STATUS_GOOD; } void sane_cancel (SANE_Handle handle) { Artec48U_Scanner *s = handle; XDBG ((2, "sane_cancel: handle = %p\n", handle)); if (s->scanning) do_cancel (s, SANE_FALSE); } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { Artec48U_Scanner *s = (Artec48U_Scanner *) handle; XDBG ((1, "sane_set_io_mode: non_blocking=%d\n", non_blocking)); if (!s->scanning) { XDBG ((4, "ERROR: not scanning !\n")); return SANE_STATUS_INVAL; } if (-1 == s->pipe) { XDBG ((4, "ERROR: not supported !\n")); return SANE_STATUS_UNSUPPORTED; } if (fcntl (s->pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0) { XDBG ((4, "ERROR: can?t set to non-blocking mode !\n")); return SANE_STATUS_IO_ERROR; } XDBG ((1, "sane_set_io_mode done\n")); return SANE_STATUS_GOOD; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { Artec48U_Scanner *s = (Artec48U_Scanner *) handle; XDBG ((1, "sane_get_select_fd\n")); if (!s->scanning) { XDBG ((4, "ERROR: not scanning !\n")); return SANE_STATUS_INVAL; } *fd = s->pipe; XDBG ((1, "sane_get_select_fd done\n")); return SANE_STATUS_GOOD; } SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { Artec48U_Device *device = 0; SANE_Status status; char str[PATH_MAX] = _DEFAULT_DEVICE; char temp[PATH_MAX]; size_t len; FILE *fp; double gamma_m = 1.9; double gamma_r = 1.0; double gamma_g = 1.0; double gamma_b = 1.0; int epro_default = 0; DBG_INIT (); eProMult = 1; isEPro = 0; temp[0] = 0; strcpy (vendor_string, "Artec"); strcpy (model_string, "E+ 48U"); sanei_usb_init (); sanei_thread_init (); /* do some presettings... */ auth = authorize; if (version_code != NULL) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); fp = sanei_config_open (ARTEC48U_CONFIG_FILE); /* default to _DEFAULT_DEVICE instead of insisting on config file */ if (NULL == fp) { status = attach (_DEFAULT_DEVICE, &device); return status; } while (sanei_config_read (str, sizeof (str), fp)) { XDBG ((1, "sane_init, >%s<\n", str)); /* ignore line comments */ if (str[0] == '#') continue; len = strlen (str); /* ignore empty lines */ if (0 == len) continue; /* check for options */ if (0 == strncmp (str, "option", 6)) { if(decodeVal (str,"ePlusPro",_INT, &isEPro,&epro_default) == SANE_TRUE) { eProMult = 1; if(isEPro != 0) { eProMult = 2; XDBG ((3, "Is Artec E Pro\n")); } else XDBG ((3, "Is Artec E+ 48U\n")); } decodeVal (str, "masterGamma", _FLOAT, &gamma_master_default, &gamma_m); decodeVal (str, "redGamma", _FLOAT, &gamma_r_default, &gamma_r); decodeVal (str, "greenGamma", _FLOAT, &gamma_g_default, &gamma_g); decodeVal (str, "blueGamma", _FLOAT, &gamma_b_default, &gamma_b); decodeVal (str, "redOffset", _BYTE, &afe_params.r_offset, &default_afe_params.r_offset); decodeVal (str, "greenOffset", _BYTE, &afe_params.g_offset, &default_afe_params.g_offset); decodeVal (str, "blueOffset", _BYTE, &afe_params.b_offset, &default_afe_params.b_offset); decodeVal (str, "redExposure", _INT, &exp_params.r_time, &default_exp_params.r_time); decodeVal (str, "greenExposure", _INT, &exp_params.g_time, &default_exp_params.g_time); decodeVal (str, "blueExposure", _INT, &exp_params.b_time, &default_exp_params.b_time); decodeVal (str, "modelString", _STRING, model_string, model_string); decodeVal (str, "vendorString", _STRING, vendor_string, vendor_string); decodeVal (str, "artecFirmwareFile", _STRING, firmwarePath, firmwarePath); } else if (0 == strncmp (str, "usb", 3)) { if (temp[0] != 0) { XDBG ((3, "trying to attach: %s\n", temp)); XDBG ((3, " vendor: %s\n", vendor_string)); XDBG ((3, " model: %s\n", model_string)); sanei_usb_attach_matching_devices (temp, attach_one_device); } /*save config line in temp */ strcpy (temp, str); } else if (0 == strncmp (str, "device", 6)) { if (SANE_TRUE == decodeDevName (str, devName)) { if (devName[0] != 0) sanei_usb_attach_matching_devices (devName, attach_one_device); temp[0] = 0; } } else { /* ignore other stuff... */ XDBG ((1, "ignoring >%s<\n", str)); } } if (temp[0] != 0) { XDBG ((3, "trying to attach: %s\n", temp)); XDBG ((3, " vendor: %s\n", vendor_string)); XDBG ((3, " model: %s\n", model_string)); sanei_usb_attach_matching_devices (temp, attach_one_device); temp[0] = 0; } fclose (fp); return SANE_STATUS_GOOD; } void sane_exit (void) { Artec48U_Device *dev, *next; XDBG ((5, "sane_exit: start\n")); for (dev = first_dev; dev; dev = next) { next = dev->next; /*function will check, whether device is really open */ artec48u_device_close (dev); artec48u_device_free (dev); } XDBG ((5, "sane_exit: exit\n")); return; } backends-1.3.0/backend/artec_eplus48u.conf.in000066400000000000000000000100651456256263500210170ustar00rootroot00000000000000# 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" option ePlusPro 0 # ---------------------------------------------------------------------------- # 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" backends-1.3.0/backend/artec_eplus48u.h000066400000000000000000000454401456256263500177210ustar00rootroot00000000000000#ifndef ARTEC48U_H #define ARTEC48U_H #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #include #ifdef HAVE_SYS_IPC_H #include #endif #include #include #include #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_thread.h" #define _MAX_ID_LEN 20 /*Uncomment next line for button support. This actually isn't supported by the frontends. */ /*#define ARTEC48U_USE_BUTTONS 1*/ #define ARTEC48U_PACKET_SIZE 64 #define DECLARE_FUNCTION_NAME(name) \ IF_DBG ( static const char function_name[] = name; ) typedef SANE_Byte Artec48U_Packet[ARTEC48U_PACKET_SIZE]; #define XDBG(args) do { IF_DBG ( DBG args ); } while (0) /* calculate the minimum/maximum values */ #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MAX(a, b) (((a) > (b)) ? (a) : (b)) /* return the lower/upper 8 bits of a 16 bit word */ #define HIBYTE(w) ((SANE_Byte)(((SANE_Word)(w) >> 8) & 0xFF)) #define LOBYTE(w) ((SANE_Byte)(w)) #define CHECK_DEV_NOT_NULL(dev, func_name) \ do { \ if (!(dev)) \ { \ XDBG ((3, "%s: BUG: NULL device\n", (func_name))); \ return SANE_STATUS_INVAL; \ } \ } while (SANE_FALSE) /** Check that the device is open. * * @param dev Pointer to the device object (Artec48U_Device). * @param func_name Function name (for use in debug messages). */ #define CHECK_DEV_OPEN(dev, func_name) \ do { \ CHECK_DEV_NOT_NULL ((dev), (func_name)); \ if ((dev)->fd == -1) \ { \ XDBG ((3, "%s: BUG: device %p not open\n", (func_name), (void*)(dev)));\ return SANE_STATUS_INVAL; \ } \ } while (SANE_FALSE) #define CHECK_DEV_ACTIVE(dev,func_name) \ do { \ CHECK_DEV_OPEN ((dev), (func_name)); \ if (!(dev)->active) \ { \ XDBG ((3, "%s: BUG: device %p not active\n", \ (func_name), (void*)(dev))); \ return SANE_STATUS_INVAL; \ } \ } while (SANE_FALSE) typedef struct Artec48U_Device Artec48U_Device; typedef struct Artec48U_Scan_Request Artec48U_Scan_Request; typedef struct Artec48U_Scanner Artec48U_Scanner; typedef struct Artec48U_Scan_Parameters Artec48U_Scan_Parameters; typedef struct Artec48U_AFE_Parameters Artec48U_AFE_Parameters; typedef struct Artec48U_Exposure_Parameters Artec48U_Exposure_Parameters; typedef struct Artec48U_Line_Reader Artec48U_Line_Reader; typedef struct Artec48U_Delay_Buffer Artec48U_Delay_Buffer; enum artec_options { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_SCAN_MODE, OPT_BIT_DEPTH, OPT_BLACK_LEVEL, OPT_RESOLUTION, OPT_ENHANCEMENT_GROUP, OPT_BRIGHTNESS, OPT_CONTRAST, OPT_GAMMA, OPT_GAMMA_R, OPT_GAMMA_G, OPT_GAMMA_B, OPT_DEFAULT_ENHANCEMENTS, OPT_GEOMETRY_GROUP, OPT_TL_X, OPT_TL_Y, OPT_BR_X, OPT_BR_Y, OPT_CALIBRATION_GROUP, OPT_CALIBRATE, OPT_CALIBRATE_SHADING, #ifdef ARTEC48U_USE_BUTTONS OPT_BUTTON_STATE, #endif /* must come last: */ NUM_OPTIONS }; /** Artec48U analog front-end (AFE) parameters. */ struct Artec48U_AFE_Parameters { SANE_Byte r_offset; /**< Red channel offset */ SANE_Byte r_pga; /**< Red channel PGA gain */ SANE_Byte g_offset; /**< Green channel offset (also used for mono) */ SANE_Byte g_pga; /**< Green channel PGA gain (also used for mono) */ SANE_Byte b_offset; /**< Blue channel offset */ SANE_Byte b_pga; /**< Blue channel PGA gain */ }; /** TV9693 exposure time parameters. */ struct Artec48U_Exposure_Parameters { SANE_Int r_time; /**< Red exposure time */ SANE_Int g_time; /**< Red exposure time */ SANE_Int b_time; /**< Red exposure time */ }; struct Artec48U_Device { Artec48U_Device *next; /** Device file descriptor. */ int fd; /** Device activation flag. */ SANE_Bool active; SANE_String_Const name; SANE_Device sane; /** Scanner model data. */ SANE_String_Const firmware_path; double gamma_master; double gamma_r; double gamma_g; double gamma_b; Artec48U_Exposure_Parameters exp_params; Artec48U_AFE_Parameters afe_params; Artec48U_AFE_Parameters artec_48u_afe_params; Artec48U_Exposure_Parameters artec_48u_exposure_params; SANE_Int optical_xdpi; SANE_Int optical_ydpi; SANE_Int base_ydpi; SANE_Int xdpi_offset; /* in optical_xdpi units */ SANE_Int ydpi_offset; /* in optical_ydpi units */ SANE_Int x_size; /* in optical_xdpi units */ SANE_Int y_size; /* in optical_ydpi units */ /* the number of lines, that we move forward before we start reading the shading lines */ int shading_offset; /* the number of lines we read for the black shading buffer */ int shading_lines_b; /* the number of lines we read for the white shading buffer */ int shading_lines_w; SANE_Fixed x_offset, y_offset; SANE_Bool read_active; SANE_Byte *read_buffer; size_t requested_buffer_size; size_t read_pos; size_t read_bytes_in_buffer; size_t read_bytes_left; unsigned int is_epro; unsigned int epro_mult; }; /** Scan parameters for artec48u_device_setup_scan(). * * These parameters describe a low-level scan request; many such requests are * executed during calibration, and they need to have parameters separate from * the main request (Artec48U_Scan_Request). E.g., on the BearPaw 2400 TA the * scan to find the home position is always done at 300dpi 8-bit mono with * fixed width and height, regardless of the high-level scan parameters. */ struct Artec48U_Scan_Parameters { SANE_Int xdpi; /**< Horizontal resolution */ SANE_Int ydpi; /**< Vertical resolution */ SANE_Int depth; /**< Number of bits per channel */ SANE_Bool color; /**< Color mode flag */ SANE_Int pixel_xs; /**< Logical width in pixels */ SANE_Int pixel_ys; /**< Logical height in pixels */ SANE_Int scan_xs; /**< Physical width in pixels */ SANE_Int scan_ys; /**< Physical height in pixels */ SANE_Int scan_bpl; /**< Number of bytes per scan line */ SANE_Bool lineart; /**. 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 backend for the Artec AS6E by making a bridge to the as6edriver program. The as6edriver program can be found at http://as6edriver.sourceforge.net . */ #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/saneopts.h" #define BACKENDNAME as6e #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_config.h" #include "as6e.h" static int num_devices; static AS6E_Device *first_dev; static AS6E_Scan *first_handle; static const SANE_Device **devlist = 0; static SANE_Status attach (const char *devname, AS6E_Device ** devp); /* static SANE_Status attach_one (const char *dev); */ static const SANE_String_Const mode_list[] = { SANE_VALUE_SCAN_MODE_LINEART, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_COLOR, 0 }; static const SANE_Word resolution_list[] = { 4, 300, 200, 100, 50 }; static const SANE_Range x_range = { SANE_FIX (0), SANE_FIX (215.91), SANE_FIX (0) }; static const SANE_Range y_range = { SANE_FIX (0), SANE_FIX (297.19), SANE_FIX (0) }; static const SANE_Range brightness_range = { -100, 100, 1 }; static const SANE_Range contrast_range = { -100, 100, 1 }; /*--------------------------------------------------------------------------*/ static SANE_Int as6e_unit_convert (SANE_Fixed value) { double precise; SANE_Int return_value; precise = SANE_UNFIX (value); precise = (precise * 300) / MM_PER_INCH; return_value = precise; return return_value; } /*--------------------------------------------------------------------------*/ SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { AS6E_Scan *s = handle; SANE_Word buffer_offset = 0; int written = 0, bytes_read = 0, maxbytes; SANE_Word bytecounter, linebufcounter, ctlbytes; SANE_Byte *linebuffer; DBG (3, "reading %d bytes, %d bytes in carryover buffer\n", max_len, s->scan_buffer_count); if ((unsigned int) s->image_counter >= s->bytes_to_read) { *len = 0; if (s->scanning) { read (s->as6e_params.ctlinpipe, &written, sizeof (written)); if (written != -1) DBG (3, "pipe error\n"); DBG (3, "trying to read -1 ...written = %d\n", written); } s->scanning = SANE_FALSE; DBG (1, "image data complete, sending EOF...\n"); return SANE_STATUS_EOF; } /*image complete */ linebuffer = s->line_buffer; if (s->scan_buffer_count > 0) { /*there are leftover bytes from the last call */ if (s->scan_buffer_count <= max_len) { for (*len = 0; *len < s->scan_buffer_count; (*len)++) { buf[*len] = s->scan_buffer[*len]; buffer_offset++; } s->scan_buffer_count = 0; if (s->scan_buffer_count == max_len) { s->scan_buffer_count = 0; s->image_counter += max_len; DBG (3, "returning %d bytes from the carryover buffer\n", *len); return SANE_STATUS_GOOD; } } else { for (*len = 0; *len < max_len; (*len)++) buf[*len] = s->scan_buffer[*len]; for (bytecounter = max_len; bytecounter < s->scan_buffer_count; bytecounter++) s->scan_buffer[bytecounter - max_len] = s->scan_buffer[bytecounter]; s->scan_buffer_count -= max_len; s->image_counter += max_len; DBG (3, "returning %d bytes from the carryover buffer\n", *len); return SANE_STATUS_GOOD; } } else { *len = 0; /*no bytes in the buffer */ if (!s->scanning) { DBG (1, "scan over returning %d\n", *len); if (s->scan_buffer_count) return SANE_STATUS_GOOD; else return SANE_STATUS_EOF; } } while (*len < max_len) { DBG (3, "trying to read number of bytes...\n"); ctlbytes = read (s->as6e_params.ctlinpipe, &written, sizeof (written)); DBG (3, "bytes written = %d, ctlbytes =%d\n", written, ctlbytes); fflush (stdout); if ((s->cancelled) && (written == 0)) { /*first clear -1 from pipe */ DBG (1, "sending SANE_STATUS_CANCELLED\n"); read (s->as6e_params.ctlinpipe, &written, sizeof (written)); s->scanning = SANE_FALSE; return SANE_STATUS_CANCELLED; } if (written == -1) { DBG (1, "-1READ Scanner through. returning %d bytes\n", *len); s->image_counter += *len; s->scanning = SANE_FALSE; return SANE_STATUS_GOOD; } linebufcounter = 0; DBG (3, "linebufctr reset, len =%d written =%d bytes_read =%d, max = %d\n", *len, written, bytes_read, max_len); maxbytes = written; while (linebufcounter < written) { DBG (4, "trying to read data pipe\n"); bytes_read = read (s->as6e_params.datapipe, linebuffer + linebufcounter, maxbytes); linebufcounter += bytes_read; maxbytes -= bytes_read; DBG (3, "bytes_read = %d linebufcounter = %d\n", bytes_read, linebufcounter); } DBG (3, "written =%d max_len =%d len =%d\n", written, max_len, *len); if (written <= (max_len - *len)) { for (bytecounter = 0; bytecounter < written; bytecounter++) { buf[bytecounter + buffer_offset] = linebuffer[bytecounter]; (*len)++; } buffer_offset += written; DBG (3, "buffer offset = %d\n", buffer_offset); } else if (max_len > *len) { /*there's still room to send data */ for (bytecounter = 0; bytecounter < (max_len - *len); bytecounter++) buf[bytecounter + buffer_offset] = linebuffer[bytecounter]; DBG (3, "topping off buffer\n"); for (bytecounter = (max_len - *len); bytecounter < written; bytecounter++) { s->scan_buffer[s->scan_buffer_count + bytecounter - (max_len - *len)] = linebuffer[bytecounter]; } s->scan_buffer_count += (written - (max_len - *len)); *len = max_len; } else { /*everything goes into the carryover buffer */ for (bytecounter = 0; bytecounter < written; bytecounter++) s->scan_buffer[s->scan_buffer_count + bytecounter] = linebuffer[bytecounter]; s->scan_buffer_count += written; } } /*while there's space in the buffer */ s->image_counter += *len; DBG (3, "image ctr = %d bytes_to_read = %lu returning %d\n", s->image_counter, (u_long) s->bytes_to_read, *len); return SANE_STATUS_GOOD; } /*--------------------------------------------------------------------------*/ void sane_cancel (SANE_Handle h) { AS6E_Scan *s = h; SANE_Word test; DBG (2, "trying to cancel...\n"); if (s->scanning) { test = kill (s->child_pid, SIGUSR1); if (test == 0) s->cancelled = SANE_TRUE; } } /*--------------------------------------------------------------------------*/ SANE_Status sane_start (SANE_Handle handle) { AS6E_Scan *s = handle; SANE_Status status; int repeat = 1; SANE_Word numbytes; int scan_params[8]; /* First make sure we have a current parameter set. Some of the * parameters will be overwritten below, but that's OK. */ DBG (2, "sane_start\n"); status = sane_get_parameters (s, 0); if (status != SANE_STATUS_GOOD) return status; DBG (1, "Got params again...\n"); numbytes = write (s->as6e_params.ctloutpipe, &repeat, sizeof (repeat)); if (numbytes != sizeof (repeat)) return (SANE_STATUS_IO_ERROR); DBG (1, "sending start_scan signal\n"); scan_params[0] = s->as6e_params.resolution; if (strcmp (s->value[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0) scan_params[1] = 0; else if (strcmp (s->value[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY) == 0) scan_params[1] = 1; else if (strcmp (s->value[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) == 0) scan_params[1] = 2; else return (SANE_STATUS_JAMMED); /*this should never happen */ scan_params[2] = s->as6e_params.startpos; scan_params[3] = s->as6e_params.stoppos; scan_params[4] = s->as6e_params.startline; scan_params[5] = s->as6e_params.stopline; scan_params[6] = s->value[OPT_BRIGHTNESS].w; scan_params[7] = s->value[OPT_CONTRAST].w; DBG (1, "scan params = %d %d %d %d %d %d %d %d\n", scan_params[0], scan_params[1], scan_params[2], scan_params[3], scan_params[4], scan_params[5], scan_params[6], scan_params[7]); numbytes = write (s->as6e_params.ctloutpipe, scan_params, sizeof (scan_params)); if (numbytes != sizeof (scan_params)) return (SANE_STATUS_IO_ERROR); s->scanning = SANE_TRUE; s->scan_buffer_count = 0; s->image_counter = 0; s->cancelled = 0; return (SANE_STATUS_GOOD); } /*--------------------------------------------------------------------------*/ SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { AS6E_Scan *s = handle; SANE_String mode; SANE_Word divisor = 1; DBG (2, "sane_get_parameters\n"); if (!s->scanning) { memset (&s->sane_params, 0, sizeof (s->sane_params)); s->as6e_params.resolution = s->value[OPT_RESOLUTION].w; s->as6e_params.startpos = as6e_unit_convert (s->value[OPT_TL_X].w); s->as6e_params.stoppos = as6e_unit_convert (s->value[OPT_BR_X].w); s->as6e_params.startline = as6e_unit_convert (s->value[OPT_TL_Y].w); s->as6e_params.stopline = as6e_unit_convert (s->value[OPT_BR_Y].w); if ((s->as6e_params.resolution == 200) || (s->as6e_params.resolution == 100)) divisor = 3; else if (s->as6e_params.resolution == 50) divisor = 6; /*get legal values for 200 dpi */ s->as6e_params.startpos = (s->as6e_params.startpos / divisor) * divisor; s->as6e_params.stoppos = (s->as6e_params.stoppos / divisor) * divisor; s->as6e_params.startline = (s->as6e_params.startline / divisor) * divisor; s->as6e_params.stopline = (s->as6e_params.stopline / divisor) * divisor; s->sane_params.pixels_per_line = (s->as6e_params.stoppos - s->as6e_params.startpos) * s->as6e_params.resolution / 300; s->sane_params.lines = (s->as6e_params.stopline - s->as6e_params.startline) * s->as6e_params.resolution / 300; mode = s->value[OPT_MODE].s; /* if ((strcmp (s->mode, SANE_VALUE_SCAN_MODE_LINEART) == 0) || (strcmp (s->mode, SANE_VALUE_SCAN_MODE_HALFTONE) == 0)) { s->sane_params.format = SANE_FRAME_GRAY; s->sane_params.bytes_per_line = (s->sane_params.pixels_per_line + 7) / 8; s->sane_params.depth = 1; } */ /*else*/ if ((strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) || (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0)) { s->sane_params.format = SANE_FRAME_GRAY; s->sane_params.bytes_per_line = s->sane_params.pixels_per_line; s->sane_params.depth = 8; } /*grey frame */ else { s->sane_params.format = SANE_FRAME_RGB; s->sane_params.bytes_per_line = 3 * s->sane_params.pixels_per_line; s->sane_params.depth = 8; } /*color frame */ s->bytes_to_read = s->sane_params.lines * s->sane_params.bytes_per_line; s->sane_params.last_frame = SANE_TRUE; } /*!scanning */ if (params) *params = s->sane_params; return (SANE_STATUS_GOOD); } /*--------------------------------------------------------------------------*/ SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { AS6E_Scan *s = handle; SANE_Status status = 0; SANE_Word cap; DBG (2, "sane_control_option\n"); if (info) *info = 0; if (s->scanning) return SANE_STATUS_DEVICE_BUSY; if (option >= NUM_OPTIONS) return SANE_STATUS_INVAL; cap = s->options_list[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) return SANE_STATUS_INVAL; if (action == SANE_ACTION_GET_VALUE) { DBG (1, "sane_control_option %d, get value\n", option); switch (option) { /* word options: */ case OPT_RESOLUTION: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_NUM_OPTS: case OPT_CONTRAST: case OPT_BRIGHTNESS: *(SANE_Word *) val = s->value[option].w; return (SANE_STATUS_GOOD); /* string options: */ case OPT_MODE: strcpy (val, s->value[option].s); return (SANE_STATUS_GOOD); } } else if (action == SANE_ACTION_SET_VALUE) { DBG (1, "sane_control_option %d, set value\n", option); if (!SANE_OPTION_IS_SETTABLE (cap)) return (SANE_STATUS_INVAL); /* status = sanei_constrain_value (s->options_list[option], val, info);*/ if (status != SANE_STATUS_GOOD) return (status); switch (option) { /* (mostly) side-effect-free word options: */ case OPT_RESOLUTION: case OPT_BR_X: case OPT_BR_Y: case OPT_TL_X: case OPT_TL_Y: if (info && s->value[option].w != *(SANE_Word *) val) *info |= SANE_INFO_RELOAD_PARAMS; /* fall through */ case OPT_NUM_OPTS: case OPT_CONTRAST: case OPT_BRIGHTNESS: s->value[option].w = *(SANE_Word *) val; DBG (1, "set brightness to\n"); return (SANE_STATUS_GOOD); case OPT_MODE: if (s->value[option].s) free (s->value[option].s); s->value[option].s = strdup (val); return (SANE_STATUS_GOOD); } } return (SANE_STATUS_INVAL); } /*--------------------------------------------------------------------------*/ const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { AS6E_Scan *s = handle; DBG (2, "sane_get_option_descriptor\n"); if ((unsigned) option >= NUM_OPTIONS) return (0); return (&s->options_list[option]); } /*--------------------------------------------------------------------------*/ void sane_close (SANE_Handle handle) { AS6E_Scan *prev, *s; SANE_Word repeat = 0; DBG (2, "sane_close\n"); /* remove handle from list of open handles: */ prev = 0; for (s = first_handle; s; s = s->next) { if (s == handle) break; prev = s; } if (!s) { DBG (1, "close: invalid handle %p\n", handle); return; /* oops, not a handle we know about */ } if (s->scanning) sane_cancel (handle); write (s->as6e_params.ctloutpipe, &repeat, sizeof (repeat)); close (s->as6e_params.ctloutpipe); free (s->scan_buffer); free (s->line_buffer); if (prev) prev->next = s->next; else first_handle = s; free (handle); } /*--------------------------------------------------------------------------*/ void sane_exit (void) { AS6E_Device *next; DBG (2, "sane_exit\n"); while (first_dev != NULL) { next = first_dev->next; free (first_dev); first_dev = next; } if (devlist) free (devlist); } /*--------------------------------------------------------------------------*/ static SANE_Status as6e_open (AS6E_Scan * s) { int data_processed, exec_result, as6e_status; int ctloutpipe[2], ctlinpipe[2], datapipe[2]; char inpipe_desc[32], outpipe_desc[32], datapipe_desc[32]; pid_t fork_result; DBG (1, "as6e_open\n"); memset (inpipe_desc, '\0', sizeof (inpipe_desc)); memset (outpipe_desc, '\0', sizeof (outpipe_desc)); memset (datapipe_desc, '\0', sizeof (datapipe_desc)); if ((pipe (ctloutpipe) == 0) && (pipe (ctlinpipe) == 0) && (pipe (datapipe) == 0)) { fork_result = fork (); if (fork_result == (pid_t) - 1) { DBG (1, "Fork failure"); return (SANE_STATUS_IO_ERROR); } if (fork_result == 0) { /*in child */ sprintf (inpipe_desc, "%d", ctlinpipe[WRITEPIPE]); sprintf (outpipe_desc, "%d", ctloutpipe[READPIPE]); sprintf (datapipe_desc, "%d", datapipe[WRITEPIPE]); exec_result = execlp ("as6edriver", "as6edriver", "-s", inpipe_desc, outpipe_desc, datapipe_desc, (char *) 0); DBG (1, "The SANE backend was unable to start \"as6edriver\".\n"); DBG (1, "This must be installed in a directory in your PATH.\n"); DBG (1, "To acquire the as6edriver program,\n"); DBG (1, "go to http://as6edriver.sourceforge.net.\n"); write (ctlinpipe[WRITEPIPE], &exec_result, sizeof (exec_result)); exit (-1); } else { /*parent process */ data_processed = read (ctlinpipe[READPIPE], &as6e_status, sizeof (as6e_status)); DBG (1, "%d - read %d status = %d\n", getpid (), data_processed, as6e_status); if (as6e_status == -2) { DBG (1, "Port access denied.\n"); return (SANE_STATUS_IO_ERROR); } if (as6e_status == -1) { DBG (1, "Could not contact scanner.\n"); return (SANE_STATUS_IO_ERROR); } if (as6e_status == 1) DBG (1, "Using nibble mode.\n"); if (as6e_status == 2) DBG (1, "Using byte mode.\n"); if (as6e_status == 3) DBG (1, "Using EPP mode.\n"); s->as6e_params.ctlinpipe = ctlinpipe[READPIPE]; s->as6e_params.ctloutpipe = ctloutpipe[WRITEPIPE]; s->as6e_params.datapipe = datapipe[READPIPE]; s->child_pid = fork_result; return (SANE_STATUS_GOOD); } /*else */ } else return (SANE_STATUS_IO_ERROR); } /*--------------------------------------------------------------------------*/ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { char dev_name[PATH_MAX]; size_t len; FILE *fp = NULL; DBG_INIT (); DBG (2, "sane_init (authorize %s null)\n", (authorize) ? "!=" : "=="); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); /* fp = sanei_config_open (AS6E_CONFIG_FILE);*/ if (!fp) { return (attach ("as6edriver", 0)); } while (fgets (dev_name, sizeof (dev_name), fp)) { if (dev_name[0] == '#') /* ignore line comments */ continue; len = strlen (dev_name); if (dev_name[len - 1] == '\n') dev_name[--len] = '\0'; if (!len) continue; /* ignore empty lines */ /* sanei_config_attach_matching_devices (dev_name, attach_one);*/ } fclose (fp); return (SANE_STATUS_GOOD); } /*--------------------------------------------------------------------------*/ /* static SANE_Status attach_one (const char *dev) { DBG (2, "attach_one\n"); attach (dev, 0); return (SANE_STATUS_GOOD); } */ /*--------------------------------------------------------------------------*/ SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { static const SANE_Device **devlist = 0; AS6E_Device *dev; int i; DBG (3, "sane_get_devices (local_only = %d)\n", local_only); if (devlist) free (devlist); devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); if (!devlist) return SANE_STATUS_NO_MEM; i = 0; for (dev = first_dev; i < num_devices; dev = dev->next) devlist[i++] = &dev->sane; devlist[i++] = 0; *device_list = devlist; return (SANE_STATUS_GOOD); } /*--------------------------------------------------------------------------*/ static size_t max_string_size (const SANE_String_Const strings[]) { size_t size, max_size = 0; int i; for (i = 0; strings[i]; ++i) { size = strlen (strings[i]) + 1; if (size > max_size) max_size = size; } return (max_size); } /*--------------------------------------------------------------------------*/ static void initialize_options_list (AS6E_Scan * s) { SANE_Int option; DBG (2, "initialize_options_list\n"); for (option = 0; option < NUM_OPTIONS; ++option) { s->options_list[option].size = sizeof (SANE_Word); s->options_list[option].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } s->options_list[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS; s->options_list[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; s->options_list[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; s->options_list[OPT_NUM_OPTS].type = SANE_TYPE_INT; s->options_list[OPT_NUM_OPTS].unit = SANE_UNIT_NONE; s->options_list[OPT_NUM_OPTS].size = sizeof (SANE_Word); s->options_list[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; s->options_list[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE; s->value[OPT_NUM_OPTS].w = NUM_OPTIONS; s->options_list[OPT_MODE].name = SANE_NAME_SCAN_MODE; s->options_list[OPT_MODE].title = SANE_TITLE_SCAN_MODE; s->options_list[OPT_MODE].desc = SANE_DESC_SCAN_MODE; s->options_list[OPT_MODE].type = SANE_TYPE_STRING; s->options_list[OPT_MODE].size = max_string_size (mode_list); s->options_list[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->options_list[OPT_MODE].constraint.string_list = mode_list; s->value[OPT_MODE].s = strdup (mode_list[2]); s->options_list[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->options_list[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; s->options_list[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; s->options_list[OPT_RESOLUTION].type = SANE_TYPE_INT; s->options_list[OPT_RESOLUTION].unit = SANE_UNIT_DPI; s->options_list[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->options_list[OPT_RESOLUTION].constraint.word_list = resolution_list; s->value[OPT_RESOLUTION].w = 200; s->options_list[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->options_list[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->options_list[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->options_list[OPT_TL_X].type = SANE_TYPE_FIXED; s->options_list[OPT_TL_X].unit = SANE_UNIT_MM; s->options_list[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->options_list[OPT_TL_X].constraint.range = &x_range; s->value[OPT_TL_X].w = s->options_list[OPT_TL_X].constraint.range->min; s->options_list[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->options_list[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->options_list[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->options_list[OPT_TL_Y].type = SANE_TYPE_FIXED; s->options_list[OPT_TL_Y].unit = SANE_UNIT_MM; s->options_list[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->options_list[OPT_TL_Y].constraint.range = &y_range; s->value[OPT_TL_Y].w = s->options_list[OPT_TL_Y].constraint.range->min; s->options_list[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->options_list[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->options_list[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->options_list[OPT_BR_X].type = SANE_TYPE_FIXED; s->options_list[OPT_BR_X].unit = SANE_UNIT_MM; s->options_list[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->options_list[OPT_BR_X].constraint.range = &x_range; s->value[OPT_BR_X].w = s->options_list[OPT_BR_X].constraint.range->max; s->options_list[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->options_list[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->options_list[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->options_list[OPT_BR_Y].type = SANE_TYPE_FIXED; s->options_list[OPT_BR_Y].unit = SANE_UNIT_MM; s->options_list[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->options_list[OPT_BR_Y].constraint.range = &y_range; s->value[OPT_BR_Y].w = s->options_list[OPT_BR_Y].constraint.range->max; s->options_list[OPT_CONTRAST].name = SANE_NAME_CONTRAST; s->options_list[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; s->options_list[OPT_CONTRAST].desc = SANE_DESC_CONTRAST; s->options_list[OPT_CONTRAST].type = SANE_TYPE_INT; s->options_list[OPT_CONTRAST].unit = SANE_UNIT_NONE; s->options_list[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; s->options_list[OPT_CONTRAST].constraint.range = &brightness_range; s->value[OPT_BRIGHTNESS].w = 10; s->options_list[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; s->options_list[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; s->options_list[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; s->options_list[OPT_BRIGHTNESS].type = SANE_TYPE_INT; s->options_list[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE; s->options_list[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; s->options_list[OPT_BRIGHTNESS].constraint.range = &contrast_range; s->value[OPT_CONTRAST].w = -32; } /*--------------------------------------------------------------------------*/ static int check_for_driver (const char *devname) { #define NAMESIZE 128 struct stat statbuf; mode_t modes; char *path; char dir[NAMESIZE]; int count = 0, offset = 0, valid; path = getenv ("PATH"); if (!path) return 0; while (path[count] != '\0') { memset (dir, '\0', sizeof (dir)); valid = 1; while ((path[count] != ':') && (path[count] != '\0')) { /* prevent writing data, which are out of bounds */ if ((unsigned int)(count - offset) < sizeof (dir)) dir[count - offset] = path[count]; else valid = 0; count++; } if (valid == 1) { char fullname[NAMESIZE]; int len = snprintf(fullname, sizeof(fullname), "%s/%s", dir, devname); if ((len > 0) && (len <= (int)sizeof(fullname))) { if (!stat (fullname, &statbuf)) { modes = statbuf.st_mode; if (S_ISREG (modes)) return (1); /* found as6edriver */ } } } if (path[count] == '\0') return (0); /* end of path --no driver found */ count++; offset = count; } return (0); } /*--------------------------------------------------------------------------*/ static SANE_Status attach (const char *devname, AS6E_Device ** devp) { AS6E_Device *dev; /* SANE_Status status; */ DBG (2, "attach\n"); for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->sane.name, devname) == 0) { if (devp) *devp = dev; return (SANE_STATUS_GOOD); } } dev = malloc (sizeof (*dev)); if (!dev) return (SANE_STATUS_NO_MEM); memset (dev, 0, sizeof (*dev)); dev->sane.name = strdup (devname); if (!check_for_driver (devname)) { free (dev); return (SANE_STATUS_INVAL); } dev->sane.model = "AS6E"; dev->sane.vendor = "Artec"; dev->sane.type = "flatbed scanner"; ++num_devices; dev->next = first_dev; first_dev = dev; if (devp) *devp = dev; return (SANE_STATUS_GOOD); } /*--------------------------------------------------------------------------*/ SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { SANE_Status status; AS6E_Device *dev; AS6E_Scan *s; DBG (2, "sane_open\n"); if (devicename[0]) { for (dev = first_dev; dev; dev = dev->next) if (strcmp (dev->sane.name, devicename) == 0) break; if (!dev) { status = attach (devicename, &dev); if (status != SANE_STATUS_GOOD) return (status); } } else { /* empty devicname -> use first device */ dev = first_dev; } if (!dev) return SANE_STATUS_INVAL; s = malloc (sizeof (*s)); if (!s) return SANE_STATUS_NO_MEM; memset (s, 0, sizeof (*s)); s->scan_buffer = malloc (SCAN_BUF_SIZE); if (!s->scan_buffer) return SANE_STATUS_NO_MEM; memset (s->scan_buffer, 0, SCAN_BUF_SIZE); s->line_buffer = malloc (SCAN_BUF_SIZE); if (!s->line_buffer) return SANE_STATUS_NO_MEM; memset (s->line_buffer, 0, SCAN_BUF_SIZE); status = as6e_open (s); if (status != SANE_STATUS_GOOD) return status; initialize_options_list (s); s->scanning = 0; /* insert newly opened handle into list of open handles: */ s->next = first_handle; first_handle = s; *handle = s; return (status); } /*--------------------------------------------------------------------------*/ SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { DBG (2, "sane_set_io_mode( %p, %d )\n", handle, non_blocking); return (SANE_STATUS_UNSUPPORTED); } /*---------------------------------------------------------------------------*/ SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { DBG (2, "sane_get_select_fd( %p, %p )\n",(void *) handle, (void *) fd); return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/as6e.h000066400000000000000000000062011456256263500157000ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Artec AS6E backend. Copyright (C) 2000 Eugene S. Weiss 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, see . 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 backend for the Artec AS6E by making a bridge to the as6edriver program. The as6edriver program can be found at http://as6edriver.sourceforge.net . */ #ifndef as6e_h #define as6e_h #include #include #include "../include/sane/sane.h" typedef enum { OPT_NUM_OPTS = 0, OPT_MODE, OPT_RESOLUTION, OPT_TL_X, /* top-left x */ OPT_TL_Y, /* top-left y */ OPT_BR_X, /* bottom-right x */ OPT_BR_Y, /* bottom-right y */ OPT_BRIGHTNESS, OPT_CONTRAST, /* must come last */ NUM_OPTIONS } AS6E_Option; typedef struct { int color; int resolution; int startpos; int stoppos; int startline; int stopline; int ctloutpipe; int ctlinpipe; int datapipe; } AS6E_Params; typedef struct AS6E_Device { struct AS6E_Device *next; SANE_Device sane; } AS6E_Device; typedef struct AS6E_Scan { struct AS6E_Scan *next; SANE_Option_Descriptor options_list[NUM_OPTIONS]; Option_Value value[NUM_OPTIONS]; SANE_Bool scanning; SANE_Bool cancelled; SANE_Parameters sane_params; AS6E_Params as6e_params; pid_t child_pid; size_t bytes_to_read; SANE_Byte *scan_buffer; SANE_Byte *line_buffer; SANE_Word scan_buffer_count; SANE_Word image_counter; } AS6E_Scan; #ifndef PATH_MAX #define PATH_MAX 1024 #endif #define AS6E_CONFIG_FILE "as6e.conf" #define READPIPE 0 #define WRITEPIPE 1 #define SCAN_BUF_SIZE 32768 #endif /* as6e_h */ backends-1.3.0/backend/avision.c000066400000000000000000010664661456256263500165310ustar00rootroot00000000000000/******************************************************************************* * SANE - Scanner Access Now Easy. avision.c 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, see . 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. ***************************************************************************** This file implements a SANE backend for the Avision SCSI Scanners (like the AV 630 / 620 (CS) ...) and some Avision (OEM) USB scanners (like the HP 53xx, 74xx, Minolta FS-V1 ...) or Fujitsu ScanPartner with the AVISION SCSI-2/3 or USB command set and written by René Rebe and Meino Cramer. Copyright 2002 - 2015 by "Ren Rebe" Copyright 1999, 2000, 2001 by "René Rebe" "Meino Christian Cramer" Copyright 2002 by "Jose Paulo Moitinho de Almeida" Copyright 2010, 2011 by "Mike Kelly" Additional Contributors: "Gunter Wagner" (some fixes and the transparency option) "Martin Jelínek" nice attach debug output "Marcin Siennicki" found some typos and contributed fixes for the HP 7400 "Frank Zago" Mitsubishi IDs and report Avision INC example code to handle calibration and C5 ASIC specifics "Franz Bakan" OS/2 threading support "Falk Rohsiepe" Spelling and whitespace as well as HP5370 quirks Many additional special thanks to: Avision INC for providing protocol documentation. Avision INC for sponsoring an AV 8000S with ADF. Avision Europe and BHS Binkert for sponsoring several more scanners. Archivista GmbH, Switzerland, for sponsoring several features Roberto Di Cosmo who sponsored a HP 5370 scanner. Oliver Neukum who sponsored a HP 5300 USB scanner. Matthias Wiedemann for lending his HP 7450C for some weeks. Compusoft, C.A. Caracas / Venezuela for sponsoring a HP 7450 scanner and so enhanced ADF support. Chris Komatsu for the nice ADF scanning observation. All the many other beta-tester and debug-log sender! Thanks to all the people and companies above. Without you the Avision backend would not be in the shape it is today! ;-) ********************************************************************************/ /* SANE-FLOW-DIAGRAMM (from umax.c) * * - sane_init() : initialize backend, attach scanners(devicename,0) * . - sane_get_devices() : query list of scanner-devices * . - sane_open() : open a particular scanner-device and attach_scanner(devicename,&dev) * . . - 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) * * in ADF mode this is done often: * . . - sane_start() : start image acquisition * . . - sane_get_parameters() : returns actual scan-parameters * . . - sane_read() : read image-data (from pipe) * * . . - sane_cancel() : cancel operation, kill reader_process * * . - sane_close() : do_cancel, close opened scanner-device, free buffer and handle * - sane_exit() : terminate use of backend, free devicename and device-structure */ #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include #define BACKEND_NAME avision #define BACKEND_BUILD 297 /* avision backend BUILD version */ #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_scsi.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_backend.h" #include /* For timeval... */ #ifdef DEBUG #include #endif /* Attention: The comments must stay as they are - they are automatically parsed to generate the SANE avision.desc file, as well as HTML online content! */ /* Attention2: This device table is part of the source code and as such licensed under the terms of the license as listed above (GPL2+). By using this data you obviously create derived work! -ReneR */ static Avision_HWEntry Avision_Device_List [] = { { NULL, NULL, 0x0638, 0x2E59, "Avision", "AD345F", AV_INT_BUTTON | AV_CANCEL_BUTTON | AV_USE_GRAY_FILTER, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="duplex! sheetfed + flatbed scanner" */ /* status="basic" */ { "AVISION", "AV100CS", 0, 0, "Avision", "AV100CS", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { "AVISION", "AV100IIICS", 0, 0, "Avision", "AV100IIICS", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { "AVISION", "AV100S", 0, 0, "Avision", "AV100S", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { NULL, NULL, 0x0638, 0x0A27, "Avision", "AV120", AV_INT_STATUS, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A3C, "Avision", "AV121", AV_INT_BUTTON | AV_DOES_KEEP_WINDOW | AV_DOES_KEEP_GAMMA, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="sheetfed scanner" */ /* status="good" */ { NULL, NULL, 0x0638, 0x0A33, "Avision", "AV122", AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_NO_REAR | AV_SOFT_SCALE | AV_DOES_KEEP_WINDOW | AV_DOES_KEEP_GAMMA, { 0, {0, 0}, {{0, 0}, {-12.7, 0}} } }, /* comment="sheetfed duplex scanner" */ /* status="good" */ { NULL, NULL, 0x0638, 0x0A93, "Avision", "AV122 C2", AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_NO_REAR | AV_SOFT_SCALE | AV_DOES_NOT_KEEP_WINDOW | AV_DOES_KEEP_GAMMA, { 0, {0, 0}, {{0, 0}, {-12.7, 0}} } }, /* comment="sheetfed duplex scanner" */ /* status="good" */ { NULL, NULL, 0x0638, 0x0A24, "Avision", "AV210", AV_INT_BUTTON | AV_ACCEL_TABLE, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A25, "Avision", "AV210", AV_INT_BUTTON | AV_ACCEL_TABLE | AV_NO_64BYTE_ALIGN, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A3A, "Avision", "AV210C2", AV_INT_BUTTON | AV_GRAY_MODES, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A2F, "Avision", "AV210C2-G", AV_INT_BUTTON | AV_GRAY_MODES, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x1A35, "Avision", "AV210D2+", AV_INT_BUTTON | AV_USE_GRAY_FILTER, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A23, "Avision", "AV220", AV_INT_BUTTON | AV_GRAY_MODES, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="duplex! sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A2A, "Avision", "AV220C2", AV_INT_BUTTON | AV_CANCEL_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="duplex! sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A2B, "Avision", "AV220D2", AV_INT_BUTTON | AV_CANCEL_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="duplex! sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x1A31, "Avision", "AV220D2+", AV_INT_BUTTON | AV_CANCEL_BUTTON | AV_USE_GRAY_FILTER, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="duplex! sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x1A33, "Avision", "AV186+", AV_INT_BUTTON | AV_CANCEL_BUTTON | AV_USE_GRAY_FILTER, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="duplex! sheetfed scanner" */ /* status="good" */ { NULL, NULL, 0x0638, 0x2AD9, "Avision", "AV188", AV_INT_BUTTON | AV_CANCEL_BUTTON | AV_USE_GRAY_FILTER, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="duplex! sheetfed scanner" */ /* status="untested" */ { NULL, NULL, 0x0638, 0x0A2C, "Avision", "AV220+", AV_INT_BUTTON | AV_CANCEL_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="duplex! sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A2D, "Avision", "AV220C2-G", AV_INT_BUTTON | AV_CANCEL_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="duplex! sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A2E, "Avision", "AV220C2-B", AV_INT_BUTTON | AV_CANCEL_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="duplex! sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A94, "Avision", "AV220-G", AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_FIRMWARE, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="duplex! sheetfed scanner" */ /* status="complete" */ { "AVISION", "AV240SC", 0, 0, "Avision", "AV240SC", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { "AVISION", "AV260CS", 0, 0, "Avision", "AV260CS", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { "AVISION", "AV360CS", 0, 0, "Avision", "AV360CS", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { "AVISION", "AV363CS", 0, 0, "Avision", "AV363CS", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { "AVISION", "AV420CS", 0, 0, "Avision", "AV420CS", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { "AVISION", "AV6120", 0, 0, "Avision", "AV6120", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { NULL, "AV610", 0x0638, 0x0a18, "Avision", "AV610", AV_GRAY_CALIB_BLUE | AV_ACCEL_TABLE | AV_NO_64BYTE_ALIGN | AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="good" */ { NULL, NULL, 0x0638, 0x0a18, "Avision", "AV600U Plus", /* If this unit requires the AV_INT_STATUS flag, then we'll need to alter the code to deal with two different devices with the same USB id (AV610 above) */ AV_GRAY_CALIB_BLUE | AV_ACCEL_TABLE | AV_NO_64BYTE_ALIGN | /* AV_INT_STATUS | */ AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="good" */ { NULL, NULL, 0x0638, 0x0a5e, "Avision", "AV610C2", AV_NO_BACKGROUND | AV_INT_BUTTON, /* cancel button -> sense abort! */ { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="good" */ { NULL, NULL, 0x0638, 0x0a41, "Avision", "AM3000 Series", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="MFD" */ /* status="basic" */ { NULL, NULL, 0x0638, 0x0a16, "Avision", "DS610CU Scancopier", AV_INT_STATUS, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 600 dpi, A4" */ /* status="good" */ { "AVISION", "AV620CS", 0, 0, "Avision", "AV620CS", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 600 dpi" */ /* status="complete" */ { "AVISION", "AV620CS Plus", 0, 0, "Avision", "AV620CS Plus", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 1200 dpi" */ /* status="complete" */ { "AVISION", "AV630CS", 0, 0, "Avision", "AV630CS", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 1200 dpi" */ /* status="complete" */ { "AVISION", "AV630CSL", 0, 0, "Avision", "AV630CSL", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 1200 dpi" */ /* status="untested" */ { "AVISION", "AV6240", 0, 0, "Avision", "AV6240", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, ??? dpi" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A13, "Avision", "AV600U", AV_MULTI_CALIB_CMD | AV_ADF_BGR_ORDER_INVERT | AV_SOFT_SCALE | AV_INT_STATUS | AV_NO_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 600 dpi" */ /* status="good" */ { "AVISION", "AV660S", 0, 0, "Avision", "AV660S", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, ??? dpi" */ /* status="untested" */ { "AVISION", "AV680S", 0, 0, "Avision", "AV680S", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, ??? dpi" */ /* status="untested" */ { "AVISION", "AV690U", 0, 0, "Avision", "AV690U", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 2400 dpi" */ /* status="untested" */ { "AVISION", "AV800S", 0, 0, "Avision", "AV800S", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, ??? dpi" */ /* status="untested" */ { "AVISION", "AV810C", 0, 0, "Avision", "AV810C", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, ??? dpi" */ /* status="untested" */ { "AVISION", "AV820", 0, 0, "Avision", "AV820", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, ??? dpi" */ /* status="untested" */ { "AVISION", "AV820C", 0, 0, "Avision", "AV820C", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, ??? dpi" */ /* status="complete" */ { "AVISION", "AV820C Plus", 0, 0, "Avision", "AV820C Plus", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, ??? dpi" */ /* status="complete" */ { "AVISION", "AV830C", 0, 0, "Avision", "AV830C", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, ??? dpi" */ /* status="complete" */ { "AVISION", "AV830C Plus", 0, 0, "Avision", "AV830C Plus", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, ??? dpi" */ /* status="untested" */ { "AVISION", "AV880", 0, 0, "Avision", "AV880", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, ??? dpi" */ /* status="untested" */ { "AVISION", "AV880C", 0, 0, "Avision", "AV880C", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, ??? dpi" */ /* status="untested" */ { "AVISION", "AV3200C", 0, 0, "Avision", "AV3200C", AV_NON_INTERLACED_DUPLEX_300 | AV_FASTER_WITH_FILTER, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, ??? dpi" */ /* status="complete" */ { "AVISION", "AV3200SU", 0x0638, 0x0A4E, "Avision", "AV3200SU", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, ??? dpi" */ /* status="complete" */ { "AVISION", "AV3730SU", 0x0638, 0x0A4F, "Avision", "AV3730SU", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, ??? dpi" */ /* status="complete" */ { "AVISION", "AV3750SU", 0x0638, 0x0A65, "Avision", "AV3750SU", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, ??? dpi" */ /* status="complete" */ { "AVISION", "AV3800C", 0, 0, "Avision", "AV3800C", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, ??? dpi" */ /* status="complete" */ { "AVISION", "AV3850SU", 0x0638, 0x0a66, "Avision", "AV3850SU", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, ??? dpi" */ /* status="complete" */ { "AVISION", "FB6000E", 0, 0, "Avision", "FB6000E", AV_NON_INTERLACED_DUPLEX_300, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 1200 dpi, A3 - duplex! - zero edge!" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0a82, "Avision", "FB6080E", AV_NON_INTERLACED_DUPLEX_300, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 1200 dpi, A3 - duplex! - zero edge!" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0a84, "Avision", "FB2080E", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 600 dpi, zero-edge" ASIC 7 */ /* status="basic" */ { NULL, NULL, 0x0638, 0x2a1f, "Avision", "FB2280E", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 600 dpi, zero-edge" ASIC 7 */ /* status="basic" */ { "AVISION", "AV8000S", 0, 0, "Avision", "AV8000S", AV_DOES_NOT_KEEP_WINDOW, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 1200 dpi, A3" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0a4d, "Avision", "AV8050U", AV_NON_INTERLACED_DUPLEX_300 | AV_DOES_NOT_KEEP_GAMMA, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 1200 dpi, A3 - duplex!" */ /* status="complete" */ { "AVISION", "AV8300", 0x0638, 0x0A40, "Avision", "AV8300", AV_NON_INTERLACED_DUPLEX_300 | AV_DOES_NOT_KEEP_GAMMA, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 1200 dpi, A3 - duplex!" */ /* status="complete" */ { "AVISION", "AV8350", 0x0638, 0x0A68, "Avision", "AV8350", AV_NON_INTERLACED_DUPLEX_300 | AV_DOES_NOT_KEEP_GAMMA, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 1200 dpi, A3 - duplex!" */ /* status="complete" */ { NULL, NULL, 0x0638, 0x0A61, "Avision", "IT8300", AV_NON_INTERLACED_DUPLEX_300 | AV_ACCEL_TABLE, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 1200 dpi, A3 - duplex!, LCD screen, paper sensors" */ /* status="good" */ { NULL, NULL, 0x0638, 0x0AA1, "Avision", "@V2500", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="" */ /* status="untested" */ { NULL, NULL, 0x0638, 0x0A45, "Avision", "@V5100", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 1200 dpi, A3 - duplex!, LCD screen, paper sensors" */ /* status="good" */ { "AVISION", "AVA3", 0, 0, "Avision", "AVA3", AV_FORCE_A3, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 600 dpi, A3" */ /* status="basic" */ /* and possibly more avisions ;-) */ { "HP", "ScanJet 5300C", 0x03f0, 0x0701, "Hewlett-Packard", "ScanJet 5300C", AV_INT_STATUS, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 2400 dpi - some FW revisions have x-axis image scaling problems over 1200 dpi" */ /* status="complete" */ { "HP", "ScanJet 5370C", 0x03f0, 0x0701, "Hewlett-Packard", "ScanJet 5370C", AV_MULTI_CALIB_CMD | AV_INT_STATUS, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 2400 dpi - some FW revisions have x-axis image scaling problems over 1200 dpi" */ /* status="good" */ { "hp", "scanjet 7400c", 0x03f0, 0x0801, "Hewlett-Packard", "ScanJet 7400c", AV_LIGHT_CHECK_BOGUS | AV_NO_64BYTE_ALIGN | AV_INT_STATUS, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 2400 dpi - dual USB/SCSI interface" */ /* status="good" */ #ifdef FAKE_ENTRIES_FOR_DESC_GENERATION { "hp", "scanjet 7450c", 0x03f0, 0x0801, "Hewlett-Packard", "ScanJet 7450c", AV_NO_64BYTE_ALIGN | AV_INT_STATUS, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 2400 dpi - dual USB/SCSI interface" */ /* status="good" */ { "hp", "scanjet 7490c", 0x03f0, 0x0801, "Hewlett-Packard", "ScanJet 7490c", AV_NO_64BYTE_ALIGN | AV_INT_STATUS, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 1200 dpi - dual USB/SCSI interface" */ /* status="good" */ #endif { "HP", "C9930A", 0x03f0, 0x0b01, "Hewlett-Packard", "ScanJet 8200", AV_ADF_FLIPPING_DUPLEX | AV_FIRMWARE, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 4800 (?) dpi - USB 2.0" */ /* status="good" */ #ifdef FAKE_ENTRIES_FOR_DESC_GENERATION { "HP", "C9930A", 0x03f0, 0x0b01, "Hewlett-Packard", "ScanJet 8250", AV_ADF_FLIPPING_DUPLEX | AV_FIRMWARE, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 4800 (?) dpi - USB 2.0" */ /* status="good" */ #endif { "HP", "C9930A", 0x03f0, 0x3905, "Hewlett-Packard", "ScanJet 8270", AV_ADF_FLIPPING_DUPLEX | AV_FIRMWARE, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 4800 (?) dpi - USB 2.0" */ /* status="good" */ #ifdef FAKE_ENTRIES_FOR_DESC_GENERATION { "HP", "C9930A", 0x03f0, 0x0b01, "Hewlett-Packard", "ScanJet 8290", AV_ADF_FLIPPING_DUPLEX | AV_FIRMWARE, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 4800 (?) dpi - USB 2.0 and SCSI - only SCSI tested so far" */ /* status="good" */ #endif { "HP", "C9930A", 0x03f0, 0x3805, "Hewlett-Packard", "ScanJet 8300", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 4800 (?) dpi - USB 2.0" */ /* status="good" */ #ifdef FAKE_ENTRIES_FOR_DESC_GENERATION { "HP", "C9930A", 0x03f0, 0x3805, "Hewlett-Packard", "ScanJet 8350", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 4800 (?) dpi - USB 2.0" */ /* status="good" */ { "HP", "C9930A", 0x03f0, 0x3805, "Hewlett-Packard", "ScanJet 8390", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 4800 (?) dpi - USB 2.0" */ /* status="good" */ #endif { "Minolta", "#2882", 0, 0, "Minolta", "Dimage Scan Dual I", AV_FORCE_FILM | AV_NO_START_SCAN, /* not AV_FILMSCANNER (no frame control) */ { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="basic" */ { "Minolta", "#2887", 0, 0, "Minolta", "Scan Multi Pro", AV_FORCE_FILM | AV_NO_START_SCAN, /* not AV_FILMSCANNER (no frame control) */ { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { "MINOLTA", "FS-V1", 0x0638, 0x026a, "Minolta", "Dimage Scan Dual II", AV_FILMSCANNER | AV_ONE_CALIB_CMD | AV_12_BIT_MODE, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, film-scanner" */ /* status="good" */ { "MINOLTA", "Elite II", 0x0686, 0x4004, "Minolta", "Elite II", AV_FILMSCANNER | AV_ONE_CALIB_CMD, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, film-scanner" */ /* status="untested" */ { "MINOLTA", "FS-V3", 0x0686, 0x400d, "Minolta", "Dimage Scan Dual III", AV_FILMSCANNER | AV_ONE_CALIB_CMD | AV_ACCEL_TABLE, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, film-scanner" */ /* status="good" */ { "MINOLTA", "FS-V4", 0x0686, 0x400e, "Minolta", "Dimage Scan Elite 5400", AV_FILMSCANNER | AV_ONE_CALIB_CMD | /*AV_ACCEL_TABLE |*/ AV_NO_START_SCAN, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, film-scanner" */ /* status="good" */ { "QMS", "SC-110", 0x0638, 0x0a15, "Minolta-QMS", "SC-110", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="" */ /* status="untested" */ { "QMS", "SC-215", 0x0638, 0x0a16, "Minolta-QMS", "SC-215", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="" */ /* status="good" */ { "MITSBISH", "MCA-ADFC", 0, 0, "Mitsubishi", "MCA-ADFC", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { "MITSBISH", "MCA-S1200C", 0, 0, "Mitsubishi", "S1200C", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { "MITSBISH", "MCA-S600C", 0, 0, "Mitsubishi", "S600C", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { "MITSBISH", "SS600", 0, 0, "Mitsubishi", "SS600", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="good" */ /* The next are all untested ... */ { "FCPA", "ScanPartner", 0, 0, "Fujitsu", "ScanPartner", AV_FUJITSU, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { "FCPA", "ScanPartner 10", 0, 0, "Fujitsu", "ScanPartner 10", AV_FUJITSU, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { "FCPA", "ScanPartner 10C", 0, 0, "Fujitsu", "ScanPartner 10C", AV_FUJITSU, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { "FCPA", "ScanPartner 15C", 0, 0, "Fujitsu", "ScanPartner 15C", AV_FUJITSU, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { "FCPA", "ScanPartner 300C", 0, 0, "Fujitsu", "ScanPartner 300C", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { "FCPA", "ScanPartner 600C", 0, 0, "Fujitsu", "ScanPartner 600C", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { "FCPA", "ScanPartner 620C", 0, 0, "Fujitsu", "ScanPartner 620C", AV_LIGHT_CHECK_BOGUS, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="good" */ { "FCPA", "ScanPartner Jr", 0, 0, "Fujitsu", "ScanPartner Jr", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { "FCPA", "ScanStation", 0, 0, "Fujitsu", "ScanStation", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { NULL, NULL, 0x04c5, 0x1029, "Fujitsu", "fi-4010CU", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { NULL, NULL, 0x04c5, 0x10ef, "Fujitsu", "fi-5015C", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="good" */ { NULL, NULL, 0x040a, 0x6001, "Kodak", "i30", AV_INT_BUTTON | AV_GRAY_MODES, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { NULL, NULL, 0x040a, 0x6002, "Kodak", "i40", AV_INT_BUTTON | AV_GRAY_MODES, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="basic" */ { NULL, NULL, 0x040a, 0x6003, "Kodak", "i50", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ #ifdef FAKE_ENTRIES_FOR_DESC_GENERATION { NULL, NULL, 0x040a, 0x6003, "Kodak", "i55", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ #endif { NULL, NULL, 0x040a, 0x6004, "Kodak", "i60", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ #ifdef FAKE_ENTRIES_FOR_DESC_GENERATION { NULL, NULL, 0x040a, 0x6004, "Kodak", "i65", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ #endif { NULL, NULL, 0x040a, 0x6005, "Kodak", "i80", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="good" */ { NULL, NULL, 0x040a, 0x6013, "Kodak", "i1120", AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_USE_GRAY_FILTER | AV_SOFT_SCALE | AV_FORCE_CALIB | AV_NO_QSCAN_MODE | AV_NO_QCALIB_MODE | AV_OVERSCAN_OPTDPI | AV_NO_REAR | AV_FASTFEED_ON_CANCEL | AV_GAMMA_10 | AV_MULTI_SHEET_SCAN, { /* offsets */ -1.5, /* first sheet (should be identical for single / duplex) */ {2.5, -6.0}, /* front-only scan */ {{2.0, -14.0}, {-10.0, -2.0}} /* duplex scan */ } }, /* comment="duplex sheetfed scanner" */ /* status="good" */ /* This is a Kodak OEM device manufactured by avision. It uses an Avision firmware modified by Kodak, so some modifications needed to be done here. */ { "iVina", "1200U", 0x0638, 0x0268, "iVina", "1200U", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { NULL, NULL, 0x04a7, 0x0424, "Visioneer", "Strobe XP 450", AV_INT_BUTTON | AV_ACCEL_TABLE, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x0491, "Visioneer", "Strobe XP 450-G", AV_INT_BUTTON | AV_ACCEL_TABLE, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x0479, "Visioneer", "Strobe XP 470", AV_INT_BUTTON | AV_ACCEL_TABLE, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x048F, "Visioneer", "Strobe XP 470-G", AV_INT_BUTTON | AV_ACCEL_TABLE, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x0420, "Visioneer", "9320", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x0421, "Visioneer", "9450", AV_MULTI_CALIB_CMD | AV_ADF_BGR_ORDER_INVERT | AV_NO_BUTTON | AV_NO_TUNE_SCAN_LENGTH, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x047A, "Visioneer", "9450-G", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x0422, "Visioneer", "9550", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x0390, "Visioneer", "9650", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x047B, "Visioneer", "9650-G", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x0423, "Visioneer", "9750", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x0493, "Visioneer", "9750-G", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x0497, "Visioneer", "Patriot 430", AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_NO_REAR | AV_SOFT_SCALE | AV_DOES_KEEP_WINDOW | AV_DOES_KEEP_GAMMA, { 0, {0, 0}, {{0, 0}, {-12.7, 0}} } }, /* comment="sheetfed scanner" */ /* status="complete" */ #ifdef FAKE_ENTRIES_FOR_DESC_GENERATION { NULL, NULL, 0x04a7, 0x048F, "Visioneer", "Patriot 470", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="sheetfed scanner" */ /* status="complete" */ #endif { NULL, NULL, 0x04a7, 0x0498, "Visioneer", "Patriot 680", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x0499, "Visioneer", "Patriot 780", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="sheetfed scanner" */ /* status="complete" */ { NULL, NULL, 0x04a7, 0x049C, "Xerox", "DocuMate150", AV_INT_BUTTON | AV_SOFT_SCALE | AV_DOES_KEEP_WINDOW | AV_DOES_KEEP_GAMMA | AV_BACKGROUND_QUIRK, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="good" */ { NULL, NULL, 0x04a7, 0x0477, "Xerox", "DocuMate152", AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_NO_REAR | AV_SOFT_SCALE | AV_DOES_KEEP_WINDOW | AV_DOES_KEEP_GAMMA | AV_BACKGROUND_QUIRK, { 0, {0, 0}, {{0, 0}, {-12.7, 0}} } }, /* status="good" */ { NULL, NULL, 0x04a7, 0x049D, "Xerox", "DocuMate162", AV_INT_BUTTON | AV_2ND_LINE_INTERLACED | AV_NO_REAR | AV_SOFT_SCALE | AV_DOES_KEEP_WINDOW | AV_DOES_KEEP_GAMMA | AV_BACKGROUND_QUIRK, { 0, {0, 0}, {{0, 0}, {-12.7, 0}} } }, /* status="good" */ { NULL, NULL, 0x04a7, 0x0448, "Xerox", "DocuMate250", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="good" */ { NULL, NULL, 0x04a7, 0x0490, "Xerox", "DocuMate250-G", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="good" */ { NULL, NULL, 0x04a7, 0x0449, "Xerox", "DocuMate252", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="good" */ { NULL, NULL, 0x04a7, 0x048C, "Xerox", "DocuMate252-G", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="good" */ { NULL, NULL, 0x04a7, 0x0476, "Xerox", "DocuMate232", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="good" */ { NULL, NULL, 0x04a7, 0x044c, "Xerox", "DocuMate262", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="good" */ { NULL, NULL, 0x04a7, 0x048D, "Xerox", "DocuMate262-G", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="good" */ { NULL, NULL, 0x04a7, 0x04a7, "Xerox", "DocuMate262i", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="good" */ { NULL, NULL, 0x04a7, 0x0475, "Xerox", "DocuMate272", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { NULL, NULL, 0x04a7, 0x048E, "Xerox", "DocuMate272-G", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { NULL, NULL, 0x04a7, 0x0446, "Xerox", "DocuMate510", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { NULL, NULL, 0x04a7, 0x0495, "Xerox", "DocuMate512", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { NULL, NULL, 0x04a7, 0x047c, "Xerox", "DocuMate510-G", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { NULL, NULL, 0x04a7, 0x0447, "Xerox", "DocuMate520", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { NULL, NULL, 0x04a7, 0x0492, "Xerox", "DocuMate520-G", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ #ifdef FAKE_ENTRIES_FOR_DESC_GENERATION { NULL, NULL, 0x04a7, 0x0498, "Xerox", "DocuMate632", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ #endif { NULL, NULL, 0x04a7, 0x0478, "Xerox", "DocuMate752", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ { NULL, NULL, 0x04a7, 0x049A, "Xerox", "DocuMate752", AV_INT_BUTTON, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* status="untested" */ #ifdef FAKE_ENTRIES_FOR_DESC_GENERATION { NULL, NULL, 0x0638, 0x0a16, "OKI", "S700 Scancopier", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, 600 dpi, A4" */ /* status="good" */ #endif { "B+H", "2000F", 0, 0, "Bell+Howell", "2000F", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, ??? dpi, A4" */ /* status="basic" */ { NULL, NULL, 0x0482, 0x0335, "Kyocera", "FS-1016MFP", 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } }, /* comment="1 pass, ??? dpi, A4" */ /* status="untested" */ /* More IDs from the Avision dll: ArtiScan ProA3 FB1065 FB1265 PHI860S PSDC SCSI SCSI Scan 19200 V6240 */ /* Possibly: Lexmark 4600 MFP Option MFP Options Lexmark 4600 MFP Option (C772n) MFP Options Lexmark X215 Lexmark Optra Image X242 Lexmark X443 Lexmark 3100 Lexmark 3200 Lexmark X340 MFP Multifunction Lexmark X342n MFP Multifunction Lexmark X522 Lexmark X630 Lexmark X632E Lexmark X642e MFP Multifunction Lexmark X644e MFP Multifunction Lexmark X646dte MFP Multifunction Lexmark X646e MFP Multifunction Lexmark X646ef MFP Multifunction Lexmark X772e Multifunction Lexmark X850e MFP Multifunction Lexmark X852e MFP Multifunction Lexmark X854e MFP Multifunction Lexmark X4500 MFP */ /* last entry detection */ { NULL, NULL, 0, 0, NULL, NULL, 0, { 0, {0, 0}, {{0, 0}, {0, 0}} } } }; #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 ? */ #define A4_Y_RANGE 11.8 #define A3_X_RANGE 11.8 #define A3_Y_RANGE 16.5 /* or 17 ? */ #define FILM_X_RANGE 1.0 /* really ? */ #define FILM_Y_RANGE 1.0 #define SHEETFEED_Y_RANGE 14.0 #define AVISION_CONFIG_FILE "avision.conf" #define STD_INQUIRY_SIZE 0x24 #define AVISION_INQUIRY_SIZE_V1 0x60 #define AVISION_INQUIRY_SIZE_V2 0x88 #define AVISION_INQUIRY_SIZE_MAX AVISION_INQUIRY_SIZE_V2 #define AVISION_BASE_RES 300 /* calibration (shading) defines */ #define INVALID_WHITE_SHADING 0x0000 #define DEFAULT_WHITE_SHADING 0xFFF0 #define MAX_WHITE_SHADING 0xFFFF /* originally the WHITE_MAP_RANGE was 0x4000 - but this always * resulted in slightly too dark images - thus I have chosen * 0x4FFF ... */ #define WHITE_MAP_RANGE 0x4FFF #define INVALID_DARK_SHADING 0xFFFF #define DEFAULT_DARK_SHADING 0x0000 #define read_constrains(s,var) {\ if (s->hw->hw->feature_type & AV_NO_64BYTE_ALIGN) {\ if (var % 64 == 0) var /= 2;\ if (var % 64 == 0) var += 2;\ }\ }\ static unsigned int num_devices; static Avision_Device* first_dev; static Avision_Scanner* first_handle; static const SANE_Device** devlist = 0; /* this is a bit hacky to get extra information in the attach callback */ static Avision_HWEntry* attaching_hw = 0; /* disable the usage of a custom gamma-table */ static SANE_Bool disable_gamma_table = SANE_FALSE; /* 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; static SANE_Bool force_a3 = SANE_FALSE; /* trust ADF-presence flag, even if ADF model is nonzero */ static SANE_Bool skip_adf = SANE_FALSE; /* hardware resolutions to interpolate from */ static const int hw_res_list_c5[] = { /* tested on AV600U */ 75, 150, 300, 600, 1200, 2400, 4800, /* ... */ 0 }; static const int hw_res_list_generic[] = { 50, /* slower than 150 on the AV122/DM152, left for USB 1 host's preview */ 75, /* slower than 150 on the AV122/DM152, left for USB 1 host's */ 150, 200, 300, /* 400,*/ /* AV122 simplex y-scaling and duplex interlacing corrupt */ 600, 1200, 2400, 4800, /* ... */ 0 }; static SANE_Bool static_calib_list[3] = { SANE_FALSE, SANE_FALSE, SANE_FALSE }; static const SANE_Range u8_range = { 0, /* minimum */ 255, /* maximum */ 0 /* quantization */ }; static const SANE_Range percentage_range = { SANE_FIX (-100), /* minimum */ SANE_FIX (100), /* maximum */ SANE_FIX (1) /* quantization */ }; static const SANE_Range exposure_range = { 0, /* minimum */ 1000, /* maximum */ 1 /* quantization */ }; static const SANE_Range overscan_range = { SANE_FIX (0), /* minimum */ SANE_FIX (4), /* maximum */ /* 4mm, measured on AV122, AV220C2, i40 */ 0 /* quantization */ }; /* The 0x32 is a random guess based on USB logs. Might need a per-device value in the future - 0x32 was tested on the AV122, DM152, AV220. */ static const SANE_Range background_range = { 0, /* minimum */ 0x32, /* maximum */ 0 /* quantization */ }; static const uint8_t test_unit_ready[] = { AVISION_SCSI_TEST_UNIT_READY, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* Remove #ifdef and this comment when this SCSI command is used for something. Keeping this definition around so we don't loose info about the protocol. */ #ifdef ENABLE_AVISION_SCSI_GET_DATA_STATUS static const uint8_t get_status[] = { AVISION_SCSI_GET_DATA_STATUS, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00 }; #endif static size_t max_string_size (const SANE_String_Const strings[]) { size_t size, max_size = 0; int i; DBG (3, "max_string_size:\n"); for (i = 0; strings[i]; ++ i) { size = strlen (strings[i]) + 1; if (size > max_size) max_size = size; } return max_size; } static SANE_Status constrain_value (Avision_Scanner* s, SANE_Int option, void* value, SANE_Int* info) { DBG (3, "constrain_value:\n"); return sanei_constrain_value (s->opt + option, value, info); } static void debug_print_raw (int dbg_level, char* info, const uint8_t* data, size_t count) { size_t i; DBG (dbg_level, "%s", info); for (i = 0; i < count; ++ i) { DBG (dbg_level, " [%lu] %1d%1d%1d%1d%1d%1d%1d%1db %3oo %3dd %2xx\n", (u_long) i, BIT(data[i],7), BIT(data[i],6), BIT(data[i],5), BIT(data[i],4), BIT(data[i],3), BIT(data[i],2), BIT(data[i],1), BIT(data[i],0), data[i], data[i], data[i]); } } static void debug_print_hex_raw (int dbg_level, char* info, const uint8_t* data, size_t count) { int address = 0; char text [16*3+1]; DBG (dbg_level, "%s", info); while (count) { char* t = text; int i = 0; while (i < 16 && count) { t += sprintf (t, "%02x ", *data++); count--; i++; } *--t = 0; DBG (dbg_level, " [%08x] %s\n", address, text); address += 16; } } 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, (int16_t)get_double(nvram->flatbed_leading_edge)); DBG (dbg_level, "%s: flatbed side edge: %d\n", func, (int16_t)get_double(nvram->flatbed_side_edge)); DBG (dbg_level, "%s: ADF leading edge: %d\n", func, (int16_t)get_double(nvram->adf_leading_edge)); DBG (dbg_level, "%s: ADF side edge: %d\n", func, (int16_t)get_double(nvram->adf_side_edge)); DBG (dbg_level, "%s: ADF rear leading edge: %d\n", func, (int16_t)get_double(nvram->adf_rear_leading_edge)); DBG (dbg_level, "%s: ADF rear side edge: %d\n", func, (int16_t)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)); DBG (dbg_level, "%s: auto feed: %d\n", func, nvram->auto_feed); DBG (dbg_level, "%s: roller count: %d\n", func, get_quad(nvram->roller_count)); DBG (dbg_level, "%s: multifeed count: %d\n", func, get_quad(nvram->multifeed_count)); DBG (dbg_level, "%s: jam count: %d\n", func, get_quad(nvram->jam_count)); DBG (dbg_level, "%s: identify info: '%.16s'\n", /* 16 chars max */ func, nvram->identify_info); DBG (dbg_level, "%s: formal_name: '%.16s'\n", /* 16 chars max */ func, nvram->formal_name); } static void debug_print_avdimen (int dbg_level, char* func, Avision_Dimensions* avdimen) { DBG (dbg_level, "%s: hw_xres: %d, hw_yres: %d, line_difference: %d\n", func, avdimen->hw_xres, avdimen->hw_yres, avdimen->line_difference); DBG (dbg_level, "%s: tlx: %ld, tly: %ld, brx: %ld, bry: %ld\n", func, avdimen->tlx, avdimen->tly, avdimen->brx, avdimen->bry); DBG (dbg_level, "%s: hw_pixel_per_line: %d, hw_lines: %d, hw_bytes_per_line: %d\n", func, avdimen->hw_pixels_per_line, avdimen->hw_lines, avdimen->hw_bytes_per_line); DBG (dbg_level, "%s: xres: %d, yres: %d\n", func, avdimen->xres, avdimen->yres); } static void debug_print_params (int dbg_level, char* func, SANE_Parameters* params) { DBG (dbg_level, "%s: pixel_per_line: %d, lines: %d\n", func, params->pixels_per_line, params->lines); DBG (dbg_level, "%s: depth: %d, bytes_per_line: %d\n", func, params->depth, params->bytes_per_line); } static void debug_print_calib_format (int dbg_level, char* func, uint8_t* result) { debug_print_raw (dbg_level + 2, "debug_print_calib_format:\n", result, 32); DBG (dbg_level, "%s: [0-1] pixels per line: %d\n", func, get_double ( &(result[0]) )); DBG (dbg_level, "%s: [2] bytes per channel: %d\n", func, result[2]); DBG (dbg_level, "%s: [3] line count: %d\n", func, result[3]); DBG (dbg_level, "%s: [4] FLAG:%s%s%s\n", func, result[4] == 1?" MUST_DO_CALIBRATION":"", result[4] == 2?" SCAN_IMAGE_DOES_CALIBRATION":"", result[4] == 3?" NEEDS_NO_CALIBRATION":""); DBG (dbg_level, "%s: [5] Ability1:%s%s%s%s%s%s%s%s\n", func, BIT(result[5],7)?" NONE_PACKED":" PACKED", BIT(result[5],6)?" INTERPOLATED":"", BIT(result[5],5)?" SEND_REVERSED":"", BIT(result[5],4)?" PACKED_DATA":"", BIT(result[5],3)?" COLOR_CALIB":"", BIT(result[5],2)?" DARK_CALIB":"", BIT(result[5],1)?" NEEDS_WHITE_BLACK_SHADING_DATA":"", BIT(result[5],0)?" NEEDS_CALIB_TABLE_CHANNEL_BY_CHANNEL":""); DBG (dbg_level, "%s: [6] R gain: %d\n", func, result[6]); DBG (dbg_level, "%s: [7] G gain: %d\n", func, result[7]); DBG (dbg_level, "%s: [8] B gain: %d\n", func, result[8]); DBG (dbg_level, "%s: [9-10] R shading target: %x\n", func, get_double ( &(result[9]) ) ); DBG (dbg_level, "%s: [11-12] G shading target: %x\n", func, get_double ( &(result[11]) ) ); DBG (dbg_level, "%s: [13-14] B shading target: %x\n", func, get_double ( &(result[13]) ) ); DBG (dbg_level, "%s: [15-16] R dark shading target: %x\n", func, get_double ( &(result[15]) ) ); DBG (dbg_level, "%s: [17-18] G dark shading target: %x\n", func, get_double ( &(result[17]) ) ); DBG (dbg_level, "%s: [19-20] B dark shading target: %x\n", func, get_double ( &(result[19]) ) ); DBG (dbg_level, "%s: [21] true-gray gain: %d\n", func, result[21]); DBG (dbg_level, "%s: [22-23] true-gray shading target: %x\n", func, get_double ( &(result[22]) ) ); DBG (dbg_level, "%s: [24-25] true-gray dark shading target: %x\n", func, get_double ( &(result[24]) ) ); } static void debug_print_accel_info (int dbg_level, char* func, uint8_t* result) { debug_print_raw (dbg_level + 2, "debug_print_accel_info:\n", result, 24); DBG (dbg_level, "%s: [0-1] acceleration step count: %d\n", func, get_double ( &(result[0]) )); DBG (dbg_level, "%s: [2-3] stable step count: %d\n", func, get_double ( &(result[2]) )); DBG (dbg_level, "%s: [4-7] table units: %d\n", func, get_quad ( &(result[4]) )); DBG (dbg_level, "%s: [8-11] base units: %d\n", func, get_quad ( &(result[8]) )); DBG (dbg_level, "%s: [12-13] start speed: %d\n", func, get_double ( &(result[12]) )); DBG (dbg_level, "%s: [14-15] target speed: %d\n", func, get_double ( &(result[14]) )); DBG (dbg_level, "%s: [16] ability:%s%s\n", func, BIT(result[16],0)?" TWO_BYTES_PER_ELEM":" SINGLE_BYTE_PER_ELEM", BIT(result[16],1)?" LOW_HIGH_ORDER":" HIGH_LOW_ORDER"); DBG (dbg_level, "%s: [17] table count: %d\n", func, result[17]); } static void debug_print_window_descriptor (int dbg_level, char* func, command_set_window_window* window) { debug_print_raw (dbg_level + 1, "window_data_header: \n", (uint8_t*)(&window->header), sizeof(window->header)); debug_print_raw (dbg_level + 1, "window_descriptor: \n", (uint8_t*)(&window->descriptor), sizeof(*window) - sizeof(window->header)); DBG (dbg_level, "%s: [0] window_id: %d\n", func, window->descriptor.winid); DBG (dbg_level, "%s: [2-3] x-axis res: %d\n", func, get_double (window->descriptor.xres)); DBG (dbg_level, "%s: [4-5] y-axis res: %d\n", func, get_double (window->descriptor.yres)); DBG (dbg_level, "%s: [6-9] x-axis upper left: %d\n", func, get_quad (window->descriptor.ulx)); DBG (dbg_level, "%s: [10-13] y-axis upper left: %d\n", func, get_quad (window->descriptor.uly)); DBG (dbg_level, "%s: [14-17] window width: %d\n", func, get_quad (window->descriptor.width)); DBG (dbg_level, "%s: [18-21] window length: %d\n", func, get_quad (window->descriptor.length)); DBG (dbg_level, "%s: [22] brightness: %d\n", func, window->descriptor.brightness); DBG (dbg_level, "%s: [23] threshold: %d\n", func, window->descriptor.threshold); DBG (dbg_level, "%s: [24] contrast: %d\n", func, window->descriptor.contrast); DBG (dbg_level, "%s: [25] image composition: %x\n", func, window->descriptor.image_comp); DBG (dbg_level, "%s: [26] bits per channel: %d\n", func, window->descriptor.bpc); DBG (dbg_level, "%s: [27-28] halftone pattern: %x\n", func, get_double (window->descriptor.halftone)); DBG (dbg_level, "%s: [29] padding_and_bitset: %x\n", func, window->descriptor.padding_and_bitset); DBG (dbg_level, "%s: [30-31] bit ordering: %x\n", func, get_double (window->descriptor.bitordering)); DBG (dbg_level, "%s: [32] compression type: %x\n", func, window->descriptor.compr_type); DBG (dbg_level, "%s: [33] compression argument: %x\n", func, window->descriptor.compr_arg); DBG (dbg_level, "%s: [34-35] paper length: %x\n", func, get_double (window->descriptor.paper_length) ); DBG (dbg_level, "%s: [40] vendor id: %x\n", func, window->descriptor.vendor_specific); DBG (dbg_level, "%s: [41] param length: %d\n", func, window->descriptor.paralen); DBG (dbg_level, "%s: [42] bitset1: %x\n", func, window->avision.bitset1); DBG (dbg_level, "%s: [43] highlight: %d\n", func, window->avision.highlight); DBG (dbg_level, "%s: [44] shadow: %d\n", func, window->avision.shadow); DBG (dbg_level, "%s: [45-46] line-width: %d\n", func, get_double (window->avision.line_width)); DBG (dbg_level, "%s: [47-48] line-count: %d\n", func, get_double (window->avision.line_count)); DBG (dbg_level, "%s: [49] bitset2: %x\n", func, window->avision.type.normal.bitset2); DBG (dbg_level, "%s: [50] ir exposure time: %x\n", func, window->avision.type.normal.ir_exposure_time); DBG (dbg_level, "%s: [51-52] r exposure: %x\n", func, get_double (window->avision.type.normal.r_exposure_time)); DBG (dbg_level, "%s: [53-54] g exposure: %x\n", func, get_double (window->avision.type.normal.g_exposure_time)); DBG (dbg_level, "%s: [55-56] b exposure: %x\n", func, get_double (window->avision.type.normal.b_exposure_time)); DBG (dbg_level, "%s: [57] bitset3: %x\n", func, window->avision.type.normal.bitset3); DBG (dbg_level, "%s: [58] auto focus: %d\n", func, window->avision.type.normal.auto_focus); DBG (dbg_level, "%s: [59] line-width (MSB): %d\n", func, window->avision.type.normal.line_width_msb); DBG (dbg_level, "%s: [60] line-count (MSB): %d\n", func, window->avision.type.normal.line_count_msb); DBG (dbg_level, "%s: [61] background lines: %d\n", func, window->avision.type.normal.background_lines); } static SANE_String_Const avision_strdatatypecode (uint8_t datatypecode) { static char buf[80]; switch (datatypecode) { case AVISION_DATATYPECODE_LIGHT_STATUS: return "Light status"; case AVISION_DATATYPECODE_POWER_SAVING_TIMER: return "Power saving timer"; case AVISION_DATATYPECODE_FIRMWARE_STATUS: return "Firmware status"; case AVISION_DATATYPECODE_FLASH_RAM_INFO: return "Flash RAM info"; case AVISION_DATATYPECODE_READ_NVRAM_DATA: return "Read NVRAM data"; case AVISION_DATATYPECODE_SEND_NVRAM_DATA: return "Send NVRAM data"; case AVISION_DATATYPECODE_FLASH_DATA: return "Flash data"; case AVISION_DATATYPECODE_UNKNOWN: return "Unknown"; case AVISION_DATATYPECODE_DETECT_ACCESSORIES: return "Detect accessories"; case AVISION_DATATYPECODE_BUTTON_STATUS: return "Button status"; case AVISION_DATATYPECODE_FILM_HOLDER_SENSE: return "Film holder sense"; case AVISION_DATATYPECODE_READ_DUPLEX_INFO: return "Read duplex info"; case AVISION_DATATYPECODE_READ_GENERAL_ABILITY_PARAM: return "Read general ability/parameter"; case AVISION_DATATYPECODE_ATTACH_TRUNCATE_HEAD: return "Attach/Truncate head (left) of scan length"; case AVISION_DATATYPECODE_ATTACH_TRUNCATE_TAIL: return "Attach/Truncate tail (right) of scan length"; case AVISION_DATATYPECODE_GET_CALIBRATION_FORMAT: return "Get calibration format"; case AVISION_DATATYPECODE_DOWNLOAD_GAMMA_TABLE: return "Download gamma table"; case AVISION_DATATYPECODE_3X3_COLOR_MATRIX: return "3x3 color matrix"; case AVISION_DATATYPECODE_ACCELERATION_TABLE: return "Acceleration table"; case AVISION_DATATYPECODE_GET_BACKGROUND_RASTER: return "Get background raster"; case AVISION_DATATYPECODE_READ_IMAGE_DATA: return "Read image data"; default: /* non-reentrant, but better than nothing */ sprintf (buf, "Unknown data type code %02X", datatypecode); return buf; } } static int avision_strcmd (SANE_String buffer, size_t size, const void* cmd) { const uint8_t* m_cmd = (const uint8_t*)cmd; uint8_t opc = m_cmd[0]; uint8_t datatypecode = m_cmd[2]; switch (opc) { case AVISION_SCSI_TEST_UNIT_READY: return snprintf (buffer, size, "Test unit ready"); case AVISION_SCSI_REQUEST_SENSE: return snprintf (buffer, size, "Request sense"); case AVISION_SCSI_MEDIA_CHECK: return snprintf (buffer, size, "Media check"); case AVISION_SCSI_INQUIRY: return snprintf (buffer, size, "Inquiry"); case AVISION_SCSI_MODE_SELECT: return snprintf (buffer, size, "Mode select"); case AVISION_SCSI_RESERVE_UNIT: return snprintf (buffer, size, "Reserve unit"); case AVISION_SCSI_RELEASE_UNIT: return snprintf (buffer, size, "Release unit"); case AVISION_SCSI_SCAN: return snprintf (buffer, size, "Scan"); case AVISION_SCSI_SET_WINDOW: return snprintf (buffer, size, "Set window"); case AVISION_SCSI_READ: return snprintf (buffer, size, "Read (%s)", avision_strdatatypecode (datatypecode)); case AVISION_SCSI_SEND: return snprintf (buffer, size, "Send (%s)", avision_strdatatypecode (datatypecode)); case AVISION_SCSI_OBJECT_POSITION: return snprintf (buffer, size, "Object position"); case AVISION_SCSI_GET_DATA_STATUS: return snprintf (buffer, size, "Get data status"); default: return snprintf (buffer, size, "Unknown OPC %d", opc); } } static int write_pnm_header (FILE* f, color_mode m, int depth, int width, int height) { int maxval = (1 << depth) - 1; const char* hdr_str = NULL; /* construct PNM header */ switch (m) { case AV_THRESHOLDED: case AV_DITHERED: hdr_str = "P4\n%d %d\n"; break; case AV_GRAYSCALE: case AV_GRAYSCALE12: case AV_GRAYSCALE16: hdr_str = "P5\n%d %d\n%d\n"; break; case AV_TRUECOLOR: case AV_TRUECOLOR12: case AV_TRUECOLOR16: hdr_str = "P6\n%d %d\n%d\n"; break; case AV_COLOR_MODE_LAST: ; /* silence compiler warning */ } return fprintf (f, hdr_str, width, height, maxval); } static SANE_Status sense_handler (int fd, u_char* sense, void* arg) { SANE_Status status = SANE_STATUS_IO_ERROR; /* default case */ char* text; char textbuf[64]; uint8_t error_code = sense[0] & 0x7f; uint8_t sense_key = sense[2] & 0xf; uint8_t additional_sense = sense[7]; (void) fd; /* silence gcc */ (void) arg; /* silence gcc */ DBG (3, "sense_handler:\n"); switch (error_code) { case 0x70: text = "standard sense"; break; case 0x7f: text = "Avision-specific sense"; break; default: text = "unknown sense"; } debug_print_raw (1, "sense_handler: data:\n", sense, 8 + additional_sense); /* request valid? */ if (! (sense[0] & (1<<7))) { DBG (1, "sense_handler: sense not valid ...\n"); return status; } switch (sense_key) { case 0x00: status = SANE_STATUS_GOOD; text = "ok ?!?"; break; case 0x02: text = "NOT READY"; break; case 0x03: text = "MEDIUM ERROR (mostly ADF)"; status = SANE_STATUS_JAMMED; break; case 0x04: text = "HARDWARE ERROR"; break; case 0x05: text = "ILLEGAL REQUEST"; break; case 0x06: text = "UNIT ATTENTION"; break; case 0x09: text = "VENDOR SPECIFIC"; break; case 0x0b: text = "ABORTED COMMAND"; status = SANE_STATUS_CANCELLED; /* AV610C2 cancel button */ break; default: sprintf (textbuf, "got unknown sense code 0x%02x", (int)sense_key); text = textbuf; } DBG (1, "sense_handler: sense code: %s\n", text); if (sense[2] & (1<<6)) DBG (1, "sense_handler: end of scan\n"); else DBG (1, "sense_handler: scan has not yet been completed\n"); if (sense[2] & (1<<5)) DBG (1, "sense_handler: incorrect logical length\n"); else DBG (1, "sense_handler: correct logical length\n"); { uint8_t asc = sense[12]; uint8_t ascq = sense[13]; #define ADDITIONAL_SENSE(asc,ascq,txt) \ case ( (asc << 8) + ascq): text = txt; break switch ( (asc << 8) + ascq ) { /* normal */ ADDITIONAL_SENSE (0x00,0x00, "No additional sense information"); ADDITIONAL_SENSE (0x00,0x06, "I/O process terminated"); ADDITIONAL_SENSE (0x15,0x01, "Mechanical positioning error"); ADDITIONAL_SENSE (0x15,0x02, "Flatbed Home Sensor Error (OKI only"); ADDITIONAL_SENSE (0x15,0x03, "ADF Home Sensor Error (OKI only)"); ADDITIONAL_SENSE (0x15,0x04, "Lock Error (OKI only)"); ADDITIONAL_SENSE (0x1a,0x00, "parameter list length error"); ADDITIONAL_SENSE (0x20,0x00, "Invalid command"); ADDITIONAL_SENSE (0x24,0x00, "Invalid field in CDB"); ADDITIONAL_SENSE (0x25,0x00, "Logical unit not supported"); ADDITIONAL_SENSE (0x26,0x00, "Invalid field in parameter list"); ADDITIONAL_SENSE (0x26,0x01, "parameter not supported"); ADDITIONAL_SENSE (0x26,0x02, "parameter value invalid"); ADDITIONAL_SENSE (0x29,0x00, "Power-on, reset or bus device reset occurred"); ADDITIONAL_SENSE (0x2c,0x02, "Invalid combination of window specified"); ADDITIONAL_SENSE (0x2f,0x00, "Command cleared by another initiator"); ADDITIONAL_SENSE (0x3D,0x00, "Invalid Bit in Identify Message"); ADDITIONAL_SENSE (0x43,0x00, "Message error"); ADDITIONAL_SENSE (0x44,0x00, "Internal target failure"); ADDITIONAL_SENSE (0x44,0x01, "Flatbed DRAM Error(OKI only)"); ADDITIONAL_SENSE (0x44,0x02, "ADF DRAM Error(OKI only)"); ADDITIONAL_SENSE (0x44,0x03, "Write NVRAM Error"); ADDITIONAL_SENSE (0x47,0x00, "SCSI parity error"); ADDITIONAL_SENSE (0x49,0x00, "Invalid message error"); ADDITIONAL_SENSE (0x60,0x00, "Lamp failure"); ADDITIONAL_SENSE (0x60,0x01, "Flatbed Lamp error (Oki only)"); ADDITIONAL_SENSE (0x60,0x02, "ADF lamp error (Oki only)"); ADDITIONAL_SENSE (0x62,0x00, "Scan head positioning error"); ADDITIONAL_SENSE (0x80,0x01, "ADF paper jam"; status = SANE_STATUS_JAMMED); ADDITIONAL_SENSE (0x80,0x02, "ADF cover open"; status = SANE_STATUS_COVER_OPEN); ADDITIONAL_SENSE (0x80,0x03, "ADF chute empty"; status = SANE_STATUS_NO_DOCS); ADDITIONAL_SENSE (0x80,0x04, "ADF paper end"; status = SANE_STATUS_EOF); ADDITIONAL_SENSE (0x80,0x05, "Multi-feed (AV220,Kodak)"); ADDITIONAL_SENSE (0x80,0x06, "ADF prefeeding (OKI only)"); ADDITIONAL_SENSE (0x80,0x07, "Flatbed cover open (OKI only)"; status = SANE_STATUS_COVER_OPEN); ADDITIONAL_SENSE (0x80,0x08, "FW module doesn't match with scanner"); ADDITIONAL_SENSE (0x80,0x09, "Papers fed from multiple trays (DM272)"); ADDITIONAL_SENSE (0x80,0x0A, "ADF Paper Start"); ADDITIONAL_SENSE (0x80,0x0B, "Multiple ADF paper End and Start"); ADDITIONAL_SENSE (0x80,0x0C, "Multiple ADF paper End"); /* film scanner */ ADDITIONAL_SENSE (0x81,0x00, "ADF/MFP front door open"; status = SANE_STATUS_COVER_OPEN); ADDITIONAL_SENSE (0x81,0x01, "ADF holder cartridge open"; status = SANE_STATUS_COVER_OPEN); ADDITIONAL_SENSE (0x81,0x02, "ADF no film inside"; status = SANE_STATUS_NO_DOCS); ADDITIONAL_SENSE (0x81,0x03, "ADF initial load fail"); ADDITIONAL_SENSE (0x81,0x04, "ADF film end"; status = SANE_STATUS_NO_DOCS); ADDITIONAL_SENSE (0x81,0x05, "ADF forward feed error"); ADDITIONAL_SENSE (0x81,0x06, "ADF rewind error"); ADDITIONAL_SENSE (0x81,0x07, "ADF set unload"); ADDITIONAL_SENSE (0x81,0x08, "ADF adapter error"); ADDITIONAL_SENSE (0xA0,0x01, "Filter Positioning Error"); ADDITIONAL_SENSE (0x90,0x00, "Scanner busy (FW busy)"); default: sprintf (textbuf, "Unknown sense code asc: 0x%02x, ascq: 0x%02x", (int)asc, (int)ascq); text = textbuf; } #undef ADDITIONAL_SENSE DBG (1, "sense_handler: sense code: %s\n", text); /* sense code specific for invalid request * it is possible to get a detailed error location here ;-)*/ if (sense_key == 0x05) { if (sense[15] & (1<<7) ) { if (sense[15] & (1<<6) ) DBG (1, "sense_handler: error in command parameter\n"); else DBG (1, "sense_handler: error in data parameter\n"); DBG (1, "sense_handler: error in parameter byte: %d, %x\n", get_double(&(sense[16])), get_double(&(sense[16]))); /* bit pointer valid ?*/ if (sense[15] & (1<<3) ) DBG (1, "sense_handler: error in command parameter\n"); else DBG (1, "sense_handler: bit pointer invalid\n"); } } } return status; } /* * Avision scsi/usb multiplexers - to keep the code clean: */ static SANE_Status avision_usb_status (Avision_Connection* av_con, int retry, int timeout) { SANE_Status status = 0; uint8_t usb_status[1] = {0}; size_t count = 0; int t_retry = retry; #define valid_status(status,a) (status == SANE_STATUS_GOOD ? a : 0) DBG (4, "avision_usb_status: timeout %d, %d retries\n", timeout, retry); #ifndef HAVE_SANEI_USB_SET_TIMEOUT #error "You must update include/sane/sanei_usb.h and sanei/sanei_usb.c accordingly!" #endif sanei_usb_set_timeout (timeout); /* 1st try bulk transfers - they are more lightweight ... */ for (; count == 0 && (av_con->usb_status == AVISION_USB_BULK_STATUS || av_con->usb_status == AVISION_USB_UNTESTED_STATUS) && retry > 0; --retry) { count = sizeof (usb_status); DBG (5, "==> (bulk read) going down ...\n"); status = sanei_usb_read_bulk (av_con->usb_dn, usb_status, &count); DBG (5, "<== (bulk read) got: %ld, status: %d\n", (u_long)count, valid_status(status, usb_status[0])); if (count > 0) { av_con->usb_status = AVISION_USB_BULK_STATUS; } } /* reset retry count ... */ retry = t_retry; /* 2nd try interrupt status read - if not yet disabled */ for (; count == 0 && (av_con->usb_status == AVISION_USB_INT_STATUS || av_con->usb_status == AVISION_USB_UNTESTED_STATUS) && retry > 0; --retry) { count = sizeof (usb_status); DBG (5, "==> (interrupt read) going down ...\n"); status = sanei_usb_read_int (av_con->usb_dn, usb_status, &count); DBG (5, "<== (interrupt read) got: %ld, status: %d\n", (u_long)count, valid_status(status, usb_status[0])); if (count > 0) av_con->usb_status = AVISION_USB_INT_STATUS; } if (status != SANE_STATUS_GOOD) return status; if (count == 0) return SANE_STATUS_IO_ERROR; /* 0 = ok, 2 => request sense, 8 ==> busy, else error */ switch (usb_status[0]) { case AVISION_USB_GOOD: return SANE_STATUS_GOOD; case AVISION_USB_REQUEST_SENSE: DBG (2, "avision_usb_status: Needs to request sense!\n"); return SANE_STATUS_INVAL; case AVISION_USB_BUSY: DBG (2, "avision_usb_status: Busy!\n"); return SANE_STATUS_DEVICE_BUSY; default: DBG (1, "avision_usb_status: Unknown!\n"); return SANE_STATUS_INVAL; } } static SANE_Status avision_open (const char* device_name, Avision_Connection* av_con, SANEI_SCSI_Sense_Handler sense_handler, void *sense_arg) { if (av_con->connection_type == AV_SCSI) { return sanei_scsi_open (device_name, &(av_con->scsi_fd), sense_handler, sense_arg); } else { SANE_Status status; status = sanei_usb_open (device_name, &(av_con->usb_dn)); return status; } } static SANE_Status avision_open_extended (const char* device_name, Avision_Connection* av_con, SANEI_SCSI_Sense_Handler sense_handler, void *sense_arg, int *buffersize) { if (av_con->connection_type == AV_SCSI) { return sanei_scsi_open_extended (device_name, &(av_con->scsi_fd), sense_handler, sense_arg, buffersize); } else { SANE_Status status; status = sanei_usb_open (device_name, &(av_con->usb_dn)); return status; } } static void avision_close (Avision_Connection* av_con) { if (av_con->connection_type == AV_SCSI) { sanei_scsi_close (av_con->scsi_fd); av_con->scsi_fd = -1; } else { sanei_usb_close (av_con->usb_dn); av_con->usb_dn = -1; } } static SANE_Bool avision_is_open (Avision_Connection* av_con) { if (av_con->connection_type == AV_SCSI) { return av_con->scsi_fd >= 0; } else { return av_con->usb_dn >= 0; } } static SANE_Status avision_cmd (Avision_Connection* av_con, const void* cmd, size_t cmd_size, const void* src, size_t src_size, void* dst, size_t* dst_size) { SANE_Char strcmd[80]; avision_strcmd (strcmd, sizeof (strcmd), cmd); DBG (7, "avision_cmd: %s\n", strcmd); if (av_con->connection_type == AV_SCSI) { return sanei_scsi_cmd2 (av_con->scsi_fd, cmd, cmd_size, src, src_size, dst, dst_size); } else { SANE_Status status = SANE_STATUS_GOOD; size_t i, count, out_count; /* 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; int status_timeout = STD_STATUS_TIMEOUT; /* simply to allow nicer code below */ const uint8_t* m_cmd = (const uint8_t*)cmd; const uint8_t* m_src = (const uint8_t*)src; uint8_t* m_dst = (uint8_t*)dst; /* may I vote for the possibility to use C99 ... */ #define min_usb_size 10 #define max_usb_size 256 * 1024 /* or 0x10000, used by AV Windows driver during background raster read, ... ? */ /* 1st send command data - at least 10 Bytes for USB scanners */ uint8_t enlarged_cmd [min_usb_size]; if (cmd_size < min_usb_size) { DBG (1, "filling command to have a length of 10, was: %lu\n", (u_long) cmd_size); memcpy (enlarged_cmd, m_cmd, cmd_size); memset (enlarged_cmd + cmd_size, 0, min_usb_size - cmd_size); m_cmd = enlarged_cmd; cmd_size = min_usb_size; } /* per command class timeout tweaks */ switch (m_cmd[0]) { case AVISION_SCSI_INQUIRY: read_timeout = 1000; /* quickly timeout on initial detection */ status_timeout = 1000; break; case AVISION_SCSI_TEST_UNIT_READY: read_timeout = 15000; /* quickly timeout on initial detection */ status_timeout = 15000; 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"); return SANE_STATUS_IO_ERROR; } count = cmd_size; 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); if (status != SANE_STATUS_GOOD || count != cmd_size) { DBG (3, "=== Got error %d trying to write, wrote: %ld. ===\n", status, (long)count); if (status != SANE_STATUS_GOOD) /* == SANE_STATUS_EOF) */ { DBG (3, "try to read status to clear the FIFO\n"); status = avision_usb_status (av_con, 1, 500); if (status != SANE_STATUS_GOOD) { DBG (3, "=== Got error %d trying to read status. ===\n", status); return SANE_STATUS_IO_ERROR; } else goto write_usb_cmd; } else { DBG (3, "Retrying to send command\n"); goto write_usb_cmd; } return SANE_STATUS_IO_ERROR; } /* 2nd send command data (if any) */ for (i = 0; i < src_size; ) { count = src_size - i; /* if (count > max_usb_size) count = max_usb_size; */ DBG (8, "try to write src, count: %lu.\n", (u_long) count); sanei_usb_set_timeout (write_timeout); status = sanei_usb_write_bulk (av_con->usb_dn, &(m_src[i]), &count); DBG (8, "wrote %lu bytes\n", (u_long) count); if (status == SANE_STATUS_GOOD) { i += count; } else { goto write_usb_cmd; } } /* 3rd: read the resulting data (payload) (if any) */ if (status == SANE_STATUS_GOOD && dst != NULL && *dst_size > 0) { out_count = 0; sanei_usb_set_timeout (read_timeout); while (out_count < *dst_size) { count = (*dst_size - out_count); DBG (8, "try to read %lu bytes\n", (u_long) count); status = sanei_usb_read_bulk(av_con->usb_dn, &(m_dst[out_count]), &count); DBG (8, "read %lu bytes\n", (u_long) count); if (count == 1 && (*dst_size - out_count > 1)) { DBG (1, "Got 1 byte - status? (%d) Resending.\n", m_dst[out_count]); goto write_usb_cmd; } else if (count > 0) { out_count += count; } else { DBG (1, "No data arrived.\n"); goto write_usb_cmd; } } } /* last: read the device status via a pseudo interrupt transfer * this is needed - otherwise the scanner will hang ... */ sanei_usb_set_timeout (status_timeout); status = avision_usb_status (av_con, /*retry*/ 1, status_timeout); /* next i/o hardening attempt - and yes this gets ugly ... */ if (status != SANE_STATUS_GOOD && status != SANE_STATUS_INVAL) goto write_usb_cmd; if (status == SANE_STATUS_INVAL) { struct { command_header header; uint8_t pad[4]; } sense_cmd; uint8_t sense_buffer[22]; DBG (3, "Error during status read!\n"); DBG (3, "=== Try to request sense ===\n"); /* we can not call avision_cmd recursively - we might ending in an endless recursion requesting sense for failing request sense transfers ...*/ memset (&sense_cmd, 0, sizeof (sense_cmd) ); memset (&sense_buffer, 0, sizeof (sense_buffer) ); sense_cmd.header.opc = AVISION_SCSI_REQUEST_SENSE; sense_cmd.header.len = sizeof (sense_buffer); count = sizeof(sense_cmd); DBG (8, "try to write %lu bytes\n", (u_long) count); sanei_usb_set_timeout (write_timeout); status = sanei_usb_write_bulk (av_con->usb_dn, (uint8_t*) &sense_cmd, &count); DBG (8, "wrote %lu bytes\n", (u_long) count); if (status != SANE_STATUS_GOOD) { DBG (3, "=== Got error %d trying to request sense! ===\n", status); } else { count = sizeof (sense_buffer); DBG (8, "try to read %lu bytes sense data\n", (u_long) count); sanei_usb_set_timeout (read_timeout); status = sanei_usb_read_bulk(av_con->usb_dn, sense_buffer, &count); DBG (8, "read %lu bytes sense data\n", (u_long) count); /* we need to read out the status from the scanner i/o buffer */ status = avision_usb_status (av_con, 1, status_timeout); /* some scanner return NEED_SENSE even after reading it */ if (status != SANE_STATUS_GOOD && status != SANE_STATUS_INVAL) DBG (3, "=== Got error %d trying to read sense! ===\n", status); else { /* read complete -> call our sense handler */ status = sense_handler (-1, sense_buffer, 0); } } /* end read sense data */ } /* end request sense */ return status; } /* end cmd usb */ } /* A bubble sort for the calibration. It only sorts the first third * and returns an average of the top 2/3 values. The input data is * 16bit big endian and the count is the count of the words - not * bytes! */ static uint16_t bubble_sort (uint8_t* sort_data, size_t count) { size_t i, j, limit, k; double sum = 0.0; limit = count / 3; for (i = 0; i < limit; ++i) { uint16_t ti = 0; uint16_t tj = 0; for (j = (i + 1); j < count; ++j) { ti = (uint16_t) get_double ((sort_data + i*2)); tj = (uint16_t) get_double ((sort_data + j*2)); if (ti > tj) { set_double ((sort_data + i*2), tj); set_double ((sort_data + j*2), ti); } } } for (k = 0, i = limit; i < count; ++i) { sum += get_double ((sort_data + i*2)); ++ k; } /* DBG (7, "bubble_sort: %d values for average\n", k); */ if (k > 0) /* if avg to compute */ return (uint16_t) (sum /(double) k); else return (uint16_t) (sum); /* always zero? */ } static SANE_Status add_color_mode (Avision_Device* dev, color_mode mode, SANE_String name) { int i; DBG (3, "add_color_mode: %d %s\n", mode, name); for (i = 0; i < AV_COLOR_MODE_LAST; ++i) { if (dev->color_list [i] == 0) { dev->color_list [i] = strdup (name); dev->color_list_num [i] = mode; return SANE_STATUS_GOOD; } else if (strcmp (dev->color_list [i], name) == 0) { /* already in list */ return SANE_STATUS_GOOD; } } DBG (3, "add_color_mode: failed\n"); return SANE_STATUS_NO_MEM; } static int last_color_mode (Avision_Device* dev) { int i = 1; while (dev->color_list [i] != 0 && i < AV_COLOR_MODE_LAST) ++i; /* we are off by one */ --i; return i; } static color_mode match_color_mode (Avision_Device* dev, SANE_String name) { int i; DBG (3, "match_color_mode:\n"); for (i = 0; i < AV_COLOR_MODE_LAST; ++i) { if (dev->color_list [i] != 0 && strcmp (dev->color_list [i], name) == 0) { DBG (3, "match_color_mode: found at %d mode: %d\n", i, dev->color_list_num [i]); return dev->color_list_num [i]; } } DBG (3, "match_color_mode: source mode invalid\n"); return AV_GRAYSCALE; } static SANE_Bool color_mode_is_shaded (color_mode mode) { return mode >= AV_GRAYSCALE; } static SANE_Bool color_mode_is_color (color_mode mode) { return mode >= AV_TRUECOLOR; } static SANE_Bool is_adf_scan (Avision_Scanner* s) { return s->hw->scanner_type == AV_SHEETFEED || (s->hw->scanner_type == AV_FLATBED && s->source_mode_dim == AV_ADF_DIM); } static SANE_Status add_source_mode (Avision_Device* dev, source_mode mode, SANE_String name) { int i; for (i = 0; i < AV_SOURCE_MODE_LAST; ++i) { if (dev->source_list [i] == 0) { dev->source_list [i] = strdup (name); dev->source_list_num [i] = mode; return SANE_STATUS_GOOD; } else if (strcmp (dev->source_list [i], name) == 0) { /* already in list */ return SANE_STATUS_GOOD; } } return SANE_STATUS_NO_MEM; } static source_mode match_source_mode (Avision_Device* dev, SANE_String name) { int i; DBG (3, "match_source_mode: \"%s\"\n", name); for (i = 0; i < AV_SOURCE_MODE_LAST; ++i) { if (dev->source_list [i] != 0 && strcmp (dev->source_list [i], name) == 0) { DBG (3, "match_source_mode: found at %d mode: %d\n", i, dev->source_list_num [i]); return dev->source_list_num [i]; } } DBG (3, "match_source_mode: source mode invalid\n"); return AV_NORMAL; } static source_mode_dim match_source_mode_dim (source_mode sm) { DBG (3, "match_source_mode_dim: %d\n", sm); switch (sm) { case AV_NORMAL: return AV_NORMAL_DIM; case AV_TRANSPARENT: return AV_TRANSPARENT_DIM; case AV_ADF: case AV_ADF_REAR: case AV_ADF_DUPLEX: return AV_ADF_DIM; default: DBG (3, "match_source_mode_dim: source mode invalid\n"); return AV_NORMAL_DIM; } } static int get_pixel_boundary (Avision_Scanner* s) { Avision_Device* dev = s->hw; int boundary; switch (s->c_mode) { case AV_TRUECOLOR: case AV_TRUECOLOR12: case AV_TRUECOLOR16: boundary = dev->inquiry_color_boundary; break; case AV_GRAYSCALE: case AV_GRAYSCALE12: case AV_GRAYSCALE16: boundary = dev->inquiry_gray_boundary; break; case AV_DITHERED: if (dev->inquiry_asic_type != AV_ASIC_C5) boundary = 32; else boundary = dev->inquiry_dithered_boundary; break; case AV_THRESHOLDED: if (dev->inquiry_asic_type != AV_ASIC_C5) boundary = 32; else boundary = dev->inquiry_thresholded_boundary; break; default: boundary = 8; } return boundary; } static SANE_Status compute_parameters (Avision_Scanner* s) { Avision_Device* dev = s->hw; int boundary = get_pixel_boundary (s); SANE_Bool gray_mode = color_mode_is_shaded (s->c_mode); /* interlaced duplex (higher end) or flipping paper (HP8xxx)? */ s->avdimen.interlaced_duplex = s->source_mode == AV_ADF_DUPLEX && dev->inquiry_duplex_interlaced; /* for infra-red we use the same code path es for interlaced duplex */ if (s->val[OPT_IR].w) s->avdimen.interlaced_duplex = 1; #ifdef AVISION_ENHANCED_SANE /* quick fix for Microsoft Office Products ... */ switch (s->c_mode) { case AV_THRESHOLDED: case AV_DITHERED: /* our backend already has this restriction - so this line is for documentation purposes only */ boundary = boundary > 32 ? boundary : 32; break; case AV_GRAYSCALE: case AV_GRAYSCALE12: case AV_GRAYSCALE16: boundary = boundary > 4 ? boundary : 4; break; case AV_TRUECOLOR: case AV_TRUECOLOR12: case AV_TRUECOLOR16: /* 12 bytes for 24bit color - 48bit is untested w/ Office */ boundary = boundary > 4 ? boundary : 4; break; } #endif DBG (3, "sane_compute_parameters:\n"); DBG (3, "sane_compute_parameters: boundary %d, gray_mode: %d, \n", boundary, gray_mode); /* TODO: Implement different x/y resolutions support */ s->avdimen.xres = s->val[OPT_RESOLUTION].w; s->avdimen.yres = s->val[OPT_RESOLUTION].w; /* soft scale ? */ if (dev->hw->feature_type & AV_SOFT_SCALE) { /* find supported hardware resolution */ const int* hw_res; const int* hw_res_list = dev->inquiry_asic_type == AV_ASIC_C5 ? hw_res_list_c5 : hw_res_list_generic; for (hw_res = hw_res_list; *hw_res && *hw_res < s->avdimen.xres; ++hw_res) /* just iterate */; s->avdimen.hw_xres = *hw_res; for (hw_res = hw_res_list; *hw_res && *hw_res < s->avdimen.yres; ++hw_res) /* just iterate */; s->avdimen.hw_yres = *hw_res; DBG (3, "sane_compute_parameters: soft scale, hw res: %dx%d\n", s->avdimen.hw_xres, s->avdimen.hw_yres); if (!s->avdimen.hw_xres || ! s->avdimen.hw_yres) { DBG (1, "sane_compute_parameters: no matching HW res for: %dx%d\n", s->avdimen.xres, s->avdimen.yres); return SANE_STATUS_INVAL; } } else { s->avdimen.hw_xres = s->val[OPT_RESOLUTION].w; s->avdimen.hw_yres = s->val[OPT_RESOLUTION].w; } DBG (3, "sane_compute_parameters: tlx: %f, tly: %f, brx: %f, bry: %f\n", SANE_UNFIX (s->val[OPT_TL_X].w), SANE_UNFIX (s->val[OPT_TL_Y].w), SANE_UNFIX (s->val[OPT_BR_X].w), SANE_UNFIX (s->val[OPT_BR_Y].w)); /* window parameter in pixel */ s->avdimen.tlx = (long int) (s->avdimen.hw_xres * SANE_UNFIX (s->val[OPT_TL_X].w) / MM_PER_INCH); s->avdimen.tly = (long int) (s->avdimen.hw_yres * SANE_UNFIX (s->val[OPT_TL_Y].w) / MM_PER_INCH); s->avdimen.brx = (long int) (s->avdimen.hw_xres * SANE_UNFIX (s->val[OPT_BR_X].w) / MM_PER_INCH); s->avdimen.bry = (long int) (s->avdimen.hw_yres * SANE_UNFIX (s->val[OPT_BR_Y].w) / MM_PER_INCH); /* line difference */ if (color_mode_is_color (s->c_mode) && dev->inquiry_needs_software_colorpack && dev->inquiry_line_difference) { s->avdimen.line_difference = (dev->inquiry_line_difference * s->avdimen.hw_yres) / dev->inquiry_optical_res; s->avdimen.bry += 2 * s->avdimen.line_difference; /* limit bry + line_difference to real scan boundary */ { long y_max = (long int) (dev->inquiry_y_ranges[s->source_mode_dim] * s->avdimen.hw_yres / MM_PER_INCH); DBG (3, "sane_compute_parameters: y_max: %ld, bry: %ld, line_difference: %d\n", y_max, s->avdimen.bry, s->avdimen.line_difference); if (s->avdimen.bry + 2 * s->avdimen.line_difference > y_max) { DBG (1, "sane_compute_parameters: bry limited!\n"); s->avdimen.bry = y_max - 2 * s->avdimen.line_difference; } } } /* end if needs software colorpack */ else { s->avdimen.line_difference = 0; } /* add overscan */ if (dev->inquiry_tune_scan_length && is_adf_scan (s)) { /* some extra effort for precise rounding ... */ int overscan = (int) ((s->avdimen.hw_yres * (SANE_UNFIX (s->val[OPT_OVERSCAN_TOP].w) + SANE_UNFIX (s->val[OPT_OVERSCAN_BOTTOM].w)) + (MM_PER_INCH - 1) ) / MM_PER_INCH); DBG (3, "sane_compute_parameters: overscan lines: %d\n", overscan); s->avdimen.bry += overscan; } /* ADF offset compensation Calculate offsets for skipping lines later with considering overscan which applies to both, front and rear. The difference needs to be cut off on the other side. */ if (dev->adf_offset_compensation && s->avdimen.interlaced_duplex) { /* ADF Duplex scan */ struct { mm_offset front; mm_offset rear; } offsets = {{0, 0}, {0, 0}}; double overscan; double bry_offset = 0; /* top */ overscan = fmax(0, fmax(dev->hw->offset.duplex.front.top, dev->hw->offset.duplex.rear.top)); offsets.front.top += overscan - dev->hw->offset.duplex.front.top; offsets.rear.top += overscan - dev->hw->offset.duplex.rear.top; bry_offset += overscan; /* bottom */ overscan = fmax(0, fmax(dev->hw->offset.duplex.front.bottom, dev->hw->offset.duplex.rear.bottom)); offsets.front.bottom += overscan - dev->hw->offset.duplex.front.bottom; offsets.rear.bottom += overscan - dev->hw->offset.duplex.rear.bottom; bry_offset += overscan; /* first page offset */ if (dev->hw->feature_type & AV_MULTI_SHEET_SCAN) { /* only applies to multi-sheet-scan */ if (dev->hw->offset.first > 0) { /* move down: - add top overscan in send_tune_scan_length (effective for all pages!) - skip bottom lines at page n=0, front and rear - skip top lines at page n>0, front and rear */ if (s->page == 0) { offsets.front.bottom += dev->hw->offset.first; offsets.rear.bottom += dev->hw->offset.first; } else { offsets.front.top += dev->hw->offset.first; offsets.rear.top += dev->hw->offset.first; } } else if (dev->hw->offset.first < 0) { /* move up: - add bottom overscan in send_tune_scan_length (effective for all pages!) - skip top lines at page n=0, front and rear - skip bottom lines at page n>0, front and rear */ if (s->page == 0) { offsets.front.top += fabs(dev->hw->offset.first); offsets.rear.top += fabs(dev->hw->offset.first); } else { offsets.front.bottom += fabs(dev->hw->offset.first); offsets.rear.bottom += fabs(dev->hw->offset.first); } } bry_offset += fabs(dev->hw->offset.first); } /* convert to lines */ s->avdimen.offset.front.top = (int) ( offsets.front.top * s->avdimen.yres / MM_PER_INCH ); s->avdimen.offset.front.bottom = (int) ( offsets.front.bottom * s->avdimen.yres / MM_PER_INCH ); s->avdimen.offset.rear.top = (int) ( offsets.rear.top * s->avdimen.yres / MM_PER_INCH ); s->avdimen.offset.rear.bottom = (int) ( offsets.rear.bottom * s->avdimen.yres / MM_PER_INCH ); /* add overscan to bry (hw_lines) */ s->avdimen.bry += (long) ( bry_offset * s->avdimen.hw_yres / MM_PER_INCH ); DBG (1, "sane_compute_parameters: front offset: top: %d!\n", s->avdimen.offset.front.top); DBG (1, "sane_compute_parameters: front offset: bottom: %d!\n", s->avdimen.offset.front.bottom); DBG (1, "sane_compute_parameters: rear offset: top: %d!\n", s->avdimen.offset.rear.top); DBG (1, "sane_compute_parameters: rear offset: bottom: %d!\n", s->avdimen.offset.rear.bottom); } else if (dev->adf_offset_compensation && s->source_mode == AV_ADF) { /* ADF front scan */ mm_offset offsets = {0, 0}; double bry_offset = 0; /* top */ if (dev->hw->offset.front.top < 0) offsets.top += fabs(dev->hw->offset.front.top); else bry_offset += dev->hw->offset.front.top; /* bottom */ if (dev->hw->offset.front.bottom < 0) offsets.bottom += fabs(dev->hw->offset.front.bottom); else bry_offset += dev->hw->offset.front.bottom; /* first page offset */ if (dev->hw->feature_type & AV_MULTI_SHEET_SCAN) { /* only applies to multi-sheet-scan */ if (dev->hw->offset.first > 0) { /* move down: - add top overscan in send_tune_scan_length (effective for all pages!) - skip bottom lines at page n=0 - skip top lines at page n>0 */ if (s->page == 0) offsets.bottom += dev->hw->offset.first; else offsets.top += dev->hw->offset.first; } else if (dev->hw->offset.first < 0) { /* move up: - add bottom overscan in send_tune_scan_length (effective for all pages!) - skip top lines at page n=0 - skip bottom lines at page n>0 */ if (s->page == 0) offsets.top += fabs(dev->hw->offset.first); else offsets.bottom += fabs(dev->hw->offset.first); } bry_offset += fabs(dev->hw->offset.first); } /* convert to lines */ s->avdimen.offset.front.top = (int) ( offsets.top * s->avdimen.yres / MM_PER_INCH ); s->avdimen.offset.front.bottom = (int) ( offsets.bottom * s->avdimen.yres / MM_PER_INCH ); /* add overscan to bry (hw_lines) */ s->avdimen.bry += (long) ( bry_offset * s->avdimen.hw_yres / MM_PER_INCH ); DBG (1, "sane_compute_parameters: front offset: top: %d!\n", s->avdimen.offset.front.top); DBG (1, "sane_compute_parameters: front offset: bottom: %d!\n", s->avdimen.offset.front.bottom); } else { s->avdimen.offset.front.top = 0; s->avdimen.offset.front.bottom = 0; s->avdimen.offset.rear.top = 0; s->avdimen.offset.rear.bottom = 0; } memset (&s->params, 0, sizeof (s->params)); s->avdimen.hw_pixels_per_line = (int) (s->avdimen.brx - s->avdimen.tlx); s->avdimen.hw_pixels_per_line -= s->avdimen.hw_pixels_per_line % boundary; s->avdimen.hw_lines = (int) (s->avdimen.bry - s->avdimen.tly - 2 * s->avdimen.line_difference); if (s->avdimen.interlaced_duplex && dev->scanner_type != AV_FILM) s->avdimen.hw_lines -= s->avdimen.hw_lines % dev->read_stripe_size; s->params.pixels_per_line = s->avdimen.hw_pixels_per_line * s->avdimen.xres / s->avdimen.hw_xres; s->params.lines = s->avdimen.hw_lines * s->avdimen.xres / s->avdimen.hw_xres; if (is_adf_scan (s)) /* we can't know how many lines we'll see with an ADF because that depends on the paper length */ s->params.lines = -1; if (s->c_mode == AV_THRESHOLDED || s->c_mode == AV_DITHERED) s->params.pixels_per_line -= s->params.pixels_per_line % 8; debug_print_avdimen (1, "sane_compute_parameters", &s->avdimen); switch (s->c_mode) { case AV_THRESHOLDED: s->params.format = SANE_FRAME_GRAY; s->avdimen.hw_bytes_per_line = s->avdimen.hw_pixels_per_line / 8; s->params.bytes_per_line = s->params.pixels_per_line / 8; s->params.depth = 1; break; case AV_DITHERED: s->params.format = SANE_FRAME_GRAY; s->avdimen.hw_bytes_per_line = s->avdimen.hw_pixels_per_line / 8; s->params.bytes_per_line = s->params.pixels_per_line / 8; s->params.depth = 1; break; case AV_GRAYSCALE: s->params.format = SANE_FRAME_GRAY; s->avdimen.hw_bytes_per_line = s->avdimen.hw_pixels_per_line; s->params.bytes_per_line = s->params.pixels_per_line; s->params.depth = 8; break; case AV_GRAYSCALE12: case AV_GRAYSCALE16: s->params.format = SANE_FRAME_GRAY; s->avdimen.hw_bytes_per_line = s->avdimen.hw_pixels_per_line * 2; s->params.bytes_per_line = s->params.pixels_per_line * 2; s->params.depth = 16; break; case AV_TRUECOLOR: s->params.format = SANE_FRAME_RGB; s->avdimen.hw_bytes_per_line = s->avdimen.hw_pixels_per_line * 3; s->params.bytes_per_line = s->params.pixels_per_line * 3; s->params.depth = 8; break; case AV_TRUECOLOR12: case AV_TRUECOLOR16: s->params.format = SANE_FRAME_RGB; s->avdimen.hw_bytes_per_line = s->avdimen.hw_pixels_per_line * 3 * 2; s->params.bytes_per_line = s->params.pixels_per_line * 3 * 2; s->params.depth = 16; break; default: DBG (1, "Invalid mode. %d\n", s->c_mode); return SANE_STATUS_INVAL; } /* end switch */ s->params.last_frame = SANE_TRUE; debug_print_params (1, "sane_compute_parameters", &s->params); return SANE_STATUS_GOOD; } static SANE_Status inquiry (Avision_Connection av_con, uint8_t* data, size_t len) { SANE_Status status; command_header inquiry; int try = 2; DBG (3, "inquiry: length: %ld\n", (long)len); memset (&inquiry, 0, sizeof(inquiry)); inquiry.opc = AVISION_SCSI_INQUIRY; inquiry.len = (uint8_t) len; do { size_t size = inquiry.len; DBG (3, "inquiry: inquiring ...\n"); status = avision_cmd (&av_con, &inquiry, sizeof (inquiry), 0, 0, data, &size); if (status == SANE_STATUS_GOOD && size == inquiry.len) break; DBG (1, "inquiry: inquiry failed (%s)\n", sane_strstatus (status)); --try; } while (try > 0); return status; } static SANE_Status wait_ready (Avision_Connection* av_con, int delay) { SANE_Status status; int try; for (try = 0; try < 10; ++ try) { DBG (3, "wait_ready: sending TEST_UNIT_READY\n"); status = avision_cmd (av_con, test_unit_ready, sizeof (test_unit_ready), 0, 0, 0, 0); sleep ((unsigned int) delay); switch (status) { default: /* Ignore errors while waiting for scanner to become ready. Some SCSI drivers return EIO while the scanner is returning to the home position. */ DBG (1, "wait_ready: test unit ready failed (%s)\n", sane_strstatus (status)); /* fall through */ case SANE_STATUS_DEVICE_BUSY: break; case SANE_STATUS_GOOD: return status; } } DBG (1, "wait_ready: timed out after %d attempts\n", try); return SANE_STATUS_INVAL; } static SANE_Status wait_4_light (Avision_Scanner* s) { Avision_Device* dev = s->hw; /* read stuff */ struct command_read rcmd; char* light_status[] = { "off", "on", "warming up", "needs warm up test", "light check error", "backlight on", "RESERVED" }; SANE_Status status; uint8_t result; int try; size_t size = 1; DBG (3, "wait_4_light: getting light status.\n"); memset (&rcmd, 0, sizeof (rcmd)); rcmd.opc = AVISION_SCSI_READ; rcmd.datatypecode = AVISION_DATATYPECODE_LIGHT_STATUS; /* get light status */ set_double (rcmd.datatypequal, dev->data_dq); set_triple (rcmd.transferlen, size); for (try = 0; try < 90; ++ try) { if (s->cancelled) { DBG (3, "wait_4_light: cancelled\n"); return SANE_STATUS_CANCELLED; } DBG (5, "wait_4_light: read bytes %lu\n", (u_long) size); status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd), 0, 0, &result, &size); if (status != SANE_STATUS_GOOD || size != sizeof (result)) { status = (status != SANE_STATUS_GOOD)? status: SANE_STATUS_IO_ERROR; DBG (1, "wait_4_light: read failed (%s)\n", sane_strstatus (status)); return status; } DBG (3, "wait_4_light: command is %d. Result is %d (%s)\n", status, result, light_status[(result>5)?6:result]); if (result == 1 || result == 5) { return SANE_STATUS_GOOD; } else if (dev->hw->feature_type & AV_LIGHT_CHECK_BOGUS) { DBG (3, "wait_4_light: scanner marked as returning bogus values in device-list!!\n"); return SANE_STATUS_GOOD; } else { struct command_send scmd; uint8_t light_on = 1; /* turn on the light */ DBG (3, "wait_4_light: setting light status.\n"); memset (&scmd, 0, sizeof (scmd)); scmd.opc = AVISION_SCSI_SEND; scmd.datatypecode = AVISION_DATATYPECODE_LIGHT_STATUS; /* send light status */ set_double (scmd.datatypequal, dev->data_dq); set_triple (scmd.transferlen, size); status = avision_cmd (&s->av_con, &scmd, sizeof (scmd), &light_on, sizeof (light_on), 0, 0); if (status != SANE_STATUS_GOOD) { DBG (1, "wait_4_light: send failed (%s)\n", sane_strstatus (status)); return status; } } sleep (1); } DBG (1, "wait_4_light: timed out after %d attempts\n", try); return SANE_STATUS_DEVICE_BUSY; } static SANE_Status set_power_save_time (Avision_Scanner* s, int time) { struct { struct command_send cmd; uint8_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 = AVISION_DATATYPECODE_POWER_SAVING_TIMER; /* 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 */ struct command_read rcmd; size_t size; SANE_Status status; firmware_status result; DBG (3, "get_firmware_status\n"); size = sizeof (result); memset (&rcmd, 0, sizeof (rcmd)); rcmd.opc = AVISION_SCSI_READ; rcmd.datatypecode = AVISION_DATATYPECODE_FIRMWARE_STATUS; /* firmware status */ set_double (rcmd.datatypequal, 0); /* dev->data_dq not available */ set_triple (rcmd.transferlen, size); status = avision_cmd (av_con, &rcmd, sizeof (rcmd), 0, 0, &result, &size); if (status != SANE_STATUS_GOOD || size != sizeof (result)) { status = (status != SANE_STATUS_GOOD)? status: SANE_STATUS_IO_ERROR; DBG (1, "get_firmware_status: read failed (%s)\n", sane_strstatus (status)); return (status); } debug_print_raw (6, "get_firmware_status: raw data:\n", (uint8_t*)&result, size); DBG (3, "get_firmware_status: [0] needs firmware %x\n", result.download_firmware); DBG (3, "get_firmware_status: [1] side edge: %d\n", get_double ( result.first_effective_pixel_flatbed )); DBG (3, "get_firmware_status: [3] side edge: %d\n", get_double ( result.first_effective_pixel_adf_front )); DBG (3, "get_firmware_status: [5] side edge: %d\n", get_double ( result.first_effective_pixel_adf_rear )); return SANE_STATUS_GOOD; } static SANE_Status get_flash_ram_info (Avision_Connection* av_con) { /* read stuff */ struct command_read rcmd; size_t size; SANE_Status status; uint8_t result[40]; DBG (3, "get_flash_ram_info\n"); size = sizeof (result); memset (&rcmd, 0, sizeof (rcmd)); rcmd.opc = AVISION_SCSI_READ; rcmd.datatypecode = AVISION_DATATYPECODE_FLASH_RAM_INFO; /* flash ram information */ set_double (rcmd.datatypequal, 0); /* dev->data_dq not available */ set_triple (rcmd.transferlen, size); status = avision_cmd (av_con, &rcmd, sizeof (rcmd), 0, 0, result, &size); if (status != SANE_STATUS_GOOD || size != sizeof (result)) { status = (status != SANE_STATUS_GOOD)? status: SANE_STATUS_IO_ERROR; DBG (1, "get_flash_ram_info: read failed (%s)\n", sane_strstatus (status)); return (status); } debug_print_raw (6, "get_flash_ram_info: raw data:\n", result, size); DBG (3, "get_flash_ram_info: [0] data type %x\n", result [0]); DBG (3, "get_flash_ram_info: [1] Ability1:%s%s%s%s%s%s%s%s\n", BIT(result[1],7)?" RESERVED_BIT7":"", BIT(result[1],6)?" RESERVED_BIT6":"", BIT(result[1],5)?" FONT(r/w)":"", BIT(result[1],4)?" FPGA(w)":"", BIT(result[1],3)?" FMDBG(r)":"", BIT(result[1],2)?" RAWLINE(r)":"", BIT(result[1],1)?" FIRMWARE(r/w)":"", BIT(result[1],0)?" CTAB(r/w)":""); DBG (3, "get_flash_ram_info: [2-5] size CTAB: %d\n", get_quad ( &(result[2]) ) ); DBG (3, "get_flash_ram_info: [6-9] size FIRMWARE: %d\n", get_quad ( &(result[6]) ) ); DBG (3, "get_flash_ram_info: [10-13] size RAWLINE: %d\n", get_quad ( &(result[10]) ) ); DBG (3, "get_flash_ram_info: [14-17] size FMDBG: %d\n", get_quad ( &(result[14]) ) ); DBG (3, "get_flash_ram_info: [18-21] size FPGA: %d\n", get_quad ( &(result[18]) ) ); DBG (3, "get_flash_ram_info: [22-25] size FONT: %d\n", get_quad ( &(result[22]) ) ); DBG (3, "get_flash_ram_info: [26-29] size RESERVED: %d\n", get_quad ( &(result[26]) ) ); DBG (3, "get_flash_ram_info: [30-33] size RESERVED: %d\n", get_quad ( &(result[30]) ) ); 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 = AVISION_DATATYPECODE_READ_NVRAM_DATA; /* 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, "get_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, size_t n) { SANE_Status status; size_t i = 0; int x; nvram_data nvram; uint8_t inquiry_result [AVISION_INQUIRY_SIZE_V1]; status = inquiry (s->av_con, inquiry_result, sizeof(inquiry_result)); if (status == SANE_STATUS_GOOD) { i += (size_t) snprintf (str+i, n-i, "Vendor: %.8s", inquiry_result+8); i += (size_t) snprintf (str+i, n-i, "\nModel: %.16s", inquiry_result+16); i += (size_t) 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 += (size_t) snprintf (str+i, n-i, "\nSerial: %.24s", nvram.serial); if (get_double(nvram.born_year)) i += (size_t) 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 (get_double(nvram.first_scan_year)) i += (size_t) 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 += (size_t) snprintf (str+i, n-i, "\nFlatbed scans: %d", x); x = get_quad (nvram.pad_scans); if (x) i += (size_t) snprintf (str+i, n-i, "\nPad scans: %d", x); x = get_quad (nvram.adf_simplex_scans); if (x) i += (size_t) snprintf (str+i, n-i, "\nADF simplex scans: %d", x); x = get_quad (nvram.adf_duplex_scans); if (x) i += (size_t) 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 send_nvram_data (Avision_Connection* av_con) { /* read stuff */ struct command_send scmd; size_t size; SANE_Status status; DBG (3, "send_nvram_data\n"); size = sizeof (c7_nvram); memset (&scmd, 0, sizeof (scmd)); scmd.opc = AVISION_SCSI_SEND; scmd.datatypecode = AVISION_DATATYPECODE_SEND_NVRAM_DATA; /* nvram data */ set_double (scmd.datatypequal, 0); /* dev->data_dq not available */ set_triple (scmd.transferlen, size); status = avision_cmd (av_con, &scmd, sizeof (scmd), &c7_nvram, size, 0, 0); if (status != SANE_STATUS_GOOD) { DBG (1, "send_nvram_data: send failed (%s)\n", sane_strstatus (status)); return (status); } return SANE_STATUS_GOOD; } static SANE_Status send_flash_ram_data (Avision_Connection* av_con) { /* read stuff */ struct command_send scmd; size_t size; SANE_Status status; DBG (3, "send_flash_ram_data\n"); size = sizeof (c7_flash_ram); memset (&scmd, 0, sizeof (scmd)); scmd.opc = AVISION_SCSI_SEND; scmd.datatypecode = AVISION_DATATYPECODE_FLASH_DATA; /* flash data */ set_double (scmd.datatypequal, 0); set_triple (scmd.transferlen, size); status = avision_cmd (av_con, &scmd, sizeof (scmd), &c7_flash_ram, size, 0, 0); if (status != SANE_STATUS_GOOD) { DBG (1, "send_flash_ram_data: send failed (%s)\n", sane_strstatus (status)); return (status); } return SANE_STATUS_GOOD; } #endif static SANE_Status adf_reset (Avision_Scanner* s) { SANE_Status status; Avision_Device* dev = s->hw; struct command_send scmd; struct command_read rcmd; uint8_t payload[4]; size_t size; size_t n; ssize_t i; DBG (3, "adf_reset\n"); /* loop twice */ for (i=1; i >= 0; i--) { n= (size_t) i; memset (&scmd, 0, sizeof (scmd)); memset (&payload, 0, sizeof (payload)); scmd.opc = AVISION_SCSI_SEND; scmd.datatypecode = AVISION_DATATYPECODE_UNKNOWN; /* unknown */ set_double (scmd.datatypequal, 0); size = 2; set_triple (scmd.transferlen, size); payload[1] = (uint8_t) (0x10 * i); /* write 0x10 the first time, 0x00 the second */ status = avision_cmd (&s->av_con, &scmd, sizeof (scmd), payload, size, 0, 0); if (status != SANE_STATUS_GOOD) { DBG (1, "adf_reset: write %zu failed (%s)\n", (2-i), sane_strstatus (status)); return (status); } DBG (3, "adf_reset: write %zu complete.\n", (2-i)); memset (&rcmd, 0, sizeof (rcmd)); memset (&payload, 0, sizeof (payload)); rcmd.opc = AVISION_SCSI_READ; rcmd.datatypecode = AVISION_DATATYPECODE_READ_NVRAM_DATA; /* Read NVRAM data */ set_double (rcmd.datatypequal, dev->data_dq); size = (size_t) (4 - i); /* read 3 bytes the first time, 4 the second */ set_triple (rcmd.transferlen, size); status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd), 0, 0, payload, &size); if (status != SANE_STATUS_GOOD || size != (4-n)) { status = (status != SANE_STATUS_GOOD)? status: SANE_STATUS_IO_ERROR; DBG (1, "adf_reset: read %zu failed (%s)\n", (4-n), sane_strstatus (status)); return (status); } debug_print_raw (3, "adf_reset: raw data:\n", payload, size); } return SANE_STATUS_GOOD; } static SANE_Status get_accessories_info (Avision_Scanner* s) { Avision_Device* dev = s->hw; int try = 1; /* read stuff */ struct command_read rcmd; size_t size; SANE_Status status; uint8_t result[8]; char* adf_model[] = { "Origami", "Oodles", "HP9930", "unknown" }; const int adf_models = sizeof (adf_model) / sizeof(char*) - 1; DBG (3, "get_accessories_info\n"); size = sizeof (result); memset (&rcmd, 0, sizeof (rcmd)); rcmd.opc = AVISION_SCSI_READ; rcmd.datatypecode = AVISION_DATATYPECODE_DETECT_ACCESSORIES; /* detect accessories */ set_double (rcmd.datatypequal, dev->data_dq); set_triple (rcmd.transferlen, size); /* after resetting the ADF unit, try reprobing it again */ RETRY: status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd), 0, 0, result, &size); if (status != SANE_STATUS_GOOD || size != sizeof (result)) { status = (status != SANE_STATUS_GOOD)? status: SANE_STATUS_IO_ERROR; DBG (1, "get_accessories_info: read failed (%s)\n", sane_strstatus (status)); return (status); } debug_print_raw (6, "get_accessories_info: raw data:\n", result, size); DBG (3, "get_accessories_info: [0] ADF: %x\n", result[0]); DBG (3, "get_accessories_info: [1] Light Box: %x\n", result[1]); DBG (3, "get_accessories_info: [2] ADF model: %d (%s)\n", result [2], adf_model[ (result[2] < adf_models) ? result[2] : adf_models ]); /* * Cope with ADF presence flag being present but the device *not* reporting * ADF capability. Maybe there are some devices that do that? [RL] * */ dev->inquiry_adf_present = result [0]; /* * Note: this feature_type check is a bit of a hack. * Only the HP Scanjet 8200 series supports this so it is code * specific to this family of scanners. [RL] * */ if (dev->hw->feature_type & AV_ADF_FLIPPING_DUPLEX) { if (result[0] == 1) { dev->inquiry_duplex = 1; dev->inquiry_duplex_interlaced = 0; } else if (result[0] == 0 && result[2] != 0 && !skip_adf) { /* Sometimes the scanner will report that there is no ADF attached, yet * an ADF model number will still be reported. This happens on the * HP8200 series and possibly others. In this case we need to reset the * the adf and try reading it again. Skip this if the configuration says * to do so, so that we don't fail out the scanner as being broken and * unsupported if there isn't actually an ADF present. * * Note further: Some models (like the ScanJet 8300) report that they have ADF * *capability* in the INQUIRY response but that doesn't necessarily mean that * an ADF is plugged in. In my case it has the lightbox accessory instead and * result[0] == FALSE. * Trying to reset the ADF 3 times is excessive and takes an unreasonable amount * of time on the 8300 with no ADF plugged in, so let's do it just once and if * it fails to report presence, then don't assume it is an error, just that * there is no ADF. [RL] * */ if (!try) { DBG ( 1, "get_accessories_info: Maximum retries attempted, ADF unresponsive.\n"); dev->inquiry_adf_present = SANE_FALSE; //return SANE_STATUS_UNSUPPORTED; } else { try--; DBG(3, "get_accessories_info: Found ADF model number but the ADF-present flag is not set. " "Trying to reset the ADF just in case it is there but unresponsive...\n"); status = adf_reset (s); if (status != SANE_STATUS_GOOD) { DBG (3, "get_accessories_info: Failed to reset ADF: %s\n", sane_strstatus (status)); return status; } DBG(1,"get_accessories_info: Waiting while ADF firmware resets...\n"); sleep (3); status = wait_ready (&s->av_con, 1); if (status != SANE_STATUS_GOOD) { DBG (1, "get_accessories_info: wait_ready() failed: %s\n", sane_strstatus (status)); return status; } goto RETRY; } } } /* only honor a 1, some scanner without adapter set 0xff */ if (result[1] == 1) dev->inquiry_light_box_present = 1; return SANE_STATUS_GOOD; } /* Returns a pointer to static char* strings or NULL for cancel (we do not want to start memcmp'ing for the cancel case). */ static const char* string_for_button (Avision_Scanner* s, uint8_t button) { static char buffer [16]; Avision_Device* dev = s->hw; /* dev->sane.model dev->inquiry_asic_type */ if (dev->inquiry_buttons == 1) goto return_scan; /* simplex / duplex buttons */ if (strcmp (dev->sane.vendor, "Xerox") == 0 || strcmp (dev->sane.vendor, "Visioneer") == 0 || strcmp (dev->sane.model, "AV121") == 0 || strcmp (dev->sane.model, "AV122") == 0 ) { switch (button) { case 1: return "simplex"; case 2: return "duplex"; } } if (strcmp (dev->sane.model, "AV210C2") == 0 || strcmp (dev->sane.model, "AV210D2+") == 0 || strcmp (dev->sane.model, "AV220C2") == 0 || strcmp (dev->sane.model, "AV610C2") == 0 ) { if (button == 1) return NULL; /* cancel */ else goto return_scan; } /* those are unique, right now */ if (strcmp (dev->sane.model, "AV610") == 0) { switch (button) { case 0: return "email"; case 1: return "copy"; case 2: return "scan"; } } /* last resort */ snprintf (buffer, sizeof (buffer), "button%d", button); return buffer; return_scan: return "scan"; } static SANE_Status get_button_status (Avision_Scanner* s) { Avision_Device* dev = s->hw; /* read stuff */ struct command_read rcmd; size_t size; SANE_Status status; /* was only 6 in an old SPEC - maybe we need a feature override :-( -ReneR */ struct { uint8_t press_state; uint8_t buttons[5]; uint8_t display; /* AV220 et.al. 7 segment LED display */ uint8_t reserved[9]; } result; unsigned int i; DBG (3, "get_button_status:\n"); size = sizeof (result); /* AV220 et.al. */ if (! (dev->hw->feature_type & AV_INT_BUTTON)) { memset (&rcmd, 0, sizeof (rcmd)); rcmd.opc = AVISION_SCSI_READ; rcmd.datatypecode = AVISION_DATATYPECODE_BUTTON_STATUS; /* button status */ set_double (rcmd.datatypequal, dev->data_dq); set_triple (rcmd.transferlen, size); status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd), 0, 0, (uint8_t*)&result, &size); if (status != SANE_STATUS_GOOD || size != sizeof (result)) { status = (status != SANE_STATUS_GOOD)? status: SANE_STATUS_IO_ERROR; DBG (1, "get_button_status: read failed (%s)\n", sane_strstatus (status)); return status; } } else { /* only try to read the first 8 bytes ...*/ size = 8; /* no SCSI equivalent */ /* either there was a button press and this completes quickly or there is no point waiting for a future press */ sanei_usb_set_timeout (100); /* 10th of a second */ DBG (5, "==> (interrupt read) going down ...\n"); status = sanei_usb_read_int (s->av_con.usb_dn, (uint8_t*)&result, &size); DBG (5, "==> (interrupt read) got: %ld\n", (long)size); if (status != SANE_STATUS_GOOD) { DBG (1, "get_button_status: interrupt read failed (%s)\n", sane_strstatus (status)); return SANE_STATUS_GOOD; } if (size < sizeof (result)) memset ((char*)result.buttons + size, 0, sizeof (result) - size); /* hack to fill in meaningful values for the AV 210 / 610 and under some conditions the AV 220 */ if (size == 1) { /* AV 210, AV 610 */ DBG (1, "get_button_status: just one byte, filling the rest\n"); if (result.press_state > 0) { debug_print_raw (6, "get_button_status: raw data\n", (uint8_t*)&result, size); result.buttons[0] = result.press_state; result.press_state = 0x80 | 1; size = 2; } else /* nothing pressed */ return SANE_STATUS_GOOD; } else if (size >= 8 && result.press_state == 0) { /* AV 220 */ debug_print_raw (6, "get_button_status: raw data\n", (uint8_t*)&result, size); DBG (1, "get_button_status: zero buttons - filling values ...\n"); /* simulate button press of the last button ... */ result.press_state = 0x80 | 1; result.buttons[0] = (uint8_t) dev->inquiry_buttons; /* 1 based */ } } debug_print_raw (6, "get_button_status: raw data\n", (uint8_t*)&result, size); DBG (3, "get_button_status: [0] Button status: %x\n", result.press_state); for (i = 0; i < 5; ++i) DBG (3, "get_button_status: [%d] Button number %d: %x\n", i+1, i, result.buttons[i]); DBG (3, "get_button_status: [7] Display: %d\n", result.display); { char* message_begin = s->val[OPT_MESSAGE].s; char* message_end = s->val[OPT_MESSAGE].s + s->opt[OPT_MESSAGE].size; char* message = message_begin; #define add_token(format,value) do { \ int n = snprintf (message, (size_t) (message_end - message), "%s" format, \ message == message_begin ? "" : ":", value); \ message += n > 0 ? n : 0; \ } while (0) if (result.display > 0) add_token ("%d", result.display); if (result.press_state >> 7) /* AV220 et.al. bit 6 is long/short press? */ { const unsigned int buttons_pressed = result.press_state & 0x7F; DBG (3, "get_button_status: %d button(s) pressed\n", buttons_pressed); /* reset the hardware button status */ if (! (dev->hw->feature_type & AV_INT_BUTTON)) { struct command_send scmd; uint8_t button_reset = 1; DBG (3, "get_button_status: resetting status\n"); memset (&scmd, 0, sizeof (scmd)); scmd.opc = AVISION_SCSI_SEND; scmd.datatypecode = AVISION_DATATYPECODE_BUTTON_STATUS; /* button control */ set_double (scmd.datatypequal, dev->data_dq); set_triple (scmd.transferlen, size); status = avision_cmd (&s->av_con, &scmd, sizeof (scmd), &button_reset, sizeof (button_reset), 0, 0); if (status != SANE_STATUS_GOOD) { DBG (1, "get_button_status: send failed (%s)\n", sane_strstatus (status)); return status; } } for (i = 0; i < buttons_pressed; ++i) { const uint8_t button = result.buttons[i] - 1; /* 1 based ... */ DBG (3, "get_button_status: button %d pressed\n", button); if (button >= dev->inquiry_buttons) { DBG (1, "get_button_status: button %d not allocated as not indicated in inquiry\n", button); } else { const char* label = string_for_button (s, button); if (label) add_token ("%s", label); else return SANE_STATUS_CANCELLED; } } } else DBG (3, "get_button_status: no button pressed\n"); } return SANE_STATUS_GOOD; #undef add_token } static SANE_Status get_frame_info (Avision_Scanner* s) { Avision_Device* dev = s->hw; /* read stuff */ struct command_read rcmd; size_t size; SANE_Status status; uint8_t result[8]; size_t i; DBG (3, "get_frame_info:\n"); size = sizeof (result); memset (&rcmd, 0, sizeof (rcmd)); rcmd.opc = AVISION_SCSI_READ; rcmd.datatypecode = AVISION_DATATYPECODE_FILM_HOLDER_SENSE; /* film holder sense */ set_double (rcmd.datatypequal, dev->data_dq); set_triple (rcmd.transferlen, size); status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd), 0, 0, result, &size); if (status != SANE_STATUS_GOOD || size != sizeof (result)) { status = (status != SANE_STATUS_GOOD)? status: SANE_STATUS_IO_ERROR; DBG (1, "get_frame_info: read failed (%s)\n", sane_strstatus (status)); return (status); } debug_print_raw (6, "get_frame_info: raw data\n", result, size); DBG (3, "get_frame_info: [0] Holder type: %s\n", (result[0]==1)?"APS": (result[0]==2)?"Film holder (35mm)": (result[0]==3)?"Slide holder": (result[0]==0xff)?"Empty":"unknown"); DBG (3, "get_frame_info: [1] Current frame number: %d\n", result[1]); DBG (3, "get_frame_info: [2] Frame amount: %d\n", result[2]); DBG (3, "get_frame_info: [3] Mode: %s\n", BIT(result[3],4)?"APS":"Not APS"); DBG (3, "get_frame_info: [3] Exposures (if APS): %s\n", ((i=(size_t) (BIT(result[3],3)<<1)+BIT(result[2],2))==0)?"Unknown": (i==1)?"15":(i==2)?"25":"40"); DBG (3, "get_frame_info: [3] Film Type (if APS): %s\n", ((i=(size_t) (BIT(result[1],3)<<1)+BIT(result[0],2))==0)?"Unknown": (i==1)?"B&W Negative":(i==2)?"Color slide":"Color Negative"); dev->holder_type = result[0]; dev->current_frame = result[1]; dev->frame_range.min = 1; dev->frame_range.quant = 1; if (result[0] != 0xff) dev->frame_range.max = result[2]; else dev->frame_range.max = 1; return SANE_STATUS_GOOD; } static SANE_Status get_duplex_info (Avision_Scanner* s) { Avision_Device* dev = s->hw; /* read stuff */ struct command_read rcmd; struct { uint8_t mode; uint8_t color_line_difference[2]; uint8_t gray_line_difference[2]; uint8_t lineart_line_difference[2]; uint8_t image_info; } result; size_t size; SANE_Status status; DBG (3, "get_duplex_info:\n"); size = sizeof (result); memset (&rcmd, 0, sizeof (rcmd)); rcmd.opc = AVISION_SCSI_READ; rcmd.datatypecode = AVISION_DATATYPECODE_READ_DUPLEX_INFO; /* read duplex info */ set_double (rcmd.datatypequal, dev->data_dq); set_triple (rcmd.transferlen, size); status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd), 0, 0, &result, &size); if (status != SANE_STATUS_GOOD || size != sizeof (result)) { status = (status != SANE_STATUS_GOOD)? status: SANE_STATUS_IO_ERROR; DBG (1, "get_duplex_info: read failed (%s)\n", sane_strstatus (status)); return (status); } debug_print_raw (6, "get_duplex_info: raw data\n", (uint8_t*)&result, size); DBG (3, "get_duplex_info: [0] Mode: %s%s\n", BIT(result.mode,0)?"MERGED_PAGES":"", BIT(result.mode,1)?"2ND_PAGE_FOLLOWS":""); DBG (3, "get_duplex_info: [1-2] Color line difference: %d\n", get_double(result.color_line_difference)); DBG (3, "get_duplex_info: [3-4] Gray line difference: %d\n", get_double(result.gray_line_difference)); DBG (3, "get_duplex_info: [5-6] Lineart line difference: %d\n", get_double(result.lineart_line_difference)); /* isn't this supposed to be result.info ?!? */ DBG (3, "get_duplex_info: [7] Mode: %s%s%s%s\n", BIT(result.image_info,0)?" FLATBED_BGR":" FLATBED_RGB", BIT(result.image_info,1)?" ADF_BGR":" ADF_RGB", BIT(result.image_info,2)?" FLATBED_NEEDS_MIRROR_IMAGE":"", BIT(result.image_info,3)?" ADF_NEEDS_MIRROR_IMAGE":""); return SANE_STATUS_GOOD; } static SANE_Status set_frame (Avision_Scanner* s, SANE_Word frame) { struct { struct command_send cmd; uint8_t data[8]; } scmd; Avision_Device* dev = s->hw; SANE_Status status; DBG (3, "set_frame: request frame %d\n", frame); /* Better check the current status of the film holder, because it can be changed between scans. */ status = get_frame_info (s); if (status != SANE_STATUS_GOOD) return status; /* No film holder? */ if (dev->holder_type == 0xff) { DBG (1, "set_frame: No film holder!!\n"); return SANE_STATUS_INVAL; } /* Requesting frame 0xff indicates eject/rewind */ if (frame != 0xff && (frame < 1 || frame > dev->frame_range.max) ) { DBG (1, "set_frame: Illegal frame (%d) requested (min=1, max=%d)\n", frame, dev->frame_range.max); return SANE_STATUS_INVAL; } memset (&scmd, 0, sizeof (scmd)); scmd.cmd.opc = AVISION_SCSI_SEND; scmd.cmd.datatypecode = AVISION_DATATYPECODE_FILM_HOLDER_SENSE; /* send film holder "sense" */ set_double (scmd.cmd.datatypequal, dev->data_dq); set_triple (scmd.cmd.transferlen, sizeof (scmd.data) ); scmd.data[0] = (uint8_t) dev->holder_type; scmd.data[1] = (uint8_t) frame; status = avision_cmd (&s->av_con, &scmd.cmd, sizeof (scmd.cmd), &scmd.data, sizeof (scmd.data), 0, 0); if (status != SANE_STATUS_GOOD) { DBG (1, "set_frame: send_data (%s)\n", sane_strstatus (status)); } return status; } static SANE_Status attach (SANE_String_Const devname, Avision_ConnectionType con_type, Avision_Device** devp) { uint8_t result [AVISION_INQUIRY_SIZE_MAX]; int model_num; Avision_Device* dev; SANE_Status status; Avision_Connection av_con; char mfg [9]; char model [17]; char rev [5]; unsigned int i; char* s; SANE_Bool found; DBG (3, "attach:\n"); memset (result, 0, sizeof(result)); for (dev = first_dev; dev; dev = dev->next) if (strcmp (dev->sane.name, devname) == 0) { if (devp) *devp = dev; return SANE_STATUS_GOOD; } av_con.connection_type = con_type; if (av_con.connection_type == AV_USB) av_con.usb_status = AVISION_USB_UNTESTED_STATUS; /* set known USB status type */ if (attaching_hw && (attaching_hw->feature_type & AV_INT_STATUS)) av_con.usb_status = AVISION_USB_INT_STATUS; DBG (3, "attach: opening %s\n", devname); status = avision_open (devname, &av_con, sense_handler, 0); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: open failed (%s)\n", sane_strstatus (status)); return SANE_STATUS_INVAL; } /* first: get the standard inquiry? */ status = inquiry (av_con, result, AVISION_INQUIRY_SIZE_V1); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: 1st inquiry failed (%s)\n", sane_strstatus (status)); return status; } /* copy string information - and zero terminate them c-style */ memcpy (&mfg, result + 8, 8); mfg [8] = 0; memcpy (&model, result + 16, 16); model [16] = 0; memcpy (&rev, result + 32, 4); rev [4] = 0; /* shorten strings (-1 for last index -1 for last 0; >0 because one char at least) */ for (i = sizeof (mfg) - 2; i > 0; i--) { if (mfg[i] == 0x20) mfg[i] = 0; else break; } for (i = sizeof (model) - 2; i > 0; i--) { if (model[i] == 0x20) model[i] = 0; else break; } DBG (1, "attach: Inquiry gives mfg=%s, model=%s, product revision=%s.\n", mfg, model, rev); model_num = 0; found = 0; /* * Search for a matching device in the device list. * Primarily we need two matches for SCSI devices. * However, multiple USB device entries share the same * SCSI info. For USB devices, we will also do a mandatory * USB Product/Vendor check to pick the right one. Otherwise * at the very least the device name is incorrect. * */ SANE_Word usb_vendor = 0; SANE_Word usb_product = 0; if (con_type == AV_USB) { status = sanei_usb_get_vendor_product_byname (devname, &usb_vendor, &usb_product); if (status != SANE_STATUS_GOOD) { DBG (0, "attach: Could not retrieve USB vendor nor product for USB device.\n"); status = SANE_STATUS_INVAL; goto close_scanner_and_return; } } /* while not at at end of list NULL terminator */ while (Avision_Device_List[model_num].real_mfg != NULL || Avision_Device_List[model_num].scsi_mfg != NULL) { int matches = 0, match_count = 0; /* count number of matches */ DBG (1, "attach: Checking model: %d\n", model_num); if (Avision_Device_List[model_num].scsi_mfg) { ++match_count; if (strcmp (mfg, Avision_Device_List[model_num].scsi_mfg) == 0) ++matches; } if (Avision_Device_List[model_num].scsi_model) { ++match_count; if (strcmp (model, Avision_Device_List[model_num].scsi_model) == 0) ++matches; } /* * Must match on USB vendor product also for USB devices. * We will *always* know the vendor and product for USB devices. * */ if (con_type == AV_USB) { ++match_count; if ((Avision_Device_List[model_num].usb_product == usb_product) && (Avision_Device_List[model_num].usb_vendor == usb_vendor)) { ++matches; } } /* we need 2 matches (mfg, model) for SCSI entries, or the ones available for "we know what we are looking for" USB entries */ if ((attaching_hw == &(Avision_Device_List[model_num])) && (matches == match_count)) { DBG ( 1, "attach: Scanner matched entry: %d: \"%s\", \"%s\", 0x%.4x, 0x%.4x\n", model_num, Avision_Device_List[model_num].scsi_mfg, Avision_Device_List[model_num].scsi_model, Avision_Device_List[model_num].usb_vendor, Avision_Device_List[model_num].usb_product); found = 1; break; } ++model_num; } if (!found) { DBG (0, "attach: \"%s\" - \"%s\" not yet in whitelist!\n", mfg, model); 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; } /* second: maybe ask for the firmware status and flash ram info */ if (Avision_Device_List [model_num].feature_type & AV_FIRMWARE) { DBG (3, "attach: reading firmware status\n"); status = get_firmware_status (&av_con); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: get firmware status failed (%s)\n", sane_strstatus (status)); goto close_scanner_and_return; } DBG (3, "attach: reading flash ram info\n"); status = get_flash_ram_info (&av_con); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: get flash ram info failed (%s)\n", sane_strstatus (status)); goto close_scanner_and_return; } #ifdef FIRMWARE_DATABASE_INCLUDED /* Send new NV-RAM (firmware) data */ status = send_nvram_data (&av_con); if (status != SANE_STATUS_GOOD) goto close_scanner_and_return; #endif } /* third: get the extended Avision inquiry */ status = inquiry (av_con, result, AVISION_INQUIRY_SIZE_V1); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: avision v1 inquiry failed (%s)\n", sane_strstatus (status)); goto close_scanner_and_return; } dev = malloc (sizeof (*dev)); if (!dev) { status = SANE_STATUS_NO_MEM; goto close_scanner_and_return; } memset (dev, 0, sizeof (*dev)); dev->hw = &Avision_Device_List[model_num]; dev->sane.name = strdup (devname); dev->sane.vendor = dev->hw->real_mfg ? dev->hw->real_mfg : strdup (mfg); dev->sane.model = dev->hw->real_model ? dev->hw->real_model : strdup (model); dev->connection.connection_type = av_con.connection_type; dev->connection.usb_status = av_con.usb_status; /* and finally Avision even extended this one later on the AV220C2 does not grok this */ dev->inquiry_asic_type = (int) result[91]; if (dev->inquiry_asic_type == AV_ASIC_C6) { status = inquiry (av_con, result, AVISION_INQUIRY_SIZE_V2); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: avision v2 inquiry failed (%s)\n", sane_strstatus (status)); goto close_scanner_and_return; } } debug_print_raw (6, "attach: raw data:\n", result, sizeof (result) ); DBG (3, "attach: [8-15] Vendor id.: '%8.8s'\n", result+8); DBG (3, "attach: [16-31] Product id.: '%16.16s'\n", result+16); DBG (3, "attach: [32-35] Product rev.: '%4.4s'\n", result+32); i = (result[36] >> 4) & 0x7; switch (result[36] & 0x07) { case 0: s = " RGB"; break; case 1: s = " BGR"; break; default: s = " unknown (RESERVED)"; } DBG (3, "attach: [36] Bitfield:%s%s%s%s%s%s%s color plane\n", BIT(result[36],7)?" ADF":"", (i==0)?" B&W only":"", BIT(i, 1)?" 3-pass color":"", BIT(i, 2)?" 1-pass color":"", BIT(i, 2) && BIT(i, 0) ?" 1-pass color (ScanPartner only)":"", BIT(result[36],3)?" IS_NOT_FLATBED":"", s); DBG (3, "attach: [37] Optical res.: %d00 dpi\n", result[37]); DBG (3, "attach: [38] Maximum res.: %d00 dpi\n", result[38]); DBG (3, "attach: [39] Bitfield1:%s%s%s%s%s%s\n", BIT(result[39],7)?" TRANS":"", BIT(result[39],6)?" Q_SCAN":"", BIT(result[39],5)?" EXTENDED_RES":"", BIT(result[39],4)?" SUPPORTS_CALIB":"", BIT(result[39],2)?" NEW_PROTOCOL":"", (result[39] & 0x03) == 0x03 ? " AVISION":" OEM"); DBG (3, "attach: [40-41] X res. in gray: %d dpi\n", get_double ( &(result[40]) )); DBG (3, "attach: [42-43] Y res. in gray: %d dpi\n", get_double ( &(result[42]) )); DBG (3, "attach: [44-45] X res. in color: %d dpi\n", get_double ( &(result[44]) )); DBG (3, "attach: [46-47] Y res. in color: %d dpi\n", get_double ( &(result[46]) )); DBG (3, "attach: [48-49] USB max read: %d\n", get_double ( &(result[48] ) )); DBG (3, "attach: [50] ESA1:%s%s%s%s%s%s%s%s\n", BIT(result[50],7)?" LIGHT_CONTROL":"", BIT(result[50],6)?" BUTTON_CONTROL":"", BIT(result[50],5)?" NEED_SW_COLORPACK":"", BIT(result[50],4)?" SW_CALIB":"", BIT(result[50],3)?" NEED_SW_GAMMA":"", BIT(result[50],2)?" KEEPS_GAMMA":"", BIT(result[50],1)?" KEEPS_WINDOW_CMD":"", BIT(result[50],0)?" XYRES_DIFFERENT":""); DBG (3, "attach: [51] ESA2:%s%s%s%s%s%s%s%s\n", BIT(result[51],7)?" EXPOSURE_CTRL":"", BIT(result[51],6)?" NEED_SW_TRIGGER_CAL":"", BIT(result[51],5)?" NEED_WHITE_PAPER_CALIB":"", BIT(result[51],4)?" SUPPORTS_QUALITY_SPEED_CAL":"", BIT(result[51],3)?" NEED_TRANSP_CAL":"", BIT(result[51],2)?" HAS_PUSH_BUTTON":"", BIT(result[51],1)?" NEW_CAL_METHOD_3x3_MATRIX_(NO_GAMMA_TABLE)":"", BIT(result[51],0)?" ADF_MIRRORS_IMAGE":""); DBG (3, "attach: [52] ESA3:%s%s%s%s%s%s%s%s\n", BIT(result[52],7)?" GRAY_WHITE":"", BIT(result[52],6)?" SUPPORTS_GAIN_CONTROL":"", BIT(result[52],5)?" SUPPORTS_TET":"", /* "Text Enhanced Technology" */ BIT(result[52],4)?" 3x3COL_TABLE":"", BIT(result[52],3)?" 1x3FILTER":"", BIT(result[52],2)?" INDEX_COLOR":"", BIT(result[52],1)?" POWER_SAVING_TIMER":"", BIT(result[52],0)?" NVM_DATA_REC":""); /* print some more scanner features/params */ DBG (3, "attach: [53] line difference (software color pack): %d\n", result[53]); DBG (3, "attach: [54] color mode pixel boundary: %d\n", result[54]); DBG (3, "attach: [55] gray mode pixel boundary: %d\n", result[55]); DBG (3, "attach: [56] 4bit gray mode pixel boundary: %d\n", result[56]); DBG (3, "attach: [57] lineart mode pixel boundary: %d\n", result[57]); DBG (3, "attach: [58] halftone mode pixel boundary: %d\n", result[58]); DBG (3, "attach: [59] error-diffusion mode pixel boundary: %d\n", result[59]); DBG (3, "attach: [60] channels per pixel:%s%s%s\n", BIT(result[60],7)?" 1":"", BIT(result[60],6)?" 3":"", (result[60] & 0x3F) != 0 ? " RESERVED":""); DBG (3, "attach: [61] bits per channel:%s%s%s%s%s%s%s\n", BIT(result[61],7)?" 1":"", BIT(result[61],6)?" 4":"", BIT(result[61],5)?" 6":"", BIT(result[61],4)?" 8":"", BIT(result[61],3)?" 10":"", BIT(result[61],2)?" 12":"", BIT(result[61],1)?" 16":""); DBG (3, "attach: [62] scanner type:%s%s%s%s%s%s\n", BIT(result[62],7)?" Flatbed":"", BIT(result[62],6)?" Roller (ADF)":"", BIT(result[62],5)?" Flatbed (ADF/Lightbox)":"", BIT(result[62],4)?" Roller":"", /* does not feed multiple pages, AV25 */ BIT(result[62],3)?" Film scanner":"", BIT(result[62],2)?" Duplex":""); DBG (3, "attach: [75-76] Max shading target : %x\n", get_double ( &(result[75]) )); DBG (3, "attach: [77-78] Max X of transparency: %d dots * base_dpi\n", get_double ( &(result[77]) )); DBG (3, "attach: [79-80] Max Y of transparency: %d dots * base_dpi\n", get_double ( &(result[79]) )); DBG (3, "attach: [81-82] Max X of flatbed: %d dots * base_dpi\n", get_double ( &(result[81]) )); DBG (3, "attach: [83-84] Max Y of flatbed: %d dots * base_dpi\n", get_double ( &(result[83]) )); DBG (3, "attach: [85-86] Max X of ADF: %d dots * base_dpi\n", get_double ( &(result[85]) )); DBG (3, "attach: [87-88] Max Y of ADF: %d dots * base_dpi\n", get_double ( &(result[87]) )); /* 0xFFFF means unlimited length */ DBG (3, "attach: [89-90] Res. in Ex. mode: %d dpi\n", get_double ( &(result[89]) )); DBG (3, "attach: [91] ASIC: %d\n", result[91]); DBG (3, "attach: [92] Buttons: %d\n", result[92]); DBG (3, "attach: [93] ESA4:%s%s%s%s%s%s%s%s\n", BIT(result[93],7)?" SUPPORTS_ACCESSORIES_DETECT":"", BIT(result[93],6)?" ADF_IS_BGR_ORDERED":"", BIT(result[93],5)?" NO_SINGLE_CHANNEL_GRAY_MODE":"", BIT(result[93],4)?" SUPPORTS_FLASH_UPDATE":"", BIT(result[93],3)?" SUPPORTS_ASIC_UPDATE":"", BIT(result[93],2)?" SUPPORTS_LIGHT_DETECT":"", BIT(result[93],1)?" SUPPORTS_READ_PRNU_DATA":"", BIT(result[93],0)?" FLATBED_MIRRORS_IMAGE":""); DBG (3, "attach: [94] ESA5:%s%s%s%s%s%s%s%s\n", BIT(result[94],7)?" IGNORE_LINE_DIFFERENCE_FOR_ADF":"", BIT(result[94],6)?" NEEDS_SW_LINE_COLOR_PACK":"", BIT(result[94],5)?" SUPPORTS_DUPLEX_SCAN":"", BIT(result[94],4)?" INTERLACED_DUPLEX_SCAN":"", BIT(result[94],3)?" SUPPORTS_TWO_MODE_ADF_SCANS":"", BIT(result[94],2)?" SUPPORTS_TUNE_SCAN_LENGTH":"", BIT(result[94],1)?" SUPPORTS_SWITCH_STRIP_FOR_DESKEW":"", /* Kodak i80 only */ BIT(result[94],0)?" SEARCHES_LEADING_SIDE_EDGE_BY_FIRMWARE":""); DBG (3, "attach: [95] ESA6:%s%s%s%s%s%s%s%s\n", BIT(result[95],7)?" SUPPORTS_PAPER_SIZE_AUTO_DETECTION":"", BIT(result[95],6)?" SUPPORTS_DO_HOUSEKEEPING":"", /* Kodak i80 only */ BIT(result[95],5)?" SUPPORTS_PAPER_LENGTH_SETTING":"", /* AV220, Kodak */ BIT(result[95],4)?" SUPPORTS_PRE_GAMMA_LINEAR_CORRECTION":"", BIT(result[95],3)?" SUPPORTS_PREFEEDING":"", /* OKI S9800 */ BIT(result[95],2)?" SUPPORTS_GET_BACKGROUND_RASTER":"", /* AV220 et.al. */ BIT(result[95],1)?" SUPPORTS_NVRAM_RESET":"", BIT(result[95],0)?" SUPPORTS_BATCH_SCAN":""); DBG (3, "attach: [128] ESA7:%s%s%s%s%s%s%s%s\n", BIT(result[128],7)?" SUPPORTS_ADF_CONTINUOUS":"", BIT(result[128],6)?" SUPPORTS_YCbCr_COLOR":"", BIT(result[128],5)?" SUPPORTS_ADF_3PASS":"", BIT(result[128],4)?" SUPPORTS_TUNE_SCAN_LENGTH_HORIZ":"", BIT(result[128],3)?" SUPPORTS_READ_WRITE_ABILITY_PARAMETER":"", BIT(result[128],2)?" SUPPORTS_JOB_CONTROL":"", BIT(result[128],1)?" SUPPORTS_INF_LENGTH":"", BIT(result[128],0)?" ULTRA_SONIC_DOUBLE_FEED_DETECTION":""); DBG (3, "attach: [129] YCbCr:%s%s%s%s%s%s%s%s\n", BIT(result[129],7)?" YCC4:2:0":"", BIT(result[129],6)?" YCC(profile2)":"", BIT(result[129],5)?" YCC(profile3)":"", BIT(result[129],4)?" YCC(profile4)":"", BIT(result[129],3)?" JPEG(profile1)":"", BIT(result[129],2)?" JPEG(profile2)":"", BIT(result[129],1)?" JPEG(profile3)":"", BIT(result[129],0)?" JPEG(profile4)":""); /* I have no idea how film scanner could reliably be detected -ReneR */ if (dev->hw->feature_type & AV_FILMSCANNER) { dev->scanner_type = AV_FILM; dev->sane.type = "film scanner"; } else if ( BIT(result[62],6) || BIT(result[62],4) ) { dev->scanner_type = AV_SHEETFEED; dev->sane.type = "sheetfed scanner"; } else { dev->scanner_type = AV_FLATBED; dev->sane.type = "flatbed scanner"; } 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_capability = BIT (result[62], 5); dev->inquiry_duplex = BIT (result[62], 2) || BIT (result[94], 5); dev->inquiry_duplex_interlaced = BIT(result[62],2) || BIT (result[94], 4); /* the first avision scanners (AV3200) do not set the interlaced bit */ if (dev->inquiry_duplex && dev->inquiry_asic_type < AV_ASIC_C6) dev->inquiry_duplex_interlaced = 1; dev->inquiry_paper_length = BIT (result[95], 5); dev->inquiry_batch_scan = BIT (result[95], 0); /* AV122, DM152 */ dev->inquiry_detect_accessories = BIT (result[93], 7); dev->inquiry_needs_calibration = BIT (result[50], 4); 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; if (Avision_Device_List [model_num].feature_type & AV_DOES_KEEP_WINDOW) dev->inquiry_keeps_window = 1; dev->inquiry_needs_gamma = BIT (result[50], 3); dev->inquiry_keeps_gamma = BIT (result[50], 2); if (Avision_Device_List [model_num].feature_type & AV_DOES_NOT_KEEP_GAMMA) dev->inquiry_keeps_gamma = 0; if (Avision_Device_List [model_num].feature_type & AV_DOES_KEEP_GAMMA) dev->inquiry_keeps_gamma = 1; 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); dev->inquiry_button_control = BIT (result[50], 6) | BIT (result[51],2); dev->inquiry_exposure_control = BIT(result[51],7); if (dev->scanner_type != AV_FILM && !(dev->hw->feature_type & AV_FORCE_FILM)) dev->inquiry_exposure_control = 0; dev->inquiry_max_shading_target = get_double ( &(result[75]) ); dev->inquiry_color_boundary = result[54]; if (dev->inquiry_color_boundary == 0) dev->inquiry_color_boundary = 8; dev->inquiry_gray_boundary = result[55]; if (dev->inquiry_gray_boundary == 0) dev->inquiry_gray_boundary = 8; dev->inquiry_dithered_boundary = result[59]; if (dev->inquiry_dithered_boundary == 0) dev->inquiry_dithered_boundary = 8; dev->inquiry_thresholded_boundary = result[57]; if (dev->inquiry_thresholded_boundary == 0) dev->inquiry_thresholded_boundary = 8; dev->inquiry_line_difference = result[53]; /* compensation according to real world hardware */ switch (dev->inquiry_asic_type) { case AV_ASIC_C2: /* HP 5300 */ case AV_ASIC_C5: /* HP 53xx R2 */ dev->inquiry_line_difference /= 2; /* HP 5300 */ break; case AV_ASIC_C7: dev->inquiry_line_difference *= 2; /* AV610C2 */ break; default: ; } if (dev->inquiry_new_protocol) { dev->inquiry_optical_res = get_double ( &(result[89]) ); dev->inquiry_max_res = get_double ( &(result[44]) ); } else { dev->inquiry_optical_res = result[37] * 100; dev->inquiry_max_res = result[38] * 100; } /* fixup max res */ if (dev->inquiry_optical_res > dev->inquiry_max_res) { DBG (1, "Inquiry optical resolution > max_resolution, adjusting!\n"); dev->inquiry_max_res = dev->inquiry_optical_res; } if (dev->inquiry_optical_res == 0) { DBG (1, "Inquiry optical resolution is invalid!\n"); if (dev->hw->feature_type & AV_FORCE_FILM) dev->inquiry_optical_res = 2438; /* verify */ if (dev->scanner_type == AV_SHEETFEED) dev->inquiry_optical_res = 300; else dev->inquiry_optical_res = 600; } if (dev->inquiry_max_res == 0) { DBG (1, "Inquiry max resolution is invalid, using 1200 dpi!\n"); dev->inquiry_max_res = 1200; } DBG (1, "attach: optical resolution set to: %d dpi\n", dev->inquiry_optical_res); DBG (1, "attach: max resolution set to: %d dpi\n", dev->inquiry_max_res); if (BIT(result[60],6)) dev->inquiry_channels_per_pixel = 3; else if (BIT(result[60],7)) dev->inquiry_channels_per_pixel = 1; else if ( ((result[36] >> 4) & 0x7) > 0) dev->inquiry_channels_per_pixel = 3; else dev->inquiry_channels_per_pixel = 1; if (BIT(result[61],1)) dev->inquiry_bits_per_channel = 16; else if (BIT(result[61],2)) dev->inquiry_bits_per_channel = 12; else if (BIT(result[61],3)) dev->inquiry_bits_per_channel = 10; else if (BIT(result[61],4)) dev->inquiry_bits_per_channel = 8; else if (BIT(result[61],5)) dev->inquiry_bits_per_channel = 6; else if (BIT(result[61],6)) dev->inquiry_bits_per_channel = 4; else if (BIT(result[61],7)) dev->inquiry_bits_per_channel = 1; else dev->inquiry_bits_per_channel = 8; /* default for old scanners */ if (dev->hw->feature_type & AV_12_BIT_MODE) dev->inquiry_bits_per_channel = 12; if (! (dev->hw->feature_type & AV_GRAY_MODES)) dev->inquiry_no_gray_modes = BIT(result[93],5); DBG (1, "attach: max channels per pixel: %d, max bits per channel: %d\n", dev->inquiry_channels_per_pixel, dev->inquiry_bits_per_channel); if (! (dev->hw->feature_type & AV_NO_BUTTON)) dev->inquiry_buttons = result[92]; /* get max x/y ranges for the different modes */ { double base_dpi; /* TODO: make int */ if (dev->scanner_type != AV_FILM) { base_dpi = AVISION_BASE_RES; } else { /* ZP: The right number is 2820, whether it is 40-41, 42-43, 44-45, * 46-47 or 89-90 I don't know but I would bet for the last ! * ReneR: OK. We use it via the optical_res which we need anyway ... */ base_dpi = dev->inquiry_optical_res; } /* .1 to slightly increase the size to match the one of American standard paper formats that would otherwise be .1 mm too large to scan ... */ dev->inquiry_x_ranges [AV_NORMAL_DIM] = (double)get_double (&(result[81])) * MM_PER_INCH / base_dpi + .1; dev->inquiry_y_ranges [AV_NORMAL_DIM] = (double)get_double (&(result[83])) * MM_PER_INCH / base_dpi; dev->inquiry_x_ranges [AV_TRANSPARENT_DIM] = (double)get_double (&(result[77])) * MM_PER_INCH / base_dpi + .1; dev->inquiry_y_ranges [AV_TRANSPARENT_DIM] = (double)get_double (&(result[79])) * MM_PER_INCH / base_dpi; dev->inquiry_x_ranges [AV_ADF_DIM] = (double)get_double (&(result[85])) * MM_PER_INCH / base_dpi + .1; dev->inquiry_y_ranges [AV_ADF_DIM] = (double)get_double (&(result[87])) * MM_PER_INCH / base_dpi; } dev->inquiry_tune_scan_length = BIT(result[94],2); if (Avision_Device_List [model_num].feature_type & AV_NO_TUNE_SCAN_LENGTH) dev->inquiry_tune_scan_length = 0; dev->inquiry_background_raster = BIT(result[95],2); if (dev->hw->feature_type & AV_NO_BACKGROUND) dev->inquiry_background_raster = 0; if (dev->inquiry_background_raster) { dev->inquiry_background_raster_pixel = get_double(&(result[85])) * dev->inquiry_optical_res / AVISION_BASE_RES; } /* check if x/y ranges are valid :-((( */ { source_mode_dim mode; for (mode = AV_NORMAL_DIM; mode < AV_SOURCE_MODE_DIM_LAST; ++ mode) { if (dev->inquiry_x_ranges [mode] != 0 && dev->inquiry_y_ranges [mode] != 0) { DBG (3, "attach: x/y-range for mode %d is valid!\n", mode); if (force_a4) { DBG (1, "attach: \"force_a4\" found! Using default (ISO A4).\n"); dev->inquiry_x_ranges [mode] = A4_X_RANGE * MM_PER_INCH; dev->inquiry_y_ranges [mode] = A4_Y_RANGE * MM_PER_INCH; } else if (force_a3) { DBG (1, "attach: \"force_a3\" found! Using default (ISO A3).\n"); dev->inquiry_x_ranges [mode] = A3_X_RANGE * MM_PER_INCH; dev->inquiry_y_ranges [mode] = A3_Y_RANGE * MM_PER_INCH; } } else /* mode is invalid */ { DBG (1, "attach: x/y-range for mode %d is invalid! Using a default.\n", mode); if (dev->hw->feature_type & AV_FORCE_A3) { dev->inquiry_x_ranges [mode] = A3_X_RANGE * MM_PER_INCH; dev->inquiry_y_ranges [mode] = A3_Y_RANGE * MM_PER_INCH; } else if (dev->hw->feature_type & AV_FORCE_FILM) { dev->inquiry_x_ranges [mode] = FILM_X_RANGE * MM_PER_INCH; dev->inquiry_y_ranges [mode] = FILM_Y_RANGE * MM_PER_INCH; } else { dev->inquiry_x_ranges [mode] = A4_X_RANGE * MM_PER_INCH; if (dev->scanner_type == AV_SHEETFEED) dev->inquiry_y_ranges [mode] = SHEETFEED_Y_RANGE * MM_PER_INCH; else dev->inquiry_y_ranges [mode] = A4_Y_RANGE * MM_PER_INCH; } } DBG (1, "attach: Mode %d range is now: %f x %f mm.\n", mode, dev->inquiry_x_ranges [mode], dev->inquiry_y_ranges [mode]); } /* end for all modes */ } /* We need a bigger buffer for USB devices, since they seem to have a firmware bug and do not support reading the calibration data in tiny chunks */ if (av_con.connection_type == AV_USB) dev->scsi_buffer_size = 1024 * 1024; /* or 0x10000, used by AV Windows driver during background raster read, ... ? */ else dev->scsi_buffer_size = sanei_scsi_max_request_size; if (dev->inquiry_asic_type > AV_ASIC_C7 && dev->inquiry_asic_type < AV_ASIC_OA980) dev->read_stripe_size = 16; else if (dev->inquiry_asic_type >= AV_ASIC_C5) dev->read_stripe_size = 32; else /* tested on AV3200 with it's max of 300dpi @color */ dev->read_stripe_size = 8; /* maybe made dynamic on scan res ... */ /* normally the data_dq is 0x0a0d - but some newer scanner hang with it ... */ if (dev->inquiry_new_protocol) /* TODO: match on ASIC? which model hung? */ dev->data_dq = 0x0a0d; else dev->data_dq = 0; avision_close (&av_con); ++ num_devices; dev->next = first_dev; first_dev = dev; if (devp) *devp = dev; return SANE_STATUS_GOOD; close_scanner_and_return: avision_close (&av_con); return status; } static SANE_Status get_tune_scan_length (Avision_Scanner* s) { SANE_Status status; int i; struct command_read rcmd; size_t size; struct max_value { uint8_t max [2]; } payload; /* turn on the light */ DBG (3, "get_tune_scan_length:\n"); memset (&rcmd, 0, sizeof (rcmd)); size = sizeof (payload); rcmd.opc = AVISION_SCSI_READ; rcmd.datatypecode = AVISION_DATATYPECODE_READ_GENERAL_ABILITY_PARAM; /* Read General Ability/Parameter */ for (i = 1; i <= 8; ++i) { memset (&payload, 0, sizeof (payload)); set_double (rcmd.datatypequal, i); /* type */ set_triple (rcmd.transferlen, size); status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd), 0, 0, &payload, &size); if (status != SANE_STATUS_GOOD) { DBG (1, "get_tune_scan_length: read %d failed (%s)\n", i, sane_strstatus (status)); return status; } DBG (1, "get_tune_scan_length: %d: %d\n", i, get_double (payload.max)); } return SANE_STATUS_GOOD; } static SANE_Status send_tune_scan_length (Avision_Scanner* s) { Avision_Device* dev = s->hw; int top, bottom, dpi; double offset = 0; SANE_Status status; size_t size; struct command_send scmd; struct truncate_attach { uint8_t vertical [2]; /* uint8_t horizontal [2]; not send by the Windows driver, yet */ } payload; DBG (3, "send_tune_scan_length:\n"); memset (&scmd, 0, sizeof (scmd)); size = sizeof (payload); scmd.opc = AVISION_SCSI_SEND; scmd.datatypecode = AVISION_DATATYPECODE_ATTACH_TRUNCATE_HEAD; /* Attach/Truncate head(left) of scan length */ set_triple (scmd.transferlen, size); /* the SPEC says optical DPI, but real world measuring suggests it is 1200 as in the window descriptor. MN: This is not true for at least Kodak i1120 where it is optical DPI */ dpi = 1200; if (dev->hw->feature_type & AV_OVERSCAN_OPTDPI) dpi = dev->inquiry_optical_res; top = (int) (dpi * SANE_UNFIX (s->val[OPT_OVERSCAN_TOP].w) / MM_PER_INCH); DBG (3, "send_tune_scan_length: top: %d\n", top); /* top offset compensation */ if (dev->adf_offset_compensation) { if (s->avdimen.interlaced_duplex) offset += fmax(0, fmax(dev->hw->offset.duplex.front.top, dev->hw->offset.duplex.rear.top) ); else if (s->source_mode == AV_ADF) offset += fmax(0, dev->hw->offset.front.top); /* first page offset */ if (dev->hw->offset.first > 0) offset += dev->hw->offset.first; /* convert to lines */ int top_offset = (int) (dpi * offset / MM_PER_INCH); top += top_offset; DBG (3, "send_tune_scan_length: top offset: %d\n", top_offset); } set_double (scmd.datatypequal, 0x0001); /* attach, 0x000 is shorten */ set_double (payload.vertical, top); /* set_double (payload.horizontal, 0); */ /* we always send it, even for 0 as the scanner keeps it in RAM and previous runs could already have set something */ status = avision_cmd (&s->av_con, &scmd, sizeof (scmd), &payload, sizeof (payload), 0, 0); if (status != SANE_STATUS_GOOD) { DBG (1, "send_tune_scan_length: send top/left failed (%s)\n", sane_strstatus (status)); return status; } scmd.datatypecode = AVISION_DATATYPECODE_ATTACH_TRUNCATE_TAIL; /* Attach/Truncate tail(right) of scan length */ bottom = (int) (dpi * SANE_UNFIX (s->val[OPT_OVERSCAN_BOTTOM].w) / MM_PER_INCH); DBG (3, "send_tune_scan_length: bottom: %d\n", bottom); /* bottom offset compensation */ offset = 0; if (dev->adf_offset_compensation) { if (s->avdimen.interlaced_duplex) offset += fmax(0, fmax(dev->hw->offset.duplex.front.bottom, dev->hw->offset.duplex.rear.bottom) ); else if (s->source_mode == AV_ADF) offset += fmax(0, dev->hw->offset.front.bottom); /* first page offset */ if (dev->hw->offset.first < 0) offset += fabs(dev->hw->offset.first); /* convert to lines */ int bottom_offset = (int) (dpi * offset / MM_PER_INCH); bottom += bottom_offset; DBG (3, "send_tune_scan_length: bottom offset: %d\n", bottom_offset); } set_double (payload.vertical, bottom); /*set_double (payload.horizontal, 0); */ size = sizeof (payload); status = avision_cmd (&s->av_con, &scmd, sizeof (scmd), &payload, sizeof (payload), 0, 0); if (status != SANE_STATUS_GOOD) { DBG (1, "send_tune_scan_length: send bottom/right failed (%s)\n", sane_strstatus (status)); return status; } return SANE_STATUS_GOOD; } static SANE_Status additional_probe (Avision_Scanner* s) { Avision_Device* dev = s->hw; /* we should wait until the scanner is ready before we perform further actions */ SANE_Status status; /* try to retrieve additional accessory information */ if (dev->inquiry_detect_accessories) { status = get_accessories_info (s); if (status != SANE_STATUS_GOOD) return status; } /* for a film scanner try to retrieve additional frame information */ if (dev->scanner_type == AV_FILM) { status = get_frame_info (s); if (status != SANE_STATUS_GOOD) return status; } /* no scanner did support this so far: tried on AV220, DM152 */ if (0 && dev->inquiry_duplex) { status = get_duplex_info (s); if (status != SANE_STATUS_GOOD) return status; } /* get overscan ("head/tail tune") information: hangs AV220, zeros on AV122 */ if (0 && dev->inquiry_tune_scan_length) { status = get_tune_scan_length (s); if (status != SANE_STATUS_GOOD) return status; } /* create dynamic *-mode entries */ if (!dev->inquiry_no_gray_modes) { if (dev->inquiry_bits_per_channel > 0) { add_color_mode (dev, AV_THRESHOLDED, SANE_VALUE_SCAN_MODE_LINEART); add_color_mode (dev, AV_DITHERED, "Dithered"); } if (dev->inquiry_bits_per_channel >= 8) add_color_mode (dev, AV_GRAYSCALE, SANE_VALUE_SCAN_MODE_GRAY); if (dev->inquiry_bits_per_channel == 12) add_color_mode (dev, AV_GRAYSCALE12, "12bit Gray"); if (dev->inquiry_bits_per_channel >= 16) add_color_mode (dev, AV_GRAYSCALE16, "16bit Gray"); } if (dev->inquiry_channels_per_pixel > 1) { add_color_mode (dev, AV_TRUECOLOR, SANE_VALUE_SCAN_MODE_COLOR); if (dev->inquiry_bits_per_channel == 12) add_color_mode (dev, AV_TRUECOLOR12, "12bit Color"); if (dev->inquiry_bits_per_channel >= 16) add_color_mode (dev, AV_TRUECOLOR16, "16bit Color"); } /* now choose the default mode - avoiding the 12/16 bit modes */ dev->color_list_default = last_color_mode (dev); if (dev->inquiry_bits_per_channel > 8 && dev->color_list_default > 0) { dev->color_list_default--; } if (dev->scanner_type == AV_SHEETFEED) { add_source_mode (dev, AV_ADF, "ADF Front"); } else { add_source_mode (dev, AV_NORMAL, "Normal"); if (dev->inquiry_light_box_present) add_source_mode (dev, AV_TRANSPARENT, "Transparency"); if (dev->inquiry_adf_present) add_source_mode (dev, AV_ADF, "ADF Front"); } if (dev->inquiry_duplex) { if (dev->inquiry_duplex_interlaced && !(dev->hw->feature_type & AV_NO_REAR)) add_source_mode (dev, AV_ADF_REAR, "ADF Back"); add_source_mode (dev, AV_ADF_DUPLEX, "ADF Duplex"); } return SANE_STATUS_GOOD; } static SANE_Status get_calib_format (Avision_Scanner* s, struct calibration_format* format) { SANE_Status status; struct command_read rcmd; uint8_t result [32]; size_t size; DBG (3, "get_calib_format:\n"); size = sizeof (result); memset (&rcmd, 0, sizeof (rcmd)); rcmd.opc = AVISION_SCSI_READ; rcmd.datatypecode = AVISION_DATATYPECODE_GET_CALIBRATION_FORMAT; /* get calibration format */ set_double (rcmd.datatypequal, s->hw->data_dq); set_triple (rcmd.transferlen, size); DBG (3, "get_calib_format: read_data: %lu bytes\n", (u_long) size); status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd), 0, 0, result, &size); if (status != SANE_STATUS_GOOD || size != sizeof (result) ) { status = (status != SANE_STATUS_GOOD)? status: SANE_STATUS_IO_ERROR; DBG (1, "get_calib_format: read calib. info failed (%s)\n", sane_strstatus (status) ); return status; } debug_print_calib_format (3, "get_calib_format", result); format->pixel_per_line = (uint16_t) get_double (&(result[0])); format->bytes_per_channel = result[2]; format->lines = result[3]; format->flags = result[4]; format->ability1 = result[5]; format->r_gain = result[6]; format->g_gain = result[7]; format->b_gain = result[8]; format->r_shading_target = (uint16_t) get_double (&(result[9])); format->g_shading_target = (uint16_t) get_double (&(result[11])); format->b_shading_target = (uint16_t) get_double (&(result[13])); format->r_dark_shading_target = (uint16_t) get_double (&(result[15])); format->g_dark_shading_target = (uint16_t) get_double (&(result[17])); format->b_dark_shading_target = (uint16_t) get_double (&(result[19])); /* now translate to normal! */ /* firmware return R--RG--GB--B with 3 line count */ /* software format it as 1 line if true color scan */ /* only line interleave format to be supported */ if (color_mode_is_color (s->c_mode) || BIT(format->ability1, 3)) { format->channels = 3; format->lines /= 3; /* line interleave */ } else format->channels = 1; DBG (3, "get_calib_format: channels: %d\n", format->channels); return SANE_STATUS_GOOD; } static SANE_Status get_calib_data (Avision_Scanner* s, uint8_t data_type, uint8_t* calib_data, size_t calib_size) { SANE_Status status; uint8_t *calib_ptr; size_t get_size, data_size, chunk_size; struct command_read rcmd; chunk_size = calib_size; DBG (3, "get_calib_data: type %x, size %lu, chunk_size: %lu\n", data_type, (u_long) calib_size, (u_long) chunk_size); memset (&rcmd, 0, sizeof (rcmd)); rcmd.opc = AVISION_SCSI_READ; rcmd.datatypecode = data_type; set_double (rcmd.datatypequal, s->hw->data_dq); calib_ptr = calib_data; get_size = chunk_size; data_size = calib_size; while (data_size) { if (get_size > data_size) get_size = data_size; read_constrains(s, get_size); set_triple (rcmd.transferlen, get_size); DBG (3, "get_calib_data: Reading %ld bytes calibration data\n", (long)get_size); status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd), 0, 0, calib_ptr, &get_size); if (status != SANE_STATUS_GOOD) { DBG (1, "get_calib_data: read data failed (%s)\n", sane_strstatus (status)); return status; } DBG (3, "get_calib_data: Got %ld bytes calibration data\n", (long)get_size); data_size -= get_size; calib_ptr += get_size; } return SANE_STATUS_GOOD; } static SANE_Status set_calib_data (Avision_Scanner* s, struct calibration_format* format, uint8_t* dark_data, uint8_t* white_data) { Avision_Device* dev = s->hw; const size_t elements_per_line = format->pixel_per_line * format->channels; SANE_Status status; uint8_t send_type; uint16_t send_type_q; struct command_send scmd; size_t i; DBG (3, "set_calib_data:\n"); send_type = 0x82; /* download calibration data */ /* do we use a color mode? */ if (format->channels > 1) { send_type_q = 0x12; /* color calib data */ } else { if (dev->hw->feature_type & AV_GRAY_CALIB_BLUE) send_type_q = 0x2; /* gray/bw calib data on the blue channel (AV610) */ else send_type_q = 0x11; /* gray/bw calib data */ } memset (&scmd, 0x00, sizeof (scmd)); scmd.opc = AVISION_SCSI_SEND; scmd.datatypecode = send_type; /* data corrections due to dark calibration data merge */ if (BIT (format->ability1, 2) ) { DBG (3, "set_calib_data: merging dark calibration data\n"); for (i = 0; i < elements_per_line; ++i) { uint16_t value_orig = (uint16_t) get_double_le (white_data + i*2); uint16_t value_new = value_orig; value_new &= 0xffc0; value_new |= (uint16_t) ((get_double_le (dark_data + i*2) >> 10) & 0x3f); DBG (9, "set_calib_data: element %zu, dark difference %d\n", i, value_orig - value_new); set_double_le ((white_data + i*2), value_new); } } /* send data in one command? */ /* FR: HP5370 reports one-pass, but needs multi (or other format in single) */ if (format->channels == 1 || ( ( (dev->hw->feature_type & AV_ONE_CALIB_CMD) || ! BIT(format->ability1, 0) ) && ! (dev->hw->feature_type & AV_MULTI_CALIB_CMD) ) ) /* one command (most scanners) */ { size_t send_size = elements_per_line * 2; DBG (3, "set_calib_data: all channels in one command\n"); DBG (3, "set_calib_data: send_size: %zu\n", send_size); memset (&scmd, 0, sizeof (scmd) ); scmd.opc = AVISION_SCSI_SEND; scmd.datatypecode = send_type; set_double (scmd.datatypequal, send_type_q); set_triple (scmd.transferlen, send_size); status = avision_cmd (&s->av_con, &scmd, sizeof (scmd), (char*) white_data, send_size, 0, 0); /* not return immediately to free mem at the end */ } else /* send data channel by channel (some USB ones) */ { size_t conv_out_size = format->pixel_per_line * 2; uint16_t* conv_out_data; /* here it is save to use 16bit data since we only move whole words around */ DBG (3, "set_calib_data: channels in single commands\n"); conv_out_data = (uint16_t*) malloc (conv_out_size); if (!conv_out_data) { status = SANE_STATUS_NO_MEM; } else { int channel; for (channel = 0; channel < 3; ++ channel) { int i; /* no need for endianness handling since whole word copy */ uint16_t* casted_avg_data = (uint16_t*) white_data; DBG (3, "set_calib_data_calibration: channel: %i\n", channel); for (i = 0; i < format->pixel_per_line; ++ i) conv_out_data [i] = casted_avg_data [i * 3 + channel]; DBG (3, "set_calib_data: sending %zu bytes now\n", conv_out_size); memset (&scmd, 0, sizeof (scmd)); scmd.opc = AVISION_SCSI_SEND; scmd.datatypecode = send_type; /* send calibration data */ set_double (scmd.datatypequal, channel); set_triple (scmd.transferlen, conv_out_size); status = avision_cmd (&s->av_con, &scmd, sizeof (scmd), conv_out_data, conv_out_size, 0, 0); if (status != SANE_STATUS_GOOD) { DBG (3, "set_calib_data: send_data failed (%s)\n", sane_strstatus (status)); /* not return immediately to free mem at the end */ } } /* end for each channel */ free (conv_out_data); } /* end else send calib data*/ } return SANE_STATUS_GOOD; } /* Sort data pixel by pixel and average first 2/3 of the data. The caller has to free return pointer. R,G,B pixels interleave to R,G,B line interleave. The input data data is in 16 bits little endian, always. That is a = b[1] << 8 + b[0] in all system. We convert it to SCSI high-endian (big-endian) since we use it all over the place anyway .... - Sorry for this mess. */ static uint8_t* sort_and_average (struct calibration_format* format, uint8_t* data) { const size_t elements_per_line = format->pixel_per_line * format->channels; const size_t stride = format->bytes_per_channel * elements_per_line; size_t i, line; uint8_t *sort_data, *avg_data; DBG (1, "sort_and_average:\n"); if (!format || !data) return NULL; sort_data = malloc (format->lines * 2); if (!sort_data) return NULL; avg_data = malloc (elements_per_line * 2); if (!avg_data) { free (sort_data); return NULL; } /* for each pixel */ for (i = 0; i < elements_per_line; ++ i) { uint8_t* ptr1 = data + i * format->bytes_per_channel; uint16_t temp; /* copy all lines for pixel i into the linear array sort_data */ for (line = 0; line < format->lines; ++ line) { uint8_t* ptr2 = ptr1 + line * stride; /* pixel */ if (format->bytes_per_channel == 1) temp = 0xffff * *ptr2 / 255; else temp = get_double_le (ptr2); /* little-endian! */ set_double ((sort_data + line*2), temp); /* store big-endian */ /* DBG (7, "ReneR to sort: %x\n", temp); */ } temp = bubble_sort (sort_data, format->lines); /* DBG (7, "ReneR averaged: %x\n", temp); */ set_double ((avg_data + i*2), temp); /* store big-endian */ } free ((void *) sort_data); return avg_data; } /* shading data is 16bits little endian format when send/read from firmware */ static void compute_dark_shading_data (Avision_Scanner* s, struct calibration_format* format, uint8_t* data) { uint16_t map_value = DEFAULT_DARK_SHADING; uint16_t rgb_map_value[3]; int elements_per_line, i; DBG (3, "compute_dark_shading_data:\n"); if (s->hw->inquiry_max_shading_target != INVALID_DARK_SHADING) map_value = (uint16_t) (s->hw->inquiry_max_shading_target << 8); rgb_map_value[0] = format->r_dark_shading_target; rgb_map_value[1] = format->g_dark_shading_target; rgb_map_value[2] = format->b_dark_shading_target; for (i = 0; i < format->channels; ++i) { if (rgb_map_value[i] == INVALID_DARK_SHADING) rgb_map_value[i] = map_value; } if (format->channels == 1) { /* set to green, TODO: should depend on color drop-out and true-gray -ReneR */ rgb_map_value[0] = rgb_map_value[1] = rgb_map_value[2] = rgb_map_value[1]; } elements_per_line = format->pixel_per_line * format->channels; /* Check line interleave or pixel interleave. */ /* It seems no ASIC use line interleave right now. */ /* Avision SCSI protocol document has bad description. */ for (i = 0; i < elements_per_line; ++i) { uint16_t tmp_data = (uint16_t) get_double_le((data + i*2)); if (tmp_data > rgb_map_value[i % 3]) { set_double ((data + i*2), tmp_data - rgb_map_value[i % 3]); } else { set_double ((data + i*2), 0); } } } static void compute_white_shading_data (Avision_Scanner* s, struct calibration_format* format, uint8_t* data) { int i; uint16_t inquiry_mst = DEFAULT_WHITE_SHADING; uint16_t mst[3]; int elements_per_line = format->pixel_per_line * format->channels; /* debug counter */ int values_invalid = 0; int values_limitted = 0; DBG (3, "compute_white_shading_data:\n"); if (s->hw->inquiry_max_shading_target != INVALID_WHITE_SHADING) inquiry_mst = (uint16_t) (s->hw->inquiry_max_shading_target << 4); mst[0] = format->r_shading_target; mst[1] = format->g_shading_target; mst[2] = format->b_shading_target; for (i = 0; i < 3; ++i) { if (mst[i] == INVALID_WHITE_SHADING) /* mst[i] > MAX_WHITE_SHADING) */ { DBG (3, "compute_white_shading_data: target %d invalid (%x) using inquiry (%x)\n", i, mst[i], inquiry_mst); mst[i] = inquiry_mst; } /* some firmware versions seems to return the bytes swapped? */ else if (mst[i] < 0x110) { uint8_t* swap_mst = (uint8_t*) &mst[i]; uint8_t low_nibble_mst = swap_mst [0]; swap_mst [0] = swap_mst[1]; swap_mst [1] = low_nibble_mst; DBG (3, "compute_white_shading_data: target %d: bytes swapped.\n", i); } if (mst[i] < DEFAULT_WHITE_SHADING / 2) { DBG (3, "compute_white_shading_data: target %d: too low (%d) using default (%d).\n", i, mst[i], DEFAULT_WHITE_SHADING); mst[i] = DEFAULT_WHITE_SHADING; } else DBG (3, "compute_white_shading_data: target %d: %x\n", i, mst[0]); } /* some Avision example code was present here until SANE/Avision * BUILD 57. */ if (format->channels == 1) { /* set to green, TODO: should depend on color drop-out and true-gray -ReneR */ mst[0] = mst[1] = mst[2] = mst[1]; } /* calculate calibration data */ for (i = 0; i < elements_per_line; ++ i) { int result; /* calculate calibration value for pixel i */ uint16_t tmp_data = (uint16_t) get_double((data + i*2)); if (tmp_data == INVALID_WHITE_SHADING) { tmp_data = DEFAULT_WHITE_SHADING; ++ values_invalid; } result = (int) ( (int)mst[i % 3] * WHITE_MAP_RANGE / (tmp_data + 0.5)); /* sanity check for over-amplification, clipping */ if (result > MAX_WHITE_SHADING) { result = WHITE_MAP_RANGE; ++ values_limitted; } /* for visual debugging ... */ if (static_calib_list [i % 3] == SANE_TRUE) result = 0xA000; /* the output to the scanner will be 16 bit little endian again */ set_double_le ((data + i*2), result); } DBG (3, "compute_white_shading_data: %d invalid, %d limited\n", values_invalid, values_limitted); } /* old_r_calibration was here until SANE/Avision BUILD 90 */ static SANE_Status normal_calibration (Avision_Scanner* s) { SANE_Status status; struct calibration_format calib_format; size_t calib_data_size; int calib_bytes_per_line; uint8_t read_type; uint8_t *calib_tmp_data; DBG (1, "normal_calibration:\n"); /* get calibration format and data */ status = get_calib_format (s, &calib_format); if (status != SANE_STATUS_GOOD) return status; /* check if need do calibration */ if (calib_format.flags != 1 && !(s->hw->hw->feature_type & AV_FORCE_CALIB)) { DBG (1, "normal_calibration: Scanner claims no calibration needed -> skipped!\n"); return SANE_STATUS_GOOD; } /* calculate calibration data size for read from scanner */ /* size = lines * bytes_per_channel * pixels_per_line * channel */ calib_bytes_per_line = calib_format.bytes_per_channel * calib_format.pixel_per_line * calib_format.channels; calib_data_size = (size_t) calib_format.lines * (size_t) calib_bytes_per_line; calib_tmp_data = malloc (calib_data_size); if (!calib_tmp_data) return SANE_STATUS_NO_MEM; /* check if we need to do dark calibration (shading) */ if (BIT(calib_format.ability1, 2)) { DBG (1, "normal_calibration: reading dark data\n"); /* read dark calib data */ status = get_calib_data (s, 0x66, calib_tmp_data, calib_data_size); if (status != SANE_STATUS_GOOD) { free (calib_tmp_data); return status; } /* process dark data: sort and average. */ if (s->dark_avg_data) { free (s->dark_avg_data); s->dark_avg_data = 0; } s->dark_avg_data = sort_and_average (&calib_format, calib_tmp_data); if (!s->dark_avg_data) { free (calib_tmp_data); return SANE_STATUS_NO_MEM; } compute_dark_shading_data (s, &calib_format, s->dark_avg_data); } /* do we use a color mode? */ if (calib_format.channels > 1) { DBG (3, "normal_calibration: using color calibration\n"); read_type = 0x62; /* read color calib data */ } else { DBG (3, "normal_calibration: using gray calibration\n"); read_type = 0x61; /* gray calib data */ } /* do white calibration: read gray or color data */ status = get_calib_data (s, read_type, calib_tmp_data, calib_data_size); if (status != SANE_STATUS_GOOD) { free (calib_tmp_data); return status; } if (0) /* debug */ { FILE* f = NULL; f = fopen ("calibration-white.pnm", "w"); write_pnm_header (f, AV_GRAYSCALE, calib_format.bytes_per_channel * 8, calib_format.pixel_per_line, calib_format.lines * calib_format.channels); fwrite (calib_tmp_data, 1, calib_data_size, f); fclose (f); } if (s->white_avg_data) { free (s->white_avg_data); s->white_avg_data = 0; } s->white_avg_data = sort_and_average (&calib_format, calib_tmp_data); if (!s->white_avg_data) { free (calib_tmp_data); return SANE_STATUS_NO_MEM; } /* decrease white average data (if dark average data is present) */ if (s->dark_avg_data) { int elements_per_line = calib_format.pixel_per_line * calib_format.channels; int i; 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]; } } compute_white_shading_data (s, &calib_format, s->white_avg_data); status = set_calib_data (s, &calib_format, s->dark_avg_data, s->white_avg_data); free (calib_tmp_data); return status; } /* next was taken from the GIMP and is a bit modified ... ;-) * original Copyright (C) 1995 Spencer Kimball and Peter Mattis */ static double brightness_contrast_func (double brightness, double contrast, double value) { double nvalue; double power; /* apply brightness */ if (brightness < 0.0) value = value * (1.0 + brightness); else value = value + ((1.0 - value) * brightness); /* apply contrast */ if (contrast < 0.0) { if (value > 0.5) nvalue = 1.0 - value; else nvalue = value; if (nvalue < 0.0) nvalue = 0.0; nvalue = 0.5 * pow (nvalue * 2.0 , (double) (1.0 + contrast)); if (value > 0.5) value = 1.0 - nvalue; else value = nvalue; } else { if (value > 0.5) nvalue = 1.0 - value; else nvalue = value; if (nvalue < 0.0) nvalue = 0.0; power = (contrast == 1.0) ? 127 : 1.0 / (1.0 - contrast); nvalue = 0.5 * pow (2.0 * nvalue, power); if (value > 0.5) value = 1.0 - nvalue; else value = nvalue; } return value; } static SANE_Status send_gamma (Avision_Scanner* s) { Avision_Device* dev = s->hw; SANE_Status status = SANE_STATUS_GOOD; int invert_table = 0; size_t gamma_table_raw_size; size_t gamma_table_size; size_t gamma_values; struct command_send scmd; uint8_t *gamma_data; int color; /* current color */ size_t i; /* big table index */ size_t j; /* little table index */ size_t k; /* big table sub index */ double v1, v2; double brightness; double contrast; if (dev->inquiry_asic_type != AV_ASIC_OA980) invert_table = (s->c_mode == AV_THRESHOLDED) || (s->c_mode == AV_DITHERED); switch (dev->inquiry_asic_type) { case AV_ASIC_Cx: case AV_ASIC_C1: gamma_table_raw_size = 4096; gamma_table_size = 2048; break; case AV_ASIC_C5: gamma_table_raw_size = 256; gamma_table_size = 256; break; break; case AV_ASIC_OA980: gamma_table_raw_size = 4096; gamma_table_size = 4096; break; case AV_ASIC_OA982: gamma_table_raw_size = 256; gamma_table_size = 256; break; default: gamma_table_raw_size = 512; /* SPEC claims: 256 ... ? */ gamma_table_size = 512; } gamma_values = gamma_table_size / 256; DBG (3, "send_gamma: table_raw_size: %lu, table_size: %lu\n", (u_long) gamma_table_raw_size, (u_long) gamma_table_size); DBG (3, "send_gamma: values: %lu, invert_table: %d\n", (u_long) gamma_values, invert_table); /* prepare for emulating contrast, brightness ... via the gamma-table */ brightness = SANE_UNFIX (s->val[OPT_BRIGHTNESS].w); brightness /= 100; contrast = SANE_UNFIX (s->val[OPT_CONTRAST].w); contrast /= 100; DBG (3, "send_gamma: brightness: %f, contrast: %f\n", brightness, contrast); gamma_data = malloc (gamma_table_raw_size); if (!gamma_data) return SANE_STATUS_NO_MEM; memset (&scmd, 0, sizeof (scmd) ); scmd.opc = AVISION_SCSI_SEND; scmd.datatypecode = AVISION_DATATYPECODE_DOWNLOAD_GAMMA_TABLE; /* 0x81 for download gamma table */ set_triple (scmd.transferlen, gamma_table_raw_size); for (color = 0; color < 3 && status == SANE_STATUS_GOOD; ++ color) { /* color: 0=red; 1=green; 2=blue */ set_double (scmd.datatypequal, color); i = 0; /* big table index */ for (j = 0; j < 256; ++ j) /* little table index */ { /* calculate mode dependent values v1 and v2 * v1 <- current value for table * v2 <- next value for table (for interpolation) */ switch (s->c_mode) { case AV_TRUECOLOR: case AV_TRUECOLOR12: case AV_TRUECOLOR16: { v1 = (double) s->gamma_table [1 + color][j]; if (j == 255) v2 = (double) v1; else v2 = (double) s->gamma_table [1 + color][j + 1]; } break; default: /* for all other modes: */ { v1 = (double) s->gamma_table [0][j]; if (j == 255) v2 = (double) v1; else v2 = (double) s->gamma_table [0][j + 1]; } } /*end switch */ /* Emulate brightness and contrast (at least the Avision AV6[2,3]0 * as well as many others do not have a hardware implementation, * --$. The function was taken from the GIMP source - maybe I'll * optimize it in the future (when I have spare time). */ v1 /= 255; v2 /= 255; v1 = (brightness_contrast_func (brightness, contrast, v1) ); v2 = (brightness_contrast_func (brightness, contrast, v2) ); v1 *= 255; v2 *= 255; if (invert_table) { v1 = 255 - v1; v2 = 255 - v2; if (v1 <= 0) v1 = 0; if (v2 <= 0) v2 = 0; } if (s->hw->hw->feature_type & AV_GAMMA_UINT16) { /* Use some pointer-cast magic to use gamma_data as uint16 array and write as big-endian since values get swapped */ ((uint16_t *)gamma_data) [i++] = (uint16_t)v1<<8; } else { /* interpolate gamma_values to gamma_data */ for (k = 0; k < gamma_values; ++ k, ++ i) { gamma_data [i] = (uint8_t) (((v1 * (double) (gamma_values - k)) + (v2 * (double) k) ) / (double) gamma_values); } } } /* with AV_GAMMA_UINT16 only every second value is filled, so double i */ if (s->hw->hw->feature_type & AV_GAMMA_UINT16) i *= 2; /* fill the gamma table - (e.g.) if 11bit (old protocol) table */ { size_t t_i = i-1; if (i < gamma_table_raw_size) { DBG (4, "send_gamma: (old protocol) - filling the table.\n"); for ( ; i < gamma_table_raw_size; ++ i) gamma_data [i] = gamma_data [t_i]; } } DBG (4, "send_gamma: sending %lu bytes gamma table.\n", (u_long) gamma_table_raw_size); status = avision_cmd (&s->av_con, &scmd, sizeof (scmd), gamma_data, gamma_table_raw_size, 0, 0); if (status != SANE_STATUS_GOOD) { DBG (1, "send_gamma: gamma table upload failed: %s\n", sane_strstatus (status)); } } free (gamma_data); return status; } static SANE_Status send_3x3_matrix (Avision_Scanner* s) { SANE_Status status; #define SIGN_BIT 0x1000 #define INT_PART 10 struct matrix_cmd { struct command_send scmd; struct matrix_3x3 matrix; } cmd; /* 04 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00 */ int i, a_i; static const double c5_matrix[] = { 1.000, 0.000, 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 1.000 }; double a_f, b_f; uint16_t m; DBG (3, "send_3x3_matrix:\n"); memset (&cmd, 0, sizeof (cmd)); for (i = 0; i < 9; i++) { m = 0; a_f = c5_matrix[i]; if (a_f < 0) { m |= SIGN_BIT; a_f = -a_f; } a_i = (int) a_f; /* integer */ b_f = a_f - (double) a_i; /* float */ m |= (uint16_t) ((a_i & 0x3) << INT_PART); m |= (uint16_t) (b_f * 1024); set_double (((uint8_t*)(&cmd.matrix.v[i])), m); } cmd.scmd.opc = AVISION_SCSI_SEND; cmd.scmd.datatypecode = AVISION_DATATYPECODE_3X3_COLOR_MATRIX; /* 0x83 for 3x3 color matrix */ set_triple (cmd.scmd.transferlen, sizeof (struct matrix_3x3)); if (1) { DBG (3, "send_3x3_matrix: sending matrix split into two commands\n"); status = avision_cmd (&s->av_con, &cmd.scmd, sizeof (cmd.scmd), &cmd.matrix, sizeof(cmd.matrix), 0, 0); } else { DBG (3, "send_3x3_matrix: sending matrix in one command\n"); status = avision_cmd (&s->av_con, &cmd, sizeof (cmd), 0, 0, 0, 0); } return status; } static SANE_Status get_acceleration_info (Avision_Scanner* s, struct acceleration_info* info) { SANE_Status status; struct command_read rcmd; uint8_t result [24]; size_t size; DBG (3, "get_acceleration_info:\n"); size = sizeof (result); memset (&rcmd, 0, sizeof (rcmd)); rcmd.opc = AVISION_SCSI_READ; rcmd.datatypecode = AVISION_DATATYPECODE_ACCELERATION_TABLE; /* get acceleration information */ set_double (rcmd.datatypequal, s->hw->data_dq); set_triple (rcmd.transferlen, size); DBG (3, "get_acceleration_info: read_data: %lu bytes\n", (u_long) size); status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd), 0, 0, result, &size); if (status != SANE_STATUS_GOOD || size != sizeof (result) ) { status = (status != SANE_STATUS_GOOD)? status: SANE_STATUS_IO_ERROR; DBG (1, "get_acceleration_info: read accel. info failed (%s)\n", sane_strstatus (status) ); return status; } debug_print_accel_info (3, "get_acceleration_info", result); info->total_steps = (uint16_t) get_double (&(result[0])); info->stable_steps = (uint16_t) get_double (&(result[2])); info->table_units = (uint32_t) get_quad (&(result[4])); info->base_units = (uint32_t) get_quad (&(result[8])); info->start_speed = (uint16_t) get_double (&(result[12])); info->target_speed = (uint16_t) get_double (&(result[14])); info->ability = result[16]; info->table_count = result[17]; return SANE_STATUS_GOOD; } static SANE_Status send_acceleration_table (Avision_Scanner* s) { SANE_Status status; struct command_send scmd; int table = 0; int i; struct acceleration_info accel_info = accel_info; uint8_t* table_data; DBG (3, "send_acceleration_table:\n"); do { status = get_acceleration_info (s, &accel_info); if (accel_info.table_count == 0) { DBG (3, "send_acceleration_table: device does not need tables\n"); return SANE_STATUS_GOOD; } 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: table does not look right.\n"); return SANE_STATUS_INVAL; } if (accel_info.ability != 0) { DBG (1, "send_acceleration_table: ability non-zero - insert code\n"); return SANE_STATUS_INVAL; } /* so far I assume we have one byte tables as used in the C6 ASIC ... */ table_data = malloc (accel_info.total_steps + 1000); memset (&scmd, 0x00, sizeof (scmd)); scmd.opc = AVISION_SCSI_SEND; scmd.datatypecode = AVISION_DATATYPECODE_ACCELERATION_TABLE; /* send acceleration table */ set_double (scmd.datatypequal, table); set_triple (scmd.transferlen, accel_info.total_steps); /* construct the table - Warning: This code is derived from Avision sample code and is a bit scary! I have no idea why the scanner needs such a dumb table and also do not know /why/ it has to be constructed this way. "Works for me" -ReneR */ { float low_lim = (float) 0.001; float up_lim = 1.0; uint16_t accel_steps = (uint16_t) (accel_info.total_steps - accel_info.stable_steps + 1); /* acceleration ramp */ while ((up_lim - low_lim) > 0.0001) { float mid = (up_lim + low_lim) / 2; /* accel rate */ uint16_t now_count = accel_info.start_speed; uint16_t i = 0; float now_count_f = now_count; table_data [i++] = (uint8_t) accel_info.start_speed; while (now_count != accel_info.target_speed) { now_count_f = now_count_f - (now_count_f - accel_info.target_speed) * mid; now_count = (uint16_t)(now_count_f + 0.5); table_data[i++] = (uint8_t) now_count; } if (i == accel_steps) break; if (i > accel_steps) low_lim = mid; else up_lim = mid; } /* fill stable steps */ for (i = accel_steps; i < accel_info.total_steps; i++) table_data [i] = table_data [i-1]; debug_print_hex_raw (5, "send_acceleration_table: first pass:\n", table_data, accel_info.total_steps); /* maybe post fix-up */ { int add_count; /* count total steps in table */ uint32_t table_total = 0; for (i = 0; i < accel_info.total_steps; i++) table_total += table_data [i]; i = 0; if (((table_total * accel_info.table_units) % accel_info.base_units) == 0) add_count = 0; else add_count = (int) ((accel_info.base_units - ((table_total*accel_info.table_units) % accel_info.base_units)) / accel_info.table_units); /* add_count should not be bigger than 255 */ if (add_count > 255) { DBG (1, "send_acceleration_table: add_count limited, was: %d\n", add_count); add_count = 255; } for (i = 0; i < accel_info.total_steps - 1 && add_count > 0; i++) { uint16_t temp_count = 255 - table_data [i]; temp_count = (uint16_t) (temp_count > add_count ? add_count : temp_count); table_data [i] += (uint8_t) temp_count; add_count -= temp_count; } } } debug_print_hex_raw (5, "send_acceleration_table: fixed up:\n", table_data, accel_info.total_steps); /* decrease all by one ... */ for (i = 0; i < accel_info.total_steps; i++) { table_data[i]--; } DBG (1, "send_acceleration_table: sending table %d\n", table); debug_print_hex_raw (5, "send_acceleration_table: final:\n", table_data, accel_info.total_steps); status = avision_cmd (&s->av_con, &scmd, sizeof (scmd), (char*) table_data, accel_info.total_steps, 0, 0); if (status != SANE_STATUS_GOOD) { DBG (3, "send_acceleration_table: send_data failed (%s)\n", sane_strstatus (status)); } free (table_data); table_data = 0; table++; } while (table < accel_info.table_count); return status; } static SANE_Status set_window (Avision_Scanner* s) { Avision_Device* dev = s->hw; SANE_Status status; int base_dpi_abs, base_dpi_rel; size_t transferlen; size_t paralen; int bytes_per_line; int line_count; struct { struct command_set_window cmd; struct command_set_window_window window; } cmd; DBG (1, "set_window:\n"); /* plain old scanners, the C3 ASIC HP 53xx and the C6 ASIC HP 74xx and up do use 1200 as base - only the C5 differs */ switch (dev->inquiry_asic_type) { case AV_ASIC_C5: base_dpi_abs = 1200; /* round down to the next multiple of 300 */ base_dpi_rel = s->avdimen.hw_xres - s->avdimen.hw_xres % 300; if (base_dpi_rel > dev->inquiry_optical_res) base_dpi_rel = dev->inquiry_optical_res; else if (s->avdimen.hw_xres <= 150) base_dpi_rel = 150; break; default: base_dpi_abs = 1200; base_dpi_rel = 1200; } DBG (2, "set_window: base_dpi_abs: %d, base_dpi_rel: %d\n", base_dpi_abs, base_dpi_rel); /* wipe out anything */ memset (&cmd, 0, sizeof (cmd) ); cmd.window.descriptor.winid = AV_WINID; /* normally defined to be zero */ /* optional parameter length to use */ paralen = sizeof (cmd.window.avision) - sizeof (cmd.window.avision.type); DBG (2, "set_window: base paralen: %zu\n", paralen); if (dev->hw->feature_type & AV_FUJITSU) paralen += sizeof (cmd.window.avision.type.fujitsu); else if (!dev->inquiry_new_protocol) paralen += sizeof (cmd.window.avision.type.old); else if (dev->hw->feature_type & AV_MULTI_SHEET_SCAN) paralen += sizeof (cmd.window.avision.type.normal); else paralen += sizeof (cmd.window.avision.type.normal) - 1; DBG (2, "set_window: final paralen: %zu\n", paralen); transferlen = sizeof (cmd.window) - sizeof (cmd.window.avision) + paralen; DBG (2, "set_window: transferlen: %zu\n", transferlen); /* command setup */ cmd.cmd.opc = AVISION_SCSI_SET_WINDOW; set_triple (cmd.cmd.transferlen, transferlen); set_double (cmd.window.header.desclen, sizeof (cmd.window.descriptor) + paralen); /* resolution parameters */ set_double (cmd.window.descriptor.xres, s->avdimen.hw_xres); set_double (cmd.window.descriptor.yres, s->avdimen.hw_yres); /* upper left corner x/y as well as width/length in inch * base_dpi - avdimen are world pixels */ set_quad (cmd.window.descriptor.ulx, s->avdimen.tlx * base_dpi_abs / s->avdimen.hw_xres); set_quad (cmd.window.descriptor.uly, s->avdimen.tly * base_dpi_abs / s->avdimen.hw_yres); set_quad (cmd.window.descriptor.width, s->avdimen.hw_pixels_per_line * base_dpi_rel / s->avdimen.hw_xres + 1); line_count = s->avdimen.hw_lines + 2 * s->avdimen.line_difference; set_quad (cmd.window.descriptor.length, line_count * base_dpi_rel / s->avdimen.hw_yres + 1); /* interlaced duplex scans are twice as long */ if (s->avdimen.interlaced_duplex && dev->scanner_type != AV_FILM) { DBG (2, "set_window: interlaced duplex scan, doubled line count\n"); line_count *= 2; } bytes_per_line = s->avdimen.hw_bytes_per_line; set_double (cmd.window.avision.line_width, bytes_per_line); set_double (cmd.window.avision.line_count, line_count); /* here go the most significant bits if bigger than 16 bit */ if (dev->inquiry_new_protocol && !(dev->hw->feature_type & AV_FUJITSU) ) { DBG (2, "set_window: large data-transfer support (>16bit)!\n"); cmd.window.avision.type.normal.line_width_msb = (uint8_t) (bytes_per_line >> 16); cmd.window.avision.type.normal.line_count_msb = (uint8_t) (line_count >> 16); } if (dev->inquiry_background_raster) cmd.window.avision.type.normal.background_lines = (uint8_t) s->val[OPT_BACKGROUND].w; /* scanner should use our line-width and count */ SET_BIT (cmd.window.avision.bitset1, 6); /* set speed */ cmd.window.avision.bitset1 |= (uint8_t) (s->val[OPT_SPEED].w & 0x07); /* only 3 bit */ /* ADF scan? */ DBG (3, "set_window: source mode %d source mode dim %d\n", s->source_mode, s->source_mode_dim); if (s->source_mode == AV_ADF || s->source_mode == AV_ADF_REAR || s->source_mode == AV_ADF_DUPLEX) { DBG (3, "set_window: filling ADF bits\n"); SET_BIT (cmd.window.avision.bitset1, 7); if (dev->hw->feature_type & AV_MULTI_SHEET_SCAN) { /* Always set bit 7 to enable single_sheet_scan option (defaults to off). This removes the 1s pause between two sheets and fixes some offsets. */ SET_BIT(cmd.window.avision.type.normal.bitset3, 7); cmd.window.avision.type.normal.single_sheet_scan = 0; } /* normal, interlaced duplex scanners */ if (dev->inquiry_duplex_interlaced) { DBG (3, "set_window: interlaced duplex type\n"); if (s->source_mode == AV_ADF_REAR) { SET_BIT(cmd.window.avision.type.normal.bitset3, 3); /* 0x08 */ } if (s->source_mode == AV_ADF_DUPLEX) { SET_BIT(cmd.window.avision.type.normal.bitset3, 4); /* 0x10 */ } } else if (s->source_mode == AV_ADF_DUPLEX) /* HP 2-pass duplex */ { DBG (3, "set_window: non-interlaced duplex type (HP)\n"); SET_BIT(cmd.window.avision.type.normal.bitset3, 0); /* DPLX 0x01 */ if (s->val[OPT_ADF_FLIP].w) SET_BIT(cmd.window.avision.type.normal.bitset3, 1); /* FLIP 0x02 */ SET_BIT(cmd.window.avision.type.normal.bitset3, 2); /* MIRR 0x04 */ } } if (s->val[OPT_PAPERLEN].w != SANE_FALSE) { set_double (cmd.window.descriptor.paper_length, (int)((double)30.0*1200)); } if ( !(dev->hw->feature_type & AV_FUJITSU) ) { /* quality scan option switch */ if (s->val[OPT_QSCAN].w == SANE_TRUE && !(dev->hw->feature_type & AV_NO_QSCAN_MODE)) { SET_BIT (cmd.window.avision.type.normal.bitset2, 4); } /* quality calibration option switch (inverted! if set == speed) */ if (s->val[OPT_QCALIB].w == SANE_FALSE && !(dev->hw->feature_type & AV_NO_QCALIB_MODE)) { SET_BIT (cmd.window.avision.type.normal.bitset2, 3); } /* transparency option switch */ if (s->source_mode_dim == AV_TRANSPARENT_DIM) { SET_BIT (cmd.window.avision.type.normal.bitset2, 7); } if (dev->scanner_type == AV_FILM) { /* TODO: wire to IR exposure option? */ cmd.window.avision.type.normal.ir_exposure_time = 100; set_double (cmd.window.avision.type.normal.r_exposure_time, s->val[OPT_EXPOSURE].w); set_double (cmd.window.avision.type.normal.g_exposure_time, s->val[OPT_EXPOSURE].w); set_double (cmd.window.avision.type.normal.b_exposure_time, s->val[OPT_EXPOSURE].w); if (s->val[OPT_IR].w) cmd.window.avision.type.normal.bitset3 |= (1 << 0); if (s->val[OPT_MULTISAMPLE].w) cmd.window.avision.type.normal.bitset3 |= (1 << 1); } } /* fixed values */ cmd.window.descriptor.padding_and_bitset = 3; cmd.window.descriptor.vendor_specific = 0xFF; cmd.window.descriptor.paralen = (uint8_t) paralen; /* R² was: 9, later 14 */ /* This is normally unsupported by Avision scanners, and we do this via the gamma table - which works for all devices ... */ cmd.window.descriptor.threshold = 128; cmd.window.descriptor.brightness = 128; cmd.window.descriptor.contrast = 128; cmd.window.avision.highlight = 0xFF; cmd.window.avision.shadow = 0x00; /* mode dependent settings */ switch (s->c_mode) { case AV_THRESHOLDED: cmd.window.descriptor.bpc = 1; cmd.window.descriptor.image_comp = 0; break; case AV_DITHERED: cmd.window.descriptor.bpc = 1; cmd.window.descriptor.image_comp = 1; break; case AV_GRAYSCALE: cmd.window.descriptor.bpc = 8; cmd.window.descriptor.image_comp = 2; break; case AV_GRAYSCALE12: cmd.window.descriptor.bpc = 12; cmd.window.descriptor.image_comp = 2; break; case AV_GRAYSCALE16: cmd.window.descriptor.bpc = 16; cmd.window.descriptor.image_comp = 2; break; case AV_TRUECOLOR: cmd.window.descriptor.bpc = 8; cmd.window.descriptor.image_comp = 5; break; case AV_TRUECOLOR12: cmd.window.descriptor.bpc = 12; cmd.window.descriptor.image_comp = 5; break; case AV_TRUECOLOR16: cmd.window.descriptor.bpc = 16; cmd.window.descriptor.image_comp = 5; 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; } else { if (dev->hw->feature_type & AV_FASTER_WITH_FILTER) cmd.window.avision.bitset1 |= AVISION_FILTER_GREEN; else if (dev->hw->feature_type & AV_USE_GRAY_FILTER) cmd.window.avision.bitset1 |= AVISION_FILTER_GRAY; else cmd.window.avision.bitset1 |= AVISION_FILTER_NONE; } debug_print_window_descriptor (5, "set_window", &(cmd.window)); DBG (3, "set_window: sending command. Bytes: %zu\n", transferlen); status = avision_cmd (&s->av_con, &cmd, sizeof (cmd.cmd), &(cmd.window), transferlen, 0, 0); return status; } static SANE_Status get_background_raster (Avision_Scanner* s) { const int debug = 0; Avision_Device* dev = s->hw; SANE_Status status; struct command_read rcmd; size_t size; int bytes_per_line, i; const int bpp = color_mode_is_color (s->c_mode) ? 3 : 1; const int lines = s->val[OPT_BACKGROUND].w * (s->avdimen.interlaced_duplex ? 2 : 1); uint8_t* background = NULL; DBG (1, "get_background_raster:\n"); if (lines == 0) { DBG (1, "get_background_raster: no background requested\n"); return SANE_STATUS_GOOD; } /* full width, always :-(, duplex *2 for front and rear */ bytes_per_line = dev->inquiry_background_raster_pixel * s->avdimen.hw_xres / dev->inquiry_optical_res; bytes_per_line *= bpp; DBG (3, "get_background_raster: native raster pixels: %d, raster bytes_per_line: %d\n", dev->inquiry_background_raster_pixel, bytes_per_line); /* according to spec only 8-bit gray or color, TODO: test for bi-level scans */ size = (size_t) bytes_per_line * (size_t) lines; DBG (3, "get_background_raster: buffer size: %ld\n", (long)size); background = s->background_raster = realloc (s->background_raster, size); if (!background) return SANE_STATUS_NO_MEM; memset (&rcmd, 0, sizeof (rcmd)); rcmd.opc = AVISION_SCSI_READ; rcmd.datatypecode = AVISION_DATATYPECODE_GET_BACKGROUND_RASTER; /* get background raster */ set_double (rcmd.datatypequal, s->hw->data_dq); /* Ok, well - this part is very messy. The AV122 and DM152 appear to contain differently buggy ASICs. The only combination I found to at least get a correct front raster out of them is to read it line by line and then every second line appears to be valid front data, ... */ /* read the raster data */ for (i = 0; i < lines;) { uint8_t* dst_raster = background + bytes_per_line * i; /* read stripe by stripe, or all in one chunk */ size_t this_read, read_size; int this_lines; if (dev->hw->feature_type & AV_2ND_LINE_INTERLACED) { if (dev->hw->feature_type & AV_BACKGROUND_QUIRK) this_lines = 1; else this_lines = lines; } else { this_lines = s->val[OPT_BACKGROUND].w; } this_read = (size_t) bytes_per_line * (size_t) this_lines; DBG (3, "get_background_raster: line: %d, lines: %d, %lu bytes\n", i, this_lines, (u_long) this_read); set_triple (rcmd.transferlen, this_read); read_size = this_read; status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd), 0, 0, dst_raster, &read_size); if (status != SANE_STATUS_GOOD || read_size != this_read) { status = (status != SANE_STATUS_GOOD)? status: SANE_STATUS_IO_ERROR; DBG (1, "get_background_raster: read raster failed (%s)\n", sane_strstatus (status) ); return status; } i += this_lines; } /* dump raw result while debugging */ if (debug) { FILE* f = NULL; f = fopen ("background-raw.pnm", "w"); write_pnm_header (f, (color_mode_is_color (s->c_mode) ? AV_TRUECOLOR : AV_GRAYSCALE), 8, bytes_per_line / bpp, lines); fwrite (background, 1, (size_t) bytes_per_line * (size_t) lines, f); fclose (f); } /* line-pack - move to unified processing flow, later */ if (dev->inquiry_needs_line_pack) { /* TODO: add 16bit per sample code? */ int l, p; uint8_t* tmp_data = malloc ((size_t) bytes_per_line); for (l = 0; l < lines; ++l) { uint8_t* out_data = tmp_data; uint8_t* r_ptr = background + (bytes_per_line * l); uint8_t* g_ptr = r_ptr + bytes_per_line / bpp; uint8_t* b_ptr = g_ptr + bytes_per_line / bpp; for (p = 0; p < bytes_per_line;) { out_data [p++] = *(r_ptr++); out_data [p++] = *(g_ptr++); out_data [p++] = *(b_ptr++); } memcpy (background + (bytes_per_line * l), tmp_data, (size_t) bytes_per_line); } free (tmp_data); } /* end line pack */ /* deinterlace? */ if (s->avdimen.interlaced_duplex && (dev->hw->feature_type & AV_2ND_LINE_INTERLACED)) { uint8_t* deinterlaced = malloc (size * 2); if (!deinterlaced) return SANE_STATUS_NO_MEM; for (i = 0; i < lines; ++i) { int dst_i = i / 2 + (i % 2) * (lines / 2); uint8_t* dst_raster; /* just no C99 in SANE :-( */ uint8_t* src_raster; /* for the quirky devices and some resolutions the interlacing differs */ if ((dev->hw->feature_type & AV_BACKGROUND_QUIRK) && (s->avdimen.hw_xres >= 150)) dst_i = i / 2 + ((i+1) % 2) * (lines / 2); dst_raster = deinterlaced + bytes_per_line * dst_i; src_raster = background + bytes_per_line * i; DBG(3, "get_background_raster: deinterlaced %d -> %d\n", i, dst_i); memcpy(dst_raster, src_raster, (size_t) bytes_per_line); } free (background); background = s->background_raster = deinterlaced; } /* dump raw result while debugging */ for (i = 0; debug && i < (s->avdimen.interlaced_duplex ? 2 : 1); ++i) { FILE* f = NULL; uint8_t* raster = background; if (i == 0) { f = fopen ("background.pnm", "w"); } else { f = fopen ("background-rear.pnm", "w"); raster += bytes_per_line * s->val[OPT_BACKGROUND].w; } write_pnm_header (f, (color_mode_is_color (s->c_mode) ? AV_TRUECOLOR : AV_GRAYSCALE), 8, bytes_per_line / bpp, s->val[OPT_BACKGROUND].w); fwrite (raster, 1, (size_t) bytes_per_line * (size_t) s->val[OPT_BACKGROUND].w, f); fclose (f); } /* crop from full-width scanlines to scan window */ { uint8_t *dst_ptr, *src_ptr; dst_ptr = background; src_ptr = background + s->avdimen.tlx * bpp; for (i = 0; i < lines; ++i) { memmove (dst_ptr, src_ptr, (size_t) s->avdimen.hw_bytes_per_line); dst_ptr += s->avdimen.hw_bytes_per_line; src_ptr += bytes_per_line; } } /* soft-scale - move to unified processing flow, later */ if (s->avdimen.hw_xres != s->avdimen.xres) { const uint8_t* out_data = background; uint8_t* dst = background; int l; for (l = 0; l < lines; ++l) { const int hwbpl = s->avdimen.hw_bytes_per_line; const int sy = l; int x; for (x = 0; x < s->params.pixels_per_line; ++x) { const double bx = (-1.0 + s->avdimen.hw_pixels_per_line) * x / s->params.pixels_per_line; const int sx = (int)floor(bx); const int xdist = (int) ((bx - sx) * 256); const int sxx = sx + 1; switch (bpp) { case 1: { uint8_t v = (uint8_t) (( out_data [sy*hwbpl + sx ] * (256-xdist) + out_data [sy*hwbpl + sxx] * xdist ) / (256)); *dst++ = v; } break; case 3: { int c; for (c = 0; c < 3; ++c) { uint8_t v = (uint8_t) (( out_data [sy*hwbpl + sx*3 + c] * (256-xdist) + out_data [sy*hwbpl + sxx*3 + c] * xdist ) / (256)); *dst++ = v; } } break; } } } } /* dump final result while debugging */ if (debug) { for (i = 0; i < (s->avdimen.interlaced_duplex ? 2 : 1); ++i) { FILE* f = NULL; uint8_t* raster = background; if (i == 0) { f = fopen ("background-final.pnm", "w"); } else { f = fopen ("background-final-rear.pnm", "w"); raster += s->params.bytes_per_line * s->val[OPT_BACKGROUND].w; } write_pnm_header (f, (color_mode_is_color (s->c_mode) ? AV_TRUECOLOR : AV_GRAYSCALE), 8, s->params.bytes_per_line / bpp, s->val[OPT_BACKGROUND].w); fwrite (raster, 1, (size_t) s->params.bytes_per_line * (size_t) s->val[OPT_BACKGROUND].w, f); fclose (f); } } return SANE_STATUS_GOOD; } static SANE_Status reserve_unit (Avision_Scanner* s) { char cmd[] = {AVISION_SCSI_RESERVE_UNIT, 0, 0, 0, 0, 0}; SANE_Status status; DBG (1, "reserve_unit:\n"); status = avision_cmd (&s->av_con, cmd, sizeof (cmd), 0, 0, 0, 0); return status; } static SANE_Status release_unit (Avision_Scanner* s, int type) { char cmd[] = {AVISION_SCSI_RELEASE_UNIT, 0, 0, 0, 0, 0}; SANE_Status status; DBG (1, "release unit: type: %d\n", type); cmd[5] = (char) type; /* latest scanners also allow 1: release paper and 2: end job */ status = avision_cmd (&s->av_con, cmd, sizeof (cmd), 0, 0, 0, 0); return status; } /* Check if a sheet is present. */ static SANE_Status media_check (Avision_Scanner* s) { char cmd[] = {AVISION_SCSI_MEDIA_CHECK, 0, 0, 0, 1, 0}; /* 1, 4 */ SANE_Status status; uint8_t result[1]; /* 4 */ size_t size = sizeof(result); status = avision_cmd (&s->av_con, cmd, sizeof (cmd), 0, 0, result, &size); debug_print_raw (5, "media_check: result\n", result, size); if (status == SANE_STATUS_GOOD) { if (!(result[0] & 0x1)) status = SANE_STATUS_NO_DOCS; } return status; } #if 0 /* unused */ static SANE_Status flush_media (Avision_Scanner* s) { Avision_Device* dev = s->hw; SANE_Status status; if (s->source_mode_dim == AV_ADF_DIM && dev->inquiry_batch_scan) { DBG (1, "flush_media: flushing pages out of batch scanner\n"); do { status = media_check (s); if (status == SANE_STATUS_GOOD) { SANE_Status status2 = reserve_unit (s); DBG (1, "flush_media: reserve status: %d\n", status2); status2 = release_unit (s, 0); DBG (1, "flush_media: release status: %d\n", status2); } } while (status == SANE_STATUS_GOOD); } return SANE_STATUS_GOOD; } #endif /* 0 - unused */ static SANE_Status object_position (Avision_Scanner* s, uint8_t position) { SANE_Status status; uint8_t cmd [10]; memset (cmd, 0, sizeof (cmd)); cmd[0] = AVISION_SCSI_OBJECT_POSITION; cmd[1] = position; DBG (1, "object_position: %d\n", position); status = avision_cmd (&s->av_con, cmd, sizeof(cmd), 0, 0, 0, 0); return status; } static SANE_Status start_scan (Avision_Scanner* s) { struct command_scan cmd; size_t size = sizeof (cmd); DBG (3, "start_scan:\n"); memset (&cmd, 0, sizeof (cmd)); cmd.opc = AVISION_SCSI_SCAN; cmd.transferlen = 1; /* AV610C2 in ADF preview mode does not detect the page end (...) */ if (s->val[OPT_PREVIEW].w == SANE_TRUE && s->hw->inquiry_asic_type != AV_ASIC_C7) { SET_BIT(cmd.bitset1,6); } if (s->val[OPT_QSCAN].w == SANE_TRUE && !(s->hw->hw->feature_type & AV_NO_QSCAN_MODE)) { SET_BIT(cmd.bitset1,7); } DBG (3, "start_scan: sending command. Bytes: %lu\n", (u_long) size); return avision_cmd (&s->av_con, &cmd, size, 0, 0, 0, 0); } static SANE_Status do_eof (Avision_Scanner *s) { int exit_status; DBG (3, "do_eof:\n"); /* we do not scan anymore */ s->prepared = s->scanning = SANE_FALSE; /* we can now mark the rear data as valid */ if (s->avdimen.interlaced_duplex || (s->hw->hw->feature_type & AV_ADF_FLIPPING_DUPLEX && s->source_mode == AV_ADF_DUPLEX)) { DBG (3, "do_eof: toggling duplex rear data valid\n"); s->duplex_rear_valid = !s->duplex_rear_valid; DBG (3, "do_eof: duplex rear data valid: %x\n", s->duplex_rear_valid); } if (s->read_fds >= 0) { close (s->read_fds); s->read_fds = -1; } /* join our processes - without a wait() you will produce zombies (defunct children) */ sanei_thread_waitpid (s->reader_pid, &exit_status); sanei_thread_invalidate (s->reader_pid); DBG (3, "do_eof: returning %d\n", exit_status); return (SANE_Status)exit_status; } static SANE_Status do_cancel (Avision_Scanner* s) { int status; DBG (3, "do_cancel:\n"); s->prepared = s->scanning = SANE_FALSE; s->duplex_rear_valid = SANE_FALSE; s->page = 0; s->cancelled = SANE_TRUE; if (s->read_fds >= 0) { close(s->read_fds); s->read_fds = -1; } if (sanei_thread_is_valid (s->reader_pid)) { int exit_status; /* ensure child knows it's time to stop: */ sanei_thread_kill (s->reader_pid); sanei_thread_waitpid (s->reader_pid, &exit_status); sanei_thread_invalidate (s->reader_pid); } if (s->hw->hw->feature_type & AV_FASTFEED_ON_CANCEL) { status = release_unit (s, 1); if (status != SANE_STATUS_GOOD) DBG (1, "do_cancel: release_unit failed\n"); } DBG (4, "FORCE RELEASE UNIT ON CANCEL\n"); status = release_unit (s, 1); if (status != SANE_STATUS_GOOD) DBG (1, "do_cancel: release_unit failed\n"); return SANE_STATUS_CANCELLED; } static SANE_Status read_data (Avision_Scanner* s, SANE_Byte* buf, size_t* count) { struct command_read rcmd; SANE_Status status; DBG (9, "read_data: %lu\n", (u_long) *count); memset (&rcmd, 0, sizeof (rcmd)); rcmd.opc = AVISION_SCSI_READ; rcmd.datatypecode = AVISION_DATATYPECODE_READ_IMAGE_DATA; /* read image data */ set_double (rcmd.datatypequal, s->hw->data_dq); set_triple (rcmd.transferlen, *count); status = avision_cmd (&s->av_con, &rcmd, sizeof (rcmd), 0, 0, buf, count); return status; } static SANE_Status init_options (Avision_Scanner* s) { Avision_Device* dev = s->hw; int i; DBG (3, "init_options:\n"); memset (s->opt, 0, sizeof (s->opt)); memset (s->val, 0, sizeof (s->val)); /* * Set defaults for all the options. * */ for (i = 0; i < NUM_OPTIONS; ++i) { s->opt[i].name = ""; s->opt[i].desc = ""; s->opt[i].unit = SANE_UNIT_NONE; s->opt[i].size = sizeof(SANE_Word); s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } /* Init the SANE option from the scanner inquiry data */ switch (dev->inquiry_asic_type) { case AV_ASIC_C2: dev->dpi_range.min = 100; break; case AV_ASIC_C5: dev->dpi_range.min = 80; break; case AV_ASIC_C6: /* TODO: AV610 in ADF mode does not scan less than 180 or so; */ /* Scanjet 8250 does not work with 50 in normal mode */ dev->dpi_range.min = 60; break; case AV_ASIC_C7: /* AV610C2 empirically tested out */ dev->dpi_range.min = 75; break; default: dev->dpi_range.min = 50; } DBG (1, "init_options: dpi_range.min set to %d\n", dev->dpi_range.min); dev->dpi_range.quant = 1; /* any, including 72, 144, etc. */ dev->dpi_range.max = dev->inquiry_max_res; dev->speed_range.min = (SANE_Int)0; dev->speed_range.max = (SANE_Int)4; dev->speed_range.quant = (SANE_Int)1; s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].size = sizeof(SANE_TYPE_INT); s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* "Mode" group: */ s->opt[OPT_MODE_GROUP].title = SANE_TITLE_SCAN_MODE; s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MODE_GROUP].cap = 0; s->opt[OPT_MODE_GROUP].size = 0; s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* color mode */ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; 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 = (SANE_Int) max_string_size (dev->color_list); s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_MODE].constraint.string_list = dev->color_list; s->val[OPT_MODE].s = strdup (dev->color_list[dev->color_list_default]); s->c_mode = match_color_mode (dev, s->val[OPT_MODE].s); /* source mode */ s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE; s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE; s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE; s->opt[OPT_SOURCE].type = SANE_TYPE_STRING; s->opt[OPT_SOURCE].size = (SANE_Int) max_string_size(dev->source_list); s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_SOURCE].constraint.string_list = &dev->source_list[0]; s->val[OPT_SOURCE].s = strdup(dev->source_list[0]); s->source_mode = match_source_mode (dev, s->val[OPT_SOURCE].s); s->source_mode_dim = match_source_mode_dim (s->source_mode); dev->x_range.max = SANE_FIX ( (int)dev->inquiry_x_ranges[s->source_mode_dim]); dev->x_range.quant = 0; dev->y_range.max = SANE_FIX ( (int)dev->inquiry_y_ranges[s->source_mode_dim]); dev->y_range.quant = 0; /* resolution */ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_RESOLUTION].constraint.range = &dev->dpi_range; s->val[OPT_RESOLUTION].w = OPT_RESOLUTION_DEFAULT; /* preview */ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; s->val[OPT_PREVIEW].w = 0; /* speed option */ s->opt[OPT_SPEED].name = SANE_NAME_SCAN_SPEED; s->opt[OPT_SPEED].title = SANE_TITLE_SCAN_SPEED; s->opt[OPT_SPEED].desc = SANE_DESC_SCAN_SPEED; s->opt[OPT_SPEED].type = SANE_TYPE_INT; s->opt[OPT_SPEED].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_SPEED].constraint.range = &dev->speed_range; s->val[OPT_SPEED].w = 0; if (dev->scanner_type == AV_SHEETFEED) s->opt[OPT_SPEED].cap |= SANE_CAP_INACTIVE; /* "Geometry" group: */ s->opt[OPT_GEOMETRY_GROUP].title = "Geometry"; s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_GEOMETRY_GROUP].size = 0; s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* top-left x */ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; s->opt[OPT_TL_X].unit = SANE_UNIT_MM; s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_X].constraint.range = &dev->x_range; s->val[OPT_TL_X].w = 0; /* top-left y */ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_Y].constraint.range = &dev->y_range; s->val[OPT_TL_Y].w = 0; /* bottom-right x */ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; s->opt[OPT_BR_X].unit = SANE_UNIT_MM; s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_X].constraint.range = &dev->x_range; s->val[OPT_BR_X].w = dev->x_range.max; /* bottom-right y */ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_Y].constraint.range = &dev->y_range; s->val[OPT_BR_Y].w = dev->y_range.max; /* overscan top */ s->opt[OPT_OVERSCAN_TOP].name = "overscan-top"; s->opt[OPT_OVERSCAN_TOP].title = SANE_TITLE_OVERSCAN_TOP; s->opt[OPT_OVERSCAN_TOP].desc = SANE_DESC_OVERSCAN_TOP; s->opt[OPT_OVERSCAN_TOP].type = SANE_TYPE_FIXED; s->opt[OPT_OVERSCAN_TOP].unit = SANE_UNIT_MM; s->opt[OPT_OVERSCAN_TOP].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_OVERSCAN_TOP].constraint.range = &overscan_range; s->val[OPT_OVERSCAN_TOP].w = SANE_FIX(0); /* overscan bottom */ s->opt[OPT_OVERSCAN_BOTTOM].name = "overscan-bottom"; s->opt[OPT_OVERSCAN_BOTTOM].title = SANE_TITLE_OVERSCAN_BOTTOM; s->opt[OPT_OVERSCAN_BOTTOM].desc = SANE_DESC_OVERSCAN_BOTTOM; s->opt[OPT_OVERSCAN_BOTTOM].type = SANE_TYPE_FIXED; s->opt[OPT_OVERSCAN_BOTTOM].unit = SANE_UNIT_MM; s->opt[OPT_OVERSCAN_BOTTOM].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_OVERSCAN_BOTTOM].constraint.range = &overscan_range; s->val[OPT_OVERSCAN_BOTTOM].w = SANE_FIX(0); if (!dev->inquiry_tune_scan_length) s->opt[OPT_OVERSCAN_TOP].cap |= SANE_CAP_INACTIVE; if (!dev->inquiry_tune_scan_length) s->opt[OPT_OVERSCAN_BOTTOM].cap |= SANE_CAP_INACTIVE; /* background raster */ s->opt[OPT_BACKGROUND].name = "background-lines"; s->opt[OPT_BACKGROUND].title = SANE_TITLE_BACKGROUND_LINES; s->opt[OPT_BACKGROUND].desc = SANE_DESC_BACKGROUND_LINES; s->opt[OPT_BACKGROUND].type = SANE_TYPE_INT; s->opt[OPT_BACKGROUND].unit = SANE_UNIT_PIXEL; s->opt[OPT_BACKGROUND].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BACKGROUND].constraint.range = &background_range; s->val[OPT_BACKGROUND].w = 0; if (!dev->inquiry_background_raster) { s->opt[OPT_BACKGROUND].cap |= SANE_CAP_INACTIVE; } /* "Enhancement" group: */ s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_TITLE_ENHANCEMENT; s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_ENHANCEMENT_GROUP].cap = 0; s->opt[OPT_ENHANCEMENT_GROUP].size = 0; s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* brightness */ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_FIXED; if (disable_gamma_table) s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT; s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BRIGHTNESS].constraint.range = &percentage_range; s->val[OPT_BRIGHTNESS].w = SANE_FIX(0); /* contrast */ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST; s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST; s->opt[OPT_CONTRAST].type = SANE_TYPE_FIXED; if (disable_gamma_table) s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CONTRAST].unit = SANE_UNIT_PERCENT; s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CONTRAST].constraint.range = &percentage_range; s->val[OPT_CONTRAST].w = SANE_FIX(0); /* Quality Scan */ s->opt[OPT_QSCAN].name = "quality-scan"; s->opt[OPT_QSCAN].title = SANE_TITLE_QUALITY_SCAN; s->opt[OPT_QSCAN].desc = SANE_DESC_QUALITY_SCAN; s->opt[OPT_QSCAN].type = SANE_TYPE_BOOL; s->opt[OPT_QSCAN].unit = SANE_UNIT_NONE; s->val[OPT_QSCAN].w = SANE_TRUE; if (dev->hw->feature_type & AV_NO_QSCAN_MODE) s->opt[OPT_QSCAN].cap |= SANE_CAP_INACTIVE; /* Quality Calibration */ s->opt[OPT_QCALIB].name = SANE_NAME_QUALITY_CAL; s->opt[OPT_QCALIB].title = SANE_TITLE_QUALITY_CAL; s->opt[OPT_QCALIB].desc = SANE_DESC_QUALITY_CAL; s->opt[OPT_QCALIB].type = SANE_TYPE_BOOL; s->opt[OPT_QCALIB].unit = SANE_UNIT_NONE; s->val[OPT_QCALIB].w = SANE_TRUE; if (dev->hw->feature_type & AV_NO_QCALIB_MODE) s->opt[OPT_QCALIB].cap |= SANE_CAP_INACTIVE; /* gray scale gamma vector */ s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR; s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR; s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR; s->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR].wa = &s->gamma_table[0][0]; /* red gamma vector */ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[1][0]; /* green gamma vector */ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[2][0]; /* blue gamma vector */ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[3][0]; if (!disable_gamma_table) { if (color_mode_is_color (s->c_mode)) { s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; } else { s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; } } /* exposure */ s->opt[OPT_EXPOSURE].name = "exposure"; s->opt[OPT_EXPOSURE].title = SANE_TITLE_MANUAL_EXPOSURE; s->opt[OPT_EXPOSURE].desc = SANE_DESC_MANUAL_EXPOSURE; s->opt[OPT_EXPOSURE].type = SANE_TYPE_INT; s->opt[OPT_EXPOSURE].unit = SANE_UNIT_PERCENT; s->opt[OPT_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_EXPOSURE].constraint.range = &exposure_range; s->val[OPT_EXPOSURE].w = 100; if (!dev->inquiry_exposure_control) { s->opt[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE; } /* Multi sample */ s->opt[OPT_MULTISAMPLE].name = "multi-sample"; s->opt[OPT_MULTISAMPLE].title = SANE_TITLE_MULTI_SAMPLE; s->opt[OPT_MULTISAMPLE].desc = SANE_DESC_MULTI_SAMPLE; s->opt[OPT_MULTISAMPLE].type = SANE_TYPE_BOOL; s->opt[OPT_MULTISAMPLE].unit = SANE_UNIT_NONE; s->val[OPT_MULTISAMPLE].w = SANE_FALSE; /* TODO: No idea how to detect, assume exposure control devices are new enough to support this, for now. -ReneR */ if (!dev->inquiry_exposure_control) { s->opt[OPT_MULTISAMPLE].cap |= SANE_CAP_INACTIVE; } /* Infra-red */ s->opt[OPT_IR].name = SANE_NAME_INFRARED; s->opt[OPT_IR].title = SANE_TITLE_INFRARED; s->opt[OPT_IR].desc = SANE_DESC_INFRARED; s->opt[OPT_IR].type = SANE_TYPE_BOOL; s->opt[OPT_IR].unit = SANE_UNIT_NONE; s->val[OPT_IR].w = SANE_FALSE; /* TODO: No idea how to detect, assume exposure control devices are new enough to support this, for now. -ReneR */ if (!dev->inquiry_exposure_control) { s->opt[OPT_IR].cap |= SANE_CAP_INACTIVE; } /* "MISC" group: */ s->opt[OPT_MISC_GROUP].title = SANE_TITLE_MISC_GROUP; s->opt[OPT_MISC_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MISC_GROUP].cap = 0; s->opt[OPT_MISC_GROUP].size = 0; s->opt[OPT_MISC_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* film holder control */ s->opt[OPT_FRAME].name = SANE_NAME_FRAME; s->opt[OPT_FRAME].title = SANE_TITLE_FRAME; s->opt[OPT_FRAME].desc = SANE_DESC_FRAME; s->opt[OPT_FRAME].type = SANE_TYPE_INT; s->opt[OPT_FRAME].unit = SANE_UNIT_NONE; 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; if (dev->scanner_type != AV_FILM) s->opt[OPT_FRAME].cap |= SANE_CAP_INACTIVE; /* power save time */ s->opt[OPT_POWER_SAVE_TIME].name = "power-save-time"; s->opt[OPT_POWER_SAVE_TIME].title = SANE_TITLE_POWER_SAVE_TIME; s->opt[OPT_POWER_SAVE_TIME].desc = SANE_DESC_POWER_SAVE_TIME; 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; if (!dev->inquiry_power_save_time) s->opt[OPT_POWER_SAVE_TIME].cap |= SANE_CAP_INACTIVE; /* message, like options set on the scanner, LED no. & co */ s->opt[OPT_MESSAGE].name = "message"; s->opt[OPT_MESSAGE].title = SANE_TITLE_OPTIONS_MSG; s->opt[OPT_MESSAGE].desc = SANE_DESC_OPTIONS_MSG; s->opt[OPT_MESSAGE].type = SANE_TYPE_STRING; s->opt[OPT_MESSAGE].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; s->opt[OPT_MESSAGE].size = 129; s->opt[OPT_MESSAGE].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_MESSAGE].s = malloc((size_t) s->opt[OPT_MESSAGE].size); s->val[OPT_MESSAGE].s[0] = 0; /* NVRAM */ s->opt[OPT_NVRAM].name = "nvram-values"; s->opt[OPT_NVRAM].title = SANE_TITLE_NVRAM; s->opt[OPT_NVRAM].desc = SANE_DESC_NVRAM; 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((size_t) s->opt[OPT_NVRAM].size); s->val[OPT_NVRAM].s[0] = 0; s->opt[OPT_NVRAM].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; if (!dev->inquiry_nvram_read) s->opt[OPT_NVRAM].cap |= SANE_CAP_INACTIVE; /* paper_length */ s->opt[OPT_PAPERLEN].name = "paper-length"; s->opt[OPT_PAPERLEN].title = SANE_TITLE_PAPER_LENGTH; s->opt[OPT_PAPERLEN].desc = SANE_DESC_PAPER_LENGTH; s->opt[OPT_PAPERLEN].type = SANE_TYPE_BOOL; s->opt[OPT_PAPERLEN].unit = SANE_UNIT_NONE; s->opt[OPT_PAPERLEN].size = sizeof(SANE_Word); s->opt[OPT_PAPERLEN].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_PAPERLEN].w = SANE_FALSE; s->opt[OPT_PAPERLEN].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; if (!dev->inquiry_paper_length) s->opt[OPT_PAPERLEN].cap |= SANE_CAP_INACTIVE; /* ADF page flipping */ s->opt[OPT_ADF_FLIP].name = "flip-page"; s->opt[OPT_ADF_FLIP].title = SANE_TITLE_FLIP_PAGE; s->opt[OPT_ADF_FLIP].desc = SANE_DESC_FLIP_PAGE; s->opt[OPT_ADF_FLIP].type = SANE_TYPE_BOOL; s->opt[OPT_ADF_FLIP].unit = SANE_UNIT_NONE; s->opt[OPT_ADF_FLIP].size = sizeof(SANE_Word); s->opt[OPT_ADF_FLIP].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_ADF_FLIP].w = SANE_TRUE; s->opt[OPT_ADF_FLIP].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC | SANE_CAP_ADVANCED; if (!((s->hw->hw->feature_type & AV_ADF_FLIPPING_DUPLEX) && (s->source_mode == AV_ADF_DUPLEX))) s->opt[OPT_ADF_FLIP].cap |= SANE_CAP_INACTIVE; /* "Options" group: */ s->opt[OPT_OPTIONS_GROUP].title = SANE_TITLE_INSTALLED_OPTS_GROUP; s->opt[OPT_OPTIONS_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_OPTIONS_GROUP].cap = 0; s->opt[OPT_OPTIONS_GROUP].size = 0; s->opt[OPT_OPTIONS_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* ADF Installed */ s->opt[OPT_OPTION_ADF].name = "adf-installed"; s->opt[OPT_OPTION_ADF].title = SANE_TITLE_ADF_INSTALLED; s->opt[OPT_OPTION_ADF].desc = SANE_DESC_ADF_INSTALLED; s->opt[OPT_OPTION_ADF].type = SANE_TYPE_BOOL; s->opt[OPT_OPTION_ADF].unit = SANE_UNIT_NONE; s->opt[OPT_OPTION_ADF].size = sizeof(SANE_Word); s->opt[OPT_OPTION_ADF].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_OPTION_ADF].w = dev->inquiry_adf_present; s->opt[OPT_OPTION_ADF].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; /* Lightbox Installed */ s->opt[OPT_OPTION_LIGHTBOX].name = "lightbox-installed"; s->opt[OPT_OPTION_LIGHTBOX].title = SANE_TITLE_LIGHTBOX_INSTALLED; s->opt[OPT_OPTION_LIGHTBOX].desc = SANE_DESC_LIGHTBOX_INSTALLED; s->opt[OPT_OPTION_LIGHTBOX].type = SANE_TYPE_BOOL; s->opt[OPT_OPTION_LIGHTBOX].unit = SANE_UNIT_NONE; s->opt[OPT_OPTION_LIGHTBOX].size = sizeof(SANE_Word); s->opt[OPT_OPTION_LIGHTBOX].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_OPTION_LIGHTBOX].w = dev->inquiry_light_box_present; s->opt[OPT_OPTION_LIGHTBOX].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; return SANE_STATUS_GOOD; } /* This function is executed as a child process. The reason this is executed as a subprocess is because some (most?) generic SCSI interfaces block a SCSI request until it has completed. With a subprocess, we can let it block waiting for the request to finish while the main process can go about to do more important things (such as recognizing when the user presses a cancel button). WARNING: Since this is executed as a subprocess, it's NOT possible to update any of the variables in the main process (in particular the scanner state cannot be updated). */ static int reader_process (void *data) { struct Avision_Scanner *s = (struct Avision_Scanner *) data; int fd = s->write_fds; Avision_Device* dev = s->hw; SANE_Status status; SANE_Status exit_status = SANE_STATUS_GOOD; sigset_t sigterm_set; sigset_t ignore_set; struct SIGACTION act; FILE* fp; FILE* fp_fd = 0; /* for ADF bottom offset truncating */ FILE* rear_fp = 0; /* used to store the deinterlaced rear data */ FILE* raw_fp = 0; /* used to write the RAW image data for debugging */ /* the complex params */ 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 */ int hw_line = 0; int line = 0; unsigned int stripe_size; unsigned int stripe_fill; unsigned int out_size; size_t total_size; size_t processed_bytes; enum { NONE, /* do not de-interlace at all */ STRIPE, /* every 2nd stripe */ HALF, /* the 2nd half */ LINE /* every 2nd line */ } deinterlace = NONE; /* the fat strip we currently puzzle together to perform software-colorpack and more */ uint8_t* stripe_data; /* the corrected output data */ uint8_t* out_data; /* interpolation output data, one line */ uint8_t* ip_history = 0; uint8_t* ip_data = 0; DBG (3, "reader_process:\n"); if (sanei_thread_is_forked()) { close (s->read_fds); s->read_fds = -1; sigfillset (&ignore_set); sigdelset (&ignore_set, SIGTERM); #if defined (__APPLE__) && defined (__MACH__) sigdelset (&ignore_set, SIGUSR2); #endif sigprocmask (SIG_SETMASK, &ignore_set, 0); memset (&act, 0, sizeof (act)); sigaction (SIGTERM, &act, 0); sigemptyset (&sigterm_set); sigaddset (&sigterm_set, SIGTERM); } #ifdef USE_PTHREAD else { int old; pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, &old); pthread_setcanceltype (PTHREAD_CANCEL_DEFERRED, &old); } #endif gray_mode = color_mode_is_shaded (s->c_mode); if (s->avdimen.interlaced_duplex) { deinterlace = STRIPE; if ( (dev->hw->feature_type & AV_NON_INTERLACED_DUPLEX_300) && (s->avdimen.hw_xres <= 300 && s->avdimen.hw_yres <= 300) ) deinterlace = HALF; if (dev->hw->feature_type & AV_2ND_LINE_INTERLACED) deinterlace = LINE; if (dev->scanner_type == AV_FILM) deinterlace = LINE; } fp = fdopen (fd, "w"); if (!fp) return SANE_STATUS_NO_MEM; if (dev->adf_offset_compensation) { char duplex_offtmp_fname [] = "/tmp/avision-offtmp-XXXXXX"; int fd = mkstemp(duplex_offtmp_fname); if (fd == -1) { DBG (1, "reader_process: failed to generate temporary fname for ADF offset compensation temp file\n"); return SANE_STATUS_NO_MEM; } DBG (1, "reader_process: temporary fname for ADF offset compensation temp file: %s\n", duplex_offtmp_fname); if (unlink(duplex_offtmp_fname) == -1) { DBG(1, "reader_process: failed to delete temporary file prior to use: %s\n", duplex_offtmp_fname); // continue though. } DBG (3, "reader_process: redirecting output data to temp file for ADF offset compensation.\n"); fp_fd = fp; fp = fdopen (fd, "w+"); if (!fp) { fclose(fp_fd); close(fd); return SANE_STATUS_NO_MEM; } } /* start scan ? */ if ((deinterlace == NONE && !((dev->hw->feature_type & AV_ADF_FLIPPING_DUPLEX) && s->source_mode == AV_ADF_DUPLEX && s->duplex_rear_valid)) || (deinterlace != NONE && !s->duplex_rear_valid)) { /* reserve unit - in the past we did this in open - but the windows driver does reserves for each scan and some ADF devices need a release for each sheet anyway ... */ status = reserve_unit (s); if (status != SANE_STATUS_GOOD) { DBG (1, "reader_process: reserve_unit failed: %s\n", sane_strstatus (status)); return status; } if (dev->hw->feature_type & AV_NO_START_SCAN) { DBG (1, "reader_process: start_scan skipped due to device-list!\n"); } else { status = start_scan (s); if (status != SANE_STATUS_GOOD) { DBG (1, "reader_process: start_scan failed: %s\n", sane_strstatus (status)); return status; } } if (dev->hw->feature_type & AV_ACCEL_TABLE) /* (s->hw->inquiry_asic_type == AV_ASIC_C6) */ { status = send_acceleration_table (s); if (status != SANE_STATUS_GOOD) { DBG (1, "reader_process: send_acceleration_table failed: %s\n", sane_strstatus (status)); return status; } } } /* setup file i/o for deinterlacing scans or if we are the back page with a flipping duplexer */ if (deinterlace != NONE || (dev->hw->feature_type & AV_ADF_FLIPPING_DUPLEX && s->source_mode == AV_ADF_DUPLEX && !(s->page % 2))) { 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"); } else { /* open saved rear data */ DBG (3, "reader_process: opening duplex rear file for reading.\n"); rear_fp = fopen (s->duplex_rear_fname, "r"); } if (! rear_fp) { fclose (fp); fclose (fp_fd); return SANE_STATUS_IO_ERROR; } } /* it takes quite a few lines to saturate the (USB) bus */ lines_per_stripe = (unsigned int) dev->read_stripe_size; if (s->avdimen.line_difference) lines_per_stripe += (2 * (unsigned int) s->avdimen.line_difference); stripe_size = (unsigned int) s->avdimen.hw_bytes_per_line * lines_per_stripe; lines_per_output = lines_per_stripe - 2 * (unsigned int) s->avdimen.line_difference; if (s->av_con.connection_type == AV_SCSI) /* maybe better not /2 ... */ max_bytes_per_read = (unsigned int) dev->scsi_buffer_size / 2; else /* vast buffer size to saturate the bus */ max_bytes_per_read = 0x100000; out_size = (unsigned int) s->avdimen.hw_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: %d, pixels_per_line: %d\n", s->avdimen.hw_bytes_per_line, s->avdimen.hw_pixels_per_line); DBG (3, "lines_per_stripe: %d, lines_per_output: %d\n", lines_per_stripe, lines_per_output); 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); /* for software scaling we need an additional interpolation line buffer */ if (s->avdimen.hw_xres != s->avdimen.xres || s->avdimen.hw_yres != s->avdimen.yres) { /* layout out_data so that the interpolation history is exactly in front */ ip_history = malloc ((size_t) s->avdimen.hw_bytes_per_line + out_size); out_data = ip_history + s->avdimen.hw_bytes_per_line; ip_data = malloc ((size_t) s->params.bytes_per_line); } else { out_data = malloc (out_size); } /* calculate params for the reading loop */ total_size = (size_t) s->avdimen.hw_bytes_per_line * ((size_t) s->avdimen.hw_lines + 2 * (size_t) s->avdimen.line_difference); if (deinterlace != NONE && !s->duplex_rear_valid) total_size *= 2; DBG (3, "reader_process: total_size: %lu\n", (u_long) total_size); /* write a RAW PNM file for debugging -ReneR */ if (0 /* DEBUG */ && (deinterlace == NONE || (deinterlace != NONE && !s->duplex_rear_valid)) ) { raw_fp = fopen ("/tmp/sane-avision.raw", "w"); write_pnm_header (fp, s->c_mode, s->params.depth, s->avdimen.hw_pixels_per_line, (int) (total_size / (size_t) s->avdimen.hw_bytes_per_line)); } processed_bytes = 0; stripe_fill = 0; /* First, dump background raster, bypassing all the other processing. */ if (dev->inquiry_background_raster && s->val[OPT_BACKGROUND].w) { uint8_t* background = s->background_raster; if (s->duplex_rear_valid) background += s->params.bytes_per_line * s->val[OPT_BACKGROUND].w; DBG (5, "reader_process: dumping background raster\n"); fwrite (background, (size_t) s->params.bytes_per_line, (size_t) s->val[OPT_BACKGROUND].w, fp); } /* Data read; loop until all data has been processed. Might exit before all lines are transferred for ADF paper end. */ while (exit_status == SANE_STATUS_GOOD && processed_bytes < total_size) { unsigned int useful_bytes; DBG (5, "reader_process: stripe filled: %d\n", stripe_fill); /* fill the stripe buffer with real data */ while (!s->duplex_rear_valid && processed_bytes < total_size && stripe_fill < stripe_size && exit_status == SANE_STATUS_GOOD) { size_t this_read = stripe_size - stripe_fill; /* Limit reads to max_bytes_per_read and global data boundaries. Rounded to the next lower multiple of byte_per_lines, otherwise some scanners freeze. */ if (this_read > max_bytes_per_read) this_read = (max_bytes_per_read - max_bytes_per_read % (unsigned int) s->avdimen.hw_bytes_per_line); if (processed_bytes + this_read > total_size) this_read = total_size - processed_bytes; read_constrains(s, this_read); 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); if (sanei_thread_is_forked()) sigprocmask (SIG_BLOCK, &sigterm_set, 0); #ifdef USE_PTHREAD else { int old; pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &old); } #endif status = read_data (s, stripe_data + stripe_fill, &this_read); if (sanei_thread_is_forked()) sigprocmask (SIG_UNBLOCK, &sigterm_set, 0); #ifdef USE_PTHREAD else { int old; pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, &old); } #endif /* only EOF on the second stripe, as otherwise the rear page is shorter */ if (status == SANE_STATUS_EOF && deinterlace == STRIPE) { if (dev->inquiry_asic_type > AV_ASIC_C7 && dev->inquiry_asic_type < AV_ASIC_OA980) { this_read = 0; } else { static int already_eof = 0; if (!already_eof) { DBG (5, "reader_process: first EOF on stripe interlace: hiding.\n"); status = SANE_STATUS_GOOD; already_eof = 1; } } } /* write RAW data to file for debugging */ if (raw_fp && this_read > 0) fwrite (stripe_data + stripe_fill, this_read, 1, raw_fp); if (status == SANE_STATUS_EOF || this_read == 0) { DBG (1, "reader_process: read_data failed due to EOF\n"); exit_status = SANE_STATUS_EOF; } if (status != SANE_STATUS_GOOD) { DBG (1, "reader_process: read_data failed with status: %d\n", status); exit_status = status; } stripe_fill += (unsigned int) this_read; processed_bytes += this_read; } /* fill the stripe buffer with stored, virtual data */ if (s->duplex_rear_valid) { size_t this_read = stripe_size - stripe_fill; size_t got; /* limit reads to max_read and global data boundaries */ if (this_read > max_bytes_per_read) this_read = max_bytes_per_read; if (processed_bytes + this_read > total_size) this_read = total_size - processed_bytes; 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); got = fread (stripe_data + stripe_fill, 1, this_read, rear_fp); stripe_fill += (unsigned int) got; processed_bytes += got; if (got != this_read) exit_status = SANE_STATUS_EOF; } DBG (5, "reader_process: stripe filled: %d\n", stripe_fill); useful_bytes = stripe_fill; if (color_mode_is_color (s->c_mode)) useful_bytes -= (unsigned int) (2 * s->avdimen.line_difference * s->avdimen.hw_bytes_per_line); DBG (3, "reader_process: useful_bytes %i\n", useful_bytes); /* Deinterlace, save the rear stripes. For some scanners (AV220) that is every 2nd stripe, the 2nd half of the transferred data ((AV83xx), or every 2nd line (AV122)). */ if (deinterlace != NONE && !s->duplex_rear_valid) { /* for all lines we have in the buffer: */ unsigned int absline = (unsigned int) ((processed_bytes - stripe_fill) / (size_t) s->avdimen.hw_bytes_per_line); unsigned int abslines = absline + useful_bytes / (unsigned int) s->avdimen.hw_bytes_per_line; uint8_t* ptr = stripe_data; for ( ; absline < abslines; ++absline) { DBG (9, "reader_process: deinterlacing line %d\n", absline); /* interlaced? save the back data to the rear buffer */ if ( (deinterlace == STRIPE && absline % (lines_per_stripe*2) >= lines_per_stripe) || (deinterlace == HALF && absline >= total_size / (size_t) s->avdimen.hw_bytes_per_line / 2) || (deinterlace == LINE && (absline & 0x1)) ) /* last bit equals % 2 */ { DBG (9, "reader_process: saving rear line %d to temporary file.\n", absline); fwrite (ptr, (size_t) s->avdimen.hw_bytes_per_line, 1, rear_fp); if (deinterlace == LINE) memmove (ptr, ptr+s->avdimen.hw_bytes_per_line, (size_t) (stripe_data + stripe_fill - ptr - s->avdimen.hw_bytes_per_line)); else ptr += s->avdimen.hw_bytes_per_line; useful_bytes -= (unsigned int) s->avdimen.hw_bytes_per_line; stripe_fill -= (unsigned int) s->avdimen.hw_bytes_per_line; } else ptr += s->avdimen.hw_bytes_per_line; } DBG (9, "reader_process: after deinterlacing: useful_bytes: %d, stripe_fill: %d\n", useful_bytes, stripe_fill); } if ((dev->hw->feature_type & AV_ADF_FLIPPING_DUPLEX) && s->source_mode == AV_ADF_DUPLEX && !(s->page % 2) && !s->duplex_rear_valid) { /* Here we flip the image by writing the lines from the end of the file to the beginning. */ unsigned int absline = (unsigned int) ((processed_bytes - stripe_fill) / (size_t) s->avdimen.hw_bytes_per_line); unsigned int abslines = absline + useful_bytes / (unsigned int) s->avdimen.hw_bytes_per_line; uint8_t* ptr = stripe_data; for ( ; absline < abslines; ++absline) { fseek (rear_fp, ((0 - s->params.lines) - (SANE_Int) absline - 2) * s->avdimen.hw_bytes_per_line, SEEK_SET); fwrite (ptr, (size_t) s->avdimen.hw_bytes_per_line, 1, rear_fp); useful_bytes -= (unsigned int) s->avdimen.hw_bytes_per_line; stripe_fill -= (unsigned int) s->avdimen.hw_bytes_per_line; ptr += s->avdimen.hw_bytes_per_line; } DBG (9, "reader_process: after page flip: useful_bytes: %d, stripe_fill: %d\n", useful_bytes, stripe_fill); } else { /* * Perform needed data conversions (packing, ...) and/or copy the * image data. */ if (s->c_mode != AV_TRUECOLOR && s->c_mode != AV_TRUECOLOR16) /* simple copy */ { memcpy (out_data, stripe_data, useful_bytes); } else /* AV_TRUECOLOR* */ { /* 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? */ unsigned int i; int c_offset = s->avdimen.line_difference * s->avdimen.hw_bytes_per_line; uint8_t* r_ptr = stripe_data; uint8_t* g_ptr = stripe_data + c_offset + 1; uint8_t* b_ptr = stripe_data + 2 * c_offset + 2; for (i = 0; i < useful_bytes;) { out_data [i++] = *r_ptr; r_ptr += 3; out_data [i++] = *g_ptr; g_ptr += 3; out_data [i++] = *b_ptr; b_ptr += 3; } } /* end color pack */ else if (dev->inquiry_needs_line_pack) /* line-pack */ { /* TODO: add 16bit per sample code? */ unsigned int i = 0, l, p; const unsigned int lines = useful_bytes / (unsigned int) s->avdimen.hw_bytes_per_line; for (l = 0; l < lines; ++l) { uint8_t* r_ptr = stripe_data + ((unsigned int) s->avdimen.hw_bytes_per_line * l); uint8_t* g_ptr = r_ptr + s->avdimen.hw_pixels_per_line; uint8_t* b_ptr = g_ptr + s->avdimen.hw_pixels_per_line; for (p = 0; p < (unsigned int) s->avdimen.hw_pixels_per_line; ++p) { out_data [i++] = *(r_ptr++); out_data [i++] = *(g_ptr++); out_data [i++] = *(b_ptr++); } } } /* end line pack */ else /* else no packing was required -> simple copy */ { memcpy (out_data, stripe_data, useful_bytes); } } /* end if AV_TRUECOLOR* */ /* FURTHER POST-PROCESSING ON THE FINAL OUTPUT DATA */ /* maybe mirroring in ADF mode */ if (s->source_mode_dim == AV_ADF_DIM && dev->inquiry_adf_need_mirror) { if ( (s->c_mode != AV_TRUECOLOR) || (s->c_mode == AV_TRUECOLOR && dev->inquiry_adf_bgr_order) ) { /* Mirroring with bgr -> rgb conversion: Just mirror the * whole line */ unsigned int l; unsigned int lines = useful_bytes / (unsigned int) s->avdimen.hw_bytes_per_line; for (l = 0; l < lines; ++l) { uint8_t* begin_ptr = out_data + (l * (unsigned int) s->avdimen.hw_bytes_per_line); uint8_t* end_ptr = begin_ptr + s->avdimen.hw_bytes_per_line; while (begin_ptr < end_ptr) { uint8_t tmp; tmp = *begin_ptr; *begin_ptr++ = *end_ptr; *end_ptr-- = tmp; } } } else /* non trivial mirroring */ { /* Non-trivial Mirroring with element swapping */ unsigned int l; unsigned int lines = useful_bytes / (unsigned int) s->avdimen.hw_bytes_per_line; for (l = 0; l < lines; ++l) { uint8_t* begin_ptr = out_data + (l * (unsigned int) s->avdimen.hw_bytes_per_line); uint8_t* end_ptr = begin_ptr + s->avdimen.hw_bytes_per_line - 3; while (begin_ptr < end_ptr) { uint8_t tmp; /* R */ tmp = *begin_ptr; *begin_ptr++ = *end_ptr; *end_ptr++ = tmp; /* G */ tmp = *begin_ptr; *begin_ptr++ = *end_ptr; *end_ptr++ = tmp; /* B */ tmp = *begin_ptr; *begin_ptr++ = *end_ptr; *end_ptr = tmp; end_ptr -= 5; } } } } /* end if mirroring needed */ /* byte swapping and software calibration 16bit mode */ if (s->c_mode == AV_GRAYSCALE12 || s->c_mode == AV_GRAYSCALE16 || s->c_mode == AV_TRUECOLOR12 || s->c_mode == AV_TRUECOLOR16) { unsigned int l; unsigned int lines = useful_bytes / (unsigned int) s->avdimen.hw_bytes_per_line; uint8_t* dark_avg_data = s->dark_avg_data; uint8_t* white_avg_data = s->white_avg_data; uint8_t* begin_ptr = out_data; uint8_t* end_ptr = begin_ptr + s->avdimen.hw_bytes_per_line; uint8_t* line_ptr; double scale = 1.0; if (s->c_mode == AV_GRAYSCALE12 || s->c_mode == AV_TRUECOLOR12) scale = (double) (1<<4); while (begin_ptr < end_ptr) { uint16_t dark_avg = 0; uint16_t white_avg = WHITE_MAP_RANGE; if (dark_avg_data) dark_avg = (uint16_t) get_double_le (dark_avg_data); if (white_avg_data) white_avg = (uint16_t) get_double_le (white_avg_data); line_ptr = begin_ptr; for (l = 0; l < lines; ++ l) { double v = (double) get_double_le (line_ptr) * scale; uint16_t v2; if (0) v = (v - dark_avg) * white_avg / WHITE_MAP_RANGE; v2 = ((uint16_t) v) < 0xFFFF ? ((uint16_t) v) : 0xFFFF; /* SANE Standard 3.2.1 "... bytes of each sample value are transmitted in the machine's native byte order." */ *line_ptr = (uint8_t) v2; line_ptr += s->avdimen.hw_bytes_per_line; } begin_ptr += 2; if (dark_avg_data) dark_avg_data += 2; if (white_avg_data) white_avg_data += 2; } } /* SOFTWARE SCALING WITH INTERPOLATION (IF NECESSARY) */ if (s->avdimen.hw_xres != s->avdimen.xres || s->avdimen.hw_yres != s->avdimen.yres) /* Software scaling */ { int x; /* for convenience in the 16bit code path */ uint16_t* out_data16 = (uint16_t*) out_data; const int hw_line_end = hw_line + (int) useful_bytes / s->avdimen.hw_bytes_per_line; /* on-the-fly bi-linear interpolation */ while (1) { double by = (-1.0 + s->avdimen.hw_lines) * line / (s->avdimen.hw_lines * s->avdimen.xres / s->avdimen.hw_xres + s->val[OPT_BACKGROUND].w); int sy = (int)floor(by); int ydist = (int) ((by - sy) * 256); int syy = sy + 1; const int hwbpl = s->avdimen.hw_bytes_per_line; uint8_t* dst = ip_data; uint16_t* dst16 = (uint16_t*) ip_data; unsigned int v; /* accumulator */ /* Break out if we do not have the hw source line - yet, or when we are past the end of wanted data */ if (deinterlace != NONE && !s->duplex_rear_valid && syy >= s->avdimen.hw_lines) { DBG (7, "reader_process: skip due past intended front page lines: %d\n", sy); break; } if (sy >= hw_line_end || syy >= hw_line_end) { DBG (3, "reader_process: source line %d-%d not yet avail\n", sy, syy); break; } DBG (8, "reader_process: out line: %d <- from: %d-%d\n", line, sy, syy); /* convert to offset in current stripe */ sy -= hw_line; syy -= hw_line; if (sy < -1) { DBG (1, "reader_process: need more history: %d???\n", sy); sy = -1; } for (x = 0; x < s->params.pixels_per_line; ++x) { const double bx = (-1.0 + s->avdimen.hw_pixels_per_line) * x / s->params.pixels_per_line; const int sx = (int)floor(bx); const int xdist = (int) ((bx - sx) * 256); const int sxx = sx + 1; if (x == 0 || x == s->params.pixels_per_line - 1) DBG (8, "reader_process: x: %d <- from: %d-%d\n", x, sx, sxx); switch (s->c_mode) { case AV_THRESHOLDED: case AV_DITHERED: { /* Repeating this over and over again is not fast, but as a seldom used code-path we want it readable. x/8 is the byte, and x%8 the bit position. */ v = (unsigned int) ( ( ((out_data [sy*hwbpl + sx/8 ] >> (7-sx%8 )) & 1) * (256-xdist) * (256-ydist) + ((out_data [sy*hwbpl + sxx/8] >> (7-sxx%8)) & 1) * xdist * (256-ydist) + ((out_data [syy*hwbpl + sx/8 ] >> (7-sx%8 )) & 1) * (256-xdist) * ydist + ((out_data [syy*hwbpl + sxx/8] >> (7-sxx%8)) & 1) * xdist * ydist ) / (1 + 1 * 256)); /* Shift and or the result together and eventually jump to the next byte. */ *dst = (uint8_t) ((*dst << 1) | ((v>>7)&1)); if (x % 8 == 7) ++dst; } break; case AV_GRAYSCALE: { v = (unsigned int) ( ( out_data [sy*hwbpl + sx ] * (256-xdist) * (256-ydist) + out_data [sy*hwbpl + sxx] * xdist * (256-ydist) + out_data [syy*hwbpl + sx ] * (256-xdist) * ydist + out_data [syy*hwbpl + sxx] * xdist * ydist ) / (256 * 256)); *dst++ = (uint8_t) v; } break; case AV_GRAYSCALE12: case AV_GRAYSCALE16: { /* TODO: test! */ v = (unsigned int) ( ( out_data16 [sy*hwbpl + sx ] * (256-xdist) * (256-ydist) + out_data16 [sy*hwbpl + sxx] * xdist * (256-ydist) + out_data16 [syy*hwbpl + sx ] * (256-xdist) * ydist + out_data16 [syy*hwbpl + sxx] * xdist * ydist ) / (256 * 256)); *dst16++ = (uint16_t) v; } break; case AV_TRUECOLOR: { int c; for (c = 0; c < 3; ++c) { v = (unsigned int) ( ( out_data [sy*hwbpl + sx*3 + c] * (256-xdist) * (256-ydist) + out_data [sy*hwbpl + sxx*3 + c] * xdist * (256-ydist) + out_data [syy*hwbpl + sx*3 + c] * (256-xdist) * ydist + out_data [syy*hwbpl + sxx*3 + c] * xdist * ydist ) / (256 * 256)); *dst++ = (uint8_t) v; } } break; case AV_TRUECOLOR12: case AV_TRUECOLOR16: { /* TODO: test! */ int c; for (c = 0; c < 3; ++c) { v = (unsigned int) ( ( out_data16 [sy*hwbpl + sx*3 + c] * (256-xdist) * (256-ydist) + out_data16 [sy*hwbpl + sxx*3 + c] * xdist * (256-ydist) + out_data16 [syy*hwbpl + sx*3 + c] * (256-xdist) * ydist + out_data16 [syy*hwbpl + sxx*3 + c] * xdist * ydist ) / (256 * 256)); *dst16++ = (uint16_t) v; } } break; case AV_COLOR_MODE_LAST: ; /* silence compiler warning */ } } fwrite (ip_data, (size_t) s->params.bytes_per_line, 1, fp); ++line; } /* copy one line of history for the next pass */ memcpy (ip_history, out_data + useful_bytes - s->avdimen.hw_bytes_per_line, (size_t) s->avdimen.hw_bytes_per_line); } else /* No scaling */ { fwrite (out_data, useful_bytes, 1, fp); line += (int) useful_bytes / s->avdimen.hw_bytes_per_line; } } /* save image date in stripe buffer for next next stripe */ stripe_fill -= useful_bytes; if (stripe_fill > 0) memcpy (stripe_data, stripe_data + useful_bytes, stripe_fill); hw_line += (int) useful_bytes / s->avdimen.hw_bytes_per_line; DBG (3, "reader_process: end of iteration\n"); } /* end while not all lines or inf. mode */ DBG (3, "reader_process: i/o loop finished\n"); if (exit_status == SANE_STATUS_GOOD) exit_status = SANE_STATUS_EOF; if (raw_fp) fclose (raw_fp); /* maybe we need to fill in some white data */ if (exit_status == SANE_STATUS_EOF && line < s->params.lines) { DBG (3, "reader_process: padding with white data\n"); memset (out_data, gray_mode ? 0xff : 0x00, (size_t) s->params.bytes_per_line); DBG (6, "reader_process: padding line %d - %d\n", line, s->params.lines); while (line < s->params.lines) { fwrite (out_data, (size_t) s->params.bytes_per_line, 1, fp); ++line; } } /* ADF offset compensation */ if (dev->adf_offset_compensation) { long lines; uint8_t* buffer; buffer = malloc ((size_t) s->params.bytes_per_line); lines = ftell(fp) / s->params.bytes_per_line; rewind(fp); for (long line = 0; line < lines; line++) { fread(buffer, (size_t) s->params.bytes_per_line, 1, fp); if ( (!s->duplex_rear_valid && (line < s->avdimen.offset.front.top)) || (s->duplex_rear_valid && (line < s->avdimen.offset.rear.top)) ) { DBG (7, "reader_process: skip due read offset line: %ld\n", line); continue; } if ( (!s->duplex_rear_valid && (line > (lines - s->avdimen.offset.front.bottom))) || (s->duplex_rear_valid && (line > (lines - s->avdimen.offset.rear.bottom))) ) { DBG (7, "reader_process: skip due read offset line: %ld to %ld\n", line, lines); break; /* nothing more to write, so break out here */ } fwrite(buffer, (size_t) s->params.bytes_per_line, 1, fp_fd); } } /* Eject film holder and/or release_unit - but only for non-duplex-rear / non-virtual scans. */ if ((deinterlace != NONE && s->duplex_rear_valid) || ((dev->hw->feature_type & AV_ADF_FLIPPING_DUPLEX) && s->source_mode == AV_ADF_DUPLEX && !(s->page % 2) && s->duplex_rear_valid)) { DBG (1, "reader_process: virtual duplex scan - no device cleanup!\n"); } else { /* poll the cancel button if the scanner is marked as having one */ if (dev->hw->feature_type & AV_CANCEL_BUTTON) { if (get_button_status (s) == SANE_STATUS_CANCELLED) exit_status = SANE_STATUS_CANCELLED; } if (dev->inquiry_new_protocol && dev->scanner_type == AV_FILM) { status = object_position (s, AVISION_SCSI_OP_GO_HOME); if (status != SANE_STATUS_GOOD) DBG (1, "reader_process: object position go-home failed!\n"); } status = release_unit (s, 0); if (status != SANE_STATUS_GOOD) DBG (1, "reader_process: release_unit failed\n"); } if ((dev->hw->feature_type & AV_ADF_FLIPPING_DUPLEX) && s->source_mode == AV_ADF_DUPLEX && s->page % 2) { /* front page of flipping duplex */ if (exit_status == SANE_STATUS_EOF) { if (s->val[OPT_ADF_FLIP].w) { /* The page flip bit must be reset after every scan, but if the * user doesn't care, there's no reason to reset. */ status = set_window (s); if (status != SANE_STATUS_GOOD) { DBG (1, "reader_process: set scan window command failed: %s\n", sane_strstatus (status)); return status; } } /* We run in a separate process or thread. In the latter case, any change we make to s before the reader_process invocation needs to be reverted. */ SANE_Int lines = s->params.lines; s->page += 1; s->params.lines = -line; exit_status = reader_process (s); s->params.lines = lines; s->page -= 1; } /* TODO: * else { * spit out the page if an error was encountered... * assuming the error won't prevent it. * } */ } else { fclose (fp); } if (rear_fp) fclose (rear_fp); if (fp_fd) fclose(fp_fd); if (ip_data) free (ip_data); if (ip_history) free (ip_history); else free (out_data); /* if we have ip_history out_data is included there */ free (stripe_data); DBG (3, "reader_process: returning success\n"); return exit_status; } /* SANE callback to attach a SCSI device */ static SANE_Status attach_one_scsi (const char* dev) { attach (dev, AV_SCSI, 0); return SANE_STATUS_GOOD; } /* SANE callback to attach a USB device */ static SANE_Status attach_one_usb (const char* dev) { attach (dev, AV_USB, 0); return SANE_STATUS_GOOD; } static SANE_Status sane_reload_devices (void) { FILE* fp; char line[PATH_MAX]; const char* cp = 0; char* word; int linenumber = 0; int model_num = 0; sanei_usb_init (); fp = sanei_config_open (AVISION_CONFIG_FILE); if (fp <= (FILE*)0) { DBG (1, "sane_reload_devices: No config file present!\n"); } else { /* first parse the config file */ while (sanei_config_read (line, sizeof (line), fp)) { attaching_hw = 0; word = NULL; ++ linenumber; DBG (5, "sane_reload_devices: parsing config line \"%s\"\n", line); cp = sanei_config_get_string (line, &word); if (!word || cp == line) { DBG (5, "sane_reload_devices: config file line %d: ignoring empty line\n", linenumber); if (word) { free (word); word = NULL; } continue; } if (!word) { DBG (1, "sane_reload_devices: config file line %d: could not be parsed\n", linenumber); continue; } if (word[0] == '#') { DBG (5, "sane_reload_devices: config file line %d: ignoring comment line\n", linenumber); free (word); word = NULL; continue; } if (strcmp (word, "option") == 0) { free (word); word = NULL; cp = sanei_config_get_string (cp, &word); if (strcmp (word, "disable-gamma-table") == 0) { DBG (3, "sane_reload_devices: config file line %d: disable-gamma-table\n", linenumber); disable_gamma_table = SANE_TRUE; } else if (strcmp (word, "disable-calibration") == 0) { DBG (3, "sane_reload_devices: config file line %d: disable-calibration\n", linenumber); disable_calibration = SANE_TRUE; } else if (strcmp (word, "force-calibration") == 0) { DBG (3, "sane_reload_devices: config file line %d: force-calibration\n", linenumber); force_calibration = SANE_TRUE; } else if (strcmp (word, "force-a4") == 0) { DBG (3, "sane_reload_devices: config file line %d: enabling force-a4\n", linenumber); force_a4 = SANE_TRUE; } else if (strcmp (word, "force-a3") == 0) { DBG (3, "sane_reload_devices: config file line %d: enabling force-a3\n", linenumber); force_a3 = SANE_TRUE; } else if (strcmp (word, "skip-adf") == 0) { DBG (3, "sane_reload_devices: config file line %d: enabling skip-adf\n", linenumber); skip_adf = SANE_TRUE; } else if (strcmp (word, "static-red-calib") == 0) { DBG (3, "sane_reload_devices: config file line %d: static red calibration\n", linenumber); static_calib_list [0] = SANE_TRUE; } else if (strcmp (word, "static-green-calib") == 0) { DBG (3, "sane_reload_devices: config file line %d: static green calibration\n", linenumber); static_calib_list [1] = SANE_TRUE; } else if (strcmp (word, "static-blue-calib") == 0) { DBG (3, "sane_reload_devices: config file line %d: static blue calibration\n", linenumber); static_calib_list [2] = SANE_TRUE; } else DBG (1, "sane_reload_devices: config file line %d: options unknown!\n", linenumber); } else if (strcmp (word, "usb") == 0) { DBG (2, "sane_reload_devices: config file line %d: trying to attach USB:`%s'\n", linenumber, line); /* try to attach USB device */ sanei_usb_attach_matching_devices (line, attach_one_usb); } else if (strcmp (word, "scsi") == 0) { DBG (2, "sane_reload_devices: config file line %d: trying to attach SCSI: %s'\n", linenumber, line); /* the last time I verified (2003-03-18) this function only matches SCSI devices ... */ sanei_config_attach_matching_devices (line, attach_one_scsi); } else { DBG (1, "sane_reload_devices: config file line %d: OBSOLETE !! use the scsi keyword!\n", linenumber); DBG (1, "sane_reload_devices: (see man sane-avision for details): trying to attach SCSI: %s'\n", line); /* the last time I verified (2003-03-18) this function only matched SCSI devices ... */ sanei_config_attach_matching_devices (line, attach_one_scsi); } free (word); word = NULL; } /* end while read */ fclose (fp); if (word) free (word); } /* end if fp */ /* search for all supported SCSI/USB devices */ while (Avision_Device_List [model_num].scsi_mfg != NULL || Avision_Device_List [model_num].real_mfg != NULL) { /* also potentially accessed from the attach_* callbacks */ attaching_hw = &(Avision_Device_List [model_num]); if (attaching_hw->scsi_mfg != NULL) 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_reload_devices: Trying to find USB device %.4x %.4x ...\n", attaching_hw->usb_vendor, attaching_hw->usb_product); /* TODO: check return value */ if (sanei_usb_find_devices (attaching_hw->usb_vendor, attaching_hw->usb_product, attach_one_usb) != SANE_STATUS_GOOD) { DBG (1, "sane_reload_devices: error during USB device detection!\n"); } } ++ model_num; } /* end for all devices in supported list */ attaching_hw = 0; return SANE_STATUS_GOOD; } SANE_Status sane_init (SANE_Int* version_code, SANE_Auth_Callback authorize) { (void) authorize; /* silence gcc */ DBG_INIT(); #ifdef AVISION_STATIC_DEBUG_LEVEL DBG_LEVEL = AVISION_STATIC_DEBUG_LEVEL; #endif DBG (3, "sane_init:(Version: %i.%i Build: %i)\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BACKEND_BUILD); /* must come first */ sanei_thread_init (); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BACKEND_BUILD); sane_reload_devices (); return SANE_STATUS_GOOD; } void sane_exit (void) { Avision_Device* dev; Avision_Device* next; DBG (3, "sane_exit:\n"); for (dev = first_dev; dev; dev = next) { next = dev->next; /* no warning for stripping const - C lacks a const_cast<> */ free ((void*)(size_t) dev->sane.name); free (dev); } first_dev = NULL; free(devlist); devlist = NULL; } SANE_Status sane_get_devices (const SANE_Device*** device_list, SANE_Bool local_only) { Avision_Device* dev; unsigned int i; (void) local_only; /* silence gcc */ DBG (3, "sane_get_devices:\n"); sane_reload_devices (); if (devlist) free (devlist); devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); if (!devlist) return SANE_STATUS_NO_MEM; i = 0; for (dev = first_dev; i < num_devices; dev = dev->next) devlist[i++] = &dev->sane; devlist[i++] = 0; *device_list = devlist; return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle *handle) { Avision_Device* dev; SANE_Status status; Avision_Scanner* s; int i, j; uint8_t inquiry_result[AVISION_INQUIRY_SIZE_V1]; DBG (3, "sane_open:\n"); if (devicename[0]) { for (dev = first_dev; dev; dev = dev->next) if (strcmp (dev->sane.name, devicename) == 0) break; if (dev) { status = attach (devicename, dev->connection.connection_type, &dev); if (status != SANE_STATUS_GOOD) return status; } } else { /* empty devicename -> use first device */ dev = first_dev; } if (!dev) return SANE_STATUS_INVAL; s = malloc (sizeof (*s)); if (!s) return SANE_STATUS_NO_MEM; /* initialize ... */ /* the other states (scanning, ...) rely on this memset (0) */ memset (s, 0, sizeof (*s)); /* initialize connection state */ s->av_con.connection_type = dev->connection.connection_type; s->av_con.usb_status = dev->connection.usb_status; s->av_con.scsi_fd = -1; s->av_con.usb_dn = -1; sanei_thread_initialize (s->reader_pid); s->read_fds = -1; s->hw = dev; /* We initialize the table to a gamma value of 2.22, since this is what papers about Colorimetry suggest. http://www.poynton.com/GammaFAQ.html Avision's driver defaults to 2.2 though. MN: This is not true for at least Kodak i1120's windows driver. Some real-world testing showed that a gamma of 1.0 is needed for this scanner to give decent scan results. Add an option for this... */ { double gamma = 2.22; if (s->hw->hw->feature_type & AV_GAMMA_10) gamma = 1.0; const double one_over_gamma = 1. / gamma; for (i = 0; i < 4; ++ i) for (j = 0; j < 256; ++ j) s->gamma_table[i][j] = (SANE_Int) (pow( (double) j / 255, one_over_gamma) * 255); } /* insert newly opened handle into list of open handles: */ s->next = first_handle; first_handle = s; *handle = s; /* open the device */ if (! avision_is_open (&s->av_con) ) { #ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED DBG (1, "sane_open: using open_extended\n"); status = avision_open_extended (s->hw->sane.name, &s->av_con, sense_handler, 0, &(dev->scsi_buffer_size)); #else status = avision_open (s->hw->sane.name, &s->av_con, sense_handler, 0); #endif if (status != SANE_STATUS_GOOD) { DBG (1, "sane_open: open of %s failed: %s\n", s->hw->sane.name, sane_strstatus (status)); return status; } 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; } status = wait_ready (&s->av_con, 1); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_open: wait_ready() failed: %s\n", sane_strstatus (status)); return status; } /* update settings based on additional accessory information */ status = additional_probe (s); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_open: additional probe failed: %s\n", sane_strstatus (status)); return status; } /* initialize the options */ init_options (s); if (dev->inquiry_duplex_interlaced && (dev->hw->offset.first != 0 || dev->hw->offset.front.top != 0 || dev->hw->offset.front.bottom != 0 || dev->hw->offset.duplex.front.top != 0 || dev->hw->offset.duplex.front.bottom != 0 || dev->hw->offset.duplex.rear.top != 0 || dev->hw->offset.duplex.rear.bottom != 0) ) dev->adf_offset_compensation = SANE_TRUE; if (dev->inquiry_duplex_interlaced || dev->scanner_type == AV_FILM || dev->hw->feature_type & AV_ADF_FLIPPING_DUPLEX) { /* Might need at least *DOS (Windows flavour and OS/2) portability fix However, I was told Cygwin (et al.) takes care of it. */ /* Create the file but close the fd. It is not used to open the file later. :( */ strncpy(s->duplex_rear_fname, "/tmp/avision-rear-XXXXXX", PATH_MAX); int fd = mkstemp(s->duplex_rear_fname); if (fd == -1) { DBG (1, "sane_open: failed to generate temporary fname for duplex scans\n"); return SANE_STATUS_NO_MEM; } DBG (1, "sane_open: temporary fname for duplex scans: %s\n", s->duplex_rear_fname); close(fd); } /* calibrate film scanners, as this must be done without the film holder and at the full resolution */ if (dev->scanner_type == AV_FILM) { int default_res = s->val[OPT_RESOLUTION].w; s->val[OPT_RESOLUTION].w = dev->inquiry_optical_res; DBG (1, "sane_open: early calibration for film scanner.\n"); compute_parameters (s); status = set_window (s); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_open: set scan window command failed: %s\n", sane_strstatus (status)); return status; } if (!(dev->hw->feature_type & AV_NO_CALIB)) { status = normal_calibration (s); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_open: perform calibration failed: %s\n", sane_strstatus (status)); return status; } } if (dev->scanner_type == AV_FILM) { status = object_position (s, AVISION_SCSI_OP_GO_HOME); if (status != SANE_STATUS_GOOD) DBG (1, "reader_open: object position go-home failed!\n"); } s->val[OPT_RESOLUTION].w = default_res; } return SANE_STATUS_GOOD; } void sane_close (SANE_Handle handle) { Avision_Scanner* prev; Avision_Scanner* s; int i; DBG (3, "sane_close:\n"); for (prev = 0, s = first_handle; s; prev = s, s = s->next) { if (s == handle) break; } /* a handle we know about ? */ if (!s) { DBG (1, "sane_close: invalid handle %p\n", handle); return; } if (s->scanning) do_cancel (handle); /* close the device */ if (avision_is_open(&s->av_con)) { avision_close(&s->av_con); } /* remove handle from list of open handles */ if (prev) prev->next = s->next; else first_handle = s->next; for (i = 1; i < NUM_OPTIONS; ++ i) { if (s->opt[i].type == SANE_TYPE_STRING && s->val[i].s) { free (s->val[i].s); } } if (s->white_avg_data) free (s->white_avg_data); if (s->dark_avg_data) free (s->dark_avg_data); if (s->background_raster) free (s->background_raster); if (*(s->duplex_rear_fname)) { unlink (s->duplex_rear_fname); *(s->duplex_rear_fname) = 0; } free (handle); } const SANE_Option_Descriptor* sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { Avision_Scanner* s = handle; DBG (3, "sane_get_option_descriptor: %d\n", option); if ((unsigned) option >= NUM_OPTIONS) return 0; return s->opt + option; } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void* val, SANE_Int* info) { Avision_Scanner* s = handle; Avision_Device* dev = s->hw; SANE_Status status; SANE_Word cap; DBG (3, "sane_control_option: option=%d, action=%d\n", (int)option, (int)action); DBG (5, "sane_control_option: option=%s, action=%s\n", s->opt[option].name, action == SANE_ACTION_GET_VALUE ? "GET" : (action == SANE_ACTION_SET_VALUE ? "SET" : (action == SANE_ACTION_SET_AUTO ? "AUTO" : "UNKNOWN") ) ); if (info) *info = 0; if (s->scanning) return SANE_STATUS_DEVICE_BUSY; if (option >= NUM_OPTIONS) return SANE_STATUS_INVAL; cap = s->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) return SANE_STATUS_INVAL; if (action == SANE_ACTION_GET_VALUE) { switch (option) { /* word options: */ case OPT_PREVIEW: case OPT_RESOLUTION: case OPT_SPEED: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_OVERSCAN_TOP: case OPT_OVERSCAN_BOTTOM: case OPT_BACKGROUND: case OPT_NUM_OPTS: case OPT_BRIGHTNESS: case OPT_CONTRAST: case OPT_EXPOSURE: case OPT_IR: case OPT_MULTISAMPLE: case OPT_QSCAN: case OPT_QCALIB: case OPT_PAPERLEN: case OPT_ADF_FLIP: *(SANE_Word*) val = s->val[option].w; return SANE_STATUS_GOOD; /* specially treated word options */ case OPT_FRAME: status = get_frame_info (s); *(SANE_Word*) val = s->val[option].w; return status; 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; /* word-array options: */ case OPT_GAMMA_VECTOR: case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: memcpy (val, s->val[option].wa, (size_t) s->opt[option].size); return SANE_STATUS_GOOD; /* string options: */ case OPT_MODE: case OPT_SOURCE: strcpy (val, s->val[option].s); return SANE_STATUS_GOOD; /* specially treated string options */ case OPT_MESSAGE: if (dev->inquiry_button_control || dev->inquiry_buttons) status = get_button_status (s); strcpy (val, s->val[option].s); s->val[option].s[0] = 0; 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; /* Boolean options. */ case OPT_OPTION_ADF: case OPT_OPTION_LIGHTBOX: *(SANE_Bool*) val = s->val[option].b; return SANE_STATUS_GOOD; } /* end switch option */ } /* end if GET_ACTION_GET_VALUE */ else if (action == SANE_ACTION_SET_VALUE) { if (!SANE_OPTION_IS_SETTABLE (cap)) return SANE_STATUS_INVAL; status = constrain_value (s, option, val, info); if (status != SANE_STATUS_GOOD) return status; switch (option) { /* side-effect-free word options: */ case OPT_SPEED: case OPT_PREVIEW: case OPT_BRIGHTNESS: case OPT_CONTRAST: case OPT_EXPOSURE: case OPT_IR: case OPT_MULTISAMPLE: case OPT_QSCAN: case OPT_QCALIB: case OPT_OVERSCAN_TOP: case OPT_OVERSCAN_BOTTOM: case OPT_BACKGROUND: case OPT_PAPERLEN: case OPT_ADF_FLIP: s->val[option].w = *(SANE_Word*) val; return SANE_STATUS_GOOD; /* side-effect-free word-array options: */ case OPT_GAMMA_VECTOR: case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: memcpy (s->val[option].wa, val, (size_t) s->opt[option].size); return SANE_STATUS_GOOD; /* options with side-effects: */ case OPT_RESOLUTION: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: s->val[option].w = *(SANE_Word*) val; if (info) *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; /* string options with side-effects: */ case OPT_SOURCE: if (s->val[option].s) { free(s->val[option].s); } s->val[option].s = strdup(val); s->source_mode = match_source_mode (dev, s->val[option].s); s->source_mode_dim = match_source_mode_dim (s->source_mode); /* set side-effects */ dev->x_range.max = SANE_FIX ( dev->inquiry_x_ranges[s->source_mode_dim]); dev->y_range.max = SANE_FIX ( dev->inquiry_y_ranges[s->source_mode_dim]); if (s->hw->hw->feature_type & AV_ADF_FLIPPING_DUPLEX && s->source_mode == AV_ADF_DUPLEX) { s->opt[OPT_ADF_FLIP].cap &= ~SANE_CAP_INACTIVE; } else { s->opt[OPT_ADF_FLIP].cap |= SANE_CAP_INACTIVE; } if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case OPT_MODE: { if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); s->c_mode = match_color_mode (dev, s->val[OPT_MODE].s); /* set to mode specific values */ /* the gamma table related */ if (!disable_gamma_table) { if (color_mode_is_color (s->c_mode) ) { s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; } else /* gray or mono */ { 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; } } if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; } case OPT_FRAME: { SANE_Word frame = *((SANE_Word *) val); status = set_frame (s, frame); if (status == SANE_STATUS_GOOD) { s->val[OPT_FRAME].w = frame; dev->current_frame = frame; } 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 */ } else if (action == SANE_ACTION_SET_AUTO) { if (!SANE_OPTION_IS_SETTABLE (cap)) return SANE_STATUS_INVAL; switch (option) { case OPT_ADF_FLIP: s->val[option].w = SANE_TRUE; return SANE_STATUS_GOOD; } /* end switch option */ } /* end else SET_VALUE */ return SANE_STATUS_INVAL; } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters* params) { Avision_Scanner* s = handle; DBG (3, "sane_get_parameters:\n"); /* During an actual scan these parameters will have been computed in sane_start(). Otherwise, the values must be computed on demand. The values cannot be changed during a scan to avoid inconsistency. */ if (!s->scanning) { DBG (3, "sane_get_parameters: computing parameters\n"); compute_parameters (s); } if (params) { *params = s->params; /* add background raster lines */ params->lines += s->val[OPT_BACKGROUND].w; } return SANE_STATUS_GOOD; } SANE_Status sane_start (SANE_Handle handle) { Avision_Scanner* s = handle; Avision_Device* dev = s->hw; SANE_Status status; int fds [2]; DBG (1, "sane_start:\n"); /* Make sure there is no scan running!!! */ if (s->scanning) return SANE_STATUS_DEVICE_BUSY; /* Clear cancellation status */ s->cancelled = SANE_FALSE; /* Make sure we have a current parameter set. Some of the parameters will be overwritten below, but that's OK. */ status = sane_get_parameters (s, &s->params); if (status != SANE_STATUS_GOOD) { return status; } /* for non ADF scans (e.g. scanimage --batch-prompt on a Flatbed scanner) make sure we do not assume it's an ADF scan and optimize something away*/ if (!is_adf_scan (s)) s->page = 0; if (s->page > 0 && s->duplex_rear_valid) { DBG (1, "sane_start: virtual duplex rear data valid.\n"); goto start_scan_end; } /* Check for paper during ADF scans and for sheetfed scanners. */ if (is_adf_scan (s)) { status = media_check (s); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: media_check failed: %s\n", sane_strstatus (status)); return status; } else DBG (1, "sane_start: media_check ok\n"); } /* Check the light early, to return to the GUI and notify the user. */ if (s->prepared == SANE_FALSE) { if (dev->inquiry_light_control) { status = wait_4_light (s); if (status != SANE_STATUS_GOOD) { return status; } } } if (s->page > 0 && 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; } /* Re-check the light, as setting the window may have changed * which light is to be turned on. */ if (s->prepared == SANE_FALSE && dev->inquiry_light_control) { status = wait_4_light (s); if (status != SANE_STATUS_GOOD) { return status; } } } #ifdef DEBUG_TEST /* debug window size test ... */ if (dev->inquiry_new_protocol) { size_t size = 16; uint8_t result[16]; DBG (5, "sane_start: reading scanner window size\n"); status = simple_read (s, 0x80, 0, &size, result); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: get pixel size command failed: %s\n", sane_strstatus (status)); goto stop_scanner_and_return; } debug_print_raw (5, "sane_start: pixel_size:", result, size); DBG (5, "sane_start: x-pixels: %d, y-pixels: %d\n", get_quad (&(result[0])), get_quad (&(result[4]))); } #endif /* no calibration for ADF pages */ if (s->page > 0) { 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) { DBG (1, "sane_start: old protocol no calibration needed!\n"); goto calib_end; } if (!dev->inquiry_needs_calibration) { DBG (1, "sane_start: due to inquiry no calibration needed!\n"); goto calib_end; } /* calibration allowed for this scanner? */ if (dev->hw->feature_type & AV_NO_CALIB) { DBG (1, "sane_start: calibration disabled in device list!!\n"); goto calib_end; } /* Not for film scanners, ... */ if (dev->scanner_type == AV_FILM) { DBG (1, "sane_start: no calibration for film scanner!\n"); goto calib_end; } /* check whether calibration is disabled by the user */ if (disable_calibration) { DBG (1, "sane_start: calibration disabled in config - skipped!\n"); goto calib_end; } /* 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) { DBG (1, "sane_start: perform calibration failed: %s\n", sane_strstatus (status)); goto stop_scanner_and_return; } calib_end: if (dev->inquiry_3x3_matrix && dev->inquiry_asic_type >= AV_ASIC_C6 && s->page == 0) { status = send_3x3_matrix (s); if (status != SANE_STATUS_GOOD) { return status; } } /* check whether gamma-table is disabled by the user? */ if (disable_gamma_table) { DBG (1, "sane_start: gamma-table disabled in config - skipped!\n"); goto gamma_end; } if (dev->hw->feature_type & AV_NO_GAMMA) { DBG (1, "sane_start: gamma table skipped due to device-list!!\n"); goto gamma_end; } if (s->page > 0 && dev->inquiry_keeps_gamma) DBG (1, "sane_start: Optimized send_gamma away.\n"); else { status = send_gamma (s); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: send gamma failed: %s\n", sane_strstatus (status)); goto stop_scanner_and_return; } } gamma_end: if (dev->inquiry_tune_scan_length && is_adf_scan (s)) { status = send_tune_scan_length (s); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: tune_scan_length command failed: %s\n", sane_strstatus (status)); goto stop_scanner_and_return; } } /* if the device supports retrieving background raster data inquire the data no matter if the user/applications asks for it in order to use it for bottom padding */ if (s->page == 0 && dev->inquiry_background_raster) { status = get_background_raster (s); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: get background raster command failed: %s\n", sane_strstatus (status)); goto stop_scanner_and_return; } } /* check film holder */ if (dev->scanner_type == AV_FILM && dev->holder_type == 0xff) { DBG (1, "sane_start: no film holder or APS cassette!\n"); /* Normally "go_home" is executed from the reader process, but as it will not start we have to reset things here */ if (dev->inquiry_new_protocol) { status = object_position (s, AVISION_SCSI_OP_GO_HOME); if (status != SANE_STATUS_GOOD) DBG (1, "sane_start: go home failed: %s\n", sane_strstatus (status)); } goto stop_scanner_and_return; } start_scan_end: s->scanning = SANE_TRUE; s->page += 1; /* processing next page */ if (pipe (fds) < 0) { return SANE_STATUS_IO_ERROR; } s->read_fds = fds[0]; s->write_fds = fds[1]; /* 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->write_fds); s->write_fds = -1; } return SANE_STATUS_GOOD; stop_scanner_and_return: /* cancel the scan nicely */ do_cancel (s); return status; } SANE_Status sane_read (SANE_Handle handle, SANE_Byte* buf, SANE_Int max_len, SANE_Int* len) { Avision_Scanner* s = handle; ssize_t nread; *len = 0; DBG (8, "sane_read: max_len: %d\n", max_len); nread = read (s->read_fds, buf, (size_t) max_len); if (nread > 0) { DBG (8, "sane_read: got %ld bytes\n", (long) nread); } else { DBG (3, "sane_read: got %ld bytes, err: %d %s\n", (long) nread, errno, strerror(errno)); } if (!s->scanning) return SANE_STATUS_CANCELLED; if (nread < 0) { if (errno == EAGAIN) { return SANE_STATUS_GOOD; } else { do_cancel (s); return SANE_STATUS_IO_ERROR; } } *len = (SANE_Int) nread; /* if all data was passed through */ if (nread == 0) return do_eof (s); return SANE_STATUS_GOOD; } void sane_cancel (SANE_Handle handle) { Avision_Scanner* s = handle; DBG (3, "sane_cancel:\n"); /* always do the housekeeping, e.g. flush batch scanner pages */ do_cancel (s); } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { Avision_Scanner* s = handle; DBG (3, "sane_set_io_mode:\n"); if (!s->scanning) { DBG (3, "sane_set_io_mode: not yet scanning\n"); return SANE_STATUS_INVAL; } if (fcntl (s->read_fds, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0) return SANE_STATUS_IO_ERROR; return SANE_STATUS_GOOD; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int* fd) { Avision_Scanner* s = handle; DBG (3, "sane_get_select_fd:\n"); if (!s->scanning) { DBG (3, "sane_get_select_fd: not yet scanning\n"); return SANE_STATUS_INVAL; } *fd = s->read_fds; return SANE_STATUS_GOOD; } backends-1.3.0/backend/avision.conf.in000066400000000000000000000010431456256263500176140ustar00rootroot00000000000000 # These 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: Mike Kelly #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 backends-1.3.0/backend/avision.h000066400000000000000000000672031456256263500165230ustar00rootroot00000000000000/******************************************************************************* * SANE - Scanner Access Now Easy. avision.h 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, see . 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. ***************************************************************************** This backend is based upon the Tamarack backend and adapted to the Avision scanners by René Rebe and Meino Cramer. Check the avision.c file for detailed copyright and change-log information. ********************************************************************************/ #ifndef avision_h #define avision_h #ifdef HAVE_STDINT_H # include /* available in ISO C99 */ #else # include typedef uint8_t uint8_t; typedef uint16_t uint16_t; typedef uint32_t uint32_t; #endif /* HAVE_STDINT_H */ #ifndef PATH_MAX # define PATH_MAX 1024 #endif typedef enum Avision_ConnectionType { AV_SCSI, AV_USB } Avision_ConnectionType; /* * Translatable custom options text. * */ #define SANE_TITLE_MISC_GROUP SANE_I18N("Miscellaneous") #define SANE_TITLE_INSTALLED_OPTS_GROUP SANE_I18N("Installed options") #define SANE_TITLE_OVERSCAN_TOP SANE_I18N("Overscan top") #define SANE_TITLE_OVERSCAN_BOTTOM SANE_I18N("Overscan bottom") #define SANE_TITLE_BACKGROUND_LINES SANE_I18N("Background raster lines") #define SANE_TITLE_QUALITY_SCAN SANE_I18N("Quality scan") #define SANE_TITLE_MANUAL_EXPOSURE SANE_I18N("Exposure") #define SANE_TITLE_MULTI_SAMPLE SANE_I18N("Multi-sample") #define SANE_TITLE_POWER_SAVE_TIME SANE_I18N("Power save timer control") #define SANE_TITLE_OPTIONS_MSG SANE_I18N("Message text from the scanner") #define SANE_TITLE_NVRAM SANE_I18N("Obtain NVRAM values") #define SANE_TITLE_PAPER_LENGTH SANE_I18N("Use paper length") #define SANE_TITLE_FLIP_PAGE SANE_I18N("Flip document after duplex scanning") #define SANE_TITLE_ADF_INSTALLED SANE_I18N("ADF installed") #define SANE_TITLE_LIGHTBOX_INSTALLED SANE_I18N("Lightbox installed") #define SANE_DESC_OVERSCAN_TOP \ SANE_I18N("The top overscan controls the additional area to scan before the "\ "paper is detected.") #define SANE_DESC_OVERSCAN_BOTTOM \ SANE_I18N("The bottom overscan controls the additional area to scan after "\ "the paper end is detected.") #define SANE_DESC_BACKGROUND_LINES \ SANE_I18N("The background raster controls the additional background lines to "\ "scan before the paper is feed through the scanner.") #define SANE_DESC_QUALITY_SCAN \ SANE_I18N("Turn on quality scanning (slower but better).") #define SANE_DESC_MANUAL_EXPOSURE \ SANE_I18N("Manual exposure adjustment.") #define SANE_DESC_MULTI_SAMPLE \ SANE_I18N("Enable multi-sample scan mode.") #define SANE_DESC_POWER_SAVE_TIME \ SANE_I18N("Allows control of the scanner's power save timer, dimming or "\ "turning off the light.") #define SANE_DESC_OPTIONS_MSG \ SANE_I18N("This text contains device specific options controlled by the "\ "user on the scanner hardware.") #define SANE_DESC_NVRAM \ SANE_I18N("Allows access obtaining the scanner's NVRAM values as pretty "\ "printed text.") #define SANE_DESC_PAPER_LENGTH \ SANE_I18N("Newer scanners can utilize this paper length to detect double feeds. "\ "However some others (DM152) can get confused during media flush if it is set.") #define SANE_DESC_FLIP_PAGE \ SANE_I18N("Tells page-flipping document scanners to flip the paper back to its "\ "original orientation before dropping it in the output tray. "\ "Turning this off might make scanning a little faster if you don't "\ "care about manually flipping the pages afterwards.") #define SANE_DESC_ADF_INSTALLED \ SANE_I18N("ADF option is detected as installed.") #define SANE_DESC_LIGHTBOX_INSTALLED \ SANE_I18N("Lightbox option is detected as installed.") /* information needed for device access */ typedef struct Avision_Connection { Avision_ConnectionType connection_type; int scsi_fd; /* SCSI filedescriptor */ SANE_Int usb_dn; /* USB (libusb or scanner.c) device number */ enum { AVISION_USB_UNTESTED_STATUS, /* status type untested */ AVISION_USB_INT_STATUS, /* interrupt endp. (USB 1.x device) status */ AVISION_USB_BULK_STATUS /* bulk endp. (USB 2.0 device) status */ } usb_status; } Avision_Connection; /* structure for ADF offsets in mm */ typedef struct mm_offset { double top; double bottom; } mm_offset; typedef struct Avision_HWEntry { const char* scsi_mfg; const char* scsi_model; int usb_vendor; int usb_product; const char* real_mfg; const char* real_model; /* feature overwrites - as embedded CPUs have 16bit enums - this would need a change ... */ /* force no calibration */ #define AV_NO_CALIB ((uint64_t)1<<0) /* force all in one command calibration */ #define AV_ONE_CALIB_CMD ((uint64_t)1<<1) /* no gamma table */ #define AV_NO_GAMMA ((uint64_t)1<<2) /* light check is bogus */ #define AV_LIGHT_CHECK_BOGUS ((uint64_t)1<<3) /* no button though the device advertise it */ #define AV_NO_BUTTON ((uint64_t)1<<4) /* if the scan area needs to be forced to A3 */ #define AV_FORCE_A3 ((uint64_t)1<<5) /* if the scan area and resolution needs to be forced for films */ #define AV_FORCE_FILM ((uint64_t)1<<6) /* does not support, or very broken background (added for AV610C2) */ #define AV_NO_BACKGROUND ((uint64_t)1<<7) /* is film scanner - no detection yet */ #define AV_FILMSCANNER ((uint64_t)1<<8) /* fujitsu adaption */ #define AV_FUJITSU ((uint64_t)1<<9) /* gray calibration data has to be uploaded on the blue channel ... ? */ #define AV_GRAY_CALIB_BLUE ((uint64_t)1<<10) /* Interrupt endpoint button readout (so far AV220) */ #define AV_INT_BUTTON ((uint64_t)1<<11) /* send acceleration table ... */ #define AV_ACCEL_TABLE ((uint64_t)1<<12) /* non-interlaced scanns up to 300 dpi (AV32xx / AV83xx) */ #define AV_NON_INTERLACED_DUPLEX_300 ((uint64_t)1<<13) /* do not read multiples of 64 bytes - stalls the USB chip */ #define AV_NO_64BYTE_ALIGN ((uint64_t)1<<14) /* force channel-by-channel calibration */ #define AV_MULTI_CALIB_CMD ((uint64_t)1<<15) /* non color scans are faster with a filter applied (AV32xx) */ #define AV_FASTER_WITH_FILTER ((uint64_t)1<<16) /* interlaced data with 1 line distance */ #define AV_2ND_LINE_INTERLACED ((uint64_t)1<<17) /* does not keep the window though it advertices so */ #define AV_DOES_NOT_KEEP_WINDOW ((uint64_t)1<<18) /* does not keep the gamma though it advertices so */ #define AV_DOES_NOT_KEEP_GAMMA ((uint64_t)1<<19) /* advertises ADF is BGR order, but isn't (or vice versa) */ #define AV_ADF_BGR_ORDER_INVERT ((uint64_t)1<<20) /* allows 12bit mode, though not flagged */ #define AV_12_BIT_MODE ((uint64_t)1<<21) /* very broken background raster */ #define AV_BACKGROUND_QUIRK ((uint64_t)1<<22) /* though marked as GRAY only the scanner can do GRAY modes */ #define AV_GRAY_MODES ((uint64_t)1<<23) /* no separate, single REAR scan (AV122, DM152, ...) */ #define AV_NO_REAR ((uint64_t)1<<24) /* only scan with some known good hardware resolutions, as the scanner fails to properly interpoloate in between (e.g. AV121, DM152 on duplex scans - but also the AV600), software scale and interpolate to all the others */ #define AV_SOFT_SCALE ((uint64_t)1<<25) /* does keep window though it does not advertise it - the AV122/DM152 mess up image data if window is resend between ADF pages */ #define AV_DOES_KEEP_WINDOW ((uint64_t)1<<26) /* does keep gamma though it does not advertise it */ #define AV_DOES_KEEP_GAMMA ((uint64_t)1<<27) /* does the scanner contain a Cancel button? */ #define AV_CANCEL_BUTTON ((uint64_t)1<<28) /* some devices do not need a START_SCAN, even hang with it */ #define AV_NO_START_SCAN ((uint64_t)1<<30) #define AV_INT_STATUS ((uint64_t)1<<31) /* force no calibration */ #define AV_NO_TUNE_SCAN_LENGTH ((uint64_t)1<<32) /* for gray scans, set grey filter */ #define AV_USE_GRAY_FILTER ((uint64_t)1<<33) /* For (HP) scanners with flipping duplexers */ #define AV_ADF_FLIPPING_DUPLEX ((uint64_t)1<<34) /* For scanners which need to have their firmware read to properly function. */ #define AV_FIRMWARE ((uint64_t)1<<35) /* at least Kodak i1120 claims no calibration needed but windows driver does it anyways */ #define AV_FORCE_CALIB ((uint64_t)1<<36) /* at least Kodak i1120 does not have an explicit "quality-scan" mode */ #define AV_NO_QSCAN_MODE ((uint64_t)1<<37) /* at least Kodak i1120 optical DPI is used for overscan calculation */ #define AV_OVERSCAN_OPTDPI ((uint64_t)1<<38) /* some scanners support fast feed-out of the sheet when cancelling a running scan */ #define AV_FASTFEED_ON_CANCEL ((uint64_t)1<<39) /* at least Kodak i1120 does not have an explicit "quality-calibration" mode */ #define AV_NO_QCALIB_MODE ((uint64_t)1<<40) /* Kodak i1120 needs gamma = 1.0 to give decent results */ #define AV_GAMMA_10 ((uint64_t)1<<41) /* Kodak i1120 has a different gamma table format (like a uint16/double array) */ #define AV_GAMMA_UINT16 ((uint64_t)1<<42) /* Kodak i1120 has single-sheet and multi-sheet scan modes. This option sets bitset3[7] which enables multi-sheet scan by default so there is no pause of 1s between two sheets in ADF scan mode. This also fixes some offsets when scanning multiple sheets. */ #define AV_MULTI_SHEET_SCAN ((uint64_t)1<<43) /* maybe more ...*/ uint64_t feature_type; /* ADF offsets in mm */ struct { float first; /* offset difference first sheet */ mm_offset front; /* front-only */ struct { mm_offset front; mm_offset rear; } duplex; } offset; } Avision_HWEntry; typedef enum { AV_ASIC_Cx = 0, AV_ASIC_C1 = 1, AV_ASIC_W1 = 2, AV_ASIC_C2 = 3, AV_ASIC_C5 = 5, AV_ASIC_C6 = 6, AV_ASIC_C7 = 7, AV_ASIC_OA980 = 128, AV_ASIC_OA982 = 129 } asic_type; typedef enum { AV_THRESHOLDED, AV_DITHERED, AV_GRAYSCALE, /* all gray needs to be before color for is_color() */ AV_GRAYSCALE12, AV_GRAYSCALE16, AV_TRUECOLOR, AV_TRUECOLOR12, AV_TRUECOLOR16, AV_COLOR_MODE_LAST } color_mode; typedef enum { AV_NORMAL, AV_TRANSPARENT, AV_ADF, AV_ADF_REAR, AV_ADF_DUPLEX, AV_SOURCE_MODE_LAST } source_mode; typedef enum { AV_NORMAL_DIM, AV_TRANSPARENT_DIM, AV_ADF_DIM, AV_SOURCE_MODE_DIM_LAST } source_mode_dim; enum Avision_Option { OPT_NUM_OPTS = 0, /* must come first */ OPT_MODE_GROUP, OPT_MODE, OPT_RESOLUTION, #define OPT_RESOLUTION_DEFAULT 150 OPT_SPEED, OPT_PREVIEW, OPT_SOURCE, /* scan source normal, transparency, ADF */ OPT_GEOMETRY_GROUP, OPT_TL_X, /* top-left x */ OPT_TL_Y, /* top-left y */ OPT_BR_X, /* bottom-right x */ OPT_BR_Y, /* bottom-right y */ OPT_OVERSCAN_TOP, /* overscan for auto-crop/deskew, if supported */ OPT_OVERSCAN_BOTTOM, OPT_BACKGROUND, /* background raster lines to read out */ OPT_ENHANCEMENT_GROUP, OPT_BRIGHTNESS, OPT_CONTRAST, OPT_QSCAN, OPT_QCALIB, OPT_GAMMA_VECTOR, /* first must be gray */ OPT_GAMMA_VECTOR_R, /* then r g b vector */ OPT_GAMMA_VECTOR_G, OPT_GAMMA_VECTOR_B, OPT_EXPOSURE, /* film exposure adjustment */ OPT_IR, /* infra-red */ OPT_MULTISAMPLE, /* multi-sample */ OPT_MISC_GROUP, OPT_FRAME, /* Film holder control */ OPT_POWER_SAVE_TIME, /* set power save time to the scanner */ OPT_MESSAGE, /* optional message from the scanner display */ OPT_NVRAM, /* retrieve NVRAM values as pretty printed text */ OPT_PAPERLEN, /* Use paper_length field to detect double feeds */ OPT_ADF_FLIP, /* For flipping duplex, reflip the document */ OPT_OPTIONS_GROUP, OPT_OPTION_ADF, // ADF installed/detected? OPT_OPTION_LIGHTBOX, // LightBox installed/detected? NUM_OPTIONS /* must come last */ }; /* structure for ADF offsets in pixels of HW res */ typedef struct hwpx_offset { int top; int bottom; } hwpx_offset; typedef struct Avision_Dimensions { /* in dpi */ int xres; int yres; /* in pixels */ long tlx; long tly; long brx; long bry; /* in pixels */ int line_difference; struct { hwpx_offset front; hwpx_offset rear; } offset; /* interlaced duplex scan */ SANE_Bool interlaced_duplex; /* in dpi, likewise - different if software scaling required */ int hw_xres; int hw_yres; int hw_pixels_per_line; int hw_bytes_per_line; int hw_lines; } Avision_Dimensions; /* this contains our low-level info - not relevant for the SANE interface */ typedef struct Avision_Device { struct Avision_Device* next; SANE_Device sane; Avision_Connection connection; /* structs used to store config options */ SANE_Range dpi_range; SANE_Range x_range; SANE_Range y_range; SANE_Range speed_range; asic_type inquiry_asic_type; SANE_Bool inquiry_new_protocol; SANE_Bool inquiry_nvram_read; SANE_Bool inquiry_power_save_time; SANE_Bool inquiry_adf_capability; SANE_Bool inquiry_duplex; SANE_Bool inquiry_duplex_interlaced; SANE_Bool inquiry_paper_length; SANE_Bool inquiry_batch_scan; SANE_Bool inquiry_detect_accessories; SANE_Bool inquiry_needs_calibration; SANE_Bool inquiry_needs_gamma; SANE_Bool inquiry_keeps_gamma; SANE_Bool inquiry_keeps_window; SANE_Bool inquiry_calibration; SANE_Bool inquiry_3x3_matrix; SANE_Bool inquiry_needs_software_colorpack; SANE_Bool inquiry_needs_line_pack; SANE_Bool inquiry_adf_need_mirror; SANE_Bool inquiry_adf_bgr_order; SANE_Bool inquiry_light_detect; SANE_Bool inquiry_light_control; SANE_Bool inquiry_exposure_control; // Determines from accessories query. SANE_Bool inquiry_light_box_present; SANE_Bool inquiry_adf_present; int inquiry_max_shading_target; SANE_Bool inquiry_button_control; unsigned int inquiry_buttons; SANE_Bool inquiry_tune_scan_length; SANE_Bool inquiry_background_raster; int inquiry_background_raster_pixel; enum {AV_FLATBED, AV_FILM, AV_SHEETFEED } scanner_type; /* the list of available color modes */ SANE_String_Const color_list[AV_COLOR_MODE_LAST + 1]; color_mode color_list_num[AV_COLOR_MODE_LAST]; color_mode color_list_default; /* the list of available source modes */ SANE_String_Const source_list[AV_SOURCE_MODE_LAST + 1]; source_mode source_list_num[AV_SOURCE_MODE_LAST]; int inquiry_optical_res; /* in dpi */ int inquiry_max_res; /* in dpi */ double inquiry_x_ranges [AV_SOURCE_MODE_DIM_LAST]; /* in mm */ double inquiry_y_ranges [AV_SOURCE_MODE_DIM_LAST]; /* in mm */ int inquiry_color_boundary; int inquiry_gray_boundary; int inquiry_dithered_boundary; int inquiry_thresholded_boundary; int inquiry_line_difference; /* software color pack */ int inquiry_channels_per_pixel; int inquiry_bits_per_channel; int inquiry_no_gray_modes; SANE_Bool adf_offset_compensation; int scsi_buffer_size; /* nice to have SCSI buffer size */ int read_stripe_size; /* stripes to be read at-a-time */ /* film scanner attributes - maybe these should be in the scanner struct? */ SANE_Range frame_range; SANE_Word current_frame; SANE_Word holder_type; /* some version corrections */ uint16_t data_dq; /* was ox0A0D - but hangs some new scanners */ Avision_HWEntry* hw; } Avision_Device; /* all the state relevant for the SANE interface */ typedef struct Avision_Scanner { struct Avision_Scanner* next; Avision_Device* hw; SANE_Option_Descriptor opt [NUM_OPTIONS]; Option_Value val [NUM_OPTIONS]; SANE_Int gamma_table [4][256]; /* we now save the calib data because we might need it for 16bit software calibration :-( */ uint8_t* dark_avg_data; uint8_t* white_avg_data; /* background raster data, if duplex first front, then rear */ uint8_t* background_raster; /* 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 */ unsigned int page; /* page counter, 0: uninitialized, 1: scanning 1st page, ... */ int cancelled; SANE_Parameters params; /* scan window */ Avision_Dimensions avdimen; /* scan window - detailed internals */ /* Internal data for duplex scans */ char duplex_rear_fname [PATH_MAX]; SANE_Bool duplex_rear_valid; color_mode c_mode; source_mode source_mode; source_mode_dim source_mode_dim; /* Avision HW Access Connection (SCSI/USB abstraction) */ Avision_Connection av_con; SANE_Pid reader_pid; /* process id of reader */ int read_fds; /* pipe reading end */ int write_fds; /* pipe writing end */ } Avision_Scanner; /* Some Avision driver internal defines */ #define AV_WINID 0 /* Avision SCSI over USB error codes */ #define AVISION_USB_GOOD 0x00 #define AVISION_USB_REQUEST_SENSE 0x02 #define AVISION_USB_BUSY 0x08 /* SCSI commands that the Avision scanners understand: */ #define AVISION_SCSI_TEST_UNIT_READY 0x00 #define AVISION_SCSI_REQUEST_SENSE 0x03 #define AVISION_SCSI_MEDIA_CHECK 0x08 #define AVISION_SCSI_INQUIRY 0x12 #define AVISION_SCSI_MODE_SELECT 0x15 #define AVISION_SCSI_RESERVE_UNIT 0x16 #define AVISION_SCSI_RELEASE_UNIT 0x17 #define AVISION_SCSI_SCAN 0x1b #define AVISION_SCSI_SET_WINDOW 0x24 #define AVISION_SCSI_READ 0x28 #define AVISION_SCSI_SEND 0x2a #define AVISION_SCSI_OBJECT_POSITION 0x31 #define AVISION_SCSI_GET_DATA_STATUS 0x34 #define AVISION_SCSI_OP_REJECT_PAPER 0x00 #define AVISION_SCSI_OP_LOAD_PAPER 0x01 #define AVISION_SCSI_OP_GO_HOME 0x02 #define AVISION_SCSI_OP_TRANS_CALIB_GRAY 0x04 #define AVISION_SCSI_OP_TRANS_CALIB_COLOR 0x05 /* These apply to bitset1. The values are 0 to 6, shifted 3 bits to the left */ #define AVISION_FILTER_NONE 0x00 #define AVISION_FILTER_RED 0x08 #define AVISION_FILTER_GREEN 0x10 #define AVISION_FILTER_BLUE 0x18 #define AVISION_FILTER_RGB 0x20 #define AVISION_FILTER_CMYK 0x28 #define AVISION_FILTER_GRAY 0x30 /* The SCSI structures that we have to send to an avision to get it to do various stuff... */ typedef struct command_header { uint8_t opc; uint8_t pad0 [3]; uint8_t len; uint8_t pad1; } command_header; typedef struct command_set_window { uint8_t opc; uint8_t reserved0 [5]; uint8_t transferlen [3]; uint8_t control; } command_set_window; typedef struct command_read { uint8_t opc; uint8_t bitset1; uint8_t datatypecode; uint8_t readtype; uint8_t datatypequal [2]; uint8_t transferlen [3]; uint8_t control; } command_read; typedef struct command_scan { uint8_t opc; uint8_t bitset0; uint8_t reserved0 [2]; uint8_t transferlen; uint8_t bitset1; } command_scan; typedef struct command_send { uint8_t opc; uint8_t bitset1; uint8_t datatypecode; uint8_t reserved0; uint8_t datatypequal [2]; uint8_t transferlen [3]; uint8_t reserved1; } command_send; typedef struct firmware_status { uint8_t download_firmware; uint8_t first_effective_pixel_flatbed [2]; uint8_t first_effective_pixel_adf_front [2]; uint8_t first_effective_pixel_adf_rear [2]; uint8_t reserved; } firmware_status; typedef struct nvram_data { uint8_t pad_scans [4]; uint8_t adf_simplex_scans [4]; uint8_t adf_duplex_scans [4]; uint8_t flatbed_scans [4]; uint8_t flatbed_leading_edge [2]; uint8_t flatbed_side_edge [2]; uint8_t adf_leading_edge [2]; uint8_t adf_side_edge [2]; uint8_t adf_rear_leading_edge [2]; uint8_t adf_rear_side_edge [2]; uint8_t born_month [2]; uint8_t born_day [2]; uint8_t born_year [2]; uint8_t first_scan_month [2]; uint8_t first_scan_day [2]; uint8_t first_scan_year [2]; uint8_t vertical_magnification [2]; uint8_t horizontal_magnification [2]; uint8_t ccd_type; uint8_t scan_speed; char serial [24]; uint8_t power_saving_time [2]; uint8_t auto_feed; uint8_t roller_count [4]; uint8_t multifeed_count [4]; uint8_t jam_count [4]; uint8_t reserved; char identify_info[16]; char formal_name[16]; uint8_t reserved2 [10]; } nvram_data; typedef struct command_set_window_window { struct { uint8_t reserved0 [6]; uint8_t desclen [2]; } header; struct { uint8_t winid; uint8_t reserved0; uint8_t xres [2]; uint8_t yres [2]; uint8_t ulx [4]; uint8_t uly [4]; uint8_t width [4]; uint8_t length [4]; uint8_t brightness; uint8_t threshold; uint8_t contrast; uint8_t image_comp; uint8_t bpc; uint8_t halftone [2]; uint8_t padding_and_bitset; uint8_t bitordering [2]; uint8_t compr_type; uint8_t compr_arg; uint8_t paper_length[2]; uint8_t reserved1 [4]; /* Avision specific parameters */ uint8_t vendor_specific; uint8_t paralen; /* bytes following after this byte */ } descriptor; struct { uint8_t bitset1; uint8_t highlight; uint8_t shadow; uint8_t line_width [2]; uint8_t line_count [2]; /* the tail is quite version and model specific */ union { struct { uint8_t bitset2; uint8_t reserved; } old; struct { uint8_t bitset2; uint8_t ir_exposure_time; /* optional */ uint8_t r_exposure_time [2]; uint8_t g_exposure_time [2]; uint8_t b_exposure_time [2]; uint8_t bitset3; /* reserved in the v2 */ uint8_t auto_focus; uint8_t line_width_msb; uint8_t line_count_msb; uint8_t background_lines; uint8_t single_sheet_scan; /* from Kodak SVT tool */ } normal; struct { uint8_t reserved0 [4]; uint8_t paper_size; uint8_t paperx [4]; uint8_t papery [4]; uint8_t reserved1 [2]; } fujitsu; } type; } avision; } command_set_window_window; typedef struct page_header { uint8_t pad0 [4]; uint8_t code; uint8_t length; } page_header; typedef struct avision_page { uint8_t gamma; uint8_t thresh; uint8_t masks; uint8_t delay; uint8_t features; uint8_t pad0; } avision_page; typedef struct calibration_format { uint16_t pixel_per_line; uint8_t bytes_per_channel; uint8_t lines; uint8_t flags; uint8_t ability1; uint8_t r_gain; uint8_t g_gain; uint8_t b_gain; uint16_t r_shading_target; uint16_t g_shading_target; uint16_t b_shading_target; uint16_t r_dark_shading_target; uint16_t g_dark_shading_target; uint16_t b_dark_shading_target; /* not returned but useful in some places */ uint8_t channels; } calibration_format; typedef struct matrix_3x3 { uint16_t v[9]; } matrix_3x3; typedef struct acceleration_info { uint16_t total_steps; uint16_t stable_steps; uint32_t table_units; uint32_t base_units; uint16_t start_speed; uint16_t target_speed; uint8_t ability; uint8_t table_count; uint8_t reserved[6]; } acceleration_info; /* set/get SCSI highended (big-endian) variables. Declare them as an array * of chars endianness-safe, int-size safe ... */ #define set_double(var,val) var[0] = (uint8_t) (((val) >> 8) & 0xff); \ var[1] = (uint8_t) (((val) ) & 0xff) #define set_triple(var,val) var[0] = (uint8_t) (((val) >> 16) & 0xff); \ var[1] = (uint8_t) (((val) >> 8 ) & 0xff); \ var[2] = (uint8_t) (((val) ) & 0xff) #define set_quad(var,val) var[0] = (uint8_t) (((val) >> 24) & 0xff); \ var[1] = (uint8_t) (((val) >> 16) & 0xff); \ var[2] = (uint8_t) (((val) >> 8 ) & 0xff); \ var[3] = (uint8_t) (((val) ) & 0xff) #define get_double(var) ((*var << 8) + *(var + 1)) #define get_triple(var) ((*var << 16) + \ (*(var + 1) << 8) + *(var + 2)) #define get_quad(var) ((*var << 24) + \ (*(var + 1) << 16) + \ (*(var + 2) << 8) + *(var + 3)) /* set/get Avision lowended (little-endian) shading data */ #define set_double_le(var,val) var[0] = (uint8_t) (((val) ) & 0xff); \ var[1] = (uint8_t) (((val) >> 8) & 0xff) #define get_double_le(var) ((uint16_t) ((*(var + 1) << 8) + *(var))) #define BIT(n, p) ((n & (1 << p)) ? 1 : 0) #define SET_BIT(n, p) (n |= (1 << p)) #define CLEAR_BIT(n, p) (n &= ~(1 << p)) /* These should be in saneopts.h */ #define SANE_NAME_FRAME "frame" #define SANE_TITLE_FRAME SANE_I18N("Number of the frame to scan") #define SANE_DESC_FRAME SANE_I18N("Selects the number of the frame to scan") #define SANE_NAME_DUPLEX "duplex" #define SANE_TITLE_DUPLEX SANE_I18N("Duplex scan") #define SANE_DESC_DUPLEX SANE_I18N("Duplex scan provides a scan of the front and back side of the document") #ifdef AVISION_ENHANCED_SANE #warning "Compiled Avision backend will violate the SANE standard" /* Some Avision SANE extensions */ typedef enum { SANE_STATUS_LAMP_WARMING = SANE_STATUS_ACCESS_DENIED + 1 /* lamp is warming up */ } SANE_Avision_Status; /* public API extension */ extern SANE_Status ENTRY(media_check) (SANE_Handle handle); #endif typedef enum { AVISION_DATATYPECODE_READ_IMAGE_DATA = 0x00, AVISION_DATATYPECODE_GET_CALIBRATION_FORMAT = 0x60, AVISION_DATATYPECODE_DETECT_ACCESSORIES = 0x64, AVISION_DATATYPECODE_READ_NVRAM_DATA = 0x69, AVISION_DATATYPECODE_FLASH_RAM_INFO = 0x6a, AVISION_DATATYPECODE_ACCELERATION_TABLE = 0x6c, AVISION_DATATYPECODE_DOWNLOAD_GAMMA_TABLE = 0x81, AVISION_DATATYPECODE_3X3_COLOR_MATRIX = 0x83, AVISION_DATATYPECODE_SEND_NVRAM_DATA = 0x85, AVISION_DATATYPECODE_FLASH_DATA = 0x86, AVISION_DATATYPECODE_FILM_HOLDER_SENSE = 0x87, AVISION_DATATYPECODE_FIRMWARE_STATUS = 0x90, AVISION_DATATYPECODE_ATTACH_TRUNCATE_TAIL = 0x95, AVISION_DATATYPECODE_ATTACH_TRUNCATE_HEAD = 0x96, AVISION_DATATYPECODE_GET_BACKGROUND_RASTER = 0x9b, AVISION_DATATYPECODE_LIGHT_STATUS = 0xa0, AVISION_DATATYPECODE_BUTTON_STATUS = 0xa1, AVISION_DATATYPECODE_POWER_SAVING_TIMER = 0xa2, AVISION_DATATYPECODE_READ_DUPLEX_INFO = 0xb1, AVISION_DATATYPECODE_UNKNOWN = 0xd0, AVISION_DATATYPECODE_READ_GENERAL_ABILITY_PARAM = 0xd2, } Avision_Datatypecode; #endif /* avision_h */ backends-1.3.0/backend/bh.c000066400000000000000000003407121456256263500154360ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1999,2000 Tom Martone This file is part of a SANE backend for Bell and Howell Copiscan II Scanners using the Remote SCSI Controller(RSC). 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, see . 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 #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_scsi.h" #include "../include/sane/sanei_config.h" #define BACKEND_NAME bh #include "../include/sane/sanei_backend.h" #define BUILD 4 #include "bh.h" #define MIN(x,y) ((x)<(y) ? (x) : (y)) #define MAX(x,y) ((x)>(y) ? (x) : (y)) static const SANE_Device **devlist = 0; static int num_devices = 0; static BH_Device *first_dev = NULL; static BH_Scanner *first_handle = NULL; static SANE_Char inquiry_data[255] = "Bell+Howell scanner"; static SANE_Int disable_optional_frames = 0; static SANE_Int fake_inquiry = 0; static int allblank(const char *s) { while (s && *s) if (!isspace(*s++)) return 0; return 1; } static size_t max_string_size (const SANE_String_Const strings[]) { size_t size, max_size = 0; int i; for (i = 0; strings[i]; ++i) { size = strlen (strings[i]) + 1; if (size > max_size) max_size = size; } return max_size; } static void trim_spaces(char *s, size_t n) { for (s += (n-1); n > 0; n--, s--) { if (*s && !isspace(*s)) break; *s = '\0'; } } static SANE_String_Const print_devtype (SANE_Byte devtype) { static SANE_String devtypes[] = { "disk", "tape", "printer", "processor", "CD-writer", "CD-drive", "scanner", "optical-drive", "jukebox", "communicator" }; return (devtype > 0 && devtype < NELEMS(devtypes)) ? devtypes[devtype] : "unknown-device"; } static SANE_String_Const print_barcodetype (SANE_Int i) { return (i > 0 && i < NELEMS(barcode_search_bar_list)) ? barcode_search_bar_list[i] : (SANE_String_Const) "unknown"; } static SANE_String_Const print_orientation (SANE_Int i) { switch(i) { case 0: case 7: return "vertical upwards"; case 1: case 2: return "horizontal right"; case 3: case 4: return "vertical downwards"; case 5: case 6: return "horizontal left"; default: return "unknown"; } } static SANE_String_Const print_read_type (SANE_Int i) { static char buf[32]; SANE_Int n; /* translate BH_SCSI_READ_TYPE_ codes to a human-readable string */ if (i == BH_SCSI_READ_TYPE_FRONT) { strcpy(buf, "front page"); } else if (i == BH_SCSI_READ_TYPE_BACK) { strcpy(buf, "back page"); } else if (i > BH_SCSI_READ_TYPE_FRONT && i <= BH_SCSI_READ_TYPE_FRONT + NUM_SECTIONS) { n = i - BH_SCSI_READ_TYPE_FRONT; sprintf(buf, "front section %d", n); } else if (i > BH_SCSI_READ_TYPE_BACK && i <= BH_SCSI_READ_TYPE_BACK + NUM_SECTIONS) { n = i - BH_SCSI_READ_TYPE_BACK; sprintf(buf, "back section %d", n); } else if (i == BH_SCSI_READ_TYPE_FRONT_BARCODE) { strcpy(buf, "front page barcode"); } else if (i == BH_SCSI_READ_TYPE_BACK_BARCODE) { strcpy(buf, "back page barcode"); } else if (i > BH_SCSI_READ_TYPE_FRONT_BARCODE && i <= BH_SCSI_READ_TYPE_FRONT_BARCODE + NUM_SECTIONS) { n = i - BH_SCSI_READ_TYPE_FRONT_BARCODE; sprintf(buf, "front barcode section %d", n); } else if (i > BH_SCSI_READ_TYPE_BACK_BARCODE && i <= BH_SCSI_READ_TYPE_BACK_BARCODE + NUM_SECTIONS) { n = i - BH_SCSI_READ_TYPE_BACK_BARCODE; sprintf(buf, "back barcode section %d", n); } else if (i == BH_SCSI_READ_TYPE_FRONT_PATCHCODE) { strcpy(buf, "front page patchcode"); } else if (i == BH_SCSI_READ_TYPE_BACK_PATCHCODE) { strcpy(buf, "back page patchcode"); } else if (i > BH_SCSI_READ_TYPE_FRONT_PATCHCODE && i <= BH_SCSI_READ_TYPE_FRONT_PATCHCODE + NUM_SECTIONS) { n = i - BH_SCSI_READ_TYPE_FRONT_PATCHCODE; sprintf(buf, "front patchcode section %d", n); } else if (i > BH_SCSI_READ_TYPE_BACK_PATCHCODE && i <= BH_SCSI_READ_TYPE_BACK_PATCHCODE + NUM_SECTIONS) { n = i - BH_SCSI_READ_TYPE_BACK_PATCHCODE; sprintf(buf, "back patchcode section %d", n); } else if (i == BH_SCSI_READ_TYPE_FRONT_ICON) { strcpy(buf, "front page icon"); } else if (i == BH_SCSI_READ_TYPE_BACK_ICON) { strcpy(buf, "back page icon"); } else if (i == BH_SCSI_READ_TYPE_SENDBARFILE) { strcpy(buf, "transmit bar/patch codes"); } else { strcpy(buf, "unknown"); } return buf; } static SANE_Int get_rotation_id(char *s) { SANE_Int i; for (i = 0; rotation_list[i]; i++) if (strcmp(s, rotation_list[i]) == 0) break; /* unknown strings are treated as '0' */ return rotation_list[i] ? i : 0; } static SANE_Int get_compression_id(char *s) { SANE_Int i; for (i = 0; compression_list[i]; i++) if (strcmp(s, compression_list[i]) == 0) break; /* unknown strings are treated as 'none' */ return compression_list[i] ? i : 0; } static SANE_Int get_barcode_id(char *s) { SANE_Int i; for (i = 0; barcode_search_bar_list[i]; i++) if (strcmp(s, barcode_search_bar_list[i]) == 0) break; /* unknown strings are treated as 'none' */ return barcode_search_bar_list[i] ? i : 0; } static SANE_Int get_scan_mode_id(char *s) { SANE_Int i; for (i = 0; scan_mode_list[i]; i++) if (strcmp(s, scan_mode_list[i]) == 0) break; /* unknown strings are treated as 'lineart' */ return scan_mode_list[i] ? i : 0; } static SANE_Int get_paper_id(char *s) { SANE_Int i; for (i = 0; paper_list[i]; i++) if (strcmp(s, paper_list[i]) == 0) break; /* unknown strings are treated as 'custom' */ return paper_list[i] ? i : 0; } static SANE_Int get_barcode_search_mode(char *s) { SANE_Int i; if (strcmp(s, "horizontal") == 0) { i = 1; } else if (strcmp(s, "vertical") == 0) { i = 2; } else if (strcmp(s, "vert-horiz") == 0) { i = 6; } else if (strcmp(s, "horiz-vert") == 0) { i = 9; } else { /* unknown strings are treated as 'horiz-vert' */ DBG(1, "get_barcode_search_mode: unrecognized string `%s'\n", s); i = 9; } return i; } static void appendStdList(BH_Info *sc, SANE_Int res) { /* append entry to resolution list - a SANE_WORD_LIST */ sc->resStdList[sc->resStdList[0]+1] = res; sc->resStdList[0]++; } static void ScannerDump(BH_Scanner *s) { int i; BH_Info *info; SANE_Device *sdev; info = &s->hw->info; sdev = &s->hw->sane; DBG (1, "SANE Device: '%s' Vendor: '%s' Model: '%s' Type: '%s'\n", sdev->name, sdev->vendor, sdev->model, sdev->type); DBG (1, "Type: '%s' Vendor: '%s' Product: '%s' Revision: '%s'\n", print_devtype(info->devtype), info->vendor, info->product, info->revision); DBG (1, "Automatic Document Feeder:%s\n", info->canADF ? " " : " "); DBG (1, "Colors:%s%s\n", info->colorBandW ? " " : "", info->colorHalftone ? " " : ""); DBG (1, "Data processing:%s%s%s%s%s%s\n", info->canWhiteFrame ? " " : "", info->canBlackFrame ? " " : "", info->canEdgeExtract ? " " : "", info->canNoiseFilter ? " " : "", info->canSmooth ? " " : "", info->canLineBold ? " " : ""); DBG (1, "Compression:%s%s%s\n", info->comprG3_1D ? " " : "", info->comprG3_2D ? " " : "", info->comprG4 ? " " : ""); DBG (1, "Optional Features:%s%s%s%s\n", info->canBorderRecog ? " " : "", info->canBarCode ? " " : "", info->canIcon ? " " : "", info->canSection ? "

" : ""); DBG (1, "Max bytes per scan-line: %d (%d pixels)\n", info->lineMaxBytes, info->lineMaxBytes * 8); DBG (1, "Basic resolution (X/Y): %d/%d\n", info->resBasicX, info->resBasicY); DBG (1, "Maximum resolution (X/Y): %d/%d\n", info->resMaxX, info->resMaxY); DBG (1, "Minimum resolution (X/Y): %d/%d\n", info->resMinX, info->resMinY); DBG (1, "Standard Resolutions:\n"); for (i = 0; i < info->resStdList[0]; i++) DBG (1, " %d\n", info->resStdList[i+1]); DBG (1, "Window Width/Height (in basic res) %d/%d (%.2f/%.2f inches)\n", info->winWidth, info->winHeight, (info->resBasicX != 0) ? ((float) info->winWidth) / info->resBasicX : 0.0, (info->resBasicY) ? ((float) info->winHeight) / info->resBasicY : 0.0); DBG (1, "Summary:%s%s%s\n", info->canDuplex ? "Duplex Scanner" : "Simplex Scanner", info->canACE ? " (ACE capable)" : "", info->canCheckADF ? " (ADF Paper Sensor capable)" : ""); sprintf(inquiry_data, "Vendor: %s Product: %s Rev: %s %s%s%s\n", info->vendor, info->product, info->revision, info->canDuplex ? "Duplex Scanner" : "Simplex Scanner", info->canACE ? " (ACE capable)" : "", info->canCheckADF ? " (ADF Paper Sensor capable)" : ""); DBG (5, "autoborder_default=%d\n", info->autoborder_default); DBG (5, "batch_default=%d\n", info->batch_default); DBG (5, "deskew_default=%d\n", info->deskew_default); DBG (5, "check_adf_default=%d\n", info->check_adf_default); DBG (5, "duplex_default=%d\n", info->duplex_default); DBG (5, "timeout_adf_default=%d\n", info->timeout_adf_default); DBG (5, "timeout_manual_default=%d\n", info->timeout_manual_default); DBG (5, "control_panel_default=%d\n", info->control_panel_default); } static SANE_Status test_unit_ready (int fd) { static SANE_Byte cmd[6]; SANE_Status status; DBG (3, "test_unit_ready called\n"); cmd[0] = BH_SCSI_TEST_UNIT_READY; memset (cmd, 0, sizeof (cmd)); status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0); return status; } static SANE_Status object_position (BH_Scanner *s) { static SANE_Byte cmd[10]; SANE_Status status; DBG (3, "object_position called\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = BH_SCSI_OBJECT_POSITION; cmd[1] = 0x01; status = sanei_scsi_cmd (s->fd, cmd, sizeof (cmd), 0, 0); return status; } static SANE_Status read_barcode_data (BH_Scanner *s, FILE *fp) { static SANE_Byte cmd[10]; SANE_Status status; SANE_Int num_found = 0; double w, l, x, y, res; struct barcode_data buf; size_t buf_size = sizeof(buf); DBG (3, "read_barcode_data called\n"); memset (&cmd, 0, sizeof (cmd)); cmd[0] = BH_SCSI_READ_SCANNED_DATA; cmd[2] = s->readlist[s->readptr]; _lto3b(buf_size, &cmd[6]); /* transfer length */ s->barcode_not_found = SANE_FALSE; do { memset (&buf, 0, sizeof(buf)); status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), &buf, &buf_size); if (status != SANE_STATUS_GOOD) break; if (s->barcode_not_found == SANE_TRUE) break; num_found++; buf.barcodedata[sizeof(buf.barcodedata)-1] = '\0'; /* calculate the bounding rectangle */ x = MIN((int) _2btol(buf.posxb), (int) _2btol(buf.posxa)); y = MIN((int) _2btol(buf.posyb), (int) _2btol(buf.posyd)); w = MAX((int) _2btol(buf.posxd), (int) _2btol(buf.posxd)) - x; l = MAX((int) _2btol(buf.posya), (int) _2btol(buf.posyc)) - y; /* convert from pixels to mm */ res = _OPT_VAL_WORD(s, OPT_RESOLUTION); if (res <= 0.0) { /* avoid divide by zero */ DBG(1, "read_barcode_data: warning: " "encountered bad resolution value '%f', replacing with '%f'\n", res, 200.0); res = 200.0; } x = x * MM_PER_INCH / res; y = y * MM_PER_INCH / res; w = w * MM_PER_INCH / res; l = l * MM_PER_INCH / res; /* add a bit of a border around the edges */ x = MAX(0.0, x - BH_DECODE_FUDGE); y = MAX(0.0, y - BH_DECODE_FUDGE); w += (BH_DECODE_FUDGE * 4); l += (BH_DECODE_FUDGE * 4); /* write the decoded barcode data into the file */ fprintf(fp, "\n
%s
\n", print_read_type((int) s->readlist[s->readptr])); fprintf(fp, " %s\n %d\n", print_barcodetype((int) _2btol(buf.barcodetype)), (int) _2btol(buf.statusflag)); fprintf(fp, " %s\n", print_orientation((int) _2btol(buf.barcodeorientation))); fprintf(fp, " \n %d%d\n", (int) _2btol(buf.posxb), (int) _2btol(buf.posyb)); fprintf(fp, " %d%d\n", (int) _2btol(buf.posxd), (int) _2btol(buf.posyd)); fprintf(fp, " %d%d\n", (int) _2btol(buf.posxa), (int) _2btol(buf.posya)); fprintf(fp, "
%d%d
\n
\n", (int) _2btol(buf.posxc), (int) _2btol(buf.posyc)); fprintf(fp, " %.2fx%.2f+%.2f+%.2f\n", w, l, x, y); fprintf(fp, " %d\n %d\n", (int) _2btol(buf.barcodesearchtime), (int) buf.barcodelen); fprintf(fp, " %s\n
\n", buf.barcodedata); } while (num_found <= BH_DECODE_TRIES); DBG (3, "read_barcode_data: found %d barcodes, returning %s\n", num_found, sane_strstatus(status)); return status; } static SANE_Status read_icon_data (BH_Scanner *s) { static SANE_Byte cmd[10]; SANE_Status status; struct icon_data buf; size_t buf_size = sizeof(buf); DBG (3, "read_icon_data called\n"); memset (&cmd, 0, sizeof (cmd)); cmd[0] = BH_SCSI_READ_SCANNED_DATA; cmd[2] = s->readlist[s->readptr]; _lto3b(buf_size, &cmd[6]); /* transfer length */ memset (&buf, 0, sizeof(buf)); status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), &buf, &buf_size); /* set the fields in the scanner handle for later reference */ s->iconwidth = _4btol(buf.iconwidth); s->iconlength = _4btol(buf.iconlength); DBG(3, "read_icon_data: windowwidth:%lu, windowlength:%lu\n", _4btol(buf.windowwidth), _4btol(buf.windowlength)); DBG(3, "read_icon_data: iconwidth:%lu, iconlength:%lu, iconwidth(bytes):%lu\n", _4btol(buf.iconwidth), _4btol(buf.iconlength), _4btol(buf.iconwidthbytes)); DBG(3, "read_icon_data: bitordering:%02x, icondatalen:%lu\n", buf.bitordering, _4btol(buf.icondatalen)); DBG (3, "read_icon_data returning %d\n", status); return status; } static SANE_Status read_barfile (BH_Scanner *s, void *buf, size_t *buf_size) { SANE_Status status = SANE_STATUS_GOOD; size_t nread; DBG (3, "read_barfile called (%lu bytes)\n", (u_long) *buf_size); if (s->barf != NULL) { /* this function needs to set InvalidBytes so it looks * like a B&H scsi EOF */ if ((nread = fread(buf, 1, *buf_size, s->barf)) < *buf_size) { /* set InvalidBytes */ s->InvalidBytes = *buf_size - nread; if (ferror(s->barf)) { status = SANE_STATUS_IO_ERROR; fclose(s->barf); s->barf = NULL; unlink(s->barfname); } else if (feof(s->barf)) { /* it also needs to close the file and delete it when EOF is * reached. */ fclose(s->barf); s->barf = NULL; unlink(s->barfname); } } } else { /* set InvalidBytes */ s->InvalidBytes = *buf_size; } return status; } static SANE_Status read_data (BH_Scanner *s, void *buf, size_t *buf_size) { static SANE_Byte cmd[10]; SANE_Status status; DBG (3, "read_data called (%lu bytes)\n", (u_long) *buf_size); if (s->readlist[s->readptr] == BH_SCSI_READ_TYPE_SENDBARFILE) { /* call special barcode data read function. */ status = read_barfile(s, buf, buf_size); } else { memset (&cmd, 0, sizeof (cmd)); cmd[0] = BH_SCSI_READ_SCANNED_DATA; cmd[2] = s->readlist[s->readptr]; _lto3b(*buf_size, &cmd[6]); /* transfer length */ status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), buf, buf_size); } return status; } static SANE_Status mode_select_measurement (BH_Scanner *s) { static struct { SANE_Byte cmd[6]; struct mode_page_03 mp; } select_cmd; SANE_Status status; DBG (3, "mode_select_measurement called (bmu:%d mud:%d)\n", s->bmu, s->mud); memset (&select_cmd, 0, sizeof (select_cmd)); select_cmd.cmd[0] = BH_SCSI_MODE_SELECT; select_cmd.cmd[1] = 0x10; select_cmd.cmd[4] = sizeof(select_cmd.mp); select_cmd.mp.pagecode = BH_MODE_MEASUREMENT_PAGE_CODE; select_cmd.mp.paramlen = 0x06; select_cmd.mp.bmu = s->bmu; _lto2b(s->mud, select_cmd.mp.mud); status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0); return status; } static SANE_Status mode_select_timeout (BH_Scanner *s) { static struct { SANE_Byte cmd[6]; struct mode_page_20 mp; } select_cmd; SANE_Status status; DBG (3, "mode_select_timeout called\n"); memset (&select_cmd, 0, sizeof (select_cmd)); select_cmd.cmd[0] = BH_SCSI_MODE_SELECT; select_cmd.cmd[1] = 0x10; select_cmd.cmd[4] = sizeof(select_cmd.mp); select_cmd.mp.pagecode = BH_MODE_TIMEOUT_PAGE_CODE; select_cmd.mp.paramlen = 0x06; select_cmd.mp.timeoutmanual = _OPT_VAL_WORD(s, OPT_TIMEOUT_MANUAL); select_cmd.mp.timeoutadf = _OPT_VAL_WORD(s, OPT_TIMEOUT_ADF); status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0); return status; } static SANE_Status mode_select_icon (BH_Scanner *s) { static struct { SANE_Byte cmd[6]; struct mode_page_21 mp; } select_cmd; SANE_Status status; DBG (3, "mode_select_icon called\n"); memset (&select_cmd, 0, sizeof (select_cmd)); select_cmd.cmd[0] = BH_SCSI_MODE_SELECT; select_cmd.cmd[1] = 0x10; select_cmd.cmd[4] = sizeof(select_cmd.mp); select_cmd.mp.pagecode = BH_MODE_ICON_PAGE_CODE; select_cmd.mp.paramlen = 0x06; _lto2b(_OPT_VAL_WORD(s, OPT_ICON_WIDTH), select_cmd.mp.iconwidth); _lto2b(_OPT_VAL_WORD(s, OPT_ICON_LENGTH), select_cmd.mp.iconlength); status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0); return status; } static SANE_Status mode_select_barcode_priority (BH_Scanner *s) { static struct { SANE_Byte cmd[6]; struct mode_page_30 mp; } select_cmd; SANE_Status status; int i; DBG (3, "mode_select_barcode_priority called\n"); memset (&select_cmd, 0, sizeof (select_cmd)); select_cmd.cmd[0] = BH_SCSI_MODE_SELECT; select_cmd.cmd[1] = 0x10; select_cmd.cmd[4] = sizeof(select_cmd.mp); select_cmd.mp.pagecode = BH_MODE_BARCODE_PRIORITY_PAGE_CODE; select_cmd.mp.paramlen = 0x06; for (i = 0; i < NUM_SEARCH_BARS; i++) { /* anything after a 'none' is ignored */ if ((select_cmd.mp.priority[i] = s->search_bars[i]) == 0) break; } status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0); return status; } static SANE_Status mode_select_barcode_param1 (BH_Scanner *s) { static struct { SANE_Byte cmd[6]; struct mode_page_31 mp; } select_cmd; SANE_Status status; DBG (3, "mode_select_barcode_param1 called\n"); memset (&select_cmd, 0, sizeof (select_cmd)); select_cmd.cmd[0] = BH_SCSI_MODE_SELECT; select_cmd.cmd[1] = 0x10; select_cmd.cmd[4] = sizeof(select_cmd.mp); select_cmd.mp.pagecode = BH_MODE_BARCODE_PARAM1_PAGE_CODE; select_cmd.mp.paramlen = 0x06; _lto2b((SANE_Int)_OPT_VAL_WORD_THOUSANDTHS(s, OPT_BARCODE_HMIN), select_cmd.mp.minbarheight); select_cmd.mp.searchcount = _OPT_VAL_WORD(s, OPT_BARCODE_SEARCH_COUNT); select_cmd.mp.searchmode = get_barcode_search_mode(_OPT_VAL_STRING(s, OPT_BARCODE_SEARCH_MODE)); _lto2b(_OPT_VAL_WORD(s, OPT_BARCODE_SEARCH_TIMEOUT), select_cmd.mp.searchtimeout); status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0); return status; } static SANE_Status mode_select_barcode_param2 (BH_Scanner *s) { static struct { SANE_Byte cmd[6]; struct mode_page_32 mp; } select_cmd; SANE_Status status; size_t len; DBG (3, "mode_select_barcode_param2 called\n"); /* first we'll do a mode sense, then we'll overwrite with * our new values, and then do a mode select */ memset (&select_cmd, 0, sizeof (select_cmd)); select_cmd.cmd[0] = BH_SCSI_MODE_SENSE; select_cmd.cmd[2] = BH_MODE_BARCODE_PARAM2_PAGE_CODE; select_cmd.cmd[4] = sizeof(select_cmd.mp); len = sizeof(select_cmd.mp); status = sanei_scsi_cmd (s->fd, &select_cmd.cmd, sizeof (select_cmd.cmd), &select_cmd.mp, &len); if (status == SANE_STATUS_GOOD) { DBG(8, "mode_select_barcode_param2: sensed values: relmax:%d barmin:%d barmax:%d\n", (int) _2btol(select_cmd.mp.relmax), (int) _2btol(select_cmd.mp.barmin), (int) _2btol(select_cmd.mp.barmax)); memset (&select_cmd.cmd, 0, sizeof (select_cmd.cmd)); select_cmd.cmd[0] = BH_SCSI_MODE_SELECT; select_cmd.cmd[1] = 0x10; select_cmd.cmd[4] = sizeof(select_cmd.mp); select_cmd.mp.modedatalen = 0x00; select_cmd.mp.mediumtype = 0x00; select_cmd.mp.devicespecificparam = 0x00; select_cmd.mp.blockdescriptorlen = 0x00; select_cmd.mp.pagecode = BH_MODE_BARCODE_PARAM2_PAGE_CODE; select_cmd.mp.paramlen = 0x06; /* only overwrite the default values if the option is non-zero */ if (_OPT_VAL_WORD(s, OPT_BARCODE_RELMAX) != 0) { _lto2b(_OPT_VAL_WORD(s, OPT_BARCODE_RELMAX), select_cmd.mp.relmax); } if (_OPT_VAL_WORD(s, OPT_BARCODE_BARMIN) != 0) { _lto2b(_OPT_VAL_WORD(s, OPT_BARCODE_BARMIN), select_cmd.mp.barmin); } if (_OPT_VAL_WORD(s, OPT_BARCODE_BARMAX) != 0) { _lto2b(_OPT_VAL_WORD(s, OPT_BARCODE_BARMAX), select_cmd.mp.barmax); } DBG(8, "mode_select_barcode_param2: param values: relmax:%d barmin:%d barmax:%d\n", (int) _OPT_VAL_WORD(s, OPT_BARCODE_RELMAX), (int) _OPT_VAL_WORD(s, OPT_BARCODE_BARMIN), (int) _OPT_VAL_WORD(s, OPT_BARCODE_BARMAX)); DBG(8, "mode_select_barcode_param2: select values: relmax:%d barmin:%d barmax:%d\n", (int) _2btol(select_cmd.mp.relmax), (int) _2btol(select_cmd.mp.barmin), (int) _2btol(select_cmd.mp.barmax)); status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0); } return status; } static SANE_Status mode_select_barcode_param3 (BH_Scanner *s) { static struct { SANE_Byte cmd[6]; struct mode_page_33 mp; } select_cmd; SANE_Status status; size_t len; DBG (3, "mode_select_barcode_param3 called\n"); /* first we'll do a mode sense, then we'll overwrite with * our new values, and then do a mode select */ memset (&select_cmd, 0, sizeof (select_cmd)); select_cmd.cmd[0] = BH_SCSI_MODE_SENSE; select_cmd.cmd[2] = BH_MODE_BARCODE_PARAM3_PAGE_CODE; select_cmd.cmd[4] = sizeof(select_cmd.mp); len = sizeof(select_cmd.mp); status = sanei_scsi_cmd (s->fd, &select_cmd.cmd, sizeof (select_cmd.cmd), &select_cmd.mp, &len); if (status == SANE_STATUS_GOOD) { DBG(8, "mode_select_barcode_param3: sensed values: contrast:%d patchmode:%d\n", (int) _2btol(select_cmd.mp.barcodecontrast), (int) _2btol(select_cmd.mp.patchmode)); memset (&select_cmd.cmd, 0, sizeof (select_cmd.cmd)); select_cmd.cmd[0] = BH_SCSI_MODE_SELECT; select_cmd.cmd[1] = 0x10; select_cmd.cmd[4] = sizeof(select_cmd.mp); select_cmd.mp.modedatalen = 0x00; select_cmd.mp.mediumtype = 0x00; select_cmd.mp.devicespecificparam = 0x00; select_cmd.mp.blockdescriptorlen = 0x00; select_cmd.mp.pagecode = BH_MODE_BARCODE_PARAM3_PAGE_CODE; select_cmd.mp.paramlen = 0x06; /* only overwrite the default values if the option is non-zero */ if (_OPT_VAL_WORD(s, OPT_BARCODE_CONTRAST) != 0) { _lto2b(_OPT_VAL_WORD(s, OPT_BARCODE_CONTRAST), select_cmd.mp.barcodecontrast); } if (_OPT_VAL_WORD(s, OPT_BARCODE_PATCHMODE) != 0) { _lto2b(_OPT_VAL_WORD(s, OPT_BARCODE_PATCHMODE), select_cmd.mp.patchmode); } DBG(8, "mode_select_barcode_param3: param values: contrast:%d patchmode:%d\n", (int) _OPT_VAL_WORD(s, OPT_BARCODE_CONTRAST), (int) _OPT_VAL_WORD(s, OPT_BARCODE_PATCHMODE)); DBG(8, "mode_select_barcode_param3: select values: contrast:%d patchmode:%d\n", (int) _2btol(select_cmd.mp.barcodecontrast), (int) _2btol(select_cmd.mp.patchmode)); status = sanei_scsi_cmd (s->fd, &select_cmd, sizeof (select_cmd), 0, 0); } return status; } static SANE_Status inquiry (int fd, void *buf, size_t *buf_size, SANE_Byte evpd, SANE_Byte page_code) { static SANE_Byte cmd[6]; SANE_Status status; DBG (3, "inquiry called\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = BH_SCSI_INQUIRY; cmd[1] = evpd; cmd[2] = page_code; cmd[4] = *buf_size; status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, buf_size); return status; } static SANE_Status set_window (BH_Scanner *s, SANE_Byte batchmode) { static struct { SANE_Byte cmd[10]; SANE_Byte hdr[8]; struct window_data window; } set_window_cmd; SANE_Status status; SANE_Int width, length, i, format, rotation, deskew ; DBG (3, "set_window called\n"); /* set to thousandths for set_window */ s->bmu = BH_UNIT_INCH; s->mud = 1000; status = mode_select_measurement(s); if (status != SANE_STATUS_GOOD) return status; memset (&set_window_cmd, 0, sizeof (set_window_cmd)); set_window_cmd.cmd[0] = BH_SCSI_SET_WINDOW; DBG(3, "set_window: sizeof(hdr) %d, sizeof(window): %d\n", (int)sizeof(set_window_cmd.hdr), (int)sizeof(set_window_cmd.window)); _lto3b(sizeof(set_window_cmd.hdr) + sizeof(set_window_cmd.window), &set_window_cmd.cmd[6]); _lto2b(256, &set_window_cmd.hdr[6]); set_window_cmd.window.windowid = 0; set_window_cmd.window.autoborder = _OPT_VAL_WORD(s, OPT_AUTOBORDER); DBG (5, "autoborder set to=%d\n", set_window_cmd.window.autoborder); _lto2b(_OPT_VAL_WORD(s, OPT_RESOLUTION), set_window_cmd.window.xres); _lto2b(_OPT_VAL_WORD(s, OPT_RESOLUTION), set_window_cmd.window.yres); _lto4b((int) _OPT_VAL_WORD_THOUSANDTHS(s, OPT_TL_X), set_window_cmd.window.ulx); _lto4b((int) _OPT_VAL_WORD_THOUSANDTHS(s, OPT_TL_Y), set_window_cmd.window.uly); width = (SANE_Int) (_OPT_VAL_WORD_THOUSANDTHS(s, OPT_BR_X) - _OPT_VAL_WORD_THOUSANDTHS(s, OPT_TL_X)); length = (SANE_Int) (_OPT_VAL_WORD_THOUSANDTHS(s, OPT_BR_Y) - _OPT_VAL_WORD_THOUSANDTHS(s, OPT_TL_Y)); _lto4b(width, set_window_cmd.window.windowwidth); _lto4b(length, set_window_cmd.window.windowlength); /* brightness (1-255) 0 is default, aka 128. Ignored with ACE scanners */ set_window_cmd.window.brightness = _OPT_VAL_WORD(s, OPT_BRIGHTNESS); /* threshold (1-255) 0 is default, aka 128. Ignored with ACE scanners */ set_window_cmd.window.threshold = _OPT_VAL_WORD(s, OPT_THRESHOLD); /*!!! contrast (not used) */ /*!!! set_window_cmd.window.contrast = _OPT_VAL_WORD(s, OPT_CONTRAST); */ /* imagecomposition 0x00 lineart, 0x01 dithered/halftone, 0x02 grayscale*/ set_window_cmd.window.imagecomposition = get_scan_mode_id(_OPT_VAL_STRING(s, OPT_SCAN_MODE)); set_window_cmd.window.bitsperpixel = 0x01; /*!!! halftone code (not used) */ /*!!! halftone id (not used) */ set_window_cmd.window.paddingtype = 0x03; /* truncate byte */ if (_OPT_VAL_WORD(s, OPT_NEGATIVE) == SANE_TRUE) { /* reverse image format (valid when bitsperpixel=1) * 0x00 normal, 0x01 reversed. This is bit 7 of paddingtype. */ set_window_cmd.window.paddingtype |= 0x80; } set_window_cmd.window.bitordering[0] = 0x00; /* we must always sent plain gray data in preview mode */ format = (_OPT_VAL_WORD(s, OPT_PREVIEW)) ? BH_COMP_NONE : get_compression_id(_OPT_VAL_STRING(s, OPT_COMPRESSION)); switch (format) { case BH_COMP_G31D: set_window_cmd.window.compressiontype = 0x01; set_window_cmd.window.compressionarg = 0x00; set_window_cmd.window.bitordering[1] = 0x01; /* Bit ordering LSB */ break; case BH_COMP_G32D: set_window_cmd.window.compressiontype = 0x02; set_window_cmd.window.compressionarg = 0x04; set_window_cmd.window.bitordering[1] = 0x01; /* Bit ordering LSB */ break; case BH_COMP_G42D: set_window_cmd.window.compressiontype = 0x03; set_window_cmd.window.compressionarg = 0x00; set_window_cmd.window.bitordering[1] = 0x01; /* Bit ordering LSB */ break; case BH_COMP_NONE: default: set_window_cmd.window.compressiontype = 0x00; set_window_cmd.window.compressionarg = 0x00; set_window_cmd.window.bitordering[1] = 0x00; /* n/a */ break; } /* rotation and deskew settings, if autoborder is turned on */ if(set_window_cmd.window.autoborder){ /*--- setting byte 46 of the window descriptor block only works with autoborder */ rotation = get_rotation_id(_OPT_VAL_STRING(s, OPT_ROTATION)); if (_OPT_VAL_WORD(s, OPT_DESKEW) == SANE_TRUE) deskew = BH_DESKEW_ENABLE; else deskew = BH_DESKEW_DISABLE; set_window_cmd.window.border_rotation = ( rotation | deskew ); /*--- deskew assumes autoborder */ } /* remote - 0x00 ACE set in window; 0x01 ACE set by control panel */ set_window_cmd.window.remote = _OPT_VAL_WORD(s, OPT_CONTROL_PANEL); if (set_window_cmd.window.remote == 0x00) { /* acefunction (ignored on non-ACE scanners) */ set_window_cmd.window.acefunction = _OPT_VAL_WORD(s, OPT_ACE_FUNCTION); /* acesensitivity (ignored on non-ACE scanners) */ set_window_cmd.window.acesensitivity = _OPT_VAL_WORD(s, OPT_ACE_SENSITIVITY); } set_window_cmd.window.batchmode = batchmode; /* fill in the section descriptor blocks */ for (i = 0; i < s->num_sections; i++) { BH_SectionBlock *b; b = &set_window_cmd.window.sectionblock[i]; _lto4b(s->sections[i].left, b->ul_x); _lto4b(s->sections[i].top, b->ul_y); _lto4b(s->sections[i].width, b->width); _lto4b(s->sections[i].length, b->length); b->compressiontype = s->sections[i].compressiontype; b->compressionarg = s->sections[i].compressionarg; } status = sanei_scsi_cmd (s->fd, &set_window_cmd, sizeof (set_window_cmd), 0, 0); DBG (5, "sanei_scsi_cmd executed, status=%d\n", status ); if (status != SANE_STATUS_GOOD) return status; /* set to points for reading */ s->bmu = BH_UNIT_POINT; s->mud = 1; status = mode_select_measurement(s); return status; } static SANE_Status get_window (BH_Scanner *s, SANE_Int *w, SANE_Int *h, SANE_Bool backpage) { SANE_Byte cmd[10]; static struct { SANE_Byte hdr[8]; struct window_data window; } get_window_data; SANE_Status status; SANE_Int x, y, i = 0, get_window_delay = 1; SANE_Bool autoborder; size_t len; DBG (3, "get_window called\n"); autoborder = _OPT_VAL_WORD(s, OPT_AUTOBORDER) == 1; while (1) { i++; memset (&cmd, 0, sizeof (cmd)); memset (&get_window_data, 0, sizeof (get_window_data)); cmd[0] = BH_SCSI_GET_WINDOW; _lto3b(sizeof(get_window_data), &cmd[6]); _lto2b(256, &get_window_data.hdr[6]); get_window_data.window.windowid = (backpage == SANE_TRUE) ? 1 : 0; len = sizeof(get_window_data); status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), &get_window_data, &len); if (status == SANE_STATUS_GOOD) { x =_4btol(get_window_data.window.ulx); y =_4btol(get_window_data.window.uly); *w =_4btol(get_window_data.window.windowwidth); *h =_4btol(get_window_data.window.windowlength); if (autoborder) { /* we try repeatedly until we get the autoborder bit set */ if (get_window_data.window.autoborder != 1 && i < BH_AUTOBORDER_TRIES) { DBG (5, "waiting %d second[s], try: %d\n",get_window_delay,i); sleep(get_window_delay); /*--- page 4-5 of B&H Copiscan 8000 ESC OEM Tech Manual */ /*--- requires at least 50ms wait between each GET WINDOW command */ /*--- experience shows that this can take 3 to 4 seconds */ continue; } if (get_window_data.window.autoborder != 1) { DBG(1, "Automatic Border Detection not done within %d tries\n", BH_AUTOBORDER_TRIES); status = SANE_STATUS_IO_ERROR; } DBG (0, "page dimension: wide:%d high:%d \n",*w,*h); } DBG (3, "*** Window size: %dx%d+%d+%d\n", *w, *h, x, y); DBG (5, "*** get_window found autoborder=%02xh\n", get_window_data.window.autoborder); DBG (5, "*** get_window found border_rotation=%02xh\n", get_window_data.window.border_rotation); } /* we are 'outta here' */ break; } return status; } static SANE_Status get_parameters (SANE_Handle handle, SANE_Parameters *params) { BH_Scanner *s = handle; SANE_Int width, length, res, comp; double br_x, tl_x, br_y, tl_y; SANE_Frame format; DBG(3, "get_parameters called\n"); memset (&s->params, 0, sizeof (s->params)); res = _OPT_VAL_WORD(s, OPT_RESOLUTION); /* make best-effort guess at what parameters will look like once the scan starts. */ br_x = _OPT_VAL_WORD_THOUSANDTHS(s, OPT_BR_X); br_y = _OPT_VAL_WORD_THOUSANDTHS(s, OPT_BR_Y); tl_x = _OPT_VAL_WORD_THOUSANDTHS(s, OPT_TL_X); tl_y = _OPT_VAL_WORD_THOUSANDTHS(s, OPT_TL_Y); width = (br_x - tl_x + 1) * res / 1000.0; length = (br_y - tl_y + 1) * res / 1000.0; /* figure out the default image format for front/back pages */ comp = get_compression_id(_OPT_VAL_STRING(s, OPT_COMPRESSION)); switch (comp) { case BH_COMP_G31D: format = SANE_FRAME_G31D; break; case BH_COMP_G32D: format = SANE_FRAME_G32D; break; case BH_COMP_G42D: format = SANE_FRAME_G42D; break; case BH_COMP_NONE: default: format = SANE_FRAME_GRAY; break; } if (s->scanning) { SANE_Int w, l, status; SANE_Byte itemtype; itemtype = s->readlist[s->readptr]; /* update parameters based on the current item */ status = SANE_STATUS_GOOD; if (itemtype == BH_SCSI_READ_TYPE_FRONT) { DBG (3, "get_parameters: sending GET WINDOW (front)\n"); status = get_window (s, &w, &l, SANE_FALSE); if (status == SANE_STATUS_GOOD) { width = w; length = l; } } else if (itemtype == BH_SCSI_READ_TYPE_BACK) { DBG (3, "get_parameters: sending GET WINDOW (back)\n"); status = get_window (s, &w, &l, SANE_TRUE); if (status == SANE_STATUS_GOOD) { width = w; length = l; } } else if (itemtype == BH_SCSI_READ_TYPE_FRONT_ICON || itemtype == BH_SCSI_READ_TYPE_BACK_ICON) { /* the icon is never compressed */ format = SANE_FRAME_GRAY; width = s->iconwidth; length = s->iconlength; } else if (itemtype > BH_SCSI_READ_TYPE_FRONT && itemtype <= (BH_SCSI_READ_TYPE_FRONT + NUM_SECTIONS)) { /* a front section */ SANE_Int sectnum = itemtype - BH_SCSI_READ_TYPE_FRONT; format = s->sections[sectnum - 1].format; /* convert from thousandths to pixels */ width = s->sections[sectnum - 1].width * res / 1000.0; length = s->sections[sectnum - 1].length * res / 1000.0; } else if (itemtype > BH_SCSI_READ_TYPE_BACK && itemtype <= (BH_SCSI_READ_TYPE_BACK + NUM_SECTIONS)) { /* a back section */ SANE_Int sectnum = itemtype - BH_SCSI_READ_TYPE_BACK; format = s->sections[sectnum - 1].format; /* convert from thousandths to pixels */ width = s->sections[sectnum - 1].width * res / 1000.0; length = s->sections[sectnum - 1].length * res / 1000.0; } else if ( (itemtype >= BH_SCSI_READ_TYPE_BACK_BARCODE && itemtype <= (BH_SCSI_READ_TYPE_BACK_BARCODE + NUM_SECTIONS)) || (itemtype >= BH_SCSI_READ_TYPE_FRONT_BARCODE && itemtype <= (BH_SCSI_READ_TYPE_FRONT_BARCODE + NUM_SECTIONS)) ) { /* decoded barcode data */ format = SANE_FRAME_TEXT; width = 8; length = -1; } else if (itemtype == BH_SCSI_READ_TYPE_SENDBARFILE) { /* decoded barcode data file */ format = SANE_FRAME_TEXT; width = 8; length = -1; } else { format = SANE_FRAME_GRAY; width = 8; length = -1; DBG(1, "get_parameters: unrecognized read itemtype: %d\n", itemtype); } if (status != SANE_STATUS_GOOD) { DBG(1, "get_parameters: failed\n"); return status; } } if (res <= 0 || width <= 0) { DBG(1, "get_parameters:illegal parameters res=%d, width=%d, length=%d\n", res, width, length); return SANE_STATUS_INVAL; } /* we disable our compression/barcode formats in preview as well * as with the disable_optional_frames configuration option. NOTE: * we may still be delivering 'wierd' data and lying about it being _GRAY! */ if (format != SANE_FRAME_GRAY && (_OPT_VAL_WORD(s, OPT_PREVIEW) || disable_optional_frames)) { DBG(1, "get_parameters: warning: delivering %s data as gray", sane_strframe(format)); format = SANE_FRAME_GRAY; } s->params.format = format; s->params.depth = 1; s->params.last_frame = SANE_TRUE; s->params.pixels_per_line = width; s->params.lines = length; s->params.bytes_per_line = (s->params.pixels_per_line + 7) / 8; /* The Bell and Howell truncates to the byte */ s->params.pixels_per_line = s->params.bytes_per_line * 8; if (params) *params = s->params; DBG (1, "get_parameters: format=%d, pixels/line=%d, bytes/line=%d, " "lines=%d, dpi=%d\n", (int) s->params.format, s->params.pixels_per_line, s->params.bytes_per_line, s->params.lines, res); return SANE_STATUS_GOOD; } static SANE_Status section_parse(const char *val, BH_Section *sect, SANE_Int res, SANE_Int comp) { SANE_Status status = SANE_STATUS_INVAL; char buf[255+1], *x, *y, *w, *l, *f, *ep; const char *seps = "x+:"; double mm, fpixels; u_long pixels; DBG(3, "section_parse called\n"); /* a section option looks something like this: * x++: * Example: * 76.2x25.4+50.8+0:frontbar:back:front * the width, length, tl-x, and tl-y are in mm. * the function codes are one or more of: * front, back, frontbar, backbar, frontpatch, backpatch */ if (strlen(val) > sizeof(buf) - 1) { DBG(1, "section_parse: option string too long\n"); status = SANE_STATUS_INVAL; } else { do { strcpy(buf, val); x = y = w = l = f = NULL; w = strtok(buf, seps); if (w) l = strtok(NULL, seps); if (l) x = strtok(NULL, seps); if (x) y = strtok(NULL, seps); if (y) f = strtok(NULL, seps); if (!x || !y || !w || !l) break; mm = strtod(x, &ep); if (*ep != '\0' || errno == ERANGE || mm < 0.0) break; sect->left = mm * 1000.0 / MM_PER_INCH; mm = strtod(y, &ep); if (*ep != '\0' || errno == ERANGE || mm < 0.0) break; sect->top = mm * 1000.0 / MM_PER_INCH; mm = strtod(w, &ep); if (*ep != '\0' || errno == ERANGE || mm < 0.0) break; sect->width = mm * 1000.0 / MM_PER_INCH; /* the window width must be truncated to 16 bit points */ fpixels = sect->width * res / 1000.0; pixels = fpixels / 16; sect->width = pixels * 16 * 1000 / res; mm = strtod(l, &ep); if (*ep != '\0' || errno == ERANGE || mm < 0.0) break; sect->length = mm * 1000.0 / MM_PER_INCH; status = SANE_STATUS_GOOD; while (f) { /* parse the function modifiers and set flags */ if (strcmp(f, "front") == 0) sect->flags |= BH_SECTION_FRONT_IMAGE; else if (strcmp(f, "frontbar") == 0) sect->flags |= BH_SECTION_FRONT_BAR; else if (strcmp(f, "frontpatch") == 0) sect->flags |= BH_SECTION_FRONT_PATCH; else if (strcmp(f, "back") == 0) sect->flags |= BH_SECTION_BACK_IMAGE; else if (strcmp(f, "backbar") == 0) sect->flags |= BH_SECTION_BACK_BAR; else if (strcmp(f, "backpatch") == 0) sect->flags |= BH_SECTION_BACK_PATCH; else if (strcmp(f, "g42d") == 0) comp = BH_COMP_G42D; else if (strcmp(f, "g32d") == 0) comp = BH_COMP_G32D; else if (strcmp(f, "g31d") == 0) comp = BH_COMP_G31D; else if (strcmp(f, "none") == 0) comp = BH_COMP_NONE; else DBG(1, "section_parse: ignoring unrecognized function " "code '%s'\n", f); f = strtok(NULL, seps); } switch (comp) { case BH_COMP_G31D: sect->compressiontype = 0x01; sect->compressionarg = 0x00; sect->format = SANE_FRAME_G31D; break; case BH_COMP_G32D: sect->compressiontype = 0x02; sect->compressionarg = 0x04; sect->format = SANE_FRAME_G32D; break; case BH_COMP_G42D: sect->compressiontype = 0x03; sect->compressionarg = 0x00; sect->format = SANE_FRAME_G42D; break; case BH_COMP_NONE: default: sect->compressiontype = 0x00; sect->compressionarg = 0x00; sect->format = SANE_FRAME_GRAY; break; } DBG(3, "section_parse: converted '%s' (mm) to " "%ldx%ld+%ld+%ld (thousandths) " "flags=%02x compression=[%d,%d] frame=%s\n", val, sect->width, sect->length, sect->left, sect->top, sect->flags, sect->compressiontype, sect->compressionarg, sane_strframe(sect->format)); } while (0); /* perform 'loop' once */ } return status; } static SANE_Status setup_sections (BH_Scanner *s, const char *val) { SANE_Status status = SANE_STATUS_GOOD; SANE_Int sectnum = 0; char buf[255+1], *section; DBG(3, "setup_sections called\n"); memset(s->sections, '\0', sizeof(s->sections)); if (strlen(val) > sizeof(buf) - 1) { DBG(1, "setup_sections: option string too long\n"); status = SANE_STATUS_INVAL; } else { strcpy(buf, val); section = strtok(buf, ","); while (section != NULL && sectnum < NUM_SECTIONS) { if (!allblank(section)) { SANE_Int res = _OPT_VAL_WORD(s, OPT_RESOLUTION); SANE_Int format = get_compression_id(_OPT_VAL_STRING(s, OPT_COMPRESSION)); status = section_parse(section, &s->sections[sectnum], res, format); if (status != SANE_STATUS_GOOD) { DBG(1, "setup_sections: error parsing section `%s'\n", section); break; } sectnum++; } section += strlen(section) + 1; if (section > buf + strlen(val)) break; section = strtok(section, ","); } } s->num_sections = sectnum; return status; } static SANE_Status start_setup (BH_Scanner *s) { SANE_Status status; SANE_Bool duplex; SANE_Int i, imagecnt; SANE_Byte batchmode; DBG(3, "start_setup called\n"); duplex = _OPT_VAL_WORD(s, OPT_DUPLEX); /* get the _SECTION option, parse it and fill in the sections */ status = setup_sections(s, _OPT_VAL_STRING(s, OPT_SECTION)); if (status != SANE_STATUS_GOOD) { DBG(1, "start_setup: setup_sections failed: %s\n", sane_strstatus(status)); return status; } /* see whether we'll be decoding barcodes and * set the barcodes flag appropriately */ if (s->search_bars[0] == 0) { s->barcodes = SANE_FALSE; } else { s->barcodes = SANE_TRUE; } /* see whether we'll be handling icons (thumbnails) * set the icons flag appropriately */ if (_OPT_VAL_WORD(s, OPT_ICON_WIDTH) >= 8 && _OPT_VAL_WORD(s, OPT_ICON_LENGTH) >= 8) { s->icons = SANE_TRUE; } else { s->icons = SANE_FALSE; } /* calculate a new readlist for this 'batch' */ s->readptr = s->readcnt = 0; /* always read the front image */ s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_FRONT; /* read back page only if duplex is true */ if (duplex == SANE_TRUE) { s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_BACK; } /* add image section reads to the readlist */ for (i = 0; i < s->num_sections; i++) { SANE_Word flags = s->sections[i].flags; if (flags & BH_SECTION_FRONT_IMAGE) s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_FRONT + i + 1; if (flags & BH_SECTION_BACK_IMAGE) s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_BACK + i + 1; } /* icons (thumbnails) */ if (s->icons) { s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_FRONT_ICON; /* read back icon only if duplex is true */ if (duplex == SANE_TRUE) { s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_BACK_ICON; } } /* NOTE: It is important that all of the image data comes before * the barcode/patchcode data. */ /* barcodes */ imagecnt = s->readcnt; if (s->barcodes) { if (s->num_sections == 0) { /* we only decode the entire page(s) if there are no * sections defined */ s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_FRONT_BARCODE; /* read back barcode only if duplex is true */ if (duplex == SANE_TRUE) { s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_BACK_BARCODE; } } else { /* add barcode section reads to the readlist */ for (i = 0; i < s->num_sections; i++) { SANE_Word flags = s->sections[i].flags; if (flags & BH_SECTION_FRONT_BAR) s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_FRONT_BARCODE + i + 1; if (flags & BH_SECTION_BACK_BAR) s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_BACK_BARCODE + i + 1; } } } /* patchcodes */ if (s->patchcodes) { if (s->num_sections == 0) { /* we only decode the entire page(s) if there are no * sections defined */ s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_FRONT_PATCHCODE; /* read back patchcode only if duplex is true */ if (duplex == SANE_TRUE) { s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_BACK_PATCHCODE; } } else { /* add patchcode section reads to the readlist */ for (i = 0; i < s->num_sections; i++) { SANE_Word flags = s->sections[i].flags; if (flags & BH_SECTION_FRONT_PATCH) s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_FRONT_PATCHCODE + i + 1; if (flags & BH_SECTION_BACK_PATCH) s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_BACK_PATCHCODE + i + 1; } } } /* add the special item to the read list which transfers the barcode * file that's built as a result of processing barcode and patchcode * readitems. NOTE: this one must be last! */ if (s->readcnt > imagecnt) { s->readlist[s->readcnt++] = BH_SCSI_READ_TYPE_SENDBARFILE; } if (_OPT_VAL_WORD(s, OPT_BATCH) == SANE_TRUE) { /* if batchmode is enabled, then call set_window to * abort the batch (even though there might not (and probably * isn't) a batch in progress). This avoids a batch start error * in the case where a previous batch was not aborted. */ DBG(5, "start_setup: calling set_window to abort batch\n"); set_window(s, BH_BATCH_ABORT); batchmode = BH_BATCH_ENABLE; } else { batchmode = BH_BATCH_DISABLE; } DBG(5, "start_setup: duplex=%s, barcodes=%s, patchcodes=%s, " "icons=%s, batch=%s\n", (duplex == SANE_TRUE) ? "yes" : "no", (s->barcodes == SANE_TRUE) ? "yes" : "no", (s->patchcodes == SANE_TRUE) ? "yes" : "no", (s->icons == SANE_TRUE) ? "yes" : "no", (batchmode == BH_BATCH_ENABLE) ? "yes" : "no"); DBG(5, "start_setup: sections=%d\n", s->num_sections); for (i = 0; i < s->num_sections; i++) { DBG(5, "start_setup: " "[%d] %lux%lu+%lu+%lu flags=%02x compression=[%d,%d]\n", i+1, s->sections[i].width, s->sections[i].length, s->sections[i].left, s->sections[i].top, s->sections[i].flags, s->sections[i].compressiontype, s->sections[i].compressionarg); } DBG(5, "start_setup: read list length=%d\n", s->readcnt); for (i = 0; i < s->readcnt; i++) { DBG(5, "start_setup: [%d] %s\n", i+1, print_read_type(s->readlist[i])); } DBG(5, "start_setup: sending SET WINDOW\n"); status = set_window(s, batchmode); if (status != SANE_STATUS_GOOD) { DBG(1, "start_setup: SET WINDOW failed: %s\n", sane_strstatus(status)); return status; } DBG(5, "start_setup: sending mode_select_timeout\n"); status = mode_select_timeout(s); if (status != SANE_STATUS_GOOD) { DBG(1, "start_setup: mode_select_timeout failed: %s\n", sane_strstatus(status)); return status; } if (s->icons == SANE_TRUE) { DBG(5, "start_setup: sending mode_select_icon\n"); status = mode_select_icon(s); if (status != SANE_STATUS_GOOD) { DBG(1, "start_setup: mode_select_icon failed: %s\n", sane_strstatus(status)); return status; } } if (s->barcodes == SANE_TRUE) { DBG(5, "start_setup: sending mode_select_barcode_priority\n"); status = mode_select_barcode_priority(s); if (status != SANE_STATUS_GOOD) { DBG(1, "start_setup: mode_select_barcode_priority failed: %s\n", sane_strstatus(status)); return status; } DBG(5, "start_setup: sending mode_select_barcode_param1\n"); status = mode_select_barcode_param1(s); if (status != SANE_STATUS_GOOD) { DBG(1, "start_setup: mode_select_barcode_param1 failed: %s\n", sane_strstatus(status)); return status; } DBG(5, "start_setup: sending mode_select_barcode_param2\n"); status = mode_select_barcode_param2(s); if (status != SANE_STATUS_GOOD) { DBG(1, "start_setup: mode_select_barcode_param2 failed: %s\n", sane_strstatus(status)); return status; } DBG(5, "start_setup: sending mode_select_barcode_param3\n"); status = mode_select_barcode_param3(s); if (status != SANE_STATUS_GOOD) { DBG(1, "start_setup: mode_select_barcode_param3 failed: %s\n", sane_strstatus(status)); return status; } } return status; } static SANE_Status start_scan (BH_Scanner *s) { static SANE_Byte cmd[8]; SANE_Status status = SANE_STATUS_GOOD; SANE_Bool check_adf, duplex; DBG (3, "start_scan called\n"); /* SANE front ends will call this function between 'FRAMES'. * A single scan on the B&H may result in up to 56 different * things to read (20 are SANE image frames, 36 are non-SANE * data - decoded bar/patch codes). */ if (s->readcnt > 1 && s->scanning == SANE_TRUE) { DBG(3, "start_scan: any more items in the readlist?\n"); /* we've been reading data from this scan, so we just * move on to the next item in the readlist without * starting a new scan. */ s->readptr++; if (s->readptr < s->readcnt) { SANE_Byte itemtype; for (; s->readptr < s->readcnt; s->readptr++) { itemtype = s->readlist[s->readptr]; DBG(3, "start_scan: advance readlist(%d, %d)\n", s->readptr, (int) itemtype); /* 'dance' by the non-SANE data streams * like bar/patch code data */ if (!BH_HAS_IMAGE_DATA(itemtype)) { int fd; FILE *fp; strncpy(s->barfname, "/tmp/bhXXXXXX", sizeof(s->barfname)); s->barfname[sizeof(s->barfname)-1] = '\0'; fd = mkstemp(s->barfname); if (fd !=-1 && (fp = fdopen(fd, "w")) != NULL) { fprintf(fp, "\n"); for (; s->readptr < s->readcnt && status == SANE_STATUS_GOOD; s->readptr++) { if (s->readlist[s->readptr] == BH_SCSI_READ_TYPE_SENDBARFILE) { break; } status = read_barcode_data(s, fp); if (status != SANE_STATUS_GOOD) break; } fprintf(fp, "\n"); /* close file; re-open for read(setting s->barfd) */ fclose(fp); if ((s->barf = fopen(s->barfname, "r")) == NULL) { DBG(1, "sane_start: error opening barfile `%s'\n", s->barfname); status = SANE_STATUS_IO_ERROR; } } else { DBG(1, "sane_start: error opening barfile `%s'\n", s->barfname); if (fd !=-1) { close(fd); unlink(s->barfname); } status = SANE_STATUS_IO_ERROR; } } else if (itemtype == BH_SCSI_READ_TYPE_FRONT_ICON || itemtype == BH_SCSI_READ_TYPE_BACK_ICON) { /* read the icon header setting the iconwidth and iconlength * to the actual values so get_parameters will have them. * Subsequent calls to sane_read will get pure image data * since the icon header has been consumed. */ status = read_icon_data(s); } if (status == SANE_STATUS_GOOD) { /* update our parameters to reflect the new item */ status = get_parameters (s, 0); } if (status != SANE_STATUS_GOOD) s->scanning = SANE_FALSE; return status; } /* if we reach here, we're finished with the readlist and * will drop through to start a new scan */ } } s->readptr = 0; check_adf = _OPT_VAL_WORD(s, OPT_CHECK_ADF); duplex = _OPT_VAL_WORD(s, OPT_DUPLEX); memset (&cmd, 0, sizeof (cmd)); cmd[0] = BH_SCSI_START_SCAN; cmd[4] = (duplex == SANE_TRUE) ? 2 : 1; cmd[6] = 0; cmd[7] = 1; if (check_adf) { status = object_position(s); if (status != SANE_STATUS_GOOD) { DBG(3, "object_position: returned %d\n", status); return status; } } status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), 0, 0); if (status == SANE_STATUS_GOOD) { s->scanning = SANE_TRUE; /* update our parameters, * now that we're scanning we'll do a GET_WINDOW */ status = get_parameters (s, 0); if (status != SANE_STATUS_GOOD) { s->scanning = SANE_FALSE; } } return status; } /* a sensible sense handler, courtesy of Franck; arg is a pointer to the associated BH_Scanner structure */ static SANE_Status sense_handler (int scsi_fd, u_char *result, void *arg) { BH_Scanner *s = (BH_Scanner *) arg; u_char sense, asc, ascq, EOM, ILI, ErrorCode, ValidData; u_long InvalidBytes; char *sense_str = "", *as_str = ""; SANE_Int i; SANE_Status status = SANE_STATUS_INVAL; SANE_Char print_sense[(16 * 3) + 1]; (void) scsi_fd; /* get rid of compiler warning */ ErrorCode = result[0] & 0x7F; ValidData = (result[0] & 0x80) != 0; sense = result[2] & 0x0f; /* Key */ asc = result[12]; /* Code */ ascq = result[13]; /* Qual */ EOM = (result[2] & 0x40) != 0; /* End Of Media */ ILI = (result[2] & 0x20) != 0; /* Invalid Length Indicator */ InvalidBytes = ValidData ? _4btol(&result[3]) : 0; DBG(3, "sense_handler: result=%x, sense=%x, asc=%x, ascq=%x\n", result[0], sense, asc, ascq); DBG(3, "sense_handler: ErrorCode %02x ValidData: %d " "EOM: %d ILI: %d InvalidBytes: %lu\n", ErrorCode, ValidData, EOM, ILI, InvalidBytes); memset(print_sense, '\0', sizeof(print_sense)); for (i = 0; i < 16; i++) { sprintf(print_sense + strlen(print_sense), "%02x ", result[i]); } DBG(5, "sense_handler: sense=%s\n", print_sense); if (ErrorCode != 0x70 && ErrorCode != 0x71) { DBG (3, "sense_handler: error code is invalid.\n"); return SANE_STATUS_IO_ERROR; /* error code is invalid */ } /* handle each sense key; * RSC supports 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x0B */ switch (sense) { case 0x00: /* no sense */ sense_str = "No sense."; status = SANE_STATUS_GOOD; if (ILI && asc == 0x00 && ascq == 0x05) { /* from read_data function */ as_str = "ILI bit is set."; if (s != NULL) { s->InvalidBytes = InvalidBytes; } status = SANE_STATUS_GOOD; } else if (EOM && asc == 0x00 && ascq == 0x02) { /* from adfStatus or startScan function */ as_str = "Out of paper in the hopper."; status = SANE_STATUS_NO_DOCS; } else if (EOM) { /* from adfStatus or startScan function */ as_str = "Out of paper in the hopper."; status = SANE_STATUS_NO_DOCS; } break; case 0x01: /* recovered error */ sense_str = "Recovered error."; status = SANE_STATUS_GOOD; break; case 0x02: /* not ready */ sense_str = "Not ready."; status = SANE_STATUS_DEVICE_BUSY; if (asc == 0x40 && ascq == 0x01) { as_str = "P.O.D. error: Scanner not found."; status = SANE_STATUS_INVAL; } else if (asc == 0x40 && ascq == 0x02) { as_str = "P.O.D. error: Scanner not ready(paper in transport)."; status = SANE_STATUS_DEVICE_BUSY; } else if (asc == 0x40 && ascq == 0x03) { as_str = "P.O.D. error: Unknown scanner."; status = SANE_STATUS_INVAL; } break; case 0x03: /* medium error */ sense_str = "Medium error."; status = SANE_STATUS_IO_ERROR; if (asc == 0x00 && ascq == 0x00) { as_str = "Scanner error: paper jam detected."; status = SANE_STATUS_JAMMED; } break; case 0x04: /* hardware error */ sense_str = "Hardware error."; status = SANE_STATUS_IO_ERROR; if (asc == 0x60 && ascq == 0x00) { as_str = "Scanner error: illumination lamps failure."; status = SANE_STATUS_IO_ERROR; } else if (asc == 0x80 && ascq == 0x03) { as_str = "Communication error between RSC and scanner."; status = SANE_STATUS_IO_ERROR; } else if (asc == 0x80 && ascq == 0x06) { as_str = "Scanner error: page detected but lamps are off."; status = SANE_STATUS_IO_ERROR; } else if (asc == 0x80 && ascq == 0x07) { as_str = "Scanner error: camera white level problem."; status = SANE_STATUS_IO_ERROR; } else if (asc == 0x80 && ascq == 0x08) { /* could be caught from start_scan or read_data */ /* stop button pressed */ as_str = "Scanner error: operator pressed the Stop key."; status = SANE_STATUS_NO_DOCS; } else if (asc == 0x80 && ascq == 0x12) { as_str = "Scanner error: transport motor failure."; status = SANE_STATUS_IO_ERROR; } else if (asc == 0x80 && ascq == 0x15) { as_str = "Scanner error: device / page sensor(s) bouncing."; status = SANE_STATUS_IO_ERROR; } else if (asc == 0x80 && ascq == 0x16) { as_str = "Scanner error: feeder is not attached."; status = SANE_STATUS_IO_ERROR; } else if (asc == 0x80 && ascq == 0x18) { as_str = "Scanner error: logic system general failure."; status = SANE_STATUS_IO_ERROR; } else if (asc == 0x80 && ascq == 0x34) { as_str = "Scanner error: no dual logic communication."; status = SANE_STATUS_IO_ERROR; } break; case 0x05: /* illegal request */ sense_str = "Illegal request."; status = SANE_STATUS_INVAL; if (asc == 0x1a && ascq == 0x00) { as_str = "Parameter list length error."; status = SANE_STATUS_INVAL; } else if (asc == 0x20 && ascq == 0x00) { as_str = "Invalid command operation code."; status = SANE_STATUS_INVAL; } else if (asc == 0x24 && ascq == 0x00) { /* caught from object_position (via reverse engineering) */ /* Not supported? */ as_str = "Invalid field in CDB."; status = SANE_STATUS_INVAL; } else if (asc == 0x25 && ascq == 0x00) { as_str = "Unsupported LUN."; status = SANE_STATUS_INVAL; } else if (asc == 0x26 && ascq == 0x00) { /* caught from mode_select (as well as others) */ /* Bar/Patch code detection support not installed */ /* See Appendix A, Section A.5 */ as_str = "Invalid field in parameter list."; status = SANE_STATUS_INVAL; } else if (asc == 0x2c && ascq == 0x00) { /* we were getting this in read_data during the time that the ADF was misbehaving. Hopefully we will not see it anymore. */ as_str = "Command out of sequence."; status = SANE_STATUS_INVAL; } else if (asc == 0x2c && ascq == 0x01) { as_str = "Too many windows defined."; status = SANE_STATUS_INVAL; } else if (asc == 0x2c && ascq == 0x02) { as_str = "Batch start error."; status = SANE_STATUS_INVAL; } else if (asc == 0x2c && ascq == 0x03) { as_str = "Batch abort error."; status = SANE_STATUS_INVAL; } else if (asc == 0x3d && ascq == 0x00) { as_str = "Invalid bits in IDENTIFY message."; status = SANE_STATUS_INVAL; } break; case 0x06: /* unit attention */ sense_str = "Unit attention."; status = SANE_STATUS_IO_ERROR; if (asc == 0x04 && ascq == 0x01) { as_str = "Reset detected, LUN is becoming ready."; status = SANE_STATUS_DEVICE_BUSY; } break; case 0x07: /* data protect */ sense_str = "Data protect."; status = SANE_STATUS_IO_ERROR; break; case 0x08: /* blank check */ sense_str = "Blank check."; status = SANE_STATUS_IO_ERROR; break; case 0x09: /* vendor specific */ sense_str = "Vendor specific."; status = SANE_STATUS_IO_ERROR; break; case 0x0A: /* copy aborted */ sense_str = "Copy aborted."; status = SANE_STATUS_IO_ERROR; break; case 0x0B: /* aborted command */ sense_str = "Aborted command."; status = SANE_STATUS_IO_ERROR; if (asc == 0x00 && ascq == 0x00) { as_str = "Aborted command (unspecified error)."; status = SANE_STATUS_IO_ERROR; } else if (asc == 0x08 && ascq == 0x01) { /* caught from start_scan */ /* manual feed timeout */ as_str = "SCSI Time-out, paper Time-out (SCAN command)."; status = SANE_STATUS_NO_DOCS; } else if (asc == 0x47 && ascq == 0x00) { as_str = "SCSI parity error."; status = SANE_STATUS_IO_ERROR; } else if (asc == 0x80 && ascq == 0x00) { as_str = "Aborted command due to memory error."; status = SANE_STATUS_IO_ERROR; } else if (asc == 0x80 && ascq == 0x01) { /* caught from read_data */ /* section border error; border is outside the main window */ /* See Appendix A, Section A.4 */ as_str = "Section Read error (out of border)."; status = SANE_STATUS_INVAL; } else if (asc == 0x80 && ascq == 0x02) { /* caught from read_data */ /* No code found; no barcode data is found */ /* See Appendix A, Section A.5 */ s->barcode_not_found = SANE_TRUE; as_str = "No Bar/Patch Code found."; status = SANE_STATUS_GOOD; } else if (asc == 0x80 && ascq == 0x03) { as_str = "Icon Read error (out of border)."; status = SANE_STATUS_INVAL; } break; case 0x0C: /* equal */ sense_str = "Equal."; status = SANE_STATUS_IO_ERROR; break; case 0x0D: /* volume overflow */ sense_str = "Volume overflow."; status = SANE_STATUS_IO_ERROR; break; case 0x0E: /* miscompare */ sense_str = "Miscompare."; status = SANE_STATUS_IO_ERROR; break; case 0x0F: /* reserved */ sense_str = "Reserved."; status = SANE_STATUS_IO_ERROR; break; default: sense_str = "Unhandled case."; status = SANE_STATUS_IO_ERROR; break; } DBG(3, "sense_handler: '%s' '%s' return:%d\n", sense_str, as_str, status); return status; } static SANE_Status init_options (BH_Scanner * s) { int i; DBG (3, "init_options called\n"); memset (s->opt, 0, sizeof (s->opt)); memset (s->val, 0, sizeof (s->val)); for (i = 0; i < NUM_OPTIONS; ++i) { s->opt[i].size = sizeof (SANE_Word); s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } 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; s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* "Scan Mode" group: */ s->opt[OPT_MODE_GROUP].name = ""; s->opt[OPT_MODE_GROUP].title = SANE_TITLE_SCAN_MODE_GROUP; s->opt[OPT_MODE_GROUP].desc = ""; s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MODE_GROUP].cap = 0; s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* Preview: */ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; s->opt[OPT_PREVIEW].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_PREVIEW].w = 0; /* Inquiry */ s->opt[OPT_INQUIRY].name = SANE_NAME_INQUIRY; s->opt[OPT_INQUIRY].title = SANE_TITLE_INQUIRY; s->opt[OPT_INQUIRY].desc = SANE_DESC_INQUIRY; s->opt[OPT_INQUIRY].type = SANE_TYPE_STRING; s->opt[OPT_INQUIRY].size = sizeof(inquiry_data); s->opt[OPT_INQUIRY].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_INQUIRY].s = strdup(inquiry_data); s->opt[OPT_INQUIRY].cap = SANE_CAP_SOFT_DETECT; /* scan mode */ s->opt[OPT_SCAN_MODE].name = SANE_NAME_SCAN_MODE; s->opt[OPT_SCAN_MODE].title = SANE_TITLE_SCAN_MODE; s->opt[OPT_SCAN_MODE].desc = SANE_DESC_SCAN_MODE; s->opt[OPT_SCAN_MODE].type = SANE_TYPE_STRING; s->opt[OPT_SCAN_MODE].size = max_string_size (scan_mode_list); s->opt[OPT_SCAN_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_SCAN_MODE].constraint.string_list = scan_mode_list; s->val[OPT_SCAN_MODE].s = strdup (scan_mode_list[0]); /* Standard resolutions */ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_RESOLUTION].constraint.word_list = s->hw->info.resStdList; s->val[OPT_RESOLUTION].w = s->hw->info.res_default; /* compression */ s->opt[OPT_COMPRESSION].name = SANE_NAME_COMPRESSION; s->opt[OPT_COMPRESSION].title = SANE_TITLE_COMPRESSION; s->opt[OPT_COMPRESSION].desc = SANE_DESC_COMPRESSION; s->opt[OPT_COMPRESSION].type = SANE_TYPE_STRING; s->opt[OPT_COMPRESSION].size = max_string_size (compression_list); s->opt[OPT_COMPRESSION].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_COMPRESSION].constraint.string_list = compression_list; s->val[OPT_COMPRESSION].s = strdup (compression_list[0]); if (s->hw->info.colorHalftone == SANE_FALSE) { s->opt[OPT_SCAN_MODE].size = max_string_size (scan_mode_min_list); s->opt[OPT_SCAN_MODE].constraint.string_list = scan_mode_min_list; } if (s->hw->info.comprG3_1D == SANE_FALSE || s->hw->info.comprG3_2D == SANE_FALSE || s->hw->info.comprG4 == SANE_FALSE) { s->opt[OPT_COMPRESSION].cap |= SANE_CAP_INACTIVE; } /* "Geometry" group: */ s->opt[OPT_GEOMETRY_GROUP].name = ""; s->opt[OPT_GEOMETRY_GROUP].title = SANE_TITLE_GEOMETRY_GROUP; s->opt[OPT_GEOMETRY_GROUP].desc = ""; s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_GEOMETRY_GROUP].cap = 0; s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* Autoborder: */ s->opt[OPT_AUTOBORDER].name = SANE_NAME_AUTOBORDER; s->opt[OPT_AUTOBORDER].title = SANE_TITLE_AUTOBORDER; s->opt[OPT_AUTOBORDER].desc = SANE_DESC_AUTOBORDER; s->opt[OPT_AUTOBORDER].type = SANE_TYPE_BOOL; s->opt[OPT_AUTOBORDER].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_AUTOBORDER].w = s->hw->info.autoborder_default; /* Paper Size */ s->opt[OPT_PAPER_SIZE].name = SANE_NAME_PAPER_SIZE; s->opt[OPT_PAPER_SIZE].title = SANE_TITLE_PAPER_SIZE; s->opt[OPT_PAPER_SIZE].desc = SANE_DESC_PAPER_SIZE; s->opt[OPT_PAPER_SIZE].type = SANE_TYPE_STRING; s->opt[OPT_PAPER_SIZE].size = max_string_size (paper_list); s->opt[OPT_PAPER_SIZE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_PAPER_SIZE].constraint.string_list = paper_list; s->val[OPT_PAPER_SIZE].s = strdup (paper_list[0]); /* rotation */ s->opt[OPT_ROTATION].name = SANE_NAME_ROTATION; s->opt[OPT_ROTATION].title = SANE_TITLE_ROTATION; s->opt[OPT_ROTATION].desc = SANE_DESC_ROTATION; s->opt[OPT_ROTATION].type = SANE_TYPE_STRING; s->opt[OPT_ROTATION].size = max_string_size (rotation_list); s->opt[OPT_ROTATION].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_ROTATION].constraint.string_list = rotation_list; s->val[OPT_ROTATION].s = strdup (rotation_list[0]); /* Deskew: */ s->opt[OPT_DESKEW].name = SANE_NAME_DESKEW; s->opt[OPT_DESKEW].title = SANE_TITLE_DESKEW; s->opt[OPT_DESKEW].desc = SANE_DESC_DESKEW; s->opt[OPT_DESKEW].type = SANE_TYPE_BOOL; s->opt[OPT_DESKEW].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_DESKEW].w = s->hw->info.deskew_default; /* top-left x */ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; s->opt[OPT_TL_X].unit = SANE_UNIT_MM; s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_X].constraint.range = &(s->hw->info.x_range); s->val[OPT_TL_X].w = SANE_FIX(0.0); /* top-left y */ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_Y].constraint.range = &(s->hw->info.y_range); s->val[OPT_TL_Y].w = SANE_FIX(0.0); /* bottom-right x */ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; s->opt[OPT_BR_X].unit = SANE_UNIT_MM; s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_X].constraint.range = &(s->hw->info.x_range); s->val[OPT_BR_X].w = s->hw->info.x_range.max; /* bottom-right y */ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_Y].constraint.range = &(s->hw->info.y_range); s->val[OPT_BR_Y].w = s->hw->info.y_range.max; if (s->hw->info.canBorderRecog == SANE_FALSE) { s->opt[OPT_AUTOBORDER].cap |= SANE_CAP_INACTIVE; } /* "Feeder" group: */ s->opt[OPT_FEEDER_GROUP].name = ""; s->opt[OPT_FEEDER_GROUP].title = SANE_TITLE_FEEDER_GROUP; s->opt[OPT_FEEDER_GROUP].desc = ""; s->opt[OPT_FEEDER_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_FEEDER_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_FEEDER_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* scan source */ s->opt[OPT_SCAN_SOURCE].name = SANE_NAME_SCAN_SOURCE; s->opt[OPT_SCAN_SOURCE].title = SANE_TITLE_SCAN_SOURCE; s->opt[OPT_SCAN_SOURCE].desc = SANE_DESC_SCAN_SOURCE; s->opt[OPT_SCAN_SOURCE].type = SANE_TYPE_STRING; s->opt[OPT_SCAN_SOURCE].size = max_string_size (scan_source_list); s->opt[OPT_SCAN_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_SCAN_SOURCE].constraint.string_list = scan_source_list; s->val[OPT_SCAN_SOURCE].s = strdup (scan_source_list[0]); /* Batch: */ s->opt[OPT_BATCH].name = SANE_NAME_BATCH; s->opt[OPT_BATCH].title = SANE_TITLE_BATCH; s->opt[OPT_BATCH].desc = SANE_DESC_BATCH; s->opt[OPT_BATCH].type = SANE_TYPE_BOOL; s->opt[OPT_BATCH].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_BATCH].w = s->hw->info.batch_default; /* Check ADF: */ s->opt[OPT_CHECK_ADF].name = SANE_NAME_CHECK_ADF; s->opt[OPT_CHECK_ADF].title = SANE_TITLE_CHECK_ADF; s->opt[OPT_CHECK_ADF].desc = SANE_DESC_CHECK_ADF; s->opt[OPT_CHECK_ADF].type = SANE_TYPE_BOOL; s->opt[OPT_CHECK_ADF].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_CHECK_ADF].w = s->hw->info.check_adf_default; /* Duplex: */ s->opt[OPT_DUPLEX].name = SANE_NAME_DUPLEX; s->opt[OPT_DUPLEX].title = SANE_TITLE_DUPLEX; s->opt[OPT_DUPLEX].desc = SANE_DESC_DUPLEX; s->opt[OPT_DUPLEX].type = SANE_TYPE_BOOL; s->opt[OPT_DUPLEX].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_DUPLEX].w = s->hw->info.duplex_default; /* timeout adf */ s->opt[OPT_TIMEOUT_ADF].name = SANE_NAME_TIMEOUT_ADF; s->opt[OPT_TIMEOUT_ADF].title = SANE_TITLE_TIMEOUT_ADF; s->opt[OPT_TIMEOUT_ADF].desc = SANE_DESC_TIMEOUT_ADF; s->opt[OPT_TIMEOUT_ADF].type = SANE_TYPE_INT; s->opt[OPT_TIMEOUT_ADF].unit = SANE_UNIT_NONE; s->opt[OPT_TIMEOUT_ADF].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TIMEOUT_ADF].constraint.range = &u8_range; s->val[OPT_TIMEOUT_ADF].w = s->hw->info.timeout_adf_default; /* timeout manual */ s->opt[OPT_TIMEOUT_MANUAL].name = SANE_NAME_TIMEOUT_MANUAL; s->opt[OPT_TIMEOUT_MANUAL].title = SANE_TITLE_TIMEOUT_MANUAL; s->opt[OPT_TIMEOUT_MANUAL].desc = SANE_DESC_TIMEOUT_MANUAL; s->opt[OPT_TIMEOUT_MANUAL].type = SANE_TYPE_INT; s->opt[OPT_TIMEOUT_MANUAL].unit = SANE_UNIT_NONE; s->opt[OPT_TIMEOUT_MANUAL].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TIMEOUT_MANUAL].constraint.range = &u8_range; s->val[OPT_TIMEOUT_MANUAL].w = s->hw->info.timeout_manual_default; if (s->hw->info.canCheckADF == SANE_FALSE) { s->opt[OPT_CHECK_ADF].cap |= SANE_CAP_INACTIVE; } if (s->hw->info.canDuplex == SANE_FALSE) { s->opt[OPT_DUPLEX].cap |= SANE_CAP_INACTIVE; } if (s->hw->info.canADF == SANE_FALSE) { s->opt[OPT_TIMEOUT_ADF].cap |= SANE_CAP_INACTIVE; } /* "Enhancement" group: */ s->opt[OPT_ENHANCEMENT_GROUP].name = ""; s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_TITLE_ENHANCEMENT_GROUP; s->opt[OPT_ENHANCEMENT_GROUP].desc = ""; s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* Control Panel: */ s->opt[OPT_CONTROL_PANEL].name = SANE_NAME_CONTROL_PANEL; s->opt[OPT_CONTROL_PANEL].title = SANE_TITLE_CONTROL_PANEL; s->opt[OPT_CONTROL_PANEL].desc = SANE_DESC_CONTROL_PANEL; s->opt[OPT_CONTROL_PANEL].type = SANE_TYPE_BOOL; s->opt[OPT_CONTROL_PANEL].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_CONTROL_PANEL].w = s->hw->info.control_panel_default; /* Ace_Function */ s->opt[OPT_ACE_FUNCTION].name = SANE_NAME_ACE_FUNCTION; s->opt[OPT_ACE_FUNCTION].title = SANE_TITLE_ACE_FUNCTION; s->opt[OPT_ACE_FUNCTION].desc = SANE_DESC_ACE_FUNCTION; s->opt[OPT_ACE_FUNCTION].type = SANE_TYPE_INT; s->opt[OPT_ACE_FUNCTION].unit = SANE_UNIT_NONE; s->opt[OPT_ACE_FUNCTION].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_ACE_FUNCTION].constraint.range = &ace_function_range; s->val[OPT_ACE_FUNCTION].w = 0; /* Ace_Sensitivity */ s->opt[OPT_ACE_SENSITIVITY].name = SANE_NAME_ACE_SENSITIVITY; s->opt[OPT_ACE_SENSITIVITY].title = SANE_TITLE_ACE_SENSITIVITY; s->opt[OPT_ACE_SENSITIVITY].desc = SANE_DESC_ACE_SENSITIVITY; s->opt[OPT_ACE_SENSITIVITY].type = SANE_TYPE_INT; s->opt[OPT_ACE_SENSITIVITY].unit = SANE_UNIT_NONE; s->opt[OPT_ACE_SENSITIVITY].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_ACE_SENSITIVITY].constraint.range = &ace_sensitivity_range; s->val[OPT_ACE_SENSITIVITY].w = 4; /* Brightness */ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT; s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE; s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BRIGHTNESS].constraint.range = &u8_range; s->val[OPT_BRIGHTNESS].w = 0; /* 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_INT; s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE; s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_THRESHOLD].constraint.range = &u8_range; s->val[OPT_THRESHOLD].w = 0; /* Contrast */ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST; s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST; s->opt[OPT_CONTRAST].type = SANE_TYPE_INT; s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE; s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CONTRAST].constraint.range = &u8_range; s->val[OPT_CONTRAST].w = 0; /* Negative: */ s->opt[OPT_NEGATIVE].name = SANE_NAME_NEGATIVE; s->opt[OPT_NEGATIVE].title = SANE_TITLE_NEGATIVE; s->opt[OPT_NEGATIVE].desc = SANE_DESC_NEGATIVE; s->opt[OPT_NEGATIVE].type = SANE_TYPE_BOOL; s->opt[OPT_NEGATIVE].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_NEGATIVE].w = SANE_FALSE; /* Contrast is not used in any case; why did we add it? */ s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; if (s->hw->info.control_panel_default == SANE_TRUE) { s->opt[OPT_ACE_FUNCTION].cap |= SANE_CAP_INACTIVE; s->opt[OPT_ACE_SENSITIVITY].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; } else if (s->hw->info.canACE == SANE_FALSE) { s->opt[OPT_ACE_FUNCTION].cap |= SANE_CAP_INACTIVE; s->opt[OPT_ACE_SENSITIVITY].cap |= SANE_CAP_INACTIVE; } else { s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; } /* "ICON" group: */ s->opt[OPT_ICON_GROUP].name = ""; s->opt[OPT_ICON_GROUP].title = SANE_TITLE_ICON_GROUP; s->opt[OPT_ICON_GROUP].desc = ""; s->opt[OPT_ICON_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_ICON_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_ICON_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* Icon_Width */ s->opt[OPT_ICON_WIDTH].name = SANE_NAME_ICON_WIDTH; s->opt[OPT_ICON_WIDTH].title = SANE_TITLE_ICON_WIDTH; s->opt[OPT_ICON_WIDTH].desc = SANE_DESC_ICON_WIDTH; s->opt[OPT_ICON_WIDTH].type = SANE_TYPE_INT; s->opt[OPT_ICON_WIDTH].unit = SANE_UNIT_PIXEL; s->opt[OPT_ICON_WIDTH].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_ICON_WIDTH].constraint.range = &icon_range; s->val[OPT_ICON_WIDTH].w = 0; /* Icon_Length */ s->opt[OPT_ICON_LENGTH].name = SANE_NAME_ICON_LENGTH; s->opt[OPT_ICON_LENGTH].title = SANE_TITLE_ICON_LENGTH; s->opt[OPT_ICON_LENGTH].desc = SANE_DESC_ICON_LENGTH; s->opt[OPT_ICON_LENGTH].type = SANE_TYPE_INT; s->opt[OPT_ICON_LENGTH].unit = SANE_UNIT_PIXEL; s->opt[OPT_ICON_LENGTH].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_ICON_LENGTH].constraint.range = &icon_range; s->val[OPT_ICON_LENGTH].w = 0; if (s->hw->info.canIcon == SANE_FALSE) { s->opt[OPT_ICON_GROUP].cap |= SANE_CAP_INACTIVE; s->opt[OPT_ICON_WIDTH].cap |= SANE_CAP_INACTIVE; s->opt[OPT_ICON_LENGTH].cap |= SANE_CAP_INACTIVE; } /* "Barcode" group: */ s->opt[OPT_BARCODE_GROUP].name = ""; s->opt[OPT_BARCODE_GROUP].title = SANE_TITLE_BARCODE_GROUP; s->opt[OPT_BARCODE_GROUP].desc = ""; s->opt[OPT_BARCODE_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_BARCODE_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_BARCODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* Add to barcode search priority. */ s->opt[OPT_BARCODE_SEARCH_BAR].name = SANE_NAME_BARCODE_SEARCH_BAR; s->opt[OPT_BARCODE_SEARCH_BAR].title = SANE_TITLE_BARCODE_SEARCH_BAR; s->opt[OPT_BARCODE_SEARCH_BAR].desc = SANE_DESC_BARCODE_SEARCH_BAR; s->opt[OPT_BARCODE_SEARCH_BAR].type = SANE_TYPE_STRING; s->opt[OPT_BARCODE_SEARCH_BAR].unit = SANE_UNIT_NONE; s->opt[OPT_BARCODE_SEARCH_BAR].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_BARCODE_SEARCH_BAR].constraint.string_list = barcode_search_bar_list; s->opt[OPT_BARCODE_SEARCH_BAR].size = max_string_size (barcode_search_bar_list); s->val[OPT_BARCODE_SEARCH_BAR].s = strdup (barcode_search_bar_list[0]); /* Barcode search count (1-7, default 1). */ s->opt[OPT_BARCODE_SEARCH_COUNT].name = SANE_NAME_BARCODE_SEARCH_COUNT; s->opt[OPT_BARCODE_SEARCH_COUNT].title = SANE_TITLE_BARCODE_SEARCH_COUNT; s->opt[OPT_BARCODE_SEARCH_COUNT].desc = SANE_DESC_BARCODE_SEARCH_COUNT; s->opt[OPT_BARCODE_SEARCH_COUNT].type = SANE_TYPE_INT; s->opt[OPT_BARCODE_SEARCH_COUNT].unit = SANE_UNIT_NONE; s->opt[OPT_BARCODE_SEARCH_COUNT].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BARCODE_SEARCH_COUNT].constraint.range = &barcode_search_count_range; s->val[OPT_BARCODE_SEARCH_COUNT].w = 3; /* Barcode search mode. horiz-vert, horizontal, vertical, vert-horiz */ s->opt[OPT_BARCODE_SEARCH_MODE].name = SANE_NAME_BARCODE_SEARCH_MODE; s->opt[OPT_BARCODE_SEARCH_MODE].title = SANE_TITLE_BARCODE_SEARCH_MODE; s->opt[OPT_BARCODE_SEARCH_MODE].desc = SANE_DESC_BARCODE_SEARCH_MODE; s->opt[OPT_BARCODE_SEARCH_MODE].type = SANE_TYPE_STRING; s->opt[OPT_BARCODE_SEARCH_MODE].size = max_string_size (barcode_search_mode_list); s->opt[OPT_BARCODE_SEARCH_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_BARCODE_SEARCH_MODE].constraint.string_list = barcode_search_mode_list; s->val[OPT_BARCODE_SEARCH_MODE].s = strdup(barcode_search_mode_list[0]); /* Patch code min height (def=5mm) */ s->opt[OPT_BARCODE_HMIN].name = SANE_NAME_BARCODE_HMIN; s->opt[OPT_BARCODE_HMIN].title = SANE_TITLE_BARCODE_HMIN; s->opt[OPT_BARCODE_HMIN].desc = SANE_DESC_BARCODE_HMIN; s->opt[OPT_BARCODE_HMIN].type = SANE_TYPE_INT; s->opt[OPT_BARCODE_HMIN].unit = SANE_UNIT_MM; s->opt[OPT_BARCODE_HMIN].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BARCODE_HMIN].constraint.range = &barcode_hmin_range; s->val[OPT_BARCODE_HMIN].w = 5; /* Barcode search timeout in ms (20-65535,default is 10000). */ s->opt[OPT_BARCODE_SEARCH_TIMEOUT].name = SANE_NAME_BARCODE_SEARCH_TIMEOUT; s->opt[OPT_BARCODE_SEARCH_TIMEOUT].title = SANE_TITLE_BARCODE_SEARCH_TIMEOUT; s->opt[OPT_BARCODE_SEARCH_TIMEOUT].desc = SANE_DESC_BARCODE_SEARCH_TIMEOUT; s->opt[OPT_BARCODE_SEARCH_TIMEOUT].type = SANE_TYPE_INT; s->opt[OPT_BARCODE_SEARCH_TIMEOUT].unit = SANE_UNIT_MICROSECOND; s->opt[OPT_BARCODE_SEARCH_TIMEOUT].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BARCODE_SEARCH_TIMEOUT].constraint.range = &barcode_search_timeout_range; s->val[OPT_BARCODE_SEARCH_TIMEOUT].w = 10000; /* Specify image sections and functions */ s->opt[OPT_SECTION].name = SANE_NAME_SECTION; s->opt[OPT_SECTION].title = SANE_TITLE_SECTION; s->opt[OPT_SECTION].desc = SANE_DESC_SECTION; s->opt[OPT_SECTION].type = SANE_TYPE_STRING; s->opt[OPT_SECTION].unit = SANE_UNIT_NONE; s->opt[OPT_SECTION].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_SECTION].size = 255; s->val[OPT_SECTION].s = strdup (""); /* Barcode_Relmax */ s->opt[OPT_BARCODE_RELMAX].name = SANE_NAME_BARCODE_RELMAX; s->opt[OPT_BARCODE_RELMAX].title = SANE_TITLE_BARCODE_RELMAX; s->opt[OPT_BARCODE_RELMAX].desc = SANE_DESC_BARCODE_RELMAX; s->opt[OPT_BARCODE_RELMAX].type = SANE_TYPE_INT; s->opt[OPT_BARCODE_RELMAX].unit = SANE_UNIT_NONE; s->opt[OPT_BARCODE_RELMAX].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BARCODE_RELMAX].constraint.range = &u8_range; s->val[OPT_BARCODE_RELMAX].w = 0; /* Barcode_Barmin */ s->opt[OPT_BARCODE_BARMIN].name = SANE_NAME_BARCODE_BARMIN; s->opt[OPT_BARCODE_BARMIN].title = SANE_TITLE_BARCODE_BARMIN; s->opt[OPT_BARCODE_BARMIN].desc = SANE_DESC_BARCODE_BARMIN; s->opt[OPT_BARCODE_BARMIN].type = SANE_TYPE_INT; s->opt[OPT_BARCODE_BARMIN].unit = SANE_UNIT_NONE; s->opt[OPT_BARCODE_BARMIN].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BARCODE_BARMIN].constraint.range = &u8_range; s->val[OPT_BARCODE_BARMIN].w = 0; /* Barcode_Barmax */ s->opt[OPT_BARCODE_BARMAX].name = SANE_NAME_BARCODE_BARMAX; s->opt[OPT_BARCODE_BARMAX].title = SANE_TITLE_BARCODE_BARMAX; s->opt[OPT_BARCODE_BARMAX].desc = SANE_DESC_BARCODE_BARMAX; s->opt[OPT_BARCODE_BARMAX].type = SANE_TYPE_INT; s->opt[OPT_BARCODE_BARMAX].unit = SANE_UNIT_NONE; s->opt[OPT_BARCODE_BARMAX].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BARCODE_BARMAX].constraint.range = &u8_range; s->val[OPT_BARCODE_BARMAX].w = 0; /* Barcode_Contrast */ s->opt[OPT_BARCODE_CONTRAST].name = SANE_NAME_BARCODE_CONTRAST; s->opt[OPT_BARCODE_CONTRAST].title = SANE_TITLE_BARCODE_CONTRAST; s->opt[OPT_BARCODE_CONTRAST].desc = SANE_DESC_BARCODE_CONTRAST; s->opt[OPT_BARCODE_CONTRAST].type = SANE_TYPE_INT; s->opt[OPT_BARCODE_CONTRAST].unit = SANE_UNIT_NONE; s->opt[OPT_BARCODE_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BARCODE_CONTRAST].constraint.range = &barcode_contrast_range; s->val[OPT_BARCODE_CONTRAST].w = 3; /* Barcode_Patchmode */ s->opt[OPT_BARCODE_PATCHMODE].name = SANE_NAME_BARCODE_PATCHMODE; s->opt[OPT_BARCODE_PATCHMODE].title = SANE_TITLE_BARCODE_PATCHMODE; s->opt[OPT_BARCODE_PATCHMODE].desc = SANE_DESC_BARCODE_PATCHMODE; s->opt[OPT_BARCODE_PATCHMODE].type = SANE_TYPE_INT; s->opt[OPT_BARCODE_PATCHMODE].unit = SANE_UNIT_NONE; s->opt[OPT_BARCODE_PATCHMODE].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BARCODE_PATCHMODE].constraint.range = &barcode_patchmode_range; s->val[OPT_BARCODE_PATCHMODE].w = 0; if (s->hw->info.canSection == SANE_FALSE) { s->opt[OPT_SECTION].cap |= SANE_CAP_INACTIVE; } if (s->hw->info.canBarCode == SANE_FALSE) { s->opt[OPT_BARCODE_GROUP].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BARCODE_SEARCH_BAR].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BARCODE_SEARCH_COUNT].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BARCODE_SEARCH_MODE].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BARCODE_HMIN].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BARCODE_SEARCH_TIMEOUT].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BARCODE_RELMAX].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BARCODE_BARMIN].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BARCODE_BARMAX].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BARCODE_CONTRAST].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BARCODE_PATCHMODE].cap |= SANE_CAP_INACTIVE; } return SANE_STATUS_GOOD; } static SANE_Status attach (const char *devnam, BH_Device ** devp) { SANE_Status status; BH_Device *dev; struct inquiry_standard_data ibuf; struct inquiry_vpd_data vbuf; struct inquiry_jis_data jbuf; size_t buf_size; int fd = -1; double mm; DBG (3, "attach called\n"); for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->sane.name, devnam) == 0) { if (devp) *devp = dev; return SANE_STATUS_GOOD; } } #ifdef FAKE_INQUIRY if (fake_inquiry) { DBG (3, "attach: faking inquiry of %s\n", devnam); memset (&ibuf, 0, sizeof (ibuf)); ibuf.devtype = 6; memcpy(ibuf.vendor, "**FAKE**", 8); memcpy(ibuf.product, "COPISCAN II 6338", 16); memcpy(ibuf.revision, "0016", 4); DBG (1, "attach: reported devtype='%d', vendor='%.8s', " "product='%.16s', revision='%.4s'\n", ibuf.devtype, ibuf.vendor, ibuf.product, ibuf.revision); memset (&vbuf, 0, sizeof (vbuf)); memset (&jbuf, 0, sizeof (jbuf)); } else #endif { DBG (3, "attach: opening %s\n", devnam); status = sanei_scsi_open (devnam, &fd, sense_handler, NULL); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: open failed: %s\n", sane_strstatus (status)); return status; } DBG (3, "attach: sending TEST_UNIT_READY\n"); status = test_unit_ready (fd); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: test unit ready failed (%s)\n", sane_strstatus (status)); sanei_scsi_close (fd); return status; } DBG (3, "attach: sending INQUIRY (standard data)\n"); memset (&ibuf, 0, sizeof (ibuf)); buf_size = sizeof(ibuf); status = inquiry (fd, &ibuf, &buf_size, 0, BH_INQUIRY_STANDARD_PAGE_CODE); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: inquiry (standard data) failed: %s\n", sane_strstatus (status)); sanei_scsi_close (fd); return status; } DBG (1, "attach: reported devtype='%d', vendor='%.8s', " "product='%.16s', revision='%.4s'\n", ibuf.devtype, ibuf.vendor, ibuf.product, ibuf.revision); if (ibuf.devtype != 6 || strncmp ((char *)ibuf.vendor, "B&H SCSI", 8) != 0 || strncmp ((char *)ibuf.product, "COPISCAN ", 9) != 0) { DBG (1, "attach: device is not a recognized Bell and Howell scanner\n"); sanei_scsi_close (fd); return SANE_STATUS_INVAL; } DBG (3, "attach: sending INQUIRY (vpd data)\n"); memset (&vbuf, 0, sizeof (vbuf)); buf_size = sizeof(vbuf); status = inquiry (fd, &vbuf, &buf_size, 1, BH_INQUIRY_VPD_PAGE_CODE); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: inquiry (vpd data) failed: %s\n", sane_strstatus (status)); sanei_scsi_close (fd); return status; } DBG (3, "attach: sending INQUIRY (jis data)\n"); memset (&jbuf, 0, sizeof (jbuf)); buf_size = sizeof(jbuf); status = inquiry (fd, &jbuf, &buf_size, 1, BH_INQUIRY_JIS_PAGE_CODE); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: inquiry (jis data) failed: %s\n", sane_strstatus (status)); sanei_scsi_close (fd); return status; } sanei_scsi_close (fd); } dev = malloc (sizeof (*dev)); if (!dev) return SANE_STATUS_NO_MEM; memset (dev, 0, sizeof (*dev)); dev->info.devtype = ibuf.devtype; sprintf(dev->info.vendor, "%.8s", ibuf.vendor); trim_spaces(dev->info.vendor, sizeof(dev->info.vendor)); sprintf(dev->info.product, "%.16s", ibuf.product); trim_spaces(dev->info.product, sizeof(dev->info.product)); sprintf(dev->info.revision, "%.4s", ibuf.revision); trim_spaces(dev->info.revision, sizeof(dev->info.revision)); dev->sane.name = strdup (devnam); dev->sane.vendor = strdup(dev->info.vendor); dev->sane.model = strdup(dev->info.product);; dev->sane.type = strdup(print_devtype(dev->info.devtype)); /* set capabilities from vpd */ dev->info.canADF = vbuf.adf & 0x01; dev->info.colorBandW = vbuf.imagecomposition & 0x01; dev->info.colorHalftone = vbuf.imagecomposition & 0x02; dev->info.canWhiteFrame = vbuf.imagedataprocessing[1] & 0x01; dev->info.canBlackFrame = vbuf.imagedataprocessing[1] & 0x02; dev->info.canEdgeExtract = vbuf.imagedataprocessing[1] & 0x04; dev->info.canNoiseFilter = vbuf.imagedataprocessing[1] & 0x08; dev->info.canSmooth = vbuf.imagedataprocessing[1] & 0x10; dev->info.canLineBold = vbuf.imagedataprocessing[1] & 0x20; dev->info.comprG3_1D = vbuf.compression & 0x01; dev->info.comprG3_2D = vbuf.compression & 0x02; dev->info.comprG4 = vbuf.compression & 0x04; dev->info.canBorderRecog = vbuf.sizerecognition & 0x01; dev->info.canBarCode = vbuf.optionalfeatures & 0x01; dev->info.canIcon = vbuf.optionalfeatures & 0x02; dev->info.canSection = vbuf.optionalfeatures & 0x04; dev->info.lineMaxBytes = _2btol(vbuf.xmaxoutputbytes); #ifdef FAKE_INQUIRY if (fake_inquiry) { dev->info.canADF = SANE_FALSE; dev->info.colorBandW = SANE_TRUE; dev->info.colorHalftone = SANE_TRUE; dev->info.canWhiteFrame = SANE_TRUE; dev->info.canBlackFrame = SANE_TRUE; dev->info.canEdgeExtract = SANE_TRUE; dev->info.canNoiseFilter = SANE_TRUE; dev->info.canSmooth = SANE_TRUE; dev->info.canLineBold = SANE_TRUE; dev->info.comprG3_1D = SANE_TRUE; dev->info.comprG3_2D = SANE_TRUE; dev->info.comprG4 = SANE_TRUE; dev->info.canBorderRecog = SANE_TRUE; dev->info.canBarCode = SANE_TRUE; dev->info.canIcon = SANE_TRUE; dev->info.canSection = SANE_TRUE; dev->info.lineMaxBytes = 450; } #endif /* set capabilities from jis */ dev->info.resBasicX = _2btol(jbuf.basicxres); dev->info.resBasicY = _2btol(jbuf.basicyres); dev->info.resMaxX = _2btol(jbuf.maxxres); dev->info.resMaxY = _2btol(jbuf.maxyres); dev->info.resMinX = _2btol(jbuf.minxres); dev->info.resMinY = _2btol(jbuf.minyres); /* set the length of the list to zero first, then append standard resolutions */ dev->info.resStdList[0] = 0; if (jbuf.standardres[0] & 0x80) appendStdList(&dev->info, 60); if (jbuf.standardres[0] & 0x40) appendStdList(&dev->info, 75); if (jbuf.standardres[0] & 0x20) appendStdList(&dev->info, 100); if (jbuf.standardres[0] & 0x10) appendStdList(&dev->info, 120); if (jbuf.standardres[0] & 0x08) appendStdList(&dev->info, 150); if (jbuf.standardres[0] & 0x04) appendStdList(&dev->info, 160); if (jbuf.standardres[0] & 0x02) appendStdList(&dev->info, 180); if (jbuf.standardres[0] & 0x01) appendStdList(&dev->info, 200); if (jbuf.standardres[1] & 0x80) appendStdList(&dev->info, 240); if (jbuf.standardres[1] & 0x40) appendStdList(&dev->info, 300); if (jbuf.standardres[1] & 0x20) appendStdList(&dev->info, 320); if (jbuf.standardres[1] & 0x10) appendStdList(&dev->info, 400); if (jbuf.standardres[1] & 0x08) appendStdList(&dev->info, 480); if (jbuf.standardres[1] & 0x04) appendStdList(&dev->info, 600); if (jbuf.standardres[1] & 0x02) appendStdList(&dev->info, 800); if (jbuf.standardres[1] & 0x01) appendStdList(&dev->info, 1200); if (dev->info.resStdList[0] == 0) { /* make a default standard resolutions for 200 and 300dpi */ DBG(1, "attach: no standard resolutions reported\n"); dev->info.resStdList[0] = 2; dev->info.resStdList[1] = 200; dev->info.resStdList[2] = 300; dev->info.resBasicX = dev->info.resBasicY = 300; } dev->info.winWidth = _4btol(jbuf.windowwidth); dev->info.winHeight = _4btol(jbuf.windowlength); if (dev->info.winWidth <= 0) { dev->info.winWidth = (SANE_Int) (dev->info.resBasicX * 8.5); DBG(1, "attach: invalid window width reported, using %d\n", dev->info.winWidth); } if (dev->info.winHeight <= 0) { dev->info.winHeight = dev->info.resBasicY * 14; DBG(1, "attach: invalid window height reported, using %d\n", dev->info.winHeight); } mm = (dev->info.resBasicX > 0) ? ((double) dev->info.winWidth / (double) dev->info.resBasicX * MM_PER_INCH) : 0.0; dev->info.x_range.min = SANE_FIX(0.0); dev->info.x_range.max = SANE_FIX(mm); dev->info.x_range.quant = SANE_FIX(0.0); mm = (dev->info.resBasicY > 0) ? ((double) dev->info.winHeight / (double) dev->info.resBasicY * MM_PER_INCH) : 0.0; dev->info.y_range.min = SANE_FIX(0.0); dev->info.y_range.max = SANE_FIX(mm); dev->info.y_range.quant = SANE_FIX(0.0); /* set additional discovered/guessed capabilities */ /* if all of the ACE capabilities are present, declare it ACE capable */ dev->info.canACE = dev->info.canEdgeExtract && dev->info.canNoiseFilter && dev->info.canSmooth && dev->info.canLineBold; /* if the model is known to be a duplex, declare it duplex capable */ if (strcmp(dev->info.product, "COPISCAN II 6338") == 0) { dev->info.canDuplex = SANE_TRUE; } else { dev->info.canDuplex = SANE_FALSE; } /* the paper sensor requires RSC revision 1.4 or higher and an * installed feeder. NOTE: It also requires SW-4 on and the * AccufeedPlus feeder, but we cannot discover that. */ if (strcmp(dev->info.revision, "0014") >= 0) { dev->info.canCheckADF = dev->info.canADF; } else { dev->info.canCheckADF = SANE_FALSE; } /* set option defaults based on inquiry information */ dev->info.res_default = dev->info.resBasicX; dev->info.autoborder_default = dev->info.canBorderRecog; dev->info.batch_default = SANE_FALSE; dev->info.deskew_default = SANE_FALSE; dev->info.check_adf_default = SANE_FALSE; dev->info.duplex_default = SANE_FALSE; dev->info.timeout_adf_default = 0; dev->info.timeout_manual_default = 0; dev->info.control_panel_default = dev->info.canACE; ++num_devices; dev->next = first_dev; first_dev = dev; if (devp) *devp = dev; return SANE_STATUS_GOOD; } static SANE_Status attach_one(const char *devnam) { attach (devnam, NULL); return SANE_STATUS_GOOD; } SANE_Status sane_init (SANE_Int *version_code, SANE_Auth_Callback authorize) { char devnam[PATH_MAX] = "/dev/scanner"; FILE *fp; (void) authorize; /* get rid of compiler warning */ DBG_INIT(); DBG(3, "sane_init called\n"); DBG(1, "Bell+Howell SANE backend %d.%d build %d %s endian\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD, _is_host_little_endian() ? "little" : "big"); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); fp = sanei_config_open(BH_CONFIG_FILE); if (fp) { char line[PATH_MAX]; const char *lp; size_t len; /* read config file */ while (sanei_config_read (line, sizeof (line), fp)) { if (line[0] == '#') /* ignore line comments */ continue; len = strlen (line); if (!len) continue; /* ignore empty lines */ lp = sanei_config_skip_whitespace (line); DBG(16, "sane_init: processing config file line '%s'\n", line); if (strncmp(lp, "option", 6) == 0 && (isspace (lp[6]) || lp[6] == '\0')) { lp += 6; lp = sanei_config_skip_whitespace (lp); if (strncmp(lp, "disable-optional-frames", 23) == 0) { DBG(1, "sane_init: configuration option " "'disable-optional-frames' set\n"); disable_optional_frames = 1; } else if (strncmp(lp, "fake-inquiry", 12) == 0) { DBG(1, "sane_init: configuration option " "'fake-inquiry' set\n"); fake_inquiry = 1; } else { DBG(1, "sane_init: ignoring unknown " "configuration option '%s'\n", lp); } } else { DBG(16, "sane_init: found a device: line '%s'\n", lp); strncpy (devnam, lp, sizeof(devnam)); devnam[sizeof(devnam)-1] = '\0'; sanei_config_attach_matching_devices(devnam, attach_one); } } fclose (fp); } else { /* configure the /dev/scanner device in the absence of config file */ sanei_config_attach_matching_devices ("/dev/scanner", attach_one); } return SANE_STATUS_GOOD; } SANE_Status sane_get_devices (const SANE_Device ***device_list, SANE_Bool local) { BH_Device *dev; int i; DBG(3, "sane_get_devices called\n"); (void) local; /* get rid of compiler warning */ if (devlist) free (devlist); devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); if (!devlist) return SANE_STATUS_NO_MEM; i = 0; for (dev = first_dev; dev; dev = dev->next) devlist[i++] = &dev->sane; devlist[i++] = 0; *device_list = devlist; return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const devnam, SANE_Handle *handle) { SANE_Status status; BH_Device *dev; BH_Scanner *s; DBG(3, "sane_open called\n"); if (devnam[0] != '\0') { for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->sane.name, devnam) == 0) break; } if (!dev) { status = attach (devnam, &dev); if (status != SANE_STATUS_GOOD) return status; } } else { dev = first_dev; } if (!dev) return SANE_STATUS_INVAL; s = malloc (sizeof (*s)); if (!s) return SANE_STATUS_NO_MEM; memset (s, 0, sizeof (*s)); s->fd = -1; s->hw = dev; s->bmu = BH_UNIT_POINT; s->mud = 1; ScannerDump(s); init_options (s); s->next = first_handle; first_handle = s; /* initialize our parameters */ get_parameters(s, 0); *handle = s; #ifdef FAKE_INQUIRY if (fake_inquiry) { DBG (1, "sane_open: faking open of %s\n", s->hw->sane.name); } else #endif { status = sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, s); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_open: open of %s failed: %s\n", s->hw->sane.name, sane_strstatus (status)); return status; } } return SANE_STATUS_GOOD; } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { BH_Scanner *s = handle; DBG(3, "sane_get_option_descriptor called (option:%d)\n", option); if ((unsigned) option >= NUM_OPTIONS) return 0; return (s->opt + option); } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Word *info) { BH_Scanner *s = handle; SANE_Status status; SANE_Word cap; SANE_String_Const name; DBG(3, "sane_control_option called\n"); name = s->opt[option].name ? s->opt[option].name : "(nil)"; if (info) *info = 0; if (s->scanning && action == SANE_ACTION_SET_VALUE) return SANE_STATUS_DEVICE_BUSY; if (option >= NUM_OPTIONS) return SANE_STATUS_INVAL; cap = s->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) return SANE_STATUS_INVAL; if (action == SANE_ACTION_GET_VALUE) { DBG(16, "sane_control_option: get_value %s [#%d]\n", name, option); switch (option) { /* word options: */ case OPT_RESOLUTION: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_TIMEOUT_ADF: case OPT_TIMEOUT_MANUAL: case OPT_ACE_FUNCTION: case OPT_ACE_SENSITIVITY: case OPT_BRIGHTNESS: case OPT_THRESHOLD: case OPT_CONTRAST: case OPT_ICON_WIDTH: case OPT_ICON_LENGTH: case OPT_BARCODE_SEARCH_COUNT: case OPT_BARCODE_HMIN: case OPT_BARCODE_SEARCH_TIMEOUT: case OPT_BARCODE_RELMAX: case OPT_BARCODE_BARMIN: case OPT_BARCODE_BARMAX: case OPT_BARCODE_CONTRAST: case OPT_BARCODE_PATCHMODE: case OPT_NUM_OPTS: *(SANE_Word *) val = s->val[option].w; return SANE_STATUS_GOOD; /* string options: */ case OPT_INQUIRY: case OPT_SCAN_SOURCE: case OPT_SCAN_MODE: case OPT_COMPRESSION: case OPT_PAPER_SIZE: case OPT_ROTATION: case OPT_BARCODE_SEARCH_BAR: case OPT_BARCODE_SEARCH_MODE: case OPT_SECTION: strcpy (val, s->val[option].s); return SANE_STATUS_GOOD; /* boolean options: */ case OPT_PREVIEW: case OPT_AUTOBORDER: case OPT_DESKEW: case OPT_BATCH: case OPT_CHECK_ADF: case OPT_DUPLEX: case OPT_CONTROL_PANEL: case OPT_NEGATIVE: *(SANE_Word *) val = s->val[option].w; return SANE_STATUS_GOOD; default: DBG(1, "sane_control_option:invalid option number %d\n", option); return SANE_STATUS_INVAL; } } else if (action == SANE_ACTION_SET_VALUE) { switch (s->opt[option].type) { case SANE_TYPE_BOOL: case SANE_TYPE_INT: DBG(16, "sane_control_option: set_value %s [#%d] to %d\n", name, option, *(SANE_Word *) val); break; case SANE_TYPE_FIXED: DBG(16, "sane_control_option: set_value %s [#%d] to %f\n", name, option, SANE_UNFIX(*(SANE_Word *) val)); break; case SANE_TYPE_STRING: DBG(16, "sane_control_option: set_value %s [#%d] to %s\n", name, option, (char *) val); break; default: DBG(16, "sane_control_option: set_value %s [#%d]\n", name, option); } if (!SANE_OPTION_IS_SETTABLE (cap)) return SANE_STATUS_INVAL; status = sanei_constrain_value (s->opt + option, val, info); if (status != SANE_STATUS_GOOD) return status; switch (option) { /* (mostly) side-effect-free word options: */ case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: /* make sure that paper-size is set to custom */ if (s->val[option].w != *(SANE_Word *) val) { if (info) *info |= SANE_INFO_RELOAD_PARAMS; if (get_paper_id(_OPT_VAL_STRING(s, OPT_PAPER_SIZE)) != 0) { if (info) *info |= SANE_INFO_RELOAD_OPTIONS; /* set paper size to 'custom' */ free (s->val[OPT_PAPER_SIZE].s); s->val[OPT_PAPER_SIZE].s = strdup(paper_list[0]); } } /* fall through */ case OPT_RESOLUTION: if (info && s->val[option].w != *(SANE_Word *) val) *info |= SANE_INFO_RELOAD_PARAMS; /* fall through */ case OPT_TIMEOUT_ADF: case OPT_TIMEOUT_MANUAL: case OPT_ACE_FUNCTION: case OPT_ACE_SENSITIVITY: case OPT_BRIGHTNESS: case OPT_THRESHOLD: case OPT_CONTRAST: case OPT_ICON_WIDTH: case OPT_ICON_LENGTH: case OPT_BARCODE_SEARCH_COUNT: case OPT_BARCODE_HMIN: case OPT_BARCODE_SEARCH_TIMEOUT: case OPT_BARCODE_RELMAX: case OPT_BARCODE_BARMIN: case OPT_BARCODE_BARMAX: case OPT_BARCODE_CONTRAST: case OPT_BARCODE_PATCHMODE: case OPT_NUM_OPTS: s->val[option].w = *(SANE_Word *) val; return SANE_STATUS_GOOD; /* string options */ case OPT_BARCODE_SEARCH_BAR: /*!!! we're supporting only a single barcode type via the option */ s->search_bars[0] = get_barcode_id(val); /* fall through */ case OPT_SCAN_SOURCE: case OPT_COMPRESSION: case OPT_ROTATION: case OPT_BARCODE_SEARCH_MODE: case OPT_SECTION: if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); return SANE_STATUS_GOOD; /* boolean options: */ case OPT_AUTOBORDER: /*!!! autoborder true disables geometry controls * and sets them to defaults? */ /* fall through */ case OPT_PREVIEW: case OPT_BATCH: case OPT_DESKEW: case OPT_CHECK_ADF: case OPT_DUPLEX: case OPT_NEGATIVE: s->val[option].w = *(SANE_Word *) val; return SANE_STATUS_GOOD; /* options with side effects */ case OPT_CONTROL_PANEL: /* a boolean option */ /* control-panel true enables/disables some enhancement controls */ if (s->val[option].w != *(SANE_Word *) val) { if (info) *info |= SANE_INFO_RELOAD_OPTIONS; s->val[option].w = *(SANE_Word *) val; if (*(SANE_Word *) val == SANE_TRUE) { if (s->hw->info.canACE == SANE_TRUE) { s->opt[OPT_ACE_FUNCTION].cap |= SANE_CAP_INACTIVE; s->opt[OPT_ACE_SENSITIVITY].cap |= SANE_CAP_INACTIVE; } else { s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; } } else { if (s->hw->info.canACE == SANE_TRUE) { s->opt[OPT_ACE_FUNCTION].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_ACE_SENSITIVITY].cap &= ~SANE_CAP_INACTIVE; } else { s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE; } } } return SANE_STATUS_GOOD; case OPT_SCAN_MODE: /* a string option */ /* scan mode != lineart disables compression, setting it to * 'none' */ if (strcmp (s->val[option].s, (SANE_String) val)) { if (info) *info |= SANE_INFO_RELOAD_OPTIONS; if (get_scan_mode_id((SANE_String) val) != 0) { /* scan mode is not lineart, disable compression * and set compression to 'none' */ s->opt[OPT_COMPRESSION].cap |= SANE_CAP_INACTIVE; if (s->val[OPT_COMPRESSION].s && get_compression_id(s->val[OPT_COMPRESSION].s) != 0) { free (s->val[OPT_COMPRESSION].s); s->val[OPT_COMPRESSION].s = strdup(compression_list[0]); } } else { /* scan mode is lineart, enable compression */ s->opt[OPT_COMPRESSION].cap &= ~SANE_CAP_INACTIVE; } free (s->val[option].s); s->val[option].s = strdup (val); } return SANE_STATUS_GOOD; case OPT_PAPER_SIZE: /* a string option */ /* changes geometry options, therefore _RELOAD_PARAMS and _RELOAD_OPTIONS */ if (strcmp (s->val[option].s, (SANE_String) val)) { SANE_Int paper_id = get_paper_id((SANE_String) val); /* paper_id 0 is a special case (custom) that * disables the paper size control of geometry */ if (paper_id != 0) { double left, x_max, y_max, x, y; x_max = SANE_UNFIX(s->hw->info.x_range.max); y_max = SANE_UNFIX(s->hw->info.y_range.max); /* a dimension of 0.0 (or less) is replaced with the max value */ x = (paper_sizes[paper_id].width <= 0.0) ? x_max : paper_sizes[paper_id].width; y = (paper_sizes[paper_id].length <= 0.0) ? y_max : paper_sizes[paper_id].length; if (info) *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; /* set geometry options based on paper size */ /* set geometry options based on paper size */ if (s->hw->info.canADF) { /* when the feeder is used the paper is centered in the * hopper; with the manual feed it is aligned left. */ left = (x_max - x) / 2.0; if (left < 0.0) left = 0.0; } else { left = 0.0; } s->val[OPT_TL_X].w = SANE_FIX(left); s->val[OPT_TL_Y].w = SANE_FIX(0.0); s->val[OPT_BR_X].w = SANE_FIX(MIN(x + left, x_max)); s->val[OPT_BR_Y].w = SANE_FIX(MIN(y, y_max)); } free (s->val[option].s); s->val[option].s = strdup (val); } return SANE_STATUS_GOOD; default: DBG(1, "sane_control_option:invalid option number %d\n", option); return SANE_STATUS_INVAL; } } return SANE_STATUS_INVAL; } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters *params) { BH_Scanner *s = handle; SANE_Int status = SANE_STATUS_GOOD; DBG(3, "sane_get_parameters called\n"); if (params) { SANE_Int res; if (!s->scanning) { /* update our parameters ONLY if we're not scanning */ status = get_parameters(s, 0); } *params = s->params; res = _OPT_VAL_WORD(s, OPT_RESOLUTION); DBG (1, "get_parameters: format=%d, pixels/line=%d, bytes/line=%d, " "lines=%d, dpi=%d\n", (int) s->params.format, s->params.pixels_per_line, s->params.bytes_per_line, s->params.lines, res); } return status; } SANE_Status sane_start (SANE_Handle handle) { BH_Scanner *s = handle; SANE_Status status; DBG(3, "sane_start called\n"); s->cancelled = SANE_FALSE; if (s->scanning == SANE_FALSE) { /* get preliminary parameters */ status = get_parameters (s, 0); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: get_parameters failed: %s\n", sane_strstatus (status)); return status; } /* Do the setup once per 'batch'. The SANE standard requires the * frontend to call sane_cancel once all desired frames have been * acquired. That is when scanning is set back to SANE_FALSE and * the 'batch' is considered done. */ status = start_setup (s); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: start_setup failed: %s\n", sane_strstatus (status)); return status; } } status = start_scan (s); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: start_scan failed: %s\n", sane_strstatus (status)); return status; } return SANE_STATUS_GOOD; } SANE_Status sane_read (SANE_Handle handle, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *len) { BH_Scanner *s = handle; SANE_Status status; size_t nread; DBG(3, "sane_read called\n"); *len = 0; if (s->cancelled) { DBG (3, "sane_read: cancelled!\n"); return SANE_STATUS_CANCELLED; } if (!s->scanning) { DBG (3, "sane_read: scanning is false!\n"); sane_cancel(s); return SANE_STATUS_CANCELLED; } nread = maxlen; DBG (3, "sane_read: request %lu bytes\n", (u_long) nread); /* set InvalidBytes to 0 before read; sense_handler will set it * to non-zero if we do the last partial read. */ s->InvalidBytes = 0; status = read_data (s, buf, &nread); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_read: read_data failed %s\n", sane_strstatus(status)); sane_cancel (s); return status; } nread = maxlen - s->InvalidBytes; DBG (3, "sane_read: got %lu bytes\n", (u_long) nread); *len = nread; return (maxlen != 0 && nread == 0) ? SANE_STATUS_EOF : SANE_STATUS_GOOD; } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { #ifdef NONBLOCKSUPPORTED BH_Scanner *s = handle; #endif DBG(3, "sane_set_io_mode called: non_blocking=%d\n", non_blocking); #ifdef NONBLOCKSUPPORTED if (s->fd < 0) { return SANE_STATUS_INVAL; } if (fcntl (s->fd, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0) { DBG(1, "sane_set_io_mode: error setting io mode\n"); return SANE_STATUS_IO_ERROR; } return SANE_STATUS_GOOD; #else (void) handle; /* get rid of compiler warning */ return (non_blocking == 1) ? SANE_STATUS_UNSUPPORTED : SANE_STATUS_GOOD; #endif } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int *fd) { #ifdef NONBLOCKSUPPORTED BH_Scanner *s = handle; #endif DBG(3, "sane_get_select_fd called\n"); #ifdef NONBLOCKSUPPORTED if (s->fd < 0) { return SANE_STATUS_INVAL; } *fd = s->fd; return SANE_STATUS_GOOD; #else (void) handle; (void) fd; /* get rid of compiler warning */ return SANE_STATUS_UNSUPPORTED; #endif } void sane_cancel (SANE_Handle handle) { BH_Scanner *s = (BH_Scanner *) handle; DBG(3, "sane_cancel called\n"); if (s->scanning) { /* if batchmode is enabled, then call set_window to * abort the batch */ if (_OPT_VAL_WORD(s, OPT_BATCH) == SANE_TRUE) { DBG(5, "sane_cancel: calling set_window to abort batch\n"); set_window(s, BH_BATCH_ABORT); } } s->scanning = SANE_FALSE; s->cancelled = SANE_TRUE; } void sane_close (SANE_Handle handle) { BH_Scanner *s = (BH_Scanner *) handle; DBG(3, "sane_close called\n"); if (s->fd != -1) sanei_scsi_close (s->fd); s->fd = -1; free (s); } void sane_exit (void) { BH_Device *dev, *next; DBG(3, "sane_exit called\n"); for (dev = first_dev; dev; dev = next) { next = dev->next; free (dev); } if (devlist) free (devlist); } backends-1.3.0/backend/bh.conf.in000066400000000000000000000000351456256263500165350ustar00rootroot00000000000000scsi "B&H SCSI" /dev/scanner backends-1.3.0/backend/bh.h000066400000000000000000000673341456256263500154510ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1999,2000 Tom Martone This file is part of a SANE backend for Bell and Howell Copiscan II Scanners using the Remote SCSI Controller(RSC). 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, see . 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 BH_H #define BH_H 1 #ifndef PATH_MAX #define PATH_MAX (1024) #endif #define BH_CONFIG_FILE "bh.conf" /* number of barcode types that can be searched at one time */ #define NUM_SEARCH_BARS 6 /* number of additional scanning/decoding sections supported */ #define NUM_SECTIONS 8 /* number of possible reads per scan plus the extra one for * the barcode file */ #define NUM_READS 56 + 1 /* specify sanity limits for autoborder detection and barcode decoding */ #define BH_AUTOBORDER_TRIES 100 #define BH_DECODE_TRIES 100 /* specify a fudge factor in mm for border around decoded barcodes */ #define BH_DECODE_FUDGE 1.0 /* section flags - what operation(s) to perform on section */ #define BH_SECTION_FRONT_IMAGE (1 << 0) #define BH_SECTION_BACK_IMAGE (1 << 1) #define BH_SECTION_FRONT_BAR (1 << 2) #define BH_SECTION_BACK_BAR (1 << 3) #define BH_SECTION_FRONT_PATCH (1 << 4) #define BH_SECTION_BACK_PATCH (1 << 5) typedef enum { BH_UNIT_INCH, BH_UNIT_MM, BH_UNIT_POINT } bh_measureUnit; typedef enum { BH_COMP_NONE, BH_COMP_G31D, BH_COMP_G32D, BH_COMP_G42D } bh_compress; typedef enum { BH_ROTATION_0, BH_ROTATION_90, BH_ROTATION_180, BH_ROTATION_270 } bh_rotation; typedef enum { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, /* inquiry string */ OPT_INQUIRY, /* preview mode */ OPT_PREVIEW, /* scan mode */ OPT_SCAN_MODE, /* resolution */ OPT_RESOLUTION, /* hardware compression */ OPT_COMPRESSION, OPT_GEOMETRY_GROUP, /* automatic border detection */ OPT_AUTOBORDER, /* hardware rotation */ OPT_ROTATION, /* hardware deskew */ OPT_DESKEW, /* paper size */ OPT_PAPER_SIZE, /* top-left x */ OPT_TL_X, /* top-left y */ OPT_TL_Y, /* bottom-right x */ OPT_BR_X, /* bottom-right y */ OPT_BR_Y, OPT_FEEDER_GROUP, /* scan source (eg. ADF) */ OPT_SCAN_SOURCE, /* scan in batch mode */ OPT_BATCH, /* scan both sides of the page */ OPT_DUPLEX, /* timeout in seconds with manual feed */ OPT_TIMEOUT_MANUAL, /* timeout in seconds with ADF */ OPT_TIMEOUT_ADF, /* check for page in ADF before scanning */ OPT_CHECK_ADF, OPT_ENHANCEMENT_GROUP, /* Enables the scanner's control panel */ OPT_CONTROL_PANEL, /* ACE Function */ OPT_ACE_FUNCTION, /* ACE Sensitivity */ OPT_ACE_SENSITIVITY, /* Brightness */ OPT_BRIGHTNESS, /* Threshold */ OPT_THRESHOLD, /* Contrast */ OPT_CONTRAST, /* Negative (reverse image) */ OPT_NEGATIVE, OPT_ICON_GROUP, /* Width of icon (thumbnail) image in pixels */ OPT_ICON_WIDTH, /* Length of icon (thumbnail) image in pixels */ OPT_ICON_LENGTH, OPT_BARCODE_GROUP, /* Add to barcode search priority. */ OPT_BARCODE_SEARCH_BAR, /* Barcode search count (1-7, default 3). */ OPT_BARCODE_SEARCH_COUNT, /* Barcode search mode. * (1 = horizontal,2 = vertical, 6 = v then h, 9 = h then v). */ OPT_BARCODE_SEARCH_MODE, /* Patch code min height (def=127 (5mm)) */ OPT_BARCODE_HMIN, /* Barcode search timeout in ms * (20-65535,default is disabled). */ OPT_BARCODE_SEARCH_TIMEOUT, /* Specify image sections and functions */ OPT_SECTION, /* Specifies the maximum relation from the widest to * the smallest bar */ OPT_BARCODE_RELMAX, /* Specifies the minimum number of bars in Bar/Patch code */ OPT_BARCODE_BARMIN, /* Specifies the maximum number of bars in a Bar/Patch code */ OPT_BARCODE_BARMAX, /* Specifies the image contrast used in decoding. * Use higher values when there are more white pixels * in the code */ OPT_BARCODE_CONTRAST, /* Controls Patch Code detection. */ OPT_BARCODE_PATCHMODE, /* must come last: */ NUM_OPTIONS } BH_Option; /* macros for accessing the value for an option within a scanning context */ #define _OPT_VAL_WORD(s, o) ((s)->val[(o)].w) #define _OPT_VAL_WORD_THOUSANDTHS(s, o) \ (SANE_UNFIX(_OPT_VAL_WORD((s), (o))) * 1000.0 / MM_PER_INCH) #define _OPT_VAL_STRING(s, o) ((s)->val[(o)].s) #define _OPT_VAL_WORD_ARRAY(s, o) ((s)->val[(o)].wa) typedef struct _BH_Paper { SANE_String name; /* paper dimensions in mm */ double width, length; } BH_Paper; typedef struct _BH_Section { /* section dimensions - in millimeters */ u_long top, left, width, length; /* compression type/arg/frameformat */ SANE_Byte compressiontype; SANE_Byte compressionarg; SANE_Frame format; /* Flags (see BH_SECTION_...) indicating operation(s) to perform * on the section. If zero, the section is completely disabled * and will not even be defined in set_window. */ SANE_Word flags; } BH_Section; typedef struct _BH_Info { SANE_Range x_range; SANE_Range y_range; SANE_Int res_default; SANE_Bool autoborder_default; SANE_Bool batch_default; SANE_Bool deskew_default; SANE_Bool check_adf_default; SANE_Bool duplex_default; SANE_Int timeout_adf_default; SANE_Int timeout_manual_default; SANE_Bool control_panel_default; /* additional discovered/guessed capabilities */ SANE_Bool canACE; SANE_Bool canDuplex; SANE_Bool canCheckADF; /* standard information */ SANE_Byte devtype; SANE_Char vendor[9]; /* model name */ SANE_Char product[17]; /* product name */ SANE_Char revision[5]; /* revision */ /* VPD information */ SANE_Bool canADF; /* is there an ADF available */ SANE_Bool colorBandW; /* can scanner do black and white */ SANE_Bool colorHalftone; /* can scanner do Halftone */ SANE_Bool canWhiteFrame; /* data processing: White Framing */ SANE_Bool canBlackFrame; /* data processing: Black Framing */ SANE_Bool canEdgeExtract; /* data processing: ACE: Edge Extraction */ SANE_Bool canNoiseFilter; /* data processing: ACE: Noise Filtering */ SANE_Bool canSmooth; /* data processing: ACE: Smoothing */ SANE_Bool canLineBold; /* data processing: ACE: LineBolding */ SANE_Bool comprG3_1D; /* compression: Group 3, 1 dimensional */ SANE_Bool comprG3_2D; /* compression: Group 3, 2 dimensional */ SANE_Bool comprG4; /* compression: Group 4 */ SANE_Bool canBorderRecog; /* can do border recognition */ SANE_Bool canBarCode; /* bar code support available */ SANE_Bool canIcon; /* icon support available */ SANE_Bool canSection; /* section support available */ SANE_Int lineMaxBytes; /* maximum bytes per scan-line */ /* jis information */ SANE_Int resBasicX; /* basic X resolution */ SANE_Int resBasicY; /* basic Y resolution */ SANE_Int resMaxX; /* maximum X resolution */ SANE_Int resMaxY; /* maximum Y resolution */ SANE_Int resMinX; /* minimum X resolution */ SANE_Int resMinY; /* minimum Y resolution */ SANE_Int resStdList[16+1]; /* list of available standard resolutions * (first slot is the length) */ SANE_Int winWidth; /* length of window (in BasicX res DPI) */ SANE_Int winHeight; /* height of window (in BasicY res DPI) */ } BH_Info; typedef struct _BH_Device BH_Device; struct _BH_Device { BH_Device *next; SANE_Device sane; BH_Info info; }; typedef struct _BH_Scanner BH_Scanner; struct _BH_Scanner { /* all the state needed to define a scan request: */ /* linked list for housekeeping */ BH_Scanner *next; /* scanner dependent/low-level state: */ BH_Device *hw; /* SCSI filedescriptor */ int fd; /* tempfile which is used to send decoded barcode data */ FILE *barf; char barfname[PATH_MAX+1]; /* SANE option descriptors and values */ SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; /* additional values that don't fit into Option_Value representation */ SANE_Byte search_bars[NUM_SEARCH_BARS]; BH_Section sections[NUM_SECTIONS]; SANE_Int num_sections; /* SANE image parameters */ SANE_Parameters params; /* state information - not options */ /* Basic Measurement Unit */ SANE_Int bmu; /* Measurement Unit Divisor */ SANE_Int mud; /* track data to be read. ReadList contains the codes of the read types * (see BH_READ_TYPE...) to perform, readcnt is the total number of reads * for this scan and readptr points to the current read operation. */ SANE_Byte readlist[NUM_READS]; SANE_Int readcnt, readptr; u_long InvalidBytes; SANE_Bool scanning; SANE_Bool cancelled; SANE_Bool backpage; SANE_Bool barcodes; SANE_Bool patchcodes; SANE_Bool icons; u_long iconwidth, iconlength; SANE_Bool barcode_not_found; }; static const SANE_Range u8_range = { 0, /* minimum */ 255, /* maximum */ 0 /* quantization */ }; static const SANE_Range u16_range = { 0, /* minimum */ 65535, /* maximum */ 0 /* quantization */ }; static const SANE_Range icon_range = { 0, /* minimum */ 3600, /* maximum */ 8 /* quantization */ }; static const SANE_Range barcode_search_timeout_range = { 20, /* minimum */ 65535, /* maximum */ 0 /* quantization */ }; static const SANE_Range barcode_hmin_range = { 1, /* minimum */ 1660, /* maximum (when converted from mm * to thousandths will still be less * than 65536) */ 0 /* quantization */ }; static const SANE_Range barcode_search_count_range = { 1, /* minimum */ 7, /* maximum */ 0 /* quantization */ }; static const SANE_Range barcode_relmax_range = { 0, /* minimum */ 6, /* maximum */ 0 /* quantization */ }; static const SANE_Range barcode_contrast_range = { 0, /* minimum */ 6, /* maximum */ 0 /* quantization */ }; static const SANE_Range barcode_patchmode_range = { 0, /* minimum */ 1, /* maximum */ 0 /* quantization */ }; static const SANE_Range ace_function_range = { -4, /* minimum */ 4, /* maximum */ 0 /* quantization */ }; static const SANE_Range ace_sensitivity_range = { 0, /* minimum */ 9, /* maximum */ 0 /* quantization */ }; static SANE_String_Const scan_mode_list[] = { "lineart", "halftone", 0 }; static SANE_String_Const scan_mode_min_list[] = { "lineart", 0 }; static SANE_String_Const barcode_search_mode_list[] = { "horiz-vert", /* 9 */ "horizontal", /* 1 */ "vertical", /* 2 */ "vert-horiz", /* 6 */ 0 }; static SANE_String_Const scan_source_list[] = { "Automatic Document Feeder", "Manual Feed Tray", 0 }; static SANE_String_Const compression_list[] = { "none", "g31d", "g32d", "g42d", 0 }; /* list of supported bar/patch codes */ static SANE_String_Const barcode_search_bar_list[] = { "none", "ean-8", "ean-13", "reserved-ean-add", "code39", "code2-5-interleaved", "code2-5-3lines-matrix", "code2-5-3lines-datalogic", "code2-5-5lines-industrial", "patchcode", "codabar", "codabar-with-start-stop", "code39ascii", "code128", "code2-5-5lines-iata", 0 }; /* list of supported rotation angles */ static SANE_String_Const rotation_list[] = { "0", "90", "180", "270", 0 }; /* list of support paper sizes */ /* 'custom' MUST be item 0; otherwise a width or length of 0 indicates * the maximum value supported by the scanner */ static const BH_Paper paper_sizes[] = { {"Custom", 0.0, 0.0}, {"Letter", 215.9, 279.4}, {"Legal", 215.9, 355.6}, {"A3", 297, 420}, {"A4", 210, 297}, {"A5", 148.5, 210}, {"A6", 105, 148.5}, {"B4", 250, 353}, {"B5", 182, 257}, {"Full", 0.0, 0.0}, }; /* MUST be kept in sync with paper_sizes */ static SANE_String_Const paper_list[] = { "Custom", "Letter", "Legal", "A3", "A4", "A5", "A6", "B4", "B5", "Full", 0 }; static /* inline */ int _is_host_little_endian(void); static /* inline */ int _is_host_little_endian() { SANE_Int val = 255; unsigned char *firstbyte = (unsigned char *) &val; return (*firstbyte == 255) ? SANE_TRUE : SANE_FALSE; } static /* inline */ void _lto2b(u_long val, SANE_Byte *bytes) { bytes[0] = (val >> 8) & 0xff; bytes[1] = val & 0xff; } static /* inline */ void _lto3b(u_long val, SANE_Byte *bytes) { bytes[0] = (val >> 16) & 0xff; bytes[1] = (val >> 8) & 0xff; bytes[2] = val & 0xff; } static /* inline */ void _lto4b(u_long val, SANE_Byte *bytes) { bytes[0] = (val >> 24) & 0xff; bytes[1] = (val >> 16) & 0xff; bytes[2] = (val >> 8) & 0xff; bytes[3] = val & 0xff; } static /* inline */ u_long _2btol(SANE_Byte *bytes) { u_long rv; rv = (bytes[0] << 8) | bytes[1]; return rv; } static /* inline */ u_long _4btol(SANE_Byte *bytes) { u_long rv; rv = (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]; return rv; } #define SANE_TITLE_SCAN_MODE_GROUP "Scan Mode" #define SANE_TITLE_GEOMETRY_GROUP "Geometry" #define SANE_TITLE_FEEDER_GROUP "Feeder" #define SANE_TITLE_ENHANCEMENT_GROUP "Enhancement" #define SANE_TITLE_ICON_GROUP "Icon" #define SANE_TITLE_BARCODE_GROUP "Barcode" #define SANE_NAME_AUTOBORDER "autoborder" #define SANE_TITLE_AUTOBORDER "Autoborder" #define SANE_DESC_AUTOBORDER "Enable Automatic Border Detection" #define SANE_NAME_COMPRESSION "compression" #define SANE_TITLE_COMPRESSION "Data Compression" #define SANE_DESC_COMPRESSION "Sets the compression mode of the scanner" #define SANE_NAME_ROTATION "rotation" #define SANE_TITLE_ROTATION "Page Rotation" #define SANE_DESC_ROTATION "Sets the page rotation mode of the scanner" #define SANE_NAME_DESKEW "deskew" #define SANE_TITLE_DESKEW "Page Deskew" #define SANE_DESC_DESKEW "Enable Deskew Mode" #define SANE_NAME_TIMEOUT_ADF "timeout-adf" #define SANE_TITLE_TIMEOUT_ADF "ADF Timeout" #define SANE_DESC_TIMEOUT_ADF "Sets the timeout in seconds for the ADF" #define SANE_NAME_TIMEOUT_MANUAL "timeout-manual" #define SANE_TITLE_TIMEOUT_MANUAL "Manual Timeout" #define SANE_DESC_TIMEOUT_MANUAL "Sets the timeout in seconds for manual feeder" #define SANE_NAME_BATCH "batch" #define SANE_TITLE_BATCH "Batch" #define SANE_DESC_BATCH "Enable Batch Mode" #define SANE_NAME_CHECK_ADF "check-adf" #define SANE_TITLE_CHECK_ADF "Check ADF" #define SANE_DESC_CHECK_ADF "Check ADF Status prior to starting scan" #define SANE_NAME_DUPLEX "duplex" #define SANE_TITLE_DUPLEX "Duplex" #define SANE_DESC_DUPLEX "Enable Duplex (Dual-Sided) Scanning" #define SANE_NAME_BARCODE_SEARCH_COUNT "barcode-search-count" #define SANE_TITLE_BARCODE_SEARCH_COUNT "Barcode Search Count" #define SANE_DESC_BARCODE_SEARCH_COUNT "Number of barcodes to search for in the scanned image" #define SANE_NAME_BARCODE_HMIN "barcode-hmin" #define SANE_TITLE_BARCODE_HMIN "Barcode Minimum Height" #define SANE_DESC_BARCODE_HMIN "Sets the Barcode Minimum Height (larger values increase recognition speed)" #define SANE_NAME_BARCODE_SEARCH_MODE "barcode-search-mode" #define SANE_TITLE_BARCODE_SEARCH_MODE "Barcode Search Mode" #define SANE_DESC_BARCODE_SEARCH_MODE "Chooses the orientation of barcodes to be searched" #define SANE_NAME_BARCODE_SEARCH_TIMEOUT "barcode-search-timeout" #define SANE_TITLE_BARCODE_SEARCH_TIMEOUT "Barcode Search Timeout" #define SANE_DESC_BARCODE_SEARCH_TIMEOUT "Sets the timeout for barcode searching" #define SANE_NAME_BARCODE_SEARCH_BAR "barcode-search-bar" #define SANE_TITLE_BARCODE_SEARCH_BAR "Barcode Search Bar" #define SANE_DESC_BARCODE_SEARCH_BAR "Specifies the barcode type to search for" #define SANE_NAME_SECTION "section" #define SANE_TITLE_SECTION "Image/Barcode Search Sections" #define SANE_DESC_SECTION "Specifies an image section and/or a barcode search region" #define SANE_NAME_BARCODE_RELMAX "barcode-relmax" #define SANE_TITLE_BARCODE_RELMAX "Barcode RelMax" #define SANE_DESC_BARCODE_RELMAX "Specifies the maximum relation from the widest to the smallest bar" #define SANE_NAME_BARCODE_BARMIN "barcode-barmin" #define SANE_TITLE_BARCODE_BARMIN "Barcode Bar Minimum" #define SANE_DESC_BARCODE_BARMIN "Specifies the minimum number of bars in Bar/Patch code" #define SANE_NAME_BARCODE_BARMAX "barcode-barmax" #define SANE_TITLE_BARCODE_BARMAX "Barcode Bar Maximum" #define SANE_DESC_BARCODE_BARMAX "Specifies the maximum number of bars in a Bar/Patch code" #define SANE_NAME_BARCODE_CONTRAST "barcode-contrast" #define SANE_TITLE_BARCODE_CONTRAST "Barcode Contrast" #define SANE_DESC_BARCODE_CONTRAST "Specifies the image contrast used in decoding. Use higher values when " \ "there are more white pixels in the code" #define SANE_NAME_BARCODE_PATCHMODE "barcode-patchmode" #define SANE_TITLE_BARCODE_PATCHMODE "Barcode Patch Mode" #define SANE_DESC_BARCODE_PATCHMODE "Controls Patch Code detection." #define SANE_NAME_CONTROL_PANEL "control-panel" #define SANE_TITLE_CONTROL_PANEL "Control Panel " #define SANE_DESC_CONTROL_PANEL "Enables the scanner's control panel" #define SANE_NAME_ACE_FUNCTION "ace-function" #define SANE_TITLE_ACE_FUNCTION "ACE Function" #define SANE_DESC_ACE_FUNCTION "ACE Function" #define SANE_NAME_ACE_SENSITIVITY "ace-sensitivity" #define SANE_TITLE_ACE_SENSITIVITY "ACE Sensitivity" #define SANE_DESC_ACE_SENSITIVITY "ACE Sensitivity" #define SANE_NAME_ICON_WIDTH "icon-width" #define SANE_TITLE_ICON_WIDTH "Icon Width" #define SANE_DESC_ICON_WIDTH "Width of icon (thumbnail) image in pixels" #define SANE_NAME_ICON_LENGTH "icon-length" #define SANE_TITLE_ICON_LENGTH "Icon Length" #define SANE_DESC_ICON_LENGTH "Length of icon (thumbnail) image in pixels" #define SANE_NAME_PAPER_SIZE "paper-size" #define SANE_TITLE_PAPER_SIZE "Paper Size" #define SANE_DESC_PAPER_SIZE "Specify the scan window geometry by specifying the paper size " \ "of the documents to be scanned" #define SANE_NAME_INQUIRY "inquiry" #define SANE_TITLE_INQUIRY "Inquiry Data" #define SANE_DESC_INQUIRY "Displays scanner inquiry data" /* low level SCSI commands and buffers */ /* SCSI commands */ #define BH_SCSI_TEST_UNIT_READY 0x00 #define BH_SCSI_SET_WINDOW 0x24 #define BH_SCSI_GET_WINDOW 0x25 #define BH_SCSI_READ_SCANNED_DATA 0x28 #define BH_SCSI_INQUIRY 0x12 #define BH_SCSI_MODE_SELECT 0x15 #define BH_SCSI_START_SCAN 0x1b #define BH_SCSI_MODE_SENSE 0x1a #define BH_SCSI_GET_BUFFER_STATUS 0x34 #define BH_SCSI_OBJECT_POSITION 0x31 /* page codes used with BH_SCSI_INQUIRY */ #define BH_INQUIRY_STANDARD_PAGE_CODE 0x00 #define BH_INQUIRY_VPD_PAGE_CODE 0xC0 #define BH_INQUIRY_JIS_PAGE_CODE 0xF0 /* page codes used with BH_SCSI_MODE_SELECT and BH_SCSI_MODE_SENSE */ #define BH_MODE_MEASUREMENT_PAGE_CODE 0x03 #define BH_MODE_TIMEOUT_PAGE_CODE 0x20 #define BH_MODE_ICON_PAGE_CODE 0x21 #define BH_MODE_BARCODE_PRIORITY_PAGE_CODE 0x30 #define BH_MODE_BARCODE_PARAM1_PAGE_CODE 0x31 #define BH_MODE_BARCODE_PARAM2_PAGE_CODE 0x32 #define BH_MODE_BARCODE_PARAM3_PAGE_CODE 0x32 /* data type codes used with BH_SCSI_READ_SCANNED_DATA */ #define BH_SCSI_READ_TYPE_FRONT 0x80 /* 0x81 thru 0x88 read front page sections 1 thru 8 respectively */ #define BH_SCSI_READ_TYPE_BACK 0x90 /* 0x91 thru 0x98 read back page sections 1 thru 8 respectively */ #define BH_SCSI_READ_TYPE_FRONT_BARCODE 0xA0 /* 0xA1 thru 0xA8 read front page barcodes in sections 1 thru 8 respectively */ #define BH_SCSI_READ_TYPE_BACK_BARCODE 0xB0 /* 0xB1 thru 0xB8 read back page barcodes in sections 1 thru 8 respectively */ #define BH_SCSI_READ_TYPE_FRONT_PATCHCODE 0xC0 /* 0xC1 thru 0xC8 read front page patchcodes in sections 1 thru 8 respectively */ #define BH_SCSI_READ_TYPE_BACK_PATCHCODE 0xD0 /* 0xD1 thru 0xD8 read back page patchcodes in sections 1 thru 8 respectively */ #define BH_SCSI_READ_TYPE_FRONT_ICON 0x89 #define BH_SCSI_READ_TYPE_BACK_ICON 0x99 /* this one is not a real readtype; it's used to help transfer the barcode file */ #define BH_SCSI_READ_TYPE_SENDBARFILE 0xBB #define BH_HAS_IMAGE_DATA(i) ((i) >= BH_SCSI_READ_TYPE_FRONT && \ (i) <= BH_SCSI_READ_TYPE_BACK_ICON) /* batchmode codes used with BH_SCSI_SET_WINDOW */ #define BH_BATCH_DISABLE 0x00 #define BH_BATCH_ENABLE 0x01 #define BH_BATCH_TERMINATE 0x02 #define BH_BATCH_ABORT 0x03 /* deskew mode codes used with BH_SCSI_SET_WINDOW */ #define BH_DESKEW_DISABLE 0x00 /* border detection is assumed, see page 3-37 of 8000 manual */ #define BH_DESKEW_ENABLE 0x04 /* deskew and border detection */ /* used with BH_SCSI_SET_WINDOW, BH_SCSI_GET_WINDOW */ typedef struct _BH_SectionBlock { SANE_Byte ul_x[4]; SANE_Byte ul_y[4]; SANE_Byte width[4]; SANE_Byte length[4]; SANE_Byte compressiontype; SANE_Byte compressionarg; SANE_Byte reserved[6]; } BH_SectionBlock; /* used with BH_SCSI_SET_WINDOW, BH_SCSI_GET_WINDOW */ struct window_data { /* window descriptor block byte layout */ SANE_Byte windowid; /* 0 */ SANE_Byte autoborder; /* 1 */ SANE_Byte xres[2]; /* 2,3 */ SANE_Byte yres[2]; /* 4,5 */ SANE_Byte ulx[4]; /* 6-9 */ SANE_Byte uly[4]; /* 10-13 */ SANE_Byte windowwidth[4]; /* 14-17 */ SANE_Byte windowlength[4]; /* 18-21 */ SANE_Byte brightness; /* 22 */ SANE_Byte threshold; /* 23 */ SANE_Byte contrast; /* 24 */ SANE_Byte imagecomposition; /* 25 */ SANE_Byte bitsperpixel; /* 26 */ SANE_Byte halftonecode; /* 27 */ SANE_Byte halftoneid; /* 28 */ SANE_Byte paddingtype; /* 29 */ SANE_Byte bitordering[2]; /* 30,31 */ SANE_Byte compressiontype; /* 32 */ SANE_Byte compressionarg; /* 33 */ SANE_Byte reserved2[6]; /* 34-39 */ SANE_Byte remote; /* 40 */ SANE_Byte acefunction; /* 41 */ SANE_Byte acesensitivity; /* 42 */ SANE_Byte batchmode; /* 43 */ SANE_Byte reserved3[2]; /* 44,45 */ SANE_Byte border_rotation; /* 46 added this for copiscan 8080 */ SANE_Byte reserved4[17]; /* 47-63 added this for copiscan 8080 */ BH_SectionBlock sectionblock[NUM_SECTIONS]; }; /* used with BH_SCSI_READ_SCANNED_DATA */ /* structure for returned decoded barcode data */ struct barcode_data { SANE_Byte reserved1[2]; SANE_Byte barcodetype[2]; SANE_Byte statusflag[2]; SANE_Byte barcodeorientation[2]; SANE_Byte posxa[2]; SANE_Byte posya[2]; SANE_Byte posxb[2]; SANE_Byte posyb[2]; SANE_Byte posxc[2]; SANE_Byte posyc[2]; SANE_Byte posxd[2]; SANE_Byte posyd[2]; SANE_Byte barcodesearchtime[2]; SANE_Byte reserved2[13]; SANE_Byte barcodelen; SANE_Byte barcodedata[160]; }; /* structure for returned icon data block */ struct icon_data { SANE_Byte windowwidth[4]; SANE_Byte windowlength[4]; SANE_Byte iconwidth[4]; SANE_Byte iconwidthbytes[4]; SANE_Byte iconlength[4]; SANE_Byte bitordering; SANE_Byte reserved[7]; SANE_Byte icondatalen[4]; }; /* used with BH_SCSI_INQUIRY */ /* Standard Data [EVPD=0] */ struct inquiry_standard_data { SANE_Byte devtype; SANE_Byte reserved[7]; SANE_Byte vendor[8]; SANE_Byte product[16]; SANE_Byte revision[4]; }; /* VPD Information [EVPD=1, PageCode=C0H] */ struct inquiry_vpd_data { SANE_Byte devtype; SANE_Byte pagecode; SANE_Byte reserved1; SANE_Byte alloclen; SANE_Byte adf; SANE_Byte reserved2[2]; SANE_Byte imagecomposition; SANE_Byte imagedataprocessing[2]; SANE_Byte compression; SANE_Byte reserved3; SANE_Byte sizerecognition; SANE_Byte optionalfeatures; SANE_Byte xmaxoutputbytes[2]; }; /* JIS Information [EVPD=1, PageCode=F0H] */ struct inquiry_jis_data { SANE_Byte devtype; SANE_Byte pagecode; SANE_Byte jisversion; SANE_Byte reserved1; SANE_Byte alloclen; SANE_Byte basicxres[2]; SANE_Byte basicyres[2]; SANE_Byte resolutionstep; SANE_Byte maxxres[2]; SANE_Byte maxyres[2]; SANE_Byte minxres[2]; SANE_Byte minyres[2]; SANE_Byte standardres[2]; SANE_Byte windowwidth[4]; SANE_Byte windowlength[4]; SANE_Byte functions; SANE_Byte reserved2; }; /* used with BH_SCSI_MODE_SELECT and BH_SCSI_MODE_SENSE */ /* Scanning Measurement Parameters PageCode=03H */ struct mode_page_03 { SANE_Byte modedatalen; SANE_Byte mediumtype; SANE_Byte devicespecificparam; SANE_Byte blockdescriptorlen; SANE_Byte pagecode; SANE_Byte paramlen; SANE_Byte bmu; SANE_Byte reserved1; SANE_Byte mud[2]; SANE_Byte reserved2[2]; }; /* Scan Command Timeout PageCode=20H */ struct mode_page_20 { SANE_Byte modedatalen; SANE_Byte mediumtype; SANE_Byte devicespecificparam; SANE_Byte blockdescriptorlen; SANE_Byte pagecode; SANE_Byte paramlen; SANE_Byte timeoutmanual; SANE_Byte timeoutadf; SANE_Byte reserved[4]; }; /* Icon Definition PageCode=21H */ struct mode_page_21 { SANE_Byte modedatalen; SANE_Byte mediumtype; SANE_Byte devicespecificparam; SANE_Byte blockdescriptorlen; SANE_Byte pagecode; SANE_Byte paramlen; SANE_Byte iconwidth[2]; SANE_Byte iconlength[2]; SANE_Byte reserved[2]; }; /* Bar/Patch Code search priority order PageCode=30H */ struct mode_page_30 { SANE_Byte modedatalen; SANE_Byte mediumtype; SANE_Byte devicespecificparam; SANE_Byte blockdescriptorlen; SANE_Byte pagecode; SANE_Byte paramlen; SANE_Byte priority[6]; }; /* Bar/Patch Code search parameters 1 of 3 PageCode=31H */ struct mode_page_31 { SANE_Byte modedatalen; SANE_Byte mediumtype; SANE_Byte devicespecificparam; SANE_Byte blockdescriptorlen; SANE_Byte pagecode; SANE_Byte paramlen; SANE_Byte minbarheight[2]; SANE_Byte searchcount; SANE_Byte searchmode; SANE_Byte searchtimeout[2]; }; /* Bar/Patch Code search parameters 2 of 3 PageCode=32H */ struct mode_page_32 { SANE_Byte modedatalen; SANE_Byte mediumtype; SANE_Byte devicespecificparam; SANE_Byte blockdescriptorlen; SANE_Byte pagecode; SANE_Byte paramlen; SANE_Byte relmax[2]; SANE_Byte barmin[2]; SANE_Byte barmax[2]; }; /* Bar/Patch Code search parameters 3 of 3 PageCode=33H */ struct mode_page_33 { SANE_Byte modedatalen; SANE_Byte mediumtype; SANE_Byte devicespecificparam; SANE_Byte blockdescriptorlen; SANE_Byte pagecode; SANE_Byte paramlen; SANE_Byte barcodecontrast[2]; SANE_Byte patchmode[2]; SANE_Byte reserved[2]; }; #ifndef sane_isbasicframe #define SANE_FRAME_TEXT 10 #define SANE_FRAME_JPEG 11 #define SANE_FRAME_G31D 12 #define SANE_FRAME_G32D 13 #define SANE_FRAME_G42D 14 #define sane_strframe(f) ( (f) == SANE_FRAME_GRAY ? "gray" : \ (f) == SANE_FRAME_RGB ? "RGB" : \ (f) == SANE_FRAME_RED ? "red" : \ (f) == SANE_FRAME_GREEN ? "green" : \ (f) == SANE_FRAME_BLUE ? "blue" : \ (f) == SANE_FRAME_TEXT ? "text" : \ (f) == SANE_FRAME_JPEG ? "jpeg" : \ (f) == SANE_FRAME_G31D ? "g31d" : \ (f) == SANE_FRAME_G32D ? "g32d" : \ (f) == SANE_FRAME_G42D ? "g42d" : \ "unknown" ) #define sane_isbasicframe(f) ( (f) == SANE_FRAME_GRAY || \ (f) == SANE_FRAME_RGB || \ (f) == SANE_FRAME_RED || \ (f) == SANE_FRAME_GREEN || \ (f) == SANE_FRAME_BLUE ) #endif #endif /* BH_H */ backends-1.3.0/backend/canon-sane.c000066400000000000000000001703231456256263500170660ustar00rootroot00000000000000SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize) { char devnam[PATH_MAX] = "/dev/scanner"; FILE *fp; int i, j; SANE_Byte primary, secondary, inmask, priMask, secMask; DBG_INIT (); DBG (1, ">> sane_init\n"); /****** for lineart mode of FB1200S ******/ for (i = 0; i < 256; i++) { primary = secondary = 0; inmask = 0x80; priMask = 0x40; secMask = 0x80; for (j = 0; j < 4; j++) { if (i & inmask) { primary |= priMask; secondary |= secMask; } priMask = priMask >> 2; secMask = secMask >> 2; inmask = inmask >> 1; } primaryHigh[i] = primary; secondaryHigh[i] = secondary; primary = secondary = 0; priMask = 0x40; secMask = 0x80; for (j = 0; j < 4; j++) { if (i & inmask) { primary |= priMask; secondary |= secMask; } priMask = priMask >> 2; secMask = secMask >> 2; inmask = inmask >> 1; } primaryLow[i] = primary; secondaryLow[i] = secondary; } /******************************************/ #if defined PACKAGE && defined VERSION DBG (2, "sane_init: " PACKAGE " " VERSION "\n"); #endif if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); fp = sanei_config_open (CANON_CONFIG_FILE); if (fp) { char line[PATH_MAX]; size_t len; /* read config file */ /* while (fgets (line, sizeof (line), fp)) */ while (sanei_config_read (line, sizeof (line), fp)) { if (line[0] == '#') /* ignore line comments */ continue; len = strlen (line); /*if (line[len - 1] == '\n') line[--len] = '\0'; */ if (!len) continue; /* ignore empty lines */ strcpy (devnam, line); } fclose (fp); } sanei_config_attach_matching_devices (devnam, attach_one); DBG (1, "<< sane_init\n"); return SANE_STATUS_GOOD; } /**************************************************************************/ void sane_exit (void) { CANON_Device *dev, *next; DBG (1, ">> sane_exit\n"); for (dev = first_dev; dev; dev = next) { next = dev->next; free ((void *) dev->sane.name); free ((void *) dev->sane.model); free ((void *) dev); } DBG (1, "<< sane_exit\n"); } /**************************************************************************/ SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only) { static const SANE_Device **devlist = 0; CANON_Device *dev; int i; DBG (1, ">> sane_get_devices\n"); if (devlist) free (devlist); devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); if (!devlist) return (SANE_STATUS_NO_MEM); i = 0; for (dev = first_dev; dev; dev = dev->next) devlist[i++] = &dev->sane; devlist[i++] = 0; *device_list = devlist; DBG (1, "<< sane_get_devices\n"); return SANE_STATUS_GOOD; } /**************************************************************************/ SANE_Status sane_open (SANE_String_Const devnam, SANE_Handle * handle) { SANE_Status status; CANON_Device *dev; CANON_Scanner *s; int i, j, c; DBG (1, ">> sane_open\n"); if (devnam[0] == '\0') { for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->sane.name, devnam) == 0) break; } } else dev = first_dev; if (!dev) { status = attach (devnam, &dev); if (status != SANE_STATUS_GOOD) return (status); } if (!dev) return (SANE_STATUS_INVAL); s = malloc (sizeof (*s)); if (!s) return SANE_STATUS_NO_MEM; memset (s, 0, sizeof (*s)); s->fd = -1; s->hw = dev; if (s->hw->info.model == FS2710) { for (i = 0; i < 4; i++) { s->gamma_map[i][0] = '\0'; s->gamma_table[i][0] = 0; } for (j = 1; j < 4096; ++j) { /* FS2710 needs initial gamma 2.0 */ c = (int) (256.0 * pow (((double) j) / 4096.0, 0.5)); for (i = 0; i < 4; i++) { s->gamma_map[i][j] = (u_char) c; if ((j & 0xf) == 0) s->gamma_table[i][j >> 4] = c; } } s->colour = 1; s->auxbuf_len = 0; } else { for (i = 0; i < 4; ++i) { for (j = 0; j < 256; ++j) s->gamma_table[i][j] = j; } } init_options (s); if (s->hw->info.model == FB1200) s->inbuffer = malloc (30894); /* modification for FB1200S */ else s->inbuffer = malloc (15312); /* modification for FB620S */ if (!s->inbuffer) return SANE_STATUS_NO_MEM; if (s->hw->info.model == FB1200) s->outbuffer = malloc (30894); /* modification for FB1200S */ else s->outbuffer = malloc (15312); /* modification for FB620S */ if (!s->outbuffer) { free (s->inbuffer); return SANE_STATUS_NO_MEM; } s->next = first_handle; first_handle = s; *handle = s; DBG (1, "<< sane_open\n"); return SANE_STATUS_GOOD; } /**************************************************************************/ void sane_close (SANE_Handle handle) { CANON_Scanner *s = (CANON_Scanner *) handle; SANE_Status status; DBG (1, ">> sane_close\n"); if (s->val[OPT_EJECT_BEFOREEXIT].w) { if (s->fd == -1) sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, s->hw); status = medium_position (s->fd); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_close: MEDIUM POSITION failed\n"); sanei_scsi_close (s->fd); s->fd = -1; } s->AF_NOW = SANE_TRUE; DBG (1, "sane_close AF_NOW = '%d'\n", s->AF_NOW); } if (s->fd != -1) sanei_scsi_close (s->fd); if (s->inbuffer) free (s->inbuffer); /* modification for FB620S */ if (s->outbuffer) free (s->outbuffer); /* modification for FB620S */ if (s->auxbuf_len > 0) free (s->auxbuf); /* modification for FS2710S */ free (s); DBG (1, ">> sane_close\n"); } /**************************************************************************/ const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { CANON_Scanner *s = handle; DBG (21, ">> sane_get_option_descriptor option number %d\n", option); if ((unsigned) option >= NUM_OPTIONS) return (0); DBG (21, " sane_get_option_descriptor option name %s\n", option_name[option]); DBG (21, "<< sane_get_option_descriptor option number %d\n", option); return (s->opt + option); } /**************************************************************************/ SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { CANON_Scanner *s = handle; SANE_Status status; SANE_Word w, cap; SANE_Byte gbuf[4096]; size_t buf_size; int i, neg, gamma_component, int_t, transfer_data_type; time_t dtime, rt; DBG (21, ">> sane_control_option %s\n", option_name[option]); if (info) *info = 0; if (s->scanning == SANE_TRUE) { DBG (21, ">> sane_control_option: device is busy scanning\n"); return (SANE_STATUS_DEVICE_BUSY); } if (option >= NUM_OPTIONS) return (SANE_STATUS_INVAL); cap = s->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) return (SANE_STATUS_INVAL); if (action == SANE_ACTION_GET_VALUE) { DBG (21, "sane_control_option get value of %s\n", option_name[option]); switch (option) { /* word options: */ case OPT_FLATBED_ONLY: case OPT_TPU_ON: case OPT_TPU_PN: case OPT_TPU_TRANSPARENCY: case OPT_RESOLUTION_BIND: case OPT_HW_RESOLUTION_ONLY: /* 990320, ss */ case OPT_X_RESOLUTION: case OPT_Y_RESOLUTION: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_NUM_OPTS: case OPT_BRIGHTNESS: case OPT_CONTRAST: case OPT_CUSTOM_GAMMA: case OPT_CUSTOM_GAMMA_BIND: case OPT_HNEGATIVE: /* case OPT_GRC: */ case OPT_MIRROR: case OPT_AE: case OPT_PREVIEW: case OPT_BIND_HILO: case OPT_HILITE_R: case OPT_SHADOW_R: case OPT_HILITE_G: case OPT_SHADOW_G: case OPT_HILITE_B: case OPT_SHADOW_B: case OPT_EJECT_AFTERSCAN: case OPT_EJECT_BEFOREEXIT: case OPT_THRESHOLD: case OPT_AF: case OPT_AF_ONCE: case OPT_FOCUS: if ((option >= OPT_NEGATIVE) && (option <= OPT_SHADOW_B)) { DBG (21, "GET_VALUE for %s: s->val[%s].w = %d\n", option_name[option], option_name[option], s->val[option].w); } *(SANE_Word *) val = s->val[option].w; DBG (21, "value for option %s: %d\n", option_name[option], s->val[option].w); return (SANE_STATUS_GOOD); case OPT_GAMMA_VECTOR: case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: memset (gbuf, 0, sizeof (gbuf)); buf_size = 256; transfer_data_type = 0x03; DBG (21, "sending GET_DENSITY_CURVE\n"); if (s->val[OPT_CUSTOM_GAMMA_BIND].w == SANE_TRUE) /* If using bind analog gamma, option will be OPT_GAMMA_VECTOR. In this case, use the curve for green */ gamma_component = 2; else /* Else use a different index for each curve */ gamma_component = option - OPT_GAMMA_VECTOR; /* Now get the values from the scanner */ if (s->hw->info.model != FS2710) { sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, s->hw); status = get_density_curve (s->fd, gamma_component, gbuf, &buf_size, transfer_data_type); sanei_scsi_close (s->fd); s->fd = -1; if (status != SANE_STATUS_GOOD) { DBG (21, "GET_DENSITY_CURVE\n"); return (SANE_STATUS_INVAL); } } else status = get_density_curve_fs2710 (s, gamma_component, gbuf, &buf_size); neg = (s->hw->info.is_filmscanner) ? strcmp (filmtype_list[1], s->val[OPT_NEGATIVE].s) : s->val[OPT_HNEGATIVE].w; for (i = 0; i < 256; i++) { if (!neg) s->gamma_table[option - OPT_GAMMA_VECTOR][i] = (SANE_Int) gbuf[i]; else s->gamma_table[option - OPT_GAMMA_VECTOR][i] = 255 - (SANE_Int) gbuf[255 - i]; } memcpy (val, s->val[option].wa, s->opt[option].size); DBG (21, "value for option %s: %d\n", option_name[option], s->val[option].w); return (SANE_STATUS_GOOD); /* string options: */ case OPT_TPU_DCM: case OPT_TPU_FILMTYPE: case OPT_MODE: case OPT_NEGATIVE: case OPT_NEGATIVE_TYPE: case OPT_SCANNING_SPEED: strcpy (val, s->val[option].s); DBG (21, "value for option %s: %s\n", option_name[option], s->val[option].s); return (SANE_STATUS_GOOD); default: val = 0; return (SANE_STATUS_GOOD); } } else if (action == SANE_ACTION_SET_VALUE) { DBG (21, "sane_control_option set value for %s\n", option_name[option]); if (!SANE_OPTION_IS_SETTABLE (cap)) return (SANE_STATUS_INVAL); status = sanei_constrain_value (s->opt + option, val, info); if (status != SANE_STATUS_GOOD) return status; switch (option) { /* (mostly) side-effect-free word options: */ case OPT_TPU_PN: case OPT_TPU_TRANSPARENCY: case OPT_X_RESOLUTION: case OPT_Y_RESOLUTION: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: if (info && s->val[option].w != *(SANE_Word *) val) *info |= SANE_INFO_RELOAD_PARAMS; /* fall through */ case OPT_NUM_OPTS: case OPT_BRIGHTNESS: case OPT_CONTRAST: case OPT_THRESHOLD: case OPT_HNEGATIVE: /* case OPT_GRC: */ case OPT_MIRROR: case OPT_AE: case OPT_PREVIEW: case OPT_HILITE_R: case OPT_SHADOW_R: case OPT_HILITE_G: case OPT_SHADOW_G: case OPT_HILITE_B: case OPT_SHADOW_B: case OPT_AF_ONCE: case OPT_FOCUS: case OPT_EJECT_AFTERSCAN: case OPT_EJECT_BEFOREEXIT: s->val[option].w = *(SANE_Word *) val; DBG (21, "SET_VALUE for %s: s->val[%s].w = %d\n", option_name[option], option_name[option], s->val[option].w); return (SANE_STATUS_GOOD); case OPT_RESOLUTION_BIND: if (s->val[option].w != *(SANE_Word *) val) { s->val[option].w = *(SANE_Word *) val; if (info) *info |= SANE_INFO_RELOAD_OPTIONS; if (!s->val[option].w) { /* don't bind */ s->opt[OPT_Y_RESOLUTION].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_X_RESOLUTION; s->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_X_RESOLUTION; } else { /* bind */ s->opt[OPT_Y_RESOLUTION].cap |= SANE_CAP_INACTIVE; s->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; } } return SANE_STATUS_GOOD; /* 990320, ss: switch between slider and option menu for resolution */ case OPT_HW_RESOLUTION_ONLY: if (s->val[option].w != *(SANE_Word *) val) { int iPos, xres, yres; s->val[option].w = *(SANE_Word *) val; if (info) *info |= SANE_INFO_RELOAD_OPTIONS; if (!s->val[option].w) /* use complete range */ { s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_X_RESOLUTION].constraint.range = &s->hw->info.xres_range; s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_Y_RESOLUTION].constraint.range = &s->hw->info.yres_range; } else /* use only hardware resolutions */ { s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_X_RESOLUTION].constraint.word_list = s->xres_word_list; s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_Y_RESOLUTION].constraint.word_list = s->yres_word_list; /* adjust resolutions */ xres = s->xres_word_list[1]; for (iPos = 0; iPos < s->xres_word_list[0]; iPos++) { if (s->val[OPT_X_RESOLUTION].w >= s->xres_word_list[iPos + 1]) xres = s->xres_word_list[iPos + 1]; } s->val[OPT_X_RESOLUTION].w = xres; yres = s->yres_word_list[1]; for (iPos = 0; iPos < s->yres_word_list[0]; iPos++) { if (s->val[OPT_Y_RESOLUTION].w >= s->yres_word_list[iPos + 1]) yres = s->yres_word_list[iPos + 1]; } s->val[OPT_Y_RESOLUTION].w = yres; } } return (SANE_STATUS_GOOD); case OPT_BIND_HILO: if (s->val[option].w != *(SANE_Word *) val) { s->val[option].w = *(SANE_Word *) val; if (info) *info |= SANE_INFO_RELOAD_OPTIONS; if (!s->val[option].w) { /* don't bind */ s->opt[OPT_HILITE_R].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_SHADOW_R].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_HILITE_B].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_SHADOW_B].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_HILITE_G].title = SANE_TITLE_HIGHLIGHT_G; s->opt[OPT_HILITE_G].name = SANE_NAME_HIGHLIGHT_G; s->opt[OPT_HILITE_G].desc = SANE_DESC_HIGHLIGHT_G; s->opt[OPT_SHADOW_G].title = SANE_TITLE_SHADOW_G; s->opt[OPT_SHADOW_G].name = SANE_NAME_SHADOW_G; s->opt[OPT_SHADOW_G].desc = SANE_DESC_SHADOW_G; } else { /* bind */ s->opt[OPT_HILITE_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_SHADOW_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_HILITE_B].cap |= SANE_CAP_INACTIVE; s->opt[OPT_SHADOW_B].cap |= SANE_CAP_INACTIVE; s->opt[OPT_HILITE_G].title = SANE_TITLE_HIGHLIGHT; s->opt[OPT_HILITE_G].name = SANE_NAME_HIGHLIGHT; s->opt[OPT_HILITE_G].desc = SANE_DESC_HIGHLIGHT; s->opt[OPT_SHADOW_G].title = SANE_TITLE_SHADOW; s->opt[OPT_SHADOW_G].name = SANE_NAME_SHADOW; s->opt[OPT_SHADOW_G].desc = SANE_DESC_SHADOW; } } return SANE_STATUS_GOOD; case OPT_AF: if (info && s->val[option].w != *(SANE_Word *) val) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; s->val[option].w = *(SANE_Word *) val; w = *(SANE_Word *) val; if (w) { s->opt[OPT_AF_ONCE].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_FOCUS].cap |= SANE_CAP_INACTIVE; } else { s->opt[OPT_AF_ONCE].cap |= SANE_CAP_INACTIVE; s->opt[OPT_FOCUS].cap &= ~SANE_CAP_INACTIVE; } return (SANE_STATUS_GOOD); case OPT_FLATBED_ONLY: s->val[option].w = *(SANE_Word *) val; if (s->hw->adf.Status != ADF_STAT_NONE && s->val[option].w) { /* switch on */ s->hw->adf.Priority |= 0x03; /* flatbed mode */ s->hw->adf.Feeder &= 0x00; /* autofeed off (default) */ s->hw->adf.Status = ADF_STAT_DISABLED; } /* if it isn't connected, don't bother fixing */ return SANE_STATUS_GOOD; case OPT_TPU_ON: s->val[option].w = *(SANE_Word *) val; if (s->val[option].w) /* switch on */ { s->hw->tpu.Status = TPU_STAT_ACTIVE; s->opt[OPT_TPU_TRANSPARENCY].cap &= (s->hw->tpu.ControlMode == 3) ? ~SANE_CAP_INACTIVE : ~0; s->opt[OPT_TPU_FILMTYPE].cap &= (s->hw->tpu.ControlMode == 1) ? ~SANE_CAP_INACTIVE : ~0; } else /* switch off */ { s->hw->tpu.Status = TPU_STAT_INACTIVE; s->opt[OPT_TPU_TRANSPARENCY].cap |= SANE_CAP_INACTIVE; s->opt[OPT_TPU_FILMTYPE].cap |= SANE_CAP_INACTIVE; } s->opt[OPT_TPU_PN].cap ^= SANE_CAP_INACTIVE; s->opt[OPT_TPU_DCM].cap ^= SANE_CAP_INACTIVE; if (info) *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_TPU_DCM: if (s->val[OPT_TPU_DCM].s) free (s->val[OPT_TPU_DCM].s); s->val[OPT_TPU_DCM].s = strdup (val); s->opt[OPT_TPU_TRANSPARENCY].cap |= SANE_CAP_INACTIVE; s->opt[OPT_TPU_FILMTYPE].cap |= SANE_CAP_INACTIVE; if (!strcmp (s->val[OPT_TPU_DCM].s, SANE_I18N("Correction according to transparency ratio"))) { s->hw->tpu.ControlMode = 3; s->opt[OPT_TPU_TRANSPARENCY].cap &= ~SANE_CAP_INACTIVE; } else if (!strcmp (s->val[OPT_TPU_DCM].s, SANE_I18N("Correction according to film type"))) { s->hw->tpu.ControlMode = 1; s->opt[OPT_TPU_FILMTYPE].cap &= ~SANE_CAP_INACTIVE; } else s->hw->tpu.ControlMode = 0; if (info) *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_TPU_FILMTYPE: if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); return SANE_STATUS_GOOD; case OPT_MODE: if (info && strcmp (s->val[option].s, (SANE_String) val)) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); if (!strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) || !strcmp (val, SANE_VALUE_SCAN_MODE_HALFTONE)) { /* For Lineart and Halftone: */ /* Enable "threshold" */ s->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE; /* Disable "custom gamma" and "brightness & contrast" */ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CUSTOM_GAMMA_BIND].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; } else { /* For Gray and Color modes: */ /* Disable "threshold" */ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; /* Enable "custom gamma" and "brightness & contrast" */ s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE; if (s->val[OPT_CUSTOM_GAMMA].w) { if (!strcmp (val, SANE_VALUE_SCAN_MODE_COLOR) || !strcmp (val, SANE_I18N("Fine color"))) { s->opt[OPT_CUSTOM_GAMMA_BIND].cap &= ~SANE_CAP_INACTIVE; if (s->val[OPT_CUSTOM_GAMMA_BIND].w == SANE_TRUE) { s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; } else { s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; } } else { s->opt[OPT_CUSTOM_GAMMA_BIND].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; } } else { s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE; } } return (SANE_STATUS_GOOD); case OPT_NEGATIVE: if (info && strcmp (s->val[option].s, (SANE_String) val)) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); if (!strcmp (val, SANE_I18N("Negatives"))) { s->RIF = 0; s->opt[OPT_NEGATIVE_TYPE].cap &= ~SANE_CAP_INACTIVE; if (SANE_OPTION_IS_SETTABLE(s->opt[OPT_SCANNING_SPEED].cap)) s->opt[OPT_SCANNING_SPEED].cap &= ~SANE_CAP_INACTIVE; } else { s->RIF = 1; s->opt[OPT_NEGATIVE_TYPE].cap |= SANE_CAP_INACTIVE; s->opt[OPT_SCANNING_SPEED].cap |= SANE_CAP_INACTIVE; } return (SANE_STATUS_GOOD); case OPT_NEGATIVE_TYPE: if (info && strcmp (s->val[option].s, (SANE_String) val)) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); for (i = 0; strcmp (val, negative_filmtype_list[i]); i++); s->negative_filmtype = i; return (SANE_STATUS_GOOD); case OPT_SCANNING_SPEED: if (info && strcmp (s->val[option].s, (SANE_String) val)) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); for (i = 0; strcmp (val, scanning_speed_list[i]); i++); s->scanning_speed = i; return (SANE_STATUS_GOOD); /* modification for FB620S */ case OPT_CALIBRATION_NOW: sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, s->hw); if (status == SANE_STATUS_GOOD) { status = execute_calibration (s->fd); if (status != SANE_STATUS_GOOD) { DBG (21, "EXECUTE CALIBRATION failed\n"); sanei_scsi_close (s->fd); s->fd = -1; return (SANE_STATUS_INVAL); } DBG (21, "EXECUTE CALIBRATION\n"); sanei_scsi_close (s->fd); } else DBG (1, "calibration: cannot open device file\n"); s->fd = -1; return status; case OPT_SCANNER_SELF_DIAGNOSTIC: sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, s->hw); if (status == SANE_STATUS_GOOD) { status = send_diagnostic (s->fd); { if (status != SANE_STATUS_GOOD) { DBG (21, "SEND DIAGNOSTIC error: %s\n", sane_strstatus (status)); sanei_scsi_close (s->fd); s->fd = -1; return (status); } DBG (21, "SEND DIAGNOSTIC result: %s\n", sane_strstatus (status)); sanei_scsi_close (s->fd); } } else DBG (1, "send diagnostic: cannot open device file\n"); s->fd = -1; return status; case OPT_RESET_SCANNER: sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, s->hw); if (status == SANE_STATUS_GOOD) { time (&(s->time1)); DBG (11, "time0 = %ld\n", s->time0); DBG (11, "time1 = %ld\n", s->time1); dtime = (s->time1) - (s->time0); DBG (11, "dtime = %ld\n", dtime); DBG (11, "switch_preview = %d\n", s->switch_preview); if (s->switch_preview == 0) { rt = sqrt (15 * 15 * (SANE_UNFIX (s->val[OPT_BR_Y].w)) / 297) + 0.5; rt = rt + 2; } else rt = 17; DBG (11, "SANE_UNFIX(s->val[OPT_BR_Y].w) = %f\n", SANE_UNFIX (s->val[OPT_BR_Y].w)); DBG (11, "rt = %ld\n", rt); if (dtime < rt) { int_t = (int) (rt - dtime); DBG (11, "int_t = %d\n", int_t); sleep (int_t); } status = reset_scanner (s->fd); { if (status != SANE_STATUS_GOOD) { DBG (21, "RESET SCANNER failed\n"); sanei_scsi_close (s->fd); s->fd = -1; return (status); } DBG (21, "RESET SCANNER\n"); sanei_scsi_close (s->fd); } } else DBG (1, "reset scanner: cannot open device file\n"); s->fd = -1; return status; case OPT_EJECT_NOW: sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, s->hw); status = medium_position (s->fd); if (status != SANE_STATUS_GOOD) { DBG (21, "MEDIUM POSITION failed\n"); sanei_scsi_close (s->fd); s->fd = -1; return (SANE_STATUS_INVAL); } DBG (21, "AF_NOW before = '%d'\n", s->AF_NOW); s->AF_NOW = SANE_TRUE; DBG (21, "AF_NOW after = '%d'\n", s->AF_NOW); sanei_scsi_close (s->fd); s->fd = -1; return status; case OPT_CUSTOM_GAMMA: w = *(SANE_Word *) val; if (w == s->val[OPT_CUSTOM_GAMMA].w) return SANE_STATUS_GOOD; /* no change */ if (info) *info |= SANE_INFO_RELOAD_OPTIONS; s->val[OPT_CUSTOM_GAMMA].w = w; if (w) { const char *mode = s->val[OPT_MODE].s; if (!strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY)) s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; else if (!strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) || !strcmp (mode, SANE_I18N("Fine color"))) { s->opt[OPT_CUSTOM_GAMMA_BIND].cap &= ~SANE_CAP_INACTIVE; if (s->val[OPT_CUSTOM_GAMMA_BIND].w) { s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; } else { 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; } } s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; } else { s->opt[OPT_CUSTOM_GAMMA_BIND].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE; } return SANE_STATUS_GOOD; case OPT_CUSTOM_GAMMA_BIND: w = *(SANE_Word *) val; if (w == s->val[OPT_CUSTOM_GAMMA_BIND].w) return SANE_STATUS_GOOD; /* no change */ if (info) *info |= SANE_INFO_RELOAD_OPTIONS; s->val[OPT_CUSTOM_GAMMA_BIND].w = w; if (w) { s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; } else { 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; } return SANE_STATUS_GOOD; case OPT_GAMMA_VECTOR: case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: memcpy (s->val[option].wa, val, s->opt[option].size); DBG (21, "setting gamma vector\n"); /* if (info) */ /* *info |= SANE_INFO_RELOAD_OPTIONS; */ return (SANE_STATUS_GOOD); } } DBG (1, "<< sane_control_option %s\n", option_name[option]); return (SANE_STATUS_INVAL); } /**************************************************************************/ SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters *params) { CANON_Scanner *s = handle; DBG (1, ">> sane_get_parameters\n"); if (!s->scanning) { int width, length, xres, yres; const char *mode; memset (&s->params, 0, sizeof (s->params)); width = SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w) * s->hw->info.mud / MM_PER_INCH; length = SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w) * s->hw->info.mud / MM_PER_INCH; xres = s->val[OPT_X_RESOLUTION].w; yres = s->val[OPT_Y_RESOLUTION].w; if (s->val[OPT_RESOLUTION_BIND].w || s->val[OPT_PREVIEW].w) yres = xres; /* make best-effort guess at what parameters will look like once scanning starts. */ if (xres > 0 && yres > 0 && width > 0 && length > 0) { DBG (11, "sane_get_parameters: width='%d', xres='%d', mud='%d'\n", width, xres, s->hw->info.mud); s->params.pixels_per_line = width * xres / s->hw->info.mud; DBG (11, "sane_get_parameters: length='%d', yres='%d', mud='%d'\n", length, yres, s->hw->info.mud); s->params.lines = length * yres / s->hw->info.mud; DBG (11, "sane_get_parameters: pixels_per_line='%d', lines='%d'\n", s->params.pixels_per_line, s->params.lines); } mode = s->val[OPT_MODE].s; if (!strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) || !strcmp (mode, SANE_VALUE_SCAN_MODE_HALFTONE)) { s->params.format = SANE_FRAME_GRAY; s->params.bytes_per_line = s->params.pixels_per_line / 8; /* workaround rounding problems */ s->params.pixels_per_line = s->params.bytes_per_line * 8; s->params.depth = 1; } else if (!strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY)) { s->params.format = SANE_FRAME_GRAY; s->params.bytes_per_line = s->params.pixels_per_line; s->params.depth = 8; } else if (!strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) || !strcmp (mode, SANE_I18N("Fine color"))) { s->params.format = SANE_FRAME_RGB; s->params.bytes_per_line = 3 * s->params.pixels_per_line; s->params.depth = 8; } else { s->params.format = SANE_FRAME_RGB; s->params.bytes_per_line = 6 * s->params.pixels_per_line; s->params.depth = 16; } s->params.last_frame = SANE_TRUE; } DBG (11, "sane_get_parameters: xres='%d', yres='%d', pixels_per_line='%d', " "bytes_per_line='%d', lines='%d'\n", s->xres, s->yres, s->params.pixels_per_line, s->params.bytes_per_line, s->params.lines); if (params) *params = s->params; DBG (1, "<< sane_get_parameters\n"); return (SANE_STATUS_GOOD); } /**************************************************************************/ SANE_Status sane_start (SANE_Handle handle) { char *mode_str; CANON_Scanner *s = handle; SANE_Status status; u_char wbuf[72], dbuf[28], ebuf[72]; u_char cbuf[2]; /* modification for FB620S */ size_t buf_size, i; DBG (1, ">> sane_start\n"); s->tmpfile = -1; /* for FB1200S */ /******* making a tempfile for 1200 dpi scanning of FB1200S ******/ if (s->hw->info.model == FB1200) { char tmpfilename[] = "/tmp/canon.XXXXXX"; /* for FB1200S */ s->tmpfile = mkstemp(tmpfilename); if (s->tmpfile == -1) { DBG(1, "error opening temp file %s\n", tmpfilename); DBG(1, "errno: %i; %s\n", errno, strerror(errno)); errno = 0; return (SANE_STATUS_INVAL); } DBG(1, " ****** tmpfile is opened ****** \n"); unlink(tmpfilename); } /******************************************************************/ s->scanning = SANE_FALSE; if ((s->hw->adf.Status != ADF_STAT_NONE) && (s->val[OPT_FLATBED_ONLY].w != SANE_TRUE) && (s->hw->adf.Problem != 0)) { DBG (3, "SCANNER ADF HAS A PROBLEM\n"); if (s->hw->adf.Problem & 0x08) { status = SANE_STATUS_COVER_OPEN; DBG (3, "ADF Cover Open\n"); } else if (s->hw->adf.Problem & 0x04) { status = SANE_STATUS_JAMMED; DBG (3, "ADF Paper Jam\n"); } else /* adf.Problem = 0x02 */ { status = SANE_STATUS_NO_DOCS; DBG (3, "ADF No More Documents\n"); } return status; } else if ((s->hw->adf.Status != ADF_STAT_NONE) && (s->val[OPT_FLATBED_ONLY].w == SANE_TRUE)) { set_adf_mode (s->fd, s->hw->adf.Priority); /* 2.23 define ADF Mode */ } /* First make sure we have a current parameter set. Some of the parameters will be overwritten below, but that's OK. */ status = sane_get_parameters (s, 0); if (status != SANE_STATUS_GOOD) return status; status = sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, s->hw); if (status != SANE_STATUS_GOOD) { DBG (1, "open of %s failed: %s\n", s->hw->sane.name, sane_strstatus (status)); return (status); } #if 0 /* code moved after define_scan() calls */ /* Do focus, but not for the preview */ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_FOCUS_GROUP].cap) && !s->val[OPT_PREVIEW].w && s->AF_NOW) { if ((status = do_focus (s)) != SANE_STATUS_GOOD) return (status); if (s->val[OPT_AF_ONCE].w) s->AF_NOW = SANE_FALSE; } #endif if (s->val[OPT_CUSTOM_GAMMA].w) { if ((status = do_gamma (s)) != SANE_STATUS_GOOD) return (status); } DBG (3, "attach: sending GET SCAN MODE for scan control conditions\n"); memset (ebuf, 0, sizeof (ebuf)); buf_size = 20; status = get_scan_mode (s->fd, (u_char) SCAN_CONTROL_CONDITIONS, ebuf, &buf_size); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: GET SCAN MODE for scan control conditions failed\n"); sanei_scsi_close (s->fd); return (SANE_STATUS_INVAL); } for (i = 0; i < buf_size; i++) DBG (3, "scan mode control byte[%d] = %d\n", (int) i, ebuf[i]); if (s->hw->adf.Status != ADF_STAT_NONE) { DBG (3, "attach: sending GET SCAN MODE for transparency unit\n"); memset (ebuf, 0, sizeof (ebuf)); buf_size = 12; status = get_scan_mode (s->fd, (u_char) TRANSPARENCY_UNIT, ebuf, &buf_size); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: GET SCAN MODE for transparency unit failed\n"); sanei_scsi_close (s->fd); return (SANE_STATUS_INVAL); } for (i = 0; i < buf_size; i++) DBG (3, "scan mode control byte[%d] = %d\n", (int) i, ebuf[i]); } mode_str = s->val[OPT_MODE].s; s->xres = s->val[OPT_X_RESOLUTION].w; s->yres = s->val[OPT_Y_RESOLUTION].w; if (s->val[OPT_RESOLUTION_BIND].w || s->val[OPT_PREVIEW].w) s->yres = s->xres; s->ulx = SANE_UNFIX (s->val[OPT_TL_X].w) * s->hw->info.mud / MM_PER_INCH; s->uly = SANE_UNFIX (s->val[OPT_TL_Y].w) * s->hw->info.mud / MM_PER_INCH; s->width = SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w) * s->hw->info.mud / MM_PER_INCH; s->length = SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w) * s->hw->info.mud / MM_PER_INCH; DBG (11, "s->width='%d', s->length='%d'\n", s->width, s->length); if (s->hw->info.model != CS2700 && s->hw->info.model != FS2710) { if (!strcmp (mode_str, SANE_VALUE_SCAN_MODE_LINEART) || !strcmp (mode_str, SANE_VALUE_SCAN_MODE_HALFTONE)) s->RIF = s->val[OPT_HNEGATIVE].w; else s->RIF = !s->val[OPT_HNEGATIVE].w; } s->brightness = s->val[OPT_BRIGHTNESS].w; s->contrast = s->val[OPT_CONTRAST].w; s->threshold = s->val[OPT_THRESHOLD].w; s->bpp = s->params.depth; s->GRC = s->val[OPT_CUSTOM_GAMMA].w; s->Mirror = s->val[OPT_MIRROR].w; s->AE = s->val[OPT_AE].w; s->HiliteG = s->val[OPT_HILITE_G].w; s->ShadowG = s->val[OPT_SHADOW_G].w; if (s->val[OPT_BIND_HILO].w) { s->HiliteR = s->val[OPT_HILITE_G].w; s->ShadowR = s->val[OPT_SHADOW_G].w; s->HiliteB = s->val[OPT_HILITE_G].w; s->ShadowB = s->val[OPT_SHADOW_G].w; } else { s->HiliteR = s->val[OPT_HILITE_R].w; s->ShadowR = s->val[OPT_SHADOW_R].w; s->HiliteB = s->val[OPT_HILITE_B].w; s->ShadowB = s->val[OPT_SHADOW_B].w; } if (!strcmp (mode_str, SANE_VALUE_SCAN_MODE_LINEART)) { s->image_composition = 0; } else if (!strcmp (mode_str, SANE_VALUE_SCAN_MODE_HALFTONE)) { s->image_composition = 1; } else if (!strcmp (mode_str, SANE_VALUE_SCAN_MODE_GRAY)) { s->image_composition = 2; } else if (!strcmp (mode_str, SANE_VALUE_SCAN_MODE_COLOR) || !strcmp (mode_str, SANE_I18N("Fine color"))) { s->image_composition = 5; } else if (!strcmp (mode_str, SANE_I18N("Raw"))) { s->image_composition = 5; } else { s->image_composition = 5; } memset (wbuf, 0, sizeof (wbuf)); wbuf[7] = 64; wbuf[10] = s->xres >> 8; wbuf[11] = s->xres; wbuf[12] = s->yres >> 8; wbuf[13] = s->yres; wbuf[14] = s->ulx >> 24; wbuf[15] = s->ulx >> 16; wbuf[16] = s->ulx >> 8; wbuf[17] = s->ulx; wbuf[18] = s->uly >> 24; wbuf[19] = s->uly >> 16; wbuf[20] = s->uly >> 8; wbuf[21] = s->uly; wbuf[22] = s->width >> 24; wbuf[23] = s->width >> 16; wbuf[24] = s->width >> 8; wbuf[25] = s->width; wbuf[26] = s->length >> 24; wbuf[27] = s->length >> 16; wbuf[28] = s->length >> 8; wbuf[29] = s->length; wbuf[30] = s->brightness; wbuf[31] = s->threshold; wbuf[32] = s->contrast; wbuf[33] = s->image_composition; wbuf[34] = (s->hw->info.model == FS2710) ? 12 : s->bpp; wbuf[36] = 1; wbuf[37] = (1 << 7) + 0x03; wbuf[50] = (s->GRC << 3) | (s->Mirror << 2); #if 1 wbuf[50] |= s->AE; /* AE also for preview; needed by frontend controls */ #else if (!s->val[OPT_PREVIEW].w) wbuf[50] |= s->AE; /* AE not during preview */ #endif wbuf[54] = 2; wbuf[57] = 1; wbuf[58] = 1; wbuf[59] = s->HiliteR; wbuf[60] = s->ShadowR; wbuf[62] = s->HiliteG; wbuf[64] = s->ShadowG; wbuf[70] = s->HiliteB; wbuf[71] = s->ShadowB; DBG (7, "RIF=%d, GRC=%d, Mirror=%d, AE=%d, Speed=%d\n", s->RIF, s->GRC, s->Mirror, s->AE, s->scanning_speed); DBG (7, "HR=%d, SR=%d, HG=%d, SG=%d, HB=%d, SB=%d\n", s->HiliteR, s->ShadowR, s->HiliteG, s->ShadowG, s->HiliteB, s->ShadowB); if (s->hw->info.model == FB620) /* modification for FB620S */ { wbuf[36] = 0; wbuf[37] = (s->RIF << 7) + 0x3; wbuf[50] = s->GRC << 3; wbuf[54] = 0; wbuf[57] = 0; wbuf[58] = 0; } else if (s->hw->info.model == FB1200) /* modification for FB1200S */ { #if 0 wbuf[34] = (((600 < s->val[OPT_X_RESOLUTION].w) || (600 < s->val[OPT_Y_RESOLUTION].w)) && (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) != 0)) ? 12 : s->bpp; #endif wbuf[36] = 0; wbuf[37] = (s->RIF << 7) + 0x3; wbuf[50] = (1 << 4) | (s->GRC << 3); wbuf[57] = 1; wbuf[58] = 1; } else if (s->hw->info.model == IX4015) /* modification for IX-4015 */ { wbuf[36] = 0; wbuf[37] = (s->RIF << 7); wbuf[57] = 0; wbuf[58] = 0; /* no highlight and shadow control */ wbuf[59] = 0; wbuf[60] = 0; wbuf[62] = 0; wbuf[64] = 0; wbuf[70] = 0; wbuf[71] = 0; } buf_size = sizeof (wbuf); status = set_window (s->fd, wbuf); if (status != SANE_STATUS_GOOD) { DBG (1, "SET WINDOW failed: %s\n", sane_strstatus (status)); return (status); } if (s->hw->info.model == FS2710) status = set_parameters_fs2710 (s); buf_size = sizeof (wbuf); memset (wbuf, 0, buf_size); status = get_window (s->fd, wbuf, &buf_size); if (status != SANE_STATUS_GOOD) { DBG (1, "GET WINDOW failed: %s\n", sane_strstatus (status)); return (status); } DBG (5, "xres=%d\n", (wbuf[10] << 8) + wbuf[11]); DBG (5, "yres=%d\n", (wbuf[12] << 8) + wbuf[13]); DBG (5, "ulx=%d\n", (wbuf[14] << 24) + (wbuf[15] << 16) + (wbuf[16] << 8) + wbuf[17]); DBG (5, "uly=%d\n", (wbuf[18] << 24) + (wbuf[19] << 16) + (wbuf[20] << 8) + wbuf[21]); DBG (5, "width=%d\n", (wbuf[22] << 24) + (wbuf[23] << 16) + (wbuf[24] << 8) + wbuf[25]); DBG (5, "length=%d\n", (wbuf[26] << 24) + (wbuf[27] << 16) + (wbuf[28] << 8) + wbuf[29]); DBG (5, "Highlight Red=%d\n", wbuf[59]); DBG (5, "Shadow Red=%d\n", wbuf[60]); DBG (5, "Highlight (Green)=%d\n", wbuf[62]); DBG (5, "Shadow (Green)=%d\n", wbuf[64]); DBG (5, "Highlight Blue=%d\n", wbuf[70]); DBG (5, "Shadow Blue=%d\n", wbuf[71]); if (s->hw->tpu.Status == TPU_STAT_ACTIVE || s->hw->info.is_filmscanner) { DBG (3, "sane_start: sending DEFINE SCAN MODE for transparency unit, " "NP=%d, Negative film type=%d\n", !s->RIF, s->negative_filmtype); memset (wbuf, 0, sizeof (wbuf)); wbuf[0] = 0x02; wbuf[1] = 6; wbuf[2] = 0x80; wbuf[3] = 0x05; wbuf[4] = 39; wbuf[5] = 16; wbuf[6] = !s->RIF; wbuf[7] = s->negative_filmtype; status = define_scan_mode (s->fd, TRANSPARENCY_UNIT, wbuf); /* note: If we implement a TPU for the FB1200S, we need TRANSPARENCY_UNIT_FB1200 here. */ if (status != SANE_STATUS_GOOD) { DBG (1, "define scan mode failed: %s\n", sane_strstatus (status)); return (status); } } DBG (3, "sane_start: sending DEFINE SCAN MODE for scan control " "conditions\n"); memset (wbuf, 0, sizeof (wbuf)); wbuf[0] = 0x20; if (s->hw->info.model == FB1200) { wbuf[1] = 17; wbuf[16] = 3; wbuf[17] = 8; wbuf[18] = (1 << 7) | (1 << 3); DBG (3, "sane_start: sending DEFINE SCAN MODE for scan control " "conditions of FB1200\n"); status = define_scan_mode (s->fd, SCAN_CONTROL_CON_FB1200, wbuf); } else { wbuf[1] = 14; /* For preview use always normal speed: */ if (!s->val[OPT_PREVIEW].w && s->hw->info.is_filmscanner) wbuf[11] = s->scanning_speed; wbuf[15] = (s->hw->info.model == FB620 && !strcmp (mode_str, SANE_I18N("Fine color")) && !s->val[OPT_PREVIEW].w) ? 1 << 3 : 0; status = define_scan_mode (s->fd, SCAN_CONTROL_CONDITIONS, wbuf); } if (status != SANE_STATUS_GOOD) { DBG (1, "define scan mode failed: %s\n", sane_strstatus (status)); return (status); } DBG (3, "sane_start: sending GET SCAN MODE for scan control conditions\n"); memset (ebuf, 0, sizeof (ebuf)); buf_size = sizeof (ebuf); status = get_scan_mode (s->fd, SCAN_CONTROL_CONDITIONS, ebuf, &buf_size); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: GET SCAN MODE for scan control conditions " "failed\n"); sanei_scsi_close (s->fd); return (SANE_STATUS_INVAL); } for (i = 0; i < buf_size; 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) && !s->val[OPT_PREVIEW].w && s->AF_NOW && (s->RIF || s->AE || s->scanning_speed == 0)) { if ((status = do_focus (s)) != SANE_STATUS_GOOD) return (status); if (s->val[OPT_AF_ONCE].w) s->AF_NOW = SANE_FALSE; } /* ============= modification for FB620S ============= */ DBG (3, "TEST_UNIT_READY\n"); status = test_unit_ready (s->fd); if (status != SANE_STATUS_GOOD) { DBG (1, "test unit ready failed (%s)\n", sane_strstatus (status)); sanei_scsi_close (s->fd); s->fd = -1; return (status); } if (s->hw->info.can_calibrate) { DBG (3, "sane_start: sending GET_CALIBRATION_STATUS\n"); buf_size = sizeof (cbuf); memset (cbuf, 0, buf_size); status = get_calibration_status (s->fd, cbuf, &buf_size); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: GET_CALIBRATION_STATUS failed: %s\n", sane_strstatus (status)); sanei_scsi_close (s->fd); s->fd = -1; return (status); } DBG (1, "cbuf[0] = %d\n", cbuf[0]); DBG (1, "cbuf[1] = %d\n", cbuf[1]); cbuf[0] &= 3; if (cbuf[0] == 1 || cbuf[0] == 2 || cbuf[0] == 3) { status = execute_calibration (s->fd); DBG (3, "sane_start: EXECUTE_CALIBRATION\n"); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: EXECUTE_CALIBRATION failed\n"); sanei_scsi_close (s->fd); s->fd = -1; return (status); } DBG (3, "after calibration: GET_CALIBRATION_STATUS\n"); buf_size = sizeof (cbuf); memset (cbuf, 0, buf_size); status = get_calibration_status (s->fd, cbuf, &buf_size); if (status != SANE_STATUS_GOOD) { DBG (1, "after calibration: GET_CALIBRATION_STATUS failed: %s\n", sane_strstatus (status)); sanei_scsi_close (s->fd); s->fd = -1; return (status); } DBG (1, "cbuf[0] = %d\n", cbuf[0]); DBG (1, "cbuf[1] = %d\n", cbuf[1]); } } status = scan (s->fd); if (status != SANE_STATUS_GOOD) { DBG (1, "start of scan failed: %s\n", sane_strstatus (status)); return (status); } buf_size = sizeof (dbuf); memset (dbuf, 0, buf_size); status = get_data_status (s->fd, dbuf, &buf_size); if (status != SANE_STATUS_GOOD) { DBG (1, "GET DATA STATUS failed: %s\n", sane_strstatus (status)); return (status); } DBG (5, ">> GET DATA STATUS\n"); DBG (5, "Scan Data Available=%d\n", (dbuf[9] << 16) + (dbuf[10] << 8) + dbuf[11]); DBG (5, "Magnified Width=%d\n", (dbuf[12] <<24) + (dbuf[13] << 16) + (dbuf[14] << 8) + dbuf[15]); DBG (5, "Magnified Length=%d\n", (dbuf[16] << 24) + (dbuf[17] << 16) + (dbuf[18] << 8) + dbuf[19]); DBG (5, "Rest Data=%d bytes\n", (dbuf[20] << 24) + (dbuf[21] << 16) + (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, "<< GET DATA STATUS\n"); s->bytes_to_read = s->params.bytes_per_line * s->params.lines; if (s->hw->info.model == FB1200) { if (s->bytes_to_read != (((size_t) dbuf[9] << 16) + ((size_t) dbuf[10] << 8) + (size_t) dbuf[11])) { s->params.bytes_per_line = (((size_t) dbuf[12] << 24) + ((size_t) dbuf[13] << 16) + ((size_t) dbuf[14] << 8) + (size_t)dbuf[15]); s->params.lines = (((size_t) dbuf[16] << 24) + ((size_t) dbuf[17] << 16) + ((size_t) dbuf[18] << 8) + (size_t) dbuf[19]); s->bytes_to_read = s->params.bytes_per_line * s->params.lines; mode_str = s->val[OPT_MODE].s; if (!strcmp (mode_str, SANE_VALUE_SCAN_MODE_LINEART)) { if (((600 < s->val[OPT_X_RESOLUTION].w) || (600 < s->val[OPT_Y_RESOLUTION].w))) { s->params.bytes_per_line *= 2; s->params.lines /= 2; } s->params.pixels_per_line = s->params.bytes_per_line * 8; } else if (!strcmp (mode_str, SANE_VALUE_SCAN_MODE_GRAY)) s->params.pixels_per_line = s->params.bytes_per_line; else if (!strcmp (mode_str, SANE_VALUE_SCAN_MODE_COLOR) || !strcmp (mode_str, SANE_I18N("Fine color"))) s->params.pixels_per_line = s->params.bytes_per_line / 3; else s->params.pixels_per_line = s->params.bytes_per_line / 6; } } DBG (1, "%d pixels per line, %d bytes, %d lines high, total %lu bytes, " "dpi=%d\n", s->params.pixels_per_line, s->params.bytes_per_line, s->params.lines, (u_long) s->bytes_to_read, s->val[OPT_X_RESOLUTION].w); /**************************************************/ /* modification for FB620S and FB1200S */ s->buf_used = 0; s->buf_pos = 0; /**************************************************/ s->scanning = SANE_TRUE; DBG (1, "<< sane_start\n"); return (SANE_STATUS_GOOD); } /**************************************************************************/ static SANE_Status sane_read_direct (SANE_Handle handle, SANE_Byte *buf, SANE_Int max_len, SANE_Int *len) { CANON_Scanner *s = handle; SANE_Status status; size_t nread; DBG (21, ">> sane_read\n"); *len = 0; nread = max_len; 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); return (SANE_STATUS_EOF); } if (!s->scanning) return (do_cancel (s)); if (nread > s->bytes_to_read) nread = s->bytes_to_read; status = read_data (s->fd, buf, &nread); if (status != SANE_STATUS_GOOD) { do_cancel (s); return (SANE_STATUS_IO_ERROR); } *len = nread; s->bytes_to_read -= nread; 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); } /**************************************************************************/ static SANE_Status read_fs2710 (SANE_Handle handle, SANE_Byte *buf, SANE_Int max_len, SANE_Int *len) { CANON_Scanner *s = handle; SANE_Status status; int c; size_t i, nread, nread2; 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", (int) nread, (int) s->bytes_to_read); if (nread > s->bytes_to_read) nread = s->bytes_to_read; if (s->bytes_to_read == 0) { do_cancel (s); return (SANE_STATUS_EOF); } if (!s->scanning) return (do_cancel (s)); /* 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, SANE_VALUE_SCAN_MODE_COLOR)) { if (max_len > s->auxbuf_len) { /* extend buffer? */ if (s->auxbuf_len > 0) free (s->auxbuf); s->auxbuf_len = max_len; if ((s->auxbuf = (u_char *) malloc (2 * max_len)) == NULL) { DBG (1, "sane_read buffer size insufficient\n"); do_cancel (s); return SANE_STATUS_NO_MEM; } } nread2 = 2 * nread; if ((status = read_data (s->fd, s->auxbuf, &nread2)) != SANE_STATUS_GOOD) { do_cancel (s); return (SANE_STATUS_IO_ERROR); } nread = nread2 / 2; for (i = 0, p = s->auxbuf; i < nread; i++) { c = *p++ >> 4; c |= *p++ << 4; *buf++ = s->gamma_map[s->colour++][c]; if (s->colour > 3) s->colour = 1; /* cycle through RGB */ } } else { if ((status = read_data (s->fd, buf, &nread)) != SANE_STATUS_GOOD) { 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++; *p = b; } #endif } *len = nread; s->bytes_to_read -= nread; 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); } /**************************************************************************/ /* modification for FB620S */ static SANE_Status read_fb620 (SANE_Handle handle, SANE_Byte *buf, SANE_Int max_len, SANE_Int *len) { CANON_Scanner *s = handle; SANE_Status status; SANE_Byte *out, *red, *green, *blue; SANE_Int ncopy; size_t nread = 0, i, pixel_per_line; DBG (21, ">> read_fb620\n"); *len = 0; 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) { s->reset_flag = 0; /* no reset */ do_cancel (s); DBG (21, "do_cancel(EOF)\n"); DBG (21, "reset_flag = %d\n", s->reset_flag); return (SANE_STATUS_EOF); } else { s->reset_flag = 1; /* do reset */ DBG (21, "reset_flag = %d\n", s->reset_flag); } DBG (21, " read_fb620: buf_pos=%d, buf_used=%d\n", s->buf_pos, s->buf_used); if (!s->scanning) return (do_cancel (s)); if (s->buf_pos < s->buf_used) { ncopy = s->buf_used - s->buf_pos; if (ncopy > max_len) ncopy = max_len; memcpy (buf, &(s->outbuffer[s->buf_pos]), ncopy); max_len -= ncopy; *len += ncopy; buf = &(buf[ncopy]); s->buf_pos += ncopy; } if (s->buf_pos >= s->buf_used && s->bytes_to_read) { /* buffer is empty: read in scan line and sort color data as shown above */ nread = s->params.bytes_per_line; if (nread > s->bytes_to_read) nread = s->bytes_to_read; status = read_data (s->fd, s->inbuffer, &nread); if (status != SANE_STATUS_GOOD) { do_cancel (s); return (SANE_STATUS_IO_ERROR); } s->buf_used = s->params.bytes_per_line; out = s->outbuffer; pixel_per_line = s->params.pixels_per_line; red = s->inbuffer; green = &(s->inbuffer[pixel_per_line]); blue = &(s->inbuffer[2 * pixel_per_line]); for (i = 0; i < pixel_per_line; i++) { *out++ = *red++; *out++ = *green++; *out++ = *blue++; } s->buf_pos = 0; s->bytes_to_read -= s->buf_used; } if (max_len && s->buf_pos < s->buf_used) { ncopy = s->buf_used - s->buf_pos; if (ncopy > max_len) ncopy = max_len; memcpy (buf, &(s->outbuffer[s->buf_pos]), ncopy); *len += ncopy; s->buf_pos += ncopy; } DBG (21, "<< read_fb620\n"); return (SANE_STATUS_GOOD); } /**************************************************************************/ /* modification for FB1200S */ static SANE_Status read_fb1200 (SANE_Handle handle, SANE_Byte *buf, SANE_Int max_len, SANE_Int *len) { CANON_Scanner *s = handle; SANE_Status status; SANE_Byte *firstimage, *secondimage/*, inmask, outmask, outbyte, primaryHigh[256], primaryLow[256], secondaryHigh[256], secondaryLow[256] */; SANE_Int ncopy; u_char dbuf[28]; size_t buf_size, nread, remain, nwritten, nremain, pos, pix, pixel_per_line, byte, byte_per_line/*, bit*/; ssize_t wres, readres; int maxpix; DBG (21, ">> read_fb1200\n"); buf_size = sizeof (dbuf); memset (dbuf, 0, buf_size); status = get_data_status (s->fd, dbuf, &buf_size); if (status != SANE_STATUS_GOOD) { DBG (1, "GET DATA STATUS failed: %s\n", sane_strstatus (status)); return (status); } DBG (5, ">> GET DATA STATUS\n"); DBG (5, "Scan Data Available=%d\n", (dbuf[9] << 16) + (dbuf[10] << 8) + dbuf[11]); DBG (5, "Rest Data=%d bytes\n", (dbuf[20] << 24) + (dbuf[21] << 16) + (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", (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", (int) s->bytes_to_read); if (s->bytes_to_read == 0 && s->buf_pos == s->buf_used) { do_cancel (s); DBG (21, "do_cancel(EOF)\n"); return (SANE_STATUS_EOF); } DBG (21, " read_fb1200: buf_pos=%d, buf_used=%d\n", s->buf_pos, s->buf_used); if (!s->scanning) return (do_cancel (s)); if (s->buf_pos >= s->buf_used && s->bytes_to_read) { nread = s->params.bytes_per_line / 2; if (nread > s->bytes_to_read) nread = s->bytes_to_read; status = read_data (s->fd, s->inbuffer, &nread); if (status != SANE_STATUS_GOOD) { do_cancel (s); return (SANE_STATUS_IO_ERROR); } /**** save the primary scan data to tmpfile ****/ if ((SANE_Int) s->bytes_to_read > s->params.bytes_per_line * s->params.lines / 2) { remain = nread; nwritten = 0; while (remain) { errno = 0; wres = write (s->tmpfile, &s->inbuffer[nwritten], remain); if (wres == -1) { DBG(1, "error write tmp file: %i, %s\n", errno, strerror(errno)); do_cancel(s); return (SANE_STATUS_NO_MEM); } remain -= wres; nwritten += wres; } s->bytes_to_read -= nread; if ((SANE_Int) s->bytes_to_read <= s->params.bytes_per_line * s->params.lines / 2) { if ((SANE_Int) s->bytes_to_read < s->params.bytes_per_line * s->params.lines / 2) DBG(1, "warning: read more data for the primary scan " "than expected\n"); lseek (s->tmpfile, 0L, SEEK_SET); *len = 0; *buf = 0; return (SANE_STATUS_GOOD); } DBG(1, "writing: the primary data to tmp file\n"); *len = 0; *buf = 0; return (SANE_STATUS_GOOD); } /** the primary scan data from tmpfile and the secondary scan data are merged **/ s->buf_used = s->params.bytes_per_line; byte_per_line = s->params.bytes_per_line; pixel_per_line = s->params.pixels_per_line; /** read an entire scan line from the primary scan **/ remain = nread; pos = 0; firstimage = &(s->inbuffer[byte_per_line/2]); while (remain > 0) { nremain = (remain < SSIZE_MAX)? remain: SSIZE_MAX; errno = 0; readres = read (s->tmpfile, &(firstimage[pos]), nremain); if (readres == -1) { DBG(1, "error reading tmp file: %i %s\n", errno, strerror(errno)); do_cancel(s); return (SANE_STATUS_IO_ERROR); } if (readres == 0) { DBG(1, "0 byte read from temp file. premature EOF?\n"); return (SANE_STATUS_INVAL); /* perhaps an error return? */ } DBG(1, "reading: the primary data from tmp file\n"); remain -= readres; pos += readres; } secondimage = s->inbuffer; if (!strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR)) { maxpix = pixel_per_line / 2; for (pix = 0; (int) pix < maxpix; pix++) { s->outbuffer[6 * pix] = secondimage[3 * pix]; s->outbuffer[6 * pix + 1] = secondimage[3 * pix + 1]; s->outbuffer[6 * pix + 2] = secondimage[3 * pix + 2]; s->outbuffer[6 * pix + 3] = firstimage[3 * pix]; s->outbuffer[6 * pix + 4] = firstimage[3 * pix + 1]; s->outbuffer[6 * pix + 5] = firstimage[3 * pix + 2]; } } else if (!strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY)) { for (pix = 0; pix < pixel_per_line / 2; pix++) { s->outbuffer[2 * pix] = secondimage[pix]; s->outbuffer[2 * pix + 1] = firstimage[pix]; } } else /* for lineart mode */ { maxpix = byte_per_line / 2; for (byte = 0; (int) byte < maxpix; byte++) { s->outbuffer[2 * byte] = primaryHigh[firstimage[byte]] | secondaryHigh[secondimage[byte]]; s->outbuffer[2 * byte + 1] = primaryLow[firstimage[byte]] | secondaryLow[secondimage[byte]]; #if 0 inmask = 128; outmask = 128; outbyte = 0; for (bit = 0; bit < 4; bit++) { if (inmask == (secondimage[byte] & inmask)) outbyte = outbyte | outmask; outmask = outmask >> 1; if (inmask == (firstimage[byte] & inmask)) outbyte = outbyte | outmask; outmask = outmask >> 1; inmask = inmask >> 1; } s->outbuffer[2 * byte] = outbyte; outmask = 128; outbyte = 0; for (bit = 0; bit < 4; bit++) { if (inmask == (secondimage[byte] & inmask)) outbyte = outbyte | outmask; outmask = outmask >> 1; if (inmask == (firstimage[byte] & inmask)) outbyte = outbyte | outmask; outmask = outmask >> 1; inmask = inmask >> 1; } s->outbuffer[2 * byte + 1] = outbyte; #endif } } s->buf_pos = 0; s->bytes_to_read -= nread; } if (max_len && s->buf_pos < s->buf_used) { ncopy = s->buf_used - s->buf_pos; if (ncopy > max_len) ncopy = max_len; memcpy (buf, &(s->outbuffer[s->buf_pos]), ncopy * 2); *len += ncopy; s->buf_pos += ncopy; } DBG (21, "<< read_fb1200\n"); return (SANE_STATUS_GOOD); } /**************************************************************************/ SANE_Status sane_read (SANE_Handle handle, SANE_Byte *buf, SANE_Int max_len, SANE_Int *len) { CANON_Scanner *s = handle; SANE_Status status; if (s->hw->info.model == FB620 && s->params.format == SANE_FRAME_RGB) status = read_fb620 (handle, buf, max_len, len); else if (s->hw->info.model == FS2710) status = read_fs2710 (handle, buf, max_len, len); else if (s->hw->info.model == FB1200 && ((600 < s->val[OPT_X_RESOLUTION].w) || (600 < s->val[OPT_Y_RESOLUTION].w))) status = read_fb1200 (handle, buf, max_len, len); else status = sane_read_direct (handle, buf, max_len, len); if (s->time0 == -1) s->time0 = 0; else time (&(s->time0)); DBG (11, "sane_read: time0 = %ld\n", s->time0); s->switch_preview = s->val[OPT_PREVIEW].w; return (status); } /**************************************************************************/ void sane_cancel (SANE_Handle handle) { CANON_Scanner *s = handle; DBG (1, ">> sane_cancel\n"); /******** for FB1200S ************/ if(s->hw->info.model == FB1200) { if (s->tmpfile != -1) { close (s->tmpfile); DBG(1, " ****** tmpfile is closed ****** \n"); } else { DBG(1, "tmpfile is failed\n"); /* return (SANE_STATUS_INVAL);*/ } } /*********************************/ s->scanning = SANE_FALSE; DBG (1, "<< sane_cancel\n"); } /**************************************************************************/ SANE_Status sane_set_io_mode (SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking) { DBG (1, ">> sane_set_io_mode\n"); DBG (1, "<< sane_set_io_mode\n"); return SANE_STATUS_UNSUPPORTED; } /**************************************************************************/ SANE_Status sane_get_select_fd (SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ * fd) { DBG (1, ">> sane_get_select_fd\n"); DBG (1, "<< sane_get_select_fd\n"); return SANE_STATUS_UNSUPPORTED; } /**************************************************************************/ backends-1.3.0/backend/canon-scsi.c000066400000000000000000000424151456256263500171010ustar00rootroot00000000000000/* 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 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, see . 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 the low-level scsi-commands. */ static SANE_Status test_unit_ready (int fd) { static u_char cmd[6]; int status; DBG (31, ">> test_unit_ready\n"); memset (cmd, 0, sizeof (cmd)); status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, NULL, NULL); DBG (31, "<< test_unit_ready\n"); return (status); } #ifdef IMPLEMENT_ALL_SCANNER_SCSI_COMMANDS static SANE_Status request_sense (int fd, void *buf, size_t *buf_size) { static u_char cmd[6]; int status; DBG (31, ">> request_sense\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = 0x03; cmd[4] = 14; status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size); DBG (31, "<< request_sense\n"); return (status); } #endif static SANE_Status inquiry (int fd, int evpd, void *buf, size_t *buf_size) { static u_char cmd[6]; int status; DBG (31, ">> inquiry\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = 0x12; cmd[1] = evpd; cmd[2] = evpd ? 0xf0 : 0; cmd[4] = evpd ? 74 : 36; status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size); DBG (31, "<< inquiry\n"); return (status); } #ifdef IMPLEMENT_ALL_SCANNER_SCSI_COMMANDS static SANE_Status mode_select (int fd) { static u_char cmd[6 + 12]; int status; DBG (31, ">> mode_select\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = 0x15; cmd[1] = 16; cmd[4] = 12; cmd[6 + 4] = 3; cmd[6 + 5] = 6; cmd[6 + 8] = 0x02; cmd[6 + 9] = 0x58; status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), NULL, NULL); DBG (31, "<< mode_select\n"); return (status); } #endif static SANE_Status reserve_unit (int fd) { static u_char cmd[6]; int status; DBG (31, ">> reserve_unit\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = 0x16; status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, NULL, NULL); DBG (31, "<< reserve_unit\n"); return (status); } #ifdef IMPLEMENT_ALL_SCANNER_SCSI_COMMANDS static SANE_Status release_unit (int fd) { static u_char cmd[6]; int status; DBG (31, ">> release_unit\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = 0x17; status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, NULL, NULL); DBG (31, "<< release_unit\n"); return (status); } #endif static SANE_Status mode_sense (int fd, void *buf, size_t *buf_size) { static u_char cmd[6]; int status; DBG (31, ">> mode_sense\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = 0x1a; cmd[2] = 3; cmd[4] = 12; status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size); DBG (31, "<< mode_sense\n"); return (status); } static SANE_Status scan (int fd) { static u_char cmd[6 + 1]; int status; DBG (31, ">> scan\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = 0x1b; cmd[4] = 1; status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), NULL, NULL); DBG (31, "<< scan\n"); return (status); } static SANE_Status send_diagnostic (int fd) { static u_char cmd[6]; int status; DBG (31, ">> send_diagnostic\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = 0x1d; cmd[1] = 4; status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, NULL, NULL); DBG (31, "<< send_diagnostic\n"); return (status); } static SANE_Status set_window (int fd, void *data) { static u_char cmd[10]; int status; DBG (31, ">> set_window\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = 0x24; cmd[8] = 72; status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), data, 72, NULL, NULL); DBG (31, "<< set_window\n"); return (status); } static SANE_Status get_window (int fd, void *buf, size_t *buf_size) { static u_char cmd[10]; int status; DBG (31, ">> get_window\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = 0x25; cmd[1] = 1; cmd[8] = 72; status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size); DBG (31, "<< get_window\n"); return (status); } static SANE_Status read_data (int fd, void *buf, size_t *buf_size) { static u_char cmd[10]; int status; DBG (31, ">> read_data\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = 0x28; cmd[6] = *buf_size >> 16; cmd[7] = *buf_size >> 8; cmd[8] = *buf_size; status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size); DBG (31, "<< read_data\n"); return (status); } static SANE_Status medium_position (int fd) { static u_char cmd[10]; int status; DBG (31, ">> medium_position\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = 0x31; status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, NULL, NULL); DBG (31, "<< medium_position\n"); return (status); } #ifdef IMPLEMENT_ALL_SCANNER_SCSI_COMMANDS static SANE_Status execute_shading (int fd) { static u_char cmd[10]; int status; DBG (31, ">> execute shading\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = 0xe2; status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, NULL, NULL); DBG (31, "<< execute shading\n"); return (status); } #endif static SANE_Status execute_auto_focus (int fd, int AF, int speed, int AE, int count) { static u_char cmd[10]; int status; DBG (7, ">> execute_auto_focus\n"); DBG (7, ">> focus: mode='%d', speed='%d', AE='%d', count='%d'\n", AF, speed, AE, count); memset (cmd, 0, sizeof (cmd)); cmd[0] = 0xe0; cmd[1] = (u_char) AF; cmd[2] = (u_char) ((speed << 7) | AE); #if 1 cmd[4] = (u_char) count; /* seems to work, but may be unsafe */ #else /* The Canon software uses this: */ cmd[4] = (u_char) (28 * ((int) (count / 28.5)) + 16); #endif status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, NULL, NULL); DBG (7, "<< execute_auto_focus\n"); return (status); } static SANE_Status set_adf_mode (int fd, u_char priority) { static u_char cmd[6]; int status; memset (cmd, 0, sizeof (cmd)); cmd[0] = 0xd4; cmd[4] = 0x01; status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), &priority, 1, NULL, NULL); return (status); } static SANE_Status get_scan_mode (int fd, u_char page, void *buf, size_t *buf_size) { static u_char cmd[6]; int status; int PageLen = 0x00; memset (cmd, 0, sizeof (cmd)); cmd[0] = 0xd5; cmd[2] = page; switch (page) { case AUTO_DOC_FEEDER_UNIT: case TRANSPARENCY_UNIT: cmd[4] = 0x0c + PageLen; break; case SCAN_CONTROL_CONDITIONS: cmd[4] = 0x14 + PageLen; break; case SCAN_CONTROL_CON_FB1200: cmd[2] = 0x20; cmd[4] = 0x17 + PageLen; break; default: cmd[4] = 0x24 + PageLen; break; } DBG (31, "get scan mode: cmd[4]='0x%0X'\n", cmd[4]); status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size); DBG (31, "<< get scan mode\n"); return (status); } static SANE_Status define_scan_mode (int fd, u_char page, void *data) { static u_char cmd[6]; u_char pdata[36]; size_t i; int status, pdatalen; DBG (31, ">> define scan mode\n"); memset (cmd, 0, sizeof (cmd)); memset (pdata, 0, sizeof (pdata)); cmd[0] = 0xd6; cmd[1] = 0x10; cmd[4] = (page == TRANSPARENCY_UNIT) ? 0x0c : (page == TRANSPARENCY_UNIT_FB1200) ? 0x0c : (page == SCAN_CONTROL_CONDITIONS) ? 0x14 : (page == SCAN_CONTROL_CON_FB1200) ? 0x17 : 0x24; memcpy (pdata + 4, data, (page == TRANSPARENCY_UNIT) ? 8 : (page == TRANSPARENCY_UNIT_FB1200) ? 10 : (page == SCAN_CONTROL_CONDITIONS) ? 16 : (page == SCAN_CONTROL_CON_FB1200) ? 19 : 32); for (i = 0; i < sizeof (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", (int) i, pdata[i]); pdatalen = (page == TRANSPARENCY_UNIT) ? 12 : (page == TRANSPARENCY_UNIT_FB1200) ? 14 : (page == SCAN_CONTROL_CONDITIONS) ? 20 : (page == SCAN_CONTROL_CON_FB1200) ? 23 : 36; status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), pdata, pdatalen, NULL, NULL); DBG (31, "<< define scan mode\n"); return (status); } static SANE_Status get_density_curve (int fd, int component, void *buf, size_t *buf_size, int transfer_data_type) { static u_char cmd[10]; int status; DBG (31, ">> get_density_curve\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = 0x28; cmd[2] = transfer_data_type; cmd[4] = component; cmd[5] = 0; cmd[6] = ((*buf_size) >> 16) & 0xff; cmd[7] = ((*buf_size) >> 8) & 0xff; cmd[8] = (*buf_size) & 0xff; status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size); DBG (31, "<< get_density_curve\n"); return (status); } #ifdef IMPLEMENT_ALL_SCANNER_SCSI_COMMANDS static SANE_Status get_density_curve_data_format (int fd, void *buf, size_t *buf_size) { static u_char cmd[10]; int status; DBG (31, ">> get_density_curve_data_format\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = 0x28; cmd[2] = 0x03; cmd[4] = 0xff; cmd[5] = 0; cmd[6] = 0; cmd[7] = 0; cmd[8] = 14; status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size); DBG (31, "<< get_density_curve_data_format\n"); return (status); } #endif static SANE_Status set_density_curve (int fd, int component, void *buf, size_t *buf_size, int transfer_data_type) { static u_char cmd[10]; int status; DBG (31, ">> set_density_curve\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = 0x2a; cmd[2] = transfer_data_type; cmd[4] = component; cmd[5] = 0; cmd[6] = ((*buf_size) >> 16) & 0xff; cmd[7] = ((*buf_size) >> 8) & 0xff; cmd[8] = (*buf_size) & 0xff; status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), buf, *buf_size, NULL, NULL); DBG (31, "<< set_density_curve\n"); return (status); } /* static SANE_Status */ /* set_density_curve_data_format (int fd, void *buf, size_t *buf_size) */ /* { */ /* static u_char cmd[10]; */ /* int status, i; */ /* DBG (31, ">> set_density_curve_data_format\n"); */ /* memset (cmd, 0, sizeof (cmd)); */ /* cmd[0] = 0x2a; */ /* cmd[2] = 0x03; */ /* cmd[4] = 0xff; */ /* cmd[5] = 0; */ /* cmd[6] = 0; */ /* cmd[7] = 0; */ /* cmd[8] = 14; */ /* status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, buf_size); */ /* DBG (31, "<< set_density_curve_data_format\n"); */ /* return (status); */ /* } */ #ifdef IMPLEMENT_ALL_SCANNER_SCSI_COMMANDS static SANE_Status get_power_on_timer (int fd, void *buf, size_t *buf_size) { static u_char cmd[10]; int status; DBG (31, ">> get power on timer\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = 0xe3; cmd[6] = 1; cmd[7] = 0; cmd[8] = 0; status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size); DBG (31, "<< get power on timer\n"); return (status); } #endif static SANE_Status get_film_status (int fd, void *buf, size_t *buf_size) { static u_char cmd[10]; int status; DBG (31, ">> get film status\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = 0xe1; cmd[6] = 0; cmd[7] = 0; cmd[8] = 4; status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size); DBG (31, "<< get film status\n"); return (status); } static SANE_Status get_data_status (int fd, void *buf, size_t *buf_size) { static u_char cmd[10]; int status; DBG (31, ">> get_data_status\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = 0x34; cmd[8] = 28; status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size); DBG (31, "<< get_data_status\n"); return (status); } /*************** modification for FB620S ***************/ static SANE_Status reset_scanner (int fd) { static u_char cmd[6]; int status; DBG (31, ">> reset_scanner\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = 0xc1; status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, NULL, NULL); DBG (31, "<< reset_scanner \n"); return (status); } static SANE_Status execute_calibration (int fd) { static u_char cmd[6]; u_char data[2]; int status; DBG (31, ">> execute_calibration\n"); memset (cmd, 0, sizeof (cmd)); memset (data, 0, sizeof (data)); cmd[0] = 0xc2; cmd[4] = 2; status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), data, sizeof (data), NULL, NULL); DBG (31, "<< execute_calibration\n"); return (status); } static SANE_Status get_calibration_status (int fd, void *buf, size_t *buf_size) { static u_char cmd[6]; int status; DBG (31, ">> get_calibration_status\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = 0xc3; cmd[4] = *buf_size; status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size); DBG (31, "<< get_calibration_status\n"); return (status); } #if 0 static SANE_Status get_switch_status (int fd, void *buf, size_t *buf_size) { static u_char cmd[6]; int status; DBG (31, ">> get_switch_status\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = 0xc4; cmd[4] = 2; status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, buf, buf_size); DBG (31, "<< get_switch_status\n"); return (status); } static SANE_Status wait_ready(int fd) { SANE_Status status; int retry = 0; while ((status = test_unit_ready (fd)) != SANE_STATUS_GOOD) { DBG(5, "wait_ready failed (%d)\n", retry); if (retry++ > 15) return SANE_STATUS_IO_ERROR; sleep(3); } return(status); } #endif /*************** modification for FB1200S ***************/ static SANE_Status cancel (int fd) { static u_char cmd[10]; int status; DBG (31, ">> cancel_FB1200S\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = 0xe4; status = sanei_scsi_cmd2 (fd, cmd, sizeof (cmd), NULL, 0, NULL, NULL); DBG (31, "<< cancel_FB1200S \n"); return (status); } /**************************************************************************/ /* As long as we do not know how this scanner stores its density curves, we do the gamma correction with a 8 <--> 12 bit translation table stored in the CANON_Scanner structure. */ static SANE_Status get_density_curve_fs2710 (SANE_Handle handle, int component, u_char *buf, size_t *buf_size) { CANON_Scanner *s = handle; int i; for (i = 0; i < 256; i++) *buf++ = s->gamma_map[component][i << 4]; *buf_size = 256; return (SANE_STATUS_GOOD); } static SANE_Status set_density_curve_fs2710 (SANE_Handle handle, int component, u_char *buf) { CANON_Scanner *s = handle; int i, j, hi, lo; u_char *p; for (i = 1, hi = *buf++, p = &s->gamma_map[component][0]; i <= 256; i++) { lo = hi; hi = (i < 256) ? *buf++ : 2 * *(buf - 1) - *(buf - 2); if (hi > 255) hi = 255; for (j = 0; j < 16; j++) /* do a linear interpolation */ *p++ = (u_char) (lo + ((double) ((hi - lo) * j)) / 16.0 + 0.5); } return (SANE_STATUS_GOOD); } static SANE_Status set_parameters_fs2710 (SANE_Handle handle) { CANON_Scanner *s = handle; int i, j, shadow[4], hilite[4]; double x, b, c; shadow[1] = s->ShadowR << 4; shadow[2] = s->ShadowG << 4; shadow[3] = s->ShadowB << 4; hilite[1] = s->HiliteR << 4; hilite[2] = s->HiliteG << 4; hilite[3] = s->HiliteB << 4; c = ((double) s->contrast) / 128.0; b = ((double) (s->brightness - 128)) / 128.0; for (i = 1; i < 4; i++) { for (j = 0; j < 4096; j++) { if (j <= shadow[i]) s->gamma_map[i][j] = (u_char) ((s->brightness >= 128) ? 2 * s->brightness - 256 : 0); else if (j < hilite[i]) { x = ((double) (j - shadow[i])) / ((double) (hilite[i] - shadow[i])); /* first do the contrast correction */ x = (x <= 0.5) ? 0.5 * pow (2 * x, c) : 1.0 - 0.5 * pow (2 * (1.0 - x), c); x = pow (x, 0.5); /* default gamma correction */ x += b; /* brightness correction */ s->gamma_map[i][j] = (u_char) MAX (0, MIN (255, (int) (255.0 * x))); } else s->gamma_map[i][j] = (u_char) ((s->brightness >= 128) ? 255 : 2 * s->brightness); } } return (SANE_STATUS_GOOD); } /**************************************************************************/ backends-1.3.0/backend/canon.c000066400000000000000000002002231456256263500161330ustar00rootroot00000000000000/* 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 FB620 and FB1200 support by Mitsuru Okaniwa FS2710 support by Ulrich Deiters backend version: 1.13e 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, see . 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 the sane-api */ /* SANE-FLOW-DIAGRAMM - sane_init() : initialize backend, attach scanners(devicename,0) . - sane_get_devices() : query list of scanner-devices . - sane_open() : open a particular scanner-device and attach_scanner(devicename,&dev) . . - 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, kill reader_process . - sane_close() : close opened scanner-device, do_cancel, free buffer and handle - sane_exit() : terminate use of backend, free devicename and device-struture */ /* This driver's flow: - sane_init . - attach_one . . - inquiry . . - test_unit_ready . . - medium_position . . - extended inquiry . . - mode sense . . - get_density_curve - sane_get_devices - sane_open . - init_options - 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, kill reader_process - sane_close() : close opened scanner-device, do_cancel, free buffer and handle - sane_exit() : terminate use of backend, free devicename and device-struture */ #include "../include/sane/config.h" #include #include #include #include #include #include #include #include /* for FB1200S */ #include /* for FB1200S */ #include /* for FB1200S */ #include "../include/sane/sane.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_scsi.h" #define BACKEND_NAME canon #include "../include/sane/sanei_backend.h" #ifndef PATH_MAX #define PATH_MAX 1024 #endif #include "../include/sane/sanei_config.h" #define CANON_CONFIG_FILE "canon.conf" #include #ifndef SANE_I18N #define SANE_I18N(text) text #endif static SANE_Byte primaryHigh[256], primaryLow[256], secondaryHigh[256], secondaryLow[256]; /* modification for FB1200S */ static int num_devices = 0; static CANON_Device *first_dev = NULL; static CANON_Scanner *first_handle = NULL; static const SANE_String_Const mode_list[] = { SANE_VALUE_SCAN_MODE_LINEART, SANE_VALUE_SCAN_MODE_HALFTONE, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_COLOR, 0 }; /* modification for FS2710 */ static const SANE_String_Const mode_list_fs2710[] = { SANE_VALUE_SCAN_MODE_COLOR, SANE_I18N("Raw"), 0 }; /* modification for FB620S */ static const SANE_String_Const mode_list_fb620[] = { SANE_VALUE_SCAN_MODE_LINEART, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_COLOR, SANE_I18N("Fine color"), 0 }; /* modification for FB1200S */ static const SANE_String_Const mode_list_fb1200[] = { SANE_VALUE_SCAN_MODE_LINEART, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_COLOR, 0 }; static const SANE_String_Const tpu_dc_mode_list[] = { SANE_I18N("No transparency correction"), SANE_I18N("Correction according to film type"), SANE_I18N("Correction according to transparency ratio"), 0 }; static const SANE_String_Const filmtype_list[] = { SANE_I18N("Negatives"), SANE_I18N("Slides"), 0 }; static const SANE_String_Const negative_filmtype_list[] = { "Kodak", "Fuji", "Agfa", "Konica", 0 }; static const SANE_String_Const scanning_speed_list[] = { SANE_I18N("Automatic"), SANE_I18N("Normal speed"), SANE_I18N("1/2 normal speed"), SANE_I18N("1/3 normal speed"), 0 }; static const SANE_String_Const tpu_filmtype_list[] = { "Film 0", "Film 1", "Film 2", "Film 3", 0 }; /**************************************************/ static const SANE_Range u8_range = { 0, /* minimum */ 255, /* maximum */ 0 /* quantization */ }; #include "canon-scsi.c" /**************************************************************************/ static size_t max_string_size (const SANE_String_Const strings[]) { size_t size, max_size = 0; int i; DBG (11, ">> max_string_size\n"); for (i = 0; strings[i]; ++i) { size = strlen (strings[i]) + 1; if (size > max_size) max_size = size; } DBG (11, "<< max_string_size\n"); return max_size; } /**************************************************************************/ static void get_tpu_stat (int fd, CANON_Device * dev) { unsigned char tbuf[12 + 5]; size_t buf_size, i; SANE_Status status; DBG (3, ">> get tpu stat\n"); memset (tbuf, 0, sizeof (tbuf)); buf_size = sizeof (tbuf); status = get_scan_mode (fd, TRANSPARENCY_UNIT, tbuf, &buf_size); if (status != SANE_STATUS_GOOD) { DBG (1, "get scan mode failed: %s\n", sane_strstatus (status)); return; } for (i = 0; i < buf_size; 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 != TPU_STAT_NONE) /* TPU available */ { dev->tpu.Status = (tbuf[2 + 4 + 5] & 0x04) ? TPU_STAT_INACTIVE : TPU_STAT_ACTIVE; } dev->tpu.ControlMode = tbuf[3 + 4 + 5] & 0x03; dev->tpu.Transparency = tbuf[4 + 4 + 5] * 256 + tbuf[5 + 4 + 5]; dev->tpu.PosNeg = tbuf[6 + 4 + 5] & 0x01; dev->tpu.FilmType = tbuf[7 + 4 + 5]; if(dev->tpu.FilmType > 3) dev->tpu.FilmType = 0; DBG (11, "TPU Status: %d\n", dev->tpu.Status); DBG (11, "TPU ControlMode: %d\n", dev->tpu.ControlMode); DBG (11, "TPU Transparency: %d\n", dev->tpu.Transparency); DBG (11, "TPU PosNeg: %d\n", dev->tpu.PosNeg); DBG (11, "TPU FilmType: %d\n", dev->tpu.FilmType); DBG (3, "<< get tpu stat\n"); return; } /**************************************************************************/ static void get_adf_stat (int fd, CANON_Device * dev) { size_t buf_size = 0x0C, i; unsigned char abuf[0x0C]; SANE_Status status; DBG (3, ">> get adf stat\n"); memset (abuf, 0, buf_size); status = get_scan_mode (fd, AUTO_DOC_FEEDER_UNIT, abuf, &buf_size); if (status != SANE_STATUS_GOOD) { DBG (1, "get scan mode failed: %s\n", sane_strstatus (status)); perror ("get scan mode failed"); return; } for (i = 0; i < buf_size; 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; if (dev->adf.Status != ADF_STAT_NONE) /* ADF available / INACTIVE */ { dev->adf.Status = (abuf[ADF_Status] & ADF_PROBLEM) ? ADF_STAT_INACTIVE : ADF_STAT_ACTIVE; } dev->adf.Problem = (abuf[ADF_Status] & ADF_PROBLEM); dev->adf.Priority = (abuf[ADF_Settings] & ADF_PRIORITY); dev->adf.Feeder = (abuf[ADF_Settings] & ADF_FEEDER); DBG (11, "ADF Status: %d\n", dev->adf.Status); DBG (11, "ADF Priority: %d\n", dev->adf.Priority); DBG (11, "ADF Problem: %d\n", dev->adf.Problem); DBG (11, "ADF Feeder: %d\n", dev->adf.Feeder); DBG (3, "<< get adf stat\n"); return; } /**************************************************************************/ static SANE_Status sense_handler (int scsi_fd, u_char * result, void *arg) { static char me[] = "canon_sense_handler"; u_char sense; int asc; char *sense_str = NULL; SANE_Status status; DBG (1, ">> sense_handler\n"); DBG (11, "%s(%ld, %p, %p)\n", me, (long) scsi_fd, (void *) result, (void *) arg); DBG (11, "sense buffer: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x " "%02x %02x %02x %02x %02x %02x\n", result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7], result[8], result[9], result[10], result[11], result[12], result[13], result[14], result[15]); status = SANE_STATUS_GOOD; DBG(11, "sense data interpretation for SCSI-2 devices\n"); sense = result[2] & 0x0f; /* extract the sense key */ if (result[7] > 3) /* additional sense code available? */ { asc = (result[12] << 8) + result[13]; /* 12: additional sense code */ } /* 13: a.s.c. qualifier */ else asc = 0xffff; switch (sense) { case 0x00: DBG(11, "sense category: no error\n"); status = SANE_STATUS_GOOD; break; case 0x01: DBG(11, "sense category: recovered error\n"); switch (asc) { case 0x3700: sense_str = SANE_I18N("rounded parameter"); break; default: sense_str = SANE_I18N("unknown"); } status = SANE_STATUS_GOOD; break; case 0x03: DBG(11, "sense category: medium error\n"); switch (asc) { case 0x8000: sense_str = SANE_I18N("ADF jam"); break; case 0x8001: sense_str = SANE_I18N("ADF cover open"); break; default: sense_str = SANE_I18N("unknown"); } status = SANE_STATUS_IO_ERROR; break; case 0x04: DBG(11, "sense category: hardware error\n"); switch (asc) { case 0x6000: sense_str = SANE_I18N("lamp failure"); break; case 0x6200: sense_str = SANE_I18N("scan head positioning error"); break; case 0x8001: sense_str = SANE_I18N("CPU check error"); break; case 0x8002: sense_str = SANE_I18N("RAM check error"); break; case 0x8003: sense_str = SANE_I18N("ROM check error"); break; case 0x8004: sense_str = SANE_I18N("hardware check error"); break; case 0x8005: sense_str = SANE_I18N("transparency unit lamp failure"); break; case 0x8006: sense_str = SANE_I18N("transparency unit scan head " "positioning failure"); break; default: sense_str = SANE_I18N("unknown"); } status = SANE_STATUS_IO_ERROR; break; case 0x05: DBG(11, "sense category: illegal request\n"); switch (asc) { case 0x1a00: sense_str = SANE_I18N("parameter list length error"); status = SANE_STATUS_IO_ERROR; break; case 0x2000: sense_str = SANE_I18N("invalid command operation code"); status = SANE_STATUS_UNSUPPORTED; break; case 0x2400: sense_str = SANE_I18N("invalid field in CDB"); status = SANE_STATUS_IO_ERROR; break; case 0x2500: sense_str = SANE_I18N("unsupported LUN"); status = SANE_STATUS_UNSUPPORTED; break; case 0x2600: sense_str = SANE_I18N("invalid field in parameter list"); status = SANE_STATUS_UNSUPPORTED; break; case 0x2c00: sense_str = SANE_I18N("command sequence error"); status = SANE_STATUS_UNSUPPORTED; break; case 0x2c01: sense_str = SANE_I18N("too many windows specified"); status = SANE_STATUS_UNSUPPORTED; break; case 0x3a00: sense_str = SANE_I18N("medium not present"); status = SANE_STATUS_IO_ERROR; break; case 0x3d00: sense_str = SANE_I18N("invalid bit IDENTIFY message"); status = SANE_STATUS_UNSUPPORTED; break; case 0x8002: sense_str = SANE_I18N("option not correct"); status = SANE_STATUS_UNSUPPORTED; break; default: sense_str = SANE_I18N("unknown"); status = SANE_STATUS_UNSUPPORTED; } break; case 0x06: DBG(11, "sense category: unit attention\n"); switch (asc) { case 0x2900: sense_str = SANE_I18N("power on reset / bus device reset"); status = SANE_STATUS_GOOD; break; case 0x2a00: sense_str = SANE_I18N("parameter changed by another initiator"); status = SANE_STATUS_IO_ERROR; break; default: sense_str = SANE_I18N("unknown"); status = SANE_STATUS_IO_ERROR; } break; case 0x0b: DBG(11, "sense category: non-standard\n"); switch (asc) { case 0x0000: sense_str = SANE_I18N("no additional sense information"); status = SANE_STATUS_IO_ERROR; break; case 0x4500: sense_str = SANE_I18N("reselect failure"); status = SANE_STATUS_IO_ERROR; break; case 0x4700: sense_str = SANE_I18N("SCSI parity error"); status = SANE_STATUS_IO_ERROR; break; case 0x4800: sense_str = SANE_I18N("initiator detected error message " "received"); status = SANE_STATUS_IO_ERROR; break; case 0x4900: sense_str = SANE_I18N("invalid message error"); status = SANE_STATUS_UNSUPPORTED; break; case 0x8000: sense_str = SANE_I18N("timeout error"); status = SANE_STATUS_IO_ERROR; break; case 0x8001: sense_str = SANE_I18N("transparency unit shading error"); status = SANE_STATUS_IO_ERROR; break; case 0x8003: sense_str = SANE_I18N("lamp not stabilized"); status = SANE_STATUS_IO_ERROR; break; default: sense_str = SANE_I18N("unknown"); status = SANE_STATUS_IO_ERROR; } break; default: DBG(11, "sense category: else\n"); } DBG (11, "sense message: %s\n", sense_str); #if 0 /* superfluous? [U.D.] */ s->sense_str = sense_str; #endif DBG (1, "<< sense_handler\n"); return status; } /***************************************************************/ static SANE_Status do_gamma (CANON_Scanner * s) { SANE_Status status; u_char gbuf[256]; size_t buf_size; int i, j, neg, transfer_data_type, from; DBG (7, "sending SET_DENSITY_CURVE\n"); buf_size = 256 * sizeof (u_char); transfer_data_type = 0x03; neg = (s->hw->info.is_filmscanner) ? strcmp (filmtype_list[1], s->val[OPT_NEGATIVE].s) : s->val[OPT_HNEGATIVE].w; if (!strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY)) { /* If scanning in gray mode, use the first curve for the scanner's monochrome gamma component */ for (j = 0; j < 256; j++) { if (!neg) { gbuf[j] = (u_char) s->gamma_table[0][j]; DBG (22, "set_density %d: gbuf[%d] = [%d]\n", 0, j, gbuf[j]); } else { gbuf[255 - j] = (u_char) (255 - s->gamma_table[0][j]); DBG (22, "set_density %d: gbuf[%d] = [%d]\n", 0, 255 - j, gbuf[255 - j]); } } if ((status = set_density_curve (s->fd, 0, gbuf, &buf_size, transfer_data_type)) != SANE_STATUS_GOOD) { DBG (7, "SET_DENSITY_CURVE\n"); sanei_scsi_close (s->fd); s->fd = -1; return (SANE_STATUS_INVAL); } } else { /* colour mode */ /* If in RGB mode but with gamma bind, use the first curve for all 3 colors red, green, blue */ for (i = 1; i < 4; i++) { from = (s->val[OPT_CUSTOM_GAMMA_BIND].w) ? 0 : i; for (j = 0; j < 256; j++) { if (!neg) { gbuf[j] = (u_char) s->gamma_table[from][j]; DBG (22, "set_density %d: gbuf[%d] = [%d]\n", i, j, gbuf[j]); } else { gbuf[255 - j] = (u_char) (255 - s->gamma_table[from][j]); DBG (22, "set_density %d: gbuf[%d] = [%d]\n", i, 255 - j, gbuf[255 - j]); } } if (s->hw->info.model == FS2710) status = set_density_curve_fs2710 (s, i, gbuf); else { if ((status = set_density_curve (s->fd, i, gbuf, &buf_size, transfer_data_type)) != SANE_STATUS_GOOD) { DBG (7, "SET_DENSITY_CURVE\n"); sanei_scsi_close (s->fd); s->fd = -1; return (SANE_STATUS_INVAL); } } } } return (SANE_STATUS_GOOD); } /**************************************************************************/ static SANE_Status attach (const char *devnam, CANON_Device ** devp) { SANE_Status status; CANON_Device *dev; int fd; u_char ibuf[36], ebuf[74], mbuf[12]; size_t buf_size, i; char *str; DBG (1, ">> attach\n"); for (dev = first_dev; dev; dev = dev->next) { if (!strcmp (dev->sane.name, devnam)) { if (devp) *devp = dev; return (SANE_STATUS_GOOD); } } DBG (3, "attach: opening %s\n", devnam); status = sanei_scsi_open (devnam, &fd, sense_handler, dev); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: open failed: %s\n", sane_strstatus (status)); return (status); } DBG (3, "attach: sending (standard) INQUIRY\n"); memset (ibuf, 0, sizeof (ibuf)); buf_size = sizeof (ibuf); status = inquiry (fd, 0, ibuf, &buf_size); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: inquiry failed: %s\n", sane_strstatus (status)); sanei_scsi_close (fd); fd = -1; return (status); } if (ibuf[0] != 6 || strncmp ((char *) (ibuf + 8), "CANON", 5) != 0 || strncmp ((char *) (ibuf + 16), "IX-", 3) != 0) { DBG (1, "attach: device doesn't look like a Canon scanner\n"); sanei_scsi_close (fd); fd = -1; return (SANE_STATUS_INVAL); } DBG (3, "attach: sending TEST_UNIT_READY\n"); status = test_unit_ready (fd); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: test unit ready failed (%s)\n", sane_strstatus (status)); sanei_scsi_close (fd); fd = -1; return (status); } #if 0 DBG (3, "attach: sending REQUEST SENSE\n"); memset (sbuf, 0, sizeof (sbuf)); buf_size = sizeof (sbuf); status = request_sense (fd, sbuf, &buf_size); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: REQUEST_SENSE failed\n"); sanei_scsi_close (fd); fd = -1; return (SANE_STATUS_INVAL); } DBG (3, "attach: sending MEDIUM POSITION\n"); status = medium_position (fd); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: MEDIUM POSITION failed\n"); sanei_scsi_close (fd); fd = -1; return (SANE_STATUS_INVAL); } /* s->val[OPT_AF_NOW].w == SANE_TRUE; */ #endif DBG (3, "attach: sending RESERVE UNIT\n"); status = reserve_unit (fd); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: RESERVE UNIT failed\n"); sanei_scsi_close (fd); fd = -1; return (SANE_STATUS_INVAL); } #if 0 DBG (3, "attach: sending GET SCAN MODE for transparency unit\n"); memset (ebuf, 0, sizeof (ebuf)); buf_size = sizeof (ebuf); buf_size = 12; status = get_scan_mode (fd, TRANSPARENCY_UNIT, ebuf, &buf_size); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: GET SCAN MODE for transparency unit failed\n"); sanei_scsi_close (fd); return (SANE_STATUS_INVAL); } for (i = 0; i < buf_size; i++) DBG(3, "scan mode trans byte[%d] = %d\n", i, ebuf[i]); #endif DBG (3, "attach: sending GET SCAN MODE for scan control conditions\n"); memset (ebuf, 0, sizeof (ebuf)); buf_size = sizeof (ebuf); status = get_scan_mode (fd, SCAN_CONTROL_CONDITIONS, ebuf, &buf_size); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: GET SCAN MODE for scan control conditions failed\n"); sanei_scsi_close (fd); return (SANE_STATUS_INVAL); } for (i = 0; i < buf_size; i++) { DBG (3, "scan mode byte[%d] = %d\n", (int) i, ebuf[i]); } DBG (3, "attach: sending (extended) INQUIRY\n"); memset (ebuf, 0, sizeof (ebuf)); buf_size = sizeof (ebuf); status = inquiry (fd, 1, ebuf, &buf_size); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: (extended) INQUIRY failed\n"); sanei_scsi_close (fd); fd = -1; return (SANE_STATUS_INVAL); } #if 0 DBG (3, "attach: sending GET SCAN MODE for transparency unit\n"); memset (ebuf, 0, sizeof (ebuf)); buf_size = 64; status = get_scan_mode (fd, ALL_SCAN_MODE_PAGES, /* transparency unit */ ebuf, &buf_size); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: GET SCAN MODE for scan control conditions failed\n"); sanei_scsi_close (fd); return (SANE_STATUS_INVAL); } for (i = 0; i < buf_size; i++) DBG (3, "scan mode control byte[%d] = %d\n", i, ebuf[i]); #endif #if 0 DBG (3, "attach: sending GET SCAN MODE for all scan mode pages\n"); memset (ebuf, 0, sizeof (ebuf)); buf_size = 32; status = get_scan_mode (fd, (u_char)ALL_SCAN_MODE_PAGES, ebuf, &buf_size); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: GET SCAN MODE for scan control conditions failed\n"); sanei_scsi_close (fd); return (SANE_STATUS_INVAL); } for (i = 0; i < buf_size; i++) DBG(3, "scan mode control byte[%d] = %d\n", i, ebuf[i]); #endif DBG (3, "attach: sending MODE SENSE\n"); memset (mbuf, 0, sizeof (mbuf)); buf_size = sizeof (mbuf); status = mode_sense (fd, mbuf, &buf_size); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: MODE_SENSE failed\n"); sanei_scsi_close (fd); fd = -1; return (SANE_STATUS_INVAL); } dev = malloc (sizeof (*dev)); if (!dev) { sanei_scsi_close (fd); fd = -1; return (SANE_STATUS_NO_MEM); } memset (dev, 0, sizeof (*dev)); dev->sane.name = strdup (devnam); dev->sane.vendor = "CANON"; if ((str = strndup ((char *) ibuf + 16, 16)) == NULL) { sanei_scsi_close (fd); fd = -1; return (SANE_STATUS_NO_MEM); } /* Register the fixed properties of the scanner below: - whether it is a film scanner or a flatbed scanner - whether it can have an automatic document feeder (ADF) - whether it can be equipped with a transparency unit (TPU) - whether it has got focus control - whether it can optimize image parameters (autoexposure) - whether it can calibrate itself - whether it can diagnose itself - whether it can eject the media - whether it can mirror the scanned data - whether it is a film scanner (or can be used as one) - whether it has fixed, hardware-set scan resolutions only */ if (!strncmp (str, "IX-27015", 8)) /* FS2700S */ { dev->info.model = CS2700; dev->sane.model = strdup("FS2700S"); dev->sane.type = SANE_I18N("film scanner"); dev->adf.Status = ADF_STAT_NONE; dev->tpu.Status = TPU_STAT_NONE; dev->info.can_focus = SANE_TRUE; dev->info.can_autoexpose = SANE_TRUE; dev->info.can_calibrate = SANE_FALSE; dev->info.can_diagnose = SANE_FALSE; dev->info.can_eject = SANE_TRUE; dev->info.can_mirror = SANE_TRUE; dev->info.is_filmscanner = SANE_TRUE; dev->info.has_fixed_resolutions = SANE_TRUE; } else if (!strncmp (str, "IX-27025E", 9)) /* FS2710S */ { dev->info.model = FS2710; dev->sane.model = strdup("FS2710S"); dev->sane.type = SANE_I18N("film scanner"); dev->adf.Status = ADF_STAT_NONE; dev->tpu.Status = TPU_STAT_NONE; dev->info.can_focus = SANE_TRUE; dev->info.can_autoexpose = SANE_TRUE; dev->info.can_calibrate = SANE_FALSE; dev->info.can_diagnose = SANE_FALSE; dev->info.can_eject = SANE_TRUE; dev->info.can_mirror = SANE_TRUE; dev->info.is_filmscanner = SANE_TRUE; dev->info.has_fixed_resolutions = SANE_TRUE; } else if (!strncmp (str, "IX-06035E", 9)) /* FB620S */ { dev->info.model = FB620; dev->sane.model = strdup("FB620S"); dev->sane.type = SANE_I18N("flatbed scanner"); dev->adf.Status = ADF_STAT_NONE; dev->tpu.Status = TPU_STAT_NONE; dev->info.can_focus = SANE_FALSE; dev->info.can_autoexpose = SANE_FALSE; dev->info.can_calibrate = SANE_TRUE; dev->info.can_diagnose = SANE_TRUE; dev->info.can_eject = SANE_FALSE; dev->info.can_mirror = SANE_FALSE; dev->info.is_filmscanner = SANE_FALSE; dev->info.has_fixed_resolutions = SANE_TRUE; } else if (!strncmp (str, "IX-12015E", 9)) /* FB1200S */ { dev->info.model = FB1200; dev->sane.model = strdup("FB1200S"); dev->sane.type = SANE_I18N("flatbed scanner"); dev->adf.Status = ADF_STAT_INACTIVE; dev->tpu.Status = TPU_STAT_INACTIVE; dev->info.can_focus = SANE_FALSE; dev->info.can_autoexpose = SANE_FALSE; dev->info.can_calibrate = SANE_FALSE; dev->info.can_diagnose = SANE_FALSE; dev->info.can_eject = SANE_FALSE; dev->info.can_mirror = SANE_FALSE; dev->info.is_filmscanner = SANE_FALSE; dev->info.has_fixed_resolutions = SANE_TRUE; } else if (!strncmp (str, "IX-4015", 7)) /* IX-4015 */ { dev->info.model = IX4015; dev->sane.type = SANE_I18N("flatbed scanner"); dev->adf.Status = ADF_STAT_INACTIVE; dev->tpu.Status = TPU_STAT_INACTIVE; dev->info.can_focus = SANE_FALSE; dev->info.can_autoexpose = SANE_TRUE; dev->info.can_calibrate = SANE_FALSE; dev->info.can_diagnose = SANE_TRUE; dev->info.can_eject = SANE_FALSE; dev->info.can_mirror = SANE_TRUE; dev->info.is_filmscanner = SANE_FALSE; dev->info.has_fixed_resolutions = SANE_FALSE; } else /* CS300, CS600 */ { dev->info.model = CS3_600; dev->sane.type = SANE_I18N("flatbed scanner"); dev->adf.Status = ADF_STAT_INACTIVE; dev->tpu.Status = TPU_STAT_INACTIVE; dev->info.can_focus = SANE_FALSE; dev->info.can_autoexpose = SANE_FALSE; dev->info.can_calibrate = SANE_FALSE; dev->info.can_diagnose = SANE_FALSE; dev->info.can_eject = SANE_FALSE; dev->info.can_mirror = SANE_TRUE; dev->info.is_filmscanner = SANE_FALSE; dev->info.has_fixed_resolutions = SANE_FALSE; } /* * Use the model from the device if we don't have more * common model name for the device, otherwise free the * string with internal model name. * * Please keep the memory allocation source consistent * for model string - allocate on the heap via dynamic * allocation. */ if (dev->sane.model == NULL) dev->sane.model = str; else free(str); DBG (5, "dev->sane.name = '%s'\n", dev->sane.name); DBG (5, "dev->sane.vendor = '%s'\n", dev->sane.vendor); DBG (5, "dev->sane.model = '%s'\n", dev->sane.model); DBG (5, "dev->sane.type = '%s'\n", dev->sane.type); if (dev->tpu.Status != TPU_STAT_NONE) get_tpu_stat (fd, dev); /* Query TPU */ if (dev->adf.Status != ADF_STAT_NONE) get_adf_stat (fd, dev); /* Query ADF */ dev->info.bmu = mbuf[6]; DBG (5, "bmu=%d\n", dev->info.bmu); dev->info.mud = (mbuf[8] << 8) + mbuf[9]; DBG (5, "mud=%d\n", dev->info.mud); dev->info.xres_default = (ebuf[5] << 8) + ebuf[6]; DBG (5, "xres_default=%d\n", dev->info.xres_default); dev->info.xres_range.max = (ebuf[10] << 8) + ebuf[11]; DBG (5, "xres_range.max=%d\n", dev->info.xres_range.max); dev->info.xres_range.min = (ebuf[14] << 8) + ebuf[15]; DBG (5, "xres_range.min=%d\n", dev->info.xres_range.min); dev->info.xres_range.quant = ebuf[9] >> 4; DBG (5, "xres_range.quant=%d\n", dev->info.xres_range.quant); dev->info.yres_default = (ebuf[7] << 8) + ebuf[8]; DBG (5, "yres_default=%d\n", dev->info.yres_default); dev->info.yres_range.max = (ebuf[12] << 8) + ebuf[13]; DBG (5, "yres_range.max=%d\n", dev->info.yres_range.max); dev->info.yres_range.min = (ebuf[16] << 8) + ebuf[17]; DBG (5, "yres_range.min=%d\n", dev->info.yres_range.min); dev->info.yres_range.quant = ebuf[9] & 0x0f; DBG (5, "xres_range.quant=%d\n", dev->info.xres_range.quant); dev->info.x_range.min = SANE_FIX (0.0); dev->info.x_range.max = (ebuf[20] << 24) + (ebuf[21] << 16) + (ebuf[22] << 8) + ebuf[23] - 1; dev->info.x_range.max = SANE_FIX (dev->info.x_range.max * MM_PER_INCH / dev->info.mud); DBG (5, "x_range.max=%d\n", dev->info.x_range.max); dev->info.x_range.quant = 0; dev->info.y_range.min = SANE_FIX (0.0); dev->info.y_range.max = (ebuf[24] << 24) + (ebuf[25] << 16) + (ebuf[26] << 8) + ebuf[27] - 1; dev->info.y_range.max = SANE_FIX (dev->info.y_range.max * MM_PER_INCH / dev->info.mud); DBG (5, "y_range.max=%d\n", dev->info.y_range.max); dev->info.y_range.quant = 0; dev->info.x_adf_range.max = (ebuf[30] << 24) + (ebuf[31] << 16) + (ebuf[32] << 8) + ebuf[33] - 1; DBG (5, "x_adf_range.max=%d\n", dev->info.x_adf_range.max); dev->info.y_adf_range.max = (ebuf[34] << 24) + (ebuf[35] << 16) + (ebuf[36] << 8) + ebuf[37] - 1; DBG (5, "y_adf_range.max=%d\n", dev->info.y_adf_range.max); dev->info.brightness_range.min = 0; dev->info.brightness_range.max = 255; dev->info.brightness_range.quant = 0; dev->info.contrast_range.min = 1; dev->info.contrast_range.max = 255; dev->info.contrast_range.quant = 0; dev->info.threshold_range.min = 1; dev->info.threshold_range.max = 255; dev->info.threshold_range.quant = 0; dev->info.HiliteR_range.min = 0; dev->info.HiliteR_range.max = 255; dev->info.HiliteR_range.quant = 0; dev->info.ShadowR_range.min = 0; dev->info.ShadowR_range.max = 254; dev->info.ShadowR_range.quant = 0; dev->info.HiliteG_range.min = 0; dev->info.HiliteG_range.max = 255; dev->info.HiliteG_range.quant = 0; dev->info.ShadowG_range.min = 0; dev->info.ShadowG_range.max = 254; dev->info.ShadowG_range.quant = 0; dev->info.HiliteB_range.min = 0; dev->info.HiliteB_range.max = 255; dev->info.HiliteB_range.quant = 0; dev->info.ShadowB_range.min = 0; dev->info.ShadowB_range.max = 254; dev->info.ShadowB_range.quant = 0; dev->info.focus_range.min = 0; dev->info.focus_range.max = 255; dev->info.focus_range.quant = 0; dev->info.TPU_Transparency_range.min = 0; dev->info.TPU_Transparency_range.max = 10000; dev->info.TPU_Transparency_range.quant = 100; sanei_scsi_close (fd); fd = -1; ++num_devices; dev->next = first_dev; first_dev = dev; if (devp) *devp = dev; DBG (1, "<< attach\n"); return (SANE_STATUS_GOOD); } /**************************************************************************/ static SANE_Status do_cancel (CANON_Scanner * s) { SANE_Status status; DBG (1, ">> do_cancel\n"); s->scanning = SANE_FALSE; if (s->fd >= 0) { if (s->val[OPT_EJECT_AFTERSCAN].w && !(s->val[OPT_PREVIEW].w && s->hw->info.is_filmscanner)) { DBG (3, "do_cancel: sending MEDIUM POSITION\n"); status = medium_position (s->fd); if (status != SANE_STATUS_GOOD) { DBG (1, "do_cancel: MEDIUM POSITION failed\n"); return (SANE_STATUS_INVAL); } s->AF_NOW = SANE_TRUE; DBG (1, "do_cancel AF_NOW = '%d'\n", s->AF_NOW); } DBG (21, "do_cancel: reset_flag = %d\n", s->reset_flag); if ((s->reset_flag == 1) && (s->hw->info.model == FB620)) { status = reset_scanner (s->fd); if (status != SANE_STATUS_GOOD) { DBG (21, "RESET SCANNER failed\n"); sanei_scsi_close (s->fd); s->fd = -1; return (SANE_STATUS_INVAL); } DBG (21, "RESET SCANNER\n"); s->reset_flag = 0; DBG (21, "do_cancel: reset_flag = %d\n", s->reset_flag); s->time0 = -1; DBG (21, "time0 = %ld\n", s->time0); } if (s->hw->info.model == FB1200) { DBG (3, "CANCEL FB1200S\n"); status = cancel (s->fd); if (status != SANE_STATUS_GOOD) { DBG (1, "CANCEL FB1200S failed\n"); return (SANE_STATUS_INVAL); } DBG (3, "CANCEL FB1200S OK\n"); } sanei_scsi_close (s->fd); s->fd = -1; } DBG (1, "<< do_cancel\n"); return (SANE_STATUS_CANCELLED); } /**************************************************************************/ static SANE_Status init_options (CANON_Scanner * s) { int i; DBG (1, ">> init_options\n"); memset (s->opt, 0, sizeof (s->opt)); memset (s->val, 0, sizeof (s->val)); s->AF_NOW = SANE_TRUE; for (i = 0; i < NUM_OPTIONS; ++i) { s->opt[i].size = sizeof (SANE_Word); s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* "Mode" group: */ s->opt[OPT_MODE_GROUP].title = SANE_I18N("Scan mode"); s->opt[OPT_MODE_GROUP].desc = ""; s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MODE_GROUP].cap = 0; s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* scan mode */ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; 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].constraint_type = SANE_CONSTRAINT_STRING_LIST; switch (s->hw->info.model) { case FB620: s->opt[OPT_MODE].size = max_string_size (mode_list_fb620); s->opt[OPT_MODE].constraint.string_list = mode_list_fb620; s->val[OPT_MODE].s = strdup (mode_list_fb620[3]); break; case FB1200: s->opt[OPT_MODE].size = max_string_size (mode_list_fb1200); s->opt[OPT_MODE].constraint.string_list = mode_list_fb1200; s->val[OPT_MODE].s = strdup (mode_list_fb1200[2]); break; case FS2710: s->opt[OPT_MODE].size = max_string_size (mode_list_fs2710); s->opt[OPT_MODE].constraint.string_list = mode_list_fs2710; s->val[OPT_MODE].s = strdup (mode_list_fs2710[0]); break; default: s->opt[OPT_MODE].size = max_string_size (mode_list); s->opt[OPT_MODE].constraint.string_list = mode_list; s->val[OPT_MODE].s = strdup (mode_list[3]); } /* Slides or negatives */ s->opt[OPT_NEGATIVE].name = "film-type"; s->opt[OPT_NEGATIVE].title = SANE_I18N("Film type"); s->opt[OPT_NEGATIVE].desc = SANE_I18N("Selects the film type, i.e. " "negatives or slides"); s->opt[OPT_NEGATIVE].type = SANE_TYPE_STRING; s->opt[OPT_NEGATIVE].size = max_string_size (filmtype_list); s->opt[OPT_NEGATIVE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_NEGATIVE].constraint.string_list = filmtype_list; s->opt[OPT_NEGATIVE].cap |= (s->hw->info.is_filmscanner)? 0 : SANE_CAP_INACTIVE; s->val[OPT_NEGATIVE].s = strdup (filmtype_list[1]); /* Negative film type */ s->opt[OPT_NEGATIVE_TYPE].name = "negative-film-type"; s->opt[OPT_NEGATIVE_TYPE].title = SANE_I18N("Negative film type"); s->opt[OPT_NEGATIVE_TYPE].desc = SANE_I18N("Selects the negative film type"); s->opt[OPT_NEGATIVE_TYPE].type = SANE_TYPE_STRING; s->opt[OPT_NEGATIVE_TYPE].size = max_string_size (negative_filmtype_list); s->opt[OPT_NEGATIVE_TYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_NEGATIVE_TYPE].constraint.string_list = negative_filmtype_list; s->opt[OPT_NEGATIVE_TYPE].cap |= SANE_CAP_INACTIVE; s->val[OPT_NEGATIVE_TYPE].s = strdup (negative_filmtype_list[0]); /* Scanning speed */ s->opt[OPT_SCANNING_SPEED].name = SANE_NAME_SCAN_SPEED; s->opt[OPT_SCANNING_SPEED].title = SANE_TITLE_SCAN_SPEED; s->opt[OPT_SCANNING_SPEED].desc = SANE_DESC_SCAN_SPEED; s->opt[OPT_SCANNING_SPEED].type = SANE_TYPE_STRING; s->opt[OPT_SCANNING_SPEED].size = max_string_size (scanning_speed_list); s->opt[OPT_SCANNING_SPEED].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_SCANNING_SPEED].constraint.string_list = scanning_speed_list; s->opt[OPT_SCANNING_SPEED].cap |= (s->hw->info.model == CS2700) ? 0 : SANE_CAP_INACTIVE; if (s->hw->info.model != CS2700) s->opt[OPT_SCANNING_SPEED].cap &= ~SANE_CAP_SOFT_SELECT; s->val[OPT_SCANNING_SPEED].s = strdup (scanning_speed_list[0]); /* "Resolution" group: */ s->opt[OPT_RESOLUTION_GROUP].title = SANE_I18N("Scan resolution"); s->opt[OPT_RESOLUTION_GROUP].desc = ""; s->opt[OPT_RESOLUTION_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_RESOLUTION_GROUP].cap = 0; s->opt[OPT_RESOLUTION_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* bind resolution */ s->opt[OPT_RESOLUTION_BIND].name = SANE_NAME_RESOLUTION_BIND; s->opt[OPT_RESOLUTION_BIND].title = SANE_TITLE_RESOLUTION_BIND; s->opt[OPT_RESOLUTION_BIND].desc = SANE_DESC_RESOLUTION_BIND; s->opt[OPT_RESOLUTION_BIND].type = SANE_TYPE_BOOL; s->val[OPT_RESOLUTION_BIND].w = SANE_TRUE; /* hardware resolutions only */ s->opt[OPT_HW_RESOLUTION_ONLY].name = "hw-resolution-only"; s->opt[OPT_HW_RESOLUTION_ONLY].title = SANE_I18N("Hardware resolution"); s->opt[OPT_HW_RESOLUTION_ONLY].desc = SANE_I18N("Use only hardware " "resolutions"); s->opt[OPT_HW_RESOLUTION_ONLY].type = SANE_TYPE_BOOL; s->val[OPT_HW_RESOLUTION_ONLY].w = SANE_TRUE; s->opt[OPT_HW_RESOLUTION_ONLY].cap |= (s->hw->info.has_fixed_resolutions)? 0 : SANE_CAP_INACTIVE; /* x-resolution */ s->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_X_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_X_RESOLUTION].unit = SANE_UNIT_DPI; if (s->hw->info.has_fixed_resolutions) { int iCnt; float iRes; /* modification for FB620S */ s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; iCnt = 0; iRes = s->hw->info.xres_range.max; DBG (5, "hw->info.xres_range.max=%d\n", s->hw->info.xres_range.max); s->opt[OPT_X_RESOLUTION].constraint.word_list = s->xres_word_list; /* go to minimum resolution by dividing by 2 */ while (iRes >= s->hw->info.xres_range.min) iRes /= 2; /* fill array up to maximum resolution */ while (iRes < s->hw->info.xres_range.max) { iRes *= 2; s->xres_word_list[++iCnt] = iRes; } s->xres_word_list[0] = iCnt; s->val[OPT_X_RESOLUTION].w = s->xres_word_list[2]; } else { s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_X_RESOLUTION].constraint.range = &s->hw->info.xres_range; s->val[OPT_X_RESOLUTION].w = 300; } /* y-resolution */ s->opt[OPT_Y_RESOLUTION].name = SANE_NAME_SCAN_Y_RESOLUTION; s->opt[OPT_Y_RESOLUTION].title = SANE_TITLE_SCAN_Y_RESOLUTION; s->opt[OPT_Y_RESOLUTION].desc = SANE_DESC_SCAN_Y_RESOLUTION; s->opt[OPT_Y_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_Y_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_Y_RESOLUTION].cap |= SANE_CAP_INACTIVE; if (s->hw->info.has_fixed_resolutions) { int iCnt; float iRes; /* modification for FB620S */ s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; iCnt = 0; iRes = s->hw->info.yres_range.max; DBG (5, "hw->info.yres_range.max=%d\n", s->hw->info.yres_range.max); s->opt[OPT_Y_RESOLUTION].constraint.word_list = s->yres_word_list; /* go to minimum resolution by dividing by 2 */ while (iRes >= s->hw->info.yres_range.min) iRes /= 2; /* fill array up to maximum resolution */ while (iRes < s->hw->info.yres_range.max) { iRes *= 2; s->yres_word_list[++iCnt] = iRes; } s->yres_word_list[0] = iCnt; s->val[OPT_Y_RESOLUTION].w = s->yres_word_list[2]; } else { s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_Y_RESOLUTION].constraint.range = &s->hw->info.yres_range; s->val[OPT_Y_RESOLUTION].w = 300; } /* Focus group: */ s->opt[OPT_FOCUS_GROUP].title = SANE_I18N("Focus"); s->opt[OPT_FOCUS_GROUP].desc = ""; s->opt[OPT_FOCUS_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_FOCUS_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_FOCUS_GROUP].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_FOCUS_GROUP].cap |= (s->hw->info.can_focus) ? 0 : SANE_CAP_INACTIVE; /* Auto-Focus switch */ s->opt[OPT_AF].name = "af"; s->opt[OPT_AF].title = SANE_I18N("Auto focus"); s->opt[OPT_AF].desc = SANE_I18N("Enable/disable auto focus"); s->opt[OPT_AF].type = SANE_TYPE_BOOL; s->opt[OPT_AF].cap |= (s->hw->info.can_focus) ? 0 : SANE_CAP_INACTIVE; s->val[OPT_AF].w = s->hw->info.can_focus; /* Auto-Focus once switch */ s->opt[OPT_AF_ONCE].name = "afonce"; s->opt[OPT_AF_ONCE].title = SANE_I18N("Auto focus only once"); s->opt[OPT_AF_ONCE].desc = SANE_I18N("Do auto focus only once between " "ejects"); s->opt[OPT_AF_ONCE].type = SANE_TYPE_BOOL; s->opt[OPT_AF_ONCE].cap |= (s->hw->info.can_focus) ? 0 : SANE_CAP_INACTIVE; s->val[OPT_AF_ONCE].w = s->hw->info.can_focus; /* Manual focus */ s->opt[OPT_FOCUS].name = "focus"; s->opt[OPT_FOCUS].title = SANE_I18N("Manual focus position"); s->opt[OPT_FOCUS].desc = SANE_I18N("Set the optical system's focus " "position by hand (default: 128)."); s->opt[OPT_FOCUS].type = SANE_TYPE_INT; s->opt[OPT_FOCUS].unit = SANE_UNIT_NONE; s->opt[OPT_FOCUS].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_FOCUS].constraint.range = &s->hw->info.focus_range; s->opt[OPT_FOCUS].cap |= (s->hw->info.can_focus) ? 0 : SANE_CAP_INACTIVE; s->val[OPT_FOCUS].w = (s->hw->info.can_focus) ? 128 : 0; /* Margins group: */ s->opt[OPT_MARGINS_GROUP].title = SANE_I18N("Scan margins"); s->opt[OPT_MARGINS_GROUP].desc = ""; s->opt[OPT_MARGINS_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MARGINS_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_MARGINS_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* top-left x */ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; s->opt[OPT_TL_X].unit = SANE_UNIT_MM; s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_X].constraint.range = &s->hw->info.x_range; s->val[OPT_TL_X].w = 0; /* top-left y */ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_Y].constraint.range = &s->hw->info.y_range; s->val[OPT_TL_Y].w = 0; /* bottom-right x */ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; s->opt[OPT_BR_X].unit = SANE_UNIT_MM; s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_X].constraint.range = &s->hw->info.x_range; s->val[OPT_BR_X].w = s->hw->info.x_range.max; /* bottom-right y */ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_Y].constraint.range = &s->hw->info.y_range; s->val[OPT_BR_Y].w = s->hw->info.y_range.max; /* Colors group: */ s->opt[OPT_COLORS_GROUP].title = SANE_I18N("Extra color adjustments"); s->opt[OPT_COLORS_GROUP].desc = ""; s->opt[OPT_COLORS_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_COLORS_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_COLORS_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* Positive/Negative switch for the CanoScan 300/600 models */ s->opt[OPT_HNEGATIVE].name = SANE_NAME_NEGATIVE; s->opt[OPT_HNEGATIVE].title = SANE_TITLE_NEGATIVE; s->opt[OPT_HNEGATIVE].desc = SANE_DESC_NEGATIVE; s->opt[OPT_HNEGATIVE].type = SANE_TYPE_BOOL; s->opt[OPT_HNEGATIVE].cap |= (s->hw->info.model == CS2700 || s->hw->info.model == FS2710) ? SANE_CAP_INACTIVE : 0; s->val[OPT_HNEGATIVE].w = SANE_FALSE; /* Same values for highlight and shadow points for red, green, blue */ s->opt[OPT_BIND_HILO].name = "bind-highlight-shadow-points"; s->opt[OPT_BIND_HILO].title = SANE_TITLE_RGB_BIND; s->opt[OPT_BIND_HILO].desc = SANE_DESC_RGB_BIND; s->opt[OPT_BIND_HILO].type = SANE_TYPE_BOOL; s->opt[OPT_BIND_HILO].cap |= (s->hw->info.model == FB620 || s->hw->info.model == IX4015) ? SANE_CAP_INACTIVE : 0; s->val[OPT_BIND_HILO].w = SANE_TRUE; /* highlight point for red */ s->opt[OPT_HILITE_R].name = SANE_NAME_HIGHLIGHT_R; s->opt[OPT_HILITE_R].title = SANE_TITLE_HIGHLIGHT_R; s->opt[OPT_HILITE_R].desc = SANE_DESC_HIGHLIGHT_R; s->opt[OPT_HILITE_R].type = SANE_TYPE_INT; s->opt[OPT_HILITE_R].unit = SANE_UNIT_NONE; s->opt[OPT_HILITE_R].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_HILITE_R].constraint.range = &s->hw->info.HiliteR_range; s->opt[OPT_HILITE_R].cap |= SANE_CAP_INACTIVE; s->val[OPT_HILITE_R].w = 255; /* shadow point for red */ s->opt[OPT_SHADOW_R].name = SANE_NAME_SHADOW_R; s->opt[OPT_SHADOW_R].title = SANE_TITLE_SHADOW_R; s->opt[OPT_SHADOW_R].desc = SANE_DESC_SHADOW_R; s->opt[OPT_SHADOW_R].type = SANE_TYPE_INT; s->opt[OPT_SHADOW_R].unit = SANE_UNIT_NONE; s->opt[OPT_SHADOW_R].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_SHADOW_R].constraint.range = &s->hw->info.ShadowR_range; s->opt[OPT_SHADOW_R].cap |= SANE_CAP_INACTIVE; s->val[OPT_SHADOW_R].w = 0; /* highlight point for green */ s->opt[OPT_HILITE_G].name = SANE_NAME_HIGHLIGHT; s->opt[OPT_HILITE_G].title = SANE_TITLE_HIGHLIGHT; s->opt[OPT_HILITE_G].desc = SANE_DESC_HIGHLIGHT; s->opt[OPT_HILITE_G].type = SANE_TYPE_INT; s->opt[OPT_HILITE_G].unit = SANE_UNIT_NONE; s->opt[OPT_HILITE_G].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_HILITE_G].constraint.range = &s->hw->info.HiliteG_range; s->opt[OPT_HILITE_G].cap |= (s->hw->info.model == IX4015) ? SANE_CAP_INACTIVE : 0; s->val[OPT_HILITE_G].w = 255; /* shadow point for green */ s->opt[OPT_SHADOW_G].name = SANE_NAME_SHADOW; s->opt[OPT_SHADOW_G].title = SANE_TITLE_SHADOW; s->opt[OPT_SHADOW_G].desc = SANE_DESC_SHADOW; s->opt[OPT_SHADOW_G].type = SANE_TYPE_INT; s->opt[OPT_SHADOW_G].unit = SANE_UNIT_NONE; s->opt[OPT_SHADOW_G].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_SHADOW_G].constraint.range = &s->hw->info.ShadowG_range; s->opt[OPT_SHADOW_G].cap |= (s->hw->info.model == IX4015) ? SANE_CAP_INACTIVE : 0; s->val[OPT_SHADOW_G].w = 0; /* highlight point for blue */ s->opt[OPT_HILITE_B].name = SANE_NAME_HIGHLIGHT_B; s->opt[OPT_HILITE_B].title = SANE_TITLE_HIGHLIGHT_B; s->opt[OPT_HILITE_B].desc = SANE_DESC_HIGHLIGHT_B; s->opt[OPT_HILITE_B].type = SANE_TYPE_INT; s->opt[OPT_HILITE_B].unit = SANE_UNIT_NONE; s->opt[OPT_HILITE_B].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_HILITE_B].constraint.range = &s->hw->info.HiliteB_range; s->opt[OPT_HILITE_B].cap |= SANE_CAP_INACTIVE; s->val[OPT_HILITE_B].w = 255; /* shadow point for blue */ s->opt[OPT_SHADOW_B].name = SANE_NAME_SHADOW_B; s->opt[OPT_SHADOW_B].title = SANE_TITLE_SHADOW_B; s->opt[OPT_SHADOW_B].desc = SANE_DESC_SHADOW_B; s->opt[OPT_SHADOW_B].type = SANE_TYPE_INT; s->opt[OPT_SHADOW_B].unit = SANE_UNIT_NONE; s->opt[OPT_SHADOW_B].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_SHADOW_B].constraint.range = &s->hw->info.ShadowB_range; s->opt[OPT_SHADOW_B].cap |= SANE_CAP_INACTIVE; s->val[OPT_SHADOW_B].w = 0; /* "Enhancement" group: */ s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N("Enhancement"); s->opt[OPT_ENHANCEMENT_GROUP].desc = ""; s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_ENHANCEMENT_GROUP].cap = 0; s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* brightness */ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT; s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE; s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BRIGHTNESS].constraint.range = &s->hw->info.brightness_range; s->opt[OPT_BRIGHTNESS].cap |= 0; s->val[OPT_BRIGHTNESS].w = 128; /* contrast */ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST; s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST; s->opt[OPT_CONTRAST].type = SANE_TYPE_INT; s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE; s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CONTRAST].constraint.range = &s->hw->info.contrast_range; s->opt[OPT_CONTRAST].cap |= 0; s->val[OPT_CONTRAST].w = 128; /* 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_INT; s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE; s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_THRESHOLD].constraint.range = &s->hw->info.threshold_range; s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; s->val[OPT_THRESHOLD].w = 128; s->opt[OPT_MIRROR].name = "mirror"; s->opt[OPT_MIRROR].title = SANE_I18N("Mirror image"); s->opt[OPT_MIRROR].desc = SANE_I18N("Mirror the image horizontally"); s->opt[OPT_MIRROR].type = SANE_TYPE_BOOL; s->opt[OPT_MIRROR].cap |= (s->hw->info.can_mirror) ? 0: SANE_CAP_INACTIVE; s->val[OPT_MIRROR].w = SANE_FALSE; /* analog-gamma curve */ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL; s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE; /* bind analog-gamma */ s->opt[OPT_CUSTOM_GAMMA_BIND].name = "bind-custom-gamma"; s->opt[OPT_CUSTOM_GAMMA_BIND].title = SANE_TITLE_RGB_BIND; s->opt[OPT_CUSTOM_GAMMA_BIND].desc = SANE_DESC_RGB_BIND; s->opt[OPT_CUSTOM_GAMMA_BIND].type = SANE_TYPE_BOOL; s->opt[OPT_CUSTOM_GAMMA_BIND].cap |= SANE_CAP_INACTIVE; s->val[OPT_CUSTOM_GAMMA_BIND].w = SANE_TRUE; /* grayscale gamma vector */ s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR; s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR; s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR; s->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR].wa = &s->gamma_table[0][0]; /* red gamma vector */ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[1][0]; /* green gamma vector */ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[2][0]; /* blue gamma vector */ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[3][0]; s->opt[OPT_AE].name = "ae"; s->opt[OPT_AE].title = SANE_I18N("Auto exposure"); s->opt[OPT_AE].desc = SANE_I18N("Enable/disable the auto exposure feature"); s->opt[OPT_AE].cap |= (s->hw->info.can_autoexpose) ? 0 : SANE_CAP_INACTIVE; s->opt[OPT_AE].type = SANE_TYPE_BOOL; s->val[OPT_AE].w = SANE_FALSE; /* "Calibration" group */ s->opt[OPT_CALIBRATION_GROUP].title = SANE_I18N("Calibration"); s->opt[OPT_CALIBRATION_GROUP].desc = ""; s->opt[OPT_CALIBRATION_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_CALIBRATION_GROUP].cap |= (s->hw->info.can_calibrate || s->hw->info.can_diagnose) ? 0 : SANE_CAP_INACTIVE; s->opt[OPT_CALIBRATION_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* calibration now */ s->opt[OPT_CALIBRATION_NOW].name = "calibration-now"; s->opt[OPT_CALIBRATION_NOW].title = SANE_I18N("Calibration now"); s->opt[OPT_CALIBRATION_NOW].desc = SANE_I18N("Execute calibration *now*"); s->opt[OPT_CALIBRATION_NOW].type = SANE_TYPE_BUTTON; s->opt[OPT_CALIBRATION_NOW].unit = SANE_UNIT_NONE; s->opt[OPT_CALIBRATION_NOW].cap |= (s->hw->info.can_calibrate) ? 0 : SANE_CAP_INACTIVE; s->opt[OPT_CALIBRATION_NOW].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_CALIBRATION_NOW].constraint.range = NULL; /* scanner self diagnostic */ s->opt[OPT_SCANNER_SELF_DIAGNOSTIC].name = "self-diagnostic"; s->opt[OPT_SCANNER_SELF_DIAGNOSTIC].title = SANE_I18N("Self diagnosis"); s->opt[OPT_SCANNER_SELF_DIAGNOSTIC].desc = SANE_I18N("Perform scanner " "self diagnosis"); s->opt[OPT_SCANNER_SELF_DIAGNOSTIC].type = SANE_TYPE_BUTTON; s->opt[OPT_SCANNER_SELF_DIAGNOSTIC].unit = SANE_UNIT_NONE; s->opt[OPT_SCANNER_SELF_DIAGNOSTIC].cap |= (s->hw->info.can_diagnose) ? 0 : SANE_CAP_INACTIVE; s->opt[OPT_SCANNER_SELF_DIAGNOSTIC].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_SCANNER_SELF_DIAGNOSTIC].constraint.range = NULL; /* reset scanner for FB620S */ s->opt[OPT_RESET_SCANNER].name = "reset-scanner"; s->opt[OPT_RESET_SCANNER].title = SANE_I18N("Reset scanner"); s->opt[OPT_RESET_SCANNER].desc = SANE_I18N("Reset the scanner"); s->opt[OPT_RESET_SCANNER].type = SANE_TYPE_BUTTON; s->opt[OPT_RESET_SCANNER].unit = SANE_UNIT_NONE; s->opt[OPT_RESET_SCANNER].cap |= (s->hw->info.model == FB620) ? 0 : SANE_CAP_INACTIVE; s->opt[OPT_RESET_SCANNER].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_RESET_SCANNER].constraint.range = NULL; /* "Eject" group (active only for film scanners) */ s->opt[OPT_EJECT_GROUP].title = SANE_I18N("Medium handling"); s->opt[OPT_EJECT_GROUP].desc = ""; s->opt[OPT_EJECT_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_EJECT_GROUP].cap |= (s->hw->info.can_eject) ? 0 : SANE_CAP_INACTIVE; s->opt[OPT_EJECT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* eject after scan */ s->opt[OPT_EJECT_AFTERSCAN].name = "eject-after-scan"; s->opt[OPT_EJECT_AFTERSCAN].title = SANE_I18N("Eject film after each scan"); s->opt[OPT_EJECT_AFTERSCAN].desc = SANE_I18N("Automatically eject the " "film from the device after each scan"); s->opt[OPT_EJECT_AFTERSCAN].cap |= (s->hw->info.can_eject) ? 0 : SANE_CAP_INACTIVE; s->opt[OPT_EJECT_AFTERSCAN].type = SANE_TYPE_BOOL; /* IX-4015 requires medium_position command after cancel */ s->val[OPT_EJECT_AFTERSCAN].w = (s->hw->info.model == IX4015) ? SANE_TRUE : SANE_FALSE; /* eject before exit */ s->opt[OPT_EJECT_BEFOREEXIT].name = "eject-before-exit"; s->opt[OPT_EJECT_BEFOREEXIT].title = SANE_I18N("Eject film before exit"); s->opt[OPT_EJECT_BEFOREEXIT].desc = SANE_I18N("Automatically eject the " "film from the device before exiting the program"); s->opt[OPT_EJECT_BEFOREEXIT].cap |= (s->hw->info.can_eject) ? 0 : SANE_CAP_INACTIVE; s->opt[OPT_EJECT_BEFOREEXIT].type = SANE_TYPE_BOOL; s->val[OPT_EJECT_BEFOREEXIT].w = s->hw->info.can_eject; /* eject now */ s->opt[OPT_EJECT_NOW].name = "eject-now"; s->opt[OPT_EJECT_NOW].title = SANE_I18N("Eject film now"); s->opt[OPT_EJECT_NOW].desc = SANE_I18N("Eject the film *now*"); s->opt[OPT_EJECT_NOW].type = SANE_TYPE_BUTTON; s->opt[OPT_EJECT_NOW].unit = SANE_UNIT_NONE; s->opt[OPT_EJECT_NOW].cap |= (s->hw->info.can_eject) ? 0 : SANE_CAP_INACTIVE; s->opt[OPT_EJECT_NOW].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_EJECT_NOW].constraint.range = NULL; /* "NO-ADF" option: */ s->opt[OPT_ADF_GROUP].title = SANE_I18N("Document feeder extras"); s->opt[OPT_ADF_GROUP].desc = ""; s->opt[OPT_ADF_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_ADF_GROUP].cap = 0; s->opt[OPT_ADF_GROUP].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_FLATBED_ONLY].name = "noadf"; s->opt[OPT_FLATBED_ONLY].title = SANE_I18N("Flatbed only"); s->opt[OPT_FLATBED_ONLY].desc = SANE_I18N("Disable auto document feeder " "and use flatbed only"); s->opt[OPT_FLATBED_ONLY].type = SANE_TYPE_BOOL; s->opt[OPT_FLATBED_ONLY].unit = SANE_UNIT_NONE; s->opt[OPT_FLATBED_ONLY].size = sizeof (SANE_Word); s->opt[OPT_FLATBED_ONLY].cap |= (s->hw->adf.Status == ADF_STAT_NONE) ? SANE_CAP_INACTIVE : 0; s->val[OPT_FLATBED_ONLY].w = SANE_FALSE; /* "TPU" group: */ s->opt[OPT_TPU_GROUP].title = SANE_I18N("Transparency unit"); s->opt[OPT_TPU_GROUP].desc = ""; s->opt[OPT_TPU_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_TPU_GROUP].cap = 0; s->opt[OPT_TPU_GROUP].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_TPU_GROUP].cap |= (s->hw->tpu.Status != TPU_STAT_NONE) ? 0 : SANE_CAP_INACTIVE; /* Transparency Unit (FAU, Film Adapter Unit) */ s->opt[OPT_TPU_ON].name = "transparency-unit-on-off"; s->opt[OPT_TPU_ON].title = SANE_I18N("Transparency unit"); s->opt[OPT_TPU_ON].desc = SANE_I18N("Switch on/off the transparency unit " "(FAU, film adapter unit)"); s->opt[OPT_TPU_ON].type = SANE_TYPE_BOOL; s->opt[OPT_TPU_ON].unit = SANE_UNIT_NONE; s->val[OPT_TPU_ON].w = (s->hw->tpu.Status == TPU_STAT_ACTIVE) ? SANE_TRUE : SANE_FALSE; s->opt[OPT_TPU_ON].cap |= (s->hw->tpu.Status != TPU_STAT_NONE) ? 0 : SANE_CAP_INACTIVE; s->opt[OPT_TPU_PN].name = "transparency-unit-negative-film"; s->opt[OPT_TPU_PN].title = SANE_I18N("Negative film"); s->opt[OPT_TPU_PN].desc = SANE_I18N("Positive or negative film"); s->opt[OPT_TPU_PN].type = SANE_TYPE_BOOL; s->opt[OPT_TPU_PN].unit = SANE_UNIT_NONE; s->val[OPT_TPU_PN].w = s->hw->tpu.PosNeg; s->opt[OPT_TPU_PN].cap |= (s->hw->tpu.Status == TPU_STAT_ACTIVE) ? 0 : SANE_CAP_INACTIVE; /* density control mode */ s->opt[OPT_TPU_DCM].name = "TPMDC"; s->opt[OPT_TPU_DCM].title = SANE_I18N("Density control"); s->opt[OPT_TPU_DCM].desc = SANE_I18N("Set density control mode"); s->opt[OPT_TPU_DCM].type = SANE_TYPE_STRING; s->opt[OPT_TPU_DCM].size = max_string_size (tpu_dc_mode_list); s->opt[OPT_TPU_DCM].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_TPU_DCM].constraint.string_list = tpu_dc_mode_list; s->val[OPT_TPU_DCM].s = strdup (tpu_dc_mode_list[s->hw->tpu.ControlMode]); s->opt[OPT_TPU_DCM].cap |= (s->hw->tpu.Status == TPU_STAT_ACTIVE) ? 0 : SANE_CAP_INACTIVE; /* Transparency Ratio */ s->opt[OPT_TPU_TRANSPARENCY].name = "Transparency-Ratio"; s->opt[OPT_TPU_TRANSPARENCY].title = SANE_I18N("Transparency ratio"); s->opt[OPT_TPU_TRANSPARENCY].desc = ""; s->opt[OPT_TPU_TRANSPARENCY].type = SANE_TYPE_INT; s->opt[OPT_TPU_TRANSPARENCY].unit = SANE_UNIT_NONE; s->opt[OPT_TPU_TRANSPARENCY].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TPU_TRANSPARENCY].constraint.range = &s->hw->info.TPU_Transparency_range; s->val[OPT_TPU_TRANSPARENCY].w = s->hw->tpu.Transparency; s->opt[OPT_TPU_TRANSPARENCY].cap |= (s->hw->tpu.Status == TPU_STAT_ACTIVE && s->hw->tpu.ControlMode == 3) ? 0 : SANE_CAP_INACTIVE; /* Select Film type */ s->opt[OPT_TPU_FILMTYPE].name = "Filmtype"; s->opt[OPT_TPU_FILMTYPE].title = SANE_I18N("Select film type"); s->opt[OPT_TPU_FILMTYPE].desc = SANE_I18N("Select the film type"); s->opt[OPT_TPU_FILMTYPE].type = SANE_TYPE_STRING; s->opt[OPT_TPU_FILMTYPE].size = max_string_size (tpu_filmtype_list); s->opt[OPT_TPU_FILMTYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_TPU_FILMTYPE].constraint.string_list = tpu_filmtype_list; s->val[OPT_TPU_FILMTYPE].s = strdup (tpu_filmtype_list[s->hw->tpu.FilmType]); s->opt[OPT_TPU_FILMTYPE].cap |= (s->hw->tpu.Status == TPU_STAT_ACTIVE && s->hw->tpu.ControlMode == 1) ? 0 : SANE_CAP_INACTIVE; /* preview */ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; s->val[OPT_PREVIEW].w = SANE_FALSE; DBG (1, "<< init_options\n"); return SANE_STATUS_GOOD; } /**************************************************************************/ static SANE_Status attach_one (const char *dev) { DBG (1, ">> attach_one\n"); attach (dev, 0); DBG (1, "<< attach_one\n"); return SANE_STATUS_GOOD; } /**************************************************************************/ static SANE_Status do_focus (CANON_Scanner * s) { SANE_Status status; u_char ebuf[74]; size_t buf_size; DBG (3, "do_focus: sending GET FILM STATUS\n"); memset (ebuf, 0, sizeof (ebuf)); buf_size = 4; status = get_film_status (s->fd, ebuf, &buf_size); if (status != SANE_STATUS_GOOD) { DBG (1, "do_focus: GET FILM STATUS failed\n"); if (status == SANE_STATUS_UNSUPPORTED) return (SANE_STATUS_GOOD); else { DBG (1, "do_focus: ... for unknown reasons\n"); sanei_scsi_close (s->fd); s->fd = -1; return (SANE_STATUS_INVAL); } } DBG (3, "focus point before autofocus : %d\n", ebuf[3]); status = execute_auto_focus (s->fd, s->val[OPT_AF].w, (s->scanning_speed == 0 && !s->RIF && s->hw->info.model == CS2700), (int) s->AE, s->val[OPT_FOCUS].w); if (status != SANE_STATUS_GOOD) { DBG (7, "execute_auto_focus failed\n"); if (status == SANE_STATUS_UNSUPPORTED) return (SANE_STATUS_GOOD); else { DBG (1, "do_focus: ... for unknown reasons\n"); sanei_scsi_close (s->fd); s->fd = -1; return (SANE_STATUS_INVAL); } } DBG (3, "do_focus: sending GET FILM STATUS\n"); memset (ebuf, 0, sizeof (ebuf)); buf_size = 4; status = get_film_status (s->fd, ebuf, &buf_size); if (status != SANE_STATUS_GOOD) { DBG (1, "do_focus: GET FILM STATUS failed\n"); if (status == SANE_STATUS_UNSUPPORTED) return (SANE_STATUS_GOOD); else { DBG (1, "do_focus: ... for unknown reasons\n"); sanei_scsi_close (s->fd); s->fd = -1; return (SANE_STATUS_INVAL); } } else DBG (3, "focus point after autofocus : %d\n", ebuf[3]); return (SANE_STATUS_GOOD); } /**************************************************************************/ #include "canon-sane.c" backends-1.3.0/backend/canon.conf.in000066400000000000000000000000321456256263500172370ustar00rootroot00000000000000#canon.conf scsi CANON IX backends-1.3.0/backend/canon.h000066400000000000000000000242051456256263500161440ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1997 BYTEC GmbH Germany Written by Helmut Koeberle, Email: helmut.koeberle@bytec.de Modified by Manuel Panea , Markus Mertinat , and ULrich Deiters 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, see . 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 canon_h #define canon_h 1 /* all the different possible model names. */ #define FB1200S "IX-12015E " #define FB620S "IX-06035E " #define CS300 "IX-03035B " #define CS600 "IX-06015C " #define CS2700F "IX-27015C " #define IX_4025 "IX-4025 " #define IX_4015 "IX-4015 " #define IX_3010 "IX-3010 " #include #define AUTO_DOC_FEEDER_UNIT 0x01 #define TRANSPARENCY_UNIT 0x02 #define TRANSPARENCY_UNIT_FB1200 0x03 #define SCAN_CONTROL_CONDITIONS 0x20 #define SCAN_CONTROL_CON_FB1200 0x21 #define ALL_SCAN_MODE_PAGES 0x3F #define RED 0 #define GREEN 1 #define BLUE 2 #define ADF_STAT_NONE 0 #define ADF_STAT_INACTIVE 1 #define ADF_STAT_ACTIVE 2 #define ADF_STAT_DISABLED 3 #define ADF_Status (4+2) /* byte positioning */ #define ADF_Settings (4+3) /* in data block */ #define ADF_NOT_PRESENT 0x01 /* bit selection */ #define ADF_PROBLEM 0x0E /* from bytes in */ #define ADF_PRIORITY 0x03 /* data block. */ #define ADF_FEEDER 0x04 /* */ #define TPU_STAT_NONE 0 #define TPU_STAT_INACTIVE 1 #define TPU_STAT_ACTIVE 2 #define CS3_600 0 /* CanoScan 300/600 */ #define CS2700 1 /* CanoScan 2700F */ #define FB620 2 /* CanoScan FB620S */ #define FS2710 3 /* CanoScan FS2710S */ #define FB1200 4 /* CanoScan FB1200S */ #define IX4015 5 /* IX-4015 */ #ifndef MAX #define MAX(A,B) (((A) > (B))? (A) : (B)) #endif #ifndef MIN #define MIN(A,B) (((A) < (B))? (A) : (B)) #endif #ifndef SSIZE_MAX #define SSIZE_MAX LONG_MAX #endif typedef struct { SANE_Int Status; /* Auto Document Feeder Unit Status */ SANE_Int Problem; /* ADF Problems list */ SANE_Int Priority; /* ADF Priority setting */ SANE_Int Feeder; /* ADF Feeder setting */ } CANON_ADF; typedef struct { SANE_Int Status; /* Transparency Unit Status */ SANE_Bool PosNeg; /* Negative/Positive Film */ SANE_Int Transparency; /* TPU Transparency */ SANE_Int ControlMode; /* TPU Density Control Mode */ SANE_Int FilmType; /* TPU Film Type */ } CANON_TPU; typedef enum { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_MODE, OPT_NEGATIVE, /* Reverse image format */ OPT_NEGATIVE_TYPE, /* Negative film type */ OPT_SCANNING_SPEED, OPT_RESOLUTION_GROUP, OPT_RESOLUTION_BIND, OPT_HW_RESOLUTION_ONLY, OPT_X_RESOLUTION, OPT_Y_RESOLUTION, OPT_ENHANCEMENT_GROUP, OPT_BRIGHTNESS, OPT_CONTRAST, OPT_THRESHOLD, OPT_MIRROR, OPT_CUSTOM_GAMMA, /* use custom gamma tables? */ OPT_CUSTOM_GAMMA_BIND, /* The gamma vectors MUST appear in the order gray, red, green, blue. */ OPT_GAMMA_VECTOR, OPT_GAMMA_VECTOR_R, OPT_GAMMA_VECTOR_G, OPT_GAMMA_VECTOR_B, OPT_AE, /* Auto Exposure */ OPT_CALIBRATION_GROUP, /* Calibration for FB620S */ OPT_CALIBRATION_NOW, /* Execute Calibration now for FB620S */ OPT_SCANNER_SELF_DIAGNOSTIC, /* Self diagnostic for FB620S */ OPT_RESET_SCANNER, /* Reset scanner for FB620S */ OPT_EJECT_GROUP, OPT_EJECT_AFTERSCAN, OPT_EJECT_BEFOREEXIT, OPT_EJECT_NOW, OPT_FOCUS_GROUP, OPT_AF, /* Auto Focus */ OPT_AF_ONCE, /* Auto Focus only once between ejects */ OPT_FOCUS, /* Manual focus position */ OPT_MARGINS_GROUP, /* scan margins */ OPT_TL_X, /* top-left x */ OPT_TL_Y, /* top-left y */ OPT_BR_X, /* bottom-right x */ OPT_BR_Y, /* bottom-right y */ OPT_COLORS_GROUP, OPT_HNEGATIVE, /* Reverse image format */ OPT_BIND_HILO, /* Same values vor highlight and shadow points for red, green, blue */ OPT_HILITE_R, /* highlight point for red */ OPT_SHADOW_R, /* shadow point for red */ OPT_HILITE_G, /* highlight point for green */ OPT_SHADOW_G, /* shadow point for green */ OPT_HILITE_B, /* highlight point for blue */ OPT_SHADOW_B, /* shadow point for blue */ OPT_ADF_GROUP, /* to allow display of options. */ OPT_FLATBED_ONLY, /* in case you have a sheetfeeder but don't want to use it. */ OPT_TPU_GROUP, OPT_TPU_ON, OPT_TPU_PN, OPT_TPU_DCM, OPT_TPU_TRANSPARENCY, OPT_TPU_FILMTYPE, OPT_PREVIEW, /* must come last: */ NUM_OPTIONS } CANON_Option; typedef struct CANON_Info { int model; SANE_Range xres_range; SANE_Range yres_range; SANE_Range x_range; SANE_Range y_range; SANE_Range brightness_range; SANE_Range contrast_range; SANE_Range threshold_range; SANE_Range HiliteR_range; SANE_Range ShadowR_range; SANE_Range HiliteG_range; SANE_Range ShadowG_range; SANE_Range HiliteB_range; SANE_Range ShadowB_range; SANE_Range focus_range; SANE_Range x_adf_range; SANE_Range y_adf_range; SANE_Int xres_default; SANE_Int yres_default; SANE_Int bmu; SANE_Int mud; SANE_Range TPU_Transparency_range; SANE_Int TPU_Stat; SANE_Bool can_focus; /* has got focus control */ SANE_Bool can_autoexpose; /* can do autoexposure by hardware */ SANE_Bool can_calibrate; /* has got calibration control */ SANE_Bool can_diagnose; /* has diagnostic command */ SANE_Bool can_eject; /* can eject medium */ SANE_Bool can_mirror; /* can mirror image by hardware */ SANE_Bool is_filmscanner; SANE_Bool has_fixed_resolutions; /* only a finite number possible */ } CANON_Info; typedef struct CANON_Device { struct CANON_Device *next; SANE_Device sane; CANON_Info info; CANON_ADF adf; CANON_TPU tpu; } CANON_Device; typedef struct CANON_Scanner { struct CANON_Scanner *next; int fd; CANON_Device *hw; SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; char *sense_str; /* sense string */ SANE_Int gamma_table[4][256]; SANE_Parameters params; SANE_Bool AF_NOW; /* To keep track of when to do AF */ SANE_Int xres; SANE_Int yres; SANE_Int ulx; SANE_Int uly; SANE_Int width; SANE_Int length; SANE_Int brightness; SANE_Int contrast; SANE_Int threshold; SANE_Int image_composition; SANE_Int bpp; SANE_Bool RIF; /* Reverse Image Format */ SANE_Int negative_filmtype; SANE_Int scanning_speed; SANE_Bool GRC; /* Gray Response Curve */ SANE_Bool Mirror; SANE_Bool AE; /* Auto Exposure */ SANE_Int HiliteR; SANE_Int ShadowR; SANE_Int HiliteG; SANE_Int ShadowG; SANE_Int HiliteB; SANE_Int ShadowB; /* 990320, ss: array for fixed resolutions */ SANE_Word xres_word_list[16]; SANE_Word yres_word_list[16]; SANE_Byte *inbuffer; /* modification for FB620S */ SANE_Byte *outbuffer; /* modification for FB620S */ SANE_Int buf_used; /* modification for FB620S */ SANE_Int buf_pos; /* modification for FB620S */ time_t time0; /* modification for FB620S */ time_t time1; /* modification for FB620S */ int switch_preview; /* modification for FB620S */ int reset_flag; /* modification for FB620S */ int tmpfile; /* modification for FB1200S */ size_t bytes_to_read; int scanning; u_char gamma_map[4][4096]; /* for FS2710S: */ int colour; /* index to gamma_map */ int auxbuf_len; /* size of auxiliary buffer */ u_char *auxbuf; } CANON_Scanner; static char *option_name[] = { "OPT_NUM_OPTS", "OPT_MODE_GROUP", "OPT_MODE", "OPT_NEGATIVE", "OPT_NEGATIVE_TYPE", "OPT_SCANNING_SPEED", "OPT_RESOLUTION_GROUP", "OPT_RESOLUTION_BIND", "OPT_HW_RESOLUTION_ONLY", "OPT_X_RESOLUTION", "OPT_Y_RESOLUTION", "OPT_ENHANCEMENT_GROUP", "OPT_BRIGHTNESS", "OPT_CONTRAST", "OPT_THRESHOLD", "OPT_MIRROR", "OPT_CUSTOM_GAMMA", "OPT_CUSTOM_GAMMA_BIND", "OPT_GAMMA_VECTOR", "OPT_GAMMA_VECTOR_R", "OPT_GAMMA_VECTOR_G", "OPT_GAMMA_VECTOR_B", "OPT_AE", "OPT_CALIBRATION_GROUP", "OPT_CALIBRATION_NOW", "OPT_SCANNER_SELF_DIAGNOSTIC", "OPT_RESET_SCANNER", "OPT_EJECT_GROUP", "OPT_EJECT_AFTERSCAN", "OPT_EJECT_BEFOREEXIT", "OPT_EJECT_NOW", "OPT_FOCUS_GROUP", "OPT_AF", "OPT_AF_ONCE", "OPT_FOCUS", "OPT_MARGINS_GROUP", "OPT_TL_X", "OPT_TL_Y", "OPT_BR_X", "OPT_BR_Y", "OPT_COLORS_GROUP", "OPT_HNEGATIVE", "OPT_BIND_HILO", "OPT_HILITE_R", "OPT_SHADOW_R", "OPT_HILITE_G", "OPT_SHADOW_G", "OPT_HILITE_B", "OPT_SHADOW_B", "OPT_ADF_GROUP", "OPT_FLATBED_ONLY", "OPT_TPU_GROUP", "OPT_TPU_ON", "OPT_TPU_PN", "OPT_TPU_DCM", "OPT_TPU_TRANSPARENCY", "OPT_TPU_FILMTYPE", "OPT_PREVIEW", "NUM_OPTIONS" }; #endif /* not canon_h */ backends-1.3.0/backend/canon630u-common.c000066400000000000000000001327361456256263500200540ustar00rootroot00000000000000/* (c) 2001,2002 Nathan Rutman nathan@gordian.com 10/17/01 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, see . 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. */ /* Communication, calibration, and scanning with the Canon CanoScan FB630U flatbed scanner under linux. Reworked into SANE-compatible format. The usb-parallel port interface chip is GL640usb, on the far side of which is an LM9830 parallel-port scanner-on-a-chip. This code has not been tested on anything other than Linux/i386. */ #include #include /* open */ #include #include #include #include /* usleep */ #include #include /* exp() */ #ifdef HAVE_OS2_H #include /* mode_t */ #endif #include #include "lm9830.h" #define USB_TYPE_VENDOR (0x02 << 5) #define USB_RECIP_DEVICE 0x00 #define USB_DIR_OUT 0x00 #define USB_DIR_IN 0x80 /* Assign status and verify a good return code */ #define CHK(A) {if( (status = A) != SANE_STATUS_GOOD ) { \ DBG( 1, "Failure on line of %s: %d\n", __FILE__, \ __LINE__ ); return A; }} typedef SANE_Byte byte; /***************************************************** GL640 communication primitives Provides I/O routines to Genesys Logic GL640USB USB-IEEE1284 parallel port bridge. Used in HP3300c, Canon FB630u. ******************************************************/ /* Register codes for the bridge. These are NOT the registers for the scanner chip on the other side of the bridge. */ typedef enum { GL640_BULK_SETUP = 0x82, GL640_EPP_ADDR = 0x83, GL640_EPP_DATA_READ = 0x84, GL640_EPP_DATA_WRITE = 0x85, GL640_SPP_STATUS = 0x86, GL640_SPP_CONTROL = 0x87, GL640_SPP_DATA = 0x88, GL640_GPIO_OE = 0x89, GL640_GPIO_READ = 0x8a, GL640_GPIO_WRITE = 0x8b } GL640_Request; /* Write to the usb-parallel port bridge. */ static SANE_Status gl640WriteControl (int fd, GL640_Request req, byte * data, unsigned int size) { SANE_Status status; status = sanei_usb_control_msg (fd, /* rqttype */ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT /*0x40? */ , /* rqt */ (size > 1) ? 0x04 : 0x0C, /* val */ (SANE_Int) req, /* ind */ 0, /* len */ size, /* dat */ data); if (status != SANE_STATUS_GOOD) DBG (1, "gl640WriteControl error\n"); return status; } /* Read from the usb-parallel port bridge. */ static SANE_Status gl640ReadControl (int fd, GL640_Request req, byte * data, unsigned int size) { SANE_Status status; status = sanei_usb_control_msg (fd, /* rqttype */ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN /*0xc0? */ , /* rqt */ (size > 1) ? 0x04 : 0x0C, /* val */ (SANE_Int) req, /* ind */ 0, /* len */ size, /* dat */ data); if (status != SANE_STATUS_GOOD) DBG (1, "gl640ReadControl error\n"); return status; } /* Wrappers to read or write a single byte to the bridge */ static inline SANE_Status gl640WriteReq (int fd, GL640_Request req, byte data) { return gl640WriteControl (fd, req, &data, 1); } static inline SANE_Status gl640ReadReq (int fd, GL640_Request req, byte * data) { return gl640ReadControl (fd, req, data, 1); } /* Write USB bulk data setup is an apparently scanner-specific sequence: {(0=read, 1=write), 0x00, 0x00, 0x00, sizelo, sizehi, 0x00, 0x00} hp3400: setup[1] = 0x01 fb630u: setup[2] = 0x80 */ static SANE_Status gl640WriteBulk (int fd, byte * setup, byte * data, size_t size) { SANE_Status status; setup[0] = 1; setup[4] = (size) & 0xFF; setup[5] = (size >> 8) & 0xFF; CHK (gl640WriteControl (fd, GL640_BULK_SETUP, setup, 8)); status = sanei_usb_write_bulk (fd, data, &size); if (status != SANE_STATUS_GOOD) DBG (1, "gl640WriteBulk error\n"); return status; } /* Read USB bulk data setup is an apparently scanner-specific sequence: {(0=read, 1=write), 0x00, 0x00, 0x00, sizelo, sizehi, 0x00, 0x00} fb630u: setup[2] = 0x80 */ static SANE_Status gl640ReadBulk (int fd, byte * setup, byte * data, size_t size) { SANE_Status status; setup[0] = 0; setup[4] = (size) & 0xFF; setup[5] = (size >> 8) & 0xFF; CHK (gl640WriteControl (fd, GL640_BULK_SETUP, setup, 8)); status = sanei_usb_read_bulk (fd, data, &size); if (status != SANE_STATUS_GOOD) DBG (1, "gl640ReadBulk error\n"); return status; } /***************************************************** LM9830 communication primitives parallel-port scanner-on-a-chip. ******************************************************/ /* write 1 byte to a LM9830 register address */ static SANE_Status write_byte (int fd, byte addr, byte val) { SANE_Status status; DBG (14, "write_byte(fd, 0x%02x, 0x%02x);\n", addr, val); CHK (gl640WriteReq (fd, GL640_EPP_ADDR, addr)); CHK (gl640WriteReq (fd, GL640_EPP_DATA_WRITE, val)); return status; } /* read 1 byte from a LM9830 register address */ static SANE_Status read_byte (int fd, byte addr, byte * val) { SANE_Status status; CHK (gl640WriteReq (fd, GL640_EPP_ADDR, addr)); CHK (gl640ReadReq (fd, GL640_EPP_DATA_READ, val)); DBG (14, "read_byte(fd, 0x%02x, &result); /* got %02x */\n", addr, *val); return status; } static byte bulk_setup_data[] = { 0, 0, 0x80, 0, 0, 0, 0, 0 }; /* Bulk write */ static SANE_Status write_bulk (int fd, unsigned int addr, void *src, size_t count) { SANE_Status status; DBG (13, "write_bulk(fd, 0x%02x, buf, 0x%04lx);\n", addr, (u_long) count); if (!src) { DBG (1, "write_bulk: bad src\n"); return SANE_STATUS_INVAL; } /* destination address */ CHK (gl640WriteReq (fd, GL640_EPP_ADDR, addr)); /* write */ CHK (gl640WriteBulk (fd, bulk_setup_data, src, count)); return status; } /* Bulk read */ static SANE_Status read_bulk (int fd, unsigned int addr, void *dst, size_t count) { SANE_Status status; DBG (13, "read_bulk(fd, 0x%02x, buf, 0x%04lx);\n", addr, (u_long) count); if (!dst) { DBG (1, "read_bulk: bad dest\n"); return SANE_STATUS_INVAL; } /* destination address */ CHK (gl640WriteReq (fd, GL640_EPP_ADDR, addr)); /* read */ CHK (gl640ReadBulk (fd, bulk_setup_data, dst, count)); return status; } /***************************************************** useful macro routines ******************************************************/ /* write a 16-bit int to two sequential registers */ static SANE_Status write_word (int fd, unsigned int addr, unsigned int data) { SANE_Status status; /* MSB */ CHK (write_byte (fd, addr, (data >> 8) & 0xff)); /* LSB */ CHK (write_byte (fd, addr + 1, data & 0xff)); return status; } /* write multiple bytes, one at a time (non-bulk) */ static SANE_Status write_many (int fd, unsigned int addr, const byte *src, size_t count) { SANE_Status status; size_t i; DBG (14, "multi write %lu\n", (u_long) count); for (i = 0; i < count; i++) { DBG (15, " %04lx:%02x", (u_long) (addr + i), src[i]); status = write_byte (fd, addr + i, src[i]); if (status != SANE_STATUS_GOOD) { DBG (15, "\n"); return status; } } DBG (15, "\n"); return SANE_STATUS_GOOD; } /* read multiple bytes, one at a time (non-bulk) */ static SANE_Status read_many (int fd, unsigned int addr, void *dst, size_t count) { SANE_Status status; size_t i; byte val; DBG (14, "multi read %lu\n", (u_long) count); for (i = 0; i < count; i++) { status = read_byte (fd, addr + i, &val); ((byte *) dst)[i] = val; DBG (15, " %04lx:%02x", (u_long) (addr + i), ((byte *) dst)[i]); /* on err, return number of success */ if (status != SANE_STATUS_GOOD) { DBG (15, "\n"); return status; } } DBG (15, "\n"); return SANE_STATUS_GOOD; } /* Poll addr until result & mask = val */ static int read_poll_flag (int fd, unsigned int addr, unsigned int mask, unsigned int val) { SANE_Status status; byte result = 0; time_t start_time = time (NULL); DBG (12, "read_poll_flag...\n"); do { status = read_byte (fd, addr, &result); if (status != SANE_STATUS_GOOD) return -1; /* Give it a minute */ if ((time (NULL) - start_time) > 60) { DBG (1, "read_poll_flag: timed out (%d)\n", result); return -1; } usleep (100000); } while ((result & mask) != val); return result; } /* Keep reading addr until results >= min */ static int read_poll_min (int fd, unsigned int addr, unsigned int min) { SANE_Status status; byte result; time_t start_time = time (NULL); DBG (12, "waiting...\n"); do { status = read_byte (fd, addr, &result); if (status != SANE_STATUS_GOOD) return -1; /* Give it a minute */ if ((time (NULL) - start_time) > 60) { DBG (1, "read_poll_min: timed out (%d < %d)\n", result, min); return -1; } /* no sleep here, or calibration gets unhappy. */ } while (result < min); return result; } /* Bulk read "ks" kilobytes + "remainder" bytes of data, to a buffer if the buffer is valid. */ static int read_bulk_size (int fd, int ks, int remainder, byte * dest, int destsize) { byte *buf; int bytes = (ks - 1) * 1024 + remainder; int dropdata = ((dest == 0) || (destsize < bytes)); if (bytes < 0) { DBG (1, "read_bulk_size: invalid size %02x (%d)\n", ks, bytes); return -1; } if (destsize && (destsize < bytes)) { DBG (3, "read_bulk_size: more data than buffer (%d/%d)\n", destsize, bytes); bytes = destsize; } if (bytes == 0) return 0; if (dropdata) { buf = malloc (bytes); DBG (3, " ignoring data "); } else buf = dest; read_bulk (fd, 0x00, buf, bytes); if (dropdata) free (buf); return bytes; } /***************************************************** fb630u calibration and scan ******************************************************/ /* data structures and constants */ typedef struct CANON_Handle { int fd; /* scanner fd */ int x1, x2, y1, y2; /* in pixels, 600 dpi */ int width, height; /* at scan resolution */ int resolution; /* dpi */ char *fname; /* output file name */ FILE *fp; /* output file pointer (for reading) */ char *buf, *ptr; /* data buffer */ unsigned char gain; /* static analog gain, 0 - 31 */ double gamma; /* gamma correction */ int flags; #define FLG_GRAY 0x01 /* grayscale */ #define FLG_FORCE_CAL 0x02 /* force calibration */ #define FLG_BUF 0x04 /* save scan to buffer instead of file */ #define FLG_NO_INTERLEAVE 0x08 /* don't interleave r,g,b pixels; leave them in row format */ #define FLG_PPM_HEADER 0x10 /* include PPM header in scan file */ } CANON_Handle; /* offset/gain calibration file name */ #define CAL_FILE_OGN "/tmp/canon.cal" /* at 600 dpi */ #define CANON_MAX_WIDTH 5100 /* 8.5in */ /* this may not be right */ #define CANON_MAX_HEIGHT 7000 /* 11.66in */ /* scanline end-of-line data byte, returned after each r,g,b segment, specific to the FB630u */ #define SCANLINE_END 0x0c static const byte seq002[] = { /*r08 */ 0x04, /*300 dpi */ 0x1a, 0x00, 0x0d, 0x4c, 0x2f, 0x00, 0x01, /*r10 */ 0x07, 0x04, 0x05, 0x06, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x25, 0x00, 0x4b, /*r20 */ 0x15, 0xe0, /*data px start */ 0x00, 0x4b, /*data px end */ 0x14, 0x37, 0x15, 0x00 }; static const byte seq003[] = { 0x02, 0x00, 0x00, /*lights out */ 0x03, 0xff, 0x00, 0x01, 0x03, 0xff, 0x00, 0x01, 0x03, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1d, 0x00, 0x13, 0x04, 0x1a, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x57, 0x02, 0x00, 0x3c, 0x35, 0x94, 0x00, 0x10, 0x08, 0x3f, 0x2b, 0x91, 0x00, 0x00, 0x01, 0x00, 0x80, 0x00 }; /* Scanner init, called at calibration and scan time. Returns 1 if this was the first time the scanner was plugged in, 0 afterward, and -1 on error. */ static int init (int fd) { byte result, rv; if (gl640WriteReq (fd, GL640_GPIO_OE, 0x71) != SANE_STATUS_GOOD) { DBG(1, "Initial write request failed.\n"); return -1; } /* Gets 0x04 or 0x05 first run, gets 0x64 subsequent runs. */ if (gl640ReadReq (fd, GL640_GPIO_READ, &rv) != SANE_STATUS_GOOD) { DBG(1, "Initial read request failed.\n"); return -1; } gl640WriteReq (fd, GL640_GPIO_OE, 0x70); DBG (2, "init query: %x\n", rv); if (rv != 0x64) { gl640WriteReq (fd, GL640_GPIO_WRITE, 0x00); gl640WriteReq (fd, GL640_GPIO_WRITE, 0x40); } gl640WriteReq (fd, GL640_SPP_DATA, 0x99); gl640WriteReq (fd, GL640_SPP_DATA, 0x66); gl640WriteReq (fd, GL640_SPP_DATA, 0xcc); gl640WriteReq (fd, GL640_SPP_DATA, 0x33); /* parallel port setting */ write_byte (fd, PARALLEL_PORT, 0x06); /* sensor control settings */ write_byte (fd, 0x0b, 0x0d); write_byte (fd, 0x0c, 0x4c); write_byte (fd, 0x0d, 0x2f); read_byte (fd, 0x0b, &result); /* wants 0d */ read_byte (fd, 0x0c, &result); /* wants 4c */ read_byte (fd, 0x0d, &result); /* wants 2f */ /* parallel port noise filter */ write_byte (fd, 0x70, 0x73); DBG (2, "init post-reset: %x\n", rv); /* Returns 1 if this was the first time the scanner was plugged in. */ return (rv != 0x64); } /* Turn off the lamps */ static void lights_out (int fd) { write_word (fd, LAMP_R_ON, 0x3fff); write_word (fd, LAMP_R_OFF, 0x0001); write_word (fd, LAMP_G_ON, 0x3fff); write_word (fd, LAMP_G_OFF, 0x0001); write_word (fd, LAMP_B_ON, 0x3fff); write_word (fd, LAMP_B_OFF, 0x0001); } /* Do the scan and save the resulting image as r,g,b interleaved PPM file. */ static SANE_Status do_scan (CANON_Handle * s) { SANE_Status status = SANE_STATUS_GOOD; int numbytes, datasize, level = 0, line = 0, pixel = 0; byte *buf, *ptr, *redptr; FILE *fp; #define BUFSIZE 0xf000 buf = malloc (BUFSIZE); if (!buf) return SANE_STATUS_NO_MEM; if (s->flags & FLG_BUF) { /* read the whole thing into buf */ if (!s->buf) return SANE_STATUS_NO_MEM; s->ptr = s->buf; fp = NULL; } else { fp = fopen (s->fname, "w"); if (!fp) { free (buf); DBG (1, "err:%s when opening %s\n", strerror (errno), s->fname); return SANE_STATUS_IO_ERROR; } } if (fp && (s->flags & FLG_PPM_HEADER)) /* PPM format header */ fprintf (fp, "P6\n%d %d\n255\n", s->width, s->height); /* lights off */ write_byte (s->fd, COMMAND, 0x08); /* lights on */ write_byte (s->fd, COMMAND, 0x00); /* begin scan */ write_byte (s->fd, COMMAND, 0x03); ptr = redptr = buf; while (line < s->height) { datasize = read_poll_min (s->fd, IMAGE_DATA_AVAIL, 2); if (datasize < 0) { DBG (1, "no data\n"); break; } DBG (12, "scan line %d %dk\n", line, datasize - 1); /* Read may cause scan head to move */ numbytes = read_bulk_size (s->fd, datasize, 0, ptr, BUFSIZE - level); if (numbytes < 0) { status = SANE_STATUS_INVAL; break; } /* Data coming back is "width" bytes Red data followed by 0x0c, width bytes Green, 0x0c, width bytes Blue, 0x0c, repeat for "height" lines. */ if (s->flags & FLG_NO_INTERLEAVE) { /* number of full lines */ line += (numbytes + level) / (s->width * 3); /* remainder (partial line) */ level = (numbytes + level) % (s->width * 3); /* but if last line, don't store extra */ if (line >= s->height) numbytes -= (line - s->height) * s->width * 3 + level; if (fp) fwrite (buf, 1, numbytes, fp); else { memcpy (s->ptr, buf, numbytes); s->ptr += numbytes; } } else { /* Contorsions to convert data from line-by-line RGB to byte-by-byte RGB, without reading in the whole buffer first. We use the sliding window redptr with the temp buffer buf. */ ptr += numbytes; /* point to the end of data */ /* while we have RGB triple data */ while (redptr + s->width + s->width <= ptr) { if (*redptr == SCANLINE_END) DBG (13, "-%d- ", pixel); if (fp) { /* for PPM binary (P6), 3-byte RGB pixel */ fwrite (redptr, 1, 1, fp); /* Red */ fwrite (redptr + s->width, 1, 1, fp); /* Green */ fwrite (redptr + s->width + s->width, 1, 1, fp); /* Blue */ /* for PPM ascii (P3) fprintf(fp, "%3d %3d %3d\n", *redptr, *(redptr + s->width), *(redptr + s->width + s->width)); */ } else { /* R */ *s->ptr = *redptr; s->ptr++; /* G */ *s->ptr = *(redptr + s->width); s->ptr++; /* B */ *s->ptr = *(redptr + s->width + s->width); s->ptr++; } redptr++; pixel++; if (pixel && !(pixel % s->width)) { /* end of a line, move redptr to the next Red section */ line++; redptr += s->width + s->width; #if 0 /* progress */ printf ("%2d%%\r", line * 100 / s->height); fflush (stdout); #endif /* don't record any extra */ if (line >= s->height) break; } } /* keep the extra around for next time */ level = ptr - redptr; if (level < 0) level = 0; memmove (buf, redptr, level); ptr = buf + level; redptr = buf; } } if (fp) { fclose (fp); DBG (6, "created scan file %s\n", s->fname); } free (buf); DBG (6, "%d lines, %d pixels, %d extra bytes\n", line, pixel, level); /* motor off */ write_byte (s->fd, COMMAND, 0x00); return status; } static int wait_for_return (int fd) { return read_poll_flag (fd, STATUS, STATUS_HOME, STATUS_HOME); } static SANE_Status compute_ogn (char *calfilename); /* This is the calibration routine Win2k goes through when the scanner is first plugged in. Original usb trace from Win2k with USBSnoopy ("usb sniffer for w2k" http://benoit.papillault.free.fr/speedtouch/sniff-2000.en.php3) */ static int plugin_cal (CANON_Handle * s) { SANE_Status status; unsigned int temp; byte result; byte *buf; int fd = s->fd; DBG (6, "Calibrating\n"); /* reserved? */ read_byte (fd, 0x69, &result); /* wants 02 */ /* parallel port setting */ write_byte (fd, PARALLEL_PORT, 0x06); write_many (fd, 0x08, seq002, sizeof (seq002)); /* addr 0x28 isn't written */ write_many (fd, 0x29, seq003, sizeof (seq003)); /* Verification */ buf = malloc (0x400); read_many (fd, 0x08, buf, sizeof (seq002)); if (memcmp (seq002, buf, sizeof (seq002))) DBG (1, "seq002 verification error\n"); /* addr 0x28 isn't read */ read_many (fd, 0x29, buf, sizeof (seq003)); if (memcmp (seq003, buf, sizeof (seq003))) DBG (1, "seq003 verification error\n"); /* parallel port noise filter */ write_byte (fd, 0x70, 0x73); lights_out (fd); /* Home motor */ read_byte (fd, STATUS, &result); /* wants 2f or 2d */ if (!(result & STATUS_HOME) /*0x2d */ ) write_byte (fd, COMMAND, 0x02); wait_for_return (fd); /* Motor forward */ write_byte (fd, COMMAND, 0x01); usleep (600000); read_byte (fd, STATUS, &result); /* wants 0c or 2c */ read_byte (fd, STATUS, &result); /* wants 0c */ /* Return home */ write_byte (fd, COMMAND, 0x02); /* Gamma tables */ /* Linear gamma */ for (temp = 0; temp < 0x0400; temp++) buf[temp] = temp / 4; /* Gamma Red */ write_byte (fd, DATAPORT_TARGET, DP_R | DP_GAMMA); write_word (fd, DATAPORT_ADDR, DP_WRITE); write_bulk (fd, DATAPORT, buf, 0x0400); /* Gamma Green */ write_byte (fd, DATAPORT_TARGET, DP_G | DP_GAMMA); write_word (fd, DATAPORT_ADDR, DP_WRITE); write_bulk (fd, DATAPORT, buf, 0x0400); /* Gamma Blue */ write_byte (fd, DATAPORT_TARGET, DP_B | DP_GAMMA); write_word (fd, DATAPORT_ADDR, DP_WRITE); write_bulk (fd, DATAPORT, buf, 0x0400); /* Read back gamma tables. I suppose I should check results... */ /* Gamma Red */ write_byte (fd, DATAPORT_TARGET, DP_R | DP_GAMMA); write_word (fd, DATAPORT_ADDR, DP_READ); read_bulk (fd, DATAPORT, buf, 0x0400); /* Gamma Green */ write_byte (fd, DATAPORT_TARGET, DP_G | DP_GAMMA); write_word (fd, DATAPORT_ADDR, DP_READ); read_bulk (fd, DATAPORT, buf, 0x0400); /* Gamma Blue */ write_byte (fd, DATAPORT_TARGET, DP_B | DP_GAMMA); write_word (fd, DATAPORT_ADDR, DP_READ); read_bulk (fd, DATAPORT, buf, 0x0400); free (buf); /* Make sure STATUS_HOME */ read_byte (fd, STATUS, &result); /* wants 0e */ /* stepper forward */ write_byte (fd, COMMAND, 0x01); read_byte (fd, STATUS, &result); /* wants 0c */ /* not sure if this rigid read/write pattern is required */ read_byte (fd, CLOCK_DIV, &result); /* wants 04 */ write_byte (fd, CLOCK_DIV, 0x04); read_byte (fd, STEP_SIZE, &result); /* wants 04 */ write_byte (fd, STEP_SIZE, 0x3f); read_byte (fd, 0x47, &result); /* wants 1a */ write_byte (fd, 0x47, 0xff); read_byte (fd, FAST_STEP, &result); /* wants 01 */ write_byte (fd, FAST_STEP, 0x01); read_byte (fd, 0x49, &result); /* wants 04 */ write_byte (fd, 0x49, 0x04); read_byte (fd, SKIP_STEPS, &result); /* wants 00 */ write_byte (fd, SKIP_STEPS, 0x00); read_byte (fd, 0x4b, &result); /* wants 00 */ write_byte (fd, 0x4b, 0xc8); read_byte (fd, BUFFER_LIMIT, &result); /* wants 57 */ write_byte (fd, BUFFER_LIMIT, 0x04); read_byte (fd, BUFFER_RESUME, &result); /* wants 02 */ write_byte (fd, BUFFER_RESUME, 0x02); read_byte (fd, REVERSE_STEPS, &result); /* wants 00 */ write_byte (fd, REVERSE_STEPS, 0x00); write_byte (fd, STEP_PWM, 0x1f); /* Reset motor */ write_byte (fd, COMMAND, 0x08); write_byte (fd, COMMAND, 0x00); /* Scan */ write_byte (fd, COMMAND, 0x03); /* Wants 02 or 03, gets a bunch of 0's first */ read_poll_min (fd, IMAGE_DATA_AVAIL, 2); write_byte (fd, COMMAND, 0x00); write_byte (fd, STEP_PWM, 0x3f); write_byte (fd, CLOCK_DIV, 0x04); /* 300 dpi */ write_word (fd, STEP_SIZE, 0x041a); write_word (fd, FAST_STEP, 0x0104); /* Don't skip the black/white calibration area at the bottom of the scanner. */ write_word (fd, SKIP_STEPS, 0x0000); write_byte (fd, BUFFER_LIMIT, 0x57); write_byte (fd, BUFFER_RESUME, 0x02); write_byte (fd, REVERSE_STEPS, 0x00); write_byte (fd, BUFFER_LIMIT, 0x09); write_byte (fd, STEP_PWM, 0x1f); read_byte (fd, MICROSTEP, &result); /* wants 13, active */ write_byte (fd, MICROSTEP, 0x03 /* tristate */ ); /* Calibration data taken under 3 different lighting conditions */ /* dark */ write_word (fd, LAMP_R_ON, 0x0017); write_word (fd, LAMP_R_OFF, 0x0100); write_word (fd, LAMP_G_ON, 0x0017); write_word (fd, LAMP_G_OFF, 0x0100); write_word (fd, LAMP_B_ON, 0x0017); write_word (fd, LAMP_B_OFF, 0x0100); /* coming in, we've got 300dpi, data px start : 0x004b data px end : 0x1437 for a total of 5100(13ec) 600-dpi pixels, (8.5 inches) or 2550 300-dpi pixels (7653 bytes). Interestingly, the scan head never moves, no matter how many rows are read. */ s->width = 2551; s->height = 1; s->flags = FLG_BUF | FLG_NO_INTERLEAVE; s->buf = malloc (s->width * s->height * 3); /* FIXME do something with this data */ CHK (do_scan (s)); /* Lighting */ /* medium */ write_word (fd, LAMP_R_ON, 0x0017); write_word (fd, LAMP_R_OFF, 0x0200); write_word (fd, LAMP_G_ON, 0x0017); write_word (fd, LAMP_G_OFF, 0x01d7 /* also 01db */ ); write_word (fd, LAMP_B_ON, 0x0017); write_word (fd, LAMP_B_OFF, 0x01af /* also 01b2 */ ); /* FIXME do something with this data */ CHK (do_scan (s)); /* Lighting */ /* bright */ write_word (fd, LAMP_R_ON, 0x0017); write_word (fd, LAMP_R_OFF, 0x0e8e /* also 1040 */ ); write_word (fd, LAMP_G_ON, 0x0017); write_word (fd, LAMP_G_OFF, 0x0753 /* also 0718 */ ); write_word (fd, LAMP_B_ON, 0x0017); write_word (fd, LAMP_B_OFF, 0x03f8 /* also 040d */ ); /* FIXME do something with this data */ CHK (do_scan (s)); free (s->buf); s->buf = NULL; /* The trace gets a little iffy from here on out since the log files are missing different urb's. This is kind of a puzzled-out compilation. */ write_byte (fd, MICROSTEP, 0x13 /* pins active */ ); write_byte (fd, STEP_PWM, 0x3f); read_byte (fd, STATUS, &result); /* wants 0c */ /* Stepper home */ write_byte (fd, COMMAND, 0x02); /* Step size */ write_word (fd, STEP_SIZE, 0x041a /* 300 dpi */ ); /* Skip steps */ write_word (fd, SKIP_STEPS, 0x0000); /* Pause buffer levels */ write_byte (fd, BUFFER_LIMIT, 0x57); /* Resume buffer levels */ write_byte (fd, BUFFER_RESUME, 0x02); wait_for_return (fd); /* stepper forward small */ write_byte (fd, COMMAND, 0x01); read_byte (fd, STATUS, &result); /* wants 0c */ usleep (200000); write_byte (fd, STEP_PWM, 0x1f); /* Read in cal strip at bottom of scanner (to adjust gain/offset tables. Note that this isn't the brightest lighting condition.) At 300 dpi: black rows 0-25; white rows 30-75; beginning of glass 90. This produces 574k of data, so save it to a temp file. */ if (!s->fname) { DBG (1, "No temp filename!\n"); s->fname = strdup ("/tmp/cal.XXXXXX"); /* FIXME: we should be using fd, not discarding it, and also checking for error! */ int fd = mkstemp (s->fname); close(fd); } s->width = 2551; s->height = 75; s->flags = FLG_PPM_HEADER | FLG_NO_INTERLEAVE; CHK (do_scan (s)); compute_ogn (s->fname); unlink (s->fname); write_byte (fd, STEP_PWM, 0x3f); /* stepper home */ write_byte (fd, COMMAND, 0x02); /* discard the remaining data */ read_byte (fd, IMAGE_DATA_AVAIL, &result); /* wants 42,4c */ if (result > 1) { read_bulk_size (fd, result, 0, 0, 0); DBG (11, "read %dk extra\n", result); } read_byte (fd, 0x69, &result); /* wants 02 */ write_byte (fd, 0x69, 0x0a); lights_out (fd); init (fd); #if 0 /* Repeatedly send this every 1 second. Button scan? FIXME */ gl640ReadReq (fd, GL640_GPIO_READ, &result); /* wants 00 */ #endif return 0; } /* The number of regions in the calibration strip (black & white). */ #define NREGIONS 2 /* Compute the offset/gain table from the calibration strip. This is somewhat more complicated than necessary because I don't hard-code the strip widths; I try to figure out the regions based on the scan data. Theoretically, the region-finder should work for any number of distinct regions (but there are only 2 on this scanner.) This produces the CAL_FILE_OGN file, the final offset/gain table. */ static SANE_Status compute_ogn (char *calfilename) { byte *linebuf, *oldline, *newline; mode_t oldmask; FILE *fp; int width, height, nlines = 0, region = -1, i, transition = 1, badcnt; int pct; int reglines[NREGIONS]; float *avg; float max_range[3], tmp1, tmp2; fp = fopen (calfilename, "r"); if (!fp) { DBG (1, "open %s\n", calfilename); return SANE_STATUS_EOF; } fscanf (fp, "P6 %d %d %*d ", &width, &height); DBG (12, "cal file %s %dx%d\n", calfilename, width, height); width = width * 3; /* 1 byte each of r, g, b */ /* make a buffer holding 2 lines of data */ linebuf = calloc (width * 2, sizeof (linebuf[0])); /* first line is data read buffer */ newline = linebuf; /* second line is a temporary holding spot in case the next line read is the black/white transition, in which case we'll disregard this one. */ oldline = linebuf + width; /* column averages per region */ avg = calloc (width * NREGIONS, sizeof (avg[0])); while (nlines < height) { if (fread (newline, 1, width, fp) != (size_t) width) break; nlines++; /* Check if new line is majorly different than old. Criteria is 10 pixels differing by more than 10%. */ badcnt = 0; for (i = 0; i < width; i++) { pct = newline[i] - oldline[i]; /* Fix by M.Reinelt * do NOT use 10% (think of a dark area with * oldline=4 and newline=5, which is a change of 20% !! * Use an absolute difference of 10 as criteria */ if (pct < -10 || pct > 10) { badcnt++; DBG (16, "pix%d[%d/%d] ", i, newline[i], oldline[i]); } } DBG (13, "line %d changed %d\n", nlines, badcnt); if ((badcnt > 10) || (nlines == height)) { /* End of region. Lines are different or end of data. */ transition++; if (transition == 1) DBG (12, "Region %d lines %d-%d\n", region, nlines - reglines[region], nlines - 1); } else { /* Lines are similar, so still in region. */ if (transition) { /* There was just a transition, so this is the start of a new region. */ region++; if (region >= NREGIONS) /* Too many regions detected. Err below. */ break; transition = 0; reglines[region] = 0; } /* Add oldline to the current region's average */ for (i = 0; i < width; i++) avg[i + region * width] += oldline[i]; reglines[region]++; } /* And newline becomes old */ memcpy (oldline, newline, width); } fclose (fp); free (linebuf); region++; /* now call it number of regions instead of index */ DBG (11, "read %d lines as %d regions\n", nlines, region); /* Check to see if we screwed up */ if (region != NREGIONS) { DBG (1, "Warning: gain/offset compute failed.\n" "Found %d regions instead of %d.\n", region, NREGIONS); for (i = 0; i < region; i++) DBG (1, " Region %d: %d lines\n", i, (i >= NREGIONS) ? -1 : reglines[i]); free (avg); return SANE_STATUS_UNSUPPORTED; } /* Now we've got regions and sums. Find averages and range. */ max_range[0] = max_range[1] = max_range[2] = 0.0; for (i = 0; i < width; i++) { /* Convert sums to averages */ /* black region */ tmp1 = avg[i] /= reglines[0]; /* white region */ tmp2 = avg[i + width] /= reglines[1]; /* Track largest range for each color. If image is interleaved, use 'i%3', if not, 'i/(width/3)' */ if ((tmp2 - tmp1) > max_range[i / (width / 3)]) { max_range[i / (width / 3)] = tmp2 - tmp1; DBG (14, "max %d@%d %f-%f=%f\n", i / (width / 3), i, tmp2, tmp1, tmp2 - tmp1); } } DBG (13, "max range r %f\n", max_range[0]); DBG (13, "max range g %f\n", max_range[1]); DBG (13, "max range b %f\n", max_range[2]); /* Set umask to world r/w so other users can overwrite common cal... */ oldmask = umask (0); fp = fopen (CAL_FILE_OGN, "w"); /* ... and set it back. */ umask (oldmask); if (!fp) { DBG (1, "open " CAL_FILE_OGN); free (avg); return SANE_STATUS_IO_ERROR; } /* Finally, compute offset and gain */ for (i = 0; i < width; i++) { int gain, offset; byte ogn[2]; /* skip line termination flags */ if (!((i + 1) % (width / 3))) { DBG (13, "skip scanline EOL %d/%d\n", i, width); continue; } /* Gain multiplier: 255 : 1.5 times brighter 511 : 2 times brighter 1023: 3 times brighter */ #if 1 /* Original gain/offset */ gain = 512 * ((max_range[i / (width / 3)] / (avg[i + width] - avg[i])) - 1); offset = avg[i]; #else /* This doesn't work for some people. For instance, a negative offset would be bad. */ /* Enhanced offset and gain calculation by M.Reinelt * These expressions were found by an iterative calibration process, * by changing gain and offset values for every pixel until the desired * values for black and white were reached, and finding an approximation * formula. * Note that offset is linear, but gain isn't! */ offset = (double)3.53 * avg[i] - 125; gain = (double)3861.0 * exp(-0.0168 * (avg[i + width] - avg[i])); #endif DBG (14, "%d wht=%f blk=%f diff=%f gain=%d offset=%d\n", i, avg[i + width], avg[i], avg[i + width] - avg[i], gain, offset); /* 10-bit gain, 6-bit offset (subtractor) in two bytes */ ogn[0] = (byte) (((offset << 2) + (gain >> 8)) & 0xFF); ogn[1] = (byte) (gain & 0xFF); fwrite (ogn, sizeof (byte), 2, fp); /* Annoyingly, we seem to use ogn data at 600dpi, while we scanned at 300, so double our file. Much easier than doubling at the read. */ fwrite (ogn, sizeof (byte), 2, fp); } fclose (fp); free (avg); return SANE_STATUS_GOOD; } static int check_ogn_file (void) { FILE *fp; fp = fopen (CAL_FILE_OGN, "r"); if (fp) { fclose (fp); return 1; } return 0; } /* Load or fake the offset/gain table */ static void install_ogn (int fd) { int temp; byte *buf; FILE *fp; /* 8.5in at 600dpi = 5104 pixels in scan head 10-bit gain + 6-bit offset = 2 bytes per pixel, so 10208 bytes */ buf = malloc (10208); fp = fopen (CAL_FILE_OGN, "r"); if (fp) { fread (buf, 2, 5100, fp); /* screw the last 4 pixels */ } else { /* Make up the gain/offset data. */ #define GAIN 256 /* 1.5x */ #define OFFSET 0 for (temp = 0; temp < 10208; temp += 2) { buf[temp] = (byte) ((OFFSET << 2) + (GAIN >> 8)); buf[temp + 1] = (byte) (GAIN & 0xFF); } } /* Gain/offset table (r,g,b) */ write_byte (fd, DATAPORT_TARGET, DP_R | DP_OFFSET); write_word (fd, DATAPORT_ADDR, DP_WRITE); write_bulk (fd, DATAPORT, buf, 10208); if (fp) fread (buf, 2, 5100, fp); write_byte (fd, DATAPORT_TARGET, DP_G | DP_OFFSET); write_word (fd, DATAPORT_ADDR, DP_WRITE); write_bulk (fd, DATAPORT, buf, 10208); if (fp) { fread (buf, 2, 5100, fp); fclose (fp); } write_byte (fd, DATAPORT_TARGET, DP_B | DP_OFFSET); write_word (fd, DATAPORT_ADDR, DP_WRITE); write_bulk (fd, DATAPORT, buf, 10208); free (buf); return; } /* Scan sequence */ /* resolution is 75,150,300,600,1200 scan coordinates in 600-dpi pixels */ static SANE_Status scan (CANON_Handle * opt) { SANE_Status status; const int left_edge = 0x004b; /* Just for my scanner, or is this universal? Calibrate? */ int temp; int fd = opt->fd; byte result; byte *buf; /* Check status. (not in w2k driver) */ read_byte (fd, STATUS, &result); /* wants 2f or 2d */ if (!(result & STATUS_HOME) /*0x2d */ ) return SANE_STATUS_DEVICE_BUSY; /* or force it to return? write_byte(fd, COMMAND, 0x02); wait_for_return(fd); */ /* reserved? */ read_byte (fd, 0x69, &result); /* wants 0a */ read_byte (fd, STATUS, &result); /* wants 0e */ read_byte (fd, PAPER_SENSOR, &result); /* wants 2b */ write_byte (fd, PAPER_SENSOR, 0x2b); /* Color mode: 1-Channel Line Rate Color 0x15. 1-Channel Grayscale 0x14 (and we skip some of these tables) */ write_byte (fd, COLOR_MODE, 0x15); /* install the offset/gain table */ install_ogn (fd); read_byte (fd, STATUS, &result); /* wants 0e */ /* move forward to "glass 0" */ write_byte (fd, COMMAND, 0x01); read_byte (fd, STATUS, &result); /* wants 0c */ /* create gamma table */ buf = malloc (0x400); for (temp = 0; temp < 0x0400; temp++) /* gamma calculation by M.Reinelt */ buf[temp] = (double) 255.0 * exp(log((temp + 0.5) / 1023.0) / opt->gamma) + 0.5; /* Gamma R, write and verify */ write_byte (fd, DATAPORT_TARGET, DP_R | DP_GAMMA); write_word (fd, DATAPORT_ADDR, DP_WRITE); write_bulk (fd, DATAPORT, buf, 0x0400); write_byte (fd, DATAPORT_TARGET, DP_R | DP_GAMMA); write_word (fd, DATAPORT_ADDR, DP_READ); read_bulk (fd, DATAPORT, buf, 0x0400); /* Gamma G */ write_byte (fd, DATAPORT_TARGET, DP_G | DP_GAMMA); write_word (fd, DATAPORT_ADDR, DP_WRITE); write_bulk (fd, DATAPORT, buf, 0x0400); write_byte (fd, DATAPORT_TARGET, DP_G | DP_GAMMA); write_word (fd, DATAPORT_ADDR, DP_READ); read_bulk (fd, DATAPORT, buf, 0x0400); /* Gamma B */ write_byte (fd, DATAPORT_TARGET, DP_B | DP_GAMMA); write_word (fd, DATAPORT_ADDR, DP_WRITE); write_bulk (fd, DATAPORT, buf, 0x0400); write_byte (fd, DATAPORT_TARGET, DP_B | DP_GAMMA); write_word (fd, DATAPORT_ADDR, DP_READ); read_bulk (fd, DATAPORT, buf, 0x0400); free (buf); write_byte (fd, CLOCK_DIV, 0x04); /* Resolution: dpi 75(ie) 100,150(1c) 200,300(1a) 600,1200(18) */ switch (opt->resolution) { case 150: write_byte (fd, 0x09, 0x1c); break; case 300: write_byte (fd, 0x09, 0x1a); break; case 600: case 1200: /* actually 600 dpi horiz max */ write_byte (fd, 0x09, 0x18); break; default: /* 75 */ write_byte (fd, 0x09, 0x1e); opt->resolution = 75; } write_word (fd, ACTIVE_PX_START, left_edge); /* Data pixel start. Measured at 600dpi regardless of scan resolution. 0-position is 0x004b */ write_word (fd, DATA_PX_START, left_edge + opt->x1); /* Data pixel end. Measured at 600dpi regardless of scan resolution. */ write_word (fd, DATA_PX_END, left_edge + opt->x2); /* greyscale has 14,03, different lights */ write_byte (fd, COLOR_MODE, 0x15); write_byte (fd, 0x29, 0x02); /* Lights */ write_word (fd, LAMP_R_ON, 0x0017); /* "Hi-low color" selection from windows driver. low(1437) hi(1481) */ write_word (fd, LAMP_R_OFF, 0x1437); write_word (fd, LAMP_G_ON, 0x0017); write_word (fd, LAMP_G_OFF, 0x094e); write_word (fd, LAMP_B_ON, 0x0017); write_word (fd, LAMP_B_OFF, 0x0543); /* Analog static offset R,G,B. Greyscale has 0,0,0 */ write_byte (fd, 0x38, 0x3f); write_byte (fd, 0x39, 0x3f); write_byte (fd, 0x3a, 0x3f); /* Analog static gain R,G,B (normally 0x01) */ write_byte (fd, 0x3b, opt->gain); write_byte (fd, 0x3c, opt->gain); write_byte (fd, 0x3d, opt->gain); /* Digital gain/offset settings. Greyscale has 0 */ write_byte (fd, 0x3e, 0x1a); { /* Stepper motion setup. */ int stepsize, faststep = 0x0104, reverse = 0x28, phase, pwm = 0x1f; switch (opt->resolution) { case 75: stepsize = 0x0106; faststep = 0x0106; reverse = 0; phase = 0x39a8; pwm = 0x3f; break; case 150: stepsize = 0x020d; phase = 0x3198; break; case 300: stepsize = 0x041a; phase = 0x2184; break; case 600: stepsize = 0x0835; phase = 0x0074; break; case 1200: /* 1200 dpi y only, x is 600 dpi */ stepsize = 0x106b; phase = 0x41ac; break; default: DBG (1, "BAD RESOLUTION"); return SANE_STATUS_UNSUPPORTED; } write_word (fd, STEP_SIZE, stepsize); write_word (fd, FAST_STEP, faststep); /* There sounds like a weird step disjoint at the end of skipsteps at 75dpi, so I think that's why skipsteps=0 at 75dpi in the Windows driver. It still works at the normal 0x017a though. */ /* cal strip is 0x17a steps, plus 2 300dpi microsteps per pixel */ write_word (fd, SKIP_STEPS, 0x017a /* cal strip */ + opt->y1 * 2); /* FIXME could be 0x57, why not? */ write_byte (fd, BUFFER_LIMIT, 0x20); write_byte (fd, BUFFER_RESUME, 0x02); write_byte (fd, REVERSE_STEPS, reverse); /* motor resume phasing */ write_word (fd, 0x52, phase); write_byte (fd, STEP_PWM, pwm); } read_byte (fd, PAPER_SENSOR, &result); /* wants 2b */ write_byte (fd, PAPER_SENSOR, 0x0b); opt->width = (opt->x2 - opt->x1) * opt->resolution / 600 + 1; opt->height = (opt->y2 - opt->y1) * opt->resolution / 600; opt->flags = 0; DBG (1, "width=%d height=%d dpi=%d\n", opt->width, opt->height, opt->resolution); CHK (do_scan (opt)); read_byte (fd, PAPER_SENSOR, &result); /* wants 0b */ write_byte (fd, PAPER_SENSOR, 0x2b); write_byte (fd, STEP_PWM, 0x3f); lights_out (fd); /* home */ write_byte (fd, COMMAND, 0x02); return status; } static SANE_Status CANON_set_scan_parameters (CANON_Handle * scan, const int forceCal, const int gray, const int left, const int top, const int right, const int bottom, const int res, const int gain, const double gamma) { DBG (2, "CANON_set_scan_parameters:\n"); DBG (2, "cal = %d\n", forceCal); DBG (2, "gray = %d (ignored)\n", gray); DBG (2, "res = %d\n", res); DBG (2, "gain = %d\n", gain); DBG (2, "gamma = %f\n", gamma); DBG (2, "in 600dpi pixels:\n"); DBG (2, "left = %d, top = %d\n", left, top); DBG (2, "right = %d, bottom = %d\n", right, bottom); /* Validate the input parameters */ if ((left < 0) || (right > CANON_MAX_WIDTH)) return SANE_STATUS_INVAL; if ((top < 0) || (bottom > CANON_MAX_HEIGHT)) return SANE_STATUS_INVAL; if (((right - left) < 10) || ((bottom - top) < 10)) return SANE_STATUS_INVAL; if ((res != 75) && (res != 150) && (res != 300) && (res != 600) && (res != 1200)) return SANE_STATUS_INVAL; if ((gain < 0) || (gain > 64)) return SANE_STATUS_INVAL; if (gamma <= 0.0) return SANE_STATUS_INVAL; /* Store params */ scan->resolution = res; scan->x1 = left; scan->x2 = right - /* subtract 1 pixel */ 600 / scan->resolution; scan->y1 = top; scan->y2 = bottom; scan->gain = gain; scan->gamma = gamma; scan->flags = forceCal ? FLG_FORCE_CAL : 0; return SANE_STATUS_GOOD; } static SANE_Status CANON_close_device (CANON_Handle * scan) { DBG (3, "CANON_close_device:\n"); sanei_usb_close (scan->fd); return SANE_STATUS_GOOD; } static SANE_Status CANON_open_device (CANON_Handle * scan, const char *dev) { SANE_Word vendor; SANE_Word product; SANE_Status res; DBG (3, "CANON_open_device: `%s'\n", dev); scan->fname = NULL; scan->fp = NULL; scan->flags = 0; res = sanei_usb_open (dev, &scan->fd); if (res != SANE_STATUS_GOOD) { DBG (1, "CANON_open_device: couldn't open device `%s': %s\n", dev, sane_strstatus (res)); return res; } #ifndef NO_AUTODETECT /* We have opened the device. Check that it is a USB scanner. */ if (sanei_usb_get_vendor_product (scan->fd, &vendor, &product) != SANE_STATUS_GOOD) { DBG (1, "CANON_open_device: sanei_usb_get_vendor_product failed\n"); /* This is not a USB scanner, or SANE or the OS doesn't support it. */ sanei_usb_close (scan->fd); scan->fd = -1; return SANE_STATUS_UNSUPPORTED; } /* Make sure we have a CANON scanner */ if ((vendor != 0x04a9) || (product != 0x2204)) { DBG (1, "CANON_open_device: incorrect vendor/product (0x%x/0x%x)\n", vendor, product); sanei_usb_close (scan->fd); scan->fd = -1; return SANE_STATUS_UNSUPPORTED; } #endif return SANE_STATUS_GOOD; } static const char * CANON_get_device_name (CANON_Handle * scanner) { (void) scanner; /* Eliminate warning about unused parameters */ return "Canoscan FB630U"; } static SANE_Status CANON_finish_scan (CANON_Handle * scanner) { DBG (3, "CANON_finish_scan:\n"); if (scanner->fp) fclose (scanner->fp); scanner->fp = NULL; /* remove temp file */ if (scanner->fname) { DBG (4, "removing temp file %s\n", scanner->fname); unlink (scanner->fname); free (scanner->fname); } scanner->fname = NULL; return SANE_STATUS_GOOD; } static SANE_Status CANON_start_scan (CANON_Handle * scanner) { int rv; SANE_Status status; DBG (3, "CANON_start_scan called\n"); /* choose a temp file name for scan data */ scanner->fname = strdup ("/tmp/scan.XXXXXX"); /* FIXME: we should be using fd, not discarding it! */ int fd = mkstemp (scanner->fname); if (fd == -1) return SANE_STATUS_IO_ERROR; close(fd); /* calibrate if needed */ rv = init (scanner->fd); if (rv < 0) { DBG(1, "Can't talk on USB.\n"); return SANE_STATUS_IO_ERROR; } if ((rv == 1) || !check_ogn_file () || (scanner->flags & FLG_FORCE_CAL)) { plugin_cal (scanner); wait_for_return (scanner->fd); } /* scan */ if ((status = scan (scanner)) != SANE_STATUS_GOOD) { CANON_finish_scan (scanner); return status; } /* read the temp file back out */ scanner->fp = fopen (scanner->fname, "r"); DBG (4, "reading %s\n", scanner->fname); if (!scanner->fp) { DBG (1, "open %s", scanner->fname); return SANE_STATUS_IO_ERROR; } return SANE_STATUS_GOOD; } static SANE_Status CANON_read (CANON_Handle * scanner, SANE_Byte * data, SANE_Int max_length, SANE_Int * length) { SANE_Status status; int red_len; DBG (5, "CANON_read called\n"); if (!scanner->fp) return SANE_STATUS_INVAL; red_len = fread (data, 1, max_length, scanner->fp); /* return some data */ if (red_len > 0) { *length = red_len; DBG (5, "CANON_read returned (%d/%d)\n", *length, max_length); return SANE_STATUS_GOOD; } /* EOF or file err */ *length = 0; if (feof (scanner->fp)) { DBG (4, "EOF\n"); status = SANE_STATUS_EOF; } else { DBG (4, "IO ERR\n"); status = SANE_STATUS_IO_ERROR; } CANON_finish_scan (scanner); DBG (5, "CANON_read returned (%d/%d)\n", *length, max_length); return status; } backends-1.3.0/backend/canon630u.c000066400000000000000000000623601456256263500165610ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2002, Nathan Rutman Copyright (C) 2001, Marcio Luis Teixeira Parts copyright (C) 1996, 1997 Andreas Beck Parts copyright (C) 2000, 2001 Michael Herder Parts copyright (C) 2001 Henning Meier-Geinitz 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, see . 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. */ #define BUILD 1 #define MM_IN_INCH 25.4 #include "../include/sane/config.h" #include #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_usb.h" #define BACKEND_NAME canon630u #define CANONUSB_CONFIG_FILE "canon630u.conf" #include "../include/sane/sanei_backend.h" #include "canon630u-common.c" typedef struct Canon_Device { struct Canon_Device *next; SANE_String name; SANE_Device sane; } Canon_Device; typedef struct Canon_Scanner { struct Canon_Scanner *next; Canon_Device *device; CANON_Handle scan; } Canon_Scanner; static int num_devices = 0; static const SANE_Device **devlist = NULL; static Canon_Device *first_dev = NULL; static Canon_Scanner *first_handle = NULL; static SANE_Parameters parms = { SANE_FRAME_RGB, 0, 0, /* Number of bytes returned per scan line: */ 0, /* Number of pixels per scan line. */ 0, /* Number of lines for the current scan. */ 8 /* Number of bits per sample. */ }; struct _SANE_Option { SANE_Option_Descriptor *descriptor; SANE_Status (*callback) (struct _SANE_Option * option, SANE_Handle handle, SANE_Action action, void *value, SANE_Int * info); }; typedef struct _SANE_Option SANE_Option; /*-----------------------------------------------------------------*/ static SANE_Word getNumberOfOptions (void); /* Forward declaration */ /* This read-only option returns the number of options available for the device. It should be the first option in the options array declared below. */ static SANE_Option_Descriptor optionNumOptionsDescriptor = { SANE_NAME_NUM_OPTIONS, SANE_TITLE_NUM_OPTIONS, SANE_DESC_NUM_OPTIONS, SANE_TYPE_INT, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} }; static SANE_Status optionNumOptionsCallback (SANE_Option * option, SANE_Handle handle, SANE_Action action, void *value, SANE_Int * info) { (void) option; (void) handle; (void) info; /* Eliminate warning about unused parameters */ if (action != SANE_ACTION_GET_VALUE) return SANE_STATUS_INVAL; *(SANE_Word *) value = getNumberOfOptions (); return SANE_STATUS_GOOD; } /*-----------------------------------------------------------------*/ /* This option lets the user force scanner calibration. Normally, this is done only once, at first scan after powerup. */ static SANE_Word optionCalibrateValue = SANE_FALSE; static SANE_Option_Descriptor optionCalibrateDescriptor = { "cal", SANE_I18N ("Calibrate Scanner"), SANE_I18N ("Force scanner calibration before scan"), SANE_TYPE_BOOL, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} }; static SANE_Status optionCalibrateCallback (SANE_Option * option, SANE_Handle handle, SANE_Action action, void *value, SANE_Int * info) { (void) handle; (void) option; /* Eliminate warning about unused parameters */ switch (action) { case SANE_ACTION_SET_AUTO: return SANE_STATUS_INVAL; break; case SANE_ACTION_SET_VALUE: *info |= SANE_INFO_RELOAD_PARAMS; optionCalibrateValue = *(SANE_Bool *) value; break; case SANE_ACTION_GET_VALUE: *(SANE_Word *) value = optionCalibrateValue; break; } return SANE_STATUS_GOOD; } /*-----------------------------------------------------------------*/ /* This option lets the user select the scan resolution. The Canon fb630u scanner supports the following resolutions: 75, 150, 300, 600, 1200 */ static const SANE_Word optionResolutionList[] = { 4, /* Number of elements */ 75, 150, 300, 600 /* Resolution list */ /* also 600x1200, but ignore that for now. */ }; static SANE_Option_Descriptor optionResolutionDescriptor = { SANE_NAME_SCAN_RESOLUTION, SANE_TITLE_SCAN_RESOLUTION, SANE_DESC_SCAN_RESOLUTION, SANE_TYPE_INT, SANE_UNIT_DPI, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC, SANE_CONSTRAINT_WORD_LIST, {(const SANE_String_Const *) optionResolutionList} }; static SANE_Word optionResolutionValue = 75; static SANE_Status optionResolutionCallback (SANE_Option * option, SANE_Handle handle, SANE_Action action, void *value, SANE_Int * info) { SANE_Status status; SANE_Word autoValue = 75; (void) handle; /* Eliminate warning about unused parameters */ switch (action) { case SANE_ACTION_SET_AUTO: status = sanei_constrain_value (option->descriptor, (void *) &autoValue, info); if (status != SANE_STATUS_GOOD) return status; optionResolutionValue = autoValue; *info |= SANE_INFO_RELOAD_PARAMS; break; case SANE_ACTION_SET_VALUE: *info |= SANE_INFO_RELOAD_PARAMS; optionResolutionValue = *(SANE_Word *) value; break; case SANE_ACTION_GET_VALUE: *(SANE_Word *) value = optionResolutionValue; break; } return SANE_STATUS_GOOD; } /*-----------------------------------------------------------------*/ #ifdef GRAY /* This option lets the user select a gray scale scan */ static SANE_Word optionGrayscaleValue = SANE_FALSE; static SANE_Option_Descriptor optionGrayscaleDescriptor = { "gray", SANE_I18N ("Grayscale scan"), SANE_I18N ("Do a grayscale rather than color scan"), SANE_TYPE_BOOL, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} }; static SANE_Status optionGrayscaleCallback (SANE_Option * option, SANE_Handle handle, SANE_Action action, void *value, SANE_Int * info) { (void) handle; (void) option; /* Eliminate warning about unused parameters */ switch (action) { case SANE_ACTION_SET_AUTO: return SANE_STATUS_INVAL; break; case SANE_ACTION_SET_VALUE: *info |= SANE_INFO_RELOAD_PARAMS; optionGrayscaleValue = *(SANE_Bool *) value; break; case SANE_ACTION_GET_VALUE: *(SANE_Word *) value = optionGrayscaleValue; break; } return SANE_STATUS_GOOD; } #endif /* GRAY */ /*-----------------------------------------------------------------*/ /* Analog Gain setting */ static const SANE_Range aGainRange = { 0, /* minimum */ 64, /* maximum */ 1 /* quantization */ }; static SANE_Int optionAGainValue = 1; static SANE_Option_Descriptor optionAGainDescriptor = { "gain", SANE_I18N ("Analog Gain"), SANE_I18N ("Increase or decrease the analog gain of the CCD array"), SANE_TYPE_INT, SANE_UNIT_NONE, sizeof (SANE_Int), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED, SANE_CONSTRAINT_RANGE, {(const SANE_String_Const *) &aGainRange} }; static SANE_Status optionAGainCallback (SANE_Option * option, SANE_Handle handle, SANE_Action action, void *value, SANE_Int * info) { (void) option; (void) handle; (void) info; /* Eliminate warning about unused parameters */ switch (action) { case SANE_ACTION_SET_AUTO: return SANE_STATUS_INVAL; break; case SANE_ACTION_SET_VALUE: optionAGainValue = *(SANE_Int *) value; *info |= SANE_INFO_RELOAD_PARAMS; break; case SANE_ACTION_GET_VALUE: *(SANE_Int *) value = optionAGainValue; break; } return SANE_STATUS_GOOD; } /*-----------------------------------------------------------------*/ /* Scanner gamma setting */ static SANE_Fixed optionGammaValue = SANE_FIX (1.6); static SANE_Option_Descriptor optionGammaDescriptor = { "gamma", SANE_I18N ("Gamma Correction"), SANE_I18N ("Selects the gamma corrected transfer curve"), SANE_TYPE_FIXED, SANE_UNIT_NONE, sizeof (SANE_Int), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED, SANE_CONSTRAINT_NONE, {NULL} }; static SANE_Status optionGammaCallback (SANE_Option * option, SANE_Handle handle, SANE_Action action, void *value, SANE_Int * info) { (void) option; (void) handle; (void) info; /* Eliminate warning about unused parameters */ switch (action) { case SANE_ACTION_SET_AUTO: return SANE_STATUS_INVAL; break; case SANE_ACTION_SET_VALUE: optionGammaValue = *(SANE_Fixed *) value; *info |= SANE_INFO_RELOAD_PARAMS; break; case SANE_ACTION_GET_VALUE: *(SANE_Fixed *) value = optionGammaValue; break; } return SANE_STATUS_GOOD; } /*-----------------------------------------------------------------*/ /* Scan range */ static const SANE_Range widthRange = { 0, /* minimum */ SANE_FIX (CANON_MAX_WIDTH * MM_IN_INCH / 600), /* maximum */ 0 /* quantization */ }; static const SANE_Range heightRange = { 0, /* minimum */ SANE_FIX (CANON_MAX_HEIGHT * MM_IN_INCH / 600), /* maximum */ 0 /* quantization */ }; /*-----------------------------------------------------------------*/ /* This option controls the top-left-x corner of the scan */ static SANE_Fixed optionTopLeftXValue = 0; static SANE_Option_Descriptor optionTopLeftXDescriptor = { SANE_NAME_SCAN_TL_X, SANE_TITLE_SCAN_TL_X, SANE_DESC_SCAN_TL_X, SANE_TYPE_FIXED, SANE_UNIT_MM, sizeof (SANE_Fixed), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_RANGE, {(const SANE_String_Const *) &widthRange} }; static SANE_Status optionTopLeftXCallback (SANE_Option * option, SANE_Handle handle, SANE_Action action, void *value, SANE_Int * info) { (void) option; (void) handle; (void) value; /* Eliminate warning about unused parameters */ switch (action) { case SANE_ACTION_SET_AUTO: return SANE_STATUS_INVAL; break; case SANE_ACTION_SET_VALUE: optionTopLeftXValue = *(SANE_Fixed *) value; *info |= SANE_INFO_RELOAD_PARAMS; break; case SANE_ACTION_GET_VALUE: *(SANE_Fixed *) value = optionTopLeftXValue; break; } return SANE_STATUS_GOOD; } /*-----------------------------------------------------------------*/ /* This option controls the top-left-y corner of the scan */ static SANE_Fixed optionTopLeftYValue = 0; static SANE_Option_Descriptor optionTopLeftYDescriptor = { SANE_NAME_SCAN_TL_Y, SANE_TITLE_SCAN_TL_Y, SANE_DESC_SCAN_TL_Y, SANE_TYPE_FIXED, SANE_UNIT_MM, sizeof (SANE_Fixed), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_RANGE, {(const SANE_String_Const *) &heightRange} }; static SANE_Status optionTopLeftYCallback (SANE_Option * option, SANE_Handle handle, SANE_Action action, void *value, SANE_Int * info) { /* Eliminate warnings about unused parameters */ (void) option; (void) handle; switch (action) { case SANE_ACTION_SET_AUTO: return SANE_STATUS_INVAL; break; case SANE_ACTION_SET_VALUE: optionTopLeftYValue = *(SANE_Fixed *) value; *info |= SANE_INFO_RELOAD_PARAMS; break; case SANE_ACTION_GET_VALUE: *(SANE_Fixed *) value = optionTopLeftYValue; break; } return SANE_STATUS_GOOD; } /*-----------------------------------------------------------------*/ /* This option controls the bot-right-x corner of the scan Default to 215.9mm, max. */ static SANE_Fixed optionBotRightXValue = SANE_FIX (215.9); static SANE_Option_Descriptor optionBotRightXDescriptor = { SANE_NAME_SCAN_BR_X, SANE_TITLE_SCAN_BR_X, SANE_DESC_SCAN_BR_X, SANE_TYPE_FIXED, SANE_UNIT_MM, sizeof (SANE_Fixed), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_RANGE, {(const SANE_String_Const *) &widthRange} }; static SANE_Status optionBotRightXCallback (SANE_Option * option, SANE_Handle handle, SANE_Action action, void *value, SANE_Int * info) { /* Eliminate warnings about unused parameters */ (void) option; (void) handle; switch (action) { case SANE_ACTION_SET_AUTO: return SANE_STATUS_INVAL; break; case SANE_ACTION_SET_VALUE: optionBotRightXValue = *(SANE_Fixed *) value; *info |= SANE_INFO_RELOAD_PARAMS; break; case SANE_ACTION_GET_VALUE: *(SANE_Fixed *) value = optionBotRightXValue; break; } return SANE_STATUS_GOOD; } /*-----------------------------------------------------------------*/ /* This option controls the bot-right-y corner of the scan Default to 296.3mm, max */ static SANE_Fixed optionBotRightYValue = SANE_FIX (296.3); static SANE_Option_Descriptor optionBotRightYDescriptor = { SANE_NAME_SCAN_BR_Y, SANE_TITLE_SCAN_BR_Y, SANE_DESC_SCAN_BR_Y, SANE_TYPE_FIXED, SANE_UNIT_MM, sizeof (SANE_Fixed), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_RANGE, {(const SANE_String_Const *) &heightRange} }; static SANE_Status optionBotRightYCallback (SANE_Option * option, SANE_Handle handle, SANE_Action action, void *value, SANE_Int * info) { /* Eliminate warnings about unused parameters */ (void) option; (void) handle; switch (action) { case SANE_ACTION_SET_AUTO: return SANE_STATUS_INVAL; break; case SANE_ACTION_SET_VALUE: optionBotRightYValue = *(SANE_Fixed *) value; *info |= SANE_INFO_RELOAD_PARAMS; break; case SANE_ACTION_GET_VALUE: *(SANE_Fixed *) value = optionBotRightYValue; break; } return SANE_STATUS_GOOD; } /*-----------------------------------------------------------------*/ /* The following array binds the option descriptors to their respective callback routines */ static SANE_Option so[] = { {&optionNumOptionsDescriptor, optionNumOptionsCallback}, {&optionResolutionDescriptor, optionResolutionCallback}, {&optionCalibrateDescriptor, optionCalibrateCallback}, #ifdef GRAY {&optionGrayscaleDescriptor, optionGrayscaleCallback}, #endif {&optionAGainDescriptor, optionAGainCallback}, {&optionGammaDescriptor, optionGammaCallback}, {&optionTopLeftXDescriptor, optionTopLeftXCallback}, {&optionTopLeftYDescriptor, optionTopLeftYCallback}, {&optionBotRightXDescriptor, optionBotRightXCallback}, {&optionBotRightYDescriptor, optionBotRightYCallback} }; static SANE_Word getNumberOfOptions (void) { return NELEMS (so); } /* This routine dispatches the control message to the appropriate callback routine, it outght to be called by sane_control_option after any driver specific validation. */ static SANE_Status dispatch_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int * info) { SANE_Option *op = so + option; SANE_Int myinfo = 0; SANE_Status status = SANE_STATUS_GOOD; if (option < 0 || option >= NELEMS (so)) return SANE_STATUS_INVAL; /* Unknown option ... */ if ((action == SANE_ACTION_SET_VALUE) && ((op->descriptor->cap & SANE_CAP_SOFT_SELECT) == 0)) return SANE_STATUS_INVAL; if ((action == SANE_ACTION_GET_VALUE) && ((op->descriptor->cap & SANE_CAP_SOFT_DETECT) == 0)) return SANE_STATUS_INVAL; if ((action == SANE_ACTION_SET_AUTO) && ((op->descriptor->cap & SANE_CAP_AUTOMATIC) == 0)) return SANE_STATUS_INVAL; if (action == SANE_ACTION_SET_VALUE) { status = sanei_constrain_value (op->descriptor, value, &myinfo); if (status != SANE_STATUS_GOOD) return status; } status = (op->callback) (op, handle, action, value, &myinfo); if (info) *info = myinfo; return status; } static SANE_Status attach_scanner (const char *devicename, Canon_Device ** devp) { CANON_Handle scan; Canon_Device *dev; SANE_Status status; DBG (3, "attach_scanner: %s\n", devicename); for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->sane.name, devicename) == 0) { if (devp) *devp = dev; return SANE_STATUS_GOOD; } } dev = malloc (sizeof (*dev)); if (!dev) return SANE_STATUS_NO_MEM; memset (dev, '\0', sizeof (Canon_Device)); /* clear structure */ DBG (4, "attach_scanner: opening %s\n", devicename); status = CANON_open_device (&scan, devicename); if (status != SANE_STATUS_GOOD) { DBG (1, "ERROR: attach_scanner: opening %s failed\n", devicename); free (dev); return status; } dev->name = strdup (devicename); dev->sane.name = dev->name; dev->sane.vendor = "CANON"; dev->sane.model = CANON_get_device_name (&scan); dev->sane.type = "flatbed scanner"; CANON_close_device (&scan); ++num_devices; dev->next = first_dev; first_dev = dev; if (devp) *devp = dev; return SANE_STATUS_GOOD; } /* callback function for sanei_usb_attach_matching_devices */ static SANE_Status attach_one (const char *name) { attach_scanner (name, 0); return SANE_STATUS_GOOD; } /* Find our devices */ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { char config_line[PATH_MAX]; size_t len; FILE *fp; DBG_INIT (); #if 0 DBG_LEVEL = 10; #endif DBG (2, "sane_init: version_code %s 0, authorize %s 0\n", version_code == 0 ? "=" : "!=", authorize == 0 ? "=" : "!="); DBG (1, "sane_init: SANE Canon630u backend version %d.%d.%d from %s\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD, PACKAGE_STRING); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); sanei_usb_init (); fp = sanei_config_open (CANONUSB_CONFIG_FILE); if (!fp) { /* no config-file: try these */ attach_scanner ("/dev/scanner", 0); attach_scanner ("/dev/usbscanner", 0); attach_scanner ("/dev/usb/scanner", 0); return SANE_STATUS_GOOD; } DBG (3, "reading configure file %s\n", CANONUSB_CONFIG_FILE); while (sanei_config_read (config_line, sizeof (config_line), fp)) { if (config_line[0] == '#') continue; /* ignore line comments */ len = strlen (config_line); if (!len) continue; /* ignore empty lines */ DBG (4, "attach_matching_devices(%s)\n", config_line); sanei_usb_attach_matching_devices (config_line, attach_one); } DBG (4, "finished reading configure file\n"); fclose (fp); return SANE_STATUS_GOOD; } void sane_exit (void) { Canon_Device *dev, *next; DBG (3, "sane_exit\n"); for (dev = first_dev; dev; dev = next) { next = dev->next; free (dev->name); free (dev); } if (devlist) free (devlist); return; } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { Canon_Device *dev; int i; DBG (3, "sane_get_devices(local_only = %d)\n", local_only); if (devlist) free (devlist); devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); if (!devlist) return SANE_STATUS_NO_MEM; i = 0; for (dev = first_dev; i < num_devices; dev = dev->next) devlist[i++] = &dev->sane; devlist[i++] = 0; *device_list = devlist; return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { Canon_Device *dev; SANE_Status status; Canon_Scanner *scanner; DBG (3, "sane_open\n"); if (devicename[0]) /* search for devicename */ { DBG (4, "sane_open: devicename=%s\n", devicename); for (dev = first_dev; dev; dev = dev->next) if (strcmp (dev->sane.name, devicename) == 0) break; if (!dev) { status = attach_scanner (devicename, &dev); if (status != SANE_STATUS_GOOD) return status; } } else { DBG (2, "sane_open: no devicename, opening first device\n"); dev = first_dev; } if (!dev) return SANE_STATUS_INVAL; scanner = malloc (sizeof (*scanner)); if (!scanner) return SANE_STATUS_NO_MEM; memset (scanner, 0, sizeof (*scanner)); scanner->device = dev; status = CANON_open_device (&scanner->scan, dev->sane.name); if (status != SANE_STATUS_GOOD) { free (scanner); return status; } *handle = scanner; /* insert newly opened handle into list of open handles: */ scanner->next = first_handle; first_handle = scanner; return SANE_STATUS_GOOD; } void sane_close (SANE_Handle handle) { Canon_Scanner *prev, *scanner; DBG (3, "sane_close\n"); if (!first_handle) { DBG (1, "ERROR: sane_close: no handles opened\n"); return; } /* remove handle from list of open handles: */ prev = NULL; for (scanner = first_handle; scanner; scanner = scanner->next) { if (scanner == handle) break; prev = scanner; } if (!scanner) { DBG (1, "ERROR: sane_close: invalid handle %p\n", handle); return; /* oops, not a handle we know about */ } if (prev) prev->next = scanner->next; else first_handle = scanner->next; CANON_close_device (&scanner->scan); free (scanner); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { (void) handle; /* Eliminate compiler warning */ DBG (3, "sane_get_option_descriptor: option = %d\n", option); if (option < 0 || option >= NELEMS (so)) return NULL; return so[option].descriptor; } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int * info) { (void) handle; /* Eliminate compiler warning */ DBG (3, "sane_control_option: handle=%p, opt=%d, act=%d, val=%p, info=%p\n", handle, option, action, value, (void *)info); return dispatch_control_option (handle, option, action, value, info); } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { int rc = SANE_STATUS_GOOD; int w = SANE_UNFIX (optionBotRightXValue - optionTopLeftXValue) / MM_IN_INCH * optionResolutionValue; int h = SANE_UNFIX (optionBotRightYValue - optionTopLeftYValue) / MM_IN_INCH * optionResolutionValue; (void) handle; /* Eliminate compiler warning */ DBG (3, "sane_get_parameters\n"); parms.depth = 8; parms.last_frame = SANE_TRUE; parms.pixels_per_line = w; parms.lines = h; #ifdef GRAY if (optionGrayscaleValue == SANE_TRUE) { parms.format = SANE_FRAME_GRAY; parms.bytes_per_line = w; } else #endif { parms.format = SANE_FRAME_RGB; parms.bytes_per_line = w * 3; } *params = parms; return rc; } SANE_Status sane_start (SANE_Handle handle) { Canon_Scanner *scanner = handle; SANE_Status res; DBG (3, "sane_start\n"); res = CANON_set_scan_parameters (&scanner->scan, optionCalibrateValue, #ifdef GRAY optionGrayscaleValue, #else SANE_FALSE, #endif SANE_UNFIX (optionTopLeftXValue) / MM_IN_INCH * 600, SANE_UNFIX (optionTopLeftYValue) / MM_IN_INCH * 600, SANE_UNFIX (optionBotRightXValue) / MM_IN_INCH * 600, SANE_UNFIX (optionBotRightYValue) / MM_IN_INCH * 600, optionResolutionValue, optionAGainValue, SANE_UNFIX (optionGammaValue)); if (res != SANE_STATUS_GOOD) return res; return CANON_start_scan (&scanner->scan); } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * data, SANE_Int max_length, SANE_Int * length) { Canon_Scanner *scanner = handle; return CANON_read (&scanner->scan, data, max_length, length); } void sane_cancel (SANE_Handle handle) { DBG (3, "sane_cancel: handle = %p\n", handle); DBG (3, "sane_cancel: cancelling is unsupported in this backend\n"); } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { DBG (3, "sane_set_io_mode: handle = %p, non_blocking = %d\n", handle, non_blocking); if (non_blocking != SANE_FALSE) return SANE_STATUS_UNSUPPORTED; return SANE_STATUS_GOOD; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { (void) handle; /* silence gcc */ (void) fd; /* silence gcc */ return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/canon630u.conf.in000066400000000000000000000003011456256263500176540ustar00rootroot00000000000000# 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 backends-1.3.0/backend/canon_dr-cmd.h000066400000000000000000000621431456256263500173750ustar00rootroot00000000000000#ifndef CANON_DR_CMD_H #define CANON_DR_CMD_H /* * Part of SANE - Scanner Access Now Easy. * Please see opening comments in canon_dr.c */ /****************************************************/ #define USB_HEADER_LEN 12 #define USB_COMMAND_LEN 12 #define USB_STATUS_LEN 4 #define USB_PACKET_TIMEOUT 30000 /*static inline void */ static void setbitfield (unsigned char *pageaddr, int mask, int shift, int val) { *pageaddr = (*pageaddr & ~(mask << shift)) | ((val & mask) << shift); } /* ------------------------------------------------------------------------- */ /*static inline int */ static int getbitfield (unsigned char *pageaddr, int mask, int shift) { return ((*pageaddr >> shift) & mask); } /* ------------------------------------------------------------------------- */ static int getnbyte (unsigned char *pnt, int nbytes) { unsigned int result = 0; int i; #ifdef DEBUG assert (nbytes < 5); #endif for (i = 0; i < nbytes; i++) result = (result << 8) | (pnt[i] & 0xff); return result; } /* ------------------------------------------------------------------------- */ /*static inline void */ static void putnbyte (unsigned char *pnt, unsigned int value, unsigned int nbytes) { int i; #ifdef DEBUG assert (nbytes < 5); #endif for (i = nbytes - 1; i >= 0; i--) { pnt[i] = value & 0xff; value = value >> 8; } } /* ==================================================================== */ /* extended status packet */ #define get_ES_length(b) getnbyte(b+0x04, 4) /* ==================================================================== */ /* USB packets */ #define set_USB_CMD_xfer_length(sb, val) putnbyte(sb + 1, val, 3) #define set_USB_OUT_xfer_length(sb, val) putnbyte(sb + 1, val, 3) /* ==================================================================== */ /* SCSI commands */ #define set_SCSI_opcode(out, val) out[0]=val #define set_SCSI_lun(out, val) setbitfield(out + 1, 7, 5, val) /* ==================================================================== */ /* TEST_UNIT_READY */ #define TEST_UNIT_READY_code 0x00 #define TEST_UNIT_READY_len 6 /* ==================================================================== */ /* REQUEST_SENSE */ #define REQUEST_SENSE_code 0x03 #define REQUEST_SENSE_len 6 #define RS_return_size 0x0e #define set_RS_return_size(icb,val) icb[0x04]=val /* defines for request sense return block */ #define get_RS_information_valid(b) getbitfield(b + 0x00, 1, 7) #define get_RS_error_code(b) getbitfield(b + 0x00, 0x7f, 0) #define get_RS_filemark(b) getbitfield(b + 0x02, 1, 7) #define get_RS_EOM(b) getbitfield(b + 0x02, 1, 6) #define get_RS_ILI(b) getbitfield(b + 0x02, 1, 5) #define get_RS_sense_key(b) getbitfield(b + 0x02, 0x0f, 0) #define get_RS_information(b) getnbyte(b+0x03, 4) /* normally 0 */ #define get_RS_additional_length(b) b[0x07] /* always 6? */ #define get_RS_ASC(b) b[0x0c] #define get_RS_ASCQ(b) b[0x0d] #define get_RS_SKSV(b) getbitfield(b+0x0f,1,7) /* valid=0 */ #define get_RS_SKSB(b) getnbyte(b+0x0f, 3) /* when RS is 0x05/0x26 bad bytes listed in sksb */ /* #define get_RS_offending_byte(b) getnbyte(b+0x10, 2) */ /* ==================================================================== */ /* INQUIRY */ #define INQUIRY_code 0x12 #define INQUIRY_len 6 #define INQUIRY_std_max_len 0x30 #define INQUIRY_std_typ_len 0x30 #define INQUIRY_vpd_max_len 0x30 #define INQUIRY_vpd_typ_len 0x1e #define set_IN_evpd(icb, val) setbitfield(icb + 1, 1, 0, val) #define set_IN_page_code(icb, val) icb[0x02]=val #define set_IN_return_size(icb,val) icb[0x04]=val #define get_IN_periph_qual(in) getbitfield(in, 0x07, 5) #define IN_periph_qual_lun 0x00 #define IN_periph_qual_nolun 0x03 #define get_IN_periph_devtype(in) getbitfield(in, 0x1f, 0) #define IN_periph_devtype_scanner 0x06 #define IN_periph_devtype_unknown 0x1f #define get_IN_response_format(in) getbitfield(in + 0x03, 0x07, 0) #define IN_recognized 0x02 #define get_IN_vendor(in, buf) snprintf(buf, 0x08 + 1, "%.*s", \ 0x08, (char*)in + 0x08) #define get_IN_product(in, buf) snprintf(buf, 0x10 + 1, "%.*s", \ 0x10, (char*)in + 0x10) #define get_IN_version(in, buf) snprintf(buf, 0x04 + 1, "%.*s", \ 0x04, (char*)in + 0x20) /* the VPD response */ #define get_IN_page_length(in) in[0x04] #define get_IN_basic_x_res(in) getnbyte(in + 0x05, 2) #define get_IN_basic_y_res(in) getnbyte(in + 0x07, 2) #define get_IN_step_x_res(in) getbitfield(in+0x09, 1, 0) #define get_IN_step_y_res(in) getbitfield(in+0x09, 1, 4) #define get_IN_max_x_res(in) getnbyte(in + 0x0a, 2) #define get_IN_max_y_res(in) getnbyte(in + 0x0c, 2) #define get_IN_min_x_res(in) getnbyte(in + 0x0e, 2) #define get_IN_min_y_res(in) getnbyte(in + 0x10, 2) #define get_IN_std_res_60(in) getbitfield(in+ 0x12, 1, 7) #define get_IN_std_res_75(in) getbitfield(in+ 0x12, 1, 6) #define get_IN_std_res_100(in) getbitfield(in+ 0x12, 1, 5) #define get_IN_std_res_120(in) getbitfield(in+ 0x12, 1, 4) #define get_IN_std_res_150(in) getbitfield(in+ 0x12, 1, 3) #define get_IN_std_res_160(in) getbitfield(in+ 0x12, 1, 2) #define get_IN_std_res_180(in) getbitfield(in+ 0x12, 1, 1) #define get_IN_std_res_200(in) getbitfield(in+ 0x12, 1, 0) #define get_IN_std_res_240(in) getbitfield(in+ 0x13, 1, 7) #define get_IN_std_res_300(in) getbitfield(in+ 0x13, 1, 6) #define get_IN_std_res_320(in) getbitfield(in+ 0x13, 1, 5) #define get_IN_std_res_400(in) getbitfield(in+ 0x13, 1, 4) #define get_IN_std_res_480(in) getbitfield(in+ 0x13, 1, 3) #define get_IN_std_res_600(in) getbitfield(in+ 0x13, 1, 2) #define get_IN_std_res_800(in) getbitfield(in+ 0x13, 1, 1) #define get_IN_std_res_1200(in) getbitfield(in+ 0x13, 1, 0) #define get_IN_window_width(in) getnbyte(in + 0x14, 4) #define get_IN_window_length(in) getnbyte(in + 0x18, 4) #define get_IN_awd(in) getbitfield(in+0x1c, 1, 7) #define get_IN_ce_emphasis(in) getbitfield(in+0x1c, 1, 6) #define get_IN_c_emphasis(in) getbitfield(in+0x1c, 1, 5) #define get_IN_high_quality(in) getbitfield(in+0x1c, 1, 4) #define get_IN_multilevel(in) getbitfield(in+0x1c, 1, 3) #define get_IN_half_tone(in) getbitfield(in+0x1c, 1, 2) #define get_IN_monochrome(in) getbitfield(in+0x1c, 1, 1) #define get_IN_overflow(in) getbitfield(in+0x1c, 1, 0) /* some scanners need evpd inquiry data manipulated */ #define set_IN_page_length(in,val) in[0x04]=val /* ==================================================================== */ /* RESERVE_UNIT */ #define RESERVE_UNIT_code 0x16 #define RESERVE_UNIT_len 6 /* ==================================================================== */ /* RELEASE_UNIT */ #define RELEASE_UNIT_code 0x17 #define RELEASE_UNIT_len 6 /* ==================================================================== */ /* SCAN */ #define SCAN_code 0x1b #define SCAN_len 6 #define set_SC_xfer_length(sb, val) sb[0x04] = (unsigned char)val /* ==================================================================== */ /* SET_WINDOW */ #define SET_WINDOW_code 0x24 #define SET_WINDOW_len 10 #define set_SW_xferlen(sb, len) putnbyte(sb + 0x06, len, 3) #define SW_header_len 8 #define SW_desc_len 0x2c /* ==================================================================== */ /* GET_WINDOW */ #define GET_WINDOW_code 0x25 #define GET_WINDOW_len 0 /* ==================================================================== */ /* READ/SEND page codes */ #define SR_datatype_image 0x00 #define SR_datatype_lut 0x03 #define SR_datatype_pixelsize 0x80 /*DR-G1130*/ #define SR_datatype_panel 0x84 #define SR_datatype_sensors 0x8b #define SR_datatype_counters 0x8c #define SR_datatype_endorser 0x90 #define SR_datatype_fineoffset 0x90 #define SR_datatype_finegain 0x91 #define SR_datatype_imprinters 0x96 /*DR-X10C*/ /* ==================================================================== */ /* READ */ #define READ_code 0x28 #define READ_len 10 #define set_R_datatype_code(sb, val) sb[0x02] = val #define set_R_xfer_uid(sb, val) sb[4] = val #define set_R_xfer_lid(sb, val) sb[5] = val #define set_R_xfer_length(sb, val) putnbyte(sb + 0x06, val, 3) /*image needs no macros?*/ /*lut unread?*/ /*panel*/ #define R_PANEL_len 0x08 #define get_R_PANEL_start(in) getbitfield(in, 1, 7) #define get_R_PANEL_stop(in) getbitfield(in, 1, 6) #define get_R_PANEL_butt3(in) getbitfield(in, 1, 2) #define get_R_PANEL_new_file(in) getbitfield(in+1, 1, 0) #define get_R_PANEL_count_only(in) getbitfield(in+1, 1, 1) #define get_R_PANEL_bypass_mode(in) getbitfield(in+1, 1, 2) #define get_R_PANEL_enable_led(in) getbitfield(in+2, 1, 0) #define get_R_PANEL_counter(in) getnbyte(in + 0x04, 4) /*sensors*/ #define R_SENSORS_len 0x01 #define get_R_SENSORS_card(in) getbitfield(in, 1, 3) #define get_R_SENSORS_adf(in) getbitfield(in, 1, 0) /*counters*/ #define R_COUNTERS_len 0x80 #define get_R_COUNTERS_total(in) getnbyte(in + 0x04, 4) #define get_R_COUNTERS_last_srv(in) getnbyte(in + 0x44, 4) /*imprinters*/ #define R_IMPRINTER_len 0x20 #define R_PRE_IMPRINTER 0 #define R_POST_IMPRINTER 1 #define get_R_IMPRINTER_found(in) getbitfield(in+1, 1, 0) /*4 bytes at in + 0: (DR-X10C) pre-imprinter on: 0x07010000 pre-imprinter off: 0x06000000 post-imprinter on: 0x03010000 post-imprinter off: 0x02000000 */ /*endorser unread?*/ /*fine gain*/ #define R_FINE_uid_gray 0x07 #define R_FINE_uid_red 0x0c #define R_FINE_uid_green 0x0a #define R_FINE_uid_blue 0x09 #define R_FINE_uid_unknown 0x14 /* get pixel size */ #define R_PSIZE_len 16 #define get_R_PSIZE_width(in) getnbyte(in + 0x08, 4) #define get_R_PSIZE_length(in) getnbyte(in + 0x0c, 4) /* ==================================================================== */ /* SEND */ #define SEND_code 0x2a #define SEND_len 10 #define set_S_xfer_datatype(sb, val) sb[0x02] = (unsigned char)val #define set_S_xfer_id(sb, val) putnbyte(sb + 4, val, 2) #define set_S_xfer_length(sb, val) putnbyte(sb + 6, val, 3) /*lut*/ #define S_LUT_len 0x100 #define S_LUT_id_front 0x82 #define S_LUT_id_unk1 0x84 #define S_LUT_id_unk2 0x88 #define S_LUT_id_unk3 0x90 /*panel*/ #define S_PANEL_len 0x08 #define set_S_PANEL_enable_led(in,val) setbitfield(in+2, 1, 0, val) #define set_S_PANEL_counter(sb,val) putnbyte(sb + 0x04, val, 4) /*counters*/ /*endorser*/ /*fine calibration*/ #define set_S_FCAL_datatype(sb, val) sb[0x00] = (unsigned char)val /* these are offset, OR with 0x40 to get gain */ #define S_FCAL_id_f_red 0x00 #define S_FCAL_id_f_green 0x04 #define S_FCAL_id_f_blue 0x08 #define S_FCAL_id_b_red 0x01 #define S_FCAL_id_b_green 0x05 #define S_FCAL_id_b_blue 0x09 /* ==================================================================== */ /* OBJECT_POSITION */ #define OBJECT_POSITION_code 0x31 #define OBJECT_POSITION_len 10 #define set_OP_autofeed(b,val) setbitfield(b+0x01, 0x07, 0, val) #define OP_Discharge 0x00 #define OP_Feed 0x01 /* ==================================================================== */ /* Page codes used by GET/SET SCAN MODE */ #define SM_pc_adf 0x01 #define SM_pc_tpu 0x02 #define SM_pc_scan_ctl 0x20 #define SM_pc_df 0x30 #define SM_pc_buffer 0x32 #define SM_pc_imprinter 0x34 #define SM_pc_dropout 0x36 #define SM_pc_unknown 0x37 #define SM_pc_all_pc 0x3F /* ==================================================================== */ /* GET SCAN MODE */ #define GET_SCAN_MODE_code 0xd5 #define GET_SCAN_MODE_len 6 #define set_GSM_unknown(sb, val) sb[0x01] = val #define set_GSM_page_code(sb, val) sb[0x02] = val #define set_GSM_len(sb, val) sb[0x04] = val #define GSM_PSIZE_len 0x5a /* ==================================================================== */ /* SET SCAN MODE */ #define SET_SCAN_MODE_code 0xd6 #define SET_SCAN_MODE_len 6 #define set_SSM_pf(sb, val) setbitfield(sb + 1, 1, 4, val) #define set_SSM_pay_len(sb, val) sb[0x04] = val /* the payload */ #define SSM_PAY_len 0x14 #define SSM_PAY_HEAD_len 0x13 #define set_SSM_pay_head_len(sb, val) sb[0x01] = val #define set_SSM_page_code(sb, val) sb[0x04] = val #define SSM_PAGE_len 0x0e #define set_SSM_page_len(sb, val) sb[0x05] = val /* for DF (0x30) page */ #define set_SSM_DF_deskew_roll(sb, val) setbitfield(sb+7, 1, 5, val) #define set_SSM_DF_staple(sb, val) setbitfield(sb+7, 1, 4, val) #define set_SSM_DF_thick(sb, val) setbitfield(sb+7, 1, 2, val) #define set_SSM_DF_len(sb, val) setbitfield(sb+7, 1, 0, val) #define set_SSM_DF_textdir(sb, val) setbitfield(sb+9, 0xf, 0, val) /* for BUFFER (0x32) page */ #define set_SSM_BUFF_duplex(sb, val) setbitfield(sb+6, 1, 1, val) #define set_SSM_BUFF_unk(sb, val) sb[0x07] = val #define set_SSM_BUFF_async(sb, val) setbitfield(sb+0x0a, 1, 6, val) #define set_SSM_BUFF_ald(sb, val) setbitfield(sb+0x0a, 1, 5, val) #define set_SSM_BUFF_fb(sb, val) setbitfield(sb+0x0a, 1, 4, val) #define set_SSM_BUFF_card(sb, val) setbitfield(sb+0x0a, 1, 3, val) /* for DO (0x36) page */ #define SSM_DO_none 0 #define SSM_DO_red 1 #define SSM_DO_green 2 #define SSM_DO_blue 3 #define set_SSM_DO_unk1(sb, val) sb[0x07] = val #define set_SSM_DO_unk2(sb, val) sb[0x09] = val #define set_SSM_DO_f_do(sb, val) sb[0x0b] = val #define set_SSM_DO_b_do(sb, val) sb[0x0c] = val #define set_SSM_DO_f_en(sb, val) sb[0x0d] = val #define set_SSM_DO_b_en(sb, val) sb[0x0e] = val /* ==================================================================== */ /* Cancel */ #define CANCEL_code 0xd8 #define CANCEL_len 6 /* ==================================================================== */ /* Coarse Calibration */ #define COR_CAL_code 0xe1 #define COR_CAL_len 10 #define set_CC_version(sb, val) sb[5] = val #define set_CC_xferlen(sb, len) putnbyte(sb + 0x06, len, 3) /* the payload */ #define CC_pay_len 0x20 #define CC_pay_ver 0x00 #define set_CC_f_gain(sb, val) sb[0] = val #define set_CC_unk1(sb, val) sb[1] = val #define set_CC_f_offset(sb, val) sb[2] = val #define set_CC_unk2(sb, val) sb[3] = val #define set_CC_exp_f_r1(sb, val) putnbyte(sb + 0x04, val, 2) #define set_CC_exp_f_g1(sb, val) putnbyte(sb + 0x06, val, 2) #define set_CC_exp_f_b1(sb, val) putnbyte(sb + 0x08, val, 2) #define set_CC_exp_f_r2(sb, val) putnbyte(sb + 0x0a, val, 2) #define set_CC_exp_f_g2(sb, val) putnbyte(sb + 0x0c, val, 2) #define set_CC_exp_f_b2(sb, val) putnbyte(sb + 0x0e, val, 2) #define set_CC_b_gain(sb, val) sb[0x10] = val #define set_CC_b_offset(sb, val) sb[0x12] = val #define set_CC_exp_b_r1(sb, val) putnbyte(sb + 0x14, val, 2) #define set_CC_exp_b_g1(sb, val) putnbyte(sb + 0x16, val, 2) #define set_CC_exp_b_b1(sb, val) putnbyte(sb + 0x18, val, 2) #define set_CC_exp_b_r2(sb, val) putnbyte(sb + 0x1a, val, 2) #define set_CC_exp_b_g2(sb, val) putnbyte(sb + 0x1c, val, 2) #define set_CC_exp_b_b2(sb, val) putnbyte(sb + 0x1e, val, 2) /* the 'version 3' payload (P-208 and P-215) */ #define CC3_pay_len 0x28 #define CC3_pay_ver 0x03 #define set_CC3_gain_f_r(sb, val) sb[0] = val #define set_CC3_gain_f_g(sb, val) sb[1] = val #define set_CC3_gain_f_b(sb, val) sb[2] = val #define set_CC3_off_f_r(sb, val) sb[4] = val #define set_CC3_off_f_g(sb, val) sb[5] = val #define set_CC3_off_f_b(sb, val) sb[6] = val #define set_CC3_exp_f_r(sb, val) putnbyte(sb + 0x08, val, 2) #define set_CC3_exp_f_g(sb, val) putnbyte(sb + 0x0a, val, 2) #define set_CC3_exp_f_b(sb, val) putnbyte(sb + 0x0c, val, 2) #define set_CC3_gain_b_r(sb, val) sb[0x14] = val #define set_CC3_gain_b_g(sb, val) sb[0x15] = val #define set_CC3_gain_b_b(sb, val) sb[0x16] = val #define set_CC3_off_b_r(sb, val) sb[0x18] = val #define set_CC3_off_b_g(sb, val) sb[0x19] = val #define set_CC3_off_b_b(sb, val) sb[0x1a] = val #define set_CC3_exp_b_r(sb, val) putnbyte(sb + 0x1c, val, 2) #define set_CC3_exp_b_g(sb, val) putnbyte(sb + 0x1e, val, 2) #define set_CC3_exp_b_b(sb, val) putnbyte(sb + 0x20, val, 2) /* ==================================================================== */ /* Page codes used by GET/SET SCAN MODE 2 */ #define SM2_pc_df 0x00 #define SM2_pc_ultra 0x01 #define SM2_pc_buffer 0x02 #define SM2_pc_hw_enhancement 0x03 #define SM2_pc_dropout 0x06 #define SM2_pc_date_time 0x07 #define SM2_pc_imprinter_settings 0x33 #define SM2_pc_imprinter_specstring 0x34 /* ==================================================================== */ /* GET SCAN MODE 2 */ #define GET_SCAN_MODE2_code 0xe4 #define GET_SCAN_MODE2_len 12 /* ==================================================================== */ /* SET SCAN MODE 2 */ #define SET_SCAN_MODE2_code 0xe5 #define SET_SCAN_MODE2_len 12 #define set_SSM2_page_code(sb, val) sb[0x02] = val #define set_SSM2_pay_len(sb, val) sb[0x08] = val /* the payload */ #define SSM2_PAY_len 0x10 #define SSM2_IMPRINTER_STRING_PAY_len 0x8e /* for DF (0x00) page */ #define set_SSM2_DF_imprint(sb, val) setbitfield(sb+2, 1, 0, val) #define set_SSM2_DF_post_addon(sb, val) setbitfield(sb+2, 1, 1, val) #define set_SSM2_DF_len(sb, val) setbitfield(sb+3, 1, 0, val) #define set_SSM2_DF_thick(sb, val) setbitfield(sb+3, 1, 2, val) #define set_SSM2_DF_staple(sb, val) setbitfield(sb+3, 1, 4, val) #define set_SSM2_DF_imprint2(sb, val) setbitfield(sb+3, 1, 6, val) /* for ULTRA (0x01) page */ #define set_SSM2_ULTRA_top(sb, val) putnbyte(sb + 0x07, val, 2) #define set_SSM2_ULTRA_bot(sb, val) putnbyte(sb + 0x09, val, 2) /* for BUFFER (0x02) page */ #define set_SSM2_BUFF_unk(sb, val) sb[0x03] = val #define set_SSM2_BUFF_unk2(sb, val) sb[0x06] = val #define set_SSM2_BUFF_sync(sb, val) sb[0x09] = val /* for HARDWARE ENHANCEMENT (0x03) */ #define set_SSM2_roller_deskew(sb, val) setbitfield(sb+6, 1, 3, val) #define set_SSM2_hw_crop(sb, val) setbitfield(sb+6, 1, 5, val) /* for DROPOUT (0x06) page */ #define set_SSM2_DO_do(sb, val) sb[0x09] = val #define set_SSM2_DO_en(sb, val) sb[0x0a] = val #define set_SSM2_DO_side(sb, val) sb[0x05] = val /* for IMPRINT DATE & TIME (0x07) */ #define set_SSM2_imprint_year(sb, val) putnbyte(sb + 0x02, val, 2) #define set_SSM2_imprint_month(sb, val) putnbyte(sb + 0x04, val, 1) #define set_SSM2_imprint_day(sb, val) putnbyte(sb + 0x05, val, 1) #define set_SSM2_imprint_hour(sb, val) putnbyte(sb + 0x06, val, 1) #define set_SSM2_imprint_min(sb, val) putnbyte(sb + 0x07, val, 1) #define set_SSM2_imprint_sec(sb, val) putnbyte(sb + 0x08, val, 1) /* for IMPRINTER SETTINGS (0x33) page */ #define set_SSM2_postimprint_cmd(sb) sb[0x04] = 1 #define set_SSM2_postimprint_addon(sb) setbitfield(sb, 1, 1, 1) #define set_SSM2_imprint_hoffset(sb, val) putnbyte(sb + 0x05, val, 2) #define set_SSM2_imprint_voffset(sb, val) putnbyte(sb + 0x07, val, 2) /* for IMPRINTER STRING SPECIFICATION (0x34) page */ #define IMPRINTER_12x12_FONT 0 #define IMPRINTER_8x12_FONT 1 #define set_SSM2_imprint_fontsize(sb, val) setbitfield(sb + 0xA, 1, 0, val) #define set_SSM2_imprint_spacing(sb, val) setbitfield(sb + 0xA, 1, 1, val) #define set_SSM2_imprint_addonmode(sb, val) setbitfield(sb + 0x9, 3, 4, val) #define IMPRINTER_0_FONT_ROT 0 #define IMPRINTER_90_FONT_ROT 1 #define IMPRINTER_180_FONT_ROT 2 #define IMPRINTER_270_FONT_ROT 3 #define set_SSM2_imprint_fontrot(sb, val) setbitfield(sb + 0x9, 3, 0, val) /* ==================================================================== */ /* window descriptor macros for SET_WINDOW and GET_WINDOW */ #define set_WPDB_wdblen(sb, len) putnbyte(sb + 0x06, len, 2) /* ==================================================================== */ /* 0x00 - Window Identifier */ #define set_WD_wid(sb, val) sb[0] = val #define WD_wid_front 0x00 #define WD_wid_back 0x01 /* 0x01 - Reserved (bits 7-1), AUTO (bit 0) */ #define set_WD_auto(sb, val) setbitfield(sb + 0x01, 1, 0, val) #define get_WD_auto(sb) getbitfield(sb + 0x01, 1, 0) /* 0x02,0x03 - X resolution in dpi */ #define set_WD_Xres(sb, val) putnbyte(sb + 0x02, val, 2) #define get_WD_Xres(sb) getnbyte(sb + 0x02, 2) /* 0x04,0x05 - Y resolution in dpi */ #define set_WD_Yres(sb, val) putnbyte(sb + 0x04, val, 2) #define get_WD_Yres(sb) getnbyte(sb + 0x04, 2) /* 0x06-0x09 - Upper Left X in 1/1200 inch */ #define set_WD_ULX(sb, val) putnbyte(sb + 0x06, val, 4) #define get_WD_ULX(sb) getnbyte(sb + 0x06, 4) /* 0x0a-0x0d - Upper Left Y in 1/1200 inch */ #define set_WD_ULY(sb, val) putnbyte(sb + 0x0a, val, 4) #define get_WD_ULY(sb) getnbyte(sb + 0x0a, 4) /* 0x0e-0x11 - Width in 1/1200 inch */ #define set_WD_width(sb, val) putnbyte(sb + 0x0e, val, 4) #define get_WD_width(sb) getnbyte(sb + 0x0e, 4) /* 0x12-0x15 - Height in 1/1200 inch */ #define set_WD_length(sb, val) putnbyte(sb + 0x12, val, 4) #define get_WD_length(sb) getnbyte(sb + 0x12, 4) /* 0x16 - Brightness */ #define set_WD_brightness(sb, val) sb[0x16] = val #define get_WD_brightness(sb) sb[0x16] /* 0x17 - Threshold */ #define set_WD_threshold(sb, val) sb[0x17] = val #define get_WD_threshold(sb) sb[0x17] /* 0x18 - Contrast */ #define set_WD_contrast(sb, val) sb[0x18] = val #define get_WD_contrast(sb) sb[0x18] /* 0x19 - Image Composition (color mode) */ #define set_WD_composition(sb, val) sb[0x19] = val #define get_WD_composition(sb) sb[0x19] #define WD_comp_LA 0 #define WD_comp_HT 1 #define WD_comp_GS 2 #define WD_comp_CL 3 #define WD_comp_CH 4 #define WD_comp_CG 5 /* 0x1a - Depth */ #define set_WD_bitsperpixel(sb, val) sb[0x1a] = val #define get_WD_bitsperpixel(sb) sb[0x1a] /* 0x1b,0x1c - Halftone Pattern */ #define set_WD_ht_type(sb, val) sb[0x1b] = val #define get_WD_ht_type(sb) sb[0x1b] #define WD_ht_type_DEFAULT 0 #define WD_ht_type_DITHER 1 #define WD_ht_type_DIFFUSION 2 #define set_WD_ht_pattern(sb, val) sb[0x1c] = val #define get_WD_ht_pattern(sb) sb[0x1c] /* 0x1d - Reverse image, reserved area, padding type */ #define set_WD_rif(sb, val) setbitfield(sb + 0x1d, 1, 7, val) #define get_WD_rif(sb) getbitfield(sb + 0x1d, 1, 7) #define set_WD_rgb(sb, val) setbitfield(sb + 0x1d, 7, 4, val) #define get_WD_rgb(sb) getbitfield(sb + 0x1d, 7, 4) #define set_WD_padding(sb, val) setbitfield(sb + 0x1d, 7, 0, val) #define get_WD_padding(sb) getbitfield(sb + 0x1d, 7, 0) /* 0x1e,0x1f - Bit ordering */ #define set_WD_bitorder(sb, val) putnbyte(sb + 0x1e, val, 2) #define get_WD_bitorder(sb) getnbyte(sb + 0x1e, 2) /* 0x20 - compression type */ #define set_WD_compress_type(sb, val) sb[0x20] = val #define get_WD_compress_type(sb) sb[0x20] #define WD_cmp_NONE 0 #define WD_cmp_MH 1 #define WD_cmp_MR 2 #define WD_cmp_MMR 3 #define WD_cmp_JPEG 0x80 /* 0x21 - compression argument * specify "k" parameter with MR compress, * or with JPEG- Q param, 0-7 */ #define set_WD_compress_arg(sb, val) sb[0x21] = val #define get_WD_compress_arg(sb) sb[0x21] /* 0x22-0x27 - reserved */ /* 0x28-0x2c - vendor unique */ /* FIXME: more params here? */ #define set_WD_reserved2(sb, val) sb[0x2a] = val #define get_WD_reserved2(sb) sb[0x2a] /* ==================================================================== */ #endif backends-1.3.0/backend/canon_dr.c000066400000000000000000010120201456256263500166150ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. This file is part of the SANE package, and implements a SANE backend for various Canon DR-series scanners. Copyright (C) 2008-2022 m. allan noah Yabarana Corp. www.yabarana.com provided significant funding EvriChart, Inc. www.evrichart.com provided funding and loaned equipment Canon, USA. www.usa.canon.com loaned equipment HPrint hprint.com.br provided funding and testing for DR-2510 support Stone-IT www.stone-it.com provided funding for DR-2010 and DR-2050 support Smartmatic www.smartmatic.com provided testing and changes for DR-X10C support -------------------------------------------------------------------------- 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, see . 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. -------------------------------------------------------------------------- The source code is divided in sections which you can easily find by searching for the tag "@@". Section 1 - Init & static stuff Section 2 - sane_init, _get_devices, _open & friends Section 3 - sane_*_option functions Section 4 - sane_start, _get_param, _read & friends Section 5 - calibration functions Section 6 - sane_close functions Section 7 - misc functions Section 8 - image processing functions Changes: v1 2008-10-29, MAN - initial version v2 2008-11-04, MAN - round scanlines to even bytes - spin RS and usb_clear_halt code into new function - update various scsi payloads - calloc out block so it gets set to 0 initially v3 2008-11-07, MAN - back window uses id 1 - add option and functions to read/send page counter - add rif option v4 2008-11-11, MAN - eject document when sane_read() returns EOF v5 2008-11-25, MAN - remove EOF ejection code - add SSM and GSM commands - add dropout, doublefeed, and jpeg compression options - disable adf backside - fix adf duplex - read two extra lines (ignore errors) at end of image - only send scan command at beginning of batch - fix bug in hexdump with 0 length string - DR-7580 support v6 2008-11-29, MAN - fix adf simplex - rename ssm_duplex to ssm_buffer - add --buffer option - reduce inter-page commands when buffering is enabled - improve sense_handler output - enable counter option - drop unused code v7 2008-11-29, MAN - jpeg support (size rounding and header overwrite) - call object_position(load) between pages even if buffering is on - use request sense info bytes on short scsi reads - byte swap color BGR to RGB - round image width down, not up - round image height down to even # of lines - always transfer even # of lines per block - scsi and jpeg don't require reading extra lines to reach EOF - rename buffer option to buffermode to avoid conflict with scanimage - send ssm_do and ssm_df during sane_start - improve sense_handler output v8 2008-12-07, MAN - rename read/send_counter to read/send_panel - enable control panel during init - add options for all buttons - call TUR twice in wait_scanner(), even if first succeeds - disable rif - enable brightness/contrast/threshold options v9 2008-12-07, MAN - add rollerdeskew and stapledetect options - add rollerdeskew and stapledetect bits to ssm_df() v10 2008-12-10, MAN - add all documented request sense codes to sense_handler() - fix color jpeg (remove unneeded BGR to RGB swapping code) - add macros for LUT data v11 2009-01-10, MAN - send_panel() can disable too - add cancel() to send d8 command - call cancel() only after final read from scanner - stop button requests cancel v12 2009-01-21, MAN - don't export private symbols v13 2009-03-06, MAN - new vendor ID for recent machines - add usb ids for several new machines v14 2009-03-07, MAN - remove HARD_SELECT from counter (Legitimate, but API violation) - attach to CR-series scanners as well v15 2009-03-15, MAN - add byte-oriented duplex interlace code - add RRGGBB color interlace code - add basic support for DR-2580C v16 2009-03-20, MAN - add more unknown setwindow bits - add support for 16 byte status packets - clean do_usb_cmd error handling (call reset more often) - add basic support for DR-2050C, DR-2080C, DR-2510C v17 2009-03-20, MAN - set status packet size from config file v18 2009-03-21, MAN - rewrite config file parsing to reset options after each scanner - add config options for vendor, model, version - don't call inquiry if those 3 options are set - remove default config file from code - add initial gray deinterlacing code for DR-2510C - rename do_usb_reset to do_usb_clear v19 2009-03-22, MAN - pad gray deinterlacing area for DR-2510C - override tl_x and br_x for fixed width scanners v20 2009-03-23, MAN - improved macros for inquiry and set window - shorten inquiry vpd length to match windows driver - remove status-length config option - add padded-read config option - rewrite do_usb_cmd to pad reads and calloc/copy buffers v21 2009-03-24, MAN - correct rgb padding macro - skip send_panel and ssm_df commands for DR-20xx scanners v22 2009-03-25, MAN - add deinterlacing code for DR-2510C in duplex and color v23 2009-03-27, MAN - rewrite all image data processing code - handle more image interlacing formats - re-enable binary mode on some scanners - limit some machines to full-width scanning v24 2009-04-02, MAN - fix DR-2510C duplex deinterlacing code - rewrite sane_read helpers to read until EOF - update sane_start for scanners that don't use object_position - don't call sanei_usb_clear_halt() if device is not open - increase default buffer size to 4 megs - set buffermode on by default - hide modes and resolutions that DR-2510C lies about - read_panel() logs front-end access to sensors instead of timing - rewrite do_usb_cmd() to use remainder from RS info v25 2009-04-12, MAN - disable SANE_FRAME_JPEG v26 2009-04-14, MAN (SANE 1.0.20) - return cmd status for reads on sensors - allow rs to adjust read length for all bad status responses v27 2009-05-08, MAN - bug fix in read_panel() - initialize vars in do_usb_cmd() - set buffermode off by default - clear page counter during init and sane_start() - eject previous page during init and sane_start() - improved SSM_BUFF macros - moved set_window() to after ssm-*() - add coarse calibration (AFE offset/gain & per-channel exposure) - add fine calibration (per-cell offset/gain) - free image and fine cal buffers in sane_close() - compare page counter of small scanners only in non-buffered mode - add back-side gray mirroring code for DR-2580C v28 2009-05-20, MAN - use average instead of min/max for fine offset and gain - rewrite supported resolution list as x and y arrays - merge x and y resolution options into single option - move scan params into two new structs, s->u and s->s - sane_get_parameters() just returns values from s->u - don't call wait_scanner() in object_position() - don't call ssm_*() from option handler - refactor sane_start() - read_from_buffer() can workaround missing res, modes and cropping - set most DR-2xxx machines to use the read_from_buffer workarounds - set default threshold to 90 - add option for button #3 of some machines - don't eject paper during init - add DR-2010 quirks - switch counter to HARD_SELECT, not SOFT v29 2009-06-01, MAN - split coarse and fine cal to run independently - add side option - reset scan params to user request if calibration fails - better handling of sane_cancel - better handling of errors during sane_start and sane_read v30 2009-06-17, MAN - add fine cal support for machines with internal buffer (2050/2080) - support fixed-width machines that require even bytes per scanline - pad end of scan with gray if scanner stops prematurely - better handling of errors during calibration - cleanup canceling debug messages - remove old cancel() prototype - small sleep before clearing usb halt condition v31 2009-06-29, MAN - reduce default buffer size to 2 megs v32 2009-07-21, MAN - crop/resample image data before buffering, not after - shink image buffers to size of output image, not input - correct some debug message - better handling of EOF - add intermediate param struct to existing user and scan versions v33 2009-07-23, MAN - add software brightness/contrast for dumb scanners - add blocking mode to allow full-page manipulation options to run - add swdespeck option and support code - add swdeskew and swcrop options (disabled) v34 2009-07-28, MAN - add simplified Hough transform based deskewing code - add extremity detecting cropping code - use per-model background color to fill corners after deskew - request and chop extra scanlines instead of rounding down - remove padding dumb scanners add to top of front side - sane_get_params uses intermediate struct instead of user struct - if scanner stops, clone the last line until the end of buffer - reset some intermediate params between duplex sides v35 2010-02-09, MAN (SANE 1.0.21) - cleanup #includes and copyright - add SANE_I18N to static strings - don't fail if scsi buffer is too small v36 2011-01-03, MAN - initial support for DR-3080 and DR-5060 - add code to clamp scan width to an arbitrary byte width boundary - add code to prevent setting of brightness/threshold/contrast - don't send dropout color command on non-color scanners - initial support for DR-7090C - update credits v37 2011-01-26, MAN (SANE 1.0.22) - don't center window when using flatbed - improve request sense error messages - enable flatbed for all known models v38 2011-07-06, MAN - initial support for DR-5020 - use ppl_mod instead of Bpl_mod, apply to all modes - invert logic of read_panel tracking - add ability to disable read_panel() - automatically disable read/send_panel if unsupported v39 2011-11-01, MAN - DR-2580C pads the backside of duplex scans v40 2012-11-01, MAN - initial DR-9050C, DR-7550C, DR-6050C and DR-3010C support v41 2013-07-31, MAN (SANE 1.0.24) - initial P-208 and P-215 support - bug fix for calibration of scanners with duplex_offset - allow duplex_offset to be controlled from config file v42 2013-12-09, MAN - initial DR-G1100 support - add support for paper sensors (P-215 & P-208) - add initial support for card reader (P-215) - removed unused var from do_scsi_cmd() v43 2014-03-13, MAN - initial DR-M140 support - add extra_status config and code - split status code into do_usb_status - fix copy_line margin offset - add new color interlacing modes and code - comment out ssm2 - add timestamp to do_usb_cmd v44 2014-03-26, MAN - buffermode support for machines with ssm2 command - DR-M140 needs always_op=0 v45 2014-03-29, MAN - dropout support for machines with ssm2 command - doublefeed support for machines with ssm2 command v46 2014-04-09, MAN - split debug level 30 into two levels - simplify jpeg ifdefs - add support for DR-M160 v47 2014-07-07, MAN - initial DR-G1130 support v48 2014-08-06, MAN - set another unknown byte in buffermode for ssm2 - add another gettimeofday call at end of do_usb_cmd - don't print 0 length line in hexdump v49 2015-03-18, MAN - initial support for DR-C125 v50 2015-08-23, MAN - DR-C125 adds duplex padding on back side - initial support for DR-C225 v51 2015-08-25, MAN (SANE 1.0.25) - DR-C125 does not invert_tly, does need sw_lut v52 2015-11-03, MAN - set can_color=1 by default (recent models don't have 'C' in name) - enable jpeg for DR-6080 - add must_downsample and must_fully_buffer - improve dropout option handling - add software dropout implementation for downsampled modes v53 2015-11-06, MAN - replace image processing methods with sanei_magic - add swskip option - reorder geometry group options - use bg_color to fill missing image data v54 2015-11-21, MAN - br_x and br_y locked to page_width/height until changed v55 2016-03-19, MAN - fixed-width scanners were calculating left-side offset incorrectly in color - initial support for DR-F120 - rename all DUPLEX_INTERLACE_* to indicate start and end of line v56 2016-08-23, MAN - initial support for P-150 v57 2019-02-24, manuarg - complete support for X-10, including hardware cropping v58 2019-11-10, MAN - adjust wait_scanner to set runRS only as a last resort, bug #154 v59 2020-09-23, MAN - restructure fine calibration code - initial support for uploading fine calibration payloads - improve DR-C225 support v60 2020-11-28, MAN - add new gray and color interlacing options for DR-C120 - initial support for DR-C120 and C130 - enable fine calibration for P-208 (per @sashacmc in !546) v61 2021-02-13, MAN - treat DR-P208 like P-208 (#356) - treat DR-P215 like P-215 (#356) - adjust wait_scanner to try one TUR with a long timeout (#142) v62 2021-02-13, MAN - allow config file to set inq and vpd lengths for DR-M1060 (#263) - rewrite do_cmd() timeout handling - remove long timeout TUR from v61 (did not help) - allow config file to set initial tur timeout for DR-X10C (#142) v63 2022-11-18, CQ, MAN - add support for reading the total and roller counters v64 2022-11-18, CQ, MAN - add complete support for imprinters on X10C (#585) v65 2023-06-06, MAN - fix imprinter support (#672) - update attach_one and other init functions SANE FLOW DIAGRAM - 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 . . - sane_read() : read image data (from pipe) . . (sane_read called multiple times; after sane_read returns EOF, . . loop may continue with sane_start which may return a 2nd page . . when doing duplex scans, or load the next page from the ADF) . . . . - sane_cancel() : cancel operation . - sane_close() : close opened scanner device - sane_exit() : terminate use of backend */ /* * @@ Section 1 - Init */ #include "../include/sane/config.h" #include /*memcpy...*/ #include /*isspace*/ #include /*tan*/ #include /*usleep*/ #include /*gettimeofday*/ #include /*localtime*/ #include /*strtol*/ #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_scsi.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_magic.h" #include "canon_dr-cmd.h" #include "canon_dr.h" #define DEBUG 1 #define BUILD 65 /* values for SANE_DEBUG_CANON_DR env var: - errors 5 - function trace 10 - function detail 15 - get/setopt cmds 20 - scsi/usb trace 25 - scsi/usb writes 30 - scsi/usb reads 31 - useless noise 35 */ /* ------------------------------------------------------------------------- */ /* if JPEG support is not enabled in sane.h, we setup our own defines */ #ifndef SANE_FRAME_JPEG #define SANE_FRAME_JPEG 0x0B #define SANE_JPEG_DISABLED 1 #endif /* ------------------------------------------------------------------------- */ #define STRING_FLATBED SANE_I18N("Flatbed") #define STRING_ADFFRONT SANE_I18N("ADF Front") #define STRING_ADFBACK SANE_I18N("ADF Back") #define STRING_ADFDUPLEX SANE_I18N("ADF Duplex") #define STRING_CARDFRONT SANE_I18N("Card Front") #define STRING_CARDBACK SANE_I18N("Card Back") #define STRING_CARDDUPLEX SANE_I18N("Card Duplex") #define STRING_LINEART SANE_VALUE_SCAN_MODE_LINEART #define STRING_HALFTONE SANE_VALUE_SCAN_MODE_HALFTONE #define STRING_GRAYSCALE SANE_VALUE_SCAN_MODE_GRAY #define STRING_COLOR SANE_VALUE_SCAN_MODE_COLOR #define STRING_RED SANE_I18N("Red") #define STRING_GREEN SANE_I18N("Green") #define STRING_BLUE SANE_I18N("Blue") #define STRING_EN_RED SANE_I18N("Enhance Red") #define STRING_EN_GREEN SANE_I18N("Enhance Green") #define STRING_EN_BLUE SANE_I18N("Enhance Blue") #define STRING_NONE SANE_I18N("None") #define STRING_JPEG SANE_I18N("JPEG") #define STRING_IMPRINTER_8x12_FONT SANE_I18N("8x12") #define STRING_IMPRINTER_12x12_FONT SANE_I18N("12x12") #define STRING_IMPRINTER_ADDON_BoW SANE_I18N("Black-on-White") #define STRING_IMPRINTER_ADDON_BoI SANE_I18N("Black-on-Image") #define STRING_IMPRINTER_ADDON_WoB SANE_I18N("White-on-Black") /* Also set via config file. */ static int global_buffer_size; static int global_buffer_size_default = 2 * 1024 * 1024; static int global_padded_read; static int global_padded_read_default = 0; static int global_extra_status; static int global_extra_status_default = 0; static int global_duplex_offset; static int global_duplex_offset_default = 0; static int global_inquiry_length; static int global_vpd_length; static int global_tur_timeout; static int global_tur_timeout_default = USB_PACKET_TIMEOUT/60; /* half second */ static char global_vendor_name[9]; static char global_model_name[17]; static char global_version_name[5]; /* * 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 scanner structs */ static const SANE_Device **sane_devArray = NULL; static struct scanner *scanner_devList = NULL; /* * @@ Section 2 - SANE & scanner init code */ /* * 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) { (void) authorize; /* get rid of compiler warning */ DBG_INIT (); DBG (10, "sane_init: start\n"); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); DBG (5, "sane_init: canon_dr backend %d.%d.%d, from %s\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD, PACKAGE_STRING); DBG (10, "sane_init: finish\n"); 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. */ /* * Read the config file, find scanners with help from sanei_* * and store in global device structs */ SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { SANE_Status ret = SANE_STATUS_GOOD; struct scanner * s; struct scanner * prev = NULL; char line[PATH_MAX]; const char *lp; FILE *fp; int num_devices=0; int i=0; (void) local_only; /* get rid of compiler warning */ DBG (10, "sane_get_devices: start\n"); /* mark all existing scanners as missing, attach_one will remove mark */ for (s = scanner_devList; s; s = s->next) { s->missing = 1; } sanei_usb_init(); /* reset globals before reading the file */ default_globals(); fp = sanei_config_open (CANON_DR_CONFIG_FILE); if (fp) { DBG (15, "sane_get_devices: reading config file %s\n", CANON_DR_CONFIG_FILE); while (sanei_config_read (line, PATH_MAX, fp)) { lp = line; /* ignore comments */ if (*lp == '#') continue; /* skip empty lines */ if (*lp == 0) continue; if (!strncmp ("option", lp, 6) && isspace (lp[6])) { lp += 6; lp = sanei_config_skip_whitespace (lp); /* BUFFERSIZE: > 4K */ if (!strncmp (lp, "buffer-size", 11) && isspace (lp[11])) { int buf; lp += 11; lp = sanei_config_skip_whitespace (lp); buf = atoi (lp); if (buf < 4096) { DBG (5, "sane_get_devices: config option \"buffer-size\" " "(%d) is < 4096, ignoring!\n", buf); continue; } if (buf > global_buffer_size_default) { DBG (5, "sane_get_devices: config option \"buffer-size\" " "(%d) is > %d, scanning problems may result\n", buf, global_buffer_size_default); } DBG (15, "sane_get_devices: setting \"buffer-size\" to %d\n", buf); global_buffer_size = buf; } /* PADDED READ: we clamp to 0 or 1 */ else if (!strncmp (lp, "padded-read", 11) && isspace (lp[11])) { int buf; lp += 11; lp = sanei_config_skip_whitespace (lp); buf = atoi (lp); if (buf < 0) { DBG (5, "sane_get_devices: config option \"padded-read\" " "(%d) is < 0, ignoring!\n", buf); continue; } if (buf > 1) { DBG (5, "sane_get_devices: config option \"padded-read\" " "(%d) is > 1, ignoring!\n", buf); continue; } DBG (15, "sane_get_devices: setting \"padded-read\" to %d\n", buf); global_padded_read = buf; } /* EXTRA STATUS: we clamp to 0 or 1 */ else if (!strncmp (lp, "extra-status", 12) && isspace (lp[12])) { int buf; lp += 12; lp = sanei_config_skip_whitespace (lp); buf = atoi (lp); if (buf < 0) { DBG (5, "sane_get_devices: config option \"extra-status\" " "(%d) is < 0, ignoring!\n", buf); continue; } if (buf > 1) { DBG (5, "sane_get_devices: config option \"extra-status\" " "(%d) is > 1, ignoring!\n", buf); continue; } DBG (15, "sane_get_devices: setting \"extra-status\" to %d\n", buf); global_extra_status = buf; } /* DUPLEXOFFSET: < 2400 */ else if (!strncmp (lp, "duplex-offset", 13) && isspace (lp[13])) { int buf; lp += 13; lp = sanei_config_skip_whitespace (lp); buf = atoi (lp); if (buf > 2400) { DBG (5, "sane_get_devices: config option \"duplex-offset\" " "(%d) is > 2400, ignoring!\n", buf); continue; } if (buf < 0) { DBG (5, "sane_get_devices: config option \"duplex-offset\" " "(%d) is < 0, ignoring!\n", buf); continue; } DBG (15, "sane_get_devices: setting \"duplex-offset\" to %d\n", buf); global_duplex_offset = buf; } /* INQUIRY_LENGTH: <= 0x30 */ else if (!strncmp (lp, "inquiry-length", 14) && isspace (lp[14])) { int buf; lp += 14; lp = sanei_config_skip_whitespace (lp); buf = (int) strtol (lp,NULL,16); if (buf > INQUIRY_std_max_len) { DBG (5, "sane_get_devices: config option \"inquiry-length\" " "(%#04x) is > %#04x, ignoring!\n", buf, INQUIRY_std_max_len); continue; } if (buf < 0) { DBG (5, "sane_get_devices: config option \"inquiry-length\" " "(%#04x) is < 0, ignoring!\n", buf); continue; } DBG (15, "sane_get_devices: setting \"inquiry-length\" to %#04x\n", buf); global_inquiry_length = buf; } /* VPD_LENGTH: <= 0x30 */ else if (!strncmp (lp, "vpd-length", 10) && isspace (lp[10])) { int buf; lp += 10; lp = sanei_config_skip_whitespace (lp); buf = (int) strtol (lp,NULL,16); if (buf > INQUIRY_vpd_max_len) { DBG (5, "sane_get_devices: config option \"vpd-length\" " "(%#04x) is > %#04x, ignoring!\n", buf, INQUIRY_vpd_max_len); continue; } if (buf < 0) { DBG (5, "sane_get_devices: config option \"vpd-length\" " "(%#04x) is < 0, ignoring!\n", buf); continue; } DBG (15, "sane_get_devices: setting \"vpd-length\" to %#04x\n", buf); global_vpd_length = buf; } /* TUR_TIMEOUT <= 60000 */ else if (!strncmp (lp, "tur-timeout", 11) && isspace (lp[11])) { int buf; lp += 11; lp = sanei_config_skip_whitespace (lp); buf = atoi (lp); if (buf > 60000) { DBG (5, "sane_get_devices: config option \"tur-timeout\" " "(%d) is > 60000, ignoring!\n", buf); continue; } if (buf < 0) { DBG (5, "sane_get_devices: config option \"tur-timeout\" " "(%d) is < 0, ignoring!\n", buf); continue; } DBG (15, "sane_get_devices: setting \"tur-timeout\" to %d\n", buf); global_tur_timeout = buf; } /* VENDOR: we ingest up to 8 bytes */ else if (!strncmp (lp, "vendor-name", 11) && isspace (lp[11])) { lp += 11; lp = sanei_config_skip_whitespace (lp); strncpy(global_vendor_name, lp, 8); global_vendor_name[8] = 0; DBG (15, "sane_get_devices: setting \"vendor-name\" to %s\n", global_vendor_name); } /* MODEL: we ingest up to 16 bytes */ else if (!strncmp (lp, "model-name", 10) && isspace (lp[10])) { lp += 10; lp = sanei_config_skip_whitespace (lp); strncpy(global_model_name, lp, 16); global_model_name[16] = 0; DBG (15, "sane_get_devices: setting \"model-name\" to %s\n", global_model_name); } /* VERSION: we ingest up to 4 bytes */ else if (!strncmp (lp, "version-name", 12) && isspace (lp[12])) { lp += 12; lp = sanei_config_skip_whitespace (lp); strncpy(global_version_name, lp, 4); global_version_name[4] = 0; DBG (15, "sane_get_devices: setting \"version-name\" to %s\n", global_version_name); } else { DBG (5, "sane_get_devices: config option \"%s\" unrecognized " "- ignored.\n", lp); } } else if ((strncmp ("usb", lp, 3) == 0) && isspace (lp[3])) { DBG (15, "sane_get_devices: looking for '%s'\n", lp); sanei_usb_attach_matching_devices(lp, attach_one_usb); /* re-default these after reading the usb line */ default_globals(); } else if ((strncmp ("scsi", lp, 4) == 0) && isspace (lp[4])) { DBG (15, "sane_get_devices: looking for '%s'\n", lp); sanei_config_attach_matching_devices (lp, attach_one_scsi); /* re-default these after reading the scsi line */ default_globals(); } else{ DBG (5, "sane_get_devices: config line \"%s\" unrecognized - " "ignored.\n", lp); } } fclose (fp); } else { DBG (5, "sane_get_devices: missing required config file '%s'!\n", CANON_DR_CONFIG_FILE); } /*delete missing scanners from list*/ for (s = scanner_devList; s;) { if(s->missing){ DBG (5, "sane_get_devices: missing scanner %s\n",s->device_name); /*splice s out of list by changing pointer in prev to next*/ if(prev){ prev->next = s->next; free(s); s=prev->next; } /*remove s from head of list, using prev to cache it*/ else{ prev = s; s = s->next; free(prev); prev=NULL; /*reset head to next s*/ scanner_devList = s; } } else{ prev = s; s=prev->next; } } for (s = scanner_devList; s; s=s->next) { DBG (15, "sane_get_devices: found scanner %s\n",s->device_name); num_devices++; } DBG (15, "sane_get_devices: found %d scanner(s)\n",num_devices); if (sane_devArray) free (sane_devArray); sane_devArray = calloc (num_devices + 1, sizeof (SANE_Device*)); if (!sane_devArray) return SANE_STATUS_NO_MEM; for (s = scanner_devList; s; s=s->next) { sane_devArray[i++] = (SANE_Device *)&s->sane; } sane_devArray[i] = 0; if(device_list){ *device_list = sane_devArray; } DBG (10, "sane_get_devices: finish\n"); return ret; } /* callbacks used by sane_get_devices */ static SANE_Status attach_one_scsi (const char *device_name) { return attach_one(device_name,CONNECTION_SCSI); } static SANE_Status attach_one_usb (const char *device_name) { return attach_one(device_name,CONNECTION_USB); } /* build the scanner struct and link to global list * unless struct is already loaded, then pretend */ static SANE_Status attach_one (const char *device_name, int connType) { struct scanner *s; int ret; DBG (10, "attach_one: start\n"); DBG (15, "attach_one: looking for '%s'\n", device_name); for (s = scanner_devList; s; s = s->next) { if (strcmp (s->device_name, device_name) == 0){ DBG (10, "attach_one: already attached!\n"); s->missing = 0; return SANE_STATUS_GOOD; } } /* build a scanner struct to hold it */ if ((s = calloc (sizeof (*s), 1)) == NULL) return SANE_STATUS_NO_MEM; /* config file settings */ s->buffer_size = global_buffer_size; s->padded_read = global_padded_read; s->extra_status = global_extra_status; s->duplex_offset = global_duplex_offset; s->inquiry_length = global_inquiry_length; s->vpd_length = global_vpd_length; s->tur_timeout = global_tur_timeout; /* copy the device name */ strcpy (s->device_name, device_name); /* connect the fd */ s->connection = connType; s->fd = -1; ret = connect_fd(s); if(ret != SANE_STATUS_GOOD){ free (s); return ret; } /* query the device to load its vendor/model/version, */ /* if config file doesn't give all three */ if ( !strlen(global_vendor_name) || !strlen(global_model_name) || !strlen(global_version_name) ){ ret = init_inquire (s); if (ret != SANE_STATUS_GOOD) { disconnect_fd(s); free (s); DBG (5, "attach_one: inquiry failed\n"); return ret; } } /* override any inquiry settings with those from config file */ if(strlen(global_vendor_name)) strcpy(s->vendor_name, global_vendor_name); if(strlen(global_model_name)) strcpy(s->model_name, global_model_name); if(strlen(global_version_name)) strcpy(s->version_name, global_version_name); /* load detailed specs/capabilities from the device */ /* if a model cannot support inquiry vpd, this function will die */ ret = init_vpd (s); if (ret != SANE_STATUS_GOOD) { disconnect_fd(s); free (s); DBG (5, "attach_one: vpd failed\n"); return ret; } /* clean up the scanner struct based on model */ /* this is the big piece of model specific code */ ret = init_model (s); if (ret != SANE_STATUS_GOOD) { disconnect_fd(s); free (s); DBG (5, "attach_one: model failed\n"); return ret; } /* this detects imprinters if they are available */ ret = init_imprinters (s); if (ret != SANE_STATUS_GOOD) { DBG (5, "attach_one: unable to init_imprinters, continuing\n"); } /* enable/read the buttons */ ret = init_panel (s); if (ret != SANE_STATUS_GOOD) { DBG (5, "attach_one: unable init_panel, continuing\n"); } /* enable/read the lifecycle counters */ ret = init_counters (s); if (ret != SANE_STATUS_GOOD) { DBG (5, "attach_one: unable to init_counters, continuing\n"); } /* sets SANE option 'values' to good defaults */ ret = init_user (s); if (ret != SANE_STATUS_GOOD) { disconnect_fd(s); free (s); DBG (5, "attach_one: user failed\n"); return ret; } /* sets the s->opt array to blank */ ret = init_options (s); if (ret != SANE_STATUS_GOOD) { disconnect_fd(s); free (s); DBG (5, "attach_one: options failed\n"); return ret; } /* load strings into sane_device struct */ s->sane.name = s->device_name; s->sane.vendor = s->vendor_name; s->sane.model = s->model_name; s->sane.type = "scanner"; /* change name in sane_device struct if scanner has serial number ret = init_serial (s); if (ret == SANE_STATUS_GOOD) { s->sane.name = s->serial_name; } else{ DBG (5, "attach_one: serial number unsupported?\n"); } */ /* we close the connection, so that another backend can talk to scanner */ disconnect_fd(s); /* store this scanner in global vars */ s->next = scanner_devList; scanner_devList = s; DBG (10, "attach_one: finish\n"); return SANE_STATUS_GOOD; } /* * connect the fd in the scanner struct */ static SANE_Status connect_fd (struct scanner *s) { SANE_Status ret; int buffer_size = s->buffer_size; DBG (10, "connect_fd: start\n"); 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 (%s)\n", s->device_name); ret = sanei_usb_open (s->device_name, &(s->fd)); if(!ret){ ret = sanei_usb_clear_halt(s->fd); } } else { DBG (15, "connect_fd: opening SCSI device (%s)\n", s->device_name); ret = sanei_scsi_open_extended (s->device_name, &(s->fd), sense_handler, s, &s->buffer_size); if(!ret && buffer_size != s->buffer_size){ DBG (5, "connect_fd: cannot get requested buffer size (%d/%d)\n", buffer_size, s->buffer_size); } } if(ret == 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); } } else{ DBG (5, "connect_fd: could not open device: %d\n", ret); } DBG (10, "connect_fd: finish\n"); return ret; } /* * This routine will check if a certain device is a Canon scanner * It also copies interesting data from INQUIRY into the handle structure */ static SANE_Status init_inquire (struct scanner *s) { int i; SANE_Status ret; unsigned char cmd[INQUIRY_len]; size_t cmdLen = INQUIRY_len; unsigned char in[INQUIRY_std_max_len]; size_t inLen = s->inquiry_length; DBG (10, "init_inquire: start\n"); memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, INQUIRY_code); set_IN_return_size (cmd, inLen); set_IN_evpd (cmd, 0); set_IN_page_code (cmd, 0); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if (ret != SANE_STATUS_GOOD){ DBG (10, "init_inquire: failed: %d\n", ret); return ret; } if (get_IN_periph_devtype (in) != IN_periph_devtype_scanner){ DBG (5, "The device at '%s' is not a scanner.\n", s->device_name); return SANE_STATUS_INVAL; } get_IN_vendor (in, s->vendor_name); get_IN_product (in, s->model_name); get_IN_version (in, s->version_name); s->vendor_name[8] = 0; s->model_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->model_name[i] == ' ' && i >= 0; i--) s->model_name[i] = 0; for (i = 3; s->version_name[i] == ' ' && i >= 0; i--) s->version_name[i] = 0; /*check for vendor name*/ if (strcmp ("CANON", 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 Canon products.\n"); return SANE_STATUS_INVAL; } /*check for model name*/ if (strncmp ("DR", s->model_name, 2) && strncmp ("CR", s->model_name, 2) && strncmp ("P-", s->model_name, 2) && strncmp ("R", s->model_name, 1) ) { DBG (5, "The device at '%s' is reported to be a '%s'\n", s->device_name, s->model_name); DBG (5, "This backend only supports Canon P-, CR & DR-series products.\n"); return SANE_STATUS_INVAL; } DBG (15, "init_inquire: Found %s scanner %s version %s at %s\n", s->vendor_name, s->model_name, s->version_name, s->device_name); DBG (10, "init_inquire: finish\n"); return SANE_STATUS_GOOD; } /* * Use INQUIRY VPD to setup more detail about the scanner */ static SANE_Status init_vpd (struct scanner *s) { SANE_Status ret; unsigned char cmd[INQUIRY_len]; size_t cmdLen = INQUIRY_len; unsigned char in[INQUIRY_vpd_max_len]; size_t inLen = s->vpd_length; DBG (10, "init_vpd: start\n"); /* get EVPD */ memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, INQUIRY_code); set_IN_return_size (cmd, inLen); set_IN_evpd (cmd, 1); set_IN_page_code (cmd, 0xf0); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); DBG (15, "init_vpd: length=%0x\n",get_IN_page_length (in)); /* This scanner supports vital product data. * Use this data to set dpi-lists etc. */ if (ret == SANE_STATUS_GOOD || ret == SANE_STATUS_EOF) { DBG (15, "standard options\n"); s->basic_x_res = get_IN_basic_x_res (in); DBG (15, " basic x res: %d dpi\n",s->basic_x_res); s->basic_y_res = get_IN_basic_y_res (in); DBG (15, " basic y res: %d dpi\n",s->basic_y_res); s->step_x_res = get_IN_step_x_res (in); DBG (15, " step x res: %d dpi\n", s->step_x_res); s->step_y_res = get_IN_step_y_res (in); DBG (15, " step y res: %d dpi\n", s->step_y_res); s->max_x_res = get_IN_max_x_res (in); DBG (15, " max x res: %d dpi\n", s->max_x_res); s->max_y_res = get_IN_max_y_res (in); DBG (15, " max y res: %d dpi\n", s->max_y_res); s->min_x_res = get_IN_min_x_res (in); DBG (15, " min x res: %d dpi\n", s->min_x_res); s->min_y_res = get_IN_min_y_res (in); DBG (15, " min y res: %d dpi\n", s->min_y_res); /* some scanners list B&W resolutions. */ s->std_res_x[DPI_60] = get_IN_std_res_60 (in); s->std_res_y[DPI_60] = s->std_res_x[DPI_60]; DBG (15, " 60 dpi: %d\n", s->std_res_x[DPI_60]); s->std_res_x[DPI_75] = get_IN_std_res_75 (in); s->std_res_y[DPI_75] = s->std_res_x[DPI_75]; DBG (15, " 75 dpi: %d\n", s->std_res_x[DPI_75]); s->std_res_x[DPI_100] = get_IN_std_res_100 (in); s->std_res_y[DPI_100] = s->std_res_x[DPI_100]; DBG (15, " 100 dpi: %d\n", s->std_res_x[DPI_100]); s->std_res_x[DPI_120] = get_IN_std_res_120 (in); s->std_res_y[DPI_120] = s->std_res_x[DPI_120]; DBG (15, " 120 dpi: %d\n", s->std_res_x[DPI_120]); s->std_res_x[DPI_150] = get_IN_std_res_150 (in); s->std_res_y[DPI_150] = s->std_res_x[DPI_150]; DBG (15, " 150 dpi: %d\n", s->std_res_x[DPI_150]); s->std_res_x[DPI_160] = get_IN_std_res_160 (in); s->std_res_y[DPI_160] = s->std_res_x[DPI_160]; DBG (15, " 160 dpi: %d\n", s->std_res_x[DPI_160]); s->std_res_x[DPI_180] = get_IN_std_res_180 (in); s->std_res_y[DPI_180] = s->std_res_x[DPI_180]; DBG (15, " 180 dpi: %d\n", s->std_res_x[DPI_180]); s->std_res_x[DPI_200] = get_IN_std_res_200 (in); s->std_res_y[DPI_200] = s->std_res_x[DPI_200]; DBG (15, " 200 dpi: %d\n", s->std_res_x[DPI_200]); s->std_res_x[DPI_240] = get_IN_std_res_240 (in); s->std_res_y[DPI_240] = s->std_res_x[DPI_240]; DBG (15, " 240 dpi: %d\n", s->std_res_x[DPI_240]); s->std_res_x[DPI_300] = get_IN_std_res_300 (in); s->std_res_y[DPI_300] = s->std_res_x[DPI_300]; DBG (15, " 300 dpi: %d\n", s->std_res_x[DPI_300]); s->std_res_x[DPI_320] = get_IN_std_res_320 (in); s->std_res_y[DPI_320] = s->std_res_x[DPI_320]; DBG (15, " 320 dpi: %d\n", s->std_res_x[DPI_320]); s->std_res_x[DPI_400] = get_IN_std_res_400 (in); s->std_res_y[DPI_400] = s->std_res_x[DPI_400]; DBG (15, " 400 dpi: %d\n", s->std_res_x[DPI_400]); s->std_res_x[DPI_480] = get_IN_std_res_480 (in); s->std_res_y[DPI_480] = s->std_res_x[DPI_480]; DBG (15, " 480 dpi: %d\n", s->std_res_x[DPI_480]); s->std_res_x[DPI_600] = get_IN_std_res_600 (in); s->std_res_y[DPI_600] = s->std_res_x[DPI_600]; DBG (15, " 600 dpi: %d\n", s->std_res_x[DPI_600]); s->std_res_x[DPI_800] = get_IN_std_res_800 (in); s->std_res_y[DPI_800] = s->std_res_x[DPI_800]; DBG (15, " 800 dpi: %d\n", s->std_res_x[DPI_800]); s->std_res_x[DPI_1200] = get_IN_std_res_1200 (in); s->std_res_y[DPI_1200] = s->std_res_x[DPI_1200]; DBG (15, " 1200 dpi: %d\n", s->std_res_x[DPI_1200]); /* maximum window width and length are reported in basic units.*/ s->max_x = get_IN_window_width(in) * 1200 / s->basic_x_res; DBG(15, " max width: %d (%2.2f in)\n",s->max_x,(float)s->max_x/1200); s->max_y = get_IN_window_length(in) * 1200 / s->basic_y_res; DBG(15, " max length: %d (%2.2f in)\n",s->max_y,(float)s->max_y/1200); DBG (15, " AWD: %d\n", get_IN_awd(in)); DBG (15, " CE Emphasis: %d\n", get_IN_ce_emphasis(in)); DBG (15, " C Emphasis: %d\n", get_IN_c_emphasis(in)); DBG (15, " High quality: %d\n", get_IN_high_quality(in)); /* known modes FIXME more here? */ s->can_grayscale = get_IN_multilevel (in); DBG (15, " grayscale: %d\n", s->can_grayscale); s->can_halftone = get_IN_half_tone (in); DBG (15, " halftone: %d\n", s->can_halftone); s->can_monochrome = get_IN_monochrome (in); DBG (15, " monochrome: %d\n", s->can_monochrome); s->can_overflow = get_IN_overflow(in); DBG (15, " overflow: %d\n", s->can_overflow); } /*FIXME no vpd, set some defaults? */ else{ DBG (5, "init_vpd: Your scanner does not support VPD?\n"); DBG (5, "init_vpd: Please contact kitno455 at gmail dot com\n"); DBG (5, "init_vpd: with details of your scanner model.\n"); } DBG (10, "init_vpd: finish\n"); return ret; } /* * get model specific info that is not in vpd, and correct * errors in vpd data. struct is already initialized to 0. */ static SANE_Status init_model (struct scanner *s) { DBG (10, "init_model: start\n"); 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; s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_RGB; s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_RGB; s->always_op = 1; s->has_df = 1; s->has_btc = 1; s->has_counter = 1; s->has_adf = 1; s->has_duplex = 1; s->has_buffer = 1; s->can_read_panel = 1; s->can_write_panel = 1; s->has_ssm = 1; s->brightness_steps = 255; s->contrast_steps = 255; s->threshold_steps = 255; s->ppl_mod = 1; s->bg_color = 0xee; /* assume these are same as adf, override below */ s->valid_x = s->max_x; s->max_x_fb = s->max_x; s->max_y_fb = s->max_y; /* missing from vpd- we will unset these for some machines below */ s->can_color = 1; s->can_read_lifecycle_counters = 1; /* specific settings missing from vpd */ if (strstr (s->model_name,"DR-9080")){ s->has_comp_JPEG = 1; s->rgb_format = 2; } else if (strstr (s->model_name,"DR-6080") || strstr (s->model_name,"DR-7580")){ s->has_comp_JPEG = 1; s->can_color = 0; } else if (strstr (s->model_name,"DR-7090")){ s->has_flatbed = 1; } else if (strstr (s->model_name,"DR-9050") || strstr (s->model_name,"DR-7550") || strstr (s->model_name,"DR-6050") || strstr (s->model_name,"DR-G1100") || strstr (s->model_name,"DR-G1130") ){ /*missing*/ s->std_res_x[DPI_100]=1; s->std_res_y[DPI_100]=1; s->std_res_x[DPI_150]=1; s->std_res_y[DPI_150]=1; s->std_res_x[DPI_200]=1; s->std_res_y[DPI_200]=1; s->std_res_x[DPI_240]=1; s->std_res_y[DPI_240]=1; s->std_res_x[DPI_300]=1; s->std_res_y[DPI_300]=1; s->std_res_x[DPI_400]=1; s->std_res_y[DPI_400]=1; s->std_res_x[DPI_600]=1; s->std_res_y[DPI_600]=1; /*weirdness*/ s->has_ssm = 0; s->has_ssm2 = 1; } else if (strstr (s->model_name,"DR-4080") || strstr (s->model_name,"DR-4580") || strstr (s->model_name,"DR-7080")){ s->has_flatbed = 1; } else if (strstr (s->model_name,"DR-2580")){ s->invert_tly = 1; s->rgb_format = 1; s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_RRGGBB; s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_rRgGbB; s->gray_interlace[SIDE_BACK] = GRAY_INTERLACE_gG; s->duplex_interlace = DUPLEX_INTERLACE_FBfb; s->need_ccal = 1; s->fcal_src = FCAL_SRC_SCAN; s->fcal_dest = FCAL_DEST_SW; /*s->duplex_offset = 432; now set in config file*/ s->duplex_offset_side = SIDE_BACK; /*lies*/ s->can_halftone=0; s->can_monochrome=0; } else if (strstr (s->model_name,"DR-2510") || strstr (s->model_name,"DR-2010") ){ s->rgb_format = 1; s->always_op = 0; s->unknown_byte2 = 0x80; s->fixed_width = 1; s->valid_x = 8.5 * 1200; s->gray_interlace[SIDE_FRONT] = GRAY_INTERLACE_2510; s->gray_interlace[SIDE_BACK] = GRAY_INTERLACE_2510; s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_2510; s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_2510; s->duplex_interlace = DUPLEX_INTERLACE_2510; /*s->duplex_offset = 400; now set in config file*/ s->need_ccal = 1; s->fcal_src = FCAL_SRC_SCAN; s->fcal_dest = FCAL_DEST_SW; s->sw_lut = 1; /*s->invert_tly = 1;*/ /*only in Y direction, so we trash them in X*/ s->std_res_x[DPI_100]=0; s->std_res_x[DPI_150]=0; s->std_res_x[DPI_200]=0; s->std_res_x[DPI_240]=0; s->std_res_x[DPI_400]=0; /*lies*/ s->can_halftone=0; s->can_monochrome=0; } else if (strstr (s->model_name,"R40") ){ /* confirmed */ s->gray_interlace[SIDE_FRONT] = GRAY_INTERLACE_C120; s->gray_interlace[SIDE_BACK] = GRAY_INTERLACE_C120; s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_C120; s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_C120; s->duplex_interlace = DUPLEX_INTERLACE_2510; /*s->duplex_offset = 320; now set in config file*/ s->fixed_width = 1; s->need_ccal = 1; s->fcal_src = FCAL_SRC_SCAN; s->fcal_dest = FCAL_DEST_SW; s->rgb_format = 1; s->sw_lut = 1; /*only in Y direction, so we trash them in X*/ s->std_res_x[DPI_100]=0; s->std_res_x[DPI_150]=0; s->std_res_x[DPI_200]=0; s->std_res_x[DPI_240]=0; s->std_res_x[DPI_400]=0; /* suspected settings */ s->ccal_version = 3; s->has_df_ultra = 1; } /* copied from 2510, possibly incorrect */ else if (strstr (s->model_name,"DR-3010")){ s->rgb_format = 1; s->always_op = 0; s->unknown_byte2 = 0x80; s->fixed_width = 1; s->valid_x = 8.5 * 1200; s->gray_interlace[SIDE_FRONT] = GRAY_INTERLACE_2510; s->gray_interlace[SIDE_BACK] = GRAY_INTERLACE_2510; s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_2510; s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_2510; s->duplex_interlace = DUPLEX_INTERLACE_2510; /*s->duplex_offset = 400; now set in config file*/ s->need_ccal = 1; s->fcal_src = FCAL_SRC_SCAN; s->fcal_dest = FCAL_DEST_SW; s->sw_lut = 1; s->invert_tly = 1; /*only in Y direction, so we trash them in X*/ s->std_res_x[DPI_100]=0; s->std_res_x[DPI_150]=0; s->std_res_x[DPI_200]=0; s->std_res_x[DPI_240]=0; s->std_res_x[DPI_400]=0; /*lies*/ s->can_halftone=0; s->can_monochrome=0; } else if (strstr (s->model_name,"DR-2050") || strstr (s->model_name,"DR-2080")){ s->can_write_panel = 0; s->has_df = 0; s->fixed_width = 1; s->even_Bpl = 1; s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_RRGGBB; s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_RRGGBB; s->duplex_interlace = DUPLEX_INTERLACE_FBfb; s->fcal_src = FCAL_SRC_HW; s->fcal_dest = FCAL_DEST_SW; s->bg_color = 0x08; /*s->duplex_offset = 840; now set in config file*/ s->sw_lut = 1; /*lies*/ s->can_halftone=0; s->can_monochrome=0; } else if (strstr (s->model_name,"DR-3080")){ s->can_write_panel = 0; s->has_df = 0; s->has_btc = 0; } else if (strstr (s->model_name,"DR-5060F")){ s->can_write_panel = 0; s->has_df = 0; s->has_btc = 0; s->ppl_mod = 32; s->reverse_by_mode[MODE_LINEART] = 0; s->reverse_by_mode[MODE_HALFTONE] = 0; s->can_color = 0; } else if (strstr (s->model_name,"DR-5020")){ s->can_read_panel = 0; s->can_write_panel = 0; s->has_df = 0; s->has_btc = 0; s->ppl_mod = 32; s->reverse_by_mode[MODE_LINEART] = 0; s->reverse_by_mode[MODE_HALFTONE] = 0; s->can_color = 0; } /* all copied from P-215 */ else if (strstr (s->model_name, "P-150")) { s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_rRgGbB; s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_RRGGBB; s->gray_interlace[SIDE_FRONT] = GRAY_INTERLACE_gG; s->duplex_interlace = DUPLEX_INTERLACE_FBfb; s->need_ccal = 1; s->invert_tly = 1; s->unknown_byte2 = 0x88; s->rgb_format = 1; s->has_ssm_pay_head_len = 1; s->ppl_mod = 8; s->ccal_version = 3; s->can_read_sensors = 1; s->has_card = 1; } else if (strstr (s->model_name, "P-208") || strstr (s->model_name,"DR-P208")){ s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_RRGGBB; s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_rRgGbB; s->gray_interlace[SIDE_BACK] = GRAY_INTERLACE_gG; s->duplex_interlace = DUPLEX_INTERLACE_FBfb; s->need_ccal = 1; s->fcal_src = FCAL_SRC_SCAN; s->fcal_dest = FCAL_DEST_SW; s->invert_tly = 1; s->unknown_byte2 = 0x88; s->rgb_format = 1; s->has_ssm_pay_head_len = 1; s->ppl_mod = 8; s->ccal_version = 3; s->can_read_sensors = 1; } else if (strstr (s->model_name, "P-215") || strstr (s->model_name,"DR-P215")){ s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_rRgGbB; s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_RRGGBB; s->gray_interlace[SIDE_FRONT] = GRAY_INTERLACE_gG; s->duplex_interlace = DUPLEX_INTERLACE_FBfb; s->need_ccal = 1; s->invert_tly = 1; s->unknown_byte2 = 0x88; s->rgb_format = 1; s->has_ssm_pay_head_len = 1; s->ppl_mod = 8; s->ccal_version = 3; s->can_read_sensors = 1; s->has_card = 1; } else if (strstr (s->model_name,"DR-M160")){ /*missing*/ s->std_res_x[DPI_100]=1; s->std_res_y[DPI_100]=1; s->std_res_x[DPI_150]=1; s->std_res_y[DPI_150]=1; s->std_res_x[DPI_200]=1; s->std_res_y[DPI_200]=1; s->std_res_x[DPI_300]=1; s->std_res_y[DPI_300]=1; s->std_res_x[DPI_400]=1; s->std_res_y[DPI_400]=1; s->std_res_x[DPI_600]=1; s->std_res_y[DPI_600]=1; s->has_comp_JPEG = 1; s->rgb_format = 1; s->has_df_ultra = 1; s->color_inter_by_res[DPI_100] = COLOR_INTERLACE_GBR; s->color_inter_by_res[DPI_150] = COLOR_INTERLACE_GBR; s->color_inter_by_res[DPI_200] = COLOR_INTERLACE_BRG; s->color_inter_by_res[DPI_400] = COLOR_INTERLACE_GBR; /*weirdness*/ s->always_op = 0; s->fixed_width = 1; s->invert_tly = 1; s->can_write_panel = 0; s->has_ssm = 0; s->has_ssm2 = 1; s->duplex_interlace = DUPLEX_INTERLACE_FfBb; s->duplex_offset_side = SIDE_FRONT; /*lies*/ s->can_halftone=0; s->can_monochrome=0; } else if (strstr (s->model_name,"DR-M140")){ /*missing*/ s->std_res_x[DPI_100]=1; s->std_res_y[DPI_100]=1; s->std_res_x[DPI_150]=1; s->std_res_y[DPI_150]=1; s->std_res_x[DPI_200]=1; s->std_res_y[DPI_200]=1; s->std_res_x[DPI_300]=1; s->std_res_y[DPI_300]=1; s->std_res_x[DPI_400]=1; s->std_res_y[DPI_400]=1; s->std_res_x[DPI_600]=1; s->std_res_y[DPI_600]=1; s->has_comp_JPEG = 1; s->rgb_format = 1; s->has_df_ultra = 1; s->color_inter_by_res[DPI_100] = COLOR_INTERLACE_GBR; s->color_inter_by_res[DPI_150] = COLOR_INTERLACE_GBR; s->color_inter_by_res[DPI_200] = COLOR_INTERLACE_BRG; s->color_inter_by_res[DPI_400] = COLOR_INTERLACE_GBR; /*weirdness*/ s->always_op = 0; s->fixed_width = 1; s->invert_tly = 1; s->can_write_panel = 0; s->has_ssm = 0; s->has_ssm2 = 1; s->duplex_interlace = DUPLEX_INTERLACE_FfBb; s->duplex_offset_side = SIDE_BACK; /*lies*/ s->can_halftone=0; s->can_monochrome=0; } else if (strstr (s->model_name,"DR-C120") || strstr (s->model_name,"DR-C130") ){ /*confirmed settings*/ s->need_ccal = 1; s->ccal_version = 3; s->gray_interlace[SIDE_FRONT] = GRAY_INTERLACE_C120; s->gray_interlace[SIDE_BACK] = GRAY_INTERLACE_C120; s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_C120; s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_C120; s->duplex_interlace = DUPLEX_INTERLACE_2510; s->duplex_offset_side = SIDE_BACK; s->unknown_byte2 = 0x88; s->fcal_src = FCAL_SRC_SCAN; s->fcal_dest = FCAL_DEST_SW; s->sw_lut = 1; s->rgb_format = 1; /*s->duplex_offset = 400; now set in config file*/ /*only in Y direction, so we trash them in X*/ s->std_res_x[DPI_100]=0; s->std_res_x[DPI_150]=0; s->std_res_x[DPI_200]=0; s->std_res_x[DPI_240]=0; s->std_res_x[DPI_400]=0; /*suspected settings*/ s->always_op = 0; s->fixed_width = 1; s->valid_x = 8.5 * 1200; } else if (strstr (s->model_name,"DR-C125")){ /*confirmed settings*/ s->gray_interlace[SIDE_FRONT] = GRAY_INTERLACE_2510; s->gray_interlace[SIDE_BACK] = GRAY_INTERLACE_2510; s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_2510; s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_2510; s->duplex_interlace = DUPLEX_INTERLACE_2510; s->duplex_offset_side = SIDE_BACK; s->unknown_byte2 = 0x88; s->need_ccal = 1; s->ccal_version = 3; s->fcal_src = FCAL_SRC_SCAN; s->fcal_dest = FCAL_DEST_SW; s->sw_lut = 1; s->rgb_format = 1; /*s->duplex_offset = 400; now set in config file*/ /*only in Y direction, so we trash them in X*/ s->std_res_x[DPI_100]=0; s->std_res_x[DPI_150]=0; s->std_res_x[DPI_200]=0; s->std_res_x[DPI_240]=0; s->std_res_x[DPI_400]=0; /*suspected settings*/ s->always_op = 0; s->fixed_width = 1; s->valid_x = 8.5 * 1200; } else if (strstr (s->model_name,"DR-C225")){ s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_RRGGBB; s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_RRGGBB; s->gray_interlace[SIDE_BACK] = GRAY_INTERLACE_gG; s->duplex_interlace = DUPLEX_INTERLACE_PER_CHANNEL; s->unknown_byte2 = 0x88; s->need_ccal = 1; s->ccal_version = 3; s->fcal_src = FCAL_SRC_SCAN; s->fcal_dest = FCAL_DEST_HW; s->invert_tly = 1; s->rgb_format = 1; /*s->duplex_offset = 400; now set in config file*/ /*only in Y direction, so we trash them in X*/ s->std_res_x[DPI_100]=0; s->std_res_x[DPI_150]=0; s->std_res_x[DPI_200]=0; s->std_res_x[DPI_240]=0; s->std_res_x[DPI_400]=0; /*suspected settings*/ s->always_op = 0; s->fixed_width = 1; s->valid_x = 8.5 * 1200; } else if (strstr (s->model_name,"DR-F120")){ /* TODO items: * * has_rif = 0 ? is this correct * * has_comp_JPEG = 0 ? is this correct * * need_ccal = need_fcal = need_fcal_buffer = ccal_version = 0 ? is this correct */ /* Required for USB coms */ s->has_ssm = 0; s->has_ssm2 = 1; /*missing*/ s->std_res_x[DPI_100] = 1; s->std_res_y[DPI_100] = 1; // DPI_150 not supported s->std_res_x[DPI_200] = 1; s->std_res_y[DPI_200] = 1; s->std_res_x[DPI_300] = 1; s->std_res_y[DPI_300] = 1; // DPI_400 not supported s->std_res_x[DPI_600]= 1; s->std_res_y[DPI_600] = 1; // DPI_1200 not supported // NOTE: This scanner supports higher resolutions // in the Y direction, but 600 is maximum in X // This is true however only the ADF is ever selected in hardware // FIXME: What extra option is needed to select this in the USB comms s->has_flatbed = 1; /* duplex */ s->duplex_interlace = DUPLEX_INTERLACE_fFBb; s->color_interlace[SIDE_FRONT] = COLOR_INTERLACE_GBR; s->color_interlace[SIDE_BACK] = COLOR_INTERLACE_GBR; s->color_inter_by_res[DPI_100] = COLOR_INTERLACE_RGB; s->color_inter_by_res[DPI_600] = COLOR_INTERLACE_RGB; s->duplex_offset_side = SIDE_BACK; /* weirdness */ s->fixed_width = 1; /* lies */ s->can_halftone = 0; } else if (strstr (s->model_name,"DR-X10C")){ int i = 0; /* Required for USB coms */ s->has_ssm = 0; s->has_ssm2 = 1; /* missing */ s->std_res_x[DPI_100]=1; s->std_res_y[DPI_100]=1; s->std_res_x[DPI_150]=1; s->std_res_y[DPI_150]=1; s->std_res_x[DPI_200]=1; s->std_res_y[DPI_200]=1; s->std_res_x[DPI_240]=1; s->std_res_y[DPI_240]=1; s->std_res_x[DPI_300]=1; s->std_res_y[DPI_300]=1; s->std_res_x[DPI_400]=1; s->std_res_y[DPI_400]=1; s->std_res_x[DPI_600]=1; s->std_res_y[DPI_600]=1; s->has_hwcrop = 1; /*valid horizontal offsets for post-imprinter*/ s->post_imprinter_h_offset_list[++i] = 21; s->post_imprinter_h_offset_list[++i] = 35; s->post_imprinter_h_offset_list[++i] = 47; s->post_imprinter_h_offset_list[++i] = 59; s->post_imprinter_h_offset_list[++i] = 72; s->post_imprinter_h_offset_list[++i] = 99; s->post_imprinter_h_offset_list[++i] = 114; s->post_imprinter_h_offset_list[++i] = 143; s->post_imprinter_h_offset_list[++i] = 155; s->post_imprinter_h_offset_list[++i] = 167; s->post_imprinter_h_offset_list[++i] = 196; s->post_imprinter_h_offset_list[++i] = 211; s->post_imprinter_h_offset_list[++i] = 239; s->post_imprinter_h_offset_list[++i] = 251; s->post_imprinter_h_offset_list[++i] = 263; s->post_imprinter_h_offset_list[++i] = 275; s->post_imprinter_h_offset_list[++i] = 289; s->post_imprinter_h_offset_list[0] = i; i = 0; /*valid horizontal offsets for pre-imprinter*/ s->pre_imprinter_h_offset_list[++i] = 14; s->pre_imprinter_h_offset_list[++i] = 28; s->pre_imprinter_h_offset_list[++i] = 41; s->pre_imprinter_h_offset_list[++i] = 53; s->pre_imprinter_h_offset_list[++i] = 65; s->pre_imprinter_h_offset_list[++i] = 106; s->pre_imprinter_h_offset_list[0] = i; /*valid vertical offsets for imprinters*/ s->imprinter_v_offset_range.min = 0; s->imprinter_v_offset_range.max = 500; s->imprinter_v_offset_range.quant = 1; i = 0; /*valid font angles for imprinters*/ s->imprinter_font_angle_list[++i] = 0; s->imprinter_font_angle_list[++i] = 90; s->imprinter_font_angle_list[++i] = 180; s->imprinter_font_angle_list[++i] = 270; s->imprinter_font_angle_list[0] = i; i = 0; s->imprint_font_size_list[i++] = STRING_IMPRINTER_8x12_FONT; s->imprint_font_size_list[i++] = STRING_IMPRINTER_12x12_FONT; s->imprint_font_size_list[i] = NULL; i = 0; s->imprint_addon_mode_list[i++] = STRING_IMPRINTER_ADDON_BoI; s->imprint_addon_mode_list[i++] = STRING_IMPRINTER_ADDON_BoW; s->imprint_addon_mode_list[i++] = STRING_IMPRINTER_ADDON_WoB; s->imprint_addon_mode_list[i++] = STRING_NONE; s->imprint_addon_mode_list[i] = NULL; } DBG (10, "init_model: finish\n"); return SANE_STATUS_GOOD; } /* * try to detect imprinters. */ static SANE_Status init_imprinters (struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; DBG (10, "init_imprinters: start\n"); /* check the pre imprinter first */ ret = detect_imprinter(s,R_PRE_IMPRINTER); if(ret == SANE_STATUS_GOOD){ DBG (15, "init_imprinters: preimprinter found\n"); s->has_pre_imprinter = 1; } /* these scanners only support one imprinter */ /* so only ask for postimp if preimp not found */ else if(ret == SANE_STATUS_UNSUPPORTED){ ret = detect_imprinter(s,R_POST_IMPRINTER); if(ret == SANE_STATUS_GOOD){ DBG (15, "init_imprinters: postimprinter found\n"); s->has_post_imprinter = 1; } } DBG (10, "init_imprinters: finish\n"); return ret; } /* * This function enables the buttons and preloads the current panel values */ static SANE_Status init_panel (struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; DBG (10, "init_panel: start\n"); ret = read_panel(s,0); if(ret){ DBG (5, "init_panel: disabling read_panel\n"); s->can_read_panel = 0; } s->panel_enable_led = 1; s->panel_counter = 0; ret = send_panel(s); if(ret){ DBG (5, "init_panel: disabling send_panel\n"); s->can_write_panel = 0; } DBG (10, "init_panel: finish\n"); return ret; } /* * This function disables the lifecycle counters if not available */ static SANE_Status init_counters (struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; DBG (10, "init_counters: start\n"); ret = read_counters(s); if(ret){ DBG (5, "init_counters: disabling lifecycle counters\n"); s->can_read_lifecycle_counters = 0; } DBG (10, "init_counters: finish\n"); return ret; } /* * set good default user values. * struct is already initialized to 0. */ static SANE_Status init_user (struct scanner *s) { DBG (10, "init_user: start\n"); /* source */ if(s->has_flatbed) s->u.source = SOURCE_FLATBED; else if(s->has_adf) s->u.source = SOURCE_ADF_FRONT; else if(s->has_card) s->u.source = SOURCE_CARD_FRONT; /* scan mode */ if(s->can_monochrome) s->u.mode=MODE_LINEART; else if(s->can_halftone) s->u.mode=MODE_HALFTONE; else if(s->can_grayscale) s->u.mode=MODE_GRAYSCALE; else if(s->can_color) s->u.mode=MODE_COLOR; /*x and y res*/ s->u.dpi_x = s->basic_x_res; s->u.dpi_y = s->basic_x_res; /* page width US-Letter */ s->u.page_x = 8.5 * 1200; if(s->u.page_x > s->valid_x){ s->u.page_x = s->valid_x; } /* page height US-Letter */ s->u.page_y = 11 * 1200; if(s->u.page_y > s->max_y){ s->u.page_y = s->max_y; } /* bottom-right x */ s->u.br_x = s->u.page_x; /* bottom-right y */ s->u.br_y = s->u.page_y; s->threshold = 90; s->compress_arg = 50; s->pre_imprint.h_offset = 65; s->post_imprint.h_offset = 155; s->post_imprint_addon_mode = ADDON_BoI; DBG (10, "init_user: finish\n"); return SANE_STATUS_GOOD; } /* * This function presets the "option" array to blank */ static SANE_Status init_options (struct scanner *s) { int i; DBG (10, "init_options: start\n"); 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; } /* 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].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; DBG (10, "init_options: finish\n"); return SANE_STATUS_GOOD; } /* * 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 scanner *dev = NULL; struct scanner *s = NULL; SANE_Status ret; DBG (10, "sane_open: start\n"); if(scanner_devList){ DBG (15, "sane_open: searching currently attached scanners\n"); } else{ DBG (15, "sane_open: no scanners currently attached, attaching\n"); ret = sane_get_devices(NULL,0); if(ret != SANE_STATUS_GOOD){ return ret; } } if(name[0] == 0){ DBG (15, "sane_open: no device requested, using default\n"); s = scanner_devList; } else{ DBG (15, "sane_open: device %s requested\n", name); for (dev = scanner_devList; dev; dev = dev->next) { if (strcmp (dev->sane.name, name) == 0 || strcmp (dev->device_name, name) == 0) { /*always allow sanei devname*/ s = dev; break; } } } if (!s) { DBG (5, "sane_open: no device found\n"); return SANE_STATUS_INVAL; } DBG (15, "sane_open: device %s found\n", s->sane.name); *handle = s; /* connect the fd so we can talk to scanner */ ret = connect_fd(s); if(ret != SANE_STATUS_GOOD){ return ret; } DBG (10, "sane_open: finish\n"); return SANE_STATUS_GOOD; } /* * @@ Section 3 - SANE Options functions */ /* * 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 scanner *s = handle; int i; SANE_Option_Descriptor *opt = &s->opt[option]; DBG (20, "sane_get_option_descriptor: %d\n", option); if ((unsigned) option >= NUM_OPTIONS) return NULL; /* "Mode" group -------------------------------------------------------- */ if(option==OPT_STANDARD_GROUP){ opt->name = SANE_NAME_STANDARD; opt->title = SANE_TITLE_STANDARD; opt->desc = SANE_DESC_STANDARD; opt->type = SANE_TYPE_GROUP; opt->constraint_type = SANE_CONSTRAINT_NONE; } /* source */ if(option==OPT_SOURCE){ i=0; if(s->has_flatbed){ s->source_list[i++]=STRING_FLATBED; } if(s->has_adf){ s->source_list[i++]=STRING_ADFFRONT; if(s->has_back){ s->source_list[i++]=STRING_ADFBACK; } if(s->has_duplex){ s->source_list[i++]=STRING_ADFDUPLEX; } } if(s->has_card){ s->source_list[i++]=STRING_CARDFRONT; if(s->has_back){ s->source_list[i++]=STRING_CARDBACK; } if(s->has_duplex){ s->source_list[i++]=STRING_CARDDUPLEX; } } 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; } /* scan mode */ if(option==OPT_MODE){ i=0; if(s->can_monochrome || s->can_grayscale || s->can_color){ s->mode_list[i++]=STRING_LINEART; } if(s->can_halftone){ s->mode_list[i++]=STRING_HALFTONE; } if(s->can_grayscale || s->can_color){ s->mode_list[i++]=STRING_GRAYSCALE; } if(s->can_color){ s->mode_list[i++]=STRING_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; } /* resolution */ /* some scanners only support fixed res * build a list of possible choices */ /* we actually only look at the y resolution choices, * and interpolate the image data as required for limited x resolutions */ if(option==OPT_RES){ i=0; if(s->std_res_y[DPI_60] && s->max_y_res >= 60 && s->min_y_res <= 60){ s->res_list[++i] = 60; } if(s->std_res_y[DPI_75] && s->max_y_res >= 75 && s->min_y_res <= 75){ s->res_list[++i] = 75; } if(s->std_res_y[DPI_100] && s->max_y_res >= 100 && s->min_y_res <= 100){ s->res_list[++i] = 100; } if(s->std_res_y[DPI_120] && s->max_y_res >= 120 && s->min_y_res <= 120){ s->res_list[++i] = 120; } if(s->std_res_y[DPI_150] && s->max_y_res >= 150 && s->min_y_res <= 150){ s->res_list[++i] = 150; } if(s->std_res_y[DPI_160] && s->max_y_res >= 160 && s->min_y_res <= 160){ s->res_list[++i] = 160; } if(s->std_res_y[DPI_180] && s->max_y_res >= 180 && s->min_y_res <= 180){ s->res_list[++i] = 180; } if(s->std_res_y[DPI_200] && s->max_y_res >= 200 && s->min_y_res <= 200){ s->res_list[++i] = 200; } if(s->std_res_y[DPI_240] && s->max_y_res >= 240 && s->min_y_res <= 240){ s->res_list[++i] = 240; } if(s->std_res_y[DPI_300] && s->max_y_res >= 300 && s->min_y_res <= 300){ s->res_list[++i] = 300; } if(s->std_res_y[DPI_320] && s->max_y_res >= 320 && s->min_y_res <= 320){ s->res_list[++i] = 320; } if(s->std_res_y[DPI_400] && s->max_y_res >= 400 && s->min_y_res <= 400){ s->res_list[++i] = 400; } if(s->std_res_y[DPI_480] && s->max_y_res >= 480 && s->min_y_res <= 480){ s->res_list[++i] = 480; } if(s->std_res_y[DPI_600] && s->max_y_res >= 600 && s->min_y_res <= 600){ s->res_list[++i] = 600; } if(s->std_res_y[DPI_800] && s->max_y_res >= 800 && s->min_y_res <= 800){ s->res_list[++i] = 800; } if(s->std_res_y[DPI_1200] && s->max_y_res >= 1200 && s->min_y_res <= 1200){ s->res_list[++i] = 1200; } s->res_list[0] = i; opt->name = SANE_NAME_SCAN_RESOLUTION; opt->title = SANE_TITLE_SCAN_RESOLUTION; opt->desc = SANE_DESC_SCAN_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->res_range.min = s->min_y_res; s->res_range.max = s->max_y_res; s->res_range.quant = s->step_y_res; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->res_range; } else{ opt->constraint_type = SANE_CONSTRAINT_WORD_LIST; opt->constraint.word_list = s->res_list; } } /* "Geometry" group ---------------------------------------------------- */ if(option==OPT_GEOMETRY_GROUP){ opt->name = SANE_NAME_GEOMETRY; opt->title = SANE_TITLE_GEOMETRY; opt->desc = SANE_DESC_GEOMETRY; opt->type = SANE_TYPE_GROUP; opt->constraint_type = SANE_CONSTRAINT_NONE; } /* 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(get_page_width(s)); 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; } /* 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(get_page_height(s)); 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; } /* 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(get_page_width(s)); 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; } /* 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(get_page_height(s)); 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; } /* 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->valid_x); s->paper_x_range.quant = MM_PER_UNIT_FIX; opt->name = SANE_NAME_PAGE_WIDTH; opt->title = SANE_TITLE_PAGE_WIDTH; opt->desc = SANE_DESC_PAGE_WIDTH; 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->has_adf || s->has_card){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(s->u.source == SOURCE_FLATBED){ opt->cap |= SANE_CAP_INACTIVE; } } else{ opt->cap = SANE_CAP_INACTIVE; } } /* 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 = SANE_NAME_PAGE_HEIGHT; opt->title = SANE_TITLE_PAGE_HEIGHT; opt->desc = SANE_DESC_PAGE_HEIGHT; 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->has_adf || s->has_card){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(s->u.source == SOURCE_FLATBED){ opt->cap |= SANE_CAP_INACTIVE; } } else{ opt->cap = SANE_CAP_INACTIVE; } } /* "Enhancement" group ------------------------------------------------- */ if(option==OPT_ENHANCEMENT_GROUP){ opt->name = SANE_NAME_ENHANCEMENT; opt->title = SANE_TITLE_ENHANCEMENT; opt->desc = SANE_DESC_ENHANCEMENT; opt->type = SANE_TYPE_GROUP; opt->constraint_type = SANE_CONSTRAINT_NONE; } /* 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.quant=1; /* some have hardware brightness (always 0 to 255?) */ /* some use LUT or GT (-127 to +127)*/ if (s->brightness_steps){ s->brightness_range.min=-127; s->brightness_range.max=127; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } else{ opt->cap = SANE_CAP_INACTIVE; } } /* 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.quant=1; /* some have hardware contrast (always 0 to 255?) */ /* some use LUT or GT (-127 to +127)*/ if (s->contrast_steps){ s->contrast_range.min=-127; s->contrast_range.max=127; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } else { opt->cap = SANE_CAP_INACTIVE; } } /*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){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(s->u.mode != MODE_LINEART){ opt->cap |= SANE_CAP_INACTIVE; } } else { opt->cap = SANE_CAP_INACTIVE; } } 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; } /* "Advanced" group ------------------------------------------------------ */ if(option==OPT_ADVANCED_GROUP){ opt->name = SANE_NAME_ADVANCED; opt->title = SANE_TITLE_ADVANCED; opt->desc = SANE_DESC_ADVANCED; opt->type = SANE_TYPE_GROUP; opt->constraint_type = SANE_CONSTRAINT_NONE; } /*image compression*/ if(option==OPT_COMPRESS){ i=0; s->compress_list[i++]=STRING_NONE; if(s->has_comp_JPEG){ #ifndef SANE_JPEG_DISABLED s->compress_list[i++]=STRING_JPEG; #endif } s->compress_list[i]=NULL; opt->name = "compression"; opt->title = "Compression"; opt->desc = "Enable compressed data. May crash your front-end program"; opt->type = SANE_TYPE_STRING; opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; opt->constraint.string_list = s->compress_list; opt->size = maxStringSize (opt->constraint.string_list); if (i > 1){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if ( must_downsample(s) || s->s.mode < MODE_GRAYSCALE ){ opt->cap |= SANE_CAP_INACTIVE; } } else opt->cap = SANE_CAP_INACTIVE; } /*image compression arg*/ if(option==OPT_COMPRESS_ARG){ opt->name = "compression-arg"; opt->title = "Compression argument"; opt->desc = "Level of JPEG compression. 1 is small file, 100 is large file."; opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->compress_arg_range; s->compress_arg_range.quant=1; if(s->has_comp_JPEG){ s->compress_arg_range.min=0; s->compress_arg_range.max=100; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(s->compress != COMP_JPEG){ opt->cap |= SANE_CAP_INACTIVE; } } else opt->cap = SANE_CAP_INACTIVE; } /*double feed by length*/ if(option==OPT_DF_LENGTH){ opt->name = "df-length"; opt->title = "DF by length"; opt->desc = "Detect double feeds by comparing document lengths"; opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_NONE; if (1) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } /*double feed by thickness */ if(option==OPT_DF_THICKNESS){ opt->name = "df-thickness"; opt->title = "DF by thickness"; opt->desc = "Detect double feeds using thickness sensor"; opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_NONE; if (1){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; } else opt->cap = SANE_CAP_INACTIVE; } /*deskew by roller*/ if(option==OPT_ROLLERDESKEW){ opt->name = "rollerdeskew"; opt->title = "Roller deskew"; opt->desc = "Request scanner to correct skewed pages mechanically"; opt->type = SANE_TYPE_BOOL; if (1) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } /*deskew by software*/ if(option==OPT_SWDESKEW){ opt->name = "swdeskew"; opt->title = "Software deskew"; opt->desc = "Request driver to rotate skewed pages digitally"; opt->type = SANE_TYPE_BOOL; if (1) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } /*software despeckle radius*/ if(option==OPT_SWDESPECK){ opt->name = "swdespeck"; opt->title = "Software despeckle diameter"; opt->desc = "Maximum diameter of lone dots to remove from scan"; opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->swdespeck_range; s->swdespeck_range.quant=1; if(1){ s->swdespeck_range.min=0; s->swdespeck_range.max=9; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } else opt->cap = SANE_CAP_INACTIVE; } /*crop by software*/ if(option==OPT_SWCROP){ opt->name = "swcrop"; opt->title = "Software crop"; opt->desc = "Request driver to remove border from pages digitally"; opt->type = SANE_TYPE_BOOL; if (1) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } /* Software blank page skip */ if(option==OPT_SWSKIP){ opt->name = "swskip"; opt->title = SANE_I18N ("Software blank skip percentage"); opt->desc = SANE_I18N("Request driver to discard pages with low percentage of dark pixels"); opt->type = SANE_TYPE_FIXED; opt->unit = SANE_UNIT_PERCENT; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->swskip_range; s->swskip_range.quant=SANE_FIX(0.10001); s->swskip_range.min=SANE_FIX(0); s->swskip_range.max=SANE_FIX(100); opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } /*staple detection*/ if(option==OPT_STAPLEDETECT){ opt->name = "stapledetect"; opt->title = "Staple detect"; opt->desc = "Request scanner to halt if stapled pages are detected"; opt->type = SANE_TYPE_BOOL; if (1) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } /*dropout color front*/ if(option==OPT_DROPOUT_COLOR_F){ s->do_color_list[0] = STRING_NONE; s->do_color_list[1] = STRING_RED; s->do_color_list[2] = STRING_GREEN; s->do_color_list[3] = STRING_BLUE; s->do_color_list[4] = STRING_EN_RED; s->do_color_list[5] = STRING_EN_GREEN; s->do_color_list[6] = STRING_EN_BLUE; s->do_color_list[7] = NULL; opt->name = "dropout-front"; opt->title = "Dropout color front"; 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 (1){ opt->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_ADVANCED; if(s->u.mode == MODE_COLOR) opt->cap |= SANE_CAP_INACTIVE; } else opt->cap = SANE_CAP_INACTIVE; } /*dropout color back*/ if(option==OPT_DROPOUT_COLOR_B){ s->do_color_list[0] = STRING_NONE; s->do_color_list[1] = STRING_RED; s->do_color_list[2] = STRING_GREEN; s->do_color_list[3] = STRING_BLUE; s->do_color_list[4] = STRING_EN_RED; s->do_color_list[5] = STRING_EN_GREEN; s->do_color_list[6] = STRING_EN_BLUE; s->do_color_list[7] = NULL; opt->name = "dropout-back"; opt->title = "Dropout color back"; 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 (1){ opt->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_ADVANCED; if(s->u.mode == MODE_COLOR) opt->cap |= SANE_CAP_INACTIVE; } else opt->cap = SANE_CAP_INACTIVE; } /*buffer mode*/ if(option==OPT_BUFFERMODE){ opt->name = "buffermode"; opt->title = "Buffer mode"; opt->desc = "Request scanner to read pages async into internal memory"; opt->type = SANE_TYPE_BOOL; if (s->has_buffer) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_SIDE){ opt->name = "side"; opt->title = "Duplex side"; opt->desc = "Tells which side (0=front, 1=back) of a duplex scan the next call to sane_read will return."; opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; opt->size = sizeof(SANE_Word); opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; opt->constraint_type = SANE_CONSTRAINT_NONE; } /*hardware crop*/ if(option==OPT_HW_CROP){ opt->name = "hwcrop"; opt->title = "Hardware crop"; opt->desc = "Request scanner to crop image automatically"; opt->type = SANE_TYPE_BOOL; if (s->has_hwcrop) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } /* "Imprinter" group --------------------------------------------------- */ if(option==OPT_IMPRINT_GROUP){ opt->name = "imprinter-options"; opt->title = SANE_I18N ("Imprinter Options"); opt->desc = SANE_I18N ("Controls for imprinter units"); opt->type = SANE_TYPE_GROUP; opt->constraint_type = SANE_CONSTRAINT_NONE; /*flaming hack to get scanimage to hide group*/ if ( !(s->has_pre_imprinter || s->has_post_imprinter) ) opt->type = SANE_TYPE_BOOL; } if(option==OPT_PRE_IMPRINT_SPECSTRING){ opt->name = "pre-imprint-string"; opt->title = "Pre-Imprinter string"; opt->desc = "String specifier for the pre-imprinter text"; opt->type = SANE_TYPE_STRING; opt->size = IMPRINT_SPECSTRING_LEN; if (s->has_pre_imprinter) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_PRE_IMPRINT_H_OFFSET){ opt->name = "pre-imprint-h-offset"; opt->title = "Pre-Imprinter horizontal offset"; opt->desc = "Integer specifying the horizontal positioning of the pre-imprinter"; opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_MM; opt->size = sizeof(SANE_Word); opt->constraint_type = SANE_CONSTRAINT_WORD_LIST; opt->constraint.word_list = s->pre_imprinter_h_offset_list; if (s->has_pre_imprinter) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_PRE_IMPRINT_V_OFFSET){ opt->name = "pre-imprint-v-offset"; opt->title = "Pre-Imprinter vertical offset"; opt->desc = "Integer specifying the vertical positioning of the pre-imprinter"; opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_MM; opt->size = sizeof(SANE_Word); opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->imprinter_v_offset_range; if (s->has_pre_imprinter) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_PRE_IMPRINT_FONT_SIZE){ opt->name = "pre-imprint-font-size"; opt->title = "Pre-Imprinter font size"; opt->desc = "Integer specifying the pre-imprint font size"; opt->type = SANE_TYPE_STRING; opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; opt->constraint.string_list = s->imprint_font_size_list; opt->size = maxStringSize (opt->constraint.string_list); if (s->has_pre_imprinter) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_PRE_IMPRINT_FONT_ROT){ opt->name = "pre-imprint-font-rot"; opt->title = "Pre-Imprinter font rotation"; opt->desc = "Integer specifying the pre-imprint font rotation"; opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_NONE; opt->size = sizeof(SANE_Word); opt->constraint_type = SANE_CONSTRAINT_WORD_LIST; opt->constraint.word_list = s->imprinter_font_angle_list; if (s->has_pre_imprinter) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_PRE_IMPRINT_SPACING){ opt->name = "pre-imprint-spacing"; opt->title = "Pre-Imprinter spacing"; opt->desc = "Enables the pre-imprint extra spacing"; opt->type = SANE_TYPE_BOOL; if (s->has_pre_imprinter) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_POST_IMPRINT_SPECSTRING){ opt->name = "post-imprint-string"; opt->title = "Post-Imprinter string"; opt->desc = "String specifier for the post-imprinter text"; opt->type = SANE_TYPE_STRING; opt->size = IMPRINT_SPECSTRING_LEN; if (s->has_post_imprinter) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_POST_IMPRINT_H_OFFSET){ opt->name = "post-imprint-h-offset"; opt->title = "Post-Imprinter horizontal offset"; opt->desc = "Integer specifying the horizontal positioning of the post-imprinter"; opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_MM; opt->size = sizeof(SANE_Word); opt->constraint_type = SANE_CONSTRAINT_WORD_LIST; opt->constraint.word_list = s->post_imprinter_h_offset_list; if (s->has_post_imprinter) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_POST_IMPRINT_V_OFFSET){ opt->name = "post-imprint-v-offset"; opt->title = "Post-Imprinter vertical offset"; opt->desc = "Integer specifying the vertical positioning of the post-imprinter"; opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_MM; opt->size = sizeof(SANE_Word); opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->imprinter_v_offset_range; if (s->has_post_imprinter) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_POST_IMPRINT_FONT_SIZE){ opt->name = "post-imprint-font-size"; opt->title = "Post-Imprinter font size"; opt->desc = "Integer specifying the post-imprint font size"; opt->type = SANE_TYPE_STRING; opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; opt->constraint.string_list = s->imprint_font_size_list; opt->size = maxStringSize (opt->constraint.string_list); if (s->has_post_imprinter) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_POST_IMPRINT_FONT_ROT){ opt->name = "post-imprint-font-rot"; opt->title = "Post-Imprinter font rotation"; opt->desc = "Integer specifying the post-imprint font rotation"; opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_NONE; opt->size = sizeof(SANE_Word); opt->constraint_type = SANE_CONSTRAINT_WORD_LIST; opt->constraint.word_list = s->imprinter_font_angle_list; if (s->has_post_imprinter) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_POST_IMPRINT_SPACING){ opt->name = "post-imprint-spacing"; opt->title = "Post-Imprinter spacing"; opt->desc = "Enables the post-imprint extra spacing"; opt->type = SANE_TYPE_BOOL; if (s->has_post_imprinter) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_POST_IMPRINT_ADDON_MODE){ opt->name = "post-imprint-addon-mode"; opt->title = "Post-Imprinter addon mode"; opt->desc = "Integer specifying the type of post-imprint addon rendered in the scanned image"; opt->type = SANE_TYPE_STRING; opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; opt->constraint.string_list = s->imprint_addon_mode_list; opt->size = maxStringSize (opt->constraint.string_list); if (s->has_post_imprinter) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } /* "Sensor" group ------------------------------------------------------ */ if(option==OPT_SENSOR_GROUP){ opt->name = SANE_NAME_SENSORS; opt->title = SANE_TITLE_SENSORS; opt->desc = SANE_DESC_SENSORS; opt->type = SANE_TYPE_GROUP; opt->constraint_type = SANE_CONSTRAINT_NONE; } if(option==OPT_START){ opt->name = "start"; opt->title = "Start/1 button"; opt->desc = "Big green or small 1 button"; opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; if(!s->can_read_panel) opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_STOP){ opt->name = "stop"; opt->title = "Stop/2 button"; opt->desc = "Small orange or small 2 button"; opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; if(!s->can_read_panel) opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_BUTT3){ opt->name = "button-3"; opt->title = "3 button"; opt->desc = "Small 3 button"; opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; if(!s->can_read_panel) opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_NEWFILE){ opt->name = "newfile"; opt->title = "New File button"; opt->desc = "New File button"; opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; if(!s->can_read_panel) opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_COUNTONLY){ opt->name = "countonly"; opt->title = "Count Only button"; opt->desc = "Count Only button"; opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; if(!s->can_read_panel) opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_BYPASSMODE){ opt->name = "bypassmode"; opt->title = "Bypass Mode button"; opt->desc = "Bypass Mode button"; opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; if(!s->can_read_panel) opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_COUNTER){ opt->name = "counter"; opt->title = "Counter"; opt->desc = "Scan counter"; opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->counter_range; s->counter_range.min=0; s->counter_range.max=500; s->counter_range.quant=1; if (s->can_read_panel && s->has_counter) opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_ROLLERCOUNTER){ opt->name = "roller-counter"; opt->title = "Roller Counter"; opt->desc = "Scans since last roller replacement"; opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_NONE; if (s->can_read_lifecycle_counters) opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_TOTALCOUNTER){ opt->name = "total-counter"; opt->title = "Total Counter"; opt->desc = "Total scan count of the device"; opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_NONE; if (s->can_read_lifecycle_counters) opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_ADF_LOADED){ opt->name = "adf-loaded"; opt->title = "ADF Loaded"; opt->desc = "Paper available in ADF input hopper"; opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; if(!s->can_read_sensors) opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_CARD_LOADED){ opt->name = "card-loaded"; opt->title = "Card Loaded"; opt->desc = "Paper available in card reader"; opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; if(!s->can_read_sensors || !s->has_card) opt->cap = SANE_CAP_INACTIVE; } return opt; } /** * 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 scanner *s = (struct scanner *) handle; SANE_Int dummy = 0; SANE_Status ret = SANE_STATUS_GOOD; /* Make sure that all those statements involving *info cannot break (better * than having to do "if (info) ..." everywhere!) */ if (info == 0) info = &dummy; if (option >= NUM_OPTIONS) { DBG (5, "sane_control_option: %d too big\n", option); return SANE_STATUS_INVAL; } if (!SANE_OPTION_IS_ACTIVE (s->opt[option].cap)) { DBG (5, "sane_control_option: %d inactive\n", option); return SANE_STATUS_INVAL; } /* * 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; DBG (20, "sane_control_option: get value for '%s' (%d)\n", s->opt[option].name,option); switch (option) { case OPT_NUM_OPTS: *val_p = NUM_OPTIONS; return SANE_STATUS_GOOD; case OPT_SOURCE: if(s->u.source == SOURCE_FLATBED){ strcpy (val, STRING_FLATBED); } else if(s->u.source == SOURCE_ADF_FRONT){ strcpy (val, STRING_ADFFRONT); } else if(s->u.source == SOURCE_ADF_BACK){ strcpy (val, STRING_ADFBACK); } else if(s->u.source == SOURCE_ADF_DUPLEX){ strcpy (val, STRING_ADFDUPLEX); } else if(s->u.source == SOURCE_CARD_FRONT){ strcpy (val, STRING_CARDFRONT); } else if(s->u.source == SOURCE_CARD_BACK){ strcpy (val, STRING_CARDBACK); } else if(s->u.source == SOURCE_CARD_DUPLEX){ strcpy (val, STRING_CARDDUPLEX); } return SANE_STATUS_GOOD; case OPT_MODE: if(s->u.mode == MODE_LINEART){ strcpy (val, STRING_LINEART); } else if(s->u.mode == MODE_HALFTONE){ strcpy (val, STRING_HALFTONE); } else if(s->u.mode == MODE_GRAYSCALE){ strcpy (val, STRING_GRAYSCALE); } else if(s->u.mode == MODE_COLOR){ strcpy (val, STRING_COLOR); } return SANE_STATUS_GOOD; case OPT_RES: *val_p = s->u.dpi_x; return SANE_STATUS_GOOD; case OPT_TL_X: *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u.tl_x); return SANE_STATUS_GOOD; case OPT_TL_Y: *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u.tl_y); return SANE_STATUS_GOOD; case OPT_BR_X: *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u.br_x); return SANE_STATUS_GOOD; case OPT_BR_Y: *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u.br_y); return SANE_STATUS_GOOD; case OPT_PAGE_WIDTH: *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u.page_x); return SANE_STATUS_GOOD; case OPT_PAGE_HEIGHT: *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u.page_y); return SANE_STATUS_GOOD; case OPT_BRIGHTNESS: *val_p = s->brightness; return SANE_STATUS_GOOD; case OPT_CONTRAST: *val_p = s->contrast; return SANE_STATUS_GOOD; case OPT_THRESHOLD: *val_p = s->threshold; return SANE_STATUS_GOOD; case OPT_RIF: *val_p = s->rif; return SANE_STATUS_GOOD; /* Advanced Group */ case OPT_COMPRESS: if(s->compress == COMP_JPEG){ strcpy (val, STRING_JPEG); } else{ strcpy (val, STRING_NONE); } return SANE_STATUS_GOOD; case OPT_COMPRESS_ARG: *val_p = s->compress_arg; return SANE_STATUS_GOOD; case OPT_DF_LENGTH: *val_p = s->df_length; return SANE_STATUS_GOOD; case OPT_DF_THICKNESS: *val_p = s->df_thickness; return SANE_STATUS_GOOD; case OPT_ROLLERDESKEW: *val_p = s->rollerdeskew; return SANE_STATUS_GOOD; case OPT_SWDESKEW: *val_p = s->swdeskew; return SANE_STATUS_GOOD; case OPT_SWDESPECK: *val_p = s->swdespeck; return SANE_STATUS_GOOD; case OPT_SWCROP: *val_p = s->swcrop; return SANE_STATUS_GOOD; case OPT_SWSKIP: *val_p = SANE_FIX(s->swskip); return SANE_STATUS_GOOD; case OPT_STAPLEDETECT: *val_p = s->stapledetect; return SANE_STATUS_GOOD; case OPT_DROPOUT_COLOR_F: switch (s->dropout_color[SIDE_FRONT]) { case COLOR_NONE: strcpy (val, STRING_NONE); break; case COLOR_RED: strcpy (val, STRING_RED); break; case COLOR_GREEN: strcpy (val, STRING_GREEN); break; case COLOR_BLUE: strcpy (val, STRING_BLUE); break; case COLOR_EN_RED: strcpy (val, STRING_EN_RED); break; case COLOR_EN_GREEN: strcpy (val, STRING_EN_GREEN); break; case COLOR_EN_BLUE: strcpy (val, STRING_EN_BLUE); break; } return SANE_STATUS_GOOD; case OPT_DROPOUT_COLOR_B: switch (s->dropout_color[SIDE_BACK]) { case COLOR_NONE: strcpy (val, STRING_NONE); break; case COLOR_RED: strcpy (val, STRING_RED); break; case COLOR_GREEN: strcpy (val, STRING_GREEN); break; case COLOR_BLUE: strcpy (val, STRING_BLUE); break; case COLOR_EN_RED: strcpy (val, STRING_EN_RED); break; case COLOR_EN_GREEN: strcpy (val, STRING_EN_GREEN); break; case COLOR_EN_BLUE: strcpy (val, STRING_EN_BLUE); break; } return SANE_STATUS_GOOD; case OPT_BUFFERMODE: *val_p = s->buffermode; return SANE_STATUS_GOOD; case OPT_SIDE: *val_p = s->side; return SANE_STATUS_GOOD; case OPT_HW_CROP: *val_p = s->hwcrop; return SANE_STATUS_GOOD; case OPT_PRE_IMPRINT_SPECSTRING: strcpy(val, s->pre_imprint.specstring); return SANE_STATUS_GOOD; case OPT_PRE_IMPRINT_H_OFFSET: *val_p = s->pre_imprint.h_offset; return SANE_STATUS_GOOD; case OPT_PRE_IMPRINT_V_OFFSET: *val_p = s->pre_imprint.v_offset; return SANE_STATUS_GOOD; case OPT_PRE_IMPRINT_FONT_SIZE: switch (s->pre_imprint.font_size){ case IMPRINTER_12x12_FONT: strcpy(val, STRING_IMPRINTER_12x12_FONT); break; case IMPRINTER_8x12_FONT: strcpy(val, STRING_IMPRINTER_8x12_FONT); break; } return SANE_STATUS_GOOD; case OPT_PRE_IMPRINT_FONT_ROT: switch (s->pre_imprint.font_rot){ case IMPRINTER_0_FONT_ROT: *val_p = 0; break; case IMPRINTER_90_FONT_ROT: *val_p = 90; break; case IMPRINTER_180_FONT_ROT: *val_p = 180; break; case IMPRINTER_270_FONT_ROT: *val_p = 270; break; } return SANE_STATUS_GOOD; case OPT_PRE_IMPRINT_SPACING: *val_p = s->pre_imprint.spacing; return SANE_STATUS_GOOD; case OPT_POST_IMPRINT_SPECSTRING: strcpy(val, s->post_imprint.specstring); return SANE_STATUS_GOOD; case OPT_POST_IMPRINT_H_OFFSET: *val_p = s->post_imprint.h_offset; return SANE_STATUS_GOOD; case OPT_POST_IMPRINT_V_OFFSET: *val_p = s->post_imprint.v_offset; return SANE_STATUS_GOOD; case OPT_POST_IMPRINT_FONT_SIZE: switch (s->post_imprint.font_size){ case IMPRINTER_12x12_FONT: strcpy(val, STRING_IMPRINTER_12x12_FONT); break; case IMPRINTER_8x12_FONT: strcpy(val, STRING_IMPRINTER_8x12_FONT); break; } return SANE_STATUS_GOOD; case OPT_POST_IMPRINT_FONT_ROT: switch (s->post_imprint.font_rot){ case IMPRINTER_0_FONT_ROT: *val_p = 0; break; case IMPRINTER_90_FONT_ROT: *val_p = 90; break; case IMPRINTER_180_FONT_ROT: *val_p = 180; break; case IMPRINTER_270_FONT_ROT: *val_p = 270; break; } return SANE_STATUS_GOOD; case OPT_POST_IMPRINT_SPACING: *val_p = s->post_imprint.spacing; return SANE_STATUS_GOOD; case OPT_POST_IMPRINT_ADDON_MODE: switch (s->post_imprint_addon_mode){ case ADDON_BoW: strcpy(val, STRING_IMPRINTER_ADDON_BoW); break; case ADDON_BoI: strcpy(val, STRING_IMPRINTER_ADDON_BoI); break; case ADDON_WoB: strcpy(val, STRING_IMPRINTER_ADDON_WoB); break; case ADDON_DISABLED: strcpy(val, STRING_NONE); break; } return SANE_STATUS_GOOD; /* Sensor Group */ case OPT_START: ret = read_panel(s,OPT_START); *val_p = s->panel_start; return ret; case OPT_STOP: ret = read_panel(s,OPT_STOP); *val_p = s->panel_stop; return ret; case OPT_BUTT3: ret = read_panel(s,OPT_BUTT3); *val_p = s->panel_butt3; return ret; case OPT_NEWFILE: ret = read_panel(s,OPT_NEWFILE); *val_p = s->panel_new_file; return ret; case OPT_COUNTONLY: ret = read_panel(s,OPT_COUNTONLY); *val_p = s->panel_count_only; return ret; case OPT_BYPASSMODE: ret = read_panel(s,OPT_BYPASSMODE); *val_p = s->panel_bypass_mode; return ret; case OPT_COUNTER: ret = read_panel(s,OPT_COUNTER); *val_p = s->panel_counter; return ret; case OPT_ROLLERCOUNTER: ret = read_counters(s); *val_p = s->roller_counter; return ret; case OPT_TOTALCOUNTER: ret = read_counters(s); *val_p = s->total_counter; return ret; case OPT_ADF_LOADED: ret = read_sensors(s,OPT_ADF_LOADED); *val_p = s->sensor_adf_loaded; return ret; case OPT_CARD_LOADED: ret = read_sensors(s,OPT_CARD_LOADED); *val_p = s->sensor_card_loaded; return ret; } } 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: can't set, device busy\n"); return SANE_STATUS_DEVICE_BUSY; } 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; } /* may have been changed by constrain, so don't copy until now */ val_c = *(SANE_Word *)val; /* * 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) { /* Mode Group */ case OPT_SOURCE: if (!strcmp (val, STRING_ADFFRONT)) { tmp = SOURCE_ADF_FRONT; } else if (!strcmp (val, STRING_ADFBACK)) { tmp = SOURCE_ADF_BACK; } else if (!strcmp (val, STRING_ADFDUPLEX)) { tmp = SOURCE_ADF_DUPLEX; } else if (!strcmp (val, STRING_CARDFRONT)) { tmp = SOURCE_CARD_FRONT; } else if (!strcmp (val, STRING_CARDBACK)) { tmp = SOURCE_CARD_BACK; } else if (!strcmp (val, STRING_CARDDUPLEX)) { tmp = SOURCE_CARD_DUPLEX; } else{ tmp = SOURCE_FLATBED; } if (s->u.source == tmp) return SANE_STATUS_GOOD; s->u.source = tmp; *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_MODE: if (!strcmp (val, STRING_LINEART)) { tmp = MODE_LINEART; } else if (!strcmp (val, STRING_HALFTONE)) { tmp = MODE_HALFTONE; } else if (!strcmp (val, STRING_GRAYSCALE)) { tmp = MODE_GRAYSCALE; } else{ tmp = MODE_COLOR; } if (tmp == s->u.mode) return SANE_STATUS_GOOD; s->u.mode = tmp; *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_RES: if (s->u.dpi_x == val_c && s->u.dpi_y == val_c) return SANE_STATUS_GOOD; s->u.dpi_x = val_c; s->u.dpi_y = val_c; *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; /* Geometry Group */ case OPT_TL_X: if (s->u.tl_x == FIXED_MM_TO_SCANNER_UNIT(val_c)) return SANE_STATUS_GOOD; s->u.tl_x = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_TL_Y: if (s->u.tl_y == FIXED_MM_TO_SCANNER_UNIT(val_c)) return SANE_STATUS_GOOD; s->u.tl_y = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_BR_X: if (s->u.br_x == FIXED_MM_TO_SCANNER_UNIT(val_c)) return SANE_STATUS_GOOD; s->u.br_x = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_BR_Y: if (s->u.br_y == FIXED_MM_TO_SCANNER_UNIT(val_c)) return SANE_STATUS_GOOD; s->u.br_y = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_PAGE_WIDTH: if (s->u.page_x == FIXED_MM_TO_SCANNER_UNIT(val_c)) return SANE_STATUS_GOOD; /* if full width image, and paper size is changed, change the image size to match new paper */ if (s->u.tl_x == 0 && s->u.br_x == s->u.page_x){ DBG (20, "sane_control_option: br_x tracking page_width\n"); s->u.br_x = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_PARAMS; } s->u.page_x = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_PAGE_HEIGHT: if (s->u.page_y == FIXED_MM_TO_SCANNER_UNIT(val_c)) return SANE_STATUS_GOOD; /* if full height image, and paper size is changed, change the image size to match new paper */ if (s->u.tl_y == 0 && s->u.br_y == s->u.page_y){ DBG (20, "sane_control_option: br_y tracking page_height\n"); s->u.br_y = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_PARAMS; } s->u.page_y = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; /* Enhancement Group */ case OPT_BRIGHTNESS: s->brightness = val_c; return SANE_STATUS_GOOD; case OPT_CONTRAST: s->contrast = val_c; return SANE_STATUS_GOOD; case OPT_THRESHOLD: s->threshold = val_c; return SANE_STATUS_GOOD; case OPT_RIF: s->rif = val_c; return SANE_STATUS_GOOD; /* Advanced Group */ case OPT_COMPRESS: if (!strcmp (val, STRING_JPEG)) { s->compress = COMP_JPEG; } else{ s->compress = COMP_NONE; } return SANE_STATUS_GOOD; case OPT_COMPRESS_ARG: s->compress_arg = val_c; return SANE_STATUS_GOOD; case OPT_DF_LENGTH: s->df_length = val_c; return SANE_STATUS_GOOD; case OPT_DF_THICKNESS: s->df_thickness = val_c; return SANE_STATUS_GOOD; case OPT_ROLLERDESKEW: s->rollerdeskew = val_c; return SANE_STATUS_GOOD; case OPT_SWDESKEW: s->swdeskew = val_c; return SANE_STATUS_GOOD; case OPT_SWDESPECK: s->swdespeck = val_c; return SANE_STATUS_GOOD; case OPT_SWCROP: s->swcrop = val_c; return SANE_STATUS_GOOD; case OPT_SWSKIP: s->swskip = SANE_UNFIX(val_c); return SANE_STATUS_GOOD; case OPT_STAPLEDETECT: s->stapledetect = val_c; return SANE_STATUS_GOOD; case OPT_DROPOUT_COLOR_F: if (!strcmp(val, STRING_NONE)) s->dropout_color[SIDE_FRONT] = COLOR_NONE; else if (!strcmp(val, STRING_RED)) s->dropout_color[SIDE_FRONT] = COLOR_RED; else if (!strcmp(val, STRING_GREEN)) s->dropout_color[SIDE_FRONT] = COLOR_GREEN; else if (!strcmp(val, STRING_BLUE)) s->dropout_color[SIDE_FRONT] = COLOR_BLUE; else if (!strcmp(val, STRING_EN_RED)) s->dropout_color[SIDE_FRONT] = COLOR_EN_RED; else if (!strcmp(val, STRING_EN_GREEN)) s->dropout_color[SIDE_FRONT] = COLOR_EN_GREEN; else if (!strcmp(val, STRING_EN_BLUE)) s->dropout_color[SIDE_FRONT] = COLOR_EN_BLUE; return SANE_STATUS_GOOD; case OPT_DROPOUT_COLOR_B: if (!strcmp(val, STRING_NONE)) s->dropout_color[SIDE_BACK] = COLOR_NONE; else if (!strcmp(val, STRING_RED)) s->dropout_color[SIDE_BACK] = COLOR_RED; else if (!strcmp(val, STRING_GREEN)) s->dropout_color[SIDE_BACK] = COLOR_GREEN; else if (!strcmp(val, STRING_BLUE)) s->dropout_color[SIDE_BACK] = COLOR_BLUE; else if (!strcmp(val, STRING_EN_RED)) s->dropout_color[SIDE_BACK] = COLOR_EN_RED; else if (!strcmp(val, STRING_EN_GREEN)) s->dropout_color[SIDE_BACK] = COLOR_EN_GREEN; else if (!strcmp(val, STRING_EN_BLUE)) s->dropout_color[SIDE_BACK] = COLOR_EN_BLUE; return SANE_STATUS_GOOD; case OPT_BUFFERMODE: s->buffermode = val_c; return SANE_STATUS_GOOD; case OPT_HW_CROP: s->hwcrop = val_c; return SANE_STATUS_GOOD; case OPT_PRE_IMPRINT_SPECSTRING: if (strlen(val) < IMPRINT_SPECSTRING_LEN){ strncpy(s->pre_imprint.specstring, val, IMPRINT_SPECSTRING_LEN); return SANE_STATUS_GOOD; } DBG (5, "sane_control_option: pre-imprint spec string '%s' exceed the limit of %d characters\n", (SANE_String)val, IMPRINT_SPECSTRING_LEN); return SANE_STATUS_INVAL; case OPT_PRE_IMPRINT_H_OFFSET: s->pre_imprint.h_offset = val_c; return SANE_STATUS_GOOD; case OPT_PRE_IMPRINT_V_OFFSET: s->pre_imprint.v_offset = val_c; return SANE_STATUS_GOOD; case OPT_PRE_IMPRINT_FONT_SIZE: if (!strcmp (val, STRING_IMPRINTER_12x12_FONT)) { s->pre_imprint.font_size = IMPRINTER_12x12_FONT; } if (!strcmp (val, STRING_IMPRINTER_8x12_FONT)) { s->pre_imprint.font_size = IMPRINTER_8x12_FONT; } return SANE_STATUS_GOOD; case OPT_PRE_IMPRINT_FONT_ROT: switch (val_c){ case 0: s->pre_imprint.font_rot = IMPRINTER_0_FONT_ROT; break; case 90: s->pre_imprint.font_rot = IMPRINTER_90_FONT_ROT; break; case 180: s->pre_imprint.font_rot = IMPRINTER_180_FONT_ROT; break; case 270: s->pre_imprint.font_rot = IMPRINTER_270_FONT_ROT; break; } return SANE_STATUS_GOOD; case OPT_PRE_IMPRINT_SPACING: s->pre_imprint.spacing = val_c; return SANE_STATUS_GOOD; case OPT_POST_IMPRINT_SPECSTRING: if (strlen(val) < IMPRINT_SPECSTRING_LEN){ strncpy(s->post_imprint.specstring, val, IMPRINT_SPECSTRING_LEN); return SANE_STATUS_GOOD; } DBG (5, "sane_control_option: post-imprint spec string '%s' exceed the limit of %d characters\n", (SANE_String)val, IMPRINT_SPECSTRING_LEN); return SANE_STATUS_INVAL; case OPT_POST_IMPRINT_H_OFFSET: s->post_imprint.h_offset = val_c; return SANE_STATUS_GOOD; case OPT_POST_IMPRINT_V_OFFSET: s->post_imprint.v_offset = val_c; return SANE_STATUS_GOOD; case OPT_POST_IMPRINT_FONT_SIZE: if (!strcmp (val, STRING_IMPRINTER_12x12_FONT)) { s->post_imprint.font_size = IMPRINTER_12x12_FONT; } if (!strcmp (val, STRING_IMPRINTER_8x12_FONT)) { s->post_imprint.font_size = IMPRINTER_8x12_FONT; } return SANE_STATUS_GOOD; case OPT_POST_IMPRINT_FONT_ROT: switch (val_c){ case 0: s->post_imprint.font_rot = IMPRINTER_0_FONT_ROT; break; case 90: s->post_imprint.font_rot = IMPRINTER_90_FONT_ROT; break; case 180: s->post_imprint.font_rot = IMPRINTER_180_FONT_ROT; break; case 270: s->post_imprint.font_rot = IMPRINTER_270_FONT_ROT; break; } return SANE_STATUS_GOOD; case OPT_POST_IMPRINT_SPACING: s->post_imprint.spacing = val_c; return SANE_STATUS_GOOD; case OPT_POST_IMPRINT_ADDON_MODE: if (!strcmp (val, STRING_IMPRINTER_ADDON_BoW)) { s->post_imprint_addon_mode = ADDON_BoW; } if (!strcmp (val, STRING_IMPRINTER_ADDON_BoI)) { s->post_imprint_addon_mode = ADDON_BoI; } if (!strcmp (val, STRING_IMPRINTER_ADDON_WoB)) { s->post_imprint_addon_mode = ADDON_WoB; } if (!strcmp (val, STRING_NONE)) { s->post_imprint_addon_mode = ADDON_DISABLED; } return SANE_STATUS_GOOD; } } /* else */ return SANE_STATUS_INVAL; } static SANE_Status ssm_buffer (struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; DBG (10, "ssm_buffer: start\n"); if(s->has_ssm){ unsigned char cmd[SET_SCAN_MODE_len]; size_t cmdLen = SET_SCAN_MODE_len; unsigned char out[SSM_PAY_len]; size_t outLen = SSM_PAY_len; memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SET_SCAN_MODE_code); set_SSM_pf(cmd, 1); set_SSM_pay_len(cmd, outLen); memset(out,0,outLen); if(s->has_ssm_pay_head_len){ set_SSM_pay_head_len(out, SSM_PAY_HEAD_len); } set_SSM_page_code(out, SM_pc_buffer); set_SSM_page_len(out, SSM_PAGE_len); if(s->s.source == SOURCE_ADF_DUPLEX || s->s.source == SOURCE_CARD_DUPLEX){ set_SSM_BUFF_duplex(out, 1); } if(s->s.source == SOURCE_FLATBED){ set_SSM_BUFF_fb(out, 1); } else if(s->s.source >= SOURCE_CARD_FRONT){ set_SSM_BUFF_card(out, 1); } if(s->buffermode){ set_SSM_BUFF_async(out, 1); } if(0){ set_SSM_BUFF_ald(out, 1); } if(0){ set_SSM_BUFF_unk(out,1); } ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); } else if(s->has_ssm2){ unsigned char cmd[SET_SCAN_MODE2_len]; size_t cmdLen = SET_SCAN_MODE2_len; unsigned char out[SSM2_PAY_len]; size_t outLen = SSM2_PAY_len; memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SET_SCAN_MODE2_code); set_SSM2_page_code(cmd, SM2_pc_buffer); set_SSM2_pay_len(cmd, outLen); memset(out,0,outLen); set_SSM2_BUFF_unk(out, !s->buffermode); set_SSM2_BUFF_unk2(out, 0x40); set_SSM2_BUFF_sync(out, !s->buffermode); ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); } else{ DBG (10, "ssm_buffer: unsupported\n"); } DBG (10, "ssm_buffer: finish\n"); return ret; } static SANE_Status ssm_df (struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; DBG (10, "ssm_df: start\n"); if(!s->has_df){ DBG (10, "ssm_df: unsupported, finishing\n"); return ret; } if(s->has_ssm){ unsigned char cmd[SET_SCAN_MODE_len]; size_t cmdLen = SET_SCAN_MODE_len; unsigned char out[SSM_PAY_len]; size_t outLen = SSM_PAY_len; memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SET_SCAN_MODE_code); set_SSM_pf(cmd, 1); set_SSM_pay_len(cmd, outLen); memset(out,0,outLen); if(s->has_ssm_pay_head_len){ set_SSM_pay_head_len(out, SSM_PAY_HEAD_len); } set_SSM_page_code(out, SM_pc_df); set_SSM_page_len(out, SSM_PAGE_len); /* deskew by roller */ if(s->rollerdeskew){ set_SSM_DF_deskew_roll(out, 1); } /* staple detection */ if(s->stapledetect){ set_SSM_DF_staple(out, 1); } /* thickness */ if(s->df_thickness){ set_SSM_DF_thick(out, 1); } /* length */ if(s->df_length){ set_SSM_DF_len(out, 1); } ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); } else if(s->has_ssm2){ unsigned char cmd[SET_SCAN_MODE2_len]; size_t cmdLen = SET_SCAN_MODE2_len; unsigned char out[SSM2_PAY_len]; size_t outLen = SSM2_PAY_len; /* send ultrasonic offsets first */ if(s->df_thickness && s->has_df_ultra){ memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SET_SCAN_MODE2_code); set_SSM2_page_code(cmd, SM2_pc_ultra); set_SSM2_pay_len(cmd, outLen); memset(out,0,outLen); set_SSM2_ULTRA_top(out, 0); set_SSM2_ULTRA_bot(out, 0); ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); } memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SET_SCAN_MODE2_code); set_SSM2_page_code(cmd, SM2_pc_df); set_SSM2_pay_len(cmd, outLen); memset(out,0,outLen); /* thickness */ if(s->df_thickness){ set_SSM2_DF_thick(out, 1); } /* length */ if(s->df_length){ set_SSM2_DF_len(out, 1); } /* staple detection */ if(s->stapledetect){ set_SSM2_DF_staple(out, 1); } int requires_postimprint = s->has_post_imprinter && (strlen(s->post_imprint.specstring) > 0); int requires_preimprint = s->has_pre_imprinter && (strlen(s->pre_imprint.specstring) > 0); if (s->has_post_imprinter) set_SSM2_DF_post_addon(out, requires_postimprint); if (requires_postimprint || requires_preimprint){ set_SSM2_DF_imprint(out, 1); } ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); } else{ DBG (10, "ssm_df: unsupported\n"); } DBG (10, "ssm_df: finish\n"); return ret; } static SANE_Status ssm2_hw_enhancement (struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; DBG (10, "ssm2_hw_enhancement: start\n"); if(s->has_ssm2){ unsigned char cmd[SET_SCAN_MODE2_len]; size_t cmdLen = SET_SCAN_MODE2_len; unsigned char out[SSM2_PAY_len]; size_t outLen = SSM2_PAY_len; memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SET_SCAN_MODE2_code); set_SSM2_page_code(cmd, SM2_pc_hw_enhancement); set_SSM2_pay_len(cmd, outLen); memset(out,0,outLen); if(s->rollerdeskew){ set_SSM2_roller_deskew(out, 1); } if(s->hwcrop){ set_SSM2_hw_crop(out, 1); } ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); } else{ DBG (10, "ssm2_hw_enhancement: unsupported\n"); } DBG (10, "ssm2_hw_enhancement: finish\n"); return ret; } static SANE_Status ssm_do (struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; DBG (10, "ssm_do: start\n"); if(!s->can_color){ DBG (10, "ssm_do: unsupported, finishing\n"); return ret; } if(s->s.mode == MODE_COLOR){ DBG (10, "ssm_do: unneeded, finishing\n"); return ret; } if(s->has_ssm){ unsigned char cmd[SET_SCAN_MODE_len]; size_t cmdLen = SET_SCAN_MODE_len; unsigned char out[SSM_PAY_len]; size_t outLen = SSM_PAY_len; memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SET_SCAN_MODE_code); set_SSM_pf(cmd, 1); set_SSM_pay_len(cmd, outLen); memset(out,0,outLen); if(s->has_ssm_pay_head_len){ set_SSM_pay_head_len(out, SSM_PAY_HEAD_len); } set_SSM_page_code(out, SM_pc_dropout); set_SSM_page_len(out, SSM_PAGE_len); set_SSM_DO_unk1(out, 0x03); switch(s->dropout_color[SIDE_FRONT]){ case COLOR_RED: set_SSM_DO_unk2(out, 0x05); set_SSM_DO_f_do(out,SSM_DO_red); break; case COLOR_GREEN: set_SSM_DO_unk2(out, 0x05); set_SSM_DO_f_do(out,SSM_DO_green); break; case COLOR_BLUE: set_SSM_DO_unk2(out, 0x05); set_SSM_DO_f_do(out,SSM_DO_blue); break; case COLOR_EN_RED: set_SSM_DO_unk2(out, 0x05); set_SSM_DO_f_en(out,SSM_DO_red); break; case COLOR_EN_GREEN: set_SSM_DO_unk2(out, 0x05); set_SSM_DO_f_en(out,SSM_DO_green); break; case COLOR_EN_BLUE: set_SSM_DO_unk2(out, 0x05); set_SSM_DO_f_en(out,SSM_DO_blue); break; } switch(s->dropout_color[SIDE_BACK]){ case COLOR_RED: set_SSM_DO_unk2(out, 0x05); set_SSM_DO_b_do(out,SSM_DO_red); break; case COLOR_GREEN: set_SSM_DO_unk2(out, 0x05); set_SSM_DO_b_do(out,SSM_DO_green); break; case COLOR_BLUE: set_SSM_DO_unk2(out, 0x05); set_SSM_DO_b_do(out,SSM_DO_blue); break; case COLOR_EN_RED: set_SSM_DO_unk2(out, 0x05); set_SSM_DO_b_en(out,SSM_DO_red); break; case COLOR_EN_GREEN: set_SSM_DO_unk2(out, 0x05); set_SSM_DO_b_en(out,SSM_DO_green); break; case COLOR_EN_BLUE: set_SSM_DO_unk2(out, 0x05); set_SSM_DO_b_en(out,SSM_DO_blue); break; } ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); } else if(s->has_ssm2){ unsigned char cmd[SET_SCAN_MODE2_len]; size_t cmdLen = SET_SCAN_MODE2_len; unsigned char out[SSM2_PAY_len]; size_t outLen = SSM2_PAY_len; memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SET_SCAN_MODE2_code); set_SSM2_page_code(cmd, SM2_pc_dropout); set_SSM2_pay_len(cmd, outLen); memset(out,0,outLen); switch(s->dropout_color[SIDE_FRONT]){ case COLOR_RED: set_SSM2_DO_do(out,SSM_DO_red); break; case COLOR_GREEN: set_SSM2_DO_do(out,SSM_DO_green); break; case COLOR_BLUE: set_SSM2_DO_do(out,SSM_DO_blue); break; case COLOR_EN_RED: set_SSM2_DO_en(out,SSM_DO_red); break; case COLOR_EN_GREEN: set_SSM2_DO_en(out,SSM_DO_green); break; case COLOR_EN_BLUE: set_SSM2_DO_en(out,SSM_DO_blue); break; } ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); if(ret == SANE_STATUS_GOOD && (s->s.source == SOURCE_ADF_DUPLEX || s->s.source == SOURCE_CARD_DUPLEX)){ memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SET_SCAN_MODE2_code); set_SSM2_page_code(cmd, SM2_pc_dropout); set_SSM2_DO_side(cmd, SIDE_BACK); set_SSM2_pay_len(cmd, outLen); memset(out,0,outLen); switch(s->dropout_color[SIDE_BACK]){ case COLOR_RED: set_SSM2_DO_do(out,SSM_DO_red); break; case COLOR_GREEN: set_SSM2_DO_do(out,SSM_DO_green); break; case COLOR_BLUE: set_SSM2_DO_do(out,SSM_DO_blue); break; case COLOR_EN_RED: set_SSM2_DO_en(out,SSM_DO_red); break; case COLOR_EN_GREEN: set_SSM2_DO_en(out,SSM_DO_green); break; case COLOR_EN_BLUE: set_SSM2_DO_en(out,SSM_DO_blue); break; } ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); } } else{ DBG (10, "ssm_do: unsupported\n"); } DBG (10, "ssm_do: finish\n"); return ret; } static SANE_Status read_counters(struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[READ_len]; size_t cmdLen = READ_len; unsigned char in[R_COUNTERS_len]; size_t inLen = R_COUNTERS_len; if (!s->can_read_lifecycle_counters){ DBG(10, "read_counters: unsupported\n"); return ret; } DBG(10, "read_counters: start\n"); memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, READ_code); set_R_datatype_code(cmd, SR_datatype_counters); set_R_xfer_length(cmd, inLen); ret = do_cmd( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if (ret == SANE_STATUS_GOOD || ret == SANE_STATUS_EOF){ s->total_counter = get_R_COUNTERS_total(in); s->roller_counter = s->total_counter - get_R_COUNTERS_last_srv(in); DBG(10, "read_counters: total counter: %d roller_counter %d \n",s->total_counter,s->roller_counter); ret = SANE_STATUS_GOOD; }else{ DBG(10, "read_counters: ERROR: %d\n",ret); } return ret; } static SANE_Status read_sensors(struct scanner *s,SANE_Int option) { SANE_Status ret=SANE_STATUS_GOOD; unsigned char cmd[READ_len]; size_t cmdLen = READ_len; unsigned char in[R_SENSORS_len]; size_t inLen = R_SENSORS_len; DBG (10, "read_sensors: start %d\n", option); if(!s->can_read_sensors){ DBG (10, "read_sensors: unsupported, finishing\n"); return ret; } /* only run this if frontend has already read the last time we got it */ /* or if we don't care for such bookkeeping (private use) */ if (!option || !s->sensors_read[option-OPT_ADF_LOADED]) { DBG (15, "read_sensors: running\n"); memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, READ_code); set_R_datatype_code (cmd, SR_datatype_sensors); set_R_xfer_length (cmd, inLen); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if (ret == SANE_STATUS_GOOD || ret == SANE_STATUS_EOF) { /*set flags indicating there is data to read*/ memset(s->sensors_read,1,sizeof(s->sensors_read)); s->sensor_adf_loaded = get_R_SENSORS_adf(in); s->sensor_card_loaded = get_R_SENSORS_card(in); ret = SANE_STATUS_GOOD; } } if(option) s->sensors_read[option-OPT_ADF_LOADED] = 0; DBG (10, "read_sensors: finish\n"); return ret; } static SANE_Status read_panel(struct scanner *s,SANE_Int option) { SANE_Status ret=SANE_STATUS_GOOD; unsigned char cmd[READ_len]; size_t cmdLen = READ_len; unsigned char in[R_PANEL_len]; size_t inLen = R_PANEL_len; DBG (10, "read_panel: start %d\n", option); if(!s->can_read_panel){ DBG (10, "read_panel: unsupported, finishing\n"); return ret; } /* only run this if frontend has already read the last time we got it */ /* or if we don't care for such bookkeeping (private use) */ if (!option || !s->panel_read[option-OPT_START]) { DBG (15, "read_panel: running\n"); memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, READ_code); set_R_datatype_code (cmd, SR_datatype_panel); set_R_xfer_length (cmd, inLen); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if (ret == SANE_STATUS_GOOD || ret == SANE_STATUS_EOF) { /*set flags indicating there is data to read*/ memset(s->panel_read,1,sizeof(s->panel_read)); s->panel_start = get_R_PANEL_start(in); s->panel_stop = get_R_PANEL_stop(in); s->panel_butt3 = get_R_PANEL_butt3(in); s->panel_new_file = get_R_PANEL_new_file(in); s->panel_count_only = get_R_PANEL_count_only(in); s->panel_bypass_mode = get_R_PANEL_bypass_mode(in); s->panel_enable_led = get_R_PANEL_enable_led(in); s->panel_counter = get_R_PANEL_counter(in); ret = SANE_STATUS_GOOD; } } if(option) s->panel_read[option-OPT_START] = 0; DBG (10, "read_panel: finish %d\n",s->panel_counter); return ret; } static SANE_Status send_panel(struct scanner *s) { SANE_Status ret=SANE_STATUS_GOOD; unsigned char cmd[SEND_len]; size_t cmdLen = SEND_len; unsigned char out[S_PANEL_len]; size_t outLen = S_PANEL_len; DBG (10, "send_panel: start\n"); if(!s->can_write_panel){ DBG (10, "send_panel: unsupported, finishing\n"); return ret; } memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SEND_code); set_S_xfer_datatype (cmd, SR_datatype_panel); set_S_xfer_length (cmd, outLen); memset(out,0,outLen); set_S_PANEL_enable_led(out,s->panel_enable_led); set_S_PANEL_counter(out,s->panel_counter); ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); if (ret == SANE_STATUS_EOF) { ret = SANE_STATUS_GOOD; } DBG (10, "send_panel: finish %d\n", ret); return ret; } /* * Request the size of the scanned image */ /* we should really be updating s->s and s->i instead */ static SANE_Status get_pixelsize(struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[READ_len]; size_t cmdLen = READ_len; unsigned char in[R_PSIZE_len]; size_t inLen = R_PSIZE_len; int i = 0; const int MAX_TRIES = 5; DBG (10, "get_pixelsize: start\n"); if(!s->hwcrop){ DBG (10, "get_pixelsize: unneeded, finishing\n"); return ret; } memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, READ_code); set_R_datatype_code(cmd, SR_datatype_pixelsize); set_R_xfer_lid(cmd, 0x02); set_R_xfer_length(cmd, inLen); /* May need to retry/block until the scanner is done */ for(i=0;i 0 && get_R_PSIZE_length(in) > 0){ DBG (15, "get_pixelsize: w:%d h:%d\n", get_R_PSIZE_width(in) * s->u.dpi_x / 1200, get_R_PSIZE_length(in) * s->u.dpi_y / 1200); /* * Round up to byte boundary if needed. * For 1 bpp the resulting size may not fit in a byte boundary. */ int remainder = (get_R_PSIZE_width(in) * s->u.dpi_x / 1200) % 8; if (s->u.mode < MODE_GRAYSCALE && remainder) { int rounded_up = (8 - remainder) + (get_R_PSIZE_width(in) * s->u.dpi_x / 1200); s->u.br_x = rounded_up * 1200 / s->u.dpi_x; } else{ s->u.br_x = get_R_PSIZE_width(in); } s->u.tl_x = 0; s->u.br_y = get_R_PSIZE_length(in); s->u.tl_y = 0; s->u.page_x = s->u.br_x; s->u.page_y = s->u.br_y; update_params(s,0); clean_params(s); break; } else{ DBG (10, "get_pixelsize: error reading, status = %d w:%d h:%d\n", ret, get_R_PSIZE_width(in), get_R_PSIZE_length(in)); ret = SANE_STATUS_INVAL; usleep(1000000); } } DBG (10, "get_pixelsize: finish\n"); return ret; } /* * @@ 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) { SANE_Status ret = SANE_STATUS_GOOD; struct scanner *s = (struct scanner *) handle; DBG (10, "sane_get_parameters: start\n"); if(!s->started){ ret = update_params(s,0); if(ret){ DBG (5, "sane_get_parameters: up error, returning %d\n", ret); return ret; } } /* this backend only sends single frame images */ params->last_frame = 1; params->format = s->i.format; params->lines = s->i.height; params->depth = s->i.bpp; if(params->depth == 24) params->depth = 8; params->pixels_per_line = s->i.width; params->bytes_per_line = s->i.Bpl; DBG(15,"sane_get_parameters: x: max=%d, page=%d, gpw=%d, res=%d\n", s->valid_x, s->i.page_x, get_page_width(s), s->i.dpi_x); DBG(15,"sane_get_parameters: y: max=%d, page=%d, gph=%d, res=%d\n", s->max_y, s->i.page_y, get_page_height(s), s->i.dpi_y); DBG(15,"sane_get_parameters: area: tlx=%d, brx=%d, tly=%d, bry=%d\n", s->i.tl_x, s->i.br_x, s->i.tl_y, s->i.br_y); DBG (15, "sane_get_parameters: params: ppl=%d, Bpl=%d, lines=%d\n", params->pixels_per_line, params->bytes_per_line, params->lines); DBG (15, "sane_get_parameters: params: format=%d, depth=%d, last=%d\n", params->format, params->depth, params->last_frame); DBG (10, "sane_get_parameters: finish\n"); return ret; } SANE_Status update_params(struct scanner *s, int calib) { SANE_Status ret = SANE_STATUS_GOOD; DBG (10, "update_params: start\n"); s->u.width = (s->u.br_x - s->u.tl_x) * s->u.dpi_x / 1200; s->u.height = (s->u.br_y - s->u.tl_y) * s->u.dpi_y / 1200; if (s->u.mode == MODE_COLOR) { s->u.format = SANE_FRAME_RGB; s->u.bpp = 24; } else if (s->u.mode == MODE_GRAYSCALE) { s->u.format = SANE_FRAME_GRAY; s->u.bpp = 8; } else { s->u.format = SANE_FRAME_GRAY; s->u.bpp = 1; /* round down to byte boundary */ s->u.width -= s->u.width % 8; } /* round down to pixel boundary for some scanners */ s->u.width -= s->u.width % s->ppl_mod; /* jpeg requires 8x8 squares */ if(s->compress == COMP_JPEG && s->u.mode >= MODE_GRAYSCALE){ s->u.format = SANE_FRAME_JPEG; s->u.width -= s->u.width % 8; s->u.height -= s->u.height % 8; } s->u.Bpl = s->u.width * s->u.bpp / 8; s->u.valid_Bpl = s->u.Bpl; s->u.valid_width = s->u.width; DBG (15, "update_params: user params: w:%d h:%d m:%d f:%d b:%d\n", s->u.width, s->u.height, s->u.mode, s->u.format, s->u.bpp); DBG (15, "update_params: user params: B:%d vB:%d vw:%d\n", s->u.Bpl, s->u.valid_Bpl, s->u.valid_width); DBG (15, "update_params: user params: x b:%d t:%d d:%d y b:%d t:%d d:%d\n", s->u.br_x, s->u.tl_x, s->u.dpi_x, s->u.br_y, s->u.tl_y, s->u.dpi_y); /* some scanners are limited in their valid scan params * make a second version of the params struct, but * override the user's values with what the scanner can actually do */ memcpy(&s->s,&s->u,sizeof(struct img_params)); /*********** missing modes (move up to valid one) **************/ if(s->s.mode == MODE_LINEART && !s->can_monochrome){ s->s.mode = MODE_GRAYSCALE; s->s.format = SANE_FRAME_GRAY; s->s.bpp = 8; } if(s->s.mode == MODE_GRAYSCALE && !s->can_grayscale){ s->s.mode = MODE_COLOR; s->s.format = SANE_FRAME_RGB; s->s.bpp = 24; } if(s->s.mode == MODE_COLOR && !s->can_color){ DBG (5, "update_params: no valid mode\n"); return SANE_STATUS_INVAL; } /********** missing resolutions (move up to valid one) *********/ if(!s->step_x_res){ int i; for(i=0;is.dpi_x > dpi_list[i] || !s->std_res_x[i]) continue; /* same & valid res, done */ if(s->s.dpi_x == dpi_list[i]) break; /* different & valid res, switch */ s->s.dpi_x = dpi_list[i]; break; } if(i > DPI_1200){ DBG (5, "update_params: no dpi\n"); return SANE_STATUS_INVAL; } } /*********** weird scan area (increase to valid one) *********/ if(s->fixed_width){ s->s.tl_x = 0; s->s.br_x = s->max_x; s->s.page_x = s->max_x; } /*recalculate new params*/ s->s.width = (s->s.br_x - s->s.tl_x) * s->s.dpi_x / 1200; /* round down to byte boundary */ if(s->s.mode < MODE_GRAYSCALE){ s->s.width -= s->s.width % 8; } /* round down to pixel boundary for some scanners */ s->s.width -= s->s.width % s->ppl_mod; s->s.valid_width = s->s.width; s->s.valid_Bpl = s->s.valid_width * s->s.bpp / 8; /* some machines (DR-2050) require even bytes per scanline */ /* increase width and Bpl, but not valid_width and valid_Bpl */ if(s->even_Bpl && (s->s.width % 2)){ s->s.width++; } s->s.Bpl = s->s.width * s->s.bpp / 8; /* figure out how many valid bytes per line (2510 is padded) */ if(s->color_interlace[SIDE_FRONT] == COLOR_INTERLACE_2510){ s->s.valid_Bpl = s->s.Bpl*11/12; s->s.valid_width = s->s.width*11/12; } /* some scanners need longer scans because front/back is offset */ if((s->u.source == SOURCE_ADF_DUPLEX || s->u.source == SOURCE_CARD_DUPLEX) && s->duplex_offset && !calib) s->s.height = (s->u.br_y-s->u.tl_y+s->duplex_offset) * s->u.dpi_y / 1200; /* round lines up to even number */ s->s.height += s->s.height % 2; DBG (15, "update_params: scan params: w:%d h:%d m:%d f:%d b:%d\n", s->s.width, s->s.height, s->s.mode, s->s.format, s->s.bpp); DBG (15, "update_params: scan params: B:%d vB:%d vw:%d\n", s->s.Bpl, s->s.valid_Bpl, s->s.valid_width); DBG (15, "update_params: scan params: x b:%d t:%d d:%d y b:%d t:%d d:%d\n", s->s.br_x, s->s.tl_x, s->s.dpi_x, s->s.br_y, s->s.tl_y, s->s.dpi_y); /* make a third (intermediate) version of the params struct, * currently identical to the user's params. this is what * we actually will send back to the user (though buffer_xxx * functions might change these values after this runs) */ /* calibration code needs the data just as it comes from the scanner */ if(calib) memcpy(&s->i,&s->s,sizeof(struct img_params)); /* normal scans need the data cleaned for presentation to the user */ else{ memcpy(&s->i,&s->u,sizeof(struct img_params)); /*dumb scanners pad the top of front page in duplex*/ if(s->i.source == SOURCE_ADF_DUPLEX || s->i.source == SOURCE_CARD_DUPLEX) s->i.skip_lines[s->duplex_offset_side] = s->duplex_offset * s->i.dpi_y / 1200; } DBG (15, "update_params: i params: w:%d h:%d m:%d f:%d b:%d\n", s->i.width, s->i.height, s->i.mode, s->i.format, s->i.bpp); DBG (15, "update_params: i params: B:%d vB:%d vw:%d\n", s->i.Bpl, s->i.valid_Bpl, s->i.valid_width); DBG (15, "update_params: i params: x b:%d t:%d d:%d y b:%d t:%d d:%d\n", s->i.br_x, s->i.tl_x, s->i.dpi_x, s->i.br_y, s->i.tl_y, s->i.dpi_y); DBG (10, "update_params: finish\n"); return ret; } /* simplify handling cmd SANE_STATUS_EOF as SANE_STATUS_GOOD */ SANE_Status send_cmd(struct scanner *s, unsigned char* cmd, size_t cmdLen, unsigned char* out, size_t outLen, unsigned char * inBuff, size_t * inLen) { SANE_Status ret=SANE_STATUS_GOOD; ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, inBuff, inLen ); if (ret == SANE_STATUS_EOF) { ret = SANE_STATUS_GOOD; } return ret; } SANE_Status send_imprint_positioning(struct scanner* s, int is_postimprint, int enabled) { unsigned char cmd[SET_SCAN_MODE2_len]; size_t cmdLen=SET_SCAN_MODE2_len; unsigned char out[SSM2_PAY_len]; size_t outLen=SSM2_PAY_len; unsigned char out_prefix[5]={ 0x01, 0x00, 0x60, 0x00, 0x60 }; size_t outPrefixLen=5; memset(cmd,0,cmdLen); set_SCSI_opcode(cmd,SET_SCAN_MODE2_code); set_SSM2_page_code(cmd,SM2_pc_imprinter_settings); if (is_postimprint) set_SSM2_postimprint_cmd(cmd); set_SSM2_pay_len(cmd,outLen); memset(out,0,outLen); memcpy(out,out_prefix,outPrefixLen); int h_offset; int v_offset; if (is_postimprint){ if (s->post_imprint_addon_mode != ADDON_DISABLED) set_SSM2_postimprint_addon(out); h_offset = s->post_imprint.h_offset; v_offset = s->post_imprint.v_offset; if (enabled) DBG (10, "send_imprint_positioning: post-imprinter: h_offset: %d v_offset: %d\n",h_offset,v_offset); }else{ h_offset = s->pre_imprint.h_offset; v_offset = s->pre_imprint.v_offset; if (enabled) DBG (10, "send_imprint_positioning: pre-imprinter: h_offset: %d v_offset: %d\n",h_offset,v_offset); } if(!enabled) h_offset = v_offset = 0; set_SSM2_imprint_hoffset(out,h_offset); set_SSM2_imprint_voffset(out,v_offset); return send_cmd(s, cmd, cmdLen, out, outLen, NULL, NULL); } SANE_Status send_imprint_specstring(struct scanner* s, int is_postimprint) { unsigned char cmd[SET_SCAN_MODE2_len]; size_t cmdLen = SET_SCAN_MODE2_len; unsigned char out[SSM2_IMPRINTER_STRING_PAY_len]; size_t outLen = SSM2_IMPRINTER_STRING_PAY_len; memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SET_SCAN_MODE2_code); set_SSM2_page_code(cmd, SM2_pc_imprinter_specstring); if (is_postimprint) set_SSM2_postimprint_cmd(cmd); set_SSM2_pay_len(cmd, outLen); memset(out,0,outLen); /* most of these bytes have yet to be identified to specific functionalities, as they never seem to change under different imprinting mode */ unsigned char out_prefix[32] = { 0x01, 0x00, 0x60, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00 }; memcpy(out, out_prefix, 32); if (is_postimprint){ set_SSM2_imprint_fontsize(out, s->post_imprint.font_size); set_SSM2_imprint_fontrot(out, s->post_imprint.font_rot); set_SSM2_imprint_spacing(out, s->post_imprint.spacing); if (s->post_imprint_addon_mode != ADDON_DISABLED) set_SSM2_imprint_addonmode(out, s->post_imprint_addon_mode); strcpy((SANE_Char*)(out + 45), (SANE_String_Const) s->post_imprint.specstring); DBG (10, "send_imprint_specstring: post-imprinter: font size: %d rotation: %d spacing: %d text: '%s' imprint-addon-mode: %d\n",s->post_imprint.font_size,s->post_imprint.font_rot,s->post_imprint.spacing,s->post_imprint.specstring,s->post_imprint_addon_mode); }else{ set_SSM2_imprint_fontsize(out, s->pre_imprint.font_size); set_SSM2_imprint_fontrot(out, s->pre_imprint.font_rot); set_SSM2_imprint_spacing(out, s->pre_imprint.spacing); strcpy((SANE_Char*)(out + 45), (SANE_String_Const) s->pre_imprint.specstring); DBG (10, "send_imprint_specstring: pre-imprinter: font size: %d rotation: %d spacing: %d text: '%s'\n",s->pre_imprint.font_size,s->pre_imprint.font_rot,s->pre_imprint.spacing,s->pre_imprint.specstring); } return send_cmd(s, cmd, cmdLen, out, outLen, NULL, NULL); } SANE_Status send_imprint_date_and_time(struct scanner* s) { unsigned char cmd[SET_SCAN_MODE2_len]; size_t cmdLen = SET_SCAN_MODE2_len; unsigned char out[SSM2_PAY_len]; size_t outLen = SSM2_PAY_len; memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SET_SCAN_MODE2_code); set_SSM2_page_code(cmd, SM2_pc_date_time); set_SSM2_pay_len(cmd, outLen); memset(out,0,outLen); time_t t = time(NULL); struct tm tM = *localtime(&t); set_SSM2_imprint_year(out, tM.tm_year + 1900); set_SSM2_imprint_month(out, tM.tm_mon + 1); set_SSM2_imprint_day(out, tM.tm_mday); set_SSM2_imprint_hour(out, tM.tm_hour); set_SSM2_imprint_min(out, tM.tm_min); set_SSM2_imprint_sec(out, tM.tm_sec); return send_cmd(s, cmd, cmdLen, out, outLen, NULL, NULL); } SANE_Status load_imprinting_settings(struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; int requires_preimprint = (strlen(s->pre_imprint.specstring) > 0); int requires_postimprint = (strlen(s->post_imprint.specstring) > 0); int send_date_time = (s->has_pre_imprinter && requires_preimprint) || (s->has_post_imprinter && requires_postimprint); if (s->has_pre_imprinter){ ret = send_imprint_positioning(s, 0, requires_preimprint); DBG(10, "load_imprinting_settings: send_pre_imprint_positioning = %d \n", ret); if (ret != SANE_STATUS_GOOD) return ret; if (requires_preimprint){ ret = send_imprint_specstring(s, 0); DBG(10, "load_imprinting_settings: send_pre_imprint_specstring = %d \n", ret); if (ret != SANE_STATUS_GOOD) return ret; } } if (s->has_post_imprinter){ ret = send_imprint_positioning(s, 1, requires_postimprint); DBG(10, "load_imprinting_settings: send_post_imprint_positioning = %d \n", ret); if (ret != SANE_STATUS_GOOD) return ret; if (requires_postimprint){ ret = send_imprint_specstring(s, 1); DBG(10, "load_imprinting_settings: send_post_imprint_specstring = %d \n", ret); if (ret != SANE_STATUS_GOOD) return ret; } } if (send_date_time){ ret = send_imprint_date_and_time(s); DBG(10, "load_imprinting_settings: send_imprint_date_and_time = %d \n", ret); } return ret; } /* look for a particular imprinter * SANE_STATUS_GOOD = found * SANE_STATUS_UNSUPPORTED = not found * SANE_STATUS_INVAL = all other errors */ static SANE_Status detect_imprinter(struct scanner *s, SANE_Int imp_side) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[READ_len]; size_t cmdLen = READ_len; unsigned char in[R_IMPRINTER_len]; size_t inLen = R_IMPRINTER_len; DBG (10, "detect_imprinter: start %d\n", imp_side); memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, READ_code); set_R_datatype_code(cmd, SR_datatype_imprinters); set_R_xfer_uid(cmd, imp_side); set_R_xfer_length(cmd, inLen); ret = do_cmd( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); /* some scanners return eof for success, so we change it */ if (ret == SANE_STATUS_EOF) { ret = SANE_STATUS_GOOD; } /* failed commands are 'inval' */ if(ret){ DBG (15, "detect_imprinter: error, converting %d to invalid\n", ret); ret = SANE_STATUS_INVAL; } /* negative responses are 'unsupported' */ else if(!get_R_IMPRINTER_found(in)){ DBG (15, "detect_imprinter: not found, converting to unsupported\n"); ret = SANE_STATUS_UNSUPPORTED; } DBG (10, "detect_imprinter: finish %d\n", ret); return ret; } /* reset image size parameters after buffer_xxx functions changed them */ SANE_Status update_i_params(struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; DBG (10, "update_i_params: start\n"); s->i.width = s->u.width; s->i.Bpl = s->u.Bpl; DBG (10, "update_i_params: finish\n"); return ret; } /* * Called by SANE when a page acquisition operation is to be started. * commands: set window, object pos, and scan * * this will be called between sides of a duplex scan, * and at the start of each page of an adf batch. * hence, we spend a lot of time playing with s->started, etc. */ SANE_Status sane_start (SANE_Handle handle) { struct scanner *s = handle; SANE_Status ret = SANE_STATUS_GOOD; DBG (10, "sane_start: start\n"); DBG (15, "started=%d, side=%d, source=%d\n", s->started, s->side, s->u.source); /* undo any prior sane_cancel calls */ s->cancelled=0; /* protect this block from sane_cancel */ s->reading=1; /* not finished with current side, error */ if (s->started && !s->u.eof[s->side]) { DBG(5,"sane_start: previous transfer not finished?"); return SANE_STATUS_INVAL; } /* batch start? inititalize struct and scanner */ if(!s->started){ /* load side marker */ if(s->u.source == SOURCE_ADF_BACK || s->u.source == SOURCE_CARD_BACK){ s->side = SIDE_BACK; } else{ s->side = SIDE_FRONT; } /* eject paper leftover*/ if(object_position (s, SANE_FALSE)){ DBG (5, "sane_start: ERROR: cannot eject page\n"); } /* wait for scanner to finish eject */ ret = wait_scanner (s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot wait scanner\n"); goto errors; } /* load the brightness/contrast lut with linear slope for calibration */ ret = load_lut (s->lut, 8, 8, 0, 255, 0, 0); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot load lut\n"); goto errors; } /* AFE cal */ ret = calibrate_AFE(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot cal afe\n"); goto errors; } /* fine cal */ ret = calibrate_fine(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot cal fine\n"); goto errors; } if (s->has_pre_imprinter || s->has_post_imprinter){ ret = load_imprinting_settings(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: invalid imprinting settings\n"); goto errors; } } /* reset the page counter after calibration */ s->panel_counter = 0; s->prev_page = 0; if(send_panel(s)){ DBG (5, "sane_start: ERROR: cannot send panel\n"); } /* we should really be updating s->s and s->i instead */ if(s->hwcrop){ s->u.br_x = s->max_x; s->u.tl_x = 0; s->u.br_y = s->max_y; s->u.tl_y = 0; s->u.page_x = s->max_x; s->u.page_y = s->max_y; } /* load our own private copy of scan params */ ret = update_params(s,0); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot update_params\n"); goto errors; } /* set window command */ ret = set_window(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot set window\n"); goto errors; } /* buffer/duplex/ald/fb/card command */ ret = ssm_buffer(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot ssm buffer\n"); goto errors; } /* dropout color command */ ret = ssm_do(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot ssm do\n"); goto errors; } /* double feed detection command */ ret = ssm_df(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot ssm df\n"); goto errors; } ret = ssm2_hw_enhancement(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot ssm2 hw enhancement\n"); goto errors; } /* clean scan params for new scan */ ret = clean_params(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot clean_params\n"); goto errors; } /* make large buffers to hold the images */ ret = image_buffers(s,1); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot load buffers\n"); goto errors; } /* load the brightness/contrast lut with user choices */ ret = load_lut (s->lut, 8, 8, 0, 255, s->contrast, s->brightness); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot load lut\n"); goto errors; } /* card reader dislikes op? */ if(s->s.source < SOURCE_CARD_FRONT){ /* grab next page */ ret = object_position (s, SANE_TRUE); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot load page\n"); goto errors; } /* wait for scanner to finish load */ ret = wait_scanner (s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot wait scanner\n"); goto errors; } } /* start scanning */ ret = start_scan (s,0); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot start_scan\n"); goto errors; } ret = get_pixelsize(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot get pixel size\n"); goto errors; } s->started = 1; } /* stuff done for subsequent images */ else{ /* duplex needs to switch sides */ if(s->s.source == SOURCE_ADF_DUPLEX || s->s.source == SOURCE_CARD_DUPLEX){ s->side = !s->side; } /* reset the intermediate params */ ret = update_i_params(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot update_i_params\n"); goto errors; } /* set clean defaults with new sheet of paper */ /* don't reset the transfer vars on backside of duplex page */ /* otherwise buffered back page will be lost */ /* ingest paper with adf (no-op for fb) */ /* don't call object pos or scan on back side of duplex scan */ if(s->side == SIDE_FRONT || s->s.source == SOURCE_ADF_BACK || s->s.source == SOURCE_CARD_BACK){ /* clean scan params for new scan */ ret = clean_params(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot clean_params\n"); goto errors; } /* big scanners and small ones in non-buff mode: OP to detect paper */ if(s->always_op || !s->buffermode){ ret = object_position (s, SANE_TRUE); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot load page\n"); goto errors; } /* user wants unbuffered scans */ /* send scan command */ if(!s->buffermode){ ret = start_scan (s,0); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot start_scan\n"); goto errors; } } } /* small, buffering scanners check for more pages by reading counter */ else{ ret = read_panel (s, OPT_COUNTER); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot load page\n"); goto errors; } if(s->prev_page == s->panel_counter){ DBG (5, "sane_start: same counter (%d) no paper?\n",s->prev_page); ret = SANE_STATUS_NO_DOCS; goto errors; } DBG (5, "sane_start: diff counter (%d/%d)\n", s->prev_page,s->panel_counter); } ret = get_pixelsize(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot get pixel size\n"); goto errors; } } } /* reset jpeg params on each page */ s->jpeg_stage=JPEG_STAGE_NONE; s->jpeg_ff_offset=0; DBG (15, "started=%d, side=%d, source=%d\n", s->started, s->side, s->u.source); /* certain options require the entire image to * be collected from the scanner before we can * tell the user the size of the image. the sane * API has no way to inform the frontend of this, * so we block and buffer. yuck */ if(must_fully_buffer(s)){ /* get image */ while(!s->s.eof[s->side] && !ret){ SANE_Int len = 0; ret = sane_read((SANE_Handle)s, NULL, 0, &len); } /* check for errors */ if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot buffer image\n"); goto errors; } DBG (5, "sane_start: OK: done buffering\n"); /* finished buffering, adjust image as required */ if(s->swdeskew){ buffer_deskew(s,s->side); } if(s->swcrop){ buffer_crop(s,s->side); } if(s->swdespeck){ buffer_despeck(s,s->side); } if(s->swskip){ /* Skipping means throwing out this image. * Pretend the user read the whole thing * and call sane_start again. * This assumes we are running in batch mode. */ if(buffer_isblank(s,s->side)){ s->u.eof[s->side] = 1; return sane_start(handle); } } } ret = check_for_cancel(s); s->reading = 0; DBG (10, "sane_start: finish %d\n", ret); return ret; errors: DBG (10, "sane_start: error %d\n", ret); s->started = 0; s->cancelled = 0; s->reading = 0; return ret; } /* * cleans params for new scan */ static SANE_Status clean_params (struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; DBG (10, "clean_params: start\n"); s->u.eof[0]=0; s->u.eof[1]=0; s->u.bytes_sent[0]=0; s->u.bytes_sent[1]=0; s->u.bytes_tot[0]=0; s->u.bytes_tot[1]=0; s->i.eof[0]=0; s->i.eof[1]=0; s->i.bytes_sent[0]=0; s->i.bytes_sent[1]=0; s->i.bytes_tot[0]=0; s->i.bytes_tot[1]=0; s->s.eof[0]=0; s->s.eof[1]=0; s->s.bytes_sent[0]=0; s->s.bytes_sent[1]=0; s->s.bytes_tot[0]=0; s->s.bytes_tot[1]=0; /* store the number of front bytes */ if ( s->u.source != SOURCE_ADF_BACK && s->u.source != SOURCE_CARD_BACK ) s->u.bytes_tot[SIDE_FRONT] = s->u.Bpl * s->u.height; if ( s->i.source != SOURCE_ADF_BACK && s->i.source != SOURCE_CARD_BACK ) s->i.bytes_tot[SIDE_FRONT] = s->i.Bpl * s->i.height; if ( s->s.source != SOURCE_ADF_BACK && s->s.source != SOURCE_CARD_BACK ) s->s.bytes_tot[SIDE_FRONT] = s->s.Bpl * s->s.height; /* store the number of back bytes */ if ( s->u.source == SOURCE_ADF_DUPLEX || s->u.source == SOURCE_ADF_BACK || s->u.source == SOURCE_CARD_DUPLEX || s->u.source == SOURCE_CARD_BACK ) s->u.bytes_tot[SIDE_BACK] = s->u.Bpl * s->u.height; if ( s->i.source == SOURCE_ADF_DUPLEX || s->i.source == SOURCE_ADF_BACK || s->i.source == SOURCE_CARD_DUPLEX || s->i.source == SOURCE_CARD_BACK ) s->i.bytes_tot[SIDE_BACK] = s->i.Bpl * s->i.height; if ( s->s.source == SOURCE_ADF_DUPLEX || s->s.source == SOURCE_ADF_BACK || s->s.source == SOURCE_CARD_DUPLEX || s->s.source == SOURCE_CARD_BACK ) s->s.bytes_tot[SIDE_BACK] = s->s.Bpl * s->s.height; DBG (10, "clean_params: finish\n"); return ret; } /* * frees/callocs buffers to hold the scan data */ static SANE_Status image_buffers (struct scanner *s, int setup) { SANE_Status ret = SANE_STATUS_GOOD; int side; DBG (10, "image_buffers: start\n"); for(side=0;side<2;side++){ /* free current buffer */ if (s->buffers[side]) { DBG (15, "image_buffers: free buffer %d.\n",side); free(s->buffers[side]); s->buffers[side] = NULL; } /* build new buffer if asked */ if(s->i.bytes_tot[side] && setup){ s->buffers[side] = calloc (1,s->i.bytes_tot[side]); if (!s->buffers[side]) { DBG (5, "image_buffers: Error, no buffer %d.\n",side); return SANE_STATUS_NO_MEM; } } } DBG (10, "image_buffers: finish\n"); return ret; } /* * This routine issues a SCSI SET WINDOW command to the scanner, using the * values currently in the s->s param structure. */ static SANE_Status set_window (struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; /* The command specifies the number of bytes in the data phase * the data phase has a header, followed by 1 window desc block * the header specifies the number of bytes in 1 window desc block */ unsigned char cmd[SET_WINDOW_len]; size_t cmdLen = SET_WINDOW_len; unsigned char out[SW_header_len + SW_desc_len]; size_t outLen = SW_header_len + SW_desc_len; unsigned char * header = out; /*header*/ unsigned char * desc1 = out + SW_header_len; /*descriptor*/ DBG (10, "set_window: start\n"); /*build the payload*/ memset(out,0,outLen); /* set window desc size in header */ set_WPDB_wdblen(header, SW_desc_len); /* init the window block */ if (s->s.source == SOURCE_ADF_BACK || s->s.source == SOURCE_CARD_BACK) { set_WD_wid (desc1, WD_wid_back); } else{ set_WD_wid (desc1, WD_wid_front); } set_WD_Xres (desc1, s->s.dpi_x); set_WD_Yres (desc1, s->s.dpi_y); /* some machines need max width */ if(s->fixed_width){ set_WD_ULX (desc1, 0); set_WD_width (desc1, s->max_x); } /* or they align left */ else if(s->u.source == SOURCE_FLATBED){ set_WD_ULX (desc1, s->s.tl_x); set_WD_width (desc1, s->s.width * 1200/s->s.dpi_x); } /* or we have to center the window ourselves */ else{ set_WD_ULX (desc1, (s->max_x - s->s.page_x) / 2 + s->s.tl_x); set_WD_width (desc1, s->s.width * 1200/s->s.dpi_x); } /* some models require that the tly value be inverted? */ if(s->invert_tly) set_WD_ULY (desc1, ~s->s.tl_y); else set_WD_ULY (desc1, s->s.tl_y); set_WD_length (desc1, s->s.height * 1200/s->s.dpi_y); if(s->has_btc){ /*convert our common -127 to +127 range into HW's range *FIXME: this code assumes hardware range of 0-255 */ set_WD_brightness (desc1, s->brightness+128); set_WD_threshold (desc1, s->threshold); /*convert our common -127 to +127 range into HW's range *FIXME: this code assumes hardware range of 0-255 */ set_WD_contrast (desc1, s->contrast+128); } set_WD_composition (desc1, s->s.mode); if(s->s.bpp == 24) set_WD_bitsperpixel (desc1, 8); else set_WD_bitsperpixel (desc1, s->s.bpp); if(s->s.mode == MODE_HALFTONE){ /*set_WD_ht_type(desc1, s->ht_type); set_WD_ht_pattern(desc1, s->ht_pattern);*/ } set_WD_rif (desc1, s->rif); set_WD_rgb(desc1, s->rgb_format); set_WD_padding(desc1, s->padding); /*FIXME: what is this? */ set_WD_reserved2(desc1, s->unknown_byte2); set_WD_compress_type(desc1, COMP_NONE); set_WD_compress_arg(desc1, 0); /* some scanners support jpeg image compression, for color/gs only */ if(s->s.format == SANE_FRAME_JPEG){ set_WD_compress_type(desc1, COMP_JPEG); set_WD_compress_arg(desc1, s->compress_arg); } /*build the command*/ memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SET_WINDOW_code); set_SW_xferlen(cmd, outLen); ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); if (!ret && (s->s.source == SOURCE_ADF_DUPLEX || s->s.source == SOURCE_CARD_DUPLEX)) { set_WD_wid (desc1, WD_wid_back); ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); } DBG (10, "set_window: finish\n"); return ret; } /* * Issues the SCSI OBJECT POSITION command if an ADF is in use. */ static SANE_Status object_position (struct scanner *s, int i_load) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[OBJECT_POSITION_len]; size_t cmdLen = OBJECT_POSITION_len; DBG (10, "object_position: start\n"); if (s->u.source == SOURCE_FLATBED) { DBG (10, "object_position: flatbed no-op\n"); return SANE_STATUS_GOOD; } memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, OBJECT_POSITION_code); if (i_load) { DBG (15, "object_position: load\n"); set_OP_autofeed (cmd, OP_Feed); } else { DBG (15, "object_position: eject\n"); set_OP_autofeed (cmd, OP_Discharge); } ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, NULL, NULL ); if (ret != SANE_STATUS_GOOD) return ret; DBG (10, "object_position: finish\n"); return ret; } /* * Issues SCAN command. * * (This doesn't actually read anything, it just tells the scanner * to start scanning.) */ static SANE_Status start_scan (struct scanner *s, int type) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[SCAN_len]; size_t cmdLen = SCAN_len; unsigned char out[] = {WD_wid_front, WD_wid_back}; size_t outLen = 2; DBG (10, "start_scan: start\n"); /* calibration scans use 0xff or 0xfe */ if(type){ out[0] = type; out[1] = type; } if (s->s.source != SOURCE_ADF_DUPLEX && s->s.source != SOURCE_CARD_DUPLEX) { outLen--; if(s->s.source == SOURCE_ADF_BACK || s->s.source == SOURCE_CARD_BACK) { out[0] = WD_wid_back; } } memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SCAN_code); set_SC_xfer_length (cmd, outLen); ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); DBG (10, "start_scan: finish\n"); return ret; } /* * 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 scanner *s = (struct scanner *) handle; SANE_Status ret=SANE_STATUS_GOOD; DBG (10, "sane_read: start\n"); *len=0; /* maybe cancelled? */ if(!s->started){ DBG (5, "sane_read: not started, call sane_start\n"); return SANE_STATUS_CANCELLED; } /* sane_start required between sides */ if(s->u.bytes_sent[s->side] == s->i.bytes_tot[s->side]){ s->u.eof[s->side] = 1; DBG (15, "sane_read: returning eof\n"); return SANE_STATUS_EOF; } s->reading = 1; /* double width pnm interlacing */ if((s->s.source == SOURCE_ADF_DUPLEX || s->s.source == SOURCE_CARD_DUPLEX) && s->s.format <= SANE_FRAME_RGB && s->duplex_interlace != DUPLEX_INTERLACE_NONE ){ /* buffer both sides */ if(!s->s.eof[SIDE_FRONT] || !s->s.eof[SIDE_BACK]){ ret = read_from_scanner_duplex(s, 0); if(ret){ DBG(5,"sane_read: front returning %d\n",ret); goto errors; } /*read last block, update counter*/ if(s->s.eof[SIDE_FRONT] && s->s.eof[SIDE_BACK]){ s->prev_page++; DBG(15,"sane_read: duplex counter %d\n",s->prev_page); } } } /* simplex or non-alternating duplex */ else{ if(!s->s.eof[s->side]){ ret = read_from_scanner(s, s->side, 0); if(ret){ DBG(5,"sane_read: side %d returning %d\n",s->side,ret); goto errors; } /*read last block, update counter*/ if(s->s.eof[s->side]){ s->prev_page++; DBG(15,"sane_read: side %d counter %d\n",s->side,s->prev_page); } } } /* copy a block from buffer to frontend */ ret = read_from_buffer(s,buf,max_len,len,s->side); if(ret) goto errors; ret = check_for_cancel(s); s->reading = 0; DBG (10, "sane_read: finish %d\n", ret); return ret; errors: DBG (10, "sane_read: error %d\n", ret); s->reading = 0; s->cancelled = 0; s->started = 0; return ret; } static SANE_Status read_from_scanner(struct scanner *s, int side, int exact) { SANE_Status ret=SANE_STATUS_GOOD; unsigned char cmd[READ_len]; size_t cmdLen = READ_len; unsigned char * in; size_t inLen = 0; size_t bytes = s->buffer_size; size_t remain = s->s.bytes_tot[side] - s->s.bytes_sent[side]; DBG (10, "read_from_scanner: start\n"); /* all requests must end on line boundary */ bytes -= (bytes % s->s.Bpl); /* some larger scanners require even bytes per block */ if(bytes % 2){ bytes -= s->s.Bpl; } /* usually (image) we want to read too much data, and get RS */ /* sometimes (calib) we want to do an exact read */ if(exact && bytes > remain){ bytes = remain; } DBG(15, "read_from_scanner: si:%d to:%d rx:%d re:%lu bu:%d pa:%lu ex:%d\n", side, s->s.bytes_tot[side], s->s.bytes_sent[side], (unsigned long)remain, s->buffer_size, (unsigned long)bytes, exact); inLen = bytes; in = malloc(inLen); if(!in){ DBG(5, "read_from_scanner: not enough mem for buffer: %d\n",(int)inLen); return SANE_STATUS_NO_MEM; } memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, READ_code); set_R_datatype_code (cmd, SR_datatype_image); set_R_xfer_length (cmd, inLen); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if (ret == SANE_STATUS_GOOD) { DBG(15, "read_from_scanner: got GOOD, returning GOOD %lu\n", (unsigned long)inLen); } else if (ret == SANE_STATUS_EOF) { DBG(15, "read_from_scanner: got EOF, finishing %lu\n", (unsigned long)inLen); } else if (ret == SANE_STATUS_DEVICE_BUSY) { DBG(5, "read_from_scanner: got BUSY, returning GOOD\n"); inLen = 0; ret = SANE_STATUS_GOOD; } else { DBG(5, "read_from_scanner: error reading data block status = %d\n",ret); inLen = 0; } /* this is jpeg data, we need to fix the missing image size */ if(s->s.format == SANE_FRAME_JPEG){ /* look for the SOF header near the beginning */ if(s->jpeg_stage == JPEG_STAGE_NONE || s->jpeg_ff_offset < 0x0d){ size_t i; for(i=0;ijpeg_stage == JPEG_STAGE_NONE && in[i] == 0xff){ s->jpeg_ff_offset=0; continue; } s->jpeg_ff_offset++; /* last byte was an ff, this byte is SOF */ if(s->jpeg_ff_offset == 1 && in[i] == 0xc0){ s->jpeg_stage = JPEG_STAGE_SOF; continue; } if(s->jpeg_stage == JPEG_STAGE_SOF){ /* lines in start of frame, overwrite it */ if(s->jpeg_ff_offset == 5){ in[i] = (s->s.height >> 8) & 0xff; continue; } if(s->jpeg_ff_offset == 6){ in[i] = s->s.height & 0xff; continue; } /* width in start of frame, overwrite it */ if(s->jpeg_ff_offset == 7){ in[i] = (s->s.width >> 8) & 0xff; continue; } if(s->jpeg_ff_offset == 8){ in[i] = s->s.width & 0xff; continue; } } } } } /*scanner may have sent more data than we asked for, chop it*/ if(inLen > remain){ inLen = remain; } /* we've got some data, descramble and store it */ if(inLen){ copy_simplex(s,in,inLen,side); } free(in); /* we've read all data, but not eof. clear and pretend */ if(exact && inLen == remain){ DBG (10, "read_from_scanner: exact read, clearing\n"); ret = object_position (s,SANE_FALSE); if(ret){ return ret; } ret = SANE_STATUS_EOF; } if(ret == SANE_STATUS_EOF){ /* this is jpeg data, we need to change the total size */ if(s->s.format == SANE_FRAME_JPEG){ s->s.bytes_tot[side] = s->s.bytes_sent[side]; s->i.bytes_tot[side] = s->i.bytes_sent[side]; s->u.bytes_tot[side] = s->i.bytes_sent[side]; } /* this is non-jpeg data, fill remainder, change rx'd size */ else{ fill_image(s,side); } s->i.eof[side] = 1; s->s.eof[side] = 1; ret = SANE_STATUS_GOOD; } DBG(15, "read_from_scanner: sto:%d srx:%d sef:%d uto:%d urx:%d uef:%d\n", s->s.bytes_tot[side], s->s.bytes_sent[side], s->s.eof[side], s->u.bytes_tot[side], s->u.bytes_sent[side], s->u.eof[side]); DBG (10, "read_from_scanner: finish\n"); return ret; } /* cheaper scanners interlace duplex scans on a byte basis * this code requests double width lines from scanner */ static SANE_Status read_from_scanner_duplex(struct scanner *s,int exact) { SANE_Status ret=SANE_STATUS_GOOD; unsigned char cmd[READ_len]; size_t cmdLen = READ_len; unsigned char * in; size_t inLen = 0; size_t bytes = s->buffer_size; size_t remain = s->s.bytes_tot[SIDE_FRONT] + s->s.bytes_tot[SIDE_BACK] - s->s.bytes_sent[SIDE_FRONT] - s->s.bytes_sent[SIDE_BACK]; DBG (10, "read_from_scanner_duplex: start\n"); /* all requests must end on WIDE line boundary */ bytes -= (bytes % (s->s.Bpl*2)); /* usually (image) we want to read too much data, and get RS */ /* sometimes (calib) we want to do an exact read */ if(exact && bytes > remain){ bytes = remain; } DBG(15, "read_from_scanner_duplex: re:%lu bu:%d pa:%lu ex:%d\n", (unsigned long)remain, s->buffer_size, (unsigned long)bytes, exact); inLen = bytes; in = malloc(inLen); if(!in){ DBG(5, "read_from_scanner_duplex: not enough mem for buffer: %d\n", (int)inLen); return SANE_STATUS_NO_MEM; } memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, READ_code); set_R_datatype_code (cmd, SR_datatype_image); set_R_xfer_length (cmd, inLen); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if (ret == SANE_STATUS_GOOD) { DBG(15, "read_from_scanner_duplex: got GOOD, returning GOOD %lu\n", (unsigned long)inLen); } else if (ret == SANE_STATUS_EOF) { DBG(15, "read_from_scanner_duplex: got EOF, finishing %lu\n", (unsigned long)inLen); } else if (ret == SANE_STATUS_DEVICE_BUSY) { DBG(5, "read_from_scanner_duplex: got BUSY, returning GOOD\n"); inLen = 0; ret = SANE_STATUS_GOOD; } else { DBG(5, "read_from_scanner_duplex: error reading data block status = %d\n", ret); inLen = 0; } /*scanner may have sent more data than we asked for, chop it*/ if(inLen > remain){ inLen = remain; } /* we've got some data, descramble and store it */ if(inLen){ copy_duplex(s,in,inLen); } free(in); /* we've read all data, but not eof. clear and pretend */ if(exact && inLen == remain){ DBG (10, "read_from_scanner_duplex: exact read, clearing\n"); ret = object_position (s,SANE_FALSE); if(ret){ return ret; } ret = SANE_STATUS_EOF; } if(ret == SANE_STATUS_EOF){ /* this is jpeg data, we need to change the total size */ if(s->s.format == SANE_FRAME_JPEG){ s->s.bytes_tot[SIDE_FRONT] = s->s.bytes_sent[SIDE_FRONT]; s->s.bytes_tot[SIDE_BACK] = s->s.bytes_sent[SIDE_BACK]; s->i.bytes_tot[SIDE_FRONT] = s->i.bytes_sent[SIDE_FRONT]; s->i.bytes_tot[SIDE_BACK] = s->i.bytes_sent[SIDE_BACK]; s->u.bytes_tot[SIDE_FRONT] = s->i.bytes_sent[SIDE_FRONT]; s->u.bytes_tot[SIDE_BACK] = s->i.bytes_sent[SIDE_BACK]; } /* this is non-jpeg data, fill remainder, change rx'd size */ else{ fill_image(s,SIDE_FRONT); fill_image(s,SIDE_BACK); } s->i.eof[SIDE_FRONT] = 1; s->i.eof[SIDE_BACK] = 1; s->s.eof[SIDE_FRONT] = 1; s->s.eof[SIDE_BACK] = 1; ret = SANE_STATUS_GOOD; } DBG (10, "read_from_scanner_duplex: finish\n"); return ret; } /* these functions copy image data from input buffer to scanner struct * descrambling it, and putting it in the right side buffer */ /* NOTE: they assume buffer is scanline aligned */ static SANE_Status copy_simplex(struct scanner *s, unsigned char * buf, int len, int side) { SANE_Status ret=SANE_STATUS_GOOD; int i, j; int bwidth = s->s.Bpl; int pwidth = s->s.width; int t = bwidth/3; int f = bwidth/4; int tw = bwidth/12; unsigned char * line = NULL; int line_next = 0; int inter = get_color_inter(s,side,s->s.dpi_x); /* jpeg data should not pass thru this function, so copy and bail out */ if(s->s.format > SANE_FRAME_RGB){ DBG (15, "copy_simplex: jpeg bulk copy\n"); memcpy(s->buffers[side]+s->i.bytes_sent[side], buf, len); s->i.bytes_sent[side] += len; s->s.bytes_sent[side] += len; return ret; } DBG (15, "copy_simplex: per-line copy\n"); line = malloc(bwidth); if(!line) return SANE_STATUS_NO_MEM; /* ingest each line */ for(i=0; is.bytes_sent[side] / bwidth; /*increment number of bytes rx'd from scanner*/ s->s.bytes_sent[side] += bwidth; /*have some padding from scanner to drop*/ if ( lineNum < s->i.skip_lines[side] || lineNum - s->i.skip_lines[side] >= s->i.height ){ continue; } line_next = 0; if(s->s.format == SANE_FRAME_GRAY){ switch (s->gray_interlace[side]) { /* one line has the following format: ggg...GGG * where the 'capital' letters are the beginning of the line */ case GRAY_INTERLACE_gG: DBG (17, "copy_simplex: gray, gG\n"); for (j=bwidth-1; j>=0; j--){ line[line_next++] = buf[i+j]; } break; case GRAY_INTERLACE_2510: DBG (17, "copy_simplex: gray, 2510\n"); /* first read head (third byte of every three) */ for(j=bwidth-1;j>=0;j-=3){ line[line_next++] = buf[i+j]; } /* second read head (first byte of every three) */ for(j=bwidth*3/4-3;j>=0;j-=3){ line[line_next++] = buf[i+j]; } /* third read head (second byte of every three) */ for(j=bwidth-2;j>=0;j-=3){ line[line_next++] = buf[i+j]; } /* padding */ for(j=0;j=0;j-=3){ line[line_next++] = buf[i+j]; } /* second read head (first byte of every three) */ for(j=bwidth-3;j>=0;j-=3){ line[line_next++] = buf[i+j]; } /* third read head (second byte of every three) */ for(j=bwidth-2;j>=0;j-=3){ line[line_next++] = buf[i+j]; } break; } } else if (s->s.format == SANE_FRAME_RGB){ switch (inter) { /* scanner returns color data as bgrbgr... */ case COLOR_INTERLACE_BGR: DBG (17, "copy_simplex: color, BGR\n"); for (j=0; j=0; j--){ line[line_next++] = buf[i+j]; line[line_next++] = buf[i+pwidth+j]; line[line_next++] = buf[i+2*pwidth+j]; } break; case COLOR_INTERLACE_2510: DBG (17, "copy_simplex: color, 2510\n"); /* first read head (third byte of every three) */ for(j=t-1;j>=0;j-=3){ line[line_next++] = buf[i+j]; line[line_next++] = buf[i+t+j]; line[line_next++] = buf[i+2*t+j]; } /* second read head (first byte of every three) */ for(j=f-3;j>=0;j-=3){ line[line_next++] = buf[i+j]; line[line_next++] = buf[i+t+j]; line[line_next++] = buf[i+2*t+j]; } /* third read head (second byte of every three) */ for(j=t-2;j>=0;j-=3){ line[line_next++] = buf[i+j]; line[line_next++] = buf[i+t+j]; line[line_next++] = buf[i+2*t+j]; } /* padding */ for(j=0;j=0;j-=3){ line[line_next++] = buf[i+j]; line[line_next++] = buf[i+t+j]; line[line_next++] = buf[i+2*t+j]; } /* second read head (first byte of every three) */ for(j=t-3;j>=0;j-=3){ line[line_next++] = buf[i+j]; line[line_next++] = buf[i+t+j]; line[line_next++] = buf[i+2*t+j]; } /* third read head (second byte of every three) */ for(j=t-2;j>=0;j-=3){ line[line_next++] = buf[i+j]; line[line_next++] = buf[i+t+j]; line[line_next++] = buf[i+2*t+j]; } break; } } /* nothing sent above? just copy one line of the block */ /* used by uninterlaced gray/color */ if(!line_next){ DBG (17, "copy_simplex: default\n"); memcpy(line+line_next,buf+i,bwidth); line_next = bwidth; } /* invert image if scanner needs it for this mode */ if(s->reverse_by_mode[s->s.mode]){ for(j=0; jf_offset[side]){ DBG (17, "copy_simplex: apply offset\n"); for(j=0; js.valid_Bpl; j++){ int curr = line[j] - s->f_offset[side][j]; if(curr < 0) curr = 0; line[j] = curr; } } if(s->f_gain[side]){ DBG (17, "copy_simplex: apply gain\n"); for(j=0; js.valid_Bpl; j++){ int curr = line[j] * 240/s->f_gain[side][j]; if(curr > 255) curr = 255; line[j] = curr; } } /* apply brightness and contrast if hardware cannot do it */ if(s->sw_lut && (s->s.mode == MODE_COLOR || s->s.mode == MODE_GRAYSCALE)){ DBG (17, "copy_simplex: apply brightness/contrast\n"); for(j=0; js.valid_Bpl; j++){ line[j] = s->lut[line[j]]; } } /*copy the line into the buffer*/ ret = copy_line(s,line,side); if(ret){ break; } } free(line); DBG (10, "copy_simplex: finished\n"); return ret; } /* split the data between two buffers, hand them to copy_simplex() * assumes that the buffer aligns to a double-wide line boundary */ static SANE_Status copy_duplex(struct scanner *s, unsigned char * buf, int len) { SANE_Status ret=SANE_STATUS_GOOD; int i,j; int pwidth = s->s.width; int bwidth = s->s.Bpl; int dbwidth = 2*bwidth; unsigned char * front; unsigned char * back; int flen=0, blen=0; DBG (10, "copy_duplex: start\n"); /*split the input into two simplex output buffers*/ front = calloc(1,len/2); if(!front){ DBG (5, "copy_duplex: no front mem\n"); return SANE_STATUS_NO_MEM; } back = calloc(1,len/2); if(!back){ DBG (5, "copy_duplex: no back mem\n"); free(front); return SANE_STATUS_NO_MEM; } if(s->duplex_interlace == DUPLEX_INTERLACE_2510){ DBG (10, "copy_duplex: 2510\n"); for(i=0; i 1st byte */ /* 3rd head: 4th byte -> 2nd byte */ /* 1st head: 5th byte -> 3rd byte */ front[flen++] = buf[i+j+2]; front[flen++] = buf[i+j+4]; front[flen++] = buf[i+j+5]; /* back */ /* 2nd head: 3rd byte -> 1st byte */ /* 3rd head: 0th byte -> 2nd byte */ /* 1st head: 1st byte -> 3rd byte */ back[blen++] = buf[i+j+3]; back[blen++] = buf[i+j]; back[blen++] = buf[i+j+1]; } } } /* line is in 6 sections, front red, back red, front green, etc. */ else if(s->duplex_interlace == DUPLEX_INTERLACE_PER_CHANNEL){ DBG (10, "copy_duplex: per channel\n"); for(i=0; iduplex_interlace == DUPLEX_INTERLACE_FfBb || s->duplex_interlace == DUPLEX_INTERLACE_fFBb){ for(i=0; iduplex_interlace == DUPLEX_INTERLACE_FfBb){ memcpy(front+flen,buf+i,bwidth); }else{ rmemcpy(front+flen,buf+i,bwidth,3); // only 24bit color is supported } flen+=bwidth; memcpy(back+blen,buf+i+bwidth,bwidth); blen+=bwidth; } } /*just alternating bytes, FBfb*/ else { for(i=0; is.width; int sbwidth = s->s.Bpl; int ibwidth = s->i.Bpl; unsigned char * line; int offset = 0; int i, j; DBG (20, "copy_line: start\n"); /* the 'standard' case: non-stupid scan */ if(s->s.width == s->i.width && s->s.dpi_x == s->i.dpi_x && s->s.mode == s->i.mode ){ memcpy(s->buffers[side]+s->i.bytes_sent[side], buff, sbwidth); s->i.bytes_sent[side] += sbwidth; DBG (20, "copy_line: finished smart\n"); return ret; } /* the 'corner' case: stupid scan */ /*setup 24 bit color single line buffer*/ line = malloc(spwidth*3); if(!line) return SANE_STATUS_NO_MEM; /*load single line color buffer*/ switch (s->s.mode) { case MODE_COLOR: if(must_downsample(s) && s->dropout_color[side]){ switch(s->dropout_color[side]){ case COLOR_RED: for(i=0;i> 7) & 1) ?0:255; line[i*24+3] = line[i*24+4] = line[i*24+5] = ((curr >> 6) & 1) ?0:255; line[i*24+6] = line[i*24+7] = line[i*24+8] = ((curr >> 5) & 1) ?0:255; line[i*24+9] = line[i*24+10] = line[i*24+11] = ((curr >> 4) & 1) ?0:255; line[i*24+12] = line[i*24+13] = line[i*24+14] =((curr >> 3) & 1) ?0:255; line[i*24+15] = line[i*24+16] = line[i*24+17] =((curr >> 2) & 1) ?0:255; line[i*24+18] = line[i*24+19] = line[i*24+20] =((curr >> 1) & 1) ?0:255; line[i*24+21] = line[i*24+22] = line[i*24+23] =((curr >> 0) & 1) ?0:255; } break; } /* scan is higher res than user wanted, scale it */ /*FIXME: interpolate instead */ if(s->i.dpi_x != s->s.dpi_x){ for(i=0;is.dpi_x/s->i.dpi_x * 3; if(source+2 >= spwidth*3) break; line[i*3] = line[source]; line[i*3+1] = line[source+1]; line[i*3+2] = line[source+2]; } } /* scan is wider than user wanted, skip some pixels on left side */ if(s->i.width != s->s.width){ offset = ((s->valid_x-s->i.page_x) / 2 + s->i.tl_x) * s->i.dpi_x/1200; } /* change mode, store line in buffer */ switch (s->i.mode) { case MODE_COLOR: memcpy(s->buffers[side]+s->i.bytes_sent[side], line+(offset*3), ibwidth); s->i.bytes_sent[side] += ibwidth; break; case MODE_GRAYSCALE: for(i=0;ibuffers[side][s->i.bytes_sent[side]++] = ((int)line[source] + line[source+1] + line[source+2])/3; } break; default: /*loop over output bytes*/ for(i=0;ithreshold*3; /*loop over output bits*/ for(j=0;j<8;j++){ int source = offset*3 + i*24 + j*3; if( (line[source] + line[source+1] + line[source+2]) < thresh ){ curr |= 1 << (7-j); } } s->buffers[side][s->i.bytes_sent[side]++] = curr; } break; } free(line); DBG (20, "copy_line: finish stupid\n"); return ret; } static SANE_Status read_from_buffer(struct scanner *s, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len, int side) { SANE_Status ret=SANE_STATUS_GOOD; int bytes = max_len; int remain = s->i.bytes_sent[side] - s->u.bytes_sent[side]; DBG (10, "read_from_buffer: start\n"); /* figure out the max amount to transfer */ if(bytes > remain) bytes = remain; *len = bytes; /*FIXME this needs to timeout eventually */ if(!bytes){ DBG(5,"read_from_buffer: nothing to do\n"); return SANE_STATUS_GOOD; } DBG(15, "read_from_buffer: si:%d to:%d tx:%d bu:%d pa:%d\n", side, s->i.bytes_tot[side], s->u.bytes_sent[side], max_len, bytes); /* copy to caller */ memcpy(buf,s->buffers[side]+s->u.bytes_sent[side],bytes); s->u.bytes_sent[side] += bytes; DBG (10, "read_from_buffer: finished\n"); return ret; } /* fill remainder of buffer with background if scanner stops early */ static SANE_Status fill_image(struct scanner *s,int side) { SANE_Status ret=SANE_STATUS_GOOD; unsigned char bg_color = calc_bg_color(s); int fill_bytes = s->i.bytes_tot[side]-s->i.bytes_sent[side]; if(!fill_bytes){ return ret; } DBG (15, "fill_image: side:%d bytes:%d bg_color:%02x\n", side, fill_bytes, bg_color); /* fill the rest with bg_color */ memset(s->buffers[side]+s->i.bytes_sent[side],bg_color,fill_bytes); /* pretend we got all the data from scanner */ s->i.bytes_sent[side] = s->i.bytes_tot[side]; s->s.bytes_sent[side] = s->s.bytes_tot[side]; return ret; } /* return the bg color based on scanner settings */ static unsigned char calc_bg_color(struct scanner *s) { unsigned char bg_color = s->lut[s->bg_color]; if(s->u.mode <= MODE_HALFTONE) bg_color = (bg_colorthreshold)?0xff:0x00; return bg_color; } /* * @@ Section 5 - calibration functions */ #if 0 static SANE_Status foo_AFE(struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[] = { 0x3b, 0x00, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00 }; size_t cmdLen = 12; unsigned char in[4]; size_t inLen = 4; DBG (10, "foo_AFE: start\n"); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if (ret != SANE_STATUS_GOOD) return ret; DBG (10, "foo_AFE: finish\n"); return ret; } #endif /* * makes several scans, adjusts coarse calibration */ static SANE_Status calibrate_AFE (struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; int i, j, k; int min, max; int lines = 8; /*buffer these for later*/ int old_tl_y = s->u.tl_y; int old_br_y = s->u.br_y; int old_mode = s->u.mode; int old_source = s->u.source; DBG (10, "calibrate_AFE: start\n"); if(!s->need_ccal){ DBG (10, "calibrate_AFE: not required\n"); return ret; } /* always cal with a short scan in duplex color */ s->u.tl_y = 0; s->u.br_y = lines * 1200 / s->u.dpi_y; s->u.mode = MODE_COLOR; s->u.source = SOURCE_ADF_DUPLEX; /* load our own private copy of scan params */ ret = update_params(s,1); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_AFE: ERROR: cannot update_params\n"); goto cleanup; } if(s->c_res == s->s.dpi_x && s->c_mode == s->s.mode){ DBG (10, "calibrate_AFE: already done\n"); goto cleanup; } /* clean scan params for new scan */ ret = clean_params(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_AFE: ERROR: cannot clean_params\n"); goto cleanup; } /* make buffers to hold the images */ ret = image_buffers(s,1); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_AFE: ERROR: cannot load buffers\n"); goto cleanup; } /*blast the existing fine cal data so reading code won't apply it*/ ret = offset_buffers(s,0); ret = gain_buffers(s,0); /* need to tell it we want duplex */ ret = ssm_buffer(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_AFE: ERROR: cannot ssm buffer\n"); goto cleanup; } /* set window command */ ret = set_window(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_AFE: ERROR: cannot set window\n"); goto cleanup; } /* first pass (black offset), lamp off, no offset/gain/exposure */ DBG (15, "calibrate_AFE: offset\n"); /* blast all the existing coarse cal data */ for(i=0;i<2;i++){ s->c_gain[i] = 1; s->c_offset[i] = 1; for(j=0;j<3;j++){ s->c_exposure[i][j] = 0; } } ret = write_AFE(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_AFE: ERROR: cannot write afe\n"); goto cleanup; } ret = calibration_scan(s,0xff); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_AFE: ERROR: cannot make offset cal scan\n"); goto cleanup; } for(i=0;i<2;i++){ min = 255; for(j=0; js.valid_Bpl; j++){ if(s->buffers[i][j] < min) min = s->buffers[i][j]; } s->c_offset[i] = min*3-2; DBG (15, "calibrate_AFE: offset %d %d %02x\n", i, min, s->c_offset[i]); } /*handle second pass (per channel exposure), lamp on, overexposed*/ DBG (15, "calibrate_AFE: exposure\n"); for(i=0;i<2;i++){ for(j=0; j<3; j++){ s->c_exposure[i][j] = 0x320; } } ret = write_AFE(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_AFE: ERROR: cannot write afe\n"); goto cleanup; } ret = calibration_scan(s,0xfe); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_AFE: ERROR: cannot make exposure cal scan\n"); goto cleanup; } for(i=0;i<2;i++){ /*sides*/ for(j=0;j<3;j++){ /*channels*/ max = 0; for(k=j; ks.valid_Bpl; k+=3){ /*bytes*/ if(s->buffers[i][k] > max) max = s->buffers[i][k]; } /*generally we reduce the exposure (smaller number) */ if(old_mode == MODE_COLOR) s->c_exposure[i][j] = s->c_exposure[i][j] * 102/max; else s->c_exposure[i][j] = s->c_exposure[i][j] * 64/max; DBG (15, "calibrate_AFE: exp %d %d %d %02x\n", i, j, max, s->c_exposure[i][j]); } } /*handle third pass (gain), lamp on with current offset/exposure */ DBG (15, "calibrate_AFE: gain\n"); ret = write_AFE(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_AFE: ERROR: cannot write afe\n"); goto cleanup; } ret = calibration_scan(s,0xfe); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_AFE: ERROR: cannot make gain cal scan\n"); goto cleanup; } for(i=0;i<2;i++){ max = 0; for(j=0; js.valid_Bpl; j++){ if(s->buffers[i][j] > max) max = s->buffers[i][j]; } if(old_mode == MODE_COLOR) s->c_gain[i] = (250-max)*4/5; else s->c_gain[i] = (125-max)*4/5; if(s->c_gain[i] < 1) s->c_gain[i] = 1; DBG (15, "calibrate_AFE: gain %d %d %02x\n", i, max, s->c_gain[i]); } /*handle fourth pass (offset again), lamp off*/ #if 0 DBG (15, "calibrate_AFE: offset2\n"); ret = write_AFE(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_AFE: ERROR: cannot write afe\n"); goto cleanup; } ret = calibration_scan(s,0xff); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_AFE: ERROR: cannot make offset2 cal scan\n"); goto cleanup; } for(i=0;i<2;i++){ min = 255; for(j=0; js.valid_Bpl; j++){ if(s->buffers[i][j] < min) min = s->buffers[i][j]; } /*s->c_offset[i] += min*3-2;*/ DBG (15, "calibrate_AFE: offset2 %d %d %02x\n", i, min, s->c_offset[i]); } #endif /*send final afe params to scanner*/ ret = write_AFE(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_AFE: ERROR: cannot write afe\n"); goto cleanup; } /* log current cal type */ s->c_res = s->s.dpi_x; s->c_mode = s->s.mode; cleanup: /* recover user settings */ s->u.tl_y = old_tl_y; s->u.br_y = old_br_y; s->u.mode = old_mode; s->u.source = old_source; DBG (10, "calibrate_AFE: finish %d\n",ret); return ret; } /* * fine calibration produces a per-cell offset and gain value, * which is then used to adjust the output from the scanner. * There is quite a bit of variation here, with different models * needing different types/amounts of help from the software. * * This function is a common entry point for all variations. */ static SANE_Status calibrate_fine (struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; DBG (10, "calibrate_fine: start\n"); if(s->fcal_src == FCAL_SRC_NONE || s->fcal_dest == FCAL_DEST_NONE){ DBG (10, "calibrate_fine: not required\n"); goto cleanup; } /* don't recalibrate if we've already done it with these params */ if(s->f_res == s->s.dpi_x && s->f_mode == s->s.mode){ DBG (10, "calibrate_fine: already done\n"); goto cleanup; } /* get calibration data from scanner memory */ if(s->fcal_src == FCAL_SRC_HW){ ret = calibrate_fine_src_hw(s); if (ret != SANE_STATUS_GOOD) goto cleanup; } /* get calibration data by making scans */ if(s->fcal_src == FCAL_SRC_SCAN){ ret = calibrate_fine_src_scan(s); if (ret != SANE_STATUS_GOOD) goto cleanup; } /* send calibration data to scanner */ if(s->fcal_dest == FCAL_DEST_HW){ ret = calibrate_fine_dest_hw(s); if (ret != SANE_STATUS_GOOD) goto cleanup; } /* log current cal settings so we won't recalibrate on next scan with same params */ s->f_res = s->s.dpi_x; s->f_mode = s->s.mode; cleanup: DBG (10, "calibrate_fine: finish %d\n",ret); return ret; } /* extracts fine calibration data from scanner memory */ static SANE_Status calibrate_fine_src_hw (struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; int i, j, k; unsigned char cmd[READ_len]; size_t cmdLen = READ_len; unsigned char * in = NULL; size_t inLen = 0, reqLen = 0; /*buffer these for later*/ int old_tl_y = s->u.tl_y; int old_br_y = s->u.br_y; int old_source = s->u.source; DBG (10, "calibrate_fine_src_hw: start\n"); /* pretend we are doing a 1 line scan in duplex */ s->u.tl_y = 0; s->u.br_y = 1200 / s->u.dpi_y; s->u.source = SOURCE_ADF_DUPLEX; /* load our own private copy of scan params */ ret = update_params(s,1); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_fine_src_hw: ERROR: cannot update_params\n"); goto cleanup; } /* clean scan params for new scan */ ret = clean_params(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_fine_src_hw: ERROR: cannot clean_params\n"); goto cleanup; } /*calibration buffers in scanner are single color channel, but duplex*/ reqLen = s->s.width*2; in = malloc(reqLen); if (!in) { DBG (5, "calibrate_fine_src_hw: ERROR: cannot malloc in\n"); ret = SANE_STATUS_NO_MEM; goto cleanup; } /*fine offset*/ ret = offset_buffers(s,1); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_fine_src_hw: ERROR: cannot load offset buffers\n"); goto cleanup; } DBG (10, "calibrate_fine_src_hw: %d %x\n", s->s.dpi_x/10, s->s.dpi_x/10); memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, READ_code); set_R_datatype_code (cmd, SR_datatype_fineoffset); set_R_xfer_lid (cmd, s->s.dpi_x/10); set_R_xfer_length (cmd, reqLen); inLen = reqLen; ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if (ret != SANE_STATUS_GOOD) goto cleanup; for(i=0;i<2;i++){ /*color mode, expand offset across all three channels? */ if(s->s.format == SANE_FRAME_RGB){ for(j=0; js.valid_width; j++){ /*red*/ s->f_offset[i][j*3] = in[j*2+i]; if(s->f_offset[i][j*3] < 1) s->f_offset[i][j*3] = 1; /*green and blue, same as red*/ s->f_offset[i][j*3+1] = s->f_offset[i][j*3+2] = s->f_offset[i][j*3]; } } /*gray mode, copy*/ else{ for(j=0; js.valid_width; j++){ s->f_offset[i][j] = in[j*2+i]; if(s->f_offset[i][j] < 1) s->f_offset[i][j] = 1; } } } /*fine gain*/ ret = gain_buffers(s,1); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_fine_src_hw: ERROR: cannot load gain buffers\n"); goto cleanup; } memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, READ_code); set_R_datatype_code (cmd, SR_datatype_finegain); set_R_xfer_lid (cmd, s->s.dpi_x/10); set_R_xfer_length (cmd, reqLen); /*color gain split into three buffers, grab them and merge*/ if(s->s.format == SANE_FRAME_RGB){ int codes[] = {R_FINE_uid_red,R_FINE_uid_green,R_FINE_uid_blue}; for(k=0;k<3;k++){ set_R_xfer_uid (cmd, codes[k]); inLen = reqLen; ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if (ret != SANE_STATUS_GOOD) goto cleanup; for(i=0;i<2;i++){ for(j=0; js.valid_width; j++){ s->f_gain[i][j*3+k] = in[j*2+i]*3/4; if(s->f_gain[i][j*3+k] < 1) s->f_gain[i][j*3+k] = 1; } } } } /*gray gain, copy*/ else{ set_R_xfer_uid (cmd, R_FINE_uid_gray); inLen = reqLen; ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if (ret != SANE_STATUS_GOOD) goto cleanup; for(i=0;i<2;i++){ for(j=0; js.valid_width; j++){ s->f_gain[i][j] = in[j*2+i]*3/4; if(s->f_gain[i][j] < 1) s->f_gain[i][j] = 1; } } } cleanup: if(in){ free(in); } /* recover user settings */ s->u.tl_y = old_tl_y; s->u.br_y = old_br_y; s->u.source = old_source; DBG (10, "calibrate_fine_src_hw: finish %d\n",ret); return ret; } /* * makes several scans, generates fine calibration data */ static SANE_Status calibrate_fine_src_scan (struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; int i, j, k; int min, max; int lines = 8; /*buffer these for later*/ int old_tl_y = s->u.tl_y; int old_br_y = s->u.br_y; int old_source = s->u.source; DBG (10, "calibrate_fine_src_scan: start\n"); /* always cal with a short scan in duplex */ s->u.tl_y = 0; s->u.br_y = lines * 1200 / s->u.dpi_y; s->u.source = SOURCE_ADF_DUPLEX; /* load our own private copy of scan params */ ret = update_params(s,1); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_fine_src_scan: ERROR: cannot update_params\n"); goto cleanup; } /* clean scan params for new scan */ ret = clean_params(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibration_fine: ERROR: cannot clean_params\n"); goto cleanup; } /* make buffers to hold the images */ ret = image_buffers(s,1); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_fine_src_scan: ERROR: cannot load buffers\n"); goto cleanup; } /*blast the existing fine cal data so reading code won't apply it*/ ret = offset_buffers(s,0); ret = gain_buffers(s,0); /* need to tell it we want duplex */ ret = ssm_buffer(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_fine_src_scan: ERROR: cannot ssm buffer\n"); goto cleanup; } /* set window command */ ret = set_window(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_fine_src_scan: ERROR: cannot set window\n"); goto cleanup; } /* first pass (fine offset), lamp off */ DBG (15, "calibrate_fine_src_scan: offset\n"); ret = calibration_scan(s,0xff); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_fine_src_scan: ERROR: cannot make offset cal scan\n"); goto cleanup; } ret = offset_buffers(s,1); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_fine_src_scan: ERROR: cannot load offset buffers\n"); goto cleanup; } for(i=0;i<2;i++){ for(j=0; js.valid_Bpl; j++){ min = 0; for(k=j;ks.Bpl;k+=s->s.Bpl){ min += s->buffers[i][k]; } s->f_offset[i][j] = min/lines; } hexdump(15, "off:", s->f_offset[i], s->s.valid_Bpl); } /* second pass (fine gain), lamp on */ DBG (15, "calibrate_fine_src_scan: gain\n"); ret = calibration_scan(s,0xfe); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_fine_src_scan: ERROR: cannot make gain cal scan\n"); goto cleanup; } ret = gain_buffers(s,1); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibrate_fine_src_scan: ERROR: cannot load gain buffers\n"); goto cleanup; } for(i=0;i<2;i++){ for(j=0; js.valid_Bpl; j++){ max = 0; for(k=j;ks.Bpl;k+=s->s.Bpl){ max += s->buffers[i][k]; } s->f_gain[i][j] = max/lines; if(s->f_gain[i][j] < 1) s->f_gain[i][j] = 1; } hexdump(15, "gain:", s->f_gain[i], s->s.valid_Bpl); } cleanup: /* recover user settings */ s->u.tl_y = old_tl_y; s->u.br_y = old_br_y; s->u.source = old_source; DBG (10, "calibrate_fine_src_scan: finish %d\n",ret); return ret; } /* write calibration data to scanner memory and delete from struct */ static SANE_Status calibrate_fine_dest_hw (struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; int i, j, k; unsigned char cmd[SEND_len]; size_t cmdLen = SEND_len; unsigned char * out = NULL; size_t outLen = 0; DBG (10, "calibrate_fine_dest_hw: start\n"); /* calibration buffers in scanner are single color channel, but 16 bit, plus 4 byte header */ outLen = s->s.width*2 + 4; out = calloc(outLen,1); if (!out) { DBG (5, "calibrate_fine_dest_hw: ERROR: cannot calloc out\n"); ret = SANE_STATUS_NO_MEM; goto cleanup; } // sides for(i=0;i<2;i++){ // colors for(j=0;j<3;j++){ int codes[] = { S_FCAL_id_f_red, S_FCAL_id_f_green, S_FCAL_id_f_blue, S_FCAL_id_b_red, S_FCAL_id_b_green, S_FCAL_id_b_blue}; // offset memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SEND_code); set_S_xfer_datatype (cmd, SR_datatype_fineoffset); set_S_xfer_length (cmd, outLen); set_S_FCAL_datatype (out, codes[i*3+j]); for(k=0; ks.valid_width; k++){ out[4+k*2] = 0; // TODO: calculate this instead of hardcode out[4+k*2+1] = 140; } ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, 0 ); if (ret != SANE_STATUS_GOOD) goto cleanup; // gain set_S_FCAL_datatype (out, codes[i*3+j] | 0x40); for(k=0; ks.valid_width; k++){ out[4+k*2] = 0; // TODO: calculate this instead of hardcode out[4+k*2+1] = 40; } ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, 0 ); if (ret != SANE_STATUS_GOOD) goto cleanup; } } cleanup: /*blast the fine cal data we generated above, so reading code wont apply it*/ offset_buffers(s,0); gain_buffers(s,0); if(out){ free(out); } DBG (10, "calibrate_fine_dest_hw: finish %d\n",ret); return ret; } /* * does a simple scan, ingests entire duplex image into buffers */ static SANE_Status calibration_scan (struct scanner *s, int scan) { SANE_Status ret = SANE_STATUS_GOOD; DBG (10, "calibration_scan: start\n"); /* clean scan params for new scan */ ret = clean_params(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibration_scan: ERROR: cannot clean_params\n"); return ret; } /* start scanning */ ret = start_scan (s,scan); if (ret != SANE_STATUS_GOOD) { DBG (5, "calibration_scan: ERROR: cannot start_scan\n"); return ret; } while(!s->s.eof[SIDE_FRONT] && !s->s.eof[SIDE_BACK]){ ret = read_from_scanner_duplex(s,1); } DBG (10, "calibration_scan: finished\n"); return ret; } /* * sends AFE and exposure params */ static SANE_Status write_AFE(struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[COR_CAL_len]; size_t cmdLen = COR_CAL_len; /*use the longest payload for buffer*/ unsigned char pay[CC3_pay_len]; size_t payLen = CC3_pay_len; DBG (10, "write_AFE: start\n"); /* newer scanners use a longer cc payload */ if(s->ccal_version == 3){ memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, COR_CAL_code); set_CC_version(cmd,CC3_pay_ver); set_CC_xferlen(cmd,payLen); memset(pay,0,payLen); set_CC3_gain_f_r(pay,s->c_gain[SIDE_FRONT]); set_CC3_gain_f_g(pay,s->c_gain[SIDE_FRONT]); set_CC3_gain_f_b(pay,s->c_gain[SIDE_FRONT]); set_CC3_off_f_r(pay,s->c_offset[SIDE_FRONT]); set_CC3_off_f_g(pay,s->c_offset[SIDE_FRONT]); set_CC3_off_f_b(pay,s->c_offset[SIDE_FRONT]); set_CC3_exp_f_r(pay,s->c_exposure[SIDE_FRONT][CHAN_RED]); set_CC3_exp_f_g(pay,s->c_exposure[SIDE_FRONT][CHAN_GREEN]); set_CC3_exp_f_b(pay,s->c_exposure[SIDE_FRONT][CHAN_BLUE]); set_CC3_gain_b_r(pay,s->c_gain[SIDE_BACK]); set_CC3_gain_b_g(pay,s->c_gain[SIDE_BACK]); set_CC3_gain_b_b(pay,s->c_gain[SIDE_BACK]); set_CC3_off_b_r(pay,s->c_offset[SIDE_BACK]); set_CC3_off_b_g(pay,s->c_offset[SIDE_BACK]); set_CC3_off_b_b(pay,s->c_offset[SIDE_BACK]); set_CC3_exp_b_r(pay,s->c_exposure[SIDE_BACK][CHAN_RED]); set_CC3_exp_b_g(pay,s->c_exposure[SIDE_BACK][CHAN_GREEN]); set_CC3_exp_b_b(pay,s->c_exposure[SIDE_BACK][CHAN_BLUE]); } else{ payLen = CC_pay_len; memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, COR_CAL_code); set_CC_version(cmd,CC_pay_ver); set_CC_xferlen(cmd,payLen); memset(pay,0,payLen); set_CC_f_gain(pay,s->c_gain[SIDE_FRONT]); set_CC_unk1(pay,1); set_CC_f_offset(pay,s->c_offset[SIDE_FRONT]); set_CC_unk2(pay,1); set_CC_exp_f_r1(pay,s->c_exposure[SIDE_FRONT][CHAN_RED]); set_CC_exp_f_g1(pay,s->c_exposure[SIDE_FRONT][CHAN_GREEN]); set_CC_exp_f_b1(pay,s->c_exposure[SIDE_FRONT][CHAN_BLUE]); set_CC_exp_f_r2(pay,s->c_exposure[SIDE_FRONT][CHAN_RED]); set_CC_exp_f_g2(pay,s->c_exposure[SIDE_FRONT][CHAN_GREEN]); set_CC_exp_f_b2(pay,s->c_exposure[SIDE_FRONT][CHAN_BLUE]); set_CC_b_gain(pay,s->c_gain[SIDE_BACK]); set_CC_b_offset(pay,s->c_offset[SIDE_BACK]); set_CC_exp_b_r1(pay,s->c_exposure[SIDE_BACK][CHAN_RED]); set_CC_exp_b_g1(pay,s->c_exposure[SIDE_BACK][CHAN_GREEN]); set_CC_exp_b_b1(pay,s->c_exposure[SIDE_BACK][CHAN_BLUE]); set_CC_exp_b_r2(pay,s->c_exposure[SIDE_BACK][CHAN_RED]); set_CC_exp_b_g2(pay,s->c_exposure[SIDE_BACK][CHAN_GREEN]); set_CC_exp_b_b2(pay,s->c_exposure[SIDE_BACK][CHAN_BLUE]); } ret = do_cmd ( s, 1, 0, cmd, cmdLen, pay, payLen, NULL, NULL ); if (ret != SANE_STATUS_GOOD) return ret; DBG (10, "write_AFE: finish\n"); return ret; } /* * frees/callocs buffers to hold the fine cal offset data */ static SANE_Status offset_buffers (struct scanner *s, int setup) { SANE_Status ret = SANE_STATUS_GOOD; int side; DBG (10, "offset_buffers: start\n"); for(side=0;side<2;side++){ if (s->f_offset[side]) { DBG (15, "offset_buffers: free f_offset %d.\n",side); free(s->f_offset[side]); s->f_offset[side] = NULL; } if(setup){ s->f_offset[side] = calloc (1,s->s.Bpl); if (!s->f_offset[side]) { DBG (5, "offset_buffers: error, no f_offset %d.\n",side); return SANE_STATUS_NO_MEM; } } } DBG (10, "offset_buffers: finish\n"); return ret; } /* * frees/callocs buffers to hold the fine cal gain data */ static SANE_Status gain_buffers (struct scanner *s, int setup) { SANE_Status ret = SANE_STATUS_GOOD; int side; DBG (10, "gain_buffers: start\n"); for(side=0;side<2;side++){ if (s->f_gain[side]) { DBG (15, "gain_buffers: free f_gain %d.\n",side); free(s->f_gain[side]); s->f_gain[side] = NULL; } if(setup){ s->f_gain[side] = calloc (1,s->s.Bpl); if (!s->f_gain[side]) { DBG (5, "gain_buffers: error, no f_gain %d.\n",side); return SANE_STATUS_NO_MEM; } } } DBG (10, "gain_buffers: finish\n"); return ret; } /* * @@ Section 6 - 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 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 operation 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) { struct scanner * s = (struct scanner *) handle; DBG (10, "sane_cancel: start\n"); s->cancelled = 1; /* if there is no other running function to check, we do it */ if(!s->reading) check_for_cancel(s); DBG (10, "sane_cancel: finish\n"); } /* checks started and cancelled flags in scanner struct, * sends cancel command to scanner if required. don't call * this function asynchronously, wait for pending operation */ static SANE_Status check_for_cancel(struct scanner *s) { SANE_Status ret=SANE_STATUS_GOOD; DBG (10, "check_for_cancel: start\n"); if(s->started && s->cancelled){ unsigned char cmd[CANCEL_len]; size_t cmdLen = CANCEL_len; DBG (15, "check_for_cancel: cancelling\n"); /* cancel scan */ memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, CANCEL_code); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, NULL, NULL ); if(ret){ DBG (5, "check_for_cancel: ignoring bad cancel: %d\n",ret); } ret = object_position(s,SANE_FALSE); if(ret){ DBG (5, "check_for_cancel: ignoring bad eject: %d\n",ret); } s->started = 0; s->cancelled = 0; ret = SANE_STATUS_CANCELLED; } else if(s->cancelled){ DBG (15, "check_for_cancel: already cancelled\n"); s->cancelled = 0; ret = SANE_STATUS_CANCELLED; } DBG (10, "check_for_cancel: finish %d\n",ret); return ret; } /* * 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) { struct scanner * s = (struct scanner *) handle; DBG (10, "sane_close: start\n"); disconnect_fd(s); image_buffers(s,0); offset_buffers(s,0); gain_buffers(s,0); DBG (10, "sane_close: finish\n"); } static SANE_Status disconnect_fd (struct scanner *s) { DBG (10, "disconnect_fd: start\n"); 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 (10, "disconnect_fd: finish\n"); return SANE_STATUS_GOOD; } /* * 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 scanner *dev, *next; DBG (10, "sane_exit: start\n"); for (dev = scanner_devList; dev; dev = next) { disconnect_fd(dev); next = dev->next; free (dev); } if (sane_devArray) free (sane_devArray); scanner_devList = NULL; sane_devArray = NULL; DBG (10, "sane_exit: finish\n"); } /* * @@ Section 7 - misc helper functions */ static void default_globals(void) { global_buffer_size = global_buffer_size_default; global_padded_read = global_padded_read_default; global_extra_status = global_extra_status_default; global_duplex_offset = global_duplex_offset_default; global_inquiry_length = INQUIRY_std_typ_len; global_vpd_length = INQUIRY_vpd_typ_len; global_tur_timeout = global_tur_timeout_default; global_vendor_name[0] = 0; global_model_name[0] = 0; global_version_name[0] = 0; } /* * 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) { struct scanner *s = 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); DBG (5, "sense_handler: start\n"); /* kill compiler warning */ (void) 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 0: if (ili == 1) { s->rs_info = info; DBG (5, "No sense: EOM remainder:%d\n",info); return SANE_STATUS_EOF; } DBG (5, "No sense: unknown asc/ascq\n"); return SANE_STATUS_GOOD; case 1: if (asc == 0x37 && ascq == 0x00) { DBG (5, "Recovered error: parameter rounded\n"); return SANE_STATUS_GOOD; } DBG (5, "Recovered error: unknown asc/ascq\n"); return SANE_STATUS_GOOD; case 2: if (asc == 0x04 && ascq == 0x01) { DBG (5, "Not ready: previous command unfinished\n"); return SANE_STATUS_DEVICE_BUSY; } DBG (5, "Not ready: unknown asc/ascq\n"); return SANE_STATUS_DEVICE_BUSY; case 3: if (asc == 0x36 && ascq == 0x00) { DBG (5, "Medium error: no cartridge\n"); return SANE_STATUS_IO_ERROR; } if (asc == 0x3a && ascq == 0x00) { DBG (5, "Medium error: hopper empty\n"); return SANE_STATUS_NO_DOCS; } if (asc == 0x80 && ascq == 0x00) { DBG (5, "Medium error: paper jam\n"); return SANE_STATUS_JAMMED; } if (asc == 0x80 && ascq == 0x01) { DBG (5, "Medium error: cover open\n"); return SANE_STATUS_COVER_OPEN; } if (asc == 0x81 && ascq == 0x01) { DBG (5, "Medium error: double feed\n"); return SANE_STATUS_JAMMED; } if (asc == 0x81 && ascq == 0x02) { DBG (5, "Medium error: skew detected\n"); return SANE_STATUS_JAMMED; } if (asc == 0x81 && ascq == 0x04) { DBG (5, "Medium error: staple detected\n"); return SANE_STATUS_JAMMED; } DBG (5, "Medium error: unknown asc/ascq\n"); return SANE_STATUS_IO_ERROR; case 4: if (asc == 0x60 && ascq == 0x00) { DBG (5, "Hardware error: lamp error\n"); return SANE_STATUS_IO_ERROR; } if (asc == 0x80 && ascq == 0x01) { DBG (5, "Hardware error: CPU check error\n"); return SANE_STATUS_IO_ERROR; } if (asc == 0x80 && ascq == 0x02) { DBG (5, "Hardware error: RAM check error\n"); return SANE_STATUS_IO_ERROR; } if (asc == 0x80 && ascq == 0x03) { DBG (5, "Hardware error: ROM check error\n"); return SANE_STATUS_IO_ERROR; } if (asc == 0x80 && ascq == 0x04) { DBG (5, "Hardware error: hardware check error\n"); return SANE_STATUS_IO_ERROR; } DBG (5, "Hardware error: unknown asc/ascq\n"); return SANE_STATUS_IO_ERROR; case 5: if (asc == 0x1a && ascq == 0x00) { DBG (5, "Illegal request: Parameter list error\n"); return SANE_STATUS_INVAL; } if (asc == 0x20 && ascq == 0x00) { DBG (5, "Illegal request: invalid command\n"); return SANE_STATUS_INVAL; } if (asc == 0x24 && ascq == 0x00) { DBG (5, "Illegal request: invalid CDB field\n"); return SANE_STATUS_INVAL; } if (asc == 0x25 && ascq == 0x00) { DBG (5, "Illegal request: unsupported logical unit\n"); return SANE_STATUS_UNSUPPORTED; } if (asc == 0x26 && ascq == 0x00) { DBG (5, "Illegal request: invalid field in parm list\n"); return SANE_STATUS_INVAL; } if (asc == 0x2c && ascq == 0x00) { DBG (5, "Illegal request: command sequence error\n"); return SANE_STATUS_INVAL; } if (asc == 0x2c && ascq == 0x01) { DBG (5, "Illegal request: too many windows\n"); return SANE_STATUS_INVAL; } if (asc == 0x3a && ascq == 0x00) { DBG (5, "Illegal request: no paper\n"); return SANE_STATUS_NO_DOCS; } if (asc == 0x3d && ascq == 0x00) { DBG (5, "Illegal request: invalid IDENTIFY\n"); return SANE_STATUS_INVAL; } if (asc == 0x55 && ascq == 0x00) { DBG (5, "Illegal request: scanner out of memory\n"); return SANE_STATUS_NO_MEM; } DBG (5, "Illegal request: unknown asc/ascq\n"); return SANE_STATUS_IO_ERROR; break; case 6: if (asc == 0x29 && ascq == 0x00) { DBG (5, "Unit attention: device reset\n"); return SANE_STATUS_GOOD; } if (asc == 0x2a && ascq == 0x00) { DBG (5, "Unit attention: param changed by 2nd initiator\n"); return SANE_STATUS_GOOD; } DBG (5, "Unit attention: unknown asc/ascq\n"); return SANE_STATUS_IO_ERROR; break; case 7: DBG (5, "Data protect: unknown asc/ascq\n"); return SANE_STATUS_IO_ERROR; case 8: DBG (5, "Blank check: unknown asc/ascq\n"); return SANE_STATUS_IO_ERROR; case 9: DBG (5, "Vendor defined: unknown asc/ascq\n"); return SANE_STATUS_IO_ERROR; case 0xa: DBG (5, "Copy aborted: unknown asc/ascq\n"); return SANE_STATUS_IO_ERROR; case 0xb: if (asc == 0x00 && ascq == 0x00) { DBG (5, "Aborted command: no sense/cancelled\n"); return SANE_STATUS_CANCELLED; } if (asc == 0x45 && ascq == 0x00) { DBG (5, "Aborted command: reselect failure\n"); return SANE_STATUS_IO_ERROR; } if (asc == 0x47 && ascq == 0x00) { DBG (5, "Aborted command: SCSI parity error\n"); return SANE_STATUS_IO_ERROR; } if (asc == 0x48 && ascq == 0x00) { DBG (5, "Aborted command: initiator error message\n"); return SANE_STATUS_IO_ERROR; } if (asc == 0x49 && ascq == 0x00) { DBG (5, "Aborted command: invalid message\n"); return SANE_STATUS_IO_ERROR; } if (asc == 0x80 && ascq == 0x00) { DBG (5, "Aborted command: timeout\n"); return SANE_STATUS_IO_ERROR; } DBG (5, "Aborted command: unknown asc/ascq\n"); return SANE_STATUS_IO_ERROR; break; case 0xc: DBG (5, "Equal: unknown asc/ascq\n"); return SANE_STATUS_IO_ERROR; case 0xd: DBG (5, "Volume overflow: unknown asc/ascq\n"); return SANE_STATUS_IO_ERROR; case 0xe: if (asc == 0x3b && ascq == 0x0d) { DBG (5, "Miscompare: too many docs\n"); return SANE_STATUS_IO_ERROR; } if (asc == 0x3b && ascq == 0x0e) { DBG (5, "Miscompare: too few docs\n"); return SANE_STATUS_IO_ERROR; } DBG (5, "Miscompare: unknown asc/ascq\n"); return SANE_STATUS_IO_ERROR; default: DBG (5, "Unknown Sense Code\n"); return SANE_STATUS_IO_ERROR; } DBG (5, "sense_handler: should never happen!\n"); return SANE_STATUS_IO_ERROR; } /* * take a bunch of pointers, send commands to scanner */ static SANE_Status do_cmd(struct scanner *s, int runRS, int timeout, 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, timeout, cmdBuff, cmdLen, outBuff, outLen, inBuff, inLen ); } if (s->connection == CONNECTION_USB) { return do_usb_cmd(s, runRS, timeout, cmdBuff, cmdLen, outBuff, outLen, inBuff, inLen ); } return SANE_STATUS_INVAL; } static SANE_Status do_scsi_cmd(struct scanner *s, int runRS, int timeout, unsigned char * cmdBuff, size_t cmdLen, unsigned char * outBuff, size_t outLen, unsigned char * inBuff, size_t * inLen ) { int ret; /*shut up compiler*/ (void) runRS; (void) timeout; DBG(10, "do_scsi_cmd: start\n"); DBG(25, "cmd: writing %d bytes\n", (int)cmdLen); hexdump(30, "cmd: >>", cmdBuff, cmdLen); 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); } ret = sanei_scsi_cmd2(s->fd, cmdBuff, cmdLen, outBuff, outLen, inBuff, inLen); if(ret != SANE_STATUS_GOOD && ret != SANE_STATUS_EOF){ DBG(5,"do_scsi_cmd: return '%s'\n",sane_strstatus(ret)); return ret; } if (inBuff && inLen){ if(ret == SANE_STATUS_EOF){ DBG(25, "in: short read, remainder %lu bytes\n", (u_long)s->rs_info); *inLen -= s->rs_info; } hexdump(31, "in: <<", inBuff, *inLen); DBG(25, "in: read %d bytes\n", (int)*inLen); } DBG(10, "do_scsi_cmd: finish\n"); return ret; } static SANE_Status do_usb_cmd(struct scanner *s, int runRS, int timeout, unsigned char * cmdBuff, size_t cmdLen, unsigned char * outBuff, size_t outLen, unsigned char * inBuff, size_t * inLen ) { size_t cmdOffset = 0; size_t cmdLength = 0; size_t cmdActual = 0; unsigned char * cmdBuffer = NULL; size_t outOffset = 0; size_t outLength = 0; size_t outActual = 0; unsigned char * outBuffer = NULL; size_t inOffset = 0; size_t inLength = 0; size_t inActual = 0; unsigned char * inBuffer = NULL; size_t extraLength = 0; int actTimeout = timeout ? timeout : USB_PACKET_TIMEOUT; int ret = 0; int ret2 = 0; struct timeval timer; gettimeofday(&timer,NULL); DBG (10, "do_usb_cmd: start %lu %lu\n", (long unsigned int)timer.tv_sec, (long unsigned int)timer.tv_usec); /* change timeout */ sanei_usb_set_timeout(actTimeout); /****************************************************************/ /* the command stage */ { cmdOffset = USB_HEADER_LEN; cmdLength = cmdOffset+USB_COMMAND_LEN; cmdActual = cmdLength; /* build buffer */ cmdBuffer = calloc(cmdLength,1); if(!cmdBuffer){ DBG(5,"cmd: no mem\n"); return SANE_STATUS_NO_MEM; } /* build a USB packet around the SCSI command */ set_USB_CMD_xfer_length(cmdBuffer,cmdLength-4); cmdBuffer[5] = 1; cmdBuffer[6] = 0x90; memcpy(cmdBuffer+cmdOffset,cmdBuff,cmdLen); /* write the command out */ DBG(25, "cmd: writing %d bytes, timeout %d\n", (int)cmdLength, actTimeout); hexdump(30, "cmd: >>", cmdBuffer, cmdLength); ret = sanei_usb_write_bulk(s->fd, cmdBuffer, &cmdActual); DBG(25, "cmd: wrote %d bytes, retVal %d\n", (int)cmdActual, ret); if(cmdLength != cmdActual){ DBG(5,"cmd: wrong size %d/%d\n", (int)cmdLength, (int)cmdActual); free(cmdBuffer); return SANE_STATUS_IO_ERROR; } if(ret != SANE_STATUS_GOOD){ DBG(5,"cmd: write error '%s'\n",sane_strstatus(ret)); free(cmdBuffer); return ret; } free(cmdBuffer); } /****************************************************************/ /* the extra status stage, used by few scanners */ /* this is like the regular status block, with an additional */ /* length component at the end */ if(s->extra_status){ ret2 = do_usb_status(s,runRS,timeout,&extraLength); /* bail out on bad RS status */ if(ret2){ DBG(5,"extra: bad RS status, %d\n", ret2); return ret2; } } /****************************************************************/ /* the output stage */ if(outBuff && outLen){ outOffset = USB_HEADER_LEN; outLength = outOffset+outLen; outActual = outLength; /* build outBuffer */ outBuffer = calloc(outLength,1); if(!outBuffer){ DBG(5,"out: no mem\n"); return SANE_STATUS_NO_MEM; } /* build a USB packet around the SCSI command */ set_USB_OUT_xfer_length(outBuffer,outLength-4); outBuffer[5] = 2; outBuffer[6] = 0xb0; memcpy(outBuffer+outOffset,outBuff,outLen); /* write the command out */ DBG(25, "out: writing %d bytes, timeout %d\n", (int)outLength, actTimeout); hexdump(30, "out: >>", outBuffer, outLength); ret = sanei_usb_write_bulk(s->fd, outBuffer, &outActual); DBG(25, "out: wrote %d bytes, retVal %d\n", (int)outActual, ret); if(outLength != outActual){ DBG(5,"out: wrong size %d/%d\n", (int)outLength, (int)outActual); free(outBuffer); return SANE_STATUS_IO_ERROR; } if(ret != SANE_STATUS_GOOD){ DBG(5,"out: write error '%s'\n",sane_strstatus(ret)); free(outBuffer); return ret; } free(outBuffer); } /****************************************************************/ /* the input stage */ if(inBuff && inLen){ inOffset = 0; if(s->padded_read) inOffset = USB_HEADER_LEN; inLength = inOffset+*inLen; inActual = inLength; /* use the extra length to alter the amount of in we request */ if(s->extra_status && extraLength && *inLen > extraLength){ DBG(5,"in: adjust extra, %d %d\n", (int)*inLen, (int)extraLength); inActual = inOffset+extraLength; } /*blast caller's copy in case we error out*/ *inLen = 0; /* build inBuffer */ inBuffer = calloc(inActual,1); if(!inBuffer){ DBG(5,"in: no mem\n"); return SANE_STATUS_NO_MEM; } DBG(25, "in: reading %d bytes, timeout %d\n", (int)inActual, actTimeout); ret = sanei_usb_read_bulk(s->fd, inBuffer, &inActual); DBG(25, "in: read %d bytes, retval %d\n", (int)inActual, ret); hexdump(31, "in: <<", inBuffer, inActual); if(!inActual){ DBG(5,"in: got no data, clearing\n"); free(inBuffer); return do_usb_clear(s,1,runRS); } if(inActual < inOffset){ DBG(5,"in: read shorter than inOffset\n"); free(inBuffer); return SANE_STATUS_IO_ERROR; } if(ret != SANE_STATUS_GOOD){ DBG(5,"in: return error '%s'\n",sane_strstatus(ret)); free(inBuffer); return ret; } /* note that inBuffer is not copied and freed here...*/ } /****************************************************************/ /* the normal status stage */ ret2 = do_usb_status(s,runRS,timeout,&extraLength); /* if status said EOF, adjust input with remainder count */ if(ret2 == SANE_STATUS_EOF && inBuffer){ /* EOF is ok */ ret2 = SANE_STATUS_GOOD; if(inActual < inLength - s->rs_info){ DBG(5,"in: we read < RS, ignoring RS: %d < %d (%d-%d)\n", (int)inActual,(int)(inLength-s->rs_info),(int)inLength,(int)s->rs_info); } else if(inActual > inLength - s->rs_info){ DBG(5,"in: we read > RS, using RS: %d to %d (%d-%d)\n", (int)inActual,(int)(inLength-s->rs_info),(int)inLength,(int)s->rs_info); inActual = inLength - s->rs_info; } } /* bail out on bad RS status */ if(ret2){ if(inBuffer) free(inBuffer); DBG(5,"stat: bad RS status, %d\n", ret2); return ret2; } /* now that we have read status, deal with input buffer */ if(inBuffer){ if(inLength != inActual){ ret = SANE_STATUS_EOF; DBG(5,"in: short read, %d/%d\n", (int)inLength,(int)inActual); } /* ignore the USB packet around the SCSI command */ *inLen = inActual - inOffset; memcpy(inBuff,inBuffer+inOffset,*inLen); free(inBuffer); } gettimeofday(&timer,NULL); DBG (10, "do_usb_cmd: finish %lu %lu\n", (long unsigned int)timer.tv_sec, (long unsigned int)timer.tv_usec); return ret; } static SANE_Status do_usb_status(struct scanner *s, int runRS, int timeout, size_t * extraLength) { #define EXTRA_READ_len 4 size_t statPadding = 0; size_t statOffset = 0; size_t statLength = 0; size_t statActual = 0; unsigned char * statBuffer = NULL; int actTimeout = timeout ? timeout : USB_PACKET_TIMEOUT; int ret = 0; if(s->padded_read) statPadding = USB_HEADER_LEN; statLength = statPadding+USB_STATUS_LEN; statOffset = statLength-1; if(s->extra_status) statLength += EXTRA_READ_len; statActual = statLength; /* change timeout */ sanei_usb_set_timeout(timeout ? timeout : USB_PACKET_TIMEOUT); /* build statBuffer */ statBuffer = calloc(statLength,1); if(!statBuffer){ DBG(5,"stat: no mem\n"); return SANE_STATUS_NO_MEM; } DBG(25, "stat: reading %d bytes, timeout %d\n", (int)statLength, actTimeout); ret = sanei_usb_read_bulk(s->fd, statBuffer, &statActual); DBG(25, "stat: read %d bytes, retval %d\n", (int)statActual, ret); hexdump(30, "stat: <<", statBuffer, statActual); /*weird status*/ if(ret != SANE_STATUS_GOOD){ DBG(5,"stat: clearing error '%s'\n",sane_strstatus(ret)); ret = do_usb_clear(s,1,runRS); } /*short read*/ else if(statLength != statActual){ DBG(5,"stat: clearing short %d/%d\n",(int)statLength,(int)statActual); ret = do_usb_clear(s,1,runRS); } /*inspect the status byte of the response*/ else if(statBuffer[statOffset]){ DBG(5,"stat: status %d\n",statBuffer[statOffset]); ret = do_usb_clear(s,0,runRS); } /*extract the extra length byte of the response*/ if(s->extra_status){ *extraLength = get_ES_length(statBuffer); DBG(15,"stat: extra %d\n",(int)*extraLength); } free(statBuffer); return ret; } static SANE_Status do_usb_clear(struct scanner *s, int clear, int runRS) { SANE_Status ret, ret2; DBG (10, "do_usb_clear: start\n"); usleep(100000); if(clear){ DBG (15, "do_usb_clear: clear halt\n"); ret = sanei_usb_clear_halt(s->fd); if(ret != SANE_STATUS_GOOD){ DBG(5,"do_usb_clear: can't clear halt, returning %d\n", ret); return ret; } } /* caller is interested in having RS run on errors */ if(runRS){ unsigned char rs_cmd[REQUEST_SENSE_len]; size_t rs_cmdLen = REQUEST_SENSE_len; unsigned char rs_in[RS_return_size]; size_t rs_inLen = RS_return_size; memset(rs_cmd,0,rs_cmdLen); set_SCSI_opcode(rs_cmd, REQUEST_SENSE_code); set_RS_return_size(rs_cmd, rs_inLen); DBG(25,"rs sub call >>\n"); ret2 = do_cmd( s, 0, 0, rs_cmd, rs_cmdLen, NULL,0, rs_in, &rs_inLen ); DBG(25,"rs sub call <<\n"); if(ret2 == SANE_STATUS_EOF){ DBG(5,"rs: got EOF, returning IO_ERROR\n"); return SANE_STATUS_IO_ERROR; } if(ret2 != SANE_STATUS_GOOD){ DBG(5,"rs: return error '%s'\n",sane_strstatus(ret2)); return ret2; } /* parse the rs data */ ret2 = sense_handler( 0, rs_in, (void *)s ); DBG (10, "do_usb_clear: finish after RS\n"); return ret2; } DBG (10, "do_usb_clear: finish with io error\n"); return SANE_STATUS_IO_ERROR; } static SANE_Status wait_scanner(struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[TEST_UNIT_READY_len]; size_t cmdLen = TEST_UNIT_READY_len; DBG (10, "wait_scanner: start\n"); memset(cmd,0,cmdLen); set_SCSI_opcode(cmd,TEST_UNIT_READY_code); ret = do_cmd ( s, 0, s->tur_timeout, cmd, cmdLen, NULL, 0, NULL, NULL ); if (ret != SANE_STATUS_GOOD) { DBG(5,"WARNING: Brain-dead scanner. Hitting with stick.\n"); ret = do_cmd ( s, 0, s->tur_timeout, cmd, cmdLen, NULL, 0, NULL, NULL ); } if (ret != SANE_STATUS_GOOD) { DBG(5,"WARNING: Brain-dead scanner. Hitting with stick again.\n"); ret = do_cmd ( s, 0, s->tur_timeout, cmd, cmdLen, NULL, 0, NULL, NULL ); } // some scanners (such as DR-F120) are OK but will not respond to commands // when in sleep mode. By checking the sense it wakes them up. if (ret != SANE_STATUS_GOOD) { DBG(5,"WARNING: Brain-dead scanner. Hitting with stick and request sense.\n"); ret = do_cmd ( s, 1, s->tur_timeout, cmd, cmdLen, NULL, 0, NULL, NULL ); } if (ret != SANE_STATUS_GOOD) { DBG(5,"WARNING: Brain-dead scanner. Hitting with stick a fourth time.\n"); ret = do_cmd ( s, 0, s->tur_timeout, cmd, cmdLen, NULL, 0, NULL, NULL ); } if (ret != SANE_STATUS_GOOD) { DBG (5, "wait_scanner: error '%s'\n", sane_strstatus (ret)); } DBG (10, "wait_scanner: finish (status=%d)\n", ret); return ret; } /* Some scanners have per-resolution * color interlacing values, but most * don't. This helper can tell the * difference. */ static int get_color_inter(struct scanner *s, int side, int res) { int i; for(i=0;icolor_inter_by_res[i]) return s->color_inter_by_res[i]; return s->color_interlace[side]; } /* s->u.page_x stores the user setting * for the paper width in adf. sometimes, * we need a value that differs from this * due to using FB or overscan. */ static int get_page_width(struct scanner *s) { int width = s->u.page_x; /* scanner max for fb */ if(s->u.source == SOURCE_FLATBED){ return s->max_x_fb; } /* can't overscan larger than scanner max */ if(width > s->valid_x){ return s->valid_x; } /* overscan adds a margin to both sides */ return width; } /* s->u.page_y stores the user setting * for the paper height in adf. sometimes, * we need a value that differs from this * due to using FB or overscan. */ static int get_page_height(struct scanner *s) { int height = s->u.page_y; /* scanner max for fb */ if(s->u.source == SOURCE_FLATBED){ return s->max_y_fb; } /* can't overscan larger than scanner max */ if(height > s->max_y){ return s->max_y; } /* overscan adds a margin to both sides */ return height; } /** * 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; } return max_size; } /* * 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[70]; /* 'xxx: xx xx ... xx xx abc */ char *hex = line+4; char *bin = line+53; if(DBG_LEVEL < level) return; line[0] = 0; DBG (level, "%s\n", comment); for (i = 0; i < l; i++, p++) { /* at start of line */ if ((i % 16) == 0) { /* not at start of first line, print current, reset */ if (i) { DBG (level, "%s\n", line); } memset(line,0x20,69); line[69] = 0; hex = line + 4; bin = line + 53; sprintf (line, "%3.3x:", i); } /* the hex section */ sprintf (hex, " %2.2x", *p); hex += 3; *hex = ' '; /* the char section */ if(*p >= 0x20 && *p <= 0x7e){ *bin=*p; } else{ *bin='.'; } bin++; } /* print last (partial) line */ if (i) DBG (level, "%s\n", line); } /** * 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 (15, "%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) { DBG (10, "sane_get_select_fd\n"); DBG (15, "%p %d\n", h, *fdp); return SANE_STATUS_UNSUPPORTED; } /* * @@ Section 8 - Image processing functions */ /* Look in image for likely upper and left paper edges, then rotate * image so that upper left corner of paper is upper left of image. * FIXME: should we do this before we binarize instead of after? */ static SANE_Status buffer_deskew(struct scanner *s, int side) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char bg_color = calc_bg_color(s); DBG (10, "buffer_deskew: start\n"); ret = sane_get_parameters((SANE_Handle) s, &s->s_params); /*only find skew on first image from a page, or if first image had error */ if(s->side == SIDE_FRONT || s->u.source == SOURCE_ADF_BACK || s->deskew_stat){ s->deskew_stat = sanei_magic_findSkew( &s->s_params,s->buffers[side],s->u.dpi_x,s->u.dpi_y, &s->deskew_vals[0],&s->deskew_vals[1],&s->deskew_slope); if(s->deskew_stat){ DBG (5, "buffer_deskew: bad findSkew, bailing\n"); goto cleanup; } } /* backside images can use a 'flipped' version of frontside data */ else{ s->deskew_slope *= -1; s->deskew_vals[0] = s->s_params.pixels_per_line - s->deskew_vals[0]; } ret = sanei_magic_rotate(&s->s_params,s->buffers[side], s->deskew_vals[0],s->deskew_vals[1],s->deskew_slope,bg_color); if(ret){ DBG(5,"buffer_deskew: rotate error: %d",ret); ret = SANE_STATUS_GOOD; goto cleanup; } cleanup: DBG (10, "buffer_deskew: finish\n"); return ret; } /* Look in image for likely left/right/bottom paper edges, then crop * image to match. Does not attempt to rotate the image. * FIXME: should we do this before we binarize instead of after? */ static SANE_Status buffer_crop(struct scanner *s, int side) { SANE_Status ret = SANE_STATUS_GOOD; DBG (10, "buffer_crop: start\n"); ret = sane_get_parameters((SANE_Handle) s, &s->s_params); ret = sanei_magic_findEdges( &s->s_params,s->buffers[side],s->u.dpi_x,s->u.dpi_y, &s->crop_vals[0],&s->crop_vals[1],&s->crop_vals[2],&s->crop_vals[3]); if(ret){ DBG (5, "buffer_crop: bad edges, bailing\n"); ret = SANE_STATUS_GOOD; goto cleanup; } DBG (15, "buffer_crop: t:%d b:%d l:%d r:%d\n", s->crop_vals[0],s->crop_vals[1],s->crop_vals[2],s->crop_vals[3]); /* if we will later binarize this image, make sure the width * is a multiple of 8 pixels, by adjusting the right side */ if ( must_downsample(s) && s->u.mode < MODE_GRAYSCALE ){ s->crop_vals[3] -= (s->crop_vals[3]-s->crop_vals[2]) % 8; } /* now crop the image */ ret = sanei_magic_crop(&s->s_params,s->buffers[side], s->crop_vals[0],s->crop_vals[1],s->crop_vals[2],s->crop_vals[3]); if(ret){ DBG (5, "buffer_crop: bad crop, bailing\n"); ret = SANE_STATUS_GOOD; goto cleanup; } /* need to update user with new size */ s->i.width = s->s_params.pixels_per_line; s->i.height = s->s_params.lines; s->i.Bpl = s->s_params.bytes_per_line; /* update image size counter to new, smaller size */ s->i.bytes_tot[side] = s->s_params.lines * s->s_params.bytes_per_line; s->i.bytes_sent[side] = s->i.bytes_tot[side]; s->u.bytes_sent[side] = 0; cleanup: DBG (10, "buffer_crop: finish\n"); return ret; } /* Look in image for disconnected 'spots' of the requested size. * Replace the spots with the average color of the surrounding pixels. * FIXME: should we do this before we binarize instead of after? */ static SANE_Status buffer_despeck(struct scanner *s, int side) { SANE_Status ret = SANE_STATUS_GOOD; DBG (10, "buffer_despeck: start\n"); ret = sane_get_parameters((SANE_Handle) s, &s->s_params); ret = sanei_magic_despeck(&s->s_params,s->buffers[side],s->swdespeck); if(ret){ DBG (5, "buffer_despeck: bad despeck, bailing\n"); ret = SANE_STATUS_GOOD; goto cleanup; } cleanup: DBG (10, "buffer_despeck: finish\n"); return ret; } /* Look if image has too few dark pixels.*/ static int buffer_isblank(struct scanner *s, int side) { SANE_Status ret = SANE_STATUS_GOOD; int status = 0; DBG (10, "buffer_isblank: start\n"); ret = sane_get_parameters((SANE_Handle) s, &s->s_params); ret = sanei_magic_isBlank2(&s->s_params, s->buffers[side], s->u.dpi_x, s->u.dpi_y, s->swskip); if(ret == SANE_STATUS_NO_DOCS){ DBG (5, "buffer_isblank: blank!\n"); status = 1; } else if(ret){ DBG (5, "buffer_isblank: error %d\n",ret); } DBG (10, "buffer_isblank: finished\n"); return status; } /* certain options require the entire image to * be collected from the scanner before we can * tell the user the size of the image. */ static int must_fully_buffer(struct scanner *s) { if( (s->swdeskew || s->swdespeck || s->swcrop) && s->s.format != SANE_FRAME_JPEG ){ return 1; } return 0; } /* certain scanners require the mode of the * image to be changed in software. */ static int must_downsample(struct scanner *s) { if(s->s.mode != s->i.mode && s->compress != COMP_JPEG ){ return 1; } return 0; } /* Function to build a lookup table (LUT), often used by scanners to implement brightness/contrast/gamma or by backends to speed binarization/thresholding offset and slope inputs are -127 to +127 slope rotates line around central input/output val, 0 makes horizontal line pos zero neg . x . . x . x . . x out . x .xxxxxxxxxxx . x . x . . x ....x....... ............ .......x.... in in in offset moves line vertically, and clamps to output range 0 keeps the line crossing the center of the table pos zero neg . xxxxxxxx . xx . . x . x . out x . x . x . . x . x ............ xx.......... xxxxxxxx.... in in out_min/max provide bounds on output values, useful when building thresholding lut. 0 and 255 are good defaults otherwise. */ static SANE_Status load_lut (unsigned char * lut, int in_bits, int out_bits, int out_min, int out_max, int slope, int offset) { SANE_Status ret = SANE_STATUS_GOOD; int i, j; double shift, rise; int max_in_val = (1 << in_bits) - 1; int max_out_val = (1 << out_bits) - 1; unsigned char * lut_p = lut; DBG (10, "load_lut: start %d %d\n", slope, offset); /* slope is converted to rise per unit run: * first [-127,127] to [-.999,.999] * then to [-PI/4,PI/4] then [0,PI/2] * then take the tangent (T.O.A) * then multiply by the normal linear slope * because the table may not be square, i.e. 1024x256*/ rise = tan((double)slope/128 * M_PI_4 + M_PI_4) * max_out_val / max_in_val; /* line must stay vertically centered, so figure * out vertical offset at central input value */ shift = (double)max_out_val/2 - (rise*max_in_val/2); /* convert the user offset setting to scale of output * first [-127,127] to [-1,1] * then to [-max_out_val/2,max_out_val/2]*/ shift += (double)offset / 127 * max_out_val / 2; for(i=0;i<=max_in_val;i++){ j = rise*i + shift; if(jout_max){ j=out_max; } *lut_p=j; lut_p++; } hexdump(5, "load_lut: ", lut, max_in_val+1); DBG (10, "load_lut: finish\n"); return ret; } backends-1.3.0/backend/canon_dr.conf.in000066400000000000000000000100161456256263500177270ustar00rootroot00000000000000####################################################################### # NOTE: 'option' lines only apply to the devices found by # the NEXT 'usb' or 'scsi' line. You may repeat the option line if # required for multiple scanners of different models/connections. ####################################################################### # Some machines are incapable of providing basic inquiry info, and will # lock up if asked for it. The driver will not ask for this info if all # three of these options are provided. They should NOT be used unless # you know for sure that your machine requires it. # NOTE: the vendor and model must be correct. The version need not. #option vendor-name CANON #option model-name DR-2050C #option version-name XXXX ####################################################################### # Set data buffer size, in bytes. The value ranges from 4096 - infinity # Large values may cause timeouts, or long pauses at the end of each # page. Small values may cause slow scans. 2MB is the default. #option buffer-size 2097152 ####################################################################### # Most scanners don't pad their reads #option padded-read 0 ####################################################################### # SCSI scanners: # To search for any CANON scsi device, if name starts with 'CR' or 'DR' scsi CANON CR scsi CANON DR # To use a specific scsi device #scsi /dev/sg1 ####################################################################### # USB scanners: # For Canon scanners connected via USB on a known device (kernel driver): #usb /dev/usb/scanner0 # For Canon 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. kitno455 at # gmail dot com - with canon_dr in the subject line # DR-2080C (uses weird protocol) option duplex-offset 840 option vendor-name CANON option model-name DR-2080C option version-name XXXX option padded-read 1 usb 0x04a9 0x1601 # CR-180 usb 0x04a9 0x1602 # DR-9080C usb 0x04a9 0x1603 # DR-7080C usb 0x04a9 0x1604 # DR-5010C usb 0x04a9 0x1606 # DR-6080C usb 0x04a9 0x1607 # DR-2580C option duplex-offset 432 usb 0x04a9 0x1608 # DR-3080CII option padded-read 1 usb 0x04a9 0x1609 # DR-2050C/SP (uses weird protocol) option duplex-offset 840 option vendor-name CANON option model-name DR-2050C option version-name XXXX option padded-read 1 usb 0x04a9 0x160a # DR-7580 usb 0x04a9 0x160b # CR-55 usb 0x1083 0x160c # DR-1210C (two versions?) usb 0x1083 0x160f usb 0x04a9 0x2222 # DR-4010C usb 0x1083 0x1614 # DR-2510C option duplex-offset 400 usb 0x1083 0x1617 # DR-X10C option tur-timeout 5000 usb 0x1083 0x1618 # CR-25 usb 0x1083 0x161a # DR-2010C option duplex-offset 400 usb 0x1083 0x161b # DR-3010C option duplex-offset 400 usb 0x1083 0x161d # DR-7090C usb 0x1083 0x1620 # DR-9050C usb 0x1083 0x1622 # DR-7550C usb 0x1083 0x1623 # DR-6050C usb 0x1083 0x1624 # DR-6010C usb 0x1083 0x1626 # CR-190i usb 0x1083 0x162b # P-150M usb 0x1083 0x162c # DR-6030C usb 0x1083 0x1638 # CR-135i usb 0x1083 0x1639 # DR-M160 option extra-status 1 option duplex-offset 400 usb 0x1083 0x163e # DR-M140 option extra-status 1 option duplex-offset 400 usb 0x1083 0x163f # DR-C125 option duplex-offset 400 usb 0x1083 0x1640 # DR-P215 usb 0x1083 0x1641 # P-215 usb 0x1083 0x1646 # FSU-201 usb 0x1083 0x1648 # DR-C130 usb 0x1083 0x164a # DR-P208 usb 0x1083 0x164b # P-208 option duplex-offset 260 usb 0x1083 0x164c # DR-G1130 option buffer-size 8000000 usb 0x1083 0x164f # DR-G1100 option buffer-size 8000000 usb 0x1083 0x1650 # DR-C120 usb 0x1083 0x1651 # P-201 usb 0x1083 0x1652 # DR-F120 option duplex-offset 1640 usb 0x1083 0x1654 # DR-M1060 option inquiry-length 0x24 option vpd-length 0x30 usb 0x1083 0x1657 # DR-C225 usb 0x1083 0x1658 # DR-P215II usb 0x1083 0x1659 # P-215II usb 0x1083 0x165b # DR-P208II usb 0x1083 0x165d # P-208II usb 0x1083 0x165f # R40 option duplex-offset 320 usb 0x1083 0x1679 backends-1.3.0/backend/canon_dr.h000066400000000000000000000541341456256263500166350ustar00rootroot00000000000000#ifndef CANON_DR_H #define CANON_DR_H /* * Part of SANE - Scanner Access Now Easy. * Please see opening comments in canon_dr.c */ /* ------------------------------------------------------------------------- * 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 * still the possibility to say so, later. */ enum scanner_Option { OPT_NUM_OPTS = 0, OPT_STANDARD_GROUP, OPT_SOURCE, /*fb/adf/front/back/duplex*/ OPT_MODE, /*mono/gray/color*/ OPT_RES, /*a range or a list*/ OPT_GEOMETRY_GROUP, OPT_PAGE_WIDTH, OPT_PAGE_HEIGHT, OPT_TL_X, OPT_TL_Y, OPT_BR_X, OPT_BR_Y, OPT_ENHANCEMENT_GROUP, OPT_BRIGHTNESS, OPT_CONTRAST, OPT_THRESHOLD, OPT_RIF, OPT_ADVANCED_GROUP, OPT_COMPRESS, OPT_COMPRESS_ARG, OPT_DF_THICKNESS, OPT_DF_LENGTH, OPT_ROLLERDESKEW, OPT_SWDESKEW, OPT_SWDESPECK, OPT_SWCROP, OPT_SWSKIP, OPT_STAPLEDETECT, OPT_DROPOUT_COLOR_F, OPT_DROPOUT_COLOR_B, OPT_BUFFERMODE, OPT_SIDE, OPT_HW_CROP, /*imprinter option group*/ OPT_IMPRINT_GROUP, OPT_PRE_IMPRINT_SPECSTRING, OPT_PRE_IMPRINT_H_OFFSET, OPT_PRE_IMPRINT_V_OFFSET, OPT_PRE_IMPRINT_FONT_SIZE, OPT_PRE_IMPRINT_FONT_ROT, OPT_PRE_IMPRINT_SPACING, OPT_POST_IMPRINT_SPECSTRING, OPT_POST_IMPRINT_H_OFFSET, OPT_POST_IMPRINT_V_OFFSET, OPT_POST_IMPRINT_FONT_SIZE, OPT_POST_IMPRINT_FONT_ROT, OPT_POST_IMPRINT_SPACING, OPT_POST_IMPRINT_ADDON_MODE, /*sensor group*/ OPT_SENSOR_GROUP, OPT_START, OPT_STOP, OPT_BUTT3, OPT_NEWFILE, OPT_COUNTONLY, OPT_BYPASSMODE, OPT_COUNTER, OPT_ROLLERCOUNTER, OPT_TOTALCOUNTER, OPT_ADF_LOADED, OPT_CARD_LOADED, /* must come last: */ NUM_OPTIONS }; struct img_params { int mode; /*color,lineart,etc*/ int source; /*fb,adf front,adf duplex,etc*/ int dpi_x; /*these are in dpi */ int dpi_y; int tl_x; /*these are in 1200dpi units */ int tl_y; int br_x; int br_y; int page_x; int page_y; int width; /*these are in pixels*/ int height; SANE_Frame format; /*SANE_FRAME_**/ int bpp; /* 1,8,24 */ int Bpl; /* in bytes */ int valid_width; /*some machines have black padding*/ int valid_Bpl; /* done yet? */ int eof[2]; /* how far we have read/written */ int bytes_sent[2]; /* total to read/write */ int bytes_tot[2]; /* dumb scanners send extra data */ int skip_lines[2]; }; struct imprint_params { /* ------------------------------------------------------------------------------ */ /* allowed values for post imprinter (in mm units): */ /* 21, 35, 47, 59, 72, 99, 114, 143, 155, 167, 196, 211, 239, 251, 263, 275, 289 */ /* allowed values for pre imprinter (in mm units): */ /* 14, 28, 41, 53, 65, 106 */ int h_offset; /* --------------------------------------- */ /* allowed values: 0 to 500 (in mm units) */ int v_offset; int font_size; int font_rot; int spacing; #define IMPRINT_SPECSTRING_LEN 64 /* ---------------------------------- */ /* special tokens: */ /* \TIME time in HH:MM:SS */ /* \DMY date in DD/MM/YYYY */ /* \YMD date in YYYY/MM/DD */ /* [[0-9]+] scan page count */ char specstring[IMPRINT_SPECSTRING_LEN]; }; struct scanner { /* --------------------------------------------------------------------- */ /* immutable values which are set during init of scanner. */ struct scanner *next; char device_name[1024]; /* The name of the device from sanei */ int missing; /* used to mark unplugged scanners */ /* --------------------------------------------------------------------- */ /* immutable values which are set during reading of config file. */ int connection; /* hardware interface type */ int buffer_size; int duplex_offset; /* number of lines of padding added to front (1/1200)*/ int extra_status; /* some machines need extra status read after cmd */ int inquiry_length; /* some models are particular about inquiry size */ int padded_read; /* some machines need extra 12 bytes on reads */ int tur_timeout; /* some models are slow to wake up */ int vpd_length; /* some models are particular about vpd size */ /* --------------------------------------------------------------------- */ /* immutable values which are set during inquiry probing of the scanner. */ /* members in order found in scsi data. These can also be in config file */ char vendor_name[9]; /* raw data as returned by SCSI inquiry. */ char model_name[17]; /* raw data as returned by SCSI inquiry. */ char version_name[5]; /* raw data as returned by SCSI inquiry. */ /* --------------------------------------------------------------------- */ /* 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_x[16]; int std_res_y[16]; /* max scan size in pixels converted to 1200dpi units */ int max_x; int max_y; /*FIXME: 4 more unknown values here*/ int can_grayscale; int can_halftone; int can_monochrome; int can_overflow; /* --------------------------------------------------------------------- */ /* immutable values which are hard coded because they are not in vpd */ int brightness_steps; int threshold_steps; int contrast_steps; int ppl_mod; /* modulus of scanline width */ /* the scan size in 1/1200th inches, NOT basic_units or sane units */ int min_x; int min_y; int valid_x; int max_x_fb; int max_y_fb; int can_color; /* actually might be in vpd, but which bit? */ int need_ccal; /* scanner needs software to help with afe calibration */ int ccal_version; /* 0 in most scanners, 3 in newer ones */ int fcal_src; /* where fine offset/gain calibration data comes from */ int fcal_dest; /* where fine offset/gain calibration data is used */ #define FCAL_SRC_NONE 0 /* scanner does not require fine calibration */ #define FCAL_SRC_SCAN 1 /* make calibration scans, store gain/offset in struct */ #define FCAL_SRC_HW 2 /* calibration permanently stored in scanner, downloaded into struct */ #define FCAL_DEST_NONE 0 /* scanner does not require fine calibration */ #define FCAL_DEST_SW 1 /* use gain/offset in struct to adjust output in software */ #define FCAL_DEST_HW 2 /* send calibration data into scanner for use in hardware */ int has_counter; int has_rif; int has_adf; int has_flatbed; int has_duplex; int has_back; /* not all duplex scanners can do adf back side only */ int has_card; /* P215 has a card reader instead of fb */ int has_comp_JPEG; int has_buffer; int has_df; int has_df_ultra; int has_btc; int has_ssm; /* older scanners use this set scan mode command */ int has_ssm2; /* newer scanners user this similar command */ int has_ssm_pay_head_len; /* newer scanners put the length twice in ssm */ int has_hwcrop; int has_pre_imprinter; int has_post_imprinter; int can_read_sensors; int can_read_panel; int can_write_panel; int can_read_lifecycle_counters; int rgb_format; /* meaning unknown */ int padding; /* meaning unknown */ int always_op; /* send object pos between pages */ int invert_tly; /* weird bug in some smaller scanners */ int unknown_byte2; /* weird byte, required, meaning unknown */ int fixed_width; /* some machines always scan full width */ int even_Bpl; /* some machines require even bytes per line */ int gray_interlace[2]; /* different models interlace heads differently */ int color_interlace[2]; /* different models interlace colors differently */ int color_inter_by_res[16]; /* and some even change by resolution */ int duplex_interlace; /* different models interlace sides differently */ int jpeg_interlace; /* different models interlace jpeg sides differently */ int duplex_offset_side; /* padding added to front or back? */ int sw_lut; /* no hardware brightness/contrast support */ int bg_color; /* needed to fill in after rotation */ int reverse_by_mode[6]; /* mode specific */ /* --------------------------------------------------------------------- */ /* immutable values which are set during serial number probing scanner */ char serial_name[28]; /* 16 char model, ':', 10 byte serial, null */ /* --------------------------------------------------------------------- */ /* struct with pointers to device/vendor/model names, and a type value */ /* used to inform sane frontend about the device */ SANE_Device sane; /* --------------------------------------------------------------------- */ /* 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 */ /* long array of option structs */ SANE_Option_Descriptor opt[NUM_OPTIONS]; /*mode group*/ SANE_String_Const mode_list[7]; SANE_String_Const source_list[8]; SANE_Int res_list[17]; SANE_Range res_range; /*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; /*enhancement group*/ SANE_Range brightness_range; SANE_Range contrast_range; SANE_Range threshold_range; /*advanced group*/ SANE_String_Const compress_list[3]; SANE_Range compress_arg_range; SANE_Range swdespeck_range; SANE_Range swskip_range; SANE_String_Const do_color_list[8]; /*sensor group*/ SANE_Range counter_range; /* --------------------------------------------------------------------- */ /* changeable vars to hold user input. modified by SANE_Options above */ /* the user's requested image params */ /* exposed in standard and geometry option groups */ struct img_params u; /*enhancement group*/ int brightness; int contrast; int threshold; int rif; /*advanced group*/ int compress; int compress_arg; int df_length; int df_thickness; int dropout_color[2]; int buffermode; int rollerdeskew; int swdeskew; int swdespeck; int swcrop; int swskip; int stapledetect; int hwcrop; /* --------------------------------------------------------------------- */ /* values which are derived from setting the options above */ /* the user never directly modifies these */ /* the scanner image params (what we ask from scanner) */ struct img_params s; /* the intermediate image params (like user, but possible higher depth) */ struct img_params i; /* the brightness/contrast LUT for dumb scanners */ unsigned char lut[256]; /* --------------------------------------------------------------------- */ /* values used by the software enhancement code (deskew, crop, etc) */ SANE_Status deskew_stat; int deskew_vals[2]; double deskew_slope; int crop_vals[4]; /* imprinter params */ struct imprint_params pre_imprint; struct imprint_params post_imprint; enum { /*Black-on-White*/ ADDON_BoW = 0, /*White-on-Black*/ ADDON_WoB, /*Black-on-Image*/ ADDON_BoI, ADDON_DISABLED } post_imprint_addon_mode; /* imprinter param constraints */ SANE_Int pre_imprinter_h_offset_list[7]; SANE_Int post_imprinter_h_offset_list[18]; SANE_Range imprinter_v_offset_range; SANE_String_Const imprint_font_size_list[3]; SANE_Int imprinter_font_angle_list[5]; SANE_String_Const imprint_addon_mode_list[5]; /* 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 s_params; /* --------------------------------------------------------------------- */ /* values which are set by calibration functions */ int c_res; int c_mode; int c_offset[2]; int c_gain[2]; int c_exposure[2][3]; int f_res; int f_mode; unsigned char * f_offset[2]; unsigned char * f_gain[2]; /* --------------------------------------------------------------------- */ /* values which are set by scanning functions to keep track of pages, etc */ int started; int reading; int cancelled; int side; int prev_page; int jpeg_stage; int jpeg_ff_offset; unsigned char * buffers[2]; /* --------------------------------------------------------------------- */ /* values used by the command and data sending functions (scsi/usb) */ int fd; /* The scanner device file descriptor. */ size_t rs_info; /* --------------------------------------------------------------------- */ /* values used to hold hardware or control panel status */ int panel_start; int panel_stop; int panel_butt3; int panel_new_file; int panel_count_only; int panel_bypass_mode; int panel_enable_led; int panel_counter; int sensor_adf_loaded; int sensor_card_loaded; int roller_counter; int total_counter; /* values which are used to track the frontend's access to sensors */ char panel_read[OPT_COUNTER - OPT_START + 1]; char sensors_read[OPT_CARD_LOADED - OPT_ADF_LOADED + 1]; }; #define CONNECTION_SCSI 0 /* SCSI interface */ #define CONNECTION_USB 1 /* USB interface */ #define SIDE_FRONT 0 #define SIDE_BACK 1 #define CHAN_RED 0 #define CHAN_GREEN 1 #define CHAN_BLUE 2 #define SOURCE_FLATBED 0 #define SOURCE_ADF_FRONT 1 #define SOURCE_ADF_BACK 2 #define SOURCE_ADF_DUPLEX 3 #define SOURCE_CARD_FRONT 4 #define SOURCE_CARD_BACK 5 #define SOURCE_CARD_DUPLEX 6 static const int dpi_list[] = { 60,75,100,120,150,160,180,200, 240,300,320,400,480,600,800,1200 }; #define DPI_60 0 #define DPI_75 1 #define DPI_100 2 #define DPI_120 3 #define DPI_150 4 #define DPI_160 5 #define DPI_180 6 #define DPI_200 7 #define DPI_240 8 #define DPI_300 9 #define DPI_320 10 #define DPI_400 11 #define DPI_480 12 #define DPI_600 13 #define DPI_800 14 #define DPI_1200 15 #define COMP_NONE WD_cmp_NONE #define COMP_JPEG WD_cmp_JPEG #define JPEG_STAGE_NONE 0 #define JPEG_STAGE_SOF 1 /* 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 WD_comp_CG enum { COLOR_NONE = 0, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_EN_RED, COLOR_EN_GREEN, COLOR_EN_BLUE }; /* these are same as scsi data to make code easier */ #define COLOR_WHITE 1 #define COLOR_BLACK 2 #define GRAY_INTERLACE_NONE 0 #define GRAY_INTERLACE_2510 1 #define GRAY_INTERLACE_gG 2 #define GRAY_INTERLACE_C120 3 #define COLOR_INTERLACE_UNK 0 #define COLOR_INTERLACE_RGB 1 #define COLOR_INTERLACE_BGR 2 #define COLOR_INTERLACE_BRG 3 #define COLOR_INTERLACE_GBR 4 #define COLOR_INTERLACE_RRGGBB 5 #define COLOR_INTERLACE_rRgGbB 6 #define COLOR_INTERLACE_2510 7 #define COLOR_INTERLACE_C120 8 #define DUPLEX_INTERLACE_NONE 0 #define DUPLEX_INTERLACE_FfBb 1 #define DUPLEX_INTERLACE_FBfb 2 #define DUPLEX_INTERLACE_2510 3 #define DUPLEX_INTERLACE_fFBb 4 #define DUPLEX_INTERLACE_PER_CHANNEL 5 #define JPEG_INTERLACE_ALT 0 #define JPEG_INTERLACE_NONE 1 #define CROP_RELATIVE 0 #define CROP_ABSOLUTE 1 /* ------------------------------------------------------------------------- */ #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 CANON_DR_CONFIG_FILE "canon_dr.conf" #ifndef PATH_MAX # define PATH_MAX 1024 #endif /* ------------------------------------------------------------------------- */ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize); SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only); SANE_Status sane_open (SANE_String_Const name, SANE_Handle * handle); SANE_Status sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking); SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int * fdp); 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, SANE_Action action, void *val, SANE_Int * info); SANE_Status sane_start (SANE_Handle handle); SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params); SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len); void sane_cancel (SANE_Handle h); void sane_close (SANE_Handle h); void sane_exit (void); /* ------------------------------------------------------------------------- */ 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 SANE_Status connect_fd (struct scanner *s); static SANE_Status disconnect_fd (struct scanner *s); static SANE_Status sense_handler (int scsi_fd, u_char * result, void *arg); static SANE_Status init_inquire (struct scanner *s); static SANE_Status init_vpd (struct scanner *s); static SANE_Status init_model (struct scanner *s); static SANE_Status init_imprinters (struct scanner *s); static SANE_Status init_panel (struct scanner *s); static SANE_Status init_counters (struct scanner *s); static SANE_Status init_user (struct scanner *s); static SANE_Status init_options (struct scanner *s); static SANE_Status do_cmd(struct scanner *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 scanner *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 scanner *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_status(struct scanner *s, int runRS, int shortTime, size_t * extraLength); static SANE_Status do_usb_clear(struct scanner *s, int clear, int runRS); static SANE_Status wait_scanner (struct scanner *s); static SANE_Status object_position (struct scanner *s, int i_load); static SANE_Status ssm_buffer (struct scanner *s); static SANE_Status ssm_do (struct scanner *s); static SANE_Status ssm_df (struct scanner *s); static int get_color_inter(struct scanner *s, int side, int res); static int get_page_width (struct scanner *s); static int get_page_height (struct scanner *s); static SANE_Status set_window (struct scanner *s); static SANE_Status update_params (struct scanner *s, int calib); static SANE_Status update_i_params (struct scanner *s); static SANE_Status clean_params (struct scanner *s); static SANE_Status read_counters(struct scanner *s); static SANE_Status read_sensors(struct scanner *s, SANE_Int option); static SANE_Status read_panel(struct scanner *s, SANE_Int option); static SANE_Status send_panel(struct scanner *s); static SANE_Status start_scan (struct scanner *s, int type); static SANE_Status check_for_cancel(struct scanner *s); static SANE_Status read_from_scanner(struct scanner *s, int side, int exact); static SANE_Status read_from_scanner_duplex(struct scanner *s, int exact); static SANE_Status copy_simplex(struct scanner *s, unsigned char * buf, int len, int side); static SANE_Status copy_duplex(struct scanner *s, unsigned char * buf, int len); static SANE_Status copy_line(struct scanner *s, unsigned char * buf, int side); static SANE_Status fill_image(struct scanner *s,int side); static int must_downsample (struct scanner *s); static int must_fully_buffer (struct scanner *s); static unsigned char calc_bg_color(struct scanner *s); static SANE_Status buffer_despeck(struct scanner *s, int side); static SANE_Status buffer_deskew(struct scanner *s, int side); static SANE_Status buffer_crop(struct scanner *s, int side); static int buffer_isblank(struct scanner *s, int side); static SANE_Status load_lut (unsigned char * lut, int in_bits, int out_bits, int out_min, int out_max, int slope, int offset); static SANE_Status read_from_buffer(struct scanner *s, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len, int side); static SANE_Status image_buffers (struct scanner *s, int setup); static SANE_Status offset_buffers (struct scanner *s, int setup); static SANE_Status gain_buffers (struct scanner *s, int setup); static SANE_Status calibrate_AFE(struct scanner *s); static SANE_Status calibrate_fine(struct scanner *s); static SANE_Status calibrate_fine_src_scan(struct scanner *s); static SANE_Status calibrate_fine_src_hw(struct scanner *s); static SANE_Status calibrate_fine_dest_hw(struct scanner *s); static SANE_Status write_AFE (struct scanner *s); static SANE_Status calibration_scan (struct scanner *s, int); static SANE_Status send_imprint_positioning(struct scanner* s, int is_postimprint, int enabled); static SANE_Status send_imprint_specstring(struct scanner* s, int is_postimprint); static SANE_Status send_imprint_date_and_time(struct scanner* s); static SANE_Status load_imprinting_settings(struct scanner *s); static SANE_Status detect_imprinter(struct scanner *s, SANE_Int option); static void hexdump (int level, char *comment, unsigned char *p, int l); static void default_globals (void); static size_t maxStringSize (const SANE_String_Const strings[]); static void rmemcpy(void* dest, const void* src, size_t count, size_t stride); #endif /* CANON_DR_H */ backends-1.3.0/backend/canon_lide70-common.c000066400000000000000000002506511456256263500205770ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. BACKEND canon_lide70 Copyright (C) 2019-2021 Juergen Ernst and pimvantend. 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, see . This file implements a SANE backend for the Canon CanoScan LiDE 70 and 600 */ #include #include /* open */ #include #include #include #include /* usleep */ #include #include /* pow() */ #ifdef HAVE_OS2_H #include /* mode_t */ #endif #include #define USB_TYPE_VENDOR (0x02 << 5) #define USB_RECIP_DEVICE 0x00 #define USB_DIR_OUT 0x00 #define USB_DIR_IN 0x80 #define MSEC 1000 /* 1ms = 1000us */ /* Assign status and verify a good return code */ #define CHK(A) {if ((status = A) != SANE_STATUS_GOOD) {\ DBG (1, "Failure on line of %s: %d\n", \ __FILE__, __LINE__ ); return A; }} typedef SANE_Byte byte; /***************************************************** Canon LiDE70 calibration and scan ******************************************************/ /* at 600 dpi */ #define CANON_MAX_WIDTH 5104 /* 8.5in */ /* this may not be right */ #define CANON_MAX_HEIGHT 7300 /* 11.66in */ /* Just for my scanner, or is this universal? Calibrate? */ /* data structures and constants */ typedef struct CANON_Handle { /* options */ SANE_Option_Descriptor opt[num_options]; Option_Value val[num_options]; SANE_Parameters params; SANE_Word graymode; char *product; /* product name */ int productcode; /* product code, 0x2224 or 0x2225 */ int fd; /* scanner fd */ int x1, x2, y1, y2; /* in pixels, at 600 dpi */ long width, height; /* at scan resolution */ unsigned char value_08, value_09; /* left */ unsigned char value_0a, value_0b; /* right */ unsigned char value_66, value_67, value_68; /* bottom */ unsigned char value_51; /* lamp colors */ unsigned char value_90; /* motor mode */ int resolution; /* dpi */ char *fname; /* output file name */ FILE *fp; /* output file pointer (for reading) */ unsigned char absolute_threshold; double table_gamma; double table_gamma_blue; unsigned char highlight_red_enhanced; unsigned char highlight_blue_reduced; unsigned char highlight_other; } CANON_Handle; /***************************************************** CP2155 communication primitives Provides I/O routines to Philips CP2155BE chip ******************************************************/ typedef int CP2155_Register; /* Write single byte to CP2155 register */ static SANE_Status cp2155_set (int fd, CP2155_Register reg, byte data) { SANE_Status status; byte cmd_buffer[5]; size_t count = 5 /* = sizeof(cmd_buffer) */ ; cmd_buffer[0] = (reg >> 8) & 0xff; cmd_buffer[1] = (reg) & 0xff; cmd_buffer[2] = 0x01; cmd_buffer[3] = 0x00; cmd_buffer[4] = data; /* if (cmd_buffer[0]==0 && cmd_buffer[1]>0x21 && cmd_buffer[1]<0x44) { */ DBG (1, "cp2155_set %02x %02x %02x %02x %02x\n", cmd_buffer[0], cmd_buffer[1], cmd_buffer[2], cmd_buffer[3], cmd_buffer[4]); /* } */ /* */ usleep (0.0 * MSEC); /* */ status = sanei_usb_write_bulk (fd, cmd_buffer, &count); if (status != SANE_STATUS_GOOD) { DBG (1, "cp2155_set: sanei_usb_write_bulk error\n"); /* exit(0); */ } return status; } /* Read single byte from CP2155 register */ static SANE_Status cp2155_get (int fd, CP2155_Register reg, byte * data) { SANE_Status status; byte cmd_buffer[4]; size_t count = 4; /* = sizeof(cmd_buffer) */ cmd_buffer[0] = 0x01; cmd_buffer[1] = (reg) & 0xff; cmd_buffer[2] = 0x01; cmd_buffer[3] = 0x00; status = sanei_usb_write_bulk (fd, cmd_buffer, &count); if (status != SANE_STATUS_GOOD) { DBG (1, "cp2155_get: sanei_usb_write_bulk error\n"); return status; } usleep (1 * MSEC); count = 1; status = sanei_usb_read_bulk (fd, data, &count); if (status != SANE_STATUS_GOOD) { DBG (1, "cp2155_get: sanei_usb_read_bulk error\n"); } return status; } /* Read a block of data from CP2155 chip */ static SANE_Status cp2155_read (int fd, byte * data, size_t size) { SANE_Status status; byte cmd_buffer[4]; size_t count = 4; /* = sizeof(cmd_buffer) */ cmd_buffer[0] = 0x05; cmd_buffer[1] = 0x70; cmd_buffer[2] = (size) & 0xff; cmd_buffer[3] = (size >> 8) & 0xff; status = sanei_usb_write_bulk (fd, cmd_buffer, &count); if (status != SANE_STATUS_GOOD) { DBG (1, "cp2155_read: sanei_usb_write_bulk error\n"); return status; } usleep (1 * MSEC); count = size; status = sanei_usb_read_bulk (fd, data, &count); /* if (status != SANE_STATUS_GOOD) { DBG (1, "cp2155_read: sanei_usb_read_bulk error %lu\n", (u_long) count); } */ return status; } /*****************************************************/ static void cp2155_write_gamma_block (int fd, unsigned int addr, byte * data) { byte value_71 = 0x16; size_t count = 0x100; while ((count & 0x0f) != 0) { count++; } byte pgLO = (count) & 0xff; byte pgHI = (count >> 8) & 0xff; /* DBG (1, "cp2155_write_gamma_block %06x %02x %04lx %04lx\n", addr, v001, (u_long) size, (u_long) count); */ cp2155_set (fd, 0x71, 0x01); cp2155_set (fd, 0x0230, 0x11); cp2155_set (fd, 0x71, value_71); cp2155_set (fd, 0x72, pgHI); cp2155_set (fd, 0x73, pgLO); cp2155_set (fd, 0x74, (addr >> 16) & 0xff); cp2155_set (fd, 0x75, (addr >> 8) & 0xff); cp2155_set (fd, 0x76, (addr) & 0xff); cp2155_set (fd, 0x0239, 0x40); cp2155_set (fd, 0x0238, 0x89); cp2155_set (fd, 0x023c, 0x2f); cp2155_set (fd, 0x0264, 0x20); count = count + 4; sanei_usb_write_bulk (fd, data, &count); } void makegammatable (double gamma, int highlight, unsigned char *buf) { int maxin = 255; /* 8 bit gamma input */ int maxout = 255; /* 8 bit gamma output */ int in = 0; int out; buf[0] = 0x04; buf[1] = 0x70; buf[2] = 0x00; buf[3] = 0x01; while (in < highlight) { out = maxout * pow ((double) in / highlight, (1.0 / gamma)); buf[in + 4] = (unsigned char) out; in++; } while (in <= maxin) { buf[in + 4] = maxout; in++; } return; } static void cp2155_set_gamma (int fd, CANON_Handle * chndl) { DBG (1, "cp2155_set_gamma\n"); unsigned char buf[260]; /* gamma tables */ makegammatable (chndl->table_gamma, chndl->highlight_other, buf); cp2155_write_gamma_block (fd, 0x000, buf); cp2155_write_gamma_block (fd, 0x100, buf); cp2155_write_gamma_block (fd, 0x200, buf); } static void cp2155_set_gamma_red_enhanced (int fd, CANON_Handle * chndl) { DBG (1, "cp2155_set_gamma\n"); unsigned char buf[260]; /* gamma tables */ makegammatable (chndl->table_gamma, chndl->highlight_red_enhanced, buf); cp2155_write_gamma_block (fd, 0x000, buf); makegammatable (chndl->table_gamma, chndl->highlight_other, buf); cp2155_write_gamma_block (fd, 0x100, buf); makegammatable (chndl->table_gamma_blue, chndl->highlight_blue_reduced, buf); cp2155_write_gamma_block (fd, 0x200, buf); } void make_descending_slope (size_t start_descent, double coefficient, unsigned char *buf) { size_t count, position; int top_value; int value; unsigned char value_lo, value_hi; DBG (1, "start_descent = %lx\n", start_descent); top_value = buf[start_descent - 2] + 256 * buf[start_descent - 1]; DBG (1, "buf[start_descent-2] = %02x buf[start_descent-1] = %02x\n", buf[start_descent - 2], buf[start_descent - 1]); count = buf[2] + 256 * buf[3]; position = start_descent; DBG (1, "count = %ld top_value = %d\n", count, top_value); while (position < count + 4) { value = (int) (top_value / (1.0 + coefficient * (position + 2 - start_descent))); value_lo = value & 0xff; value_hi = (value >> 8) & 0xff; buf[position] = value_lo; buf[position + 1] = value_hi; DBG (1, "position = %03lx buf[position]= %02x buf[position+1] = %02x\n", position, buf[position], buf[position + 1]); position += 2; } } void make_constant_buf (size_t count, unsigned int hiword, unsigned int loword, unsigned char *buf) { size_t i = 4; unsigned char hihi = (hiword >> 8) & 0xff; unsigned char hilo = (hiword) & 0xff; unsigned char lohi = (loword >> 8) & 0xff; unsigned char lolo = (loword) & 0xff; buf[0] = 0x04; buf[1] = 0x70; buf[2] = (count - 4) & 0xff; buf[3] = ((count - 4) >> 8) & 0xff; while (i < count) { buf[i] = hilo; i++; buf[i] = hihi; i++; buf[i] = lolo; i++; buf[i] = lohi; i++; } } void make_slope_table (size_t count, unsigned int word, size_t start_descent, double coefficient, unsigned char *buf) { size_t i = 4; unsigned char hi = (word >> 8) & 0xff; unsigned char lo = (word) & 0xff; buf[0] = 0x04; buf[1] = 0x70; buf[2] = (count - 4) & 0xff; buf[3] = ((count - 4) >> 8) & 0xff; while (i < start_descent) { buf[i] = lo; i++; buf[i] = hi; i++; } make_descending_slope (start_descent, coefficient, buf); } void write_buf (int fd, size_t count, unsigned char *buf, unsigned char value_74, unsigned char value_75) { unsigned char value_72, value_73; value_72 = ((count - 4) >> 8) & 0xff; value_73 = (count - 4) & 0xff; cp2155_set (fd, 0x71, 0x01); cp2155_set (fd, 0x0230, 0x11); cp2155_set (fd, 0x71, 0x14); cp2155_set (fd, 0x72, value_72); cp2155_set (fd, 0x73, value_73); cp2155_set (fd, 0x74, value_74); cp2155_set (fd, 0x75, value_75); cp2155_set (fd, 0x76, 0x00); cp2155_set (fd, 0x0239, 0x40); cp2155_set (fd, 0x0238, 0x89); cp2155_set (fd, 0x023c, 0x2f); cp2155_set (fd, 0x0264, 0x20); sanei_usb_write_bulk (fd, buf, &count); } void big_write (int fd, size_t count, unsigned char *buf) { make_constant_buf (count, 62756, 20918, buf); write_buf (fd, count, buf, 0x00, 0x00); write_buf (fd, count, buf, 0x00, 0xb0); write_buf (fd, count, buf, 0x01, 0x60); write_buf (fd, count, buf, 0x02, 0x10); } void big_write_2224 (int fd, size_t count, unsigned char *buf) { make_constant_buf (count, 62756, 30918, buf); write_buf (fd, count, buf, 0x00, 0x00); write_buf (fd, count, buf, 0x00, 0xb0); write_buf (fd, count, buf, 0x01, 0x60); write_buf (fd, count, buf, 0x02, 0x10); } void big_write_film (int fd, size_t count, unsigned char *buf) { make_constant_buf (count, 62756, 20918, buf); write_buf (fd, count, buf, 0x00, 0x00); write_buf (fd, count, buf, 0x02, 0x00); write_buf (fd, count, buf, 0x04, 0x00); write_buf (fd, count, buf, 0x06, 0x00); } void general_motor_2225 (int fd) { cp2155_set (fd, 0x9b, 0x02); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x11, 0x91); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x03, 0x01); cp2155_set (fd, 0x71, 0x01); cp2155_set (fd, 0x0230, 0x11); cp2155_set (fd, 0x71, 0x18); cp2155_set (fd, 0x72, 0x00); cp2155_set (fd, 0x73, 0x10); cp2155_set (fd, 0x0239, 0x40); cp2155_set (fd, 0x0238, 0x89); cp2155_set (fd, 0x023c, 0x2f); cp2155_set (fd, 0x0264, 0x20); } void general_motor_2224 (int fd) { cp2155_set (fd, 0x90, 0xfa); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x11, 0x91); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x03, 0x01); cp2155_set (fd, 0x71, 0x01); cp2155_set (fd, 0x0230, 0x11); cp2155_set (fd, 0x71, 0x18); cp2155_set (fd, 0x72, 0x00); cp2155_set (fd, 0x73, 0x10); cp2155_set (fd, 0x0239, 0x40); cp2155_set (fd, 0x0238, 0x89); cp2155_set (fd, 0x023c, 0x2f); cp2155_set (fd, 0x0264, 0x20); } void register_table (int fd, unsigned char register_value, unsigned char *buf) { cp2155_set (fd, 0x1a, 0x00); cp2155_set (fd, 0x1b, 0x00); cp2155_set (fd, 0x1c, 0x02); cp2155_set (fd, 0x15, 0x80); cp2155_set (fd, 0x14, 0x7c); cp2155_set (fd, 0x17, 0x01); cp2155_set (fd, 0x43, 0x1c); cp2155_set (fd, 0x44, 0x9c); cp2155_set (fd, 0x45, 0x38); if (register_value > 0) { unsigned char register_number = 0x23; while (register_number < 0x43) { cp2155_set (fd, register_number, register_value); register_number++; } } else { int buffer_index = 0; cp2155_set (fd, 0x23 + buffer_index, buf[buffer_index]); cp2155_set (fd, 0x33 + buffer_index, buf[buffer_index]); buffer_index++; while (buffer_index < 9) { cp2155_set (fd, 0x23 + buffer_index, buf[buffer_index]); cp2155_set (fd, 0x33 + buffer_index, buf[buffer_index]); cp2155_set (fd, 0x43 - buffer_index, buf[buffer_index]); cp2155_set (fd, 0x33 - buffer_index, buf[buffer_index]); buffer_index++; } } cp2155_set (fd, 0xca, 0x00); cp2155_set (fd, 0xca, 0x00); cp2155_set (fd, 0xca, 0x00); } void register_table_4800 (int fd, unsigned char register_value, unsigned char *buf) { cp2155_set (fd, 0x1a, 0x00); cp2155_set (fd, 0x1b, 0x00); cp2155_set (fd, 0x1c, 0x02); cp2155_set (fd, 0x15, 0x80); cp2155_set (fd, 0x14, 0x7a); cp2155_set (fd, 0x17, 0x02); cp2155_set (fd, 0x43, 0x1c); cp2155_set (fd, 0x44, 0x9c); cp2155_set (fd, 0x45, 0x38); if (register_value > 0) { unsigned char register_number = 0x23; while (register_number < 0x43) { cp2155_set (fd, register_number, register_value); register_number++; } } else { int buffer_index = 0; cp2155_set (fd, 0x23 + buffer_index, buf[buffer_index]); cp2155_set (fd, 0x33 + buffer_index, buf[buffer_index]); buffer_index++; while (buffer_index < 9) { cp2155_set (fd, 0x23 + buffer_index, buf[buffer_index]); cp2155_set (fd, 0x33 + buffer_index, buf[buffer_index]); cp2155_set (fd, 0x43 - buffer_index, buf[buffer_index]); cp2155_set (fd, 0x33 - buffer_index, buf[buffer_index]); buffer_index++; } } cp2155_set (fd, 0xca, 0x00); cp2155_set (fd, 0xca, 0x00); cp2155_set (fd, 0xca, 0x00); } void startblob_2225_0075 (CANON_Handle * chndl, unsigned char *buf) { int fd; fd = chndl->fd; size_t count; unsigned int top_value = 0x2580; unsigned char value_62 = 0x2e; /* original: unsigned int top_value = 0x2580; unsigned char value_62 = 0x2e; ratio 320 decimal */ cp2155_set (fd, 0x90, 0xd8); cp2155_set (fd, 0x90, 0xd8); cp2155_set (fd, 0xb0, 0x03); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x08, chndl->value_08); cp2155_set (fd, 0x09, chndl->value_09); cp2155_set (fd, 0x0a, chndl->value_0a); cp2155_set (fd, 0x0b, chndl->value_0b); cp2155_set (fd, 0xa0, 0x1d); cp2155_set (fd, 0xa1, 0x00); cp2155_set (fd, 0xa2, 0x06); cp2155_set (fd, 0xa3, 0x70); cp2155_set (fd, 0x64, 0x00); cp2155_set (fd, 0x65, 0x00); cp2155_set (fd, 0x61, 0x00); cp2155_set (fd, 0x62, value_62); cp2155_set (fd, 0x63, 0x00); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x5a, 0x32); cp2155_set (fd, 0x5b, 0x32); cp2155_set (fd, 0x5c, 0x32); cp2155_set (fd, 0x5d, 0x32); cp2155_set (fd, 0x52, 0x09); cp2155_set (fd, 0x53, 0x5a); cp2155_set (fd, 0x54, 0x06); cp2155_set (fd, 0x55, 0x08); cp2155_set (fd, 0x56, 0x05); cp2155_set (fd, 0x57, 0x5f); cp2155_set (fd, 0x58, 0xa9); cp2155_set (fd, 0x59, 0xce); cp2155_set (fd, 0x5e, 0x02); cp2155_set (fd, 0x5f, 0x00); cp2155_set (fd, 0x5f, 0x03); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x81, 0x29); cp2155_set (fd, 0x81, 0x29); cp2155_set (fd, 0x82, 0x09); cp2155_set (fd, 0x82, 0x09); cp2155_set (fd, 0x83, 0x02); cp2155_set (fd, 0x84, 0x06); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0xb0, 0x0b); big_write (fd, 0x5174, buf); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x9b, 0x03); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x11, 0xc1); cp2155_set (fd, 0x11, 0xc1); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x12, 0x40); cp2155_set (fd, 0x13, 0x40); cp2155_set (fd, 0x16, 0x40); cp2155_set (fd, 0x21, 0x06); cp2155_set (fd, 0x22, 0x40); cp2155_set (fd, 0x20, 0x06); cp2155_set (fd, 0x1d, 0x00); cp2155_set (fd, 0x1e, 0x00); cp2155_set (fd, 0x1f, 0xf0); cp2155_set (fd, 0x66, 0x00); cp2155_set (fd, 0x67, chndl->value_67); cp2155_set (fd, 0x68, chndl->value_68); memcpy (buf, "\x28\x27\x25\x21\x1c\x16\x0f\x08\x00", 9); register_table (fd, 0, buf); cp2155_set (fd, 0x18, 0x00); count = 260; make_slope_table (count, top_value, 0x6a, 0.021739, buf); write_buf (fd, count, buf, 0x03, 0x00); write_buf (fd, count, buf, 0x03, 0x02); write_buf (fd, count, buf, 0x03, 0x06); count = 36; make_slope_table (count, top_value, 0x06, 0.15217, buf); write_buf (fd, count, buf, 0x03, 0x04); write_buf (fd, count, buf, 0x03, 0x08); general_motor_2225 (fd); } void startblob_2225_0150 (CANON_Handle * chndl, unsigned char *buf) { int fd; fd = chndl->fd; size_t count; unsigned int top_value = 0x2580; unsigned char value_62 = 0x1e; /* original: unsigned int top_value = 0x2580; unsigned char value_62 = 0x1e; ratio 320 decimal */ cp2155_set (fd, 0x90, 0xd8); cp2155_set (fd, 0x90, 0xd8); cp2155_set (fd, 0xb0, 0x02); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x08, chndl->value_08); cp2155_set (fd, 0x09, chndl->value_09); cp2155_set (fd, 0x0a, chndl->value_0a); cp2155_set (fd, 0x0b, chndl->value_0b); cp2155_set (fd, 0xa0, 0x1d); cp2155_set (fd, 0xa1, 0x00); cp2155_set (fd, 0xa2, 0x0c); cp2155_set (fd, 0xa3, 0xd0); cp2155_set (fd, 0x64, 0x00); cp2155_set (fd, 0x65, 0x00); cp2155_set (fd, 0x61, 0x00); cp2155_set (fd, 0x62, value_62); cp2155_set (fd, 0x63, 0xa0); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x5a, 0x32); cp2155_set (fd, 0x5b, 0x32); cp2155_set (fd, 0x5c, 0x32); cp2155_set (fd, 0x5d, 0x32); cp2155_set (fd, 0x52, 0x09); cp2155_set (fd, 0x53, 0x5a); cp2155_set (fd, 0x54, 0x06); cp2155_set (fd, 0x55, 0x08); cp2155_set (fd, 0x56, 0x05); cp2155_set (fd, 0x57, 0x5f); cp2155_set (fd, 0x58, 0xa9); cp2155_set (fd, 0x59, 0xce); cp2155_set (fd, 0x5e, 0x02); cp2155_set (fd, 0x5f, 0x00); cp2155_set (fd, 0x5f, 0x03); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x81, 0x29); cp2155_set (fd, 0x81, 0x29); cp2155_set (fd, 0x82, 0x09); cp2155_set (fd, 0x82, 0x09); cp2155_set (fd, 0x83, 0x02); cp2155_set (fd, 0x84, 0x06); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0xb0, 0x0a); big_write (fd, 0x5174, buf); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x9b, 0x03); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x12, 0x40); cp2155_set (fd, 0x13, 0x40); cp2155_set (fd, 0x16, 0x40); cp2155_set (fd, 0x21, 0x06); cp2155_set (fd, 0x22, 0x40); cp2155_set (fd, 0x20, 0x06); cp2155_set (fd, 0x1d, 0x00); cp2155_set (fd, 0x1e, 0x00); cp2155_set (fd, 0x1f, 0x04); cp2155_set (fd, 0x66, 0x00); cp2155_set (fd, 0x67, chndl->value_67); cp2155_set (fd, 0x68, chndl->value_68); memcpy (buf, "\x28\x27\x25\x21\x1c\x16\x0f\x08\x00", 9); register_table (fd, 0, buf); cp2155_set (fd, 0x18, 0x00); count = 260; make_slope_table (count, top_value, 0x06, 0.0089185, buf); write_buf (fd, count, buf, 0x03, 0x00); write_buf (fd, count, buf, 0x03, 0x02); write_buf (fd, count, buf, 0x03, 0x06); count = 36; make_slope_table (count, top_value, 0x06, 0.102968, buf); write_buf (fd, count, buf, 0x03, 0x04); write_buf (fd, count, buf, 0x03, 0x08); general_motor_2225 (fd); } void startblob_2225_0300 (CANON_Handle * chndl, unsigned char *buf) { int fd; fd = chndl->fd; size_t count; unsigned int top_value = 0x2580; unsigned char value_62 = 0x2a; /* original: unsigned int top_value = 0x2580; unsigned char value_62 = 0x2a; ratio 228 decimal */ cp2155_set (fd, 0x90, 0xd8); cp2155_set (fd, 0x90, 0xd8); cp2155_set (fd, 0xb0, 0x01); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x08, chndl->value_08); cp2155_set (fd, 0x09, chndl->value_09); cp2155_set (fd, 0x0a, chndl->value_0a); cp2155_set (fd, 0x0b, chndl->value_0b); cp2155_set (fd, 0xa0, 0x1d); cp2155_set (fd, 0xa1, 0x00); cp2155_set (fd, 0xa2, 0x19); cp2155_set (fd, 0xa3, 0x30); cp2155_set (fd, 0x64, 0x00); cp2155_set (fd, 0x65, 0x00); cp2155_set (fd, 0x61, 0x00); cp2155_set (fd, 0x62, value_62); cp2155_set (fd, 0x63, 0x80); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x5a, 0x32); cp2155_set (fd, 0x5b, 0x32); cp2155_set (fd, 0x5c, 0x32); cp2155_set (fd, 0x5d, 0x32); cp2155_set (fd, 0x52, 0x09); cp2155_set (fd, 0x53, 0x5a); cp2155_set (fd, 0x54, 0x06); cp2155_set (fd, 0x55, 0x08); cp2155_set (fd, 0x56, 0x05); cp2155_set (fd, 0x57, 0x5f); cp2155_set (fd, 0x58, 0xa9); cp2155_set (fd, 0x59, 0xce); cp2155_set (fd, 0x5e, 0x02); cp2155_set (fd, 0x5f, 0x00); cp2155_set (fd, 0x5f, 0x03); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x81, 0x29); cp2155_set (fd, 0x81, 0x29); cp2155_set (fd, 0x82, 0x09); cp2155_set (fd, 0x82, 0x09); cp2155_set (fd, 0x83, 0x02); cp2155_set (fd, 0x84, 0x06); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0xb0, 0x09); big_write (fd, 0x5174, buf); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x9b, 0x01); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x12, 0x0c); cp2155_set (fd, 0x13, 0x0c); cp2155_set (fd, 0x16, 0x0c); cp2155_set (fd, 0x21, 0x06); cp2155_set (fd, 0x22, 0x0c); cp2155_set (fd, 0x20, 0x06); cp2155_set (fd, 0x1d, 0x00); cp2155_set (fd, 0x1e, 0x00); cp2155_set (fd, 0x1f, 0x04); cp2155_set (fd, 0x66, 0x00); cp2155_set (fd, 0x67, chndl->value_67); cp2155_set (fd, 0x68, chndl->value_68); register_table (fd, 0x14, buf); cp2155_set (fd, 0x18, 0x00); count = 52; make_slope_table (count, top_value, 0x06, 0.0038363, buf); write_buf (fd, count, buf, 0x03, 0x00); write_buf (fd, count, buf, 0x03, 0x02); write_buf (fd, count, buf, 0x03, 0x06); count = 36; make_slope_table (count, top_value, 0x06, 0.0080213, buf); write_buf (fd, count, buf, 0x03, 0x04); write_buf (fd, count, buf, 0x03, 0x08); general_motor_2225 (fd); } void startblob_2225_0600 (CANON_Handle * chndl, unsigned char *buf) { int fd; fd = chndl->fd; size_t count; unsigned int top_value = 0x2580; unsigned char value_62 = 0x15; /* original: unsigned int top_value = 0x2580; unsigned char value_62 = 0x15; ratio 457 decimal */ cp2155_set (fd, 0x90, 0xd8); cp2155_set (fd, 0x90, 0xd8); cp2155_set (fd, 0xb0, 0x00); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x08, chndl->value_08); cp2155_set (fd, 0x09, chndl->value_09); cp2155_set (fd, 0x0a, chndl->value_0a); cp2155_set (fd, 0x0b, chndl->value_0b); cp2155_set (fd, 0xa0, 0x1d); cp2155_set (fd, 0xa1, 0x00); cp2155_set (fd, 0xa2, 0x77); cp2155_set (fd, 0xa3, 0xb0); cp2155_set (fd, 0x64, 0x00); cp2155_set (fd, 0x65, 0x00); cp2155_set (fd, 0x61, 0x00); cp2155_set (fd, 0x62, value_62); cp2155_set (fd, 0x63, 0xe0); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x5a, 0x32); cp2155_set (fd, 0x5b, 0x32); cp2155_set (fd, 0x5c, 0x32); cp2155_set (fd, 0x5d, 0x32); cp2155_set (fd, 0x52, 0x07); cp2155_set (fd, 0x53, 0xd0); cp2155_set (fd, 0x54, 0x07); cp2155_set (fd, 0x55, 0xd0); cp2155_set (fd, 0x56, 0x07); cp2155_set (fd, 0x57, 0xd0); cp2155_set (fd, 0x58, 0x00); cp2155_set (fd, 0x59, 0x01); cp2155_set (fd, 0x5e, 0x02); cp2155_set (fd, 0x5f, 0x00); cp2155_set (fd, 0x5f, 0x03); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x81, 0x29); cp2155_set (fd, 0x81, 0x29); cp2155_set (fd, 0x82, 0x09); cp2155_set (fd, 0x82, 0x09); cp2155_set (fd, 0x83, 0x02); cp2155_set (fd, 0x84, 0x06); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0xb0, 0x00); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x9b, 0x01); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x11, 0xc3); cp2155_set (fd, 0x11, 0xc3); cp2155_set (fd, 0x11, 0xc3); cp2155_set (fd, 0x11, 0xc1); cp2155_set (fd, 0x11, 0xc1); cp2155_set (fd, 0x12, 0x12); cp2155_set (fd, 0x13, 0x00); cp2155_set (fd, 0x16, 0x12); cp2155_set (fd, 0x21, 0x06); cp2155_set (fd, 0x22, 0x12); cp2155_set (fd, 0x20, 0x06); cp2155_set (fd, 0x1d, 0x00); cp2155_set (fd, 0x1e, 0x00); cp2155_set (fd, 0x1f, 0x04); cp2155_set (fd, 0x66, 0x00); cp2155_set (fd, 0x67, chndl->value_67); cp2155_set (fd, 0x68, chndl->value_68); register_table (fd, 0x14, buf); cp2155_set (fd, 0x18, 0x00); count = 84; make_slope_table (count, top_value, 0x06, 0.0020408, buf); write_buf (fd, count, buf, 0x03, 0x00); write_buf (fd, count, buf, 0x03, 0x02); write_buf (fd, count, buf, 0x03, 0x06); count = 36; make_slope_table (count, top_value, 0x06, 0.0064935, buf); write_buf (fd, count, buf, 0x03, 0x04); write_buf (fd, count, buf, 0x03, 0x08); general_motor_2225 (fd); } void startblob_2225_1200 (CANON_Handle * chndl, unsigned char *buf) { int fd; fd = chndl->fd; size_t count; unsigned int top_value = 0xff00; unsigned char value_62 = 0xaa; /* original: unsigned int top_value = 0xff00; unsigned char value_62 = 0xaa; */ cp2155_set (fd, 0x90, 0xc8); cp2155_set (fd, 0x90, 0xe8); cp2155_set (fd, 0xb0, 0x00); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x08, chndl->value_08); cp2155_set (fd, 0x09, chndl->value_09); cp2155_set (fd, 0x0a, chndl->value_0a); cp2155_set (fd, 0x0b, chndl->value_0b); cp2155_set (fd, 0xa0, 0x1d); cp2155_set (fd, 0xa1, 0x00); cp2155_set (fd, 0xa2, 0x63); cp2155_set (fd, 0xa3, 0xd0); cp2155_set (fd, 0x64, 0x00); cp2155_set (fd, 0x65, 0x00); cp2155_set (fd, 0x61, 0x00); cp2155_set (fd, 0x62, value_62); cp2155_set (fd, 0x63, 0x00); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x5a, 0x32); cp2155_set (fd, 0x5b, 0x32); cp2155_set (fd, 0x5c, 0x32); cp2155_set (fd, 0x5d, 0x32); cp2155_set (fd, 0x52, 0x11); cp2155_set (fd, 0x53, 0x50); cp2155_set (fd, 0x54, 0x0c); cp2155_set (fd, 0x55, 0x01); cp2155_set (fd, 0x56, 0x0a); cp2155_set (fd, 0x57, 0xae); cp2155_set (fd, 0x58, 0xa9); cp2155_set (fd, 0x59, 0xce); cp2155_set (fd, 0x5e, 0x02); cp2155_set (fd, 0x5f, 0x00); cp2155_set (fd, 0x5f, 0x03); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x81, 0x29); cp2155_set (fd, 0x81, 0x29); cp2155_set (fd, 0x82, 0x09); cp2155_set (fd, 0x82, 0x09); cp2155_set (fd, 0x83, 0x02); cp2155_set (fd, 0x84, 0x06); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0xb0, 0x08); big_write (fd, 0xa1a4, buf); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x9b, 0x01); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x12, 0x06); cp2155_set (fd, 0x13, 0x06); cp2155_set (fd, 0x16, 0x06); cp2155_set (fd, 0x21, 0x06); cp2155_set (fd, 0x22, 0x06); cp2155_set (fd, 0x20, 0x06); cp2155_set (fd, 0x1d, 0x00); cp2155_set (fd, 0x1e, 0x00); cp2155_set (fd, 0x1f, 0x04); cp2155_set (fd, 0x66, 0x00); cp2155_set (fd, 0x67, chndl->value_67); cp2155_set (fd, 0x68, chndl->value_68); memcpy (buf, "\x14\x14\x12\x11\x0e\x0b\x08\x04\x00", 9); register_table (fd, 0, buf); cp2155_set (fd, 0x18, 0x01); count = 36; make_slope_table (count, top_value, 0x06, 0.0, buf); write_buf (fd, count, buf, 0x03, 0x00); write_buf (fd, count, buf, 0x03, 0x02); write_buf (fd, count, buf, 0x03, 0x06); write_buf (fd, count, buf, 0x03, 0x04); write_buf (fd, count, buf, 0x03, 0x08); general_motor_2225 (fd); } void startblob_2224_0075 (CANON_Handle * chndl, unsigned char *buf) { int fd; fd = chndl->fd; size_t count; unsigned int top_value = 0x2580; unsigned char value_62 = 0x2e; /* original: unsigned int top_value = 0x2580; unsigned char value_62 = 0x2e; ratio 208 decimal */ cp2155_set (fd, 0x90, 0xe8); cp2155_set (fd, 0x9b, 0x06); cp2155_set (fd, 0x9b, 0x04); cp2155_set (fd, 0x90, 0xf8); cp2155_set (fd, 0xb0, 0x03); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x08, chndl->value_08); cp2155_set (fd, 0x09, chndl->value_09); cp2155_set (fd, 0x0a, chndl->value_0a); cp2155_set (fd, 0x0b, chndl->value_0b); cp2155_set (fd, 0xa0, 0x1d); cp2155_set (fd, 0xa1, 0x00); cp2155_set (fd, 0xa2, 0x06); cp2155_set (fd, 0xa3, 0x70); cp2155_set (fd, 0x64, 0x00); cp2155_set (fd, 0x65, 0x00); cp2155_set (fd, 0x61, 0x00); cp2155_set (fd, 0x62, value_62); cp2155_set (fd, 0x63, 0x00); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x90, 0xf8); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x5a, 0xff); cp2155_set (fd, 0x5b, 0xff); cp2155_set (fd, 0x5c, 0xff); cp2155_set (fd, 0x5d, 0xff); cp2155_set (fd, 0x52, 0x0c); cp2155_set (fd, 0x53, 0xda); cp2155_set (fd, 0x54, 0x0c); cp2155_set (fd, 0x55, 0x44); cp2155_set (fd, 0x56, 0x08); cp2155_set (fd, 0x57, 0xbb); cp2155_set (fd, 0x58, 0x1d); cp2155_set (fd, 0x59, 0xa1); cp2155_set (fd, 0x5e, 0x02); cp2155_set (fd, 0x5f, 0x00); cp2155_set (fd, 0x5f, 0x03); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x81, 0x31); cp2155_set (fd, 0x81, 0x31); cp2155_set (fd, 0x82, 0x11); cp2155_set (fd, 0x82, 0x11); cp2155_set (fd, 0x83, 0x01); cp2155_set (fd, 0x84, 0x05); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0xb0, 0x0b); big_write (fd, 0x5694, buf); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x11, 0xc1); cp2155_set (fd, 0x11, 0xc1); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x12, 0x7d); cp2155_set (fd, 0x13, 0x7d); cp2155_set (fd, 0x16, 0x7d); cp2155_set (fd, 0x21, 0x06); cp2155_set (fd, 0x22, 0x7d); cp2155_set (fd, 0x20, 0x06); cp2155_set (fd, 0x1d, 0x00); cp2155_set (fd, 0x1e, 0x00); cp2155_set (fd, 0x1f, 0x71); cp2155_set (fd, 0x66, 0x00); cp2155_set (fd, 0x67, chndl->value_67); cp2155_set (fd, 0x68, chndl->value_68); register_table (fd, 0x0f, buf); cp2155_set (fd, 0x18, 0x00); count = 516; make_slope_table (count, top_value, 0x6a, 0.0084116, buf); write_buf (fd, count, buf, 0x03, 0x00); write_buf (fd, count, buf, 0x03, 0x02); write_buf (fd, count, buf, 0x03, 0x06); count = 36; make_slope_table (count, top_value, 0x06, 0.15217, buf); write_buf (fd, count, buf, 0x03, 0x04); write_buf (fd, count, buf, 0x03, 0x08); general_motor_2224 (fd); } void startblob_2224_0150 (CANON_Handle * chndl, unsigned char *buf) { int fd; fd = chndl->fd; size_t count; unsigned int top_value = 0x2580; unsigned char value_62 = 0x1e; /* original: unsigned int top_value = 0x2580; unsigned char value_62 = 0x1e; ratio 320 decimal */ cp2155_set (fd, 0x90, 0xe8); cp2155_set (fd, 0x9b, 0x06); cp2155_set (fd, 0x9b, 0x04); cp2155_set (fd, 0x90, 0xf8); cp2155_set (fd, 0xb0, 0x02); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x08, chndl->value_08); cp2155_set (fd, 0x09, chndl->value_09); cp2155_set (fd, 0x0a, chndl->value_0a); cp2155_set (fd, 0x0b, chndl->value_0b); cp2155_set (fd, 0xa0, 0x1d); cp2155_set (fd, 0xa1, 0x00); cp2155_set (fd, 0xa2, 0x0c); cp2155_set (fd, 0xa3, 0xd0); cp2155_set (fd, 0x64, 0x00); cp2155_set (fd, 0x65, 0x00); cp2155_set (fd, 0x61, 0x00); cp2155_set (fd, 0x62, value_62); cp2155_set (fd, 0x63, 0xa0); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x90, 0xf8); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x5a, 0xff); cp2155_set (fd, 0x5b, 0xff); cp2155_set (fd, 0x5c, 0xff); cp2155_set (fd, 0x5d, 0xff); cp2155_set (fd, 0x52, 0x0c); cp2155_set (fd, 0x53, 0xda); cp2155_set (fd, 0x54, 0x0c); cp2155_set (fd, 0x55, 0x44); cp2155_set (fd, 0x56, 0x08); cp2155_set (fd, 0x57, 0xbb); cp2155_set (fd, 0x58, 0x1d); cp2155_set (fd, 0x59, 0xa1); cp2155_set (fd, 0x5e, 0x02); cp2155_set (fd, 0x5f, 0x00); cp2155_set (fd, 0x5f, 0x03); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x81, 0x31); cp2155_set (fd, 0x81, 0x31); cp2155_set (fd, 0x82, 0x11); cp2155_set (fd, 0x82, 0x11); cp2155_set (fd, 0x83, 0x01); cp2155_set (fd, 0x84, 0x05); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0xb0, 0x0a); big_write (fd, 0x5694, buf); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x12, 0x40); cp2155_set (fd, 0x13, 0x40); cp2155_set (fd, 0x16, 0x40); cp2155_set (fd, 0x21, 0x06); cp2155_set (fd, 0x22, 0x40); cp2155_set (fd, 0x20, 0x06); cp2155_set (fd, 0x1d, 0x00); cp2155_set (fd, 0x1e, 0x00); cp2155_set (fd, 0x1f, 0x04); cp2155_set (fd, 0x66, 0x00); cp2155_set (fd, 0x67, chndl->value_67); cp2155_set (fd, 0x68, chndl->value_68); register_table (fd, 0x0d, buf); cp2155_set (fd, 0x18, 0x00); count = 260; make_slope_table (count, top_value, 0x86, 0.017979, buf); write_buf (fd, count, buf, 0x03, 0x00); write_buf (fd, count, buf, 0x03, 0x02); write_buf (fd, count, buf, 0x03, 0x06); count = 36; make_slope_table (count, top_value, 0x06, 0.102968, buf); write_buf (fd, count, buf, 0x03, 0x04); write_buf (fd, count, buf, 0x03, 0x08); general_motor_2224 (fd); } void startblob_2224_0300 (CANON_Handle * chndl, unsigned char *buf) { int fd; fd = chndl->fd; size_t count; unsigned int top_value = 0x3200; unsigned char value_62 = 0x15; /* original: unsigned int top_value = 0x3200; unsigned char value_62 = 0x15; ratio 609.52 decimal */ cp2155_set (fd, 0x90, 0xe8); cp2155_set (fd, 0x9b, 0x06); cp2155_set (fd, 0x9b, 0x04); cp2155_set (fd, 0x90, 0xf8); cp2155_set (fd, 0xb0, 0x01); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x08, chndl->value_08); cp2155_set (fd, 0x09, chndl->value_09); cp2155_set (fd, 0x0a, chndl->value_0a); cp2155_set (fd, 0x0b, chndl->value_0b); cp2155_set (fd, 0xa0, 0x1d); cp2155_set (fd, 0xa1, 0x00); cp2155_set (fd, 0xa2, 0x03); cp2155_set (fd, 0xa3, 0x10); cp2155_set (fd, 0x64, 0x00); cp2155_set (fd, 0x65, 0x00); cp2155_set (fd, 0x61, 0x00); cp2155_set (fd, 0x62, value_62); cp2155_set (fd, 0x63, 0xe0); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x90, 0xf8); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x5a, 0xff); cp2155_set (fd, 0x5b, 0xff); cp2155_set (fd, 0x5c, 0xff); cp2155_set (fd, 0x5d, 0xff); cp2155_set (fd, 0x52, 0x0a); cp2155_set (fd, 0x53, 0xf0); cp2155_set (fd, 0x54, 0x0a); cp2155_set (fd, 0x55, 0xf0); cp2155_set (fd, 0x56, 0x0a); cp2155_set (fd, 0x57, 0xf0); cp2155_set (fd, 0x58, 0x00); cp2155_set (fd, 0x59, 0x01); cp2155_set (fd, 0x5e, 0x02); cp2155_set (fd, 0x5f, 0x00); cp2155_set (fd, 0x5f, 0x03); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x81, 0x31); cp2155_set (fd, 0x81, 0x31); cp2155_set (fd, 0x82, 0x11); cp2155_set (fd, 0x82, 0x11); cp2155_set (fd, 0x83, 0x01); cp2155_set (fd, 0x84, 0x05); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0xb0, 0x01); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x11, 0xc3); cp2155_set (fd, 0x11, 0xc3); cp2155_set (fd, 0x11, 0xc3); cp2155_set (fd, 0x11, 0xc1); cp2155_set (fd, 0x11, 0xc1); cp2155_set (fd, 0x12, 0x40); cp2155_set (fd, 0x13, 0x00); cp2155_set (fd, 0x16, 0x40); cp2155_set (fd, 0x21, 0x06); cp2155_set (fd, 0x22, 0x40); cp2155_set (fd, 0x20, 0x06); cp2155_set (fd, 0x1d, 0x00); cp2155_set (fd, 0x1e, 0x00); cp2155_set (fd, 0x1f, 0x04); cp2155_set (fd, 0x66, 0x00); cp2155_set (fd, 0x67, chndl->value_67); cp2155_set (fd, 0x68, chndl->value_68); register_table (fd, 0x0a, buf); cp2155_set (fd, 0x18, 0x00); count = 260; make_slope_table (count, top_value, 0x66, 0.0129596, buf); write_buf (fd, count, buf, 0x03, 0x00); write_buf (fd, count, buf, 0x03, 0x02); write_buf (fd, count, buf, 0x03, 0x06); count = 36; make_slope_table (count, top_value, 0x06, 0.09307359, buf); write_buf (fd, count, buf, 0x03, 0x04); write_buf (fd, count, buf, 0x03, 0x08); general_motor_2224 (fd); } void startblob_2224_0600 (CANON_Handle * chndl, unsigned char *buf) { int fd; fd = chndl->fd; size_t count; unsigned int top_value = 0x2580; unsigned char value_62 = 0x19; /* original: unsigned int top_value = 0x7f80; unsigned char value_62 = 0x55; ratio 384 decimal */ cp2155_set (fd, 0x90, 0xe8); cp2155_set (fd, 0x9b, 0x06); cp2155_set (fd, 0x9b, 0x04); cp2155_set (fd, 0x90, 0xf8); cp2155_set (fd, 0xb0, 0x00); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x08, chndl->value_08); cp2155_set (fd, 0x09, chndl->value_09); cp2155_set (fd, 0x0a, chndl->value_0a); cp2155_set (fd, 0x0b, chndl->value_0b); cp2155_set (fd, 0xa0, 0x1d); cp2155_set (fd, 0xa1, 0x00); cp2155_set (fd, 0xa2, 0x31); cp2155_set (fd, 0xa3, 0xf0); cp2155_set (fd, 0x64, 0x00); cp2155_set (fd, 0x65, 0x00); cp2155_set (fd, 0x61, 0x00); cp2155_set (fd, 0x62, value_62); cp2155_set (fd, 0x63, 0x00); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x90, 0xf8); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x5a, 0xff); cp2155_set (fd, 0x5b, 0xff); cp2155_set (fd, 0x5c, 0xff); cp2155_set (fd, 0x5d, 0xff); cp2155_set (fd, 0x52, 0x0c); cp2155_set (fd, 0x53, 0xda); cp2155_set (fd, 0x54, 0x0c); cp2155_set (fd, 0x55, 0x44); cp2155_set (fd, 0x56, 0x08); cp2155_set (fd, 0x57, 0xbb); cp2155_set (fd, 0x58, 0x1d); cp2155_set (fd, 0x59, 0xa1); cp2155_set (fd, 0x5e, 0x02); cp2155_set (fd, 0x5f, 0x00); cp2155_set (fd, 0x5f, 0x03); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x81, 0x31); cp2155_set (fd, 0x81, 0x31); cp2155_set (fd, 0x82, 0x11); cp2155_set (fd, 0x82, 0x11); cp2155_set (fd, 0x83, 0x01); cp2155_set (fd, 0x84, 0x05); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0xb0, 0x08); big_write_2224 (fd, 0x5694, buf); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x12, 0x06); cp2155_set (fd, 0x13, 0x06); cp2155_set (fd, 0x16, 0x06); cp2155_set (fd, 0x21, 0x06); cp2155_set (fd, 0x22, 0x06); cp2155_set (fd, 0x20, 0x06); cp2155_set (fd, 0x1d, 0x00); cp2155_set (fd, 0x1e, 0x00); cp2155_set (fd, 0x1f, 0x04); cp2155_set (fd, 0x66, 0x00); cp2155_set (fd, 0x67, chndl->value_67); cp2155_set (fd, 0x68, chndl->value_68); register_table (fd, 0x0c, buf); cp2155_set (fd, 0x18, 0x00); count = 36; make_slope_table (count, top_value, 0x06, 0.0, buf); write_buf (fd, count, buf, 0x03, 0x00); write_buf (fd, count, buf, 0x03, 0x02); write_buf (fd, count, buf, 0x03, 0x06); write_buf (fd, count, buf, 0x03, 0x04); write_buf (fd, count, buf, 0x03, 0x08); general_motor_2224 (fd); } void startblob_2224_1200 (CANON_Handle * chndl, unsigned char *buf) { /* chndl->value_51 = 0x0f; */ int fd; fd = chndl->fd; size_t count; unsigned int top_value = 0x7c71; unsigned char value_62 = 0x29; /* unsigned int top_value = 0x3fc7; unsigned char value_62 = 0x15; ratio 777 decimal */ cp2155_set (fd, 0x90, 0xe0); /* e8 */ double n_msec = 10.0; int n_9b = 10; /* 2 */ while (n_9b > 0) { cp2155_set (fd, 0x9b, 0x06); usleep (n_msec * MSEC); cp2155_set (fd, 0x9b, 0x04); usleep (n_msec * MSEC); n_9b--; } cp2155_set (fd, 0x90, 0xf0); /* f8 */ cp2155_set (fd, 0xb0, 0x00); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x08, chndl->value_08); cp2155_set (fd, 0x09, chndl->value_09); cp2155_set (fd, 0x0a, chndl->value_0a); cp2155_set (fd, 0x0b, chndl->value_0b); cp2155_set (fd, 0xa0, 0x1d); cp2155_set (fd, 0xa1, 0x00); cp2155_set (fd, 0xa2, 0x63); cp2155_set (fd, 0xa3, 0xd0); cp2155_set (fd, 0x64, 0x00); cp2155_set (fd, 0x65, 0x00); cp2155_set (fd, 0x61, 0x00); cp2155_set (fd, 0x62, value_62); cp2155_set (fd, 0x63, 0x00); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x90, 0xf8); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x5a, 0xff); cp2155_set (fd, 0x5b, 0xff); cp2155_set (fd, 0x5c, 0xff); cp2155_set (fd, 0x5d, 0xff); cp2155_set (fd, 0x52, 0x19); cp2155_set (fd, 0x53, 0x5a); cp2155_set (fd, 0x54, 0x17); cp2155_set (fd, 0x55, 0x98); cp2155_set (fd, 0x56, 0x11); cp2155_set (fd, 0x57, 0xae); cp2155_set (fd, 0x58, 0xa9); cp2155_set (fd, 0x59, 0x01); cp2155_set (fd, 0x5e, 0x02); cp2155_set (fd, 0x5f, 0x00); cp2155_set (fd, 0x5f, 0x03); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x81, 0x31); cp2155_set (fd, 0x81, 0x31); cp2155_set (fd, 0x82, 0x11); cp2155_set (fd, 0x82, 0x11); cp2155_set (fd, 0x83, 0x01); cp2155_set (fd, 0x84, 0x05); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0xb0, 0x08); big_write (fd, 0xa1a4, buf); /* big_write_film (fd, 0xf004, buf); */ cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x12, 0x50); cp2155_set (fd, 0x13, 0x50); cp2155_set (fd, 0x16, 0x50); cp2155_set (fd, 0x21, 0x06); cp2155_set (fd, 0x22, 0x50); cp2155_set (fd, 0x20, 0x06); cp2155_set (fd, 0x1d, 0x00); cp2155_set (fd, 0x1e, 0x00); cp2155_set (fd, 0x1f, 0x04); cp2155_set (fd, 0x66, 0x00); cp2155_set (fd, 0x67, chndl->value_67); cp2155_set (fd, 0x68, chndl->value_68); memcpy (buf, "\x01\x03\x05\x07\x09\x0a\x0b\x0c\x0c", 9); register_table (fd, 0, buf); cp2155_set (fd, 0x18, 0x00); count = 324; make_slope_table (count, top_value, 0x06, 0.0, buf); write_buf (fd, count, buf, 0x03, 0x00); write_buf (fd, count, buf, 0x03, 0x02); write_buf (fd, count, buf, 0x03, 0x06); count = 36; make_slope_table (count, top_value, 0x06, 0.0, buf); write_buf (fd, count, buf, 0x03, 0x04); write_buf (fd, count, buf, 0x03, 0x08); general_motor_2224 (fd); } void startblob_2224_2400 (CANON_Handle * chndl, unsigned char *buf) { int fd; fd = chndl->fd; size_t count; unsigned int top_value = 0x5555; /* was 0x7c71 */ unsigned char value_62 = 0x0e; /* at 0x15 ratio 1517 decimal, value_62 was 0x29 */ cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x11, 0x83); /* unsigned int top_value = 0x3fc7; unsigned char value_62 = 0x15; ratio 777 decimal cp2155_set (fd, 0x01, 0x2b); cp2155_set (fd, 0x04, 0x08); cp2155_set (fd, 0x05, 0x00); cp2155_set (fd, 0x06, 0x00); */ cp2155_set (fd, 0x90, 0xe0); double n_msec = 10.0; int n_9b = 11; while (n_9b > 0) { cp2155_set (fd, 0x9b, 0x06); usleep (n_msec * MSEC); cp2155_set (fd, 0x9b, 0x04); usleep (n_msec * MSEC); n_9b--; } cp2155_set (fd, 0x90, 0xf0); cp2155_set (fd, 0xb0, 0x00); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x08, chndl->value_08); cp2155_set (fd, 0x09, chndl->value_09); cp2155_set (fd, 0x0a, chndl->value_0a); cp2155_set (fd, 0x0b, chndl->value_0b); cp2155_set (fd, 0xa0, 0x25); cp2155_set (fd, 0xa1, 0x00); cp2155_set (fd, 0xa2, 0x92); cp2155_set (fd, 0xa3, 0x10); cp2155_set (fd, 0x64, 0x00); cp2155_set (fd, 0x65, 0x00); cp2155_set (fd, 0x61, 0x00); cp2155_set (fd, 0x62, value_62); cp2155_set (fd, 0x63, 0x00); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x90, 0xf1); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x5a, 0xff); cp2155_set (fd, 0x5b, 0xff); cp2155_set (fd, 0x5c, 0xff); cp2155_set (fd, 0x5d, 0xff); cp2155_set (fd, 0x52, 0x47); cp2155_set (fd, 0x53, 0x3d); cp2155_set (fd, 0x54, 0x2b); cp2155_set (fd, 0x55, 0xd1); cp2155_set (fd, 0x56, 0x20); cp2155_set (fd, 0x57, 0x3d); cp2155_set (fd, 0x58, 0x13); cp2155_set (fd, 0x59, 0x25); cp2155_set (fd, 0x5e, 0x02); cp2155_set (fd, 0x5f, 0x00); cp2155_set (fd, 0x5f, 0x03); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x81, 0x31); /* 0x29); = darker */ cp2155_set (fd, 0x81, 0x31); /* 0x29); */ cp2155_set (fd, 0x82, 0x11); cp2155_set (fd, 0x82, 0x11); cp2155_set (fd, 0x83, 0x01); cp2155_set (fd, 0x84, 0x05); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0xb0, 0x08); big_write (fd, 0xa1a4, buf); big_write_film (fd, 0xf004, buf); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x12, 0x50); cp2155_set (fd, 0x13, 0x50); cp2155_set (fd, 0x16, 0x50); cp2155_set (fd, 0x21, 0x06); cp2155_set (fd, 0x22, 0x50); cp2155_set (fd, 0x20, 0x06); cp2155_set (fd, 0x1d, 0x00); cp2155_set (fd, 0x1e, 0x00); cp2155_set (fd, 0x1f, 0x04); cp2155_set (fd, 0x66, chndl->value_66); cp2155_set (fd, 0x67, chndl->value_67); cp2155_set (fd, 0x68, chndl->value_68); memcpy (buf, "\x02\x04\x04\x06\x06\x08\x08\x0a\x0a", 9); register_table (fd, 0, buf); cp2155_set (fd, 0x18, 0x00); count = 324; make_slope_table (count, top_value, 0x06, 0.0, buf); write_buf (fd, count, buf, 0x03, 0x00); write_buf (fd, count, buf, 0x03, 0x02); write_buf (fd, count, buf, 0x03, 0x06); count = 36; make_slope_table (count, top_value, 0x06, 0.0, buf); write_buf (fd, count, buf, 0x03, 0x04); write_buf (fd, count, buf, 0x03, 0x08); general_motor_2224 (fd); } void startblob_2224_4800 (CANON_Handle * chndl, unsigned char *buf) { int fd; fd = chndl->fd; size_t count; unsigned int top_value = 0x3fc7; /* was 0x7c71 */ unsigned char value_62 = 0x15; /* at 0x15 ratio 1517 decimal, value_62 was 0x29 */ cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x11, 0x83); /* unsigned int top_value = 0x3fc7; unsigned char value_62 = 0x15; ratio 777 decimal cp2155_set (fd, 0x01, 0x2b); cp2155_set (fd, 0x04, 0x08); cp2155_set (fd, 0x05, 0x00); cp2155_set (fd, 0x06, 0x00); */ cp2155_set (fd, 0x90, 0xe0); double n_msec = 10.0; int n_9b = 12; while (n_9b > 0) { cp2155_set (fd, 0x9b, 0x06); usleep (n_msec * MSEC); cp2155_set (fd, 0x9b, 0x04); usleep (n_msec * MSEC); n_9b--; } cp2155_set (fd, 0x90, 0xf0); cp2155_set (fd, 0xb0, 0x00); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x08, chndl->value_08); cp2155_set (fd, 0x09, chndl->value_09); cp2155_set (fd, 0x0a, chndl->value_0a); cp2155_set (fd, 0x0b, chndl->value_0b); cp2155_set (fd, 0xa0, 0x25); cp2155_set (fd, 0xa1, 0x00); cp2155_set (fd, 0xa2, 0x92); cp2155_set (fd, 0xa3, 0x10); cp2155_set (fd, 0x64, 0x00); cp2155_set (fd, 0x65, 0x00); cp2155_set (fd, 0x61, 0x00); cp2155_set (fd, 0x62, value_62); cp2155_set (fd, 0x63, 0x00); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x90, 0xf1); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x5a, 0xff); cp2155_set (fd, 0x5b, 0xff); cp2155_set (fd, 0x5c, 0xff); cp2155_set (fd, 0x5d, 0xff); cp2155_set (fd, 0x52, 0x47); cp2155_set (fd, 0x53, 0x3d); cp2155_set (fd, 0x54, 0x2b); cp2155_set (fd, 0x55, 0xd1); cp2155_set (fd, 0x56, 0x20); cp2155_set (fd, 0x57, 0x3d); cp2155_set (fd, 0x58, 0x13); cp2155_set (fd, 0x59, 0x25); cp2155_set (fd, 0x5e, 0x02); cp2155_set (fd, 0x5f, 0x00); cp2155_set (fd, 0x5f, 0x03); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x81, 0x31); /* 0x29); = darker */ cp2155_set (fd, 0x81, 0x31); /* 0x29); */ cp2155_set (fd, 0x82, 0x11); cp2155_set (fd, 0x82, 0x11); cp2155_set (fd, 0x83, 0x01); cp2155_set (fd, 0x84, 0x05); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0xb0, 0x08); big_write (fd, 0xa1a4, buf); big_write_film (fd, 0xf004, buf); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x12, 0x50); cp2155_set (fd, 0x13, 0x50); cp2155_set (fd, 0x16, 0x50); cp2155_set (fd, 0x21, 0x06); cp2155_set (fd, 0x22, 0x50); cp2155_set (fd, 0x20, 0x06); cp2155_set (fd, 0x1d, 0x00); cp2155_set (fd, 0x1e, 0x00); cp2155_set (fd, 0x1f, 0x04); cp2155_set (fd, 0x66, chndl->value_66); cp2155_set (fd, 0x67, chndl->value_67); cp2155_set (fd, 0x68, chndl->value_68); memcpy (buf, "\x02\x04\x04\x06\x06\x08\x08\x0a\x0a", 9); register_table (fd, 0, buf); cp2155_set (fd, 0x18, 0x00); count = 324; make_slope_table (count, top_value, 0x06, 0.0, buf); write_buf (fd, count, buf, 0x03, 0x00); write_buf (fd, count, buf, 0x03, 0x02); write_buf (fd, count, buf, 0x03, 0x06); count = 36; make_slope_table (count, top_value, 0x06, 0.0, buf); write_buf (fd, count, buf, 0x03, 0x04); write_buf (fd, count, buf, 0x03, 0x08); general_motor_2224 (fd); } void startblob_2224_4799 (CANON_Handle * chndl, unsigned char *buf) { int fd; fd = chndl->fd; size_t count; unsigned int top_value = 0x1400; /* was 0x7c71 */ unsigned char value_62 = 0x14; /* at 0x15 ratio 1517 decimal, value_62 was 0x29 */ cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x11, 0x83); /* unsigned int top_value = 0x3fc7; unsigned char value_62 = 0x15; ratio 777 decimal cp2155_set (fd, 0x01, 0x2b); cp2155_set (fd, 0x04, 0x08); cp2155_set (fd, 0x05, 0x00); cp2155_set (fd, 0x06, 0x00); */ cp2155_set (fd, 0x90, 0xe0); double n_msec = 10.0; int n_9b = 12; while (n_9b > 0) { cp2155_set (fd, 0x9b, 0x06); usleep (n_msec * MSEC); cp2155_set (fd, 0x9b, 0x04); usleep (n_msec * MSEC); n_9b--; } cp2155_set (fd, 0x90, 0xf0); cp2155_set (fd, 0xb0, 0x00); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x08, chndl->value_08); cp2155_set (fd, 0x09, chndl->value_09); cp2155_set (fd, 0x0a, chndl->value_0a); cp2155_set (fd, 0x0b, chndl->value_0b); cp2155_set (fd, 0xa0, 0x25); cp2155_set (fd, 0xa1, 0x01); cp2155_set (fd, 0xa2, 0x23); cp2155_set (fd, 0xa3, 0x10); cp2155_set (fd, 0x64, 0x00); cp2155_set (fd, 0x65, 0x00); cp2155_set (fd, 0x61, 0x00); cp2155_set (fd, 0x62, value_62); cp2155_set (fd, 0x63, 0x00); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x90, 0xf1); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x5a, 0xff); cp2155_set (fd, 0x5b, 0xff); cp2155_set (fd, 0x5c, 0xff); cp2155_set (fd, 0x5d, 0xff); cp2155_set (fd, 0x52, 0x92); cp2155_set (fd, 0x53, 0xa0); cp2155_set (fd, 0x54, 0x58); cp2155_set (fd, 0x55, 0x29); cp2155_set (fd, 0x56, 0x40); cp2155_set (fd, 0x57, 0x08); cp2155_set (fd, 0x58, 0x27); cp2155_set (fd, 0x59, 0xc7); cp2155_set (fd, 0x5e, 0x02); cp2155_set (fd, 0x5f, 0x00); cp2155_set (fd, 0x5f, 0x03); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x51, chndl->value_51); cp2155_set (fd, 0x81, 0x29); cp2155_set (fd, 0x81, 0x29); cp2155_set (fd, 0x82, 0x11); cp2155_set (fd, 0x82, 0x11); cp2155_set (fd, 0x83, 0x01); cp2155_set (fd, 0x84, 0x05); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0xb0, 0x08); big_write (fd, 0xa1a4, buf); big_write_film (fd, 0xf004, buf); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x11, 0x83); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x11, 0x81); cp2155_set (fd, 0x12, 0x50); cp2155_set (fd, 0x13, 0x50); cp2155_set (fd, 0x16, 0x50); cp2155_set (fd, 0x21, 0x06); cp2155_set (fd, 0x22, 0x50); cp2155_set (fd, 0x20, 0x06); cp2155_set (fd, 0x1d, 0x00); cp2155_set (fd, 0x1e, 0x00); cp2155_set (fd, 0x1f, 0x04); cp2155_set (fd, 0x66, chndl->value_66); cp2155_set (fd, 0x67, chndl->value_67); cp2155_set (fd, 0x68, chndl->value_68); register_table_4800 (fd, 0x05, buf); cp2155_set (fd, 0x18, 0x02); count = 324; make_slope_table (count, top_value, 0x06, 0.0, buf); write_buf (fd, count, buf, 0x03, 0x00); write_buf (fd, count, buf, 0x03, 0x02); write_buf (fd, count, buf, 0x03, 0x06); count = 36; make_slope_table (count, top_value, 0x06, 0.0, buf); write_buf (fd, count, buf, 0x03, 0x04); write_buf (fd, count, buf, 0x03, 0x08); general_motor_2224 (fd); } void send_start_blob (CANON_Handle * chndl) { unsigned char buf[0xfff0]; int fd; fd = chndl->fd; /* value_51: lamp colors bit 0 set: red on, bit 1 set: green on, bit 2 set: blue on, bit 3 set: infrared on all bits off: no scan is made */ chndl->value_51 = 0x07; chndl->value_66 = 0x00; switch (chndl->val[opt_resolution].w) { case 75: chndl->value_67 = 0x0a; /* 3*7300/8 */ chndl->value_68 = 0xb1; break; case 150: chndl->value_67 = 0x15; /* 3*7300/4 */ chndl->value_68 = 0x63; break; case 300: chndl->value_67 = 0x2a; /* 3*7300/2 */ chndl->value_68 = 0xc6; break; case 600: chndl->value_67 = 0x55; /* 3*7300 */ chndl->value_68 = 0x8c; break; case 1200: chndl->value_67 = 0xab; /* 6*7300 */ chndl->value_68 = 0x18; break; case 2400: chndl->value_66 = 0x01; chndl->value_67 = 0x56; /* 12*7300 */ chndl->value_68 = 0x30; break; case 4800: chndl->value_66 = 0x02; chndl->value_67 = 0xac; /* 24*7300 */ chndl->value_68 = 0x60; } unsigned char value_11 = 0xc1; /* 0x00; */ cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x11, value_11); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x11, value_11); cp2155_set (fd, 0x90, 0xf8); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x11, value_11); cp2155_set (fd, 0x01, 0x29); cp2155_set (fd, 0x04, 0x0c); cp2155_set (fd, 0x05, 0x00); cp2155_set (fd, 0x06, 0x00); cp2155_set (fd, 0x01, 0x29); cp2155_set_gamma (fd, chndl); switch (chndl->val[opt_resolution].w) { case 75: if (chndl->productcode == 0x2225) { startblob_2225_0075 (chndl, buf); } else { startblob_2224_0075 (chndl, buf); } break; case 150: if (chndl->productcode == 0x2225) { startblob_2225_0150 (chndl, buf); } else { startblob_2224_0150 (chndl, buf); } break; case 300: if (chndl->productcode == 0x2225) { startblob_2225_0300 (chndl, buf); } else { cp2155_set_gamma_red_enhanced (fd, chndl); startblob_2224_0300 (chndl, buf); } break; case 600: if (chndl->productcode == 0x2225) { cp2155_set_gamma_red_enhanced (fd, chndl); startblob_2225_0600 (chndl, buf); /* startblob_2225_0600_extra (chndl, buf); */ } else { startblob_2224_0600 (chndl, buf); } break; case 1200: if (chndl->productcode == 0x2225) { startblob_2225_1200 (chndl, buf); } else { startblob_2224_1200 (chndl, buf); } break; case 2400: if (chndl->productcode == 0x2225) { startblob_2225_1200 (chndl, buf); } else { startblob_2224_2400 (chndl, buf); } break; case 4800: if (chndl->productcode == 0x2225) { startblob_2225_1200 (chndl, buf); } else { startblob_2224_4800 (chndl, buf); } break; } } /* Wait until data ready */ static long wait_for_data (CANON_Handle * chndl) { int fd; fd = chndl->fd; time_t start_time = time (NULL); long size; byte value; DBG (12, "waiting...\n"); while (1) { size = 0; cp2155_get (fd, 0x46, &value); DBG (1, "home sensor: %02x\n", value); if (value == 0) { send_start_blob (chndl); cp2155_get (fd, 0x46, &value); DBG (1, "home sensor: %02x\n", value); } if (cp2155_get (fd, 0xa5, &value) != SANE_STATUS_GOOD) { return -1; } size += value; if (cp2155_get (fd, 0xa6, &value) != SANE_STATUS_GOOD) { return -1; } size <<= 8; size += value; if (cp2155_get (fd, 0xa7, &value) != SANE_STATUS_GOOD) { return -1; } size <<= 8; size += value; if (size != 0) { return 2 * size; } /* Give it 5 seconds */ if ((time (NULL) - start_time) > 5) { DBG (1, "wait_for_data: timed out (%ld)\n", size); return -1; } usleep (1 * MSEC); } } static int init_2225 (CANON_Handle * chndl) { int fd = chndl->fd; byte value; int result = 0; cp2155_get (fd, 0xd0, &value); /* Detect if scanner is plugged in */ if (value != 0x81 && value != 0x40) { DBG (1, "INIT: unexpected value: %x\n", value); } if (value == 0x00) { return -1; } cp2155_set (fd, 0x02, 0x01); cp2155_set (fd, 0x02, 0x00); cp2155_set (fd, 0x01, 0x00); cp2155_set (fd, 0x01, 0x28); cp2155_set (fd, 0x90, 0x4f); cp2155_set (fd, 0x92, 0xff); cp2155_set (fd, 0x93, 0x00); cp2155_set (fd, 0x91, 0x1f); cp2155_set (fd, 0x95, 0x1f); cp2155_set (fd, 0x97, 0x1f); cp2155_set (fd, 0x9b, 0x00); cp2155_set (fd, 0x9c, 0x07); cp2155_set (fd, 0x90, 0x4d); cp2155_set (fd, 0x90, 0xcd); cp2155_set (fd, 0x90, 0xcc); cp2155_set (fd, 0x9b, 0x01); cp2155_set (fd, 0xa0, 0x04); cp2155_set (fd, 0xa0, 0x05); cp2155_set (fd, 0x01, 0x28); cp2155_set (fd, 0x04, 0x0c); cp2155_set (fd, 0x05, 0x00); cp2155_set (fd, 0x06, 0x00); cp2155_set (fd, 0x98, 0x00); cp2155_set (fd, 0x98, 0x00); cp2155_set (fd, 0x98, 0x02); cp2155_set (fd, 0x99, 0x28); cp2155_set (fd, 0x9a, 0x03); cp2155_set (fd, 0x80, 0x10); cp2155_set (fd, 0x8d, 0x00); cp2155_set (fd, 0x8d, 0x04); cp2155_set (fd, 0x85, 0x00); cp2155_set (fd, 0x87, 0x00); cp2155_set (fd, 0x88, 0x70); cp2155_set (fd, 0x85, 0x03); cp2155_set (fd, 0x87, 0x00); cp2155_set (fd, 0x88, 0x28); cp2155_set (fd, 0x85, 0x06); cp2155_set (fd, 0x87, 0x00); cp2155_set (fd, 0x88, 0x28); DBG (1, "INIT state: %0d\n", result); return result; } static int init_2224 (CANON_Handle * chndl) { int fd = chndl->fd; byte value; int result = 0; cp2155_get (fd, 0xd0, &value); /* Detect if scanner is plugged in */ if (value != 0x81 && value != 0x40) { DBG (1, "INIT: unexpected value: %x\n", value); } if (value == 0x00) { return -1; } cp2155_set (fd, 0x02, 0x01); cp2155_set (fd, 0x02, 0x00); cp2155_set (fd, 0x01, 0x00); cp2155_set (fd, 0x01, 0x28); cp2155_set (fd, 0xa0, 0x04); cp2155_set (fd, 0xa0, 0x05); cp2155_set (fd, 0x01, 0x28); cp2155_set (fd, 0x04, 0x0c); cp2155_set (fd, 0x05, 0x00); cp2155_set (fd, 0x06, 0x00); cp2155_set (fd, 0x90, 0x27); cp2155_set (fd, 0x92, 0xf7); cp2155_set (fd, 0x94, 0xf7); cp2155_set (fd, 0x93, 0x00); cp2155_set (fd, 0x91, 0x1f); cp2155_set (fd, 0x95, 0x0f); cp2155_set (fd, 0x97, 0x0f); cp2155_set (fd, 0x9b, 0x00); cp2155_set (fd, 0x9c, 0x07); cp2155_set (fd, 0x90, 0xf0); cp2155_set (fd, 0x9b, 0x04); cp2155_set (fd, 0x98, 0x00); cp2155_set (fd, 0x98, 0x00); cp2155_set (fd, 0x98, 0x02); cp2155_set (fd, 0x99, 0x3b); cp2155_set (fd, 0x9a, 0x03); cp2155_set (fd, 0x80, 0x10); cp2155_set (fd, 0x8d, 0x00); cp2155_set (fd, 0x8d, 0x04); DBG (1, "INIT state: %0d\n", result); return result; } static int init (CANON_Handle * chndl) { int result; if (chndl->productcode == 0x2225) { chndl->table_gamma = 2.2; chndl->table_gamma_blue = 2.2; chndl->highlight_red_enhanced = 190; chndl->highlight_other = 240; chndl->highlight_blue_reduced = 240; result = init_2225 (chndl); } else { chndl->table_gamma = 2.2; chndl->table_gamma_blue = 1.95; chndl->highlight_red_enhanced = 190; chndl->highlight_other = 215; chndl->highlight_blue_reduced = 255; result = init_2224 (chndl); } return result; } void back2225 (int fd, unsigned char *buf) { size_t count; unsigned int top_value = 0x2580; unsigned char value_62 = 0x2e; /* original: unsigned int top_value = 0x2580; unsigned char value_62 = 0x2e; ratio 320 decimal */ cp2155_set (fd, 0x90, 0xc8); cp2155_set (fd, 0x90, 0xc8); cp2155_set (fd, 0xb0, 0x03); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x08, 0x00); cp2155_set (fd, 0x09, 0x69); cp2155_set (fd, 0x0a, 0x00); cp2155_set (fd, 0x0b, 0xe8); cp2155_set (fd, 0xa0, 0x1d); cp2155_set (fd, 0xa1, 0x00); cp2155_set (fd, 0xa2, 0x00); cp2155_set (fd, 0xa3, 0x70); cp2155_set (fd, 0x64, 0x00); cp2155_set (fd, 0x65, 0x00); cp2155_set (fd, 0x61, 0x00); cp2155_set (fd, 0x62, value_62); cp2155_set (fd, 0x63, 0x00); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x51, 0x07); cp2155_set (fd, 0x5a, 0x32); cp2155_set (fd, 0x5b, 0x32); cp2155_set (fd, 0x5c, 0x32); cp2155_set (fd, 0x5d, 0x32); cp2155_set (fd, 0x52, 0x00); cp2155_set (fd, 0x53, 0x01); cp2155_set (fd, 0x54, 0x00); cp2155_set (fd, 0x55, 0x01); cp2155_set (fd, 0x56, 0x00); cp2155_set (fd, 0x57, 0x01); cp2155_set (fd, 0x58, 0x00); cp2155_set (fd, 0x59, 0x01); cp2155_set (fd, 0x5e, 0x02); cp2155_set (fd, 0x5f, 0x00); cp2155_set (fd, 0x5f, 0x03); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x51, 0x07); cp2155_set (fd, 0x81, 0x29); cp2155_set (fd, 0x81, 0x29); cp2155_set (fd, 0x82, 0x09); cp2155_set (fd, 0x82, 0x09); cp2155_set (fd, 0x83, 0x02); cp2155_set (fd, 0x84, 0x06); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0xb0, 0x03); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x9b, 0x03); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x11, 0x41); cp2155_set (fd, 0x11, 0x61); cp2155_set (fd, 0x11, 0x21); cp2155_set (fd, 0x11, 0x21); cp2155_set (fd, 0x11, 0x25); cp2155_set (fd, 0x11, 0x25); cp2155_set (fd, 0x11, 0x25); cp2155_set (fd, 0x12, 0x40); cp2155_set (fd, 0x13, 0x40); cp2155_set (fd, 0x16, 0x40); cp2155_set (fd, 0x21, 0x06); cp2155_set (fd, 0x22, 0x40); cp2155_set (fd, 0x20, 0x06); cp2155_set (fd, 0x1d, 0x00); cp2155_set (fd, 0x1e, 0x36); cp2155_set (fd, 0x1f, 0xd0); cp2155_set (fd, 0x66, 0x00); cp2155_set (fd, 0x67, 0x00); cp2155_set (fd, 0x68, 0x06); memcpy (buf, "\x28\x27\x25\x21\x1c\x16\x0f\x08\x00", 9); register_table (fd, 0, buf); cp2155_set (fd, 0x18, 0x00); count = 260; make_slope_table (count, top_value, 0x6a, 0.021739, buf); write_buf (fd, count, buf, 0x03, 0x00); write_buf (fd, count, buf, 0x03, 0x02); write_buf (fd, count, buf, 0x03, 0x06); count = 36; make_slope_table (count, top_value, 0x06, 0.15217, buf); write_buf (fd, count, buf, 0x03, 0x04); write_buf (fd, count, buf, 0x03, 0x08); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x11, 0x35); cp2155_set (fd, 0x60, 0x15); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x03, 0x01); } void back2224 (int fd, unsigned char *buf) { size_t count; unsigned int top_value = 0x2580; unsigned char value_62 = 0x2e; /* original: unsigned int top_value = 0x2580; unsigned char value_62 = 0x2e; ratio 320 decimal */ cp2155_set (fd, 0x90, 0xe8); cp2155_set (fd, 0x9b, 0x06); cp2155_set (fd, 0x9b, 0x04); cp2155_set (fd, 0x90, 0xf8); cp2155_set (fd, 0xb0, 0x03); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x07, 0x00); cp2155_set (fd, 0x08, 0x01); cp2155_set (fd, 0x09, 0xb3); cp2155_set (fd, 0x0a, 0x02); cp2155_set (fd, 0x0b, 0x32); cp2155_set (fd, 0xa0, 0x1d); cp2155_set (fd, 0xa1, 0x00); cp2155_set (fd, 0xa2, 0x00); cp2155_set (fd, 0xa3, 0x70); cp2155_set (fd, 0x64, 0x00); cp2155_set (fd, 0x65, 0x00); cp2155_set (fd, 0x61, 0x00); cp2155_set (fd, 0x62, value_62); cp2155_set (fd, 0x63, 0x00); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x50, 0x04); /* cp2155_set (fd, 0x90, 0xf8); */ cp2155_set (fd, 0x51, 0x07); cp2155_set (fd, 0x5a, 0xff); cp2155_set (fd, 0x5b, 0xff); cp2155_set (fd, 0x5c, 0xff); cp2155_set (fd, 0x5d, 0xff); cp2155_set (fd, 0x52, 0x00); cp2155_set (fd, 0x53, 0x01); cp2155_set (fd, 0x54, 0x00); cp2155_set (fd, 0x55, 0x01); cp2155_set (fd, 0x56, 0x00); cp2155_set (fd, 0x57, 0x01); cp2155_set (fd, 0x58, 0x00); cp2155_set (fd, 0x59, 0x01); cp2155_set (fd, 0x5e, 0x02); cp2155_set (fd, 0x5f, 0x00); cp2155_set (fd, 0x5f, 0x03); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x50, 0x04); cp2155_set (fd, 0x51, 0x07); cp2155_set (fd, 0x81, 0x31); cp2155_set (fd, 0x81, 0x31); cp2155_set (fd, 0x82, 0x11); cp2155_set (fd, 0x82, 0x11); cp2155_set (fd, 0x83, 0x01); cp2155_set (fd, 0x84, 0x05); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0xb0, 0x03); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x11, 0x41); cp2155_set (fd, 0x11, 0x61); cp2155_set (fd, 0x11, 0x21); cp2155_set (fd, 0x11, 0x21); cp2155_set (fd, 0x11, 0x25); cp2155_set (fd, 0x11, 0x25); cp2155_set (fd, 0x11, 0x25); cp2155_set (fd, 0x12, 0x7d); cp2155_set (fd, 0x13, 0x7d); cp2155_set (fd, 0x16, 0x7d); cp2155_set (fd, 0x21, 0x06); cp2155_set (fd, 0x22, 0x7d); cp2155_set (fd, 0x20, 0x06); cp2155_set (fd, 0x1d, 0x00); cp2155_set (fd, 0x1e, 0x36); cp2155_set (fd, 0x1f, 0xd0); cp2155_set (fd, 0x66, 0x00); cp2155_set (fd, 0x67, 0x00); cp2155_set (fd, 0x68, 0x06); register_table (fd, 0x0d, buf); cp2155_set (fd, 0x18, 0x00); count = 516; make_slope_table (count, top_value, 0x06, 0.0067225, buf); write_buf (fd, count, buf, 0x03, 0x00); write_buf (fd, count, buf, 0x03, 0x02); write_buf (fd, count, buf, 0x03, 0x06); count = 36; make_slope_table (count, top_value, 0x06, 0.15217, buf); write_buf (fd, count, buf, 0x03, 0x04); write_buf (fd, count, buf, 0x03, 0x08); cp2155_set (fd, 0x10, 0x05); cp2155_set (fd, 0x11, 0x35); cp2155_set (fd, 0x60, 0x01); cp2155_set (fd, 0x80, 0x12); cp2155_set (fd, 0x03, 0x01); } static void go_home_without_wait (CANON_Handle * chndl) { unsigned char buf[0x400]; int fd = chndl->fd; byte value; cp2155_get (fd, 0x46, &value); if (value == 0x08) { return; } DBG (1, "go_home_without_wait: product code: %x\n", chndl->productcode); if (chndl->productcode == 0x2225) { back2225 (fd, buf); } else { back2224 (fd, buf); } } static int go_home (CANON_Handle * chndl) { int fd = chndl->fd; byte value; cp2155_get (fd, 0x46, &value); DBG (1, "state sensor: %02x\n", value); if (value == 0x08) { return 0; } go_home_without_wait (chndl); while (1) { usleep (200 * MSEC); cp2155_get (fd, 0x46, &value); DBG (1, "state sensor: %02x\n", value); if (value == 0x08) { break; } } return 0; } /* Scan and save the resulting image as r,g,b non-interleaved PPM file */ static SANE_Status preread (CANON_Handle * chndl, SANE_Byte * data, FILE * fp) { SANE_Status status = SANE_STATUS_GOOD; static byte linebuf[0x40000]; byte readbuf[0xf000]; int fd = chndl->fd; long width = chndl->params.pixels_per_line; /* set width to next multiple of 0x10 */ while ((width % 0x10) != 0xf) { width++; } width++; byte *srcptr = readbuf; static byte *dstptr = linebuf; byte *endptr = linebuf + 3 * width; /* Red line + Green line + Blue line */ long datasize = 0; static long line = 0; size_t offset = 0; size_t bytes_written; static byte slot = 0; /* Data coming back is "width" bytes Red data, width bytes Green, width bytes Blue, repeat for "height" lines. */ /* while (line < height) process one buffer from the scanner */ long startline = line; if (line >= (chndl->y1) * chndl->val[opt_resolution].w / 600 + chndl->params.lines) { status = SANE_STATUS_EOF; init (chndl); line = 0; slot = 0; dstptr = linebuf; return status; } datasize = wait_for_data (chndl); if (datasize < 0) { DBG (1, "no data\n"); status = SANE_STATUS_EOF; return status; } if (datasize > 0xf000) { datasize = 0xf000; } DBG (12, "scan line %ld %ld\n", line, datasize); cp2155_set (fd, 0x72, (datasize >> 8) & 0xff); cp2155_set (fd, 0x73, (datasize) & 0xff); status = cp2155_read (fd, readbuf, datasize); if (status != SANE_STATUS_GOOD) { status = SANE_STATUS_INVAL; return status; } /* Contorsions to convert data from line-by-line RGB to byte-by-byte RGB, without reading in the whole buffer first. One image line is constructed in buffer linebuf and written to temp file if complete. */ int idx = 0; srcptr = readbuf; while (idx < datasize) { *dstptr = (byte) * srcptr; idx++; srcptr += 1; dstptr += 3; if (dstptr >= endptr) /* line of one color complete */ { slot++; /* next color for this line */ dstptr = linebuf + slot; /* restart shortly after beginning */ if (slot == 3) /* all colors done */ { slot = 0; /* back to first color */ dstptr = linebuf; /* back to beginning of line */ line++; /* number of line just completed */ /* use scanner->width instead of width to remove pad bytes */ if (line > (chndl->y1) * chndl->val[opt_resolution].w / 600) { if (chndl->params.format == SANE_FRAME_RGB) { memcpy (data + offset, linebuf, 3 * chndl->width); offset += 3 * chndl->width; } else { int grayvalue; int lineelement = 0; while (lineelement < chndl->width) { grayvalue = linebuf[3 * lineelement] + linebuf[3 * lineelement + 1] + linebuf[3 * lineelement + 2]; grayvalue /= 3; if (chndl->params.depth == 8) /* gray */ { data[offset + lineelement] = (byte) grayvalue; } else /* lineart */ { if (lineelement % 8 == 0) { data[offset + (lineelement >> 3)] = 0; } if ((byte) grayvalue < chndl->absolute_threshold) { data[offset + (lineelement >> 3)] |= (1 << (7 - lineelement % 8)); } } lineelement++; } offset += chndl->params.bytes_per_line; } DBG (6, "line %ld written...\n", line); } if (line == (chndl->y1) * chndl->val[opt_resolution].w / 600 + chndl->params.lines) { break; } } } } /* one readbuf processed */ bytes_written = fwrite (data, 1, offset, fp); DBG (6, "%ld bytes written\n", bytes_written); if (bytes_written != offset) { status = SANE_STATUS_IO_ERROR; } DBG (6, "%ld lines from readbuf\n", line - startline); return status; /* to escape from this loop after processing only one data buffer */ } /* Scan and save the resulting image as r,g,b non-interleaved PPM file */ static SANE_Status do_scan (CANON_Handle * chndl) { SANE_Status status = SANE_STATUS_GOOD; SANE_Byte outbuf[0x40000]; FILE *fp; fp = fopen (chndl->fname, "w"); if (!fp) { DBG (1, "err:%s when opening %s\n", strerror (errno), chndl->fname); return SANE_STATUS_IO_ERROR; } long width = chndl->params.pixels_per_line; if (chndl->val[opt_resolution].w < 600) { width = width * 600 / chndl->val[opt_resolution].w; } /* set width to next multiple of 0x10 */ while ((width % 0x10) != 0xf) { width++; } long x_start; long x_end; long left_edge = 0x69; switch (chndl->val[opt_resolution].w) { case 75: case 150: case 300: case 600: if (chndl->productcode == 0x2224) { left_edge = 0x1b3; } else { left_edge = 0x69; } break; case 1200: if (chndl->productcode == 0x2224) { left_edge = 0x1b2; } else { left_edge = 0x87; } break; case 2400: if (chndl->productcode == 0x2224) { left_edge = 0x287; /* 0x2eb; */ } else { left_edge = 0x87; } break; case 4800: if (chndl->productcode == 0x2224) { left_edge = 0x2e3; /* should be adjusted; 0x23e; 0x2eb; */ } else { left_edge = 0x87; } } x_start = left_edge + chndl->x1 * chndl->val[opt_resolution].w / 600; if (chndl->val[opt_resolution].w < 600) { x_start = left_edge + chndl->x1; } x_end = x_start + (width); width++; chndl->value_08 = (x_start >> 8) & 0xff; chndl->value_09 = (x_start) & 0xff; chndl->value_0a = (x_end >> 8) & 0xff; chndl->value_0b = (x_end) & 0xff; DBG (3, "val_08: %02x\n", chndl->value_08); DBG (3, "val_09: %02x\n", chndl->value_09); DBG (3, "val_0a: %02x\n", chndl->value_0a); DBG (3, "val_0b: %02x\n", chndl->value_0b); DBG (3, "chndl->width: %04lx\n", chndl->width); send_start_blob (chndl); while (status == SANE_STATUS_GOOD) { status = preread (chndl, outbuf, fp); } go_home_without_wait (chndl); if (status == SANE_STATUS_EOF) { status = SANE_STATUS_GOOD; } fclose (fp); DBG (6, "created scan file %s\n", chndl->fname); return status; } /* Scan sequence */ /* resolution is 75,150,300,600,1200,2400,4800 scan coordinates in 600-dpi pixels */ static SANE_Status scan (CANON_Handle * chndl) { SANE_Status status = SANE_STATUS_GOOD; /* Resolution: dpi 75, 150, 300, 600, 1200, 2400, 4800 */ switch (chndl->val[opt_resolution].w) { case 75: case 150: case 300: case 600: case 1200: case 2400: case 4800: break; default: chndl->val[opt_resolution].w = 600; } chndl->width = chndl->params.pixels_per_line; chndl->height = (chndl->y2 - chndl->y1) * chndl->val[opt_resolution].w / 600; DBG (1, "dpi=%d\n", chndl->val[opt_resolution].w); DBG (1, "x1=%d y1=%d\n", chndl->x1, chndl->y1); DBG (1, "x2=%d y2=%d\n", chndl->x2, chndl->y2); DBG (1, "width=%ld height=%ld\n", chndl->width, chndl->height); CHK (do_scan (chndl)); return status; } static SANE_Status CANON_set_scan_parameters (CANON_Handle * chndl) { int left; int top; int right; int bottom; double leftf; double rightf; double topf; double bottomf; double widthf; double heightf; int widthi; int heighti; int top_edge = 7; /* in mm */ if (chndl->val[opt_resolution].w < 300) { top_edge = 0; } if (chndl->val[opt_resolution].w == 300 && chndl->productcode == 0x2224) { top_edge = 0; } left = SANE_UNFIX (chndl->val[opt_tl_x].w) / MM_IN_INCH * 600; top = (top_edge + SANE_UNFIX (chndl->val[opt_tl_y].w)) / MM_IN_INCH * 600; right = SANE_UNFIX (chndl->val[opt_br_x].w) / MM_IN_INCH * 600; bottom = (top_edge + SANE_UNFIX (chndl->val[opt_br_y].w)) / MM_IN_INCH * 600; leftf = SANE_UNFIX (chndl->val[opt_tl_x].w); rightf = SANE_UNFIX (chndl->val[opt_br_x].w); topf = SANE_UNFIX (chndl->val[opt_tl_y].w); bottomf = SANE_UNFIX (chndl->val[opt_br_y].w); widthf = (rightf - leftf) / MM_PER_INCH * 600; widthi = (int) widthf; heightf = (bottomf - topf) / MM_PER_INCH * 600; heighti = (int) heightf; DBG (2, "CANON_set_scan_parameters:\n"); DBG (2, "widthf = %f\n", widthf); DBG (2, "widthi = %d\n", widthi); DBG (2, "in 600dpi pixels:\n"); DBG (2, "left = %d, top = %d\n", left, top); DBG (2, "right = %d, bottom = %d\n", right, bottom); /* Validate the input parameters */ if ((left < 0) || (right > CANON_MAX_WIDTH)) { return SANE_STATUS_INVAL; } if ((top < 0) || (bottom > CANON_MAX_HEIGHT)) { return SANE_STATUS_INVAL; } if (((right - left) < 10) || ((bottom - top) < 10)) { return SANE_STATUS_INVAL; } if ((chndl->val[opt_resolution].w != 75) && (chndl->val[opt_resolution].w != 150) && (chndl->val[opt_resolution].w != 300) && (chndl->val[opt_resolution].w != 600) && (chndl->val[opt_resolution].w != 1200) && (chndl->val[opt_resolution].w != 2400) && (chndl->val[opt_resolution].w != 4800)) { return SANE_STATUS_INVAL; } /* Store params */ chndl->x1 = left; chndl->x2 = left + widthi; chndl->y1 = top; chndl->y2 = top + heighti; chndl->absolute_threshold = (chndl->val[opt_threshold].w * 255) / 100; return SANE_STATUS_GOOD; } static SANE_Status CANON_close_device (CANON_Handle * scan) { DBG (3, "CANON_close_device:\n"); sanei_usb_close (scan->fd); return SANE_STATUS_GOOD; } static SANE_Status CANON_open_device (CANON_Handle * scan, const char *dev) { SANE_Word vendor; SANE_Word product; SANE_Status res; DBG (3, "CANON_open_device: `%s'\n", dev); scan->fname = NULL; scan->fp = NULL; res = sanei_usb_open (dev, &scan->fd); if (res != SANE_STATUS_GOOD) { DBG (1, "CANON_open_device: couldn't open device `%s': %s\n", dev, sane_strstatus (res)); return res; } scan->product = "unknown"; #ifndef NO_AUTODETECT /* We have opened the device. Check that it is a USB scanner. */ if (sanei_usb_get_vendor_product (scan->fd, &vendor, &product) != SANE_STATUS_GOOD) { DBG (1, "CANON_open_device: sanei_usb_get_vendor_product failed\n"); /* This is not a USB scanner, or SANE or the OS doesn't support it. */ sanei_usb_close (scan->fd); scan->fd = -1; return SANE_STATUS_UNSUPPORTED; } /* Make sure we have a CANON scanner */ if (vendor == 0x04a9) { scan->product = "Canon"; scan->productcode = product; if (product == 0x2224) { scan->product = "CanoScan LiDE 600F"; } else if (product == 0x2225) { scan->product = "CanoScan LiDE 70"; } else { DBG (1, "CANON_open_device: incorrect vendor/product (0x%x/0x%x)\n", vendor, product); sanei_usb_close (scan->fd); scan->fd = -1; return SANE_STATUS_UNSUPPORTED; } } #endif return SANE_STATUS_GOOD; } static const char * CANON_get_device_name (CANON_Handle * chndl) { return chndl->product; } static SANE_Status CANON_finish_scan (CANON_Handle * chndl) { DBG (3, "CANON_finish_scan:\n"); if (chndl->fp) { fclose (chndl->fp); } chndl->fp = NULL; /* remove temp file */ if (chndl->fname) { DBG (4, "removing temp file %s\n", chndl->fname); unlink (chndl->fname); free (chndl->fname); } chndl->fname = NULL; return SANE_STATUS_GOOD; } static SANE_Status CANON_start_scan (CANON_Handle * chndl) { SANE_Status status; int result; int fd; DBG (3, "CANON_start_scan called\n"); /* choose a temp file name for scan data */ chndl->fname = strdup ("/tmp/scan.XXXXXX"); fd = mkstemp (chndl->fname); if (fd == -1) { return SANE_STATUS_IO_ERROR; } close (fd); /* check if calibration needed */ result = init (chndl); if (result < 0) { DBG (1, "Can't talk on USB.\n"); return SANE_STATUS_IO_ERROR; } go_home (chndl); /* scan */ if ((status = scan (chndl)) != SANE_STATUS_GOOD) { CANON_finish_scan (chndl); return status; } /* prepare for reading the temp file back out */ chndl->fp = fopen (chndl->fname, "r"); DBG (4, "reading %s\n", chndl->fname); if (!chndl->fp) { DBG (1, "open %s", chndl->fname); return SANE_STATUS_IO_ERROR; } return SANE_STATUS_GOOD; } static SANE_Status CANON_read (CANON_Handle * chndl, SANE_Byte * data, SANE_Int max_length, SANE_Int * length) { SANE_Status status; int read_len; DBG (5, "CANON_read called\n"); if (!chndl->fp) { return SANE_STATUS_INVAL; } read_len = fread (data, 1, max_length, chndl->fp); /* return some data */ if (read_len > 0) { *length = read_len; DBG (5, "CANON_read returned (%d/%d)\n", *length, max_length); return SANE_STATUS_GOOD; } /* EOF or file err */ *length = 0; if (feof (chndl->fp)) { DBG (4, "EOF\n"); status = SANE_STATUS_EOF; } else { DBG (4, "IO ERR\n"); status = SANE_STATUS_IO_ERROR; } CANON_finish_scan (chndl); DBG (5, "CANON_read returned (%d/%d)\n", *length, max_length); return status; } backends-1.3.0/backend/canon_lide70.c000066400000000000000000000616741456256263500173160ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. BACKEND canon_lide70 Copyright (C) 2019 Juergen Ernst and pimvantend. 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, see . This file implements a SANE backend for the Canon CanoScan LiDE 70 and 600 */ #define BUILD 0 #define MM_IN_INCH 25.4 #include "../include/sane/config.h" #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_usb.h" #define BACKEND_NAME canon_lide70 #define CANONUSB_CONFIG_FILE "canon_lide70.conf" #include "../include/sane/sanei_backend.h" typedef enum { opt_num_opts = 0, opt_mode_group, opt_threshold, opt_mode, opt_resolution, opt_non_blocking, opt_geometry_group, opt_tl_x, opt_tl_y, opt_br_x, opt_br_y, /* must come last: */ num_options } canon_opts; #include "canon_lide70-common.c" 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; } static SANE_String_Const mode_list[] = { SANE_VALUE_SCAN_MODE_COLOR, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_LINEART, 0 }; static SANE_Fixed init_tl_x = SANE_FIX (0.0); static SANE_Fixed init_tl_y = SANE_FIX (0.0); static SANE_Fixed init_br_x = SANE_FIX (80.0); static SANE_Fixed init_br_y = SANE_FIX (100.0); static SANE_Int init_threshold = 75; static SANE_Int init_resolution = 600; static SANE_String init_mode = SANE_VALUE_SCAN_MODE_COLOR; static SANE_Int init_graymode = 0; static SANE_Bool init_non_blocking = SANE_FALSE; /*-----------------------------------------------------------------*/ /* Scan range */ static const SANE_Range widthRange = { 0, /* minimum */ SANE_FIX (CANON_MAX_WIDTH * MM_IN_INCH / 600), /* maximum */ 0 /* quantization */ }; static const SANE_Range heightRange = { 0, /* minimum */ /* SANE_FIX (CANON_MAX_HEIGHT * MM_IN_INCH / 600 - TOP_EDGE ), maximum */ SANE_FIX (297.0), 0 /* quantization */ }; static const SANE_Range threshold_range = { 0, 100, 1 }; static SANE_Int resolution_list[] = { 5, 75, 150, 300, 600, 1200 }; typedef struct Canon_Device { struct Canon_Device *next; SANE_String name; SANE_Device sane; } Canon_Device; /* Canon_Scanner is the type used for the sane handle */ typedef struct Canon_Scanner { struct Canon_Scanner *next; Canon_Device *device; CANON_Handle scan; } Canon_Scanner; static int num_devices = 0; static const SANE_Device **devlist = NULL; static Canon_Device *first_dev = NULL; static Canon_Scanner *first_handle = NULL; /*-----------------------------------------------------------------*/ static SANE_Status attach_scanner (const char *devicename, Canon_Device ** devp) { CANON_Handle scan; Canon_Device *dev; SANE_Status status; DBG (3, "attach_scanner: %s\n", devicename); for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->sane.name, devicename) == 0) { if (devp) *devp = dev; return SANE_STATUS_GOOD; } } dev = malloc (sizeof (*dev)); if (!dev) return SANE_STATUS_NO_MEM; memset (dev, '\0', sizeof (Canon_Device)); /* clear structure */ DBG (4, "attach_scanner: opening %s\n", devicename); status = CANON_open_device (&scan, devicename); if (status != SANE_STATUS_GOOD) { DBG (1, "ERROR: attach_scanner: opening %s failed\n", devicename); free (dev); return status; } dev->name = strdup (devicename); dev->sane.name = dev->name; dev->sane.vendor = "CANON"; dev->sane.model = CANON_get_device_name (&scan); dev->sane.type = "flatbed scanner"; CANON_close_device (&scan); ++num_devices; dev->next = first_dev; first_dev = dev; if (devp) *devp = dev; return SANE_STATUS_GOOD; } /* callback function for sanei_usb_attach_matching_devices */ static SANE_Status attach_one (const char *name) { attach_scanner (name, 0); return SANE_STATUS_GOOD; } /* Find our devices */ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { char config_line[PATH_MAX]; size_t len; FILE *fp; DBG_INIT (); #if 0 DBG_LEVEL = 10; #endif DBG (2, "sane_init: version_code %s 0, authorize %s 0\n", version_code == 0 ? "=" : "!=", authorize == 0 ? "=" : "!="); DBG (1, "sane_init: SANE Canon LiDE70 backend version %d.%d.%d from %s\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD, PACKAGE_STRING); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); sanei_usb_init (); fp = sanei_config_open (CANONUSB_CONFIG_FILE); if (!fp) { /* no config-file: try these */ attach_scanner ("/dev/scanner", 0); attach_scanner ("/dev/usbscanner", 0); attach_scanner ("/dev/usb/scanner", 0); return SANE_STATUS_GOOD; } DBG (3, "reading configure file %s\n", CANONUSB_CONFIG_FILE); while (sanei_config_read (config_line, sizeof (config_line), fp)) { if (config_line[0] == '#') continue; /* ignore line comments */ len = strlen (config_line); if (!len) continue; /* ignore empty lines */ DBG (4, "attach_matching_devices(%s)\n", config_line); sanei_usb_attach_matching_devices (config_line, attach_one); } DBG (4, "finished reading configure file\n"); fclose (fp); return SANE_STATUS_GOOD; } void sane_exit (void) { Canon_Device *dev, *next; DBG (3, "sane_exit\n"); for (dev = first_dev; dev; dev = next) { next = dev->next; free (dev->name); free (dev); } if (devlist) free (devlist); return; } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { Canon_Device *dev; int i; DBG (3, "sane_get_devices(local_only = %d)\n", local_only); if (devlist) free (devlist); devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); if (!devlist) return SANE_STATUS_NO_MEM; i = 0; for (dev = first_dev; i < num_devices; dev = dev->next) devlist[i++] = &dev->sane; devlist[i++] = 0; *device_list = devlist; return SANE_STATUS_GOOD; } static SANE_Status init_options (CANON_Handle * chndl) { SANE_Option_Descriptor *od; DBG (2, "begin init_options: chndl=%p\n", (void *) chndl); /* opt_num_opts */ od = &chndl->opt[opt_num_opts]; od->name = ""; od->title = SANE_TITLE_NUM_OPTIONS; od->desc = SANE_DESC_NUM_OPTIONS; od->type = SANE_TYPE_INT; od->unit = SANE_UNIT_NONE; od->size = sizeof (SANE_Word); od->cap = SANE_CAP_SOFT_DETECT; od->constraint_type = SANE_CONSTRAINT_NONE; od->constraint.range = 0; chndl->val[opt_num_opts].w = num_options; DBG (2, "val[opt_num_opts]: %d\n", chndl->val[opt_num_opts].w); /* opt_mode_group */ od = &chndl->opt[opt_mode_group]; od->name = ""; od->title = SANE_I18N ("Scan Mode"); od->desc = ""; od->type = SANE_TYPE_GROUP; od->unit = SANE_UNIT_NONE; od->size = 0; od->cap = 0; od->constraint_type = SANE_CONSTRAINT_NONE; od->constraint.range = 0; chndl->val[opt_mode_group].w = 0; /* opt_mode */ od = &chndl->opt[opt_mode]; od->name = SANE_NAME_SCAN_MODE; od->title = SANE_TITLE_SCAN_MODE; od->desc = SANE_DESC_SCAN_MODE; od->type = SANE_TYPE_STRING; od->unit = SANE_UNIT_NONE; od->size = max_string_size (mode_list); od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; od->constraint_type = SANE_CONSTRAINT_STRING_LIST; od->constraint.string_list = mode_list; chndl->val[opt_mode].s = malloc (od->size); if (!chndl->val[opt_mode].s) return SANE_STATUS_NO_MEM; strcpy (chndl->val[opt_mode].s, init_mode); chndl->graymode = init_graymode; /* opt_threshold */ od = &chndl->opt[opt_threshold]; od->name = SANE_NAME_THRESHOLD; od->title = SANE_TITLE_THRESHOLD; od->desc = SANE_DESC_THRESHOLD; od->type = SANE_TYPE_INT; od->unit = SANE_UNIT_PERCENT; od->size = sizeof (SANE_Word); od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_INACTIVE; od->constraint_type = SANE_CONSTRAINT_RANGE; od->constraint.range = &threshold_range; chndl->val[opt_threshold].w = init_threshold; /* opt_resolution */ od = &chndl->opt[opt_resolution]; od->name = SANE_NAME_SCAN_RESOLUTION; od->title = SANE_TITLE_SCAN_RESOLUTION; od->desc = SANE_DESC_SCAN_RESOLUTION; od->type = SANE_TYPE_INT; od->unit = SANE_UNIT_DPI; od->size = sizeof (SANE_Word); od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; od->constraint_type = SANE_CONSTRAINT_WORD_LIST; if (chndl->productcode == 0x2224) { resolution_list[0] = 4; } od->constraint.word_list = resolution_list; chndl->val[opt_resolution].w = init_resolution; /* opt_non_blocking */ od = &chndl->opt[opt_non_blocking]; od->name = "non-blocking"; od->title = SANE_I18N ("Use non-blocking IO"); od->desc = SANE_I18N ("Use non-blocking IO for sane_read() if supported " "by the frontend."); od->type = SANE_TYPE_BOOL; od->unit = SANE_UNIT_NONE; od->size = sizeof (SANE_Word); od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_INACTIVE; od->constraint_type = SANE_CONSTRAINT_NONE; od->constraint.range = 0; chndl->val[opt_non_blocking].w = init_non_blocking; /* opt_geometry_group */ od = &chndl->opt[opt_geometry_group]; od->name = ""; od->title = SANE_I18N ("Geometry"); od->desc = ""; od->type = SANE_TYPE_GROUP; od->unit = SANE_UNIT_NONE; od->size = 0; od->cap = 0; od->constraint_type = SANE_CONSTRAINT_NONE; od->constraint.range = 0; chndl->val[opt_geometry_group].w = 0; /* opt_tl_x */ od = &chndl->opt[opt_tl_x]; od->name = SANE_NAME_SCAN_TL_X; od->title = SANE_TITLE_SCAN_TL_X; od->desc = SANE_DESC_SCAN_TL_X; od->type = SANE_TYPE_FIXED; od->unit = SANE_UNIT_MM; od->size = sizeof (SANE_Word); od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; od->constraint_type = SANE_CONSTRAINT_RANGE; od->constraint.range = &widthRange; chndl->val[opt_tl_x].w = init_tl_x; /* opt_tl_y */ od = &chndl->opt[opt_tl_y]; od->name = SANE_NAME_SCAN_TL_Y; od->title = SANE_TITLE_SCAN_TL_Y; od->desc = SANE_DESC_SCAN_TL_Y; od->type = SANE_TYPE_FIXED; od->unit = SANE_UNIT_MM; od->size = sizeof (SANE_Word); od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; od->constraint_type = SANE_CONSTRAINT_RANGE; od->constraint.range = &heightRange; chndl->val[opt_tl_y].w = init_tl_y; /* opt_br_x */ od = &chndl->opt[opt_br_x]; od->name = SANE_NAME_SCAN_BR_X; od->title = SANE_TITLE_SCAN_BR_X; od->desc = SANE_DESC_SCAN_BR_X; od->type = SANE_TYPE_FIXED; od->unit = SANE_UNIT_MM; od->size = sizeof (SANE_Word); od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; od->constraint_type = SANE_CONSTRAINT_RANGE; od->constraint.range = &widthRange; chndl->val[opt_br_x].w = init_br_x; /* opt_br_y */ od = &chndl->opt[opt_br_y]; od->name = SANE_NAME_SCAN_BR_Y; od->title = SANE_TITLE_SCAN_BR_Y; od->desc = SANE_DESC_SCAN_BR_Y; od->type = SANE_TYPE_FIXED; od->unit = SANE_UNIT_MM; od->size = sizeof (SANE_Word); od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; od->constraint_type = SANE_CONSTRAINT_RANGE; od->constraint.range = &heightRange; chndl->val[opt_br_y].w = init_br_y; DBG (2, "end init_options: chndl=%p\n", (void *) chndl); return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { Canon_Device *dev; SANE_Status status; Canon_Scanner *scanner; DBG (3, "sane_open\n"); if (devicename[0]) /* search for devicename */ { DBG (4, "sane_open: devicename=%s\n", devicename); for (dev = first_dev; dev; dev = dev->next) if (strcmp (dev->sane.name, devicename) == 0) break; if (!dev) { status = attach_scanner (devicename, &dev); if (status != SANE_STATUS_GOOD) return status; } } else { DBG (2, "sane_open: no devicename, opening first device\n"); dev = first_dev; } if (!dev) return SANE_STATUS_INVAL; scanner = malloc (sizeof (*scanner)); if (!scanner) return SANE_STATUS_NO_MEM; memset (scanner, 0, sizeof (*scanner)); scanner->device = dev; status = CANON_open_device (&scanner->scan, dev->sane.name); if (status != SANE_STATUS_GOOD) { free (scanner); return status; } status = init_options (&scanner->scan); *handle = scanner; /* insert newly opened handle into list of open handles: */ scanner->next = first_handle; first_handle = scanner; return status; } static void print_options (CANON_Handle * chndl) { SANE_Option_Descriptor *od; SANE_Word option_number; SANE_Char caps[1024]; for (option_number = 0; option_number < num_options; option_number++) { od = &chndl->opt[option_number]; DBG (50, "-----> number: %d\n", option_number); DBG (50, " name: `%s'\n", od->name); DBG (50, " title: `%s'\n", od->title); DBG (50, " description: `%s'\n", od->desc); DBG (50, " type: %s\n", od->type == SANE_TYPE_BOOL ? "SANE_TYPE_BOOL" : od->type == SANE_TYPE_INT ? "SANE_TYPE_INT" : od->type == SANE_TYPE_FIXED ? "SANE_TYPE_FIXED" : od->type == SANE_TYPE_STRING ? "SANE_TYPE_STRING" : od->type == SANE_TYPE_BUTTON ? "SANE_TYPE_BUTTON" : od->type == SANE_TYPE_GROUP ? "SANE_TYPE_GROUP" : "unknown"); DBG (50, " unit: %s\n", od->unit == SANE_UNIT_NONE ? "SANE_UNIT_NONE" : od->unit == SANE_UNIT_PIXEL ? "SANE_UNIT_PIXEL" : od->unit == SANE_UNIT_BIT ? "SANE_UNIT_BIT" : od->unit == SANE_UNIT_MM ? "SANE_UNIT_MM" : od->unit == SANE_UNIT_DPI ? "SANE_UNIT_DPI" : od->unit == SANE_UNIT_PERCENT ? "SANE_UNIT_PERCENT" : od->unit == SANE_UNIT_MICROSECOND ? "SANE_UNIT_MICROSECOND" : "unknown"); DBG (50, " size: %d\n", od->size); caps[0] = '\0'; if (od->cap & SANE_CAP_SOFT_SELECT) strcat (caps, "SANE_CAP_SOFT_SELECT "); if (od->cap & SANE_CAP_HARD_SELECT) strcat (caps, "SANE_CAP_HARD_SELECT "); if (od->cap & SANE_CAP_SOFT_DETECT) strcat (caps, "SANE_CAP_SOFT_DETECT "); if (od->cap & SANE_CAP_EMULATED) strcat (caps, "SANE_CAP_EMULATED "); if (od->cap & SANE_CAP_AUTOMATIC) strcat (caps, "SANE_CAP_AUTOMATIC "); if (od->cap & SANE_CAP_INACTIVE) strcat (caps, "SANE_CAP_INACTIVE "); if (od->cap & SANE_CAP_ADVANCED) strcat (caps, "SANE_CAP_ADVANCED "); DBG (50, " capabilities: %s\n", caps); DBG (50, "constraint type: %s\n", od->constraint_type == SANE_CONSTRAINT_NONE ? "SANE_CONSTRAINT_NONE" : od->constraint_type == SANE_CONSTRAINT_RANGE ? "SANE_CONSTRAINT_RANGE" : od->constraint_type == SANE_CONSTRAINT_WORD_LIST ? "SANE_CONSTRAINT_WORD_LIST" : od->constraint_type == SANE_CONSTRAINT_STRING_LIST ? "SANE_CONSTRAINT_STRING_LIST" : "unknown"); if (od->type == SANE_TYPE_INT) DBG (50, " value: %d\n", chndl->val[option_number].w); else if (od->type == SANE_TYPE_FIXED) DBG (50, " value: %f\n", SANE_UNFIX (chndl->val[option_number].w)); else if (od->type == SANE_TYPE_STRING) DBG (50, " value: %s\n", chndl->val[option_number].s); } } void sane_close (SANE_Handle handle) { Canon_Scanner *prev, *scanner; SANE_Status res; DBG (3, "sane_close\n"); scanner = handle; print_options (&scanner->scan); if (!first_handle) { DBG (1, "ERROR: sane_close: no handles opened\n"); return; } /* remove handle from list of open handles: */ prev = NULL; for (scanner = first_handle; scanner; scanner = scanner->next) { if (scanner == handle) break; prev = scanner; } if (!scanner) { DBG (1, "ERROR: sane_close: invalid handle %p\n", handle); return; /* oops, not a handle we know about */ } if (prev) prev->next = scanner->next; else first_handle = scanner->next; res = CANON_close_device (&scanner->scan); DBG (3, "CANON_close_device returned: %d\n", res); free (scanner); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { Canon_Scanner *scanner = handle; CANON_Handle *chndl = &scanner->scan; DBG (4, "sane_get_option_descriptor: handle=%p, option = %d\n", (void *) handle, option); if (option < 0 || option >= num_options) { DBG (3, "sane_get_option_descriptor: option < 0 || " "option > num_options\n"); return 0; } return &chndl->opt[option]; } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int * info) { Canon_Scanner *scanner = handle; CANON_Handle *chndl = &scanner->scan; SANE_Int myinfo = 0; SANE_Status status; DBG (4, "sane_control_option: handle=%p, opt=%d, act=%d, val=%p, info=%p\n", (void *) handle, option, action, (void *) value, (void *) info); if (option < 0 || option >= num_options) { DBG (1, "sane_control_option: option < 0 || option > num_options\n"); return SANE_STATUS_INVAL; } if (!SANE_OPTION_IS_ACTIVE (chndl->opt[option].cap)) { DBG (1, "sane_control_option: option is inactive\n"); return SANE_STATUS_INVAL; } if (chndl->opt[option].type == SANE_TYPE_GROUP) { DBG (1, "sane_control_option: option is a group\n"); return SANE_STATUS_INVAL; } switch (action) { case SANE_ACTION_SET_VALUE: if (!SANE_OPTION_IS_SETTABLE (chndl->opt[option].cap)) { DBG (1, "sane_control_option: option is not setable\n"); return SANE_STATUS_INVAL; } status = sanei_constrain_value (&chndl->opt[option], value, &myinfo); if (status != SANE_STATUS_GOOD) { DBG (3, "sane_control_option: sanei_constrain_value returned %s\n", sane_strstatus (status)); return status; } switch (option) { case opt_tl_x: /* Fixed with parameter reloading */ case opt_tl_y: case opt_br_x: case opt_br_y: if (chndl->val[option].w == *(SANE_Fixed *) value) { DBG (4, "sane_control_option: option %d (%s) not changed\n", option, chndl->opt[option].name); break; } chndl->val[option].w = *(SANE_Fixed *) value; myinfo |= SANE_INFO_RELOAD_PARAMS; DBG (4, "sane_control_option: set option %d (%s) to %.0f %s\n", option, chndl->opt[option].name, SANE_UNFIX (*(SANE_Fixed *) value), chndl->opt[option].unit == SANE_UNIT_MM ? "mm" : "dpi"); break; case opt_non_blocking: if (chndl->val[option].w == *(SANE_Bool *) value) { DBG (4, "sane_control_option: option %d (%s) not changed\n", option, chndl->opt[option].name); break; } chndl->val[option].w = *(SANE_Bool *) value; DBG (4, "sane_control_option: set option %d (%s) to %s\n", option, chndl->opt[option].name, *(SANE_Bool *) value == SANE_TRUE ? "true" : "false"); break; case opt_resolution: case opt_threshold: if (chndl->val[option].w == *(SANE_Int *) value) { DBG (4, "sane_control_option: option %d (%s) not changed\n", option, chndl->opt[option].name); break; } chndl->val[option].w = *(SANE_Int *) value; myinfo |= SANE_INFO_RELOAD_PARAMS; myinfo |= SANE_INFO_RELOAD_OPTIONS; DBG (4, "sane_control_option: set option %d (%s) to %d\n", option, chndl->opt[option].name, *(SANE_Int *) value); break; case opt_mode: if (strcmp (chndl->val[option].s, value) == 0) { DBG (4, "sane_control_option: option %d (%s) not changed\n", option, chndl->opt[option].name); break; } strcpy (chndl->val[option].s, (SANE_String) value); if (strcmp (chndl->val[option].s, SANE_VALUE_SCAN_MODE_LINEART) == 0) { chndl->opt[opt_threshold].cap &= ~SANE_CAP_INACTIVE; chndl->graymode = 2; } if (strcmp (chndl->val[option].s, SANE_VALUE_SCAN_MODE_COLOR) == 0) { chndl->opt[opt_threshold].cap |= SANE_CAP_INACTIVE; chndl->graymode = 0; } if (strcmp (chndl->val[option].s, SANE_VALUE_SCAN_MODE_GRAY) == 0) { chndl->opt[opt_threshold].cap |= SANE_CAP_INACTIVE; chndl->graymode = 1; } myinfo |= SANE_INFO_RELOAD_PARAMS; myinfo |= SANE_INFO_RELOAD_OPTIONS; DBG (4, "sane_control_option: set option %d (%s) to %s\n", option, chndl->opt[option].name, (SANE_String) value); break; default: DBG (1, "sane_control_option: trying to set unexpected option\n"); return SANE_STATUS_INVAL; } break; case SANE_ACTION_GET_VALUE: switch (option) { case opt_num_opts: *(SANE_Word *) value = num_options; DBG (4, "sane_control_option: get option 0, value = %d\n", num_options); break; case opt_tl_x: /* Fixed options */ case opt_tl_y: case opt_br_x: case opt_br_y: { *(SANE_Fixed *) value = chndl->val[option].w; DBG (4, "sane_control_option: get option %d (%s), value=%.1f %s\n", option, chndl->opt[option].name, SANE_UNFIX (*(SANE_Fixed *) value), chndl->opt[option].unit == SANE_UNIT_MM ? "mm" : (chndl->opt[option].unit == SANE_UNIT_DPI ? "dpi" : "")); break; } case opt_non_blocking: *(SANE_Bool *) value = chndl->val[option].w; DBG (4, "sane_control_option: get option %d (%s), value=%s\n", option, chndl->opt[option].name, *(SANE_Bool *) value == SANE_TRUE ? "true" : "false"); break; case opt_mode: /* String (list) options */ strcpy (value, chndl->val[option].s); DBG (4, "sane_control_option: get option %d (%s), value=`%s'\n", option, chndl->opt[option].name, (SANE_String) value); break; case opt_resolution: case opt_threshold: *(SANE_Int *) value = chndl->val[option].w; DBG (4, "sane_control_option: get option %d (%s), value=%d\n", option, chndl->opt[option].name, *(SANE_Int *) value); break; default: DBG (1, "sane_control_option: trying to get unexpected option\n"); return SANE_STATUS_INVAL; } break; default: DBG (1, "sane_control_option: trying unexpected action %d\n", action); return SANE_STATUS_INVAL; } if (info) *info = myinfo; return SANE_STATUS_GOOD; } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { Canon_Scanner *hndl = handle; /* Eliminate compiler warning */ CANON_Handle *chndl = &hndl->scan; SANE_Status rc = SANE_STATUS_GOOD; int w = SANE_UNFIX (chndl->val[opt_br_x].w - chndl->val[opt_tl_x].w) / MM_IN_INCH * chndl->val[opt_resolution].w; int h = SANE_UNFIX (chndl->val[opt_br_y].w - chndl->val[opt_tl_y].w) / MM_IN_INCH * chndl->val[opt_resolution].w; DBG (3, "sane_get_parameters\n"); chndl->params.depth = 8; chndl->params.last_frame = SANE_TRUE; chndl->params.pixels_per_line = w; chndl->params.lines = h; if (chndl->graymode == 1) { chndl->params.format = SANE_FRAME_GRAY; chndl->params.bytes_per_line = w; } else if (chndl->graymode == 2) { chndl->params.format = SANE_FRAME_GRAY; w /= 8; if ((chndl->params.pixels_per_line % 8) != 0) w++; chndl->params.bytes_per_line = w; chndl->params.depth = 1; } else { chndl->params.format = SANE_FRAME_RGB; chndl->params.bytes_per_line = w * 3; } *params = chndl->params; DBG (1, "%d\n", chndl->params.format); return rc; } SANE_Status sane_start (SANE_Handle handle) { Canon_Scanner *scanner = handle; CANON_Handle *chndl = &scanner->scan; SANE_Status res; DBG (3, "sane_start\n"); res = sane_get_parameters (handle, &chndl->params); res = CANON_set_scan_parameters (&scanner->scan); if (res != SANE_STATUS_GOOD) return res; return CANON_start_scan (&scanner->scan); } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * data, SANE_Int max_length, SANE_Int * length) { Canon_Scanner *scanner = handle; return CANON_read (&scanner->scan, data, max_length, length); } void sane_cancel (SANE_Handle handle) { DBG (3, "sane_cancel: handle = %p\n", handle); DBG (3, "sane_cancel: cancelling is unsupported in this backend\n"); } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { DBG (3, "sane_set_io_mode: handle = %p, non_blocking = %d\n", handle, non_blocking); if (non_blocking != SANE_FALSE) return SANE_STATUS_UNSUPPORTED; return SANE_STATUS_GOOD; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { (void) handle; /* silence gcc */ (void) fd; /* silence gcc */ return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/canon_lide70.conf.in000066400000000000000000000004021456256263500204040ustar00rootroot00000000000000# Options for the canon_lide70 backend # Autodetect the Canon CanoScan LiDE 70 usb 0x04a9 0x2225 # Autodetect the Canon CanoScan LiDE 600 usb 0x04a9 0x2224 # device list for non-linux-systems (enable if autodetect fails): #/dev/scanner #/dev/usb/scanner0 backends-1.3.0/backend/canon_pp-dev.c000066400000000000000000001127101456256263500174110ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2001-2002 Matthew C. Duggan and Simon Krix 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, see . 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 is part of the canon_pp backend, supporting Canon CanoScan Parallel scanners and also distributed as part of the stand-alone driver. Misc constants for Canon CanoScan Parallel scanners and high-level scan functions. Simon Krix */ #ifdef _AIX #include #endif #ifndef NOSANE #include "../include/sane/config.h" #endif #include #include #include #include #include #include #include #include "canon_pp-io.h" #include "canon_pp-dev.h" #ifdef NOSANE /* No SANE, Things that only apply to stand-alone */ #include #include static void DBG(int level, const char *format, ...) { va_list args; va_start(args, format); if (level < 50) vfprintf(stderr, format, args); va_end(args); } #else #define DEBUG_DECLARE_ONLY #include "canon_pp.h" #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_backend.h" #endif struct scanner_hardware_desc { char *name; unsigned int natural_xresolution; unsigned int natural_yresolution; unsigned int scanbedlength; unsigned int scanheadwidth; /* 0 means provided by scanner */ unsigned int type; }; static const struct scanner_hardware_desc /* The known scanner types */ hw_fb320p = { "FB320P", 2, 2, 3508, 2552, 0 }, hw_fb330p = { "FB330P", 2, 2, 3508, 0, 1 }, hw_fb620p = { "FB620P", 3, 3, 7016, 5104, 0 }, hw_fb630p = { "FB630P", 3, 3, 7016, 0, 1 }, hw_n640p = { "N640P", 3, 3, 7016, 0, 1 }, hw_n340p = { "N340P", 2, 2, 3508, 0, 1 }, /* A few generic scanner descriptions for aliens */ hw_alien600 = { "Unknown 600dpi", 3, 3, 7016, 0, 1 }, hw_alien300 = { "Unknown 300dpi", 2, 2, 3508, 0, 1 }, hw_alien = { "Unknown (600dpi?)", 3, 3, 7016, 0, 1 }; /* ID table linking ID strings with hardware descriptions */ struct scanner_id { char *id; const struct scanner_hardware_desc *hw; }; static const struct scanner_id scanner_id_table[] = { { "CANON IX-03055C", &hw_fb320p }, { "CANON IX-06025C", &hw_fb620p }, { "CANON IX-03075E", &hw_fb330p }, { "CANON IX-06075E", &hw_fb630p }, { "CANON IX-03095G", &hw_n340p }, { "CANON IX-06115G", &hw_n640p }, { NULL, NULL } }; /*const int scanline_count = 6;*/ static const char *header = "#CANONPP"; static const int fileversion = 3; /* Internal functions */ static unsigned long column_sum(image_segment *image, int x); static int adjust_output(image_segment *image, scan_parameters *scanp, scanner_parameters *scannerp); static int check8(unsigned char *p, int s); /* Converts from weird scanner format -> sequential data */ static void convdata(unsigned char *srcbuffer, unsigned char *dstbuffer, int width, int mode); /* Sets up the scan command. This could use a better name (and a rewrite). */ static int scanner_setup_params(unsigned char *buf, scanner_parameters *sp, scan_parameters *scanp); /* file reading and writing helpers */ static int safe_write(int fd, const char *p, unsigned long len); static int safe_read(int fd, char *p, unsigned long len); /* Command sending loop (waiting for ready status) */ static int send_command(struct parport *port, unsigned char *buf, int bufsize, int delay, int timeout); /* Commands ================================================ */ /* Command_1[] moved to canon_pp-io.c for neatness */ /* Read device ID command */ /* after this 0x26 (38) bytes are read */ static unsigned char cmd_readid[] = { 0xfe, 0x20, 0, 0, 0, 0, 0, 0, 0x26, 0 }; /* Reads 12 bytes of unknown information */ static unsigned char cmd_readinfo[] = { 0xf3, 0x20, 0, 0, 0, 0, 0, 0, 0x0c, 0 }; /* Scan init command: Always followed immediately by command cmd_scan */ static unsigned char cmd_initscan[] = { 0xde, 0x20, 0, 0, 0, 0, 0, 0, 0x2e, 0 }; /* Scan information block */ static unsigned char cmd_scan[45] = { 0x11, 0x2c, 0x11, 0x2c, 0x10, 0x4b, 0x10, 0x4b, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x01, 0x01, 0x80, 0x01, 0x80, 0x80, 0x02, 0, 0, 0xc1, 0, 0x08, 0x01, 0x01, 0, 0, 0, 0, 0 }; /* Read 6 byte buffer status block */ static unsigned char cmd_buf_status[] = { 0xf3, 0x21, 0, 0, 0, 0, 0, 0, 0x06, 0 }; /* Request a block of image data */ static unsigned char cmd_packet_req[] = {0xd4, 0x20, 0, 0, 0, 0, 0, 0x09, 0x64, 0}; /* "*SCANEND" command - returns the scanner to transparent mode */ static unsigned char cmd_scanend[] = { 0x1b, 0x2a, 0x53, 0x43, 0x41, 0x4e, 0x45, 0x4e, 0x44, 0x0d }; /* Reads BLACK calibration image */ static unsigned char cmd_calblack[] ={0xf8, 0x20, 0, 0, 0, 0, 0, 0x4a, 0xc4, 0}; /* Clear the existing gamma table and create a new one */ static unsigned char cmd_cleargamma[] = {0xc5, 0x20, 0, 0, 0, 0, 0, 0, 0, 0}; /* Read back the gamma table values */ static unsigned char cmd_readgamma[] = {0xf6, 0x20, 0, 0, 0, 0, 0, 0, 0x20, 0}; /* Reads COLOUR (R,G or B) calibration image */ static unsigned char cmd_calcolour[]={0xf9, 0x20, 0, 0, 0, 0, 0, 0x4a, 0xc4, 0}; /* Abort scan */ static unsigned char cmd_abort[] = {0xef, 0x20, 0, 0, 0, 0, 0, 0, 0, 0}; /* Upload the gamma table (followed by 32 byte write) */ static unsigned char cmd_setgamma[] = {0xe6, 0x20, 0, 0, 0, 0, 0, 0, 0x20, 0}; #if 0 /* Something about RGB gamma/gain values? Not currently used by this code */ static unsigned char command_14[32] = { 0x2, 0x0, 0x3, 0x7f, 0x2, 0x0, 0x3, 0x7f, 0x2, 0x0, 0x3, 0x7f, 0, 0, 0, 0, 0x12, 0xd1, 0x14, 0x82, 0, 0, 0, 0, 0x0f, 0xff, 0x0f, 0xff, 0x0f, 0xff, 0, 0 }; #endif /* Misc functions =================================== */ /* * safe_write(): a small wrapper which ensures all the data is written in calls * to write(), since the POSIX call doesn't ensure it. */ static int safe_write(int fd, const char *p, unsigned long len) { int diff; unsigned long total = 0; do { diff = write(fd, p+total, len-total); if (diff < 0) { if (errno == EINTR) continue; return -1; } total += diff; } while (len > total); return 0; } /* same dealie for read, except in the case of read the return of 0 bytes with * no INTR error indicates EOF */ static int safe_read(int fd, char *p, unsigned long len) { int diff; unsigned long total = 0; do { diff = read(fd, p+total, len-total); if (diff <= 0) { if (errno == EINTR) continue; if (diff == 0) return -2; return -1; } total += diff; } while (len > total); return 0; } /* Scan-related functions =================================== */ int sanei_canon_pp_init_scan(scanner_parameters *sp, scan_parameters *scanp) { /* Command for: Initialise and begin the scan procedure */ unsigned char command_b[56]; /* Buffer for buffer info block */ unsigned char buffer_info_block[6]; /* The image size the scanner says we asked for (based on the scanner's replies) */ int true_scanline_size, true_scanline_count; /* The image size we expect to get (based on *scanp) */ int expected_scanline_size, expected_scanline_count; /* Set up the default scan command packet */ memcpy(command_b, cmd_initscan, 10); memcpy(command_b+10, cmd_scan, 45); /* Load the proper settings into it */ scanner_setup_params(command_b+10, sp, scanp); /* Add checksum byte */ command_b[55] = check8(command_b+10, 45); if (send_command(sp->port, command_b, 56, 50000, 1000000)) return -1; /* Ask the scanner about the buffer */ if (send_command(sp->port, cmd_buf_status, 10, 50000, 1000000)) return -1; /* Read buffer information block */ sanei_canon_pp_read(sp->port, 6, buffer_info_block); if (check8(buffer_info_block, 6)) DBG(1, "init_scan: ** Warning: Checksum error reading buffer " "info block.\n"); expected_scanline_count = scanp->height; switch(scanp->mode) { case 0: /* greyscale; 10 bits per pixel */ expected_scanline_size = scanp->width * 1.25; break; case 1: /* true-colour; 30 bits per pixel */ expected_scanline_size = scanp->width * 3.75; break; default: DBG(1, "init_scan: Illegal mode %i requested in " "init_scan().\n", scanp->mode); DBG(1, "This is a bug. Please report it.\n"); return -1; } /* The scanner's idea of the length of each scanline in bytes */ true_scanline_size = (buffer_info_block[0]<<8) | buffer_info_block[1]; /* The scanner's idea of the number of scanlines in total */ true_scanline_count = (buffer_info_block[2]<<8) | buffer_info_block[3]; if ((expected_scanline_size != true_scanline_size) || (expected_scanline_count != true_scanline_count)) { DBG(10, "init_scan: Warning: Scanner is producing an image " "of unexpected size:\n"); DBG(10, "expected: %i bytes wide, %i scanlines tall.\n", expected_scanline_size, expected_scanline_count); DBG(10, "true: %i bytes wide, %i scanlines tall.\n", true_scanline_size, true_scanline_count); if (scanp->mode == 0) scanp->width = true_scanline_size / 1.25; else scanp->width = true_scanline_size / 3.75; scanp->height = true_scanline_count; } return 0; } /* Wake the scanner, detect it, and fill sp with stuff */ int sanei_canon_pp_initialise(scanner_parameters *sp, int mode) { unsigned char scanner_info[12]; const struct scanner_id *cur_id; const struct scanner_hardware_desc *hw; /* Hopefully take the scanner out of transparent mode */ if (sanei_canon_pp_wake_scanner(sp->port, mode)) { DBG(10, "initialise: could not wake scanner\n"); return 1; } /* This block of code does something unknown but necessary */ DBG(50, "initialise: >> scanner_init\n"); if (sanei_canon_pp_scanner_init(sp->port)) { /* If we're using an unsupported ieee1284 mode here, this is * where it will fail, so fall back to nibble. */ sanei_canon_pp_set_ieee1284_mode(M1284_NIBBLE); if (sanei_canon_pp_scanner_init(sp->port)) { DBG(10, "initialise: Could not init scanner.\n"); return 1; } } DBG(50, "initialise: << scanner_init\n"); /* Read Device ID */ memset(sp->id_string, 0, sizeof sp->id_string); if (send_command(sp->port, cmd_readid, 10, 10000, 100000)) return -1; sanei_canon_pp_read(sp->port, 38, (unsigned char *)(sp->id_string)); /* Read partially unknown data */ if (send_command(sp->port, cmd_readinfo, 10, 10000, 100000)) return -1; sanei_canon_pp_read(sp->port, 12, scanner_info); if (check8(scanner_info, 12)) { DBG(10, "initialise: Checksum error reading Info Block.\n"); return 2; } sp->scanheadwidth = (scanner_info[2] << 8) | scanner_info[3]; /* Set up various known values */ cur_id = scanner_id_table; while (cur_id->id) { if (!strncmp(sp->id_string+8, cur_id->id, strlen(cur_id->id))) break; cur_id++; } if (cur_id->id) { hw = cur_id->hw; } else if (sp->scanheadwidth == 5104) { /* Guess 600dpi scanner */ hw = &hw_alien600; } else if (sp->scanheadwidth == 2552) { /* Guess 300dpi scanner */ hw = &hw_alien300; } else { /* Guinea Pigs :) */ hw = &hw_alien; } strcpy(sp->name, hw->name); sp->natural_xresolution = hw->natural_xresolution; sp->natural_yresolution = hw->natural_yresolution; sp->scanbedlength = hw->scanbedlength; if (hw->scanheadwidth) sp->scanheadwidth = hw->scanheadwidth; sp->type = hw->type; return 0; } /* Shut scanner down */ int sanei_canon_pp_close_scanner(scanner_parameters *sp) { /* Put scanner in transparent mode */ sanei_canon_pp_sleep_scanner(sp->port); /* Free memory (with purchase of memory of equal or greater value) */ if (sp->blackweight != NULL) { free(sp->blackweight); sp->blackweight = NULL; } if (sp->redweight != NULL) { free(sp->redweight); sp->redweight = NULL; } if (sp->greenweight != NULL) { free(sp->greenweight); sp->greenweight = NULL; } if (sp->blueweight != NULL) { free(sp->blueweight); sp->blueweight = NULL; } return 0; } /* Read the calibration information from file */ int sanei_canon_pp_load_weights(const char *filename, scanner_parameters *sp) { int fd; int cal_data_size = sp->scanheadwidth * sizeof(unsigned long); int cal_file_size; char buffer[10]; int temp, ret; /* Open file */ if ((fd = open(filename, O_RDONLY)) == -1) return -1; /* Read header and check it's right */ ret = safe_read(fd, buffer, strlen(header) + 1); if ((ret < 0) || strcmp(buffer, header) != 0) { DBG(1,"Calibration file header is wrong, recalibrate please\n"); close(fd); return -2; } /* Read and check file version (the calibrate file format changes from time to time) */ ret = safe_read(fd, (char *)&temp, sizeof(int)); if ((ret < 0) || (temp != fileversion)) { DBG(1,"Calibration file is wrong version, recalibrate please\n"); close(fd); return -3; } /* Allocate memory for calibration values */ if (((sp->blueweight = malloc(cal_data_size)) == NULL) || ((sp->redweight = malloc(cal_data_size)) == NULL) || ((sp->greenweight = malloc(cal_data_size)) == NULL) || ((sp->blackweight = malloc(cal_data_size)) == NULL)) return -4; /* Read width of calibration data */ ret = safe_read(fd, (char *)&cal_file_size, sizeof(cal_file_size)); if ((ret < 0) || (cal_file_size != sp->scanheadwidth)) { DBG(1, "Calibration doesn't match scanner, recalibrate?\n"); close(fd); return -5; } /* Read calibration data */ if (safe_read(fd, (char *)(sp->blackweight), cal_data_size) < 0) { DBG(1, "Error reading black calibration data, recalibrate?\n"); close(fd); return -6; } if (safe_read(fd, (char *)sp->redweight, cal_data_size) < 0) { DBG(1, "Error reading red calibration data, recalibrate?\n"); close(fd); return -7; } if (safe_read(fd, (char *)sp->greenweight, cal_data_size) < 0) { DBG(1, "Error reading green calibration data, recalibrate?\n"); close(fd); return -8; } if (safe_read(fd, (char *)sp->blueweight, cal_data_size) < 0) { DBG(1, "Error reading blue calibration data, recalibrate?\n"); close(fd); return -9; } /* Read white-balance/gamma data */ if (safe_read(fd, (char *)&(sp->gamma), 32) < 0) { close(fd); return -10; } close(fd); return 0; } /* Mode is 0 for greyscale source data or 1 for RGB */ static void convert_to_rgb(image_segment *dest, unsigned char *src, int width, int scanlines, int mode) { int curline; const int colour_size = width * 1.25; const int scanline_size = (mode == 0 ? colour_size : colour_size * 3); for (curline = 0; curline < scanlines; curline++) { if (mode == 0) /* Grey */ { convdata(src + (curline * scanline_size), dest->image_data + (curline * width * 2), width, 1); } else if (mode == 1) /* Truecolour */ { /* Red */ convdata(src + (curline * scanline_size), dest->image_data + (curline * width *3*2) + 4, width, 2); /* Green */ convdata(src + (curline * scanline_size) + colour_size, dest->image_data + (curline * width *3*2) + 2, width, 2); /* Blue */ convdata(src + (curline * scanline_size) + (2 * colour_size), dest->image_data + (curline * width *3*2), width, 2); } } /* End of scanline loop */ } int sanei_canon_pp_read_segment(image_segment **dest, scanner_parameters *sp, scan_parameters *scanp, int scanline_number, int do_adjust, int scanlines_left) { unsigned char *input_buffer = NULL; image_segment *output_image = NULL; unsigned char packet_header[4]; unsigned char packet_req_command[10]; int read_data_size; int scanline_size; if (scanp->mode == 1) /* RGB */ scanline_size = scanp->width * 3.75; else /* Greyscale */ scanline_size = scanp->width * 1.25; read_data_size = scanline_size * scanline_number; /* Allocate output_image struct */ if ((output_image = malloc(sizeof(*output_image))) == NULL) { DBG(1, "read_segment: Error: Not enough memory for scanner " "input buffer\n"); goto error_out; } /* Allocate memory for input buffer */ if ((input_buffer = malloc(scanline_size * scanline_number)) == NULL) { DBG(1, "read_segment: Error: Not enough memory for scanner " "input buffer\n"); goto error_out; } output_image->width = scanp->width; output_image->height = scanline_number; /* Allocate memory for dest image segment */ output_image->image_data = malloc(output_image->width * output_image->height * (scanp->mode ? 3 : 1) * 2); if (output_image->image_data == NULL) { DBG(1, "read_segment: Error: Not enough memory for " "image data\n"); goto error_out; } /* Set up packet request command */ memcpy(packet_req_command, cmd_packet_req, 10); packet_req_command[7] = ((read_data_size + 4) & 0xFF00) >> 8; packet_req_command[8] = (read_data_size + 4) & 0xFF; /* Send packet req. and wait for the scanner's READY signal */ if (send_command(sp->port, packet_req_command, 10, 9000, 2000000)) { DBG(1, "read_segment: Error: didn't get response within 2s " "of sending request"); goto error_out; } /* Read packet header */ if (sanei_canon_pp_read(sp->port, 4, packet_header)) { DBG(1, "read_segment: Error reading packet header\n"); goto error_out; } if ((packet_header[2]<<8) + packet_header[3] != read_data_size) { DBG(1, "read_segment: Error: Expected data size: %i bytes.\n", read_data_size); DBG(1, "read_segment: Expecting %i bytes times %i " "scanlines.\n", scanline_size, scanline_number); DBG(1, "read_segment: Actual data size: %i bytes.\n", (packet_header[2] << 8) + packet_header[3]); goto error_out; } /* Read scanlines_this_packet scanlines into the input buf */ if (sanei_canon_pp_read(sp->port, read_data_size, input_buffer)) { DBG(1, "read_segment: Segment read incorrectly, and we don't " "know how to recover.\n"); goto error_out; } /* This is the only place we can abort safely - * between reading one segment and requesting the next one. */ if (sp->abort_now) goto error_out; if (scanlines_left >= (scanline_number * 2)) { DBG(100, "read_segment: Speculatively starting more scanning " "(%d left)\n", scanlines_left); sanei_canon_pp_write(sp->port, 10, packet_req_command); /* Don't read status, it's unlikely to be ready *just* yet */ } DBG(100, "read_segment: Convert to RGB\n"); /* Convert data */ convert_to_rgb(output_image, input_buffer, scanp->width, scanline_number, scanp->mode); /* Adjust pixel readings according to calibration data */ if (do_adjust) { DBG(100, "read_segment: Adjust output\n"); adjust_output(output_image, scanp, sp); } /* output */ *dest = output_image; /* finished with this now */ free(input_buffer); return 0; error_out: if (output_image && output_image->image_data) free(output_image->image_data); if (output_image) free(output_image); if (input_buffer) free(input_buffer); sp->abort_now = 0; return -1; } /* check8: Calculates the checksum-8 for s bytes pointed to by p. For messages from the scanner, this should normally end up returning 0, since the last byte of most packets is the value that makes the total up to 0 (or 256 if you're left-handed). Hence, usage: if (check8(buffer, size)) {DBG(10, "checksum error!\n");} Can also be used to generate valid checksums for sending to the scanner. */ static int check8(unsigned char *p, int s) { int total=0,i; for(i=0;i linear width is in pixels, not bytes. */ /* This function could use a rewrite */ static void convdata(unsigned char *srcbuffer, unsigned char *dstbuffer, int width, int mode) /* This is a tricky (read: crap) function (read: hack) which is why I probably spent more time commenting it than programming it. The thing to remember here is that the scanner uses interpolated scanlines, so it's RRRRRRRGGGGGGBBBBBB not RGBRGBRGBRGBRGB. So, the calling function just increments the destination pointer slightly to handle green, then a bit more for blue. If you don't understand, tough. */ { int count; int i, j, k; for (count = 0; count < width; count++) { /* The scanner stores data in a bizarre butchered 10-bit format. I'll try to explain it in 100 words or less: Scanlines are made up of groups of 4 pixels. Each group of 4 is stored inside 5 bytes. The first 4 bytes of the group contain the lowest 8 bits of one pixel each (in the right order). The 5th byte contains the most significant 2 bits of each pixel in the same order. */ i = srcbuffer[count + (count >> 2)]; /* Low byte for pixel */ j = srcbuffer[(((count / 4) + 1) * 5) - 1]; /* "5th" byte */ j = j >> ((count % 4) * 2); /* Get upper 2 bits of intensity */ j = j & 0x03; /* Can't hurt */ /* And the final 10-bit pixel value is: */ k = (j << 8) | i; /* now we return this as a 16 bit value */ k = k << 6; if (mode == 1) /* Scanner -> Grey */ { dstbuffer[count * 2] = HIGH_BYTE(k); dstbuffer[(count * 2) + 1] = LOW_BYTE(k); } else if (mode == 2) /* Scanner -> RGB */ { dstbuffer[count * 3 * 2] = HIGH_BYTE(k); dstbuffer[(count * 3 * 2) + 1] = LOW_BYTE(k); } } } static int adjust_output(image_segment *image, scan_parameters *scanp, scanner_parameters *scannerp) /* Needing a good cleanup */ { /* light and dark points for the CCD sensor in question * (stored in file as 0-1024, scaled to 0-65536) */ unsigned long hi, lo; /* The result of our calculations */ unsigned long result; unsigned long temp; /* The CCD sensor which read the current pixel - this is a tricky value to get right. */ int ccd, scaled_xoff; /* Loop variables */ unsigned int scanline, pixelnum, colour; unsigned long int pixel_address; unsigned int cols = scanp->mode ? 3 : 1; for (scanline = 0; scanline < image->height; scanline++) { for (pixelnum = 0; pixelnum < image->width; pixelnum++) { /* Figure out CCD sensor number */ /* MAGIC FORMULA ALERT! */ ccd = (pixelnum << (scannerp->natural_xresolution - scanp->xresolution)) + (1 << (scannerp->natural_xresolution - scanp->xresolution)) - 1; scaled_xoff = scanp->xoffset << (scannerp->natural_xresolution - scanp->xresolution); ccd += scaled_xoff; for (colour = 0; colour < cols; colour++) { /* Address of pixel under scrutiny */ pixel_address = (scanline * image->width * cols * 2) + (pixelnum * cols * 2) + (colour * 2); /* Dark value is easy * Range of lo is 0-18k */ lo = (scannerp->blackweight[ccd]) * 3; /* Light value depends on the colour, * and is an average in greyscale mode. */ if (scanp->mode == 1) /* RGB */ { switch (colour) { case 0: hi = scannerp->redweight[ccd] * 3; break; case 1: hi = scannerp->greenweight[ccd] * 3; break; default: hi = scannerp->blueweight[ccd] * 3; break; } } else /* Grey - scanned using green */ { hi = scannerp->greenweight[ccd] * 3; } /* Check for bad calibration data as it can cause a divide-by-0 error */ if (hi <= lo) { DBG(1, "adjust_output: Bad cal data!" " hi: %ld lo: %ld\n" "Recalibrate, that " "should fix it.\n", hi, lo); return -1; } /* Start with the pixel value in result */ result = MAKE_SHORT(*(image->image_data + pixel_address), *(image->image_data + pixel_address + 1)); result = result >> 6; /* Range now = 0-1023 */ /* if (scanline == 10) DBG(200, "adjust_output: Initial pixel" " value: %ld\n", result); */ result *= 54; /* Range now = 0-54k */ /* Clip to dark and light values */ if (result < lo) result = lo; if (result > hi) result = hi; /* result = (base-lo) * max_value / (hi-lo) */ temp = result - lo; temp *= 65536; temp /= (hi - lo); /* Clip output result has been clipped to lo, * and hi >= lo, so temp can't be < 0 */ if (temp > 65535) temp = 65535; /* if (scanline == 10) { DBG(200, "adjust_output: %d: base = " "%lu, result %lu (%lu " "- %lu)\n", pixelnum, result, temp, lo, hi); } */ result = temp; /* Store the value back where it came * from (always bigendian) */ *(image->image_data + pixel_address) = HIGH_BYTE(result); *(image->image_data + pixel_address+1) = LOW_BYTE(result); } } } /*DBG(100, "Finished adjusting output\n");*/ return 0; } /* Calibration run. Aborting allowed at "safe" points where the scanner won't * be left in a crap state. */ int sanei_canon_pp_calibrate(scanner_parameters *sp, char *cal_file) { int count, readnum, colournum, scanlinenum; int outfile; int scanline_size; int scanline_count = 6; /* Don't change this unless you also want to change do_adjust */ const int calibration_reads = 3; unsigned char command_buffer[10]; image_segment image; unsigned char *databuf; char colours[3][6] = {"Red", "Green", "Blue"}; /* Calibration data is monochromatic (greyscale format) */ scanline_size = sp->scanheadwidth * 1.25; /* 620P has to be difficult here... */ if (!(sp->type) ) scanline_count = 8; /* Probably shouldn't have to abort *just* yet, but may as well check */ if (sp->abort_now) return -1; DBG(40, "Calibrating %ix%i pixels calibration image " "(%i bytes each scan).\n", sp->scanheadwidth, scanline_count, scanline_size * scanline_count); /* Allocate memory for calibration data */ sp->blackweight = (unsigned long *) calloc(sizeof(unsigned long), sp->scanheadwidth); sp->redweight = (unsigned long *) calloc(sizeof(unsigned long), sp->scanheadwidth); sp->greenweight = (unsigned long *) calloc(sizeof(unsigned long), sp->scanheadwidth); sp->blueweight = (unsigned long *) calloc(sizeof(unsigned long), sp->scanheadwidth); /* The data buffer needs to hold a number of images (calibration_reads) * per colour, each sp->scanheadwidth x scanline_count */ databuf = malloc(scanline_size * scanline_count * calibration_reads*3); /* And allocate space for converted image data in this image_segment */ image.image_data = malloc(scanline_count * sp->scanheadwidth * 2 * calibration_reads); image.width = sp->scanheadwidth; image.height = scanline_count * calibration_reads; /* Sending the "dark calibration" command */ memcpy(command_buffer, cmd_calblack, 10); /* Which includes the size of data we expect the scanner to return */ command_buffer[7] = ((scanline_size * scanline_count) & 0xff00) >> 8; command_buffer[8] = (scanline_size * scanline_count) & 0xff; DBG(40, "Step 1/3: Calibrating black level...\n"); for (readnum = 0; readnum < calibration_reads; readnum++) { DBG(40, " * Black scan number %d/%d.\n", readnum + 1, calibration_reads); if (sp->abort_now) return -1; if (send_command(sp->port, command_buffer, 10, 100000, 5000000)) { DBG(1, "Error reading black level!\n"); free (image.image_data); free(databuf); return -1; } /* Black reference data */ sanei_canon_pp_read(sp->port, scanline_size * scanline_count, databuf + (readnum * scanline_size * scanline_count)); } /* Convert scanner format to a greyscale 16bpp image */ for (scanlinenum = 0; scanlinenum < scanline_count * calibration_reads; scanlinenum++) { convdata(databuf + (scanlinenum * scanline_size), image.image_data + (scanlinenum * sp->scanheadwidth*2), sp->scanheadwidth, 1); } /* Take column totals */ for (count = 0; count < sp->scanheadwidth; count++) { /* Value is normalised as if we took 6 scanlines, even if we * didn't (620P I'm looking at you!) */ sp->blackweight[count] = (column_sum(&image, count) * 6) / scanline_count >> 6; } /* 620P has to be difficult here... */ if (!(sp->type) ) { scanline_count = 6; image.height = scanline_count * calibration_reads; } DBG(40, "Step 2/3: Gamma tables...\n"); DBG(40, " * Requesting creation of new of gamma tables...\n"); if (sp->abort_now) return -1; if (send_command(sp->port, cmd_cleargamma, 10, 100000, 5000000)) { DBG(1,"Error sending gamma command!\n"); free (image.image_data); free(databuf); return -1; } DBG(20, " * Snoozing for 15 seconds while the scanner calibrates..."); usleep(15000000); DBG(40, "done.\n"); DBG(40, " * Requesting gamma table values..."); if (send_command(sp->port, cmd_readgamma, 10, 100000, 10000000)) { DBG(1,"Error sending gamma table request!\n"); free (image.image_data); free(databuf); return -1; } DBG(40, "done.\n"); DBG(40, " * Reading white-balance/gamma data... "); sanei_canon_pp_read(sp->port, 32, sp->gamma); DBG(40, "done.\n"); if (sp->abort_now) return -1; memcpy(command_buffer, cmd_calcolour, 10); /* Set up returned data size */ command_buffer[7] = ((scanline_size * scanline_count) & 0xff00) >> 8; command_buffer[8] = (scanline_size * scanline_count) & 0xff; DBG(40, "Step 3/3: Calibrating sensors...\n"); /* Now for the RGB high-points */ for (colournum = 1; colournum < 4; colournum++) { /* Set the colour we want to read */ command_buffer[3] = colournum; for (readnum = 0; readnum < 3; readnum++) { DBG(10, " * %s sensors, scan number %d/%d.\n", colours[colournum-1], readnum + 1, calibration_reads); if (sp->abort_now) return -1; if (send_command(sp->port, command_buffer, 10, 100000, 5000000)) { DBG(1,"Error sending scan request!"); free (image.image_data); free(databuf); return -1; } sanei_canon_pp_read(sp->port, scanline_size * scanline_count, databuf + (readnum * scanline_size * scanline_count)); } /* Convert colour data from scanner format to RGB data */ for (scanlinenum = 0; scanlinenum < scanline_count * calibration_reads; scanlinenum++) { convdata(databuf + (scanlinenum * scanline_size), image.image_data + (scanlinenum * sp->scanheadwidth * 2), sp->scanheadwidth, 1); } /* Sum each column of the image and store the results in sp */ for (count = 0; count < sp->scanheadwidth; count++) { if (colournum == 1) sp->redweight[count] = column_sum(&image, count) >> 6; else if (colournum == 2) sp->greenweight[count] = column_sum(&image, count) >> 6; else sp->blueweight[count] = column_sum(&image, count) >> 6; } } if (sp->abort_now) return -1; /* cal_file == NUL indicates we want an in-memory scan only */ if (cal_file != NULL) { DBG(40, "Writing calibration to %s\n", cal_file); outfile = open(cal_file, O_WRONLY | O_TRUNC | O_CREAT, 0600); if (outfile < 0) { DBG(10, "Error opening cal file for writing\n"); } /* Header */ if (safe_write(outfile, header, strlen(header) + 1) < 0) DBG(10, "Write error on calibration file %s", cal_file); if (safe_write(outfile, (const char *)&fileversion, sizeof(int)) < 0) DBG(10, "Write error on calibration file %s", cal_file); /* Data */ if (safe_write(outfile, (char *)&(sp->scanheadwidth), sizeof(sp->scanheadwidth)) < 0) DBG(10, "Write error on calibration file %s", cal_file); if (safe_write(outfile, (char *)(sp->blackweight), sp->scanheadwidth * sizeof(long)) < 0) DBG(10, "Write error on calibration file %s", cal_file); if (safe_write(outfile, (char *)(sp->redweight), sp->scanheadwidth * sizeof(long)) < 0) DBG(10, "Write error on calibration file %s", cal_file); if (safe_write(outfile, (char *)(sp->greenweight), sp->scanheadwidth * sizeof(long)) < 0) DBG(10, "Write error on calibration file %s", cal_file); if (safe_write(outfile, (char *)(sp->blueweight), sp->scanheadwidth * sizeof(long)) < 0) DBG(10, "Write error on calibration file %s", cal_file); if (safe_write(outfile, (char *)(sp->gamma), 32) < 0) DBG(10, "Write error on calibration file %s", cal_file); close(outfile); } free(databuf); free(image.image_data); return 0; } static unsigned long column_sum(image_segment *image, int x) /* This gives us a number from 0-n*65535 where n is the height of the image */ { unsigned int row, p; unsigned long total = 0; p = x; for (row = 0; row < image->height; row++) { total+= MAKE_SHORT(image->image_data[2*p], image->image_data[2*p+1]); p += image->width; } return total; } static int scanner_setup_params(unsigned char *buf, scanner_parameters *sp, scan_parameters *scanp) { int scaled_width, scaled_height; int scaled_xoff, scaled_yoff; /* Natural resolution (I think) */ if (sp->scanheadwidth == 2552) { buf[0] = 0x11; /* 300 | 0x1000 */ buf[1] = 0x2c; buf[2] = 0x11; buf[3] = 0x2c; } else { buf[0] = 0x12; /* 600 | 0x1000*/ buf[1] = 0x58; buf[2] = 0x12; buf[3] = 0x58; } scaled_width = scanp->width << (sp->natural_xresolution - scanp->xresolution); /* YO! This needs fixing if we ever use yresolution! */ scaled_height = scanp->height << (sp->natural_xresolution - scanp->xresolution); scaled_xoff = scanp->xoffset << (sp->natural_xresolution - scanp->xresolution); scaled_yoff = scanp->yoffset << (sp->natural_xresolution - scanp->xresolution); /* Input resolution */ buf[4] = (((75 << scanp->xresolution) & 0xff00) >> 8) | 0x10; buf[5] = (75 << scanp->xresolution) & 0xff; /* Interpolated resolution */ buf[6] = (((75 << scanp->xresolution) & 0xff00) >> 8) | 0x10;; buf[7] = (75 << scanp->xresolution) & 0xff; /* X offset */ buf[8] = (scaled_xoff & 0xff000000) >> 24; buf[9] = (scaled_xoff & 0xff0000) >> 16; buf[10] = (scaled_xoff & 0xff00) >> 8; buf[11] = scaled_xoff & 0xff; /* Y offset */ buf[12] = (scaled_yoff & 0xff000000) >> 24; buf[13] = (scaled_yoff & 0xff0000) >> 16; buf[14] = (scaled_yoff & 0xff00) >> 8; buf[15] = scaled_yoff & 0xff; /* Width of image to be scanned */ buf[16] = (scaled_width & 0xff000000) >> 24; buf[17] = (scaled_width & 0xff0000) >> 16; buf[18] = (scaled_width & 0xff00) >> 8; buf[19] = scaled_width & 0xff; /* Height of image to be scanned */ buf[20] = (scaled_height & 0xff000000) >> 24; buf[21] = (scaled_height & 0xff0000) >> 16; buf[22] = (scaled_height & 0xff00) >> 8; buf[23] = scaled_height & 0xff; /* These appear to be the only two colour mode possibilities. Pure black-and-white mode probably just uses greyscale and then gets its contrast adjusted by the driver. I forget. */ if (scanp->mode == 1) /* Truecolour */ buf[24] = 0x08; else /* Greyscale */ buf[24] = 0x04; return 0; } int sanei_canon_pp_abort_scan(scanner_parameters *sp) { /* The abort command (hopefully) */ sanei_canon_pp_write(sp->port, 10, cmd_abort); sanei_canon_pp_check_status(sp->port); return 0; } /* adjust_gamma: Upload a gamma profile to the scanner */ int sanei_canon_pp_adjust_gamma(scanner_parameters *sp) { sp->gamma[31] = check8(sp->gamma, 31); if (sanei_canon_pp_write(sp->port, 10, cmd_setgamma)) return -1; if (sanei_canon_pp_write(sp->port, 32, sp->gamma)) return -1; return 0; } int sanei_canon_pp_sleep_scanner(struct parport *port) { /* *SCANEND Command - puts scanner to sleep */ sanei_canon_pp_write(port, 10, cmd_scanend); sanei_canon_pp_check_status(port); ieee1284_terminate(port); return 0; /* FIXME: I murdered Simon's code here */ /* expect(port, "Enter Transparent Mode", 0x1f, 0x1f, 1000000); */ } int sanei_canon_pp_detect(struct parport *port, int mode) { /*int caps;*/ /* This code needs to detect whether or not a scanner is present on * the port, quickly and reliably. Fast version of * sanei_canon_pp_initialise() * * If this detect returns true, a more comprehensive check will * be conducted * Return values: * 0 = scanner present * anything else = scanner not present * PRE: port is open/unclaimed * POST: port is closed/unclaimed */ /* port is already open, just need to claim it */ if (ieee1284_claim(port) != E1284_OK) { DBG(0,"detect: Unable to claim port\n"); return 2; } if (sanei_canon_pp_wake_scanner(port, mode)) { DBG(10, "detect: could not wake scanner\n"); ieee1284_release(port); return 3; } /* Goodo, sleep (snaps fingers) */ sanei_canon_pp_sleep_scanner(port); ieee1284_release(port); /* ieee1284_close(port); */ return 0; } static int send_command(struct parport *port, unsigned char *buf, int bufsize, int delay, int timeout) /* Sends a command until the scanner says it is ready. * sleeps for delay microsecs between reads * returns -1 on error, -2 on timeout */ { int retries = 0; do { /* Send command */ if (sanei_canon_pp_write(port, bufsize, buf)) return -1; /* sleep a bit */ usleep(delay); } while (sanei_canon_pp_check_status(port) && retries++ < (timeout/delay)); if (retries >= (timeout/delay)) return -2; return 0; } backends-1.3.0/backend/canon_pp-dev.h000066400000000000000000000125151456256263500174200ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2001-2002 Matthew C. Duggan and Simon Krix 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, see . 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 is part of the canon_pp backend, supporting Canon FBX30P and NX40P scanners and also part of the stand-alone driver. Simon Krix */ #ifndef CANON_PP_DEV_H #define CANON_PP_DEV_H /* Signal names */ /* C port */ #define READY 0x1f #define NSELECTIN 0x08 #define NINIT 0x04 #define HOSTBUSY 0x02 #define HOSTCLK 0x01 /* S port */ #define BUSY 0x10 #define NACK 0x08 #define PTRCLK 0x08 #define PERROR 0x04 #define ACKDATAREQ 0x04 #define XFLAG 0x02 #define SELECT 0x02 #define NERROR 0x01 #define NFAULT 0x01 #define NDATAAVAIL 0x01 /* Scanner things */ #define CALSIZE 18 /* Lines per calibration */ /* Init modes */ #define INITMODE_20P 1 #define INITMODE_30P 2 #define INITMODE_AUTO 3 /* Misc things */ #define T_SCAN 1 #define T_CALIBRATE 2 /* Macros */ #define MAKE_SHORT(a,b) (((short)a)*0x100+(short)b) #define LOW_BYTE(a) (a%0x100) #define HIGH_BYTE(a) (a/0x100) typedef struct scanner_parameter_struct { /* This is the port the scanner is on, in libieee1284-readable form */ struct parport *port; /* Width of the scanning head in pixels */ int scanheadwidth; int scanbedlength; /* Resolution of the scan head where dpi = 75 << natural_resolution */ int natural_xresolution; int natural_yresolution; int max_xresolution; int max_yresolution; /* ID String. Should only be 38(?) bytes long, so we can reduce the size later. */ char id_string[80]; /* Short, readable scanner name, such as "FB330P" */ char name[40]; /* Pixel weight values from calibration, one per pixel on the scan head. These must be allocated before any scanning can be done. */ unsigned long *blackweight; unsigned long *redweight; unsigned long *greenweight; unsigned long *blueweight; /* Not understood white-balance/gain values */ unsigned char gamma[32]; /* Type of scanner ( 0 = *20P, 1 = [*30P|*40P] ) */ unsigned char type; /* Are we aborting this scanner now */ unsigned char abort_now; } scanner_parameters; typedef struct scan_parameter_struct { /* Size of image */ unsigned int width, height; /* Position of image on the scanner bed */ unsigned int xoffset, yoffset; /* Resolution at which to scan (remember it's 75 << resolution) */ int xresolution, yresolution; /* Mode of image. 0 = greyscale, 1 = truecolour */ int mode; } scan_parameters; typedef struct image_segment_struct { /* Size of image segment */ unsigned int width, height; /* Which part of the image this is */ unsigned int start_scanline; /* Pointer to image data */ unsigned char *image_data; } image_segment; /* Scan-related functions ========================= */ /* Brings the scanner in and out of transparent mode and detects model information */ int sanei_canon_pp_initialise(scanner_parameters *sp, int mode); int sanei_canon_pp_close_scanner(scanner_parameters *sp); /* Image scanning functions */ int sanei_canon_pp_init_scan(scanner_parameters *sp, scan_parameters *scanp); int sanei_canon_pp_read_segment(image_segment **dest, scanner_parameters *sp, scan_parameters *scanp, int scanline_count, int do_adjust, int scanlines_left); int sanei_canon_pp_abort_scan(scanner_parameters *sp); /* Loads the gain offset values. Needs a new name. */ int sanei_canon_pp_load_weights(const char *filename, scanner_parameters *sp); int sanei_canon_pp_calibrate(scanner_parameters *sp, char *cal_file); int sanei_canon_pp_adjust_gamma(scanner_parameters *sp); /* Detect if a scanner is present on a given port */ int sanei_canon_pp_detect(struct parport *port, int mode); /* Put a scanner to sleep */ int sanei_canon_pp_sleep_scanner(struct parport *port); #endif backends-1.3.0/backend/canon_pp-io.c000066400000000000000000000354251456256263500172510ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2001-2002 Matthew C. Duggan and Simon Krix 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, see . 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 is part of the canon_pp backend, supporting Canon CanoScan Parallel scanners and also distributed as part of the stand-alone driver. Low Level Function library for Canon CanoScan Parallel Scanners by Simon Krix */ #ifndef NOSANE #include "../include/sane/config.h" #endif #include #include #include #include "canon_pp-io.h" #include "canon_pp-dev.h" #ifdef NOSANE /* No SANE, Things that only apply to stand-alone */ #include #include static void DBG(int level, const char *format, ...) { va_list args; va_start(args, format); if (level < 50) vfprintf(stderr, format, args); va_end(args); } #else /* Fix problem with DBG macro definition having a - in the name */ #define DEBUG_DECLARE_ONLY #include "canon_pp.h" #include "../include/sane/sanei_debug.h" #include "../include/sane/sanei_config.h" #endif /* 0x00 = Nibble Mode (M1284_NIBBLE) 0x10 = ECP Mode (M1284_ECP) The scanner driver seems not to support ECP RLE mode (which is a huge bummer because compression would be ace) nor EPP mode. */ static int ieee_mode = M1284_NIBBLE; /* For super-verbose debugging */ /* #define DUMP_PACKETS 1 */ /* Some sort of initialisation command */ static unsigned char cmd_init[10] = { 0xec, 0x20, 0, 0, 0, 0, 0, 0, 0, 0 }; /************* Local Prototypes ******************/ /* Used by wake_scanner */ static int scanner_reset(struct parport *port); static void scanner_chessboard_control(struct parport *port); static void scanner_chessboard_data(struct parport *port, int mode); /* Used by read_data */ static int ieee_transfer(struct parport *port, int length, unsigned char *data); /* Low level functions */ static int readstatus(struct parport *port); static int expect(struct parport *port, const char *step, int s, int mask, unsigned int delay); /* Port-level functions */ static void outdata(struct parport *port, int d); static void outcont(struct parport *port, int d, int mask); static void outboth(struct parport *port, int d, int c); /************************************/ /* * IEEE 1284 defines many values for m, * but these scanners only support 2: nibble and ECP modes. * And no data compression either (argh!) * 0 = Nibble-mode reverse channel transfer * 16 = ECP-mode */ void sanei_canon_pp_set_ieee1284_mode(int m) { ieee_mode = m; } int sanei_canon_pp_wake_scanner(struct parport *port, int mode) { /* The scanner tristates the printer's control lines (essentially disabling the passthrough port) and exits from Transparent Mode ready for communication. */ int i = 0; int tmp; int max_cycles = 3; tmp = readstatus(port); /* Reset only works on 30/40 models */ if (mode != INITMODE_20P) { if ((tmp != READY)) { DBG(40, "Scanner not ready (0x%x). Attempting to " "reset...\n", tmp); scanner_reset(port); /* give it more of a chance to reset in this case */ max_cycles = 5; } } else { DBG(0, "WARNING: Don't know how to reset an FBx20P, you may " "have to power cycle\n"); } do { i++; /* Send the wakeup sequence */ scanner_chessboard_control(port); scanner_chessboard_data(port, mode); if (expect(port, NULL, 0x03, 0x1f, 800000) && (mode == INITMODE_AUTO)) { /* 630 Style init failed, try 620 style */ scanner_chessboard_control(port); scanner_chessboard_data(port, INITMODE_20P); } if (expect(port, "Scanner wakeup reply 1", 0x03, 0x1f, 50000)) { outboth(port, 0x04, 0x0d); usleep(100000); outcont(port, 0x07, 0x0f); usleep(100000); } } while ((i < max_cycles) && expect(port, "Scanner wakeup reply 2", 0x03, 0x1f, 100000)); /* Block just after chessboarding Reply 1 (S3 and S4 on, S5 and S7 off) */ outcont(port, 0, HOSTBUSY); /* C1 off */ /* Reply 2 - If it ain't happening by now, it ain't gonna happen. */ if (expect(port, "Reply 2", 0xc, 0x1f, 800000)) return -1; outcont(port, HOSTBUSY, HOSTBUSY); /* C1 on */ if (expect(port, "Reply 3", 0x0b, 0x1f, 800000)) return -1; outboth(port, 0, NSELECTIN | NINIT | HOSTCLK); /* Clear D, C3+, C1- */ /* If we had to try the wakeup cycle more than once, we should wait * here for 10 seconds to let the scanner pull itself together - * it can actually take longer, but I can't wait that long! */ if (i > 1) { DBG(10, "Had to reset scanner, waiting for the " "head to get back.\n"); usleep(10000000); } return 0; } int sanei_canon_pp_write(struct parport *port, int length, unsigned char *data) { #ifdef DUMP_PACKETS ssize_t count; DBG(10,"Sending: "); for (count = 0; count < length; count++) { DBG(10,"%02x ", data[count]); if (count % 20 == 19) DBG(10,"\n "); } if (count % 20 != 19) DBG(10,"\n"); #endif DBG(100, "NEW Send Command (length %i):\n", length); switch (ieee_mode) { case M1284_BECP: case M1284_ECPRLE: case M1284_ECPSWE: case M1284_ECP: ieee1284_negotiate(port, ieee_mode); if (ieee1284_ecp_write_data(port, 0, (char *)data, length) != length) return -1; break; case M1284_NIBBLE: if (ieee1284_compat_write(port, 0, (char *)data, length) != length) return -1; break; default: DBG(0, "Invalid mode in write!\n"); } DBG(100, "<< write"); return 0; } int sanei_canon_pp_read(struct parport *port, int length, unsigned char *data) { int count, offset; DBG(200, "NEW read_data (%i bytes):\n", length); ieee1284_negotiate(port, ieee_mode); /* This is special; Nibble mode needs a little extra help from us. */ if (ieee_mode == M1284_NIBBLE) { /* Interrupt phase */ outcont(port, NSELECTIN, HOSTBUSY | NSELECTIN); if (expect(port, "Read Data 1", 0, NDATAAVAIL, 6000000)) { DBG(10,"Error 1\n"); ieee1284_terminate(port); return 1; } outcont(port, HOSTBUSY, HOSTBUSY); if (expect(port, "Read Data 2", NACK, NACK, 1000000)) { DBG(1,"Error 2\n"); ieee1284_terminate(port); return 1; } if (expect(port, "Read Data 3 (Ready?)", 0, PERROR, 1000000)) { DBG(1,"Error 3\n"); ieee1284_terminate(port); return 1; } /* Host-Busy Data Available phase */ if ((readstatus(port) & NDATAAVAIL) == NDATAAVAIL) { DBG(1,"No data to read.\n"); ieee1284_terminate(port); return 1; } } offset = 0; DBG(100, "-> ieee_transfer(%d) *\n", length); count = ieee_transfer(port, length, data); DBG(100, "<- (%d)\n", count); /* Early-out if it was not implemented */ if (count == E1284_NOTIMPL) return 2; length -= count; offset+= count; while (length > 0) { /* If 0 bytes were transferred, it's a legal "No data" condition (I think). Otherwise, it may have run out of buffer.. keep reading*/ if (count < 0) { DBG(10, "Couldn't read enough data (need %d more " "of %d)\n", length+count,length+offset); ieee1284_terminate(port); return 1; } DBG(100, "-> ieee_transfer(%d)\n", length); count = ieee_transfer(port, length, data+offset); DBG(100, "<- (%d)\n", count); length-=count; offset+= count; } #ifdef DUMP_PACKETS if (length <= 60) { DBG(10,"Read: "); for (count = 0; count < length; count++) { DBG(10,"%02x ", data[count]); if (count % 20 == 19) DBG(10,"\n "); } if (count % 20 != 19) DBG(10,"\n"); } else { DBG(10,"Read: %i bytes\n", length); } #endif if (ieee_mode == M1284_NIBBLE) ieee1284_terminate(port); return 0; } static int ieee_transfer(struct parport *port, int length, unsigned char *data) { int result = 0; DBG(100, "IEEE transfer (%i bytes)\n", length); switch (ieee_mode) { case M1284_BECP: case M1284_ECP: case M1284_ECPRLE: case M1284_ECPSWE: result = ieee1284_ecp_read_data(port, 0, (char *)data, length); break; case M1284_NIBBLE: result = ieee1284_nibble_read(port, 0, (char *)data, length); break; default: DBG(1, "Internal error: Wrong mode for transfer.\n" "Please email stauff1@users.sourceforge.net\n" "or kinsei@users.sourceforge.net\n"); } return result; } int sanei_canon_pp_check_status(struct parport *port) { int status; unsigned char data[2]; DBG(200, "* Check Status:\n"); if (sanei_canon_pp_read(port, 2, data)) return -1; status = data[0] | (data[1] << 8); switch(status) { case 0x0606: DBG(200, "Ready - 0x0606\n"); return 0; break; case 0x1414: DBG(200, "Busy - 0x1414\n"); return 1; break; case 0x0805: DBG(200, "Resetting - 0x0805\n"); return 3; break; case 0x1515: DBG(1, "!! Invalid Command - 0x1515\n"); return 2; break; case 0x0000: DBG(200, "Nothing - 0x0000"); return 4; break; default: DBG(1, "!! Unknown status - %04x\n", status); return 100; } } /* Send a raw byte to the printer port */ static void outdata(struct parport *port, int d) { ieee1284_write_data(port, d & 0xff); } /* Send the low nibble of d to the control port. The mask affects which bits are changed. */ static void outcont(struct parport *port, int d, int mask) { static int control_port_status = 0; control_port_status = (control_port_status & ~mask) | (d & mask); ieee1284_write_control(port, (control_port_status & 0x0f)); } /* Send a byte to both ports */ static void outboth(struct parport *port, int d, int c) { ieee1284_write_data(port, d & 0xff); outcont(port, c, 0x0f); } /* readstatus(): Returns the LOGIC value of the S register (ie: all input lines) shifted right to to make it easier to read. Note: S5 is inverted by ieee1284_read_status so we don't need to */ static int readstatus(struct parport *port) { return (ieee1284_read_status(port) & 0xf8) >> 3; } static void scanner_chessboard_control(struct parport *port) { /* Wiggle C1 and C3 (twice) */ outboth(port, 0x0, 13); usleep(10); outcont(port, 7, 0xf); usleep(10); outcont(port, 13, 0xf); usleep(10); outcont(port, 7, 0xf); usleep(10); } static void scanner_chessboard_data(struct parport *port, int mode) { int count; /* initial weirdness here for 620P - seems to go quite fast, * just ignore it! */ for (count = 0; count < 2; count++) { /* Wiggle data lines (4 times) while strobing C1 */ /* 33 here for *30P, 55 for *20P */ if (mode == INITMODE_20P) outdata(port, 0x55); else outdata(port, 0x33); outcont(port, HOSTBUSY, HOSTBUSY); usleep(10); outcont(port, 0, HOSTBUSY); usleep(10); outcont(port, HOSTBUSY, HOSTBUSY); usleep(10); if (mode == INITMODE_20P) outdata(port, 0xaa); else outdata(port, 0xcc); outcont(port, HOSTBUSY, HOSTBUSY); usleep(10); outcont(port, 0, HOSTBUSY); usleep(10); outcont(port, HOSTBUSY, HOSTBUSY); usleep(10); } } /* Reset the scanner. At least, it works 50% of the time. */ static int scanner_reset(struct parport *port) { /* Resetting only works for the *30Ps, sorry */ if (readstatus(port) == 0x0b) { /* Init Block 1 - composed of a 0-byte IEEE read */ ieee1284_negotiate(port, 0x0); ieee1284_terminate(port); ieee1284_negotiate(port, 0x0); ieee1284_terminate(port); scanner_chessboard_data(port, 1); scanner_chessboard_data(port, 1); scanner_chessboard_data(port, 1); scanner_chessboard_data(port, 1); scanner_chessboard_data(port, 0); scanner_chessboard_data(port, 0); scanner_chessboard_data(port, 0); scanner_chessboard_data(port, 0); } /* Reset Block 2 =============== */ outboth(port, 0x04, 0x0d); /* Specifically, we want this: 00111 on S */ if (expect(port, "Reset 2 response 1", 0x7, 0x1f, 500000)) return 1; outcont(port, 0, HOSTCLK); usleep(5); outcont(port, 0x0f, 0xf); /* All lines must be 1. */ /* All lines 1 */ if (expect(port, "Reset 2 response 2 (READY)", 0x1f, 0x1f, 500000)) return 1; outcont(port, 0, HOSTBUSY); usleep(100000); /* a short pause */ outcont(port, HOSTBUSY, HOSTBUSY | NSELECTIN); return 0; } /* A timed version of expect, which will wait for delay before erroring This is the one and only one we should be using */ static int expect(struct parport *port, const char *msg, int s, int mask, unsigned int delay) { struct timeval tv; tv.tv_sec = delay / 1000000; tv.tv_usec = delay % 1000000; if (ieee1284_wait_status(port, mask << 3, s << 3, &tv)) { if (msg) DBG(10, "Timeout: %s (0x%02x in 0x%02x) - Status " "= 0x%02x\n", msg, s, mask, readstatus(port)); return 1; } return 0; } int sanei_canon_pp_scanner_init(struct parport *port) { int tries = 0; int tmp = 0; /* Put the scanner in nibble mode */ ieee1284_negotiate(port, 0x0); /* No data to read yet - return to idle mode */ ieee1284_terminate(port); /* In Windows, this is always ECP (or an attempt at it) */ if (sanei_canon_pp_write(port, 10, cmd_init)) return -1; /* Note that we don't really mind what the status was as long as it * wasn't a read error (returns -1) */ /* In fact, the 620P gives an error on that last command, but they * keep going anyway */ if (sanei_canon_pp_check_status(port) < 0) return -1; /* Try until it's ready */ sanei_canon_pp_write(port, 10, cmd_init); while ((tries < 3) && (tmp = sanei_canon_pp_check_status(port))) { if (tmp < 0) return -1; DBG(10, "scanner_init: Giving the scanner a snooze...\n"); usleep(500000); tries++; sanei_canon_pp_write(port, 10, cmd_init); } if (tries == 3) return 1; return 0; } backends-1.3.0/backend/canon_pp-io.h000066400000000000000000000046211456256263500172500ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2001-2002 Matthew C. Duggan and Simon Krix 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, see . 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 is part of the canon_pp backend, supporting Canon FBX30P and NX40P scanners and also distributed as part of the stand-alone driver. Low-level scanner interface */ #ifndef CANON_PP_IO_H #define CANON_PP_IO_H /* Actual Interface */ void sanei_canon_pp_set_ieee1284_mode(int m); int sanei_canon_pp_wake_scanner(struct parport *port, int mode); int sanei_canon_pp_write(struct parport *port, int length, unsigned char *data); int sanei_canon_pp_read(struct parport *port, int length, unsigned char *data); int sanei_canon_pp_check_status(struct parport *port); int sanei_canon_pp_scanner_init(struct parport *port); #endif backends-1.3.0/backend/canon_pp.c000066400000000000000000001476311456256263500166470ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2001-2002 Matthew C. Duggan and Simon Krix 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, see . 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 is part of the canon_pp backend, supporting Canon FBX30P and NX40P scanners */ #ifdef _AIX #include /* MUST come first for AIX! */ #endif #define BACKEND_NAME canon_pp #define THREE_BITS 0xE0 #define TWO_BITS 0xC0 #define MM_PER_IN 25.4 #ifndef NOSANE #include "../include/sane/config.h" #endif #include #include #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/saneopts.h" #include "canon_pp-dev.h" #include "canon_pp-io.h" #include "canon_pp.h" /* #include "../include/sane/sanei_pio.h" */ #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_backend.h" /* #include "../include/sane/sanei_debug.h" */ /* Prototypes */ static SANE_Status init_device(struct parport *pp); /* create a calibration file and give it initial values */ static int init_cal(char *file); static SANE_Status fix_weights_file(CANONP_Scanner *cs); static SANE_Status detect_mode(CANONP_Scanner *cs); /* Global Variables (ack!) */ /* The first device in a linked list of devices */ static CANONP_Scanner *first_dev = NULL; /* The default scanner to open */ static char *def_scanner = NULL; /* The number of devices */ static int num_devices = 0; /* ieee1284 parallel ports */ struct parport_list pl; /* leftover from the last read */ static SANE_Byte *read_leftover = NULL; /* leftover from the last read */ static SANE_Bool force_nibble = SANE_FALSE; /* Constants */ /* Colour Modes */ static const SANE_String_Const cmodes[] = { SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_COLOR, NULL }; /* bit depths */ static const SANE_String_Const depths[] = { "8", "12", NULL }; /* resolutions */ static const SANE_Int res300[] = {3, 75, 150, 300}; static const SANE_Int res600[] = {4, 75, 150, 300, 600}; /************************************************************************* * * sane_init() * * Initialises data for the list of scanners, stored in canon-p.conf. * * Scanners are not sent any commands until sane_open() is called. * *************************************************************************/ SANE_Status sane_init (SANE_Int *vc, SANE_Auth_Callback cb) { SANE_Status status = SANE_STATUS_GOOD; int i, tmp; int tmp_im = INITMODE_AUTO; FILE *fp; char line[81]; /* plus 1 for a null */ char *tmp_wf, *tmp_port; CANONP_Scanner *s_tmp; DBG_INIT(); #if defined PACKAGE && defined VERSION DBG(2, ">> sane_init (version %s null, authorize %s null): " PACKAGE " " VERSION "\n", (vc) ? "!=" : "==", (cb) ? "!=" : "=="); #endif if(vc) *vc = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); DBG(2,"sane_init: >> ieee1284_find_ports\n"); /* Find lp ports */ tmp = ieee1284_find_ports(&pl, 0); DBG(2,"sane_init: %d << ieee1284_find_ports\n", tmp); if (tmp != E1284_OK) { DBG(1,"sane_init: Error trying to get port list\n"); return SANE_STATUS_IO_ERROR; } if (pl.portc < 1) { DBG(1,"sane_init: Error, no parallel ports found.\n"); return SANE_STATUS_IO_ERROR; } DBG(10,"sane_init: %i parallel port(s) found.\n", pl.portc); /* Setup data structures for each port */ for(i=0; iname); status = init_device(pl.portv[i]); /* Now's a good time to quit if we got an error */ if (status != SANE_STATUS_GOOD) return status; } /* This should never be true here */ if (num_devices == 0) status = SANE_STATUS_IO_ERROR; /* just to be extra sure, the line will always have an end: */ line[sizeof(line)-1] = '\0'; /* * Read information from config file: pixel weight location and default * port. */ if((fp = sanei_config_open(CANONP_CONFIG_FILE))) { while(sanei_config_read(line, sizeof (line) - 1, fp)) { DBG(100, "sane_init: >%s<\n", line); if(line[0] == '#') /* ignore line comments */ continue; if(!strlen(line)) continue; /* ignore empty lines */ if(strncmp(line,"calibrate ", 10) == 0) { /* warning: pointer trickyness ahead * Do not free tmp_port! */ DBG(40, "sane_init: calibrate line, %s\n", line); tmp_wf = strdup(line+10); tmp_port = strstr(tmp_wf, " "); if ((tmp_port == tmp_wf) || (tmp_port == NULL)) { /* They have used an old style config * file which does not specify scanner * Assume first port */ DBG(1, "sane_init: old config line:" "\"%s\". Please add " "a port argument.\n", line); /* first_dev should never be null here * because we found at least one * parallel port above */ first_dev->weights_file = tmp_wf; DBG(100, "sane_init: Successfully " "parsed (old) cal, " "weight file is " "'%s'.\n", tmp_wf); continue; } /* Now find which scanner wants * this calibration file */ s_tmp = first_dev; DBG(100, "sane_init: Finding scanner on port " "'%s'\n", tmp_port+1); while (s_tmp != NULL) { if (!strcmp(s_tmp->params.port->name, tmp_port+1)) { DBG(100, "sane_init: Found!\n"); /* Now terminate the weight * file string */ *tmp_port = '\0'; s_tmp->weights_file = tmp_wf; DBG(100, "sane_init: Parsed " "cal, for port" " '%s', weight" " file is '%s'" ".\n", s_tmp->params. port->name, tmp_wf); break; } s_tmp = s_tmp->next; } if (s_tmp == NULL) { /* we made it all the way through the * list and didn't find the port */ free(tmp_wf); DBG(10, "sane_init: calibrate line is " "for unknown port!\n"); } continue; } if(strncmp(line,"ieee1284 ", 9) == 0) { DBG(100, "sane_init: Successfully parsed " "default scanner.\n"); /* this will be our default scanner */ def_scanner = strdup(line+9); continue; } if(strncmp(line,"force_nibble", 12) == 0) { DBG(100, "sane_init: force_nibble " "requested.\n"); force_nibble = SANE_TRUE; continue; } if(strncmp(line,"init_mode ", 10) == 0) { /* parse what sort of initialisation mode to * use */ if (strncmp(line+10, "FB620P", 6) == 0) tmp_im = INITMODE_20P; else if (strncmp(line+10, "FB630P", 6) == 0) tmp_im = INITMODE_30P; else if (strncmp(line+10, "AUTO", 4) == 0) tmp_im = INITMODE_AUTO; /* now work out which port it blongs to */ tmp_port = strstr(line+10, " "); if (tmp_port == NULL) { /* first_dev should never be null here * because we found at least one * parallel port above */ first_dev->init_mode = tmp_im; DBG(100, "sane_init: Parsed init-1.\n"); continue; } s_tmp = first_dev; while (s_tmp != NULL) { if (!strcmp(s_tmp->params.port->name, tmp_port+1)) { s_tmp->init_mode = tmp_im; DBG(100, "sane_init: Parsed " "init.\n"); break; } s_tmp = s_tmp->next; } if (s_tmp == NULL) { /* we made it all the way through the * list and didn't find the port */ DBG(10, "sane_init: init_mode line is " "for unknown port!\n"); } continue; } DBG(1, "sane_init: Unknown configuration command!"); } fclose (fp); } /* There should now be a LL of ports starting at first_dev */ for (s_tmp = first_dev; s_tmp != NULL; s_tmp = s_tmp->next) { /* Assume there's no scanner present until proven otherwise */ s_tmp->scanner_present = SANE_FALSE; /* Try to detect if there's a scanner there, and if so, * what sort of scanner it is */ status = detect_mode(s_tmp); if (status != SANE_STATUS_GOOD) { DBG(10,"sane_init: Error detecting port mode on %s!\n", s_tmp->params.port->name); s_tmp->scanner_present = SANE_FALSE; continue; } /* detect_mode succeeded, so the port is open. This beholdens * us to call ieee1284_close in any of the remaining error * cases in this loop. */ #if 0 tmp = sanei_canon_pp_detect(s_tmp->params.port, s_tmp->init_mode); if (tmp && (s_tmp->ieee1284_mode != M1284_NIBBLE)) { /* A failure, try again in nibble mode... */ DBG(1, "sane_init: Failed on ECP mode, falling " "back to nibble mode\n"); s_tmp->ieee1284_mode = M1284_NIBBLE; sanei_canon_pp_set_ieee1284_mode(s_tmp->ieee1284_mode); tmp = sanei_canon_pp_detect(s_tmp->params.port, s_tmp->init_mode); } /* still no go? */ if (tmp) { DBG(1,"sane_init: couldn't find a scanner on port " "%s\n", s_tmp->params.port->name); ieee1284_close(s_tmp->params.port); continue; } #endif /* all signs point to yes, try it out */ if (ieee1284_claim(s_tmp->params.port) != E1284_OK) { DBG(10, "sane_init: Couldn't claim port %s.\n", s_tmp->params.port->name); ieee1284_close(s_tmp->params.port); continue; } DBG(2, "sane_init: >> initialise\n"); tmp = sanei_canon_pp_initialise(&(s_tmp->params), s_tmp->init_mode); DBG(2, "sane_init: << %d initialise\n", tmp); if (tmp) { DBG(10, "sane_init: Couldn't contact scanner on port " "%s. Probably no scanner there?\n", s_tmp->params.port->name); ieee1284_release(s_tmp->params.port); ieee1284_close(s_tmp->params.port); s_tmp->scanner_present = SANE_FALSE; continue; } /* put it back to sleep until we're ready to * open for business again - this will only work * if we actually have a scanner there! */ DBG(100, "sane_init: And back to sleep again\n"); sanei_canon_pp_sleep_scanner(s_tmp->params.port); /* leave the port open but not claimed - this is regardless * of the return value of initialise */ ieee1284_release(s_tmp->params.port); /* Finally, we're sure there's a scanner there! Now we * just have to load the weights file...*/ if (fix_weights_file(s_tmp) != SANE_STATUS_GOOD) { DBG(1, "sane_init: Eeek! fix_weights_file failed for " "scanner on port %s!\n", s_tmp->params.port->name); /* non-fatal.. scans will look ugly as sin unless * they calibrate */ } /* Cocked, locked and ready to rock */ s_tmp->hw.model = s_tmp->params.name; s_tmp->scanner_present = SANE_TRUE; } DBG(2, "<< sane_init\n"); return status; } /************************************************************************* * * sane_get_devices() * * Gives a list of devices available. In our case, that's the linked * list produced by sane_init. * *************************************************************************/ SANE_Status sane_get_devices (const SANE_Device ***dl, SANE_Bool local) { static const SANE_Device **devlist; CANONP_Scanner *dev; int i; DBG(2, ">> sane_get_devices (%p, %d)\n", (const void*)dl, local); if (dl == NULL) { DBG(1, "sane_get_devices: ERROR: devlist pointer is NULL!"); return SANE_STATUS_INVAL; } if (devlist != NULL) { /* this has been called already */ *dl = devlist; return SANE_STATUS_GOOD; } devlist = malloc((num_devices + 1) * sizeof(*devlist)); if (devlist == NULL) return SANE_STATUS_NO_MEM; i = 0; for (dev = first_dev; dev != NULL; dev = dev->next) { if (dev->scanner_present == SANE_TRUE) { devlist[i] = &(dev->hw); i++; } } devlist[i] = NULL; *dl = devlist; DBG(2, "<< sane_get_devices\n"); return SANE_STATUS_GOOD; } /************************************************************************* * * sane_open() * * Open the scanner described by name. Ask libieee1284 to claim the port * and call Simon's init code. Also configure data structures. * *************************************************************************/ SANE_Status sane_open (SANE_String_Const name, SANE_Handle *h) { CANONP_Scanner *cs; SANE_Range *tmp_range; int tmp; DBG(2, ">> sane_open (h=%p, name=\"%s\")\n", (void *)h, name); if ((h == NULL) || (name == NULL)) { DBG(2,"sane_open: Null pointer received!\n"); return SANE_STATUS_INVAL; } if (!strlen(name)) { DBG(10,"sane_open: Empty name given, assuming first/" "default scanner\n"); if (def_scanner == NULL) name = first_dev->params.port->name; else name = def_scanner; /* we don't _have_ to fit this name, so _don't_ fail if it's * not there */ cs = first_dev; while((cs != NULL) && strcmp(cs->params.port->name, name)) cs = cs->next; /* if we didn't find the port they want, or there's no scanner * there, we just want to find _any_ scanner */ if ((cs == NULL) || (cs->scanner_present != SANE_TRUE)) { cs = first_dev; while((cs != NULL) && (cs->scanner_present == SANE_FALSE)) cs = cs->next; } } else { /* they're dead keen for this name, so _do_ fail if it's * not there */ cs = first_dev; while((cs != NULL) && strcmp(cs->params.port->name, name)) cs = cs->next; } if (cs == NULL) { DBG(2,"sane_open: No scanner found or requested port " "doesn't exist (%s)\n", name); return SANE_STATUS_IO_ERROR; } if (cs->scanner_present == SANE_FALSE) { DBG(1,"sane_open: Request to open port with no scanner " "(%s)\n", name); return SANE_STATUS_IO_ERROR; } if (cs->opened == SANE_TRUE) { DBG(2,"sane_open; Oi!, That scanner's already open.\n"); return SANE_STATUS_DEVICE_BUSY; } /* If the scanner has already been opened once, we don't have to do * this setup again */ if (cs->setup == SANE_TRUE) { cs->opened = SANE_TRUE; *h = (SANE_Handle)cs; return SANE_STATUS_GOOD; } tmp = ieee1284_claim(cs->params.port); if (tmp != E1284_OK) { DBG(1, "sane_open: Could not claim port!\n"); return SANE_STATUS_IO_ERROR; } /* I put the scanner to sleep before, better wake it back up */ DBG(2, "sane_open: >> initialise\n"); tmp = sanei_canon_pp_initialise(&(cs->params), cs->init_mode); DBG(2, "sane_open: << %d initialise\n", tmp); if (tmp != 0) { DBG(1, "sane_open: initialise returned %d, something is " "wrong with the scanner!\n", tmp); DBG(1, "sane_open: Can't contact scanner. Try power " "cycling scanner, and unplug any " "printers\n"); ieee1284_release(cs->params.port); return SANE_STATUS_IO_ERROR; } if (cs->weights_file != NULL) DBG(2, "sane_open: >> load_weights(%s, %p)\n", cs->weights_file, (const void *)(&(cs->params))); else DBG(2, "sane_open: >> load_weights(NULL, %p)\n", (const void *)(&(cs->params))); tmp = sanei_canon_pp_load_weights(cs->weights_file, &(cs->params)); DBG(2, "sane_open: << %d load_weights\n", tmp); if (tmp != 0) { DBG(1, "sane_open: WARNING: Error on load_weights: " "returned %d. This could be due to a corrupt " "calibration file. Try recalibrating and if " "problems persist, please report the problem " "to the canon_pp maintainer\n", tmp); cs->cal_valid = SANE_FALSE; } else { cs->cal_valid = SANE_TRUE; DBG(10, "sane_open: loadweights successful, uploading gamma" " profile...\n"); tmp = sanei_canon_pp_adjust_gamma(&(cs->params)); if (tmp != 0) DBG(1, "sane_open: WARNING: adjust_gamma returned " "%d!\n", tmp); DBG(10, "sane_open: after adjust_gamma Status = %i\n", sanei_canon_pp_check_status(cs->params.port)); } /* Configure ranges etc */ /* Resolution - determined by magic number */ if (cs->params.scanheadwidth == 2552) cs->opt[OPT_RESOLUTION].constraint.word_list = res300; else cs->opt[OPT_RESOLUTION].constraint.word_list = res600; /* TL-X */ if(!(tmp_range = malloc(sizeof(*tmp_range)))) return SANE_STATUS_NO_MEM; (*tmp_range).min = 0; (*tmp_range).max = 215; cs->opt[OPT_TL_X].constraint.range = tmp_range; /* TL-Y */ if(!(tmp_range = malloc(sizeof(*tmp_range)))) return SANE_STATUS_NO_MEM; (*tmp_range).min = 0; (*tmp_range).max = 296; cs->opt[OPT_TL_Y].constraint.range = tmp_range; /* BR-X */ if(!(tmp_range = malloc(sizeof(*tmp_range)))) return SANE_STATUS_NO_MEM; (*tmp_range).min = 3; (*tmp_range).max = 216; cs->opt[OPT_BR_X].constraint.range = tmp_range; /* BR-Y */ if(!(tmp_range = malloc(sizeof(*tmp_range)))) return SANE_STATUS_NO_MEM; (*tmp_range).min = 1; (*tmp_range).max = 297; cs->opt[OPT_BR_Y].constraint.range = tmp_range; cs->opened = SANE_TRUE; cs->setup = SANE_TRUE; *h = (SANE_Handle)cs; DBG(2, "<< sane_open\n"); return SANE_STATUS_GOOD; } /************************************************************************* * * sane_get_option_descriptor() * * Return the structure for option number opt. * *************************************************************************/ const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle h, SANE_Int opt) { CANONP_Scanner *cs = ((CANONP_Scanner *)h); /*DBG(2, ">> sane_get_option_descriptor (h=%p, opt=%d)\n", h, opt);*/ if (h == NULL) { DBG(10,"sane_get_option_descriptor: WARNING: h==NULL!\n"); return NULL; } if ((unsigned)opt >= NUM_OPTIONS) { DBG(10,"sane_get_option_descriptor: Note: opt >= " "NUM_OPTIONS!\n"); return NULL; } if (cs->opened == SANE_FALSE) { DBG(1,"sane_get_option_descriptor: That scanner (%p) ain't " "open yet\n", h); return NULL; } /*DBG(2, "<< sane_get_option_descriptor\n");*/ return (cs->opt + opt); } /************************************************************************* * * sane_control_option() * * Set a value for one of the options provided. * *************************************************************************/ SANE_Status sane_control_option (SANE_Handle h, SANE_Int opt, SANE_Action act, void *val, SANE_Word *info) { CANONP_Scanner *cs = ((CANONP_Scanner *)h); int i = 0, tmp, maxresi; DBG(2, ">> sane_control_option (h=%p, opt=%d, act=%d)\n", h,opt,act); /* Do some sanity checks on the parameters * note that val can be null for buttons */ if ((h == NULL) || ((val == NULL) && (opt != OPT_CAL))) /* || (info == NULL)) - Don't check this any more.. * frontends seem to like passing a null */ { DBG(1,"sane_control_option: Frontend passed me a null! " "(h=%p,val=%p,info=%p)\n",(void*)h, val,(void*)info); return SANE_STATUS_INVAL; } if (((unsigned)opt) >= NUM_OPTIONS) { DBG(1,"sane_control_option: I don't do option %d.\n", opt); return SANE_STATUS_INVAL; } if (cs->opened == SANE_FALSE) { DBG(1,"sane_control_option: That scanner (%p) ain't " "open yet\n", h); return SANE_STATUS_INVAL; } if (cs->scanning == SANE_TRUE) { DBG(1,"sane_control_option: That scanner (%p) is scanning!\n", h); return SANE_STATUS_DEVICE_BUSY; } switch(act) { case SANE_ACTION_GET_VALUE: switch (opt) { case OPT_COLOUR_MODE: strcpy((char *)val, cmodes[cs->vals[opt]]); break; case OPT_DEPTH: strcpy((char *)val, depths[cs->vals[opt]]); break; case OPT_RESOLUTION: *((int *)val) = res600[cs->vals[opt]]; break; default: *((int *)val) = cs->vals[opt]; break; } break; case SANE_ACTION_SET_VALUE: /* val has been checked for NULL if opt != OPT_CAL */ if (opt != OPT_CAL) i = *((int *)val); if (info != NULL) *info = 0; switch (opt) { case OPT_NUM_OPTIONS: /* you can't set that! */ return SANE_STATUS_INVAL; case OPT_RESOLUTION: i = cs->vals[opt]; cs->vals[opt] = 1; maxresi = cs->opt[OPT_RESOLUTION]. constraint.word_list[0]; while ((cs->vals[opt] <= maxresi) && (res600[cs->vals[opt]] < *((int *)val))) { cs->vals[opt] += 1; } if (res600[cs->vals[opt]] != *((int *)val)) { if (info != NULL) *info |= SANE_INFO_INEXACT; } break; case OPT_COLOUR_MODE: cs->vals[opt] = 0; while ((cmodes[cs->vals[opt]] != NULL) && strcmp(cmodes[cs->vals[opt]], (char *)val)) { cs->vals[opt] += 1; } if (info != NULL) *info |= SANE_INFO_RELOAD_PARAMS; break; case OPT_DEPTH: cs->vals[opt] = 0; while ((depths[cs->vals[opt]] != NULL) && strcmp(depths[cs->vals[opt]], (char *)val)) { cs->vals[opt] += 1; } if (info != NULL) *info |= SANE_INFO_RELOAD_PARAMS; break; case OPT_TL_X: case OPT_BR_X: case OPT_TL_Y: case OPT_BR_Y: if ((iopt[opt].constraint.range->min) || (i>cs->opt[opt].constraint.range->max)) return SANE_STATUS_INVAL; cs->vals[opt] = i; break; case OPT_CAL: /* Call the calibration code */ if ((cs->weights_file==NULL) || cs->cal_readonly ) DBG(2, ">> calibrate(x, " "NULL)\n"); else DBG(2, ">> calibrate(x," "%s)\n", cs->weights_file); if (cs->cal_readonly) tmp = sanei_canon_pp_calibrate( &(cs->params), NULL); else tmp = sanei_canon_pp_calibrate( &(cs->params), cs->weights_file); DBG(2, "<< %d calibrate\n", tmp); if (tmp != 0) { DBG(1, "sane_control_option: " "WARNING: " "calibrate " "returned %d!", tmp); cs->cal_valid = SANE_FALSE; return SANE_STATUS_IO_ERROR; } else { cs->cal_valid = SANE_TRUE; } break; /*case OPT_PREVIEW: if (i) cs->vals[opt] = 1; else cs->vals[opt] = 0; break;*/ default: /* Should never happen */ return SANE_STATUS_INVAL; } break; case SANE_ACTION_SET_AUTO: DBG(2, "sane_control_option: attempt at " "automatic control! (unsupported)\n"); /* Auto? are they mad? I'm not that smart! */ /* fall through. */ default: return SANE_STATUS_INVAL; } DBG(2, "<< sane_control_option\n"); return SANE_STATUS_GOOD; } /************************************************************************* * * sane_get_parameters() * * Get information about the next packet. If a scan hasn't started, results * only have to be best guesses. * *************************************************************************/ SANE_Status sane_get_parameters (SANE_Handle h, SANE_Parameters *params) { int res, max_width, max_height, max_res; CANONP_Scanner *cs = ((CANONP_Scanner *)h); DBG(2, ">> sane_get_parameters (h=%p, params=%p)\n", (void*)h, (void*)params); if (h == NULL) return SANE_STATUS_INVAL; if (cs->opened == SANE_FALSE) { DBG(1,"sane_get_parameters: That scanner (%p) ain't " "open yet\n", h); return SANE_STATUS_INVAL; } /* We use 600 res list here because the 300 res list is just a shorter * version, so this will always work. */ res = res600[cs->vals[OPT_RESOLUTION]]; /* * These don't change whether we're scanning or not * NOTE: Assumes options don't change after scanning commences, which * is part of the standard */ /* Copy the options stored in the vals into the scaninfo */ params->pixels_per_line = ((cs->vals[OPT_BR_X] - cs->vals[OPT_TL_X]) * res) / MM_PER_IN; params->lines = ((cs->vals[OPT_BR_Y] - cs->vals[OPT_TL_Y]) * res) / MM_PER_IN; /* FIXME: Magic numbers ahead! */ max_res = cs->params.scanheadwidth == 2552 ? 300 : 600; /* x values have to be divisible by 4 (round down) */ params->pixels_per_line -= (params->pixels_per_line%4); /* Can't scan less than 64 */ if (params->pixels_per_line < 64) params->pixels_per_line = 64; max_width = cs->params.scanheadwidth / (max_res / res); max_height = (cs->params.scanheadwidth == 2552 ? 3508 : 7016) / (max_res / res); if(params->pixels_per_line > max_width) params->pixels_per_line = max_width; if(params->lines > max_height) params->lines = max_height; params->depth = cs->vals[OPT_DEPTH] ? 16 : 8; switch (cs->vals[OPT_COLOUR_MODE]) { case 0: params->format = SANE_FRAME_GRAY; break; case 1: params->format = SANE_FRAME_RGB; break; default: /* shouldn't happen */ break; } if (!(params->pixels_per_line)) { params->last_frame = SANE_TRUE; params->lines = 0; } /* Always the "last frame" */ params->last_frame = SANE_TRUE; params->bytes_per_line = params->pixels_per_line * (params->depth/8) * (cs->vals[OPT_COLOUR_MODE] ? 3 : 1); DBG(10, "get_params: bytes_per_line=%d, pixels_per_line=%d, lines=%d\n" "max_res=%d, res=%d, max_height=%d, br_y=%d, tl_y=%d, " "mm_per_in=%f\n", params->bytes_per_line, params->pixels_per_line, params->lines, max_res, res, max_height, cs->vals[OPT_BR_Y], cs->vals[OPT_TL_Y], MM_PER_IN); DBG(2, "<< sane_get_parameters\n"); return SANE_STATUS_GOOD; } /************************************************************************* * * sane_start() * * Starts scanning an image. * *************************************************************************/ SANE_Status sane_start (SANE_Handle h) { unsigned int i, res, max_width, max_height, max_res, tmp; CANONP_Scanner *cs = ((CANONP_Scanner *)h); DBG(2, ">> sane_start (h=%p)\n", h); if (h == NULL) return SANE_STATUS_INVAL; if (cs->scanning) return SANE_STATUS_DEVICE_BUSY; if (cs->opened == SANE_FALSE) { DBG(1,"sane_start: That scanner (%p) ain't " "open yet\n", h); return SANE_STATUS_INVAL; } /* We use 600 res list here because the 300 res list is just a shorter * version, so this will always work. */ res = res600[cs->vals[OPT_RESOLUTION]]; /* Copy the options stored in the vals into the scaninfo */ cs->scan.width = ((cs->vals[OPT_BR_X] - cs->vals[OPT_TL_X]) * res) / MM_PER_IN; cs->scan.height = ((cs->vals[OPT_BR_Y] - cs->vals[OPT_TL_Y]) * res) / MM_PER_IN; cs->scan.xoffset = (cs->vals[OPT_TL_X] * res) / MM_PER_IN; cs->scan.yoffset = (cs->vals[OPT_TL_Y] * res) / MM_PER_IN; /* * These values have to pass the requirements of not exceeding * dimensions (simple clipping) and both width values have to be some * integer multiple of 4 */ /* FIXME: Magic numbers ahead! */ max_res = cs->params.scanheadwidth == 2552 ? 300 : 600; /* x values have to be divisible by 4 (round down) */ cs->scan.width -= (cs->scan.width%4); cs->scan.xoffset -= (cs->scan.xoffset%4); /* Can't scan less than 64 */ if (cs->scan.width < 64) cs->scan.width = 64; max_width = cs->params.scanheadwidth / (max_res / res); max_height = (cs->params.scanheadwidth == 2552 ? 3508 : 7016) / (max_res / res); if (cs->scan.width > max_width) cs->scan.width = max_width; if (cs->scan.width + cs->scan.xoffset > max_width) cs->scan.xoffset = max_width - cs->scan.width; if (cs->scan.height > max_height) cs->scan.height = max_height; /* We pass a value to init_scan which is the power of 2 that 75 * is multiplied by for the resolution. ie: * 75 -> 0 * 150 -> 1 * 300 -> 2 * 600 -> 4 * * This rather strange parameter is a result of the way the scanner * takes its resolution argument */ i = 0; while (res > 75) { i++; res = res >> 1; } /* FIXME? xres == yres for now. */ cs->scan.xresolution = i; cs->scan.yresolution = i; if (((cs->vals[OPT_BR_Y] - cs->vals[OPT_TL_Y]) <= 0) || ((cs->vals[OPT_BR_X] - cs->vals[OPT_TL_X]) <= 0)) { DBG(1,"sane_start: height = %d, Width = %d. " "Can't scan void range!", cs->scan.height, cs->scan.width); return SANE_STATUS_INVAL; } cs->scan.mode = cs->vals[OPT_COLOUR_MODE]; DBG(10, ">> init_scan()\n"); tmp = sanei_canon_pp_init_scan(&(cs->params), &(cs->scan)); DBG(10, "<< %d init_scan\n", tmp); if (tmp != 0) { DBG(1,"sane_start: WARNING: init_scan returned %d!", tmp); return SANE_STATUS_IO_ERROR; } cs->scanning = SANE_TRUE; cs->cancelled = SANE_FALSE; cs->sent_eof = SANE_FALSE; cs->lines_scanned = 0; cs->bytes_sent = 0; DBG(2, "<< sane_start\n"); return SANE_STATUS_GOOD; } /************************************************************************* * * sane_read() * * Reads some information from the buffer. * *************************************************************************/ SANE_Status sane_read (SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *lenp) { CANONP_Scanner *cs = ((CANONP_Scanner *)h); image_segment *is; unsigned int lines, bytes, bpl; unsigned int i; short *shortptr; SANE_Byte *charptr; int tmp; static SANE_Byte *lbuf; static unsigned int bytesleft; DBG(2, ">> sane_read (h=%p, buf=%p, maxlen=%d)\n", h, (const void *)buf, maxlen); /* default to returning 0 - for errors */ *lenp = 0; if ((h == NULL) || (buf == NULL) || (lenp == NULL)) { DBG(1, "sane_read: This frontend's passing me dodgy gear! " "(h=%p, buf=%p, lenp=%p)\n", (void*)h, (void*)buf, (void*)lenp); return SANE_STATUS_INVAL; } /* Now we have to see if we have some leftover from last time */ if (read_leftover != NULL) { /* feed some more data in until we've run out - don't care * whether or not we _think_ the scanner is scanning now, * because we may still have data left over to send */ DBG(200, "sane_read: didn't send it all last time\n"); /* Now feed it some data from lbuf */ if (bytesleft <= (unsigned int)maxlen) { /* enough buffer to send the lot */ memcpy(buf, read_leftover, bytesleft); free(lbuf); *lenp = bytesleft; lbuf = NULL; read_leftover = NULL; bytesleft = 0; cs->bytes_sent += bytesleft; return SANE_STATUS_GOOD; } else { /* only enough to send maxlen */ memcpy(buf, read_leftover, maxlen); read_leftover += maxlen; bytesleft -= maxlen; *lenp = maxlen; cs->bytes_sent += maxlen; DBG(100, "sane_read: sent %d bytes, still have %d to " "go\n", maxlen, bytesleft); return SANE_STATUS_GOOD; } } /* Has the last scan ended (other than by cancelling)? */ if (((unsigned)cs->scan.height <= (unsigned)cs->lines_scanned) || (cs->sent_eof) || !(cs->scanning)) { cs->sent_eof = SANE_TRUE; cs->scanning = SANE_FALSE; cs->cancelled = SANE_FALSE; cs->lines_scanned = 0; cs->bytes_sent = 0; read_leftover = NULL; return SANE_STATUS_EOF; } /* At this point we have to read more data from the scanner - or the * scan has been cancelled, which means we have to call read_segment * to leave the scanner consistent */ /* Decide how many lines we can fit into this buffer */ if (cs->vals[OPT_DEPTH] == 0) bpl = cs->scan.width * (cs->vals[OPT_COLOUR_MODE] ? 3 : 1); else bpl = cs->scan.width * (cs->vals[OPT_COLOUR_MODE] ? 6 : 2); /* New way: scan a whole scanner buffer full, and return as much as * the frontend wants. It's faster and more reliable since the * scanners crack the shits if we ask for too many small packets */ lines = (BUF_MAX * 4 / 5) / bpl; if (lines > (cs->scan.height - cs->lines_scanned)) lines = cs->scan.height - cs->lines_scanned; if (!lines) { /* can't fit a whole line into the buffer * (should never happen!) */ lines = 1; } bytes = lines * bpl; /* Allocate a local buffer to hold the data while we play */ if ((lbuf = malloc(bytes)) == NULL) { DBG(10, "sane_read: Not enough memory to hold a " "local buffer. You're doomed\n"); return SANE_STATUS_NO_MEM; } /* This call required a lot of debugging information.. */ DBG(10, "sane_read: Here's what we're sending read_segment:\n"); DBG(10, "scanner setup: shw=%d xres=%d yres=%d %d %d id=%s\n", cs->params.scanheadwidth, cs->params.natural_xresolution, cs->params.natural_yresolution, cs->params.max_xresolution, cs->params.max_yresolution, (cs->params.id_string)+8); DBG(10, "scan_params->: width=%d, height=%d, xoffset=%d, " "yoffset=%d\n\txresolution=%d, yresolution=%d, " "mode=%d, (lines=%d)\n", cs->scan.width, cs->scan.height, cs->scan.xoffset, cs->scan.yoffset, cs->scan.xresolution, cs->scan.yresolution, cs->scan.mode, lines); DBG(2, ">> read_segment(x, x, x, %d, %d, %d)\n", lines, cs->cal_valid, cs->scan.height - cs->lines_scanned); tmp = sanei_canon_pp_read_segment(&is, &(cs->params), &(cs->scan), lines, cs->cal_valid, cs->scan.height - cs->lines_scanned); DBG(2, "<< %d read_segment\n", tmp); if (tmp != 0) { if (cs->cancelled) { DBG(10, "sane_read: cancelling.\n"); cs->sent_eof = SANE_TRUE; cs->scanning = SANE_FALSE; read_leftover = NULL; sanei_canon_pp_abort_scan(&(cs->params)); return SANE_STATUS_CANCELLED; } DBG(1, "sane_read: WARNING: read_segment returned %d!\n", tmp); return SANE_STATUS_IO_ERROR; } DBG(10, "sane_read: bpl=%d, lines=%d, bytes=%d\n", bpl, lines, bytes); cs->lines_scanned += lines; /* translate data out of buffer */ if (cs->vals[OPT_DEPTH] == 0) { /* 8bpp */ for(i = 0; i < bytes; i++) { charptr = lbuf + i; if (cs->vals[OPT_COLOUR_MODE]) { if (i % 3 == 0) charptr += 2; if (i % 3 == 2) charptr -= 2; } *charptr = *((char *)(is->image_data) + (i*2)); } } else { /* 16bpp */ for(i = 0; i < (bytes/2); i++) { shortptr = ((short *)lbuf + i); if (cs->vals[OPT_COLOUR_MODE]) { if (i % 3 == 0) shortptr += 2; if (i % 3 == 2) shortptr -= 2; } *shortptr = MAKE_SHORT( *((char *)(is->image_data) + (i*2)), *((char *)(is->image_data) + (i*2)+1) ); } } /* Free data structures allocated in read_segment */ free(is->image_data); free(is); /* Now feed it some data from lbuf */ if (bytes <= (unsigned int)maxlen) { /* enough buffer to send the lot */ memcpy(buf, lbuf, bytes); *lenp = bytes; free(lbuf); lbuf = NULL; read_leftover = NULL; bytesleft = 0; cs->bytes_sent += bytes; } else { /* only enough to send maxlen */ memcpy(buf, lbuf, maxlen); *lenp = maxlen; read_leftover = lbuf + maxlen; bytesleft = bytes - maxlen; cs->bytes_sent += maxlen; DBG(100, "sane_read: sent %d bytes, still have %d to go\n", maxlen, bytesleft); } if ((unsigned)cs->lines_scanned >= cs->scan.height) { /* The scan is over! Don't need to call anything in the * hardware, it will sort itself out */ DBG(10, "sane_read: Scan is finished.\n"); cs->scanning = SANE_FALSE; cs->lines_scanned = 0; cs->bytes_sent = 0; } DBG(2, "<< sane_read\n"); return SANE_STATUS_GOOD; } /************************************************************************* * * sane_cancel() * * Cancels a scan in progress * *************************************************************************/ void sane_cancel (SANE_Handle h) { /* Note: assume handle is valid apart from NULLs */ CANONP_Scanner *cs = ((CANONP_Scanner *)h); DBG(2, ">> sane_cancel (h=%p)\n", h); if (h == NULL) return; read_leftover = NULL; if (!(cs->scanning)) { DBG(2, "<< sane_cancel (not scanning)\n"); return; } cs->cancelled = SANE_TRUE; cs->params.abort_now = 1; DBG(2, "<< sane_cancel\n"); } /************************************************************************* * * sane_close() * * Closes a scanner handle. Scanner is assumed to be free after this. * *************************************************************************/ void sane_close (SANE_Handle h) { /* Note: assume handle is valid apart from NULLs */ CANONP_Scanner *cs = ((CANONP_Scanner *)h); DBG(2, ">> sane_close (h=%p)\n", h); if (h == NULL) return; if (cs->opened == SANE_FALSE) { DBG(1,"sane_close: That scanner (%p) ain't " "open yet\n", h); return; } /* Put scanner back in transparent mode */ sanei_canon_pp_close_scanner(&(cs->params)); cs->opened = SANE_FALSE; /* if it was scanning, it's not any more */ cs->scanning = SANE_FALSE; cs->sent_eof = SANE_TRUE; ieee1284_release(cs->params.port); DBG(2, "<< sane_close\n"); } /************************************************************************* * * sane_exit() * * Shut it down! * *************************************************************************/ void sane_exit (void) { CANONP_Scanner *dev, *next; DBG(2, ">> sane_exit\n"); for (dev = first_dev; dev != NULL; dev = next) { next = dev->next; /* These were only created if the scanner has been init'd */ /* Should normally nullify pointers after freeing, but in * this case we're about to free the whole structure so * there's not a lot of point. */ /* Constraints (mostly) allocated when the scanner is opened */ if(dev->opt[OPT_TL_X].constraint.range) free((void *)(dev->opt[OPT_TL_X].constraint.range)); if(dev->opt[OPT_TL_Y].constraint.range) free((void *)(dev->opt[OPT_TL_Y].constraint.range)); if(dev->opt[OPT_BR_X].constraint.range) free((void *)(dev->opt[OPT_BR_X].constraint.range)); if(dev->opt[OPT_BR_Y].constraint.range) free((void *)(dev->opt[OPT_BR_Y].constraint.range)); /* Weights file now on a per-scanner basis */ if (dev->weights_file != NULL) free(dev->weights_file); if (dev->scanner_present) { if (dev->opened == SANE_TRUE) { /* naughty boys, should have closed first */ ieee1284_release(dev->params.port); } ieee1284_close(dev->params.port); } free (dev); } first_dev = NULL; def_scanner = NULL; read_leftover = NULL; num_devices = 0; /* FIXEDME: this created a segfault in DLL code. */ /* Bug was fixed in libieee1284 0.1.5 */ ieee1284_free_ports(&pl); DBG(2, "<< sane_exit\n"); } /************************************************************************* * * init_device() * * (Not part of the SANE API) * * Initialises a CANONP_Scanner data structure for a new device. * NOTE: The device is not ready to scan until initialise() has been * called in scan library! * *************************************************************************/ static SANE_Status init_device(struct parport *pp) { int i; static const char *hw_vendor = "CANON"; static const char *hw_type = "flatbed scanner"; static const char *opt_names[] = { SANE_NAME_NUM_OPTIONS, SANE_NAME_SCAN_RESOLUTION, SANE_NAME_SCAN_MODE, SANE_NAME_BIT_DEPTH, SANE_NAME_SCAN_TL_X, SANE_NAME_SCAN_TL_Y, SANE_NAME_SCAN_BR_X, SANE_NAME_SCAN_BR_Y, SANE_NAME_QUALITY_CAL #if 0 SANE_NAME_GAMMA_R, SANE_NAME_GAMMA_G, SANE_NAME_GAMMA_B #endif }; static const char *opt_titles[] = { SANE_TITLE_NUM_OPTIONS, SANE_TITLE_SCAN_RESOLUTION, SANE_TITLE_SCAN_MODE, SANE_TITLE_BIT_DEPTH, SANE_TITLE_SCAN_TL_X, SANE_TITLE_SCAN_TL_Y, SANE_TITLE_SCAN_BR_X, SANE_TITLE_SCAN_BR_Y, SANE_TITLE_QUALITY_CAL #if 0 SANE_TITLE_GAMMA_R, SANE_TITLE_GAMMA_G, SANE_TITLE_GAMMA_B #endif }; static const char *opt_descs[] = { SANE_DESC_NUM_OPTIONS, SANE_DESC_SCAN_RESOLUTION, SANE_DESC_SCAN_MODE, SANE_DESC_BIT_DEPTH, SANE_DESC_SCAN_TL_X, SANE_DESC_SCAN_TL_Y, SANE_DESC_SCAN_BR_X, SANE_DESC_SCAN_BR_Y, SANE_DESC_QUALITY_CAL #if 0 SANE_DESC_GAMMA_R, SANE_DESC_GAMMA_G, SANE_DESC_GAMMA_B #endif }; CANONP_Scanner *cs = NULL; DBG(2, ">> init_device\n"); cs = malloc(sizeof(*cs)); if (cs == NULL) { return SANE_STATUS_NO_MEM; } memset(cs, 0, sizeof(*cs)); #if 0 if ((cs->params.port = malloc(sizeof(*(cs->params.port)))) == NULL) return SANE_STATUS_NO_MEM; memcpy(cs->params.port, pp, sizeof(*pp)); #endif cs->params.port = pp; /* ensure these are null to start off with, otherwise they might be * erroneously free'd. Note that we set everything to 0 above * but that's not *always* the same thing */ cs->params.blackweight = NULL; cs->params.redweight = NULL; cs->params.greenweight = NULL; cs->params.blueweight = NULL; /* Set some sensible defaults */ cs->hw.name = cs->params.port->name; cs->hw.vendor = hw_vendor; cs->hw.type = hw_type; cs->opened = SANE_FALSE; cs->scanning = SANE_FALSE; cs->cancelled = SANE_FALSE; cs->sent_eof = SANE_TRUE; cs->lines_scanned = 0; cs->bytes_sent = 0; cs->init_mode = INITMODE_AUTO; DBG(10, "init_device: [configuring options]\n"); /* take a punt at each option, then we change it later */ for (i = 0; i < NUM_OPTIONS; i++) { cs->opt[i].name = opt_names[i]; cs->opt[i].title = opt_titles[i]; cs->opt[i].desc = opt_descs[i]; cs->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; cs->opt[i].type = SANE_TYPE_INT; cs->opt[i].size = sizeof(SANE_Int); } DBG(100, "init_device: configuring opt: num_options\n"); /* The number of options option */ cs->opt[OPT_NUM_OPTIONS].unit = SANE_UNIT_NONE; cs->opt[OPT_NUM_OPTIONS].cap = SANE_CAP_SOFT_DETECT; cs->vals[OPT_NUM_OPTIONS] = NUM_OPTIONS; DBG(100, "init_device: configuring opt: resolution\n"); /* The resolution of scanning (X res == Y res for now)*/ cs->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; cs->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; /* should never point at first element (wordlist size) */ cs->vals[OPT_RESOLUTION] = 1; DBG(100, "init_device: configuring opt: colour mode\n"); /* The colour mode (0=grey 1=rgb) */ cs->opt[OPT_COLOUR_MODE].type = SANE_TYPE_STRING; cs->opt[OPT_COLOUR_MODE].size = 20; cs->opt[OPT_COLOUR_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; /* Set this one here because it doesn't change by scanner (yet) */ cs->opt[OPT_COLOUR_MODE].constraint.string_list = cmodes; DBG(100, "init_device: configuring opt: bit depth\n"); /* The bit depth */ cs->opt[OPT_DEPTH].type = SANE_TYPE_STRING; cs->opt[OPT_DEPTH].size = 20; cs->opt[OPT_DEPTH].cap |= SANE_CAP_EMULATED; cs->opt[OPT_DEPTH].constraint_type = SANE_CONSTRAINT_STRING_LIST; cs->opt[OPT_DEPTH].constraint.string_list = depths; DBG(100, "init_device: configuring opt: tl-x\n"); /* The top-left-x */ cs->opt[OPT_TL_X].unit = SANE_UNIT_MM; cs->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; DBG(100, "init_device: configuring opt: tl-y\n"); /* The top-left-y */ cs->opt[OPT_TL_Y].unit = SANE_UNIT_MM; cs->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; DBG(100, "init_device: configuring opt: br-x\n"); /* The bottom-right-x */ cs->opt[OPT_BR_X].unit = SANE_UNIT_MM; cs->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; /* default scan width */ cs->vals[OPT_BR_X] = 100; DBG(100, "init_device: configuring opt: br-y\n"); /* The bottom-right-y */ cs->opt[OPT_BR_Y].unit = SANE_UNIT_MM; cs->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; cs->vals[OPT_BR_Y] = 100; DBG(100, "init_device: configuring opt: calibrate\n"); /* The calibration button */ cs->opt[OPT_CAL].type = SANE_TYPE_BUTTON; cs->opt[OPT_CAL].constraint_type = SANE_CONSTRAINT_NONE; if (cs->cal_readonly) cs->opt[OPT_CAL].cap |= SANE_CAP_INACTIVE; #if 0 /* the gamma values (once we do them) */ cs->opt[OPT_GAMMA_R].caps |= SANE_CAP_ADVANCED; cs->opt[OPT_GAMMA_G].caps |= SANE_CAP_ADVANCED; cs->opt[OPT_GAMMA_B].caps |= SANE_CAP_ADVANCED; #endif /* * NOTE: Ranges and lists are actually set when scanner is opened, * because that's when we find out what sort of scanner it is */ DBG(100, "init_device: done opts\n"); /* add it to the head of the tree */ cs->next = first_dev; first_dev = cs; num_devices++; DBG(2, "<< init_device\n"); return SANE_STATUS_GOOD; } /************************************************************************* * * These two are optional ones... maybe if I get really keen? * *************************************************************************/ SANE_Status sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking) { DBG(2, ">> sane_set_io_mode (%p, %d) (not really supported)\n", h, non_blocking); if (non_blocking == SANE_FALSE) return SANE_STATUS_GOOD; DBG(2, "<< sane_set_io_mode\n"); return SANE_STATUS_UNSUPPORTED; } SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int *fdp) { DBG(2, ">> sane_get_select_fd (%p, %p) (not supported)\n", h, (const void *)fdp); DBG(2, "<< sane_get_select_fd\n"); return SANE_STATUS_UNSUPPORTED; } /************************************************************************* * * init_cal(): Try to create a calibration file * has to be changed. * ************************************************************************/ static int init_cal(char *file) { char *tmp, *path; int f, i; if ((f = open(file, O_CREAT | O_WRONLY, 0600)) < 0) { if (errno == ENOENT) { /* we need to try and make ~/.sane perhaps - * find the last / in the file path, and try * to create it */ if ((tmp = strrchr(file, '/')) == NULL) return -1; path = strdup(file); *(path + (tmp-file)) = '\0'; i = mkdir(path, 0777); free(path); if (i) return -1; /* Path has been created, now try this again.. */ if ((f = open(file, O_CREAT | O_WRONLY, 0600)) < 0) return -1; } else { /* Error is something like access denied - too * hard to fix, so i give up... */ return -1; } } /* should probably set defaults here.. */ close(f); return 0; } /************************************************************************* * * fix_weights_file(): Ensures that the weights_file setting for a given * scanner is valid * ************************************************************************/ static SANE_Status fix_weights_file(CANONP_Scanner *cs) { static const char default_weights_file_prefix[] = "~/.sane/canon_pp-calibration-"; char *tmp, *myhome; int i; struct stat *f_stat; if (cs == NULL) { DBG(0, "fix_weights_file: FATAL: NULL passed by my code, " "please report this!\n"); return SANE_STATUS_INVAL; } /* Assume this is false and then correct it */ cs->cal_readonly = SANE_FALSE; if (cs->weights_file == NULL) { /* Form is ~/.sane/canon_pp-calibration-parport0 or -0x378 */ i = strlen(default_weights_file_prefix) + strlen(cs->params.port->name); if ((cs->weights_file = malloc(i + 1)) == NULL) return SANE_STATUS_NO_MEM; sprintf(cs->weights_file, "%s%s", default_weights_file_prefix, cs->params.port->name); } /* Get the user's home dir if they used ~ */ if (cs->weights_file[0] == '~') { if ((myhome = getenv("HOME")) == NULL) { DBG(0,"fix_weights_file: FATAL: ~ used, but $HOME not" " set!\n"); return SANE_STATUS_INVAL; } i = strlen(myhome) + strlen(&cs->weights_file[1]); if ((tmp = malloc(i + 1)) == NULL) return SANE_STATUS_NO_MEM; sprintf(tmp, "%s%s", myhome, &cs->weights_file[1]); free(cs->weights_file); cs->weights_file = tmp; tmp = NULL; } if ((f_stat = malloc(sizeof(*f_stat))) == NULL) return SANE_STATUS_NO_MEM; if(stat(cs->weights_file, f_stat)) { /* this non-intuitive if basically is if we got some error that * wasn't no-such-file, or we can't create the file.. */ if ((errno != ENOENT) || init_cal(cs->weights_file)) { /* Some nasty error returned. Give up. */ DBG(2,"fix_weights_file: error stating cal file" " (%s)\n", strerror(errno)); DBG(2,"fix_weights_file: Changes to cal data won't" " be saved!\n"); free(cs->weights_file); cs->weights_file = NULL; } } else { /* No error returned.. Check read/writability */ i = open(cs->weights_file, O_RDWR | O_APPEND); if (i <= 0) { DBG(10,"fix_weighs_file: Note: Changes to cal data " "won't be saved!\n"); i = open(cs->weights_file, O_RDONLY); if (i <= 0) { /* * Open failed (do i care why?) */ DBG(2,"fix_weights_file: error opening cal " "(%s)\n", strerror(errno)); free(cs->weights_file); cs->weights_file = NULL; } else { DBG(2,"fix_weights_file: file is read-only, " "changes won't be saved\n"); cs->cal_readonly = SANE_TRUE; close(i); } } else { /* good! */ DBG(10,"fix_weights_file: Calibration file is good " "for opening!\n"); close(i); } } /* cleanup */ free(f_stat); return SANE_STATUS_GOOD; } /* detect_mode * PRE: * cs->params.port is not open * POST: * cs->params.port is left opened iff SANE_STATUS_GOOD returned. */ SANE_Status detect_mode(CANONP_Scanner *cs) { int capabilities, tmp; /* Open then claim parallel port using libieee1284 */ DBG(10,"detect_mode: Opening port %s\n", (cs->params.port->name)); tmp = ieee1284_open(cs->params.port, 0, &capabilities); if (tmp != E1284_OK) { switch (tmp) { case E1284_INVALIDPORT: DBG(1, "detect_mode: Invalid port.\n"); break; case E1284_SYS: DBG(1, "detect_mode: System error: %s\n", strerror(errno)); break; case E1284_INIT: DBG(1, "detect_mode: Initialisation error.\n"); break; default: DBG(1, "detect_mode: Unknown error.\n"); break; } return SANE_STATUS_IO_ERROR; } DBG(10,"detect_mode: Claiming port.\n"); if (ieee1284_claim(cs->params.port) != E1284_OK) { DBG(1,"detect_mode: Unable to claim port\n"); ieee1284_close(cs->params.port); return SANE_STATUS_IO_ERROR; } /* Check that compatibility-mode (required) is supported */ if (!(capabilities & CAP1284_COMPAT)) { DBG(0,"detect_mode: Compatibility mode (required) not " "supported.\n"); ieee1284_release(cs->params.port); ieee1284_close(cs->params.port); return SANE_STATUS_IO_ERROR; } /* Check capabilities which will enchance speed */ if (capabilities & CAP1284_ECP) DBG(2, "detect_mode: Port supports ECP-H.\n"); else if (capabilities & CAP1284_ECPSWE) DBG(2, "detect_mode: Port supports ECP-S.\n"); if (capabilities & CAP1284_IRQ) DBG(2, "detect_mode: Port supports interrupts.\n"); if (capabilities & CAP1284_DMA) DBG(2, "detect_mode: Port supports DMA.\n"); /* Check whether ECP mode is possible */ if (capabilities & CAP1284_ECP) { cs->ieee1284_mode = M1284_ECP; DBG(10, "detect_mode: Using ECP-H Mode\n"); } else if (capabilities & CAP1284_ECPSWE) { cs->ieee1284_mode = M1284_ECPSWE; DBG(10, "detect_mode: Using ECP-S Mode\n"); } else if (capabilities & CAP1284_NIBBLE) { cs->ieee1284_mode = M1284_NIBBLE; DBG(10, "detect_mode: Using nibble mode\n"); } else { DBG(0, "detect_mode: No supported parport modes available!\n"); ieee1284_release(cs->params.port); ieee1284_close(cs->params.port); return SANE_STATUS_IO_ERROR; } /* Check to make sure ECP mode really is supported */ /* Have disabled the hardware ECP check because it's always supported * by libieee1284 now, and it's too prone to hitting a ppdev bug */ /* Disabled check entirely.. check now in initialise when we * actually do a read */ #if 0 if ((cs->ieee1284_mode == M1284_ECP) || (cs->ieee1284_mode == M1284_ECPSWE)) { DBG(1, "detect_mode: attempting a 0 byte read, if we hang " "here, it's a ppdev bug!\n"); /* * 29/06/02 * NOTE: * This causes an infinite loop in ppdev on 2.4.18. * Not checking on hardware ECP mode should work-around * effectively. * * I have sent email to twaugh about it, should be fixed in * 2.4.19 and above. */ if (ieee1284_ecp_read_data(cs->params.port, 0, NULL, 0) == E1284_NOTIMPL) { DBG(10, "detect_mode: Your version of libieee1284 " "doesn't support ECP mode - defaulting" " to nibble mode instead.\n"); cs->ieee1284_mode = M1284_NIBBLE; } } #endif if (force_nibble == SANE_TRUE) { DBG(10, "detect_mode: Nibble mode force in effect.\n"); cs->ieee1284_mode = M1284_NIBBLE; } ieee1284_release(cs->params.port); sanei_canon_pp_set_ieee1284_mode(cs->ieee1284_mode); return SANE_STATUS_GOOD; } backends-1.3.0/backend/canon_pp.conf.in000066400000000000000000000022061456256263500177430ustar00rootroot00000000000000# 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 dependent on your OS version. # # If a file isn't specified, 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 backends-1.3.0/backend/canon_pp.h000066400000000000000000000063361456256263500166500ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2001-2002 Matthew C. Duggan and Simon Krix 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, see . 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 is part of the canon_pp backend, supporting Canon FBX30P and NX40P scanners */ #ifndef CANON_PARALLEL_H #define CANON_PARALLEL_H #ifdef BACKEND_NAME #undef BACKEND_NAME #define BACKEND_NAME canon_pp #endif #define DEBUG_NOT_STATIC #include "../include/sane/sanei_debug.h" #ifndef PACKAGE #define PACKAGE "Canon Parallel SANE Backend" #endif #define CMODE_COLOUR "Colour" #define CMODE_MONO "Mono" #define CANONP_CONFIG_FILE "canon_pp.conf" /* options: num,res,colour,depth,tl-x,tl-y,br-x,br-y,cal */ /* preview option disabled */ #define NUM_OPTIONS 9 #define BUF_MAX 64000 /* Indexes into options array */ #define OPT_NUM_OPTIONS 0 #define OPT_RESOLUTION 1 #define OPT_COLOUR_MODE 2 #define OPT_DEPTH 3 #define OPT_TL_X 4 #define OPT_TL_Y 5 #define OPT_BR_X 6 #define OPT_BR_Y 7 #define OPT_CAL 8 #define OPT_PREVIEW 9 #if 0 #define OPT_GAMMA_R 10 #define OPT_GAMMA_G 11 #define OPT_GAMMA_B 12 #endif /*#define OPT_GAMMA 13*/ typedef struct CANONP_Scanner_Struct CANONP_Scanner; struct CANONP_Scanner_Struct { CANONP_Scanner *next; SANE_Device hw; SANE_Option_Descriptor opt[NUM_OPTIONS]; SANE_Int vals[NUM_OPTIONS]; SANE_Bool opened; SANE_Bool scanning; SANE_Bool sent_eof; SANE_Bool cancelled; SANE_Bool setup; SANE_Int lines_scanned; SANE_Int bytes_sent; char *weights_file; SANE_Bool cal_readonly; SANE_Bool cal_valid; scanner_parameters params; scan_parameters scan; int ieee1284_mode; int init_mode; SANE_Bool scanner_present; }; #endif backends-1.3.0/backend/cardscan.c000066400000000000000000001330041456256263500166150ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. This file is part of the SANE package, and implements a SANE backend for various Corex Cardscan scanners. Copyright (C) 2007-2015 m. allan noah -------------------------------------------------------------------------- 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, see . 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. -------------------------------------------------------------------------- The source code is divided in sections which you can easily find by searching for the tag "@@". Section 1 - Init & static stuff 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: v0, 2007-05-09, MAN (SANE v1.0.19) - initial release v1, 2008-02-14, MAN - sanei_config_read has already cleaned string (#310597) v2, 2010-02-10, MAN - add lines_per_block config option - add has_cal_buffer config option - basic support for 600c - clean #include lines v3, 2015-11-04, MAN - add USB IDs for newer model 800c ################################################## DATA FROM TRACE OF WINDOWS DRIVER: cmd packet format: cmdcode cmdlenlow cmdlenhigh cmdpayloadbytes resp packet format: respcode paperfound resplenlow resplenhigh respayloadbytes ############ status read loop? ################## >> 01 01 00 00 << 81 00 07 00 00 09 0c 61 c2 7a 0a >> 34 00 00 << b4 00 00 00 >> 01 01 00 00 << 81 00 07 00 00 09 0c 61 c2 7a 0a >> 34 00 00 << b4 00 00 00 >> 01 01 00 00 << 81 00 07 00 00 09 0c 61 c2 7a 0a ############# scanner settings read? (0x04b8 is scan width) ############# >> 48 00 00 << c8 00 0c 00 b8 04 60 00 00 80 00 00 00 58 ca 7d ############## color and gray calibration data read ############ >> 45 00 00 << 0x2600 bytes, bbbBBBgggGGGrrrRRRxxxXXX ############ 34/b4 and 01/81 status loop til paper inserted ############## >> 35 01 00 00 << b5 01 01 00 00 always together? { >> 14 05 00 80 1b 28 00 0f << 94 01 05 00 80 1b 28 00 0f >> 22 01 00 00 << a2 01 01 00 00 } >> 1a 01 00 66 << 9a 01 01 00 66 >> 19 03 00 51 62 49 << 99 01 03 00 51 62 49 ############# heat up lamp? ################# ===========color=================== three times { >> 18 07 00 00 01 60 00 61 00 07 << 0x40 read and 0x03 read the 3 byte drops from f4 f4 f4 to 17 10 08 etc. } ===========gray=================== three times { >> 12 06 00 00 01 60 00 61 00 << 0x40 read and 0x01 read } the 1 byte drops from f4 to 02 ================================== >> 35 01 00 00 << b5 01 01 00 00 >> 13 01 00 28 << 93 01 01 00 28 ===========color=================== three times { >> 18 07 00 01 10 60 00 18 05 07 << 0xe2c0 read } 14/94 and 22/a2 many times { >> 18 07 00 01 10 60 00 18 05 07 << 0xe2c0 read } ===========gray=================== two times { >> 12 06 00 01 10 60 00 18 05 << 0x4bc0 read } 14/94 and 22/a2 many times { >> 12 06 00 01 10 60 00 18 05 << 0x4bc0 read } ================================== >> 35 01 00 ff << b5 00 01 00 ff 14/94 and 22/a2 ########### discharge capacitor? ########### four times { >> 21 02 00 0a 00 << a1 00 02 00 0a 00 } >> 01 01 00 00 << 81 00 07 00 00 09 0c 61 c2 7a 0a >> 35 01 00 ff << b5 00 01 00 ff >> 34 00 00 << b4 00 00 00 ############################################# SANE FLOW DIAGRAM - 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 . . - sane_read() : read image data (from pipe) . . (sane_read called multiple times; after sane_read returns EOF, . . loop may continue with sane_start which may return a 2nd page . . when doing duplex scans, or load the next page from the ADF) . . . . - sane_cancel() : cancel operation . - sane_close() : close opened scanner device - sane_exit() : terminate use of backend */ /* * @@ Section 1 - Init */ #include "../include/sane/config.h" #include /*memcpy...*/ #include /*isspace*/ #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_config.h" #include "cardscan.h" #define DEBUG 1 #define BUILD 3 /* values for SANE_DEBUG_CARDSCAN env var: - errors 5 - function trace 10 - function detail 15 - get/setopt cmds 20 - usb cmd trace 25 - usb cmd detail 30 - useless noise 35 */ int global_has_cal_buffer = 1; int global_lines_per_block = 16; /* ------------------------------------------------------------------------- */ #define STRING_GRAYSCALE SANE_VALUE_SCAN_MODE_GRAY #define STRING_COLOR SANE_VALUE_SCAN_MODE_COLOR /* * 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 scanner structs */ static const SANE_Device **sane_devArray = NULL; static struct scanner *scanner_devList = NULL; /* * @@ Section 2 - SANE & scanner init code */ /* * 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) { (void) authorize; /* get rid of compiler warning */ DBG_INIT (); DBG (10, "sane_init: start\n"); sanei_usb_init(); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); DBG (5, "sane_init: cardscan backend %d.%d.%d, from %s\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD, PACKAGE_STRING); DBG (10, "sane_init: finish\n"); 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. * * Read the config file, find scanners with help from sanei_* * store in global device structs */ SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { struct scanner *dev; char line[PATH_MAX]; const char *lp; FILE *fp; int num_devices=0; int i=0; (void) local_only; /* get rid of compiler warning */ DBG (10, "sane_get_devices: start\n"); global_has_cal_buffer = 1; global_lines_per_block = 16; fp = sanei_config_open (CONFIG_FILE); if (fp) { DBG (15, "sane_get_devices: reading config file %s\n", CONFIG_FILE); while (sanei_config_read (line, PATH_MAX, fp)) { lp = line; /* ignore comments */ if (*lp == '#') continue; /* skip empty lines */ if (*lp == 0) continue; if ((strncmp ("usb", lp, 3) == 0) && isspace (lp[3])) { DBG (15, "sane_get_devices: looking for '%s'\n", lp); sanei_usb_attach_matching_devices(lp, attach_one); } else if (!strncmp(lp, "has_cal_buffer", 14) && isspace (lp[14])) { int buf; lp += 14; lp = sanei_config_skip_whitespace (lp); buf = atoi (lp); if(buf){ global_has_cal_buffer = 1; } else{ global_has_cal_buffer = 0; } DBG (15, "sane_get_devices: setting \"has_cal_buffer\" to %d\n", global_has_cal_buffer); } else if (!strncmp(lp, "lines_per_block", 15) && isspace (lp[15])) { int buf; lp += 15; lp = sanei_config_skip_whitespace (lp); buf = atoi (lp); if(buf < 1 || buf > 32){ DBG (15, "sane_get_devices: \"lines_per_block\"=%d\n out of range", buf ); continue; } DBG (15, "sane_get_devices: \"lines_per_block\" is %d\n", buf); global_lines_per_block = buf; } else{ DBG (5, "sane_get_devices: config line \"%s\" ignored.\n", lp); } } fclose (fp); } else { DBG (5, "sane_get_devices: no config file '%s', using defaults\n", CONFIG_FILE); DBG (15, "sane_get_devices: looking for 'usb 0x08F0 0x0005'\n"); sanei_usb_attach_matching_devices("usb 0x08F0 0x0005", attach_one); } for (dev = scanner_devList; dev; dev=dev->next) { DBG (15, "sane_get_devices: found scanner %s\n",dev->device_name); num_devices++; } DBG (15, "sane_get_devices: found %d scanner(s)\n",num_devices); sane_devArray = calloc (num_devices + 1, sizeof (SANE_Device*)); if (!sane_devArray) return SANE_STATUS_NO_MEM; for (dev = scanner_devList; dev; dev=dev->next) { sane_devArray[i++] = (SANE_Device *)&dev->sane; } sane_devArray[i] = 0; *device_list = sane_devArray; DBG (10, "sane_get_devices: finish\n"); return SANE_STATUS_GOOD; } /* callback used by sane_get_devices * build the scanner struct and link to global list * unless struct is already loaded, then pretend */ static SANE_Status attach_one (const char *device_name) { struct scanner *s; int ret, i; SANE_Word vid, pid; DBG (10, "attach_one: start '%s'\n", device_name); for (s = scanner_devList; s; s = s->next) { if (strcmp (s->sane.name, device_name) == 0) { DBG (10, "attach_one: already attached!\n"); return SANE_STATUS_GOOD; } } /* build a scanner struct to hold it */ DBG (15, "attach_one: init struct\n"); if ((s = calloc (sizeof (*s), 1)) == NULL) return SANE_STATUS_NO_MEM; /* copy the device name */ s->device_name = strdup (device_name); if (!s->device_name){ free (s); return SANE_STATUS_NO_MEM; } /* connect the fd */ DBG (15, "attach_one: connect fd\n"); s->fd = -1; ret = connect_fd(s); if(ret != SANE_STATUS_GOOD){ free (s->device_name); free (s); return ret; } /* clean up the scanner struct based on model */ /* this is the only piece of model specific code */ sanei_usb_get_vendor_product(s->fd,&vid,&pid); if(vid == 0x08f0){ s->vendor_name = "CardScan"; if(pid == 0x0005){ s->product_name = "800c"; } else if(pid == 0x0002){ s->product_name = "600c"; } else{ DBG (5, "Unknown product, using default settings\n"); s->product_name = "Unknown"; } } else if(vid == 0x0451){ s->vendor_name = "Sanford"; if(pid == 0x6250){ s->product_name = "800c"; } else{ DBG (5, "Unknown product, using default settings\n"); s->product_name = "Unknown"; } } else{ DBG (5, "Unknown vendor/product, using default settings\n"); s->vendor_name = "Unknown"; s->product_name = "Unknown"; } DBG (15, "attach_one: Found %s scanner %s at %s\n", s->vendor_name, s->product_name, s->device_name); /*copy config file settings*/ s->has_cal_buffer = global_has_cal_buffer; s->lines_per_block = global_lines_per_block; s->color_block_size = s->lines_per_block * PIXELS_PER_LINE * 3; s->gray_block_size = s->lines_per_block * PIXELS_PER_LINE; /* try to get calibration */ if(s->has_cal_buffer){ DBG (15, "attach_one: scanner calibration\n"); ret = load_calibration(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot calibrate, incompatible?\n"); free (s->device_name); free (s); return ret; } } else{ DBG (15, "attach_one: skipping calibration\n"); } /* set SANE option 'values' to good defaults */ DBG (15, "attach_one: init options\n"); /* go ahead and setup the first opt, because * frontend may call control_option on it * before calling get_option_descriptor */ 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; } 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].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; DBG (15, "attach_one: init settings\n"); /* 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"; s->next = scanner_devList; scanner_devList = s; DBG (10, "attach_one: finish\n"); return SANE_STATUS_GOOD; } /* * connect the fd in the scanner struct */ static SANE_Status connect_fd (struct scanner *s) { SANE_Status ret; DBG (10, "connect_fd: start\n"); if(s->fd > -1){ DBG (5, "connect_fd: already open\n"); ret = SANE_STATUS_GOOD; } else { DBG (15, "connect_fd: opening USB device\n"); ret = sanei_usb_open (s->device_name, &(s->fd)); } if(ret != SANE_STATUS_GOOD){ DBG (5, "connect_fd: could not open device: %d\n", ret); } DBG (10, "connect_fd: finish\n"); return ret; } static SANE_Status load_calibration(struct scanner *s) { SANE_Status ret=SANE_STATUS_GOOD; unsigned char cmd[] = {0x45, 0x00, 0x00}; unsigned char * buf; size_t bytes = HEADER_SIZE + CAL_COLOR_SIZE*2 + CAL_GRAY_SIZE*2; int j; DBG (10, "load_calibration: start\n"); buf = malloc(bytes); if(!buf){ DBG(5, "load_calibration: not enough mem for buffer: %ld\n",(long)bytes); return SANE_STATUS_NO_MEM; } ret = do_cmd( s, 0, cmd, sizeof(cmd), NULL, 0, buf, &bytes ); if (ret == SANE_STATUS_GOOD) { DBG(15, "load_calibration: got GOOD\n"); /* * color cal data comes from scaner like: * bbbbbbbBBBBBBBgggggggGGGGGGGrrrrrrrRRRRRRR * where b=darkblue, B=lightblue, etc * reorder the data into two buffers * bbbbbbbgggggggrrrrrrr and BBBBBBBGGGGGGGRRRRRRR */ /*dark/light blue*/ memcpy(s->cal_color_b, buf+HEADER_SIZE, PIXELS_PER_LINE); memcpy(s->cal_color_w, buf+HEADER_SIZE+PIXELS_PER_LINE, PIXELS_PER_LINE); /*dark/light green*/ memcpy(s->cal_color_b+PIXELS_PER_LINE, buf+HEADER_SIZE+(PIXELS_PER_LINE*2), PIXELS_PER_LINE); memcpy(s->cal_color_w+PIXELS_PER_LINE, buf+HEADER_SIZE+(PIXELS_PER_LINE*3), PIXELS_PER_LINE); /*dark/light red*/ memcpy(s->cal_color_b+(PIXELS_PER_LINE*2), buf+HEADER_SIZE+(PIXELS_PER_LINE*4), PIXELS_PER_LINE); memcpy(s->cal_color_w+(PIXELS_PER_LINE*2), buf+HEADER_SIZE+(PIXELS_PER_LINE*5), PIXELS_PER_LINE); /* then slide the light data down using the dark offset */ for(j=0;jcal_color_w[j] -= s->cal_color_b[j]; } /*dark/light gray*/ memcpy(s->cal_gray_b, buf+HEADER_SIZE+(CAL_COLOR_SIZE*2), PIXELS_PER_LINE); memcpy(s->cal_gray_w, buf+HEADER_SIZE+(CAL_COLOR_SIZE*2)+PIXELS_PER_LINE, PIXELS_PER_LINE); /* then slide the light data down using the dark offset */ for(j=0;jcal_gray_w[j] -= s->cal_gray_b[j]; } hexdump(35, "cal_color_b:", s->cal_color_b, CAL_COLOR_SIZE); hexdump(35, "cal_color_w:", s->cal_color_w, CAL_COLOR_SIZE); hexdump(35, "cal_gray_b:", s->cal_gray_b, CAL_GRAY_SIZE); hexdump(35, "cal_gray_w:", s->cal_gray_w, CAL_GRAY_SIZE); } else { DBG(5, "load_calibration: error reading data block status = %d\n", ret); } DBG (10, "load_calibration: finish\n"); return ret; } /* * 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 scanner *dev = NULL; struct scanner *s = NULL; SANE_Status ret; DBG (10, "sane_open: start\n"); if(name[0] == 0){ if(scanner_devList){ DBG (15, "sane_open: no device requested, using first\n"); s = scanner_devList; } else{ DBG (15, "sane_open: no device requested, none found\n"); } } else{ DBG (15, "sane_open: device %s requested, attaching\n", name); ret = attach_one(name); if(ret){ DBG (5, "sane_open: attach error %d\n",ret); return ret; } for (dev = scanner_devList; dev; dev = dev->next) { if (strcmp (dev->sane.name, name) == 0) { s = dev; break; } } } if (!s) { DBG (5, "sane_open: no device found\n"); return SANE_STATUS_INVAL; } DBG (15, "sane_open: device %s found\n", s->sane.name); *handle = s; /* connect the fd so we can talk to scanner */ ret = connect_fd(s); if(ret != SANE_STATUS_GOOD){ return ret; } DBG (10, "sane_open: finish\n"); return SANE_STATUS_GOOD; } /* * @@ Section 3 - SANE Options functions */ /* * 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 scanner *s = handle; int i; SANE_Option_Descriptor *opt = &s->opt[option]; DBG (20, "sane_get_option_descriptor: %d\n", option); if ((unsigned) option >= NUM_OPTIONS) return NULL; /* "Mode" group -------------------------------------------------------- */ if(option==OPT_MODE_GROUP){ opt->title = "Scan Mode"; opt->desc = ""; opt->type = SANE_TYPE_GROUP; opt->constraint_type = SANE_CONSTRAINT_NONE; } /* scan mode */ else if(option==OPT_MODE){ i=0; s->mode_list[i++]=STRING_GRAYSCALE; s->mode_list[i++]=STRING_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; } return opt; } /** * 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 scanner *s = (struct scanner *) handle; SANE_Int dummy = 0; /* Make sure that all those statements involving *info cannot break (better * than having to do "if (info) ..." everywhere!) */ if (info == 0) info = &dummy; if (option >= NUM_OPTIONS) { DBG (5, "sane_control_option: %d too big\n", option); return SANE_STATUS_INVAL; } if (!SANE_OPTION_IS_ACTIVE (s->opt[option].cap)) { DBG (5, "sane_control_option: %d inactive\n", option); return SANE_STATUS_INVAL; } /* * 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; DBG (20, "sane_control_option: get value for '%s' (%d)\n", s->opt[option].name,option); switch (option) { case OPT_NUM_OPTS: *val_p = NUM_OPTIONS; return SANE_STATUS_GOOD; case OPT_MODE: if(s->mode == MODE_GRAYSCALE){ strcpy (val, STRING_GRAYSCALE); } else if(s->mode == MODE_COLOR){ strcpy (val, STRING_COLOR); } return SANE_STATUS_GOOD; } } else if (action == SANE_ACTION_SET_VALUE) { int tmp; 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: can't set, device busy\n"); return SANE_STATUS_DEVICE_BUSY; } 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; } /* * 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) { /* Mode Group */ case OPT_MODE: if (!strcmp (val, STRING_GRAYSCALE)) { tmp = MODE_GRAYSCALE; } else{ tmp = MODE_COLOR; } if (tmp == s->mode) return SANE_STATUS_GOOD; s->mode = tmp; *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; } /* switch */ } /* else */ return SANE_STATUS_INVAL; } /* * @@ 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 scanner *s = (struct scanner *) handle; DBG (10, "sane_get_parameters: start\n"); params->pixels_per_line = PIXELS_PER_LINE; params->lines = -1; params->last_frame = 1; if (s->mode == MODE_COLOR) { params->format = SANE_FRAME_RGB; params->depth = 8; params->bytes_per_line = params->pixels_per_line * 3; } else if (s->mode == MODE_GRAYSCALE) { params->format = SANE_FRAME_GRAY; params->depth = 8; params->bytes_per_line = params->pixels_per_line; } 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); DBG (10, "sane_get_parameters: finish\n"); return SANE_STATUS_GOOD; } /* * Called by SANE when a page acquisition operation is to be started. */ SANE_Status sane_start (SANE_Handle handle) { struct scanner *s = handle; SANE_Status ret; DBG (10, "sane_start: start\n"); /* first page of batch */ if(s->started){ DBG(5,"sane_start: previous transfer not finished?"); sane_cancel((SANE_Handle)s); return SANE_STATUS_CANCELLED; } /* set clean defaults */ s->started=1; s->bytes_rx=0; s->bytes_tx=0; s->paperless_lines=0; /* heat up the lamp */ if(s->mode == MODE_COLOR){ ret = heat_lamp_color(s); } else{ ret = heat_lamp_gray(s); } if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: failed to heat lamp\n"); sane_cancel((SANE_Handle)s); return ret; } DBG (10, "sane_start: finish\n"); return SANE_STATUS_GOOD; } static SANE_Status heat_lamp_gray(struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; SANE_Status ret2 = SANE_STATUS_GOOD; unsigned char cmd[] = {0x12, 0x06, 0x00, 0x00, 0x01, 0x60, 0x00, 0x61, 0x00}; size_t bytes = HEADER_SIZE + 1; unsigned char * buf; int i; DBG (10, "heat_lamp_gray: start\n"); buf = malloc(bytes); if(!buf){ DBG(5, "heat_lamp_gray: not enough mem for buffer: %lu\n", (long unsigned)bytes); return SANE_STATUS_NO_MEM; } for(i=0;i<10;i++){ ret2 = do_cmd( s, 0, cmd, sizeof(cmd), NULL, 0, buf, &bytes ); if (ret2 != SANE_STATUS_GOOD) { DBG(5, "heat_lamp_gray: %d error\n",i); ret = ret2; break; } if(!buf[1]){ DBG(5, "heat_lamp_gray: %d got no docs\n",i); ret = SANE_STATUS_NO_DOCS; break; } DBG(15, "heat_lamp_gray: %d got: %d %d\n",i, buf[HEADER_SIZE],s->cal_gray_b[0]); if(buf[HEADER_SIZE] < 0x20){ DBG(15, "heat_lamp_gray: hot\n"); ret = SANE_STATUS_GOOD; break; } else{ DBG(15, "heat_lamp_gray: cold\n"); ret = SANE_STATUS_DEVICE_BUSY; } } free(buf); DBG (10, "heat_lamp_gray: finish %d\n",ret); return ret; } static SANE_Status heat_lamp_color(struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; SANE_Status ret2 = SANE_STATUS_GOOD; unsigned char cmd[] = {0x18, 0x07, 0x00, 0x00, 0x01, 0x60, 0x00, 0x61, 0x00, 0x07}; size_t bytes = HEADER_SIZE + 3; unsigned char * buf; int i; DBG (10, "heat_lamp_color: start\n"); buf = malloc(bytes); if(!buf){ DBG(5, "heat_lamp_color: not enough mem for buffer: %lu\n", (long unsigned)bytes); return SANE_STATUS_NO_MEM; } for(i=0;i<10;i++){ ret2 = do_cmd( s, 0, cmd, sizeof(cmd), NULL, 0, buf, &bytes ); if (ret2 != SANE_STATUS_GOOD) { DBG(5, "heat_lamp_color: %d error\n",i); ret = ret2; break; } if(!buf[1]){ DBG(5, "heat_lamp_color: %d got no docs\n",i); ret = SANE_STATUS_NO_DOCS; break; } DBG(15, "heat_lamp_color: %d got: %d,%d,%d %d,%d,%d\n",i, buf[HEADER_SIZE],buf[HEADER_SIZE+1],buf[HEADER_SIZE+2], s->cal_color_b[0],s->cal_color_b[1],s->cal_color_b[2]); if(buf[HEADER_SIZE] < 0x20 && buf[HEADER_SIZE+1] < 0x20 && buf[HEADER_SIZE+2] < 0x20){ DBG(15, "heat_lamp_color: hot\n"); ret = SANE_STATUS_GOOD; break; } else{ DBG(15, "heat_lamp_color: cold\n"); ret = SANE_STATUS_DEVICE_BUSY; } } free(buf); DBG (10, "heat_lamp_color: finish %d\n",ret); return ret; } /* * 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 scanner *s = (struct scanner *) handle; SANE_Status ret=SANE_STATUS_GOOD; DBG (10, "sane_read: start\n"); *len = 0; /* cancelled? */ if(!s->started){ DBG (5, "sane_read: call sane_start first\n"); return SANE_STATUS_CANCELLED; } /* have sent all of current buffer */ if(s->bytes_tx == s->bytes_rx){ /* at end of data, stop */ if(s->paperless_lines >= MAX_PAPERLESS_LINES){ DBG (15, "sane_read: returning eof\n"); power_down(s); return SANE_STATUS_EOF; } /* more to get, reset and go */ s->bytes_tx = 0; s->bytes_rx = 0; if(s->mode == MODE_COLOR){ ret = read_from_scanner_color(s); } else{ ret = read_from_scanner_gray(s); } if(ret){ DBG(5,"sane_read: returning %d\n",ret); return ret; } } /* data in current buffer, send some of it */ *len = s->bytes_rx - s->bytes_tx; if(*len > max_len){ *len = max_len; } memcpy(buf,s->buffer+s->bytes_tx,*len); s->bytes_tx += *len; DBG (10, "sane_read: %d,%d,%d finish\n", *len,s->bytes_rx,s->bytes_tx); return ret; } static SANE_Status read_from_scanner_gray(struct scanner *s) { SANE_Status ret=SANE_STATUS_GOOD; /*cmd len-le16 move lines ??? ??? ??? ???*/ unsigned char cmd[] = {0x12, 0x06, 0x00, 0x01, 0x01, 0x60, 0x00, 0x18, 0x05}; size_t bytes = HEADER_SIZE + s->gray_block_size; unsigned char * buf; int i,j; DBG (10, "read_from_scanner_gray: start\n"); cmd[4] = s->lines_per_block; buf = malloc(bytes); if(!buf){ DBG(5, "read_from_scanner_gray: not enough mem for buffer: %lu\n", (long unsigned)bytes); return SANE_STATUS_NO_MEM; } ret = do_cmd( s, 0, cmd, sizeof(cmd), NULL, 0, buf, &bytes ); if (ret == SANE_STATUS_GOOD) { DBG(15, "read_from_scanner_gray: got GOOD\n"); if(!buf[1]){ s->paperless_lines += s->lines_per_block; } s->bytes_rx = s->gray_block_size; /*memcpy(s->buffer,buf+HEADER_SIZE,s->gray_block_size);*/ /* reorder the gray data into the struct's buffer */ for(i=0;igray_block_size;i+=PIXELS_PER_LINE){ for(j=0;jcal_gray_b[j]; unsigned char wcal = s->cal_gray_w[j]; byte = (byte <= bcal)?0:(byte-bcal); byte = (byte >= wcal)?255:(byte*255/wcal); s->buffer[i+j] = byte; } } } else { DBG(5, "read_from_scanner_gray: error reading status = %d\n", ret); } free(buf); DBG (10, "read_from_scanner_gray: finish\n"); return ret; } static SANE_Status read_from_scanner_color(struct scanner *s) { SANE_Status ret=SANE_STATUS_GOOD; unsigned char cmd[] = {0x18, 0x07, 0x00, 0x01, 0x01, 0x60, 0x00, 0x18, 0x05, 0x07}; size_t bytes = HEADER_SIZE + s->color_block_size; unsigned char * buf; int i,j,k; DBG (10, "read_from_scanner_color: start\n"); cmd[4] = s->lines_per_block; buf = malloc(bytes); if(!buf){ DBG(5, "read_from_scanner_color: not enough mem for buffer: %lu\n", (long unsigned)bytes); return SANE_STATUS_NO_MEM; } ret = do_cmd( s, 0, cmd, sizeof(cmd), NULL, 0, buf, &bytes ); if (ret == SANE_STATUS_GOOD) { DBG(15, "read_from_scanner_color: got GOOD\n"); if(!buf[1]){ s->paperless_lines += s->lines_per_block; } s->bytes_rx = s->color_block_size; /*memcpy(s->buffer,buf+HEADER_SIZE,s->color_block_size);*/ /* reorder the color data into the struct's buffer */ for(i=0;icolor_block_size;i+=PIXELS_PER_LINE*3){ for(j=0;jcal_color_b[offset]; unsigned char wcal = s->cal_color_w[offset]; byte = (byte <= bcal)?0:(byte-bcal); byte = (byte >= wcal)?255:(byte*255/wcal); s->buffer[i+j*3+k] = byte; } } } } else { DBG(5, "read_from_scanner_color: error reading status = %d\n", ret); } free(buf); DBG (10, "read_from_scanner_color: finish\n"); return ret; } /* * @@ Section 4 - SANE cleanup functions */ /* * Cancels a scan. * * 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 operation 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) { struct scanner * s = (struct scanner *) handle; DBG (10, "sane_cancel: start\n"); s->started = 0; DBG (10, "sane_cancel: finish\n"); } static SANE_Status power_down(struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[] = {0x21, 0x02, 0x00, 0x0a, 0x00}; unsigned char buf[6]; size_t bytes = sizeof(buf); int i; DBG (10, "power_down: start\n"); for(i=0;i<5;i++){ ret = do_cmd( s, 0, cmd, sizeof(cmd), NULL, 0, buf, &bytes ); if(ret != SANE_STATUS_GOOD){ break; } } #if 0 unsigned char cmd[] = {0x35, 0x01, 0x00, 0xff}; unsigned char buf[5]; size_t bytes = sizeof(buf); DBG (10, "power_down: start\n"); ret = do_cmd( s, 0, cmd, sizeof(cmd), NULL, 0, buf, &bytes ); #endif DBG (10, "power_down: finish %d\n",ret); return ret; } /* * 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"); sane_cancel(handle); disconnect_fd((struct scanner *) handle); DBG (10, "sane_close: finish\n"); } static SANE_Status disconnect_fd (struct scanner *s) { DBG (10, "disconnect_fd: start\n"); if(s->fd > -1){ DBG (15, "disconnecting usb device\n"); sanei_usb_close (s->fd); s->fd = -1; } DBG (10, "disconnect_fd: finish\n"); return SANE_STATUS_GOOD; } /* * 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 scanner *dev, *next; DBG (10, "sane_exit: start\n"); for (dev = scanner_devList; dev; dev = next) { disconnect_fd(dev); next = dev->next; free (dev->device_name); free (dev); } if (sane_devArray) free (sane_devArray); scanner_devList = NULL; sane_devArray = NULL; DBG (10, "sane_exit: finish\n"); } /* * @@ Section 5 - misc helper functions */ /* * take a bunch of pointers, send commands to scanner */ static SANE_Status do_cmd(struct scanner *s, int shortTime, unsigned char * cmdBuff, size_t cmdLen, unsigned char * outBuff, size_t outLen, unsigned char * inBuff, size_t * inLen ) { /* sanei_usb overwrites the transfer size, so make some local copies */ size_t loc_cmdLen = cmdLen; size_t loc_outLen = outLen; size_t loc_inLen = *inLen; int cmdTime = USB_COMMAND_TIME; int outTime = USB_DATA_TIME; int inTime = USB_DATA_TIME; int ret = 0; DBG (10, "do_cmd: start\n"); if(shortTime){ cmdTime /= 20; outTime /= 20; inTime /= 20; } /* change timeout */ sanei_usb_set_timeout(cmdTime); /* write the command out */ DBG(25, "cmd: writing %ld bytes, timeout %d\n", (long)cmdLen, cmdTime); hexdump(30, "cmd: >>", cmdBuff, cmdLen); ret = sanei_usb_write_bulk(s->fd, cmdBuff, &cmdLen); DBG(25, "cmd: wrote %ld bytes, retVal %d\n", (long)cmdLen, ret); if(ret == SANE_STATUS_EOF){ DBG(5,"cmd: got EOF, returning IO_ERROR\n"); return SANE_STATUS_IO_ERROR; } if(ret != SANE_STATUS_GOOD){ DBG(5,"cmd: return error '%s'\n",sane_strstatus(ret)); return ret; } if(loc_cmdLen != cmdLen){ DBG(5,"cmd: wrong size %ld/%ld\n", (long)loc_cmdLen, (long)cmdLen); return SANE_STATUS_IO_ERROR; } /* this command has a write component, and a place to get it */ if(outBuff && outLen && outTime){ /* change timeout */ sanei_usb_set_timeout(outTime); DBG(25, "out: writing %ld bytes, timeout %d\n", (long)outLen, outTime); hexdump(30, "out: >>", outBuff, outLen); ret = sanei_usb_write_bulk(s->fd, outBuff, &outLen); DBG(25, "out: wrote %ld bytes, retVal %d\n", (long)outLen, ret); if(ret == SANE_STATUS_EOF){ DBG(5,"out: got EOF, returning IO_ERROR\n"); return SANE_STATUS_IO_ERROR; } if(ret != SANE_STATUS_GOOD){ DBG(5,"out: return error '%s'\n",sane_strstatus(ret)); return ret; } if(loc_outLen != outLen){ DBG(5,"out: wrong size %ld/%ld\n", (long)loc_outLen, (long)outLen); return SANE_STATUS_IO_ERROR; } } /* this command has a read component, and a place to put it */ if(inBuff && inLen && inTime){ memset(inBuff,0,*inLen); /* change timeout */ sanei_usb_set_timeout(inTime); DBG(25, "in: reading %ld bytes, timeout %d\n", (long)*inLen, inTime); ret = sanei_usb_read_bulk(s->fd, inBuff, inLen); DBG(25, "in: retVal %d\n", ret); if(ret == SANE_STATUS_EOF){ DBG(5,"in: got EOF, continuing\n"); } else if(ret != SANE_STATUS_GOOD){ DBG(5,"in: return error '%s'\n",sane_strstatus(ret)); return ret; } DBG(25, "in: read %ld bytes\n", (long)*inLen); if(*inLen){ hexdump(30, "in: <<", inBuff, *inLen); } if(loc_inLen != *inLen){ ret = SANE_STATUS_EOF; DBG(5,"in: short read %ld/%ld\n", (long)loc_inLen, (long)*inLen); } } DBG (10, "do_cmd: finish\n"); return ret; } /** * 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; } return max_size; } /** * 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; if(DBG_LEVEL < level) return; 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; } *ptr = '\0'; DBG (level, "%s\n", line); } /** * 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 (15, "%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) { DBG (10, "sane_get_select_fd\n"); DBG (15, "%p %d\n", h, *fdp); return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/cardscan.conf.in000066400000000000000000000010501456256263500177200ustar00rootroot00000000000000# For scanners connected via USB on a known device (kernel driver): #usb /dev/usb/scanner0 # For 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. kitno455 at # gmail dot com - with cardscan in the subject line # Corex Cardscan 800c usb 0x08f0 0x0005 # Sanford Cardscan 800c usb 0x0451 0x6250 # Corex Cardscan 600c has_cal_buffer 0 lines_per_block 1 usb 0x08f0 0x0002 backends-1.3.0/backend/cardscan.h000066400000000000000000000144421456256263500166260ustar00rootroot00000000000000#ifndef CARDSCAN_H #define CARDSCAN_H /* * Part of SANE - Scanner Access Now Easy. * Please see opening comment in cardscan.c */ /* ------------------------------------------------------------------------- * 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 * still the possibility to say so, later. */ enum scanner_Option { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_MODE, /*mono/gray/color*/ /* must come last: */ NUM_OPTIONS }; /* values common to calib and image data */ #define HEADER_SIZE 64 #define PIXELS_PER_LINE 1208 /* values for calib data */ #define CAL_COLOR_SIZE (PIXELS_PER_LINE * 3) #define CAL_GRAY_SIZE PIXELS_PER_LINE /* values for image data */ #define MAX_PAPERLESS_LINES 210 struct scanner { /* --------------------------------------------------------------------- */ /* immutable values which are set during init of scanner. */ struct scanner *next; char *device_name; /* The name of the scanner device for sane */ /* --------------------------------------------------------------------- */ /* immutable values which are set during inquiry probing of the scanner. */ SANE_Device sane; char * vendor_name; char * product_name; /* --------------------------------------------------------------------- */ /* immutable values which are set during reading of config file. */ int has_cal_buffer; int lines_per_block; int color_block_size; int gray_block_size; /* --------------------------------------------------------------------- */ /* changeable SANE_Option structs provide our interface to frontend. */ /* long array of option structs */ SANE_Option_Descriptor opt[NUM_OPTIONS]; /* --------------------------------------------------------------------- */ /* 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 */ /*mode group*/ SANE_String_Const mode_list[3]; /* --------------------------------------------------------------------- */ /* changeable vars to hold user input. modified by SANE_Options above */ /*mode group*/ int mode; /*color,lineart,etc*/ /* --------------------------------------------------------------------- */ /* 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; /* --------------------------------------------------------------------- */ /* calibration data read once */ unsigned char cal_color_b[CAL_COLOR_SIZE]; unsigned char cal_gray_b[CAL_GRAY_SIZE]; unsigned char cal_color_w[CAL_COLOR_SIZE]; unsigned char cal_gray_w[CAL_GRAY_SIZE]; /* --------------------------------------------------------------------- */ /* values which are set by scanning functions to keep track of pages, etc */ int started; int paperless_lines; /* buffer part of image */ unsigned char buffer[PIXELS_PER_LINE * 3 * 32]; /* how far we have read from scanner into buffer */ int bytes_rx; /* how far we have written from buffer to frontend */ int bytes_tx; /* --------------------------------------------------------------------- */ /* values used by the command and data sending function */ int fd; /* The scanner device file descriptor. */ }; #define USB_COMMAND_TIME 10000 #define USB_DATA_TIME 10000 #define MODE_COLOR 0 #define MODE_GRAYSCALE 1 /* ------------------------------------------------------------------------- */ #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 CONFIG_FILE "cardscan.conf" #ifndef PATH_MAX # define PATH_MAX 1024 #endif /* ------------------------------------------------------------------------- */ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize); SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only); SANE_Status sane_open (SANE_String_Const name, SANE_Handle * handle); SANE_Status sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking); SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int * fdp); 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, SANE_Action action, void *val, SANE_Int * info); SANE_Status sane_start (SANE_Handle handle); SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params); SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len); void sane_cancel (SANE_Handle h); void sane_close (SANE_Handle h); void sane_exit (void); /* ------------------------------------------------------------------------- */ static SANE_Status attach_one (const char *devicename); static SANE_Status connect_fd (struct scanner *s); static SANE_Status disconnect_fd (struct scanner *s); static SANE_Status do_cmd(struct scanner *s, int shortTime, unsigned char * cmdBuff, size_t cmdLen, unsigned char * outBuff, size_t outLen, unsigned char * inBuff, size_t * inLen ); static SANE_Status load_calibration (struct scanner *s); static SANE_Status heat_lamp_color(struct scanner *s); static SANE_Status heat_lamp_gray(struct scanner *s); static SANE_Status read_from_scanner_color(struct scanner *s); static SANE_Status read_from_scanner_gray(struct scanner *s); static SANE_Status power_down(struct scanner *s); static void hexdump (int level, char *comment, unsigned char *p, int l); static size_t maxStringSize (const SANE_String_Const strings[]); #endif /* CARDSCAN_H */ backends-1.3.0/backend/coolscan-scsidef.h000066400000000000000000000701661456256263500202740ustar00rootroot00000000000000 /* ------------------------------------------------------------------------- */ /* coolscan-scsidef.h: scsi-definiton header file for COOLSCAN scanner driver. 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, see . 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 COOLSCAN_SCSIDEF_H #define COOLSCAN_SCSIDEF_H /* ========================================================================= */ /* I'm using functions derived from Eric Youngdale's scsiinfo * program here for dealing with parts of SCSI commands. */ static inline void setbitfield (unsigned char *pageaddr, int mask, int shift, int val) \ { *pageaddr = (*pageaddr & ~(mask << shift)) | ((val & mask) << shift); } /* ------------------------------------------------------------------------- */ static inline void resetbitfield (unsigned char *pageaddr, int mask, int shift, int val) \ { *pageaddr = (*pageaddr & ~(mask << shift)) | (((!val) & mask) << shift); } /* ------------------------------------------------------------------------- */ static inline int getbitfield (unsigned char *pageaddr, int mask, int shift) \ { return ((*pageaddr >> shift) & mask); } /* ------------------------------------------------------------------------- */ static inline int getnbyte (unsigned char *pnt, int nbytes) \ { unsigned int result = 0; int i; #ifdef DEBUG assert (nbytes < 5); #endif for (i = 0; i < nbytes; i++) result = (result << 8) | (pnt[i] & 0xff); return result; } /* ------------------------------------------------------------------------- */ static inline void putnbyte (unsigned char *pnt, unsigned int value, unsigned int nbytes) \ { int i; #ifdef DEBUG assert (nbytes < 5); #endif for (i = nbytes - 1; i >= 0; i--) \ { pnt[i] = value & 0xff; value = value >> 8; } } /* ==================================================================== */ /* Not all of these are defined in scsi.h, so we'll make sure * we agree about them here... */ #undef WRITE_BUFFER /* correct size write_buffer for scanner */ #define TEST_UNIT_READY 0x00 #define REQUEST_SENSE 0x03 #define INQUIRY 0x12 #define MODE_SELECT 0x15 #define RESERVE_UNIT 0x16 #define RELEASE_UNIT 0x17 #define MODE_SENSE 0x1a #define SCAN 0x1b #define SEND_DIAGNOSTIC 0x1d #define SET_WINDOW 0x24 #define GET_WINDOW 0x25 #define READ 0x28 #define SEND 0x2a #define OBJECT_POSITION 0x31 #define WRITE_BUFFER 0x3b #define READ_BUFFER 0x3c #define SABORT 0xc0 #define COMMAND_C1 0xc1 #define AUTO_FOCUS 0xc2 #define UNIT_MOVE 0xe0 /* ==================================================================== */ /* wdb_len if nothing is set by inquiry */ #define STD_WDB_LEN 0x28 /* ==================================================================== */ /* SCSI commands */ typedef struct { unsigned char *cmd; int size; } scsiblk; /* ==================================================================== */ #define set_inquiry_return_size(icb,val) icb[0x04]=val static unsigned char inquiryC[] = {INQUIRY, 0x00, 0x00, 0x00, 0x1f, 0x00}; static scsiblk inquiry = {inquiryC, sizeof (inquiryC)}; #define get_inquiry_periph_qual(in) getbitfield(in, 0x07, 5) #define IN_periph_qual_lun 0x00 #define IN_periph_qual_nolun 0x03 #define get_inquiry_periph_devtype(in) getbitfield(in, 0x1f, 0) #define IN_periph_devtype_scanner 0x06 #define IN_periph_devtype_unknown 0x1f #define get_inquiry_response_format(in) getbitfield(in + 0x03, 0x07, 0) #define IN_recognized 0x02 #define get_inquiry_additional_length(in) in[0x04] #define get_inquiry_length(in) in[0x03] #define set_inquiry_length(out,n) out[0x04]=n-5 #define get_inquiry_vendor(in, buf) strncpy(buf, in + 0x08, 0x08) #define get_inquiry_product(in, buf) strncpy(buf, in + 0x10, 0x010) #define get_inquiry_version(in, buf) strncpy(buf, in + 0x20, 0x04) /* ==================================================================== */ /* static unsigned char mode_selectC[] = { MODE_SELECT, 0x10, 0x00, 0x00, 0x00, 0x00 static scsiblk mode_select = { mode_selectC,sizeof(mode_selectC) }; */ /* ==================================================================== */ static unsigned char test_unit_readyC[] = { TEST_UNIT_READY, 0x00, 0x00, 0x00, 0x00, 0x00 }; static scsiblk test_unit_ready = {test_unit_readyC, sizeof (test_unit_readyC)}; /* ==================================================================== */ static unsigned char reserve_unitC[] = { RESERVE_UNIT, 0x00, 0x00, 0x00, 0x00, 0x00 }; static scsiblk reserve_unit = {reserve_unitC, sizeof (reserve_unitC)}; /* ==================================================================== */ static unsigned char release_unitC[] = { RELEASE_UNIT, 0x00, 0x00, 0x00, 0x00, 0x00 }; static scsiblk release_unit = {release_unitC, sizeof (release_unitC)}; /* ==================================================================== */ static unsigned char mode_senseC[] = { MODE_SENSE, 0x18, 0x03, 0x00, 0x00, 0x00, /* PF set, page type 03 */ }; #define set_MS_DBD(b, val) setbitfield(b, 0x01, 3, (val?1:0)) #define set_MS_len(b, val) putnbyte(b+0x04, val, 1) #define get_MS_MUD(b) getnbyte(b+(0x04+((int)*(b+0x3)))+0x4,2) static scsiblk mode_sense = {mode_senseC, sizeof (mode_senseC)}; /* ==================================================================== */ static unsigned char set_windowC[] = { SET_WINDOW, 0x00, /* opcode, lun */ 0x00, 0x00, 0x00, 0x00, /* reserved */ 0x00, 0x00, 0x00, /* transfer length; needs to be set */ 0x00, /* control byte */ }; #define set_SW_xferlen(sb, len) putnbyte(sb + 0x06, len, 3) static scsiblk set_window = {set_windowC, sizeof (set_windowC)}; /* ==================================================================== */ static unsigned char get_windowC[] = { GET_WINDOW, 0x01, /* opcode, lun, misc (should be 0x01? */ 0x00, 0x00, 0x00, /* reserved */ 0x00, /* Window identifier */ 0x00, 0x00, 0x00, /* transfer length; needs to be get */ 0x00, /* control byte */ }; #define set_GW_xferlen(sb, len) putnbyte(sb + 0x06, len, 3) #define set_WindowID_wid(sb, val) sb[5] = val static scsiblk get_window = {get_windowC, sizeof (get_windowC)}; /* ==================================================================== */ /* We use the same structure for both SET WINDOW and GET WINDOW. */ static unsigned char window_parameter_data_blockC[] = { 0x00, 0x00, /* window data length, in case of get, or 0 in case of set */ 0x00, 0x00, 0x00, 0x00, /* reserved */ 0x00, 0x00, /* Window Descriptor Length, value must be set by SHORT_WDB define */ }; #define set_WPDB_wdblen(sb, len) putnbyte(sb + 0x06, len, 2) static scsiblk window_parameter_data_block = { window_parameter_data_blockC, sizeof (window_parameter_data_blockC) }; /* ==================================================================== */ static unsigned char window_descriptor_blockC[] = { /* Any field marked with 'R' (e.g. R0x55) indicate a field provided for * development. In normal operation, 0 is set here. If any other value is set, * operationis not guaranteed! */ #define max_WDB_size 0xff #define used_WDB_size 0x75 0x00, /* 0x00 */ /* Window Identifier */ #define set_WD_wid(sb, val) sb[0] = val #define WD_wid_all 0x00 /* Only one supported */ 0x00, /* 0x01 */ /* reserved, AUTO */ #define set_WD_auto(sb, val) setbitfield(sb + 0x01, 1, 0, val) #define get_WD_auto(sb) getbitfield(sb + 0x01, 1, 0) 0x00, 0x00, /* 0x02 */ /* X Resolution in dpi */ #define set_WD_Xres(sb, val) putnbyte(sb + 0x02, val, 2) #define get_WD_Xres(sb) getnbyte(sb + 0x02, 2) 0x00, 0x00, /* 0x04 */ /* Y Resolution in dpi */ #define set_WD_Yres(sb, val) putnbyte(sb + 0x04, val, 2) #define get_WD_Yres(sb) getnbyte(sb + 0x04, 2) 0x00, 0x00, 0x00, 0x00, /* 0x06 */ /* Upper Left X in 1200|2700pt/inch */ #define set_WD_ULX(sb, val) putnbyte(sb + 0x06, val, 4) #define get_WD_ULX(sb) getnbyte(sb + 0x06, 4) 0x00, 0x00, 0x00, 0x00, /* 0x0a */ /* Upper Left Y in 1200|2700pt/inch */ #define set_WD_ULY(sb, val) putnbyte(sb + 0x0a, val, 4) #define get_WD_ULY(sb) getnbyte(sb + 0x0a, 4) 0x00, 0x00, 0x00, 0x00, /* 0x0e */ /* Width 1200pt/inch */ #define set_WD_width(sb, val) putnbyte(sb + 0x0e, val, 4) #define get_WD_width(sb) getnbyte(sb + 0x0e, 4) 0x00, 0x00, 0x00, 0x00, /* 0x12 */ /* Length 1200pt/inch */ #define set_WD_length(sb, val) putnbyte(sb + 0x12, val, 4) #define get_WD_length(sb) getnbyte(sb + 0x12, 4) 0x00, /* 0x16 */ /* Brightness */ #define set_WD_brightness(sb, val) sb[0x16] = val #define get_WD_brightness(sb) sb[0x16] 0x00, /* 0x17 */ /* Reserved */ 0x00, /* 0x18 */ /* Contrast */ #define set_WD_contrast(sb, val) sb[0x18] = val #define get_WD_contrast(sb) sb[0x18] 0x05, /* 0x19 */ /* Image Mode */ #define set_WD_composition(sb, val) sb[0x19] = val #define get_WD_composition(sb) sb[0x19] #define WD_comp_grey 0x02 #define WD_comp_gray 0x02 #define WD_comp_rgb_full 0x05 0x08, /* 0x1a */ /* Bits/Pixel */ #define set_WD_bitsperpixel(sb, val) sb[0x1a] = val #define get_WD_bitsperpixel(sb) sb[0x1a] #define WD_bits_8 0x08 0, 0, 0, 0, 0, /* 0x1b */ /* Reserved */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28 */ /* X-axis pixel count (1-2592 */ #define get_WD_xpixels(sb) getnbyte(sb + 0x28, 4) 0, 0, 0, 0, /* 0x2c */ /* Y-axis pixel count (1-3888) */ #define get_WD_ypixels(sb) getnbyte(sb + 0x2c, 4) 0x01, /* 0x30 */ /* Reserved, negative/positive * reserved, drop-out color * (Green) */ #define set_WD_negative(sb, val) setbitfield(sb + 0x30, 0x1, 4, (val?1:0)) #define get_WD_negative(sb) getbitfield(sb + 0x30, 0x1, 4) #define WD_Negative 0x01 #define WD_Positive 0x00 #define set_WD_dropoutcolor(sb, val) setbitfield(sb + 0x30, 0x3, 0, val) #define get_WD_dropoutcolor(sb) getbitfield(sb + 0x30, 0x3, 0) #define WD_Dropout_Red 0x00 #define WD_Dropout_Green 0x01 #define WD_Dropout_Blue 0x02 0x00, /* 0x31 */ /* scan mode */ #define set_WD_scanmode(sb, val) setbitfield(sb + 0x31, 0x3, 4, val) #define get_WD_scanmode(sb) getbitfield(sb + 0x31, 0x3, 4) #define WD_Scan 0x00 #define WD_Prescan 0x01 0x40, /* 0x32 */ /* Data transfer mode */ #define set_WD_transfermode(sb, val) setbitfield(sb + 0x32, 0x3, 6, val) #define get_WD_transfermode(sb) getbitfield(sb + 0x32, 0x3, 6) #define WD_LineSequence 0x2 #define WD_DotSequence 0x1 0x02, /* 0x33 */ /* Gamma selection */ #define set_WD_gammaselection(sb, val) putnbyte(sb + 0x33, val, 1) #define get_WD_gammaselection(sb) getnbyte(sb + 0x33, 1) #define WD_Linear 0x2 #define WD_Monitor 0x3 0, /* 0x34 */ /* Reserved */ 0x40, /* 0x35 */ /* Reserved, shading, analog gamma, averaging */ #define set_WD_shading(sb, val) setbitfield(sb + 0x35, 0x1, 6, val) #define get_WD_shading(sb) getbitfield(sb + 0x35, 0x1, 6) #define WD_Shading_ON 0x0 #define WD_Shading_OFF 0x1 #define set_WD_analog_gamma_R(sb, val) setbitfield(sb + 0x35, 0x1, 5, val) #define set_WD_analog_gamma_G(sb, val) setbitfield(sb + 0x35, 0x1, 4, val) #define set_WD_analog_gamma_B(sb, val) setbitfield(sb + 0x35, 0x1, 3, val) #define get_WD_analog_gamma_R(sb) getbitfield(sb + 0x35, 0x1, 5) #define get_WD_analog_gamma_G(sb) getbitfield(sb + 0x35, 0x1, 4) #define get_WD_analog_gamma_B(sb) getbitfield(sb + 0x35, 0x1, 3) #define WD_Analog_Gamma_ON 0x0 #define WD_Analog_Gamma_OFF 0x1 #define set_WD_averaging(sb, val) setbitfield(sb + 0x35, 0x7, 0, (val?7:0)) #define get_WD_averaging(sb) getbitfield(sb + 0x35, 0x7, 0) #define WD_Averaging_ON 0x0 #define WD_Averaging_OFF 0x1 0, /* 0x36 */ /* reserved */ 0, /* 0x37 */ /* R brightness */ #define set_WD_brightness_R(b, val) putnbyte(b + 0x37, val, 1) #define get_WD_brightness_R(b) getnbyte(b + 0x37, 1) 0, /* 0x38 */ /* G brightness */ #define set_WD_brightness_G(b, val) putnbyte(b + 0x38, val, 1) #define get_WD_brightness_G(b) getnbyte(b + 0x38, 1) 0, /* 0x39 */ /* B brightness */ #define set_WD_brightness_B(b, val) putnbyte(b + 0x39, val, 1) #define get_WD_brightness_B(b) getnbyte(b + 0x39, 1) 0, /* 0x3a */ /* R contrast */ #define set_WD_contrast_R(b, val) putnbyte(b + 0x3a, val, 1) #define get_WD_contrast_R(b) getnbyte(b + 0x3a, 1) 0, /* 0x3b */ /* G contrast */ #define set_WD_contrast_G(b, val) putnbyte(b + 0x3b, val, 1) #define get_WD_contrast_G(b) getnbyte(b + 0x3b, 1) 0, /* 0x3c */ /* B contrast */ #define set_WD_contrast_B(b, val) putnbyte(b + 0x3c, val, 1) #define get_WD_contrast_B(b) getnbyte(b + 0x3c, 1) 0, 0, 0, 0, /* 0x3d */ /* Reserved */ 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x49 */ /* R exposure time adjustment [0, 12-200] */ #define set_WD_exposure_R(b, val) putnbyte(b + 0x49, val, 1) #define get_WD_exposure_R(b) getnbyte(b + 0x49, 1) 0, /* 0x4a */ /* G exposure time adjustment [0, 12-200] */ #define set_WD_exposure_G(b, val) putnbyte(b + 0x4a, val, 1) #define get_WD_exposure_G(b) getnbyte(b + 0x4a, 1) 0, /* 0x4b */ /* B exposure time adjustment [0, 12-200] */ #define set_WD_exposure_B(b, val) putnbyte(b + 0x4b, val, 1) #define get_WD_exposure_B(b) getnbyte(b + 0x4b, 1) 0, 0, 0, 0, /* 0x4c */ /* Reserved */ 0, 0, 0, /* 0x52 */ /* Amount of R shift [0, 128+-15] */ #define set_WD_shift_R(b, val) putnbyte(b + 0x52, val, 1) #define get_WD_shift_R(b) getnbyte(b + 0x52, 1) 0, /* 0x53 */ /* Amount of G shift [0, 128+-15] */ #define set_WD_shift_G(b, val) putnbyte(b + 0x53, val, 1) #define get_WD_shift_G(b) getnbyte(b + 0x53, 1) 0, /* 0x54 */ /* Amount of B shift [0, 128+-15] */ #define set_WD_shift_B(b, val) putnbyte(b + 0x54, val, 1) #define get_WD_shift_B(b) getnbyte(b + 0x54, 1) 0, /* R0x55 */ /* Amount of R offset [0-255] */ 0, /* R0x56 */ /* Amount of G offset [0-255] */ 0, /* R0x57 */ /* Amount of B offset [0-255] */ 0, 0, /* 0x58 */ /* Maximum resolution (for GET WINDOW: [2700]) */ #define get_WD_maxres(b) getnbyte(b + 0x58, 2) 0, 0, /* 0x5a */ /* Reserved */ 0, /* 0x5c */ /* LUT-R, LUT-G */ #define set_WD_LUT_R(b, val) setbitfield(b + 0x5c, 0x0f, 4, val) #define set_WD_LUT_G(b, val) setbitfield(b + 0x5c, 0x0f, 0, val) #define get_WD_LUT_R(b) getbitfield(b + 0x5c, 0x0f, 4) #define get_WD_LUT_G(b) getbitfield(b + 0x5c, 0x0f, 0) 0, /* 0x5d */ /* LUT-B, reserved */ #define set_WD_LUT_B(b, val) setbitfield(b + 0x5d, 0x0f, 4, val) #define get_WD_LUT_B(b) getbitfield(b + 0x5d, 0x0f, 4) 0, /* R0x5e */ /* LS-1000: reserved. LS-20: R B/W reference point */ 0, /* R0x5f */ /* LS-1000: reserved. LS-20: G B/W reference point */ 0, /* R0x60 */ /* LS-1000: reserved. LS-20: B B/W reference point */ 0, /* R0x61 */ /* R exposure time unit [0-7] (LS-1000); [0, 2-1] (LS-20) */ 0, /* R0x62 */ /* G exposure time unit [0-7] (LS-1000); [0, 2-1] (LS-20) */ 0, /* R0x63 */ /* B exposure time unit [0-7] (LS-1000); [0, 2-1] (LS-20) */ 0, /* 0x64 */ /* Reserved */ 0, /* 0x65 */ /* Reserved, stop */ #define set_WD_stop(b, val) setbitfield(b+0x65, 0x01, 0, val) #define get_WD_stop(b) getbitfield(b+0x65, 0x01, 0) 0, /* R0x66 */ /* R gain [0-4] (LS-1000), [0-255] (LS-20) */ 0, /* R0x67 */ /* G gain [0-4] (LS-1000), [0-255] (LS-20) */ 0, /* R0x68 */ /* B gain [0-4] (LS-1000), [0-255] (LS-20) */ 0, 0, 0, 0, /* R0x69 */ /* R exposure time variable [0, 64-65535] */ 0, 0, 0, 0, /* R0x6d */ /* G exposure time variable [0, 64-65535] */ 0, 0, 0, 0, /* R0x71 */ /* B exposure time variable [0, 64-65535] */ /* 0x75 (last) */ }; static scsiblk window_descriptor_block = { window_descriptor_blockC, sizeof (window_descriptor_blockC) }; /* LS-30 has different window-descriptor ! */ static unsigned char window_descriptor_blockC_LS30[] = { #define used_WDB_size_LS30 0x32 0x00, /* 0x00 */ /* Window Identifier */ #define WD_wid_0 0x00 /* Only one supported */ #define WD_wid_1 0x01 #define WD_wid_2 0x02 #define WD_wid_3 0x03 #define WD_wid_4 0x04 #define WD_wid_9 0x09 0x00, /* reserved, AUTO */ 0x00, 0x00, /* 0x02 */ /* X Resolution in dpi */ 0x00, 0x00, /* 0x04 */ /* Y Resolution in dpi */ 0x00, 0x00, 0x00, 0x00, /* 0x06 */ /* Upper Left X in 2700pt/inch */ 0x00, 0x00, 0x00, 0x00, /* 0x0a */ /* Upper Left Y in 2700pt/inch */ 0x00, 0x00, 0x00, 0x00, /* 0x0e */ /* Width 1200pt/inch */ 0x00, 0x00, 0x00, 0x00, /* 0x12 */ /* Length 1200pt/inch */ 0x00, /* 0x16 */ /* Brightness */ 0x00, /* 0x17 */ /* Reserved */ 0x00, /* 0x18 */ /* Contrast */ 0x05, /* 0x19 */ /* Image Mode */ 0x08, /* 0x1a */ /* Bits/Pixel */ #define WD_bits_10 0x0a 0, 0, 0, 0, 0, /* 0x1b */ /* Reserved */ 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 */ 0, /* 0x29 */ /* Negative/positive prevue/scan */ #define set_WD_negative_LS30(sb, val) setbitfield(sb + 0x29, 0x1, 0, (val?0:1)) #define get_WD_negative_LS30(sb) getbitfield(sb + 0x29, 0x1, 0) /* scan mode */ #define set_WD_scanmode_LS30(sb, val) setbitfield(sb + 0x29, 0x3, 0, val) #define get_WD_scanmode_LS30(sb) getbitfield(sb + 0x29, 0x3, 0) 0x04, /* 0x2a */ 0x02, /* 0x2b */ 0x01, /* 0x2c */ 0xff, /* 0x2d */ 0,0, /* 0x2e */ 0,0, /* 0x30 */ #define set_gain_LS30(sb, val) putnbyte(sb + 0x2e, val, 4) #define get_gain_LS30(sb) getnbyte(sb + 0x2e, 4) }; static scsiblk window_descriptor_block_LS30 = { window_descriptor_blockC, sizeof (window_descriptor_blockC_LS30) }; /* ==================================================================== */ /*#define set_WDB_length(length) (window_descriptor_block.size = (length)) */ #define WPDB_OFF(b) (b + set_window.size) #define WDB_OFF(b, n) (b + set_window.size + \ window_parameter_data_block.size + \ ( window_descriptor_block.size * (n - 1) ) ) #define set_WPDB_wdbnum(sb,n) set_WPDB_wdblen(sb,window_descriptor_block.size*n) /* ==================================================================== */ static unsigned char scanC[] = { SCAN, 0x00, 0x00, 0x00, 0x00, 0x00 }; static scsiblk scan = {scanC, sizeof (scanC)}; #define set_SC_xfer_length(sb, val) sb[0x04] = (unsigned char)val /* ==================================================================== */ /* static unsigned char send_diagnosticC[] = { SEND_DIAGNOSTIC, 0x04, 0x00, 0x00, 0x00, 0x00 }; static scsiblk send_diagnostic = { send_diagnosticC, sizeof(send_diagnosticC) }; */ /* ==================================================================== */ /* sread instead of read because read is a libc primitive */ static unsigned char sreadC[] = { READ, 0x00, 0x00, /* Data Type Code */ 0x00, /* reserved */ 0x00, 0x00, /* data type qualifier */ 0x00, 0x00, 0x00, /* transfer length */ 0x00 /* control */ }; static scsiblk sread = {sreadC, sizeof (sreadC)}; #define set_R_data1_code(sb, val) sb[0x01] = val #define set_R_datatype_code(sb, val) sb[0x02] = val #define R_datatype_imagedata 0x00 #define R_EX_datatype_LUT 0x01 /* Experiment code */ #define R_image_positions 0x88 #define R_EX_datatype_shading_data 0xa0 /* Experiment code */ #define R_user_reg_gamma 0xc0 #define R_device_internal_info 0xe0 #define set_R_datatype_qual_upper(sb, val) sb[0x04] = val #define set_R_datatype_qual_lower(sb, val) sb[0x05] = val #define R_DQ_none 0x00 #define R_DQ_Rcomp 0x06 #define R_DQ_Gcomp 0x07 #define R_DQ_Bcomp 0x08 #define R_DQ_Reg1 0x01 #define R_DQ_Reg2 0x02 #define R_DQ_Reg3 0x03 #define set_R_xfer_length(sb, val) putnbyte(sb + 0x06, val, 3) /* ==================================================================== */ /* Length of internal info structure */ #define DI_length 256 /* Functions for picking out data from the internal info structure */ #define get_DI_ADbits(b) getnbyte(b + 0x00, 1) #define get_DI_Outputbits(b) getnbyte(b + 0x01, 1) #define get_DI_MaxResolution(b) getnbyte(b + 0x02, 2) #define get_DI_Xmax(b) getnbyte(b + 0x04, 2) #define get_DI_Ymax(b) getnbyte(b + 0x06, 2) #define get_DI_Xmaxpixel(b) getnbyte(b + 0x08, 2) #define get_DI_Ymaxpixel(b) getnbyte(b + 0x0a, 2) #define get_DI_currentY(b) getnbyte(b + 0x10, 2) #define get_DI_currentFocus(b) getnbyte(b + 0x12, 2) #define get_DI_currentscanpitch(b) getnbyte(b + 0x14, 1) #define get_DI_autofeeder(b) getnbyte(b + 0x1e, 1) #define get_DI_analoggamma(b) getnbyte(b + 0x1f, 1) #define get_DI_deviceerror0(b) getnbyte(b + 0x40, 1) #define get_DI_deviceerror1(b) getnbyte(b + 0x41, 1) #define get_DI_deviceerror2(b) getnbyte(b + 0x42, 1) #define get_DI_deviceerror3(b) getnbyte(b + 0x43, 1) #define get_DI_deviceerror4(b) getnbyte(b + 0x44, 1) #define get_DI_deviceerror5(b) getnbyte(b + 0x45, 1) #define get_DI_deviceerror6(b) getnbyte(b + 0x46, 1) #define get_DI_deviceerror7(b) getnbyte(b + 0x47, 1) #define get_DI_WBETR_R(b) getnbyte(b + 0x80, 2) /* White balance exposure time variable R */ #define get_DI_WBETR_G(b) getnbyte(b + 0x82, 2) #define get_DI_WBETR_B(b) getnbyte(b + 0x84, 2) #define get_DI_PRETV_R(b) getnbyte(b + 0x88, 2) /* Prescan result exposure tim4e variable R */ #define get_DI_PRETV_G(b) getnbyte(b + 0x8a, 2) #define get_DI_PRETV_B(b) getnbyte(b + 0x8c, 2) #define get_DI_CETV_R(b) getnbyte(b + 0x90, 2) /* Current exposure time variable R */ #define get_DI_CETV_G(b) getnbyte(b + 0x92, 2) #define get_DI_CETV_B(b) getnbyte(b + 0x94, 2) #define get_DI_IETU_R(b) getnbyte(b + 0x98, 1) /* Internal exposure time unit R */ #define get_DI_IETU_G(b) getnbyte(b + 0x99, 1) #define get_DI_IETU_B(b) getnbyte(b + 0x9a, 1) #define get_DI_limitcondition(b) getnbyte(b + 0xa0, 1) #define get_DI_offsetdata_R(b) getnbyte(b + 0xa1, 1) #define get_DI_offsetdata_G(b) getnbyte(b + 0xa2, 1) #define get_DI_offsetdata_B(b) getnbyte(b + 0xa3, 1) #define get_DI_poweron_errors(b,to) memcpy(to, (b + 0xa8), 8) /* ==================================================================== */ static unsigned char sendC[] = { SEND, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static scsiblk send = {sendC, sizeof (sendC)}; #define set_S_datatype_code(sb, val) sb[0x02] = (unsigned char)val #define S_datatype_imagedatai 0x00 #define S_EX_datatype_LUT 0x01 /* Experiment code */ #define S_EX_datatype_shading_data 0xa0 /* Experiment code */ #define S_user_reg_gamma 0xc0 #define S_device_internal_info 0x03 #define set_S_datatype_qual_upper(sb, val) sb[0x04] = (unsigned char)val #define S_DQ_none 0x00 #define S_DQ_Rcomp 0x06 #define S_DQ_Gcomp 0x07 #define S_DQ_Bcomp 0x08 #define S_DQ_Reg1 0x01 #define S_DQ_Reg2 0x02 #define S_DQ_Reg3 0x03 #define S_DQ_Reg9 0x09 #define set_S_xfer_length(sb, val) putnbyte(sb + 0x06, val, 3) /* static unsigned char gamma_user_LUT_LS1K[512] = { 0x00 }; static scsiblk gamma_user_LUT_LS1K_LS1K = { gamma_user_LUT_LS1K, sizeof(gamma_user_LUT_LS1K) }; */ /* ==================================================================== */ static unsigned char object_positionC[] = { OBJECT_POSITION, 0x00, /* Auto feeder function */ 0x00, 0x00, 0x00, /* Count */ 0x00, 0x00, 0x00, 0x00, /* Reserved */ 0x00 /* Control byte */ }; #define set_OP_autofeed(b,val) setbitfield(b+0x01, 0x07, 0, val) #define OP_Discharge 0x00 #define OP_Feed 0x01 #define OP_Absolute 0x02 /* For development only */ static scsiblk object_position = { object_positionC, sizeof (object_positionC) }; /* ==================================================================== */ static unsigned char autofocusC[] = { AUTO_FOCUS, 0x00, 0x00, 0x00, 0x00, /* transfer length (0|8) */ 0x00 /* Control byte */ }; #define set_AF_transferlength(b, val) b[0x04] = (unsigned char)val #define get_AF_transferlength(b) ((int)b[0x04] & 0xff) #define set_AF_XPoint(b, val) putnbyte(b+0x06, val, 4) #define set_AF_YPoint(b, val) putnbyte(b+0x0a, val, 4) #define AF_Point_length 8 static scsiblk autofocus = {autofocusC, sizeof (autofocusC)}; /* ==================================================================== */ static unsigned char command_c1_C[] = { 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* */ 0x00, 0x00 /* transfer length*/ }; static scsiblk command_c1 = {command_c1_C, sizeof (command_c1_C)}; /* ==================================================================== */ static unsigned char autofocusLS30C[] = { 0xe0, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, /* */ 0x09, 0x00 /* transfer length*/ }; static unsigned char autofocuspos[] = { 0x00, 0x00, 0x00, 0x05, 0x10, /* x-position */ 0x00, 0x00, 0x07, 0x9b, /* y-position */ }; #define set_AF_transferlength(b, val) b[0x04] = (unsigned char)val #define get_AF_transferlength(b) ((int)b[0x04] & 0xff) #define set_AF_XPoint(b, val) putnbyte(b+0x06, val, 4) #define set_AF_YPoint(b, val) putnbyte(b+0x0a, val, 4) #define AF_Point_length 8 static scsiblk autofocusLS30 = {autofocusLS30C, sizeof (autofocusLS30C)}; /* ==================================================================== */ static unsigned char commande1C[] = { 0xe1, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, /* */ 0x0d, 0x00 /* transfer length*/ }; static scsiblk commande1 = {commande1C, sizeof (commande1C)}; /* ==================================================================== */ /* static unsigned char request_senseC[] = { REQUEST_SENSE, 0x00, 0x00, 0x00, 0x00, 0x00 #define set_RS_allocation_length(sb,val) sb[0x04] = (unsigned char)val }; static scsiblk request_sense = { request_senseC, sizeof (request_senseC) }; */ /* defines for request sense return block */ #define get_RS_information_valid(b) getbitfield(b + 0x00, 1, 7) #define get_RS_error_code(b) getbitfield(b + 0x00, 0x7f, 0) #define get_RS_filemark(b) getbitfield(b + 0x02, 1, 7) #define get_RS_EOM(b) getbitfield(b + 0x02, 1, 6) #define get_RS_ILI(b) getbitfield(b + 0x02, 1, 5) #define get_RS_sense_key(b) getbitfield(b + 0x02, 0x0f, 0) #define get_RS_information(b) getnbyte(b+0x03, 4) /* normally 0 */ #define get_RS_additional_length(b) b[0x07] /* always 10 */ #define get_RS_ASC(b) b[0x0c] #define get_RS_ASCQ(b) b[0x0d] #define get_RS_SKSV(b) getbitfield(b+0x0f,1,7) /* valid, always 0 */ #define rs_return_block_size 18 /* Says Nikon */ #endif backends-1.3.0/backend/coolscan.c000066400000000000000000003465551456256263500166610ustar00rootroot00000000000000/* ------------------------------------------------------------------------- */ /* sane - Scanner Access Now Easy. coolscan.c , version 0.4.4 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, see . 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 COOLSCAN flatbed scanners. */ /* ------------------------------------------------------------------------- */ /* SANE-FLOW-DIAGRAMM - 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 */ #ifdef _AIX # include "lalloca.h" /* MUST come first for AIX! */ #endif #include "../include/sane/config.h" #include "lalloca.h" #include #include #include #include #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_scsi.h" #include "../include/sane/sanei_debug.h" #include "../include/sane/sanei_thread.h" #include "../include/sane/sanei_config.h" #define COOLSCAN_CONFIG_FILE "coolscan.conf" #include "../include/sane/sanei_backend.h" #include "coolscan.h" #include "coolscan-scsidef.h" #ifndef PATH_MAX #define PATH_MAX 1024 #endif /* ------------------------------------------------------------------------- */ static const SANE_Int resolution_list[] = { 25, 2700, 1350, 900, 675, 540, 450, 385, 337, 300, 270, 245, 225, 207, 192, 180, 168, 158, 150, 142, 135, 128, 122, 117, 112, 108 }; #define coolscan_do_scsi_open(dev, fd, handler) sanei_scsi_open(dev, fd, handler) #define coolscan_do_scsi_close(fd) sanei_scsi_close(fd) #define COOLSCAN_MAX_RETRY 25 static SANE_Status sense_handler (int scsi_fd, unsigned char * result, void *arg); static int coolscan_check_values (Coolscan_t * s); static int get_internal_info (Coolscan_t *); static void coolscan_get_inquiry_values (Coolscan_t *); static void hexdump (int level, char *comment, unsigned char *p, int l); static int swap_res (Coolscan_t * s); /* --------------------------- COOLSCAN_DO_SCSI_CMD ----------------------- */ static int do_scsi_cmd (int fd, unsigned char *cmd, int cmd_len, unsigned char *out, size_t out_len) { int ret; size_t ol = out_len; hexdump (20, "", cmd, cmd_len); ret = sanei_scsi_cmd (fd, cmd, cmd_len, out, &ol); if ((out_len != 0) && (out_len != ol)) { DBG (1, "sanei_scsi_cmd: asked %lu bytes, got %lu\n", (u_long) out_len, (u_long) ol); } if (ret) { DBG (1, "sanei_scsi_cmd: returning 0x%08x\n", ret); } DBG (10, "sanei_scsi_cmd: returning %lu bytes:\n", (u_long) ol); if (out != NULL && out_len != 0) hexdump (15, "", out, (out_len > 0x60) ? 0x60 : out_len); return ret; } static int request_sense_parse (unsigned char *sensed_data) { int ret, sense, asc, ascq; sense = get_RS_sense_key (sensed_data); asc = get_RS_ASC (sensed_data); ascq = get_RS_ASCQ (sensed_data); ret = SANE_STATUS_IO_ERROR; switch (sense) { case 0x0: DBG (5, "\t%d/%d/%d: Scanner ready\n", sense, asc, ascq); return SANE_STATUS_GOOD; case 0x1: if ((0x37 == asc) && (0x00 == ascq)) { DBG (1, "\t%d/%d/%d: Rounded Parameter\n", sense, asc, ascq); ret = SANE_STATUS_GOOD; } else if ((0x61 == asc) && (0x02 == ascq)) DBG (1, "\t%d/%d/%d: Out Of Focus\n", sense, asc, ascq); else DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq); break; case 0x2: if ((0x4 == asc) && (0x1 == ascq)) { DBG (10, "\t%d/%d/%d: Logical unit is in process of becoming ready\n", sense, asc, ascq); ret = SANE_STATUS_DEVICE_BUSY; } else if ((0x3A == asc) && (0x00 == ascq)) { DBG (1, "\t%d/%d/%d: No Diapo inserted\n", sense, asc, ascq); ret = SANE_STATUS_GOOD; } else if ((0x60 == asc) && (0x00 == ascq)) DBG (1, "\t%d/%d/%d: Lamp Failure\n", sense, asc, ascq); else { DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq); ret = SANE_STATUS_GOOD; } break; case 0x3: if ((0x3b == asc) && (0xe == ascq)) DBG (1, "\t%d/%d/%d: Medium source element empty\n", sense, asc, ascq); else if ((0x53 == asc) && (0x00 == ascq)) DBG (1, "\t%d/%d/%d: Media Load of Eject Failed\n", sense, asc, ascq); else DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq); break; case 0x4: if ((0x15 == asc) && (0x1 == ascq)) DBG (1, "\t%d/%d/%d: Mechanical Positioning Error\n", sense, asc, ascq); else DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq); break; case 0x5: if ((0x00 == asc) && (0x5 == ascq)) DBG (1, "\t%d/%d/%d: End-Of-Data Detected\n", sense, asc, ascq); else if ((0x1a == asc) && (0x00 == ascq)) DBG (1, "\t%d/%d/%d: Parameter List Length Error\n", sense, asc, ascq); else if ((0x20 == asc) && (0x00 == ascq)) DBG (1, "\t%d/%d/%d: Invalid Command Operation Code\n", sense, asc, ascq); else if ((0x24 == asc) && (0x00 == ascq)) DBG (1, "\t%d/%d/%d: Invalid Field In CDB\n", sense, asc, ascq); else if ((0x25 == asc) && (0x00 == ascq)) DBG (1, "\t%d/%d/%d: Logical Unit Not Supported\n", sense, asc, ascq); else if ((0x26 == asc) && (0x00 == ascq)) DBG (1, "\t%d/%d/%d: Invalid Field in Parameter List\n", sense, asc, ascq); else if ((0x2c == asc) && (0x00 == ascq)) DBG (1, "\t%d/%d/%d: Command Sequence Error\n", sense, asc, ascq); else if ((0x39 == asc) && (0x00 == ascq)) DBG (1, "\t%d/%d/%d: Saving Parameters Not Supported\n", sense, asc, ascq); else if ((0x3d == asc) && (0x00 == ascq)) DBG (1, "\t%d/%d/%d: Invalid Bits In Identify Message\n", sense, asc, ascq); else DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq); break; case 0x6: if ((0x29 == asc) && (0x0 == ascq)) DBG (1, "\t%d/%d/%d: Power On, Reset, or Bus Device Reset Occurred\n", sense, asc, ascq); else if ((0x2a == asc) && (0x1 == ascq)) DBG (1, "\t%d/%d/%d: Mode Parameters Changed\n", sense, asc, ascq); else DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq); break; case 0xb: if ((0x43 == asc) && (0x0 == ascq)) DBG (1, "\t%d/%d/%d: Message Error\n", sense, asc, ascq); else if ((0x47 == asc) && (0x0 == ascq)) DBG (1, "\t%d/%d/%d: SCSI Parity Error\n", sense, asc, ascq); else if ((0x48 == asc) && (0x0 == ascq)) DBG (1, "\t%d/%d/%d: Initiator Detected Error Message Received\n", sense, asc, ascq); else if ((0x49 == asc) && (0x0 == ascq)) DBG (1, "\t%d/%d/%d: Invalid Message Error\n", sense, asc, ascq); else if ((0x4e == asc) && (0x0 == ascq)) DBG (1, "\t%d/%d/%d: Overlapped Commands Attempted\n", sense, asc, ascq); else DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq); break; default: DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq); break; } /* switch */ return ret; } /* * wait_scanner should spin until TEST_UNIT_READY returns 0 (GOOD) * returns 0 on success, * returns -1 on error. */ static int wait_scanner (Coolscan_t * s) { int ret = -1; int cnt = 0; DBG (10, "wait_scanner: Testing if scanner is ready\n"); while (ret != 0) { ret = do_scsi_cmd (s->sfd, test_unit_ready.cmd, test_unit_ready.size, 0, 0); if (ret == SANE_STATUS_DEVICE_BUSY) { usleep (500000); /* wait 0.5 seconds */ if (cnt++ > 40) { /* 20 sec. max (prescan takes up to 15 sec. */ DBG (1, "wait_scanner: scanner does NOT get ready\n"); return -1; } } else if (ret == SANE_STATUS_GOOD) { DBG (10, "wait_scanner: scanner is ready\n"); return ret; } else { DBG (1, "wait_scanner: test unit ready failed (%s)\n", sane_strstatus (ret)); } } return 0; } /* ------------------------- COOLSCAN GRAB SCANNER ----------------------------- */ /* coolscan_grab_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 * * It is then responsible for installing appropriate signal handlers * to call emergency_give_scanner() if user aborts. */ static int coolscan_grab_scanner (Coolscan_t * s) { int ret; DBG (10, "grabbing scanner\n"); wait_scanner (s); /* wait for scanner ready, if not print sense and return 1 */ ret = do_scsi_cmd (s->sfd, reserve_unit.cmd, reserve_unit.size, NULL, 0); if (ret) return ret; DBG (10, "scanner reserved\n"); return 0; } /* * Convert a size in ilu to the units expected by the scanner */ static int resDivToVal (int res_div) { if (res_div < 1 || res_div > resolution_list[0]) { DBG (1, "Invalid resolution divisor %d \n", res_div); return 2700; } else { return resolution_list[res_div]; } } static int resValToDiv (int res_val) { int res_div; int max_res = resolution_list[0]; for (res_div = 1; res_div <= max_res; res_div++) { if (resolution_list[res_div] == res_val) break; } if (res_div > max_res) { DBG (1, "Invalid resolution value\n"); return 1; } else { return res_div; } } /* * use mode select to force a measurement divisor of 2700 */ static unsigned char mode_select[] = { MODE_SELECT, 0x10, 0, 0, 20, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 1, 3, 6, 0, 0, 0xA, 0x8C, 0, 0}; static int select_MUD (Coolscan_t * s) { return do_scsi_cmd (s->sfd, mode_select, 26, NULL, 0); } static int coolscan_autofocus_LS30 (Coolscan_t * s) { int x, y; wait_scanner(s); memcpy(s->buffer, autofocusLS30.cmd, autofocusLS30.size); memcpy(s->buffer+ autofocusLS30.size, autofocuspos, 9); x = s->xmaxpix - (s->brx + s->tlx) / 2; y = (s->bry + s->tly) / 2; DBG (10, "Attempting AutoFocus at x=%d, y=%d\n", x, y); do_scsi_cmd (s->sfd, s->buffer, autofocusLS30.size + 9, NULL, 0); /* Trashes when used in combination with scsi-driver AM53C974.o */ do_scsi_cmd (s->sfd, command_c1.cmd, command_c1.size, NULL, 0); DBG (10, "\tWaiting end of Autofocus\n"); wait_scanner (s); DBG (10, "AutoFocused.\n"); return 0; } static int coolscan_autofocus (Coolscan_t * s) { int x, y; if(s->LS>=2) { return coolscan_autofocus_LS30(s); } wait_scanner(s); memcpy(s->buffer, autofocus.cmd, autofocus.size); x = s->xmaxpix - (s->brx + s->tlx) / 2; y = (s->bry + s->tly) / 2; DBG (10, "Attempting AutoFocus at x=%d, y=%d\n", x, y); set_AF_XPoint (s->buffer, x); set_AF_YPoint (s->buffer, y); set_AF_transferlength (s->buffer, 0); /* should be 8 !*/ do_scsi_cmd (s->sfd, s->buffer, autofocus.size + AF_Point_length, NULL, 0); sleep(5); /* autofocus takes a minimum of 5 sec. */ DBG (10, "\tWaiting end of Autofocus\n"); wait_scanner (s); DBG (10, "AutoFocused.\n"); return 0; } /* static int coolscan_abort_scan (Coolscan_t * s) { int ret; DBG (5, "Aborting scan...\n"); ret = do_scsi_cmd (s->sfd, sabort.cmd, sabort.size, NULL, 0); if (ret) DBG (5, "Scan Aborted\n"); else DBG (5, "Not scanning\n"); return 0; } */ static int coolscan_mode_sense (Coolscan_t * s) { int ret, len; DBG (10, "Mode Sense...\n"); len = 12; set_MS_DBD (mode_sense.cmd, 1); set_MS_len (mode_sense.cmd, len); ret = do_scsi_cmd (s->sfd, mode_sense.cmd, mode_sense.size, s->buffer, len); if (ret == 0) { s->MUD = get_MS_MUD (s->buffer); DBG (10, "\tMode Sensed (MUD is %d)\n", s->MUD); } return ret; } static int coolscan_object_discharge (Coolscan_t * s) { int ret; DBG (10, "Trying to discharge object...\n"); memcpy (s->buffer, object_position.cmd, object_position.size); set_OP_autofeed (s->buffer, OP_Discharge); ret = do_scsi_cmd (s->sfd, s->buffer, object_position.size, NULL, 0); wait_scanner (s); DBG (10, "Object discharged.\n"); return ret; } static int coolscan_object_feed (Coolscan_t * s) { int ret; DBG (10, "Trying to feed object...\n"); if (!s->asf) { DBG (10, "\tAutofeeder not present.\n"); return 0; } memcpy (s->buffer, object_position.cmd, object_position.size); set_OP_autofeed (s->buffer, OP_Feed); ret = do_scsi_cmd (s->sfd, s->buffer, object_position.size, NULL, 0); wait_scanner (s); DBG (10, "Object fed.\n"); return ret; } /* coolscan_give_scanner should go through the following sequence: * OBJECT POSITION DISCHARGE * GOOD * RELEASE UNIT * GOOD */ static int coolscan_give_scanner (Coolscan_t * s) { DBG (10, "trying to release scanner ...\n"); coolscan_object_discharge (s); wait_scanner (s); do_scsi_cmd (s->sfd, release_unit.cmd, release_unit.size, NULL, 0); DBG (10, "scanner released\n"); return 0; } static int coolscan_set_window_param_LS20 (Coolscan_t * s, int prescan) { unsigned char buffer_r[max_WDB_size]; int ret; wait_scanner (s); memset (buffer_r, '\0', max_WDB_size); /* clear buffer */ memcpy (buffer_r, window_descriptor_block.cmd, window_descriptor_block.size); /* copy preset data */ set_WD_wid (buffer_r, WD_wid_all); /* window identifier */ set_WD_auto (buffer_r, s->set_auto); /* 0 or 1: don't know what it is */ set_WD_negative (buffer_r, s->negative); /* Negative/positive slide */ if (prescan) { set_WD_scanmode (buffer_r, WD_Prescan); } else { set_WD_scanmode (buffer_r, WD_Scan); /* geometry */ set_WD_Xres (buffer_r, resDivToVal (s->x_nres)); /* x resolution in dpi */ set_WD_Yres (buffer_r, resDivToVal (s->y_nres)); /* y resolution in dpi */ /* the coolscan uses the upper right corner as the origin of coordinates */ /* xmax and ymax are given in 1200 dpi */ set_WD_ULX (buffer_r, (s->xmaxpix - s->brx)); set_WD_ULY (buffer_r, s->tly); /* upper_edge y */ set_WD_width (buffer_r, (s->brx - s->tlx + 1)); set_WD_length (buffer_r, (s->bry - s->tly + 1)); /* BTC */ if (s->brightness == 128) { set_WD_brightness (buffer_r, 0); } else { set_WD_brightness (buffer_r, s->brightness); /* brightness */ } if (s->contrast == 128) { set_WD_contrast (buffer_r, 0); } else { set_WD_contrast (buffer_r, s->contrast); /* contrast */ } /* scanmode */ if (s->colormode == GREYSCALE) set_WD_composition (buffer_r, WD_comp_grey); /* GRAY composition */ else set_WD_composition (buffer_r, WD_comp_rgb_full); /* RGB composition */ set_WD_dropoutcolor (buffer_r, s->dropoutcolor); /* Which color to scan with when grayscale scan */ set_WD_transfermode (buffer_r, WD_LineSequence); set_WD_gammaselection (buffer_r, s->gammaselection); /* monitor/linear */ set_WD_shading (buffer_r, WD_Shading_ON); /* default for non-manufacturing */ if (1 == s->LS) { /* Analog gamma reserved on LS-1000 */ set_WD_analog_gamma_R (buffer_r, 0); set_WD_analog_gamma_G (buffer_r, 0); set_WD_analog_gamma_R (buffer_r, 0); } else { /* Quote spec: "It is recommended that analog gamma bits 5, 4 and 3 be * set to 1 (OFF) when the object type of byte 48 is positive and the * gamma specification of byte 51 is linear, and to 0 (ON) in all * other cases." */ /* int foo; if ((buffer_r[48] == WD_Positive) && (buffer_r[51] == WD_Linear)) foo = WD_Analog_Gamma_OFF; else foo = WD_Analog_Gamma_ON; set_WD_analog_gamma_R (buffer_r, foo); set_WD_analog_gamma_G (buffer_r, foo); set_WD_analog_gamma_B (buffer_r, foo); */ set_WD_analog_gamma_R (buffer_r, s->analog_gamma_r); set_WD_analog_gamma_G (buffer_r, s->analog_gamma_g); set_WD_analog_gamma_B (buffer_r, s->analog_gamma_b); if (s->gamma_bind) { set_WD_LUT_R (buffer_r, 1); set_WD_LUT_G (buffer_r, 1); set_WD_LUT_B (buffer_r, 1); } else { set_WD_LUT_R (buffer_r, 1); set_WD_LUT_G (buffer_r, 2); set_WD_LUT_B (buffer_r, 3); } } set_WD_averaging (buffer_r, s->averaging); set_WD_brightness_R (buffer_r, s->brightness_R); set_WD_brightness_G (buffer_r, s->brightness_G); set_WD_brightness_B (buffer_r, s->brightness_B); set_WD_contrast_R (buffer_r, s->contrast_R); set_WD_contrast_G (buffer_r, s->contrast_G); set_WD_contrast_B (buffer_r, s->contrast_B); set_WD_exposure_R (buffer_r, s->exposure_R); set_WD_exposure_G (buffer_r, s->exposure_G); set_WD_exposure_B (buffer_r, s->exposure_B); set_WD_shift_R (buffer_r, s->shift_R); set_WD_shift_G (buffer_r, s->shift_G); set_WD_shift_B (buffer_r, s->shift_B); /* FIXME: LUT-[RGB] */ /* FIXME: stop on/off */ } DBG (10, "\tx_nres=%d, y_nres=%d, upper left-x=%d, upper left-y=%d\n", s->x_nres, s->y_nres, s->tlx, s->tly); DBG (10, "\twindow width=%d, MUD=%d, brx=%d\n", s->brx - s->tlx, s->MUD, s->brx); DBG (10, "\tcolormode=%d, bits per pixel=%d\n", s->colormode, s->bits_per_color); DBG (10, "\tnegative=%d, dropoutcolor=%d, preview=%d, transfermode=%d, gammasel=%d\n", s->negative, s->dropoutcolor, s->preview, s->transfermode, s->gammaselection); /* prepare SCSI-BUFFER */ memcpy (s->buffer, set_window.cmd, set_window.size); /* SET-WINDOW cmd */ memcpy ((s->buffer + set_window.size), /* add WPDB */ window_parameter_data_block.cmd, window_parameter_data_block.size); set_WPDB_wdblen ((s->buffer + set_window.size), used_WDB_size); /* set WD_len */ memcpy (s->buffer + set_window.size + window_parameter_data_block.size, buffer_r, window_descriptor_block.size); hexdump (15, "Window set", buffer_r, s->wdb_len); set_SW_xferlen (s->buffer, (window_parameter_data_block.size + window_descriptor_block.size)); ret = do_scsi_cmd (s->sfd, s->buffer, set_window.size + window_parameter_data_block.size + window_descriptor_block.size, NULL, 0); DBG (10, "window set.\n"); return ret; } static int coolscan_set_window_param_LS30 (Coolscan_t * s, int wid, int prescan) { unsigned char buffer_r[max_WDB_size]; int ret; wait_scanner (s); memset (buffer_r, '\0', max_WDB_size); /* clear buffer */ memcpy (buffer_r, window_descriptor_block_LS30.cmd, window_descriptor_block_LS30.size); /* copy preset data */ set_WD_wid (buffer_r, wid); /* window identifier */ set_WD_auto (buffer_r, s->set_auto); /* 0 or 1: don't know what it is */ /* geometry */ set_WD_Xres (buffer_r, resDivToVal (s->x_nres)); /* x resolution in dpi */ set_WD_Yres (buffer_r, resDivToVal (s->y_nres)); /* y resolution in dpi */ if (prescan) { set_WD_scanmode_LS30 (buffer_r, WD_Prescan); set_WD_Xres (buffer_r, resDivToVal (1)); /* x res. in dpi */ set_WD_Yres (buffer_r, resDivToVal (1)); /* y res. in dpi */ buffer_r[0x29]=0x81; buffer_r[0x2a]=0x04; buffer_r[0x2b]=0x02; buffer_r[0x2c]=0x01; buffer_r[0x2d]=0xff; buffer_r[0x30]=0x00; buffer_r[0x31]=0x00; buffer_r[0x32]=0x00; buffer_r[0x33]=0x00; set_WD_width (buffer_r,(2592)); set_WD_length (buffer_r,(3894)); } else { set_WD_scanmode_LS30 (buffer_r, WD_Scan); /* the coolscan LS-30 uses the upper left corner as the origin of coordinates */ /* xmax and ymax are given in 1200 dpi */ set_WD_ULX (buffer_r, s->tlx); set_WD_ULY (buffer_r, s->tly); /* upper_edge y */ set_WD_width (buffer_r, (s->brx - s->tlx+1)); set_WD_length (buffer_r, (s->bry - s->tly+1)); /* BTC */ if (s->brightness == 128) { buffer_r[0x32]=0x00; } else { buffer_r[0x32]=s->brightness; /* brightness */ } if (s->contrast == 128) { buffer_r[0x33]=0x00; } else { buffer_r[0x33]=s->contrast; /* contrast */ } /* scanmode */ if (s->colormode == GREYSCALE) set_WD_composition (buffer_r, WD_comp_grey); /* GRAY composition */ else set_WD_composition (buffer_r, WD_comp_rgb_full); /* RGB composition */ set_WD_composition (buffer_r, WD_comp_rgb_full); /* always RGB composition */ /* Bits per pixel */ set_WD_bitsperpixel(buffer_r, s->bits_per_color); buffer_r[0x29]=0x81; buffer_r[0x2a]=0x01; buffer_r[0x2b]=0x02; buffer_r[0x2c]=0x01; buffer_r[0x2d]=0xff; buffer_r[0x30]=0x00; } set_WD_negative_LS30(buffer_r, s->negative); /* Negative/positive slide */ switch(wid) { case 1: set_gain_LS30(buffer_r,(s->exposure_R*s->pretv_r)/50); break; case 2: set_gain_LS30(buffer_r,(s->exposure_G*s->pretv_g)/50); break; case 3: set_gain_LS30(buffer_r,(s->exposure_B*s->pretv_b)/50); break; } DBG (10, "\texpo_r=%d, expo_g=%d, expob=%d\n", s->exposure_R, s->exposure_G, s->exposure_B); DBG (10, "\tpre_r=%d, pre_g=%d, preb=%d\n", s->pretv_r, s->pretv_g, s->pretv_b); DBG (10, "\tx_nres=%d, y_nres=%d, upper left-x=%d, upper left-y=%d\n", s->x_nres, s->y_nres, s->tlx, s->tly); DBG (10, "\twindow width=%d, MUD=%d, brx=%d\n", s->brx - s->tlx, s->MUD, s->brx); DBG (10, "\tcolormode=%d, bits per pixel=%d\n", s->colormode, s->bits_per_color); DBG (10, "\tnegative=%d, dropoutcolor=%d, preview=%d, transfermode=%d, gammasel=%d\n", s->negative, s->dropoutcolor, s->preview, s->transfermode, s->gammaselection); /* prepare SCSI-BUFFER */ memcpy (s->buffer, set_window.cmd, set_window.size); /* SET-WINDOW cmd */ memcpy ((s->buffer + set_window.size), /* add WPDB */ window_parameter_data_block.cmd, window_parameter_data_block.size); set_WPDB_wdblen ((s->buffer + set_window.size), used_WDB_size_LS30); /* set WD_len */ memcpy (s->buffer + set_window.size + window_parameter_data_block.size, buffer_r, window_descriptor_block_LS30.size); hexdump (15, "Window set", buffer_r, s->wdb_len); set_SW_xferlen (s->buffer, (window_parameter_data_block.size + window_descriptor_block_LS30.size)); ret = do_scsi_cmd (s->sfd, s->buffer, set_window.size + window_parameter_data_block.size + window_descriptor_block_LS30.size, NULL, 0); DBG (10, "window set.\n"); return ret; } static int coolscan_set_window_param (Coolscan_t * s, int prescan) { int ret; ret=0; DBG (10, "set_window_param\n"); if(s->LS<2) /* distinguish between old and new scanners */ { ret=coolscan_set_window_param_LS20 (s,prescan); } else { do_scsi_cmd (s->sfd,commande1.cmd,commande1.size,s->buffer,0x0d); wait_scanner (s); wait_scanner (s); coolscan_set_window_param_LS30(s,1,prescan); ret=coolscan_set_window_param_LS30(s,2,prescan); ret=coolscan_set_window_param_LS30(s,3,prescan); if(s->colormode&0x08) { ret=coolscan_set_window_param_LS30(s,9,prescan); } } return ret; } /* * The only purpose of get_window is debugging. None of the return parameters * is currently used. */ static int coolscan_get_window_param_LS30 (Coolscan_t * s, int wid,int prescanok) { int translen; unsigned char *buf; DBG (10, "GET_WINDOW_PARAM\n"); /* wait_scanner (s); */ translen = window_parameter_data_block.size + window_descriptor_block_LS30.size; /* prepare SCSI-BUFFER */ memset (s->buffer, '\0', max_WDB_size); /* clear buffer */ set_SW_xferlen (get_window.cmd, translen); /* Transfer length */ get_window.cmd[5]= wid; /* window identifier */ hexdump (15, "Get window cmd", get_window.cmd, get_window.size); do_scsi_cmd (s->sfd, get_window.cmd, get_window.size, s->buffer, translen); buf = s->buffer + window_parameter_data_block.size; hexdump (10, "Window get", buf, 117); s->brightness = buf[0x32]; /* brightness */ s->contrast = buf[0x33]; /* contrast */ DBG (10, "\tbrightness=%d, contrast=%d\n", s->brightness, s->contrast); /* Useful? */ s->bits_per_color = get_WD_bitsperpixel (buf); /* bits/pixel (8) */ DBG (10, "\tcolormode=%d, bits per pixel=%d\n", s->colormode, s->bits_per_color); if(prescanok) { switch(wid) { case 1: s->pretv_r = get_gain_LS30(buf); break; case 2: s->pretv_g = get_gain_LS30(buf); break; case 3: s->pretv_b = get_gain_LS30(buf); break; } } /* Should this one be set at all, here? */ s->transfermode = get_WD_transfermode (buf); s->gammaselection = get_WD_gammaselection (buf); /* monitor/linear */ DBG (10, "\tpre_r=%d, pre_g=%d, preb=%d\n", s->pretv_r, s->pretv_g, s->pretv_b); DBG (5, "\tnegative=%d, dropoutcolor=%d, preview=%d, transfermode=%d, gammasel=%d\n", s->negative, s->dropoutcolor, s->preview, s->transfermode, s->gammaselection); DBG (10, "get_window_param - return\n"); return 0; } /* * The only purpose of get_window is debugging. None of the return parameters * is currently used. */ static int coolscan_get_window_param_LS20 (Coolscan_t * s) { int translen; unsigned char *buf; DBG (10, "GET_WINDOW_PARAM\n"); wait_scanner (s); translen = window_parameter_data_block.size + window_descriptor_block.size; /* prepare SCSI-BUFFER */ memset (s->buffer, '\0', max_WDB_size); /* clear buffer */ set_SW_xferlen (get_window.cmd, translen); /* Transfer length */ hexdump (15, "Get window cmd", get_window.cmd, get_window.size); do_scsi_cmd (s->sfd, get_window.cmd, get_window.size, s->buffer, translen); buf = s->buffer + window_parameter_data_block.size; hexdump (10, "Window get", buf, 117); /* BTC */ s->brightness = get_WD_brightness (buf); /* brightness */ s->contrast = get_WD_contrast (buf); /* contrast */ DBG (10, "\tbrightness=%d, contrast=%d\n", s->brightness, s->contrast); if (WD_comp_gray == get_WD_composition (buf)) s->colormode = GREYSCALE; else s->colormode = RGB; /* Useful? */ s->bits_per_color = get_WD_bitsperpixel (buf); /* bits/pixel (8) */ DBG (10, "\tcolormode=%d, bits per pixel=%d\n", s->colormode, s->bits_per_color); s->dropoutcolor = get_WD_dropoutcolor (buf); /* Which color to scan with when grayscale scan */ /* Should this one be set at all, here? */ s->transfermode = get_WD_transfermode (buf); s->gammaselection = get_WD_gammaselection (buf); /* monitor/linear */ DBG (5, "\tnegative=%d, dropoutcolor=%d, preview=%d, transfermode=%d, gammasel=%d\n", s->negative, s->dropoutcolor, s->preview, s->transfermode, s->gammaselection); /* Should this one be set at all? */ s->shading = get_WD_shading (buf); s->averaging = get_WD_averaging (buf); DBG (10, "get_window_param - return\n"); return 0; } /* * The only purpose of get_window is debugging. None of the return parameters * is currently used. */ static int coolscan_get_window_param (Coolscan_t * s, int prescanok) { int ret; DBG (10, "get_window_param\n"); ret=0; if(s->LS<2) /* distinguish between old and new scanners */ { ret=coolscan_get_window_param_LS20 (s); } else { ret=coolscan_get_window_param_LS30(s,1,prescanok); ret=coolscan_get_window_param_LS30(s,2,prescanok); ret=coolscan_get_window_param_LS30(s,3,prescanok); if(s->colormode&0x08) { ret=coolscan_get_window_param_LS30(s,9,prescanok); } } return ret; } static int coolscan_start_scanLS30 (Coolscan_t * s) { int channels; DBG (10, "starting scan\n"); channels=1; memcpy (s->buffer, scan.cmd, scan.size); switch(s->colormode) { case RGB: case GREYSCALE: channels=s->buffer[4]=0x03; /* window 1 */ s->buffer[6]=0x01; /* window 1 */ s->buffer[7]=0x02; /* window 2 */ s->buffer[8]=0x03; /* window 3 */ break; case RGBI: channels=s->buffer[4]=0x04; /* window 1 */ s->buffer[6]=0x01; /* window 1 */ s->buffer[7]=0x02; /* window 2 */ s->buffer[8]=0x03; /* window 3 */ s->buffer[9]=0x09; /* window 3 */ break; case IRED: channels=s->buffer[4]=0x01; /* window 1 */ s->buffer[8]=0x09; /* window 3 */ break; } return do_scsi_cmd (s->sfd, s->buffer, scan.size+channels, NULL, 0); } static int coolscan_start_scan (Coolscan_t * s) { DBG (10, "starting scan\n"); if(s->LS>=2) { return coolscan_start_scanLS30(s); } return do_scsi_cmd (s->sfd, scan.cmd, scan.size, NULL, 0); } static int prescan (Coolscan_t * s) { int ret; DBG (10, "Starting prescan...\n"); if(s->LS<2) { coolscan_set_window_param (s, 1); } else { do_scsi_cmd (s->sfd,commande1.cmd,commande1.size,s->buffer,0x0d); wait_scanner (s); wait_scanner (s); coolscan_set_window_param_LS30 (s,1,1); coolscan_set_window_param_LS30 (s,2,1); coolscan_set_window_param_LS30 (s,3,1); } ret = coolscan_start_scan(s); sleep(8); /* prescan takes a minimum of 10 sec. */ wait_scanner (s); DBG (10, "Prescan done\n"); return ret; } static SANE_Status do_prescan_now (Coolscan_t * scanner) { DBG (10, "do_prescan_now \n"); if (scanner->scanning == SANE_TRUE) return SANE_STATUS_DEVICE_BUSY; if (scanner->sfd < 0) { /* first call */ if (sanei_scsi_open (scanner->sane.name, &(scanner->sfd), sense_handler, 0) != SANE_STATUS_GOOD) { DBG (1, "do_prescan_now: open of %s failed:\n", scanner->sane.name); return SANE_STATUS_INVAL; } } scanner->scanning = SANE_TRUE; if (coolscan_check_values (scanner) != 0) { /* Verify values */ DBG (1, "ERROR: invalid scan-values\n"); scanner->scanning = SANE_FALSE; coolscan_give_scanner (scanner); sanei_scsi_close (scanner->sfd); scanner->sfd = -1; return SANE_STATUS_INVAL; } if (coolscan_grab_scanner (scanner)) { sanei_scsi_close (scanner->sfd); scanner->sfd = -1; DBG (5, "WARNING: unable to reserve scanner: device busy\n"); scanner->scanning = SANE_FALSE; return SANE_STATUS_DEVICE_BUSY; } prescan (scanner); if(scanner->LS<2) { get_internal_info(scanner); } coolscan_get_window_param (scanner,1); scanner->scanning = SANE_FALSE; coolscan_give_scanner (scanner); return SANE_STATUS_GOOD; } static int send_one_LUT (Coolscan_t * s, SANE_Word * LUT, int reg) { int i; short lutval; short bytesperval; unsigned char *gamma, *gamma_p; unsigned short *gamma_s; DBG (10, "send LUT\n"); if(s->LS<2) { set_S_datatype_code (send.cmd, R_user_reg_gamma); bytesperval=1; } else { send.cmd[0x02]=3; send.cmd[0x05]=1; bytesperval=2; } set_S_xfer_length (send.cmd, s->lutlength*bytesperval); set_S_datatype_qual_upper (send.cmd, reg); gamma = alloca (send.size + s->lutlength*2); memcpy (gamma, send.cmd, send.size); if(s->LS<2) { gamma_p = &gamma[send.size]; for (i = 0; i < s->lutlength; i++) { if (LUT[i] > 255) LUT[i] = 255; /* broken gtk */ *gamma_p++ = (unsigned char) LUT[i]; } } else if(s->LS==2) { gamma_s = (unsigned short*)( &gamma[send.size]); for (i = 0; i < s->lutlength; i++) { if(s->negative) { lutval=(unsigned short)(LUT[(s->lutlength-i)]); } else { lutval=(unsigned short)(LUT[i]); } if (LUT[i] >= s->max_lut_val) LUT[i] = s->max_lut_val-1; /* broken gtk */ if(s->low_byte_first) /* if on little endian machine: */ { lutval=((lutval&0x00ff)<<8)+((lutval&0xff00)>>8); /* inverse byteorder */ } *gamma_s++ = lutval; } } else if(s->LS==3) { gamma_s = (unsigned short*)( &gamma[send.size]); for (i = 0; i < s->lutlength; i++) { if(s->negative) { lutval=(unsigned short)(LUT[s->lutlength-i]); } else { lutval=(unsigned short)(LUT[i]); } if (LUT[i] >= s->max_lut_val) LUT[i] = s->max_lut_val-1; /* broken gtk */ if(s->low_byte_first) /* if on little endian machine: */ { lutval=((lutval&0x00ff)<<8)+((lutval&0xff00)>>8); /* inverse byteorder */ } *gamma_s++ = lutval; } } return do_scsi_cmd (s->sfd, gamma, send.size + s->lutlength*bytesperval, NULL, 0); } static int send_LUT (Coolscan_t * s) { wait_scanner (s); if (s->gamma_bind) { send_one_LUT (s, s->gamma, S_DQ_Reg1); if(s->LS>=2) { send_one_LUT (s, s->gamma, S_DQ_Reg2); send_one_LUT (s, s->gamma, S_DQ_Reg3); if(s->colormode&0x08) { send_one_LUT (s, s->gamma, S_DQ_Reg9); } } } else { send_one_LUT (s, s->gamma_r, S_DQ_Reg1); send_one_LUT (s, s->gamma_g, S_DQ_Reg2); send_one_LUT (s, s->gamma_b, S_DQ_Reg3); if(s->colormode&0x08) { send_one_LUT (s, s->gamma_r, S_DQ_Reg9); } } return 0; } static int coolscan_read_data_block (Coolscan_t * s, unsigned int datatype, unsigned int length) { int r; DBG (10, "read_data_block (type= %x length = %d)\n",datatype,length); /*wait_scanner(s); */ set_R_datatype_code (sread.cmd, datatype); sread.cmd[4]=00; sread.cmd[5]=00; set_R_xfer_length (sread.cmd, length); r = do_scsi_cmd (s->sfd, sread.cmd, sread.size, s->buffer, length); return ((r != 0) ? -1 : (int) length); } static void coolscan_do_inquiry (Coolscan_t * s) { int size; DBG (10, "do_inquiry\n"); memset (s->buffer, '\0', 256); /* clear buffer */ size = 36; /* Hardcoded, and as specified by Nikon */ /* then get inquiry with actual size */ set_inquiry_return_size (inquiry.cmd, size); do_scsi_cmd (s->sfd, inquiry.cmd, inquiry.size, s->buffer, size); } static int coolscan_identify_scanner (Coolscan_t * s) { unsigned char vendor[9]; unsigned char product[0x11]; unsigned char version[5]; unsigned char *pp; int i; vendor[8] = product[0x10] = version[4] = 0; DBG (10, "identify_scanner\n"); coolscan_do_inquiry (s); /* get inquiry */ if (get_inquiry_periph_devtype (s->buffer) != IN_periph_devtype_scanner) { DBG (5, "identify_scanner: not a scanner\n"); return 1; } /* no, continue searching */ coolscan_get_inquiry_values (s); get_inquiry_vendor ((char *)s->buffer, (char *)vendor); get_inquiry_product ((char *)s->buffer, (char *)product); get_inquiry_version ((char *)s->buffer, (char *)version); if (strncmp ("Nikon ", (char *)vendor, 8)) { DBG (5, "identify_scanner: \"%s\" isn't a Nikon product\n", vendor); return 1; } /* Not a Nikon product */ pp = &vendor[8]; vendor[8] = ' '; while (*pp == ' ') { *pp-- = '\0'; } pp = &product[0x10]; product[0x10] = ' '; while (*(pp - 1) == ' ') { *pp-- = '\0'; } /* leave one blank at the end! */ pp = &version[4]; version[4] = ' '; while (*pp == ' ') { *pp-- = '\0'; } DBG (10, "Found Nikon scanner %sversion %s on device %s\n", product, version, s->devicename); /* look for scanners that do not give all inquiry-informations */ /* and if possible use driver-known inquiry-data */ if (get_inquiry_additional_length (s->buffer) >= 0x1f) { /* Now identify full supported scanners */ for (i = 0; i < known_scanners; i++) { if (!strncmp ((char *)product, scanner_str[i], strlen (scanner_str[i]))) { s->LS = i; return 0; } } if (s->cont) return 0; else return 1; } else return 1; } static int pixels_per_line (Coolscan_t * s) { int pic_dot; if(s->LS<2) { pic_dot = (s->brx - s->tlx + s->x_nres) / s->x_nres; } else { pic_dot = (s->brx - s->tlx + 1) / s->x_nres; } DBG (10, "pic_dot=%d\n", pic_dot); return pic_dot; } static int lines_per_scan (Coolscan_t * s) { int pic_line; if(s->LS<2) { pic_line = (s->bry - s->tly + s->y_nres) / s->y_nres; } else { pic_line = (( s->bry - s->tly + 1.0 ) / s->y_nres); } DBG (10, "pic_line=%d\n", pic_line); return pic_line; } static int scan_bytes_per_line (Coolscan_t * s) { int bpl; switch(s->colormode) { case RGB: case GREYSCALE: bpl=pixels_per_line (s) * 3; if(s->bits_per_color>8) bpl=bpl*2; return bpl; break; case RGBI: case IRED: bpl=pixels_per_line (s) * 4; if(s->bits_per_color>8) bpl=bpl*2; return bpl; break; } return 0; } static int write_bytes_per_line (Coolscan_t * s) { int bpl; switch(s->colormode) { case RGB: bpl=pixels_per_line (s) * 3; if(s->bits_per_color>8) bpl=bpl*2; return bpl; break; case RGBI: bpl=pixels_per_line (s) * 4; if(s->bits_per_color>8) bpl=bpl*2; return bpl; break; case IRED: case GREYSCALE: bpl= pixels_per_line (s) ; if(s->bits_per_color>8) bpl=bpl*2; return bpl; break; } return 0; } static void coolscan_trim_rowbufsize (Coolscan_t * s) { unsigned int row_len; row_len = scan_bytes_per_line (s); s->row_bufsize = (s->row_bufsize < row_len) ? s->row_bufsize : s->row_bufsize - (s->row_bufsize % row_len); DBG (10, "trim_bufsize to %d\n", s->row_bufsize); } static int coolscan_check_values (Coolscan_t * s) { DBG (10, "check_values\n"); /* -------------------------- asf --------------------------------- */ if (s->asf != 0) { if (s->autofeeder == 0) { DBG (1, "ERROR: ASF-MODE NOT SUPPORTED BY SCANNER, ABORTING\n"); return (1); } } return (0); } /* test_little_endian */ static SANE_Bool coolscan_test_little_endian(void) { SANE_Int testvalue = 255; unsigned char *firstbyte = (unsigned char *) &testvalue; if (*firstbyte == 255) { return SANE_TRUE; } return SANE_FALSE; } static int get_inquiery_part_LS30 (Coolscan_t * s, unsigned char part) { int size; /* Get length of response */ inquiry.cmd[1]=0x01; inquiry.cmd[2]=part; size=4; set_inquiry_return_size (inquiry.cmd, size); do_scsi_cmd (s->sfd, inquiry.cmd, inquiry.size, s->buffer, size); size=get_inquiry_length(s->buffer); size+=4; /* then get inquiry with actual size */ set_inquiry_return_size (inquiry.cmd, size); do_scsi_cmd (s->sfd, inquiry.cmd, inquiry.size, s->buffer, size); return size; } static int coolscan_read_var_data_block (Coolscan_t * s,int datatype) { int r; int size; DBG (10, "read_data_block (type= %x)\n",datatype); /*wait_scanner(s); */ sread.cmd[2]=datatype; sread.cmd[4]=00; sread.cmd[5]=03; size=6; set_R_xfer_length (sread.cmd, size); r = do_scsi_cmd (s->sfd, sread.cmd, sread.size, s->buffer, size); size=s->buffer[5]; set_R_xfer_length (sread.cmd, size); r = do_scsi_cmd (s->sfd, sread.cmd, sread.size, s->buffer, size); return ((r != 0) ? -1 : size); } static int get_inquiery_LS30 (Coolscan_t * s) { unsigned char part; unsigned char parts[5]; int i; /* Get vector of inquiery parts */ get_inquiery_part_LS30(s, (unsigned char) 0); /* Get the parts of inquiery */ for(i=0;i<5;i++) { parts[i]=((unsigned char *)s->buffer)[4+11+i]; } for(i=0;i<5;i++) { part=parts[i]; get_inquiery_part_LS30 (s, part); switch(part) { case 0x0c1:/* max size and resolution */ s->adbits = 8; s->outputbits = 8; s->maxres = getnbyte(s->buffer+0x12,2)-1; s->xmaxpix = getnbyte(s->buffer+0x53,2)-1; s->ymaxpix = getnbyte(s->buffer+0x3c,2)-1; break; case 0x0d1: break; case 0x0e1: break; case 0x0f0: break; case 0x0f8: break; } } /* get windows */ coolscan_get_window_param_LS30 (s,0,0); s->xmax = get_WD_width(s->buffer); s->ymax = get_WD_length(s->buffer); coolscan_get_window_param_LS30 (s,1,0); coolscan_get_window_param_LS30 (s,2,0); coolscan_get_window_param_LS30 (s,3,0); coolscan_get_window_param_LS30 (s,4,0); coolscan_get_window_param_LS30 (s,9,0); s->analoggamma = 0; return 1; } static int get_feeder_type_LS30 (Coolscan_t * s) { int size; unsigned char *ptr; int ima; /* find out about Film-strip-feeder or Mount-Feeder */ size=get_inquiery_part_LS30(s, (unsigned char) 1); if(strncmp((char *)s->buffer+5,"Strip",5)==0) { s->feeder=STRIP_FEEDER; s->autofeeder = 1; } if(strncmp((char *)s->buffer+5,"Mount",5)==0) { s->feeder=MOUNT_FEEDER; } /* find out about Film-strip-feeder positions*/ if(s->feeder==STRIP_FEEDER) { size=coolscan_read_var_data_block (s,(int)0x88); if(size>=4) { s->numima=s->buffer[3]; if(s->numima>6) s->numima=6; /* limit to 6 images for now */ if(s->numima>(size-4)/16) s->numima=(size-4)/16; ptr=s->buffer+4; for(ima=0;imanumima;ima++) { s->ipos[ima].start=getnbyte(ptr,4); s->ipos[ima].offset=getnbyte(ptr+4,4); s->ipos[ima].end=getnbyte(ptr+8,4); s->ipos[ima].height=getnbyte(ptr+12,4); ptr+=16; } } s->posima=0; } return 1; } static int get_internal_info_LS20 (Coolscan_t * s) { int ret; DBG (10, "get_internal_info\n"); wait_scanner (s); memset (s->buffer, '\0', DI_length); /* clear buffer */ set_R_datatype_code (sread.cmd, R_device_internal_info); set_R_datatype_qual_upper (sread.cmd, R_DQ_none); set_R_xfer_length (sread.cmd, DI_length); /* then get inquiry with actual size */ ret = do_scsi_cmd (s->sfd, sread.cmd, sread.size, s->buffer, DI_length); s->adbits = get_DI_ADbits (s->buffer); s->outputbits = get_DI_Outputbits (s->buffer); s->maxres = get_DI_MaxResolution (s->buffer); s->xmax = get_DI_Xmax (s->buffer); s->ymax = get_DI_Ymax (s->buffer); s->xmaxpix = get_DI_Xmaxpixel (s->buffer); s->ymaxpix = get_DI_Ymaxpixel (s->buffer); s->ycurrent = get_DI_currentY (s->buffer); s->currentfocus = get_DI_currentFocus (s->buffer); s->currentscanpitch = get_DI_currentscanpitch (s->buffer); s->autofeeder = get_DI_autofeeder (s->buffer); s->analoggamma = get_DI_analoggamma (s->buffer); s->derr[0] = get_DI_deviceerror0 (s->buffer); s->derr[1] = get_DI_deviceerror1 (s->buffer); s->derr[2] = get_DI_deviceerror2 (s->buffer); s->derr[3] = get_DI_deviceerror3 (s->buffer); s->derr[4] = get_DI_deviceerror4 (s->buffer); s->derr[5] = get_DI_deviceerror5 (s->buffer); s->derr[6] = get_DI_deviceerror6 (s->buffer); s->derr[7] = get_DI_deviceerror7 (s->buffer); s->wbetr_r = get_DI_WBETR_R (s->buffer); s->webtr_g = get_DI_WBETR_G (s->buffer); s->webtr_b = get_DI_WBETR_B (s->buffer); s->pretv_r = get_DI_PRETV_R (s->buffer); s->pretv_g = get_DI_PRETV_G (s->buffer); s->pretv_r = get_DI_PRETV_R (s->buffer); s->cetv_r = get_DI_CETV_R (s->buffer); s->cetv_g = get_DI_CETV_G (s->buffer); s->cetv_b = get_DI_CETV_B (s->buffer); s->ietu_r = get_DI_IETU_R (s->buffer); s->ietu_g = get_DI_IETU_G (s->buffer); s->ietu_b = get_DI_IETU_B (s->buffer); s->limitcondition = get_DI_limitcondition (s->buffer); s->offsetdata_r = get_DI_offsetdata_R (s->buffer); s->offsetdata_g = get_DI_offsetdata_G (s->buffer); s->offsetdata_b = get_DI_offsetdata_B (s->buffer); get_DI_poweron_errors (s->buffer, s->power_on_errors); DBG (10, "\tadbits=%d\toutputbits=%d\tmaxres=%d\txmax=%d\tymax=%d\n" "\txmaxpix=%d\tymaxpix=%d\tycurrent=%d\tcurrentfocus=%d\n" "\tautofeeder=%s\tanaloggamma=%s\tcurrentscanpitch=%d\n", s->adbits, s->outputbits, s->maxres, s->xmax, s->ymax, s->xmaxpix, s->ymaxpix, s->ycurrent, s->currentfocus, s->autofeeder ? "Yes" : "No", s->analoggamma ? "Yes" : "No", s->currentscanpitch); DBG (10, "\tWhite balance exposure time var [RGB]=\t%d %d %d\n" "\tPrescan result exposure time var [RGB]=\t%d %d %d\n" "\tCurrent exposure time var.[RGB]=\t%d %d %d\n" "\tInternal exposure time unit[RGB]=\t%d %d %d\n", s->wbetr_r, s->webtr_g, s->webtr_b, s->pretv_r, s->pretv_g, s->pretv_r, s->cetv_r, s->cetv_g, s->cetv_b, s->ietu_r, s->ietu_g, s->ietu_b); DBG (10, "\toffsetdata_[rgb]=\t0x%x 0x%x 0x%x\n" "\tlimitcondition=0x%x\n" "\tdevice error code = 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n" "\tpower-on errors = 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", s->offsetdata_r, s->offsetdata_g, s->offsetdata_b, s->limitcondition, s->derr[0], s->derr[1], s->derr[2], s->derr[3], s->derr[4], s->derr[5], s->derr[6], s->derr[7], s->power_on_errors[0], s->power_on_errors[1], s->power_on_errors[2], s->power_on_errors[3], s->power_on_errors[4], s->power_on_errors[5], s->power_on_errors[6], s->power_on_errors[7]); return ret; } static int get_internal_info (Coolscan_t * s) { int ret; DBG (10, "get_internal_info\n"); if(s->LS<2) /* distinguish between old and new scanners */ { ret=get_internal_info_LS20 (s); } else { ret=get_inquiery_LS30 (s); } return ret; } static void coolscan_get_inquiry_values (Coolscan_t * s) { unsigned char *inquiry_block; DBG (10, "get_inquiry_values\n"); inquiry_block = (unsigned char *) s->buffer; s->inquiry_len = 36; get_inquiry_vendor ((char *)inquiry_block, (char *)s->vendor); s->vendor[8] = '\0'; get_inquiry_product ((char *)inquiry_block, (char *)s->product); s->product[16] = '\0'; get_inquiry_version ((char *)inquiry_block, (char *)s->version); s->version[4] = '\0'; if (s->inquiry_len < 36) { DBG (1, "WARNING: inquiry return block is unexpected short (%d instead of 36).\n", s->inquiry_len); } s->inquiry_wdb_len = 117; return; } static void coolscan_initialize_values (Coolscan_t * s) { int i; DBG (10, "initialize_values\n"); /* Initialize us structure */ if(s->LS<2) /* LS-20 or LS-10000 */ { select_MUD (s); /* must be before mode_sense - not for LS-30*/ coolscan_mode_sense (s); /* Obtain MUD (Measurement Unit Divisor) */ get_internal_info (s); /* MUST be called first. */ s->wdb_len = 117; } if(s->LS>=2) /* LS-30 */ { get_inquiery_LS30(s); /* Info about scanner*/ select_MUD (s); /* must be before mode_sense */ get_feeder_type_LS30(s); s->wdb_len = 117; } s->cont = 0; /* do not continue if scanner is unknown */ s->verbose = 2; /* 1=verbose,2=very verbose */ s->x_nres = s->y_nres = 2; /* 2 => 1350 dpi */ s->x_p_nres = s->y_p_nres = 9; /* 9 => 300 dpi */ s->tlx = 0; s->tly = 0; s->brx = s->xmaxpix; /* 2700 / 1200; */ s->bry = s->ymaxpix; /* 2700 / 1200; */ s->set_auto = 0; /* Always 0 on Nikon LS-{100|2}0 */ s->preview = 0; /* 1 for preview */ s->colormode = RGB; /* GREYSCALE or RGB */ s->colormode_p = RGB; /* GREYSCALE or RGB for preview*/ s->asf = 0; /* 1 if asf shall be used */ s->gammaselection = WD_Linear; s->brightness = 128; s->brightness_R = 128; s->brightness_G = 128; s->brightness_B = 128; s->contrast = 128; s->contrast_R = 128; s->contrast_G = 128; s->contrast_B = 128; s->exposure_R = 50; s->exposure_G = 50; s->exposure_B = 50; s->pretv_r=40000; s->pretv_g=40000; s->pretv_b=40000; s->shift_R = 128; s->shift_G = 128; s->shift_B = 128; s->ired_red=60; s->ired_green=1; s->ired_blue=1; s->prescan = 1; s->bits_per_color = 8; s->rgb_control = 0; s->gamma_bind = 1; switch(s->LS) { case 0:s->lutlength=2048; s->max_lut_val=256; break; case 1:s->lutlength=512; s->max_lut_val=512; break; case 2:s->lutlength=1024; s->max_lut_val=1024; break; case 3:s->lutlength=4096; s->max_lut_val=4096; break; } for (i = 0; i < s->lutlength; i++) { s->gamma[i] =((short)((((double)i)/s->lutlength)*s->max_lut_val)); s->gamma_r[i] = s->gamma[i]; s->gamma_g[i] = s->gamma[i]; s->gamma_b[i] = s->gamma[i]; } if (coolscan_test_little_endian() == SANE_TRUE) { s->low_byte_first = 1; /* in 2 byte mode send lowbyte first */ DBG(10,"backend runs on little endian machine\n"); } else { s->low_byte_first = 0; /* in 2 byte mode send highbyte first */ DBG(10,"backend runs on big endian machine\n"); } } 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.3d:", i); ptr += 4; } sprintf (ptr, " %2.2x", *p); ptr += 3; } *ptr = '\0'; DBG (level, "%s\n", line); } static SANE_Status sense_handler (int scsi_fd, unsigned char * result, void *arg) { (void) scsi_fd; (void) arg; if (result[0] != 0x70) { return SANE_STATUS_IO_ERROR; /* we only know about this one */ } return request_sense_parse(result); } /* ------------------------------------------------------------------------- */ /* ilu per mm */ #define length_quant SANE_UNFIX(SANE_FIX(MM_PER_INCH / 2700.0)) #define mmToIlu(mm) ((mm) / length_quant) #define iluToMm(ilu) ((ilu) * length_quant) #define P_200_TO_255(per) SANE_UNFIX((per + 100) * 255/200 ) #define P_100_TO_255(per) SANE_UNFIX(per * 255/100 ) static const char negativeStr[] = "Negative"; static const char positiveStr[] = "Positive"; static SANE_String_Const type_list[] = { positiveStr, negativeStr, 0 }; static const char colorStr[] = SANE_VALUE_SCAN_MODE_COLOR; static const char grayStr[] = SANE_VALUE_SCAN_MODE_GRAY; static const char rgbiStr[] = "RGBI"; static const char iredStr[] = "Infrared"; static SANE_String_Const scan_mode_list_LS20[] = { colorStr, grayStr, NULL }; static SANE_String_Const scan_mode_list_LS30[] = { colorStr, grayStr, #ifdef HAS_IRED rgbiStr, #endif /* HAS_IRED */ NULL }; static SANE_Int bit_depth_list[9]; static const char neverStr[] = "never"; static const char previewStr[] = "before preview"; static const char scanStr[] = "before scan"; static const char preandscanStr[] = "before preview and scan"; static SANE_String_Const autofocus_mode_list[] = { neverStr, previewStr, scanStr, preandscanStr, NULL }; static SANE_String_Const source_list[4] = {NULL, NULL, NULL, NULL}; static const SANE_Range gamma_range_8 = { 0, /* minimum */ 255, /* maximum */ 1 /* quantization */ }; static const SANE_Range gamma_range_9 = { 0, /* minimum */ 511, /* maximum */ 1 /* quantization */ }; static const SANE_Range gamma_range_10 = { 0, /* minimum */ 1023, /* maximum */ 1 /* quantization */ }; static const SANE_Range gamma_range_12 = { 0, /* minimum */ 4096, /* maximum */ 1 /* quantization */ }; static const SANE_Range brightness_range = { -5, +5, 1 }; static const SANE_Range contrast_range = { -5, +5, 0 }; static const SANE_Range exposure_range = { 24, 400, 2 }; static const SANE_Range shift_range = { -15, +15, 0 }; static const SANE_Device **devlist = 0; static int num_devices; static Coolscan_t *first_dev; static size_t max_string_size (const SANE_String_Const strings[]) { size_t size, max_size = 0; int i; for (i = 0; strings[i]; ++i) { size = strlen (strings[i]) + 1; if (size > max_size) max_size = size; } return max_size; } static SANE_Status do_eof (Coolscan_t * scanner) { DBG (10, "do_eof\n"); if (scanner->pipe >= 0) { close (scanner->pipe); scanner->pipe = -1; } return SANE_STATUS_EOF; } static SANE_Status do_cancel (Coolscan_t * scanner) { DBG (10, "do_cancel\n"); swap_res (scanner); scanner->scanning = SANE_FALSE; do_eof (scanner); /* close pipe and reposition scanner */ if (sanei_thread_is_valid (scanner->reader_pid)) { int exit_status; DBG (10, "do_cancel: kill reader_process\n"); /* ensure child knows it's time to stop: */ sanei_thread_kill (scanner->reader_pid); while (sanei_thread_waitpid(scanner->reader_pid, &exit_status) != scanner->reader_pid ); sanei_thread_invalidate (scanner->reader_pid); } if (scanner->sfd >= 0) { coolscan_give_scanner (scanner); DBG (10, "do_cancel: close filedescriptor\n"); sanei_scsi_close (scanner->sfd); scanner->sfd = -1; } return SANE_STATUS_CANCELLED; } static SANE_Status attach_scanner (const char *devicename, Coolscan_t ** devp) { Coolscan_t *dev; int sfd; DBG (10, "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 (10, "attach_scanner: opening %s\n", devicename); if (sanei_scsi_open (devicename, &sfd, sense_handler, 0) != 0) { DBG (1, "attach_scanner: open failed\n"); return SANE_STATUS_INVAL; } if (NULL == (dev = malloc (sizeof (*dev)))) return SANE_STATUS_NO_MEM; dev->row_bufsize = (sanei_scsi_max_request_size < (64 * 1024)) ? sanei_scsi_max_request_size : 64 * 1024; if ((dev->buffer = malloc (dev->row_bufsize)) == NULL) /* if ((dev->buffer = malloc (sanei_scsi_max_request_size)) == NULL)*/ return SANE_STATUS_NO_MEM; if ((dev->obuffer = malloc (dev->row_bufsize)) == NULL) return SANE_STATUS_NO_MEM; dev->devicename = strdup (devicename); dev->sfd = sfd; /* Nikon manual: Step 1 */ if (coolscan_identify_scanner (dev) != 0) { DBG (1, "attach_scanner: scanner-identification failed\n"); sanei_scsi_close (dev->sfd); free (dev->buffer); free (dev); return SANE_STATUS_INVAL; } /* Get MUD (via mode_sense), internal info (via get_internal_info), and * initialize values */ coolscan_initialize_values (dev); /* Why? */ sanei_scsi_close (dev->sfd); dev->sfd = -1; dev->sane.name = dev->devicename; dev->sane.vendor = dev->vendor; dev->sane.model = dev->product; dev->sane.type = "slide scanner"; dev->x_range.min = SANE_FIX (0); dev->x_range.quant = SANE_FIX (length_quant); dev->x_range.max = SANE_FIX ((double) ((dev->xmaxpix) * length_quant)); dev->y_range.min = SANE_FIX (0.0); dev->y_range.quant = SANE_FIX (length_quant); dev->y_range.max = SANE_FIX ((double) ((dev->ymaxpix) * length_quant)); /* ...and this?? */ dev->dpi_range.min = SANE_FIX (108); dev->dpi_range.quant = SANE_FIX (0); dev->dpi_range.max = SANE_FIX (dev->maxres); DBG (10, "attach: dev->dpi_range.max = %f\n", SANE_UNFIX (dev->dpi_range.max)); ++num_devices; dev->next = first_dev; first_dev = dev; if (devp) { *devp = dev; } DBG (10, "attach_scanner done\n"); return SANE_STATUS_GOOD; } static SANE_Status attach_one (const char *devName) { return attach_scanner(devName, 0); } static void sigterm_handler (int signal) { (void) signal; sanei_scsi_req_flush_all (); /* flush SCSI queue */ _exit (SANE_STATUS_GOOD); } typedef struct Color_correct_s { int sum; /* number of pixels summed so far */ double sumr; /* sum of red pixel values*/ double sumi; /* sum of infrared pixel values*/ double sumri; /* sum of red*ired pixel values*/ double sumii; /* sum of ired*ired pixel values*/ double sumrr; /* sum of ired*ired pixel values*/ int mr; /* factor between red and ired values (*256) */ int br; /* offset of ired values */ } ColorCorrect; /* --------------------------------------------------------------- function: RGBIfix task: Correct the infrared channel import: unsigned char * rgbimat - RGBI - matrix from scanner int size - number of pixels to correct int *lutr - lookup table for red correction int *lutg - lookup table for red correction int *lutb - lookup table for red correction int *lutr - lookup table for red correction export: unsigned char * orgbimat - RGBI - corrected matrix written by: Andreas RICK 19.6.1999 ----------------------------------------------------------------*/ static int Calc_fix_LUT(Coolscan_t * s) { int uselutr,uselutg,uselutb,useluti; /* static int irmulr= -34*25; */ int irmulr= -64*25; int irmulg= -1*25; int irmulb= -0*25; int irmuli= 256*25; int div; int i; irmulr=s->ired_red*(25); irmulg=s->ired_green*(25); irmulb=s->ired_blue*(25); irmuli=25*256; if(s->LS==2) /* TODO: right conversion factors for 10 and 12 bit */ { div=4; } else if(s->LS==3) { div=16; } else { return 0; } memset(s->lutr, 0,256*4); memset(s->lutg, 0,256*4); memset(s->lutb, 0,256*4); memset(s->luti, 0,256*4); for(i=0;ilutlength;i++) { if(s->gamma_bind) { uselutr=uselutg=uselutb=useluti=s->gamma[i]/div; } else { uselutr=s->gamma_r[i]/div; uselutg=s->gamma_g[i]/div; uselutb=s->gamma_b[i]/div; useluti=s->gamma_r[i]/div; } s->lutr[uselutr]=(int)(irmulr*pow((double)i,(double)0.333333)); s->lutg[uselutg]=(int)(irmulg*pow((double)i,(double)0.333333)); s->lutb[uselutb]=(int)(irmulb*pow((double)i,(double)0.333333)); s->luti[useluti]=(int)(irmuli*pow((double)i,(double)0.333333)); if(uselutr<255) { if(s->lutr[uselutr+1]==0) s->lutr[uselutr+1]=s->lutr[uselutr]; } if(uselutg<255) { if(s->lutg[uselutg+1]==0) s->lutg[uselutg+1]=s->lutg[uselutg]; } if(uselutb<255) { if(s->lutb[uselutb+1]==0) s->lutb[uselutb+1]=s->lutb[uselutb]; } if(useluti<255) { if(s->luti[useluti+1]==0) s->luti[useluti+1]=s->luti[useluti]; } } /* DEBUG for(i=0;i<255;i++) { fprintf(stderr,"%d %d %d %d\n" ,s->lutr[i],s->lutg[i],s->lutb[i],s->luti[i]); } */ return 1; } /* --------------------------------------------------------------- function: RGBIfix task: Correct the infrared channel import: unsigned char * rgbimat - RGBI - matrix from scanner int size - number of pixels to correct int *lutr - lookup table for red correction int *lutg - lookup table for red correction int *lutb - lookup table for red correction int *lutr - lookup table for red correction export: unsigned char * orgbimat - RGBI - corrected matrix written by: Andreas RICK 19.6.1999 ----------------------------------------------------------------*/ static int RGBIfix(Coolscan_t * scanner, unsigned char* rgbimat, unsigned char* orgbimat, int size, int *lutr, int *lutg, int *lutb, int *luti) { unsigned char *pr,*pg,*pb,*pi; unsigned char *opr,*opg,*opb,*opi; int r,g,b,i; int ii; int x; for(x=0;x255*256)ii=255*256; if(scanner->negative) { (*opi)=(unsigned char)(255-(ii>>8)); } else { (*opi)=(unsigned char)(ii>>8); } } return 1; } /* --------------------------------------------------------------- function: RGBIfix16 task: Correct the infrared channel for 16 bit images (doesn't do anything for now) import: unsigned char * rgbimat - RGBI - matrix from scanner int size - number of pixels to correct int *lutr - lookup table for red correction int *lutg - lookup table for red correction int *lutb - lookup table for red correction int *lutr - lookup table for red correction export: unsigned char * orgbimat - RGBI - corrected matrix written by: Andreas RICK 19.6.1999 ----------------------------------------------------------------*/ static int RGBIfix16(Coolscan_t * scanner, unsigned short* rgbimat, unsigned short* orgbimat, int size, int *lutr, int *lutg, int *lutb, int *luti) { unsigned short *pr,*pg,*pb,*pi; unsigned short *opr,*opg,*opb,*opi; int x; (void) scanner; (void) lutr; (void) lutg; (void) lutb; (void) luti; for(x=0;x>8); (*opg)=(((*pg)&0x00ff)<<8)+(((*pg)&0xff00)>>8); (*opb)=(((*pb)&0x00ff)<<8)+(((*pb)&0xff00)>>8); (*opi)=(((*pi)&0x00ff)<<8)+(((*pi)&0xff00)>>8); } return 1; } /* --------------------------------------------------------------- function: rgb2g task: Convert RGB data to grey import: unsigned char * rgbimat - RGB - matrix from scanner int size - size of input data (num pixel) export: unsigned char * gomat - Grey matrix written by: Andreas RICK 13.7.1999 ----------------------------------------------------------------*/ #define RtoG ((int)(0.27*256)) #define GtoG ((int)(0.54*256)) #define BtoG ((int)(0.19*256)) static int rgb2g(unsigned char* rgbimat,unsigned char* gomat, int size) { unsigned char *pr,*pg,*pb; unsigned char *opg; int g; int x; for(x=0;x>8); } return 1; } /* --------------------------------------------------------------- function: RGBIfix1 task: Correct the infrared channel. The input image data is the output of scanning with LUT. To calculate the original values the lutr and luti is applied. The infrared values is corrected by: Ir=mr*lutr(r)+luti(i) import: unsigned char * rgbimat - RGBI - matrix from scanner int size - number of pixels to correct ColorCorrect *cc, int *lutr - lookup table for red correction int *luti - lookup table for ired correction export: unsigned char * orgbimat - RGBI - corrected matrix written by: Andreas RICK 3.7.1999 ----------------------------------------------------------------*/ #if 0 static int RGBIfix1(unsigned char* rgbimat,unsigned char* orgbimat, int size, int *lutr, int *lutg, int *lutb, int *luti) { unsigned char *pr,*pg,*pb,*pi; unsigned char *opr,*opg,*opb,*opi; ColorCorrect cc; int r,i; static int thresi=100; int ii; int x; (void) lutg; (void) lutb; /* calculate regression between r and ir */ cc.sum=0; cc.sumr=cc.sumii=cc.sumrr=cc.sumi=cc.sumri=0.0; for(x=0;xthresi) { cc.sum++; cc.sumr+=r; cc.sumii+=(i*i); cc.sumrr+=(r*r); cc.sumi+=i; cc.sumri+=(i*r); } } if((cc.sumii!=0)&&(cc.sum!=0)) { double dn,dz,dm; dz=(cc.sumri-cc.sumr*cc.sumi/cc.sum); dn=(cc.sumrr-cc.sumr*cc.sumr/cc.sum); DBG (2, "Reg:dz:%e dn:%e\n",dz,dn); if(dn!=0) { dm=(dz/dn); cc.mr=(int)(dm*1024); } else { cc.mr=0; dm=0; } cc.br=(int)((cc.sumi-dm*cc.sumr)/cc.sum); } else { cc.mr=0; } DBG (2, "Regression: size:%d I=%d/1024*R b:%d s:%d sr:%e si:%e sii:%e sri:%e srr:%e\n", size,cc.mr,cc.br,cc.sum,cc.sumr,cc.sumi,cc.sumii,cc.sumri,cc.sumrr); for(x=0;x>10)-cc.br)>>2) +128; (*opr)=(*pr); (*opg)=(*pg); (*opb)=(*pb); if(ii<0) ii=0; if(ii>255) ii=255; (*opi)=(unsigned char)(ii); } return 1; } #endif /* This function is executed as a child process. */ static int reader_process (void *data ) { int status; unsigned int i; unsigned char h; unsigned int data_left; unsigned int data_to_read; unsigned int data_to_write; FILE *fp; sigset_t sigterm_set, ignore_set; struct SIGACTION act; unsigned int bpl, linesPerBuf, lineOffset; unsigned char r_data, g_data, b_data; unsigned int j, line; Coolscan_t * scanner = (Coolscan_t*)data; if (sanei_thread_is_forked ()) { DBG (10, "reader_process started (forked)\n"); close (scanner->pipe); scanner->pipe = -1; sigfillset ( &ignore_set ); sigdelset ( &ignore_set, SIGTERM ); #if defined (__APPLE__) && defined (__MACH__) sigdelset ( &ignore_set, SIGUSR2 ); #endif sigprocmask( SIG_SETMASK, &ignore_set, 0 ); memset (&act, 0, sizeof (act)); sigaction (SIGTERM, &act, 0); } else { DBG (10, "reader_process started (as thread)\n"); } sigemptyset (&sigterm_set); sigaddset (&sigterm_set, SIGTERM); fp = fdopen ( scanner->reader_fds, "w"); if (!fp) { DBG (1, "reader_process: couldn't open pipe!\n"); return 1; } DBG (10, "reader_process: starting to READ data\n"); data_left = scan_bytes_per_line (scanner) * lines_per_scan (scanner); /*scanner->row_bufsize = sanei_scsi_max_request_size;*/ coolscan_trim_rowbufsize (scanner); /* trim bufsize */ DBG (10, "reader_process: reading %u bytes in blocks of %u bytes\n", data_left, scanner->row_bufsize); memset (&act, 0, sizeof (act)); act.sa_handler = sigterm_handler; sigaction (SIGTERM, &act, 0); /* wait_scanner(scanner); */ do { data_to_read = (data_left < scanner->row_bufsize) ? data_left : scanner->row_bufsize; data_to_write=data_to_read; status = coolscan_read_data_block (scanner ,R_datatype_imagedata,data_to_read); if (status == 0) { continue; } if (status == -1) { DBG (1, "reader_process: unable to get image data from scanner!\n"); fclose (fp); return (-1); } if (scanner->LS == 1) { /* mirror image for LS-1000 */ bpl = scan_bytes_per_line(scanner); linesPerBuf = data_to_read / bpl; for (line = 0, lineOffset = 0; line < linesPerBuf; line++, lineOffset += bpl ) { if (scanner->colormode == RGB) { for (j = 0; j < bpl/2 ; j += 3) { r_data=scanner->buffer[lineOffset + j]; g_data=scanner->buffer[lineOffset + j + 1]; b_data=scanner->buffer[lineOffset + j + 2]; scanner->buffer[lineOffset + j] = scanner->buffer[lineOffset + bpl -1 - j - 2 ]; scanner->buffer[lineOffset + j + 1] = scanner->buffer[lineOffset + bpl -1 - j - 1 ]; scanner->buffer[lineOffset + j + 2] = scanner->buffer[lineOffset + bpl -1 - j ]; scanner->buffer[lineOffset + bpl -1 - j - 2 ] = r_data; scanner->buffer[lineOffset + bpl -1 - j - 1] = g_data; scanner->buffer[lineOffset + bpl -1 - j] = b_data; } } else { for (j = 0; j < bpl/2; j++) { r_data=scanner->buffer[lineOffset + j]; scanner->buffer[lineOffset + j] = scanner->buffer[lineOffset + bpl - 1 - j]; scanner->buffer[lineOffset + bpl - 1 - j] = r_data; } } } } if(scanner->colormode==RGBI) { /* Correct Infrared Channel */ if(scanner->bits_per_color>8) { RGBIfix16(scanner, (unsigned short * ) scanner->buffer, (unsigned short * )scanner->obuffer, data_to_read/8,scanner->lutr, scanner->lutg,scanner->lutb,scanner->luti); } else { RGBIfix(scanner,scanner->buffer,scanner->obuffer, data_to_read/4,scanner->lutr, scanner->lutg,scanner->lutb,scanner->luti); } } else if((scanner->colormode==GREYSCALE)&&(scanner->LS>=2)) { /* Convert to Grey */ data_to_write/=3; rgb2g(scanner->buffer,scanner->obuffer,data_to_write); } else { /* or just copy */ memcpy (scanner->obuffer, scanner->buffer,data_to_read); } if((!scanner->low_byte_first)&&(scanner->bits_per_color>8)) { for(i=0;iobuffer[i]; scanner->obuffer[i]=scanner->obuffer[i+1]; i++; scanner->obuffer[i]=h; } } fwrite (scanner->obuffer, 1, data_to_write, fp); fflush (fp); data_left -= data_to_read; DBG (10, "reader_process: buffer of %d bytes read; %d bytes to go\n", data_to_read, data_left); } while (data_left); fclose (fp); DBG (10, "reader_process: finished reading data\n"); return 0; } static SANE_Status init_options (Coolscan_t * scanner) { int i; int bit_depths; DBG (10, "init_options\n"); memset (scanner->opt, 0, sizeof (scanner->opt)); for (i = 0; i < NUM_OPTIONS; ++i) { scanner->opt[i].size = sizeof (SANE_Word); scanner->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } scanner->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; scanner->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; scanner->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; scanner->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; /* "Mode" group: */ scanner->opt[OPT_MODE_GROUP].title = "Scan Mode"; scanner->opt[OPT_MODE_GROUP].desc = ""; scanner->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; scanner->opt[OPT_MODE_GROUP].cap = 0; scanner->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* scan mode */ scanner->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; scanner->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE; scanner->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE; scanner->opt[OPT_MODE].type = SANE_TYPE_STRING; if(scanner->LS<2) { scanner->opt[OPT_MODE].size = max_string_size (scan_mode_list_LS20); scanner->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; scanner->opt[OPT_MODE].constraint.string_list = scan_mode_list_LS20; } else { scanner->opt[OPT_MODE].size = max_string_size (scan_mode_list_LS30); scanner->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; scanner->opt[OPT_MODE].constraint.string_list = scan_mode_list_LS30; } /* source */ source_list[0] = "Slide"; source_list[1] = "Automatic Slide Feeder"; source_list[2] = NULL; if (!scanner->autofeeder) { scanner->opt[OPT_SOURCE].cap = SANE_CAP_INACTIVE; } scanner->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE; scanner->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE; scanner->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE; scanner->opt[OPT_SOURCE].type = SANE_TYPE_STRING; scanner->opt[OPT_SOURCE].size = max_string_size (source_list); scanner->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; scanner->opt[OPT_SOURCE].constraint.string_list = source_list; /* negative */ scanner->opt[OPT_TYPE].name = "type"; scanner->opt[OPT_TYPE].title = "Film type"; scanner->opt[OPT_TYPE].desc = "Select the film type (positive (slide) or negative)"; scanner->opt[OPT_TYPE].type = SANE_TYPE_STRING; scanner->opt[OPT_TYPE].size = max_string_size (type_list); scanner->opt[OPT_TYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST; scanner->opt[OPT_TYPE].constraint.string_list = type_list; scanner->opt[OPT_PRESCAN].name = "prescan"; scanner->opt[OPT_PRESCAN].title = "Prescan"; scanner->opt[OPT_PRESCAN].desc = "Perform a prescan during preview"; scanner->opt[OPT_PRESCAN].type = SANE_TYPE_BOOL; scanner->opt[OPT_PRESCAN].unit = SANE_UNIT_NONE; scanner->opt[OPT_PRESCAN_NOW].name = "prescan now"; scanner->opt[OPT_PRESCAN_NOW].title = "Prescan now"; scanner->opt[OPT_PRESCAN_NOW].desc = "Perform a prescan now"; scanner->opt[OPT_PRESCAN_NOW].type = SANE_TYPE_BUTTON; scanner->opt[OPT_PRESCAN_NOW].unit = SANE_UNIT_NONE; scanner->opt[OPT_PRESCAN_NOW].size = 0; scanner->opt[OPT_PRESCAN_NOW].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; scanner->opt[OPT_PRESCAN_NOW].constraint_type = SANE_CONSTRAINT_NONE; scanner->opt[OPT_PRESCAN_NOW].constraint.string_list = 0; /* bit depth */ bit_depths=0; bit_depth_list[++bit_depths] = 8; if (scanner->LS==2) { bit_depth_list[++bit_depths] = 10; } if (scanner->LS==3) { bit_depth_list[++bit_depths] = 12; } bit_depth_list[0] = bit_depths; scanner->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH; scanner->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH; scanner->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH; scanner->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT; scanner->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_BIT; scanner->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST; scanner->opt[OPT_BIT_DEPTH].constraint.word_list = bit_depth_list; /* resolution */ scanner->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; scanner->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; scanner->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; scanner->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; scanner->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; scanner->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; scanner->opt[OPT_RESOLUTION].constraint.word_list = resolution_list; scanner->opt[OPT_PREVIEW_RESOLUTION].name = "preview-resolution"; scanner->opt[OPT_PREVIEW_RESOLUTION].title = "Preview resolution"; scanner->opt[OPT_PREVIEW_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; scanner->opt[OPT_PREVIEW_RESOLUTION].type = SANE_TYPE_INT; scanner->opt[OPT_PREVIEW_RESOLUTION].unit = SANE_UNIT_DPI; scanner->opt[OPT_PREVIEW_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; scanner->opt[OPT_PREVIEW_RESOLUTION].constraint.word_list = resolution_list; /* "Geometry" group: */ scanner->opt[OPT_GEOMETRY_GROUP].title = "Geometry"; scanner->opt[OPT_GEOMETRY_GROUP].desc = ""; scanner->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; scanner->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; scanner->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* top-left x */ scanner->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; scanner->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; scanner->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; scanner->opt[OPT_TL_X].type = SANE_TYPE_FIXED; scanner->opt[OPT_TL_X].unit = SANE_UNIT_MM; scanner->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; scanner->opt[OPT_TL_X].constraint.range = &(scanner->x_range); /* top-left y */ scanner->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; scanner->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; scanner->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; scanner->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; scanner->opt[OPT_TL_Y].unit = SANE_UNIT_MM; scanner->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; scanner->opt[OPT_TL_Y].constraint.range = &(scanner->y_range); /* bottom-right x */ scanner->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; scanner->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; scanner->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; scanner->opt[OPT_BR_X].type = SANE_TYPE_FIXED; scanner->opt[OPT_BR_X].unit = SANE_UNIT_MM; scanner->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; scanner->opt[OPT_BR_X].constraint.range = &(scanner->x_range); /* bottom-right y */ scanner->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; scanner->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; scanner->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; scanner->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; scanner->opt[OPT_BR_Y].unit = SANE_UNIT_MM; scanner->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; scanner->opt[OPT_BR_Y].constraint.range = &(scanner->y_range); /* ------------------------------ */ /* "Enhancement" group: */ scanner->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement"; scanner->opt[OPT_ENHANCEMENT_GROUP].desc = ""; scanner->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; scanner->opt[OPT_ENHANCEMENT_GROUP].cap = 0; scanner->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; scanner->opt[OPT_GAMMA_BIND].name = "gamma-bind"; scanner->opt[OPT_GAMMA_BIND].title = "Gamma bind"; scanner->opt[OPT_GAMMA_BIND].desc = "Use same gamma correction for all colours"; scanner->opt[OPT_GAMMA_BIND].type = SANE_TYPE_BOOL; scanner->opt[OPT_GAMMA_BIND].unit = SANE_UNIT_NONE; scanner->opt[OPT_ANALOG_GAMMA].name = "analog_gamma"; scanner->opt[OPT_ANALOG_GAMMA].title = "Analog Gamma"; scanner->opt[OPT_ANALOG_GAMMA].desc = "Analog Gamma"; scanner->opt[OPT_ANALOG_GAMMA].type = SANE_TYPE_BOOL; scanner->opt[OPT_ANALOG_GAMMA].unit = SANE_UNIT_NONE; if (!scanner->analoggamma) { scanner->opt[OPT_ANALOG_GAMMA].cap = SANE_CAP_INACTIVE; } scanner->opt[OPT_AVERAGING].name = "averaging"; scanner->opt[OPT_AVERAGING].title = "Averaging"; scanner->opt[OPT_AVERAGING].desc = "Averaging"; scanner->opt[OPT_AVERAGING].type = SANE_TYPE_BOOL; scanner->opt[OPT_AVERAGING].unit = SANE_UNIT_NONE; scanner->opt[OPT_RGB_CONTROL].name = "rgb-control"; scanner->opt[OPT_RGB_CONTROL].title = "RGB control"; scanner->opt[OPT_RGB_CONTROL].desc = "toggles brightness/contrast control over individual colours"; scanner->opt[OPT_RGB_CONTROL].type = SANE_TYPE_BOOL; scanner->opt[OPT_RGB_CONTROL].unit = SANE_UNIT_NONE; if(scanner->LS>=2) { scanner->opt[OPT_RGB_CONTROL].cap |= SANE_CAP_INACTIVE; } /* brightness */ scanner->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; scanner->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; scanner->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; scanner->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT; scanner->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE; scanner->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; scanner->opt[OPT_BRIGHTNESS].constraint.range = &brightness_range; if(scanner->LS>=2) { scanner->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; } scanner->opt[OPT_R_BRIGHTNESS].name = "red-brightness"; scanner->opt[OPT_R_BRIGHTNESS].title = "Red brightness"; scanner->opt[OPT_R_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; scanner->opt[OPT_R_BRIGHTNESS].type = SANE_TYPE_INT; scanner->opt[OPT_R_BRIGHTNESS].unit = SANE_UNIT_NONE; scanner->opt[OPT_R_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; scanner->opt[OPT_R_BRIGHTNESS].constraint.range = &brightness_range; scanner->opt[OPT_R_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; scanner->opt[OPT_G_BRIGHTNESS].name = "green-brightness"; scanner->opt[OPT_G_BRIGHTNESS].title = "Green brightness"; scanner->opt[OPT_G_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; scanner->opt[OPT_G_BRIGHTNESS].type = SANE_TYPE_INT; scanner->opt[OPT_G_BRIGHTNESS].unit = SANE_UNIT_NONE; scanner->opt[OPT_G_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; scanner->opt[OPT_G_BRIGHTNESS].constraint.range = &brightness_range; scanner->opt[OPT_G_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; scanner->opt[OPT_B_BRIGHTNESS].name = "blue-brightness"; scanner->opt[OPT_B_BRIGHTNESS].title = "Blue brightness"; scanner->opt[OPT_B_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; scanner->opt[OPT_B_BRIGHTNESS].type = SANE_TYPE_INT; scanner->opt[OPT_B_BRIGHTNESS].unit = SANE_UNIT_NONE; scanner->opt[OPT_B_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; scanner->opt[OPT_B_BRIGHTNESS].constraint.range = &brightness_range; scanner->opt[OPT_B_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; /* contrast */ scanner->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST; scanner->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; scanner->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST; scanner->opt[OPT_CONTRAST].type = SANE_TYPE_INT; scanner->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE; scanner->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; scanner->opt[OPT_CONTRAST].constraint.range = &contrast_range; if(scanner->LS>=2) { scanner->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; } scanner->opt[OPT_R_CONTRAST].name = "red-contrast"; scanner->opt[OPT_R_CONTRAST].title = "Red contrast"; scanner->opt[OPT_R_CONTRAST].desc = SANE_DESC_CONTRAST; scanner->opt[OPT_R_CONTRAST].type = SANE_TYPE_INT; scanner->opt[OPT_R_CONTRAST].unit = SANE_UNIT_NONE; scanner->opt[OPT_R_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; scanner->opt[OPT_R_CONTRAST].constraint.range = &contrast_range; scanner->opt[OPT_R_CONTRAST].cap |= SANE_CAP_INACTIVE; scanner->opt[OPT_G_CONTRAST].name = "green-contrast"; scanner->opt[OPT_G_CONTRAST].title = "Green contrast"; scanner->opt[OPT_G_CONTRAST].desc = SANE_DESC_CONTRAST; scanner->opt[OPT_G_CONTRAST].type = SANE_TYPE_INT; scanner->opt[OPT_G_CONTRAST].unit = SANE_UNIT_NONE; scanner->opt[OPT_G_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; scanner->opt[OPT_G_CONTRAST].constraint.range = &contrast_range; scanner->opt[OPT_G_CONTRAST].cap |= SANE_CAP_INACTIVE; scanner->opt[OPT_B_CONTRAST].name = "blue-contrast"; scanner->opt[OPT_B_CONTRAST].title = "Blue contrast"; scanner->opt[OPT_B_CONTRAST].desc = SANE_DESC_CONTRAST; scanner->opt[OPT_B_CONTRAST].type = SANE_TYPE_INT; scanner->opt[OPT_B_CONTRAST].unit = SANE_UNIT_NONE; scanner->opt[OPT_B_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; scanner->opt[OPT_B_CONTRAST].constraint.range = &contrast_range; scanner->opt[OPT_B_CONTRAST].cap |= SANE_CAP_INACTIVE; scanner->opt[OPT_EXPOSURE].name = "exposure"; scanner->opt[OPT_EXPOSURE].title = "Exposure"; scanner->opt[OPT_EXPOSURE].desc = ""; scanner->opt[OPT_EXPOSURE].type = SANE_TYPE_INT; scanner->opt[OPT_EXPOSURE].cap &= ~SANE_CAP_INACTIVE; scanner->opt[OPT_EXPOSURE].unit = SANE_UNIT_PERCENT; scanner->opt[OPT_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE; scanner->opt[OPT_EXPOSURE].constraint.range = &exposure_range; scanner->opt[OPT_R_EXPOSURE].name = "red-exposure"; scanner->opt[OPT_R_EXPOSURE].title = "Red exposure"; scanner->opt[OPT_R_EXPOSURE].desc = ""; scanner->opt[OPT_R_EXPOSURE].type = SANE_TYPE_INT; scanner->opt[OPT_R_EXPOSURE].cap |= SANE_CAP_INACTIVE; scanner->opt[OPT_R_EXPOSURE].unit = SANE_UNIT_PERCENT; scanner->opt[OPT_R_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE; scanner->opt[OPT_R_EXPOSURE].constraint.range = &exposure_range; scanner->opt[OPT_G_EXPOSURE].name = "green-exposure"; scanner->opt[OPT_G_EXPOSURE].title = "Green exposure"; scanner->opt[OPT_G_EXPOSURE].desc = ""; scanner->opt[OPT_G_EXPOSURE].type = SANE_TYPE_INT; scanner->opt[OPT_G_EXPOSURE].unit = SANE_UNIT_PERCENT; scanner->opt[OPT_G_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE; scanner->opt[OPT_G_EXPOSURE].constraint.range = &exposure_range; scanner->opt[OPT_G_EXPOSURE].cap |= SANE_CAP_INACTIVE; scanner->opt[OPT_B_EXPOSURE].name = "blue-exposure"; scanner->opt[OPT_B_EXPOSURE].title = "Blue exposre"; scanner->opt[OPT_B_EXPOSURE].desc = ""; scanner->opt[OPT_B_EXPOSURE].type = SANE_TYPE_INT; scanner->opt[OPT_B_EXPOSURE].unit = SANE_UNIT_PERCENT; scanner->opt[OPT_B_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE; scanner->opt[OPT_B_EXPOSURE].constraint.range = &exposure_range; scanner->opt[OPT_B_EXPOSURE].cap |= SANE_CAP_INACTIVE; if(scanner->LS>=2) { scanner->opt[OPT_R_EXPOSURE].cap &= ~SANE_CAP_INACTIVE; scanner->opt[OPT_G_EXPOSURE].cap &= ~SANE_CAP_INACTIVE; scanner->opt[OPT_B_EXPOSURE].cap &= ~SANE_CAP_INACTIVE; scanner->opt[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE; } scanner->opt[OPT_R_SHIFT].name = "red-shift"; scanner->opt[OPT_R_SHIFT].title = "Red shift"; scanner->opt[OPT_R_SHIFT].desc = ""; scanner->opt[OPT_R_SHIFT].type = SANE_TYPE_INT; scanner->opt[OPT_R_SHIFT].unit = SANE_UNIT_NONE; scanner->opt[OPT_R_SHIFT].constraint_type = SANE_CONSTRAINT_RANGE; scanner->opt[OPT_R_SHIFT].constraint.range = &shift_range; if(scanner->LS>=2) { scanner->opt[OPT_R_SHIFT].cap |= SANE_CAP_INACTIVE; } scanner->opt[OPT_G_SHIFT].name = "green-shift"; scanner->opt[OPT_G_SHIFT].title = "Green shift"; scanner->opt[OPT_G_SHIFT].desc = ""; scanner->opt[OPT_G_SHIFT].type = SANE_TYPE_INT; scanner->opt[OPT_G_SHIFT].unit = SANE_UNIT_NONE; scanner->opt[OPT_G_SHIFT].constraint_type = SANE_CONSTRAINT_RANGE; scanner->opt[OPT_G_SHIFT].constraint.range = &shift_range; if(scanner->LS>=2) { scanner->opt[OPT_G_SHIFT].cap |= SANE_CAP_INACTIVE; } scanner->opt[OPT_B_SHIFT].name = "blue-shift"; scanner->opt[OPT_B_SHIFT].title = "Blue shift"; scanner->opt[OPT_B_SHIFT].desc = ""; scanner->opt[OPT_B_SHIFT].type = SANE_TYPE_INT; scanner->opt[OPT_B_SHIFT].unit = SANE_UNIT_NONE; scanner->opt[OPT_B_SHIFT].constraint_type = SANE_CONSTRAINT_RANGE; scanner->opt[OPT_B_SHIFT].constraint.range = &shift_range; if(scanner->LS>=2) { scanner->opt[OPT_B_SHIFT].cap |= SANE_CAP_INACTIVE; } /* R+G+B gamma vector */ scanner->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR; scanner->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR; scanner->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR; scanner->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT; if (scanner->LS == 1) { scanner->opt[OPT_GAMMA_VECTOR].cap = SANE_CAP_INACTIVE; } scanner->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE; switch(scanner->LS) { case 0: scanner->opt[OPT_GAMMA_VECTOR].constraint.range = &gamma_range_8; scanner->lutlength=2048; break; case 1: scanner->opt[OPT_GAMMA_VECTOR].constraint.range = &gamma_range_9; scanner->lutlength=512; break; case 2: scanner->opt[OPT_GAMMA_VECTOR].constraint.range = &gamma_range_10; scanner->lutlength=1024; break; case 3: scanner->opt[OPT_GAMMA_VECTOR].constraint.range = &gamma_range_12; scanner->lutlength=4096; break; } scanner->opt[OPT_GAMMA_VECTOR].size = scanner->lutlength * sizeof (SANE_Word); scanner->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE; /* red gamma vector */ scanner->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; scanner->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R; scanner->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R; scanner->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT; scanner->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; scanner->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE; switch(scanner->LS) { case 0: scanner->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range_8; scanner->lutlength=2048; break; case 1: scanner->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range_9; scanner->lutlength=512; break; case 2: scanner->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range_10; scanner->lutlength=1024; break; case 3: scanner->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range_12; scanner->lutlength=4096; break; } scanner->opt[OPT_GAMMA_VECTOR_R].size = scanner->lutlength * sizeof (SANE_Word); scanner->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE; /* green gamma vector */ scanner->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; scanner->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G; scanner->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G; scanner->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT; scanner->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; scanner->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE; switch(scanner->LS) { case 0: scanner->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range_8; scanner->lutlength=2048; break; case 1: scanner->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range_9; scanner->lutlength=512; break; case 2: scanner->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range_10; scanner->lutlength=1024; break; case 3: scanner->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range_12; scanner->lutlength=4096; break; } scanner->opt[OPT_GAMMA_VECTOR_G].size = scanner->lutlength * sizeof (SANE_Word); scanner->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE; /* blue gamma vector */ scanner->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; scanner->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B; scanner->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B; scanner->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT; scanner->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; scanner->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE; switch(scanner->LS) { case 0: scanner->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range_8; scanner->lutlength=2048; break; case 1: scanner->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range_9; scanner->lutlength=512; break; case 2: scanner->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range_10; scanner->lutlength=1024; break; case 3: scanner->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range_12; scanner->lutlength=4096; break; } scanner->opt[OPT_GAMMA_VECTOR_B].size = scanner->lutlength * sizeof (SANE_Word); scanner->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE; /* ------------------------------ */ /* "Advanced" group: */ scanner->opt[OPT_ADVANCED_GROUP].title = "Advanced"; scanner->opt[OPT_ADVANCED_GROUP].desc = ""; scanner->opt[OPT_ADVANCED_GROUP].type = SANE_TYPE_GROUP; scanner->opt[OPT_ADVANCED_GROUP].cap = SANE_CAP_ADVANCED; scanner->opt[OPT_ADVANCED_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* preview */ scanner->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; scanner->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; scanner->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; scanner->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; /* Autofocus */ scanner->opt[OPT_AUTOFOCUS].name = "Autofocus"; scanner->opt[OPT_AUTOFOCUS].title ="Autofocus"; scanner->opt[OPT_AUTOFOCUS].desc = "When to do autofocussing"; scanner->opt[OPT_AUTOFOCUS].type = SANE_TYPE_STRING; scanner->opt[OPT_AUTOFOCUS].size = max_string_size (autofocus_mode_list); scanner->opt[OPT_AUTOFOCUS].constraint_type = SANE_CONSTRAINT_STRING_LIST; scanner->opt[OPT_AUTOFOCUS].constraint.string_list = autofocus_mode_list; scanner->opt[OPT_IRED_RED].name = "IRED cor. red"; scanner->opt[OPT_IRED_RED].title ="IRED cor. red"; scanner->opt[OPT_IRED_RED].desc = "Correction of infrared from red"; scanner->opt[OPT_IRED_RED].type = SANE_TYPE_INT; scanner->opt[OPT_IRED_RED].unit = SANE_UNIT_NONE; scanner->opt[OPT_IRED_RED].constraint_type = SANE_CONSTRAINT_RANGE; scanner->opt[OPT_IRED_RED].constraint.range = &gamma_range_8; scanner->opt[OPT_IRED_RED].cap |= SANE_CAP_ADVANCED; if(scanner->LS<2) { scanner->opt[OPT_IRED_RED].cap |= SANE_CAP_INACTIVE; } /* scanner->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; */ return SANE_STATUS_GOOD; } SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { char dev_name[PATH_MAX]; size_t len; FILE *fp; (void) authorize; DBG_INIT (); sanei_thread_init (); DBG (10, "sane_init\n"); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); fp = sanei_config_open (COOLSCAN_CONFIG_FILE); if (!fp) { attach_scanner ("/dev/scanner", 0); /* no config-file: /dev/scanner */ return SANE_STATUS_GOOD; } while (sanei_config_read (dev_name, sizeof (dev_name), fp)) { if (dev_name[0] == '#') continue; /* ignore line comments */ len = strlen (dev_name); if (!len) continue; /* ignore empty lines */ sanei_config_attach_matching_devices (dev_name, attach_one); /*attach_scanner (dev_name, 0);*/ } fclose (fp); return SANE_STATUS_GOOD; } void sane_exit (void) { Coolscan_t *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->obuffer); free (dev); } if (devlist) free (devlist); } /* ----------------------------- SANE GET DEVICES -------------------------- */ SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { Coolscan_t *dev; int i; (void) local_only; DBG (10, "sane_get_devices\n"); if (devlist) free (devlist); devlist = calloc (num_devices + 1, sizeof (devlist[0])); if (!devlist) return SANE_STATUS_NO_MEM; i = 0; for (dev = first_dev; i < num_devices; dev = dev->next) devlist[i++] = &dev->sane; devlist[i++] = 0; *device_list = devlist; return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { Coolscan_t *dev; SANE_Status status; DBG (10, "sane_open\n"); if (devicename[0]) { /* search for devicename */ for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->sane.name, devicename) == 0) { break; } } if (!dev) { status = attach_scanner (devicename, &dev); if (status != SANE_STATUS_GOOD) { return status; } } } else { dev = first_dev; /* empty devicname -> use first device */ } if (!dev) return SANE_STATUS_INVAL; dev->sfd = -1; dev->pipe = -1; dev->scanning = SANE_FALSE; init_options (dev); *handle = dev; return SANE_STATUS_GOOD; } void sane_close (SANE_Handle handle) { DBG (10, "sane_close\n"); if (((Coolscan_t *) handle)->scanning) do_cancel (handle); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { Coolscan_t *scanner = handle; DBG (10, "sane_get_option_descriptor %d\n", option); if ((unsigned) option >= NUM_OPTIONS) return 0; return &scanner->opt[option]; } /* static void worddump(char *comment, SANE_Word * p, int l) { int i; char line[128]; char *ptr; DBG (5, "%s\n", comment); ptr = line; for (i = 0; i < l; i++, p++) { if ((i % 8) == 0) { if (ptr != line) { *ptr = '\0'; DBG (5, "%s\n", line); ptr = line; } sprintf (ptr, "%3.3d:", i); ptr += 4; } sprintf (ptr, " %4.4d", *p); ptr += 5; } *ptr = '\0'; DBG (5, "%s\n", line); } */ SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { Coolscan_t *scanner = handle; SANE_Status status; SANE_Word cap; if (info) *info = 0; if (scanner->scanning) return SANE_STATUS_DEVICE_BUSY; if (option >= NUM_OPTIONS) return SANE_STATUS_INVAL; cap = scanner->opt[option].cap; if (action == SANE_ACTION_GET_VALUE) { DBG (10, "sane_control_option %d, get value\n", option); switch (option) { /* word options: */ case OPT_TL_X: *(SANE_Word *) val = SANE_FIX (iluToMm (scanner->tlx)); return SANE_STATUS_GOOD; case OPT_TL_Y: *(SANE_Word *) val = SANE_FIX (iluToMm (scanner->tly)); return SANE_STATUS_GOOD; case OPT_BR_X: *(SANE_Word *) val = SANE_FIX (iluToMm (scanner->brx)); return SANE_STATUS_GOOD; case OPT_BR_Y: *(SANE_Word *) val = SANE_FIX (iluToMm (scanner->bry)); return SANE_STATUS_GOOD; case OPT_PREVIEW: *(SANE_Word *) val = scanner->preview; return SANE_STATUS_GOOD; case OPT_AUTOFOCUS: switch(scanner->autofocus) { case AF_NEVER: strcpy (val,neverStr); break; case AF_PREVIEW:strcpy (val,previewStr); break; case AF_SCAN:if(scanner->LS>=2) strcpy (val,scanStr); break; case AF_PREANDSCAN:if(scanner->LS>=2) strcpy (val,preandscanStr); break; } return SANE_STATUS_GOOD; case OPT_NUM_OPTS: *(SANE_Word *) val = NUM_OPTIONS; return SANE_STATUS_GOOD; case OPT_RESOLUTION: *(SANE_Word *) val = resDivToVal (scanner->x_nres); return SANE_STATUS_GOOD; case OPT_PREVIEW_RESOLUTION: *(SANE_Word *) val = resDivToVal (scanner->x_p_nres); return SANE_STATUS_GOOD; case OPT_BIT_DEPTH: *(SANE_Word *) val = scanner->bits_per_color; return SANE_STATUS_GOOD; case OPT_CONTRAST: *(SANE_Word *) val = scanner->contrast - 128; return SANE_STATUS_GOOD; case OPT_R_CONTRAST: *(SANE_Word *) val = scanner->contrast_R - 128; return SANE_STATUS_GOOD; case OPT_G_CONTRAST: *(SANE_Word *) val = scanner->contrast_G - 128; return SANE_STATUS_GOOD; case OPT_B_CONTRAST: *(SANE_Word *) val = scanner->contrast_B - 128; return SANE_STATUS_GOOD; case OPT_BRIGHTNESS: *(SANE_Word *) val = scanner->brightness - 128; return SANE_STATUS_GOOD; case OPT_R_BRIGHTNESS: *(SANE_Word *) val = scanner->brightness_R - 128; return SANE_STATUS_GOOD; case OPT_G_BRIGHTNESS: *(SANE_Word *) val = scanner->brightness_G - 128; return SANE_STATUS_GOOD; case OPT_B_BRIGHTNESS: *(SANE_Word *) val = scanner->brightness_B - 128; return SANE_STATUS_GOOD; case OPT_EXPOSURE: *(SANE_Word *) val = scanner->exposure_R * 2; return SANE_STATUS_GOOD; case OPT_R_EXPOSURE: *(SANE_Word *) val = scanner->exposure_R * 2; return SANE_STATUS_GOOD; case OPT_G_EXPOSURE: *(SANE_Word *) val = scanner->exposure_G * 2; return SANE_STATUS_GOOD; case OPT_B_EXPOSURE: *(SANE_Word *) val = scanner->exposure_B * 2; return SANE_STATUS_GOOD; case OPT_R_SHIFT: *(SANE_Word *) val = scanner->shift_R - 128; return SANE_STATUS_GOOD; case OPT_G_SHIFT: *(SANE_Word *) val = scanner->shift_G - 128; return SANE_STATUS_GOOD; case OPT_B_SHIFT: *(SANE_Word *) val = scanner->shift_B - 128; return SANE_STATUS_GOOD; case OPT_IRED_RED: *(SANE_Word *) val = scanner->ired_red; return SANE_STATUS_GOOD; /* string options: */ case OPT_TYPE: strcpy (val, ((scanner->negative) ? negativeStr : positiveStr)); return SANE_STATUS_GOOD; case OPT_MODE: switch(scanner->colormode) { case RGB: strcpy (val,colorStr); break; case GREYSCALE:strcpy (val,grayStr); break; case RGBI:if(scanner->LS>=2) strcpy (val,rgbiStr); else strcpy (val,colorStr); break; case IRED:if(scanner->LS>=2) strcpy (val,iredStr); else strcpy (val,grayStr); break; } if (info) { *info |= SANE_INFO_RELOAD_PARAMS; } return SANE_STATUS_GOOD; case OPT_PRESCAN: *(SANE_Word *) val = (scanner->prescan) ? SANE_TRUE : SANE_FALSE; return SANE_STATUS_GOOD; case OPT_PRESCAN_NOW: return SANE_STATUS_GOOD; case OPT_RGB_CONTROL: *(SANE_Word *) val = (scanner->rgb_control) ? SANE_TRUE : SANE_FALSE; return SANE_STATUS_GOOD; case OPT_GAMMA_BIND: *(SANE_Word *) val = (scanner->gamma_bind) ? SANE_TRUE : SANE_FALSE; return SANE_STATUS_GOOD; case OPT_ANALOG_GAMMA: *(SANE_Word *) val = (scanner->analog_gamma_r) ? SANE_TRUE : SANE_FALSE; return SANE_STATUS_GOOD; case OPT_AVERAGING: *(SANE_Word *) val = (scanner->averaging) ? SANE_TRUE : SANE_FALSE; return SANE_STATUS_GOOD; case OPT_GAMMA_VECTOR: memcpy (val, scanner->gamma, scanner->opt[option].size); return SANE_STATUS_GOOD; case OPT_GAMMA_VECTOR_R: memcpy (val, scanner->gamma_r, scanner->opt[option].size); return SANE_STATUS_GOOD; case OPT_GAMMA_VECTOR_G: memcpy (val, scanner->gamma_g, scanner->opt[option].size); return SANE_STATUS_GOOD; case OPT_GAMMA_VECTOR_B: memcpy (val, scanner->gamma_b, scanner->opt[option].size); return SANE_STATUS_GOOD; case OPT_SOURCE: if (strcmp (val, "Automatic Slide Feeder") == 0) { /* Feed/Discharge/update filename/etc */ } else { /* Reset above */ } if (info) { *info |= SANE_INFO_RELOAD_PARAMS; } } } else if (action == SANE_ACTION_SET_VALUE) { DBG (10, "sane_control_option %d, set value\n", option); if (!SANE_OPTION_IS_ACTIVE (cap)) return SANE_STATUS_INVAL; if (!SANE_OPTION_IS_SETTABLE (cap)) return SANE_STATUS_INVAL; status = sanei_constrain_value (scanner->opt + option, val, info); if (status != SANE_STATUS_GOOD) return status; switch (option) { case OPT_GAMMA_BIND: scanner->gamma_bind = (*(SANE_Word *) val == SANE_TRUE); if (scanner->LS != 1) { if (scanner->gamma_bind) { scanner->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; scanner->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; scanner->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; scanner->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; } else { scanner->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; scanner->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; scanner->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; scanner->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; } } if (info) { *info |= SANE_INFO_RELOAD_OPTIONS; } return SANE_STATUS_GOOD; case OPT_ANALOG_GAMMA: scanner->analog_gamma_r = scanner->analog_gamma_g = scanner->analog_gamma_b = (*(SANE_Word *) val == SANE_TRUE); return SANE_STATUS_GOOD; case OPT_AVERAGING: scanner->averaging = (*(SANE_Word *) val == SANE_TRUE); return SANE_STATUS_GOOD; case OPT_PRESCAN: scanner->prescan = (*(SANE_Word *) val == SANE_TRUE); return SANE_STATUS_GOOD; case OPT_PRESCAN_NOW: do_prescan_now(scanner); return SANE_STATUS_GOOD; case OPT_BIT_DEPTH: scanner->bits_per_color=(*(SANE_Word *)val); if (info) *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case OPT_RGB_CONTROL: scanner->rgb_control = (*(SANE_Word *) val == SANE_TRUE); if (scanner->rgb_control) { scanner->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; scanner->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; scanner->opt[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE; scanner->opt[OPT_R_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; scanner->opt[OPT_G_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; scanner->opt[OPT_B_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; scanner->opt[OPT_R_CONTRAST].cap &= ~SANE_CAP_INACTIVE; scanner->opt[OPT_G_CONTRAST].cap &= ~SANE_CAP_INACTIVE; scanner->opt[OPT_B_CONTRAST].cap &= ~SANE_CAP_INACTIVE; scanner->opt[OPT_R_EXPOSURE].cap &= ~SANE_CAP_INACTIVE; scanner->opt[OPT_G_EXPOSURE].cap &= ~SANE_CAP_INACTIVE; scanner->opt[OPT_B_EXPOSURE].cap &= ~SANE_CAP_INACTIVE; scanner->contrast_R = 128; scanner->contrast_G = 128; scanner->contrast_B = 128; scanner->brightness_R = 128; scanner->brightness_G = 128; scanner->brightness_B = 128; scanner->exposure_R = 50; scanner->exposure_G = 50; scanner->exposure_B = 50; } else { scanner->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; scanner->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE; scanner->opt[OPT_EXPOSURE].cap &= ~SANE_CAP_INACTIVE; scanner->contrast = 128; scanner->brightness = 128; scanner->exposure_R = 50; scanner->exposure_G = 50; scanner->exposure_B = 50; scanner->opt[OPT_R_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; scanner->opt[OPT_G_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; scanner->opt[OPT_B_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; scanner->opt[OPT_R_CONTRAST].cap |= SANE_CAP_INACTIVE; scanner->opt[OPT_G_CONTRAST].cap |= SANE_CAP_INACTIVE; scanner->opt[OPT_B_CONTRAST].cap |= SANE_CAP_INACTIVE; scanner->opt[OPT_R_EXPOSURE].cap |= SANE_CAP_INACTIVE; scanner->opt[OPT_G_EXPOSURE].cap |= SANE_CAP_INACTIVE; scanner->opt[OPT_B_EXPOSURE].cap |= SANE_CAP_INACTIVE; } if (info) { *info |= SANE_INFO_RELOAD_OPTIONS; } return SANE_STATUS_GOOD; case OPT_RESOLUTION: scanner->y_nres = scanner->x_nres = resValToDiv (*(SANE_Word *) val); if (info) *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case OPT_PREVIEW_RESOLUTION: scanner->y_p_nres = scanner->x_p_nres = resValToDiv (*(SANE_Word *) val); if (info) *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case OPT_TL_X: scanner->tlx = mmToIlu (SANE_UNFIX (*(SANE_Word *) val)); if (info) *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case OPT_TL_Y: scanner->tly = mmToIlu (SANE_UNFIX (*(SANE_Word *) val)); if (info) *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case OPT_BR_X: scanner->brx = mmToIlu (SANE_UNFIX (*(SANE_Word *) val)); if (info) *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case OPT_BR_Y: scanner->bry = mmToIlu (SANE_UNFIX (*(SANE_Word *) val)); if (info) *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case OPT_NUM_OPTS: return SANE_STATUS_GOOD; case OPT_PREVIEW: scanner->preview = *(SANE_Word *) val; return SANE_STATUS_GOOD; case OPT_AUTOFOCUS: if(strcmp(val,neverStr)==0) { scanner->autofocus=AF_NEVER; } if(strcmp(val,previewStr)==0) { scanner->autofocus=AF_PREVIEW; } if(strcmp(val,scanStr)==0) { scanner->autofocus=AF_SCAN; } if(strcmp(val,preandscanStr)==0) { scanner->autofocus=AF_PREANDSCAN;; } return SANE_STATUS_GOOD; case OPT_CONTRAST: scanner->contrast = *(SANE_Word *) val + 128; return SANE_STATUS_GOOD; case OPT_R_CONTRAST: scanner->contrast_R = *(SANE_Word *) val + 128; return SANE_STATUS_GOOD; case OPT_G_CONTRAST: scanner->contrast_G = *(SANE_Word *) val + 128; return SANE_STATUS_GOOD; case OPT_B_CONTRAST: scanner->contrast_B = *(SANE_Word *) val + 128; return SANE_STATUS_GOOD; case OPT_BRIGHTNESS: scanner->brightness = *(SANE_Word *) val + 128; return SANE_STATUS_GOOD; case OPT_R_BRIGHTNESS: scanner->brightness_R = *(SANE_Word *) val + 128; return SANE_STATUS_GOOD; case OPT_G_BRIGHTNESS: scanner->brightness_G = *(SANE_Word *) val + 128; return SANE_STATUS_GOOD; case OPT_B_BRIGHTNESS: scanner->brightness_B = *(SANE_Word *) val + 128; return SANE_STATUS_GOOD; case OPT_EXPOSURE: scanner->exposure_R = *(SANE_Word *) val / 2; scanner->exposure_G = *(SANE_Word *) val / 2; scanner->exposure_B = *(SANE_Word *) val / 2; return SANE_STATUS_GOOD; case OPT_R_EXPOSURE: scanner->exposure_R = *(SANE_Word *) val / 2; return SANE_STATUS_GOOD; case OPT_G_EXPOSURE: scanner->exposure_G = *(SANE_Word *) val / 2; return SANE_STATUS_GOOD; case OPT_B_EXPOSURE: scanner->exposure_B = *(SANE_Word *) val / 2; return SANE_STATUS_GOOD; case OPT_R_SHIFT: scanner->shift_R = *(SANE_Word *) val + 128; return SANE_STATUS_GOOD; case OPT_G_SHIFT: scanner->shift_G = *(SANE_Word *) val + 128; return SANE_STATUS_GOOD; case OPT_B_SHIFT: scanner->shift_B = *(SANE_Word *) val + 128; return SANE_STATUS_GOOD; case OPT_IRED_RED: scanner->ired_red= *(SANE_Word *) val; return SANE_STATUS_GOOD; case OPT_SOURCE: scanner->asf = (strcmp (val, "Automatic...") == 0); return SANE_STATUS_GOOD; case OPT_TYPE: scanner->negative = (strcmp (val, negativeStr) == 0); if (info) { *info |= SANE_INFO_RELOAD_PARAMS; } return SANE_STATUS_GOOD; case OPT_MODE: if(strcmp(val,colorStr)==0) { scanner->colormode=RGB; scanner->colormode_p=RGB; } if(strcmp(val,grayStr)==0) { scanner->colormode=GREYSCALE; scanner->colormode_p=GREYSCALE; } if(strcmp(val,rgbiStr)==0) { scanner->colormode=RGBI; scanner->colormode_p=RGB; } if(strcmp(val,iredStr)==0) { scanner->colormode=IRED; scanner->colormode_p=GREYSCALE; } if (info) { *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; } return SANE_STATUS_GOOD; case OPT_GAMMA_VECTOR: memcpy (scanner->gamma, val, scanner->opt[option].size); if(scanner->LS>2) Calc_fix_LUT(scanner); return SANE_STATUS_GOOD; case OPT_GAMMA_VECTOR_R: memcpy (scanner->gamma_r, val, scanner->opt[option].size); if(scanner->LS>2) Calc_fix_LUT(scanner); return SANE_STATUS_GOOD; case OPT_GAMMA_VECTOR_G: memcpy (scanner->gamma_g, val, scanner->opt[option].size); if(scanner->LS>2) Calc_fix_LUT(scanner); return SANE_STATUS_GOOD; case OPT_GAMMA_VECTOR_B: memcpy (scanner->gamma_b, val, scanner->opt[option].size); if(scanner->LS>2) Calc_fix_LUT(scanner); return SANE_STATUS_GOOD; } /* switch */ } /* else */ return SANE_STATUS_INVAL; } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { Coolscan_t *scanner = handle; DBG (10, "sane_get_parameters"); switch(scanner->colormode) { case RGB: params->format = SANE_FRAME_RGB; break; #ifdef HAS_IRED case RGBI: params->format = SANE_FRAME_RGBA; break; #endif /* HAS_RGBI */ case GREYSCALE: params->format = SANE_FRAME_GRAY; break; } params->depth = scanner->bits_per_color>8?16:8; params->pixels_per_line = pixels_per_line (scanner); params->lines = lines_per_scan (scanner); params->bytes_per_line = write_bytes_per_line (scanner); params->last_frame = 1; return SANE_STATUS_GOOD; } static int swap_res (Coolscan_t * s) { if (s->preview) { /* swap preview/scan resolutions */ int xres, yres, cmode; xres = s->x_nres; yres = s->y_nres; s->x_nres = s->x_p_nres; s->y_nres = s->y_p_nres; s->x_p_nres = xres; s->y_p_nres = yres; cmode=s->colormode; s->colormode=s->colormode_p; s->colormode_p=cmode; } return 0; } SANE_Status sane_start (SANE_Handle handle) { Coolscan_t *scanner = handle; int fds[2]; DBG (10, "sane_start\n"); if (scanner->scanning == SANE_TRUE) return SANE_STATUS_DEVICE_BUSY; if (scanner->sfd < 0) { /* first call */ if (sanei_scsi_open (scanner->sane.name, &(scanner->sfd), sense_handler, 0) != SANE_STATUS_GOOD) { DBG (1, "sane_start: open of %s failed:\n", scanner->sane.name); return SANE_STATUS_INVAL; } } scanner->scanning = SANE_TRUE; if (coolscan_check_values (scanner) != 0) { /* Verify values */ DBG (1, "ERROR: invalid scan-values\n"); scanner->scanning = SANE_FALSE; coolscan_give_scanner (scanner); sanei_scsi_close (scanner->sfd); scanner->sfd = -1; return SANE_STATUS_INVAL; } if (coolscan_grab_scanner (scanner)) { sanei_scsi_close (scanner->sfd); scanner->sfd = -1; DBG (5, "WARNING: unable to reserve scanner: device busy\n"); scanner->scanning = SANE_FALSE; return SANE_STATUS_DEVICE_BUSY; } /* hoho, step 2c, -perm */ coolscan_object_feed (scanner); swap_res (scanner); if (!scanner->preview) { if(scanner->autofocus & 0x02) { coolscan_autofocus (scanner); } } else { if(scanner->autofocus & 0x01) { coolscan_autofocus (scanner); } if (scanner->prescan) { prescan (scanner); if(scanner->LS<2) { get_internal_info(scanner); } coolscan_get_window_param (scanner,1); } } /*read_LUT(scanner); */ if(scanner->LS<2) { send_LUT (scanner); coolscan_set_window_param (scanner, 0); coolscan_get_window_param (scanner,0); coolscan_start_scan (scanner); } else { coolscan_set_window_param (scanner, 0); send_LUT (scanner); Calc_fix_LUT(scanner); coolscan_start_scan (scanner); wait_scanner (scanner); coolscan_get_window_param (scanner,0); } DBG (10, "bytes per line = %d\n", scan_bytes_per_line (scanner)); DBG (10, "pixels_per_line = %d\n", pixels_per_line (scanner)); DBG (10, "lines = %d\n", lines_per_scan (scanner)); DBG (10, "negative = %d\n", scanner->negative); DBG (10, "brightness (halftone) = %d\n", scanner->brightness); DBG (10, "contrast (halftone) = %d\n", scanner->contrast); DBG (10, "fast preview function = %d\n", scanner->preview); /* create a pipe, fds[0]=read-fd, fds[1]=write-fd */ if (pipe (fds) < 0) { DBG (1, "ERROR: could not create pipe\n"); swap_res (scanner); scanner->scanning = SANE_FALSE; coolscan_give_scanner (scanner); sanei_scsi_close (scanner->sfd); scanner->sfd = -1; return SANE_STATUS_IO_ERROR; } scanner->pipe = fds[0]; scanner->reader_fds = fds[1]; scanner->reader_pid = sanei_thread_begin( reader_process, (void*)scanner ); if (!sanei_thread_is_valid (scanner->reader_pid)) { DBG (1, "sane_start: sanei_thread_begin failed (%s)\n", strerror (errno)); return SANE_STATUS_NO_MEM; } if (sanei_thread_is_forked ()) { close (scanner->reader_fds); scanner->reader_fds = -1; } return SANE_STATUS_GOOD; } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { Coolscan_t *scanner = handle; ssize_t nread; *len = 0; nread = read (scanner->pipe, buf, max_len); DBG (10, "sane_read: read %ld bytes\n", (long) nread); if (!(scanner->scanning)) { return do_cancel (scanner); } if (nread < 0) { if (errno == EAGAIN) { return SANE_STATUS_GOOD; } else { do_cancel (scanner); return SANE_STATUS_IO_ERROR; } } *len = nread; if (nread == 0) return do_eof (scanner); /* close pipe */ return SANE_STATUS_GOOD; } void sane_cancel (SANE_Handle handle) { Coolscan_t *s = handle; if (sanei_thread_is_valid (s->reader_pid)) { sanei_thread_kill ( s->reader_pid ); sanei_thread_waitpid( s->reader_pid, NULL ); sanei_thread_invalidate (s->reader_pid); } swap_res (s); s->scanning = SANE_FALSE; } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { Coolscan_t *scanner = handle; DBG (10, "sane_set_io_mode: non_blocking=%d\n", non_blocking); if (!scanner->scanning) return SANE_STATUS_INVAL; if (fcntl (scanner->pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0) return SANE_STATUS_IO_ERROR; return SANE_STATUS_GOOD; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { Coolscan_t *scanner = handle; DBG (10, "sane_get_select_fd\n"); if (!scanner->scanning) { return SANE_STATUS_INVAL; } *fd = scanner->pipe; return SANE_STATUS_GOOD; } backends-1.3.0/backend/coolscan.conf.in000066400000000000000000000000421456256263500177430ustar00rootroot00000000000000scsi Nikon * Scanner /dev/scanner backends-1.3.0/backend/coolscan.h000066400000000000000000000237111456256263500166500ustar00rootroot00000000000000/* --------------------------------------------------------------------- */ /* coolscan.h - headerfile for SANE-backend for coolscan scanners 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, see . 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 coolscan_h #define coolscan_h #include "sys/types.h" enum Coolscan_Option { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_MODE, OPT_SOURCE, OPT_RESOLUTION, OPT_PREVIEW_RESOLUTION, OPT_TYPE, OPT_BIT_DEPTH, OPT_PRESCAN, OPT_PRESCAN_NOW, OPT_GEOMETRY_GROUP, OPT_TL_X, /* top-left x */ OPT_TL_Y, /* top-left y */ OPT_BR_X, /* bottom-right x */ OPT_BR_Y, /* bottom-right y */ OPT_ENHANCEMENT_GROUP, OPT_GAMMA_BIND, OPT_ANALOG_GAMMA, OPT_AVERAGING, OPT_RGB_CONTROL, OPT_BRIGHTNESS, OPT_R_BRIGHTNESS, OPT_G_BRIGHTNESS, OPT_B_BRIGHTNESS, OPT_CONTRAST, OPT_R_CONTRAST, OPT_G_CONTRAST, OPT_B_CONTRAST, OPT_EXPOSURE, OPT_R_EXPOSURE, OPT_G_EXPOSURE, OPT_B_EXPOSURE, OPT_R_SHIFT, OPT_G_SHIFT, OPT_B_SHIFT, OPT_ADVANCED_GROUP, OPT_PREVIEW, /* preview */ OPT_AUTOFOCUS, /* autofocus */ OPT_IRED_RED, OPT_GAMMA_VECTOR, OPT_GAMMA_VECTOR_R, OPT_GAMMA_VECTOR_G, OPT_GAMMA_VECTOR_B, /* must come last: */ NUM_OPTIONS }; typedef struct Image_Pos { int start; /* start position of image on film strip */ int end; /* end position of image on film strip */ int offset /* always 0 */; int height; /* image height always 2591 */ } Image_Pos_t; typedef struct Coolscan { struct Coolscan *next; SANE_Option_Descriptor opt[NUM_OPTIONS]; SANE_Pid reader_pid; int reader_fds; int pipe; int scanning; /*--------------------------*/ SANE_Device sane; SANE_Range dpi_range; SANE_Range x_range; SANE_Range y_range; /*--------------------------*/ /* buffer used for scsi-transfer and writing*/ unsigned char *buffer; unsigned char *obuffer; unsigned int row_bufsize; char *devicename; /* name of the scanner device */ int sfd; /* output file descriptor, scanner device */ char vendor[9]; /* will be Nikon */ char product[17]; /* e.g. "LS-1000 " or so */ char version[5]; /* e.g. V1.6 */ int LS; /* index in scanner_str */ int cont; /* continue although scanner is unknown */ int verbose; /* 1,2=output information */ int asf; /* Automatic Slide Feeder enabled? */ int MUD; /* Measurement Unit Divisor (1200 or 2700) */ int inquiry_len; /* length of inquiry return block [36] */ int inquiry_wdb_len; /* length of window descriptor block [117] */ int wdb_len; /* use this length of WDB */ double width; /* use this width of scan-area */ double length; /* use this length of scan-area */ int x_nres; int y_nres; int x_p_nres; /* same as above, but apply to preview */ int y_p_nres; int tlx; /* Left edge in 'Internal Length Units'. */ int tly; /* Top edge in ILU */ int brx; /* Right edge in ILU. */ int bry; /* Bottom edge in ILU. */ int bits_per_color; /* bits per color (8/10/12) */ int bits_per_pixel; /* bits per pixel (24/30/40) */ int negative; /* Negative/positive object */ int dropoutcolor; /* Which color to scan when gray */ int transfermode; /**/ int gammaselection; /* Linear/Monitor*/ int shading; int averaging; int brightness_R; int brightness_G; int brightness_B; int contrast_R; int contrast_G; int contrast_B; int exposure_R; int exposure_G; int exposure_B; int shift_R; int shift_G; int shift_B; int set_auto; /* 0 or 1, don't know what it is */ int preview; /* 1 if preview */ int autofocus; /* when to do autofocus */ #define AF_NEVER 0x00 #define AF_PREVIEW 0x01 #define AF_SCAN 0x02 #define AF_PREANDSCAN 0x03 int colormode; /* GREYSCALE or RGB */ int colormode_p; /* GREYSCALE or RGB for preview */ #define GREYSCALE 0x01 #define RGB 0x07 #define IRED 0x08 #define RGBI 0x0f int low_byte_first; /* 1 if little-endian - 0 if big-endian */ /* Internal information */ int adbits; /* Number of A/D bits [8 or 12] */ int outputbits; /* Number of output image data bits [8] */ int maxres; /* Maximum resolution [2700] (dpi) */ int xmax; /* X-axis coordinate maximum value (basic measurement unit when measurement unit divisor = 1200) [1151] */ int ymax; /* Y-axis coordinate maximum value (basic measurement unit when measurement unit divisor = 1200) [1727] */ int xmaxpix; /* X-axis coordinate maximum value (pixel address value) [2591] */ int ymaxpix; /* Y-axis coordinate maximum value (pixel address value) [3887] */ int ycurrent; /* Current stage position (Y-axis direction pixel address) [0-7652] */ int currentfocus; /* Current focus position (focus direction address) [0-200] */ int currentscanpitch; /* Current scan pitch [1-25] */ int autofeeder; /* Provision of auto feeder [Yes: 1, No: 0] */ int analoggamma; /* Analog gamma support [Yes: 1, No: 0] */ int derr[8]; /* Device error code (0 is latest, 7 oldest) */ int wbetr_r; /* White balance exposure time variable (R) */ int webtr_g; /* White balance exposure time variable (G) */ int webtr_b; /* White balance exposure time variable (B) */ int pretv_r; /* Prescan result exposure time variable (R) */ int pretv_g; /* Prescan result exposure time variable (G) */ int pretv_b; /* Prescan result exposure time variable (B) */ int cetv_r; /* Current exposure time variable (R) */ int cetv_g; /* Current exposure time variable (G) */ int cetv_b; /* Current exposure time variable (B) */ int ietu_r; /* Internal exposure time unit (R) */ int ietu_g; /* Internal exposure time unit (G) */ int ietu_b; /* Internal exposure time unit (B) */ int limitcondition; /* Condition of each limit SW, DIP SW, etc. */ int offsetdata_r; /* Offset data (R) */ int offsetdata_g; /* Offset data (G) */ int offsetdata_b; /* Offset data (B) */ char power_on_errors[8]; /* Records of error code at power on */ /* End of internal information */ int brightness; /* (128) cbhs_range 0-255, halftone mode */ int contrast; /* (128) cbhs_range 0-255, halftone-mode */ int prescan; /* */ int rgb_control; /* */ int gamma_bind; /* TRUE -> RGB */ int lutlength; /* length of gamma table */ int max_lut_val; /* maximum value in lut */ SANE_Word gamma[4096]; /* gamma value for RGB */ SANE_Word gamma_r[4096]; /* gamma value for red */ SANE_Word gamma_g[4096]; /* gamma value for green */ SANE_Word gamma_b[4096]; /* gamma value for blue */ int luti[4096]; /* lut value for infrared */ int lutr[4096]; /* lut value for red */ int lutg[4096]; /* lut value for green */ int lutb[4096]; /* lut value for blue */ char *gamma_file_r; /* file for gamma download */ char *gamma_file_g; /* file for gamma download */ char *gamma_file_b; /* file for gamma download */ int analog_gamma_r; /* analog gamma red and grey */ int analog_gamma_g; /* analog gamma green */ int analog_gamma_b; /* analog gamma blue */ /* Infrared correction values */ int ired_red; int ired_green; int ired_blue; int feeder; /* type of feeder used */ int numima; /* number of images on film strip */ int posima; /* current image */ Image_Pos_t ipos[6]; /* positions for 6 images */ #define STRIP_FEEDER 1 #define MOUNT_FEEDER 2 } Coolscan_t; typedef struct { char *scanner; char *inquiry; int inquiry_len; } inquiry_blk; /* ==================================================================== */ /* names of scanners that are supported because */ /* the inquiry_return_block is ok and driver is tested */ static char *scanner_str[] = { "COOLSCAN II ", "LS-1000 ", "COOLSCANIII ", "LS-2000 ", }; #define known_scanners 4 /* Comment this line if you haven't patched sane.h to include SANE_FRAME_RGBA */ /* #define HAS_IRED 1 */ #endif /* coolscan-sane_h */ backends-1.3.0/backend/coolscan2.c000066400000000000000000002363041456256263500167310ustar00rootroot00000000000000/* ========================================================================= */ /* SANE - Scanner Access Now Easy. coolscan2.c , version 0.1.8 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, see . 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 Nikon Coolscan film scanners. Written by András Major (andras@users.sourceforge.net), 2001-2002. The developers wish to express their thanks to Nikon Corporation for providing technical information and thus making this backend possible. */ /* ========================================================================= */ /* ========================================================================= */ /* 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 0.1.6, 14/06/2002, andras: types etc. fixed, fixes for LS-8000 0.1.5, 26/04/2002, andras: lots of minor fixes related to saned 0.1.4, 22/04/2002, andras: first version to be included in SANE CVS */ /* ========================================================================= */ #ifdef _AIX # include "../include/lalloca.h" /* MUST come first for AIX! */ #endif #include "../include/sane/config.h" #include "../include/lalloca.h" #include #include #include #include #include #include #include /* #include #include */ #include "../include/_stdint.h" #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_scsi.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_debug.h" #include "../include/sane/sanei_config.h" #define BACKEND_NAME coolscan2 #include "../include/sane/sanei_backend.h" /* must be last */ #define CS2_VERSION_MAJOR 0 #define CS2_VERSION_MINOR 1 #define CS2_REVISION 8 #define CS2_CONFIG_FILE "coolscan2.conf" #define WSIZE (sizeof (SANE_Word)) /* #define CS2_BLEEDING_EDGE */ /* ========================================================================= */ /* typedefs */ typedef enum { 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; typedef enum { CS2_INTERFACE_UNKNOWN, CS2_INTERFACE_SCSI, /* includes IEEE1394 via SBP2 */ CS2_INTERFACE_USB } cs2_interface_t; typedef enum { CS2_PHASE_NONE = 0x00, CS2_PHASE_STATUS = 0x01, CS2_PHASE_OUT = 0x02, CS2_PHASE_IN = 0x03, CS2_PHASE_BUSY = 0x04 } cs2_phase_t; typedef enum { CS2_SCAN_NORMAL, CS2_SCAN_AE, CS2_SCAN_AE_WB } cs2_scan_t; typedef enum { CS2_INFRARED_OFF, CS2_INFRARED_IN, CS2_INFRARED_OUT } cs2_infrared_t; typedef enum { CS2_STATUS_READY = 0, CS2_STATUS_BUSY = 1, CS2_STATUS_NO_DOCS = 2, CS2_STATUS_PROCESSING = 4, CS2_STATUS_ERROR = 8, CS2_STATUS_REISSUE = 16, CS2_STATUS_ALL = 31 /* sum of all others */ } cs2_status_t; typedef enum { CS2_OPTION_NUM = 0, CS2_OPTION_PREVIEW, CS2_OPTION_NEGATIVE, CS2_OPTION_INFRARED, CS2_OPTION_SAMPLES_PER_SCAN, CS2_OPTION_DEPTH, CS2_OPTION_EXPOSURE, CS2_OPTION_EXPOSURE_R, CS2_OPTION_EXPOSURE_G, CS2_OPTION_EXPOSURE_B, CS2_OPTION_SCAN_AE, CS2_OPTION_SCAN_AE_WB, CS2_OPTION_LUT_R, CS2_OPTION_LUT_G, CS2_OPTION_LUT_B, CS2_OPTION_RES, CS2_OPTION_RESX, CS2_OPTION_RESY, CS2_OPTION_RES_INDEPENDENT, CS2_OPTION_PREVIEW_RESOLUTION, CS2_OPTION_FRAME, CS2_OPTION_SUBFRAME, CS2_OPTION_XMIN, CS2_OPTION_XMAX, CS2_OPTION_YMIN, CS2_OPTION_YMAX, CS2_OPTION_LOAD, CS2_OPTION_EJECT, CS2_OPTION_RESET, CS2_OPTION_FOCUS_ON_CENTRE, CS2_OPTION_FOCUS, CS2_OPTION_AUTOFOCUS, CS2_OPTION_FOCUSX, CS2_OPTION_FOCUSY, CS2_N_OPTIONS /* must be last -- counts number of enum items */ } cs2_option_t; typedef unsigned int cs2_pixel_t; typedef struct { /* interface */ cs2_interface_t interface; int fd; SANE_Byte *send_buf, *recv_buf; size_t send_buf_size, recv_buf_size; size_t n_cmd, n_send, n_recv; /* device characteristics */ char vendor_string[9], product_string[17], revision_string[5]; cs2_type_t type; int maxbits; unsigned int resx_optical, resx_min, resx_max, *resx_list, resx_n_list; unsigned int resy_optical, resy_min, resy_max, *resy_list, resy_n_list; unsigned long boundaryx, boundaryy; unsigned long frame_offset; unsigned int unit_dpi; double unit_mm; int n_frames; int focus_min, focus_max; /* settings */ SANE_Bool preview, negative, infrared; int samples_per_scan, depth, real_depth, bytes_per_pixel, shift_bits, n_colour_in, n_colour_out; cs2_pixel_t n_lut; cs2_pixel_t *lut_r, *lut_g, *lut_b, *lut_neutral; unsigned long resx, resy, res, res_independent, res_preview; unsigned long xmin, xmax, ymin, ymax; int i_frame; double subframe; unsigned int real_resx, real_resy, real_pitchx, real_pitchy; 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]; SANE_Bool focus_on_centre; unsigned long focusx, focusy, real_focusx, real_focusy; int focus; /* status */ SANE_Bool scanning; cs2_infrared_t infrared_stage, infrared_next; SANE_Byte *infrared_buf; size_t n_infrared_buf, infrared_index; SANE_Byte *line_buf; ssize_t n_line_buf, i_line_buf; unsigned long sense_key, sense_asc, sense_ascq, sense_info; unsigned long sense_code; cs2_status_t status; size_t xfer_position, xfer_bytes_total; /* SANE stuff */ SANE_Option_Descriptor option_list[CS2_N_OPTIONS]; } cs2_t; /* ========================================================================= */ /* prototypes */ static SANE_Status cs2_open (const char *device, cs2_interface_t interface, cs2_t ** sp); static void cs2_close (cs2_t * s); static SANE_Status cs2_attach (const char *dev); static SANE_Status cs2_scsi_sense_handler (int fd, u_char * sense_buffer, void *arg); static SANE_Status cs2_parse_sense_data (cs2_t * s); static void cs2_init_buffer (cs2_t * s); static SANE_Status cs2_pack_byte (cs2_t * s, SANE_Byte byte); static SANE_Status cs2_parse_cmd (cs2_t * s, char *text); static SANE_Status cs2_grow_send_buffer (cs2_t * s); static SANE_Status cs2_issue_cmd (cs2_t * s); static cs2_phase_t cs2_phase_check (cs2_t * s); static SANE_Status cs2_set_boundary (cs2_t *s); static SANE_Status cs2_scanner_ready (cs2_t * s, int flags); static SANE_Status cs2_page_inquiry (cs2_t * s, int page); static SANE_Status cs2_full_inquiry (cs2_t * s); static SANE_Status cs2_execute (cs2_t * s); static SANE_Status cs2_load (cs2_t * s); static SANE_Status cs2_eject (cs2_t * s); static SANE_Status cs2_reset (cs2_t * s); static SANE_Status cs2_focus (cs2_t * s); static SANE_Status cs2_autofocus (cs2_t * s); static SANE_Status cs2_get_exposure (cs2_t * s); static SANE_Status cs2_convert_options (cs2_t * s); static SANE_Status cs2_scan (cs2_t * s, cs2_scan_t type); static void *cs2_xmalloc (size_t size); static void *cs2_xrealloc (void *p, size_t size); static void cs2_xfree (const void *p); /* ========================================================================= */ /* global variables */ static int cs2_colour_list[] = { 1, 2, 3, 9 }; static SANE_Device **device_list = NULL; static int n_device_list = 0; static cs2_interface_t try_interface = CS2_INTERFACE_UNKNOWN; static int open_devices = 0; /* ========================================================================= */ /* SANE entry points */ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { DBG_INIT (); DBG (10, "sane_init() called.\n"); DBG (1, "coolscan2 backend, version %i.%i.%i initializing.\n", CS2_VERSION_MAJOR, CS2_VERSION_MINOR, CS2_REVISION); (void) authorize; /* to shut up compiler */ if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); sanei_usb_init (); return SANE_STATUS_GOOD; } void sane_exit (void) { int i; DBG (10, "sane_exit() called.\n"); for (i = 0; i < n_device_list; i++) { cs2_xfree (device_list[i]->name); cs2_xfree (device_list[i]->vendor); cs2_xfree (device_list[i]->model); cs2_xfree (device_list[i]); } cs2_xfree (device_list); } SANE_Status sane_get_devices (const SANE_Device *** list, SANE_Bool local_only) { char line[PATH_MAX], *p; FILE *config; (void) local_only; /* to shut up compiler */ DBG (10, "sane_get_devices() called.\n"); if (device_list) DBG (6, "sane_get_devices(): Device list already populated, not probing again.\n"); else { if (open_devices) { DBG (4, "sane_get_devices(): Devices open, not scanning for scanners.\n"); return SANE_STATUS_IO_ERROR; } config = sanei_config_open (CS2_CONFIG_FILE); if (config) { DBG (4, "sane_get_devices(): Reading config file.\n"); while (sanei_config_read (line, sizeof (line), config)) { p = line; p += strspn (line, " \t"); if (strlen (p) && (p[0] != '\n') && (p[0] != '#')) cs2_open (line, CS2_INTERFACE_UNKNOWN, NULL); } fclose (config); } else { DBG (4, "sane_get_devices(): No config file found.\n"); cs2_open ("auto", CS2_INTERFACE_UNKNOWN, NULL); } switch (n_device_list) { case 0: DBG (6, "sane_get_devices(): No devices detected.\n"); break; case 1: DBG (6, "sane_get_devices(): 1 device detected.\n"); break; default: DBG (6, "sane_get_devices(): %i devices detected.\n", n_device_list); break; } } *list = (const SANE_Device **) device_list; return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const name, SANE_Handle * h) { SANE_Status status; cs2_t *s; int i_option; unsigned int i_list; SANE_Option_Descriptor o; SANE_Word *word_list; SANE_Range *range = NULL; int alloc_failed = 0; DBG (10, "sane_open() called.\n"); status = cs2_open (name, CS2_INTERFACE_UNKNOWN, &s); if (status) return status; *h = (SANE_Handle) s; /* get device properties */ s->lut_r = s->lut_g = s->lut_b = s->lut_neutral = NULL; s->resx_list = s->resy_list = NULL; s->resx_n_list = s->resy_n_list = 0; status = cs2_full_inquiry (s); if (status) return status; /* option descriptors */ for (i_option = 0; i_option < CS2_N_OPTIONS; i_option++) { o.name = o.title = o.desc = NULL; o.type = SANE_TYPE_BOOL; o.unit = SANE_UNIT_NONE; o.size = o.cap = 0; o.constraint_type = SANE_CONSTRAINT_NONE; o.constraint.range = NULL; /* only one union member needs to be NULLed */ switch (i_option) { case CS2_OPTION_NUM: o.name = ""; o.title = SANE_TITLE_NUM_OPTIONS; o.desc = SANE_DESC_NUM_OPTIONS; o.type = SANE_TYPE_INT; o.size = WSIZE; o.cap = SANE_CAP_SOFT_DETECT; break; case CS2_OPTION_PREVIEW: o.name = "preview"; o.title = "Preview mode"; o.desc = "Preview mode"; o.type = SANE_TYPE_BOOL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; break; case CS2_OPTION_NEGATIVE: o.name = "negative"; o.title = "Negative"; o.desc = "Negative film: make scanner invert colours"; o.type = SANE_TYPE_BOOL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; #ifndef CS2_BLEEDING_EDGE o.cap |= SANE_CAP_INACTIVE; #endif break; case CS2_OPTION_INFRARED: o.name = "infrared"; o.title = "Read infrared channel"; o.desc = "Read infrared channel in addition to scan colours"; o.type = SANE_TYPE_BOOL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; break; case CS2_OPTION_SAMPLES_PER_SCAN: o.name = "samples-per-scan"; o.title = "Samples per Scan"; o.desc = "Number of samples per scan"; o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_NONE; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if (s->type != CS2_TYPE_LS2000 && s->type != CS2_TYPE_LS4000 && s->type != CS2_TYPE_LS5000 && s->type != CS2_TYPE_LS8000) o.cap |= SANE_CAP_INACTIVE; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range)); if (! range) alloc_failed = 1; else { range->min = 1; range->max = 16; range->quant = 1; o.constraint.range = range; } break; case CS2_OPTION_DEPTH: o.name = "depth"; o.title = "Bit depth per channel"; o.desc = "Number of bits output by scanner for each channel"; o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_NONE; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_WORD_LIST; word_list = (SANE_Word *) cs2_xmalloc (2 * sizeof (SANE_Word)); if (!word_list) alloc_failed = 1; else { word_list[1] = 8; word_list[2] = s->maxbits; word_list[0] = 2; o.constraint.word_list = word_list; } break; case CS2_OPTION_EXPOSURE: o.name = "exposure"; o.title = "Exposure multiplier"; o.desc = "Exposure multiplier for all channels"; o.type = SANE_TYPE_FIXED; o.unit = SANE_UNIT_NONE; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range)); if (!range) alloc_failed = 1; else { range->min = SANE_FIX (0.); range->max = SANE_FIX (10.); range->quant = SANE_FIX (0.1); o.constraint.range = range; } break; case CS2_OPTION_EXPOSURE_R: o.name = "red-exposure"; o.title = "Red exposure time"; o.desc = "Exposure time for red channel"; o.type = SANE_TYPE_FIXED; o.unit = SANE_UNIT_MICROSECOND; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range)); if (!range) alloc_failed = 1; else { range->min = SANE_FIX (50.); range->max = SANE_FIX (20000.); range->quant = SANE_FIX (10.); o.constraint.range = range; } break; case CS2_OPTION_EXPOSURE_G: o.name = "green-exposure"; o.title = "Green exposure time"; o.desc = "Exposure time for green channel"; o.type = SANE_TYPE_FIXED; o.unit = SANE_UNIT_MICROSECOND; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range)); if (!range) alloc_failed = 1; else { range->min = SANE_FIX (50.); range->max = SANE_FIX (20000.); range->quant = SANE_FIX (10.); o.constraint.range = range; } break; case CS2_OPTION_EXPOSURE_B: o.name = "blue-exposure"; o.title = "Blue exposure time"; o.desc = "Exposure time for blue channel"; o.type = SANE_TYPE_FIXED; o.unit = SANE_UNIT_MICROSECOND; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range)); if (!range) alloc_failed = 1; else { range->min = SANE_FIX (50.); range->max = SANE_FIX (20000.); range->quant = SANE_FIX (10.); o.constraint.range = range; } break; case CS2_OPTION_LUT_R: o.name = "red-gamma-table"; o.title = "LUT for red channel"; o.desc = "LUT for red channel"; o.type = SANE_TYPE_INT; o.size = s->n_lut * WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range)); if (!range) alloc_failed = 1; else { range->min = 0; range->max = s->n_lut - 1; range->quant = 1; o.constraint.range = range; } break; case CS2_OPTION_LUT_G: o.name = "green-gamma-table"; o.title = "LUT for green channel"; o.desc = "LUT for green channel"; o.type = SANE_TYPE_INT; o.size = s->n_lut * WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range)); if (!range) alloc_failed = 1; else { range->min = 0; range->max = s->n_lut - 1; range->quant = 1; o.constraint.range = range; } break; case CS2_OPTION_LUT_B: o.name = "blue-gamma-table"; o.title = "LUT for blue channel"; o.desc = "LUT for blue channel"; o.type = SANE_TYPE_INT; o.size = s->n_lut * WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range)); if (!range) alloc_failed = 1; else { range->min = 0; range->max = s->n_lut - 1; range->quant = 1; o.constraint.range = range; } break; case CS2_OPTION_LOAD: o.name = "load"; o.title = "Load"; o.desc = "Load next slide"; o.type = SANE_TYPE_BUTTON; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; break; case CS2_OPTION_EJECT: o.name = "eject"; o.title = "Eject"; o.desc = "Eject loaded medium"; o.type = SANE_TYPE_BUTTON; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; break; case CS2_OPTION_RESET: o.name = "reset"; o.title = "Reset scanner"; o.desc = "Initialize scanner"; o.type = SANE_TYPE_BUTTON; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; break; case CS2_OPTION_RESX: case CS2_OPTION_RES: case CS2_OPTION_PREVIEW_RESOLUTION: if (i_option == CS2_OPTION_PREVIEW_RESOLUTION) { o.name = "preview-resolution"; o.title = "Preview resolution"; o.desc = "Scanning resolution for preview mode in dpi, affecting both x and y directions"; } else if (i_option == CS2_OPTION_RES) { o.name = "resolution"; o.title = "Resolution"; o.desc = "Scanning resolution in dpi, affecting both x and y directions"; } else { o.name = "x-resolution"; o.title = "X resolution"; o.desc = "Scanning resolution in dpi, affecting x direction only"; } o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_DPI; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if (i_option == CS2_OPTION_RESX) o.cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; if (i_option == CS2_OPTION_PREVIEW_RESOLUTION) o.cap |= SANE_CAP_ADVANCED; o.constraint_type = SANE_CONSTRAINT_WORD_LIST; word_list = (SANE_Word *) cs2_xmalloc ((s->resx_n_list + 1) * sizeof (SANE_Word)); if (!word_list) alloc_failed = 1; else { for (i_list = 0; i_list < s->resx_n_list; i_list++) word_list[i_list + 1] = s->resx_list[i_list]; word_list[0] = s->resx_n_list; o.constraint.word_list = word_list; } break; case CS2_OPTION_RESY: o.name = "y-resolution"; o.title = "Y resolution"; o.desc = "Scanning resolution in dpi, affecting y direction only"; o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_DPI; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; o.constraint_type = SANE_CONSTRAINT_WORD_LIST; word_list = (SANE_Word *) cs2_xmalloc ((s->resy_n_list + 1) * sizeof (SANE_Word)); if (!word_list) alloc_failed = 1; else { for (i_list = 0; i_list < s->resy_n_list; i_list++) word_list[i_list + 1] = s->resy_list[i_list]; word_list[0] = s->resy_n_list; o.constraint.word_list = word_list; } break; case CS2_OPTION_RES_INDEPENDENT: o.name = "independent-res"; o.title = "Independent x/y resolutions"; o.desc = "Enable independent controls for scanning resolution in x and y direction"; o.type = SANE_TYPE_BOOL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; break; case CS2_OPTION_FRAME: o.name = "frame"; o.title = "Frame number"; o.desc = "Number of frame to be scanned, starting with 1"; o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_NONE; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if (s->n_frames <= 1) o.cap |= SANE_CAP_INACTIVE; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range)); if (!range) alloc_failed = 1; else { range->min = 1; range->max = s->n_frames; range->quant = 1; o.constraint.range = range; } break; case CS2_OPTION_SUBFRAME: o.name = "subframe"; o.title = "Frame shift"; o.desc = "Fine position within the selected frame"; o.type = SANE_TYPE_FIXED; o.unit = SANE_UNIT_MM; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range)); if (!range) alloc_failed = 1; else { range->min = SANE_FIX (0.); range->max = SANE_FIX ((s->boundaryy - 1) * s->unit_mm); range->quant = SANE_FIX (0.); o.constraint.range = range; } break; case CS2_OPTION_XMIN: o.name = "tl-x"; o.title = "Left x value of scan area"; o.desc = "Left x value of scan area"; o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_PIXEL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; if (!range) alloc_failed = 1; else { range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range)); range->min = 0; range->max = s->boundaryx - 1; range->quant = 1; o.constraint.range = range; } break; case CS2_OPTION_XMAX: o.name = "br-x"; o.title = "Right x value of scan area"; o.desc = "Right x value of scan area"; o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_PIXEL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range)); if (!range) alloc_failed = 1; else { range->min = 0; range->max = s->boundaryx - 1; range->quant = 1; o.constraint.range = range; } break; case CS2_OPTION_YMIN: o.name = "tl-y"; o.title = "Top y value of scan area"; o.desc = "Top y value of scan area"; o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_PIXEL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range)); if (!range) alloc_failed = 1; else { range->min = 0; range->max = s->boundaryy - 1; range->quant = 1; o.constraint.range = range; } break; case CS2_OPTION_YMAX: o.name = "br-y"; o.title = "Bottom y value of scan area"; o.desc = "Bottom y value of scan area"; o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_PIXEL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range)); if (!range) alloc_failed = 1; else { range->min = 0; range->max = s->boundaryy - 1; range->quant = 1; o.constraint.range = range; } break; case CS2_OPTION_FOCUS_ON_CENTRE: o.name = "focus-on-centre"; o.title = "Use centre of scan area as AF point"; o.desc = "Use centre of scan area as AF point instead of manual AF point selection"; o.type = SANE_TYPE_BOOL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; break; case CS2_OPTION_FOCUS: o.name = SANE_NAME_FOCUS; o.title = SANE_TITLE_FOCUS; o.desc = SANE_DESC_FOCUS; o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_NONE; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range)); if (!range) alloc_failed = 1; else { range->min = s->focus_min; range->max = s->focus_max; range->quant = 1; o.constraint.range = range; } break; case CS2_OPTION_AUTOFOCUS: o.name = SANE_NAME_AUTOFOCUS; o.title = SANE_TITLE_AUTOFOCUS; o.desc = SANE_DESC_AUTOFOCUS; o.type = SANE_TYPE_BUTTON; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; break; case CS2_OPTION_FOCUSX: o.name = "focusx"; o.title = "X coordinate of AF point"; o.desc = "X coordinate of AF point"; o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_PIXEL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range)); if (!range) alloc_failed = 1; else { range->min = 0; range->max = s->boundaryx - 1; range->quant = 1; o.constraint.range = range; } break; case CS2_OPTION_FOCUSY: o.name = "focusy"; o.title = "Y coordinate of AF point"; o.desc = "Y coordinate of AF point"; o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_PIXEL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs2_xmalloc (sizeof (SANE_Range)); if (!range) alloc_failed = 1; else { range->min = 0; range->max = s->boundaryy - 1; range->quant = 1; o.constraint.range = range; } break; case CS2_OPTION_SCAN_AE: o.name = "ae"; o.title = "Auto-exposure scan now"; o.desc = "Perform auto-exposure scan"; o.type = SANE_TYPE_BUTTON; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; break; case CS2_OPTION_SCAN_AE_WB: o.name = "ae-wb"; o.title = "Auto-exposure scan with white balance now"; o.desc = "Perform auto-exposure scan with white balance"; o.type = SANE_TYPE_BUTTON; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; break; default: DBG (1, "BUG: sane_open(): Unknown option number.\n"); return SANE_STATUS_INVAL; break; } s->option_list[i_option] = o; } s->scanning = SANE_FALSE; s->preview = SANE_FALSE; s->negative = SANE_FALSE; s->depth = 8; s->infrared = 0; s->samples_per_scan = 1; s->i_frame = 1; s->subframe = 0.; s->res = s->resx = s->resx_max; s->resy = s->resy_max; s->res_independent = SANE_FALSE; s->res_preview = s->resx_max / 10; if (s->res_preview < s->resx_min) s->res_preview = s->resx_min; s->xmin = 0; s->xmax = s->boundaryx - 1; s->ymin = 0; s->ymax = s->boundaryy - 1; s->focus_on_centre = SANE_TRUE; s->focus = 0; s->focusx = 0; s->focusy = 0; s->exposure = 1.; s->exposure_r = 1200.; s->exposure_g = 1200.; s->exposure_b = 1000.; s->infrared_stage = CS2_INFRARED_OFF; s->infrared_next = CS2_INFRARED_OFF; s->infrared_buf = NULL; s->n_infrared_buf = 0; s->line_buf = NULL; s->n_line_buf = 0; if (alloc_failed) { cs2_close (s); return SANE_STATUS_NO_MEM; } return SANE_STATUS_GOOD; } void sane_close (SANE_Handle h) { cs2_t *s = (cs2_t *) h; DBG (10, "sane_close() called.\n"); cs2_close (s); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle h, SANE_Int n) { cs2_t *s = (cs2_t *) h; DBG (10, "sane_get_option_descriptor() called, option #%i.\n", n); if ((n >= 0) && (n < CS2_N_OPTIONS)) return &s->option_list[n]; else return NULL; } SANE_Status sane_control_option (SANE_Handle h, SANE_Int n, SANE_Action a, void *v, SANE_Int * i) { cs2_t *s = (cs2_t *) h; SANE_Int flags = 0; cs2_pixel_t pixel; SANE_Status status; SANE_Option_Descriptor o = s->option_list[n]; DBG (10, "sane_control_option() called, option #%i, action #%i.\n", n, a); switch (a) { case SANE_ACTION_GET_VALUE: switch (n) { case CS2_OPTION_NUM: *(SANE_Word *) v = CS2_N_OPTIONS; break; case CS2_OPTION_NEGATIVE: *(SANE_Word *) v = s->negative; break; case CS2_OPTION_INFRARED: *(SANE_Word *) v = s->infrared; break; case CS2_OPTION_SAMPLES_PER_SCAN: *(SANE_Word *) v = s->samples_per_scan; break; case CS2_OPTION_DEPTH: *(SANE_Word *) v = s->depth; break; case CS2_OPTION_PREVIEW: *(SANE_Word *) v = s->preview; break; case CS2_OPTION_EXPOSURE: *(SANE_Word *) v = SANE_FIX (s->exposure); break; case CS2_OPTION_EXPOSURE_R: *(SANE_Word *) v = SANE_FIX (s->exposure_r); break; case CS2_OPTION_EXPOSURE_G: *(SANE_Word *) v = SANE_FIX (s->exposure_g); break; case CS2_OPTION_EXPOSURE_B: *(SANE_Word *) v = SANE_FIX (s->exposure_b); break; case CS2_OPTION_LUT_R: if (!(s->lut_r)) return SANE_STATUS_INVAL; for (pixel = 0; pixel < s->n_lut; pixel++) ((SANE_Word *) v)[pixel] = s->lut_r[pixel]; break; case CS2_OPTION_LUT_G: if (!(s->lut_g)) return SANE_STATUS_INVAL; for (pixel = 0; pixel < s->n_lut; pixel++) ((SANE_Word *) v)[pixel] = s->lut_g[pixel]; break; case CS2_OPTION_LUT_B: if (!(s->lut_b)) return SANE_STATUS_INVAL; for (pixel = 0; pixel < s->n_lut; pixel++) ((SANE_Word *) v)[pixel] = s->lut_b[pixel]; break; case CS2_OPTION_EJECT: break; case CS2_OPTION_LOAD: break; case CS2_OPTION_RESET: break; case CS2_OPTION_FRAME: *(SANE_Word *) v = s->i_frame; break; case CS2_OPTION_SUBFRAME: *(SANE_Word *) v = SANE_FIX (s->subframe); break; case CS2_OPTION_RES: *(SANE_Word *) v = s->res; break; case CS2_OPTION_RESX: *(SANE_Word *) v = s->resx; break; case CS2_OPTION_RESY: *(SANE_Word *) v = s->resy; break; case CS2_OPTION_RES_INDEPENDENT: *(SANE_Word *) v = s->res_independent; break; case CS2_OPTION_PREVIEW_RESOLUTION: *(SANE_Word *) v = s->res_preview; break; case CS2_OPTION_XMIN: *(SANE_Word *) v = s->xmin; break; case CS2_OPTION_XMAX: *(SANE_Word *) v = s->xmax; break; case CS2_OPTION_YMIN: *(SANE_Word *) v = s->ymin; break; case CS2_OPTION_YMAX: *(SANE_Word *) v = s->ymax; break; case CS2_OPTION_FOCUS_ON_CENTRE: *(SANE_Word *) v = s->focus_on_centre; break; case CS2_OPTION_FOCUS: *(SANE_Word *) v = s->focus; break; case CS2_OPTION_AUTOFOCUS: break; case CS2_OPTION_FOCUSX: *(SANE_Word *) v = s->focusx; break; case CS2_OPTION_FOCUSY: *(SANE_Word *) v = s->focusy; break; case CS2_OPTION_SCAN_AE: break; case CS2_OPTION_SCAN_AE_WB: break; default: DBG (4, "Error: sane_control_option(): Unknown option (bug?).\n"); return SANE_STATUS_INVAL; } break; case SANE_ACTION_SET_VALUE: if (s->scanning) return SANE_STATUS_INVAL; /* XXXXXXXXXXXXXXXXX do this for all elements of arrays */ switch (o.type) { case SANE_TYPE_BOOL: if ((*(SANE_Word *) v != SANE_TRUE) && (*(SANE_Word *) v != SANE_FALSE)) return SANE_STATUS_INVAL; break; case SANE_TYPE_INT: case SANE_TYPE_FIXED: switch (o.constraint_type) { case SANE_CONSTRAINT_RANGE: if (*(SANE_Word *) v < o.constraint.range->min) { *(SANE_Word *) v = o.constraint.range->min; flags |= SANE_INFO_INEXACT; } else if (*(SANE_Word *) v > o.constraint.range->max) { *(SANE_Word *) v = o.constraint.range->max; flags |= SANE_INFO_INEXACT; } break; case SANE_CONSTRAINT_WORD_LIST: break; default: break; } break; case SANE_TYPE_STRING: break; case SANE_TYPE_BUTTON: break; case SANE_TYPE_GROUP: break; } switch (n) { case CS2_OPTION_NUM: return SANE_STATUS_INVAL; break; case CS2_OPTION_NEGATIVE: s->negative = *(SANE_Word *) v; break; case CS2_OPTION_INFRARED: s->infrared = *(SANE_Word *) v; /* flags |= SANE_INFO_RELOAD_PARAMS; XXXXXXXXXXXXXXXXX */ break; case CS2_OPTION_SAMPLES_PER_SCAN: s->samples_per_scan = *(SANE_Word *) v; break; case CS2_OPTION_DEPTH: s->depth = *(SANE_Word *) v; flags |= SANE_INFO_RELOAD_PARAMS; break; case CS2_OPTION_PREVIEW: s->preview = *(SANE_Word *) v; break; case CS2_OPTION_EXPOSURE: s->exposure = SANE_UNFIX (*(SANE_Word *) v); break; case CS2_OPTION_EXPOSURE_R: s->exposure_r = SANE_UNFIX (*(SANE_Word *) v); break; case CS2_OPTION_EXPOSURE_G: s->exposure_g = SANE_UNFIX (*(SANE_Word *) v); break; case CS2_OPTION_EXPOSURE_B: s->exposure_b = SANE_UNFIX (*(SANE_Word *) v); break; case CS2_OPTION_LUT_R: if (!(s->lut_r)) return SANE_STATUS_INVAL; for (pixel = 0; pixel < s->n_lut; pixel++) s->lut_r[pixel] = ((SANE_Word *) v)[pixel]; break; case CS2_OPTION_LUT_G: if (!(s->lut_g)) return SANE_STATUS_INVAL; for (pixel = 0; pixel < s->n_lut; pixel++) s->lut_g[pixel] = ((SANE_Word *) v)[pixel]; break; case CS2_OPTION_LUT_B: if (!(s->lut_b)) return SANE_STATUS_INVAL; for (pixel = 0; pixel < s->n_lut; pixel++) s->lut_b[pixel] = ((SANE_Word *) v)[pixel]; break; case CS2_OPTION_LOAD: cs2_load (s); break; case CS2_OPTION_EJECT: cs2_eject (s); break; case CS2_OPTION_RESET: cs2_reset (s); break; case CS2_OPTION_FRAME: s->i_frame = *(SANE_Word *) v; break; case CS2_OPTION_SUBFRAME: s->subframe = SANE_UNFIX (*(SANE_Word *) v); break; case CS2_OPTION_RES: s->res = *(SANE_Word *) v; flags |= SANE_INFO_RELOAD_PARAMS; break; case CS2_OPTION_RESX: s->resx = *(SANE_Word *) v; flags |= SANE_INFO_RELOAD_PARAMS; break; case CS2_OPTION_RESY: s->resy = *(SANE_Word *) v; flags |= SANE_INFO_RELOAD_PARAMS; break; case CS2_OPTION_RES_INDEPENDENT: s->res_independent = *(SANE_Word *) v; flags |= SANE_INFO_RELOAD_PARAMS; break; case CS2_OPTION_PREVIEW_RESOLUTION: s->res_preview = *(SANE_Word *) v; flags |= SANE_INFO_RELOAD_PARAMS; break; case CS2_OPTION_XMIN: s->xmin = *(SANE_Word *) v; flags |= SANE_INFO_RELOAD_PARAMS; break; case CS2_OPTION_XMAX: s->xmax = *(SANE_Word *) v; flags |= SANE_INFO_RELOAD_PARAMS; break; case CS2_OPTION_YMIN: s->ymin = *(SANE_Word *) v; flags |= SANE_INFO_RELOAD_PARAMS; break; case CS2_OPTION_YMAX: s->ymax = *(SANE_Word *) v; flags |= SANE_INFO_RELOAD_PARAMS; break; case CS2_OPTION_FOCUS_ON_CENTRE: s->focus_on_centre = *(SANE_Word *) v; if (s->focus_on_centre) { s->option_list[CS2_OPTION_FOCUSX].cap |= SANE_CAP_INACTIVE; s->option_list[CS2_OPTION_FOCUSY].cap |= SANE_CAP_INACTIVE; } else { s->option_list[CS2_OPTION_FOCUSX].cap &= ~SANE_CAP_INACTIVE; s->option_list[CS2_OPTION_FOCUSY].cap &= ~SANE_CAP_INACTIVE; } flags |= SANE_INFO_RELOAD_OPTIONS; break; case CS2_OPTION_FOCUS: s->focus = *(SANE_Word *) v; break; case CS2_OPTION_AUTOFOCUS: cs2_autofocus (s); flags |= SANE_INFO_RELOAD_OPTIONS; break; case CS2_OPTION_FOCUSX: s->focusx = *(SANE_Word *) v; break; case CS2_OPTION_FOCUSY: s->focusy = *(SANE_Word *) v; break; case CS2_OPTION_SCAN_AE: cs2_scanner_ready (s, CS2_STATUS_NO_DOCS); status = cs2_scan (s, CS2_SCAN_AE); if (status) return status; status = cs2_get_exposure (s); if (status) return status; s->exposure = 1.; s->exposure_r = s->real_exposure[1] / 100.; s->exposure_g = s->real_exposure[2] / 100.; s->exposure_b = s->real_exposure[3] / 100.; flags |= SANE_INFO_RELOAD_OPTIONS; break; case CS2_OPTION_SCAN_AE_WB: cs2_scanner_ready (s, CS2_STATUS_NO_DOCS); status = cs2_scan (s, CS2_SCAN_AE_WB); if (status) return status; status = cs2_get_exposure (s); if (status) return status; s->exposure = 1.; s->exposure_r = s->real_exposure[1] / 100.; s->exposure_g = s->real_exposure[2] / 100.; s->exposure_b = s->real_exposure[3] / 100.; flags |= SANE_INFO_RELOAD_OPTIONS; break; default: DBG (4, "Error: sane_control_option(): Unknown option number (bug?).\n"); return SANE_STATUS_INVAL; break; } break; default: DBG (1, "BUG: sane_control_option(): Unknown action number.\n"); return SANE_STATUS_INVAL; break; } if (i) *i = flags; return SANE_STATUS_GOOD; } SANE_Status sane_get_parameters (SANE_Handle h, SANE_Parameters * p) { cs2_t *s = (cs2_t *) h; SANE_Status status; DBG (10, "sane_get_parameters() called.\n"); if (!s->scanning) /* only recalculate when not scanning */ { status = cs2_convert_options (s); if (status) return status; } if (s->infrared_stage == CS2_INFRARED_OUT) { p->format = SANE_FRAME_GRAY; p->bytes_per_line = s->logical_width * s->bytes_per_pixel; } else { p->format = SANE_FRAME_RGB; /* XXXXXXXX CCCCCCCCCC */ p->bytes_per_line = s->n_colour_out * s->logical_width * s->bytes_per_pixel; } p->last_frame = SANE_TRUE; p->lines = s->logical_height; p->depth = 8 * s->bytes_per_pixel; p->pixels_per_line = s->logical_width; return SANE_STATUS_GOOD; } SANE_Status sane_start (SANE_Handle h) { cs2_t *s = (cs2_t *) h; SANE_Status status; DBG (10, "sane_start() called.\n"); if (s->scanning) return SANE_STATUS_INVAL; status = cs2_convert_options (s); if (status) return status; s->infrared_index = 0; s->i_line_buf = 0; s->xfer_position = 0; s->scanning = SANE_TRUE; if (s->infrared_stage == CS2_INFRARED_OUT) return SANE_STATUS_GOOD; else return cs2_scan (s, CS2_SCAN_NORMAL); } SANE_Status sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len) { cs2_t *s = (cs2_t *) h; SANE_Status status; ssize_t xfer_len_in, xfer_len_line, xfer_len_out; unsigned long index; int colour, n_colours, sample_pass; uint8_t *s8 = NULL; uint16_t *s16 = NULL; double m_avg_sum; SANE_Byte *line_buf_new; DBG (10, "sane_read() called, maxlen = %i.\n", maxlen); if (!s->scanning) { *len = 0; return SANE_STATUS_CANCELLED; } if (s->infrared_stage == CS2_INFRARED_OUT) { xfer_len_out = maxlen; if (s->xfer_position + xfer_len_out > s->n_infrared_buf) xfer_len_out = s->n_infrared_buf - s->xfer_position; if (xfer_len_out == 0) /* no more data */ { *len = 0; s->scanning = SANE_FALSE; return SANE_STATUS_EOF; } memcpy (buf, &(s->infrared_buf[s->xfer_position]), xfer_len_out); s->xfer_position += xfer_len_out; if (s->xfer_position >= s->n_infrared_buf) s->infrared_next = CS2_INFRARED_OFF; *len = xfer_len_out; return SANE_STATUS_GOOD; } if (s->i_line_buf > 0) { xfer_len_out = s->n_line_buf - s->i_line_buf; if (xfer_len_out > maxlen) xfer_len_out = maxlen; memcpy (buf, &(s->line_buf[s->i_line_buf]), xfer_len_out); s->i_line_buf += xfer_len_out; if (s->i_line_buf >= s->n_line_buf) s->i_line_buf = 0; *len = xfer_len_out; return SANE_STATUS_GOOD; } xfer_len_line = s->n_colour_out * s->logical_width * s->bytes_per_pixel; 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%06lx)\n", (long) 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 */ if (xfer_len_line == 0) /* no more data */ { *len = 0; s->scanning = SANE_FALSE; return SANE_STATUS_EOF; } if (xfer_len_line != s->n_line_buf) { line_buf_new = (SANE_Byte *) cs2_xrealloc (s->line_buf, xfer_len_line * sizeof (SANE_Byte)); if (!line_buf_new) { *len = 0; return SANE_STATUS_NO_MEM; } s->line_buf = line_buf_new; s->n_line_buf = xfer_len_line; } /* adapt for multi-sampling */ xfer_len_in *= s->samples_per_scan; cs2_scanner_ready (s, CS2_STATUS_READY); cs2_init_buffer (s); cs2_parse_cmd (s, "28 00 00 00 00 00"); cs2_pack_byte (s, (xfer_len_in >> 16) & 0xff); cs2_pack_byte (s, (xfer_len_in >> 8) & 0xff); cs2_pack_byte (s, xfer_len_in & 0xff); cs2_parse_cmd (s, "00"); s->n_recv = xfer_len_in; status = cs2_issue_cmd (s); if (status) { *len = 0; return status; } n_colours = s->n_colour_out + (s->infrared_stage == CS2_INFRARED_IN ? 1 : 0); for (index = 0; index < s->logical_width; index++) for (colour = 0; colour < n_colours; colour++) { m_avg_sum = 0.0; switch (s->bytes_per_pixel) { case 1: /* calculate target address */ if ((s->infrared_stage == CS2_INFRARED_IN) && (colour == s->n_colour_out)) s8 = (uint8_t *) & (s->infrared_buf[s->infrared_index++]); else s8 = (uint8_t *) & (s->line_buf[s->n_colour_out * index + colour]); if (s->samples_per_scan > 1) { /* calculate average of multi samples */ for (sample_pass = 0; sample_pass < s->samples_per_scan; sample_pass++) m_avg_sum += (double) s->recv_buf[s->logical_width * (sample_pass * n_colours + colour) + (colour + 1) * s->odd_padding + index]; *s8 = (uint8_t) (m_avg_sum / s->samples_per_scan + 0.5); } else /* shortcut for single sample */ *s8 = s->recv_buf[colour * s->logical_width + (colour + 1) * s->odd_padding + index]; break; case 2: /* calculate target address */ if ((s->infrared_stage == CS2_INFRARED_IN) && (colour == s->n_colour_out)) s16 = (uint16_t *) & (s->infrared_buf[2 * (s->infrared_index++)]); else s16 = (uint16_t *) & (s-> line_buf[2 * (s->n_colour_out * index + colour)]); if (s->samples_per_scan > 1) { /* calculate average of multi samples */ for (sample_pass = 0; s->samples_per_scan > 1 && sample_pass < s->samples_per_scan; sample_pass++) m_avg_sum += (double) (s->recv_buf[2 * (s->logical_width * (sample_pass * n_colours + colour) + index)] * 256 + s->recv_buf[2 * (s->logical_width * (sample_pass * n_colours + colour) + index) + 1]); *s16 = (uint16_t) (m_avg_sum / s->samples_per_scan + 0.5); } else /* shortcut for single sample */ *s16 = s->recv_buf[2 * (colour * s->logical_width + index)] * 256 + s->recv_buf[2 * (colour * s->logical_width + index) + 1]; *s16 <<= s->shift_bits; break; default: DBG (1, "BUG: sane_read(): Unknown number of bytes per pixel.\n"); *len = 0; return SANE_STATUS_INVAL; break; } } s->xfer_position += xfer_len_line; xfer_len_out = xfer_len_line; if (xfer_len_out > maxlen) xfer_len_out = maxlen; memcpy (buf, s->line_buf, xfer_len_out); if (xfer_len_out < xfer_len_line) s->i_line_buf = xfer_len_out; /* data left in the line buffer, read out next time */ if ((s->infrared_stage == CS2_INFRARED_IN) && (s->xfer_position >= s->n_infrared_buf)) s->infrared_next = CS2_INFRARED_OUT; *len = xfer_len_out; return SANE_STATUS_GOOD; } void sane_cancel (SANE_Handle h) { cs2_t *s = (cs2_t *) h; if (s->scanning) DBG (10, "sane_cancel() called while scanning.\n"); else DBG (10, "sane_cancel() called while not scanning.\n"); if (s->scanning && (s->infrared_stage != CS2_INFRARED_OUT)) { cs2_init_buffer (s); cs2_parse_cmd (s, "c0 00 00 00 00 00"); cs2_issue_cmd (s); } s->scanning = SANE_FALSE; } SANE_Status sane_set_io_mode (SANE_Handle h, SANE_Bool m) { cs2_t *s = (cs2_t *) h; DBG (10, "sane_set_io_mode() called.\n"); if (!s->scanning) return SANE_STATUS_INVAL; if (m == SANE_FALSE) return SANE_STATUS_GOOD; else return SANE_STATUS_UNSUPPORTED; } SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int * fd) { cs2_t *s = (cs2_t *) h; DBG (10, "sane_get_select_fd() called.\n"); (void) fd; /* to shut up compiler */ (void) s; /* to shut up compiler */ return SANE_STATUS_UNSUPPORTED; } /* ========================================================================= */ /* private functions */ static SANE_Status cs2_open (const char *device, cs2_interface_t interface, cs2_t ** sp) { SANE_Status status; cs2_t *s; char *prefix = NULL, *line; const char *device2; int i; int alloc_failed = 0; SANE_Device **device_list_new; DBG (6, "cs2_open() called, with device = %s and interface = %i\n", device, interface); if (!strncmp (device, "auto", 5)) { try_interface = CS2_INTERFACE_SCSI; 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; } if ((s = (cs2_t *) cs2_xmalloc (sizeof (cs2_t))) == NULL) return SANE_STATUS_NO_MEM; memset (s, 0, sizeof (cs2_t)); s->send_buf = s->recv_buf = NULL; s->send_buf_size = s->recv_buf_size = 0; switch (interface) { case CS2_INTERFACE_UNKNOWN: for (i = 0; i < 2; i++) { switch (i) { case 1: prefix = "usb:"; try_interface = CS2_INTERFACE_USB; break; default: prefix = "scsi:"; try_interface = CS2_INTERFACE_SCSI; break; } if (!strncmp (device, prefix, strlen (prefix))) { device2 = device + strlen (prefix); cs2_xfree (s); return cs2_open (device2, try_interface, sp); } } cs2_xfree (s); return SANE_STATUS_INVAL; break; case CS2_INTERFACE_SCSI: s->interface = CS2_INTERFACE_SCSI; DBG (6, "cs2_open(): Trying to open %s, assuming SCSI or SBP2 interface ...\n", device); status = sanei_scsi_open (device, &s->fd, cs2_scsi_sense_handler, s); if (status) { DBG (6, "cs2_open(): ... failed: %s.\n", sane_strstatus (status)); cs2_xfree (s); return status; } break; case CS2_INTERFACE_USB: s->interface = CS2_INTERFACE_USB; DBG (6, "cs2_open(): Trying to open %s, assuming USB interface ...\n", device); status = sanei_usb_open (device, &s->fd); if (status) { DBG (6, "cs2_open(): ... failed: %s.\n", sane_strstatus (status)); cs2_xfree (s); return status; } break; } open_devices++; DBG (6, "cs2_open(): ... looks OK, trying to identify device.\n"); /* identify scanner */ status = cs2_page_inquiry (s, -1); if (status) { DBG (4, "Error: cs2_open(): failed to get page: %s.\n", sane_strstatus (status)); cs2_close (s); return status; } strncpy (s->vendor_string, (char *)s->recv_buf + 8, 8); s->vendor_string[8] = '\0'; strncpy (s->product_string, (char *)s->recv_buf + 16, 16); s->product_string[16] = '\0'; strncpy (s->revision_string, (char *)s->recv_buf + 32, 4); s->revision_string[4] = '\0'; DBG (10, "cs2_open(): Inquiry reveals: vendor = '%s', product = '%s', revision = '%s'.\n", s->vendor_string, s->product_string, s->revision_string); if (!strncmp (s->product_string, "COOLSCANIII ", 16)) 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; if (s->type != CS2_TYPE_UNKOWN) DBG (10, "cs2_open(): Device identified as coolscan2 type #%i.\n", s->type); else { DBG (10, "cs2_open(): Device not identified.\n"); cs2_close (s); return SANE_STATUS_UNSUPPORTED; } if (sp) *sp = s; else { device_list_new = (SANE_Device **) cs2_xrealloc (device_list, (n_device_list + 2) * sizeof (SANE_Device *)); if (!device_list_new) return SANE_STATUS_NO_MEM; device_list = device_list_new; device_list[n_device_list] = (SANE_Device *) cs2_xmalloc (sizeof (SANE_Device)); if (!device_list[n_device_list]) return SANE_STATUS_NO_MEM; switch (interface) { case CS2_INTERFACE_UNKNOWN: DBG (1, "BUG: cs2_open(): unknown interface.\n"); cs2_close (s); return SANE_STATUS_UNSUPPORTED; break; case CS2_INTERFACE_SCSI: prefix = "scsi:"; break; case CS2_INTERFACE_USB: prefix = "usb:"; break; } line = (char *) cs2_xmalloc (strlen (device) + strlen (prefix) + 1); if (!line) alloc_failed = 1; else { strcpy (line, prefix); strcat (line, device); device_list[n_device_list]->name = line; } line = (char *) cs2_xmalloc (strlen (s->vendor_string) + 1); if (!line) alloc_failed = 1; else { strcpy (line, s->vendor_string); device_list[n_device_list]->vendor = line; } line = (char *) cs2_xmalloc (strlen (s->product_string) + 1); if (!line) alloc_failed = 1; else { strcpy (line, s->product_string); device_list[n_device_list]->model = line; } device_list[n_device_list]->type = "film scanner"; if (alloc_failed) { cs2_xfree (device_list[n_device_list]->name); cs2_xfree (device_list[n_device_list]->vendor); cs2_xfree (device_list[n_device_list]->model); cs2_xfree (device_list[n_device_list]); } else n_device_list++; device_list[n_device_list] = NULL; cs2_close (s); } return SANE_STATUS_GOOD; } void cs2_close (cs2_t * s) { cs2_xfree (s->lut_r); cs2_xfree (s->lut_g); cs2_xfree (s->lut_b); cs2_xfree (s->lut_neutral); cs2_xfree (s->infrared_buf); cs2_xfree (s->line_buf); switch (s->interface) { case CS2_INTERFACE_UNKNOWN: DBG (1, "BUG: cs2_close(): Unknown interface number.\n"); break; case CS2_INTERFACE_SCSI: sanei_scsi_close (s->fd); open_devices--; break; case CS2_INTERFACE_USB: sanei_usb_close (s->fd); open_devices--; break; } cs2_xfree (s); } static SANE_Status cs2_attach (const char *dev) { SANE_Status status; if (try_interface == CS2_INTERFACE_UNKNOWN) return SANE_STATUS_UNSUPPORTED; status = cs2_open (dev, try_interface, NULL); return status; } static SANE_Status cs2_scsi_sense_handler (int fd, u_char * sense_buffer, void *arg) { cs2_t *s = (cs2_t *) arg; (void) fd; /* to shut up compiler */ /* sort this out ! XXXXXXXXX */ s->sense_key = sense_buffer[2] & 0x0f; s->sense_asc = sense_buffer[12]; s->sense_ascq = sense_buffer[13]; s->sense_info = sense_buffer[3]; return cs2_parse_sense_data (s); } static SANE_Status cs2_parse_sense_data (cs2_t * s) { SANE_Status status = SANE_STATUS_GOOD; s->sense_code = (s->sense_key << 24) + (s->sense_asc << 16) + (s->sense_ascq << 8) + s->sense_info; if (s->sense_key) DBG (10, "Sense code: %02lx-%02lx-%02lx-%02lx\n", s->sense_key, s->sense_asc, s->sense_ascq, s->sense_info); switch (s->sense_key) { case 0x00: s->status = CS2_STATUS_READY; break; case 0x02: switch (s->sense_asc) { case 0x04: s->status = CS2_STATUS_PROCESSING; break; case 0x3a: s->status = CS2_STATUS_NO_DOCS; break; default: s->status = CS2_STATUS_ERROR; status = SANE_STATUS_IO_ERROR; break; } break; default: s->status = CS2_STATUS_ERROR; status = SANE_STATUS_IO_ERROR; break; } if ((s->sense_code == 0x09800600) || (s->sense_code == 0x09800601)) s->status = CS2_STATUS_REISSUE; return status; } static void cs2_init_buffer (cs2_t * s) { s->n_cmd = 0; s->n_send = 0; s->n_recv = 0; } static SANE_Status cs2_pack_byte (cs2_t * s, SANE_Byte byte) { while (s->send_buf_size <= s->n_send) { s->send_buf_size += 16; s->send_buf = (SANE_Byte *) cs2_xrealloc (s->send_buf, s->send_buf_size); if (!s->send_buf) return SANE_STATUS_NO_MEM; } s->send_buf[s->n_send++] = byte; return SANE_STATUS_GOOD; } static SANE_Status cs2_parse_cmd (cs2_t * s, char *text) { size_t i, j; char c, h; SANE_Status status; for (i = 0; i < strlen (text); i += 2) if (text[i] == ' ') i--; /* a bit dirty... advance by -1+2=1 */ else { if ((!isxdigit (text[i])) || (!isxdigit (text[i + 1]))) DBG (1, "BUG: cs2_parse_cmd(): Parser got invalid character.\n"); c = 0; for (j = 0; j < 2; j++) { h = tolower (text[i + j]); if ((h >= 'a') && (h <= 'f')) c += 10 + h - 'a'; else c += h - '0'; if (j == 0) c <<= 4; } status = cs2_pack_byte (s, c); if (status) return status; } return SANE_STATUS_GOOD; } static SANE_Status cs2_grow_send_buffer (cs2_t * s) { if (s->n_send > s->send_buf_size) { s->send_buf_size = s->n_send; s->send_buf = (SANE_Byte *) cs2_xrealloc (s->send_buf, s->send_buf_size); if (!s->send_buf) return SANE_STATUS_NO_MEM; } return SANE_STATUS_GOOD; } static SANE_Status cs2_issue_cmd (cs2_t * s) { SANE_Status status = SANE_STATUS_INVAL; size_t n_data, n_status; static SANE_Byte status_buf[8]; int status_only = 0; DBG (20, "cs2_issue_cmd(): opcode = 0x%02x, n_send = %lu, n_recv = %lu.\n", s->send_buf[0], (unsigned long) s->n_send, (unsigned long) s->n_recv); s->status = CS2_STATUS_READY; if (!s->n_cmd) switch (s->send_buf[0]) { case 0x00: case 0x12: case 0x15: case 0x16: case 0x17: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0xc0: case 0xc1: s->n_cmd = 6; break; case 0x24: case 0x25: case 0x28: case 0x2a: case 0xe0: case 0xe1: s->n_cmd = 10; break; default: DBG (1, "BUG: cs2_issue_cmd(): Unknown command opcode 0x%02x.\n", s->send_buf[0]); break; } if (s->n_send < s->n_cmd) { DBG (1, "BUG: cs2_issue_cmd(): Negative number of data out bytes requested.\n"); return SANE_STATUS_INVAL; } n_data = s->n_send - s->n_cmd; if (s->n_recv > 0) { if (n_data > 0) { DBG (1, "BUG: cs2_issue_cmd(): Both data in and data out requested.\n"); return SANE_STATUS_INVAL; } else { n_data = s->n_recv; } } s->recv_buf = (SANE_Byte *) cs2_xrealloc (s->recv_buf, s->n_recv); if (!s->recv_buf) return SANE_STATUS_NO_MEM; switch (s->interface) { case CS2_INTERFACE_UNKNOWN: DBG (1, "BUG: cs2_issue_cmd(): Unknown or uninitialized interface number.\n"); break; case CS2_INTERFACE_SCSI: sanei_scsi_cmd2 (s->fd, s->send_buf, s->n_cmd, s->send_buf + s->n_cmd, s->n_send - s->n_cmd, s->recv_buf, &s->n_recv); status = SANE_STATUS_GOOD; 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: if (s->n_send - s->n_cmd < n_data || !n_data) { DBG (4, "Error: cs2_issue_cmd(): Unexpected data out phase.\n"); return SANE_STATUS_IO_ERROR; } status = sanei_usb_write_bulk (s->fd, s->send_buf + s->n_cmd, &n_data); break; case CS2_PHASE_IN: if (s->n_recv < n_data || !n_data) { DBG (4, "Error: cs2_issue_cmd(): Unexpected data in phase.\n"); return SANE_STATUS_IO_ERROR; } 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) { DBG (4, "Error: cs2_issue_cmd(): Unexpected non-data phase, but n_data != 0.\n"); status_only = 1; } break; } n_status = 8; status = sanei_usb_read_bulk (s->fd, status_buf, &n_status); if (n_status != 8) { DBG (4, "Error: cs2_issue_cmd(): Failed to read 8 status bytes from USB.\n"); return SANE_STATUS_IO_ERROR; } s->sense_key = status_buf[1] & 0x0f; s->sense_asc = status_buf[2] & 0xff; s->sense_ascq = status_buf[3] & 0xff; s->sense_info = status_buf[4] & 0xff; cs2_parse_sense_data (s); break; } if (status_only) return SANE_STATUS_IO_ERROR; else return status; } static cs2_phase_t cs2_phase_check (cs2_t * s) { static SANE_Byte phase_send_buf[1] = { 0xd0 }, phase_recv_buf[1]; SANE_Status status = 0; size_t n = 1; status = sanei_usb_write_bulk (s->fd, phase_send_buf, &n); status |= sanei_usb_read_bulk (s->fd, phase_recv_buf, &n); DBG (6, "cs2_phase_check(): Phase check returned phase = 0x%02x.\n", phase_recv_buf[0]); if (status) return -1; else return phase_recv_buf[0]; } static SANE_Status cs2_scanner_ready (cs2_t * s, int flags) { SANE_Status status = SANE_STATUS_GOOD; int i = -1; unsigned long count = 0; int retry = 3; do { if (i >= 0) /* dirty !!! */ usleep (500000); cs2_init_buffer (s); for (i = 0; i < 6; i++) cs2_pack_byte (s, 0x00); status = cs2_issue_cmd (s); if (status) if (--retry < 0) return status; if (++count > 240) { /* 120s timeout */ DBG (4, "Error: cs2_scanner_ready(): Timeout expired.\n"); status = SANE_STATUS_IO_ERROR; break; } } while (s->status & ~flags); /* until all relevant bits are 0 */ return status; } static SANE_Status cs2_page_inquiry (cs2_t * s, int page) { SANE_Status status; size_t n; if (page >= 0) { cs2_scanner_ready (s, CS2_STATUS_NO_DOCS); cs2_init_buffer (s); cs2_parse_cmd (s, "12 01"); cs2_pack_byte (s, page); cs2_parse_cmd (s, "00 04 00"); s->n_recv = 4; status = cs2_issue_cmd (s); if (status) { DBG (4, "Error: cs2_page_inquiry(): Inquiry of page size failed: %s.\n", sane_strstatus (status)); return status; } n = s->recv_buf[3] + 4; } else n = 36; cs2_scanner_ready (s, CS2_STATUS_NO_DOCS); cs2_init_buffer (s); if (page >= 0) { cs2_parse_cmd (s, "12 01"); cs2_pack_byte (s, page); cs2_parse_cmd (s, "00"); } else cs2_parse_cmd (s, "12 00 00 00"); cs2_pack_byte (s, n); cs2_parse_cmd (s, "00"); s->n_recv = n; status = cs2_issue_cmd (s); if (status) { DBG (4, "Error: cs2_page_inquiry(): Inquiry of page failed: %s.\n", sane_strstatus (status)); return status; } return SANE_STATUS_GOOD; } static SANE_Status cs2_full_inquiry (cs2_t * s) { SANE_Status status; int pitch, pitch_max; cs2_pixel_t pixel; status = cs2_page_inquiry (s, 0xc1); if (status) { DBG (4, "Error: cs2_full_inquiry(): Failed to get page: %s\n", sane_strstatus (status)); return status; } s->maxbits = s->recv_buf[82]; if (s->type == CS2_TYPE_LS30) /* must be overridden, LS-30 claims to have 12 bits */ s->maxbits = 10; s->n_lut = 1; s->n_lut <<= s->maxbits; s->lut_r = (cs2_pixel_t *) cs2_xrealloc (s->lut_r, s->n_lut * sizeof (cs2_pixel_t)); s->lut_g = (cs2_pixel_t *) cs2_xrealloc (s->lut_g, s->n_lut * sizeof (cs2_pixel_t)); s->lut_b = (cs2_pixel_t *) cs2_xrealloc (s->lut_b, s->n_lut * sizeof (cs2_pixel_t)); s->lut_neutral = (cs2_pixel_t *) cs2_xrealloc (s->lut_neutral, s->n_lut * sizeof (cs2_pixel_t)); if (!s->lut_r || !s->lut_g || !s->lut_b || !s->lut_neutral) { cs2_xfree (s->lut_r); cs2_xfree (s->lut_g); cs2_xfree (s->lut_b); cs2_xfree (s->lut_neutral); return SANE_STATUS_NO_MEM; } for (pixel = 0; pixel < s->n_lut; pixel++) s->lut_r[pixel] = s->lut_g[pixel] = s->lut_b[pixel] = s->lut_neutral[pixel] = pixel; s->resx_optical = 256 * s->recv_buf[18] + s->recv_buf[19]; 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 * (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 * (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]; s->focus_max = 256 * s->recv_buf[78] + s->recv_buf[79]; s->n_frames = s->recv_buf[75]; s->frame_offset = s->resy_max * 1.5 + 1; /* works for LS-30, maybe not for others */ /* generate resolution list for x */ s->resx_n_list = pitch_max = floor (s->resx_max / (double) s->resx_min); s->resx_list = (unsigned int *) cs2_xrealloc (s->resx_list, pitch_max * sizeof (unsigned int)); for (pitch = 1; pitch <= pitch_max; pitch++) s->resx_list[pitch - 1] = s->resx_max / pitch; /* generate resolution list for y */ s->resy_n_list = pitch_max = floor (s->resy_max / (double) s->resy_min); s->resy_list = (unsigned int *) cs2_xrealloc (s->resy_list, pitch_max * sizeof (unsigned int)); for (pitch = 1; pitch <= pitch_max; pitch++) s->resy_list[pitch - 1] = s->resy_max / pitch; s->unit_dpi = s->resx_max; s->unit_mm = 25.4 / s->unit_dpi; return SANE_STATUS_GOOD; } static SANE_Status cs2_execute (cs2_t * s) { cs2_scanner_ready (s, CS2_STATUS_NO_DOCS); cs2_init_buffer (s); cs2_parse_cmd (s, "c1 00 00 00 00 00"); return cs2_issue_cmd (s); } static SANE_Status cs2_load (cs2_t * s) { SANE_Status status; cs2_scanner_ready (s, CS2_STATUS_NO_DOCS); cs2_init_buffer (s); cs2_parse_cmd (s, "e0 00 d1 00 00 00 00 00 0d 00"); s->n_send += 13; status = cs2_grow_send_buffer (s); if (status) return status; status = cs2_issue_cmd (s); if (status) return status; return cs2_execute (s); } static SANE_Status cs2_eject (cs2_t * s) { SANE_Status status; cs2_scanner_ready (s, CS2_STATUS_NO_DOCS); cs2_init_buffer (s); cs2_parse_cmd (s, "e0 00 d0 00 00 00 00 00 0d 00"); s->n_send += 13; status = cs2_grow_send_buffer (s); if (status) return status; status = cs2_issue_cmd (s); if (status) return status; return cs2_execute (s); } static SANE_Status cs2_reset (cs2_t * s) { SANE_Status status; cs2_scanner_ready (s, CS2_STATUS_NO_DOCS); cs2_init_buffer (s); cs2_parse_cmd (s, "e0 00 80 00 00 00 00 00 0d 00"); s->n_send += 13; status = cs2_grow_send_buffer (s); if (status) return status; status = cs2_issue_cmd (s); if (status) return status; return cs2_execute (s); } static SANE_Status cs2_focus (cs2_t * s) { SANE_Status status; cs2_scanner_ready (s, CS2_STATUS_READY); cs2_init_buffer (s); cs2_parse_cmd (s, "e0 00 c1 00 00 00 00 00 0d 00 00"); cs2_pack_byte (s, (s->focus >> 24) & 0xff); cs2_pack_byte (s, (s->focus >> 16) & 0xff); cs2_pack_byte (s, (s->focus >> 8) & 0xff); cs2_pack_byte (s, s->focus & 0xff); cs2_parse_cmd (s, "00 00 00 00 00 00 00 00"); status = cs2_issue_cmd (s); if (status) return status; return cs2_execute (s); } static SANE_Status cs2_autofocus (cs2_t * s) { SANE_Status status; cs2_convert_options (s); cs2_scanner_ready (s, CS2_STATUS_READY); cs2_init_buffer (s); cs2_parse_cmd (s, "e0 00 a0 00 00 00 00 00 0d 00 00"); cs2_pack_byte (s, (s->real_focusx >> 24) & 0xff); cs2_pack_byte (s, (s->real_focusx >> 16) & 0xff); cs2_pack_byte (s, (s->real_focusx >> 8) & 0xff); cs2_pack_byte (s, s->real_focusx & 0xff); cs2_pack_byte (s, (s->real_focusy >> 24) & 0xff); cs2_pack_byte (s, (s->real_focusy >> 16) & 0xff); cs2_pack_byte (s, (s->real_focusy >> 8) & 0xff); cs2_pack_byte (s, s->real_focusy & 0xff); cs2_parse_cmd (s, "00 00 00 00"); status = cs2_issue_cmd (s); if (status) return status; status = cs2_execute (s); if (status) return status; cs2_scanner_ready (s, CS2_STATUS_READY); cs2_init_buffer (s); cs2_parse_cmd (s, "e1 00 c1 00 00 00 00 00 0d 00"); s->n_recv = 13; status = cs2_issue_cmd (s); if (status) return status; s->focus = 65536 * (256 * s->recv_buf[1] + s->recv_buf[2]) + 256 * s->recv_buf[3] + s->recv_buf[4]; return status; } static SANE_Status cs2_get_exposure (cs2_t * s) { SANE_Status status; int i_colour; for (i_colour = 0; i_colour < 3; i_colour++) { /* XXXXXXXXXXXXX CCCCCCCCCCCCC */ cs2_scanner_ready (s, CS2_STATUS_NO_DOCS); cs2_init_buffer (s); cs2_parse_cmd (s, "25 01 00 00 00"); cs2_pack_byte (s, cs2_colour_list[i_colour]); cs2_parse_cmd (s, "00 00 3a 00"); s->n_recv = 58; status = cs2_issue_cmd (s); if (status) return status; s->real_exposure[cs2_colour_list[i_colour]] = 65536 * (256 * s->recv_buf[54] + s->recv_buf[55]) + 256 * s->recv_buf[56] + s->recv_buf[57]; DBG (6, "cs2_get_exposure(): exposure for colour %i: %li * 10ns\n", cs2_colour_list[i_colour], s->real_exposure[cs2_colour_list[i_colour]]); } return SANE_STATUS_GOOD; } static SANE_Status cs2_convert_options (cs2_t * s) { int i_colour; unsigned long xmin, xmax, ymin, ymax; SANE_Byte *infrared_buf_new; s->real_depth = (s->preview ? 8 : s->depth); s->bytes_per_pixel = (s->real_depth > 8 ? 2 : 1); s->shift_bits = 8 * s->bytes_per_pixel - s->real_depth; if (s->preview) { s->real_resx = s->res_preview; s->real_resy = s->res_preview; } else if (s->res_independent) { s->real_resx = s->resx; s->real_resy = s->resy; } else { s->real_resx = s->res; s->real_resy = s->res; } s->real_pitchx = s->resx_max / s->real_resx; s->real_pitchy = s->resy_max / s->real_resy; s->real_resx = s->resx_max / s->real_pitchx; s->real_resy = s->resy_max / s->real_pitchy; /* The prefix "real_" refers to data in device units (1/maxdpi), "logical_" refers to resolution-dependent data. */ if (s->xmin < s->xmax) { xmin = s->xmin; xmax = s->xmax; } else { xmin = s->xmax; xmax = s->xmin; } if (s->ymin < s->ymax) { ymin = s->ymin; ymax = s->ymax; } else { ymin = s->ymax; ymax = s->ymin; } s->real_xoffset = xmin; s->real_yoffset = ymin + (s->i_frame - 1) * s->frame_offset + s->subframe / s->unit_mm; s->logical_width = (xmax - xmin + 1) / s->real_pitchx; /* XXXXXXXXX use mm units */ s->logical_height = (ymax - ymin + 1) / s->real_pitchy; s->real_width = s->logical_width * s->real_pitchx; s->real_height = s->logical_height * s->real_pitchy; s->odd_padding = 0; if ((s->bytes_per_pixel == 1) && (s->logical_width & 0x01) && (s->type != CS2_TYPE_LS30) && (s->type != CS2_TYPE_LS2000)) s->odd_padding = 1; if (s->focus_on_centre) { s->real_focusx = s->real_xoffset + s->real_width / 2; s->real_focusy = s->real_yoffset + s->real_height / 2; } else { s->real_focusx = s->focusx; s->real_focusy = s->focusy + (s->i_frame - 1) * s->frame_offset + s->subframe / s->unit_mm; } s->real_exposure[1] = s->exposure * s->exposure_r * 100.; s->real_exposure[2] = s->exposure * s->exposure_g * 100.; s->real_exposure[3] = s->exposure * s->exposure_b * 100.; for (i_colour = 0; i_colour < 3; i_colour++) if (s->real_exposure[cs2_colour_list[i_colour]] < 1) s->real_exposure[cs2_colour_list[i_colour]] = 1; s->n_colour_out = s->n_colour_in = 3; /* XXXXXXXXXXXXXX CCCCCCCCCCCCCC */ s->xfer_bytes_total = s->bytes_per_pixel * s->n_colour_out * s->logical_width * s->logical_height; if (s->preview) s->infrared_stage = s->infrared_next = CS2_INFRARED_OFF; else { if ((s->infrared) && (s->infrared_stage == CS2_INFRARED_OFF)) s->infrared_next = CS2_INFRARED_IN; s->infrared_stage = s->infrared_next; if (s->infrared) { s->n_colour_in ++; s->n_infrared_buf = s->bytes_per_pixel * s->logical_width * s->logical_height; infrared_buf_new = (SANE_Byte *) cs2_xrealloc (s->infrared_buf, s->n_infrared_buf); if (infrared_buf_new) s->infrared_buf = infrared_buf_new; else return SANE_STATUS_NO_MEM; } } return SANE_STATUS_GOOD; } static SANE_Status cs2_set_boundary (cs2_t *s) { SANE_Status status; 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"); cs2_pack_byte (s, ((4 + s->n_frames * 16) >> 16) & 0xff); cs2_pack_byte (s, ((4 + s->n_frames * 16) >> 8) & 0xff); cs2_pack_byte (s, (4 + s->n_frames * 16) & 0xff); cs2_parse_cmd (s, "00"); cs2_pack_byte (s, ((4 + s->n_frames * 16) >> 8) & 0xff); cs2_pack_byte (s, (4 + s->n_frames * 16) & 0xff); cs2_pack_byte (s, s->n_frames); cs2_pack_byte (s, s->n_frames); for (i_boundary = 0; i_boundary < s->n_frames; i_boundary++) { lvalue = s->frame_offset * i_boundary + s->subframe / s->unit_mm; cs2_pack_byte (s, (lvalue >> 24) & 0xff); cs2_pack_byte (s, (lvalue >> 16) & 0xff); cs2_pack_byte (s, (lvalue >> 8) & 0xff); cs2_pack_byte (s, lvalue & 0xff); lvalue = 0; cs2_pack_byte (s, (lvalue >> 24) & 0xff); cs2_pack_byte (s, (lvalue >> 16) & 0xff); cs2_pack_byte (s, (lvalue >> 8) & 0xff); cs2_pack_byte (s, lvalue & 0xff); lvalue = s->frame_offset * i_boundary + s->subframe / s->unit_mm + s->frame_offset - 1; cs2_pack_byte (s, (lvalue >> 24) & 0xff); cs2_pack_byte (s, (lvalue >> 16) & 0xff); cs2_pack_byte (s, (lvalue >> 8) & 0xff); cs2_pack_byte (s, lvalue & 0xff); lvalue = s->boundaryx - 1; cs2_pack_byte (s, (lvalue >> 24) & 0xff); cs2_pack_byte (s, (lvalue >> 16) & 0xff); cs2_pack_byte (s, (lvalue >> 8) & 0xff); cs2_pack_byte (s, lvalue & 0xff); } status = cs2_issue_cmd (s); if (status) return status; return SANE_STATUS_GOOD; } static SANE_Status cs2_scan (cs2_t * s, cs2_scan_t type) { SANE_Status status; int i_colour; cs2_pixel_t pixel; cs2_pixel_t *lut; /* wait for device to be ready with document, and set device unit */ status = cs2_scanner_ready (s, CS2_STATUS_NO_DOCS); if (status) return status; if (s->status & CS2_STATUS_NO_DOCS) return SANE_STATUS_NO_DOCS; cs2_scanner_ready (s, CS2_STATUS_READY); cs2_init_buffer (s); /* 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"); status = cs2_issue_cmd (s); if (status) return status; status = cs2_convert_options (s); 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; switch (type) { case CS2_SCAN_NORMAL: for (i_colour = 0; i_colour < s->n_colour_in; i_colour++) { cs2_scanner_ready (s, CS2_STATUS_READY); switch (i_colour) { case 0: lut = s->lut_r; break; case 1: lut = s->lut_g; break; case 2: lut = s->lut_b; break; case 3: lut = s->lut_neutral; break; default: DBG (1, "BUG: cs2_scan(): Unknown colour number for LUT download.\n"); return SANE_STATUS_INVAL; break; } cs2_init_buffer (s); cs2_parse_cmd (s, "2a 00 03 00"); cs2_pack_byte (s, cs2_colour_list[i_colour]); cs2_pack_byte (s, 2 - 1); /* XXXXXXXXXX number of bytes per data point - 1 */ cs2_pack_byte (s, ((2 * s->n_lut) >> 16) & 0xff); /* XXXXXXXXXX 2 bytes per point */ cs2_pack_byte (s, ((2 * s->n_lut) >> 8) & 0xff); /* XXXXXXXXXX 2 bytes per point */ cs2_pack_byte (s, (2 * s->n_lut) & 0xff); /* XXXXXXXXXX 2 bytes per point */ cs2_pack_byte (s, 0x00); for (pixel = 0; pixel < s->n_lut; pixel++) { /* XXXXXXXXXXXXXXX 2 bytes per point */ cs2_pack_byte (s, (lut[pixel] >> 8) & 0xff); cs2_pack_byte (s, lut[pixel] & 0xff); } status = cs2_issue_cmd (s); if (status) return status; } break; default: break; } for (i_colour = 0; i_colour < s->n_colour_in; i_colour++) { cs2_scanner_ready (s, CS2_STATUS_READY); cs2_init_buffer (s); if ((s->type == CS2_TYPE_LS40) || (s->type == CS2_TYPE_LS4000)) cs2_parse_cmd (s, "24 00 00 00 00 00 00 00 3a 80"); else cs2_parse_cmd (s, "24 00 00 00 00 00 00 00 3a 00"); cs2_parse_cmd (s, "00 00 00 00 00 00 00 32"); cs2_pack_byte (s, cs2_colour_list[i_colour]); cs2_pack_byte (s, 0x00); cs2_pack_byte (s, s->real_resx >> 8); cs2_pack_byte (s, s->real_resx & 0xff); cs2_pack_byte (s, s->real_resy >> 8); cs2_pack_byte (s, s->real_resy & 0xff); cs2_pack_byte (s, (s->real_xoffset >> 24) & 0xff); cs2_pack_byte (s, (s->real_xoffset >> 16) & 0xff); cs2_pack_byte (s, (s->real_xoffset >> 8) & 0xff); cs2_pack_byte (s, s->real_xoffset & 0xff); cs2_pack_byte (s, (s->real_yoffset >> 24) & 0xff); cs2_pack_byte (s, (s->real_yoffset >> 16) & 0xff); cs2_pack_byte (s, (s->real_yoffset >> 8) & 0xff); cs2_pack_byte (s, s->real_yoffset & 0xff); cs2_pack_byte (s, (s->real_width >> 24) & 0xff); cs2_pack_byte (s, (s->real_width >> 16) & 0xff); cs2_pack_byte (s, (s->real_width >> 8) & 0xff); cs2_pack_byte (s, s->real_width & 0xff); cs2_pack_byte (s, (s->real_height >> 24) & 0xff); cs2_pack_byte (s, (s->real_height >> 16) & 0xff); cs2_pack_byte (s, (s->real_height >> 8) & 0xff); cs2_pack_byte (s, s->real_height & 0xff); cs2_pack_byte (s, 0x00); /* brightness, etc. */ cs2_pack_byte (s, 0x00); cs2_pack_byte (s, 0x00); cs2_pack_byte (s, 0x05); /* image composition CCCCCCC */ 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, ((s->samples_per_scan - 1) << 4) + 0x00); /* multiread, ordering */ /* 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: cs2_pack_byte (s, 0x01); break; case CS2_SCAN_AE: cs2_pack_byte (s, 0x20); break; case CS2_SCAN_AE_WB: cs2_pack_byte (s, 0x40); break; default: DBG (1, "BUG: cs2_scan(): Unknown scanning type.\n"); return SANE_STATUS_INVAL; } if (s->samples_per_scan == 1) cs2_pack_byte (s, 0x02); /* scanning mode single */ else cs2_pack_byte (s, 0x10); /* scanning mode multi */ cs2_pack_byte (s, 0x02); /* colour interleaving */ cs2_pack_byte (s, 0xff); /* (ae) */ if (i_colour == 3) /* infrared */ cs2_parse_cmd (s, "00 00 00 00"); /* automatic */ else { cs2_pack_byte (s, (s-> real_exposure[cs2_colour_list[i_colour]] >> 24) & 0xff); cs2_pack_byte (s, (s-> real_exposure[cs2_colour_list[i_colour]] >> 16) & 0xff); cs2_pack_byte (s, (s-> real_exposure[cs2_colour_list[i_colour]] >> 8) & 0xff); cs2_pack_byte (s, s->real_exposure[cs2_colour_list[i_colour]] & 0xff); } status = cs2_issue_cmd (s); if (status) return status; } cs2_scanner_ready (s, CS2_STATUS_READY); cs2_focus (s); cs2_scanner_ready (s, CS2_STATUS_READY); 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; default: DBG (1, "BUG: cs2_scan(): Unknown number of input colours.\n"); break; } status = cs2_issue_cmd (s); if (status) 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; } return SANE_STATUS_GOOD; } static void * cs2_xmalloc (size_t size) { register void *value = malloc (size); if (!value) DBG (0, "Error: cs2_xmalloc(): Failed to malloc() %lu bytes.\n", (unsigned long) size); return value; } static void * cs2_xrealloc (void *p, size_t size) { register void *value; if (!size) return p; value = realloc (p, size); if (!value) DBG (0, "Error: cs2_xrealloc(): Failed to realloc() %lu bytes.\n", (unsigned long) size); return value; } static void cs2_xfree (const void *p) { if (p) free ((void *) p); } backends-1.3.0/backend/coolscan2.conf.in000066400000000000000000000013611456256263500200320ustar00rootroot00000000000000# coolscan2.conf: sample configuration file for coolscan2 backend # # The following entry 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. backends-1.3.0/backend/coolscan3.c000066400000000000000000002271341456256263500167330ustar00rootroot00000000000000/* * SANE - Scanner Access Now Easy. * coolscan3.c * * This file implements a SANE backend for Nikon Coolscan film scanners. * * coolscan3.c is based on coolscan2.c, a work of András Major, Ariel Garcia * and Giuseppe Sacco. * * Copyright (C) 2007-08 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. * */ /* ========================================================================= */ #include "../include/sane/config.h" #include #include #include #include #include #include #include #include "../include/_stdint.h" #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_scsi.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_debug.h" #include "../include/sane/sanei_config.h" #define BACKEND_NAME coolscan3 #include "../include/sane/sanei_backend.h" /* must be last */ #define CS3_VERSION_MAJOR 1 #define CS3_VERSION_MINOR 0 #define CS3_REVISION 0 #define CS3_CONFIG_FILE "coolscan3.conf" #define WSIZE (sizeof (SANE_Word)) /* ========================================================================= */ /* typedefs */ typedef enum { CS3_TYPE_UNKOWN, CS3_TYPE_LS30, CS3_TYPE_LS40, CS3_TYPE_LS50, CS3_TYPE_LS2000, CS3_TYPE_LS4000, CS3_TYPE_LS5000, CS3_TYPE_LS8000 } cs3_type_t; typedef enum { CS3_INTERFACE_UNKNOWN, CS3_INTERFACE_SCSI, /* includes IEEE1394 via SBP2 */ CS3_INTERFACE_USB } cs3_interface_t; typedef enum { CS3_PHASE_NONE = 0x00, CS3_PHASE_STATUS = 0x01, CS3_PHASE_OUT = 0x02, CS3_PHASE_IN = 0x03, CS3_PHASE_BUSY = 0x04 } cs3_phase_t; typedef enum { CS3_SCAN_NORMAL, CS3_SCAN_AE, CS3_SCAN_AE_WB } cs3_scan_t; typedef enum { CS3_STATUS_READY = 0, CS3_STATUS_BUSY = 1, CS3_STATUS_NO_DOCS = 2, CS3_STATUS_PROCESSING = 4, CS3_STATUS_ERROR = 8, CS3_STATUS_REISSUE = 16, CS3_STATUS_ALL = 31 /* sum of all others */ } cs3_status_t; typedef enum { CS3_OPTION_NUM = 0, CS3_OPTION_PREVIEW, CS3_OPTION_NEGATIVE, CS3_OPTION_INFRARED, CS3_OPTION_SAMPLES_PER_SCAN, CS3_OPTION_DEPTH, CS3_OPTION_EXPOSURE, CS3_OPTION_EXPOSURE_R, CS3_OPTION_EXPOSURE_G, CS3_OPTION_EXPOSURE_B, CS3_OPTION_SCAN_AE, CS3_OPTION_SCAN_AE_WB, CS3_OPTION_LUT_R, CS3_OPTION_LUT_G, CS3_OPTION_LUT_B, CS3_OPTION_RES, CS3_OPTION_RESX, CS3_OPTION_RESY, CS3_OPTION_RES_INDEPENDENT, CS3_OPTION_PREVIEW_RESOLUTION, CS3_OPTION_FRAME, CS3_OPTION_FRAME_COUNT, CS3_OPTION_SUBFRAME, CS3_OPTION_XMIN, CS3_OPTION_XMAX, CS3_OPTION_YMIN, CS3_OPTION_YMAX, CS3_OPTION_LOAD, CS3_OPTION_AUTOLOAD, CS3_OPTION_EJECT, CS3_OPTION_RESET, CS3_OPTION_FOCUS_ON_CENTRE, CS3_OPTION_FOCUS, CS3_OPTION_AUTOFOCUS, CS3_OPTION_FOCUSX, CS3_OPTION_FOCUSY, CS3_N_OPTIONS /* must be last -- counts number of enum items */ } cs3_option_t; typedef unsigned int cs3_pixel_t; #define CS3_COLOR_MAX 10 /* 9 + 1, see cs3_colors */ /* Given that there is no way to give scanner vendor * and model to the calling software, I have to use * an ugly hack here. :( That's very sad. Suggestions * that can provide the same features are appreciated. */ #ifndef SANE_COOKIE #define SANE_COOKIE 0x0BADCAFE struct SANE_Cookie { uint16_t version; const char *vendor; const char *model; const char *revision; }; #endif typedef struct { /* magic bits :( */ uint32_t magic; struct SANE_Cookie *cookie_ptr; struct SANE_Cookie cookie; /* interface */ cs3_interface_t interface; int fd; SANE_Byte *send_buf, *recv_buf; size_t send_buf_size, recv_buf_size; size_t n_cmd, n_send, n_recv; /* device characteristics */ char vendor_string[9], product_string[17], revision_string[5]; cs3_type_t type; int maxbits; unsigned int resx_optical, resx_min, resx_max, *resx_list, resx_n_list; unsigned int resy_optical, resy_min, resy_max, *resy_list, resy_n_list; unsigned long boundaryx, boundaryy; unsigned long frame_offset; unsigned int unit_dpi; double unit_mm; int n_frames; int focus_min, focus_max; /* settings */ SANE_Bool preview, negative, infrared, autoload, autofocus, ae, aewb; int samples_per_scan, depth, real_depth, bytes_per_pixel, shift_bits, n_colors; cs3_pixel_t n_lut; cs3_pixel_t *lut_r, *lut_g, *lut_b, *lut_neutral; unsigned long resx, resy, res, res_independent, res_preview; unsigned long xmin, xmax, ymin, ymax; int i_frame, frame_count; double subframe; unsigned int real_resx, real_resy, real_pitchx, real_pitchy; 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[CS3_COLOR_MAX]; SANE_Bool focus_on_centre; unsigned long focusx, focusy, real_focusx, real_focusy; int focus; /* status */ SANE_Bool scanning; SANE_Byte *line_buf; ssize_t n_line_buf, i_line_buf; unsigned long sense_key, sense_asc, sense_ascq, sense_info; unsigned long sense_code; cs3_status_t status; size_t xfer_position, xfer_bytes_total; /* SANE stuff */ SANE_Option_Descriptor option_list[CS3_N_OPTIONS]; } cs3_t; /* ========================================================================= */ /* prototypes */ static SANE_Status cs3_open(const char *device, cs3_interface_t interface, cs3_t ** sp); static void cs3_close(cs3_t * s); static SANE_Status cs3_attach(const char *dev); static SANE_Status cs3_scsi_sense_handler(int fd, u_char * sense_buffer, void *arg); static SANE_Status cs3_parse_sense_data(cs3_t * s); static void cs3_init_buffer(cs3_t * s); static SANE_Status cs3_pack_byte(cs3_t * s, SANE_Byte byte); static void cs3_pack_long(cs3_t * s, unsigned long val); static void cs3_pack_word(cs3_t * s, unsigned long val); static SANE_Status cs3_parse_cmd(cs3_t * s, char *text); static SANE_Status cs3_grow_send_buffer(cs3_t * s); static SANE_Status cs3_issue_cmd(cs3_t * s); static cs3_phase_t cs3_phase_check(cs3_t * s); static SANE_Status cs3_set_boundary(cs3_t * s); static SANE_Status cs3_scanner_ready(cs3_t * s, int flags); static SANE_Status cs3_page_inquiry(cs3_t * s, int page); static SANE_Status cs3_full_inquiry(cs3_t * s); static SANE_Status cs3_mode_select(cs3_t * s); static SANE_Status cs3_reserve_unit(cs3_t * s); static SANE_Status cs3_release_unit(cs3_t * s); static SANE_Status cs3_execute(cs3_t * s); static SANE_Status cs3_load(cs3_t * s); static SANE_Status cs3_eject(cs3_t * s); static SANE_Status cs3_reset(cs3_t * s); static SANE_Status cs3_set_focus(cs3_t * s); static SANE_Status cs3_autofocus(cs3_t * s); static SANE_Status cs3_autoexposure(cs3_t * s, int wb); static SANE_Status cs3_get_exposure(cs3_t * s); static SANE_Status cs3_set_window(cs3_t * s, cs3_scan_t type); static SANE_Status cs3_convert_options(cs3_t * s); static SANE_Status cs3_scan(cs3_t * s, cs3_scan_t type); static void *cs3_xmalloc(size_t size); static void *cs3_xrealloc(void *p, size_t size); static void cs3_xfree(void *p); /* ========================================================================= */ /* global variables */ static int cs3_colors[] = { 1, 2, 3, 9 }; static SANE_Device **device_list = NULL; static int n_device_list = 0; static cs3_interface_t try_interface = CS3_INTERFACE_UNKNOWN; static int open_devices = 0; /* ========================================================================= */ /* SANE entry points */ SANE_Status sane_init(SANE_Int * version_code, SANE_Auth_Callback authorize) { DBG_INIT(); DBG(1, "coolscan3 backend, version %i.%i.%i initializing.\n", CS3_VERSION_MAJOR, CS3_VERSION_MINOR, CS3_REVISION); (void) authorize; /* to shut up compiler */ if (version_code) *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); sanei_usb_init(); return SANE_STATUS_GOOD; } void sane_exit(void) { int i; DBG(10, "%s\n", __func__); for (i = 0; i < n_device_list; i++) { cs3_xfree((void *)device_list[i]->name); cs3_xfree((void *)device_list[i]->vendor); cs3_xfree((void *)device_list[i]->model); cs3_xfree(device_list[i]); } cs3_xfree(device_list); } SANE_Status sane_get_devices(const SANE_Device *** list, SANE_Bool local_only) { char line[PATH_MAX], *p; FILE *config; (void) local_only; /* to shut up compiler */ DBG(10, "%s\n", __func__); if (device_list) DBG(6, "sane_get_devices(): Device list already populated, not probing again.\n"); else { if (open_devices) { DBG(4, "sane_get_devices(): Devices open, not scanning for scanners.\n"); return SANE_STATUS_IO_ERROR; } config = sanei_config_open(CS3_CONFIG_FILE); if (config) { DBG(4, "sane_get_devices(): Reading config file.\n"); while (sanei_config_read(line, sizeof(line), config)) { p = line; p += strspn(line, " \t"); if (strlen(p) && (p[0] != '\n') && (p[0] != '#')) cs3_open(line, CS3_INTERFACE_UNKNOWN, NULL); } fclose(config); } else { DBG(4, "sane_get_devices(): No config file found.\n"); cs3_open("auto", CS3_INTERFACE_UNKNOWN, NULL); } DBG(6, "%s: %i device(s) detected.\n", __func__, n_device_list); } *list = (const SANE_Device **) device_list; return SANE_STATUS_GOOD; } SANE_Status sane_open(SANE_String_Const name, SANE_Handle * h) { SANE_Status status; cs3_t *s; int i_option; unsigned int i_list; SANE_Option_Descriptor o; SANE_Word *word_list; SANE_Range *range = NULL; int alloc_failed = 0; DBG(10, "%s\n", __func__); status = cs3_open(name, CS3_INTERFACE_UNKNOWN, &s); if (status != SANE_STATUS_GOOD) return status; *h = (SANE_Handle) s; /* get device properties */ s->lut_r = s->lut_g = s->lut_b = s->lut_neutral = NULL; s->resx_list = s->resy_list = NULL; s->resx_n_list = s->resy_n_list = 0; status = cs3_full_inquiry(s); if (status != SANE_STATUS_GOOD) return status; status = cs3_mode_select(s); if (status != SANE_STATUS_GOOD) return status; /* option descriptors */ for (i_option = 0; i_option < CS3_N_OPTIONS; i_option++) { o.name = o.title = o.desc = NULL; o.type = SANE_TYPE_BOOL; o.unit = SANE_UNIT_NONE; o.size = o.cap = 0; o.constraint_type = SANE_CONSTRAINT_NONE; o.constraint.range = NULL; /* only one union member needs to be NULLed */ switch (i_option) { case CS3_OPTION_NUM: o.name = ""; o.title = SANE_TITLE_NUM_OPTIONS; o.desc = SANE_DESC_NUM_OPTIONS; o.type = SANE_TYPE_INT; o.size = WSIZE; o.cap = SANE_CAP_SOFT_DETECT; break; case CS3_OPTION_PREVIEW: o.name = "preview"; o.title = "Preview mode"; o.desc = "Preview mode"; o.type = SANE_TYPE_BOOL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; break; case CS3_OPTION_NEGATIVE: o.name = "negative"; o.title = "Negative"; o.desc = "Negative film: make scanner invert colors"; o.type = SANE_TYPE_BOOL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; /*o.cap |= SANE_CAP_INACTIVE; */ break; case CS3_OPTION_INFRARED: o.name = "infrared"; o.title = "Read infrared channel"; o.desc = "Read infrared channel in addition to scan colors"; o.type = SANE_TYPE_BOOL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; #ifndef SANE_FRAME_RGBI o.cap |= SANE_CAP_INACTIVE; #endif break; case CS3_OPTION_SAMPLES_PER_SCAN: o.name = "samples-per-scan"; o.title = "Samples per Scan"; o.desc = "Number of samples per scan"; o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_NONE; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if (s->type != CS3_TYPE_LS2000 && s->type != CS3_TYPE_LS4000 && s->type != CS3_TYPE_LS5000 && s->type != CS3_TYPE_LS8000) o.cap |= SANE_CAP_INACTIVE; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs3_xmalloc (sizeof (SANE_Range)); if (! range) alloc_failed = 1; else { range->min = 1; range->max = 16; range->quant = 1; o.constraint.range = range; } break; case CS3_OPTION_DEPTH: o.name = "depth"; o.title = "Bit depth per channel"; o.desc = "Number of bits output by scanner for each channel"; o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_NONE; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_WORD_LIST; word_list = (SANE_Word *) cs3_xmalloc(2 * sizeof(SANE_Word)); if (!word_list) alloc_failed = 1; else { word_list[1] = 8; word_list[2] = s->maxbits; word_list[0] = 2; o.constraint.word_list = word_list; } break; case CS3_OPTION_EXPOSURE: o.name = "exposure"; o.title = "Exposure multiplier"; o.desc = "Exposure multiplier for all channels"; o.type = SANE_TYPE_FIXED; o.unit = SANE_UNIT_NONE; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs3_xmalloc(sizeof(SANE_Range)); if (!range) alloc_failed = 1; else { range->min = SANE_FIX(0.); range->max = SANE_FIX(10.); range->quant = SANE_FIX(0.1); o.constraint.range = range; } break; case CS3_OPTION_EXPOSURE_R: o.name = "red-exposure"; o.title = "Red exposure time"; o.desc = "Exposure time for red channel"; o.type = SANE_TYPE_FIXED; o.unit = SANE_UNIT_MICROSECOND; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs3_xmalloc(sizeof(SANE_Range)); if (!range) alloc_failed = 1; else { range->min = SANE_FIX(50.); range->max = SANE_FIX(20000.); range->quant = SANE_FIX(10.); o.constraint.range = range; } break; case CS3_OPTION_EXPOSURE_G: o.name = "green-exposure"; o.title = "Green exposure time"; o.desc = "Exposure time for green channel"; o.type = SANE_TYPE_FIXED; o.unit = SANE_UNIT_MICROSECOND; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs3_xmalloc(sizeof(SANE_Range)); if (!range) alloc_failed = 1; else { range->min = SANE_FIX(50.); range->max = SANE_FIX(20000.); range->quant = SANE_FIX(10.); o.constraint.range = range; } break; case CS3_OPTION_EXPOSURE_B: o.name = "blue-exposure"; o.title = "Blue exposure time"; o.desc = "Exposure time for blue channel"; o.type = SANE_TYPE_FIXED; o.unit = SANE_UNIT_MICROSECOND; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs3_xmalloc(sizeof(SANE_Range)); if (!range) alloc_failed = 1; else { range->min = SANE_FIX(50.); range->max = SANE_FIX(20000.); range->quant = SANE_FIX(10.); o.constraint.range = range; } break; case CS3_OPTION_LUT_R: o.name = "red-gamma-table"; o.title = "LUT for red channel"; o.desc = "LUT for red channel"; o.type = SANE_TYPE_INT; o.size = s->n_lut * WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs3_xmalloc(sizeof(SANE_Range)); if (!range) alloc_failed = 1; else { range->min = 0; range->max = s->n_lut - 1; range->quant = 1; o.constraint.range = range; } break; case CS3_OPTION_LUT_G: o.name = "green-gamma-table"; o.title = "LUT for green channel"; o.desc = "LUT for green channel"; o.type = SANE_TYPE_INT; o.size = s->n_lut * WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs3_xmalloc(sizeof(SANE_Range)); if (!range) alloc_failed = 1; else { range->min = 0; range->max = s->n_lut - 1; range->quant = 1; o.constraint.range = range; } break; case CS3_OPTION_LUT_B: o.name = "blue-gamma-table"; o.title = "LUT for blue channel"; o.desc = "LUT for blue channel"; o.type = SANE_TYPE_INT; o.size = s->n_lut * WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs3_xmalloc(sizeof(SANE_Range)); if (!range) alloc_failed = 1; else { range->min = 0; range->max = s->n_lut - 1; range->quant = 1; o.constraint.range = range; } break; case CS3_OPTION_LOAD: o.name = "load"; o.title = "Load"; o.desc = "Load next slide"; o.type = SANE_TYPE_BUTTON; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if (s->n_frames > 1) o.cap |= SANE_CAP_INACTIVE; break; case CS3_OPTION_AUTOLOAD: o.name = "autoload"; o.title = "Autoload"; o.desc = "Autoload slide before each scan"; o.type = SANE_TYPE_BOOL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if (s->n_frames > 1) o.cap |= SANE_CAP_INACTIVE; break; case CS3_OPTION_EJECT: o.name = "eject"; o.title = "Eject"; o.desc = "Eject loaded medium"; o.type = SANE_TYPE_BUTTON; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; break; case CS3_OPTION_RESET: o.name = "reset"; o.title = "Reset scanner"; o.desc = "Initialize scanner"; o.type = SANE_TYPE_BUTTON; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; break; case CS3_OPTION_RESX: case CS3_OPTION_RES: case CS3_OPTION_PREVIEW_RESOLUTION: if (i_option == CS3_OPTION_PREVIEW_RESOLUTION) { o.name = "preview-resolution"; o.title = "Preview resolution"; o.desc = "Scanning resolution for preview mode in dpi, affecting both x and y directions"; } else if (i_option == CS3_OPTION_RES) { o.name = "resolution"; o.title = "Resolution"; o.desc = "Scanning resolution in dpi, affecting both x and y directions"; } else { o.name = "x-resolution"; o.title = "X resolution"; o.desc = "Scanning resolution in dpi, affecting x direction only"; } o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_DPI; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if (i_option == CS3_OPTION_RESX) o.cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; if (i_option == CS3_OPTION_PREVIEW_RESOLUTION) o.cap |= SANE_CAP_ADVANCED; o.constraint_type = SANE_CONSTRAINT_WORD_LIST; word_list = (SANE_Word *) cs3_xmalloc((s->resx_n_list + 1) * sizeof(SANE_Word)); if (!word_list) alloc_failed = 1; else { for (i_list = 0; i_list < s->resx_n_list; i_list++) word_list[i_list + 1] = s->resx_list[i_list]; word_list[0] = s->resx_n_list; o.constraint.word_list = word_list; } break; case CS3_OPTION_RESY: o.name = "y-resolution"; o.title = "Y resolution"; o.desc = "Scanning resolution in dpi, affecting y direction only"; o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_DPI; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; o.constraint_type = SANE_CONSTRAINT_WORD_LIST; word_list = (SANE_Word *) cs3_xmalloc((s->resy_n_list + 1) * sizeof(SANE_Word)); if (!word_list) alloc_failed = 1; else { for (i_list = 0; i_list < s->resy_n_list; i_list++) word_list[i_list + 1] = s->resy_list[i_list]; word_list[0] = s->resy_n_list; o.constraint.word_list = word_list; } break; case CS3_OPTION_RES_INDEPENDENT: o.name = "independent-res"; o.title = "Independent x/y resolutions"; o.desc = "Enable independent controls for scanning resolution in x and y direction"; o.type = SANE_TYPE_BOOL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; break; case CS3_OPTION_FRAME: o.name = "frame"; o.title = "Frame number"; o.desc = "Number of frame to be scanned, starting with 1"; o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_NONE; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if (s->n_frames <= 1) o.cap |= SANE_CAP_INACTIVE; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs3_xmalloc(sizeof(SANE_Range)); if (!range) alloc_failed = 1; else { range->min = 1; range->max = s->n_frames; range->quant = 1; o.constraint.range = range; } break; case CS3_OPTION_FRAME_COUNT: o.name = "frame-count"; o.title = "Frame count"; o.desc = "Amount of frames to scan"; o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_NONE; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if (s->n_frames <= 1) o.cap |= SANE_CAP_INACTIVE; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs3_xmalloc(sizeof(SANE_Range)); if (!range) alloc_failed = 1; else { range->min = 1; range->max = s->n_frames - s->i_frame + 1; range->quant = 1; o.constraint.range = range; } break; case CS3_OPTION_SUBFRAME: o.name = "subframe"; o.title = "Frame shift"; o.desc = "Fine position within the selected frame"; o.type = SANE_TYPE_FIXED; o.unit = SANE_UNIT_MM; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs3_xmalloc(sizeof(SANE_Range)); if (!range) alloc_failed = 1; else { range->min = SANE_FIX(0.); range->max = SANE_FIX((s->boundaryy - 1) * s->unit_mm); range->quant = SANE_FIX(0.); o.constraint.range = range; } break; case CS3_OPTION_XMIN: o.name = "tl-x"; o.title = "Left x value of scan area"; o.desc = "Left x value of scan area"; o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_PIXEL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; if (!range) alloc_failed = 1; else { range = (SANE_Range *) cs3_xmalloc(sizeof(SANE_Range)); range->min = 0; range->max = s->boundaryx - 1; range->quant = 1; o.constraint.range = range; } break; case CS3_OPTION_XMAX: o.name = "br-x"; o.title = "Right x value of scan area"; o.desc = "Right x value of scan area"; o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_PIXEL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs3_xmalloc(sizeof(SANE_Range)); if (!range) alloc_failed = 1; else { range->min = 0; range->max = s->boundaryx - 1; range->quant = 1; o.constraint.range = range; } break; case CS3_OPTION_YMIN: o.name = "tl-y"; o.title = "Top y value of scan area"; o.desc = "Top y value of scan area"; o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_PIXEL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs3_xmalloc(sizeof(SANE_Range)); if (!range) alloc_failed = 1; else { range->min = 0; range->max = s->boundaryy - 1; range->quant = 1; o.constraint.range = range; } break; case CS3_OPTION_YMAX: o.name = "br-y"; o.title = "Bottom y value of scan area"; o.desc = "Bottom y value of scan area"; o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_PIXEL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs3_xmalloc(sizeof(SANE_Range)); if (!range) alloc_failed = 1; else { range->min = 0; range->max = s->boundaryy - 1; range->quant = 1; o.constraint.range = range; } break; case CS3_OPTION_FOCUS_ON_CENTRE: o.name = "focus-on-centre"; o.title = "Use centre of scan area as AF point"; o.desc = "Use centre of scan area as AF point instead of manual AF point selection"; o.type = SANE_TYPE_BOOL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; break; case CS3_OPTION_FOCUS: o.name = SANE_NAME_FOCUS; o.title = SANE_TITLE_FOCUS; o.desc = SANE_DESC_FOCUS; o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_NONE; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs3_xmalloc(sizeof(SANE_Range)); if (!range) alloc_failed = 1; else { range->min = s->focus_min; range->max = s->focus_max; range->quant = 1; o.constraint.range = range; } break; case CS3_OPTION_AUTOFOCUS: o.name = SANE_NAME_AUTOFOCUS; o.title = SANE_TITLE_AUTOFOCUS; o.desc = SANE_DESC_AUTOFOCUS; o.type = SANE_TYPE_BOOL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; break; case CS3_OPTION_FOCUSX: o.name = "focusx"; o.title = "X coordinate of AF point"; o.desc = "X coordinate of AF point"; o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_PIXEL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs3_xmalloc(sizeof(SANE_Range)); if (!range) alloc_failed = 1; else { range->min = 0; range->max = s->boundaryx - 1; range->quant = 1; o.constraint.range = range; } break; case CS3_OPTION_FOCUSY: o.name = "focusy"; o.title = "Y coordinate of AF point"; o.desc = "Y coordinate of AF point"; o.type = SANE_TYPE_INT; o.unit = SANE_UNIT_PIXEL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE; o.constraint_type = SANE_CONSTRAINT_RANGE; range = (SANE_Range *) cs3_xmalloc(sizeof(SANE_Range)); if (!range) alloc_failed = 1; else { range->min = 0; range->max = s->boundaryy - 1; range->quant = 1; o.constraint.range = range; } break; case CS3_OPTION_SCAN_AE: o.name = "ae"; o.title = "Auto-exposure"; o.desc = "Perform auto-exposure before scan"; o.type = SANE_TYPE_BOOL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; break; case CS3_OPTION_SCAN_AE_WB: o.name = "ae-wb"; o.title = "Auto-exposure with white balance"; o.desc = "Perform auto-exposure with white balance before scan"; o.type = SANE_TYPE_BOOL; o.size = WSIZE; o.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; break; default: DBG(1, "BUG: sane_open(): Unknown option number: %d\n", i_option); break; } s->option_list[i_option] = o; } s->scanning = SANE_FALSE; s->preview = SANE_FALSE; s->negative = SANE_FALSE; s->autoload = SANE_FALSE; s->infrared = SANE_FALSE; s->ae = SANE_FALSE; s->aewb = SANE_FALSE; s->samples_per_scan = 1; s->depth = 8; s->i_frame = 1; s->frame_count = 1; s->subframe = 0.; s->res = s->resx = s->resx_max; s->resy = s->resy_max; s->res_independent = SANE_FALSE; s->res_preview = s->resx_max / 10; if (s->res_preview < s->resx_min) s->res_preview = s->resx_min; s->xmin = 0; s->xmax = s->boundaryx - 1; s->ymin = 0; s->ymax = s->boundaryy - 1; s->focus_on_centre = SANE_TRUE; s->focus = 0; s->focusx = 0; s->focusy = 0; s->exposure = 1.; s->exposure_r = 1200.; s->exposure_g = 1200.; s->exposure_b = 1000.; s->line_buf = NULL; s->n_line_buf = 0; if (alloc_failed) { cs3_close(s); return SANE_STATUS_NO_MEM; } return cs3_reserve_unit(s); } void sane_close(SANE_Handle h) { cs3_t *s = (cs3_t *) h; DBG(10, "%s\n", __func__); cs3_release_unit(s); cs3_close(s); } const SANE_Option_Descriptor * sane_get_option_descriptor(SANE_Handle h, SANE_Int n) { cs3_t *s = (cs3_t *) h; DBG(24, "%s, option %i\n", __func__, n); if ((n >= 0) && (n < CS3_N_OPTIONS)) return &s->option_list[n]; else return NULL; } SANE_Status sane_control_option(SANE_Handle h, SANE_Int n, SANE_Action a, void *v, SANE_Int * i) { cs3_t *s = (cs3_t *) h; SANE_Int flags = 0; cs3_pixel_t pixel; SANE_Option_Descriptor o = s->option_list[n]; DBG(24, "%s, option %i, action %i.\n", __func__, n, a); switch (a) { case SANE_ACTION_GET_VALUE: switch (n) { case CS3_OPTION_NUM: *(SANE_Word *) v = CS3_N_OPTIONS; break; case CS3_OPTION_NEGATIVE: *(SANE_Word *) v = s->negative; break; case CS3_OPTION_INFRARED: *(SANE_Word *) v = s->infrared; break; case CS3_OPTION_SAMPLES_PER_SCAN: *(SANE_Word *) v = s->samples_per_scan; break; case CS3_OPTION_DEPTH: *(SANE_Word *) v = s->depth; break; case CS3_OPTION_PREVIEW: *(SANE_Word *) v = s->preview; break; case CS3_OPTION_AUTOLOAD: *(SANE_Word *) v = s->autoload; break; case CS3_OPTION_EXPOSURE: *(SANE_Word *) v = SANE_FIX(s->exposure); break; case CS3_OPTION_EXPOSURE_R: *(SANE_Word *) v = SANE_FIX(s->exposure_r); break; case CS3_OPTION_EXPOSURE_G: *(SANE_Word *) v = SANE_FIX(s->exposure_g); break; case CS3_OPTION_EXPOSURE_B: *(SANE_Word *) v = SANE_FIX(s->exposure_b); break; case CS3_OPTION_LUT_R: if (!(s->lut_r)) return SANE_STATUS_INVAL; for (pixel = 0; pixel < s->n_lut; pixel++) ((SANE_Word *) v)[pixel] = s->lut_r[pixel]; break; case CS3_OPTION_LUT_G: if (!(s->lut_g)) return SANE_STATUS_INVAL; for (pixel = 0; pixel < s->n_lut; pixel++) ((SANE_Word *) v)[pixel] = s->lut_g[pixel]; break; case CS3_OPTION_LUT_B: if (!(s->lut_b)) return SANE_STATUS_INVAL; for (pixel = 0; pixel < s->n_lut; pixel++) ((SANE_Word *) v)[pixel] = s->lut_b[pixel]; break; case CS3_OPTION_EJECT: break; case CS3_OPTION_LOAD: break; case CS3_OPTION_RESET: break; case CS3_OPTION_FRAME: *(SANE_Word *) v = s->i_frame; break; case CS3_OPTION_FRAME_COUNT: *(SANE_Word *) v = s->frame_count; break; case CS3_OPTION_SUBFRAME: *(SANE_Word *) v = SANE_FIX(s->subframe); break; case CS3_OPTION_RES: *(SANE_Word *) v = s->res; break; case CS3_OPTION_RESX: *(SANE_Word *) v = s->resx; break; case CS3_OPTION_RESY: *(SANE_Word *) v = s->resy; break; case CS3_OPTION_RES_INDEPENDENT: *(SANE_Word *) v = s->res_independent; break; case CS3_OPTION_PREVIEW_RESOLUTION: *(SANE_Word *) v = s->res_preview; break; case CS3_OPTION_XMIN: *(SANE_Word *) v = s->xmin; break; case CS3_OPTION_XMAX: *(SANE_Word *) v = s->xmax; break; case CS3_OPTION_YMIN: *(SANE_Word *) v = s->ymin; break; case CS3_OPTION_YMAX: *(SANE_Word *) v = s->ymax; break; case CS3_OPTION_FOCUS_ON_CENTRE: *(SANE_Word *) v = s->focus_on_centre; break; case CS3_OPTION_FOCUS: *(SANE_Word *) v = s->focus; break; case CS3_OPTION_AUTOFOCUS: *(SANE_Word *) v = s->autofocus; break; case CS3_OPTION_FOCUSX: *(SANE_Word *) v = s->focusx; break; case CS3_OPTION_FOCUSY: *(SANE_Word *) v = s->focusy; break; case CS3_OPTION_SCAN_AE: *(SANE_Word *) v = s->ae; break; case CS3_OPTION_SCAN_AE_WB: *(SANE_Word *) v = s->aewb; break; default: DBG(4, "%s: Unknown option (bug?).\n", __func__); return SANE_STATUS_INVAL; } break; case SANE_ACTION_SET_VALUE: if (s->scanning) return SANE_STATUS_INVAL; /* XXX do this for all elements of arrays */ switch (o.type) { case SANE_TYPE_BOOL: if ((*(SANE_Word *) v != SANE_TRUE) && (*(SANE_Word *) v != SANE_FALSE)) return SANE_STATUS_INVAL; break; case SANE_TYPE_INT: case SANE_TYPE_FIXED: switch (o.constraint_type) { case SANE_CONSTRAINT_RANGE: if (*(SANE_Word *) v < o.constraint.range->min) { *(SANE_Word *) v = o.constraint.range->min; flags |= SANE_INFO_INEXACT; } else if (*(SANE_Word *) v > o.constraint.range->max) { *(SANE_Word *) v = o.constraint.range->max; flags |= SANE_INFO_INEXACT; } break; case SANE_CONSTRAINT_WORD_LIST: break; default: break; } break; case SANE_TYPE_STRING: break; case SANE_TYPE_BUTTON: break; case SANE_TYPE_GROUP: break; } switch (n) { case CS3_OPTION_NUM: return SANE_STATUS_INVAL; break; case CS3_OPTION_NEGATIVE: s->negative = *(SANE_Word *) v; break; case CS3_OPTION_INFRARED: s->infrared = *(SANE_Word *) v; /* flags |= SANE_INFO_RELOAD_PARAMS; XXX */ break; case CS3_OPTION_SAMPLES_PER_SCAN: s->samples_per_scan = *(SANE_Word *) v; break; case CS3_OPTION_DEPTH: if (*(SANE_Word *) v > s->maxbits) return SANE_STATUS_INVAL; s->depth = *(SANE_Word *) v; flags |= SANE_INFO_RELOAD_PARAMS; break; case CS3_OPTION_PREVIEW: s->preview = *(SANE_Word *) v; break; case CS3_OPTION_AUTOLOAD: s->autoload = *(SANE_Word *) v; break; case CS3_OPTION_EXPOSURE: s->exposure = SANE_UNFIX(*(SANE_Word *) v); break; case CS3_OPTION_EXPOSURE_R: s->exposure_r = SANE_UNFIX(*(SANE_Word *) v); break; case CS3_OPTION_EXPOSURE_G: s->exposure_g = SANE_UNFIX(*(SANE_Word *) v); break; case CS3_OPTION_EXPOSURE_B: s->exposure_b = SANE_UNFIX(*(SANE_Word *) v); break; case CS3_OPTION_LUT_R: if (!(s->lut_r)) return SANE_STATUS_INVAL; for (pixel = 0; pixel < s->n_lut; pixel++) s->lut_r[pixel] = ((SANE_Word *) v)[pixel]; break; case CS3_OPTION_LUT_G: if (!(s->lut_g)) return SANE_STATUS_INVAL; for (pixel = 0; pixel < s->n_lut; pixel++) s->lut_g[pixel] = ((SANE_Word *) v)[pixel]; break; case CS3_OPTION_LUT_B: if (!(s->lut_b)) return SANE_STATUS_INVAL; for (pixel = 0; pixel < s->n_lut; pixel++) s->lut_b[pixel] = ((SANE_Word *) v)[pixel]; break; case CS3_OPTION_LOAD: cs3_load(s); break; case CS3_OPTION_EJECT: cs3_eject(s); break; case CS3_OPTION_RESET: cs3_reset(s); break; case CS3_OPTION_FRAME: s->i_frame = *(SANE_Word *) v; break; case CS3_OPTION_FRAME_COUNT: if (*(SANE_Word *) v > (s->n_frames - s->i_frame + 1)) return SANE_STATUS_INVAL; s->frame_count = *(SANE_Word *) v; flags |= SANE_INFO_RELOAD_PARAMS; break; case CS3_OPTION_SUBFRAME: s->subframe = SANE_UNFIX(*(SANE_Word *) v); break; case CS3_OPTION_RES: s->res = *(SANE_Word *) v; flags |= SANE_INFO_RELOAD_PARAMS; break; case CS3_OPTION_RESX: s->resx = *(SANE_Word *) v; flags |= SANE_INFO_RELOAD_PARAMS; break; case CS3_OPTION_RESY: s->resy = *(SANE_Word *) v; flags |= SANE_INFO_RELOAD_PARAMS; break; case CS3_OPTION_RES_INDEPENDENT: s->res_independent = *(SANE_Word *) v; flags |= SANE_INFO_RELOAD_PARAMS; break; case CS3_OPTION_PREVIEW_RESOLUTION: s->res_preview = *(SANE_Word *) v; flags |= SANE_INFO_RELOAD_PARAMS; break; case CS3_OPTION_XMIN: s->xmin = *(SANE_Word *) v; flags |= SANE_INFO_RELOAD_PARAMS; break; case CS3_OPTION_XMAX: s->xmax = *(SANE_Word *) v; flags |= SANE_INFO_RELOAD_PARAMS; break; case CS3_OPTION_YMIN: s->ymin = *(SANE_Word *) v; flags |= SANE_INFO_RELOAD_PARAMS; break; case CS3_OPTION_YMAX: s->ymax = *(SANE_Word *) v; flags |= SANE_INFO_RELOAD_PARAMS; break; case CS3_OPTION_FOCUS_ON_CENTRE: s->focus_on_centre = *(SANE_Word *) v; if (s->focus_on_centre) { s->option_list[CS3_OPTION_FOCUSX].cap |= SANE_CAP_INACTIVE; s->option_list[CS3_OPTION_FOCUSY].cap |= SANE_CAP_INACTIVE; } else { s->option_list[CS3_OPTION_FOCUSX].cap &= ~SANE_CAP_INACTIVE; s->option_list[CS3_OPTION_FOCUSY].cap &= ~SANE_CAP_INACTIVE; } flags |= SANE_INFO_RELOAD_OPTIONS; break; case CS3_OPTION_FOCUS: s->focus = *(SANE_Word *) v; break; case CS3_OPTION_AUTOFOCUS: s->autofocus = *(SANE_Word *) v; break; case CS3_OPTION_FOCUSX: s->focusx = *(SANE_Word *) v; break; case CS3_OPTION_FOCUSY: s->focusy = *(SANE_Word *) v; break; case CS3_OPTION_SCAN_AE: s->ae = *(SANE_Word *) v; break; case CS3_OPTION_SCAN_AE_WB: s->aewb = *(SANE_Word *) v; break; default: DBG(4, "Error: sane_control_option(): Unknown option number (bug?).\n"); return SANE_STATUS_INVAL; break; } break; default: DBG(1, "BUG: sane_control_option(): Unknown action number.\n"); return SANE_STATUS_INVAL; break; } if (i) *i = flags; return SANE_STATUS_GOOD; } SANE_Status sane_get_parameters(SANE_Handle h, SANE_Parameters * p) { cs3_t *s = (cs3_t *) h; SANE_Status status; DBG(10, "%s\n", __func__); if (!s->scanning) { /* only recalculate when not scanning */ status = cs3_convert_options(s); if (status != SANE_STATUS_GOOD) return status; } p->bytes_per_line = s->n_colors * s->logical_width * s->bytes_per_pixel; #ifdef SANE_FRAME_RGBI if (s->infrared) { p->format = SANE_FRAME_RGBI; } else { #endif p->format = SANE_FRAME_RGB; /* XXXXXXXX CCCCCCCCCC */ #ifdef SANE_FRAME_RGBI } #endif p->last_frame = SANE_TRUE; p->lines = s->logical_height; p->depth = 8 * s->bytes_per_pixel; p->pixels_per_line = s->logical_width; return SANE_STATUS_GOOD; } SANE_Status sane_start(SANE_Handle h) { cs3_t *s = (cs3_t *) h; SANE_Status status; DBG(10, "%s\n", __func__); if (s->scanning) return SANE_STATUS_INVAL; if (s->n_frames > 1 && s->frame_count == 0) { DBG(4, "%s: no more frames\n", __func__); return SANE_STATUS_NO_DOCS; } if (s->n_frames > 1) { DBG(4, "%s: scanning frame at position %d, %d to go\n", __func__, s->i_frame, s->frame_count); } status = cs3_convert_options(s); if (status != SANE_STATUS_GOOD) return status; s->i_line_buf = 0; s->xfer_position = 0; s->scanning = SANE_TRUE; /* load if appropriate */ if (s->autoload) { status = cs3_load(s); if (status != SANE_STATUS_GOOD) return status; } /* check for documents */ status = cs3_scanner_ready(s, CS3_STATUS_NO_DOCS); if (status != SANE_STATUS_GOOD) return status; if (s->status & CS3_STATUS_NO_DOCS) return SANE_STATUS_NO_DOCS; if (s->autofocus) { status = cs3_autofocus(s); if (status != SANE_STATUS_GOOD) return status; } if (s->aewb) { status = cs3_autoexposure(s, 1); if (status != SANE_STATUS_GOOD) return status; } else if (s->ae) { status = cs3_autoexposure(s, 0); if (status != SANE_STATUS_GOOD) return status; } return cs3_scan(s, CS3_SCAN_NORMAL); } SANE_Status sane_read(SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len) { cs3_t *s = (cs3_t *) h; SANE_Status status; ssize_t xfer_len_in, xfer_len_line, xfer_len_out; unsigned long index; int color, sample_pass; uint8_t *s8 = NULL; uint16_t *s16 = NULL; double m_avg_sum; SANE_Byte *line_buf_new; DBG(32, "%s, maxlen = %i.\n", __func__, maxlen); if (!s->scanning) { *len = 0; return SANE_STATUS_CANCELLED; } /* transfer from buffer */ if (s->i_line_buf > 0) { xfer_len_out = s->n_line_buf - s->i_line_buf; if (xfer_len_out > maxlen) xfer_len_out = maxlen; memcpy(buf, &(s->line_buf[s->i_line_buf]), xfer_len_out); s->i_line_buf += xfer_len_out; if (s->i_line_buf >= s->n_line_buf) s->i_line_buf = 0; *len = xfer_len_out; return SANE_STATUS_GOOD; } xfer_len_line = s->n_colors * s->logical_width * s->bytes_per_pixel; xfer_len_in = xfer_len_line + (s->n_colors * s->odd_padding); if ((xfer_len_in & 0x3f)) { int d = ((xfer_len_in / 512) * 512) + 512; s->block_padding = d - xfer_len_in; } DBG(22, "%s: block_padding = %d, odd_padding = %d\n", __func__, s->block_padding, s->odd_padding); DBG(22, "%s: colors = %d, logical_width = %ld, bytes_per_pixel = %d\n", __func__, s->n_colors, s->logical_width, s->bytes_per_pixel); /* Do not change the behaviour of older models, pad to 512 */ if ((s->type == CS3_TYPE_LS50) || (s->type == CS3_TYPE_LS5000)) { xfer_len_in += s->block_padding; if (xfer_len_in & 0x3f) DBG(1, "BUG: %s, not a multiple of 64. (0x%06lx)\n", __func__, (long) xfer_len_in); } if (s->xfer_position + xfer_len_line > s->xfer_bytes_total) xfer_len_line = s->xfer_bytes_total - s->xfer_position; /* just in case */ if (xfer_len_line == 0) { /* no more data */ *len = 0; /* increment frame number if appropriate */ if (s->n_frames > 1 && --s->frame_count) { s->i_frame++; } s->scanning = SANE_FALSE; return SANE_STATUS_EOF; } if (xfer_len_line != s->n_line_buf) { line_buf_new = (SANE_Byte *) cs3_xrealloc(s->line_buf, xfer_len_line * sizeof(SANE_Byte)); if (!line_buf_new) { *len = 0; return SANE_STATUS_NO_MEM; } s->line_buf = line_buf_new; s->n_line_buf = xfer_len_line; } /* adapt for multi-sampling */ xfer_len_in *= s->samples_per_scan; cs3_scanner_ready(s, CS3_STATUS_READY); cs3_init_buffer(s); cs3_parse_cmd(s, "28 00 00 00 00 00"); cs3_pack_byte(s, (xfer_len_in >> 16) & 0xff); cs3_pack_byte(s, (xfer_len_in >> 8) & 0xff); cs3_pack_byte(s, xfer_len_in & 0xff); cs3_parse_cmd(s, "00"); s->n_recv = xfer_len_in; status = cs3_issue_cmd(s); if (status != SANE_STATUS_GOOD) { *len = 0; return status; } for (index = 0; index < s->logical_width; index++) { for (color = 0; color < s->n_colors; color++) { int where = s->bytes_per_pixel * (s->n_colors * index + color); m_avg_sum = 0.0; switch (s->bytes_per_pixel) { case 1: { /* target address */ s8 = (uint8_t *) & (s->line_buf[where]); if (s->samples_per_scan > 1) { /* calculate average of multi samples */ for (sample_pass = 0; sample_pass < s->samples_per_scan; sample_pass++) { /* source index */ int p8 = (sample_pass * s->n_colors + color) * s->logical_width + (color + 1) * s->odd_padding + index; m_avg_sum += (double) s->recv_buf[p8]; } *s8 = (uint8_t) (m_avg_sum / s->samples_per_scan + 0.5); } else { /* shortcut for single sample */ int p8 = s->logical_width * color + (color + 1) * s->odd_padding + index; *s8 = s->recv_buf[p8]; } } break; case 2: { /* target address */ s16 = (uint16_t *) & (s->line_buf[where]); if (s->samples_per_scan > 1) { /* calculate average of multi samples */ for (sample_pass = 0; sample_pass < s->samples_per_scan; sample_pass++) { /* source index */ int p16 = 2 * ((sample_pass * s->n_colors + color) * s->logical_width + index); m_avg_sum += (double) ((s->recv_buf[p16] << 8) + s->recv_buf[p16 + 1]); } *s16 = (uint16_t) (m_avg_sum / s->samples_per_scan + 0.5); } else { /* shortcut for single sample */ int p16 = 2 * (color * s->logical_width + index); *s16 = (s->recv_buf[p16] << 8) + s->recv_buf[p16 + 1]; } *s16 <<= s->shift_bits; } break; default: DBG(1, "BUG: sane_read(): Unknown number of bytes per pixel.\n"); *len = 0; return SANE_STATUS_INVAL; break; } } } s->xfer_position += xfer_len_line; xfer_len_out = xfer_len_line; if (xfer_len_out > maxlen) xfer_len_out = maxlen; memcpy(buf, s->line_buf, xfer_len_out); if (xfer_len_out < xfer_len_line) s->i_line_buf = xfer_len_out; /* data left in the line buffer, read out next time */ *len = xfer_len_out; return SANE_STATUS_GOOD; } void sane_cancel(SANE_Handle h) { cs3_t *s = (cs3_t *) h; DBG(10, "%s, scanning = %d.\n", __func__, s->scanning); if (s->scanning) { cs3_init_buffer(s); cs3_parse_cmd(s, "c0 00 00 00 00 00"); cs3_issue_cmd(s); } s->scanning = SANE_FALSE; } SANE_Status sane_set_io_mode(SANE_Handle h, SANE_Bool m) { cs3_t *s = (cs3_t *) h; DBG(10, "%s\n", __func__); if (!s->scanning) return SANE_STATUS_INVAL; if (m == SANE_FALSE) return SANE_STATUS_GOOD; else return SANE_STATUS_UNSUPPORTED; } SANE_Status sane_get_select_fd(SANE_Handle h, SANE_Int * fd) { cs3_t *s = (cs3_t *) h; DBG(10, "%s\n", __func__); (void) fd; /* to shut up compiler */ (void) s; /* to shut up compiler */ return SANE_STATUS_UNSUPPORTED; } /* ========================================================================= */ /* private functions */ static void cs3_trim(char *s) { int i, l = strlen(s); for (i = l - 1; i > 0; i--) { if (s[i] == ' ') s[i] = '\0'; else break; } } static SANE_Status cs3_open(const char *device, cs3_interface_t interface, cs3_t ** sp) { SANE_Status status; cs3_t *s; char *prefix = NULL, *line; int i; int alloc_failed = 0; SANE_Device **device_list_new; DBG(6, "%s, device = %s, interface = %i\n", __func__, device, interface); if (!strncmp(device, "auto", 5)) { try_interface = CS3_INTERFACE_SCSI; sanei_config_attach_matching_devices("scsi Nikon *", cs3_attach); try_interface = CS3_INTERFACE_USB; sanei_usb_attach_matching_devices("usb 0x04b0 0x4000", cs3_attach); sanei_usb_attach_matching_devices("usb 0x04b0 0x4001", cs3_attach); sanei_usb_attach_matching_devices("usb 0x04b0 0x4002", cs3_attach); return SANE_STATUS_GOOD; } if ((s = (cs3_t *) cs3_xmalloc(sizeof(cs3_t))) == NULL) return SANE_STATUS_NO_MEM; memset(s, 0, sizeof(cs3_t)); /* fill magic bits */ s->magic = SANE_COOKIE; s->cookie_ptr = &s->cookie; s->cookie.version = 0x01; s->cookie.vendor = s->vendor_string; s->cookie.model = s->product_string; s->cookie.revision = s->revision_string; s->send_buf = s->recv_buf = NULL; s->send_buf_size = s->recv_buf_size = 0; switch (interface) { case CS3_INTERFACE_UNKNOWN: for (i = 0; i < 2; i++) { switch (i) { case 1: prefix = "usb:"; try_interface = CS3_INTERFACE_USB; break; default: prefix = "scsi:"; try_interface = CS3_INTERFACE_SCSI; break; } if (!strncmp(device, prefix, strlen(prefix))) { const void *p = device + strlen(prefix); cs3_xfree(s); return cs3_open(p, try_interface, sp); } } cs3_xfree(s); return SANE_STATUS_INVAL; break; case CS3_INTERFACE_SCSI: s->interface = CS3_INTERFACE_SCSI; DBG(6, "%s, trying to open %s, assuming SCSI or SBP2 interface\n", __func__, device); status = sanei_scsi_open(device, &s->fd, cs3_scsi_sense_handler, s); if (status != SANE_STATUS_GOOD) { DBG(6, " ...failed: %s.\n", sane_strstatus(status)); cs3_xfree(s); return status; } break; case CS3_INTERFACE_USB: s->interface = CS3_INTERFACE_USB; DBG(6, "%s, trying to open %s, assuming USB interface\n", __func__, device); status = sanei_usb_open(device, &s->fd); if (status != SANE_STATUS_GOOD) { DBG(6, " ...failed: %s.\n", sane_strstatus(status)); cs3_xfree(s); return status; } break; } open_devices++; DBG(6, "%s, trying to identify device.\n", __func__); /* identify scanner */ status = cs3_page_inquiry(s, -1); if (status != SANE_STATUS_GOOD) { cs3_close(s); return status; } strncpy(s->vendor_string, (char *) s->recv_buf + 8, 8); s->vendor_string[8] = '\0'; strncpy(s->product_string, (char *) s->recv_buf + 16, 16); s->product_string[16] = '\0'; strncpy(s->revision_string, (char *) s->recv_buf + 32, 4); s->revision_string[4] = '\0'; DBG(10, "%s, vendor = '%s', product = '%s', revision = '%s'.\n", __func__, s->vendor_string, s->product_string, s->revision_string); if (!strncmp(s->product_string, "COOLSCANIII ", 16)) s->type = CS3_TYPE_LS30; else if (!strncmp(s->product_string, "LS-40 ED ", 16)) s->type = CS3_TYPE_LS40; else if (!strncmp(s->product_string, "LS-50 ED ", 16)) s->type = CS3_TYPE_LS50; else if (!strncmp(s->product_string, "LS-2000 ", 16)) s->type = CS3_TYPE_LS2000; else if (!strncmp(s->product_string, "LS-4000 ED ", 16)) s->type = CS3_TYPE_LS4000; else if (!strncmp(s->product_string, "LS-5000 ED ", 16)) s->type = CS3_TYPE_LS5000; else if (!strncmp(s->product_string, "LS-8000 ED ", 16)) s->type = CS3_TYPE_LS8000; if (s->type != CS3_TYPE_UNKOWN) DBG(10, "%s, device identified as coolscan3 type #%i.\n", __func__, s->type); else { DBG(10, "%s, device not identified.\n", __func__); cs3_close(s); return SANE_STATUS_UNSUPPORTED; } cs3_trim(s->vendor_string); cs3_trim(s->product_string); cs3_trim(s->revision_string); if (sp) *sp = s; else { device_list_new = (SANE_Device **) cs3_xrealloc(device_list, (n_device_list + 2) * sizeof(SANE_Device *)); if (!device_list_new) return SANE_STATUS_NO_MEM; device_list = device_list_new; device_list[n_device_list] = (SANE_Device *) cs3_xmalloc(sizeof(SANE_Device)); if (!device_list[n_device_list]) return SANE_STATUS_NO_MEM; switch (interface) { case CS3_INTERFACE_UNKNOWN: DBG(1, "BUG: cs3_open(): unknown interface.\n"); cs3_close(s); return SANE_STATUS_UNSUPPORTED; break; case CS3_INTERFACE_SCSI: prefix = "scsi:"; break; case CS3_INTERFACE_USB: prefix = "usb:"; break; } line = (char *) cs3_xmalloc(strlen(device) + strlen(prefix) + 1); if (!line) alloc_failed = 1; else { strcpy(line, prefix); strcat(line, device); device_list[n_device_list]->name = line; } line = (char *) cs3_xmalloc(strlen(s->vendor_string) + 1); if (!line) alloc_failed = 1; else { strcpy(line, s->vendor_string); device_list[n_device_list]->vendor = line; } line = (char *) cs3_xmalloc(strlen(s->product_string) + 1); if (!line) alloc_failed = 1; else { strcpy(line, s->product_string); device_list[n_device_list]->model = line; } device_list[n_device_list]->type = "film scanner"; if (alloc_failed) { cs3_xfree((void *)device_list[n_device_list]->name); cs3_xfree((void *)device_list[n_device_list]->vendor); cs3_xfree((void *)device_list[n_device_list]->model); cs3_xfree(device_list[n_device_list]); } else n_device_list++; device_list[n_device_list] = NULL; cs3_close(s); } return SANE_STATUS_GOOD; } void cs3_close(cs3_t * s) { cs3_xfree(s->lut_r); cs3_xfree(s->lut_g); cs3_xfree(s->lut_b); cs3_xfree(s->lut_neutral); cs3_xfree(s->line_buf); switch (s->interface) { case CS3_INTERFACE_UNKNOWN: DBG(0, "BUG: %s: Unknown interface number.\n", __func__); break; case CS3_INTERFACE_SCSI: sanei_scsi_close(s->fd); open_devices--; break; case CS3_INTERFACE_USB: sanei_usb_close(s->fd); open_devices--; break; } cs3_xfree(s); } static SANE_Status cs3_attach(const char *dev) { SANE_Status status; if (try_interface == CS3_INTERFACE_UNKNOWN) return SANE_STATUS_UNSUPPORTED; status = cs3_open(dev, try_interface, NULL); return status; } static SANE_Status cs3_scsi_sense_handler(int fd, u_char * sense_buffer, void *arg) { cs3_t *s = (cs3_t *) arg; (void) fd; /* to shut up compiler */ /* sort this out ! XXX */ s->sense_key = sense_buffer[2] & 0x0f; s->sense_asc = sense_buffer[12]; s->sense_ascq = sense_buffer[13]; s->sense_info = sense_buffer[3]; return cs3_parse_sense_data(s); } static SANE_Status cs3_parse_sense_data(cs3_t * s) { SANE_Status status = SANE_STATUS_GOOD; s->sense_code = (s->sense_key << 24) + (s->sense_asc << 16) + (s->sense_ascq << 8) + s->sense_info; if (s->sense_key) DBG(14, "sense code: %02lx-%02lx-%02lx-%02lx\n", s->sense_key, s->sense_asc, s->sense_ascq, s->sense_info); switch (s->sense_key) { case 0x00: s->status = CS3_STATUS_READY; break; case 0x02: switch (s->sense_asc) { case 0x04: DBG(15, " processing\n"); s->status = CS3_STATUS_PROCESSING; break; case 0x3a: DBG(15, " no docs\n"); s->status = CS3_STATUS_NO_DOCS; break; default: DBG(15, " default\n"); s->status = CS3_STATUS_ERROR; status = SANE_STATUS_IO_ERROR; break; } break; case 0x09: if ((s->sense_code == 0x09800600) || (s->sense_code == 0x09800601)) s->status = CS3_STATUS_REISSUE; break; default: s->status = CS3_STATUS_ERROR; status = SANE_STATUS_IO_ERROR; break; } return status; } static void cs3_init_buffer(cs3_t * s) { s->n_cmd = 0; s->n_send = 0; s->n_recv = 0; } static SANE_Status cs3_pack_byte(cs3_t * s, SANE_Byte byte) { while (s->send_buf_size <= s->n_send) { s->send_buf_size += 16; s->send_buf = (SANE_Byte *) cs3_xrealloc(s->send_buf, s->send_buf_size); if (!s->send_buf) return SANE_STATUS_NO_MEM; } s->send_buf[s->n_send++] = byte; return SANE_STATUS_GOOD; } static void cs3_pack_long(cs3_t * s, unsigned long val) { cs3_pack_byte(s, (val >> 24) & 0xff); cs3_pack_byte(s, (val >> 16) & 0xff); cs3_pack_byte(s, (val >> 8) & 0xff); cs3_pack_byte(s, val & 0xff); } static void cs3_pack_word(cs3_t * s, unsigned long val) { cs3_pack_byte(s, (val >> 8) & 0xff); cs3_pack_byte(s, val & 0xff); } static SANE_Status cs3_parse_cmd(cs3_t * s, char *text) { size_t i, j; char c, h; SANE_Status status; for (i = 0; i < strlen(text); i += 2) if (text[i] == ' ') i--; /* a bit dirty... advance by -1+2=1 */ else { if ((!isxdigit(text[i])) || (!isxdigit(text[i + 1]))) DBG(1, "BUG: cs3_parse_cmd(): Parser got invalid character.\n"); c = 0; for (j = 0; j < 2; j++) { h = tolower(text[i + j]); if ((h >= 'a') && (h <= 'f')) c += 10 + h - 'a'; else c += h - '0'; if (j == 0) c <<= 4; } status = cs3_pack_byte(s, c); if (status != SANE_STATUS_GOOD) return status; } return SANE_STATUS_GOOD; } static SANE_Status cs3_grow_send_buffer(cs3_t * s) { if (s->n_send > s->send_buf_size) { s->send_buf_size = s->n_send; s->send_buf = (SANE_Byte *) cs3_xrealloc(s->send_buf, s->send_buf_size); if (!s->send_buf) return SANE_STATUS_NO_MEM; } return SANE_STATUS_GOOD; } static SANE_Status cs3_issue_cmd(cs3_t * s) { SANE_Status status = SANE_STATUS_INVAL; size_t n_data, n_status; static SANE_Byte status_buf[8]; int status_only = 0; DBG(20, "cs3_issue_cmd(): opcode = 0x%02x, n_send = %lu, n_recv = %lu.\n", s->send_buf[0], (unsigned long) s->n_send, (unsigned long) s->n_recv); s->status = CS3_STATUS_READY; if (!s->n_cmd) switch (s->send_buf[0]) { case 0x00: case 0x12: case 0x15: case 0x16: case 0x17: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0xc0: case 0xc1: s->n_cmd = 6; break; case 0x24: case 0x25: case 0x28: case 0x2a: case 0xe0: case 0xe1: s->n_cmd = 10; break; default: DBG(1, "BUG: cs3_issue_cmd(): Unknown command opcode 0x%02x.\n", s->send_buf[0]); break; } if (s->n_send < s->n_cmd) { DBG(1, "BUG: cs3_issue_cmd(): Negative number of data out bytes requested.\n"); return SANE_STATUS_INVAL; } n_data = s->n_send - s->n_cmd; if (s->n_recv > 0) { if (n_data > 0) { DBG(1, "BUG: cs3_issue_cmd(): Both data in and data out requested.\n"); return SANE_STATUS_INVAL; } else { n_data = s->n_recv; } } s->recv_buf = (SANE_Byte *) cs3_xrealloc(s->recv_buf, s->n_recv); if (!s->recv_buf) return SANE_STATUS_NO_MEM; switch (s->interface) { case CS3_INTERFACE_UNKNOWN: DBG(1, "BUG: cs3_issue_cmd(): Unknown or uninitialized interface number.\n"); break; case CS3_INTERFACE_SCSI: sanei_scsi_cmd2(s->fd, s->send_buf, s->n_cmd, s->send_buf + s->n_cmd, s->n_send - s->n_cmd, s->recv_buf, &s->n_recv); status = SANE_STATUS_GOOD; break; case CS3_INTERFACE_USB: status = sanei_usb_write_bulk(s->fd, s->send_buf, &s->n_cmd); if (status != SANE_STATUS_GOOD) { DBG(1, "Error: cs3_issue_cmd(): Could not write command.\n"); return SANE_STATUS_IO_ERROR; } switch (cs3_phase_check(s)) { case CS3_PHASE_OUT: if (s->n_send - s->n_cmd < n_data || !n_data) { DBG(4, "Error: cs3_issue_cmd(): Unexpected data out phase.\n"); return SANE_STATUS_IO_ERROR; } status = sanei_usb_write_bulk(s->fd, s->send_buf + s->n_cmd, &n_data); break; case CS3_PHASE_IN: if (s->n_recv < n_data || !n_data) { DBG(4, "Error: cs3_issue_cmd(): Unexpected data in phase.\n"); return SANE_STATUS_IO_ERROR; } status = sanei_usb_read_bulk(s->fd, s->recv_buf, &n_data); s->n_recv = n_data; break; case CS3_PHASE_NONE: DBG(4, "%s: No command received!\n", __func__); return SANE_STATUS_IO_ERROR; default: if (n_data) { DBG(4, "%s: Unexpected non-data phase, but n_data != 0 (%lu).\n", __func__, (u_long) n_data); status_only = 1; } break; } n_status = 8; status = sanei_usb_read_bulk(s->fd, status_buf, &n_status); if (n_status != 8) { DBG(4, "Error: cs3_issue_cmd(): Failed to read 8 status bytes from USB.\n"); return SANE_STATUS_IO_ERROR; } s->sense_key = status_buf[1] & 0x0f; s->sense_asc = status_buf[2] & 0xff; s->sense_ascq = status_buf[3] & 0xff; s->sense_info = status_buf[4] & 0xff; status = cs3_parse_sense_data(s); break; } if (status_only) return SANE_STATUS_IO_ERROR; else return status; } static cs3_phase_t cs3_phase_check(cs3_t * s) { static SANE_Byte phase_send_buf[1] = { 0xd0 }, phase_recv_buf[1]; SANE_Status status = 0; size_t n = 1; status = sanei_usb_write_bulk(s->fd, phase_send_buf, &n); status |= sanei_usb_read_bulk(s->fd, phase_recv_buf, &n); DBG(40, "%s: returned phase = 0x%02x.\n", __func__, phase_recv_buf[0]); if (status != SANE_STATUS_GOOD) return -1; else return phase_recv_buf[0]; } static SANE_Status cs3_scanner_ready(cs3_t * s, int flags) { SANE_Status status = SANE_STATUS_GOOD; int i = -1; unsigned long count = 0; int retry = 3; do { if (i >= 0) /* dirty !!! */ usleep(1000000); /* test unit ready */ cs3_init_buffer(s); for (i = 0; i < 6; i++) cs3_pack_byte(s, 0x00); status = cs3_issue_cmd(s); if (status != SANE_STATUS_GOOD) if (--retry < 0) return status; if (++count > 120) { /* 120s timeout */ DBG(4, "Error: %s: Timeout expired.\n", __func__); status = SANE_STATUS_IO_ERROR; break; } } while (s->status & ~flags); /* until all relevant bits are 0 */ return status; } static SANE_Status cs3_page_inquiry(cs3_t * s, int page) { SANE_Status status; size_t n; if (page >= 0) { cs3_scanner_ready(s, CS3_STATUS_NO_DOCS); cs3_init_buffer(s); cs3_parse_cmd(s, "12 01"); cs3_pack_byte(s, page); cs3_parse_cmd(s, "00 04 00"); s->n_recv = 4; status = cs3_issue_cmd(s); if (status != SANE_STATUS_GOOD) { DBG(4, "Error: cs3_page_inquiry(): Inquiry of page size failed: %s.\n", sane_strstatus(status)); return status; } n = s->recv_buf[3] + 4; } else n = 36; cs3_scanner_ready(s, CS3_STATUS_NO_DOCS); cs3_init_buffer(s); if (page >= 0) { cs3_parse_cmd(s, "12 01"); cs3_pack_byte(s, page); cs3_parse_cmd(s, "00"); } else cs3_parse_cmd(s, "12 00 00 00"); cs3_pack_byte(s, n); cs3_parse_cmd(s, "00"); s->n_recv = n; status = cs3_issue_cmd(s); if (status != SANE_STATUS_GOOD) { DBG(4, "Error: %s: inquiry of page failed: %s.\n", __func__, sane_strstatus(status)); return status; } return SANE_STATUS_GOOD; } static SANE_Status cs3_full_inquiry(cs3_t * s) { SANE_Status status; int pitch, pitch_max; cs3_pixel_t pixel; DBG(4, "%s\n", __func__); status = cs3_page_inquiry(s, 0xc1); if (status != SANE_STATUS_GOOD) return status; s->maxbits = s->recv_buf[82]; if (s->type == CS3_TYPE_LS30) /* must be overridden, LS-30 claims to have 12 bits */ s->maxbits = 10; s->n_lut = 1; s->n_lut <<= s->maxbits; s->lut_r = (cs3_pixel_t *) cs3_xrealloc(s->lut_r, s->n_lut * sizeof(cs3_pixel_t)); s->lut_g = (cs3_pixel_t *) cs3_xrealloc(s->lut_g, s->n_lut * sizeof(cs3_pixel_t)); s->lut_b = (cs3_pixel_t *) cs3_xrealloc(s->lut_b, s->n_lut * sizeof(cs3_pixel_t)); s->lut_neutral = (cs3_pixel_t *) cs3_xrealloc(s->lut_neutral, s->n_lut * sizeof(cs3_pixel_t)); if (!s->lut_r || !s->lut_g || !s->lut_b || !s->lut_neutral) { cs3_xfree(s->lut_r); cs3_xfree(s->lut_g); cs3_xfree(s->lut_b); cs3_xfree(s->lut_neutral); return SANE_STATUS_NO_MEM; } for (pixel = 0; pixel < s->n_lut; pixel++) { s->lut_r[pixel] = s->lut_g[pixel] = s->lut_b[pixel] = s->lut_neutral[pixel] = pixel; } s->resx_optical = 256 * s->recv_buf[18] + s->recv_buf[19]; 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 * (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 * (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]; s->focus_max = 256 * s->recv_buf[78] + s->recv_buf[79]; s->n_frames = s->recv_buf[75]; s->frame_offset = s->resy_max * 1.5 + 1; /* works for LS-30, maybe not for others */ /* generate resolution list for x */ s->resx_n_list = pitch_max = floor(s->resx_max / (double) s->resx_min); s->resx_list = (unsigned int *) cs3_xrealloc(s->resx_list, pitch_max * sizeof(unsigned int)); for (pitch = 1; pitch <= pitch_max; pitch++) s->resx_list[pitch - 1] = s->resx_max / pitch; /* generate resolution list for y */ s->resy_n_list = pitch_max = floor(s->resy_max / (double) s->resy_min); s->resy_list = (unsigned int *) cs3_xrealloc(s->resy_list, pitch_max * sizeof(unsigned int)); for (pitch = 1; pitch <= pitch_max; pitch++) s->resy_list[pitch - 1] = s->resy_max / pitch; s->unit_dpi = s->resx_max; s->unit_mm = 25.4 / s->unit_dpi; DBG(4, " maximum depth: %d\n", s->maxbits); DBG(4, " focus: %d/%d\n", s->focus_min, s->focus_max); DBG(4, " resolution (x): %d (%d-%d)\n", s->resx_optical, s->resx_min, s->resx_max); DBG(4, " resolution (y): %d (%d-%d)\n", s->resy_optical, s->resy_min, s->resy_max); DBG(4, " frames: %d\n", s->n_frames); DBG(4, " frame offset: %ld\n", s->frame_offset); return SANE_STATUS_GOOD; } static SANE_Status cs3_execute(cs3_t * s) { DBG(16, "%s\n", __func__); cs3_scanner_ready(s, CS3_STATUS_NO_DOCS); cs3_init_buffer(s); cs3_parse_cmd(s, "c1 00 00 00 00 00"); return cs3_issue_cmd(s); } static SANE_Status cs3_issue_and_execute(cs3_t * s) { SANE_Status status; DBG(10, "%s, opcode = %02x\n", __func__, s->send_buf[0]); status = cs3_issue_cmd(s); if (status != SANE_STATUS_GOOD) return status; return cs3_execute(s); } static SANE_Status cs3_mode_select(cs3_t * s) { DBG(4, "%s\n", __func__); cs3_scanner_ready(s, CS3_STATUS_NO_DOCS); cs3_init_buffer(s); cs3_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"); cs3_pack_word(s, s->unit_dpi); cs3_parse_cmd(s, "00 00"); return cs3_issue_cmd(s); } static SANE_Status cs3_load(cs3_t * s) { SANE_Status status; DBG(6, "%s\n", __func__); cs3_scanner_ready(s, CS3_STATUS_NO_DOCS); cs3_init_buffer(s); cs3_parse_cmd(s, "e0 00 d1 00 00 00 00 00 0d 00"); s->n_send += 13; status = cs3_grow_send_buffer(s); if (status != SANE_STATUS_GOOD) return status; return cs3_issue_and_execute(s); } static SANE_Status cs3_eject(cs3_t * s) { SANE_Status status; cs3_scanner_ready(s, CS3_STATUS_NO_DOCS); cs3_init_buffer(s); cs3_parse_cmd(s, "e0 00 d0 00 00 00 00 00 0d 00"); s->n_send += 13; status = cs3_grow_send_buffer(s); if (status != SANE_STATUS_GOOD) return status; return cs3_issue_and_execute(s); } static SANE_Status cs3_reset(cs3_t * s) { SANE_Status status; cs3_scanner_ready(s, CS3_STATUS_NO_DOCS); cs3_init_buffer(s); cs3_parse_cmd(s, "e0 00 80 00 00 00 00 00 0d 00"); s->n_send += 13; status = cs3_grow_send_buffer(s); if (status != SANE_STATUS_GOOD) return status; return cs3_issue_and_execute(s); } static SANE_Status cs3_reserve_unit(cs3_t * s) { DBG(10, "%s\n", __func__); cs3_init_buffer(s); cs3_parse_cmd(s, "16 00 00 00 00 00"); return cs3_issue_cmd(s); } static SANE_Status cs3_release_unit(cs3_t * s) { DBG(10, "%s\n", __func__); cs3_init_buffer(s); cs3_parse_cmd(s, "17 00 00 00 00 00"); return cs3_issue_cmd(s); } static SANE_Status cs3_set_focus(cs3_t * s) { DBG(6, "%s: setting focus to %d\n", __func__, s->focus); cs3_scanner_ready(s, CS3_STATUS_READY); cs3_init_buffer(s); cs3_parse_cmd(s, "e0 00 c1 00 00 00 00 00 09 00 00"); cs3_pack_long(s, s->focus); cs3_parse_cmd(s, "00 00 00 00"); return cs3_issue_and_execute(s); } static SANE_Status cs3_read_focus(cs3_t * s) { SANE_Status status; cs3_scanner_ready(s, CS3_STATUS_READY); cs3_init_buffer(s); cs3_parse_cmd(s, "e1 00 c1 00 00 00 00 00 0d 00"); s->n_recv = 13; status = cs3_issue_cmd(s); if (status != SANE_STATUS_GOOD) return status; s->focus = 65536 * (256 * s->recv_buf[1] + s->recv_buf[2]) + 256 * s->recv_buf[3] + s->recv_buf[4]; DBG(4, "%s: focus at %d\n", __func__, s->focus); return status; } static SANE_Status cs3_autofocus(cs3_t * s) { SANE_Status status; DBG(6, "%s: focusing at %ld,%ld\n", __func__, s->real_focusx, s->real_focusy); cs3_convert_options(s); status = cs3_read_focus(s); if (status != SANE_STATUS_GOOD) return status; /* set parameter, autofocus */ cs3_scanner_ready(s, CS3_STATUS_READY); cs3_init_buffer(s); cs3_parse_cmd(s, "e0 00 a0 00 00 00 00 00 09 00 00"); cs3_pack_long(s, s->real_focusx); cs3_pack_long(s, s->real_focusy); /*cs3_parse_cmd(s, "00 00 00 00"); */ status = cs3_issue_and_execute(s); if (status != SANE_STATUS_GOOD) return status; return cs3_read_focus(s); } static SANE_Status cs3_autoexposure(cs3_t * s, int wb) { SANE_Status status; DBG(6, "%s, wb = %d\n", __func__, wb); cs3_scanner_ready(s, CS3_STATUS_NO_DOCS); status = cs3_scan(s, wb ? CS3_SCAN_AE_WB : CS3_SCAN_AE); if (status != SANE_STATUS_GOOD) return status; status = cs3_get_exposure(s); if (status != SANE_STATUS_GOOD) return status; s->exposure = 1.; s->exposure_r = s->real_exposure[1] / 100.; s->exposure_g = s->real_exposure[2] / 100.; s->exposure_b = s->real_exposure[3] / 100.; return status; } static SANE_Status cs3_get_exposure(cs3_t * s) { SANE_Status status; int i_color, colors = s->n_colors; DBG(6, "%s\n", __func__); if ((s->type == CS3_TYPE_LS50) || (s->type == CS3_TYPE_LS5000)) colors = 3; cs3_scanner_ready(s, CS3_STATUS_NO_DOCS); /* GET WINDOW */ for (i_color = 0; i_color < colors; i_color++) { /* XXXXXXXXXXXXX CCCCCCCCCCCCC */ cs3_init_buffer(s); cs3_parse_cmd(s, "25 01 00 00 00"); cs3_pack_byte(s, cs3_colors[i_color]); cs3_parse_cmd(s, "00 00 3a 00"); s->n_recv = 58; status = cs3_issue_cmd(s); if (status != SANE_STATUS_GOOD) return status; s->real_exposure[cs3_colors[i_color]] = 65536 * (256 * s->recv_buf[54] + s->recv_buf[55]) + 256 * s->recv_buf[56] + s->recv_buf[57]; DBG(6, "%s, exposure for color %i: %li * 10ns\n", __func__, cs3_colors[i_color], s->real_exposure[cs3_colors[i_color]]); DBG(6, "%02x %02x %02x %02x\n", s->recv_buf[48], s->recv_buf[49], s->recv_buf[50], s->recv_buf[51]); } return SANE_STATUS_GOOD; } static SANE_Status cs3_convert_options(cs3_t * s) { int i_color; unsigned long xmin, xmax, ymin, ymax; DBG(4, "%s\n", __func__); s->real_depth = (s->preview ? 8 : s->depth); s->bytes_per_pixel = (s->real_depth > 8 ? 2 : 1); s->shift_bits = 8 * s->bytes_per_pixel - s->real_depth; DBG(12, " depth = %d, bpp = %d, shift = %d\n", s->real_depth, s->bytes_per_pixel, s->shift_bits); if (s->preview) { s->real_resx = s->res_preview; s->real_resy = s->res_preview; } else if (s->res_independent) { s->real_resx = s->resx; s->real_resy = s->resy; } else { s->real_resx = s->res; s->real_resy = s->res; } s->real_pitchx = s->resx_max / s->real_resx; s->real_pitchy = s->resy_max / s->real_resy; s->real_resx = s->resx_max / s->real_pitchx; s->real_resy = s->resy_max / s->real_pitchy; DBG(12, " resx = %d, resy = %d, pitchx = %d, pitchy = %d\n", s->real_resx, s->real_resy, s->real_pitchx, s->real_pitchy); /* The prefix "real_" refers to data in device units (1/maxdpi), * "logical_" refers to resolution-dependent data. */ if (s->xmin < s->xmax) { xmin = s->xmin; xmax = s->xmax; } else { xmin = s->xmax; xmax = s->xmin; } if (s->ymin < s->ymax) { ymin = s->ymin; ymax = s->ymax; } else { ymin = s->ymax; ymax = s->ymin; } DBG(12, " xmin = %ld, xmax = %ld\n", xmin, xmax); DBG(12, " ymin = %ld, ymax = %ld\n", ymin, ymax); s->real_xoffset = xmin; s->real_yoffset = ymin + (s->i_frame - 1) * s->frame_offset + s->subframe / s->unit_mm; DBG(12, " xoffset = %ld, yoffset = %ld\n", s->real_xoffset, s->real_yoffset); s->logical_width = (xmax - xmin + 1) / s->real_pitchx; /* XXX use mm units */ s->logical_height = (ymax - ymin + 1) / s->real_pitchy; s->real_width = s->logical_width * s->real_pitchx; s->real_height = s->logical_height * s->real_pitchy; DBG(12, " lw = %ld, lh = %ld, rw = %ld, rh = %ld\n", s->logical_width, s->logical_height, s->real_width, s->real_height); s->odd_padding = 0; if ((s->bytes_per_pixel == 1) && (s->logical_width & 0x01) && (s->type != CS3_TYPE_LS30) && (s->type != CS3_TYPE_LS2000)) s->odd_padding = 1; if (s->focus_on_centre) { s->real_focusx = s->real_xoffset + s->real_width / 2; s->real_focusy = s->real_yoffset + s->real_height / 2; } else { s->real_focusx = s->focusx; s->real_focusy = s->focusy + (s->i_frame - 1) * s->frame_offset + s->subframe / s->unit_mm; } DBG(12, " focusx = %ld, focusy = %ld\n", s->real_focusx, s->real_focusy); s->real_exposure[1] = s->exposure * s->exposure_r * 100.; s->real_exposure[2] = s->exposure * s->exposure_g * 100.; s->real_exposure[3] = s->exposure * s->exposure_b * 100.; /* XXX IR? */ for (i_color = 0; i_color < 3; i_color++) if (s->real_exposure[cs3_colors[i_color]] < 1) s->real_exposure[cs3_colors[i_color]] = 1; s->n_colors = 3; /* XXXXXXXXXXXXXX CCCCCCCCCCCCCC */ if (s->infrared) s->n_colors = 4; s->xfer_bytes_total = s->bytes_per_pixel * s->n_colors * s->logical_width * s->logical_height; if (s->preview) s->infrared = SANE_FALSE; return SANE_STATUS_GOOD; } static SANE_Status cs3_set_boundary(cs3_t * s) { SANE_Status status; int i_boundary; /* Ariel - Check this function */ cs3_scanner_ready(s, CS3_STATUS_READY); cs3_init_buffer(s); cs3_parse_cmd(s, "2a 00 88 00 00 03"); cs3_pack_byte(s, ((4 + s->n_frames * 16) >> 16) & 0xff); cs3_pack_byte(s, ((4 + s->n_frames * 16) >> 8) & 0xff); cs3_pack_byte(s, (4 + s->n_frames * 16) & 0xff); cs3_parse_cmd(s, "00"); cs3_pack_byte(s, ((4 + s->n_frames * 16) >> 8) & 0xff); cs3_pack_byte(s, (4 + s->n_frames * 16) & 0xff); cs3_pack_byte(s, s->n_frames); cs3_pack_byte(s, s->n_frames); for (i_boundary = 0; i_boundary < s->n_frames; i_boundary++) { unsigned long lvalue = s->frame_offset * i_boundary + s->subframe / s->unit_mm; cs3_pack_long(s, lvalue); cs3_pack_long(s, 0); lvalue = s->frame_offset * i_boundary + s->subframe / s->unit_mm + s->frame_offset - 1; cs3_pack_long(s, lvalue); cs3_pack_long(s, s->boundaryx - 1); } status = cs3_issue_cmd(s); if (status != SANE_STATUS_GOOD) return status; return SANE_STATUS_GOOD; } static SANE_Status cs3_send_lut(cs3_t * s) { int color; SANE_Status status; cs3_pixel_t *lut, pixel; DBG(6, "%s\n", __func__); for (color = 0; color < s->n_colors; color++) { /*cs3_scanner_ready(s, CS3_STATUS_READY); */ switch (color) { case 0: lut = s->lut_r; break; case 1: lut = s->lut_g; break; case 2: lut = s->lut_b; break; case 3: lut = s->lut_neutral; break; default: DBG(1, "BUG: %s: Unknown color number for LUT download.\n", __func__); return SANE_STATUS_INVAL; break; } cs3_init_buffer(s); cs3_parse_cmd(s, "2a 00 03 00"); cs3_pack_byte(s, cs3_colors[color]); cs3_pack_byte(s, 2 - 1); /* XXX number of bytes per data point - 1 */ cs3_pack_byte(s, ((2 * s->n_lut) >> 16) & 0xff); /* XXX 2 bytes per point */ cs3_pack_byte(s, ((2 * s->n_lut) >> 8) & 0xff); /* XXX 2 bytes per point */ cs3_pack_byte(s, (2 * s->n_lut) & 0xff); /* XXX 2 bytes per point */ cs3_pack_byte(s, 0x00); for (pixel = 0; pixel < s->n_lut; pixel++) { /* XXX 2 bytes per point */ cs3_pack_word(s, lut[pixel]); } status = cs3_issue_cmd(s); if (status != SANE_STATUS_GOOD) return status; } return status; } static SANE_Status cs3_set_window(cs3_t * s, cs3_scan_t type) { int color; SANE_Status status = SANE_STATUS_INVAL; /* SET WINDOW */ for (color = 0; color < s->n_colors; color++) { DBG(8, "%s: color %d\n", __func__, cs3_colors[color]); cs3_scanner_ready(s, CS3_STATUS_READY); cs3_init_buffer(s); if ((s->type == CS3_TYPE_LS40) || (s->type == CS3_TYPE_LS4000) || (s->type == CS3_TYPE_LS50) || (s->type == CS3_TYPE_LS5000)) cs3_parse_cmd(s, "24 00 00 00 00 00 00 00 3a 80"); else cs3_parse_cmd(s, "24 00 00 00 00 00 00 00 3a 00"); cs3_parse_cmd(s, "00 00 00 00 00 00 00 32"); cs3_pack_byte(s, cs3_colors[color]); cs3_pack_byte(s, 0x00); cs3_pack_word(s, s->real_resx); cs3_pack_word(s, s->real_resy); cs3_pack_long(s, s->real_xoffset); cs3_pack_long(s, s->real_yoffset); cs3_pack_long(s, s->real_width); cs3_pack_long(s, s->real_height); cs3_pack_byte(s, 0x00); /* brightness, etc. */ cs3_pack_byte(s, 0x00); cs3_pack_byte(s, 0x00); cs3_pack_byte(s, 0x05); /* image composition CCCCCCC */ cs3_pack_byte(s, s->real_depth); /* pixel composition */ cs3_parse_cmd(s, "00 00 00 00 00 00 00 00 00 00 00 00 00"); cs3_pack_byte(s, ((s->samples_per_scan - 1) << 4) | 0x00); /* multiread, ordering */ cs3_pack_byte(s, 0x80 | (s->negative ? 0 : 1)); /* averaging, pos/neg */ switch (type) { /* scanning kind */ case CS3_SCAN_NORMAL: cs3_pack_byte(s, 0x01); break; case CS3_SCAN_AE: cs3_pack_byte(s, 0x20); break; case CS3_SCAN_AE_WB: cs3_pack_byte(s, 0x40); break; default: DBG(1, "BUG: cs3_scan(): Unknown scanning type.\n"); return SANE_STATUS_INVAL; } if (s->samples_per_scan == 1) cs3_pack_byte(s, 0x02); /* scanning mode single */ else cs3_pack_byte(s, 0x10); /* scanning mode multi */ cs3_pack_byte(s, 0x02); /* color interleaving */ cs3_pack_byte(s, 0xff); /* (ae) */ if (color == 3) /* infrared */ cs3_parse_cmd(s, "00 00 00 00"); /* automatic */ else { DBG(4, "%s: exposure = %ld * 10ns\n", __func__, s->real_exposure[cs3_colors[color]]); cs3_pack_long(s, s->real_exposure[cs3_colors[color]]); } status = cs3_issue_cmd(s); if (status != SANE_STATUS_GOOD) return status; } return status; } static SANE_Status cs3_scan(cs3_t * s, cs3_scan_t type) { SANE_Status status; s->block_padding = 0; DBG(6, "%s, type = %d, colors = %d\n", __func__, type, s->n_colors); switch (type) { case CS3_SCAN_NORMAL: DBG(16, "%s: normal scan\n", __func__); break; case CS3_SCAN_AE: DBG(16, "%s: ae scan\n", __func__); break; case CS3_SCAN_AE_WB: DBG(16, "%s: ae wb scan\n", __func__); break; } /* wait for device to be ready with document, and set device unit */ status = cs3_scanner_ready(s, CS3_STATUS_NO_DOCS); if (status != SANE_STATUS_GOOD) return status; if (s->status & CS3_STATUS_NO_DOCS) return SANE_STATUS_NO_DOCS; status = cs3_convert_options(s); if (status != SANE_STATUS_GOOD) return status; status = cs3_set_boundary(s); if (status != SANE_STATUS_GOOD) return status; cs3_set_focus(s); cs3_scanner_ready(s, CS3_STATUS_READY); if (type == CS3_SCAN_NORMAL) cs3_send_lut(s); status = cs3_set_window(s, type); if (status != SANE_STATUS_GOOD) return status; status = cs3_get_exposure(s); if (status != SANE_STATUS_GOOD) return status; /* cs3_scanner_ready(s, CS3_STATUS_READY); */ cs3_init_buffer(s); switch (s->n_colors) { case 3: cs3_parse_cmd(s, "1b 00 00 00 03 00 01 02 03"); break; case 4: cs3_parse_cmd(s, "1b 00 00 00 04 00 01 02 03 09"); break; default: DBG(0, "BUG: %s: Unknown number of input colors.\n", __func__); break; } status = cs3_issue_cmd(s); if (status != SANE_STATUS_GOOD) { DBG(6, "scan setup failed\n"); return status; } if (s->status == CS3_STATUS_REISSUE) { status = cs3_issue_cmd(s); if (status != SANE_STATUS_GOOD) return status; } return SANE_STATUS_GOOD; } static void * cs3_xmalloc(size_t size) { register void *value = malloc(size); if (value == NULL) { DBG(0, "error: %s: failed to malloc() %lu bytes.\n", __func__, (unsigned long) size); } return value; } static void * cs3_xrealloc(void *p, size_t size) { register void *value; if (!size) return p; value = realloc(p, size); if (value == NULL) { DBG(0, "error: %s: failed to realloc() %lu bytes.\n", __func__, (unsigned long) size); } return value; } static void cs3_xfree(void *p) { if (p) free(p); } backends-1.3.0/backend/coolscan3.conf.in000066400000000000000000000013611456256263500200330ustar00rootroot00000000000000# coolscan3.conf: sample configuration file for coolscan3 backend # # The following entry 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. backends-1.3.0/backend/dc210.c000066400000000000000000001043711456256263500156550ustar00rootroot00000000000000/*************************************************************************** * SANE - Scanner Access Now Easy. dc210.c 11/11/98 This file (C) 1998 Brian J. Murrell 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, see . 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 the Kodak DC-210 digital camera. THIS IS EXTREMELY ALPHA CODE! USE AT YOUR OWN RISK!! (feedback to: sane-dc210@interlinx.bc.ca This backend is based somewhat on the dc25 backend included in this package by Peter Fales ***************************************************************************/ #include "../include/sane/config.h" #include #include #include #include #include #include #include "../include/sane/sanei_jpeg.h" #include #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #define BACKEND_NAME dc210 #include "../include/sane/sanei_backend.h" #include "dc210.h" #ifndef PATH_MAX # define PATH_MAX 1024 #endif #define MAGIC (void *)0xab730324 #define DC210_CONFIG_FILE "dc210.conf" #define THUMBSIZE 20736 #ifdef B115200 # define DEFAULT_BAUD_RATE B115200 #else # define DEFAULT_BAUD_RATE B38400 #endif #if defined (__sgi) # define DEFAULT_TTY "/dev/ttyd1" /* Irix */ #elif defined (__sun) # define DEFAULT_TTY "/dev/term/a" /* Solaris */ #elif defined (hpux) # define DEFAULT_TTY "/dev/tty1d0" /* HP-UX */ #elif defined (__osf__) # define DEFAULT_TTY "/dev/tty00" /* Digital UNIX */ #else # define DEFAULT_TTY "/dev/ttyS0" /* Linux */ #endif static SANE_Bool is_open = 0; static SANE_Bool dc210_opt_thumbnails; static SANE_Bool dc210_opt_snap; static SANE_Bool dc210_opt_lowres; static SANE_Bool dc210_opt_erase; static SANE_Bool dumpinquiry; static struct jpeg_decompress_struct cinfo; static djpeg_dest_ptr dest_mgr = NULL; static unsigned long cmdrespause = 250000UL; /* pause after sending cmd */ static unsigned long breakpause = 1000000UL; /* pause after sending break */ static int bytes_in_buffer; static int bytes_read_from_buffer; static int total_bytes_read; static DC210 Camera; static SANE_Range image_range = { 0, 14, 0 }; static SANE_Option_Descriptor sod[] = { { SANE_NAME_NUM_OPTIONS, SANE_TITLE_NUM_OPTIONS, SANE_DESC_NUM_OPTIONS, SANE_TYPE_INT, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} } , #define D25_OPT_IMAGE_SELECTION 1 { "", "Image Selection", "Selection of the image to load.", SANE_TYPE_GROUP, SANE_UNIT_NONE, 0, 0, SANE_CONSTRAINT_NONE, {NULL} } , #define DC210_OPT_IMAGE_NUMBER 2 { "image", "Image Number", "Select Image Number to load from camera", SANE_TYPE_INT, SANE_UNIT_NONE, 4, SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_RANGE, {(void *) & image_range} } , #define DC210_OPT_THUMBS 3 { "thumbs", "Load Thumbnail", "Load the image as thumbnail.", SANE_TYPE_BOOL, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} } , #define DC210_OPT_SNAP 4 { "snap", "Snap new picture", "Take new picture and download it", SANE_TYPE_BOOL, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT /* | SANE_CAP_ADVANCED */ , SANE_CONSTRAINT_NONE, {NULL} } , #define DC210_OPT_LOWRES 5 { "lowres", "Low Resolution", "Resolution of new pictures", SANE_TYPE_BOOL, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE /* | SANE_CAP_ADVANCED */ , SANE_CONSTRAINT_NONE, {NULL} } , #define DC210_OPT_ERASE 6 { "erase", "Erase", "Erase the picture after downloading", SANE_TYPE_BOOL, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} } , #define DC210_OPT_DEFAULT 7 { "default-enhancements", "Defaults", "Set default values for enhancement controls.", SANE_TYPE_BUTTON, SANE_UNIT_NONE, 0, SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} } , #define DC210_OPT_INIT_DC210 8 { "camera-init", "Re-establish Communications", "Re-establish communications with camera (in case of timeout, etc.)", SANE_TYPE_BUTTON, SANE_UNIT_NONE, 0, SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} } }; static SANE_Parameters parms = { SANE_FRAME_RGB, 0, 0, /* Number of bytes returned per scan line: */ 0, /* Number of pixels per scan line. */ 0, /* Number of lines for the current scan. */ 8, /* Number of bits per sample. */ }; static unsigned char shoot_pck[] = SHOOT_PCK; static unsigned char init_pck[] = INIT_PCK; static unsigned char thumb_pck[] = THUMBS_PCK; static unsigned char pic_pck[] = PICS_PCK; static unsigned char pic_info_pck[] = PICS_INFO_PCK; static unsigned char info_pck[] = INFO_PCK; static unsigned char erase_pck[] = ERASE_PCK; static unsigned char res_pck[] = RES_PCK; static struct pkt_speed speeds[] = SPEEDS; static struct termios tty_orig; #include #include static int send_pck (int fd, unsigned char *pck) { int n; unsigned char r = 0xf0; /* prime the loop with a "camera busy" */ /* keep trying if camera says it's busy */ while (r == 0xf0) { /* * Not quite sure why we need this, but the program works a whole * lot better (at least on the DC210) with this short delay. */ if (write (fd, (char *) pck, 8) != 8) { DBG (2, "send_pck: error: write returned -1\n"); return -1; } /* need to wait before we read command result */ usleep (cmdrespause); if ((n = read (fd, (char *) &r, 1)) != 1) { DBG (2, "send_pck: error: read returned -1\n"); return -1; } } return (r == 0xd1) ? 0 : -1; } static int init_dc210 (DC210 * camera) { struct termios tty_new; int speed_index; for (speed_index = 0; speed_index < NELEMS (speeds); speed_index++) { if (speeds[speed_index].baud == camera->baud) { init_pck[2] = speeds[speed_index].pkt_code[0]; init_pck[3] = speeds[speed_index].pkt_code[1]; break; } } if (init_pck[2] == 0) { DBG (2, "unsupported baud rate.\n"); return -1; } /* Open device file. */ if ((camera->fd = open (camera->tty_name, O_RDWR)) == -1) { DBG (2, "init_dc210: error: could not open %s for read/write\n", camera->tty_name); return -1; } /* Save old device information to restore when we are done. */ if (tcgetattr (camera->fd, &tty_orig) == -1) { DBG (2, "init_dc210: error: could not get attributes\n"); return -1; } memcpy ((char *) &tty_new, (char *) &tty_orig, sizeof (struct termios)); /* We need the device to be raw. 8 bits even parity on 9600 baud to start. */ #ifdef HAVE_CFMAKERAW cfmakeraw (&tty_new); #else /* Modified to set the port REALLY as required. Code inspired by the gPhoto2 serial port setup */ /* input control settings */ tty_new.c_iflag &= ~(IGNBRK | IGNCR | INLCR | ICRNL | IUCLC | IXANY | IXON | IXOFF | INPCK | ISTRIP); tty_new.c_iflag |= (BRKINT | IGNPAR); /* output control settings */ tty_new.c_oflag &= ~OPOST; /* hardware control settings */ tty_new.c_cflag = (tty_new.c_cflag & ~CSIZE) | CS8; tty_new.c_cflag &= ~(PARENB | PARODD | CSTOPB); # if defined(__sgi) tty_new.c_cflag &= ~CNEW_RTSCTS; # else /* OS/2 doesn't have CRTSCTS - will this work for them? */ # ifdef CRTSCTS tty_new.c_cflag &= ~CRTSCTS; # endif # endif tty_new.c_cflag |= CLOCAL | CREAD; #endif /* line discipline settings */ tty_new.c_lflag &= ~(ICANON | ISIG | ECHO | ECHONL | ECHOE | ECHOK | IEXTEN); tty_new.c_cc[VMIN] = 0; tty_new.c_cc[VTIME] = 5; cfsetospeed (&tty_new, B9600); cfsetispeed (&tty_new, B9600); if (tcsetattr (camera->fd, TCSANOW, &tty_new) == -1) { DBG (2, "init_dc210: error: could not set attributes\n"); return -1; } /* send a break to get it back to a known state */ /* Used to supply a non-zero argument to tcsendbreak(), TCSBRK, * and TCSBRKP, but that is system dependent. e.g. on irix a non-zero * value does a drain instead of a break. A zero value is universally * used to send a break. */ #ifdef HAVE_TCSENDBREAK tcsendbreak (camera->fd, 0); # if defined(__sgi) tcdrain (camera->fd); # endif # elif defined(TCSBRKP) ioctl (camera->fd, TCSBRKP, 0); # elif defined(TCSBRK) ioctl (camera->fd, TCSBRK, 0); #endif /* and wait for it to recover from the break */ #ifdef HAVE_USLEEP usleep (breakpause); #else sleep (1); #endif if (send_pck (camera->fd, init_pck) == -1) { /* * The camera always powers up at 9600, so we try * that first. However, it may be already set to * a different speed. Try the entries in the table: */ for (speed_index = NELEMS (speeds) - 1; speed_index > 0; speed_index--) { int x; DBG (3, "init_dc210: changing speed to %d\n", (int) speeds[speed_index].baud); cfsetospeed (&tty_new, speeds[speed_index].baud); cfsetispeed (&tty_new, speeds[speed_index].baud); if (tcsetattr (camera->fd, TCSANOW, &tty_new) == -1) { DBG (2, "init_dc210: error: could not set attributes\n"); return -1; } for (x = 0; x < 3; x++) if (send_pck (camera->fd, init_pck) != -1) break; } if (speed_index == 0) { tcsetattr (camera->fd, TCSANOW, &tty_orig); DBG (2, "init_dc210: error: no suitable baud rate\n"); return -1; } } /* Set speed to requested speed. */ cfsetospeed (&tty_new, Camera.baud); cfsetispeed (&tty_new, Camera.baud); if (tcsetattr (camera->fd, TCSANOW, &tty_new) == -1) { DBG (2, "init_dc210: error: could not set attributes\n"); return -1; } return camera->fd; } static void close_dc210 (int fd) { /* * Put the camera back to 9600 baud */ if (close (fd) == -1) { DBG (4, "close_dc210: error: could not close device\n"); } } int get_info (DC210 * camera) { char f[] = "get_info"; unsigned char buf[256]; if (send_pck (camera->fd, info_pck) == -1) { DBG (2, "%s: error: send_pck returned -1\n", f); return -1; } DBG (9, "%s: read info packet\n", f); if (read_data (camera->fd, buf, 256) == -1) { DBG (2, "%s: error: read_data returned -1\n", f); return -1; } if (end_of_data (camera->fd) == -1) { DBG (2, "%s: error: end_of_data returned -1\n", f); return -1; } camera->model = buf[1]; camera->ver_major = buf[2]; camera->ver_minor = buf[3]; camera->pic_taken = buf[56] << 8 | buf[57]; camera->pic_left = buf[72] << 8 | buf[73]; camera->flags.low_res = buf[22]; camera->flags.low_batt = buf[8]; return 0; } static int read_data (int fd, unsigned char *buf, int sz) { unsigned char ccsum; unsigned char rcsum; unsigned char c; int n; int r = 0; int i; /* read the control byte */ if (read (fd, &c, 1) != 1) { DBG (2, "read_data: error: read for packet control byte returned bad status\n"); return -1; } if (c != 1) { DBG (2, "read_data: error: incorrect packet control byte: %02x\n", c); return -1; } for (n = 0; n < sz && (r = read (fd, (char *) &buf[n], sz - n)) > 0; n += r) ; if (r <= 0) { DBG (2, "read_data: error: read returned -1\n"); return -1; } if (n < sz || read (fd, &rcsum, 1) != 1) { DBG (2, "read_data: error: buffer underrun or no checksum\n"); return -1; } for (i = 0, ccsum = 0; i < n; i++) ccsum ^= buf[i]; if (ccsum != rcsum) { DBG (2, "read_data: error: bad checksum (%02x !=%02x)\n", rcsum, ccsum); return -1; } c = 0xd2; if (write (fd, (char *) &c, 1) != 1) { DBG (2, "read_data: error: write ack\n"); return -1; } return 0; } static int end_of_data (int fd) { unsigned char c; do { /* loop until the camera isn't busy */ if (read (fd, &c, 1) != 1) { DBG (2, "end_of_data: error: read returned -1\n"); return -1; } if (c == 0) /* got successful end of data */ return 0; /* return success */ sleep (1); /* not too fast */ } while (c == 0xf0); /* Accck! Not busy, but not a good end of data either */ if (c != 0) { DBG (2, "end_of_data: error: bad EOD from camera (%02x)\n", (unsigned) c); return -1; } return 0; /* should never get here but shut gcc -Wall up */ } static int erase (int fd) { if (send_pck (fd, erase_pck) == -1) { DBG (3, "erase: error: send_pck returned -1\n"); return -1; } if (end_of_data (fd) == -1) { DBG (3, "erase: error: end_of_data returned -1\n"); return -1; } return 0; } static int change_res (int fd, unsigned char res) { char f[] = "change_res"; DBG (127, "%s called\n", f); if (res != 0 && res != 1) { DBG (3, "%s: error: unsupported resolution\n", f); return -1; } /* cameras resolution semantics are opposite of ours */ res = !res; DBG (127, "%s: setting res to %d\n", f, res); res_pck[2] = res; if (send_pck (fd, res_pck) == -1) { DBG (4, "%s: error: send_pck returned -1\n", f); } if (end_of_data (fd) == -1) { DBG (4, "%s: error: end_of_data returned -1\n", f); } return 0; } SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize) { char f[] = "sane_init"; char dev_name[PATH_MAX], *p; size_t len; FILE *fp; int baud; DBG_INIT (); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); fp = sanei_config_open (DC210_CONFIG_FILE); /* defaults */ Camera.baud = DEFAULT_BAUD_RATE; Camera.tty_name = DEFAULT_TTY; if (!fp) { /* default to /dev/whatever instead of insisting on config file */ DBG (1, "%s: missing config file '%s'\n", f, DC210_CONFIG_FILE); } else { while (sanei_config_read (dev_name, sizeof (dev_name), fp)) { dev_name[sizeof (dev_name) - 1] = '\0'; DBG (20, "%s: config- %s\n", f, dev_name); if (dev_name[0] == '#') continue; /* ignore line comments */ len = strlen (dev_name); if (!len) continue; /* ignore empty lines */ if (strncmp (dev_name, "port=", 5) == 0) { p = strchr (dev_name, '/'); if (p) Camera.tty_name = strdup (p); DBG (20, "Config file port=%s\n", Camera.tty_name); } else if (strncmp (dev_name, "baud=", 5) == 0) { baud = atoi (&dev_name[5]); switch (baud) { case 9600: Camera.baud = B9600; break; case 19200: Camera.baud = B19200; break; case 38400: Camera.baud = B38400; break; #ifdef B57600 case 57600: Camera.baud = B57600; break; #endif #ifdef B115200 case 115200: Camera.baud = B115200; break; #endif } DBG (20, "Config file baud=%d\n", Camera.baud); } else if (strcmp (dev_name, "dumpinquiry") == 0) { dumpinquiry = SANE_TRUE; } else if (strncmp (dev_name, "cmdrespause=", 12) == 0) { cmdrespause = atoi (&dev_name[12]); DBG (20, "Config file cmdrespause=%lu\n", cmdrespause); } else if (strncmp (dev_name, "breakpause=", 11) == 0) { breakpause = atoi (&dev_name[11]); DBG (20, "Config file breakpause=%lu\n", breakpause); } } fclose (fp); } if (init_dc210 (&Camera) == -1) return SANE_STATUS_INVAL; if (get_info (&Camera) == -1) { DBG (2, "error: could not get info\n"); close_dc210 (Camera.fd); return SANE_STATUS_INVAL; } if (Camera.pic_taken == 0) { sod[DC210_OPT_IMAGE_NUMBER].cap |= SANE_CAP_INACTIVE; image_range.min = 0; image_range.max = 0; } else { sod[DC210_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE; image_range.min = 1; image_range.max = Camera.pic_taken; } /* load the current images array */ Camera.Pictures = get_pictures_info (); if (Camera.pic_taken == 0) { Camera.current_picture_number = 0; parms.bytes_per_line = 0; parms.pixels_per_line = 0; parms.lines = 0; } else { Camera.current_picture_number = 1; if (Camera.Pictures[Camera.current_picture_number - 1].low_res) { parms.bytes_per_line = 640 * 3; parms.pixels_per_line = 640; parms.lines = 480; } else { parms.bytes_per_line = 1152 * 3; parms.pixels_per_line = 1152; parms.lines = 864; } } if (dumpinquiry) { DBG (0, "\nCamera information:\n~~~~~~~~~~~~~~~~~\n\n"); DBG (0, "Model...........: DC%x\n", Camera.model); DBG (0, "Firmware version: %d.%d\n", Camera.ver_major, Camera.ver_minor); DBG (0, "Pictures........: %d/%d\n", Camera.pic_taken, Camera.pic_taken + Camera.pic_left); DBG (0, "Resolution......: %s\n", Camera.flags.low_res ? "low" : "high"); DBG (0, "Battery state...: %s\n", Camera.flags.low_batt ? "low" : "good"); } return SANE_STATUS_GOOD; } void sane_exit (void) { } /* Device select/open/close */ static const SANE_Device dev[] = { { "0", "Kodak", "DC-210", "still camera"}, }; SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only) { static const SANE_Device *devlist[] = { dev + 0, 0 }; DBG (127, "sane_get_devices called\n"); *device_list = devlist; return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { int i; DBG (127, "sane_open for device %s\n", devicename); if (!devicename[0]) { i = 0; } else { for (i = 0; i < NELEMS (dev); ++i) { if (strcmp (devicename, dev[i].name) == 0) { break; } } } if (i >= NELEMS (dev)) { return SANE_STATUS_INVAL; } if (is_open) { return SANE_STATUS_DEVICE_BUSY; } is_open = 1; *handle = MAGIC; DBG (3, "sane_open: pictures taken=%d\n", Camera.pic_taken); return SANE_STATUS_GOOD; } void sane_close (SANE_Handle handle) { DBG (127, "sane_close called\n"); if (handle == MAGIC) is_open = 0; DBG (127, "sane_close returning\n"); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { if (handle != MAGIC || !is_open) return NULL; /* wrong device */ if (option < 0 || option >= NELEMS (sod)) return NULL; return &sod[option]; } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int * info) { SANE_Int myinfo = 0; SANE_Status status; DBG (127, "control_option(handle=%p,opt=%s,act=%s,val=%p,info=%p)\n", handle, sod[option].title, (action == SANE_ACTION_SET_VALUE ? "SET" : (action == SANE_ACTION_GET_VALUE ? "GET" : "SETAUTO")), value, (void *)info); if (handle != MAGIC || !is_open) return SANE_STATUS_INVAL; /* Unknown handle ... */ if (option < 0 || option >= NELEMS (sod)) return SANE_STATUS_INVAL; /* Unknown option ... */ switch (action) { case SANE_ACTION_SET_VALUE: status = sanei_constrain_value (sod + option, value, &myinfo); if (status != SANE_STATUS_GOOD) { DBG (1, "Constraint error in control_option\n"); return status; } switch (option) { case DC210_OPT_IMAGE_NUMBER: Camera.current_picture_number = *(SANE_Word *) value; myinfo |= SANE_INFO_RELOAD_PARAMS; /* get the image's resolution */ if (Camera.Pictures[Camera.current_picture_number - 1].low_res) { parms.bytes_per_line = 640 * 3; parms.pixels_per_line = 640; parms.lines = 480; } else { parms.bytes_per_line = 1152 * 3; parms.pixels_per_line = 1152; parms.lines = 864; } break; case DC210_OPT_THUMBS: dc210_opt_thumbnails = !!*(SANE_Word *) value; myinfo |= SANE_INFO_RELOAD_PARAMS; if (dc210_opt_thumbnails) { /* * DC210 thumbnail are 96x72x8x3 */ parms.bytes_per_line = 96 * 3; parms.pixels_per_line = 96; parms.lines = 72; } else { if (Camera.Pictures[Camera.current_picture_number - 1].low_res) { parms.bytes_per_line = 640 * 3; parms.pixels_per_line = 640; parms.lines = 480; } else { parms.bytes_per_line = 1152 * 3; parms.pixels_per_line = 1152; parms.lines = 864; } } break; case DC210_OPT_SNAP: dc210_opt_snap = !!*(SANE_Word *) value; myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; /* if we are snapping a new one */ if (dc210_opt_snap) { /* activate the resolution setting */ sod[DC210_OPT_LOWRES].cap &= ~SANE_CAP_INACTIVE; /* and de-activate the image number selector */ sod[DC210_OPT_IMAGE_NUMBER].cap |= SANE_CAP_INACTIVE; } else { /* deactivate the resolution setting */ sod[DC210_OPT_LOWRES].cap |= SANE_CAP_INACTIVE; /* and activate the image number selector */ sod[DC210_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE; } /* set params according to resolution settings */ if (dc210_opt_lowres) { parms.bytes_per_line = 640 * 3; parms.pixels_per_line = 640; parms.lines = 480; } else { parms.bytes_per_line = 1152 * 3; parms.pixels_per_line = 1152; parms.lines = 864; } break; case DC210_OPT_LOWRES: dc210_opt_lowres = !!*(SANE_Word *) value; myinfo |= SANE_INFO_RELOAD_PARAMS; if (!dc210_opt_thumbnails) { /* XXX - change the number of pictures left depending on resolution perhaps just call get_info again? */ if (dc210_opt_lowres) { parms.bytes_per_line = 640 * 3; parms.pixels_per_line = 640; parms.lines = 480; } else { parms.bytes_per_line = 1152 * 3; parms.pixels_per_line = 1152; parms.lines = 864; } } break; case DC210_OPT_ERASE: dc210_opt_erase = !!*(SANE_Word *) value; break; case DC210_OPT_DEFAULT: DBG (1, "Fixme: Set all defaults here!\n"); break; case DC210_OPT_INIT_DC210: if ((Camera.fd = init_dc210 (&Camera)) == -1) { return SANE_STATUS_INVAL; } break; default: return SANE_STATUS_INVAL; } break; case SANE_ACTION_GET_VALUE: switch (option) { case 0: *(SANE_Word *) value = NELEMS (sod); break; case DC210_OPT_IMAGE_NUMBER: *(SANE_Word *) value = Camera.current_picture_number; break; case DC210_OPT_THUMBS: *(SANE_Word *) value = dc210_opt_thumbnails; break; case DC210_OPT_SNAP: *(SANE_Word *) value = dc210_opt_snap; break; case DC210_OPT_LOWRES: *(SANE_Word *) value = dc210_opt_lowres; break; case DC210_OPT_ERASE: *(SANE_Word *) value = dc210_opt_erase; break; default: return SANE_STATUS_INVAL; } break; case SANE_ACTION_SET_AUTO: switch (option) { default: return SANE_STATUS_UNSUPPORTED; /* We are DUMB */ } } if (info) *info = myinfo; return SANE_STATUS_GOOD; } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { int rc = SANE_STATUS_GOOD; DBG (127, "sane_get_params called\n"); if (handle != MAGIC || !is_open) rc = SANE_STATUS_INVAL; /* Unknown handle ... */ parms.last_frame = SANE_TRUE; /* Have no idea what this does */ *params = parms; DBG (127, "sane_get_params return %d\n", rc); return rc; } typedef struct { struct jpeg_source_mgr pub; JOCTET *buffer; } my_source_mgr; typedef my_source_mgr *my_src_ptr; METHODDEF (void) sanei_jpeg_init_source (j_decompress_ptr __sane_unused__ cinfo) { /* nothing to do */ } METHODDEF (boolean) sanei_jpeg_fill_input_buffer (j_decompress_ptr cinfo) { my_src_ptr src = (my_src_ptr) cinfo->src; if (read_data (Camera.fd, src->buffer, 1024) == -1) { DBG (5, "sane_start: read_data failed\n"); src->buffer[0] = (JOCTET) 0xFF; src->buffer[1] = (JOCTET) JPEG_EOI; return FALSE; } src->pub.next_input_byte = src->buffer; src->pub.bytes_in_buffer = 1024; return TRUE; } METHODDEF (void) sanei_jpeg_skip_input_data (j_decompress_ptr cinfo, long num_bytes) { my_src_ptr src = (my_src_ptr) cinfo->src; if (num_bytes > 0) { while (num_bytes > (long) src->pub.bytes_in_buffer) { num_bytes -= (long) src->pub.bytes_in_buffer; (void) sanei_jpeg_fill_input_buffer (cinfo); } } src->pub.next_input_byte += (size_t) num_bytes; src->pub.bytes_in_buffer -= (size_t) num_bytes; } METHODDEF (void) sanei_jpeg_term_source (j_decompress_ptr __sane_unused__ cinfo) { /* no work necessary here */ } SANE_Status sane_start (SANE_Handle handle) { DBG (127, "sane_start called\n"); if (handle != MAGIC || !is_open || (Camera.current_picture_number == 0 && dc210_opt_snap == SANE_FALSE)) return SANE_STATUS_INVAL; /* Unknown handle ... */ if (Camera.scanning) return SANE_STATUS_EOF; if (dc210_opt_snap) { /* * Don't allow picture unless there is room in the * camera. */ if (Camera.pic_left == 0) { DBG (3, "No room to store new picture\n"); return SANE_STATUS_INVAL; } if (snap_pic (Camera.fd) != SANE_STATUS_GOOD) { DBG (1, "Failed to snap new picture\n"); return SANE_STATUS_INVAL; } } if (dc210_opt_thumbnails) { thumb_pck[3] = (unsigned char) Camera.current_picture_number - 1; thumb_pck[4] = 1; if (send_pck (Camera.fd, thumb_pck) == -1) { DBG (4, "sane_start: error: send_pck returned -1\n"); return SANE_STATUS_INVAL; } parms.bytes_per_line = 96 * 3; parms.pixels_per_line = 96; parms.lines = 72; bytes_in_buffer = 0; bytes_read_from_buffer = 0; } else { my_src_ptr src; struct jpeg_error_mgr jerr; pic_pck[3] = (unsigned char) Camera.current_picture_number - 1; if (send_pck (Camera.fd, pic_pck) == -1) { DBG (4, "sane_start: error: send_pck returned -1\n"); return SANE_STATUS_INVAL; } cinfo.err = jpeg_std_error (&jerr); jpeg_create_decompress (&cinfo); cinfo.src = (struct jpeg_source_mgr *) (*cinfo.mem->alloc_small) ((j_common_ptr) & cinfo, JPOOL_PERMANENT, sizeof (my_source_mgr)); src = (my_src_ptr) cinfo.src; src->buffer = (JOCTET *) (*cinfo.mem->alloc_small) ((j_common_ptr) & cinfo, JPOOL_PERMANENT, 1024 * sizeof (JOCTET)); src->pub.init_source = sanei_jpeg_init_source; src->pub.fill_input_buffer = sanei_jpeg_fill_input_buffer; src->pub.skip_input_data = sanei_jpeg_skip_input_data; src->pub.resync_to_restart = jpeg_resync_to_restart; /* default */ src->pub.term_source = sanei_jpeg_term_source; src->pub.bytes_in_buffer = 0; src->pub.next_input_byte = NULL; (void) jpeg_read_header (&cinfo, TRUE); dest_mgr = sanei_jpeg_jinit_write_ppm (&cinfo); (void) jpeg_start_decompress (&cinfo); } Camera.scanning = SANE_TRUE; /* don't overlap scan requests */ total_bytes_read = 0; return SANE_STATUS_GOOD; } SANE_Status sane_read (SANE_Handle __sane_unused__ handle, SANE_Byte * data, SANE_Int max_length, SANE_Int * length) { static char buffer[1024]; if (dc210_opt_thumbnails) { if (total_bytes_read == THUMBSIZE) { if (dc210_opt_erase) { if (erase (Camera.fd) == -1) { DBG (1, "Failed to erase memory\n"); return SANE_STATUS_INVAL; } Camera.pic_taken--; Camera.pic_left++; Camera.current_picture_number = Camera.pic_taken; image_range.max--; } return SANE_STATUS_EOF; } *length = 0; if (!(bytes_in_buffer - bytes_read_from_buffer)) { if (read_data (Camera.fd, (unsigned char *) buffer, 1024) == -1) { DBG (5, "sane_read: read_data failed\n"); return SANE_STATUS_INVAL; } bytes_in_buffer = 1024; bytes_read_from_buffer = 0; } while (bytes_read_from_buffer < bytes_in_buffer && max_length && total_bytes_read < THUMBSIZE) { *data++ = buffer[bytes_read_from_buffer++]; (*length)++; max_length--; total_bytes_read++; } if (total_bytes_read == THUMBSIZE) { if (end_of_data (Camera.fd) == -1) { DBG (4, "sane_read: end_of_data error\n"); return SANE_STATUS_INVAL; } else { return SANE_STATUS_GOOD; } } else { return SANE_STATUS_GOOD; } } else { int lines = 0; if (cinfo.output_scanline >= cinfo.output_height) { /* clean up comms with the camera */ if (end_of_data (Camera.fd) == -1) { DBG (2, "sane_read: error: end_of_data returned -1\n"); return SANE_STATUS_INVAL; } if (dc210_opt_erase) { DBG (127, "sane_read bp%d, erase image\n", __LINE__); if (erase (Camera.fd) == -1) { DBG (1, "Failed to erase memory\n"); return SANE_STATUS_INVAL; } Camera.pic_taken--; Camera.pic_left++; Camera.current_picture_number = Camera.pic_taken; image_range.max--; } return SANE_STATUS_EOF; } /* XXX - we should read more than 1 line at a time here */ lines = 1; (void) jpeg_read_scanlines (&cinfo, dest_mgr->buffer, lines); (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, lines, (char *) data); *length = cinfo.output_width * cinfo.output_components * lines; return SANE_STATUS_GOOD; } } void sane_cancel (SANE_Handle __sane_unused__ handle) { DBG (127, "sane_cancel() called\n"); if (Camera.scanning) Camera.scanning = SANE_FALSE; /* done with scan */ else DBG (127, "sane_cancel() aborted, scanner not scanning\n"); } SANE_Status sane_set_io_mode (SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking) { return SANE_STATUS_UNSUPPORTED; } SANE_Status sane_get_select_fd (SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ * fd) { return SANE_STATUS_UNSUPPORTED; } static PictureInfo * get_pictures_info (void) { char f[] = "get_pictures_info"; unsigned int p; PictureInfo *pics; if ((pics = (PictureInfo *) malloc (Camera.pic_taken * sizeof (PictureInfo))) == NULL) { DBG (4, "%s: error: allocate memory for pictures array\n", f); return NULL; } for (p = 0; p < (unsigned int) Camera.pic_taken; p++) { if (get_picture_info (pics + p, p) == -1) { free (pics); return NULL; } } return pics; } static int get_picture_info (PictureInfo * pic, int p) { char f[] = "get_picture_info"; static char buffer[256]; DBG (4, "%s: info for pic #%d\n", f, p); pic_info_pck[3] = (unsigned char) p; if (send_pck (Camera.fd, pic_info_pck) == -1) { DBG (4, "%s: error: send_pck returned -1\n", f); return -1; } if (read_data (Camera.fd, (unsigned char *) buffer, 256) == -1) { DBG (2, "%s: error: read_data returned -1\n", f); return -1; } if (end_of_data (Camera.fd) == -1) { DBG (2, "%s: error: end_of_data returned -1\n", f); return -1; } if (buffer[3] == 0) { pic->low_res = SANE_TRUE; } else if (buffer[3] == 1) { pic->low_res = SANE_FALSE; } else { DBG (2, "%s: error: unknown resolution code %u\n", f, buffer[3]); return -1; } pic->size = (buffer[8] & 0xFF) << 24; pic->size |= (buffer[9] & 0xFF) << 16; pic->size |= (buffer[10] & 0xFF) << 8; pic->size |= (buffer[11] & 0xFF); return 0; } static SANE_Status snap_pic (int fd) { char f[] = "snap_pic"; /* make sure camera is set to our settings state */ if (change_res (Camera.fd, dc210_opt_lowres) == -1) { DBG (1, "%s: Failed to set resolution\n", f); return SANE_STATUS_INVAL; } /* take the picture */ if (send_pck (fd, shoot_pck) == -1) { DBG (4, "%s: error: send_pck returned -1\n", f); return SANE_STATUS_INVAL; } else { if (end_of_data (Camera.fd) == -1) { DBG (2, "%s: error: end_of_data returned -1\n", f); return SANE_STATUS_INVAL; } } Camera.pic_taken++; Camera.pic_left--; Camera.current_picture_number = Camera.pic_taken; image_range.max++; sod[DC210_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE; /* add this one to the Pictures array */ if ((Camera.Pictures = (PictureInfo *) realloc (Camera.Pictures, Camera.pic_taken * sizeof (PictureInfo))) == NULL) { DBG (4, "%s: error: allocate memory for pictures array\n", f); return SANE_STATUS_INVAL; } if (get_picture_info (Camera.Pictures + Camera.pic_taken, Camera.pic_taken) == -1) { DBG (1, "%s: Failed to get new picture info\n", f); /* XXX - I guess we should try to erase the image here */ return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } backends-1.3.0/backend/dc210.conf.in000066400000000000000000000017271456256263500167660ustar00rootroot00000000000000# 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; backends-1.3.0/backend/dc210.h000066400000000000000000000162661456256263500156670ustar00rootroot00000000000000/*************************************************************************** * SANE - Scanner Access Now Easy. dc210.c 11/11/98 This file (C) 1998 Brian J. Murrell 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, see . 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 the Kodak DC-210 digital camera. THIS IS EXTREMELY ALPHA CODE! USE AT YOUR OWN RISK!! (feedback to: sane-dc210@interlinx.bc.ca This backend is based somewhat on the dc25 backend included in this package by Peter Fales ***************************************************************************/ #include #include #include #include #include #include #ifndef TRUE #define TRUE (1==1) #endif #ifndef FALSE #define FALSE (!TRUE) #endif #ifndef NULL #define NULL 0L #endif typedef struct picture_info { unsigned int low_res; unsigned int size; } PictureInfo; typedef struct DC210_s { int fd; /* file descriptor to talk to it */ char *tty_name; /* the tty port name it's on */ speed_t baud; /* current tty speed */ SANE_Bool scanning; /* currently scanning an image? */ unsigned char model; unsigned char ver_major; unsigned char ver_minor; int pic_taken; int pic_left; struct { unsigned int low_res:1; unsigned int low_batt:1; } flags; PictureInfo *Pictures; /* array of pictures */ unsigned int current_picture_number; /* picture being operated on */ } DC210; typedef struct dc210_info_s { unsigned char model; unsigned char ver_major; unsigned char ver_minor; int pic_taken; int pic_left; struct { unsigned int low_res:1; unsigned int low_batt:1; } flags; } Dc210Info, *Dc210InfoPtr; static int get_info (DC210 *); #define INIT_PCK {0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A} /* ^^^^^^^^^^ * Baud rate: (see pkt_speed structure) * 0x96 0x00 -> 9600 baud * 0x19 0x20 -> 19200 baud * 0x38 0x40 -> 38400 baud * 0x57 0x60 -> 57600 baud * 0x11 0x52 -> 115200 baud */ #define INFO_PCK {0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A} #define SHOOT_PCK {0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A} #define ERASE_PCK {0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A} #define RES_PCK {0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A} /* ^^^^ * Resolution: 0x00 = high, 0x01 = low */ #define THUMBS_PCK {0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A} /* ^^^^ * Thumbnail number */ #define PICS_PCK {0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A} /* ^^^^ * Picture number */ #define PICS_INFO_PCK {0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A} /* ^^^^ * Picture number */ struct pkt_speed { speed_t baud; unsigned char pkt_code[2]; }; #if defined (B57600) && defined (B115200) # define SPEEDS { { B9600, { 0x96, 0x00 } }, \ { B19200, { 0x19, 0x20 } }, \ { B38400, { 0x38, 0x40 } }, \ { B57600, { 0x57, 0x60 } }, \ { B115200, { 0x11, 0x52 } } } #else # define SPEEDS { { B9600, { 0x96, 0x00 } }, \ { B19200, { 0x19, 0x20 } }, \ { B38400, { 0x38, 0x40 } } } #endif #define HIGH_RES 0 #define LOW_RES 1 /* * Image parameters */ #define LOW_CAMERA_HEADER 256 #define HIGH_CAMERA_HEADER 512 #define CAMERA_HEADER(r) ( (r) ? LOW_CAMERA_HEADER : HIGH_CAMERA_HEADER ) #define LOW_WIDTH 256 #define HIGH_WIDTH 512 #define WIDTH(r) ( (r) ? LOW_WIDTH : HIGH_WIDTH ) #define HEIGHT 243 #define LEFT_MARGIN 1 #define LOW_RIGHT_MARGIN 5 #define HIGH_RIGHT_MARGIN 10 #define RIGHT_MARGIN(r) ( (r) ? LOW_RIGHT_MARGIN : HIGH_RIGHT_MARGIN ) #define TOP_MARGIN 1 #define BOTTOM_MARGIN 1 #define BLOCK_SIZE 1024 #define LOW_BLOCKS 61 #define HIGH_BLOCKS 122 #define BLOCKS(r) ( (r) ? LOW_BLOCKS : HIGH_BLOCKS ) #define LOW_IMAGE_SIZE ( LOW_BLOCKS * BLOCK_SIZE ) #define HIGH_IMAGE_SIZE ( HIGH_BLOCKS * BLOCK_SIZE ) #define IMAGE_SIZE(r) ( (r) ? LOW_IMAGE_SIZE : HIGH_IMAGE_SIZE ) #define MAX_IMAGE_SIZE ( HIGH_IMAGE_SIZE ) /* * Comet file */ #define COMET_MAGIC "COMET" #define COMET_HEADER_SIZE 128 #define COMET_EXT "cmt" /* * Pixmap structure */ struct pixmap { int width; int height; int components; unsigned char *planes; }; /* * Rotations */ #define ROT_STRAIGHT 0x00 #define ROT_LEFT 0x01 #define ROT_RIGHT 0x02 #define ROT_HEADDOWN 0x03 #define ROT_MASK 0x03 /* * File formats */ #define SAVE_RAW 0x01 #define SAVE_GREYSCALE 0x02 #define SAVE_24BITS 0x04 #define SAVE_FILES 0x07 #define SAVE_FORMATS 0x38 #define SAVE_ADJASPECT 0x80 /* * External definitions */ extern char *__progname; /* Defined in /usr/lib/crt0.o */ #include FILE *sanei_config_open (const char *filename); static int init_dc210 (DC210 *); static void close_dc210 (int); static int read_data (int fd, unsigned char *buf, int sz); static int end_of_data (int fd); static PictureInfo *get_pictures_info (void); static int get_picture_info (PictureInfo * pic, int p); static SANE_Status snap_pic (int); char *sanei_config_read (char *str, int n, FILE * stream); backends-1.3.0/backend/dc240.c000066400000000000000000001403371456256263500156620ustar00rootroot00000000000000/*************************************************************************** * _S_A_N_E - Scanner Access Now Easy. dc240.c 03/12/01 - Peter Fales Based on the dc210 driver, (C) 1998 Brian J. Murrell (which is based on dc25 driver (C) 1998 by Peter Fales) This file (C) 2001 by Peter Fales 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, see . 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 the Kodak DC-240 digital camera. THIS IS EXTREMELY ALPHA CODE! USE AT YOUR OWN RISK!! (feedback to: dc240-devel@fales-lorenz.net) This backend is based somewhat on the dc25 backend included in this package by Peter Fales, and the dc210 backend by Brian J. Murrell ***************************************************************************/ #include "../include/sane/config.h" #include #include #include #include #include #include #include "../include/sane/sanei_jpeg.h" #include #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #define BACKEND_NAME dc240 #include "../include/sane/sanei_backend.h" #include "dc240.h" #ifndef PATH_MAX # define PATH_MAX 1024 #endif #define MAGIC (void *)0xab730324 #define DC240_CONFIG_FILE "dc240.conf" #define THUMBSIZE 20736 #ifdef B115200 # define DEFAULT_BAUD_RATE B115200 #else # define DEFAULT_BAUD_RATE B38400 #endif #if defined (__sgi) # define DEFAULT_TTY "/dev/ttyd1" /* Irix */ #elif defined (__sun) # define DEFAULT_TTY "/dev/term/a" /* Solaris */ #elif defined (hpux) # define DEFAULT_TTY "/dev/tty1d0" /* HP-UX */ #elif defined (__osf__) # define DEFAULT_TTY "/dev/tty00" /* Digital UNIX */ #else # define DEFAULT_TTY "/dev/ttyS0" /* Linux */ #endif static SANE_Bool is_open = 0; static SANE_Bool dc240_opt_thumbnails; static SANE_Bool dc240_opt_snap; static SANE_Bool dc240_opt_lowres; static SANE_Bool dc240_opt_erase; static SANE_Bool dc240_opt_autoinc; static SANE_Bool dumpinquiry; static struct jpeg_decompress_struct cinfo; static djpeg_dest_ptr dest_mgr = NULL; static unsigned long cmdrespause = 250000UL; /* pause after sending cmd */ static unsigned long breakpause = 1000000UL; /* pause after sending break */ static DC240 Camera; static SANE_Range image_range = { 0, 0, 0 }; static SANE_String **folder_list; static SANE_Int current_folder = 0; static SANE_Option_Descriptor sod[] = { { SANE_NAME_NUM_OPTIONS, SANE_TITLE_NUM_OPTIONS, SANE_DESC_NUM_OPTIONS, SANE_TYPE_INT, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} } , #define DC240_OPT_IMAGE_SELECTION 1 { "", "Image Selection", "Selection of the image to load.", SANE_TYPE_GROUP, SANE_UNIT_NONE, 0, 0, SANE_CONSTRAINT_NONE, {NULL} } , #define DC240_OPT_FOLDER 2 { "folder", "Folder", "Select folder within camera", SANE_TYPE_STRING, SANE_UNIT_NONE, 256, SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_STRING_LIST, {NULL} } , #define DC240_OPT_IMAGE_NUMBER 3 { "image", "Image Number", "Select Image Number to load from camera", SANE_TYPE_INT, SANE_UNIT_NONE, 4, SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_RANGE, {(SANE_String_Const *) & image_range} /* this is ANSI conformant! */ } , #define DC240_OPT_THUMBS 4 { "thumbs", "Load Thumbnail", "Load the image as thumbnail.", SANE_TYPE_BOOL, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} } , #define DC240_OPT_SNAP 5 { "snap", "Snap new picture", "Take new picture and download it", SANE_TYPE_BOOL, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT /* | SANE_CAP_ADVANCED */ , SANE_CONSTRAINT_NONE, {NULL} } , #define DC240_OPT_LOWRES 6 { "lowres", "Low Resolution", "Resolution of new pictures", SANE_TYPE_BOOL, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE /* | SANE_CAP_ADVANCED */ , SANE_CONSTRAINT_NONE, {NULL} } , #define DC240_OPT_ERASE 7 { "erase", "Erase", "Erase the picture after downloading", SANE_TYPE_BOOL, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} } , #define DC240_OPT_DEFAULT 8 { "default-enhancements", "Defaults", "Set default values for enhancement controls.", SANE_TYPE_BUTTON, SANE_UNIT_NONE, 0, SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} } , #define DC240_OPT_INIT_DC240 9 { "camera-init", "Re-establish Communications", "Re-establish communications with camera (in case of timeout, etc.)", SANE_TYPE_BUTTON, SANE_UNIT_NONE, 0, SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} } , #define DC240_OPT_AUTOINC 10 { "autoinc", "Auto Increment", "Increment image number after each scan", SANE_TYPE_BOOL, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED, SANE_CONSTRAINT_NONE, {NULL} } , }; static SANE_Parameters parms = { SANE_FRAME_RGB, 0, 0, /* Number of bytes returned per scan line: */ 0, /* Number of pixels per scan line. */ 0, /* Number of lines for the current scan. */ 8, /* Number of bits per sample. */ }; static SANE_Byte shoot_pck[] = SHOOT_PCK; static SANE_Byte init_pck[] = INIT_PCK; static SANE_Byte thumb_pck[] = THUMBS_PCK; static SANE_Byte pic_pck[] = PICS_PCK; static SANE_Byte pic_info_pck[] = PICS_INFO_PCK; static SANE_Byte info_pck[] = INFO_PCK; static SANE_Byte erase_pck[] = ERASE_PCK; static SANE_Byte res_pck[] = RES_PCK; static SANE_Byte open_card_pck[] = OPEN_CARD_PCK; static SANE_Byte read_dir_pck[] = READ_DIR_PCK; static struct pkt_speed speeds[] = SPEEDS; static struct termios tty_orig; SANE_Byte dir_buf2[2 + CAMDIRENTRYSIZE * DIRENTRIES]; static struct cam_dirlist *dir_head = NULL; static SANE_Byte info_buf[256]; static SANE_Byte name_buf[60]; #include #include static SANE_Int send_pck (SANE_Int fd, SANE_Byte * pck) { SANE_Int n; SANE_Byte r = 0xf0; /* prime the loop with a "camera busy" */ DBG (127, "send_pck<%x %x %x %x %x %x %x %x>\n", pck[0], pck[1], pck[2], pck[3], pck[4], pck[5], pck[6], pck[7]); /* keep trying if camera says it's busy */ while (r == 0xf0) { if (write (fd, (char *) pck, 8) != 8) { DBG (1, "send_pck: error: write returned -1\n"); return -1; } /* need to wait before we read command result */ usleep (cmdrespause); if ((n = read (fd, (char *) &r, 1)) != 1) { DBG (1, "send_pck: error: read returned -1\n"); return -1; } } DBG (127, "send_pck: read one byte result from camera = %x\n", r); return (r == 0xd1) ? 0 : -1; } static SANE_Int init_dc240 (DC240 * camera) { struct termios tty_new; SANE_Int speed_index; SANE_Char buf[5], n; DBG (1, "DC-240 Backend 05/16/01\n"); for (speed_index = 0; speed_index < NELEMS (speeds); speed_index++) { if (speeds[speed_index].baud == camera->baud) { init_pck[2] = speeds[speed_index].pkt_code[0]; init_pck[3] = speeds[speed_index].pkt_code[1]; break; } } if (init_pck[2] == 0) { DBG (1, "unsupported baud rate.\n"); return -1; } /* Open device file. */ if ((camera->fd = open (camera->tty_name, O_RDWR)) == -1) { DBG (1, "init_dc240: error: could not open %s for read/write\n", camera->tty_name); return -1; } /* Save old device information to restore when we are done. */ if (tcgetattr (camera->fd, &tty_orig) == -1) { DBG (1, "init_dc240: error: could not get attributes\n"); return -1; } memcpy ((char *) &tty_new, (char *) &tty_orig, sizeof (struct termios)); /* We need the device to be raw. 8 bits even parity on 9600 baud to start. */ #ifdef HAVE_CFMAKERAW cfmakeraw (&tty_new); #else /* Modified to set the port REALLY as required (9600, 8b, 1sb, NO parity). Code inspired by the gPhoto2 serial port setup */ /* input control settings */ tty_new.c_iflag &= ~(IGNBRK | IGNCR | INLCR | ICRNL | IUCLC | IXANY | IXON | IXOFF | INPCK | ISTRIP); tty_new.c_iflag |= (BRKINT | IGNPAR); /* output control settings */ tty_new.c_oflag &= ~OPOST; /* hardware control settings */ tty_new.c_cflag = (tty_new.c_cflag & ~CSIZE) | CS8; tty_new.c_cflag &= ~(PARENB | PARODD | CSTOPB); # if defined(__sgi) tty_new.c_cflag &= ~CNEW_RTSCTS; # else /* OS/2 doesn't have CRTSCTS - will this work for them? */ # ifdef CRTSCTS tty_new.c_cflag &= ~CRTSCTS; # endif # endif tty_new.c_cflag |= CLOCAL | CREAD; #endif /* line discipline settings */ tty_new.c_lflag &= ~(ICANON | ISIG | ECHO | ECHONL | ECHOE | ECHOK | IEXTEN); tty_new.c_cc[VMIN] = 0; tty_new.c_cc[VTIME] = 5; cfsetospeed (&tty_new, B9600); cfsetispeed (&tty_new, B9600); if (tcsetattr (camera->fd, TCSANOW, &tty_new) == -1) { DBG (1, "init_dc240: error: could not set attributes\n"); return -1; } /* send a break to get it back to a known state */ /* Used to supply a non-zero argument to tcsendbreak(), TCSBRK, * and TCSBRKP, but that is system dependent. e.g. on irix a non-zero * value does a drain instead of a break. A zero value is universally * used to send a break. */ #ifdef HAVE_TCSENDBREAK tcsendbreak (camera->fd, 0); # if defined(__sgi) tcdrain (camera->fd); # endif # elif defined(TCSBRKP) ioctl (camera->fd, TCSBRKP, 0); # elif defined(TCSBRK) ioctl (camera->fd, TCSBRK, 0); #endif /* and wait for it to recover from the break */ #ifdef HAVE_USLEEP usleep (breakpause); #else sleep (1); #endif /* We seem to get some garbage following the break, so * read anything pending */ n = read (camera->fd, buf, 5); DBG (127, "init_dc240 flushed %d bytes: %x %x %x %x %x\n", n, buf[0], buf[1], buf[2], buf[3], buf[4]); if (send_pck (camera->fd, init_pck) == -1) { /* * The camera always powers up at 9600, so we try * that first. However, it may be already set to * a different speed. Try the entries in the table: */ tcsetattr (camera->fd, TCSANOW, &tty_orig); DBG (1, "init_dc240: error: no response from camera\n"); return -1; } n = read (camera->fd, buf, 5); DBG (127, "init_dc240 flushed %d bytes: %x %x %x %x %x\n", n, buf[0], buf[1], buf[2], buf[3], buf[4]); /* Set speed to requested speed. */ cfsetospeed (&tty_new, Camera.baud); cfsetispeed (&tty_new, Camera.baud); if (tcsetattr (camera->fd, TCSANOW, &tty_new) == -1) { DBG (1, "init_dc240: error: could not set attributes\n"); return -1; } if (send_pck (camera->fd, open_card_pck) == -1) { DBG (1, "init_dc240: error: send_pck returned -1\n"); return -1; } if (end_of_data (camera->fd) == -1) { DBG (1, "init_dc240: error: end_of_data returned -1\n"); return -1; } return camera->fd; } static void close_dc240 (SANE_Int fd) { /* * Put the camera back to 9600 baud */ if (close (fd) == -1) { DBG (1, "close_dc240: error: could not close device\n"); } } int get_info (DC240 * camera) { SANE_Char f[] = "get_info"; SANE_Byte buf[256]; SANE_Int n; struct cam_dirlist *e; if (send_pck (camera->fd, info_pck) == -1) { DBG (1, "%s: error: send_pck returned -1\n", f); return -1; } DBG (9, "%s: read info packet\n", f); if (read_data (camera->fd, buf, 256) == -1) { DBG (1, "%s: error: read_data returned -1\n", f); return -1; } if (end_of_data (camera->fd) == -1) { DBG (1, "%s: error: end_of_data returned -1\n", f); return -1; } camera->model = buf[1]; if (camera->model != 0x5) { DBG (0, "Camera model (%d) is not DC-240 (5). " "Only the DC-240 is supported by this driver.\n", camera->model); } camera->ver_major = buf[2]; camera->ver_minor = buf[3]; camera->pic_taken = buf[14] << 8 | buf[15]; DBG (4, "pic_taken=%d\n", camera->pic_taken); camera->pic_left = buf[64] << 8 | buf[65]; DBG (4, "pictures left (at current res)=%d\n", camera->pic_left); camera->flags.low_batt = buf[8]; DBG (4, "battery=%d (0=OK, 1=weak, 2=empty)\n", camera->flags.low_batt); DBG (4, "AC adapter status=%d\n", buf[9]); dc240_opt_lowres = !buf[79]; if (Camera.pic_taken == 0) { sod[DC240_OPT_IMAGE_NUMBER].cap |= SANE_CAP_INACTIVE; image_range.min = 0; image_range.max = 0; } else { sod[DC240_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE; image_range.min = 1; image_range.max = Camera.pic_taken; } n = read_dir ("\\PCCARD\\DCIM\\*.*"); /* If we've already got a folder_list, free it up before starting * the new one */ if (folder_list != NULL) { int tmp; for (tmp = 0; folder_list[tmp]; tmp++) { free (folder_list[tmp]); } free (folder_list); } folder_list = (SANE_String * *)malloc ((n + 1) * sizeof (SANE_String *)); for (e = dir_head, n = 0; e; e = e->next, n++) { folder_list[n] = (SANE_String *) strdup (e->name); if (strchr ((char *) folder_list[n], ' ')) { *strchr ((char *) folder_list[n], ' ') = '\0'; } } folder_list[n] = NULL; sod[DC240_OPT_FOLDER].constraint.string_list = (SANE_String_Const *) folder_list; return 0; } /* NEW */ static SANE_Int read_data (SANE_Int fd, SANE_Byte * buf, SANE_Int sz) { SANE_Byte ccsum; SANE_Byte rcsum; SANE_Byte c; SANE_Int retries = 0; SANE_Int n; SANE_Int r = 0; SANE_Int i; while (retries++ < 5) { /* * If this is not the first time through, then it must be * a retry - signal the camera that we didn't like what * we got. In either case, start filling the packet */ if (retries != 1) { DBG (2, "Attempt retry %d\n", retries); c = 0xe3; if (write (fd, (char *) &c, 1) != 1) { DBG (1, "read_data: error: write ack\n"); return -1; } } /* read the control byte */ if (read (fd, &c, 1) != 1) { DBG (3, "read_data: error: " "read for packet control byte returned bad stat!us\n"); return -1; } if (c != 1 && c != 0) { DBG (1, "read_data: error: incorrect packet control byte: %02x\n", c); return -1; } for (n = 0; n < sz && (r = read (fd, (char *) &buf[n], sz - n)) > 0; n += r); if (r <= 0) { DBG (2, "read_data: warning: read returned -1\n"); continue; } if (n < sz || read (fd, &rcsum, 1) != 1) { DBG (2, "read_data: warning: buffer underrun or no checksum\n"); continue; } for (i = 0, ccsum = 0; i < n; i++) ccsum ^= buf[i]; if (ccsum != rcsum) { DBG (2, "read_data: warning: " "bad checksum (got %02x != expected %02x)\n", rcsum, ccsum); continue; } /* If we got this far, then the packet is OK */ break; } c = 0xd2; if (write (fd, (char *) &c, 1) != 1) { DBG (1, "read_data: error: write ack\n"); return -1; } return 0; } static SANE_Int end_of_data (SANE_Int fd) { SANE_Int n; SANE_Byte c; do { /* loop until the camera isn't busy */ if ((n = read (fd, &c, 1)) == -1) { DBG (1, "end_of_data: error: read returned -1\n"); return -1; } if (n == 1 && c == 0) /* got successful end of data */ return 0; /* return success */ if (n == 1) { DBG (127, "end_of_data: got %x while waiting\n", c); } else { DBG (127, "end_of_data: waiting...\n"); } sleep (1); /* not too fast */ } /* It's not documented, but we see a d1 after snapping a picture */ while (c == 0xf0 || c == 0xd1); /* Accck! Not busy, but not a good end of data either */ if (c != 0) { DBG (1, "end_of_data: error: bad EOD from camera (%02x)\n", (unsigned) c); return -1; } return 0; /* should never get here but shut gcc -Wall up */ } static SANE_Int erase (SANE_Int fd) { if (send_pck (fd, erase_pck) == -1) { DBG (1, "erase: error: send_pck returned -1\n"); return -1; } if (send_data (name_buf) == -1) { DBG (1, "erase: error: send_data returned -1\n"); return SANE_STATUS_INVAL; } if (end_of_data (fd) == -1) { DBG (1, "erase: error: end_of_data returned -1\n"); return -1; } return 0; } static SANE_Int change_res (SANE_Int fd, SANE_Byte res) { SANE_Char f[] = "change_res"; DBG (127, "%s called, low_res=%d\n", f, res); if (res != 0 && res != 1) { DBG (1, "%s: error: unsupported resolution\n", f); return -1; } /* cameras resolution semantics are opposite of ours */ res = !res; DBG (127, "%s: setting res to %d\n", f, res); res_pck[2] = res; if (send_pck (fd, res_pck) == -1) { DBG (1, "%s: error: send_pck returned -1\n", f); } if (end_of_data (fd) == -1) { DBG (1, "%s: error: end_of_data returned -1\n", f); } return 0; } SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize) { SANE_Char f[] = "sane_init"; SANE_Char dev_name[PATH_MAX], *p; size_t len; FILE *fp; SANE_Int baud; DBG_INIT (); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); fp = sanei_config_open (DC240_CONFIG_FILE); /* defaults */ Camera.baud = DEFAULT_BAUD_RATE; Camera.tty_name = DEFAULT_TTY; if (!fp) { /* default to /dev/whatever instead of insisting on config file */ DBG (1, "%s: missing config file '%s'\n", f, DC240_CONFIG_FILE); } else { while (sanei_config_read (dev_name, sizeof (dev_name), fp)) { dev_name[sizeof (dev_name) - 1] = '\0'; DBG (20, "%s: config- %s\n", f, dev_name); if (dev_name[0] == '#') continue; /* ignore line comments */ len = strlen (dev_name); if (!len) continue; /* ignore empty lines */ if (strncmp (dev_name, "port=", 5) == 0) { p = strchr (dev_name, '/'); if (p) Camera.tty_name = strdup (p); DBG (20, "Config file port=%s\n", Camera.tty_name); } else if (strncmp (dev_name, "baud=", 5) == 0) { baud = atoi (&dev_name[5]); switch (baud) { case 9600: Camera.baud = B9600; break; case 19200: Camera.baud = B19200; break; case 38400: Camera.baud = B38400; break; #ifdef B57600 case 57600: Camera.baud = B57600; break; #endif #ifdef B115200 case 115200: Camera.baud = B115200; break; #endif } DBG (20, "Config file baud=%d\n", Camera.baud); } else if (strcmp (dev_name, "dumpinquiry") == 0) { dumpinquiry = SANE_TRUE; } else if (strncmp (dev_name, "cmdrespause=", 12) == 0) { cmdrespause = atoi (&dev_name[12]); DBG (20, "Config file cmdrespause=%lu\n", cmdrespause); } else if (strncmp (dev_name, "breakpause=", 11) == 0) { breakpause = atoi (&dev_name[11]); DBG (20, "Config file breakpause=%lu\n", breakpause); } } fclose (fp); } if (init_dc240 (&Camera) == -1) return SANE_STATUS_INVAL; if (get_info (&Camera) == -1) { DBG (1, "error: could not get info\n"); close_dc240 (Camera.fd); return SANE_STATUS_INVAL; } /* load the current images array */ get_pictures_info (); if (Camera.pic_taken == 0) { Camera.current_picture_number = 0; parms.bytes_per_line = 0; parms.pixels_per_line = 0; parms.lines = 0; } else { Camera.current_picture_number = 1; set_res (Camera.Pictures[Camera.current_picture_number - 1].low_res); } if (dumpinquiry) { DBG (0, "\nCamera information:\n~~~~~~~~~~~~~~~~~\n\n"); DBG (0, "Model...........: DC%s\n", "240"); DBG (0, "Firmware version: %d.%d\n", Camera.ver_major, Camera.ver_minor); DBG (0, "Pictures........: %d/%d\n", Camera.pic_taken, Camera.pic_taken + Camera.pic_left); DBG (0, "Battery state...: %s\n", Camera.flags.low_batt == 0 ? "good" : (Camera.flags.low_batt == 1 ? "weak" : "empty")); } return SANE_STATUS_GOOD; } void sane_exit (void) { } /* Device select/open/close */ static const SANE_Device dev[] = { { "0", "Kodak", "DC-240", "still camera"}, }; static const SANE_Device *devlist[] = { dev + 0, 0 }; SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only) { DBG (127, "sane_get_devices called\n"); *device_list = devlist; return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { SANE_Int i; DBG (127, "sane_open for device %s\n", devicename); if (!devicename[0]) { i = 0; } else { for (i = 0; i < NELEMS (dev); ++i) { if (strcmp (devicename, dev[i].name) == 0) { break; } } } if (i >= NELEMS (dev)) { return SANE_STATUS_INVAL; } if (is_open) { return SANE_STATUS_DEVICE_BUSY; } is_open = 1; *handle = MAGIC; DBG (4, "sane_open: pictures taken=%d\n", Camera.pic_taken); return SANE_STATUS_GOOD; } void sane_close (SANE_Handle handle) { DBG (127, "sane_close called\n"); if (handle == MAGIC) is_open = 0; DBG (127, "sane_close returning\n"); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { if (handle != MAGIC || !is_open) return NULL; /* wrong device */ if (option < 0 || option >= NELEMS (sod)) return NULL; return &sod[option]; } static SANE_Int myinfo = 0; SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int * info) { SANE_Status status; if (option < 0 || option >= NELEMS (sod)) return SANE_STATUS_INVAL; /* Unknown option ... */ /* Need to put this DBG line after the range check on option */ DBG (127, "control_option(handle=%p,opt=%s,act=%s,val=%p,info=%p)\n", handle, sod[option].title, (action == SANE_ACTION_SET_VALUE ? "SET" : (action == SANE_ACTION_GET_VALUE ? "GET" : "SETAUTO")), value, (void *) info); if (handle != MAGIC || !is_open) return SANE_STATUS_INVAL; /* Unknown handle ... */ switch (action) { case SANE_ACTION_SET_VALUE: /* Can't set disabled options */ if (!SANE_OPTION_IS_ACTIVE (sod[option].cap)) { return (SANE_STATUS_INVAL); } /* initialize info to zero - we'll OR in various values later */ if (info) *info = 0; status = sanei_constrain_value (sod + option, value, &myinfo); if (status != SANE_STATUS_GOOD) { DBG (2, "Constraint error in control_option\n"); return status; } switch (option) { case DC240_OPT_IMAGE_NUMBER: if (*(SANE_Word *) value <= Camera.pic_taken) Camera.current_picture_number = *(SANE_Word *) value; else Camera.current_picture_number = Camera.pic_taken; myinfo |= SANE_INFO_RELOAD_PARAMS; /* get the image's resolution, unless the camera has no * pictures yet */ if (Camera.pic_taken != 0) { set_res (Camera. Pictures[Camera.current_picture_number - 1].low_res); } break; case DC240_OPT_THUMBS: dc240_opt_thumbnails = !!*(SANE_Word *) value; /* Thumbnail forces an image size change: */ myinfo |= SANE_INFO_RELOAD_PARAMS; if (Camera.pic_taken != 0) { set_res (Camera. Pictures[Camera.current_picture_number - 1].low_res); } break; case DC240_OPT_SNAP: switch (*(SANE_Bool *) value) { case SANE_TRUE: dc240_opt_snap = SANE_TRUE; break; case SANE_FALSE: dc240_opt_snap = SANE_FALSE; break; default: return SANE_STATUS_INVAL; } /* Snap forces new image size and changes image range */ myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; /* if we are snapping a new one */ if (dc240_opt_snap) { /* activate the resolution setting */ sod[DC240_OPT_LOWRES].cap &= ~SANE_CAP_INACTIVE; /* and de-activate the image number selector */ sod[DC240_OPT_IMAGE_NUMBER].cap |= SANE_CAP_INACTIVE; } else { /* deactivate the resolution setting */ sod[DC240_OPT_LOWRES].cap |= SANE_CAP_INACTIVE; /* and activate the image number selector */ sod[DC240_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE; } /* set params according to resolution settings */ set_res (dc240_opt_lowres); break; case DC240_OPT_LOWRES: dc240_opt_lowres = !!*(SANE_Word *) value; myinfo |= SANE_INFO_RELOAD_PARAMS; /* XXX - change the number of pictures left depending on resolution perhaps just call get_info again? */ set_res (dc240_opt_lowres); break; case DC240_OPT_ERASE: dc240_opt_erase = !!*(SANE_Word *) value; break; case DC240_OPT_AUTOINC: dc240_opt_autoinc = !!*(SANE_Word *) value; break; case DC240_OPT_FOLDER: DBG (1, "FIXME set folder not implemented yet\n"); break; case DC240_OPT_DEFAULT: dc240_opt_thumbnails = 0; dc240_opt_snap = 0; /* deactivate the resolution setting */ sod[DC240_OPT_LOWRES].cap |= SANE_CAP_INACTIVE; /* and activate the image number selector */ sod[DC240_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE; myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; DBG (1, "Fixme: Set all defaults here!\n"); break; case DC240_OPT_INIT_DC240: if ((Camera.fd = init_dc240 (&Camera)) == -1) { return SANE_STATUS_INVAL; } if (get_info (&Camera) == -1) { DBG (1, "error: could not get info\n"); close_dc240 (Camera.fd); return SANE_STATUS_INVAL; } /* load the current images array */ get_pictures_info (); myinfo |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; break; default: return SANE_STATUS_INVAL; } break; case SANE_ACTION_GET_VALUE: /* Can't return status for disabled options */ if (!SANE_OPTION_IS_ACTIVE (sod[option].cap)) { return (SANE_STATUS_INVAL); } switch (option) { case 0: *(SANE_Word *) value = NELEMS (sod); break; case DC240_OPT_IMAGE_NUMBER: *(SANE_Word *) value = Camera.current_picture_number; break; case DC240_OPT_THUMBS: *(SANE_Word *) value = dc240_opt_thumbnails; break; case DC240_OPT_SNAP: *(SANE_Word *) value = dc240_opt_snap; break; case DC240_OPT_LOWRES: *(SANE_Word *) value = dc240_opt_lowres; break; case DC240_OPT_ERASE: *(SANE_Word *) value = dc240_opt_erase; break; case DC240_OPT_AUTOINC: *(SANE_Word *) value = dc240_opt_autoinc; break; case DC240_OPT_FOLDER: strcpy ((char *) value, (char *) folder_list[current_folder]); break; default: return SANE_STATUS_INVAL; } break; case SANE_ACTION_SET_AUTO: switch (option) { default: return SANE_STATUS_UNSUPPORTED; /* We are DUMB */ } } if (info && action == SANE_ACTION_SET_VALUE) { *info = myinfo; myinfo = 0; } return SANE_STATUS_GOOD; } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { SANE_Int rc = SANE_STATUS_GOOD; DBG (127, "sane_get_params called, wid=%d,height=%d\n", parms.pixels_per_line, parms.lines); if (handle != MAGIC || !is_open) rc = SANE_STATUS_INVAL; /* Unknown handle ... */ parms.last_frame = SANE_TRUE; /* Have no idea what this does */ *params = parms; DBG (127, "sane_get_params return %d\n", rc); return rc; } typedef struct { struct jpeg_source_mgr pub; JOCTET *buffer; } my_source_mgr; typedef my_source_mgr *my_src_ptr; METHODDEF (void) jpeg_init_source (j_decompress_ptr __sane_unused__ cinfo) { /* nothing to do */ } METHODDEF (boolean) jpeg_fill_input_buffer (j_decompress_ptr cinfo) { my_src_ptr src = (my_src_ptr) cinfo->src; if (read_data (Camera.fd, src->buffer, 512) == -1) { DBG (5, "sane_start: read_data failed\n"); src->buffer[0] = (JOCTET) 0xFF; src->buffer[1] = (JOCTET) JPEG_EOI; return FALSE; } src->pub.next_input_byte = src->buffer; src->pub.bytes_in_buffer = 512; return TRUE; } METHODDEF (void) jpeg_skip_input_data (j_decompress_ptr cinfo, long num_bytes) { my_src_ptr src = (my_src_ptr) cinfo->src; if (num_bytes > 0) { while (num_bytes > (long) src->pub.bytes_in_buffer) { num_bytes -= (long) src->pub.bytes_in_buffer; (void) jpeg_fill_input_buffer (cinfo); } } src->pub.next_input_byte += (size_t) num_bytes; src->pub.bytes_in_buffer -= (size_t) num_bytes; } static SANE_Byte linebuffer[HIGHRES_WIDTH * 3]; static SANE_Int linebuffer_size = 0; static SANE_Int linebuffer_index = 0; METHODDEF (void) jpeg_term_source (j_decompress_ptr __sane_unused__ cinfo) { /* no work necessary here */ } SANE_Status sane_start (SANE_Handle handle) { SANE_Int i; DBG (127, "sane_start called\n"); if (handle != MAGIC || !is_open || (Camera.current_picture_number == 0 && dc240_opt_snap == SANE_FALSE)) return SANE_STATUS_INVAL; /* Unknown handle ... */ if (Camera.scanning) return SANE_STATUS_EOF; /* * This shouldn't normally happen, but we allow it as a special case * when batch/autoinc are in effect. The first illegal picture number * terminates the scan */ if (Camera.current_picture_number > Camera.pic_taken) { return SANE_STATUS_INVAL; } if (dc240_opt_snap) { /* * Don't allow picture unless there is room in the * camera. */ if (Camera.pic_left == 0) { DBG (3, "No room to store new picture\n"); return SANE_STATUS_INVAL; } if (snap_pic (Camera.fd) == SANE_STATUS_INVAL) { DBG (1, "Failed to snap new picture\n"); return SANE_STATUS_INVAL; } } if (dc240_opt_thumbnails) { if (send_pck (Camera.fd, thumb_pck) == -1) { DBG (1, "sane_start: error: send_pck returned -1\n"); return SANE_STATUS_INVAL; } /* Picture size should have been set when thumbnails were * selected. But, check just in case */ if (parms.pixels_per_line != 160 || parms.bytes_per_line != 160 * 3 || parms.lines != 120) { DBG (1, "sane_start: fixme! thumbnail image size is wrong\n"); return SANE_STATUS_INVAL; } } else { if (send_pck (Camera.fd, pic_pck) == -1) { DBG (1, "sane_start: error: send_pck returned -1\n"); return SANE_STATUS_INVAL; } } { my_src_ptr src; struct jpeg_error_mgr jerr; SANE_Int n; SANE_Char f[] = "sane_start"; SANE_Char path[256]; struct cam_dirlist *e; name_buf[0] = 0x80; for (n = 1, e = dir_head; e; n++, e = e->next) { if (n == Camera.current_picture_number) break; } strcpy (path, "\\PCCARD\\DCIM\\"); strcat (path, (char *) folder_list[current_folder]); strcat (path, "\\"); strcat (path, e->name); path[strlen (path) - 3] = '\0'; strcat (path, ".JPG"); DBG (9, "%s: pic to read is %d name is %s\n", f, n, path); strcpy ((char *) &name_buf[1], path); for (i = 49; i <= 56; i++) { name_buf[i] = 0xff; } if (send_data (name_buf) == -1) { DBG (1, "%s: error: send_data returned -1\n", f); return SANE_STATUS_INVAL; } cinfo.err = jpeg_std_error (&jerr); jpeg_create_decompress (&cinfo); cinfo.src = (struct jpeg_source_mgr *) (*cinfo.mem-> alloc_small) ((j_common_ptr) & cinfo, JPOOL_PERMANENT, sizeof (my_source_mgr)); src = (my_src_ptr) cinfo.src; src->buffer = (JOCTET *) (*cinfo.mem->alloc_small) ((j_common_ptr) & cinfo, JPOOL_PERMANENT, 1024 * sizeof (JOCTET)); src->pub.init_source = jpeg_init_source; src->pub.fill_input_buffer = jpeg_fill_input_buffer; src->pub.skip_input_data = jpeg_skip_input_data; src->pub.resync_to_restart = jpeg_resync_to_restart; /* default */ src->pub.term_source = jpeg_term_source; src->pub.bytes_in_buffer = 0; src->pub.next_input_byte = NULL; (void) jpeg_read_header (&cinfo, TRUE); dest_mgr = sanei_jpeg_jinit_write_ppm (&cinfo); (void) jpeg_start_decompress (&cinfo); linebuffer_size = 0; linebuffer_index = 0; } Camera.scanning = SANE_TRUE; /* don't overlap scan requests */ return SANE_STATUS_GOOD; } SANE_Status sane_read (SANE_Handle __sane_unused__ handle, SANE_Byte * data, SANE_Int max_length, SANE_Int * length) { SANE_Int lines = 0; SANE_Char filename_buf[256]; if (Camera.scanning == SANE_FALSE) { return SANE_STATUS_INVAL; } /* If there is anything in the buffer, satisfy the read from there */ if (linebuffer_size && linebuffer_index < linebuffer_size) { *length = linebuffer_size - linebuffer_index; if (*length > max_length) { *length = max_length; } memcpy (data, linebuffer + linebuffer_index, *length); linebuffer_index += *length; return SANE_STATUS_GOOD; } if (cinfo.output_scanline >= cinfo.output_height) { *length = 0; /* clean up comms with the camera */ if (end_of_data (Camera.fd) == -1) { DBG (1, "sane_read: error: end_of_data returned -1\n"); return SANE_STATUS_INVAL; } if (dc240_opt_erase) { DBG (127, "sane_read bp%d, erase image\n", __LINE__); if (erase (Camera.fd) == -1) { DBG (1, "Failed to erase memory\n"); return SANE_STATUS_INVAL; } Camera.pic_taken--; Camera.pic_left++; Camera.current_picture_number = Camera.pic_taken; image_range.max--; myinfo |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; strcpy ((char *) filename_buf, strrchr ((char *) name_buf + 1, '\\') + 1); strcpy (strrchr ((char *) filename_buf, '.'), "JPG"); dir_delete ((SANE_String) filename_buf); } if (dc240_opt_autoinc) { if (Camera.current_picture_number <= Camera.pic_taken) { Camera.current_picture_number++; myinfo |= SANE_INFO_RELOAD_PARAMS; /* get the image's resolution */ set_res (Camera.Pictures[Camera.current_picture_number - 1]. low_res); } DBG (4, "Increment count to %d (total %d)\n", Camera.current_picture_number, Camera.pic_taken); } return SANE_STATUS_EOF; } /* XXX - we should read more than 1 line at a time here */ lines = 1; (void) jpeg_read_scanlines (&cinfo, dest_mgr->buffer, lines); (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, lines, (char *) linebuffer); *length = cinfo.output_width * cinfo.output_components * lines; linebuffer_size = *length; linebuffer_index = 0; if (*length > max_length) { *length = max_length; } memcpy (data, linebuffer + linebuffer_index, *length); linebuffer_index += *length; return SANE_STATUS_GOOD; } void sane_cancel (SANE_Handle __sane_unused__ handle) { unsigned char cancel_byte[] = { 0xe4 }; if (Camera.scanning) { /* Flush any pending data from the camera before continuing */ { SANE_Int n; SANE_Char flush[1024]; do { sleep (1); n = read (Camera.fd, flush, 1024); if (n > 0) { DBG (127, "%s: flushed %d bytes\n", "sane_cancel", n); } else { DBG (127, "%s: nothing to flush\n", "sane_cancel"); } } while (n > 0); } if (cinfo.output_scanline < cinfo.output_height) { write (Camera.fd, cancel_byte, 1); } Camera.scanning = SANE_FALSE; /* done with scan */ } else DBG (4, "sane_cancel: not scanning - nothing to do\n"); } SANE_Status sane_set_io_mode (SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking) { /* sane_set_io_mode() is only valid during a scan */ if (Camera.scanning) { if (non_blocking == SANE_FALSE) { return SANE_STATUS_GOOD; } else { return SANE_STATUS_UNSUPPORTED; } } else { /* We aren't currently scanning */ return SANE_STATUS_INVAL; } } SANE_Status sane_get_select_fd (SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ * fd) { return SANE_STATUS_UNSUPPORTED; } /* * get_pictures_info - load information about all pictures currently in * camera: Mainly the mapping of picture number * to picture name, and the resolution of each picture. */ static PictureInfo * get_pictures_info (void) { SANE_Char f[] = "get_pictures_info"; SANE_Char path[256]; SANE_Int num_pictures; SANE_Int p; PictureInfo *pics; if (Camera.Pictures) { free (Camera.Pictures); Camera.Pictures = NULL; } strcpy (path, "\\PCCARD\\DCIM\\"); strcat (path, (char *) folder_list[current_folder]); strcat (path, "\\*.*"); num_pictures = read_dir (path); if (num_pictures != Camera.pic_taken) { DBG (2, "%s: warning: Number of pictures in directory (%d) doesn't match camera status table (%d). Using directory count\n", f, num_pictures, Camera.pic_taken); Camera.pic_taken = num_pictures; image_range.max = num_pictures; } if ((pics = (PictureInfo *) malloc (Camera.pic_taken * sizeof (PictureInfo))) == NULL) { DBG (1, "%s: error: allocate memory for pictures array\n", f); return NULL; } for (p = 0; p < Camera.pic_taken; p++) { if (get_picture_info (pics + p, p) == -1) { free (pics); return NULL; } } Camera.Pictures = pics; return pics; } static SANE_Int get_picture_info (PictureInfo * pic, SANE_Int p) { SANE_Char f[] = "get_picture_info"; SANE_Int n; struct cam_dirlist *e; DBG (4, "%s: info for pic #%d\n", f, p); for (n = 0, e = dir_head; e && n < p; n++, e = e->next) ; DBG (4, "Name is %s\n", e->name); read_info (e->name); /* Validate picture info * byte 0 - 1 == picture info * byte 1 - 5 == DC240 Camera * byte 2 - 3 == JFIF file * byte 6 - 0 == Image is complete */ if (info_buf[0] != 1 || info_buf[1] != 5 || info_buf[2] != 3 || info_buf[6] != 0) { DBG (1, "%s: error: Image %s does not come from a DC-240.\n", f, e->name); return -1; } pic->low_res = info_buf[3] == 0 ? SANE_TRUE : SANE_FALSE; /* * byte 12 - Year MSB * byte 13 - Year LSB * byte 14 - Month * byte 15 - Day * byte 16 - Hour * byte 17 - Minute * byte 18 - Second */ DBG (1, "Picture %d taken %02d/%02d/%02d %02d:%02d:%02d\n", p, info_buf[14], info_buf[15], (info_buf[12] << 8) + info_buf[13], info_buf[16], info_buf[17], info_buf[18]); return 0; } /* * snap_pic - take a picture (and call get_pictures_info to re-create * the directory related data structures) */ static SANE_Status snap_pic (SANE_Int fd) { SANE_Char f[] = "snap_pic"; #if 0 /* Just checking... It looks like after snapping the picture, we * get two "d1" ACK responses back, even though the documentation seems * to say that there should only be one. I thought the first one could * have been an unread response from an earlier command, so I * I tried flushing any data here. Still seeing the multiple * d1 bytes, however... */ { SANE_Int n; SANE_Char flush[10]; n = read (Camera.fd, flush, 10); if (n > 0) { DBG (127, "%s: flushed %d bytes\n", f, n); } else { DBG (127, "%s: nothing to flush\n", f); } } #endif /* make sure camera is set to our settings state */ if (change_res (Camera.fd, dc240_opt_lowres) == -1) { DBG (1, "%s: Failed to set resolution\n", f); return SANE_STATUS_INVAL; } /* take the picture */ if (send_pck (fd, shoot_pck) == -1) { DBG (1, "%s: error: send_pck returned -1\n", f); return SANE_STATUS_INVAL; } else { if (end_of_data (Camera.fd) == -1) { DBG (1, "%s: error: end_of_data returned -1\n", f); return SANE_STATUS_INVAL; } } Camera.pic_taken++; Camera.pic_left--; Camera.current_picture_number = Camera.pic_taken; image_range.max++; sod[DC240_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE; /* add this one to the Pictures array */ if (get_pictures_info () == NULL) { DBG (1, "%s: Failed to get new picture info\n", f); /* XXX - I guess we should try to erase the image here */ return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } /* * read_dir - read a list of file names from the specified directory * and create a linked list of file name entries in * alphabetical order. The first entry in the list will * be "picture #1", etc. */ static SANE_Int read_dir (SANE_String dir) { SANE_Int retval = 0; SANE_Byte buf[256]; SANE_Byte *next_buf; SANE_Int i, entries; SANE_Byte r = 0xf0; /* prime the loop with a "camera busy" */ SANE_Char f[] = "read_dir"; struct cam_dirlist *e, *next; /* Free up current list */ for (e = dir_head; e; e = next) { DBG (127, "%s: free entry %s\n", f, e->name); next = e->next; free (e); } dir_head = NULL; if (send_pck (Camera.fd, read_dir_pck) == -1) { DBG (1, "%s: error: send_pck returned -1\n", f); return -1; } buf[0] = 0x80; strcpy ((char *) &buf[1], dir); for (i = 49; i <= 56; i++) { buf[i] = 0xff; } if (send_data (buf) == -1) { DBG (1, "%s: error: send_data returned -1\n", f); return -1; } if (read_data (Camera.fd, (SANE_Byte *) & dir_buf2, 256) == -1) { DBG (1, "%s: error: read_data returned -1\n", f); return -1; } entries = (dir_buf2[0] << 8) + dir_buf2[1]; DBG (127, "%s: result of dir read is %x, number of entries=%d\n", f, r, entries); if (entries > 1001) { DBG (1, "%s: error: more than 999 pictures not supported yet\n", f); return -1; } /* Determine if it's time to read another 256 byte buffer from the camera */ next_buf = ((SANE_Byte *) & dir_buf2) + 256; while (dir_buf2 + 2 + CAMDIRENTRYSIZE * entries >= (SANE_Byte *) next_buf) { DBG (127, "%s: reading additional directory buffer\n", f); if (read_data (Camera.fd, next_buf, 256) == -1) { DBG (1, "%s: error: read_data returned -1\n", f); return -1; } next_buf += 256; } for (i = 0; i < entries; i++) { /* Hack: I don't know what attr is used for, so setting it * to zero is a convenient way to put in the null terminator */ get_attr (i) = 0; DBG (127, "%s: entry=%s\n", f, get_name (i)); if ((get_name (i))[0] == '.') { continue; } if (dir_insert ((struct cam_dirent *) &dir_buf2[2 + CAMDIRENTRYSIZE * i]) != 0) { DBG (1, "%s: error: failed to insert dir entry\n", f); return -1; } retval++; } if (end_of_data (Camera.fd) == -1) { DBG (1, "%s: error: end_of_data returned -1\n", f); return -1; } return retval; } /* * read_info - read the info block from camera for the specified file */ static SANE_Int read_info (SANE_String fname) { SANE_Byte buf[256]; SANE_Int i; SANE_Char f[] = "read_info"; SANE_Char path[256]; strcpy (path, "\\PCCARD\\DCIM\\"); strcat (path, (char *) folder_list[current_folder]); strcat (path, "\\"); strcat (path, fname); path[strlen (path) - 3] = '\0'; strcat (path, ".JPG"); if (send_pck (Camera.fd, pic_info_pck) == -1) { DBG (1, "%s: error: send_pck returned -1\n", f); return SANE_STATUS_INVAL; } buf[0] = 0x80; strcpy ((char *) &buf[1], path); for (i = 49; i <= 56; i++) { buf[i] = 0xff; } if (send_data (buf) == -1) { DBG (1, "%s: error: send_data returned -1\n", f); return SANE_STATUS_INVAL; } if (read_data (Camera.fd, info_buf, 256) != 0) { DBG (1, "%s: error: Failed in read_data\n", f); return -1; } DBG (9, "%s: data type=%d, cam type=%d, file type=%d\n", f, info_buf[0], info_buf[1], info_buf[2]); if (end_of_data (Camera.fd) == -1) { DBG (1, "%s: error: end_of_data returned -1\n", f); return -1; } return 0; } /* * send_data - Send a data block - assumes all data blocks to camera * are 60 bytes long */ static SANE_Int send_data (SANE_Byte * buf) { SANE_Byte r = 0xf0; /* prime the loop with a "camera busy" */ SANE_Int i, n; SANE_Byte csum; SANE_Char f[] = "send_data"; for (i = 1, csum = 0; i < 59; i++) { csum ^= buf[i]; } buf[59] = csum; DBG (127, "%s: about to send data block\n", f); /* keep trying if camera says it's busy */ while (r == 0xf0) { if (write (Camera.fd, (char *) buf, 60) != 60) { DBG (1, "%s: error: write returned -1\n", f); return -1; } /* need to wait before we read command result */ #ifdef HAVE_USLEEP usleep (cmdrespause); #else sleep (1); #endif if ((n = read (Camera.fd, (char *) &r, 1)) != 1) { DBG (1, "%s: error: read returned -1\n", f); return -1; } } if (r != 0xd2) { DBG (1, "%s: error: bad response to send_data (%d)\n", f, r); return -1; } return 0; } /* * dir_insert - Add (in alphabetical order) a directory entry to the * current list of entries. */ static SANE_Int dir_insert (struct cam_dirent *entry) { struct cam_dirlist *cur, *e; cur = (struct cam_dirlist *) malloc (sizeof (struct cam_dirlist)); if (cur == NULL) { DBG (1, "dir_insert: error: could not malloc entry\n"); return -1; } strcpy (cur->name, entry->name); DBG (127, "dir_insert: name is %s\n", cur->name); cur->next = NULL; if (dir_head == NULL) { dir_head = cur; } else if (strcmp (cur->name, dir_head->name) < 0) { cur->next = dir_head; dir_head = cur; return 0; } else { for (e = dir_head; e->next; e = e->next) { if (strcmp (e->next->name, cur->name) > 0) { cur->next = e->next; e->next = cur; return 0; } } e->next = cur; } return 0; } /* * dir_delete - Delete a directory entry from the linked list of file * names */ static SANE_Int dir_delete (SANE_String fname) { struct cam_dirlist *cur, *e; DBG (127, "dir_delete: %s\n", fname); if (strcmp (fname, dir_head->name) == 0) { cur = dir_head; dir_head = dir_head->next; free (cur); return 0; } for (e = dir_head; e->next; e = e->next) { if (strcmp (fname, e->next->name) == 0) { cur = e->next; e->next = e->next->next; free (cur); return (0); } } DBG (1, "dir_delete: Couldn't find entry %s in dir list\n", fname); return -1; } /* * set_res - set picture size depending on resolution settings */ static void set_res (SANE_Int lowres) { if (dc240_opt_thumbnails) { parms.bytes_per_line = 160 * 3; parms.pixels_per_line = 160; parms.lines = 120; } else if (lowres) { parms.bytes_per_line = LOWRES_WIDTH * 3; parms.pixels_per_line = LOWRES_WIDTH; parms.lines = LOWRES_HEIGHT; } else { parms.bytes_per_line = HIGHRES_WIDTH * 3; parms.pixels_per_line = HIGHRES_WIDTH; parms.lines = HIGHRES_HEIGHT; } } backends-1.3.0/backend/dc240.conf.in000066400000000000000000000017271456256263500167710ustar00rootroot00000000000000# 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; backends-1.3.0/backend/dc240.h000066400000000000000000000161011456256263500156560ustar00rootroot00000000000000/*************************************************************************** * SANE - Scanner Access Now Easy. dc240.h 03/12/01 - Peter Fales Based on the dc210 driver, (C) 1998 Brian J. Murrell (which is based on dc25 driver (C) 1998 by Peter Fales) This file (C) 2001 by Peter Fales 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, see . 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 the Kodak DC-240 digital camera. THIS IS EXTREMELY ALPHA CODE! USE AT YOUR OWN RISK!! (feedback to: dc240-devel@fales-lorenz.net) This backend is based somewhat on the dc25 backend included in this package by Peter Fales, and the dc210 backend by Brian J. Murrell ***************************************************************************/ #include #include #include #include #include #include #ifndef TRUE #define TRUE (1==1) #endif #ifndef FALSE #define FALSE (!TRUE) #endif #ifndef NULL #define NULL 0L #endif typedef struct picture_info { int low_res; int size; } PictureInfo; typedef struct DC240_s { SANE_Int fd; /* file descriptor to talk to it */ char *tty_name; /* the tty port name it's on */ speed_t baud; /* current tty speed */ SANE_Bool scanning; /* currently scanning an image? */ SANE_Byte model; SANE_Byte ver_major; SANE_Byte ver_minor; SANE_Int pic_taken; SANE_Int pic_left; struct { unsigned int low_res:1; unsigned int low_batt:1; } flags; PictureInfo *Pictures; /* array of pictures */ SANE_Int current_picture_number; /* picture being operated on */ } DC240; typedef struct dc240_info_s { SANE_Byte model; SANE_Byte ver_major; SANE_Byte ver_minor; SANE_Int pic_taken; SANE_Int pic_left; struct { SANE_Int low_res:1; SANE_Int low_batt:1; } flags; } Dc240Info, *Dc240InfoPtr; static SANE_Int get_info (DC240 *); #define INIT_PCK {0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A} /* ^^^^^^^^^^ * Baud rate: (see pkt_speed structure) * 0x96 0x00 -> 9600 baud * 0x19 0x20 -> 19200 baud * 0x38 0x40 -> 38400 baud * 0x57 0x60 -> 57600 baud * 0x11 0x52 -> 115200 baud */ #define INFO_PCK {0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A} #define SHOOT_PCK {0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A} #define ERASE_PCK {0x9D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A} #define RES_PCK {0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A} /* ^^^^ * Resolution: 0x00 = low, 0x01 = high */ #define THUMBS_PCK {0x93, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x1A} /* * */ #define PICS_PCK {0x9A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A} /* * */ #define PICS_INFO_PCK {0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A} /* * */ #define OPEN_CARD_PCK {0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A} #define READ_DIR_PCK {0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A} /* ^^^^ * 1=Report number of entries only */ struct pkt_speed { speed_t baud; SANE_Byte pkt_code[2]; }; #if defined (B57600) && defined (B115200) # define SPEEDS { { B9600, { 0x96, 0x00 } }, \ { B19200, { 0x19, 0x20 } }, \ { B38400, { 0x38, 0x40 } }, \ { B57600, { 0x57, 0x60 } }, \ { B115200, { 0x11, 0x52 } } } #else # define SPEEDS { { B9600, { 0x96, 0x00 } }, \ { B19200, { 0x19, 0x20 } }, \ { B38400, { 0x38, 0x40 } } } #endif #define HIGH_RES 0 #define LOW_RES 1 #define HIGHRES_WIDTH 1280 #define HIGHRES_HEIGHT 960 #define LOWRES_WIDTH 640 #define LOWRES_HEIGHT 480 /* * External definitions */ extern char *__progname; /* Defined in /usr/lib/crt0.o */ struct cam_dirent { SANE_Char name[11]; SANE_Byte attr; SANE_Byte create_time[2]; SANE_Byte creat_date[2]; long size; }; #ifdef OLD /* This is the layout of the directory in the camera - Unfortunately, * this only works in gcc. */ struct dir_buf { SANE_Byte entries_msb PACKED; SANE_Byte entries_lsb PACKED; struct cam_dirent entry[1000] PACKED; }; #else /* So, we have to do it the hard way... */ #define CAMDIRENTRYSIZE 20 #define DIRENTRIES 1000 #define get_name(entry) (SANE_Char*) &dir_buf2[2+CAMDIRENTRYSIZE*(entry)] #define get_attr(entry) dir_buf2[2+11+CAMDIRENTRYSIZE*(entry)] #define get_create_time(entry) \ ( dir_buf2[2+12+CAMDIRENTRYSIZE*(entry)] << 8 \ + dir_buf2[2+13+CAMDIRENTRYSIZE*(entry)]) #endif struct cam_dirlist { SANE_Char name[48]; struct cam_dirlist *next; }; #include FILE *sanei_config_open (const char *filename); static SANE_Int init_dc240 (DC240 *); static void close_dc240 (SANE_Int); static SANE_Int read_data (SANE_Int fd, SANE_Byte * buf, SANE_Int sz); static SANE_Int end_of_data (SANE_Int fd); static PictureInfo *get_pictures_info (void); static SANE_Int get_picture_info (PictureInfo * pic, SANE_Int p); static SANE_Status snap_pic (SANE_Int fd); char *sanei_config_read (char *str, int n, FILE * stream); static SANE_Int read_dir (SANE_String dir); static SANE_Int read_info (SANE_String fname); static SANE_Int dir_insert (struct cam_dirent *entry); static SANE_Int dir_delete (SANE_String name); static SANE_Int send_data (SANE_Byte * buf); static void set_res (SANE_Int lowres); backends-1.3.0/backend/dc25.c000066400000000000000000001734411456256263500156050ustar00rootroot00000000000000/*************************************************************************** * SANE - Scanner Access Now Easy. dc25.c This file (C) 1998 Peter Fales 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, see . 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 the Kodak DC-25 (and probably the DC-20) digital cameras. THIS IS EXTREMELY ALPHA CODE! USE AT YOUR OWN RISK!! (feedback to: dc25-devel@fales-lorenz.net) This backend is based heavily on the dc20ctrl package by Ugo Paternostro . I've attached his header below: *************************************************************************** * Copyright (C) 1998 Ugo Paternostro * * This file is part of the dc20ctrl package. The complete package can be * downloaded from: * http://aguirre.dsi.unifi.it/~paterno/binaries/dc20ctrl.tar.gz * * This package is derived from the dc20 package, built by Karl Hakimian * that you can find it at ftp.eecs.wsu.edu in the * /pub/hakimian directory. The complete URL is: * ftp://ftp.eecs.wsu.edu/pub/hakimian/dc20.tar.gz * * This package also includes a slightly modified version of the Comet to ppm * conversion routine written by YOSHIDA Hideki * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #include "../include/sane/config.h" #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #define BACKEND_NAME dc25 #include "../include/sane/sanei_backend.h" #include "dc25.h" #ifndef PATH_MAX # define PATH_MAX 1024 #endif #define MAGIC (void *)0xab730324 #define DC25_CONFIG_FILE "dc25.conf" #define THUMBSIZE ( (CameraInfo.model == 0x25 ) ? 14400 : 5120 ) static SANE_Bool is_open = 0; static SANE_Byte dc25_opt_image_number = 1; /* Image to load */ static SANE_Bool dc25_opt_thumbnails; /* Load thumbnails */ static SANE_Bool dc25_opt_snap; /* Take new picture */ static SANE_Bool dc25_opt_lowres; /* Use low resoluiton */ #define DC25_OPT_CONTRAST_DEFAULT 1.6 /* Contrast enhancement */ static SANE_Fixed dc25_opt_contrast = SANE_FIX (DC25_OPT_CONTRAST_DEFAULT); #define DC25_OPT_GAMMA_DEFAULT 4.5 /* Gamma correction (10x) */ static SANE_Fixed dc25_opt_gamma = SANE_FIX (DC25_OPT_GAMMA_DEFAULT); static SANE_Bool dc25_opt_erase; /* Erase all after download */ static SANE_Bool dc25_opt_erase_one; /* Erase one after download */ static SANE_Bool dumpinquiry; static SANE_Int info_flags; static int tfd; /* Camera File Descriptor */ static char tty_name[PATH_MAX]; #define DEF_TTY_NAME "/dev/ttyS0" static speed_t tty_baud = DEFAULT_TTY_BAUD; #define TMPFILE_PATTERN "/tmp/dc25XXXXXX"; static Dc20Info *dc20_info; static Dc20Info CameraInfo; static SANE_Byte contrast_table[256]; static struct pixmap *pp; static const SANE_Range contrast_range = { 0 << SANE_FIXED_SCALE_SHIFT, /* minimum */ 3 << SANE_FIXED_SCALE_SHIFT, /* maximum */ 16384 /* quantization ~ 0.025 */ }; static const SANE_Range gamma_range = { 0 << SANE_FIXED_SCALE_SHIFT, /* minimum */ 10 << SANE_FIXED_SCALE_SHIFT, /* maximum */ 16384 /* quantization ~ 0.025 */ }; static SANE_Range image_range = { 0, 14, 0 }; static SANE_Option_Descriptor sod[] = { { SANE_NAME_NUM_OPTIONS, SANE_TITLE_NUM_OPTIONS, SANE_DESC_NUM_OPTIONS, SANE_TYPE_INT, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} } , #define D25_OPT_IMAGE_SELECTION 1 { "", "Image Selection", "Selection of the image to load.", SANE_TYPE_GROUP, SANE_UNIT_NONE, 0, 0, SANE_CONSTRAINT_NONE, {NULL} } , #define DC25_OPT_IMAGE_NUMBER 2 { "image", "Image Number", "Select Image Number to load from camera", SANE_TYPE_INT, SANE_UNIT_NONE, 4, SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_RANGE, {(SANE_String_Const *) & image_range} /* this is ANSI conformant! */ } , #define DC25_OPT_THUMBS 3 { "thumbs", "Load Thumbnail", "Load the image as thumbnail.", SANE_TYPE_BOOL, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} } , #define DC25_OPT_SNAP 4 { "snap", "Snap new picture", "Take new picture and download it", SANE_TYPE_BOOL, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED, SANE_CONSTRAINT_NONE, {NULL} } , #define DC25_OPT_LOWRES 5 { "lowres", "Low Resolution", "New pictures taken in low resolution", SANE_TYPE_BOOL, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE | SANE_CAP_ADVANCED, SANE_CONSTRAINT_NONE, {NULL} } , #define DC25_OPT_ERASE 6 { "erase", "Erase", "Erase all pictures after downloading", SANE_TYPE_BOOL, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} } , #define DC25_OPT_ERASE_ONE 7 { "erase-one", "Erase One", "Erase downloaded picture after downloading", SANE_TYPE_BOOL, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} } , #define DC25_OPT_ENHANCE 8 { "", "Image Parameters", "Modifications to image parameters", SANE_TYPE_GROUP, SANE_UNIT_NONE, 0, 0, SANE_CONSTRAINT_NONE, {NULL} } , #define DC25_OPT_CONTRAST 9 { "contrast", "Contrast Adjustment", "Values > 1 enhance contrast", SANE_TYPE_FIXED, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_RANGE, {(const SANE_String_Const *) &contrast_range} /* this is ANSI conformant! */ }, #define DC25_OPT_GAMMA 10 { "gamma", "Gamma Adjustment", "Larger values make image darker", SANE_TYPE_FIXED, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_RANGE, {(const SANE_String_Const *) &gamma_range} /* this is ANSI conformant! */ }, #define DC25_OPT_DEFAULT 11 { "default-enhancements", "Defaults", "Set default values for enhancement controls (i.e. contrast).", SANE_TYPE_BUTTON, SANE_UNIT_NONE, 0, SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} } }; static SANE_Parameters parms = { SANE_FRAME_RGB, 1, 500, /* Number of bytes returned per scan line: */ 500, /* Number of pixels per scan line. */ 373, /* Number of lines for the current scan. */ 8, /* Number of bits per sample. */ }; static unsigned char init_pck[] = INIT_PCK; /* * List of speeds to try to establish connection with the camera. * Check 9600 first, as it's the speed the camera comes up in, then * 115200, as that is the one most likely to be configured from a * previous run */ static struct pkt_speed speeds[] = { {B9600, {0x96, 0x00}}, #ifdef B115200 {B115200, {0x11, 0x52}}, #endif #ifdef B57600 {B57600, {0x57, 0x60}}, #endif {B38400, {0x38, 0x40}}, {B19200, {0x19, 0x20}}, }; #define NUM_OF_SPEEDS ((int)(sizeof(speeds) / sizeof(struct pkt_speed))) static struct termios tty_orig; static int send_pck (int fd, unsigned char *pck) { int n; unsigned char r; /* * Not quite sure why we need this, but the program works a whole * lot better (at least on the DC25) with this short delay. */ #ifdef HAVE_USLEEP usleep (10); #else sleep (1); #endif if (write (fd, (char *) pck, 8) != 8) { DBG (2, "send_pck: error: write returned -1\n"); return -1; } if ((n = read (fd, (char *) &r, 1)) != 1) { DBG (2, "send_pck: error: read returned -1\n"); return -1; } return (r == 0xd1) ? 0 : -1; } static int init_dc20 (char *device, speed_t speed) { struct termios tty_new; int speed_index; DBG (1, "DC-20/25 Backend 05/07/01\n"); for (speed_index = 0; speed_index < NUM_OF_SPEEDS; speed_index++) { if (speeds[speed_index].baud == speed) { init_pck[2] = speeds[speed_index].pkt_code[0]; init_pck[3] = speeds[speed_index].pkt_code[1]; break; } } if (init_pck[2] == 0) { DBG (2, "unsupported baud rate.\n"); return -1; } /* Open device file. */ if ((tfd = open (device, O_RDWR)) == -1) { DBG (2, "init_dc20: error: could not open %s for read/write\n", device); return -1; } /* Save old device information to restore when we are done. */ if (tcgetattr (tfd, &tty_orig) == -1) { DBG (2, "init_dc20: error: could not get attributes\n"); return -1; } memcpy ((char *) &tty_new, (char *) &tty_orig, sizeof (struct termios)); /* We need the device to be raw. 8 bits even parity on 9600 baud to start. */ #ifdef HAVE_CFMAKERAW cfmakeraw (&tty_new); #else tty_new.c_lflag &= ~(ICANON | ECHO | ISIG); #endif tty_new.c_oflag &= ~CSTOPB; tty_new.c_cflag |= PARENB; tty_new.c_cflag &= ~PARODD; tty_new.c_cc[VMIN] = 0; tty_new.c_cc[VTIME] = 50; cfsetospeed (&tty_new, B9600); cfsetispeed (&tty_new, B9600); if (tcsetattr (tfd, TCSANOW, &tty_new) == -1) { DBG (2, "init_dc20: error: could not set attributes\n"); return -1; } if (send_pck (tfd, init_pck) == -1) { /* * The camera always powers up at 9600, so we try * that first. However, it may be already set to * a different speed. Try the entries in the table: */ for (speed_index = NUM_OF_SPEEDS - 1; speed_index > 0; speed_index--) { DBG (3, "init_dc20: changing speed to %d\n", (int) speeds[speed_index].baud); cfsetospeed (&tty_new, speeds[speed_index].baud); cfsetispeed (&tty_new, speeds[speed_index].baud); if (tcsetattr (tfd, TCSANOW, &tty_new) == -1) { DBG (2, "init_dc20: error: could not set attributes\n"); return -1; } if (send_pck (tfd, init_pck) != -1) break; } if (speed_index == 0) { tcsetattr (tfd, TCSANOW, &tty_orig); DBG (2, "init_dc20: error: no suitable baud rate\n"); return -1; } } /* Set speed to requested speed. Also, make a long timeout (we need this for erase and shoot operations) */ tty_new.c_cc[VTIME] = 150; cfsetospeed (&tty_new, speed); cfsetispeed (&tty_new, speed); if (tcsetattr (tfd, TCSANOW, &tty_new) == -1) { DBG (2, "init_dc20: error: could not set attributes\n"); return -1; } return tfd; } static void close_dc20 (int fd) { DBG (127, "close_dc20() called\n"); /* * Put the camera back to 9600 baud */ init_pck[2] = speeds[0].pkt_code[0]; init_pck[3] = speeds[0].pkt_code[1]; if (send_pck (fd, init_pck) == -1) { DBG (4, "close_dc20: error: could not set attributes\n"); } /* Restore original device settings. */ if (tcsetattr (fd, TCSANOW, &tty_orig) == -1) { DBG (4, "close_dc20: error: could not set attributes\n"); } if (close (fd) == -1) { DBG (4, "close_dc20: error: could not close device\n"); } } static unsigned char info_pck[] = INFO_PCK; static Dc20Info * get_info (int fd) { unsigned char buf[256]; if (send_pck (fd, info_pck) == -1) { DBG (2, "get_info: error: send_pck returned -1\n"); return NULL; } DBG (9, "get_info: read info packet\n"); if (read_data (fd, buf, 256) == -1) { DBG (2, "get_info: error: read_data returned -1\n"); return NULL; } if (end_of_data (fd) == -1) { DBG (2, "get_info: error: end_of_data returned -1\n"); return NULL; } CameraInfo.model = buf[1]; CameraInfo.ver_major = buf[2]; CameraInfo.ver_minor = buf[3]; CameraInfo.pic_taken = buf[8] << 8 | buf[9]; if (CameraInfo.model == 0x25) { /* Not sure where the previous line came from. All the * information I have says that even on the DC20 the number of * standard res pics is in byte 17 and the number of high res pics * is in byte 19. This is definitely true on my DC25. */ CameraInfo.pic_taken = buf[17] + buf[19]; } image_range.max = CameraInfo.pic_taken; image_range.min = CameraInfo.pic_taken ? 1 : 0; CameraInfo.pic_left = buf[10] << 8 | buf[11]; if (CameraInfo.model == 0x25) { /* Not sure where the previous line came from. All the * information I have says that even on the DC20 the number of * standard res pics left is in byte 23 and the number of high res * pics left is in byte 21. It seems to me that the conservative * approach is to report the number of high res pics left. */ CameraInfo.pic_left = buf[21]; } CameraInfo.flags.low_res = buf[23]; if (CameraInfo.model == 0x25) { /* Not sure where the previous line came from. All the * information I have says that even on the DC20 the low_res * byte is 11. */ CameraInfo.flags.low_res = buf[11]; } CameraInfo.flags.low_batt = buf[29]; return &CameraInfo; } static int read_data (int fd, unsigned char *buf, int sz) { unsigned char ccsum; unsigned char rcsum; unsigned char c; int retries = 0; int n; int r = 0; int i; while (retries++ < 5) { /* * If this is not the first time through, then it must be * a retry - signal the camera that we didn't like what * we got. In either case, start filling the packet */ if (retries != 1) { DBG (2, "Attempt retry %d\n", retries); c = 0xe3; if (write (fd, (char *) &c, 1) != 1) { DBG (2, "read_data: error: write ack\n"); return -1; } } for (n = 0; n < sz && (r = read (fd, (char *) &buf[n], sz - n)) > 0; n += r) ; if (r <= 0) { DBG (2, "read_data: error: read returned -1\n"); continue; } if (n < sz || read (fd, &rcsum, 1) != 1) { DBG (2, "read_data: error: buffer underrun or no checksum\n"); continue; } for (i = 0, ccsum = 0; i < n; i++) ccsum ^= buf[i]; if (ccsum != rcsum) { DBG (2, "read_data: error: bad checksum (%02x != %02x)\n", rcsum, ccsum); continue; } /* If we got this far, then the packet is OK */ break; } c = 0xd2; if (write (fd, (char *) &c, 1) != 1) { DBG (2, "read_data: error: write ack\n"); return -1; } return 0; } static int end_of_data (int fd) { char c; if (read (fd, &c, 1) != 1) { DBG (2, "end_of_data: error: read returned -1\n"); return -1; } if (c != 0) { DBG (2, "end_of_data: error: bad EOD from camera (%02x)\n", (unsigned) c); return -1; } return 0; } #include #define BIDIM_ARRAY(name, x, y, width) (name[((x) + ((y) * (width)))]) /* * These definitions depend on the resolution of the image */ #define MY_LOW_RIGHT_MARGIN 6 /* * These definitions are constant with resolution */ #define MY_LEFT_MARGIN 2 #define NET_COLUMNS (columns - MY_LEFT_MARGIN - right_margin) #define NET_LINES (HEIGHT - TOP_MARGIN - BOTTOM_MARGIN) #define NET_PIXELS (NET_COLUMNS * NET_LINES) #define SCALE 64 #define SMAX (256 * SCALE - 1) #define HORIZONTAL_INTERPOLATIONS 3 #define HISTOGRAM_STEPS 4096 #define RFACTOR 0.64 #define GFACTOR 0.58 #define BFACTOR 1.00 #define RINTENSITY 0.476 #define GINTENSITY 0.299 #define BINTENSITY 0.175 #define SATURATION 1.0 #define NORM_PERCENTAGE 3 static int columns = HIGH_WIDTH, right_margin = HIGH_RIGHT_MARGIN, camera_header_size = HIGH_CAMERA_HEADER; static int low_i = -1, high_i = -1, norm_percentage = NORM_PERCENTAGE; static float saturation = SATURATION, rfactor = RFACTOR, gfactor = GFACTOR, bfactor = BFACTOR; static void set_initial_interpolation (const unsigned char ccd[], short horizontal_interpolation[]) { int column, line; for (line = 0; line < HEIGHT; line++) { BIDIM_ARRAY (horizontal_interpolation, MY_LEFT_MARGIN, line, columns) = BIDIM_ARRAY (ccd, MY_LEFT_MARGIN + 1, line, columns) * SCALE; BIDIM_ARRAY (horizontal_interpolation, columns - right_margin - 1, line, columns) = BIDIM_ARRAY (ccd, columns - right_margin - 2, line, columns) * SCALE; for (column = MY_LEFT_MARGIN + 1; column < columns - right_margin - 1; column++) { BIDIM_ARRAY (horizontal_interpolation, column, line, columns) = (BIDIM_ARRAY (ccd, column - 1, line, columns) + BIDIM_ARRAY (ccd, column + 1, line, columns)) * (SCALE / 2); } } } static void interpolate_horizontally (const unsigned char ccd[], short horizontal_interpolation[]) { int column, line, i, initial_column; for (line = TOP_MARGIN - 1; line < HEIGHT - BOTTOM_MARGIN + 1; line++) { for (i = 0; i < HORIZONTAL_INTERPOLATIONS; i++) { for (initial_column = MY_LEFT_MARGIN + 1; initial_column <= MY_LEFT_MARGIN + 2; initial_column++) { for (column = initial_column; column < columns - right_margin - 1; column += 2) { BIDIM_ARRAY (horizontal_interpolation, column, line, columns) = ((float) BIDIM_ARRAY (ccd, column - 1, line, columns) / BIDIM_ARRAY (horizontal_interpolation, column - 1, line, columns) + (float) BIDIM_ARRAY (ccd, column + 1, line, columns) / BIDIM_ARRAY (horizontal_interpolation, column + 1, line, columns)) * BIDIM_ARRAY (ccd, column, line, columns) * (SCALE * SCALE / 2) + 0.5; } } } } } static void interpolate_vertically (const unsigned char ccd[], const short horizontal_interpolation[], short red[], short green[], short blue[]) { int column, line; for (line = TOP_MARGIN; line < HEIGHT - BOTTOM_MARGIN; line++) { for (column = MY_LEFT_MARGIN; column < columns - right_margin; column++) { int r2gb, g2b, rg2, rgb2, r, g, b; int this_ccd = BIDIM_ARRAY (ccd, column, line, columns) * SCALE; int up_ccd = BIDIM_ARRAY (ccd, column, line - 1, columns) * SCALE; int down_ccd = BIDIM_ARRAY (ccd, column, line + 1, columns) * SCALE; int this_horizontal_interpolation = BIDIM_ARRAY (horizontal_interpolation, column, line, columns); int this_intensity = this_ccd + this_horizontal_interpolation; int up_intensity = BIDIM_ARRAY (horizontal_interpolation, column, line - 1, columns) + up_ccd; int down_intensity = BIDIM_ARRAY (horizontal_interpolation, column, line + 1, columns) + down_ccd; int this_vertical_interpolation; /* * PSF: I don't understand all this code, but I've found pictures * where up_intensity or down_intensity are zero, resulting in a * divide by zero error. It looks like this only happens when * up_ccd or down_ccd are also zero, so we just set the intensity * value to non-zero to prevent the error. */ if (down_ccd == 0) DBG (10, "down_ccd==0 at %d,%d\n", line, column); if (up_ccd == 0) DBG (10, "up_ccd==0 at %d,%d\n", line, column); if (down_intensity == 0) { DBG (9, "Found down_intensity==0 at %d,%d down_ccd=%d\n", line, column, down_ccd); down_intensity = 1; } if (up_intensity == 0) { DBG (9, "Found up_intensity==0 at %d,%d up_ccd=%d\n", line, column, up_ccd); up_intensity = 1; } if (line == TOP_MARGIN) { this_vertical_interpolation = (float) down_ccd / down_intensity * this_intensity + 0.5; } else if (line == HEIGHT - BOTTOM_MARGIN - 1) { this_vertical_interpolation = (float) up_ccd / up_intensity * this_intensity + 0.5; } else { this_vertical_interpolation = ((float) up_ccd / up_intensity + (float) down_ccd / down_intensity) * this_intensity / 2.0 + 0.5; } if (line & 1) { if (column & 1) { r2gb = this_ccd; g2b = this_horizontal_interpolation; rg2 = this_vertical_interpolation; r = (2 * (r2gb - g2b) + rg2) / 5; g = (rg2 - r) / 2; b = g2b - 2 * g; } else { g2b = this_ccd; r2gb = this_horizontal_interpolation; rgb2 = this_vertical_interpolation; r = (3 * r2gb - g2b - rgb2) / 5; g = 2 * r - r2gb + g2b; b = g2b - 2 * g; } } else { if (column & 1) { rg2 = this_ccd; rgb2 = this_horizontal_interpolation; r2gb = this_vertical_interpolation; b = (3 * rgb2 - r2gb - rg2) / 5; g = (rgb2 - r2gb + rg2 - b) / 2; r = rg2 - 2 * g; } else { rgb2 = this_ccd; rg2 = this_horizontal_interpolation; g2b = this_vertical_interpolation; b = (g2b - 2 * (rg2 - rgb2)) / 5; g = (g2b - b) / 2; r = rg2 - 2 * g; } } if (r < 0) r = 0; if (g < 0) g = 0; if (b < 0) b = 0; BIDIM_ARRAY (red, column, line, columns) = r; BIDIM_ARRAY (green, column, line, columns) = g; BIDIM_ARRAY (blue, column, line, columns) = b; } } } static void adjust_color_and_saturation (short red[], short green[], short blue[]) { int line, column; int r_min = SMAX, g_min = SMAX, b_min = SMAX; int r_max = 0, g_max = 0, b_max = 0; float sqr_saturation = sqrt (saturation); for (line = TOP_MARGIN; line < HEIGHT - BOTTOM_MARGIN; line++) { for (column = MY_LEFT_MARGIN; column < columns - right_margin; column++) { float r = BIDIM_ARRAY (red, column, line, columns) * rfactor; float g = BIDIM_ARRAY (green, column, line, columns) * gfactor; float b = BIDIM_ARRAY (blue, column, line, columns) * bfactor; if (saturation != 1.0) { float *min, *mid, *max, new_intensity; float intensity = r * RINTENSITY + g * GINTENSITY + b * BINTENSITY; if (r > g) { if (r > b) { max = &r; if (g > b) { min = &b; mid = &g; } else { min = &g; mid = &b; } } else { min = &g; mid = &r; max = &b; } } else { if (g > b) { max = &g; if (r > b) { min = &b; mid = &r; } else { min = &r; mid = &b; } } else { min = &r; mid = &g; max = &b; } } *mid = *min + sqr_saturation * (*mid - *min); *max = *min + saturation * (*max - *min); new_intensity = r * RINTENSITY + g * GINTENSITY + b * BINTENSITY; r *= intensity / new_intensity; g *= intensity / new_intensity; b *= intensity / new_intensity; } r += 0.5; g += 0.5; b += 0.5; if (r_min > r) r_min = r; if (g_min > g) g_min = g; if (b_min > b) b_min = b; if (r_max < r) r_max = r; if (g_max < g) g_max = g; if (b_max < b) b_max = b; BIDIM_ARRAY (red, column, line, columns) = r; BIDIM_ARRAY (green, column, line, columns) = g; BIDIM_ARRAY (blue, column, line, columns) = b; } } } static int min3 (int x, int y, int z) { return (x < y ? (x < z ? x : z) : (y < z ? y : z)); } static int max3 (int x, int y, int z) { return (x > y ? (x > z ? x : z) : (y > z ? y : z)); } static void determine_limits (const short red[], const short green[], const short blue[], int *low_i_ptr, int *high_i_ptr) { unsigned int histogram[HISTOGRAM_STEPS + 1]; int column, line, i, s; int low_i = *low_i_ptr, high_i = *high_i_ptr; int max_i = 0; for (line = TOP_MARGIN; line < HEIGHT - BOTTOM_MARGIN; line++) { for (column = MY_LEFT_MARGIN; column < columns - right_margin; column++) { i = max3 (BIDIM_ARRAY (red, column, line, columns), BIDIM_ARRAY (green, column, line, columns), BIDIM_ARRAY (blue, column, line, columns)); if (i > max_i) max_i = i; } } if (low_i == -1) { for (i = 0; i <= HISTOGRAM_STEPS; i++) histogram[i] = 0; for (line = TOP_MARGIN; line < HEIGHT - BOTTOM_MARGIN; line++) { for (column = MY_LEFT_MARGIN; column < columns - right_margin; column++) { i = min3 (BIDIM_ARRAY (red, column, line, columns), BIDIM_ARRAY (green, column, line, columns), BIDIM_ARRAY (blue, column, line, columns)); histogram[i * HISTOGRAM_STEPS / max_i]++; } } for (low_i = 0, s = 0; low_i <= HISTOGRAM_STEPS && s < NET_PIXELS * norm_percentage / 100; low_i++) { s += histogram[low_i]; } low_i = (low_i * max_i + HISTOGRAM_STEPS / 2) / HISTOGRAM_STEPS; *low_i_ptr = low_i; } if (high_i == -1) { for (i = 0; i <= HISTOGRAM_STEPS; i++) histogram[i] = 0; for (line = TOP_MARGIN; line < HEIGHT - BOTTOM_MARGIN; line++) { for (column = MY_LEFT_MARGIN; column < columns - right_margin; column++) { i = max3 (BIDIM_ARRAY (red, column, line, columns), BIDIM_ARRAY (green, column, line, columns), BIDIM_ARRAY (blue, column, line, columns)); histogram[i * HISTOGRAM_STEPS / max_i]++; } } for (high_i = HISTOGRAM_STEPS, s = 0; high_i >= 0 && s < NET_PIXELS * norm_percentage / 100; high_i--) { s += histogram[high_i]; } high_i = (high_i * max_i + HISTOGRAM_STEPS / 2) / HISTOGRAM_STEPS; *high_i_ptr = high_i; } /* if (verbose) printf ("%s: determine_limits: low_i = %d, high_i = %d\n", __progname, low_i, high_i); */ } /* * The original dc20ctrl program used a default gamma of 0.35, but I thought * 0.45 looks better. In addition, since xscanimage seems to always force * a resolution of 0.1, I multiply everything by 10 and make the default * 4.5. */ static unsigned char * make_gamma_table (int range) { int i; double factor = pow (256.0, 1.0 / (SANE_UNFIX (dc25_opt_gamma) / 10.0)) / range; unsigned char *gamma_table; if ((gamma_table = malloc (range * sizeof (unsigned char))) == NULL) { DBG (1, "make_gamma_table: can't allocate memory for gamma table\n"); return NULL; } for (i = 0; i < range; i++) { int g = pow ((double) i * factor, (SANE_UNFIX (dc25_opt_gamma) / 10.0)) + 0.5; /* if (verbose) fprintf (stderr, "%s: make_gamma_table: gamma[%4d] = %3d\n", __progname, i, g); */ if (g > 255) g = 255; gamma_table[i] = g; } return gamma_table; } static int lookup_gamma_table (int i, int low_i, int high_i, const unsigned char gamma_table[]) { if (i <= low_i) return 0; if (i >= high_i) return 255; return gamma_table[i - low_i]; } static int output_rgb (const short red[], const short green[], const short blue[], int low_i, int high_i, struct pixmap *pp) { int r_min = 255, g_min = 255, b_min = 255; int r_max = 0, g_max = 0, b_max = 0; int column, line; unsigned char *gamma_table = make_gamma_table (high_i - low_i); if (gamma_table == NULL) { DBG (10, "output_rgb: error: cannot make gamma table\n"); return -1; } for (line = TOP_MARGIN; line < HEIGHT - BOTTOM_MARGIN; line++) { for (column = MY_LEFT_MARGIN; column < columns - right_margin; column++) { int r = lookup_gamma_table (BIDIM_ARRAY (red, column, line, columns), low_i, high_i, gamma_table); int g = lookup_gamma_table (BIDIM_ARRAY (green, column, line, columns), low_i, high_i, gamma_table); int b = lookup_gamma_table (BIDIM_ARRAY (blue, column, line, columns), low_i, high_i, gamma_table); if (r > 255) r = 255; else if (r < 0) r = 0; if (g > 255) g = 255; else if (g < 0) g = 0; if (b > 255) b = 255; else if (b < 0) b = 0; set_pixel_rgb (pp, column - MY_LEFT_MARGIN, line - TOP_MARGIN, r, g, b); if (r_min > r) r_min = r; if (g_min > g) g_min = g; if (b_min > b) b_min = b; if (r_max < r) r_max = r; if (g_max < g) g_max = g; if (b_max < b) b_max = b; } } free (gamma_table); return 0; } static int comet_to_pixmap (unsigned char *pic, struct pixmap *pp) { unsigned char *ccd; short *horizontal_interpolation, *red, *green, *blue; int retval = 0; if (pic == NULL) { DBG (1, "cmttoppm: error: no input image\n"); return -1; } if (pic[4] == 0x01) { /* Low resolution mode */ columns = LOW_WIDTH; right_margin = MY_LOW_RIGHT_MARGIN; camera_header_size = LOW_CAMERA_HEADER; } else { /* High resolution mode */ columns = HIGH_WIDTH; right_margin = HIGH_RIGHT_MARGIN; camera_header_size = HIGH_CAMERA_HEADER; } ccd = pic + camera_header_size; if ((horizontal_interpolation = malloc (sizeof (short) * HEIGHT * columns)) == NULL) { DBG (1, "cmttoppm: error: not enough memory for horizontal_interpolation\n"); return -1; } if ((red = malloc (sizeof (short) * HEIGHT * columns)) == NULL) { DBG (1, "error: not enough memory for red\n"); return -1; } if ((green = malloc (sizeof (short) * HEIGHT * columns)) == NULL) { DBG (1, "error: not enough memory for green\n"); return -1; } if ((blue = malloc (sizeof (short) * HEIGHT * columns)) == NULL) { DBG (1, "error: not enough memory for blue\n"); return -1; } /* Decode raw CCD data to RGB */ set_initial_interpolation (ccd, horizontal_interpolation); interpolate_horizontally (ccd, horizontal_interpolation); interpolate_vertically (ccd, horizontal_interpolation, red, green, blue); adjust_color_and_saturation (red, green, blue); /* Determine lower and upper limit using histogram */ if (low_i == -1 || high_i == -1) { determine_limits (red, green, blue, &low_i, &high_i); } /* Output pixmap structure */ retval = output_rgb (red, green, blue, low_i, high_i, pp); return retval; } static int convert_pic (char *base_name, int format) { FILE *ifp; unsigned char pic[MAX_IMAGE_SIZE]; int res, image_width, net_width, components; struct pixmap *pp2; DBG (127, "convert_pic() called\n"); /* * Read the image in memory */ if ((ifp = fopen (base_name, "rb")) == NULL) { DBG (10, "convert_pic: error: cannot open %s for reading\n", base_name); return -1; } if (fread (pic, COMET_HEADER_SIZE, 1, ifp) != 1) { DBG (10, "convert_pic: error: cannot read COMET header\n"); fclose (ifp); return -1; } if (strncmp ((char *) pic, COMET_MAGIC, sizeof (COMET_MAGIC)) != 0) { DBG (10, "convert_pic: error: file %s is not in COMET format\n", base_name); fclose (ifp); return -1; } if (fread (pic, LOW_CAMERA_HEADER, 1, ifp) != 1) { DBG (10, "convert_pic: error: cannot read camera header\n"); fclose (ifp); return -1; } res = pic[4]; if (res == 0) { /* * We just read a LOW_CAMERA_HEADER block, so resync with the * HIGH_CAMERA_HEADER length by reading once more one of this. */ if (fread (pic + LOW_CAMERA_HEADER, LOW_CAMERA_HEADER, 1, ifp) != 1) { DBG (10, "convert_pic: error: cannot resync with high resolution header\n"); fclose (ifp); return -1; } } if (fread (pic + CAMERA_HEADER (res), WIDTH (res), HEIGHT, ifp) != HEIGHT) { DBG (9, "convert_pic: error: cannot read picture\n"); fclose (ifp); return -1; } fclose (ifp); /* * Setup image size with resolution */ image_width = WIDTH (res); net_width = image_width - LEFT_MARGIN - RIGHT_MARGIN (res); components = (format & SAVE_24BITS) ? 3 : 1; /* * Convert the image to 24 bits */ if ((pp = alloc_pixmap (net_width - 1, HEIGHT - BOTTOM_MARGIN - 1, components)) == NULL) { DBG (1, "convert_pic: error: alloc_pixmap\n"); return -1; } comet_to_pixmap (pic, pp); if (format & SAVE_ADJASPECT) { /* * Stretch image */ if (res) pp2 = alloc_pixmap (320, HEIGHT - BOTTOM_MARGIN - 1, components); else pp2 = alloc_pixmap (net_width - 1, 373, components); if (pp2 == NULL) { DBG (2, "convert_pic: error: alloc_pixmap\n"); free_pixmap (pp); return -1; } if (res) zoom_x (pp, pp2); else zoom_y (pp, pp2); free_pixmap (pp); pp = pp2; pp2 = NULL; } return 0; } #define PGM_EXT "pgm" #define PPM_EXT "ppm" #define RED 0.30 #define GREEN 0.59 #define BLUE 0.11 #define RED_OFFSET 0 #define GREEN_OFFSET 1 #define BLUE_OFFSET 2 #define GET_COMP(pp, x, y, c) (pp->planes[((x) + (y)*pp->width)*pp->components + (c)]) #define GET_R(pp, x, y) (GET_COMP(pp, x, y, RED_OFFSET)) #define GET_G(pp, x, y) (GET_COMP(pp, x, y, GREEN_OFFSET)) #define GET_B(pp, x, y) (GET_COMP(pp, x, y, BLUE_OFFSET)) static struct pixmap * alloc_pixmap (int x, int y, int d) { struct pixmap *result = NULL; if (d == 1 || d == 3) { if (x > 0) { if (y > 0) { if ((result = malloc (sizeof (struct pixmap))) != NULL) { result->width = x; result->height = y; result->components = d; if (!(result->planes = malloc (x * y * d))) { DBG (10, "alloc_pixmap: error: not enough memory for bitplanes\n"); free (result); result = NULL; } } else DBG (10, "alloc_pixmap: error: not enough memory for pixmap\n"); } else DBG (10, "alloc_pixmap: error: y is out of range\n"); } else DBG (10, "alloc_pixmap: error: x is out of range\n"); } else DBG (10, "alloc_pixmap: error: cannot handle %d components\n", d); return result; } static void free_pixmap (struct pixmap *p) { if (p) { free (p->planes); free (p); } } static int set_pixel_rgb (struct pixmap *p, int x, int y, unsigned char r, unsigned char g, unsigned char b) { int result = 0; if (p) { if (x >= 0 && x < p->width) { if (y >= 0 && y < p->height) { if (p->components == 1) { GET_R (p, x, y) = RED * r + GREEN * g + BLUE * b; } else { GET_R (p, x, y) = r; GET_G (p, x, y) = g; GET_B (p, x, y) = b; } } else { DBG (10, "set_pixel_rgb: error: y out of range\n"); result = -1; } } else { DBG (10, "set_pixel_rgb: error: x out of range\n"); result = -1; } } return result; } static int zoom_x (struct pixmap *source, struct pixmap *dest) { int result = 0, dest_col, row, component, src_index; float ratio, src_ptr, delta; unsigned char src_component; if (source && dest) { /* * We could think of resizing a pixmap and changing the number of * components at the same time. Maybe this will be implemented later. */ if (source->height == dest->height && source->components == dest->components) { if (source->width < dest->width) { ratio = ((float) source->width / (float) dest->width); for (src_ptr = 0, dest_col = 0; dest_col < dest->width; src_ptr += ratio, dest_col++) { /* * dest[dest_col] = source[(int)src_ptr] + * (source[((int)src_ptr) + 1] - source[(int)src_ptr]) * * (src_ptr - (int)src_ptr); */ src_index = (int) src_ptr; delta = src_ptr - src_index; for (row = 0; row < source->height; row++) { for (component = 0; component < source->components; component++) { src_component = GET_COMP (source, src_index, row, component); GET_COMP (dest, dest_col, row, component) = src_component + (GET_COMP (source, src_index + 1, row, component) - src_component) * delta; } } } } else { DBG (10, "zoom_x: error: can only zoom out\n"); result = -1; } } else { DBG (10, "zoom_x: error: incompatible pixmaps\n"); result = -1; } } return result; } static int zoom_y (struct pixmap *source, struct pixmap *dest) { int result = 0, dest_row, column, component, src_index; float ratio, src_ptr, delta; unsigned char src_component; if (source && dest) { /* * We could think of resizing a pixmap and changing the number of * components at the same time. Maybe this will be implemented later. */ if (source->width == dest->width && source->components == dest->components) { if (source->height < dest->height) { ratio = ((float) source->height / (float) dest->height); for (src_ptr = 0, dest_row = 0; dest_row < dest->height; src_ptr += ratio, dest_row++) { /* * dest[dest_row] = source[(int)src_ptr] + * (source[((int)src_ptr) + 1] - source[(int)src_ptr]) * * (src_ptr - (int)src_ptr); */ src_index = (int) src_ptr; delta = src_ptr - src_index; for (column = 0; column < source->width; column++) { for (component = 0; component < source->components; component++) { src_component = GET_COMP (source, column, src_index, component); GET_COMP (dest, column, dest_row, component) = src_component + (GET_COMP (source, column, src_index + 1, component) - src_component) * delta; } } } } else { DBG (10, "zoom_y: error: can only zoom out\n"); result = -1; } } else { DBG (10, "zoom_y: error: incompatible pixmaps\n"); result = -1; } } return result; } static unsigned char shoot_pck[] = SHOOT_PCK; static int shoot (int fd) { struct termios tty_temp, tty_old; int result = 0; DBG (127, "shoot() called\n"); if (write (fd, (char *) shoot_pck, 8) != 8) { DBG (3, "shoot: error: write error\n"); return -1; } if (CameraInfo.model != 0x25) { /* * WARNING: now we set the serial port to 9600 baud! */ if (tcgetattr (fd, &tty_old) == -1) { DBG (3, "shoot: error: could not get attributes\n"); return -1; } memcpy ((char *) &tty_temp, (char *) &tty_old, sizeof (struct termios)); cfsetispeed (&tty_temp, B9600); cfsetospeed (&tty_temp, B9600); /* * Apparently there is a bug in the DC20 where the response to * the shoot request is always at 9600. The DC25 does not have * this bug, so we skip this block. */ if (tcsetattr (fd, TCSANOW, &tty_temp) == -1) { DBG (3, "shoot: error: could not set attributes\n"); return -1; } } if (read (fd, (char *) &result, 1) != 1) { DBG (3, "shoot: error: read returned -1\n"); result = -1; } else { result = (result == 0xD1) ? 0 : -1; } if (CameraInfo.model != 0x25) { /* * We reset the serial to its original speed. * We can skip this on the DC25 also. */ if (tcsetattr (fd, TCSANOW, &tty_old) == -1) { DBG (3, "shoot: error: could not reset attributes\n"); result = -1; } } if (result == 0) { if (CameraInfo.model == 0x25) { /* * If we don't put this in, the next read will time out * and return failure. Does the DC-20 need it too? */ sleep (3); } if (end_of_data (fd) == -1) { DBG (3, "shoot: error: end_of_data returned -1\n"); result = -1; } } return result; } static unsigned char erase_pck[] = ERASE_PCK; static int erase (int fd) { int count = 0; DBG (127, "erase() called for image %d\n", dc25_opt_image_number); erase_pck[3] = dc25_opt_image_number; if (dc25_opt_erase) { erase_pck[3] = 0; } if (send_pck (fd, erase_pck) == -1) { DBG (3, "erase: error: send_pck returned -1\n"); return -1; } if (CameraInfo.model == 0x25) { /* * This block may really apply to the DC20 also, but since I * don't have one, it's hard to say for sure. On the DC25, erase * takes long enough that the read may timeout without returning * any data before the erase is complete. We let this happen * up to 4 times, then give up. */ while (count < 4) { if (end_of_data (fd) == -1) { count++; } else { break; } } if (count == 4) { DBG (3, "erase: error: end_of_data returned -1\n"); return -1; } } else { /* Assume DC-20 */ if (end_of_data (fd) == -1) { DBG (3, "erase: error: end_of_data returned -1\n"); return -1; } } return 0; } static unsigned char res_pck[] = RES_PCK; static int change_res (int fd, unsigned char res) { DBG (127, "change_res called\n"); if (res != 0 && res != 1) { DBG (3, "change_res: error: unsupported resolution\n"); return -1; } res_pck[2] = res; if (send_pck (fd, res_pck) == -1) { DBG (4, "change_res: error: send_pck returned -1\n"); } if (end_of_data (fd) == -1) { DBG (4, "change_res: error: end_of_data returned -1\n"); } return 0; } SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize) { char dev_name[PATH_MAX], *p; size_t len; FILE *fp; int baud; strcpy (tty_name, DEF_TTY_NAME); DBG_INIT (); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); fp = sanei_config_open (DC25_CONFIG_FILE); DBG (127, "sane_init()\n"); if (!fp) { /* default to /dev/ttyS0 instead of insisting on config file */ DBG (1, "sane_init: missing config file '%s'\n", DC25_CONFIG_FILE); } else { while (sanei_config_read (dev_name, sizeof (dev_name), fp)) { dev_name[sizeof (dev_name) - 1] = '\0'; DBG (20, "sane_init: config- %s", dev_name); if (dev_name[0] == '#') continue; /* ignore line comments */ len = strlen (dev_name); if (!len) continue; /* ignore empty lines */ if (strncmp (dev_name, "port=", 5) == 0) { p = strchr (dev_name, '/'); if (p) { strcpy (tty_name, p); } DBG (20, "Config file port=%s\n", tty_name); } else if (strncmp (dev_name, "baud=", 5) == 0) { baud = atoi (&dev_name[5]); switch (baud) { case 9600: tty_baud = B9600; break; case 19200: tty_baud = B19200; break; case 38400: tty_baud = B38400; break; #ifdef B57600 case 57600: tty_baud = B57600; break; #endif #ifdef B115200 case 115200: tty_baud = B115200; break; #endif default: DBG (20, "Unknown baud=%d\n", baud); tty_baud = DEFAULT_TTY_BAUD; break; } DBG (20, "Config file baud=%lu\n", (u_long) tty_baud); } else if (strcmp (dev_name, "dumpinquiry") == 0) { dumpinquiry = SANE_TRUE; } } fclose (fp); } if ((tfd = init_dc20 (tty_name, tty_baud)) == -1) { return SANE_STATUS_INVAL; } if ((dc20_info = get_info (tfd)) == NULL) { DBG (2, "error: could not get info\n"); close_dc20 (tfd); return SANE_STATUS_INVAL; } if (dumpinquiry) { DBG (0, "\nCamera information:\n~~~~~~~~~~~~~~~~~\n\n"); DBG (0, "Model...........: DC%x\n", dc20_info->model); DBG (0, "Firmware version: %d.%d\n", dc20_info->ver_major, dc20_info->ver_minor); DBG (0, "Pictures........: %d/%d\n", dc20_info->pic_taken, dc20_info->pic_taken + dc20_info->pic_left); DBG (0, "Resolution......: %s\n", dc20_info->flags.low_res ? "low" : "high"); DBG (0, "Battery state...: %s\n", dc20_info->flags.low_batt ? "low" : "good"); } if (CameraInfo.pic_taken == 0) { /* sod[DC25_OPT_IMAGE_NUMBER].cap |= SANE_CAP_INACTIVE; */ image_range.min = 0; dc25_opt_image_number = 0; } else { /* sod[DC25_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE; */ image_range.min = 1; } return SANE_STATUS_GOOD; } void sane_exit (void) { } /* Device select/open/close */ static const SANE_Device dev[] = { { "0", "Kodak", "DC-25", "still camera"}, }; SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only) { static const SANE_Device *devlist[] = { dev + 0, 0 }; DBG (127, "sane_get_devices called\n"); if (dc20_info == NULL) { return SANE_STATUS_INVAL; } *device_list = devlist; return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { int i; DBG (127, "sane_open for device %s\n", devicename); if (!devicename[0]) { i = 0; } else { for (i = 0; i < NELEMS (dev); ++i) { if (strcmp (devicename, dev[i].name) == 0) { break; } } } if (i >= NELEMS (dev)) { return SANE_STATUS_INVAL; } if (is_open) { return SANE_STATUS_DEVICE_BUSY; } is_open = 1; *handle = MAGIC; if (dc20_info == NULL) { DBG (1, "No device info\n"); } DBG (3, "sane_open: pictures taken=%d\n", dc20_info->pic_taken); return SANE_STATUS_GOOD; } void sane_close (SANE_Handle handle) { DBG (127, "sane_close called\n"); if (handle == MAGIC) is_open = 0; if (pp) { free_pixmap (pp); pp = NULL; } close_dc20 (tfd); DBG (127, "sane_close returning\n"); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { if (handle != MAGIC || !is_open) return NULL; /* wrong device */ if (option < 0 || option >= NELEMS (sod)) return NULL; return &sod[option]; } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int * info) { SANE_Int myinfo = info_flags; SANE_Status status; info_flags = 0; DBG (127, "control_option(handle=%p,opt=%s,act=%s,val=%p,info=%p)\n", handle, sod[option].title, (action == SANE_ACTION_SET_VALUE ? "SET" : (action == SANE_ACTION_GET_VALUE ? "GET" : "SETAUTO")), value, (void *)info); if (handle != MAGIC || !is_open) return SANE_STATUS_INVAL; /* Unknown handle ... */ if (option < 0 || option >= NELEMS (sod)) return SANE_STATUS_INVAL; /* Unknown option ... */ switch (action) { case SANE_ACTION_SET_VALUE: status = sanei_constrain_value (sod + option, value, &myinfo); if (status != SANE_STATUS_GOOD) { DBG (1, "Constraint error in control_option\n"); return status; } switch (option) { case DC25_OPT_IMAGE_NUMBER: dc25_opt_image_number = *(SANE_Word *) value; /* myinfo |= SANE_INFO_RELOAD_OPTIONS; */ break; case DC25_OPT_THUMBS: dc25_opt_thumbnails = !!*(SANE_Word *) value; myinfo |= SANE_INFO_RELOAD_PARAMS; if (dc25_opt_thumbnails) { /* * DC20 thumbnail are 80x60 grayscale, DC25 * thumbnails are color. */ parms.format = (CameraInfo.model == 0x25) ? SANE_FRAME_RGB : SANE_FRAME_GRAY; parms.bytes_per_line = 80 * 3; parms.pixels_per_line = 80; parms.lines = 60; } else { parms.format = SANE_FRAME_RGB; if (dc20_info->flags.low_res) { parms.bytes_per_line = 320 * 3; parms.pixels_per_line = 320; parms.lines = 243; } else { parms.bytes_per_line = 500 * 3; parms.pixels_per_line = 500; parms.lines = 373; } } break; case DC25_OPT_SNAP: dc25_opt_snap = !!*(SANE_Word *) value; myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; if (dc25_opt_snap) { sod[DC25_OPT_LOWRES].cap &= ~SANE_CAP_INACTIVE; } else { sod[DC25_OPT_LOWRES].cap |= SANE_CAP_INACTIVE; } break; case DC25_OPT_LOWRES: dc25_opt_lowres = !!*(SANE_Word *) value; myinfo |= SANE_INFO_RELOAD_PARAMS; if (!dc25_opt_thumbnails) { parms.format = SANE_FRAME_RGB; if (dc20_info->flags.low_res) { parms.bytes_per_line = 320 * 3; parms.pixels_per_line = 320; parms.lines = 243; } else { parms.bytes_per_line = 500 * 3; parms.pixels_per_line = 500; parms.lines = 373; } } break; case DC25_OPT_CONTRAST: dc25_opt_contrast = *(SANE_Word *) value; break; case DC25_OPT_GAMMA: dc25_opt_gamma = *(SANE_Word *) value; break; case DC25_OPT_ERASE: dc25_opt_erase = !!*(SANE_Word *) value; /* * erase and erase_one are mutually exclusive. If * this one is turned on, the other must be off */ if (dc25_opt_erase && dc25_opt_erase_one) { dc25_opt_erase_one = SANE_FALSE; myinfo |= SANE_INFO_RELOAD_OPTIONS; } break; case DC25_OPT_ERASE_ONE: dc25_opt_erase_one = !!*(SANE_Word *) value; /* * erase and erase_one are mutually exclusive. If * this one is turned on, the other must be off */ if (dc25_opt_erase_one && dc25_opt_erase) { dc25_opt_erase = SANE_FALSE; myinfo |= SANE_INFO_RELOAD_OPTIONS; } break; case DC25_OPT_DEFAULT: dc25_opt_contrast = SANE_FIX (DC25_OPT_CONTRAST_DEFAULT); dc25_opt_gamma = SANE_FIX (DC25_OPT_GAMMA_DEFAULT); myinfo |= SANE_INFO_RELOAD_OPTIONS; break; default: return SANE_STATUS_INVAL; } break; case SANE_ACTION_GET_VALUE: switch (option) { case 0: *(SANE_Word *) value = NELEMS (sod); break; case DC25_OPT_IMAGE_NUMBER: *(SANE_Word *) value = dc25_opt_image_number; break; case DC25_OPT_THUMBS: *(SANE_Word *) value = dc25_opt_thumbnails; break; case DC25_OPT_SNAP: *(SANE_Word *) value = dc25_opt_snap; break; case DC25_OPT_LOWRES: *(SANE_Word *) value = dc25_opt_lowres; break; case DC25_OPT_CONTRAST: *(SANE_Word *) value = dc25_opt_contrast; break; case DC25_OPT_GAMMA: *(SANE_Word *) value = dc25_opt_gamma; break; case DC25_OPT_ERASE: *(SANE_Word *) value = dc25_opt_erase; break; case DC25_OPT_ERASE_ONE: *(SANE_Word *) value = dc25_opt_erase_one; break; default: return SANE_STATUS_INVAL; } break; case SANE_ACTION_SET_AUTO: switch (option) { #if 0 case DC25_OPT_CONTRAST: dc25_opt_contrast = SANE_FIX (DC25_OPT_CONTRAST_DEFAULT); break; case DC25_OPT_GAMMA: dc25_opt_gamma = SANE_FIX (DC25_OPT_GAMMA_DEFAULT); break; #endif default: return SANE_STATUS_UNSUPPORTED; /* We are DUMB */ } } if (info) *info = myinfo; return SANE_STATUS_GOOD; } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { int rc = SANE_STATUS_GOOD; DBG (127, "sane_get_params called\n"); if (handle != MAGIC || !is_open) rc = SANE_STATUS_INVAL; /* Unknown handle ... */ *params = parms; return rc; } static unsigned char thumb_pck[] = THUMBS_PCK; static unsigned char pic_pck[] = PICS_PCK; static int bytes_in_buffer; static int bytes_read_from_buffer; static SANE_Byte buffer[1024]; static int total_bytes_read; static SANE_Bool started = SANE_FALSE; static int outbytes; SANE_Status sane_start (SANE_Handle handle) { int n, i; FILE *f; DBG (127, "sane_start called, handle=%lx\n", (u_long) handle); if (handle != MAGIC || !is_open || (dc25_opt_image_number == 0 && dc25_opt_snap == SANE_FALSE)) return SANE_STATUS_INVAL; /* Unknown handle ... */ if (started) { return SANE_STATUS_EOF; } if (dc25_opt_snap) { /* * Don't allow picture unless there is room in the * camera. */ if (CameraInfo.pic_left == 0) { DBG (3, "No room to store new picture\n"); return SANE_STATUS_INVAL; } /* * DC-20 can only change resolution when camer is empty. * DC-25 can do it any time. */ if (CameraInfo.model != 0x20 || CameraInfo.pic_taken == 0) { if (change_res (tfd, dc25_opt_lowres) == -1) { DBG (1, "Failed to set resolution\n"); return SANE_STATUS_INVAL; } } /* * Not sure why this delay is needed, but it seems to help: */ #ifdef HAVE_USLEEP usleep (10); #else sleep (1); #endif if (shoot (tfd) == -1) { DBG (1, "Failed to snap new picture\n"); return SANE_STATUS_INVAL; } else { info_flags |= SANE_INFO_RELOAD_OPTIONS; CameraInfo.pic_taken++; CameraInfo.pic_left--; dc25_opt_image_number = CameraInfo.pic_taken; if (image_range.min == 0) image_range.min = 1; image_range.max++; sod[DC25_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE; } } if (dc25_opt_thumbnails) { /* * For thumbnails, we can do things right where we * start the download, and grab the first block * from the camera. The reamining blocks will be * fetched as necessary by sane_read(). */ thumb_pck[3] = (unsigned char) dc25_opt_image_number; if (send_pck (tfd, thumb_pck) == -1) { DBG (4, "sane_start: error: send_pck returned -1\n"); return SANE_STATUS_INVAL; } if (read_data (tfd, buffer, 1024) == -1) { DBG (4, "sane_start: read_data failed\n"); return SANE_STATUS_INVAL; } /* * DC20 thumbnail are 80x60 grayscale, DC25 * thumbnails are color. */ parms.format = (CameraInfo.model == 0x25) ? SANE_FRAME_RGB : SANE_FRAME_GRAY; parms.bytes_per_line = 80 * 3; /* 80 pixels, 3 colors */ parms.pixels_per_line = 80; parms.lines = 60; bytes_in_buffer = 1024; bytes_read_from_buffer = 0; } else { /* * We do something a little messy, and violates the SANE * philosophy. However, since it is fairly tricky to * convert the DC2x "comet" files on the fly, we read in * the entire data stream in sane_open(), and use convert_pic * to convert it to an in-memory pixpmap. Then when * sane_read() is called, we fill the requests from * memory. A good project for me (or some kind volunteer) * would be to rewrite this and move the actual download * to sane_read(). However, one argument for keeping it * this way is that the data comes down pretty fast, and * it helps to dedicate the processor to this task. We * might get serial port overruns if we try to do other * things at the same time. * * Also, as a side note, I was constantly getting serial * port overruns on a 90MHz pentium until I used hdparm * to set the "-u1" flag on the system drives. */ char tmpnamebuf[] = TMPFILE_PATTERN; int fd = mkstemp (tmpnamebuf); if (fd == -1) { DBG (0, "Unable to make temp file %s\n", tmpnamebuf); return SANE_STATUS_INVAL; } f = fdopen (fd, "wb"); if (f == NULL) { DBG (0, "Unable to fdopen tmp file\n"); return SANE_STATUS_INVAL; } strcpy ((char *) buffer, COMET_MAGIC); fwrite (buffer, 1, COMET_HEADER_SIZE, f); pic_pck[3] = (unsigned char) dc25_opt_image_number; if (send_pck (tfd, pic_pck) == -1) { DBG (4, "sane_start: error: send_pck returned -1\n"); return SANE_STATUS_INVAL; } if (read_data (tfd, buffer, 1024) == -1) { DBG (5, "sane_start: read_data failed\n"); return SANE_STATUS_INVAL; } if (buffer[4] == 0) { /* hi-res image */ DBG (5, "sane_start: hi-res image\n"); n = 122; parms.bytes_per_line = 500 * 3; /* 3 colors */ parms.pixels_per_line = 500; parms.lines = 373; bytes_in_buffer = 1024; bytes_read_from_buffer = 0; } else { n = 61; DBG (5, "sane_start: low-res image\n"); parms.bytes_per_line = 320 * 3; /* 3 Colors */ parms.pixels_per_line = 320; parms.lines = 243; bytes_in_buffer = 1024; bytes_read_from_buffer = 0; } fwrite (buffer, 1, 1024, f); for (i = 1; i < n; i++) { if (read_data (tfd, buffer, 1024) == -1) { DBG (5, "sane_start: read_data failed\n"); return SANE_STATUS_INVAL; } fwrite (buffer, 1, 1024, f); } if (end_of_data (tfd) == -1) { fclose (f); DBG (4, "sane_open: end_of_data error\n"); return SANE_STATUS_INVAL; } else { fclose (f); if (convert_pic (tmpnamebuf, SAVE_ADJASPECT | SAVE_24BITS) == -1) { DBG (3, "sane_open: unable to convert\n"); return SANE_STATUS_INVAL; } unlink (tmpnamebuf); outbytes = 0; } } started = SANE_TRUE; total_bytes_read = 0; return SANE_STATUS_GOOD; } SANE_Status sane_read (SANE_Handle __sane_unused__ handle, SANE_Byte * data, SANE_Int max_length, SANE_Int * length) { DBG (127, "sane_read called, maxlen=%d\n", max_length); if ( ! started ) { return SANE_STATUS_INVAL; } if (dc25_opt_thumbnails) { if (total_bytes_read == THUMBSIZE) { if (dc25_opt_erase || dc25_opt_erase_one) { if (erase (tfd) == -1) { DBG (1, "Failed to erase memory\n"); return SANE_STATUS_INVAL; } dc25_opt_erase = SANE_FALSE; dc25_opt_erase_one = SANE_FALSE; info_flags |= SANE_INFO_RELOAD_OPTIONS; if (get_info (tfd) == NULL) { DBG (2, "error: could not get info\n"); close_dc20 (tfd); return SANE_STATUS_INVAL; } DBG (10, "Call get_info!, image range=%d,%d\n", image_range.min, image_range.max); } return SANE_STATUS_EOF; } *length = 0; if (!(bytes_in_buffer - bytes_read_from_buffer)) { if (read_data (tfd, buffer, 1024) == -1) { DBG (5, "sane_read: read_data failed\n"); return SANE_STATUS_INVAL; } bytes_in_buffer = 1024; bytes_read_from_buffer = 0; } while (bytes_read_from_buffer < bytes_in_buffer && max_length && total_bytes_read < THUMBSIZE) { *data++ = buffer[bytes_read_from_buffer++]; (*length)++; max_length--; total_bytes_read++; } if (total_bytes_read == THUMBSIZE) { if (end_of_data (tfd) == -1) { DBG (4, "sane_read: end_of_data error\n"); return SANE_STATUS_INVAL; } else { return SANE_STATUS_GOOD; } } else { return SANE_STATUS_GOOD; } } else { int i; int filesize = parms.bytes_per_line * parms.lines; /* * If outbytes is zero, then this is the first time * we've been called, so update the contrast table. * The formula is something I came up with that has the * following properties: * 1) It's a smooth curve that provides the effect I wanted * (bright pixels are made brighter, dim pixels are made * dimmer) * 2) The contrast parameter can be adjusted to provide * different amounts of contrast. * 3) A parameter of 1.0 can be used to pass the data * through unchanged (but values around 1.75 look * a lot better */ if (outbytes == 0) { double d; double cont = SANE_UNFIX (dc25_opt_contrast); for (i = 0; i < 256; i++) { d = (i * 2.0) / 255 - 1.0; d = ((-pow (1 - d, cont)) + 1) * (d >= 0) + (((pow (d + 1, cont)) - 1)) * (d < 0); contrast_table[i] = (d * 127.5) + 127.5; /* fprintf (stderr,"%03d %03d\n",i,contrast_table[i]); */ } } /* We're done, so return EOF */ if (outbytes >= filesize) { free_pixmap (pp); pp = NULL; if (dc25_opt_erase || dc25_opt_erase_one) { if (erase (tfd) == -1) { DBG (1, "Failed to erase memory\n"); return SANE_STATUS_INVAL; } } if (get_info (tfd) == NULL) { DBG (2, "error: could not get info\n"); close_dc20 (tfd); return SANE_STATUS_INVAL; } DBG (10, "Call get_info!, image range=%d,%d\n", image_range.min, image_range.max); get_info (tfd); *length=0; return SANE_STATUS_EOF; } if (max_length > filesize - outbytes) { *length = filesize - outbytes; } else { *length = max_length; } memcpy (data, pp->planes + outbytes, *length); outbytes += *length; for (i = 0; i < *length; i++) { data[i] = contrast_table[data[i]]; } return SANE_STATUS_GOOD; } } void sane_cancel (SANE_Handle __sane_unused__ handle) { DBG (127, "sane_cancel() called\n"); started = SANE_FALSE; } SANE_Status sane_set_io_mode (SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking) { /* sane_set_io_mode() is only valid during a scan */ if (started) { if (non_blocking == SANE_FALSE) { return SANE_STATUS_GOOD; } else { return SANE_STATUS_UNSUPPORTED; } } else { /* We aren't currently scanning */ return SANE_STATUS_INVAL; } } SANE_Status sane_get_select_fd (SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ * fd) { return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/dc25.conf.in000066400000000000000000000012771456256263500167120ustar00rootroot00000000000000# 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 backends-1.3.0/backend/dc25.h000066400000000000000000000172271456256263500156110ustar00rootroot00000000000000/*************************************************************************** * SANE - Scanner Access Now Easy. dc25.h 6/1/98 This file (C) 1998 Peter Fales 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, see . 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 the Kodak DC-25 (and probably the DC-20) digital cameras. THIS IS EXTREMELY ALPHA CODE! USE AT YOUR OWN RISK!! (feedback to: dc25-devel@fales-lorenz.net) This backend is based heavily on the dc20ctrl package by Ugo Paternostro . I've attached his header below: *************************************************************************** * Copyright (C) 1998 Ugo Paternostro * * This file is part of the dc20ctrl package. The complete package can be * downloaded from: * http://aguirre.dsi.unifi.it/~paterno/binaries/dc20ctrl.tar.gz * * This package is derived from the dc20 package, built by Karl Hakimian * that you can find it at ftp.eecs.wsu.edu in the * /pub/hakimian directory. The complete URL is: * ftp://ftp.eecs.wsu.edu/pub/hakimian/dc20.tar.gz * * This package also includes a slightly modified version of the Comet to ppm * conversion routine written by YOSHIDA Hideki * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #include #include #include #include #include #include #ifndef TRUE #define TRUE (1==1) #endif #ifndef FALSE #define FALSE (!TRUE) #endif #ifndef NULL #define NULL 0L #endif typedef struct dc20_info_s { unsigned char model; unsigned char ver_major; unsigned char ver_minor; int pic_taken; int pic_left; struct { unsigned int low_res:1; unsigned int low_batt:1; } flags; } Dc20Info, *Dc20InfoPtr; static Dc20Info *get_info (int); #define INIT_PCK {0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A} /* ^^^^^^^^^^ * Baud rate: (see pkt_speed structure) * 0x96 0x00 -> 9600 baud * 0x19 0x20 -> 19200 baud * 0x38 0x40 -> 38400 baud * 0x57 0x60 -> 57600 baud * 0x11 0x52 -> 115200 baud */ #define INFO_PCK {0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A} #define SHOOT_PCK {0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A} #define ERASE_PCK {0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A} #define RES_PCK {0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A} /* ^^^^ * Resolution: 0x00 = high, 0x01 = low */ #define THUMBS_PCK {0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A} /* ^^^^ * Thumbnail number */ #define PICS_PCK {0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A} /* ^^^^ * Picture number */ struct pkt_speed { speed_t baud; unsigned char pkt_code[2]; }; #define DEFAULT_TTY_BAUD B38400 #define HIGH_RES 0 #define LOW_RES 1 /* * Image parameters */ #define LOW_CAMERA_HEADER 256 #define HIGH_CAMERA_HEADER 512 #define CAMERA_HEADER(r) ( (r) ? LOW_CAMERA_HEADER : HIGH_CAMERA_HEADER ) #define LOW_WIDTH 256 #define HIGH_WIDTH 512 #define WIDTH(r) ( (r) ? LOW_WIDTH : HIGH_WIDTH ) #define HEIGHT 243 #define LEFT_MARGIN 1 #define LOW_RIGHT_MARGIN 5 #define HIGH_RIGHT_MARGIN 10 #define RIGHT_MARGIN(r) ( (r) ? LOW_RIGHT_MARGIN : HIGH_RIGHT_MARGIN ) #define TOP_MARGIN 1 #define BOTTOM_MARGIN 1 #define BLOCK_SIZE 1024 #define LOW_BLOCKS 61 #define HIGH_BLOCKS 122 #define BLOCKS(r) ( (r) ? LOW_BLOCKS : HIGH_BLOCKS ) #define LOW_IMAGE_SIZE ( LOW_BLOCKS * BLOCK_SIZE ) #define HIGH_IMAGE_SIZE ( HIGH_BLOCKS * BLOCK_SIZE ) #define IMAGE_SIZE(r) ( (r) ? LOW_IMAGE_SIZE : HIGH_IMAGE_SIZE ) #define MAX_IMAGE_SIZE ( HIGH_IMAGE_SIZE ) /* * Comet file */ #define COMET_MAGIC "COMET" #define COMET_HEADER_SIZE 128 #define COMET_EXT "cmt" /* * Pixmap structure */ struct pixmap { int width; int height; int components; unsigned char *planes; }; /* * Rotations */ #define ROT_STRAIGHT 0x00 #define ROT_LEFT 0x01 #define ROT_RIGHT 0x02 #define ROT_HEADDOWN 0x03 #define ROT_MASK 0x03 /* * File formats */ #define SAVE_RAW 0x01 #define SAVE_GREYSCALE 0x02 #define SAVE_24BITS 0x04 #define SAVE_FILES 0x07 #define SAVE_FORMATS 0x38 #define SAVE_ADJASPECT 0x80 /* * External definitions */ extern char *__progname; /* Defined in /usr/lib/crt0.o */ #include FILE * sanei_config_open (const char *filename); char *sanei_config_read (char *str, int n, FILE * stream); static int init_dc20 (char *, speed_t); static void close_dc20 (int); static int read_data (int fd, unsigned char *buf, int sz); static int end_of_data (int fd); static int set_pixel_rgb (struct pixmap *, int, int, unsigned char, unsigned char, unsigned char); static struct pixmap *alloc_pixmap (int x, int y, int d); static void free_pixmap (struct pixmap *p); static int zoom_x (struct pixmap *source, struct pixmap *dest); static int zoom_y (struct pixmap *source, struct pixmap *dest); static int comet_to_pixmap (unsigned char *, struct pixmap *); backends-1.3.0/backend/dell1600n_net.c000066400000000000000000001646041456256263500173240ustar00rootroot00000000000000/* 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, see . 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. 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/sane/sanei_config.h" #include #include #include #include #include /* :NOTE: these are likely to be platform-specific! */ #include #include #include #include #include #include #include /* OS/2... */ #ifndef SHUT_RDWR #define SHUT_RDWR 2 #endif /*********************************************************** * DEFINITIONS ***********************************************************/ /* Maximum number of scanners */ #define MAX_SCANNERS 32 /* version number */ #define DRIVER_VERSION SANE_VERSION_CODE( SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0 ) /* size of buffer for socket communication */ #define SOCK_BUF_SIZE 2048 /* size of registation name */ #define REG_NAME_SIZE 64 struct DeviceRecord { SANE_Device m_device; char * m_pName; /* storage of name */ char * m_pModel; /* storage of model */ }; /* 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 cancellation */ 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 DeviceRecord pointer on success (caller frees), NULL on failure */ static struct DeviceRecord *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 struct DeviceRecord *gKnownDevices[MAX_SCANNERS]; /* Empty list for when network devices are not wanted */ static const SANE_Device *gEmptyDeviceList[1]; /* 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 local_only) { int ret; unsigned char sockBuf[SOCK_BUF_SIZE]; int sock, optYes; struct DeviceRecord *pDevice; struct ComBuf queryPacket; struct sockaddr_in remoteAddr; unsigned char ucVal; fd_set readFds; struct timeval selTimeVal; int nread, iNextDevice; FILE *fConfig; char configBuf[ 256 ]; const char *pVal; int valLen; if (local_only) { *device_list = gEmptyDeviceList; return SANE_STATUS_GOOD; } /* init variables */ ret = SANE_STATUS_GOOD; sock = 0; pDevice = NULL; optYes = 1; InitComBuf (&queryPacket); /* clear previous results */ ClearKnownDevices (); iNextDevice = 0; /* look for a config file */ fConfig = sanei_config_open( "dell1600n_net.conf" ); if ( fConfig ) { while ( ! feof( fConfig ) ) { if ( ! sanei_config_read ( configBuf, sizeof( configBuf ), fConfig ) ) break; /* skip whitespace */ pVal = sanei_config_skip_whitespace ( configBuf ); /* skip comments */ if ( *pVal == '#' ) continue; /* process named_scanner */ valLen = strlen( "named_scanner:" ); if ( ! strncmp( pVal, "extra_scanner:", valLen ) ){ pVal = sanei_config_skip_whitespace ( pVal + valLen ); pDevice = malloc (sizeof (struct DeviceRecord)); if (!pDevice) { DBG (1, "sane_get_devices: memory allocation failure\n"); break; } pDevice->m_pName = strdup (pVal); pDevice->m_device.vendor = "Dell"; pDevice->m_pModel = strdup( "1600n" ); pDevice->m_device.type = "multi-function peripheral"; pDevice->m_device.name = pDevice->m_pName; pDevice->m_device.model = pDevice->m_pModel; /* add to list */ gKnownDevices[iNextDevice++] = pDevice; continue; } /* if */ } /* while */ /* Close the file */ fclose( fConfig ); } /* 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, (struct sockaddr *) &remoteAddr, sizeof (remoteAddr)) == -1) { DBG (1, "Error sending broadcast packet\n"); ret = SANE_STATUS_NO_MEM; goto cleanup; } /* process replies */ 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) (unsigned long)iHandle; return status; cleanup: if (iHandle != -1) FreeScannerState (iHandle); return status; } /* sane_open */ /***********************************************************/ void sane_close (SANE_Handle handle) { DBG( 5, "sane_close: %lx\n", (unsigned long)handle ); FreeScannerState ((unsigned long) 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) (unsigned long)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: %lu, image size: %d\n", iHandle, (unsigned long)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; iHandle = (int) (unsigned long)handle; DBG( 5, "sane_start: %x\n", iHandle ); /* fetch and check scanner index */ if (!ValidScannerNumber (iHandle)) return SANE_STATUS_INVAL; /* check if we still have outstanding 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, (struct sockaddr *) &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) (unsigned long)handle; int dataSize; struct PageInfo pageInfo; DBG( 5, "sane_read: %x (max_length=%d)\n", iHandle, 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, %lu total remaining, image: %dx%d\n", dataSize, gOpenScanners[iHandle]->m_bytesRead, pageInfo.m_bytesRemaining , (unsigned long)(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) (unsigned long)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]->m_pName) free ( gKnownDevices[i]->m_pName ); if (gKnownDevices[i]->m_pModel) free ( gKnownDevices[i]->m_pModel ); 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; size_t lineBufFree; char itemBuf[16] = { 0 }, lineBuf[256] = { 0 }; if (DBG_LEVEL < debugLevel) return; for (i = 0; i < bufSize; ++i) { if (!(i % 16)) sprintf (lineBuf, "%p: ", (void *) &buf[i]); sprintf (itemBuf, "%02x ", (const unsigned int) buf[i]); lineBufFree = sizeof (lineBuf) - strlen (lineBuf) - 1; strncat (lineBuf, itemBuf, lineBufFree); 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, "."); } lineBufFree = sizeof (lineBuf) - strlen (lineBuf) - 1; strncat (lineBuf, itemBuf, lineBufFree); } /* for j */ DBG (debugLevel, "%s\n", lineBuf); lineBuf[0] = 0; } /* for i */ if (i % 16) { for (j = (i % 16); j < 16; ++j) { lineBufFree = sizeof (lineBuf) - strlen (lineBuf) - 1; strncat (lineBuf, " ", lineBufFree); } 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, "."); } lineBufFree = sizeof (lineBuf) - strlen (lineBuf) - 1; strncat (lineBuf, itemBuf, lineBufFree); } 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 struct DeviceRecord pointer on success (caller frees), NULL on failure */ struct DeviceRecord * ProcessFindResponse (unsigned char *pData, size_t size) { struct DeviceRecord *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 %lu bytes, pData=%p\n", (unsigned long) size, (void *) 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 (struct DeviceRecord)); 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->m_pName = strdup( printerName ); pDevice->m_device.vendor = "Dell"; pDevice->m_pModel = strdup (pModel); pDevice->m_device.type = "multi-function peripheral"; pDevice->m_device.name = pDevice->m_pName; pDevice->m_device.model = pDevice->m_pModel; 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; char sockBuf[SOCK_BUF_SIZE], *pName; struct ComBuf tcpBuf; int nread; unsigned int numUsed; HexDump (15, pData, size); DBG (10, "ProcessUdpResponse: processing %lu bytes, pData=%p\n", (unsigned long) size, (void *) 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; 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 %lu bytes, pData=%p\n", (unsigned long) pTcpBuf->m_used, (void *) 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); /* 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); /* 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 %lu bytes of scan data so far\n", (unsigned long)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 successful, >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) { 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_t *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, (unsigned char*)& 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 */ TIFFSetField (pTiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); 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, (unsigned char*)& 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 */ { DBG (1, "ProcessPageData: Unexpected compression flag %d\n", ntohl (pState->m_compression)); 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 */ /***********************************************************/ backends-1.3.0/backend/dell1600n_net.conf.in000066400000000000000000000007521456256263500204250ustar00rootroot00000000000000# An example config file for dell1600n_net backend. # # Jon Chambers , 2006-08-12 # # For scanner(s) not detected by the default UDP broadcast method (eg: perhaps it # is not on your local network) then you can add one or more "named_scanner" entries # explicitly giving the hostname/IP address. Uncomment one of the following and # modify the hostname appropriately: # #named_scanner: printer #named_scanner: 192.168.0.20 #named_scanner: myscanner.somewhere.else.org backends-1.3.0/backend/dll.aliases000066400000000000000000000002551456256263500170120ustar00rootroot00000000000000# List of aliased or hiddend backends. See sane-dll(5) for details. # # Format: # alias SomeName SaneDeviceName # alias "Some Name" SaneDeviceName # hide SaneDeviceName backends-1.3.0/backend/dll.c000066400000000000000000001116261456256263500156200ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1996, 1997 David Mosberger-Tang 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, see . 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 dynamic linking based SANE meta backend. It allows managing an arbitrary number of SANE backends by using dynamic linking to load backends on demand. */ /* Please increase version number with every change (don't forget to update dll.desc) */ #define DLL_VERSION "1.0.13" #ifdef _AIX # include "lalloca.h" /* MUST come first for AIX! */ #endif #ifdef __BEOS__ #include #include #include #include #endif #include "../include/sane/config.h" #include "lalloca.h" #include #include #include #include #include #if defined(HAVE_DLOPEN) && defined(HAVE_DLFCN_H) # include /* This works around a pedantic GCC compiler warning. The ISO C standard says that the behaviour of converting an object pointer like the void * returned by dlsym() to a function pointer like void *(*)() is implementation defined. POSIX though guarantees that this works fine. Workaround based on http://stackoverflow.com/a/36385690. Turns off pedantic warnings for the duration of the definition only. */ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wpedantic" typedef void *(*func_ptr)(void); func_ptr posix_dlsym (void *handle, const char *func) { return dlsym (handle, func); } # pragma GCC diagnostic pop /* Similar to the above, GCC also warns about conversion between pointers to functions. The ISO C standard says that invoking a converted pointer to a function whose type is not compatible with the pointed-to type, the behavior is undefined. Although GCC is correct to warn about this, the dll backend has been using these conversions without issues for a very long time already. Rather than push/pop around every use, which would get very ugly real fast, ignore this particular warning for the remainder of the file. */ # pragma GCC diagnostic ignored "-Wpragmas" /* backward compatibility */ # pragma GCC diagnostic ignored "-Wcast-function-type" /* Older versions of dlopen() don't define RTLD_NOW and RTLD_LAZY. They all seem to use a mode of 1 to indicate RTLD_NOW and some do not support RTLD_LAZY at all. Hence, unless defined, we define both macros as 1 to play it safe. */ # ifndef RTLD_NOW # define RTLD_NOW 1 # endif # ifndef RTLD_LAZY # define RTLD_LAZY 1 # endif # define HAVE_DLL #endif /* HP/UX DLL support */ #if defined (HAVE_SHL_LOAD) && defined(HAVE_DL_H) # include # define HAVE_DLL #endif /* Mac OS X/Darwin support */ #if defined (HAVE_NSLINKMODULE) && defined(HAVE_MACH_O_DYLD_H) # include # define HAVE_DLL #endif #include #include #include #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #define BACKEND_NAME dll #include "../include/sane/sanei_backend.h" #ifndef PATH_MAX # define PATH_MAX 1024 #endif #ifndef NAME_MAX # define NAME_MAX FILENAME_MAX #endif #if defined(_WIN32) || defined(HAVE_OS2_H) # define DIR_SEP ";" #else # define DIR_SEP ":" #endif #include "../include/sane/sanei_config.h" #define DLL_CONFIG_FILE "dll.conf" #define DLL_ALIASES_FILE "dll.aliases" #include "../include/sane/sanei_usb.h" enum SANE_Ops { OP_INIT = 0, OP_EXIT, OP_GET_DEVS, OP_OPEN, OP_CLOSE, OP_GET_OPTION_DESC, OP_CTL_OPTION, OP_GET_PARAMS, OP_START, OP_READ, OP_CANCEL, OP_SET_IO_MODE, OP_GET_SELECT_FD, NUM_OPS }; typedef SANE_Status (*op_init_t) (SANE_Int *, SANE_Auth_Callback); typedef void (*op_exit_t) (void); typedef SANE_Status (*op_get_devs_t) (const SANE_Device ***, SANE_Bool); typedef SANE_Status (*op_open_t) (SANE_String_Const, SANE_Handle *); typedef void (*op_close_t) (SANE_Handle); typedef const SANE_Option_Descriptor * (*op_get_option_desc_t) (SANE_Handle, SANE_Int); typedef SANE_Status (*op_ctl_option_t) (SANE_Handle, SANE_Int, SANE_Action, void *, SANE_Int *); typedef SANE_Status (*op_get_params_t) (SANE_Handle, SANE_Parameters *); typedef SANE_Status (*op_start_t) (SANE_Handle); typedef SANE_Status (*op_read_t) (SANE_Handle, SANE_Byte *, SANE_Int, SANE_Int *); typedef void (*op_cancel_t) (SANE_Handle); typedef SANE_Status (*op_set_io_mode_t) (SANE_Handle, SANE_Bool); typedef SANE_Status (*op_get_select_fd_t) (SANE_Handle, SANE_Int *); struct backend { struct backend *next; char *name; u_int permanent:1; /* is the backend preloaded? */ u_int loaded:1; /* are the functions available? */ u_int inited:1; /* has the backend been initialized? */ void *handle; /* handle returned by dlopen() */ void *(*op[NUM_OPS]) (void); }; #define BE_ENTRY(be,func) sane_##be##_##func #define PRELOAD_DECL(name) \ extern SANE_Status BE_ENTRY(name,init) (SANE_Int *, SANE_Auth_Callback); \ extern void BE_ENTRY(name,exit) (void); \ extern SANE_Status BE_ENTRY(name,get_devices) (const SANE_Device ***, SANE_Bool); \ extern SANE_Status BE_ENTRY(name,open) (SANE_String_Const, SANE_Handle *); \ extern void BE_ENTRY(name,close) (SANE_Handle); \ extern const SANE_Option_Descriptor *BE_ENTRY(name,get_option_descriptor) (SANE_Handle, SANE_Int); \ extern SANE_Status BE_ENTRY(name,control_option) (SANE_Handle, SANE_Int, SANE_Action, void *, SANE_Int *); \ extern SANE_Status BE_ENTRY(name,get_parameters) (SANE_Handle, SANE_Parameters *); \ extern SANE_Status BE_ENTRY(name,start) (SANE_Handle); \ extern SANE_Status BE_ENTRY(name,read) (SANE_Handle, SANE_Byte *, SANE_Int, SANE_Int *); \ extern void BE_ENTRY(name,cancel) (SANE_Handle); \ extern SANE_Status BE_ENTRY(name,set_io_mode) (SANE_Handle, SANE_Bool); \ extern SANE_Status BE_ENTRY(name,get_select_fd) (SANE_Handle, SANE_Int *); #define PRELOAD_DEFN(name) \ { \ 0 /* next */, #name, \ 1 /* permanent */, \ 1 /* loaded */, \ 0 /* inited */, \ 0 /* handle */, \ { \ BE_ENTRY(name,init), \ BE_ENTRY(name,exit), \ BE_ENTRY(name,get_devices), \ BE_ENTRY(name,open), \ BE_ENTRY(name,close), \ BE_ENTRY(name,get_option_descriptor), \ BE_ENTRY(name,control_option), \ BE_ENTRY(name,get_parameters), \ BE_ENTRY(name,start), \ BE_ENTRY(name,read), \ BE_ENTRY(name,cancel), \ BE_ENTRY(name,set_io_mode), \ BE_ENTRY(name,get_select_fd) \ } \ } #ifndef __BEOS__ #ifdef ENABLE_PRELOAD #include "dll-preload.h" #else static struct backend preloaded_backends[] = { { 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }} }; #endif #endif struct meta_scanner { struct backend *be; SANE_Handle handle; }; struct alias { struct alias *next; char *oldname; char *newname; }; /* * List of available devices, allocated by sane_get_devices, released * by sane_exit() */ static SANE_Device **devlist = NULL; static int devlist_size = 0, devlist_len = 0; static struct alias *first_alias; static SANE_Auth_Callback auth_callback; static struct backend *first_backend; #ifndef __BEOS__ static const char *op_name[] = { "init", "exit", "get_devices", "open", "close", "get_option_descriptor", "control_option", "get_parameters", "start", "read", "cancel", "set_io_mode", "get_select_fd" }; #else static const char *op_name[] = { "sane_init", "sane_exit", "sane_get_devices", "sane_open", "sane_close", "sane_get_option_descriptor", "sane_control_option", "sane_get_parameters", "sane_start", "sane_read", "sane_cancel", "sane_set_io_mode", "sane_get_select_fd" }; #endif /* __BEOS__ */ static void * op_unsupported (void) { DBG (1, "op_unsupported: call to unsupported backend operation\n"); return (void *) (long) SANE_STATUS_UNSUPPORTED; } static SANE_Status add_backend (const char *name, struct backend **bep) { struct backend *be, *prev; DBG (3, "add_backend: adding backend `%s'\n", name); if (strcmp (name, "dll") == 0) { DBG (0, "add_backend: remove the dll-backend from your dll.conf!\n"); return SANE_STATUS_GOOD; } for (prev = 0, be = first_backend; be; prev = be, be = be->next) if (strcmp (be->name, name) == 0) { DBG (1, "add_backend: `%s' is already there\n", name); /* move to front so we preserve order that we'd get with dynamic loading: */ if (prev) { prev->next = be->next; be->next = first_backend; first_backend = be; } if (bep) *bep = be; return SANE_STATUS_GOOD; } be = calloc (1, sizeof (*be)); if (!be) return SANE_STATUS_NO_MEM; be->name = strdup (name); if (!be->name) return SANE_STATUS_NO_MEM; be->next = first_backend; first_backend = be; if (bep) *bep = be; return SANE_STATUS_GOOD; } #if defined(HAVE_NSLINKMODULE) static const char *dyld_get_error_str (); static const char * dyld_get_error_str () { NSLinkEditErrors c; int errorNumber; const char *fileName; const char *errorString; NSLinkEditError (&c, &errorNumber, &fileName, &errorString); return errorString; } #endif #ifdef __BEOS__ #include static SANE_Status load (struct backend *be) { /* 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 */ for (w = 0; (w < 3) && (id < 0) && (find_directory(which[w],0,true,path,PATH_MAX) == 0); w++) { strcat(path,"/SANE/"); strcat(path,be->name); DBG(1, "loading backend %s\n", be->name); /* initialize all ops to "unsupported" so we can "use" the backend even if the stuff later in this function fails */ be->loaded = 1; be->handle = 0; for (i = 0; i < NUM_OPS; ++i) be->op[i] = op_unsupported; DBG(2, "dlopen()ing `%s'\n", path); id=load_add_on(path); if (id < 0) { continue; /* try next path */ } be->handle=(void *)id; for (i = 0; i < NUM_OPS; ++i) { void *(*op) (); op = NULL; /* Look for the symbol */ if ((get_image_symbol(id, op_name[i],B_SYMBOL_TYPE_TEXT,(void **)&op) < 0) || !op) DBG(2, "unable to find %s\n", op_name[i]); else be->op[i]=op; } } if (id < 0) { DBG(2, "load: couldn't find %s\n",path); return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } #else static SANE_Status load (struct backend *be) { #ifdef HAVE_DLL int mode = 0; char *funcname, *src, *orig_src = 0, *dir, *path = 0; char libname[PATH_MAX]; int i; int src_len; FILE *fp = 0; #if defined(HAVE_DLOPEN) # define PREFIX "libsane-" # ifdef __hpux # define POSTFIX ".sl.%u" # define ALT_POSTFIX ".so.%u" # elif defined (HAVE_WINDOWS_H) # undef PREFIX # define PREFIX "cygsane-" # define POSTFIX "-%u.dll" # elif defined (HAVE_OS2_H) # undef PREFIX # define PREFIX "" # define POSTFIX ".dll" # elif defined (__APPLE__) && defined (__MACH__) # define POSTFIX ".%u.so" # else # define POSTFIX ".so.%u" # endif mode = getenv ("LD_BIND_NOW") ? RTLD_NOW : RTLD_LAZY; #elif defined(HAVE_SHL_LOAD) # define PREFIX "libsane-" # define POSTFIX ".sl.%u" mode = BIND_DEFERRED; #elif defined(HAVE_NSLINKMODULE) # define PREFIX "libsane-" # define POSTFIX ".%u.so" mode = NSLINKMODULE_OPTION_RETURN_ON_ERROR + NSLINKMODULE_OPTION_PRIVATE; #else # error "Tried to compile unsupported DLL." #endif /* HAVE_DLOPEN */ /* initialize all ops to "unsupported" so we can "use" the backend even if the stuff later in this function fails */ be->loaded = 1; be->handle = 0; for (i = 0; i < NUM_OPS; ++i) be->op[i] = op_unsupported; path = getenv ("LD_LIBRARY_PATH"); if (!path) path = getenv ("SHLIB_PATH"); /* for HP-UX */ if (!path) path = getenv ("LIBPATH"); /* for AIX */ if (path) { src_len = strlen (path) + strlen (DIR_SEP) + strlen(LIBDIR) + 1; src = malloc (src_len); if (!src) { DBG (1, "load: malloc failed: %s\n", strerror (errno)); return SANE_STATUS_NO_MEM; } snprintf (src, src_len, "%s%s%s", path, DIR_SEP, LIBDIR); } else { src = LIBDIR; src = strdup (src); if (!src) { DBG (1, "load: strdup failed: %s\n", strerror (errno)); return SANE_STATUS_NO_MEM; } } DBG (3, "load: searching backend `%s' in `%s'\n", be->name, src); orig_src = src; dir = strsep (&src, DIR_SEP); while (dir) { #ifdef HAVE_OS2_H /* only max 7.3 names work with dlopen() for DLLs on OS/2 */ snprintf (libname, sizeof (libname), "%s/" PREFIX "%.2s%.5s" POSTFIX, dir, be->name, strlen(be->name)>7 ? (be->name)+strlen(be->name)-5 : (be->name)+2, V_MAJOR); #else snprintf (libname, sizeof (libname), "%s/" PREFIX "%s" POSTFIX, dir, be->name, V_MAJOR); #endif 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)); #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) free (orig_src); if (!fp) { DBG (1, "load: couldn't find backend `%s' (%s)\n", be->name, strerror (errno)); return SANE_STATUS_INVAL; } fclose (fp); DBG (3, "load: dlopen()ing `%s'\n", libname); #ifdef HAVE_DLOPEN be->handle = dlopen (libname, mode); #elif defined(HAVE_SHL_LOAD) be->handle = (shl_t) shl_load (libname, mode, 0L); #elif defined(HAVE_NSLINKMODULE) { NSObjectFileImage objectfile_img = NULL; if (NSCreateObjectFileImageFromFile (libname, &objectfile_img) == NSObjectFileImageSuccess) { be->handle = NSLinkModule (objectfile_img, libname, mode); NSDestroyObjectFileImage (objectfile_img); } } #else # error "Tried to compile unsupported DLL." #endif /* HAVE_DLOPEN */ if (!be->handle) { #ifdef HAVE_DLOPEN DBG (1, "load: dlopen() failed (%s)\n", dlerror ()); #elif defined(HAVE_NSLINKMODULE) DBG (1, "load: dyld error (%s)\n", dyld_get_error_str ()); #else DBG (1, "load: dlopen() failed (%s)\n", strerror (errno)); #endif return SANE_STATUS_INVAL; } /* all is dandy---lookup and fill in backend ops: */ funcname = alloca (strlen (be->name) + 64); for (i = 0; i < NUM_OPS; ++i) { void *(*op) (void); sprintf (funcname, "_sane_%s_%s", be->name, op_name[i]); /* First try looking up the symbol without a leading underscore. */ #ifdef HAVE_DLOPEN op = posix_dlsym (be->handle, funcname + 1); #elif defined(HAVE_SHL_LOAD) shl_findsym ((shl_t *) & (be->handle), funcname + 1, TYPE_UNDEFINED, &op); #elif defined(HAVE_NSLINKMODULE) { NSSymbol *nssym = NSLookupSymbolInModule (be->handle, funcname); if (!nssym) { DBG (15, "dyld error: %s\n", dyld_get_error_str ()); } else { op = (void *(*)(void)) NSAddressOfSymbol (nssym); } } #else # error "Tried to compile unsupported DLL." #endif /* HAVE_DLOPEN */ if (op) be->op[i] = op; else { /* Try again, with an underscore prepended. */ #ifdef HAVE_DLOPEN op = posix_dlsym (be->handle, funcname); #elif defined(HAVE_SHL_LOAD) shl_findsym (be->handle, funcname, TYPE_UNDEFINED, &op); #elif defined(HAVE_NSLINKMODULE) { NSSymbol *nssym = NSLookupSymbolInModule (be->handle, funcname); if (!nssym) { DBG (15, "dyld error: %s\n", dyld_get_error_str ()); } else { op = (void *(*)(void)) NSAddressOfSymbol (nssym); } } #else # error "Tried to compile unsupported DLL." #endif /* HAVE_DLOPEN */ if (op) be->op[i] = op; } if (NULL == op) DBG (1, "load: unable to find %s\n", funcname); } return SANE_STATUS_GOOD; # undef PREFIX # undef POSTFIX #else /* HAVE_DLL */ DBG (1, "load: ignoring attempt to load `%s'; compiled without dl support\n", be->name); return SANE_STATUS_UNSUPPORTED; #endif /* HAVE_DLL */ } #endif /* __BEOS__ */ static SANE_Status init (struct backend *be) { SANE_Status status; SANE_Int version; if (!be->loaded) { status = load (be); if (status != SANE_STATUS_GOOD) return status; } DBG (3, "init: initializing backend `%s'\n", be->name); status = (*(op_init_t)be->op[OP_INIT]) (&version, auth_callback); if (status != SANE_STATUS_GOOD) return status; if (SANE_VERSION_MAJOR (version) != SANE_CURRENT_MAJOR) { DBG (1, "init: backend `%s' has a wrong major version (%d instead of %d)\n", be->name, SANE_VERSION_MAJOR (version), SANE_CURRENT_MAJOR); return SANE_STATUS_INVAL; } DBG (4, "init: backend `%s' is version %d.%d.%d\n", be->name, SANE_VERSION_MAJOR (version), SANE_VERSION_MINOR (version), SANE_VERSION_BUILD (version)); be->inited = 1; return SANE_STATUS_GOOD; } static void add_alias (const char *line_param) { #ifndef __BEOS__ const char *command; enum { CMD_ALIAS, CMD_HIDE } cmd; const char *oldname, *oldend, *newname; size_t oldlen, newlen; struct alias *alias; char *line; command = sanei_config_skip_whitespace (line_param); if (!*command) return; line = strchr (command, '#'); if (line) *line = '\0'; line = strpbrk (command, " \t"); if (!line) return; *line++ = '\0'; if (strcmp (command, "alias") == 0) cmd = CMD_ALIAS; else if (strcmp (command, "hide") == 0) cmd = CMD_HIDE; else return; newlen = 0; newname = NULL; if (cmd == CMD_ALIAS) { char *newend; newname = sanei_config_skip_whitespace (line); if (!*newname) return; if (*newname == '\"') { ++newname; newend = strchr (newname, '\"'); } else newend = strpbrk (newname, " \t"); if (!newend) return; newlen = newend - newname; line = (char *) (newend + 1); } oldname = sanei_config_skip_whitespace (line); if (!*oldname) return; oldend = oldname + strcspn (oldname, " \t"); oldlen = oldend - oldname; alias = malloc (sizeof (struct alias)); if (alias) { alias->oldname = malloc (oldlen + newlen + 2); if (alias->oldname) { strncpy (alias->oldname, oldname, oldlen); alias->oldname[oldlen] = '\0'; if (cmd == CMD_ALIAS) { alias->newname = alias->oldname + oldlen + 1; strncpy (alias->newname, newname, newlen); alias->newname[newlen] = '\0'; } else alias->newname = NULL; alias->next = first_alias; first_alias = alias; return; } free (alias); } return; #endif } static void read_config (const char *conffile) { FILE *fp; char config_line[PATH_MAX]; char *backend_name; fp = sanei_config_open (conffile); if (!fp) { DBG (1, "sane_init/read_config: Couldn't open config file (%s): %s\n", conffile, strerror (errno)); return; /* don't insist on config file */ } DBG (5, "sane_init/read_config: reading %s\n", conffile); while (sanei_config_read (config_line, sizeof (config_line), fp)) { char *comment; SANE_String_Const cp; cp = sanei_config_get_string (config_line, &backend_name); /* ignore empty lines */ if (!backend_name || cp == config_line) { if (backend_name) free (backend_name); continue; } /* ignore line comments */ if (backend_name[0] == '#') { free (backend_name); continue; } /* ignore comments after backend names */ comment = strchr (backend_name, '#'); if (comment) *comment = '\0'; add_backend (backend_name, 0); free (backend_name); } fclose (fp); } static void read_dlld (void) { DIR *dlld; struct dirent *dllconf; struct stat st; char dlldir[PATH_MAX]; char conffile[PATH_MAX + strlen("/") + NAME_MAX]; size_t len, plen; const char *dir_list; char *copy, *next, *dir; dir_list = sanei_config_get_paths (); if (!dir_list) { DBG(2, "sane_init/read_dlld: Unable to detect configuration directories\n"); return; } copy = strdup (dir_list); for (next = copy; (dir = strsep (&next, DIR_SEP)) != NULL;) { snprintf (dlldir, sizeof (dlldir), "%s%s", dir, "/dll.d"); DBG(4, "sane_init/read_dlld: attempting to open directory `%s'\n", dlldir); dlld = opendir (dlldir); if (dlld) { /* length of path to parent dir of dll.d/ */ plen = strlen (dir) + 1; DBG(3, "sane_init/read_dlld: using config directory `%s'\n", dlldir); break; } } free (copy); if (dlld == NULL) { DBG (1, "sane_init/read_dlld: opendir failed: %s\n", strerror (errno)); return; } while ((dllconf = readdir (dlld)) != NULL) { /* dotfile (or directory) */ if (dllconf->d_name[0] == '.') continue; len = strlen (dllconf->d_name); /* backup files */ if ((dllconf->d_name[len-1] == '~') || (dllconf->d_name[len-1] == '#')) continue; snprintf (conffile, sizeof(conffile), "%s/%s", dlldir, dllconf->d_name); DBG (5, "sane_init/read_dlld: considering %s\n", conffile); if (stat (conffile, &st) != 0) continue; if (!S_ISREG (st.st_mode)) continue; /* expects a path relative to PATH_SANE_CONFIG_DIR */ read_config (conffile+plen); } closedir (dlld); DBG (5, "sane_init/read_dlld: done.\n"); } SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { #ifndef __BEOS__ char config_line[PATH_MAX]; size_t len; FILE *fp; int i; #else DIR *dir; struct dirent *dirent; char path[1024]; directory_which which[3] = { B_USER_ADDONS_DIRECTORY, B_COMMON_ADDONS_DIRECTORY, B_BEOS_ADDONS_DIRECTORY }; int i; #endif DBG_INIT (); auth_callback = authorize; DBG (1, "sane_init: SANE dll backend version %s from %s\n", DLL_VERSION, PACKAGE_STRING); #ifndef __BEOS__ /* chain preloaded backends together: */ for (i = 0; i < NELEMS (preloaded_backends); ++i) { if (!preloaded_backends[i].name) continue; DBG (3, "sane_init: adding backend `%s' (preloaded)\n", preloaded_backends[i].name); preloaded_backends[i].next = first_backend; first_backend = &preloaded_backends[i]; } /* Return the version number of the sane-backends package to allow the frontend to print them. This is done only for net and dll, because these backends are usually called by the frontend. */ if (version_code) *version_code = SANE_VERSION_CODE (SANE_DLL_V_MAJOR, SANE_DLL_V_MINOR, SANE_DLL_V_BUILD); /* * Read dll.conf & dll.d * Read dll.d first, so that the extras backends will be tried last */ read_dlld (); read_config (DLL_CONFIG_FILE); fp = sanei_config_open (DLL_ALIASES_FILE); if (!fp) return SANE_STATUS_GOOD; /* don't insist on aliases file */ DBG (5, "sane_init: reading %s\n", DLL_ALIASES_FILE); while (sanei_config_read (config_line, sizeof (config_line), fp)) { if (config_line[0] == '#') /* ignore line comments */ continue; len = strlen (config_line); if (!len) continue; /* ignore empty lines */ add_alias (config_line); } fclose (fp); #else /* 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) continue; strcat(path,"/SANE/"); dir=opendir(path); if(!dir) continue; while((dirent=readdir(dir))) { if((strcmp(dirent->d_name,".")==0) || (strcmp(dirent->d_name,"..")==0)) continue; if((strcmp(dirent->d_name,"dll")==0)) continue; add_backend(dirent->d_name,0); } closedir(dir); } #endif /* __BEOS__ */ return SANE_STATUS_GOOD; } void sane_exit (void) { struct backend *be, *next; struct alias *alias; DBG (2, "sane_exit: exiting\n"); for (be = first_backend; be; be = next) { next = be->next; if (be->loaded) { if (be->inited) { DBG (3, "sane_exit: calling backend `%s's exit function\n", be->name); (*(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); #else #ifdef HAVE_DLL #ifdef HAVE_DLOPEN if (be->handle) dlclose (be->handle); #elif defined(HAVE_SHL_LOAD) if (be->handle) shl_unload (be->handle); #elif defined(HAVE_NSLINKMODULE) if (be->handle) NSUnLinkModule (be->handle, NSUNLINKMODULE_OPTION_NONE # ifdef __ppc__ | NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES # endif ); #else # error "Tried to compile unsupported DLL." #endif /* HAVE_DLOPEN */ #endif /* HAVE_DLL */ #endif /* __BEOS__ */ } if (!be->permanent) { if (be->name) free ((void *) be->name); free (be); } else { be->inited = 0; } } first_backend = 0; while ((alias = first_alias) != NULL) { first_alias = first_alias->next; free (alias->oldname); free (alias); } if (NULL != devlist) { /* Release memory allocated by sane_get_devices(). */ int i = 0; while (devlist[i]) free (devlist[i++]); free (devlist); devlist = NULL; devlist_size = 0; devlist_len = 0; } DBG (3, "sane_exit: finished\n"); } /* Note that a call to get_devices() implies that we'll have to load all backends. To avoid this, you can call sane_open() directly (assuming you know the name of the backend/device). This is appropriate for the command-line interface of SANE, for example. */ SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { const SANE_Device **be_list; struct backend *be; SANE_Status status; char *full_name; int i, num_devs; size_t len; #define ASSERT_SPACE(n) do \ { \ if (devlist_len + (n) > devlist_size) \ { \ devlist_size += (n) + 15; \ if (devlist) \ devlist = realloc (devlist, devlist_size * sizeof (devlist[0])); \ else \ devlist = malloc (devlist_size * sizeof (devlist[0])); \ if (!devlist) \ return SANE_STATUS_NO_MEM; \ } \ } while (0) DBG (3, "sane_get_devices\n"); if (devlist) for (i = 0; i < devlist_len; ++i) free ((void *) devlist[i]); devlist_len = 0; for (be = first_backend; be; be = be->next) { if (!be->inited) if (init (be) != SANE_STATUS_GOOD) continue; status = (*(op_get_devs_t)be->op[OP_GET_DEVS]) (&be_list, local_only); if (status != SANE_STATUS_GOOD || !be_list) continue; /* count the number of devices for this backend: */ for (num_devs = 0; be_list[num_devs]; ++num_devs); ASSERT_SPACE (num_devs); for (i = 0; i < num_devs; ++i) { SANE_Device *dev; char *mem; struct alias *alias; for (alias = first_alias; alias != NULL; alias = alias->next) { len = strlen (be->name); if (strlen (alias->oldname) <= len) continue; if (strncmp (alias->oldname, be->name, len) == 0 && alias->oldname[len] == ':' && strcmp (&alias->oldname[len + 1], be_list[i]->name) == 0) break; } if (alias) { if (!alias->newname) /* hidden device */ continue; len = strlen (alias->newname); mem = malloc (sizeof (*dev) + len + 1); if (!mem) return SANE_STATUS_NO_MEM; full_name = mem + sizeof (*dev); strcpy (full_name, alias->newname); } else { /* create a new device entry with a device name that is the sum of the backend name a colon and the backend's device name: */ len = strlen (be->name) + 1 + strlen (be_list[i]->name); mem = malloc (sizeof (*dev) + len + 1); if (!mem) return SANE_STATUS_NO_MEM; full_name = mem + sizeof (*dev); strcpy (full_name, be->name); strcat (full_name, ":"); strcat (full_name, be_list[i]->name); } dev = (SANE_Device *) mem; dev->name = full_name; dev->vendor = be_list[i]->vendor; dev->model = be_list[i]->model; dev->type = be_list[i]->type; devlist[devlist_len++] = dev; } } /* terminate device list with NULL entry: */ ASSERT_SPACE (1); devlist[devlist_len++] = 0; *device_list = (const SANE_Device **) devlist; DBG (3, "sane_get_devices: found %d devices\n", devlist_len - 1); return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const full_name, SANE_Handle * meta_handle) { char *be_name; const char *dev_name; struct meta_scanner *s; SANE_Handle handle; struct backend *be; SANE_Status status; struct alias *alias; DBG (3, "sane_open: trying to open `%s'\n", full_name); for (alias = first_alias; alias != NULL; alias = alias->next) { if (!alias->newname) continue; if (strcmp (alias->newname, full_name) == 0) { full_name = alias->oldname; break; } } dev_name = strchr (full_name, ':'); int is_fakeusb = 0, is_fakeusbdev = 0, is_fakeusbout = 0; if (dev_name) { is_fakeusb = strncmp(full_name, "fakeusb", dev_name - full_name) == 0 && dev_name - full_name == 7; is_fakeusbdev = strncmp(full_name, "fakeusbdev", dev_name - full_name) == 0 && dev_name - full_name == 10; is_fakeusbout = strncmp(full_name, "fakeusbout", dev_name - full_name) == 0 && dev_name - full_name == 10; } if (is_fakeusb || is_fakeusbdev) { ++dev_name; // skip colon status = sanei_usb_testing_enable_replay(dev_name, is_fakeusbdev); if (status != SANE_STATUS_GOOD) return status; be_name = sanei_usb_testing_get_backend(); if (be_name == NULL) { DBG (0, "%s: unknown backend for testing\n", __func__); return SANE_STATUS_ACCESS_DENIED; } } else { char* fakeusbout_path = NULL; if (is_fakeusbout) { ++dev_name; // skip colon const char* path_end = strchr(dev_name, ':'); if (path_end == NULL) { DBG (0, "%s: the device name does not contain path\n", __func__); return SANE_STATUS_INVAL; } fakeusbout_path = strndup(dev_name, path_end - dev_name); full_name = path_end + 1; // skip colon dev_name = strchr(full_name, ':'); } if (dev_name) { be_name = strndup(full_name, dev_name - full_name); ++dev_name; /* skip colon */ } else { /* if no colon interpret full_name as the backend name; an empty backend device name will cause us to open the first device of that backend. */ be_name = strdup(full_name); dev_name = ""; } if (is_fakeusbout) { status = sanei_usb_testing_enable_record(fakeusbout_path, be_name); free(fakeusbout_path); if (status != SANE_STATUS_GOOD) return status; } } if (!be_name) return SANE_STATUS_NO_MEM; if (!be_name[0]) be = first_backend; else for (be = first_backend; be; be = be->next) if (strcmp (be->name, be_name) == 0) break; if (!be) { status = add_backend (be_name, &be); if (status != SANE_STATUS_GOOD) { free(be_name); return status; } } free(be_name); if (!be->inited) { status = init (be); if (status != SANE_STATUS_GOOD) return status; } status = (*(op_open_t)be->op[OP_OPEN]) (dev_name, &handle); if (status != SANE_STATUS_GOOD) return status; s = calloc (1, sizeof (*s)); if (!s) return SANE_STATUS_NO_MEM; s->be = be; s->handle = handle; *meta_handle = s; DBG (3, "sane_open: open successful\n"); return SANE_STATUS_GOOD; } void sane_close (SANE_Handle handle) { struct meta_scanner *s = handle; DBG (3, "sane_close(handle=%p)\n", handle); (*(op_close_t)s->be->op[OP_CLOSE]) (s->handle); free (s); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { struct meta_scanner *s = handle; DBG (3, "sane_get_option_descriptor(handle=%p,option=%d)\n", handle, option); return (*(op_get_option_desc_t)s->be->op[OP_GET_OPTION_DESC]) (s->handle, option); } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Word * info) { struct meta_scanner *s = handle; DBG (3, "sane_control_option(handle=%p,option=%d,action=%d,value=%p,info=%p)\n", handle, option, action, value, (void *) info); return (*(op_ctl_option_t)s->be->op[OP_CTL_OPTION]) (s->handle, option, action, value, info); } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { struct meta_scanner *s = handle; DBG (3, "sane_get_parameters(handle=%p,params=%p)\n", handle, (void *) params); return (*(op_get_params_t)s->be->op[OP_GET_PARAMS]) (s->handle, params); } SANE_Status sane_start (SANE_Handle handle) { struct meta_scanner *s = handle; DBG (3, "sane_start(handle=%p)\n", handle); return (*(op_start_t)s->be->op[OP_START]) (s->handle); } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * data, SANE_Int max_length, SANE_Int * length) { struct meta_scanner *s = handle; DBG (3, "sane_read(handle=%p,data=%p,maxlen=%d,lenp=%p)\n", handle, (void *) data, max_length, (void *) length); return (*(op_read_t)s->be->op[OP_READ]) (s->handle, data, max_length, length); } void sane_cancel (SANE_Handle handle) { struct meta_scanner *s = handle; DBG (3, "sane_cancel(handle=%p)\n", handle); (*(op_cancel_t)s->be->op[OP_CANCEL]) (s->handle); } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { struct meta_scanner *s = handle; DBG (3, "sane_set_io_mode(handle=%p,nonblocking=%d)\n", handle, non_blocking); return (*(op_set_io_mode_t)s->be->op[OP_SET_IO_MODE]) (s->handle, non_blocking); } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { struct meta_scanner *s = handle; DBG (3, "sane_get_select_fd(handle=%p,fdp=%p)\n", handle, (void *) fd); return (*(op_get_select_fd_t)s->be->op[OP_GET_SELECT_FD]) (s->handle, fd); } backends-1.3.0/backend/dll.conf.in000066400000000000000000000021501456256263500167170ustar00rootroot00000000000000# dll.conf - Configuration file for the SANE dynamic backend loader # # Backends can also be enabled by configuration snippets under the dll.d/ # directory -- third party backends can drop their configuration file in # this in this directory, named after the backend. # # The next line enables the network backend; comment it out if you don't # need to use a remote SANE scanner over the network -- see sane-net(5) # and saned(8) for details. net abaton agfafocus apple artec artec_eplus48u as6e avision bh canon canon630u canon_dr canon_lide70 #canon_pp cardscan coolscan #coolscan2 coolscan3 #dc210 #dc240 #dc25 dell1600n_net dmc epjitsu #epson epson2 epsonds escl fujitsu genesys #gphoto2 gt68xx hp hp3500 hp3900 hp4200 hp5400 hp5590 hpljm1005 hpsj5s hs2p ibm kodak kodakaio kvs1025 kvs20xx kvs40xx leo lexmark ma1509 magicolor matsushita microtek microtek2 mustek #mustek_pp mustek_usb mustek_usb2 nec niash #p5 pie pieusb pint pixma plustek #plustek_pp #pnm qcam ricoh ricoh2 rts8891 s9036 sceptre sharp sm3600 sm3840 snapscan sp15c #st400 #stv680 tamarack teco1 teco2 teco3 #test u12 umax umax1220u #umax_pp v4l xerox_mfp backends-1.3.0/backend/dmc.c000066400000000000000000001227531456256263500156130ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1998 Dianne Skoll Heavily based on "hp.c" driver for HP Scanners, by David Mosberger-Tang. 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, see . 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 the Polaroid Digital Microscope Camera. */ #include "../include/sane/config.h" #include #include #include #include #include "../include/_stdint.h" #include "../include/sane/sane.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_scsi.h" #define BACKEND_NAME dmc #include "../include/sane/sanei_backend.h" #ifndef PATH_MAX # define PATH_MAX 1024 #endif #include "../include/sane/sanei_config.h" #define DMC_CONFIG_FILE "dmc.conf" #include "dmc.h" /* A linked-list of attached devices and handles */ static DMC_Device *FirstDevice = NULL; static DMC_Camera *FirstHandle = NULL; static int NumDevices = 0; static SANE_Device const **devlist = NULL; static SANE_String_Const ValidModes[] = { "Full frame", "Viewfinder", "Raw", "Thumbnail", "Super-Resolution", NULL }; static SANE_String_Const ValidBalances[] = { "Daylight", "Incandescent", "Fluorescent", NULL }; static SANE_Word ValidASAs[] = { 3, 25, 50, 100 }; /* Convert between 32-us ticks and milliseconds */ #define MS_TO_TICKS(x) (((x) * 1000 + 16) / 32) #define TICKS_TO_MS(x) (((x) * 32) / 1000) /* Macros for stepping along the raw lines for super-resolution mode They are very ugly because they handle boundary conditions at the edges of the image. Yuck... */ #define PREV_RED(i) (((i)/3)*3) #define NEXT_RED(i) (((i) >= BYTES_PER_RAW_LINE-3) ? BYTES_PER_RAW_LINE-3 : \ PREV_RED(i)+3) #define PREV_GREEN(i) ((i)<1 ? 1 : PREV_RED((i)-1)+1) #define NEXT_GREEN(i) ((i)<1 ? 1 : ((i) >= BYTES_PER_RAW_LINE-2) ? \ BYTES_PER_RAW_LINE-2 : PREV_GREEN(i)+3) #define PREV_BLUE(i) ((i)<2 ? 2 : PREV_RED((i)-2)+2) #define NEXT_BLUE(i) ((i)<2 ? 2 : ((i) >= BYTES_PER_RAW_LINE-1) ? \ BYTES_PER_RAW_LINE-1 : PREV_BLUE(i)+3) #define ADVANCE_COEFF(i) (((i)==1) ? 3 : (i)-1); /********************************************************************** //%FUNCTION: DMCRead //%ARGUMENTS: // fd -- file descriptor // typecode -- data type code // qualifier -- data type qualifier // maxlen -- transfer length // buf -- buffer to store data in // len -- set to actual length of data //%RETURNS: // A SANE status code //%DESCRIPTION: // Reads the particular data selected by typecode and qualifier // *********************************************************************/ static SANE_Status DMCRead(int fd, unsigned int typecode, unsigned int qualifier, SANE_Byte *buf, size_t maxlen, size_t *len) { uint8_t readCmd[10]; SANE_Status status; readCmd[0] = 0x28; readCmd[1] = 0; readCmd[2] = typecode; readCmd[3] = 0; readCmd[4] = (qualifier >> 8) & 0xFF; readCmd[5] = qualifier & 0xFF; readCmd[6] = (maxlen >> 16) & 0xFF; readCmd[7] = (maxlen >> 8) & 0xFF; readCmd[8] = maxlen & 0xFF; readCmd[9] = 0; DBG(3, "DMCRead: typecode=%x, qualifier=%x, maxlen=%lu\n", typecode, qualifier, (u_long) maxlen); *len = maxlen; status = sanei_scsi_cmd(fd, readCmd, sizeof(readCmd), buf, len); DBG(3, "DMCRead: Read %lu bytes\n", (u_long) *len); return status; } /********************************************************************** //%FUNCTION: DMCWrite //%ARGUMENTS: // fd -- file descriptor // typecode -- data type code // qualifier -- data type qualifier // maxlen -- transfer length // buf -- buffer to store data in //%RETURNS: // A SANE status code //%DESCRIPTION: // Writes the particular data selected by typecode and qualifier // *********************************************************************/ static SANE_Status DMCWrite(int fd, unsigned int typecode, unsigned int qualifier, SANE_Byte *buf, size_t maxlen) { uint8_t *writeCmd; SANE_Status status; writeCmd = malloc(maxlen + 10); if (!writeCmd) return SANE_STATUS_NO_MEM; writeCmd[0] = 0x2A; writeCmd[1] = 0; writeCmd[2] = typecode; writeCmd[3] = 0; writeCmd[4] = (qualifier >> 8) & 0xFF; writeCmd[5] = qualifier & 0xFF; writeCmd[6] = (maxlen >> 16) & 0xFF; writeCmd[7] = (maxlen >> 8) & 0xFF; writeCmd[8] = maxlen & 0xFF; writeCmd[9] = 0; memcpy(writeCmd+10, buf, maxlen); DBG(3, "DMCWrite: typecode=%x, qualifier=%x, maxlen=%lu\n", typecode, qualifier, (u_long) maxlen); status = sanei_scsi_cmd(fd, writeCmd, 10+maxlen, NULL, NULL); free(writeCmd); return status; } /********************************************************************** //%FUNCTION: DMCAttach //%ARGUMENTS: // devname -- name of device file to open // devp -- a DMC_Device structure which we fill in if it's not NULL. //%RETURNS: // SANE_STATUS_GOOD -- We have a Polaroid DMC attached and all looks good. // SANE_STATUS_INVAL -- There's a problem. //%DESCRIPTION: // Verifies that a Polaroid DMC is attached. Sets up device options in // DMC_Device structure. // *********************************************************************/ #define INQ_LEN 255 static SANE_Status DMCAttach(char const *devname, DMC_Device **devp) { DMC_Device *dev; SANE_Status status; int fd; size_t size; char result[INQ_LEN]; uint8_t exposureCalculationResults[16]; uint8_t userInterfaceSettings[16]; static uint8_t const inquiry[] = { 0x12, 0x00, 0x00, 0x00, INQ_LEN, 0x00 }; static uint8_t const test_unit_ready[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static uint8_t const no_viewfinder[] = { 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* If we're already attached, do nothing */ for (dev = FirstDevice; dev; dev = dev->next) { if (!strcmp(dev->sane.name, devname)) { if (devp) *devp = dev; return SANE_STATUS_GOOD; } } DBG(3, "DMCAttach: opening `%s'\n", devname); status = sanei_scsi_open(devname, &fd, 0, 0); if (status != SANE_STATUS_GOOD) { DBG(1, "DMCAttach: open failed (%s)\n", sane_strstatus(status)); return status; } DBG(3, "DMCAttach: sending INQUIRY\n"); size = sizeof(result); status = sanei_scsi_cmd(fd, inquiry, sizeof(inquiry), result, &size); if (status != SANE_STATUS_GOOD || size < 32) { if (status == SANE_STATUS_GOOD) status = SANE_STATUS_INVAL; DBG(1, "DMCAttach: inquiry failed (%s)\n", sane_strstatus(status)); sanei_scsi_close(fd); return status; } /* Verify that we have a Polaroid DMC */ if (result[0] != 6 || strncmp(result+8, "POLAROID", 8) || strncmp(result+16, "DMC ", 8)) { sanei_scsi_close(fd); DBG(1, "DMCAttach: Device does not look like a Polaroid DMC\n"); return SANE_STATUS_INVAL; } DBG(3, "DMCAttach: sending TEST_UNIT_READY\n"); status = sanei_scsi_cmd(fd, test_unit_ready, sizeof(test_unit_ready), NULL, NULL); if (status != SANE_STATUS_GOOD) { DBG(1, "DMCAttach: test unit ready failed (%s)\n", sane_strstatus(status)); sanei_scsi_close(fd); return status; } /* Read current ASA and shutter speed settings */ status = DMCRead(fd, 0x87, 0x4, exposureCalculationResults, sizeof(exposureCalculationResults), &size); if (status != SANE_STATUS_GOOD || size < sizeof(exposureCalculationResults)) { DBG(1, "DMCAttach: Couldn't read exposure calculation results (%s)\n", sane_strstatus(status)); sanei_scsi_close(fd); if (status == SANE_STATUS_GOOD) status = SANE_STATUS_IO_ERROR; return status; } /* Read current white balance settings */ status = DMCRead(fd, 0x82, 0x0, userInterfaceSettings, sizeof(userInterfaceSettings), &size); if (status != SANE_STATUS_GOOD || size < sizeof(userInterfaceSettings)) { DBG(1, "DMCAttach: Couldn't read user interface settings (%s)\n", sane_strstatus(status)); sanei_scsi_close(fd); if (status == SANE_STATUS_GOOD) status = SANE_STATUS_IO_ERROR; return status; } /* Shut off viewfinder mode */ status = sanei_scsi_cmd(fd, no_viewfinder, sizeof(no_viewfinder), NULL, NULL); if (status != SANE_STATUS_GOOD) { sanei_scsi_close(fd); return status; } sanei_scsi_close(fd); DBG(3, "DMCAttach: Looks like we have a Polaroid DMC\n"); dev = malloc(sizeof(*dev)); if (!dev) return SANE_STATUS_NO_MEM; memset(dev, 0, sizeof(*dev)); dev->sane.name = strdup(devname); dev->sane.vendor = "Polaroid"; dev->sane.model = "DMC"; dev->sane.type = "still camera"; dev->next = FirstDevice; dev->whiteBalance = userInterfaceSettings[5]; if (dev->whiteBalance > WHITE_BALANCE_FLUORESCENT) { dev->whiteBalance = WHITE_BALANCE_FLUORESCENT; } /* Bright Eyes documentation gives these as shutter speed ranges (ms) */ /* dev->shutterSpeedRange.min = 8; */ /* dev->shutterSpeedRange.max = 320; */ /* User's manual says these are shutter speed ranges (ms) */ dev->shutterSpeedRange.min = 8; dev->shutterSpeedRange.max = 1000; dev->shutterSpeedRange.quant = 2; dev->shutterSpeed = (exposureCalculationResults[10] << 8) + exposureCalculationResults[11]; /* Convert from ticks to ms */ dev->shutterSpeed = TICKS_TO_MS(dev->shutterSpeed); dev->asa = exposureCalculationResults[13]; if (dev->asa > ASA_100) dev->asa = ASA_100; dev->asa = ValidASAs[dev->asa + 1]; FirstDevice = dev; NumDevices++; if (devp) *devp = dev; return SANE_STATUS_GOOD; } /********************************************************************** //%FUNCTION: ValidateHandle //%ARGUMENTS: // handle -- a handle for an opened camera //%RETURNS: // A validated pointer to the camera or NULL if handle is not valid. // *********************************************************************/ static DMC_Camera * ValidateHandle(SANE_Handle handle) { DMC_Camera *c; for (c = FirstHandle; c; c = c->next) { if (c == handle) return c; } DBG(1, "ValidateHandle: invalid handle %p\n", handle); return NULL; } /********************************************************************** //%FUNCTION: DMCInitOptions //%ARGUMENTS: // c -- a DMC camera device //%RETURNS: // SANE_STATUS_GOOD -- OK // SANE_STATUS_INVAL -- There's a problem. //%DESCRIPTION: // Initializes the options in the DMC_Camera structure // *********************************************************************/ static SANE_Status DMCInitOptions(DMC_Camera *c) { int i; /* Image is initially 801x600 */ c->tl_x_range.min = 0; c->tl_x_range.max = c->tl_x_range.min; c->tl_x_range.quant = 1; c->tl_y_range.min = 0; c->tl_y_range.max = c->tl_y_range.min; c->tl_y_range.quant = 1; c->br_x_range.min = 800; c->br_x_range.max = c->br_x_range.min; c->br_x_range.quant = 1; c->br_y_range.min = 599; c->br_y_range.max = c->br_y_range.min; c->br_y_range.quant = 1; memset(c->opt, 0, sizeof(c->opt)); memset(c->val, 0, sizeof(c->val)); for (i=0; iopt[i].type = SANE_TYPE_INT; c->opt[i].size = sizeof(SANE_Word); c->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; c->opt[i].unit = SANE_UNIT_NONE; } c->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS; c->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; c->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; c->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; c->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; c->opt[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE; c->val[OPT_NUM_OPTS].w = NUM_OPTIONS; c->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; c->opt[OPT_GEOMETRY_GROUP].name = ""; c->opt[OPT_GEOMETRY_GROUP].title = "Geometry"; c->opt[OPT_GEOMETRY_GROUP].desc = ""; c->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; c->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* top-left x */ c->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; c->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; c->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; c->opt[OPT_TL_X].type = SANE_TYPE_INT; c->opt[OPT_TL_X].unit = SANE_UNIT_PIXEL; c->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; c->opt[OPT_TL_X].constraint.range = &c->tl_x_range; c->val[OPT_TL_X].w = c->tl_x_range.min; /* top-left y */ c->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; c->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; c->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; c->opt[OPT_TL_Y].type = SANE_TYPE_INT; c->opt[OPT_TL_Y].unit = SANE_UNIT_PIXEL; c->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; c->opt[OPT_TL_Y].constraint.range = &c->tl_y_range; c->val[OPT_TL_Y].w = c->tl_y_range.min; /* bottom-right x */ c->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; c->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; c->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; c->opt[OPT_BR_X].type = SANE_TYPE_INT; c->opt[OPT_BR_X].unit = SANE_UNIT_PIXEL; c->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; c->opt[OPT_BR_X].constraint.range = &c->br_x_range; c->val[OPT_BR_X].w = c->br_x_range.min; /* bottom-right y */ c->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; c->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; c->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; c->opt[OPT_BR_Y].type = SANE_TYPE_INT; c->opt[OPT_BR_Y].unit = SANE_UNIT_PIXEL; c->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; c->opt[OPT_BR_Y].constraint.range = &c->br_y_range; c->val[OPT_BR_Y].w = c->br_y_range.min; c->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; c->opt[OPT_MODE_GROUP].name = ""; c->opt[OPT_MODE_GROUP].title = "Imaging Mode"; c->opt[OPT_MODE_GROUP].desc = ""; c->opt[OPT_MODE_GROUP].cap = SANE_CAP_ADVANCED; c->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; c->opt[OPT_IMAGE_MODE].name = "imagemode"; c->opt[OPT_IMAGE_MODE].title = "Image Mode"; c->opt[OPT_IMAGE_MODE].desc = "Selects image mode: 800x600 full frame, 270x201 viewfinder mode, 1599x600 \"raw\" image, 80x60 thumbnail image or 1599x1200 \"super-resolution\" image"; c->opt[OPT_IMAGE_MODE].type = SANE_TYPE_STRING; c->opt[OPT_IMAGE_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; c->opt[OPT_IMAGE_MODE].constraint.string_list = ValidModes; c->opt[OPT_IMAGE_MODE].size = 16; c->val[OPT_IMAGE_MODE].s = "Full frame"; c->opt[OPT_ASA].name = "asa"; c->opt[OPT_ASA].title = "ASA Setting"; c->opt[OPT_ASA].desc = "Equivalent ASA setting"; c->opt[OPT_ASA].constraint_type = SANE_CONSTRAINT_WORD_LIST; c->opt[OPT_ASA].constraint.word_list = ValidASAs; c->val[OPT_ASA].w = c->hw->asa; c->opt[OPT_SHUTTER_SPEED].name = "shutterspeed"; c->opt[OPT_SHUTTER_SPEED].title = "Shutter Speed (ms)"; c->opt[OPT_SHUTTER_SPEED].desc = "Shutter Speed in milliseconds"; c->opt[OPT_SHUTTER_SPEED].constraint_type = SANE_CONSTRAINT_RANGE; c->opt[OPT_SHUTTER_SPEED].constraint.range = &c->hw->shutterSpeedRange; c->val[OPT_SHUTTER_SPEED].w = c->hw->shutterSpeed; c->opt[OPT_WHITE_BALANCE].name = "whitebalance"; c->opt[OPT_WHITE_BALANCE].title = "White Balance"; c->opt[OPT_WHITE_BALANCE].desc = "Selects white balance"; c->opt[OPT_WHITE_BALANCE].type = SANE_TYPE_STRING; c->opt[OPT_WHITE_BALANCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; c->opt[OPT_WHITE_BALANCE].constraint.string_list = ValidBalances; c->opt[OPT_WHITE_BALANCE].size = 16; c->val[OPT_WHITE_BALANCE].s = (SANE_String) ValidBalances[c->hw->whiteBalance]; return SANE_STATUS_GOOD; } /********************************************************************** //%FUNCTION: DMCSetMode //%ARGUMENTS: // c -- a DMC camera device // mode -- Imaging mode //%RETURNS: // SANE_STATUS_GOOD -- OK // SANE_STATUS_INVAL -- There's a problem. //%DESCRIPTION: // Sets the camera's imaging mode. // *********************************************************************/ static SANE_Status DMCSetMode(DMC_Camera *c, int mode) { switch (mode) { case IMAGE_MFI: c->tl_x_range.min = 0; c->tl_x_range.max = 800; c->tl_y_range.min = 0; c->tl_y_range.max = 599; c->br_x_range.min = c->tl_x_range.min; c->br_x_range.max = c->tl_x_range.max; c->br_y_range.min = c->tl_y_range.min; c->br_y_range.max = c->tl_y_range.max; break; case IMAGE_VIEWFINDER: c->tl_x_range.min = 0; c->tl_x_range.max = 269; c->tl_y_range.min = 0; c->tl_y_range.max = 200; c->br_x_range.min = c->tl_x_range.min; c->br_x_range.max = c->tl_x_range.max; c->br_y_range.min = c->tl_y_range.min; c->br_y_range.max = c->tl_y_range.max; break; case IMAGE_RAW: c->tl_x_range.min = 0; c->tl_x_range.max = 1598; c->tl_y_range.min = 0; c->tl_y_range.max = 599; c->br_x_range.min = c->tl_x_range.min; c->br_x_range.max = c->tl_x_range.max; c->br_y_range.min = c->tl_y_range.min; c->br_y_range.max = c->tl_y_range.max; break; case IMAGE_THUMB: c->tl_x_range.min = 0; c->tl_x_range.max = 79; c->tl_y_range.min = 0; c->tl_y_range.max = 59; c->br_x_range.min = c->tl_x_range.min; c->br_x_range.max = c->tl_x_range.max; c->br_y_range.min = c->tl_y_range.min; c->br_y_range.max = c->tl_y_range.max; break; case IMAGE_SUPER_RES: c->tl_x_range.min = 0; c->tl_x_range.max = 1598; c->tl_y_range.min = 0; c->tl_y_range.max = 1199; c->br_x_range.min = c->tl_x_range.min; c->br_x_range.max = c->tl_x_range.max; c->br_y_range.min = c->tl_y_range.min; c->br_y_range.max = c->tl_y_range.max; break; default: return SANE_STATUS_INVAL; } c->imageMode = mode; c->val[OPT_TL_X].w = c->tl_x_range.min; c->val[OPT_TL_Y].w = c->tl_y_range.min; c->val[OPT_BR_X].w = c->br_x_range.min; c->val[OPT_BR_Y].w = c->br_y_range.min; return SANE_STATUS_GOOD; } /********************************************************************** //%FUNCTION: DMCCancel //%ARGUMENTS: // c -- a DMC camera device //%RETURNS: // SANE_STATUS_CANCELLED //%DESCRIPTION: // Cancels DMC image acquisition // *********************************************************************/ static SANE_Status DMCCancel(DMC_Camera *c) { if (c->fd >= 0) { sanei_scsi_close(c->fd); c->fd = -1; } return SANE_STATUS_CANCELLED; } /********************************************************************** //%FUNCTION: DMCSetASA //%ARGUMENTS: // fd -- SCSI file descriptor // asa -- the ASA to set //%RETURNS: // A sane status value //%DESCRIPTION: // Sets the equivalent ASA setting of the camera. // *********************************************************************/ static SANE_Status DMCSetASA(int fd, unsigned int asa) { uint8_t exposureCalculationResults[16]; SANE_Status status; size_t len; int i; DBG(3, "DMCSetAsa: %d\n", asa); for (i=1; i<=ASA_100+1; i++) { if (asa == (unsigned int) ValidASAs[i]) break; } if (i > ASA_100+1) return SANE_STATUS_INVAL; status = DMCRead(fd, 0x87, 0x4, exposureCalculationResults, sizeof(exposureCalculationResults), &len); if (status != SANE_STATUS_GOOD) return status; if (len < sizeof(exposureCalculationResults)) return SANE_STATUS_IO_ERROR; exposureCalculationResults[13] = (uint8_t) i - 1; return DMCWrite(fd, 0x87, 0x4, exposureCalculationResults, sizeof(exposureCalculationResults)); } /********************************************************************** //%FUNCTION: DMCSetWhiteBalance //%ARGUMENTS: // fd -- SCSI file descriptor // mode -- white balance mode //%RETURNS: // A sane status value //%DESCRIPTION: // Sets the equivalent ASA setting of the camera. // *********************************************************************/ static SANE_Status DMCSetWhiteBalance(int fd, int mode) { uint8_t userInterfaceSettings[16]; SANE_Status status; size_t len; DBG(3, "DMCSetWhiteBalance: %d\n", mode); status = DMCRead(fd, 0x82, 0x0, userInterfaceSettings, sizeof(userInterfaceSettings), &len); if (status != SANE_STATUS_GOOD) return status; if (len < sizeof(userInterfaceSettings)) return SANE_STATUS_IO_ERROR; userInterfaceSettings[5] = (uint8_t) mode; return DMCWrite(fd, 0x82, 0x0, userInterfaceSettings, sizeof(userInterfaceSettings)); } /********************************************************************** //%FUNCTION: DMCSetShutterSpeed //%ARGUMENTS: // fd -- SCSI file descriptor // speed -- shutter speed in ms //%RETURNS: // A sane status value //%DESCRIPTION: // Sets the shutter speed of the camera // *********************************************************************/ static SANE_Status DMCSetShutterSpeed(int fd, unsigned int speed) { uint8_t exposureCalculationResults[16]; SANE_Status status; size_t len; DBG(3, "DMCSetShutterSpeed: %u\n", speed); /* Convert from ms to ticks */ speed = MS_TO_TICKS(speed); status = DMCRead(fd, 0x87, 0x4, exposureCalculationResults, sizeof(exposureCalculationResults), &len); if (status != SANE_STATUS_GOOD) return status; if (len < sizeof(exposureCalculationResults)) return SANE_STATUS_IO_ERROR; exposureCalculationResults[10] = (speed >> 8) & 0xFF; exposureCalculationResults[11] = speed & 0xFF; return DMCWrite(fd, 0x87, 0x4, exposureCalculationResults, sizeof(exposureCalculationResults)); } /********************************************************************** //%FUNCTION: DMCReadTwoSuperResolutionLines //%ARGUMENTS: // c -- DMC Camera // buf -- where to put output. // lastLine -- if true, these are the last two lines in the super-resolution // image to read. //%RETURNS: // Nothing //%DESCRIPTION: // Reads a single "raw" line from the camera (if needed) and constructs // two "super-resolution" output lines in "buf" // *********************************************************************/ static SANE_Status DMCReadTwoSuperResolutionLines(DMC_Camera *c, SANE_Byte *buf, int lastLine) { SANE_Status status; size_t len; SANE_Byte *output, *prev; int redCoeff, greenCoeff, blueCoeff; int red, green, blue; int i; if (c->nextRawLineValid) { memcpy(c->currentRawLine, c->nextRawLine, BYTES_PER_RAW_LINE); } else { status = DMCRead(c->fd, 0x00, IMAGE_RAW, c->currentRawLine, BYTES_PER_RAW_LINE, &len); if (status != SANE_STATUS_GOOD) return status; } if (!lastLine) { status = DMCRead(c->fd, 0x00, IMAGE_RAW, c->nextRawLine, BYTES_PER_RAW_LINE, &len); if (status != SANE_STATUS_GOOD) return status; c->nextRawLineValid = 1; } redCoeff = 3; greenCoeff = 1; blueCoeff = 2; /* Do the first super-resolution line */ output = buf; for (i=0; icurrentRawLine[PREV_RED(i)] + (3-redCoeff) * c->currentRawLine[NEXT_RED(i)]; green = greenCoeff * c->currentRawLine[PREV_GREEN(i)] + (3-greenCoeff) * c->currentRawLine[NEXT_GREEN(i)]; blue = blueCoeff * c->currentRawLine[PREV_BLUE(i)] + (3-blueCoeff) * c->currentRawLine[NEXT_BLUE(i)]; *output++ = red/3; *output++ = green/3; *output++ = blue/3; redCoeff = ADVANCE_COEFF(redCoeff); greenCoeff = ADVANCE_COEFF(greenCoeff); blueCoeff = ADVANCE_COEFF(blueCoeff); } /* Do the next super-resolution line and interpolate vertically */ if (lastLine) { memcpy(buf+BYTES_PER_RAW_LINE*3, buf, BYTES_PER_RAW_LINE*3); return SANE_STATUS_GOOD; } redCoeff = 3; greenCoeff = 1; blueCoeff = 2; prev = buf; for (i=0; inextRawLine[PREV_RED(i)] + (3-redCoeff) * c->nextRawLine[NEXT_RED(i)]; green = greenCoeff * c->nextRawLine[PREV_GREEN(i)] + (3-greenCoeff) * c->nextRawLine[NEXT_GREEN(i)]; blue = blueCoeff * c->nextRawLine[PREV_BLUE(i)] + (3-blueCoeff) * c->nextRawLine[NEXT_BLUE(i)]; *output++ = (red/3 + *prev++) / 2; *output++ = (green/3 + *prev++) / 2; *output++ = (blue/3 + *prev++) / 2; redCoeff = ADVANCE_COEFF(redCoeff); greenCoeff = ADVANCE_COEFF(greenCoeff); blueCoeff = ADVANCE_COEFF(blueCoeff); } return SANE_STATUS_GOOD; } /*********************************************************************** //%FUNCTION: attach_one (static function) //%ARGUMENTS: // dev -- device to attach //%RETURNS: // SANE_STATUS_GOOD //%DESCRIPTION: // tries to attach a device found by sanei_config_attach_matching_devices // *********************************************************************/ static SANE_Status attach_one (const char *dev) { DMCAttach (dev, 0); return SANE_STATUS_GOOD; } /********************************************************************** //%FUNCTION: sane_init //%ARGUMENTS: // version_code -- pointer to where we stick our version code // authorize -- authorization function //%RETURNS: // A sane status value //%DESCRIPTION: // Initializes DMC sane system. // *********************************************************************/ SANE_Status sane_init(SANE_Int *version_code, SANE_Auth_Callback authorize) { char dev_name[PATH_MAX]; size_t len; FILE *fp; (void) authorize; DBG_INIT(); if (version_code) { *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); } fp = sanei_config_open(DMC_CONFIG_FILE); if (!fp) { /* default to /dev/camera instead of insisting on config file */ if (DMCAttach ("/dev/camera", NULL) != SANE_STATUS_GOOD) { /* OK, try /dev/scanner */ DMCAttach("/dev/scanner", NULL); } return SANE_STATUS_GOOD; } while (sanei_config_read (dev_name, sizeof (dev_name), fp)) { if (dev_name[0] == '#') { /* ignore line comments */ continue; } len = strlen (dev_name); if (!len) continue; /* ignore empty lines */ sanei_config_attach_matching_devices(dev_name, attach_one); } fclose (fp); return SANE_STATUS_GOOD; } /********************************************************************** //%FUNCTION: sane_exit //%ARGUMENTS: // None //%RETURNS: // Nothing //%DESCRIPTION: // Cleans up all the SANE information // *********************************************************************/ void sane_exit(void) { DMC_Device *dev, *next; /* Close all handles */ while(FirstHandle) { sane_close(FirstHandle); } /* Free all devices */ dev = FirstDevice; while(dev) { next = dev->next; free((char *) dev->sane.model); free(dev); dev = next; } if (devlist) free (devlist); } /********************************************************************** //%FUNCTION: sane_get_devices //%ARGUMENTS: // device_list -- set to allocated list of devices // local_only -- ignored //%RETURNS: // A SANE status //%DESCRIPTION: // Returns a list of all known DMC devices // *********************************************************************/ SANE_Status sane_get_devices(SANE_Device const ***device_list, SANE_Bool local_only) { DMC_Device *dev; int i = 0; (void) local_only; if (devlist) free(devlist); devlist = malloc((NumDevices+1) * sizeof(devlist[0])); if (!devlist) return SANE_STATUS_NO_MEM; for (dev=FirstDevice; dev; dev = dev->next) { devlist[i++] = &dev->sane; } devlist[i] = NULL; if (device_list) *device_list = devlist; return SANE_STATUS_GOOD; } /********************************************************************** //%FUNCTION: sane_open //%ARGUMENTS: // name -- name of device to open // handle -- set to a handle for the opened device //%RETURNS: // A SANE status //%DESCRIPTION: // Opens a DMC camera device // *********************************************************************/ SANE_Status sane_open(SANE_String_Const name, SANE_Handle *handle) { SANE_Status status; DMC_Device *dev; DMC_Camera *c; /* If we're given a device name, search for it */ if (*name) { for (dev = FirstDevice; dev; dev = dev->next) { if (!strcmp(dev->sane.name, name)) { break; } } if (!dev) { status = DMCAttach(name, &dev); if (status != SANE_STATUS_GOOD) return status; } } else { dev = FirstDevice; } if (!dev) return SANE_STATUS_INVAL; c = malloc(sizeof(*c)); if (!c) return SANE_STATUS_NO_MEM; memset(c, 0, sizeof(*c)); c->fd = -1; c->hw = dev; c->readBuffer = NULL; c->readPtr = NULL; c->imageMode = IMAGE_MFI; c->inViewfinderMode = 0; c->nextRawLineValid = 0; DMCInitOptions(c); c->next = FirstHandle; FirstHandle = c; if (handle) *handle = c; return SANE_STATUS_GOOD; } /********************************************************************** //%FUNCTION: sane_close //%ARGUMENTS: // handle -- handle of device to close //%RETURNS: // A SANE status //%DESCRIPTION: // Closes a DMC camera device // *********************************************************************/ void sane_close(SANE_Handle handle) { DMC_Camera *prev, *c; prev = NULL; for (c = FirstHandle; c; c = c->next) { if (c == handle) break; prev = c; } if (!c) { DBG(1, "close: invalid handle %p\n", handle); return; } DMCCancel(c); if (prev) prev->next = c->next; else FirstHandle = c->next; if (c->readBuffer) { free(c->readBuffer); } free(c); } /********************************************************************** //%FUNCTION: sane_get_option_descriptor //%ARGUMENTS: // handle -- handle of device // option -- option number to retrieve //%RETURNS: // An option descriptor or NULL on error // *********************************************************************/ SANE_Option_Descriptor const * sane_get_option_descriptor(SANE_Handle handle, SANE_Int option) { DMC_Camera *c = ValidateHandle(handle); if (!c) return NULL; if ((unsigned) option >= NUM_OPTIONS) return NULL; return c->opt + option; } /********************************************************************** //%FUNCTION: sane_control_option //%ARGUMENTS: // handle -- handle of device // option -- option number to retrieve // action -- what to do with the option // val -- value to set option to // info -- returned info flags //%RETURNS: // SANE status //%DESCRIPTION: // Sets or queries option values // *********************************************************************/ SANE_Status sane_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int *info) { DMC_Camera *c; SANE_Word cap; int i; if (info) *info = 0; c = ValidateHandle(handle); if (!c) return SANE_STATUS_INVAL; if (c->fd >= 0) return SANE_STATUS_DEVICE_BUSY; if (option >= NUM_OPTIONS) return SANE_STATUS_INVAL; cap = c->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE(cap)) return SANE_STATUS_INVAL; if (action == SANE_ACTION_GET_VALUE) { switch(c->opt[option].type) { case SANE_TYPE_INT: * (SANE_Int *) val = c->val[option].w; return SANE_STATUS_GOOD; case SANE_TYPE_STRING: strcpy(val, c->val[option].s); return SANE_STATUS_GOOD; default: DBG(3, "impossible option type!\n"); return SANE_STATUS_INVAL; } } if (action == SANE_ACTION_SET_AUTO) { return SANE_STATUS_UNSUPPORTED; } switch(option) { case OPT_IMAGE_MODE: for (i=0; ival[OPT_IMAGE_MODE].s = (SANE_String) ValidModes[i]; if (info) *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; } } break; case OPT_WHITE_BALANCE: for (i=0; i<=WHITE_BALANCE_FLUORESCENT; i++) { if (!strcmp(val, ValidBalances[i])) { c->val[OPT_WHITE_BALANCE].s = (SANE_String) ValidBalances[i]; return SANE_STATUS_GOOD; } } break; case OPT_ASA: for (i=1; i<= ASA_100+1; i++) { if (* ((SANE_Int *) val) == ValidASAs[i]) { c->val[OPT_ASA].w = ValidASAs[i]; return SANE_STATUS_GOOD; } } break; case OPT_SHUTTER_SPEED: if (* (SANE_Int *) val < c->hw->shutterSpeedRange.min || * (SANE_Int *) val > c->hw->shutterSpeedRange.max) { return SANE_STATUS_INVAL; } c->val[OPT_SHUTTER_SPEED].w = * (SANE_Int *) val; /* Do any roundoff */ c->val[OPT_SHUTTER_SPEED].w = TICKS_TO_MS(MS_TO_TICKS(c->val[OPT_SHUTTER_SPEED].w)); if (c->val[OPT_SHUTTER_SPEED].w != * (SANE_Int *) val) { if (info) *info |= SANE_INFO_INEXACT; } return SANE_STATUS_GOOD; default: /* Should really be INVAL, but just bit-bucket set requests... */ return SANE_STATUS_GOOD; } return SANE_STATUS_INVAL; } /********************************************************************** //%FUNCTION: sane_get_parameters //%ARGUMENTS: // handle -- handle of device // params -- set to device parameters //%RETURNS: // SANE status //%DESCRIPTION: // Returns parameters for current or next image. // *********************************************************************/ SANE_Status sane_get_parameters(SANE_Handle handle, SANE_Parameters *params) { DMC_Camera *c = ValidateHandle(handle); if (!c) return SANE_STATUS_INVAL; if (c->fd < 0) { int width, height; memset(&c->params, 0, sizeof(c->params)); width = c->val[OPT_BR_X].w - c->val[OPT_TL_X].w; height = c->val[OPT_BR_Y].w - c->val[OPT_TL_Y].w; c->params.pixels_per_line = width + 1; c->params.lines = height+1; c->params.depth = 8; c->params.last_frame = SANE_TRUE; switch(c->imageMode) { case IMAGE_SUPER_RES: case IMAGE_MFI: case IMAGE_THUMB: c->params.format = SANE_FRAME_RGB; c->params.bytes_per_line = c->params.pixels_per_line * 3; break; case IMAGE_RAW: case IMAGE_VIEWFINDER: c->params.format = SANE_FRAME_GRAY; c->params.bytes_per_line = c->params.pixels_per_line; break; } } if (params) *params = c->params; return SANE_STATUS_GOOD; } /********************************************************************** //%FUNCTION: sane_start //%ARGUMENTS: // handle -- handle of device //%RETURNS: // SANE status //%DESCRIPTION: // Starts acquisition // *********************************************************************/ SANE_Status sane_start(SANE_Handle handle) { static uint8_t const acquire[] = { 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static uint8_t const viewfinder[] = { 0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static uint8_t const no_viewfinder[] = { 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; DMC_Camera *c = ValidateHandle(handle); SANE_Status status; int i; if (!c) return SANE_STATUS_INVAL; /* If we're already open, barf -- not sure this is the best status */ if (c->fd >= 0) return SANE_STATUS_DEVICE_BUSY; /* Get rid of old read buffers */ if (c->readBuffer) { free(c->readBuffer); c->readBuffer = NULL; c->readPtr = NULL; } c->nextRawLineValid = 0; /* Refresh parameter list */ status = sane_get_parameters(c, NULL); if (status != SANE_STATUS_GOOD) return status; status = sanei_scsi_open(c->hw->sane.name, &c->fd, NULL, NULL); if (status != SANE_STATUS_GOOD) { c->fd = -1; DBG(1, "DMC: Open of `%s' failed: %s\n", c->hw->sane.name, sane_strstatus(status)); return status; } /* Set ASA and shutter speed if they're no longer current */ if (c->val[OPT_ASA].w != c->hw->asa) { status = DMCSetASA(c->fd, c->val[OPT_ASA].w); if (status != SANE_STATUS_GOOD) { DMCCancel(c); return status; } c->hw->asa = c->val[OPT_ASA].w; } if ((unsigned int) c->val[OPT_SHUTTER_SPEED].w != c->hw->shutterSpeed) { status = DMCSetShutterSpeed(c->fd, c->val[OPT_SHUTTER_SPEED].w); if (status != SANE_STATUS_GOOD) { DMCCancel(c); return status; } c->hw->shutterSpeed = c->val[OPT_SHUTTER_SPEED].w; } /* Set white balance mode if needed */ for (i=0; i<=WHITE_BALANCE_FLUORESCENT; i++) { if (!strcmp(ValidBalances[i], c->val[OPT_WHITE_BALANCE].s)) { if (i != c->hw->whiteBalance) { status = DMCSetWhiteBalance(c->fd, i); if (status != SANE_STATUS_GOOD) { DMCCancel(c); return status; } c->hw->whiteBalance = i; } } } /* Flip into viewfinder mode if needed */ if (c->imageMode == IMAGE_VIEWFINDER && !c->inViewfinderMode) { status = sanei_scsi_cmd(c->fd, viewfinder, sizeof(viewfinder), NULL, NULL); if (status != SANE_STATUS_GOOD) { DMCCancel(c); return status; } c->inViewfinderMode = 1; } /* Flip out of viewfinder mode if needed */ if (c->imageMode != IMAGE_VIEWFINDER && c->inViewfinderMode) { status = sanei_scsi_cmd(c->fd, no_viewfinder, sizeof(no_viewfinder), NULL, NULL); if (status != SANE_STATUS_GOOD) { DMCCancel(c); return status; } c->inViewfinderMode = 0; } status = sanei_scsi_cmd(c->fd, acquire, sizeof(acquire), NULL, NULL); if (status != SANE_STATUS_GOOD) { DMCCancel(c); return status; } c->bytes_to_read = c->params.bytes_per_line * c->params.lines; return SANE_STATUS_GOOD; } /********************************************************************** //%FUNCTION: sane_read //%ARGUMENTS: // handle -- handle of device // buf -- destination for data // max_len -- maximum amount of data to store // len -- set to actual amount of data stored. //%RETURNS: // SANE status //%DESCRIPTION: // Reads image data from the camera // *********************************************************************/ SANE_Status sane_read(SANE_Handle handle, SANE_Byte *buf, SANE_Int max_len, SANE_Int *len) { SANE_Status status; DMC_Camera *c = ValidateHandle(handle); size_t size; SANE_Int i; if (!c) return SANE_STATUS_INVAL; if (c->fd < 0) return SANE_STATUS_INVAL; if (c->bytes_to_read == 0) { if (c->readBuffer) { free(c->readBuffer); c->readBuffer = NULL; c->readPtr = NULL; } DMCCancel(c); return SANE_STATUS_EOF; } if (max_len == 0) { return SANE_STATUS_GOOD; } if (c->imageMode == IMAGE_SUPER_RES) { /* We have to read *two* complete rows... */ max_len = (max_len / (2*c->params.bytes_per_line)) * (2*c->params.bytes_per_line); /* If user is trying to read less than two complete lines, fail */ if (max_len == 0) return SANE_STATUS_INVAL; if ((unsigned int) max_len > c->bytes_to_read) max_len = c->bytes_to_read; for (i=0; iparams.bytes_per_line) { c->bytes_to_read -= 2*c->params.bytes_per_line; status = DMCReadTwoSuperResolutionLines(c, buf+i, !c->bytes_to_read); if (status != SANE_STATUS_GOOD) return status; } *len = max_len; return SANE_STATUS_GOOD; } if (c->imageMode == IMAGE_MFI || c->imageMode == IMAGE_RAW) { /* We have to read complete rows... */ max_len = (max_len / c->params.bytes_per_line) * c->params.bytes_per_line; /* If user is trying to read less than one complete row, fail */ if (max_len == 0) return SANE_STATUS_INVAL; if ((unsigned int) max_len > c->bytes_to_read) max_len = c->bytes_to_read; c->bytes_to_read -= (unsigned int) max_len; status = DMCRead(c->fd, 0x00, c->imageMode, buf, max_len, &size); *len = size; return status; } if ((unsigned int) max_len > c->bytes_to_read) max_len = c->bytes_to_read; if (c->readPtr) { *len = max_len; memcpy(buf, c->readPtr, max_len); c->readPtr += max_len; c->bytes_to_read -= max_len; return SANE_STATUS_GOOD; } /* Fill the read buffer completely */ c->readBuffer = malloc(c->bytes_to_read); if (!c->readBuffer) return SANE_STATUS_NO_MEM; c->readPtr = c->readBuffer; status = DMCRead(c->fd, 0x00, c->imageMode, (SANE_Byte *) c->readBuffer, c->bytes_to_read, &size); *len = size; if (status != SANE_STATUS_GOOD) return status; if ((unsigned int) *len != c->bytes_to_read) return SANE_STATUS_IO_ERROR; /* Now copy */ *len = max_len; memcpy(buf, c->readPtr, max_len); c->readPtr += max_len; c->bytes_to_read -= max_len; return SANE_STATUS_GOOD; } /********************************************************************** //%FUNCTION: sane_cancel //%ARGUMENTS: // handle -- handle of device //%RETURNS: // Nothing //%DESCRIPTION: // A quick cancellation of the scane // *********************************************************************/ void sane_cancel (SANE_Handle handle) { DMC_Camera *c = ValidateHandle(handle); if (!c) return; DMCCancel(c); } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { (void) handle; (void) non_blocking; return SANE_STATUS_UNSUPPORTED; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int *fd) { (void) handle; (void) fd; return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/dmc.conf.in000066400000000000000000000000141456256263500167040ustar00rootroot00000000000000/dev/camera backends-1.3.0/backend/dmc.h000066400000000000000000000075151456256263500156160ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1998 Dianne Skoll 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, see . 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 polaroid_dmc_h #define polaroid_dmc_h #include #define BYTES_PER_RAW_LINE 1599 typedef enum { OPT_NUM_OPTS = 0, OPT_GEOMETRY_GROUP, OPT_TL_X, /* top-left x */ OPT_TL_Y, /* top-left y */ OPT_BR_X, /* bottom-right x */ OPT_BR_Y, /* bottom-right y */ OPT_MODE_GROUP, /* Image acquisition mode */ OPT_IMAGE_MODE, /* Thumbnail, center cut or MFI'd image */ OPT_ASA, /* ASA Settings */ OPT_SHUTTER_SPEED, /* Shutter speed */ OPT_WHITE_BALANCE, /* White balance */ /* must come last: */ NUM_OPTIONS } DMC_Option; typedef struct DMC_Device { struct DMC_Device *next; SANE_Device sane; SANE_Range shutterSpeedRange; unsigned int shutterSpeed; int asa; int whiteBalance; } DMC_Device; typedef struct DMC_Camera { /* all the state needed to define a scan request: */ struct DMC_Camera *next; SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; SANE_Parameters params; size_t bytes_to_read; SANE_Range tl_x_range; SANE_Range tl_y_range; SANE_Range br_x_range; SANE_Range br_y_range; int imageMode; /* The DMC needs certain reads to be done in one chunk, meaning we might have to buffer them. */ char *readBuffer; char *readPtr; int inViewfinderMode; int fd; /* SCSI filedescriptor */ SANE_Byte currentRawLine[BYTES_PER_RAW_LINE]; SANE_Byte nextRawLine[BYTES_PER_RAW_LINE]; int nextRawLineValid; /* scanner dependent/low-level state: */ DMC_Device *hw; } DMC_Camera; /* We only support the following four imaging modes */ #define IMAGE_MFI 0x0000 /* 801x600 filtered image */ #define IMAGE_VIEWFINDER 0x0001 /* 270x201 viewfinder image */ #define IMAGE_RAW 0x0002 /* 1599x600 raw image */ #define IMAGE_THUMB 0x0003 /* 80x60 thumbnail image */ #define IMAGE_SUPER_RES 0x0004 #define NUM_IMAGE_MODES 5 #define ASA_25 0 #define ASA_50 1 #define ASA_100 2 #define WHITE_BALANCE_DAYLIGHT 0 #define WHITE_BALANCE_INCANDESCENT 1 #define WHITE_BALANCE_FLUORESCENT 2 #endif /* polaroid_dmc_h */ backends-1.3.0/backend/epjitsu-cmd.h000066400000000000000000001252311456256263500172730ustar00rootroot00000000000000static void putnbyte (unsigned char *pnt, unsigned int value, unsigned int nbytes) { int i; for (i = nbytes - 1; i >= 0; i--) { pnt[i] = value & 0xff; value = value >> 8; } } #define SET_WINDOW_LEN 72 #define set_SW_byte7(out, val) out[7] = val #define set_SW_xres(out, val) putnbyte(out + 0x0a, val, 2) #define set_SW_yres(out, val) putnbyte(out + 0x0c, val, 2) #define set_SW_xpix(out, val) putnbyte(out + 0x16, val, 4) #define set_SW_ypix(out, val) putnbyte(out + 0x1a, val, 4) #define set_SW_compo(out, val) out[0x21] = val /*color=5,gray=2*/ #define set_SW_bpp(out, val) out[0x22] = val #define set_SW_byte31(out, val) out[0x31] = val #define set_SW_byte32(out, val) out[0x32] = val #define set_SW_byte33(out, val) out[0x33] = val #define set_SW_lpb(out, val) out[0x34] = val #define set_SW_byte35(out, val) out[0x35] = val #define set_SW_byte36(out, val) out[0x36] = val #define set_SW_byte38(out, val) out[0x38] = val /*move motor?*/ #define set_SW_fres(out, val) putnbyte(out + 0x39, val, 2) /*************** COARSE CALIBRATION DEFAULT PAYLOAD *************/ /* 1b c6 (send coarse cal) command payload - not resolution specific? */ /* first group of 3 is offset?, larger # == brighter */ /* second group of 3 is gain?, larger # == brighter */ static unsigned char coarseCalData_FI60F[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x26, 0x00, 0x26, 0x00, 0x26, 0x00, 0x00, 0x0b, 0x22, 0x00, 0x00, 0x0b, 0x22, 0x00, 0x00, 0x0b, 0x22 }; static unsigned char coarseCalData_S300[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x24, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static unsigned char coarseCalData_S1300i[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x24, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static unsigned char coarseCalData_S1100[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x00, 0x25, 0x00, 0x00, 0x00, 0x26, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /*************** fi-60F 300dpi gray *************/ #if 0 static unsigned char setWindowScan_FI60F_300_g[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x06, 0xd5, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x6c, 0x01, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /*************** fi-60F 400dpi gray *************/ static unsigned char setWindowScan_FI60F_400_g[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x58, 0x01, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x09, 0x1c, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0xca, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /*************** fi-60F 600dpi gray *************/ static unsigned char setWindowScan_FI60F_600_g[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x58, 0x02, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x0d, 0xaa, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0xca, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; #endif /*************** fi-60F 150dpi *************/ static unsigned char setWindowScan_FI60F_150[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x60, 0x00, 0x00, 0x03, 0x6b, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x48, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /*************** fi-60F 200dpi *************/ static unsigned char setWindowScan_FI60F_200[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x2c, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x60, 0x00, 0x00, 0x04, 0x8e, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x48, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /*************** fi-60F 300dpi *************/ /* 1b d1 (set window) before coarse cal (read 1 line of 0x1c20 bytes) */ static unsigned char setWindowCoarseCal_FI60F_300[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x60, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before fine cal (read 16 lines of 0x1c20 bytes) */ static unsigned char setWindowFineCal_FI60F_300[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x60, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x10, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before gain/offset tables (write 1 line of 0x3840 bytes) */ static unsigned char setWindowSendCal_FI60F_300[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x60, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b c3 (gain?) command header */ static unsigned char sendCal1Header_FI60F_300[] = { /* plus 0x3840 data bytes */ 0x8c, 0x0f, 0x8c, 0x0f, 0x8c, 0x0f, 0x8c, 0x0f, 0x8c, 0x0f, 0x8c, 0x0f, 0x00, 0x04 }; /* 1b c4 (offset?) command header */ static unsigned char sendCal2Header_FI60F_300[] = { 0x39, 0x3f, 0x39, 0x3f, 0x39, 0x3f, 0x07 }; /* 1b d1 (set window) before scan */ static unsigned char setWindowScan_FI60F_300[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x60, 0x00, 0x00, 0x06, 0xd5, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x48, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /*************** fi-60F 400dpi *************/ static unsigned char setWindowScan_FI60F_400[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x58, 0x01, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x20, 0x00, 0x00, 0x09, 0x1c, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x3d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /*************** fi-60F 600dpi *************/ /* 1b d1 (set window) before coarse cal (read 1 line of 0x2160 bytes) */ static unsigned char setWindowCoarseCal_FI60F_600[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x02, 0x58, 0x02, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before fine cal (read 16 lines of 0x2160 bytes) */ static unsigned char setWindowFineCal_FI60F_600[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x58, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before gain/offset tables (write 1 line of 0x42c0 bytes) */ static unsigned char setWindowSendCal_FI60F_600[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x58, 0x02, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x20, 0x00, 0x00, 0x0d, 0xaa, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x3d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b c3 (gain?) command header */ static unsigned char sendCal1Header_FI60F_600[] = { 0x4f, 0x10, 0x4f, 0x10, 0x4f, 0x10, 0x4f, 0x10, 0x4f, 0x10, 0x4f, 0x10, 0x00, 0x04 }; /* 1b c4 (offset?) command header */ static unsigned char sendCal2Header_FI60F_600[] = { 0x2b, 0x40, 0x2b, 0x40, 0x2b, 0x40, 0x07 }; /* 1b d1 (set window) before scan */ static unsigned char setWindowScan_FI60F_600[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x58, 0x02, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x20, 0x00, 0x00, 0x0d, 0xaa, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x3d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /*************** S300 150dpi *************/ /* 1b d1 (set window) before coarse cal (read 1 line of 0x63c0 bytes) */ static unsigned char setWindowCoarseCal_S300_150[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before fine cal (read 16 lines of 0x63c0 bytes) */ static unsigned char setWindowFineCal_S300_150[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x90, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before gain/offset tables (write 1 line of 0xc780 bytes) */ static unsigned char setWindowSendCal_S300_150[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x40, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b c3 (gain?) command header */ static unsigned char sendCal1Header_S300_150[] = { /* plus 0xc780 data bytes */ 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0x00, 0x04 /*0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0x00, 0x05*/ }; /* 1b c4 (offset?) command header */ static unsigned char sendCal2Header_S300_150[] = { /* plus 0xc780 data bytes */ 0x77, 0x26, 0x77, 0x26, 0x77, 0x26, 0x07 /*0xd0, 0x34, 0xd0, 0x34, 0xd0, 0x34, 0x08*/ }; /* 1b d1 (set window) before scan */ static unsigned char setWindowScan_S300_150[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x96, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xc8, 0x00, 0x00, 0x06, 0xe2, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /*************** S300 225dpi *************/ /* 1b d1 (set window) before coarse cal (read 1 line of 0x63c0 bytes) */ static unsigned char setWindowCoarseCal_S300_225[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before fine cal (read 16 lines of 0x63c0 bytes) */ static unsigned char setWindowFineCal_S300_225[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xf0, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before gain/offset tables (write 1 line of 0xc780 bytes) */ static unsigned char setWindowSendCal_S300_225[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b c3 (gain?) command header */ static unsigned char sendCal1Header_S300_225[] = { /* plus 0xc780 data bytes */ 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0x00, 0x04 /*0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0x00, 0x05*/ }; /* 1b c4 (offset?) command header */ static unsigned char sendCal2Header_S300_225[] = { /* plus 0xc780 data bytes */ 0x77, 0x26, 0x77, 0x26, 0x77, 0x26, 0x07 /*0xd0, 0x34, 0xd0, 0x34, 0xd0, 0x34, 0x08*/ }; /* 1b d1 (set window) before scan */ static unsigned char setWindowScan_S300_225[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xe1, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x34, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /*************** S300 300dpi *************/ /* 1b d1 (set window) before coarse cal (read 1 line of 0x6000 bytes) */ static unsigned char setWindowCoarseCal_S300_300[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before fine cal (read 16 lines of 0x6000 bytes) */ static unsigned char setWindowFineCal_S300_300[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xf0, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before gain/offset tables (write 1 line of 0xc000 bytes) */ static unsigned char setWindowSendCal_S300_300[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b c3 (gain?) command header */ static unsigned char sendCal1Header_S300_300[] = { /* plus 0xc000 data bytes */ 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0x00, 0x04 /*0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x00, 0x05*/ }; /* 1b c4 (offset?) command header */ static unsigned char sendCal2Header_S300_300[] = { /* plus 0xc000 data bytes */ 0x77, 0x26, 0x77, 0x26, 0x77, 0x26, 0x07 /*0xb8, 0x34, 0xb8, 0x34, 0xb8, 0x34, 0x08*/ }; /* 1b d1 (set window) before scan */ static unsigned char setWindowScan_S300_300[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xf0, 0x00, 0x00, 0x0d, 0xc4, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /*************** S300 600dpi USB and AC power *************/ /* 1b d1 (set window) before coarse cal (read 1 line of 0xbc40 bytes) */ static unsigned char setWindowCoarseCal_S300_600[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x02, 0x58, 0x02, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before fine cal (read 16 lines of 0xbc40 bytes) */ static unsigned char setWindowFineCal_S300_600[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x02, 0x58, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x40, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before gain/offset tables (write 1 line of 0x17880 bytes) */ static unsigned char setWindowSendCal_S300_600[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x02, 0x58, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0xc0, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b c3 (gain?) command header */ static unsigned char sendCal1Header_S300_600[] = { 0x7f, 0x0b, 0x7f, 0x0b, 0x7f, 0x0b, 0x7f, 0x0b, 0x7f, 0x0b, 0x7f, 0x0b, 0x00, 0x04 }; /* 1b c4 (offset?) command header */ static unsigned char sendCal2Header_S300_600[] = { 0xc7, 0x23, 0xc7, 0x23, 0xc7, 0x23, 0x07 }; /* 1b d1 (set window) before scan */ static unsigned char setWindowScan_S300_600[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x02, 0x58, 0x02, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x40, 0x00, 0x00, 0x24, 0x21, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /*S300/S1300/S1300i can also use a USB power cable, but it requires a different set of params?*/ /*************** S300/S1300/S1300i 150dpi USB *************/ /* 1b d1 (set window) before coarse cal (read 1 line of 0x63c0 bytes) */ static unsigned char setWindowCoarseCal_S300_150_U[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before fine cal (read 16 lines of 0x63c0 bytes) */ static unsigned char setWindowFineCal_S300_150_U[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before gain/offset tables (write 1 line of 0xc780 bytes) */ static unsigned char setWindowSendCal_S300_150_U[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x60, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b c3 (gain?) command header */ static unsigned char sendCal1Header_S300_150_U[] = { /* plus 0xc780 data bytes */ 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0x00, 0x04 /*0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0x00, 0x05*/ }; /* 1b c4 (offset?) command header */ static unsigned char sendCal2Header_S300_150_U[] = { /* plus 0xc780 data bytes */ 0x77, 0x26, 0x77, 0x26, 0x77, 0x26, 0x07 /*0xd0, 0x34, 0xd0, 0x34, 0xd0, 0x34, 0x08*/ }; /* 1b d1 (set window) before scan */ static unsigned char setWindowScan_S300_150_U[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x96, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x90, 0x00, 0x00, 0x09, 0x0d, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /*************** S300/S1300/S1300i 225dpi USB *************/ /* 1b d1 (set window) before coarse cal (read 1 line of 0x63c0 bytes) */ static unsigned char setWindowCoarseCal_S300_225_U[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before fine cal (read 16 lines of 0x63c0 bytes) */ static unsigned char setWindowFineCal_S300_225_U[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x80, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before gain/offset tables (write 1 line of 0xc780 bytes) */ static unsigned char setWindowSendCal_S300_225_U[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b c3 (gain?) command header */ static unsigned char sendCal1Header_S300_225_U[] = { /* plus 0xc780 data bytes */ 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0x00, 0x04 /*0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0x00, 0x05*/ }; /* 1b c4 (offset?) command header */ static unsigned char sendCal2Header_S300_225_U[] = { /* plus 0xc780 data bytes */ 0x77, 0x26, 0x77, 0x26, 0x77, 0x26, 0x07 /*0xd0, 0x34, 0xd0, 0x34, 0xd0, 0x34, 0x08*/ }; /* 1b d1 (set window) before scan */ static unsigned char setWindowScan_S300_225_U[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xe1, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xe0, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /*************** S300/S1300/S1300i 300dpi USB *************/ /* 1b d1 (set window) before coarse cal (read 1 line of 0x63c0 bytes) */ /* 1b d1 (set window) before coarse cal (read 1 line of 0x63c0 bytes) */ static unsigned char setWindowCoarseCal_S300_300_U[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before fine cal (read 16 lines of 0x63c0 bytes) */ static unsigned char setWindowFineCal_S300_300_U[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0xf0, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before gain/offset tables (write 1 line of 0xc780 bytes) */ static unsigned char setWindowSendCal_S300_300_U[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b c3 (gain?) command header */ static unsigned char sendCal1Header_S300_300_U[] = { /* plus 0xc780 data bytes */ 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0xe2, 0x0a, 0x00, 0x04 /*0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0xa5, 0x0b, 0x00, 0x05*/ }; /* 1b c4 (offset?) command header */ static unsigned char sendCal2Header_S300_300_U[] = { /* plus 0xc780 data bytes */ 0x77, 0x26, 0x77, 0x26, 0x77, 0x26, 0x07 /*0xd0, 0x34, 0xd0, 0x34, 0xd0, 0x34, 0x08*/ }; /* 1b d1 (set window) before scan */ static unsigned char setWindowScan_S300_300_U[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0xf0, 0x00, 0x00, 0x12, 0x11, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /*************** S300/S1300/S1300i USB is same as AC power *************/ /*************** S1300i 150dpi *************/ /* 1b d1 (set window) before coarse cal (read 1 line of 0x5e24 bytes) */ static unsigned char setWindowCoarseCal_S1300i_150[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xa0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before fine cal (read 16 lines of 0x5e24 bytes) */ static unsigned char setWindowFineCal_S1300i_150[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xa0, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before gain/offset tables (write 1 line of 0xbc40 bytes) */ static unsigned char setWindowSendCal_S1300i_150[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x60, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b c3 (gain?) command header */ static unsigned char sendCal1Header_S1300i_150[] = { /* plus 0xc780 data bytes */ 0xc4, 0x06, 0xc4, 0x06, 0xc4, 0x06, 0xc4, 0x06, 0xc4, 0x06, 0xc4, 0x06, 0x00, 0x04 }; /* 1b c4 (offset?) command header */ static unsigned char sendCal2Header_S1300i_150[] = { /* plus 0xc780 data bytes */ 0xd7, 0x3b, 0xd7, 0x3b, 0xd7, 0x3b, 0x07 }; /* 1b d1 (set window) before scan */ static unsigned char setWindowScan_S1300i_150[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x96, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x50, 0x00, 0x00, 0x06, 0xe2, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /*************** S1300i 225dpi *************/ /* 1b d1 (set window) before coarse cal (read 1 line of 0x63c0 bytes) */ static unsigned char setWindowCoarseCal_S1300i_225[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xc0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before fine cal (read 16 lines of 0x63c0 bytes) */ static unsigned char setWindowFineCal_S1300i_225[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xc0, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before gain/offset tables (write 1 line of 0xc780 bytes) */ static unsigned char setWindowSendCal_S1300i_225[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xa0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b c3 (gain?) command header */ static unsigned char sendCal1Header_S1300i_225[] = { /* plus 0xc780 data bytes */ 0x2f, 0x07, 0x2f, 0x07, 0x2f, 0x07, 0x2f, 0x07, 0x2f, 0x07, 0x2f, 0x07, 0x00, 0x04 }; /* 1b c4 (offset?) command header */ static unsigned char sendCal2Header_S1300i_225[] = { /* plus 0xc780 data bytes */ 0xa5, 0x3b, 0xa5, 0x3b, 0xa5, 0x3b, 0x07 }; /* 1b d1 (set window) before scan */ static unsigned char setWindowScan_S1300i_225[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xe1, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /*************** S1300i 300dpi *************/ /* 1b d1 (set window) before coarse cal (read 1 line of 0x5ee0 bytes) */ static unsigned char setWindowCoarseCal_S1300i_300[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xc0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before fine cal (read 16 lines of 0x5ee0 bytes) */ static unsigned char setWindowFineCal_S1300i_300[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xc0, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before gain/offset tables (write 1 line of 0xbdc0 bytes) */ static unsigned char setWindowSendCal_S1300i_300[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xa0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b c3 (gain?) command header */ static unsigned char sendCal1Header_S1300i_300[] = { /* plus 0xc000 data bytes */ 0xdd, 0x06, 0xdd, 0x06, 0xdd, 0x06, 0xdd, 0x06, 0xdd, 0x06, 0xdd, 0x06, 0x00, 0x04 }; /* 1b c4 (offset?) command header */ static unsigned char sendCal2Header_S1300i_300[] = { /* plus 0xc000 data bytes */ 0x75, 0x3c, 0x75, 0x3c, 0x75, 0x3c, 0x07 }; /* 1b d1 (set window) before scan */ static unsigned char setWindowScan_S1300i_300[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xc0, 0x00, 0x00, 0x0d, 0xc4, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /*************** S1300i 600dpi AC power is same as S300 *************/ /*************** except the calibration headers *************/ /* 1b c3 (gain?) command header */ static unsigned char sendCal1Header_S1300i_USB[] = { 0x4d, 0x06, 0x4d, 0x06, 0x4d, 0x06, 0x4d, 0x06, 0x4d, 0x06, 0x4d, 0x06, 0x00, 0x04 }; /* 1b c4 (offset?) command header */ static unsigned char sendCal2Header_S1300i_USB[] = { 0x8f, 0x40, 0x8f, 0x40, 0x8f, 0x40, 0x07 }; /*************** S1300i all resolutions USB power is same as S300 *************/ /*************** S1100 300dpi USB *************/ /* 1b d1 (set window) before coarse cal (read 1 line of 0x45a0 bytes) */ static unsigned char setWindowCoarseCal_S1100_300_U[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x58, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before fine cal (read 16 lines of 0x45a0 bytes) */ static unsigned char setWindowFineCal_S1100_300_U[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x58, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before gain/offset tables (write 1 line of 0x45a0 bytes) */ static unsigned char setWindowSendCal_S1100_300_U[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xd0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b c3 (gain?) command header */ static unsigned char sendCal1Header_S1100_300_U[] = { /* plus 0x45a0 data bytes */ 0x1e, 0x10, 0x1e, 0x10, 0x1e, 0x10, 0x1e, 0x10, 0x1e, 0x10, 0x1e, 0x10, 0x00, 0x03 }; /* 1b c4 (offset?) command header */ static unsigned char sendCal2Header_S1100_300_U[] = { /* plus 0x45a0 data bytes */ 0x63, 0x86, 0x63, 0x86, 0x63, 0x86, 0x07 }; /* 1b d1 (set window) before scan */ static unsigned char setWindowScan_S1100_300_U[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x58, 0x00, 0x00, 0x1b, 0xe1, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /*************** S1100 600dpi USB *************/ /* 1b d1 (set window) before coarse cal (read 1 line of 0x3e20 bytes) */ static unsigned char setWindowCoarseCal_S1100_600_U[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x02, 0x58, 0x02, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before fine cal (read 16 lines of 0x3e20 bytes) */ static unsigned char setWindowFineCal_S1100_600_U[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x02, 0x58, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xf0, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b d1 (set window) before gain/offset tables (write 1 line of 0x7c40 bytes) */ static unsigned char setWindowSendCal_S1100_600_U[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x02, 0x58, 0x02, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* 1b c3 (gain?) command header */ static unsigned char sendCal1Header_S1100_600_U[] = { /* plus 0x7c40 data bytes */ 0xff, 0x11, 0xff, 0x11, 0xff, 0x11, 0xff, 0x11, 0xff, 0x11, 0xff, 0x11, 0x00, 0x03 }; /* 1b c4 (offset?) command header */ static unsigned char sendCal2Header_S1100_600_U[] = { /* plus 0x7c40 data bytes */ 0x4b, 0x81, 0x4b, 0x81, 0x4b, 0x81, 0x07 }; /* 1b d1 (set window) before scan */ static unsigned char setWindowScan_S1100_600_U[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x02, 0x58, 0x02, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xf0, 0x00, 0x00, 0x37, 0xbf, 0x00, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; backends-1.3.0/backend/epjitsu.c000066400000000000000000004475701456256263500165420ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. This file implements a SANE backend for the Fujitsu fi-60F, the ScanSnap S300/S1300, and (hopefully) other Epson-based scanners. Copyright 2007-2022 by m. allan noah Copyright 2009 by Richard Goedeken Development funded by Microdea, Inc., TrueCheck, Inc. and Archivista, GmbH -------------------------------------------------------------------------- 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, see . 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. -------------------------------------------------------------------------- The source code is divided in sections which you can easily find by searching for the tag "@@". Section 1 - Init & static stuff 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: v0, 2007-08-08, MAN - initial alpha release, S300 raw data only v1, 2007-09-03, MAN - only supports 300dpi duplex binary for S300 v2, 2007-09-05, MAN - add resolution option (only one choice) - add simplex option v3, 2007-09-12, MAN - add support for 150 dpi resolution v4, 2007-10-03, MAN - change binarization algo to use average of all channels v5, 2007-10-10, MAN - move data blocks to separate file - add basic fi-60F support (600dpi color) v6, 2007-11-12, MAN - move various data vars into transfer structs - move most of read_from_scanner to sane_read - add single line reads to calibration code - generate calibration buffer from above reads v7, 2007-12-05, MAN - split calibration into fine and coarse functions - add S300 fine calibration code - add S300 color and grayscale support v8, 2007-12-06, MAN - change sane_start to call ingest earlier - enable SOURCE_ADF_BACK - add if() around memcopy and better debugs in sane_read - shorten default scan sizes from 15.4 to 11.75 inches v9, 2007-12-17, MAN - fi-60F 300 & 600 dpi support (150 is non-square?) - fi-60F gray & binary support - fi-60F improved calibration v10, 2007-12-19, MAN (SANE v1.0.19) - fix missing function (and memory leak) v11 2008-02-14, MAN - sanei_config_read has already cleaned string (#310597) v12 2008-02-28, MAN - cleanup double free bug with new destroy() v13 2008-09-18, MAN - add working page-height control - add working brightness, contrast and threshold controls - add disabled threshold curve and geometry controls - move initialization code to sane_get_devices, for hotplugging v14 2008-09-24, MAN - support S300 on USB power - support S300 225x200 and 600x600 scans - support for automatic paper length detection (parm.lines = -1) v15 2008-09-24, MAN - expose hardware buttons/sensors as options for S300 v16 2008-10-01, MAN - split fill_frontback_buffers_S300 into 3 functions - enable threshold_curve option - add 1-D dynamic binary thresholding code - remove y-resolution option - pad 225x200 data to 225x225 v17 2008-10-03, MAN - increase scan height ~1/2 inch due to head offset - change page length autodetection condition v18 2009-01-21, MAN - don't export private symbols v19 2009-08-31, RG - rewritten calibration routines v20 2010-02-09, MAN (SANE 1.0.21 to 1.0.24) - cleanup #include lines & copyright - add S1300 v21 2011-04-15, MAN - unreleased attempt at S1100 support v22 2014-05-15, MAN/Hiroshi Miura - port some S1100 changes from v21 - add paper size support v23 2014-05-20, MAN - add S1300i support - fix buffer overruns in read_from_scanner - set default page width - simplified the 225x200 resolution code v24 2014-06-01, MAN - enable fine calibration for S1300i 225 & 300 dpi, and S300 150 dpi v25 2014-06-04, MAN - initial support for fi-65F - initial support for S1100 v26 2014-06-28, MAN - add resolution scaling - fix 150 dpi settings for fi-60F and fi-65F - make adf_height_padding variable - make white_factor variable v27 2015-01-24, MAN - don't override br_x and br_y - call change_params after changing page_width v28 2015-03-23, MAN - call get_hardware_status before starting scan v29 2017-03-18, MAN - fix infinite loop when scaling in Y direction v30 2017-03-21, MAN - fix image truncation when using 150 DPI in Y direction - add 200 and 400 DPI Y direction support for fi-60F/65F v31 2017-04-09, MAN - hardware gray support for fi-60F/65F (disabled pending calibration) - merge fi-60F/65F settings v32 2022-11-15, MAN - fix hanging scan when using source = ADF Back (fixes #601) v33 2022-11-17, MAN - S1300i: fix color plane offset at 225 and 330 dpi (fixes #538) SANE FLOW DIAGRAM - 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 . . - sane_read() : read image data (from pipe) . . (sane_read called multiple times; after sane_read returns EOF, . . loop may continue with sane_start which may return a 2nd page . . when doing duplex scans, or load the next page from the ADF) . . . . - sane_cancel() : cancel operation . - sane_close() : close opened scanner device - sane_exit() : terminate use of backend */ /* * @@ Section 1 - Init */ #include "../include/sane/config.h" #include /*memcpy...*/ #include /*isspace*/ #include /*tan*/ #include /*usleep*/ #include /*time*/ #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_config.h" #include "epjitsu.h" #include "epjitsu-cmd.h" #define DEBUG 1 #define BUILD 33 #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif #ifndef MAX #define MAX(a,b) ((a) > (b) ? (a) : (b)) #endif #ifndef MAX3 #define MAX3(a,b,c) ((a) > (b) ? ((a) > (c) ? a : c) : ((b) > (c) ? b : c)) #endif unsigned char global_firmware_filename[PATH_MAX]; /* values for SANE_DEBUG_EPJITSU env var: - errors 5 - function trace 10 - function detail 15 - get/setopt cmds 20 - usb cmd trace 25 - usb cmd detail 30 - useless noise 35 */ /* Calibration settings */ #define COARSE_OFFSET_TARGET 15 static int coarse_gain_min[3] = { 88, 88, 88 }; /* front, back, FI-60F 3rd plane */ static int coarse_gain_max[3] = { 92, 92, 92 }; static int fine_gain_target[3] = {185, 150, 170}; /* front, back, FI-60F is this ok? */ /* ------------------------------------------------------------------------- */ #define STRING_FLATBED SANE_I18N("Flatbed") #define STRING_ADFFRONT SANE_I18N("ADF Front") #define STRING_ADFBACK SANE_I18N("ADF Back") #define STRING_ADFDUPLEX SANE_I18N("ADF Duplex") #define STRING_LINEART SANE_VALUE_SCAN_MODE_LINEART #define STRING_GRAYSCALE SANE_VALUE_SCAN_MODE_GRAY #define STRING_COLOR SANE_VALUE_SCAN_MODE_COLOR /* * 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 scanner structs */ static const SANE_Device **sane_devArray = NULL; static struct scanner *scanner_devList = NULL; /* * @@ Section 2 - SANE & scanner init code */ /* * 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) { (void) authorize; /* get rid of compiler warning */ DBG_INIT (); DBG (10, "sane_init: start\n"); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); DBG (5, "sane_init: epjitsu backend %d.%d.%d, from %s\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD, PACKAGE_STRING); DBG (10, "sane_init: finish\n"); 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. * * Read the config file, find scanners with help from sanei_* * store in global device structs */ SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { SANE_Status ret = SANE_STATUS_GOOD; struct scanner * s; struct scanner * prev = NULL; char line[PATH_MAX]; const char *lp; FILE *fp; int num_devices=0; int i=0; (void) local_only; /* get rid of compiler warning */ DBG (10, "sane_get_devices: start\n"); /* mark all existing scanners as missing, attach_one will remove mark */ for (s = scanner_devList; s; s = s->next) { s->missing = 1; } sanei_usb_init(); fp = sanei_config_open (CONFIG_FILE); if (fp) { DBG (15, "sane_get_devices: reading config file %s\n", CONFIG_FILE); while (sanei_config_read (line, PATH_MAX, fp)) { lp = line; /* ignore comments */ if (*lp == '#') continue; /* skip empty lines */ if (*lp == 0) continue; if ((strncmp ("firmware", lp, 8) == 0) && isspace (lp[8])) { size_t firmware_len; lp += 8; lp = sanei_config_skip_whitespace (lp); DBG (15, "sane_get_devices: firmware '%s'\n", lp); firmware_len = strlen(lp); if (firmware_len > sizeof(global_firmware_filename) - 1) { DBG (5, "sane_get_devices: firmware file too long. ignoring '%s'\n", lp); } else { strcpy((char *)global_firmware_filename, lp); } } else if ((strncmp ("usb", lp, 3) == 0) && isspace (lp[3])) { DBG (15, "sane_get_devices: looking for '%s'\n", lp); sanei_usb_attach_matching_devices(lp, attach_one); } else{ DBG (5, "sane_get_devices: config line \"%s\" ignored.\n", lp); } } fclose (fp); } else { DBG (5, "sane_get_devices: no config file '%s'!\n", CONFIG_FILE); } /*delete missing scanners from list*/ for (s = scanner_devList; s;) { if(s->missing){ DBG (5, "sane_get_devices: missing scanner %s\n",s->sane.name); /*splice s out of list by changing pointer in prev to next*/ if(prev){ prev->next = s->next; free(s); s=prev->next; } /*remove s from head of list, using prev to cache it*/ else{ prev = s; s = s->next; free(prev); prev=NULL; /*reset head to next s*/ scanner_devList = s; } } else{ prev = s; s=prev->next; } } for (s = scanner_devList; s; s=s->next) { DBG (15, "sane_get_devices: found scanner %s\n",s->sane.name); num_devices++; } DBG (15, "sane_get_devices: found %d scanner(s)\n",num_devices); if (sane_devArray) free (sane_devArray); sane_devArray = calloc (num_devices + 1, sizeof (SANE_Device*)); if (!sane_devArray) return SANE_STATUS_NO_MEM; for (s = scanner_devList; s; s=s->next) { sane_devArray[i++] = (SANE_Device *)&s->sane; } sane_devArray[i] = 0; if(device_list){ *device_list = sane_devArray; } DBG (10, "sane_get_devices: finish\n"); return ret; } /* callback used by sane_init * build the scanner struct and link to global list * unless struct is already loaded, then pretend */ static SANE_Status attach_one (const char *name) { struct scanner *s; int ret, i; DBG (10, "attach_one: start '%s'\n", name); for (s = scanner_devList; s; s = s->next) { if (strcmp (s->sane.name, name) == 0) { DBG (10, "attach_one: already attached!\n"); s->missing = 0; return SANE_STATUS_GOOD; } } /* build a scanner struct to hold it */ DBG (15, "attach_one: init struct\n"); if ((s = calloc (sizeof (*s), 1)) == NULL) return SANE_STATUS_NO_MEM; /* copy the device name */ s->sane.name = strdup (name); if (!s->sane.name){ destroy(s); return SANE_STATUS_NO_MEM; } /* connect the fd */ DBG (15, "attach_one: connect fd\n"); s->fd = -1; ret = connect_fd(s); if(ret != SANE_STATUS_GOOD){ destroy(s); return ret; } /* load the firmware file into scanner */ ret = load_fw(s); if (ret != SANE_STATUS_GOOD) { destroy(s); DBG (5, "attach_one: firmware load failed\n"); return ret; } /* Now query the device to load its vendor/model/version */ ret = get_ident(s); if (ret != SANE_STATUS_GOOD) { destroy(s); DBG (5, "attach_one: identify failed\n"); return ret; } DBG (15, "attach_one: Found %s scanner %s at %s\n", s->sane.vendor, s->sane.model, s->sane.name); if (strstr (s->sane.model, "S1300i")){ unsigned char stat; DBG (15, "attach_one: Found S1300i\n"); stat = get_stat(s); if(stat & 0x01){ DBG (5, "attach_one: on USB power?\n"); s->usb_power=1; } s->model = MODEL_S1300i; s->has_adf = 1; s->has_adf_duplex = 1; s->min_res = 50; s->max_res = 600; s->adf_height_padding = 600; /* Blue, Red, Green */ s->white_factor[0] = 1.0; s->white_factor[1] = 0.93; s->white_factor[2] = 0.98; s->source = SOURCE_ADF_FRONT; s->mode = MODE_LINEART; s->resolution = 300; s->page_height = 11.5 * 1200; s->page_width = 8.5 * 1200; s->threshold = 120; s->threshold_curve = 55; } else if (strstr (s->sane.model, "S300") || strstr (s->sane.model, "S1300")){ unsigned char stat; DBG (15, "attach_one: Found S300/S1300\n"); stat = get_stat(s); if(stat & 0x01){ DBG (5, "attach_one: on USB power?\n"); s->usb_power=1; } s->model = MODEL_S300; s->has_adf = 1; s->has_adf_duplex = 1; s->min_res = 50; s->max_res = 600; s->adf_height_padding = 600; /* Blue, Red, Green */ s->white_factor[0] = 1.0; s->white_factor[1] = 0.93; s->white_factor[2] = 0.98; s->source = SOURCE_ADF_FRONT; s->mode = MODE_LINEART; s->resolution = 300; s->page_height = 11.5 * 1200; s->page_width = 8.5 * 1200; s->threshold = 120; s->threshold_curve = 55; } else if (strstr (s->sane.model, "S1100")){ DBG (15, "attach_one: Found S1100\n"); s->model = MODEL_S1100; s->usb_power = 1; s->has_adf = 1; s->has_adf_duplex = 0; s->min_res = 50; s->max_res = 600; s->adf_height_padding = 450; /* Blue, Red, Green */ s->white_factor[0] = 0.95; s->white_factor[1] = 1.0; s->white_factor[2] = 1.0; s->source = SOURCE_ADF_FRONT; s->mode = MODE_LINEART; s->resolution = 300; s->page_height = 11.5 * 1200; s->page_width = 8.5 * 1200; s->threshold = 120; s->threshold_curve = 55; } else if (strstr (s->sane.model, "fi-60F")){ DBG (15, "attach_one: Found fi-60F\n"); s->model = MODEL_FI60F; s->has_fb = 1; s->min_res = 50; s->max_res = 600; /* Blue, Red, Green */ s->white_factor[0] = 1.0; s->white_factor[1] = 0.93; s->white_factor[2] = 0.98; s->source = SOURCE_FLATBED; s->mode = MODE_COLOR; s->resolution = 300; s->page_height = 5.83 * 1200; s->page_width = 4.1 * 1200; s->threshold = 120; s->threshold_curve = 55; } else if (strstr (s->sane.model, "fi-65F")){ DBG (15, "attach_one: Found fi-65F\n"); s->model = MODEL_FI65F; s->has_fb = 1; s->min_res = 50; s->max_res = 600; /* Blue, Red, Green */ s->white_factor[0] = 1.0; s->white_factor[1] = 0.93; s->white_factor[2] = 0.98; s->source = SOURCE_FLATBED; s->mode = MODE_COLOR; s->resolution = 300; s->page_height = 5.83 * 1200; s->page_width = 4.1 * 1200; s->threshold = 120; s->threshold_curve = 55; } else{ DBG (15, "attach_one: Found other\n"); } /* set SANE option 'values' to good defaults */ DBG (15, "attach_one: init options\n"); /* go ahead and setup the first opt, because * frontend may call control_option on it * before calling get_option_descriptor */ 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; } 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; DBG (15, "attach_one: init settings\n"); ret = change_params(s); /* we close the connection, so that another backend can talk to scanner */ disconnect_fd(s); s->next = scanner_devList; scanner_devList = s; DBG (10, "attach_one: finish\n"); return SANE_STATUS_GOOD; } /* * connect the fd in the scanner struct */ static SANE_Status connect_fd (struct scanner *s) { SANE_Status ret; DBG (10, "connect_fd: start\n"); if(s->fd > -1){ DBG (5, "connect_fd: already open\n"); ret = SANE_STATUS_GOOD; } else { DBG (15, "connect_fd: opening USB device\n"); ret = sanei_usb_open (s->sane.name, &(s->fd)); } if(ret != SANE_STATUS_GOOD){ DBG (5, "connect_fd: could not open device: %d\n", ret); } DBG (10, "connect_fd: finish\n"); return ret; } /* * try to load fw into scanner */ static SANE_Status load_fw (struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; int file, i; int len = 0; unsigned char * buf; unsigned char cmd[4]; size_t cmdLen; unsigned char stat[2]; size_t statLen; DBG (10, "load_fw: start\n"); /*check status*/ /*reuse stat buffer*/ stat[0] = get_stat(s); if(stat[0] & 0x10){ DBG (5, "load_fw: firmware already loaded?\n"); return SANE_STATUS_GOOD; } if(!global_firmware_filename[0]){ DBG (5, "load_fw: missing filename\n"); return SANE_STATUS_NO_DOCS; } file = open((char *)global_firmware_filename,O_RDONLY); if(!file){ DBG (5, "load_fw: failed to open file %s\n",global_firmware_filename); return SANE_STATUS_NO_DOCS; } /* skip first 256 (=0x100) bytes */ if(lseek(file,0x100,SEEK_SET) != 0x100){ DBG (5, "load_fw: failed to lseek file %s\n",global_firmware_filename); close(file); return SANE_STATUS_NO_DOCS; } buf = malloc(FIRMWARE_LENGTH); if(!buf){ DBG (5, "load_fw: failed to alloc mem\n"); close(file); return SANE_STATUS_NO_MEM; } len = read(file,buf,FIRMWARE_LENGTH); close(file); if(len != FIRMWARE_LENGTH){ DBG (5, "load_fw: firmware file %s wrong length\n", global_firmware_filename); free(buf); return SANE_STATUS_NO_DOCS; } DBG (15, "load_fw: read firmware file %s ok\n", global_firmware_filename); /* firmware upload is in three commands */ /*start/status*/ cmd[0] = 0x1b; cmd[1] = 0x06; cmdLen = 2; statLen = 1; ret = do_cmd( s, 0, cmd, cmdLen, NULL, 0, stat, &statLen ); if(ret){ DBG (5, "load_fw: error on cmd 1\n"); free(buf); return ret; } if(stat[0] != 6){ DBG (5, "load_fw: bad stat on cmd 1\n"); free(buf); return SANE_STATUS_IO_ERROR; } /*length/data*/ cmd[0] = 0x01; cmd[1] = 0x00; cmd[2] = 0x01; cmd[3] = 0x00; cmdLen = 4; ret = do_cmd( s, 0, cmd, cmdLen, buf, FIRMWARE_LENGTH, NULL, 0 ); if(ret){ DBG (5, "load_fw: error on cmd 2\n"); free(buf); return ret; } /*checksum/status*/ cmd[0] = 0; for(i=0;i= 0; i--){ in[i] = 0; } s->sane.vendor = strndup((char *)in, 8); for (i = 23; (in[i] == ' ' || in[i] == 0xff) && i >= 8; i--){ in[i] = 0; } s->sane.model= strndup((char *)in+8, 24); s->sane.type = "scanner"; DBG (10, "get_ident: finish\n"); return ret; } /* * 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 scanner *dev = NULL; struct scanner *s = NULL; SANE_Status ret; DBG (10, "sane_open: start\n"); if(scanner_devList){ DBG (15, "sane_open: searching currently attached scanners\n"); } else{ DBG (15, "sane_open: no scanners currently attached, attaching\n"); ret = sane_get_devices(NULL,0); if(ret != SANE_STATUS_GOOD){ return ret; } } if(name[0] == 0){ DBG (15, "sane_open: no device requested, using default\n"); s = scanner_devList; } else{ DBG (15, "sane_open: device %s requested, attaching\n", name); for (dev = scanner_devList; dev; dev = dev->next) { if (strcmp (dev->sane.name, name) == 0) { s = dev; break; } } } if (!s) { DBG (5, "sane_open: no device found\n"); return SANE_STATUS_INVAL; } DBG (15, "sane_open: device %s found\n", s->sane.name); *handle = s; /* connect the fd so we can talk to scanner */ ret = connect_fd(s); if(ret != SANE_STATUS_GOOD){ return ret; } DBG (10, "sane_open: finish\n"); return SANE_STATUS_GOOD; } /* * @@ Section 3 - SANE Options functions */ /* * 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 scanner *s = handle; int i; SANE_Option_Descriptor *opt = &s->opt[option]; DBG (20, "sane_get_option_descriptor: %d\n", option); if ((unsigned) option >= NUM_OPTIONS) return NULL; /* "Mode" group -------------------------------------------------------- */ if(option==OPT_MODE_GROUP){ opt->title = "Scan Mode"; opt->desc = ""; opt->type = SANE_TYPE_GROUP; opt->constraint_type = SANE_CONSTRAINT_NONE; } /* source */ else if(option==OPT_SOURCE){ i=0; if(s->has_fb){ s->source_list[i++]=STRING_FLATBED; } if(s->has_adf){ s->source_list[i++]=STRING_ADFFRONT; if(s->has_adf_duplex){ s->source_list[i++]=STRING_ADFBACK; s->source_list[i++]=STRING_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); if(i > 1){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } } /* scan mode */ else if(option==OPT_MODE){ i=0; s->mode_list[i++]=STRING_LINEART; s->mode_list[i++]=STRING_GRAYSCALE; s->mode_list[i++]=STRING_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); if(i > 1){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } } else if(option==OPT_RES){ opt->name = SANE_NAME_SCAN_RESOLUTION; opt->title = SANE_TITLE_SCAN_RESOLUTION; opt->desc = SANE_DESC_SCAN_RESOLUTION; opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_DPI; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->res_range.min = s->min_res; s->res_range.max = s->max_res; s->res_range.quant = 1; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->res_range; } /* "Geometry" group ---------------------------------------------------- */ if(option==OPT_GEOMETRY_GROUP){ opt->name = SANE_NAME_GEOMETRY; opt->title = SANE_TITLE_GEOMETRY; opt->desc = SANE_DESC_GEOMETRY; opt->type = SANE_TYPE_GROUP; opt->constraint_type = SANE_CONSTRAINT_NONE; } /* 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(0); s->tl_x_range.max = SCANNER_UNIT_TO_FIXED_MM(MAX(0, get_page_width(s)-s->min_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; opt->cap = SANE_CAP_INACTIVE; } /* 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(0); s->tl_y_range.max = SCANNER_UNIT_TO_FIXED_MM(MAX(0, get_page_height(s)-s->min_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; } /* 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(MAX(s->min_x, get_page_width(s))); 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; opt->cap = SANE_CAP_INACTIVE; } /* 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(MAX(s->min_y, get_page_height(s))); 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; opt->cap = SANE_CAP_INACTIVE; } /* 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 = SANE_NAME_PAGE_WIDTH; opt->title = SANE_TITLE_PAGE_WIDTH; opt->desc = SANE_DESC_PAGE_WIDTH; 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->has_adf){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(s->source == SOURCE_FLATBED){ opt->cap |= SANE_CAP_INACTIVE; } } else{ opt->cap = SANE_CAP_INACTIVE; } } /* 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(0); s->paper_y_range.max = SCANNER_UNIT_TO_FIXED_MM(s->max_y); s->paper_y_range.quant = MM_PER_UNIT_FIX; opt->name = SANE_NAME_PAGE_HEIGHT; opt->title = SANE_TITLE_PAGE_HEIGHT; opt->desc = "Specifies the height of the media, 0 will auto-detect."; 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->has_adf){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(s->source == SOURCE_FLATBED){ opt->cap |= SANE_CAP_INACTIVE; } } else{ opt->cap = SANE_CAP_INACTIVE; } } /* "Enhancement" group ------------------------------------------------- */ if(option==OPT_ENHANCEMENT_GROUP){ opt->name = SANE_NAME_ENHANCEMENT; opt->title = SANE_TITLE_ENHANCEMENT; opt->desc = SANE_DESC_ENHANCEMENT; opt->type = SANE_TYPE_GROUP; opt->constraint_type = SANE_CONSTRAINT_NONE; } /* 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.quant=1; s->brightness_range.min=-127; s->brightness_range.max=127; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } /* 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.quant=1; s->contrast_range.min=-127; s->contrast_range.max=127; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } /* gamma */ if(option==OPT_GAMMA){ opt->name = "gamma"; opt->title = "Gamma function exponent"; opt->desc = "Changes intensity of midtones"; opt->type = SANE_TYPE_FIXED; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->gamma_range; /* value ranges from .3 to 5, should be log scale? */ s->gamma_range.quant=SANE_FIX(0.01); s->gamma_range.min=SANE_FIX(0.3); s->gamma_range.max=SANE_FIX(5); /*if (s->num_download_gamma){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; }*/ opt->cap = SANE_CAP_INACTIVE; } /*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=255; s->threshold_range.quant=1; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(s->mode != MODE_LINEART){ opt->cap |= SANE_CAP_INACTIVE; } } if(option==OPT_THRESHOLD_CURVE){ opt->name = "threshold-curve"; opt->title = "Threshold curve"; opt->desc = "Dynamic threshold curve, from light to dark, normally 50-65"; opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->threshold_curve_range; s->threshold_curve_range.min=0; s->threshold_curve_range.max=127; s->threshold_curve_range.quant=1; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(s->mode != MODE_LINEART){ opt->cap |= SANE_CAP_INACTIVE; } } /* "Sensor" group ------------------------------------------------------ */ if(option==OPT_SENSOR_GROUP){ opt->name = SANE_NAME_SENSORS; opt->title = SANE_TITLE_SENSORS; opt->desc = SANE_DESC_SENSORS; opt->type = SANE_TYPE_GROUP; opt->constraint_type = SANE_CONSTRAINT_NONE; /*flaming hack to get scanimage to hide group*/ if (!s->has_adf) opt->type = SANE_TYPE_BOOL; } if(option==OPT_SCAN_SW){ opt->name = SANE_NAME_SCAN; opt->title = SANE_TITLE_SCAN; opt->desc = SANE_DESC_SCAN; opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; if (s->has_adf) opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_HOPPER){ opt->name = SANE_NAME_PAGE_LOADED; opt->title = SANE_TITLE_PAGE_LOADED; opt->desc = SANE_DESC_PAGE_LOADED; opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; if (s->has_adf) opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_TOP){ opt->name = "top-edge"; 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_adf) opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_ADF_OPEN){ opt->name = SANE_NAME_COVER_OPEN; opt->title = SANE_TITLE_COVER_OPEN; opt->desc = SANE_DESC_COVER_OPEN; opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; if (s->has_adf) opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_SLEEP){ opt->name = "power-save"; opt->title = "Power saving"; opt->desc = "Scanner in power saving mode"; opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; if (s->has_adf) opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } return opt; } /** * 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 scanner *s = (struct scanner *) handle; SANE_Int dummy = 0; /* Make sure that all those statements involving *info cannot break (better * than having to do "if (info) ..." everywhere!) */ if (info == 0) info = &dummy; if (option >= NUM_OPTIONS) { DBG (5, "sane_control_option: %d too big\n", option); return SANE_STATUS_INVAL; } if (!SANE_OPTION_IS_ACTIVE (s->opt[option].cap)) { DBG (5, "sane_control_option: %d inactive\n", option); return SANE_STATUS_INVAL; } /* * 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; DBG (20, "sane_control_option: get value for '%s' (%d)\n", s->opt[option].name,option); switch (option) { case OPT_NUM_OPTS: *val_p = NUM_OPTIONS; return SANE_STATUS_GOOD; case OPT_SOURCE: if(s->source == SOURCE_FLATBED){ strcpy (val, STRING_FLATBED); } else if(s->source == SOURCE_ADF_FRONT){ strcpy (val, STRING_ADFFRONT); } else if(s->source == SOURCE_ADF_BACK){ strcpy (val, STRING_ADFBACK); } else if(s->source == SOURCE_ADF_DUPLEX){ strcpy (val, STRING_ADFDUPLEX); } else{ DBG(5,"missing option val for source\n"); } return SANE_STATUS_GOOD; case OPT_MODE: if(s->mode == MODE_LINEART){ strcpy (val, STRING_LINEART); } else if(s->mode == MODE_GRAYSCALE){ strcpy (val, STRING_GRAYSCALE); } else if(s->mode == MODE_COLOR){ strcpy (val, STRING_COLOR); } return SANE_STATUS_GOOD; case OPT_RES: *val_p = s->resolution; return SANE_STATUS_GOOD; case OPT_TL_X: *val_p = SCANNER_UNIT_TO_FIXED_MM(s->tl_x); return SANE_STATUS_GOOD; case OPT_TL_Y: *val_p = SCANNER_UNIT_TO_FIXED_MM(s->tl_y); return SANE_STATUS_GOOD; 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; 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_CONTRAST: *val_p = s->contrast; return SANE_STATUS_GOOD; case OPT_GAMMA: *val_p = SANE_FIX(s->gamma); return SANE_STATUS_GOOD; case OPT_THRESHOLD: *val_p = s->threshold; return SANE_STATUS_GOOD; case OPT_THRESHOLD_CURVE: *val_p = s->threshold_curve; return SANE_STATUS_GOOD; /* Sensor Group */ case OPT_SCAN_SW: get_hardware_status(s); *val_p = s->hw_scan_sw; return SANE_STATUS_GOOD; case OPT_HOPPER: get_hardware_status(s); *val_p = s->hw_hopper; return SANE_STATUS_GOOD; case OPT_TOP: get_hardware_status(s); *val_p = s->hw_top; 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; } } 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: can't set, device busy\n"); return SANE_STATUS_DEVICE_BUSY; } 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; } /* may have been changed by constraints, so don't copy until now */ val_c = *(SANE_Word *)val; /* * 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) { /* Mode Group */ case OPT_SOURCE: if (!strcmp (val, STRING_ADFFRONT)) { tmp = SOURCE_ADF_FRONT; } else if (!strcmp (val, STRING_ADFBACK)) { tmp = SOURCE_ADF_BACK; } else if (!strcmp (val, STRING_ADFDUPLEX)) { tmp = SOURCE_ADF_DUPLEX; } else{ tmp = SOURCE_FLATBED; } if (s->source == tmp) return SANE_STATUS_GOOD; s->source = tmp; *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_MODE: if (!strcmp (val, STRING_LINEART)) { tmp = MODE_LINEART; } else if (!strcmp (val, STRING_GRAYSCALE)) { tmp = MODE_GRAYSCALE; } else{ tmp = MODE_COLOR; } if (tmp == s->mode) return SANE_STATUS_GOOD; s->mode = tmp; *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_RES: if (s->resolution == val_c) return SANE_STATUS_GOOD; s->resolution = val_c; *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; /* Geometry Group */ case OPT_TL_X: if (s->tl_x == FIXED_MM_TO_SCANNER_UNIT(val_c)) return SANE_STATUS_GOOD; s->tl_x = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_TL_Y: if (s->tl_y == FIXED_MM_TO_SCANNER_UNIT(val_c)) return SANE_STATUS_GOOD; s->tl_y = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return change_params(s); case OPT_BR_X: if (s->br_x == FIXED_MM_TO_SCANNER_UNIT(val_c)) return SANE_STATUS_GOOD; s->br_x = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_BR_Y: if (s->br_y == FIXED_MM_TO_SCANNER_UNIT(val_c)) return SANE_STATUS_GOOD; s->br_y = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_PAGE_WIDTH: if (s->page_width == FIXED_MM_TO_SCANNER_UNIT(val_c)) return SANE_STATUS_GOOD; s->page_width = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return change_params(s); case OPT_PAGE_HEIGHT: if (s->page_height == FIXED_MM_TO_SCANNER_UNIT(val_c)) return SANE_STATUS_GOOD; s->page_height = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return change_params(s); /* Enhancement Group */ case OPT_BRIGHTNESS: s->brightness = val_c; return SANE_STATUS_GOOD; case OPT_CONTRAST: s->contrast = val_c; return SANE_STATUS_GOOD; case OPT_GAMMA: s->gamma = SANE_UNFIX(val_c); return SANE_STATUS_GOOD; case OPT_THRESHOLD: s->threshold = val_c; return SANE_STATUS_GOOD; case OPT_THRESHOLD_CURVE: s->threshold_curve = val_c; return SANE_STATUS_GOOD; } /* switch */ } /* else */ return SANE_STATUS_INVAL; } /* use height and width to initialize rest of transfer vals */ static void update_transfer_totals(struct transfer * t) { if (t->image == NULL) return; t->total_bytes = t->line_stride * t->image->height; t->rx_bytes = 0; t->done = 0; } /* each model has various settings that differ based on X resolution */ /* we hard-code the list (determined from usb snoops) here */ struct model_res { int model; int mode; int x_res; int y_res; int usb_power; int max_x; int min_x; int max_y; int min_y; int line_stride; /* byte width of 1 raw side, with padding */ int plane_stride; /* byte width of 1 raw color plane, with padding */ int plane_width; /* byte width of 1 raw color plane, without padding */ int block_height; int cal_line_stride; int cal_plane_stride; int cal_plane_width; unsigned char * sw_coarsecal; unsigned char * sw_finecal; unsigned char * sw_sendcal; unsigned char * head_cal1; unsigned char * head_cal2; unsigned char * sw_scan; }; static struct model_res settings[] = { /*S300 AC*/ /* model mode xres yres u mxx mnx mxy mny lin_s pln_s pln_w bh cls cps cpw */ { MODEL_S300, MODE_COLOR, 150, 150, 0, 1296, 32, 2662, 32, 4256*3, 1480*3, 1296, 41, 8512*3, 2960*3, 2592, setWindowCoarseCal_S300_150, setWindowFineCal_S300_150, setWindowSendCal_S300_150, sendCal1Header_S300_150, sendCal2Header_S300_150, setWindowScan_S300_150 }, { MODEL_S300, MODE_COLOR, 225, 200, 0, 1944, 32, 3993, 32, 6144*3, 2100*3, 1944, 28, 8192*3, 2800*3, 2592, setWindowCoarseCal_S300_225, setWindowFineCal_S300_225, setWindowSendCal_S300_225, sendCal1Header_S300_225, sendCal2Header_S300_225, setWindowScan_S300_225 }, { MODEL_S300, MODE_COLOR, 300, 300, 0, 2592, 32, 5324, 32, 8192*3, 2800*3, 2592, 21, 8192*3, 2800*3, 2592, setWindowCoarseCal_S300_300, setWindowFineCal_S300_300, setWindowSendCal_S300_300, sendCal1Header_S300_300, sendCal2Header_S300_300, setWindowScan_S300_300 }, { MODEL_S300, MODE_COLOR, 600, 600, 0, 5184, 32, 10648, 32, 16064*3, 5440*3, 5184, 10, 16064*3, 5440*3, 5184, setWindowCoarseCal_S300_600, setWindowFineCal_S300_600, setWindowSendCal_S300_600, sendCal1Header_S300_600, sendCal2Header_S300_600, setWindowScan_S300_600 }, /*S300 USB*/ /* model mode xres yres u mxx mnx mxy mny lin_s pln_s pln_w bh cls cps cpw */ { MODEL_S300, MODE_COLOR, 150, 150, 1, 1296, 32, 2662, 32, 7216*3, 2960*3, 1296, 24, 14432*3, 5920*3, 2592, setWindowCoarseCal_S300_150_U, setWindowFineCal_S300_150_U, setWindowSendCal_S300_150_U, sendCal1Header_S300_150_U, sendCal2Header_S300_150_U, setWindowScan_S300_150_U }, { MODEL_S300, MODE_COLOR, 225, 200, 1, 1944, 32, 3993, 32, 10584*3, 4320*3, 1944, 16, 14112*3, 5760*3, 2592, setWindowCoarseCal_S300_225_U, setWindowFineCal_S300_225_U, setWindowSendCal_S300_225_U, sendCal1Header_S300_225_U, sendCal2Header_S300_225_U, setWindowScan_S300_225_U }, { MODEL_S300, MODE_COLOR, 300, 300, 1, 2592, 32, 5324, 32, 15872*3, 6640*3, 2592, 11, 15872*3, 6640*3, 2592, setWindowCoarseCal_S300_300_U, setWindowFineCal_S300_300_U, setWindowSendCal_S300_300_U, sendCal1Header_S300_300_U, sendCal2Header_S300_300_U, setWindowScan_S300_300_U }, { MODEL_S300, MODE_COLOR, 600, 600, 1, 5184, 32, 10648, 32, 16064*3, 5440*3, 5184, 10, 16064*3, 5440*3, 5184, setWindowCoarseCal_S300_600, setWindowFineCal_S300_600, setWindowSendCal_S300_600, sendCal1Header_S300_600, sendCal2Header_S300_600, setWindowScan_S300_600 }, /*S1300i AC*/ /* model mode xres yres u mxx mnx mxy mny lin_s pln_s pln_w bh cls cps cpw */ { MODEL_S1300i, MODE_COLOR, 150, 150, 0, 1296, 32, 2662, 32, 4016*3, 1360*3, 1296, 43, 8032*3, 2720*3, 2592, setWindowCoarseCal_S1300i_150, setWindowFineCal_S1300i_150, setWindowSendCal_S1300i_150, sendCal1Header_S1300i_150, sendCal2Header_S1300i_150, setWindowScan_S1300i_150 }, { MODEL_S1300i, MODE_COLOR, 225, 200, 0, 1944, 32, 3993, 32, 6072*3, 2063*3, 1944, 28, 8096*3, 2752*3, 2592, setWindowCoarseCal_S1300i_225, setWindowFineCal_S1300i_225, setWindowSendCal_S1300i_225, sendCal1Header_S1300i_225, sendCal2Header_S1300i_225, setWindowScan_S1300i_225 }, { MODEL_S1300i, MODE_COLOR, 300, 300, 0, 2592, 32, 5324, 32, 8096*3, 2751*3, 2592, 21, 8096*3, 2752*3, 2592, setWindowCoarseCal_S1300i_300, setWindowFineCal_S1300i_300, setWindowSendCal_S1300i_300, sendCal1Header_S1300i_300, sendCal2Header_S1300i_300, setWindowScan_S1300i_300 }, /*NOTE: S1300i uses S300 data blocks for remainder*/ { MODEL_S1300i, MODE_COLOR, 600, 600, 0, 5184, 32, 10648, 32, 16064*3, 5440*3, 5184, 10, 16064*3, 5440*3, 5184, setWindowCoarseCal_S300_600, setWindowFineCal_S300_600, setWindowSendCal_S300_600, sendCal1Header_S300_600, sendCal2Header_S300_600, setWindowScan_S300_600 }, /*S1300i USB*/ /* model mode xres yres u mxx mnx mxy mny lin_s pln_s pln_w bh cls cps cpw */ { MODEL_S1300i, MODE_COLOR, 150, 150, 1, 1296, 32, 2662, 32, 7216*3, 2960*3, 1296, 24, 14432*3, 5920*3, 2592, setWindowCoarseCal_S300_150_U, setWindowFineCal_S300_150_U, setWindowSendCal_S300_150_U, sendCal1Header_S1300i_USB, sendCal2Header_S1300i_USB, setWindowScan_S300_150_U }, { MODEL_S1300i, MODE_COLOR, 225, 200, 1, 1944, 32, 3993, 32, 10584*3, 4320*3, 1944, 16, 14112*3, 5760*3, 2592, setWindowCoarseCal_S300_225_U, setWindowFineCal_S300_225_U, setWindowSendCal_S300_225_U, sendCal1Header_S1300i_USB, sendCal2Header_S1300i_USB, setWindowScan_S300_225_U }, { MODEL_S1300i, MODE_COLOR, 300, 300, 1, 2592, 32, 5324, 32, 15872*3, 6640*3, 2592, 11, 15872*3, 6640*3, 2592, setWindowCoarseCal_S300_300_U, setWindowFineCal_S300_300_U, setWindowSendCal_S300_300_U, sendCal1Header_S1300i_USB, sendCal2Header_S1300i_USB, setWindowScan_S300_300_U }, { MODEL_S1300i, MODE_COLOR, 600, 600, 1, 5184, 32, 10648, 32, 16064*3, 5440*3, 5184, 10, 16064*3, 5440*3, 5184, setWindowCoarseCal_S300_600, setWindowFineCal_S300_600, setWindowSendCal_S300_600, sendCal1Header_S1300i_USB, sendCal2Header_S1300i_USB, setWindowScan_S300_600 }, /*fi-60F/65F GRAY */ /* model mode xres yres u mxx mnx mxy mny lin_s pln_s pln_w bh cls cps cpw */ /* disabled until calibration code supports grayscale { MODEL_FI60F | MODEL_FI65F, MODE_GRAYSCALE, 300, 300, 0, 1296, 32, 1749, 32, 1440, 480, 432, 364, 2400*3, 958*3, 432, setWindowCoarseCal_FI60F_300, setWindowFineCal_FI60F_300, setWindowSendCal_FI60F_300, sendCal1Header_FI60F_300, sendCal2Header_FI60F_300, setWindowScan_FI60F_300_g }, { MODEL_FI60F | MODEL_FI65F, MODE_GRAYSCALE, 600, 400, 0, 2592, 32, 2332, 32, 2592, 864, 864, 202, 2848*3, 978*3, 864, setWindowCoarseCal_FI60F_600, setWindowFineCal_FI60F_600, setWindowSendCal_FI60F_600, sendCal1Header_FI60F_600, sendCal2Header_FI60F_600, setWindowScan_FI60F_400_g }, { MODEL_FI60F | MODEL_FI65F, MODE_GRAYSCALE, 600, 600, 0, 2592, 32, 3498, 32, 2592, 864, 864, 202, 2848*3, 978*3, 864, setWindowCoarseCal_FI60F_600, setWindowFineCal_FI60F_600, setWindowSendCal_FI60F_600, sendCal1Header_FI60F_600, sendCal2Header_FI60F_600, setWindowScan_FI60F_600_g }, */ /*fi-60F/65F*/ /* model mode xres yres u mxx mnx mxy mny lin_s pln_s pln_w bh cls cps cpw */ { MODEL_FI60F | MODEL_FI65F, MODE_COLOR, 300, 150, 0, 1296, 32, 875, 32, 2400*3, 958*3, 432, 72, 2400*3, 958*3, 432, setWindowCoarseCal_FI60F_300, setWindowFineCal_FI60F_300, setWindowSendCal_FI60F_300, sendCal1Header_FI60F_300, sendCal2Header_FI60F_300, setWindowScan_FI60F_150 }, { MODEL_FI60F | MODEL_FI65F, MODE_COLOR, 300, 200, 0, 1296, 32, 1166, 32, 2400*3, 958*3, 432, 72, 2400*3, 958*3, 432, setWindowCoarseCal_FI60F_300, setWindowFineCal_FI60F_300, setWindowSendCal_FI60F_300, sendCal1Header_FI60F_300, sendCal2Header_FI60F_300, setWindowScan_FI60F_200 }, { MODEL_FI60F | MODEL_FI65F, MODE_COLOR, 300, 300, 0, 1296, 32, 1749, 32, 2400*3, 958*3, 432, 72, 2400*3, 958*3, 432, setWindowCoarseCal_FI60F_300, setWindowFineCal_FI60F_300, setWindowSendCal_FI60F_300, sendCal1Header_FI60F_300, sendCal2Header_FI60F_300, setWindowScan_FI60F_300 }, { MODEL_FI60F | MODEL_FI65F, MODE_COLOR, 600, 400, 0, 2592, 32, 2332, 32, 2848*3, 978*3, 864, 61, 2848*3, 978*3, 864, setWindowCoarseCal_FI60F_600, setWindowFineCal_FI60F_600, setWindowSendCal_FI60F_600, sendCal1Header_FI60F_600, sendCal2Header_FI60F_600, setWindowScan_FI60F_400 }, { MODEL_FI60F | MODEL_FI65F, MODE_COLOR, 600, 600, 0, 2592, 32, 3498, 32, 2848*3, 978*3, 864, 61, 2848*3, 978*3, 864, setWindowCoarseCal_FI60F_600, setWindowFineCal_FI60F_600, setWindowSendCal_FI60F_600, sendCal1Header_FI60F_600, sendCal2Header_FI60F_600, setWindowScan_FI60F_600 }, /*S1100 USB*/ /* model mode xres yres u mxx mnx mxy mny lin_s pln_s pln_w bh cls cps cpw */ { MODEL_S1100, MODE_COLOR, 300, 300, 1, 2592, 32, 5324, 32, 8912, 3160, 2592, 58, 8912, 3160, 2592, setWindowCoarseCal_S1100_300_U, setWindowFineCal_S1100_300_U, setWindowSendCal_S1100_300_U, sendCal1Header_S1100_300_U, sendCal2Header_S1100_300_U, setWindowScan_S1100_300_U }, { MODEL_S1100, MODE_COLOR, 600, 600, 1, 5184, 32, 10648, 32, 15904, 5360, 5184, 32, 15904, 5360, 5184, setWindowCoarseCal_S1100_600_U, setWindowFineCal_S1100_600_U, setWindowSendCal_S1100_600_U, sendCal1Header_S1100_600_U, sendCal2Header_S1100_600_U, setWindowScan_S1100_600_U }, { MODEL_NONE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL }, }; /* * clean up scanner struct vals when user changes mode, res, etc */ static SANE_Status change_params(struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; int img_heads, img_pages, width; int i=0; DBG (10, "change_params: start\n"); do { if(settings[i].model & s->model && settings[i].mode <= s->mode && settings[i].x_res >= s->resolution && settings[i].y_res >= s->resolution && settings[i].usb_power == s->usb_power ){ break; } i++; } while (settings[i].model); if (!settings[i].model){ return SANE_STATUS_INVAL; } /*1200 dpi*/ s->max_x = PIX_TO_SCANNER_UNIT( settings[i].max_x, settings[i].x_res ); s->min_x = PIX_TO_SCANNER_UNIT( settings[i].min_x, settings[i].x_res ); s->max_y = PIX_TO_SCANNER_UNIT( settings[i].max_y, settings[i].y_res ); s->min_y = PIX_TO_SCANNER_UNIT( settings[i].min_y, settings[i].y_res ); /*current dpi*/ s->setWindowCoarseCal = settings[i].sw_coarsecal; s->setWindowCoarseCalLen = SET_WINDOW_LEN; s->setWindowFineCal = settings[i].sw_finecal; s->setWindowFineCalLen = SET_WINDOW_LEN; s->setWindowSendCal = settings[i].sw_sendcal; s->setWindowSendCalLen = SET_WINDOW_LEN; s->sendCal1Header = settings[i].head_cal1; s->sendCal1HeaderLen = 14; s->sendCal2Header = settings[i].head_cal2; s->sendCal2HeaderLen = 7; s->setWindowScan = settings[i].sw_scan; s->setWindowScanLen = SET_WINDOW_LEN; if (s->model == MODEL_S300 || s->model == MODEL_S1300i) { img_heads = 1; /* image width is the same as the plane width on the S300 */ img_pages = 2; } else if (s->model == MODEL_S1100) { img_heads = 1; /* image width is the same as the plane width on the S1000 */ img_pages = 1; } else /* MODEL_FI60F or MODEL_FI65F */ { img_heads = 3; /* image width is 3* the plane width on the FI-60F */ img_pages = 1; } /* height */ if (s->tl_y > s->max_y - s->min_y) s->tl_y = s->max_y - s->min_y - s->adf_height_padding; s->page_height = MIN(s->page_height, s->max_y - s->adf_height_padding - s->tl_y); if (s->page_height > 0) s->page_height = MAX(s->page_height, s->min_y); if (s->tl_y + s->page_height > s->max_y) s->tl_y = s->max_y - s->adf_height_padding - s->page_height; s->tl_y = MAX(s->tl_y, 0); if (s->page_height > 0) { s->br_y = s->tl_y + s->page_height; } else { s->br_y = s->max_y; } /*width*/ s->page_width = MIN(s->page_width, s->max_x); s->page_width = MAX(s->page_width, s->min_x); s->tl_x = (s->max_x - s->page_width)/2; s->br_x = (s->max_x + s->page_width)/2; /*=============================================================*/ /* set up the calibration scan structs */ /* generally full width, short height, full resolution */ s->cal_image.line_stride = settings[i].cal_line_stride; s->cal_image.plane_stride = settings[i].cal_plane_stride; s->cal_image.plane_width = settings[i].cal_plane_width; s->cal_image.mode = MODE_COLOR; s->cal_image.x_res = settings[i].x_res; s->cal_image.y_res = settings[i].y_res; s->cal_image.raw_data = NULL; s->cal_image.image = NULL; /* width is the same, but there are 2 bytes per pixel component */ s->cal_data.line_stride = settings[i].cal_line_stride * 2; s->cal_data.plane_stride = settings[i].cal_plane_stride * 2; s->cal_data.plane_width = settings[i].cal_plane_width; s->cal_data.mode = MODE_COLOR; s->cal_data.x_res = settings[i].x_res; s->cal_data.y_res = settings[i].y_res; s->cal_data.raw_data = NULL; s->cal_data.image = &s->sendcal; /*=============================================================*/ /* set up the calibration image blocks */ width = s->cal_image.plane_width * img_heads; s->coarsecal.width_pix = s->darkcal.width_pix = s->lightcal.width_pix = width; s->coarsecal.width_bytes = s->darkcal.width_bytes = s->lightcal.width_bytes = width * 3; s->coarsecal.height = 1; s->coarsecal.mode = MODE_COLOR; s->coarsecal.x_res = s->darkcal.x_res = s->lightcal.x_res = settings[i].x_res; s->coarsecal.y_res = s->darkcal.y_res = s->lightcal.y_res = settings[i].y_res; s->darkcal.height = s->lightcal.height = 16; s->coarsecal.pages = s->darkcal.pages = s->lightcal.pages = img_pages; s->coarsecal.buffer = s->darkcal.buffer = s->lightcal.buffer = NULL; /* set up the calibration data block */ width = s->cal_data.plane_width * img_heads; s->sendcal.width_pix = width; s->sendcal.width_bytes = width * 6; /* 2 bytes of cal data per pixel component */ s->sendcal.height = 1; s->sendcal.mode = MODE_COLOR; s->sendcal.x_res = settings[i].x_res; s->sendcal.y_res = settings[i].y_res; s->sendcal.pages = img_pages; s->sendcal.buffer = NULL; /*=============================================================*/ /* set up the fullscan parameters */ /* this is bookkeeping for what we actually pull from the scanner */ /* note that this has no image, just dimensions and counters */ s->fullscan.width_bytes = settings[i].line_stride; s->fullscan.mode = settings[i].mode; s->fullscan.x_res = settings[i].x_res; s->fullscan.y_res = settings[i].y_res; if(s->source == SOURCE_FLATBED || !s->page_height) { /* flatbed and adf in autodetect always ask for all*/ s->fullscan.height = SCANNER_UNIT_TO_PIX(s->max_y, s->fullscan.y_res); } else { /* adf with specified paper size requires padding on top of page_height (~1/2in) */ s->fullscan.height = SCANNER_UNIT_TO_PIX((s->page_height + s->tl_y + s->adf_height_padding), s->fullscan.y_res); } /*=============================================================*/ /* set up the input block raw struct */ /* this holds up to 512k of raw scan data */ s->block_xfr.line_stride = settings[i].line_stride; s->block_xfr.plane_stride = settings[i].plane_stride; s->block_xfr.plane_width = settings[i].plane_width; s->block_xfr.mode = settings[i].mode; s->block_xfr.x_res = settings[i].x_res; s->block_xfr.y_res = settings[i].y_res; s->block_xfr.raw_data = NULL; s->block_xfr.image = &s->block_img; /* set up the input block image struct */ /* note that this is the same width/x_res as the final output image */ /* but the mode, height and y_res are the same as block_xfr */ width = (settings[i].max_x * s->resolution / settings[i].x_res); s->block_img.width_pix = width; s->block_img.width_bytes = width * (settings[i].mode == MODE_COLOR ? 3 : 1); s->block_img.height = settings[i].block_height; s->block_img.mode = settings[i].mode; s->block_img.x_res = s->resolution; s->block_img.y_res = settings[i].y_res; s->block_img.pages = img_pages; s->block_img.buffer = NULL; /*=============================================================*/ /* set up the output image structs */ /* output image might be different from scan due to interpolation */ s->front.mode = s->mode; s->front.x_res = s->resolution; s->front.y_res = s->resolution; if(s->source == SOURCE_FLATBED) { /* flatbed ignores the tly */ s->front.height = SCANNER_UNIT_TO_PIX(s->max_y - s->tl_y, s->front.y_res); } else if(!s->page_height) { /* adf in autodetect always asks for all */ s->front.height = SCANNER_UNIT_TO_PIX(s->max_y, s->front.y_res); } else { /* adf with specified paper size */ s->front.height = SCANNER_UNIT_TO_PIX(s->page_height, s->front.y_res); } s->front.width_pix = SCANNER_UNIT_TO_PIX(s->page_width, s->resolution * img_heads); s->front.x_start_offset = (s->block_xfr.image->width_pix - s->front.width_pix)/2; switch (s->mode) { case MODE_COLOR: s->front.width_bytes = s->front.width_pix*3; s->front.x_offset_bytes = s->front.x_start_offset *3; break; case MODE_GRAYSCALE: s->front.width_bytes = s->front.width_pix; s->front.x_offset_bytes = s->front.x_start_offset; break; default: /*binary*/ s->front.width_bytes = s->front.width_pix/8; s->front.width_pix = s->front.width_bytes * 8; /*s->page_width = PIX_TO_SCANNER_UNIT(s->front.width_pix, (img_heads * s->resolution_x));*/ s->front.x_offset_bytes = s->front.x_start_offset/8; break; } /* ADF front need to remove padding header */ if (s->source != SOURCE_FLATBED) { s->front.y_skip_offset = SCANNER_UNIT_TO_PIX(s->tl_y+s->adf_height_padding, s->fullscan.y_res); } else { s->front.y_skip_offset = SCANNER_UNIT_TO_PIX(s->tl_y, s->fullscan.y_res); } s->front.pages = 1; s->front.buffer = NULL; /* back settings always same as front settings */ s->back.width_pix = s->front.width_pix; s->back.width_bytes = s->front.width_bytes; s->back.mode = s->front.mode; s->back.x_res = s->front.x_res; s->back.y_res = s->front.y_res; s->back.height = s->front.height; s->back.x_start_offset = s->front.x_start_offset; s->back.x_offset_bytes = s->front.x_offset_bytes; s->back.y_skip_offset = SCANNER_UNIT_TO_PIX(s->tl_y, s->fullscan.y_res); s->back.pages = 1; s->back.buffer = NULL; /* dynamic threshold temp buffer, in gray */ s->dt.width_pix = s->front.width_pix; s->dt.width_bytes = s->front.width_pix; s->dt.mode = MODE_GRAYSCALE; s->dt.x_res = s->front.x_res; s->dt.y_res = s->front.y_res; s->dt.height = 1; s->dt.pages = 1; s->dt.buffer = NULL; /* set up the pointers to the page images in the page structs */ s->pages[SIDE_FRONT].image = &s->front; s->pages[SIDE_BACK].image = &s->back; s->pages[SIDE_FRONT].done = 0; s->pages[SIDE_BACK].done = 0; DBG (10, "change_params: finish\n"); return ret; } /* Function to build a lookup table (LUT), often used by scanners to implement brightness/contrast/gamma or by backends to speed binarization/thresholding offset and slope inputs are -127 to +127 slope rotates line around central input/output val, 0 makes horizontal line pos zero neg . x . . x . x . . x out . x .xxxxxxxxxxx . x . x . . x ....x....... ............ .......x.... in in in offset moves line vertically, and clamps to output range 0 keeps the line crossing the center of the table high low . xxxxxxxx . . x . out x . x . . x ............ xxxxxxxx.... in in out_min/max provide bounds on output values, useful when building thresholding lut. 0 and 255 are good defaults otherwise. */ static SANE_Status load_lut (unsigned char * lut, int in_bits, int out_bits, int out_min, int out_max, int slope, int offset) { SANE_Status ret = SANE_STATUS_GOOD; int i, j; double shift, rise; int max_in_val = (1 << in_bits) - 1; int max_out_val = (1 << out_bits) - 1; unsigned char * lut_p = lut; DBG (10, "load_lut: start\n"); /* slope is converted to rise per unit run: * first [-127,127] to [-1,1] * then multiply by PI/2 to convert to radians * then take the tangent (T.O.A) * then multiply by the normal linear slope * because the table may not be square, i.e. 1024x256*/ rise = tan((double)slope/127 * M_PI/2) * max_out_val / max_in_val; /* line must stay vertically centered, so figure * out vertical offset at central input value */ shift = (double)max_out_val/2 - (rise*max_in_val/2); /* convert the user offset setting to scale of output * first [-127,127] to [-1,1] * then to [-max_out_val/2,max_out_val/2]*/ shift += (double)offset / 127 * max_out_val / 2; for(i=0;i<=max_in_val;i++){ j = rise*i + shift; j = MAX(j, out_min); j = MIN(j, out_max); *lut_p=j; lut_p++; } hexdump(5, "load_lut: ", lut, max_in_val+1); DBG (10, "load_lut: finish\n"); return ret; } /* * @@ 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 scanner *s = (struct scanner *) handle; DBG (10, "sane_get_parameters: start\n"); params->pixels_per_line = s->front.width_pix; params->bytes_per_line = s->front.width_bytes; if(!s->page_height){ params->lines = -1; } else{ params->lines = s->front.height; } params->last_frame = 1; if (s->mode == MODE_COLOR) { params->format = SANE_FRAME_RGB; params->depth = 8; } else if (s->mode == MODE_GRAYSCALE) { params->format = SANE_FRAME_GRAY; params->depth = 8; } else if (s->mode == MODE_LINEART) { params->format = SANE_FRAME_GRAY; params->depth = 1; } 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); DBG (10, "sane_get_parameters: finish\n"); return SANE_STATUS_GOOD; } /* * Called by SANE when a page acquisition operation is to be started. * FIXME: won't handle SOURCE_ADF_BACK */ SANE_Status sane_start (SANE_Handle handle) { struct scanner *s = handle; SANE_Status ret; int i; DBG (10, "sane_start: start\n"); /* set side marker on first page */ if(!s->started){ if(s->source == SOURCE_ADF_BACK){ s->side = SIDE_BACK; } else{ s->side = SIDE_FRONT; } } /* if already running, duplex needs to switch sides */ else if(s->source == SOURCE_ADF_DUPLEX){ s->side = !s->side; } /* recent scanners need ghs called before scanning */ ret = get_hardware_status(s); /* ingest paper with adf */ if( s->source == SOURCE_ADF_BACK || s->source == SOURCE_ADF_FRONT || (s->source == SOURCE_ADF_DUPLEX && s->side == SIDE_FRONT) ){ ret = object_position(s,EPJITSU_PAPER_INGEST); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: failed to ingest\n"); sane_cancel((SANE_Handle)s); return ret; } } /* first page requires buffers, etc */ if(!s->started){ DBG(15,"sane_start: first page\n"); s->started=1; ret = teardown_buffers(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: failed to teardown buffers\n"); sane_cancel((SANE_Handle)s); return SANE_STATUS_NO_MEM; } ret = change_params(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: failed to change_params\n"); sane_cancel((SANE_Handle)s); return SANE_STATUS_NO_MEM; } ret = setup_buffers(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: failed to setup buffers\n"); sane_cancel((SANE_Handle)s); return SANE_STATUS_NO_MEM; } ret = load_lut(s->dt_lut, 8, 8, 50, 205, s->threshold_curve, s->threshold-127); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: failed to load_lut for dt\n"); sane_cancel((SANE_Handle)s); return ret; } ret = coarsecal(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: failed to coarsecal\n"); sane_cancel((SANE_Handle)s); return ret; } ret = finecal(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: failed to finecal\n"); sane_cancel((SANE_Handle)s); return ret; } ret = send_lut(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: failed to send lut\n"); sane_cancel((SANE_Handle)s); return ret; } ret = lamp(s,1); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: failed to heat lamp\n"); sane_cancel((SANE_Handle)s); return ret; } /*should this be between each page*/ ret = set_window(s,WINDOW_SCAN); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: failed to set window\n"); sane_cancel((SANE_Handle)s); return ret; } } /* reset everything when starting any front, or just back */ if(s->side == SIDE_FRONT || s->source == SOURCE_ADF_BACK){ DBG(15,"sane_start: reset counters\n"); /* reset scan */ s->fullscan.done = 0; s->fullscan.rx_bytes = 0; s->fullscan.total_bytes = s->fullscan.width_bytes * s->fullscan.height; /* reset block */ update_transfer_totals(&s->block_xfr); /* reset front and back page counters */ for (i = 0; i < 2; i++) { struct image *page_img = s->pages[i].image; s->pages[i].bytes_total = page_img->width_bytes * page_img->height; s->pages[i].bytes_scanned = 0; s->pages[i].bytes_read = 0; s->pages[i].lines_rx = 0; s->pages[i].lines_pass = 0; s->pages[i].lines_tx = 0; s->pages[i].done = 0; } ret = scan(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: failed to start scan\n"); sane_cancel((SANE_Handle)s); return ret; } } else{ DBG(15,"sane_start: back side\n"); } DBG (10, "sane_start: finish\n"); return SANE_STATUS_GOOD; } /* the +8 on all the lengths is to makeup for potential block trailers */ static SANE_Status setup_buffers(struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; DBG (10, "setup_buffers: start\n"); /* temporary cal data */ s->coarsecal.buffer = calloc (1,s->coarsecal.width_bytes * s->coarsecal.height * s->coarsecal.pages); if(!s->coarsecal.buffer){ DBG (5, "setup_buffers: ERROR: failed to setup coarse cal buffer\n"); return SANE_STATUS_NO_MEM; } s->darkcal.buffer = calloc (1,s->darkcal.width_bytes * s->darkcal.height * s->darkcal.pages); if(!s->darkcal.buffer){ DBG (5, "setup_buffers: ERROR: failed to setup fine cal buffer\n"); return SANE_STATUS_NO_MEM; } s->lightcal.buffer = calloc (1,s->lightcal.width_bytes * s->lightcal.height * s->lightcal.pages); if(!s->lightcal.buffer){ DBG (5, "setup_buffers: ERROR: failed to setup fine cal buffer\n"); return SANE_STATUS_NO_MEM; } s->sendcal.buffer = calloc (1,s->sendcal.width_bytes * s->sendcal.height * s->sendcal.pages); if(!s->sendcal.buffer){ DBG (5, "setup_buffers: ERROR: failed to setup send cal buffer\n"); return SANE_STATUS_NO_MEM; } s->cal_image.raw_data = calloc(1, s->cal_image.line_stride * 16 + 8); /* maximum 16 lines input for fine calibration */ if(!s->cal_image.raw_data){ DBG (5, "setup_buffers: ERROR: failed to setup calibration input raw data buffer\n"); return SANE_STATUS_NO_MEM; } s->cal_data.raw_data = calloc(1, s->cal_data.line_stride); /* only 1 line of data is sent */ if(!s->cal_data.raw_data){ DBG (5, "setup_buffers: ERROR: failed to setup calibration output raw data buffer\n"); return SANE_STATUS_NO_MEM; } /* grab up to 512K at a time */ s->block_img.buffer = calloc (1,s->block_img.width_bytes * s->block_img.height * s->block_img.pages); if(!s->block_img.buffer){ DBG (5, "setup_buffers: ERROR: failed to setup block image buffer\n"); return SANE_STATUS_NO_MEM; } s->block_xfr.raw_data = calloc(1, s->block_xfr.line_stride * s->block_img.height + 8); if(!s->block_xfr.raw_data){ DBG (5, "setup_buffers: ERROR: failed to setup block raw data buffer\n"); return SANE_STATUS_NO_MEM; } /* one grayscale line for dynamic threshold */ s->dt.buffer = calloc (1,s->dt.width_bytes * s->dt.height * s->dt.pages); if(!s->dt.buffer){ DBG (5, "setup_buffers: ERROR: failed to setup dt buffer\n"); return SANE_STATUS_NO_MEM; } /* make image buffer to hold frontside data */ if(s->source != SOURCE_ADF_BACK){ s->front.buffer = calloc (1,s->front.width_bytes * s->front.height * s->front.pages); if(!s->front.buffer){ DBG (5, "setup_buffers: ERROR: failed to setup front buffer\n"); return SANE_STATUS_NO_MEM; } } /* make image buffer to hold backside data */ if(s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_ADF_BACK){ s->back.buffer = calloc (1,s->back.width_bytes * s->back.height * s->back.pages); if(!s->back.buffer){ DBG (5, "setup_buffers: ERROR: failed to setup back buffer\n"); return SANE_STATUS_NO_MEM; } } DBG (10, "setup_buffers: finish\n"); return ret; } /* coarse calibration consists of: 1. turn lamp off (d0) 2. set window for single line of data (d1) 3. get line (d2) 4. update dark coarse cal (c6) 5. return to #3 if not dark enough 6. turn lamp on (d0) 7. get line (d2) 8. update light coarse cal (c6) 9. return to #7 if not light enough */ static SANE_Status coarsecal_send_cal(struct scanner *s, unsigned char *pay) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[2]; unsigned char stat[1]; size_t cmdLen,statLen,payLen; DBG (10, "coarsecal_send_cal: start\n"); /* send coarse cal (c6) */ cmd[0] = 0x1b; cmd[1] = 0xc6; cmdLen = 2; stat[0] = 0; statLen = 1; ret = do_cmd( s, 0, cmd, cmdLen, NULL, 0, stat, &statLen ); if(ret){ DBG (5, "coarsecal_send_cal: error sending c6 cmd\n"); return ret; } if(stat[0] != 6){ DBG (5, "coarsecal_send_cal: cmd bad c6 status?\n"); return SANE_STATUS_IO_ERROR; } /*send coarse cal payload*/ stat[0] = 0; statLen = 1; payLen = 28; ret = do_cmd( s, 0, pay, payLen, NULL, 0, stat, &statLen ); if(ret){ DBG (5, "coarsecal_send_cal: error sending c6 payload\n"); return ret; } if(stat[0] != 6){ DBG (5, "coarsecal_send_cal: c6 payload bad status?\n"); return SANE_STATUS_IO_ERROR; } DBG (10, "coarsecal_send_cal: finish\n"); return ret; } static SANE_Status coarsecal_get_line(struct scanner *s, struct image *img) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[2]; unsigned char stat[1]; size_t cmdLen,statLen; DBG (10, "coarsecal_get_line: start\n"); /* send scan d2 command */ cmd[0] = 0x1b; cmd[1] = 0xd2; cmdLen = 2; stat[0] = 0; statLen = 1; ret = do_cmd( s, 0, cmd, cmdLen, NULL, 0, stat, &statLen ); if(ret){ DBG (5, "coarsecal_get_line: error sending d2 cmd\n"); return ret; } if(stat[0] != 6){ DBG (5, "coarsecal_get_line: cmd bad d2 status?\n"); return SANE_STATUS_IO_ERROR; } s->cal_image.image = img; update_transfer_totals(&s->cal_image); while(!s->cal_image.done){ ret = read_from_scanner(s,&s->cal_image); if(ret){ DBG (5, "coarsecal_get_line: can't read from scanner\n"); return ret; } } /* convert the raw data into normal packed pixel data */ descramble_raw(s, &s->cal_image); DBG (10, "coarsecal_get_line: finish\n"); return ret; } static SANE_Status coarsecal_dark(struct scanner *s, unsigned char *pay) { SANE_Status ret = SANE_STATUS_GOOD; int try_count, cal_good[2], x, j; int param[2], zcount[2], high_param[2], low_param[2], avg[2], maxval[2]; DBG (10, "coarsecal_dark: start\n"); /* dark cal, lamp off */ ret = lamp(s,0); if(ret){ DBG (5, "coarsecal_dark: error lamp off\n"); return ret; } try_count = 8; param[0] = 63; param[1] = 63; low_param[0] = low_param[1] = -64; /* The S300 will accept coarse offsets from -128 to 127 */ high_param[0] = high_param[1] = 63; /* By our range is limited to converge faster */ cal_good[0] = cal_good[1] = 0; while (try_count > 0){ try_count--; /* update the coarsecal payload to use our new dark offset parameters */ if (s->model == MODEL_S300 || s->model == MODEL_S1300i) { pay[5] = param[0]; pay[7] = param[1]; } else /* MODEL_S1100 or MODEL_FI60F or MODEL_FI65F */ { pay[5] = param[0]; pay[7] = param[0]; pay[9] = param[0]; } ret = coarsecal_send_cal(s, pay); DBG(15, "coarsecal_dark offset: parameter front: %i back: %i\n", param[0], param[1]); ret = coarsecal_get_line(s, &s->coarsecal); /* gather statistics: count the proportion of 0-valued pixels */ /* since the lamp is off, there's no point in looking at the green or blue data - they're all from the same sensor anyway */ zcount[0] = zcount[1] = 0; avg[0] = avg[1] = 0; maxval[0] = maxval[1] = 0; for (j = 0; j < s->coarsecal.pages; j++) { int page_offset = j * s->coarsecal.width_bytes * s->coarsecal.height; for (x = 0; x < s->coarsecal.width_bytes; x++) { int val = s->coarsecal.buffer[page_offset + x]; avg[j] += val; if (val == 0) zcount[j]++; if (val > maxval[j]) maxval[j] = val; } } /* convert the zero counts from a pixel count to a proportion in tenths of a percent */ for (j = 0; j < s->coarsecal.pages; j++) { avg[j] /= s->coarsecal.width_bytes; zcount[j] = zcount[j] * 1000 / s->coarsecal.width_bytes; } DBG(15, "coarsecal_dark offset: average pixel values front: %i back: %i\n", avg[0], avg[1]); DBG(15, "coarsecal_dark offset: maximum pixel values front: %i back: %i\n", maxval[0], maxval[1]); DBG(15, "coarsecal_dark offset: 0-valued pixel count front: %f%% back: %f%%\n", zcount[0] / 10.0f, zcount[1] / 10.0f); /* check the values, adjust parameters if they are not within the target range */ for (j = 0; j < s->coarsecal.pages; j++) { if (!cal_good[j]) { if (avg[j] > COARSE_OFFSET_TARGET) { high_param[j] = param[j]; param[j] = (low_param[j] + high_param[j]) / 2; } else if (avg[j] < COARSE_OFFSET_TARGET) { low_param[j] = param[j]; param[j] = (low_param[j] + high_param[j]) / 2; } else cal_good[j] = 1; } } if (cal_good[0] + cal_good[1] == s->coarsecal.pages) break; } /* continue looping for up to 8 tries */ DBG (10, "coarsecal_dark: finish\n"); return ret; } static SANE_Status coarsecal_light(struct scanner *s, unsigned char *pay) { SANE_Status ret = SANE_STATUS_GOOD; int try_count, cal_good[2], x, i, j; int param[2], zcount[2], high_param[2], low_param[2], avg[2]; int rgb_avg[2][3], rgb_hicount[2][3]; DBG (10, "coarsecal_light: start\n"); /* light cal, lamp on */ ret = lamp(s,1); if(ret){ DBG (5, "coarsecal_light: error lamp on\n"); return ret; } try_count = 8; param[0] = pay[11]; param[1] = pay[13]; low_param[0] = low_param[1] = 0; high_param[0] = high_param[1] = 63; cal_good[0] = cal_good[1] = 0; while (try_count > 0){ try_count--; ret = coarsecal_send_cal(s, pay); DBG(15, "coarsecal_light gain: parameter front: %i back: %i\n", param[0], param[1]); ret = coarsecal_get_line(s, &s->coarsecal); /* gather statistics: count the proportion of 255-valued pixels in each color channel */ /* count the average pixel value in each color channel */ for (i = 0; i < s->coarsecal.pages; i++) for (j = 0; j < 3; j++) rgb_avg[i][j] = rgb_hicount[i][j] = 0; for (i = 0; i < s->coarsecal.pages; i++) { for (x = 0; x < s->coarsecal.width_pix; x++) { /* get color channel values and count of pixels pegged at 255 */ unsigned char *rgbpix = s->coarsecal.buffer + (i * s->coarsecal.width_bytes * s->coarsecal.height) + x * 3; for (j = 0; j < 3; j++) { rgb_avg[i][j] += rgbpix[j]; if (rgbpix[j] == 255) rgb_hicount[i][j]++; } } } /* apply the color correction factors to the averages */ for (i = 0; i < s->coarsecal.pages; i++) for (j = 0; j < 3; j++) rgb_avg[i][j] *= s->white_factor[j]; /* set the gain so that none of the color channels are clipping, ie take the highest channel values */ for (i = 0; i < s->coarsecal.pages; i++) { avg[i] = MAX3(rgb_avg[i][0], rgb_avg[i][1], rgb_avg[i][2]) / s->coarsecal.width_pix; for (j = 0; j < 3; j++) rgb_avg[i][j] /= s->coarsecal.width_pix; } /* convert the 255-counts from a pixel count to a proportion in tenths of a percent */ for (i = 0; i < s->coarsecal.pages; i++) { for (j = 0; j < 3; j++) { rgb_hicount[i][j] = rgb_hicount[i][j] * 1000 / s->coarsecal.width_pix; } zcount[i] = MAX3(rgb_hicount[i][0], rgb_hicount[i][1], rgb_hicount[i][2]); } DBG(15, "coarsecal_light gain: average RGB values front: (%i,%i,%i) back: (%i,%i,%i)\n", rgb_avg[0][0], rgb_avg[0][1], rgb_avg[0][2], rgb_avg[1][0], rgb_avg[1][1], rgb_avg[1][2]); DBG(15, "coarsecal_light gain: 255-valued pixel count front: (%g,%g,%g) back: (%g,%g,%g)\n", rgb_hicount[0][0]/10.0f, rgb_hicount[0][1]/10.0f, rgb_hicount[0][2]/10.0f, rgb_hicount[1][0]/10.0f, rgb_hicount[1][1]/10.0f, rgb_hicount[1][2]/10.0f); /* check the values, adjust parameters if they are not within the target range */ for (x = 0; x < s->coarsecal.pages; x++) { if (!cal_good[x]) { if (zcount[x] > 9 || avg[x] > coarse_gain_max[x]) { high_param[x] = param[x]; param[x] = (low_param[x] + high_param[x]) / 2; } else if (avg[x] < coarse_gain_min[x]) { low_param[x] = param[x]; param[x] = (low_param[x] + high_param[x]) / 2; } else cal_good[x] = 1; } } if (cal_good[0] + cal_good[1] == s->coarsecal.pages) break; /* update the coarsecal payload to use the new gain parameters */ if (s->model == MODEL_S300 || s->model == MODEL_S1300i) { pay[11] = param[0]; pay[13] = param[1]; } else /* MODEL_S1100 or MODEL_FI60F or MODEL_FI65F */ { pay[11] = param[0]; pay[13] = param[0]; pay[15] = param[0]; } } DBG (10, "coarsecal_light: finish\n"); return ret; } static SANE_Status coarsecal(struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char pay[28]; size_t payLen; DBG (10, "coarsecal: start\n"); payLen = sizeof(pay); if(s->model == MODEL_S300){ memcpy(pay,coarseCalData_S300,payLen); } else if(s->model == MODEL_S1300i){ memcpy(pay,coarseCalData_S1300i,payLen); } else if(s->model == MODEL_S1100){ memcpy(pay,coarseCalData_S1100,payLen); } else{ memcpy(pay,coarseCalData_FI60F,payLen); } /* ask for 1 line */ ret = set_window(s, WINDOW_COARSECAL); if(ret){ DBG (5, "coarsecal: error sending setwindow\n"); return ret; } if(s->model == MODEL_S1100){ ret = coarsecal_send_cal(s, pay); } else{ ret = coarsecal_dark(s, pay); ret = coarsecal_light(s, pay); } DBG (10, "coarsecal: finish\n"); return ret; } static SANE_Status finecal_send_cal(struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; size_t cmdLen = 2; unsigned char cmd[2]; size_t statLen = 1; unsigned char stat[2]; int i, j, k; unsigned char *p_out, *p_in = s->sendcal.buffer; int planes; DBG (10, "finecal_send_cal: start\n"); if(s->model == MODEL_FI60F || s->model == MODEL_FI65F) planes = 3; if(s->model == MODEL_S300 || s->model == MODEL_S1300i) planes = 2; /* scramble the raster buffer data into scanner raw format */ /* this is reverse of descramble_raw */ memset(s->cal_data.raw_data, 0, s->cal_data.line_stride); if(s->model == MODEL_S1100){ planes = 1; for (k = 0; k < s->sendcal.width_pix; k++){ /* column (x) */ /* input is RrGgBb (capital is offset, small is gain) */ /* output is Bb...BbRr...RrGg...Gg*/ /*red*/ p_out = s->cal_data.raw_data + s->cal_data.plane_stride + k*2; *p_out = *p_in; p_out++; p_in++; *p_out = *p_in; p_in++; /*green*/ p_out = s->cal_data.raw_data + 2*s->cal_data.plane_stride + k*2; *p_out = *p_in; p_out++; p_in++; *p_out = *p_in; p_in++; /*blue*/ p_out = s->cal_data.raw_data + k*2; *p_out = *p_in; p_out++; p_in++; *p_out = *p_in; p_in++; } } else{ for (i = 0; i < planes; i++) for (j = 0; j < s->cal_data.plane_width; j++) for (k = 0; k < 3; k++) { p_out = (s->cal_data.raw_data + k * s->cal_data.plane_stride + j * 6 + i * 2); *p_out = *p_in++; /* dark offset */ p_out++; *p_out = *p_in++; /* gain */ } } ret = set_window(s, WINDOW_SENDCAL); if(ret){ DBG (5, "finecal_send_cal: error sending setwindow\n"); return ret; } /*first unknown cal block*/ cmd[0] = 0x1b; cmd[1] = 0xc3; stat[0] = 0; statLen = 1; ret = do_cmd( s, 0, cmd, cmdLen, NULL, 0, stat, &statLen ); if(ret){ DBG (5, "finecal_send_cal: error sending c3 cmd\n"); return ret; } if(stat[0] != 6){ DBG (5, "finecal_send_cal: cmd bad c3 status?\n"); return SANE_STATUS_IO_ERROR; } /*send header*/ /*send payload*/ statLen = 1; ret = do_cmd( s, 0, s->sendCal1Header, s->sendCal1HeaderLen, s->cal_data.raw_data, s->cal_data.line_stride, stat, &statLen ); if(ret){ DBG (5, "finecal_send_cal: error sending c3 payload\n"); return ret; } if(stat[0] != 6){ DBG (5, "finecal_send_cal: payload bad c3 status?\n"); return SANE_STATUS_IO_ERROR; } /*second unknown cal block*/ cmd[1] = 0xc4; statLen = 1; ret = do_cmd( s, 0, cmd, cmdLen, NULL, 0, stat, &statLen ); if(ret){ DBG (5, "finecal_send_cal: error sending c4 cmd\n"); return ret; } if(stat[0] != 6){ DBG (5, "finecal_send_cal: cmd bad c4 status?\n"); return SANE_STATUS_IO_ERROR; } /*send header*/ /*send payload*/ statLen = 1; ret = do_cmd( s, 0, s->sendCal2Header, s->sendCal2HeaderLen, s->cal_data.raw_data, s->cal_data.line_stride, stat, &statLen ); if(ret){ DBG (5, "finecal_send_cal: error sending c4 payload\n"); return ret; } if(stat[0] != 6){ DBG (5, "finecal_send_cal: payload bad c4 status?\n"); return SANE_STATUS_IO_ERROR; } DBG (10, "finecal_send_cal: finish\n"); return ret; } static SANE_Status finecal_get_line(struct scanner *s, struct image *img) { SANE_Status ret = SANE_STATUS_GOOD; size_t cmdLen = 2; unsigned char cmd[2]; size_t statLen = 1; unsigned char stat[2]; int round_offset = img->height / 2; int i, j, k; DBG (10, "finecal_get_line: start\n"); /* ask for 16 lines */ ret = set_window(s, WINDOW_FINECAL); if(ret){ DBG (5, "finecal_get_line: error sending setwindowcal\n"); return ret; } /* send scan d2 command */ cmd[0] = 0x1b; cmd[1] = 0xd2; stat[0] = 0; statLen = 1; ret = do_cmd( s, 0, cmd, cmdLen, NULL, 0, stat, &statLen ); if(ret){ DBG (5, "finecal_get_line: error sending d2 cmd\n"); return ret; } if(stat[0] != 6){ DBG (5, "finecal_get_line: cmd bad d2 status?\n"); return SANE_STATUS_IO_ERROR; } s->cal_image.image = img; update_transfer_totals(&s->cal_image); while(!s->cal_image.done){ ret = read_from_scanner(s,&s->cal_image); if(ret){ DBG (5, "finecal_get_line: can't read from scanner\n"); return ret; } } /* convert the raw data into normal packed pixel data */ descramble_raw(s, &s->cal_image); /* average the columns of pixels together and put the results in the top line(s) */ for (i = 0; i < img->pages; i++) { unsigned char *linepix = img->buffer + i * img->width_bytes * img->height; unsigned char *avgpix = img->buffer + i * img->width_bytes; for (j = 0; j < img->width_bytes; j++) { int total = 0; for (k = 0; k < img->height; k++) total += linepix[j + k * img->width_bytes]; avgpix[j] = (total + round_offset) / img->height; } } DBG (10, "finecal_get_line: finish\n"); return ret; } /* roundf() is c99, so we provide our own, though this version won't return -0 */ static float round2(float x) { return (float)(x >= 0.0) ? (int)(x+0.5) : (int)(x-0.5); } static SANE_Status finecal(struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; int max_pages; int gain_delta = 0xff - 0xbf; float *gain_slope, *last_error; int i, j, k, idx, try_count, cal_good; DBG (10, "finecal: start\n"); if (s->model == MODEL_S300 || s->model == MODEL_S1300i) { /* S300, S1300 */ max_pages = 2; } else /* fi-60f, S1100 */ { max_pages = 1; } /* set fine dark offset to 0 and fix all fine gains to lowest parameter (0xFF) */ for (i = 0; i < s->sendcal.width_bytes * s->sendcal.pages / 2; i++) { s->sendcal.buffer[i*2] = 0; s->sendcal.buffer[i*2+1] = 0xff; } ret = finecal_send_cal(s); if(ret) return ret; /* grab rows with lamp on */ ret = lamp(s,1); if(ret){ DBG (5, "finecal: error lamp on\n"); return ret; } /* read the low-gain average of 16 lines */ ret = finecal_get_line(s, &s->darkcal); if(ret) return ret; /* set fine dark offset to 0 and fine gain to a fixed higher-gain parameter (0xBF) */ for (i = 0; i < s->sendcal.width_bytes * s->sendcal.pages / 2; i++) { s->sendcal.buffer[i*2] = 0; s->sendcal.buffer[i*2+1] = 0xbf; } ret = finecal_send_cal(s); if(ret) return ret; /* read the high-gain average of 16 lines */ ret = finecal_get_line(s, &s->lightcal); if(ret) return ret; /* calculate the per pixel slope of pixel value delta over gain delta */ gain_slope = malloc(s->lightcal.width_bytes * s->lightcal.pages * sizeof(float)); if (!gain_slope) return SANE_STATUS_NO_MEM; idx = 0; for (i = 0; i < s->lightcal.pages; i++) { for (j = 0; j < s->lightcal.width_pix; j++) { for (k = 0; k < 3; k++) { int value_delta = s->lightcal.buffer[idx] - s->darkcal.buffer[idx]; /* limit this slope to 1 or less, to avoid overshoot if the lightcal ref input is clipped at 255 */ if (value_delta < gain_delta) gain_slope[idx] = -1.0; else gain_slope[idx] = (float) -gain_delta / value_delta; idx++; } } } /* keep track of the last iteration's pixel error. If we overshoot, we can reduce the value of the gain slope */ last_error = malloc(s->lightcal.width_bytes * s->lightcal.pages * sizeof(float)); if (!last_error) { free(gain_slope); return SANE_STATUS_NO_MEM; } for (i = 0; i < s->lightcal.width_bytes * s->lightcal.pages; i++) last_error[i] = 0.0; /* fine calibration feedback loop */ try_count = 8; while (try_count > 0) { int min_value[2][3], max_value[2][3]; float avg_value[2][3], variance[2][3]; int high_pegs = 0, low_pegs = 0; try_count--; /* clear statistics arrays */ for (i = 0; i < max_pages; i++) { for (k = 0; k < 3; k++) { min_value[i][k] = 0xff; max_value[i][k] = 0; avg_value[i][k] = 0; variance[i][k] = 0; } } /* gather statistics and calculate new fine gain parameters based on observed error and the value/gain slope */ idx = 0; for (i = 0; i < max_pages; i++) { for (j = 0; j < s->lightcal.width_pix; j++) { for (k = 0; k < 3; k++) { int pixvalue = s->lightcal.buffer[idx]; float pixerror = (fine_gain_target[i] * s->white_factor[k] - pixvalue); int oldgain = s->sendcal.buffer[idx * 2 + 1]; int newgain; /* if we overshot the last correction, reduce the gain_slope */ if (pixerror * last_error[idx] < 0.0) gain_slope[idx] *= 0.75; last_error[idx] = pixerror; /* set the new gain */ newgain = oldgain + (int) round2(pixerror * gain_slope[idx]); if (newgain < 0) { low_pegs++; s->sendcal.buffer[idx * 2 + 1] = 0; } else if (newgain > 0xff) { high_pegs++; s->sendcal.buffer[idx * 2 + 1] = 0xff; } else s->sendcal.buffer[idx * 2 + 1] = newgain; /* update statistics */ min_value[i][k] = MIN(min_value[i][k], pixvalue); max_value[i][k] = MAX(max_value[i][k], pixvalue); avg_value[i][k] += pixerror; variance[i][k] += (pixerror * pixerror); idx++; } } } /* finish the statistics calculations */ cal_good = 1; for (i = 0; i < max_pages; i++) { for (k = 0; k < 3; k++) { float sum = avg_value[i][k]; float sum2 = variance[i][k]; avg_value[i][k] = sum / s->lightcal.width_pix; variance[i][k] = ((sum2 - (sum * sum / s->lightcal.width_pix)) / s->lightcal.width_pix); /* if any color channel is too far out of whack, set cal_good to 0 so we'll iterate again */ if (fabs(avg_value[i][k]) > 1.0 || variance[i][k] > 3.0) cal_good = 0; } } /* print debug info */ DBG (15, "finecal: -------------------- Gain\n"); DBG (15, "finecal: RGB Average Error - Front: (%.1f,%.1f,%.1f) - Back: (%.1f,%.1f,%.1f)\n", avg_value[0][0], avg_value[0][1], avg_value[0][2], avg_value[1][0], avg_value[1][1], avg_value[1][2]); DBG (15, "finecal: RGB Maximum - Front: (%i,%i,%i) - Back: (%i,%i,%i)\n", max_value[0][0], max_value[0][1], max_value[0][2], max_value[1][0], max_value[1][1], max_value[1][2]); DBG (15, "finecal: RGB Minimum - Front: (%i,%i,%i) - Back: (%i,%i,%i)\n", min_value[0][0], min_value[0][1], min_value[0][2], min_value[1][0], min_value[1][1], min_value[1][2]); DBG (15, "finecal: Variance - Front: (%.1f,%.1f,%.1f) - Back: (%.1f,%.1f,%.1f)\n", variance[0][0], variance[0][1], variance[0][2], variance[1][0], variance[1][1], variance[1][2]); DBG (15, "finecal: Pegged gain parameters - High (0xff): %i - Low (0): %i\n", high_pegs, low_pegs); /* break out of the loop if our calibration is done */ if (cal_good) break; /* send the new calibration and read a new line */ ret = finecal_send_cal(s); if(ret) { free(gain_slope); free(last_error); return ret; } ret = finecal_get_line(s, &s->lightcal); if(ret) { free(gain_slope); free(last_error); return ret; } } /* release the memory for the reference slope data */ free(gain_slope); free(last_error); DBG (10, "finecal: finish\n"); return ret; } /* * set scanner lamp brightness */ static SANE_Status lamp(struct scanner *s, unsigned char set) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[2]; size_t cmdLen = 2; unsigned char stat[1]; size_t statLen = 1; DBG (10, "lamp: start (%d)\n", set); /*send cmd*/ cmd[0] = 0x1b; cmd[1] = 0xd0; ret = do_cmd( s, 0, cmd, cmdLen, NULL, 0, stat, &statLen ); if(ret){ DBG (5, "lamp: error sending cmd\n"); return ret; } if(stat[0] != 6){ DBG (5, "lamp: cmd bad status?\n"); return SANE_STATUS_IO_ERROR; } /*send payload*/ cmd[0] = set; cmdLen = 1; statLen = 1; ret = do_cmd( s, 0, cmd, cmdLen, NULL, 0, stat, &statLen ); if(ret){ DBG (5, "lamp: error sending payload\n"); return ret; } if(stat[0] != 6){ DBG (5, "lamp: payload bad status?\n"); return SANE_STATUS_IO_ERROR; } DBG (10, "lamp: finish\n"); return ret; } static SANE_Status set_window(struct scanner *s, int window) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[] = {0x1b, 0xd1}; size_t cmdLen = sizeof(cmd); unsigned char stat[] = {0}; size_t statLen = sizeof(stat); unsigned char * payload; size_t paylen = SET_WINDOW_LEN; DBG (10, "set_window: start, window %d\n",window); switch (window) { case WINDOW_COARSECAL: payload = s->setWindowCoarseCal; paylen = s->setWindowCoarseCalLen; break; case WINDOW_FINECAL: payload = s->setWindowFineCal; paylen = s->setWindowFineCalLen; break; case WINDOW_SENDCAL: payload = s->setWindowSendCal; paylen = s->setWindowSendCalLen; break; case WINDOW_SCAN: payload = s->setWindowScan; paylen = s->setWindowScanLen; set_SW_ypix(payload,s->fullscan.height); break; default: DBG (5, "set_window: unknown window\n"); return SANE_STATUS_INVAL; } /*send cmd*/ ret = do_cmd( s, 0, cmd, cmdLen, NULL, 0, stat, &statLen ); if(ret){ DBG (5, "set_window: error sending cmd\n"); return ret; } if(stat[0] != 6){ DBG (5, "set_window: cmd bad status?\n"); return SANE_STATUS_IO_ERROR; } /*send payload*/ statLen = 1; ret = do_cmd( s, 0, payload, paylen, NULL, 0, stat, &statLen ); if(ret){ DBG (5, "set_window: error sending payload\n"); return ret; } if(stat[0] != 6){ DBG (5, "set_window: payload bad status?\n"); return SANE_STATUS_IO_ERROR; } DBG (10, "set_window: finish\n"); return ret; } /* instead of internal brightness/contrast/gamma scanners uses 12bit x 12bit LUT default is linear table of slope 1 brightness and contrast inputs are -127 to +127 contrast rotates slope of line around central input val high low . x . . x . xx out . x . xxxxxxxx . x xx ....x....... ............ in in then brightness moves line vertically, and clamps to 8bit bright dark . xxxxxxxx . . x . out x . x . . x ............ xxxxxxxx.... in in */ static SANE_Status send_lut (struct scanner *s) { SANE_Status ret=SANE_STATUS_GOOD; unsigned char cmd[] = {0x1b, 0xc5}; size_t cmdLen = 2; unsigned char stat[1]; size_t statLen = 1; unsigned char *out; size_t outLen; int i, j; double b, slope, offset; int width; int height; DBG (10, "send_lut: start\n"); if (s->model == MODEL_S1100){ outLen = 0x200; width = outLen / 2; /* 1 color, 2 bytes */ height = width; /* square table */ } else if (s->model == MODEL_FI65F){ outLen = 0x600; width = outLen / 6; /* 3 color, 2 bytes */ height = width; /* square table */ } else { outLen = 0x6000; width = outLen / 6; /* 3 colors, 2 bytes */ height = width; /* square table */ } out = ( unsigned char *)malloc(outLen*sizeof(unsigned char)); if (out == NULL){ return SANE_STATUS_NO_MEM; } /* contrast is converted to a slope [0,90] degrees: * first [-127,127] to [0,254] then to [0,1] * then multiply by PI/2 to convert to radians * then take the tangent to get slope (T.O.A) * then multiply by the normal linear slope * because the table may not be square, i.e. 1024x256*/ slope = tan(((double)s->contrast+127)/254 * M_PI/2); /* contrast slope must stay centered, so figure * out vertical offset at central input value */ offset = height/2 - slope*width/2; /* convert the user brightness setting (-127 to +127) * into a scale that covers the range required * to slide the contrast curve entirely off the table */ b = ((double)s->brightness/127) * (slope*(width-1) + offset); DBG (15, "send_lut: %d %f %d %f %f\n", s->brightness, b, s->contrast, slope, offset); for(i=0;imodel == MODEL_S1100){ /*only one table, be order*/ out[i*2] = (j >> 8) & 0xff; out[i*2+1] = j & 0xff; } else if (s->model == MODEL_FI65F){ /*first table, be order*/ out[i*2] = (j >> 8) & 0xff; out[i*2+1] = j & 0xff; /*second table, be order*/ out[width*2 + i*2] = (j >> 8) & 0xff; out[width*2 + i*2+1] = j & 0xff; /*third table, be order*/ out[width*4 + i*2] = (j >> 8) & 0xff; out[width*4 + i*2+1] = j & 0xff; } else { /*first table, le order*/ out[i*2] = j & 0xff; out[i*2+1] = (j >> 8) & 0x0f; /*second table, le order*/ out[width*2 + i*2] = j & 0xff; out[width*2 + i*2+1] = (j >> 8) & 0x0f; /*third table, le order*/ out[width*4 + i*2] = j & 0xff; out[width*4 + i*2+1] = (j >> 8) & 0x0f; } } ret = do_cmd( s, 0, cmd, cmdLen, NULL, 0, stat, &statLen ); if(ret){ DBG (5, "send_lut: error sending cmd\n"); return ret; } if(stat[0] != 6){ DBG (5, "send_lut: cmd bad status?\n"); return SANE_STATUS_IO_ERROR; } statLen = 1; ret = do_cmd( s, 0, out, outLen, NULL, 0, stat, &statLen ); if(ret){ DBG (5, "send_lut: error sending out\n"); return ret; } if(stat[0] != 6){ DBG (5, "send_lut: out bad status?\n"); return SANE_STATUS_IO_ERROR; } DBG (10, "send_lut: finish\n"); return ret; } static SANE_Status get_hardware_status (struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; DBG (10, "get_hardware_status: start\n"); /* only run this once every second */ if (s->last_ghs < time(NULL)) { unsigned char cmd[2]; size_t cmdLen = sizeof(cmd); unsigned char pay[4]; size_t payLen = sizeof(pay); DBG (15, "get_hardware_status: running\n"); cmd[0] = 0x1b; cmd[1] = 0x33; ret = do_cmd( s, 0, cmd, cmdLen, NULL, 0, pay, &payLen ); if(ret){ DBG (5, "get_hardware_status: error sending cmd\n"); return ret; } hexdump(5,"ghspayload: ", pay, payLen); s->last_ghs = time(NULL); s->hw_top = ((pay[0] >> 7) & 0x01); s->hw_hopper = !((pay[0] >> 6) & 0x01); s->hw_adf_open = ((pay[0] >> 5) & 0x01); s->hw_sleep = ((pay[1] >> 7) & 0x01); s->hw_scan_sw = ((pay[1] >> 0) & 0x01); } DBG (10, "get_hardware_status: finish\n"); return ret; } static SANE_Status object_position(struct scanner *s, int ingest) { SANE_Status ret = SANE_STATUS_GOOD; int i; unsigned char cmd[2]; size_t cmdLen = sizeof(cmd); unsigned char stat[1]; size_t statLen = sizeof(stat); unsigned char pay[2]; size_t payLen = sizeof(pay); DBG (10, "object_position: start\n"); i = (ingest)?5:1; while(i--){ /*send paper load cmd*/ cmd[0] = 0x1b; cmd[1] = 0xd4; statLen = 1; ret = do_cmd( s, 0, cmd, cmdLen, NULL, 0, stat, &statLen ); if(ret){ DBG (5, "object_position: error sending cmd\n"); return ret; } if(stat[0] != 6){ DBG (5, "object_position: cmd bad status? %d\n",stat[0]); continue; } /*send payload*/ statLen = 1; payLen = 1; pay[0] = ingest; ret = do_cmd( s, 0, pay, payLen, NULL, 0, stat, &statLen ); if(ret){ DBG (5, "object_position: error sending payload\n"); return ret; } if(stat[0] == 6){ DBG (5, "object_position: found paper?\n"); break; } else if(stat[0] == 0x15 || stat[0] == 0){ DBG (5, "object_position: no paper?\n"); ret=SANE_STATUS_NO_DOCS; continue; } else{ DBG (5, "object_position: payload bad status?\n"); return SANE_STATUS_IO_ERROR; } } DBG (10, "object_position: finish\n"); return ret; } static SANE_Status scan(struct scanner *s) { SANE_Status ret=SANE_STATUS_GOOD; unsigned char cmd[] = {0x1b, 0xd2}; size_t cmdLen = 2; unsigned char stat[1]; size_t statLen = 1; DBG (10, "scan: start\n"); if(s->model == MODEL_S300 || s->model == MODEL_S1100 || s->model == MODEL_S1300i){ cmd[1] = 0xd6; } ret = do_cmd( s, 0, cmd, cmdLen, NULL, 0, stat, &statLen ); if(ret){ DBG (5, "scan: error sending cmd\n"); return ret; } if(stat[0] != 6){ DBG (5, "scan: cmd bad status?\n"); return SANE_STATUS_IO_ERROR; } DBG (10, "scan: finish\n"); return ret; } /* * 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 scanner *s = (struct scanner *) handle; SANE_Status ret=SANE_STATUS_GOOD; struct page * page; DBG (10, "sane_read: start si:%d len:%d max:%d\n",s->side,*len,max_len); *len = 0; /* cancelled? */ if(!s->started){ DBG (5, "sane_read: call sane_start first\n"); return SANE_STATUS_CANCELLED; } page = &s->pages[s->side]; /* have sent all of current buffer */ if(s->fullscan.done && page->done){ DBG (10, "sane_read: returning eof\n"); /*S1100 needs help to turn off button*/ if(s->model == MODEL_S1100){ usleep(15000); /* eject paper */ ret = object_position(s,EPJITSU_PAPER_EJECT); if (ret != SANE_STATUS_GOOD && ret != SANE_STATUS_NO_DOCS) { DBG (5, "sane_read: ERROR: failed to eject\n"); return ret; } /* reset flashing button? */ ret = six5(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_read: ERROR: failed to six5\n"); return ret; } } return SANE_STATUS_EOF; } /* scan not finished, get more into block buffer */ if(!s->fullscan.done) { /* block buffer currently empty, clean up */ if(!s->block_xfr.rx_bytes) { /* block buffer bigger than remainder of scan, shrink block */ int remainTotal = s->fullscan.total_bytes - s->fullscan.rx_bytes; if(remainTotal < s->block_xfr.total_bytes) { DBG (15, "sane_read: shrinking block to %lu\n", (unsigned long)remainTotal); s->block_xfr.total_bytes = remainTotal; } /* send d3 cmd for S300, S1100, S1300 */ if(s->model == MODEL_S300 || s->model == MODEL_S1100 || s->model == MODEL_S1300i) { unsigned char cmd[] = {0x1b, 0xd3}; size_t cmdLen = 2; unsigned char stat[1]; size_t statLen = 1; DBG (15, "sane_read: d3\n"); ret = do_cmd( s, 0, cmd, cmdLen, NULL, 0, stat, &statLen ); if(ret){ DBG (5, "sane_read: error sending d3 cmd\n"); return ret; } if(stat[0] != 6){ DBG (5, "sane_read: cmd bad status?\n"); return SANE_STATUS_IO_ERROR; } } } ret = read_from_scanner(s, &s->block_xfr); if(ret){ DBG (5, "sane_read: can't read from scanner\n"); return ret; } /* block filled, copy to front/back */ if(s->block_xfr.done) { DBG (15, "sane_read: block buffer full\n"); /* convert the raw color data into normal packed pixel data */ descramble_raw(s, &s->block_xfr); s->block_xfr.done = 0; /* get the 0x43 cmd for the S300, S1100, S1300 */ if(s->model == MODEL_S300 || s->model == MODEL_S1100 || s->model == MODEL_S1300i){ unsigned char cmd[] = {0x1b, 0x43}; size_t cmdLen = 2; unsigned char in[10]; size_t inLen = 10; ret = do_cmd( s, 0, cmd, cmdLen, NULL, 0, in, &inLen ); hexdump(15, "cmd 43: ", in, inLen); if(ret){ DBG (5, "sane_read: error sending 43 cmd\n"); return ret; } /*copy backside data into buffer*/ if( s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_ADF_BACK ) ret = copy_block_to_page(s, SIDE_BACK); /*copy frontside data into buffer*/ if( s->source != SOURCE_ADF_BACK ) ret = copy_block_to_page(s, SIDE_FRONT); if(ret){ DBG (5, "sane_read: can't copy to front/back\n"); return ret; } s->fullscan.rx_bytes += s->block_xfr.rx_bytes; /* autodetect mode, check for change length */ if( s->source != SOURCE_FLATBED && !s->page_height ){ int get = (in[6] << 8) | in[7]; /*always have to get full blocks*/ if(get % s->block_img.height){ get += s->block_img.height - (get % s->block_img.height); } if(get < s->fullscan.height){ DBG (15, "sane_read: paper out? %d\n",get); s->fullscan.total_bytes = s->fullscan.width_bytes * get; } } } else { /*fi-60f*/ ret = copy_block_to_page(s, SIDE_FRONT); if(ret){ DBG (5, "sane_read: can't copy to front/back\n"); return ret; } s->fullscan.rx_bytes += s->block_xfr.rx_bytes; } /* reset for next pass */ update_transfer_totals(&s->block_xfr); /* scan now finished */ if(s->fullscan.rx_bytes == s->fullscan.total_bytes){ DBG (15, "sane_read: last block\n"); s->fullscan.done = 1; } } } *len = page->bytes_scanned - page->bytes_read; *len = MIN(*len, max_len); if(*len){ DBG (10, "sane_read: copy rx:%d tx:%d tot:%d len:%d\n", page->bytes_scanned, page->bytes_read, page->bytes_total,*len); memcpy(buf, page->image->buffer + page->bytes_read, *len); page->bytes_read += *len; } /* sent it all, return eof on next read */ if(page->bytes_read == page->bytes_scanned && s->fullscan.done){ DBG (10, "sane_read: side done\n"); page->done = 1; } DBG (10, "sane_read: finish si:%d len:%d max:%d\n",s->side,*len,max_len); return ret; } static SANE_Status six5 (struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[2]; size_t cmdLen = sizeof(cmd); unsigned char stat[1]; size_t statLen = sizeof(stat); DBG (10, "six5: start\n"); cmd[0] = 0x1b; cmd[1] = 0x65; statLen = 1; ret = do_cmd( s, 0, cmd, cmdLen, NULL, 0, stat, &statLen ); if(ret){ DBG (5, "six5: error sending cmd\n"); return ret; } if(stat[0] != 6){ DBG (5, "six5: cmd bad status? %d\n",stat[0]); return SANE_STATUS_IO_ERROR; } DBG (10, "six5: finish\n"); return ret; } /* de-scrambles the raw data from the scanner into the image buffer */ /* the output image might be lower dpi than input image, so we scale horizontally */ /* if the input image is mirrored left to right, we do not correct it here */ /* if the input image has padding (at the end or between heads), it is removed here */ static SANE_Status descramble_raw(struct scanner *s, struct transfer * tp) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char *p_out = tp->image->buffer; int height = tp->total_bytes / tp->line_stride; int i, j, k; /* raw gray data handled in another function */ if(tp->mode == MODE_GRAYSCALE){ return descramble_raw_gray(s, tp); } DBG(15, "descramble_raw: start\n"); if (s->model == MODEL_S300 || s->model == MODEL_S1300i) { for (i = 0; i < 2; i++){ /* page, front/back */ for (j = 0; j < height; j++){ /* row (y)*/ int curr_col = 0; int r=0, g=0, b=0, ppc=0; int g_offset=0, b_offset=0; for (k = 0; k <= tp->plane_width; k++){ /* column (x) */ int this_col = k*tp->image->x_res/tp->x_res; /* going to change output pixel, dump rgb and reset */ if(ppc && curr_col != this_col){ *p_out = r/ppc; p_out++; *p_out = g/ppc; p_out++; *p_out = b/ppc; p_out++; r = g = b = ppc = 0; curr_col = this_col; } if(k == tp->plane_width || this_col >= tp->image->width_pix){ break; } /* if we're using an S1300i with scan resolution 225 or 300, on AC power, the color planes are shifted */ if(s->model == MODEL_S1300i && !s->usb_power && (tp->x_res == 225 || tp->x_res == 300) && tp != &s->cal_image && k + 2 <= tp->plane_width){ g_offset = 3; b_offset = 6; } /*red is first*/ r += tp->raw_data[j*tp->line_stride + k*3 + i]; /*green is second*/ g += tp->raw_data[j*tp->line_stride + tp->plane_stride + k*3 + i + g_offset]; /*blue is third*/ b += tp->raw_data[j*tp->line_stride + 2*tp->plane_stride + k*3 + i + b_offset]; ppc++; } } } } else if (s->model == MODEL_S1100){ for (j = 0; j < height; j++){ /* row (y)*/ int curr_col = 0; int r=0, g=0, b=0, ppc=0; for (k = 0; k <= tp->plane_width; k++){ /* column (x) */ int this_col = k*tp->image->x_res/tp->x_res; /* going to change output pixel, dump rgb and reset */ if(ppc && curr_col != this_col){ *p_out = r/ppc; p_out++; *p_out = g/ppc; p_out++; *p_out = b/ppc; p_out++; r = g = b = ppc = 0; curr_col = this_col; } if(k == tp->plane_width || this_col >= tp->image->width_pix){ break; } /*red is second*/ r += tp->raw_data[j*tp->line_stride + tp->plane_stride + k]; /*green is third*/ g += tp->raw_data[j*tp->line_stride + 2*tp->plane_stride + k]; /*blue is first*/ b += tp->raw_data[j*tp->line_stride + k]; ppc++; } } } else { /* MODEL_FI60F or MODEL_FI65F */ for (j = 0; j < height; j++){ /* row (y)*/ int curr_col = 0; for (i = 0; i < 3; i++){ /* read head */ int r=0, g=0, b=0, ppc=0; for (k = 0; k <= tp->plane_width; k++){ /* column (x) within the read head */ int this_col = (k+i*tp->plane_width)*tp->image->x_res/tp->x_res; /* going to change output pixel, dump rgb and reset */ if(ppc && curr_col != this_col){ *p_out = r/ppc; p_out++; *p_out = g/ppc; p_out++; *p_out = b/ppc; p_out++; r = g = b = ppc = 0; curr_col = this_col; } if(k == tp->plane_width || this_col >= tp->image->width_pix){ break; } /*red is first*/ r += tp->raw_data[j*tp->line_stride + k*3 + i]; /*green is second*/ g += tp->raw_data[j*tp->line_stride + tp->plane_stride + k*3 + i]; /*blue is third*/ b += tp->raw_data[j*tp->line_stride + 2*tp->plane_stride + k*3 + i]; ppc++; } } } } DBG(15, "descramble_raw: finish %d\n", ret); return ret; } /* de-scrambles the raw gray data from the scanner into the image buffer */ /* the output image might be lower dpi than input image, so we scale horizontally */ /* if the input image is mirrored left to right, we do not correct it here */ /* if the input image has padding (at the end or between heads), it is removed here */ static SANE_Status descramble_raw_gray(struct scanner *s, struct transfer * tp) { SANE_Status ret = SANE_STATUS_GOOD; int height = tp->total_bytes / tp->line_stride; int row, col_out; DBG(15, "descramble_raw_gray: start\n"); if (s->model == MODEL_FI60F || s->model == MODEL_FI65F) { for (row = 0; row < height; row++){ unsigned char *p_in = tp->raw_data + row * tp->line_stride; unsigned char *p_out = tp->image->buffer + row * tp->image->width_pix; for (col_out = 0; col_out < tp->image->width_pix; col_out++){ int col_in = col_out * tp->x_res/tp->image->x_res; int offset = col_in%tp->plane_width; int step = col_in/tp->plane_width; *p_out = *(p_in + offset*3 + step); p_out++; } } } else{ DBG(5, "internal error: descramble_raw_gray not supported\n"); ret = SANE_STATUS_INVAL; } DBG(15, "descramble_raw_gray: finish %d\n", ret); return ret; } /* fills block buffer a little per pass */ static SANE_Status read_from_scanner(struct scanner *s, struct transfer * tp) { SANE_Status ret=SANE_STATUS_GOOD; size_t bytes = MAX_IMG_PASS; size_t remainBlock = tp->total_bytes - tp->rx_bytes + 8; unsigned char * buf; size_t bufLen; /* determine amount to ask for, S1300i wants big requests */ if(s->model != MODEL_S1300i){ bytes = MIN(bytes, remainBlock); } if (tp->image == NULL) { DBG(5, "internal error: read_from_scanner called with no destination image.\n"); return SANE_STATUS_INVAL; } DBG (10, "read_from_scanner: start rB:%lu len:%lu\n", (unsigned long)remainBlock, (unsigned long)bytes); if(!bytes){ DBG(10, "read_from_scanner: no bytes!\n"); return SANE_STATUS_INVAL; } bufLen = bytes; buf = malloc(bufLen); if(!buf){ DBG (5, "read_from_scanner: failed to alloc mem\n"); return SANE_STATUS_NO_MEM; } ret = do_cmd( s, 0, NULL, 0, NULL, 0, buf, &bytes ); /* full read or short read */ if (ret == SANE_STATUS_GOOD || (ret == SANE_STATUS_EOF && bytes) ) { DBG(15,"read_from_scanner: got GOOD/EOF (%lu)\n",(unsigned long)bytes); if(bytes > remainBlock){ DBG(15,"read_from_scanner: block too big?\n"); bytes = remainBlock; } if(bytes == remainBlock){ DBG(15,"read_from_scanner: block done, ignoring trailer\n"); bytes -= 8; tp->done = 1; } memcpy(tp->raw_data + tp->rx_bytes, buf, bytes); tp->rx_bytes += bytes; ret = SANE_STATUS_GOOD; } else { DBG(5, "read_from_scanner: error reading status = %d\n", ret); } free(buf); DBG (10, "read_from_scanner: finish rB:%lu len:%lu\n", (unsigned long)(tp->total_bytes - tp->rx_bytes + 8), (unsigned long)bytes); return ret; } /* copies block buffer into front or back image buffer */ /* converts pixel data from input mode (color/gray) to output mode (color/gray/binary) */ /* the output image might be lower dpi than input image, so we scale vertically */ /* the input is already scaled horizontally and padding skipped if required */ /* if the input is mirrored left to right, we fix it here */ static SANE_Status copy_block_to_page(struct scanner *s,int side) { SANE_Status ret = SANE_STATUS_GOOD; struct transfer * block = &s->block_xfr; struct page * page = &s->pages[side]; int image_height = block->total_bytes / block->line_stride; int page_width = page->image->width_pix; int block_page_stride = block->image->width_bytes * block->image->height; int line_reverse = (side == SIDE_BACK) || (s->model == MODEL_FI60F) || (s->model == MODEL_FI65F); int i,j,k=0; int curr_in_row = s->fullscan.rx_bytes/s->fullscan.width_bytes; int last_out_row = (page->bytes_scanned / page->image->width_bytes) - 1; DBG (10, "copy_block_to_page: start\n"); /* skip padding and tl_y */ if (s->fullscan.rx_bytes + s->block_xfr.rx_bytes <= block->line_stride * page->image->y_skip_offset) { DBG (10, "copy_block_to_page: before the start? %d\n", side); return ret; } else if (s->fullscan.rx_bytes < block->line_stride * page->image->y_skip_offset) { k = page->image->y_skip_offset - s->fullscan.rx_bytes / block->line_stride; DBG (10, "copy_block_to_page: k start? %d\n", k); } /* loop over all the lines in the block */ for (i = k; i < image_height; i++) { /* determine source and dest rows (dpi scaling) */ int this_in_row = curr_in_row + i; int this_out_row = (this_in_row - page->image->y_skip_offset) * page->image->y_res / s->fullscan.y_res; DBG (15, "copy_block_to_page: in %d out %d lastout %d\n", this_in_row, this_out_row, last_out_row); DBG (15, "copy_block_to_page: bs %d wb %d\n", page->bytes_scanned, page->image->width_bytes); /* don't walk off the end of the output buffer */ if(this_out_row >= page->image->height || this_out_row < 0){ DBG (10, "copy_block_to_page: out of space? %d\n", side); DBG (10, "copy_block_to_page: rx:%d tx:%d tot:%d line:%d\n", page->bytes_scanned, page->bytes_read, page->bytes_total,page->image->width_bytes); return ret; } /* ok, different output row, so we do the math */ if(this_out_row > last_out_row){ unsigned char * p_in = block->image->buffer + (side * block_page_stride) + (i * block->image->width_bytes) + page->image->x_start_offset * 3; unsigned char * p_out = page->image->buffer + this_out_row * page->image->width_bytes; unsigned char * lineStart = p_out; last_out_row = this_out_row; if (block->mode == MODE_COLOR){ /* reverse order for back side or FI-60F scanner */ if (line_reverse) p_in += (page_width - 1) * 3; /* convert all of the pixels in this row */ for (j = 0; j < page_width; j++) { unsigned char r, g, b; if (s->model == MODEL_S300 || s->model == MODEL_S1300i) { r = p_in[1]; g = p_in[2]; b = p_in[0]; } else /* MODEL_FI60F or MODEL_FI65F or MODEL_S1100 */ { r = p_in[0]; g = p_in[1]; b = p_in[2]; } if (s->mode == MODE_COLOR) { *p_out++ = r; *p_out++ = g; *p_out++ = b; } else if (s->mode == MODE_GRAYSCALE) { *p_out++ = (r + g + b) / 3; } else if (s->mode == MODE_LINEART) { s->dt.buffer[j] = (r + g + b) / 3; /* stores dt temp image buffer and binarize afterward */ } if (line_reverse) p_in -= 3; else p_in += 3; } } /* grayscale input */ else{ unsigned char * p_in = block->image->buffer + (side * block_page_stride) + (i * block->image->width_bytes) + page->image->x_start_offset; /* reverse order for back side or FI-60F scanner */ if (line_reverse) p_in += (page_width - 1); //memcpy(p_out,p_in,page->image->width_bytes); for (j = 0; j < page_width; j++) { if (s->mode == MODE_GRAYSCALE) { *p_out++ = *p_in; } else if (s->mode == MODE_LINEART) { s->dt.buffer[j] = *p_in; /* stores dt temp image buffer and binarize afterward */ } if (line_reverse) p_in--; else p_in++; } } /* skip non-transfer pixels in block image buffer */ if (line_reverse) p_in -= page->image->x_offset_bytes; else p_in += page->image->x_offset_bytes; /* for MODE_LINEART, binarize the gray line stored in the temp image buffer(dt) */ /* because dt.width = page_width, we pass page_width */ if (s->mode == MODE_LINEART) binarize_line(s, lineStart, page_width); page->bytes_scanned += page->image->width_bytes; } } DBG (10, "copy_block_to_page: finish\n"); return ret; } /*uses the threshold/threshold_curve to control binarization*/ static SANE_Status binarize_line(struct scanner *s, unsigned char *lineOut, int width) { SANE_Status ret = SANE_STATUS_GOOD; int j, windowX, sum = 0; /* ~1mm works best, but the window needs to have odd # of pixels */ windowX = 6 * s->resolution / 150; if (!(windowX % 2)) windowX++; /*second, prefill the sliding sum*/ for (j = 0; j < windowX; j++) sum += s->dt.buffer[j]; /* third, walk the dt buffer, update the sliding sum, */ /* determine threshold, output bits */ for (j = 0; j < width; j++) { /*output image location*/ int offset = j % 8; unsigned char mask = 0x80 >> offset; int thresh = s->threshold; /* move sum/update threshold only if there is a curve*/ if (s->threshold_curve) { int addCol = j + windowX/2; int dropCol = addCol - windowX; if (dropCol >= 0 && addCol < width) { sum -= s->dt.buffer[dropCol]; sum += s->dt.buffer[addCol]; } thresh = s->dt_lut[sum/windowX]; } /*use average to lookup threshold*/ if (s->dt.buffer[j] > thresh) *lineOut &= ~mask; /* white */ else *lineOut |= mask; /* black */ if (offset == 7) lineOut++; } return ret; } /* * @@ Section 4 - SANE cleanup functions */ /* * Cancels a scan. * * 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 operation 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) { /*FIXME: actually ask the scanner to stop?*/ struct scanner * s = (struct scanner *) handle; DBG (10, "sane_cancel: start\n"); s->started = 0; DBG (10, "sane_cancel: finish\n"); } /* * 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) { struct scanner * s = (struct scanner *) handle; DBG (10, "sane_close: start\n"); /* still connected- drop it */ if(s->fd >= 0){ sane_cancel(handle); lamp(s, 0); disconnect_fd(s); } DBG (10, "sane_close: finish\n"); } static SANE_Status disconnect_fd (struct scanner *s) { DBG (10, "disconnect_fd: start\n"); if(s->fd > -1){ DBG (15, "disconnecting usb device\n"); sanei_usb_close (s->fd); s->fd = -1; } DBG (10, "disconnect_fd: finish\n"); return SANE_STATUS_GOOD; } static SANE_Status destroy(struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; DBG (10, "destroy: start\n"); teardown_buffers(s); if(s->sane.name){ free((void *) s->sane.name); } if(s->sane.vendor){ free((void *) s->sane.vendor); } if(s->sane.model){ free((void *) s->sane.model); } free(s); DBG (10, "destroy: finish\n"); return ret; } static SANE_Status teardown_buffers(struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; DBG (10, "teardown_buffers: start\n"); /* temporary cal data */ if(s->coarsecal.buffer){ free(s->coarsecal.buffer); s->coarsecal.buffer = NULL; } if(s->darkcal.buffer){ free(s->darkcal.buffer); s->darkcal.buffer = NULL; } if(s->sendcal.buffer){ free(s->sendcal.buffer); s->sendcal.buffer = NULL; } if(s->cal_image.raw_data){ free(s->cal_image.raw_data); s->cal_image.raw_data = NULL; } if(s->cal_data.raw_data){ free(s->cal_data.raw_data); s->cal_data.raw_data = NULL; } /* image slice */ if(s->block_img.buffer){ free(s->block_img.buffer); s->block_img.buffer = NULL; } if(s->block_xfr.raw_data){ free(s->block_xfr.raw_data); s->block_xfr.raw_data = NULL; } /* dynamic thresh slice */ if(s->dt.buffer){ free(s->dt.buffer); s->dt.buffer = NULL; } /* image buffer to hold frontside data */ if(s->front.buffer){ free(s->front.buffer); s->front.buffer = NULL; } /* image buffer to hold backside data */ if(s->back.buffer){ free(s->back.buffer); s->back.buffer = NULL; } DBG (10, "teardown_buffers: finish\n"); return ret; } /* * 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 scanner *dev, *next; DBG (10, "sane_exit: start\n"); for (dev = scanner_devList; dev; dev = next) { next = dev->next; destroy(dev); } if (sane_devArray) free (sane_devArray); scanner_devList = NULL; sane_devArray = NULL; DBG (10, "sane_exit: finish\n"); } /* * @@ Section 5 - misc helper functions */ /* * take a bunch of pointers, send commands to scanner */ static SANE_Status do_cmd(struct scanner *s, int shortTime, unsigned char * cmdBuff, size_t cmdLen, unsigned char * outBuff, size_t outLen, unsigned char * inBuff, size_t * inLen ) { /* sanei_usb overwrites the transfer size, so make some local copies */ size_t loc_cmdLen = cmdLen; size_t loc_outLen = outLen; size_t loc_inLen = 0; int cmdTime = USB_COMMAND_TIME; int outTime = USB_DATA_TIME; int inTime = USB_DATA_TIME; int ret = 0; DBG (10, "do_cmd: start\n"); if(shortTime){ cmdTime /= 20; outTime /= 20; inTime /= 20; } /* this command has a cmd component, and a place to get it */ if(cmdBuff && cmdLen && cmdTime){ /* change timeout */ sanei_usb_set_timeout(cmdTime); /* write the command out */ DBG(25, "cmd: writing %ld bytes, timeout %d\n", (long)cmdLen, cmdTime); hexdump(30, "cmd: >>", cmdBuff, cmdLen); ret = sanei_usb_write_bulk(s->fd, cmdBuff, &cmdLen); DBG(25, "cmd: wrote %ld bytes, retVal %d\n", (long)cmdLen, ret); if(ret == SANE_STATUS_EOF){ DBG(5,"cmd: got EOF, returning IO_ERROR\n"); return SANE_STATUS_IO_ERROR; } if(ret != SANE_STATUS_GOOD){ DBG(5,"cmd: return error '%s'\n",sane_strstatus(ret)); return ret; } if(loc_cmdLen != cmdLen){ DBG(5,"cmd: wrong size %ld/%ld\n", (long)loc_cmdLen, (long)cmdLen); return SANE_STATUS_IO_ERROR; } } /* this command has a write component, and a place to get it */ if(outBuff && outLen && outTime){ /* change timeout */ sanei_usb_set_timeout(outTime); DBG(25, "out: writing %ld bytes, timeout %d\n", (long)outLen, outTime); hexdump(30, "out: >>", outBuff, outLen); ret = sanei_usb_write_bulk(s->fd, outBuff, &outLen); DBG(25, "out: wrote %ld bytes, retVal %d\n", (long)outLen, ret); if(ret == SANE_STATUS_EOF){ DBG(5,"out: got EOF, returning IO_ERROR\n"); return SANE_STATUS_IO_ERROR; } if(ret != SANE_STATUS_GOOD){ DBG(5,"out: return error '%s'\n",sane_strstatus(ret)); return ret; } if(loc_outLen != outLen){ DBG(5,"out: wrong size %ld/%ld\n", (long)loc_outLen, (long)outLen); return SANE_STATUS_IO_ERROR; } } /* this command has a read component, and a place to put it */ if(inBuff && inLen && inTime){ loc_inLen = *inLen; DBG(25, "in: memset %ld bytes\n", (long)*inLen); memset(inBuff,0,*inLen); /* change timeout */ sanei_usb_set_timeout(inTime); DBG(25, "in: reading %ld bytes, timeout %d\n", (long)*inLen, inTime); ret = sanei_usb_read_bulk(s->fd, inBuff, inLen); DBG(25, "in: retVal %d\n", ret); if(ret == SANE_STATUS_EOF){ DBG(5,"in: got EOF, continuing\n"); } else if(ret != SANE_STATUS_GOOD){ DBG(5,"in: return error '%s'\n",sane_strstatus(ret)); return ret; } DBG(25, "in: read %ld bytes\n", (long)*inLen); if(*inLen){ hexdump(30, "in: <<", inBuff, *inLen); } if(loc_inLen != *inLen){ ret = SANE_STATUS_EOF; DBG(5,"in: short read %ld/%ld\n", (long)loc_inLen, (long)*inLen); } } DBG (10, "do_cmd: finish\n"); return ret; } /** * 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; max_size = MAX(max_size, size); } return max_size; } /** * 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; if(DBG_LEVEL < level) return; 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; } *ptr = '\0'; DBG (level, "%s\n", line); } /** * 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 (15, "%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) { DBG (10, "sane_get_select_fd\n"); DBG (15, "%p %d\n", h, *fdp); return SANE_STATUS_UNSUPPORTED; } /* s->page_width stores the user setting * for the paper width in adf. sometimes, * we need a value that differs from this * due to using FB */ static int get_page_width(struct scanner *s) { /* scanner max for fb */ if(s->source == SOURCE_FLATBED){ return s->max_x; } return s->page_width; } /* s->page_height stores the user setting * for the paper height in adf. sometimes, * we need a value that differs from this * due to using FB. */ static int get_page_height(struct scanner *s) { /* scanner max for fb */ if(s->source == SOURCE_FLATBED){ return s->max_y; } return s->page_height; } backends-1.3.0/backend/epjitsu.conf.in000066400000000000000000000052461456256263500176400ustar00rootroot00000000000000# For scanners connected via USB on a known device (kernel driver): #usb /dev/usb/scanner0 # For 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. kitno455 at # gmail dot com - with epjitsu in the subject line # These devices require a firmware file in order to function, which must be # extracted from the Fujitsu Windows driver. Presumably the Mac versions # contain the firmware as well, but the author has no access such a machine. # Firmware is installed in several different locations by the fujitsu software, # using the windows 'search' feature to look for '*.nal' is the easiest way to # find them. They should be ~65K, and have the scanner's name as part of the # file name. They are often inside a .cab file. # To extract .nal files from cabinet files on Linux, use the following steps: # (you need the "unshield" tool, which is - in Debian - in the unshield package) # # 1) Mount the ScanSnap installation DVD # (mount point e.g. /media/dvd) # # 2) Extract the .nal files c$ from the cabinet files on the DVD # using the following shell script: # # # loop over all cabinet files found on the DVD # for cab in $(find /media/dvd/ -name \*.cab); do # # search for .nal files in the cabinet files # nalinfo=$(unshield l $cab | grep '\.nal$') # # # we found something # if [ -n "$nalinfo" ]; then # #echo -e "=== $cab ===\n$nalinfo" # # # loop over all fields in $nalinfo # for nal in $nalinfo; do # # if the element of $nalinfo is a .nal file name # if echo "$nal" | grep -q '\.nal$' - 2>/dev/null; then # # extract .nal file form the cabinet file # unshield x "$cab" "${nal##*\\}" # fi # done # fi # done # Copy the file someplace sane can reach it. Then update the line below. # NOTE: the firmware line must occur BEFORE the usb line for your scanner # Fujitsu fi-60F firmware @DATADIR@/sane/epjitsu/60f_0A00.nal usb 0x04c5 0x10c7 # Fujitsu S300 firmware @DATADIR@/sane/epjitsu/300_0C00.nal usb 0x04c5 0x1156 # Fujitsu S300M firmware @DATADIR@/sane/epjitsu/300M_0C00.nal usb 0x04c5 0x117f # Fujitsu fi-65F firmware @DATADIR@/sane/epjitsu/65f_0A01.nal usb 0x04c5 0x11bd # Fujitsu S1300 firmware @DATADIR@/sane/epjitsu/1300_0C26.nal usb 0x04c5 0x11ed # Fujitsu S1100 firmware @DATADIR@/sane/epjitsu/1100_0B00.nal usb 0x04c5 0x1200 # Fujitsu S1300i firmware @DATADIR@/sane/epjitsu/1300i_0D12.nal usb 0x04c5 0x128d # Fujitsu S1100i firmware @DATADIR@/sane/epjitsu/1100i_0A00.nal usb 0x04c5 0x1447 backends-1.3.0/backend/epjitsu.h000066400000000000000000000264001456256263500165300ustar00rootroot00000000000000#ifndef EPJITSU_H #define EPJITSU_H /* * Part of SANE - Scanner Access Now Easy. * Please see opening comment in epjitsu.c */ /* ------------------------------------------------------------------------- * 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 * still the possibility to say so, later. */ enum scanner_Option { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_SOURCE, /*adffront/adfback/adfduplex/fb*/ OPT_MODE, /*mono/gray/color*/ OPT_RES, OPT_GEOMETRY_GROUP, OPT_TL_X, OPT_TL_Y, OPT_BR_X, OPT_BR_Y, OPT_PAGE_WIDTH, OPT_PAGE_HEIGHT, OPT_ENHANCEMENT_GROUP, OPT_BRIGHTNESS, OPT_CONTRAST, OPT_GAMMA, OPT_THRESHOLD, OPT_THRESHOLD_CURVE, OPT_SENSOR_GROUP, OPT_SCAN_SW, OPT_HOPPER, OPT_TOP, OPT_ADF_OPEN, OPT_SLEEP, /* must come last: */ NUM_OPTIONS }; #define FIRMWARE_LENGTH 0x10000 #define MAX_IMG_PASS 0x10000 #define MAX_IMG_BLOCK 0x80000 struct image { int width_pix; int width_bytes; int height; int pages; int mode; int x_res; int y_res; int x_start_offset; int x_offset_bytes; int y_skip_offset; unsigned char * buffer; }; struct transfer { int plane_width; /* in RGB pixels */ int plane_stride; /* in bytes */ int line_stride; /* in bytes */ int total_bytes; int rx_bytes; int done; int mode; int x_res; int y_res; unsigned char * raw_data; struct image * image; }; struct page { int bytes_total; int bytes_scanned; int bytes_read; int lines_rx; /* received from scanner */ int lines_pass; /* passed thru from scanner to user (might be smaller than tx for 225dpi) */ int lines_tx; /* transmitted to user */ int done; struct image *image; }; struct scanner { /* --------------------------------------------------------------------- */ /* immutable values which are set during init of scanner. */ struct scanner *next; int missing; int model; int usb_power; int has_fb; int has_adf; int has_adf_duplex; int min_res; int max_res; float white_factor[3]; int adf_height_padding; /* 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; /* --------------------------------------------------------------------- */ /* immutable values which are set during inquiry probing of the scanner. */ SANE_Device sane; /*contains: name, vendor, model, type*/ /* --------------------------------------------------------------------- */ /* changeable SANE_Option structs provide our interface to frontend. */ /* long array of option structs */ SANE_Option_Descriptor opt[NUM_OPTIONS]; /* --------------------------------------------------------------------- */ /* 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 */ /*mode group, room for lineart, gray, color, null */ SANE_String_Const source_list[5]; SANE_String_Const mode_list[4]; SANE_Range res_range; /*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; /*enhancement group*/ SANE_Range brightness_range; SANE_Range contrast_range; SANE_Range gamma_range; SANE_Range threshold_range; SANE_Range threshold_curve_range; /* --------------------------------------------------------------------- */ /* changeable vars to hold user input. modified by SANE_Options above */ /*mode group*/ int source; /* adf or fb */ int mode; /* color,lineart,etc */ int resolution; /* 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 contrast; int gamma; int threshold; int threshold_curve; int height; /* may run out on adf */ /* --------------------------------------------------------------------- */ /* values which are set by user parameter changes, scanner specific */ unsigned char * setWindowCoarseCal; /* sent before coarse cal */ size_t setWindowCoarseCalLen; unsigned char * setWindowFineCal; /* sent before fine cal */ size_t setWindowFineCalLen; unsigned char * setWindowSendCal; /* sent before send cal */ size_t setWindowSendCalLen; unsigned char * sendCal1Header; /* part of 1b c3 command */ size_t sendCal1HeaderLen; unsigned char * sendCal2Header; /* part of 1b c4 command */ size_t sendCal2HeaderLen; unsigned char * setWindowScan; /* sent before scan */ size_t setWindowScanLen; /* --------------------------------------------------------------------- */ /* values which are set by scanning functions to keep track of pages, etc */ int started; int side; /* holds temp buffers for getting 16 lines of cal data */ struct transfer cal_image; struct image coarsecal; struct image darkcal; struct image lightcal; /* holds temp buffer for building calibration data */ struct transfer cal_data; struct image sendcal; /* scanner transmits more data per line than requested */ /* due to padding and/or duplex interlacing */ /* the scan struct holds these larger numbers, but image buffer is unused */ struct { int done; int mode; int x_res; int y_res; int height; int rx_bytes; int width_bytes; int total_bytes; } fullscan; /* The page structs contain data about the progress as the application reads */ /* data from the front/back image buffers via the sane_read() function */ struct page pages[2]; /* scanner transmits data in blocks, up to 512k */ /* but always ends on a scanline. */ /* the block struct holds the most recent buffer */ struct transfer block_xfr; struct image block_img; /* temporary buffers used by dynamic threshold code */ struct image dt; unsigned char dt_lut[256]; /* final-sized front image, always used */ struct image front; /* final-sized back image, only used during duplex/backside */ struct image back; /* --------------------------------------------------------------------- */ /* values used by the command and data sending function */ int fd; /* The scanner device file descriptor. */ /* --------------------------------------------------------------------- */ /* values which are used by the get hardware status command */ time_t last_ghs; int hw_scan_sw; int hw_hopper; int hw_top; int hw_adf_open; int hw_sleep; }; #define MODEL_NONE (1<<0) #define MODEL_S300 (1<<1) #define MODEL_FI60F (1<<2) #define MODEL_S1100 (1<<3) #define MODEL_S1300i (1<<4) #define MODEL_FI65F (1<<5) #define USB_COMMAND_TIME 10000 #define USB_DATA_TIME 10000 #define SIDE_FRONT 0 #define SIDE_BACK 1 #define SOURCE_FLATBED 0 #define SOURCE_ADF_FRONT 1 #define SOURCE_ADF_BACK 2 #define SOURCE_ADF_DUPLEX 3 #define MODE_COLOR 0 #define MODE_GRAYSCALE 1 #define MODE_LINEART 2 #define WINDOW_COARSECAL 0 #define WINDOW_FINECAL 1 #define WINDOW_SENDCAL 2 #define WINDOW_SCAN 3 #define EPJITSU_PAPER_INGEST 1 #define EPJITSU_PAPER_EJECT 0 /* ------------------------------------------------------------------------- */ #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 PIX_TO_SCANNER_UNIT(number, dpi) SANE_UNFIX(SANE_FIX((number) * 1200 / dpi )) #define SCANNER_UNIT_TO_PIX(number, dpi) SANE_UNFIX(SANE_FIX((number) * dpi / 1200 )) #define CONFIG_FILE "epjitsu.conf" #ifndef PATH_MAX # define PATH_MAX 1024 #endif /* ------------------------------------------------------------------------- */ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize); SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only); SANE_Status sane_open (SANE_String_Const name, SANE_Handle * handle); SANE_Status sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking); SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int * fdp); 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, SANE_Action action, void *val, SANE_Int * info); SANE_Status sane_start (SANE_Handle handle); SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params); SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len); void sane_cancel (SANE_Handle h); void sane_close (SANE_Handle h); void sane_exit (void); /* ------------------------------------------------------------------------- */ static SANE_Status attach_one (const char *devicename); static SANE_Status connect_fd (struct scanner *s); static SANE_Status disconnect_fd (struct scanner *s); static SANE_Status do_cmd(struct scanner *s, int shortTime, unsigned char * cmdBuff, size_t cmdLen, unsigned char * outBuff, size_t outLen, unsigned char * inBuff, size_t * inLen ); /* static SANE_Status load_calibration (struct scanner *s); static SANE_Status read_from_scanner_gray(struct scanner *s); */ /* commands */ static SANE_Status load_fw(struct scanner *s); static SANE_Status get_ident(struct scanner *s); static SANE_Status change_params(struct scanner *s); static SANE_Status destroy(struct scanner *s); static SANE_Status teardown_buffers(struct scanner *s); static SANE_Status setup_buffers(struct scanner *s); static SANE_Status object_position(struct scanner *s, int ingest); static SANE_Status six5 (struct scanner *s); static SANE_Status coarsecal(struct scanner *s); static SANE_Status finecal(struct scanner *s); static SANE_Status send_lut(struct scanner *s); static SANE_Status lamp(struct scanner *s, unsigned char set); static SANE_Status set_window(struct scanner *s, int window); static SANE_Status scan(struct scanner *s); static SANE_Status read_from_scanner(struct scanner *s, struct transfer *tp); static SANE_Status descramble_raw_gray(struct scanner *s, struct transfer * tp); static SANE_Status descramble_raw(struct scanner *s, struct transfer * tp); static SANE_Status copy_block_to_page(struct scanner *s, int side); static SANE_Status binarize_line(struct scanner *s, unsigned char *lineOut, int width); static SANE_Status get_hardware_status (struct scanner *s); static SANE_Status load_lut (unsigned char * lut, int in_bits, int out_bits, int out_min, int out_max, int slope, int offset); static int get_page_width (struct scanner *s); static int get_page_height (struct scanner *s); static unsigned char get_stat(struct scanner *s); /* utils */ static void update_transfer_totals(struct transfer * t); static void hexdump (int level, char *comment, unsigned char *p, int l); static size_t maxStringSize (const SANE_String_Const strings[]); #endif /* EPJITSU_H */ backends-1.3.0/backend/epson.c000066400000000000000000005322401456256263500161700ustar00rootroot00000000000000/* epson.c - SANE library for Epson flatbed scanners. Based on Kazuhiro Sasayama previous Work on epson.[ch] file from the SANE package. Original code taken from sane-0.71 Copyright (C) 1997 Hypercore Software Design, Ltd. modifications Copyright (C) 1998-1999 Christian Bucher Copyright (C) 1998-1999 Kling & Hautzinger GmbH Copyright (C) 1999 Norihiko Sawa 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.47 - 2006-08-21" #define SANE_EPSON_BUILD 247 /* 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, see . 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. */ /* 2006-08-21 Fix buffer overflow error (submitted by Johannes Meixner) 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 2004-10-16 Added USB ID for Expression 10000XL 2004-05-08 Disable feed() for Perfection1640 2004-02-08 Reformat all source code with "indent -bli0" 2004-02-01 Added D7 function level as copy of D1 for CX-6400 Added IDs for CX-6400 and Perfection 4870 2003-10-27 Replaced DBG(0, ... with DBG(1, ... 2003-09-12 Increment only once in loop to find USB scanners Fix rounding problem when determining number of lines to scan 2003-08-21 Removed '//' comments - again ... Added EPSON Kowa copyright message 2003-08-15 Added support for GT-30000, with support for the ADF in simplex mode Borrowed some code from the EPSON Kowa IScan version of the backend Use sanei_scsi_cmd2() to send commands. This makes this backend useable for SBP-2 under FreeBSD 2003-05-11 Initialize OPT_LIMIT_RESOLUTION before first call to filter_resolution_list() Fix memory problem in get_identity_information(). Both problems were reported to the Debian bug database. 2003-03-26 Fixed two warnings reported by der Mouse 2003-02-16 Code cleanup, use more descriptive variable names. 2003-02-15 Move sanei_usb_init() to sane_init(). Thanks to Ron Cemer for providing the patch. 2003-02-15 Fix problem with "usb syntax in config file 2002-12-28 Added advanced option to display only short resolution list for displays that can not show the complete list. 2002-11-23 Fixed problem with dropout color. 2002-11-03 Full libusb support. 2002-10-05 Fixed problem with incorrect response to sane_get_parameters() in certain situations. 2002-09-01 USB scanners are now using libsane-usb functions 2002-08-17 Fixed typo in variable name. Fixed IEEE-1394 problem with Perfection-2450. Fixed problem with older B3 level SCSI scanners that do not support the extended status request. 2002-04-22 Declare close_scanner() and open_scanner() before they are used. 2002-04-13 Check if scanner needs to be opened for the reset call. (Thanks to Thomas Wenrich for pointing this out) Added product IDs for Perfection 1650 and 2450 2002-01-18 Recognize GT-xxxx type scanners also when using the SCSI or IEEE-1394 interface 2002-01-06 Disable TEST_IOCTL again, which was enabled by accident. Also protect the ioctl portion with an #ifdef __linux__ 2002-01-05 Version 0.2.17 Check for and set s->fd to -1 when device is closed. Removed black gamma table - only use RGB even for grayscale 2002-01-01 Do not call access() for OS/2 systems 2001-11-13 Version 0.2.16 Do not call access() for parallel port scanners. 2001-11-11 Version 0.2.15 Fixed "wait-for-button" functionality, accidentally merged back wrong version after code freeze. Corrected "need-strange-reorder" recognition. Added IOCTL support to header file. 2001-11-10 Version 0.2.14 Added "wait-for-button" functionality 2001-10-30 I18N patches (Stefan Roellin) 2001-10-28 Fixed bug with 1650 recognition 2001-06-09 Version 0.2.09 Changed debug level for sense handler from 0 to 2 2001-05-25 Version 0.2.07 Allow more than 8 bit color depth even for preview mode since Xsane can handle this. Some code cleanup. 2001-05-24 Removed ancient code that was used to determine the resolution back when the backend still had a slider for the resolution selection. 2001-05-22 Version 0.2.06 Added sense_handler to support the GT-8000 scanner. Thanks to Matthias Trute for figuring out the details. Also added experimental code to use USB scanner probing. Need kernel patch for this. 2001-05-19 Version 0.2.05 fixed the year in the recent change log entries - I now that it's 2001... Finally fixed the TPU problem with B4 level scanners 2001-05-13 Version 0.2.04 Removed check for '\n' before end of line Free memory malloced in sane_get_devices() in sane_exit() again 2001-04-22 Version 0.2.03 Check first if the scanner does support the set film type and set focus position before the GUI elements are displayed. This caused problems with older (B4 level) scanners when a TPU was connected. 2001-03-31 Version 0.2.02 2001-03-17 Next attempt to get the reported number of lines correct for the "color shuffling" part. Added more comments. 2000-12-25 Version 0.2.01 Fixed problem with bilevel scanning with Perfection610: The line count has to be an even number with this scanner. Several initialization fixes regarding bit depth selection. This version goes back into the CVS repository, the 1.0.4 release is out and therefore the code freeze is over. Some general cleanup, added more comments. 2000-12-09 Version 0.2.00 Cleaned up printing of gamma table data. 16 elements are now printed in one line without the [epson] in between the values. Values are only printed for Debug levels >= 10. 2000-12-04 We've introduced the concept of inverting images when scanning from a TPU. This is fine, but the user supplied gamma tables no longer work. This is because the data a frontend is going to compute a gamma table for is not what the scanner actually sent. So, we have to back into the proper gamma table. I think this works. See set_gamma_table. (mjp) 2000-12-03 added the 12/14/16 bit support again. 2000-12-03 Version 0.1.38 removed changes regarding 12/14 bit support because of SANE feature freeze for 1.0.4. The D1 fix for reading the values from the scanner instead of using hardcoded values and the fix for the off-by-one error in the reorder routine are still in the code base. Also force reload after change of scan mode. The full backend can be downloaded from my web site at http://www.freecolormanagement.com/sane 2000-12-03 Fixed off-by-one error in color reordering function. 2000-12-02 Read information about optical resolution and line distance from scanner instead of hardcoded values. Add support for color depth > 8 bits per channel. 2000-11-23 Display "Set Focus" control only for scanners that can actually handle the command. 2000-11-19 Added support for the "set focus position" command, this is necessary for the Expression1600. 2000-07-28 Changed #include <...> to #include "..." for the sane/... include files. 2000-07-26 Fixed problem with Perfection610: The variable s->color_shuffle_line was never correctly initialized 2000-06-28 When closing the scanner device the data that's still in the scanner, waiting to be transferred is flushed. This fixes the problem with scanimage -T 2000-06-13 Invert image when scanning negative with TPU, Show film type only when TPU is selected 2000-06-13 Initialize optical_res to 0 (Dave Hill) 2000-06-07 Fix in sane_close() - found by Henning Meier-Geinitz 2000-06-01 Threshold should only be active when scan depth is 1 and halftoning is off. (mjp) 2000-05-28 Turned on scanner based color correction. Dependencies between many options are now being enforced. For instance, auto area seg (AAS) should only be on when scan depth == 1. Added some routines to active and deactivate options. Routines report if option changed. Help prevent extraneous option reloads. Split sane_control_option in getvalue and setvalue. Further split up setvalue into several different routines. (mjp) 2000-05-21 In sane_close use close_scanner instead of just the SCSI close function. 2000-05-20 ... finally fixed the problem with the 610 Added resolution_list to Epson_Device structure in epson.h - this fixes a bug that caused problems when more than one EPSON scanner was connected. 2000-05-13 Fixed the color problem with the Perfection 610. The few lines with "garbage" at the beginning of the scan are not yet removed. 2000-05-06 Added support for multiple EPSON scanners. At this time this may not be bug free, but it's a start and it seems to work well with just one scanner. 2000-04-06 Did some cleanup on the gamma correction part. The user defined table is now initialized to gamma=1, the gamma handling is also no longer depending on platform specific tables (handled instead by pointers to the actual tables) 2000-03-27 Disable request for push button status 2000-03-22 Removed free() calls to static strings to remove compile warnings. These were introduced to apparently fix an OS/2 bug. It now turned out that they are not necessary. The real fix was in the repository for a long time (2000-01-25). 2000-03-19 Fixed problem with A4 level devices - they use the line mode instead of the block mode. The routine to handle this was screwed up pretty bad. Now I have a solid version that handles all variations of line mode (automatically deals with the order the color lines are sent). 2000-03-06 Fixed occasional crash after warm up when the "in warmup state" went away in between doing ESC G and getting the extended status message. 2000-03-02 Code cleanup, disabled ZOOM until I have time to deal with all the side effects. 2000-03-01 More D1 fixes. In the future I have to come up with a more elegant solution to distinguish between different function levels. The level > n does not work anymore with D1. Added support for "set threshold" and "set zoom". 2000-02-23 First stab at level D1 support, also added a test for valid "set halftone" command to enable OPT_HALFTONE 2000-02-21 Check for "warming up" in after sane_start. This is IMHO a horrible hack, but that's the only way without a major redesign that will work. (KHK) 2000-02-20 Added some cleanup on error conditions in attach() Use new sanei_config_read() instead of fgets() for compatibility with OS/2 (Yuri Dario) 2000-02-19 Changed some "int" to "size_t" types Removed "Preview Resolution" Implemented resolution list as WORD_LIST instead of a RANGE (KHK) 2000-02-11 Default scan source is always "Flatbed", regardless of installed options. Corrected some typos. (KHK) 2000-02-03 Gamma curves now coupled with gamma correction menu. Only when "User defined" is selected are the curves selected. (Dave Hill) Renamed "Contrast" to "Gamma Correction" (KHK) 2000-02-02 "Brown Paper Bag Release" Put the USB fix finally into the CVS repository. 2000-02-01 Fixed problem with USB scanner not being recognized because of the changes to attach a few days ago. (KHK) 2000-01-29 fixed core dump with xscanimage by moving the gamma curves to the standard interface (no longer advanced) Removed pragma pack() from source code to make it easier to compile on non-gcc compilers (KHK) 2000-01-26 fixed problem with resolution selection when using the resolution list in xsane (KHK) 2000-01-25 moved the section where the device name is assigned in attach. This avoids the core dump of frontend applications when no scanner is found (Dave Hill) 2000-01-24 reorganization of SCSI related "helper" functions started support for user defined color correction - this is not yet available via the UI (Christian Bucher) 2000-01-24 Removed C++ style comments '//' (KHK) */ /* #define TEST_IOCTL */ /* DON'T CHANGE THE NEXT LINE ! */ /* #undef FORCE_COLOR_SHUFFLE */ #ifdef _AIX #include /* MUST come first for AIX! */ #endif /* --------------------- SANE INTERNATIONALISATION ------------------ */ #ifndef SANE_I18N #define SANE_I18N(text) (text) #endif #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_scsi.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_pio.h" #define BACKEND_NAME epson #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_config.h" #include "epson.h" #include "epson_scsi.h" #include "epson_usb.h" #define EPSON_CONFIG_FILE "epson.conf" #ifndef PATH_MAX #define PATH_MAX (1024) #endif #define walloc(x) (x *)malloc(sizeof(x)) #define walloca(x) (x *)alloca(sizeof(x)) #ifndef XtNumber #define XtNumber(x) ( sizeof(x)/ sizeof(x[0]) ) #define XtOffset(p_type,field) ((size_t)&(((p_type)NULL)->field)) #define XtOffsetOf(s_type,field) XtOffset(s_type*,field) #endif #define NUM_OF_HEX_ELEMENTS (16) /* number of hex numbers per line for data dump */ #define DEVICE_NAME_LEN (16) /* length of device name in extended status */ /* NOTE: you can find these codes with "man ascii". */ #define STX 0x02 #define ACK 0x06 #define NAK 0x15 #define CAN 0x18 #define ESC 0x1B #define PF 0x19 #define S_ACK "\006" #define S_CAN "\030" #define STATUS_FER 0x80 /* fatal error */ #define STATUS_AREA_END 0x20 /* area end */ #define STATUS_OPTION 0x10 /* option installed */ #define EXT_STATUS_FER 0x80 /* fatal error */ #define EXT_STATUS_FBF 0x40 /* flat bed scanner */ #define EXT_STATUS_WU 0x02 /* warming up */ #define EXT_STATUS_PB 0x01 /* scanner has a push button */ #define EXT_STATUS_IST 0x80 /* option detected */ #define EXT_STATUS_EN 0x40 /* option enabled */ #define EXT_STATUS_ERR 0x20 /* other error */ #define EXT_STATUS_PE 0x08 /* no paper */ #define EXT_STATUS_PJ 0x04 /* paper jam */ #define EXT_STATUS_OPN 0x02 /* cover open */ #define EPSON_LEVEL_A1 0 #define EPSON_LEVEL_A2 1 #define EPSON_LEVEL_B1 2 #define EPSON_LEVEL_B2 3 #define EPSON_LEVEL_B3 4 #define EPSON_LEVEL_B4 5 #define EPSON_LEVEL_B5 6 #define EPSON_LEVEL_B6 7 #define EPSON_LEVEL_B7 8 #define EPSON_LEVEL_B8 9 #define EPSON_LEVEL_F5 10 #define EPSON_LEVEL_D1 11 #define EPSON_LEVEL_D7 12 #define EPSON_LEVEL_D8 13 /* there is also a function level "A5", which I'm ignoring here until somebody can convince me that this is still needed. The A5 level was for the GT-300, which was (is) a monochrome only scanner. So if somebody really wants to use this scanner with SANE get in touch with me and we can work something out - khk */ #define EPSON_LEVEL_DEFAULT EPSON_LEVEL_B3 static EpsonCmdRec epson_cmd[] = { /* * request identity * | request identity2 * | | request status * | | | request condition * | | | | set color mode * | | | | | start scanning * | | | | | | set data format * | | | | | | | set resolution * | | | | | | | | set zoom * | | | | | | | | | set scan area * | | | | | | | | | | set brightness * | | | | | | | | | | | set gamma * | | | | | | | | | | | | set halftoning * | | | | | | | | | | | | | set color correction * | | | | | | | | | | | | | | initialize scanner * | | | | | | | | | | | | | | | set speed * | | | | | | | | | | | | | | | | set lcount * | | | | | | | | | | | | | | | | | mirror image * | | | | | | | | | | | | | | | | | | set gamma table * | | | | | | | | | | | | | | | | | | | set outline emphasis * | | | | | | | | | | | | | | | | | | | | set dither * | | | | | | | | | | | | | | | | | | | | | set color correction coefficients * | | | | | | | | | | | | | | | | | | | | | | request extension status * | | | | | | | | | | | | | | | | | | | | | | | control an extension * | | | | | | | | | | | | | | | | | | | | | | | | forward feed / eject * | | | | | | | | | | | | | | | | | | | | | | | | | feed * | | | | | | | | | | | | | | | | | | | | | | | | | | request push button status * | | | | | | | | | | | | | | | | | | | | | | | | | | | control auto area segmentation * | | | | | | | | | | | | | | | | | | | | | | | | | | | | set film type * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set exposure time * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set bay * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set threshold * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set focus position * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | request focus position * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | */ {"A1", 'I', 0, 'F','S', 0, 'G', 0, 'R', 0, 'A', 0, {0,0,0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {"A2", 'I', 0, 'F','S', 0, 'G','D','R','H','A','L',{-3,3,0}, 'Z','B', 0, '@', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {"B1", 'I', 0, 'F','S','C','G','D','R', 0, 'A', 0, {0,0,0}, 0, 'B', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {"B2", 'I', 0, 'F','S','C','G','D','R','H','A','L',{-3,3,0}, 'Z','B', 0, '@', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {"B3", 'I', 0, 'F','S','C','G','D','R','H','A','L',{-3,3,0}, 'Z','B','M','@', 0, 0, 0, 0, 0, 0, 'm','f','e', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {"B4", 'I', 0, 'F','S','C','G','D','R','H','A','L',{-3,3,0}, 'Z','B','M','@','g','d', 0, 'z','Q','b','m','f','e', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {"B5", 'I', 0, 'F','S','C','G','D','R','H','A','L',{-3,3,0}, 'Z','B','M','@','g','d','K','z','Q','b','m','f','e', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {"B6", 'I', 0, 'F','S','C','G','D','R','H','A','L',{-3,3,0}, 'Z','B','M','@','g','d','K','z','Q','b','m','f','e', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {"B7", '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', 0, '!','s','N', 0, 0, 't', 0, 0}, {"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', 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}, }; /* * Definition of the mode_param struct, that is used to * specify the valid parameters for the different scan modes. * * The depth variable gets updated when the bit depth is modified. */ struct mode_param { int color; int mode_flags; int dropout_mask; int depth; }; static struct mode_param mode_params[] = { {0, 0x00, 0x30, 1}, {0, 0x00, 0x30, 8}, {1, 0x02, 0x00, 8} }; static const SANE_String_Const mode_list[] = { SANE_VALUE_SCAN_MODE_LINEART, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_COLOR, NULL }; static const SANE_String_Const adf_mode_list[] = { SANE_I18N ("Simplex"), SANE_I18N ("Duplex"), NULL }; /* * Define the different scan sources: */ #define FBF_STR SANE_I18N("Flatbed") #define TPU_STR SANE_I18N("Transparency Unit") #define ADF_STR SANE_I18N("Automatic Document Feeder") /* * source list need one dummy entry (save device settings is crashing). * NOTE: no const - this list gets created while exploring the capabilities * of the scanner. */ static SANE_String_Const source_list[] = { FBF_STR, NULL, NULL, NULL }; /* some defines to make handling the TPU easier: */ #define FILM_TYPE_POSITIVE (0) #define FILM_TYPE_NEGATIVE (1) static const SANE_String_Const film_list[] = { SANE_I18N ("Positive Film"), SANE_I18N ("Negative Film"), NULL }; static const SANE_String_Const focus_list[] = { SANE_I18N ("Focus on glass"), SANE_I18N ("Focus 2.5mm above glass"), NULL }; /* * TODO: add some missing const. */ #define HALFTONE_NONE 0x01 #define HALFTONE_TET 0x03 static int halftone_params[] = { HALFTONE_NONE, 0x00, 0x10, 0x20, 0x80, 0x90, 0xa0, 0xb0, HALFTONE_TET, 0xc0, 0xd0 }; static const SANE_String_Const halftone_list[] = { SANE_I18N ("None"), SANE_I18N ("Halftone A (Hard Tone)"), SANE_I18N ("Halftone B (Soft Tone)"), SANE_I18N ("Halftone C (Net Screen)"), NULL }; static const SANE_String_Const halftone_list_4[] = { SANE_I18N ("None"), SANE_I18N ("Halftone A (Hard Tone)"), SANE_I18N ("Halftone B (Soft Tone)"), SANE_I18N ("Halftone C (Net Screen)"), SANE_I18N ("Dither A (4x4 Bayer)"), SANE_I18N ("Dither B (4x4 Spiral)"), SANE_I18N ("Dither C (4x4 Net Screen)"), SANE_I18N ("Dither D (8x4 Net Screen)"), NULL }; static const SANE_String_Const halftone_list_7[] = { SANE_I18N ("None"), SANE_I18N ("Halftone A (Hard Tone)"), SANE_I18N ("Halftone B (Soft Tone)"), SANE_I18N ("Halftone C (Net Screen)"), SANE_I18N ("Dither A (4x4 Bayer)"), SANE_I18N ("Dither B (4x4 Spiral)"), SANE_I18N ("Dither C (4x4 Net Screen)"), SANE_I18N ("Dither D (8x4 Net Screen)"), SANE_I18N ("Text Enhanced Technology"), SANE_I18N ("Download pattern A"), SANE_I18N ("Download pattern B"), NULL }; static int dropout_params[] = { 0x00, /* none */ 0x10, /* red */ 0x20, /* green */ 0x30 /* blue */ }; static const SANE_String_Const dropout_list[] = { SANE_I18N ("None"), SANE_I18N ("Red"), SANE_I18N ("Green"), SANE_I18N ("Blue"), NULL }; /* * Color correction: * One array for the actual parameters that get sent to the scanner (color_params[]), * one array for the strings that get displayed in the user interface (color_list[]) * and one array to mark the user defined color correction (dolor_userdefined[]). */ static int color_params[] = { 0x00, 0x01, 0x10, 0x20, 0x40, 0x80 }; static SANE_Bool color_userdefined[] = { SANE_FALSE, SANE_TRUE, SANE_FALSE, SANE_FALSE, SANE_FALSE, SANE_FALSE }; static const SANE_String_Const color_list[] = { SANE_I18N ("No Correction"), SANE_I18N ("User defined"), SANE_I18N ("Impact-dot printers"), SANE_I18N ("Thermal printers"), SANE_I18N ("Ink-jet printers"), SANE_I18N ("CRT monitors"), NULL }; /* * Gamma correction: * The A and B level scanners work differently than the D level scanners, therefore * I define two different sets of arrays, plus one set of variables that get set to * the actually used params and list arrays at runtime. */ static int gamma_params_ab[] = { 0x01, 0x03, 0x00, 0x10, 0x20 }; static const SANE_String_Const gamma_list_ab[] = { SANE_I18N ("Default"), SANE_I18N ("User defined"), SANE_I18N ("High density printing"), SANE_I18N ("Low density printing"), SANE_I18N ("High contrast printing"), NULL }; static SANE_Bool gamma_userdefined_ab[] = { SANE_FALSE, SANE_TRUE, SANE_FALSE, SANE_FALSE, SANE_FALSE, }; static int gamma_params_d[] = { 0x03, 0x04 }; static const SANE_String_Const gamma_list_d[] = { SANE_I18N ("User defined (Gamma=1.0)"), SANE_I18N ("User defined (Gamma=1.8)"), NULL }; static SANE_Bool gamma_userdefined_d[] = { SANE_TRUE, SANE_TRUE }; static SANE_Bool *gamma_userdefined; static int *gamma_params; /* flaming hack to get USB scanners working without timeouts under linux */ /* (cribbed from fujitsu.c) */ static unsigned int r_cmd_count = 0; static unsigned int w_cmd_count = 0; /* Bay list: * this is used for the FilmScan */ static const SANE_String_Const bay_list[] = { " 1 ", " 2 ", " 3 ", " 4 ", " 5 ", " 6 ", NULL }; /* * minimum, maximum, quantization. */ static const SANE_Range u8_range = { 0, 255, 0 }; static const SANE_Range s8_range = { -127, 127, 0 }; static const SANE_Range zoom_range = { 50, 200, 0 }; /* * The "switch_params" are used for several boolean choices */ static int switch_params[] = { 0, 1 }; #define mirror_params switch_params #define speed_params switch_params #define film_params switch_params static const SANE_Range outline_emphasis_range = { -2, 2, 0 }; /* static const SANE_Range gamma_range = { -2, 2, 0 }; */ struct qf_param { SANE_Word tl_x; SANE_Word tl_y; SANE_Word br_x; SANE_Word br_y; }; /* gcc don't like to overwrite const field */ static /*const */ struct qf_param qf_params[] = { {0, 0, SANE_FIX (120.0), SANE_FIX (120.0)}, {0, 0, SANE_FIX (148.5), SANE_FIX (210.0)}, {0, 0, SANE_FIX (210.0), SANE_FIX (148.5)}, {0, 0, SANE_FIX (215.9), SANE_FIX (279.4)}, /* 8.5" x 11" */ {0, 0, SANE_FIX (210.0), SANE_FIX (297.0)}, {0, 0, 0, 0} }; static const SANE_String_Const qf_list[] = { SANE_I18N ("CD"), SANE_I18N ("A5 portrait"), SANE_I18N ("A5 landscape"), SANE_I18N ("Letter"), SANE_I18N ("A4"), SANE_I18N ("Max"), NULL }; static SANE_Word *bitDepthList = NULL; /* * List of pointers to devices - will be dynamically allocated depending * on the number of devices found. */ static const SANE_Device **devlist = 0; /* * Some utility functions */ static size_t max_string_size (const SANE_String_Const strings[]) { size_t size, max_size = 0; int i; for (i = 0; strings[i]; i++) { size = strlen (strings[i]) + 1; if (size > max_size) max_size = size; } return max_size; } typedef struct { u_char code; u_char status; u_char count1; u_char count2; u_char buf[1]; } EpsonHdrRec, *EpsonHdr; typedef struct { u_char code; u_char status; u_char count1; u_char count2; u_char type; u_char level; u_char buf[1]; } EpsonIdentRec, *EpsonIdent; typedef union { EpsonHdrRec hdr; EpsonIdentRec ident; } EpsonHdrUnionRec, *EpsonHdrUnion; typedef struct { u_char code; u_char status; u_short count; u_char buf[1]; } EpsonParameterRec, *EpsonParameter; typedef struct { u_char code; u_char status; u_char buf[4]; } EpsonDataRec, *EpsonData; /* * * */ static EpsonHdrUnion command (Epson_Scanner * s, u_char * cmd, size_t cmd_size, SANE_Status * status); static SANE_Status get_identity_information (SANE_Handle handle); static SANE_Status get_identity2_information (SANE_Handle handle); static int send (Epson_Scanner * s, void *buf, size_t buf_size, SANE_Status * status); static ssize_t receive (Epson_Scanner * s, void *buf, ssize_t buf_size, SANE_Status * status); static SANE_Status color_shuffle (SANE_Handle handle, int *new_length); static SANE_Status request_focus_position (SANE_Handle handle, u_char * position); static SANE_Bool request_push_button_status (SANE_Handle handle, SANE_Bool * theButtonStatus); static void activateOption (Epson_Scanner * s, SANE_Int option, SANE_Bool * change); static void deactivateOption (Epson_Scanner * s, SANE_Int option, SANE_Bool * change); static void setOptionState (Epson_Scanner * s, SANE_Bool state, SANE_Int option, SANE_Bool * change); static void close_scanner (Epson_Scanner * s); static SANE_Status open_scanner (Epson_Scanner * s); SANE_Status sane_auto_eject (Epson_Scanner * s); static SANE_Status attach_one_usb (SANE_String_Const devname); static void filter_resolution_list (Epson_Scanner * s); static void get_size (char c1, char c2, double *w, double *h); static void scan_finish (Epson_Scanner * s); /* * * */ static int send (Epson_Scanner * s, void *buf, size_t buf_size, SANE_Status * status) { DBG (3, "send buf, size = %lu\n", (u_long) buf_size); #if 1 { unsigned int k; const u_char *s = buf; for (k = 0; k < buf_size; k++) { DBG (125, "buf[%d] %02x %c\n", k, s[k], isprint (s[k]) ? s[k] : '.'); } } #endif if (s->hw->connection == SANE_EPSON_SCSI) { return sanei_epson_scsi_write (s->fd, buf, buf_size, status); } else if (s->hw->connection == SANE_EPSON_PIO) { size_t n; if (buf_size == (n = sanei_pio_write (s->fd, buf, buf_size))) *status = SANE_STATUS_GOOD; else *status = SANE_STATUS_INVAL; return n; } else if (s->hw->connection == SANE_EPSON_USB) { size_t n; n = buf_size; *status = sanei_usb_write_bulk (s->fd, buf, &n); w_cmd_count++; DBG (5, "w_cmd_count = %d\n",w_cmd_count); DBG (5, "r_cmd_count = %d\n",r_cmd_count); return n; } return SANE_STATUS_INVAL; /* never reached */ } /* * * */ static ssize_t receive (Epson_Scanner * s, void *buf, ssize_t buf_size, SANE_Status * status) { ssize_t n = 0; if (s->hw->connection == SANE_EPSON_SCSI) { n = sanei_epson_scsi_read (s->fd, buf, buf_size, status); } else if (s->hw->connection == SANE_EPSON_PIO) { if (buf_size == (n = sanei_pio_read (s->fd, buf, (size_t) buf_size))) *status = SANE_STATUS_GOOD; else *status = SANE_STATUS_INVAL; } else if (s->hw->connection == SANE_EPSON_USB) { /* !!! only report an error if we don't read anything */ n = buf_size; /* buf_size gets overwritten */ *status = sanei_usb_read_bulk (s->fd, (SANE_Byte *) buf, (size_t *) & n); r_cmd_count += (n+63)/64; /* add # of packets, rounding up */ DBG (5, "w_cmd_count = %d\n",w_cmd_count); DBG (5, "r_cmd_count = %d\n",r_cmd_count); if (n > 0) *status = SANE_STATUS_GOOD; } DBG (7, "receive buf, expected = %lu, got = %ld\n", (u_long) buf_size, (long) n); #if 1 if (n > 0) { int k; const u_char *s = buf; for (k = 0; k < n; k++) { DBG (127, "buf[%d] %02x %c\n", k, s[k], isprint (s[k]) ? s[k] : '.'); } } #else { int i; ssize_t k; ssize_t hex_start = 0; const u_char *s = buf; char hex_str[NUM_OF_HEX_ELEMENTS * 3 + 1]; char tmp_str[NUM_OF_HEX_ELEMENTS * 3 + 1]; char ascii_str[NUM_OF_HEX_ELEMENTS * 2 + 1]; hex_str[0] = '\0'; ascii_str[0] = '\0'; for (k = 0; k < buf_size; k++) { /* write out the data in lines of 16 bytes */ /* add the next hex value to the hex string */ sprintf (tmp_str, "%s %02x", hex_str, s[k]); strcpy (hex_str, tmp_str); /* add the character to the ascii string */ sprintf (tmp_str, "%s %c", ascii_str, isprint (s[k]) ? s[k] : '.'); strcpy (ascii_str, tmp_str); if ((k % (NUM_OF_HEX_ELEMENTS)) == 0) { if (k != 0) /* don't do this the first time */ { for (i = strlen (hex_str); i < (NUM_OF_HEX_ELEMENTS * 3); i++) { hex_str[i] = ' '; } hex_str[NUM_OF_HEX_ELEMENTS + 1] = '\0'; DBG (125, "recv buf[%05d]: %s %s\n", hex_start, hex_str, ascii_str); hex_start = k; hex_str[0] = '\0'; ascii_str[0] = '\0'; } } } for (i = strlen (hex_str); i < NUM_OF_HEX_ELEMENTS * 3; i++) { hex_str[i] = ' '; } hex_str[NUM_OF_HEX_ELEMENTS + 1] = '\0'; DBG (125, "recv buf[%05d]: %s %s\n", hex_start, hex_str, ascii_str); } #endif return n; } /* * * */ static SANE_Status expect_ack (Epson_Scanner * s) { u_char result[1]; size_t len; SANE_Status status; len = sizeof (result); receive (s, result, len, &status); if (SANE_STATUS_GOOD != status) return status; if (ACK != result[0]) return SANE_STATUS_INVAL; return SANE_STATUS_GOOD; } /* * * */ static SANE_Status set_cmd (Epson_Scanner * s, u_char cmd, int val) { SANE_Status status; u_char params[2]; if (!cmd) return SANE_STATUS_UNSUPPORTED; params[0] = ESC; params[1] = cmd; send (s, params, 2, &status); if (SANE_STATUS_GOOD != (status = expect_ack (s))) return status; params[0] = val; send (s, params, 1, &status); status = expect_ack (s); return status; } /* A little helper function to correct the extended status reply gotten from scanners with known buggy firmware. */ static void fix_up_extended_status_reply (const char *model, u_char * buf) { if (0 == strncmp (model, "ES-9000H", strlen ("ES-9000H")) || 0 == strncmp (model, "GT-30000", strlen ("GT-30000"))) { DBG (1, "Fixing up buggy ADF max scan dimensions.\n"); buf[2] = 0xB0; buf[3] = 0x6D; buf[4] = 0x60; buf[5] = 0x9F; } } static void print_params (const SANE_Parameters params) { DBG (5, "params.format = %d\n", params.format); DBG (5, "params.last_frame = %d\n", params.last_frame); DBG (5, "params.bytes_per_line = %d\n", params.bytes_per_line); DBG (5, "params.pixels_per_line = %d\n", params.pixels_per_line); DBG (5, "params.lines = %d\n", params.lines); DBG (5, "params.depth = %d\n", params.depth); } /* * * */ #define set_focus_position(s,v) set_cmd( s,(s)->hw->cmd->set_focus_position,v) #define set_color_mode(s,v) set_cmd( s,(s)->hw->cmd->set_color_mode,v) #define set_data_format(s,v) set_cmd( s,(s)->hw->cmd->set_data_format, v) #define set_halftoning(s,v) set_cmd( s,(s)->hw->cmd->set_halftoning, v) #define set_gamma(s,v) set_cmd( s,(s)->hw->cmd->set_gamma, v) #define set_color_correction(s,v) set_cmd( s,(s)->hw->cmd->set_color_correction, v) #define set_lcount(s,v) set_cmd( s,(s)->hw->cmd->set_lcount, v) #define set_bright(s,v) set_cmd( s,(s)->hw->cmd->set_bright, v) #define mirror_image(s,v) set_cmd( s,(s)->hw->cmd->mirror_image, v) #define set_speed(s,v) set_cmd( s,(s)->hw->cmd->set_speed, v) #define set_outline_emphasis(s,v) set_cmd( s,(s)->hw->cmd->set_outline_emphasis, v) #define control_auto_area_segmentation(s,v) set_cmd( s,(s)->hw->cmd->control_auto_area_segmentation, v) #define set_film_type(s,v) set_cmd( s,(s)->hw->cmd->set_film_type, v) #define set_exposure_time(s,v) set_cmd( s,(s)->hw->cmd->set_exposure_time, v) #define set_bay(s,v) set_cmd( s,(s)->hw->cmd->set_bay, v) #define set_threshold(s,v) set_cmd( s,(s)->hw->cmd->set_threshold, v) #define control_extension(s,v) set_cmd( s,(s)->hw->cmd->control_an_extension, v) /*#define (s,v) set_cmd( s,(s)->hw->cmd->, v) */ static SANE_Status set_zoom (Epson_Scanner * s, int x_zoom, int y_zoom) { SANE_Status status; u_char cmd[2]; u_char params[2]; if (!s->hw->cmd->set_zoom) return SANE_STATUS_GOOD; cmd[0] = ESC; cmd[1] = s->hw->cmd->set_zoom; send (s, cmd, 2, &status); status = expect_ack (s); if (status != SANE_STATUS_GOOD) return status; params[0] = x_zoom; params[1] = y_zoom; send (s, params, 2, &status); status = expect_ack (s); return status; } static SANE_Status set_resolution (Epson_Scanner * s, int xres, int yres) { SANE_Status status; u_char params[4]; if (!s->hw->cmd->set_resolution) return SANE_STATUS_GOOD; params[0] = ESC; params[1] = s->hw->cmd->set_resolution; send (s, params, 2, &status); status = expect_ack (s); if (status != SANE_STATUS_GOOD) return status; params[0] = xres; params[1] = xres >> 8; params[2] = yres; params[3] = yres >> 8; send (s, params, 4, &status); status = expect_ack (s); return status; } /* * set_scan_area() * * Sends the "set scan area" command to the scanner with the currently selected * scan area. This scan area is already corrected for "color shuffling" if * necessary. */ static SANE_Status set_scan_area (Epson_Scanner * s, int x, int y, int width, int height) { SANE_Status status; u_char params[8]; DBG (1, "set_scan_area: %p %d %d %d %d\n", (void *) s, x, y, width, height); if (!s->hw->cmd->set_scan_area) { DBG (1, "set_scan_area not supported\n"); return SANE_STATUS_GOOD; } /* verify the scan area */ if (x < 0 || y < 0 || width <= 0 || height <= 0) return SANE_STATUS_INVAL; params[0] = ESC; params[1] = s->hw->cmd->set_scan_area; send (s, params, 2, &status); status = expect_ack (s); if (status != SANE_STATUS_GOOD) return status; params[0] = x; params[1] = x >> 8; params[2] = y; params[3] = y >> 8; params[4] = width; params[5] = width >> 8; params[6] = height; params[7] = height >> 8; send (s, params, 8, &status); status = expect_ack (s); return status; } /* * set_color_correction_coefficients() * * Sends the "set color correction coefficients" command with the currently selected * parameters to the scanner. */ static SANE_Status set_color_correction_coefficients (Epson_Scanner * s) { SANE_Status status; u_char cmd = s->hw->cmd->set_color_correction_coefficients; u_char params[2]; const int length = 9; signed char cct[9]; DBG (1, "set_color_correction_coefficients: starting.\n"); if (!cmd) return SANE_STATUS_UNSUPPORTED; params[0] = ESC; params[1] = cmd; send (s, params, 2, &status); if (SANE_STATUS_GOOD != (status = expect_ack (s))) return status; cct[0] = s->val[OPT_CCT_1].w; cct[1] = s->val[OPT_CCT_2].w; cct[2] = s->val[OPT_CCT_3].w; cct[3] = s->val[OPT_CCT_4].w; cct[4] = s->val[OPT_CCT_5].w; cct[5] = s->val[OPT_CCT_6].w; cct[6] = s->val[OPT_CCT_7].w; cct[7] = s->val[OPT_CCT_8].w; cct[8] = s->val[OPT_CCT_9].w; DBG (1, "set_color_correction_coefficients: %d,%d,%d %d,%d,%d %d,%d,%d.\n", cct[0], cct[1], cct[2], cct[3], cct[4], cct[5], cct[6], cct[7], cct[8]); send (s, cct, length, &status); status = expect_ack (s); DBG (1, "set_color_correction_coefficients: ending=%d.\n", status); return status; } /* * * */ static SANE_Status set_gamma_table (Epson_Scanner * s) { SANE_Status status; u_char cmd = s->hw->cmd->set_gamma_table; u_char params[2]; const int length = 257; u_char gamma[257]; int n; int table; /* static const char gamma_cmds[] = { 'M', 'R', 'G', 'B' }; */ static const char gamma_cmds[] = { 'R', 'G', 'B' }; DBG (1, "set_gamma_table: starting.\n"); if (!cmd) return SANE_STATUS_UNSUPPORTED; params[0] = ESC; params[1] = cmd; /* * Print the gamma tables before sending them to the scanner. */ if (DBG_LEVEL > 0) { int c, i, j; DBG (1, "set_gamma_table()\n"); for (c = 0; c < 3; c++) { for (i = 0; i < 256; i += 16) { char gammaValues[16 * 3 + 1], newValue[4]; gammaValues[0] = '\0'; for (j = 0; j < 16; j++) { sprintf (newValue, " %02x", s->gamma_table[c][i + j]); strcat (gammaValues, newValue); } DBG (10, "Gamma Table[%d][%d] %s\n", c, i, gammaValues); } } } /* * TODO: &status in send makes no sense like that. */ /* * When handling inverted images, we must also invert the user * supplied gamma function. This is *not* just 255-gamma - * this gives a negative image. */ for (table = 0; table < 3; table++) { gamma[0] = gamma_cmds[table]; if (s->invert_image) { for (n = 0; n < 256; ++n) { gamma[n + 1] = 255 - s->gamma_table[table][255 - n]; } } else { for (n = 0; n < 256; ++n) { gamma[n + 1] = s->gamma_table[table][n]; } } send (s, params, 2, &status); if (SANE_STATUS_GOOD != (status = expect_ack (s))) return status; send (s, gamma, length, &status); if (SANE_STATUS_GOOD != (status = expect_ack (s))) return status; } DBG (1, "set_gamma_table: complete = %d.\n", status); return status; } void get_size (char c1, char c2, double *w, double *h) { int ind; unsigned char flag; double wsizetbl[] = { 11.60, /* A3V */ 11.00, /* WLT */ 10.12, /* B4V */ 8.50, /* LGV */ 8.27, /* A4V */ 11.69, /* A4H */ 8.50, /* LTV */ 11.00, /* LTH */ 7.17, /* B5V */ 10.12, /* B5H */ 5.83, /* A5V */ 8.27, /* A5H */ 7.25, /* EXV */ 10.50, /* EXH */ 11.69, /* unknown */ 11.69, /* unknown */ 11.69, /* unknown */ }; double hsizetbl[] = { 16.54, /* A3V */ 17.00, /* WLT */ 14.33, /* B4V */ 14.00, /* LGV */ 11.69, /* A4V */ 8.27, /* A4H */ 11.00, /* LTV */ 8.50, /* LTH */ 10.12, /* B5V */ 7.17, /* B5H */ 8.27, /* A5V */ 5.83, /* A5H */ 10.50, /* EXV */ 7.25, /* EXH */ 17.00, /* unknown */ 17.00, /* unknown */ 17.00, /* unknown */ }; flag = c1; for (ind = 0; ind < 8; ind++) { if (flag & 0x80) goto DetectSize; flag = flag << 1; } flag = c2; for (; ind < 16; ind++) { if (flag & 0x80) goto DetectSize; flag = flag << 1; } DetectSize: *w = wsizetbl[ind]; *h = hsizetbl[ind]; DBG (10, "detected width: %f\n", *w); DBG (10, "detected height: %f\n", *h); } /* * check_ext_status() * * Requests the extended status flag from the scanner. The "warming up" condition * is reported as a warning (only visible if debug level is set to 10 or greater) - * every other condition is reported as an error. * * This function only gets called when we are dealing with a scanner that supports the * "warming up" code, so it's not a problem for B3 level scanners, that don't handle * request extended status commands. */ static SANE_Status check_ext_status (Epson_Scanner * s, int *max_x, int *max_y) { SANE_Status status; u_char cmd = s->hw->cmd->request_extended_status; u_char params[2]; u_char *buf; EpsonHdr head; *max_x = 0; *max_y = 0; if (cmd == 0) return SANE_STATUS_UNSUPPORTED; params[0] = ESC; params[1] = cmd; head = (EpsonHdr) command (s, params, 2, &status); if (NULL == head) { DBG (1, "Extended status flag request failed\n"); return status; } buf = &head->buf[0]; if (buf[0] & EXT_STATUS_WU) { DBG (10, "option: warming up\n"); status = SANE_STATUS_DEVICE_BUSY; } if (buf[0] & EXT_STATUS_FER) { DBG (1, "option: fatal error\n"); status = SANE_STATUS_INVAL; } if (s->hw->ADF && s->hw->use_extension && s->hw->cmd->feed) { fix_up_extended_status_reply (s->hw->sane.model, buf); *max_x = buf[3] << 8 | buf[2]; *max_y = buf[5] << 8 | buf[4]; if (0 == strcmp ("ES-9000H", s->hw->sane.model) || 0 == strcmp ("GT-30000", s->hw->sane.model)) { /* set size of current sheet, but don't clobber zoom settings (which should always be smaller than the detected sheet size) */ double w, h; get_size (buf[16], buf[17], &w, &h); w = SANE_FIX (w * MM_PER_INCH); h = SANE_FIX (h * MM_PER_INCH); if (w < s->val[OPT_BR_X].w) s->val[OPT_BR_X].w = w; if (h < s->val[OPT_BR_Y].w) s->val[OPT_BR_Y].w = h; } } if (buf[1] & EXT_STATUS_ERR) { DBG (1, "ADF: other error\n"); status = SANE_STATUS_INVAL; } if (buf[1] & EXT_STATUS_PE) { DBG (1, "ADF: no paper\n"); status = SANE_STATUS_NO_DOCS; return status; } if (buf[1] & EXT_STATUS_PJ) { DBG (1, "ADF: paper jam\n"); status = SANE_STATUS_JAMMED; } if (buf[1] & EXT_STATUS_OPN) { DBG (1, "ADF: cover open\n"); status = SANE_STATUS_COVER_OPEN; } if (buf[6] & EXT_STATUS_ERR) { DBG (1, "TPU: other error\n"); status = SANE_STATUS_INVAL; } /* return the max. scan area for the ADF */ if (buf[6] & EXT_STATUS_IST) { *max_x = buf[8] << 8 | buf[7]; *max_y = buf[10] << 8 | buf[9]; } /* return the max. scan area for the flatbed */ if (s->hw->devtype == 3 && s->hw->use_extension == 0) { double w, h; get_size (buf[18], buf[19], &w, &h); *max_x = (int) (w * s->hw->dpi_range.max); *max_y = (int) (h * s->hw->dpi_range.max); } free (head); return status; } /* * reset() * * Send the "initialize scanner" command to the device and reset it. * */ static SANE_Status reset (Epson_Scanner * s) { SANE_Status status; u_char param[2]; SANE_Bool needToClose = SANE_FALSE; DBG (5, "reset()\n"); if (!s->hw->cmd->initialize_scanner) return SANE_STATUS_GOOD; param[0] = ESC; param[1] = s->hw->cmd->initialize_scanner; if (s->fd == -1) { needToClose = SANE_TRUE; DBG (5, "reset calling open_scanner\n"); if ((status = open_scanner (s)) != SANE_STATUS_GOOD) return status; } send (s, param, 2, &status); status = expect_ack (s); if (needToClose) { close_scanner (s); } return status; } /* * close_scanner() * * Close the open scanner. Depending on the connection method, a different * close function is called. */ static void close_scanner (Epson_Scanner * s) { DBG (5, "close_scanner(fd = %d)\n", s->fd); if (s->fd == -1) return; if (r_cmd_count % 2) { /* send a request_status. This toggles w_cmd_count and r_cmd_count */ u_char param[3]; u_char result[5]; SANE_Status status; param[0] = ESC; param[1] = s->hw->cmd->request_status; param[2]='\0'; send(s,param,2,&status); receive(s,result,4,&status); } DBG (5, "w_cmd_count = %d\n",w_cmd_count); DBG (5, "r_cmd_count = %d\n",r_cmd_count); if (w_cmd_count % 2) { int junk1,junk2; /* check extended status. This toggles w_cmd_count%2 only */ check_ext_status (s,&junk1,&junk2); } DBG (5, "w_cmd_count = %d\n",w_cmd_count); DBG (5, "r_cmd_count = %d\n",r_cmd_count); if (s->hw->connection == SANE_EPSON_SCSI) { sanei_scsi_close (s->fd); } else if (s->hw->connection == SANE_EPSON_PIO) { sanei_pio_close (s->fd); } else if (s->hw->connection == SANE_EPSON_USB) { sanei_usb_close (s->fd); } s->fd = -1; return; } /* * open_scanner() * * Open the scanner device. Depending on the connection method, * different open functions are called. */ static SANE_Status open_scanner (Epson_Scanner * s) { SANE_Status status = 0; DBG (5, "open_scanner()\n"); if (s->fd != -1) { DBG (5, "scanner is already open: fd = %d\n", s->fd); return SANE_STATUS_GOOD; /* no need to open the scanner */ } /* don't do this for OS2: */ #ifndef HAVE_OS2_H #if 0 /* test the device name */ if ((s->hw->connection != SANE_EPSON_PIO) && (access (s->hw->sane.name, R_OK | W_OK) != 0)) { DBG (1, "sane_start: access(%s, R_OK | W_OK) failed\n", s->hw->sane.name); return SANE_STATUS_ACCESS_DENIED; } #endif #endif if (s->hw->connection == SANE_EPSON_SCSI) { status = sanei_scsi_open (s->hw->sane.name, &s->fd, sanei_epson_scsi_sense_handler, NULL); if (SANE_STATUS_GOOD != status) { DBG (1, "sane_start: %s open failed: %s\n", s->hw->sane.name, sane_strstatus (status)); return status; } } else if (s->hw->connection == SANE_EPSON_PIO) { status = sanei_pio_open (s->hw->sane.name, &s->fd); if (SANE_STATUS_GOOD != status) { DBG (1, "sane_start: %s open failed: %s\n", s->hw->sane.name, sane_strstatus (status)); return status; } } else if (s->hw->connection == SANE_EPSON_USB) { status = sanei_usb_open (s->hw->sane.name, &s->fd); return status; } return SANE_STATUS_GOOD; } /* * feed ( ) */ static SANE_Status feed (Epson_Scanner * s) { SANE_Status status; u_char params[2]; u_char cmd = s->hw->cmd->feed; DBG (5, "feed()\n"); if (!cmd) { DBG (5, "feed() is not supported\n"); return SANE_STATUS_UNSUPPORTED; } params[0] = cmd; send (s, params, 1, &status); if (SANE_STATUS_GOOD != (status = expect_ack (s))) { close_scanner (s); return status; } return status; } /* * eject() * * Eject the current page from the ADF. The scanner is opened prior to * sending the command and closed afterwards. * */ static SANE_Status eject (Epson_Scanner * s) { SANE_Status status; u_char params[2]; u_char cmd = s->hw->cmd->eject; SANE_Bool needToClose = SANE_FALSE; DBG (5, "eject()\n"); if (!cmd) return SANE_STATUS_UNSUPPORTED; if (s->fd == -1) { needToClose = SANE_TRUE; if (SANE_STATUS_GOOD != (status = open_scanner (s))) return status; } params[0] = cmd; send (s, params, 1, &status); if (SANE_STATUS_GOOD != (status = expect_ack (s))) { close_scanner (s); return status; } if (needToClose) close_scanner (s); return status; } /* * * */ static int num_devices = 0; /* number of EPSON scanners attached to backend */ static Epson_Device *first_dev = NULL; /* first EPSON scanner in list */ static Epson_Scanner *first_handle = NULL; static EpsonHdrUnion command (Epson_Scanner * s, u_char * cmd, size_t cmd_size, SANE_Status * status) { EpsonHdrUnion hdrunion, hdrunion_bak; EpsonHdr head; u_char *buf; int count; if (NULL == (hdrunion = walloc (EpsonHdrUnionRec))) { DBG (1, "out of memory (line %d)\n", __LINE__); *status = SANE_STATUS_NO_MEM; return (EpsonHdrUnion) 0; } head = &(hdrunion->hdr); send (s, cmd, cmd_size, status); if (SANE_STATUS_GOOD != *status) { /* this is necessary for the GT-8000. I don't know why, but it seems to fix the problem. It should not have any ill effects on other scanners. */ *status = SANE_STATUS_GOOD; send (s, cmd, cmd_size, status); if (SANE_STATUS_GOOD != *status) return (EpsonHdrUnion) 0; } buf = (u_char *) head; if (s->hw->connection == SANE_EPSON_SCSI) { receive (s, buf, 4, status); buf += 4; } else if (s->hw->connection == SANE_EPSON_USB) { int bytes_read; bytes_read = receive (s, buf, 4, status); buf += bytes_read; } else { receive (s, buf, 1, status); buf += 1; } if (SANE_STATUS_GOOD != *status) return (EpsonHdrUnion) 0; DBG (4, "code %02x\n", (int) head->code); switch (head->code) { case NAK: /* fall through */ /* !!! is this really sufficient to report an error ? */ case ACK: break; /* no need to read any more data after ACK or NAK */ case STX: if (s->hw->connection == SANE_EPSON_SCSI) { /* nope */ } else if (s->hw->connection == SANE_EPSON_USB) { /* we've already read the complete data */ } else { receive (s, buf, 3, status); /* buf += 3; */ } if (SANE_STATUS_GOOD != *status) return (EpsonHdrUnion) 0; DBG (4, "status %02x\n", (int) head->status); count = head->count2 * 255 + head->count1; DBG (4, "count %d\n", count); hdrunion_bak = hdrunion; if (NULL == (hdrunion = realloc (hdrunion, sizeof (EpsonHdrUnionRec) + count))) { free(hdrunion_bak); DBG (1, "out of memory (line %d)\n", __LINE__); *status = SANE_STATUS_NO_MEM; return (EpsonHdrUnion) 0; } head = &(hdrunion->hdr); buf = head->buf; receive (s, buf, count, status); if (SANE_STATUS_GOOD != *status) return (EpsonHdrUnion) 0; break; default: if (0 == head->code) DBG (1, "Incompatible printer port (probably bi/directional)\n"); else if (cmd[cmd_size - 1] == head->code) DBG (1, "Incompatible printer port (probably not bi/directional)\n"); DBG (2, "Illegal response of scanner for command: %02x\n", head->code); break; } return hdrunion; } /* * static SANE_Status attach() * * Attach one device with name *dev_name to the backend. */ static SANE_Status attach (const char *dev_name, Epson_Device * *devp, int type) { SANE_Status status; Epson_Scanner *s = walloca (Epson_Scanner); char *str; struct Epson_Device *dev; SANE_String_Const *source_list_add = source_list; int port; DBG (1, "%s\n", SANE_EPSON_VERSION); DBG (5, "attach(%s, %d)\n", dev_name, type); for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->sane.name, dev_name) == 0) { if (devp) { *devp = dev; } return SANE_STATUS_GOOD; } } dev = malloc (sizeof (*dev)); if (!dev) { DBG (1, "out of memory (line %d)\n", __LINE__); return SANE_STATUS_NO_MEM; } /* check for PIO devices */ /* can we convert the device name to an integer? This is only possible with PIO devices */ port = atoi (dev_name); if (port != 0) { type = SANE_EPSON_PIO; } if (strncmp (dev_name, SANE_EPSON_CONFIG_PIO, strlen (SANE_EPSON_CONFIG_PIO)) == 0) { /* we have a match for the PIO string and adjust the device name */ dev_name += strlen (SANE_EPSON_CONFIG_PIO); dev_name = sanei_config_skip_whitespace (dev_name); type = SANE_EPSON_PIO; } /* * set dummy values. */ s->hw = dev; s->hw->sane.name = NULL; s->hw->sane.type = "flatbed scanner"; s->hw->sane.vendor = "Epson"; s->hw->sane.model = NULL; s->hw->optical_res = 0; /* just to have it initialized */ s->hw->color_shuffle = SANE_FALSE; s->hw->extension = SANE_FALSE; s->hw->use_extension = SANE_FALSE; s->hw->need_color_reorder = SANE_FALSE; s->hw->need_double_vertical = SANE_FALSE; s->hw->cmd = &epson_cmd[EPSON_LEVEL_DEFAULT]; /* default function level */ s->hw->connection = type; DBG (3, "attach: opening %s\n", dev_name); s->hw->last_res = 0; s->hw->last_res_preview = 0; /* set resolution to safe values */ /* * decide if interface is USB, SCSI or parallel. */ /* * if interface is SCSI do an inquiry. */ if (s->hw->connection == SANE_EPSON_SCSI) { u_char buf[INQUIRY_BUF_SIZE + 1]; size_t buf_size = INQUIRY_BUF_SIZE; status = sanei_scsi_open (dev_name, &s->fd, sanei_epson_scsi_sense_handler, NULL); if (SANE_STATUS_GOOD != status) { DBG (1, "attach: open failed: %s\n", sane_strstatus (status)); return status; } DBG (3, "attach: sending INQUIRY\n"); status = sanei_epson_scsi_inquiry (s->fd, 0, buf, &buf_size); if (SANE_STATUS_GOOD != status) { DBG (1, "attach: inquiry failed: %s\n", sane_strstatus (status)); close_scanner (s); return status; } buf[INQUIRY_BUF_SIZE] = 0; DBG (1, ">%s<\n", buf + 8); /* * For USB and PIO scanners this will be done later, once * we have communication established with the device. */ if (buf[0] != TYPE_PROCESSOR || 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); return SANE_STATUS_INVAL; } } /* use the SANEI functions to handle a PIO device */ else if (s->hw->connection == SANE_EPSON_PIO) { if (SANE_STATUS_GOOD != (status = sanei_pio_open (dev_name, &s->fd))) { DBG (1, "Cannot open %s as a parallel-port device: %s\n", dev_name, sane_strstatus (status)); return status; } } /* use the SANEI functions to handle a USB device */ else if (s->hw->connection == SANE_EPSON_USB) { SANE_Word vendor; SANE_Word product; SANE_Bool isLibUSB; isLibUSB = (strncmp (dev_name, "libusb:", strlen ("libusb:")) == 0); if ((!isLibUSB) && (strlen (dev_name) == 0)) { int i; int numIds; numIds = sanei_epson_getNumberOfUSBProductIds (); for (i = 0; i < numIds; i++) { product = sanei_epson_usb_product_ids[i]; vendor = 0x4b8; status = sanei_usb_find_devices (vendor, product, attach_one_usb); } return SANE_STATUS_INVAL; /* return - the attach_one_usb() will take care of this */ } status = sanei_usb_open (dev_name, &s->fd); if (SANE_STATUS_GOOD != status) { return status; } /* if the sanei_usb_get_vendor_product call is not supported, then we just ignore this and rely on the user to config the correct device. */ if (sanei_usb_get_vendor_product (s->fd, &vendor, &product) == SANE_STATUS_GOOD) { int i; /* loop variable */ int numIds; SANE_Bool is_valid; /* check the vendor ID to see if we are dealing with an EPSON device */ if (vendor != SANE_EPSON_VENDOR_ID) { /* this is not a supported vendor ID */ DBG (1, "The device at %s is not manufactured by EPSON (vendor id=0x%x)\n", dev_name, vendor); sanei_usb_close (s->fd); s->fd = -1; return SANE_STATUS_INVAL; } numIds = sanei_epson_getNumberOfUSBProductIds (); is_valid = SANE_FALSE; i = 0; /* check all known product IDs to verify that we know about the device */ while (i != numIds && !is_valid) { if (product == sanei_epson_usb_product_ids[i]) is_valid = SANE_TRUE; i++; } if (is_valid == SANE_FALSE) { DBG (1, "The device at %s is not a supported EPSON scanner (product id=0x%x)\n", dev_name, product); sanei_usb_close (s->fd); s->fd = -1; return SANE_STATUS_INVAL; } DBG (1, "Found valid EPSON scanner: 0x%x/0x%x (vendorID/productID)\n", vendor, product); } else { DBG (1, "Cannot use IOCTL interface to verify that device is a scanner - will continue\n"); } } /* * Initialize the scanner (ESC @). */ reset (s); /* * Identification Request (ESC I). */ if (s->hw->cmd->request_identity != 0) { status = get_identity_information (s); if (status != SANE_STATUS_GOOD) return status; } /* request identity */ /* * Check for "Request Identity 2" command. If this command is available * get the information from the scanner and store it in dev */ if (s->hw->cmd->request_identity2 != 0) { status = get_identity2_information (s); if (status != SANE_STATUS_GOOD) return status; } /* request identity 2 */ /* * Check for the max. supported color depth and assign * the values to the bitDepthList. */ bitDepthList = malloc (sizeof (SANE_Word) * 4); if (bitDepthList == NULL) { DBG (1, "out of memory (line %d)\n", __LINE__); return SANE_STATUS_NO_MEM; } bitDepthList[0] = 1; /* we start with one element in the list */ bitDepthList[1] = 8; /* 8bit is the default */ if (set_data_format (s, 16) == SANE_STATUS_GOOD) { s->hw->maxDepth = 16; bitDepthList[0]++; bitDepthList[bitDepthList[0]] = 16; } else if (set_data_format (s, 14) == SANE_STATUS_GOOD) { s->hw->maxDepth = 14; bitDepthList[0]++; bitDepthList[bitDepthList[0]] = 14; } else if (set_data_format (s, 12) == SANE_STATUS_GOOD) { s->hw->maxDepth = 12; bitDepthList[0]++; bitDepthList[bitDepthList[0]] = 12; } else { s->hw->maxDepth = 8; /* the default depth is already in the list */ } DBG (1, "Max. supported color depth = %d\n", s->hw->maxDepth); /* * Check for "request focus position" command. If this command is * supported, then the scanner does also support the "set focus * position" command. */ if (request_focus_position (s, &s->currentFocusPosition) == SANE_STATUS_GOOD) { DBG (1, "Enabling 'Set Focus' support\n"); s->hw->focusSupport = SANE_TRUE; s->opt[OPT_FOCUS].cap &= ~SANE_CAP_INACTIVE; /* reflect the current focus position in the GUI */ if (s->currentFocusPosition < 0x4C) { /* focus on glass */ s->val[OPT_FOCUS].w = 0; } else { /* focus 2.5mm above glass */ s->val[OPT_FOCUS].w = 1; } } else { DBG (1, "Disabling 'Set Focus' support\n"); s->hw->focusSupport = SANE_FALSE; s->opt[OPT_FOCUS].cap |= SANE_CAP_INACTIVE; s->val[OPT_FOCUS].w = 0; /* on glass - just in case */ } /* * Set defaults for no extension. */ dev->x_range = &dev->fbf_x_range; dev->y_range = &dev->fbf_y_range; /* * Correct for a firmware bug in some Perfection 1650 scanners: * Firmware version 1.08 reports only half the vertical scan area, we have * to double the number. To find out if we have to do this, we just compare * is the vertical range is smaller than the horizontal range. */ if ((dev->x_range->max - dev->x_range->min) > (dev->y_range->max - dev->y_range->min)) { dev->y_range->max += (dev->y_range->max - dev->y_range->min); dev->need_double_vertical = SANE_TRUE; dev->need_color_reorder = SANE_TRUE; } /* * Extended status flag request (ESC f). * this also requests the scanner device name from the scanner */ /* * because we are also using the device name from this command, * we have to run this block even if the scanner does not report * an extension. The extensions are only reported if the ADF or * the TPU are actually detected. */ 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))) { DBG (1, "Extended status flag request failed\n"); dev->sane.model = strdup ("Unknown model"); *source_list_add++ = FBF_STR; } else { buf = &head->buf[0]; /* * Add the flatbed option to the source list */ *source_list_add++ = FBF_STR; s->hw->devtype = buf[11] >> 6; /* * Get the device name and copy it to dev->sane.model. * The device name starts at buf[0x1A] and is up to 16 bytes long * We are overwriting whatever was set previously! */ { char device_name[DEVICE_NAME_LEN + 1]; char *end_ptr; int len; /* make sure that the end of string is marked */ device_name[DEVICE_NAME_LEN] = '\0'; /* copy the string to an area where we can work with it */ memcpy (device_name, buf + 0x1A, DEVICE_NAME_LEN); end_ptr = strchr (device_name, ' '); if (end_ptr != NULL) { *end_ptr = '\0'; } len = strlen (device_name); str = malloc (len + 1); str[len] = '\0'; dev->sane.model = (char *) memcpy (str, device_name, len); } /* * ADF */ if (dev->extension && (buf[1] & EXT_STATUS_IST)) { DBG (1, "ADF detected\n"); /* the GT-30000 does not report the ADF scan area */ if ((strcmp (dev->sane.model, "GT-30000") == 0) || (strcmp (dev->sane.model, "ES-9000H") == 0)) { fix_up_extended_status_reply ((const char *) buf + 26, buf); dev->duplexSupport = (buf[0] & 0x10) != 0; if (dev->duplexSupport) { DBG (1, "Found DUPLEX ADF\n"); } } if (buf[1] & EXT_STATUS_EN) { DBG (1, "ADF is enabled\n"); dev->x_range = &dev->adf_x_range; dev->y_range = &dev->adf_y_range; } dev->adf_x_range.min = 0; dev->adf_x_range.max = SANE_FIX ((buf[3] << 8 | buf[2]) * 25.4 / dev->dpi_range.max); dev->adf_x_range.quant = 0; dev->adf_max_x = buf[3] << 8 | buf[2]; dev->adf_y_range.min = 0; dev->adf_y_range.max = SANE_FIX ((buf[5] << 8 | buf[4]) * 25.4 / dev->dpi_range.max); dev->adf_y_range.quant = 0; dev->adf_max_y = buf[5] << 8 | buf[4]; DBG (5, "adf tlx %f tly %f brx %f bry %f [mm]\n", SANE_UNFIX (dev->adf_x_range.min), SANE_UNFIX (dev->adf_y_range.min), SANE_UNFIX (dev->adf_x_range.max), SANE_UNFIX (dev->adf_y_range.max)); *source_list_add++ = ADF_STR; dev->ADF = SANE_TRUE; } /* * TPU */ if (dev->extension && (buf[6] & EXT_STATUS_IST)) { DBG (1, "TPU detected\n"); if (buf[6] & EXT_STATUS_EN) { DBG (1, "TPU is enabled\n"); dev->x_range = &dev->tpu_x_range; dev->y_range = &dev->tpu_y_range; } dev->tpu_x_range.min = 0; dev->tpu_x_range.max = SANE_FIX ((buf[8] << 8 | buf[7]) * 25.4 / dev->dpi_range.max); dev->tpu_x_range.quant = 0; dev->tpu_y_range.min = 0; dev->tpu_y_range.max = 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 physically supports 4800 dpi. * This is simulated here... * Further 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), SANE_UNFIX (dev->tpu_x_range.max), SANE_UNFIX (dev->tpu_y_range.max)); *source_list_add++ = TPU_STR; dev->TPU = SANE_TRUE; } /* * Get the device name and copy it to dev->sane.model. * The device name starts at buf[0x1A] and is up to 16 bytes long * We are overwriting whatever was set previously! */ { char device_name[DEVICE_NAME_LEN + 1]; char *end_ptr; int len; /* make sure that the end of string is marked */ device_name[DEVICE_NAME_LEN] = '\0'; /* copy the string to an area where we can work with it */ memcpy (device_name, buf + 0x1A, DEVICE_NAME_LEN); end_ptr = strchr (device_name, ' '); if (end_ptr != NULL) { *end_ptr = '\0'; } len = strlen (device_name); str = malloc (len + 1); str[len] = '\0'; /* finally copy the device name to the structure */ dev->sane.model = (char *) memcpy (str, device_name, len); } } } else /* command is not known */ { dev->sane.model = strdup ("EPSON Scanner"); } *source_list_add = NULL; /* add end marker to source list */ DBG (1, "scanner model: %s\n", dev->sane.model); /* establish defaults */ s->hw->need_reset_on_source_change = SANE_FALSE; if (strcmp ("ES-9000H", dev->sane.model) == 0 || strcmp ("GT-30000", dev->sane.model) == 0) { s->hw->cmd->set_focus_position = 0; s->hw->cmd->feed = 0x19; } else if (strcmp ("GT-8200", dev->sane.model) == 0 || strcmp ("Perfection1650", dev->sane.model) == 0 || strcmp ("Perfection1640", dev->sane.model) == 0 || strcmp ("GT-8700", dev->sane.model) == 0) { s->hw->cmd->feed = 0; s->hw->cmd->set_focus_position = 0; s->hw->need_reset_on_source_change = SANE_TRUE; } /* * Set values for quick format "max" entry. */ qf_params[XtNumber (qf_params) - 1].tl_x = dev->x_range->min; qf_params[XtNumber (qf_params) - 1].tl_y = dev->y_range->min; qf_params[XtNumber (qf_params) - 1].br_x = dev->x_range->max; qf_params[XtNumber (qf_params) - 1].br_y = dev->y_range->max; /* * Now we can finally set the device name: */ str = malloc (strlen (dev_name) + 1); dev->sane.name = strcpy (str, dev_name); close_scanner (s); /* * we are done with this one, prepare for the next scanner: */ ++num_devices; dev->next = first_dev; first_dev = dev; if (devp) { *devp = dev; } return SANE_STATUS_GOOD; } /* * attach_one() * * Part of the SANE API: Attaches the scanner with the device name in *dev. */ static SANE_Status attach_one (const char *dev) { DBG (5, "attach_one(%s)\n", dev); return attach (dev, 0, SANE_EPSON_SCSI); } SANE_Status attach_one_usb (SANE_String_Const devname) { int len = strlen (devname); char *attach_string; DBG (5, "attach_one_usb(%s)\n", devname); attach_string = alloca (len + 5); if (attach_string == NULL) return SANE_STATUS_NO_MEM; return attach (devname, 0, SANE_EPSON_USB); } /* * sane_init() * * */ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { size_t len; FILE *fp; (void) authorize; /* get rid of compiler warning */ /* sanei_authorization(devicename, STRINGIFY(BACKEND_NAME), auth_callback); */ DBG_INIT (); #if defined PACKAGE && defined VERSION DBG (2, "sane_init: " PACKAGE " " VERSION "\n"); #endif if (version_code != NULL) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, SANE_EPSON_BUILD); sanei_usb_init (); /* default to /dev/scanner instead of insisting on config file */ if ((fp = sanei_config_open (EPSON_CONFIG_FILE))) { char line[PATH_MAX]; while (sanei_config_read (line, sizeof (line), fp)) { int vendor, product; DBG (4, "sane_init, >%s<\n", line); if (line[0] == '#') /* ignore line comments */ continue; len = strlen (line); if (!len) continue; /* ignore empty lines */ if (sscanf (line, "usb %i %i", &vendor, &product) == 2) { int numIds; /* add the vendor and product IDs to the list of known devices before we call the attach function */ numIds = sanei_epson_getNumberOfUSBProductIds (); if (vendor != 0x4b8) continue; /* this is not an EPSON device */ sanei_epson_usb_product_ids[numIds - 1] = product; sanei_usb_attach_matching_devices (line, attach_one_usb); } else if (strncmp (line, "usb", 3) == 0) { const char *dev_name; /* remove the "usb" sub string */ dev_name = sanei_config_skip_whitespace (line + 3); attach_one_usb (dev_name); } else { sanei_config_attach_matching_devices (line, attach_one); } } fclose (fp); } /* read the option section and assign the connection type to the scanner structure - which we don't have at this time. So I have to come up with something :-) */ return SANE_STATUS_GOOD; } /* * void sane_exit(void) * * Clean up the list of attached scanners. */ void sane_exit (void) { Epson_Device *dev, *next; for (dev = first_dev; dev; dev = next) { next = dev->next; free ((void *) dev->sane.name); free ((void *) dev->sane.model); free (dev); } free (devlist); } /* * * */ SANE_Status sane_get_devices (const SANE_Device * **device_list, SANE_Bool local_only) { Epson_Device *dev; int i; DBG (5, "sane_get_devices()\n"); (void) local_only; /* just to get rid of the compiler warning */ if (devlist) { free (devlist); } devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); if (!devlist) { DBG (1, "out of memory (line %d)\n", __LINE__); return SANE_STATUS_NO_MEM; } i = 0; for (dev = first_dev; i < num_devices; dev = dev->next) { devlist[i++] = &dev->sane; } devlist[i++] = 0; *device_list = devlist; return SANE_STATUS_GOOD; } /* * * */ static SANE_Status init_options (Epson_Scanner * s) { int i; SANE_Bool dummy; DBG (5, "init_options()\n"); for (i = 0; i < NUM_OPTIONS; ++i) { s->opt[i].size = sizeof (SANE_Word); s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* "Scan Mode" group: */ s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode"); s->opt[OPT_MODE_GROUP].desc = ""; s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MODE_GROUP].cap = 0; /* scan mode */ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; 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 = max_string_size (mode_list); s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_MODE].constraint.string_list = mode_list; s->val[OPT_MODE].w = 0; /* Binary */ /* bit depth */ s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT; s->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_NONE; s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_BIT_DEPTH].constraint.word_list = bitDepthList; s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; s->val[OPT_BIT_DEPTH].w = bitDepthList[1]; /* the first "real" element is the default */ if (bitDepthList[0] == 1) /* only one element in the list -> hide the option */ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; /* halftone */ s->opt[OPT_HALFTONE].name = SANE_NAME_HALFTONE; s->opt[OPT_HALFTONE].title = SANE_TITLE_HALFTONE; s->opt[OPT_HALFTONE].desc = SANE_I18N ("Selects the halftone."); s->opt[OPT_HALFTONE].type = SANE_TYPE_STRING; s->opt[OPT_HALFTONE].size = max_string_size (halftone_list_7); s->opt[OPT_HALFTONE].constraint_type = SANE_CONSTRAINT_STRING_LIST; if (s->hw->level >= 7) s->opt[OPT_HALFTONE].constraint.string_list = halftone_list_7; else if (s->hw->level >= 4) s->opt[OPT_HALFTONE].constraint.string_list = halftone_list_4; else s->opt[OPT_HALFTONE].constraint.string_list = halftone_list; s->val[OPT_HALFTONE].w = 1; /* Halftone A */ if (!s->hw->cmd->set_halftoning) { s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE; } /* dropout */ s->opt[OPT_DROPOUT].name = "dropout"; s->opt[OPT_DROPOUT].title = SANE_I18N ("Dropout"); s->opt[OPT_DROPOUT].desc = SANE_I18N ("Selects the dropout."); s->opt[OPT_DROPOUT].type = SANE_TYPE_STRING; s->opt[OPT_DROPOUT].size = max_string_size (dropout_list); s->opt[OPT_DROPOUT].cap |= SANE_CAP_ADVANCED; s->opt[OPT_DROPOUT].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_DROPOUT].constraint.string_list = dropout_list; s->val[OPT_DROPOUT].w = 0; /* None */ /* brightness */ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].desc = SANE_I18N ("Selects the brightness."); s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT; s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE; s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BRIGHTNESS].constraint.range = &s->hw->cmd->bright_range; s->val[OPT_BRIGHTNESS].w = 0; /* Normal */ if (!s->hw->cmd->set_bright) { s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; } /* sharpness */ s->opt[OPT_SHARPNESS].name = "sharpness"; s->opt[OPT_SHARPNESS].title = SANE_I18N ("Sharpness"); s->opt[OPT_SHARPNESS].desc = ""; s->opt[OPT_SHARPNESS].type = SANE_TYPE_INT; s->opt[OPT_SHARPNESS].unit = SANE_UNIT_NONE; s->opt[OPT_SHARPNESS].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_SHARPNESS].constraint.range = &outline_emphasis_range; s->val[OPT_SHARPNESS].w = 0; /* Normal */ if (!s->hw->cmd->set_outline_emphasis) { s->opt[OPT_SHARPNESS].cap |= SANE_CAP_INACTIVE; } /* gamma */ s->opt[OPT_GAMMA_CORRECTION].name = SANE_NAME_GAMMA_CORRECTION; s->opt[OPT_GAMMA_CORRECTION].title = SANE_TITLE_GAMMA_CORRECTION; s->opt[OPT_GAMMA_CORRECTION].desc = SANE_DESC_GAMMA_CORRECTION; s->opt[OPT_GAMMA_CORRECTION].type = SANE_TYPE_STRING; s->opt[OPT_GAMMA_CORRECTION].constraint_type = SANE_CONSTRAINT_STRING_LIST; /* * special handling for D1 function level - at this time I'm not * testing for D1, I'm just assuming that all D level scanners will * behave the same way. This has to be confirmed with the next D-level * scanner */ if (s->hw->cmd->level[0] == 'D') { s->opt[OPT_GAMMA_CORRECTION].size = max_string_size (gamma_list_d); s->opt[OPT_GAMMA_CORRECTION].constraint.string_list = gamma_list_d; s->val[OPT_GAMMA_CORRECTION].w = 1; /* Default */ gamma_userdefined = gamma_userdefined_d; gamma_params = gamma_params_d; } else { s->opt[OPT_GAMMA_CORRECTION].size = max_string_size (gamma_list_ab); s->opt[OPT_GAMMA_CORRECTION].constraint.string_list = gamma_list_ab; s->val[OPT_GAMMA_CORRECTION].w = 0; /* Default */ gamma_userdefined = gamma_userdefined_ab; gamma_params = gamma_params_ab; } if (!s->hw->cmd->set_gamma) { s->opt[OPT_GAMMA_CORRECTION].cap |= SANE_CAP_INACTIVE; } /* gamma vector */ /* s->opt[ OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR; s->opt[ OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR; s->opt[ OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR; s->opt[ OPT_GAMMA_VECTOR].type = SANE_TYPE_INT; s->opt[ OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE; s->opt[ OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word); s->opt[ OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[ OPT_GAMMA_VECTOR].constraint.range = &u8_range; s->val[ OPT_GAMMA_VECTOR].wa = &s->gamma_table [ 0] [ 0]; */ /* red gamma vector */ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[0][0]; /* green gamma vector */ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[1][0]; /* red gamma vector */ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[2][0]; if (s->hw->cmd->set_gamma_table && gamma_userdefined[s->val[OPT_GAMMA_CORRECTION].w] == SANE_TRUE) { /* s->opt[ OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; */ s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; } else { /* 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; } /* initialize the Gamma tables */ memset (&s->gamma_table[0], 0, 256 * sizeof (SANE_Word)); memset (&s->gamma_table[1], 0, 256 * sizeof (SANE_Word)); memset (&s->gamma_table[2], 0, 256 * sizeof (SANE_Word)); /* memset(&s->gamma_table[3], 0, 256 * sizeof(SANE_Word)); */ for (i = 0; i < 256; i++) { s->gamma_table[0][i] = i; s->gamma_table[1][i] = i; s->gamma_table[2][i] = i; /* s->gamma_table[3][i] = i; */ } /* color correction */ s->opt[OPT_COLOR_CORRECTION].name = "color-correction"; s->opt[OPT_COLOR_CORRECTION].title = SANE_I18N ("Color correction"); s->opt[OPT_COLOR_CORRECTION].desc = SANE_I18N ("Sets the color correction table for the selected output device."); s->opt[OPT_COLOR_CORRECTION].type = SANE_TYPE_STRING; s->opt[OPT_COLOR_CORRECTION].size = 32; s->opt[OPT_COLOR_CORRECTION].cap |= SANE_CAP_ADVANCED; s->opt[OPT_COLOR_CORRECTION].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_COLOR_CORRECTION].constraint.string_list = color_list; s->val[OPT_COLOR_CORRECTION].w = 5; /* scanner default: CRT monitors */ if (!s->hw->cmd->set_color_correction) { s->opt[OPT_COLOR_CORRECTION].cap |= SANE_CAP_INACTIVE; } /* resolution */ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_RESOLUTION].constraint.word_list = s->hw->resolution_list; s->val[OPT_RESOLUTION].w = s->hw->dpi_range.min; /* 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_INT; s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE; s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_THRESHOLD].constraint.range = &u8_range; s->val[OPT_THRESHOLD].w = 0x80; if (!s->hw->cmd->set_threshold) { s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; } s->opt[OPT_CCT_GROUP].title = SANE_I18N ("Color correction coefficients"); s->opt[OPT_CCT_GROUP].desc = SANE_I18N ("Matrix multiplication of RGB"); s->opt[OPT_CCT_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_CCT_GROUP].cap = SANE_CAP_ADVANCED; /* color correction coefficients */ s->opt[OPT_CCT_1].name = "cct-1"; s->opt[OPT_CCT_2].name = "cct-2"; s->opt[OPT_CCT_3].name = "cct-3"; s->opt[OPT_CCT_4].name = "cct-4"; s->opt[OPT_CCT_5].name = "cct-5"; s->opt[OPT_CCT_6].name = "cct-6"; s->opt[OPT_CCT_7].name = "cct-7"; s->opt[OPT_CCT_8].name = "cct-8"; s->opt[OPT_CCT_9].name = "cct-9"; s->opt[OPT_CCT_1].title = SANE_I18N ("Green"); s->opt[OPT_CCT_2].title = SANE_I18N ("Shift green to red"); s->opt[OPT_CCT_3].title = SANE_I18N ("Shift green to blue"); s->opt[OPT_CCT_4].title = SANE_I18N ("Shift red to green"); s->opt[OPT_CCT_5].title = SANE_I18N ("Red"); s->opt[OPT_CCT_6].title = SANE_I18N ("Shift red to blue"); s->opt[OPT_CCT_7].title = SANE_I18N ("Shift blue to green"); s->opt[OPT_CCT_8].title = SANE_I18N ("Shift blue to red"); s->opt[OPT_CCT_9].title = SANE_I18N ("Blue"); s->opt[OPT_CCT_1].desc = SANE_I18N ("Controls green level"); s->opt[OPT_CCT_2].desc = SANE_I18N ("Adds to red based on green level"); s->opt[OPT_CCT_3].desc = SANE_I18N ("Adds to blue based on green level"); s->opt[OPT_CCT_4].desc = SANE_I18N ("Adds to green based on red level"); s->opt[OPT_CCT_5].desc = SANE_I18N ("Controls red level"); s->opt[OPT_CCT_6].desc = SANE_I18N ("Adds to blue based on red level"); s->opt[OPT_CCT_7].desc = SANE_I18N ("Adds to green based on blue level"); s->opt[OPT_CCT_8].desc = SANE_I18N ("Adds to red based on blue level"); s->opt[OPT_CCT_9].desc = SANE_I18N ("Controls blue level"); s->opt[OPT_CCT_1].type = SANE_TYPE_INT; s->opt[OPT_CCT_2].type = SANE_TYPE_INT; s->opt[OPT_CCT_3].type = SANE_TYPE_INT; s->opt[OPT_CCT_4].type = SANE_TYPE_INT; s->opt[OPT_CCT_5].type = SANE_TYPE_INT; s->opt[OPT_CCT_6].type = SANE_TYPE_INT; s->opt[OPT_CCT_7].type = SANE_TYPE_INT; s->opt[OPT_CCT_8].type = SANE_TYPE_INT; s->opt[OPT_CCT_9].type = SANE_TYPE_INT; s->opt[OPT_CCT_1].cap |= SANE_CAP_ADVANCED; s->opt[OPT_CCT_2].cap |= SANE_CAP_ADVANCED; s->opt[OPT_CCT_3].cap |= SANE_CAP_ADVANCED; s->opt[OPT_CCT_4].cap |= SANE_CAP_ADVANCED; s->opt[OPT_CCT_5].cap |= SANE_CAP_ADVANCED; s->opt[OPT_CCT_6].cap |= SANE_CAP_ADVANCED; s->opt[OPT_CCT_7].cap |= SANE_CAP_ADVANCED; s->opt[OPT_CCT_8].cap |= SANE_CAP_ADVANCED; s->opt[OPT_CCT_9].cap |= SANE_CAP_ADVANCED; s->opt[OPT_CCT_1].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CCT_2].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CCT_3].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CCT_4].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CCT_5].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CCT_6].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CCT_7].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CCT_8].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CCT_9].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CCT_1].unit = SANE_UNIT_NONE; s->opt[OPT_CCT_2].unit = SANE_UNIT_NONE; s->opt[OPT_CCT_3].unit = SANE_UNIT_NONE; s->opt[OPT_CCT_4].unit = SANE_UNIT_NONE; s->opt[OPT_CCT_5].unit = SANE_UNIT_NONE; s->opt[OPT_CCT_6].unit = SANE_UNIT_NONE; s->opt[OPT_CCT_7].unit = SANE_UNIT_NONE; s->opt[OPT_CCT_8].unit = SANE_UNIT_NONE; s->opt[OPT_CCT_9].unit = SANE_UNIT_NONE; s->opt[OPT_CCT_1].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CCT_2].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CCT_3].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CCT_4].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CCT_5].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CCT_6].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CCT_7].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CCT_8].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CCT_9].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CCT_1].constraint.range = &s8_range; s->opt[OPT_CCT_2].constraint.range = &s8_range; s->opt[OPT_CCT_3].constraint.range = &s8_range; s->opt[OPT_CCT_4].constraint.range = &s8_range; s->opt[OPT_CCT_5].constraint.range = &s8_range; s->opt[OPT_CCT_6].constraint.range = &s8_range; s->opt[OPT_CCT_7].constraint.range = &s8_range; s->opt[OPT_CCT_8].constraint.range = &s8_range; s->opt[OPT_CCT_9].constraint.range = &s8_range; s->val[OPT_CCT_1].w = 32; s->val[OPT_CCT_2].w = 0; s->val[OPT_CCT_3].w = 0; s->val[OPT_CCT_4].w = 0; s->val[OPT_CCT_5].w = 32; s->val[OPT_CCT_6].w = 0; s->val[OPT_CCT_7].w = 0; s->val[OPT_CCT_8].w = 0; s->val[OPT_CCT_9].w = 32; if (!s->hw->cmd->set_color_correction_coefficients) { s->opt[OPT_CCT_1].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CCT_2].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CCT_3].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CCT_4].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CCT_5].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CCT_6].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CCT_7].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CCT_8].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CCT_9].cap |= SANE_CAP_INACTIVE; } /* "Advanced" group: */ s->opt[OPT_ADVANCED_GROUP].title = SANE_I18N ("Advanced"); s->opt[OPT_ADVANCED_GROUP].desc = ""; s->opt[OPT_ADVANCED_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_ADVANCED_GROUP].cap = SANE_CAP_ADVANCED; /* mirror */ s->opt[OPT_MIRROR].name = "mirror"; s->opt[OPT_MIRROR].title = SANE_I18N ("Mirror image"); s->opt[OPT_MIRROR].desc = SANE_I18N ("Mirror the image."); s->opt[OPT_MIRROR].type = SANE_TYPE_BOOL; s->val[OPT_MIRROR].w = SANE_FALSE; if (!s->hw->cmd->mirror_image) { s->opt[OPT_MIRROR].cap |= SANE_CAP_INACTIVE; } /* speed */ s->opt[OPT_SPEED].name = SANE_NAME_SCAN_SPEED; s->opt[OPT_SPEED].title = SANE_TITLE_SCAN_SPEED; s->opt[OPT_SPEED].desc = SANE_DESC_SCAN_SPEED; s->opt[OPT_SPEED].type = SANE_TYPE_BOOL; s->val[OPT_SPEED].w = SANE_FALSE; if (!s->hw->cmd->set_speed) { s->opt[OPT_SPEED].cap |= SANE_CAP_INACTIVE; } /* preview speed */ s->opt[OPT_PREVIEW_SPEED].name = "preview-speed"; s->opt[OPT_PREVIEW_SPEED].title = SANE_I18N ("Fast preview"); s->opt[OPT_PREVIEW_SPEED].desc = ""; s->opt[OPT_PREVIEW_SPEED].type = SANE_TYPE_BOOL; s->val[OPT_PREVIEW_SPEED].w = SANE_FALSE; if (!s->hw->cmd->set_speed) { s->opt[OPT_PREVIEW_SPEED].cap |= SANE_CAP_INACTIVE; } /* auto area segmentation */ s->opt[OPT_AAS].name = "auto-area-segmentation"; s->opt[OPT_AAS].title = SANE_I18N ("Auto area segmentation"); s->opt[OPT_AAS].desc = ""; s->opt[OPT_AAS].type = SANE_TYPE_BOOL; s->val[OPT_AAS].w = SANE_TRUE; if (!s->hw->cmd->control_auto_area_segmentation) { s->opt[OPT_AAS].cap |= SANE_CAP_INACTIVE; } /* limit resolution list */ s->opt[OPT_LIMIT_RESOLUTION].name = "short-resolution"; s->opt[OPT_LIMIT_RESOLUTION].title = SANE_I18N ("Short resolution list"); s->opt[OPT_LIMIT_RESOLUTION].desc = SANE_I18N ("Display short resolution list"); s->opt[OPT_LIMIT_RESOLUTION].type = SANE_TYPE_BOOL; s->val[OPT_LIMIT_RESOLUTION].w = SANE_FALSE; /* zoom */ s->opt[OPT_ZOOM].name = "zoom"; s->opt[OPT_ZOOM].title = SANE_I18N ("Zoom"); s->opt[OPT_ZOOM].desc = SANE_I18N ("Defines the zoom factor the scanner will use"); s->opt[OPT_ZOOM].type = SANE_TYPE_INT; s->opt[OPT_ZOOM].unit = SANE_UNIT_NONE; s->opt[OPT_ZOOM].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_ZOOM].constraint.range = &zoom_range; s->val[OPT_ZOOM].w = 100; /* if( ! s->hw->cmd->set_zoom) */ { s->opt[OPT_ZOOM].cap |= SANE_CAP_INACTIVE; } /* "Preview settings" group: */ s->opt[OPT_PREVIEW_GROUP].title = SANE_TITLE_PREVIEW; s->opt[OPT_PREVIEW_GROUP].desc = ""; s->opt[OPT_PREVIEW_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_PREVIEW_GROUP].cap = SANE_CAP_ADVANCED; /* preview */ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; s->val[OPT_PREVIEW].w = SANE_FALSE; /* "Geometry" group: */ s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry"); s->opt[OPT_GEOMETRY_GROUP].desc = ""; s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; /* top-left x */ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; s->opt[OPT_TL_X].unit = SANE_UNIT_MM; s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_X].constraint.range = s->hw->x_range; s->val[OPT_TL_X].w = 0; /* top-left y */ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_Y].constraint.range = s->hw->y_range; s->val[OPT_TL_Y].w = 0; /* bottom-right x */ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; s->opt[OPT_BR_X].unit = SANE_UNIT_MM; s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_X].constraint.range = s->hw->x_range; s->val[OPT_BR_X].w = s->hw->x_range->max; /* bottom-right y */ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_Y].constraint.range = s->hw->y_range; s->val[OPT_BR_Y].w = s->hw->y_range->max; /* Quick format */ s->opt[OPT_QUICK_FORMAT].name = "quick-format"; s->opt[OPT_QUICK_FORMAT].title = SANE_I18N ("Quick format"); s->opt[OPT_QUICK_FORMAT].desc = ""; s->opt[OPT_QUICK_FORMAT].type = SANE_TYPE_STRING; s->opt[OPT_QUICK_FORMAT].size = max_string_size (qf_list); s->opt[OPT_QUICK_FORMAT].cap |= SANE_CAP_ADVANCED; s->opt[OPT_QUICK_FORMAT].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_QUICK_FORMAT].constraint.string_list = qf_list; s->val[OPT_QUICK_FORMAT].w = XtNumber (qf_params) - 1; /* max */ /* "Optional equipment" group: */ s->opt[OPT_EQU_GROUP].title = SANE_I18N ("Optional equipment"); s->opt[OPT_EQU_GROUP].desc = ""; s->opt[OPT_EQU_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_EQU_GROUP].cap = SANE_CAP_ADVANCED; /* source */ s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE; s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE; s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE; s->opt[OPT_SOURCE].type = SANE_TYPE_STRING; s->opt[OPT_SOURCE].size = max_string_size (source_list); s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_SOURCE].constraint.string_list = source_list; if (!s->hw->extension) { s->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE; } s->val[OPT_SOURCE].w = 0; /* always use Flatbed as default */ /* film type */ s->opt[OPT_FILM_TYPE].name = "film-type"; s->opt[OPT_FILM_TYPE].title = SANE_I18N ("Film type"); s->opt[OPT_FILM_TYPE].desc = ""; s->opt[OPT_FILM_TYPE].type = SANE_TYPE_STRING; s->opt[OPT_FILM_TYPE].size = max_string_size (film_list); s->opt[OPT_FILM_TYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_FILM_TYPE].constraint.string_list = film_list; s->val[OPT_FILM_TYPE].w = 0; deactivateOption (s, OPT_FILM_TYPE, &dummy); /* default is inactive */ /* focus position */ s->opt[OPT_FOCUS].name = SANE_EPSON_FOCUS_NAME; s->opt[OPT_FOCUS].title = SANE_EPSON_FOCUS_TITLE; s->opt[OPT_FOCUS].desc = SANE_EPSON_FOCUS_DESC; s->opt[OPT_FOCUS].type = SANE_TYPE_STRING; s->opt[OPT_FOCUS].size = max_string_size (focus_list); s->opt[OPT_FOCUS].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_FOCUS].constraint.string_list = focus_list; s->val[OPT_FOCUS].w = 0; s->opt[OPT_FOCUS].cap |= SANE_CAP_ADVANCED; if (s->hw->focusSupport == SANE_TRUE) { s->opt[OPT_FOCUS].cap &= ~SANE_CAP_INACTIVE; } else { s->opt[OPT_FOCUS].cap |= SANE_CAP_INACTIVE; } #if 0 if ((!s->hw->TPU) && (!s->hw->cmd->set_bay)) { /* Hack: Using set_bay to indicate. */ SANE_Bool dummy; deactivateOption (s, OPT_FILM_TYPE, &dummy); } #endif /* forward feed / eject */ s->opt[OPT_EJECT].name = "eject"; s->opt[OPT_EJECT].title = SANE_I18N ("Eject"); s->opt[OPT_EJECT].desc = SANE_I18N ("Eject the sheet in the ADF"); s->opt[OPT_EJECT].type = SANE_TYPE_BUTTON; if ((!s->hw->ADF) && (!s->hw->cmd->set_bay)) { /* Hack: Using set_bay to indicate. */ s->opt[OPT_EJECT].cap |= SANE_CAP_INACTIVE; } /* auto forward feed / eject */ s->opt[OPT_AUTO_EJECT].name = "auto-eject"; s->opt[OPT_AUTO_EJECT].title = SANE_I18N ("Auto eject"); s->opt[OPT_AUTO_EJECT].desc = SANE_I18N ("Eject document after scanning"); s->opt[OPT_AUTO_EJECT].type = SANE_TYPE_BOOL; s->val[OPT_AUTO_EJECT].w = SANE_FALSE; if (!s->hw->ADF) { s->opt[OPT_AUTO_EJECT].cap |= SANE_CAP_INACTIVE; } s->opt[OPT_ADF_MODE].name = "adf_mode"; s->opt[OPT_ADF_MODE].title = SANE_I18N ("ADF Mode"); s->opt[OPT_ADF_MODE].desc = SANE_I18N ("Selects the ADF mode (simplex/duplex)"); s->opt[OPT_ADF_MODE].type = SANE_TYPE_STRING; s->opt[OPT_ADF_MODE].size = max_string_size (adf_mode_list); s->opt[OPT_ADF_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_ADF_MODE].constraint.string_list = adf_mode_list; s->val[OPT_ADF_MODE].w = 0; /* simplex */ if ((!s->hw->ADF) || (s->hw->duplexSupport == SANE_FALSE)) { s->opt[OPT_ADF_MODE].cap |= SANE_CAP_INACTIVE; } /* select bay */ s->opt[OPT_BAY].name = "bay"; s->opt[OPT_BAY].title = SANE_I18N ("Bay"); s->opt[OPT_BAY].desc = SANE_I18N ("Select bay to scan"); s->opt[OPT_BAY].type = SANE_TYPE_STRING; s->opt[OPT_BAY].size = max_string_size (bay_list); s->opt[OPT_BAY].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_BAY].constraint.string_list = bay_list; s->val[OPT_BAY].w = 0; /* Bay 1 */ if (!s->hw->cmd->set_bay) { s->opt[OPT_BAY].cap |= SANE_CAP_INACTIVE; } s->opt[OPT_WAIT_FOR_BUTTON].name = SANE_EPSON_WAIT_FOR_BUTTON_NAME; s->opt[OPT_WAIT_FOR_BUTTON].title = SANE_EPSON_WAIT_FOR_BUTTON_TITLE; s->opt[OPT_WAIT_FOR_BUTTON].desc = SANE_EPSON_WAIT_FOR_BUTTON_DESC; s->opt[OPT_WAIT_FOR_BUTTON].type = SANE_TYPE_BOOL; s->opt[OPT_WAIT_FOR_BUTTON].unit = SANE_UNIT_NONE; s->opt[OPT_WAIT_FOR_BUTTON].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_WAIT_FOR_BUTTON].constraint.range = NULL; s->opt[OPT_WAIT_FOR_BUTTON].cap |= SANE_CAP_ADVANCED; if (!s->hw->cmd->request_push_button_status) { s->opt[OPT_WAIT_FOR_BUTTON].cap |= SANE_CAP_INACTIVE; } return SANE_STATUS_GOOD; } /* * * */ SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { Epson_Device *dev; Epson_Scanner *s; DBG (5, "sane_open(%s)\n", devicename); /* search for device */ if (devicename[0]) { for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->sane.name, devicename) == 0) { break; } } if (!dev) { #if 0 status = attach (devicename, &dev, SANE_EPSON_); if (status != SANE_STATUS_GOOD) { return status; } #endif DBG (1, "Error opening the device"); return SANE_STATUS_INVAL; } } else { dev = first_dev; } if (!dev) { return SANE_STATUS_INVAL; } s = calloc (sizeof (Epson_Scanner), 1); if (!s) { DBG (1, "out of memory (line %d)\n", __LINE__); return SANE_STATUS_NO_MEM; } s->fd = -1; s->hw = dev; init_options (s); /* insert newly opened handle into list of open handles */ s->next = first_handle; first_handle = s; *handle = (SANE_Handle) s; open_scanner (s); return SANE_STATUS_GOOD; } /* * * */ void sane_close (SANE_Handle handle) { Epson_Scanner *s, *prev; /* * Test if there is still data pending from * the scanner. If so, then do a cancel */ s = (Epson_Scanner *) handle; /* remove handle from list of open handles */ prev = 0; for (s = first_handle; s; s = s->next) { if (s == handle) break; prev = s; } if (!s) { DBG (1, "close: invalid handle (0x%p)\n", handle); return; } if (prev) prev->next = s->next; else first_handle = s->next; if (s->fd != -1) close_scanner (s); free (s); } /* * * */ const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { Epson_Scanner *s = (Epson_Scanner *) handle; if (option < 0 || option >= NUM_OPTIONS) return NULL; return (s->opt + option); } /* * * */ static const SANE_String_Const * search_string_list (const SANE_String_Const * list, SANE_String value) { while (*list != NULL && strcmp (value, *list) != 0) { ++list; } return ((*list == NULL) ? NULL : list); } /* * * */ /* Activate, deactivate an option. Subroutines so we can add debugging info if we want. The change flag is set to TRUE if we changed an option. If we did not change an option, then the value of the changed flag is not modified. */ static void activateOption (Epson_Scanner * s, SANE_Int option, SANE_Bool * change) { if (!SANE_OPTION_IS_ACTIVE (s->opt[option].cap)) { s->opt[option].cap &= ~SANE_CAP_INACTIVE; *change = SANE_TRUE; } } static void deactivateOption (Epson_Scanner * s, SANE_Int option, SANE_Bool * change) { if (SANE_OPTION_IS_ACTIVE (s->opt[option].cap)) { s->opt[option].cap |= SANE_CAP_INACTIVE; *change = SANE_TRUE; } } static void setOptionState (Epson_Scanner * s, SANE_Bool state, SANE_Int option, SANE_Bool * change) { if (state) { activateOption (s, option, change); } else { deactivateOption (s, option, change); } } /** End of activateOption, deactivateOption, setOptionState. **/ static SANE_Status getvalue (SANE_Handle handle, SANE_Int option, void *value) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Option_Descriptor *sopt = &(s->opt[option]); Option_Value *sval = &(s->val[option]); switch (option) { /* case OPT_GAMMA_VECTOR: */ case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: memcpy (value, sval->wa, sopt->size); break; case OPT_NUM_OPTS: case OPT_RESOLUTION: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_MIRROR: case OPT_SPEED: case OPT_PREVIEW_SPEED: case OPT_AAS: case OPT_PREVIEW: case OPT_BRIGHTNESS: case OPT_SHARPNESS: case OPT_AUTO_EJECT: case OPT_CCT_1: case OPT_CCT_2: case OPT_CCT_3: case OPT_CCT_4: case OPT_CCT_5: case OPT_CCT_6: case OPT_CCT_7: case OPT_CCT_8: case OPT_CCT_9: case OPT_THRESHOLD: case OPT_ZOOM: case OPT_BIT_DEPTH: case OPT_WAIT_FOR_BUTTON: case OPT_LIMIT_RESOLUTION: *((SANE_Word *) value) = sval->w; break; case OPT_MODE: case OPT_ADF_MODE: case OPT_HALFTONE: case OPT_DROPOUT: case OPT_QUICK_FORMAT: case OPT_SOURCE: case OPT_FILM_TYPE: case OPT_GAMMA_CORRECTION: case OPT_COLOR_CORRECTION: case OPT_BAY: case OPT_FOCUS: strcpy ((char *) value, sopt->constraint.string_list[sval->w]); break; #if 0 case OPT_MODEL: strcpy (value, sval->s); break; #endif default: return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } /** End of getvalue. **/ static void handle_depth_halftone (Epson_Scanner * s, SANE_Bool * reload) /* This routine handles common options between OPT_MODE and OPT_HALFTONE. These options are TET (a HALFTONE mode), AAS - auto area segmentation, and threshold. Apparently AAS is some method to differentiate between text and photos. Or something like that. AAS is available when the scan color depth is 1 and the halftone method is not TET. Threshold is available when halftone is NONE, and depth is 1. */ { int hti = s->val[OPT_HALFTONE].w; int mdi = s->val[OPT_MODE].w; SANE_Bool aas = SANE_FALSE; SANE_Bool thresh = SANE_FALSE; if (!s->hw->cmd->control_auto_area_segmentation) return; if (mode_params[mdi].depth == 1) { if (halftone_params[hti] != HALFTONE_TET) { aas = SANE_TRUE; } if (halftone_params[hti] == HALFTONE_NONE) { thresh = SANE_TRUE; } } setOptionState (s, aas, OPT_AAS, reload); setOptionState (s, thresh, OPT_THRESHOLD, reload); } /** End of handle_depth_halftone. **/ static void handle_source (Epson_Scanner * s, SANE_Int optindex, char *value) /* Handles setting the source (flatbed, transparency adapter (TPU), or auto document feeder (ADF)). For newer scanners it also sets the focus according to the glass / TPU settings. */ { int force_max = SANE_FALSE; SANE_Bool dummy; /* reset the scanner when we are changing the source setting - this is necessary for the Perfection 1650 */ if (s->hw->need_reset_on_source_change) reset (s); s->focusOnGlass = SANE_TRUE; /* this is the default */ if (s->val[OPT_SOURCE].w == optindex) return; s->val[OPT_SOURCE].w = optindex; if (s->val[OPT_TL_X].w == s->hw->x_range->min && s->val[OPT_TL_Y].w == s->hw->y_range->min && s->val[OPT_BR_X].w == s->hw->x_range->max && s->val[OPT_BR_Y].w == s->hw->y_range->max) { force_max = SANE_TRUE; } if (strcmp (ADF_STR, value) == 0) { s->hw->x_range = &s->hw->adf_x_range; s->hw->y_range = &s->hw->adf_y_range; s->hw->use_extension = SANE_TRUE; /* disable film type option */ deactivateOption (s, OPT_FILM_TYPE, &dummy); s->val[OPT_FOCUS].w = 0; if (s->hw->duplexSupport) { activateOption (s, OPT_ADF_MODE, &dummy); } else { deactivateOption (s, OPT_ADF_MODE, &dummy); s->val[OPT_ADF_MODE].w = 0; } } else if (strcmp (TPU_STR, value) == 0) { s->hw->x_range = &s->hw->tpu_x_range; s->hw->y_range = &s->hw->tpu_y_range; s->hw->use_extension = SANE_TRUE; /* enable film type option only if the scanner supports it */ if (s->hw->cmd->set_film_type != 0) { activateOption (s, OPT_FILM_TYPE, &dummy); } else { deactivateOption (s, OPT_FILM_TYPE, &dummy); } /* enable focus position if the scanner supports it */ if (s->hw->cmd->set_focus_position != 0) { s->val[OPT_FOCUS].w = 1; s->focusOnGlass = SANE_FALSE; } deactivateOption (s, OPT_ADF_MODE, &dummy); deactivateOption (s, OPT_EJECT, &dummy); deactivateOption (s, OPT_AUTO_EJECT, &dummy); } else /* neither ADF nor TPU active */ { s->hw->x_range = &s->hw->fbf_x_range; s->hw->y_range = &s->hw->fbf_y_range; s->hw->use_extension = SANE_FALSE; /* disable film type option */ deactivateOption (s, OPT_FILM_TYPE, &dummy); s->val[OPT_FOCUS].w = 0; deactivateOption (s, OPT_ADF_MODE, &dummy); } qf_params[XtNumber (qf_params) - 1].tl_x = s->hw->x_range->min; qf_params[XtNumber (qf_params) - 1].tl_y = s->hw->y_range->min; qf_params[XtNumber (qf_params) - 1].br_x = s->hw->x_range->max; qf_params[XtNumber (qf_params) - 1].br_y = s->hw->y_range->max; s->opt[OPT_BR_X].constraint.range = s->hw->x_range; s->opt[OPT_BR_Y].constraint.range = s->hw->y_range; if (s->val[OPT_TL_X].w < s->hw->x_range->min || force_max) s->val[OPT_TL_X].w = s->hw->x_range->min; if (s->val[OPT_TL_Y].w < s->hw->y_range->min || force_max) s->val[OPT_TL_Y].w = s->hw->y_range->min; if (s->val[OPT_BR_X].w > s->hw->x_range->max || force_max) s->val[OPT_BR_X].w = s->hw->x_range->max; if (s->val[OPT_BR_Y].w > s->hw->y_range->max || force_max) s->val[OPT_BR_Y].w = s->hw->y_range->max; setOptionState (s, s->hw->ADF && s->hw->use_extension, OPT_AUTO_EJECT, &dummy); setOptionState (s, s->hw->ADF && s->hw->use_extension, OPT_EJECT, &dummy); #if 0 BAY is part of the filmscan device.We are not sure if we are really going to support this device in this code.Is there an online manual for it ? setOptionState (s, s->hw->ADF && s->hw->use_extension, OPT_BAY, &reload); #endif } /** End of handle_source. **/ static SANE_Status setvalue (SANE_Handle handle, SANE_Int option, void *value, SANE_Int * info) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Option_Descriptor *sopt = &(s->opt[option]); Option_Value *sval = &(s->val[option]); SANE_Status status; const SANE_String_Const *optval; int optindex; SANE_Bool reload = SANE_FALSE; DBG (5, "setvalue(option = %d, value = %p)\n", option, value); status = sanei_constrain_value (sopt, value, info); if (status != SANE_STATUS_GOOD) return status; s->option_has_changed = SANE_TRUE; optval = NULL; optindex = 0; if (sopt->constraint_type == SANE_CONSTRAINT_STRING_LIST) { optval = search_string_list (sopt->constraint.string_list, (char *) value); if (optval == NULL) return SANE_STATUS_INVAL; optindex = optval - sopt->constraint.string_list; } switch (option) { /* case OPT_GAMMA_VECTOR: */ case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: memcpy (sval->wa, value, sopt->size); /* Word arrays */ break; case OPT_CCT_1: case OPT_CCT_2: case OPT_CCT_3: case OPT_CCT_4: case OPT_CCT_5: case OPT_CCT_6: case OPT_CCT_7: case OPT_CCT_8: case OPT_CCT_9: sval->w = *((SANE_Word *) value); /* Simple values */ break; case OPT_DROPOUT: case OPT_FILM_TYPE: case OPT_BAY: case OPT_FOCUS: sval->w = optindex; /* Simple lists */ break; case OPT_EJECT: /* return eject( s ); */ eject (s); break; case OPT_RESOLUTION: sval->w = *((SANE_Word *) value); reload = SANE_TRUE; break; case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: sval->w = *((SANE_Word *) value); DBG (1, "set = %f\n", SANE_UNFIX (sval->w)); if (NULL != info) *info |= SANE_INFO_RELOAD_PARAMS; break; case OPT_SOURCE: handle_source (s, optindex, (char *) value); reload = SANE_TRUE; break; case OPT_MODE: { SANE_Bool isColor = mode_params[optindex].color; SANE_Bool userDefined = color_userdefined[s->val[OPT_COLOR_CORRECTION].w]; sval->w = optindex; if (s->hw->cmd->set_halftoning != 0) { setOptionState (s, mode_params[optindex].depth == 1, OPT_HALFTONE, &reload); } setOptionState (s, !isColor, OPT_DROPOUT, &reload); if (s->hw->cmd->set_color_correction) { setOptionState (s, isColor, OPT_COLOR_CORRECTION, &reload); } if (s->hw->cmd->set_color_correction_coefficients) { setOptionState (s, isColor && userDefined, OPT_CCT_1, &reload); setOptionState (s, isColor && userDefined, OPT_CCT_2, &reload); setOptionState (s, isColor && userDefined, OPT_CCT_3, &reload); setOptionState (s, isColor && userDefined, OPT_CCT_4, &reload); setOptionState (s, isColor && userDefined, OPT_CCT_5, &reload); setOptionState (s, isColor && userDefined, OPT_CCT_6, &reload); setOptionState (s, isColor && userDefined, OPT_CCT_7, &reload); setOptionState (s, isColor && userDefined, OPT_CCT_8, &reload); setOptionState (s, isColor && userDefined, OPT_CCT_9, &reload); } /* if binary, then disable the bit depth selection */ if (optindex == 0) { s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; } else { if (bitDepthList[0] == 1) s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; else { s->opt[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE; s->val[OPT_BIT_DEPTH].w = mode_params[optindex].depth; } } handle_depth_halftone (s, &reload); reload = SANE_TRUE; break; } case OPT_ADF_MODE: sval->w = optindex; break; case OPT_BIT_DEPTH: sval->w = *((SANE_Word *) value); mode_params[s->val[OPT_MODE].w].depth = sval->w; reload = SANE_TRUE; break; case OPT_HALFTONE: sval->w = optindex; handle_depth_halftone (s, &reload); break; case OPT_COLOR_CORRECTION: { SANE_Bool f = color_userdefined[optindex]; sval->w = optindex; setOptionState (s, f, OPT_CCT_1, &reload); setOptionState (s, f, OPT_CCT_2, &reload); setOptionState (s, f, OPT_CCT_3, &reload); setOptionState (s, f, OPT_CCT_4, &reload); setOptionState (s, f, OPT_CCT_5, &reload); setOptionState (s, f, OPT_CCT_6, &reload); setOptionState (s, f, OPT_CCT_7, &reload); setOptionState (s, f, OPT_CCT_8, &reload); setOptionState (s, f, OPT_CCT_9, &reload); break; } case OPT_GAMMA_CORRECTION: { SANE_Bool f = gamma_userdefined[optindex]; sval->w = optindex; /* setOptionState(s, f, OPT_GAMMA_VECTOR, &reload ); */ setOptionState (s, f, OPT_GAMMA_VECTOR_R, &reload); setOptionState (s, f, OPT_GAMMA_VECTOR_G, &reload); setOptionState (s, f, OPT_GAMMA_VECTOR_B, &reload); setOptionState (s, !f, OPT_BRIGHTNESS, &reload); /* Note... */ break; } case OPT_MIRROR: case OPT_SPEED: case OPT_PREVIEW_SPEED: case OPT_AAS: case OPT_PREVIEW: /* needed? */ case OPT_BRIGHTNESS: case OPT_SHARPNESS: case OPT_AUTO_EJECT: case OPT_THRESHOLD: case OPT_ZOOM: case OPT_WAIT_FOR_BUTTON: sval->w = *((SANE_Word *) value); break; case OPT_LIMIT_RESOLUTION: sval->w = *((SANE_Word *) value); filter_resolution_list (s); reload = SANE_TRUE; break; case OPT_QUICK_FORMAT: sval->w = optindex; s->val[OPT_TL_X].w = qf_params[sval->w].tl_x; s->val[OPT_TL_Y].w = qf_params[sval->w].tl_y; s->val[OPT_BR_X].w = qf_params[sval->w].br_x; s->val[OPT_BR_Y].w = qf_params[sval->w].br_y; if (s->val[OPT_TL_X].w < s->hw->x_range->min) s->val[OPT_TL_X].w = s->hw->x_range->min; if (s->val[OPT_TL_Y].w < s->hw->y_range->min) s->val[OPT_TL_Y].w = s->hw->y_range->min; if (s->val[OPT_BR_X].w > s->hw->x_range->max) s->val[OPT_BR_X].w = s->hw->x_range->max; if (s->val[OPT_BR_Y].w > s->hw->y_range->max) s->val[OPT_BR_Y].w = s->hw->y_range->max; reload = SANE_TRUE; break; default: return SANE_STATUS_INVAL; } if (reload && info != NULL) { *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; } return SANE_STATUS_GOOD; } /** End of setvalue. **/ SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int * info) { if (option < 0 || option >= NUM_OPTIONS) return SANE_STATUS_INVAL; if (info != NULL) *info = 0; switch (action) { case SANE_ACTION_GET_VALUE: return (getvalue (handle, option, value)); case SANE_ACTION_SET_VALUE: return (setvalue (handle, option, value, info)); default: return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } /* * sane_get_parameters() * * This function is part of the SANE API and gets called when the front end * requests information aobut the scan configuration (e.g. color depth, mode, * bytes and pixels per line, number of lines. This information is returned * in the SANE_Parameters structure. * * Once a scan was started, this routine has to report the correct values, if * it is called before the scan is actually started, the values are based on * the current settings. * */ SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { Epson_Scanner *s = (Epson_Scanner *) handle; int ndpi; int bytes_per_pixel; DBG (5, "sane_get_parameters()\n"); /* * If sane_start was already called, then just retrieve the parameters * from the scanner data structure */ if (!s->eof && s->ptr != NULL) { DBG (5, "Returning saved params structure\n"); if (params != NULL) { DBG(1, "Restoring parameters from saved parameters\n"); *params = s->params; } DBG (3, "Preview = %d\n", s->val[OPT_PREVIEW].w); DBG (3, "Resolution = %d\n", s->val[OPT_RESOLUTION].w); DBG (1, "get para %p %p tlx %f tly %f brx %f bry %f [mm]\n", (void *) s, (void *) s->val, SANE_UNFIX (s->val[OPT_TL_X].w), SANE_UNFIX (s->val[OPT_TL_Y].w), SANE_UNFIX (s->val[OPT_BR_X].w), SANE_UNFIX (s->val[OPT_BR_Y].w)); print_params (s->params); return SANE_STATUS_GOOD; } /* otherwise initialize the params structure and gather the data */ memset (&s->params, 0, sizeof (SANE_Parameters)); ndpi = s->val[OPT_RESOLUTION].w; s->params.pixels_per_line = SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w) / 25.4 * ndpi + 0.5; s->params.lines = SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w) / 25.4 * ndpi + 0.5; /* * Make sure that the number of lines is correct for color shuffling: * The shuffling algorithm produces 2xline_distance lines at the * beginning and the same amount at the end of the scan that are not * useable. If s->params.lines gets negative, 0 lines are reported * back to the frontend. */ if (s->hw->color_shuffle) { s->params.lines -= 4 * s->line_distance; if (s->params.lines < 0) { s->params.lines = 0; } DBG (1, "Adjusted params.lines for color_shuffle by %d to %d\n", 4 * s->line_distance, s->params.lines); } DBG (3, "Preview = %d\n", s->val[OPT_PREVIEW].w); DBG (3, "Resolution = %d\n", s->val[OPT_RESOLUTION].w); DBG (1, "get para %p %p tlx %f tly %f brx %f bry %f [mm]\n", (void *) s, (void *) s->val, SANE_UNFIX (s->val[OPT_TL_X].w), SANE_UNFIX (s->val[OPT_TL_Y].w), SANE_UNFIX (s->val[OPT_BR_X].w), SANE_UNFIX (s->val[OPT_BR_Y].w)); /* * Calculate bytes_per_pixel and bytes_per_line for * any color depths. * * The default color depth is stored in mode_params.depth: */ if (mode_params[s->val[OPT_MODE].w].depth == 1) { s->params.depth = 1; } else { s->params.depth = s->val[OPT_BIT_DEPTH].w; } if (s->params.depth > 8) { s->params.depth = 16; /* * The frontends can only handle 8 or 16 bits * for gray or color - so if it's more than 8, * it gets automatically set to 16. This works * as long as EPSON does not come out with a * scanner that can handle more than 16 bits * per color channel. */ } bytes_per_pixel = s->params.depth / 8; /* this works because it can only be set to 1, 8 or 16 */ if (s->params.depth % 8) /* just in case ... */ { bytes_per_pixel++; } /* pixels_per_line is rounded to the next 8bit boundary */ s->params.pixels_per_line = s->params.pixels_per_line & ~7; s->params.last_frame = SANE_TRUE; if (mode_params[s->val[OPT_MODE].w].color) { s->params.format = SANE_FRAME_RGB; s->params.bytes_per_line = 3 * s->params.pixels_per_line * bytes_per_pixel; } else { s->params.format = SANE_FRAME_GRAY; s->params.bytes_per_line = s->params.pixels_per_line * s->params.depth / 8; } if (NULL != params) *params = s->params; print_params (s->params); return SANE_STATUS_GOOD; } /* * sane_start() * * This function is part of the SANE API and gets called from the front end to * start the scan process. * */ SANE_Status sane_start (SANE_Handle handle) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Status status; SANE_Bool button_status; const struct mode_param *mparam; u_char params[4]; int ndpi; int left, top; int lcount; int i, j; /* loop counter */ DBG (5, "sane_start()\n"); open_scanner (s); /* * There is some undocumented special behavior with the TPU enable/disable. * TPU power ESC e status * on 0 NAK * on 1 ACK * off 0 ACK * off 1 NAK * * It makes no sense to scan with TPU powered on and source flatbed, because * light will come from both sides. */ if (s->hw->extension) { int max_x, max_y; int extensionCtrl; extensionCtrl = (s->hw->use_extension ? 1 : 0); if (s->hw->use_extension && (s->val[OPT_ADF_MODE].w == 1)) extensionCtrl = 2; status = control_extension (s, extensionCtrl); if (SANE_STATUS_GOOD != status) { DBG (1, "You may have to power %s your TPU\n", s->hw->use_extension ? "on" : "off"); DBG (1, "Also you may have to restart the Sane frontend.\n"); close_scanner (s); return status; } if (s->hw->cmd->request_extended_status != 0) { status = check_ext_status (s, &max_x, &max_y); if (SANE_STATUS_GOOD != status && SANE_STATUS_DEVICE_BUSY != status) { close_scanner (s); return status; } } if (s->hw->ADF && s->hw->use_extension && s->hw->cmd->feed) { status = feed (s); if (SANE_STATUS_GOOD != status) { close_scanner (s); return status; } check_ext_status (s, &max_x, &max_y); s->hw->adf_max_x = max_x; s->hw->adf_max_y = max_y; } /* * set the focus position according to the extension used: * if the TPU is selected, then focus 2.5mm above the glass, * otherwise focus on the glass. Scanners that don't support * this feature, will just ignore these calls. */ if (s->hw->focusSupport == SANE_TRUE) { if (s->val[OPT_FOCUS].w == 0) { DBG (1, "Setting focus to glass surface\n"); set_focus_position (s, 0x40); } else { DBG (1, "Setting focus to 2.5mm above glass\n"); set_focus_position (s, 0x59); } } } /* use the flatbed size for the max. scansize for the GT-30000 and similar scanners if the ADF is not enabled */ if (s->hw->devtype == 3 && s->hw->use_extension == 0) { int max_x, max_y; status = check_ext_status (s, &max_x, &max_y); if (SANE_STATUS_GOOD != status && SANE_STATUS_DEVICE_BUSY != status) { close_scanner (s); return status; } s->hw->fbf_max_x = max_x; s->hw->fbf_max_y = max_y; } mparam = mode_params + s->val[OPT_MODE].w; DBG (1, "sane_start: Setting data format to %d bits\n", mparam->depth); status = set_data_format (s, mparam->depth); if (SANE_STATUS_GOOD != status) { DBG (1, "sane_start: set_data_format failed: %s\n", sane_strstatus (status)); return status; } /* * The byte sequence mode was introduced in B5, for B[34] we need line sequence mode */ if ((s->hw->cmd->level[0] == 'D' || (s->hw->cmd->level[0] == 'B' && s->hw->level >= 5)) && mparam->mode_flags == 0x02) { status = set_color_mode (s, 0x13); } else { status = set_color_mode (s, mparam->mode_flags | (mparam->dropout_mask & dropout_params[s-> val [OPT_DROPOUT]. w])); } if (SANE_STATUS_GOOD != status) { DBG (1, "sane_start: set_color_mode failed: %s\n", sane_strstatus (status)); return status; } if (s->hw->cmd->set_halftoning && SANE_OPTION_IS_ACTIVE (s->opt[OPT_HALFTONE].cap)) { status = set_halftoning (s, halftone_params[s->val[OPT_HALFTONE].w]); if (SANE_STATUS_GOOD != status) { DBG (1, "sane_start: set_halftoning failed: %s\n", sane_strstatus (status)); return status; } } if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_BRIGHTNESS].cap)) { status = set_bright (s, s->val[OPT_BRIGHTNESS].w); if (SANE_STATUS_GOOD != status) { DBG (1, "sane_start: set_bright failed: %s\n", sane_strstatus (status)); return status; } } if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_MIRROR].cap)) { status = mirror_image (s, mirror_params[s->val[OPT_MIRROR].w]); if (SANE_STATUS_GOOD != status) { DBG (1, "sane_start: mirror_image failed: %s\n", sane_strstatus (status)); return status; } } if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_SPEED].cap)) { if (s->val[OPT_PREVIEW].w) status = set_speed (s, speed_params[s->val[OPT_PREVIEW_SPEED].w]); else status = set_speed (s, speed_params[s->val[OPT_SPEED].w]); if (SANE_STATUS_GOOD != status) { DBG (1, "sane_start: set_speed failed: %s\n", sane_strstatus (status)); return status; } } /* * use of speed_params is ok here since they are false and true. * NOTE: I think I should throw that "params" stuff as long w is already the value. */ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_AAS].cap)) { status = control_auto_area_segmentation (s, speed_params[s->val[OPT_AAS].w]); if (SANE_STATUS_GOOD != status) { DBG (1, "sane_start: control_auto_area_segmentation failed: %s\n", sane_strstatus (status)); return status; } } s->invert_image = SANE_FALSE; /* default: to not inverting the image */ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_FILM_TYPE].cap)) { s->invert_image = (s->val[OPT_FILM_TYPE].w == FILM_TYPE_NEGATIVE); status = set_film_type (s, film_params[s->val[OPT_FILM_TYPE].w]); if (SANE_STATUS_GOOD != status) { DBG (1, "sane_start: set_film_type failed: %s\n", sane_strstatus (status)); return status; } } if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_BAY].cap)) { status = set_bay (s, s->val[OPT_BAY].w); if (SANE_STATUS_GOOD != status) { DBG (1, "sane_start: set_bay: %s\n", sane_strstatus (status)); return status; } } if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_SHARPNESS].cap)) { status = set_outline_emphasis (s, s->val[OPT_SHARPNESS].w); if (SANE_STATUS_GOOD != status) { DBG (1, "sane_start: set_outline_emphasis failed: %s\n", sane_strstatus (status)); return status; } } if (s->hw->cmd->set_gamma && SANE_OPTION_IS_ACTIVE (s->opt[OPT_GAMMA_CORRECTION].cap)) { int val; if (s->hw->cmd->level[0] == 'D') { /* * The D1 level has only the two user defined gamma * settings. */ val = gamma_params[s->val[OPT_GAMMA_CORRECTION].w]; } else { val = gamma_params[s->val[OPT_GAMMA_CORRECTION].w]; /* * If "Default" is selected then determine the actual value * to send to the scanner: If bilevel mode, just send the * value from the table (0x01), for grayscale or color mode * add one and send 0x02. */ /* if( s->val[ OPT_GAMMA_CORRECTION].w <= 1) { */ if (s->val[OPT_GAMMA_CORRECTION].w == 0) { val += mparam->depth == 1 ? 0 : 1; } } DBG (1, "sane_start: set_gamma( s, 0x%x ).\n", val); status = set_gamma (s, val); if (SANE_STATUS_GOOD != status) { DBG (1, "sane_start: set_gamma failed: %s\n", sane_strstatus (status)); return status; } } if (s->hw->cmd->set_gamma_table && gamma_userdefined[s->val[OPT_GAMMA_CORRECTION].w]) { /* user defined. */ status = set_gamma_table (s); if (SANE_STATUS_GOOD != status) { DBG (1, "sane_start: set_gamma_table failed: %s\n", sane_strstatus (status)); return status; } } /* * TODO: think about if SANE_OPTION_IS_ACTIVE is a good criteria to send commands. */ if (SANE_OPTION_IS_ACTIVE (s->opt[OPT_COLOR_CORRECTION].cap)) { int val = color_params[s->val[OPT_COLOR_CORRECTION].w]; DBG (1, "sane_start: set_color_correction( s, 0x%x )\n", val); status = set_color_correction (s, val); if (SANE_STATUS_GOOD != status) { DBG (1, "sane_start: set_color_correction failed: %s\n", sane_strstatus (status)); return status; } } if (1 == s->val[OPT_COLOR_CORRECTION].w) { /* user defined. */ status = set_color_correction_coefficients (s); if (SANE_STATUS_GOOD != status) { DBG (1, "sane_start: set_color_correction_coefficients failed: %s\n", sane_strstatus (status)); return status; } } if (s->hw->cmd->set_threshold != 0 && SANE_OPTION_IS_ACTIVE (s->opt[OPT_THRESHOLD].cap)) { status = set_threshold (s, s->val[OPT_THRESHOLD].w); if (SANE_STATUS_GOOD != status) { DBG (1, "sane_start: set_threshold(%d) failed: %s\n", s->val[OPT_THRESHOLD].w, sane_strstatus (status)); return status; } } ndpi = s->val[OPT_RESOLUTION].w; status = set_resolution (s, ndpi, ndpi); if (SANE_STATUS_GOOD != status) { DBG (1, "sane_start: set_resolution(%d, %d) failed: %s\n", ndpi, ndpi, sane_strstatus (status)); return status; } status = sane_get_parameters (handle, NULL); if (status != SANE_STATUS_GOOD) return status; /* set the zoom */ if (s->hw->cmd->set_zoom != 0 && SANE_OPTION_IS_ACTIVE (s->opt[OPT_ZOOM].cap)) { status = set_zoom (s, s->val[OPT_ZOOM].w, s->val[OPT_ZOOM].w); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: set_zoom(%d) failed: %s\n", s->val[OPT_ZOOM].w, sane_strstatus (status)); return status; } } /* * Now s->params is initialized. */ /* * If WAIT_FOR_BUTTON is active, then do just that: Wait until the button is * pressed. If the button was already pressed, then we will get the button * Pressed event right away. */ if (s->val[OPT_WAIT_FOR_BUTTON].w == SANE_TRUE) { s->hw->wait_for_button = SANE_TRUE; while (s->hw->wait_for_button == SANE_TRUE) { if (s->canceling == SANE_TRUE) { s->hw->wait_for_button = SANE_FALSE; } /* get the button status from the scanner */ else if (request_push_button_status (s, &button_status) == SANE_STATUS_GOOD) { if (button_status == SANE_TRUE) { s->hw->wait_for_button = SANE_FALSE; } else { sleep (1); } } else { /* we ran into an error condition, just continue */ s->hw->wait_for_button = SANE_FALSE; } } } /* * in file:frontend/preview.c * * The preview strategy is as follows: * * 1) A preview always acquires an image that covers the entire * scan surface. This is necessary so the user can see not * only what is, but also what isn't selected. */ left = SANE_UNFIX (s->val[OPT_TL_X].w) / 25.4 * ndpi + 0.5; top = SANE_UNFIX (s->val[OPT_TL_Y].w) / 25.4 * ndpi + 0.5; /* * Calculate correction for line_distance in D1 scanner: * Start line_distance lines earlier and add line_distance lines at the end * * Because the actual line_distance is not yet calculated we have to do this * first. */ s->hw->color_shuffle = SANE_FALSE; s->current_output_line = 0; s->lines_written = 0; s->color_shuffle_line = 0; if ((s->hw->optical_res != 0) && (mparam->depth == 8) && (mparam->mode_flags != 0)) { s->line_distance = s->hw->max_line_distance * ndpi / s->hw->optical_res; if (s->line_distance != 0) { s->hw->color_shuffle = SANE_TRUE; } else s->hw->color_shuffle = SANE_FALSE; } /* * for debugging purposes: */ #ifdef FORCE_COLOR_SHUFFLE DBG (1, "Test mode: FORCE_COLOR_SHUFFLE = TRUE\n"); s->hw->color_shuffle = SANE_TRUE; #endif /* * Modify the scan area: If the scanner requires color shuffling, then we try to * scan more lines to compensate for the lines that will be removed from the scan * due to the color shuffling algorithm. * At this time we add two times the line distance to the number of scan lines if * this is possible - if not, then we try to calculate the number of additional * lines according to the selected scan area. */ if (s->hw->color_shuffle == SANE_TRUE) { /* start the scan 2*line_distance earlier */ top -= 2 * s->line_distance; if (top < 0) { top = 0; } /* scan 4*line_distance lines more */ s->params.lines += 4 * s->line_distance; } /* * If (top + s->params.lines) is larger than the max scan area, reset * the number of scan lines: */ if (SANE_UNFIX (s->val[OPT_BR_Y].w) / 25.4 * ndpi < (s->params.lines + top)) { s->params.lines = ((int) SANE_UNFIX (s->val[OPT_BR_Y].w) / 25.4 * ndpi + 0.5) - top; } status = set_scan_area (s, left, top, s->params.pixels_per_line, s->params.lines); if (SANE_STATUS_GOOD != status) { DBG (1, "sane_start: set_scan_area failed: %s\n", sane_strstatus (status)); return status; } s->block = SANE_FALSE; lcount = 1; /* * The set line count commands needs to be sent for certain scanners in * color mode. The D1 level requires it, we are however only testing for * 'D' and not for the actual numeric level. */ if (((s->hw->cmd->level[0] == 'B') && ((s->hw->level >= 5) || ((s->hw->level >= 4) && (!mode_params[s->val[OPT_MODE].w].color)))) || (s->hw->cmd->level[0] == 'D')) { s->block = SANE_TRUE; lcount = sanei_scsi_max_request_size / s->params.bytes_per_line; if (lcount >= 255) { lcount = 255; } if (s->hw->TPU && s->hw->use_extension && lcount > 32) { lcount = 32; } /* * The D1 series of scanners only allow an even line number * for bi-level scanning. If a bit depth of 1 is selected, then * make sure the next lower even number is selected. */ if (s->hw->cmd->level[0] == 'D') { if (lcount % 2) { lcount -= 1; } } if (lcount == 0) { DBG (1, "out of memory (line %d)\n", __LINE__); return SANE_STATUS_NO_MEM; } status = set_lcount (s, lcount); if (SANE_STATUS_GOOD != status) { DBG (1, "sane_start: set_lcount(%d) failed: %s\n", lcount, sane_strstatus (status)); return status; } } if (s->hw->cmd->request_extended_status != 0 && SANE_TRUE == s->hw->extension) { u_char result[4]; u_char *buf; size_t len; params[0] = ESC; params[1] = s->hw->cmd->request_extended_status; send (s, params, 2, &status); /* send ESC f (request extended status) */ if (SANE_STATUS_GOOD == status) { len = 4; /* receive header */ receive (s, result, len, &status); if (SANE_STATUS_GOOD != status) return status; len = result[3] << 8 | result[2]; buf = alloca (len); receive (s, buf, len, &status); /* receive actual status data */ if (buf[0] & 0x80) { close_scanner (s); return SANE_STATUS_INVAL; } } else { DBG (1, "Extended status flag request failed\n"); } } /* * for debug purpose * check scanner conditions */ #if 1 if (s->hw->cmd->request_condition != 0) { u_char result[4]; u_char *buf; size_t len; params[0] = ESC; params[1] = s->hw->cmd->request_condition; send (s, params, 2, &status); /* send request condition */ if (SANE_STATUS_GOOD != status) return status; len = 4; receive (s, result, len, &status); if (SANE_STATUS_GOOD != status) return status; len = result[3] << 8 | result[2]; buf = alloca (len); receive (s, buf, len, &status); if (SANE_STATUS_GOOD != status) return status; #if 0 DBG (10, "SANE_START: length=%d\n", len); for (i = 1; i <= len; i++) { DBG (10, "SANE_START: %d: %c\n", i, buf[i - 1]); } #endif DBG (5, "SANE_START: Color: %d\n", (int) buf[1]); DBG (5, "SANE_START: Resolution (x, y): (%d, %d)\n", (int) (buf[4] << 8 | buf[3]), (int) (buf[6] << 8 | buf[5])); DBG (5, "SANE_START: Scan area(pixels) (x0, y0), (x1, y1): (%d, %d), (%d, %d)\n", (int) (buf[9] << 8 | buf[8]), (int) (buf[11] << 8 | buf[10]), (int) (buf[13] << 8 | buf[12]), (int) (buf[15] << 8 | buf[14])); DBG (5, "SANE_START: Data format: %d\n", (int) buf[17]); DBG (5, "SANE_START: Halftone: %d\n", (int) buf[19]); DBG (5, "SANE_START: Brightness: %d\n", (int) buf[21]); DBG (5, "SANE_START: Gamma: %d\n", (int) buf[23]); DBG (5, "SANE_START: Zoom (x, y): (%d, %d)\n", (int) buf[26], (int) buf[25]); DBG (5, "SANE_START: Color correction: %d\n", (int) buf[28]); DBG (5, "SANE_START: Sharpness control: %d\n", (int) buf[30]); DBG (5, "SANE_START: Scanning mode: %d\n", (int) buf[32]); DBG (5, "SANE_START: Mirroring: %d\n", (int) buf[34]); DBG (5, "SANE_START: Auto area segmentation: %d\n", (int) buf[36]); DBG (5, "SANE_START: Threshold: %d\n", (int) buf[38]); DBG (5, "SANE_START: Line counter: %d\n", (int) buf[40]); DBG (5, "SANE_START: Option unit control: %d\n", (int) buf[42]); DBG (5, "SANE_START: Film type: %d\n", (int) buf[44]); } #endif /* set the retry count to 0 */ s->retry_count = 0; if (s->hw->color_shuffle == SANE_TRUE) { /* initialize the line buffers */ for (i = 0; i < s->line_distance * 2 + 1; i++) { if (s->line_buffer[i] != NULL) free (s->line_buffer[i]); s->line_buffer[i] = malloc (s->params.bytes_per_line); if (s->line_buffer[i] == NULL) { /* free the memory we've malloced so far */ for (j = 0; j < i; j++) { free (s->line_buffer[j]); s->line_buffer[j] = NULL; } DBG (1, "out of memory (line %d)\n", __LINE__); return SANE_STATUS_NO_MEM; } } } params[0] = ESC; params[1] = s->hw->cmd->start_scanning; send (s, params, 2, &status); if (SANE_STATUS_GOOD != status) { DBG (1, "sane_start: start failed: %s\n", sane_strstatus (status)); return status; } s->eof = SANE_FALSE; s->buf = realloc (s->buf, lcount * s->params.bytes_per_line); s->ptr = s->end = s->buf; s->canceling = SANE_FALSE; return SANE_STATUS_GOOD; } /* sane_start */ /* * * TODO: clean up the eject and direct cmd mess. */ SANE_Status sane_auto_eject (Epson_Scanner * s) { DBG (5, "sane_auto_eject()\n"); if (s->hw->ADF && s->hw->use_extension && s->val[OPT_AUTO_EJECT].w) { /* sequence! */ SANE_Status status; u_char params[1]; u_char cmd = s->hw->cmd->eject; if (!cmd) return SANE_STATUS_UNSUPPORTED; params[0] = cmd; send (s, params, 1, &status); if (SANE_STATUS_GOOD != (status = expect_ack (s))) { return status; } } return SANE_STATUS_GOOD; } /* * * */ static SANE_Status read_data_block (Epson_Scanner * s, EpsonDataRec * result) { SANE_Status status; u_char param[3]; receive (s, result, s->block ? 6 : 4, &status); if (SANE_STATUS_GOOD != status) return status; if (STX != result->code) { DBG (1, "code %02x\n", (int) result->code); DBG (1, "error, expected STX\n"); return SANE_STATUS_INVAL; } if (result->status & STATUS_FER) { int dummy_x, dummy_y; DBG (1, "fatal error - Status = %02x\n", result->status); status = check_ext_status (s, &dummy_x, &dummy_y); /* * Hack Alert!!! * If the status is SANE_STATUS_DEVICE_BUSY then we need to * re-issue the command again. We can assume that the command that * caused this problem was ESC G, so in a loop with a sleep 1 we * are testing this over and over and over again, until the lamp * "thinks" it is ready. * * TODO: Store the last command and execute what was actually used * as the last command. For all situations this error may occur * ESC G is very very likely to be the command in question, but * we better make sure that this is the case. * */ /* * let's safe some stack space: If this is not the first go around, * then just return the status and let the loop handle this - otherwise * we would run this function recursively. */ if ((status == SANE_STATUS_DEVICE_BUSY && s->retry_count > 0) || (status == SANE_STATUS_GOOD && s->retry_count > 0)) { return SANE_STATUS_DEVICE_BUSY; /* return busy even if we just read OK so that the following loop can end gracefully */ } while (status == SANE_STATUS_DEVICE_BUSY) { if (s->retry_count > SANE_EPSON_MAX_RETRIES) { DBG (1, "Max retry count exceeded (%d)\n", s->retry_count); return SANE_STATUS_INVAL; } sleep (1); /* wait one second for the next attempt */ DBG (1, "retrying ESC G - %d\n", ++(s->retry_count)); param[0] = ESC; param[1] = s->hw->cmd->start_scanning; send (s, param, 2, &status); if (SANE_STATUS_GOOD != status) { DBG (1, "read_data_block: start failed: %s\n", sane_strstatus (status)); return status; } status = read_data_block (s, result); } } return status; } /* * * */ void scan_finish (Epson_Scanner * s) { SANE_Status status; int i, x, y; DBG (5, "scan_finish()\n"); free (s->buf); s->buf = NULL; status = check_ext_status (s, &x, &y); if (SANE_STATUS_NO_DOCS == status && s->hw->ADF && s->hw->use_extension) sane_auto_eject (s); for (i = 0; i < s->line_distance; i++) { if (s->line_buffer[i] != NULL) { free (s->line_buffer[i]); s->line_buffer[i] = NULL; } } } #define GET_COLOR(x) ((x.status>>2) & 0x03) SANE_Status sane_read (SANE_Handle handle, SANE_Byte * data, SANE_Int max_length, SANE_Int * length) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Status status; int index = 0; SANE_Bool reorder = SANE_FALSE; SANE_Bool needStrangeReorder = SANE_FALSE; START_READ: DBG (5, "sane_read: begin\n"); if (s->ptr == s->end) { EpsonDataRec result; size_t buf_len; if ((s->fd != -1) && s->eof) { if (s->hw->color_shuffle) { DBG (1, "Written %d lines after color shuffle\n", s->lines_written); DBG (1, "Lines requested: %d\n", s->params.lines); } *length = 0; scan_finish (s); return SANE_STATUS_EOF; } DBG (5, "sane_read: begin scan1\n"); if (SANE_STATUS_GOOD != (status = read_data_block (s, &result))) { *length = 0; scan_finish (s); return status; } buf_len = result.buf[1] << 8 | result.buf[0]; DBG (5, "sane_read: buf len = %lu\n", (u_long) buf_len); if (s->block) { buf_len *= (result.buf[3] << 8 | result.buf[2]); DBG (5, "sane_read: buf len (adjusted) = %lu\n", (u_long) buf_len); } if (!s->block && SANE_FRAME_RGB == s->params.format) { /* * Read color data in line mode */ /* * read the first color line - the number of bytes to read * is already known (from last call to read_data_block() * We determine where to write the line from the color information * in the data block. At the end we want the order RGB, but the * way the data is delivered does not guarantee this - actually it's * most likely that the order is GRB if it's not RGB! */ switch (GET_COLOR (result)) { case 1: index = 1; break; case 2: index = 0; break; case 3: index = 2; break; } receive (s, s->buf + index * s->params.pixels_per_line, buf_len, &status); if (SANE_STATUS_GOOD != status) return status; /* * send the ACK signal to the scanner in order to make * it ready for the next data block. */ send (s, S_ACK, 1, &status); /* * ... and request the next data block */ if (SANE_STATUS_GOOD != (status = read_data_block (s, &result))) return status; buf_len = result.buf[1] << 8 | result.buf[0]; /* * this should never happen, because we are already in * line mode, but it does not hurt to check ... */ if (s->block) buf_len *= (result.buf[3] << 8 | result.buf[2]); DBG (5, "sane_read: buf len2 = %lu\n", (u_long) buf_len); switch (GET_COLOR (result)) { case 1: index = 1; break; case 2: index = 0; break; case 3: index = 2; break; } receive (s, s->buf + index * s->params.pixels_per_line, buf_len, &status); if (SANE_STATUS_GOOD != status) { scan_finish (s); *length = 0; return status; } send (s, S_ACK, 1, &status); /* * ... and the last data block */ if (SANE_STATUS_GOOD != (status = read_data_block (s, &result))) { *length = 0; scan_finish (s); return status; } buf_len = result.buf[1] << 8 | result.buf[0]; if (s->block) buf_len *= (result.buf[3] << 8 | result.buf[2]); DBG (5, "sane_read: buf len3 = %lu\n", (u_long) buf_len); switch (GET_COLOR (result)) { case 1: index = 1; break; case 2: index = 0; break; case 3: index = 2; break; } receive (s, s->buf + index * s->params.pixels_per_line, buf_len, &status); if (SANE_STATUS_GOOD != status) { *length = 0; scan_finish (s); return status; } } else { /* * Read data in block mode */ /* do we have to reorder the data ? */ if (GET_COLOR (result) == 0x01) { reorder = SANE_TRUE; } receive (s, s->buf, buf_len, &status); if (SANE_STATUS_GOOD != status) { *length = 0; scan_finish (s); return status; } } if (result.status & STATUS_AREA_END) { s->eof = SANE_TRUE; } else { if (s->canceling) { send (s, S_CAN, 1, &status); expect_ack (s); *length = 0; scan_finish (s); return SANE_STATUS_CANCELLED; } else send (s, S_ACK, 1, &status); } s->end = s->buf + buf_len; s->ptr = s->buf; /* * if we have to re-order the color components (GRB->RGB) we * are doing this here: */ /* * Some scaners (e.g. the Perfection 1640 and GT-2200) seem * to have the R and G channels swapped. * The GT-8700 is the Asian version of the Perfection1640. * If the scanner name is one of these, and the scan mode is * RGB then swap the colors. */ needStrangeReorder = (strstr (s->hw->sane.model, "GT-2200") || ((strstr (s->hw->sane.model, "1640") && strstr (s->hw->sane.model, "Perfection")) || strstr (s->hw->sane.model, "GT-8700"))) && s->params.format == SANE_FRAME_RGB; /* * Certain Perfection 1650 also need this re-ordering of the two * color channels. These scanners are identified by the problem * with the half vertical scanning area. When we corrected this, * we also set the variable s->hw->need_color_reorder */ if (s->hw->need_color_reorder) { needStrangeReorder = SANE_TRUE; } if (needStrangeReorder) reorder = SANE_FALSE; /* reordering once is enough */ if (s->params.format != SANE_FRAME_RGB) reorder = SANE_FALSE; /* don't reorder for BW or gray */ if (reorder) { SANE_Byte *ptr; ptr = s->buf; while (ptr < s->end) { if (s->params.depth > 8) { SANE_Byte tmp; /* R->G G->R */ tmp = ptr[0]; ptr[0] = ptr[2]; /* first Byte G */ ptr[2] = tmp; /* first Byte R */ tmp = ptr[1]; ptr[1] = ptr[3]; /* second Byte G */ ptr[3] = tmp; /* second Byte R */ ptr += 6; /* go to next pixel */ } else { /* R->G G->R */ SANE_Byte tmp; tmp = ptr[0]; ptr[0] = ptr[1]; /* G */ ptr[1] = tmp; /* R */ /* B stays the same */ ptr += 3; /* go to next pixel */ } } } /* * Do the color_shuffle if everything else is correct - at this time * most of the stuff is hardcoded for the Perfection 610 */ if (s->hw->color_shuffle) { int new_length = 0; status = color_shuffle (s, &new_length); /* * If no bytes are returned, check if the scanner is already done, if so, * we'll probably just return, but if there is more data to process get * the next batch. */ if (new_length == 0 && s->end != s->ptr) { goto START_READ; } s->end = s->buf + new_length; s->ptr = s->buf; } DBG (5, "sane_read: begin scan2\n"); } /* * copy the image data to the data memory area */ if (!s->block && SANE_FRAME_RGB == s->params.format) { max_length /= 3; if (max_length > s->end - s->ptr) max_length = s->end - s->ptr; *length = 3 * max_length; if (s->invert_image == SANE_TRUE) { while (max_length-- != 0) { /* invert the three values */ *data++ = (u_char) ~ (s->ptr[0]); *data++ = (u_char) ~ (s->ptr[s->params.pixels_per_line]); *data++ = (u_char) ~ (s->ptr[2 * s->params.pixels_per_line]); ++s->ptr; } } else { while (max_length-- != 0) { *data++ = s->ptr[0]; *data++ = s->ptr[s->params.pixels_per_line]; *data++ = s->ptr[2 * s->params.pixels_per_line]; ++s->ptr; } } } else { if (max_length > s->end - s->ptr) max_length = s->end - s->ptr; *length = max_length; if (1 == s->params.depth) { if (s->invert_image == SANE_TRUE) { while (max_length-- != 0) *data++ = *s->ptr++; } else { while (max_length-- != 0) *data++ = ~*s->ptr++; } } else { if (s->invert_image == SANE_TRUE) { int i; for (i = 0; i < max_length; i++) { data[i] = (u_char) ~ (s->ptr[i]); } } else { memcpy (data, s->ptr, max_length); } s->ptr += max_length; } } DBG (5, "sane_read: end\n"); return SANE_STATUS_GOOD; } static SANE_Status color_shuffle (SANE_Handle handle, int *new_length) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Byte *buf = s->buf; int length = s->end - s->buf; if (s->hw->color_shuffle == SANE_TRUE) { SANE_Byte *data_ptr; /* ptr to data to process */ SANE_Byte *data_end; /* ptr to end of processed data */ SANE_Byte *out_data_ptr; /* ptr to memory when writing data */ int i; /* loop counter */ /* * It looks like we are dealing with a scanner that has an odd way * of dealing with colors... The red and blue scan lines are shifted * up or down by a certain number of lines relative to the green line. */ DBG (5, "sane_read: color_shuffle\n"); /* * Initialize the variables we are going to use for the * copying of the data. data_ptr is the pointer to * the currently worked on scan line. data_end is the * end of the data area as calculated from adding *length * to the start of data. * out_data_ptr is used when writing out the processed data * and always points to the beginning of the next line to * write. */ data_ptr = out_data_ptr = buf; data_end = data_ptr + length; /* * The image data is in *buf, we know that the buffer contains s->end - s->buf ( = length) * bytes of data. The width of one line is in s->params.bytes_per_line */ /* * The buffer area is supposed to have a number of full scan * lines, let's test if this is the case. */ if (length % s->params.bytes_per_line != 0) { DBG (1, "ERROR in size of buffer: %d / %d\n", length, s->params.bytes_per_line); return SANE_STATUS_INVAL; } while (data_ptr < data_end) { SANE_Byte *source_ptr, *dest_ptr; int loop; /* copy the green information into the current line */ source_ptr = data_ptr + 1; dest_ptr = s->line_buffer[s->color_shuffle_line] + 1; for (i = 0; i < s->params.bytes_per_line / 3; i++) { *dest_ptr = *source_ptr; dest_ptr += 3; source_ptr += 3; } /* copy the red information n lines back */ if (s->color_shuffle_line >= s->line_distance) { source_ptr = data_ptr + 2; dest_ptr = s->line_buffer[s->color_shuffle_line - s->line_distance] + 2; /* while (source_ptr < s->line_buffer[s->color_shuffle_line] + s->params.bytes_per_line) */ for (loop = 0; loop < s->params.bytes_per_line / 3; loop++) { *dest_ptr = *source_ptr; dest_ptr += 3; source_ptr += 3; } } /* copy the blue information n lines forward */ source_ptr = data_ptr; dest_ptr = s->line_buffer[s->color_shuffle_line + s->line_distance]; /* while (source_ptr < s->line_buffer[s->color_shuffle_line] + s->params.bytes_per_line) */ for (loop = 0; loop < s->params.bytes_per_line / 3; loop++) { *dest_ptr = *source_ptr; dest_ptr += 3; source_ptr += 3; } data_ptr += s->params.bytes_per_line; if (s->color_shuffle_line == s->line_distance) { /* * we just finished the line in line_buffer[0] - write it to the * output buffer and continue. */ /* * The output buffer is still "buf", but because we are * only overwriting from the beginning of the memory area * we are not interfering with the "still to shuffle" data * in the same area. */ /* * Strip the first and last n lines and limit to */ if ((s->current_output_line >= s->line_distance) && (s->current_output_line < s->params.lines + s->line_distance)) { memcpy (out_data_ptr, s->line_buffer[0], s->params.bytes_per_line); out_data_ptr += s->params.bytes_per_line; s->lines_written++; } s->current_output_line++; /* * Now remove the 0-entry and move all other * lines up by one. There are 2*line_distance + 1 * buffers, * therefore the loop has to run from 0 * to * 2*line_distance, and because we want to * copy every n+1st entry to n the loop runs * from - to 2*line_distance-1! */ free (s->line_buffer[0]); for (i = 0; i < s->line_distance * 2; i++) { s->line_buffer[i] = s->line_buffer[i + 1]; } /* * and create one new buffer at the end */ s->line_buffer[s->line_distance * 2] = malloc (s->params.bytes_per_line); if (s->line_buffer[s->line_distance * 2] == NULL) { int i; for (i = 0; i < s->line_distance * 2; i++) { free (s->line_buffer[i]); s->line_buffer[i] = NULL; } DBG (1, "out of memory (line %d)\n", __LINE__); return SANE_STATUS_NO_MEM; } } else { s->color_shuffle_line++; /* increase the buffer number */ } } /* * At this time we've used up all the new data from the scanner, some of * it is still in the line_buffers, but we are ready to return some of it * to the front end software. To do so we have to adjust the size of the * data area and the *new_length variable. */ *new_length = out_data_ptr - buf; } return SANE_STATUS_GOOD; } /* * static SANE_Status get_identity_information ( SANE_Handle handle) * * Request Identity information from scanner and fill in information * into dev and/or scanner structures. */ static SANE_Status get_identity_information (SANE_Handle handle) { Epson_Scanner *s = (Epson_Scanner *) handle; Epson_Device *dev = s->hw; EpsonIdent ident; u_char param[3]; SANE_Status status; u_char *buf; DBG (5, "get_identity_information()\n"); if (!s->hw->cmd->request_identity) return SANE_STATUS_INVAL; param[0] = ESC; param[1] = s->hw->cmd->request_identity; param[2] = '\0'; if (NULL == (ident = (EpsonIdent) command (s, param, 2, &status))) { DBG (1, "ident failed\n"); return SANE_STATUS_INVAL; } DBG (1, "type %3c 0x%02x\n", ident->type, ident->type); DBG (1, "level %3c 0x%02x\n", ident->level, ident->level); { char *force = getenv ("SANE_EPSON_CMD_LVL"); if (force) { ident->type = force[0]; ident->level = force[1]; DBG (1, "type %3c 0x%02x\n", ident->type, ident->type); DBG (1, "level %3c 0x%02x\n", ident->level, ident->level); DBG (1, "forced\n"); } } /* * check if option equipment is installed. */ if (ident->status & STATUS_OPTION) { DBG (1, "option equipment is installed\n"); dev->extension = SANE_TRUE; } else { DBG (1, "no option equipment installed\n"); dev->extension = SANE_FALSE; } dev->TPU = SANE_FALSE; dev->ADF = SANE_FALSE; /* * set command type and level. */ { int n; for (n = 0; n < NELEMS (epson_cmd); n++) { char type_level[3]; sprintf(type_level, "%c%c", ident->type, ident->level); if (!strncmp (type_level, epson_cmd[n].level, 2)) break; } if (n < NELEMS (epson_cmd)) { dev->cmd = &epson_cmd[n]; } else { dev->cmd = &epson_cmd[EPSON_LEVEL_DEFAULT]; DBG (1, "Unknown type %c or level %c, using %s\n", ident->type, ident->level, dev->cmd->level); } s->hw->level = dev->cmd->level[1] - '0'; } /* set command type and level */ /* * Setting available resolutions and xy ranges for sane frontend. */ s->hw->res_list_size = 0; s->hw->res_list = (SANE_Int *) calloc (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; } { int n, k; int x = 0, y = 0; int count = ident->count2 * 255 + ident->count1; /* 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) { case 'R': { int val = buf[2] << 8 | buf[1]; 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; DBG (1, "resolution (dpi): %d\n", val); k = 3; continue; } case 'A': { x = buf[2] << 8 | buf[1]; y = buf[4] << 8 | buf[3]; 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 physically supports 4800 dpi. * This is simulated here... * Further 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: break; } /* case */ break; } /* for */ dev->dpi_range.min = s->hw->res_list[0]; dev->dpi_range.max = s->hw->res_list[s->hw->res_list_size - 1]; dev->dpi_range.quant = 0; dev->fbf_x_range.min = 0; dev->fbf_x_range.max = SANE_FIX (x * 25.4 / dev->dpi_range.max); dev->fbf_x_range.quant = 0; dev->fbf_y_range.min = 0; dev->fbf_y_range.max = SANE_FIX (y * 25.4 / dev->dpi_range.max); dev->fbf_y_range.quant = 0; DBG (5, "fbf tlx %f tly %f brx %f bry %f [mm]\n", SANE_UNFIX (dev->fbf_x_range.min), SANE_UNFIX (dev->fbf_y_range.min), SANE_UNFIX (dev->fbf_x_range.max), SANE_UNFIX (dev->fbf_y_range.max)); } /* * Copy the resolution list to the resolution_list array so that the frontend can * display the correct values */ s->hw->resolution_list = malloc ((s->hw->res_list_size + 1) * sizeof (SANE_Word)); if (s->hw->resolution_list == NULL) { DBG (1, "out of memory (line %d)\n", __LINE__); return SANE_STATUS_NO_MEM; } *(s->hw->resolution_list) = s->hw->res_list_size; memcpy (&(s->hw->resolution_list[1]), s->hw->res_list, s->hw->res_list_size * sizeof (SANE_Word)); /* filter the resolution list */ /* the option is not yet initialized, for now just set it to false */ s->val[OPT_LIMIT_RESOLUTION].w = SANE_FALSE; filter_resolution_list (s); return SANE_STATUS_GOOD; } /* request identity */ /* * static SANE_Status get_identity2_information ( SANE_Handle handle) * * Request Identity2 information from scanner and fill in information * into dev and/or scanner structures. */ static SANE_Status get_identity2_information (SANE_Handle handle) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Status status; int len; u_char param[3]; u_char result[4]; u_char *buf; DBG (5, "get_identity2_information()\n"); if (s->hw->cmd->request_identity2 == 0) return SANE_STATUS_UNSUPPORTED; param[0] = ESC; param[1] = s->hw->cmd->request_identity2; param[2] = '\0'; send (s, param, 2, &status); if (SANE_STATUS_GOOD != status) return status; len = 4; /* receive header */ receive (s, result, len, &status); if (SANE_STATUS_GOOD != status) return status; len = result[3] << 8 | result[2]; buf = alloca (len); receive (s, buf, len, &status); /* receive actual status data */ /* the first two bytes of the buffer contain the optical resolution */ s->hw->optical_res = buf[1] << 8 | buf[0]; /* * the 4th and 5th byte contain the line distance. Both values have to * be identical, otherwise this software can not handle this scanner. */ if (buf[4] != buf[5]) { close_scanner (s); return SANE_STATUS_INVAL; } s->hw->max_line_distance = buf[4]; return SANE_STATUS_GOOD; } /* * void sane_cancel(SANE_Handle handle) * * Set the cancel flag to true. The next time the backend requests data * from the scanner the CAN message will be sent. */ void sane_cancel (SANE_Handle handle) { Epson_Scanner *s = (Epson_Scanner *) handle; /* * If the s->ptr pointer is not NULL, then a scan operation * was started and if s->eof is FALSE, it was not finished. */ if (s->buf != NULL) { u_char *dummy; int len; /* malloc one line */ dummy = malloc (s->params.bytes_per_line); if (dummy == NULL) { DBG (1, "Out of memory\n"); return; } else { /* there is still data to read from the scanner */ s->canceling = SANE_TRUE; while (!s->eof && SANE_STATUS_CANCELLED != sane_read (s, dummy, s->params.bytes_per_line, &len)) { /* empty body, the while condition does the processing */ } free (dummy); } } } static SANE_Status request_focus_position (SANE_Handle handle, u_char * position) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Status status; int len; u_char param[3]; u_char result[4]; u_char *buf; DBG (5, "request_focus_position()\n"); if (s->hw->cmd->request_focus_position == 0) return SANE_STATUS_UNSUPPORTED; param[0] = ESC; param[1] = s->hw->cmd->request_focus_position; param[2] = '\0'; send (s, param, 2, &status); if (SANE_STATUS_GOOD != status) return status; len = 4; /* receive header */ receive (s, result, len, &status); if (SANE_STATUS_GOOD != status) return status; len = result[3] << 8 | result[2]; buf = alloca (len); receive (s, buf, len, &status); /* receive actual status data */ *position = buf[1]; DBG (1, "Focus position = 0x%x\n", buf[1]); return SANE_STATUS_GOOD; } /* * Request the push button status * returns SANE_TRUE if the button was pressed * and SANE_FALSE if the button was not pressed * it also returns SANE_TRUE in case of an error. * This is necessary so that a process that waits for * the button does not block indefinitely. */ static SANE_Bool request_push_button_status (SANE_Handle handle, SANE_Bool * theButtonStatus) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Status status; int len; u_char param[3]; u_char result[4]; u_char *buf; DBG (5, "request_push_button_status()\n"); if (s->hw->cmd->request_push_button_status == 0) { DBG (1, "push button status unsupported\n"); return SANE_STATUS_UNSUPPORTED; } param[0] = ESC; param[1] = s->hw->cmd->request_push_button_status; param[2] = '\0'; send (s, param, 2, &status); if (SANE_STATUS_GOOD != status) { DBG (1, "error sending command\n"); return status; } len = 4; /* receive header */ receive (s, result, len, &status); if (SANE_STATUS_GOOD != status) return status; len = result[3] << 8 | result[2]; /* this should be 1 for scanners with one button */ buf = alloca (len); receive (s, buf, len, &status); /* receive actual status data */ DBG (1, "Push button status = %d\n", buf[0] & 0x01); *theButtonStatus = ((buf[0] & 0x01) != 0); return (SANE_STATUS_GOOD); } static void filter_resolution_list (Epson_Scanner * s) { /* re-create the list */ if (s->val[OPT_LIMIT_RESOLUTION].w == SANE_TRUE) { /* copy the short list */ /* filter out all values that are not 300 or 400 dpi based */ int i; int new_size = 0; SANE_Bool is_correct_resolution = SANE_FALSE; for (i = 1; i <= s->hw->res_list_size; i++) { SANE_Word res; res = s->hw->res_list[i]; if ((res < 100) || (0 == (res % 300)) || (0 == (res % 400))) { /* add the value */ new_size++; s->hw->resolution_list[new_size] = s->hw->res_list[i]; /* check for a valid current resolution */ if (res == s->val[OPT_RESOLUTION].w) { is_correct_resolution = SANE_TRUE; } } } s->hw->resolution_list[0] = new_size; if (is_correct_resolution == SANE_FALSE) { for (i = 1; i <= new_size; i++) { if (s->val[OPT_RESOLUTION].w < s->hw->resolution_list[i]) { s->val[OPT_RESOLUTION].w = s->hw->resolution_list[i]; i = new_size + 1; } } } } else { /* copy the full list */ s->hw->resolution_list[0] = s->hw->res_list_size; memcpy (&(s->hw->resolution_list[1]), s->hw->res_list, s->hw->res_list_size * sizeof (SANE_Word)); } } /**********************************************************************************/ /* * SANE_Status sane_set_io_mode() * * not supported - for asynchronous I/O */ SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { /* get rid of compiler warning */ (void) handle; (void) non_blocking; return SANE_STATUS_UNSUPPORTED; } /* * SANE_Status sane_get_select_fd() * * not supported - for asynchronous I/O */ SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { /* get rid of compiler warnings */ (void) handle; (void) fd; return SANE_STATUS_UNSUPPORTED; } /* vim:ts=2:sw=2:cindent: */ backends-1.3.0/backend/epson.conf.in000066400000000000000000000014301456256263500172700ustar00rootroot00000000000000# 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 backends-1.3.0/backend/epson.h000066400000000000000000000221211456256263500161650ustar00rootroot00000000000000/* epson.h - SANE library for Epson flatbed scanners. based on Kazuhiro Sasayama previous Work on epson.[ch] file from the SANE package. original code taken from sane-0.71 Copyright (C) 1997 Hypercore Software Design, Ltd. modifications Copyright (C) 1998-1999 Christian Bucher Copyright (C) 1998-1999 Kling & Hautzinger GmbH Copyright (C) 1999 Norihiko Sawa Copyright (C) 2000 Karl Heinz Kremer 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, see . 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 epson_h #define epson_h #include #include /* some string constants that are used in the config file */ #define SANE_EPSON_CONFIG_USB "usb" #define SANE_EPSON_CONFIG_PIO "pio" /* string constants for GUI elements that are not defined SANE-wide */ #define SANE_NAME_GAMMA_CORRECTION "gamma-correction" #define SANE_TITLE_GAMMA_CORRECTION SANE_I18N("Gamma Correction") #define SANE_DESC_GAMMA_CORRECTION SANE_I18N("Selects the gamma correction value from a list of pre-defined devices or the user defined table, which can be downloaded to the scanner") #define SANE_EPSON_FOCUS_NAME "focus-position" #define SANE_EPSON_FOCUS_TITLE SANE_I18N("Focus Position") #define SANE_EPSON_FOCUS_DESC SANE_I18N("Sets the focus position to either the glass or 2.5mm above the glass") #define SANE_EPSON_WAIT_FOR_BUTTON_NAME "wait-for-button" #define SANE_EPSON_WAIT_FOR_BUTTON_TITLE SANE_I18N("Wait for Button") #define SANE_EPSON_WAIT_FOR_BUTTON_DESC SANE_I18N("After sending the scan command, wait until the button on the scanner is pressed to actually start the scan process."); #define LINES_SHUFFLE_MAX (17) /* 2 x 8 lines plus 1 */ #define SANE_EPSON_MAX_RETRIES (120) /* how often do we retry during warmup ? */ typedef struct { char *level; unsigned char request_identity; unsigned char request_identity2; /* new request identity command for Dx command level */ unsigned char request_status; unsigned char request_condition; unsigned char set_color_mode; unsigned char start_scanning; unsigned char set_data_format; unsigned char set_resolution; unsigned char set_zoom; unsigned char set_scan_area; unsigned char set_bright; SANE_Range bright_range; unsigned char set_gamma; unsigned char set_halftoning; unsigned char set_color_correction; unsigned char initialize_scanner; unsigned char set_speed; /* B4 and later */ unsigned char set_lcount; unsigned char mirror_image; /* B5 and later */ unsigned char set_gamma_table; /* B4 and later */ unsigned char set_outline_emphasis; /* B4 and later */ unsigned char set_dither; /* B4 and later */ unsigned char set_color_correction_coefficients; /* B3 and later */ unsigned char request_extended_status; /* get extended status from scanner */ unsigned char control_an_extension; /* for extension control */ unsigned char eject; /* for extension control */ unsigned char feed; unsigned char request_push_button_status; unsigned char control_auto_area_segmentation; unsigned char set_film_type; /* for extension control */ unsigned char set_exposure_time; /* F5 only */ unsigned char set_bay; /* F5 only */ unsigned char set_threshold; unsigned char set_focus_position; /* B8 only */ unsigned char request_focus_position; /* B8 only */ } EpsonCmdRec, *EpsonCmd; enum { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_MODE, OPT_BIT_DEPTH, OPT_HALFTONE, OPT_DROPOUT, OPT_BRIGHTNESS, OPT_SHARPNESS, OPT_GAMMA_CORRECTION, OPT_COLOR_CORRECTION, OPT_RESOLUTION, OPT_THRESHOLD, OPT_ADVANCED_GROUP, OPT_MIRROR, OPT_SPEED, OPT_AAS, OPT_LIMIT_RESOLUTION, OPT_ZOOM, /* OPT_GAMMA_VECTOR */ OPT_GAMMA_VECTOR_R, OPT_GAMMA_VECTOR_G, OPT_GAMMA_VECTOR_B, OPT_WAIT_FOR_BUTTON, OPT_CCT_GROUP, OPT_CCT_1, OPT_CCT_2, OPT_CCT_3, OPT_CCT_4, OPT_CCT_5, OPT_CCT_6, OPT_CCT_7, OPT_CCT_8, OPT_CCT_9, OPT_PREVIEW_GROUP, OPT_PREVIEW, OPT_PREVIEW_SPEED, OPT_GEOMETRY_GROUP, OPT_TL_X, OPT_TL_Y, OPT_BR_X, OPT_BR_Y, OPT_QUICK_FORMAT, OPT_EQU_GROUP, OPT_SOURCE, OPT_AUTO_EJECT, OPT_FILM_TYPE, OPT_FOCUS, OPT_BAY, OPT_EJECT, OPT_ADF_MODE, NUM_OPTIONS }; typedef enum { /* hardware connection to the scanner */ SANE_EPSON_NODEV, /* default, no HW specified yet */ SANE_EPSON_SCSI, /* SCSI interface */ SANE_EPSON_PIO, /* parallel interface */ SANE_EPSON_USB /* USB interface */ } Epson_Connection_Type; typedef struct { u_short opt_resolution; u_char sensor; u_char scan_order; u_char line_dist1; u_char line_dist2; u_short main_res1; u_short main_res2; u_short main_res3; u_short main_res4; u_short main_res5; u_short main_res6; u_short main_res7; u_short sub_res1; u_short sub_res2; u_short sub_res3; u_short sub_res4; u_short sub_res5; u_short sub_res6; } Epson_Identity2; struct Epson_Device { struct Epson_Device *next; SANE_Device sane; SANE_Int level; SANE_Range dpi_range; SANE_Range *x_range; /* x range w/out extension */ SANE_Range *y_range; /* y range w/out extension */ SANE_Range fbf_x_range; /* flattbed x range */ SANE_Range fbf_y_range; /* flattbed y range */ SANE_Range adf_x_range; /* autom. document feeder x range */ SANE_Range adf_y_range; /* autom. document feeder y range */ SANE_Range tpu_x_range; /* transparency unit x range */ SANE_Range tpu_y_range; /* transparency unit y range */ Epson_Connection_Type connection; /* hardware interface type */ SANE_Int *res_list; /* list of resolutions */ SANE_Int res_list_size; /* number of entries in this list */ SANE_Int last_res; /* last selected resolution */ SANE_Int last_res_preview; /* last selected preview resolution */ SANE_Word *resolution_list; /* for display purposes we store a second copy */ SANE_Bool extension; /* extension is installed */ SANE_Int use_extension; /* use the installed extension */ SANE_Bool TPU; /* TPU is installed */ SANE_Bool ADF; /* ADF is installed */ SANE_Bool duplexSupport; /* does the ADF handle duplex scanning */ SANE_Bool focusSupport; /* does this scanner have support for "set focus position" ? */ SANE_Bool color_shuffle; /* does this scanner need color shuffling */ SANE_Int maxDepth; /* max. color depth */ SANE_Int optical_res; /* optical resolution */ SANE_Int max_line_distance; SANE_Bool need_double_vertical; SANE_Bool need_color_reorder; SANE_Bool need_reset_on_source_change; SANE_Bool wait_for_button; /* do we have to wait until the scanner button is pressed? */ SANE_Int fbf_max_x; SANE_Int fbf_max_y; SANE_Int adf_max_x; SANE_Int adf_max_y; SANE_Int devtype; EpsonCmd cmd; }; typedef struct Epson_Device Epson_Device; struct Epson_Scanner { struct Epson_Scanner *next; int fd; Epson_Device *hw; SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; SANE_Parameters params; SANE_Bool block; SANE_Bool eof; SANE_Byte *buf, *end, *ptr; SANE_Bool canceling; SANE_Bool invert_image; SANE_Bool focusOnGlass; SANE_Byte currentFocusPosition; /* SANE_Word gamma_table [ 4] [ 256]; */ SANE_Word gamma_table[3][256]; SANE_Int retry_count; SANE_Byte *line_buffer[LINES_SHUFFLE_MAX]; /* buffer lines for color shuffling */ SANE_Int color_shuffle_line; /* current line number for color shuffling */ SANE_Int line_distance; /* current line distance */ SANE_Int current_output_line; /* line counter when color shuffling */ SANE_Int lines_written; /* debug variable */ SANE_Bool option_has_changed; /* did one of the options change it's value? */ }; typedef struct Epson_Scanner Epson_Scanner; #endif /* not epson_h */ backends-1.3.0/backend/epson2-cct.c000066400000000000000000001065421456256263500170230ustar00rootroot00000000000000/* epson2 hardware colour correction coefficients * Copyright (C) 2001-2009 SEIKO EPSON Corporation * Copyright (C) 2009 Tower Technologies * * License: GPLv2 * * This file is part of the SANE epson2 backend and has been derived * from the epkowa backend distributed with Image Scan! * */ /*! Hardware colour correction coefficients (CCC). Each entry starts with a unique identifier, followed by four CCC profiles; the first is for reflective materials, the second for colour negatives, the third for monochrome negatives, and the fourth and last one is for colour positives. */ #define DEBUG_DECLARE_ONLY #include "epson2.h" const struct epson_profile epson_cct_profiles[] = { {0x00, /* default */ {{1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x05, /* ES-6000H */ {{1.1419,-0.0596,-0.0825,-0.1234, 1.2812,-0.1413, 0.0703,-0.5720, 1.5016}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.1419,-0.0596,-0.0825,-0.1234, 1.2812,-0.1413, 0.0703,-0.5720, 1.5016}}}, {0x06, /* GT-6600 */ {{1.1442,-0.0705,-0.0737,-0.0702, 1.1013,-0.0311,-0.0080,-0.3588, 1.3668}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x07, /* GT-7600 */ {{1.1967,-0.1379,-0.0588,-0.0538, 1.0385, 0.0153, 0.0348,-0.4070, 1.3721}, {1.0010,-0.0010, 0.0000,-0.1120, 1.1710,-0.0590, 0.0000,-0.0910, 1.0920}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.1967,-0.1379,-0.0588,-0.0538, 1.0385, 0.0153, 0.0348,-0.4070, 1.3721}}}, {0x0D, /* ES-2000 */ {{1.1980,-0.1365,-0.0616,-0.1530, 1.1729,-0.0198,-0.0025,-0.2776, 1.2801}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.1980,-0.1365,-0.0616,-0.1530, 1.1729,-0.0198,-0.0025,-0.2776, 1.2801}}}, {0x0F, /* ES-8500 */ {{1.0961,-0.0181,-0.0779,-0.1279, 1.1957,-0.0678, 0.0315,-0.3891, 1.3576}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0961,-0.0181,-0.0779,-0.1279, 1.1957,-0.0678, 0.0315,-0.3891, 1.3576}}}, {0x15, /* GT-6700 */ {{1.0999,-0.0425,-0.0574,-0.0806, 1.0835,-0.0028, 0.0057,-0.2924, 1.2866}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x16, /* GT-8700 */ {{1.2020,-0.1518,-0.0502,-0.0847, 1.1385,-0.0538, 0.0059,-0.3255, 1.3196}, {1.0030,-0.0030, 0.0000,-0.0980, 1.1500,-0.0520,-0.0030,-0.0840, 1.0880}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.2456,-0.1617,-0.0839,-0.1160, 1.1862,-0.0702,-0.0036,-0.3438, 1.3473}}}, {0x18, /* GT-7700 */ {{1.1339,-0.0526,-0.0813,-0.1177, 1.1661,-0.0485,-0.0030,-0.3298, 1.3328}, {1.0010,-0.0010, 0.0000,-0.1120, 1.1710,-0.0590, 0.0000,-0.0910, 1.0920}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.2066,-0.0360,-0.1706,-0.1313, 1.2523,-0.1210,-0.0299,-0.3377, 1.3676}}}, {0x1A, /* ES-9000H */ {{1.0986, 0.0235,-0.1221,-0.1294, 1.0896, 0.0399, 0.0928,-0.6043, 1.5115}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x1B, /* ES-2200 */ {{1.1855,-0.1372,-0.0483,-0.2060, 1.2468,-0.0407, 0.0358,-0.3059, 1.2701}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.1976,-0.1182,-0.0794,-0.1578, 1.2720,-0.1142, 0.0122,-0.3467, 1.3345}}}, {0x1D, /* GT-7200 */ {{1.0675,-0.0586,-0.0088,-0.0332, 0.9716, 0.0616, 0.0175,-0.4054, 1.3879}, {1.0090,-0.0090, 0.0000,-0.0390, 1.0750,-0.0360,-0.0070,-0.1060, 1.1130}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.1394,-0.0829,-0.0564,-0.0003, 1.0008,-0.0004,-0.0059,-0.3674, 1.3733}}}, {0x1F, /* GT-8200 */ {{1.0800,-0.0607,-0.0193,-0.0787, 1.0846,-0.0059, 0.0135,-0.3334, 1.3199}, {1.0040,-0.0040, 0.0000,-0.0780, 1.1360,-0.0570,-0.0020,-0.0810, 1.0830}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.1334,-0.0929,-0.0405,-0.0418, 1.0689,-0.0271,-0.0521,-0.3262, 1.3783}}}, {0x21, /* GT-9700 */ {{1.0919,-0.0739,-0.0180,-0.0941, 1.1150,-0.0209, 0.0220,-0.3744, 1.3524}, {1.0090,-0.0100, 0.0010,-0.0720, 1.1310,-0.0600, 0.0000,-0.1000, 1.1000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.1374,-0.1396, 0.0021,-0.0489, 1.0655,-0.0166, 0.0081,-0.3492, 1.3411}}}, {0x23, /* GT-7300 */ {{1.0339,-0.0166,-0.0173,-0.0117, 0.9797, 0.0319, 0.0010,-0.3609, 1.3599}, {1.0090,-0.0090, 0.0000,-0.0390, 1.0750,-0.0360,-0.0070,-0.1060, 1.1130}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.1666,-0.0898,-0.0768,-0.0076, 1.0157,-0.0081, 0.0012,-0.3048, 1.3036}}}, {0x25, /* GT-8300 */ {{1.0800,-0.0607,-0.0193,-0.0787, 1.0846,-0.0059, 0.0135,-0.3334, 1.3199}, {1.0040,-0.0040, 0.0000,-0.0780, 1.1360,-0.0570,-0.0020,-0.0810, 1.0830}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.1334,-0.0929,-0.0405,-0.0418, 1.0689,-0.0271,-0.0521,-0.3262, 1.3783}}}, {0x27, /* GT-9300 */ {{1.0919,-0.0739,-0.0180,-0.0941, 1.1150,-0.0209, 0.0220,-0.3744, 1.3524}, {1.0083,-0.0094, 0.0011,-0.0760, 1.1379,-0.0619,-0.0002,-0.0945, 1.0947}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.1952,-0.1519,-0.0433,-0.0932, 1.1613,-0.0681,-0.0418,-0.3140, 1.3558}}}, {0x29, /* GT-9800F */ {{1.0369,-0.0210,-0.0160,-0.0820, 1.1160,-0.0341, 0.0150,-0.5035, 1.4885}, {1.0122,-0.0151, 0.0029,-0.0861, 1.1402,-0.0542,-0.0061,-0.1607, 1.1669}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.1764,-0.1749,-0.0014,-0.0590, 1.0983,-0.0393, 0.0208,-0.5194, 1.4986}}}, {0x2B, /* ES-7000H */ {{1.0305,-0.0116,-0.0189,-0.0936, 1.1245,-0.0309,-0.0072,-0.1413, 1.1485}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x32, /* GT-9400 */ {{1.0932,-0.0529,-0.0403,-0.1077, 1.1416,-0.0338, 0.0079,-0.5525, 1.5446}, {1.0259,-0.0356, 0.0097,-0.1085, 1.2225,-0.1140,-0.0046,-0.1848, 1.1894}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.2720,-0.2665,-0.0054,-0.0672, 1.1301,-0.0629,-0.0048,-0.3917, 1.3965}}}, {0x2D, /* CC-600PX */ {{1.0436,-0.0078,-0.0359,-0.0169, 1.0114, 0.0056, 0.0308,-0.4425, 1.4117}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x3A, /* PM-A850 */ {{1.1150,-0.0677,-0.0473,-0.1179, 1.1681,-0.0502, 0.0052,-0.4858, 1.4806}, {1.0133,-0.0151, 0.0017,-0.1216, 1.2207,-0.0991,-0.0003,-0.1512, 1.1515}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.2105,-0.1644,-0.0461,-0.1124, 1.1945,-0.0820,-0.0450,-0.3367, 1.3817}}}, {0x36, /* CX5300/CX5400 */ {{1.0848,-0.0153,-0.0695,-0.0902, 1.0611, 0.0291, 0.0344,-0.5002, 1.4658}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x34, /* GT-X700 */ {{1.1032,-0.0590,-0.0442,-0.1915, 1.3371,-0.1456, 0.0387,-0.5804, 1.5417}, {1.0232,-0.0258, 0.0026,-0.1296, 1.2882,-0.1587,-0.0011,-0.1928, 1.1940}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.2662,-0.2664, 0.0002,-0.1050, 1.3168,-0.2118,-0.0058,-0.4370, 1.4428}}}, {0x38, /* RX500/RX510 */ {{1.1150,-0.0677,-0.0473,-0.1179, 1.1681,-0.0502, 0.0052,-0.4858, 1.4806}, {1.0133,-0.0151, 0.0017,-0.1216, 1.2207,-0.0991,-0.0003,-0.1512, 1.1515}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.2105,-0.1644,-0.0461,-0.1124, 1.1945,-0.0820,-0.0450,-0.3367, 1.3817}}}, {0x37, /* CX6300/CX6400 */ {{0.9640, 0.1455,-0.1095, 0.0108, 1.1933,-0.2041, 0.0071,-0.3487, 1.3416}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x3F, /* ES-10000G */ {{1.1223,-0.0985,-0.0238,-0.0847, 1.1502,-0.0655, 0.0118,-0.5022, 1.4904}, {1.0077,-0.0129, 0.0052,-0.0904, 1.1785,-0.0881, 0.0000,-0.1528, 1.1528}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.1927,-0.1646,-0.0280,-0.0655, 1.1033,-0.0378, 0.0034,-0.4173, 1.4139}}}, {0x41, /* GT-F500/F550 */ {{1.0732,-0.0581,-0.0150,-0.0897, 1.1553,-0.0657,-0.0179,-0.6500, 1.6679}, {1.0163,-0.0203, 0.0040,-0.1125, 1.1797,-0.0672,-0.0091,-0.2343, 1.2434}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.2437,-0.2022,-0.0415,-0.0352, 1.0735,-0.0383,-0.0188,-0.5020, 1.5209}}}, {0x43, /* GT-F600 */ {{1.0782,-0.0697,-0.0085,-0.1605, 1.2862,-0.1257, 0.0148,-0.5854, 1.5706}, {1.0136,-0.0151, 0.0016,-0.1836, 1.3422,-0.1586,-0.0014,-0.1851, 1.1865}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.1491,-0.1456,-0.0035,-0.0990, 1.2657,-0.1666, 0.0015,-0.3868, 1.3853}}}, {0x46, /* CX3500/CX3600/CX4500/CX4600 */ {{0.9828, 0.0924,-0.0752, 0.0255, 1.1510,-0.1765, 0.0049,-0.3250, 1.3201}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x48, /* PM-A700/RX420/RX430 */ {{0.9716, 0.0927,-0.0643, 0.0010, 1.1068,-0.1078, 0.0101,-0.3046, 1.2945}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x49, /* CX6500/CX6600 */ {{0.9640, 0.1455,-0.1095, 0.0108, 1.1933,-0.2041, 0.0071,-0.3487, 1.3416}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x4B, /* PM-A870 */ {{1.1150,-0.0677,-0.0473,-0.1179, 1.1681,-0.0502, 0.0052,-0.4858, 1.4806}, {1.0133,-0.0151, 0.0017,-0.1216, 1.2207,-0.0991,-0.0003,-0.1512, 1.1515}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.2105,-0.1644,-0.0461,-0.1124, 1.1945,-0.0820,-0.0450,-0.3367, 1.3817}}}, {0x4D, /* PM-A900 */ {{1.1011,-0.0824,-0.0186,-0.0970, 1.1991,-0.1021,-0.0161,-0.6247, 1.6408}, {1.0259,-0.0356, 0.0097,-0.1085, 1.2225,-0.1140,-0.0046,-0.1848, 1.1894}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.2150,-0.2074,-0.0076,-0.0521, 1.1430,-0.0909,-0.0204,-0.4156, 1.4360}}}, {0x4F, /* GT-X800 */ {{1.1052,-0.0850,-0.0202,-0.1050, 1.2294,-0.1245,-0.0486,-0.4160, 1.4646}, {1.0255,-0.0272, 0.0017,-0.0919, 1.2098,-0.1180,-0.0021,-0.1296, 1.1317}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.2950,-0.2619,-0.0332,-0.0562, 1.1587,-0.1025,-0.0397,-0.3100, 1.3497}}}, {0x51, /* LP-A500 */ {{1.0614,-0.0361,-0.0253,-0.1081, 1.1320,-0.0240,-0.0536,-0.2045, 1.2580}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x52, /* GT-F520/F570 */ {{1.0978,-0.0806,-0.0173,-0.0802, 1.1515,-0.0713,-0.0476,-0.4656, 1.5132}, {1.0192,-0.0192, 0.0000,-0.0974, 1.1846,-0.0872,-0.0031,-0.1797, 1.1828}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.2490,-0.2030,-0.0460,-0.0469, 1.1046,-0.0577,-0.0361,-0.3857, 1.4217}}}, {0x54, /* GT-X750 */ {{1.0905,-0.0654,-0.0251,-0.1030, 1.1801,-0.0771,-0.0685,-0.4238, 1.4923}, {1.0206,-0.0207, 0.0000,-0.0890, 1.1770,-0.0880,-0.0014,-0.1450, 1.1464}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.3041,-0.2907,-0.0134,-0.0383, 1.0908,-0.0525,-0.0327,-0.2947, 1.3275}}}, {0x56, /* LP-M5500 */ {{1.0784,-0.0560,-0.0224,-0.1793, 1.2234,-0.0441,-0.0041,-0.2636, 1.2677}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x57, /* Stylus CX3700/CX3800/DX3800 */ {{0.9828, 0.0924,-0.0752, 0.0255, 1.1510,-0.1765, 0.0049,-0.3250, 1.3201}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x58, /* PX-A650/Stylus CX4700/CX4800/DX4800 */ {{0.9716, 0.0927,-0.0643, 0.0010, 1.1068,-0.1078, 0.0101,-0.3046, 1.2945}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x59, /* Stylus CX4100/CX4200/DX4200 */ {{0.9716, 0.0927,-0.0643, 0.0010, 1.1068,-0.1078, 0.0101,-0.3046, 1.2945}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x5B, /* Stylus CX7700/CX7800 */ {{0.9764, 0.1095,-0.0859, 0.0149, 1.1154,-0.1303, 0.0051,-0.2851, 1.2800}, {1.0024,-0.0149, 0.0124,-0.2569, 1.3432,-0.0864,-0.0043,-0.1306, 1.1349}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.1003,-0.0493,-0.0510,-0.1607, 1.2748,-0.1142,-0.0059,-0.3161, 1.3220}}}, {0x5D, /* Stylus Photo RX520/RX530 */ {{0.9764, 0.1095,-0.0859, 0.0149, 1.1154,-0.1303, 0.0051,-0.2851, 1.2800}, {1.0024,-0.0149, 0.0124,-0.2569, 1.3432,-0.0864,-0.0043,-0.1306, 1.1349}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.1003,-0.0493,-0.0510,-0.1607, 1.2748,-0.1142,-0.0059,-0.3161, 1.3220}}}, {0x5F, /* Stylus Photo RX640/RX650 */ {{1.0697,-0.0561,-0.0137,-0.0824, 1.1291,-0.0467,-0.0390,-0.5218, 1.5608}, {1.0208,-0.0209, 0.0000,-0.0923, 1.2017,-0.1093,-0.0020,-0.1290, 1.1310}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.2606,-0.2125,-0.0482,-0.0567, 1.1441,-0.0874,-0.0431,-0.3490, 1.3921}}}, {0x61, /* PM-A950 */ {{1.0921,-0.0722,-0.0199,-0.0831, 1.1550,-0.0718,-0.0452,-0.3721, 1.4173}, {1.0168,-0.0168, 0.0000,-0.0953, 1.1928,-0.0975,-0.0012,-0.1235, 1.1247}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.2603,-0.2763, 0.0155,-0.0398, 1.1033,-0.0635,-0.0249,-0.2675, 1.2924}}}, {0x63, /* GT-X900 */ {{1.0976,-0.0789,-0.0187,-0.0958, 1.1821,-0.0863,-0.0565,-0.4179, 1.4744}, {1.0250,-0.0267, 0.0016,-0.0930, 1.2108,-0.1178,-0.0022,-0.1296, 1.1317}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.3111,-0.2979,-0.0132,-0.0441, 1.1148,-0.0707,-0.0348,-0.2971, 1.3319}}}, {0x65, /* ES-H300 */ {{1.0359,-0.0146,-0.0213,-0.0752, 1.0963,-0.0211,-0.0456,-0.3238, 1.3693}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x66, /* GT-S600/F650, Perfection V10/V100 */ {{1.0878,-0.0667,-0.0211,-0.0892, 1.1513,-0.0622,-0.0654,-0.5175, 1.5829}, {1.0208,-0.0209, 0.0000,-0.0923, 1.2017,-0.1093,-0.0020,-0.1290, 1.1310}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.2688,-0.2522,-0.0166,-0.0559, 1.1291,-0.0733,-0.0377,-0.3519, 1.3896}}}, {0x68, /* GT-F700, Perfection V350 */ {{1.0950,-0.0646,-0.0305,-0.0792, 1.1398,-0.0606,-0.0123,-0.5175, 1.5298}, {1.0258,-0.0306, 0.0048,-0.0995, 1.2173,-0.1178,-0.0054,-0.1242, 1.1296}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.2697,-0.2501,-0.0195,-0.0351, 1.1236,-0.0885,-0.0131,-0.3268, 1.3400}}}, {0x6A, /* Stylus CX2800/CX2900/ME200 */ {{0.9828, 0.0924,-0.0752, 0.0255, 1.1510,-0.1765, 0.0049,-0.3250, 1.3201}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x6B, /* Stylus PX-A620, CX3900/DX4000 */ {{0.9828, 0.0924,-0.0752, 0.0255, 1.1510,-0.1765, 0.0049,-0.3250, 1.3201}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x6C, /* Stylus CX5900/CX6000/DX6000 */ {{0.9716, 0.0927,-0.0643, 0.0010, 1.1068,-0.1078, 0.0101,-0.3046, 1.2945}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x70, /* Stylus Photo RX560/RX580/RX590 */ {{0.9533, 0.0885,-0.0418, 0.0033, 1.0627,-0.0660,-0.0137,-0.1904, 1.2041}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x71, /* PM-A920 */ {{1.0697,-0.0561,-0.0137,-0.0824, 1.1291,-0.0467,-0.0390,-0.5218, 1.5608}, {1.0208,-0.0209, 0.0000,-0.0923, 1.2017,-0.1093,-0.0020,-0.1290, 1.1310}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.2606,-0.2125,-0.0482,-0.0567, 1.1441,-0.0874,-0.0431,-0.3490, 1.3921}}}, {0x73, /* PM-A970 */ {{1.0828,-0.0739,-0.0089,-0.0895, 1.1597,-0.0702,-0.0531,-0.4291, 1.4822}, {1.0258,-0.0306, 0.0048,-0.0995, 1.2173,-0.1178,-0.0054,-0.1242, 1.1296}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.2579,-0.2384,-0.0195,-0.0569, 1.1454,-0.0884,-0.0411,-0.3072, 1.3483}}}, {0x75, /* PM-T990 */ {{1.0828,-0.0739,-0.0089,-0.0895, 1.1597,-0.0702,-0.0531,-0.4291, 1.4822}, {1.0258,-0.0306, 0.0048,-0.0995, 1.2173,-0.1178,-0.0054,-0.1242, 1.1296}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.2579,-0.2384,-0.0195,-0.0569, 1.1454,-0.0884,-0.0411,-0.3072, 1.3483}}}, {0x77, /* Stylus CX4900/CX5000/DX5000 */ {{0.9716, 0.0927,-0.0643, 0.0010, 1.1068,-0.1078, 0.0101,-0.3046, 1.2945}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x78, /* LP-M5600 */ {{1.0784,-0.0560,-0.0224,-0.1793, 1.2234,-0.0441,-0.0041,-0.2636, 1.2677}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x79, /* AcuLaser CX21 */ {{1.0614,-0.0361,-0.0253,-0.1081, 1.1320,-0.0240,-0.0536,-0.2045, 1.2580}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x7A, /* GT-F670, Perfection V200 */ {{1.1754,-0.1173,-0.0580,-0.0687, 1.1307,-0.0620,-0.0255,-0.4699, 1.4954}, {1.0150,-0.0173, 0.0022,-0.0853, 1.2238,-0.1384,-0.0073,-0.1490, 1.1562}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.4283,-0.4335, 0.0052,-0.0170, 1.1308,-0.1138,-0.0147,-0.2230, 1.2377}}}, {0x7C, /* GT-X770, Perfection V500 */ {{1.2470,-0.2041,-0.0429,-0.1920, 1.2918,-0.0998,-0.0100,-0.2503, 1.2603}, {1.0050,-0.0076, 0.0026,-0.2532, 1.1289, 0.1243,-0.0733,-0.0960, 1.1693}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.4724,-0.4599,-0.0125,-0.0876, 1.1562,-0.0686,-0.0097,-0.2278, 1.2375}}}, {0x7E, /* Stylus CX4300/CX4400/CX5500/CX5600/DX4400 */ {{0.9828, 0.0924,-0.0752, 0.0255, 1.1510,-0.1765, 0.0049,-0.3250, 1.3201}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x7F, /* PX-A640, Stylus CX7300/CX7400/DX7400 */ {{1.0936,-0.0142,-0.0795,-0.0001, 1.0951,-0.0949, 0.0308,-0.2967, 1.2659}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x80, /* PX-A740, Stylus CX8300/CX8400/DX8400 */ {{1.0936,-0.0142,-0.0795,-0.0001, 1.0951,-0.0949, 0.0308,-0.2967, 1.2659}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x81, /* PX-FA700, Stylus CX9300F/CX9400Fax/DX9400F */ {{1.1090,-0.0304,-0.0786, 0.0194, 1.1078,-0.1272,-0.0077,-0.1293, 1.1370}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x82, /* PM-T960 */ {{1.1622,-0.1102,-0.0519,-0.0717, 1.1060,-0.0343,-0.0248,-0.4138, 1.4385}, {0.9913, 0.0082, 0.0005,-0.1259, 1.0452, 0.0807,-0.0072,-0.0767, 1.0839}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.3900,-0.3008,-0.0892,-0.0254, 1.0890,-0.0636,-0.0300,-0.2501, 1.2801}}}, {0x84, /* PM-A940, Stylus Photo RX680/RX685/RX690 */ {{1.0934,-0.0042,-0.0892, 0.0052, 1.1019,-0.1071, 0.0259,-0.2651, 1.2392}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x85, /* PM-A840/A840S, Stylus Photo RX585/RX595/RX610 */ {{1.0534, 0.0399,-0.0934, 0.0098, 1.0589,-0.0687, 0.0016,-0.1131, 1.1115}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x86, /* GT-D1000, GT-1500 */ {{1.1945,-0.1413,-0.0532,-0.1929, 1.2525,-0.0596,-0.0235,-0.2761, 1.2996}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x87, /* GT-X970 */ {{1.1978,-0.1417,-0.0561,-0.0852, 1.1610,-0.0758,-0.0395,-0.3212, 1.3607}, {1.0000, 0.0009,-0.0009,-0.1268, 1.0523, 0.0745,-0.0075,-0.0873, 1.0948}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.4475,-0.3957,-0.0518,-0.0138, 1.0644,-0.0506,-0.0199,-0.2050, 1.2249}}}, {0x97, /* LP-M5000 */ {{1.1115,-0.0377,-0.0738,-0.0658, 1.0624, 0.0034, 0.0042,-0.2883, 1.2841}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x89, /* LP-M6000 */ {{1.1115,-0.0377,-0.0738,-0.0658, 1.0624, 0.0034, 0.0042,-0.2883, 1.2841}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x8A, /* ES-H7200, GT-20000 */ {{1.1221,-0.0396,-0.0825,-0.0718, 1.0822,-0.0104, 0.0112,-0.2995, 1.2883}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x8B, /* GT-F720/S620, Perfection V30/V300 */ {{1.2402,-0.1891,-0.0511,-0.1535, 1.2008,-0.0473,-0.0316,-0.3293, 1.3609}, {1.0027,-0.0048, 0.0021,-0.2067, 1.0878, 0.1189,-0.0408,-0.0767, 1.1175}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.4524,-0.4346,-0.0178,-0.0601, 1.1273,-0.0672,-0.0173,-0.1823, 1.1996}}}, {0x8D, /* Stylus NX200/SX200/TX200 */ {{1.0936,-0.0142,-0.0795,-0.0001, 1.0951,-0.0949, 0.0308,-0.2967, 1.2659}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x8E, /* PX-501A, Stylus NX400/SX400/TX400 */ {{1.0936,-0.0142,-0.0795,-0.0001, 1.0951,-0.0949, 0.0308,-0.2967, 1.2659}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x8F, /* Stylus NX300 / Stylus Office BX300F/TX300F / ME Office 600F */ {{1.1090,-0.0304,-0.0786, 0.0194, 1.1078,-0.1272,-0.0077,-0.1293, 1.1370}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x90, /* PX-601F, Stylus SX600FW/TX600FW / Stylus Office BX600FW / WorkForce 600 */ {{1.0316, 0.0864,-0.1180, 0.0268, 1.1111,-0.1379, 0.0213,-0.2235, 1.2022}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x91, /* EP-901A/901F, Artisan 800 / Stylus Photo PX800FW/FX800FW */ {{1.0777, 0.0152,-0.0929, 0.0244, 1.1221,-0.1465, 0.0103,-0.1544, 1.1441}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x92, /* EP-801A, Artisan 700 / Stylus Photo PX700W/TX700W */ {{1.0777, 0.0152,-0.0929, 0.0244, 1.1221,-0.1465, 0.0103,-0.1544, 1.1441}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x93, /* PX-401A, Stylus NX100/SX100/TX100 / ME 300 */ {{1.0934,-0.0042,-0.0892, 0.0052, 1.1019,-0.1071, 0.0259,-0.2651, 1.2392}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x96, /* WorkForce 500 */ {{1.1090,-0.0304,-0.0786, 0.0194, 1.1078,-0.1272,-0.0077,-0.1293, 1.1370}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x98, {{1.0936,-0.0142,-0.0795,-0.0001, 1.0951,-0.0949, 0.0308,-0.2967, 1.2659}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x99, {{1.1090,-0.0304,-0.0786, 0.0194, 1.1078,-0.1272,-0.0077,-0.1293, 1.1370}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x9A, {{1.0779, 0.0132,-0.0911, 0.0214, 1.1003,-0.1217, 0.0109,-0.1487, 1.1378}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x9B, {{1.0779, 0.0132,-0.0911, 0.0214, 1.1003,-0.1217, 0.0109,-0.1487, 1.1378}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x9C, {{1.0316, 0.0864,-0.1180, 0.0268, 1.1111,-0.1379, 0.0213,-0.2235, 1.2022}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x9D, {{1.0316, 0.0864,-0.1180, 0.0268, 1.1111,-0.1379, 0.0213,-0.2235, 1.2022}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x9E, {{1.0534, 0.0399,-0.0934, 0.0098, 1.0589,-0.0687, 0.0016,-0.1131, 1.1115}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0x9F, {{1.0777, 0.0152,-0.0929, 0.0244, 1.1221,-0.1465, 0.0103,-0.1544, 1.1441}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0xA0, {{1.0777, 0.0152,-0.0929, 0.0244, 1.1221,-0.1465, 0.0103,-0.1544, 1.1441}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}}}, {0xA1, {{1.2578,-0.2140,-0.0438,-0.1939, 1.2856,-0.0917,-0.0258,-0.2642, 1.2900}, {0.9989,-0.0018, 0.0029,-0.2608, 1.1305, 0.1303,-0.0802,-0.0807, 1.1609}, {1.0000, 0.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 0.0000, 1.0000}, {1.4431,-0.4193,-0.0238,-0.0915, 1.1507,-0.0592,-0.0226,-0.1978, 1.2204}}}, {0xFF, /* terminator */ {{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}}} }; /* cat hw-data.c | grep '{"' | cut -d ',' -f1,2 | awk '{ print $0 " },"}' */ const struct epson_profile_map epson_cct_models[] = { {"GT-10000", 0x05 }, {"ES-6000", 0x05 }, {"Perfection610", 0x06 }, {"GT-6600", 0x06 }, {"Perfection1200", 0x07 }, {"GT-7600", 0x07 }, {"Expression1600", 0x0D }, {"ES-2000", 0x0D }, {"Expression1640XL", 0x0F }, {"ES-8500", 0x0F }, {"Perfection640", 0x15 }, {"GT-6700", 0x15 }, {"Perfection1640", 0x16 }, {"GT-8700", 0x16 }, {"Perfection1240", 0x18 }, {"GT-7700", 0x18 }, {"GT-30000", 0x1A }, {"ES-9000H", 0x1A }, {"Expression1680", 0x1B }, {"ES-2200", 0x1B }, {"GT-7200", 0x1D }, {"GT-8200", 0x1F }, {"GT-9700", 0x21 }, {"GT-7300", 0x23 }, {"GT-8300", 0x25 }, {"GT-9300", 0x27 }, {"GT-9800", 0x29 }, {"ES-7000H", 0x2B }, {"LP-A500", 0x51 }, {"AL-CX11", 0x51 }, {"GT-9400", 0x32 }, {"CC-600PX", 0x2D }, {"PM-A850", 0x3A }, {"CX5400", 0x36 }, {"GT-X700", 0x34 }, {"RX500", 0x38 }, {"PX-A650", 0x37 }, {"ES-10000G", 0x3F }, {"Expression10000", 0x3F }, {"CX4600", 0x46 }, {"CX6600", 0x49 }, {"CX3600", 0x46 }, {"RX420", 0x48 }, {"PM-A700", 0x48 }, {"PM-A870", 0x4B }, {"GT-F500", 0x41 }, {"GT-F600", 0x43 }, {"PM-A900", 0x4D }, {"GT-X800", 0x4F }, {"GT-X750", 0x54 }, {"LP-M5500", 0x56 }, {"LP-M5600", 0x78 }, {"GT-F520", 0x52 }, {"CX3800", 0x57 }, {"CX7800", 0x5B }, {"PM-A750", 0x5D }, {"CX4800", 0x58 }, {"CX4200", 0x59 }, {"PM-A950", 0x61 }, {"PM-A890", 0x5F }, {"GT-X900", 0x63 }, {"CX4000", 0x6B }, {"CX3000v", 0x6A }, {"ES-H300", 0x65 }, {"CX6000", 0x6C }, {"PM-A820", 0x70 }, {"PM-A920", 0x71 }, {"PM-A970", 0x73 }, {"PM-T990", 0x75 }, {"CX5000", 0x77 }, {"GT-S600", 0x66 }, {"GT-F700", 0x68 }, {"AL-CX21", 0x79 }, {"GT-F670", 0x7A }, {"GT-X770", 0x7C }, {"CX4400", 0x7E }, {"CX7400", 0x7F }, {"CX8400", 0x80 }, {"CX9400Fax", 0x81 }, {"PM-T960", 0x82 }, {"PM-A940", 0x84 }, {"PM-A840", 0x85 }, {"GT-D1000", 0x86 }, {"GT-X970", 0x87 }, {"LP-M5000", 0x97 }, {"LP-M6000", 0x89 }, {"ES-H7200", 0x8A }, {"GT-20000", 0x8A }, {"NX200", 0x8D }, {"NX400", 0x8E }, {"NX100", 0x93 }, {"NX300", 0x8F }, {"WorkForce 600", 0x90 }, {"Artisan 800", 0x91 }, {"Artisan 700", 0x92 }, {"WorkForce 500", 0x96 }, {"GT-F720", 0x8B }, {"GT-S620", 0x8B }, {"GT-S50", 0x00 }, {"GT-S80", 0x00 }, {"PID 0851", 0x98 }, {"PID 084D", 0x99 }, {"PID 084F", 0x9A }, {"PID 0854", 0x9B }, {"PID 0856", 0x9C }, {"PID 0855", 0x9D }, {"PID 0850", 0x9E }, {"PID 0852", 0xA0 }, {"PID 0853", 0x9F }, {"GT-X820", 0xA1 }, {NULL, 0x00 } /* terminator */ }; backends-1.3.0/backend/epson2-commands.c000066400000000000000000000604311456256263500200470ustar00rootroot00000000000000/* * ESC/I commands for Epson scanners * * Based on Kazuhiro Sasayama previous * Work on epson.[ch] file from the SANE package. * Please see those files for original copyrights. * * Copyright (C) 2006 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ #define DEBUG_DECLARE_ONLY #include "sane/config.h" #include #include #include #include "epson2.h" #include "epson2-io.h" #include "epson2-commands.h" /* ESC H, set zoom */ SANE_Status esci_set_zoom(Epson_Scanner * s, unsigned char x, unsigned char y) { SANE_Status status; unsigned char params[2]; DBG(8, "%s: x = %d, y = %d\n", __func__, x, y); if (!s->hw->cmd->set_zoom) { DBG(1, "%s: not supported\n", __func__); return SANE_STATUS_GOOD; } params[0] = ESC; params[1] = s->hw->cmd->set_zoom; status = e2_cmd_simple(s, params, 2); if (status != SANE_STATUS_GOOD) return status; params[0] = x; params[1] = y; return e2_cmd_simple(s, params, 2); } /* ESC R */ SANE_Status esci_set_resolution(Epson_Scanner * s, int x, int y) { SANE_Status status; unsigned char params[4]; DBG(8, "%s: x = %d, y = %d\n", __func__, x, y); if (!s->hw->cmd->set_resolution) { DBG(1, "%s: not supported\n", __func__); return SANE_STATUS_GOOD; } params[0] = ESC; params[1] = s->hw->cmd->set_resolution; status = e2_cmd_simple(s, params, 2); if (status != SANE_STATUS_GOOD) return status; params[0] = x; params[1] = x >> 8; params[2] = y; params[3] = y >> 8; return e2_cmd_simple(s, params, 4); } /* * Sends the "set scan area" command to the scanner with the currently selected * scan area. This scan area must be already corrected for "color shuffling" if * necessary. */ SANE_Status esci_set_scan_area(Epson_Scanner * s, int x, int y, int width, int height) { SANE_Status status; unsigned char params[8]; DBG(8, "%s: x = %d, y = %d, w = %d, h = %d\n", __func__, x, y, width, height); if (!s->hw->cmd->set_scan_area) { DBG(1, "%s: not supported\n", __func__); return SANE_STATUS_UNSUPPORTED; } /* verify the scan area */ if (x < 0 || y < 0 || width <= 0 || height <= 0) return SANE_STATUS_INVAL; params[0] = ESC; params[1] = s->hw->cmd->set_scan_area; status = e2_cmd_simple(s, params, 2); if (status != SANE_STATUS_GOOD) return status; params[0] = x; params[1] = x >> 8; params[2] = y; params[3] = y >> 8; params[4] = width; params[5] = width >> 8; params[6] = height; params[7] = height >> 8; return e2_cmd_simple(s, params, 8); } static int get_roundup_index(double frac[], int n) { int i, index = -1; double max_val = 0.0; for (i = 0; i < n; i++) { if (frac[i] < 0) continue; if (max_val < frac[i]) { index = i; max_val = frac[i]; } } return index; } static int get_rounddown_index(double frac[], int n) { int i, index = -1; double min_val = 1.0; for (i = 0; i < n; i++) { if (frac[i] > 0) continue; if (min_val > frac[i]) { index = i; min_val = frac[i]; } } return index; } static unsigned char int2cpt(int val) { if (val >= 0) { if (val > 127) val = 127; return (unsigned char) val; } else { val = -val; if (val > 127) val = 127; return (unsigned char) (0x80 | val); } } static void round_cct(double org_cct[], int rnd_cct[]) { int loop = 0; int i, j, sum[3]; double mult_cct[9], frac[9]; for (i = 0; i < 9; i++) { mult_cct[i] = org_cct[i] * 32; rnd_cct[i] = (int) floor(mult_cct[i] + 0.5); } do { for (i = 0; i < 3; i++) { int k = i * 3; if ((rnd_cct[k] == 11) && (rnd_cct[k] == rnd_cct[k + 1]) && (rnd_cct[k] == rnd_cct[k + 2])) { rnd_cct[k + i]--; mult_cct[k + i] = rnd_cct[k + i]; } } for (i = 0; i < 3; i++) { int k = i * 3; for (sum[i] = j = 0; j < 3; j++) sum[i] += rnd_cct[k + j]; } for (i = 0; i < 9; i++) frac[i] = mult_cct[i] - rnd_cct[i]; for (i = 0; i < 3; i++) { int k = i * 3; if (sum[i] < 32) { int index = get_roundup_index(&frac[k], 3); if (index != -1) { rnd_cct[k + index]++; mult_cct[k + index] = rnd_cct[k + index]; sum[i]++; } } else if (sum[i] > 32) { int index = get_rounddown_index(&frac[k], 3); if (index != -1) { rnd_cct[k + index]--; mult_cct[k + index] = rnd_cct[k + index]; sum[i]--; } } } } while ((++loop < 2) && ((sum[0] != 32) || (sum[1] != 32) || (sum[2] != 32))); } static void profile_to_colorcoeff(double *profile, unsigned char *color_coeff) { int cc_idx[] = { 4, 1, 7, 3, 0, 6, 5, 2, 8 }; int i, color_table[9]; round_cct(profile, color_table); for (i = 0; i < 9; i++) color_coeff[i] = int2cpt(color_table[cc_idx[i]]); } /* * Sends the "set color correction coefficients" command with the * currently selected parameters to the scanner. */ SANE_Status esci_set_color_correction_coefficients(Epson_Scanner * s, SANE_Word *table) { SANE_Status status; unsigned char params[2]; unsigned char data[9]; double cct[9]; DBG(8, "%s\n", __func__); if (!s->hw->cmd->set_color_correction_coefficients) { DBG(1, "%s: not supported\n", __func__); return SANE_STATUS_UNSUPPORTED; } params[0] = ESC; params[1] = s->hw->cmd->set_color_correction_coefficients; status = e2_cmd_simple(s, params, 2); if (status != SANE_STATUS_GOOD) return status; cct[0] = SANE_UNFIX(table[0]); cct[1] = SANE_UNFIX(table[1]); cct[2] = SANE_UNFIX(table[2]); cct[3] = SANE_UNFIX(table[3]); cct[4] = SANE_UNFIX(table[4]); cct[5] = SANE_UNFIX(table[5]); cct[6] = SANE_UNFIX(table[6]); cct[7] = SANE_UNFIX(table[7]); cct[8] = SANE_UNFIX(table[8]); profile_to_colorcoeff(cct, data); DBG(11, "%s: %d,%d,%d %d,%d,%d %d,%d,%d\n", __func__, data[0] , data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8]); return e2_cmd_simple(s, data, 9); } SANE_Status esci_set_gamma_table(Epson_Scanner * s) { SANE_Status status; unsigned char params[2]; unsigned char gamma[257]; int n; int table; /* static const char gamma_cmds[] = { 'M', 'R', 'G', 'B' }; */ static const char gamma_cmds[] = { 'R', 'G', 'B' }; DBG(8, "%s\n", __func__); if (!s->hw->cmd->set_gamma_table) return SANE_STATUS_UNSUPPORTED; params[0] = ESC; params[1] = s->hw->cmd->set_gamma_table; /* Print the gamma tables before sending them to the scanner */ if (DBG_LEVEL >= 16) { int c, i, j; for (c = 0; c < 3; c++) { for (i = 0; i < 256; i += 16) { char gammaValues[16 * 3 + 1], newValue[4]; gammaValues[0] = '\0'; for (j = 0; j < 16; j++) { sprintf(newValue, " %02x", s->gamma_table[c][i + j]); strcat(gammaValues, newValue); } DBG(16, "gamma table[%d][%d] %s\n", c, i, gammaValues); } } } for (table = 0; table < 3; table++) { gamma[0] = gamma_cmds[table]; for (n = 0; n < 256; ++n) gamma[n + 1] = s->gamma_table[table][n]; status = e2_cmd_simple(s, params, 2); if (status != SANE_STATUS_GOOD) return status; status = e2_cmd_simple(s, gamma, 257); if (status != SANE_STATUS_GOOD) return status; } return status; } /* ESC F - Request Status * -> ESC f * <- Information block */ SANE_Status esci_request_status(SANE_Handle handle, unsigned char *scanner_status) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Status status; unsigned char params[2]; DBG(8, "%s\n", __func__); if (s->hw->cmd->request_status == 0) return SANE_STATUS_UNSUPPORTED; params[0] = ESC; params[1] = s->hw->cmd->request_status; e2_send(s, params, 2, 4, &status); if (status != SANE_STATUS_GOOD) return status; status = e2_recv_info_block(s, params, 4, NULL); if (status != SANE_STATUS_GOOD) return status; if (scanner_status) *scanner_status = params[0]; DBG(1, "status: %02x\n", params[0]); if (params[0] & STATUS_NOT_READY) DBG(1, " scanner in use on another interface\n"); else DBG(1, " ready\n"); if (params[0] & STATUS_FER) DBG(1, " system error\n"); if (params[0] & STATUS_OPTION) DBG(1, " option equipment is installed\n"); else DBG(1, " no option equipment installed\n"); if (params[0] & STATUS_EXT_COMMANDS) DBG(1, " support extended commands\n"); else DBG(1, " does NOT support extended commands\n"); if (params[0] & STATUS_RESERVED) DBG(0, " a reserved bit is set, please contact the author.\n"); return status; } /* extended commands */ /* FS I, Request Extended Identity * -> FS I * <- Extended identity data (80) * * Request the properties of the scanner. */ SANE_Status esci_request_extended_identity(SANE_Handle handle, unsigned char *buf) { unsigned char model[17]; Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Status status; unsigned char params[2]; DBG(8, "%s\n", __func__); if (buf == NULL) return SANE_STATUS_INVAL; if (s->hw->cmd->request_extended_identity == 0) return SANE_STATUS_UNSUPPORTED; params[0] = FS; params[1] = s->hw->cmd->request_extended_identity; status = e2_txrx(s, params, 2, buf, 80); if (status != SANE_STATUS_GOOD) return status; DBG(1, " command level : %c%c\n", buf[0], buf[1]); DBG(1, " basic resolution: %lu\n", (unsigned long) le32atoh(&buf[4])); DBG(1, " min resolution : %lu\n", (unsigned long) le32atoh(&buf[8])); DBG(1, " max resolution : %lu\n", (unsigned long) le32atoh(&buf[12])); DBG(1, " max pixel num : %lu\n", (unsigned long) le32atoh(&buf[16])); DBG(1, " scan area : %lux%lu\n", (unsigned long) le32atoh(&buf[20]), (unsigned long) le32atoh(&buf[24])); DBG(1, " adf area : %lux%lu\n", (unsigned long) le32atoh(&buf[28]), (unsigned long) le32atoh(&buf[32])); DBG(1, " tpu area : %lux%lu\n", (unsigned long) le32atoh(&buf[36]), (unsigned long) le32atoh(&buf[40])); DBG(1, " capabilities (1): 0x%02x\n", buf[44]); DBG(1, " capabilities (2): 0x%02x\n", buf[45]); DBG(1, " input depth : %d\n", buf[66]); DBG(1, " max output depth: %d\n", buf[67]); DBG(1, " rom version : %c%c%c%c\n", buf[62], buf[63], buf[64], buf[65]); memcpy(model, &buf[46], 16); model[16] = '\0'; DBG(1, " model name : %s\n", model); DBG(1, "options:\n"); if (le32atoh(&buf[28]) > 0) DBG(1, " ADF detected\n"); if (le32atoh(&buf[36]) > 0) DBG(1, " TPU detected\n"); if (buf[44]) DBG(1, "capabilities (1):\n"); if (buf[44] & EXT_IDTY_CAP1_DLF) DBG(1, " main lamp change is supported\n"); if (buf[44] & EXT_IDTY_CAP1_NOTFBF) DBG(1, " the device is NOT flatbed\n"); if (buf[44] & EXT_IDTY_CAP1_ADFT) DBG(1, " page type ADF is installed\n"); if (buf[44] & EXT_IDTY_CAP1_ADFS) DBG(1, " ADF is duplex capable\n"); if (buf[44] & EXT_IDTY_CAP1_ADFO) DBG(1, " page type ADF loads from the first sheet\n"); if (buf[44] & EXT_IDTY_CAP1_LID) DBG(1, " lid type option is installed\n"); if (buf[44] & EXT_IDTY_CAP1_TPIR) DBG(1, " infrared scanning is supported\n"); if (buf[44] & EXT_IDTY_CAP1_PB) DBG(1, " push button is supported\n"); if (buf[45]) DBG(1, "capabilities (2):\n"); if (buf[45] & EXT_IDTY_CAP2_AFF) DBG(1, " ADF has auto form feed\n"); if (buf[45] & EXT_IDTY_CAP2_DFD) DBG(1, " ADF has double feed detection\n"); if (buf[45] & EXT_IDTY_CAP2_ADFAS) DBG(1, " ADF has auto scan\n"); return SANE_STATUS_GOOD; } /* FS F, request scanner status */ SANE_Status esci_request_scanner_status(SANE_Handle handle, unsigned char *buf) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Status status; unsigned char params[2]; DBG(8, "%s\n", __func__); if (!s->hw->extended_commands) return SANE_STATUS_UNSUPPORTED; if (buf == NULL) return SANE_STATUS_INVAL; params[0] = FS; params[1] = 'F'; status = e2_txrx(s, params, 2, buf, 16); if (status != SANE_STATUS_GOOD) return status; DBG(1, "global status : 0x%02x\n", buf[0]); if (buf[0] & FSF_STATUS_MAIN_FER) DBG(1, " system error\n"); if (buf[0] & FSF_STATUS_MAIN_NR) DBG(1, " not ready\n"); if (buf[0] & FSF_STATUS_MAIN_WU) DBG(1, " scanner is warming up\n"); if (buf[0] & FSF_STATUS_MAIN_CWU) DBG(1, " warmup can be cancelled\n"); DBG(1, "adf status : 0x%02x\n", buf[1]); if (buf[1] & FSF_STATUS_ADF_IST) DBG(11, " installed\n"); else DBG(11, " not installed\n"); if (buf[1] & FSF_STATUS_ADF_EN) DBG(11, " enabled\n"); else DBG(11, " not enabled\n"); if (buf[1] & FSF_STATUS_ADF_ERR) DBG(1, " error\n"); if (buf[1] & FSF_STATUS_ADF_PE) DBG(1, " paper empty\n"); if (buf[1] & FSF_STATUS_ADF_PJ) DBG(1, " paper jam\n"); if (buf[1] & FSF_STATUS_ADF_OPN) DBG(1, " cover open\n"); if (buf[1] & FSF_STATUS_ADF_PAG) DBG(1, " duplex capable\n"); DBG(1, "tpu status : 0x%02x\n", buf[2]); if (buf[2] & FSF_STATUS_TPU_IST) DBG(11, " installed\n"); else DBG(11, " not installed\n"); if (buf[2] & FSF_STATUS_TPU_EN) DBG(11, " enabled\n"); else DBG(11, " not enabled\n"); if (buf[2] & FSF_STATUS_TPU_ERR) DBG(1, " error\n"); if (buf[1] & FSF_STATUS_TPU_OPN) DBG(1, " cover open\n"); DBG(1, "device type : 0x%02x\n", buf[3] & 0xC0); DBG(1, "main body status: 0x%02x\n", buf[3] & 0x3F); if (buf[3] & FSF_STATUS_MAIN2_PE) DBG(1, " paper empty\n"); if (buf[3] & FSF_STATUS_MAIN2_PJ) DBG(1, " paper jam\n"); if (buf[3] & FSF_STATUS_MAIN2_OPN) DBG(1, " cover open\n"); return SANE_STATUS_GOOD; } SANE_Status esci_set_scanning_parameter(SANE_Handle handle, unsigned char *buf) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Status status; unsigned char params[2]; DBG(8, "%s\n", __func__); if (buf == NULL) return SANE_STATUS_INVAL; params[0] = FS; params[1] = 'W'; DBG(10, "resolution of main scan : %lu\n", (unsigned long) le32atoh(&buf[0])); DBG(10, "resolution of sub scan : %lu\n", (unsigned long) le32atoh(&buf[4])); DBG(10, "offset length of main scan : %lu\n", (unsigned long) le32atoh(&buf[8])); DBG(10, "offset length of sub scan : %lu\n", (unsigned long) le32atoh(&buf[12])); DBG(10, "scanning length of main scan: %lu\n", (unsigned long) le32atoh(&buf[16])); DBG(10, "scanning length of sub scan : %lu\n", (unsigned long) le32atoh(&buf[20])); DBG(10, "scanning color : %d\n", buf[24]); DBG(10, "data format : %d\n", buf[25]); DBG(10, "option control : %d\n", buf[26]); DBG(10, "scanning mode : %d\n", buf[27]); DBG(10, "block line number : %d\n", buf[28]); DBG(10, "gamma correction : %d\n", buf[29]); DBG(10, "brightness : %d\n", buf[30]); DBG(10, "color correction : %d\n", buf[31]); DBG(10, "halftone processing : %d\n", buf[32]); DBG(10, "threshold : %d\n", buf[33]); DBG(10, "auto area segmentation : %d\n", buf[34]); DBG(10, "sharpness control : %d\n", buf[35]); DBG(10, "mirroring : %d\n", buf[36]); DBG(10, "film type : %d\n", buf[37]); DBG(10, "main lamp lighting mode : %d\n", buf[38]); status = e2_cmd_simple(s, params, 2); if (status != SANE_STATUS_GOOD) return status; status = e2_cmd_simple(s, buf, 64); if (status != SANE_STATUS_GOOD) { DBG(1, "%s: invalid scanning parameters\n", __func__); return status; } return SANE_STATUS_GOOD; } /* FS S */ SANE_Status esci_get_scanning_parameter(SANE_Handle handle, unsigned char *buf) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Status status; unsigned char params[2]; DBG(8, "%s\n", __func__); if (buf == NULL) return SANE_STATUS_INVAL; params[0] = FS; params[1] = 'S'; status = e2_txrx(s, params, 2, buf, 64); if (status != SANE_STATUS_GOOD) return status; DBG(10, "resolution of main scan : %lu\n", (u_long) le32atoh(&buf[0])); DBG(10, "resolution of sub scan : %lu\n", (u_long) le32atoh(&buf[4])); DBG(10, "offset length of main scan : %lu\n", (u_long) le32atoh(&buf[8])); DBG(10, "offset length of sub scan : %lu\n", (u_long) le32atoh(&buf[12])); DBG(10, "scanning length of main scan: %lu\n", (u_long) le32atoh(&buf[16])); DBG(10, "scanning length of sub scan : %lu\n", (u_long) le32atoh(&buf[20])); DBG(10, "scanning color : %d\n", buf[24]); DBG(10, "data format : %d\n", buf[25]); DBG(10, "option control : %d\n", buf[26]); DBG(10, "scanning mode : %d\n", buf[27]); DBG(10, "block line number : %d\n", buf[28]); DBG(10, "gamma correction : %d\n", buf[29]); DBG(10, "brightness : %d\n", buf[30]); DBG(10, "color correction : %d\n", buf[31]); DBG(10, "halftone processing : %d\n", buf[32]); DBG(10, "threshold : %d\n", buf[33]); DBG(10, "auto area segmentation : %d\n", buf[34]); DBG(10, "sharpness control : %d\n", buf[35]); DBG(10, "mirroring : %d\n", buf[36]); DBG(10, "film type : %d\n", buf[37]); DBG(10, "main lamp lighting mode : %d\n", buf[38]); return SANE_STATUS_GOOD; } /* ESC # */ SANE_Status esci_enable_infrared(SANE_Handle handle) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Status status; int i; unsigned char params[2]; unsigned char buf[64]; unsigned char seq[32] = { 0xCA, 0xFB, 0x77, 0x71, 0x20, 0x16, 0xDA, 0x09, 0x5F, 0x57, 0x09, 0x12, 0x04, 0x83, 0x76, 0x77, 0x3C, 0x73, 0x9C, 0xBE, 0x7A, 0xE0, 0x52, 0xE2, 0x90, 0x0D, 0xFF, 0x9A, 0xEF, 0x4C, 0x2C, 0x81 }; DBG(8, "%s\n", __func__); status = esci_get_scanning_parameter(handle, buf); if (status != SANE_STATUS_GOOD) return status; for (i = 0; i < 32; i++) { buf[i] = seq[i] ^ buf[i]; } params[0] = ESC; params[1] = '#'; status = e2_cmd_simple(s, params, 2); if (status != SANE_STATUS_GOOD) return status; status = e2_cmd_simple(s, buf, 32); if (status != SANE_STATUS_GOOD) return status; return SANE_STATUS_GOOD; } SANE_Status esci_request_command_parameter(SANE_Handle handle, unsigned char *buf) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Status status; unsigned char params[2]; DBG(8, "%s\n", __func__); if (s->hw->cmd->request_condition == 0) return SANE_STATUS_UNSUPPORTED; params[0] = ESC; params[1] = s->hw->cmd->request_condition; status = e2_cmd_info_block(s, params, 2, 45, &buf, NULL); if (status != SANE_STATUS_GOOD) return status; DBG(1, "scanning parameters:\n"); DBG(1, "color : %d\n", buf[1]); DBG(1, "resolution : %dx%d\n", buf[4] << 8 | buf[3], buf[6] << 8 | buf[5]); DBG(1, "halftone : %d\n", buf[19]); DBG(1, "brightness : %d\n", buf[21]); DBG(1, "color correction : %d\n", buf[28]); DBG(1, "gamma : %d\n", buf[23]); DBG(1, "sharpness : %d\n", buf[30]); DBG(1, "threshold : %d\n", buf[38]); DBG(1, "data format : %d\n", buf[17]); DBG(1, "mirroring : %d\n", buf[34]); DBG(1, "option unit control : %d\n", buf[42]); DBG(1, "film type : %d\n", buf[44]); DBG(1, "auto area segmentation : %d\n", buf[36]); DBG(1, "line counter : %d\n", buf[40]); DBG(1, "scanning mode : %d\n", buf[32]); DBG(1, "zoom : %d,%d\n", buf[26], buf[25]); DBG(1, "scan area : %d,%d %d,%d\n", buf[9] << 8 | buf[8], buf[11] << 8 | buf[10], buf[13] << 8 | buf[12], buf[15] << 8 | buf[14]); return status; } /* ESC q - Request Focus Position * -> ESC q * <- Information block * <- Focus position status (2) * 0 - Error status * 1 - Focus position */ SANE_Status esci_request_focus_position(SANE_Handle handle, unsigned char *position) { SANE_Status status; unsigned char *buf; Epson_Scanner *s = (Epson_Scanner *) handle; unsigned char params[2]; DBG(8, "%s\n", __func__); if (s->hw->cmd->request_focus_position == 0) return SANE_STATUS_UNSUPPORTED; params[0] = ESC; params[1] = s->hw->cmd->request_focus_position; status = e2_cmd_info_block(s, params, 2, 2, &buf, NULL); if (status != SANE_STATUS_GOOD) return status; if (buf[0] & 0x01) DBG(1, "autofocus error\n"); *position = buf[1]; DBG(8, " focus position = 0x%x\n", buf[1]); free(buf); return status; } /* ESC ! - Request Push Button Status * -> ESC ! * <- Information block * <- Push button status (1) */ SANE_Status esci_request_push_button_status(SANE_Handle handle, unsigned char *bstatus) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Status status; unsigned char params[2]; unsigned char *buf; DBG(8, "%s\n", __func__); if (s->hw->cmd->request_push_button_status == 0) { DBG(1, "push button status unsupported\n"); return SANE_STATUS_UNSUPPORTED; } params[0] = ESC; params[1] = s->hw->cmd->request_push_button_status; status = e2_cmd_info_block(s, params, 2, 1, &buf, NULL); if (status != SANE_STATUS_GOOD) return status; DBG(1, "push button status = %d\n", buf[0]); *bstatus = buf[0]; free(buf); return status; } /* * Request Identity information from scanner and fill in information * into dev and/or scanner structures. * XXX information should be parsed separately. */ SANE_Status esci_request_identity(SANE_Handle handle, unsigned char **buf, size_t *len) { Epson_Scanner *s = (Epson_Scanner *) handle; unsigned char params[2]; DBG(8, "%s\n", __func__); if (!s->hw->cmd->request_identity) return SANE_STATUS_INVAL; params[0] = ESC; params[1] = s->hw->cmd->request_identity; return e2_cmd_info_block(s, params, 2, 0, buf, len); } /* * Request information from scanner */ SANE_Status esci_request_identity2(SANE_Handle handle, unsigned char **buf) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Status status; size_t len; unsigned char params[2]; DBG(8, "%s\n", __func__); if (s->hw->cmd->request_identity2 == 0) return SANE_STATUS_UNSUPPORTED; params[0] = ESC; params[1] = s->hw->cmd->request_identity2; status = e2_cmd_info_block(s, params, 2, 0, buf, &len); if (status != SANE_STATUS_GOOD) return status; return status; } /* Send the "initialize scanner" command to the device and reset it */ SANE_Status esci_reset(Epson_Scanner * s) { SANE_Status status; unsigned char params[2]; DBG(8, "%s\n", __func__); if (!s->hw->cmd->initialize_scanner) return SANE_STATUS_GOOD; params[0] = ESC; params[1] = s->hw->cmd->initialize_scanner; if (s->fd == -1) return SANE_STATUS_GOOD; status = e2_cmd_simple(s, params, 2); return status; } SANE_Status esci_feed(Epson_Scanner * s) { unsigned char params[1]; DBG(8, "%s\n", __func__); if (!s->hw->cmd->feed) return SANE_STATUS_UNSUPPORTED; params[0] = s->hw->cmd->feed; return e2_cmd_simple(s, params, 1); } /* * Eject the current page from the ADF. The scanner is opened prior to * sending the command and closed afterwards. */ SANE_Status esci_eject(Epson_Scanner * s) { unsigned char params[1]; DBG(8, "%s\n", __func__); if (!s->hw->cmd->eject) return SANE_STATUS_UNSUPPORTED; if (s->fd == -1) return SANE_STATUS_GOOD; params[0] = s->hw->cmd->eject; return e2_cmd_simple(s, params, 1); } SANE_Status esci_request_extended_status(SANE_Handle handle, unsigned char **data, size_t * data_len) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Status status = SANE_STATUS_GOOD; unsigned char params[2]; unsigned char *buf; size_t buf_len; DBG(8, "%s\n", __func__); if (s->hw->cmd->request_extended_status == 0) return SANE_STATUS_UNSUPPORTED; params[0] = ESC; params[1] = s->hw->cmd->request_extended_status; /* This command returns 33 bytes of data on old scanners * and 42 (CMD_SIZE_EXT_STATUS) on new ones. */ status = e2_cmd_info_block(s, params, 2, CMD_SIZE_EXT_STATUS, &buf, &buf_len); if (status != SANE_STATUS_GOOD) return status; switch (buf_len) { case 33: case 42: break; default: DBG(1, "%s: unknown reply length (%lu)\n", __func__, (unsigned long) buf_len); break; } DBG(4, "main = %02x, ADF = %02x, TPU = %02x, main 2 = %02x\n", buf[0], buf[1], buf[6], buf[11]); if (buf[0] & EXT_STATUS_FER) DBG(1, "system error\n"); if (buf[0] & EXT_STATUS_WU) DBG(1, "scanner is warming up\n"); if (buf[1] & EXT_STATUS_ERR) DBG(1, "ADF: other error\n"); if (buf[1] & EXT_STATUS_PE) DBG(1, "ADF: no paper\n"); if (buf[1] & EXT_STATUS_PJ) DBG(1, "ADF: paper jam\n"); if (buf[1] & EXT_STATUS_OPN) DBG(1, "ADF: cover open\n"); if (buf[6] & EXT_STATUS_ERR) DBG(1, "TPU: other error\n"); /* give back a pointer to the payload * if the user requested it, otherwise * free it. */ if (data) *data = buf; else free(buf); if (data_len) *data_len = buf_len; return status; } backends-1.3.0/backend/epson2-commands.h000066400000000000000000000066411456256263500200570ustar00rootroot00000000000000/* * Prototypes for Epson ESC/I commands * * Based on Kazuhiro Sasayama previous * Work on epson.[ch] file from the SANE package. * Please see those files for original copyrights. * * Copyright (C) 2006 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ /* simple scanner commands, ESC */ #define esci_set_focus_position(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_focus_position, v) #define esci_set_color_mode(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_color_mode, v) #define esci_set_data_format(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_data_format, v) #define esci_set_halftoning(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_halftoning, v) #define esci_set_gamma(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_gamma, v) #define esci_set_color_correction(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_color_correction, v) #define esci_set_lcount(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_lcount, v) #define esci_set_bright(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_bright, v) #define esci_mirror_image(s,v) e2_esc_cmd( s,(s)->hw->cmd->mirror_image, v) #define esci_set_speed(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_speed, v) #define esci_set_sharpness(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_outline_emphasis, v) #define esci_set_auto_area_segmentation(s,v) e2_esc_cmd( s,(s)->hw->cmd->control_auto_area_segmentation, v) #define esci_set_film_type(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_film_type, v) #define esci_set_exposure_time(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_exposure_time, v) #define esci_set_bay(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_bay, v) #define esci_set_threshold(s,v) e2_esc_cmd( s,(s)->hw->cmd->set_threshold, v) #define esci_control_extension(s,v) e2_esc_cmd( s,(s)->hw->cmd->control_an_extension, v) SANE_Status esci_set_zoom(Epson_Scanner * s, unsigned char x, unsigned char y); SANE_Status esci_set_resolution(Epson_Scanner * s, int x, int y); SANE_Status esci_set_scan_area(Epson_Scanner * s, int x, int y, int width, int height); SANE_Status esci_set_color_correction_coefficients(Epson_Scanner * s, SANE_Word *table); SANE_Status esci_set_gamma_table(Epson_Scanner * s); SANE_Status esci_request_status(SANE_Handle handle, unsigned char *scanner_status); SANE_Status esci_request_extended_identity(SANE_Handle handle, unsigned char *buf); SANE_Status esci_request_scanner_status(SANE_Handle handle, unsigned char *buf); SANE_Status esci_set_scanning_parameter(SANE_Handle handle, unsigned char *buf); SANE_Status esci_get_scanning_parameter(SANE_Handle handle, unsigned char *buf); SANE_Status esci_request_command_parameter(SANE_Handle handle, unsigned char *buf); SANE_Status esci_request_focus_position(SANE_Handle handle, unsigned char *position); SANE_Status esci_request_push_button_status(SANE_Handle handle, unsigned char *bstatus); SANE_Status esci_request_identity(SANE_Handle handle, unsigned char **buf, size_t *len); SANE_Status esci_request_identity2(SANE_Handle handle, unsigned char **buf); SANE_Status esci_reset(Epson_Scanner * s); SANE_Status esci_feed(Epson_Scanner * s); SANE_Status esci_eject(Epson_Scanner * s); SANE_Status esci_request_extended_status(SANE_Handle handle, unsigned char **data, size_t * data_len); SANE_Status esci_enable_infrared(SANE_Handle handle); backends-1.3.0/backend/epson2-io.c000066400000000000000000000224671456256263500166640ustar00rootroot00000000000000/* * I/O routines for Epson scanners * * Based on Kazuhiro Sasayama previous * Work on epson.[ch] file from the SANE package. * Please see those files for original copyrights. * * Copyright (C) 2006 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ #define DEBUG_DECLARE_ONLY #include "sane/config.h" #include #include "epson2.h" #include "epson2-io.h" #include "sane/sanei_scsi.h" #include "sane/sanei_usb.h" #include "sane/sanei_pio.h" #include "sane/sanei_tcp.h" #include "epson2_scsi.h" #include "epson_usb.h" #include "epson2_net.h" #include "byteorder.h" /* flaming hack to get USB scanners * working without timeouts under linux * (cribbed from fujitsu.c) */ unsigned int r_cmd_count = 0; unsigned int w_cmd_count = 0; int e2_send(Epson_Scanner * s, void *buf, size_t buf_size, size_t reply_len, SANE_Status * status) { DBG(15, "%s: size = %lu, reply = %lu\n", __func__, (u_long) buf_size, (u_long) reply_len); if (buf_size == 2) { char *cmd = buf; switch (cmd[0]) { case ESC: DBG(9, "%s: ESC %c\n", __func__, cmd[1]); break; case FS: DBG(9, "%s: FS %c\n", __func__, cmd[1]); break; } } if (DBG_LEVEL >= 125) { unsigned int k; const unsigned char *s = buf; for (k = 0; k < buf_size; k++) { DBG(125, "buf[%d] %02x %c\n", k, s[k], isprint(s[k]) ? s[k] : '.'); } } if (s->hw->connection == SANE_EPSON_NET) { if (reply_len == 0) { DBG(0, "Cannot send this command to a networked scanner\n"); *status = SANE_STATUS_INVAL; return 0; /* nothing actually sent */ } return sanei_epson_net_write(s, 0x2000, buf, buf_size, reply_len, status); } else if (s->hw->connection == SANE_EPSON_SCSI) { return sanei_epson2_scsi_write(s->fd, buf, buf_size, status); } else if (s->hw->connection == SANE_EPSON_PIO) { size_t n; if (buf_size == (n = sanei_pio_write(s->fd, buf, buf_size))) *status = SANE_STATUS_GOOD; else *status = SANE_STATUS_INVAL; return n; } else if (s->hw->connection == SANE_EPSON_USB) { size_t n; n = buf_size; *status = sanei_usb_write_bulk(s->fd, buf, &n); w_cmd_count++; DBG(20, "%s: cmd count, r = %d, w = %d\n", __func__, r_cmd_count, w_cmd_count); return n; } *status = SANE_STATUS_INVAL; return 0; /* never reached */ } ssize_t e2_recv(Epson_Scanner *s, void *buf, ssize_t buf_size, SANE_Status *status) { ssize_t n = buf_size; /* network interface needs to read header back even data is 0.*/ DBG(15, "%s: size = %ld, buf = %p\n", __func__, (long) buf_size, buf); *status = SANE_STATUS_GOOD; if (s->hw->connection == SANE_EPSON_NET) { n = sanei_epson_net_read(s, buf, buf_size, status); } else if (s->hw->connection == SANE_EPSON_SCSI) { if (buf_size) n = sanei_epson2_scsi_read(s->fd, buf, buf_size, status); } else if (s->hw->connection == SANE_EPSON_PIO) { if (buf_size) { if (buf_size == (n = sanei_pio_read(s->fd, buf, (size_t) buf_size))) *status = SANE_STATUS_GOOD; else *status = SANE_STATUS_INVAL; } } else if (s->hw->connection == SANE_EPSON_USB) { /* !!! only report an error if we don't read anything */ if (n) { *status = sanei_usb_read_bulk(s->fd, (SANE_Byte *) buf, (size_t *) & n); r_cmd_count += (n + 63) / 64; /* add # of packets, rounding up */ DBG(20, "%s: cmd count, r = %d, w = %d\n", __func__, r_cmd_count, w_cmd_count); if (n > 0) *status = SANE_STATUS_GOOD; } } if (n < buf_size) { DBG(1, "%s: expected = %lu, got = %ld, canceling: %d\n", __func__, (u_long) buf_size, (long) n, s->canceling); *status = SANE_STATUS_IO_ERROR; } /* dump buffer if appropriate */ if (DBG_LEVEL >= 127 && n > 0) { int k; const unsigned char *s = buf; for (k = 0; k < n; k++) DBG(127, "buf[%d] %02x %c\n", k, s[k], isprint(s[k]) ? s[k] : '.'); } return n; } /* Simple function to exchange a fixed amount of * data with the scanner */ SANE_Status e2_txrx(Epson_Scanner * s, unsigned char *txbuf, size_t txlen, unsigned char *rxbuf, size_t rxlen) { SANE_Status status; size_t done; done = e2_send(s, txbuf, txlen, rxlen, &status); if (status != SANE_STATUS_GOOD) { DBG(1, "%s: tx err, %s\n", __func__, sane_strstatus(status)); return status; } if (done != txlen) { DBG(1, "%s: tx err, short write\n", __func__); return SANE_STATUS_IO_ERROR; } e2_recv(s, rxbuf, rxlen, &status); if (status != SANE_STATUS_GOOD) { DBG(1, "%s: rx err, %s\n", __func__, sane_strstatus(status)); } DBG(1, "%s: eds_recv status, %s\n", __func__, sane_strstatus(status)); return status; } /* This function should be used to send codes that only requires the scanner * to give back an ACK or a NAK. */ SANE_Status e2_cmd_simple(Epson_Scanner * s, void *buf, size_t buf_size) { unsigned char result; SANE_Status status; DBG(12, "%s: size = %lu\n", __func__, (u_long) buf_size); status = e2_txrx(s, buf, buf_size, &result, 1); if (status != SANE_STATUS_GOOD) { DBG(1, "%s: failed, %s\n", __func__, sane_strstatus(status)); return status; } if (result == ACK) return SANE_STATUS_GOOD; if (result == NAK) { DBG(3, "%s: NAK\n", __func__); return SANE_STATUS_INVAL; } DBG(1, "%s: result is neither ACK nor NAK but 0x%02x\n", __func__, result); return SANE_STATUS_GOOD; } /* receives a 4 or 6 bytes information block from the scanner*/ SANE_Status e2_recv_info_block(Epson_Scanner * s, unsigned char *scanner_status, size_t info_size, size_t * payload_size) { SANE_Status status; unsigned char info[6]; if (s->hw->connection == SANE_EPSON_PIO) e2_recv(s, info, 1, &status); else e2_recv(s, info, info_size, &status); if (status != SANE_STATUS_GOOD) return status; /* check for explicit NAK */ if (info[0] == NAK) { DBG(1, "%s: command not supported\n", __func__); return SANE_STATUS_UNSUPPORTED; } /* check the first byte: if it's not STX, bail out */ if (info[0] != STX) { DBG(1, "%s: expecting STX, got %02X\n", __func__, info[0]); return SANE_STATUS_INVAL; } /* if connection is PIO read the remaining bytes. */ if (s->hw->connection == SANE_EPSON_PIO) { e2_recv(s, &info[1], info_size - 1, &status); if (status != SANE_STATUS_GOOD) return status; } if (scanner_status) *scanner_status = info[1]; if (payload_size) { *payload_size = le16atoh(&info[2]); if (info_size == 6) *payload_size *= le16atoh(&info[4]); DBG(14, "%s: payload length: %lu\n", __func__, (u_long) *payload_size); } return SANE_STATUS_GOOD; } /* This function can be called for commands that * will be answered by the scanner with an info block of 4 bytes * and a variable payload. The payload is passed back to the caller * in **buf. The caller must free it if != NULL, * even if the status != SANE_STATUS_GOOD. */ SANE_Status e2_cmd_info_block(SANE_Handle handle, unsigned char *params, unsigned char params_len, size_t reply_len, unsigned char **buf, size_t * buf_len) { SANE_Status status; Epson_Scanner *s = (Epson_Scanner *) handle; size_t len; DBG(13, "%s, params len = %d, reply len = %lu, buf = %p\n", __func__, params_len, (u_long) reply_len, (void *) buf); if (buf == NULL) return SANE_STATUS_INVAL; /* initialize */ *buf = NULL; /* send command, we expect the info block + reply_len back */ e2_send(s, params, params_len, reply_len ? reply_len + 4 : 0, &status); if (status != SANE_STATUS_GOOD) goto end; status = e2_recv_info_block(s, NULL, 4, &len); if (status != SANE_STATUS_GOOD) goto end; /* do we need to provide the length of the payload? */ if (buf_len) *buf_len = len; /* no payload, stop here */ if (len == 0) goto end; /* if a reply_len has been specified and the actual * length differs, throw a warning */ if (reply_len && (len != reply_len)) { DBG(1, "%s: mismatched len - expected %lu, got %lu\n", __func__, (u_long) reply_len, (u_long) len); } /* allocate and receive the payload */ *buf = malloc(len); if (*buf) { memset(*buf, 0x00, len); e2_recv(s, *buf, len, &status); /* receive actual data */ } else status = SANE_STATUS_NO_MEM; end: if (status != SANE_STATUS_GOOD) { DBG(1, "%s: failed, %s\n", __func__, sane_strstatus(status)); if (*buf) { free(*buf); *buf = NULL; } } return status; } /* This is used for ESC commands with a single byte parameter. Scanner * will answer with ACK/NAK. */ SANE_Status e2_esc_cmd(Epson_Scanner * s, unsigned char cmd, unsigned char val) { SANE_Status status; unsigned char params[2]; DBG(8, "%s: cmd = 0x%02x, val = %d\n", __func__, cmd, val); if (!cmd) return SANE_STATUS_UNSUPPORTED; params[0] = ESC; params[1] = cmd; status = e2_cmd_simple(s, params, 2); if (status != SANE_STATUS_GOOD) return status; params[0] = val; return e2_cmd_simple(s, params, 1); } /* Send an ACK to the scanner */ SANE_Status e2_ack(Epson_Scanner * s) { SANE_Status status; e2_send(s, S_ACK, 1, 0, &status); return status; } SANE_Status e2_ack_next(Epson_Scanner * s, size_t reply_len) { SANE_Status status; e2_send(s, S_ACK, 1, reply_len, &status); return status; } SANE_Status e2_cancel(Epson_Scanner * s) { DBG(1, "%s\n", __func__); return e2_cmd_simple(s, S_CAN, 1); } backends-1.3.0/backend/epson2-io.h000066400000000000000000000030471456256263500166620ustar00rootroot00000000000000/* * Prototypes for epson2 I/O functions * * Based on Kazuhiro Sasayama previous * Work on epson.[ch] file from the SANE package. * Please see those files for original copyrights. * * Copyright (C) 2006 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ #ifndef epson2_io_h #define epson2_io_h extern unsigned int r_cmd_count; extern unsigned int w_cmd_count; SANE_Status e2_cmd_simple(Epson_Scanner * s, void *buf, size_t buf_size); int e2_send(Epson_Scanner * s, void *buf, size_t buf_size, size_t reply_len, SANE_Status * status); ssize_t e2_recv(Epson_Scanner * s, void *buf, ssize_t buf_size, SANE_Status * status); SANE_Status e2_txrx(Epson_Scanner * s, unsigned char *txbuf, size_t txlen, unsigned char *rxbuf, size_t rxlen); SANE_Status e2_recv_info_block(Epson_Scanner * s, unsigned char *scanner_status, size_t info_size, size_t * payload_size); SANE_Status e2_cmd_info_block(SANE_Handle handle, unsigned char *params, unsigned char params_len, size_t reply_len, unsigned char **buf, size_t * buf_len); SANE_Status e2_ack(Epson_Scanner * s); SANE_Status e2_ack_next(Epson_Scanner * s, size_t reply_len); SANE_Status e2_cancel(Epson_Scanner * s); SANE_Status e2_esc_cmd(Epson_Scanner * s, unsigned char cmd, unsigned char val); #endif /* epson2_io_h */ backends-1.3.0/backend/epson2-ops.c000066400000000000000000001617451456256263500170610ustar00rootroot00000000000000/* * epson2.c - SANE library for Epson scanners. * * Based on Kazuhiro Sasayama previous * Work on epson.[ch] file from the SANE package. * Please see those files for additional copyrights. * * Copyright (C) 2006-09 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ #define DEBUG_DECLARE_ONLY #include "sane/config.h" #include /* sleep */ #ifdef HAVE_SYS_SELECT_H #include #endif #include "byteorder.h" #include "epson2.h" #include "epson2-ops.h" #include "epson2-io.h" #include "epson2-commands.h" /* * request identity * | request identity2 * | | request status * | | | request condition * | | | | set color mode * | | | | | start scanning * | | | | | | set data format * | | | | | | | set resolution * | | | | | | | | set zoom * | | | | | | | | | set scan area * | | | | | | | | | | set brightness * | | | | | | | | | | | set gamma * | | | | | | | | | | | | set halftoning * | | | | | | | | | | | | | set color correction * | | | | | | | | | | | | | | initialize scanner * | | | | | | | | | | | | | | | set speed * | | | | | | | | | | | | | | | | set lcount * | | | | | | | | | | | | | | | | | mirror image * | | | | | | | | | | | | | | | | | | set gamma table * | | | | | | | | | | | | | | | | | | | set outline emphasis * | | | | | | | | | | | | | | | | | | | | set dither * | | | | | | | | | | | | | | | | | | | | | set color correction coefficients * | | | | | | | | | | | | | | | | | | | | | | request extension status * | | | | | | | | | | | | | | | | | | | | | | | control an extension * | | | | | | | | | | | | | | | | | | | | | | | | forward feed / eject * | | | | | | | | | | | | | | | | | | | | | | | | | feed * | | | | | | | | | | | | | | | | | | | | | | | | | | request push button status * | | | | | | | | | | | | | | | | | | | | | | | | | | | control auto area segmentation * | | | | | | | | | | | | | | | | | | | | | | | | | | | | set film type * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set exposure time * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set bay * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set threshold * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set focus position * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | request focus position * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | request extended identity * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | request scanner status * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | */ static struct EpsonCmd epson_cmd[] = { {"A1",'I', 0 ,'F','S', 0 ,'G', 0 ,'R', 0 ,'A', 0 ,{ 0, 0, 0}, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }, {"A2",'I', 0 ,'F','S', 0 ,'G','D','R','H','A','L',{-3, 3, 0},'Z','B', 0 ,'@', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }, {"B1",'I', 0 ,'F','S','C','G','D','R', 0 ,'A', 0 ,{ 0, 0, 0}, 0 ,'B', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }, {"B2",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z','B', 0 ,'@', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }, {"B3",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z','B','M','@', 0 , 0 , 0 , 0 , 0 , 0 ,'m','f','e', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }, {"B4",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z','B','M','@','g','d', 0 ,'z','Q','b','m','f','e', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }, {"B5",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z','B','M','@','g','d','K','z','Q','b','m','f','e', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }, {"B6",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z','B','M','@','g','d','K','z','Q','b','m','f','e', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }, {"B7",'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', 0 ,'!','s','N', 0 , 0 ,'t', 0 , 0 ,'I','F'}, {"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 ,'t','p','q','I','F'}, /* XXX 'f' probably not supported on F5 */ {"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 , 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 ,'t', 0 , 0 , 0 , 0 }, {"D2",'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 ,'N', 0 , 0 ,'t', 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','e','\f', 0 ,'!', 0 ,'N', 0 , 0 ,'t', 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','\f', 0 ,'!', 0 ,'N', 0 , 0 ,'t', 0 , 0 , 0 , 0 }, }; extern struct mode_param mode_params[]; /* Define the different scan sources */ #define FBF_STR SANE_I18N("Flatbed") #define TPU_STR SANE_I18N("Transparency Unit") #define TP2_STR SANE_I18N("TPU8x10") #define ADF_STR SANE_I18N("Automatic Document Feeder") /* * source list need one dummy entry (save device settings is crashing). * NOTE: no const - this list gets created while exploring the capabilities * of the scanner. */ extern SANE_String_Const source_list[]; static int film_params[] = { 0, 1, 2, 3 }; extern const int halftone_params[]; static const int dropout_params[] = { 0x00, /* none */ 0x10, /* red */ 0x20, /* green */ 0x30 /* blue */ }; /* * Color correction: * One array for the actual parameters that get sent to the scanner (color_params[]), * one array for the strings that get displayed in the user interface (correction_list[]) * and one array to mark the user defined color correction (correction_userdefined[]). */ static const int correction_params[] = { 0x00, /* None */ 0x01, /* Auto */ 0x01, /* User defined */ }; void e2_dev_init(Epson_Device *dev, const char *devname, int conntype) { DBG(5, "%s\n", __func__); dev->name = NULL; dev->model = NULL; dev->connection = conntype; dev->model_id = 0; dev->sane.name = devname; dev->sane.model = NULL; dev->sane.type = "flatbed scanner"; dev->sane.vendor = "Epson"; dev->optical_res = 0; /* just to have it initialized */ dev->color_shuffle = SANE_FALSE; dev->extension = SANE_FALSE; dev->use_extension = SANE_FALSE; dev->need_color_reorder = SANE_FALSE; dev->need_double_vertical = SANE_FALSE; dev->cct_profile = &epson_cct_profiles[0]; /* default profile */ dev->cmd = &epson_cmd[EPSON_LEVEL_DEFAULT]; /* Change default level when using a network connection */ if (dev->connection == SANE_EPSON_NET) dev->cmd = &epson_cmd[EPSON_LEVEL_B7]; dev->last_res = 0; dev->last_res_preview = 0; /* set resolution to safe values */ dev->res_list_size = 0; dev->res_list = NULL; } SANE_Status e2_dev_post_init(struct Epson_Device *dev) { int i, last; DBG(5, "%s\n", __func__); /* find cct model id */ for (i = 0; epson_cct_models[i].name != NULL; i++) { if (strcmp(epson_cct_models[i].name, dev->model) == 0) { dev->model_id = epson_cct_models[i].id; break; } } /* find cct profile */ for (i = 0; epson_cct_profiles[i].model != 0xFF; i++) { if (epson_cct_profiles[i].model == dev->model_id) { dev->cct_profile = &epson_cct_profiles[i]; break; } } DBG(1, "CCT model id is 0x%02x, profile offset %d\n", dev->model_id, i); /* If we have been unable to obtain supported resolutions * due to the fact we are on the network transport, * add some convenient ones */ if (dev->res_list_size == 0) { int val = (dev->dpi_range.min < 150) ? 150 : dev->dpi_range.min; DBG(1, "cannot obtain resolution list, faking (%d-%d)\n", dev->dpi_range.min, dev->dpi_range.max); if (dev->dpi_range.min <= 25) e2_add_resolution(dev, 25); if (dev->dpi_range.min <= 50) e2_add_resolution(dev, 50); if (dev->dpi_range.min <= 75) e2_add_resolution(dev, 75); if (dev->dpi_range.min <= 100) e2_add_resolution(dev, 100); while (val <= dev->dpi_range.max) { e2_add_resolution(dev, val); val *= 2; } } /* try to expand the resolution list where appropriate */ last = dev->res_list[dev->res_list_size - 1]; DBG(1, "highest available resolution: %d\n", last); if (dev->optical_res > last) { DBG(1, "adding optical resolution (%d)\n", dev->optical_res); e2_add_resolution(dev, dev->optical_res); } /* add missing resolutions for known scanners */ if (e2_dev_model(dev, "GT-X800") || e2_dev_model(dev, "GT-X700")) { DBG(1, "known scanner, integrating resolution list\n"); e2_add_resolution(dev, 4800); e2_add_resolution(dev, 6400); e2_add_resolution(dev, 9600); e2_add_resolution(dev, 12800); last = dev->res_list[dev->res_list_size - 1]; } /* guess for the others */ if (dev->dpi_range.max > last && dev->dpi_range.max != dev->optical_res) { int val = last + last; DBG(1, "integrating resolution list (%d-%d)\n", val, dev->dpi_range.max); while (val <= dev->dpi_range.max) { e2_add_resolution(dev, val); val += last; } } /* * Copy the resolution list to the resolution_list array so that the frontend can * display the correct values */ dev->resolution_list = malloc((dev->res_list_size + 1) * sizeof(SANE_Word)); if (dev->resolution_list == NULL) return SANE_STATUS_NO_MEM; *(dev->resolution_list) = dev->res_list_size; memcpy(&(dev->resolution_list[1]), dev->res_list, dev->res_list_size * sizeof(SANE_Word)); /* establish defaults */ dev->need_reset_on_source_change = SANE_FALSE; if (e2_dev_model(dev, "ES-9000H") || e2_dev_model(dev, "GT-30000")) { dev->focusSupport = SANE_FALSE; dev->cmd->feed = 0x19; } if (e2_dev_model(dev, "GT-8200") || e2_dev_model(dev, "Perfection1650") || e2_dev_model(dev, "Perfection1640") || e2_dev_model(dev, "GT-8700")) { dev->focusSupport = SANE_FALSE; dev->cmd->feed = 0; dev->need_reset_on_source_change = SANE_TRUE; } if (e2_dev_model(dev, "DS-G20000")) dev->cmd->bright_range.min = -3; return SANE_STATUS_GOOD; } SANE_Bool e2_dev_model(Epson_Device *dev, const char *model) { if (dev->model == NULL) return SANE_FALSE; if (strncmp(dev->model, model, strlen(model)) == 0) return SANE_TRUE; return SANE_FALSE; } void e2_set_cmd_level(SANE_Handle handle, unsigned char *level) { Epson_Scanner *s = (Epson_Scanner *) handle; Epson_Device *dev = s->hw; int n; DBG(1, "%s: %c%c\n", __func__, level[0], level[1]); /* set command type and level */ for (n = 0; n < NELEMS(epson_cmd); n++) { char type_level[3]; sprintf(type_level, "%c%c", level[0], level[1]); if (!strncmp(type_level, epson_cmd[n].level, 2)) break; } if (n < NELEMS(epson_cmd)) { dev->cmd = &epson_cmd[n]; } else { dev->cmd = &epson_cmd[EPSON_LEVEL_DEFAULT]; DBG(1, " unknown type %c or level %c, using %s\n", level[0], level[1], dev->cmd->level); } s->hw->level = dev->cmd->level[1] - '0'; } SANE_Status e2_set_model(Epson_Scanner * s, unsigned char *model, size_t len) { unsigned char *buf; unsigned char *p; struct Epson_Device *dev = s->hw; buf = malloc(len + 1); if (buf == NULL) return SANE_STATUS_NO_MEM; memcpy(buf, model, len); buf[len] = '\0'; p = &buf[len - 1]; while (*p == ' ') { *p = '\0'; p--; } if (dev->model) free(dev->model); dev->model = strndup((const char *) buf, len); dev->sane.model = dev->model; DBG(10, "%s: model is '%s'\n", __func__, dev->model); free(buf); return SANE_STATUS_GOOD; } SANE_Status e2_add_resolution(Epson_Device *dev, int r) { dev->res_list_size++; dev->res_list = (SANE_Int *) realloc(dev->res_list, dev->res_list_size * sizeof(SANE_Word)); DBG(10, "%s: add (dpi): %d\n", __func__, r); if (dev->res_list == NULL) return SANE_STATUS_NO_MEM; dev->res_list[dev->res_list_size - 1] = (SANE_Int) r; return SANE_STATUS_GOOD; } void e2_set_fbf_area(Epson_Scanner * s, int x, int y, int unit) { struct Epson_Device *dev = s->hw; if (x == 0 || y == 0) return; dev->fbf_x_range.min = 0; dev->fbf_x_range.max = SANE_FIX(x * MM_PER_INCH / unit); dev->fbf_x_range.quant = 0; dev->fbf_y_range.min = 0; dev->fbf_y_range.max = SANE_FIX(y * MM_PER_INCH / unit); dev->fbf_y_range.quant = 0; DBG(5, "%s: %f,%f %f,%f %d [mm]\n", __func__, SANE_UNFIX(dev->fbf_x_range.min), SANE_UNFIX(dev->fbf_y_range.min), SANE_UNFIX(dev->fbf_x_range.max), SANE_UNFIX(dev->fbf_y_range.max), unit); } void e2_set_adf_area(struct Epson_Scanner *s, int x, int y, int unit) { struct Epson_Device *dev = s->hw; dev->adf_x_range.min = 0; dev->adf_x_range.max = SANE_FIX(x * MM_PER_INCH / unit); dev->adf_x_range.quant = 0; dev->adf_y_range.min = 0; dev->adf_y_range.max = SANE_FIX(y * MM_PER_INCH / unit); dev->adf_y_range.quant = 0; DBG(5, "%s: %f,%f %f,%f %d [mm]\n", __func__, SANE_UNFIX(dev->adf_x_range.min), SANE_UNFIX(dev->adf_y_range.min), SANE_UNFIX(dev->adf_x_range.max), SANE_UNFIX(dev->adf_y_range.max), unit); } void e2_set_tpu_area(struct Epson_Scanner *s, int x, int y, int unit) { struct Epson_Device *dev = s->hw; dev->tpu_x_range.min = 0; dev->tpu_x_range.max = SANE_FIX(x * MM_PER_INCH / unit); dev->tpu_x_range.quant = 0; dev->tpu_y_range.min = 0; dev->tpu_y_range.max = SANE_FIX(y * MM_PER_INCH / unit); dev->tpu_y_range.quant = 0; DBG(5, "%s: %f,%f %f,%f %d [mm]\n", __func__, SANE_UNFIX(dev->tpu_x_range.min), SANE_UNFIX(dev->tpu_y_range.min), SANE_UNFIX(dev->tpu_x_range.max), SANE_UNFIX(dev->tpu_y_range.max), unit); } void e2_set_tpu2_area(struct Epson_Scanner *s, int x, int y, int unit) { struct Epson_Device *dev = s->hw; dev->tpu2_x_range.min = 0; dev->tpu2_x_range.max = SANE_FIX(x * MM_PER_INCH / unit); dev->tpu2_x_range.quant = 0; dev->tpu2_y_range.min = 0; dev->tpu2_y_range.max = SANE_FIX(y * MM_PER_INCH / unit); dev->tpu2_y_range.quant = 0; DBG(5, "%s: %f,%f %f,%f %d [mm]\n", __func__, SANE_UNFIX(dev->tpu2_x_range.min), SANE_UNFIX(dev->tpu2_y_range.min), SANE_UNFIX(dev->tpu2_x_range.max), SANE_UNFIX(dev->tpu2_y_range.max), unit); } void e2_add_depth(Epson_Device *dev, SANE_Int depth) { DBG(10, "%s: add (bpp): %d\n", __func__, depth); if (depth > dev->maxDepth) dev->maxDepth = depth; dev->depth_list[0]++; dev->depth_list[dev->depth_list[0]] = depth; } /* A little helper function to correct the extended status reply * gotten from scanners with known buggy firmware. */ static void fix_up_extended_status_reply(Epson_Scanner * s, unsigned char *buf) { if (e2_model(s, "ES-9000H") || e2_model(s, "GT-30000")) { DBG(1, "fixing up buggy ADF max scan dimensions.\n"); buf[2] = 0xB0; buf[3] = 0x6D; buf[4] = 0x60; buf[5] = 0x9F; } } SANE_Status e2_discover_capabilities(Epson_Scanner *s) { SANE_Status status; unsigned char scanner_status; Epson_Device *dev = s->hw; SANE_String_Const *source_list_add = source_list; DBG(5, "%s\n", __func__); /* always add flatbed */ *source_list_add++ = FBF_STR; /* ESC I, request identity * this must be the first command on the FilmScan 200 */ if (dev->connection != SANE_EPSON_NET) { unsigned int n, k, x = 0, y = 0; unsigned char *buf, *area; size_t len; status = esci_request_identity(s, &buf, &len); if (status != SANE_STATUS_GOOD) return status; e2_set_cmd_level(s, &buf[0]); /* Setting available resolutions and xy ranges for sane frontend. */ /* cycle thru the resolutions, saving them in a list */ for (n = 2, k = 0; n < len; n += k) { area = buf + n; switch (*area) { case 'R': { int val = area[2] << 8 | area[1]; status = e2_add_resolution(s->hw, val); k = 3; continue; } case 'A': { x = area[2] << 8 | area[1]; y = area[4] << 8 | area[3]; DBG(1, "maximum scan area: %dx%d\n", x, y); k = 5; continue; } default: break; } } /* min and max dpi */ dev->dpi_range.min = dev->res_list[0]; dev->dpi_range.max = dev->res_list[dev->res_list_size - 1]; dev->dpi_range.quant = 0; e2_set_fbf_area(s, x, y, dev->dpi_range.max); free(buf); } /* ESC F, request status */ status = esci_request_status(s, &scanner_status); if (status != SANE_STATUS_GOOD) return status; /* set capabilities */ if (scanner_status & STATUS_OPTION) dev->extension = SANE_TRUE; if (scanner_status & STATUS_EXT_COMMANDS) dev->extended_commands = 1; /* * Extended status flag request (ESC f). * this also requests the scanner device name from the scanner. * It seems unsupported on the network transport (CX11NF/LP-A500). */ if (dev->cmd->request_extended_status && dev->connection != SANE_EPSON_NET) { unsigned char *es; size_t es_len; DBG(1, "detection with request_extended_status\n"); status = esci_request_extended_status(s, &es, &es_len); if (status != SANE_STATUS_GOOD) return status; /* * Get the device name and copy it to dev->sane.model. * The device name starts at es[0x1A] and is up to 16 bytes long * We are overwriting whatever was set previously! */ if (es_len == CMD_SIZE_EXT_STATUS) /* 42 */ e2_set_model(s, es + 0x1A, 16); if (es[0] & EXT_STATUS_LID) DBG(1, "LID detected\n"); if (es[0] & EXT_STATUS_PB) DBG(1, "push button detected\n"); else dev->cmd->request_push_button_status = 0; /* Flatbed */ e2_set_fbf_area(s, es[13] << 8 | es[12], es[15] << 8 | es[14], dev->dpi_range.max); /* ADF */ if (dev->extension && (es[1] & EXT_STATUS_IST)) { DBG(1, "ADF detected\n"); fix_up_extended_status_reply(s, es); dev->duplex = (es[0] & EXT_STATUS_ADFS) != 0; if (dev->duplex) DBG(1, "ADF supports duplex\n"); if (es[1] & EXT_STATUS_EN) { DBG(1, "ADF is enabled\n"); dev->x_range = &dev->adf_x_range; dev->y_range = &dev->adf_y_range; } e2_set_adf_area(s, es[3] << 8 | es[2], es[5] << 8 | es[4], dev->dpi_range.max); *source_list_add++ = ADF_STR; dev->ADF = SANE_TRUE; } /* TPU */ if (dev->extension && (es[6] & EXT_STATUS_IST)) { DBG(1, "TPU detected\n"); if (es[6] & EXT_STATUS_EN) { DBG(1, "TPU is enabled\n"); dev->x_range = &dev->tpu_x_range; dev->y_range = &dev->tpu_y_range; } e2_set_tpu_area(s, (es[8] << 8 | es[7]), (es[10] << 8 | es[9]), dev->dpi_range.max); *source_list_add++ = TPU_STR; dev->TPU = SANE_TRUE; } free(es); *source_list_add = NULL; /* add end marker to source list */ } /* FS I, request extended identity (B7/B8) */ if (dev->extended_commands && dev->cmd->request_extended_identity) { unsigned char buf[80]; DBG(1, "detection with request_extended_identity\n"); status = esci_request_extended_identity(s, buf); if (status != SANE_STATUS_GOOD) return status; e2_set_cmd_level(s, &buf[0]); dev->maxDepth = buf[67]; /* set model name. it will probably be * different than the one reported by request_identity * for the same unit (i.e. LP-A500 vs CX11) . */ e2_set_model(s, &buf[46], 16); dev->optical_res = le32atoh(&buf[4]); dev->dpi_range.min = le32atoh(&buf[8]); dev->dpi_range.max = le32atoh(&buf[12]); /* Flatbed */ e2_set_fbf_area(s, le32atoh(&buf[20]), le32atoh(&buf[24]), dev->optical_res); /* ADF */ if (le32atoh(&buf[28]) > 0) { e2_set_adf_area(s, le32atoh(&buf[28]), le32atoh(&buf[32]), dev->optical_res); if (!dev->ADF) { *source_list_add++ = ADF_STR; dev->ADF = SANE_TRUE; } if (buf[44] & EXT_IDTY_CAP1_ADFS) { dev->duplex = SANE_TRUE; } } /* TPU */ if (le32atoh(&buf[36]) > 0 && !dev->TPU) { e2_set_tpu_area(s, le32atoh(&buf[36]), le32atoh(&buf[40]), dev->optical_res); *source_list_add++ = TPU_STR; dev->TPU = SANE_TRUE; } /* TPU2 */ if (e2_model(s, "GT-X800") || e2_model(s, "GT-X900") || e2_model(s, "GT-X980")) { if (le32atoh(&buf[68]) > 0 ) { e2_set_tpu2_area(s, le32atoh(&buf[68]), le32atoh(&buf[72]), dev->optical_res); *source_list_add++ = TP2_STR; } } *source_list_add = NULL; /* add end marker to source list */ } else { DBG(1, "no command available to detect capabilities\n"); } /* * request identity 2 (ESC i), if available will * get the information from the scanner and store it in dev */ if (dev->cmd->request_identity2 && dev->connection != SANE_EPSON_NET) { unsigned char *buf; status = esci_request_identity2(s, &buf); if (status != SANE_STATUS_GOOD) return status; /* the first two bytes of the buffer contain the optical resolution */ dev->optical_res = buf[1] << 8 | buf[0]; /* * the 4th and 5th byte contain the line distance. Both values have to * be identical, otherwise this software can not handle this scanner. */ if (buf[4] != buf[5]) { status = SANE_STATUS_INVAL; return status; } dev->max_line_distance = buf[4]; } /* * Check for the max. supported color depth and assign * the values to the bitDepthList. */ dev->depth_list = malloc(sizeof(SANE_Int) * (4 + 1)); if (dev->depth_list == NULL) { DBG(1, "out of memory (line %d)\n", __LINE__); return SANE_STATUS_NO_MEM; } dev->depth_list[0] = 0; /* maximum depth discovery */ DBG(3, "discovering max depth, NAKs are expected\n"); /* add default depth */ e2_add_depth(dev, 8); if (dev->maxDepth >= 12 || dev->maxDepth == 0) { if (esci_set_data_format(s, 12) == SANE_STATUS_GOOD) e2_add_depth(dev, 12); } if (dev->maxDepth >= 14 || dev->maxDepth == 0) { if (esci_set_data_format(s, 14) == SANE_STATUS_GOOD) e2_add_depth(dev, 14); } if (dev->maxDepth >= 16 || dev->maxDepth == 0) { if (esci_set_data_format(s, 16) == SANE_STATUS_GOOD) e2_add_depth(dev, 16); } DBG(1, "maximum supported color depth: %d\n", dev->maxDepth); /* * We assume that setting focus is supported when we can get the focus. * This assumption may be overridden in e2_dev_post_init() */ if (esci_request_focus_position(s, &s->currentFocusPosition) == SANE_STATUS_GOOD) { DBG(1, "getting focus is supported, current focus: %u\n", s->currentFocusPosition); dev->focusSupport = SANE_TRUE; s->opt[OPT_FOCUS_POS].cap &= ~SANE_CAP_INACTIVE; s->val[OPT_FOCUS_POS].w = s->currentFocusPosition; } else { DBG(1, "getting focus is not supported\n"); dev->focusSupport = SANE_FALSE; s->opt[OPT_FOCUS_POS].cap |= SANE_CAP_INACTIVE; s->val[OPT_FOCUS_POS].w = FOCUS_ON_GLASS; /* just in case */ } /* Set defaults for no extension. */ dev->x_range = &dev->fbf_x_range; dev->y_range = &dev->fbf_y_range; /* * Correct for a firmware bug in some Perfection 1650 scanners: * Firmware version 1.08 reports only half the vertical scan area, we have * to double the number. To find out if we have to do this, we just compare * is the vertical range is smaller than the horizontal range. */ if ((dev->x_range->max - dev->x_range->min) > (dev->y_range->max - dev->y_range->min)) { DBG(1, "found buggy scan area, doubling it.\n"); dev->y_range->max += (dev->y_range->max - dev->y_range->min); dev->need_double_vertical = SANE_TRUE; dev->need_color_reorder = SANE_TRUE; } /* FS F, request scanner status */ if (dev->extended_commands) { unsigned char buf[16]; status = esci_request_scanner_status(s, buf); if (status != SANE_STATUS_GOOD) return status; } return status; } SANE_Status e2_set_extended_scanning_parameters(Epson_Scanner * s) { unsigned char buf[64]; const struct mode_param *mparam; DBG(1, "%s\n", __func__); mparam = &mode_params[s->val[OPT_MODE].w]; memset(buf, 0x00, sizeof(buf)); /* ESC R, resolution */ htole32a(&buf[0], s->val[OPT_RESOLUTION].w); htole32a(&buf[4], s->val[OPT_RESOLUTION].w); /* ESC A, scanning area */ htole32a(&buf[8], s->left); htole32a(&buf[12], s->top); htole32a(&buf[16], s->params.pixels_per_line); htole32a(&buf[20], s->params.lines); /* * The byte sequence mode was introduced in B5, *for B[34] we need line sequence mode */ /* ESC C, set color */ if ((s->hw->cmd->level[0] == 'D' || (s->hw->cmd->level[0] == 'B' && s->hw->level >= 5)) && mparam->flags == 0x02) { buf[24] = 0x13; } else { buf[24] = mparam->flags | (mparam->dropout_mask & dropout_params[s-> val[OPT_DROPOUT]. w]); } /* ESC D, set data format */ mparam = &mode_params[s->val[OPT_MODE].w]; buf[25] = mparam->depth; /* ESC e, control option */ if (s->hw->extension) { char extensionCtrl; extensionCtrl = (s->hw->use_extension ? 1 : 0); if (s->hw->use_extension && (s->val[OPT_ADF_MODE].w == 1)) { extensionCtrl = 2; } /* Test for TPU2 * Epson Perfection 4990 Command Specifications * JZIS-0075 Rev. A, page 31 */ if (s->hw->use_extension && s->hw->TPU2) { extensionCtrl = 5; } if (s->val[OPT_MODE].w == MODE_INFRARED) { /* only infrared in TPU mode (NOT in TPU2 or flatbeth) * XXX investigate this ... only tested on GT-X800 */ if (extensionCtrl == 1) /* test for TPU */ extensionCtrl = 3; else return SANE_STATUS_UNSUPPORTED; } /* ESC e */ buf[26] = extensionCtrl; } /* ESC g, scanning mode (normal or high speed) */ if (s->val[OPT_PREVIEW].w) buf[27] = 1; /* High speed */ else buf[27] = 0; /* ESC d, block line number */ buf[28] = s->lcount; /* ESC Z, set gamma correction */ buf[29] = 0x01; /* default */ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_GAMMA_CORRECTION].cap)) { char val; if (s->hw->cmd->level[0] == 'D') { /* The D1 level has only the two user defined gamma * settings. */ val = gamma_params[s->val[OPT_GAMMA_CORRECTION].w]; } else { val = gamma_params[s->val[OPT_GAMMA_CORRECTION].w]; /* * If "Default" is selected then determine the actual value * to send to the scanner: If bilevel mode, just send the * value from the table (0x01), for grayscale or color mode * add one and send 0x02. */ if (s->val[OPT_GAMMA_CORRECTION].w == 0) { val += mparam->depth == 1 ? 0 : 1; } } buf[29] = val; } /* ESC L, set brightness */ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_BRIGHTNESS].cap)) buf[30] = s->val[OPT_BRIGHTNESS].w; /* ESC B, set halftoning mode / halftone processing */ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_HALFTONE].cap)) buf[32] = halftone_params[s->val[OPT_HALFTONE].w]; /* ESC s, auto area segmentation */ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_AAS].cap)) buf[34] = s->val[OPT_AAS].w; /* ESC Q, set sharpness / sharpness control */ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_SHARPNESS].cap)) buf[35] = s->val[OPT_SHARPNESS].w; /* ESC K, set data order / mirroring */ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_MIRROR].cap)) buf[36] = s->val[OPT_MIRROR].w; /* ESC N, film type */ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_FILM_TYPE].cap)) buf[37] = film_params[s->val[OPT_FILM_TYPE].w]; /* ESC M, color correction */ buf[31] = correction_params[s->val[OPT_COLOR_CORRECTION].w]; /* ESC t, threshold */ buf[33] = s->val[OPT_THRESHOLD].w; return esci_set_scanning_parameter(s, buf); } SANE_Status e2_set_scanning_parameters(Epson_Scanner * s) { SANE_Status status; struct mode_param *mparam = &mode_params[s->val[OPT_MODE].w]; unsigned char color_mode; DBG(1, "%s\n", __func__); /* * There is some undocumented special behavior with the TPU enable/disable. * TPU power ESC e status * on 0 NAK * on 1 ACK * off 0 ACK * off 1 NAK * * It makes no sense to scan with TPU powered on and source flatbed, because * light will come from both sides. */ if (s->hw->extension) { int extensionCtrl; extensionCtrl = (s->hw->use_extension ? 1 : 0); if (s->hw->use_extension && (s->val[OPT_ADF_MODE].w == 1)) extensionCtrl = 2; status = esci_control_extension(s, extensionCtrl); if (status != SANE_STATUS_GOOD) { DBG(1, "you may have to power %s your TPU\n", s->hw->use_extension ? "on" : "off"); DBG(1, "and you may also have to restart the SANE frontend.\n"); return status; } /* XXX use request_extended_status and analyze * buffer to set the scan area for * ES-9000H and GT-30000 */ } /* ESC C, Set color */ color_mode = mparam->flags | (mparam->dropout_mask & dropout_params[s->val[OPT_DROPOUT]. w]); /* * The byte sequence mode was introduced in B5, for B[34] we need line sequence mode * XXX Check what to do for the FilmScan 200 */ if ((s->hw->cmd->level[0] == 'D' || (s->hw->cmd->level[0] == 'B' && s->hw->level >= 5)) && mparam->flags == 0x02) color_mode = 0x13; status = esci_set_color_mode(s, color_mode); if (status != SANE_STATUS_GOOD) return status; /* ESC D, set data format */ DBG(1, "%s: setting data format to %d bits\n", __func__, mparam->depth); status = esci_set_data_format(s, mparam->depth); if (status != SANE_STATUS_GOOD) return status; /* ESC B, set halftoning mode */ if (s->hw->cmd->set_halftoning && SANE_OPTION_IS_ACTIVE(s->opt[OPT_HALFTONE].cap)) { status = esci_set_halftoning(s, halftone_params[s-> val [OPT_HALFTONE]. w]); if (status != SANE_STATUS_GOOD) return status; } /* ESC L, set brightness */ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_BRIGHTNESS].cap)) { status = esci_set_bright(s, s->val[OPT_BRIGHTNESS].w); if (status != SANE_STATUS_GOOD) return status; } if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_AAS].cap)) { status = esci_set_auto_area_segmentation(s, s->val[OPT_AAS].w); if (status != SANE_STATUS_GOOD) return status; } if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_FILM_TYPE].cap)) { status = esci_set_film_type(s, film_params[s->val[OPT_FILM_TYPE].w]); if (status != SANE_STATUS_GOOD) return status; } if (s->hw->cmd->set_gamma && SANE_OPTION_IS_ACTIVE(s->opt[OPT_GAMMA_CORRECTION].cap)) { int val; if (s->hw->cmd->level[0] == 'D') { /* * The D1 level has only the two user defined gamma * settings. */ val = gamma_params[s->val[OPT_GAMMA_CORRECTION].w]; } else { val = gamma_params[s->val[OPT_GAMMA_CORRECTION].w]; /* * If "Default" is selected then determine the actual value * to send to the scanner: If bilevel mode, just send the * value from the table (0x01), for grayscale or color mode * add one and send 0x02. */ /* if( s->val[ OPT_GAMMA_CORRECTION].w <= 1) { */ if (s->val[OPT_GAMMA_CORRECTION].w == 0) { val += mparam->depth == 1 ? 0 : 1; } } status = esci_set_gamma(s, val); if (status != SANE_STATUS_GOOD) return status; } if (s->hw->cmd->set_threshold != 0 && SANE_OPTION_IS_ACTIVE(s->opt[OPT_THRESHOLD].cap)) { status = esci_set_threshold(s, s->val[OPT_THRESHOLD].w); if (status != SANE_STATUS_GOOD) return status; } /* XXX ESC Z here */ /* ESC M, set color correction */ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_COLOR_CORRECTION].cap)) { status = esci_set_color_correction(s, correction_params[s->val[OPT_COLOR_CORRECTION].w]); if (status != SANE_STATUS_GOOD) return status; } /* ESC Q, set sharpness */ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_SHARPNESS].cap)) { status = esci_set_sharpness(s, s->val[OPT_SHARPNESS].w); if (status != SANE_STATUS_GOOD) return status; } /* ESC g, set scanning mode */ if (s->val[OPT_PREVIEW].w) status = esci_set_speed(s, 1); else status = esci_set_speed(s, 0); if (status != SANE_STATUS_GOOD) return status; /* ESC K, set data order */ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_MIRROR].cap)) { status = esci_mirror_image(s, s->val[OPT_MIRROR].w); if (status != SANE_STATUS_GOOD) return status; } /* ESC R */ status = esci_set_resolution(s, s->val[OPT_RESOLUTION].w, s->val[OPT_RESOLUTION].w); if (status != SANE_STATUS_GOOD) return status; /* ESC H, set zoom */ /* not implemented */ /* ESC A, set scanning area */ /* * Modify the scan area: If the scanner requires color shuffling, then we try to * scan more lines to compensate for the lines that will be removed from the scan * due to the color shuffling algorithm. */ if (s->hw->color_shuffle == SANE_TRUE) { unsigned int lines = s->params.lines + (2 * s->line_distance); int top = s->top - (1 * s->line_distance); if (top < 0) top = 0; status = esci_set_scan_area(s, s->left, top, s->params.pixels_per_line, lines); } else { status = esci_set_scan_area(s, s->left, s->top, s->params.pixels_per_line, s->params.lines); } if (status != SANE_STATUS_GOOD) return status; /* ESC d, set block line number / set line counter */ status = esci_set_lcount(s, s->lcount); if (status != SANE_STATUS_GOOD) return status; return SANE_STATUS_GOOD; } void e2_setup_block_mode(Epson_Scanner * s) { int maxreq; DBG(5, "%s\n", __func__); s->block = SANE_TRUE; if (s->hw->connection == SANE_EPSON_SCSI) maxreq = sanei_scsi_max_request_size; else if (s->hw->connection == SANE_EPSON_USB) maxreq = 128 * 1024; else maxreq = 32 * 1024; /* XXX verify if this can b extended to other models */ if (s->hw->connection == SANE_EPSON_NET && e2_model(s, "LP-A500")) maxreq = 64 * 1024; s->lcount = maxreq / s->params.bytes_per_line; DBG(1, "max req size: %d, line count: %d\n", maxreq, s->lcount); /* XXX investigate this */ if (s->lcount < 3 && (e2_model(s, "GT-X800") || e2_model(s, "GT-X900") || e2_model(s, "GT-X980"))) { s->lcount = 21; DBG(17, "%s: set lcount = %i bigger than sanei_scsi_max_request_size\n", __func__, s->lcount); } if (s->lcount >= 255) s->lcount = 255; /* XXX why this? */ if (s->hw->TPU && s->hw->use_extension && s->lcount > 32) s->lcount = 32; /* * The D1 series of scanners only allow an even line number * for bi-level scanning. If a bit depth of 1 is selected, then * make sure the next lower even number is selected. */ /* XXX check bith depth? */ if (s->hw->cmd->level[0] == 'D' && s->lcount > 3 && s->lcount % 2) s->lcount -= 1; DBG(1, "final line count is %d\n", s->lcount); } SANE_Status e2_init_parameters(Epson_Scanner * s) { int dpi, bytes_per_pixel; struct mode_param *mparam; DBG(5, "%s\n", __func__); memset(&s->params, 0, sizeof(SANE_Parameters)); dpi = s->val[OPT_RESOLUTION].w; mparam = &mode_params[s->val[OPT_MODE].w]; if (SANE_UNFIX(s->val[OPT_BR_Y].w) == 0 || SANE_UNFIX(s->val[OPT_BR_X].w) == 0) return SANE_STATUS_INVAL; s->left = ((SANE_UNFIX(s->val[OPT_TL_X].w) / MM_PER_INCH) * s->val[OPT_RESOLUTION].w) + 0.5; s->top = ((SANE_UNFIX(s->val[OPT_TL_Y].w) / MM_PER_INCH) * s->val[OPT_RESOLUTION].w) + 0.5; s->params.pixels_per_line = ((SANE_UNFIX(s->val[OPT_BR_X].w - s->val[OPT_TL_X].w) / MM_PER_INCH) * dpi) + 0.5; s->params.lines = ((SANE_UNFIX(s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w) / MM_PER_INCH) * dpi) + 0.5; DBG(1, "%s: resolution = %d, preview = %d\n", __func__, s->val[OPT_RESOLUTION].w, s->val[OPT_PREVIEW].w); DBG(1, "%s: %p %p tlx %f tly %f brx %f bry %f [mm]\n", __func__, (void *) s, (void *) s->val, SANE_UNFIX(s->val[OPT_TL_X].w), SANE_UNFIX(s->val[OPT_TL_Y].w), SANE_UNFIX(s->val[OPT_BR_X].w), SANE_UNFIX(s->val[OPT_BR_Y].w)); /* * Calculate bytes_per_pixel and bytes_per_line for * any color depths. * * The default color depth is stored in mode_params.depth: */ if (mode_params[s->val[OPT_MODE].w].depth == 1) s->params.depth = 1; else s->params.depth = s->val[OPT_BIT_DEPTH].w; if (s->params.depth > 8) { s->params.depth = 16; /* * The frontends can only handle 8 or 16 bits * for gray or color - so if it's more than 8, * it gets automatically set to 16. This works * as long as EPSON does not come out with a * scanner that can handle more than 16 bits * per color channel. */ } /* this works because it can only be set to 1, 8 or 16 */ bytes_per_pixel = s->params.depth / 8; if (s->params.depth % 8) { /* just in case ... */ bytes_per_pixel++; } /* pixels_per_line is rounded to the next 8bit boundary */ s->params.pixels_per_line = s->params.pixels_per_line & ~7; s->params.last_frame = SANE_TRUE; switch (s->val[OPT_MODE].w) { case MODE_BINARY: case MODE_GRAY: s->params.format = SANE_FRAME_GRAY; s->params.bytes_per_line = s->params.pixels_per_line * s->params.depth / 8; break; case MODE_COLOR: s->params.format = SANE_FRAME_RGB; s->params.bytes_per_line = 3 * s->params.pixels_per_line * bytes_per_pixel; break; #ifdef SANE_FRAME_IR case MODE_INFRARED: s->params.format = SANE_FRAME_IR; s->params.bytes_per_line = s->params.pixels_per_line * s->params.depth / 8; break; #endif } if (s->params.bytes_per_line == 0) return SANE_STATUS_INVAL; /* * Calculate correction for line_distance in D1 scanner: * Start line_distance lines earlier and add line_distance lines at the end * * Because the actual line_distance is not yet calculated we have to do this * first. */ s->hw->color_shuffle = SANE_FALSE; s->lines_written = 0; s->color_shuffle_line = 0; s->current_output_line = 0; if ((s->hw->optical_res != 0) && (mparam->depth == 8) && (mparam->flags != 0)) { s->line_distance = s->hw->max_line_distance * dpi / s->hw->optical_res; if (s->line_distance != 0) { s->hw->color_shuffle = SANE_TRUE; DBG(1, "%s: color shuffling required\n", __func__); } } /* * If (s->top + s->params.lines) is larger than the max scan area, reset * the number of scan lines: * XXX: precalculate the maximum scanning area elsewhere (use dev max_y) */ if (SANE_UNFIX(s->val[OPT_BR_Y].w) / MM_PER_INCH * dpi < (s->params.lines + s->top)) { s->params.lines = ((int) SANE_UNFIX(s->val[OPT_BR_Y].w) / MM_PER_INCH * dpi + 0.5) - s->top; } s->block = SANE_FALSE; s->lcount = 1; /* * The set line count commands needs to be sent for certain scanners in * color mode. The D1 level requires it, we are however only testing for * 'D' and not for the actual numeric level. */ if ((s->hw->cmd->level[0] == 'B') && (s->hw->level >= 5)) /* >= B5 */ e2_setup_block_mode(s); else if ((s->hw->cmd->level[0] == 'B') && (s->hw->level == 4) /* B4 !color */ && (!mode_params[s->val[OPT_MODE].w].color)) e2_setup_block_mode(s); else if (s->hw->cmd->level[0] == 'D') /* Dx */ e2_setup_block_mode(s); return (s->params.lines > 0) ? SANE_STATUS_GOOD : SANE_STATUS_INVAL; } void e2_wait_button(Epson_Scanner * s) { DBG(5, "%s\n", __func__); s->hw->wait_for_button = SANE_TRUE; while (s->hw->wait_for_button == SANE_TRUE) { unsigned char button_status = 0; if (s->canceling == SANE_TRUE) s->hw->wait_for_button = SANE_FALSE; /* get the button status from the scanner */ else if (esci_request_push_button_status(s, &button_status) == SANE_STATUS_GOOD) { if (button_status) s->hw->wait_for_button = SANE_FALSE; else sleep(1); } else { /* we ran into an error condition, just continue */ s->hw->wait_for_button = SANE_FALSE; } } } /* SANE_Status e2_check_extended_status(Epson_Scanner *s) { SANE_Status status = esci_request_scanner_status(s, buf); if (status != SANE_STATUS_GOOD) return status; if (buf[0] & FSF_STATUS_MAIN_WU) main -> 0 fbf -> 3 adf -> 1, 10 tpu -> 2 } */ SANE_Status e2_check_warm_up(Epson_Scanner * s, SANE_Bool * wup) { SANE_Status status; DBG(5, "%s\n", __func__); *wup = SANE_FALSE; if (s->hw->extended_commands) { unsigned char buf[16]; status = esci_request_scanner_status(s, buf); if (status != SANE_STATUS_GOOD) return status; if (buf[0] & FSF_STATUS_MAIN_WU) *wup = SANE_TRUE; } else { unsigned char *es; /* this command is not available on some scanners */ if (!s->hw->cmd->request_extended_status) return SANE_STATUS_GOOD; status = esci_request_extended_status(s, &es, NULL); if (status != SANE_STATUS_GOOD) return status; if (es[0] & EXT_STATUS_WU) *wup = SANE_TRUE; free(es); } return status; } SANE_Status e2_wait_warm_up(Epson_Scanner * s) { SANE_Status status; SANE_Bool wup; DBG(5, "%s\n", __func__); s->retry_count = 0; while (1) { if (s->canceling) return SANE_STATUS_CANCELLED; status = e2_check_warm_up(s, &wup); if (status != SANE_STATUS_GOOD) return status; if (wup == SANE_FALSE) break; s->retry_count++; if (s->retry_count > SANE_EPSON_MAX_RETRIES) { DBG(1, "max retry count exceeded (%d)\n", s->retry_count); return SANE_STATUS_DEVICE_BUSY; } sleep(5); } return SANE_STATUS_GOOD; } SANE_Status e2_check_adf(Epson_Scanner * s) { SANE_Status status; DBG(5, "%s\n", __func__); if (s->hw->use_extension == SANE_FALSE) return SANE_STATUS_GOOD; if (s->hw->extended_commands) { unsigned char buf[16]; status = esci_request_scanner_status(s, buf); if (status != SANE_STATUS_GOOD) return status; if (buf[1] & FSF_STATUS_ADF_PE) return SANE_STATUS_NO_DOCS; if (buf[1] & FSF_STATUS_ADF_PJ) return SANE_STATUS_JAMMED; } else { unsigned char *buf, t; status = esci_request_extended_status(s, &buf, NULL); if (status != SANE_STATUS_GOOD) return status; t = buf[1]; free(buf); if (t & EXT_STATUS_PE) return SANE_STATUS_NO_DOCS; if (t & EXT_STATUS_PJ) return SANE_STATUS_JAMMED; } return SANE_STATUS_GOOD; } SANE_Status e2_start_std_scan(Epson_Scanner * s) { SANE_Status status; unsigned char params[2]; DBG(5, "%s\n", __func__); /* ESC g */ params[0] = ESC; params[1] = s->hw->cmd->start_scanning; e2_send(s, params, 2, 6 + (s->lcount * s->params.bytes_per_line), &status); return status; } SANE_Status e2_start_ext_scan(Epson_Scanner * s) { SANE_Status status; unsigned char params[2]; unsigned char buf[14]; DBG(5, "%s\n", __func__); params[0] = FS; params[1] = 'G'; status = e2_txrx(s, params, 2, buf, 14); if (status != SANE_STATUS_GOOD) return status; if (buf[0] != STX) return SANE_STATUS_INVAL; if (buf[1] & STATUS_FER) { DBG(1, "%s: fatal error\n", __func__); return SANE_STATUS_IO_ERROR; } /* * The 12000XL signals busy only with FS+G, all other status queries * say non-busy. Probably because you can in deed communicate with the * device, just scanning is not yet possible. I tried polling with FS+G * every 5 seconds, but that made scary noises. So, bail out and let * the user retry manually. */ if (buf[1] & STATUS_NOT_READY) { DBG(1, "%s: device not ready\n", __func__); return SANE_STATUS_DEVICE_BUSY; } s->ext_block_len = le32atoh(&buf[2]); s->ext_blocks = le32atoh(&buf[6]); s->ext_last_len = le32atoh(&buf[10]); s->ext_counter = 0; DBG(5, " status : 0x%02x\n", buf[1]); DBG(5, " block size : %u\n", (unsigned int) le32atoh(&buf[2])); DBG(5, " block count : %u\n", (unsigned int) le32atoh(&buf[6])); DBG(5, " last block size: %u\n", (unsigned int) le32atoh(&buf[10])); if (s->ext_last_len) { s->ext_blocks++; DBG(1, "adjusted block count: %d\n", s->ext_blocks); } /* adjust block len if we have only one block to read */ if (s->ext_block_len == 0 && s->ext_last_len) s->ext_block_len = s->ext_last_len; return status; } void e2_scan_finish(Epson_Scanner * s) { DBG(5, "%s\n", __func__); free(s->buf); s->buf = NULL; if (s->hw->ADF && s->hw->use_extension && s->val[OPT_AUTO_EJECT].w) if (e2_check_adf(s) == SANE_STATUS_NO_DOCS) esci_eject(s); } void e2_copy_image_data(Epson_Scanner * s, SANE_Byte * data, SANE_Int max_length, SANE_Int * length) { if (!s->block && s->params.format == SANE_FRAME_RGB) { max_length /= 3; if (max_length > s->end - s->ptr) max_length = s->end - s->ptr; *length = 3 * max_length; while (max_length-- != 0) { *data++ = s->ptr[0]; *data++ = s->ptr[s->params.pixels_per_line]; *data++ = s->ptr[2 * s->params.pixels_per_line]; ++s->ptr; } } else { if (max_length > s->end - s->ptr) max_length = s->end - s->ptr; *length = max_length; if (s->params.depth == 1) { while (max_length-- != 0) *data++ = ~*s->ptr++; } else { memcpy(data, s->ptr, max_length); s->ptr += max_length; } } } SANE_Status e2_ext_read(struct Epson_Scanner *s) { struct Epson_Device *dev = s->hw; SANE_Status status = SANE_STATUS_GOOD; ssize_t buf_len = 0, read; DBG(18, "%s: begin\n", __func__); /* did we passed everything we read to sane? */ if (s->ptr == s->end) { if (s->eof) return SANE_STATUS_EOF; s->ext_counter++; /* sane has already got the data, read some more, the final * error byte must not be included in buf_len */ buf_len = s->ext_block_len; if (s->ext_counter == s->ext_blocks && s->ext_last_len) buf_len = s->ext_last_len; DBG(18, "%s: block %d/%d, size %lu\n", __func__, s->ext_counter, s->ext_blocks, (unsigned long) buf_len); /* receive image data + error code */ read = e2_recv(s, s->buf, buf_len + 1, &status); DBG(18, "%s: read %lu bytes, status: %d\n", __func__, (unsigned long) read, status); if (status != SANE_STATUS_GOOD) { e2_cancel(s); return status; } if (e2_dev_model(dev, "GT-8200") || e2_dev_model(dev, "Perfection1650")) { /* See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=597922#127 */ s->buf[buf_len] &= 0xc0; } if (s->buf[buf_len] & FSG_STATUS_CANCEL_REQ) { DBG(0, "%s: cancel request received\n", __func__); e2_cancel(s); return SANE_STATUS_CANCELLED; } if (s->buf[buf_len] & (FSG_STATUS_FER | FSG_STATUS_NOT_READY)) return SANE_STATUS_IO_ERROR; /* ack every block except the last one */ if (s->ext_counter < s->ext_blocks) { size_t next_len = s->ext_block_len; if (s->ext_counter == (s->ext_blocks - 1)) next_len = s->ext_last_len; if (s->canceling) { e2_cancel(s); return SANE_STATUS_CANCELLED; } status = e2_ack_next(s, next_len + 1); } else s->eof = SANE_TRUE; s->end = s->buf + buf_len; s->ptr = s->buf; } return status; } /* XXXX use routine from sane-evolution */ typedef struct { unsigned char code; unsigned char status; unsigned char buf[4]; } EpsonDataRec; /* XXX this routine is ugly and should be avoided */ static SANE_Status read_info_block(Epson_Scanner * s, EpsonDataRec * result) { SANE_Status status; unsigned char params[2]; retry: e2_recv(s, result, s->block ? 6 : 4, &status); if (status != SANE_STATUS_GOOD) return status; if (result->code != STX) { DBG(1, "error: got %02x, expected STX\n", result->code); return SANE_STATUS_INVAL; } /* XXX */ if (result->status & STATUS_FER) { unsigned char *ext_status; DBG(1, "fatal error, status = %02x\n", result->status); if (s->retry_count > SANE_EPSON_MAX_RETRIES) { DBG(1, "max retry count exceeded (%d)\n", s->retry_count); return SANE_STATUS_INVAL; } /* if the scanner is warming up, retry after a few secs */ status = esci_request_extended_status(s, &ext_status, NULL); if (status != SANE_STATUS_GOOD) return status; if (ext_status[0] & EXT_STATUS_WU) { free(ext_status); sleep(5); /* for the next attempt */ DBG(1, "retrying ESC G - %d\n", ++(s->retry_count)); params[0] = ESC; params[1] = s->hw->cmd->start_scanning; e2_send(s, params, 2, 0, &status); if (status != SANE_STATUS_GOOD) return status; goto retry; } else free(ext_status); } return status; } static SANE_Status color_shuffle(SANE_Handle handle, int *new_length) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Byte *buf = s->buf; int length = s->end - s->buf; SANE_Byte *data_ptr; /* ptr to data to process */ SANE_Byte *data_end; /* ptr to end of processed data */ SANE_Byte *out_data_ptr; /* ptr to memory when writing data */ int i; /* loop counter */ /* * It looks like we are dealing with a scanner that has an odd way * of dealing with colors... The red and blue scan lines are shifted * up or down by a certain number of lines relative to the green line. */ DBG(5, "%s\n", __func__); /* * Initialize the variables we are going to use for the * copying of the data. data_ptr is the pointer to * the currently worked on scan line. data_end is the * end of the data area as calculated from adding *length * to the start of data. * out_data_ptr is used when writing out the processed data * and always points to the beginning of the next line to * write. */ data_ptr = out_data_ptr = buf; data_end = data_ptr + length; /* * The image data is in *buf, we know that the buffer contains s->end - s->buf ( = length) * bytes of data. The width of one line is in s->params.bytes_per_line * * The buffer area is supposed to have a number of full scan * lines, let's test if this is the case. */ if (length % s->params.bytes_per_line != 0) { DBG(1, "error in buffer size: %d / %d\n", length, s->params.bytes_per_line); return SANE_STATUS_INVAL; } while (data_ptr < data_end) { SANE_Byte *source_ptr, *dest_ptr; int loop; /* copy the green information into the current line */ source_ptr = data_ptr + 1; dest_ptr = s->line_buffer[s->color_shuffle_line] + 1; for (i = 0; i < s->params.bytes_per_line / 3; i++) { *dest_ptr = *source_ptr; dest_ptr += 3; source_ptr += 3; } /* copy the red information n lines back */ if (s->color_shuffle_line >= s->line_distance) { source_ptr = data_ptr + 2; dest_ptr = s->line_buffer[s->color_shuffle_line - s->line_distance] + 2; /* while (source_ptr < s->line_buffer[s->color_shuffle_line] + s->params.bytes_per_line) */ for (loop = 0; loop < s->params.bytes_per_line / 3; loop++) { *dest_ptr = *source_ptr; dest_ptr += 3; source_ptr += 3; } } /* copy the blue information n lines forward */ source_ptr = data_ptr; dest_ptr = s->line_buffer[s->color_shuffle_line + s->line_distance]; /* while (source_ptr < s->line_buffer[s->color_shuffle_line] + s->params.bytes_per_line) */ for (loop = 0; loop < s->params.bytes_per_line / 3; loop++) { *dest_ptr = *source_ptr; dest_ptr += 3; source_ptr += 3; } data_ptr += s->params.bytes_per_line; if (s->color_shuffle_line == s->line_distance) { /* * We just finished the line in line_buffer[0] - write it to the * output buffer and continue. * * The output buffer is still "buf", but because we are * only overwriting from the beginning of the memory area * we are not interfering with the "still to shuffle" data * in the same area. */ /* * Strip the first and last n lines and limit to */ if ((s->current_output_line >= s->line_distance) && (s->current_output_line < s->params.lines + s->line_distance)) { memcpy(out_data_ptr, s->line_buffer[0], s->params.bytes_per_line); out_data_ptr += s->params.bytes_per_line; s->lines_written++; } s->current_output_line++; /* * Now remove the 0-entry and move all other * lines up by one. There are 2*line_distance + 1 * buffers, * therefore the loop has to run from 0 * to * 2*line_distance, and because we want to * copy every n+1st entry to n the loop runs * from - to 2*line_distance-1! */ free(s->line_buffer[0]); for (i = 0; i < s->line_distance * 2; i++) { s->line_buffer[i] = s->line_buffer[i + 1]; } /* * and create one new buffer at the end */ s->line_buffer[s->line_distance * 2] = malloc(s->params.bytes_per_line); if (s->line_buffer[s->line_distance * 2] == NULL) { DBG(1, "out of memory (line %d)\n", __LINE__); return SANE_STATUS_NO_MEM; } } else { s->color_shuffle_line++; /* increase the buffer number */ } } /* * At this time we've used up all the new data from the scanner, some of * it is still in the line_buffers, but we are ready to return some of it * to the front end software. To do so we have to adjust the size of the * data area and the *new_length variable. */ *new_length = out_data_ptr - buf; return SANE_STATUS_GOOD; } static inline int get_color(int status) { switch ((status >> 2) & 0x03) { case 1: return 1; case 2: return 0; case 3: return 2; default: return 0; /* required to make the compiler happy */ } } SANE_Status e2_block_read(struct Epson_Scanner *s) { SANE_Status status; SANE_Bool reorder = SANE_FALSE; START_READ: DBG(18, "%s: begin\n", __func__); if (s->ptr == s->end) { EpsonDataRec result; unsigned int buf_len; if (s->eof) { if (s->hw->color_shuffle) { DBG(1, "written %d lines after color shuffle\n", s->lines_written); DBG(1, "lines requested: %d\n", s->params.lines); } return SANE_STATUS_EOF; } status = read_info_block(s, &result); if (status != SANE_STATUS_GOOD) { return status; } buf_len = result.buf[1] << 8 | result.buf[0]; buf_len *= (result.buf[3] << 8 | result.buf[2]); DBG(18, "%s: buf len = %u\n", __func__, buf_len); { /* do we have to reorder the data ? */ if (get_color(result.status) == 0x01) reorder = SANE_TRUE; e2_recv(s, s->buf, buf_len, &status); if (status != SANE_STATUS_GOOD) { return status; } } if (result.status & STATUS_AREA_END) { DBG(1, "%s: EOF\n", __func__); s->eof = SANE_TRUE; } else { if (s->canceling) { e2_cancel(s); return SANE_STATUS_CANCELLED; } else { status = e2_ack(s); } } s->end = s->buf + buf_len; s->ptr = s->buf; /* * if we have to re-order the color components (GRB->RGB) we * are doing this here: */ /* * Certain Perfection 1650 also need this re-ordering of the two * color channels. These scanners are identified by the problem * with the half vertical scanning area. When we corrected this, * we also set the variable s->hw->need_color_reorder */ if (s->hw->need_color_reorder) reorder = SANE_FALSE; /* reordering once is enough */ if (reorder && s->params.format == SANE_FRAME_RGB) { SANE_Byte *ptr; ptr = s->buf; while (ptr < s->end) { if (s->params.depth > 8) { SANE_Byte tmp; /* R->G G->R */ tmp = ptr[0]; ptr[0] = ptr[2]; /* first Byte G */ ptr[2] = tmp; /* first Byte R */ tmp = ptr[1]; ptr[1] = ptr[3]; /* second Byte G */ ptr[3] = tmp; /* second Byte R */ ptr += 6; /* go to next pixel */ } else { /* R->G G->R */ SANE_Byte tmp; tmp = ptr[0]; ptr[0] = ptr[1]; /* G */ ptr[1] = tmp; /* R */ /* B stays the same */ ptr += 3; /* go to next pixel */ } } } /* * Do the color_shuffle if everything else is correct - at this time * most of the stuff is hardcoded for the Perfection 610 */ if (s->hw->color_shuffle) { int new_length = 0; status = color_shuffle(s, &new_length); /* XXX check status here */ /* * If no bytes are returned, check if the scanner is already done, if so, * we'll probably just return, but if there is more data to process get * the next batch. */ if (new_length == 0 && s->end != s->ptr) goto START_READ; s->end = s->buf + new_length; s->ptr = s->buf; } DBG(18, "%s: begin scan2\n", __func__); } DBG(18, "%s: end\n", __func__); return SANE_STATUS_GOOD; } backends-1.3.0/backend/epson2-ops.h000066400000000000000000000047071456256263500170600ustar00rootroot00000000000000/* * epson2.c - SANE library for Epson scanners. * * Based on Kazuhiro Sasayama previous * Work on epson.[ch] file from the SANE package. * Please see those files for additional copyrights. * * Copyright (C) 2006-07 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ /* some defines to make handling the TPU easier */ #define FILM_TYPE_NEGATIVE (1L << 0) #define FILM_TYPE_SLIDE (1L << 1) #define e2_model(s,m) e2_dev_model((s)->hw,(m)) extern int sanei_scsi_max_request_size; extern int *gamma_params; extern void e2_dev_init(Epson_Device *dev, const char *devname, int conntype); extern SANE_Status e2_dev_post_init(struct Epson_Device *dev); extern SANE_Bool e2_dev_model(Epson_Device *dev, const char *model); extern void e2_set_cmd_level(SANE_Handle handle, unsigned char *level); extern SANE_Status e2_set_model(Epson_Scanner *s, unsigned char *model, size_t len); extern SANE_Status e2_add_resolution(Epson_Device *dev, int r); extern void e2_set_fbf_area(Epson_Scanner *s, int x, int y, int unit); extern void e2_set_adf_area(struct Epson_Scanner *s, int x, int y, int unit); extern void e2_set_tpu_area(struct Epson_Scanner *s, int x, int y, int unit); extern void e2_set_tpu2_area(struct Epson_Scanner *s, int x, int y, int unit); extern void e2_add_depth(Epson_Device *dev, SANE_Word depth); extern SANE_Status e2_discover_capabilities(Epson_Scanner *s); extern SANE_Status e2_set_extended_scanning_parameters(Epson_Scanner *s); extern SANE_Status e2_set_scanning_parameters(Epson_Scanner *s); extern void e2_setup_block_mode(Epson_Scanner *s); extern SANE_Status e2_init_parameters(Epson_Scanner *s); extern void e2_wait_button(Epson_Scanner *s); extern SANE_Status e2_check_warm_up(Epson_Scanner *s, SANE_Bool *wup); extern SANE_Status e2_wait_warm_up(Epson_Scanner *s); extern SANE_Status e2_check_adf(Epson_Scanner *s); extern SANE_Status e2_start_std_scan(Epson_Scanner *s); extern SANE_Status e2_start_ext_scan(Epson_Scanner *s); extern void e2_scan_finish(Epson_Scanner *s); extern void e2_copy_image_data(Epson_Scanner *s, SANE_Byte *data, SANE_Int max_length, SANE_Int *length); extern SANE_Status e2_ext_read(struct Epson_Scanner *s); extern SANE_Status e2_block_read(struct Epson_Scanner *s); backends-1.3.0/backend/epson2.c000066400000000000000000001726641456256263500162640ustar00rootroot00000000000000/* * epson2.c - SANE library for Epson scanners. * * Based on Kazuhiro Sasayama previous * Work on epson.[ch] file from the SANE package. * Please see those files for additional copyrights. * * Copyright (C) 2006-10 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ /* debugging levels: * * 127 e2_recv buffer * 125 e2_send buffer * 32 more network progression * 24 network header * 23 network info * 20 usb cmd counters * 18 sane_read * 17 setvalue, getvalue, control_option * 16 gamma table * 15 e2_send, e2_recv calls * 13 e2_cmd_info_block * 12 epson_cmd_simple * 11 even more * 10 more debug in ESC/I commands * 9 ESC x/FS x in e2_send * 8 ESC/I commands * 7 open/close/attach * 6 print_params * 5 basic functions * 3 status information * 1 scanner info and capabilities * warnings */ #include "sane/config.h" #include "epson2.h" #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_SOCKET_H #include #endif #include "sane/saneopts.h" #include "sane/sanei_scsi.h" #include "sane/sanei_usb.h" #include "sane/sanei_pio.h" #include "sane/sanei_tcp.h" #include "sane/sanei_udp.h" #include "sane/sanei_backend.h" #include "sane/sanei_config.h" #include "epson2-io.h" #include "epson2-commands.h" #include "epson2-ops.h" #include "epson2_scsi.h" #include "epson_usb.h" #include "epson2_net.h" /* * Definition of the mode_param struct, that is used to * specify the valid parameters for the different scan modes. * * The depth variable gets updated when the bit depth is modified. */ struct mode_param mode_params[] = { {0, 0x00, 0x30, 1}, {0, 0x00, 0x30, 8}, {1, 0x02, 0x00, 8}, {0, 0x00, 0x30, 1} }; static SANE_String_Const mode_list[] = { SANE_VALUE_SCAN_MODE_LINEART, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_COLOR, #ifdef SANE_FRAME_IR SANE_I18N("Infrared"), #endif NULL }; static const SANE_String_Const adf_mode_list[] = { SANE_I18N("Simplex"), SANE_I18N("Duplex"), NULL }; /* Define the different scan sources */ #define FBF_STR SANE_I18N("Flatbed") #define TPU_STR SANE_I18N("Transparency Unit") #define TPU_STR2 SANE_I18N("TPU8x10") #define ADF_STR SANE_I18N("Automatic Document Feeder") /* * source list need one dummy entry (save device settings is crashing). * NOTE: no const - this list gets created while exploring the capabilities * of the scanner. */ SANE_String_Const source_list[] = { FBF_STR, NULL, NULL, NULL }; static const SANE_String_Const film_list[] = { SANE_I18N("Positive Film"), SANE_I18N("Negative Film"), SANE_I18N("Positive Slide"), SANE_I18N("Negative Slide"), NULL }; #define HALFTONE_NONE 0x01 #define HALFTONE_TET 0x03 const int halftone_params[] = { HALFTONE_NONE, 0x00, 0x10, 0x20, 0x80, 0x90, 0xa0, 0xb0, HALFTONE_TET, 0xc0, 0xd0 }; static const SANE_String_Const halftone_list[] = { SANE_I18N("None"), SANE_I18N("Halftone A (Hard Tone)"), SANE_I18N("Halftone B (Soft Tone)"), SANE_I18N("Halftone C (Net Screen)"), NULL }; static const SANE_String_Const halftone_list_4[] = { SANE_I18N("None"), SANE_I18N("Halftone A (Hard Tone)"), SANE_I18N("Halftone B (Soft Tone)"), SANE_I18N("Halftone C (Net Screen)"), SANE_I18N("Dither A (4x4 Bayer)"), SANE_I18N("Dither B (4x4 Spiral)"), SANE_I18N("Dither C (4x4 Net Screen)"), SANE_I18N("Dither D (8x4 Net Screen)"), NULL }; static const SANE_String_Const halftone_list_7[] = { SANE_I18N("None"), SANE_I18N("Halftone A (Hard Tone)"), SANE_I18N("Halftone B (Soft Tone)"), SANE_I18N("Halftone C (Net Screen)"), SANE_I18N("Dither A (4x4 Bayer)"), SANE_I18N("Dither B (4x4 Spiral)"), SANE_I18N("Dither C (4x4 Net Screen)"), SANE_I18N("Dither D (8x4 Net Screen)"), SANE_I18N("Text Enhanced Technology"), SANE_I18N("Download pattern A"), SANE_I18N("Download pattern B"), NULL }; static const SANE_String_Const dropout_list[] = { SANE_I18N("None"), SANE_I18N("Red"), SANE_I18N("Green"), SANE_I18N("Blue"), NULL }; static const SANE_Bool correction_userdefined[] = { SANE_FALSE, SANE_TRUE, SANE_TRUE, }; static const SANE_String_Const correction_list[] = { SANE_I18N("None"), SANE_I18N("Built in CCT profile"), SANE_I18N("User defined CCT profile"), NULL }; enum { CORR_NONE, CORR_AUTO, CORR_USER }; static const SANE_String_Const cct_mode_list[] = { "Automatic", "Reflective", "Colour negatives", "Monochrome negatives", "Colour positives", NULL }; enum { CCT_AUTO, CCT_REFLECTIVE, CCT_COLORNEG, CCT_MONONEG, CCT_COLORPOS }; /* * Gamma correction: * The A and B level scanners work differently than the D level scanners, * therefore I define two different sets of arrays, plus one set of * variables that get set to the actually used params and list arrays at runtime. */ static int gamma_params_ab[] = { 0x01, 0x03, 0x00, 0x10, 0x20 }; static const SANE_String_Const gamma_list_ab[] = { SANE_I18N("Default"), SANE_I18N("User defined"), SANE_I18N("High density printing"), SANE_I18N("Low density printing"), SANE_I18N("High contrast printing"), NULL }; static SANE_Bool gamma_userdefined_ab[] = { SANE_FALSE, SANE_TRUE, SANE_FALSE, SANE_FALSE, SANE_FALSE, }; static int gamma_params_d[] = { 0x03, 0x04 }; static const SANE_String_Const gamma_list_d[] = { SANE_I18N("User defined (Gamma=1.0)"), SANE_I18N("User defined (Gamma=1.8)"), NULL }; static SANE_Bool gamma_userdefined_d[] = { SANE_TRUE, SANE_TRUE }; static SANE_Bool *gamma_userdefined; int *gamma_params; /* Bay list: * this is used for the FilmScan * XXX Add APS loader support */ static const SANE_String_Const bay_list[] = { "1", "2", "3", "4", "5", "6", NULL }; /* minimum, maximum, quantization */ static const SANE_Range focus_range = { 0, 254, 0 }; static const SANE_Range u8_range = { 0, 255, 0 }; static const SANE_Range fx_range = { SANE_FIX(-2.0), SANE_FIX(2.0), 0 }; static const SANE_Range outline_emphasis_range = { -2, 2, 0 }; /* * List of pointers to devices - will be dynamically allocated depending * on the number of devices found. */ static const SANE_Device **devlist; /* Some utility functions */ static size_t max_string_size(const SANE_String_Const strings[]) { size_t size, max_size = 0; int i; for (i = 0; strings[i]; i++) { size = strlen(strings[i]) + 1; if (size > max_size) max_size = size; } return max_size; } static SANE_Status attach_one_usb(SANE_String_Const devname); static SANE_Status attach_one_net(SANE_String_Const devname); static void print_params(const SANE_Parameters params) { DBG(6, "params.format = %d\n", params.format); DBG(6, "params.last_frame = %d\n", params.last_frame); DBG(6, "params.bytes_per_line = %d\n", params.bytes_per_line); DBG(6, "params.pixels_per_line = %d\n", params.pixels_per_line); DBG(6, "params.lines = %d\n", params.lines); DBG(6, "params.depth = %d\n", params.depth); } /* * close_scanner() * * Close the open scanner. Depending on the connection method, a different * close function is called. */ static void close_scanner(Epson_Scanner *s) { int i; DBG(7, "%s: fd = %d\n", __func__, s->fd); if (s->fd == -1) goto free; /* send a request_status. This toggles w_cmd_count and r_cmd_count */ if (r_cmd_count % 2) esci_request_status(s, NULL); /* request extended status. This toggles w_cmd_count only */ if (w_cmd_count % 2) esci_request_extended_status(s, NULL, NULL); if (s->hw->connection == SANE_EPSON_NET) { sanei_epson_net_unlock(s); sanei_tcp_close(s->fd); } else if (s->hw->connection == SANE_EPSON_SCSI) { sanei_scsi_close(s->fd); } else if (s->hw->connection == SANE_EPSON_PIO) { sanei_pio_close(s->fd); } else if (s->hw->connection == SANE_EPSON_USB) { sanei_usb_close(s->fd); } s->fd = -1; free: for (i = 0; i < LINES_SHUFFLE_MAX; i++) { if (s->line_buffer[i] != NULL) free(s->line_buffer[i]); } free(s); } static void e2_network_discovery(void) { fd_set rfds; int fd, len; SANE_Status status; char *ip, *query = "EPSONP\x00\xff\x00\x00\x00\x00\x00\x00\x00"; unsigned char buf[76]; struct timeval to; status = sanei_udp_open_broadcast(&fd); if (status != SANE_STATUS_GOOD) return; sanei_udp_write_broadcast(fd, 3289, (unsigned char *) query, 15); DBG(5, "%s, sent discovery packet\n", __func__); to.tv_sec = 1; to.tv_usec = 0; FD_ZERO(&rfds); FD_SET(fd, &rfds); sanei_udp_set_nonblock(fd, SANE_TRUE); while (select(fd + 1, &rfds, NULL, NULL, &to) > 0) { if ((len = sanei_udp_recvfrom(fd, buf, 76, &ip)) == 76) { DBG(5, " response from %s\n", ip); /* minimal check, protocol unknown */ if (strncmp((char *) buf, "EPSON", 5) == 0) attach_one_net(ip); } } DBG(5, "%s, end\n", __func__); sanei_udp_close(fd); } /* * open_scanner() * * Open the scanner device. Depending on the connection method, * different open functions are called. */ static SANE_Status open_scanner(Epson_Scanner *s) { SANE_Status status = 0; DBG(7, "%s: %s\n", __func__, s->hw->sane.name); if (s->fd != -1) { DBG(5, "scanner is already open: fd = %d\n", s->fd); return SANE_STATUS_GOOD; /* no need to open the scanner */ } if (s->hw->connection == SANE_EPSON_NET) { unsigned char buf[5]; /* device name has the form net:ipaddr */ status = sanei_tcp_open(&s->hw->sane.name[4], 1865, &s->fd); if (status == SANE_STATUS_GOOD) { ssize_t read; struct timeval tv; tv.tv_sec = 5; tv.tv_usec = 0; setsockopt(s->fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)); s->netlen = 0; DBG(32, "awaiting welcome message\n"); /* the scanner sends a kind of welcome msg */ read = e2_recv(s, buf, 5, &status); if (read != 5) { sanei_tcp_close(s->fd); s->fd = -1; return SANE_STATUS_IO_ERROR; } DBG(32, "welcome message received, locking the scanner...\n"); /* lock the scanner for use by sane */ status = sanei_epson_net_lock(s); if (status != SANE_STATUS_GOOD) { DBG(1, "%s cannot lock scanner: %s\n", s->hw->sane.name, sane_strstatus(status)); sanei_tcp_close(s->fd); s->fd = -1; return status; } DBG(32, "scanner locked\n"); } } else if (s->hw->connection == SANE_EPSON_SCSI) status = sanei_scsi_open(s->hw->sane.name, &s->fd, sanei_epson2_scsi_sense_handler, NULL); else if (s->hw->connection == SANE_EPSON_PIO) /* device name has the form pio:0xnnn */ status = sanei_pio_open(&s->hw->sane.name[4], &s->fd); else if (s->hw->connection == SANE_EPSON_USB) status = sanei_usb_open(s->hw->sane.name, &s->fd); if (status == SANE_STATUS_ACCESS_DENIED) { DBG(1, "please check that you have permissions on the device.\n"); DBG(1, "if this is a multi-function device with a printer,\n"); DBG(1, "disable any conflicting driver (like usblp).\n"); } if (status != SANE_STATUS_GOOD) DBG(1, "%s open failed: %s\n", s->hw->sane.name, sane_strstatus(status)); else DBG(5, "scanner opened\n"); return status; } static SANE_Status detect_scsi(struct Epson_Scanner *s) { SANE_Status status; struct Epson_Device *dev = s->hw; char buf[INQUIRY_BUF_SIZE + 1]; size_t buf_size = INQUIRY_BUF_SIZE; char *vendor = buf + 8; char *model = buf + 16; char *rev = buf + 32; status = sanei_epson2_scsi_inquiry(s->fd, buf, &buf_size); if (status != SANE_STATUS_GOOD) { DBG(1, "%s: inquiry failed: %s\n", __func__, sane_strstatus(status)); return status; } buf[INQUIRY_BUF_SIZE] = 0; DBG(1, "inquiry data:\n"); DBG(1, " vendor : %.8s\n", vendor); DBG(1, " model : %.16s\n", model); DBG(1, " revision: %.4s\n", rev); if (buf[0] != TYPE_PROCESSOR) { DBG(1, "%s: device is not of processor type (%d)\n", __func__, buf[0]); return SANE_STATUS_INVAL; } if (strncmp(vendor, "EPSON", 5) != 0) { DBG(1, "%s: device doesn't look like an EPSON scanner\n", __func__); return SANE_STATUS_INVAL; } if (strncmp(model, "SCANNER ", 8) != 0 && strncmp(model, "FilmScan 200", 12) != 0 && strncmp(model, "Perfection", 10) != 0 && strncmp(model, "Expression", 10) != 0 && strncmp(model, "GT", 2) != 0) { DBG(1, "%s: this EPSON scanner is not supported\n", __func__); return SANE_STATUS_INVAL; } if (strncmp(model, "FilmScan 200", 12) == 0) { dev->sane.type = "film scanner"; e2_set_model(s, (unsigned char *) model, 12); } /* Issue a test unit ready SCSI command. The FilmScan 200 * requires it for a sort of "wake up". We might eventually * get the return code and reissue it in case of failure. */ sanei_epson2_scsi_test_unit_ready(s->fd); return SANE_STATUS_GOOD; } static SANE_Status detect_usb(struct Epson_Scanner *s, SANE_Bool assume_valid) { SANE_Status status; int vendor, product; int i, numIds; SANE_Bool is_valid = assume_valid; /* if the sanei_usb_get_vendor_product call is not supported, * then we just ignore this and rely on the user to config * the correct device. */ status = sanei_usb_get_vendor_product(s->fd, &vendor, &product); if (status != SANE_STATUS_GOOD) { DBG(1, "the device cannot be verified - will continue\n"); return SANE_STATUS_GOOD; } /* check the vendor ID to see if we are dealing with an EPSON device */ if (vendor != SANE_EPSON_VENDOR_ID) { /* this is not a supported vendor ID */ DBG(1, "not an Epson device at %s (vendor id=0x%x)\n", s->hw->sane.name, vendor); return SANE_STATUS_INVAL; } numIds = sanei_epson_getNumberOfUSBProductIds(); i = 0; /* check all known product IDs to verify that we know about the device */ while (i != numIds) { if (product == sanei_epson_usb_product_ids[i]) { is_valid = SANE_TRUE; break; } i++; } if (is_valid == SANE_FALSE) { DBG(1, "the device at %s is not supported (product id=0x%x)\n", s->hw->sane.name, product); return SANE_STATUS_INVAL; } DBG(1, "found valid Epson scanner: 0x%x/0x%x (vendorID/productID)\n", vendor, product); return SANE_STATUS_GOOD; } static int num_devices; /* number of scanners attached to backend */ static Epson_Device *first_dev; /* first EPSON scanner in list */ static struct Epson_Scanner * scanner_create(struct Epson_Device *dev, SANE_Status *status) { struct Epson_Scanner *s; s = malloc(sizeof(struct Epson_Scanner)); if (s == NULL) { *status = SANE_STATUS_NO_MEM; return NULL; } memset(s, 0x00, sizeof(struct Epson_Scanner)); s->fd = -1; s->hw = dev; return s; } static struct Epson_Scanner * device_detect(const char *name, int type, SANE_Bool assume_valid, SANE_Status *status) { struct Epson_Scanner *s; struct Epson_Device *dev; /* try to find the device in our list */ for (dev = first_dev; dev; dev = dev->next) { if (strcmp(dev->sane.name, name) == 0) { /* the device might have been just probed, * sleep a bit. */ if (dev->connection == SANE_EPSON_NET) sleep(1); return scanner_create(dev, status); } } if (type == SANE_EPSON_NODEV) { *status = SANE_STATUS_INVAL; return NULL; } /* alloc and clear our device structure */ dev = malloc(sizeof(*dev)); if (!dev) { *status = SANE_STATUS_NO_MEM; return NULL; } memset(dev, 0x00, sizeof(struct Epson_Device)); s = scanner_create(dev, status); if (s == NULL) return NULL; e2_dev_init(dev, name, type); *status = open_scanner(s); if (*status != SANE_STATUS_GOOD) { free(s); return NULL; } /* from now on, close_scanner() must be called */ /* SCSI and USB requires special care */ if (dev->connection == SANE_EPSON_SCSI) { *status = detect_scsi(s); } else if (dev->connection == SANE_EPSON_USB) { *status = detect_usb(s, assume_valid); } if (*status != SANE_STATUS_GOOD) goto close; /* set name and model (if not already set) */ if (dev->model == NULL) e2_set_model(s, (unsigned char *) "generic", 7); dev->name = strdup(name); dev->sane.name = dev->name; /* ESC @, reset */ *status = esci_reset(s); if (*status != SANE_STATUS_GOOD) goto close; *status = e2_discover_capabilities(s); if (*status != SANE_STATUS_GOOD) goto close; if (source_list[0] == NULL || dev->dpi_range.min == 0) { DBG(1, "something is wrong in the discovery process, aborting.\n"); *status = SANE_STATUS_IO_ERROR; goto close; } e2_dev_post_init(dev); *status = esci_reset(s); if (*status != SANE_STATUS_GOOD) goto close; DBG(1, "scanner model: %s\n", dev->model); /* add this scanner to the device list */ num_devices++; dev->next = first_dev; first_dev = dev; return s; close: close_scanner(s); return NULL; } static SANE_Status attach(const char *name, int type) { SANE_Status status; Epson_Scanner *s; DBG(7, "%s: devname = %s, type = %d\n", __func__, name, type); s = device_detect(name, type, 0, &status); if(s == NULL) return status; close_scanner(s); return status; } static SANE_Status attach_one_scsi(const char *dev) { DBG(7, "%s: dev = %s\n", __func__, dev); return attach(dev, SANE_EPSON_SCSI); } SANE_Status attach_one_usb(const char *dev) { DBG(7, "%s: dev = %s\n", __func__, dev); return attach(dev, SANE_EPSON_USB); } static SANE_Status attach_one_net(const char *dev) { char name[39+4]; DBG(7, "%s: dev = %s\n", __func__, dev); strcpy(name, "net:"); strcat(name, dev); return attach(name, SANE_EPSON_NET); } static SANE_Status attach_one_pio(const char *dev) { DBG(7, "%s: dev = %s\n", __func__, dev); return attach(dev, SANE_EPSON_PIO); } static SANE_Status attach_one_config(SANEI_Config __sane_unused__ *config, const char *line, void *data) { int vendor, product; SANE_Bool local_only = *(SANE_Bool*) data; int len = strlen(line); DBG(7, "%s: len = %d, line = %s\n", __func__, len, line); if (sscanf(line, "usb %i %i", &vendor, &product) == 2) { /* add the vendor and product IDs to the list of known devices before we call the attach function */ int numIds = sanei_epson_getNumberOfUSBProductIds(); if (vendor != 0x4b8) return SANE_STATUS_INVAL; /* this is not an EPSON device */ sanei_epson_usb_product_ids[numIds - 1] = product; sanei_usb_attach_matching_devices(line, attach_one_usb); } else if (strncmp(line, "usb", 3) == 0 && len == 3) { int i, numIds; numIds = sanei_epson_getNumberOfUSBProductIds(); for (i = 0; i < numIds; i++) { sanei_usb_find_devices(0x4b8, sanei_epson_usb_product_ids[i], attach_one_usb); } } else if (strncmp(line, "net", 3) == 0) { if (!local_only) { /* remove the "net" sub string */ const char *name = sanei_config_skip_whitespace(line + 3); if (strncmp(name, "autodiscovery", 13) == 0) e2_network_discovery(); else attach_one_net(name); } } else if (strncmp(line, "pio", 3) == 0) { /* remove the "pio" sub string */ const char *name = sanei_config_skip_whitespace(line + 3); attach_one_pio(name); } else { sanei_config_attach_matching_devices(line, attach_one_scsi); } return SANE_STATUS_GOOD; } static void free_devices(void) { Epson_Device *dev, *next; DBG(5, "%s\n", __func__); for (dev = first_dev; dev; dev = next) { next = dev->next; free(dev->name); free(dev->model); free(dev); } free(devlist); first_dev = NULL; } static void probe_devices(SANE_Bool local_only) { DBG(5, "%s\n", __func__); free_devices(); sanei_configure_attach(EPSON2_CONFIG_FILE, NULL, attach_one_config, &local_only); } SANE_Status sane_init(SANE_Int *version_code, SANE_Auth_Callback __sane_unused__ authorize) { DBG_INIT(); DBG(1, "%s: version " VERSION "\n", __func__); /* Keep '124' as our build version. The arg is obsolete by now */ if (version_code) *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 124); sanei_usb_init(); sanei_usb_set_timeout(60 * 1000); return SANE_STATUS_GOOD; } /* Clean up the list of attached scanners. */ void sane_exit(void) { DBG(5, "%s\n", __func__); free_devices(); } SANE_Status sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only) { Epson_Device *dev; int i; DBG(5, "%s\n", __func__); probe_devices(local_only); devlist = malloc((num_devices + 1) * sizeof(devlist[0])); if (!devlist) { DBG(1, "out of memory (line %d)\n", __LINE__); return SANE_STATUS_NO_MEM; } DBG(5, "%s - results:\n", __func__); for (i = 0, dev = first_dev; i < num_devices && dev; dev = dev->next, i++) { DBG(1, " %d (%d): %s\n", i, dev->connection, dev->model); devlist[i] = &dev->sane; } devlist[i] = NULL; *device_list = devlist; return SANE_STATUS_GOOD; } static SANE_Status init_options(Epson_Scanner *s) { int i; for (i = 0; i < NUM_OPTIONS; i++) { s->opt[i].size = sizeof(SANE_Word); s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* "Scan Mode" group: */ s->opt[OPT_MODE_GROUP].title = SANE_I18N("Scan Mode"); s->opt[OPT_MODE_GROUP].desc = ""; s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MODE_GROUP].cap = 0; /* scan mode */ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; 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 = max_string_size(mode_list); s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_MODE].constraint.string_list = mode_list; s->val[OPT_MODE].w = 0; /* Lineart */ /* disable infrared on unsupported scanners */ if (!e2_model(s, "GT-X800") && !e2_model(s, "GT-X700") && !e2_model(s, "GT-X900") && !e2_model(s, "GT-X980")) mode_list[MODE_INFRARED] = NULL; /* bit depth */ s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT; s->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_BIT; s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_BIT_DEPTH].constraint.word_list = s->hw->depth_list; s->val[OPT_BIT_DEPTH].w = 8; /* default to 8 bit */ /* default is Lineart, disable depth selection */ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; /* halftone */ s->opt[OPT_HALFTONE].name = SANE_NAME_HALFTONE; s->opt[OPT_HALFTONE].title = SANE_TITLE_HALFTONE; s->opt[OPT_HALFTONE].desc = SANE_I18N("Selects the halftone."); s->opt[OPT_HALFTONE].type = SANE_TYPE_STRING; s->opt[OPT_HALFTONE].size = max_string_size(halftone_list_7); s->opt[OPT_HALFTONE].constraint_type = SANE_CONSTRAINT_STRING_LIST; /* XXX use defines */ if (s->hw->level >= 7) s->opt[OPT_HALFTONE].constraint.string_list = halftone_list_7; else if (s->hw->level >= 4) s->opt[OPT_HALFTONE].constraint.string_list = halftone_list_4; else s->opt[OPT_HALFTONE].constraint.string_list = halftone_list; s->val[OPT_HALFTONE].w = 1; /* Halftone A */ if (!s->hw->cmd->set_halftoning) s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE; /* dropout */ s->opt[OPT_DROPOUT].name = "dropout"; s->opt[OPT_DROPOUT].title = SANE_I18N("Dropout"); s->opt[OPT_DROPOUT].desc = SANE_I18N("Selects the dropout."); s->opt[OPT_DROPOUT].type = SANE_TYPE_STRING; s->opt[OPT_DROPOUT].size = max_string_size(dropout_list); s->opt[OPT_DROPOUT].cap |= SANE_CAP_ADVANCED; s->opt[OPT_DROPOUT].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_DROPOUT].constraint.string_list = dropout_list; s->val[OPT_DROPOUT].w = 0; /* None */ /* brightness */ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].desc = SANE_I18N("Selects the brightness."); s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT; s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE; s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BRIGHTNESS].constraint.range = &s->hw->cmd->bright_range; s->val[OPT_BRIGHTNESS].w = 0; /* Normal */ if (!s->hw->cmd->set_bright) s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; /* sharpness */ s->opt[OPT_SHARPNESS].name = "sharpness"; s->opt[OPT_SHARPNESS].title = SANE_I18N("Sharpness"); s->opt[OPT_SHARPNESS].desc = ""; s->opt[OPT_SHARPNESS].type = SANE_TYPE_INT; s->opt[OPT_SHARPNESS].unit = SANE_UNIT_NONE; s->opt[OPT_SHARPNESS].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_SHARPNESS].constraint.range = &outline_emphasis_range; s->val[OPT_SHARPNESS].w = 0; /* Normal */ if (!s->hw->cmd->set_outline_emphasis) s->opt[OPT_SHARPNESS].cap |= SANE_CAP_INACTIVE; /* gamma */ s->opt[OPT_GAMMA_CORRECTION].name = SANE_NAME_GAMMA_CORRECTION; s->opt[OPT_GAMMA_CORRECTION].title = SANE_TITLE_GAMMA_CORRECTION; s->opt[OPT_GAMMA_CORRECTION].desc = SANE_DESC_GAMMA_CORRECTION; s->opt[OPT_GAMMA_CORRECTION].type = SANE_TYPE_STRING; s->opt[OPT_GAMMA_CORRECTION].constraint_type = SANE_CONSTRAINT_STRING_LIST; /* * special handling for D1 function level - at this time I'm not * testing for D1, I'm just assuming that all D level scanners will * behave the same way. This has to be confirmed with the next D-level * scanner */ if (s->hw->cmd->level[0] == 'D') { s->opt[OPT_GAMMA_CORRECTION].size = max_string_size(gamma_list_d); s->opt[OPT_GAMMA_CORRECTION].constraint.string_list = gamma_list_d; s->val[OPT_GAMMA_CORRECTION].w = 1; /* Default */ gamma_userdefined = gamma_userdefined_d; gamma_params = gamma_params_d; } else { s->opt[OPT_GAMMA_CORRECTION].size = max_string_size(gamma_list_ab); s->opt[OPT_GAMMA_CORRECTION].constraint.string_list = gamma_list_ab; s->val[OPT_GAMMA_CORRECTION].w = 0; /* Default */ gamma_userdefined = gamma_userdefined_ab; gamma_params = gamma_params_ab; } if (!s->hw->cmd->set_gamma) s->opt[OPT_GAMMA_CORRECTION].cap |= SANE_CAP_INACTIVE; /* red gamma vector */ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof(SANE_Word); s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[0][0]; /* green gamma vector */ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof(SANE_Word); s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[1][0]; /* red gamma vector */ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof(SANE_Word); s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[2][0]; if (s->hw->cmd->set_gamma_table && gamma_userdefined[s->val[OPT_GAMMA_CORRECTION].w] == SANE_TRUE) { s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; } else { 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; } /* initialize the Gamma tables */ memset(&s->gamma_table[0], 0, 256 * sizeof(SANE_Word)); memset(&s->gamma_table[1], 0, 256 * sizeof(SANE_Word)); memset(&s->gamma_table[2], 0, 256 * sizeof(SANE_Word)); /* memset(&s->gamma_table[3], 0, 256 * sizeof(SANE_Word)); */ for (i = 0; i < 256; i++) { s->gamma_table[0][i] = i; s->gamma_table[1][i] = i; s->gamma_table[2][i] = i; /* s->gamma_table[3][i] = i; */ } /* color correction */ s->opt[OPT_COLOR_CORRECTION].name = "color-correction"; s->opt[OPT_COLOR_CORRECTION].title = SANE_I18N("Color correction"); s->opt[OPT_COLOR_CORRECTION].desc = SANE_I18N("Sets the color correction table for the selected output device."); s->opt[OPT_COLOR_CORRECTION].type = SANE_TYPE_STRING; s->opt[OPT_COLOR_CORRECTION].size = max_string_size(correction_list); s->opt[OPT_COLOR_CORRECTION].cap |= SANE_CAP_ADVANCED; s->opt[OPT_COLOR_CORRECTION].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_COLOR_CORRECTION].constraint.string_list = correction_list; s->val[OPT_COLOR_CORRECTION].w = CORR_AUTO; if (!s->hw->cmd->set_color_correction) s->opt[OPT_COLOR_CORRECTION].cap |= SANE_CAP_INACTIVE; /* resolution */ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_RESOLUTION].constraint.word_list = s->hw->resolution_list; s->val[OPT_RESOLUTION].w = s->hw->dpi_range.min; /* 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_INT; s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE; s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_THRESHOLD].constraint.range = &u8_range; s->val[OPT_THRESHOLD].w = 0x80; if (!s->hw->cmd->set_threshold) s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; /* "Advanced" group: */ s->opt[OPT_ADVANCED_GROUP].title = SANE_I18N("Advanced"); s->opt[OPT_ADVANCED_GROUP].desc = ""; s->opt[OPT_ADVANCED_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_ADVANCED_GROUP].cap = SANE_CAP_ADVANCED; /* "Color correction" group: */ s->opt[OPT_CCT_GROUP].title = SANE_I18N("Color correction"); s->opt[OPT_CCT_GROUP].desc = ""; s->opt[OPT_CCT_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_CCT_GROUP].cap = SANE_CAP_ADVANCED; /* XXX disabled for now */ s->opt[OPT_CCT_MODE].name = "cct-type"; s->opt[OPT_CCT_MODE].title = "CCT Profile Type"; s->opt[OPT_CCT_MODE].desc = "Color correction profile type"; s->opt[OPT_CCT_MODE].type = SANE_TYPE_STRING; s->opt[OPT_CCT_MODE].cap |= SANE_CAP_ADVANCED | SANE_CAP_INACTIVE; s->opt[OPT_CCT_MODE].size = max_string_size(cct_mode_list); s->opt[OPT_CCT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_CCT_MODE].constraint.string_list = cct_mode_list; s->val[OPT_CCT_MODE].w = CCT_AUTO; s->opt[OPT_CCT_PROFILE].name = "cct-profile"; s->opt[OPT_CCT_PROFILE].title = "CCT Profile"; s->opt[OPT_CCT_PROFILE].desc = "Color correction profile data"; s->opt[OPT_CCT_PROFILE].type = SANE_TYPE_FIXED; s->opt[OPT_CCT_PROFILE].cap |= SANE_CAP_ADVANCED; s->opt[OPT_CCT_PROFILE].unit = SANE_UNIT_NONE; s->opt[OPT_CCT_PROFILE].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CCT_PROFILE].constraint.range = &fx_range; s->opt[OPT_CCT_PROFILE].size = 9 * sizeof(SANE_Word); s->val[OPT_CCT_PROFILE].wa = s->cct_table; /* if (!s->hw->cmd->set_color_correction) s->opt[OPT_FILM_TYPE].cap |= SANE_CAP_INACTIVE; */ /* mirror */ s->opt[OPT_MIRROR].name = "mirror"; s->opt[OPT_MIRROR].title = SANE_I18N("Mirror image"); s->opt[OPT_MIRROR].desc = SANE_I18N("Mirror the image."); s->opt[OPT_MIRROR].type = SANE_TYPE_BOOL; s->val[OPT_MIRROR].w = SANE_FALSE; if (!s->hw->cmd->mirror_image) s->opt[OPT_MIRROR].cap |= SANE_CAP_INACTIVE; /* auto area segmentation */ s->opt[OPT_AAS].name = "auto-area-segmentation"; s->opt[OPT_AAS].title = SANE_I18N("Auto area segmentation"); s->opt[OPT_AAS].desc = "Enables different dithering modes in image and text areas"; s->opt[OPT_AAS].type = SANE_TYPE_BOOL; s->val[OPT_AAS].w = SANE_TRUE; if (!s->hw->cmd->control_auto_area_segmentation) s->opt[OPT_AAS].cap |= SANE_CAP_INACTIVE; /* "Preview settings" group: */ s->opt[OPT_PREVIEW_GROUP].title = SANE_TITLE_PREVIEW; s->opt[OPT_PREVIEW_GROUP].desc = ""; s->opt[OPT_PREVIEW_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_PREVIEW_GROUP].cap = SANE_CAP_ADVANCED; /* preview */ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; s->val[OPT_PREVIEW].w = SANE_FALSE; /* "Geometry" group: */ s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N("Geometry"); s->opt[OPT_GEOMETRY_GROUP].desc = ""; s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; /* top-left x */ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; s->opt[OPT_TL_X].unit = SANE_UNIT_MM; s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_X].constraint.range = s->hw->x_range; s->val[OPT_TL_X].w = 0; /* top-left y */ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_Y].constraint.range = s->hw->y_range; s->val[OPT_TL_Y].w = 0; /* bottom-right x */ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; s->opt[OPT_BR_X].unit = SANE_UNIT_MM; s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_X].constraint.range = s->hw->x_range; s->val[OPT_BR_X].w = s->hw->x_range->max; /* bottom-right y */ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_Y].constraint.range = s->hw->y_range; s->val[OPT_BR_Y].w = s->hw->y_range->max; /* "Focus" group: */ s->opt[OPT_FOCUS_GROUP].title = SANE_I18N("Focus"); s->opt[OPT_FOCUS_GROUP].desc = ""; s->opt[OPT_FOCUS_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_FOCUS_GROUP].cap = SANE_CAP_ADVANCED; /* autofocus */ s->opt[OPT_AUTOFOCUS].name = SANE_NAME_AUTOFOCUS; s->opt[OPT_AUTOFOCUS].title = SANE_TITLE_AUTOFOCUS; s->opt[OPT_AUTOFOCUS].desc = SANE_DESC_AUTOFOCUS; s->opt[OPT_AUTOFOCUS].type = SANE_TYPE_BOOL; s->val[OPT_AUTOFOCUS].w = SANE_FALSE; s->opt[OPT_AUTOFOCUS].cap |= SANE_CAP_ADVANCED; /* focus position */ s->opt[OPT_FOCUS_POS].name = SANE_NAME_FOCUS; s->opt[OPT_FOCUS_POS].title = SANE_TITLE_FOCUS; s->opt[OPT_FOCUS_POS].desc = SANE_DESC_FOCUS; s->opt[OPT_FOCUS_POS].type = SANE_TYPE_INT; s->opt[OPT_FOCUS_POS].unit = SANE_UNIT_NONE; s->opt[OPT_FOCUS_POS].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_FOCUS_POS].constraint.range = &focus_range; s->val[OPT_FOCUS_POS].w = FOCUS_ON_GLASS; s->opt[OPT_FOCUS_POS].cap |= SANE_CAP_ADVANCED; if (s->hw->focusSupport == SANE_TRUE) { s->opt[OPT_FOCUS_POS].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_AUTOFOCUS].cap &= ~SANE_CAP_INACTIVE; } else { s->opt[OPT_FOCUS_POS].cap |= SANE_CAP_INACTIVE; s->opt[OPT_AUTOFOCUS].cap |= SANE_CAP_INACTIVE; } /* "Optional equipment" group: */ s->opt[OPT_EQU_GROUP].title = SANE_I18N("Optional equipment"); s->opt[OPT_EQU_GROUP].desc = ""; s->opt[OPT_EQU_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_EQU_GROUP].cap = SANE_CAP_ADVANCED; /* source */ s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE; s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE; s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE; s->opt[OPT_SOURCE].type = SANE_TYPE_STRING; s->opt[OPT_SOURCE].size = max_string_size(source_list); s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_SOURCE].constraint.string_list = source_list; if (!s->hw->extension) s->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE; s->val[OPT_SOURCE].w = 0; /* always use Flatbed as default */ /* film type */ s->opt[OPT_FILM_TYPE].name = "film-type"; s->opt[OPT_FILM_TYPE].title = SANE_I18N("Film type"); s->opt[OPT_FILM_TYPE].desc = ""; s->opt[OPT_FILM_TYPE].type = SANE_TYPE_STRING; s->opt[OPT_FILM_TYPE].size = max_string_size(film_list); s->opt[OPT_FILM_TYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_FILM_TYPE].constraint.string_list = film_list; s->val[OPT_FILM_TYPE].w = 0; if (!s->hw->cmd->set_bay) s->opt[OPT_FILM_TYPE].cap |= SANE_CAP_INACTIVE; /* forward feed / eject */ s->opt[OPT_EJECT].name = "eject"; s->opt[OPT_EJECT].title = SANE_I18N("Eject"); s->opt[OPT_EJECT].desc = SANE_I18N("Eject the sheet in the ADF"); s->opt[OPT_EJECT].type = SANE_TYPE_BUTTON; if ((!s->hw->ADF) && (!s->hw->cmd->set_bay)) { /* Hack: Using set_bay to indicate. */ s->opt[OPT_EJECT].cap |= SANE_CAP_INACTIVE; } /* auto forward feed / eject */ s->opt[OPT_AUTO_EJECT].name = "auto-eject"; s->opt[OPT_AUTO_EJECT].title = SANE_I18N("Auto eject"); s->opt[OPT_AUTO_EJECT].desc = SANE_I18N("Eject document after scanning"); s->opt[OPT_AUTO_EJECT].type = SANE_TYPE_BOOL; s->val[OPT_AUTO_EJECT].w = SANE_FALSE; if (!s->hw->ADF) s->opt[OPT_AUTO_EJECT].cap |= SANE_CAP_INACTIVE; s->opt[OPT_ADF_MODE].name = "adf-mode"; s->opt[OPT_ADF_MODE].title = SANE_I18N("ADF Mode"); s->opt[OPT_ADF_MODE].desc = SANE_I18N("Selects the ADF mode (simplex/duplex)"); s->opt[OPT_ADF_MODE].type = SANE_TYPE_STRING; s->opt[OPT_ADF_MODE].size = max_string_size(adf_mode_list); s->opt[OPT_ADF_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_ADF_MODE].constraint.string_list = adf_mode_list; s->val[OPT_ADF_MODE].w = 0; /* simplex */ if ((!s->hw->ADF) || (s->hw->duplex == SANE_FALSE)) s->opt[OPT_ADF_MODE].cap |= SANE_CAP_INACTIVE; /* select bay */ s->opt[OPT_BAY].name = "bay"; s->opt[OPT_BAY].title = SANE_I18N("Bay"); s->opt[OPT_BAY].desc = SANE_I18N("Select bay to scan"); s->opt[OPT_BAY].type = SANE_TYPE_STRING; s->opt[OPT_BAY].size = max_string_size(bay_list); s->opt[OPT_BAY].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_BAY].constraint.string_list = bay_list; s->val[OPT_BAY].w = 0; /* Bay 1 */ if (!s->hw->cmd->set_bay) s->opt[OPT_BAY].cap |= SANE_CAP_INACTIVE; s->opt[OPT_WAIT_FOR_BUTTON].name = SANE_EPSON_WAIT_FOR_BUTTON_NAME; s->opt[OPT_WAIT_FOR_BUTTON].title = SANE_EPSON_WAIT_FOR_BUTTON_TITLE; s->opt[OPT_WAIT_FOR_BUTTON].desc = SANE_EPSON_WAIT_FOR_BUTTON_DESC; s->opt[OPT_WAIT_FOR_BUTTON].type = SANE_TYPE_BOOL; s->opt[OPT_WAIT_FOR_BUTTON].unit = SANE_UNIT_NONE; s->opt[OPT_WAIT_FOR_BUTTON].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_WAIT_FOR_BUTTON].constraint.range = NULL; s->opt[OPT_WAIT_FOR_BUTTON].cap |= SANE_CAP_ADVANCED; if (!s->hw->cmd->request_push_button_status) s->opt[OPT_WAIT_FOR_BUTTON].cap |= SANE_CAP_INACTIVE; return SANE_STATUS_GOOD; } SANE_Status sane_open(SANE_String_Const name, SANE_Handle *handle) { SANE_Status status; Epson_Scanner *s = NULL; int l = strlen(name); DBG(7, "%s: name = %s\n", __func__, name); *handle = NULL; /* probe if empty device name provided */ if (l == 0) { probe_devices(SANE_FALSE); if (first_dev == NULL) { DBG(1, "no device detected\n"); return SANE_STATUS_INVAL; } s = device_detect(first_dev->sane.name, first_dev->connection, 0, &status); if (s == NULL) { DBG(1, "cannot open a perfectly valid device (%s)," " please report to the authors\n", name); return SANE_STATUS_INVAL; } } else { if (strncmp(name, "net:", 4) == 0) { s = device_detect(name, SANE_EPSON_NET, 0, &status); if (s == NULL) return status; } else if (strncmp(name, "libusb:", 7) == 0) { s = device_detect(name, SANE_EPSON_USB, 1, &status); if (s == NULL) return status; } else if (strncmp(name, "pio:", 4) == 0) { s = device_detect(name, SANE_EPSON_PIO, 0, &status); if (s == NULL) return status; } else { /* as a last resort, check for a match * in the device list. This should handle SCSI * devices and platforms without libusb. */ if (first_dev == NULL) probe_devices(SANE_FALSE); s = device_detect(name, SANE_EPSON_NODEV, 0, &status); if (s == NULL) { DBG(1, "invalid device name: %s\n", name); return SANE_STATUS_INVAL; } } } /* s is always valid here */ DBG(1, "handle obtained\n"); init_options(s); status = open_scanner(s); if (status != SANE_STATUS_GOOD) { free(s); return status; } status = esci_reset(s); if (status != SANE_STATUS_GOOD) { close_scanner(s); return status; } *handle = (SANE_Handle)s; return SANE_STATUS_GOOD; } void sane_close(SANE_Handle handle) { Epson_Scanner *s; DBG(1, "* %s\n", __func__); /* * XXX Test if there is still data pending from * the scanner. If so, then do a cancel */ s = (Epson_Scanner *) handle; close_scanner(s); } const SANE_Option_Descriptor * sane_get_option_descriptor(SANE_Handle handle, SANE_Int option) { Epson_Scanner *s = (Epson_Scanner *) handle; if (option < 0 || option >= NUM_OPTIONS) return NULL; return s->opt + option; } static const SANE_String_Const * search_string_list(const SANE_String_Const *list, SANE_String value) { while (*list != NULL && strcmp(value, *list) != 0) list++; return ((*list == NULL) ? NULL : list); } /* Activate, deactivate an option. Subroutines so we can add debugging info if we want. The change flag is set to TRUE if we changed an option. If we did not change an option, then the value of the changed flag is not modified. */ static void activateOption(Epson_Scanner *s, SANE_Int option, SANE_Bool *change) { if (!SANE_OPTION_IS_ACTIVE(s->opt[option].cap)) { s->opt[option].cap &= ~SANE_CAP_INACTIVE; *change = SANE_TRUE; } } static void deactivateOption(Epson_Scanner *s, SANE_Int option, SANE_Bool *change) { if (SANE_OPTION_IS_ACTIVE(s->opt[option].cap)) { s->opt[option].cap |= SANE_CAP_INACTIVE; *change = SANE_TRUE; } } static void setOptionState(Epson_Scanner *s, SANE_Bool state, SANE_Int option, SANE_Bool *change) { if (state) activateOption(s, option, change); else deactivateOption(s, option, change); } static SANE_Status getvalue(SANE_Handle handle, SANE_Int option, void *value) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Option_Descriptor *sopt = &(s->opt[option]); Option_Value *sval = &(s->val[option]); DBG(17, "%s: option = %d\n", __func__, option); switch (option) { case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: case OPT_CCT_PROFILE: memcpy(value, sval->wa, sopt->size); break; case OPT_NUM_OPTS: case OPT_RESOLUTION: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_MIRROR: case OPT_AAS: case OPT_PREVIEW: case OPT_BRIGHTNESS: case OPT_SHARPNESS: case OPT_AUTO_EJECT: case OPT_THRESHOLD: case OPT_BIT_DEPTH: case OPT_WAIT_FOR_BUTTON: case OPT_AUTOFOCUS: case OPT_FOCUS_POS: *((SANE_Word *) value) = sval->w; break; case OPT_MODE: case OPT_CCT_MODE: case OPT_ADF_MODE: case OPT_HALFTONE: case OPT_DROPOUT: case OPT_SOURCE: case OPT_FILM_TYPE: case OPT_GAMMA_CORRECTION: case OPT_COLOR_CORRECTION: case OPT_BAY: strcpy((char *) value, sopt->constraint.string_list[sval->w]); break; default: return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } /* * This routine handles common options between OPT_MODE and * OPT_HALFTONE. These options are TET (a HALFTONE mode), AAS * - auto area segmentation, and threshold. Apparently AAS * is some method to differentiate between text and photos. * Or something like that. * * AAS is available when the scan color depth is 1 and the * halftone method is not TET. * * Threshold is available when halftone is NONE, and depth is 1. */ static void handle_depth_halftone(Epson_Scanner *s, SANE_Bool *reload) { int hti = s->val[OPT_HALFTONE].w; int mdi = s->val[OPT_MODE].w; SANE_Bool aas = SANE_FALSE; SANE_Bool thresh = SANE_FALSE; /* this defaults to false */ setOptionState(s, thresh, OPT_THRESHOLD, reload); if (!s->hw->cmd->control_auto_area_segmentation) return; if (mode_params[mdi].depth == 1) { if (halftone_params[hti] != HALFTONE_TET) aas = SANE_TRUE; if (halftone_params[hti] == HALFTONE_NONE) thresh = SANE_TRUE; } setOptionState(s, aas, OPT_AAS, reload); setOptionState(s, thresh, OPT_THRESHOLD, reload); } /* * Handles setting the source (flatbed, transparency adapter (TPU), * or auto document feeder (ADF)). * * For newer scanners it also sets the focus according to the * glass / TPU settings. */ static void change_source(Epson_Scanner *s, SANE_Int optindex, char *value) { int force_max = SANE_FALSE; SANE_Bool dummy; DBG(1, "%s: optindex = %d, source = '%s'\n", __func__, optindex, value); /* reset the scanner when we are changing the source setting - this is necessary for the Perfection 1650 */ if (s->hw->need_reset_on_source_change) esci_reset(s); if (s->val[OPT_SOURCE].w == optindex) return; s->val[OPT_SOURCE].w = optindex; if (s->val[OPT_TL_X].w == s->hw->x_range->min && s->val[OPT_TL_Y].w == s->hw->y_range->min && s->val[OPT_BR_X].w == s->hw->x_range->max && s->val[OPT_BR_Y].w == s->hw->y_range->max) { force_max = SANE_TRUE; } if (strcmp(ADF_STR, value) == 0) { s->hw->x_range = &s->hw->adf_x_range; s->hw->y_range = &s->hw->adf_y_range; s->hw->use_extension = SANE_TRUE; /* disable film type option */ deactivateOption(s, OPT_FILM_TYPE, &dummy); s->val[OPT_FOCUS_POS].w = FOCUS_ON_GLASS; if (s->hw->duplex) { activateOption(s, OPT_ADF_MODE, &dummy); } else { deactivateOption(s, OPT_ADF_MODE, &dummy); s->val[OPT_ADF_MODE].w = 0; } DBG(1, "adf activated (ext: %d, duplex: %d)\n", s->hw->use_extension, s->hw->duplex); } else if (strcmp(TPU_STR, value) == 0 || strcmp(TPU_STR2, value) == 0) { if (strcmp(TPU_STR, value) == 0) { s->hw->x_range = &s->hw->tpu_x_range; s->hw->y_range = &s->hw->tpu_y_range; s->hw->TPU2 = SANE_FALSE; } if (strcmp(TPU_STR2, value) == 0) { s->hw->x_range = &s->hw->tpu2_x_range; s->hw->y_range = &s->hw->tpu2_y_range; s->hw->TPU2 = SANE_TRUE; } s->hw->use_extension = SANE_TRUE; /* enable film type option only if the scanner supports it */ if (s->hw->cmd->set_film_type != 0) activateOption(s, OPT_FILM_TYPE, &dummy); else deactivateOption(s, OPT_FILM_TYPE, &dummy); /* enable focus position if the scanner supports it */ if (s->hw->focusSupport) s->val[OPT_FOCUS_POS].w = FOCUS_ABOVE_25MM; deactivateOption(s, OPT_ADF_MODE, &dummy); deactivateOption(s, OPT_EJECT, &dummy); deactivateOption(s, OPT_AUTO_EJECT, &dummy); } else { /* neither ADF nor TPU active */ s->hw->x_range = &s->hw->fbf_x_range; s->hw->y_range = &s->hw->fbf_y_range; s->hw->use_extension = SANE_FALSE; /* disable film type option */ deactivateOption(s, OPT_FILM_TYPE, &dummy); s->val[OPT_FOCUS_POS].w = FOCUS_ON_GLASS; deactivateOption(s, OPT_ADF_MODE, &dummy); } /* special handling for FilmScan 200 */ if (s->hw->cmd->level[0] == 'F') activateOption(s, OPT_FILM_TYPE, &dummy); s->opt[OPT_BR_X].constraint.range = s->hw->x_range; s->opt[OPT_BR_Y].constraint.range = s->hw->y_range; if (s->val[OPT_TL_X].w < s->hw->x_range->min || force_max) s->val[OPT_TL_X].w = s->hw->x_range->min; if (s->val[OPT_TL_Y].w < s->hw->y_range->min || force_max) s->val[OPT_TL_Y].w = s->hw->y_range->min; if (s->val[OPT_BR_X].w > s->hw->x_range->max || force_max) s->val[OPT_BR_X].w = s->hw->x_range->max; if (s->val[OPT_BR_Y].w > s->hw->y_range->max || force_max) s->val[OPT_BR_Y].w = s->hw->y_range->max; setOptionState(s, s->hw->ADF && s->hw->use_extension, OPT_AUTO_EJECT, &dummy); setOptionState(s, s->hw->ADF && s->hw->use_extension, OPT_EJECT, &dummy); } static SANE_Status setvalue(SANE_Handle handle, SANE_Int option, void *value, SANE_Int *info) { Epson_Scanner *s = (Epson_Scanner *) handle; SANE_Option_Descriptor *sopt = &(s->opt[option]); Option_Value *sval = &(s->val[option]); SANE_Status status; const SANE_String_Const *optval = NULL; int optindex = 0; SANE_Bool reload = SANE_FALSE; DBG(17, "%s: option = %d, value = %p\n", __func__, option, value); status = sanei_constrain_value(sopt, value, info); if (status != SANE_STATUS_GOOD) return status; if (info && value && (*info & SANE_INFO_INEXACT) && sopt->type == SANE_TYPE_INT) DBG(17, "%s: constrained val = %d\n", __func__, *(SANE_Word *) value); if (sopt->constraint_type == SANE_CONSTRAINT_STRING_LIST) { optval = search_string_list(sopt->constraint.string_list, (char *) value); if (optval == NULL) return SANE_STATUS_INVAL; optindex = optval - sopt->constraint.string_list; } switch (option) { case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: case OPT_CCT_PROFILE: memcpy(sval->wa, value, sopt->size); /* Word arrays */ break; case OPT_CCT_MODE: case OPT_ADF_MODE: case OPT_DROPOUT: case OPT_FILM_TYPE: case OPT_BAY: sval->w = optindex; /* Simple lists */ break; case OPT_EJECT: /* XXX required? control_extension(s, 1); */ esci_eject(s); break; case OPT_RESOLUTION: sval->w = *((SANE_Word *) value); DBG(17, "setting resolution to %d\n", sval->w); reload = SANE_TRUE; break; case OPT_BR_X: case OPT_BR_Y: if (SANE_UNFIX(*((SANE_Word *) value)) == 0) { DBG(17, "invalid br-x or br-y\n"); return SANE_STATUS_INVAL; } // fall through case OPT_TL_X: case OPT_TL_Y: sval->w = *((SANE_Word *) value); DBG(17, "setting size to %f\n", SANE_UNFIX(sval->w)); if (NULL != info) *info |= SANE_INFO_RELOAD_PARAMS; break; case OPT_SOURCE: change_source(s, optindex, (char *) value); reload = SANE_TRUE; break; case OPT_MODE: { SANE_Bool isColor = mode_params[optindex].color; sval->w = optindex; DBG(17, "%s: setting mode to %d\n", __func__, optindex); /* halftoning available only on bw scans */ if (s->hw->cmd->set_halftoning != 0) setOptionState(s, mode_params[optindex].depth == 1, OPT_HALFTONE, &reload); /* disable dropout on non-color scans */ setOptionState(s, !isColor, OPT_DROPOUT, &reload); if (s->hw->cmd->set_color_correction) setOptionState(s, isColor, OPT_COLOR_CORRECTION, &reload); /* if binary, then disable the bit depth selection */ if (optindex == 0) { DBG(17, "%s: disabling bit depth selection\n", __func__); s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; } else { if (s->hw->depth_list[0] == 1) { DBG(17, "%s: only one depth is available\n", __func__); s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; } else { DBG(17, "%s: enabling bit depth selection\n", __func__); s->opt[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE; s->val[OPT_BIT_DEPTH].w = mode_params[optindex].depth; } } handle_depth_halftone(s, &reload); reload = SANE_TRUE; break; } case OPT_BIT_DEPTH: sval->w = *((SANE_Word *) value); mode_params[s->val[OPT_MODE].w].depth = sval->w; reload = SANE_TRUE; break; case OPT_HALFTONE: sval->w = optindex; handle_depth_halftone(s, &reload); break; case OPT_COLOR_CORRECTION: { sval->w = optindex; break; } case OPT_GAMMA_CORRECTION: { SANE_Bool f = gamma_userdefined[optindex]; sval->w = optindex; setOptionState(s, f, OPT_GAMMA_VECTOR_R, &reload); setOptionState(s, f, OPT_GAMMA_VECTOR_G, &reload); setOptionState(s, f, OPT_GAMMA_VECTOR_B, &reload); setOptionState(s, !f, OPT_BRIGHTNESS, &reload); /* Note... */ break; } case OPT_AUTOFOCUS: sval->w = *((SANE_Word *) value); setOptionState(s, !sval->w, OPT_FOCUS_POS, &reload); break; case OPT_MIRROR: case OPT_AAS: case OPT_PREVIEW: /* needed? */ case OPT_BRIGHTNESS: case OPT_SHARPNESS: case OPT_AUTO_EJECT: case OPT_THRESHOLD: case OPT_WAIT_FOR_BUTTON: case OPT_FOCUS_POS: sval->w = *((SANE_Word *) value); break; default: return SANE_STATUS_INVAL; } if (reload && info != NULL) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; DBG(17, "%s: end\n", __func__); return SANE_STATUS_GOOD; } SANE_Status sane_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int *info) { DBG(17, "%s: action = %x, option = %d\n", __func__, action, option); if (option < 0 || option >= NUM_OPTIONS) return SANE_STATUS_INVAL; if (info != NULL) *info = 0; switch (action) { case SANE_ACTION_GET_VALUE: return getvalue(handle, option, value); case SANE_ACTION_SET_VALUE: return setvalue(handle, option, value, info); default: return SANE_STATUS_INVAL; } return SANE_STATUS_INVAL; } SANE_Status sane_get_parameters(SANE_Handle handle, SANE_Parameters *params) { Epson_Scanner *s = (Epson_Scanner *) handle; DBG(5, "%s\n", __func__); if (params == NULL) DBG(1, "%s: params is NULL\n", __func__); /* * If sane_start was already called, then just retrieve the parameters * from the scanner data structure */ if (!s->eof && s->ptr != NULL) { DBG(5, "scan in progress, returning saved params structure\n"); } else { /* otherwise initialize the params structure and gather the data */ e2_init_parameters(s); } if (params != NULL) *params = s->params; print_params(s->params); return SANE_STATUS_GOOD; } static void e2_load_cct_profile(struct Epson_Scanner *s, unsigned int index) { s->cct_table[0] = SANE_FIX(s->hw->cct_profile->cct[index][0]); s->cct_table[1] = SANE_FIX(s->hw->cct_profile->cct[index][1]); s->cct_table[2] = SANE_FIX(s->hw->cct_profile->cct[index][2]); s->cct_table[3] = SANE_FIX(s->hw->cct_profile->cct[index][3]); s->cct_table[4] = SANE_FIX(s->hw->cct_profile->cct[index][4]); s->cct_table[5] = SANE_FIX(s->hw->cct_profile->cct[index][5]); s->cct_table[6] = SANE_FIX(s->hw->cct_profile->cct[index][6]); s->cct_table[7] = SANE_FIX(s->hw->cct_profile->cct[index][7]); s->cct_table[8] = SANE_FIX(s->hw->cct_profile->cct[index][8]); } /* * This function is part of the SANE API and gets called from the front end to * start the scan process. */ SANE_Status sane_start(SANE_Handle handle) { Epson_Scanner *s = (Epson_Scanner *) handle; Epson_Device *dev = s->hw; SANE_Status status; DBG(5, "* %s\n", __func__); s->eof = SANE_FALSE; s->canceling = SANE_FALSE; /* check if we just have finished working with the ADF */ status = e2_check_adf(s); if (status != SANE_STATUS_GOOD) return status; /* calc scanning parameters */ status = e2_init_parameters(s); if (status != SANE_STATUS_GOOD) return status; print_params(s->params); /* enable infrared */ if (s->val[OPT_MODE].w == MODE_INFRARED) esci_enable_infrared(handle); /* ESC , bay */ if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_BAY].cap)) { status = esci_set_bay(s, s->val[OPT_BAY].w); if (status != SANE_STATUS_GOOD) return status; } /* set scanning parameters */ if (dev->extended_commands) status = e2_set_extended_scanning_parameters(s); else status = e2_set_scanning_parameters(s); if (status != SANE_STATUS_GOOD) return status; /* * set focus after we set scanning parameters because the scanner will * use the middle of the scanning area for autofocus. If we want to * support a defined x,y position for autofocus, we'd need to send * specific scanning paramters just for autofocus. */ if (s->hw->focusSupport == SANE_TRUE) { if (s->val[OPT_AUTOFOCUS].w) { DBG(1, "setting autofocus\n"); status = esci_set_focus_position(s, 0xff); } else { DBG(1, "setting focus to %u\n", s->val[OPT_FOCUS_POS].w); status = esci_set_focus_position(s, s->val[OPT_FOCUS_POS].w); } if (status != SANE_STATUS_GOOD) { DBG(1, "setting focus failed\n"); return status; } } /* ESC z, user defined gamma table */ if (dev->cmd->set_gamma_table && gamma_userdefined[s->val[OPT_GAMMA_CORRECTION].w]) { status = esci_set_gamma_table(s); if (status != SANE_STATUS_GOOD) return status; } if (s->val[OPT_COLOR_CORRECTION].w == CORR_AUTO) { /* Automatic */ DBG(1, "using built in CCT profile\n"); if (dev->model_id == 0) DBG(1, " specific profile not available, using default\n"); if (0) { /* XXX TPU */ /* XXX check this */ if (s->val[OPT_FILM_TYPE].w == 0) e2_load_cct_profile(s, CCTP_COLORPOS); else e2_load_cct_profile(s, CCTP_COLORNEG); } else { e2_load_cct_profile(s, CCTP_REFLECTIVE); } } /* ESC m, user defined color correction */ if (s->hw->cmd->set_color_correction_coefficients && correction_userdefined[s->val[OPT_COLOR_CORRECTION].w]) { status = esci_set_color_correction_coefficients(s, s->cct_table); if (status != SANE_STATUS_GOOD) return status; } /* check if we just have finished working with the ADF. * this seems to work only after the scanner has been * set up with scanning parameters */ status = e2_check_adf(s); if (status != SANE_STATUS_GOOD) return status; /* * If WAIT_FOR_BUTTON is active, then do just that: * Wait until the button is pressed. If the button was already * pressed, then we will get the button pressed event right away. */ if (s->val[OPT_WAIT_FOR_BUTTON].w == SANE_TRUE) e2_wait_button(s); /* for debug, request command parameter */ /* if (DBG_LEVEL) { unsigned char buf[45]; request_command_parameter(s, buf); } */ /* set the retry count to 0 */ s->retry_count = 0; /* allocate buffers for color shuffling */ if (dev->color_shuffle == SANE_TRUE) { int i; /* initialize the line buffers */ for (i = 0; i < s->line_distance * 2 + 1; i++) { if (s->line_buffer[i] != NULL) free(s->line_buffer[i]); s->line_buffer[i] = malloc(s->params.bytes_per_line); if (s->line_buffer[i] == NULL) { DBG(1, "out of memory (line %d)\n", __LINE__); return SANE_STATUS_NO_MEM; } } } /* prepare buffer here so that a memory allocation failure * will leave the scanner in a sane state. * the buffer will have to hold the image data plus * an error code in the extended handshaking mode. */ s->buf = realloc(s->buf, (s->lcount * s->params.bytes_per_line) + 1); if (s->buf == NULL) return SANE_STATUS_NO_MEM; s->ptr = s->end = s->buf; /* feed the first sheet in the ADF */ if (dev->ADF && dev->use_extension && dev->cmd->feed) { status = esci_feed(s); if (status != SANE_STATUS_GOOD) return status; } /* this seems to work only for some devices */ status = e2_wait_warm_up(s); if (status != SANE_STATUS_GOOD) return status; if (s->hw->focusSupport == SANE_TRUE && s->val[OPT_AUTOFOCUS].w) { status = esci_request_focus_position(s, &s->currentFocusPosition); if (status == SANE_STATUS_GOOD) s->val[OPT_FOCUS_POS].w = s->currentFocusPosition; } /* start scanning */ DBG(1, "%s: scanning...\n", __func__); if (dev->extended_commands) { status = e2_start_ext_scan(s); /* sometimes the scanner gives an io error when * it's warming up. */ if (status == SANE_STATUS_IO_ERROR) { status = e2_wait_warm_up(s); if (status == SANE_STATUS_GOOD) status = e2_start_ext_scan(s); } } else status = e2_start_std_scan(s); if (status != SANE_STATUS_GOOD) { DBG(1, "%s: start failed: %s\n", __func__, sane_strstatus(status)); return status; } /* this is a kind of read request */ if (dev->connection == SANE_EPSON_NET) { sanei_epson_net_write(s, 0x2000, NULL, 0, s->ext_block_len + 1, &status); } return status; } /* this moves data from our buffers to SANE */ SANE_Status sane_read(SANE_Handle handle, SANE_Byte *data, SANE_Int max_length, SANE_Int *length) { SANE_Status status; Epson_Scanner *s = (Epson_Scanner *) handle; DBG(18, "* %s: eof: %d, canceling: %d\n", __func__, s->eof, s->canceling); /* sane_read called before sane_start? */ if (s->buf == NULL) { DBG(1, "%s: buffer is NULL", __func__); return SANE_STATUS_INVAL; } *length = 0; if (s->hw->extended_commands) status = e2_ext_read(s); else status = e2_block_read(s); /* The scanning operation might be canceled by the scanner itself * or the fronted program */ if (status == SANE_STATUS_CANCELLED || s->canceling) { e2_scan_finish(s); return SANE_STATUS_CANCELLED; } /* XXX if FS G and STATUS_IOERR, use e2_check_extended_status */ DBG(18, "moving data %p %p, %d (%d lines)\n", (void *) s->ptr, (void *) s->end, max_length, max_length / s->params.bytes_per_line); e2_copy_image_data(s, data, max_length, length); DBG(18, "%d lines read, eof: %d, canceling: %d, status: %d\n", *length / s->params.bytes_per_line, s->canceling, s->eof, status); /* continue reading if appropriate */ if (status == SANE_STATUS_GOOD) return status; e2_scan_finish(s); return status; } /* * void sane_cancel(SANE_Handle handle) * * Set the cancel flag to true. The next time the backend requests data * from the scanner the CAN message will be sent. */ void sane_cancel(SANE_Handle handle) { Epson_Scanner *s = (Epson_Scanner *) handle; DBG(1, "* %s\n", __func__); s->canceling = SANE_TRUE; } /* * SANE_Status sane_set_io_mode() * * not supported - for asynchronous I/O */ SANE_Status sane_set_io_mode(SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking) { return SANE_STATUS_UNSUPPORTED; } /* * SANE_Status sane_get_select_fd() * * not supported - for asynchronous I/O */ SANE_Status sane_get_select_fd(SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ *fd) { return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/epson2.conf.in000066400000000000000000000005661456256263500173630ustar00rootroot00000000000000# epson2.conf # # here are some examples for how to configure the EPSON2 backend # SCSI scsi EPSON # for the GT-6500: #scsi "EPSON SC" # Parallel port #pio 0x278 #pio 0x378 #pio 0x3BC # USB usb # For libusb support for unknown scanners use the following command # usb # e.g.: # usb 0x4b8 0x110 # Network # # net 192.168.1.123 net autodiscovery backends-1.3.0/backend/epson2.h000066400000000000000000000305651456256263500162620ustar00rootroot00000000000000/* * epson2.h - SANE library for Epson scanners. * * Based on Kazuhiro Sasayama previous * Work on epson.[ch] file from the SANE package. * Please see those files for original copyrights. * * Copyright (C) 2006 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ #ifndef epson2_h #define epson2_h #undef BACKEND_NAME #define BACKEND_NAME epson2 #define DEBUG_NOT_STATIC #define mode_params epson2_mode_params #define source_list epson2_source_list #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_STDDEF_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef NEED_SYS_TYPES_H #include #endif #include /* for memset and memcpy */ #include #include "sane/sane.h" #include "sane/sanei_backend.h" #include "sane/sanei_debug.h" #define EPSON2_CONFIG_FILE "epson2.conf" #ifndef PATH_MAX #define PATH_MAX (1024) #endif #ifndef XtNumber #define XtNumber(x) (sizeof(x) / sizeof(x[0])) #define XtOffset(p_type, field) ((size_t)&(((p_type)NULL)->field)) #define XtOffsetOf(s_type, field) XtOffset(s_type*, field) #endif #define NUM_OF_HEX_ELEMENTS (16) /* number of hex numbers per line for data dump */ #define DEVICE_NAME_LEN (16) /* length of device name in extended status */ /* string constants for GUI elements that are not defined SANE-wide */ #define SANE_NAME_GAMMA_CORRECTION "gamma-correction" #define SANE_TITLE_GAMMA_CORRECTION SANE_I18N("Gamma Correction") #define SANE_DESC_GAMMA_CORRECTION SANE_I18N("Selects the gamma correction value from a list of pre-defined devices or the user defined table, which can be downloaded to the scanner") #define SANE_EPSON_FOCUS_NAME "focus-position" #define SANE_EPSON_FOCUS_TITLE SANE_I18N("Focus Position") #define SANE_EPSON_FOCUS_DESC SANE_I18N("Sets the focus position to either the glass or 2.5mm above the glass") #define SANE_EPSON_WAIT_FOR_BUTTON_NAME "wait-for-button" #define SANE_EPSON_WAIT_FOR_BUTTON_TITLE SANE_I18N("Wait for Button") #define SANE_EPSON_WAIT_FOR_BUTTON_DESC SANE_I18N("After sending the scan command, wait until the button on the scanner is pressed to actually start the scan process."); /* misc constants */ #define LINES_SHUFFLE_MAX 17 /* 2 x 8 lines plus 1 */ #define SANE_EPSON_MAX_RETRIES 14 /* warmup max retry */ #define CMD_SIZE_EXT_STATUS 42 #define FOCUS_ON_GLASS 64 #define FOCUS_ABOVE_25MM (64 + 25) /* NOTE: you can find these codes with "man ascii". */ #define STX 0x02 #define ACK 0x06 #define NAK 0x15 #define CAN 0x18 #define ESC 0x1B #define PF 0x19 #define FS 0x1C #define S_ACK "\006" #define S_CAN "\030" /* status bits */ #define STATUS_FER 0x80 /* fatal error */ #define STATUS_NOT_READY 0x40 /* scanner is in use on another interface */ #define STATUS_AREA_END 0x20 /* area end */ #define STATUS_OPTION 0x10 /* option installed */ #define STATUS_EXT_COMMANDS 0x02 /* scanners supports extended commands */ #define STATUS_RESERVED 0x01 /* this should be always 0 */ #define EXT_STATUS_FER 0x80 /* fatal error */ #define EXT_STATUS_FBF 0x40 /* flat bed scanner */ #define EXT_STATUS_ADFT 0x20 /* page type ADF */ #define EXT_STATUS_ADFS 0x10 /* ADF is duplex capable */ #define EXT_STATUS_ADFO 0x08 /* ADF loads from the first sheet (page type only) */ #define EXT_STATUS_LID 0x04 /* lid is open */ #define EXT_STATUS_WU 0x02 /* warming up */ #define EXT_STATUS_PB 0x01 /* scanner has a push button */ #define EXT_STATUS_IST 0x80 /* option detected */ #define EXT_STATUS_EN 0x40 /* option enabled */ #define EXT_STATUS_ERR 0x20 /* other error */ #define EXT_STATUS_PE 0x08 /* no paper */ #define EXT_STATUS_PJ 0x04 /* paper jam */ #define EXT_STATUS_OPN 0x02 /* cover open */ #define EXT_IDTY_CAP1_DLF 0x80 #define EXT_IDTY_CAP1_NOTFBF 0x40 /* not a flat bed scanner */ #define EXT_IDTY_CAP1_ADFT 0x20 /* page type ADF ? */ #define EXT_IDTY_CAP1_ADFS 0x10 /* ADF is duplex capable */ #define EXT_IDTY_CAP1_ADFO 0x08 /* ADF loads from the first sheet (page type only) */ #define EXT_IDTY_CAP1_LID 0x04 /* lid type option ? */ #define EXT_IDTY_CAP1_TPIR 0x02 /* TPU with infrared */ #define EXT_IDTY_CAP1_PB 0x01 /* scanner has a push button */ #define EXT_IDTY_CAP2_AFF 0x04 /* auto form feed */ #define EXT_IDTY_CAP2_DFD 0x08 /* double feed detection */ #define EXT_IDTY_CAP2_ADFAS 0x10 /* ADF with auto scan support */ #define FSF_STATUS_MAIN_FER 0x80 /* system error */ #define FSF_STATUS_MAIN_NR 0x40 /* not ready */ #define FSF_STATUS_MAIN_WU 0x02 /* warming up */ #define FSF_STATUS_MAIN_CWU 0x01 /* warm up can be cancelled (?) */ #define FSF_STATUS_ADF_IST 0x80 /* installed */ #define FSF_STATUS_ADF_EN 0x40 /* enabled */ #define FSF_STATUS_ADF_ERR 0x20 /* system error */ #define FSF_STATUS_ADF_PE 0x08 /* paper empty */ #define FSF_STATUS_ADF_PJ 0x04 /* paper jam */ #define FSF_STATUS_ADF_OPN 0x02 /* cover open */ #define FSF_STATUS_ADF_PAG 0x01 /* duplex */ #define FSF_STATUS_TPU_IST 0x80 /* installed */ #define FSF_STATUS_TPU_EN 0x40 /* enabled */ #define FSF_STATUS_TPU_ERR 0x20 /* system error */ #define FSF_STATUS_TPU_OPN 0x02 /* cover open */ #define FSF_STATUS_MAIN2_ERR 0x20 /* system error */ #define FSF_STATUS_MAIN2_PE 0x08 /* paper empty */ #define FSF_STATUS_MAIN2_PJ 0x04 /* paper jam */ #define FSF_STATUS_MAIN2_OPN 0x02 /* cover open */ #define FSG_STATUS_FER 0x80 #define FSG_STATUS_NOT_READY 0x40 /* in use via other interface */ #define FSG_STATUS_CANCEL_REQ 0x10 /* cancel request from scanner */ #define EPSON_LEVEL_A1 0 #define EPSON_LEVEL_A2 1 #define EPSON_LEVEL_B1 2 #define EPSON_LEVEL_B2 3 #define EPSON_LEVEL_B3 4 #define EPSON_LEVEL_B4 5 #define EPSON_LEVEL_B5 6 #define EPSON_LEVEL_B6 7 #define EPSON_LEVEL_B7 8 #define EPSON_LEVEL_B8 9 #define EPSON_LEVEL_F5 10 #define EPSON_LEVEL_D1 11 #define EPSON_LEVEL_D7 12 #define EPSON_LEVEL_D8 13 /* there is also a function level "A5", which I'm ignoring here until somebody can * convince me that this is still needed. The A5 level was for the GT-300, which * was (is) a monochrome only scanner. So if somebody really wants to use this * scanner with SANE get in touch with me and we can work something out - khk */ #define EPSON_LEVEL_DEFAULT EPSON_LEVEL_B3 struct EpsonCmd { char *level; unsigned char request_identity; unsigned char request_identity2; /* new request identity level Dx */ unsigned char request_status; unsigned char request_condition; unsigned char set_color_mode; unsigned char start_scanning; unsigned char set_data_format; unsigned char set_resolution; unsigned char set_zoom; unsigned char set_scan_area; unsigned char set_bright; SANE_Range bright_range; unsigned char set_gamma; unsigned char set_halftoning; unsigned char set_color_correction; unsigned char initialize_scanner; unsigned char set_speed; /* B4 and later */ unsigned char set_lcount; unsigned char mirror_image; /* B5 and later */ unsigned char set_gamma_table; /* B4 and later */ unsigned char set_outline_emphasis; /* B4 and later */ unsigned char set_dither; /* B4 and later */ unsigned char set_color_correction_coefficients; /* B3 and later */ unsigned char request_extended_status; /* get extended status from scanner */ unsigned char control_an_extension; /* for extension control */ unsigned char eject; /* for extension control */ unsigned char feed; unsigned char request_push_button_status; unsigned char control_auto_area_segmentation; unsigned char set_film_type; /* for extension control */ unsigned char set_exposure_time; /* F5 only */ unsigned char set_bay; /* F5 only */ unsigned char set_threshold; unsigned char set_focus_position; /* B8 only */ unsigned char request_focus_position; /* B8 only */ unsigned char request_extended_identity; unsigned char request_scanner_status; }; enum { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_MODE, OPT_BIT_DEPTH, OPT_HALFTONE, OPT_DROPOUT, OPT_BRIGHTNESS, OPT_SHARPNESS, OPT_GAMMA_CORRECTION, OPT_COLOR_CORRECTION, OPT_RESOLUTION, OPT_THRESHOLD, OPT_ADVANCED_GROUP, OPT_MIRROR, OPT_AAS, OPT_GAMMA_VECTOR_R, OPT_GAMMA_VECTOR_G, OPT_GAMMA_VECTOR_B, OPT_WAIT_FOR_BUTTON, OPT_CCT_GROUP, OPT_CCT_MODE, OPT_CCT_PROFILE, OPT_PREVIEW_GROUP, OPT_PREVIEW, OPT_GEOMETRY_GROUP, OPT_TL_X, OPT_TL_Y, OPT_BR_X, OPT_BR_Y, OPT_FOCUS_GROUP, OPT_AUTOFOCUS, OPT_FOCUS_POS, OPT_EQU_GROUP, OPT_SOURCE, OPT_AUTO_EJECT, OPT_FILM_TYPE, OPT_BAY, OPT_EJECT, OPT_ADF_MODE, NUM_OPTIONS }; typedef enum { /* hardware connection to the scanner */ SANE_EPSON_NODEV, /* default, no HW specified yet */ SANE_EPSON_SCSI, /* SCSI interface */ SANE_EPSON_PIO, /* parallel interface */ SANE_EPSON_USB, /* USB interface */ SANE_EPSON_NET /* network interface */ } Epson_Connection_Type; struct epson_profile { unsigned int model; double cct[4][9]; }; enum { CCTP_REFLECTIVE = 0, CCTP_COLORNEG, CCTP_MONONEG, CCTP_COLORPOS }; struct epson_profile_map { char *name; unsigned int id; }; extern const struct epson_profile epson_cct_profiles[]; extern const struct epson_profile_map epson_cct_models[]; /* hardware description */ struct Epson_Device { struct Epson_Device *next; char *name; char *model; unsigned int model_id; SANE_Device sane; SANE_Int level; SANE_Range dpi_range; SANE_Range *x_range; /* x range w/out extension */ SANE_Range *y_range; /* y range w/out extension */ SANE_Range fbf_x_range; /* flattbed x range */ SANE_Range fbf_y_range; /* flattbed y range */ SANE_Range adf_x_range; /* autom. document feeder x range */ SANE_Range adf_y_range; /* autom. document feeder y range */ SANE_Range tpu_x_range; /* transparency unit x range */ SANE_Range tpu_y_range; /* transparency unit y range */ SANE_Range tpu2_x_range; /* transparency unit 2 x range */ SANE_Range tpu2_y_range; /* transparency unit 2 y range */ Epson_Connection_Type connection; SANE_Int *res_list; /* list of resolutions */ SANE_Int res_list_size; /* number of entries in this list */ SANE_Int last_res; /* last selected resolution */ SANE_Int last_res_preview; /* last selected preview resolution */ SANE_Word *resolution_list; /* for display purposes we store a second copy */ SANE_Bool extension; /* extension is installed */ SANE_Int use_extension; /* use the installed extension */ SANE_Bool TPU; /* TPU is installed */ SANE_Bool TPU2; /* TPU2 is installed */ SANE_Bool ADF; /* ADF is installed */ SANE_Bool duplex; /* does the ADF handle duplex scanning */ SANE_Bool focusSupport; /* does this scanner have support for "set focus position" ? */ SANE_Bool color_shuffle; /* does this scanner need color shuffling */ SANE_Int maxDepth; /* max. color depth */ SANE_Int *depth_list; SANE_Int optical_res; /* optical resolution */ SANE_Int max_line_distance; SANE_Bool need_double_vertical; SANE_Bool need_color_reorder; SANE_Bool need_reset_on_source_change; SANE_Bool wait_for_button; /* do we have to wait until the scanner button is pressed? */ SANE_Bool extended_commands; struct EpsonCmd *cmd; const struct epson_profile *cct_profile; }; typedef struct Epson_Device Epson_Device; /* an instance of a scanner */ struct Epson_Scanner { struct Epson_Scanner *next; struct Epson_Device *hw; int fd; SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; SANE_Parameters params; SANE_Bool block; SANE_Bool eof; SANE_Byte *buf, *end, *ptr; SANE_Bool canceling; SANE_Word gamma_table[3][256]; SANE_Word cct_table[9]; SANE_Int retry_count; /* buffer lines for color shuffling */ SANE_Byte *line_buffer[LINES_SHUFFLE_MAX]; SANE_Int color_shuffle_line; /* current line number for color shuffling */ SANE_Int line_distance; /* current line distance */ SANE_Int current_output_line; /* line counter when color shuffling */ SANE_Int lines_written; /* debug variable */ SANE_Int left, top, lcount; SANE_Byte currentFocusPosition; /* network buffers */ unsigned char *netbuf, *netptr; size_t netlen; /* extended image data handshaking */ SANE_Int ext_block_len; SANE_Int ext_last_len; SANE_Int ext_blocks; SANE_Int ext_counter; }; typedef struct Epson_Scanner Epson_Scanner; struct mode_param { int color; int flags; int dropout_mask; int depth; }; enum { MODE_BINARY, MODE_GRAY, MODE_COLOR, MODE_INFRARED }; #endif backends-1.3.0/backend/epson2_net.c000066400000000000000000000133311456256263500171130ustar00rootroot00000000000000/* * epson2_net.c - SANE library for Epson scanners. * * Copyright (C) 2006 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ #define DEBUG_DECLARE_ONLY #include "sane/config.h" #ifdef HAVE_SYS_SELECT_H #include #endif #include "sane/sane.h" #include "sane/saneopts.h" #include "sane/sanei_tcp.h" #include "sane/sanei_config.h" #include "sane/sanei_backend.h" #include "epson2.h" #include "epson2_net.h" #include "byteorder.h" #include "sane/sanei_debug.h" static ssize_t sanei_epson_net_read_raw(Epson_Scanner *s, unsigned char *buf, ssize_t wanted, SANE_Status *status) { int ready; ssize_t read = -1; fd_set readable; struct timeval tv; tv.tv_sec = 10; tv.tv_usec = 0; FD_ZERO(&readable); FD_SET(s->fd, &readable); ready = select(s->fd + 1, &readable, NULL, NULL, &tv); if (ready > 0) { read = sanei_tcp_read(s->fd, buf, wanted); } else { DBG(15, "%s: select failed: %d\n", __func__, ready); } *status = SANE_STATUS_GOOD; if (read < wanted) { *status = SANE_STATUS_IO_ERROR; } return read; } static ssize_t sanei_epson_net_read_buf(Epson_Scanner *s, unsigned char *buf, ssize_t wanted, SANE_Status * status) { ssize_t read = 0; DBG(23, "%s: reading up to %lu from buffer at %p, %lu available\n", __func__, (u_long) wanted, (void *) s->netptr, (u_long) s->netlen); if ((size_t) wanted > s->netlen) { *status = SANE_STATUS_IO_ERROR; wanted = s->netlen; } memcpy(buf, s->netptr, wanted); read = wanted; s->netptr += read; s->netlen -= read; if (s->netlen == 0) { DBG(23, "%s: freeing %p\n", __func__, (void *) s->netbuf); free(s->netbuf); s->netbuf = s->netptr = NULL; s->netlen = 0; } return read; } ssize_t sanei_epson_net_read(Epson_Scanner *s, unsigned char *buf, ssize_t wanted, SANE_Status * status) { if (wanted < 0) { *status = SANE_STATUS_INVAL; return 0; } size_t size; ssize_t read = 0; unsigned char header[12]; /* read from remainder of buffer */ if (s->netptr) { return sanei_epson_net_read_buf(s, buf, wanted, status); } /* receive net header */ read = sanei_epson_net_read_raw(s, header, 12, status); if (read != 12) { return 0; } /* validate header */ if (header[0] != 'I' || header[1] != 'S') { DBG(1, "header mismatch: %02X %02x\n", header[0], header[1]); *status = SANE_STATUS_IO_ERROR; return 0; } /* parse payload size */ size = be32atoh(&header[6]); *status = SANE_STATUS_GOOD; if (!s->netbuf) { DBG(15, "%s: direct read\n", __func__); DBG(23, "%s: wanted = %lu, available = %lu\n", __func__, (u_long) wanted, (u_long) size); if ((size_t) wanted > size) { wanted = size; } read = sanei_epson_net_read_raw(s, buf, wanted, status); } else { DBG(15, "%s: buffered read\n", __func__); DBG(23, "%s: bufferable = %lu, available = %lu\n", __func__, (u_long) s->netlen, (u_long) size); if (s->netlen > size) { s->netlen = size; } /* fill buffer */ read = sanei_epson_net_read_raw(s, s->netbuf, s->netlen, status); s->netptr = s->netbuf; s->netlen = (read > 0 ? read : 0); /* copy wanted part */ read = sanei_epson_net_read_buf(s, buf, wanted, status); } return read; } size_t sanei_epson_net_write(Epson_Scanner *s, unsigned int cmd, const unsigned char *buf, size_t buf_size, size_t reply_len, SANE_Status *status) { unsigned char *h1, *h2, *payload; unsigned char *packet = malloc(12 + 8 + buf_size); if (!packet) { *status = SANE_STATUS_NO_MEM; return 0; } h1 = packet; h2 = packet + 12; payload = packet + 12 + 8; if (reply_len) { if (s->netbuf) { DBG(23, "%s, freeing %p, %ld bytes unprocessed\n", __func__, (void *) s->netbuf, (u_long) s->netlen); free(s->netbuf); s->netbuf = s->netptr = NULL; s->netlen = 0; } s->netbuf = malloc(reply_len); if (!s->netbuf) { free(packet); *status = SANE_STATUS_NO_MEM; return 0; } s->netlen = reply_len; DBG(24, "%s: allocated %lu bytes at %p\n", __func__, (u_long) s->netlen, (void *) s->netbuf); } DBG(24, "%s: cmd = %04x, buf = %p, buf_size = %lu, reply_len = %lu\n", __func__, cmd, (void *) buf, (u_long) buf_size, (u_long) reply_len); memset(h1, 0x00, 12); memset(h2, 0x00, 8); h1[0] = 'I'; h1[1] = 'S'; h1[2] = cmd >> 8; h1[3] = cmd; h1[4] = 0x00; h1[5] = 0x0C; /* Don't know what's that */ DBG(24, "H1[0]: %02x %02x %02x %02x\n", h1[0], h1[1], h1[2], h1[3]); if((cmd >> 8) == 0x20) { htobe32a(&h1[6], buf_size + 8); htobe32a(&h2[0], buf_size); htobe32a(&h2[4], reply_len); DBG(24, "H1[6]: %02x %02x %02x %02x (%lu)\n", h1[6], h1[7], h1[8], h1[9], (u_long) (buf_size + 8)); DBG(24, "H2[0]: %02x %02x %02x %02x (%lu)\n", h2[0], h2[1], h2[2], h2[3], (u_long) buf_size); DBG(24, "H2[4]: %02x %02x %02x %02x (%lu)\n", h2[4], h2[5], h2[6], h2[7], (u_long) reply_len); } if ((cmd >> 8) == 0x20 && (buf_size || reply_len)) { if (buf_size) memcpy(payload, buf, buf_size); sanei_tcp_write(s->fd, packet, 12 + 8 + buf_size); } else sanei_tcp_write(s->fd, packet, 12); free(packet); *status = SANE_STATUS_GOOD; return buf_size; } SANE_Status sanei_epson_net_lock(struct Epson_Scanner *s) { SANE_Status status; unsigned char buf[1]; DBG(1, "%s\n", __func__); sanei_epson_net_write(s, 0x2100, NULL, 0, 0, &status); sanei_epson_net_read(s, buf, 1, &status); return status; } SANE_Status sanei_epson_net_unlock(struct Epson_Scanner *s) { SANE_Status status; DBG(1, "%s\n", __func__); sanei_epson_net_write(s, 0x2101, NULL, 0, 0, &status); /* sanei_epson_net_read(s, buf, 1, &status); */ return status; } backends-1.3.0/backend/epson2_net.h000066400000000000000000000010361456256263500171170ustar00rootroot00000000000000#ifndef _EPSON2_NET_H_ #define _EPSON2_NET_H_ #include #include "../include/sane/sane.h" extern ssize_t sanei_epson_net_read(struct Epson_Scanner *s, unsigned char *buf, ssize_t buf_size, SANE_Status *status); extern size_t sanei_epson_net_write(struct Epson_Scanner *s, unsigned int cmd, const unsigned char *buf, size_t buf_size, size_t reply_len, SANE_Status *status); extern SANE_Status sanei_epson_net_lock(struct Epson_Scanner *s); extern SANE_Status sanei_epson_net_unlock(struct Epson_Scanner *s); #endif backends-1.3.0/backend/epson2_scsi.c000066400000000000000000000040311456256263500172630ustar00rootroot00000000000000#undef BACKEND_NAME #define BACKEND_NAME epson2_scsi #include "../include/sane/config.h" #include "../include/sane/sanei_debug.h" #include "../include/sane/sanei_scsi.h" #include "epson2_scsi.h" #ifdef HAVE_STDDEF_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef NEED_SYS_TYPES_H #include #endif #include /* for memset and memcpy */ #include /* sense handler for the sanei_scsi_xxx comands */ SANE_Status sanei_epson2_scsi_sense_handler(int scsi_fd, unsigned char *result, void *arg) { /* to get rid of warnings */ (void) scsi_fd; (void) arg; if (result[0] && result[0] != 0x70) { DBG(2, "%s: sense code = 0x%02x\n", __func__, result[0]); return SANE_STATUS_IO_ERROR; } else { return SANE_STATUS_GOOD; } } SANE_Status sanei_epson2_scsi_inquiry(int fd, void *buf, size_t *buf_size) { unsigned char cmd[6]; int status; memset(cmd, 0, 6); cmd[0] = INQUIRY_COMMAND; cmd[4] = *buf_size > 255 ? 255 : *buf_size; status = sanei_scsi_cmd(fd, cmd, sizeof cmd, buf, buf_size); return status; } SANE_Status sanei_epson2_scsi_test_unit_ready(int fd) { unsigned char cmd[6]; memset(cmd, 0, 6); cmd[0] = TEST_UNIT_READY_COMMAND; return sanei_scsi_cmd2(fd, cmd, sizeof(cmd), NULL, 0, NULL, NULL); } int sanei_epson2_scsi_read(int fd, void *buf, size_t buf_size, SANE_Status *status) { unsigned char cmd[6]; memset(cmd, 0, 6); cmd[0] = READ_6_COMMAND; cmd[2] = buf_size >> 16; cmd[3] = buf_size >> 8; cmd[4] = buf_size; *status = sanei_scsi_cmd2(fd, cmd, sizeof(cmd), NULL, 0, buf, &buf_size); if (*status == SANE_STATUS_GOOD) return buf_size; return 0; } int sanei_epson2_scsi_write(int fd, const void *buf, size_t buf_size, SANE_Status *status) { unsigned char cmd[6]; memset(cmd, 0, sizeof(cmd)); cmd[0] = WRITE_6_COMMAND; cmd[2] = buf_size >> 16; cmd[3] = buf_size >> 8; cmd[4] = buf_size; *status = sanei_scsi_cmd2(fd, cmd, sizeof(cmd), buf, buf_size, NULL, NULL); if (*status == SANE_STATUS_GOOD) return buf_size; return 0; } backends-1.3.0/backend/epson2_scsi.h000066400000000000000000000013511456256263500172720ustar00rootroot00000000000000#ifndef _EPSON2_SCSI_H_ #define _EPSON2_SCSI_H_ #include #include "../include/sane/sane.h" #define TEST_UNIT_READY_COMMAND (0x00) #define READ_6_COMMAND (0x08) #define WRITE_6_COMMAND (0x0a) #define INQUIRY_COMMAND (0x12) #define TYPE_PROCESSOR (0x03) #define INQUIRY_BUF_SIZE (36) SANE_Status sanei_epson2_scsi_sense_handler(int scsi_fd, unsigned char *result, void *arg); SANE_Status sanei_epson2_scsi_inquiry(int fd, void *buf, size_t *buf_size); int sanei_epson2_scsi_read(int fd, void *buf, size_t buf_size, SANE_Status *status); int sanei_epson2_scsi_write(int fd, const void *buf, size_t buf_size, SANE_Status *status); SANE_Status sanei_epson2_scsi_test_unit_ready(int fd); #endif backends-1.3.0/backend/epson2_usb.c000066400000000000000000000246011456256263500171200ustar00rootroot00000000000000#include #include "../include/sane/sanei_usb.h" #include "epson_usb.h" /* generated with epson2usb.pl doc/descriptions/epson2.desc */ SANE_Word sanei_epson_usb_product_ids[] = { 0x0101, /* GT-7000U, Perfection 636U */ 0x0103, /* GT-6600U, Perfection 610 */ 0x0104, /* GT-7600U, GT-7600UF, Perfection 1200U, Perfection 1200U PHOTO */ 0x0105, /* Stylus Scan 2000 */ 0x0106, /* Stylus Scan 2500 */ 0x0107, /* ES-2000, Expression 1600 */ 0x0109, /* ES-8500, Expression 1640XL */ 0x010a, /* GT-8700, GT-8700F, Perfection 1640SU, Perfection 1640SU PHOTO */ 0x010b, /* GT-7700U, Perfection 1240U */ 0x010c, /* GT-6700U, Perfection 640U */ 0x010e, /* ES-2200, Expression 1680 */ 0x0110, /* GT-8200U, GT-8200UF, Perfection 1650, Perfection 1650 PHOTO */ 0x0112, /* GT-9700F, Perfection 2450 PHOTO */ 0x011b, /* GT-9300UF, Perfection 2400 PHOTO */ 0x011c, /* GT-9800F, Perfection 3200 PHOTO */ 0x011e, /* GT-8300UF, Perfection 1660 PHOTO */ 0x0126, /* ES-7000H, GT-15000 */ 0x0128, /* GT-X700, Perfection 4870 PHOTO */ 0x0129, /* ES-10000G, Expression 10000XL */ 0x012a, /* GT-X800, Perfection 4990 PHOTO */ 0x012b, /* ES-H300, GT-2500 */ 0x012c, /* GT-X900, Perfection V700 Photo, Perfection V750 Photo */ 0x0135, /* GT-X970 */ 0x0138, /* ES-H7200, GT-20000 */ 0x014b, /* ES-G11000, Expression 11000XL */ 0x0151, /* GT-X980, Perfection V800 Photo, Perfection V850 Pro */ 0x015b, /* DS-G20000, Expression 12000XL */ 0x0801, /* CC-600PX, Stylus CX5100, Stylus CX5200 */ 0x0802, /* CC-570L, Stylus CX3100, Stylus CX3200 */ 0x0805, /* Stylus CX6300, Stylus CX6400 */ 0x0806, /* PM-A850, Stylus Photo RX600 */ 0x0807, /* Stylus Photo RX500, Stylus Photo RX510 */ 0x0808, /* Stylus CX5300, Stylus CX5400 */ 0x080d, /* Stylus CX4500, Stylus CX4600 */ 0x080e, /* PX-A550, Stylus CX3500, Stylus CX3600, Stylus CX3650 */ 0x080f, /* Stylus Photo RX420, Stylus Photo RX425, Stylus Photo RX430 */ 0x0810, /* PM-A900, Stylus Photo RX700 */ 0x0811, /* PM-A870, Stylus Photo RX620, Stylus Photo RX630 */ 0x0813, /* Stylus CX6500, Stylus CX6600 */ 0x0814, /* PM-A700 */ 0x0815, /* AcuLaser CX11, AcuLaser CX11NF, LP-A500 */ 0x0817, /* LP-M5500, LP-M5500F */ 0x0818, /* Stylus CX3700, Stylus CX3800, Stylus CX3810, Stylus DX3800 */ 0x0819, /* PX-A650, Stylus CX4700, Stylus CX4800, Stylus DX4800, Stylus DX4850 */ 0x081a, /* PM-A750, Stylus Photo RX520, Stylus Photo RX530 */ 0x081c, /* PM-A890, Stylus Photo RX640, Stylus Photo RX650 */ 0x081d, /* PM-A950 */ 0x081f, /* Stylus CX7700, Stylus CX7800 */ 0x0820, /* Stylus CX4100, Stylus CX4200, Stylus DX4200 */ 0x0827, /* PM-A820, Stylus Photo RX560, Stylus Photo RX580, Stylus Photo RX590 */ 0x0828, /* PM-A970 */ 0x0829, /* PM-T990 */ 0x082a, /* PM-A920 */ 0x082b, /* Stylus CX4900, Stylus CX5000, Stylus DX5000 */ 0x082e, /* PX-A720, Stylus CX5900, Stylus CX6000, Stylus DX6000 */ 0x082f, /* PX-A620, Stylus CX3900, Stylus DX4000 */ 0x0830, /* ME 200, Stylus CX2800, Stylus CX2900 */ 0x0833, /* LP-M5600 */ 0x0834, /* LP-M6000 */ 0x0835, /* AcuLaser CX21 */ 0x0836, /* PM-T960 */ 0x0837, /* PM-A940, Stylus Photo RX680, Stylus Photo RX685, Stylus Photo RX690 */ 0x0838, /* PX-A640, Stylus CX7300, Stylus CX7400, Stylus DX7400 */ 0x0839, /* PX-A740, Stylus CX8300, Stylus CX8400, Stylus DX8400 */ 0x083a, /* PX-FA700, Stylus CX9300F, Stylus CX9400Fax, Stylus DX9400F */ 0x083c, /* PM-A840, PM-A840S, Stylus Photo RX585, Stylus Photo RX595, Stylus Photo RX610 */ 0x0841, /* ME 300, PX-401A, Stylus NX100, Stylus SX100, Stylus TX100 */ 0x0843, /* LP-M5000 */ 0x0844, /* Artisan 800, EP-901A, EP-901F, Stylus Photo PX800FW, Stylus Photo TX800FW */ 0x0846, /* Artisan 700, EP-801A, Stylus Photo PX700W, Stylus Photo TX700W */ 0x0847, /* ME Office 700FW, PX-601F, Stylus Office BX600FW, Stylus Office TX600FW, Stylus SX600FW, WorkForce 600 */ 0x0848, /* ME Office 600F, Stylus Office BX300F, Stylus Office TX300F, Stylus NX300 Series */ 0x0849, /* Stylus NX200, Stylus SX200, Stylus SX205, Stylus TX200, Stylus TX203, Stylus TX209 */ 0x084a, /* PX-501A, Stylus NX400, Stylus SX400, Stylus SX405, Stylus TX400 */ 0x084c, /* WorkForce 500 */ 0x084d, /* PX-402A, Stylus NX110 Series, Stylus SX110 Series, Stylus TX110 Series */ 0x084f, /* ME OFFICE 510, Stylus NX210 Series, Stylus SX210 Series, Stylus TX210 Series */ 0x0850, /* EP-702A, Stylus Photo PX650 Series, Stylus Photo TX650 Series */ 0x0851, /* Stylus NX410 Series, Stylus SX410 Series, Stylus TX410 Series */ 0x0852, /* Artisan 710 Series, EP-802A, Stylus Photo PX710W Series, Stylus Photo TX710W Series */ 0x0853, /* Artisan 810 Series, EP-902A, Stylus Photo PX810FW Series */ 0x0854, /* ME OFFICE 650FN Series, Stylus Office BX310FN Series, Stylus Office TX510FN Series, WorkForce 310 Series */ 0x0855, /* PX-602F, Stylus Office BX610FW Series, Stylus Office TX610FW Series, Stylus SX610FW Series, WorkForce 610 Series */ 0x0856, /* PX-502A, Stylus NX510 Series, Stylus SX510W Series, Stylus TX550W Series */ 0x085c, /* ME 320 Series, ME 330 Series, Stylus NX125, Stylus NX127, Stylus SX125, Stylus TX120 Series */ 0x085d, /* ME OFFICE 960FWD Series, PX-603F, Stylus Office BX625FWD, Stylus Office TX620FWD Series, Stylus SX620FW Series, WorkForce 630 Series */ 0x085e, /* ME OFFICE 900WD Series, PX-503A, Stylus Office BX525WD, Stylus NX625, Stylus SX525WD, Stylus TX560WD Series, WorkForce 625 */ 0x085f, /* Stylus Office BX320FW Series, Stylus Office TX525FW, WorkForce 520 Series */ 0x0860, /* Artisan 835, EP-903A, EP-903F, Stylus Photo PX820FWD Series, Stylus Photo TX820FWD Series */ 0x0861, /* Artisan 725, EP-803A, EP-803AW, Stylus Photo PX720WD Series, Stylus Photo TX720WD Series */ 0x0862, /* EP-703A, Stylus Photo PX660 Series */ 0x0863, /* ME OFFICE 620F Series, Stylus Office BX305F, Stylus Office BX305FW, Stylus Office TX320F Series, WorkForce 320 Series */ 0x0864, /* ME OFFICE 560W Series, Stylus NX420 Series, Stylus SX420W Series, Stylus TX420W Series */ 0x0865, /* ME OFFICE 520 Series, Stylus NX220 Series, Stylus SX218, Stylus TX220 Series */ 0x0866, /* AcuLaser MX20DN, AcuLaser MX20DNF, AcuLaser MX21DNF */ 0x0869, /* PX-1600F, WF-7510 Series */ 0x086a, /* PX-673F, Stylus Office BX925FWD, WorkForce 840 Series */ 0x0870, /* Stylus Office BX305FW Plus, WorkForce 435 */ 0x0871, /* K200 Series */ 0x0872, /* K300 Series, WorkForce K301 */ 0x0873, /* L200 Series */ 0x0878, /* Artisan 635, EP-704A */ 0x0879, /* Artisan 837, EP-904A, EP-904F, Stylus Photo PX830FWD Series */ 0x087b, /* Artisan 730 Series, EP-804A, EP-804AR, EP-804AW, Stylus Photo PX730WD Series, Stylus Photo TX730WD Series */ 0x087c, /* PX-1700F, WF-7520 Series */ 0x087d, /* PX-B750F, WP-4511, WP-4515, WP-4521, WP-4525, WP-4530 Series, WP-4540 Series */ 0x087e, /* WP-4590 Series */ 0x087f, /* PX-403A */ 0x0880, /* ME OFFICE 570W Series, PX-434A, Stylus NX330 Series, Stylus SX430W Series, Stylus TX430W Series */ 0x0881, /* ME OFFICE 535, PX-404A, Stylus SX230 Series, Stylus TX235 */ 0x0883, /* ME 340 Series, Stylus NX130 Series, Stylus SX130 Series, Stylus TX130 Series */ 0x0884, /* Stylus NX430W Series, Stylus SX440W Series, Stylus TX435W */ 0x0885, /* Stylus NX230 Series, Stylus SX235W, Stylus TX230W Series */ 0x088d, /* Epson ME 350 */ 0x088f, /* Stylus Office BX635FWD, WorkForce 645 */ 0x0890, /* ME OFFICE 940FW Series, Stylus Office BX630FW Series, WorkForce 545 */ 0x0891, /* PX-504A, Stylus Office BX535WD, Stylus NX530 Series, Stylus NX635, Stylus SX535WD */ 0x0892, /* Stylus Office BX935FWD, WorkForce 845 */ 0x0893, /* EP-774A */ 0x0894, /* LP-M5300 Series */ 0x0895, /* PX-045A, XP-100 Series */ 0x0896, /* ME-301, XP-200 Series */ 0x0897, /* ME-303, PX-405A */ 0x0898, /* ME-401, PX-435A, XP-300 Series, XP-400 Series */ 0x0899, /* PX-605F, PX-675F, WF-3520 Series, WF-3530 Series, WF-3540 Series */ 0x089a, /* EP-905F, XP-850 Series */ 0x089b, /* EP-905A, XP-800 Series */ 0x089c, /* EP-805A, EP-805AR, EP-805AW, XP-750 Series */ 0x089d, /* XP-700 Series */ 0x089e, /* EP-775A, EP-775AW, XP-600 Series */ 0x089f, /* EP-705A */ 0x08a0, /* ME-101 */ 0x08a1, /* L210 Series, L350, L351 */ 0x08a5, /* PX-505F, WF-2510 Series */ 0x08a6, /* PX-535F, WF-2520 Series, WF-2530 Series, WF-2540 Series */ 0x08a7, /* WP-M4525, WP-M4521, PX-K751F, WP-M4595 */ 0x08a8, /* L355, L358 */ 0x08a9, /* L550 Series */ 0x08aa, /* M200 Series */ 0x08ab, /* WF-M1560 Series */ 0x08ac, /* AL-MX300DN Series, AL-MX300DNF Series */ 0x08ad, /* LP-M8040, LP-M8040A, LP-M8040F */ 0x08ae, /* PX-046A, XP-211, XP-212, XP-215 */ 0x08af, /* PX-436A, XP-310 Series */ 0x08b0, /* XP-410 Series */ 0x08b3, /* EP-976A3, XP-950 Series */ 0x08b4, /* EP-906F, XP-810 Series */ 0x08b5, /* EP-806AB, EP-806AR, EP-806AW, XP-710 Series */ 0x08b6, /* EP-776AB, EP-776AW, XP-610 Series */ 0x08b7, /* EP-706A, XP-510 Series */ 0x08b8, /* PX-M740F, PX-M741F, WF-3620 Series, WF-3640 Series */ 0x08b9, /* PX-M5040F, PX-M5041F, WF-7610 Series, WF-7620 Series */ 0x08bd, /* PX-M840F, WF-5620 Series, WF-5690 Series */ 0x08be, /* WF-4630 Series, WF-4640 Series */ 0x08bf, /* PX-437A, XP-320 Series */ 0x08c0, /* PX-047A, XP-225 */ 0x08c1, /* XP-420 Series */ 0x08c3, /* PX-M650A, PX-M650F, WF-2650 Series, WF-2660 Series */ 0x08c4, /* WF-2630 Series */ 0x08c5, /* EP-977A3 */ 0x08c6, /* EP-907F, XP-820 Series, XP-860 Series */ 0x08c7, /* EP-807AB, EP-807AR, EP-807AW, XP-720 Series, XP-760 Series */ 0x08c8, /* EP-777A, XP-520 Series, XP-620 Series */ 0x08c9, /* EP-707A */ 0x08ca, /* L850 Series */ 0x08cd, /* WF-R4640 Series, WF-R5690 Series */ 0x08d0, /* PX-M350F, WF-M5690 Series */ 0x08d1, /* L360 Series */ 0x08d2, /* L365 Series, L366 Series */ 0x1102, /* PX-048A Series, XP-230 Series, XP-235 Series */ 0x1105, /* ET-2500 Series, L375 Series */ 0x110f, /* PX-M160T Series */ 0x1116, /* XP-240 243 245 247 Series, XP-427, PX-049A Series */ 0x1120, /* L380 */ 0x1121, /* ET-2650, L495 */ 0x1122, /* ET-2600 Series, ET-2610 Series, L3050 Series, L3060 Series, L395 Series, L396 Series */ 0x113d, /* XP-255 */ 0x113e, /* XP-452 455 Series */ 0x1141, /* L3100 Series */ 0x1142, /* L3110 Series */ 0x1188, /* L3210 Series */ 0x1189, /* L3200 Series */ 0 /* last entry - this is used for devices that are specified in the config file as "usb " */ }; int sanei_epson_getNumberOfUSBProductIds(void) { return sizeof (sanei_epson_usb_product_ids) / sizeof (SANE_Word); } backends-1.3.0/backend/epson_scsi.c000066400000000000000000000041631456256263500172070ustar00rootroot00000000000000#ifdef _AIX #include "../include/lalloca.h" /* MUST come first for AIX! */ #endif #undef BACKEND_NAME #define BACKEND_NAME epson_scsi #include "../include/sane/config.h" #include "../include/sane/sanei_debug.h" #include "../include/sane/sanei_scsi.h" #include "epson_scsi.h" #include "../include/lalloca.h" #ifdef HAVE_STDDEF_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef NEED_SYS_TYPES_H #include #endif #include /* for memset and memcpy */ #include /* * sense handler for the sanei_scsi_XXX comands */ SANE_Status sanei_epson_scsi_sense_handler (int scsi_fd, u_char * result, void *arg) { /* to get rid of warnings */ (void) scsi_fd; (void) arg; if (result[0] && result[0] != 0x70) { DBG (2, "sense_handler() : sense code = 0x%02x\n", result[0]); return SANE_STATUS_IO_ERROR; } else { return SANE_STATUS_GOOD; } } /* * * */ SANE_Status sanei_epson_scsi_inquiry (int fd, int page_code, void *buf, size_t * buf_size) { u_char cmd[6]; int status; memset (cmd, 0, 6); cmd[0] = INQUIRY_COMMAND; cmd[2] = page_code; cmd[4] = *buf_size > 255 ? 255 : *buf_size; status = sanei_scsi_cmd (fd, cmd, sizeof cmd, buf, buf_size); return status; } /* * * */ int sanei_epson_scsi_read (int fd, void *buf, size_t buf_size, SANE_Status * status) { u_char cmd[6]; memset (cmd, 0, 6); cmd[0] = READ_6_COMMAND; cmd[2] = buf_size >> 16; cmd[3] = buf_size >> 8; cmd[4] = buf_size; if (SANE_STATUS_GOOD == (*status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, &buf_size))) return buf_size; return 0; } /* * * */ int sanei_epson_scsi_write (int fd, const void *buf, size_t buf_size, SANE_Status * status) { u_char *cmd; cmd = alloca (8 + buf_size); memset (cmd, 0, 8); cmd[0] = WRITE_6_COMMAND; cmd[2] = buf_size >> 16; cmd[3] = buf_size >> 8; cmd[4] = buf_size; memcpy (cmd + 8, buf, buf_size); if (SANE_STATUS_GOOD == (*status = sanei_scsi_cmd2 (fd, cmd, 6, cmd + 8, buf_size, NULL, NULL))) return buf_size; return 0; } backends-1.3.0/backend/epson_scsi.h000066400000000000000000000013001456256263500172020ustar00rootroot00000000000000#ifndef _EPSON_SCSI_H_ #define _EPSON_SCSI_H_ #include #include "../include/sane/sane.h" #define TEST_UNIT_READY_COMMAND (0x00) #define READ_6_COMMAND (0x08) #define WRITE_6_COMMAND (0x0a) #define INQUIRY_COMMAND (0x12) #define TYPE_PROCESSOR (0x03) #define INQUIRY_BUF_SIZE (36) SANE_Status sanei_epson_scsi_sense_handler (int scsi_fd, u_char * result, void *arg); SANE_Status sanei_epson_scsi_inquiry (int fd, int page_code, void *buf, size_t * buf_size); int sanei_epson_scsi_read (int fd, void *buf, size_t buf_size, SANE_Status * status); int sanei_epson_scsi_write (int fd, const void *buf, size_t buf_size, SANE_Status * status); #endif backends-1.3.0/backend/epson_usb.c000066400000000000000000000027501456256263500170370ustar00rootroot00000000000000#include #include "../include/sane/sanei_usb.h" #include "epson_usb.h" /* generated with epson2usb.pl doc/descriptions/epson.desc */ SANE_Word sanei_epson_usb_product_ids[] = { 0x101, /* Perfection 636U */ 0x103, /* Perfection 610 */ 0x104, /* Perfection 1200U, Perfection 1200Photo */ 0x107, /* Expression 1600 */ 0x10a, /* Perfection 1640 */ 0x10b, /* Perfection 1240 */ 0x10c, /* Perfection 640 */ 0x10e, /* Expression 1680 */ 0x110, /* Perfection 1650 */ 0x112, /* Perfection 2450 */ 0x11b, /* Perfection 2400 */ 0x11c, /* Perfection 3200 */ 0x11e, /* Perfection 1660 */ 0x128, /* Perfection 4870 */ 0x12a, /* Perfection 4990 */ 0x12c, /* V700, V750 */ 0x801, /* CX-5200, CX-5400 */ 0x802, /* CX-3200 */ 0x805, /* CX-6300, CX-6400 */ 0x806, /* RX-600 */ 0x807, /* RX-500 */ 0x808, /* CX-5400 */ 0x80d, /* CX-4600 */ 0x80e, /* CX-3600, CX-3650 */ 0x80f, /* RX-425 */ 0x810, /* RX-700 */ 0x811, /* RX-620 */ 0x813, /* CX-6500, CX-6600 */ 0x815, /* AcuLaser CX11, AcuLaser CX11NF */ 0x818, /* DX-3850, CX-3700, CX-3800, DX-3800 */ 0x819, /* CX-4800 */ 0x820, /* CX-4200 */ 0x82b, /* CX-5000, DX-5000, DX-5050 */ 0x82e, /* DX-6000 */ 0x82f, /* DX-4050 */ 0x838, /* DX-7400 */ 0 /* last entry - this is used for devices that are specified in the config file as "usb " */ }; int sanei_epson_getNumberOfUSBProductIds (void) { return sizeof (sanei_epson_usb_product_ids) / sizeof (SANE_Word); } backends-1.3.0/backend/epson_usb.h000066400000000000000000000003041456256263500170350ustar00rootroot00000000000000#ifndef _EPSON_USB_H_ #define _EPSON_USB_H_ #define SANE_EPSON_VENDOR_ID (0x4b8) extern SANE_Word sanei_epson_usb_product_ids[]; extern int sanei_epson_getNumberOfUSBProductIds (void); #endif backends-1.3.0/backend/epsonds-cmd.c000066400000000000000000000552701456256263500172630ustar00rootroot00000000000000/* * epsonds-cmd.c - Epson ESC/I-2 routines. * * Copyright (C) 2015 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ #define DEBUG_DECLARE_ONLY #include "sane/config.h" #include #include /* sleep */ #include "epsonds.h" #include "epsonds-io.h" #include "epsonds-cmd.h" #include "epsonds-ops.h" #include "epsonds-net.h" static SANE_Status esci2_parse_block(char *buf, int len, void *userdata, SANE_Status (*cb)(void *userdata, char *token, int len)) { SANE_Status status = SANE_STATUS_GOOD; SANE_Status delayed_status = SANE_STATUS_GOOD; char *start = buf; char *end = (buf + len) - 1; /* 0 : # * 1-3: param * 4- : data */ while (1) { char param[4]; while (*start != '#' && start < end) start++; if (*start != '#') break; param[0] = *++start; param[1] = *++start; param[2] = *++start; param[3] = '\0'; if (strncmp("---", param, 3) == 0) break; /* ugly hack to skip over GMT in RESA */ if (strncmp("GMT", param, 3) == 0 && *(start + 5) == 'h') { start = start + 4 + 0x100; continue; } /* find the end of the token */ { int tlen; char *next = start; while (*next != '#' && *next != 0x00 && next < end) next++; tlen = next - start - 1; if (cb) { status = cb(userdata, start - 2, tlen); if (status != SANE_STATUS_GOOD) { delayed_status = status; } } start = next; } } if (delayed_status != SANE_STATUS_GOOD) return delayed_status; return status; } static SANE_Bool esci2_check_header(const char *cmd, const char *buf, unsigned int *more) { int err; *more = 0; if (strncmp(cmd, buf, 4) != 0) { if (strncmp("UNKN", buf, 4) == 0) { DBG(1, "UNKN reply code received\n"); } else if (strncmp("INVD", buf, 4) == 0) { DBG(1, "INVD reply code received\n"); } else { DBG(1, "%c%c%c%c, unexpected reply code\n", buf[0], buf[1], buf[2], buf[3]); } return 0; } /* INFOx0000100#.... */ /* read the answer len */ if (buf[4] != 'x') { DBG(1, "unknown type in header: %c\n", buf[4]); return 0; } err = sscanf(&buf[5], "%7x#", more); if (err != 1) { DBG(1, "cannot decode length from header\n"); return 0; } return 1; } static SANE_Status esci2_cmd(epsonds_scanner* s, char *cmd, size_t len, char *payload, size_t plen, void *userdata, SANE_Status (*cb)(void *userdata, char *token, int len)) { SANE_Status status; unsigned int more; char header[13], rbuf[64]; /* add one more byte for header buffer to correct buffer overflow issue,*/ char *buf; DBG(8, "%s: %4s len %lu, payload len: %lu\n", __func__, cmd, len, plen); memset(header, 0x00, sizeof(header)); memset(rbuf, 0x00, sizeof(rbuf)); // extra safety check, will not happen if (len != 12) { DBG(1, "%s: command has wrong size (%lu != 12)\n", __func__, len); return SANE_STATUS_INVAL; } // merge ParameterBlock size sprintf(header, "%4.4sx%07x", cmd, (unsigned int)plen); // send RequestBlock, request immediate response if there's no payload status = eds_txrx(s, header, len, rbuf, (plen > 0) ? 0 : 64); /* pointer to the token's value */ buf = rbuf + 12; /* nrd / nrdBUSY */ DBG(8, "buf = %s\n",buf); if (strncmp("#nrd", buf, 4) == 0) { buf += 4; DBG(8, "buf = %s\n",buf); if (strncmp("BUSY", buf, 4) == 0) { DBG(8, "device busy\n"); DBG(8, "SANE_STATUS:%d\n", SANE_STATUS_DEVICE_BUSY); return SANE_STATUS_DEVICE_BUSY; } } if (status != SANE_STATUS_GOOD) { return status; } /* send ParameterBlock, request response */ if (plen) { DBG(8, " %12.12s (%lu)\n", header, plen); status = eds_txrx(s, payload, plen, rbuf, 64); if (status != SANE_STATUS_GOOD) { return status; } } /* rxbuf holds the DataHeaderBlock, which should be * parsed to know if we need to read more data */ if (!esci2_check_header(cmd, rbuf, &more)) { return SANE_STATUS_IO_ERROR; } /* parse the received header block */ if (cb) { status = esci2_parse_block(rbuf + 12, 64 - 12, userdata, cb); if (status != SANE_STATUS_GOOD && status != SANE_STATUS_DEVICE_BUSY) { DBG(1, "%s: %4s error while parsing received header\n", __func__, cmd); } } /* header valid, get the data block if present */ if (more) { char *pbuf = malloc(more); if (pbuf) { if (s->hw->connection == SANE_EPSONDS_NET) { epsonds_net_request_read(s, more); } ssize_t read = eds_recv(s, pbuf, more, &status); if (read != more) { free(pbuf); return SANE_STATUS_IO_ERROR; } /* parse the received data block */ if (cb) { status = esci2_parse_block(pbuf, more, userdata, cb); if (status != SANE_STATUS_GOOD) { DBG(1, "%s: %4s error while parsing received data block\n", __func__, cmd); } } free(pbuf); } else { return SANE_STATUS_NO_MEM; } } return status; } static SANE_Status esci2_cmd_simple(epsonds_scanner* s, char *cmd, SANE_Status (*cb)(void *userdata, char *token, int len)) { return esci2_cmd(s, cmd, 12, NULL, 0, s, cb); } SANE_Status esci2_fin(epsonds_scanner *s) { SANE_Status status; DBG(5, "%s\n", __func__); status = esci2_cmd_simple(s, "FIN x0000000", NULL); for(int i = 0; i < 10; i++) { if(status == SANE_STATUS_DEVICE_BUSY || status == SANE_STATUS_IO_ERROR) { status = esci2_cmd_simple(s, "FIN x0000000", NULL); } else { DBG(1, "break\n"); break; } DBG(1, "sleep(5)\n"); sleep(5); } s->locked = 0; return status; } SANE_Status esci2_can(epsonds_scanner *s) { return esci2_cmd_simple(s, "CAN x0000000", NULL); } static int decode_value(char *buf, int len) { char tmp[10]; memcpy(tmp, buf, len); tmp[len] = '\0'; if (buf[0] == 'd' && len == 4) { return strtol(buf + 1, NULL, 10); } else if (buf[0] == 'i' && len == 8) { return strtol(buf + 1, NULL, 10); } else if (buf[0] == 'x' && len == 8) { return strtol(buf + 1, NULL, 16); } else if (buf[0] == 'h' && len == 4) { return strtol(buf + 1, NULL, 16); } return -1; } /* h000 */ static char *decode_binary(char *buf, int len) { char tmp[6]; int hl; memcpy(tmp, buf, 4); tmp[4] = '\0'; if (buf[0] != 'h') return NULL; hl = strtol(tmp + 1, NULL, 16); if (hl > len) hl = len; if (hl) { char *v = malloc(hl + 1); memcpy(v, buf + 4, hl); v[hl] = '\0'; return v; } return NULL; } static char *decode_string(char *buf, int len) { char *p, *s = decode_binary(buf, len); if (s == NULL) return NULL; /* trim white space at the end */ p = s + strlen(s); while (*--p == ' ') *p = '\0'; return s; } static void debug_token(int level, const char *func, char *token, int len) { char *tdata = malloc(len + 1); memcpy(tdata, token + 3, len); tdata[len] = '\0'; DBG(level, "%s: %3.3s / %s / %d\n", func, token, tdata, len); free(tdata); } static SANE_Status info_cb(void *userdata, char *token, int len) { epsonds_scanner *s = (epsonds_scanner *)userdata; char *value; /* pointer to the token's value */ value = token + 3; /* nrd / nrdBUSY */ if (strncmp("nrd", token, 3) == 0) { if (strncmp("BUSY", value, 4) == 0) { return SANE_STATUS_DEVICE_BUSY; } } if (strncmp("PRD", token, 3) == 0) { free(s->hw->model); s->hw->model = decode_string(value, len); s->hw->sane.model = s->hw->model; DBG(1, " product: %s\n", s->hw->model); } if (strncmp("VER", token, 3) == 0) { char *v = decode_string(value, len); DBG(1, " version: %s\n", v); free(v); } if (strncmp("S/N", token, 3) == 0) { char *v = decode_string(value, len); DBG(1, " serial: %s\n", v); free(v); } if (strncmp("ADF", token, 3) == 0) { s->hw->has_adf = 1; if (len == 8) { if (strncmp("TYPEPAGE", value, len) == 0) { DBG(1, " ADF: page type\n"); } if (strncmp("TYPEFEED", value, len) == 0) { DBG(1, " ADF: sheet feed type\n"); } if (strncmp("DPLX1SCN", value, len) == 0) { DBG(1, " ADF: duplex single pass\n"); s->hw->adf_singlepass = 1; } if (strncmp("DPLX2SCN", value, len) == 0) { DBG(1, " ADF: duplex double pass\n"); s->hw->adf_singlepass = 0; } if (strncmp("FORDPF1N", value, len) == 0) { DBG(1, " ADF: order is 1 to N\n"); } if (strncmp("FORDPFN1", value, len) == 0) { DBG(1, " ADF: order is N to 1\n"); } if (strncmp("ALGNLEFT", value, len) == 0) { DBG(1, " ADF: left aligned\n"); s->hw->adf_alignment = 0; } if (strncmp("ALGNCNTR", value, len) == 0) { DBG(1, " ADF: center aligned\n"); s->hw->adf_alignment = 1; } if (strncmp("ALGNRIGT", value, len) == 0) { DBG(1, " ADF: right aligned (not supported!)\n"); s->hw->adf_alignment = 2; } } if (len == 4) { if (strncmp("PREF", value, len) == 0) { DBG(1, " ADF: auto pre-feed\n"); } if (strncmp("ASCN", value, len) == 0) { DBG(1, " ADF: auto scan\n"); } if (strncmp("RCVR", value, len) == 0) { DBG(1, " ADF: auto recovery\n"); } } if (len == 20) { /* ADFAREAi0000850i0001400 */ if (strncmp("AREA", value, 4) == 0) { int min = decode_value(value + 4, 8); int max = decode_value(value + 4 + 8, 8); DBG(1, " ADF: area %dx%d @ 100dpi\n", min, max); eds_set_adf_area(s->hw, min, max, 100); } if (strncmp("AMIN", value, 4) == 0) { int min = decode_value(value + 4, 8); int max = decode_value(value + 4 + 8, 8); DBG(1, " ADF: min %dx%d @ 100dpi\n", min, max); } if (strncmp("AMAX", value, 4) == 0) { int min = decode_value(value + 4, 8); int max = decode_value(value + 4 + 8, 8); DBG(1, " ADF: max %dx%d @ 100dpi\n", min, max); } } if (len == 16) { if (strncmp("AREA", value, 4) == 0) { int min = decode_value(value + 4, 4); int max = decode_value(value + 4 + 4, 8); DBG(1, " ADF: area %dx%d @ 100dpi\n", min, max); eds_set_adf_area(s->hw, min, max, 100); } if (strncmp("AMAX", value, 4) == 0) { // d int min = decode_value(value + 4, 4); // i int max = decode_value(value + 4 + 4, 8); DBG(1, " ADF: max %dx%d @ 100dpi\n", min, max); } } if (len == 12) { /* RESOi0000600 */ if (strncmp("RESO", value, 4) == 0) { int res = decode_value(value + 4, 8); DBG(1, " ADF: basic resolution is %d dpi\n", res); } /* OVSNd025d035 */ if (strncmp("OVSN", value, 4) == 0) { int x = decode_value(value + 4, 4); int y = decode_value(value + 4 + 4, 4); DBG(1, " ADF: overscan %dx%d @ 100dpi\n", x, y); } } } if (strncmp("FB ", token, 3) == 0) { s->hw->has_fb = 1; if (len == 20) { /* AREAi0000850i0001400 */ if (strncmp("AREA", value, 4) == 0) { int min = decode_value(value + 4, 8); int max = decode_value(value + 4 + 8, 8); DBG(1, " FB: area %dx%d @ 100dpi\n", min, max); eds_set_fbf_area(s->hw, min, max, 100); } } if (len == 16) { /* AREAi0000850i0001400 */ if (strncmp("AREA", value, 4) == 0) { //d int min = decode_value(value + 4, 4); //i int max = decode_value(value + 4 + 4, 8); DBG(1, " FB: area %dx%d @ 100dpi\n", min, max); eds_set_fbf_area(s->hw, min, max, 100); } } if (len == 8) { if (strncmp("ALGNLEFT", value, len) == 0) { DBG(1, " FB: left aligned\n"); s->hw->fbf_alignment = 0; } if (strncmp("ALGNCNTR", value, len) == 0) { DBG(1, " FB: center aligned\n"); s->hw->fbf_alignment = 1; } if (strncmp("ALGNRIGT", value, len) == 0) { DBG(1, " FB: right aligned (not supported!)\n"); s->hw->fbf_alignment = 2; } } if (len == 12) { /* RESOi0000600 */ if (strncmp("RESO", value, 4) == 0) { int res = decode_value(value + 4, 8); DBG(1, " FB: basic resolution is %d dpi\n", res); } /* OVSNd025d035 */ if (strncmp("OVSN", value, 4) == 0) { int x = decode_value(value + 4, 4); int y = decode_value(value + 4 + 4, 4); DBG(1, " FB: overscan %dx%d @ 100dpi\n", x, y); } } if (len == 4) { if (strncmp("DETX", value, len) == 0) { DBG(1, " FB: paper width detection\n"); } if (strncmp("DETY", value, len) == 0) { DBG(1, " FB: paper height detection\n"); } } } return SANE_STATUS_GOOD; } SANE_Status esci2_info(epsonds_scanner *s) { SANE_Status status; int i = 4; DBG(1, "= gathering device information\n"); do { status = esci2_cmd_simple(s, "INFOx0000000", &info_cb); if (status == SANE_STATUS_DEVICE_BUSY) { sleep(2); } i--; } while (status == SANE_STATUS_DEVICE_BUSY && i); return status; } /* CAPA */ static SANE_Status capa_cb(void *userdata, char *token, int len) { epsonds_scanner *s = (epsonds_scanner *)userdata; char *value = token + 3; if (DBG_LEVEL >= 11) { debug_token(DBG_LEVEL, __func__, token, len); } if (len == 4) { if (strncmp("ADFDPLX", token, 3 + 4) == 0) { DBG(1, " ADF: duplex\n"); s->hw->adf_is_duplex = 1; } if (strncmp("ADFSKEW", token, 3 + 4) == 0) { DBG(1, " ADF: skew correction\n"); s->hw->adf_has_skew = 1; } if (strncmp("ADFOVSN", token, 3 + 4) == 0) { DBG(1, " ADF: overscan\n"); } if (strncmp("ADFPEDT", token, 3 + 4) == 0) { DBG(1, " ADF: paper end detection\n"); } if (strncmp("ADFLOAD", token, 3 + 4) == 0) { DBG(1, " ADF: paper load\n"); s->hw->adf_has_load = 1; } if (strncmp("ADFEJCT", token, 3 + 4) == 0) { DBG(1, " ADF: paper eject\n"); s->hw->adf_has_eject = 1; } if (strncmp("ADFCRP ", token, 3 + 4) == 0) { DBG(1, " ADF: image cropping\n"); s->hw->adf_has_crp = 1; } if (strncmp("ADFFAST", token, 3 + 4) == 0) { DBG(1, " ADF: fast mode available\n"); } if (strncmp("ADFDFL1", token, 3 + 4) == 0) { DBG(1, " ADF: double feed detection\n"); s->hw->adf_has_dfd = 1; } } if (len == 8 && strncmp("ADFDFL1DFL2", token, 3 + 4) == 0) { DBG(1, " ADF: double feed detection (high sensitivity)\n"); s->hw->adf_has_dfd = 2; } if (strncmp("FMT", token, 3) == 0) { /* a bit ugly... */ if (len >= 8) { if (strncmp("RAW ", value + 4, 4) == 0) { s->hw->has_raw = 1; } } if (len >= 12) { if (strncmp("RAW ", value + 8, 4) == 0) { s->hw->has_raw = 1; } } } if (strncmp("COLLIST", token, 3 + 4) == 0) { char *p = token + 3 + 4; int count = (len - 4); int readBytes = 0; s->hw->has_mono = 0; while (readBytes < count) { if (strncmp(p, "M001", 4) == 0) { s->hw->has_mono = 1; } readBytes+=4; p+=4; } } /* RSMRANGi0000050i0000600 */ if (strncmp("RSMRANG", token, 3 + 4) == 0) { char *p = token + 3 + 4; if (p[0] == 'i') { int min = decode_value(p, 8); int max = decode_value(p + 8, 8); eds_set_resolution_range(s->hw, min, max); DBG(1, "resolution min/max %d/%d\n", min, max); } } /* RSMLISTi0000300i0000600 */ if (strncmp("RSMLIST", token, 3 + 4) == 0) { char *p = token + 3 + 4; int count = (len - 4); int readBytes = 0; while (readBytes < count) { if(*p == 'i') { eds_add_resolution(s->hw, decode_value(p, 8)); p += 8; readBytes += 8; }else if(*p == 'd') { eds_add_resolution(s->hw, decode_value(p, 4)); p += 4; readBytes +=4; } } } return SANE_STATUS_GOOD; } SANE_Status esci2_capa(epsonds_scanner *s) { return esci2_cmd_simple(s, "CAPAx0000000", &capa_cb); } /* STAT */ static SANE_Status stat_cb(void *userdata, char *token, int len) { char *value = token + 3; (void) userdata; if (DBG_LEVEL >= 11) { debug_token(DBG_LEVEL, __func__, token, len); } if (strncmp("ERR", token, 3) == 0) { if (strncmp("ADF PE ", value, len) == 0) { DBG(1, " PE : paper empty\n"); return SANE_STATUS_NO_DOCS; } if (strncmp("ADF OPN", value, len) == 0) { DBG(1, " conver open\n"); return SANE_STATUS_COVER_OPEN; } } return SANE_STATUS_GOOD; } SANE_Status esci2_stat(epsonds_scanner *s) { return esci2_cmd_simple(s, "STATx0000000", &stat_cb); } /* RESA */ static SANE_Status resa_cb(void *userdata, char *token, int len) { /* epsonds_scanner *s = (epsonds_scanner *)userdata; */ (void) userdata; if (DBG_LEVEL >= 11) { debug_token(DBG_LEVEL, __func__, token, len); } return SANE_STATUS_GOOD; } SANE_Status esci2_resa(epsonds_scanner *s) { return esci2_cmd_simple(s, "RESAx0000000", &resa_cb); } /* PARA */ static SANE_Status para_cb(void *userdata, char *token, int len) { if (DBG_LEVEL >= 11) { debug_token(DBG_LEVEL, __func__, token, len); } (void) userdata; if (strncmp("par", token, 3) == 0) { if (strncmp("FAIL", token + 3, 4) == 0) { DBG(1, "%s: parameter setting failed\n", __func__); return SANE_STATUS_INVAL; } } return SANE_STATUS_GOOD; } SANE_Status esci2_para(epsonds_scanner *s, char *parameters, int len) { DBG(8, "%s: %s\n", __func__, parameters); return esci2_cmd(s, "PARAx0000000", 12, parameters, len, NULL, ¶_cb); } SANE_Status esci2_mech(epsonds_scanner *s, char *parameters) { DBG(8, "%s: %s\n", __func__, parameters); return esci2_cmd(s, "MECHx0000000", 12, parameters, strlen(parameters), NULL, ¶_cb); } SANE_Status esci2_trdt(epsonds_scanner *s) { return esci2_cmd_simple(s, "TRDTx0000000", NULL); } static SANE_Status img_cb(void *userdata, char *token, int len) { struct epsonds_scanner *s = userdata; if (DBG_LEVEL >= 11) { debug_token(DBG_LEVEL, __func__, token, len); } /* psti0000256i0000000i0000945 / 24 */ /* integer comparison first so it's faster */ if (len == 24 && strncmp("pst", token, 3) == 0) { s->dummy = decode_value(token + 3 + 8, 8); DBG(10, "%s: pst width: %d, height: %d, dummy: %d\n", __func__, decode_value(token + 3, 8), decode_value(token + 3 + 8 + 8, 8), s->dummy); return SANE_STATUS_GOOD; } if (len == 12 && strncmp("pst", token, 3) == 0) { s->dummy = decode_value(token + 3 + 4, 4); DBG(10, "%s: pst width: %d, height: %d, dummy: %d\n", __func__, decode_value(token + 3, 4), decode_value(token + 3 + 4 + 4, 4), s->dummy); return SANE_STATUS_GOOD; } if (len == 16 && strncmp("pst", token, 3) == 0) { s->dummy = decode_value(token + 3 + 4, 4); DBG(10, "%s: pst width: %d, height: %d, dummy: %d\n", __func__, decode_value(token + 3, 4), decode_value(token + 3 + 4 + 4, 8), s->dummy); return SANE_STATUS_GOOD; } if (len == 20 && strncmp("pst", token, 3) == 0) { s->dummy = decode_value(token + 3 + 8, 4); DBG(10, "%s: pst width: %d, height: %d, dummy: %d\n", __func__, decode_value(token + 3, 8), decode_value(token + 3 + 8 + 4, 8), s->dummy); return SANE_STATUS_GOOD; } // i0001696i0002347 if (len == 16 && strncmp("pen", token, 3) == 0) { DBG(10, "%s: page end\n", __func__); s->eof = 1; if (s->isflatbedScan) { s->scanning = 0; } DBG(10, "%s: pen width: %d, height: %d, dummy: %d\n", __func__, decode_value(token + 3, 8), decode_value(token + 3 + 8, 8), s->dummy); s->width_temp = decode_value(token + 3, 8); s->height_temp = decode_value(token + 3 + 8, 8); return SANE_STATUS_EOF; } // d696i0002347 if (len == 12 && strncmp("pen", token, 3) == 0) { DBG(10, "%s: page end\n", __func__); s->eof = 1; if (s->isflatbedScan) { s->scanning = 0; } DBG(10, "%s: pen width: %d, height: %d, dummy: %d\n", __func__, decode_value(token + 3, 4), decode_value(token + 3 + 4, 8), s->dummy); s->width_temp = decode_value(token + 3, 4); s->height_temp = decode_value(token + 3 + 4, 8); return SANE_STATUS_EOF; } // d696d2347 if (len == 8 && strncmp("pen", token, 3) == 0) { DBG(10, "%s: page end\n", __func__); s->eof = 1; if (s->isflatbedScan) { s->scanning = 0; } DBG(10, "%s: pen width: %d, height: %d, dummy: %d\n", __func__, decode_value(token + 3, 4), decode_value(token + 3 + 4, 4), s->dummy); s->width_temp = decode_value(token + 3, 4); s->height_temp = decode_value(token + 3 + 4, 4); return SANE_STATUS_EOF; } /* typIMGA or typIMGB */ if (len == 4 && strncmp("typ", token, 3) == 0) { if (token[6] == 'B') s->backside = 1; else s->backside = 0; return SANE_STATUS_GOOD; } if (strncmp("err", token, 3) == 0) { char *option = token + 3; /* ADF, TPU, FB */ char *cause = token + 3 + 4; /* OPN, PJ, PE, ERR, LTF, LOCK, DFED, DTCL, AUT, PERM */ s->scanning = 0; s->scanEnd = 1; DBG(1, "%s: error on option %3.3s, cause %4.4s\n", __func__, option, cause); if (cause[0] == 'P' && cause[1] == 'J') return SANE_STATUS_JAMMED; if (cause[0] == 'P' && cause[1] == 'E') return SANE_STATUS_NO_DOCS; if (cause[0] == 'O' && cause[1] == 'P' && cause[2] == 'N') return SANE_STATUS_COVER_OPEN; return SANE_STATUS_IO_ERROR; } if (len == 4 && strncmp("atnCAN ", token, 3 + 4) == 0) { DBG(1, "%s: cancel request\n", __func__); s->canceling = 1; s->scanning = 0; return SANE_STATUS_CANCELLED; } if (len == 4 && strncmp("lftd000", token, 3 + 4) == 0) { DBG(1, "%s:lft ok\n", __func__); s->scanEnd = 1; s->scanning = 0; } return SANE_STATUS_GOOD; } SANE_Status esci2_img(struct epsonds_scanner *s, SANE_Int *length) { SANE_Status status = SANE_STATUS_GOOD; SANE_Status parse_status; unsigned int more; ssize_t read; DBG(15, "esci2_img start\n"); *length = 0; if (s->canceling) return SANE_STATUS_CANCELLED; /* request image data */ eds_send(s, "IMG x0000000", 12, &status, 64); if (status != SANE_STATUS_GOOD) { return status; } DBG(15, "request img OK\n"); /* receive DataHeaderBlock */ memset(s->buf, 0x00, 64); eds_recv(s, s->buf, 64, &status); if (status != SANE_STATUS_GOOD) { return status; } DBG(15, "receive img OK\n"); /* check if we need to read any image data */ more = 0; if (!esci2_check_header("IMG ", (char *)s->buf, &more)) { return SANE_STATUS_IO_ERROR; } /* this handles eof and errors */ parse_status = esci2_parse_block((char *)s->buf + 12, 64 - 12, s, &img_cb); if (s->backside) { s->width_back = s->width_temp; s->height_back = s->height_temp; }else{ s->width_front = s->width_temp; s->height_front = s->height_temp; } /* no more data? return using the status of the esci2_parse_block * call, which might hold other error conditions. */ if (!more) { return parse_status; } /* more data than was accounted for in s->buf */ if (more > s->bsz) { return SANE_STATUS_IO_ERROR; } /* ALWAYS read image data */ if (s->hw->connection == SANE_EPSONDS_NET) { epsonds_net_request_read(s, more); } read = eds_recv(s, s->buf, more, &status); if (status != SANE_STATUS_GOOD) { return status; } if (read != more) { return SANE_STATUS_IO_ERROR; } /* handle esci2_parse_block errors */ if (parse_status != SANE_STATUS_GOOD) { return parse_status; } DBG(15, "%s: read %lu bytes, status: %d\n", __func__, (unsigned long) read, status); *length = read; if (s->canceling) { return SANE_STATUS_CANCELLED; } return SANE_STATUS_GOOD; } backends-1.3.0/backend/epsonds-cmd.h000066400000000000000000000016571456256263500172700ustar00rootroot00000000000000/* * epsonds-cmd.h - Epson ESC/I-2 routines. * * Copyright (C) 2015 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ #ifndef epsonds_cmd_h #define epsonds_cmd_h SANE_Status esci2_info(epsonds_scanner *s); SANE_Status esci2_fin(epsonds_scanner *s); SANE_Status esci2_can(epsonds_scanner *s); SANE_Status esci2_capa(epsonds_scanner *s); SANE_Status esci2_resa(epsonds_scanner *s); SANE_Status esci2_stat(epsonds_scanner *s); SANE_Status esci2_para(epsonds_scanner *s, char *parameters, int len); SANE_Status esci2_mech(epsonds_scanner *s, char *parameters); SANE_Status esci2_trdt(epsonds_scanner *s); SANE_Status esci2_img(struct epsonds_scanner *s, SANE_Int *length) ; #endif backends-1.3.0/backend/epsonds-io.c000066400000000000000000000100221456256263500171110ustar00rootroot00000000000000/* * epsonds-io.c - Epson ESC/I-2 driver, low level I/O. * * Copyright (C) 2015 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ #define DEBUG_DECLARE_ONLY #include "sane/config.h" #include #include /* sleep */ #ifdef HAVE_SYS_TYPES_H #include #endif #include "epsonds.h" #include "epsonds-io.h" #include "epsonds-net.h" #ifdef HAVE_SYS_TYPES_H #include #endif size_t eds_send(epsonds_scanner *s, void *buf, size_t length, SANE_Status *status, size_t reply_len) { DBG(32, "%s: size = %lu\n", __func__, (u_long) length); if (length == 2) { char *cmd = buf; switch (cmd[0]) { case FS: DBG(9, "%s: FS %c\n", __func__, cmd[1]); break; } } if (s->hw->connection == SANE_EPSONDS_NET) { return epsonds_net_write(s, 0x2000, buf, length, reply_len, status); } else if (s->hw->connection == SANE_EPSONDS_USB) { size_t n = length; *status = sanei_usb_write_bulk(s->fd, buf, &n); return n; } /* never reached */ *status = SANE_STATUS_INVAL; return 0; } size_t eds_recv(epsonds_scanner *s, void *buf, size_t length, SANE_Status *status) { size_t n = length; /* network interface needs to read header back even data is 0.*/ DBG(30, "%s: size = %ld, buf = %p\n", __func__, (long) length, buf); *status = SANE_STATUS_GOOD; if (s->hw->connection == SANE_EPSONDS_NET) { n = epsonds_net_read(s, buf, length, status); } else if (s->hw->connection == SANE_EPSONDS_USB) { /* !!! only report an error if we don't read anything */ if (n) { *status = sanei_usb_read_bulk(s->fd, (SANE_Byte *)buf, (size_t *) &n); if (n > 0) *status = SANE_STATUS_GOOD; } } if (n < length) { DBG(1, "%s: expected = %lu, got = %ld, canceling: %d\n", __func__, (u_long)length, (long)n, s->canceling); *status = SANE_STATUS_IO_ERROR; } return n; } /* Simple function to exchange a fixed amount of data with the scanner */ SANE_Status eds_txrx(epsonds_scanner* s, char *txbuf, size_t txlen, char *rxbuf, size_t rxlen) { SANE_Status status; size_t done; done = eds_send(s, txbuf, txlen, &status, rxlen); if (status != SANE_STATUS_GOOD) { DBG(1, "%s: tx err, %s\n", __func__, sane_strstatus(status)); return status; } if (done != txlen) { DBG(1, "%s: tx err, short write\n", __func__); return SANE_STATUS_IO_ERROR; } done = eds_recv(s, rxbuf, rxlen, &status); if (status != SANE_STATUS_GOOD) { DBG(1, "%s: rx err, %s\n", __func__, sane_strstatus(status)); } return status; } /* This function should be used to send codes that only requires the scanner * to give back an ACK or a NAK, namely FS X or FS Y */ SANE_Status eds_control(epsonds_scanner *s, void *buf, size_t buf_size) { char result; SANE_Status status; DBG(12, "%s: size = %lu\n", __func__, (u_long) buf_size); status = eds_txrx(s, buf, buf_size, &result, 1); if (status != SANE_STATUS_GOOD) { DBG(1, "%s: failed, %s\n", __func__, sane_strstatus(status)); return status; } if (result == ACK) return SANE_STATUS_GOOD; if (result == NAK) { DBG(3, "%s: NAK\n", __func__); return SANE_STATUS_INVAL; } DBG(1, "%s: result is neither ACK nor NAK but 0x%02x\n", __func__, result); return SANE_STATUS_INVAL; } SANE_Status eds_fsy(epsonds_scanner *s) { return eds_control(s, "\x1CY", 2); } SANE_Status eds_fsx(epsonds_scanner *s) { // SANE_Status status = eds_control(s, "\x1CZ", 2); SANE_Status status = eds_control(s, "\x1CX", 2); if (status == SANE_STATUS_GOOD) { s->locked = 1; } return status; } SANE_Status eds_lock(epsonds_scanner *s) { SANE_Status status; DBG(5, "%s\n", __func__); if (s->hw->connection == SANE_EPSONDS_USB) { sanei_usb_set_timeout(USB_SHORT_TIMEOUT); } status = eds_fsx(s); if (s->hw->connection == SANE_EPSONDS_USB) { sanei_usb_set_timeout(USB_TIMEOUT); } return status; } backends-1.3.0/backend/epsonds-io.h000066400000000000000000000017441456256263500171310ustar00rootroot00000000000000/* * epsonds-io.h - Epson ESC/I-2 driver, low level I/O. * * Copyright (C) 2015 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ #ifndef epsonds_io_h #define epsonds_io_h #define USB_TIMEOUT (6 * 1000) #define USB_SHORT_TIMEOUT (1 * 800) size_t eds_send(epsonds_scanner *s, void *buf, size_t length, SANE_Status *status, size_t reply_len); size_t eds_recv(epsonds_scanner *s, void *buf, size_t length, SANE_Status *status); SANE_Status eds_txrx(epsonds_scanner *s, char *txbuf, size_t txlen, char *rxbuf, size_t rxlen); SANE_Status eds_control(epsonds_scanner *s, void *buf, size_t buf_size); SANE_Status eds_fsy(epsonds_scanner *s); SANE_Status eds_fsx(epsonds_scanner *s); SANE_Status eds_lock(epsonds_scanner *s); #endif backends-1.3.0/backend/epsonds-jpeg.c000066400000000000000000000127701456256263500174430ustar00rootroot00000000000000/* * epsonds-jpeg.c - Epson ESC/I-2 driver, JPEG support. * * Copyright (C) 2015 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ #define DEBUG_DECLARE_ONLY #include "sane/config.h" #include #include "epsonds.h" #include "epsonds-jpeg.h" #include "epsonds-ops.h" #include struct my_error_mgr { struct jpeg_error_mgr pub; jmp_buf setjmp_buffer; }; typedef struct my_error_mgr * my_error_ptr; METHODDEF(void) my_error_exit (j_common_ptr cinfo) { char buffer[JMSG_LENGTH_MAX]; (*cinfo->err->format_message) (cinfo, buffer); DBG(10,"Jpeg decode error [%s]", buffer); } LOCAL(struct jpeg_error_mgr *) jpeg_custom_error (struct my_error_mgr * err) { struct jpeg_error_mgr* pRet = jpeg_std_error(&(err->pub)); err->pub.error_exit = my_error_exit; return pRet; } typedef struct { struct jpeg_source_mgr pub; JOCTET *buffer; int length; } epsonds_src_mgr; METHODDEF(void) jpeg_init_source(j_decompress_ptr __sane_unused__ cinfo) { } METHODDEF(void) jpeg_term_source(j_decompress_ptr __sane_unused__ cinfo) { } METHODDEF(boolean) jpeg_fill_input_buffer(j_decompress_ptr cinfo) { epsonds_src_mgr *src = (epsonds_src_mgr *)cinfo->src; /* read from scanner if no data? */ src->pub.next_input_byte = src->buffer; src->pub.bytes_in_buffer = src->length; DBG(18, "reading from ring buffer, %d left\n", src->length); return TRUE; } METHODDEF (void) jpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes) { epsonds_src_mgr *src = (epsonds_src_mgr *)cinfo->src; if (num_bytes > 0) { while (num_bytes > (long)src->pub.bytes_in_buffer) { num_bytes -= (long)src->pub.bytes_in_buffer; jpeg_fill_input_buffer(cinfo); } src->pub.next_input_byte += (size_t) num_bytes; src->pub.bytes_in_buffer -= (size_t) num_bytes; } } void eds_decode_jpeg(epsonds_scanner*s, SANE_Byte *data, SANE_Int size, ring_buffer* ringBuffer, SANE_Int isBackSide, SANE_Int needToConvertBW) { struct jpeg_decompress_struct jpeg_cinfo; struct my_error_mgr jpeg_err; { epsonds_src_mgr *src; jpeg_cinfo.err = jpeg_custom_error(&jpeg_err); jpeg_create_decompress(&jpeg_cinfo); jpeg_cinfo.src = (struct jpeg_source_mgr *)(*jpeg_cinfo.mem->alloc_small)((j_common_ptr)&jpeg_cinfo, JPOOL_PERMANENT, sizeof(epsonds_src_mgr)); memset(jpeg_cinfo.src, 0x00, sizeof(epsonds_src_mgr)); ; src = (epsonds_src_mgr *)jpeg_cinfo.src; src->pub.init_source = jpeg_init_source; src->pub.fill_input_buffer = jpeg_fill_input_buffer; src->pub.skip_input_data = jpeg_skip_input_data; src->pub.resync_to_restart = jpeg_resync_to_restart; src->pub.term_source = jpeg_term_source; src->pub.bytes_in_buffer = 0; src->pub.next_input_byte = NULL; src->buffer = (JOCTET*)data; src->length = size; } { if (jpeg_read_header(&jpeg_cinfo, TRUE)) { if (jpeg_start_decompress(&jpeg_cinfo)) { DBG(10,"%s: w: %d, h: %d, components: %d\n", __func__, jpeg_cinfo.output_width, jpeg_cinfo.output_height, jpeg_cinfo.output_components); } } } { int sum = 0; int bufSize = jpeg_cinfo.output_width * jpeg_cinfo.output_components; int monoBufSize = (jpeg_cinfo.output_width + 7)/8; JSAMPARRAY scanlines = (jpeg_cinfo.mem->alloc_sarray)((j_common_ptr)&jpeg_cinfo, JPOOL_IMAGE, bufSize, 1); while (jpeg_cinfo.output_scanline < jpeg_cinfo.output_height) { int l = jpeg_read_scanlines(&jpeg_cinfo, scanlines, 1); if (l == 0) { break; } sum += l; if (needToConvertBW) { SANE_Byte* bytes = scanlines[0]; SANE_Int imgPos = 0; for (int i = 0; i < monoBufSize; i++) { SANE_Byte outByte = 0; for(SANE_Int bitIndex = 0; bitIndex < 8 && imgPos < bufSize; bitIndex++) { //DBG(10,"bytes[imgPos] = %d\n", bytes[imgPos]); if(bytes[imgPos] >= 110) { SANE_Byte bit = 7 - (bitIndex % 8); outByte |= (1<< bit); } imgPos += 1; } //DBG(10,"outByte = %d\n", outByte); eds_ring_write(ringBuffer, &outByte, 1); } } else { eds_ring_write(ringBuffer, scanlines[0], bufSize); } // decode until valida data if (isBackSide) { if (sum >= s->height_back) { break; } }else { if (sum >= s->height_front) { break; } } } DBG(10,"decodded lines = %d\n", sum); // abandon unncessary data if ((JDIMENSION)sum < jpeg_cinfo.output_height) { // unncessary data while(1) { int l = jpeg_read_scanlines(&jpeg_cinfo, scanlines, 1); if (l == 0) { break; } } } // if not auto crop mode padding to lines if (s->val[OPT_ADF_CRP].w == 0) { unsigned char* padding = malloc(s->params.bytes_per_line); memset(padding, 255, s->params.bytes_per_line); DBG(10,"padding data lines = %d to %d pa \n", sum, s->params.lines); while(sum < s->params.lines) { eds_ring_write(ringBuffer, padding, bufSize); sum++; } free(padding); padding = NULL; } } { jpeg_finish_decompress(&jpeg_cinfo); jpeg_destroy_decompress(&jpeg_cinfo); } return; } backends-1.3.0/backend/epsonds-jpeg.h000066400000000000000000000010261456256263500174400ustar00rootroot00000000000000/* * epsonds.c - Epson ESC/I-2 driver, JPEG support. * * Copyright (C) 2015 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ void eds_decode_jpeg(epsonds_scanner*s, SANE_Byte *data, SANE_Int size, ring_buffer* ringBuffer, SANE_Int isBackSide, SANE_Int needToConvertBW); backends-1.3.0/backend/epsonds-net.c000066400000000000000000000303021456256263500172730ustar00rootroot00000000000000/* * epsonds-net.c - SANE library for Epson scanners. * * Copyright (C) 2006-2016 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ #define DEBUG_DECLARE_ONLY #include "sane/config.h" #ifdef HAVE_SYS_SELECT_H #include #endif #include "sane/sane.h" #include "sane/saneopts.h" #include "sane/sanei_tcp.h" #include "sane/sanei_config.h" #include "sane/sanei_backend.h" #include "epsonds.h" #include "epsonds-net.h" #include "byteorder.h" #include "sane/sanei_debug.h" static ssize_t epsonds_net_read_raw(epsonds_scanner *s, unsigned char *buf, ssize_t wanted, SANE_Status *status) { DBG(15, "%s: wanted: %ld\n", __func__, wanted); if (wanted == 0) { *status = SANE_STATUS_GOOD; return 0; } int ready; ssize_t read = -1; fd_set readable; struct timeval tv; tv.tv_sec = 10; tv.tv_usec = 0; FD_ZERO(&readable); FD_SET(s->fd, &readable); ready = select(s->fd + 1, &readable, NULL, NULL, &tv); if (ready > 0) { read = sanei_tcp_read(s->fd, buf, wanted); } else { DBG(15, "%s: select failed: %d\n", __func__, ready); } *status = SANE_STATUS_GOOD; if (read < wanted) { *status = SANE_STATUS_IO_ERROR; } return read; } static ssize_t epsonds_net_read_buf(epsonds_scanner *s, unsigned char *buf, ssize_t wanted, SANE_Status * status) { ssize_t read = 0; DBG(23, "%s: reading up to %lu from buffer at %p, %lu available\n", __func__, (u_long) wanted, (void *) s->netptr, (u_long) s->netlen); if ((size_t) wanted > s->netlen) { *status = SANE_STATUS_IO_ERROR; wanted = s->netlen; } memcpy(buf, s->netptr, wanted); read = wanted; s->netptr += read; s->netlen -= read; if (s->netlen == 0) { DBG(23, "%s: freeing %p\n", __func__, (void *) s->netbuf); free(s->netbuf); s->netbuf = s->netptr = NULL; s->netlen = 0; } return read; } ssize_t epsonds_net_read(epsonds_scanner *s, unsigned char *buf, ssize_t wanted, SANE_Status * status) { if (wanted < 0) { *status = SANE_STATUS_INVAL; return 0; } size_t size; ssize_t read = 0; unsigned char header[12]; /* read from remainder of buffer */ if (s->netptr) { return epsonds_net_read_buf(s, buf, wanted, status); } /* receive net header */ read = epsonds_net_read_raw(s, header, 12, status); if (read != 12) { return 0; } /* validate header */ if (header[0] != 'I' || header[1] != 'S') { DBG(1, "header mismatch: %02X %02x\n", header[0], header[1]); *status = SANE_STATUS_IO_ERROR; return 0; } /* parse payload size */ size = be32atoh(&header[6]); *status = SANE_STATUS_GOOD; if (!s->netbuf) { DBG(15, "%s: direct read\n", __func__); DBG(23, "%s: wanted = %lu, available = %lu\n", __func__, (u_long) wanted, (u_long) size); if ((size_t) wanted > size) { wanted = size; } read = epsonds_net_read_raw(s, buf, wanted, status); } else { DBG(15, "%s: buffered read\n", __func__); DBG(23, "%s: bufferable = %lu, available = %lu\n", __func__, (u_long) s->netlen, (u_long) size); if (s->netlen > size) { s->netlen = size; } /* fill buffer */ read = epsonds_net_read_raw(s, s->netbuf, s->netlen, status); s->netptr = s->netbuf; s->netlen = (read > 0 ? read : 0); /* copy wanted part */ read = epsonds_net_read_buf(s, buf, wanted, status); } return read; } SANE_Status epsonds_net_request_read(epsonds_scanner *s, size_t len) { SANE_Status status; epsonds_net_write(s, 0x2000, NULL, 0, len, &status); return status; } size_t epsonds_net_write(epsonds_scanner *s, unsigned int cmd, const unsigned char *buf, size_t buf_size, size_t reply_len, SANE_Status *status) { unsigned char *h1, *h2; unsigned char *packet = malloc(12 + 8); if (!packet) { *status = SANE_STATUS_NO_MEM; return 0; } h1 = packet; // packet header h2 = packet + 12; // data header if (reply_len) { if (s->netbuf) { DBG(23, "%s, freeing %p, %ld bytes unprocessed\n", __func__, (void *) s->netbuf, (u_long) s->netlen); free(s->netbuf); s->netbuf = s->netptr = NULL; s->netlen = 0; } s->netbuf = malloc(reply_len); if (!s->netbuf) { free(packet); *status = SANE_STATUS_NO_MEM; return 0; } s->netlen = reply_len; DBG(24, "%s: allocated %lu bytes at %p\n", __func__, (u_long) s->netlen, (void *) s->netbuf); } DBG(24, "%s: cmd = %04x, buf = %p, buf_size = %lu, reply_len = %lu\n", __func__, cmd, (void *) buf, (u_long) buf_size, (u_long) reply_len); memset(h1, 0x00, 12); memset(h2, 0x00, 8); h1[0] = 'I'; h1[1] = 'S'; h1[2] = cmd >> 8; // packet type h1[3] = cmd; // data type h1[4] = 0x00; h1[5] = 0x0C; // data offset DBG(24, "H1[0]: %02x %02x %02x %02x\n", h1[0], h1[1], h1[2], h1[3]); // 0x20 passthru // 0x21 job control if (buf_size) { htobe32a(&h1[6], buf_size); } if((cmd >> 8) == 0x20) { htobe32a(&h1[6], buf_size + 8); // data size (data header + payload) htobe32a(&h2[0], buf_size); // payload size htobe32a(&h2[4], reply_len); // expected answer size DBG(24, "H1[6]: %02x %02x %02x %02x (%lu)\n", h1[6], h1[7], h1[8], h1[9], (u_long) (buf_size + 8)); DBG(24, "H2[0]: %02x %02x %02x %02x (%lu)\n", h2[0], h2[1], h2[2], h2[3], (u_long) buf_size); DBG(24, "H2[4]: %02x %02x %02x %02x (%lu)\n", h2[4], h2[5], h2[6], h2[7], (u_long) reply_len); } if ((cmd >> 8) == 0x20 && (buf_size || reply_len)) { // send header + data header sanei_tcp_write(s->fd, packet, 12 + 8); } else { sanei_tcp_write(s->fd, packet, 12); } // send payload if (buf_size) sanei_tcp_write(s->fd, buf, buf_size); free(packet); *status = SANE_STATUS_GOOD; return buf_size; } SANE_Status epsonds_net_lock(struct epsonds_scanner *s) { SANE_Status status; unsigned char buf[7] = "\x01\xa0\x04\x00\x00\x01\x2c"; DBG(1, "%s\n", __func__); epsonds_net_write(s, 0x2100, buf, 7, 0, &status); epsonds_net_read(s, buf, 1, &status); // buf[0] should be ACK, 0x06 return status; } SANE_Status epsonds_net_unlock(struct epsonds_scanner *s) { SANE_Status status; DBG(1, "%s\n", __func__); epsonds_net_write(s, 0x2101, NULL, 0, 0, &status); /* epsonds_net_read(s, buf, 1, &status); */ return status; } #if WITH_AVAHI #include #include #include #include #include #include #include #include #include static AvahiSimplePoll *simple_poll = NULL; static struct timeval borowseEndTime; static int resolvedCount = 0; static int browsedCount = 0; static int waitResolver = 0; typedef struct { AvahiClient* client; Device_Found_CallBack callBack; }EDSAvahiUserData; static int my_avahi_simple_poll_loop(AvahiSimplePoll *s) { struct timeval currentTime; for (;;) { int r = avahi_simple_poll_iterate(s, 1); if (r != 0) { if (r >= 0 || errno != EINTR) { DBG(10, "my_avahi_simple_poll_loop end\n"); return r; } } if (waitResolver) { gettimeofday(¤tTime, NULL); if ((currentTime.tv_sec - borowseEndTime.tv_sec) >= 3) { avahi_simple_poll_quit(simple_poll); DBG(10, "resolve timeout\n"); return 0; } } } } static void epsonds_resolve_callback(AvahiServiceResolver *r, AVAHI_GCC_UNUSED AvahiIfIndex interface, AVAHI_GCC_UNUSED AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void *userdata) { // unused parameter (void)r; (void)type; (void)domain; (void)host_name; (void)port; (void)flags; EDSAvahiUserData* data = userdata; char ipAddr[AVAHI_ADDRESS_STR_MAX]; DBG(10, "epsonds_searchDevices resolve_callback\n"); resolvedCount++; switch (event) { case AVAHI_RESOLVER_FAILURE: break; case AVAHI_RESOLVER_FOUND: avahi_address_snprint(ipAddr, sizeof(ipAddr), address); DBG(10, "epsonds_searchDevices name = %s \n", name); if (strlen(name) > 7) { if (strncmp(name, "EPSON", 5) == 0) { while(txt != NULL) { char* text = (char*)avahi_string_list_get_text(txt); DBG(10, "avahi string = %s\n", text); if (strlen(text) > 4 && strncmp(text, "mdl=", 4) == 0) { if (data->callBack) { data->callBack(&text[4], ipAddr); break; } } txt = avahi_string_list_get_next(txt); } } } break; } } static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags flags, void* userdata) { DBG(10, "browse_callback event = %d\n", event); //unused parameter (void)b; (void)flags; EDSAvahiUserData *data = userdata; switch (event) { case AVAHI_BROWSER_FAILURE: avahi_simple_poll_quit(simple_poll); return; case AVAHI_BROWSER_NEW: DBG(10, "browse_callback name = %s\n", name); browsedCount++; if (!(avahi_service_resolver_new(data->client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, epsonds_resolve_callback, data))) { DBG(10, "avahi_service_resolver_new fails\n"); break; } case AVAHI_BROWSER_REMOVE: break; case AVAHI_BROWSER_ALL_FOR_NOW: DBG(10, "AVAHI_BROWSER_ALL_FOR_NOW\n"); gettimeofday(&borowseEndTime, NULL); if (browsedCount > resolvedCount) { DBG(10, "WAIT RESOLVER\n"); waitResolver = 1; }else{ DBG(10, "QUIT POLL\n"); avahi_simple_poll_quit(simple_poll); } break; case AVAHI_BROWSER_CACHE_EXHAUSTED: DBG(10, "AVAHI_BROWSER_CACHE_EXHAUSTED\n"); break; } } static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void *userdata) { assert(c); if (state == AVAHI_CLIENT_FAILURE) avahi_simple_poll_quit(simple_poll); } SANE_Status epsonds_searchDevices(Device_Found_CallBack deviceFoundCallBack) { int result = SANE_STATUS_GOOD; AvahiClient *client = NULL; AvahiServiceBrowser *sb = NULL; EDSAvahiUserData data; resolvedCount = 0; browsedCount = 0; waitResolver = 0; int error = 0; DBG(10, "epsonds_searchDevices\n"); if (!(simple_poll = avahi_simple_poll_new())) { DBG(10, "avahi_simple_poll_new failed\n"); result = SANE_STATUS_INVAL; goto fail; } client = avahi_client_new(avahi_simple_poll_get(simple_poll), 0, client_callback, NULL, &error); if (!client) { DBG(10, "avahi_client_new failed %s\n", avahi_strerror(error)); result = SANE_STATUS_INVAL; goto fail; } data.client = client; data.callBack = deviceFoundCallBack; if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_scanner._tcp", NULL, 0, browse_callback, &data))) { DBG(10, "avahi_service_browser_new failed: %s\n", avahi_strerror(avahi_client_errno(client))); result = SANE_STATUS_INVAL; goto fail; } my_avahi_simple_poll_loop(simple_poll); fail: if (sb) avahi_service_browser_free(sb); if (client) avahi_client_free(client); if (simple_poll) avahi_simple_poll_free(simple_poll); DBG(10, "epsonds_searchDevices fin\n"); return result; } #endif backends-1.3.0/backend/epsonds-net.h000066400000000000000000000014341456256263500173040ustar00rootroot00000000000000#ifndef _EPSONDS_NET_H_ #define _EPSONDS_NET_H_ #include #include "../include/sane/sane.h" typedef void (*Device_Found_CallBack) (const char* name, const char* ip); extern ssize_t epsonds_net_read(struct epsonds_scanner *s, unsigned char *buf, ssize_t buf_size, SANE_Status *status); extern size_t epsonds_net_write(struct epsonds_scanner *s, unsigned int cmd, const unsigned char *buf, size_t buf_size, size_t reply_len, SANE_Status *status); extern SANE_Status epsonds_net_lock(struct epsonds_scanner *s); extern SANE_Status epsonds_net_unlock(struct epsonds_scanner *s); extern SANE_Status epsonds_net_request_read(epsonds_scanner *s, size_t len); #if WITH_AVAHI extern SANE_Status epsonds_searchDevices(Device_Found_CallBack deviceFoundCallBack); #endif #endif backends-1.3.0/backend/epsonds-ops.c000066400000000000000000000260171456256263500173160ustar00rootroot00000000000000/* * epsonds-ops.c - Epson ESC/I-2 driver, support routines. * * Copyright (C) 2015 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ #define DEBUG_DECLARE_ONLY #include "sane/config.h" #include /* sleep */ #ifdef HAVE_SYS_SELECT_H #include #endif #include "epsonds.h" #include "epsonds-io.h" #include "epsonds-ops.h" #include "epsonds-cmd.h" extern struct mode_param mode_params[]; /* Define the different scan sources */ #define STRING_FLATBED SANE_I18N("Flatbed") #define STRING_ADFFRONT SANE_I18N("ADF Front") #define STRING_ADFDUPLEX SANE_I18N("ADF Duplex") extern SANE_String_Const source_list[]; void eds_dev_init(epsonds_device *dev) { dev->res_list = malloc(sizeof(SANE_Word)); dev->res_list[0] = 0; dev->depth_list = malloc(sizeof(SANE_Word)); dev->depth_list[0] = 0; } SANE_Status eds_dev_post_init(struct epsonds_device *dev) { SANE_String_Const *source_list_add = source_list; DBG(10, "%s\n", __func__); if (dev->has_fb) *source_list_add++ = STRING_FLATBED; if (dev->has_adf) *source_list_add++ = STRING_ADFFRONT; if (dev->adf_is_duplex) *source_list_add++ = STRING_ADFDUPLEX; if (source_list[0] == 0 || (dev->res_list[0] == 0 && dev->dpi_range.min == 0) || dev->depth_list[0] == 0) { DBG(1, "something is wrong in the discovery process, aborting.\n"); DBG(1, "sources: %ld, res: %d, depths: %d.\n", source_list_add - source_list, dev->res_list[0], dev->depth_list[0]); return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } SANE_Bool eds_is_model(epsonds_device *dev, const char *model) { if (dev->model == NULL) return SANE_FALSE; if (strncmp(dev->model, model, strlen(model)) == 0) return SANE_TRUE; return SANE_FALSE; } SANE_Status eds_add_resolution(epsonds_device *dev, int r) { DBG(10, "%s: add (dpi): %d\n", __func__, r); /* first element is the list size */ dev->res_list[0]++; dev->res_list = realloc(dev->res_list, (dev->res_list[0] + 1) * sizeof(SANE_Word)); if (dev->res_list == NULL) return SANE_STATUS_NO_MEM; dev->res_list[dev->res_list[0]] = r; return SANE_STATUS_GOOD; } SANE_Status eds_set_resolution_range(epsonds_device *dev, int min, int max) { DBG(10, "%s: set min/max (dpi): %d/%d\n", __func__, min, max); dev->dpi_range.min = min; dev->dpi_range.max = max; dev->dpi_range.quant = 1; return SANE_STATUS_GOOD; } void eds_set_fbf_area(epsonds_device *dev, int x, int y, int unit) { if (x == 0 || y == 0) return; dev->fbf_x_range.min = 0; dev->fbf_x_range.max = SANE_FIX(x * MM_PER_INCH / unit); dev->fbf_x_range.quant = 0; dev->fbf_y_range.min = 0; dev->fbf_y_range.max = SANE_FIX(y * MM_PER_INCH / unit); dev->fbf_y_range.quant = 0; DBG(5, "%s: %f,%f %f,%f %d [mm]\n", __func__, SANE_UNFIX(dev->fbf_x_range.min), SANE_UNFIX(dev->fbf_y_range.min), SANE_UNFIX(dev->fbf_x_range.max), SANE_UNFIX(dev->fbf_y_range.max), unit); } void eds_set_adf_area(struct epsonds_device *dev, int x, int y, int unit) { dev->adf_x_range.min = 0; dev->adf_x_range.max = SANE_FIX(x * MM_PER_INCH / unit); dev->adf_x_range.quant = 0; dev->adf_y_range.min = 0; dev->adf_y_range.max = SANE_FIX(y * MM_PER_INCH / unit); dev->adf_y_range.quant = 0; DBG(5, "%s: %f,%f %f,%f %d [mm]\n", __func__, SANE_UNFIX(dev->adf_x_range.min), SANE_UNFIX(dev->adf_y_range.min), SANE_UNFIX(dev->adf_x_range.max), SANE_UNFIX(dev->adf_y_range.max), unit); } void eds_set_tpu_area(struct epsonds_device *dev, int x, int y, int unit) { dev->tpu_x_range.min = 0; dev->tpu_x_range.max = SANE_FIX(x * MM_PER_INCH / unit); dev->tpu_x_range.quant = 0; dev->tpu_y_range.min = 0; dev->tpu_y_range.max = SANE_FIX(y * MM_PER_INCH / unit); dev->tpu_y_range.quant = 0; DBG(5, "%s: %f,%f %f,%f %d [mm]\n", __func__, SANE_UNFIX(dev->tpu_x_range.min), SANE_UNFIX(dev->tpu_y_range.min), SANE_UNFIX(dev->tpu_x_range.max), SANE_UNFIX(dev->tpu_y_range.max), unit); } SANE_Status eds_add_depth(epsonds_device *dev, SANE_Word depth) { DBG(5, "%s: add (bpp): %d\n", __func__, depth); /* > 8bpp not implemented yet */ if (depth > 8) { DBG(1, " not supported"); return SANE_STATUS_GOOD; } if (depth > dev->max_depth) dev->max_depth = depth; /* first element is the list size */ dev->depth_list[0]++; dev->depth_list = realloc(dev->depth_list, (dev->depth_list[0] + 1) * sizeof(SANE_Word)); if (dev->depth_list == NULL) return SANE_STATUS_NO_MEM; dev->depth_list[dev->depth_list[0]] = depth; return SANE_STATUS_GOOD; } SANE_Status eds_init_parameters(epsonds_scanner *s) { int dpi, bytes_per_pixel; memset(&s->params, 0, sizeof(SANE_Parameters)); /* setup depth according to our mode table */ if (mode_params[s->val[OPT_MODE].w].depth == 1) s->params.depth = 1; else s->params.depth = s->val[OPT_DEPTH].w; dpi = s->val[OPT_RESOLUTION].w; if (SANE_UNFIX(s->val[OPT_BR_Y].w) == 0 || SANE_UNFIX(s->val[OPT_BR_X].w) == 0) return SANE_STATUS_INVAL; s->left = ((SANE_UNFIX(s->val[OPT_TL_X].w) / MM_PER_INCH) * s->val[OPT_RESOLUTION].w) + 0.5; s->top = ((SANE_UNFIX(s->val[OPT_TL_Y].w) / MM_PER_INCH) * s->val[OPT_RESOLUTION].w) + 0.5; s->params.pixels_per_line = ((SANE_UNFIX(s->val[OPT_BR_X].w - s->val[OPT_TL_X].w) / MM_PER_INCH) * dpi) + 0.5; s->params.lines = ((SANE_UNFIX(s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w) / MM_PER_INCH) * dpi) + 0.5; DBG(5, "%s: tlx %f tly %f brx %f bry %f [mm]\n", __func__, SANE_UNFIX(s->val[OPT_TL_X].w), SANE_UNFIX(s->val[OPT_TL_Y].w), SANE_UNFIX(s->val[OPT_BR_X].w), SANE_UNFIX(s->val[OPT_BR_Y].w)); DBG(5, "%s: tlx %d tly %d brx %d bry %d [dots @ %d dpi]\n", __func__, s->left, s->top, s->params.pixels_per_line, s->params.lines, dpi); /* center aligned? */ if (s->hw->alignment == 1) { SANE_Int offset = ((SANE_UNFIX(s->hw->x_range->max) / MM_PER_INCH) * dpi) + 0.5; s->left += ((offset - s->params.pixels_per_line) / 2); DBG(5, "%s: centered to tlx %d tly %d brx %d bry %d [dots @ %d dpi]\n", __func__, s->left, s->top, s->params.pixels_per_line, s->params.lines, dpi); } /* * Calculate bytes_per_pixel and bytes_per_line for * any color depths. * * The default color depth is stored in mode_params.depth: */ /* this works because it can only be set to 1, 8 or 16 */ bytes_per_pixel = s->params.depth / 8; if (s->params.depth % 8) { /* just in case ... */ bytes_per_pixel++; } /* pixels_per_line is rounded to the next 8bit boundary */ s->params.pixels_per_line = s->params.pixels_per_line & ~7; s->params.last_frame = SANE_TRUE; switch (s->val[OPT_MODE].w) { case MODE_BINARY: case MODE_GRAY: s->params.format = SANE_FRAME_GRAY; s->params.bytes_per_line = s->params.pixels_per_line * s->params.depth / 8; break; case MODE_COLOR: s->params.format = SANE_FRAME_RGB; s->params.bytes_per_line = 3 * s->params.pixels_per_line * bytes_per_pixel; break; } if (s->params.bytes_per_line == 0) { DBG(1, "bytes_per_line is ZERO\n"); return SANE_STATUS_INVAL; } /* * If (s->top + s->params.lines) is larger than the max scan area, reset * the number of scan lines: * XXX: precalculate the maximum scanning area elsewhere (use dev max_y) */ if (SANE_UNFIX(s->val[OPT_BR_Y].w) / MM_PER_INCH * dpi < (s->params.lines + s->top)) { s->params.lines = ((int) SANE_UNFIX(s->val[OPT_BR_Y].w) / MM_PER_INCH * dpi + 0.5) - s->top; } if (s->params.lines <= 0) { DBG(1, "wrong number of lines: %d\n", s->params.lines); return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } #define min(A,B) (((A)<(B)) ? (A) : (B)) void eds_copy_image_from_ring(epsonds_scanner *s, SANE_Byte *data, SANE_Int max_length, SANE_Int *length) { int lines, available; int hw_line_size = (s->params.bytes_per_line + s->dummy); available = eds_ring_avail(s->current); if (max_length > available) max_length = available; lines = min(max_length / s->params.bytes_per_line, available / hw_line_size); DBG(18, "copying %d lines (%d, %d, %d)\n", lines, s->params.bytes_per_line, s->dummy, s->params.depth); /* need more data? */ if (lines == 0) { *length = 0; return; } *length = (lines * s->params.bytes_per_line); /* we need to copy one line at time, skipping * dummy bytes at the end of each line */ /* lineart */ if (s->params.depth == 1) { while (lines--) { int i; SANE_Byte *p; eds_ring_read(s->current, s->line_buffer, s->params.bytes_per_line); eds_ring_skip(s->current, s->dummy); p = s->line_buffer; for (i = 0; i < s->params.bytes_per_line; i++) { *data++ = ~*p++; } } } else { /* gray and color */ while (lines--) { eds_ring_read(s->current, data, s->params.bytes_per_line); eds_ring_skip(s->current, s->dummy); data += s->params.bytes_per_line; } } } SANE_Status eds_ring_init(ring_buffer *ring, SANE_Int size) { ring->ring = realloc(ring->ring, size); if (!ring->ring) { return SANE_STATUS_NO_MEM; } ring->size = size; ring->fill = 0; ring->end = ring->ring + size; ring->wp = ring->rp = ring->ring; return SANE_STATUS_GOOD; } SANE_Status eds_ring_write(ring_buffer *ring, SANE_Byte *buf, SANE_Int size) { SANE_Int tail; if (size > (ring->size - ring->fill)) { DBG(1, "ring buffer full, requested: %d, available: %d\n", size, ring->size - ring->fill); return SANE_STATUS_NO_MEM; } tail = ring->end - ring->wp; if (size < tail) { memcpy(ring->wp, buf, size); ring->wp += size; ring->fill += size; } else { memcpy(ring->wp, buf, tail); size -= tail; ring->wp = ring->ring; memcpy(ring->wp, buf + tail, size); ring->wp += size; ring->fill += (tail + size); } return SANE_STATUS_GOOD; } SANE_Int eds_ring_read(ring_buffer *ring, SANE_Byte *buf, SANE_Int size) { SANE_Int tail; DBG(18, "reading from ring, %d bytes available\n", (int)ring->fill); /* limit read to available */ if (size > ring->fill) { DBG(1, "not enough data in the ring, shouldn't happen\n"); size = ring->fill; } tail = ring->end - ring->rp; if (size < tail) { memcpy(buf, ring->rp, size); ring->rp += size; ring->fill -= size; return size; } else { memcpy(buf, ring->rp, tail); size -= tail; ring->rp = ring->ring; memcpy(buf + tail, ring->rp, size); ring->rp += size; ring->fill -= (size + tail); return size + tail; } } SANE_Int eds_ring_skip(ring_buffer *ring, SANE_Int size) { SANE_Int tail; /* limit skip to available */ if (size > ring->fill) size = ring->fill; tail = ring->end - ring->rp; if (size < tail) { ring->rp += size; } else { ring->rp = ring->ring + (size - tail); } ring->fill -= size; return size; } SANE_Int eds_ring_avail(ring_buffer *ring) { return ring->fill; } void eds_ring_flush(ring_buffer *ring) { eds_ring_skip(ring, ring->fill); } void eds_ring_destory(ring_buffer *ring) { if (ring->ring) { free(ring->ring); ring->ring = NULL; } } backends-1.3.0/backend/epsonds-ops.h000066400000000000000000000036111456256263500173160ustar00rootroot00000000000000/* * epsonds-ops.h - Epson ESC/I-2 driver. * * Copyright (C) 2015 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ extern void eds_dev_init(epsonds_device *dev); extern SANE_Status eds_dev_post_init(struct epsonds_device *dev); extern SANE_Bool eds_is_model(epsonds_device *dev, const char *model); extern SANE_Status eds_add_resolution(epsonds_device *dev, int r); extern SANE_Status eds_set_resolution_range(epsonds_device *dev, int min, int max); extern void eds_set_fbf_area(epsonds_device *dev, int x, int y, int unit); extern void eds_set_adf_area(epsonds_device *dev, int x, int y, int unit); extern void eds_set_tpu_area(epsonds_device *dev, int x, int y, int unit); extern SANE_Status eds_add_depth(epsonds_device *dev, SANE_Word depth); extern SANE_Status eds_discover_capabilities(epsonds_scanner *s); extern SANE_Status eds_set_extended_scanning_parameters(epsonds_scanner *s); extern SANE_Status eds_set_scanning_parameters(epsonds_scanner *s); extern void eds_setup_block_mode(epsonds_scanner *s); extern SANE_Status eds_init_parameters(epsonds_scanner *s); extern void eds_copy_image_from_ring(epsonds_scanner *s, SANE_Byte *data, SANE_Int max_length, SANE_Int *length); extern SANE_Status eds_ring_init(ring_buffer *ring, SANE_Int size); extern SANE_Status eds_ring_write(ring_buffer *ring, SANE_Byte *buf, SANE_Int size); extern SANE_Int eds_ring_read(ring_buffer *ring, SANE_Byte *buf, SANE_Int size); extern SANE_Int eds_ring_skip(ring_buffer *ring, SANE_Int size); extern SANE_Int eds_ring_avail(ring_buffer *ring); extern void eds_ring_flush(ring_buffer *ring) ; extern void eds_ring_destory(ring_buffer *ring) ; backends-1.3.0/backend/epsonds-usb.c000066400000000000000000000006461456256263500173060ustar00rootroot00000000000000/* * epsonds-usb.c - Epson ESC/I-2 driver, USB device list. * * Copyright (C) 2015 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ #include "epsonds-usb.h" backends-1.3.0/backend/epsonds-usb.h000066400000000000000000000010051456256263500173010ustar00rootroot00000000000000/* * epsonds-usb.h - Epson ESC/I-2 driver, USB device list. * * Copyright (C) 2015 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ #include "sane/sane.h" #ifndef _EPSONDS_USB_H_ #define _EPSONDS_USB_H_ #define SANE_EPSONDS_VENDOR_ID (0x4b8) #endif backends-1.3.0/backend/epsonds.c000066400000000000000000003246601456256263500165240ustar00rootroot00000000000000/* * epsonds.c - Epson ESC/I-2 driver. * * Copyright (C) 2015 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ #define EPSONDS_VERSION 1 #define EPSONDS_REVISION 1 #define EPSONDS_BUILD 0 /* debugging levels: * * 32 eds_send * 30 eds_recv * 20 sane_read and related * 18 sane_read and related * 17 setvalue, getvalue, control_option * 16 * 15 esci2_img * 13 image_cb * 12 eds_control * 11 all received params * 10 some received params * 9 * 8 esci2_xxx * 7 open/close/attach * 6 print_params * 5 basic functions * 3 JPEG decompressor * 1 scanner info and capabilities * 0 errors */ #include "sane/config.h" #include #ifdef HAVE_SYS_SELECT_H #include #endif #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include #include #include "sane/saneopts.h" #include "sane/sanei_config.h" #include "sane/sanei_tcp.h" #include "sane/sanei_udp.h" #include "epsonds.h" #include "epsonds-usb.h" #include "epsonds-io.h" #include "epsonds-cmd.h" #include "epsonds-ops.h" #include "epsonds-jpeg.h" #include "epsonds-net.h" static SANE_Status setvalue(SANE_Handle handle, SANE_Int option, void *value, SANE_Int *info); /* * Definition of the mode_param struct, that is used to * specify the valid parameters for the different scan modes. * * The depth variable gets updated when the bit depth is modified. */ static unsigned char LUT[][256] = { {// 0 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, 0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, 0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, 0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, 0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47, 0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57, 0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F, 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67, 0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77, 0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F, 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97, 0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7, 0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7, 0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7, 0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7, 0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7, 0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7, 0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF }, { // 1 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,0x00,0x00,0x00,0x01, 0x02,0x03,0x04,0x06,0x07,0x09,0x0B,0x0D, 0x10,0x12,0x14,0x17,0x19,0x1B,0x1E,0x20, 0x22,0x24,0x26,0x28,0x2A,0x2C,0x2E,0x30, 0x32,0x33,0x35,0x37,0x39,0x3B,0x3C,0x3E, 0x40,0x41,0x43,0x45,0x46,0x48,0x4A,0x4B, 0x4D,0x4F,0x50,0x52,0x53,0x55,0x57,0x58, 0x5A,0x5B,0x5D,0x5E,0x60,0x61,0x63,0x64, 0x66,0x67,0x69,0x6A,0x6C,0x6D,0x6F,0x70, 0x71,0x73,0x74,0x76,0x77,0x79,0x7A,0x7B, 0x7D,0x7E,0x7F,0x81,0x82,0x84,0x85,0x86, 0x88,0x89,0x8A,0x8C,0x8D,0x8E,0x90,0x91, 0x92,0x94,0x95,0x96,0x98,0x99,0x9A,0x9C, 0x9D,0x9E,0xA0,0xA1,0xA2,0xA3,0xA5,0xA6, 0xA7,0xA8,0xAA,0xAB,0xAC,0xAE,0xAF,0xB0, 0xB1,0xB3,0xB4,0xB5,0xB6,0xB8,0xB9,0xBA, 0xBB,0xBC,0xBE,0xBF,0xC0,0xC1,0xC3,0xC4, 0xC5,0xC6,0xC7,0xC9,0xCA,0xCB,0xCC,0xCD, 0xCF,0xD0,0xD1,0xD2,0xD3,0xD5,0xD6,0xD7, 0xD8,0xD9,0xDA,0xDC,0xDD,0xDE,0xDF,0xE0, 0xE1,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9, 0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF3, 0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFB,0xFC, 0xFD,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF }, { // 2 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,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01, 0x02,0x03,0x04,0x05,0x07,0x08,0x0A,0x0C, 0x0E,0x10,0x12,0x14,0x16,0x16,0x19,0x1B, 0x1D,0x1F,0x21,0x23,0x25,0x27,0x28,0x2A, 0x2C,0x2E,0x30,0x32,0x33,0x35,0x37,0x39, 0x3A,0x3C,0x3E,0x40,0x41,0x43,0x45,0x46, 0x48,0x4A,0x4B,0x4D,0x4F,0x50,0x52,0x53, 0x55,0x57,0x58,0x5A,0x5B,0x5D,0x5F,0x60, 0x62,0x63,0x65,0x66,0x68,0x69,0x6B,0x6C, 0x6E,0x6F,0x71,0x72,0x74,0x75,0x77,0x78, 0x7A,0x7B,0x7D,0x7E,0x80,0x81,0x83,0x84, 0x86,0x87,0x88,0x8A,0x8B,0x8D,0x8E,0x90, 0x91,0x92,0x94,0x95,0x97,0x98,0x99,0x9B, 0x9C,0x9E,0x9F,0xA0,0xA2,0xA3,0xA5,0xA7, 0xA9,0xAA,0xAB,0xAD,0xAE,0xB0,0xB1,0xB2, 0xB4,0xB5,0xB6,0xB8,0xB9,0xBA,0xBC,0xBD, 0xBE,0xC0,0xC1,0xC2,0xC4,0xC5,0xC6,0xC8, 0xC9,0xCA,0xCC,0xCD,0xCE,0xD0,0xD1,0xD2, 0xD4,0xD5,0xD6,0xD7,0xD9,0xDA,0xDB,0xDD, 0xDE,0xDF,0xE1,0xE2,0xE3,0xE4,0xE6,0xE7, 0xE8,0xE9,0xEB,0xEC,0xED,0xEF,0xF0,0xF1, 0xF2,0xF4,0xF5,0xF6,0xF7,0xF9,0xFA,0xFB, 0xFC,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }, { // 3 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,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x07, 0x08,0x0A,0x0C,0x0E,0x10,0x12,0x14,0x16, 0x18,0x1A,0x1C,0x1E,0x20,0x22,0x24,0x26, 0x28,0x2A,0x2B,0x2D,0x2F,0x31,0x33,0x34, 0x36,0x38,0x39,0x3B,0x3D,0x3E,0x40,0x42, 0x43,0x45,0x47,0x48,0x4A,0x4B,0x4D,0x4F, 0x50,0x52,0x53,0x55,0x56,0x58,0x59,0x5B, 0x5D,0x5E,0x60,0x61,0x63,0x64,0x66,0x67, 0x69,0x6A,0x6B,0x6D,0x6E,0x70,0x71,0x73, 0x74,0x76,0x77,0x79,0x7A,0x7B,0x7D,0x7E, 0x80,0x81,0x83,0x84,0x85,0x87,0x88,0x8A, 0x8B,0x8C,0x8E,0x8F,0x90,0x92,0x93,0x95, 0x96,0x97,0x99,0x9A,0x9B,0x9D,0x9E,0x9F, 0xA1,0xA2,0xA3,0xA5,0xA6,0xA7,0xA9,0xAA, 0xAB,0xAD,0xAE,0xAF,0xB1,0xB2,0xB3,0xB5, 0xB6,0xB7,0xB9,0xBA,0xBB,0xBD,0xBE,0xBF, 0xC0,0xC2,0xC3,0xC4,0xC6,0xC7,0xC8,0xC9, 0xCB,0xCC,0xCD,0xCF,0xD0,0xD1,0xD2,0xD4, 0xD5,0xD6,0xD7,0xD9,0xDA,0xDB,0xDC,0xDF, 0xE0,0xE1,0xE3,0xE4,0xE5,0xE6,0xE8,0xE9, 0xEA,0xEB,0xED,0xEE,0xEF,0xF0,0xF2,0xF3, 0xF4,0xF5,0xF6,0xF8,0xF9,0xFA,0xFB,0xFD, 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }, { //4 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,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x03, 0x04,0x05,0x07,0x08,0x0A,0x0C,0x0E,0x10, 0x12,0x14,0x16,0x18,0x1A,0x1C,0x1E,0x20, 0x22,0x24,0x26,0x28,0x2A,0x2B,0x2D,0x2F, 0x31,0x33,0x34,0x36,0x38,0x39,0x3B,0x3D, 0x3E,0x40,0x42,0x43,0x45,0x47,0x48,0x4A, 0x4B,0x4D,0x4F,0x50,0x52,0x53,0x55,0x56, 0x58,0x59,0x5B,0x5D,0x5E,0x60,0x61,0x63, 0x64,0x66,0x67,0x69,0x6A,0x6B,0x6D,0x6E, 0x70,0x71,0x73,0x74,0x76,0x77,0x79,0x7A, 0x7B,0x7D,0x7E,0x80,0x81,0x83,0x84,0x85, 0x87,0x88,0x8A,0x8B,0x8C,0x8E,0x8F,0x90, 0x92,0x93,0x95,0x96,0x97,0x99,0x9A,0x9B, 0x9D,0x9E,0x9F,0xA1,0xA2,0xA3,0xA5,0xA6, 0xA7,0xA9,0xAA,0xAB,0xAD,0xAE,0xAF,0xB1, 0xB2,0xB3,0xB5,0xB6,0xB7,0xB9,0xBA,0xBB, 0xBD,0xBE,0xBF,0xC0,0xC2,0xC3,0xC4,0xC6, 0xC7,0xC8,0xC9,0xCB,0xCC,0xCD,0xCF,0xD0, 0xD1,0xD2,0xD4,0xD5,0xD6,0xD7,0xD9,0xDA, 0xDB,0xDC,0xDE,0xDF,0xE0,0xE1,0xE3,0xE5, 0xE6,0xE8,0xE9,0xEA,0xEB,0xED,0xEE,0xEF, 0xF0,0xF2,0xF3,0xF4,0xF5,0xF6,0xF8,0xF9, 0xFA,0xFB,0xFD,0xFE,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }, { // 5 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,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x01,0x02,0x03,0x04,0x05,0x07,0x08,0x0A, 0x0C,0x0E,0x10,0x12,0x14,0x16,0x18,0x1A, 0x1C,0x1E,0x20,0x22,0x24,0x26,0x28,0x2A, 0x2B,0x2D,0x2F,0x31,0x33,0x34,0x36,0x38, 0x39,0x3B,0x3D,0x3E,0x40,0x42,0x43,0x45, 0x47,0x48,0x4A,0x4B,0x4D,0x4F,0x50,0x52, 0x53,0x55,0x56,0x58,0x59,0x5B,0x5D,0x5E, 0x60,0x61,0x63,0x64,0x66,0x67,0x69,0x6A, 0x6B,0x6D,0x6E,0x70,0x71,0x73,0x74,0x76, 0x77,0x79,0x7A,0x7B,0x7D,0x7E,0x80,0x81, 0x83,0x84,0x85,0x87,0x88,0x8A,0x8B,0x8C, 0x8E,0x8F,0x90,0x92,0x93,0x95,0x96,0x97, 0x99,0x9A,0x9B,0x9D,0x9E,0x9F,0xA1,0xA2, 0xA3,0xA5,0xA6,0xA7,0xA9,0xAA,0xAB,0xAD, 0xAE,0xAF,0xB1,0xB2,0xB3,0xB5,0xB6,0xB7, 0xB9,0xBA,0xBB,0xBD,0xBE,0xBF,0xC0,0xC2, 0xC3,0xC4,0xC6,0xC7,0xC8,0xC9,0xCB,0xCC, 0xCD,0xCF,0xD0,0xD1,0xD2,0xD4,0xD5,0xD6, 0xD7,0xD9,0xDA,0xDB,0xDC,0xDE,0xDF,0xE0, 0xE3,0xE4,0xE5,0xE6,0xE8,0xE9,0xEA,0xEB, 0xED,0xEE,0xEF,0xF0,0xF2,0xF3,0xF4,0xF5, 0xF6,0xF8,0xF9,0xFA,0xFB,0xFD,0xFE,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }, { // 6 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02, 0x03,0x04,0x05,0x07,0x08,0x0A,0x0C,0x0E, 0x10,0x12,0x14,0x16,0x18,0x18,0x1A,0x1C, 0x1E,0x20,0x22,0x24,0x26,0x27,0x29,0x2B, 0x2C,0x2E,0x30,0x31,0x33,0x35,0x36,0x38, 0x39,0x3B,0x3C,0x3E,0x40,0x41,0x43,0x44, 0x45,0x47,0x48,0x4A,0x4B,0x4D,0x4E,0x50, 0x51,0x52,0x54,0x55,0x56,0x58,0x59,0x5B, 0x5C,0x5D,0x5F,0x60,0x61,0x63,0x64,0x65, 0x67,0x68,0x69,0x6A,0x6C,0x6D,0x6E,0x70, 0x71,0x72,0x73,0x75,0x76,0x77,0x78,0x7A, 0x7B,0x7C,0x7D,0x7E,0x80,0x81,0x82,0x83, 0x85,0x86,0x87,0x88,0x89,0x8A,0x8C,0x8D, 0x8E,0x8F,0x90,0x92,0x93,0x94,0x95,0x96, 0x97,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, 0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8, 0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1, 0xB2,0xB3,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA, 0xBB,0xBC,0xBD,0xBE,0xBF,0xC0,0xC2,0xC4, 0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC, 0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD4,0xD5, 0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD, 0xDE,0xDF,0xE0,0xE1,0xE2,0xE3,0xE4,0xE5, 0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED, 0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5, 0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD, 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }, { // 7 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,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02, 0x03,0x04,0x05,0x06,0x07,0x09,0x0B,0x0C, 0x0E,0x10,0x12,0x14,0x16,0x18,0x1A,0x1C, 0x1E,0x20,0x21,0x23,0x25,0x27,0x28,0x2A, 0x2C,0x2D,0x2F,0x31,0x32,0x34,0x36,0x37, 0x39,0x3B,0x3C,0x3E,0x3F,0x41,0x42,0x44, 0x45,0x47,0x48,0x4A,0x4B,0x4D,0x4E,0x50, 0x51,0x53,0x54,0x56,0x57,0x59,0x5A,0x5C, 0x5D,0x5F,0x60,0x61,0x63,0x64,0x66,0x67, 0x68,0x6A,0x6B,0x6D,0x6E,0x6F,0x71,0x72, 0x73,0x75,0x76,0x78,0x79,0x7A,0x7C,0x7D, 0x7E,0x80,0x81,0x82,0x84,0x85,0x86,0x88, 0x89,0x8A,0x8B,0x8D,0x8E,0x8F,0x91,0x92, 0x93,0x95,0x96,0x97,0x98,0x9A,0x9B,0x9C, 0x9E,0x9F,0xA0,0xA1,0xA3,0xA4,0xA5,0xA6, 0xA8,0xA9,0xAA,0xAB,0xAD,0xAE,0xAF,0xB0, 0xB2,0xB3,0xB4,0xB5,0xB7,0xB8,0xB9,0xBA, 0xBC,0xBD,0xBE,0xBF,0xC0,0xC2,0xC3,0xC4, 0xC5,0xC7,0xC8,0xC9,0xCA,0xCB,0xCD,0xCE, 0xCF,0xD0,0xD1,0xD3,0xD4,0xD5,0xD6,0xD7, 0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xE0,0xE1, 0xE2,0xE3,0xE4,0xE6,0xE7,0xE8,0xE9,0xEA, 0xEB,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3, 0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFC,0xFD, 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, } }; static unsigned char LUT_R[][256] = { { // 0 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, 0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, 0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, 0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, 0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47, 0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57, 0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F, 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67, 0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77, 0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F, 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97, 0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7, 0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7, 0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7, 0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7, 0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7, 0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7, 0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF }, { // 1 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,0x00,0x00,0x00,0x01, 0x02,0x03,0x04,0x06,0x07,0x09,0x0B,0x0D, 0x10,0x12,0x14,0x17,0x19,0x1B,0x1E,0x20, 0x22,0x24,0x26,0x28,0x2A,0x2C,0x2E,0x30, 0x32,0x33,0x35,0x37,0x39,0x3B,0x3C,0x3E, 0x40,0x41,0x43,0x45,0x46,0x48,0x4A,0x4B, 0x4D,0x4F,0x50,0x52,0x53,0x55,0x57,0x58, 0x5A,0x5B,0x5D,0x5E,0x60,0x61,0x63,0x64, 0x66,0x67,0x69,0x6A,0x6C,0x6D,0x6F,0x70, 0x71,0x73,0x74,0x76,0x77,0x79,0x7A,0x7B, 0x7D,0x7E,0x7F,0x81,0x82,0x84,0x85,0x86, 0x88,0x89,0x8A,0x8C,0x8D,0x8E,0x90,0x91, 0x92,0x94,0x95,0x96,0x98,0x99,0x9A,0x9C, 0x9D,0x9E,0xA0,0xA1,0xA2,0xA3,0xA5,0xA6, 0xA7,0xA8,0xAA,0xAB,0xAC,0xAE,0xAF,0xB0, 0xB1,0xB3,0xB4,0xB5,0xB6,0xB8,0xB9,0xBA, 0xBB,0xBC,0xBE,0xBF,0xC0,0xC1,0xC3,0xC4, 0xC5,0xC6,0xC7,0xC9,0xCA,0xCB,0xCC,0xCD, 0xCF,0xD0,0xD1,0xD2,0xD3,0xD5,0xD6,0xD7, 0xD8,0xD9,0xDA,0xDC,0xDD,0xDE,0xDF,0xE0, 0xE1,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9, 0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF3, 0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFB,0xFC, 0xFD,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF }, { // 2 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,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02, 0x03,0x04,0x05,0x07,0x08,0x0A,0x0C,0x0E, 0x10,0x12,0x14,0x16,0x19,0x1B,0x1D,0x1F, 0x21,0x23,0x25,0x27,0x28,0x2A,0x2C,0x2E, 0x30,0x32,0x33,0x35,0x37,0x39,0x3A,0x3C, 0x3E,0x40,0x41,0x43,0x45,0x46,0x48,0x4A, 0x4B,0x4D,0x4F,0x50,0x52,0x53,0x55,0x57, 0x58,0x5A,0x5B,0x5D,0x5F,0x60,0x62,0x63, 0x65,0x66,0x68,0x69,0x6B,0x6C,0x6E,0x6F, 0x71,0x72,0x74,0x75,0x77,0x78,0x7A,0x7B, 0x7D,0x7E,0x80,0x81,0x83,0x84,0x86,0x87, 0x88,0x8A,0x8B,0x8D,0x8E,0x90,0x91,0x92, 0x94,0x95,0x97,0x98,0x99,0x9B,0x9C,0x9E, 0x9F,0xA0,0xA2,0xA3,0xA5,0xA6,0xA7,0xA9, 0xAA,0xAB,0xAD,0xAE,0xB0,0xB1,0xB2,0xB4, 0xB5,0xB6,0xB8,0xB9,0xBA,0xBC,0xBD,0xBE, 0xC0,0xC1,0xC2,0xC4,0xC5,0xC6,0xC8,0xC9, 0xCA,0xCC,0xCD,0xCE,0xD0,0xD1,0xD2,0xD4, 0xD5,0xD6,0xD7,0xD9,0xDA,0xDB,0xDD,0xDE, 0xDF,0xE1,0xE2,0xE3,0xE4,0xE6,0xE7,0xE8, 0xE9,0xEB,0xEC,0xED,0xEF,0xF0,0xF1,0xF2, 0xF4,0xF5,0xF6,0xF7,0xF9,0xFA,0xFB,0xFC, 0xFE,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }, { 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,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x01,0x02,0x03,0x04,0x05,0x07,0x08,0x0A, 0x0C,0x0E,0x10,0x12,0x14,0x16,0x18,0x1A, 0x1C,0x1E,0x20,0x22,0x24,0x26,0x28,0x2A, 0x2B,0x2D,0x2F,0x31,0x33,0x34,0x36,0x38, 0x39,0x3B,0x3D,0x3E,0x40,0x42,0x43,0x45, 0x47,0x48,0x4A,0x4B,0x4D,0x4F,0x50,0x52, 0x53,0x55,0x56,0x58,0x59,0x5B,0x5D,0x5E, 0x60,0x61,0x63,0x64,0x66,0x67,0x69,0x6A, 0x6B,0x6D,0x6E,0x70,0x71,0x73,0x74,0x76, 0x77,0x79,0x7A,0x7B,0x7D,0x7E,0x80,0x81, 0x83,0x84,0x85,0x87,0x88,0x8A,0x8B,0x8C, 0x8E,0x8F,0x90,0x92,0x93,0x95,0x96,0x97, 0x99,0x9A,0x9B,0x9D,0x9E,0x9F,0xA1,0xA2, 0xA3,0xA5,0xA6,0xA7,0xA9,0xAA,0xAB,0xAD, 0xAE,0xAF,0xB1,0xB2,0xB3,0xB5,0xB6,0xB7, 0xB9,0xBA,0xBB,0xBD,0xBE,0xBF,0xC0,0xC2, 0xC3,0xC4,0xC6,0xC7,0xC8,0xC9,0xCB,0xCC, 0xCD,0xCF,0xD0,0xD1,0xD2,0xD4,0xD5,0xD6, 0xD7,0xD9,0xDA,0xDB,0xDC,0xDE,0xDF,0xE0, 0xE1,0xE3,0xE4,0xE5,0xE5,0xE6,0xE8,0xE9, 0xEA,0xEB,0xED,0xEE,0xEF,0xF0,0xF2,0xF3, 0xF4,0xF5,0xF6,0xF8,0xF9,0xFA,0xFB,0xFD, 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }, { 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,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05, 0x07,0x08,0x0A,0x0C,0x0E,0x10,0x12,0x14, 0x16,0x18,0x1A,0x1C,0x1E,0x20,0x22,0x24, 0x26,0x28,0x2A,0x2B,0x2D,0x2F,0x31,0x33, 0x34,0x36,0x38,0x39,0x3B,0x3D,0x3E,0x40, 0x42,0x43,0x45,0x47,0x48,0x4A,0x4B,0x4D, 0x4F,0x50,0x52,0x53,0x55,0x56,0x58,0x59, 0x5B,0x5D,0x5E,0x60,0x61,0x63,0x64,0x66, 0x67,0x69,0x6A,0x6B,0x6D,0x6E,0x70,0x71, 0x73,0x74,0x76,0x77,0x79,0x7A,0x7B,0x7D, 0x7E,0x80,0x81,0x83,0x84,0x85,0x87,0x88, 0x8A,0x8B,0x8C,0x8E,0x8F,0x90,0x92,0x93, 0x95,0x96,0x97,0x99,0x9A,0x9B,0x9D,0x9E, 0x9F,0xA1,0xA2,0xA3,0xA5,0xA6,0xA7,0xA9, 0xAA,0xAB,0xAD,0xAE,0xAF,0xB1,0xB2,0xB3, 0xB5,0xB6,0xB7,0xB9,0xBA,0xBB,0xBD,0xBE, 0xBF,0xC0,0xC2,0xC3,0xC4,0xC6,0xC7,0xC8, 0xC9,0xCB,0xCC,0xCD,0xCF,0xD0,0xD1,0xD2, 0xD4,0xD5,0xD6,0xD7,0xD9,0xDA,0xDB,0xDC, 0xDE,0xDF,0xE0,0xE1,0xE3,0xE4,0xE5,0xE6, 0xE8,0xE9,0xEA,0xEB,0xEB,0xED,0xEE,0xEF, 0xF0,0xF2,0xF3,0xF4,0xF5,0xF6,0xF8,0xF9, 0xFA,0xFB,0xFD,0xFE,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }, { 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,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02, 0x03,0x04,0x05,0x07,0x08,0x0A,0x0C,0x0E, 0x10,0x12,0x14,0x16,0x18,0x1A,0x1C,0x1E, 0x20,0x22,0x24,0x26,0x28,0x2A,0x2B,0x2D, 0x2F,0x31,0x33,0x34,0x36,0x38,0x39,0x3B, 0x3D,0x3E,0x40,0x42,0x43,0x45,0x47,0x48, 0x4A,0x4B,0x4D,0x4F,0x50,0x52,0x53,0x55, 0x56,0x58,0x59,0x5B,0x5D,0x5E,0x60,0x61, 0x63,0x64,0x66,0x67,0x69,0x6A,0x6B,0x6D, 0x6E,0x70,0x71,0x73,0x74,0x76,0x77,0x79, 0x7A,0x7B,0x7D,0x7E,0x80,0x81,0x83,0x84, 0x85,0x87,0x88,0x8A,0x8B,0x8C,0x8E,0x8F, 0x90,0x92,0x93,0x95,0x96,0x97,0x99,0x9A, 0x9B,0x9D,0x9E,0x9F,0xA1,0xA2,0xA3,0xA5, 0xA6,0xA7,0xA9,0xAA,0xAB,0xAD,0xAE,0xAF, 0xB1,0xB2,0xB3,0xB5,0xB6,0xB7,0xB9,0xBA, 0xBB,0xBD,0xBE,0xBF,0xC0,0xC2,0xC3,0xC4, 0xC6,0xC7,0xC8,0xC9,0xCB,0xCC,0xCD,0xCF, 0xD0,0xD1,0xD2,0xD4,0xD5,0xD6,0xD7,0xD9, 0xDA,0xDB,0xDC,0xDE,0xDF,0xE0,0xE1,0xE3, 0xE4,0xE5,0xE6,0xE8,0xE9,0xEA,0xEB,0xED, 0xEE,0xEF,0xF0,0xF2,0xF3,0xF4,0xF5,0xF6, 0xF8,0xF9,0xFA,0xFB,0xFD,0xFE,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }, { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01, 0x02,0x03,0x04,0x05,0x06,0x08,0x0A,0x0C, 0x0E,0x10,0x12,0x14,0x16,0x19,0x1B,0x1D, 0x1F,0x20,0x22,0x24,0x26,0x28,0x2A,0x2B, 0x2D,0x2F,0x31,0x32,0x34,0x35,0x37,0x39, 0x3A,0x3C,0x3D,0x3F,0x41,0x42,0x44,0x45, 0x47,0x48,0x4A,0x4B,0x4D,0x4E,0x4F,0x51, 0x52,0x54,0x55,0x57,0x58,0x59,0x5B,0x5C, 0x5E,0x5F,0x60,0x62,0x63,0x64,0x66,0x67, 0x68,0x6A,0x6B,0x6C,0x6E,0x6F,0x70,0x72, 0x73,0x74,0x75,0x77,0x78,0x79,0x7B,0x7C, 0x7D,0x7E,0x80,0x81,0x82,0x83,0x85,0x86, 0x87,0x88,0x89,0x8B,0x8C,0x8D,0x8E,0x90, 0x91,0x92,0x93,0x94,0x96,0x97,0x98,0x99, 0x9A,0x9B,0x9D,0x9E,0x9F,0xA0,0xA1,0xA2, 0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAB,0xAC, 0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB4,0xB5, 0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBD,0xBE, 0xBF,0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6, 0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, 0xD0,0xD1,0xD2,0xD3,0xD4,0xD6,0xD7,0xD8, 0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,0xE0, 0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xEA, 0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2, 0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA, 0xFB,0xFC,0xFD,0xFE,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }, { 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,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02, 0x03,0x04,0x05,0x06,0x07,0x09,0x0B,0x0C, 0x0E,0x10,0x12,0x14,0x16,0x18,0x1A,0x1C, 0x1E,0x20,0x21,0x23,0x25,0x27,0x28,0x2A, 0x2C,0x2D,0x2F,0x31,0x32,0x34,0x36,0x37, 0x39,0x3B,0x3C,0x3E,0x3F,0x41,0x42,0x44, 0x45,0x47,0x48,0x4A,0x4B,0x4D,0x4E,0x50, 0x51,0x53,0x54,0x56,0x57,0x59,0x5A,0x5C, 0x5D,0x5F,0x60,0x61,0x63,0x64,0x66,0x67, 0x68,0x6A,0x6B,0x6D,0x6E,0x6F,0x71,0x72, 0x73,0x75,0x76,0x78,0x79,0x7A,0x7C,0x7D, 0x7E,0x80,0x81,0x82,0x84,0x85,0x86,0x88, 0x89,0x8A,0x8B,0x8D,0x8E,0x8F,0x91,0x92, 0x93,0x95,0x96,0x97,0x98,0x9A,0x9B,0x9C, 0x9E,0x9F,0xA0,0xA1,0xA3,0xA4,0xA5,0xA6, 0xA8,0xA9,0xAA,0xAB,0xAD,0xAE,0xAF,0xB0, 0xB2,0xB3,0xB4,0xB5,0xB7,0xB8,0xB9,0xBA, 0xBC,0xBD,0xBE,0xBF,0xC0,0xC2,0xC3,0xC4, 0xC5,0xC7,0xC8,0xC9,0xCA,0xCB,0xCD,0xCE, 0xCF,0xD0,0xD1,0xD3,0xD4,0xD5,0xD6,0xD7, 0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xE0,0xE1, 0xE2,0xE3,0xE4,0xE6,0xE7,0xE8,0xE9,0xEA, 0xEB,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3, 0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFC,0xFD, 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF } }; static unsigned char LUT_G[][256] = { { 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, 0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, 0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, 0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, 0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47, 0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57, 0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F, 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67, 0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77, 0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F, 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97, 0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7, 0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7, 0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7, 0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7, 0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7, 0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7, 0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF }, { 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,0x00,0x00,0x00,0x01, 0x02,0x03,0x04,0x06,0x07,0x09,0x0B,0x0D, 0x10,0x12,0x14,0x17,0x19,0x1B,0x1E,0x20, 0x22,0x24,0x26,0x28,0x2A,0x2C,0x2E,0x30, 0x32,0x33,0x35,0x37,0x39,0x3B,0x3C,0x3E, 0x40,0x41,0x43,0x45,0x46,0x48,0x4A,0x4B, 0x4D,0x4F,0x50,0x52,0x53,0x55,0x57,0x58, 0x5A,0x5B,0x5D,0x5E,0x60,0x61,0x63,0x64, 0x66,0x67,0x69,0x6A,0x6C,0x6D,0x6F,0x70, 0x71,0x73,0x74,0x76,0x77,0x79,0x7A,0x7B, 0x7D,0x7E,0x7F,0x81,0x82,0x84,0x85,0x86, 0x88,0x89,0x8A,0x8C,0x8D,0x8E,0x90,0x91, 0x92,0x94,0x95,0x96,0x98,0x99,0x9A,0x9C, 0x9D,0x9E,0xA0,0xA1,0xA2,0xA3,0xA5,0xA6, 0xA7,0xA8,0xAA,0xAB,0xAC,0xAE,0xAF,0xB0, 0xB1,0xB3,0xB4,0xB5,0xB6,0xB8,0xB9,0xBA, 0xBB,0xBC,0xBE,0xBF,0xC0,0xC1,0xC3,0xC4, 0xC5,0xC6,0xC7,0xC9,0xCA,0xCB,0xCC,0xCD, 0xCF,0xD0,0xD1,0xD2,0xD3,0xD5,0xD6,0xD7, 0xD8,0xD9,0xDA,0xDC,0xDD,0xDE,0xDF,0xE0, 0xE1,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9, 0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF3, 0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFB,0xFC, 0xFD,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF }, { 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,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01, 0x02,0x03,0x04,0x05,0x07,0x08,0x0A,0x0C, 0x0E,0x10,0x12,0x14,0x16,0x16,0x19,0x1B, 0x1D,0x1F,0x21,0x23,0x25,0x27,0x28,0x2A, 0x2C,0x2E,0x30,0x32,0x33,0x35,0x37,0x39, 0x3A,0x3C,0x3E,0x40,0x41,0x43,0x45,0x46, 0x48,0x4A,0x4B,0x4D,0x4F,0x50,0x52,0x53, 0x55,0x57,0x58,0x5A,0x5B,0x5D,0x5F,0x60, 0x62,0x63,0x65,0x66,0x68,0x69,0x6B,0x6C, 0x6E,0x6F,0x71,0x72,0x74,0x75,0x77,0x78, 0x7A,0x7B,0x7D,0x7E,0x80,0x81,0x83,0x84, 0x86,0x87,0x88,0x8A,0x8B,0x8D,0x8E,0x90, 0x91,0x92,0x94,0x95,0x97,0x98,0x99,0x9B, 0x9C,0x9E,0x9F,0xA0,0xA2,0xA3,0xA5,0xA7, 0xA9,0xAA,0xAB,0xAD,0xAE,0xB0,0xB1,0xB2, 0xB4,0xB5,0xB6,0xB8,0xB9,0xBA,0xBC,0xBD, 0xBE,0xC0,0xC1,0xC2,0xC4,0xC5,0xC6,0xC8, 0xC9,0xCA,0xCC,0xCD,0xCE,0xD0,0xD1,0xD2, 0xD4,0xD5,0xD6,0xD7,0xD9,0xDA,0xDB,0xDD, 0xDE,0xDF,0xE1,0xE2,0xE3,0xE4,0xE6,0xE7, 0xE8,0xE9,0xEB,0xEC,0xED,0xEF,0xF0,0xF1, 0xF2,0xF4,0xF5,0xF6,0xF7,0xF9,0xFA,0xFB, 0xFC,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }, { 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,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x07, 0x08,0x0A,0x0C,0x0E,0x10,0x12,0x14,0x16, 0x18,0x1A,0x1C,0x1E,0x20,0x22,0x24,0x26, 0x28,0x2A,0x2B,0x2D,0x2F,0x31,0x33,0x34, 0x36,0x38,0x39,0x3B,0x3D,0x3E,0x40,0x42, 0x43,0x45,0x47,0x48,0x4A,0x4B,0x4D,0x4F, 0x50,0x52,0x53,0x55,0x56,0x58,0x59,0x5B, 0x5D,0x5E,0x60,0x61,0x63,0x64,0x66,0x67, 0x69,0x6A,0x6B,0x6D,0x6E,0x70,0x71,0x73, 0x74,0x76,0x77,0x79,0x7A,0x7B,0x7D,0x7E, 0x80,0x81,0x83,0x84,0x85,0x87,0x88,0x8A, 0x8B,0x8C,0x8E,0x8F,0x90,0x92,0x93,0x95, 0x96,0x97,0x99,0x9A,0x9B,0x9D,0x9E,0x9F, 0xA1,0xA2,0xA3,0xA5,0xA6,0xA7,0xA9,0xAA, 0xAB,0xAD,0xAE,0xAF,0xB1,0xB2,0xB3,0xB5, 0xB6,0xB7,0xB9,0xBA,0xBB,0xBD,0xBE,0xBF, 0xC0,0xC2,0xC3,0xC4,0xC6,0xC7,0xC8,0xC9, 0xCB,0xCC,0xCD,0xCF,0xD0,0xD1,0xD2,0xD4, 0xD5,0xD6,0xD7,0xD9,0xDA,0xDB,0xDC,0xDF, 0xE0,0xE1,0xE3,0xE4,0xE5,0xE6,0xE8,0xE9, 0xEA,0xEB,0xED,0xEE,0xEF,0xF0,0xF2,0xF3, 0xF4,0xF5,0xF6,0xF8,0xF9,0xFA,0xFB,0xFD, 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }, { 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,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x03, 0x04,0x05,0x07,0x08,0x0A,0x0C,0x0E,0x10, 0x12,0x14,0x16,0x18,0x1A,0x1C,0x1E,0x20, 0x22,0x24,0x26,0x28,0x2A,0x2B,0x2D,0x2F, 0x31,0x33,0x34,0x36,0x38,0x39,0x3B,0x3D, 0x3E,0x40,0x42,0x43,0x45,0x47,0x48,0x4A, 0x4B,0x4D,0x4F,0x50,0x52,0x53,0x55,0x56, 0x58,0x59,0x5B,0x5D,0x5E,0x60,0x61,0x63, 0x64,0x66,0x67,0x69,0x6A,0x6B,0x6D,0x6E, 0x70,0x71,0x73,0x74,0x76,0x77,0x79,0x7A, 0x7B,0x7D,0x7E,0x80,0x81,0x83,0x84,0x85, 0x87,0x88,0x8A,0x8B,0x8C,0x8E,0x8F,0x90, 0x92,0x93,0x95,0x96,0x97,0x99,0x9A,0x9B, 0x9D,0x9E,0x9F,0xA1,0xA2,0xA3,0xA5,0xA6, 0xA7,0xA9,0xAA,0xAB,0xAD,0xAE,0xAF,0xB1, 0xB2,0xB3,0xB5,0xB6,0xB7,0xB9,0xBA,0xBB, 0xBD,0xBE,0xBF,0xC0,0xC2,0xC3,0xC4,0xC6, 0xC7,0xC8,0xC9,0xCB,0xCC,0xCD,0xCF,0xD0, 0xD1,0xD2,0xD4,0xD5,0xD6,0xD7,0xD9,0xDA, 0xDB,0xDC,0xDE,0xDF,0xE0,0xE1,0xE3,0xE5, 0xE6,0xE8,0xE9,0xEA,0xEB,0xED,0xEE,0xEF, 0xF0,0xF2,0xF3,0xF4,0xF5,0xF6,0xF8,0xF9, 0xFA,0xFB,0xFD,0xFE,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }, { 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,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x01,0x02,0x03,0x04,0x05,0x07,0x08,0x0A, 0x0C,0x0E,0x10,0x12,0x14,0x16,0x18,0x1A, 0x1C,0x1E,0x20,0x22,0x24,0x26,0x28,0x2A, 0x2B,0x2D,0x2F,0x31,0x33,0x34,0x36,0x38, 0x39,0x3B,0x3D,0x3E,0x40,0x42,0x43,0x45, 0x47,0x48,0x4A,0x4B,0x4D,0x4F,0x50,0x52, 0x53,0x55,0x56,0x58,0x59,0x5B,0x5D,0x5E, 0x60,0x61,0x63,0x64,0x66,0x67,0x69,0x6A, 0x6B,0x6D,0x6E,0x70,0x71,0x73,0x74,0x76, 0x77,0x79,0x7A,0x7B,0x7D,0x7E,0x80,0x81, 0x83,0x84,0x85,0x87,0x88,0x8A,0x8B,0x8C, 0x8E,0x8F,0x90,0x92,0x93,0x95,0x96,0x97, 0x99,0x9A,0x9B,0x9D,0x9E,0x9F,0xA1,0xA2, 0xA3,0xA5,0xA6,0xA7,0xA9,0xAA,0xAB,0xAD, 0xAE,0xAF,0xB1,0xB2,0xB3,0xB5,0xB6,0xB7, 0xB9,0xBA,0xBB,0xBD,0xBE,0xBF,0xC0,0xC2, 0xC3,0xC4,0xC6,0xC7,0xC8,0xC9,0xCB,0xCC, 0xCD,0xCF,0xD0,0xD1,0xD2,0xD4,0xD5,0xD6, 0xD7,0xD9,0xDA,0xDB,0xDC,0xDE,0xDF,0xE0, 0xE3,0xE4,0xE5,0xE6,0xE8,0xE9,0xEA,0xEB, 0xED,0xEE,0xEF,0xF0,0xF2,0xF3,0xF4,0xF5, 0xF6,0xF8,0xF9,0xFA,0xFB,0xFD,0xFE,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }, { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02, 0x03,0x04,0x05,0x07,0x08,0x0A,0x0C,0x0E, 0x10,0x12,0x14,0x16,0x18,0x18,0x1A,0x1C, 0x1E,0x20,0x22,0x24,0x26,0x27,0x29,0x2B, 0x2C,0x2E,0x30,0x31,0x33,0x35,0x36,0x38, 0x39,0x3B,0x3C,0x3E,0x40,0x41,0x43,0x44, 0x45,0x47,0x48,0x4A,0x4B,0x4D,0x4E,0x50, 0x51,0x52,0x54,0x55,0x56,0x58,0x59,0x5B, 0x5C,0x5D,0x5F,0x60,0x61,0x63,0x64,0x65, 0x67,0x68,0x69,0x6A,0x6C,0x6D,0x6E,0x70, 0x71,0x72,0x73,0x75,0x76,0x77,0x78,0x7A, 0x7B,0x7C,0x7D,0x7E,0x80,0x81,0x82,0x83, 0x85,0x86,0x87,0x88,0x89,0x8A,0x8C,0x8D, 0x8E,0x8F,0x90,0x92,0x93,0x94,0x95,0x96, 0x97,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, 0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8, 0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1, 0xB2,0xB3,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA, 0xBB,0xBC,0xBD,0xBE,0xBF,0xC0,0xC2,0xC4, 0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC, 0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD4,0xD5, 0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD, 0xDE,0xDF,0xE0,0xE1,0xE2,0xE3,0xE4,0xE5, 0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED, 0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5, 0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD, 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }, { // 7 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,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02, 0x03,0x04,0x05,0x06,0x07,0x09,0x0B,0x0C, 0x0E,0x10,0x12,0x14,0x16,0x18,0x1A,0x1C, 0x1E,0x20,0x21,0x23,0x25,0x27,0x28,0x2A, 0x2C,0x2D,0x2F,0x31,0x32,0x34,0x36,0x37, 0x39,0x3B,0x3C,0x3E,0x3F,0x41,0x42,0x44, 0x45,0x47,0x48,0x4A,0x4B,0x4D,0x4E,0x50, 0x51,0x53,0x54,0x56,0x57,0x59,0x5A,0x5C, 0x5D,0x5F,0x60,0x61,0x63,0x64,0x66,0x67, 0x68,0x6A,0x6B,0x6D,0x6E,0x6F,0x71,0x72, 0x73,0x75,0x76,0x78,0x79,0x7A,0x7C,0x7D, 0x7E,0x80,0x81,0x82,0x84,0x85,0x86,0x88, 0x89,0x8A,0x8B,0x8D,0x8E,0x8F,0x91,0x92, 0x93,0x95,0x96,0x97,0x98,0x9A,0x9B,0x9C, 0x9E,0x9F,0xA0,0xA1,0xA3,0xA4,0xA5,0xA6, 0xA8,0xA9,0xAA,0xAB,0xAD,0xAE,0xAF,0xB0, 0xB2,0xB3,0xB4,0xB5,0xB7,0xB8,0xB9,0xBA, 0xBC,0xBD,0xBE,0xBF,0xC0,0xC2,0xC3,0xC4, 0xC5,0xC7,0xC8,0xC9,0xCA,0xCB,0xCD,0xCE, 0xCF,0xD0,0xD1,0xD3,0xD4,0xD5,0xD6,0xD7, 0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xE0,0xE1, 0xE2,0xE3,0xE4,0xE6,0xE7,0xE8,0xE9,0xEA, 0xEB,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3, 0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFC,0xFD, 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, } }; static unsigned char LUT_B[][256] = { { 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, 0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, 0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, 0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, 0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47, 0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57, 0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F, 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67, 0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77, 0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F, 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97, 0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7, 0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7, 0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7, 0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7, 0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7, 0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7, 0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF }, { 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,0x00,0x00,0x00,0x01, 0x02,0x03,0x04,0x06,0x07,0x09,0x0B,0x0D, 0x10,0x12,0x14,0x17,0x19,0x1B,0x1E,0x20, 0x22,0x24,0x26,0x28,0x2A,0x2C,0x2E,0x30, 0x32,0x33,0x35,0x37,0x39,0x3B,0x3C,0x3E, 0x40,0x41,0x43,0x45,0x46,0x48,0x4A,0x4B, 0x4D,0x4F,0x50,0x52,0x53,0x55,0x57,0x58, 0x5A,0x5B,0x5D,0x5E,0x60,0x61,0x63,0x64, 0x66,0x67,0x69,0x6A,0x6C,0x6D,0x6F,0x70, 0x71,0x73,0x74,0x76,0x77,0x79,0x7A,0x7B, 0x7D,0x7E,0x7F,0x81,0x82,0x84,0x85,0x86, 0x88,0x89,0x8A,0x8C,0x8D,0x8E,0x90,0x91, 0x92,0x94,0x95,0x96,0x98,0x99,0x9A,0x9C, 0x9D,0x9E,0xA0,0xA1,0xA2,0xA3,0xA5,0xA6, 0xA7,0xA8,0xAA,0xAB,0xAC,0xAE,0xAF,0xB0, 0xB1,0xB3,0xB4,0xB5,0xB6,0xB8,0xB9,0xBA, 0xBB,0xBC,0xBE,0xBF,0xC0,0xC1,0xC3,0xC4, 0xC5,0xC6,0xC7,0xC9,0xCA,0xCB,0xCC,0xCD, 0xCF,0xD0,0xD1,0xD2,0xD3,0xD5,0xD6,0xD7, 0xD8,0xD9,0xDA,0xDC,0xDD,0xDE,0xDF,0xE0, 0xE1,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9, 0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF3, 0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFB,0xFC, 0xFD,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF }, { 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,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01, 0x02,0x03,0x04,0x05,0x07,0x08,0x0A,0x0C, 0x0E,0x10,0x12,0x14,0x16,0x16,0x19,0x1B, 0x1D,0x1F,0x21,0x23,0x25,0x27,0x28,0x2A, 0x2C,0x2E,0x30,0x32,0x33,0x35,0x37,0x39, 0x3A,0x3C,0x3E,0x40,0x41,0x43,0x45,0x46, 0x48,0x4A,0x4B,0x4D,0x4F,0x50,0x52,0x53, 0x55,0x57,0x58,0x5A,0x5B,0x5D,0x5F,0x60, 0x62,0x63,0x65,0x66,0x68,0x69,0x6B,0x6C, 0x6E,0x6F,0x71,0x72,0x74,0x75,0x77,0x78, 0x7A,0x7B,0x7D,0x7E,0x80,0x81,0x83,0x84, 0x86,0x87,0x88,0x8A,0x8B,0x8D,0x8E,0x90, 0x91,0x92,0x94,0x95,0x97,0x98,0x99,0x9B, 0x9C,0x9E,0x9F,0xA0,0xA2,0xA3,0xA5,0xA7, 0xA9,0xAA,0xAB,0xAD,0xAE,0xB0,0xB1,0xB2, 0xB4,0xB5,0xB6,0xB8,0xB9,0xBA,0xBC,0xBD, 0xBE,0xC0,0xC1,0xC2,0xC4,0xC5,0xC6,0xC8, 0xC9,0xCA,0xCC,0xCD,0xCE,0xD0,0xD1,0xD2, 0xD4,0xD5,0xD6,0xD7,0xD9,0xDA,0xDB,0xDD, 0xDE,0xDF,0xE1,0xE2,0xE3,0xE4,0xE6,0xE7, 0xE8,0xE9,0xEB,0xEC,0xED,0xEF,0xF0,0xF1, 0xF2,0xF4,0xF5,0xF6,0xF7,0xF9,0xFA,0xFB, 0xFC,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }, { 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,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x01,0x02,0x03,0x04,0x05,0x07,0x08, 0x0A,0x0C,0x0E,0x10,0x12,0x14,0x16,0x18, 0x1A,0x1C,0x1E,0x20,0x22,0x24,0x26,0x28, 0x2A,0x2B,0x2D,0x2F,0x31,0x33,0x34,0x36, 0x38,0x39,0x3B,0x3D,0x3E,0x40,0x42,0x43, 0x45,0x47,0x48,0x4A,0x4B,0x4D,0x4F,0x50, 0x52,0x53,0x55,0x56,0x58,0x59,0x5B,0x5D, 0x5E,0x60,0x61,0x63,0x64,0x66,0x67,0x69, 0x6A,0x6B,0x6D,0x6E,0x70,0x71,0x73,0x74, 0x76,0x77,0x79,0x7A,0x7B,0x7D,0x7E,0x80, 0x81,0x83,0x84,0x85,0x87,0x88,0x8A,0x8B, 0x8C,0x8E,0x8F,0x90,0x92,0x93,0x95,0x96, 0x97,0x99,0x9A,0x9B,0x9D,0x9E,0x9F,0xA1, 0xA2,0xA3,0xA5,0xA6,0xA7,0xA9,0xAA,0xAB, 0xAD,0xAE,0xAF,0xB1,0xB2,0xB3,0xB5,0xB6, 0xB7,0xB9,0xBA,0xBB,0xBD,0xBE,0xBF,0xC0, 0xC2,0xC3,0xC4,0xC6,0xC7,0xC8,0xC9,0xCB, 0xCC,0xCD,0xCF,0xD0,0xD1,0xD2,0xD4,0xD5, 0xD6,0xD7,0xD9,0xDA,0xDB,0xDC,0xDE,0xDF, 0xE0,0xE1,0xE3,0xE4,0xE5,0xE6,0xE8,0xE9, 0xEA,0xEB,0xED,0xEE,0xEF,0xF0,0xF2,0xF3, 0xF4,0xF5,0xF6,0xF8,0xF9,0xFA,0xFB,0xFD, 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }, { 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,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x04, 0x05,0x07,0x08,0x0A,0x0C,0x0E,0x10,0x12, 0x14,0x16,0x18,0x1A,0x1C,0x1E,0x20,0x22, 0x24,0x26,0x28,0x2A,0x2B,0x2D,0x2F,0x31, 0x33,0x34,0x36,0x38,0x39,0x3B,0x3D,0x3E, 0x40,0x42,0x43,0x45,0x47,0x48,0x4A,0x4B, 0x4D,0x4F,0x50,0x52,0x53,0x55,0x56,0x58, 0x59,0x5B,0x5D,0x5E,0x60,0x61,0x63,0x64, 0x66,0x67,0x69,0x6A,0x6B,0x6D,0x6E,0x70, 0x71,0x73,0x74,0x76,0x77,0x79,0x7A,0x7B, 0x7D,0x7E,0x80,0x81,0x83,0x84,0x85,0x87, 0x88,0x8A,0x8B,0x8C,0x8E,0x8F,0x90,0x92, 0x93,0x95,0x96,0x97,0x99,0x9A,0x9B,0x9D, 0x9E,0x9F,0xA1,0xA2,0xA3,0xA5,0xA6,0xA7, 0xA9,0xAA,0xAB,0xAD,0xAE,0xAF,0xB1,0xB2, 0xB3,0xB5,0xB6,0xB7,0xB9,0xBA,0xBB,0xBD, 0xBE,0xBF,0xC0,0xC2,0xC3,0xC4,0xC6,0xC7, 0xC8,0xC9,0xCB,0xCC,0xCD,0xCF,0xD0,0xD1, 0xD2,0xD4,0xD5,0xD6,0xD7,0xD9,0xDA,0xDB, 0xDC,0xDE,0xDF,0xE0,0xE1,0xE3,0xE4,0xE5, 0xE6,0xE8,0xE9,0xEA,0xEB,0xED,0xEE,0xEF, 0xF0,0xF2,0xF3,0xF4,0xF5,0xF6,0xF8,0xF9, 0xFA,0xFB,0xFD,0xFE,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }, { 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,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01, 0x02,0x03,0x04,0x05,0x07,0x08,0x0A,0x0C, 0x0E,0x10,0x12,0x14,0x16,0x16,0x18,0x1A, 0x1C,0x1E,0x20,0x22,0x24,0x26,0x28,0x2A, 0x2B,0x2D,0x2F,0x31,0x33,0x34,0x36,0x38, 0x39,0x3B,0x3D,0x3E,0x40,0x42,0x43,0x45, 0x47,0x48,0x4A,0x4B,0x4D,0x4F,0x50,0x52, 0x53,0x55,0x56,0x58,0x59,0x5B,0x5D,0x5E, 0x60,0x61,0x63,0x64,0x66,0x67,0x69,0x6A, 0x6B,0x6D,0x6E,0x70,0x71,0x73,0x74,0x76, 0x77,0x79,0x7A,0x7B,0x7D,0x7E,0x80,0x81, 0x83,0x84,0x85,0x87,0x88,0x8A,0x8B,0x8C, 0x8E,0x8F,0x90,0x92,0x93,0x95,0x96,0x97, 0x99,0x9A,0x9B,0x9D,0x9E,0x9F,0xA1,0xA3, 0xA5,0xA6,0xA7,0xA9,0xAA,0xAB,0xAD,0xAE, 0xAF,0xB1,0xB2,0xB3,0xB5,0xB6,0xB7,0xB9, 0xBA,0xBB,0xBD,0xBE,0xBF,0xC0,0xC2,0xC3, 0xC4,0xC6,0xC7,0xC8,0xC9,0xCB,0xCC,0xCD, 0xCF,0xD0,0xD1,0xD2,0xD4,0xD5,0xD6,0xD7, 0xD9,0xDA,0xDB,0xDC,0xDE,0xDF,0xE0,0xE1, 0xE3,0xE4,0xE5,0xE6,0xE8,0xE9,0xEA,0xEB, 0xED,0xEE,0xEF,0xF0,0xF2,0xF3,0xF4,0xF5, 0xF6,0xF8,0xF9,0xFA,0xFB,0xFD,0xFE,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }, { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x03, 0x04,0x05,0x07,0x08,0x0A,0x0C,0x0E,0x10, 0x12,0x14,0x16,0x18,0x1A,0x1C,0x1E,0x20, 0x22,0x24,0x26,0x27,0x29,0x2B,0x2C,0x2E, 0x30,0x31,0x33,0x35,0x36,0x38,0x39,0x3B, 0x3C,0x3E,0x40,0x41,0x43,0x44,0x45,0x47, 0x48,0x4B,0x4D,0x4E,0x50,0x51,0x52,0x54, 0x55,0x56,0x58,0x59,0x5B,0x5C,0x5D,0x5F, 0x60,0x61,0x63,0x64,0x65,0x67,0x68,0x69, 0x6A,0x6C,0x6D,0x6E,0x70,0x71,0x72,0x73, 0x75,0x76,0x77,0x78,0x7A,0x7B,0x7C,0x7D, 0x7E,0x80,0x81,0x82,0x83,0x85,0x86,0x87, 0x88,0x89,0x8A,0x8C,0x8D,0x8E,0x8F,0x90, 0x92,0x93,0x94,0x95,0x96,0x96,0x97,0x99, 0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xA1,0xA2, 0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xAA,0xAB, 0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3, 0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC, 0xBD,0xBE,0xBF,0xC0,0xC2,0xC3,0xC4,0xC5, 0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD, 0xCE,0xCF,0xD0,0xD1,0xD2,0xD4,0xD5,0xD6, 0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE, 0xDF,0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6, 0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE, 0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6, 0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE, 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }, { 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,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02, 0x03,0x04,0x05,0x06,0x07,0x09,0x0B,0x0C, 0x0E,0x10,0x12,0x14,0x16,0x18,0x1A,0x1C, 0x1E,0x20,0x21,0x23,0x25,0x27,0x28,0x2A, 0x2C,0x2D,0x2F,0x31,0x32,0x34,0x36,0x37, 0x39,0x3B,0x3C,0x3E,0x3F,0x41,0x42,0x44, 0x45,0x47,0x48,0x4A,0x4B,0x4D,0x4E,0x50, 0x51,0x53,0x54,0x56,0x57,0x59,0x5A,0x5C, 0x5D,0x5F,0x60,0x61,0x63,0x64,0x66,0x67, 0x68,0x6A,0x6B,0x6D,0x6E,0x6F,0x71,0x72, 0x73,0x75,0x76,0x78,0x79,0x7A,0x7C,0x7D, 0x7E,0x80,0x81,0x82,0x84,0x85,0x86,0x88, 0x89,0x8A,0x8B,0x8D,0x8E,0x8F,0x91,0x92, 0x93,0x95,0x96,0x97,0x98,0x9A,0x9B,0x9C, 0x9E,0x9F,0xA0,0xA1,0xA3,0xA4,0xA5,0xA6, 0xA8,0xA9,0xAA,0xAB,0xAD,0xAE,0xAF,0xB0, 0xB2,0xB3,0xB4,0xB5,0xB7,0xB8,0xB9,0xBA, 0xBC,0xBD,0xBE,0xBF,0xC0,0xC2,0xC3,0xC4, 0xC5,0xC7,0xC8,0xC9,0xCA,0xCB,0xCD,0xCE, 0xCF,0xD0,0xD1,0xD3,0xD4,0xD5,0xD6,0xD7, 0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xE0,0xE1, 0xE2,0xE3,0xE4,0xE6,0xE7,0xE8,0xE9,0xEA, 0xEB,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3, 0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFC,0xFD, 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, } }; //// profile array //// typedef struct { int productID; // USB PID char productName[50]; // ESCI/2 procduct name char deviceID[50]; // device ID (same as bonjour mdl name) int lutID; // look up table no }epsonds_profile_map; const epsonds_profile_map epsonds_models_predefined[] = { {0x0145, "DS-5500","DS-5500", 7}, {0x0145, "DS-6500","DS-6500", 7}, {0x0145, "DS-7500","DS-7500", 7}, {0x0146, "DS-50000","DS-50000", 7}, {0x0146, "DS-60000","DS-60000", 7}, {0x0146, "DS-70000","DS-70000", 7}, {0x014C, "DS-510","DS-510", 7}, {0x0150, "DS-560","DS-560", 7}, {0x0152, "DS-40","DS-40", 7}, {0x014D, "DS-760","DS-760", 7}, {0x014D, "DS-860","DS-860", 7}, {0x0154, "DS-520","DS-520", 7}, {0x08BC, "PID 08BC","PX-M7050 Series", 7}, {0x08BC, "PID 08BC","WF-8510 Series", 7}, {0x08BC, "PID 08BC","WF-8590 Series", 7}, {0x08CC, "PID 08CC","PX-M7050FX Series", 7}, {0x08CC, "PID 08CC","WF-R8590 Series", 7}, {0x0165, "DS-410","DS-410", 7}, {0x016C, "ES-50","ES-50", 6}, {0x0160, "DS-70","DS-70", 6}, {0x016D, "ES-55R","ES-55R", 6}, {0x018C, "RR-60","RR-60", 6}, {0x016E, "ES-60W","ES-60W", 6}, {0x0166, "DS-80W","DS-80W", 6}, {0x016F, "ES-65WR","ES-65WR", 6}, {0x018B, "RR-70W","RR-70W", 6}, {0x016E, "ES-60WW","ES-60WW", 6}, {0x016E, "ES-60WB","ES-60WB", 6}, {0x015C, "DS-1630","DS-1630", 4}, {0x015D, "DS-1610","DS-1610", 4}, {0x015E, "DS-1660W","DS-1660W", 4}, {0x0159, "DS-310","DS-310", 5}, {0x0159, "ES-200","ES-200", 5}, {0x0162, "DS-320","DS-320", 5}, {0x015A, "DS-360W","DS-360W", 5}, {0x015A, "ES-300W","ES-300W", 5}, {0x0177, "ES-300WR","ES-300WR", 5}, {0x0181, "ES-400II","ES-400II", 2}, {0x0183, "DS-535II","DS-535II", 2}, {0x0184, "DS-531","DS-531", 2}, {0x0182, "DS-530II","DS-530II", 2}, {0x0185, "ES-500WII","ES-500WII", 2}, {0x0188, "DS-571W","DS-571W", 2}, {0x0187, "DS-575WII","DS-575WII", 2}, {0x0186, "DS-570WII","DS-570WII", 2}, {0x017F, "ES-580W","ES-580W", 2}, {0x0180, "RR-600W","RR-600W", 2}, {0x0167, "DS-535","DS-535", 2}, {0x017A, "DS-535H","DS-535H", 2}, {0x0156, "ES-400","ES-400", 2}, {0x0155, "DS-530","DS-530", 2}, {0x016B, "FF-680W","FF-680W", 2}, {0x0157, "DS-570W","DS-570W", 2}, {0x0157, "ES-500W","ES-500W", 2}, {0x0169, "DS-575W","DS-575W", 2}, {0x0176, "ES-500WR","ES-500WR", 2}, {0x114E, "PID 114E","EW-052A Series", 7}, {0x114E, "PID 114E","XP-2100 Series", 7}, {0x1135, "PID 1135","ET-2700 Series", 7}, {0x1135, "PID 1135","L4150 Series", 7}, {0x114A, "PID 114A","ET-M2140 Series", 7}, {0x114A, "PID 114A","M2140 Series", 7}, {0x114F, "PID 114F","ET-M3140 Series", 7}, {0x114F, "PID 114F","M3140 Series", 7}, {0x1143, "PID 1143","L3150 Series", 7}, {0x1143, "PID 1143","ET-2710 Series", 7}, {0x118A, "PID 118A","ET-2810 Series", 7}, {0x118A, "PID 118A","L3250 Series", 7}, {0x119B, "PID 119B","XP-2150 Series", 7}, {0x11B1, "PID 11B1","XP-2200 Series", 7}, {0x0193, "ES-C220","ES-C220", 5}, {0x018F, "DS-C330","DS-C330", 5}, {0x0191, "DS-C490","DS-C490", 5}, {0x00, "","", 0x00 } }; typedef struct { epsonds_profile_map *array; int used; int size; }epsonds_profile_map_array; static epsonds_profile_map_array stProfileMapArray; static void insert_profile_map(epsonds_profile_map_array *a, epsonds_profile_map element); static void init_profile_maps(epsonds_profile_map_array *a, size_t initialSize) { a->array = malloc(initialSize * sizeof(epsonds_profile_map)); a->used = 0; a->size = initialSize; for (int i = 0; epsonds_models_predefined[i].productID != 0; i++) { //DBG(6, "epsonds_models_predefined[i].productID = %x\n", epsonds_models_predefined[i].productID ); insert_profile_map(a, epsonds_models_predefined[i]); } } static void insert_profile_map(epsonds_profile_map_array *a, epsonds_profile_map element) { if (a->used == a->size) { a->size *= 2; a->array = realloc(a->array, a->size * sizeof(epsonds_profile_map)); } a->array[a->used++] = element; } static void free_profile_maps(epsonds_profile_map_array *a) { free(a->array); a->array = NULL; a->used = a->size = 0; } ///////////////////////// struct mode_param mode_params[] = { {0, 0x00, 0x30, 1}, {0, 0x00, 0x30, 8}, {1, 0x02, 0x00, 8}, {0, 0x00, 0x30, 1} }; static SANE_String_Const mode_list[] = { SANE_VALUE_SCAN_MODE_LINEART, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_COLOR, NULL }; /* Define the different scan sources */ #define STRING_FLATBED SANE_I18N("Flatbed") #define STRING_ADFFRONT SANE_I18N("ADF Front") #define STRING_ADFDUPLEX SANE_I18N("ADF Duplex") /* order will be fixed: fb, adf, tpu */ SANE_String_Const source_list[] = { NULL, NULL, NULL, NULL }; /* * List of pointers to devices - will be dynamically allocated depending * on the number of devices found. */ static const SANE_Device **devlist; /* Some utility functions */ static size_t max_string_size(const SANE_String_Const strings[]) { size_t size, max_size = 0; int i; for (i = 0; strings[i]; i++) { size = strlen(strings[i]) + 1; if (size > max_size) max_size = size; } return max_size; } static SANE_Status attach_one_usb(SANE_String_Const devname); static SANE_Status attach_one_net(SANE_String_Const devname); static SANE_Status acquire_jpeg_data(epsonds_scanner* s); static SANE_Status acquire_and_decode_jpeg_data(epsonds_scanner* s); static SANE_Status acquire_raw_data(epsonds_scanner* s); static void print_params(const SANE_Parameters params) { DBG(6, "params.format = %d\n", params.format); DBG(6, "params.last_frame = %d\n", params.last_frame); DBG(6, "params.bytes_per_line = %d\n", params.bytes_per_line); DBG(6, "params.pixels_per_line = %d\n", params.pixels_per_line); DBG(6, "params.lines = %d\n", params.lines); DBG(6, "params.depth = %d\n", params.depth); } static void close_scanner(epsonds_scanner *s) { DBG(7, "%s: fd = %d\n", __func__, s->fd); if (s->scanning) { sane_cancel(s); } if (s->fd == -1) goto free; if (s->locked) { DBG(7, " unlocking scanner\n"); esci2_fin(s); } if (s->hw->connection == SANE_EPSONDS_NET) { epsonds_net_unlock(s); sanei_tcp_close(s->fd); } else if (s->hw->connection == SANE_EPSONDS_USB) { sanei_usb_close(s->fd); } free: free(s->front.ring); free(s->back.ring); free(s->line_buffer); free(s); DBG(7, "%s: ZZZ\n", __func__); } static SANE_Status open_scanner(epsonds_scanner *s) { SANE_Status status = SANE_STATUS_INVAL; DBG(7, "%s: %s\n", __func__, s->hw->sane.name); if (s->fd != -1) { DBG(5, "scanner is already open: fd = %d\n", s->fd); return SANE_STATUS_GOOD; /* no need to open the scanner */ } if (s->hw->connection == SANE_EPSONDS_NET) { unsigned char buf[5]; /* device name has the form net:ipaddr */ status = sanei_tcp_open(&s->hw->name[4], 1865, &s->fd); if (status == SANE_STATUS_GOOD) { ssize_t read; struct timeval tv; tv.tv_sec = 5; tv.tv_usec = 0; setsockopt(s->fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)); s->netlen = 0; DBG(32, "awaiting welcome message\n"); /* the scanner sends a kind of welcome msg */ // XXX check command type, answer to connect is 0x80 read = eds_recv(s, buf, 5, &status); if (read != 5) { sanei_tcp_close(s->fd); s->fd = -1; return SANE_STATUS_IO_ERROR; } DBG(32, "welcome message received, locking the scanner...\n"); /* lock the scanner for use by sane */ status = epsonds_net_lock(s); if (status != SANE_STATUS_GOOD) { DBG(1, "%s cannot lock scanner: %s\n", s->hw->sane.name, sane_strstatus(status)); sanei_tcp_close(s->fd); s->fd = -1; return status; } DBG(32, "scanner locked\n"); } } else if (s->hw->connection == SANE_EPSONDS_USB) { status = sanei_usb_open(s->hw->sane.name, &s->fd); if (status == SANE_STATUS_GOOD) { sanei_usb_set_timeout(USB_TIMEOUT); } } else { DBG(1, "unknown connection type: %d\n", s->hw->connection); } if (status == SANE_STATUS_ACCESS_DENIED) { DBG(1, "please check that you have permissions on the device.\n"); DBG(1, "if this is a multi-function device with a printer,\n"); DBG(1, "disable any conflicting driver (like usblp).\n"); } if (status != SANE_STATUS_GOOD) DBG(1, "%s open failed: %s\n", s->hw->sane.name, sane_strstatus(status)); else DBG(5, " opened correctly\n"); return status; } static int num_devices; /* number of scanners attached to backend */ static epsonds_device *first_dev; /* first EPSON scanner in list */ static struct epsonds_scanner * scanner_create(struct epsonds_device *dev, SANE_Status *status) { struct epsonds_scanner *s; s = malloc(sizeof(struct epsonds_scanner)); if (s == NULL) { *status = SANE_STATUS_NO_MEM; return NULL; } /* clear verything */ memset(s, 0x00, sizeof(struct epsonds_scanner)); s->fd = -1; s->hw = dev; return s; } static struct epsonds_scanner * device_detect(const char *name, int type, SANE_Status *status) { struct epsonds_scanner *s; struct epsonds_device *dev; DBG(1, "%s, %s, type: %d\n", __func__, name, type); /* try to find the device in our list */ for (dev = first_dev; dev; dev = dev->next) { if (strcmp(dev->sane.name, name) == 0) { DBG(1, " found cached device\n"); // the device might have been just probed, sleep a bit. if (dev->connection == SANE_EPSONDS_NET) { sleep(1); } return scanner_create(dev, status); } } /* not found, create new if valid */ if (type == SANE_EPSONDS_NODEV) { *status = SANE_STATUS_INVAL; return NULL; } /* alloc and clear our device structure */ dev = malloc(sizeof(*dev)); if (!dev) { *status = SANE_STATUS_NO_MEM; return NULL; } memset(dev, 0x00, sizeof(struct epsonds_device)); s = scanner_create(dev, status); if (s == NULL) return NULL; dev->connection = type; dev->model = strdup("(undetermined)"); dev->name = strdup(name); dev->sane.name = dev->name; dev->sane.vendor = "Epson"; dev->sane.model = dev->model; dev->sane.type = "ESC/I-2"; *status = open_scanner(s); if (*status != SANE_STATUS_GOOD) { free(s); return NULL; } eds_dev_init(dev); /* lock scanner */ *status = eds_lock(s); if (*status != SANE_STATUS_GOOD) { goto close; } /* discover capabilities */ *status = esci2_info(s); if (*status != SANE_STATUS_GOOD) goto close; *status = esci2_capa(s); if (*status != SANE_STATUS_GOOD) goto close; *status = esci2_resa(s); if (*status != SANE_STATUS_GOOD) goto close; // assume 1 and 8 bit are always supported eds_add_depth(s->hw, 1); eds_add_depth(s->hw, 8); // setup area according to available options if (s->hw->has_fb) { dev->x_range = &dev->fbf_x_range; dev->y_range = &dev->fbf_y_range; dev->alignment = dev->fbf_alignment; } else if (s->hw->has_adf) { dev->x_range = &dev->adf_x_range; dev->y_range = &dev->adf_y_range; dev->alignment = dev->adf_alignment; } else { DBG(0, "unable to lay on the flatbed or feed the feeder. is that a scanner??\n"); } *status = eds_dev_post_init(dev); if (*status != SANE_STATUS_GOOD) goto close; DBG(1, "scanner model: %s\n", dev->model); s->hw->lut_id = 0; for (int i = 0; i < stProfileMapArray.used; i++) { epsonds_profile_map* map = &stProfileMapArray.array[i]; if (strcmp(map->productName, dev->model) == 0) { {//Convert to user friendly model name free(s->hw->model); s->hw->model = strdup(map->deviceID); s->hw->sane.model = s->hw->model; } {// set lutid s->hw->lut_id = map->lutID; } break; } } DBG(1, "scanner lut_id: %d\n", s->hw->lut_id); num_devices++; dev->next = first_dev; first_dev = dev; return s; close: DBG(1, " failed\n"); close_scanner(s); return NULL; } static SANE_Status attach(const char *name, int type) { SANE_Status status; epsonds_scanner * s; DBG(7, "%s: devname = %s, type = %d\n", __func__, name, type); s = device_detect(name, type, &status); if (s == NULL) return status; close_scanner(s); return status; } SANE_Status attach_one_usb(const char *dev) { DBG(7, "%s: dev = %s\n", __func__, dev); return attach(dev, SANE_EPSONDS_USB); } static SANE_Status attach_one_net(const char *dev) { char name[39 + 4]; DBG(7, "%s: dev = %s\n", __func__, dev); strcpy(name, "net:"); strcat(name, dev); return attach(name, SANE_EPSONDS_NET); } static void found_net_device(const char* device_name, const char* ip) { DBG(7, "Found %s: ip = %s\n", device_name, ip); int foundSupportedDevice = 0; // search models for (int i = 0; i < stProfileMapArray.used; i++) { if (strcmp(stProfileMapArray.array[i].deviceID, device_name) == 0) { foundSupportedDevice = 1; break; } } if (foundSupportedDevice) { char name[39 + 4]; strcpy(name, "net:"); strncat(name, ip, 39); int foundCache = 0; // search cache and prents duplicated model for (epsonds_device* dev = first_dev; dev; dev = dev->next) { if (strcmp(dev->sane.name, name) == 0) { foundCache = 1; } } if (foundCache == 0) { attach(name, SANE_EPSONDS_NET); } } } static void splitProfileName(const char* input, int* outProductID, char *outProductName, char* outDeviceID, int* outLutID) { char target[1024]; strncpy(target, input, 1023); strtok(target, ":");//profile //productID char* pid = strtok(NULL, ","); sscanf(pid, "%x", (unsigned int*)outProductID); //productName char* productName = strtok(NULL, ","); strncpy(outProductName, productName, 49); //deviceID char* deviceID = strtok(NULL, ","); strncpy(outDeviceID, deviceID, 49); //lutID char* lutID = strtok(NULL, ","); sscanf(lutID, "%d", outLutID); } static SANE_Status attach_one_config(SANEI_Config __sane_unused__ *config, const char *line, void *data) { int vendor, product; SANE_Bool local_only = *(SANE_Bool*) data; int len = strlen(line); DBG(7, "%s: len = %d, line = %s\n", __func__, len, line); if (strncmp(line, "profile", 7) == 0 ) { DBG(7, " found profile device profile\n"); epsonds_profile_map profle_map; splitProfileName(line, &profle_map.productID, profle_map.productName, profle_map.deviceID, &profle_map.lutID); DBG(7, "Found profile : %x %s %s %d\n", profle_map.productID, profle_map.productName, profle_map.deviceID, profle_map.lutID); insert_profile_map(&stProfileMapArray, profle_map); }else if (sscanf(line, "usb %i %i", &vendor, &product) == 2) { DBG(7, " user configured device\n"); if (vendor != SANE_EPSONDS_VENDOR_ID) return SANE_STATUS_INVAL; /* this is not an Epson device */ sanei_usb_attach_matching_devices(line, attach_one_usb); } else if (strncmp(line, "usb", 3) == 0 && len == 3) { DBG(7, " probing usb devices\n"); for (int i = 0; i < stProfileMapArray.used; i++) { int usbPid = stProfileMapArray.array[i].productID; sanei_usb_find_devices(SANE_EPSONDS_VENDOR_ID, usbPid, attach_one_usb); } } else if (strncmp(line, "net", 3) == 0) { if (!local_only) { /* remove the "net" sub string */ const char *name = sanei_config_skip_whitespace(line + 3); if (strncmp(name, "autodiscovery", 13) == 0) { #if WITH_AVAHI epsonds_searchDevices(found_net_device); #else // currently does not support //e2_network_discovery(); #endif } else attach_one_net(name); } } else { DBG(0, "unable to parse config line: %s\n", line); } return SANE_STATUS_GOOD; } static void free_devices(void) { epsonds_device *dev, *next; for (dev = first_dev; dev; dev = next) { next = dev->next; free(dev->name); free(dev->model); free(dev); } free(devlist); first_dev = NULL; } static void probe_devices(SANE_Bool local_only) { DBG(5, "%s\n", __func__); free_devices(); sanei_configure_attach(EPSONDS_CONFIG_FILE, NULL, attach_one_config, &local_only); } /**** SANE API ****/ SANE_Status sane_init(SANE_Int *version_code, SANE_Auth_Callback __sane_unused__ authorize) { DBG_INIT(); init_profile_maps(&stProfileMapArray, 100); DBG(2, "%s: " PACKAGE " " VERSION "\n", __func__); DBG(1, "epsonds backend, version %i.%i.%i\n", EPSONDS_VERSION, EPSONDS_REVISION, EPSONDS_BUILD); if (version_code != NULL) *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, EPSONDS_BUILD); sanei_usb_init(); return SANE_STATUS_GOOD; } void sane_exit(void) { DBG(5, "** %s\n", __func__); free_profile_maps(&stProfileMapArray); free_devices(); } SANE_Status sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only) { int i; epsonds_device *dev; DBG(5, "** %s local_only = %d \n", __func__, local_only); probe_devices(local_only); devlist = malloc((num_devices + 1) * sizeof(devlist[0])); if (!devlist) { DBG(1, "out of memory (line %d)\n", __LINE__); return SANE_STATUS_NO_MEM; } DBG(5, "%s - results:\n", __func__); for (i = 0, dev = first_dev; i < num_devices && dev; dev = dev->next, i++) { DBG(1, " %d (%d): %s\n", i, dev->connection, dev->model); devlist[i] = &dev->sane; } devlist[i] = NULL; *device_list = devlist; return SANE_STATUS_GOOD; } static SANE_Status init_options(epsonds_scanner *s) { DBG(5, "init_options\n"); int i; for (i = 0; i < NUM_OPTIONS; i++) { s->opt[i].size = sizeof(SANE_Word); s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* "Scan Mode" group: */ s->opt[OPT_STANDARD_GROUP].name = SANE_NAME_STANDARD; s->opt[OPT_STANDARD_GROUP].title = SANE_TITLE_STANDARD; s->opt[OPT_STANDARD_GROUP].desc = SANE_DESC_STANDARD; s->opt[OPT_STANDARD_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_STANDARD_GROUP].cap = 0; /* scan mode */ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; 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 = max_string_size(mode_list); s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_MODE].constraint.string_list = mode_list; s->val[OPT_MODE].w = 0; /* Lineart */ /* bit depth */ s->opt[OPT_DEPTH].name = SANE_NAME_BIT_DEPTH; s->opt[OPT_DEPTH].title = SANE_TITLE_BIT_DEPTH; s->opt[OPT_DEPTH].desc = SANE_DESC_BIT_DEPTH; s->opt[OPT_DEPTH].type = SANE_TYPE_INT; s->opt[OPT_DEPTH].unit = SANE_UNIT_BIT; s->opt[OPT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_DEPTH].constraint.word_list = s->hw->depth_list; s->val[OPT_DEPTH].w = s->hw->depth_list[1]; /* the first "real" element is the default */ /* default is Lineart, disable depth selection */ s->opt[OPT_DEPTH].cap |= SANE_CAP_INACTIVE; /* resolution */ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; /* range */ if (s->hw->dpi_range.quant) { s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_RESOLUTION].constraint.range = &s->hw->dpi_range; s->val[OPT_RESOLUTION].w = s->hw->dpi_range.min; } else { /* list */ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_RESOLUTION].constraint.word_list = s->hw->res_list; s->val[OPT_RESOLUTION].w = s->hw->res_list[1]; } /* "Geometry" group: */ s->opt[OPT_GEOMETRY_GROUP].name = ""; s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N("Geometry"); s->opt[OPT_GEOMETRY_GROUP].desc = ""; s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; /* top-left x */ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; s->opt[OPT_TL_X].unit = SANE_UNIT_MM; s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_X].constraint.range = s->hw->x_range; s->val[OPT_TL_X].w = 0; /* top-left y */ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_Y].constraint.range = s->hw->y_range; s->val[OPT_TL_Y].w = 0; /* bottom-right x */ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; s->opt[OPT_BR_X].unit = SANE_UNIT_MM; s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_X].constraint.range = s->hw->x_range; s->val[OPT_BR_X].w = s->hw->x_range->max; /* bottom-right y */ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_Y].constraint.range = s->hw->y_range; s->val[OPT_BR_Y].w = s->hw->y_range->max; /* "Optional equipment" group: */ s->opt[OPT_EQU_GROUP].name = ""; s->opt[OPT_EQU_GROUP].title = SANE_I18N("Optional equipment"); s->opt[OPT_EQU_GROUP].desc = ""; s->opt[OPT_EQU_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_EQU_GROUP].cap = SANE_CAP_ADVANCED; /* source */ s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE; s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE; s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE; s->opt[OPT_SOURCE].type = SANE_TYPE_STRING; s->opt[OPT_SOURCE].size = max_string_size(source_list); s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_SOURCE].constraint.string_list = source_list; s->val[OPT_SOURCE].w = 0; s->opt[OPT_EJECT].name = "eject"; s->opt[OPT_EJECT].title = SANE_I18N("Eject"); s->opt[OPT_EJECT].desc = SANE_I18N("Eject the sheet in the ADF"); s->opt[OPT_EJECT].type = SANE_TYPE_BUTTON; if (!s->hw->adf_has_eject) s->opt[OPT_EJECT].cap |= SANE_CAP_INACTIVE; s->opt[OPT_LOAD].name = "load"; s->opt[OPT_LOAD].title = SANE_I18N("Load"); s->opt[OPT_LOAD].desc = SANE_I18N("Load a sheet in the ADF"); s->opt[OPT_LOAD].type = SANE_TYPE_BUTTON; if (!s->hw->adf_has_load) s->opt[OPT_LOAD].cap |= SANE_CAP_INACTIVE; s->opt[OPT_ADF_SKEW].name = "adf-skew"; s->opt[OPT_ADF_SKEW].title = SANE_I18N("ADF Skew Correction"); s->opt[OPT_ADF_SKEW].desc = SANE_I18N("Enables ADF skew correction"); s->opt[OPT_ADF_SKEW].type = SANE_TYPE_BOOL; s->val[OPT_ADF_SKEW].w = 0; s->opt[OPT_ADF_CRP].name = "adf-crp"; s->opt[OPT_ADF_CRP].title = SANE_I18N("ADF CRP Correction"); s->opt[OPT_ADF_CRP].desc = SANE_I18N("Enables ADF auto cropping"); // s->opt[OPT_ADF_CRP].type = SANE_TYPE_BOOL; s->val[OPT_ADF_CRP].w = 0; if (!s->hw->adf_has_skew) { s->val[OPT_ADF_SKEW].w = 0; s->opt[OPT_ADF_SKEW].cap |= SANE_CAP_INACTIVE; } if(!s->hw->adf_has_crp) { s->val[OPT_ADF_CRP].w = 0; s->opt[OPT_ADF_CRP].cap |= SANE_CAP_INACTIVE; } return SANE_STATUS_GOOD; } SANE_Status sane_open(SANE_String_Const name, SANE_Handle *handle) { SANE_Status status; epsonds_scanner *s = NULL; DBG(7, "** %s: name = '%s'\n", __func__, name); /* probe if empty device name provided */ if (name[0] == '\0') { probe_devices(SANE_FALSE); if (first_dev == NULL) { DBG(1, "no devices detected\n"); return SANE_STATUS_INVAL; } s = device_detect(first_dev->sane.name, first_dev->connection, &status); if (s == NULL) { DBG(1, "cannot open a perfectly valid device (%s)," " please report to the authors\n", name); return SANE_STATUS_INVAL; } } else { if (strncmp(name, "net:", 4) == 0) { s = device_detect(name, SANE_EPSONDS_NET, &status); if (s == NULL) return status; } else if (strncmp(name, "libusb:", 7) == 0) { s = device_detect(name, SANE_EPSONDS_USB, &status); if (s == NULL) return status; } else { DBG(1, "invalid device name: %s\n", name); return SANE_STATUS_INVAL; } } /* s is always valid here */ DBG(5, "%s: handle obtained\n", __func__); init_options(s); *handle = (SANE_Handle)s; status = open_scanner(s); if (status != SANE_STATUS_GOOD) { free(s); return status; } /* lock scanner if required */ if (!s->locked) { status = eds_lock(s); } setvalue((SANE_Handle)s, OPT_MODE, (void*)SANE_VALUE_SCAN_MODE_COLOR, NULL); return status; } void sane_close(SANE_Handle handle) { epsonds_scanner *s = (epsonds_scanner *)handle; DBG(1, "** %s\n", __func__); close_scanner(s); } const SANE_Option_Descriptor * sane_get_option_descriptor(SANE_Handle handle, SANE_Int option) { epsonds_scanner *s = (epsonds_scanner *) handle; if (option < 0 || option >= NUM_OPTIONS) return NULL; return s->opt + option; } static const SANE_String_Const * search_string_list(const SANE_String_Const *list, SANE_String value) { while (*list != NULL && strcmp(value, *list) != 0) list++; return ((*list == NULL) ? NULL : list); } /* * Handles setting the source (flatbed, transparency adapter (TPU), * or auto document feeder (ADF)). * * For newer scanners it also sets the focus according to the * glass / TPU settings. */ static void change_source(epsonds_scanner *s, SANE_Int optindex, char *value) { int force_max = SANE_FALSE; DBG(1, "%s: optindex = %d, source = '%s'\n", __func__, optindex, value); s->val[OPT_SOURCE].w = optindex; /* if current selected area is the maximum available, * keep this setting on the new source. */ if (s->val[OPT_TL_X].w == s->hw->x_range->min && s->val[OPT_TL_Y].w == s->hw->y_range->min && s->val[OPT_BR_X].w == s->hw->x_range->max && s->val[OPT_BR_Y].w == s->hw->y_range->max) { force_max = SANE_TRUE; } if (strcmp(STRING_ADFFRONT, value) == 0 || strcmp(STRING_ADFDUPLEX, value) == 0) { s->hw->x_range = &s->hw->adf_x_range; s->hw->y_range = &s->hw->adf_y_range; s->hw->alignment = s->hw->adf_alignment; } else if (strcmp(TPU_STR, value) == 0) { s->hw->x_range = &s->hw->tpu_x_range; s->hw->y_range = &s->hw->tpu_y_range; } else { /* neither ADF nor TPU active, assume FB */ s->hw->x_range = &s->hw->fbf_x_range; s->hw->y_range = &s->hw->fbf_y_range; s->hw->alignment = s->hw->fbf_alignment; } s->opt[OPT_BR_X].constraint.range = s->hw->x_range; s->opt[OPT_BR_Y].constraint.range = s->hw->y_range; if (s->val[OPT_TL_X].w < s->hw->x_range->min || force_max) s->val[OPT_TL_X].w = s->hw->x_range->min; if (s->val[OPT_TL_Y].w < s->hw->y_range->min || force_max) s->val[OPT_TL_Y].w = s->hw->y_range->min; if (s->val[OPT_BR_X].w > s->hw->x_range->max || force_max) s->val[OPT_BR_X].w = s->hw->x_range->max; if (s->val[OPT_BR_Y].w > s->hw->y_range->max || force_max) s->val[OPT_BR_Y].w = s->hw->y_range->max; } static SANE_Status getvalue(SANE_Handle handle, SANE_Int option, void *value) { epsonds_scanner *s = (epsonds_scanner *)handle; SANE_Option_Descriptor *sopt = &(s->opt[option]); Option_Value *sval = &(s->val[option]); DBG(17, "%s: option = %d\n", __func__, option); switch (option) { case OPT_NUM_OPTS: case OPT_RESOLUTION: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_DEPTH: case OPT_ADF_SKEW: case OPT_ADF_CRP: *((SANE_Word *) value) = sval->w; break; case OPT_MODE: case OPT_SOURCE: strcpy((char *) value, sopt->constraint.string_list[sval->w]); break; default: return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } static SANE_Status setvalue(SANE_Handle handle, SANE_Int option, void *value, SANE_Int *info) { epsonds_scanner *s = (epsonds_scanner *) handle; SANE_Option_Descriptor *sopt = &(s->opt[option]); Option_Value *sval = &(s->val[option]); SANE_Status status; const SANE_String_Const *optval = NULL; int optindex = 0; SANE_Bool reload = SANE_FALSE; DBG(17, "** %s: option = %d, value = %p\n", __func__, option, value); status = sanei_constrain_value(sopt, value, info); if (status != SANE_STATUS_GOOD) return status; if (info && value && (*info & SANE_INFO_INEXACT) && sopt->type == SANE_TYPE_INT) DBG(17, " constrained val = %d\n", *(SANE_Word *) value); if (sopt->constraint_type == SANE_CONSTRAINT_STRING_LIST) { optval = search_string_list(sopt->constraint.string_list, (char *) value); if (optval == NULL) return SANE_STATUS_INVAL; optindex = optval - sopt->constraint.string_list; } /* block faulty frontends */ if (sopt->cap & SANE_CAP_INACTIVE) { DBG(1, " tried to modify a disabled parameter"); return SANE_STATUS_INVAL; } switch (option) { case OPT_ADF_SKEW: case OPT_RESOLUTION: case OPT_ADF_CRP: sval->w = *((SANE_Word *) value); reload = SANE_TRUE; break; case OPT_BR_X: case OPT_BR_Y: if (SANE_UNFIX(*((SANE_Word *) value)) == 0) { DBG(17, " invalid br-x or br-y\n"); return SANE_STATUS_INVAL; } // fall through case OPT_TL_X: case OPT_TL_Y: sval->w = *((SANE_Word *) value); if (NULL != info) *info |= SANE_INFO_RELOAD_PARAMS; if (option == OPT_BR_X) { DBG(17, "OPT_BR_X = %d\n", sval->w); } if (option == OPT_BR_Y) { DBG(17, "OPT_BR_Y = %d\n", sval->w); } if (option == OPT_TL_X) { DBG(17, "OPT_TL_X = %d\n", sval->w); } if (option == OPT_TL_Y) { DBG(17, "OPT_TL_Y = %d\n", sval->w); } // adf crop set to off s->val[OPT_ADF_CRP].w = 0; break; case OPT_SOURCE: change_source(s, optindex, (char *) value); reload = SANE_TRUE; break; case OPT_MODE: { DBG(17, " OPT_MODE = index %d\n", optindex); /* use JPEG mode if RAW is not available when bpp > 1 */ if (optindex > 0 && !s->hw->has_raw) { s->mode_jpeg = 1; } else { s->mode_jpeg = 0; } sval->w = optindex; /* if binary, then disable the bit depth selection */ if (optindex == 0) { s->opt[OPT_DEPTH].cap |= SANE_CAP_INACTIVE; } else { if (s->hw->depth_list[0] == 1) s->opt[OPT_DEPTH].cap |= SANE_CAP_INACTIVE; else { s->opt[OPT_DEPTH].cap &= ~SANE_CAP_INACTIVE; s->val[OPT_DEPTH].w = mode_params[optindex].depth; } } reload = SANE_TRUE; break; } case OPT_DEPTH: sval->w = *((SANE_Word *) value); mode_params[s->val[OPT_MODE].w].depth = sval->w; reload = SANE_TRUE; break; case OPT_LOAD: esci2_mech(s, "#ADFLOAD"); break; case OPT_EJECT: esci2_mech(s, "#ADFEJCT"); break; default: return SANE_STATUS_INVAL; } if (reload && info != NULL) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; } SANE_Status sane_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int *info) { DBG(17, "** %s: action = %x, option = %d\n", __func__, action, option); if (option < 0 || option >= NUM_OPTIONS) return SANE_STATUS_INVAL; if (info != NULL) *info = 0; switch (action) { case SANE_ACTION_GET_VALUE: return getvalue(handle, option, value); case SANE_ACTION_SET_VALUE: return setvalue(handle, option, value, info); default: return SANE_STATUS_INVAL; } return SANE_STATUS_INVAL; } static void setBit (SANE_Byte* bytes, SANE_Int bitIndex, SANE_Bool isTrue) { SANE_Int octet = bitIndex / 8; SANE_Byte bit = 7 - (bitIndex % 8); if (isTrue) { bytes[octet] |= (1 << bit); } else { bytes[octet] &= ~(1 << bit); } } static SANE_Bool getBit (SANE_Byte* bytes, SANE_Int bitIndex) { SANE_Int octet = bitIndex / 8; SANE_Byte mask = 1 << (7 - (bitIndex % 8)); if( bytes[octet] & mask ){ return SANE_TRUE; } return SANE_FALSE; } static void swapPixel1(SANE_Int x1, SANE_Int y1, SANE_Int x2, SANE_Int y2, SANE_Byte* bytes, SANE_Byte bitsPerSample, SANE_Int samplesPerPixel, SANE_Int bytesPerRow) { SANE_Int pixelBits = bitsPerSample * samplesPerPixel; SANE_Int widthBits = bytesPerRow * 8; SANE_Byte temp = getBit(bytes, widthBits * y1 + x1 * pixelBits); { SANE_Byte right = getBit(bytes, widthBits * y2 + x2 * pixelBits); setBit(bytes, widthBits * y1 + x1 * pixelBits, right); } setBit(bytes, widthBits * y2 + x2 * pixelBits, temp); } static void swapPixel8(SANE_Int x1, SANE_Int y1, SANE_Int x2, SANE_Int y2, SANE_Byte* bytes, SANE_Byte bitsPerSample, SANE_Int samplesPerPixel, SANE_Int bytesPerRow) { SANE_Int pixelBytes = samplesPerPixel * bitsPerSample / 8; for (SANE_Byte i = 0; i < pixelBytes; i++) { SANE_Byte temp = bytes[y1 * bytesPerRow + (pixelBytes * x1 + i)]; bytes[y1 * bytesPerRow + (pixelBytes * x1 + i)] = bytes[y2 * bytesPerRow + (pixelBytes * x2 + i)]; bytes[y2 * bytesPerRow + (pixelBytes * x2 + i)] = temp; } } static void swapPixel(SANE_Int x1, SANE_Int y1, SANE_Int x2, SANE_Int y2, SANE_Byte* bytes, SANE_Byte bitsPerSample, SANE_Int samplesPerPixel, SANE_Int bytesPerRow) { if (bitsPerSample == 1) { swapPixel1(x1, y1, x2, y2, bytes, bitsPerSample, samplesPerPixel, bytesPerRow); }else if(bitsPerSample == 8 || bitsPerSample == 16){ swapPixel8(x1, y1, x2, y2, bytes, bitsPerSample, samplesPerPixel, bytesPerRow); } } void upside_down_backside_image(epsonds_scanner *s) { // get all data from ring_buffer if (eds_ring_avail(&s->back) && (strcmp(s->hw->sane.model, (char*)"DS-1630") == 0 || strcmp(s->hw->sane.model, (char*)"DS-1610") == 0 || strcmp(s->hw->sane.model, (char*)"DS-1660W") == 0)) { SANE_Int bytesPerLine = s->params.bytes_per_line; SANE_Int imageSize = bytesPerLine * s->height_back; SANE_Byte* workBuffer = malloc(imageSize); // if there is not enough memory, do nothing. if (workBuffer) { eds_ring_read(&s->back, workBuffer, imageSize); SANE_Int samplesPerPxel = 3; if (s->params.format == SANE_FRAME_RGB) { samplesPerPxel = 3; } else if (s->params.format == SANE_FRAME_GRAY) { samplesPerPxel = 1; } SANE_Int half = (s->height_back / 2) - 1; if (half < 0) { half = 0; } if((s->height_back % 2) == 1) { SANE_Int ymid = ( (s->height_back - 1 ) / 2 ); for(SANE_Int x = 0;x < (s->width_back / 2); x++) { swapPixel(x, ymid, s->width_back - x - 1, ymid, workBuffer, s->params.depth, samplesPerPxel, s->params.bytes_per_line); } } if (s->height_back != 1) { for(SANE_Int x = 0; x < s->width_back; x++) { for(SANE_Int y = 0;y <= half; y++) { swapPixel(x, y, s->width_back - x - 1, s->height_back - y -1, workBuffer, s->params.depth, samplesPerPxel, s->params.bytes_per_line); } } } eds_ring_write(&s->back, workBuffer, imageSize); free(workBuffer); workBuffer = NULL; } } } SANE_Status get_next_image(epsonds_scanner *s) { SANE_Status status = SANE_STATUS_GOOD; if (s->acquirePage == 0 && s->current == &s->front) { DBG(20, "** %s: get_next_image\n", __func__); /*page info will be updatted by pen*/ s->width_back = 0; s->width_front = 0; s->height_back = 0; s->height_front = 0; if (s->mode_jpeg) { status = acquire_and_decode_jpeg_data(s); }else{ status = acquire_raw_data(s); } if (status != SANE_STATUS_GOOD) { eds_ring_flush(&s->front); eds_ring_flush(&s->back); eds_ring_destory(&s->front); eds_ring_destory(&s->back); } DBG(20," ringFront = %d ringBack = %d\n", eds_ring_avail(&s->front), eds_ring_avail(&s->back)); s->acquirePage = 1; } return status; } SANE_Status sane_get_parameters(SANE_Handle handle, SANE_Parameters *params) { epsonds_scanner *s = (epsonds_scanner *)handle; DBG(5, "** %s\n", __func__); if (params == NULL) DBG(1, "%s: params is NULL\n", __func__); /* * If sane_start was already called, then just retrieve the parameters * from the scanner data structure */ if (s->scanning) { DBG(5, "scan in progress, returning saved params structure\n"); } else { /* otherwise initialize the params structure */ eds_init_parameters(s); } SANE_Status status = SANE_STATUS_GOOD; status = get_next_image(s); // if size auto, update page size value if(s->val[OPT_ADF_CRP].w) { // frontside if (s->current == &s->front) { DBG(20, "front side \n"); if (s->width_front != 0 && s->height_front != 0) { if (s->params.format == SANE_FRAME_RGB) { s->params.bytes_per_line = s->width_front * 3; s->params.pixels_per_line = s->width_front; } if (s->params.format == SANE_FRAME_GRAY && s->params.depth == 8) { s->params.bytes_per_line = s->width_front; s->params.pixels_per_line = s->width_front; } if (s->params.format == SANE_FRAME_GRAY && s->params.depth == 1) { s->params.bytes_per_line = (s->width_front + 7)/8; s->params.pixels_per_line = s->width_front; } s->params.lines = s->height_front; } } // backside if (s->current == &s->back) { DBG(20, "back side \n"); if (s->width_back != 0 && s->height_back != 0) { if (s->params.format == SANE_FRAME_RGB) { s->params.bytes_per_line = s->width_back * 3; s->params.pixels_per_line = s->width_back; } if (s->params.format == SANE_FRAME_GRAY && s->params.depth == 8) { s->params.bytes_per_line = s->width_back; s->params.pixels_per_line = s->width_back; } if (s->params.format == SANE_FRAME_GRAY && s->params.depth == 1) { s->params.bytes_per_line = (s->width_back + 7)/8; s->params.pixels_per_line = s->width_back; } s->params.lines = s->height_back; } } } if (params != NULL) *params = s->params; print_params(s->params); DBG(20, "s->params.line = %d s->params.bytes_per_line = %d s->params.pixels_per_line = %d \n", s->params.lines, s->params.bytes_per_line , s->params.pixels_per_line ); return status; } typedef float ColorMatrix[3][3]; #define CCT_TABLE_SIZE 9 static int get_roundup_index(double frac[], int n) { int i, index = -1; double max_val = 0.0; for (i=0; i0) continue; if (min_val>frac[i]) { index = i; min_val = frac[i]; } } return index; } void ESCIRoundColorCorrectionMatrix(int mult, double org_cct[], int rnd_cct[]) { int i, j, index; double mult_cct[CCT_TABLE_SIZE], frac[CCT_TABLE_SIZE]; int sum[3]; int loop; for (i=0; imult) { index = get_rounddown_index(&frac[i*3], 3); if (index!=-1) { rnd_cct[i*3+index] --; mult_cct[i*3+index] = rnd_cct[i*3+index]; sum[i]--; } } } } while ((++loop<2)&&((sum[0]!=mult)||(sum[1]!=mult)||(sum[2]!=mult))); } /* * This function is part of the SANE API and gets called from the front end to * start the scan process. */ #define CMD_BUF_SIZE 1000 SANE_Status sane_start(SANE_Handle handle) { epsonds_scanner *s = (epsonds_scanner *)handle; char buf[65]; /* add one more byte to correct buffer overflow issue */ char cmd[CMD_BUF_SIZE]; /* take care not to overflow */ SANE_Status status = 0; s->pages++; DBG(5, "** %s, pages = %d, scanning = %d, backside = %d, front fill: %d, back fill: %d\n", __func__, s->pages, s->scanning, s->backside, eds_ring_avail(&s->front), eds_ring_avail(&s->back)); s->eof = 0; s->canceling = 0; s->acquirePage = 0; if ((s->pages % 2) == 1) { s->current = &s->front; } else if (eds_ring_avail(&s->back)) { DBG(5, "back side\n"); s->current = &s->back; } /* scan already in progress? (one pass adf) */ if (s->scanning || eds_ring_avail(&s->back) > 0) { DBG(5, " scan in progress, returning early\n"); return get_next_image(s); } if (strcmp(source_list[s->val[OPT_SOURCE].w], STRING_ADFFRONT) == 0 || strcmp(source_list[s->val[OPT_SOURCE].w], STRING_ADFDUPLEX) == 0) { if (s->scanEnd) { s->scanEnd = 0; return SANE_STATUS_NO_DOCS; } }else{ s->scanEnd = 0; } /* calc scanning parameters */ status = eds_init_parameters(s); if (status != SANE_STATUS_GOOD) { DBG(1, " parameters initialization failed\n"); return status; } /* allocate line buffer */ s->line_buffer = realloc(s->line_buffer, s->params.bytes_per_line); if (s->line_buffer == NULL) return SANE_STATUS_NO_MEM; /* transfer buffer size, bsz */ /* XXX read value from scanner */ s->bsz = (1048576 * 4); /* transfer buffer */ s->buf = realloc(s->buf, s->bsz); if (s->buf == NULL) return SANE_STATUS_NO_MEM; print_params(s->params); /* set scanning parameters */ s->isDuplexScan = 0; /* document source */ if (strcmp(source_list[s->val[OPT_SOURCE].w], STRING_ADFFRONT) == 0 || strcmp(source_list[s->val[OPT_SOURCE].w], STRING_ADFDUPLEX) == 0) { SANE_Int status = esci2_stat(s); if (status == SANE_STATUS_NO_DOCS) { return SANE_STATUS_NO_DOCS; } SANE_Int duplexMode = (strcmp(source_list[s->val[OPT_SOURCE].w], STRING_ADFDUPLEX) == 0); sprintf(buf, "#ADF%s%s%s", duplexMode ? "DPLX" : "", s->val[OPT_ADF_SKEW].w ? "SKEW" : "", s->val[OPT_ADF_CRP].w ? "CRP " : "" ); if (duplexMode) { s->isDuplexScan = 1; } s->isflatbedScan = 0; } else if (strcmp(source_list[s->val[OPT_SOURCE].w], STRING_FLATBED) == 0) { strcpy(buf, "#FB "); s->isflatbedScan = 1; } else { /* XXX */ } strcpy(cmd, buf); s->needToConvertBW = 0; if (s->params.format == SANE_FRAME_GRAY) { if (s->params.depth == 1 && s->hw->has_mono == 0) { sprintf(buf, "#COLM008"); s->needToConvertBW = 1; s->mode_jpeg = 1; }else { sprintf(buf, "#COLM%03d", s->params.depth); } } else if (s->params.format == SANE_FRAME_RGB) { sprintf(buf, "#COLC%03d", s->params.depth * 3); } strcat(cmd, buf); /* image transfer format */ if (!s->mode_jpeg) { if (s->params.depth > 1 || s->hw->has_raw) { strcat(cmd, "#FMTRAW "); } } else { strcat(cmd, "#FMTJPG #JPGd090"); } /* set GMM */ if (s->params.depth == 1) { sprintf(buf, "#GMMUG10"); } else { sprintf(buf, "#GMMUG18"); } strcat(cmd, buf); /* resolution (RSMi not always supported) */ if (strcmp(source_list[s->val[OPT_SOURCE].w], STRING_ADFFRONT) == 0 && s->val[OPT_RESOLUTION].w > 600) { DBG(0, "Automatic Document Feeder supported resolution of 600dpi or less. \n"); } else if (s->val[OPT_RESOLUTION].w > 999) { sprintf(buf, "#RSMi%07d#RSSi%07d", s->val[OPT_RESOLUTION].w, s->val[OPT_RESOLUTION].w); } else { sprintf(buf, "#RSMd%03d#RSSd%03d", s->val[OPT_RESOLUTION].w, s->val[OPT_RESOLUTION].w); } if (strcmp(source_list[s->val[OPT_SOURCE].w], STRING_ADFDUPLEX) == 0 && s->val[OPT_RESOLUTION].w > 600) { DBG(0, "Automatic Document Feeder supported resolution of 600dpi or less. \n"); } else if (s->val[OPT_RESOLUTION].w > 999) { sprintf(buf, "#RSMi%07d#RSSi%07d", s->val[OPT_RESOLUTION].w, s->val[OPT_RESOLUTION].w); } else { sprintf(buf, "#RSMd%03d#RSSd%03d", s->val[OPT_RESOLUTION].w, s->val[OPT_RESOLUTION].w); } strcat(cmd, buf); if (strcmp(s->hw->sane.model, (char*)"DS-70") == 0 || strcmp(s->hw->sane.model, (char*)"ES-65WR") == 0 || strcmp(s->hw->sane.model, (char*)"ES-60W") == 0 || strcmp(s->hw->sane.model, (char*)"DS-80W") == 0 || strcmp(s->hw->sane.model, (char*)"ES-55R") == 0 || strcmp(s->hw->sane.model, (char*)"ES-50") == 0){ sprintf(buf, "#BSZi0262144"); strcat(cmd, buf); } else { sprintf(buf, "#BSZi1048576"); strcat(cmd, buf); } /* scanning area */ sprintf(buf, "#ACQi%07di%07di%07di%07d", s->left, s->top, s->params.pixels_per_line, s->params.lines); if (strcmp(source_list[s->val[OPT_SOURCE].w], STRING_ADFFRONT) == 0 || strcmp(source_list[s->val[OPT_SOURCE].w], STRING_ADFDUPLEX) == 0) { status = esci2_stat(s); if (status != SANE_STATUS_GOOD) { goto end; } } strcat(cmd, buf); int pos = 0; { for (int i = 0; i < CMD_BUF_SIZE; i++) { // find end of string if(cmd[i] == 0) { pos = i; break; } } if (s->params.format == SANE_FRAME_GRAY && s->params.depth == 8) { DBG(10, "SANE_FRAME_GRAY\n"); cmd[pos++] = '#'; cmd[pos++] = 'G'; cmd[pos++] = 'M'; cmd[pos++] = 'T'; cmd[pos++] = 'M'; cmd[pos++] = 'O'; cmd[pos++] = 'N'; cmd[pos++] = 'O'; cmd[pos++] = 'h'; cmd[pos++] = '1'; cmd[pos++] = '0'; cmd[pos++] = '0'; for(int count = 0; count < 256; count++) { cmd[pos++] = LUT[s->hw->lut_id][count]; } } if (s->params.format == SANE_FRAME_GRAY && s->params.depth == 1) { DBG(10, "SANE_FRAME_GRAY\n"); cmd[pos++] = '#'; cmd[pos++] = 'G'; cmd[pos++] = 'M'; cmd[pos++] = 'T'; cmd[pos++] = 'M'; cmd[pos++] = 'O'; cmd[pos++] = 'N'; cmd[pos++] = 'O'; cmd[pos++] = 'h'; cmd[pos++] = '1'; cmd[pos++] = '0'; cmd[pos++] = '0'; for(int count = 0; count < 256; count++) { cmd[pos++] = LUT[0][count]; } } else if (s->params.format == SANE_FRAME_RGB) { DBG(10, "SANE_FRAME_RGB\n"); cmd[pos++] = '#'; cmd[pos++] = 'G'; cmd[pos++] = 'M'; cmd[pos++] = 'T'; cmd[pos++] = 'R'; cmd[pos++] = 'E'; cmd[pos++] = 'D'; cmd[pos++] = ' '; cmd[pos++] = 'h'; cmd[pos++] = '1'; cmd[pos++] = '0'; cmd[pos++] = '0'; for(int count = 0; count < 256; count++) { cmd[pos++] = LUT_R[s->hw->lut_id][count]; } cmd[pos++] = '#'; cmd[pos++] = 'G'; cmd[pos++] = 'M'; cmd[pos++] = 'T'; cmd[pos++] = 'G'; cmd[pos++] = 'R'; cmd[pos++] = 'N'; cmd[pos++] = ' '; cmd[pos++] = 'h'; cmd[pos++] = '1'; cmd[pos++] = '0'; cmd[pos++] = '0'; for(int count = 0; count < 256; count++) { cmd[pos++] = LUT_G[s->hw->lut_id][count]; } cmd[pos++] = '#'; cmd[pos++] = 'G'; cmd[pos++] = 'M'; cmd[pos++] = 'T'; cmd[pos++] = 'B'; cmd[pos++] = 'L'; cmd[pos++] = 'U'; cmd[pos++] = ' '; cmd[pos++] = 'h'; cmd[pos++] = '1'; cmd[pos++] = '0'; cmd[pos++] = '0'; for(int count = 0; count < 256; count++) { cmd[pos++] = LUT_B[s->hw->lut_id][count]; } } cmd[pos] = 0; } {// Set Color Matrix if (s->params.format == SANE_FRAME_RGB && s->hw->lut_id != 0 )/*Color Matrix Target devide and color Scan*/ { ColorMatrix matrix; // DS-530 if (s->hw->lut_id == 2) { // R matrix[0][0] = 1.0229; matrix[0][1] = 0.0009; matrix[0][2] = -0.0238; // G matrix[1][0] = 0.0031; matrix[1][1] = 1.0287; matrix[1][2] = -0.0318; //B matrix[2][0] = 0.0044; matrix[2][1] = -0.1150; matrix[2][2] = 1.1106; } // DS-1660W Flatbed if (s->hw->lut_id == 4) { // R matrix[0][0] = 1.0229; matrix[0][1] = 0.0009; matrix[0][2] = -0.0238; // G matrix[1][0] = 0.0031; matrix[1][1] = 1.0287; matrix[1][2] = -0.0318; //B matrix[2][0] = 0.0044; matrix[2][1] = -0.1150; matrix[2][2] = 1.1106; } // DS-320 if (s->hw->lut_id == 5) { // R matrix[0][0] = 1.0250; matrix[0][1] = 0.0004; matrix[0][2] = -0.0254; // G matrix[1][0] = 0.0003; matrix[1][1] = 1.0022; matrix[1][2] = -0.0025; //B matrix[2][0] = 0.0049; matrix[2][1] = -0.0949; matrix[2][2] = 1.0900; } // ES-50 if (s->hw->lut_id == 6) { // R matrix[0][0] = 1.0383; matrix[0][1] = -0.0021; matrix[0][2] = -0.0362; // G matrix[1][0] = 0.0046; matrix[1][1] = 1.0576; matrix[1][2] = -0.0622; //B matrix[2][0] = 0.0235; matrix[2][1] = -0.2396; matrix[2][2] = 1.2161; } // R matrix[0][0] = 0.9864; matrix[0][1] = 0.0248; matrix[0][2] = -0.0112; // G matrix[1][0] = 0.0021; matrix[1][1] = 1.0100; matrix[1][2] = -0.0112; //B matrix[2][0] = 0.0139; matrix[2][1] = -0.1249; matrix[2][2] = 1.1110; // Set Matrix value { cmd[pos++] = '#'; cmd[pos++] = 'C'; cmd[pos++] = 'M'; cmd[pos++] = 'X'; cmd[pos++] = 'U'; cmd[pos++] = 'M'; cmd[pos++] = '0'; cmd[pos++] = '8'; cmd[pos++] = 'h'; cmd[pos++] = '0'; cmd[pos++] = '0'; cmd[pos++] = '9'; } // Matrix to be sent to scanner must be following d1-d9 order: // // G R B // G [d1 d4 d7] // R [d2 d5 d8] // B [d3 d6 d9] // // So, we will convert it with index table. char index[9] = {4, 1, 7, 3, 0, 6, 5, 2, 8}; double flatten[9] = {0}; for (int row = 0; row < 3; row++) { for (int col = 0; col < 3; col++) { flatten[row * 3 + col] = matrix[row][col]; } } int rounded[9] = {0}; ESCIRoundColorCorrectionMatrix(32, flatten, rounded); char ordered[9] = {0}; for (int row = 0; row < 3; row++) { for (int col = 0; col < 3; col++) { int val = rounded[row * 3 + col]; unsigned char oct = (unsigned char)abs(val); oct |= ((val < 0) ? (1 << 7) : 0); ordered[(signed char)index[row * 3 + col]] = oct; } } { cmd[pos++] = ordered[0]; cmd[pos++] = ordered[1]; cmd[pos++] = ordered[2]; cmd[pos++] = ordered[3]; cmd[pos++] = ordered[4]; cmd[pos++] = ordered[5]; cmd[pos++] = ordered[6]; cmd[pos++] = ordered[7]; cmd[pos++] = ordered[8]; cmd[pos++] = 0; //padding cmd[pos++] = 0; //padding cmd[pos++] = 0; //padding DBG(1, "color matrix\n"); for (int i = 0; i < 9; i++) { DBG(1, "%d\n", ordered[i]); } } cmd[pos] = 0; } } status = esci2_para(s, cmd, pos); if (status != SANE_STATUS_GOOD) { goto end; } /* start scanning */ DBG(1, "%s: scanning...\n", __func__); /* switch to data state */ status = esci2_trdt(s); if (status != SANE_STATUS_GOOD) { goto end; } /* first page is page 1 */ s->pages = 1; s->scanning = 1; s->dummy = 0; s->scanEnd = 0; end: if (status != SANE_STATUS_GOOD) { DBG(1, "%s: start failed: %s\n", __func__, sane_strstatus(status)); } return status; } static SANE_Status acquire_jpeg_data(epsonds_scanner* s) { SANE_Int read = 0; SANE_Int jpegBufSize = s->params.bytes_per_line * s->params.lines; if (s->needToConvertBW) { jpegBufSize = s->params.pixels_per_line * s->params.lines; } s->frontJpegBuf = malloc(jpegBufSize); s->backJpegBuf = malloc(jpegBufSize); s->frontJpegBufLen = 0; s->backJpegBufLen = 0; // load all images, decode and fill buffer SANE_Int status = SANE_STATUS_GOOD; int eofFront = 0; int eofBack = 0; status = eds_ring_init(&s->front, (s->params.bytes_per_line) * s->params.lines); if (status != SANE_STATUS_GOOD) { return status; } status = eds_ring_init(&s->back, (s->params.bytes_per_line) * s->params.lines); if (status != SANE_STATUS_GOOD) { return status; } while (1) { status = esci2_img(s, &read); DBG(20, "acquire_jpeg_data read: %d, eof: %d, backside: %d, status: %d\n", read, s->eof, s->backside, status); if (read) { if (s->backside) { SANE_Byte* backBuffer = s->backJpegBuf + s->backJpegBufLen; memcpy(backBuffer, s->buf, read); s->backJpegBufLen += read; }else{ SANE_Byte* frontBuffer = s->frontJpegBuf + s->frontJpegBufLen ; memcpy(frontBuffer, s->buf, read); s->frontJpegBufLen += read; } } if (status == SANE_STATUS_GOOD) { DBG(20, "continue acquire image\n"); continue; } else if (status == SANE_STATUS_EOF) { if (s->backside) { DBG(20, "eofBack\n"); eofBack = 1; }else{ DBG(20, "eofFront\n"); eofFront = 1; } }else if (status == SANE_STATUS_CANCELLED) { // cancel cleanup esci2_can(s); free(s->frontJpegBuf); free(s->backJpegBuf); s->frontJpegBuf = NULL; s->backJpegBuf = NULL; return status; }else{ // error occurs cleanup free(s->frontJpegBuf); free(s->backJpegBuf); s->frontJpegBuf = NULL; s->backJpegBuf = NULL; return status; } if (s->isDuplexScan) { DBG(20, "eofFront = %d eofBack = %d\n", eofFront, eofBack); // acquire finish if (eofFront && eofBack) { DBG(20, "eofFront && eofBack end\n"); break; } }else{ if (eofFront) { DBG(20, "eofFront end\n"); break; } } } return SANE_STATUS_GOOD; } static SANE_Status acquire_raw_data(epsonds_scanner* s) { SANE_Int read = 0; // load all images, decode and fill buffer SANE_Int status = SANE_STATUS_GOOD; int eofFront = 0; int eofBack = 0; int firstWrite = 1; while (1) { DBG(20, "acquire_raw_data loop start\n"); status = esci2_img(s, &read); DBG(20, "acquire_raw_data read: %d, eof: %d, backside: %d, status: %d\n", read, s->eof, s->backside, status); if (read) { if (firstWrite) { status = eds_ring_init(&s->front, (s->params.bytes_per_line + s->dummy) * s->params.lines); if (status != SANE_STATUS_GOOD) { return status; } status = eds_ring_init(&s->back, (s->params.bytes_per_line + s->dummy) * s->params.lines); if (status != SANE_STATUS_GOOD) { return status; } firstWrite = 0; } DBG(20, "eds_ring_write start\n"); status = eds_ring_write(s->backside ? &s->back : &s->front, s->buf, read); DBG(20, "eds_ring_write end\n"); } DBG(20, "acquire_raw_data3\n"); if (status == SANE_STATUS_GOOD) { DBG(20, "contiune acquire image\n"); continue; } else if (status == SANE_STATUS_EOF) { if (s->backside) { eofBack = 1; }else{ eofFront = 1; } } else if (status == SANE_STATUS_CANCELLED) { esci2_can(s); return status; }else{ // error occurs cleanup return status; } if (s->isDuplexScan) { // acquire finish if (eofFront && eofBack) { break; } }else{ if (eofFront) { break; } } } int needBytes = (s->params.bytes_per_line + s->dummy) * s->params.lines; { int available = eds_ring_avail(&s->front); if (available < needBytes) { int required = needBytes - available; unsigned char* padding = (unsigned char*)malloc(required); memset(padding, 255, required); eds_ring_write(&s->front, padding, required); free(padding); } } { int available = eds_ring_avail(&s->back); if (available > 0 && available < needBytes) { int required = needBytes - available; unsigned char* padding = (unsigned char*)malloc(required); memset(padding, 255, required); eds_ring_write(&s->back, padding, required); free(padding); } } if (s->isDuplexScan) { upside_down_backside_image(s); } DBG(20, "acquire_raw_data finish"); return SANE_STATUS_GOOD; } static SANE_Status acquire_and_decode_jpeg_data(epsonds_scanner* s) { SANE_Int status = acquire_jpeg_data(s); if (status == SANE_STATUS_GOOD) { DBG(20, "** %s: sane status = %d needToConvertBW = %d \n", __func__, status, s->needToConvertBW); // process front page if (s->frontJpegBufLen > 0) { eds_decode_jpeg(s, s->frontJpegBuf, s->frontJpegBufLen, &s->front,0, s->needToConvertBW); free(s->frontJpegBuf); s->frontJpegBuf = NULL; } // process back page if (s->backJpegBufLen > 0) { eds_decode_jpeg(s, s->backJpegBuf, s->backJpegBufLen, &s->back, 1, s->needToConvertBW); free(s->backJpegBuf); s->backJpegBuf = NULL; } if (s->isDuplexScan) { upside_down_backside_image(s); } }else{ DBG(20, "** %s: sane finish status = %d\n", __func__, status); return status; } return status; } int sumLength = 0; /* this moves data from our buffers to SANE */ SANE_Status sane_read(SANE_Handle handle, SANE_Byte *data, SANE_Int max_length, SANE_Int *length) { epsonds_scanner *s = (epsonds_scanner *)handle; SANE_Int read = 0; if (s->canceling) { esci2_can(s); *length = 0; return SANE_STATUS_CANCELLED; } int available = eds_ring_avail(s->current); /* anything in the buffer? pass it to the frontend */ if (available > 0) { DBG(18, "reading from ring buffer, %d left\n", available); eds_copy_image_from_ring(s, data, max_length, &read); // data is empty fin if (read == 0) { *length = 0; eds_ring_flush(s->current); eds_ring_destory(s->current); DBG(18, "returns EOF 2\n"); return SANE_STATUS_EOF; } *length = read; return SANE_STATUS_GOOD; }else{ *length = 0; eds_ring_flush(s->current); eds_ring_destory(s->current); DBG(18, "returns EOF 1\n"); return SANE_STATUS_EOF; } } /* * void sane_cancel(SANE_Handle handle) * * Set the cancel flag to true. The next time the backend requests data * from the scanner the CAN message will be sent. */ void sane_cancel(SANE_Handle handle) { DBG(1, "** %s\n", __func__); ((epsonds_scanner *)handle)->canceling = SANE_TRUE; } /* * SANE_Status sane_set_io_mode() * * not supported - for asynchronous I/O */ SANE_Status sane_set_io_mode(SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking) { return SANE_STATUS_UNSUPPORTED; } /* * SANE_Status sane_get_select_fd() * * not supported - for asynchronous I/O */ SANE_Status sane_get_select_fd(SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ *fd) { return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/epsonds.conf.in000066400000000000000000000004211456256263500176160ustar00rootroot00000000000000# epsonds.conf # # here are some examples for how to configure the epsonds backend # USB usb # For libusb support for unknown scanners use the following command # usb # e.g.: # usb 0x4b8 0x14c # # Network # # net 192.168.1.123 net autodiscovery backends-1.3.0/backend/epsonds.h000066400000000000000000000114041456256263500165160ustar00rootroot00000000000000/* * epsonds.c - Epson ESC/I-2 driver. * * Copyright (C) 2015 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ #ifndef epsonds_h #define epsonds_h #undef BACKEND_NAME #define BACKEND_NAME epsonds #define DEBUG_NOT_STATIC #define mode_params epsonds_mode_params #define source_list epsonds_source_list #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_STDDEF_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef NEED_SYS_TYPES_H #include #endif #include /* for memset and memcpy */ #include #include "sane/sane.h" #include "sane/sanei_backend.h" #include "sane/sanei_debug.h" #include "sane/sanei_usb.h" #include "sane/sanei_jpeg.h" #define EPSONDS_CONFIG_FILE "epsonds.conf" #ifndef PATH_MAX #define PATH_MAX (1024) #endif #ifndef XtNumber #define XtNumber(x) (sizeof(x) / sizeof(x[0])) #define XtOffset(p_type, field) ((size_t)&(((p_type)NULL)->field)) #define XtOffsetOf(s_type, field) XtOffset(s_type*, field) #endif #define ACK 0x06 #define NAK 0x15 #define FS 0x1C #define FBF_STR SANE_I18N("Flatbed") #define TPU_STR SANE_I18N("Transparency Unit") #define ADF_STR SANE_I18N("Automatic Document Feeder") #define STRING_FLATBED SANE_I18N("Flatbed") #define STRING_ADFFRONT SANE_I18N("ADF Front") #define STRING_ADFDUPLEX SANE_I18N("ADF Duplex") enum { OPT_NUM_OPTS = 0, OPT_STANDARD_GROUP, OPT_SOURCE, OPT_MODE, OPT_DEPTH, OPT_RESOLUTION, OPT_GEOMETRY_GROUP, OPT_TL_X, OPT_TL_Y, OPT_BR_X, OPT_BR_Y, OPT_EQU_GROUP, OPT_EJECT, OPT_LOAD, OPT_ADF_SKEW, OPT_ADF_CRP, NUM_OPTIONS }; typedef enum { /* hardware connection to the scanner */ SANE_EPSONDS_NODEV, /* default, no HW specified yet */ SANE_EPSONDS_USB, /* USB interface */ SANE_EPSONDS_NET /* network interface */ } epsonds_conn_type; /* hardware description */ struct epsonds_device { struct epsonds_device *next; epsonds_conn_type connection; char *name; char *model; unsigned int model_id; SANE_Device sane; SANE_Range *x_range; SANE_Range *y_range; SANE_Range dpi_range; SANE_Byte alignment; SANE_Int *res_list; /* list of resolutions */ SANE_Int *depth_list; SANE_Int max_depth; /* max. color depth */ SANE_Bool has_raw; /* supports RAW format */ SANE_Bool has_mono; /*supprt M001*/ SANE_Bool has_fb; /* flatbed */ SANE_Range fbf_x_range; /* x range */ SANE_Range fbf_y_range; /* y range */ SANE_Byte fbf_alignment; /* left, center, right */ SANE_Bool fbf_has_skew; /* supports skew correction */ SANE_Bool has_adf; /* adf */ SANE_Range adf_x_range; /* x range */ SANE_Range adf_y_range; /* y range */ SANE_Bool adf_is_duplex; /* supports duplex mode */ SANE_Bool adf_singlepass; /* supports single pass duplex */ SANE_Bool adf_has_skew; /* supports skew correction */ SANE_Bool adf_has_load; /* supports load command */ SANE_Bool adf_has_eject; /* supports eject command */ SANE_Byte adf_alignment; /* left, center, right */ SANE_Byte adf_has_dfd; /* supports double feed detection */ SANE_Byte adf_has_crp; /* supports crp */ SANE_Bool has_tpu; /* tpu */ SANE_Range tpu_x_range; /* transparency unit x range */ SANE_Range tpu_y_range; /* transparency unit y range */ SANE_Int lut_id; }; typedef struct epsonds_device epsonds_device; typedef struct ring_buffer { SANE_Byte *ring, *wp, *rp, *end; SANE_Int fill, size; } ring_buffer; /* an instance of a scanner */ struct epsonds_scanner { struct epsonds_scanner *next; struct epsonds_device *hw; int fd; SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; SANE_Parameters params; size_t bsz; /* transfer buffer size */ SANE_Byte *buf, *line_buffer; ring_buffer *current, front, back; SANE_Bool eof, scanning, canceling, locked, backside, mode_jpeg; SANE_Int left, top, pages, dummy; SANE_Int width_front, height_front; SANE_Int width_back , height_back; SANE_Int width_temp, height_temp; /* jpeg stuff */ djpeg_dest_ptr jdst; struct jpeg_decompress_struct jpeg_cinfo; struct jpeg_error_mgr jpeg_err; SANE_Bool jpeg_header_seen; /* network buffers */ unsigned char *netbuf, *netptr; size_t netlen; SANE_Byte *frontJpegBuf, *backJpegBuf; SANE_Int frontJpegBufLen, backJpegBufLen; SANE_Int acquirePage; SANE_Int isflatbedScan; SANE_Int isDuplexScan; SANE_Int needToConvertBW; SANE_Int scanEnd; }; typedef struct epsonds_scanner epsonds_scanner; struct mode_param { int color; int flags; int dropout_mask; int depth; }; enum { MODE_BINARY, MODE_GRAY, MODE_COLOR }; #endif backends-1.3.0/backend/escl.conf.in000066400000000000000000000026471456256263500171050ustar00rootroot00000000000000# escl.conf -- ESCL configuration # Lines starting with a # or a ; are comments. Comments must be on a # line of their own. End-of-line comments are not supported. # Explanation : if you can't detect your device but it's an eSCL device, modify this escl conf' file to use your device. # -> uncomment the lines below, from '[device]' to 'port'. # -> put your device name instead of 'EPSON X'. # -> put your type of protocol instead of 'https' : http or https. # -> put your device ip instead of '123.456.789.10'. # -> put the port that you use instead of '88'. # For example, the lines below are for one device, but if you have several devices to use, you can duplicate the lines below as many times as you have devices. # You can also configure a device on a single line starting with 'device' # by writing a complete URL and an optional model name. # Name of the device not using the PDF format. Spaces are replaced by _. Please report the devices having this problem on sane-devel # Uncomment the line to add your device #pdfblacklist Brother_DCP-L2530DW_series #device http://123.456.789.10:8080 OptionalModel1 #device https://123.456.789.10:443 "Optional Model 2" #device https://123.456.789.10:443 "HP Color LaserJet FlowMFP M578" "hack=localhost" #device unix:/run/proxy.sock:http://123.456.789.10:80 #[device] #model EPSON X #type https #ip 123.456.789.10 #port 88 backends-1.3.0/backend/escl/000077500000000000000000000000001456256263500156205ustar00rootroot00000000000000backends-1.3.0/backend/escl/escl.c000066400000000000000000002101151456256263500167120ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Touboul Nathane Copyright (C) 2019 Thierry HUCHARD This file is part of the SANE package. SANE 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 3 of the License, or (at your option) any later version. SANE 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 sane; see the file COPYING. If not, see . This file implements a SANE backend for eSCL scanners. */ #include "escl.h" #include #include #include #include #include "../include/sane/saneopts.h" #include "../include/sane/sanei.h" #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_config.h" #ifndef SANE_NAME_SHARPEN # define SANE_NAME_SHARPEN "sharpen" # define SANE_TITLE_SHARPEN SANE_I18N("Sharpen") # define SANE_DESC_SHARPEN SANE_I18N("Set sharpen value.") #endif #ifndef SANE_NAME_THRESHOLD # define SANE_NAME_THRESHOLD "threshold" #endif #ifndef SANE_TITLE_THRESHOLD # define SANE_TITLE_THRESHOLD SANE_I18N("Threshold") #endif #ifndef SANE_DESC_THRESHOLD # define SANE_DESC_THRESHOLD \ SANE_I18N("Set threshold for line-art scans.") #endif #define min(A,B) (((A)<(B)) ? (A) : (B)) #define max(A,B) (((A)>(B)) ? (A) : (B)) #define IS_ACTIVE(OPTION) (((handler->opt[OPTION].cap) & SANE_CAP_INACTIVE) == 0) #define INPUT_BUFFER_SIZE 4096 static const SANE_Device **devlist = NULL; static ESCL_Device *list_devices_primary = NULL; static int num_devices = 0; #ifdef CURL_SSLVERSION_MAX_DEFAULT static int proto_tls[] = { CURL_SSLVERSION_MAX_DEFAULT, #ifdef CURL_SSLVERSION_MAX_TLSv1_3 CURL_SSLVERSION_MAX_TLSv1_3, #endif #ifdef CURL_SSLVERSION_MAX_TLSv1_2 CURL_SSLVERSION_MAX_TLSv1_2, #endif #ifdef CURL_SSLVERSION_MAX_TLSv1_1 CURL_SSLVERSION_MAX_TLSv1_1, #endif #ifdef CURL_SSLVERSION_MAX_TLSv1_0 CURL_SSLVERSION_MAX_TLSv1_0, #endif -1 }; #endif typedef struct Handled { struct Handled *next; ESCL_Device *device; char *result; ESCL_ScanParam param; SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; capabilities_t *scanner; SANE_Range x_range1; SANE_Range x_range2; SANE_Range y_range1; SANE_Range y_range2; SANE_Range brightness_range; SANE_Range contrast_range; SANE_Range sharpen_range; SANE_Range thresold_range; SANE_Bool cancel; SANE_Bool write_scan_data; SANE_Bool decompress_scan_data; SANE_Bool end_read; SANE_Parameters ps; } escl_sane_t; static ESCL_Device * escl_free_device(ESCL_Device *current) { if (!current) return NULL; free((void*)current->ip_address); free((void*)current->model_name); free((void*)current->type); free((void*)current->is); free((void*)current->uuid); free((void*)current->unix_socket); curl_slist_free_all(current->hack); free(current); return NULL; } #ifdef CURL_SSLVERSION_MAX_DEFAULT static int escl_tls_protocol_supported(char *url, int proto) { CURLcode res = CURLE_UNSUPPORTED_PROTOCOL; CURL *curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, url); /* ask libcurl to use TLS version 1.0 or later */ curl_easy_setopt(curl, CURLOPT_SSLVERSION, proto); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 3L); /* Perform the request */ res = curl_easy_perform(curl); curl_easy_cleanup(curl); } return res; } static int escl_is_tls(char * url, char *type) { int tls_version = 0; if(!strcmp(type, "_uscans._tcp") || !strcmp(type, "https")) { while(proto_tls[tls_version] != -1) { if (escl_tls_protocol_supported(url, proto_tls[tls_version]) == CURLE_OK) { DBG(10, "curl tls compatible (%d)\n", proto_tls[tls_version]); break; } tls_version++; } if (proto_tls[tls_version] < 1) return 0; } return proto_tls[tls_version]; } #else static int escl_is_tls(char * url, char *type) { (void)url; (void)type; return 0; } #endif void escl_free_handler(escl_sane_t *handler) { if (handler == NULL) return; escl_free_device(handler->device); free(handler); } SANE_Status escl_parse_name(SANE_String_Const name, ESCL_Device *device); static SANE_Status escl_check_and_add_device(ESCL_Device *current) { if(!current) { DBG (10, "ESCL_Device *current us null.\n"); return (SANE_STATUS_NO_MEM); } if (!current->ip_address) { DBG (10, "Ip Address allocation failure.\n"); return (SANE_STATUS_NO_MEM); } if (current->port_nb == 0) { DBG (10, "No port defined.\n"); return (SANE_STATUS_NO_MEM); } if (!current->model_name) { DBG (10, "Modele Name allocation failure.\n"); return (SANE_STATUS_NO_MEM); } if (!current->type) { DBG (10, "Scanner Type allocation failure.\n"); return (SANE_STATUS_NO_MEM); } if (!current->is) { DBG (10, "Scanner Is allocation failure.\n"); return (SANE_STATUS_NO_MEM); } ++num_devices; current->next = list_devices_primary; list_devices_primary = current; return (SANE_STATUS_GOOD); } /** * \fn static SANE_Status escl_add_in_list(ESCL_Device *current) * \brief Function that adds all the element needed to my list : * the port number, the model name, the ip address, and the type of url (http/https). * Moreover, this function counts the number of devices found. * * \return SANE_STATUS_GOOD if everything is OK. */ static SANE_Status escl_add_in_list(ESCL_Device *current) { if(!current) { DBG (10, "ESCL_Device *current us null.\n"); return (SANE_STATUS_NO_MEM); } if (SANE_STATUS_GOOD == escl_check_and_add_device(current)) { list_devices_primary = current; return (SANE_STATUS_GOOD); } current = escl_free_device(current); return (SANE_STATUS_NO_MEM); } /** * \fn SANE_Status escl_device_add(int port_nb, const char *model_name, char *ip_address, char *type) * \brief Function that browses my list ('for' loop) and returns the "escl_add_in_list" function to * adds all the element needed to my list : * the port number, the model name, the ip address and the type of the url (http / https). * * \return escl_add_in_list(current) */ SANE_Status escl_device_add(int port_nb, const char *model_name, char *ip_address, const char *is, const char *uuid, char *type) { char tmp[PATH_MAX] = { 0 }; char *model = NULL; char url_port[512] = { 0 }; int tls_version = 0; ESCL_Device *current = NULL; DBG (10, "escl_device_add\n"); snprintf(url_port, sizeof(url_port), "https://%s:%d", ip_address, port_nb); tls_version = escl_is_tls(url_port, type); for (current = list_devices_primary; current; current = current->next) { if ((strcmp(current->ip_address, ip_address) == 0) || (uuid && current->uuid && !strcmp(current->uuid, uuid))) { if (strcmp(current->type, type)) { if(!strcmp(type, "_uscans._tcp") || !strcmp(type, "https")) { free (current->type); current->type = strdup(type); if (strcmp(current->ip_address, ip_address)) { free (current->ip_address); current->ip_address = strdup(ip_address); } current->port_nb = port_nb; current->https = SANE_TRUE; current->tls = tls_version; } return (SANE_STATUS_GOOD); } else if (current->port_nb == port_nb) return (SANE_STATUS_GOOD); } } current = (ESCL_Device*)calloc(1, sizeof(*current)); if (current == NULL) { DBG (10, "New device allocation failure.\n"); return (SANE_STATUS_NO_MEM); } current->port_nb = port_nb; if (strcmp(type, "_uscan._tcp") != 0 && strcmp(type, "http") != 0) { snprintf(tmp, sizeof(tmp), "%s SSL", model_name); current->https = SANE_TRUE; } else { current->https = SANE_FALSE; } current->tls = tls_version; model = (char*)(tmp[0] != 0 ? tmp : model_name); current->model_name = strdup(model); current->ip_address = strdup(ip_address); memset(tmp, 0, PATH_MAX); snprintf(tmp, sizeof(tmp), "%s scanner", (is ? is : "flatbed or ADF")); current->is = strdup(tmp); current->type = strdup(type); if (uuid) current->uuid = strdup(uuid); return escl_add_in_list(current); } /** * \fn static inline size_t max_string_size(const SANE_String_Const strings[]) * \brief Function that browses the string ('for' loop) and counts the number of character in the string. * --> this allows to know the maximum size of the string. * * \return max_size + 1 (the size max) */ static inline size_t max_string_size(const SANE_String_Const strings[]) { size_t max_size = 0; int i = 0; for (i = 0; strings[i]; ++i) { size_t size = strlen (strings[i]); if (size > max_size) max_size = size; } return (max_size + 1); } static char * get_vendor(char *search) { if(strcasestr(search, "Epson")) return strdup("Epson"); else if(strcasestr(search, "Fujitsu")) return strdup("Fujitsu"); else if(strcasestr(search, "HP")) return strdup("HP"); else if(strcasestr(search, "Canon")) return strdup("Canon"); else if(strcasestr(search, "Lexmark")) return strdup("Lexmark"); else if(strcasestr(search, "Samsung")) return strdup("Samsung"); else if(strcasestr(search, "Xerox")) return strdup("Xerox"); else if(strcasestr(search, "OKI")) return strdup("OKI"); else if(strcasestr(search, "Hewlett Packard")) return strdup("Hewlett Packard"); else if(strcasestr(search, "IBM")) return strdup("IBM"); else if(strcasestr(search, "Mustek")) return strdup("Mustek"); else if(strcasestr(search, "Ricoh")) return strdup("Ricoh"); else if(strcasestr(search, "Sharp")) return strdup("Sharp"); else if(strcasestr(search, "UMAX")) return strdup("UMAX"); else if(strcasestr(search, "PINT")) return strdup("PINT"); else if(strcasestr(search, "Brother")) return strdup("Brother"); return NULL; } /** * \fn static SANE_Device *convertFromESCLDev(ESCL_Device *cdev) * \brief Function that checks if the url of the received scanner is secured or not (http / https). * --> if the url is not secured, our own url will be composed like "http://'ip':'port'". * --> else, our own url will be composed like "https://'ip':'port'". * AND, it's in this function that we gather all the information of the url (that were in our list) : * the model_name, the port, the ip, and the type of url. * SO, leaving this function, we have in memory the complete url. * * \return sdev (structure that contains the elements of the url) */ static SANE_Device * convertFromESCLDev(ESCL_Device *cdev) { char *tmp; int len, lv = 0; char unix_path[PATH_MAX+7] = { 0 }; SANE_Device *sdev = (SANE_Device*) calloc(1, sizeof(SANE_Device)); if (!sdev) { DBG (10, "Sane_Device allocation failure.\n"); return NULL; } if (cdev->unix_socket && strlen(cdev->unix_socket)) { snprintf(unix_path, sizeof(unix_path), "unix:%s:", cdev->unix_socket); } len = snprintf(NULL, 0, "%shttp%s://%s:%d", unix_path, cdev->https ? "s" : "", cdev->ip_address, cdev->port_nb); len++; tmp = (char *)malloc(len); if (!tmp) { DBG (10, "Name allocation failure.\n"); goto freedev; } snprintf(tmp, len, "%shttp%s://%s:%d", unix_path, cdev->https ? "s" : "", cdev->ip_address, cdev->port_nb); sdev->name = tmp; DBG( 1, "Escl add device : %s\n", tmp); sdev->vendor = get_vendor(cdev->model_name); if (!sdev->vendor) sdev->vendor = strdup("ESCL"); else lv = strlen(sdev->vendor) + 1; if (!sdev->vendor) { DBG (10, "Vendor allocation failure.\n"); goto freemodel; } sdev->model = strdup(lv + cdev->model_name); if (!sdev->model) { DBG (10, "Model allocation failure.\n"); goto freename; } sdev->type = strdup(cdev->is); if (!sdev->type) { DBG (10, "Scanner Type allocation failure.\n"); goto freevendor; } return (sdev); freevendor: free((void*)sdev->vendor); freemodel: free((void*)sdev->model); freename: free((void*)sdev->name); freedev: free((void*)sdev); return NULL; } /** * \fn SANE_Status sane_init(SANE_Int *version_code, SANE_Auth_Callback authorize) * \brief Function that's called before any other SANE function ; it's the first SANE function called. * --> this function checks the SANE config. and can check the authentication of the user if * 'authorize' value is more than SANE_TRUE. * In this case, it will be necessary to define an authentication method. * * \return SANE_STATUS_GOOD (everything is OK) */ SANE_Status sane_init(SANE_Int *version_code, SANE_Auth_Callback __sane_unused__ authorize) { DBG_INIT(); DBG (10, "escl sane_init\n"); SANE_Status status = SANE_STATUS_GOOD; curl_global_init(CURL_GLOBAL_ALL); if (version_code != NULL) *version_code = SANE_VERSION_CODE(1, 0, 0); if (status != SANE_STATUS_GOOD) return (status); return (SANE_STATUS_GOOD); } /** * \fn void sane_exit(void) * \brief Function that must be called to terminate use of a backend. * This function will first close all device handles that still might be open. * --> by freeing all the elements of my list. * After this function, no function other than 'sane_init' may be called. */ void sane_exit(void) { DBG (10, "escl sane_exit\n"); ESCL_Device *next = NULL; while (list_devices_primary != NULL) { next = list_devices_primary->next; free(list_devices_primary); list_devices_primary = next; } if (devlist) free (devlist); list_devices_primary = NULL; devlist = NULL; curl_global_cleanup(); } /** * \fn static SANE_Status attach_one_config(SANEI_Config *config, const char *line) * \brief Function that implements a configuration file to the user : * if the user can't detect some devices, he will be able to force their detection with this config' file to use them. * Thus, this function parses the config' file to use the device of the user with the information below : * the type of protocol (http/https), the ip, the port number, and the model name. * * \return escl_add_in_list(escl_device) if the parsing worked, SANE_STATUS_GOOD otherwise. */ static SANE_Status attach_one_config(SANEI_Config __sane_unused__ *config, const char *line, void __sane_unused__ *data) { int port = 0; SANE_Status status; static ESCL_Device *escl_device = NULL; if (*line == '#') return SANE_STATUS_GOOD; if (!strncmp(line, "pdfblacklist", 12)) return SANE_STATUS_GOOD; if (strncmp(line, "device", 6) == 0) { char *name_str = NULL; char *opt_model = NULL; char *opt_hack = NULL; line = sanei_config_get_string(line + 6, &name_str); DBG (10, "New Escl_Device URL [%s].\n", (name_str ? name_str : "VIDE")); if (!name_str || !*name_str) { DBG (1, "Escl_Device URL missing.\n"); return SANE_STATUS_INVAL; } if (*line) { line = sanei_config_get_string(line, &opt_model); DBG (10, "New Escl_Device model [%s].\n", opt_model); } if (*line) { line = sanei_config_get_string(line, &opt_hack); DBG (10, "New Escl_Device hack [%s].\n", opt_hack); } escl_free_device(escl_device); escl_device = (ESCL_Device*)calloc(1, sizeof(ESCL_Device)); if (!escl_device) { DBG (10, "New Escl_Device allocation failure.\n"); free(name_str); return (SANE_STATUS_NO_MEM); } status = escl_parse_name(name_str, escl_device); free(name_str); if (status != SANE_STATUS_GOOD) { escl_free_device(escl_device); escl_device = NULL; return status; } escl_device->model_name = opt_model ? opt_model : strdup("Unknown model"); escl_device->is = strdup("flatbed or ADF scanner"); escl_device->uuid = NULL; } if (strncmp(line, "[device]", 8) == 0) { escl_device = escl_free_device(escl_device); escl_device = (ESCL_Device*)calloc(1, sizeof(ESCL_Device)); if (!escl_device) { DBG (10, "New Escl_Device allocation failure."); return (SANE_STATUS_NO_MEM); } } else if (strncmp(line, "ip", 2) == 0) { const char *ip_space = sanei_config_skip_whitespace(line + 2); DBG (10, "New Escl_Device IP [%s].", (ip_space ? ip_space : "VIDE")); if (escl_device != NULL && ip_space != NULL) { DBG (10, "New Escl_Device IP Affected."); escl_device->ip_address = strdup(ip_space); } } else if (sscanf(line, "port %i", &port) == 1 && port != 0) { DBG (10, "New Escl_Device PORT [%d].", port); if (escl_device != NULL) { DBG (10, "New Escl_Device PORT Affected."); escl_device->port_nb = port; } } else if (strncmp(line, "model", 5) == 0) { const char *model_space = sanei_config_skip_whitespace(line + 5); DBG (10, "New Escl_Device MODEL [%s].", (model_space ? model_space : "VIDE")); if (escl_device != NULL && model_space != NULL) { DBG (10, "New Escl_Device MODEL Affected."); escl_device->model_name = strdup(model_space); } } else if (strncmp(line, "type", 4) == 0) { const char *type_space = sanei_config_skip_whitespace(line + 4); DBG (10, "New Escl_Device TYPE [%s].", (type_space ? type_space : "VIDE")); if (escl_device != NULL && type_space != NULL) { DBG (10, "New Escl_Device TYPE Affected."); escl_device->type = strdup(type_space); } } escl_device->is = strdup("flatbed or ADF scanner"); escl_device->uuid = NULL; char url_port[512] = { 0 }; snprintf(url_port, sizeof(url_port), "https://%s:%d", escl_device->ip_address, escl_device->port_nb); escl_device->tls = escl_is_tls(url_port, escl_device->type); status = escl_check_and_add_device(escl_device); if (status == SANE_STATUS_GOOD) escl_device = NULL; return status; } /** * \fn SANE_Status sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only) * \brief Function that searches for connected devices and places them in our 'device_list'. ('for' loop) * If the attribute 'local_only' is worth SANE_FALSE, we only returns the connected devices locally. * * \return SANE_STATUS_GOOD if devlist != NULL ; SANE_STATUS_NO_MEM otherwise. */ SANE_Status sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only) { if (local_only) /* eSCL is a network-only protocol */ return (device_list ? SANE_STATUS_GOOD : SANE_STATUS_INVAL); DBG (10, "escl sane_get_devices\n"); ESCL_Device *dev = NULL; static const SANE_Device **devlist = 0; SANE_Status status; SANE_Status status2; if (device_list == NULL) return (SANE_STATUS_INVAL); status2 = sanei_configure_attach(ESCL_CONFIG_FILE, NULL, attach_one_config, NULL); escl_devices(&status); if (status != SANE_STATUS_GOOD && status2 != SANE_STATUS_GOOD) { if (status2 != SANE_STATUS_GOOD) return (status2); if (status != SANE_STATUS_GOOD) return (status); } if (devlist) free(devlist); devlist = (const SANE_Device **) calloc (num_devices + 1, sizeof (devlist[0])); if (devlist == NULL) return (SANE_STATUS_NO_MEM); int i = 0; for (dev = list_devices_primary; i < num_devices; dev = dev->next) { SANE_Device *s_dev = convertFromESCLDev(dev); devlist[i] = s_dev; i++; } devlist[i] = 0; *device_list = devlist; return (devlist) ? SANE_STATUS_GOOD : SANE_STATUS_NO_MEM; } /* Returns the length of the longest string, including the terminating * character. */ static size_t _source_size_max (SANE_String_Const * sources) { size_t size = 0; while(*sources) { size_t t = strlen (*sources) + 1; if (t > size) size = t; sources++; } return size; } static int _get_resolution(escl_sane_t *handler, int resol) { int x = 1; int n = handler->scanner->caps[handler->scanner->source].SupportedResolutions[0] + 1; int old = -1; for (; x < n; x++) { DBG(10, "SEARCH RESOLUTION [ %d | %d]\n", resol, (int)handler->scanner->caps[handler->scanner->source].SupportedResolutions[x]); if (resol == handler->scanner->caps[handler->scanner->source].SupportedResolutions[x]) return resol; else if (resol < handler->scanner->caps[handler->scanner->source].SupportedResolutions[x]) { if (old == -1) return handler->scanner->caps[handler->scanner->source].SupportedResolutions[1]; else return old; } else old = handler->scanner->caps[handler->scanner->source].SupportedResolutions[x]; } return old; } /** * \fn static SANE_Status init_options(SANE_String_Const name, escl_sane_t *s) * \brief Function thzt initializes all the needed options of the received scanner * (the resolution / the color / the margins) thanks to the information received with * the 'escl_capabilities' function, called just before. * * \return status (if everything is OK, status = SANE_STATUS_GOOD) */ static SANE_Status init_options_small(SANE_String_Const name_source, escl_sane_t *s) { int found = 0; DBG (10, "escl init_options\n"); SANE_Status status = SANE_STATUS_GOOD; if (!s->scanner) return SANE_STATUS_INVAL; if (name_source) { int source = s->scanner->source; if (!strcmp(name_source, SANE_I18N ("ADF Duplex"))) s->scanner->source = ADFDUPLEX; else if (!strncmp(name_source, "A", 1) || !strcmp(name_source, SANE_I18N ("ADF"))) s->scanner->source = ADFSIMPLEX; else s->scanner->source = PLATEN; if (source == s->scanner->source) return status; s->scanner->caps[s->scanner->source].default_color = strdup(s->scanner->caps[source].default_color); s->scanner->caps[s->scanner->source].default_resolution = _get_resolution(s, s->scanner->caps[source].default_resolution); } if (s->scanner->caps[s->scanner->source].ColorModes == NULL) { if (s->scanner->caps[PLATEN].ColorModes) s->scanner->source = PLATEN; else if (s->scanner->caps[ADFSIMPLEX].ColorModes) s->scanner->source = ADFSIMPLEX; else if (s->scanner->caps[ADFDUPLEX].ColorModes) s->scanner->source = ADFDUPLEX; else return SANE_STATUS_INVAL; } if (s->scanner->source == PLATEN) { DBG (10, "SOURCE PLATEN.\n"); } else if (s->scanner->source == ADFDUPLEX) { DBG (10, "SOURCE ADFDUPLEX.\n"); } else if (s->scanner->source == ADFSIMPLEX) { DBG (10, "SOURCE ADFSIMPLEX.\n"); } s->x_range1.min = 0; s->x_range1.max = PIXEL_TO_MM((s->scanner->caps[s->scanner->source].MaxWidth - s->scanner->caps[s->scanner->source].MinWidth), 300.0); s->x_range1.quant = 0; s->x_range2.min = PIXEL_TO_MM(s->scanner->caps[s->scanner->source].MinWidth, 300.0); s->x_range2.max = PIXEL_TO_MM(s->scanner->caps[s->scanner->source].MaxWidth, 300.0); s->x_range2.quant = 0; s->y_range1.min = 0; s->y_range1.max = PIXEL_TO_MM((s->scanner->caps[s->scanner->source].MaxHeight - s->scanner->caps[s->scanner->source].MinHeight), 300.0); s->y_range1.quant = 0; s->y_range2.min = PIXEL_TO_MM(s->scanner->caps[s->scanner->source].MinHeight, 300.0); s->y_range2.max = PIXEL_TO_MM(s->scanner->caps[s->scanner->source].MaxHeight, 300.0); s->y_range2.quant = 0; s->opt[OPT_MODE].constraint.string_list = s->scanner->caps[s->scanner->source].ColorModes; if (s->val[OPT_MODE].s) free(s->val[OPT_MODE].s); s->val[OPT_MODE].s = NULL; if (s->scanner->caps[s->scanner->source].default_color) { int x = 0; if (!strcmp(s->scanner->caps[s->scanner->source].default_color, "Grayscale8")) s->val[OPT_MODE].s = (char *)strdup(SANE_VALUE_SCAN_MODE_GRAY); else if (!strcmp(s->scanner->caps[s->scanner->source].default_color, "BlackAndWhite1")) s->val[OPT_MODE].s = (char *)strdup(SANE_VALUE_SCAN_MODE_LINEART); else s->val[OPT_MODE].s = (char *)strdup(SANE_VALUE_SCAN_MODE_COLOR); for (x = 0; s->scanner->caps[s->scanner->source].ColorModes[x]; x++) { if (s->scanner->caps[s->scanner->source].ColorModes[x] && !strcasecmp(s->scanner->caps[s->scanner->source].ColorModes[x], s->val[OPT_MODE].s)) { found = 1; break; } } } if (!s->scanner->caps[s->scanner->source].default_color || found == 0) { if (s->scanner->caps[s->scanner->source].default_color) free(s->scanner->caps[s->scanner->source].default_color); s->val[OPT_MODE].s = strdup(s->scanner->caps[s->scanner->source].ColorModes[0]); if (!strcasecmp(s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY)) s->scanner->caps[s->scanner->source].default_color = strdup("Grayscale8"); else if (!strcasecmp(s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART)) s->scanner->caps[s->scanner->source].default_color = strdup("BlackAndWhite1"); else s->scanner->caps[s->scanner->source].default_color = strdup("RGB24"); } if (!s->val[OPT_MODE].s) { DBG (10, "Color Mode Default allocation failure.\n"); return (SANE_STATUS_NO_MEM); } if (!s->scanner->caps[s->scanner->source].default_color) { DBG (10, "Color Mode Default allocation failure.\n"); return (SANE_STATUS_NO_MEM); } s->val[OPT_RESOLUTION].w = s->scanner->caps[s->scanner->source].default_resolution; s->opt[OPT_TL_X].constraint.range = &s->x_range1; s->opt[OPT_TL_Y].constraint.range = &s->y_range1; s->opt[OPT_BR_X].constraint.range = &s->x_range2; s->opt[OPT_BR_Y].constraint.range = &s->y_range2; if (s->val[OPT_SCAN_SOURCE].s) free (s->val[OPT_SCAN_SOURCE].s); s->val[OPT_SCAN_SOURCE].s = strdup (s->scanner->Sources[s->scanner->source]); return (SANE_STATUS_GOOD); } /** * \fn static SANE_Status init_options(SANE_String_Const name, escl_sane_t *s) * \brief Function thzt initializes all the needed options of the received scanner * (the resolution / the color / the margins) thanks to the information received with * the 'escl_capabilities' function, called just before. * * \return status (if everything is OK, status = SANE_STATUS_GOOD) */ static SANE_Status init_options(SANE_String_Const name_source, escl_sane_t *s) { DBG (10, "escl init_options\n"); SANE_Status status = SANE_STATUS_GOOD; int i = 0; if (!s->scanner) return SANE_STATUS_INVAL; if (name_source) { int source = s->scanner->source; DBG (10, "escl init_options name [%s]\n", name_source); if (!strcmp(name_source, SANE_I18N ("ADF Duplex"))) s->scanner->source = ADFDUPLEX; else if (!strncmp(name_source, "A", 1) || !strcmp(name_source, SANE_I18N ("ADF"))) s->scanner->source = ADFSIMPLEX; else s->scanner->source = PLATEN; if (source == s->scanner->source) return status; } if (s->scanner->caps[s->scanner->source].ColorModes == NULL) { if (s->scanner->caps[PLATEN].ColorModes) s->scanner->source = PLATEN; else if (s->scanner->caps[ADFSIMPLEX].ColorModes) s->scanner->source = ADFSIMPLEX; else if (s->scanner->caps[ADFDUPLEX].ColorModes) s->scanner->source = ADFDUPLEX; else return SANE_STATUS_INVAL; } if (s->scanner->source == PLATEN) { DBG (10, "SOURCE PLATEN.\n"); } else if (s->scanner->source == ADFDUPLEX) { DBG (10, "SOURCE ADFDUPLEX.\n"); } else if (s->scanner->source == ADFSIMPLEX) { DBG (10, "SOURCE ADFSIMPLEX.\n"); } memset (s->opt, 0, sizeof (s->opt)); memset (s->val, 0, sizeof (s->val)); for (i = 0; i < NUM_OPTIONS; ++i) { s->opt[i].size = sizeof (SANE_Word); s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } s->x_range1.min = 0; s->x_range1.max = PIXEL_TO_MM((s->scanner->caps[s->scanner->source].MaxWidth - s->scanner->caps[s->scanner->source].MinWidth), 300.0); s->x_range1.quant = 0; s->x_range2.min = PIXEL_TO_MM(s->scanner->caps[s->scanner->source].MinWidth, 300.0); s->x_range2.max = PIXEL_TO_MM(s->scanner->caps[s->scanner->source].MaxWidth, 300.0); s->x_range2.quant = 0; s->y_range1.min = 0; s->y_range1.max = PIXEL_TO_MM((s->scanner->caps[s->scanner->source].MaxHeight - s->scanner->caps[s->scanner->source].MinHeight), 300.0); s->y_range1.quant = 0; s->y_range2.min = PIXEL_TO_MM(s->scanner->caps[s->scanner->source].MinHeight, 300.0); s->y_range2.max = PIXEL_TO_MM(s->scanner->caps[s->scanner->source].MaxHeight, 300.0); s->y_range2.quant = 0; s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; s->opt[OPT_MODE_GROUP].title = SANE_TITLE_SCAN_MODE; s->opt[OPT_MODE_GROUP].desc = ""; s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MODE_GROUP].cap = 0; s->opt[OPT_MODE_GROUP].size = 0; s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; 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].unit = SANE_UNIT_NONE; s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_MODE].constraint.string_list = s->scanner->caps[s->scanner->source].ColorModes; if (s->scanner->caps[s->scanner->source].default_color) { if (!strcasecmp(s->scanner->caps[s->scanner->source].default_color, "Grayscale8")) s->val[OPT_MODE].s = (char *)strdup(SANE_VALUE_SCAN_MODE_GRAY); else if (!strcasecmp(s->scanner->caps[s->scanner->source].default_color, "BlackAndWhite1")) s->val[OPT_MODE].s = (char *)strdup(SANE_VALUE_SCAN_MODE_LINEART); else s->val[OPT_MODE].s = (char *)strdup(SANE_VALUE_SCAN_MODE_COLOR); } else { s->val[OPT_MODE].s = (char *)strdup(s->scanner->caps[s->scanner->source].ColorModes[0]); if (!strcasecmp(s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY)) { s->scanner->caps[s->scanner->source].default_color = strdup("Grayscale8"); } else if (!strcasecmp(s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART)) { s->scanner->caps[s->scanner->source].default_color = strdup("BlackAndWhite1"); } else { s->scanner->caps[s->scanner->source].default_color = strdup("RGB24"); } } if (!s->val[OPT_MODE].s) { DBG (10, "Color Mode Default allocation failure.\n"); return (SANE_STATUS_NO_MEM); } DBG (10, "++ Color Mode Default allocation [%s].\n", s->scanner->caps[s->scanner->source].default_color); s->opt[OPT_MODE].size = max_string_size(s->scanner->caps[s->scanner->source].ColorModes); if (!s->scanner->caps[s->scanner->source].default_color) { DBG (10, "Color Mode Default allocation failure.\n"); return (SANE_STATUS_NO_MEM); } DBG (10, "Color Mode Default allocation (%s).\n", s->scanner->caps[s->scanner->source].default_color); s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_RESOLUTION].constraint.word_list = s->scanner->caps[s->scanner->source].SupportedResolutions; s->val[OPT_RESOLUTION].w = s->scanner->caps[s->scanner->source].SupportedResolutions[1]; s->scanner->caps[s->scanner->source].default_resolution = s->scanner->caps[s->scanner->source].SupportedResolutions[1]; s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; s->val[OPT_PREVIEW].w = SANE_FALSE; s->opt[OPT_GRAY_PREVIEW].name = SANE_NAME_GRAY_PREVIEW; s->opt[OPT_GRAY_PREVIEW].title = SANE_TITLE_GRAY_PREVIEW; s->opt[OPT_GRAY_PREVIEW].desc = SANE_DESC_GRAY_PREVIEW; s->opt[OPT_GRAY_PREVIEW].type = SANE_TYPE_BOOL; s->val[OPT_GRAY_PREVIEW].w = SANE_FALSE; s->opt[OPT_GEOMETRY_GROUP].title = SANE_TITLE_GEOMETRY; s->opt[OPT_GEOMETRY_GROUP].desc = SANE_DESC_GEOMETRY; s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_GEOMETRY_GROUP].size = 0; s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; s->opt[OPT_TL_X].size = sizeof(SANE_Fixed); s->opt[OPT_TL_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_TL_X].unit = SANE_UNIT_MM; s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_X].constraint.range = &s->x_range1; s->val[OPT_TL_X].w = s->x_range1.min; s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; s->opt[OPT_TL_Y].size = sizeof(SANE_Fixed); s->opt[OPT_TL_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_Y].constraint.range = &s->y_range1; s->val[OPT_TL_Y].w = s->y_range1.min; s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; s->opt[OPT_BR_X].size = sizeof(SANE_Fixed); s->opt[OPT_BR_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_BR_X].unit = SANE_UNIT_MM; s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_X].constraint.range = &s->x_range2; s->val[OPT_BR_X].w = s->x_range2.max; s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; s->opt[OPT_BR_Y].size = sizeof(SANE_Fixed); s->opt[OPT_BR_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_Y].constraint.range = &s->y_range2; s->val[OPT_BR_Y].w = s->y_range2.max; /* OPT_SCAN_SOURCE */ s->opt[OPT_SCAN_SOURCE].name = SANE_NAME_SCAN_SOURCE; s->opt[OPT_SCAN_SOURCE].title = SANE_TITLE_SCAN_SOURCE; s->opt[OPT_SCAN_SOURCE].desc = SANE_DESC_SCAN_SOURCE; s->opt[OPT_SCAN_SOURCE].type = SANE_TYPE_STRING; s->opt[OPT_SCAN_SOURCE].size = _source_size_max(s->scanner->Sources); s->opt[OPT_SCAN_SOURCE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_SCAN_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_SCAN_SOURCE].constraint.string_list = s->scanner->Sources; if (s->val[OPT_SCAN_SOURCE].s) free (s->val[OPT_SCAN_SOURCE].s); s->val[OPT_SCAN_SOURCE].s = strdup (s->scanner->Sources[s->scanner->source]); /* "Enhancement" group: */ s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement"); s->opt[OPT_ENHANCEMENT_GROUP].desc = ""; /* not valid for a group */ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_ENHANCEMENT_GROUP].size = 0; s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT; s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE; s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; if (s->scanner->brightness) { s->opt[OPT_BRIGHTNESS].constraint.range = &s->brightness_range; s->val[OPT_BRIGHTNESS].w = s->scanner->brightness->value; s->brightness_range.quant=1; s->brightness_range.min=s->scanner->brightness->min; s->brightness_range.max=s->scanner->brightness->max; } else{ SANE_Range range = { 0, 255, 0 }; s->opt[OPT_BRIGHTNESS].constraint.range = ⦥ s->val[OPT_BRIGHTNESS].w = 0; s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; } s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST; s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST; s->opt[OPT_CONTRAST].type = SANE_TYPE_INT; s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE; s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; if (s->scanner->contrast) { s->opt[OPT_CONTRAST].constraint.range = &s->contrast_range; s->val[OPT_CONTRAST].w = s->scanner->contrast->value; s->contrast_range.quant=1; s->contrast_range.min=s->scanner->contrast->min; s->contrast_range.max=s->scanner->contrast->max; } else{ SANE_Range range = { 0, 255, 0 }; s->opt[OPT_CONTRAST].constraint.range = ⦥ s->val[OPT_CONTRAST].w = 0; s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; } s->opt[OPT_SHARPEN].name = SANE_NAME_SHARPEN; s->opt[OPT_SHARPEN].title = SANE_TITLE_SHARPEN; s->opt[OPT_SHARPEN].desc = SANE_DESC_SHARPEN; s->opt[OPT_SHARPEN].type = SANE_TYPE_INT; s->opt[OPT_SHARPEN].unit = SANE_UNIT_NONE; s->opt[OPT_SHARPEN].constraint_type = SANE_CONSTRAINT_RANGE; if (s->scanner->sharpen) { s->opt[OPT_SHARPEN].constraint.range = &s->sharpen_range; s->val[OPT_SHARPEN].w = s->scanner->sharpen->value; s->sharpen_range.quant=1; s->sharpen_range.min=s->scanner->sharpen->min; s->sharpen_range.max=s->scanner->sharpen->max; } else{ SANE_Range range = { 0, 255, 0 }; s->opt[OPT_SHARPEN].constraint.range = ⦥ s->val[OPT_SHARPEN].w = 0; s->opt[OPT_SHARPEN].cap |= SANE_CAP_INACTIVE; } /*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_INT; s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE; s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; if (s->scanner->threshold) { s->opt[OPT_THRESHOLD].constraint.range = &s->thresold_range; s->val[OPT_THRESHOLD].w = s->scanner->threshold->value; s->thresold_range.quant=1; s->thresold_range.min= s->scanner->threshold->min; s->thresold_range.max=s->scanner->threshold->max; } else{ SANE_Range range = { 0, 255, 0 }; s->opt[OPT_THRESHOLD].constraint.range = ⦥ s->val[OPT_THRESHOLD].w = 0; s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; } if (!strcasecmp(s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART)) { if (s->scanner->threshold) s->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE; if (s->scanner->brightness) s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; if (s->scanner->contrast) s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; if (s->scanner->sharpen) s->opt[OPT_SHARPEN].cap |= SANE_CAP_INACTIVE; } else { if (s->scanner->threshold) s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; if (s->scanner->brightness) s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; if (s->scanner->contrast) s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE; if (s->scanner->sharpen) s->opt[OPT_SHARPEN].cap &= ~SANE_CAP_INACTIVE; } return (status); } SANE_Status escl_parse_name(SANE_String_Const name, ESCL_Device *device) { SANE_String_Const host = NULL; SANE_String_Const port_str = NULL; DBG(10, "escl_parse_name\n"); if (name == NULL || device == NULL) { return SANE_STATUS_INVAL; } if (strncmp(name, "unix:", 5) == 0) { SANE_String_Const socket = name + 5; name = strchr(socket, ':'); if (name == NULL) return SANE_STATUS_INVAL; device->unix_socket = strndup(socket, name - socket); name++; } if (strncmp(name, "https://", 8) == 0) { device->https = SANE_TRUE; device->type = strdup("https"); host = name + 8; } else if (strncmp(name, "http://", 7) == 0) { device->https = SANE_FALSE; device->type = strdup("http"); host = name + 7; } else { DBG(1, "Unknown URL scheme in %s", name); return SANE_STATUS_INVAL; } port_str = strchr(host, ':'); if (port_str == NULL) { DBG(1, "Port missing from URL: %s", name); return SANE_STATUS_INVAL; } port_str++; device->port_nb = atoi(port_str); if (device->port_nb < 1 || device->port_nb > 65535) { DBG(1, "Invalid port number in URL: %s", name); return SANE_STATUS_INVAL; } device->ip_address = strndup(host, port_str - host - 1); return SANE_STATUS_GOOD; } static void _get_hack(SANE_String_Const name, ESCL_Device *device) { FILE *fp; SANE_Char line[PATH_MAX]; DBG (3, "_get_hack: start\n"); if (device->model_name && (strcasestr(device->model_name, "LaserJet FlowMFP M578") || strcasestr(device->model_name, "LaserJet MFP M630"))) { device->hack = curl_slist_append(NULL, "Host: localhost"); DBG (3, "_get_hack: finish\n"); return; } /* open configuration file */ fp = sanei_config_open (ESCL_CONFIG_FILE); if (!fp) { DBG (2, "_get_hack: couldn't access %s\n", ESCL_CONFIG_FILE); DBG (3, "_get_hack: exit\n"); } /* loop reading the configuration file, all line beginning by "option " are * parsed for value to store in configuration structure, other line are * used are device to try to attach */ while (sanei_config_read (line, PATH_MAX, fp)) { if (strstr(line, name)) { DBG (3, "_get_hack: idevice found\n"); if (strstr(line, "hack=localhost")) { DBG (3, "_get_hack: device found\n"); device->hack = curl_slist_append(NULL, "Host: localhost"); } goto finish_hack; } } finish_hack: DBG (3, "_get_hack: finish\n"); fclose(fp); } static char* _get_blacklist_pdf(void) { FILE *fp; char *blacklist = NULL; SANE_Char line[PATH_MAX]; /* open configuration file */ fp = sanei_config_open (ESCL_CONFIG_FILE); if (!fp) { DBG (2, "_get_blacklit: couldn't access %s\n", ESCL_CONFIG_FILE); DBG (3, "_get_blacklist: exit\n"); } /* loop reading the configuration file, all line beginning by "option " are * parsed for value to store in configuration structure, other line are * used are device to try to attach */ while (sanei_config_read (line, PATH_MAX, fp)) { if (!strncmp(line, "pdfblacklist", 12)) { blacklist = strdup(line); goto finish_; } } finish_: DBG (3, "_get_blacklist_pdf: finish\n"); fclose(fp); return blacklist; } /** * \fn SANE_Status sane_open(SANE_String_Const name, SANE_Handle *h) * \brief Function that establishes a connection with the device named by 'name', * and returns a 'handler' using 'SANE_Handle *h', representing it. * Thus, it's this function that calls the 'escl_status' function firstly, * then the 'escl_capabilities' function, and, after, the 'init_options' function. * * \return status (if everything is OK, status = SANE_STATUS_GOOD, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL) */ SANE_Status sane_open(SANE_String_Const name, SANE_Handle *h) { char *blacklist = NULL; DBG (10, "escl sane_open\n"); SANE_Status status; escl_sane_t *handler = NULL; if (name == NULL) return (SANE_STATUS_INVAL); ESCL_Device *device = calloc(1, sizeof(ESCL_Device)); if (device == NULL) { DBG (10, "Handle device allocation failure.\n"); return SANE_STATUS_NO_MEM; } status = escl_parse_name(name, device); if (status != SANE_STATUS_GOOD) { escl_free_device(device); return status; } handler = (escl_sane_t *)calloc(1, sizeof(escl_sane_t)); if (handler == NULL) { escl_free_device(device); return (SANE_STATUS_NO_MEM); } handler->device = device; // Handler owns device now. blacklist = _get_blacklist_pdf(); handler->scanner = escl_capabilities(device, blacklist, &status); if (status != SANE_STATUS_GOOD) { escl_free_handler(handler); return (status); } _get_hack(name, device); status = init_options(NULL, handler); if (status != SANE_STATUS_GOOD) { escl_free_handler(handler); return (status); } handler->ps.depth = 8; handler->ps.last_frame = SANE_TRUE; handler->ps.format = SANE_FRAME_RGB; handler->ps.pixels_per_line = MM_TO_PIXEL(handler->val[OPT_BR_X].w, 300.0); handler->ps.lines = MM_TO_PIXEL(handler->val[OPT_BR_Y].w, 300.0); handler->ps.bytes_per_line = handler->ps.pixels_per_line * 3; status = sane_get_parameters(handler, 0); if (status != SANE_STATUS_GOOD) { escl_free_handler(handler); return (status); } handler->cancel = SANE_FALSE; handler->write_scan_data = SANE_FALSE; handler->decompress_scan_data = SANE_FALSE; handler->end_read = SANE_FALSE; *h = handler; return (status); } /** * \fn void sane_cancel(SANE_Handle h) * \brief Function that's used to, immediately or as quickly as possible, cancel the currently * pending operation of the device represented by 'SANE_Handle h'. * This functions calls the 'escl_scanner' functions, that resets the scan operations. */ void sane_cancel(SANE_Handle h) { DBG (10, "escl sane_cancel\n"); escl_sane_t *handler = h; if (handler->scanner->tmp) { fclose(handler->scanner->tmp); handler->scanner->tmp = NULL; } handler->scanner->work = SANE_FALSE; handler->cancel = SANE_TRUE; escl_scanner(handler->device, handler->scanner->scanJob, handler->result); free(handler->result); handler->result = NULL; free(handler->scanner->scanJob); handler->scanner->scanJob = NULL; } /** * \fn void sane_close(SANE_Handle h) * \brief Function that closes the communication with the device represented by 'SANE_Handle h'. * This function must release the resources that were allocated to the opening of 'h'. */ void sane_close(SANE_Handle h) { DBG (10, "escl sane_close\n"); if (h != NULL) { escl_free_handler(h); h = NULL; } } /** * \fn const SANE_Option_Descriptor *sane_get_option_descriptor(SANE_Handle h, SANE_Int n) * \brief Function that retrieves a descriptor from the n number option of the scanner * represented by 'h'. * The descriptor remains valid until the machine is closed. * * \return s->opt + n */ const SANE_Option_Descriptor * sane_get_option_descriptor(SANE_Handle h, SANE_Int n) { DBG (10, "escl sane_get_option_descriptor\n"); escl_sane_t *s = h; if ((unsigned) n >= NUM_OPTIONS || n < 0) return (0); return (&s->opt[n]); } /** * \fn SANE_Status sane_control_option(SANE_Handle h, SANE_Int n, SANE_Action a, void *v, SANE_Int *i) * \brief Function that defines the actions to perform for the 'n' option of the machine, * represented by 'h', if the action is 'a'. * There are 3 types of possible actions : * --> SANE_ACTION_GET_VALUE: 'v' must be used to provide the value of the option. * --> SANE_ACTION_SET_VALUE: The option must take the 'v' value. * --> SANE_ACTION_SET_AUTO: The backend or machine must affect the option with an appropriate value. * Moreover, the parameter 'i' is used to provide additional information about the state of * 'n' option if SANE_ACTION_SET_VALUE has been performed. * * \return SANE_STATUS_GOOD if everything is OK, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL */ SANE_Status sane_control_option(SANE_Handle h, SANE_Int n, SANE_Action a, void *v, SANE_Int *i) { DBG (10, "escl sane_control_option\n"); escl_sane_t *handler = h; if (i) *i = 0; if (n >= NUM_OPTIONS || n < 0) return (SANE_STATUS_INVAL); if (a == SANE_ACTION_GET_VALUE) { switch (n) { case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_NUM_OPTS: case OPT_PREVIEW: case OPT_GRAY_PREVIEW: case OPT_RESOLUTION: case OPT_BRIGHTNESS: case OPT_CONTRAST: case OPT_SHARPEN: *(SANE_Word *) v = handler->val[n].w; break; case OPT_SCAN_SOURCE: case OPT_MODE: strcpy (v, handler->val[n].s); break; case OPT_MODE_GROUP: default: break; } return (SANE_STATUS_GOOD); } if (a == SANE_ACTION_SET_VALUE) { switch (n) { case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_NUM_OPTS: case OPT_PREVIEW: case OPT_GRAY_PREVIEW: case OPT_BRIGHTNESS: case OPT_CONTRAST: case OPT_SHARPEN: handler->val[n].w = *(SANE_Word *) v; if (i) *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT; break; case OPT_SCAN_SOURCE: DBG(10, "SET OPT_SCAN_SOURCE(%s)\n", (SANE_String_Const)v); init_options_small((SANE_String_Const)v, handler); if (i) *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT; break; case OPT_MODE: if (handler->val[n].s) free (handler->val[n].s); handler->val[n].s = strdup (v); if (!handler->val[n].s) { DBG (10, "OPT_MODE allocation failure.\n"); return (SANE_STATUS_NO_MEM); } DBG(10, "SET OPT_MODE(%s)\n", (SANE_String_Const)v); if (!strcasecmp(handler->val[n].s, SANE_VALUE_SCAN_MODE_GRAY)) { handler->scanner->caps[handler->scanner->source].default_color = strdup("Grayscale8"); DBG(10, "SET OPT_MODE(Grayscale8)\n"); } else if (!strcasecmp(handler->val[n].s, SANE_VALUE_SCAN_MODE_LINEART)) { handler->scanner->caps[handler->scanner->source].default_color = strdup("BlackAndWhite1"); DBG(10, "SET OPT_MODE(BlackAndWhite1)\n"); } else { handler->scanner->caps[handler->scanner->source].default_color = strdup("RGB24"); DBG(10, "SET OPT_MODE(RGB24)\n"); } DBG (10, "Color Mode allocation (%s).\n", handler->scanner->caps[handler->scanner->source].default_color); if (i) *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT; if (handler->scanner->brightness) handler->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; if (handler->scanner->contrast) handler->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; if (handler->scanner->threshold) handler->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; if (handler->scanner->sharpen) handler->opt[OPT_SHARPEN].cap |= SANE_CAP_INACTIVE; if (!strcasecmp(handler->val[n].s, SANE_VALUE_SCAN_MODE_LINEART)) { if (handler->scanner->threshold) handler->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE; } else { if (handler->scanner->brightness) handler->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; if (handler->scanner->contrast) handler->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE; if (handler->scanner->sharpen) handler->opt[OPT_SHARPEN].cap &= ~SANE_CAP_INACTIVE; } break; case OPT_RESOLUTION: handler->val[n].w = _get_resolution(handler, (int)(*(SANE_Word *) v)); handler->scanner->caps[handler->scanner->source].default_resolution = handler->val[n].w; if (i) *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT; break; default: break; } } return (SANE_STATUS_GOOD); } static SANE_Bool _go_next_page(SANE_Status status, SANE_Status job) { // Thank's Alexander Pevzner (pzz@apevzner.com) SANE_Status st = SANE_STATUS_NO_DOCS; switch (status) { case SANE_STATUS_GOOD: case SANE_STATUS_UNSUPPORTED: case SANE_STATUS_DEVICE_BUSY: { DBG(10, "eSCL : Test next page\n"); if (job != SANE_STATUS_GOOD) { DBG(10, "eSCL : Go next page\n"); st = SANE_STATUS_GOOD; } break; } default: DBG(10, "eSCL : No next page\n"); } return st; } /** * \fn SANE_Status sane_start(SANE_Handle h) * \brief Function that initiates acquisition of an image from the device represented by handle 'h'. * This function calls the "escl_newjob" function and the "escl_scan" function. * * \return status (if everything is OK, status = SANE_STATUS_GOOD, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL) */ SANE_Status sane_start(SANE_Handle h) { DBG (10, "escl sane_start\n"); SANE_Status status = SANE_STATUS_GOOD; escl_sane_t *handler = h; int w = 0; int he = 0; int bps = 0; if (handler->device == NULL) { DBG(1, "Missing handler device.\n"); return (SANE_STATUS_INVAL); } handler->cancel = SANE_FALSE; handler->write_scan_data = SANE_FALSE; handler->decompress_scan_data = SANE_FALSE; handler->end_read = SANE_FALSE; if (handler->scanner->work == SANE_FALSE) { SANE_Status st = escl_status(handler->device, handler->scanner->source, NULL, NULL); if (st != SANE_STATUS_GOOD) return st; if (handler->val[OPT_PREVIEW].w == SANE_TRUE) { int i = 0, val = 9999; if(handler->scanner->caps[handler->scanner->source].default_color) free(handler->scanner->caps[handler->scanner->source].default_color); if (handler->val[OPT_GRAY_PREVIEW].w == SANE_TRUE || !strcasecmp(handler->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY)) handler->scanner->caps[handler->scanner->source].default_color = strdup("Grayscale8"); else handler->scanner->caps[handler->scanner->source].default_color = strdup("RGB24"); if (!handler->scanner->caps[handler->scanner->source].default_color) { DBG (10, "Default Color allocation failure.\n"); return (SANE_STATUS_NO_MEM); } for (i = 1; i < handler->scanner->caps[handler->scanner->source].SupportedResolutionsSize; i++) { if (val > handler->scanner->caps[handler->scanner->source].SupportedResolutions[i]) val = handler->scanner->caps[handler->scanner->source].SupportedResolutions[i]; } handler->scanner->caps[handler->scanner->source].default_resolution = val; } else { handler->scanner->caps[handler->scanner->source].default_resolution = handler->val[OPT_RESOLUTION].w; if (!handler->scanner->caps[handler->scanner->source].default_color) { if (!strcasecmp(handler->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY)) handler->scanner->caps[handler->scanner->source].default_color = strdup("Grayscale8"); else if (!strcasecmp(handler->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART)) handler->scanner->caps[handler->scanner->source].default_color = strdup("BlackAndWhite1"); else handler->scanner->caps[handler->scanner->source].default_color = strdup("RGB24"); } } DBG (10, "Before newjob Color Mode allocation (%s).\n", handler->scanner->caps[handler->scanner->source].default_color); handler->scanner->caps[handler->scanner->source].height = MM_TO_PIXEL(handler->val[OPT_BR_Y].w, 300.0); handler->scanner->caps[handler->scanner->source].width = MM_TO_PIXEL(handler->val[OPT_BR_X].w, 300.0);; if (handler->x_range1.min == handler->val[OPT_TL_X].w) handler->scanner->caps[handler->scanner->source].pos_x = 0; else handler->scanner->caps[handler->scanner->source].pos_x = MM_TO_PIXEL((handler->val[OPT_TL_X].w - handler->x_range1.min), 300.0); if (handler->y_range1.min == handler->val[OPT_TL_X].w) handler->scanner->caps[handler->scanner->source].pos_y = 0; else handler->scanner->caps[handler->scanner->source].pos_y = MM_TO_PIXEL((handler->val[OPT_TL_Y].w - handler->y_range1.min), 300.0); DBG(10, "Calculate Size Image [%dx%d|%dx%d]\n", handler->scanner->caps[handler->scanner->source].pos_x, handler->scanner->caps[handler->scanner->source].pos_y, handler->scanner->caps[handler->scanner->source].width, handler->scanner->caps[handler->scanner->source].height); if (!handler->scanner->caps[handler->scanner->source].default_color) { DBG (10, "Default Color allocation failure.\n"); return (SANE_STATUS_NO_MEM); } if (handler->scanner->threshold) { DBG(10, "Have Thresold\n"); if (IS_ACTIVE(OPT_THRESHOLD)) { DBG(10, "Use Thresold [%d]\n", handler->val[OPT_THRESHOLD].w); handler->scanner->val_threshold = handler->val[OPT_THRESHOLD].w; handler->scanner->use_threshold = 1; } else { DBG(10, "Not use Thresold\n"); handler->scanner->use_threshold = 0; } } else DBG(10, "Don't have Thresold\n"); if (handler->scanner->sharpen) { DBG(10, "Have Sharpen\n"); if (IS_ACTIVE(OPT_SHARPEN)) { DBG(10, "Use Sharpen [%d]\n", handler->val[OPT_SHARPEN].w); handler->scanner->val_sharpen = handler->val[OPT_SHARPEN].w; handler->scanner->use_sharpen = 1; } else { DBG(10, "Not use Sharpen\n"); handler->scanner->use_sharpen = 0; } } else DBG(10, "Don't have Sharpen\n"); if (handler->scanner->contrast) { DBG(10, "Have Contrast\n"); if (IS_ACTIVE(OPT_CONTRAST)) { DBG(10, "Use Contrast [%d]\n", handler->val[OPT_CONTRAST].w); handler->scanner->val_contrast = handler->val[OPT_CONTRAST].w; handler->scanner->use_contrast = 1; } else { DBG(10, "Not use Contrast\n"); handler->scanner->use_contrast = 0; } } else DBG(10, "Don't have Contrast\n"); if (handler->scanner->brightness) { DBG(10, "Have Brightness\n"); if (IS_ACTIVE(OPT_BRIGHTNESS)) { DBG(10, "Use Brightness [%d]\n", handler->val[OPT_BRIGHTNESS].w); handler->scanner->val_brightness = handler->val[OPT_BRIGHTNESS].w; handler->scanner->use_brightness = 1; } else { DBG(10, "Not use Brightness\n"); handler->scanner->use_brightness = 0; } } else DBG(10, "Don't have Brightness\n"); handler->result = escl_newjob(handler->scanner, handler->device, &status); if (status != SANE_STATUS_GOOD) return (status); } else { SANE_Status job = SANE_STATUS_UNSUPPORTED; SANE_Status st = escl_status(handler->device, handler->scanner->source, handler->result, &job); DBG(10, "eSCL : command returned status %s\n", sane_strstatus(st)); if (_go_next_page(st, job) != SANE_STATUS_GOOD) { handler->scanner->work = SANE_FALSE; return SANE_STATUS_NO_DOCS; } } status = escl_scan(handler->scanner, handler->device, handler->scanner->scanJob, handler->result); if (status != SANE_STATUS_GOOD) return (status); if (!strcmp(handler->scanner->caps[handler->scanner->source].default_format, "image/jpeg")) { status = get_JPEG_data(handler->scanner, &w, &he, &bps); } else if (!strcmp(handler->scanner->caps[handler->scanner->source].default_format, "image/png")) { status = get_PNG_data(handler->scanner, &w, &he, &bps); } else if (!strcmp(handler->scanner->caps[handler->scanner->source].default_format, "image/tiff")) { status = get_TIFF_data(handler->scanner, &w, &he, &bps); } else if (!strcmp(handler->scanner->caps[handler->scanner->source].default_format, "application/pdf")) { status = get_PDF_data(handler->scanner, &w, &he, &bps); } else { DBG(10, "Unknown image format\n"); return SANE_STATUS_INVAL; } DBG(10, "2-Size Image (%ld)[%dx%d|%dx%d]\n", handler->scanner->img_size, 0, 0, w, he); if (status != SANE_STATUS_GOOD) return (status); handler->ps.depth = 8; handler->ps.pixels_per_line = w; handler->ps.lines = he; handler->ps.bytes_per_line = w * bps; handler->ps.last_frame = SANE_TRUE; handler->ps.format = SANE_FRAME_RGB; handler->scanner->work = SANE_FALSE; // DBG(10, "NEXT Frame [%s]\n", (handler->ps.last_frame ? "Non" : "Oui")); DBG(10, "Real Size Image [%dx%d|%dx%d]\n", 0, 0, w, he); return (status); } /** * \fn SANE_Status sane_get_parameters(SANE_Handle h, SANE_Parameters *p) * \brief Function that retrieves the device parameters represented by 'h' and stores them in 'p'. * This function is normally used after "sane_start". * It's in this function that we choose to assign the default color. (Color or Monochrome) * * \return status (if everything is OK, status = SANE_STATUS_GOOD, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL) */ SANE_Status sane_get_parameters(SANE_Handle h, SANE_Parameters *p) { DBG (10, "escl sane_get_parameters\n"); SANE_Status status = SANE_STATUS_GOOD; escl_sane_t *handler = h; if (status != SANE_STATUS_GOOD) return (status); if (p != NULL) { p->depth = 8; p->last_frame = handler->ps.last_frame; p->format = SANE_FRAME_RGB; p->pixels_per_line = handler->ps.pixels_per_line; p->lines = handler->ps.lines; p->bytes_per_line = handler->ps.bytes_per_line; } return (status); } /** * \fn SANE_Status sane_read(SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *len) * \brief Function that's used to read image data from the device represented by handle 'h'. * The 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'. * --> When the call succeeds, the number of bytes returned can be anywhere in the range from 0 to 'maxlen' bytes. * * \return SANE_STATUS_GOOD (if everything is OK, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL) */ SANE_Status sane_read(SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *len) { DBG (10, "escl sane_read\n"); escl_sane_t *handler = h; SANE_Status status = SANE_STATUS_GOOD; long readbyte; if (!handler | !buf | !len) return (SANE_STATUS_INVAL); if (handler->cancel) return (SANE_STATUS_CANCELLED); if (!handler->write_scan_data) handler->write_scan_data = SANE_TRUE; if (!handler->decompress_scan_data) { if (status != SANE_STATUS_GOOD) return (status); handler->decompress_scan_data = SANE_TRUE; } if (handler->scanner->img_data == NULL) return (SANE_STATUS_INVAL); if (!handler->end_read) { readbyte = min((handler->scanner->img_size - handler->scanner->img_read), maxlen); memcpy(buf, handler->scanner->img_data + handler->scanner->img_read, readbyte); handler->scanner->img_read = handler->scanner->img_read + readbyte; *len = readbyte; if (handler->scanner->img_read == handler->scanner->img_size) handler->end_read = SANE_TRUE; else if (handler->scanner->img_read > handler->scanner->img_size) { *len = 0; handler->end_read = SANE_TRUE; free(handler->scanner->img_data); handler->scanner->img_data = NULL; return (SANE_STATUS_INVAL); } } else { SANE_Status job = SANE_STATUS_UNSUPPORTED; *len = 0; free(handler->scanner->img_data); handler->scanner->img_data = NULL; if (handler->scanner->source != PLATEN) { SANE_Bool next_page = SANE_FALSE; SANE_Status st = escl_status(handler->device, handler->scanner->source, handler->result, &job); DBG(10, "eSCL : command returned status %s\n", sane_strstatus(st)); if (_go_next_page(st, job) == SANE_STATUS_GOOD) next_page = SANE_TRUE; handler->scanner->work = SANE_TRUE; handler->ps.last_frame = !next_page; } return SANE_STATUS_EOF; } return (SANE_STATUS_GOOD); } SANE_Status sane_get_select_fd(SANE_Handle __sane_unused__ h, SANE_Int __sane_unused__ *fd) { return (SANE_STATUS_UNSUPPORTED); } SANE_Status sane_set_io_mode(SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking) { return (SANE_STATUS_UNSUPPORTED); } /** * \fn void escl_curl_url(CURL *handle, const ESCL_Device *device, SANE_String_Const path) * \brief Uses the device info in 'device' and the path from 'path' to construct * a full URL. Sets this URL and any necessary connection options into * 'handle'. */ void escl_curl_url(CURL *handle, const ESCL_Device *device, SANE_String_Const path) { int url_len; char *url; url_len = snprintf(NULL, 0, "%s://%s:%d%s", (device->https ? "https" : "http"), device->ip_address, device->port_nb, path); url_len++; url = (char *)malloc(url_len); snprintf(url, url_len, "%s://%s:%d%s", (device->https ? "https" : "http"), device->ip_address, device->port_nb, path); DBG( 1, "escl_curl_url: URL: %s\n", url ); curl_easy_setopt(handle, CURLOPT_URL, url); free(url); DBG( 1, "Before use hack\n"); if (device->hack) { DBG( 1, "Use hack\n"); curl_easy_setopt(handle, CURLOPT_HTTPHEADER, device->hack); } DBG( 1, "After use hack\n"); if (device->https) { DBG( 1, "Ignoring safety certificates, use https\n"); curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 0L); if (device->tls > 0) curl_easy_setopt(handle, CURLOPT_SSLVERSION, device->tls); } if (device->unix_socket != NULL) { DBG( 1, "Using local socket %s\n", device->unix_socket ); curl_easy_setopt(handle, CURLOPT_UNIX_SOCKET_PATH, device->unix_socket); } } backends-1.3.0/backend/escl/escl.h000066400000000000000000000161751456256263500167310ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Touboul Nathane Copyright (C) 2019 Thierry HUCHARD This file is part of the SANE package. SANE 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 3 of the License, or (at your option) any later version. SANE 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 sane; see the file COPYING. If not, see . This file implements a SANE backend for eSCL scanners. */ #ifndef __ESCL_H__ #define __ESCL_H__ #include "../include/sane/config.h" #if !(HAVE_LIBCURL && defined(WITH_AVAHI) && defined(HAVE_LIBXML2)) #error "The escl backend requires libcurl, libavahi and libxml2" #endif #ifndef HAVE_LIBJPEG /* FIXME: Make JPEG support optional. Support for PNG and PDF is to be added later but currently only JPEG is supported. Absence of JPEG support makes the backend a no-op at present. */ #error "The escl backend currently requires libjpeg" #endif #include "../include/sane/sane.h" #include #include #include #ifndef BACKEND_NAME #define BACKEND_NAME escl #endif #define DEBUG_NOT_STATIC #include "../include/sane/sanei_debug.h" #ifndef DBG_LEVEL #define DBG_LEVEL PASTE(sanei_debug_, BACKEND_NAME) #endif #ifndef NDEBUG # define DBGDUMP(level, buf, size) \ do { if (DBG_LEVEL >= (level)) sanei_escl_dbgdump(buf, size); } while (0) #else # define DBGDUMP(level, buf, size) #endif #define ESCL_CONFIG_FILE "escl.conf" enum { PLATEN = 0, ADFSIMPLEX, ADFDUPLEX }; typedef struct { int p1_0; int p2_0; int p3_3; int DocumentType; int p4_0; int p5_0; int p6_1; int reserve[11]; } ESCL_SCANOPTS; typedef struct ESCL_Device { struct ESCL_Device *next; char *version; char *model_name; int port_nb; char *ip_address; char *is; int tls; char *uuid; char *type; SANE_Bool https; struct curl_slist *hack; char *unix_socket; } ESCL_Device; typedef struct capst { int height; int width; int pos_x; int pos_y; SANE_String default_color; SANE_String default_format; SANE_Int default_resolution; int MinWidth; int MaxWidth; int MinHeight; int MaxHeight; int MaxScanRegions; SANE_String_Const *ColorModes; int ColorModesSize; SANE_String_Const *ContentTypes; int ContentTypesSize; SANE_String_Const *DocumentFormats; int DocumentFormatsSize; int format_ext; SANE_Int *SupportedResolutions; int SupportedResolutionsSize; SANE_String_Const *SupportedIntents; int SupportedIntentsSize; SANE_String_Const SupportedIntentDefault; int MaxOpticalXResolution; int RiskyLeftMargin; int RiskyRightMargin; int RiskyTopMargin; int RiskyBottomMargin; int duplex; int have_jpeg; int have_png; int have_tiff; int have_pdf; } caps_t; typedef struct support { int min; int max; int normal; int value; int step; } support_t; typedef struct capabilities { caps_t caps[3]; int source; SANE_String_Const *Sources; int SourcesSize; FILE *tmp; char *scanJob; unsigned char *img_data; long img_size; long img_read; size_t real_read; SANE_Bool work; support_t *brightness; support_t *contrast; support_t *sharpen; support_t *threshold; int use_brightness; int val_brightness; int use_contrast; int val_contrast; int use_sharpen; int val_sharpen; int use_threshold; int val_threshold; } capabilities_t; typedef struct { int XRes; int YRes; int Left; int Top; int Right; int Bottom; int ScanMode; int ScanMethod; ESCL_SCANOPTS opts; } ESCL_ScanParam; enum { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_MODE, OPT_RESOLUTION, OPT_SCAN_SOURCE, OPT_GEOMETRY_GROUP, OPT_TL_X, OPT_TL_Y, OPT_BR_X, OPT_BR_Y, OPT_ENHANCEMENT_GROUP, OPT_PREVIEW, OPT_GRAY_PREVIEW, OPT_BRIGHTNESS, OPT_CONTRAST, OPT_SHARPEN, OPT_THRESHOLD, NUM_OPTIONS }; #define PIXEL_TO_MM(pixels, dpi) SANE_FIX((double)pixels * 25.4 / (dpi)) #define MM_TO_PIXEL(millimeters, dpi) (SANE_Word)round(SANE_UNFIX(millimeters) * (dpi) / 25.4) ESCL_Device *escl_devices(SANE_Status *status); SANE_Status escl_device_add(int port_nb, const char *model_name, char *ip_address, const char *is, const char *uuid, char *type); SANE_Status escl_status(const ESCL_Device *device, int source, const char* jobId, SANE_Status *job); capabilities_t *escl_capabilities(ESCL_Device *device, char *blacklist, SANE_Status *status); char *escl_newjob(capabilities_t *scanner, const ESCL_Device *device, SANE_Status *status); SANE_Status escl_scan(capabilities_t *scanner, const ESCL_Device *device, char *scanJob, char *result); void escl_scanner(const ESCL_Device *device, char *scanJob, char *result); typedef void CURL; void escl_curl_url(CURL *handle, const ESCL_Device *device, SANE_String_Const path); unsigned char *escl_crop_surface(capabilities_t *scanner, unsigned char *surface, int w, int h, int bps, int *width, int *height); // JPEG SANE_Status get_JPEG_data(capabilities_t *scanner, int *width, int *height, int *bps); // PNG SANE_Status get_PNG_data(capabilities_t *scanner, int *width, int *height, int *bps); // TIFF SANE_Status get_TIFF_data(capabilities_t *scanner, int *width, int *height, int *bps); // PDF SANE_Status get_PDF_data(capabilities_t *scanner, int *width, int *height, int *bps); #endif backends-1.3.0/backend/escl/escl_capabilities.c000066400000000000000000000550471456256263500214360ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Touboul Nathane Copyright (C) 2019 Thierry HUCHARD This file is part of the SANE package. SANE 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 3 of the License, or (at your option) any later version. SANE 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 sane; see the file COPYING. If not, see . This file implements a SANE backend for eSCL scanners. */ #define DEBUG_DECLARE_ONLY #include "../include/sane/config.h" #include "escl.h" #include #include #include #include #include "../include/sane/saneopts.h" struct cap { char *memory; size_t size; }; static size_t header_callback(void *str, size_t size, size_t nmemb, void *userp) { struct cap *header = (struct cap *)userp; size_t realsize = size * nmemb; char *content = realloc(header->memory, header->size + realsize + 1); if (content == NULL) { DBG( 1, "Not enough memory (realloc returned NULL)\n"); return (0); } header->memory = content; memcpy(&(header->memory[header->size]), str, realsize); header->size = header->size + realsize; header->memory[header->size] = 0; return (realsize); } /** * \fn static SANE_String_Const convert_elements(SANE_String_Const str) * \brief Function that converts the 'color modes' of the scanner (color/gray) to be understood by SANE. * * \return SANE_VALUE_SCAN_MODE_GRAY / SANE_VALUE_SCAN_MODE_COLOR / SANE_VALUE_SCAN_MODE_LINEART; NULL otherwise */ static SANE_String_Const convert_elements(SANE_String_Const str) { if (strcmp(str, "Grayscale8") == 0) return (SANE_VALUE_SCAN_MODE_GRAY); else if (strcmp(str, "RGB24") == 0) return (SANE_VALUE_SCAN_MODE_COLOR); #if HAVE_POPPLER_GLIB else if (strcmp(str, "BlackAndWhite1") == 0) return (SANE_VALUE_SCAN_MODE_LINEART); #endif return (NULL); } /** * \fn static SANE_String_Const *char_to_array(SANE_String_Const *tab, int *tabsize, SANE_String_Const mode, int good_array) * \brief Function that creates the character arrays to put inside : * the 'color modes', the 'content types', the 'document formats' and the 'supported intents'. * * \return board (the allocated array) */ static SANE_String_Const * char_to_array(SANE_String_Const *tab, int *tabsize, SANE_String_Const mode, int good_array) { SANE_String_Const *board = NULL; int i = 0; SANE_String_Const convert = NULL; if (mode == NULL) return (tab); if (good_array != 0) { convert = convert_elements(mode); if (convert == NULL) return (tab); } else convert = mode; for (i = 0; i < (*tabsize); i++) { if (strcmp(tab[i], convert) == 0) return (tab); } (*tabsize)++; if (*tabsize == 1) board = (SANE_String_Const *)malloc(sizeof(SANE_String_Const) * ((*tabsize) + 1)); else board = (SANE_String_Const *)realloc(tab, sizeof(SANE_String_Const) * ((*tabsize) + 1)); board[*tabsize - 1] = (SANE_String_Const)strdup(convert); board[*tabsize] = NULL; return (board); } /** * \fn static SANE_Int *int_to_array(SANE_Int *tab, int *tabsize, int cont) * \brief Function that creates the integer array to put inside the 'supported resolutions'. * * \return board (the allocated array) */ static SANE_Int * int_to_array(SANE_Int *tab, int *tabsize, int cont) { SANE_Int *board = NULL; int i = 0; for (i = 0; i < (*tabsize); i++) { if (tab[i] == cont) return (tab); } (*tabsize)++; if (*tabsize == 1) { (*tabsize)++; board = malloc(sizeof(SANE_Int *) * (*tabsize) + 1); } else board = realloc(tab, sizeof(SANE_Int *) * (*tabsize) + 1); board[0] = *tabsize - 1; board[*tabsize - 1] = cont; board[*tabsize] = -1; return (board); } /** * \fn static size_t memory_callback_c(void *contents, size_t size, size_t nmemb, void *userp) * \brief Callback function that stocks in memory the content of the scanner capabilities. * * \return realsize (size of the content needed -> the scanner capabilities) */ static size_t memory_callback_c(void *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; struct cap *mem = (struct cap *)userp; char *str = realloc(mem->memory, mem->size + realsize + 1); if (str == NULL) { DBG(10, "not enough memory (realloc returned NULL)\n"); return (0); } mem->memory = str; memcpy(&(mem->memory[mem->size]), contents, realsize); mem->size = mem->size + realsize; mem->memory[mem->size] = 0; return (realsize); } /** * \fn static int find_nodes_c(xmlNode *node) * \brief Function that browses the xml file and parses it, to find the xml children node. * --> to recover the scanner capabilities. * * \return 0 if a xml child node is found, 1 otherwise */ static int find_nodes_c(xmlNode *node) { xmlNode *child = node->children; while (child) { if (child->type == XML_ELEMENT_NODE) return (0); child = child->next; } return (1); } /** * \fn static int find_valor_of_array_variables(xmlNode *node, capabilities_t *scanner) * \brief Function that searches in the xml file if a scanner capabilitie stocked * in one of the created array (character/integer array) is found. * * \return 0 */ static int find_valor_of_array_variables(xmlNode *node, capabilities_t *scanner, int type) { const char *name = (const char *)node->name; if (strcmp(name, "ColorMode") == 0) { const char *color = (SANE_String_Const)xmlNodeGetContent(node); #if HAVE_POPPLER_GLIB if (type == PLATEN || strcmp(color, "BlackAndWhite1")) #else if (strcmp(color, "BlackAndWhite1")) #endif scanner->caps[type].ColorModes = char_to_array(scanner->caps[type].ColorModes, &scanner->caps[type].ColorModesSize, (SANE_String_Const)xmlNodeGetContent(node), 1); } else if (strcmp(name, "ContentType") == 0) scanner->caps[type].ContentTypes = char_to_array(scanner->caps[type].ContentTypes, &scanner->caps[type].ContentTypesSize, (SANE_String_Const)xmlNodeGetContent(node), 0); else if (strcmp(name, "DocumentFormat") == 0) { int i = 0; SANE_Bool have_jpeg = SANE_FALSE, have_png = SANE_FALSE, have_tiff = SANE_FALSE, have_pdf = SANE_FALSE; scanner->caps[type].DocumentFormats = char_to_array(scanner->caps[type].DocumentFormats, &scanner->caps[type].DocumentFormatsSize, (SANE_String_Const)xmlNodeGetContent(node), 0); scanner->caps[type].have_jpeg = -1; scanner->caps[type].have_png = -1; scanner->caps[type].have_tiff = -1; scanner->caps[type].have_pdf = -1; for(; i < scanner->caps[type].DocumentFormatsSize; i++) { if (!strcmp(scanner->caps[type].DocumentFormats[i], "image/jpeg")) { have_jpeg = SANE_TRUE; scanner->caps[type].have_jpeg = i; } #if(defined HAVE_LIBPNG) else if(!strcmp(scanner->caps[type].DocumentFormats[i], "image/png")) { have_png = SANE_TRUE; scanner->caps[type].have_png = i; } #endif #if(defined HAVE_TIFFIO_H) else if(type == PLATEN && !strcmp(scanner->caps[type].DocumentFormats[i], "image/tiff")) { have_tiff = SANE_TRUE; scanner->caps[type].have_tiff = i; } #endif #if HAVE_POPPLER_GLIB else if(type == PLATEN && !strcmp(scanner->caps[type].DocumentFormats[i], "application/pdf")) { have_pdf = SANE_TRUE; scanner->caps[type].have_pdf = i; } #endif } if (have_pdf) scanner->caps[type].default_format = strdup("application/pdf"); else if (have_tiff) scanner->caps[type].default_format = strdup("image/tiff"); else if (have_png) scanner->caps[type].default_format = strdup("image/png"); else if (have_jpeg) scanner->caps[type].default_format = strdup("image/jpeg"); } else if (strcmp(name, "DocumentFormatExt") == 0) scanner->caps[type].format_ext = 1; else if (strcmp(name, "Intent") == 0) scanner->caps[type].SupportedIntents = char_to_array(scanner->caps[type].SupportedIntents, &scanner->caps[type].SupportedIntentsSize, (SANE_String_Const)xmlNodeGetContent(node), 0); else if (strcmp(name, "XResolution") == 0) scanner->caps[type].SupportedResolutions = int_to_array(scanner->caps[type].SupportedResolutions, &scanner->caps[type].SupportedResolutionsSize, atoi((const char *)xmlNodeGetContent(node))); return (0); } /** * \fn static int find_value_of_int_variables(xmlNode *node, capabilities_t *scanner) * \brief Function that searches in the xml file if a integer scanner capabilitie is found. * The integer scanner capabilities that are interesting are : * MinWidth, MaxWidth, MaxHeight, MinHeight, MaxScanRegions, MaxOpticalXResolution, * RiskyLeftMargin, RiskyRightMargin, RiskyTopMargin, RiskyBottomMargin. * * \return 0 */ static int find_value_of_int_variables(xmlNode *node, capabilities_t *scanner, int type) { int MaxWidth = 0; int MaxHeight = 0; const char *name = (const char *)node->name; if (strcmp(name, "MinWidth") == 0) scanner->caps[type].MinWidth = atoi((const char*)xmlNodeGetContent(node)); else if (strcmp(name, "MaxWidth") == 0) { MaxWidth = atoi((const char*)xmlNodeGetContent(node)); if (scanner->caps[type].MaxWidth == 0 || MaxWidth < scanner->caps[type].MaxWidth) scanner->caps[type].MaxWidth = atoi((const char *)xmlNodeGetContent(node)); } else if (strcmp(name, "MinHeight") == 0) scanner->caps[type].MinHeight = atoi((const char*)xmlNodeGetContent(node)); else if (strcmp(name, "MaxHeight") == 0) { MaxHeight = atoi((const char*)xmlNodeGetContent(node)); if (scanner->caps[type].MaxHeight == 0 || MaxHeight < scanner->caps[type].MaxHeight) scanner->caps[type].MaxHeight = atoi((const char *)xmlNodeGetContent(node)); } else if (strcmp(name, "MaxScanRegions") == 0) scanner->caps[type].MaxScanRegions = atoi((const char *)xmlNodeGetContent(node)); else if (strcmp(name, "MaxOpticalXResolution") == 0) scanner->caps[type].MaxOpticalXResolution = atoi((const char *)xmlNodeGetContent(node)); else if (strcmp(name, "RiskyLeftMargin") == 0) scanner->caps[type].RiskyLeftMargin = atoi((const char *)xmlNodeGetContent(node)); else if (strcmp(name, "RiskyRightMargin") == 0) scanner->caps[type].RiskyRightMargin = atoi((const char *)xmlNodeGetContent(node)); else if (strcmp(name, "RiskyTopMargin") == 0) scanner->caps[type].RiskyTopMargin = atoi((const char *)xmlNodeGetContent(node)); else if (strcmp(name, "RiskyBottomMargin") == 0) scanner->caps[type].RiskyBottomMargin = atoi((const char *)xmlNodeGetContent(node)); find_valor_of_array_variables(node, scanner, type); return (0); } static support_t* print_support(xmlNode *node) { support_t *sup = (support_t*)calloc(1, sizeof(support_t)); int cpt = 0; int have_norm = 0; while (node) { if (!strcmp((const char *)node->name, "Min")){ sup->min = atoi((const char *)xmlNodeGetContent(node)); cpt++; } else if (!strcmp((const char *)node->name, "Max")) { sup->max = atoi((const char *)xmlNodeGetContent(node)); cpt++; } else if (!strcmp((const char *)node->name, "Normal")) { sup->value = atoi((const char *)xmlNodeGetContent(node)); sup->normal = sup->value; cpt++; have_norm = 1; } else if (!strcmp((const char *)node->name, "Step")) { sup->step = atoi((const char *)xmlNodeGetContent(node)); cpt++; } node = node->next; } if (cpt == 4) return sup; if (cpt == 3 && have_norm == 0) { sup->value = (sup->max / 2 ); sup->normal = sup->value; return sup; } free(sup); return NULL; } static int find_struct_variables(xmlNode *node, capabilities_t *scanner) { const char *name = (const char *)node->name; if (strcmp(name, "BrightnessSupport") == 0) { scanner->brightness = print_support(node->children); return 1; } else if (strcmp(name, "ContrastSupport") == 0) { scanner->contrast = print_support(node->children); return 1; } else if (strcmp(name, "SharpenSupport") == 0) { scanner->sharpen = print_support(node->children); return 1; } else if (strcmp(name, "ThresholdSupport") == 0) { scanner->threshold = print_support(node->children); return 1; } return (0); } /** * \fn static int find_true_variables(xmlNode *node, capabilities_t *scanner) * \brief Function that searches in the xml file if we find a scanner capability stored * in one of the created array (character/integer array), * or, if we find a integer scanner capability. * * \return 0 */ static int find_true_variables(xmlNode *node, capabilities_t *scanner, int type) { const char *name = (const char *)node->name; if (strcmp(name, "MinWidth") == 0 || strcmp(name, "MaxWidth") == 0 || strcmp(name, "MinHeight") == 0 || strcmp(name, "MaxHeight") == 0 || strcmp(name, "MaxScanRegions") == 0 || strcmp(name, "ColorMode") == 0 || strcmp(name, "ContentType") == 0 || strcmp(name, "DocumentFormat") == 0 || strcmp(name, "XResolution") == 0 || strcmp(name, "Intent") == 0 || strcmp(name, "MaxOpticalXResolution") == 0 || strcmp(name, "RiskyLeftMargin") == 0 || strcmp(name, "RiskyRightMargin") == 0 || strcmp(name, "RiskyTopMargin") == 0 || strcmp(name, "RiskyBottomMargin") == 0 || strcmp(name, "DocumentFormatExt") == 0) find_value_of_int_variables(node, scanner, type); return (0); } static char* replace_char(char* str, char find, char replace){ char *current_pos = strchr(str,find); while (current_pos) { *current_pos = replace; current_pos = strchr(current_pos,find); } return str; } /** * \fn static int print_xml_c(xmlNode *node, capabilities_t *scanner) * \brief Function that browses the xml file, node by node. * * \return 0 */ static int print_xml_c(xmlNode *node, ESCL_Device *device, capabilities_t *scanner, int type) { while (node) { if (node->type == XML_ELEMENT_NODE) { if (find_nodes_c(node) && type != -1) find_true_variables(node, scanner, type); } if (!strcmp((const char *)node->name, "Version")&& node->ns && node->ns->prefix){ if (!strcmp((const char*)node->ns->prefix, "pwg")) device->version = strdup((const char *)xmlNodeGetContent(node)); } if (!strcmp((const char *)node->name, "MakeAndModel")){ device->model_name = strdup((const char *)xmlNodeGetContent(node)); } else if (!strcmp((const char *)node->name, "PlatenInputCaps")) { scanner->Sources[PLATEN] = (SANE_String_Const)strdup(SANE_I18N ("Flatbed")); scanner->SourcesSize++; scanner->source = PLATEN; print_xml_c(node->children, device, scanner, PLATEN); scanner->caps[PLATEN].duplex = 0; } else if (!strcmp((const char *)node->name, "AdfSimplexInputCaps")) { scanner->Sources[ADFSIMPLEX] = (SANE_String_Const)strdup(SANE_I18N("ADF")); scanner->SourcesSize++; if (scanner->source == -1) scanner->source = ADFSIMPLEX; print_xml_c(node->children, device, scanner, ADFSIMPLEX); scanner->caps[ADFSIMPLEX].duplex = 0; } else if (!strcmp((const char *)node->name, "AdfDuplexInputCaps")) { scanner->Sources[ADFDUPLEX] = (SANE_String_Const)strdup(SANE_I18N ("ADF Duplex")); scanner->SourcesSize++; if (scanner->source == -1) scanner->source = ADFDUPLEX; print_xml_c(node->children, device, scanner, ADFDUPLEX); scanner->caps[ADFDUPLEX].duplex = 1; } else if (find_struct_variables(node, scanner) == 0) print_xml_c(node->children, device, scanner, type); node = node->next; } return (0); } static void _reduce_color_modes(capabilities_t *scanner) { int type = 0; for (type = 0; type < 3; type++) { if (scanner->caps[type].ColorModesSize) { if (scanner->caps[type].default_format && strcmp(scanner->caps[type].default_format, "application/pdf")) { if (scanner->caps[type].ColorModesSize == 3) { free(scanner->caps[type].ColorModes); scanner->caps[type].ColorModes = NULL; scanner->caps[type].ColorModesSize = 0; scanner->caps[type].ColorModes = char_to_array(scanner->caps[type].ColorModes, &scanner->caps[type].ColorModesSize, (SANE_String_Const)SANE_VALUE_SCAN_MODE_GRAY, 0); scanner->caps[type].ColorModes = char_to_array(scanner->caps[type].ColorModes, &scanner->caps[type].ColorModesSize, (SANE_String_Const)SANE_VALUE_SCAN_MODE_COLOR, 0); } } } } } static void _delete_pdf(capabilities_t *scanner) { int type = 0; for (type = 0; type < 3; type++) { if (scanner->caps[type].ColorModesSize) { if (scanner->caps[type].default_format) { scanner->caps[type].have_pdf = -1; if (!strcmp(scanner->caps[type].default_format, "application/pdf")) { free(scanner->caps[type].default_format); if (scanner->caps[type].have_tiff > -1) scanner->caps[type].default_format = strdup("image/tiff"); else if (scanner->caps[type].have_png > -1) scanner->caps[type].default_format = strdup("image/png"); else if (scanner->caps[type].have_jpeg > -1) scanner->caps[type].default_format = strdup("image/jpeg"); } free(scanner->caps[type].ColorModes); scanner->caps[type].ColorModes = NULL; scanner->caps[type].ColorModesSize = 0; scanner->caps[type].ColorModes = char_to_array(scanner->caps[type].ColorModes, &scanner->caps[type].ColorModesSize, (SANE_String_Const)SANE_VALUE_SCAN_MODE_GRAY, 0); scanner->caps[type].ColorModes = char_to_array(scanner->caps[type].ColorModes, &scanner->caps[type].ColorModesSize, (SANE_String_Const)SANE_VALUE_SCAN_MODE_COLOR, 0); } } } } /** * \fn capabilities_t *escl_capabilities(const ESCL_Device *device, SANE_Status *status) * \brief Function that finally recovers all the capabilities of the scanner, using curl. * This function is called in the 'sane_open' function and it's the equivalent of * the following curl command : "curl http(s)://'ip':'port'/eSCL/ScannerCapabilities". * * \return scanner (the structure that stocks all the capabilities elements) */ capabilities_t * escl_capabilities(ESCL_Device *device, char *blacklist, SANE_Status *status) { capabilities_t *scanner = (capabilities_t*)calloc(1, sizeof(capabilities_t)); CURL *curl_handle = NULL; struct cap *var = NULL; struct cap *header = NULL; xmlDoc *data = NULL; xmlNode *node = NULL; int i = 0; const char *scanner_capabilities = "/eSCL/ScannerCapabilities"; SANE_Bool use_pdf = SANE_TRUE; *status = SANE_STATUS_GOOD; if (device == NULL) *status = SANE_STATUS_NO_MEM; var = (struct cap *)calloc(1, sizeof(struct cap)); if (var == NULL) *status = SANE_STATUS_NO_MEM; var->memory = malloc(1); var->size = 0; header = (struct cap *)calloc(1, sizeof(struct cap)); if (header == NULL) *status = SANE_STATUS_NO_MEM; header->memory = malloc(1); header->size = 0; curl_handle = curl_easy_init(); escl_curl_url(curl_handle, device, scanner_capabilities); curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, memory_callback_c); curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)var); curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, header_callback); curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void *)header); curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 3L); CURLcode res = curl_easy_perform(curl_handle); if (res == CURLE_OK) DBG( 1, "Create NewJob : the scanner header responded : [%s]\n", header->memory); if (res != CURLE_OK) { DBG( 1, "The scanner didn't respond: %s\n", curl_easy_strerror(res)); *status = SANE_STATUS_INVAL; goto clean_data; } DBG( 10, "XML Capabilities[\n%s\n]\n", var->memory); data = xmlReadMemory(var->memory, var->size, "file.xml", NULL, 0); if (data == NULL) { *status = SANE_STATUS_NO_MEM; goto clean_data; } node = xmlDocGetRootElement(data); if (node == NULL) { *status = SANE_STATUS_NO_MEM; goto clean; } if (device->hack && header && header->memory && strstr(header->memory, "Server: HP_Compact_Server")) device->hack = curl_slist_append(NULL, "Host: localhost"); scanner->source = 0; scanner->Sources = (SANE_String_Const *)malloc(sizeof(SANE_String_Const) * 4); for (i = 0; i < 4; i++) scanner->Sources[i] = NULL; print_xml_c(node, device, scanner, -1); DBG (3, "1-blacklist_pdf: %s\n", (use_pdf ? "TRUE" : "FALSE") ); if (device->model_name != NULL) { if (strcasestr(device->model_name, "MFC-J985DW")) { DBG (3, "blacklist_pdf: device not support PDF\n"); use_pdf = SANE_FALSE; } else if (blacklist) { char *model = strdup(device->model_name); replace_char(model, ' ', '_'); if (strcasestr(blacklist, model)) { use_pdf = SANE_FALSE; } free(model); } } DBG (3, "1-blacklist_pdf: %s\n", (use_pdf ? "TRUE" : "FALSE") ); if (use_pdf) _reduce_color_modes(scanner); else _delete_pdf(scanner); clean: xmlFreeDoc(data); clean_data: xmlCleanupParser(); xmlMemoryDump(); curl_easy_cleanup(curl_handle); if (header) free(header->memory); free(header); if (var) free(var->memory); free(var); return (scanner); } backends-1.3.0/backend/escl/escl_crop.c000066400000000000000000000072121456256263500177370ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2020 Thierry HUCHARD This file is part of the SANE package. SANE 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 3 of the License, or (at your option) any later version. SANE 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 sane; see the file COPYING. If not, see . This file implements a SANE backend for eSCL scanners. */ #define DEBUG_DECLARE_ONLY #include "../include/sane/config.h" #include "escl.h" #include #include #include unsigned char * escl_crop_surface(capabilities_t *scanner, unsigned char *surface, int w, int h, int bps, int *width, int *height) { double ratio = 1.0; int x_off = 0, x = 0; int real_w = 0; int y_off = 0, y = 0; int real_h = 0; unsigned char *surface_crop = NULL; DBG( 1, "Escl Image Crop\n"); ratio = (double)w / (double)scanner->caps[scanner->source].width; scanner->caps[scanner->source].width = w; if (scanner->caps[scanner->source].pos_x < 0) scanner->caps[scanner->source].pos_x = 0; if (scanner->caps[scanner->source].pos_x && (scanner->caps[scanner->source].width > scanner->caps[scanner->source].pos_x)) x_off = (int)((double)scanner->caps[scanner->source].pos_x * ratio); real_w = scanner->caps[scanner->source].width - x_off; scanner->caps[scanner->source].height = h; if (scanner->caps[scanner->source].pos_y && (scanner->caps[scanner->source].height > scanner->caps[scanner->source].pos_y)) y_off = (int)((double)scanner->caps[scanner->source].pos_y * ratio); real_h = scanner->caps[scanner->source].height - y_off; DBG( 1, "Escl Image Crop [%dx%d|%dx%d]\n", scanner->caps[scanner->source].pos_x, scanner->caps[scanner->source].pos_y, scanner->caps[scanner->source].width, scanner->caps[scanner->source].height); *width = real_w; *height = real_h; DBG( 1, "Escl Image Crop [%dx%d]\n", *width, *height); if (x_off > 0 || real_w < scanner->caps[scanner->source].width || y_off > 0 || real_h < scanner->caps[scanner->source].height) { surface_crop = (unsigned char *)malloc (sizeof (unsigned char) * real_w * real_h * bps); if(!surface_crop) { DBG( 1, "Escl Crop : Surface_crop Memory allocation problem\n"); free(surface); surface = NULL; goto finish; } for (y = 0; y < real_h; y++) { for (x = 0; x < real_w; x++) { surface_crop[(y * real_w * bps) + (x * bps)] = surface[((y + y_off) * w * bps) + ((x + x_off) * bps)]; surface_crop[(y * real_w * bps) + (x * bps) + 1] = surface[((y + y_off) * w * bps) + ((x + x_off) * bps) + 1]; surface_crop[(y * real_w * bps) + (x * bps) + 2] = surface[((y + y_off) * w * bps) + ((x + x_off) * bps) + 2]; } } free(surface); surface = surface_crop; } // we don't need row pointers anymore scanner->img_data = surface; scanner->img_size = (int)(real_w * real_h * bps); scanner->img_read = 0; finish: return surface; } backends-1.3.0/backend/escl/escl_devices.c000066400000000000000000000205251456256263500204200ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Touboul Nathane Copyright (C) 2019 Thierry HUCHARD This file is part of the SANE package. SANE 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 3 of the License, or (at your option) any later version. SANE 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 sane; see the file COPYING. If not, see . This file implements a SANE backend for eSCL scanners. */ #define DEBUG_DECLARE_ONLY #include "../include/sane/config.h" #include "escl.h" #include #include #include #include #include #include #include #include #include "../include/sane/sanei.h" static AvahiSimplePoll *simple_poll = NULL; static int count_finish = 0; /** * \fn static void resolve_callback(AvahiServiceResolver *r, AVAHI_GCC_UNUSED * AvahiIfIndex interface, AVAHI_GCC_UNUSED AvahiProtocol protocol, * AvahiResolverEvent event, const char *name, * const char *type, const char *domain, const char *host_name, * const AvahiAddress *address, uint16_t port, * AvahiStringList *txt, AvahiLookupResultFlags flags, * void *userdata) * \brief Callback function that will check if the selected scanner follows the escl * protocol or not. */ static void resolve_callback(AvahiServiceResolver *r, AVAHI_GCC_UNUSED AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char __sane_unused__ *type, const char __sane_unused__ *domain, const char __sane_unused__ *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags __sane_unused__ flags, void __sane_unused__ *userdata) { char a[(AVAHI_ADDRESS_STR_MAX + 10)] = { 0 }; char *t; const char *is; const char *uuid; AvahiStringList *s; assert(r); switch (event) { case AVAHI_RESOLVER_FAILURE: break; case AVAHI_RESOLVER_FOUND: { char *psz_addr = ((void*)0); char b[128] = { 0 }; avahi_address_snprint(b, (sizeof(b)/sizeof(b[0]))-1, address); #ifdef ENABLE_IPV6 if (protocol == AVAHI_PROTO_INET6 && strchr(b, ':')) { if ( asprintf( &psz_addr, "[%s]", b ) == -1 ) break; } else #endif { if ( asprintf( &psz_addr, "%s", b ) == -1 ) break; } t = avahi_string_list_to_string(txt); if (strstr(t, "\"rs=eSCL\"") || strstr(t, "\"rs=/eSCL\"")) { s = avahi_string_list_find(txt, "is"); if (s && s->size > 3) is = (const char*)s->text + 3; else is = (const char*)NULL; s = avahi_string_list_find(txt, "uuid"); if (s && s->size > 5) uuid = (const char*)s->text + 5; else uuid = (const char*)NULL; DBG (10, "resolve_callback [%s]\n", a); if (strstr(psz_addr, "127.0.0.1") != NULL) { escl_device_add(port, name, "localhost", is, uuid, (char*)type); DBG (10,"resolve_callback fix redirect [localhost]\n"); } else escl_device_add(port, name, psz_addr, is, uuid, (char*)type); } } } } /** * \fn static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, * AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, * const char *type, const char *domain, * AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, void* userdata) * \brief Callback function that will browse tanks to 'avahi' the scanners * connected in network. */ static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, void* userdata) { AvahiClient *c = userdata; assert(b); switch (event) { case AVAHI_BROWSER_FAILURE: avahi_simple_poll_quit(simple_poll); return; case AVAHI_BROWSER_NEW: if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, c))) break; case AVAHI_BROWSER_REMOVE: break; case AVAHI_BROWSER_ALL_FOR_NOW: case AVAHI_BROWSER_CACHE_EXHAUSTED: if (event != AVAHI_BROWSER_CACHE_EXHAUSTED) { count_finish++; if (count_finish == 2) avahi_simple_poll_quit(simple_poll); } break; } } /** * \fn static void client_callback(AvahiClient *c, AvahiClientState state, * AVAHI_GCC_UNUSED void *userdata) * \brief Callback Function that quit if it doesn't find a connected scanner, * possible thanks the "Hello Protocol". * --> Waiting for a answer by the scanner to continue the avahi process. */ static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void *userdata) { assert(c); if (state == AVAHI_CLIENT_FAILURE) avahi_simple_poll_quit(simple_poll); } /** * \fn ESCL_Device *escl_devices(SANE_Status *status) * \brief Function that calls all the avahi functions and then, recovers the * connected eSCL devices. * This function is called in the 'sane_get_devices' function. * * \return NULL (the eSCL devices found) */ ESCL_Device * escl_devices(SANE_Status *status) { AvahiClient *client = NULL; AvahiServiceBrowser *sb = NULL; int error; count_finish = 0; *status = SANE_STATUS_GOOD; if (!(simple_poll = avahi_simple_poll_new())) { DBG( 1, "Failed to create simple poll object.\n"); *status = SANE_STATUS_INVAL; goto fail; } client = avahi_client_new(avahi_simple_poll_get(simple_poll), 0, client_callback, NULL, &error); if (!client) { DBG( 1, "Failed to create client: %s\n", avahi_strerror(error)); *status = SANE_STATUS_INVAL; goto fail; } if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_uscan._tcp", NULL, 0, browse_callback, client))) { DBG( 1, "Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(client))); *status = SANE_STATUS_INVAL; goto fail; } if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_uscans._tcp", NULL, 0, browse_callback, client))) { DBG( 1, "Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(client))); *status = SANE_STATUS_INVAL; goto fail; } avahi_simple_poll_loop(simple_poll); fail: if (sb) avahi_service_browser_free(sb); if (client) avahi_client_free(client); if (simple_poll) avahi_simple_poll_free(simple_poll); return (NULL); } backends-1.3.0/backend/escl/escl_jpeg.c000066400000000000000000000172141456256263500177240ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Touboul Nathane Copyright (C) 2019 Thierry HUCHARD This file is part of the SANE package. SANE 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 3 of the License, or (at your option) any later version. SANE 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 sane; see the file COPYING. If not, see . This file implements a SANE backend for eSCL scanners. */ #define DEBUG_DECLARE_ONLY #include "../include/sane/config.h" #include "escl.h" #include "../include/sane/sanei.h" #include #include #include #if(defined HAVE_LIBJPEG) # include #endif #include #define INPUT_BUFFER_SIZE 4096 #if(defined HAVE_LIBJPEG) struct my_error_mgr { struct jpeg_error_mgr errmgr; jmp_buf escape; }; typedef struct { struct jpeg_source_mgr pub; FILE *ctx; unsigned char buffer[INPUT_BUFFER_SIZE]; } my_source_mgr; /** * \fn static boolean fill_input_buffer(j_decompress_ptr cinfo) * \brief Called in the "skip_input_data" function. * * \return TRUE (everything is OK) */ static boolean fill_input_buffer(j_decompress_ptr cinfo) { my_source_mgr *src = (my_source_mgr *) cinfo->src; int nbytes = 0; nbytes = fread(src->buffer, 1, INPUT_BUFFER_SIZE, src->ctx); if (nbytes <= 0) { src->buffer[0] = (unsigned char) 0xFF; src->buffer[1] = (unsigned char) JPEG_EOI; nbytes = 2; } src->pub.next_input_byte = src->buffer; src->pub.bytes_in_buffer = nbytes; return (TRUE); } /** * \fn static void skip_input_data(j_decompress_ptr cinfo, long num_bytes) * \brief Called in the "jpeg_RW_src" function. */ static void skip_input_data(j_decompress_ptr cinfo, long num_bytes) { my_source_mgr *src = (my_source_mgr *) cinfo->src; if (num_bytes > 0) { while (num_bytes > (long) src->pub.bytes_in_buffer) { num_bytes -= (long) src->pub.bytes_in_buffer; (void) src->pub.fill_input_buffer(cinfo); } src->pub.next_input_byte += (size_t) num_bytes; src->pub.bytes_in_buffer -= (size_t) num_bytes; } } static void term_source(j_decompress_ptr __sane_unused__ cinfo) { return; } static void init_source(j_decompress_ptr __sane_unused__ cinfo) { return; } /** * \fn static void jpeg_RW_src(j_decompress_ptr cinfo, FILE *ctx) * \brief Called in the "escl_sane_decompressor" function. */ static void jpeg_RW_src(j_decompress_ptr cinfo, FILE *ctx) { my_source_mgr *src; if (cinfo->src == NULL) { cinfo->src = (struct jpeg_source_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(my_source_mgr)); } src = (my_source_mgr *) cinfo->src; src->pub.init_source = init_source; src->pub.fill_input_buffer = fill_input_buffer; src->pub.skip_input_data = skip_input_data; src->pub.resync_to_restart = jpeg_resync_to_restart; src->pub.term_source = term_source; src->ctx = ctx; src->pub.bytes_in_buffer = 0; src->pub.next_input_byte = NULL; } static void my_error_exit(j_common_ptr cinfo) { struct my_error_mgr *err = (struct my_error_mgr *)cinfo->err; longjmp(err->escape, 1); } static void output_no_message(j_common_ptr __sane_unused__ cinfo) { } /** * \fn SANE_Status escl_sane_decompressor(escl_sane_t *handler) * \brief Function that aims to decompress the jpeg image to SANE be able to read the image. * This function is called in the "sane_read" function. * * \return SANE_STATUS_GOOD (if everything is OK, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL) */ SANE_Status get_JPEG_data(capabilities_t *scanner, int *width, int *height, int *bps) { int start = 0; struct jpeg_decompress_struct cinfo; JSAMPROW rowptr[1]; unsigned char *surface = NULL; struct my_error_mgr jerr; int lineSize = 0; JDIMENSION x_off = 0; JDIMENSION y_off = 0; JDIMENSION w = 0; JDIMENSION h = 0; int pos = 0; if (scanner->tmp == NULL) return (SANE_STATUS_INVAL); fseek(scanner->tmp, SEEK_SET, 0); start = ftell(scanner->tmp); cinfo.err = jpeg_std_error(&jerr.errmgr); jerr.errmgr.error_exit = my_error_exit; jerr.errmgr.output_message = output_no_message; if (setjmp(jerr.escape)) { jpeg_destroy_decompress(&cinfo); if (surface != NULL) free(surface); fseek(scanner->tmp, start, SEEK_SET); DBG( 1, "Escl Jpeg : Error reading jpeg\n"); if (scanner->tmp) { fclose(scanner->tmp); scanner->tmp = NULL; } return (SANE_STATUS_INVAL); } jpeg_create_decompress(&cinfo); jpeg_RW_src(&cinfo, scanner->tmp); jpeg_read_header(&cinfo, TRUE); cinfo.out_color_space = JCS_RGB; cinfo.quantize_colors = FALSE; jpeg_calc_output_dimensions(&cinfo); double ratio = (double)cinfo.output_width / (double)scanner->caps[scanner->source].width; int rw = (int)((double)scanner->caps[scanner->source].width * ratio); int rh = (int)((double)scanner->caps[scanner->source].height * ratio); int rx = (int)((double)scanner->caps[scanner->source].pos_x * ratio); int ry = (int)((double)scanner->caps[scanner->source].pos_y * ratio); if (cinfo.output_width < (unsigned int)rw) rw = cinfo.output_width; if (rx < 0) rx = 0; if (cinfo.output_height < (unsigned int)rh) rh = cinfo.output_height; if (ry < 0) ry = 0; DBG(10, "1-JPEF Geometry [%dx%d|%dx%d]\n", rx, ry, rw, rh); x_off = rx; if (x_off > (unsigned int)rw) { w = rw; x_off = 0; } else w = rw - x_off; y_off = ry; if(y_off > (unsigned int)rh) { h = rh; y_off = 0; } else h = rh - y_off; DBG(10, "2-JPEF Geometry [%dx%d|%dx%d]\n", x_off, y_off, w, h); jpeg_start_decompress(&cinfo); if (x_off > 0 || w < cinfo.output_width) jpeg_crop_scanline(&cinfo, &x_off, &w); lineSize = w * cinfo.output_components; if (y_off > 0) jpeg_skip_scanlines(&cinfo, y_off); surface = malloc(cinfo.output_width * cinfo.output_height * cinfo.output_components); if (surface == NULL) { jpeg_destroy_decompress(&cinfo); DBG( 1, "Escl Jpeg : Memory allocation problem\n"); if (scanner->tmp) { fclose(scanner->tmp); scanner->tmp = NULL; } return (SANE_STATUS_NO_MEM); } pos = 0; while (cinfo.output_scanline < (unsigned int)rh) { rowptr[0] = (JSAMPROW)surface + (lineSize * pos); // ..cinfo.output_scanline); jpeg_read_scanlines(&cinfo, rowptr, (JDIMENSION) 1); pos++; } scanner->img_data = surface; scanner->img_size = lineSize * h; scanner->img_read = 0; *width = w; *height = h; *bps = cinfo.output_components; // jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); fclose(scanner->tmp); scanner->tmp = NULL; return (SANE_STATUS_GOOD); } #else SANE_Status get_JPEG_data(capabilities_t __sane_unused__ *scanner, int __sane_unused__ *width, int __sane_unused__ *height, int __sane_unused__ *bps) { return (SANE_STATUS_INVAL); } #endif backends-1.3.0/backend/escl/escl_mupdf.c000066400000000000000000000150751456256263500201150ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Thierry HUCHARD This file is part of the SANE package. SANE 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 3 of the License, or (at your option) any later version. SANE 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 sane; see the file COPYING. If not, see . This file implements a SANE backend for eSCL scanners. */ #define DEBUG_DECLARE_ONLY #include "../include/sane/config.h" #include "escl.h" #include "../include/sane/sanei.h" #include #include #include #include #if(defined HAVE_MUPDF) #include #endif #include #if(defined HAVE_MUPDF) // TODO: WIN32: HANDLE CreateFileW(), etc. // TODO: POSIX: int creat(), read(), write(), lseeko, etc. typedef struct fz_file_stream_escl_s { FILE *file; unsigned char buffer[4096]; } fz_file_stream_escl; static int next_file_escl(fz_context *ctx, fz_stream *stm, size_t n) { fz_file_stream_escl *state = stm->state; /* n is only a hint, that we can safely ignore */ n = fread(state->buffer, 1, sizeof(state->buffer), state->file); if (n < sizeof(state->buffer) && ferror(state->file)) fz_throw(ctx, FZ_ERROR_GENERIC, "read error: %s", strerror(errno)); stm->rp = state->buffer; stm->wp = state->buffer + n; stm->pos += (int64_t)n; if (n == 0) return EOF; return *stm->rp++; } static void drop_file_escl(fz_context *ctx, void *state_) { fz_file_stream_escl *state = state_; int n = fclose(state->file); if (n < 0) fz_warn(ctx, "close error: %s", strerror(errno)); fz_free(ctx, state); } static void seek_file_escl(fz_context *ctx, fz_stream *stm, int64_t offset, int whence) { fz_file_stream_escl *state = stm->state; #ifdef _WIN32 int64_t n = _fseeki64(state->file, offset, whence); #else int64_t n = fseeko(state->file, offset, whence); #endif if (n < 0) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot seek: %s", strerror(errno)); #ifdef _WIN32 stm->pos = _ftelli64(state->file); #else stm->pos = ftello(state->file); #endif stm->rp = state->buffer; stm->wp = state->buffer; } static fz_stream * fz_open_file_ptr_escl(fz_context *ctx, FILE *file) { fz_stream *stm; fz_file_stream_escl *state = fz_malloc_struct(ctx, fz_file_stream_escl); state->file = file; stm = fz_new_stream(ctx, state, next_file_escl, drop_file_escl); stm->seek = seek_file_escl; return stm; } /** * \fn SANE_Status escl_sane_decompressor(escl_sane_t *handler) * \brief Function that aims to decompress the pdf image to SANE be able * to read the image. * This function is called in the "sane_read" function. * * \return SANE_STATUS_GOOD (if everything is OK, otherwise, * SANE_STATUS_NO_MEM/SANE_STATUS_INVAL) */ SANE_Status get_PDF_data(capabilities_t *scanner, int *width, int *height, int *bps) { int page_number = -1, page_count = -2; fz_context *ctx; fz_document *doc; fz_pixmap *pix; fz_matrix ctm; fz_stream *stream; unsigned char *surface = NULL; /* Image data */ SANE_Status status = SANE_STATUS_GOOD; /* Create a context to hold the exception stack and various caches. */ ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED); if (!ctx) { DBG(1, "cannot create mupdf context\n"); status = SANE_STATUS_INVAL; goto close_file; } /* Register the default file types to handle. */ fz_try(ctx) fz_register_document_handlers(ctx); fz_catch(ctx) { DBG(1, "cannot register document handlers: %s\n", fz_caught_message(ctx)); status = SANE_STATUS_INVAL; goto drop_context; } /* Open the stream. */ fz_try(ctx) stream = fz_open_file_ptr_escl(ctx, scanner->tmp); fz_catch(ctx) { DBG(1, "cannot open stream: %s\n", fz_caught_message(ctx)); status = SANE_STATUS_INVAL; goto drop_context; } /* Seek stream. */ fz_try(ctx) fz_seek(ctx, stream, 0, SEEK_SET); fz_catch(ctx) { DBG(1, "cannot seek stream: %s\n", fz_caught_message(ctx)); status = SANE_STATUS_INVAL; goto drop_stream; } /* Open the document. */ fz_try(ctx) doc = fz_open_document_with_stream(ctx, "filename.pdf", stream); fz_catch(ctx) { DBG(1, "cannot open document: %s\n", fz_caught_message(ctx)); status = SANE_STATUS_INVAL; goto drop_stream; } /* Count the number of pages. */ fz_try(ctx) page_count = fz_count_pages(ctx, doc); fz_catch(ctx) { DBG(1, "cannot count number of pages: %s\n", fz_caught_message(ctx)); status = SANE_STATUS_INVAL; goto drop_document; } if (page_number < 0 || page_number >= page_count) { DBG(1, "page number out of range: %d (page count %d)\n", page_number + 1, page_count); status = SANE_STATUS_INVAL; goto drop_document; } /* Compute a transformation matrix for the zoom and rotation desired. */ /* The default resolution without scaling is 72 dpi. */ fz_scale(&ctm, (float)1.0, (float)1.0); fz_pre_rotate(&ctm, (float)0.0); /* Render page to an RGB pixmap. */ fz_try(ctx) pix = fz_new_pixmap_from_page_number(ctx, doc, 0, &ctm, fz_device_rgb(ctx), 0); fz_catch(ctx) { DBG(1, "cannot render page: %s\n", fz_caught_message(ctx)); status = SANE_STATUS_INVAL; goto drop_document; } surface = malloc(pix->h * pix->stride); memcpy(surface, pix->samples, (pix->h * pix->stride)); // If necessary, trim the image. surface = escl_crop_surface(scanner, surface, pix->w, pix->h, pix->n, width, height); if (!surface) { DBG( 1, "Escl Pdf : Surface Memory allocation problem\n"); status = SANE_STATUS_NO_MEM; goto drop_pix; } *bps = pix->n; /* Clean up. */ drop_pix: fz_drop_pixmap(ctx, pix); drop_document: fz_drop_document(ctx, doc); drop_stream: fz_drop_stream(ctx, stream); drop_context: fz_drop_context(ctx); close_file: if (scanner->tmp) fclose(scanner->tmp); scanner->tmp = NULL; return status; } #else SANE_Status get_PDF_data(capabilities_t __sane_unused__ *scanner, int __sane_unused__ *width, int __sane_unused__ *height, int __sane_unused__ *bps) { return (SANE_STATUS_INVAL); } #endif backends-1.3.0/backend/escl/escl_newjob.c000066400000000000000000000346041456256263500202650ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Touboul Nathane Copyright (C) 2019 Thierry HUCHARD This file is part of the SANE package. SANE 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 3 of the License, or (at your option) any later version. SANE 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 sane; see the file COPYING. If not, see . This file implements a SANE backend for eSCL scanners. */ #define DEBUG_DECLARE_ONLY #include "../include/sane/config.h" #include "escl.h" #include #include #include #include #ifdef PATH_MAX # undef PATH_MAX #endif #define PATH_MAX 4096 struct downloading { char *memory; size_t size; }; static const char settings[] = "" \ "" \ " %s" \ " " \ " " \ " escl:ThreeHundredthsOfInches" \ " %d" \ " %d" \ " %d" \ " %d" \ " " \ " " \ "%s" \ " %s" \ " %d" \ " %d" \ " %s" \ "%s" \ "%s" \ ""; /** * \fn static size_t download_callback(void *str, size_t size, size_t nmemb, void *userp) * \brief Callback function that stocks in memory the content of the 'job'. Example below : * "Trying 192.168.14.150... * TCP_NODELAY set * Connected to 192.168.14.150 (192.168.14.150) port 80 * POST /eSCL/ScanJobs HTTP/1.1 * Host: 192.168.14.150 * User-Agent: curl/7.55.1 * Accept: / * Content-Length: 605 * Content-Type: application/x-www-form-urlencoded * upload completely sent off: 605 out of 605 bytes * < HTTP/1.1 201 Created * < MIME-Version: 1.0 * < Location: http://192.168.14.150/eSCL/ScanJobs/22b54fd0-027b-1000-9bd0-f4a99726e2fa * < Content-Length: 0 * < Connection: close * < * Closing connection 0" * * \return realsize (size of the content needed -> the 'job') */ static size_t download_callback(void *str, size_t size, size_t nmemb, void *userp) { struct downloading *download = (struct downloading *)userp; size_t realsize = size * nmemb; char *content = realloc(download->memory, download->size + realsize + 1); if (content == NULL) { DBG( 1, "Not enough memory (realloc returned NULL)\n"); return (0); } download->memory = content; memcpy(&(download->memory[download->size]), str, realsize); download->size = download->size + realsize; download->memory[download->size] = 0; return (realsize); } static char* add_support_option(char *key, int val) { int size = (strlen(key) * 3) + 10; char *tmp = (char*)calloc(1, size); snprintf (tmp, size, "%d\n", key, val, key); return tmp; } /** * \fn char *escl_newjob (capabilities_t *scanner, const ESCL_Device *device, SANE_Status *status) * \brief Function that, using curl, uploads the data (composed by the scanner capabilities) to the * server to download the 'job' and recover the 'new job' (char *result), in LOCATION. * This function is called in the 'sane_start' function and it's the equivalent of the * following curl command : "curl -v POST -d cap.xml http(s)://'ip':'port'/eSCL/ScanJobs". * * \return result (the 'new job', situated in LOCATION) */ char * escl_newjob (capabilities_t *scanner, const ESCL_Device *device, SANE_Status *status) { CURL *curl_handle = NULL; int off_x = 0, off_y = 0; struct downloading *upload = NULL; struct downloading *download = NULL; const char *scan_jobs = "/eSCL/ScanJobs"; char cap_data[PATH_MAX] = { 0 }; char *location = NULL; char *result = NULL; char *temporary = NULL; char *format_ext = NULL; char f_ext_tmp[1024]; char duplex_mode[1024] = { 0 }; int wakup_count = 0; *status = SANE_STATUS_GOOD; if (device == NULL || scanner == NULL) { *status = SANE_STATUS_NO_MEM; DBG( 1, "Create NewJob : the name or the scan are invalid.\n"); return (NULL); } upload = (struct downloading *)calloc(1, sizeof(struct downloading)); if (upload == NULL) { *status = SANE_STATUS_NO_MEM; DBG( 1, "Create NewJob : memory allocation failure\n"); return (NULL); } download = (struct downloading *)calloc(1, sizeof(struct downloading)); if (download == NULL) { free(upload); DBG( 1, "Create NewJob : memory allocation failure\n"); *status = SANE_STATUS_NO_MEM; return (NULL); } if (scanner->caps[scanner->source].default_format) free(scanner->caps[scanner->source].default_format); scanner->caps[scanner->source].default_format = NULL; int have_png = scanner->caps[scanner->source].have_png; int have_jpeg = scanner->caps[scanner->source].have_jpeg; int have_tiff = scanner->caps[scanner->source].have_tiff; int have_pdf = scanner->caps[scanner->source].have_pdf; if ((scanner->source == PLATEN && have_pdf == -1) || (scanner->source > PLATEN)) { if (have_tiff != -1) { scanner->caps[scanner->source].default_format = strdup(scanner->caps[scanner->source].DocumentFormats[have_tiff]); } else if (have_png != -1) { scanner->caps[scanner->source].default_format = strdup(scanner->caps[scanner->source].DocumentFormats[have_png]); } else if (have_jpeg != -1) { scanner->caps[scanner->source].default_format = strdup(scanner->caps[scanner->source].DocumentFormats[have_jpeg]); } } else { if (have_pdf != -1) { scanner->caps[scanner->source].default_format = strdup(scanner->caps[scanner->source].DocumentFormats[have_pdf]); } else if (have_tiff != -1) { scanner->caps[scanner->source].default_format = strdup(scanner->caps[scanner->source].DocumentFormats[have_tiff]); } else if (have_png != -1) { scanner->caps[scanner->source].default_format = strdup(scanner->caps[scanner->source].DocumentFormats[have_png]); } else if (have_jpeg != -1) { scanner->caps[scanner->source].default_format = strdup(scanner->caps[scanner->source].DocumentFormats[have_jpeg]); } } if (atof ((const char *)device->version) <= 2.0) { // For eSCL 2.0 and older clients snprintf(f_ext_tmp, sizeof(f_ext_tmp), " %s", scanner->caps[scanner->source].default_format); } else { // For eSCL 2.1 and newer clients snprintf(f_ext_tmp, sizeof(f_ext_tmp), " %s", scanner->caps[scanner->source].default_format); } format_ext = f_ext_tmp; if(scanner->source > PLATEN && scanner->Sources[ADFDUPLEX]) { snprintf(duplex_mode, sizeof(duplex_mode), " %s", scanner->source == ADFDUPLEX ? "true" : "false"); } DBG( 1, "Create NewJob : %s\n", scanner->caps[scanner->source].default_format); if (scanner->caps[scanner->source].pos_x > scanner->caps[scanner->source].width) off_x = (scanner->caps[scanner->source].pos_x > scanner->caps[scanner->source].width) / 2; if (scanner->caps[scanner->source].pos_y > scanner->caps[scanner->source].height) off_y = (scanner->caps[scanner->source].pos_y > scanner->caps[scanner->source].height) / 2; char support_options[1024]; memset(support_options, 0, 1024); char *source = (scanner->source == PLATEN ? "Platen" : "Feeder"); if (scanner->use_threshold) { if (scanner->val_threshold != scanner->threshold->value) { char *tmp = add_support_option("ThresholdSupport", scanner->val_threshold); if (support_options[0]) strcat(support_options, tmp); else strcpy(support_options, tmp); free(tmp); } } if (scanner->use_sharpen) { if (scanner->val_sharpen != scanner->sharpen->value) { char *tmp = add_support_option("SharpenSupport", scanner->val_sharpen); if (support_options[0]) strcat(support_options, tmp); else strcpy(support_options, tmp); free(tmp); } } if (scanner->use_contrast) { if (scanner->val_contrast != scanner->contrast->value) { char *tmp = add_support_option("ContrastSupport", scanner->val_contrast); if (support_options[0]) strcat(support_options, tmp); else strcpy(support_options, tmp); free(tmp); } } if (scanner->use_brightness) { if (scanner->val_brightness != scanner->brightness->value) { char *tmp = add_support_option("BrightnessSupport", scanner->val_brightness); if (support_options[0]) strcat(support_options, tmp); else strcpy(support_options, tmp); free(tmp); } } snprintf(cap_data, sizeof(cap_data), settings, device->version, scanner->caps[scanner->source].height, scanner->caps[scanner->source].width, off_x, off_y, format_ext, scanner->caps[scanner->source].default_color, scanner->caps[scanner->source].default_resolution, scanner->caps[scanner->source].default_resolution, source, duplex_mode[0] == 0 ? " " : duplex_mode, support_options[0] == 0 ? " " : support_options); upload->memory = strdup(cap_data); upload->size = strlen(cap_data); wake_up_device: DBG( 1, "Create NewJob : %s\n", cap_data); download->memory = malloc(1); download->size = 0; curl_handle = curl_easy_init(); if (curl_handle != NULL) { escl_curl_url(curl_handle, device, scan_jobs); curl_easy_setopt(curl_handle, CURLOPT_POST, 1L); curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, (const char*)upload->memory); curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, upload->size); curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, download_callback); curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void *)download); curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 3L); CURLcode res = curl_easy_perform(curl_handle); if (res != CURLE_OK) { DBG( 1, "Create NewJob : the scanner responded incorrectly: %s\n", curl_easy_strerror(res)); *status = SANE_STATUS_INVAL; } else { if (download->memory != NULL) { char *tmp_location = strstr(download->memory, "Location:"); if (tmp_location) { temporary = strchr(tmp_location, '\r'); if (temporary == NULL) temporary = strchr(tmp_location, '\n'); if (temporary != NULL) { *temporary = '\0'; location = strrchr(tmp_location,'/'); if (location) { result = strdup(location); DBG( 1, "Create NewJob : %s\n", result); *temporary = '\n'; *location = '\0'; location = strrchr(tmp_location,'/'); wakup_count = 0; if (location) { location++; scanner->scanJob = strdup(location); DBG( 1, "Full location header [%s]\n", scanner->scanJob); } else scanner->scanJob = strdup("ScanJobs"); *location = '/'; } } if (result == NULL) { DBG( 1, "Error : Create NewJob, no location: %s\n", download->memory); *status = SANE_STATUS_INVAL; } free(download->memory); download->memory = NULL; } else { DBG( 1, "Create NewJob : The creation of the failed job: %s\n", download->memory); // If "409 Conflict" appear it means that there is no paper in feeder if (strstr(download->memory, "409 Conflict") != NULL) *status = SANE_STATUS_NO_DOCS; // If "503 Service Unavailable" appear, it means that device is busy (scanning in progress) else if (strstr(download->memory, "503 Service Unavailable") != NULL) { wakup_count += 1; *status = SANE_STATUS_DEVICE_BUSY; } else *status = SANE_STATUS_INVAL; } } else { *status = SANE_STATUS_NO_MEM; DBG( 1, "Create NewJob : The creation of the failed job\n"); return (NULL); } } curl_easy_cleanup(curl_handle); } if (wakup_count > 0 && wakup_count < 4) { free(download->memory); download->memory = NULL; download->size = 0; *status = SANE_STATUS_GOOD; usleep(250); goto wake_up_device; } if (upload != NULL) { free(upload->memory); free(upload); } if (download != NULL) free(download); return (result); } backends-1.3.0/backend/escl/escl_pdf.c000066400000000000000000000156221456256263500175510ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Thierry HUCHARD This file is part of the SANE package. SANE 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 3 of the License, or (at your option) any later version. SANE 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 sane; see the file COPYING. If not, see . This file implements a SANE backend for eSCL scanners. */ #define DEBUG_DECLARE_ONLY #include "../include/sane/config.h" #include "escl.h" #include "../include/sane/sanei.h" #include #include #include #include #include #include #if HAVE_POPPLER_GLIB #include #endif #include #if HAVE_POPPLER_GLIB #define ESCL_PDF_USE_MAPPED_FILE POPPLER_CHECK_VERSION(0,82,0) #if ! ESCL_PDF_USE_MAPPED_FILE static unsigned char* set_file_in_buffer(FILE *fp, int *size) { char buffer[1024] = { 0 }; unsigned char *data = (unsigned char *)calloc(1, sizeof(char)); int nx = 0; while(!feof(fp)) { int n = fread(buffer,sizeof(char),1024,fp); unsigned char *t = realloc(data, nx + n + 1); if (t == NULL) { DBG(10, "not enough memory (realloc returned NULL)"); free(data); return NULL; } data = t; memcpy(&(data[nx]), buffer, n); nx = nx + n; data[nx] = 0; } *size = nx; return data; } #endif static unsigned char * cairo_surface_to_pixels (cairo_surface_t *surface, int bps) { int cairo_width, cairo_height, cairo_rowstride; unsigned char *data, *dst, *cairo_data; unsigned int *src; int x, y; cairo_width = cairo_image_surface_get_width (surface); cairo_height = cairo_image_surface_get_height (surface); cairo_rowstride = cairo_image_surface_get_stride (surface); cairo_data = cairo_image_surface_get_data (surface); data = (unsigned char*)calloc(1, sizeof(unsigned char) * (cairo_height * cairo_width * bps)); for (y = 0; y < cairo_height; y++) { src = (unsigned int *) (cairo_data + y * cairo_rowstride); dst = data + y * (cairo_width * bps); for (x = 0; x < cairo_width; x++) { dst[0] = (*src >> 16) & 0xff; dst[1] = (*src >> 8) & 0xff; dst[2] = (*src >> 0) & 0xff; dst += bps; src++; } } return data; } SANE_Status get_PDF_data(capabilities_t *scanner, int *width, int *height, int *bps) { cairo_surface_t *cairo_surface = NULL; cairo_t *cr; PopplerPage *page; PopplerDocument *doc; double dw, dh; int w, h; unsigned char* surface = NULL; SANE_Status status = SANE_STATUS_GOOD; #if ESCL_PDF_USE_MAPPED_FILE GMappedFile *file; GBytes *bytes; file = g_mapped_file_new_from_fd (fileno (scanner->tmp), 0, NULL); if (!file) { DBG(1, "Error : g_mapped_file_new_from_fd"); status = SANE_STATUS_INVAL; goto close_file; } bytes = g_mapped_file_get_bytes (file); if (!bytes) { DBG(1, "Error : g_mapped_file_get_bytes"); status = SANE_STATUS_INVAL; goto free_file; } doc = poppler_document_new_from_bytes (bytes, NULL, NULL); if (!doc) { DBG(1, "Error : poppler_document_new_from_bytes"); status = SANE_STATUS_INVAL; goto free_bytes; } #else int size = 0; char *data = NULL; data = (char*)set_file_in_buffer(scanner->tmp, &size); if (!data) { DBG(1, "Error : set_file_in_buffer"); status = SANE_STATUS_INVAL; goto close_file; } doc = poppler_document_new_from_data (data, size, NULL, NULL); if (!doc) { DBG(1, "Error : poppler_document_new_from_data"); status = SANE_STATUS_INVAL; goto free_data; } #endif page = poppler_document_get_page (doc, 0); if (!page) { DBG(1, "Error : poppler_document_get_page"); status = SANE_STATUS_INVAL; goto free_doc; } poppler_page_get_size (page, &dw, &dh); dw = (double)scanner->caps[scanner->source].default_resolution * dw / 72.0; dh = (double)scanner->caps[scanner->source].default_resolution * dh / 72.0; w = (int)ceil(dw); h = (int)ceil(dh); cairo_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h); if (!cairo_surface) { DBG(1, "Error : cairo_image_surface_create"); status = SANE_STATUS_INVAL; goto free_page; } cr = cairo_create (cairo_surface); if (!cairo_surface) { DBG(1, "Error : cairo_create"); status = SANE_STATUS_INVAL; goto free_surface; } cairo_scale (cr, (double)scanner->caps[scanner->source].default_resolution / 72.0, (double)scanner->caps[scanner->source].default_resolution / 72.0); cairo_save (cr); poppler_page_render (page, cr); cairo_restore (cr); cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER); cairo_set_source_rgb (cr, 1, 1, 1); cairo_paint (cr); int st = cairo_status(cr); if (st) { DBG(1, "%s", cairo_status_to_string (st)); status = SANE_STATUS_INVAL; goto destroy_cr; } *bps = 3; DBG(1, "Escl Pdf : Image Size [%dx%d]\n", w, h); surface = cairo_surface_to_pixels (cairo_surface, *bps); if (!surface) { status = SANE_STATUS_NO_MEM; DBG(1, "Escl Pdf : Surface Memory allocation problem"); goto destroy_cr; } // If necessary, trim the image. surface = escl_crop_surface(scanner, surface, w, h, *bps, width, height); if (!surface) { DBG(1, "Escl Pdf Crop: Surface Memory allocation problem"); status = SANE_STATUS_NO_MEM; } destroy_cr: cairo_destroy (cr); free_surface: cairo_surface_destroy (cairo_surface); free_page: g_object_unref (page); free_doc: g_object_unref (doc); #if ESCL_PDF_USE_MAPPED_FILE free_bytes: g_bytes_unref (bytes); free_file: g_mapped_file_unref (file); #else free_data: free(data); #endif close_file: if (scanner->tmp) fclose(scanner->tmp); scanner->tmp = NULL; return status; } #else SANE_Status get_PDF_data(capabilities_t __sane_unused__ *scanner, int __sane_unused__ *width, int __sane_unused__ *height, int __sane_unused__ *bps) { return (SANE_STATUS_INVAL); } #endif backends-1.3.0/backend/escl/escl_png.c000066400000000000000000000135441456256263500175650ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Touboul Nathane Copyright (C) 2019 Thierry HUCHARD This file is part of the SANE package. SANE 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 3 of the License, or (at your option) any later version. SANE 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 sane; see the file COPYING. If not, see . This file implements a SANE backend for eSCL scanners. */ #define DEBUG_DECLARE_ONLY #include "../include/sane/config.h" #include "escl.h" #include "../include/sane/sanei.h" #include #include #include #if(defined HAVE_LIBPNG) #include #endif #include #if(defined HAVE_LIBPNG) /** * \fn SANE_Status escl_sane_decompressor(escl_sane_t *handler) * \brief Function that aims to decompress the png image to SANE be able to read the image. * This function is called in the "sane_read" function. * * \return SANE_STATUS_GOOD (if everything is OK, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL) */ SANE_Status get_PNG_data(capabilities_t *scanner, int *width, int *height, int *bps) { unsigned int w = 0; unsigned int h = 0; int components = 3; unsigned char *surface = NULL; /* Image data */ unsigned int i = 0; png_byte magic[8]; SANE_Status status = SANE_STATUS_GOOD; // read magic number fread (magic, 1, sizeof (magic), scanner->tmp); // check for valid magic number if (!png_check_sig (magic, sizeof (magic))) { DBG( 1, "Escl Png : PNG error is not a valid PNG image!\n"); status = SANE_STATUS_INVAL; goto close_file; } // create a png read struct png_structp png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { DBG( 1, "Escl Png : PNG error create a png read struct\n"); status = SANE_STATUS_INVAL; goto close_file; } // create a png info struct png_infop info_ptr = png_create_info_struct (png_ptr); if (!info_ptr) { DBG( 1, "Escl Png : PNG error create a png info struct\n"); png_destroy_read_struct (&png_ptr, NULL, NULL); status = SANE_STATUS_INVAL; goto close_file; } // initialize the setjmp for returning properly after a libpng // error occurred if (setjmp (png_jmpbuf (png_ptr))) { png_destroy_read_struct (&png_ptr, &info_ptr, NULL); if (surface) free (surface); DBG( 1, "Escl Png : PNG read error.\n"); status = SANE_STATUS_INVAL; goto close_file; } // setup libpng for using standard C fread() function // with our FILE pointer png_init_io (png_ptr, scanner->tmp); // tell libpng that we have already read the magic number png_set_sig_bytes (png_ptr, sizeof (magic)); // read png info png_read_info (png_ptr, info_ptr); int bit_depth, color_type; // get some useful information from header bit_depth = png_get_bit_depth (png_ptr, info_ptr); color_type = png_get_color_type (png_ptr, info_ptr); // convert index color images to RGB images if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb (png_ptr); else if (color_type != PNG_COLOR_TYPE_RGB && color_type != PNG_COLOR_TYPE_RGB_ALPHA) { DBG(1, "PNG format not supported.\n"); status = SANE_STATUS_NO_MEM; goto close_file; } if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) components = 4; else components = 3; if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha (png_ptr); if (bit_depth == 16) png_set_strip_16 (png_ptr); else if (bit_depth < 8) png_set_packing (png_ptr); // update info structure to apply transformations png_read_update_info (png_ptr, info_ptr); // retrieve updated information png_get_IHDR (png_ptr, info_ptr, (png_uint_32*)(&w), (png_uint_32*)(&h), &bit_depth, &color_type, NULL, NULL, NULL); *bps = components; // we can now allocate memory for storing pixel data surface = (unsigned char *)malloc (sizeof (unsigned char) * w * h * components); if (!surface) { DBG( 1, "Escl Png : texels Memory allocation problem\n"); status = SANE_STATUS_NO_MEM; goto close_file; } png_bytep *row_pointers; // setup a pointer array. Each one points at the begening of a row. row_pointers = (png_bytep *)malloc (sizeof (png_bytep) * h); if (!row_pointers) { DBG( 1, "Escl Png : row_pointers Memory allocation problem\n"); free(surface); status = SANE_STATUS_NO_MEM; goto close_file; } for (i = 0; i < h; ++i) { row_pointers[i] = (png_bytep)(surface + ((h - (i + 1)) * w * components)); } // read pixel data using row pointers png_read_image (png_ptr, row_pointers); // If necessary, trim the image. surface = escl_crop_surface(scanner, surface, w, h, components, width, height); if (!surface) { DBG( 1, "Escl Png : Surface Memory allocation problem\n"); status = SANE_STATUS_NO_MEM; goto close_file; } free (row_pointers); close_file: if (scanner->tmp) fclose(scanner->tmp); scanner->tmp = NULL; return (status); } #else SANE_Status get_PNG_data(capabilities_t __sane_unused__ *scanner, int __sane_unused__ *width, int __sane_unused__ *height, int __sane_unused__ *bps) { return (SANE_STATUS_INVAL); } #endif backends-1.3.0/backend/escl/escl_reset.c000066400000000000000000000051631456256263500201210ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Touboul Nathane Copyright (C) 2019 Thierry HUCHARD This file is part of the SANE package. SANE 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 3 of the License, or (at your option) any later version. SANE 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 sane; see the file COPYING. If not, see . This file implements a SANE backend for eSCL scanners. */ #define DEBUG_DECLARE_ONLY #include "../include/sane/config.h" #include "escl.h" #include #include static size_t write_callback(void __sane_unused__*str, size_t __sane_unused__ size, size_t nmemb, void __sane_unused__ *userp) { return nmemb; } /** * \fn void escl_scanner(const ESCL_Device *device, char *result) * \brief Function that resets the scanner after each scan, using curl. * This function is called in the 'sane_cancel' function. */ void escl_scanner(const ESCL_Device *device, char *scanJob, char *result) { CURL *curl_handle = NULL; const char *scan_jobs = "/eSCL/"; const char *scanner_start = "/NextDocument"; char scan_cmd[PATH_MAX] = { 0 }; int i = 0; long answer = 0; if (device == NULL || result == NULL) return; CURL_CALL: curl_handle = curl_easy_init(); if (curl_handle != NULL) { snprintf(scan_cmd, sizeof(scan_cmd), "%s%s%s%s", scan_jobs, scanJob, result, scanner_start); escl_curl_url(curl_handle, device, scan_cmd); curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 3L); if (curl_easy_perform(curl_handle) == CURLE_OK) { curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &answer); i++; if (i >= 15) return; } curl_easy_cleanup(curl_handle); if (SANE_STATUS_GOOD != escl_status(device, PLATEN, NULL, NULL)) goto CURL_CALL; } } backends-1.3.0/backend/escl/escl_scan.c000066400000000000000000000075421456256263500177260ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Touboul Nathane Copyright (C) 2019 Thierry HUCHARD This file is part of the SANE package. SANE 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 3 of the License, or (at your option) any later version. SANE 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 sane; see the file COPYING. If not, see . This file implements a SANE backend for eSCL scanners. */ #define DEBUG_DECLARE_ONLY #include "../include/sane/config.h" #include "escl.h" #include #include #include #include "../include/sane/sanei.h" /** * \fn static size_t write_callback(void *str, size_t size, size_t nmemb, void *userp) * \brief Callback function that writes the image scanned into the temporary file. * * \return to_write (the result of the fwrite function) */ static size_t write_callback(void *str, size_t size, size_t nmemb, void *userp) { capabilities_t *scanner = (capabilities_t *)userp; size_t to_write = fwrite(str, size, nmemb, scanner->tmp); scanner->real_read += to_write; return (to_write); } /** * \fn SANE_Status escl_scan(capabilities_t *scanner, const ESCL_Device *device, char *result) * \brief Function that, after recovering the 'new job', scans the image writed in the * temporary file, using curl. * This function is called in the 'sane_start' function and it's the equivalent of * the following curl command : "curl -s http(s)://'ip:'port'/eSCL/ScanJobs/'new job'/NextDocument > image.jpg". * * \return status (if everything is OK, status = SANE_STATUS_GOOD, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL) */ SANE_Status escl_scan(capabilities_t *scanner, const ESCL_Device *device, char *scanJob, char *result) { CURL *curl_handle = NULL; const char *scan_jobs = "/eSCL/"; const char *scanner_start = "/NextDocument"; char scan_cmd[PATH_MAX] = { 0 }; SANE_Status status = SANE_STATUS_GOOD; if (device == NULL) return (SANE_STATUS_NO_MEM); scanner->real_read = 0; curl_handle = curl_easy_init(); if (curl_handle != NULL) { snprintf(scan_cmd, sizeof(scan_cmd), "%s%s%s%s", scan_jobs, scanJob, result, scanner_start); escl_curl_url(curl_handle, device, scan_cmd); curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 3L); if (scanner->tmp) fclose(scanner->tmp); scanner->tmp = tmpfile(); if (scanner->tmp != NULL) { curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, scanner); CURLcode res = curl_easy_perform(curl_handle); if (res != CURLE_OK) { DBG( 1, "Unable to scan: %s\n", curl_easy_strerror(res)); scanner->real_read = 0; fclose(scanner->tmp); scanner->tmp = NULL; status = SANE_STATUS_INVAL; goto cleanup; } fseek(scanner->tmp, 0, SEEK_SET); } else status = SANE_STATUS_NO_MEM; cleanup: curl_easy_cleanup(curl_handle); } DBG(10, "eSCL scan : [%s]\treal read (%ld)\n", sane_strstatus(status), scanner->real_read); if (scanner->real_read == 0) { fclose(scanner->tmp); scanner->tmp = NULL; return SANE_STATUS_NO_DOCS; } return (status); } backends-1.3.0/backend/escl/escl_status.c000066400000000000000000000230001456256263500203100ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Touboul Nathane Copyright (C) 2019 Thierry HUCHARD This file is part of the SANE package. SANE 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 3 of the License, or (at your option) any later version. SANE 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 sane; see the file COPYING. If not, see . This file implements a SANE backend for eSCL scanners. */ #define DEBUG_DECLARE_ONLY #include "../include/sane/config.h" #include "escl.h" #include #include #include #include struct idle { char *memory; size_t size; }; /** * \fn static size_t memory_callback_s(void *contents, size_t size, size_t nmemb, void *userp) * \brief Callback function that stocks in memory the content of the scanner status. * * \return realsize (size of the content needed -> the scanner status) */ static size_t memory_callback_s(void *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; struct idle *mem = (struct idle *)userp; char *str = realloc(mem->memory, mem->size + realsize + 1); if (str == NULL) { DBG(1, "not enough memory (realloc returned NULL)\n"); return (0); } mem->memory = str; memcpy(&(mem->memory[mem->size]), contents, realsize); mem->size = mem->size + realsize; mem->memory[mem->size] = 0; return (realsize); } /** * \fn static int find_nodes_s(xmlNode *node) * \brief Function that browses the xml file and parses it, to find the xml children node. * --> to recover the scanner status. * * \return 0 if a xml child node is found, 1 otherwise */ static int find_nodes_s(xmlNode *node) { xmlNode *child = node->children; while (child) { if (child->type == XML_ELEMENT_NODE) return (0); child = child->next; } return (1); } static void print_xml_job_status(xmlNode *node, SANE_Status *job, int *image) { while (node) { if (node->type == XML_ELEMENT_NODE) { if (find_nodes_s(node)) { if (strcmp((const char *)node->name, "JobState") == 0) { const char *state = (const char *)xmlNodeGetContent(node); if (!strcmp(state, "Processing")) { *job = SANE_STATUS_DEVICE_BUSY; DBG(10, "jobId Processing SANE_STATUS_DEVICE_BUSY\n"); } else if (!strcmp(state, "Completed")) { *job = SANE_STATUS_GOOD; DBG(10, "jobId Completed SANE_STATUS_GOOD\n"); } else if (strcmp((const char *)node->name, "ImagesToTransfer") == 0) { const char *state = (const char *)xmlNodeGetContent(node); *image = atoi(state); } } } } print_xml_job_status(node->children, job, image); node = node->next; } } static void print_xml_platen_and_adf_status(xmlNode *node, SANE_Status *platen, SANE_Status *adf, const char* jobId, SANE_Status *job, int *image) { while (node) { if (node->type == XML_ELEMENT_NODE) { if (find_nodes_s(node)) { if (strcmp((const char *)node->name, "State") == 0) { DBG(10, "State\t"); const char *state = (const char *)xmlNodeGetContent(node); if (!strcmp(state, "Idle")) { DBG(10, "Idle SANE_STATUS_GOOD\n"); *platen = SANE_STATUS_GOOD; } else if (!strcmp(state, "Processing")) { DBG(10, "Processing SANE_STATUS_DEVICE_BUSY\n"); *platen = SANE_STATUS_DEVICE_BUSY; } else { DBG(10, "%s SANE_STATUS_UNSUPPORTED\n", state); *platen = SANE_STATUS_UNSUPPORTED; } } // Thank's Alexander Pevzner (pzz@apevzner.com) else if (adf && strcmp((const char *)node->name, "AdfState") == 0) { const char *state = (const char *)xmlNodeGetContent(node); if (!strcmp(state, "ScannerAdfLoaded")){ DBG(10, "ScannerAdfLoaded SANE_STATUS_GOOD\n"); *adf = SANE_STATUS_GOOD; } else if (!strcmp(state, "ScannerAdfJam")) { DBG(10, "ScannerAdfJam SANE_STATUS_JAMMED\n"); *adf = SANE_STATUS_JAMMED; } else if (!strcmp(state, "ScannerAdfDoorOpen")) { DBG(10, "ScannerAdfDoorOpen SANE_STATUS_COVER_OPEN\n"); *adf = SANE_STATUS_COVER_OPEN; } else if (!strcmp(state, "ScannerAdfProcessing")) { /* Kyocera version */ DBG(10, "ScannerAdfProcessing SANE_STATUS_NO_DOC\n"); *adf = SANE_STATUS_NO_DOCS; } else if (!strcmp(state, "ScannerAdfEmpty")) { DBG(10, "ScannerAdfEmpty SANE_STATUS_NO_DOCS\n"); /* Cannon TR4500, EPSON XP-7100 */ *adf = SANE_STATUS_NO_DOCS; } else { DBG(10, "%s SANE_STATUS_NO_DOCS\n", state); *adf = SANE_STATUS_UNSUPPORTED; } } else if (jobId && job && strcmp((const char *)node->name, "JobUri") == 0) { if (strstr((const char *)xmlNodeGetContent(node), jobId)) { print_xml_job_status(node, job, image); } } } } print_xml_platen_and_adf_status(node->children, platen, adf, jobId, job, image); node = node->next; } } /** * \fn SANE_Status escl_status(const ESCL_Device *device) * \brief Function that finally recovers the scanner status ('Idle', or not), using curl. * This function is called in the 'sane_open' function and it's the equivalent of * the following curl command : "curl http(s)://'ip':'port'/eSCL/ScannerStatus". * * \return status (if everything is OK, status = SANE_STATUS_GOOD, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL) */ SANE_Status escl_status(const ESCL_Device *device, int source, const char* jobId, SANE_Status *job) { SANE_Status status = SANE_STATUS_DEVICE_BUSY; SANE_Status platen= SANE_STATUS_DEVICE_BUSY; SANE_Status adf= SANE_STATUS_DEVICE_BUSY; CURL *curl_handle = NULL; struct idle *var = NULL; xmlDoc *data = NULL; xmlNode *node = NULL; const char *scanner_status = "/eSCL/ScannerStatus"; int image = -1; int pass = 0; reload: if (device == NULL) return (SANE_STATUS_NO_MEM); status = SANE_STATUS_DEVICE_BUSY; platen= SANE_STATUS_DEVICE_BUSY; adf= SANE_STATUS_DEVICE_BUSY; var = (struct idle*)calloc(1, sizeof(struct idle)); if (var == NULL) return (SANE_STATUS_NO_MEM); var->memory = malloc(1); var->size = 0; curl_handle = curl_easy_init(); escl_curl_url(curl_handle, device, scanner_status); curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, memory_callback_s); curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)var); curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 3L); CURLcode res = curl_easy_perform(curl_handle); if (res != CURLE_OK) { DBG( 1, "The scanner didn't respond: %s\n", curl_easy_strerror(res)); status = SANE_STATUS_INVAL; goto clean_data; } DBG( 10, "eSCL : Status : %s.\n", var->memory); data = xmlReadMemory(var->memory, var->size, "file.xml", NULL, 0); if (data == NULL) { status = SANE_STATUS_NO_MEM; goto clean_data; } node = xmlDocGetRootElement(data); if (node == NULL) { status = SANE_STATUS_NO_MEM; goto clean; } /* Decode Job status */ // Thank's Alexander Pevzner (pzz@apevzner.com) print_xml_platen_and_adf_status(node, &platen, &adf, jobId, job, &image); if (platen != SANE_STATUS_GOOD && platen != SANE_STATUS_UNSUPPORTED) { status = platen; } else if (source == PLATEN) { status = platen; } else { status = adf; } DBG (10, "STATUS : %s\n", sane_strstatus(status)); clean: xmlFreeDoc(data); clean_data: xmlCleanupParser(); xmlMemoryDump(); curl_easy_cleanup(curl_handle); free(var->memory); free(var); if (pass == 0 && source != PLATEN && image == 0 && (status == SANE_STATUS_GOOD || status == SANE_STATUS_UNSUPPORTED || status == SANE_STATUS_DEVICE_BUSY)) { pass = 1; goto reload; } return (status); } backends-1.3.0/backend/escl/escl_tiff.c000066400000000000000000000063661456256263500177350ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Touboul Nathane Copyright (C) 2019 Thierry HUCHARD This file is part of the SANE package. SANE 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 3 of the License, or (at your option) any later version. SANE 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 sane; see the file COPYING. If not, see . This file implements a SANE backend for eSCL scanners. */ #define DEBUG_DECLARE_ONLY #include "../include/sane/config.h" #include "escl.h" #include "../include/_stdint.h" #include "../include/sane/sanei.h" #include #include #include #include #if(defined HAVE_TIFFIO_H) #include #endif #include #if(defined HAVE_TIFFIO_H) /** * \fn SANE_Status escl_sane_decompressor(escl_sane_t *handler) * \brief Function that aims to decompress the png image to SANE be able to read the image. * This function is called in the "sane_read" function. * * \return SANE_STATUS_GOOD (if everything is OK, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL) */ SANE_Status get_TIFF_data(capabilities_t *scanner, int *width, int *height, int *bps) { TIFF* tif = NULL; uint32_t w = 0; uint32_t h = 0; unsigned char *surface = NULL; /* image data*/ int components = 4; uint32_t npixels = 0; SANE_Status status = SANE_STATUS_GOOD; lseek(fileno(scanner->tmp), 0, SEEK_SET); tif = TIFFFdOpen(fileno(scanner->tmp), "temp", "r"); if (!tif) { DBG( 1, "Escl Tiff : Can not open, or not a TIFF file.\n"); status = SANE_STATUS_INVAL; goto close_file; } TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w); TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h); npixels = w * h; surface = (unsigned char*) malloc(npixels * sizeof (uint32_t)); if (surface == NULL) { DBG( 1, "Escl Tiff : raster Memory allocation problem.\n"); status = SANE_STATUS_INVAL; goto close_tiff; } if (!TIFFReadRGBAImage(tif, w, h, (uint32_t *)surface, 0)) { DBG( 1, "Escl Tiff : Problem reading image data.\n"); status = SANE_STATUS_INVAL; free(surface); goto close_tiff; } *bps = components; // If necessary, trim the image. surface = escl_crop_surface(scanner, surface, w, h, components, width, height); if (!surface) { DBG( 1, "Escl Tiff : Surface Memory allocation problem\n"); status = SANE_STATUS_INVAL; } close_tiff: TIFFClose(tif); close_file: if (scanner->tmp) fclose(scanner->tmp); scanner->tmp = NULL; return (status); } #else SANE_Status get_TIFF_data(capabilities_t __sane_unused__ *scanner, int __sane_unused__ *w, int __sane_unused__ *h, int __sane_unused__ *bps) { return (SANE_STATUS_INVAL); } #endif backends-1.3.0/backend/fujitsu-scsi.h000066400000000000000000001404351456256263500175020ustar00rootroot00000000000000#ifndef FUJITSU_SCSI_H #define FUJITSU_SCSI_H /* * Part of SANE - Scanner Access Now Easy. * * Please see to opening comments in fujitsu.c */ /****************************************************/ #define USB_COMMAND_CODE 0x43 #define USB_COMMAND_LEN 0x1F #define USB_COMMAND_OFFSET 0x13 #define USB_COMMAND_TIME 30000 #define USB_DATA_TIME 30000 #define USB_STATUS_CODE 0x53 #define USB_STATUS_LEN 0x0D #define USB_STATUS_OFFSET 0x09 #define USB_STATUS_TIME 30000 /*static inline void */ static void setbitfield (unsigned char *pageaddr, int mask, int shift, int val) { *pageaddr = (*pageaddr & ~(mask << shift)) | ((val & mask) << shift); } /* ------------------------------------------------------------------------- */ /*static inline int */ static int getbitfield (unsigned char *pageaddr, int mask, int shift) { return ((*pageaddr >> shift) & mask); } /* ------------------------------------------------------------------------- */ static int getnbyte (unsigned char *pnt, int nbytes) { unsigned int result = 0; int i; #ifdef DEBUG assert (nbytes < 5); #endif for (i = 0; i < nbytes; i++) result = (result << 8) | (pnt[i] & 0xff); return result; } /* ------------------------------------------------------------------------- */ /*static inline void */ static void putnbyte (unsigned char *pnt, unsigned int value, unsigned int nbytes) { int i; #ifdef DEBUG assert (nbytes < 5); #endif for (i = nbytes - 1; i >= 0; i--) { pnt[i] = value & 0xff; value = value >> 8; } } /* ==================================================================== */ /* SCSI commands */ #define set_SCSI_opcode(out, val) out[0]=val #define set_SCSI_lun(out, val) setbitfield(out + 1, 7, 5, val) /* ==================================================================== */ /* TEST_UNIT_READY */ #define TEST_UNIT_READY_code 0x00 #define TEST_UNIT_READY_len 6 /* ==================================================================== */ /* REQUEST_SENSE */ #define REQUEST_SENSE_code 0x03 #define REQUEST_SENSE_len 6 #define RS_return_size 0x12 #define set_RS_return_size(icb,val) icb[0x04]=val /* defines for request sense return block */ #define get_RS_information_valid(b) getbitfield(b + 0x00, 1, 7) #define get_RS_error_code(b) getbitfield(b + 0x00, 0x7f, 0) #define get_RS_filemark(b) getbitfield(b + 0x02, 1, 7) #define get_RS_EOM(b) getbitfield(b + 0x02, 1, 6) #define get_RS_ILI(b) getbitfield(b + 0x02, 1, 5) #define get_RS_sense_key(b) getbitfield(b + 0x02, 0x0f, 0) #define get_RS_information(b) getnbyte(b+0x03, 4) /* normally 0 */ #define get_RS_additional_length(b) b[0x07] /* always 10 */ #define get_RS_ASC(b) b[0x0c] #define get_RS_ASCQ(b) b[0x0d] #define get_RS_SKSV(b) getbitfield(b+0x0f,1,7) /* valid=0 */ #define get_RS_SKSB(b) getnbyte(b+0x0f, 3) /* when RS is 0x05/0x26 bad bytes listed in sksb */ #define get_RS_offending_byte(b) getnbyte(b+0x10, 2) /* 3091 and 3092 use RS instead of ghs. RS must be 0x00/0x80 */ /* in ascq */ #define get_RS_adf_open(in) getbitfield(in+0x0d, 1, 7) #define get_RS_send_sw(in) getbitfield(in+0x0d, 1, 5) #define get_RS_scan_sw(in) getbitfield(in+0x0d, 1, 4) #define get_RS_duplex_sw(in) getbitfield(in+0x0d, 1, 2) #define get_RS_top(in) getbitfield(in+0x0d, 1, 1) #define get_RS_hopper(in) getbitfield(in+0x0d, 1, 0) /* in sksb */ #define get_RS_function(in) getbitfield(in+0x0f, 0x0f, 3) #define get_RS_density(in) getbitfield(in+0x0f, 0x07, 0) /* ==================================================================== */ /* INQUIRY */ #define INQUIRY_code 0x12 #define INQUIRY_len 6 #define INQUIRY_std_len 96 #define INQUIRY_vpd_len 204 /* unlikely maximum value */ #define set_IN_evpd(icb, val) setbitfield(icb + 1, 1, 0, val) #define set_IN_page_code(icb, val) icb[0x02]=val #define set_IN_return_size(icb,val) icb[0x04]=val #define set_IN_length(out,n) out[0x04]=n-5 #define get_IN_periph_qual(in) getbitfield(in, 0x07, 5) #define IN_periph_qual_lun 0x00 #define IN_periph_qual_nolun 0x03 #define get_IN_periph_devtype(in) getbitfield(in, 0x1f, 0) #define IN_periph_devtype_scanner 0x06 #define IN_periph_devtype_unknown 0x1f #define get_IN_response_format(in) getbitfield(in + 0x03, 0x07, 0) #define IN_recognized 0x02 #define get_IN_vendor(in, buf) snprintf(buf, 0x08 + 1, "%.*s", \ 0x08, (char *)in + 0x08) #define get_IN_product(in, buf) snprintf(buf, 0x10 + 1, "%.*s", \ 0x10, (char *)in + 0x10) #define get_IN_version(in, buf) snprintf(buf, 0x04 + 1, "%.*s", \ 0x04, (char *)in + 0x20) #define get_IN_color_offset(in) getnbyte (in+0x2A, 2) /* offset between colors */ /* these only in some scanners */ #define get_IN_long_gray(in) getbitfield(in+0x2C, 1, 1) #define get_IN_long_color(in) getbitfield(in+0x2C, 1, 0) #define get_IN_emulation(in) getbitfield(in+0x2D, 1, 6) #define get_IN_cmp_cga(in) getbitfield(in+0x2D, 1, 5) #define get_IN_bg_back(in) getbitfield(in+0x2D, 1, 3) #define get_IN_bg_front(in) getbitfield(in+0x2D, 1, 2) #define get_IN_bg_fb(in) getbitfield(in+0x2D, 1, 1) #define get_IN_has_back(in) getbitfield(in+0x2D, 1, 0) #define get_IN_duplex_offset(in) getnbyte (in+0x2E, 2) /* the VPD response */ #define get_IN_page_length(in) in[0x04] #define get_IN_basic_x_res(in) getnbyte(in + 0x05, 2) #define get_IN_basic_y_res(in) getnbyte(in + 0x07, 2) #define get_IN_step_x_res(in) getbitfield(in+0x09, 1, 0) #define get_IN_step_y_res(in) getbitfield(in+0x09, 1, 4) #define get_IN_max_x_res(in) getnbyte(in + 0x0a, 2) #define get_IN_max_y_res(in) getnbyte(in + 0x0c, 2) #define get_IN_min_x_res(in) getnbyte(in + 0x0e, 2) #define get_IN_min_y_res(in) getnbyte(in + 0x10, 2) #define get_IN_std_res_200(in) getbitfield(in+ 0x12, 1, 0) #define get_IN_std_res_180(in) getbitfield(in+ 0x12, 1, 1) #define get_IN_std_res_160(in) getbitfield(in+ 0x12, 1, 2) #define get_IN_std_res_150(in) getbitfield(in+ 0x12, 1, 3) #define get_IN_std_res_120(in) getbitfield(in+ 0x12, 1, 4) #define get_IN_std_res_100(in) getbitfield(in+ 0x12, 1, 5) #define get_IN_std_res_75(in) getbitfield(in+ 0x12, 1, 6) #define get_IN_std_res_60(in) getbitfield(in+ 0x12, 1, 7) #define get_IN_std_res_1200(in) getbitfield(in+ 0x13, 1, 0) #define get_IN_std_res_800(in) getbitfield(in+ 0x13, 1, 1) #define get_IN_std_res_600(in) getbitfield(in+ 0x13, 1, 2) #define get_IN_std_res_480(in) getbitfield(in+ 0x13, 1, 3) #define get_IN_std_res_400(in) getbitfield(in+ 0x13, 1, 4) #define get_IN_std_res_320(in) getbitfield(in+ 0x13, 1, 5) #define get_IN_std_res_300(in) getbitfield(in+ 0x13, 1, 6) #define get_IN_std_res_240(in) getbitfield(in+ 0x13, 1, 7) #define get_IN_window_width(in) getnbyte(in + 0x14, 4) #define get_IN_window_length(in) getnbyte(in + 0x18, 4) #define get_IN_overflow(in) getbitfield(in+0x1c, 1, 0) #define get_IN_monochrome(in) getbitfield(in+0x1c, 1, 1) #define get_IN_half_tone(in) getbitfield(in+0x1c, 1, 2) #define get_IN_multilevel(in) getbitfield(in+0x1c, 1, 3) #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_adf(in) getbitfield(in+0x20, 1, 7) #define get_IN_flatbed(in) getbitfield(in+0x20, 1, 6) #define get_IN_transparency(in) getbitfield(in+0x20, 1, 5) #define get_IN_duplex(in) getbitfield(in+0x20, 1, 4) #define get_IN_endorser_b(in) getbitfield(in+0x20, 1, 3) #define get_IN_barcode(in) getbitfield(in+0x20, 1, 2) #define get_IN_operator_panel(in) getbitfield(in+0x20, 1, 1) #define get_IN_endorser_f(in) getbitfield(in+0x20, 1, 0) #define get_IN_mp_stacker(in) getbitfield(in+0x21, 1, 7) #define get_IN_prepick(in) getbitfield(in+0x21, 1, 6) #define get_IN_mf_detect(in) getbitfield(in+0x21, 1, 5) #define get_IN_paperprot(in) getbitfield(in+0x21, 1, 4) #define get_IN_adbits(in) getbitfield(in+0x21, 0x0f, 0) #define get_IN_buffer_bytes(in) getnbyte(in + 0x22, 4) /*supported scsi commands*/ #define get_IN_has_cmd_msen10(in) getbitfield(in+0x26, 1, 1) #define get_IN_has_cmd_msel10(in) getbitfield(in+0x26, 1, 0) #define get_IN_has_cmd_lsen(in) getbitfield(in+0x27, 1, 7) #define get_IN_has_cmd_lsel(in) getbitfield(in+0x27, 1, 6) #define get_IN_has_cmd_change(in) getbitfield(in+0x27, 1, 5) #define get_IN_has_cmd_rbuff(in) getbitfield(in+0x27, 1, 4) #define get_IN_has_cmd_wbuff(in) getbitfield(in+0x27, 1, 3) #define get_IN_has_cmd_cav(in) getbitfield(in+0x27, 1, 2) #define get_IN_has_cmd_comp(in) getbitfield(in+0x27, 1, 1) #define get_IN_has_cmd_gdbs(in) getbitfield(in+0x27, 1, 0) #define get_IN_has_cmd_op(in) getbitfield(in+0x28, 1, 7) #define get_IN_has_cmd_send(in) getbitfield(in+0x28, 1, 6) #define get_IN_has_cmd_read(in) getbitfield(in+0x28, 1, 5) #define get_IN_has_cmd_gwin(in) getbitfield(in+0x28, 1, 4) #define get_IN_has_cmd_swin(in) getbitfield(in+0x28, 1, 3) #define get_IN_has_cmd_sdiag(in) getbitfield(in+0x28, 1, 2) #define get_IN_has_cmd_rdiag(in) getbitfield(in+0x28, 1, 1) #define get_IN_has_cmd_scan(in) getbitfield(in+0x28, 1, 0) #define get_IN_has_cmd_msen6(in) getbitfield(in+0x29, 1, 7) #define get_IN_has_cmd_copy(in) getbitfield(in+0x29, 1, 6) #define get_IN_has_cmd_rel(in) getbitfield(in+0x29, 1, 5) #define get_IN_has_cmd_runit(in) getbitfield(in+0x29, 1, 4) #define get_IN_has_cmd_msel6(in) getbitfield(in+0x29, 1, 3) #define get_IN_has_cmd_inq(in) getbitfield(in+0x29, 1, 2) #define get_IN_has_cmd_rs(in) getbitfield(in+0x29, 1, 1) #define get_IN_has_cmd_tur(in) getbitfield(in+0x29, 1, 0) /* more stuff here? (vendor commands) */ #define get_IN_has_cmd_subwindow(in) getbitfield(in+0x2b, 1, 0) #define get_IN_has_cmd_endorser(in) getbitfield(in+0x2b, 1, 1) #define get_IN_has_cmd_hw_status(in) getbitfield(in+0x2b, 1, 2) #define get_IN_has_cmd_hw_status_2(in) getbitfield(in+0x2b, 1, 3) #define get_IN_has_cmd_hw_status_3(in) getbitfield(in+0x2b, 1, 4) #define get_IN_has_cmd_scanner_ctl(in) getbitfield(in+0x31, 1, 1) #define get_IN_has_cmd_device_restart(in) getbitfield(in+0x31, 1, 2) #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_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_rif(in) getbitfield(in+0x58, 1, 7) #define get_IN_ipc_dtc(in) getbitfield(in+0x58, 1, 6) #define get_IN_ipc_sdtc(in) getbitfield(in+0x58, 1, 5) #define get_IN_ipc_outline_extraction(in) getbitfield(in+0x58, 1, 4) #define get_IN_ipc_image_emphasis(in) getbitfield(in+0x58, 1, 3) #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_wl_follow(in) getbitfield(in+0x58, 1, 0) #define get_IN_ipc_subwindow(in) getbitfield(in+0x59, 1, 7) #define get_IN_ipc_diffusion(in) getbitfield(in+0x59, 1, 6) #define get_IN_ipc_ipc3(in) getbitfield(in+0x59, 1, 5) #define get_IN_ipc_rotation(in) getbitfield(in+0x59, 1, 4) #define get_IN_ipc_hybrid_crop_deskew(in) getbitfield(in+0x59, 1, 3) #define get_IN_vpd_thru_byte_6f(in) getbitfield(in+0x59, 1, 0) #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) #define get_IN_compression_JBIG(in) getbitfield(in+0x5a, 1, 4) #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_compression_JPG_gray(in) getbitfield(in+0x5b, 3, 6) #define IN_comp_JPG_gray_unsup 1 #define IN_comp_JPG_gray_color 2 #define IN_comp_JPG_gray_gray 3 #define get_IN_compression_JPG_YUV_422(in) getbitfield(in+0x5b, 1, 0) #define get_IN_endorser_b_mech(in) getbitfield(in+0x5c, 1, 7) #define get_IN_endorser_b_stamp(in) getbitfield(in+0x5c, 1, 6) #define get_IN_endorser_b_elec(in) getbitfield(in+0x5c, 1, 5) #define get_IN_endorser_max_id(in) getbitfield(in+0x5c, 0x0f, 0) #define get_IN_endorser_f_mech(in) getbitfield(in+0x5d, 1, 7) #define get_IN_endorser_f_stamp(in) getbitfield(in+0x5d, 1, 6) #define get_IN_endorser_f_elec(in) getbitfield(in+0x5d, 1, 5) #define get_IN_endorser_f_type(in) getbitfield(in+0x5d, 3, 2) #define get_IN_endorser_b_type(in) getbitfield(in+0x5d, 3, 0) #define get_IN_connection(in) getbitfield(in+0x62, 3, 0) #define get_IN_endorser_type_ext(in) getbitfield(in+0x63, 1, 4) #define get_IN_endorser_pre_back(in) getbitfield(in+0x63, 1, 3) #define get_IN_endorser_pre_front(in) getbitfield(in+0x63, 1, 2) #define get_IN_endorser_post_back(in) getbitfield(in+0x63, 1, 1) #define get_IN_endorser_post_front(in) getbitfield(in+0x63, 1, 0) #define get_IN_x_overscan_size(in) getnbyte(in + 0x64, 2) #define get_IN_y_overscan_size(in) getnbyte(in + 0x66, 2) #define get_IN_default_bg_adf_b(in) getbitfield(in+0x68, 1, 3) #define get_IN_default_bg_adf_f(in) getbitfield(in+0x68, 1, 2) #define get_IN_default_bg_fb(in) getbitfield(in+0x68, 1, 1) #define get_IN_auto_color(in) getbitfield(in+0x69, 1, 7) #define get_IN_blank_skip(in) getbitfield(in+0x69, 1, 6) #define get_IN_multi_image(in) getbitfield(in+0x69, 1, 5) #define get_IN_f_b_type_indep(in) getbitfield(in+0x69, 1, 4) #define get_IN_f_b_res_indep(in) getbitfield(in+0x69, 1, 3) #define get_IN_dropout_spec(in) getbitfield(in+0x6a, 1, 7) #define get_IN_dropout_non(in) getbitfield(in+0x6a, 1, 7) #define get_IN_dropout_white(in) getbitfield(in+0x6a, 1, 7) #define get_IN_skew_check(in) getbitfield(in+0x6d, 1, 7) #define get_IN_new_fd_roll(in) getbitfield(in+0x6d, 1, 6) #define get_IN_paper_prot_2(in) getbitfield(in+0x6d, 1, 1) #define get_IN_evpd_len(in) getnbyte(in + 0x6f, 1) #define get_IN_paper_count(in) getbitfield(in+0x70, 1, 7) #define get_IN_paper_number(in) getbitfield(in+0x70, 1, 6) #define get_IN_ext_send_to(in) getbitfield(in+0x70, 1, 5) #define get_IN_staple_det(in) getbitfield(in+0x70, 1, 4) #define get_IN_pause_host(in) getbitfield(in+0x70, 1, 3) #define get_IN_pause_panel(in) getbitfield(in+0x70, 1, 2) #define get_IN_pause_conf(in) getbitfield(in+0x70, 1, 1) #define get_IN_hq_print(in) getbitfield(in+0x70, 1, 0) #define get_IN_ext_GHS_len(in) getnbyte(in + 0x71, 1) #define get_IN_smbc_func(in) getbitfield(in+0x72, 1, 7) #define get_IN_imprint_chk_b(in) getbitfield(in+0x72, 1, 6) #define get_IN_imprint_chk_f(in) getbitfield(in+0x72, 1, 5) #define get_IN_force_w_bg(in) getbitfield(in+0x72, 1, 4) #define get_IN_mf_recover_lvl(in) getbitfield(in+0x72, 0x0f, 0) #define get_IN_first_read_time(in) getbitfield(in+0x73, 1, 7) #define get_IN_div_scanning(in) getbitfield(in+0x73, 1, 6) #define get_IN_start_job(in) getbitfield(in+0x73, 1, 5) #define get_IN_lifetime_log(in) getbitfield(in+0x73, 1, 4) #define get_IN_imff_save_rest(in) getbitfield(in+0x73, 1, 3) #define get_IN_wide_scsi_type(in) getbitfield(in+0x73, 0x07, 0) #define get_IN_lut_hybrid_crop(in) getbitfield(in+0x74, 1, 7) #define get_IN_over_under_amt(in) getbitfield(in+0x74, 1, 6) #define get_IN_rgb_lut(in) getbitfield(in+0x74, 1, 5) #define get_IN_num_lut_dl(in) getbitfield(in+0x74, 0x0f, 0) /*byte 75 is poorly documented*/ #define get_IN_erp_lot6_supp(in) getbitfield(in+0x76, 1, 7) #define get_IN_mode_change_jpeg(in) getbitfield(in+0x76, 1, 5) #define get_IN_mode_change_irdc(in) getbitfield(in+0x76, 1, 4) #define get_IN_mode_change_iomf(in) getbitfield(in+0x76, 1, 3) #define get_IN_sync_next_feed(in) getbitfield(in+0x76, 0x07, 0) #define get_IN_imp_func3(in) getbitfield(in+0x77, 1, 7) #define get_IN_reset_ms(in) getbitfield(in+0x78, 1, 7) #define get_IN_read_size(in) getbitfield(in+0x78, 1, 6) #define get_IN_start_end_ms(in) getbitfield(in+0x78, 1, 5) #define get_IN_battery(in) getbitfield(in+0x79, 1, 7) #define get_IN_battery_save(in) getbitfield(in+0x79, 1, 6) #define get_IN_op_reverse(in) getbitfield(in+0x79, 1, 1) #define get_IN_op_halt(in) getbitfield(in+0x7a, 1, 7) #define get_IN_return_path(in) getbitfield(in+0x7c, 1, 7) #define get_IN_energy_star3(in) getbitfield(in+0x7c, 1, 6) /* ==================================================================== */ /* page codes used by mode_sense and mode_select */ #define MS_pc_unk 0x2c /* Used by iX500 */ #define MS_pc_patch 0x2e /* Patch code scanning */ #define MS_pc_counter 0x2f /* Page number and counter reset */ #define MS_pc_autocolor 0x32 /* Automatic color detection */ #define MS_pc_prepick 0x33 /* Prepick next adf page */ #define MS_pc_sleep 0x34 /* Sleep mode */ #define MS_pc_duplex 0x35 /* ADF duplex transfer mode */ #define MS_pc_rand 0x36 /* All sorts of device controls */ #define MS_pc_bg 0x37 /* Backing switch control */ #define MS_pc_df 0x38 /* Double feed detection */ #define MS_pc_dropout 0x39 /* Drop out color */ #define MS_pc_buff 0x3a /* Scan buffer control */ #define MS_pc_auto 0x3c /* Auto paper size detection */ #define MS_pc_lamp 0x3d /* Lamp light timer set */ #define MS_pc_jobsep 0x3e /* Detect job separation sheet */ #define MS_pc_all 0x3f /* Only used with mode_sense */ /* ==================================================================== */ /* MODE_SELECT */ #define MODE_SELECT_code 0x15 #define MODE_SELECT_len 6 #define set_MSEL_pf(sb, val) setbitfield(sb + 1, 1, 4, val) #define set_MSEL_xferlen(sb, val) sb[0x04] = (unsigned char)val /* MS payloads are combined 4 byte header and 8 or 10 byte page * there is also 'descriptor block' & 'vendor-specific block' * but fujitsu seems not to use these */ /* 10 byte page only used by dropout? */ #define MSEL_header_len 4 #define MSEL_data_min_len 8 #define MSEL_data_max_len 10 #define set_MSEL_pc(sb, val) sb[0x00]=val #define set_MSEL_page_len(sb, val) sb[0x01]=val #define set_MSEL_sleep_mode(sb, val) sb[0x02]=val #define set_MSEL_transfer_mode(sb, val) setbitfield(sb + 0x02, 0x01, 0, val) #define set_MSEL_bg_enable(sb, val) setbitfield(sb + 2, 1, 7, val) #define set_MSEL_bg_front(sb, val) setbitfield(sb + 2, 1, 5, val) #define set_MSEL_bg_back(sb, val) setbitfield(sb + 2, 1, 4, val) #define set_MSEL_bg_fb(sb, val) setbitfield(sb + 2, 1, 3, val) #define set_MSEL_df_enable(sb, val) setbitfield(sb + 2, 1, 7, val) #define set_MSEL_df_continue(sb, val) setbitfield(sb + 2, 1, 6, val) #define set_MSEL_df_skew(sb, val) setbitfield(sb + 2, 1, 5, val) #define set_MSEL_df_thickness(sb, val) setbitfield(sb + 2, 1, 4, val) #define set_MSEL_df_length(sb, val) setbitfield(sb + 2, 1, 3, val) #define set_MSEL_df_diff(sb, val) setbitfield(sb + 2, 3, 0, val) #define MSEL_df_diff_DEFAULT 0 #define MSEL_df_diff_10MM 1 #define MSEL_df_diff_15MM 2 #define MSEL_df_diff_20MM 3 #define set_MSEL_df_paperprot(sb, val) setbitfield(sb + 3, 3, 6, val) #define set_MSEL_df_stapledet(sb, val) setbitfield(sb + 3, 3, 4, val) #define set_MSEL_df_recovery(sb, val) setbitfield(sb + 3, 3, 2, val) #define set_MSEL_df_paperprot2(sb, val) setbitfield(sb + 5, 3, 6, val) #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) #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 #define set_MSEL_buff_mode(sb, val) setbitfield(sb + 0x02, 0x03, 6, val) #define set_MSEL_buff_clear(sb, val) setbitfield(sb + 0x03, 0x03, 6, val) #define set_MSEL_prepick(sb, val) setbitfield(sb + 0x02, 0x03, 6, val) /*more automatic stuff with this one...*/ #define set_MSEL_awd(sb, val) setbitfield(sb + 0x02, 0x01, 7, val) #define set_MSEL_w_wfill(sb, val) setbitfield(sb + 0x02, 0x01, 6, val) #define set_MSEL_req_driv_lut(sb, val) setbitfield(sb + 0x02, 0x01, 1, val) #define set_MSEL_req_driv_crop(sb, val) setbitfield(sb + 0x02, 0x01, 0, val) #define set_MSEL_ald(sb, val) setbitfield(sb + 0x03, 0x01, 7, val) #define set_MSEL_l_wfill(sb, val) setbitfield(sb + 0x03, 0x01, 6, val) #define set_MSEL_deskew(sb, val) setbitfield(sb + 0x04, 0x01, 7, val) #define set_MSEL_overscan(sb, val) setbitfield(sb + 0x05, 0x03, 6, val) #define set_MSEL_overcrop(sb, val) setbitfield(sb + 0x05, 0x01, 5, val) #define set_MSEL_undercrop(sb, val) setbitfield(sb + 0x05, 0x01, 4, val) #define set_MSEL_over_under_amt(sb, val) sb[0x06]=val /*buffer, prepick, overscan and df use these*/ #define MSEL_DEFAULT 0 #define MSEL_OFF 2 #define MSEL_ON 3 /* ==================================================================== */ /* RESERVE_UNIT */ #define RESERVE_UNIT_code 0x16 #define RESERVE_UNIT_len 6 /* ==================================================================== */ /* RELEASE_UNIT */ #define RELEASE_UNIT_code 0x17 #define RELEASE_UNIT_len 6 /* ==================================================================== */ /* MODE_SENSE */ #define MODE_SENSE_code 0x1a #define MODE_SENSE_len 6 #define MODE_SENSE_data_len 0x14 #define set_MSEN_DBD(b, val) setbitfield(b, 0x01, 3, (val?1:0)) #define set_MSEN_pc(sb, val) setbitfield(sb + 0x02, 0x3f, 0, val) #define set_MSEN_xfer_length(sb, val) sb[0x04] = (unsigned char)val #define get_MSEN_MUD(b) getnbyte(b+(0x04+((int)*(b+0x3)))+0x4,2) /* ==================================================================== */ /* SCAN */ #define SCAN_code 0x1b #define SCAN_len 6 #define set_SC_xfer_length(sb, val) sb[0x04] = (unsigned char)val /* ==================================================================== */ /* READ_DIAGNOSTIC */ #define READ_DIAGNOSTIC_code 0x1c #define READ_DIAGNOSTIC_len 6 #define set_RD_xferlen(in, len) putnbyte(in + 3, len, 2) /* for 'FIRST READ DATE \0YMD' */ #define RD_frd_len 10 #define get_RD_date_status(in) in[0] #define RD_date_stored 0 #define RD_date_not_stored 0xff /* for 'GET FIRST DATE ' */ #define RD_gfd_len 10 #define get_RD_date_year(in) in[1] #define get_RD_date_month(in) in[2] #define get_RD_date_date(in) in[3] /* for 'GET DEVICE ID ' */ #define RD_gdi_len 10 #define get_RD_id_serial(in) getnbyte (in, 4) /* ==================================================================== */ /* SEND_DIAGNOSTIC */ #define SEND_DIAGNOSTIC_code 0x1d #define SEND_DIAGNOSTIC_len 6 #define set_SD_slftst(in, val) setbitfield(in + 1, 1, 2, val) #define set_SD_xferlen(in, len) putnbyte(in + 3, len, 2) #define SD_frd_string "FIRST READ DATE \0YMD" #define SD_frd_len 20 #define set_SD_frd_year(in, b) putnbyte(in + 0x11, b, 1) #define set_SD_frd_month(in, b) putnbyte(in + 0x12, b, 1) #define set_SD_frd_date(in, b) putnbyte(in + 0x13, b, 1) #define SD_gfd_string "GET FIRST DATE " #define SD_gfd_len 16 #define SD_gdi_string "GET DEVICE ID " #define SD_gdi_len 16 #define SD_preread_string "SET PRE READMODE" #define SD_preread_stringlen 16 #define SD_preread_len 32 #define set_SD_preread_xres(in, b) putnbyte(in + 0x10, b, 2) #define set_SD_preread_yres(in, b) putnbyte(in + 0x12, b, 2) #define set_SD_preread_paper_width(sb, val) putnbyte(sb + 0x14, val, 4) #define set_SD_preread_paper_length(sb, val) putnbyte(sb + 0x18, val, 4) #define set_SD_preread_composition(sb, val) putnbyte(sb + 0x1c, val, 1) #define set_SD_preread_escan(sb, val) putnbyte(sb + 0x1d, val, 1) #define SD_powoff_string "SET POWOFF TIME " #define SD_powoff_stringlen 16 #define SD_powoff_len 18 #define set_SD_powoff_disable(in, val) setbitfield(in + 16, 1, 7, val) #define set_SD_powoff_interval(in, val) setbitfield(in + 16, 0x7f, 0, val) #define set_SD_powoff_notify(sb, val) putnbyte(sb + 0x17, val, 1) /* ==================================================================== */ /* SET_WINDOW */ #define SET_WINDOW_code 0x24 #define SET_WINDOW_len 10 #define set_SW_xferlen(sb, len) putnbyte(sb + 0x06, len, 3) #define SW_header_len 8 #define SW_desc_len 64 /* ==================================================================== */ /* GET_WINDOW */ #define GET_WINDOW_code 0x25 #define GET_WINDOW_len 0 /* ==================================================================== */ /* READ */ #define READ_code 0x28 #define READ_len 10 #define set_R_datatype_code(sb, val) sb[0x02] = val #define R_datatype_imagedata 0x00 #define R_datatype_pixelsize 0x80 #define R_datatype_papersize 0x81 #define R_datatype_effective_id 0x82 #define set_R_window_id(sb, val) sb[0x05] = val #define set_R_xfer_length(sb, val) putnbyte(sb + 0x06, val, 3) #define R_PSIZE_len 0x20 #define get_PSIZE_num_x(in) getnbyte(in + 0x00, 4) #define get_PSIZE_num_y(in) getnbyte(in + 0x04, 4) #define get_PSIZE_paper_w(in) getnbyte(in + 0x08, 4) #define get_PSIZE_paper_l(in) getnbyte(in + 0x0C, 4) #define get_PSIZE_req_driv_crop(in) getbitfield(in + 0x10, 1, 7) #define get_PSIZE_req_driv_lut(in) getbitfield(in + 0x10, 1, 6) #define get_PSIZE_req_driv_valid(in) getbitfield(in + 0x10, 1, 0) #define R_PAPER_len 0x08 #define get_PAPER_job_sep(in) getnbyte(in + 0x02, 1) #define get_PAPER_paper_w(in) getnbyte(in + 0x03, 1) /* ==================================================================== */ /* SEND */ #define SEND_code 0x2a #define SEND_len 10 #define set_S_xfer_datatype(sb, val) sb[0x02] = (unsigned char)val #define S_datatype_halftone_mask 0x02 #define S_datatype_gamma_function 0x03 #define S_datatype_lut_data 0x83 #define S_datatype_lut_dropout 0x84 #define S_datatype_jpg_q_table 0x88 #define S_datatype_endorser_data 0x90 #define S_datatype_sendto_name 0xa0 /*#define S_EX_datatype_lut 0x01 #define S_EX_datatype_shading_data 0xa0 #define S_user_reg_gamma 0xc0 #define S_device_internal_info 0x03 #define set_S_datatype_qual_upper(sb, val) sb[0x04] = (unsigned char)val #define S_DQ_none 0x00 #define S_DQ_Rcomp 0x06 #define S_DQ_Gcomp 0x07 #define S_DQ_Bcomp 0x08 #define S_DQ_Reg1 0x01 #define S_DQ_Reg2 0x02 #define S_DQ_Reg3 0x03*/ #define set_S_xfer_id(sb, val) putnbyte(sb + 4, val, 2) #define set_S_xfer_length(sb, val) putnbyte(sb + 6, val, 3) /*lut*/ #define S_lut_header_len 0x0a #define set_S_lut_order(sb, val) putnbyte(sb + 2, val, 1) #define S_lut_order_single 0x10 #define set_S_lut_ssize(sb, val) putnbyte(sb + 4, val, 2) #define set_S_lut_dsize(sb, val) putnbyte(sb + 6, val, 2) #define S_lut_data_min_len 256 #define S_lut_data_max_len 1024 /*q-table*/ #define S_q_table_header_len 0x0a #define S_q_table_y_len 0x40 #define set_S_q_table_y_len(sb, val) putnbyte(sb + 4, val, 2) #define S_q_table_uv_len 0x40 #define set_S_q_table_uv_len(sb, val) putnbyte(sb + 6, val, 2) /*endorser*/ #define S_e_data_min_len 18 /*minimum 18 bytes no string bytes*/ #define S_e_data_max_len 98 /*maximum 18 bytes plus 80 string bytes*/ #define set_S_endorser_data_id(sb, val) sb[0] = val #define set_S_endorser_stamp(sb, val) setbitfield(sb + 0x01, 1, 7, val) #define set_S_endorser_elec(sb, val) setbitfield(sb + 0x01, 1, 6, val) #define set_S_endorser_decr(sb, val) setbitfield(sb + 0x01, 1, 5, val) #define S_e_decr_inc 0 #define S_e_decr_dec 1 #define set_S_endorser_lap24(sb, val) setbitfield(sb + 0x01, 1, 4, val) #define S_e_lap_24bit 1 #define S_e_lap_16bit 0 #define set_S_endorser_ctstep(sb, val) setbitfield(sb + 0x01, 0x03, 0, val) #define set_S_endorser_ulx(sb, val) putnbyte(sb + 0x02, val, 4) #define set_S_endorser_uly(sb, val) putnbyte(sb + 0x06, val, 4) #define set_S_endorser_font(sb, val) sb[0xa] = val #define S_e_font_horiz 0 #define S_e_font_vert 1 #define S_e_font_horiz_narrow 2 #define set_S_endorser_size(sb, val) sb[0xb] = val #define set_S_endorser_revs(sb, val) setbitfield(sb + 0x0c, 0x01, 7, val) #define S_e_revs_fwd 0 #define S_e_revs_rev 1 #define set_S_endorser_bold(sb, val) setbitfield(sb + 0x0c, 0x01, 2, val) #define set_S_endorser_dirs(sb, val) setbitfield(sb + 0x0c, 0x03, 0, val) #define S_e_dir_left_right 0 #define S_e_dir_top_bottom 1 #define S_e_dir_right_left 2 #define S_e_dir_bottom_top 3 #define set_S_endorser_string_length(sb, len) sb[0x11] = len #define set_S_endorser_string(sb,val,len) memcpy(sb+0x12,val,(size_t)len) /* ==================================================================== */ /* OBJECT_POSITION */ #define OBJECT_POSITION_code 0x31 #define OBJECT_POSITION_len 10 #define set_OP_action(b,val) setbitfield(b+0x01, 0x07, 0, val) #define OP_Discharge 0x00 #define OP_Feed 0x01 #define OP_Halt 0x04 /* ==================================================================== */ /* SET_SUBWINDOW */ #define SET_SUBWINDOW_code 0xc0 #define SET_SUBWINDOW_len 0 /* ==================================================================== */ /* ENDORSER */ #define ENDORSER_code 0xc1 #define ENDORSER_len 10 #define set_E_xferlen(sb, val) putnbyte(sb + 0x7, val, 2) /*endorser data*/ #define ED_min_len 4 #define ED_max_len 6 #define set_ED_endorser_data_id(sb, val) sb[0] = val /* enable/disable endorser printing*/ #define set_ED_stop(sb, val) setbitfield(sb + 0x01, 1, 7, val) #define ED_start 0 #define ED_stop 1 /* specifies the side of a document to be printed */ #define set_ED_side(sb, val) setbitfield(sb + 0x01, 1, 6, val) #define ED_front 0 #define ED_back 1 /* format of the counter 16/24 bit*/ #define set_ED_lap24(sb, val) setbitfield(sb + 0x01, 1, 5, val) #define ED_lap_16bit 0 #define ED_lap_24bit 1 /* initial count */ #define set_ED_initial_count_16(sb, val) putnbyte(sb + 0x02, val, 2) #define set_ED_initial_count_24(sb, val) putnbyte(sb + 0x03, val, 3) /* ==================================================================== */ /* GET_HW_STATUS*/ #define GET_HW_STATUS_code 0xc2 #define GET_HW_STATUS_len 10 #define set_GHS_allocation_length(sb, len) putnbyte(sb + 0x07, len, 2) #define GHS_data_len 12 #define get_GHS_top(in) getbitfield(in+0x02, 1, 7) #define get_GHS_fedalm(in) getbitfield(in+0x02, 1, 5) #define get_GHS_adjalm(in) getbitfield(in+0x02, 1, 4) #define get_GHS_A3(in) getbitfield(in+0x02, 1, 3) #define get_GHS_B4(in) getbitfield(in+0x02, 1, 2) #define get_GHS_A4(in) getbitfield(in+0x02, 1, 1) #define get_GHS_B5(in) getbitfield(in+0x02, 1, 0) #define get_GHS_hopper(in) !getbitfield(in+0x03, 1, 7) #define get_GHS_omr(in) getbitfield(in+0x03, 1, 6) #define get_GHS_adf_open(in) getbitfield(in+0x03, 1, 5) #define get_GHS_imp_open(in) getbitfield(in+0x03, 1, 4) #define get_GHS_fb_open(in) getbitfield(in+0x03, 1, 3) #define get_GHS_paper_end(in) getbitfield(in+0x03, 1, 2) #define get_GHS_fb_on(in) getbitfield(in+0x03, 1, 1) #define get_GHS_exit(in) getbitfield(in+0x03, 1, 0) #define get_GHS_sleep(in) getbitfield(in+0x04, 1, 7) #define get_GHS_clean(in) getbitfield(in+0x04, 1, 6) #define get_GHS_scan_sw_long(in) getbitfield(in+0x04, 1, 5) #define get_GHS_hpos(in) getbitfield(in+0x04, 1, 4) #define get_GHS_send_sw(in) getbitfield(in+0x04, 1, 2) #define get_GHS_manual_feed(in) getbitfield(in+0x04, 1, 1) #define get_GHS_scan_sw(in) getbitfield(in+0x04, 1, 0) #define get_GHS_picalm(in) getbitfield(in+0x05, 1, 7) #define get_GHS_padalm(in) getbitfield(in+0x05, 1, 6) #define get_GHS_brkalm(in) getbitfield(in+0x05, 1, 5) #define get_GHS_sepalm(in) getbitfield(in+0x05, 1, 4) #define get_GHS_function(in) getbitfield(in+0x05, 0x0f, 0) #define get_GHS_ink_empty(in) getbitfield(in+0x06, 1, 7) #define get_GHS_consume(in) getbitfield(in+0x06, 1, 6) #define get_GHS_overskew(in) getbitfield(in+0x06, 1, 5) #define get_GHS_overthick(in) getbitfield(in+0x06, 1, 4) #define get_GHS_plen(in) getbitfield(in+0x06, 1, 3) #define get_GHS_ink_side(in) getbitfield(in+0x06, 1, 2) #define get_GHS_mf_to(in) getbitfield(in+0x06, 1, 1) #define get_GHS_double_feed(in) getbitfield(in+0x06, 1, 0) #define get_GHS_error_code(in) in[0x07] #define get_GHS_skew_angle(in) in[0x09] #define get_GHS_ink_remain(in) in[0x0a] #define get_GHS_lang_code(in) getnbyte(in+0x0c, 2) #define get_GHS_adjalm_fed(in) getbitfield(in+0x0e, 1, 7) #define get_GHS_non_sep(in) getbitfield(in+0x0e, 1, 4) #define get_GHS_ext_sendto(in) getbitfield(in+0x0e, 1, 2) #define get_GHS_rq_hldimg(in) getbitfield(in+0x0e, 1, 1) #define get_GHS_pacnt(in) getbitfield(in+0x0e, 1, 0) #define get_GHS_wifi_sw(in) getbitfield(in+0x10, 1, 7) #define get_GHS_w_use(in) getbitfield(in+0x10, 1, 6) #define get_GHS_w_use2(in) getbitfield(in+0x10, 1, 5) #define get_GHS_w_use3(in) getbitfield(in+0x10, 1, 4) #define get_GHS_w_use4(in) getbitfield(in+0x10, 1, 3) #define get_GHS_battery(in) getbitfield(in+0x11, 1, 7) #define get_GHS_btr_charge(in) getbitfield(in+0x11, 1, 6) #define get_GHS_btr_chg_tmp_stp(in) getbitfield(in+0x11, 1, 5) #define get_GHS_ibtr_ene_sav(in) getbitfield(in+0x11, 1, 4) #define get_GHS_fngr_caut(in) getbitfield(in+0x11, 1, 2) #define get_GHS_trnpg_l(in) getbitfield(in+0x11, 1, 1) #define get_GHS_trnpg_r(in) getbitfield(in+0x11, 1, 0) #define get_GHS_btr_power(in) in[0x12] /* ==================================================================== */ /* SCANNER_CONTROL */ #define SCANNER_CONTROL_code 0xf1 #define SCANNER_CONTROL_len 10 #define set_SC_ric(icb, val) setbitfield(icb + 1, 1, 4, val) #define set_SC_function_1(icb, val) setbitfield(icb + 1, 0xf, 0, val) #define set_SC_function_2(icb, val) icb[2] = (val >> 4) #define SC_function_adf 0x00 #define SC_function_fb 0x01 #define SC_function_fb_hs 0x02 #define SC_function_lamp_off 0x03 #define SC_function_cancel 0x04 #define SC_function_lamp_on 0x05 #define SC_function_lamp_normal 0x06 #define SC_function_lamp_saving 0x07 #define SC_function_panel 0x08 #define SC_function_scan_complete 0x09 #define SC_function_eject_complete 0x0a #define SC_function_manual_feed 0x0c #define SC_function_mfeed 0x0f #define SC_function_continuous 0x1f #define SC_function_rpath 0x2f /* used with SC_function_panel */ #define set_SC_led_eb(icb, val) setbitfield(icb + 5, 1, 7, val) #define set_SC_led(icb, val) setbitfield(icb + 5, 1, 6, val) #define set_SC_fcno_eb(icb, val) setbitfield(icb + 5, 1, 4, val) #define set_SC_fcno(icb, val) setbitfield(icb + 5, 0xf, 0, val) #define set_SC_ric_dtq(sb, val) sb[2] = val #define set_SC_ric_len(sb, val) putnbyte(sb + 0x06, val, 3) /* ==================================================================== */ /* window descriptor macros for SET_WINDOW and GET_WINDOW */ #define set_WPDB_wdblen(sb, len) putnbyte(sb + 0x06, len, 2) /* ==================================================================== */ /* 0x00 - Window Identifier * 0x00 for 3096 * 0x00 (front) or 0x80 (back) for 3091 */ #define set_WD_wid(sb, val) sb[0] = val #define WD_wid_front 0x00 #define WD_wid_back 0x80 /* 0x01 - Reserved (bits 7-1), AUTO (bit 0) * Use 0x00 for 3091, 3096 */ #define set_WD_auto(sb, val) setbitfield(sb + 0x01, 1, 0, val) #define get_WD_auto(sb) getbitfield(sb + 0x01, 1, 0) /* 0x02,0x03 - X resolution in dpi * 3091 supports 50-300 in steps of 1 * 3096 suppors 200,240,300,400; or 100-1600 in steps of 4 * if image processiong option installed */ #define set_WD_Xres(sb, val) putnbyte(sb + 0x02, val, 2) #define get_WD_Xres(sb) getnbyte(sb + 0x02, 2) /* 0x04,0x05 - X resolution in dpi * 3091 supports 50-600 in steps of 1; 75,150,300,600 only * in color mode * 3096 suppors 200,240,300,400; or 100-1600 in steps of 4 * if image processiong option installed */ #define set_WD_Yres(sb, val) putnbyte(sb + 0x04, val, 2) #define get_WD_Yres(sb) getnbyte(sb + 0x04, 2) /* 0x06-0x09 - Upper Left X in 1/1200 inch */ #define set_WD_ULX(sb, val) putnbyte(sb + 0x06, val, 4) #define get_WD_ULX(sb) getnbyte(sb + 0x06, 4) /* 0x0a-0x0d - Upper Left Y in 1/1200 inch */ #define set_WD_ULY(sb, val) putnbyte(sb + 0x0a, val, 4) #define get_WD_ULY(sb) getnbyte(sb + 0x0a, 4) /* 0x0e-0x11 - Width in 1/1200 inch * 3091 left+width max 10200 * 3096 left+width max 14592 * also limited to page size, see bytes 0x35ff. */ #define set_WD_width(sb, val) putnbyte(sb + 0x0e, val, 4) #define get_WD_width(sb) getnbyte(sb + 0x0e, 4) /* 0x12-0x15 - Height in 1/1200 inch * 3091 top+height max 16832 * 3096 top+height max 20736, also if left+width>13199, * top+height has to be less than 19843 */ #define set_WD_length(sb, val) putnbyte(sb + 0x12, val, 4) #define get_WD_length(sb) getnbyte(sb + 0x12, 4) /* 0x16 - Brightness * 3091 always use 0x00 * 3096 if in halftone mode, 8 levels supported (01-1F, 20-3F, ..., E0-FF) * use 0x00 for user defined dither pattern */ #define set_WD_brightness(sb, val) sb[0x16] = val #define get_WD_brightness(sb) sb[0x16] /* 0x17 - Threshold * 3091 0x00 = use floating slice; 0x01..0xff fixed slice * with 0x01=brightest, 0x80=medium, 0xff=darkest; * only effective for line art mode. * 3096 0x00 = use "simplified dynamic threshold", otherwise * same as above but resolution is only 64 steps. */ #define set_WD_threshold(sb, val) sb[0x17] = val #define get_WD_threshold(sb) sb[0x17] /* 0x18 - Contrast * 3091 - not supported, always use 0x00 * 3096 - the same */ #define set_WD_contrast(sb, val) sb[0x18] = val #define get_WD_contrast(sb) sb[0x18] /* 0x19 - Image Composition (color mode) * 3091 - use 0x00 for line art, 0x01 for halftone, * 0x02 for grayscale, 0x05 for color. * 3096 - same but minus color. */ #define set_WD_composition(sb, val) sb[0x19] = val #define get_WD_composition(sb) sb[0x19] #define WD_comp_LA 0 #define WD_comp_HT 1 #define WD_comp_GS 2 #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 * 3096 - use 0x01 for b/w or 0x08 for gray */ #define set_WD_bitsperpixel(sb, val) sb[0x1a] = val #define get_WD_bitsperpixel(sb) sb[0x1a] /* 0x1b,0x1c - Halftone Pattern * 3091 byte 1b: 00h default(=dither), 01h dither, * 02h error dispersion * 1c: 00 dark images, 01h dark text+images, * 02h light images, * 03h light text+images, 80h download pattern * 3096: 1b unused; 1c bit 7=1: use downloadable pattern, * bit 7=0: use builtin pattern; rest of byte 1b denotes * pattern number, three builtin and five downloadable * supported; higher numbers = error. */ #define set_WD_ht_type(sb, val) sb[0x1b] = val #define get_WD_ht_type(sb) sb[0x1b] #define WD_ht_type_DEFAULT 0 #define WD_ht_type_DITHER 1 #define WD_ht_type_DIFFUSION 2 #define set_WD_ht_pattern(sb, val) sb[0x1c] = val #define get_WD_ht_pattern(sb) sb[0x1c] /* 0x1d - Reverse image, padding type * 3091: bit 7=1: reverse black&white * bits 0-2: padding type, must be 0 * 3096: the same; bit 7 must be set for gray and not * set for b/w. */ #define set_WD_rif(sb, val) setbitfield(sb + 0x1d, 1, 7, val) #define get_WD_rif(sb) getbitfield(sb + 0x1d, 1, 7) /* 0x1e,0x1f - Bit ordering * 3091 not supported, use 0x00 * 3096 not supported, use 0x00 */ #define set_WD_bitorder(sb, val) putnbyte(sb + 0x1e, val, 2) #define get_WD_bitorder(sb) getnbyte(sb + 0x1e, 2) /* 0x20 - compression type * not supported on smaller models, use 0x00 */ #define set_WD_compress_type(sb, val) sb[0x20] = val #define get_WD_compress_type(sb) sb[0x20] #define WD_cmp_NONE 0 #define WD_cmp_MH 1 #define WD_cmp_MR 2 #define WD_cmp_MMR 3 #define WD_cmp_JBIG 0x80 #define WD_cmp_JPG1 0x81 #define WD_cmp_JPG2 0x82 #define WD_cmp_JPG3 0x83 /* 0x21 - compression argument * specify "k" parameter with MR compress, * or with JPEG- Q param, 0-7 */ #define set_WD_compress_arg(sb, val) sb[0x21] = val #define get_WD_compress_arg(sb) sb[0x21] /* 0x22-0x27 - reserved */ /* 0x28 - vendor unique id code, decides meaning of remaining bytes * 0xc1 = color mode (fi-series) * 0xc0 = weird mode (M3091 and M3092) * 0x00 = mono mode (other M-series and fi-series) */ #define set_WD_vendor_id_code(sb, val) sb[0x28] = val #define get_WD_vendor_id_code(sb) sb[0x28] #define WD_VUID_MONO 0x00 #define WD_VUID_3091 0xc0 #define WD_VUID_COLOR 0xc1 /* 0x29 common gamma */ #define set_WD_gamma(sb, val) sb[0x29] = val #define get_WD_gamma(sb) sb[0x29] #define WD_gamma_DEFAULT 0 #define WD_gamma_NORMAL 1 #define WD_gamma_SOFT 2 #define WD_gamma_SHARP 3 /*==================================================================*/ /* 0x2a-0x3F - vary based on vuid */ /*==================================================================*/ /* vuid 0x00, mono params */ #define set_WD_outline(sb, val) setbitfield(sb + 0x2a, 1, 7, val) #define get_WD_outline(sb) getbitfield(sb + 0x2a, 1, 7) #define set_WD_emphasis(sb, val) sb[0x2b] = val #define get_WD_emphasis(sb) sb[0x2b] #define set_WD_separation(sb, val) setbitfield(sb + 0x2c, 1, 7, val) #define get_WD_separation(sb) getbitfield(sb + 0x2c, 1, 7) #define set_WD_mirroring(sb, val) setbitfield(sb + 0x2d, 1, 7, val) #define get_WD_mirroring(sb) getbitfield(sb + 0x2d, 1, 7) /* SDTC also called Auto-II mode?*/ #define set_WD_variance(sb, val) sb[0x2e] = val #define get_WD_variance(sb) sb[0x2e] /* DTC also called Auto-I mode?*/ /*warning: filtering uses inverse logic*/ #define set_WD_filtering(sb, val) setbitfield(sb + 0x2f, 1, 7, !val) #define get_WD_filtering(sb) !getbitfield(sb + 0x2f, 1, 7) /*warning: smoothing uses inverse logic*/ #define set_WD_smoothing(sb, val) setbitfield(sb + 0x2f, 3, 5, !val) #define get_WD_smoothing(sb) !getbitfield(sb + 0x2f, 3, 5) #define set_WD_gamma_curve(sb, val) setbitfield(sb + 0x2f, 3, 3, val) #define get_WD_gamma_curve(sb) getbitfield(sb + 0x2f, 3, 3) #define set_WD_threshold_curve(sb, val) setbitfield(sb + 0x2f, 7, 0, val) #define get_WD_threshold_curve(sb) getbitfield(sb + 0x2f, 7, 0) /*warning: noise removal uses inverse logic*/ #define set_WD_noise_removal(sb, val) setbitfield(sb + 0x30, 1, 5, !val) #define get_WD_noise_removal(sb) !getbitfield(sb + 0x30, 1, 5) #define set_WD_matrix5x5(sb, val) setbitfield(sb + 0x30, 1, 4, val) #define get_WD_matrix5x5(sb) getbitfield(sb + 0x30, 1, 4) #define set_WD_matrix4x4(sb, val) setbitfield(sb + 0x30, 1, 3, val) #define get_WD_matrix4x4(sb) getbitfield(sb + 0x30, 1, 3) #define set_WD_matrix3x3(sb, val) setbitfield(sb + 0x30, 1, 2, val) #define get_WD_matrix3x3(sb) getbitfield(sb + 0x30, 1, 2) #define set_WD_matrix2x2(sb, val) setbitfield(sb + 0x30, 1, 1, val) #define get_WD_matrix2x2(sb) getbitfield(sb + 0x30, 1, 1) #define set_WD_background(sb, val) setbitfield(sb + 0x30, 1, 0, val) #define get_WD_background(sb) getbitfield(sb + 0x30, 1, 0) #define WD_background_WHITE 0 #define WD_background_BLACK 1 /*31 reserved*/ #define set_WD_wl_follow(sb, val) setbitfield(sb + 0x32, 3, 6, val) #define get_WD_wl_follow(sb) getbitfield(sb + 0x32, 3, 6) #define WD_wl_follow_DEFAULT 0 #define WD_wl_follow_ON 2 #define WD_wl_follow_OFF 3 #define set_WD_subwindow_list(sb, val) putnbyte(sb + 0x33, val, 2) #define get_WD_subwindow_list(sb) getnbyte(sb + 0x33, 2) /* 0x35-0x3d - paper size */ #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 #define set_WD_paper_width_X(sb, val) putnbyte(sb + 0x36, val, 4) #define get_WD_paper_width_X(sb) getnbyte(sb + 0x36, 4) #define set_WD_paper_length_Y(sb, val) putnbyte(sb+0x3a, val, 4) #define get_WD_paper_length_Y(sb) getnbyte(sb+0x3a, 4) /* 3e switch ipc mode */ #define set_WD_ipc_mode(sb, val) setbitfield(sb + 0x3e, 3, 6, val) #define get_WD_ipc_mode(sb) getbitfield(sb + 0x3e, 3, 6) #define WD_ipc_DEFAULT 0 #define WD_ipc_DTC 1 #define WD_ipc_SDTC 2 /*3f reserved*/ /*==================================================================*/ /* vuid 0xc1, color params */ #define set_WD_scanning_order(sb, val) sb[0x2a] = val #define get_WD_scanning_order(sb) sb[0x2a] #define WD_SCAN_ORDER_LINE 0 #define WD_SCAN_ORDER_DOT 1 #define WD_SCAN_ORDER_FACE 2 #define set_WD_scanning_order_arg(sb, val) sb[0x2b] = val #define get_WD_scanning_order_arg(sb) sb[0x2b] #define WD_SCAN_ARG_RGB 0 #define WD_SCAN_ARG_RBG 1 #define WD_SCAN_ARG_GRB 2 #define WD_SCAN_ARG_GBR 3 #define WD_SCAN_ARG_BRG 4 #define WD_SCAN_ARG_BGR 5 /*2c-2d reserved*/ /*like vuid 00, but in different location*/ #define set_WD_c1_emphasis(sb, val) sb[0x2e] = val #define get_WD_c1_emphasis(sb) sb[0x2e] #define set_WD_c1_mirroring(sb, val) setbitfield(sb + 0x2f, 1, 7, val) #define get_WD_c1_mirroring(sb) getbitfield(sb + 0x2f, 1, 7) /*30-31 reserved*/ /*32 wlf (see vuid 00)*/ /*33-34 reserved*/ /*35-3d paper size (see vuid 00)*/ /*3e-3f reserved*/ /*==================================================================*/ /* vuid 0xc0, 3091/2 params */ /*2a-2b same as vuid 0xc1*/ #define set_WD_lamp_color(sb, val) sb[0x2d] = val #define get_WD_lamp_color(sb) sb[0x2d] #define WD_LAMP_DEFAULT 0x00 #define WD_LAMP_BLUE 0x01 #define WD_LAMP_RED 0x02 #define WD_LAMP_GREEN 0x04 /*2e-31 reserved*/ #define set_WD_quality(sb, val) sb[0x32] = val #define get_WD_quality(sb) sb[0x32] #define WD_QUAL_NORMAL 0x00 #define WD_QUAL_HIGH 0x02 /*33-34 reserved*/ /*35-3d paper size (see vuid 00)*/ /*3e-3f reserved*/ /*FIXME: more params here*/ /* ==================================================================== */ #endif backends-1.3.0/backend/fujitsu.c000066400000000000000000011327571456256263500165470ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. This file is part of the SANE package, and implements a SANE backend for various Fujitsu and Ricoh scanners. Copyright (C) 2000 Randolph Bentson Copyright (C) 2001 Frederik Ramm Copyright (C) 2001-2004 Oliver Schirrmeister Copyright (C) 2003-2023 m. allan noah JPEG output and low memory usage support funded by: Archivista GmbH, www.archivista.ch Endorser support funded by: O A S Oilfield Accounting Service Ltd, www.oas.ca Automatic length detection support funded by: Martin G. Miller, mgmiller at optonline.net Software image enhancement routines and recent scanner support funded by: PFU America, Inc., fujitsuscanners.com -------------------------------------------------------------------------- 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, see . 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. -------------------------------------------------------------------------- The source code is divided in sections which you can easily find by searching for the tag "@@". Section 1 - Boilerplate: Init & static stuff Section 2 - Init: sane_init, _get_devices, _open ... Section 3 - Options: sane_*_option functions Section 4 - Scanning: sane_start, _get_param, _read ... Section 5 - Cleanup: sane_cancel, ... Section 6 - Misc: sense_handler, hexdump, ... Section 7 - Image processing: deskew, crop, despeck Changes: v1, 2002-05-05, OS - release memory allocated by sane_get_devices - several bugfixes - supports the M3097 - get threshold, contrast and brightness from vpd - imprinter support - get_hardware_status now works before calling sane_start - avoid unnecessary reload of options when using source=fb v2, 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 v3, 2002-09-13, OS - 3092 support (mgoppold a t tbz-pariv.de) - tested 4097 support - changed some functions to receive compressed data v4, 2003-02-13, OS - fi-4220C support (ron a t roncemer.com) - SCSI over USB support (ron a t roncemer.com) v5, 2003-02-20, OS - set availability of options THRESHOLD und VARIANCE - option RIF is available for 3091 and 3092 v6, 2003-03-04, OS - renamed some variables - bugfix: duplex scanning now works when disconnect is enabled v7, 2003-03-10, OS - displays the offending byte in the window descriptor block v8, 2003-03-28, OS - fi-4120C support, MAN - display information about gamma in vital_product_data v9 2003-06-04, MAN - separated the 4120 and 4220 into another model - color support for the 4x20 v10 2003-06-04, MAN - removed SP15 code - sane_open actually opens the device you request v11 2003-06-11, MAN - fixed bug in that code when a scanner is disconnected v12 2003-10-06, MAN - added code to support color modes of more recent scanners v13 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. Disable option gamma was for the fi-4120 v14 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 with x=100 and y=100 dpi with an fi4120 resulted in an image with 100,75 dpi - Bugfix: Set the default value of gamma to 0x80 for all scanners that don't have built in gamma patterns - Bugfix: fi-4530 and fi-4210 don't support standard paper size v15 2003-12-16, OS - Bugfix: pagewidth and pageheight were disabled for the fi-4530C v16 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) v17 2004-03-04, OS - enabled option dropoutcolor for the fi-4530C, and fi-4x20C v18 2004-06-02, OS - bugfix: can read duplex color now v19 2004-06-28, MAN - 4220 use model code not strcmp (stan a t saticed.me.uk) v20 2004-08-24, OS - bugfix: 3091 did not work since 15.12.2003 - M4099 supported (bw only) v21 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 v22 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 v23 2006-05-14, MAN - initial attempt to recover duplex mode - fix bad usb prodID when config file missing v24 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() v25 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 v26 2006-05-23, MAN - don't send scanner control (F1) if unsupported v27 2006-05-30, MAN - speed up hexdump (adeuring A T gmx D O T net) - duplex request same size block from both sides - don't #include or call sanei_thread - split usb/scsi command DBG into 25 and 30 v28 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 v29 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) v30 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 - don't send paper size data if using flatbed v31 2006-06-13, MAN - add 5220C usb id - don't show ink level buttons if no imprinter - run ghs/rs every second instead of every other v32 2006-06-14, MAN - add 4220C2 usb id v33 2006-06-14, MAN (SANE v1.0.18) - add Fi-5900 usb id and init_model section v34 2006-07-04, MAN - add S500 usb id - gather more data from inq and vpd - allow background color setting v35 2006-07-05, MAN - allow double feed sensor settings - more consistent naming of global strings v36 2006-07-06, MAN - deal with fi-5900 even bytes problem - less verbose calculateDerivedValues() v37 2006-07-14, MAN - mode sense command support - detect mode page codes instead of hardcoding - send command support - brightness/contrast support via LUT - merge global mode page buffers v38 2006-07-15, MAN - add 'useless noise' debug level (35) - move mode sense probe errors to DBG 35 v39 2006-07-17, MAN - rewrite contrast slope math for readability v40 2006-08-26, MAN - rewrite brightness/contrast more like xsane - initial gamma support - add fi-5530 usb id - rewrite do_*_cmd functions to handle short reads and to use ptr to return read in length - new init_user function split from init_model - init_vpd allows short vpd block for older models - support MS buffer (s.scipioni AT harvardgroup DOT it) - support MS prepick - read only 1 byte of mode sense output v41 2006-08-28, MAN - do_usb_cmd() returns io error on cmd/out/status/rs EOF - fix bug in MS buffer/prepick scsi data block v42 2006-08-31, MAN - fix bug in get_hardware_status (#303798) v43 2006-09-19, MAN - add model-specific code to init_vpd for M3099 v44 2007-01-26, MAN - set SANE_CAP_HARD_SELECT on all buttons/sensors - disable sending gamma LUT, seems wrong on some units? - support MS overscan - clamp the scan area to the pagesize on ADF v45 2007-01-28, MAN - update overscan code to extend max scan area v46 2007-03-08, MAN - tweak fi-4x20c2 and M3093 settings - add fi-5110EOXM usb id - add M3093 non-alternating duplex code v47 2007-04-13, MAN - change window_gamma determination - add fi-5650C usb id and color mode v48 2007-04-16, MAN - re-enable brightness/contrast for built-in models v49 2007-06-28, MAN - add fi-5750C usb id and color mode v50 2007-07-10, MAN - updated overscan and bgcolor option descriptions - added jpeg output support - restructured usb reading code to use RS len for short reads - combined calcDerivedValues with sane_get_params v51 2007-07-26, MAN - fix bug in jpeg output support v52 2007-07-27, MAN - remove unused jpeg function - reactivate look-up-table based brightness and contrast options - change range of hardware brightness/contrast to match LUT versions - call send_lut() from sane_control_option instead of sane_start v53 2007-11-18, MAN - add S510 usb id - OPT_NUM_OPTS type is SANE_TYPE_INT (jblache) v54 2007-12-29, MAN - disable SANE_FRAME_JPEG support until SANE 1.1.0 v55 2007-12-29, MAN (SANE v1.0.19) - add S500M usb id v56 2008-02-14, MAN - sanei_config_read has already cleaned string (#310597) v57 2008-02-24, MAN - fi-5900 does not (initially) interlace colors - add mode sense for color interlacing? (page code 32) - more debug output in init_ms() v58 2008-04-19, MAN - page code 32 is not color interlacing, rename to 'unknown' - increase number of bytes in response buffer of init_ms() - protect debug modification code in init_ms() if NDEBUG is set - proper async sane_cancel support - re-enable JPEG support - replace s->img_count with s->side - sane_get_parameters(): don't round up larger than current paper size - sane_start() rewritten, shorter, more clear - return values are SANE_Status, not int - hide unused functions v59 2008-04-22, MAN - add fi-6140 usb ID, and fi-6x40 color mode v60 2008-04-27, MAN - move call to sanei_usb_init() from sane_init() to find_scanners - free sane_devArray before calloc'ing a new one v61 2008-05-11, MAN - minor cleanups to init_ms() - add fi-5530C2 usb id - merge find_scanners into sane_get_devices - inspect correct bool to enable prepick mode option v62 2008-05-20, MAN - check for all supported scsi commands - use well-known option group strings from saneopts.h - rename pagewidth to page-width, to meet sane 1.1.0, same for height - add unused get_window() v63 2008-05-21, MAN - use sane 1.1.0 well-known option names for some buttons - remove 'button-' from other buttons and sensors v64 2008-05-28, MAN - strcpy device_name[] instead of strdup/free *device_name - add send/read diag commands to get scanner serial number - use model and serial to build sane.name (idea from Ryan Duryea) - allow both serial_name and device_name to sane_open scanner - correct mode select/sense 6 vs 10 booleans - rename product_name to model_name - simulate missing VPD data for M3097G - hide get_window - improve handling of vendor unique section of set_window - add init_interlace to detect proper color mode without hardcoding - add ascii output to hexdump v65 2008-06-24, MAN - detect endorser type during init_inquiry() - add endorser options - add send_endorser() and call from sane_control_option() - add endorser() and call from sane_start() - convert set_window() to use local cmd and payload copies - remove get_window() - mode_select_buff() now clears the buffer, and called in sane_close() - fi-4990 quirks added, including modified even_scan_line code v66 2008-06-26, MAN - restructure double feed detection options for finer-grained control - add endorser side option - prevent init_interlace() from overriding init_model() - simplify sane_start() and fix interlaced duplex jpeg support - simplify sane_read() and add non-interlaced duplex jpeg support - removed unused code v67 2008-07-01, MAN - add IPC/DTC/SDTC options - call check_for_cancel() in sane_cancel, unless s->reader flag is set v68 2008-07-02, MAN - add halftone type and pattern options - support M3097G with IPC and CMP options via modified VPD response v69 2008-07-03, MAN - support hot-unplugging scanners v70 2008-07-05, MAN - fix bug in sane_get_parameters (failed to copy values) - autodetect jpeg duplex interlacing mode by inspecting scan width v71 2008-07-13, MAN - disable overscan option if vpd does not tell overscan size - fi-5110EOX crops scan area based on absolute maximum, not paper - fi-5530C/2 and fi-5650C can't handle 10 bit LUT via USB - fi-5900 has background color, though it reports otherwise v72 2008-07-13, MAN - use mode_sense to determine background color support - remove fi-5900 background color override v73 2008-07-14, MAN - correct overscan dimension calculation - provide correct overscan size overrides for fi-5110C and fi-4x20C2 - add fi-6130 usb ID - fi-5750C can't handle 10 bit LUT via USB v74 2008-08-02, MAN - replace global scsi blocks with local ones in each function v75 2008-08-07, ReneR - added fi-6230 usb ID v76 2008-08-13, MAN - add independent maximum area values for flatbed - override said values for fi-4220C, fi-4220C2 and fi-5220C v77 2008-08-26, MAN - override flatbed maximum area for fi-6230C and fi-6240C - set PF bit in all mode_select(6) CDB's - set SANE_CAP_INACTIVE on all disabled options - fix bug in mode_select page for sleep timer v78 2008-08-26, MAN - recent model names (fi-6xxx) don't end in 'C' - simplify flatbed area overrides - call scanner_control to change source during sane_start v79 2008-10-01, MAN - add usb ids for several models - print additional hardware capability bits - detect front-side endorser - disable endorser-side controls if only one side installed - add quirks for fi-6x70 v80 2008-10-08, MAN - front-side endorser uses data ID 0x80 v81 2008-10-20, MAN - increase USB timeouts - enable get_pixelsize() to update scan params after set_window() - remove even_scan_line hack v82 2008-10-31, MAN - improved front-side endorser vpd detection - send scanner_control_ric during sane_read of each side - add fi-6770A and fi-6670A USB ID's v83 2008-11-06, MAN - round binary bpl and Bpl up to byte boundary - use s->params instead of user data in set_window() - read_from_scanner() only grabs an even number of lines v84 2008-11-07, MAN - round lines down to even number to get even # of total bytes - round binary bpl and Bpl down to byte boundary v85 2008-12-10, MAN - round pixels_per_line down to arbitrary limits for fi-4990 & fi-4860 - fi-4860 returns random garbage to serial number queries - initialize *info to 0 in sane_control_option() v86 2008-12-18, MAN - get_pixelsize() sets back window ID for back side scans v87 2008-12-21, MAN - accept null pointer as empty device name - track frontend reading sensor/button values to reload - deactivate double feed options if df-action == default v88 2009-01-21, MAN - don't export private symbols v89 2009-02-20, MAN - fi-4750 returns random garbage to serial number queries v90 2009-02-23, MAN - added ScanSnap S510M usb ids v91 2009-03-20, MAN - remove unused temp file code v92 2009-04-12, MAN - disable SANE_FRAME_JPEG support (again) v93 2009-04-14, MAN (SANE 1.0.20) - return cmd status for reads on sensors - ignore errors in scanner_control(), M3091 has not worked since sane 1.0.19, due to this. - copy_buffer needs to count lines, or M309[12] cannot duplex v94 2009-05-22, MAN - add side option to show which duplex image is being transferred - convert front and simplex buffers to use much less ram - add lowmemory option which makes duplex back buffer small too - refactor image handling code to track eof's instead of lengths - do color deinterlacing after reading from scanner, before buffering v95 2009-06-02, MAN - scanner_control_ric should return a subset of the possible errors v96 2009-08-07, MAN - split sane_get_parameters into two functions - remove unused code from get_pixelsize - support hardware based auto length detection v97 2009-09-14, MAN - use sanei_magic to provide software deskew, autocrop and despeckle v98 2010-02-09, MAN (SANE 1.0.21) - clean up #include lines and copyright - add SANE_I18N to static strings - don't fail if scsi buffer is too small - disable bg_color for S1500 - enable flatbed for M3092 v99 2010-05-14, MAN - sense_handler(): collect rs_info for any ILI, not just EOM - do_usb_cmd(): use rs_info whenever set, not just EOF - read_from_*(): better handling of EOF from lower level functions - sane_read(): improve duplexing logic v100 2010-06-01, MAN - store more Request Sense data in scanner struct - clear Request Sense data at start of every do_cmd() call - track per-side ILI and global EOM flags - set per-side EOF flag if ILI and EOM are set v101 2010-06-23, MAN - fix compilation bug when jpeg is enabled v102 2010-09-22, MAN - fix infinite loop when scan is an odd number of lines v103 2010-11-23, MAN - remove compiled-in default config file - initial support for new fi-6xxx machines v104 2010-11-24, MAN - never request more than s->buffer_size from scanner - silence noisy set_window() calls from init_interlace() v105 2010-12-02, MAN - backup and restore image params around image processing code - cache software crop/deskew parameters for use on backside of duplex - fi-6110 does not support bgcolor or prepick v106 2011-01-30, MAN (SANE 1.0.22) - don't call mode_select with a page code the scanner does not support v107 2011-11-03, MAN - M3091 does not support scanner_control(adf) - Correct buffer overflow in read_from_3091duplex() - sane_read() now always calls read_from_*() - read_from_*() are callable when there is no data, and read to eof - sane_read() will keep alternate duplex reads to similar length - Added debugging statements - Corrected comments - Updated Copyright v108 2011-11-21, MAN - merged x/y resolution options - moved page width/height to start of geometry group - use mode to pick resolution list v/s range - improved M3091 resolution choices v109 2011-12-20, MAN - added some MS and INQ information - increased default buffer size for later machines in config file - renamed new fi-6xx0Z models v110 2012-05-09, MAN - correct max_y_fb for fi-62x0 series - add must_fully_buffer helper routine - add hwdeskewcrop option, with fallback to software versions - add 'actual' param to get_pixelsize for post-scan - add recent model VPD params - only set params->lines = -1 when using ald without buffering - fix bugs in background color when using software deskew v111 2012-05-10, MAN (SANE 1.0.23) - call send_* and mode_select_* from sane_start - split read payloads into new debug level - add paper-protect, staple-detect and df-recovery options v112 2013-02-22, MAN - some scanners (fi-6x70 and later) don't enable IPC by default v113 2013-02-24, MAN - support for ScanSnap iX500 - fix bug with jpeg de-interlacing code - allow has_MS_* and has_pixelsize to be set in init_model - fix use of uninitialized buffer in send_lut - add send_q_table() - allow wait_scanner() to be bypassed in object_position - moved send_lut() to after set_window v114 2013-03-01, MAN - support resolutions > 300 for iX500 using diag_preread() - remove most communication with scanner during sane_control_option() v115 2013-03-09, MAN - separate s->mode into s_mode and u_mode - separate s->params into s_params and u_params - generate grayscale and binary in software if required (iX500) v116 2013-03-23, MAN - call set_mode() in init_interlace - add swskip option v117 2013-06-11, MAN (SANE 1.0.24) - default buffer-mode to off - improved error handling in sane_start - image width must be multiple of 8 when swcrop is used before binarization (iX500) - check hopper sensor before calling object_position(load) on iX500 v118 2013-12-09, MAN - support fi-7160, fi-7260, fi-7180 and fi-7280 - remove unused var from do_scsi_cmd() - added more request_sense options - add adv_paper_protect option - enable paper protection by default - increase max_x_fb for fi-6240 and fi-6230 v119 2013-12-18, MAN - call get_pixelsize after start_scan, not before - extend get_pixelsize to request backside data - stop using backup/restore_params - don't use extended get_pixelsize on M3091 or M3092 - call software crop code on backside images too v120 2014-01-29, MAN - only call hopper_before_op code at batch start - remove unused backup/restore_params v121 2014-04-07, MAN - add JFIF APP0 marker with resolution to jpeg images - improve jpeg duplex parsing code - simplify jpeg ifdefs - add offtimer option for more recent scanners - don't print 0 length line in hexdump v122 2014-10-28, MAN - add support for object_position halt - call object_position halt in check_for_cancel when requested v123 2014-11-06, MAN - workaround Linux USB3 bugs by adding command counting code and sending an even number of reads and writes during disconnect_fd v124 2014-12-09, MAN - support resolution controlled max page-height (fi-6/7xxx scanners) - reorder scanner sections in init_model chronologically v125 2014-12-16, MAN - remove USB packet counting code from v123, fix sanei_usb instead v126 2015-08-23, MAN - initial support for iX100 - add late_lut support for iX500/iX100 v127 2015-08-25, MAN (SANE 1.0.25) - separate iX100 from iX500 settings - iX100 has gray and lineart v128 2015-11-08, MAN - do not ask fi-4340 for serial number v129 2015-11-21, MAN - br_x and br_y locked to page_width/height until changed v130 2016-02-23, MAN - run init_model before init_ms so some scanners can override - set all M309x and M409x scanners s->broken_diag_serial = 1 v131 2016-06-06, MAN - hide compression-arg option when jpeg disabled - add Send/SC/GHS macros for recent scanners - add initial support for fi-74x0 - add initial support for fi-7030 - set has_MS_lamp=0 for fi-71x0 - add I18N macros to all option titles and descriptions v132 2016-10-07, MAN - remove ipc_mode option and variables - set ipc mode based on other options - cleanup inverted logic DTC options - fixes threshold option reported in #315069 v133 2017-04-08, MAN - initial support for fi-7600/7700 - autodetect various double feed capabilities using VPD - call send_lut if we are using a downloaded gamma table v134 2019-02-23, MAN - rewrite init_vpd for scanners which fail to report overscan correctly v135 2019-11-10, MAN (SANE 1.0.29) - set has_MS_lamp=0 for fi-72x0, bug #134 v136 2020-02-07, MAN - add support for fi-800R - add support for card scanning slot (Return Path) - fix bug with reading hardware sensors on first invocation v137 2020-09-23, MAN - fix JPEG duplex memory corruption - change window_gamma init (fixes bright/contrast for iX1500) - only call send_lut after set_window (remove late_lut) v138 2022-06-01, MAN - minor updates to company name (FCPA -> PFU) v139 2022-11-15, MAN - move updated window_gamma logic to set_window - use internal gamma table if possible (fixes #618) v140 2023-03-27, MAN - add initial support for Ricoh scanners SANE FLOW DIAGRAM - 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 . . - sane_read() : read image data (from pipe) . . (sane_read called multiple times; after sane_read returns EOF, . . loop may continue with sane_start which may return a 2nd page . . when doing duplex scans, or load the next page from the ADF) . . . . - sane_cancel() : cancel operation . - sane_close() : close opened scanner device - sane_exit() : terminate use of backend */ /* * @@ Section 1 - Boilerplate */ #include "../include/sane/config.h" #include /*memcpy...*/ #include /*isspace*/ #include /*tan*/ #include /*usleep*/ #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_scsi.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_magic.h" #include "fujitsu-scsi.h" #include "fujitsu.h" #define DEBUG 1 #define BUILD 140 /* 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 writes 30 - scsi/usb reads 31 - useless noise 35 */ /* ------------------------------------------------------------------------- */ /* if JPEG support is not enabled in sane.h, we setup our own defines */ #ifndef SANE_FRAME_JPEG #define SANE_FRAME_JPEG 0x0B #define SANE_JPEG_DISABLED 1 #endif /* ------------------------------------------------------------------------- */ #define STRING_FLATBED SANE_I18N("Flatbed") #define STRING_ADFFRONT SANE_I18N("ADF Front") #define STRING_ADFBACK SANE_I18N("ADF Back") #define STRING_ADFDUPLEX SANE_I18N("ADF Duplex") #define STRING_CARDFRONT SANE_I18N("Card Front") #define STRING_CARDBACK SANE_I18N("Card Back") #define STRING_CARDDUPLEX SANE_I18N("Card Duplex") #define STRING_LINEART SANE_VALUE_SCAN_MODE_LINEART #define STRING_HALFTONE SANE_VALUE_SCAN_MODE_HALFTONE #define STRING_GRAYSCALE SANE_VALUE_SCAN_MODE_GRAY #define STRING_COLOR SANE_VALUE_SCAN_MODE_COLOR #define STRING_DEFAULT SANE_I18N("Default") #define STRING_ON SANE_I18N("On") #define STRING_OFF SANE_I18N("Off") #define STRING_DTC SANE_I18N("DTC") #define STRING_SDTC SANE_I18N("SDTC") #define STRING_DITHER SANE_I18N("Dither") #define STRING_DIFFUSION SANE_I18N("Diffusion") #define STRING_RED SANE_I18N("Red") #define STRING_GREEN SANE_I18N("Green") #define STRING_BLUE SANE_I18N("Blue") #define STRING_WHITE SANE_I18N("White") #define STRING_BLACK SANE_I18N("Black") #define STRING_NONE SANE_I18N("None") #define STRING_JPEG SANE_I18N("JPEG") #define STRING_CONTINUE SANE_I18N("Continue") #define STRING_STOP SANE_I18N("Stop") #define STRING_10MM SANE_I18N("10mm") #define STRING_15MM SANE_I18N("15mm") #define STRING_20MM SANE_I18N("20mm") #define STRING_HORIZONTAL SANE_I18N("Horizontal") #define STRING_HORIZONTALBOLD SANE_I18N("Horizontal bold") #define STRING_HORIZONTALNARROW SANE_I18N("Horizontal narrow") #define STRING_VERTICAL SANE_I18N("Vertical") #define STRING_VERTICALBOLD SANE_I18N("Vertical bold") #define STRING_TOPTOBOTTOM SANE_I18N("Top to bottom") #define STRING_BOTTOMTOTOP SANE_I18N("Bottom to top") #define STRING_FRONT SANE_I18N("Front") #define STRING_BACK SANE_I18N("Back") #define max(a,b) (((a)>(b))?(a):(b)) /* Also set via config file. */ static int global_buffer_size = 64 * 1024; /* * 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 **sane_devArray = NULL; static struct fujitsu *fujitsu_devList = NULL; /* * @@ Section 2 - SANE & scanner init code */ /* * 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) { (void) authorize; /* get rid of compiler warning */ DBG_INIT (); DBG (10, "sane_init: start\n"); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); DBG (5, "sane_init: fujitsu backend %d.%d.%d, from %s\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD, PACKAGE_STRING); sanei_magic_init(); DBG (10, "sane_init: finish\n"); 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. */ /* * Read the config file, find scanners with help from sanei_* * and store in global device structs */ SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { SANE_Status ret = SANE_STATUS_GOOD; struct fujitsu * s; struct fujitsu * prev = NULL; char line[PATH_MAX]; const char *lp; FILE *fp; int num_devices=0; int i=0; (void) local_only; /* get rid of compiler warning */ DBG (10, "sane_get_devices: start\n"); /* mark all existing scanners as missing, attach_one will remove mark */ for (s = fujitsu_devList; s; s = s->next) { s->missing = 1; } sanei_usb_init(); /* set this to 64K before reading the file */ global_buffer_size = 64 * 1024; fp = sanei_config_open (FUJITSU_CONFIG_FILE); if (fp) { DBG (15, "sane_get_devices: reading config file %s\n", FUJITSU_CONFIG_FILE); while (sanei_config_read (line, PATH_MAX, fp)) { lp = line; /* ignore comments */ if (*lp == '#') continue; /* 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, "sane_get_devices: config option \"buffer-size\" (%d) is < 4096, ignoring!\n", buf); continue; } if (buf > 64*1024) { DBG (5, "sane_get_devices: config option \"buffer-size\" (%d) is > %d, warning!\n", buf, 64*1024); } DBG (15, "sane_get_devices: setting \"buffer-size\" to %d\n", buf); global_buffer_size = buf; } else { DBG (5, "sane_get_devices: config option \"%s\" unrecognized - ignored.\n", lp); } } else if ((strncmp ("usb", lp, 3) == 0) && isspace (lp[3])) { DBG (15, "sane_get_devices: 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, "sane_get_devices: looking for '%s'\n", lp); sanei_config_attach_matching_devices (lp, attach_one_scsi); } else{ DBG (5, "sane_get_devices: config line \"%s\" unrecognized - ignored.\n", lp); } } fclose (fp); } else { DBG (5, "sane_get_devices: missing required config file '%s'!\n", FUJITSU_CONFIG_FILE); } /*delete missing scanners from list*/ for (s = fujitsu_devList; s;) { if(s->missing){ DBG (5, "sane_get_devices: missing scanner %s\n",s->device_name); /*splice s out of list by changing pointer in prev to next*/ if(prev){ prev->next = s->next; free(s); s=prev->next; } /*remove s from head of list, using prev to cache it*/ else{ prev = s; s = s->next; free(prev); prev=NULL; /*reset head to next s*/ fujitsu_devList = s; } } else{ prev = s; s=prev->next; } } for (s = fujitsu_devList; s; s=s->next) { DBG (15, "sane_get_devices: found scanner %s\n",s->device_name); num_devices++; } DBG (15, "sane_get_devices: found %d scanner(s)\n",num_devices); if (sane_devArray) free (sane_devArray); sane_devArray = calloc (num_devices + 1, sizeof (SANE_Device*)); if (!sane_devArray) return SANE_STATUS_NO_MEM; for (s = fujitsu_devList; s; s=s->next) { sane_devArray[i++] = (SANE_Device *)&s->sane; } sane_devArray[i] = 0; if(device_list){ *device_list = sane_devArray; } DBG (10, "sane_get_devices: finish\n"); return ret; } /* callbacks used by sane_get_devices */ static SANE_Status attach_one_scsi (const char *device_name) { return attach_one(device_name,CONNECTION_SCSI); } static SANE_Status attach_one_usb (const char *device_name) { return attach_one(device_name,CONNECTION_USB); } /* build the scanner struct and link to global list * unless struct is already loaded, then pretend */ static SANE_Status attach_one (const char *device_name, int connType) { struct fujitsu *s; int ret; DBG (10, "attach_one: start\n"); DBG (15, "attach_one: looking for '%s'\n", device_name); for (s = fujitsu_devList; s; s = s->next) { if (strcmp (s->device_name, device_name) == 0){ DBG (10, "attach_one: already attached!\n"); s->missing = 0; return SANE_STATUS_GOOD; } } /* build a fujitsu struct to hold it */ if ((s = calloc (sizeof (*s), 1)) == NULL) return SANE_STATUS_NO_MEM; /* scsi command/data buffer */ s->buffer_size = global_buffer_size; /* copy the device name */ strcpy (s->device_name, device_name); /* connect the fd */ s->connection = connType; s->fd = -1; ret = connect_fd(s); if(ret != SANE_STATUS_GOOD){ free (s); return ret; } /* Now query the device to load its vendor/model/version */ ret = init_inquire (s); if (ret != SANE_STATUS_GOOD) { disconnect_fd(s); free (s); DBG (5, "attach_one: inquiry failed\n"); return ret; } /* load detailed specs/capabilities from the device */ ret = init_vpd (s); if (ret != SANE_STATUS_GOOD) { disconnect_fd(s); free (s); DBG (5, "attach_one: vpd failed\n"); return ret; } /* clean up the scanner struct based on model */ /* this is the only piece of model specific code */ ret = init_model (s); if (ret != SANE_STATUS_GOOD) { disconnect_fd(s); free (s); DBG (5, "attach_one: model failed\n"); return ret; } /* see what mode pages device supports */ ret = init_ms (s); if (ret != SANE_STATUS_GOOD) { disconnect_fd(s); free (s); DBG (5, "attach_one: ms failed\n"); return ret; } /* sets SANE option 'values' to good defaults */ ret = init_user (s); if (ret != SANE_STATUS_GOOD) { disconnect_fd(s); free (s); DBG (5, "attach_one: user failed\n"); return ret; } ret = init_options (s); if (ret != SANE_STATUS_GOOD) { disconnect_fd(s); free (s); DBG (5, "attach_one: options failed\n"); return ret; } ret = init_interlace (s); if (ret != SANE_STATUS_GOOD) { disconnect_fd(s); free (s); DBG (5, "attach_one: interlace failed\n"); return ret; } /* load strings into sane_device struct */ s->sane.name = s->device_name; s->sane.vendor = s->vendor_name; s->sane.model = s->model_name; s->sane.type = "scanner"; /* change name in sane_device struct if scanner has serial number */ ret = init_serial (s); if (ret == SANE_STATUS_GOOD) { s->sane.name = s->serial_name; } else{ DBG (5, "attach_one: serial number unsupported?\n"); } /* we close the connection, so that another backend can talk to scanner */ disconnect_fd(s); /* store this scanner in global vars */ s->next = fujitsu_devList; fujitsu_devList = s; DBG (10, "attach_one: finish\n"); 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; DBG (10, "connect_fd: start\n"); 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 && buffer_size != s->buffer_size){ DBG (5, "connect_fd: cannot get requested buffer size (%d/%d)\n", buffer_size, s->buffer_size); } } if(ret == 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); } } else{ DBG (5, "connect_fd: could not open device: %d\n", ret); } DBG (10, "connect_fd: finish\n"); return ret; } /* * This routine will check if a certain device is a Fujitsu/Ricoh 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 cmd[INQUIRY_len]; size_t cmdLen = INQUIRY_len; unsigned char in[INQUIRY_std_len]; size_t inLen = INQUIRY_std_len; DBG (10, "init_inquire: start\n"); memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, INQUIRY_code); set_IN_return_size (cmd, inLen); set_IN_evpd (cmd, 0); set_IN_page_code (cmd, 0); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if (ret != SANE_STATUS_GOOD){ return ret; } if (get_IN_periph_devtype (in) != IN_periph_devtype_scanner){ DBG (5, "The device at '%s' is not a scanner.\n", s->device_name); return SANE_STATUS_INVAL; } get_IN_vendor (in, s->vendor_name); get_IN_product (in, s->model_name); get_IN_version (in, s->version_name); s->vendor_name[8] = 0; s->model_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->model_name[i] == ' ' && i >= 0; i--) s->model_name[i] = 0; for (i = 3; s->version_name[i] == ' ' && i >= 0; i--) s->version_name[i] = 0; if (strcmp ("FUJITSU", s->vendor_name) && strcmp ("RICOH", 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 and Ricoh products.\n"); return SANE_STATUS_INVAL; } DBG (15, "init_inquire: Found %s scanner %s version %s at %s\n", s->vendor_name, s->model_name, s->version_name, s->device_name); /*some scanners list random data here*/ DBG (15, "inquiry options\n"); s->color_raster_offset = get_IN_color_offset(in); DBG (15, " color offset: %d lines\n",s->color_raster_offset); /* FIXME: we don't store all of these? */ DBG (15, " long gray scan: %d\n",get_IN_long_gray(in)); DBG (15, " long color scan: %d\n",get_IN_long_color(in)); DBG (15, " emulation mode: %d\n",get_IN_emulation(in)); DBG (15, " CMP/CGA: %d\n",get_IN_cmp_cga(in)); DBG (15, " background back: %d\n",get_IN_bg_back(in)); DBG (15, " background front: %d\n",get_IN_bg_front(in)); DBG (15, " background fb: %d\n",get_IN_bg_fb(in)); DBG (15, " back only scan: %d\n",get_IN_has_back(in)); s->duplex_raster_offset = get_IN_duplex_offset(in); DBG (15, " duplex offset: %d lines\n",s->duplex_raster_offset); DBG (10, "init_inquire: finish\n"); 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 cmd[INQUIRY_len]; size_t cmdLen = INQUIRY_len; unsigned char in[INQUIRY_vpd_len]; size_t inLen = INQUIRY_vpd_len; int payload_len, payload_off; DBG (10, "init_vpd: start\n"); /* get EVPD */ memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, INQUIRY_code); set_IN_return_size (cmd, inLen); set_IN_evpd (cmd, 1); set_IN_page_code (cmd, 0xf0); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); /*FIXME no vpd, set some defaults? */ if (ret != SANE_STATUS_GOOD && ret != SANE_STATUS_EOF) { DBG (5, "init_vpd: Your scanner does not support VPD?\n"); DBG (5, "init_vpd: Please contact kitno455 at gmail dot com\n"); DBG (5, "init_vpd: with details of your scanner model.\n"); return ret; } /* In byte 4, the scanner sends the length of the remainder of * the payload. But, this value is often bogus. */ payload_len = get_IN_page_length(in); DBG (15, "init_vpd: length=%0x\n", payload_len); /* M3099 gives all data, but wrong length */ if (strstr (s->model_name, "M3099") && payload_len == 0x19){ DBG (5, "init_vpd: M3099 repair\n"); payload_len = 0x5f; } /* M3097G has short vpd, fill in missing part */ else if (strstr (s->model_name, "M3097G") && payload_len == 0x19){ unsigned char vpd3097g[] = { 0, 0, 0xc2, 0x08, 0, 0, 0, 0, 0, 0, 0xed, 0xbf, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0, 0x45, 0x35, 0, 0xe0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; DBG (5, "init_vpd: M3097G repair\n"); payload_len = 0x5f; memcpy(in+0x1e,vpd3097g,sizeof(vpd3097g)); /*IPC*/ if(strstr (s->model_name, "i")){ DBG (5, "init_vpd: M3097G IPC repair\n"); /*subwin cmd*/ in[0x2b] = 1; /*rif/dtc/sdtc/outline/emph/sep/mirr/wlf*/ in[0x58] = 0xff; /*subwin/diffusion*/ in[0x59] = 0xc0; } /*CMP*/ if(strstr (s->model_name, "m")){ DBG (5, "init_vpd: M3097G CMP repair\n"); /*4megs*/ in[0x23] = 0x40; /*mh/mr/mmr*/ in[0x5a] = 0xe0; } } /* all other known scanners have at least 0x5f, * less would require software changes like above */ else if (payload_len < 0x5f) { DBG (5, "init_vpd: Your scanner supports only partial VPD?\n"); DBG (5, "init_vpd: Please contact kitno455 at gmail dot com\n"); DBG (5, "init_vpd: with details of your scanner model.\n"); return SANE_STATUS_INVAL; } /* Special case- some scanners will under-report the amount of * valid vpd that they send, and return the default length. * Adding 4 more bytes allows us to include the overscan info. * Scanners that don't support overscan seem to have all zeros * in these bytes, so no harm is done. * This may be an 'off-by-four' error in the firmware. */ else if (payload_len == 0x5f){ payload_len += 4; } /* Having an offset from the beginning of the payload * is more useful than from byte 4, as that matches the * documentation more closely. */ payload_off = payload_len + 4; /* everything that appears in bytes 0 to 0x1d */ DBG (15, "standard options\n"); s->basic_x_res = get_IN_basic_x_res (in); DBG (15, " basic x res: %d dpi\n",s->basic_x_res); s->basic_y_res = get_IN_basic_y_res (in); DBG (15, " basic y res: %d dpi\n",s->basic_y_res); s->step_x_res[MODE_LINEART] = get_IN_step_x_res (in); DBG (15, " step x res: %d dpi\n", s->step_x_res[MODE_LINEART]); s->step_y_res[MODE_LINEART] = get_IN_step_y_res (in); DBG (15, " step y res: %d dpi\n", s->step_y_res[MODE_LINEART]); s->max_x_res = get_IN_max_x_res (in); DBG (15, " max x res: %d dpi\n", s->max_x_res); s->max_y_res = get_IN_max_y_res (in); DBG (15, " max y res: %d dpi\n", s->max_y_res); s->min_x_res = get_IN_min_x_res (in); DBG (15, " min x res: %d dpi\n", s->min_x_res); s->min_y_res = get_IN_min_y_res (in); DBG (15, " min y res: %d dpi\n", s->min_y_res); /* some scanners list B&W resolutions. */ s->std_res[0] = get_IN_std_res_60 (in); DBG (15, " 60 dpi: %d\n", s->std_res[0]); s->std_res[1] = get_IN_std_res_75 (in); DBG (15, " 75 dpi: %d\n", s->std_res[1]); s->std_res[2] = get_IN_std_res_100 (in); DBG (15, " 100 dpi: %d\n", s->std_res[2]); s->std_res[3] = get_IN_std_res_120 (in); DBG (15, " 120 dpi: %d\n", s->std_res[3]); s->std_res[4] = get_IN_std_res_150 (in); DBG (15, " 150 dpi: %d\n", s->std_res[4]); s->std_res[5] = get_IN_std_res_160 (in); DBG (15, " 160 dpi: %d\n", s->std_res[5]); s->std_res[6] = get_IN_std_res_180 (in); DBG (15, " 180 dpi: %d\n", s->std_res[6]); s->std_res[7] = get_IN_std_res_200 (in); DBG (15, " 200 dpi: %d\n", s->std_res[7]); s->std_res[8] = get_IN_std_res_240 (in); DBG (15, " 240 dpi: %d\n", s->std_res[8]); s->std_res[9] = get_IN_std_res_300 (in); DBG (15, " 300 dpi: %d\n", s->std_res[9]); s->std_res[10] = get_IN_std_res_320 (in); DBG (15, " 320 dpi: %d\n", s->std_res[10]); s->std_res[11] = get_IN_std_res_400 (in); DBG (15, " 400 dpi: %d\n", s->std_res[11]); s->std_res[12] = get_IN_std_res_480 (in); DBG (15, " 480 dpi: %d\n", s->std_res[12]); s->std_res[13] = get_IN_std_res_600 (in); DBG (15, " 600 dpi: %d\n", s->std_res[13]); s->std_res[14] = get_IN_std_res_800 (in); DBG (15, " 800 dpi: %d\n", s->std_res[14]); s->std_res[15] = get_IN_std_res_1200 (in); DBG (15, " 1200 dpi: %d\n", s->std_res[15]); /* maximum window width and length are reported in basic units.*/ s->max_x_basic = get_IN_window_width(in); DBG(15, " max width: %2.2f inches\n",(float)s->max_x_basic/s->basic_x_res); s->max_y_basic = get_IN_window_length(in); DBG(15, " max length: %2.2f inches\n",(float)s->max_y_basic/s->basic_y_res); /* known modes */ s->can_overflow = get_IN_overflow(in); DBG (15, " overflow: %d\n", s->can_overflow); s->can_mode[MODE_LINEART] = get_IN_monochrome (in); DBG (15, " monochrome: %d\n", s->can_mode[MODE_LINEART]); s->can_mode[MODE_HALFTONE] = get_IN_half_tone (in); DBG (15, " halftone: %d\n", s->can_mode[MODE_HALFTONE]); s->can_mode[MODE_GRAYSCALE] = get_IN_multilevel (in); DBG (15, " grayscale: %d\n", s->can_mode[MODE_GRAYSCALE]); DBG (15, " color_monochrome: %d\n", get_IN_monochrome_rgb(in)); DBG (15, " color_halftone: %d\n", get_IN_half_tone_rgb(in)); s->can_mode[MODE_COLOR] = get_IN_multilevel_rgb (in); DBG (15, " color_grayscale: %d\n", s->can_mode[MODE_COLOR]); /* now we look at vendor specific data in bytes 0x1e onward */ DBG (15, "vendor options\n"); s->has_adf = get_IN_adf(in); DBG (15, " adf: %d\n", s->has_adf); s->has_flatbed = get_IN_flatbed(in); DBG (15, " flatbed: %d\n", s->has_flatbed); s->has_transparency = get_IN_transparency(in); DBG (15, " transparency: %d\n", s->has_transparency); s->has_duplex = get_IN_duplex(in); s->has_back = s->has_duplex; DBG (15, " duplex: %d\n", s->has_duplex); s->has_endorser_b = get_IN_endorser_b(in); DBG (15, " back endorser: %d\n", s->has_endorser_b); s->has_barcode = get_IN_barcode(in); DBG (15, " barcode: %d\n", s->has_barcode); s->has_operator_panel = get_IN_operator_panel(in); DBG (15, " operator panel: %d\n", s->has_operator_panel); s->has_endorser_f = get_IN_endorser_f(in); DBG (15, " front endorser: %d\n", s->has_endorser_f); DBG (15, " multi-purpose stacker: %d\n", get_IN_mp_stacker(in)); DBG (15, " prepick: %d\n", get_IN_prepick(in)); DBG (15, " mf detect: %d\n", get_IN_mf_detect(in)); s->has_paper_protect = get_IN_paperprot(in); DBG (15, " paper protection: %d\n", s->has_paper_protect); s->adbits = get_IN_adbits(in); DBG (15, " A/D bits: %d\n",s->adbits); s->buffer_bytes = get_IN_buffer_bytes(in); DBG (15, " buffer bytes: %d\n",s->buffer_bytes); DBG (15, "Standard commands\n"); /* std scsi command support byte 26*/ s->has_cmd_msen10 = get_IN_has_cmd_msen10(in); DBG (15, " mode_sense_10 cmd: %d\n", s->has_cmd_msen10); s->has_cmd_msel10 = get_IN_has_cmd_msel10(in); DBG (15, " mode_select_10 cmd: %d\n", s->has_cmd_msel10); /* std scsi command support byte 27*/ s->has_cmd_lsen = get_IN_has_cmd_lsen(in); DBG (15, " log_sense cmd: %d\n", s->has_cmd_lsen); s->has_cmd_lsel = get_IN_has_cmd_lsel(in); DBG (15, " log_select cmd: %d\n", s->has_cmd_lsel); s->has_cmd_change = get_IN_has_cmd_change(in); DBG (15, " change cmd: %d\n", s->has_cmd_change); s->has_cmd_rbuff = get_IN_has_cmd_rbuff(in); DBG (15, " read_buffer cmd: %d\n", s->has_cmd_rbuff); s->has_cmd_wbuff = get_IN_has_cmd_wbuff(in); DBG (15, " write_buffer cmd: %d\n", s->has_cmd_wbuff); s->has_cmd_cav = get_IN_has_cmd_cav(in); DBG (15, " copy_and_verify cmd: %d\n", s->has_cmd_cav); s->has_cmd_comp = get_IN_has_cmd_comp(in); DBG (15, " compare cmd: %d\n", s->has_cmd_comp); s->has_cmd_gdbs = get_IN_has_cmd_gdbs(in); DBG (15, " get_d_b_status cmd: %d\n", s->has_cmd_gdbs); /* std scsi command support byte 28*/ s->has_cmd_op = get_IN_has_cmd_op(in); DBG (15, " object_pos cmd: %d\n", s->has_cmd_op); s->has_cmd_send = get_IN_has_cmd_send(in); DBG (15, " send cmd: %d\n", s->has_cmd_send); s->has_cmd_read = get_IN_has_cmd_read(in); DBG (15, " read cmd: %d\n", s->has_cmd_read); s->has_cmd_gwin = get_IN_has_cmd_gwin(in); DBG (15, " get_window cmd: %d\n", s->has_cmd_gwin); s->has_cmd_swin = get_IN_has_cmd_swin(in); DBG (15, " set_window cmd: %d\n", s->has_cmd_swin); s->has_cmd_sdiag = get_IN_has_cmd_sdiag(in); DBG (15, " send_diag cmd: %d\n", s->has_cmd_sdiag); s->has_cmd_rdiag = get_IN_has_cmd_rdiag(in); DBG (15, " read_diag cmd: %d\n", s->has_cmd_rdiag); s->has_cmd_scan = get_IN_has_cmd_scan(in); DBG (15, " scan cmd: %d\n", s->has_cmd_scan); /* std scsi command support byte 29*/ s->has_cmd_msen6 = get_IN_has_cmd_msen6(in); DBG (15, " mode_sense_6 cmd: %d\n", s->has_cmd_msen6); s->has_cmd_copy = get_IN_has_cmd_copy(in); DBG (15, " copy cmd: %d\n", s->has_cmd_copy); s->has_cmd_rel = get_IN_has_cmd_rel(in); DBG (15, " release cmd: %d\n", s->has_cmd_rel); s->has_cmd_runit = get_IN_has_cmd_runit(in); DBG (15, " reserve_unit cmd: %d\n", s->has_cmd_runit); s->has_cmd_msel6 = get_IN_has_cmd_msel6(in); DBG (15, " mode_select_6 cmd: %d\n", s->has_cmd_msel6); s->has_cmd_inq = get_IN_has_cmd_inq(in); DBG (15, " inquiry cmd: %d\n", s->has_cmd_inq); s->has_cmd_rs = get_IN_has_cmd_rs(in); DBG (15, " request_sense cmd: %d\n", s->has_cmd_rs); s->has_cmd_tur = get_IN_has_cmd_tur(in); DBG (15, " test_unit_ready cmd: %d\n", s->has_cmd_tur); /* vendor added scsi command support */ /* FIXME: there are more of these... */ DBG (15, "Vendor commands\n"); s->has_cmd_subwindow = get_IN_has_cmd_subwindow(in); DBG (15, " subwindow cmd: %d\n", s->has_cmd_subwindow); s->has_cmd_endorser = get_IN_has_cmd_endorser(in); DBG (15, " endorser cmd: %d\n", s->has_cmd_endorser); s->has_cmd_hw_status = get_IN_has_cmd_hw_status (in); DBG (15, " hardware status cmd: %d\n", s->has_cmd_hw_status); s->has_cmd_hw_status_2 = get_IN_has_cmd_hw_status_2 (in); DBG (15, " hardware status 2 cmd: %d\n", s->has_cmd_hw_status_2); s->has_cmd_hw_status_3 = get_IN_has_cmd_hw_status_3 (in); DBG (15, " hardware status 3 cmd: %d\n", s->has_cmd_hw_status_3); s->has_cmd_scanner_ctl = get_IN_has_cmd_scanner_ctl(in); DBG (15, " scanner control cmd: %d\n", s->has_cmd_scanner_ctl); s->has_cmd_device_restart = get_IN_has_cmd_device_restart(in); DBG (15, " device restart cmd: %d\n", s->has_cmd_device_restart); /* get threshold, brightness and contrast ranges. */ s->brightness_steps = get_IN_brightness_steps(in); DBG (15, " brightness steps: %d\n", s->brightness_steps); s->threshold_steps = get_IN_threshold_steps(in); DBG (15, " threshold steps: %d\n", s->threshold_steps); s->contrast_steps = get_IN_contrast_steps(in); DBG (15, " contrast steps: %d\n", s->contrast_steps); /* dither/gamma patterns */ s->num_internal_gamma = get_IN_num_gamma_internal (in); DBG (15, " built in gamma patterns: %d\n", s->num_internal_gamma); s->num_download_gamma = get_IN_num_gamma_download (in); DBG (15, " download gamma patterns: %d\n", s->num_download_gamma); s->num_internal_dither = get_IN_num_dither_internal (in); DBG (15, " built in dither patterns: %d\n", s->num_internal_dither); s->num_download_dither = get_IN_num_dither_download (in); DBG (15, " download dither patterns: %d\n", s->num_download_dither); /* ipc functions */ s->has_rif = get_IN_ipc_bw_rif (in); DBG (15, " RIF: %d\n", s->has_rif); s->has_dtc = get_IN_ipc_dtc(in); DBG (15, " DTC (AutoI): %d\n", s->has_dtc); s->has_sdtc = get_IN_ipc_sdtc(in); DBG (15, " SDTC (AutoII): %d\n", s->has_sdtc); s->has_outline = get_IN_ipc_outline_extraction (in); DBG (15, " outline extraction: %d\n", s->has_outline); s->has_emphasis = get_IN_ipc_image_emphasis (in); DBG (15, " image emphasis: %d\n", s->has_emphasis); s->has_autosep = get_IN_ipc_auto_separation (in); DBG (15, " automatic separation: %d\n", s->has_autosep); s->has_mirroring = get_IN_ipc_mirroring (in); DBG (15, " mirror image: %d\n", s->has_mirroring); s->has_wl_follow = get_IN_ipc_wl_follow (in); DBG (15, " white level follower: %d\n", s->has_wl_follow); /* byte 58 */ s->has_subwindow = get_IN_ipc_subwindow (in); DBG (15, " subwindow: %d\n", s->has_subwindow); s->has_diffusion = get_IN_ipc_diffusion (in); DBG (15, " diffusion: %d\n", s->has_diffusion); s->has_ipc3 = get_IN_ipc_ipc3 (in); DBG (15, " ipc3: %d\n", s->has_ipc3); s->has_rotation = get_IN_ipc_rotation (in); DBG (15, " rotation: %d\n", s->has_rotation); s->has_hybrid_crop_deskew = get_IN_ipc_hybrid_crop_deskew(in); DBG (15, " hybrid crop deskew: %d\n", s->has_hybrid_crop_deskew); /* this one is weird, overrides the payload length from scanner */ DBG (15, " vpd extends to byte 6f: %d\n", get_IN_vpd_thru_byte_6f(in)); if(get_IN_vpd_thru_byte_6f(in) && payload_off < 0x6f){ payload_off = 0x6f; } /* compression modes */ s->has_comp_MH = get_IN_compression_MH (in); DBG (15, " compression MH: %d\n", s->has_comp_MH); s->has_comp_MR = get_IN_compression_MR (in); DBG (15, " compression MR: %d\n", s->has_comp_MR); s->has_comp_MMR = get_IN_compression_MMR (in); DBG (15, " compression MMR: %d\n", s->has_comp_MMR); s->has_comp_JBIG = get_IN_compression_JBIG (in); DBG (15, " compression JBIG: %d\n", s->has_comp_JBIG); s->has_comp_JPG1 = get_IN_compression_JPG_BASE (in); DBG (15, " compression JPG1: %d\n", s->has_comp_JPG1); #ifdef SANE_JPEG_DISABLED DBG (15, " (Disabled)\n"); #endif s->has_comp_JPG2 = get_IN_compression_JPG_EXT (in); DBG (15, " compression JPG2: %d\n", s->has_comp_JPG2); s->has_comp_JPG3 = get_IN_compression_JPG_INDEP (in); DBG (15, " compression JPG3: %d\n", s->has_comp_JPG3); /* FIXME: we don't store these? */ DBG (15, " back endorser mech: %d\n", get_IN_endorser_b_mech(in)); DBG (15, " back endorser stamp: %d\n", get_IN_endorser_b_stamp(in)); DBG (15, " back endorser elec: %d\n", get_IN_endorser_b_elec(in)); DBG (15, " endorser max id: %d\n", get_IN_endorser_max_id(in)); DBG (15, " front endorser mech: %d\n", get_IN_endorser_f_mech(in)); DBG (15, " front endorser stamp: %d\n", get_IN_endorser_f_stamp(in)); DBG (15, " front endorser elec: %d\n", get_IN_endorser_f_elec(in)); s->endorser_type_b = get_IN_endorser_b_type(in); DBG (15, " back endorser type: %d\n", s->endorser_type_b); s->endorser_type_f = get_IN_endorser_f_type(in); DBG (15, " back endorser type: %d\n", s->endorser_type_f); DBG (15, " connection type: %d\n", get_IN_connection(in)); DBG (15, " endorser ext: %d\n", get_IN_endorser_type_ext(in)); DBG (15, " endorser pr_b: %d\n", get_IN_endorser_pre_back(in)); DBG (15, " endorser pr_f: %d\n", get_IN_endorser_pre_front(in)); DBG (15, " endorser po_b: %d\n", get_IN_endorser_post_back(in)); DBG (15, " endorser po_f: %d\n", get_IN_endorser_post_front(in)); s->os_x_basic = get_IN_x_overscan_size(in); DBG (15, " horizontal overscan: %d\n", s->os_x_basic); s->os_y_basic = get_IN_y_overscan_size(in); DBG (15, " vertical overscan: %d\n", s->os_y_basic); /* not all scanners go this far */ if (payload_off >= 0x68) { DBG (15, " default bg adf b: %d\n", get_IN_default_bg_adf_b(in)); DBG (15, " default bg adf f: %d\n", get_IN_default_bg_adf_f(in)); DBG (15, " default bg fb: %d\n", get_IN_default_bg_fb(in)); } if (payload_off >= 0x69) { DBG (15, " auto color: %d\n", get_IN_auto_color(in)); DBG (15, " blank skip: %d\n", get_IN_blank_skip(in)); DBG (15, " multi image: %d\n", get_IN_multi_image(in)); DBG (15, " f b type indep: %d\n", get_IN_f_b_type_indep(in)); DBG (15, " f b res indep: %d\n", get_IN_f_b_res_indep(in)); } if (payload_off >= 0x6a) { DBG (15, " dropout spec: %d\n", get_IN_dropout_spec(in)); DBG (15, " dropout non: %d\n", get_IN_dropout_non(in)); DBG (15, " dropout white: %d\n", get_IN_dropout_white(in)); } if (payload_off >= 0x6d) { DBG (15, " skew check: %d\n", get_IN_skew_check(in)); DBG (15, " new feed roller: %d\n", get_IN_new_fd_roll(in)); s->has_adv_paper_prot = get_IN_paper_prot_2(in); DBG (15, " paper protection: %d\n", s->has_adv_paper_prot); } /* this one is weird, overrides the payload length from scanner, * but the enlarged area is just null bytes, so we ignore this */ if (payload_off >= 0x6f) { DBG (15, " extra evpd length: %d\n", get_IN_evpd_len(in)); } if (payload_off >= 0x70) { DBG (15, " paper count: %d\n", get_IN_paper_count(in)); DBG (15, " paper number: %d\n", get_IN_paper_number(in)); DBG (15, " ext send to: %d\n", get_IN_ext_send_to(in)); s->has_staple_detect = get_IN_staple_det(in); DBG (15, " staple det: %d\n", s->has_staple_detect); DBG (15, " pause host: %d\n", get_IN_pause_host(in)); DBG (15, " pause panel: %d\n", get_IN_pause_panel(in)); DBG (15, " pause conf: %d\n", get_IN_pause_conf(in)); DBG (15, " hq print: %d\n", get_IN_hq_print(in)); } if (payload_off >= 0x71) { DBG (15, " ext GHS len: %d\n", get_IN_ext_GHS_len(in)); } if (payload_off >= 0x72) { DBG (15, " smbc func: %d\n", get_IN_smbc_func(in)); DBG (15, " imprint chk b: %d\n", get_IN_imprint_chk_b(in)); DBG (15, " imprint chk f: %d\n", get_IN_imprint_chk_f(in)); DBG (15, " force w bg: %d\n", get_IN_force_w_bg(in)); s->has_df_recovery = get_IN_mf_recover_lvl(in); DBG (15, " mf recover lvl: %d\n", s->has_df_recovery); } if (payload_off >= 0x73) { DBG (15, " first read time: %d\n", get_IN_first_read_time(in)); DBG (15, " div scanning: %d\n", get_IN_div_scanning(in)); DBG (15, " start job: %d\n", get_IN_start_job(in)); DBG (15, " lifetime log: %d\n", get_IN_lifetime_log(in)); DBG (15, " imff save rest: %d\n", get_IN_imff_save_rest(in)); DBG (15, " wide scsi type: %d\n", get_IN_wide_scsi_type(in)); } if (payload_off >= 0x74) { DBG (15, " lut hybrid crop: %d\n", get_IN_lut_hybrid_crop(in)); DBG (15, " over under amt: %d\n", get_IN_over_under_amt(in)); DBG (15, " rgb lut: %d\n", get_IN_rgb_lut(in)); DBG (15, " num lut dl: %d\n", get_IN_num_lut_dl(in)); } /* Various items below are poorly documented or missing */ if (payload_off >= 0x76) { s->has_off_mode = get_IN_erp_lot6_supp(in); DBG (15, " ErP Lot6 (power off timer): %d\n", s->has_off_mode); DBG (15, " sync next feed: %d\n", get_IN_sync_next_feed(in)); } if (payload_off >= 0x79) { DBG (15, " battery: %d\n", get_IN_battery(in)); DBG (15, " battery save: %d\n", get_IN_battery_save(in)); DBG (15, " object position reverse: %d\n", get_IN_op_reverse(in)); } if (payload_off >= 0x7a) { s->has_op_halt = get_IN_op_halt(in); DBG (15, " object position halt: %d\n", s->has_op_halt); } if (payload_off >= 0x7c) { s->has_return_path = get_IN_return_path(in); DBG (15, " return path (card) scanning: %d\n", s->has_return_path); DBG (15, " energy star 3: %d\n", get_IN_energy_star3(in)); } DBG (10, "init_vpd: finish\n"); return SANE_STATUS_GOOD; } static SANE_Status init_ms(struct fujitsu *s) { int ret; int oldDbg=0; unsigned char cmd[MODE_SENSE_len]; size_t cmdLen = MODE_SENSE_len; unsigned char in[MODE_SENSE_data_len]; size_t inLen = MODE_SENSE_data_len; DBG (10, "init_ms: start\n"); if(!s->has_cmd_msen6){ DBG (10, "init_ms: unsupported\n"); return SANE_STATUS_GOOD; } /* some of the following probes will produce errors */ /* so we reduce the dbg level to reduce the noise */ /* however, if user builds with NDEBUG, we can't do that */ /* so we protect the code with the following macro */ IF_DBG( oldDbg=DBG_LEVEL; ) IF_DBG( if(DBG_LEVEL < 35){ DBG_LEVEL = 0; } ) memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, MODE_SENSE_code); set_MSEN_xfer_length (cmd, inLen); if(s->has_MS_autocolor){ DBG (35, "init_ms: autocolor\n"); set_MSEN_pc(cmd, MS_pc_autocolor); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if(ret != SANE_STATUS_GOOD){ s->has_MS_autocolor=0; } } if(s->has_MS_prepick){ DBG (35, "init_ms: prepick\n"); set_MSEN_pc(cmd, MS_pc_prepick); inLen = MODE_SENSE_data_len; ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if(ret != SANE_STATUS_GOOD){ s->has_MS_prepick=0; } } if(s->has_MS_sleep){ DBG (35, "init_ms: sleep\n"); set_MSEN_pc(cmd, MS_pc_sleep); inLen = MODE_SENSE_data_len; ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if(ret != SANE_STATUS_GOOD){ s->has_MS_sleep=0; } } if(s->has_MS_duplex){ DBG (35, "init_ms: duplex\n"); set_MSEN_pc(cmd, MS_pc_duplex); inLen = MODE_SENSE_data_len; ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if(ret != SANE_STATUS_GOOD){ s->has_MS_duplex=0; } } if(s->has_MS_rand){ DBG (35, "init_ms: rand\n"); set_MSEN_pc(cmd, MS_pc_rand); inLen = MODE_SENSE_data_len; ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if(ret != SANE_STATUS_GOOD){ s->has_MS_rand=0; } } if(s->has_MS_bg){ DBG (35, "init_ms: bg\n"); set_MSEN_pc(cmd, MS_pc_bg); inLen = MODE_SENSE_data_len; ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if(ret != SANE_STATUS_GOOD){ s->has_MS_bg=0; } } if(s->has_MS_df){ DBG (35, "init_ms: df\n"); set_MSEN_pc(cmd, MS_pc_df); inLen = MODE_SENSE_data_len; ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if(ret != SANE_STATUS_GOOD){ s->has_MS_df=0; } } if(s->has_MS_dropout){ DBG (35, "init_ms: dropout\n"); set_MSEN_pc(cmd, MS_pc_dropout); inLen = MODE_SENSE_data_len; ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if(ret != SANE_STATUS_GOOD){ s->has_MS_dropout=0; } } if(s->has_MS_buff){ DBG (35, "init_ms: buffer\n"); set_MSEN_pc(cmd, MS_pc_buff); inLen = MODE_SENSE_data_len; ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if(ret != SANE_STATUS_GOOD){ s->has_MS_buff=0; } } if(s->has_MS_auto){ DBG (35, "init_ms: auto\n"); set_MSEN_pc(cmd, MS_pc_auto); inLen = MODE_SENSE_data_len; ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if(ret != SANE_STATUS_GOOD){ s->has_MS_auto=0; } } if(s->has_MS_lamp){ DBG (35, "init_ms: lamp\n"); set_MSEN_pc(cmd, MS_pc_lamp); inLen = MODE_SENSE_data_len; ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if(ret != SANE_STATUS_GOOD){ s->has_MS_lamp=0; } } if(s->has_MS_jobsep){ DBG (35, "init_ms: jobsep\n"); set_MSEN_pc(cmd, MS_pc_jobsep); inLen = MODE_SENSE_data_len; ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if(ret != SANE_STATUS_GOOD){ s->has_MS_jobsep=0; } } IF_DBG (DBG_LEVEL = oldDbg;) DBG (15, " autocolor: %d\n", s->has_MS_autocolor); DBG (15, " prepick: %d\n", s->has_MS_prepick); DBG (15, " sleep: %d\n", s->has_MS_sleep); DBG (15, " duplex: %d\n", s->has_MS_duplex); DBG (15, " rand: %d\n", s->has_MS_rand); DBG (15, " bg: %d\n", s->has_MS_bg); DBG (15, " df: %d\n", s->has_MS_df); DBG (15, " dropout: %d\n", s->has_MS_dropout); DBG (15, " buff: %d\n", s->has_MS_buff); DBG (15, " auto: %d\n", s->has_MS_auto); DBG (15, " lamp: %d\n", s->has_MS_lamp); DBG (15, " jobsep: %d\n", s->has_MS_jobsep); DBG (10, "init_ms: finish\n"); return SANE_STATUS_GOOD; } /* * get model specific info that is not in vpd, and correct * errors in vpd data. struct is already initialized to 0. */ static SANE_Status init_model (struct fujitsu *s) { int i; DBG (10, "init_model: start\n"); /* for most scanners these are good defaults */ if(s->can_mode[MODE_LINEART] || s->can_mode[MODE_HALFTONE] || s->can_mode[MODE_GRAYSCALE] ){ s->has_vuid_mono = 1; } if(s->can_mode[MODE_COLOR]){ s->has_vuid_color = 1; } for(i=MODE_HALFTONE;i<=MODE_COLOR;i++){ s->step_x_res[i] = s->step_x_res[MODE_LINEART]; s->step_y_res[i] = s->step_y_res[MODE_LINEART]; } 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; s->ppl_mod_by_mode[MODE_LINEART] = 8; s->ppl_mod_by_mode[MODE_HALFTONE] = 8; s->ppl_mod_by_mode[MODE_GRAYSCALE] = 1; s->ppl_mod_by_mode[MODE_COLOR] = 1; /* endorser type tells string length (among other things) */ if(s->has_endorser_b){ /*old-style is 40 bytes*/ if(s->endorser_type_b == ET_OLD){ s->endorser_string_len = 40; } /*short new style is 60 bytes*/ else if(s->endorser_type_b == ET_30){ s->endorser_string_len = 60; } /*long new style is 80 bytes*/ else if(s->endorser_type_b == ET_40){ s->endorser_string_len = 80; } } else if(s->has_endorser_f){ /*old-style is 40 bytes*/ if(s->endorser_type_f == ET_OLD){ s->endorser_string_len = 40; } /*short new style is 60 bytes*/ else if(s->endorser_type_f == ET_30){ s->endorser_string_len = 60; } /*long new style is 80 bytes*/ else if(s->endorser_type_f == ET_40){ s->endorser_string_len = 80; } } /* convert to 1200dpi units */ s->max_x = s->max_x_basic * 1200 / s->basic_x_res; s->max_y = s->max_y_basic * 1200 / s->basic_y_res; /* setup the list with a single choice, in 1200dpi units, at max res */ s->max_y_by_res[0].res = s->max_y_res; s->max_y_by_res[0].len = s->max_y; /* assume these are same as adf, override below */ s->max_x_fb = s->max_x; s->max_y_fb = s->max_y; /* assume we can do these. we will disable * them at runtime if they cannot */ s->has_pixelsize = 1; s->has_MS_autocolor = 1; s->has_MS_prepick = 1; s->has_MS_sleep = 1; s->has_MS_duplex = 1; s->has_MS_rand = 1; s->has_MS_bg = 1; s->has_MS_df = 1; s->has_MS_dropout = 1; s->has_MS_buff = 1; s->has_MS_auto = 1; s->has_MS_lamp = 1; s->has_MS_jobsep = 1; /* these two scanners lie about their capabilities, * and/or differ significantly from most other models */ if (strstr (s->model_name, "M3091") || strstr (s->model_name, "M3092")) { /* lies */ s->has_rif = 1; s->has_back = 0; s->adbits = 8; if (strstr (s->model_name, "M3092")) s->has_flatbed = 1; /*actually does have res range in non-color modes */ for(i=MODE_LINEART;istep_x_res[i] = 1; s->step_y_res[i] = 1; } /*but the color mode y list is very limited, only 75, 150, 300 (and 600)*/ for(i=0;i<16;i++){ s->std_res[i] = 0; } s->std_res[1] = 1; s->std_res[4] = 1; s->std_res[9] = 1; /* weirdness */ s->has_vuid_3091 = 1; s->has_vuid_color = 0; s->has_vuid_mono = 0; s->has_short_pixelsize = 1; s->color_interlace = COLOR_INTERLACE_3091; s->duplex_interlace = DUPLEX_INTERLACE_3091; s->ghs_in_rs = 1; /* might be inaccurate */ s->num_internal_gamma = 1; s->num_download_gamma = 0; 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->model_name, "M3093")){ /* lies */ s->has_back = 0; s->adbits = 8; /* weirdness */ s->duplex_interlace = DUPLEX_INTERLACE_NONE; } else if ( strstr (s->model_name, "M309") || strstr (s->model_name, "M409")){ /* weirdness */ s->broken_diag_serial = 1; /* lies */ s->adbits = 8; } else if (strstr (s->model_name, "fi-4120C2") || strstr (s->model_name, "fi-4220C2") ) { /* missing from vpd */ s->os_x_basic = 118; s->os_y_basic = 118; s->max_y_fb = 14032; } else if (strstr (s->model_name, "fi-4220C")){ /* missing from vpd */ s->max_y_fb = 14032; } else if (strstr (s->model_name,"fi-4340") || strstr (s->model_name, "fi-4750") ) { /* weirdness */ s->broken_diag_serial = 1; } /* some firmware versions use capital f? */ else if (strstr (s->model_name, "Fi-4860") || strstr (s->model_name, "fi-4860") ) { /* weirdness */ s->broken_diag_serial = 1; s->ppl_mod_by_mode[MODE_LINEART] = 32; s->ppl_mod_by_mode[MODE_HALFTONE] = 32; s->ppl_mod_by_mode[MODE_GRAYSCALE] = 4; s->ppl_mod_by_mode[MODE_COLOR] = 4; } /* some firmware versions use capital f? */ else if (strstr (s->model_name, "Fi-4990") || strstr (s->model_name, "fi-4990") ) { /* weirdness */ s->duplex_interlace = DUPLEX_INTERLACE_NONE; s->color_interlace = COLOR_INTERLACE_RRGGBB; s->ppl_mod_by_mode[MODE_LINEART] = 32; s->ppl_mod_by_mode[MODE_HALFTONE] = 32; s->ppl_mod_by_mode[MODE_GRAYSCALE] = 4; s->ppl_mod_by_mode[MODE_COLOR] = 4; } else if (strstr (s->model_name,"fi-5110C")){ /* missing from vpd */ s->os_x_basic = 147; s->os_y_basic = 147; } else if (strstr (s->model_name,"fi-5110EOX")){ /* weirdness */ s->cropping_mode = CROP_ABSOLUTE; } else if (strstr (s->model_name,"fi-5220C")){ /* missing from vpd */ s->max_x_fb = 10764; s->max_y_fb = 14032; } else if (strstr (s->model_name,"fi-5530") || strstr (s->model_name,"fi-5650") || strstr (s->model_name,"fi-5750")){ /* lies - usb only */ if(s->connection == CONNECTION_USB) s->adbits = 8; } else if (strstr (s->model_name,"S1500")){ /*lies*/ s->has_MS_bg=0; s->has_MS_prepick=0; } /* also includes the 'Z' models */ else if (strstr (s->model_name,"fi-6130") || strstr (s->model_name,"fi-6140")){ /* weirdness */ /* these machines have longer max paper at lower res */ s->max_y_by_res[1].res = 200; s->max_y_by_res[1].len = 151512; } /* also includes the 'Z' models */ else if (strstr (s->model_name,"fi-6230") || strstr (s->model_name,"fi-6240")){ /* weirdness */ /* these machines have longer max paper at lower res */ s->max_y_by_res[1].res = 200; s->max_y_by_res[1].len = 151512; /* missing from vpd */ s->max_x_fb = 10764; /* was previously 10488 */ s->max_y_fb = 14032; /* some scanners can be slightly more? */ } else if (strstr (s->model_name,"fi-6110")){ /* weirdness */ /* these machines have longer max paper at lower res */ s->max_y_by_res[1].res = 200; s->max_y_by_res[1].len = 151512; /*lies*/ s->has_MS_bg=0; s->has_MS_prepick=0; } else if (strstr (s->model_name,"fi-6800") || strstr (s->model_name,"fi-5900")){ /* do not need overrides */ } else if (strstr (s->model_name,"iX500")){ /* locks up scanner if we try to auto detect */ s->has_MS_lamp = 0; /* weirdness */ s->need_q_table = 1; s->need_diag_preread = 1; s->ppl_mod_by_mode[MODE_COLOR] = 2; s->hopper_before_op = 1; s->no_wait_after_op = 1; /* lies */ s->adbits = 8; /* we have to simulate these in software*/ s->can_mode[MODE_LINEART] = 2; s->can_mode[MODE_GRAYSCALE] = 2; /* don't bother with this one */ s->can_mode[MODE_HALFTONE] = 0; } /*mostly copied from iX500*/ else if (strstr (s->model_name,"iX100")){ /* locks up scanner if we try to auto detect */ s->has_MS_lamp = 0; /* weirdness */ s->need_q_table = 1; s->need_diag_preread = 1; s->ppl_mod_by_mode[MODE_COLOR] = 2; s->hopper_before_op = 1; s->no_wait_after_op = 1; /* lies */ s->adbits = 8; /* don't bother with this one */ s->can_mode[MODE_HALFTONE] = 0; } else if (strstr (s->model_name,"fi-7180") || strstr (s->model_name,"fi-7160")){ /* locks up scanner if we try to auto detect */ s->has_MS_lamp = 0; /* weirdness */ /* these machines have longer max paper at lower res */ s->max_y_by_res[1].res = 400; s->max_y_by_res[1].len = 194268; s->max_y_by_res[2].res = 300; s->max_y_by_res[2].len = 260268; s->max_y_by_res[3].res = 200; s->max_y_by_res[3].len = 266268; } else if (strstr (s->model_name,"fi-7280") || strstr (s->model_name,"fi-7260")){ /* locks up scanner if we try to auto detect */ s->has_MS_lamp = 0; /* weirdness */ /* these machines have longer max paper at lower res */ s->max_y_by_res[1].res = 400; s->max_y_by_res[1].len = 194268; s->max_y_by_res[2].res = 300; s->max_y_by_res[2].len = 260268; s->max_y_by_res[3].res = 200; s->max_y_by_res[3].len = 266268; /* missing from vpd */ s->max_x_fb = 10764; s->max_y_fb = 14032; /* some scanners can be slightly more? */ } else if (strstr (s->model_name,"fi-7480") || strstr (s->model_name,"fi-7460")){ /* weirdness */ /* these machines have longer max paper at lower res */ s->max_y_by_res[1].res = 400; s->max_y_by_res[1].len = 194268; s->max_y_by_res[2].res = 300; s->max_y_by_res[2].len = 260268; s->max_y_by_res[3].res = 200; s->max_y_by_res[3].len = 266268; } else if (strstr (s->model_name,"fi-7030")){ /* weirdness */ /* these machines have longer max paper at lower res */ s->max_y_by_res[1].res = 400; s->max_y_by_res[1].len = 192000; s->max_y_by_res[2].res = 300; s->max_y_by_res[2].len = 258000; s->max_y_by_res[3].res = 200; s->max_y_by_res[3].len = 264000; } else if (strstr (s->model_name,"fi-7700") || strstr (s->model_name,"fi-7600")){ /* weirdness */ /* these machines have longer max paper at lower res */ s->max_y_by_res[1].res = 400; s->max_y_by_res[1].len = 192000; s->max_y_by_res[2].res = 300; s->max_y_by_res[2].len = 258000; s->max_y_by_res[3].res = 200; s->max_y_by_res[3].len = 264000; } DBG (10, "init_model: finish\n"); return SANE_STATUS_GOOD; } static SANE_Status set_mode (struct fujitsu *s, int mode) { int i; /* give the user what they asked for */ s->u_mode = mode; /* give the scanner the closest mode */ for(i=MODE_COLOR;i>=mode;i--){ if(s->can_mode[i] == 1){ s->s_mode = i; } } return SANE_STATUS_GOOD; } /* * set good default user values. * struct is already initialized to 0. */ static SANE_Status init_user (struct fujitsu *s) { DBG (10, "init_user: start\n"); /* source */ if(s->has_flatbed) s->source = SOURCE_FLATBED; else if(s->has_adf) s->source = SOURCE_ADF_FRONT; else if(s->has_return_path) s->source = SOURCE_CARD_FRONT; /* scan mode */ if(s->can_mode[MODE_LINEART]) set_mode(s,MODE_LINEART); else if(s->can_mode[MODE_HALFTONE]) set_mode(s,MODE_HALFTONE); else if(s->can_mode[MODE_GRAYSCALE]) set_mode(s,MODE_GRAYSCALE); else if(s->can_mode[MODE_COLOR]) set_mode(s,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; } /* page width US-Letter */ s->page_width = 8.5 * 1200; if(s->page_width > s->max_x){ s->page_width = s->max_x; } /* page height US-Letter */ s->page_height = 11 * 1200; set_max_y(s); if(s->page_height > s->max_y){ s->page_height = s->max_y; } /* bottom-right x */ s->br_x = s->page_width; /* bottom-right y */ s->br_y = s->page_height; /* gamma ramp exponent */ s->gamma = 1; /* safe endorser settings */ s->u_endorser_bits=16; s->u_endorser_step=1; s->u_endorser_side=ED_back; if(s->has_endorser_f){ s->u_endorser_side=ED_front; } s->u_endorser_dir=DIR_TTB; strcpy((char *)s->u_endorser_string,"%05ud"); /* more recent machines default to this being 'on', * * which causes the scanner to ingest multiple pages * * even when the user only wants one */ s->buff_mode = MSEL_OFF; /* useful features of newer scanners which we turn on, * even though the scanner defaults to off */ if(s->has_paper_protect){ s->paper_protect = MSEL_ON; } if(s->has_staple_detect){ s->staple_detect = MSEL_ON; } if(s->has_df_recovery){ s->df_recovery = MSEL_ON; } if(s->has_adv_paper_prot){ s->adv_paper_prot = MSEL_ON; } s->off_time = 240; DBG (10, "init_user: finish\n"); return SANE_STATUS_GOOD; } /* * This function presets the "option" array to blank */ static SANE_Status init_options (struct fujitsu *s) { int i; DBG (10, "init_options: start\n"); 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; } /* 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].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; DBG (10, "init_options: finish\n"); return SANE_STATUS_GOOD; } /* * send set window repeatedly to color scanners, * searching for valid color interlacing mode */ static SANE_Status init_interlace (struct fujitsu *s) { SANE_Status ret = SANE_STATUS_GOOD; int curr_mode = s->u_mode; int oldDbg=0; DBG (10, "init_interlace: start\n"); if(s->color_interlace != COLOR_INTERLACE_UNK){ DBG (10, "init_interlace: already loaded\n"); return SANE_STATUS_GOOD; } if(!s->has_vuid_color){ DBG (10, "init_interlace: color unsupported\n"); return SANE_STATUS_GOOD; } /* set to color mode first */ set_mode(s,MODE_COLOR); /* load our own private copy of scan params */ ret = update_params(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "init_interlace: ERROR: cannot update params\n"); return ret; } /*loop thru all the formats we support*/ for(s->color_interlace = COLOR_INTERLACE_RGB; s->color_interlace <= COLOR_INTERLACE_RRGGBB; s->color_interlace++){ /* some of the following probes will produce errors */ /* so we reduce the dbg level to reduce the noise */ /* however, if user builds with NDEBUG, we can't do that */ /* so we protect the code with the following macro */ IF_DBG( oldDbg=DBG_LEVEL; ) IF_DBG( if(DBG_LEVEL < 35){ DBG_LEVEL = 0; } ) ret = set_window(s); IF_DBG (DBG_LEVEL = oldDbg;) if (ret == SANE_STATUS_GOOD){ break; } else{ DBG (15, "init_interlace: not %d\n", s->color_interlace); } } if (ret != SANE_STATUS_GOOD){ DBG (5, "init_interlace: no valid interlacings\n"); return SANE_STATUS_INVAL; } DBG (15, "init_interlace: color_interlace: %d\n",s->color_interlace); /* restore mode */ set_mode(s,curr_mode); DBG (10, "init_interlace: finish\n"); return SANE_STATUS_GOOD; } /* * send diag query for serial number, and read result back * use it to build a unique name for scanner in s->serial_name */ static SANE_Status init_serial (struct fujitsu *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned int sn = 0; unsigned char cmd[SEND_DIAGNOSTIC_len]; /*also big enough for READ_DIAG*/ size_t cmdLen = SEND_DIAGNOSTIC_len; unsigned char out[SD_gdi_len]; size_t outLen = SD_gdi_len; unsigned char in[RD_gdi_len]; size_t inLen = RD_gdi_len; DBG (10, "init_serial: start\n"); if (!s->has_cmd_sdiag || !s->has_cmd_rdiag || s->broken_diag_serial){ DBG (5, "init_serial: send/read diag not supported, returning\n"); return SANE_STATUS_INVAL; } memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SEND_DIAGNOSTIC_code); set_SD_slftst(cmd, 0); set_SD_xferlen(cmd, outLen); memcpy(out,SD_gdi_string,outLen); ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); if (ret != SANE_STATUS_GOOD){ DBG (5, "init_serial: send diag error: %d\n", ret); return ret; } memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, READ_DIAGNOSTIC_code); set_RD_xferlen(cmd, inLen); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if (ret != SANE_STATUS_GOOD){ DBG (5, "init_serial: read diag error: %d\n", ret); return ret; } sn = get_RD_id_serial(in); DBG (15, "init_serial: found sn %d\n",sn); sprintf(s->serial_name, "%s:%d", s->model_name, sn); DBG (15, "init_serial: serial_name: %s\n",s->serial_name); DBG (10, "init_serial: finish\n"); return SANE_STATUS_GOOD; } /* * 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 fujitsu *dev = NULL; struct fujitsu *s = NULL; SANE_Status ret; DBG (10, "sane_open: start\n"); if(fujitsu_devList){ DBG (15, "sane_open: searching currently attached scanners\n"); } else{ DBG (15, "sane_open: no scanners currently attached, attaching\n"); ret = sane_get_devices(NULL,0); if(ret != SANE_STATUS_GOOD){ return ret; } } if(!name || !name[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 || strcmp (dev->device_name, name) == 0) { /*always allow sanei devname*/ s = dev; break; } } } if (!s) { DBG (5, "sane_open: no device found\n"); return SANE_STATUS_INVAL; } DBG (15, "sane_open: device %s found\n", s->sane.name); *handle = s; /* connect the fd so we can talk to scanner */ ret = connect_fd(s); if(ret != SANE_STATUS_GOOD){ return ret; } DBG (10, "sane_open: finish\n"); return SANE_STATUS_GOOD; } /* * @@ Section 3 - SANE Options functions */ /* * 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,j; SANE_Option_Descriptor *opt = &s->opt[option]; DBG (20, "sane_get_option_descriptor: %d\n", option); if ((unsigned) option >= NUM_OPTIONS) return NULL; /* "Mode" group -------------------------------------------------------- */ if(option==OPT_STANDARD_GROUP){ opt->name = SANE_NAME_STANDARD; opt->title = SANE_TITLE_STANDARD; opt->desc = SANE_DESC_STANDARD; opt->type = SANE_TYPE_GROUP; opt->constraint_type = SANE_CONSTRAINT_NONE; } /* source */ if(option==OPT_SOURCE){ i=0; if(s->has_flatbed){ s->source_list[i++]=STRING_FLATBED; } if(s->has_adf){ s->source_list[i++]=STRING_ADFFRONT; if(s->has_back){ s->source_list[i++]=STRING_ADFBACK; } if(s->has_duplex){ s->source_list[i++]=STRING_ADFDUPLEX; } } if(s->has_return_path){ s->source_list[i++]=STRING_CARDFRONT; if(s->has_back){ s->source_list[i++]=STRING_CARDBACK; } if(s->has_duplex){ s->source_list[i++]=STRING_CARDDUPLEX; } } 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; } /* scan mode */ if(option==OPT_MODE){ i=0; if(s->can_mode[MODE_LINEART]){ s->mode_list[i++]=STRING_LINEART; } if(s->can_mode[MODE_HALFTONE]){ s->mode_list[i++]=STRING_HALFTONE; } if(s->can_mode[MODE_GRAYSCALE]){ s->mode_list[i++]=STRING_GRAYSCALE; } if(s->can_mode[MODE_COLOR]){ s->mode_list[i++]=STRING_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; } /* resolution */ /* some scanners only support fixed res * build a list of possible choices */ if(option==OPT_RES){ opt->name = SANE_NAME_SCAN_RESOLUTION; opt->title = SANE_TITLE_SCAN_RESOLUTION; opt->desc = SANE_DESC_SCAN_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->s_mode] && s->step_y_res[s->s_mode]){ s->res_range.min = s->min_x_res; s->res_range.max = s->max_x_res; s->res_range.quant = s->step_x_res[s->s_mode]; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->res_range; } else{ int reses[] = {60,75,100,120,150,160,180,200,240,300,320,400,480,600,800,1200}; i=0; for(j=0;j<16;j++){ if(s->std_res[j] && s->max_x_res >= reses[j] && s->min_x_res <= reses[j] && s->max_y_res >= reses[j] && s->min_y_res <= reses[j] ){ s->res_list[++i] = reses[j]; } } s->res_list[0] = i; opt->constraint_type = SANE_CONSTRAINT_WORD_LIST; opt->constraint.word_list = s->res_list; } } /* "Geometry" group ---------------------------------------------------- */ if(option==OPT_GEOMETRY_GROUP){ opt->name = SANE_NAME_GEOMETRY; opt->title = SANE_TITLE_GEOMETRY; opt->desc = SANE_DESC_GEOMETRY; opt->type = SANE_TYPE_GROUP; opt->constraint_type = SANE_CONSTRAINT_NONE; } /* 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(get_page_width(s)); 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; } /* 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(get_page_height(s)); 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; } /* 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(get_page_width(s)); 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; } /* 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(get_page_height(s)); 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; } /* 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 = SANE_NAME_PAGE_WIDTH; opt->title = SANE_TITLE_PAGE_WIDTH; opt->desc = SANE_DESC_PAGE_WIDTH; 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->has_adf || s->has_return_path){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(s->source == SOURCE_FLATBED){ opt->cap |= SANE_CAP_INACTIVE; } } else{ opt->cap = SANE_CAP_INACTIVE; } } /* 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 = SANE_NAME_PAGE_HEIGHT; opt->title = SANE_TITLE_PAGE_HEIGHT; opt->desc = SANE_DESC_PAGE_HEIGHT; 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->has_adf || s->has_return_path){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(s->source == SOURCE_FLATBED){ opt->cap |= SANE_CAP_INACTIVE; } } else{ opt->cap = SANE_CAP_INACTIVE; } } /* "Enhancement" group ------------------------------------------------- */ if(option==OPT_ENHANCEMENT_GROUP){ opt->name = SANE_NAME_ENHANCEMENT; opt->title = SANE_TITLE_ENHANCEMENT; opt->desc = SANE_DESC_ENHANCEMENT; opt->type = SANE_TYPE_GROUP; opt->constraint_type = SANE_CONSTRAINT_NONE; } /* 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.quant=1; /* some have hardware brightness (always 0 to 255?) */ /* some use LUT or GT (-127 to +127)*/ if (s->brightness_steps || s->num_download_gamma){ s->brightness_range.min=-127; s->brightness_range.max=127; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } else{ opt->cap = SANE_CAP_INACTIVE; } } /* 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.quant=1; /* some have hardware contrast (always 0 to 255?) */ /* some use LUT or GT (-127 to +127)*/ if (s->contrast_steps || s->num_download_gamma){ s->contrast_range.min=-127; s->contrast_range.max=127; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } else { opt->cap = SANE_CAP_INACTIVE; } } /* gamma */ if(option==OPT_GAMMA){ opt->name = "gamma"; opt->title = SANE_I18N ("Gamma function exponent"); opt->desc = SANE_I18N ("Changes intensity of midtones"); opt->type = SANE_TYPE_FIXED; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->gamma_range; /* value ranges from .3 to 5, should be log scale? */ s->gamma_range.quant=SANE_FIX(0.01); s->gamma_range.min=SANE_FIX(0.3); s->gamma_range.max=SANE_FIX(5); /* scanner has gamma via LUT or GT */ /*if (s->num_download_gamma){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } else { opt->cap = SANE_CAP_INACTIVE; }*/ opt->cap = SANE_CAP_INACTIVE; } /*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){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(s->u_mode != MODE_LINEART){ opt->cap |= SANE_CAP_INACTIVE; } } else { opt->cap = SANE_CAP_INACTIVE; } } /* =============== common ipc params ================================ */ if(option==OPT_RIF){ opt->name = "rif"; opt->title = SANE_I18N ("RIF"); opt->desc = SANE_I18N ("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; } if(option==OPT_HT_TYPE){ i=0; s->ht_type_list[i++]=STRING_DEFAULT; s->ht_type_list[i++]=STRING_DITHER; s->ht_type_list[i++]=STRING_DIFFUSION; s->ht_type_list[i]=NULL; opt->name = "ht-type"; opt->title = SANE_I18N ("Halftone type"); opt->desc = SANE_I18N ("Control type of halftone filter"); opt->type = SANE_TYPE_STRING; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; opt->constraint.string_list = s->ht_type_list; opt->size = maxStringSize (opt->constraint.string_list); if(s->has_diffusion){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(s->s_mode != MODE_HALFTONE){ opt->cap |= SANE_CAP_INACTIVE; } } else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_HT_PATTERN){ opt->name = "ht-pattern"; opt->title = SANE_I18N ("Halftone pattern"); opt->desc = SANE_I18N ("Control pattern of halftone filter"); opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->ht_pattern_range; s->ht_pattern_range.min=0; s->ht_pattern_range.max=s->num_internal_dither - 1; s->ht_pattern_range.quant=1; if (s->num_internal_dither){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(s->s_mode != MODE_HALFTONE){ opt->cap |= SANE_CAP_INACTIVE; } } else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_OUTLINE){ opt->name = "outline"; opt->title = SANE_I18N ("Outline"); opt->desc = SANE_I18N ("Perform outline extraction"); opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; if (s->has_outline) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_EMPHASIS){ opt->name = "emphasis"; opt->title = SANE_I18N ("Emphasis"); opt->desc = SANE_I18N ("Negative to smooth or positive to sharpen image"); opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->emphasis_range; s->emphasis_range.min=-128; s->emphasis_range.max=127; s->emphasis_range.quant=1; if (s->has_emphasis) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_SEPARATION){ opt->name = "separation"; opt->title = SANE_I18N ("Separation"); opt->desc = SANE_I18N ("Enable automatic separation of image and text"); opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; if (s->has_autosep) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_MIRRORING){ opt->name = "mirroring"; opt->title = SANE_I18N ("Mirroring"); opt->desc = SANE_I18N ("Reflect output image horizontally"); opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; if (s->has_mirroring) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_WL_FOLLOW){ i=0; s->wl_follow_list[i++]=STRING_DEFAULT; s->wl_follow_list[i++]=STRING_ON; s->wl_follow_list[i++]=STRING_OFF; s->wl_follow_list[i]=NULL; opt->name = "wl-follow"; opt->title = SANE_I18N ("White level follower"); opt->desc = SANE_I18N ("Control white level follower"); opt->type = SANE_TYPE_STRING; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; opt->constraint.string_list = s->wl_follow_list; opt->size = maxStringSize (opt->constraint.string_list); if (s->has_wl_follow) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; else opt->cap = SANE_CAP_INACTIVE; } /* =============== DTC params ================================ */ /* enabled when in dtc mode (manually or by default) */ if(option==OPT_BP_FILTER){ opt->name = "bp-filter"; opt->title = SANE_I18N ("BP filter"); opt->desc = SANE_I18N ("Improves quality of high resolution ball-point pen text"); opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; if ( s->has_dtc ){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(get_ipc_mode(s) == WD_ipc_SDTC){ opt->cap |= SANE_CAP_INACTIVE; } } else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_SMOOTHING){ opt->name = "smoothing"; opt->title = SANE_I18N ("Smoothing"); opt->desc = SANE_I18N ("Enable smoothing for improved OCR"); opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; if ( s->has_dtc ){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(get_ipc_mode(s) == WD_ipc_SDTC){ opt->cap |= SANE_CAP_INACTIVE; } } else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_GAMMA_CURVE){ opt->name = "gamma-curve"; opt->title = SANE_I18N ("Gamma curve"); opt->desc = SANE_I18N ("Gamma curve, from light to dark, but upper two may not work"); opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->gamma_curve_range; s->gamma_curve_range.min=0; s->gamma_curve_range.max=3; s->gamma_curve_range.quant=1; if ( s->has_dtc ){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(get_ipc_mode(s) == WD_ipc_SDTC){ opt->cap |= SANE_CAP_INACTIVE; } } else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_THRESHOLD_CURVE){ opt->name = "threshold-curve"; opt->title = SANE_I18N ("Threshold curve"); opt->desc = SANE_I18N ("Threshold curve, from light to dark, but upper two may not be linear"); opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->threshold_curve_range; s->threshold_curve_range.min=0; s->threshold_curve_range.max=7; s->threshold_curve_range.quant=1; if ( s->has_dtc ){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(get_ipc_mode(s) == WD_ipc_SDTC){ opt->cap |= SANE_CAP_INACTIVE; } } else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_THRESHOLD_WHITE){ opt->name = "threshold-white"; opt->title = SANE_I18N ("Threshold white"); opt->desc = SANE_I18N ("Set pixels equal to threshold to white instead of black"); opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; if ( s->has_dtc ){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(get_ipc_mode(s) == WD_ipc_SDTC){ opt->cap |= SANE_CAP_INACTIVE; } } else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_NOISE_REMOVAL){ opt->name = "noise-removal"; opt->title = SANE_I18N ("Noise removal"); opt->desc = SANE_I18N ("Noise removal"); opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; if ( s->has_dtc ){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(get_ipc_mode(s) == WD_ipc_SDTC){ opt->cap |= SANE_CAP_INACTIVE; } } else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_MATRIX_5){ opt->name = "matrix-5x5"; opt->title = SANE_I18N ("Matrix 5x5"); opt->desc = SANE_I18N ("Remove 5 pixel square noise"); opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; if ( s->has_dtc ){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(!s->noise_removal){ opt->cap |= SANE_CAP_INACTIVE; } } else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_MATRIX_4){ opt->name = "matrix-4x4"; opt->title = SANE_I18N ("Matrix 4x4"); opt->desc = SANE_I18N ("Remove 4 pixel square noise"); opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; if ( s->has_dtc ){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(!s->noise_removal){ opt->cap |= SANE_CAP_INACTIVE; } } else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_MATRIX_3){ opt->name = "matrix-3x3"; opt->title = SANE_I18N ("Matrix 3x3"); opt->desc = SANE_I18N ("Remove 3 pixel square noise"); opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; if ( s->has_dtc ){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(!s->noise_removal){ opt->cap |= SANE_CAP_INACTIVE; } } else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_MATRIX_2){ opt->name = "matrix-2x2"; opt->title = SANE_I18N ("Matrix 2x2"); opt->desc = SANE_I18N ("Remove 2 pixel square noise"); opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; if ( s->has_dtc ){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(!s->noise_removal){ opt->cap |= SANE_CAP_INACTIVE; } } else opt->cap = SANE_CAP_INACTIVE; } /* =============== SDTC param ================================ */ /* enabled when in sdtc mode (manually or by default) */ /* called variance with ipc2, sensitivity with ipc3 */ if(option==OPT_VARIANCE){ opt->name = "variance"; opt->title = SANE_I18N ("Variance"); opt->desc = SANE_I18N ("Set SDTC variance rate (sensitivity), 0 equals 127"); opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->variance_range; s->variance_range.min=0; s->variance_range.max=255; s->variance_range.quant=1; if ( s->has_sdtc ){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if(get_ipc_mode(s) == WD_ipc_DTC){ opt->cap |= SANE_CAP_INACTIVE; } } else opt->cap = SANE_CAP_INACTIVE; } /* "Advanced" group ------------------------------------------------------ */ if(option==OPT_ADVANCED_GROUP){ opt->name = SANE_NAME_ADVANCED; opt->title = SANE_TITLE_ADVANCED; opt->desc = SANE_DESC_ADVANCED; opt->type = SANE_TYPE_GROUP; opt->constraint_type = SANE_CONSTRAINT_NONE; } /*automatic width detection */ if(option==OPT_AWD){ opt->name = "awd"; opt->title = SANE_I18N ("Auto width detection"); opt->desc = SANE_I18N ("Scanner detects paper sides. May reduce scanning speed."); opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_NONE; /* this option is useless by itself? */ if (0 && s->has_MS_auto && s->has_hybrid_crop_deskew){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; } else opt->cap = SANE_CAP_INACTIVE; } /*automatic length detection */ if(option==OPT_ALD){ opt->name = "ald"; opt->title = SANE_I18N ("Auto length detection"); opt->desc = SANE_I18N ("Scanner detects paper lower edge. May confuse some frontends."); opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_NONE; if (s->has_MS_auto){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; } else opt->cap = SANE_CAP_INACTIVE; } /*image compression*/ if(option==OPT_COMPRESS){ i=0; s->compress_list[i++]=STRING_NONE; if(s->has_comp_JPG1){ #ifndef SANE_JPEG_DISABLED s->compress_list[i++]=STRING_JPEG; #endif } s->compress_list[i]=NULL; opt->name = "compression"; opt->title = SANE_I18N ("Compression"); opt->desc = SANE_I18N ("Enable compressed data. May crash your front-end program"); opt->type = SANE_TYPE_STRING; opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; opt->constraint.string_list = s->compress_list; opt->size = maxStringSize (opt->constraint.string_list); if (i > 1){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; if ( must_downsample(s) || s->s_mode < MODE_GRAYSCALE ){ opt->cap |= SANE_CAP_INACTIVE; } } else opt->cap = SANE_CAP_INACTIVE; } /*image compression arg*/ if(option==OPT_COMPRESS_ARG){ opt->name = "compression-arg"; opt->title = SANE_I18N ("Compression argument"); opt->desc = SANE_I18N ("Level of JPEG compression. 1 is small file, 7 is large file. 0 (default) is same as 4"); opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->compress_arg_range; s->compress_arg_range.quant=1; if(s->has_comp_JPG1){ s->compress_arg_range.min=0; s->compress_arg_range.max=7; #ifndef SANE_JPEG_DISABLED opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; #endif if(s->compress != COMP_JPEG){ opt->cap |= SANE_CAP_INACTIVE; } } else opt->cap = SANE_CAP_INACTIVE; } /*double feed detection*/ if(option==OPT_DF_ACTION){ s->df_action_list[0] = STRING_DEFAULT; s->df_action_list[1] = STRING_CONTINUE; s->df_action_list[2] = STRING_STOP; s->df_action_list[3] = NULL; opt->name = "df-action"; opt->title = SANE_I18N ("DF action"); opt->desc = SANE_I18N ("Action following double feed error"); opt->type = SANE_TYPE_STRING; opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; opt->constraint.string_list = s->df_action_list; opt->size = maxStringSize (opt->constraint.string_list); if (s->has_MS_df) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } /*double feed by skew*/ if(option==OPT_DF_SKEW){ opt->name = "df-skew"; opt->title = SANE_I18N ("DF skew"); opt->desc = SANE_I18N ("Enable double feed error due to skew"); opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_NONE; if (s->has_MS_df){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; if(!s->df_action) opt->cap |= SANE_CAP_INACTIVE; } else opt->cap = SANE_CAP_INACTIVE; } /*double feed by thickness */ if(option==OPT_DF_THICKNESS){ opt->name = "df-thickness"; opt->title = SANE_I18N ("DF thickness"); opt->desc = SANE_I18N ("Enable double feed error due to paper thickness"); opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_NONE; if (s->has_MS_df){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; if(!s->df_action) opt->cap |= SANE_CAP_INACTIVE; } else opt->cap = SANE_CAP_INACTIVE; } /*double feed by length*/ if(option==OPT_DF_LENGTH){ opt->name = "df-length"; opt->title = SANE_I18N ("DF length"); opt->desc = SANE_I18N ("Enable double feed error due to paper length"); opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_NONE; if (s->has_MS_df){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; if(!s->df_action) opt->cap |= SANE_CAP_INACTIVE; } else opt->cap = SANE_CAP_INACTIVE; } /*double feed length difference*/ if(option==OPT_DF_DIFF){ s->df_diff_list[0] = STRING_DEFAULT; s->df_diff_list[1] = STRING_10MM; s->df_diff_list[2] = STRING_15MM; s->df_diff_list[3] = STRING_20MM; s->df_diff_list[4] = NULL; opt->name = "df-diff"; opt->title = SANE_I18N ("DF length difference"); opt->desc = SANE_I18N ("Difference in page length to trigger double feed error"); opt->type = SANE_TYPE_STRING; opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; opt->constraint.string_list = s->df_diff_list; opt->size = maxStringSize (opt->constraint.string_list); if (s->has_MS_df){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; if(!s->df_action || !s->df_diff) opt->cap |= SANE_CAP_INACTIVE; } else opt->cap = SANE_CAP_INACTIVE; } /*df_recovery*/ if(option==OPT_DF_RECOVERY){ s->df_recovery_list[0] = STRING_DEFAULT; s->df_recovery_list[1] = STRING_OFF; s->df_recovery_list[2] = STRING_ON; s->df_recovery_list[3] = NULL; opt->name = "df-recovery"; opt->title = SANE_I18N ("DF recovery mode"); opt->desc = SANE_I18N ("Request scanner to reverse feed on paper jam"); opt->type = SANE_TYPE_STRING; opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; opt->constraint.string_list = s->df_recovery_list; opt->size = maxStringSize (opt->constraint.string_list); if (s->has_MS_df && s->has_df_recovery) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } /*paper_protect*/ if(option==OPT_PAPER_PROTECT){ s->paper_protect_list[0] = STRING_DEFAULT; s->paper_protect_list[1] = STRING_OFF; s->paper_protect_list[2] = STRING_ON; s->paper_protect_list[3] = NULL; opt->name = "paper-protect"; opt->title = SANE_I18N ("Paper protection"); opt->desc = SANE_I18N ("Request scanner to predict jams in the ADF"); opt->type = SANE_TYPE_STRING; opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; opt->constraint.string_list = s->paper_protect_list; opt->size = maxStringSize (opt->constraint.string_list); if (s->has_MS_df && s->has_paper_protect) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } /*adv_paper_prot*/ if(option==OPT_ADV_PAPER_PROT){ s->adv_paper_prot_list[0] = STRING_DEFAULT; s->adv_paper_prot_list[1] = STRING_OFF; s->adv_paper_prot_list[2] = STRING_ON; s->adv_paper_prot_list[3] = NULL; opt->name = "adv-paper-protect"; opt->title = SANE_I18N ("Advanced paper protection"); opt->desc = SANE_I18N ("Request scanner to predict jams in the ADF using improved sensors"); opt->type = SANE_TYPE_STRING; opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; opt->constraint.string_list = s->adv_paper_prot_list; opt->size = maxStringSize (opt->constraint.string_list); if (s->has_MS_df && s->has_adv_paper_prot) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } /*staple detection*/ if(option==OPT_STAPLE_DETECT){ s->staple_detect_list[0] = STRING_DEFAULT; s->staple_detect_list[1] = STRING_OFF; s->staple_detect_list[2] = STRING_ON; s->staple_detect_list[3] = NULL; opt->name = "staple-detect"; opt->title = SANE_I18N ("Staple detection"); opt->desc = SANE_I18N ("Request scanner to detect jams in the ADF caused by staples"); opt->type = SANE_TYPE_STRING; opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; opt->constraint.string_list = s->staple_detect_list; opt->size = maxStringSize (opt->constraint.string_list); if (s->has_MS_df && s->has_staple_detect) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } /*background color*/ if(option==OPT_BG_COLOR){ s->bg_color_list[0] = STRING_DEFAULT; s->bg_color_list[1] = STRING_WHITE; s->bg_color_list[2] = STRING_BLACK; s->bg_color_list[3] = NULL; opt->name = "bgcolor"; opt->title = SANE_I18N ("Background color"); opt->desc = SANE_I18N ("Set color of background for scans. May conflict with overscan option"); opt->type = SANE_TYPE_STRING; opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; opt->constraint.string_list = s->bg_color_list; opt->size = maxStringSize (opt->constraint.string_list); if (s->has_MS_bg) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } /*dropout color*/ if(option==OPT_DROPOUT_COLOR){ s->do_color_list[0] = STRING_DEFAULT; s->do_color_list[1] = STRING_RED; s->do_color_list[2] = STRING_GREEN; s->do_color_list[3] = STRING_BLUE; s->do_color_list[4] = NULL; opt->name = "dropoutcolor"; opt->title = SANE_I18N ("Dropout color"); opt->desc = SANE_I18N ("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_vuid_3091 || must_downsample(s)){ opt->cap = SANE_CAP_SOFT_SELECT|SANE_CAP_SOFT_DETECT|SANE_CAP_ADVANCED; if(s->u_mode == MODE_COLOR) opt->cap |= SANE_CAP_INACTIVE; } else opt->cap = SANE_CAP_INACTIVE; } /*buffer mode*/ if(option==OPT_BUFF_MODE){ s->buff_mode_list[0] = STRING_DEFAULT; s->buff_mode_list[1] = STRING_OFF; s->buff_mode_list[2] = STRING_ON; s->buff_mode_list[3] = NULL; opt->name = "buffermode"; opt->title = SANE_I18N ("Buffer mode"); opt->desc = SANE_I18N ("Request scanner to read pages quickly from ADF into internal memory"); opt->type = SANE_TYPE_STRING; opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; opt->constraint.string_list = s->buff_mode_list; opt->size = maxStringSize (opt->constraint.string_list); if (s->has_MS_buff) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } /*prepick*/ if(option==OPT_PREPICK){ s->prepick_list[0] = STRING_DEFAULT; s->prepick_list[1] = STRING_OFF; s->prepick_list[2] = STRING_ON; s->prepick_list[3] = NULL; opt->name = "prepick"; opt->title = SANE_I18N ("Prepick"); opt->desc = SANE_I18N ("Request scanner to grab next page from ADF"); opt->type = SANE_TYPE_STRING; opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; opt->constraint.string_list = s->prepick_list; opt->size = maxStringSize (opt->constraint.string_list); if (s->has_MS_prepick) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } /*overscan*/ if(option==OPT_OVERSCAN){ s->overscan_list[0] = STRING_DEFAULT; s->overscan_list[1] = STRING_OFF; s->overscan_list[2] = STRING_ON; s->overscan_list[3] = NULL; opt->name = "overscan"; opt->title = SANE_I18N ("Overscan"); opt->desc = SANE_I18N ("Collect a few mm of background on top side of scan, before paper enters ADF, and increase maximum scan area beyond paper size, to allow collection on remaining sides. May conflict with bgcolor option"); opt->type = SANE_TYPE_STRING; opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; opt->constraint.string_list = s->overscan_list; opt->size = maxStringSize (opt->constraint.string_list); if (s->has_MS_auto && (s->os_x_basic || s->os_y_basic)) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } /*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 = SANE_I18N ("Sleep timer"); opt->desc = SANE_I18N ("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; if(s->has_MS_sleep) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } /*off_time*/ if(option==OPT_OFF_TIME){ s->off_time_range.min = 0; s->off_time_range.max = 960; s->off_time_range.quant = 1; opt->name = "offtimer"; opt->title = SANE_I18N ("Off timer"); opt->desc = SANE_I18N ("Time in minutes until the internal power supply switches the scanner off. Will be rounded to nearest 15 minutes. Zero means never power off."); opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range=&s->off_time_range; if(s->has_off_mode) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } /*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 = SANE_I18N ("Duplex offset"); opt->desc = SANE_I18N ("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(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 = SANE_I18N ("Green offset"); opt->desc = SANE_I18N ("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 = SANE_I18N ("Blue offset"); opt->desc = SANE_I18N ("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_LOW_MEM){ opt->name = "lowmemory"; opt->title = SANE_I18N ("Low Memory"); opt->desc = SANE_I18N ("Limit driver memory usage for use in embedded systems. Causes some duplex transfers to alternate sides on each call to sane_read. Value of option 'side' can be used to determine correct image. This option should only be used with custom front-end software."); opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; opt->size = sizeof(SANE_Word); if (1) opt->cap= SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; opt->constraint_type = SANE_CONSTRAINT_NONE; } if(option==OPT_SIDE){ opt->name = "side"; opt->title = SANE_I18N ("Duplex side"); opt->desc = SANE_I18N ("Tells which side (0=front, 1=back) of a duplex scan the next call to sane_read will return."); opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; opt->size = sizeof(SANE_Word); opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; opt->constraint_type = SANE_CONSTRAINT_NONE; } /*deskew and crop by hardware*/ if(option==OPT_HWDESKEWCROP){ opt->name = "hwdeskewcrop"; opt->title = SANE_I18N ("Hardware deskew and crop"); opt->desc = SANE_I18N ("Request scanner to rotate and crop pages digitally."); opt->type = SANE_TYPE_BOOL; if (s->has_hybrid_crop_deskew) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } /*deskew by software*/ if(option==OPT_SWDESKEW){ opt->name = "swdeskew"; opt->title = SANE_I18N ("Software deskew"); opt->desc = SANE_I18N ("Request driver to rotate skewed pages digitally."); opt->type = SANE_TYPE_BOOL; if (1) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } /*software despeckle radius*/ if(option==OPT_SWDESPECK){ opt->name = "swdespeck"; opt->title = SANE_I18N ("Software despeckle diameter"); opt->desc = SANE_I18N ("Maximum diameter of lone dots to remove from scan."); opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->swdespeck_range; s->swdespeck_range.quant=1; if(1){ s->swdespeck_range.min=0; s->swdespeck_range.max=9; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } else opt->cap = SANE_CAP_INACTIVE; } /*crop by software*/ if(option==OPT_SWCROP){ opt->name = "swcrop"; opt->title = SANE_I18N ("Software crop"); opt->desc = SANE_I18N ("Request driver to remove border from pages digitally."); opt->type = SANE_TYPE_BOOL; if (1) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } /* Software blank page skip */ if(option==OPT_SWSKIP){ opt->name = "swskip"; opt->title = SANE_I18N ("Software blank skip percentage"); opt->desc = SANE_I18N("Request driver to discard pages with low percentage of dark pixels"); opt->type = SANE_TYPE_FIXED; opt->unit = SANE_UNIT_PERCENT; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->swskip_range; s->swskip_range.quant=SANE_FIX(0.10001); s->swskip_range.min=SANE_FIX(0); s->swskip_range.max=SANE_FIX(100); opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } /*halt scanner feeder when cancelling*/ if(option==OPT_HALT_ON_CANCEL){ opt->name = "halt-on-cancel"; opt->title = SANE_I18N ("Halt on Cancel"); opt->desc = SANE_I18N ("Request driver to halt the paper feed instead of eject during a cancel."); opt->type = SANE_TYPE_BOOL; if (s->has_op_halt) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } /* "Endorser" group ------------------------------------------------------ */ if(option==OPT_ENDORSER_GROUP){ opt->name = "endorser-options"; opt->title = SANE_I18N ("Endorser Options"); opt->desc = SANE_I18N ("Controls for endorser unit"); opt->type = SANE_TYPE_GROUP; opt->constraint_type = SANE_CONSTRAINT_NONE; /*flaming hack to get scanimage to hide group*/ if ( !(s->has_endorser_f || s->has_endorser_b) ) opt->type = SANE_TYPE_BOOL; } if(option==OPT_ENDORSER){ opt->name = "endorser"; opt->title = SANE_I18N ("Endorser"); opt->desc = SANE_I18N ("Enable endorser unit"); opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; opt->size = sizeof(SANE_Word); if (s->has_endorser_f || s->has_endorser_b) opt->cap= SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; opt->constraint_type = SANE_CONSTRAINT_NONE; } if(option==OPT_ENDORSER_BITS){ opt->name = "endorser-bits"; opt->title = SANE_I18N ("Endorser bits"); opt->desc = SANE_I18N ("Determines maximum endorser counter value."); opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_NONE; opt->size = sizeof(SANE_Word); /*old type can't do this?*/ if ((s->has_endorser_f && s->endorser_type_f != ET_OLD) || (s->has_endorser_b && s->endorser_type_b != ET_OLD)){ opt->cap=SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; if(!s->u_endorser) opt->cap |= SANE_CAP_INACTIVE; } else opt->cap = SANE_CAP_INACTIVE; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->endorser_bits_range; s->endorser_bits_range.min = 16; s->endorser_bits_range.max = 24; s->endorser_bits_range.quant = 8; } if(option==OPT_ENDORSER_VAL){ opt->name = "endorser-val"; opt->title = SANE_I18N ("Endorser value"); opt->desc = SANE_I18N ("Initial endorser counter value."); opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_NONE; opt->size = sizeof(SANE_Word); if (s->has_endorser_f || s->has_endorser_b){ opt->cap=SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; if(!s->u_endorser) opt->cap |= SANE_CAP_INACTIVE; } else opt->cap = SANE_CAP_INACTIVE; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->endorser_val_range; s->endorser_val_range.min = 0; s->endorser_val_range.max = (1 << s->u_endorser_bits)-1; s->endorser_val_range.quant = 1; } if(option==OPT_ENDORSER_STEP){ opt->name = "endorser-step"; opt->title = SANE_I18N ("Endorser step"); opt->desc = SANE_I18N ("Change endorser counter value by this much for each page."); opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_NONE; opt->size = sizeof(SANE_Word); if (s->has_endorser_f || s->has_endorser_b){ opt->cap=SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; if(!s->u_endorser) opt->cap |= SANE_CAP_INACTIVE; } else opt->cap = SANE_CAP_INACTIVE; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &s->endorser_step_range; s->endorser_step_range.min = -2; s->endorser_step_range.max = 2; s->endorser_step_range.quant = 1; } if(option==OPT_ENDORSER_Y){ opt->name = "endorser-y"; opt->title = SANE_I18N ("Endorser Y"); opt->desc = SANE_I18N ("Endorser print offset from top of paper."); opt->type = SANE_TYPE_FIXED; opt->unit = SANE_UNIT_MM; opt->size = sizeof(SANE_Word); if (s->has_endorser_f || s->has_endorser_b){ opt->cap=SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; if(!s->u_endorser) opt->cap |= SANE_CAP_INACTIVE; } else opt->cap = SANE_CAP_INACTIVE; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &(s->endorser_y_range); /* values stored in 1200 dpi units */ /* must be converted to MM for sane */ s->endorser_y_range.min = SCANNER_UNIT_TO_FIXED_MM(0); s->endorser_y_range.max = SCANNER_UNIT_TO_FIXED_MM(get_page_height(s)); s->endorser_y_range.quant = MM_PER_UNIT_FIX; } if(option==OPT_ENDORSER_FONT){ opt->name = "endorser-font"; opt->title = SANE_I18N ("Endorser font"); opt->desc = SANE_I18N ("Endorser printing font."); opt->type = SANE_TYPE_STRING; opt->unit = SANE_UNIT_NONE; /*only newest can do this?*/ if ((s->has_endorser_f && s->endorser_type_f == ET_40) || (s->has_endorser_b && s->endorser_type_b == ET_40)){ opt->cap=SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; if(!s->u_endorser) opt->cap |= SANE_CAP_INACTIVE; } else opt->cap = SANE_CAP_INACTIVE; opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; opt->constraint.string_list = s->endorser_font_list; s->endorser_font_list[0] = STRING_HORIZONTAL; s->endorser_font_list[1] = STRING_HORIZONTALBOLD; s->endorser_font_list[2] = STRING_HORIZONTALNARROW; s->endorser_font_list[3] = STRING_VERTICAL; s->endorser_font_list[4] = STRING_VERTICALBOLD; s->endorser_font_list[5] = NULL; opt->size = maxStringSize (opt->constraint.string_list); } if(option==OPT_ENDORSER_DIR){ opt->name = "endorser-dir"; opt->title = SANE_I18N ("Endorser direction"); opt->desc = SANE_I18N ("Endorser printing direction."); opt->type = SANE_TYPE_STRING; opt->unit = SANE_UNIT_NONE; if (s->has_endorser_f || s->has_endorser_b){ opt->cap=SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; if(!s->u_endorser) opt->cap |= SANE_CAP_INACTIVE; } else opt->cap = SANE_CAP_INACTIVE; opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; opt->constraint.string_list = s->endorser_dir_list; s->endorser_dir_list[0] = STRING_TOPTOBOTTOM; s->endorser_dir_list[1] = STRING_BOTTOMTOTOP; s->endorser_dir_list[2] = NULL; opt->size = maxStringSize (opt->constraint.string_list); } if(option==OPT_ENDORSER_SIDE){ opt->name = "endorser-side"; opt->title = SANE_I18N ("Endorser side"); opt->desc = SANE_I18N ("Endorser printing side, requires hardware support to change"); opt->type = SANE_TYPE_STRING; opt->unit = SANE_UNIT_NONE; /* only show if both endorsers are installed */ if (s->has_endorser_f && s->has_endorser_b){ opt->cap=SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; if(!s->u_endorser) opt->cap |= SANE_CAP_INACTIVE; } else opt->cap = SANE_CAP_INACTIVE; opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; opt->constraint.string_list = s->endorser_side_list; s->endorser_side_list[0] = STRING_FRONT; s->endorser_side_list[1] = STRING_BACK; s->endorser_side_list[2] = NULL; opt->size = maxStringSize (opt->constraint.string_list); } if(option==OPT_ENDORSER_STRING){ opt->name = "endorser-string"; opt->title = SANE_I18N ("Endorser string"); opt->desc = SANE_I18N ("Endorser alphanumeric print format. %05ud or %08ud at the end will be replaced by counter value."); opt->type = SANE_TYPE_STRING; opt->unit = SANE_UNIT_NONE; opt->size = s->endorser_string_len + 1; if (s->has_endorser_f || s->has_endorser_b){ opt->cap=SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; if(!s->u_endorser) opt->cap |= SANE_CAP_INACTIVE; } else opt->cap = SANE_CAP_INACTIVE; opt->constraint_type = SANE_CONSTRAINT_NONE; } /* "Sensor" group ------------------------------------------------------ */ if(option==OPT_SENSOR_GROUP){ opt->name = SANE_NAME_SENSORS; opt->title = SANE_TITLE_SENSORS; opt->desc = SANE_DESC_SENSORS; opt->type = SANE_TYPE_GROUP; opt->constraint_type = SANE_CONSTRAINT_NONE; } if(option==OPT_TOP){ opt->name = "top-edge"; opt->title = SANE_I18N ("Top edge"); opt->desc = SANE_I18N ("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_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_A3){ opt->name = "a3-paper"; opt->title = SANE_I18N ("A3 paper"); opt->desc = SANE_I18N ("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_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_B4){ opt->name = "b4-paper"; opt->title = SANE_I18N ("B4 paper"); opt->desc = SANE_I18N ("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_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_A4){ opt->name = "a4-paper"; opt->title = SANE_I18N ("A4 paper"); opt->desc = SANE_I18N ("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_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_B5){ opt->name = "b5-paper"; opt->title = SANE_I18N ("B5 paper"); opt->desc = SANE_I18N ("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_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_HOPPER){ opt->name = SANE_NAME_PAGE_LOADED; opt->title = SANE_TITLE_PAGE_LOADED; opt->desc = SANE_DESC_PAGE_LOADED; 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_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_OMR){ opt->name = "omr-df"; opt->title = SANE_I18N ("OMR or DF"); opt->desc = SANE_I18N ("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_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_ADF_OPEN){ opt->name = SANE_NAME_COVER_OPEN; opt->title = SANE_TITLE_COVER_OPEN; opt->desc = SANE_DESC_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_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_CARD_LOADED){ opt->name = "card-loaded"; opt->title = SANE_I18N ("Card loaded"); opt->desc = SANE_I18N ("Card slot contains paper"); opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; if (s->has_cmd_hw_status && s->has_return_path) opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_SLEEP){ opt->name = "power-save"; opt->title = SANE_I18N ("Power saving"); opt->desc = SANE_I18N ("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_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_SEND_SW){ opt->name = SANE_NAME_EMAIL; opt->title = SANE_TITLE_EMAIL; opt->desc = SANE_DESC_EMAIL; 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_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_MANUAL_FEED){ opt->name = "manual-feed"; opt->title = SANE_I18N ("Manual feed"); opt->desc = SANE_I18N ("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_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_SCAN_SW){ opt->name = SANE_NAME_SCAN; opt->title = SANE_TITLE_SCAN; opt->desc = SANE_DESC_SCAN; 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_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_FUNCTION){ opt->name = "function"; opt->title = SANE_I18N ("Function"); opt->desc = SANE_I18N ("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_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_INK_EMPTY){ opt->name = "ink-low"; opt->title = SANE_I18N ("Ink low"); opt->desc = SANE_I18N ("Imprinter ink running low"); opt->type = SANE_TYPE_BOOL; opt->unit = SANE_UNIT_NONE; if (s->has_cmd_hw_status && (s->has_endorser_f || s->has_endorser_b)) opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_DOUBLE_FEED){ opt->name = "double-feed"; opt->title = SANE_I18N ("Double feed"); opt->desc = SANE_I18N ("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_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_ERROR_CODE){ opt->name = "error-code"; opt->title = SANE_I18N ("Error code"); opt->desc = SANE_I18N ("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_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_SKEW_ANGLE){ opt->name = "skew-angle"; opt->title = SANE_I18N ("Skew angle"); opt->desc = SANE_I18N ("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_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_INK_REMAIN){ opt->name = "ink-remain"; opt->title = SANE_I18N ("Ink remaining"); opt->desc = SANE_I18N ("Imprinter ink level"); opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_NONE; if (s->has_cmd_hw_status && (s->has_endorser_f || s->has_endorser_b)) opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_DENSITY_SW){ opt->name = "density"; opt->title = SANE_I18N ("Density"); opt->desc = SANE_I18N ("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_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } if(option==OPT_DUPLEX_SW){ opt->name = "duplex"; opt->title = SANE_I18N ("Duplex switch"); opt->desc = SANE_I18N ("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_HARD_SELECT | SANE_CAP_ADVANCED; else opt->cap = SANE_CAP_INACTIVE; } return opt; } /** * 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 fujitsu *s = (struct fujitsu *) handle; SANE_Int dummy = 0; SANE_Status ret = SANE_STATUS_GOOD; /* Make sure that all those statements involving *info cannot break (better * than having to do "if (info) ..." everywhere!) */ if (info == 0) info = &dummy; /*blast info in case frontend forgot*/ *info = 0; if (option >= NUM_OPTIONS) { DBG (5, "sane_control_option: %d too big\n", option); return SANE_STATUS_INVAL; } if (!SANE_OPTION_IS_ACTIVE (s->opt[option].cap)) { DBG (5, "sane_control_option: %d inactive\n", option); return SANE_STATUS_INVAL; } /* * 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; DBG (20, "sane_control_option: get value for '%s' (%d)\n", s->opt[option].name,option); switch (option) { case OPT_NUM_OPTS: *val_p = NUM_OPTIONS; return SANE_STATUS_GOOD; case OPT_SOURCE: if(s->source == SOURCE_FLATBED){ strcpy (val, STRING_FLATBED); } else if(s->source == SOURCE_ADF_FRONT){ strcpy (val, STRING_ADFFRONT); } else if(s->source == SOURCE_ADF_BACK){ strcpy (val, STRING_ADFBACK); } else if(s->source == SOURCE_ADF_DUPLEX){ strcpy (val, STRING_ADFDUPLEX); } else if(s->source == SOURCE_CARD_FRONT){ strcpy (val, STRING_CARDFRONT); } else if(s->source == SOURCE_CARD_BACK){ strcpy (val, STRING_CARDBACK); } else if(s->source == SOURCE_CARD_DUPLEX){ strcpy (val, STRING_CARDDUPLEX); } return SANE_STATUS_GOOD; case OPT_MODE: if(s->u_mode == MODE_LINEART){ strcpy (val, STRING_LINEART); } else if(s->u_mode == MODE_HALFTONE){ strcpy (val, STRING_HALFTONE); } else if(s->u_mode == MODE_GRAYSCALE){ strcpy (val, STRING_GRAYSCALE); } else if(s->u_mode == MODE_COLOR){ strcpy (val, STRING_COLOR); } return SANE_STATUS_GOOD; case OPT_RES: *val_p = s->resolution_x; return SANE_STATUS_GOOD; case OPT_TL_X: *val_p = SCANNER_UNIT_TO_FIXED_MM(s->tl_x); return SANE_STATUS_GOOD; case OPT_TL_Y: *val_p = SCANNER_UNIT_TO_FIXED_MM(s->tl_y); return SANE_STATUS_GOOD; 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; 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_CONTRAST: *val_p = s->contrast; return SANE_STATUS_GOOD; case OPT_GAMMA: *val_p = SANE_FIX(s->gamma); return SANE_STATUS_GOOD; case OPT_THRESHOLD: *val_p = s->threshold; return SANE_STATUS_GOOD; /* IPC */ case OPT_RIF: *val_p = s->rif; return SANE_STATUS_GOOD; case OPT_HT_TYPE: switch (s->ht_type) { case WD_ht_type_DEFAULT: strcpy (val, STRING_DEFAULT); break; case WD_ht_type_DITHER: strcpy (val, STRING_DITHER); break; case WD_ht_type_DIFFUSION: strcpy (val, STRING_DIFFUSION); break; } return SANE_STATUS_GOOD; case OPT_HT_PATTERN: *val_p = s->ht_pattern; return SANE_STATUS_GOOD; case OPT_OUTLINE: *val_p = s->outline; return SANE_STATUS_GOOD; case OPT_EMPHASIS: *val_p = s->emphasis; return SANE_STATUS_GOOD; case OPT_SEPARATION: *val_p = s->separation; return SANE_STATUS_GOOD; case OPT_MIRRORING: *val_p = s->mirroring; return SANE_STATUS_GOOD; case OPT_WL_FOLLOW: switch (s->wl_follow) { case WD_wl_follow_DEFAULT: strcpy (val, STRING_DEFAULT); break; case WD_wl_follow_ON: strcpy (val, STRING_ON); break; case WD_wl_follow_OFF: strcpy (val, STRING_OFF); break; } return SANE_STATUS_GOOD; /* DTC params*/ case OPT_BP_FILTER: *val_p = s->bp_filter; return SANE_STATUS_GOOD; case OPT_SMOOTHING: *val_p = s->smoothing; return SANE_STATUS_GOOD; case OPT_GAMMA_CURVE: *val_p = s->gamma_curve; return SANE_STATUS_GOOD; case OPT_THRESHOLD_CURVE: *val_p = s->threshold_curve; return SANE_STATUS_GOOD; case OPT_THRESHOLD_WHITE: *val_p = s->threshold_white; return SANE_STATUS_GOOD; case OPT_NOISE_REMOVAL: *val_p = s->noise_removal; return SANE_STATUS_GOOD; case OPT_MATRIX_5: *val_p = s->matrix_5; return SANE_STATUS_GOOD; case OPT_MATRIX_4: *val_p = s->matrix_4; return SANE_STATUS_GOOD; case OPT_MATRIX_3: *val_p = s->matrix_3; return SANE_STATUS_GOOD; case OPT_MATRIX_2: *val_p = s->matrix_2; return SANE_STATUS_GOOD; /* SDTC params*/ case OPT_VARIANCE: *val_p = s->variance; return SANE_STATUS_GOOD; /* Advanced Group */ case OPT_AWD: *val_p = s->awd; return SANE_STATUS_GOOD; case OPT_ALD: *val_p = s->ald; return SANE_STATUS_GOOD; case OPT_COMPRESS: if(s->compress == COMP_JPEG){ strcpy (val, STRING_JPEG); } else{ strcpy (val, STRING_NONE); } return SANE_STATUS_GOOD; case OPT_COMPRESS_ARG: *val_p = s->compress_arg; return SANE_STATUS_GOOD; case OPT_DF_ACTION: switch (s->df_action) { case DF_DEFAULT: strcpy (val, STRING_DEFAULT); break; case DF_CONTINUE: strcpy (val, STRING_CONTINUE); break; case DF_STOP: strcpy (val, STRING_STOP); break; } return SANE_STATUS_GOOD; case OPT_DF_SKEW: *val_p = s->df_skew; return SANE_STATUS_GOOD; case OPT_DF_THICKNESS: *val_p = s->df_thickness; return SANE_STATUS_GOOD; case OPT_DF_LENGTH: *val_p = s->df_length; return SANE_STATUS_GOOD; case OPT_DF_DIFF: switch (s->df_diff) { case MSEL_df_diff_DEFAULT: strcpy (val, STRING_DEFAULT); break; case MSEL_df_diff_10MM: strcpy (val, STRING_10MM); break; case MSEL_df_diff_15MM: strcpy (val, STRING_15MM); break; case MSEL_df_diff_20MM: strcpy (val, STRING_20MM); break; } return SANE_STATUS_GOOD; case OPT_DF_RECOVERY: switch (s->df_recovery) { case MSEL_DEFAULT: strcpy (val, STRING_DEFAULT); break; case MSEL_ON: strcpy (val, STRING_ON); break; case MSEL_OFF: strcpy (val, STRING_OFF); break; } return SANE_STATUS_GOOD; case OPT_PAPER_PROTECT: switch (s->paper_protect) { case MSEL_DEFAULT: strcpy (val, STRING_DEFAULT); break; case MSEL_ON: strcpy (val, STRING_ON); break; case MSEL_OFF: strcpy (val, STRING_OFF); break; } return SANE_STATUS_GOOD; case OPT_ADV_PAPER_PROT: switch (s->adv_paper_prot) { case MSEL_DEFAULT: strcpy (val, STRING_DEFAULT); break; case MSEL_ON: strcpy (val, STRING_ON); break; case MSEL_OFF: strcpy (val, STRING_OFF); break; } return SANE_STATUS_GOOD; case OPT_STAPLE_DETECT: switch (s->staple_detect) { case MSEL_DEFAULT: strcpy (val, STRING_DEFAULT); break; case MSEL_ON: strcpy (val, STRING_ON); break; case MSEL_OFF: strcpy (val, STRING_OFF); break; } return SANE_STATUS_GOOD; case OPT_BG_COLOR: switch (s->bg_color) { case COLOR_DEFAULT: strcpy (val, STRING_DEFAULT); break; case COLOR_WHITE: strcpy (val, STRING_WHITE); break; case COLOR_BLACK: strcpy (val, STRING_BLACK); break; } return SANE_STATUS_GOOD; case OPT_DROPOUT_COLOR: switch (s->dropout_color) { case COLOR_DEFAULT: strcpy (val, STRING_DEFAULT); break; case COLOR_RED: strcpy (val, STRING_RED); break; case COLOR_GREEN: strcpy (val, STRING_GREEN); break; case COLOR_BLUE: strcpy (val, STRING_BLUE); break; } return SANE_STATUS_GOOD; case OPT_BUFF_MODE: switch (s->buff_mode) { case MSEL_DEFAULT: strcpy (val, STRING_DEFAULT); break; case MSEL_ON: strcpy (val, STRING_ON); break; case MSEL_OFF: strcpy (val, STRING_OFF); break; } return SANE_STATUS_GOOD; case OPT_PREPICK: switch (s->prepick) { case MSEL_DEFAULT: strcpy (val, STRING_DEFAULT); break; case MSEL_ON: strcpy (val, STRING_ON); break; case MSEL_OFF: strcpy (val, STRING_OFF); break; } return SANE_STATUS_GOOD; case OPT_OVERSCAN: switch (s->overscan) { case MSEL_DEFAULT: strcpy (val, STRING_DEFAULT); break; case MSEL_ON: strcpy (val, STRING_ON); break; case MSEL_OFF: strcpy (val, STRING_OFF); break; } return SANE_STATUS_GOOD; case OPT_SLEEP_TIME: *val_p = s->sleep_time; return SANE_STATUS_GOOD; case OPT_OFF_TIME: *val_p = s->off_time; return SANE_STATUS_GOOD; case OPT_DUPLEX_OFFSET: *val_p = s->duplex_offset; return SANE_STATUS_GOOD; case OPT_GREEN_OFFSET: *val_p = s->green_offset; return SANE_STATUS_GOOD; case OPT_BLUE_OFFSET: *val_p = s->blue_offset; return SANE_STATUS_GOOD; case OPT_LOW_MEM: *val_p = s->low_mem; return SANE_STATUS_GOOD; case OPT_SIDE: *val_p = s->side; return SANE_STATUS_GOOD; case OPT_HWDESKEWCROP: *val_p = s->hwdeskewcrop; return SANE_STATUS_GOOD; case OPT_SWDESKEW: *val_p = s->swdeskew; return SANE_STATUS_GOOD; case OPT_SWDESPECK: *val_p = s->swdespeck; return SANE_STATUS_GOOD; case OPT_SWCROP: *val_p = s->swcrop; return SANE_STATUS_GOOD; case OPT_SWSKIP: *val_p = SANE_FIX(s->swskip); return SANE_STATUS_GOOD; case OPT_HALT_ON_CANCEL: *val_p = s->halt_on_cancel; return SANE_STATUS_GOOD; /* Endorser Group */ case OPT_ENDORSER: *val_p = s->u_endorser; return SANE_STATUS_GOOD; case OPT_ENDORSER_BITS: *val_p = s->u_endorser_bits; return SANE_STATUS_GOOD; case OPT_ENDORSER_VAL: *val_p = s->u_endorser_val; return SANE_STATUS_GOOD; case OPT_ENDORSER_STEP: *val_p = s->u_endorser_step; return SANE_STATUS_GOOD; case OPT_ENDORSER_Y: *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u_endorser_y); return SANE_STATUS_GOOD; case OPT_ENDORSER_FONT: switch (s->u_endorser_font) { case FONT_H: strcpy (val, STRING_HORIZONTAL); break; case FONT_HB: strcpy (val, STRING_HORIZONTALBOLD); break; case FONT_HN: strcpy (val, STRING_HORIZONTALNARROW); break; case FONT_V: strcpy (val, STRING_VERTICAL); break; case FONT_VB: strcpy (val, STRING_VERTICALBOLD); break; } return SANE_STATUS_GOOD; case OPT_ENDORSER_DIR: switch (s->u_endorser_dir) { case DIR_TTB: strcpy (val, STRING_TOPTOBOTTOM); break; case DIR_BTT: strcpy (val, STRING_BOTTOMTOTOP); break; } return SANE_STATUS_GOOD; case OPT_ENDORSER_SIDE: switch (s->u_endorser_side) { case ED_front: strcpy (val, STRING_FRONT); break; case ED_back: strcpy (val, STRING_BACK); break; } return SANE_STATUS_GOOD; case OPT_ENDORSER_STRING: strncpy( (SANE_String)val, (SANE_String)s->u_endorser_string, s->endorser_string_len+1 ); return SANE_STATUS_GOOD; /* Sensor Group */ case OPT_TOP: ret = get_hardware_status(s,option); *val_p = s->hw_top; return ret; case OPT_A3: ret = get_hardware_status(s,option); *val_p = s->hw_A3; return ret; case OPT_B4: ret = get_hardware_status(s,option); *val_p = s->hw_B4; return ret; case OPT_A4: ret = get_hardware_status(s,option); *val_p = s->hw_A4; return ret; case OPT_B5: ret = get_hardware_status(s,option); *val_p = s->hw_B5; return ret; case OPT_HOPPER: ret = get_hardware_status(s,option); *val_p = s->hw_hopper; return ret; case OPT_OMR: ret = get_hardware_status(s,option); *val_p = s->hw_omr; return ret; case OPT_ADF_OPEN: ret = get_hardware_status(s,option); *val_p = s->hw_adf_open; return ret; case OPT_CARD_LOADED: ret = get_hardware_status(s,option); *val_p = s->hw_card_loaded; return ret; case OPT_SLEEP: ret = get_hardware_status(s,option); *val_p = s->hw_sleep; return ret; case OPT_SEND_SW: ret = get_hardware_status(s,option); *val_p = s->hw_send_sw; return ret; case OPT_MANUAL_FEED: ret = get_hardware_status(s,option); *val_p = s->hw_manual_feed; return ret; case OPT_SCAN_SW: ret = get_hardware_status(s,option); *val_p = s->hw_scan_sw; return ret; case OPT_FUNCTION: ret = get_hardware_status(s,option); *val_p = s->hw_function; return ret; case OPT_INK_EMPTY: ret = get_hardware_status(s,option); *val_p = s->hw_ink_empty; return ret; case OPT_DOUBLE_FEED: ret = get_hardware_status(s,option); *val_p = s->hw_double_feed; return ret; case OPT_ERROR_CODE: ret = get_hardware_status(s,option); *val_p = s->hw_error_code; return ret; case OPT_SKEW_ANGLE: ret = get_hardware_status(s,option); *val_p = s->hw_skew_angle; return ret; case OPT_INK_REMAIN: ret = get_hardware_status(s,option); *val_p = s->hw_ink_remain; return ret; case OPT_DENSITY_SW: ret = get_hardware_status(s,option); *val_p = s->hw_density_sw; return ret; case OPT_DUPLEX_SW: ret = get_hardware_status(s,option); *val_p = s->hw_duplex_sw; return ret; } } 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: can't set, device busy\n"); return SANE_STATUS_DEVICE_BUSY; } 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; } /* may have been changed by constrain, so don't copy until now */ val_c = *(SANE_Word *)val; /* * 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) { /* Mode Group */ case OPT_SOURCE: if (!strcmp (val, STRING_ADFFRONT)) { tmp = SOURCE_ADF_FRONT; } else if (!strcmp (val, STRING_ADFBACK)) { tmp = SOURCE_ADF_BACK; } else if (!strcmp (val, STRING_ADFDUPLEX)) { tmp = SOURCE_ADF_DUPLEX; } else if (!strcmp (val, STRING_CARDFRONT)) { tmp = SOURCE_CARD_FRONT; } else if (!strcmp (val, STRING_CARDBACK)) { tmp = SOURCE_CARD_BACK; } else if (!strcmp (val, STRING_CARDDUPLEX)) { tmp = SOURCE_CARD_DUPLEX; } else{ tmp = SOURCE_FLATBED; } if (s->source == tmp) return SANE_STATUS_GOOD; s->source = tmp; *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_MODE: if (!strcmp (val, STRING_LINEART)) { tmp = MODE_LINEART; } else if (!strcmp (val, STRING_HALFTONE)) { tmp = MODE_HALFTONE; } else if (!strcmp (val, STRING_GRAYSCALE)) { tmp = MODE_GRAYSCALE; } else{ tmp = MODE_COLOR; } if (tmp == s->u_mode) return SANE_STATUS_GOOD; set_mode(s,tmp); *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_RES: if (s->resolution_x == val_c) return SANE_STATUS_GOOD; s->resolution_x = val_c; s->resolution_y = val_c; set_max_y(s); *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; /* Geometry Group */ case OPT_TL_X: if (s->tl_x == FIXED_MM_TO_SCANNER_UNIT(val_c)) return SANE_STATUS_GOOD; s->tl_x = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_TL_Y: if (s->tl_y == FIXED_MM_TO_SCANNER_UNIT(val_c)) return SANE_STATUS_GOOD; s->tl_y = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_BR_X: if (s->br_x == FIXED_MM_TO_SCANNER_UNIT(val_c)) return SANE_STATUS_GOOD; s->br_x = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_BR_Y: if (s->br_y == FIXED_MM_TO_SCANNER_UNIT(val_c)) return SANE_STATUS_GOOD; s->br_y = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_PAGE_WIDTH: if (s->page_width == FIXED_MM_TO_SCANNER_UNIT(val_c)) return SANE_STATUS_GOOD; /* if full width image, and paper size is changed, change the image size to match new paper */ if (s->tl_x == 0 && s->br_x == s->page_width){ DBG (20, "sane_control_option: br_x tracking page_width\n"); s->br_x = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_PARAMS; } s->page_width = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_PAGE_HEIGHT: if (s->page_height == FIXED_MM_TO_SCANNER_UNIT(val_c)) return SANE_STATUS_GOOD; /* if full height image, and paper size is changed, change the image size to match new paper */ if (s->tl_y == 0 && s->br_y == s->page_height){ DBG (20, "sane_control_option: br_y tracking page_height\n"); s->br_y = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_PARAMS; } s->page_height = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; /* Enhancement Group */ case OPT_BRIGHTNESS: s->brightness = val_c; return SANE_STATUS_GOOD; case OPT_CONTRAST: s->contrast = val_c; return SANE_STATUS_GOOD; case OPT_GAMMA: s->gamma = SANE_UNFIX(val_c); return SANE_STATUS_GOOD; case OPT_THRESHOLD: s->threshold = val_c; return SANE_STATUS_GOOD; /* IPC */ case OPT_RIF: s->rif = val_c; return SANE_STATUS_GOOD; case OPT_HT_TYPE: if (!strcmp(val, STRING_DEFAULT)) s->ht_type = WD_ht_type_DEFAULT; else if (!strcmp(val, STRING_DITHER)) s->ht_type = WD_ht_type_DITHER; else if (!strcmp(val, STRING_DIFFUSION)) s->ht_type = WD_ht_type_DIFFUSION; return SANE_STATUS_GOOD; case OPT_HT_PATTERN: s->ht_pattern = val_c; return SANE_STATUS_GOOD; case OPT_OUTLINE: s->outline = val_c; return SANE_STATUS_GOOD; case OPT_EMPHASIS: s->emphasis = val_c; return SANE_STATUS_GOOD; case OPT_SEPARATION: s->separation = val_c; return SANE_STATUS_GOOD; case OPT_MIRRORING: s->mirroring = val_c; return SANE_STATUS_GOOD; case OPT_WL_FOLLOW: if (!strcmp(val, STRING_DEFAULT)) s->wl_follow = WD_wl_follow_DEFAULT; else if (!strcmp(val, STRING_ON)) s->wl_follow = WD_wl_follow_ON; else if (!strcmp(val, STRING_OFF)) s->wl_follow = WD_wl_follow_OFF; return SANE_STATUS_GOOD; /* DTC params*/ case OPT_BP_FILTER: s->bp_filter = val_c; *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_SMOOTHING: s->smoothing = val_c; *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_GAMMA_CURVE: s->gamma_curve = val_c; *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_THRESHOLD_CURVE: s->threshold_curve = val_c; *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_THRESHOLD_WHITE: s->threshold_white = val_c; *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_NOISE_REMOVAL: s->noise_removal = val_c; *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_MATRIX_5: s->matrix_5 = val_c; return SANE_STATUS_GOOD; case OPT_MATRIX_4: s->matrix_4 = val_c; return SANE_STATUS_GOOD; case OPT_MATRIX_3: s->matrix_3 = val_c; return SANE_STATUS_GOOD; case OPT_MATRIX_2: s->matrix_2 = val_c; return SANE_STATUS_GOOD; /* SDTC params*/ case OPT_VARIANCE: s->variance = val_c; *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; /* Advanced Group */ case OPT_AWD: s->awd = val_c; *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_ALD: s->ald = val_c; *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_COMPRESS: if (!strcmp (val, STRING_JPEG)) { tmp = COMP_JPEG; } else{ tmp = COMP_NONE; } if (tmp == s->compress) return SANE_STATUS_GOOD; s->compress = tmp; return SANE_STATUS_GOOD; case OPT_COMPRESS_ARG: s->compress_arg = val_c; return SANE_STATUS_GOOD; case OPT_DF_ACTION: if (!strcmp(val, STRING_DEFAULT)) s->df_action = DF_DEFAULT; else if (!strcmp(val, STRING_CONTINUE)) s->df_action = DF_CONTINUE; else if (!strcmp(val, STRING_STOP)) s->df_action = DF_STOP; *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_DF_SKEW: s->df_skew = val_c; return SANE_STATUS_GOOD; case OPT_DF_THICKNESS: s->df_thickness = val_c; return SANE_STATUS_GOOD; case OPT_DF_LENGTH: s->df_length = val_c; return SANE_STATUS_GOOD; case OPT_DF_DIFF: if (!strcmp(val, STRING_DEFAULT)) s->df_diff = MSEL_df_diff_DEFAULT; else if (!strcmp(val, STRING_10MM)) s->df_diff = MSEL_df_diff_10MM; else if (!strcmp(val, STRING_15MM)) s->df_diff = MSEL_df_diff_15MM; else if (!strcmp(val, STRING_20MM)) s->df_diff = MSEL_df_diff_20MM; return SANE_STATUS_GOOD; case OPT_DF_RECOVERY: if (!strcmp(val, STRING_DEFAULT)) s->df_recovery = MSEL_DEFAULT; else if (!strcmp(val, STRING_ON)) s->df_recovery = MSEL_ON; else if (!strcmp(val, STRING_OFF)) s->df_recovery = MSEL_OFF; return SANE_STATUS_GOOD; case OPT_PAPER_PROTECT: if (!strcmp(val, STRING_DEFAULT)) s->paper_protect = MSEL_DEFAULT; else if (!strcmp(val, STRING_ON)) s->paper_protect = MSEL_ON; else if (!strcmp(val, STRING_OFF)) s->paper_protect = MSEL_OFF; return SANE_STATUS_GOOD; case OPT_ADV_PAPER_PROT: if (!strcmp(val, STRING_DEFAULT)) s->adv_paper_prot = MSEL_DEFAULT; else if (!strcmp(val, STRING_ON)) s->adv_paper_prot = MSEL_ON; else if (!strcmp(val, STRING_OFF)) s->adv_paper_prot = MSEL_OFF; return SANE_STATUS_GOOD; case OPT_STAPLE_DETECT: if (!strcmp(val, STRING_DEFAULT)) s->staple_detect = MSEL_DEFAULT; else if (!strcmp(val, STRING_ON)) s->staple_detect = MSEL_ON; else if (!strcmp(val, STRING_OFF)) s->staple_detect = MSEL_OFF; return SANE_STATUS_GOOD; case OPT_BG_COLOR: if (!strcmp(val, STRING_DEFAULT)) s->bg_color = COLOR_DEFAULT; else if (!strcmp(val, STRING_WHITE)) s->bg_color = COLOR_WHITE; else if (!strcmp(val, STRING_BLACK)) s->bg_color = COLOR_BLACK; return SANE_STATUS_GOOD; case OPT_DROPOUT_COLOR: if (!strcmp(val, STRING_DEFAULT)) s->dropout_color = COLOR_DEFAULT; else if (!strcmp(val, STRING_RED)) s->dropout_color = COLOR_RED; else if (!strcmp(val, STRING_GREEN)) s->dropout_color = COLOR_GREEN; else if (!strcmp(val, STRING_BLUE)) s->dropout_color = COLOR_BLUE; return SANE_STATUS_GOOD; case OPT_BUFF_MODE: if (!strcmp(val, STRING_DEFAULT)) s->buff_mode = MSEL_DEFAULT; else if (!strcmp(val, STRING_ON)) s->buff_mode= MSEL_ON; else if (!strcmp(val, STRING_OFF)) s->buff_mode= MSEL_OFF; return SANE_STATUS_GOOD; case OPT_PREPICK: if (!strcmp(val, STRING_DEFAULT)) s->prepick = MSEL_DEFAULT; else if (!strcmp(val, STRING_ON)) s->prepick = MSEL_ON; else if (!strcmp(val, STRING_OFF)) s->prepick = MSEL_OFF; return SANE_STATUS_GOOD; case OPT_OVERSCAN: if (!strcmp(val, STRING_DEFAULT)) s->overscan = MSEL_DEFAULT; else if (!strcmp(val, STRING_ON)) s->overscan = MSEL_ON; else if (!strcmp(val, STRING_OFF)) s->overscan = MSEL_OFF; *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_SLEEP_TIME: s->sleep_time = val_c; return set_sleep_mode(s); case OPT_OFF_TIME: /* do our own constrain, because we want to round up */ s->off_time = (val_c + 14)/15*15; if(s->off_time != val_c){ *info |= SANE_INFO_INEXACT; } return set_off_mode(s); 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; case OPT_LOW_MEM: s->low_mem = val_c; return SANE_STATUS_GOOD; case OPT_HWDESKEWCROP: s->hwdeskewcrop = val_c; return SANE_STATUS_GOOD; case OPT_SWDESKEW: s->swdeskew = val_c; return SANE_STATUS_GOOD; case OPT_SWDESPECK: s->swdespeck = val_c; return SANE_STATUS_GOOD; case OPT_SWCROP: s->swcrop = val_c; return SANE_STATUS_GOOD; case OPT_SWSKIP: s->swskip = SANE_UNFIX(val_c); return SANE_STATUS_GOOD; case OPT_HALT_ON_CANCEL: s->halt_on_cancel = val_c; return SANE_STATUS_GOOD; /* Endorser Group */ case OPT_ENDORSER: s->u_endorser = val_c; *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_ENDORSER_BITS: s->u_endorser_bits = val_c; return SANE_STATUS_GOOD; /*this val not used in send_endorser*/ case OPT_ENDORSER_VAL: s->u_endorser_val = val_c; return SANE_STATUS_GOOD; case OPT_ENDORSER_STEP: s->u_endorser_step = val_c; return SANE_STATUS_GOOD; case OPT_ENDORSER_Y: s->u_endorser_y = FIXED_MM_TO_SCANNER_UNIT(val_c); return SANE_STATUS_GOOD; case OPT_ENDORSER_FONT: if (!strcmp (val, STRING_HORIZONTAL)){ s->u_endorser_font = FONT_H; } else if (!strcmp (val, STRING_HORIZONTALBOLD)){ s->u_endorser_font = FONT_HB; } else if (!strcmp (val, STRING_HORIZONTALNARROW)){ s->u_endorser_font = FONT_HN; } else if (!strcmp (val, STRING_VERTICAL)){ s->u_endorser_font = FONT_V; } else if (!strcmp (val, STRING_VERTICALBOLD)){ s->u_endorser_font = FONT_VB; } return SANE_STATUS_GOOD; case OPT_ENDORSER_DIR: if (!strcmp (val, STRING_TOPTOBOTTOM)){ s->u_endorser_dir = DIR_TTB; } else if (!strcmp (val, STRING_BOTTOMTOTOP)){ s->u_endorser_dir = DIR_BTT; } return SANE_STATUS_GOOD; /*this val not used in send_endorser*/ case OPT_ENDORSER_SIDE: if (!strcmp (val, STRING_FRONT)){ s->u_endorser_side = ED_front; } else if (!strcmp (val, STRING_BACK)){ s->u_endorser_side = ED_back; } return SANE_STATUS_GOOD; case OPT_ENDORSER_STRING: strncpy( (SANE_String)s->u_endorser_string, (SANE_String)val, s->endorser_string_len+1 ); return SANE_STATUS_GOOD; } /* switch */ } /* else */ return SANE_STATUS_INVAL; } static SANE_Status set_sleep_mode(struct fujitsu *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[MODE_SELECT_len]; size_t cmdLen = MODE_SELECT_len; unsigned char out[MSEL_header_len + MSEL_data_min_len]; size_t outLen = MSEL_header_len + MSEL_data_min_len; unsigned char * page = out+MSEL_header_len; DBG (10, "set_sleep_mode: start\n"); memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, MODE_SELECT_code); set_MSEL_pf(cmd, 1); set_MSEL_xferlen(cmd, outLen); memset(out,0,outLen); set_MSEL_pc(page, MS_pc_sleep); set_MSEL_page_len(page, MSEL_data_min_len-2); set_MSEL_sleep_mode(page, s->sleep_time); ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); DBG (10, "set_sleep_mode: finish\n"); return ret; } static SANE_Status set_off_mode(struct fujitsu *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[SEND_DIAGNOSTIC_len]; /*also big enough for READ_DIAG*/ size_t cmdLen = SEND_DIAGNOSTIC_len; unsigned char out[SD_powoff_len]; size_t outLen = SD_powoff_len; DBG (10, "set_off_mode: start\n"); if (!s->has_cmd_sdiag || !s->has_cmd_rdiag || !s->has_off_mode){ DBG (5, "set_off_mode: not supported, returning\n"); return ret; } memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SEND_DIAGNOSTIC_code); set_SD_slftst(cmd, 0); set_SD_xferlen(cmd, outLen); memcpy(out,SD_powoff_string,SD_powoff_stringlen); set_SD_powoff_disable(out,!s->off_time); set_SD_powoff_interval(out,s->off_time/15); ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); if (ret != SANE_STATUS_GOOD){ DBG (5, "set_off_mode: send diag error: %d\n", ret); return ret; } DBG (10, "set_off_mode: finish\n"); return SANE_STATUS_GOOD; } static SANE_Status get_hardware_status (struct fujitsu *s, SANE_Int option) { SANE_Status ret = SANE_STATUS_GOOD; DBG (10, "get_hardware_status: start\n"); /* only run this if frontend has already read the last time we got it */ /* or if we don't care for such bookkeeping (private use) */ if (!option || !s->hw_data_avail[option-OPT_TOP]) { DBG (15, "get_hardware_status: running\n"); /* mark all values as available */ memset(s->hw_data_avail,1,sizeof(s->hw_data_avail)); if (s->has_cmd_hw_status){ unsigned char cmd[GET_HW_STATUS_len]; size_t cmdLen = GET_HW_STATUS_len; unsigned char in[GHS_data_len]; size_t inLen = GHS_data_len; memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, GET_HW_STATUS_code); set_GHS_allocation_length(cmd, inLen); DBG (15, "get_hardware_status: calling ghs\n"); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if (ret == SANE_STATUS_GOOD || ret == SANE_STATUS_EOF) { s->hw_top = get_GHS_top(in); s->hw_A3 = get_GHS_A3(in); s->hw_B4 = get_GHS_B4(in); s->hw_A4 = get_GHS_A4(in); s->hw_B5 = get_GHS_B5(in); s->hw_hopper = get_GHS_hopper(in); s->hw_omr = get_GHS_omr(in); s->hw_adf_open = get_GHS_adf_open(in); s->hw_card_loaded = get_GHS_exit(in); s->hw_sleep = get_GHS_sleep(in); s->hw_send_sw = get_GHS_send_sw(in); s->hw_manual_feed = get_GHS_manual_feed(in); s->hw_scan_sw = get_GHS_scan_sw(in); s->hw_function = get_GHS_function(in); s->hw_ink_empty = get_GHS_ink_empty(in); s->hw_double_feed = get_GHS_double_feed(in); s->hw_error_code = get_GHS_error_code(in); s->hw_skew_angle = get_GHS_skew_angle(in); if(inLen > 9){ s->hw_ink_remain = get_GHS_ink_remain(in); } ret = SANE_STATUS_GOOD; } } /* 3091/2 put hardware status in RS data */ else if (s->ghs_in_rs){ unsigned char cmd[REQUEST_SENSE_len]; size_t cmdLen = REQUEST_SENSE_len; unsigned char in[RS_return_size]; size_t inLen = RS_return_size; memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, REQUEST_SENSE_code); set_RS_return_size(cmd, inLen); DBG(15,"get_hardware_status: calling rs\n"); ret = do_cmd( s,0,0, cmd, cmdLen, NULL,0, in, &inLen ); /* parse the rs data */ if(ret == SANE_STATUS_GOOD){ if(get_RS_sense_key(in)==0 && get_RS_ASC(in)==0x80){ s->hw_adf_open = get_RS_adf_open(in); s->hw_send_sw = get_RS_send_sw(in); s->hw_scan_sw = get_RS_scan_sw(in); s->hw_duplex_sw = get_RS_duplex_sw(in); s->hw_top = get_RS_top(in); s->hw_hopper = get_RS_hopper(in); s->hw_function = get_RS_function(in); s->hw_density_sw = get_RS_density(in); } else{ DBG (10, "get_hardware_status: unexpected RS values\n"); } } } } if(option) s->hw_data_avail[option-OPT_TOP] = 0; DBG (10, "get_hardware_status: finish\n"); return ret; } static SANE_Status send_endorser(struct fujitsu *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[SEND_len]; size_t cmdLen = SEND_len; size_t strLen = strlen(s->u_endorser_string); unsigned char out[S_e_data_max_len]; /*we probably send less below*/ size_t outLen = S_e_data_min_len + strLen; /*fi-5900 might want 1 more byte?*/ DBG (10, "send_endorser: start\n"); if (!s->has_endorser_f && !s->has_endorser_b){ DBG (10, "send_endorser: unsupported\n"); return ret; } /*build the payload*/ memset(out,0,outLen); /*fi-5900 front side uses 0x80, assume all others*/ if(s->u_endorser_side == ED_front){ set_S_endorser_data_id(out,0x80); } else{ set_S_endorser_data_id(out,0); } set_S_endorser_stamp(out,0); set_S_endorser_elec(out,0); if(s->u_endorser_step < 0){ set_S_endorser_decr(out,S_e_decr_dec); } else{ set_S_endorser_decr(out,S_e_decr_inc); } if(s->u_endorser_bits == 24){ set_S_endorser_lap24(out,S_e_lap_24bit); } else{ set_S_endorser_lap24(out,S_e_lap_16bit); } set_S_endorser_ctstep(out,abs(s->u_endorser_step)); set_S_endorser_ulx(out,0); set_S_endorser_uly(out,s->u_endorser_y); switch (s->u_endorser_font) { case FONT_H: set_S_endorser_font(out,S_e_font_horiz); set_S_endorser_bold(out,0); break; case FONT_HB: set_S_endorser_font(out,S_e_font_horiz); set_S_endorser_bold(out,1); break; case FONT_HN: set_S_endorser_font(out,S_e_font_horiz_narrow); set_S_endorser_bold(out,0); break; case FONT_V: set_S_endorser_font(out,S_e_font_vert); set_S_endorser_bold(out,0); break; case FONT_VB: set_S_endorser_font(out,S_e_font_vert); set_S_endorser_bold(out,1); break; } set_S_endorser_size(out,0); set_S_endorser_revs(out,0); if(s->u_endorser_dir == DIR_BTT){ set_S_endorser_dirs(out,S_e_dir_bottom_top); } else{ set_S_endorser_dirs(out,S_e_dir_top_bottom); } set_S_endorser_string_length(out, strLen); set_S_endorser_string(out, s->u_endorser_string, strLen); /*build the command*/ memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SEND_code); set_S_xfer_datatype (cmd, S_datatype_endorser_data); set_S_xfer_length (cmd, outLen); ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); DBG (10, "send_endorser: finish %d\n", ret); return ret; } /* instead of internal brightness/contrast/gamma most scanners use a 256x256 or 1024x256 LUT default is linear table of slope 1 or 1/4 resp. brightness and contrast inputs are -127 to +127 contrast rotates slope of line around central input val high low . x . . x . xx out . x . xxxxxxxx . x xx ....x....... ............ in in then brightness moves line vertically, and clamps to 8bit bright dark . xxxxxxxx . . x . out x . x . . x ............ xxxxxxxx.... in in */ static SANE_Status send_lut (struct fujitsu *s) { int i, j, bytes = 1 << s->adbits; double b, slope, offset; SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[SEND_len]; size_t cmdLen = SEND_len; unsigned char out[S_lut_header_len + S_lut_data_max_len]; size_t outLen = S_lut_header_len + S_lut_data_max_len; unsigned char * p = out + S_lut_header_len; DBG (10, "send_lut: start\n"); if(!s->num_download_gamma || !s->adbits){ DBG (10, "send_lut: unsupported\n"); return ret; } /* contrast is converted to a slope [0,90] degrees: * first [-127,127] to [0,254] then to [0,1] * then multiply by PI/2 to convert to radians * then take the tangent to get slope (T.O.A) * then multiply by the normal linear slope * because the table may not be square, i.e. 1024x256*/ slope = tan(((double)s->contrast+127)/254 * M_PI/2) * 256/bytes; /* contrast slope must stay centered, so figure * out vertical offset at central input value */ offset = 127.5-(slope*bytes/2); /* convert the user brightness setting (-127 to +127) * into a scale that covers the range required * to slide the contrast curve entirely off the table */ b = ((double)s->brightness/127) * (256 - offset); DBG (15, "send_lut: %d %f %d %f %f\n", s->brightness, b, s->contrast, slope, offset); outLen = S_lut_header_len + bytes; memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SEND_code); set_S_xfer_datatype (cmd, S_datatype_lut_data); set_S_xfer_length (cmd, outLen); memset(out,0,outLen); set_S_lut_order (out, S_lut_order_single); set_S_lut_ssize (out, bytes); set_S_lut_dsize (out, 256); for(i=0;i255){ j=255; } *p=j; p++; } ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); DBG (10, "send_lut: finish\n"); return ret; } static SANE_Status send_q_table (struct fujitsu *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[SEND_len]; size_t cmdLen = SEND_len; unsigned char out[S_q_table_header_len + S_q_table_y_len + S_q_table_uv_len]; size_t outLen = S_q_table_header_len + S_q_table_y_len + S_q_table_uv_len; unsigned char * yp = out + S_q_table_header_len; unsigned char * uvp = out + S_q_table_header_len + S_q_table_y_len; /* FIXME: generate these instead of hardcode */ unsigned char ydata[] = { 0x04, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x04, 0x03, 0x04, 0x05, 0x05, 0x04, 0x05, 0x07, 0x0c, 0x07, 0x07, 0x06, 0x06, 0x07, 0x0e, 0x0a, 0x0b, 0x08, 0x0c, 0x11, 0x0f, 0x12, 0x12, 0x11, 0x0f, 0x10, 0x10, 0x13, 0x15, 0x1b, 0x17, 0x13, 0x14, 0x1a, 0x14, 0x10, 0x10, 0x18, 0x20, 0x18, 0x1a, 0x1c, 0x1d, 0x1e, 0x1f, 0x1e, 0x12, 0x17, 0x21, 0x24, 0x21, 0x1e, 0x24, 0x1b, 0x1e, 0x1e, 0x1d }; unsigned char uvdata[] = { 0x05, 0x05, 0x05, 0x07, 0x06, 0x07, 0x0e, 0x07, 0x07, 0x0e, 0x1d, 0x13, 0x10, 0x13, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d }; DBG (10, "send_q_table: start\n"); memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SEND_code); set_S_xfer_datatype (cmd, S_datatype_jpg_q_table); set_S_xfer_length (cmd, outLen); memset(out,0,outLen); set_S_q_table_y_len (out, S_q_table_y_len); set_S_q_table_uv_len (out, S_q_table_uv_len); memcpy (yp, ydata, S_q_table_y_len); memcpy (uvp, uvdata, S_q_table_uv_len); ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); DBG (10, "send_q_table: finish\n"); return ret; } /* only used by iX500? */ #if 0 static SANE_Status mode_select_unk (struct fujitsu *s, int foo) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[MODE_SELECT_len]; size_t cmdLen = MODE_SELECT_len; unsigned char out[MSEL_header_len + MSEL_data_min_len]; size_t outLen = MSEL_header_len + MSEL_data_min_len; unsigned char * page = out+MSEL_header_len; DBG (10, "mode_select_unk: start\n"); /*if (!s->has_MS_unk){ DBG (10, "mode_select_unk: unsupported\n"); return SANE_STATUS_GOOD; }*/ memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, MODE_SELECT_code); set_MSEL_pf(cmd, 1); set_MSEL_xferlen(cmd, outLen); memset(out,0,outLen); set_MSEL_pc(page, MS_pc_unk); set_MSEL_page_len(page, MSEL_data_min_len-2); *(page + 0x02) = foo; ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); DBG (10, "mode_select_unk: finish\n"); return ret; } #endif /* only used by iX500? */ static SANE_Status diag_preread (struct fujitsu *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[SEND_DIAGNOSTIC_len]; /*also big enough for READ_DIAG*/ size_t cmdLen = SEND_DIAGNOSTIC_len; unsigned char out[SD_preread_len]; size_t outLen = SD_preread_len; DBG (10, "diag_preread: start\n"); if (!s->has_cmd_sdiag || !s->has_cmd_rdiag || !s->need_diag_preread){ DBG (5, "diag_preread: not supported, returning\n"); return ret; } memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SEND_DIAGNOSTIC_code); set_SD_slftst(cmd, 0); set_SD_xferlen(cmd, outLen); memcpy(out,SD_preread_string,SD_preread_stringlen); set_SD_preread_xres(out,s->resolution_x); set_SD_preread_yres(out,s->resolution_y); /* call helper function, scanner wants lies about paper width */ set_SD_preread_paper_width(out, get_page_width(s)); /* don't call helper function, scanner wants actual length? */ set_SD_preread_paper_length(out, s->page_height); set_SD_preread_composition(out, s->s_mode); ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); if (ret != SANE_STATUS_GOOD){ DBG (5, "diag_preread: send diag error: %d\n", ret); return ret; } DBG (10, "diag_preread: finish\n"); return SANE_STATUS_GOOD; } static SANE_Status mode_select_df (struct fujitsu *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[MODE_SELECT_len]; size_t cmdLen = MODE_SELECT_len; unsigned char out[MSEL_header_len + MSEL_data_min_len]; size_t outLen = MSEL_header_len + MSEL_data_min_len; unsigned char * page = out+MSEL_header_len; DBG (10, "mode_select_df: start\n"); if(!s->has_MS_df){ DBG (10, "mode_select_df: unsupported\n"); return SANE_STATUS_GOOD; } memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, MODE_SELECT_code); set_MSEL_pf(cmd, 1); set_MSEL_xferlen(cmd, outLen); memset(out,0,outLen); set_MSEL_pc(page, MS_pc_df); set_MSEL_page_len(page, MSEL_data_min_len-2); /* continue/stop */ if(s->df_action != DF_DEFAULT){ set_MSEL_df_enable (page, 1); /* continue */ if(s->df_action == DF_CONTINUE){ set_MSEL_df_continue (page, 1); } /* skew */ if(s->df_skew){ set_MSEL_df_skew (page, 1); } /* thickness */ if(s->df_thickness){ set_MSEL_df_thickness (page, 1); } /* length */ if(s->df_length){ set_MSEL_df_length (page, 1); set_MSEL_df_diff (page, s->df_diff); } } set_MSEL_df_paperprot(page,s->paper_protect); set_MSEL_df_stapledet(page,s->staple_detect); set_MSEL_df_recovery(page,s->df_recovery); set_MSEL_df_paperprot2(page,s->adv_paper_prot); ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); DBG (10, "mode_select_df: finish\n"); return ret; } static SANE_Status mode_select_bg (struct fujitsu *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[MODE_SELECT_len]; size_t cmdLen = MODE_SELECT_len; unsigned char out[MSEL_header_len + MSEL_data_min_len]; size_t outLen = MSEL_header_len + MSEL_data_min_len; unsigned char * page = out+MSEL_header_len; DBG (10, "mode_select_bg: start\n"); if(!s->has_MS_bg){ DBG (10, "mode_select_bg: unsupported\n"); return SANE_STATUS_GOOD; } memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, MODE_SELECT_code); set_MSEL_pf(cmd, 1); set_MSEL_xferlen(cmd, outLen); memset(out,0,outLen); set_MSEL_pc(page, MS_pc_bg); set_MSEL_page_len(page, MSEL_data_min_len-2); if(s->bg_color != COLOR_DEFAULT){ set_MSEL_bg_enable (page, 1); if(s->bg_color == COLOR_BLACK){ set_MSEL_bg_front (page, 1); set_MSEL_bg_back (page, 1); set_MSEL_bg_fb (page, 1); } } ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); DBG (10, "mode_select_bg: finish\n"); return ret; } static SANE_Status mode_select_dropout (struct fujitsu *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[MODE_SELECT_len]; size_t cmdLen = MODE_SELECT_len; unsigned char out[MSEL_header_len + MSEL_data_max_len]; size_t outLen = MSEL_header_len + MSEL_data_max_len; unsigned char * page = out+MSEL_header_len; DBG (10, "mode_select_dropout: start\n"); if(!s->has_MS_dropout){ DBG (10, "mode_select_dropout: unsupported\n"); return SANE_STATUS_GOOD; } memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, MODE_SELECT_code); set_MSEL_pf(cmd, 1); set_MSEL_xferlen(cmd, outLen); memset(out,0,outLen); set_MSEL_pc(page, MS_pc_dropout); set_MSEL_page_len(page, MSEL_data_max_len-2); set_MSEL_dropout_front (page, s->dropout_color); set_MSEL_dropout_back (page, s->dropout_color); ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); DBG (10, "mode_select_dropout: finish\n"); return ret; } static SANE_Status mode_select_buff (struct fujitsu *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[MODE_SELECT_len]; size_t cmdLen = MODE_SELECT_len; unsigned char out[MSEL_header_len + MSEL_data_min_len]; size_t outLen = MSEL_header_len + MSEL_data_min_len; unsigned char * page = out+MSEL_header_len; DBG (10, "mode_select_buff: start\n"); if (!s->has_MS_buff){ DBG (10, "mode_select_buff: unsupported\n"); return SANE_STATUS_GOOD; } memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, MODE_SELECT_code); set_MSEL_pf(cmd, 1); set_MSEL_xferlen(cmd, outLen); memset(out,0,outLen); set_MSEL_pc(page, MS_pc_buff); set_MSEL_page_len(page, MSEL_data_min_len-2); set_MSEL_buff_mode(page, s->buff_mode); set_MSEL_buff_clear(page, 3); ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); DBG (10, "mode_select_buff: finish\n"); return ret; } static SANE_Status mode_select_prepick (struct fujitsu *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[MODE_SELECT_len]; size_t cmdLen = MODE_SELECT_len; unsigned char out[MSEL_header_len + MSEL_data_min_len]; size_t outLen = MSEL_header_len + MSEL_data_min_len; unsigned char * page = out+MSEL_header_len; DBG (10, "mode_select_prepick: start\n"); if (!s->has_MS_prepick){ DBG (10, "mode_select_prepick: unsupported\n"); return SANE_STATUS_GOOD; } memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, MODE_SELECT_code); set_MSEL_pf(cmd, 1); set_MSEL_xferlen(cmd, outLen); memset(out,0,outLen); set_MSEL_pc(page, MS_pc_prepick); set_MSEL_page_len(page, MSEL_data_min_len-2); set_MSEL_prepick(page, s->prepick); ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); DBG (10, "mode_select_prepick: finish\n"); return ret; } static SANE_Status mode_select_auto (struct fujitsu *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[MODE_SELECT_len]; size_t cmdLen = MODE_SELECT_len; unsigned char out[MSEL_header_len + MSEL_data_min_len]; size_t outLen = MSEL_header_len + MSEL_data_min_len; unsigned char * page = out+MSEL_header_len; DBG (10, "mode_select_auto: start\n"); if(!s->has_MS_auto){ DBG (10, "mode_select_auto: unsupported\n"); return SANE_STATUS_GOOD; } memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, MODE_SELECT_code); set_MSEL_pf(cmd, 1); set_MSEL_xferlen(cmd, outLen); memset(out,0,outLen); set_MSEL_pc(page, MS_pc_auto); set_MSEL_page_len(page, MSEL_data_min_len-2); set_MSEL_overscan(page, s->overscan); set_MSEL_ald(page, s->ald || s->hwdeskewcrop); set_MSEL_awd(page, s->awd || s->hwdeskewcrop); set_MSEL_req_driv_crop(page, s->hwdeskewcrop && (s->swcrop || s->swdeskew)); set_MSEL_deskew(page, s->hwdeskewcrop); ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); DBG (10, "mode_select_auto: finish\n"); return ret; } /* * @@ 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) { SANE_Status ret = SANE_STATUS_GOOD; struct fujitsu *s = (struct fujitsu *) handle; DBG (10, "sane_get_parameters: start\n"); /* not started? update param data from user settings */ if(!s->started){ ret = update_params(s); if(ret) return ret; } params->format = s->u_params.format; params->last_frame = s->u_params.last_frame; params->lines = s->u_params.lines; params->depth = s->u_params.depth; params->pixels_per_line = s->u_params.pixels_per_line; params->bytes_per_line = s->u_params.bytes_per_line; /* we won't know the end until we get to it */ if(s->ald && !must_fully_buffer(s)){ DBG (15, "sane_get_parameters: hand-scanner mode\n"); params->lines = -1; } DBG (10, "sane_get_parameters: finish\n"); return ret; } /* set s_params and u_params data based on user settings * and scanner capabilities. */ SANE_Status update_params (struct fujitsu * s) { SANE_Status ret = SANE_STATUS_GOOD; SANE_Parameters * params = &(s->s_params); DBG (10, "update_params: start\n"); /* first, we setup s_params to describe the image to the scanner */ /* this backend only sends single frame images */ params->last_frame = 1; /* initial ppl from user settings */ params->pixels_per_line = s->resolution_x * (s->br_x - s->tl_x) / 1200; /* some scanners require even number of bytes in each transfer block, * so we round to even # of total lines, to ensure last block is even */ params->lines = s->resolution_y * (s->br_y - s->tl_y) / 1200; params->lines -= params->lines % 2; if (s->s_mode == MODE_COLOR) { params->depth = 8; /* jpeg requires 8x8 squares */ if(s->compress == COMP_JPEG){ params->format = SANE_FRAME_JPEG; params->pixels_per_line -= params->pixels_per_line % 8; params->lines -= params->lines % 8; } else{ params->format = SANE_FRAME_RGB; params->pixels_per_line -= params->pixels_per_line % max(s->ppl_mod_by_mode[s->s_mode], s->ppl_mod_by_mode[s->u_mode]); } params->bytes_per_line = params->pixels_per_line * 3; } else if (s->s_mode == MODE_GRAYSCALE) { params->depth = 8; /* jpeg requires 8x8 squares */ if(s->compress == COMP_JPEG){ params->format = SANE_FRAME_JPEG; params->pixels_per_line -= params->pixels_per_line % 8; params->lines -= params->lines % 8; } else{ params->format = SANE_FRAME_GRAY; params->pixels_per_line -= params->pixels_per_line % max(s->ppl_mod_by_mode[s->s_mode], s->ppl_mod_by_mode[s->u_mode]); } params->bytes_per_line = params->pixels_per_line; } else { params->depth = 1; params->format = SANE_FRAME_GRAY; params->pixels_per_line -= params->pixels_per_line % max(s->ppl_mod_by_mode[s->s_mode], s->ppl_mod_by_mode[s->u_mode]); params->bytes_per_line = params->pixels_per_line / 8; } DBG(15,"update_params: x: max=%d, page=%d, gpw=%d, res=%d\n", s->max_x, s->page_width, get_page_width(s), s->resolution_x); DBG(15,"update_params: y: max=%d, page=%d, gph=%d, res=%d\n", s->max_y, s->page_height, get_page_height(s), s->resolution_y); DBG(15,"update_params: area: tlx=%d, brx=%d, tly=%d, bry=%d\n", s->tl_x, s->br_x, s->tl_y, s->br_y); DBG(15,"update_params: params: ppl=%d, Bpl=%d, lines=%d\n", params->pixels_per_line, params->bytes_per_line, params->lines); DBG(15,"update_params: params: format=%d, depth=%d, last=%d\n", params->format, params->depth, params->last_frame); /* second, we setup u_params to describe the image to the user */ /* use a helper function cause it is called elsewhere */ ret = update_u_params(s); DBG (10, "update_params: finish\n"); return ret; } /* set u_param data based on user settings, and s_params */ SANE_Status update_u_params (struct fujitsu * s) { SANE_Status ret = SANE_STATUS_GOOD; SANE_Parameters * params = &(s->u_params); DBG (10, "update_u_params: start\n"); /* for most machines, it is the same, so we just copy */ memcpy(&(s->u_params), &(s->s_params), sizeof(SANE_Parameters)); /* some scanners don't support the user's mode, so params differ */ /* but not in jpeg mode. we don't support that. */ if(must_downsample(s)){ /* making gray from a color scan */ if (s->u_mode == MODE_GRAYSCALE) { params->format = SANE_FRAME_GRAY; params->bytes_per_line = params->pixels_per_line; } /* making binary from a gray or color scan */ else if (s->u_mode == MODE_LINEART) { params->depth = 1; params->format = SANE_FRAME_GRAY; params->bytes_per_line = params->pixels_per_line / 8; } DBG(15,"update_u_params: x: max=%d, page=%d, gpw=%d, res=%d\n", s->max_x, s->page_width, get_page_width(s), s->resolution_x); DBG(15,"update_u_params: y: max=%d, page=%d, gph=%d, res=%d\n", s->max_y, s->page_height, get_page_height(s), s->resolution_y); DBG(15,"update_u_params: area: tlx=%d, brx=%d, tly=%d, bry=%d\n", s->tl_x, s->br_x, s->tl_y, s->br_y); DBG(15,"update_u_params: params: ppl=%d, Bpl=%d, lines=%d\n", params->pixels_per_line, params->bytes_per_line, params->lines); DBG(15,"update_u_params: params: format=%d, depth=%d, last=%d\n", params->format, params->depth, params->last_frame); } DBG (10, "update_u_params: finish\n"); return ret; } /* * 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 will be called between sides of a duplex scan, * and at the start of each page of an adf batch. * hence, we spend a lot of time playing with s->started, etc. */ SANE_Status sane_start (SANE_Handle handle) { struct fujitsu *s = handle; SANE_Status ret = SANE_STATUS_GOOD; DBG (10, "sane_start: start\n"); DBG (15, "started=%d, side=%d, source=%d\n", s->started, s->side, s->source); /* undo any prior sane_cancel calls */ s->cancelled=0; /* protect this block from sane_cancel */ s->reading=1; /* not finished with current side, error */ if (s->started && !s->eof_tx[s->side]) { DBG(5,"sane_start: previous transfer not finished?"); ret = SANE_STATUS_INVAL; goto errors; } /* low mem mode messes up the side marker, reset it */ if((s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_CARD_DUPLEX) && s->low_mem && s->eof_tx[SIDE_FRONT] && s->eof_tx[SIDE_BACK] ){ s->side = SIDE_BACK; } /* batch start? initialize struct and scanner */ if(!s->started){ /* load side marker */ if(s->source == SOURCE_ADF_BACK || s->source == SOURCE_CARD_BACK){ s->side = SIDE_BACK; } else{ s->side = SIDE_FRONT; } /* load our own private copy of scan params */ ret = update_params(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot update params\n"); goto errors; } /* switch source */ if(s->source == SOURCE_FLATBED){ ret = scanner_control(s, SC_function_fb); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot control fb, ignoring\n"); } } else if(s->source == SOURCE_CARD_FRONT || s->source == SOURCE_CARD_BACK || s->source == SOURCE_CARD_DUPLEX){ ret = scanner_control(s, SC_function_rpath); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot control rp, ignoring\n"); } } else{ ret = scanner_control(s, SC_function_adf); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot control ADF, ignoring\n"); } } /* required for hi res scans on iX500? */ ret = diag_preread(s); if (ret != SANE_STATUS_GOOD) DBG (5, "sane_start: WARNING: cannot diag_preread %d\n", ret); /* enable overscan/auto detection */ ret = mode_select_auto(s); if (ret != SANE_STATUS_GOOD) DBG (5, "sane_start: WARNING: cannot mode_select_auto %d\n", ret); /* enable double feed detection */ ret = mode_select_df(s); if (ret != SANE_STATUS_GOOD) DBG (5, "sane_start: WARNING: cannot mode_select_df %d\n", ret); /* enable background color setting */ ret = mode_select_bg(s); if (ret != SANE_STATUS_GOOD) DBG (5, "sane_start: WARNING: cannot mode_select_bg %d\n", ret); /* enable dropout color setting */ ret = mode_select_dropout(s); if (ret != SANE_STATUS_GOOD) DBG (5, "sane_start: WARNING: cannot mode_select_dropout %d\n", ret); /* enable buffering setting */ ret = mode_select_buff(s); if (ret != SANE_STATUS_GOOD) DBG (5, "sane_start: WARNING: cannot mode_select_buff %d\n", ret); /* enable prepick setting */ ret = mode_select_prepick(s); if (ret != SANE_STATUS_GOOD) DBG (5, "sane_start: WARNING: cannot mode_select_prepick %d\n", ret); /* send endorser config */ ret = send_endorser(s); if (ret != SANE_STATUS_GOOD) DBG (5, "sane_start: WARNING: cannot send_endorser %d\n", ret); /* set window command */ ret = set_window(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot set window\n"); goto errors; } /* send lut if set_window said we would */ if ( s->window_gamma ){ ret = send_lut(s); if (ret != SANE_STATUS_GOOD) DBG (5, "sane_start: WARNING: cannot send_lut %d\n", ret); } /* some scanners need the q table sent, even when not scanning jpeg */ if (s->need_q_table){ ret = send_q_table(s); if (ret != SANE_STATUS_GOOD) DBG (5, "sane_start: WARNING: cannot send_q_table %d\n", ret); } /* start/stop endorser */ ret = endorser(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot start/stop endorser\n"); goto errors; } /* turn lamp on */ ret = scanner_control(s, SC_function_lamp_on); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: WARNING: cannot start lamp, ignoring\n"); } /* iX500 errors if op is called with no paper * at the beginning of a batch, so we check */ if(s->hopper_before_op && s->source != SOURCE_FLATBED){ ret = get_hardware_status(s,0); if(!s->hw_hopper){ ret = SANE_STATUS_NO_DOCS; DBG (5, "sane_start: ERROR: hopper empty\n"); goto errors; } } } /* if already running, duplex needs to switch sides */ else if(s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_CARD_DUPLEX){ s->side = !s->side; } /* set clean defaults with new sheet of paper */ /* don't reset the transfer vars on backside of duplex page */ /* otherwise buffered back page will be lost */ /* ingest paper with adf (no-op for fb) */ /* don't call object pos or scan on back side of duplex scan */ if(s->side == SIDE_FRONT || s->source == SOURCE_ADF_BACK || s->source == SOURCE_CARD_BACK){ s->bytes_rx[0]=0; s->bytes_rx[1]=0; s->lines_rx[0]=0; s->lines_rx[1]=0; s->eof_rx[0]=0; s->eof_rx[1]=0; s->ili_rx[0]=0; s->ili_rx[1]=0; s->eom_rx=0; s->bytes_tx[0]=0; s->bytes_tx[1]=0; s->eof_tx[0]=0; s->eof_tx[1]=0; s->buff_rx[0]=0; s->buff_rx[1]=0; s->buff_tx[0]=0; s->buff_tx[1]=0; /* reset jpeg just in case... */ s->jpeg_stage = JPEG_STAGE_NONE; s->jpeg_ff_offset = -1; s->jpeg_front_rst = 0; s->jpeg_back_rst = 0; ret = object_position (s, OP_Feed); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot load page\n"); goto errors; } ret = start_scan (s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot start_scan\n"); goto errors; } /* try to read scan size from scanner */ ret = get_pixelsize(s,0); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot get pixelsize\n"); goto errors; } /* store the number of front bytes */ if ( s->source != SOURCE_ADF_BACK && s->source != SOURCE_CARD_BACK ){ s->bytes_tot[SIDE_FRONT] = s->s_params.bytes_per_line * s->s_params.lines; s->buff_tot[SIDE_FRONT] = s->buffer_size; /* the front buffer is normally very small, but some scanners or * option combinations can't handle it, so we make a big one */ if( (s->s_mode == MODE_COLOR && s->color_interlace == COLOR_INTERLACE_3091) || must_fully_buffer(s) ){ s->buff_tot[SIDE_FRONT] = s->bytes_tot[SIDE_FRONT]; } } else{ s->bytes_tot[SIDE_FRONT] = 0; s->buff_tot[SIDE_FRONT] = 0; } /* store the number of back bytes */ if ( s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_ADF_BACK || s->source == SOURCE_CARD_DUPLEX || s->source == SOURCE_CARD_BACK ){ s->bytes_tot[SIDE_BACK] = s->s_params.bytes_per_line * s->s_params.lines; s->buff_tot[SIDE_BACK] = s->bytes_tot[SIDE_BACK]; /* the back buffer is normally very large, but some scanners or * option combinations don't need it, so we make a small one */ if(s->low_mem || s->source == SOURCE_ADF_BACK || s->source == SOURCE_CARD_BACK || s->duplex_interlace == DUPLEX_INTERLACE_NONE) s->buff_tot[SIDE_BACK] = s->buffer_size; } else{ s->bytes_tot[SIDE_BACK] = 0; s->buff_tot[SIDE_BACK] = 0; } /* first page of batch */ /* make large buffer to hold the images */ /* and set started flag */ if(!s->started){ ret = setup_buffers(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot load buffers\n"); goto errors; } s->started=1; } } else{ /* try to read scan size from scanner */ ret = get_pixelsize(s,0); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot get pixelsize\n"); goto errors; } } DBG (15, "started=%d, side=%d, source=%d\n", s->started, s->side, s->source); /* certain options require the entire image to * be collected from the scanner before we can * tell the user the size of the image. the sane * API has no way to inform the frontend of this, * so we block and buffer. yuck */ if( must_fully_buffer(s) ){ /* get image */ while(!s->eof_rx[s->side] && !ret){ SANE_Int len = 0; ret = sane_read((SANE_Handle)s, NULL, 0, &len); } /* check for errors */ if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot buffer image\n"); goto errors; } DBG (5, "sane_start: OK: done buffering\n"); /* hardware deskew will tell image size after transfer */ ret = get_pixelsize(s,1); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot get final pixelsize\n"); goto errors; } /* finished buffering, adjust image as required */ if(s->swdeskew && (!s->hwdeskewcrop || s->req_driv_crop)){ buffer_deskew(s,s->side); } if(s->swcrop && (!s->hwdeskewcrop || s->req_driv_crop)){ buffer_crop(s,s->side); } if(s->swdespeck){ buffer_despeck(s,s->side); } if(s->swskip){ /* Skipping means throwing out this image. * Pretend the user read the whole thing * and call sane_start again. * This assumes we are running in batch mode. */ if(buffer_isblank(s,s->side)){ s->bytes_tx[s->side] = s->bytes_rx[s->side]; s->eof_tx[s->side] = 1; return sane_start(handle); } } } /* check if user cancelled during this start */ ret = check_for_cancel(s); /* unprotect this block from sane_cancel */ s->reading=0; DBG (10, "sane_start: finish %d\n", ret); return ret; errors: DBG (10, "sane_start: error %d\n", ret); /* if we are started, but something went wrong, * chances are there is image data inside scanner, * which should be discarded via cancel command */ if(s->started){ s->cancelled = 1; check_for_cancel(s); } s->started = 0; s->cancelled = 0; s->reading = 0; return ret; } static SANE_Status endorser(struct fujitsu *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[ENDORSER_len]; size_t cmdLen = ENDORSER_len; unsigned char out[ED_max_len]; size_t outLen = ED_max_len; DBG (10, "endorser: start\n"); memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, ENDORSER_code); memset(out,0,outLen); if (s->has_endorser_f || s->has_endorser_b){ /*fi-5900 front side uses 0x80, assume all others*/ if(s->u_endorser_side == ED_front){ set_ED_endorser_data_id(out,0x80); } else{ set_ED_endorser_data_id(out,0); } if(s->u_endorser){ set_ED_stop(out,ED_start); } else{ set_ED_stop(out,ED_stop); } set_ED_side(out,s->u_endorser_side); if(s->u_endorser_bits == 24){ set_ED_lap24(out,ED_lap_24bit); set_ED_initial_count_24(out,s->u_endorser_val); } else{ outLen = ED_min_len; set_ED_lap24(out,ED_lap_16bit); set_ED_initial_count_16(out,s->u_endorser_val); } set_E_xferlen(cmd, outLen); ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); } DBG (10, "endorser: finish %d\n", ret); return ret; } static SANE_Status scanner_control (struct fujitsu *s, int function) { SANE_Status ret = SANE_STATUS_GOOD; int tries = 0; unsigned char cmd[SCANNER_CONTROL_len]; size_t cmdLen = SCANNER_CONTROL_len; DBG (10, "scanner_control: start\n"); if(s->has_cmd_scanner_ctl){ memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SCANNER_CONTROL_code); set_SC_function_1 (cmd, function); set_SC_function_2 (cmd, function); DBG (15, "scanner_control: function %d\n",function); /* don't really need to ask for adf if that's the only option */ /* doing so causes the 3091 to complain */ if(function == SC_function_adf && !s->has_flatbed && !s->has_return_path){ DBG (10, "scanner_control: adf function not required\n"); return ret; } /* extremely long retry period */ while(tries++ < 120){ ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, NULL, NULL ); if(ret == SANE_STATUS_GOOD || function != SC_function_lamp_on){ break; } usleep(500000); } if(ret == SANE_STATUS_GOOD){ DBG (15, "scanner_control: success, tries %d, ret %d\n",tries,ret); } else{ DBG (5, "scanner_control: error, tries %d, ret %d\n",tries,ret); } } DBG (10, "scanner_control: finish\n"); return ret; } static SANE_Status scanner_control_ric (struct fujitsu *s, int bytes, int side) { SANE_Status ret = SANE_STATUS_GOOD; int tries = 0; unsigned char cmd[SCANNER_CONTROL_len]; size_t cmdLen = SCANNER_CONTROL_len; DBG (10, "scanner_control_ric: start\n"); if(s->has_cmd_scanner_ctl){ memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SCANNER_CONTROL_code); set_SC_ric(cmd, 1); if (side == SIDE_BACK) { set_SC_ric_dtq(cmd, WD_wid_back); } else{ set_SC_ric_dtq(cmd, WD_wid_front); } set_SC_ric_len(cmd, bytes); DBG (15, "scanner_control_ric: %d %d\n",bytes,side); /* extremely long retry period */ while(tries++ < 120){ ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, NULL, NULL ); if(ret != SANE_STATUS_DEVICE_BUSY){ break; } usleep(500000); } if(ret == SANE_STATUS_GOOD){ DBG (15, "scanner_control_ric: success, tries %d, ret %d\n",tries,ret); } /* some errors pass thru unchanged */ else if(ret == SANE_STATUS_CANCELLED || ret == SANE_STATUS_JAMMED || ret == SANE_STATUS_NO_DOCS || ret == SANE_STATUS_COVER_OPEN ){ DBG (5, "scanner_control_ric: error, tries %d, ret %d\n",tries,ret); } /* other errors are ignored, since scanner may not support RIC */ else{ DBG (5, "scanner_control_ric: ignoring, tries %d, ret %d\n",tries,ret); ret = SANE_STATUS_GOOD; } } DBG (10, "scanner_control_ric: finish\n"); return ret; } /* * callocs a buffer to hold the scan data */ static SANE_Status setup_buffers (struct fujitsu *s) { SANE_Status ret = SANE_STATUS_GOOD; int side; DBG (10, "setup_buffers: start\n"); for(side=0;side<2;side++){ /* 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->buff_tot[side]){ s->buffers[side] = calloc (1,s->buff_tot[side]); if (!s->buffers[side]) { DBG (5, "setup_buffers: Error, no buffer %d.\n",side); return SANE_STATUS_NO_MEM; } } } DBG (10, "setup_buffers: finish\n"); return ret; } /* * This routine issues a SCSI SET WINDOW command to the scanner, using the * values currently in the scanner data structure. */ static SANE_Status set_window (struct fujitsu *s) { SANE_Status ret = SANE_STATUS_GOOD; /* 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 */ unsigned char cmd[SET_WINDOW_len]; size_t cmdLen = SET_WINDOW_len; /*this is max size, we might send less below*/ unsigned char out[SW_header_len + SW_desc_len + SW_desc_len]; size_t outLen = SW_header_len + SW_desc_len + SW_desc_len; unsigned char * header = out; /*header*/ unsigned char * desc1 = out + SW_header_len; /*1st desc*/ unsigned char * desc2 = out + SW_header_len + SW_desc_len; /*2nd desc*/ int length = 0; DBG (10, "set_window: start\n"); /*build the payload*/ memset(out,0,outLen); /* set window desc size in header */ set_WPDB_wdblen(header, SW_desc_len); /* init the window block */ if (s->source == SOURCE_ADF_BACK || s->source == SOURCE_CARD_BACK) { set_WD_wid (desc1, WD_wid_back); } else{ set_WD_wid (desc1, WD_wid_front); } set_WD_Xres (desc1, s->resolution_x); set_WD_Yres (desc1, s->resolution_y); set_WD_ULX (desc1, s->tl_x); /* low-end scanners ignore paper-size, * so we have to center the window ourselves */ if(s->cropping_mode == CROP_ABSOLUTE){ set_WD_ULX (desc1, s->tl_x + (s->max_x - s->page_width) / 2); } set_WD_ULY (desc1, s->tl_y); set_WD_width (desc1, s->s_params.pixels_per_line * 1200/s->resolution_x); length = s->s_params.lines * 1200/s->resolution_y; /* stupid trick. 3091/2 require reading extra lines, * because they have a gap between R G and B */ if(s->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 (desc1, length); set_WD_brightness (desc1, 0); if(s->brightness_steps){ /*convert our common -127 to +127 range into HW's range *FIXME: this code assumes hardware range of 0-255 */ set_WD_brightness (desc1, s->brightness+128); } set_WD_threshold (desc1, s->threshold); set_WD_contrast (desc1, 0); if(s->contrast_steps){ /*convert our common -127 to +127 range into HW's range *FIXME: this code assumes hardware range of 0-255 */ set_WD_contrast (desc1, s->contrast+128); } set_WD_composition (desc1, s->s_mode); set_WD_bitsperpixel (desc1, s->s_params.depth); if(s->s_mode == MODE_HALFTONE){ set_WD_ht_type(desc1, s->ht_type); set_WD_ht_pattern(desc1, s->ht_pattern); } set_WD_rif (desc1, s->rif); set_WD_compress_type(desc1, COMP_NONE); set_WD_compress_arg(desc1, 0); /* some scanners support jpeg image compression, for color/gs only */ if(s->s_params.format == SANE_FRAME_JPEG){ set_WD_compress_type(desc1, COMP_JPEG); set_WD_compress_arg(desc1, s->compress_arg); } /* the remainder of the block varies based on model and mode, * except for gamma and paper size, those are in the same place */ /* determine if we need to send gamma LUT. * send lut if scanner supports it and any of: * has no hardware brightness but user changed it * has no hardware contrast but user changed it * has no internal gamma table */ if ( s->num_download_gamma && ( (!s->brightness_steps && s->brightness != 0) || (!s->contrast_steps && s->contrast != 0 ) || !s->num_internal_gamma ) ){ s->window_gamma = 0x80; } /* otherwise, use the internal table */ else{ s->window_gamma = 0; } /*vuid c0*/ if(s->has_vuid_3091){ set_WD_vendor_id_code (desc1, WD_VUID_3091); set_WD_gamma (desc1, s->window_gamma); if (s->s_mode != MODE_COLOR){ switch (s->dropout_color) { case COLOR_RED: set_WD_lamp_color (desc1, WD_LAMP_RED); break; case COLOR_GREEN: set_WD_lamp_color (desc1, WD_LAMP_GREEN); break; case COLOR_BLUE: set_WD_lamp_color (desc1, WD_LAMP_BLUE); break; default: set_WD_lamp_color (desc1, WD_LAMP_DEFAULT); break; } } /*set_WD_quality(desc1,s->quality);*/ } /*vuid c1*/ else if(s->s_mode == MODE_COLOR && s->has_vuid_color){ set_WD_vendor_id_code (desc1, WD_VUID_COLOR); set_WD_gamma (desc1, s->window_gamma); if(s->color_interlace == COLOR_INTERLACE_RGB){ set_WD_scanning_order (desc1, WD_SCAN_ORDER_DOT); set_WD_scanning_order_arg (desc1, WD_SCAN_ARG_RGB); } else if(s->color_interlace == COLOR_INTERLACE_BGR){ set_WD_scanning_order (desc1, WD_SCAN_ORDER_DOT); set_WD_scanning_order_arg (desc1, WD_SCAN_ARG_BGR); } else if(s->color_interlace == COLOR_INTERLACE_RRGGBB){ set_WD_scanning_order (desc1, WD_SCAN_ORDER_LINE); set_WD_scanning_order_arg (desc1, WD_SCAN_ARG_RGB); } else{ DBG (5,"set_window: unknown color interlacing\n"); return SANE_STATUS_INVAL; } /*scanner emphasis ranges from 0 to 7f and smoothing from 80 to ff*/ /* but we expose them to user as a single linear range smooth->emphasis */ /* flip the smooth part over, and tack it onto the upper end of emphasis */ if(s->emphasis < 0) set_WD_c1_emphasis(desc1,127-s->emphasis); else set_WD_c1_emphasis(desc1,s->emphasis); set_WD_c1_mirroring(desc1,s->mirroring); set_WD_wl_follow(desc1,s->wl_follow); } /*vuid 00*/ else if(s->has_vuid_mono){ set_WD_vendor_id_code (desc1, WD_VUID_MONO); set_WD_gamma (desc1, s->window_gamma); set_WD_outline(desc1,s->outline); /*scanner emphasis ranges from 0 to 7f and smoothing from 80 to ff*/ /* but we expose them to user as a single linear range smooth->emphasis */ /* flip the smooth part over, and tack it onto the upper end of emphasis */ if(s->emphasis < 0) set_WD_emphasis(desc1,127-s->emphasis); else set_WD_emphasis(desc1,s->emphasis); set_WD_separation(desc1,s->separation); set_WD_mirroring(desc1,s->mirroring); if (get_ipc_mode(s) == WD_ipc_SDTC) set_WD_variance(desc1,s->variance); else if (get_ipc_mode(s) == WD_ipc_DTC){ set_WD_filtering(desc1,s->bp_filter); set_WD_smoothing(desc1,s->smoothing); set_WD_gamma_curve(desc1,s->gamma_curve); set_WD_threshold_curve(desc1,s->threshold_curve); set_WD_noise_removal(desc1,s->noise_removal); if(s->noise_removal){ set_WD_matrix5x5(desc1,s->matrix_5); set_WD_matrix4x4(desc1,s->matrix_4); set_WD_matrix3x3(desc1,s->matrix_3); set_WD_matrix2x2(desc1,s->matrix_2); } set_WD_background(desc1,s->threshold_white); } set_WD_wl_follow(desc1,s->wl_follow); set_WD_subwindow_list(desc1,0); set_WD_ipc_mode(desc1,get_ipc_mode(s)); } else{ DBG (5,"set_window: no vuid to send?\n"); return SANE_STATUS_INVAL; } /* common to all vuids */ if(s->source == SOURCE_FLATBED){ set_WD_paper_selection(desc1,WD_paper_SEL_UNDEFINED); } else{ set_WD_paper_selection (desc1, WD_paper_SEL_NON_STANDARD); /* call helper function, scanner wants lies about paper width */ set_WD_paper_width_X (desc1, get_page_width(s)); /* don't call helper function, scanner wants actual length? */ set_WD_paper_length_Y (desc1, s->page_height); } /* when in duplex mode, copy first desc block into second */ if (s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_CARD_DUPLEX) { memcpy (desc2, desc1, SW_desc_len); set_WD_wid (desc2, WD_wid_back); /* FIXME: do we really need these on back of page? */ set_WD_paper_selection (desc2, WD_paper_SEL_UNDEFINED); set_WD_paper_width_X (desc2, 0); set_WD_paper_length_Y (desc2, 0); } /* output shorter if not using duplex */ else{ outLen -= SW_desc_len; } /*build the command*/ memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SET_WINDOW_code); set_SW_xferlen(cmd, outLen); ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); DBG (10, "set_window: finish\n"); return ret; } /* update s_params with actual data size scanner reports */ /* then copy as required to the u_params to send to user */ static SANE_Status get_pixelsize(struct fujitsu *s, int actual) { SANE_Status ret; unsigned char cmd[READ_len]; size_t cmdLen = READ_len; unsigned char in[R_PSIZE_len]; size_t inLen = R_PSIZE_len; DBG (10, "get_pixelsize: start %d\n",actual); if (!s->has_pixelsize){ DBG (10, "get_pixelsize: unsupported\n"); return SANE_STATUS_GOOD; } memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, READ_code); set_R_datatype_code (cmd, R_datatype_pixelsize); if(s->side == SIDE_BACK){ set_R_window_id (cmd, WD_wid_back); } else{ set_R_window_id (cmd, WD_wid_front); } set_R_xfer_length (cmd, inLen); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if (ret == SANE_STATUS_GOOD){ /* when we are called post-scan, the scanner may give * more accurate data in other fields */ if(actual && !s->has_short_pixelsize && get_PSIZE_paper_w(in)){ DBG(5,"get_pixelsize: Actual width %d -> %d\n", s->s_params.pixels_per_line, get_PSIZE_paper_w(in)); s->s_params.pixels_per_line = get_PSIZE_paper_w(in); } else{ s->s_params.pixels_per_line = get_PSIZE_num_x(in); } /* stupid trick. 3091/2 require reading extra lines, * because they have a gap between R G and B * we only want to report the shorter value to the frontend */ if(s->s_mode == MODE_COLOR && s->color_interlace == COLOR_INTERLACE_3091){ DBG(5,"get_pixelsize: Ignoring length %d\n",get_PSIZE_num_y(in)); } /* when we are called post-scan, the scanner may give * more accurate data in other fields */ else if(actual && !s->has_short_pixelsize && get_PSIZE_paper_l(in)){ DBG(5,"get_pixelsize: Actual length %d -> %d\n", s->s_params.lines, get_PSIZE_paper_l(in)); s->s_params.lines = get_PSIZE_paper_l(in); } else{ s->s_params.lines = get_PSIZE_num_y(in); } /* bytes per line differs by mode */ if (s->s_mode == MODE_COLOR) { s->s_params.bytes_per_line = s->s_params.pixels_per_line * 3; } else if (s->s_mode == MODE_GRAYSCALE) { s->s_params.bytes_per_line = s->s_params.pixels_per_line; } else { s->s_params.bytes_per_line = s->s_params.pixels_per_line / 8; } /* some scanners can request that the driver clean img */ if(!s->has_short_pixelsize && get_PSIZE_req_driv_valid(in)){ s->req_driv_crop = get_PSIZE_req_driv_crop(in); s->req_driv_lut = get_PSIZE_req_driv_lut(in); DBG(5,"get_pixelsize: scanner requests: crop=%d, lut=%d\n", s->req_driv_crop,s->req_driv_lut); } DBG (15, "get_pixelsize: scan_x=%d, Bpl=%d, scan_y=%d\n", s->s_params.pixels_per_line, s->s_params.bytes_per_line, s->s_params.lines ); /* the user params are usually the same */ s->u_params.pixels_per_line = s->s_params.pixels_per_line; s->u_params.lines = s->s_params.lines; /* bytes per line differs by mode */ if (s->u_mode == MODE_COLOR) { s->u_params.bytes_per_line = s->u_params.pixels_per_line * 3; } else if (s->u_mode == MODE_GRAYSCALE) { s->u_params.bytes_per_line = s->u_params.pixels_per_line; } else { s->u_params.bytes_per_line = s->u_params.pixels_per_line / 8; } } else{ DBG (10, "get_pixelsize: got bad status %d, ignoring\n", ret); s->has_pixelsize = 0; ret = SANE_STATUS_GOOD; } DBG (10, "get_pixelsize: finish\n"); return ret; } /* * Issues the SCSI OBJECT POSITION command if an ADF or card scanner is in use. */ static SANE_Status object_position (struct fujitsu *s, int action) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[OBJECT_POSITION_len]; size_t cmdLen = OBJECT_POSITION_len; DBG (10, "object_position: start %d\n", action); if (s->source == SOURCE_FLATBED && action < OP_Halt) { DBG (10, "object_position: flatbed no-op\n"); return SANE_STATUS_GOOD; } memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, OBJECT_POSITION_code); set_OP_action (cmd, action); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, NULL, NULL ); if (ret != SANE_STATUS_GOOD) return ret; if(!s->no_wait_after_op) wait_scanner (s); DBG (10, "object_position: finish\n"); return ret; } /* * Issues SCAN command. * * (This doesn't actually read anything, it just tells the scanner * to start scanning.) */ static SANE_Status start_scan (struct fujitsu *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[SCAN_len]; size_t cmdLen = SCAN_len; unsigned char out[] = {WD_wid_front, WD_wid_back}; size_t outLen = 2; DBG (10, "start_scan: start\n"); if (s->source != SOURCE_ADF_DUPLEX && s->source != SOURCE_CARD_DUPLEX) { outLen--; if(s->source == SOURCE_ADF_BACK || s->source == SOURCE_CARD_BACK) { out[0] = WD_wid_back; } } memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, SCAN_code); set_SC_xfer_length (cmd, outLen); ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, outLen, NULL, NULL ); DBG (10, "start_scan: finish\n"); return ret; } /* checks started and cancelled flags in scanner struct, * sends cancel command to scanner if required. don't call * this function asynchronously, wait for pending operation */ static SANE_Status check_for_cancel(struct fujitsu *s) { SANE_Status ret=SANE_STATUS_GOOD; DBG (10, "check_for_cancel: start %d %d\n",s->started,s->cancelled); if(s->started && s->cancelled){ /* halt scan */ if(s->halt_on_cancel){ DBG (15, "check_for_cancel: halting\n"); ret = object_position (s, OP_Halt); } /* cancel scan */ else{ DBG (15, "check_for_cancel: cancelling\n"); ret = scanner_control(s, SC_function_cancel); } if (ret == SANE_STATUS_GOOD || ret == SANE_STATUS_CANCELLED) { ret = SANE_STATUS_CANCELLED; } else{ DBG (5, "check_for_cancel: ERROR: cannot cancel\n"); } s->started = 0; s->cancelled = 0; } else if(s->cancelled){ DBG (15, "check_for_cancel: already cancelled\n"); ret = SANE_STATUS_CANCELLED; s->cancelled = 0; } DBG (10, "check_for_cancel: finish %d\n",ret); return ret; } /* * 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; SANE_Status ret=SANE_STATUS_GOOD; DBG (10, "sane_read: start\n"); *len=0; /* maybe cancelled? */ if(!s->started){ DBG (5, "sane_read: not started, call sane_start\n"); return SANE_STATUS_CANCELLED; } /* sane_start required between sides */ if(s->eof_rx[s->side] && s->bytes_tx[s->side] == s->bytes_rx[s->side]){ DBG (15, "sane_read: returning eof\n"); s->eof_tx[s->side] = 1; /* swap sides if user asked for low-mem mode, we are duplexing, * and there is data waiting on the other side */ if(s->low_mem && (s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_CARD_DUPLEX) && (s->bytes_rx[!s->side] > s->bytes_tx[!s->side] || (s->eof_rx[!s->side] && !s->eof_tx[!s->side]) ) ){ s->side = !s->side; } return SANE_STATUS_EOF; } /* protect this block from sane_cancel */ s->reading = 1; /* ---------------------------------------------- * try to read some data from scanner into buffer * these functions are expected not to overrun */ /* 3091/2 are on crack, get their own duplex reader function */ if(s->source == SOURCE_ADF_DUPLEX && s->duplex_interlace == DUPLEX_INTERLACE_3091 ){ ret = read_from_3091duplex(s); if(ret){ DBG(5,"sane_read: 3091 returning %d\n",ret); return ret; } } /* end 3091 */ /* alternating jpeg duplex interlacing */ else if((s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_CARD_DUPLEX) && s->s_params.format == SANE_FRAME_JPEG && s->jpeg_interlace == JPEG_INTERLACE_ALT ){ ret = read_from_JPEGduplex(s); if(ret){ DBG(5,"sane_read: jpeg duplex returning %d\n",ret); return ret; } } /* end alt jpeg */ /* alternating pnm duplex interlacing */ else if((s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_CARD_DUPLEX) && s->s_params.format != SANE_FRAME_JPEG && s->duplex_interlace == DUPLEX_INTERLACE_ALT ){ /* buffer front side */ ret = read_from_scanner(s, SIDE_FRONT); if(ret){ DBG(5,"sane_read: front returning %d\n",ret); return ret; } /* buffer back side, but don't get too far ahead of the front! */ if(s->bytes_rx[SIDE_BACK] < s->bytes_rx[SIDE_FRONT] + s->buffer_size){ ret = read_from_scanner(s, SIDE_BACK); if(ret){ DBG(5,"sane_read: back returning %d\n",ret); return ret; } } } /* end alt pnm */ /* simplex or non-alternating duplex */ else{ ret = read_from_scanner(s, s->side); if(ret){ DBG(5,"sane_read: side %d returning %d\n",s->side,ret); return ret; } } /*end simplex*/ /* uncommon case, downsample and copy a block from buffer to frontend */ if(must_downsample(s)){ ret = downsample_from_buffer(s,buf,max_len,len,s->side); } /* common case, memcpy a block from buffer to frontend */ else{ ret = read_from_buffer(s,buf,max_len,len,s->side); } /*finished sending small buffer, reset it*/ if(s->buff_tx[s->side] == s->buff_rx[s->side] && s->buff_tot[s->side] < s->bytes_tot[s->side] ){ DBG (15, "sane_read: reset buffers\n"); s->buff_rx[s->side] = 0; s->buff_tx[s->side] = 0; } /* check if user cancelled during this read */ ret = check_for_cancel(s); /* swap sides if user asked for low-mem mode, we are duplexing, * and there is data waiting on the other side */ if(s->low_mem && (s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_CARD_DUPLEX) && (s->bytes_rx[!s->side] > s->bytes_tx[!s->side] || (s->eof_rx[!s->side] && !s->eof_tx[!s->side]) ) ){ s->side = !s->side; } /* unprotect this block from sane_cancel */ s->reading = 0; DBG (10, "sane_read: finish %d\n", ret); return ret; } /* bare jpeg images don't contain resolution, but JFIF APP0 does, so we add */ static SANE_Status inject_jfif_header(struct fujitsu *s, int side) { SANE_Status ret=SANE_STATUS_GOOD; unsigned char out[] = { 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x02, 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00 }; size_t outLen=JFIF_APP0_LENGTH; DBG (10, "inject_jfif_header: start %d\n", side); putnbyte(out + 12, s->resolution_x, 2); putnbyte(out + 14, s->resolution_y, 2); memcpy(s->buffers[side]+s->buff_rx[side], out, outLen); s->buff_rx[side] += outLen; s->bytes_rx[side] += outLen; DBG (10, "inject_jfif_header: finish %d\n", ret); return ret; } static SANE_Status read_from_JPEGduplex(struct fujitsu *s) { SANE_Status ret=SANE_STATUS_GOOD; unsigned char cmd[READ_len]; size_t cmdLen = READ_len; unsigned char * in; size_t inLen = 0; int bytes = s->buffer_size; int i = 0; DBG (10, "read_from_JPEGduplex: start\n"); if(s->eof_rx[SIDE_FRONT] && s->eof_rx[SIDE_BACK]){ DBG (10, "read_from_JPEGduplex: already have eofs, done\n"); return ret; } /* we don't know if the following read will give us front or back data * so we only get enough to fill whichever is smaller (and not yet done) */ if(!s->eof_rx[SIDE_FRONT]){ int avail = s->buff_tot[SIDE_FRONT] - s->buff_rx[SIDE_FRONT]; if(bytes > avail){ bytes = avail; } } if(!s->eof_rx[SIDE_BACK]){ int avail = s->buff_tot[SIDE_BACK] - s->buff_rx[SIDE_BACK]; if(bytes > avail){ bytes = avail; } } /* leave space for JFIF header in the small front side buffer, * if we are at the beginning of the image */ if(s->bytes_rx[SIDE_FRONT] < 3){ bytes -= JFIF_APP0_LENGTH; } DBG(15, "read_from_JPEGduplex: fto:%d frx:%d bto:%d brx:%d pa:%d\n", s->bytes_tot[SIDE_FRONT], s->bytes_rx[SIDE_FRONT], s->bytes_tot[SIDE_BACK], s->bytes_rx[SIDE_BACK], bytes); /* this will happen if buffer is not drained yet */ if(bytes < 1){ DBG(5, "read_from_JPEGduplex: Warning: no bytes this pass\n"); return ret; } /* fi-6770A gets mad if you 'read' too soon on usb, see if it is ready */ if(!s->bytes_rx[SIDE_FRONT] && s->connection == CONNECTION_USB){ DBG (15, "read: start of usb page, checking RIC\n"); ret = scanner_control_ric(s,bytes,SIDE_FRONT); if(ret){ DBG(5,"read: ric returning %d\n",ret); return ret; } } inLen = bytes; in = malloc(inLen); if(!in){ DBG(5, "read_from_JPEGduplex: not enough mem for buffer: %d\n",(int)inLen); return SANE_STATUS_NO_MEM; } memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, READ_code); set_R_datatype_code (cmd, R_datatype_imagedata); /* interlaced jpeg duplex always reads from front */ set_R_window_id (cmd, WD_wid_front); set_R_xfer_length (cmd, inLen); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if (ret == SANE_STATUS_GOOD || ret == SANE_STATUS_EOF) { DBG(15, "read_from_JPEGduplex: got GOOD/EOF, returning GOOD\n"); } else if (ret == SANE_STATUS_DEVICE_BUSY) { DBG(5, "read_from_JPEGduplex: got BUSY, returning GOOD\n"); inLen = 0; ret = SANE_STATUS_GOOD; } else { DBG(5, "read_from_JPEGduplex: error reading data status = %d\n", ret); inLen = 0; } for(i=0;i<(int)inLen;i++){ /* about to change stage */ if(in[i] == 0xff && s->jpeg_ff_offset != 0){ s->jpeg_ff_offset=0; continue; } /* last byte was an ff, this byte will change stage */ if(s->jpeg_ff_offset == 0){ /* first marker after SOI is not APP0, add one */ if(s->jpeg_stage == JPEG_STAGE_SOI && in[i] != 0xe0){ inject_jfif_header(s,SIDE_FRONT); inject_jfif_header(s,SIDE_BACK); s->jpeg_stage = JPEG_STAGE_HEAD; } /* SOI header, in both sides */ if(in[i] == 0xd8){ s->jpeg_stage = JPEG_STAGE_SOI; DBG(15, "read_from_JPEGduplex: stage SOI\n"); } /* headers (HuffTab/QTab/DRI), in both sides */ else if(in[i] == 0xc4 || in[i] == 0xdb || in[i] == 0xdd){ s->jpeg_stage = JPEG_STAGE_HEAD; DBG(15, "read_from_JPEGduplex: stage head\n"); } /* start of frame, in both sides, update x first */ else if(in[i]==0xc0){ s->jpeg_stage = JPEG_STAGE_SOF; DBG(15, "read_from_JPEGduplex: stage sof\n"); } /* start of scan, first few bytes of marker in both sides * but rest in front */ else if(in[i]==0xda){ s->jpeg_stage = JPEG_STAGE_SOS; DBG(15, "read_from_JPEGduplex: stage sos\n"); } /* found image block. images are not interlaced */ /* copy to front, don't change RST */ else if(in[i] >= 0xd0 && in[i] <= 0xd7 && s->jpeg_interlace == JPEG_INTERLACE_NONE){ s->jpeg_stage = JPEG_STAGE_FRONT; DBG(35, "read_from_JPEGduplex: stage front (all)\n"); } /* found even numbered image block. */ /* images are interlaced, so switch to back. */ /* also change from even RST to proper one */ else if(in[i] == 0xd0 || in[i] == 0xd2 || in[i] == 0xd4 || in[i] == 0xd6){ s->jpeg_stage = JPEG_STAGE_BACK; DBG(35, "read_from_JPEGduplex: stage back\n"); /* skip first RST for back side*/ if(!s->jpeg_back_rst){ DBG(15, "read_from_JPEGduplex: stage back jump\n"); s->jpeg_ff_offset++; s->jpeg_back_rst++; continue; } in[i] = 0xd0 + (s->jpeg_back_rst-1) % 8; s->jpeg_back_rst++; } /* finished back image block, switch to front */ /* also change from odd RST to proper one */ else if(in[i] == 0xd1 || in[i] == 0xd3 || in[i] == 0xd5 || in[i] == 0xd7){ s->jpeg_stage = JPEG_STAGE_FRONT; DBG(35, "read_from_JPEGduplex: stage front\n"); in[i] = 0xd0 + (s->jpeg_front_rst % 8); s->jpeg_front_rst++; } /* finished image, update totals */ else if(in[i]==0xd9){ s->jpeg_stage = JPEG_STAGE_EOI; DBG(15, "read_from_JPEGduplex: stage eoi %d %d\n",(int)inLen,i); } /* unknown, warn */ else if(in[i] != 0x00){ DBG(15, "read_from_JPEGduplex: unknown %02x\n", in[i]); } } s->jpeg_ff_offset++; /* first x byte in start of frame, buffer it */ if(s->jpeg_stage == JPEG_STAGE_SOF && s->jpeg_ff_offset == 7){ s->jpeg_x_byte = in[i]; continue; } /* second x byte in start of frame */ if(s->jpeg_stage == JPEG_STAGE_SOF && s->jpeg_ff_offset == 8){ int width = (s->jpeg_x_byte << 8) | in[i]; /* if image width equals what we asked for, then * the image is not interlaced, clean up the mess */ if(width == s->s_params.pixels_per_line){ DBG(15, "read_from_JPEGduplex: right width, req:%d got:%d\n", s->s_params.pixels_per_line,width); /* stop copying to the back */ s->jpeg_interlace = JPEG_INTERLACE_NONE; /* clear what is already in the back */ s->bytes_rx[SIDE_BACK]=0; s->lines_rx[SIDE_BACK]=0; s->buff_rx[SIDE_BACK]=0; /* and put the high-order width byte into front unchanged */ s->buffers[SIDE_FRONT][s->buff_rx[SIDE_FRONT]++] = s->jpeg_x_byte; s->bytes_rx[SIDE_FRONT]++; } /* image is interlaced after all, continue */ else{ DBG(15, "read_from_JPEGduplex: wrong width, req:%d got:%d\n", s->s_params.pixels_per_line,width); /* put the high-order width byte into front side, shifted down */ s->buffers[SIDE_FRONT][s->buff_rx[SIDE_FRONT]++] = width >> 9; s->bytes_rx[SIDE_FRONT]++; /* put the high-order width byte into back side, shifted down */ s->buffers[SIDE_BACK][s->buff_rx[SIDE_BACK]++] = width >> 9; s->bytes_rx[SIDE_BACK]++; /* shift down low order byte */ in[i] = (width >> 1) & 0xff; } } /* copy these stages to front */ if(s->jpeg_stage == JPEG_STAGE_SOI || s->jpeg_stage == JPEG_STAGE_HEAD || s->jpeg_stage == JPEG_STAGE_SOF || s->jpeg_stage == JPEG_STAGE_SOS || s->jpeg_stage == JPEG_STAGE_EOI || s->jpeg_stage == JPEG_STAGE_FRONT ){ /* first byte after ff, send the ff first */ if(s->jpeg_ff_offset == 1){ s->buffers[SIDE_FRONT][s->buff_rx[SIDE_FRONT]++] = 0xff; s->bytes_rx[SIDE_FRONT]++; } s->buffers[SIDE_FRONT][s->buff_rx[SIDE_FRONT]++] = in[i]; s->bytes_rx[SIDE_FRONT]++; } /* copy these stages to back */ if( s->jpeg_interlace == JPEG_INTERLACE_ALT && ( s->jpeg_stage == JPEG_STAGE_SOI || s->jpeg_stage == JPEG_STAGE_HEAD || s->jpeg_stage == JPEG_STAGE_SOF || s->jpeg_stage == JPEG_STAGE_SOS || s->jpeg_stage == JPEG_STAGE_EOI || s->jpeg_stage == JPEG_STAGE_BACK ) ){ /* first byte after ff, send the ff first */ if(s->jpeg_ff_offset == 1){ s->buffers[SIDE_BACK][s->buff_rx[SIDE_BACK]++] = 0xff; s->bytes_rx[SIDE_BACK]++; } s->buffers[SIDE_BACK][s->buff_rx[SIDE_BACK]++] = in[i]; s->bytes_rx[SIDE_BACK]++; } /* reached last byte of SOS section, next byte front */ if(s->jpeg_stage == JPEG_STAGE_SOS && s->jpeg_ff_offset == 0x0d){ s->jpeg_stage = JPEG_STAGE_FRONT; } /* last byte of file, update totals, bail out */ if(s->jpeg_stage == JPEG_STAGE_EOI){ s->eof_rx[SIDE_FRONT] = 1; if(s->jpeg_interlace == JPEG_INTERLACE_ALT) s->eof_rx[SIDE_BACK] = 1; } } free(in); /* jpeg uses in-band EOI marker, so this is usually redundant */ if(ret == SANE_STATUS_EOF){ DBG(15, "read_from_JPEGduplex: got EOF, finishing\n"); s->eof_rx[SIDE_FRONT] = 1; if(s->jpeg_interlace == JPEG_INTERLACE_ALT) s->eof_rx[SIDE_BACK] = 1; ret = SANE_STATUS_GOOD; } DBG (10, "read_from_JPEGduplex: finish\n"); return ret; } static SANE_Status read_from_3091duplex(struct fujitsu *s) { SANE_Status ret=SANE_STATUS_GOOD; unsigned char cmd[READ_len]; size_t cmdLen = READ_len; unsigned char * in; size_t inLen = 0; int side = SIDE_FRONT; int bytes = s->buffer_size; int off = (s->duplex_raster_offset+s->duplex_offset) * s->resolution_y/300; unsigned int i; DBG (10, "read_from_3091duplex: start\n"); if(s->eof_rx[SIDE_FRONT] && s->eof_rx[SIDE_BACK]){ DBG (10, "read_from_3091duplex: already have eofs, done\n"); return ret; } /* we don't know if the following read will give us front,back or both data * so we only get enough to fill whichever is smaller (and not yet done) */ if(!s->eof_rx[SIDE_FRONT]){ int avail = s->buff_tot[SIDE_FRONT] - s->buff_rx[SIDE_FRONT]; if(bytes > avail) bytes = avail; } if(!s->eof_rx[SIDE_BACK]){ int avail = s->buff_tot[SIDE_BACK] - s->buff_rx[SIDE_BACK]; if(bytes > avail) bytes = avail; } /* all requests must end on a line boundary */ bytes -= (bytes % s->s_params.bytes_per_line); DBG(15, "read_from_3091duplex: front img: to:%d rx:%d tx:%d li:%d\n", s->bytes_tot[SIDE_FRONT], s->bytes_rx[SIDE_FRONT], s->bytes_tx[SIDE_FRONT], s->lines_rx[SIDE_FRONT]); DBG(15, "read_from_3091duplex: front buf: to:%d rx:%d tx:%d\n", s->buff_tot[SIDE_FRONT], s->buff_rx[SIDE_FRONT], s->buff_tx[SIDE_FRONT]); DBG(15, "read_from_3091duplex: back img: to:%d rx:%d tx:%d li:%d\n", s->bytes_tot[SIDE_BACK], s->bytes_rx[SIDE_BACK], s->bytes_tx[SIDE_BACK], s->lines_rx[SIDE_BACK]); DBG(15, "read_from_3091duplex: back buf: to:%d rx:%d tx:%d\n", s->buff_tot[SIDE_BACK], s->buff_rx[SIDE_BACK], s->buff_tx[SIDE_BACK]); DBG(15, "read_from_3091duplex: bu:%d pa:%d of:%d\n", s->buffer_size, bytes, off); /* this could happen if the front buffer is not drained fast enough */ if(bytes < 1){ DBG(10, "read_from_3091duplex: Warning: no bytes this pass\n"); return ret; } inLen = bytes; in = malloc(inLen); if(!in){ DBG(5, "read_from_3091duplex: not enough mem for buffer: %d\n",(int)inLen); return SANE_STATUS_NO_MEM; } memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, READ_code); set_R_datatype_code (cmd, R_datatype_imagedata); /* 3091 duplex always reads from front */ set_R_window_id (cmd, WD_wid_front); set_R_xfer_length (cmd, inLen); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if (ret == SANE_STATUS_GOOD || ret == SANE_STATUS_EOF) { DBG(15, "read_from_3091duplex: got GOOD/EOF, returning GOOD\n"); } else if (ret == SANE_STATUS_DEVICE_BUSY) { DBG(5, "read_from_3091duplex: got BUSY, returning GOOD\n"); inLen = 0; ret = SANE_STATUS_GOOD; } else { DBG(5, "read_from_3091duplex: error reading data block status = %d\n", ret); inLen = 0; } /* loop thru all lines in read buffer */ for(i=0;is_params.bytes_per_line;i++){ /* start is front */ if(s->lines_rx[SIDE_FRONT] < off){ side=SIDE_FRONT; } /* end is back */ else if(s->eof_rx[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; } /* even are front */ else{ side=SIDE_FRONT; } if(s->s_mode == MODE_COLOR && s->color_interlace == COLOR_INTERLACE_3091){ copy_3091 (s, in + i*s->s_params.bytes_per_line, s->s_params.bytes_per_line, side); } else{ copy_buffer (s, in + i*s->s_params.bytes_per_line, s->s_params.bytes_per_line, side); } } if(ret == SANE_STATUS_EOF){ DBG(15, "read_from_3091duplex: got EOF, finishing both sides\n"); s->eof_rx[SIDE_FRONT] = 1; s->eof_rx[SIDE_BACK] = 1; ret = SANE_STATUS_GOOD; } free(in); DBG (10, "read_from_3091duplex: finish\n"); return ret; } static SANE_Status read_from_scanner(struct fujitsu *s, int side) { SANE_Status ret=SANE_STATUS_GOOD; unsigned char cmd[READ_len]; size_t cmdLen = READ_len; unsigned char * in; size_t inLen = 0; int bytes = s->buffer_size; int avail = s->buff_tot[side] - s->buff_rx[side]; int remain = s->bytes_tot[side] - s->bytes_rx[side]; DBG (10, "read_from_scanner: start %d\n", side); if(s->eof_rx[side]){ DBG (10, "read_from_scanner: already have eof, done\n"); return ret; } /* figure out the max amount to transfer */ if(bytes > avail) bytes = avail; /* all requests must end on line boundary */ bytes -= (bytes % s->s_params.bytes_per_line); /* some larger scanners require even bytes per block */ /* so we get even lines, but not on the last block */ /* cause odd number of lines would never finish */ if(bytes % 2 && bytes < remain){ bytes -= s->s_params.bytes_per_line; } /* jpeg scans leave space for JFIF header at start of image */ if(s->s_params.format == SANE_FRAME_JPEG && s->bytes_rx[side] < 2) bytes -= JFIF_APP0_LENGTH; DBG(15, "read_from_scanner: si:%d re:%d bs:%d by:%d av:%d\n", side, remain, s->buffer_size, bytes, avail); DBG(15, "read_from_scanner: img to:%d rx:%d tx:%d li:%d\n", s->bytes_tot[side], s->bytes_rx[side], s->bytes_tx[side], s->lines_rx[side]); DBG(15, "read_from_scanner: buf to:%d rx:%d tx:%d\n", s->buff_tot[side], s->buff_rx[side], s->buff_tx[side]); /* this will happen if buffer is not drained yet */ if(bytes < 1){ DBG(5, "read_from_scanner: no bytes this pass\n"); return ret; } /* fi-6770A gets mad if you 'read' too soon on usb, see if it is ready */ if(!s->bytes_rx[side] && s->connection == CONNECTION_USB){ DBG (15, "read_from_scanner: start of usb page, checking RIC\n"); ret = scanner_control_ric(s,bytes,side); if(ret){ DBG(5,"read_from_scanner: ric returning %d\n",ret); return ret; } } inLen = bytes; in = malloc(inLen); if(!in){ DBG(5, "read_from_scanner: not enough mem for buffer: %d\n",(int)inLen); return SANE_STATUS_NO_MEM; } memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, READ_code); set_R_datatype_code (cmd, R_datatype_imagedata); if (side == SIDE_BACK) { set_R_window_id (cmd, WD_wid_back); } else{ set_R_window_id (cmd, WD_wid_front); } set_R_xfer_length (cmd, inLen); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if (ret == SANE_STATUS_GOOD || ret == SANE_STATUS_EOF) { DBG(15, "read_from_scanner: got GOOD/EOF, returning GOOD\n"); ret = SANE_STATUS_GOOD; } else if (ret == SANE_STATUS_DEVICE_BUSY) { DBG(5, "read_from_scanner: got BUSY, returning GOOD\n"); inLen = 0; ret = SANE_STATUS_GOOD; } else { DBG(5, "read_from_scanner: error reading data block status = %d\n",ret); inLen = 0; } DBG(15, "read_from_scanner: read %lu bytes\n",(unsigned long)inLen); if(inLen){ if(s->s_mode==MODE_COLOR && s->color_interlace == COLOR_INTERLACE_3091){ copy_3091 (s, in, inLen, side); } else if(s->s_params.format == SANE_FRAME_JPEG){ copy_JPEG (s, in, inLen, side); } else{ copy_buffer (s, in, inLen, side); } } free(in); /* if this was a short read or not, log it */ s->ili_rx[side] = s->rs_ili; if(s->ili_rx[side]){ DBG(15, "read_from_scanner: got ILI\n"); } /* if this was an end of medium, log it */ if(s->rs_eom){ DBG(15, "read_from_scanner: got EOM\n"); s->eom_rx = 1; } /* paper ran out. lets try to set the eof flag on both sides, * but only if that side had a short read last time */ if(s->eom_rx){ int i; for(i=0;i<2;i++){ if(s->ili_rx[i]){ DBG(15, "read_from_scanner: finishing side %d\n",i); s->eof_rx[i] = 1; } } } DBG (10, "read_from_scanner: finish\n"); return ret; } static SANE_Status copy_3091(struct fujitsu *s, unsigned char * buf, int len, int side) { SANE_Status ret=SANE_STATUS_GOOD; int i, j, dest, boff, goff; DBG (10, "copy_3091: start\n"); /* 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 get things on correct line, and * interlace to make RGBRGB. * * 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; /* loop thru all lines in read buffer */ for(i=0;is_params.bytes_per_line){ /* red at start of line */ dest = s->lines_rx[side] * s->s_params.bytes_per_line; if(dest >= 0 && dest < s->bytes_tot[side]){ for (j=0; js_params.pixels_per_line; j++){ s->buffers[side][dest+j*3] = buf[i+j]; } } /* green is in middle of line */ dest = (s->lines_rx[side] - goff) * s->s_params.bytes_per_line; if(dest >= 0 && dest < s->bytes_tot[side]){ for (j=0; js_params.pixels_per_line; j++){ s->buffers[side][dest+j*3+1] = buf[i+s->s_params.pixels_per_line+j]; } } /* blue is at end of line */ dest = (s->lines_rx[side] - boff) * s->s_params.bytes_per_line; if(dest >= 0 && dest < s->bytes_tot[side]){ for (j=0; js_params.pixels_per_line; j++){ s->buffers[side][dest+j*3+2] = buf[i+2*s->s_params.pixels_per_line+j]; } } s->lines_rx[side]++; } /* 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->s_params.bytes_per_line; if(i < 0){ i = 0; } s->bytes_rx[side] = i; s->buff_rx[side] = i; if(s->bytes_rx[side] == s->bytes_tot[side]){ s->eof_rx[side] = 1; } DBG(15, "copy_3091: si:%d imgrx:%d bufrx:%d li:%d eof:%d\n", side, s->bytes_rx[side], s->buff_rx[side], s->lines_rx[side], s->eof_rx[side]); DBG (10, "copy_3091: finish\n"); return ret; } static SANE_Status copy_JPEG(struct fujitsu *s, unsigned char * buf, int len, int side) { SANE_Status ret=SANE_STATUS_GOOD; int i, seen = 0; DBG (10, "copy_JPEG: start\n"); /* A jpeg image starts with the SOI marker, FF D8. * This is optionally followed by the JFIF APP0 * marker, FF E0. If that marker is not present, * we add it, so we can insert the resolution */ if(!s->bytes_rx[side] && len >= 4 && buf[0] == 0xFF && buf[1] == 0xD8 && buf[2] == 0xFF && buf[3] != 0xE0 ){ /* SOI marker */ for (i=0; i<2; i++){ s->buffers[side][s->buff_rx[side]++] = buf[i]; s->bytes_rx[side]++; seen++; } /* JFIF header after SOI */ inject_jfif_header(s,side); } memcpy(s->buffers[side]+s->buff_rx[side],buf+seen,len-seen); s->buff_rx[side] += len-seen; s->bytes_rx[side] += len-seen; /* should never happen with jpeg */ if(s->bytes_rx[side] == s->bytes_tot[side]){ s->eof_rx[side] = 1; } DBG (10, "copy_JPEG: finish\n"); return ret; } static SANE_Status copy_buffer(struct fujitsu *s, unsigned char * buf, int len, int side) { SANE_Status ret=SANE_STATUS_GOOD; int i, j; int bwidth = s->s_params.bytes_per_line; int pwidth = s->s_params.pixels_per_line; DBG (10, "copy_buffer: start\n"); /* invert image if scanner needs it for this mode */ /* jpeg data does not use inverting */ if(s->s_params.format != SANE_FRAME_JPEG && s->reverse_by_mode[s->s_mode]){ for(i=0; is_params.format == SANE_FRAME_RGB){ switch (s->color_interlace) { /* scanner returns pixel data as bgrbgr... */ case COLOR_INTERLACE_BGR: for(i=0; ibuffers[side][s->buff_rx[side]++] = buf[i+j*3+2]; s->buffers[side][s->buff_rx[side]++] = buf[i+j*3+1]; s->buffers[side][s->buff_rx[side]++] = buf[i+j*3]; } } break; /* one line has the following format: rrr...rrrggg...gggbbb...bbb */ case COLOR_INTERLACE_RRGGBB: for(i=0; ibuffers[side][s->buff_rx[side]++] = buf[i+j]; s->buffers[side][s->buff_rx[side]++] = buf[i+pwidth+j]; s->buffers[side][s->buff_rx[side]++] = buf[i+2*pwidth+j]; } } break; default: memcpy(s->buffers[side]+s->buff_rx[side],buf,len); s->buff_rx[side] += len; break; } } /* jpeg/gray/ht/binary */ else{ memcpy(s->buffers[side]+s->buff_rx[side],buf,len); s->buff_rx[side] += len; } s->bytes_rx[side] += len; s->lines_rx[side] += len/s->s_params.bytes_per_line; if(s->bytes_rx[side] == s->bytes_tot[side]){ s->eof_rx[side] = 1; } DBG (10, "copy_buffer: finish\n"); return ret; } 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; int remain = s->buff_rx[side] - s->buff_tx[side]; DBG (10, "read_from_buffer: start\n"); /* figure out the max amount to transfer */ if(bytes > remain){ bytes = remain; } *len = bytes; DBG(15, "read_from_buffer: si:%d re:%d ml:%d by:%d\n", side, remain, max_len, bytes); DBG(15, "read_from_buffer: img to:%d rx:%d tx:%d\n", s->bytes_tot[side], s->bytes_rx[side], s->bytes_tx[side]); DBG(15, "read_from_buffer: buf to:%d rx:%d tx:%d\n", s->buff_tot[side], s->buff_rx[side], s->buff_tx[side]); /*FIXME this needs to timeout eventually */ if(!bytes){ DBG(5,"read_from_buffer: nothing to do\n"); return SANE_STATUS_GOOD; } memcpy(buf,s->buffers[side]+s->buff_tx[side],bytes); s->buff_tx[side] += bytes; s->bytes_tx[side] += bytes; DBG (10, "read_from_buffer: finish\n"); return ret; } /* we have bytes of higher mode image data in s->buffers */ /* user asked for lower mode image. downsample and copy to buf */ static SANE_Status downsample_from_buffer(struct fujitsu *s, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len, int side) { SANE_Status ret=SANE_STATUS_GOOD; DBG (10, "downsample_from_buffer: start %d %d %d %d\n", s->bytes_rx[side], s->bytes_tx[side], s->buff_rx[side], s->buff_tx[side]); if(s->s_mode == MODE_COLOR && s->u_mode == MODE_GRAYSCALE){ while(*len < max_len && s->buff_rx[side] - s->buff_tx[side] >= 3){ int gray = 0; switch (s->dropout_color) { case COLOR_RED: gray = *(s->buffers[side]+s->buff_tx[side]) * 3; break; case COLOR_GREEN: gray = *(s->buffers[side]+s->buff_tx[side]+1) * 3; break; case COLOR_BLUE: gray = *(s->buffers[side]+s->buff_tx[side]+2) * 3; break; default: gray = *(s->buffers[side]+s->buff_tx[side]) + *(s->buffers[side]+s->buff_tx[side]+1) + *(s->buffers[side]+s->buff_tx[side]+2); break; } /* bookkeeping for input */ s->buff_tx[side] += 3; s->bytes_tx[side] += 3; /* add byte to output */ *(buf + *len) = gray/3; (*len)++; } } else if(s->s_mode == MODE_COLOR && s->u_mode == MODE_LINEART){ /* threshold of 0 is actually middle of range */ /*FIXME: add dynamic threshold? */ unsigned char thresh = (s->threshold ? s->threshold : 127); while(*len < max_len && s->buff_rx[side] - s->buff_tx[side] >= 24){ int i; unsigned char out = 0; for(i=0; i<8; i++){ int gray = 0; switch (s->dropout_color) { case COLOR_RED: gray = *(s->buffers[side]+s->buff_tx[side]) * 3; break; case COLOR_GREEN: gray = *(s->buffers[side]+s->buff_tx[side]+1) * 3; break; case COLOR_BLUE: gray = *(s->buffers[side]+s->buff_tx[side]+2) * 3; break; default: gray = *(s->buffers[side]+s->buff_tx[side]) + *(s->buffers[side]+s->buff_tx[side]+1) + *(s->buffers[side]+s->buff_tx[side]+2); break; } /* black if input gray is lower than threshold */ if(gray/3 < thresh){ out |= (0x80 >> i); } /* bookkeeping for input */ s->buff_tx[side] += 3; s->bytes_tx[side] += 3; } /* add byte to output */ *(buf + *len) = out; (*len)++; } } else{ DBG (5, "downsample_from_buffer: invalid mode combination\n"); ret = SANE_STATUS_INVAL; } DBG (10, "downsample_from_buffer: finish %d %d %d %d\n", s->bytes_rx[side], s->bytes_tx[side], s->buff_rx[side], s->buff_tx[side]); return ret; } /* * @@ Section 5 - 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 operation 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) { struct fujitsu * s = (struct fujitsu *) handle; DBG (10, "sane_cancel: start\n"); s->cancelled = 1; /* if there is no other running function to check, we do it */ if(!s->reading) check_for_cancel(s); DBG (10, "sane_cancel: finish\n"); } /* * 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) { struct fujitsu * s = (struct fujitsu *) handle; DBG (10, "sane_close: start\n"); /*clears any held scans*/ mode_select_buff(s); disconnect_fd(s); DBG (10, "sane_close: finish\n"); } static SANE_Status disconnect_fd (struct fujitsu *s) { DBG (10, "disconnect_fd: start\n"); 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 (10, "disconnect_fd: finish\n"); return SANE_STATUS_GOOD; } /* * 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"); for (dev = fujitsu_devList; dev; dev = next) { disconnect_fd(dev); next = dev->next; free (dev); } if (sane_devArray) free (sane_devArray); fujitsu_devList = NULL; sane_devArray = NULL; DBG (10, "sane_exit: finish\n"); } /* * @@ Section 6 - 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) { struct fujitsu *s = 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); DBG (5, "sense_handler: start\n"); /* kill compiler warning */ (void) fd; /* copy the rs return data into the scanner struct so that the caller can use it if he wants */ s->rs_info = get_RS_information (sensed_data); s->rs_eom = get_RS_EOM (sensed_data); s->rs_ili = get_RS_ILI (sensed_data); DBG (5, "Sense=%#02x, ASC=%#02x, ASCQ=%#02x, EOM=%d, ILI=%d, info=%#08lx\n", sense, asc, ascq, s->rs_eom, s->rs_ili, (unsigned long)s->rs_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; } /* ready, but short read */ if (s->rs_ili) { DBG (5, "No sense: ILI remainder:%lu\n",(unsigned long)s->rs_info); } /* ready, but end of paper */ if (s->rs_eom) { DBG (5, "No sense: EOM\n"); return SANE_STATUS_EOF; } DBG (5, "No sense: ready\n"); return SANE_STATUS_GOOD; 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; 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 (0x08 == ascq) { DBG (5, "Medium error: ADF setup error\n"); return SANE_STATUS_JAMMED; } if (0x09 == ascq) { DBG (5, "Medium error: Carrier sheet\n"); return SANE_STATUS_JAMMED; } if (0x0c == ascq) { DBG (5, "Medium error: ADF blocked by card\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: endorser error\n"); return SANE_STATUS_IO_ERROR; } if (0x20 == ascq) { DBG (5, "Medium error: Stop button\n"); return SANE_STATUS_NO_DOCS; } if (0x22 == ascq) { DBG (5, "Medium error: scanning halted\n"); return SANE_STATUS_CANCELLED; } if (0x30 == ascq) { DBG (5, "Medium error: Not enough paper\n"); return SANE_STATUS_NO_DOCS; } if (0x31 == ascq) { DBG (5, "Medium error: scanning disabled\n"); return SANE_STATUS_IO_ERROR; } if (0x32 == ascq) { DBG (5, "Medium error: scanning paused\n"); return SANE_STATUS_DEVICE_BUSY; } if (0x33 == ascq) { DBG (5, "Medium error: WiFi control error\n"); return SANE_STATUS_IO_ERROR; } DBG (5, "Medium error: unknown ascq\n"); return SANE_STATUS_IO_ERROR; break; case 0x4: if (0x80 != asc && 0x44 != 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) && (0x03 == ascq)) { DBG (5, "Hardware error: lamp 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: endorser error\n"); return SANE_STATUS_IO_ERROR; } if ((0x80 == asc) && (0x11 == ascq)) { DBG (5, "Hardware error: endorser fuse\n"); return SANE_STATUS_IO_ERROR; } if ((0x80 == asc) && (0x80 == ascq)) { DBG (5, "Hardware error: interface board timeout\n"); return SANE_STATUS_IO_ERROR; } if ((0x80 == asc) && (0x81 == ascq)) { DBG (5, "Hardware error: interface board error 1\n"); return SANE_STATUS_IO_ERROR; } if ((0x80 == asc) && (0x82 == ascq)) { DBG (5, "Hardware error: interface board error 2\n"); return SANE_STATUS_IO_ERROR; } DBG (5, "Hardware error: unknown asc/ascq\n"); return SANE_STATUS_IO_ERROR; break; 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; 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; 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; default: DBG (5, "Unknown Sense Code\n"); return SANE_STATUS_IO_ERROR; } DBG (5, "sense_handler: should never happen!\n"); return SANE_STATUS_IO_ERROR; } /* * take a bunch of pointers, send commands to scanner */ 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 ) { /* unset the request sense vars first */ s->rs_info = 0; s->rs_ili = 0; s->rs_eom = 0; 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; } 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; /*shut up compiler*/ (void) runRS; (void) shortTime; DBG(10, "do_scsi_cmd: start\n"); DBG(25, "cmd: writing %d bytes\n", (int)cmdLen); hexdump(30, "cmd: >>", cmdBuff, cmdLen); 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); } ret = sanei_scsi_cmd2(s->fd, cmdBuff, cmdLen, outBuff, outLen, inBuff, inLen); if(ret != SANE_STATUS_GOOD && ret != SANE_STATUS_EOF){ DBG(5,"do_scsi_cmd: return '%s'\n",sane_strstatus(ret)); return ret; } /* FIXME: should we look at s->rs_info here? */ if (inBuff && inLen){ hexdump(30, "in: <<", inBuff, *inLen); DBG(25, "in: read %d bytes\n", (int)*inLen); } DBG(10, "do_scsi_cmd: finish\n"); return ret; } 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 ) { /*sanei_usb overwrites the transfer size, * so make some local copies */ size_t usb_cmdLen = USB_COMMAND_LEN; size_t usb_outLen = outLen; size_t usb_statLen = USB_STATUS_LEN; size_t askLen = 0; /*copy the callers buffs into larger, padded ones*/ unsigned char usb_cmdBuff[USB_COMMAND_LEN]; unsigned char usb_statBuff[USB_STATUS_LEN]; int cmdTime = USB_COMMAND_TIME; int outTime = USB_DATA_TIME; int inTime = USB_DATA_TIME; int statTime = USB_STATUS_TIME; int ret = 0; int ret2 = 0; DBG (10, "do_usb_cmd: start\n"); if(shortTime){ cmdTime = USB_COMMAND_TIME/60; outTime = USB_DATA_TIME/60; inTime = USB_DATA_TIME/60; statTime = USB_STATUS_TIME/60; } /* 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); /* change timeout */ sanei_usb_set_timeout(cmdTime); /* 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); ret = sanei_usb_write_bulk(s->fd, usb_cmdBuff, &usb_cmdLen); DBG(25, "cmd: wrote %d bytes, retVal %d\n", (int)usb_cmdLen, ret); if(ret == SANE_STATUS_EOF){ DBG(5,"cmd: got EOF, returning IO_ERROR\n"); return SANE_STATUS_IO_ERROR; } if(ret != SANE_STATUS_GOOD){ DBG(5,"cmd: return error '%s'\n",sane_strstatus(ret)); return ret; } 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; } /* this command has a write component, and a place to get it */ if(outBuff && outLen && outTime){ /* change timeout */ sanei_usb_set_timeout(outTime); DBG(25, "out: writing %d bytes, timeout %d\n", (int)outLen, outTime); hexdump(30, "out: >>", outBuff, outLen); ret = sanei_usb_write_bulk(s->fd, outBuff, &usb_outLen); DBG(25, "out: wrote %d bytes, retVal %d\n", (int)usb_outLen, ret); if(ret == SANE_STATUS_EOF){ DBG(5,"out: got EOF, returning IO_ERROR\n"); return SANE_STATUS_IO_ERROR; } if(ret != SANE_STATUS_GOOD){ DBG(5,"out: return error '%s'\n",sane_strstatus(ret)); return ret; } if(usb_outLen != outLen){ DBG(5,"out: wrong size %d/%d\n", (int)outLen, (int)usb_outLen); return SANE_STATUS_IO_ERROR; } } /* this command has a read component, and a place to put it */ if(inBuff && inLen && inTime){ askLen = *inLen; memset(inBuff,0,askLen); /* change timeout */ sanei_usb_set_timeout(inTime); DBG(25, "in: reading %lu bytes, timeout %d\n", (unsigned long)askLen, inTime); ret = sanei_usb_read_bulk(s->fd, inBuff, inLen); DBG(25, "in: retVal %d\n", ret); if(ret == SANE_STATUS_EOF){ DBG(5,"in: got EOF, continuing\n"); ret = SANE_STATUS_GOOD; } if(ret != SANE_STATUS_GOOD){ DBG(5,"in: return error '%s'\n",sane_strstatus(ret)); return ret; } DBG(25, "in: read %lu bytes\n", (unsigned long)*inLen); if(*inLen){ hexdump(31, "in: <<", inBuff, *inLen); } if(*inLen && *inLen != askLen){ ret = SANE_STATUS_EOF; DBG(5,"in: short read, %lu/%lu\n", (unsigned long)*inLen,(unsigned long)askLen); } } /*gather the scsi status byte. use ret2 instead of ret for status*/ memset(&usb_statBuff,0,USB_STATUS_LEN); /* change timeout */ sanei_usb_set_timeout(statTime); DBG(25, "stat: reading %d bytes, timeout %d\n", USB_STATUS_LEN, statTime); ret2 = 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, ret2); if(ret2 == SANE_STATUS_EOF){ DBG(5,"stat: got EOF, returning IO_ERROR\n"); return SANE_STATUS_IO_ERROR; } if(ret2 != SANE_STATUS_GOOD){ DBG(5,"stat: return error '%s'\n",sane_strstatus(ret2)); return ret2; } 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; } /* busy status */ if(usb_statBuff[USB_STATUS_OFFSET] == 8){ DBG(25,"stat: busy\n"); return SANE_STATUS_DEVICE_BUSY; } /* if there is a non-busy status >0, try to figure out why */ if(usb_statBuff[USB_STATUS_OFFSET] > 0){ DBG(25,"stat: value %d\n", usb_statBuff[USB_STATUS_OFFSET]); /* caller is interested in having RS run on errors */ if(runRS){ unsigned char rs_cmd[REQUEST_SENSE_len]; size_t rs_cmdLen = REQUEST_SENSE_len; unsigned char rs_in[RS_return_size]; size_t rs_inLen = RS_return_size; memset(rs_cmd,0,rs_cmdLen); set_SCSI_opcode(rs_cmd, REQUEST_SENSE_code); set_RS_return_size(rs_cmd, rs_inLen); DBG(25,"rs sub call >>\n"); ret2 = do_cmd( s,0,0, rs_cmd, rs_cmdLen, NULL,0, rs_in, &rs_inLen ); DBG(25,"rs sub call <<\n"); if(ret2 == SANE_STATUS_EOF){ DBG(5,"rs: got EOF, returning IO_ERROR\n"); return SANE_STATUS_IO_ERROR; } if(ret2 != SANE_STATUS_GOOD){ DBG(5,"rs: return error '%s'\n",sane_strstatus(ret2)); return ret2; } /* parse the rs data */ ret2 = sense_handler( 0, rs_in, (void *)s ); /* this was a short read, but the usb layer did not know */ if(s->rs_ili && inBuff && inLen && inTime){ *inLen = askLen - s->rs_info; DBG(5,"do_usb_cmd: short read via rs, %lu/%lu\n", (unsigned long)*inLen,(unsigned long)askLen); } return ret2; } else{ DBG(5,"do_usb_cmd: Not calling rs!\n"); return SANE_STATUS_IO_ERROR; } } DBG (10, "do_usb_cmd: finish\n"); return ret; } static SANE_Status wait_scanner(struct fujitsu *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[TEST_UNIT_READY_len]; size_t cmdLen = TEST_UNIT_READY_len; DBG (10, "wait_scanner: start\n"); memset(cmd,0,cmdLen); set_SCSI_opcode(cmd,TEST_UNIT_READY_code); ret = do_cmd ( s, 0, 1, cmd, cmdLen, NULL, 0, NULL, NULL ); if (ret != SANE_STATUS_GOOD) { DBG(5,"WARNING: Brain-dead scanner. Hitting with stick\n"); ret = do_cmd ( s, 0, 1, cmd, cmdLen, NULL, 0, NULL, NULL ); } if (ret != SANE_STATUS_GOOD) { DBG(5,"WARNING: Brain-dead scanner. Hitting with stick again\n"); ret = do_cmd ( s, 0, 1, cmd, cmdLen, NULL, 0, NULL, NULL ); } if (ret != SANE_STATUS_GOOD) { DBG (5, "wait_scanner: error '%s'\n", sane_strstatus (ret)); } DBG (10, "wait_scanner: finish\n"); return ret; } /* certain options require the entire image to * be collected from the scanner before we can * tell the user the size of the image. */ static int must_fully_buffer(struct fujitsu *s) { if(s->hwdeskewcrop){ return 1; } if( (s->swdeskew || s->swdespeck || s->swcrop || s->swskip) && s->s_params.format != SANE_FRAME_JPEG ){ return 1; } return 0; } /* certain scanners require the mode of the * image to be changed in software. */ static int must_downsample(struct fujitsu *s) { if(s->s_mode != s->u_mode && s->compress != COMP_JPEG ){ return 1; } return 0; } /* s->page_width stores the user setting * for the paper width in adf. sometimes, * we need a value that differs from this * due to using FB or overscan. */ static int get_page_width(struct fujitsu *s) { int width = s->page_width + 2 * (s->os_x_basic*1200/s->basic_x_res); /* scanner max for fb */ if(s->source == SOURCE_FLATBED){ return s->max_x_fb; } /* current paper size for adf not overscan */ if(s->overscan != MSEL_ON){ return s->page_width; } /* can't overscan larger than scanner max */ if(width > s->max_x){ return s->max_x; } /* overscan adds a margin to both sides */ return width; } /* s->page_height stores the user setting * for the paper height in adf. sometimes, * we need a value that differs from this * due to using FB or overscan. */ static int get_page_height(struct fujitsu *s) { int height = s->page_height + 2 * (s->os_y_basic*1200/s->basic_y_res); /* scanner max for fb */ if(s->source == SOURCE_FLATBED){ return s->max_y_fb; } /* current paper size for adf not overscan */ if(s->overscan != MSEL_ON){ return s->page_height; } /* can't overscan larger than scanner max */ if(height > s->max_y){ return s->max_y; } /* overscan adds a margin to both sides */ return height; } /* scanners have two different possible IPC * modes, which enable a different series of * subordinate options. Rather than provide * the user with an option to pick the IPC * mode, we show them the subordinate ones, * and pick the right mode to match. */ static int get_ipc_mode(struct fujitsu *s) { if ( s->bp_filter || s->smoothing || s->gamma_curve || s->threshold_curve || s->threshold_white || s->noise_removal || s->matrix_5 || s->matrix_4 || s->matrix_3 || s->matrix_2 ) return WD_ipc_DTC; if(s->variance) return WD_ipc_SDTC; /* special case: 0 threshold should activate IPC */ if(!s->threshold){ if(s->has_sdtc) return WD_ipc_SDTC; if(s->has_dtc) return WD_ipc_DTC; } return WD_ipc_DEFAULT; } /* s->max_y gives the maximum height of paper which can be scanned * this actually varies by resolution, so a helper to change it */ static int set_max_y(struct fujitsu *s) { int i; for(i=0;i<4;i++){ if(!s->max_y_by_res[i].res) break; if(s->resolution_x <= s->max_y_by_res[i].res){ s->max_y = s->max_y_by_res[i].len; } } return s->max_y; } /** * 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; } return max_size; } /* * 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[70]; /* 'xxx: xx xx ... xx xx abc */ char *hex = line+4; char *bin = line+53; if(DBG_LEVEL < level) return; DBG (level, "%s\n", comment); for (i = 0; i < l; i++, p++) { /* at start of line */ if ((i % 16) == 0) { /* not at start of first line, print current, reset */ if (i) { DBG (level, "%s\n", line); } memset(line,0x20,69); line[69] = 0; hex = line + 4; bin = line + 53; sprintf (line, "%3.3x:", i); } /* the hex section */ sprintf (hex, " %2.2x", *p); hex += 3; *hex = ' '; /* the char section */ if(*p >= 0x20 && *p <= 0x7e){ *bin=*p; } else{ *bin='.'; } bin++; } /* print last (partial) line */ if (i) DBG (level, "%s\n", line); } /** * 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 (15, "%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) { DBG (10, "sane_get_select_fd\n"); DBG (15, "%p %d\n", h, *fdp); return SANE_STATUS_UNSUPPORTED; } /* * @@ Section 7 - Image processing functions */ /* Look in image for likely upper and left paper edges, then rotate * image so that upper left corner of paper is upper left of image. * FIXME: should we do this before we binarize instead of after? */ static SANE_Status buffer_deskew(struct fujitsu *s, int side) { SANE_Status ret = SANE_STATUS_GOOD; int bg_color = 0xd6; DBG (10, "buffer_deskew: start\n"); /*only find skew on first image from a page, or if first image had error */ if(s->side == SIDE_FRONT || s->source == SOURCE_ADF_BACK || s->source == SOURCE_CARD_BACK || s->deskew_stat){ s->deskew_stat = sanei_magic_findSkew( &s->s_params,s->buffers[side],s->resolution_x,s->resolution_y, &s->deskew_vals[0],&s->deskew_vals[1],&s->deskew_slope); if(s->deskew_stat){ DBG (5, "buffer_deskew: bad findSkew, bailing\n"); goto cleanup; } } /* backside images can use a 'flipped' version of frontside data */ else{ s->deskew_slope *= -1; s->deskew_vals[0] = s->s_params.pixels_per_line - s->deskew_vals[0]; } /* tweak the bg color based on scanner settings */ if(s->s_mode == MODE_HALFTONE || s->s_mode == MODE_LINEART){ if(s->bg_color == COLOR_BLACK || s->hwdeskewcrop || s->overscan) bg_color = 0xff; else bg_color = 0; } else if(s->bg_color == COLOR_BLACK || s->hwdeskewcrop || s->overscan) bg_color = 0; ret = sanei_magic_rotate(&s->s_params,s->buffers[side], s->deskew_vals[0],s->deskew_vals[1],s->deskew_slope,bg_color); if(ret){ DBG(5,"buffer_deskew: rotate error: %d",ret); ret = SANE_STATUS_GOOD; goto cleanup; } cleanup: DBG (10, "buffer_deskew: finish\n"); return ret; } /* Look in image for likely left/right/bottom paper edges, then crop image. * Does not attempt to rotate the image, that should be done first. * FIXME: should we do this before we binarize instead of after? */ static SANE_Status buffer_crop(struct fujitsu *s, int side) { SANE_Status ret = SANE_STATUS_GOOD; DBG (10, "buffer_crop: start\n"); ret = sanei_magic_findEdges( &s->s_params,s->buffers[side],s->resolution_x,s->resolution_y, &s->crop_vals[0],&s->crop_vals[1],&s->crop_vals[2],&s->crop_vals[3]); if(ret){ DBG (5, "buffer_crop: bad edges, bailing\n"); ret = SANE_STATUS_GOOD; goto cleanup; } DBG (15, "buffer_crop: t:%d b:%d l:%d r:%d\n", s->crop_vals[0],s->crop_vals[1],s->crop_vals[2],s->crop_vals[3]); /* if we will later binarize this image, make sure the width * is a multiple of 8 pixels, by adjusting the right side */ if ( must_downsample(s) && s->u_mode < MODE_GRAYSCALE ){ s->crop_vals[3] -= (s->crop_vals[3]-s->crop_vals[2]) % 8; } /* now crop the image */ ret = sanei_magic_crop(&s->s_params,s->buffers[side], s->crop_vals[0],s->crop_vals[1],s->crop_vals[2],s->crop_vals[3]); if(ret){ DBG (5, "buffer_crop: bad crop, bailing\n"); ret = SANE_STATUS_GOOD; goto cleanup; } /* need to update user with new size */ update_u_params(s); /* update image size counter to new, smaller size */ s->bytes_rx[side] = s->s_params.lines * s->s_params.bytes_per_line; s->buff_rx[side] = s->bytes_rx[side]; cleanup: DBG (10, "buffer_crop: finish\n"); return ret; } /* Look in image for disconnected 'spots' of the requested size. * Replace the spots with the average color of the surrounding pixels. * FIXME: should we do this before we binarize instead of after? */ static SANE_Status buffer_despeck(struct fujitsu *s, int side) { SANE_Status ret = SANE_STATUS_GOOD; DBG (10, "buffer_despeck: start\n"); ret = sanei_magic_despeck(&s->s_params,s->buffers[side],s->swdespeck); if(ret){ DBG (5, "buffer_despeck: bad despeck, bailing\n"); ret = SANE_STATUS_GOOD; goto cleanup; } cleanup: DBG (10, "buffer_despeck: finish\n"); return ret; } /* Look if image has too few dark pixels.*/ static int buffer_isblank(struct fujitsu *s, int side) { SANE_Status ret = SANE_STATUS_GOOD; int status = 0; DBG (10, "buffer_isblank: start\n"); ret = sanei_magic_isBlank2(&s->s_params, s->buffers[side], s->resolution_x, s->resolution_y, s->swskip); if(ret == SANE_STATUS_NO_DOCS){ DBG (5, "buffer_isblank: blank!\n"); status = 1; } else if(ret){ DBG (5, "buffer_isblank: error %d\n",ret); } DBG (10, "buffer_isblank: finished\n"); return status; } backends-1.3.0/backend/fujitsu.conf.in000066400000000000000000000074221456256263500176440ustar00rootroot00000000000000# 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 old scanners may have scanning problems # with a value larger than 65536 (the default) # NOTE: this option is set to a larger value # later in this file, for more recent scanners option buffer-size 65536 # To search for all FUJITSU scsi devices scsi FUJITSU # To use a specific scsi device #scsi /dev/sg1 # For scanners connected via USB on a known device (kernel driver): #usb /dev/usb/scanner0 # For 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. kitno455 at # gmail dot com - with Fujitsu in the subject line #fi-4x20C usb 0x04c5 0x1041 usb 0x04c5 0x1042 #fi-4530C usb 0x04c5 0x1078 #fi-5750C usb 0x04c5 0x1095 #fi-5110eox/2 usb 0x04c5 0x1096 #fi-5110C usb 0x04c5 0x1097 #fi-5650C usb 0x04c5 0x10ad #fi-4x20C2 usb 0x04c5 0x10ae usb 0x04c5 0x10af #fi-4340C usb 0x04c5 0x10cf #fi-5x20C usb 0x04c5 0x10e0 usb 0x04c5 0x10e1 #fi-5530C usb 0x04c5 0x10e2 #fi-5110eox3 usb 0x04c5 0x10e6 #fi-5900C usb 0x04c5 0x10e7 #fi-5110EOXM usb 0x04c5 0x10f2 #ScanSnap S500 usb 0x04c5 0x10fe #ScanSnap S500M usb 0x04c5 0x1135 #fi-5530C2 usb 0x04c5 0x114a # More recent scanners need a larger buffer for maximum speed option buffer-size 262144 #fi-6140 usb 0x04c5 0x114d #fi-6240 usb 0x04c5 0x114e #fi-6130 usb 0x04c5 0x114f #fi-6230 usb 0x04c5 0x1150 #ScanSnap S510 usb 0x04c5 0x1155 #ScanSnap S510M usb 0x04c5 0x116f #fi-6770 usb 0x04c5 0x1174 #fi-6770A usb 0x04c5 0x1175 #fi-6670 usb 0x04c5 0x1176 #fi-6670A usb 0x04c5 0x1177 #fi-6750S usb 0x04c5 0x1178 #fi-6800 usb 0x04c5 0x119d #fi-6800-CGA usb 0x04c5 0x119e #S1500 & S1500M usb 0x04c5 0x11a2 #fi-6140Z/fi-6160ZLA usb 0x04c5 0x11f1 #fi-6240Z usb 0x04c5 0x11f2 #fi-6130Z usb 0x04c5 0x11f3 #fi-6230Z usb 0x04c5 0x11f4 #fi-6110 usb 0x04c5 0x11fc #fi-5950 usb 0x04c5 0x1213 #ScanSnap SV600 usb 0x04c5 0x128e #ScanSnap iX500 usb 0x04c5 0x132b #fi-7180 usb 0x04c5 0x132c #fi-7280 usb 0x04c5 0x132d #fi-7160 usb 0x04c5 0x132e #fi-7260 usb 0x04c5 0x132f #ScanSnap iX500EE usb 0x04c5 0x13f3 #ScanSnap iX100 usb 0x04c5 0x13f4 #fi-6140ZLA usb 0x04c5 0x145f #fi-6240ZLA usb 0x04c5 0x1460 #fi-6130ZLA usb 0x04c5 0x1461 #fi-6230ZLA usb 0x04c5 0x1462 #fi-6125ZLA usb 0x04c5 0x1463 #fi-6225ZLA usb 0x04c5 0x1464 #fi-6135ZLA usb 0x04c5 0x146b #fi-6235ZLA usb 0x04c5 0x146c #fi-6120ZLA usb 0x04c5 0x146d #fi-6220ZLA usb 0x04c5 0x146e #N7100 usb 0x04c5 0x146f #fi-6400 usb 0x04c5 0x14ac #fi-7480 usb 0x04c5 0x14b8 #fi-6420 usb 0x04c5 0x14bd #fi-7460 usb 0x04c5 0x14be #fi-7140 usb 0x04c5 0x14df #fi-7240 usb 0x04c5 0x14e0 #fi-7135 usb 0x04c5 0x14e1 #fi-7235 usb 0x04c5 0x14e2 #fi-7130 usb 0x04c5 0x14e3 #fi-7230 usb 0x04c5 0x14e4 #fi-7125 usb 0x04c5 0x14e5 #fi-7225 usb 0x04c5 0x14e6 #fi-7120 usb 0x04c5 0x14e7 #fi-7220 usb 0x04c5 0x14e8 #fi-400F usb 0x04c5 0x151e #fi-7030 usb 0x04c5 0x151f #fi-7700 usb 0x04c5 0x1520 #fi-7600 usb 0x04c5 0x1521 #fi-7700S usb 0x04c5 0x1522 #ScanSnap iX1500 usb 0x04c5 0x159f #fi-800R usb 0x04c5 0x15fc #fi-7900 usb 0x04c5 0x160a #fi-7800 usb 0x04c5 0x160b #ScanSnap iX1600 usb 0x04c5 0x1632 #ScanPartner SP30 usb 0x04c5 0x140a #fi-7300NX usb 0x04c5 0x1575 #fi-8190 usb 0x04c5 0x15fd #fi-8290 usb 0x04c5 0x15fe #fi-8170 usb 0x04c5 0x15ff #fi-8270 usb 0x04c5 0x1600 #fi-8150 usb 0x04c5 0x1601 #fi-8250 usb 0x04c5 0x1602 #fi-8150U usb 0x04c5 0x162d #fi-8250U usb 0x04c5 0x162e #ScanSnap iX1300 usb 0x04c5 0x162c #ScanSnap iX1400 usb 0x04c5 0x1630 ###################################### # Ricoh-only scanners #fi-8040 usb 0x05ca 0x0307 #fi-70F" usb 0x05ca 0x0308 backends-1.3.0/backend/fujitsu.h000066400000000000000000000560701456256263500165440ustar00rootroot00000000000000#ifndef FUJITSU_H #define FUJITSU_H /* * Part of SANE - Scanner Access Now Easy. * Please see opening comment in fujitsu.c */ /* ------------------------------------------------------------------------- * 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 * still the possibility to say so, later. */ enum fujitsu_Option { OPT_NUM_OPTS = 0, OPT_STANDARD_GROUP, OPT_SOURCE, /*fb/adf/front/back/duplex*/ OPT_MODE, /*mono/gray/color*/ OPT_RES, /*a range or a list*/ OPT_GEOMETRY_GROUP, OPT_PAGE_WIDTH, OPT_PAGE_HEIGHT, OPT_TL_X, OPT_TL_Y, OPT_BR_X, OPT_BR_Y, OPT_ENHANCEMENT_GROUP, OPT_BRIGHTNESS, OPT_CONTRAST, OPT_GAMMA, OPT_THRESHOLD, /*IPC*/ OPT_RIF, OPT_HT_TYPE, OPT_HT_PATTERN, OPT_OUTLINE, OPT_EMPHASIS, OPT_SEPARATION, OPT_MIRRORING, OPT_WL_FOLLOW, /*IPC/DTC*/ OPT_BP_FILTER, OPT_SMOOTHING, OPT_GAMMA_CURVE, OPT_THRESHOLD_CURVE, OPT_THRESHOLD_WHITE, OPT_NOISE_REMOVAL, OPT_MATRIX_5, OPT_MATRIX_4, OPT_MATRIX_3, OPT_MATRIX_2, /*IPC/SDTC*/ OPT_VARIANCE, OPT_ADVANCED_GROUP, OPT_AWD, OPT_ALD, OPT_COMPRESS, OPT_COMPRESS_ARG, OPT_DF_ACTION, OPT_DF_SKEW, OPT_DF_THICKNESS, OPT_DF_LENGTH, OPT_DF_DIFF, OPT_DF_RECOVERY, OPT_PAPER_PROTECT, OPT_ADV_PAPER_PROT, OPT_STAPLE_DETECT, OPT_BG_COLOR, OPT_DROPOUT_COLOR, OPT_BUFF_MODE, OPT_PREPICK, OPT_OVERSCAN, OPT_SLEEP_TIME, OPT_OFF_TIME, OPT_DUPLEX_OFFSET, OPT_GREEN_OFFSET, OPT_BLUE_OFFSET, OPT_LOW_MEM, OPT_SIDE, OPT_HWDESKEWCROP, OPT_SWDESKEW, OPT_SWDESPECK, OPT_SWCROP, OPT_SWSKIP, OPT_HALT_ON_CANCEL, OPT_ENDORSER_GROUP, OPT_ENDORSER, OPT_ENDORSER_BITS, OPT_ENDORSER_VAL, OPT_ENDORSER_STEP, OPT_ENDORSER_Y, OPT_ENDORSER_FONT, OPT_ENDORSER_DIR, OPT_ENDORSER_SIDE, OPT_ENDORSER_STRING, OPT_SENSOR_GROUP, OPT_TOP, OPT_A3, OPT_B4, OPT_A4, OPT_B5, OPT_HOPPER, OPT_OMR, OPT_ADF_OPEN, OPT_CARD_LOADED, 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 }; /* used to control the max page-height, which varies by resolution */ struct y_size { int res; int len; }; struct fujitsu { /* --------------------------------------------------------------------- */ /* immutable values which are set during init of scanner. */ struct fujitsu *next; char device_name[1024]; /* The name of the device from sanei */ int missing; /* used to mark unplugged scanners */ /* --------------------------------------------------------------------- */ /* 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... */ char vendor_name[9]; /* raw data as returned by SCSI inquiry. */ char model_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 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[6]; /*one for each mode*/ int step_y_res[6]; /*one for each mode*/ int max_x_res; int max_y_res; int min_x_res; int min_y_res; int std_res[16]; /*some scanners only support a few resolutions*/ /* 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_mode[6]; /* mode specific */ /* --------------------------------------------------------------------- */ /* immutable values which are set during vndr VPD probing of the scanner */ /* members in order found in scsi data... */ int has_adf; int has_flatbed; int has_transparency; int has_duplex; int has_endorser_b; int has_barcode; int has_operator_panel; int has_endorser_f; int adbits; int buffer_bytes; /*supported scsi commands*/ int has_cmd_msen10; int has_cmd_msel10; int has_cmd_lsen; int has_cmd_lsel; int has_cmd_change; int has_cmd_rbuff; int has_cmd_wbuff; int has_cmd_cav; int has_cmd_comp; int has_cmd_gdbs; int has_cmd_op; int has_cmd_send; int has_cmd_read; int has_cmd_gwin; int has_cmd_swin; int has_cmd_sdiag; int has_cmd_rdiag; int has_cmd_scan; int has_cmd_msen6; int has_cmd_copy; int has_cmd_rel; int has_cmd_runit; int has_cmd_msel6; int has_cmd_inq; int has_cmd_rs; int has_cmd_tur; /*FIXME: there are more vendor cmds? */ int has_cmd_subwindow; int has_cmd_endorser; int has_cmd_hw_status; int has_cmd_hw_status_2; int has_cmd_hw_status_3; int has_cmd_scanner_ctl; int has_cmd_device_restart; /*FIXME: do we need the vendor window param list? */ int brightness_steps; int threshold_steps; int contrast_steps; int num_internal_gamma; int num_download_gamma; int num_internal_dither; int num_download_dither; int has_df_recovery; int has_paper_protect; int has_adv_paper_prot; int has_staple_detect; int has_rif; int has_dtc; int has_sdtc; int has_outline; int has_emphasis; int has_autosep; int has_mirroring; int has_wl_follow; int has_subwindow; int has_diffusion; int has_ipc3; int has_rotation; int has_hybrid_crop_deskew; int has_off_mode; 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; int has_op_halt; int has_return_path; /*FIXME: more endorser data? */ int endorser_type_f; int endorser_type_b; /*FIXME: barcode data? */ /* overscan size in pixels comes from scanner in basic res units */ int os_x_basic; int os_y_basic; /* --------------------------------------------------------------------- */ /* immutable values which are gathered by mode_sense command */ int has_MS_autocolor; int has_MS_prepick; int has_MS_sleep; int has_MS_duplex; int has_MS_rand; int has_MS_bg; int has_MS_df; int has_MS_dropout; /* dropout color specified in mode select data */ int has_MS_buff; int has_MS_auto; int has_MS_lamp; int has_MS_jobsep; /* --------------------------------------------------------------------- */ /* 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; struct y_size max_y_by_res[4]; int min_x; int min_y; int max_x_fb; int max_y_fb; 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 jpeg_interlace; /* different models interlace jpeg sides differently */ int cropping_mode; /* lower-end scanners don't crop from paper size */ int ghs_in_rs; int endorser_string_len; int has_pixelsize; int has_short_pixelsize; /* m3091/2 put weird stuff at end, ignore it */ int broken_diag_serial; /* some scanners are just plain borked */ int need_q_table; /* some scanners won't work without these */ int need_diag_preread; int hopper_before_op; /* some scanners don't like OP when hopper empty */ int no_wait_after_op; /* some scanners don't like TUR after OP */ int has_vuid_mono; /* mono set window data */ int has_vuid_3091; /* 3091/2 set window data */ int has_vuid_color; /* color set window data */ int reverse_by_mode[6]; /* mode specific */ int ppl_mod_by_mode[6]; /* mode specific scanline length limitation */ /* --------------------------------------------------------------------- */ /* immutable values which are set during serial number probing scanner */ char serial_name[28]; /* 16 char model, ':', 10 byte serial, null */ /* --------------------------------------------------------------------- */ /* struct with pointers to device/vendor/model names, and a type value */ /* used to inform sane frontend about the device */ SANE_Device sane; /* --------------------------------------------------------------------- */ /* 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 */ /* long array of option structs */ SANE_Option_Descriptor opt[NUM_OPTIONS]; /*mode group*/ SANE_String_Const mode_list[7]; SANE_String_Const source_list[8]; SANE_Int res_list[17]; SANE_Range res_range; /*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; /*enhancement group*/ SANE_Range brightness_range; SANE_Range contrast_range; SANE_Range gamma_range; SANE_Range threshold_range; /*ipc group*/ SANE_String_Const ht_type_list[4]; SANE_Range ht_pattern_range; SANE_Range emphasis_range; SANE_String_Const wl_follow_list[4]; SANE_Range gamma_curve_range; SANE_Range threshold_curve_range; SANE_Range variance_range; /*advanced group*/ SANE_String_Const compress_list[3]; SANE_Range compress_arg_range; SANE_String_Const df_action_list[4]; SANE_String_Const df_diff_list[5]; SANE_String_Const df_recovery_list[4]; SANE_String_Const paper_protect_list[4]; SANE_String_Const adv_paper_prot_list[4]; SANE_String_Const staple_detect_list[4]; SANE_String_Const bg_color_list[4]; SANE_String_Const do_color_list[5]; SANE_String_Const lamp_color_list[5]; SANE_String_Const buff_mode_list[4]; SANE_String_Const prepick_list[4]; SANE_String_Const overscan_list[4]; SANE_Range sleep_time_range; SANE_Range off_time_range; SANE_Range duplex_offset_range; SANE_Range green_offset_range; SANE_Range blue_offset_range; SANE_Range swdespeck_range; SANE_Range swskip_range; /*endorser group*/ SANE_Range endorser_bits_range; SANE_Range endorser_val_range; SANE_Range endorser_step_range; SANE_Range endorser_y_range; SANE_String_Const endorser_font_list[6]; SANE_String_Const endorser_dir_list[3]; SANE_String_Const endorser_side_list[3]; /* --------------------------------------------------------------------- */ /* changeable vars to hold user input. modified by SANE_Options above */ /*mode group*/ int u_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 contrast; double gamma; int threshold; /* ipc */ int rif; int ht_type; int ht_pattern; int outline; int emphasis; int separation; int mirroring; int wl_follow; /* ipc_mode=DTC */ int bp_filter; int smoothing; int gamma_curve; int threshold_curve; int threshold_white; int noise_removal; int matrix_5; int matrix_4; int matrix_3; int matrix_2; /* ipc_mode = SDTC */ int variance; /*advanced group*/ int awd; int ald; int compress; int compress_arg; int df_action; int df_skew; int df_thickness; int df_length; int df_diff; int df_recovery; int paper_protect; int adv_paper_prot; int staple_detect; int bg_color; int dropout_color; int buff_mode; int prepick; int overscan; int lamp_color; int sleep_time; int off_time; int duplex_offset; int green_offset; int blue_offset; int low_mem; int hwdeskewcrop; int swdeskew; int swdespeck; int swcrop; double swskip; int halt_on_cancel; /*endorser group*/ int u_endorser; int u_endorser_bits; int u_endorser_val; int u_endorser_step; int u_endorser_y; int u_endorser_font; int u_endorser_dir; int u_endorser_side; char u_endorser_string[81]; /*max length, plus null byte*/ /* --------------------------------------------------------------------- */ /* values which are derived from setting the options above */ /* the user never directly modifies these */ int s_mode; /*color,lineart,etc: sent to scanner*/ int window_gamma; /* depends on brightness/contrast and lut */ /* 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 u_params; SANE_Parameters s_params; /* --------------------------------------------------------------------- */ /* values which are set by scanning functions to keep track of pages, etc */ int started; int reading; int cancelled; int side; /* 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*/ int eof_rx[2]; int ili_rx[2]; int eom_rx; /* how far we have written */ int bytes_tx[2]; int eof_tx[2]; /*size of buffers (can be smaller than above*/ int buff_tot[2]; int buff_rx[2]; int buff_tx[2]; unsigned char * buffers[2]; /* --------------------------------------------------------------------- */ /*hardware feature bookkeeping*/ int req_driv_crop; int req_driv_lut; /* --------------------------------------------------------------------- */ /* values used by the software enhancement code (deskew, crop, etc) */ SANE_Status deskew_stat; int deskew_vals[2]; double deskew_slope; int crop_vals[4]; /* --------------------------------------------------------------------- */ /* values used by the compression functions, esp. jpeg with duplex */ int jpeg_stage; int jpeg_ff_offset; int jpeg_front_rst; int jpeg_back_rst; int jpeg_x_byte; /* --------------------------------------------------------------------- */ /* values which used by the command and data sending functions (scsi/usb)*/ int fd; /* The scanner device file descriptor. */ size_t rs_info; int rs_eom; int rs_ili; /* --------------------------------------------------------------------- */ /* values which are used by the get hardware status command */ 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_card_loaded; 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; /* values which are used to track the frontend's access to sensors */ char hw_data_avail[NUM_OPTIONS-OPT_TOP]; }; #define CONNECTION_SCSI 0 /* SCSI interface */ #define CONNECTION_USB 1 /* USB interface */ #define SIDE_FRONT 0 #define SIDE_BACK 1 #define SOURCE_FLATBED 0 #define SOURCE_ADF_FRONT 1 #define SOURCE_ADF_BACK 2 #define SOURCE_ADF_DUPLEX 3 #define SOURCE_CARD_FRONT 4 #define SOURCE_CARD_BACK 5 #define SOURCE_CARD_DUPLEX 6 #define COMP_NONE WD_cmp_NONE #define COMP_JPEG WD_cmp_JPG1 #define JPEG_STAGE_NONE 0 #define JPEG_STAGE_SOI 1 #define JPEG_STAGE_HEAD 2 #define JPEG_STAGE_SOF 3 #define JPEG_STAGE_SOS 4 #define JPEG_STAGE_FRONT 5 #define JPEG_STAGE_BACK 6 #define JPEG_STAGE_EOI 7 #define JFIF_APP0_LENGTH 18 /* 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 dropout scsi data to make code easier */ #define COLOR_DEFAULT 0 #define COLOR_GREEN 8 #define COLOR_RED 9 #define COLOR_BLUE 11 #define COLOR_WHITE 1 #define COLOR_BLACK 2 #define COLOR_INTERLACE_UNK 0 #define COLOR_INTERLACE_RGB 1 #define COLOR_INTERLACE_BGR 2 #define COLOR_INTERLACE_RRGGBB 3 #define COLOR_INTERLACE_3091 4 #define DUPLEX_INTERLACE_ALT 0 #define DUPLEX_INTERLACE_NONE 1 #define DUPLEX_INTERLACE_3091 2 #define JPEG_INTERLACE_ALT 0 #define JPEG_INTERLACE_NONE 1 #define CROP_RELATIVE 0 #define CROP_ABSOLUTE 1 #define DF_DEFAULT 0 #define DF_CONTINUE 1 #define DF_STOP 2 #define FONT_H 0 #define FONT_HB 1 #define FONT_HN 2 #define FONT_V 3 #define FONT_VB 4 #define DIR_TTB 0 #define DIR_BTT 1 /* endorser type, same as scsi inquiry data */ #define ET_OLD 0 #define ET_30 1 #define ET_40 2 /* ------------------------------------------------------------------------- */ #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 FUJITSU_CONFIG_FILE "fujitsu.conf" #ifndef PATH_MAX # define PATH_MAX 1024 #endif /* ------------------------------------------------------------------------- */ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize); SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only); SANE_Status sane_open (SANE_String_Const name, SANE_Handle * handle); SANE_Status sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking); SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int * fdp); 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, SANE_Action action, void *val, SANE_Int * info); SANE_Status sane_start (SANE_Handle handle); SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params); SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len); void sane_cancel (SANE_Handle h); void sane_close (SANE_Handle h); void sane_exit (void); /* ------------------------------------------------------------------------- */ 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 SANE_Status connect_fd (struct fujitsu *s); static SANE_Status disconnect_fd (struct fujitsu *s); 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_ms (struct fujitsu *s); static SANE_Status init_model (struct fujitsu *s); static SANE_Status init_user (struct fujitsu *s); static SANE_Status init_options (struct fujitsu *scanner); static SANE_Status init_interlace (struct fujitsu *scanner); static SANE_Status init_serial (struct fujitsu *scanner); 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 SANE_Status wait_scanner (struct fujitsu *s); static SANE_Status object_position (struct fujitsu *s, int action); static SANE_Status scanner_control (struct fujitsu *s, int function); static SANE_Status scanner_control_ric (struct fujitsu *s, int bytes, int side); static SANE_Status mode_select_df(struct fujitsu *s); static SANE_Status mode_select_dropout(struct fujitsu *s); static SANE_Status mode_select_bg(struct fujitsu *s); static SANE_Status mode_select_buff (struct fujitsu *s); static SANE_Status mode_select_prepick (struct fujitsu *s); static SANE_Status mode_select_auto (struct fujitsu *s); static SANE_Status set_sleep_mode(struct fujitsu *s); static SANE_Status set_off_mode(struct fujitsu *s); static int must_downsample (struct fujitsu *s); static int must_fully_buffer (struct fujitsu *s); static int get_page_width (struct fujitsu *s); static int get_page_height (struct fujitsu *s); static int get_ipc_mode (struct fujitsu *s); static int set_max_y (struct fujitsu *s); static SANE_Status send_lut (struct fujitsu *s); static SANE_Status send_endorser (struct fujitsu *s); static SANE_Status endorser (struct fujitsu *s); static SANE_Status set_window (struct fujitsu *s); static SANE_Status get_pixelsize(struct fujitsu *s, int actual); static SANE_Status update_params (struct fujitsu *s); static SANE_Status update_u_params (struct fujitsu *s); static SANE_Status start_scan (struct fujitsu *s); static SANE_Status check_for_cancel(struct fujitsu *s); static SANE_Status read_from_JPEGduplex(struct fujitsu *s); static SANE_Status read_from_3091duplex(struct fujitsu *s); static SANE_Status read_from_scanner(struct fujitsu *s, int side); static SANE_Status copy_3091(struct fujitsu *s, unsigned char * buf, int len, int side); static SANE_Status copy_JPEG(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 read_from_buffer(struct fujitsu *s, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len, int side); static SANE_Status downsample_from_buffer(struct fujitsu *s, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len, int side); static SANE_Status setup_buffers (struct fujitsu *s); static SANE_Status get_hardware_status (struct fujitsu *s, SANE_Int option); static SANE_Status buffer_deskew(struct fujitsu *s, int side); static SANE_Status buffer_crop(struct fujitsu *s, int side); static SANE_Status buffer_despeck(struct fujitsu *s, int side); static int buffer_isblank(struct fujitsu *s, int side); static void hexdump (int level, char *comment, unsigned char *p, int l); static size_t maxStringSize (const SANE_String_Const strings[]); #endif /* FUJITSU_H */ backends-1.3.0/backend/genesys.conf.in000066400000000000000000000050451456256263500176270ustar00rootroot00000000000000# genesys.conf: Configuration file for Genesys Logic GL646 and GL841 based scanners # # scanners that are not yet supported # uncomment them only for development purpose # # UMAX Astra 4500 and Avision iVina 1600 #usb 0x0638 0x0a10 # Hewlett Packard ScanJet 2400c usb 0x03f0 0x0a01 # Hewlett Packard ScanJet 3670/3690c usb 0x03f0 0x1405 # Plustek OpticPro ST24 #usb 0x07b3 0x0601 # Syscan DocketPort 465 #usb 0x0a82 0x4802 # # 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 # Canon LiDE 80 usb 0x04a9 0x2214 # Canon LiDE 90 usb 0x04a9 0x1900 # Canon 4400F # Disabled to prevent possible physical damage due to overheating (#436) #usb 0x04a9 0x2228 # Canon LiDE 100 usb 0x04a9 0x1904 # Canon LiDE 110 usb 0x04a9 0x1909 # Canon LiDE 200 usb 0x04a9 0x1905 # Canon LiDE 700F usb 0x04a9 0x1907 # Canon LiDE 210 usb 0x04a9 0x190a # Canon LiDE 120 usb 0x04a9 0x190e # Canon LiDE 220 usb 0x04a9 0x190f # Canon 5600F usb 0x04a9 0x1906 # Canon 8400F usb 0x04a9 0x221e # Canon 8600F usb 0x04a9 0x2229 # Visioneer Strobe XP200 usb 0x04a7 0x0426 # Visioneer Strobe XP300 usb 0x04a7 0x0474 # Ambir/Syscan DocketPort 665 usb 0x0a82 0x4803 # Visioneer Roadwarrior usb 0x04a7 0x0494 # Visioneer XP100 rev 3 usb 0x04a7 0x049b #Pentax DSmobile 600 usb 0x0a17 0x3210 usb 0x04f9 0x2038 # Syscan DocketPort 467 usb 0x1dcc 0x4812 # Syscan DocketPort 485 usb 0x0a82 0x4800 # DCT DocketPort 487 usb 0x1dcc 0x4810 # Syscan/Ambir DocketPort 685 usb 0x0a82 0x480c # Visioneer OneTouch 7100 usb 0x04a7 0x0229 # Xerox Travel Scanner 100 usb 0x04a7 0x04ac # Panasonic KV-SS080 usb 0x04da 0x100f # Hewlett Packard ScanJet 4850C usb 0x03f0 0x1b05 # Hewlett Packard ScanJet G4010 usb 0x03f0 0x4505 # Hewlett Packard ScanJet G4050 usb 0x03f0 0x4605 # Plustek OpticBook 3600 usb 0x07b3 0x0900 # Plustek OpticFilm 7200 usb 0x07b3 0x0807 # Plustek OpticFilm 7200 V2 usb 0x07b3 0x0c07 # Plustek OpticFilm 7200i usb 0x07b3 0x0c04 # Plustek OpticFilm 7300 usb 0x07b3 0x0c12 # Plustek OpticFilm 7400 usb 0x07b3 0x0c3a # Plustek OpticFilm 7500i usb 0x07b3 0x0c13 # Plustek OpticFilm 7600i usb 0x07b3 0x0c3b # Plustek OpticFilm 8100 usb 0x07b3 0x130c # Plustek OpticFilm 8200i usb 0x07b3 0x130d # Primax Electronics, Ltd Xerox 2400 Onetouch usb 0x0461 0x038b #Hewlett Packard ScanJet N6310 usb 0x03f0 0x4705 # Canon Image Formula 101 usb 0x1803 0x162e # Plustek OpticBook 3800 usb 0x07b3 0x1300 # Plustek OpticFilm 7600i usb 0x07b3 0x0c3b backends-1.3.0/backend/genesys/000077500000000000000000000000001456256263500163475ustar00rootroot00000000000000backends-1.3.0/backend/genesys/calibration.h000066400000000000000000000046361456256263500210200ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_CALIBRATION_H #define BACKEND_GENESYS_CALIBRATION_H #include "sensor.h" #include "settings.h" #include namespace genesys { struct Genesys_Calibration_Cache { Genesys_Calibration_Cache() = default; ~Genesys_Calibration_Cache() = default; // used to check if entry is compatible SetupParams params; std::time_t last_calibration = 0; Genesys_Frontend frontend; Genesys_Sensor sensor; ScanSession session; size_t average_size = 0; std::vector white_average_data; std::vector dark_average_data; bool operator==(const Genesys_Calibration_Cache& other) const { return params == other.params && last_calibration == other.last_calibration && frontend == other.frontend && sensor == other.sensor && session == other.session && average_size == other.average_size && white_average_data == other.white_average_data && dark_average_data == other.dark_average_data; } }; template void serialize(Stream& str, Genesys_Calibration_Cache& x) { serialize(str, x.params); serialize_newline(str); serialize(str, x.last_calibration); serialize_newline(str); serialize(str, x.frontend); serialize_newline(str); serialize(str, x.sensor); serialize_newline(str); serialize(str, x.session); serialize(str, x.average_size); serialize_newline(str); serialize(str, x.white_average_data); serialize_newline(str); serialize(str, x.dark_average_data); } } // namespace genesys #endif // BACKEND_GENESYS_CALIBRATION_H backends-1.3.0/backend/genesys/command_set.h000066400000000000000000000126501456256263500210150ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_COMMAND_SET_H #define BACKEND_GENESYS_COMMAND_SET_H #include "device.h" #include "fwd.h" #include namespace genesys { /** Scanner command set description. This description contains parts which are common to all scanners with the same command set, but may have different optical resolution and other parameters. */ class CommandSet { public: virtual ~CommandSet() = default; virtual bool needs_home_before_init_regs_for_scan(Genesys_Device* dev) const = 0; virtual void init(Genesys_Device* dev) const = 0; virtual void init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* regs) const = 0; virtual void init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const = 0; /** Set up registers for a scan. Similar to init_regs_for_scan except that the session is already computed from the session */ virtual void init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, const ScanSession& session) const= 0; virtual void set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set) const = 0; virtual void set_powersaving(Genesys_Device* dev, int delay) const = 0; virtual void save_power(Genesys_Device* dev, bool enable) const = 0; virtual void begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* regs, bool start_motor) const = 0; virtual void end_scan(Genesys_Device* dev, Genesys_Register_Set* regs, bool check_stop) const = 0; /** * Send gamma tables to ASIC */ virtual void send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const = 0; virtual void offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const = 0; virtual void coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs, int dpi) const = 0; virtual SensorExposure led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const = 0; virtual void wait_for_motor_stop(Genesys_Device* dev) const = 0; virtual void move_back_home(Genesys_Device* dev, bool wait_until_home) const = 0; // Updates hardware sensor information in Genesys_Scanner.val[]. virtual void update_hardware_sensors(struct Genesys_Scanner* s) const = 0; /** Needed on some chipsets before reading the status of the home sensor as the sensor may be controlled by additional GPIO registers. */ virtual void update_home_sensor_gpio(Genesys_Device& dev) const = 0; // functions for sheetfed scanners // load document into scanner virtual void load_document(Genesys_Device* dev) const = 0; /** Detects is the scanned document has left scanner. In this case it updates the amount of data to read and set up flags in the dev struct */ virtual void detect_document_end(Genesys_Device* dev) const = 0; /// eject document from scanner virtual void eject_document(Genesys_Device* dev) const = 0; /// write shading data calibration to ASIC virtual void send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t* data, int size) const = 0; virtual bool has_send_shading_data() const { return true; } /// calculate an instance of ScanSession for scanning with the given settings virtual ScanSession calculate_scan_session(const Genesys_Device* dev, const Genesys_Sensor& sensor, const Genesys_Settings& settings) const = 0; /// cold boot init function virtual void asic_boot(Genesys_Device* dev, bool cold) const = 0; /// checks if specific scan head is at home position virtual bool is_head_home(Genesys_Device& dev, ScanHeadId scan_head) const = 0; /// enables or disables XPA slider motor virtual void set_xpa_lamp_power(Genesys_Device& dev, bool set) const = 0; /// enables or disables XPA slider motor virtual void set_motor_mode(Genesys_Device& dev, Genesys_Register_Set& regs, MotorMode mode) const = 0; }; } // namespace genesys #endif // BACKEND_GENESYS_COMMAND_SET_H backends-1.3.0/backend/genesys/command_set_common.cpp000066400000000000000000000205641456256263500227230ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "command_set_common.h" #include "low.h" #include "value_filter.h" namespace genesys { CommandSetCommon::~CommandSetCommon() = default; bool CommandSetCommon::is_head_home(Genesys_Device& dev, ScanHeadId scan_head) const { struct HeadSettings { ModelId model_id; ScanHeadId scan_head; GenesysRegisterSettingSet regs; }; HeadSettings settings[] = { { ModelId::CANON_8600F, ScanHeadId::PRIMARY, { { 0x6c, 0x20, 0x60 }, { 0xa6, 0x00, 0x01 }, } }, { ModelId::CANON_8600F, ScanHeadId::SECONDARY, { { 0x6c, 0x00, 0x60 }, { 0xa6, 0x01, 0x01 }, } }, }; for (const auto& setting : settings) { if (setting.model_id == dev.model->model_id && setting.scan_head == scan_head) { auto reg_backup = apply_reg_settings_to_device_with_backup(dev, setting.regs); auto status = scanner_read_status(dev); apply_reg_settings_to_device(dev, reg_backup); return status.is_at_home; } } auto status = scanner_read_status(dev); return status.is_at_home; } void CommandSetCommon::set_xpa_lamp_power(Genesys_Device& dev, bool set) const { DBG_HELPER(dbg); struct LampSettings { ModelId model_id; ScanMethod scan_method; GenesysRegisterSettingSet regs_on; GenesysRegisterSettingSet regs_off; }; // FIXME: BUG: we're not clearing the registers to the previous state when returning back when // turning off the lamp LampSettings settings[] = { { ModelId::CANON_4400F, ScanMethod::TRANSPARENCY, {}, {} }, { ModelId::CANON_5600F, ScanMethod::TRANSPARENCY, {}, {} }, { ModelId::CANON_8400F, ScanMethod::TRANSPARENCY, { { 0xa6, 0x34, 0xf4 }, }, { { 0xa6, 0x40, 0x70 }, } }, { ModelId::CANON_8400F, ScanMethod::TRANSPARENCY_INFRARED, { { 0x6c, 0x40, 0x40 }, { 0xa6, 0x01, 0xff }, }, { { 0x6c, 0x00, 0x40 }, { 0xa6, 0x00, 0xff }, } }, { ModelId::CANON_8600F, ScanMethod::TRANSPARENCY, { { 0xa6, 0x34, 0xf4 }, { 0xa7, 0xe0, 0xe0 }, }, { { 0xa6, 0x40, 0x70 }, } }, { ModelId::CANON_8600F, ScanMethod::TRANSPARENCY_INFRARED, { { 0xa6, 0x00, 0xc0 }, { 0xa7, 0xe0, 0xe0 }, { 0x6c, 0x80, 0x80 }, }, { { 0xa6, 0x00, 0xc0 }, { 0x6c, 0x00, 0x80 }, } }, { ModelId::PLUSTEK_OPTICFILM_7200, ScanMethod::TRANSPARENCY, {}, {} }, { ModelId::PLUSTEK_OPTICFILM_7200I, ScanMethod::TRANSPARENCY, {}, {} }, { ModelId::PLUSTEK_OPTICFILM_7200I, ScanMethod::TRANSPARENCY_INFRARED, { { 0xa8, 0x07, 0x07 }, }, { { 0xa8, 0x00, 0x07 }, } }, { ModelId::PLUSTEK_OPTICFILM_7300, ScanMethod::TRANSPARENCY, {}, {} }, { ModelId::PLUSTEK_OPTICFILM_7400, ScanMethod::TRANSPARENCY, {}, {} }, { ModelId::PLUSTEK_OPTICFILM_7500I, ScanMethod::TRANSPARENCY, {}, {} }, { ModelId::PLUSTEK_OPTICFILM_7500I, ScanMethod::TRANSPARENCY_INFRARED, { { 0xa8, 0x07, 0x07 }, }, { { 0xa8, 0x00, 0x07 }, } }, { ModelId::PLUSTEK_OPTICFILM_8200I, ScanMethod::TRANSPARENCY, {}, {} }, { ModelId::PLUSTEK_OPTICFILM_8200I, ScanMethod::TRANSPARENCY_INFRARED, { { 0xa8, 0x04, 0x04 }, }, { { 0xa8, 0x00, 0x04 }, } }, }; for (const auto& setting : settings) { if (setting.model_id == dev.model->model_id && setting.scan_method == dev.settings.scan_method) { apply_reg_settings_to_device(dev, set ? setting.regs_on : setting.regs_off); return; } } throw SaneException("Could not find XPA lamp settings"); } void CommandSetCommon::set_motor_mode(Genesys_Device& dev, Genesys_Register_Set& regs, MotorMode mode) const { DBG_HELPER(dbg); struct MotorSettings { ModelId model_id; ValueFilterAny resolutions; GenesysRegisterSettingSet regs_primary_and_secondary; GenesysRegisterSettingSet regs_primary; GenesysRegisterSettingSet regs_secondary; }; MotorSettings settings[] = { { ModelId::CANON_8400F, { 400, 800, 1600, 3200 }, { { 0x6c, 0x00, 0x90 }, { 0xa9, 0x04, 0x06 }, }, { { 0x6c, 0x90, 0x90 }, { 0xa9, 0x02, 0x06 }, }, {} }, { ModelId::CANON_8600F, { 300, 600, 1200 }, { { 0x6c, 0x00, 0x60 }, { 0xa6, 0x01, 0x41 }, }, { { 0x6c, 0x20, 0x62 }, { 0xa6, 0x00, 0x41 }, }, { { 0x6c, 0x40, 0x62 }, { 0xa6, 0x01, 0x41 }, } }, { ModelId::CANON_8600F, { 2400, 4800 }, { { 0x6c, 0x02, 0x62 }, { 0xa6, 0x01, 0x41 }, }, { { 0x6c, 0x20, 0x62 }, { 0xa6, 0x00, 0x41 }, }, { { 0x6c, 0x40, 0x62 }, { 0xa6, 0x01, 0x41 }, } }, { ModelId::HP_SCANJET_G4050, VALUE_FILTER_ANY, { { 0x6b, 0x81, 0x81 }, // set MULTFILM and GPOADF { 0x6c, 0x00, 0x40 }, // note that reverse change is not applied on off // 0xa6 register 0x08 bit likely sets motor power. No move at all without that one { 0xa6, 0x08, 0x08 }, // note that reverse change is not applied on off { 0xa8, 0x00, 0x04 }, { 0xa9, 0x30, 0x30 }, }, { { 0x6b, 0x00, 0x01 }, // BUG: note that only ADF is unset { 0xa8, 0x04, 0x04 }, { 0xa9, 0x00, 0x10 }, // note that 0x20 bit is not reset }, {} }, { ModelId::PLUSTEK_OPTICFILM_7200, VALUE_FILTER_ANY, {}, {}, {} }, { ModelId::PLUSTEK_OPTICFILM_7200I, VALUE_FILTER_ANY, {}, {}, {} }, { ModelId::PLUSTEK_OPTICFILM_7300, VALUE_FILTER_ANY, {}, {}, {} }, { ModelId::PLUSTEK_OPTICFILM_7400, VALUE_FILTER_ANY, {}, {}, {} }, { ModelId::PLUSTEK_OPTICFILM_7500I, VALUE_FILTER_ANY, {}, {}, {} }, { ModelId::PLUSTEK_OPTICFILM_8200I, VALUE_FILTER_ANY, {}, {}, {} }, }; for (const auto& setting : settings) { if (setting.model_id == dev.model->model_id && setting.resolutions.matches(dev.session.output_resolution)) { switch (mode) { case MotorMode::PRIMARY: { apply_reg_settings_to_device(dev, setting.regs_primary); break; } case MotorMode::PRIMARY_AND_SECONDARY: { apply_reg_settings_to_device(dev, setting.regs_primary_and_secondary); break; } case MotorMode::SECONDARY: { apply_reg_settings_to_device(dev, setting.regs_secondary); break; } } regs.state.motor_mode = mode; return; } } throw SaneException("Motor settings have not been found"); } } // namespace genesys backends-1.3.0/backend/genesys/command_set_common.h000066400000000000000000000026111456256263500223610ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_COMMAND_SET_COMMON_H #define BACKEND_GENESYS_COMMAND_SET_COMMON_H #include "command_set.h" namespace genesys { /** Common command set functionality */ class CommandSetCommon : public CommandSet { public: ~CommandSetCommon() override; bool is_head_home(Genesys_Device& dev, ScanHeadId scan_head) const override; void set_xpa_lamp_power(Genesys_Device& dev, bool set) const override; void set_motor_mode(Genesys_Device& dev, Genesys_Register_Set& regs, MotorMode mode) const override; }; } // namespace genesys #endif // BACKEND_GENESYS_COMMAND_SET_COMMON_H backends-1.3.0/backend/genesys/device.cpp000066400000000000000000000241441456256263500203170ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "device.h" #include "command_set.h" #include "low.h" #include "utilities.h" namespace genesys { std::vector MethodResolutions::get_resolutions() const { std::vector ret; std::copy(resolutions_x.begin(), resolutions_x.end(), std::back_inserter(ret)); std::copy(resolutions_y.begin(), resolutions_y.end(), std::back_inserter(ret)); // sort in decreasing order std::sort(ret.begin(), ret.end(), std::greater()); ret.erase(std::unique(ret.begin(), ret.end()), ret.end()); return ret; } const MethodResolutions* Genesys_Model::get_resolution_settings_ptr(ScanMethod method) const { for (const auto& res_for_method : resolutions) { for (auto res_method : res_for_method.methods) { if (res_method == method) { return &res_for_method; } } } return nullptr; } const MethodResolutions& Genesys_Model::get_resolution_settings(ScanMethod method) const { const auto* ptr = get_resolution_settings_ptr(method); if (ptr) return *ptr; throw SaneException("Could not find resolution settings for method %d", static_cast(method)); } std::vector Genesys_Model::get_resolutions(ScanMethod method) const { return get_resolution_settings(method).get_resolutions(); } bool Genesys_Model::has_method(ScanMethod method) const { return get_resolution_settings_ptr(method) != nullptr; } Genesys_Device::~Genesys_Device() { clear(); } void Genesys_Device::clear() { calib_file.clear(); calibration_cache.clear(); white_average_data.clear(); dark_average_data.clear(); } ImagePipelineNodeBufferedCallableSource& Genesys_Device::get_pipeline_source() { return static_cast(pipeline.front()); } bool Genesys_Device::is_head_pos_known(ScanHeadId scan_head) const { switch (scan_head) { case ScanHeadId::PRIMARY: return is_head_pos_primary_known_; case ScanHeadId::SECONDARY: return is_head_pos_secondary_known_; case ScanHeadId::ALL: return is_head_pos_primary_known_ && is_head_pos_secondary_known_; default: throw SaneException("Unknown scan head ID"); } } unsigned Genesys_Device::head_pos(ScanHeadId scan_head) const { switch (scan_head) { case ScanHeadId::PRIMARY: return head_pos_primary_; case ScanHeadId::SECONDARY: return head_pos_secondary_; default: throw SaneException("Unknown scan head ID"); } } void Genesys_Device::set_head_pos_unknown(ScanHeadId scan_head) { if ((scan_head & ScanHeadId::PRIMARY) != ScanHeadId::NONE) { is_head_pos_primary_known_ = false; } if ((scan_head & ScanHeadId::SECONDARY) != ScanHeadId::NONE) { is_head_pos_secondary_known_ = false; } } void Genesys_Device::set_head_pos_zero(ScanHeadId scan_head) { if ((scan_head & ScanHeadId::PRIMARY) != ScanHeadId::NONE) { head_pos_primary_ = 0; is_head_pos_primary_known_ = true; } if ((scan_head & ScanHeadId::SECONDARY) != ScanHeadId::NONE) { head_pos_secondary_ = 0; is_head_pos_secondary_known_ = true; } } void Genesys_Device::advance_head_pos_by_session(ScanHeadId scan_head) { int motor_steps = session.params.starty + (session.params.lines * motor.base_ydpi) / session.params.yres; auto direction = has_flag(session.params.flags, ScanFlag::REVERSE) ? Direction::BACKWARD : Direction::FORWARD; advance_head_pos_by_steps(scan_head, direction, motor_steps); } static void advance_pos(unsigned& pos, Direction direction, unsigned offset) { if (direction == Direction::FORWARD) { pos += offset; } else { if (pos < offset) { throw SaneException("Trying to advance head behind the home sensor"); } pos -= offset; } } void Genesys_Device::advance_head_pos_by_steps(ScanHeadId scan_head, Direction direction, unsigned steps) { if ((scan_head & ScanHeadId::PRIMARY) != ScanHeadId::NONE) { if (!is_head_pos_primary_known_) { throw SaneException("Trying to advance head while scanhead position is not known"); } advance_pos(head_pos_primary_, direction, steps); } if ((scan_head & ScanHeadId::SECONDARY) != ScanHeadId::NONE) { if (!is_head_pos_secondary_known_) { throw SaneException("Trying to advance head while scanhead position is not known"); } advance_pos(head_pos_secondary_, direction, steps); } } void print_scan_position(std::ostream& out, const Genesys_Device& dev, ScanHeadId scan_head) { if (dev.is_head_pos_known(scan_head)) { out << dev.head_pos(scan_head); } else { out <<"(unknown)"; } } std::ostream& operator<<(std::ostream& out, const Genesys_Device& dev) { StreamStateSaver state_saver{out}; out << "Genesys_Device{\n" << std::hex << " vendorId: 0x" << dev.vendorId << '\n' << " productId: 0x" << dev.productId << '\n' << std::dec << " usb_mode: " << dev.usb_mode << '\n' << " file_name: " << dev.file_name << '\n' << " calib_file: " << dev.calib_file << '\n' << " force_calibration: " << dev.force_calibration << '\n' << " ignore_offsets: " << dev.ignore_offsets << '\n' << " model: (not printed)\n" << " reg: " << format_indent_braced_list(4, dev.reg) << '\n' << " initial_regs: " << format_indent_braced_list(4, dev.initial_regs) << '\n' << " settings: " << format_indent_braced_list(4, dev.settings) << '\n' << " frontend: " << format_indent_braced_list(4, dev.frontend) << '\n' << " frontend_initial: " << format_indent_braced_list(4, dev.frontend_initial) << '\n'; if (!dev.memory_layout.regs.empty()) { out << " memory_layout.regs: " << format_indent_braced_list(4, dev.memory_layout.regs) << '\n'; } out << " gpo.regs: " << format_indent_braced_list(4, dev.gpo.regs) << '\n' << " motor: " << format_indent_braced_list(4, dev.motor) << '\n' << " control[0..6]: " << std::hex << static_cast(dev.control[0]) << ' ' << static_cast(dev.control[1]) << ' ' << static_cast(dev.control[2]) << ' ' << static_cast(dev.control[3]) << ' ' << static_cast(dev.control[4]) << ' ' << static_cast(dev.control[5]) << '\n' << std::dec << " average_size: " << dev.average_size << '\n' << " calib_session: " << format_indent_braced_list(4, dev.calib_session) << '\n' << " gamma_override_tables[0].size(): " << dev.gamma_override_tables[0].size() << '\n' << " gamma_override_tables[1].size(): " << dev.gamma_override_tables[1].size() << '\n' << " gamma_override_tables[2].size(): " << dev.gamma_override_tables[2].size() << '\n' << " white_average_data.size(): " << dev.white_average_data.size() << '\n' << " dark_average_data.size(): " << dev.dark_average_data.size() << '\n' << " already_initialized: " << dev.already_initialized << '\n' << " scanhead_position[PRIMARY]: "; print_scan_position(out, dev, ScanHeadId::PRIMARY); out << '\n' << " scanhead_position[SECONDARY]: "; print_scan_position(out, dev, ScanHeadId::SECONDARY); out << '\n' << " read_active: " << dev.read_active << '\n' << " parking: " << dev.parking << '\n' << " document: " << dev.document << '\n' << " total_bytes_read: " << dev.total_bytes_read << '\n' << " total_bytes_to_read: " << dev.total_bytes_to_read << '\n' << " session: " << format_indent_braced_list(4, dev.session) << '\n' << " calibration_cache: (not printed)\n" << " line_count: " << dev.line_count << '\n' << " segment_order: " << format_indent_braced_list(4, format_vector_unsigned(4, dev.segment_order)) << '\n' << '}'; return out; } void apply_reg_settings_to_device_write_only(Genesys_Device& dev, const GenesysRegisterSettingSet& regs) { GenesysRegisterSettingSet backup; for (const auto& reg : regs) { dev.interface->write_register(reg.address, reg.value); } } void apply_reg_settings_to_device(Genesys_Device& dev, const GenesysRegisterSettingSet& regs) { apply_reg_settings_to_device_with_backup(dev, regs); } GenesysRegisterSettingSet apply_reg_settings_to_device_with_backup(Genesys_Device& dev, const GenesysRegisterSettingSet& regs) { GenesysRegisterSettingSet backup; for (const auto& reg : regs) { std::uint8_t old_val = dev.interface->read_register(reg.address); std::uint8_t new_val = (old_val & ~reg.mask) | (reg.value & reg.mask); dev.interface->write_register(reg.address, new_val); using SettingType = GenesysRegisterSettingSet::SettingType; backup.push_back(SettingType{reg.address, static_cast(old_val & reg.mask), reg.mask}); } return backup; } } // namespace genesys backends-1.3.0/backend/genesys/device.h000066400000000000000000000253751456256263500177730ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_DEVICE_H #define BACKEND_GENESYS_DEVICE_H #include "calibration.h" #include "command_set.h" #include "enums.h" #include "image_pipeline.h" #include "motor.h" #include "settings.h" #include "sensor.h" #include "register.h" #include "usb_device.h" #include "scanner_interface.h" #include "utilities.h" #include namespace genesys { struct Genesys_Gpo { Genesys_Gpo() = default; // Genesys_Gpo GpioId id = GpioId::UNKNOWN; /* GL646 and possibly others: - have the value registers at 0x66 and 0x67 - have the enable registers at 0x68 and 0x69 GL841, GL842, GL843, GL846, GL848 and possibly others: - have the value registers at 0x6c and 0x6d. - have the enable registers at 0x6e and 0x6f. */ GenesysRegisterSettingSet regs; }; struct MemoryLayout { // This is used on GL845, GL846, GL847 and GL124 which have special registers to define the // memory layout MemoryLayout() = default; ValueFilter models; GenesysRegisterSettingSet regs; }; struct MethodResolutions { std::vector methods; std::vector resolutions_x; std::vector resolutions_y; unsigned get_min_resolution_x() const { return *std::min_element(resolutions_x.begin(), resolutions_x.end()); } unsigned get_nearest_resolution_x(unsigned resolution) const { return *std::min_element(resolutions_x.begin(), resolutions_x.end(), [&](unsigned lhs, unsigned rhs) { return std::abs(static_cast(lhs) - static_cast(resolution)) < std::abs(static_cast(rhs) - static_cast(resolution)); }); } unsigned get_min_resolution_y() const { return *std::min_element(resolutions_y.begin(), resolutions_y.end()); } std::vector get_resolutions() const; }; /** @brief structure to describe a scanner model * This structure describes a model. It is composed of information on the * sensor, the motor, scanner geometry and flags to drive operation. */ struct Genesys_Model { Genesys_Model() = default; const char* name = nullptr; const char* vendor = nullptr; const char* model = nullptr; ModelId model_id = ModelId::UNKNOWN; AsicType asic_type = AsicType::UNKNOWN; // possible x and y resolutions for each method supported by the scanner std::vector resolutions; // possible depths in gray mode std::vector bpp_gray_values; // possible depths in color mode std::vector bpp_color_values; // the default scanning method. This is used when moving the head for example ScanMethod default_method = ScanMethod::FLATBED; // All offsets below are with respect to the sensor home position // Start of scan area in mm float x_offset = 0; // Start of scan area in mm (Amount of feeding needed to get to the medium) float y_offset = 0; // Size of scan area in mm float x_size = 0; // Size of scan area in mm float y_size = 0; // Start of white strip in mm for scanners that use separate dark and white shading calibration. float y_offset_calib_white = 0; // The size of the scan area that is used to acquire shading data in mm float y_size_calib_mm = 0; // Start of the black/white strip in mm for scanners that use unified dark and white shading // calibration. float y_offset_calib_dark_white_mm = 0; // The size of the scan area that is used to acquire dark/white shading data in mm float y_size_calib_dark_white_mm = 0; // The width of the scan area that is used to acquire shading data float x_size_calib_mm = 0; // Start of black mark in mm float x_offset_calib_black = 0; // Start of scan area in transparency mode in mm float x_offset_ta = 0; // Start of scan area in transparency mode in mm float y_offset_ta = 0; // Size of scan area in transparency mode in mm float x_size_ta = 0; // Size of scan area in transparency mode in mm float y_size_ta = 0; // The position of the sensor when it's aligned with the lamp for transparency scanning float y_offset_sensor_to_ta = 0; // Start of white strip in transparency mode in mm float y_offset_calib_white_ta = 0; // Start of black strip in transparency mode in mm float y_offset_calib_black_ta = 0; // The size of the scan area that is used to acquire shading data in transparency mode in mm float y_size_calib_ta_mm = 0; // Size of scan area after paper sensor stop sensing document in mm float post_scan = 0; // Amount of feeding needed to eject document after finishing scanning in mm float eject_feed = 0; // Line-distance correction (in pixel at motor base_ydpi) for CCD scanners SANE_Int ld_shift_r = 0; SANE_Int ld_shift_g = 0; SANE_Int ld_shift_b = 0; // Order of the CCD/CIS colors ColorOrder line_mode_color_order = ColorOrder::RGB; // Is this a CIS or CCD scanner? bool is_cis = false; // Is this sheetfed scanner? bool is_sheetfed = false; // sensor type SensorId sensor_id = SensorId::UNKNOWN; // Analog-Digital converter type AdcId adc_id = AdcId::UNKNOWN; // General purpose output type GpioId gpio_id = GpioId::UNKNOWN; // stepper motor type MotorId motor_id = MotorId::UNKNOWN; // Which customizations are needed for this scanner? ModelFlag flags = ModelFlag::NONE; // Button flags, described existing buttons for the model SANE_Word buttons = 0; // how many lines are used to search start position SANE_Int search_lines = 0; // returns nullptr if method is not supported const MethodResolutions* get_resolution_settings_ptr(ScanMethod method) const; // throws if method is not supported const MethodResolutions& get_resolution_settings(ScanMethod method) const; std::vector get_resolutions(ScanMethod method) const; bool has_method(ScanMethod method) const; }; /** * Describes the current device status for the backend * session. This should be more accurately called * Genesys_Session . */ struct Genesys_Device { Genesys_Device() = default; ~Genesys_Device(); using Calibration = std::vector; // frees commonly used data void clear(); std::uint16_t vendorId = 0; // USB vendor identifier std::uint16_t productId = 0; // USB product identifier // USB mode: // 0: not set // 1: USB 1.1 // 2: USB 2.0 SANE_Int usb_mode = 0; std::string file_name; std::string calib_file; // if enabled, no calibration data will be loaded or saved to files SANE_Int force_calibration = 0; // if enabled, will ignore the scan offsets and start scanning at true origin. This allows // acquiring the positions of the black and white strips and the actual scan area bool ignore_offsets = false; const Genesys_Model* model = nullptr; // pointers to low level functions std::unique_ptr cmd_set; Genesys_Register_Set reg; Genesys_Register_Set initial_regs; Genesys_Settings settings; Genesys_Frontend frontend, frontend_initial; Genesys_Gpo gpo; MemoryLayout memory_layout; Genesys_Motor motor; std::uint8_t control[6] = {}; size_t average_size = 0; // the session that was configured for calibration ScanSession calib_session; // gamma overrides. If a respective array is not empty then it means that the gamma for that // color is overridden. std::vector gamma_override_tables[3]; std::vector white_average_data; std::vector dark_average_data; bool already_initialized = false; bool read_active = false; // signal whether the park command has been issued bool parking = false; // for sheetfed scanner's, is TRUE when there is a document in the scanner bool document = false; // total bytes read sent to frontend size_t total_bytes_read = 0; // total bytes read to be sent to frontend size_t total_bytes_to_read = 0; // contains computed data for the current setup ScanSession session; Calibration calibration_cache; // number of scan lines used during scan int line_count = 0; // array describing the order of the sub-segments of the sensor std::vector segment_order; // stores information about how the input image should be processed ImagePipelineStack pipeline; // an buffer that allows reading from `pipeline` in chunks of any size ImageBuffer pipeline_buffer; ImagePipelineNodeBufferedCallableSource& get_pipeline_source(); std::unique_ptr interface; bool is_head_pos_known(ScanHeadId scan_head) const; unsigned head_pos(ScanHeadId scan_head) const; void set_head_pos_unknown(ScanHeadId scan_head); void set_head_pos_zero(ScanHeadId scan_head); void advance_head_pos_by_session(ScanHeadId scan_head); void advance_head_pos_by_steps(ScanHeadId scan_head, Direction direction, unsigned steps); private: // the position of the primary scan head in motor->base_dpi units unsigned head_pos_primary_ = 0; bool is_head_pos_primary_known_ = true; // the position of the secondary scan head in motor->base_dpi units. Only certain scanners // have a secondary scan head. unsigned head_pos_secondary_ = 0; bool is_head_pos_secondary_known_ = true; friend class ScannerInterfaceUsb; }; std::ostream& operator<<(std::ostream& out, const Genesys_Device& dev); void apply_reg_settings_to_device(Genesys_Device& dev, const GenesysRegisterSettingSet& regs); void apply_reg_settings_to_device_write_only(Genesys_Device& dev, const GenesysRegisterSettingSet& regs); GenesysRegisterSettingSet apply_reg_settings_to_device_with_backup(Genesys_Device& dev, const GenesysRegisterSettingSet& regs); } // namespace genesys #endif backends-1.3.0/backend/genesys/enums.cpp000066400000000000000000000441701456256263500202100ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "enums.h" #include "genesys.h" #include namespace genesys { const char* scan_method_to_option_string(ScanMethod method) { switch (method) { case ScanMethod::FLATBED: return STR_FLATBED; case ScanMethod::TRANSPARENCY: return STR_TRANSPARENCY_ADAPTER; case ScanMethod::TRANSPARENCY_INFRARED: return STR_TRANSPARENCY_ADAPTER_INFRARED; } throw SaneException("Unknown scan method %d", static_cast(method)); } ScanMethod option_string_to_scan_method(const std::string& str) { if (str == STR_FLATBED) { return ScanMethod::FLATBED; } else if (str == STR_TRANSPARENCY_ADAPTER) { return ScanMethod::TRANSPARENCY; } else if (str == STR_TRANSPARENCY_ADAPTER_INFRARED) { return ScanMethod::TRANSPARENCY_INFRARED; } throw SaneException("Unknown scan method option %s", str.c_str()); } const char* scan_color_mode_to_option_string(ScanColorMode mode) { switch (mode) { case ScanColorMode::COLOR_SINGLE_PASS: return SANE_VALUE_SCAN_MODE_COLOR; case ScanColorMode::GRAY: return SANE_VALUE_SCAN_MODE_GRAY; case ScanColorMode::HALFTONE: return SANE_VALUE_SCAN_MODE_HALFTONE; case ScanColorMode::LINEART: return SANE_VALUE_SCAN_MODE_LINEART; } throw SaneException("Unknown scan mode %d", static_cast(mode)); } ScanColorMode option_string_to_scan_color_mode(const std::string& str) { if (str == SANE_VALUE_SCAN_MODE_COLOR) { return ScanColorMode::COLOR_SINGLE_PASS; } else if (str == SANE_VALUE_SCAN_MODE_GRAY) { return ScanColorMode::GRAY; } else if (str == SANE_VALUE_SCAN_MODE_HALFTONE) { return ScanColorMode::HALFTONE; } else if (str == SANE_VALUE_SCAN_MODE_LINEART) { return ScanColorMode::LINEART; } throw SaneException("Unknown scan color mode %s", str.c_str()); } std::ostream& operator<<(std::ostream& out, ColorFilter mode) { switch (mode) { case ColorFilter::RED: out << "RED"; break; case ColorFilter::GREEN: out << "GREEN"; break; case ColorFilter::BLUE: out << "BLUE"; break; case ColorFilter::NONE: out << "NONE"; break; default: out << static_cast(mode); break; } return out; } std::ostream& operator<<(std::ostream& out, ModelId id) { switch (id) { case ModelId::UNKNOWN: out << "UNKNOWN"; break; case ModelId::CANON_4400F: out << "CANON_4400F"; break; case ModelId::CANON_5600F: out << "CANON_5600F"; break; case ModelId::CANON_8400F: out << "CANON_8400F"; break; case ModelId::CANON_8600F: out << "CANON_8600F"; break; case ModelId::CANON_IMAGE_FORMULA_101: out << "CANON_IMAGE_FORMULA_101"; break; case ModelId::CANON_LIDE_50: out << "CANON_LIDE_50"; break; case ModelId::CANON_LIDE_60: out << "CANON_LIDE_60"; break; case ModelId::CANON_LIDE_80: out << "CANON_LIDE_80"; break; case ModelId::CANON_LIDE_90: out << "CANON_LIDE_90"; break; case ModelId::CANON_LIDE_100: out << "CANON_LIDE_100"; break; case ModelId::CANON_LIDE_110: out << "CANON_LIDE_110"; break; case ModelId::CANON_LIDE_120: out << "CANON_LIDE_120"; break; case ModelId::CANON_LIDE_200: out << "CANON_LIDE_200"; break; case ModelId::CANON_LIDE_210: out << "CANON_LIDE_210"; break; case ModelId::CANON_LIDE_220: out << "CANON_LIDE_220"; break; case ModelId::CANON_LIDE_700F: out << "CANON_LIDE_700F"; break; case ModelId::DCT_DOCKETPORT_487: out << "DCT_DOCKETPORT_487"; break; case ModelId::HP_SCANJET_2300C: out << "HP_SCANJET_2300C"; break; case ModelId::HP_SCANJET_2400C: out << "HP_SCANJET_2400C"; break; case ModelId::HP_SCANJET_3670: out << "HP_SCANJET_3670"; break; case ModelId::HP_SCANJET_4850C: out << "HP_SCANJET_4850C"; break; case ModelId::HP_SCANJET_G4010: out << "HP_SCANJET_G4010"; break; case ModelId::HP_SCANJET_G4050: out << "HP_SCANJET_G4050"; break; case ModelId::HP_SCANJET_N6310: out << "HP_SCANJET_N6310"; break; case ModelId::MEDION_MD5345: out << "MEDION_MD5345"; break; case ModelId::PANASONIC_KV_SS080: out << "PANASONIC_KV_SS080"; break; case ModelId::PENTAX_DSMOBILE_600: out << "PENTAX_DSMOBILE_600"; break; case ModelId::PLUSTEK_OPTICBOOK_3800: out << "PLUSTEK_OPTICBOOK_3800"; break; case ModelId::PLUSTEK_OPTICFILM_7200: out << "PLUSTEK_OPTICFILM_7200"; break; case ModelId::PLUSTEK_OPTICFILM_7200I: out << "PLUSTEK_OPTICFILM_7200I"; break; case ModelId::PLUSTEK_OPTICFILM_7300: out << "PLUSTEK_OPTICFILM_7300"; break; case ModelId::PLUSTEK_OPTICFILM_7400: out << "PLUSTEK_OPTICFILM_7400"; break; case ModelId::PLUSTEK_OPTICFILM_7500I: out << "PLUSTEK_OPTICFILM_7500I"; break; case ModelId::PLUSTEK_OPTICFILM_8200I: out << "PLUSTEK_OPTICFILM_8200I"; break; case ModelId::PLUSTEK_OPTICPRO_3600: out << "PLUSTEK_OPTICPRO_3600"; break; case ModelId::PLUSTEK_OPTICPRO_ST12: out << "PLUSTEK_OPTICPRO_ST12"; break; case ModelId::PLUSTEK_OPTICPRO_ST24: out << "PLUSTEK_OPTICPRO_ST24"; break; case ModelId::SYSCAN_DOCKETPORT_465: out << "SYSCAN_DOCKETPORT_465"; break; case ModelId::SYSCAN_DOCKETPORT_467: out << "SYSCAN_DOCKETPORT_467"; break; case ModelId::SYSCAN_DOCKETPORT_485: out << "SYSCAN_DOCKETPORT_485"; break; case ModelId::SYSCAN_DOCKETPORT_665: out << "SYSCAN_DOCKETPORT_665"; break; case ModelId::SYSCAN_DOCKETPORT_685: out << "SYSCAN_DOCKETPORT_685"; break; case ModelId::UMAX_ASTRA_4500: out << "UMAX_ASTRA_4500"; break; case ModelId::VISIONEER_7100: out << "VISIONEER_7100"; break; case ModelId::VISIONEER_ROADWARRIOR: out << "VISIONEER_ROADWARRIOR"; break; case ModelId::VISIONEER_STROBE_XP100_REVISION3: out << "VISIONEER_STROBE_XP100_REVISION3"; break; case ModelId::VISIONEER_STROBE_XP200: out << "VISIONEER_STROBE_XP200"; break; case ModelId::VISIONEER_STROBE_XP300: out << "VISIONEER_STROBE_XP300"; break; case ModelId::XEROX_2400: out << "XEROX_2400"; break; case ModelId::XEROX_TRAVELSCANNER_100: out << "XEROX_TRAVELSCANNER_100"; break; default: out << static_cast(id); break; } return out; } std::ostream& operator<<(std::ostream& out, SensorId id) { switch (id) { case SensorId::CCD_5345: out << "CCD_5345"; break; case SensorId::CCD_CANON_4400F: out << "CCD_CANON_4400F"; break; case SensorId::CCD_CANON_5600F: out << "CCD_CANON_5600F"; break; case SensorId::CCD_CANON_8400F: out << "CCD_CANON_8400F"; break; case SensorId::CCD_CANON_8600F: out << "CCD_CANON_8600F"; break; case SensorId::CCD_DP665: out << "CCD_DP665"; break; case SensorId::CCD_DP685: out << "CCD_DP685"; break; case SensorId::CCD_DSMOBILE600: out << "CCD_DSMOBILE600"; break; case SensorId::CCD_DOCKETPORT_487: out << "CCD_DOCKETPORT_487"; break; case SensorId::CCD_G4050: out << "CCD_G4050"; break; case SensorId::CCD_HP2300: out << "CCD_HP2300"; break; case SensorId::CCD_HP2400: out << "CCD_HP2400"; break; case SensorId::CCD_HP3670: out << "CCD_HP3670"; break; case SensorId::CCD_HP_N6310: out << "CCD_HP_N6310"; break; case SensorId::CCD_HP_4850C: out << "CCD_HP_4850C"; break; case SensorId::CCD_IMG101: out << "CCD_IMG101"; break; case SensorId::CCD_KVSS080: out << "CCD_KVSS080"; break; case SensorId::CCD_PLUSTEK_OPTICBOOK_3800: out << "CCD_PLUSTEK_OPTICBOOK_3800"; break; case SensorId::CCD_PLUSTEK_OPTICFILM_7200: out << "CCD_PLUSTEK_OPTICFILM_7200"; break; case SensorId::CCD_PLUSTEK_OPTICFILM_7200I: out << "CCD_PLUSTEK_OPTICFILM_7200I"; break; case SensorId::CCD_PLUSTEK_OPTICFILM_7300: out << "CCD_PLUSTEK_OPTICFILM_7300"; break; case SensorId::CCD_PLUSTEK_OPTICFILM_7400: out << "CCD_PLUSTEK_OPTICFILM_7400"; break; case SensorId::CCD_PLUSTEK_OPTICFILM_7500I: out << "CCD_PLUSTEK_OPTICFILM_7500I"; break; case SensorId::CCD_PLUSTEK_OPTICFILM_8200I: out << "CCD_PLUSTEK_OPTICFILM_8200I"; break; case SensorId::CCD_PLUSTEK_OPTICPRO_3600: out << "CCD_PLUSTEK_OPTICPRO_3600"; break; case SensorId::CCD_ROADWARRIOR: out << "CCD_ROADWARRIOR"; break; case SensorId::CCD_ST12: out << "CCD_ST12"; break; case SensorId::CCD_ST24: out << "CCD_ST24"; break; case SensorId::CCD_UMAX: out << "CCD_UMAX"; break; case SensorId::CCD_XP300: out << "CCD_XP300"; break; case SensorId::CIS_CANON_LIDE_35: out << "CIS_CANON_LIDE_35"; break; case SensorId::CIS_CANON_LIDE_60: out << "CIS_CANON_LIDE_60"; break; case SensorId::CIS_CANON_LIDE_80: out << "CIS_CANON_LIDE_80"; break; case SensorId::CIS_CANON_LIDE_90: out << "CIS_CANON_LIDE_90"; break; case SensorId::CIS_CANON_LIDE_100: out << "CIS_CANON_LIDE_100"; break; case SensorId::CIS_CANON_LIDE_110: out << "CIS_CANON_LIDE_110"; break; case SensorId::CIS_CANON_LIDE_120: out << "CIS_CANON_LIDE_120"; break; case SensorId::CIS_CANON_LIDE_200: out << "CIS_CANON_LIDE_200"; break; case SensorId::CIS_CANON_LIDE_210: out << "CIS_CANON_LIDE_210"; break; case SensorId::CIS_CANON_LIDE_220: out << "CIS_CANON_LIDE_220"; break; case SensorId::CIS_CANON_LIDE_700F: out << "CIS_CANON_LIDE_700F"; break; case SensorId::CIS_XP200: out << "CIS_XP200"; break; default: out << static_cast(id); break; } return out; } std::ostream& operator<<(std::ostream& out, AdcId id) { switch (id) { case AdcId::UNKNOWN: out << "UNKNOWN"; break; case AdcId::AD_XP200: out << "AD_XP200"; break; case AdcId::CANON_LIDE_35: out << "CANON_LIDE_35"; break; case AdcId::CANON_LIDE_80: out << "CANON_LIDE_80"; break; case AdcId::CANON_LIDE_90: out << "CANON_LIDE_90"; break; case AdcId::CANON_LIDE_110: out << "CANON_LIDE_110"; break; case AdcId::CANON_LIDE_120: out << "CANON_LIDE_120"; break; case AdcId::CANON_LIDE_200: out << "CANON_LIDE_200"; break; case AdcId::CANON_LIDE_700F: out << "CANON_LIDE_700F"; break; case AdcId::CANON_4400F: out << "CANON_4400F"; break; case AdcId::CANON_5600F: out << "CANON_5600F"; break; case AdcId::CANON_8400F: out << "CANON_8400F"; break; case AdcId::CANON_8600F: out << "CANON_8600F"; break; case AdcId::G4050: out << "G4050"; break; case AdcId::IMG101: out << "IMG101"; break; case AdcId::KVSS080: out << "KVSS080"; break; case AdcId::PLUSTEK_OPTICBOOK_3800: out << "PLUSTEK_OPTICBOOK_3800"; break; case AdcId::PLUSTEK_OPTICFILM_7200: out << "PLUSTEK_OPTICFILM_7200"; break; case AdcId::PLUSTEK_OPTICFILM_7200I: out << "PLUSTEK_OPTICFILM_7200I"; break; case AdcId::PLUSTEK_OPTICFILM_7300: out << "PLUSTEK_OPTICFILM_7300"; break; case AdcId::PLUSTEK_OPTICFILM_7400: out << "PLUSTEK_OPTICFILM_7400"; break; case AdcId::PLUSTEK_OPTICFILM_7500I: out << "PLUSTEK_OPTICFILM_7500I"; break; case AdcId::PLUSTEK_OPTICFILM_8200I: out << "PLUSTEK_OPTICFILM_8200I"; break; case AdcId::PLUSTEK_OPTICPRO_3600: out << "PLUSTEK_OPTICPRO_3600"; break; case AdcId::WOLFSON_5345: out << "WOLFSON_5345"; break; case AdcId::WOLFSON_DSM600: out << "WOLFSON_DSM600"; break; case AdcId::WOLFSON_HP2300: out << "WOLFSON_HP2300"; break; case AdcId::WOLFSON_HP2400: out << "WOLFSON_HP2400"; break; case AdcId::WOLFSON_HP3670: out << "WOLFSON_HP3670"; break; case AdcId::WOLFSON_ST12: out << "WOLFSON_ST12"; break; case AdcId::WOLFSON_ST24: out << "WOLFSON_ST24"; break; case AdcId::WOLFSON_UMAX: out << "WOLFSON_UMAX"; break; case AdcId::WOLFSON_XP300: out << "WOLFSON_XP300"; break; default: out << static_cast(id); break; } return out; } std::ostream& operator<<(std::ostream& out, GpioId id) { switch (id) { case GpioId::UNKNOWN: out << "UNKNOWN"; break; case GpioId::CANON_LIDE_35: out << "CANON_LIDE_35"; break; case GpioId::CANON_LIDE_80: out << "CANON_LIDE_80"; break; case GpioId::CANON_LIDE_90: out << "CANON_LIDE_90"; break; case GpioId::CANON_LIDE_110: out << "CANON_LIDE_110"; break; case GpioId::CANON_LIDE_120: out << "CANON_LIDE_120"; break; case GpioId::CANON_LIDE_200: out << "CANON_LIDE_200"; break; case GpioId::CANON_LIDE_210: out << "CANON_LIDE_210"; break; case GpioId::CANON_LIDE_700F: out << "CANON_LIDE_700F"; break; case GpioId::CANON_4400F: out << "CANON_4400F"; break; case GpioId::CANON_5600F: out << "CANON_5600F"; break; case GpioId::CANON_8400F: out << "CANON_8400F"; break; case GpioId::CANON_8600F: out << "CANON_8600F"; break; case GpioId::DP665: out << "DP665"; break; case GpioId::DP685: out << "DP685"; break; case GpioId::G4010: out << "G4010"; break; case GpioId::G4050: out << "G4050"; break; case GpioId::HP2300: out << "HP2300"; break; case GpioId::HP2400: out << "HP2400"; break; case GpioId::HP3670: out << "HP3670"; break; case GpioId::HP_N6310: out << "HP_N6310"; break; case GpioId::IMG101: out << "IMG101"; break; case GpioId::KVSS080: out << "KVSS080"; break; case GpioId::MD_5345: out << "MD_5345"; break; case GpioId::PLUSTEK_OPTICBOOK_3800: out << "PLUSTEK_OPTICBOOK_3800"; break; case GpioId::PLUSTEK_OPTICFILM_7200I: out << "PLUSTEK_OPTICFILM_7200I"; break; case GpioId::PLUSTEK_OPTICFILM_7300: out << "PLUSTEK_OPTICFILM_7300"; break; case GpioId::PLUSTEK_OPTICFILM_7400: out << "PLUSTEK_OPTICFILM_7400"; break; case GpioId::PLUSTEK_OPTICFILM_7500I: out << "PLUSTEK_OPTICFILM_7500I"; break; case GpioId::PLUSTEK_OPTICFILM_8200I: out << "PLUSTEK_OPTICFILM_8200I"; break; case GpioId::PLUSTEK_OPTICPRO_3600: out << "PLUSTEK_OPTICPRO_3600"; break; case GpioId::ST12: out << "ST12"; break; case GpioId::ST24: out << "ST24"; break; case GpioId::UMAX: out << "UMAX"; break; case GpioId::XP200: out << "XP200"; break; case GpioId::XP300: out << "XP300"; break; default: out << static_cast(id); break; } return out; } std::ostream& operator<<(std::ostream& out, MotorId id) { switch (id) { case MotorId::UNKNOWN: out << "UNKNOWN"; break; case MotorId::CANON_LIDE_90: out << "CANON_LIDE_90"; break; case MotorId::CANON_LIDE_100: out << "CANON_LIDE_100"; break; case MotorId::CANON_LIDE_110: out << "CANON_LIDE_110"; break; case MotorId::CANON_LIDE_120: out << "CANON_LIDE_120"; break; case MotorId::CANON_LIDE_200: out << "CANON_LIDE_200"; break; case MotorId::CANON_LIDE_210: out << "CANON_LIDE_210"; break; case MotorId::CANON_LIDE_35: out << "CANON_LIDE_35"; break; case MotorId::CANON_LIDE_60: out << "CANON_LIDE_60"; break; case MotorId::CANON_LIDE_700: out << "CANON_LIDE_700"; break; case MotorId::CANON_LIDE_80: out << "CANON_LIDE_80"; break; case MotorId::CANON_4400F: out << "CANON_4400F"; break; case MotorId::CANON_5600F: out << "CANON_5600F"; break; case MotorId::CANON_8400F: out << "CANON_8400F"; break; case MotorId::CANON_8600F: out << "CANON_8600F"; break; case MotorId::DP665: out << "DP665"; break; case MotorId::DSMOBILE_600: out << "DSMOBILE_600"; break; case MotorId::G4050: out << "G4050"; break; case MotorId::HP2300: out << "HP2300"; break; case MotorId::HP2400: out << "HP2400"; break; case MotorId::HP3670: out << "HP3670"; break; case MotorId::IMG101: out << "IMG101"; break; case MotorId::KVSS080: out << "KVSS080"; break; case MotorId::MD_5345: out << "MD_5345"; break; case MotorId::PLUSTEK_OPTICBOOK_3800: out << "PLUSTEK_OPTICBOOK_3800"; break; case MotorId::PLUSTEK_OPTICFILM_7200: out << "PLUSTEK_OPTICFILM_7200"; break; case MotorId::PLUSTEK_OPTICFILM_7200I: out << "PLUSTEK_OPTICFILM_7200I"; break; case MotorId::PLUSTEK_OPTICFILM_7300: out << "PLUSTEK_OPTICFILM_7300"; break; case MotorId::PLUSTEK_OPTICFILM_7400: out << "PLUSTEK_OPTICFILM_7400"; break; case MotorId::PLUSTEK_OPTICFILM_7500I: out << "PLUSTEK_OPTICFILM_7500I"; break; case MotorId::PLUSTEK_OPTICFILM_8200I: out << "PLUSTEK_OPTICFILM_8200I"; break; case MotorId::PLUSTEK_OPTICPRO_3600: out << "PLUSTEK_OPTICPRO_3600"; break; case MotorId::ROADWARRIOR: out << "ROADWARRIOR"; break; case MotorId::ST24: out << "ST24"; break; case MotorId::UMAX: out << "UMAX"; break; case MotorId::XP200: out << "XP200"; break; case MotorId::XP300: out << "XP300"; break; default: out << static_cast(id); break; } return out; } std::ostream& operator<<(std::ostream& out, StepType type) { switch (type) { case StepType::FULL: out << "1/1"; break; case StepType::HALF: out << "1/2"; break; case StepType::QUARTER: out << "1/4"; break; case StepType::EIGHTH: out << "1/8"; break; default: out << static_cast(type); break; } return out; } std::ostream& operator<<(std::ostream& out, ScanFlag flags) { StreamStateSaver state_saver{out}; out << "0x" << std::hex << static_cast(flags); return out; } } // namespace genesys backends-1.3.0/backend/genesys/enums.h000066400000000000000000000342671456256263500176630ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_ENUMS_H #define BACKEND_GENESYS_ENUMS_H #include #include "serialize.h" namespace genesys { enum class ScanMethod : unsigned { // normal scan method FLATBED = 0, // scan using transparency adaptor TRANSPARENCY = 1, // scan using transparency adaptor via infrared channel TRANSPARENCY_INFRARED = 2 }; inline std::ostream& operator<<(std::ostream& out, ScanMethod mode) { switch (mode) { case ScanMethod::FLATBED: out << "FLATBED"; return out; case ScanMethod::TRANSPARENCY: out << "TRANSPARENCY"; return out; case ScanMethod::TRANSPARENCY_INFRARED: out << "TRANSPARENCY_INFRARED"; return out; } return out; } inline void serialize(std::istream& str, ScanMethod& x) { unsigned value; serialize(str, value); x = static_cast(value); } inline void serialize(std::ostream& str, ScanMethod& x) { unsigned value = static_cast(x); serialize(str, value); } const char* scan_method_to_option_string(ScanMethod method); ScanMethod option_string_to_scan_method(const std::string& str); enum class ScanColorMode : unsigned { LINEART = 0, HALFTONE, GRAY, COLOR_SINGLE_PASS }; inline std::ostream& operator<<(std::ostream& out, ScanColorMode mode) { switch (mode) { case ScanColorMode::LINEART: out << "LINEART"; return out; case ScanColorMode::HALFTONE: out << "HALFTONE"; return out; case ScanColorMode::GRAY: out << "GRAY"; return out; case ScanColorMode::COLOR_SINGLE_PASS: out << "COLOR_SINGLE_PASS"; return out; } return out; } inline void serialize(std::istream& str, ScanColorMode& x) { unsigned value; serialize(str, value); x = static_cast(value); } inline void serialize(std::ostream& str, ScanColorMode& x) { unsigned value = static_cast(x); serialize(str, value); } const char* scan_color_mode_to_option_string(ScanColorMode mode); ScanColorMode option_string_to_scan_color_mode(const std::string& str); enum class ScanHeadId : unsigned { NONE = 0, PRIMARY = 1 << 0, SECONDARY = 1 << 1, ALL = PRIMARY | SECONDARY, }; inline ScanHeadId operator|(ScanHeadId left, ScanHeadId right) { return static_cast(static_cast(left) | static_cast(right)); } inline ScanHeadId operator&(ScanHeadId left, ScanHeadId right) { return static_cast(static_cast(left) & static_cast(right)); } enum class ColorFilter : unsigned { RED = 0, GREEN, BLUE, NONE }; std::ostream& operator<<(std::ostream& out, ColorFilter mode); inline void serialize(std::istream& str, ColorFilter& x) { unsigned value; serialize(str, value); x = static_cast(value); } inline void serialize(std::ostream& str, ColorFilter& x) { unsigned value = static_cast(x); serialize(str, value); } enum class ColorOrder { RGB, GBR, BGR, }; /* Enum value naming conventions: Full name must be included with the following exceptions: Canon scanners omit "Canoscan" if present */ enum class ModelId : unsigned { UNKNOWN = 0, CANON_4400F, CANON_5600F, CANON_8400F, CANON_8600F, CANON_IMAGE_FORMULA_101, CANON_LIDE_50, CANON_LIDE_60, CANON_LIDE_80, CANON_LIDE_90, CANON_LIDE_100, CANON_LIDE_110, CANON_LIDE_120, CANON_LIDE_200, CANON_LIDE_210, CANON_LIDE_220, CANON_LIDE_700F, DCT_DOCKETPORT_487, HP_SCANJET_2300C, HP_SCANJET_2400C, HP_SCANJET_3670, HP_SCANJET_4850C, HP_SCANJET_G4010, HP_SCANJET_G4050, HP_SCANJET_N6310, MEDION_MD5345, PANASONIC_KV_SS080, PENTAX_DSMOBILE_600, PLUSTEK_OPTICBOOK_3800, PLUSTEK_OPTICFILM_7200, PLUSTEK_OPTICFILM_7200I, PLUSTEK_OPTICFILM_7300, PLUSTEK_OPTICFILM_7400, PLUSTEK_OPTICFILM_7500I, PLUSTEK_OPTICFILM_8200I, PLUSTEK_OPTICPRO_3600, PLUSTEK_OPTICPRO_ST12, PLUSTEK_OPTICPRO_ST24, SYSCAN_DOCKETPORT_465, SYSCAN_DOCKETPORT_467, SYSCAN_DOCKETPORT_485, SYSCAN_DOCKETPORT_665, SYSCAN_DOCKETPORT_685, UMAX_ASTRA_4500, VISIONEER_7100, VISIONEER_ROADWARRIOR, VISIONEER_STROBE_XP100_REVISION3, VISIONEER_STROBE_XP200, VISIONEER_STROBE_XP300, XEROX_2400, XEROX_TRAVELSCANNER_100, }; inline void serialize(std::istream& str, ModelId& x) { unsigned value; serialize(str, value); x = static_cast(value); } inline void serialize(std::ostream& str, ModelId& x) { unsigned value = static_cast(x); serialize(str, value); } std::ostream& operator<<(std::ostream& out, ModelId id); enum class SensorId : unsigned { UNKNOWN = 0, CCD_5345, CCD_CANON_4400F, CCD_CANON_5600F, CCD_CANON_8400F, CCD_CANON_8600F, CCD_DP665, CCD_DP685, CCD_DSMOBILE600, CCD_DOCKETPORT_487, CCD_G4050, CCD_HP2300, CCD_HP2400, CCD_HP3670, CCD_HP_N6310, CCD_HP_4850C, CCD_IMG101, CCD_KVSS080, CCD_PLUSTEK_OPTICBOOK_3800, CCD_PLUSTEK_OPTICFILM_7200, CCD_PLUSTEK_OPTICFILM_7200I, CCD_PLUSTEK_OPTICFILM_7300, CCD_PLUSTEK_OPTICFILM_7400, CCD_PLUSTEK_OPTICFILM_7500I, CCD_PLUSTEK_OPTICFILM_8200I, CCD_PLUSTEK_OPTICPRO_3600, CCD_ROADWARRIOR, CCD_ST12, // SONY ILX548: 5340 Pixel ??? CCD_ST24, // SONY ILX569: 10680 Pixel ??? CCD_UMAX, CCD_XP300, CIS_CANON_LIDE_35, CIS_CANON_LIDE_60, CIS_CANON_LIDE_80, CIS_CANON_LIDE_90, CIS_CANON_LIDE_100, CIS_CANON_LIDE_110, CIS_CANON_LIDE_120, CIS_CANON_LIDE_200, CIS_CANON_LIDE_210, CIS_CANON_LIDE_220, CIS_CANON_LIDE_700F, CIS_XP200, }; inline void serialize(std::istream& str, SensorId& x) { unsigned value; serialize(str, value); x = static_cast(value); } inline void serialize(std::ostream& str, SensorId& x) { unsigned value = static_cast(x); serialize(str, value); } std::ostream& operator<<(std::ostream& out, SensorId id); enum class AdcId : unsigned { UNKNOWN = 0, AD_XP200, CANON_LIDE_35, CANON_LIDE_80, CANON_LIDE_90, CANON_LIDE_110, CANON_LIDE_120, CANON_LIDE_200, CANON_LIDE_700F, CANON_4400F, CANON_5600F, CANON_8400F, CANON_8600F, G4050, IMG101, KVSS080, PLUSTEK_OPTICBOOK_3800, PLUSTEK_OPTICFILM_7200, PLUSTEK_OPTICFILM_7200I, PLUSTEK_OPTICFILM_7300, PLUSTEK_OPTICFILM_7400, PLUSTEK_OPTICFILM_7500I, PLUSTEK_OPTICFILM_8200I, PLUSTEK_OPTICPRO_3600, WOLFSON_5345, WOLFSON_DSM600, WOLFSON_HP2300, WOLFSON_HP2400, WOLFSON_HP3670, WOLFSON_ST12, WOLFSON_ST24, WOLFSON_UMAX, WOLFSON_XP300, }; inline void serialize(std::istream& str, AdcId& x) { unsigned value; serialize(str, value); x = static_cast(value); } inline void serialize(std::ostream& str, AdcId& x) { unsigned value = static_cast(x); serialize(str, value); } std::ostream& operator<<(std::ostream& out, AdcId id); enum class GpioId : unsigned { UNKNOWN = 0, CANON_LIDE_35, CANON_LIDE_80, CANON_LIDE_90, CANON_LIDE_110, CANON_LIDE_120, CANON_LIDE_200, CANON_LIDE_210, CANON_LIDE_700F, CANON_4400F, CANON_5600F, CANON_8400F, CANON_8600F, DP665, DP685, G4050, G4010, HP2300, HP2400, HP3670, HP_N6310, IMG101, KVSS080, MD_5345, PLUSTEK_OPTICBOOK_3800, PLUSTEK_OPTICFILM_7200, PLUSTEK_OPTICFILM_7200I, PLUSTEK_OPTICFILM_7300, PLUSTEK_OPTICFILM_7400, PLUSTEK_OPTICFILM_7500I, PLUSTEK_OPTICFILM_8200I, PLUSTEK_OPTICPRO_3600, ST12, ST24, UMAX, XP200, XP300, }; std::ostream& operator<<(std::ostream& out, GpioId id); enum class MotorId : unsigned { UNKNOWN = 0, CANON_LIDE_100, CANON_LIDE_110, CANON_LIDE_120, CANON_LIDE_200, CANON_LIDE_210, CANON_LIDE_35, CANON_LIDE_60, CANON_LIDE_700, CANON_LIDE_80, CANON_LIDE_90, CANON_4400F, CANON_5600F, CANON_8400F, CANON_8600F, DP665, DSMOBILE_600, G4050, HP2300, HP2400, HP3670, IMG101, KVSS080, MD_5345, PLUSTEK_OPTICBOOK_3800, PLUSTEK_OPTICFILM_7200, PLUSTEK_OPTICFILM_7200I, PLUSTEK_OPTICFILM_7300, PLUSTEK_OPTICFILM_7400, PLUSTEK_OPTICFILM_7500I, PLUSTEK_OPTICFILM_8200I, PLUSTEK_OPTICPRO_3600, ROADWARRIOR, ST24, UMAX, XP200, XP300, }; std::ostream& operator<<(std::ostream& out, MotorId id); enum class StepType : unsigned { FULL = 0, HALF = 1, QUARTER = 2, EIGHTH = 3, }; std::ostream& operator<<(std::ostream& out, StepType type); inline bool operator<(StepType lhs, StepType rhs) { return static_cast(lhs) < static_cast(rhs); } inline bool operator<=(StepType lhs, StepType rhs) { return static_cast(lhs) <= static_cast(rhs); } inline bool operator>(StepType lhs, StepType rhs) { return static_cast(lhs) > static_cast(rhs); } inline bool operator>=(StepType lhs, StepType rhs) { return static_cast(lhs) >= static_cast(rhs); } enum class AsicType : unsigned { UNKNOWN = 0, GL646, GL841, GL842, GL843, GL845, GL846, GL847, GL124, }; enum class ModelFlag : unsigned { // no flags NONE = 0, // scanner is not tested, print a warning as it's likely it won't work UNTESTED = 1 << 0, // use 14-bit gamma table instead of 12-bit GAMMA_14BIT = 1 << 1, // perform lamp warmup WARMUP = 1 << 4, // whether to disable offset and gain calibration DISABLE_ADC_CALIBRATION = 1 << 5, // whether to disable exposure calibration (this currently is only done on CIS // scanners) DISABLE_EXPOSURE_CALIBRATION = 1 << 6, // whether to disable shading calibration completely DISABLE_SHADING_CALIBRATION = 1 << 7, // do dark calibration DARK_CALIBRATION = 1 << 8, // host-side calibration uses a complete scan HOST_SIDE_CALIBRATION_COMPLETE_SCAN = 1 << 9, // whether scanner must wait for the head while parking MUST_WAIT = 1 << 10, // use zeroes for dark calibration USE_CONSTANT_FOR_DARK_CALIBRATION = 1 << 11, // do dark and white calibration in one run DARK_WHITE_CALIBRATION = 1 << 12, // allow custom gamma tables CUSTOM_GAMMA = 1 << 13, // disable fast feeding mode on this scanner DISABLE_FAST_FEEDING = 1 << 14, // scan gray scans as color and combine on host side HOST_SIDE_GRAY = 1 << 15, // the scanner uses multi-segment sensors that must be handled during calibration SIS_SENSOR = 1 << 16, // the head must be reparked between shading scans SHADING_REPARK = 1 << 18, // the scanner outputs inverted pixel data INVERT_PIXEL_DATA = 1 << 19, // the scanner outputs 16-bit data that is byte-inverted SWAP_16BIT_DATA = 1 << 20, // the scanner has transparency, but it's implemented using only one motor UTA_NO_SECONDARY_MOTOR = 1 << 21, // the scanner has transparency, but it's implemented using only one lamp TA_NO_SECONDARY_LAMP = 1 << 22, }; inline ModelFlag operator|(ModelFlag left, ModelFlag right) { return static_cast(static_cast(left) | static_cast(right)); } inline ModelFlag& operator|=(ModelFlag& left, ModelFlag right) { left = left | right; return left; } inline ModelFlag operator&(ModelFlag left, ModelFlag right) { return static_cast(static_cast(left) & static_cast(right)); } inline bool has_flag(ModelFlag flags, ModelFlag which) { return (flags & which) == which; } enum class ScanFlag : unsigned { NONE = 0, SINGLE_LINE = 1 << 0, DISABLE_SHADING = 1 << 1, DISABLE_GAMMA = 1 << 2, DISABLE_BUFFER_FULL_MOVE = 1 << 3, // if this flag is set the sensor will always be handled ignoring staggering of multiple // sensors to achieve high resolution. IGNORE_STAGGER_OFFSET = 1 << 4, // if this flag is set the sensor will always be handled as if the components that scan // different colors are at the same position. IGNORE_COLOR_OFFSET = 1 << 5, DISABLE_LAMP = 1 << 6, CALIBRATION = 1 << 7, FEEDING = 1 << 8, USE_XPA = 1 << 9, ENABLE_LEDADD = 1 << 10, REVERSE = 1 << 12, // the scanner should return head to home position automatically after scan. AUTO_GO_HOME = 1 << 13, }; inline ScanFlag operator|(ScanFlag left, ScanFlag right) { return static_cast(static_cast(left) | static_cast(right)); } inline ScanFlag& operator|=(ScanFlag& left, ScanFlag right) { left = left | right; return left; } inline ScanFlag operator&(ScanFlag left, ScanFlag right) { return static_cast(static_cast(left) & static_cast(right)); } inline bool has_flag(ScanFlag flags, ScanFlag which) { return (flags & which) == which; } inline void serialize(std::istream& str, ScanFlag& x) { unsigned value; serialize(str, value); x = static_cast(value); } inline void serialize(std::ostream& str, ScanFlag& x) { unsigned value = static_cast(x); serialize(str, value); } std::ostream& operator<<(std::ostream& out, ScanFlag flags); enum class Direction : unsigned { FORWARD = 0, BACKWARD = 1 }; enum class MotorMode : unsigned { PRIMARY = 0, PRIMARY_AND_SECONDARY, SECONDARY, }; } // namespace genesys #endif // BACKEND_GENESYS_ENUMS_H backends-1.3.0/backend/genesys/error.cpp000066400000000000000000000136601456256263500202120ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "error.h" #include #include namespace genesys { extern "C" void sanei_debug_msg(int level, int max_level, const char *be, const char *fmt, std::va_list ap); #if (defined(__GNUC__) || defined(__CLANG__)) && (defined(__linux__) || defined(__APPLE__)) extern "C" char* __cxa_get_globals(); #endif static unsigned num_uncaught_exceptions() { #if __cplusplus >= 201703L int count = std::uncaught_exceptions(); return count >= 0 ? count : 0; #elif (defined(__GNUC__) || defined(__CLANG__)) && (defined(__linux__) || defined(__APPLE__)) // the format of the __cxa_eh_globals struct is enshrined into the Itanium C++ ABI and it's // very unlikely we'll get issues referencing it directly char* cxa_eh_globals_ptr = __cxa_get_globals(); return *reinterpret_cast(cxa_eh_globals_ptr + sizeof(void*)); #else return std::uncaught_exception() ? 1 : 0; #endif } SaneException::SaneException(SANE_Status status) : status_(status) { set_msg(); } SaneException::SaneException(SANE_Status status, const char* format, ...) : status_(status) { std::va_list args; va_start(args, format); set_msg(format, args); va_end(args); } SaneException::SaneException(const char* format, ...) : status_(SANE_STATUS_INVAL) { std::va_list args; va_start(args, format); set_msg(format, args); va_end(args); } SANE_Status SaneException::status() const { return status_; } const char* SaneException::what() const noexcept { return msg_.c_str(); } void SaneException::set_msg() { const char* status_msg = sane_strstatus(status_); std::size_t status_msg_len = std::strlen(status_msg); msg_.reserve(status_msg_len); msg_ = status_msg; } void SaneException::set_msg(const char* format, std::va_list vlist) { const char* status_msg = sane_strstatus(status_); std::size_t status_msg_len = std::strlen(status_msg); std::va_list vlist2; va_copy(vlist2, vlist); int msg_len = std::vsnprintf(nullptr, 0, format, vlist2); va_end(vlist2); if (msg_len < 0) { const char* formatting_error_msg = "(error formatting arguments)"; msg_.reserve(std::strlen(formatting_error_msg) + 3 + status_msg_len); msg_ = formatting_error_msg; msg_ += " : "; msg_ += status_msg; return; } msg_.reserve(msg_len + status_msg_len + 3); msg_.resize(msg_len + 1, ' '); std::vsnprintf(&msg_[0], msg_len + 1, format, vlist); msg_.resize(msg_len, ' '); msg_ += " : "; msg_ += status_msg; } DebugMessageHelper::DebugMessageHelper(const char* func) { func_ = func; num_exceptions_on_enter_ = num_uncaught_exceptions(); msg_[0] = '\0'; DBG(DBG_proc, "%s: start\n", func_); } DebugMessageHelper::DebugMessageHelper(const char* func, const char* format, ...) { func_ = func; num_exceptions_on_enter_ = num_uncaught_exceptions(); msg_[0] = '\0'; DBG(DBG_proc, "%s: start\n", func_); DBG(DBG_proc, "%s: ", func_); std::va_list args; va_start(args, format); sanei_debug_msg(DBG_proc, DBG_LEVEL, STRINGIFY(BACKEND_NAME), format, args); va_end(args); DBG(DBG_proc, "\n"); } DebugMessageHelper::~DebugMessageHelper() { if (num_exceptions_on_enter_ < num_uncaught_exceptions()) { if (msg_[0] != '\0') { DBG(DBG_error, "%s: failed during %s\n", func_, msg_); } else { DBG(DBG_error, "%s: failed\n", func_); } } else { DBG(DBG_proc, "%s: completed\n", func_); } } void DebugMessageHelper::vstatus(const char* format, ...) { std::va_list args; va_start(args, format); std::vsnprintf(msg_, MAX_BUF_SIZE, format, args); va_end(args); } void DebugMessageHelper::log(unsigned level, const char* msg) { DBG(level, "%s: %s\n", func_, msg); } void DebugMessageHelper::vlog(unsigned level, const char* format, ...) { std::string msg; std::va_list args; va_start(args, format); int msg_len = std::vsnprintf(nullptr, 0, format, args); va_end(args); if (msg_len < 0) { DBG(level, "%s: error formatting error message: %s\n", func_, format); return; } msg.resize(msg_len + 1, ' '); va_start(args, format); std::vsnprintf(&msg.front(), msg.size(), format, args); va_end(args); msg.resize(msg_len, ' '); // strip the null character DBG(level, "%s: %s\n", func_, msg.c_str()); } enum class LogImageDataStatus { NOT_SET, ENABLED, DISABLED }; static LogImageDataStatus s_log_image_data_setting = LogImageDataStatus::NOT_SET; LogImageDataStatus dbg_read_log_image_data_setting() { auto* setting = std::getenv("SANE_DEBUG_GENESYS_IMAGE"); if (!setting) return LogImageDataStatus::DISABLED; auto setting_int = std::strtol(setting, nullptr, 10); if (setting_int == 0) return LogImageDataStatus::DISABLED; return LogImageDataStatus::ENABLED; } bool dbg_log_image_data() { if (s_log_image_data_setting == LogImageDataStatus::NOT_SET) { s_log_image_data_setting = dbg_read_log_image_data_setting(); } return s_log_image_data_setting == LogImageDataStatus::ENABLED; } } // namespace genesys backends-1.3.0/backend/genesys/error.h000066400000000000000000000143671456256263500176640ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_ERROR_H #define BACKEND_GENESYS_ERROR_H #include "../include/sane/config.h" #include "../include/sane/sane.h" #include "../include/sane/sanei_backend.h" #include #include #include #include #include #define DBG_error0 0 /* errors/warnings printed even with devuglevel 0 */ #define DBG_error 1 /* fatal errors */ #define DBG_init 2 /* initialization and scanning time messages */ #define DBG_warn 3 /* warnings and non-fatal errors */ #define DBG_info 4 /* informational messages */ #define DBG_proc 5 /* starting/finishing functions */ #define DBG_io 6 /* io functions */ #define DBG_io2 7 /* io functions that are called very often */ #define DBG_data 8 /* log image data */ namespace genesys { class SaneException : public std::exception { public: SaneException(SANE_Status status); SaneException(SANE_Status status, const char* format, ...) #ifdef __GNUC__ __attribute__((format(printf, 3, 4))) #endif ; SaneException(const char* format, ...) #ifdef __GNUC__ __attribute__((format(printf, 2, 3))) #endif ; SANE_Status status() const; const char* what() const noexcept override; private: void set_msg(); void set_msg(const char* format, std::va_list vlist); std::string msg_; SANE_Status status_; }; // call a function and throw an exception on error #define TIE(function) \ do { \ SANE_Status tmp_status = function; \ if (tmp_status != SANE_STATUS_GOOD) { \ throw ::genesys::SaneException(tmp_status); \ } \ } while (false) class DebugMessageHelper { public: static constexpr unsigned MAX_BUF_SIZE = 120; DebugMessageHelper(const char* func); DebugMessageHelper(const char* func, const char* format, ...) #ifdef __GNUC__ __attribute__((format(printf, 3, 4))) #endif ; ~DebugMessageHelper(); void status(const char* msg) { vstatus("%s", msg); } void vstatus(const char* format, ...) #ifdef __GNUC__ __attribute__((format(printf, 2, 3))) #endif ; void clear() { msg_[0] = '\n'; } void log(unsigned level, const char* msg); void vlog(unsigned level, const char* format, ...) #ifdef __GNUC__ __attribute__((format(printf, 3, 4))) #endif ; private: const char* func_ = nullptr; char msg_[MAX_BUF_SIZE]; unsigned num_exceptions_on_enter_ = 0; }; #if defined(__GNUC__) || defined(__clang__) #define GENESYS_CURRENT_FUNCTION __PRETTY_FUNCTION__ #elif defined(__FUNCSIG__) #define GENESYS_CURRENT_FUNCTION __FUNCSIG__ #else #define GENESYS_CURRENT_FUNCTION __func__ #endif #define DBG_HELPER(var) DebugMessageHelper var(GENESYS_CURRENT_FUNCTION) #define DBG_HELPER_ARGS(var, ...) DebugMessageHelper var(GENESYS_CURRENT_FUNCTION, __VA_ARGS__) bool dbg_log_image_data(); template SANE_Status wrap_exceptions_to_status_code(const char* func, F&& function) { try { function(); return SANE_STATUS_GOOD; } catch (const SaneException& exc) { DBG(DBG_error, "%s: got error: %s\n", func, exc.what()); return exc.status(); } catch (const std::bad_alloc& exc) { (void) exc; DBG(DBG_error, "%s: failed to allocate memory\n", func); return SANE_STATUS_NO_MEM; } catch (const std::exception& exc) { DBG(DBG_error, "%s: got uncaught exception: %s\n", func, exc.what()); return SANE_STATUS_INVAL; } catch (...) { DBG(DBG_error, "%s: got unknown uncaught exception\n", func); return SANE_STATUS_INVAL; } } template SANE_Status wrap_exceptions_to_status_code_return(const char* func, F&& function) { try { return function(); } catch (const SaneException& exc) { DBG(DBG_error, "%s: got error: %s\n", func, exc.what()); return exc.status(); } catch (const std::bad_alloc& exc) { (void) exc; DBG(DBG_error, "%s: failed to allocate memory\n", func); return SANE_STATUS_NO_MEM; } catch (const std::exception& exc) { DBG(DBG_error, "%s: got uncaught exception: %s\n", func, exc.what()); return SANE_STATUS_INVAL; } catch (...) { DBG(DBG_error, "%s: got unknown uncaught exception\n", func); return SANE_STATUS_INVAL; } } template void catch_all_exceptions(const char* func, F&& function) { try { function(); } catch (const SaneException& exc) { DBG(DBG_error, "%s: got exception: %s\n", func, exc.what()); } catch (const std::bad_alloc& exc) { DBG(DBG_error, "%s: got exception: could not allocate memory: %s\n", func, exc.what()); } catch (const std::exception& exc) { DBG(DBG_error, "%s: got uncaught exception: %s\n", func, exc.what()); } catch (...) { DBG(DBG_error, "%s: got unknown uncaught exception\n", func); } } inline void wrap_status_code_to_exception(SANE_Status status) { if (status == SANE_STATUS_GOOD) return; throw SaneException(status); } } // namespace genesys #endif // BACKEND_GENESYS_ERROR_H backends-1.3.0/backend/genesys/fwd.h000066400000000000000000000041471456256263500173060ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_FWD_H #define BACKEND_GENESYS_FWD_H namespace genesys { // calibration.h struct Genesys_Calibration_Cache; // command_set.h class CommandSet; // device.h struct Genesys_Gpo; struct MethodResolutions; struct Genesys_Model; struct Genesys_Device; // error.h class DebugMessageHelper; class SaneException; // genesys.h class GenesysButton; struct Genesys_Scanner; // image.h class Image; // image_buffer.h class ImageBuffer; // image_pipeline.h class ImagePipelineNode; // ImagePipelineNode* skipped class ImagePipelineStack; // image_pixel.h struct Pixel; struct RawPixel; // low.h class UsbDeviceEntry; // motor.h struct Genesys_Motor; struct MotorSlope; struct MotorProfile; struct MotorSlopeTable; // register.h class Genesys_Register_Set; struct GenesysRegisterSetState; // row_buffer.h class RowBuffer; // usb_device.h class IUsbDevice; class UsbDevice; // scanner_interface.h class ScannerInterface; class ScannerInterfaceUsb; class TestScannerInterface; // sensor.h struct GenesysFrontendLayout; struct Genesys_Frontend; struct SensorExposure; struct Genesys_Sensor; // settings.h struct Genesys_Settings; struct SetupParams; struct ScanSession; // value_filter.h template class ValueFilter; template class ValueFilterAny; // test_usb_device.h class TestUsbDevice; } // namespace genesys #endif backends-1.3.0/backend/genesys/genesys.cpp000066400000000000000000006665051456256263500205520ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2003, 2004 Henning Meier-Geinitz Copyright (C) 2004, 2005 Gerhard Jaeger Copyright (C) 2004-2016 Stéphane Voltz Copyright (C) 2005-2009 Pierre Willenbrock Copyright (C) 2006 Laurent Charpentier Copyright (C) 2007 Luke Copyright (C) 2010 Chris Berry and Michael Rickmann for Plustek Opticbook 3600 support Dynamic rasterization code was taken from the epjistsu backend by m. allan noah Software processing for deskew, crop and dspeckle are inspired by allan's noah work in the fujitsu backend 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, see . */ /* * SANE backend for Genesys Logic GL646/GL841/GL842/GL843/GL846/GL847/GL124 based scanners */ #define DEBUG_NOT_STATIC #include "genesys.h" #include "gl124_registers.h" #include "gl841_registers.h" #include "gl842_registers.h" #include "gl843_registers.h" #include "gl846_registers.h" #include "gl847_registers.h" #include "usb_device.h" #include "utilities.h" #include "scanner_interface_usb.h" #include "test_scanner_interface.h" #include "test_settings.h" #include "../include/sane/sanei_config.h" #include #include #include #include #include #include #include #include #include #ifndef SANE_GENESYS_API_LINKAGE #define SANE_GENESYS_API_LINKAGE extern "C" #endif namespace genesys { // Data that we allocate to back SANE_Device objects in s_sane_devices struct SANE_Device_Data { std::string name; }; namespace { StaticInit> s_scanners; StaticInit> s_sane_devices; StaticInit> s_sane_devices_data; StaticInit> s_sane_devices_ptrs; StaticInit> s_devices; // Maximum time for lamp warm-up constexpr unsigned WARMUP_TIME = 65; } // namespace static SANE_String_Const mode_list[] = { SANE_VALUE_SCAN_MODE_COLOR, SANE_VALUE_SCAN_MODE_GRAY, // SANE_TITLE_HALFTONE, not used // SANE_VALUE_SCAN_MODE_LINEART, not used nullptr }; static SANE_String_Const color_filter_list[] = { SANE_I18N ("Red"), SANE_I18N ("Green"), SANE_I18N ("Blue"), nullptr }; static SANE_String_Const cis_color_filter_list[] = { SANE_I18N ("Red"), SANE_I18N ("Green"), SANE_I18N ("Blue"), SANE_I18N ("None"), nullptr }; static SANE_Range time_range = { 0, /* minimum */ 60, /* maximum */ 0 /* quantization */ }; static const SANE_Range u12_range = { 0, /* minimum */ 4095, /* maximum */ 0 /* quantization */ }; static const SANE_Range u14_range = { 0, /* minimum */ 16383, /* maximum */ 0 /* quantization */ }; static const SANE_Range u16_range = { 0, /* minimum */ 65535, /* maximum */ 0 /* quantization */ }; static const SANE_Range percentage_range = { float_to_fixed(0), // minimum float_to_fixed(100), // maximum float_to_fixed(1) // quantization }; /** * range for brightness and contrast */ static const SANE_Range enhance_range = { -100, /* minimum */ 100, /* maximum */ 1 /* quantization */ }; /** * range for expiration time */ static const SANE_Range expiration_range = { -1, /* minimum */ 30000, /* maximum */ 1 /* quantization */ }; const Genesys_Sensor& sanei_genesys_find_sensor_any(const Genesys_Device* dev) { DBG_HELPER(dbg); for (const auto& sensor : *s_sensors) { if (dev->model->sensor_id == sensor.sensor_id) { return sensor; } } throw std::runtime_error("Given device does not have sensor defined"); } Genesys_Sensor* find_sensor_impl(const Genesys_Device* dev, unsigned dpi, unsigned channels, ScanMethod scan_method) { DBG_HELPER_ARGS(dbg, "dpi: %d, channels: %d, scan_method: %d", dpi, channels, static_cast(scan_method)); for (auto& sensor : *s_sensors) { if (dev->model->sensor_id == sensor.sensor_id && sensor.resolutions.matches(dpi) && sensor.matches_channel_count(channels) && sensor.method == scan_method) { return &sensor; } } return nullptr; } bool sanei_genesys_has_sensor(const Genesys_Device* dev, unsigned dpi, unsigned channels, ScanMethod scan_method) { DBG_HELPER_ARGS(dbg, "dpi: %d, channels: %d, scan_method: %d", dpi, channels, static_cast(scan_method)); return find_sensor_impl(dev, dpi, channels, scan_method) != nullptr; } const Genesys_Sensor& sanei_genesys_find_sensor(const Genesys_Device* dev, unsigned dpi, unsigned channels, ScanMethod scan_method) { DBG_HELPER_ARGS(dbg, "dpi: %d, channels: %d, scan_method: %d", dpi, channels, static_cast(scan_method)); const auto* sensor = find_sensor_impl(dev, dpi, channels, scan_method); if (sensor) return *sensor; throw std::runtime_error("Given device does not have sensor defined"); } Genesys_Sensor& sanei_genesys_find_sensor_for_write(Genesys_Device* dev, unsigned dpi, unsigned channels, ScanMethod scan_method) { DBG_HELPER_ARGS(dbg, "dpi: %d, channels: %d, scan_method: %d", dpi, channels, static_cast(scan_method)); auto* sensor = find_sensor_impl(dev, dpi, channels, scan_method); if (sensor) return *sensor; throw std::runtime_error("Given device does not have sensor defined"); } std::vector> sanei_genesys_find_sensors_all(const Genesys_Device* dev, ScanMethod scan_method) { DBG_HELPER_ARGS(dbg, "scan_method: %d", static_cast(scan_method)); std::vector> ret; for (auto& sensor : *s_sensors) { if (dev->model->sensor_id == sensor.sensor_id && sensor.method == scan_method) { ret.push_back(sensor); } } return ret; } std::vector> sanei_genesys_find_sensors_all_for_write(Genesys_Device* dev, ScanMethod scan_method) { DBG_HELPER_ARGS(dbg, "scan_method: %d", static_cast(scan_method)); std::vector> ret; for (auto& sensor : *s_sensors) { if (dev->model->sensor_id == sensor.sensor_id && sensor.method == scan_method) { ret.push_back(sensor); } } return ret; } void sanei_genesys_init_structs (Genesys_Device * dev) { DBG_HELPER(dbg); bool gpo_ok = false; bool motor_ok = false; bool fe_ok = false; /* initialize the GPO data stuff */ for (const auto& gpo : *s_gpo) { if (dev->model->gpio_id == gpo.id) { dev->gpo = gpo; gpo_ok = true; break; } } // initialize the motor data stuff for (const auto& motor : *s_motors) { if (dev->model->motor_id == motor.id) { dev->motor = motor; motor_ok = true; break; } } for (const auto& frontend : *s_frontends) { if (dev->model->adc_id == frontend.id) { dev->frontend_initial = frontend; dev->frontend = frontend; fe_ok = true; break; } } if (dev->model->asic_type == AsicType::GL845 || dev->model->asic_type == AsicType::GL846 || dev->model->asic_type == AsicType::GL847 || dev->model->asic_type == AsicType::GL124) { bool memory_layout_found = false; for (const auto& memory_layout : *s_memory_layout) { if (memory_layout.models.matches(dev->model->model_id)) { dev->memory_layout = memory_layout; memory_layout_found = true; break; } } if (!memory_layout_found) { throw SaneException("Could not find memory layout"); } } if (!motor_ok || !gpo_ok || !fe_ok) { throw SaneException("bad description(s) for fe/gpo/motor=%d/%d/%d\n", static_cast(dev->model->sensor_id), static_cast(dev->model->gpio_id), static_cast(dev->model->motor_id)); } } /** @brief computes gamma table * Generates a gamma table of the given length within 0 and the given * maximum value * @param gamma_table gamma table to fill * @param size size of the table * @param maximum value allowed for gamma * @param gamma_max maximum gamma value * @param gamma gamma to compute values * @return a gamma table filled with the computed values * */ void sanei_genesys_create_gamma_table(std::vector& gamma_table, int size, float maximum, float gamma_max, float gamma) { gamma_table.clear(); gamma_table.resize(size, 0); int i; float value; DBG(DBG_proc, "%s: size = %d, ""maximum = %g, gamma_max = %g, gamma = %g\n", __func__, size, maximum, gamma_max, gamma); for (i = 0; i < size; i++) { value = static_cast(gamma_max * std::pow(static_cast(i) / size, 1.0 / gamma)); if (value > maximum) { value = maximum; } gamma_table[i] = static_cast(value); } DBG(DBG_proc, "%s: completed\n", __func__); } void sanei_genesys_create_default_gamma_table(Genesys_Device* dev, std::vector& gamma_table, float gamma) { int size = 0; int max = 0; if (dev->model->asic_type == AsicType::GL646) { if (has_flag(dev->model->flags, ModelFlag::GAMMA_14BIT)) { size = 16384; } else { size = 4096; } max = size - 1; } else if (dev->model->asic_type == AsicType::GL124 || dev->model->asic_type == AsicType::GL845 || dev->model->asic_type == AsicType::GL846 || dev->model->asic_type == AsicType::GL847) { size = 257; max = 65535; } else { size = 256; max = 65535; } sanei_genesys_create_gamma_table(gamma_table, size, max, max, gamma); } /* computes the exposure_time on the basis of the given vertical dpi, the number of pixels the ccd needs to send, the step_type and the corresponding maximum speed from the motor struct */ /* Currently considers maximum motor speed at given step_type, minimum line exposure needed for conversion and led exposure time. TODO: Should also consider maximum transfer rate: ~6.5MB/s. Note: The enhance option of the scanners does _not_ help. It only halves the amount of pixels transferred. */ SANE_Int sanei_genesys_exposure_time2(Genesys_Device * dev, const MotorProfile& profile, float ydpi, int endpixel, int exposure_by_led) { int exposure_by_ccd = endpixel + 32; unsigned max_speed_motor_w = profile.slope.max_speed_w; int exposure_by_motor = static_cast((max_speed_motor_w * dev->motor.base_ydpi) / ydpi); int exposure = exposure_by_ccd; if (exposure < exposure_by_motor) { exposure = exposure_by_motor; } if (exposure < exposure_by_led && dev->model->is_cis) { exposure = exposure_by_led; } return exposure; } /* Sends a block of shading information to the scanner. The data is placed at address 0x0000 for color mode, gray mode and unconditionally for the following CCD chips: HP2300, HP2400 and HP5345 The data needs to be of size "size", and in little endian byte order. */ static void genesys_send_offset_and_shading(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t* data, int size) { DBG_HELPER_ARGS(dbg, "(size = %d)", size); int start_address; /* ASIC higher than gl843 doesn't have register 2A/2B, so we route to * a per ASIC shading data loading function if available. * It is also used for scanners using SHDAREA */ if (dev->cmd_set->has_send_shading_data()) { dev->cmd_set->send_shading_data(dev, sensor, data, size); return; } start_address = 0x00; dev->interface->write_buffer(0x3c, start_address, data, size); } void sanei_genesys_init_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, int pixels_per_line) { DBG_HELPER_ARGS(dbg, "pixels_per_line: %d", pixels_per_line); if (dev->cmd_set->has_send_shading_data()) { return; } DBG(DBG_proc, "%s (pixels_per_line = %d)\n", __func__, pixels_per_line); unsigned channels = dev->settings.get_channels(); // 16 bit black, 16 bit white std::vector shading_data(pixels_per_line * 4 * channels, 0); std::uint8_t* shading_data_ptr = shading_data.data(); for (unsigned i = 0; i < pixels_per_line * channels; i++) { *shading_data_ptr++ = 0x00; /* dark lo */ *shading_data_ptr++ = 0x00; /* dark hi */ *shading_data_ptr++ = 0x00; /* white lo */ *shading_data_ptr++ = 0x40; /* white hi -> 0x4000 */ } genesys_send_offset_and_shading(dev, sensor, shading_data.data(), pixels_per_line * 4 * channels); } namespace gl124 { void gl124_setup_scan_gpio(Genesys_Device* dev, int resolution); } // namespace gl124 void scanner_clear_scan_and_feed_counts(Genesys_Device& dev) { switch (dev.model->asic_type) { case AsicType::GL841: { dev.interface->write_register(gl841::REG_0x0D, gl841::REG_0x0D_CLRLNCNT); break; } case AsicType::GL842: { dev.interface->write_register(gl842::REG_0x0D, gl842::REG_0x0D_CLRLNCNT); break; } case AsicType::GL843: { dev.interface->write_register(gl843::REG_0x0D, gl843::REG_0x0D_CLRLNCNT | gl843::REG_0x0D_CLRMCNT); break; } case AsicType::GL845: case AsicType::GL846: { dev.interface->write_register(gl846::REG_0x0D, gl846::REG_0x0D_CLRLNCNT | gl846::REG_0x0D_CLRMCNT); break; } case AsicType::GL847:{ dev.interface->write_register(gl847::REG_0x0D, gl847::REG_0x0D_CLRLNCNT | gl847::REG_0x0D_CLRMCNT); break; } case AsicType::GL124:{ dev.interface->write_register(gl124::REG_0x0D, gl124::REG_0x0D_CLRLNCNT | gl124::REG_0x0D_CLRMCNT); break; } default: throw SaneException("Unsupported asic type"); } } void scanner_send_slope_table(Genesys_Device* dev, const Genesys_Sensor& sensor, unsigned table_nr, const std::vector& slope_table) { DBG_HELPER_ARGS(dbg, "table_nr = %d, steps = %zu", table_nr, slope_table.size()); unsigned max_table_nr = 0; switch (dev->model->asic_type) { case AsicType::GL646: { max_table_nr = 2; break; } case AsicType::GL841: case AsicType::GL842: case AsicType::GL843: case AsicType::GL845: case AsicType::GL846: case AsicType::GL847: case AsicType::GL124: { max_table_nr = 4; break; } default: throw SaneException("Unsupported ASIC type"); } if (table_nr > max_table_nr) { throw SaneException("invalid table number %d", table_nr); } std::vector table; table.reserve(slope_table.size() * 2); for (std::size_t i = 0; i < slope_table.size(); i++) { table.push_back(slope_table[i] & 0xff); table.push_back(slope_table[i] >> 8); } if (dev->model->asic_type == AsicType::GL841 || dev->model->model_id == ModelId::CANON_LIDE_90) { // BUG: do this on all gl842 scanners auto max_table_size = get_slope_table_max_size(dev->model->asic_type); table.reserve(max_table_size * 2); while (table.size() < max_table_size * 2) { table.push_back(slope_table.back() & 0xff); table.push_back(slope_table.back() >> 8); } } if (dev->interface->is_mock()) { dev->interface->record_slope_table(table_nr, slope_table); } switch (dev->model->asic_type) { case AsicType::GL646: { unsigned dpihw = dev->reg.find_reg(0x05).value >> 6; unsigned start_address = 0; if (dpihw == 0) { // 600 dpi start_address = 0x08000; } else if (dpihw == 1) { // 1200 dpi start_address = 0x10000; } else if (dpihw == 2) { // 2400 dpi start_address = 0x1f800; } else { throw SaneException("Unexpected dpihw"); } dev->interface->write_buffer(0x3c, start_address + table_nr * 0x100, table.data(), table.size()); break; } case AsicType::GL841: case AsicType::GL842: { unsigned start_address = 0; switch (sensor.register_dpihw) { case 600: start_address = 0x08000; break; case 1200: start_address = 0x10000; break; case 2400: start_address = 0x20000; break; default: throw SaneException("Unexpected dpihw"); } dev->interface->write_buffer(0x3c, start_address + table_nr * 0x200, table.data(), table.size()); break; } case AsicType::GL843: { // slope table addresses are fixed : 0x40000, 0x48000, 0x50000, 0x58000, 0x60000 // XXX STEF XXX USB 1.1 ? sanei_genesys_write_0x8c (dev, 0x0f, 0x14); dev->interface->write_gamma(0x28, 0x40000 + 0x8000 * table_nr, table.data(), table.size()); break; } case AsicType::GL845: case AsicType::GL846: case AsicType::GL847: case AsicType::GL124: { // slope table addresses are fixed dev->interface->write_ahb(0x10000000 + 0x4000 * table_nr, table.size(), table.data()); break; } default: throw SaneException("Unsupported ASIC type"); } } bool scanner_is_motor_stopped(Genesys_Device& dev) { switch (dev.model->asic_type) { case AsicType::GL646: { auto status = scanner_read_status(dev); return !status.is_motor_enabled && status.is_feeding_finished; } case AsicType::GL841: { auto status = scanner_read_status(dev); auto reg = dev.interface->read_register(gl841::REG_0x40); return (!(reg & gl841::REG_0x40_DATAENB) && !(reg & gl841::REG_0x40_MOTMFLG) && !status.is_motor_enabled); } case AsicType::GL842: { auto status = scanner_read_status(dev); auto reg = dev.interface->read_register(gl842::REG_0x40); return (!(reg & gl842::REG_0x40_DATAENB) && !(reg & gl842::REG_0x40_MOTMFLG) && !status.is_motor_enabled); } case AsicType::GL843: { auto status = scanner_read_status(dev); auto reg = dev.interface->read_register(gl843::REG_0x40); return (!(reg & gl843::REG_0x40_DATAENB) && !(reg & gl843::REG_0x40_MOTMFLG) && !status.is_motor_enabled); } case AsicType::GL845: case AsicType::GL846: { auto status = scanner_read_status(dev); auto reg = dev.interface->read_register(gl846::REG_0x40); return (!(reg & gl846::REG_0x40_DATAENB) && !(reg & gl846::REG_0x40_MOTMFLG) && !status.is_motor_enabled); } case AsicType::GL847: { auto status = scanner_read_status(dev); auto reg = dev.interface->read_register(gl847::REG_0x40); return (!(reg & gl847::REG_0x40_DATAENB) && !(reg & gl847::REG_0x40_MOTMFLG) && !status.is_motor_enabled); } case AsicType::GL124: { auto status = scanner_read_status(dev); auto reg = dev.interface->read_register(gl124::REG_0x100); return (!(reg & gl124::REG_0x100_DATAENB) && !(reg & gl124::REG_0x100_MOTMFLG) && !status.is_motor_enabled); } default: throw SaneException("Unsupported asic type"); } } void scanner_setup_sensor(Genesys_Device& dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) { DBG_HELPER(dbg); for (const auto& custom_reg : sensor.custom_regs) { regs.set8(custom_reg.address, custom_reg.value); } if (dev.model->asic_type != AsicType::GL843) { // FIXME: remove the above check regs_set_exposure(dev.model->asic_type, regs, sensor.exposure); } dev.segment_order = sensor.segment_order; } void scanner_stop_action(Genesys_Device& dev) { DBG_HELPER(dbg); switch (dev.model->asic_type) { case AsicType::GL841: case AsicType::GL842: case AsicType::GL843: case AsicType::GL845: case AsicType::GL846: case AsicType::GL847: case AsicType::GL124: break; default: throw SaneException("Unsupported asic type"); } dev.cmd_set->update_home_sensor_gpio(dev); if (scanner_is_motor_stopped(dev)) { DBG(DBG_info, "%s: already stopped\n", __func__); return; } scanner_stop_action_no_move(dev, dev.reg); if (is_testing_mode()) { return; } for (unsigned i = 0; i < 10; ++i) { if (scanner_is_motor_stopped(dev)) { return; } dev.interface->sleep_ms(100); } throw SaneException(SANE_STATUS_IO_ERROR, "could not stop motor"); } void scanner_stop_action_no_move(Genesys_Device& dev, genesys::Genesys_Register_Set& regs) { switch (dev.model->asic_type) { case AsicType::GL646: case AsicType::GL841: case AsicType::GL842: case AsicType::GL843: case AsicType::GL845: case AsicType::GL846: case AsicType::GL847: case AsicType::GL124: break; default: throw SaneException("Unsupported asic type"); } regs_set_optical_off(dev.model->asic_type, regs); // same across all supported ASICs dev.interface->write_register(0x01, regs.get8(0x01)); // looks like certain scanners lock up if we try to scan immediately after stopping previous // action. dev.interface->sleep_ms(100); } void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, Direction direction) { DBG_HELPER_ARGS(dbg, "steps=%d direction=%d", steps, static_cast(direction)); auto local_reg = dev.reg; unsigned resolution = dev.model->get_resolution_settings(scan_method).get_min_resolution_y(); const auto& sensor = sanei_genesys_find_sensor(&dev, resolution, 3, scan_method); bool uses_secondary_head = (scan_method == ScanMethod::TRANSPARENCY || scan_method == ScanMethod::TRANSPARENCY_INFRARED) && (!has_flag(dev.model->flags, ModelFlag::UTA_NO_SECONDARY_MOTOR)); bool uses_secondary_pos = uses_secondary_head && dev.model->default_method == ScanMethod::FLATBED; if (!dev.is_head_pos_known(ScanHeadId::PRIMARY)) { throw SaneException("Unknown head position"); } if (uses_secondary_pos && !dev.is_head_pos_known(ScanHeadId::SECONDARY)) { throw SaneException("Unknown head position"); } if (direction == Direction::BACKWARD && steps > dev.head_pos(ScanHeadId::PRIMARY)) { throw SaneException("Trying to feed behind the home position %d %d", steps, dev.head_pos(ScanHeadId::PRIMARY)); } if (uses_secondary_pos && direction == Direction::BACKWARD && steps > dev.head_pos(ScanHeadId::SECONDARY)) { throw SaneException("Trying to feed behind the home position %d %d", steps, dev.head_pos(ScanHeadId::SECONDARY)); } ScanSession session; session.params.xres = resolution; session.params.yres = resolution; session.params.startx = 0; session.params.starty = steps; session.params.pixels = 50; session.params.lines = 3; session.params.depth = 8; session.params.channels = 1; session.params.scan_method = scan_method; session.params.scan_mode = ScanColorMode::GRAY; session.params.color_filter = ColorFilter::GREEN; session.params.contrast_adjustment = dev.settings.contrast; session.params.brightness_adjustment = dev.settings.brightness; session.params.flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::FEEDING | ScanFlag::IGNORE_STAGGER_OFFSET | ScanFlag::IGNORE_COLOR_OFFSET; if (dev.model->asic_type == AsicType::GL124) { session.params.flags |= ScanFlag::DISABLE_BUFFER_FULL_MOVE; } if (direction == Direction::BACKWARD) { session.params.flags |= ScanFlag::REVERSE; } compute_session(&dev, session, sensor); dev.cmd_set->init_regs_for_scan_session(&dev, sensor, &local_reg, session); if (dev.model->asic_type != AsicType::GL843) { regs_set_exposure(dev.model->asic_type, local_reg, sanei_genesys_fixup_exposure({0, 0, 0})); } scanner_clear_scan_and_feed_counts(dev); dev.interface->write_registers(local_reg); if (uses_secondary_head) { dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY_AND_SECONDARY); } try { scanner_start_action(dev, true); } catch (...) { catch_all_exceptions(__func__, [&]() { dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY); }); catch_all_exceptions(__func__, [&]() { scanner_stop_action(dev); }); // restore original registers catch_all_exceptions(__func__, [&]() { dev.interface->write_registers(dev.reg); }); throw; } if (is_testing_mode()) { dev.interface->test_checkpoint("feed"); dev.advance_head_pos_by_steps(ScanHeadId::PRIMARY, direction, steps); if (uses_secondary_pos) { dev.advance_head_pos_by_steps(ScanHeadId::SECONDARY, direction, steps); } scanner_stop_action(dev); if (uses_secondary_head) { dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY); } return; } // wait until feed count reaches the required value if (dev.model->model_id == ModelId::CANON_LIDE_700F) { dev.cmd_set->update_home_sensor_gpio(dev); } // FIXME: should porbably wait for some timeout Status status; for (unsigned i = 0;; ++i) { status = scanner_read_status(dev); if (status.is_feeding_finished || ( direction == Direction::BACKWARD && status.is_at_home)) { break; } dev.interface->sleep_ms(10); } scanner_stop_action(dev); if (uses_secondary_head) { dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY); } dev.advance_head_pos_by_steps(ScanHeadId::PRIMARY, direction, steps); if (uses_secondary_pos) { dev.advance_head_pos_by_steps(ScanHeadId::SECONDARY, direction, steps); } // looks like certain scanners lock up if we scan immediately after feeding dev.interface->sleep_ms(100); } void scanner_move_to_ta(Genesys_Device& dev) { DBG_HELPER(dbg); unsigned feed = static_cast((dev.model->y_offset_sensor_to_ta * dev.motor.base_ydpi) / MM_PER_INCH); scanner_move(dev, dev.model->default_method, feed, Direction::FORWARD); } void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home) { DBG_HELPER_ARGS(dbg, "wait_until_home = %d", wait_until_home); switch (dev.model->asic_type) { case AsicType::GL841: case AsicType::GL842: case AsicType::GL843: case AsicType::GL845: case AsicType::GL846: case AsicType::GL847: case AsicType::GL124: break; default: throw SaneException("Unsupported asic type"); } if (dev.model->is_sheetfed) { dbg.vlog(DBG_proc, "sheetfed scanner, skipping going back home"); return; } // FIXME: also check whether the scanner actually has a secondary head if ((!dev.is_head_pos_known(ScanHeadId::SECONDARY) || dev.head_pos(ScanHeadId::SECONDARY) > 0 || dev.settings.scan_method == ScanMethod::TRANSPARENCY || dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) && (!has_flag(dev.model->flags, ModelFlag::UTA_NO_SECONDARY_MOTOR))) { scanner_move_back_home_ta(dev); } if (dev.is_head_pos_known(ScanHeadId::PRIMARY) && dev.head_pos(ScanHeadId::PRIMARY) > 1000) { // leave 500 steps for regular slow back home scanner_move(dev, dev.model->default_method, dev.head_pos(ScanHeadId::PRIMARY) - 500, Direction::BACKWARD); } dev.cmd_set->update_home_sensor_gpio(dev); auto status = scanner_read_reliable_status(dev); if (status.is_at_home) { dbg.log(DBG_info, "already at home"); dev.set_head_pos_zero(ScanHeadId::PRIMARY); return; } Genesys_Register_Set local_reg = dev.reg; unsigned resolution = sanei_genesys_get_lowest_ydpi(&dev); const auto& sensor = sanei_genesys_find_sensor(&dev, resolution, 1, dev.model->default_method); ScanSession session; session.params.xres = resolution; session.params.yres = resolution; session.params.startx = 0; session.params.starty = 40000; session.params.pixels = 50; session.params.lines = 3; session.params.depth = 8; session.params.channels = 1; session.params.scan_method = dev.settings.scan_method; session.params.scan_mode = ScanColorMode::GRAY; session.params.color_filter = ColorFilter::GREEN; session.params.contrast_adjustment = dev.settings.contrast; session.params.brightness_adjustment = dev.settings.brightness; session.params.flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::IGNORE_STAGGER_OFFSET | ScanFlag::IGNORE_COLOR_OFFSET | ScanFlag::REVERSE; if (dev.model->asic_type == AsicType::GL843) { session.params.flags |= ScanFlag::DISABLE_BUFFER_FULL_MOVE; } compute_session(&dev, session, sensor); dev.cmd_set->init_regs_for_scan_session(&dev, sensor, &local_reg, session); scanner_clear_scan_and_feed_counts(dev); dev.interface->write_registers(local_reg); if (dev.model->asic_type == AsicType::GL124) { gl124::gl124_setup_scan_gpio(&dev, resolution); } try { scanner_start_action(dev, true); } catch (...) { catch_all_exceptions(__func__, [&]() { scanner_stop_action(dev); }); // restore original registers catch_all_exceptions(__func__, [&]() { dev.interface->write_registers(dev.reg); }); throw; } dev.cmd_set->update_home_sensor_gpio(dev); if (is_testing_mode()) { dev.interface->test_checkpoint("move_back_home"); dev.set_head_pos_zero(ScanHeadId::PRIMARY); return; } if (wait_until_home) { for (unsigned i = 0; i < 300; ++i) { auto status = scanner_read_status(dev); if (status.is_at_home) { dbg.log(DBG_info, "reached home position"); if (dev.model->asic_type == AsicType::GL846 || dev.model->asic_type == AsicType::GL847) { scanner_stop_action(dev); } dev.set_head_pos_zero(ScanHeadId::PRIMARY); return; } dev.interface->sleep_ms(100); } // when we come here then the scanner needed too much time for this, so we better stop // the motor catch_all_exceptions(__func__, [&](){ scanner_stop_action(dev); }); dev.set_head_pos_unknown(ScanHeadId::PRIMARY | ScanHeadId::SECONDARY); throw SaneException(SANE_STATUS_IO_ERROR, "timeout while waiting for scanhead to go home"); } dbg.log(DBG_info, "scanhead is still moving"); } namespace { bool should_use_secondary_motor_mode(Genesys_Device& dev) { bool should_use = !dev.is_head_pos_known(ScanHeadId::SECONDARY) || !dev.is_head_pos_known(ScanHeadId::PRIMARY) || dev.head_pos(ScanHeadId::SECONDARY) > dev.head_pos(ScanHeadId::PRIMARY); bool supports = dev.model->model_id == ModelId::CANON_8600F; return should_use && supports; } void handle_motor_position_after_move_back_home_ta(Genesys_Device& dev, MotorMode motor_mode) { if (motor_mode == MotorMode::SECONDARY) { dev.set_head_pos_zero(ScanHeadId::SECONDARY); return; } if (dev.is_head_pos_known(ScanHeadId::PRIMARY)) { if (dev.head_pos(ScanHeadId::PRIMARY) > dev.head_pos(ScanHeadId::SECONDARY)) { dev.advance_head_pos_by_steps(ScanHeadId::PRIMARY, Direction::BACKWARD, dev.head_pos(ScanHeadId::SECONDARY)); } else { dev.set_head_pos_zero(ScanHeadId::PRIMARY); } dev.set_head_pos_zero(ScanHeadId::SECONDARY); } } } // namespace void scanner_move_back_home_ta(Genesys_Device& dev) { DBG_HELPER(dbg); switch (dev.model->asic_type) { case AsicType::GL842: case AsicType::GL843: case AsicType::GL845: break; default: throw SaneException("Unsupported asic type"); } Genesys_Register_Set local_reg = dev.reg; auto scan_method = ScanMethod::TRANSPARENCY; unsigned resolution = dev.model->get_resolution_settings(scan_method).get_min_resolution_y(); const auto& sensor = sanei_genesys_find_sensor(&dev, resolution, 1, scan_method); if (dev.is_head_pos_known(ScanHeadId::SECONDARY) && dev.is_head_pos_known(ScanHeadId::PRIMARY) && dev.head_pos(ScanHeadId::SECONDARY) > 1000 && dev.head_pos(ScanHeadId::SECONDARY) <= dev.head_pos(ScanHeadId::PRIMARY)) { // leave 500 steps for regular slow back home scanner_move(dev, scan_method, dev.head_pos(ScanHeadId::SECONDARY) - 500, Direction::BACKWARD); } ScanSession session; session.params.xres = resolution; session.params.yres = resolution; session.params.startx = 0; session.params.starty = 40000; session.params.pixels = 50; session.params.lines = 3; session.params.depth = 8; session.params.channels = 1; session.params.scan_method = scan_method; session.params.scan_mode = ScanColorMode::GRAY; session.params.color_filter = ColorFilter::GREEN; session.params.contrast_adjustment = dev.settings.contrast; session.params.brightness_adjustment = dev.settings.brightness; session.params.flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::IGNORE_STAGGER_OFFSET | ScanFlag::IGNORE_COLOR_OFFSET | ScanFlag::REVERSE; compute_session(&dev, session, sensor); dev.cmd_set->init_regs_for_scan_session(&dev, sensor, &local_reg, session); scanner_clear_scan_and_feed_counts(dev); dev.interface->write_registers(local_reg); auto motor_mode = should_use_secondary_motor_mode(dev) ? MotorMode::SECONDARY : MotorMode::PRIMARY_AND_SECONDARY; dev.cmd_set->set_motor_mode(dev, local_reg, motor_mode); try { scanner_start_action(dev, true); } catch (...) { catch_all_exceptions(__func__, [&]() { scanner_stop_action(dev); }); // restore original registers catch_all_exceptions(__func__, [&]() { dev.interface->write_registers(dev.reg); }); throw; } if (is_testing_mode()) { dev.interface->test_checkpoint("move_back_home_ta"); handle_motor_position_after_move_back_home_ta(dev, motor_mode); scanner_stop_action(dev); dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY); return; } for (unsigned i = 0; i < 1200; ++i) { auto status = scanner_read_status(dev); if (status.is_at_home) { dbg.log(DBG_info, "TA reached home position"); handle_motor_position_after_move_back_home_ta(dev, motor_mode); scanner_stop_action(dev); dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY); return; } dev.interface->sleep_ms(100); } throw SaneException("Timeout waiting for XPA lamp to park"); } void scanner_search_strip(Genesys_Device& dev, bool forward, bool black) { DBG_HELPER_ARGS(dbg, "%s %s", black ? "black" : "white", forward ? "forward" : "reverse"); if (dev.model->asic_type == AsicType::GL841 && !black && forward) { dev.frontend.set_gain(0, 0xff); dev.frontend.set_gain(1, 0xff); dev.frontend.set_gain(2, 0xff); } // set up for a gray scan at lowest dpi const auto& resolution_settings = dev.model->get_resolution_settings(dev.settings.scan_method); unsigned dpi = resolution_settings.get_min_resolution_x(); unsigned channels = 1; auto& sensor = sanei_genesys_find_sensor(&dev, dpi, channels, dev.settings.scan_method); dev.cmd_set->set_fe(&dev, sensor, AFE_SET); scanner_stop_action(dev); // shading calibration is done with dev.motor.base_ydpi unsigned lines = static_cast(dev.model->y_size_calib_mm * dpi / MM_PER_INCH); if (dev.model->asic_type == AsicType::GL841) { lines = 10; // TODO: use dev.model->search_lines lines = static_cast((lines * dpi) / MM_PER_INCH); } unsigned pixels = dev.model->x_size_calib_mm * dpi / MM_PER_INCH; dev.set_head_pos_zero(ScanHeadId::PRIMARY); unsigned length = 20; if (dev.model->asic_type == AsicType::GL841) { // 20 cm max length for calibration sheet length = static_cast(((200 * dpi) / MM_PER_INCH) / lines); } auto local_reg = dev.reg; ScanSession session; session.params.xres = dpi; session.params.yres = dpi; session.params.startx = 0; session.params.starty = 0; session.params.pixels = pixels; session.params.lines = lines; session.params.depth = 8; session.params.channels = channels; session.params.scan_method = dev.settings.scan_method; session.params.scan_mode = ScanColorMode::GRAY; session.params.color_filter = ColorFilter::RED; session.params.contrast_adjustment = dev.settings.contrast; session.params.brightness_adjustment = dev.settings.brightness; session.params.flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA; if (dev.model->asic_type != AsicType::GL841 && !forward) { session.params.flags |= ScanFlag::REVERSE; } compute_session(&dev, session, sensor); dev.cmd_set->init_regs_for_scan_session(&dev, sensor, &local_reg, session); dev.interface->write_registers(local_reg); dev.cmd_set->begin_scan(&dev, sensor, &local_reg, true); if (is_testing_mode()) { dev.interface->test_checkpoint("search_strip"); scanner_stop_action(dev); return; } wait_until_buffer_non_empty(&dev); // now we're on target, we can read data auto image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes); scanner_stop_action(dev); unsigned pass = 0; if (dbg_log_image_data()) { char title[80]; std::sprintf(title, "gl_search_strip_%s_%s%02d.tiff", black ? "black" : "white", forward ? "fwd" : "bwd", pass); write_tiff_file(title, image); } // loop until strip is found or maximum pass number done bool found = false; while (pass < length && !found) { dev.interface->write_registers(local_reg); // now start scan dev.cmd_set->begin_scan(&dev, sensor, &local_reg, true); wait_until_buffer_non_empty(&dev); // now we're on target, we can read data image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes); scanner_stop_action(dev); if (dbg_log_image_data()) { char title[80]; std::sprintf(title, "gl_search_strip_%s_%s%02d.tiff", black ? "black" : "white", forward ? "fwd" : "bwd", static_cast(pass)); write_tiff_file(title, image); } unsigned white_level = 90; unsigned black_level = 60; std::size_t count = 0; // Search data to find black strip // When searching forward, we only need one line of the searched color since we // will scan forward. But when doing backward search, we need all the area of the ame color if (forward) { for (std::size_t y = 0; y < image.get_height() && !found; y++) { count = 0; // count of white/black pixels depending on the color searched for (std::size_t x = 0; x < image.get_width(); x++) { // when searching for black, detect white pixels if (black && image.get_raw_channel(x, y, 0) > white_level) { count++; } // when searching for white, detect black pixels if (!black && image.get_raw_channel(x, y, 0) < black_level) { count++; } } // at end of line, if count >= 3%, line is not fully of the desired color // so we must go to next line of the buffer */ // count*100/pixels < 3 auto found_percentage = (count * 100 / image.get_width()); if (found_percentage < 3) { found = 1; DBG(DBG_data, "%s: strip found forward during pass %d at line %zu\n", __func__, pass, y); } else { DBG(DBG_data, "%s: pixels=%zu, count=%zu (%zu%%)\n", __func__, image.get_width(), count, found_percentage); } } } else { /* since calibration scans are done forward, we need the whole area to be of the required color when searching backward */ count = 0; for (std::size_t y = 0; y < image.get_height(); y++) { // count of white/black pixels depending on the color searched for (std::size_t x = 0; x < image.get_width(); x++) { // when searching for black, detect white pixels if (black && image.get_raw_channel(x, y, 0) > white_level) { count++; } // when searching for white, detect black pixels if (!black && image.get_raw_channel(x, y, 0) < black_level) { count++; } } } // at end of area, if count >= 3%, area is not fully of the desired color // so we must go to next buffer auto found_percentage = count * 100 / (image.get_width() * image.get_height()); if (found_percentage < 3) { found = 1; DBG(DBG_data, "%s: strip found backward during pass %d \n", __func__, pass); } else { DBG(DBG_data, "%s: pixels=%zu, count=%zu (%zu%%)\n", __func__, image.get_width(), count, found_percentage); } } pass++; } if (found) { DBG(DBG_info, "%s: %s strip found\n", __func__, black ? "black" : "white"); } else { throw SaneException(SANE_STATUS_UNSUPPORTED, "%s strip not found", black ? "black" : "white"); } } static int dark_average_channel(const Image& image, unsigned black, unsigned channel) { auto channels = get_pixel_channels(image.get_format()); unsigned avg[3]; // computes average values on black margin for (unsigned ch = 0; ch < channels; ch++) { avg[ch] = 0; unsigned count = 0; // FIXME: start with the second line because the black pixels often have noise on the first // line; the cause is probably incorrectly cleaned up previous scan for (std::size_t y = 1; y < image.get_height(); y++) { for (unsigned j = 0; j < black; j++) { avg[ch] += image.get_raw_channel(j, y, ch); count++; } } if (count > 0) { avg[ch] /= count; } DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, ch, avg[ch]); } DBG(DBG_info, "%s: average = %d\n", __func__, avg[channel]); return avg[channel]; } bool should_calibrate_only_active_area(const Genesys_Device& dev, const Genesys_Settings& settings) { if (settings.scan_method == ScanMethod::TRANSPARENCY || settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { if (dev.model->model_id == ModelId::CANON_4400F && settings.xres >= 4800) { return true; } if (dev.model->model_id == ModelId::CANON_8600F && settings.xres == 4800) { return true; } } return false; } void scanner_offset_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) { DBG_HELPER(dbg); if (dev.model->asic_type == AsicType::GL842 && dev.frontend.layout.type != FrontendType::WOLFSON) { return; } if (dev.model->asic_type == AsicType::GL843 && dev.frontend.layout.type != FrontendType::WOLFSON) { return; } if (dev.model->asic_type == AsicType::GL845 || dev.model->asic_type == AsicType::GL846) { // no gain nor offset for AKM AFE std::uint8_t reg04 = dev.interface->read_register(gl846::REG_0x04); if ((reg04 & gl846::REG_0x04_FESET) == 0x02) { return; } } if (dev.model->asic_type == AsicType::GL847) { // no gain nor offset for AKM AFE std::uint8_t reg04 = dev.interface->read_register(gl847::REG_0x04); if ((reg04 & gl847::REG_0x04_FESET) == 0x02) { return; } } if (dev.model->asic_type == AsicType::GL124) { std::uint8_t reg0a = dev.interface->read_register(gl124::REG_0x0A); if (((reg0a & gl124::REG_0x0A_SIFSEL) >> gl124::REG_0x0AS_SIFSEL) == 3) { return; } } unsigned target_pixels = dev.model->x_size_calib_mm * sensor.full_resolution / MM_PER_INCH; unsigned start_pixel = 0; unsigned black_pixels = (sensor.black_pixels * sensor.full_resolution) / sensor.full_resolution; unsigned channels = 3; unsigned lines = 1; unsigned resolution = sensor.full_resolution; const Genesys_Sensor* calib_sensor = &sensor; if (dev.model->asic_type == AsicType::GL843) { lines = 8; // compute divider factor to compute final pixels number const auto& dpihw_sensor = sanei_genesys_find_sensor(&dev, dev.settings.xres, channels, dev.settings.scan_method); resolution = dpihw_sensor.shading_resolution; unsigned factor = sensor.full_resolution / resolution; calib_sensor = &sanei_genesys_find_sensor(&dev, resolution, channels, dev.settings.scan_method); target_pixels = dev.model->x_size_calib_mm * resolution / MM_PER_INCH; black_pixels = calib_sensor->black_pixels / factor; if (should_calibrate_only_active_area(dev, dev.settings)) { float offset = dev.model->x_offset_ta; start_pixel = static_cast((offset * calib_sensor->get_optical_resolution()) / MM_PER_INCH); float size = dev.model->x_size_ta; target_pixels = static_cast((size * calib_sensor->get_optical_resolution()) / MM_PER_INCH); } if (dev.model->model_id == ModelId::CANON_4400F && dev.settings.scan_method == ScanMethod::FLATBED) { return; } } if (dev.model->model_id == ModelId::CANON_5600F) { // FIXME: use same approach as for GL843 scanners lines = 8; } if (dev.model->asic_type == AsicType::GL847) { calib_sensor = &sanei_genesys_find_sensor(&dev, resolution, channels, dev.settings.scan_method); } ScanFlag flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::SINGLE_LINE | ScanFlag::IGNORE_STAGGER_OFFSET | ScanFlag::IGNORE_COLOR_OFFSET; if (dev.settings.scan_method == ScanMethod::TRANSPARENCY || dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { flags |= ScanFlag::USE_XPA; } ScanSession session; session.params.xres = resolution; session.params.yres = resolution; session.params.startx = start_pixel; session.params.starty = 0; session.params.pixels = target_pixels; session.params.lines = lines; session.params.depth = 8; session.params.channels = channels; session.params.scan_method = dev.settings.scan_method; session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; session.params.color_filter = dev.model->asic_type == AsicType::GL843 ? ColorFilter::RED : dev.settings.color_filter; session.params.contrast_adjustment = dev.settings.contrast; session.params.brightness_adjustment = dev.settings.brightness; session.params.flags = flags; compute_session(&dev, session, *calib_sensor); dev.cmd_set->init_regs_for_scan_session(&dev, *calib_sensor, ®s, session); unsigned output_pixels = session.output_pixels; sanei_genesys_set_motor_power(regs, false); int top[3], bottom[3]; int topavg[3], bottomavg[3], avg[3]; // init gain and offset for (unsigned ch = 0; ch < 3; ch++) { bottom[ch] = 10; dev.frontend.set_offset(ch, bottom[ch]); dev.frontend.set_gain(ch, 0); } dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET); // scan with bottom AFE settings dev.interface->write_registers(regs); DBG(DBG_info, "%s: starting first line reading\n", __func__); dev.cmd_set->begin_scan(&dev, *calib_sensor, ®s, true); if (is_testing_mode()) { dev.interface->test_checkpoint("offset_calibration"); if (dev.model->asic_type == AsicType::GL842 || dev.model->asic_type == AsicType::GL843) { scanner_stop_action_no_move(dev, regs); } return; } Image first_line; if (dev.model->asic_type == AsicType::GL842 || dev.model->asic_type == AsicType::GL843) { first_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes_raw); scanner_stop_action_no_move(dev, regs); } else { first_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes); if (dev.model->model_id == ModelId::CANON_5600F) { scanner_stop_action_no_move(dev, regs); } } if (dbg_log_image_data()) { char fn[40]; std::snprintf(fn, 40, "gl843_bottom_offset_%03d_%03d_%03d.tiff", bottom[0], bottom[1], bottom[2]); write_tiff_file(fn, first_line); } for (unsigned ch = 0; ch < 3; ch++) { bottomavg[ch] = dark_average_channel(first_line, black_pixels, ch); DBG(DBG_info, "%s: bottom avg %d=%d\n", __func__, ch, bottomavg[ch]); } // now top value for (unsigned ch = 0; ch < 3; ch++) { top[ch] = 255; dev.frontend.set_offset(ch, top[ch]); } dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET); // scan with top AFE values dev.interface->write_registers(regs); DBG(DBG_info, "%s: starting second line reading\n", __func__); dev.cmd_set->begin_scan(&dev, *calib_sensor, ®s, true); Image second_line; if (dev.model->asic_type == AsicType::GL842 || dev.model->asic_type == AsicType::GL843) { second_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes_raw); scanner_stop_action_no_move(dev, regs); } else { second_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes); if (dev.model->model_id == ModelId::CANON_5600F) { scanner_stop_action_no_move(dev, regs); } } for (unsigned ch = 0; ch < 3; ch++){ topavg[ch] = dark_average_channel(second_line, black_pixels, ch); DBG(DBG_info, "%s: top avg %d=%d\n", __func__, ch, topavg[ch]); } unsigned pass = 0; std::vector debug_image; std::size_t debug_image_lines = 0; std::string debug_image_info; // loop until acceptable level while ((pass < 32) && ((top[0] - bottom[0] > 1) || (top[1] - bottom[1] > 1) || (top[2] - bottom[2] > 1))) { pass++; for (unsigned ch = 0; ch < 3; ch++) { if (top[ch] - bottom[ch] > 1) { dev.frontend.set_offset(ch, (top[ch] + bottom[ch]) / 2); } } dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET); // scan with no move dev.interface->write_registers(regs); DBG(DBG_info, "%s: starting second line reading\n", __func__); dev.cmd_set->begin_scan(&dev, *calib_sensor, ®s, true); if (dev.model->asic_type == AsicType::GL842 || dev.model->asic_type == AsicType::GL843) { second_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes_raw); scanner_stop_action_no_move(dev, regs); } else { second_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes); if (dev.model->model_id == ModelId::CANON_5600F) { scanner_stop_action_no_move(dev, regs); } } if (dbg_log_image_data()) { char title[100]; std::snprintf(title, 100, "lines: %d pixels_per_line: %d offsets[0..2]: %d %d %d\n", lines, output_pixels, dev.frontend.get_offset(0), dev.frontend.get_offset(1), dev.frontend.get_offset(2)); debug_image_info += title; std::copy(second_line.get_row_ptr(0), second_line.get_row_ptr(0) + second_line.get_row_bytes() * second_line.get_height(), std::back_inserter(debug_image)); debug_image_lines += lines; } for (unsigned ch = 0; ch < 3; ch++) { avg[ch] = dark_average_channel(second_line, black_pixels, ch); DBG(DBG_info, "%s: avg[%d]=%d offset=%d\n", __func__, ch, avg[ch], dev.frontend.get_offset(ch)); } // compute new boundaries for (unsigned ch = 0; ch < 3; ch++) { if (topavg[ch] >= avg[ch]) { topavg[ch] = avg[ch]; top[ch] = dev.frontend.get_offset(ch); } else { bottomavg[ch] = avg[ch]; bottom[ch] = dev.frontend.get_offset(ch); } } } if (dbg_log_image_data()) { sanei_genesys_write_file("gl_offset_all_desc.txt", reinterpret_cast(debug_image_info.data()), debug_image_info.size()); write_tiff_file("gl_offset_all.tiff", debug_image.data(), session.params.depth, channels, output_pixels, debug_image_lines); } DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__, dev.frontend.get_offset(0), dev.frontend.get_offset(1), dev.frontend.get_offset(2)); } /* With offset and coarse calibration we only want to get our input range into a reasonable shape. the fine calibration of the upper and lower bounds will be done with shading. */ void scanner_coarse_gain_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs, unsigned dpi) { DBG_HELPER_ARGS(dbg, "dpi = %d", dpi); if (dev.model->asic_type == AsicType::GL842 && dev.frontend.layout.type != FrontendType::WOLFSON) { return; } if (dev.model->asic_type == AsicType::GL843 && dev.frontend.layout.type != FrontendType::WOLFSON) { return; } if (dev.model->asic_type == AsicType::GL845 || dev.model->asic_type == AsicType::GL846) { // no gain nor offset for AKM AFE std::uint8_t reg04 = dev.interface->read_register(gl846::REG_0x04); if ((reg04 & gl846::REG_0x04_FESET) == 0x02) { return; } } if (dev.model->asic_type == AsicType::GL847) { // no gain nor offset for AKM AFE std::uint8_t reg04 = dev.interface->read_register(gl847::REG_0x04); if ((reg04 & gl847::REG_0x04_FESET) == 0x02) { return; } } if (dev.model->asic_type == AsicType::GL124) { // no gain nor offset for TI AFE std::uint8_t reg0a = dev.interface->read_register(gl124::REG_0x0A); if (((reg0a & gl124::REG_0x0A_SIFSEL) >> gl124::REG_0x0AS_SIFSEL) == 3) { return; } } if (dev.model->asic_type == AsicType::GL841) { // feed to white strip if needed if (dev.model->y_offset_calib_white > 0) { unsigned move = static_cast( (dev.model->y_offset_calib_white * (dev.motor.base_ydpi)) / MM_PER_INCH); scanner_move(dev, dev.model->default_method, move, Direction::FORWARD); } } // coarse gain calibration is always done in color mode unsigned channels = 3; unsigned resolution = sensor.full_resolution; if (dev.model->asic_type == AsicType::GL841) { const auto& dpihw_sensor = sanei_genesys_find_sensor(&dev, dev.settings.xres, channels, dev.settings.scan_method); resolution = dpihw_sensor.shading_resolution; } if (dev.model->asic_type == AsicType::GL842 || dev.model->asic_type == AsicType::GL843) { const auto& dpihw_sensor = sanei_genesys_find_sensor(&dev, dpi, channels, dev.settings.scan_method); resolution = dpihw_sensor.shading_resolution; } float coeff = 1; // Follow CKSEL if (dev.model->sensor_id == SensorId::CCD_KVSS080 || dev.model->asic_type == AsicType::GL845 || dev.model->asic_type == AsicType::GL846 || dev.model->asic_type == AsicType::GL847 || dev.model->asic_type == AsicType::GL124) { if (dev.settings.xres < sensor.full_resolution) { coeff = 0.9f; } } unsigned lines = 10; if (dev.model->asic_type == AsicType::GL841) { lines = 1; } const Genesys_Sensor* calib_sensor = &sensor; if (dev.model->asic_type == AsicType::GL841 || dev.model->asic_type == AsicType::GL842 || dev.model->asic_type == AsicType::GL843 || dev.model->asic_type == AsicType::GL847) { calib_sensor = &sanei_genesys_find_sensor(&dev, resolution, channels, dev.settings.scan_method); } ScanFlag flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::SINGLE_LINE | ScanFlag::IGNORE_STAGGER_OFFSET | ScanFlag::IGNORE_COLOR_OFFSET; if (dev.settings.scan_method == ScanMethod::TRANSPARENCY || dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { flags |= ScanFlag::USE_XPA; } ScanSession session; session.params.xres = resolution; session.params.yres = dev.model->asic_type == AsicType::GL841 ? dev.settings.yres : resolution; session.params.startx = 0; session.params.starty = 0; session.params.pixels = dev.model->x_size_calib_mm * resolution / MM_PER_INCH; session.params.lines = lines; session.params.depth = dev.model->asic_type == AsicType::GL841 ? 16 : 8; session.params.channels = channels; session.params.scan_method = dev.settings.scan_method; session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; session.params.color_filter = dev.settings.color_filter; session.params.contrast_adjustment = dev.settings.contrast; session.params.brightness_adjustment = dev.settings.brightness; session.params.flags = flags; compute_session(&dev, session, *calib_sensor); std::size_t pixels = session.output_pixels; try { dev.cmd_set->init_regs_for_scan_session(&dev, *calib_sensor, ®s, session); } catch (...) { if (dev.model->asic_type != AsicType::GL841) { catch_all_exceptions(__func__, [&](){ sanei_genesys_set_motor_power(regs, false); }); } throw; } if (dev.model->asic_type != AsicType::GL841) { sanei_genesys_set_motor_power(regs, false); } dev.interface->write_registers(regs); if (dev.model->asic_type != AsicType::GL841) { dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET); } dev.cmd_set->begin_scan(&dev, *calib_sensor, ®s, true); if (is_testing_mode()) { dev.interface->test_checkpoint("coarse_gain_calibration"); scanner_stop_action(dev); dev.cmd_set->move_back_home(&dev, true); return; } Image image; if (dev.model->asic_type == AsicType::GL842 || dev.model->asic_type == AsicType::GL843) { image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes_raw); } else if (dev.model->asic_type == AsicType::GL124) { // BUG: we probably want to read whole image, not just first line image = read_unshuffled_image_from_scanner(&dev, session, session.output_line_bytes); } else { image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes); } if (dev.model->asic_type == AsicType::GL842 || dev.model->asic_type == AsicType::GL843) { scanner_stop_action_no_move(dev, regs); } if (dbg_log_image_data()) { write_tiff_file("gl_coarse_gain.tiff", image); } for (unsigned ch = 0; ch < channels; ch++) { float curr_output = 0; float target_value = 0; if (dev.model->asic_type == AsicType::GL841 || dev.model->asic_type == AsicType::GL842 || dev.model->asic_type == AsicType::GL843) { std::vector values; // FIXME: start from the second line because the first line often has artifacts. Probably // caused by unclean cleanup of previous scan for (std::size_t x = pixels / 4; x < (pixels * 3 / 4); x++) { values.push_back(image.get_raw_channel(x, 1, ch)); } // pick target value at 95th percentile of all values. There may be a lot of black values // in transparency scans for example std::sort(values.begin(), values.end()); curr_output = static_cast(values[unsigned((values.size() - 1) * 0.95)]); target_value = calib_sensor->gain_white_ref * coeff; } else { // FIXME: use the GL843 approach auto width = image.get_width(); std::uint64_t total = 0; for (std::size_t x = width / 4; x < (width * 3 / 4); x++) { total += image.get_raw_channel(x, 0, ch); } curr_output = total / (width / 2); target_value = calib_sensor->gain_white_ref * coeff; } std::uint8_t out_gain = compute_frontend_gain(curr_output, target_value, dev.frontend.layout.type); dev.frontend.set_gain(ch, out_gain); DBG(DBG_proc, "%s: channel %d, curr=%f, target=%f, out_gain:%d\n", __func__, ch, curr_output, target_value, out_gain); if (dev.model->asic_type == AsicType::GL841 && target_value / curr_output > 30) { DBG(DBG_error0, "****************************************\n"); DBG(DBG_error0, "* *\n"); DBG(DBG_error0, "* Extremely low Brightness detected. *\n"); DBG(DBG_error0, "* Check the scanning head is *\n"); DBG(DBG_error0, "* unlocked and moving. *\n"); DBG(DBG_error0, "* *\n"); DBG(DBG_error0, "****************************************\n"); throw SaneException(SANE_STATUS_JAMMED, "scanning head is locked"); } dbg.vlog(DBG_info, "gain=(%d, %d, %d)", dev.frontend.get_gain(0), dev.frontend.get_gain(1), dev.frontend.get_gain(2)); } if (dev.model->is_cis) { std::uint8_t min_gain = std::min({dev.frontend.get_gain(0), dev.frontend.get_gain(1), dev.frontend.get_gain(2)}); dev.frontend.set_gain(0, min_gain); dev.frontend.set_gain(1, min_gain); dev.frontend.set_gain(2, min_gain); } dbg.vlog(DBG_info, "final gain=(%d, %d, %d)", dev.frontend.get_gain(0), dev.frontend.get_gain(1), dev.frontend.get_gain(2)); scanner_stop_action(dev); dev.cmd_set->move_back_home(&dev, true); } namespace gl124 { void move_to_calibration_area(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs); } // namespace gl124 SensorExposure scanner_led_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) { DBG_HELPER(dbg); float move = 0; if (dev.model->asic_type == AsicType::GL841) { if (dev.model->y_offset_calib_white > 0) { move = (dev.model->y_offset_calib_white * (dev.motor.base_ydpi)) / MM_PER_INCH; scanner_move(dev, dev.model->default_method, static_cast(move), Direction::FORWARD); } } else if (dev.model->asic_type == AsicType::GL842 || dev.model->asic_type == AsicType::GL843) { // do nothing } else if (dev.model->asic_type == AsicType::GL845 || dev.model->asic_type == AsicType::GL846 || dev.model->asic_type == AsicType::GL847) { move = dev.model->y_offset_calib_white; move = static_cast((move * (dev.motor.base_ydpi / 4)) / MM_PER_INCH); if (move > 20) { scanner_move(dev, dev.model->default_method, static_cast(move), Direction::FORWARD); } } else if (dev.model->asic_type == AsicType::GL124) { gl124::move_to_calibration_area(&dev, sensor, regs); } unsigned channels = 3; unsigned resolution = sensor.shading_resolution; const auto& calib_sensor = sanei_genesys_find_sensor(&dev, resolution, channels, dev.settings.scan_method); if (dev.model->asic_type == AsicType::GL841 || dev.model->asic_type == AsicType::GL845 || dev.model->asic_type == AsicType::GL846 || dev.model->asic_type == AsicType::GL847 || dev.model->asic_type == AsicType::GL124) { regs = dev.reg; // FIXME: apply this to all ASICs } ScanSession session; session.params.xres = resolution; session.params.yres = resolution; session.params.startx = 0; session.params.starty = 0; session.params.pixels = dev.model->x_size_calib_mm * resolution / MM_PER_INCH; session.params.lines = 1; session.params.depth = 16; session.params.channels = channels; session.params.scan_method = dev.settings.scan_method; session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; session.params.color_filter = dev.settings.color_filter; session.params.contrast_adjustment = dev.settings.contrast; session.params.brightness_adjustment = dev.settings.brightness; session.params.flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::SINGLE_LINE | ScanFlag::IGNORE_STAGGER_OFFSET | ScanFlag::IGNORE_COLOR_OFFSET; compute_session(&dev, session, calib_sensor); dev.cmd_set->init_regs_for_scan_session(&dev, calib_sensor, ®s, session); std::uint16_t exp[3]; exp[0] = calib_sensor.exposure.red; exp[1] = calib_sensor.exposure.green; exp[2] = calib_sensor.exposure.blue; std::uint16_t target = sensor.gain_white_ref * 256; std::uint16_t top[3] = {}; std::uint16_t bottom[3] = {}; if (dev.model->asic_type == AsicType::GL845 || dev.model->asic_type == AsicType::GL846) { bottom[0] = 29000; bottom[1] = 29000; bottom[2] = 29000; top[0] = 41000; top[1] = 51000; top[2] = 51000; } else if (dev.model->asic_type == AsicType::GL847) { bottom[0] = 28000; bottom[1] = 28000; bottom[2] = 28000; top[0] = 32000; top[1] = 32000; top[2] = 32000; } if (dev.model->asic_type == AsicType::GL845 || dev.model->asic_type == AsicType::GL846 || dev.model->asic_type == AsicType::GL847 || dev.model->asic_type == AsicType::GL124) { sanei_genesys_set_motor_power(regs, false); } bool acceptable = false; for (unsigned i_test = 0; i_test < 100 && !acceptable; ++i_test) { regs_set_exposure(dev.model->asic_type, regs, { exp[0], exp[1], exp[2] }); dev.interface->write_registers(regs); dbg.log(DBG_info, "starting line reading"); dev.cmd_set->begin_scan(&dev, calib_sensor, ®s, true); if (is_testing_mode()) { dev.interface->test_checkpoint("led_calibration"); if (dev.model->asic_type == AsicType::GL841) { scanner_stop_action(dev); dev.cmd_set->move_back_home(&dev, true); } else if (dev.model->asic_type == AsicType::GL124) { scanner_stop_action(dev); } else { scanner_stop_action(dev); dev.cmd_set->move_back_home(&dev, true); } return { exp[0], exp[1], exp[2] }; } auto image = read_unshuffled_image_from_scanner(&dev, session, session.output_line_bytes); scanner_stop_action(dev); if (dbg_log_image_data()) { char fn[30]; std::snprintf(fn, 30, "gl_led_%02d.tiff", i_test); write_tiff_file(fn, image); } int avg[3]; for (unsigned ch = 0; ch < channels; ch++) { avg[ch] = 0; for (std::size_t x = 0; x < image.get_width(); x++) { avg[ch] += image.get_raw_channel(x, 0, ch); } avg[ch] /= image.get_width(); } dbg.vlog(DBG_info, "average: %d, %d, %d", avg[0], avg[1], avg[2]); acceptable = true; if (dev.model->asic_type == AsicType::GL845 || dev.model->asic_type == AsicType::GL846) { for (unsigned i = 0; i < 3; i++) { if (avg[i] < bottom[i]) { if (avg[i] != 0) { exp[i] = (exp[i] * bottom[i]) / avg[i]; } else { exp[i] *= 10; } acceptable = false; } if (avg[i] > top[i]) { if (avg[i] != 0) { exp[i] = (exp[i] * top[i]) / avg[i]; } else { exp[i] *= 10; } acceptable = false; } } } else if (dev.model->asic_type == AsicType::GL847) { for (unsigned i = 0; i < 3; i++) { if (avg[i] < bottom[i] || avg[i] > top[i]) { auto target = (bottom[i] + top[i]) / 2; if (avg[i] != 0) { exp[i] = (exp[i] * target) / avg[i]; } else { exp[i] *= 10; } acceptable = false; } } } else if (dev.model->asic_type == AsicType::GL841 || dev.model->asic_type == AsicType::GL124) { for (unsigned i = 0; i < 3; i++) { // we accept +- 2% delta from target if (std::abs(avg[i] - target) > target / 50) { float prev_weight = 0.5; if (avg[i] != 0) { exp[i] = exp[i] * prev_weight + ((exp[i] * target) / avg[i]) * (1 - prev_weight); } else { exp[i] = exp[i] * prev_weight + (exp[i] * 10) * (1 - prev_weight); } acceptable = false; } } } } if (dev.model->asic_type == AsicType::GL845 || dev.model->asic_type == AsicType::GL846 || dev.model->asic_type == AsicType::GL847 || dev.model->asic_type == AsicType::GL124) { // set these values as final ones for scan regs_set_exposure(dev.model->asic_type, dev.reg, { exp[0], exp[1], exp[2] }); } if (dev.model->asic_type == AsicType::GL841 || dev.model->asic_type == AsicType::GL842 || dev.model->asic_type == AsicType::GL843) { dev.cmd_set->move_back_home(&dev, true); } if (dev.model->asic_type == AsicType::GL845 || dev.model->asic_type == AsicType::GL846 || dev.model->asic_type == AsicType::GL847) { if (move > 20) { dev.cmd_set->move_back_home(&dev, true); } } dbg.vlog(DBG_info,"acceptable exposure: %d, %d, %d\n", exp[0], exp[1], exp[2]); return { exp[0], exp[1], exp[2] }; } void sanei_genesys_calculate_zmod(bool two_table, std::uint32_t exposure_time, const std::vector& slope_table, unsigned acceleration_steps, unsigned move_steps, unsigned buffer_acceleration_steps, std::uint32_t* out_z1, std::uint32_t* out_z2) { // acceleration total time unsigned sum = std::accumulate(slope_table.begin(), slope_table.begin() + acceleration_steps, 0, std::plus()); /* Z1MOD: c = sum(slope_table; reg_stepno) d = reg_fwdstep * Z1MOD = (c+d) % exposure_time */ *out_z1 = (sum + buffer_acceleration_steps * slope_table[acceleration_steps - 1]) % exposure_time; /* Z2MOD: a = sum(slope_table; reg_stepno) b = move_steps or 1 if 2 tables Z1MOD = (a+b) % exposure_time */ if (!two_table) { sum = sum + (move_steps * slope_table[acceleration_steps - 1]); } else { sum = sum + slope_table[acceleration_steps - 1]; } *out_z2 = sum % exposure_time; } /** * scans a white area with motor and lamp off to get the per CCD pixel offset * that will be used to compute shading coefficient * @param dev scanner's device */ static void genesys_shading_calibration_impl(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& local_reg, std::vector& out_average_data, bool is_dark, const std::string& log_filename_prefix) { DBG_HELPER(dbg); if (dev->model->asic_type == AsicType::GL646) { dev->cmd_set->init_regs_for_shading(dev, sensor, local_reg); local_reg = dev->reg; } else { local_reg = dev->reg; dev->cmd_set->init_regs_for_shading(dev, sensor, local_reg); dev->interface->write_registers(local_reg); } debug_dump(DBG_info, dev->calib_session); size_t size; std::uint32_t pixels_per_line; if (dev->model->asic_type == AsicType::GL842 || dev->model->asic_type == AsicType::GL843 || dev->model->model_id == ModelId::CANON_5600F) { pixels_per_line = dev->calib_session.output_pixels; } else { // BUG: this selects incorrect pixel number pixels_per_line = dev->calib_session.params.pixels; } unsigned channels = dev->calib_session.params.channels; // BUG: we are using wrong pixel number here unsigned start_offset = dev->calib_session.params.startx * sensor.full_resolution / dev->calib_session.params.xres; unsigned out_pixels_per_line = pixels_per_line + start_offset; // FIXME: we set this during both dark and white calibration. A cleaner approach should // probably be used dev->average_size = channels * out_pixels_per_line; out_average_data.clear(); out_average_data.resize(dev->average_size); if (is_dark && dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { // FIXME: dark shading currently not supported on infrared transparency scans return; } // FIXME: the current calculation is likely incorrect on non-GL843 implementations, // but this needs checking. Note the extra line when computing size. if (dev->model->asic_type == AsicType::GL842 || dev->model->asic_type == AsicType::GL843 || dev->model->model_id == ModelId::CANON_5600F) { size = dev->calib_session.output_total_bytes_raw; } else { size = channels * 2 * pixels_per_line * (dev->calib_session.params.lines + 1); } std::vector calibration_data(size / 2); // turn off motor and lamp power for flatbed scanners, but not for sheetfed scanners // because they have a calibration sheet with a sufficient black strip if (is_dark && !dev->model->is_sheetfed) { sanei_genesys_set_lamp_power(dev, sensor, local_reg, false); } else { sanei_genesys_set_lamp_power(dev, sensor, local_reg, true); } sanei_genesys_set_motor_power(local_reg, true); dev->interface->write_registers(local_reg); if (is_dark) { // wait some time to let lamp to get dark dev->interface->sleep_ms(200); } else if (has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) { // make sure lamp is bright again // FIXME: what about scanners that take a long time to warm the lamp? dev->interface->sleep_ms(500); } bool start_motor = !is_dark; dev->cmd_set->begin_scan(dev, sensor, &local_reg, start_motor); if (is_testing_mode()) { dev->interface->test_checkpoint(is_dark ? "dark_shading_calibration" : "white_shading_calibration"); dev->cmd_set->end_scan(dev, &local_reg, true); return; } sanei_genesys_read_data_from_scanner(dev, reinterpret_cast(calibration_data.data()), size); dev->cmd_set->end_scan(dev, &local_reg, true); if (has_flag(dev->model->flags, ModelFlag::SWAP_16BIT_DATA)) { for (std::size_t i = 0; i < size / 2; ++i) { auto value = calibration_data[i]; value = ((value >> 8) & 0xff) | ((value << 8) & 0xff00); calibration_data[i] = value; } } if (has_flag(dev->model->flags, ModelFlag::INVERT_PIXEL_DATA)) { for (std::size_t i = 0; i < size / 2; ++i) { calibration_data[i] = 0xffff - calibration_data[i]; } } std::fill(out_average_data.begin(), out_average_data.begin() + start_offset * channels, 0); compute_array_percentile_approx(out_average_data.data() + start_offset * channels, calibration_data.data(), dev->calib_session.params.lines, pixels_per_line * channels, 0.5f); if (dbg_log_image_data()) { write_tiff_file(log_filename_prefix + "_shading.tiff", calibration_data.data(), 16, channels, pixels_per_line, dev->calib_session.params.lines); write_tiff_file(log_filename_prefix + "_average.tiff", out_average_data.data(), 16, channels, out_pixels_per_line, 1); } } /* * this function builds dummy dark calibration data so that we can * compute shading coefficient in a clean way * todo: current values are hardcoded, we have to find if they * can be computed from previous calibration data (when doing offset * calibration ?) */ static void genesys_dark_shading_by_dummy_pixel(Genesys_Device* dev, const Genesys_Sensor& sensor) { DBG_HELPER(dbg); std::uint32_t pixels_per_line; std::uint32_t skip, xend; int dummy1, dummy2, dummy3; /* dummy black average per channel */ if (dev->model->asic_type == AsicType::GL842 || dev->model->asic_type == AsicType::GL843) { pixels_per_line = dev->calib_session.output_pixels; } else { pixels_per_line = dev->calib_session.params.pixels; } unsigned channels = dev->calib_session.params.channels; // BUG: we are using wrong pixel number here unsigned start_offset = dev->calib_session.params.startx * sensor.full_resolution / dev->calib_session.params.xres; unsigned out_pixels_per_line = pixels_per_line + start_offset; dev->average_size = channels * out_pixels_per_line; dev->dark_average_data.clear(); dev->dark_average_data.resize(dev->average_size, 0); /* we average values on 'the left' where CCD pixels are under casing and give darkest values. We then use these as dummy dark calibration */ if (dev->settings.xres <= sensor.full_resolution / 2) { skip = 4; xend = 36; } else { skip = 4; xend = 68; } if (dev->model->sensor_id==SensorId::CCD_G4050 || dev->model->sensor_id==SensorId::CCD_HP_4850C || dev->model->sensor_id==SensorId::CCD_CANON_4400F || dev->model->sensor_id==SensorId::CCD_CANON_8400F || dev->model->sensor_id==SensorId::CCD_KVSS080) { skip = 2; xend = sensor.black_pixels; } /* average each channels on half left margin */ dummy1 = 0; dummy2 = 0; dummy3 = 0; for (unsigned x = skip + 1; x <= xend; x++) { dummy1 += dev->white_average_data[channels * x]; if (channels > 1) { dummy2 += dev->white_average_data[channels * x + 1]; dummy3 += dev->white_average_data[channels * x + 2]; } } dummy1 /= (xend - skip); if (channels > 1) { dummy2 /= (xend - skip); dummy3 /= (xend - skip); } DBG(DBG_proc, "%s: dummy1=%d, dummy2=%d, dummy3=%d \n", __func__, dummy1, dummy2, dummy3); /* fill dark_average */ for (unsigned x = 0; x < out_pixels_per_line; x++) { dev->dark_average_data[channels * x] = dummy1; if (channels > 1) { dev->dark_average_data[channels * x + 1] = dummy2; dev->dark_average_data[channels * x + 2] = dummy3; } } } static void genesys_dark_shading_by_constant(Genesys_Device& dev) { dev.dark_average_data.clear(); dev.dark_average_data.resize(dev.average_size, 0x0101); } static void genesys_repark_sensor_before_shading(Genesys_Device* dev) { DBG_HELPER(dbg); if (has_flag(dev->model->flags, ModelFlag::SHADING_REPARK)) { dev->cmd_set->move_back_home(dev, true); if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { scanner_move_to_ta(*dev); } } } static void genesys_repark_sensor_after_white_shading(Genesys_Device* dev) { DBG_HELPER(dbg); if (has_flag(dev->model->flags, ModelFlag::SHADING_REPARK)) { dev->cmd_set->move_back_home(dev, true); } } static void genesys_host_shading_calibration_impl(Genesys_Device& dev, const Genesys_Sensor& sensor, std::vector& out_average_data, bool is_dark, const std::string& log_filename_prefix) { DBG_HELPER(dbg); if (is_dark && dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { // FIXME: dark shading currently not supported on infrared transparency scans return; } auto local_reg = dev.reg; dev.cmd_set->init_regs_for_shading(&dev, sensor, local_reg); auto& session = dev.calib_session; debug_dump(DBG_info, session); // turn off motor and lamp power for flatbed scanners, but not for sheetfed scanners // because they have a calibration sheet with a sufficient black strip if (is_dark && !dev.model->is_sheetfed) { sanei_genesys_set_lamp_power(&dev, sensor, local_reg, false); } else { sanei_genesys_set_lamp_power(&dev, sensor, local_reg, true); } sanei_genesys_set_motor_power(local_reg, true); dev.interface->write_registers(local_reg); if (is_dark) { // wait some time to let lamp to get dark dev.interface->sleep_ms(200); } else if (has_flag(dev.model->flags, ModelFlag::DARK_CALIBRATION)) { // make sure lamp is bright again // FIXME: what about scanners that take a long time to warm the lamp? dev.interface->sleep_ms(500); } bool start_motor = !is_dark; dev.cmd_set->begin_scan(&dev, sensor, &local_reg, start_motor); if (is_testing_mode()) { dev.interface->test_checkpoint(is_dark ? "host_dark_shading_calibration" : "host_white_shading_calibration"); dev.cmd_set->end_scan(&dev, &local_reg, true); return; } Image image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes_raw); scanner_stop_action(dev); auto start_offset = session.params.startx; auto out_pixels_per_line = start_offset + session.output_pixels; // FIXME: we set this during both dark and white calibration. A cleaner approach should // probably be used dev.average_size = session.params.channels * out_pixels_per_line; out_average_data.clear(); out_average_data.resize(dev.average_size); std::fill(out_average_data.begin(), out_average_data.begin() + start_offset * session.params.channels, 0); compute_array_percentile_approx(out_average_data.data() + start_offset * session.params.channels, reinterpret_cast(image.get_row_ptr(0)), session.params.lines, session.output_pixels * session.params.channels, 0.5f); if (dbg_log_image_data()) { write_tiff_file(log_filename_prefix + "_host_shading.tiff", image); write_tiff_file(log_filename_prefix + "_host_average.tiff", out_average_data.data(), 16, session.params.channels, out_pixels_per_line, 1); } } static void genesys_dark_shading_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& local_reg) { DBG_HELPER(dbg); if (has_flag(dev->model->flags, ModelFlag::HOST_SIDE_CALIBRATION_COMPLETE_SCAN)) { genesys_host_shading_calibration_impl(*dev, sensor, dev->dark_average_data, true, "gl_black"); } else { genesys_shading_calibration_impl(dev, sensor, local_reg, dev->dark_average_data, true, "gl_black"); } } static void genesys_white_shading_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& local_reg) { DBG_HELPER(dbg); if (has_flag(dev->model->flags, ModelFlag::HOST_SIDE_CALIBRATION_COMPLETE_SCAN)) { genesys_host_shading_calibration_impl(*dev, sensor, dev->white_average_data, false, "gl_white"); } else { genesys_shading_calibration_impl(dev, sensor, local_reg, dev->white_average_data, false, "gl_white"); } } // This calibration uses a scan over the calibration target, comprising a black and a white strip. // (So the motor must be on.) static void genesys_dark_white_shading_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& local_reg) { DBG_HELPER(dbg); if (dev->model->asic_type == AsicType::GL646) { dev->cmd_set->init_regs_for_shading(dev, sensor, local_reg); local_reg = dev->reg; } else { local_reg = dev->reg; dev->cmd_set->init_regs_for_shading(dev, sensor, local_reg); dev->interface->write_registers(local_reg); } std::size_t size; std::uint32_t pixels_per_line; unsigned int x; if (dev->model->asic_type == AsicType::GL842 || dev->model->asic_type == AsicType::GL843) { pixels_per_line = dev->calib_session.output_pixels; } else { pixels_per_line = dev->calib_session.params.pixels; } unsigned channels = dev->calib_session.params.channels; // BUG: we are using wrong pixel number here unsigned start_offset = dev->calib_session.params.startx * sensor.full_resolution / dev->calib_session.params.xres; unsigned out_pixels_per_line = pixels_per_line + start_offset; dev->average_size = channels * out_pixels_per_line; dev->white_average_data.clear(); dev->white_average_data.resize(dev->average_size); dev->dark_average_data.clear(); dev->dark_average_data.resize(dev->average_size); if (dev->model->asic_type == AsicType::GL842 || dev->model->asic_type == AsicType::GL843) { size = dev->calib_session.output_total_bytes_raw; } else { // FIXME: on GL841 this is different than dev->calib_session.output_total_bytes_raw, // needs checking size = channels * 2 * pixels_per_line * dev->calib_session.params.lines; } std::vector calibration_data(size); // turn on motor and lamp power sanei_genesys_set_lamp_power(dev, sensor, local_reg, true); sanei_genesys_set_motor_power(local_reg, true); dev->interface->write_registers(local_reg); dev->cmd_set->begin_scan(dev, sensor, &local_reg, false); if (is_testing_mode()) { dev->interface->test_checkpoint("dark_white_shading_calibration"); dev->cmd_set->end_scan(dev, &local_reg, true); return; } sanei_genesys_read_data_from_scanner(dev, calibration_data.data(), size); dev->cmd_set->end_scan(dev, &local_reg, true); if (dbg_log_image_data()) { if (dev->model->is_cis) { write_tiff_file("gl_black_white_shading.tiff", calibration_data.data(), 16, 1, pixels_per_line*channels, dev->calib_session.params.lines); } else { write_tiff_file("gl_black_white_shading.tiff", calibration_data.data(), 16, channels, pixels_per_line, dev->calib_session.params.lines); } } std::fill(dev->dark_average_data.begin(), dev->dark_average_data.begin() + start_offset * channels, 0); std::fill(dev->white_average_data.begin(), dev->white_average_data.begin() + start_offset * channels, 0); std::uint16_t* average_white = dev->white_average_data.data() + start_offset * channels; std::uint16_t* average_dark = dev->dark_average_data.data() + start_offset * channels; for (x = 0; x < pixels_per_line * channels; x++) { std::uint32_t dark = 0xffff; std::uint32_t white = 0; for (std::size_t y = 0; y < dev->calib_session.params.lines; y++) { std::uint32_t col = calibration_data[(x + y * pixels_per_line * channels) * 2]; col |= calibration_data[(x + y * pixels_per_line * channels) * 2 + 1] << 8; if (col > white) white = col; if (col < dark) dark = col; } std::uint32_t dif = white - dark; dark = dark + dif / 8; white = white - dif / 8; std::uint32_t dark_count = 0; std::uint32_t dark_sum = 0; std::uint32_t white_count = 0; std::uint32_t white_sum = 0; for (std::size_t y = 0; y < dev->calib_session.params.lines; y++) { std::uint32_t col = calibration_data[(x + y * pixels_per_line * channels) * 2]; col |= calibration_data[(x + y * pixels_per_line * channels) * 2 + 1] << 8; if (col >= white) { white_sum += col; white_count++; } if (col <= dark) { dark_sum += col; dark_count++; } } dark_sum /= dark_count; white_sum /= white_count; *average_dark++ = dark_sum; *average_white++ = white_sum; } if (dbg_log_image_data()) { write_tiff_file("gl_white_average.tiff", dev->white_average_data.data(), 16, channels, out_pixels_per_line, 1); write_tiff_file("gl_dark_average.tiff", dev->dark_average_data.data(), 16, channels, out_pixels_per_line, 1); } } /* computes one coefficient given bright-dark value * @param coeff factor giving 1.00 gain * @param target desired target code * @param value brght-dark value * */ static unsigned int compute_coefficient (unsigned int coeff, unsigned int target, unsigned int value) { int result; if (value > 0) { result = (coeff * target) / value; if (result >= 65535) { result = 65535; } } else { result = coeff; } return result; } /** @brief compute shading coefficients for LiDE scanners * The dark/white shading is actually performed _after_ reducing * resolution via averaging. only dark/white shading data for what would be * first pixel at full resolution is used. * * scanner raw input to output value calculation: * o=(i-off)*(gain/coeff) * * from datasheet: * off=dark_average * gain=coeff*bright_target/(bright_average-dark_average) * works for dark_target==0 * * what we want is these: * bright_target=(bright_average-off)*(gain/coeff) * dark_target=(dark_average-off)*(gain/coeff) * leading to * off = (dark_average*bright_target - bright_average*dark_target)/(bright_target - dark_target) * gain = (bright_target - dark_target)/(bright_average - dark_average)*coeff * * @param dev scanner's device * @param shading_data memory area where to store the computed shading coefficients * @param pixels_per_line number of pixels per line * @param words_per_color memory words per color channel * @param channels number of color channels (actually 1 or 3) * @param o shading coefficients left offset * @param coeff 4000h or 2000h depending on fast scan mode or not (GAIN4 bit) * @param target_bright value of the white target code * @param target_dark value of the black target code */ static void compute_averaged_planar(Genesys_Device * dev, const Genesys_Sensor& sensor, std::uint8_t* shading_data, unsigned int pixels_per_line, unsigned int words_per_color, unsigned int channels, unsigned int o, unsigned int coeff, unsigned int target_bright, unsigned int target_dark) { unsigned int x, i, j, br, dk, res, avgpixels, basepixels, val; unsigned int fill,factor; DBG(DBG_info, "%s: pixels=%d, offset=%d\n", __func__, pixels_per_line, o); /* initialize result */ memset (shading_data, 0xff, words_per_color * 3 * 2); /* strangely i can write 0x20000 bytes beginning at 0x00000 without overwriting slope tables - which begin at address 0x10000(for 1200dpi hw mode): memory is organized in words(2 bytes) instead of single bytes. explains quite some things */ /* another one: the dark/white shading is actually performed _after_ reducing resolution via averaging. only dark/white shading data for what would be first pixel at full resolution is used. */ /* scanner raw input to output value calculation: o=(i-off)*(gain/coeff) from datasheet: off=dark_average gain=coeff*bright_target/(bright_average-dark_average) works for dark_target==0 what we want is these: bright_target=(bright_average-off)*(gain/coeff) dark_target=(dark_average-off)*(gain/coeff) leading to off = (dark_average*bright_target - bright_average*dark_target)/(bright_target - dark_target) gain = (bright_target - dark_target)/(bright_average - dark_average)*coeff */ res = dev->settings.xres; if (sensor.full_resolution > sensor.get_optical_resolution()) { res *= 2; } // this should be evenly dividable basepixels = sensor.full_resolution / res; /* gl841 supports 1/1 1/2 1/3 1/4 1/5 1/6 1/8 1/10 1/12 1/15 averaging */ if (basepixels < 1) avgpixels = 1; else if (basepixels < 6) avgpixels = basepixels; else if (basepixels < 8) avgpixels = 6; else if (basepixels < 10) avgpixels = 8; else if (basepixels < 12) avgpixels = 10; else if (basepixels < 15) avgpixels = 12; else avgpixels = 15; /* LiDE80 packs shading data */ if (dev->model->sensor_id != SensorId::CIS_CANON_LIDE_80) { factor=1; fill=avgpixels; } else { factor=avgpixels; fill=1; } DBG(DBG_info, "%s: averaging over %d pixels\n", __func__, avgpixels); DBG(DBG_info, "%s: packing factor is %d\n", __func__, factor); DBG(DBG_info, "%s: fill length is %d\n", __func__, fill); for (x = 0; x <= pixels_per_line - avgpixels; x += avgpixels) { if ((x + o) * 2 * 2 + 3 > words_per_color * 2) break; for (j = 0; j < channels; j++) { dk = 0; br = 0; for (i = 0; i < avgpixels; i++) { // dark data dk += dev->dark_average_data[(x + i + pixels_per_line * j)]; // white data br += dev->white_average_data[(x + i + pixels_per_line * j)]; } br /= avgpixels; dk /= avgpixels; if (br * target_dark > dk * target_bright) val = 0; else if (dk * target_bright - br * target_dark > 65535 * (target_bright - target_dark)) val = 65535; else { val = (dk * target_bright - br * target_dark) / (target_bright - target_dark); } /*fill all pixels, even if only the last one is relevant*/ for (i = 0; i < fill; i++) { shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j] = val & 0xff; shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j + 1] = val >> 8; } val = br - dk; if (65535 * val > (target_bright - target_dark) * coeff) { val = (coeff * (target_bright - target_dark)) / val; } else { val = 65535; } /*fill all pixels, even if only the last one is relevant*/ for (i = 0; i < fill; i++) { shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j + 2] = val & 0xff; shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j + 3] = val >> 8; } } /* fill remaining channels */ for (j = channels; j < 3; j++) { for (i = 0; i < fill; i++) { shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j ] = shading_data[(x/factor + o + i) * 2 * 2 ]; shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j + 1] = shading_data[(x/factor + o + i) * 2 * 2 + 1]; shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j + 2] = shading_data[(x/factor + o + i) * 2 * 2 + 2]; shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j + 3] = shading_data[(x/factor + o + i) * 2 * 2 + 3]; } } } } static std::array color_order_to_cmat(ColorOrder color_order) { switch (color_order) { case ColorOrder::RGB: return {0, 1, 2}; case ColorOrder::GBR: return {2, 0, 1}; default: throw std::logic_error("Unknown color order"); } } /** * Computes shading coefficient using formula in data sheet. 16bit data values * manipulated here are little endian. For now we assume deletion scanning type * and that there is always 3 channels. * @param dev scanner's device * @param shading_data memory area where to store the computed shading coefficients * @param pixels_per_line number of pixels per line * @param channels number of color channels (actually 1 or 3) * @param cmat color transposition matrix * @param offset shading coefficients left offset * @param coeff 4000h or 2000h depending on fast scan mode or not * @param target value of the target code */ static void compute_coefficients(Genesys_Device * dev, std::uint8_t* shading_data, unsigned int pixels_per_line, unsigned int channels, ColorOrder color_order, int offset, unsigned int coeff, unsigned int target) { unsigned int x, c; unsigned int val, br, dk; unsigned int start, end; DBG(DBG_io, "%s: pixels_per_line=%d, coeff=0x%04x\n", __func__, pixels_per_line, coeff); auto cmat = color_order_to_cmat(color_order); /* compute start & end values depending of the offset */ if (offset < 0) { start = -1 * offset; end = pixels_per_line; } else { start = 0; end = pixels_per_line - offset; } for (c = 0; c < channels; c++) { for (x = start; x < end; x++) { /* TODO if channels=1 , use filter to know the base addr */ // contain 16bit words in little endian std::uint8_t* ptr = shading_data + 4 * ((x + offset) * channels + cmat[c]); // dark data dk = dev->dark_average_data[x * channels + c]; // white data br = dev->white_average_data[x * channels + c]; /* compute coeff */ val=compute_coefficient(coeff,target,br-dk); /* assign it */ ptr[0] = dk & 255; ptr[1] = dk / 256; ptr[2] = val & 0xff; ptr[3] = val / 256; } } } /** * Computes shading coefficient using formula in data sheet. 16bit data values * manipulated here are little endian. Data is in planar form, ie grouped by * lines of the same color component. * @param dev scanner's device * @param shading_data memory area where to store the computed shading coefficients * @param factor averaging factor when the calibration scan is done at a higher resolution * than the final scan * @param pixels_per_line number of pixels per line * @param words_per_color total number of shading data words for one color element * @param channels number of color channels (actually 1 or 3) * @param cmat transcoding matrix for color channel order * @param offset shading coefficients left offset * @param coeff 4000h or 2000h depending on fast scan mode or not * @param target white target value */ static void compute_planar_coefficients(Genesys_Device * dev, std::uint8_t* shading_data, unsigned int factor, unsigned int pixels_per_line, unsigned int words_per_color, unsigned int channels, ColorOrder color_order, unsigned int offset, unsigned int coeff, unsigned int target) { std::uint32_t i; std::uint32_t val, dk, br; auto cmat = color_order_to_cmat(color_order); DBG(DBG_io, "%s: factor=%d, pixels_per_line=%d, words=0x%X, coeff=0x%04x\n", __func__, factor, pixels_per_line, words_per_color, coeff); for (unsigned c = 0; c < channels; c++) { /* shading data is larger than pixels_per_line so offset can be neglected */ for (unsigned x = 0; x < pixels_per_line; x += factor) { /* x2 because of 16 bit values, and x2 since one coeff for dark * and another for white */ // contains 16bit words in little endian std::uint8_t* ptr = shading_data + words_per_color * cmat[c] * 2 + (x + offset) * 4; dk = 0; br = 0; /* average case */ for(i=0;idark_average_data[((x+i) + pixels_per_line * c)]; br += dev->white_average_data[((x+i) + pixels_per_line * c)]; } dk /= factor; br /= factor; val = compute_coefficient (coeff, target, br - dk); // we duplicate the information to have calibration data at optical resolution for (unsigned i = 0; i < factor; i++) { ptr[0 + 4 * i] = dk & 255; ptr[1 + 4 * i] = dk / 256; ptr[2 + 4 * i] = val & 0xff; ptr[3 + 4 * i] = val / 256; } } } /* in case of gray level scan, we duplicate shading information on all * three color channels */ if(channels==1) { memcpy(shading_data+cmat[1]*2*words_per_color, shading_data+cmat[0]*2*words_per_color, words_per_color*2); memcpy(shading_data+cmat[2]*2*words_per_color, shading_data+cmat[0]*2*words_per_color, words_per_color*2); } } static void compute_shifted_coefficients(Genesys_Device * dev, const Genesys_Sensor& sensor, std::uint8_t* shading_data, unsigned int pixels_per_line, unsigned int channels, ColorOrder color_order, int offset, unsigned int coeff, unsigned int target_dark, unsigned int target_bright, unsigned int patch_size) /* contiguous extent */ { unsigned int x, avgpixels, basepixels, i, j, val1, val2; unsigned int br_tmp [3], dk_tmp [3]; std::uint8_t* ptr = shading_data + offset * 3 * 4; // contain 16bit words in little endian unsigned int patch_cnt = offset * 3; /* at start, offset of first patch */ auto cmat = color_order_to_cmat(color_order); x = dev->settings.xres; if (sensor.full_resolution > sensor.get_optical_resolution()) { x *= 2; // scanner is using half-ccd mode } basepixels = sensor.full_resolution / x; // this should be evenly dividable /* gl841 supports 1/1 1/2 1/3 1/4 1/5 1/6 1/8 1/10 1/12 1/15 averaging */ if (basepixels < 1) avgpixels = 1; else if (basepixels < 6) avgpixels = basepixels; else if (basepixels < 8) avgpixels = 6; else if (basepixels < 10) avgpixels = 8; else if (basepixels < 12) avgpixels = 10; else if (basepixels < 15) avgpixels = 12; else avgpixels = 15; DBG(DBG_info, "%s: pixels_per_line=%d, coeff=0x%04x, averaging over %d pixels\n", __func__, pixels_per_line, coeff, avgpixels); for (x = 0; x <= pixels_per_line - avgpixels; x += avgpixels) { memset (&br_tmp, 0, sizeof(br_tmp)); memset (&dk_tmp, 0, sizeof(dk_tmp)); for (i = 0; i < avgpixels; i++) { for (j = 0; j < channels; j++) { br_tmp[j] += dev->white_average_data[((x + i) * channels + j)]; dk_tmp[i] += dev->dark_average_data[((x + i) * channels + j)]; } } for (j = 0; j < channels; j++) { br_tmp[j] /= avgpixels; dk_tmp[j] /= avgpixels; if (br_tmp[j] * target_dark > dk_tmp[j] * target_bright) val1 = 0; else if (dk_tmp[j] * target_bright - br_tmp[j] * target_dark > 65535 * (target_bright - target_dark)) val1 = 65535; else val1 = (dk_tmp[j] * target_bright - br_tmp[j] * target_dark) / (target_bright - target_dark); val2 = br_tmp[j] - dk_tmp[j]; if (65535 * val2 > (target_bright - target_dark) * coeff) val2 = (coeff * (target_bright - target_dark)) / val2; else val2 = 65535; br_tmp[j] = val1; dk_tmp[j] = val2; } for (i = 0; i < avgpixels; i++) { for (j = 0; j < channels; j++) { * ptr++ = br_tmp[ cmat[j] ] & 0xff; * ptr++ = br_tmp[ cmat[j] ] >> 8; * ptr++ = dk_tmp[ cmat[j] ] & 0xff; * ptr++ = dk_tmp[ cmat[j] ] >> 8; patch_cnt++; if (patch_cnt == patch_size) { patch_cnt = 0; val1 = cmat[2]; cmat[2] = cmat[1]; cmat[1] = cmat[0]; cmat[0] = val1; } } } } } static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_Sensor& sensor) { DBG_HELPER(dbg); if (sensor.use_host_side_calib) { return; } std::uint32_t pixels_per_line; int o; unsigned int length; /**> number of shading calibration data words */ unsigned int factor; unsigned int coeff, target_code, words_per_color = 0; // BUG: we are using wrong pixel number here unsigned start_offset = dev->calib_session.params.startx * sensor.full_resolution / dev->calib_session.params.xres; if (dev->model->asic_type == AsicType::GL842 || dev->model->asic_type == AsicType::GL843) { pixels_per_line = dev->calib_session.output_pixels + start_offset; } else { pixels_per_line = dev->calib_session.params.pixels + start_offset; } unsigned channels = dev->calib_session.params.channels; /* we always build data for three channels, even for gray * we make the shading data such that each color channel data line is contiguous * to the next one, which allow to write the 3 channels in 1 write * during genesys_send_shading_coefficient, some values are words, other bytes * hence the x2 factor */ switch (dev->reg.get8(0x05) >> 6) { /* 600 dpi */ case 0: words_per_color = 0x2a00; break; /* 1200 dpi */ case 1: words_per_color = 0x5500; break; /* 2400 dpi */ case 2: words_per_color = 0xa800; break; /* 4800 dpi */ case 3: words_per_color = 0x15000; break; } /* special case, memory is aligned on 0x5400, this has yet to be explained */ /* could be 0xa800 because sensor is truly 2400 dpi, then halved because * we only set 1200 dpi */ if(dev->model->sensor_id==SensorId::CIS_CANON_LIDE_80) { words_per_color = 0x5400; } length = words_per_color * 3 * 2; /* allocate computed size */ // contains 16bit words in little endian std::vector shading_data(length, 0); if (!dev->calib_session.computed) { genesys_send_offset_and_shading(dev, sensor, shading_data.data(), length); return; } /* TARGET/(Wn-Dn) = white gain -> ~1.xxx then it is multiplied by 0x2000 or 0x4000 to give an integer Wn = white average for column n Dn = dark average for column n */ if (get_registers_gain4_bit(dev->model->asic_type, dev->reg)) { coeff = 0x4000; } else { coeff = 0x2000; } /* compute avg factor */ if (dev->settings.xres > sensor.full_resolution) { factor = 1; } else { factor = sensor.full_resolution / dev->settings.xres; } /* for GL646, shading data is planar if REG_0x01_FASTMOD is set and * chunky if not. For now we rely on the fact that we know that * each sensor is used only in one mode. Currently only the CIS_XP200 * sets REG_0x01_FASTMOD. */ /* TODO setup a struct in genesys_devices that * will handle these settings instead of having this switch growing up */ switch (dev->model->sensor_id) { case SensorId::CCD_XP300: case SensorId::CCD_DOCKETPORT_487: case SensorId::CCD_ROADWARRIOR: case SensorId::CCD_DP665: case SensorId::CCD_DP685: case SensorId::CCD_DSMOBILE600: target_code = 0xdc00; o = 4; compute_planar_coefficients (dev, shading_data.data(), factor, pixels_per_line, words_per_color, channels, ColorOrder::RGB, o, coeff, target_code); break; case SensorId::CIS_XP200: target_code = 0xdc00; o = 2; compute_planar_coefficients (dev, shading_data.data(), 1, pixels_per_line, words_per_color, channels, ColorOrder::GBR, o, coeff, target_code); break; case SensorId::CCD_HP2300: target_code = 0xdc00; o = 2; if (dev->settings.xres <= sensor.full_resolution / 2) { o = o - sensor.dummy_pixel / 2; } compute_coefficients (dev, shading_data.data(), pixels_per_line, 3, ColorOrder::RGB, o, coeff, target_code); break; case SensorId::CCD_5345: target_code = 0xe000; o = 4; if(dev->settings.xres<=sensor.full_resolution/2) { o = o - sensor.dummy_pixel; } compute_coefficients (dev, shading_data.data(), pixels_per_line, 3, ColorOrder::RGB, o, coeff, target_code); break; case SensorId::CCD_HP3670: case SensorId::CCD_HP2400: target_code = 0xe000; // offset is dependent on ccd_pixels_per_system_pixel(), but we couldn't use this in // common code previously. // FIXME: use sensor.ccd_pixels_per_system_pixel() if(dev->settings.xres<=300) { o = -10; } else if(dev->settings.xres<=600) { o = -6; } else { o = +2; } compute_coefficients (dev, shading_data.data(), pixels_per_line, 3, ColorOrder::RGB, o, coeff, target_code); break; case SensorId::CCD_KVSS080: case SensorId::CCD_PLUSTEK_OPTICBOOK_3800: case SensorId::CCD_G4050: case SensorId::CCD_HP_4850C: case SensorId::CCD_CANON_4400F: case SensorId::CCD_CANON_8400F: case SensorId::CCD_CANON_8600F: case SensorId::CCD_PLUSTEK_OPTICFILM_7200: case SensorId::CCD_PLUSTEK_OPTICFILM_7200I: case SensorId::CCD_PLUSTEK_OPTICFILM_7300: case SensorId::CCD_PLUSTEK_OPTICFILM_7400: case SensorId::CCD_PLUSTEK_OPTICFILM_7500I: case SensorId::CCD_PLUSTEK_OPTICFILM_8200I: target_code = 0xe000; o = 0; compute_coefficients (dev, shading_data.data(), pixels_per_line, 3, ColorOrder::RGB, o, coeff, target_code); break; case SensorId::CIS_CANON_LIDE_700F: case SensorId::CIS_CANON_LIDE_100: case SensorId::CIS_CANON_LIDE_200: case SensorId::CIS_CANON_LIDE_110: case SensorId::CIS_CANON_LIDE_120: case SensorId::CIS_CANON_LIDE_210: case SensorId::CIS_CANON_LIDE_220: case SensorId::CCD_CANON_5600F: /* TODO store this in a data struct so we avoid * growing this switch */ switch(dev->model->sensor_id) { case SensorId::CIS_CANON_LIDE_110: case SensorId::CIS_CANON_LIDE_120: case SensorId::CIS_CANON_LIDE_210: case SensorId::CIS_CANON_LIDE_220: case SensorId::CIS_CANON_LIDE_700F: target_code = 0xc000; break; default: target_code = 0xdc00; } words_per_color=pixels_per_line*2; length = words_per_color * 3 * 2; shading_data.clear(); shading_data.resize(length, 0); compute_planar_coefficients (dev, shading_data.data(), 1, pixels_per_line, words_per_color, channels, ColorOrder::RGB, 0, coeff, target_code); break; case SensorId::CIS_CANON_LIDE_35: case SensorId::CIS_CANON_LIDE_60: case SensorId::CIS_CANON_LIDE_90: compute_averaged_planar (dev, sensor, shading_data.data(), pixels_per_line, words_per_color, channels, 4, coeff, 0xe000, 0x0a00); break; case SensorId::CIS_CANON_LIDE_80: compute_averaged_planar (dev, sensor, shading_data.data(), pixels_per_line, words_per_color, channels, 0, coeff, 0xe000, 0x0800); break; case SensorId::CCD_PLUSTEK_OPTICPRO_3600: compute_shifted_coefficients (dev, sensor, shading_data.data(), pixels_per_line, channels, ColorOrder::RGB, 12, /* offset */ coeff, 0x0001, /* target_dark */ 0xf900, /* target_bright */ 256); /* patch_size: contiguous extent */ break; default: throw SaneException(SANE_STATUS_UNSUPPORTED, "sensor %d not supported", static_cast(dev->model->sensor_id)); break; } // do the actual write of shading calibration data to the scanner genesys_send_offset_and_shading(dev, sensor, shading_data.data(), length); } /** * search calibration cache list for an entry matching required scan. * If one is found, set device calibration with it * @param dev scanner's device * @return false if no matching cache entry has been * found, true if one has been found and used. */ static bool genesys_restore_calibration(Genesys_Device * dev, Genesys_Sensor& sensor) { DBG_HELPER(dbg); // if no cache or no function to evaluate cache entry there can be no match/ if (dev->calibration_cache.empty()) { return false; } auto session = dev->cmd_set->calculate_scan_session(dev, sensor, dev->settings); /* we walk the link list of calibration cache in search for a * matching one */ for (auto& cache : dev->calibration_cache) { if (sanei_genesys_is_compatible_calibration(dev, session, &cache, false)) { dev->frontend = cache.frontend; /* we don't restore the gamma fields */ sensor.exposure = cache.sensor.exposure; dev->calib_session = cache.session; dev->average_size = cache.average_size; dev->dark_average_data = cache.dark_average_data; dev->white_average_data = cache.white_average_data; if (!dev->cmd_set->has_send_shading_data()) { genesys_send_shading_coefficient(dev, sensor); } DBG(DBG_proc, "%s: restored\n", __func__); return true; } } DBG(DBG_proc, "%s: completed(nothing found)\n", __func__); return false; } static void genesys_save_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor) { DBG_HELPER(dbg); #ifdef HAVE_SYS_TIME_H struct timeval time; #endif auto session = dev->cmd_set->calculate_scan_session(dev, sensor, dev->settings); auto found_cache_it = dev->calibration_cache.end(); for (auto cache_it = dev->calibration_cache.begin(); cache_it != dev->calibration_cache.end(); cache_it++) { if (sanei_genesys_is_compatible_calibration(dev, session, &*cache_it, true)) { found_cache_it = cache_it; break; } } /* if we found on overridable cache, we reuse it */ if (found_cache_it == dev->calibration_cache.end()) { /* create a new cache entry and insert it in the linked list */ dev->calibration_cache.push_back(Genesys_Calibration_Cache()); found_cache_it = std::prev(dev->calibration_cache.end()); } found_cache_it->average_size = dev->average_size; found_cache_it->dark_average_data = dev->dark_average_data; found_cache_it->white_average_data = dev->white_average_data; found_cache_it->params = session.params; found_cache_it->frontend = dev->frontend; found_cache_it->sensor = sensor; found_cache_it->session = dev->calib_session; #ifdef HAVE_SYS_TIME_H gettimeofday(&time, nullptr); found_cache_it->last_calibration = time.tv_sec; #endif } static void genesys_flatbed_calibration(Genesys_Device* dev, Genesys_Sensor& sensor) { DBG_HELPER(dbg); std::uint32_t pixels_per_line; unsigned coarse_res = sensor.full_resolution; if (dev->settings.yres <= sensor.full_resolution / 2) { coarse_res /= 2; } if (dev->model->model_id == ModelId::CANON_8400F) { coarse_res = 1600; } if (dev->model->model_id == ModelId::CANON_4400F || dev->model->model_id == ModelId::CANON_8600F) { coarse_res = 1200; } auto local_reg = dev->initial_regs; if (!has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION)) { // do ADC calibration first. dev->interface->record_progress_message("offset_calibration"); dev->cmd_set->offset_calibration(dev, sensor, local_reg); dev->interface->record_progress_message("coarse_gain_calibration"); dev->cmd_set->coarse_gain_calibration(dev, sensor, local_reg, coarse_res); } if (dev->model->is_cis && !has_flag(dev->model->flags, ModelFlag::DISABLE_EXPOSURE_CALIBRATION)) { // ADC now sends correct data, we can configure the exposure for the LEDs dev->interface->record_progress_message("led_calibration"); switch (dev->model->asic_type) { case AsicType::GL124: case AsicType::GL841: case AsicType::GL845: case AsicType::GL846: case AsicType::GL847: { auto calib_exposure = dev->cmd_set->led_calibration(dev, sensor, local_reg); for (auto& sensor_update : sanei_genesys_find_sensors_all_for_write(dev, sensor.method)) { sensor_update.get().exposure = calib_exposure; } sensor.exposure = calib_exposure; break; } default: { sensor.exposure = dev->cmd_set->led_calibration(dev, sensor, local_reg); } } if (!has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION)) { // recalibrate ADC again for the new LED exposure dev->interface->record_progress_message("offset_calibration"); dev->cmd_set->offset_calibration(dev, sensor, local_reg); dev->interface->record_progress_message("coarse_gain_calibration"); dev->cmd_set->coarse_gain_calibration(dev, sensor, local_reg, coarse_res); } } /* we always use sensor pixel number when the ASIC can't handle multi-segments sensor */ if (!has_flag(dev->model->flags, ModelFlag::SIS_SENSOR)) { pixels_per_line = static_cast((dev->model->x_size * dev->settings.xres) / MM_PER_INCH); } else { pixels_per_line = static_cast((dev->model->x_size_calib_mm * dev->settings.xres) / MM_PER_INCH); } // send default shading data dev->interface->record_progress_message("sanei_genesys_init_shading_data"); sanei_genesys_init_shading_data(dev, sensor, pixels_per_line); if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { scanner_move_to_ta(*dev); } // shading calibration if (!has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION)) { if (has_flag(dev->model->flags, ModelFlag::DARK_WHITE_CALIBRATION)) { dev->interface->record_progress_message("genesys_dark_white_shading_calibration"); genesys_dark_white_shading_calibration(dev, sensor, local_reg); } else { DBG(DBG_proc, "%s : genesys_dark_shading_calibration local_reg ", __func__); debug_dump(DBG_proc, local_reg); if (has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) { dev->interface->record_progress_message("genesys_dark_shading_calibration"); genesys_dark_shading_calibration(dev, sensor, local_reg); genesys_repark_sensor_before_shading(dev); } dev->interface->record_progress_message("genesys_white_shading_calibration"); genesys_white_shading_calibration(dev, sensor, local_reg); genesys_repark_sensor_after_white_shading(dev); if (!has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) { if (has_flag(dev->model->flags, ModelFlag::USE_CONSTANT_FOR_DARK_CALIBRATION)) { genesys_dark_shading_by_constant(*dev); } else { genesys_dark_shading_by_dummy_pixel(dev, sensor); } } } } if (!dev->cmd_set->has_send_shading_data()) { dev->interface->record_progress_message("genesys_send_shading_coefficient"); genesys_send_shading_coefficient(dev, sensor); } } /** * Does the calibration process for a sheetfed scanner * - offset calibration * - gain calibration * - shading calibration * During calibration a predefined calibration sheet with specific black and white * areas is used. * @param dev device to calibrate */ static void genesys_sheetfed_calibration(Genesys_Device* dev, Genesys_Sensor& sensor) { DBG_HELPER(dbg); bool forward = true; auto local_reg = dev->initial_regs; // first step, load document dev->cmd_set->load_document(dev); unsigned coarse_res = sensor.full_resolution; /* the afe needs to sends valid data even before calibration */ /* go to a white area */ try { scanner_search_strip(*dev, forward, false); } catch (...) { catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); }); throw; } if (!has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION)) { // do ADC calibration first. dev->interface->record_progress_message("offset_calibration"); dev->cmd_set->offset_calibration(dev, sensor, local_reg); dev->interface->record_progress_message("coarse_gain_calibration"); dev->cmd_set->coarse_gain_calibration(dev, sensor, local_reg, coarse_res); } if (dev->model->is_cis && !has_flag(dev->model->flags, ModelFlag::DISABLE_EXPOSURE_CALIBRATION)) { // ADC now sends correct data, we can configure the exposure for the LEDs dev->interface->record_progress_message("led_calibration"); dev->cmd_set->led_calibration(dev, sensor, local_reg); if (!has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION)) { // recalibrate ADC again for the new LED exposure dev->interface->record_progress_message("offset_calibration"); dev->cmd_set->offset_calibration(dev, sensor, local_reg); dev->interface->record_progress_message("coarse_gain_calibration"); dev->cmd_set->coarse_gain_calibration(dev, sensor, local_reg, coarse_res); } } /* search for a full width black strip and then do a 16 bit scan to * gather black shading data */ if (has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) { // seek black/white reverse/forward try { scanner_search_strip(*dev, forward, true); } catch (...) { catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); }); throw; } try { genesys_dark_shading_calibration(dev, sensor, local_reg); } catch (...) { catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); }); throw; } forward = false; } /* go to a white area */ try { scanner_search_strip(*dev, forward, false); } catch (...) { catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); }); throw; } genesys_repark_sensor_before_shading(dev); try { genesys_white_shading_calibration(dev, sensor, local_reg); genesys_repark_sensor_after_white_shading(dev); } catch (...) { catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); }); throw; } // in case we haven't black shading data, build it from black pixels of white calibration // FIXME: shouldn't we use genesys_dark_shading_by_dummy_pixel() ? if (!has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) { genesys_dark_shading_by_constant(*dev); } /* send the shading coefficient when doing whole line shading * but not when using SHDAREA like GL124 */ if (!dev->cmd_set->has_send_shading_data()) { genesys_send_shading_coefficient(dev, sensor); } // save the calibration data genesys_save_calibration(dev, sensor); // and finally eject calibration sheet dev->cmd_set->eject_document(dev); // restore settings dev->settings.xres = sensor.full_resolution; } /** * does the calibration process for a device * @param dev device to calibrate */ static void genesys_scanner_calibration(Genesys_Device* dev, Genesys_Sensor& sensor) { DBG_HELPER(dbg); if (!dev->model->is_sheetfed) { genesys_flatbed_calibration(dev, sensor); return; } genesys_sheetfed_calibration(dev, sensor); } /* ------------------------------------------------------------------------ */ /* High level (exported) functions */ /* ------------------------------------------------------------------------ */ /* * wait lamp to be warm enough by scanning the same line until * differences between two scans are below a threshold */ static void genesys_warmup_lamp(Genesys_Device* dev) { DBG_HELPER(dbg); unsigned seconds = 0; const auto& sensor = sanei_genesys_find_sensor_any(dev); dev->cmd_set->init_regs_for_warmup(dev, sensor, &dev->reg); dev->interface->write_registers(dev->reg); auto total_pixels = dev->session.output_pixels; auto total_size = dev->session.output_line_bytes; auto channels = dev->session.params.channels; auto lines = dev->session.output_line_count; std::vector first_line(total_size); std::vector second_line(total_size); do { first_line = second_line; dev->cmd_set->begin_scan(dev, sensor, &dev->reg, false); if (is_testing_mode()) { dev->interface->test_checkpoint("warmup_lamp"); dev->cmd_set->end_scan(dev, &dev->reg, true); return; } wait_until_buffer_non_empty(dev); sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size); dev->cmd_set->end_scan(dev, &dev->reg, true); // compute difference between the two scans double first_average = 0; double second_average = 0; for (unsigned pixel = 0; pixel < total_size; pixel++) { // 16 bit data if (dev->session.params.depth == 16) { first_average += (first_line[pixel] + first_line[pixel + 1] * 256); second_average += (second_line[pixel] + second_line[pixel + 1] * 256); pixel++; } else { first_average += first_line[pixel]; second_average += second_line[pixel]; } } first_average /= total_pixels; second_average /= total_pixels; if (dbg_log_image_data()) { write_tiff_file("gl_warmup1.tiff", first_line.data(), dev->session.params.depth, channels, total_size / (lines * channels), lines); write_tiff_file("gl_warmup2.tiff", second_line.data(), dev->session.params.depth, channels, total_size / (lines * channels), lines); } DBG(DBG_info, "%s: average 1 = %.2f, average 2 = %.2f\n", __func__, first_average, second_average); float average_difference = std::fabs(first_average - second_average) / second_average; if (second_average > 0 && average_difference < 0.005) { dbg.vlog(DBG_info, "difference: %f, exiting", average_difference); break; } dev->interface->sleep_ms(1000); seconds++; } while (seconds < WARMUP_TIME); if (seconds >= WARMUP_TIME) { throw SaneException(SANE_STATUS_IO_ERROR, "warmup timed out after %d seconds. Lamp defective?", seconds); } else { DBG(DBG_info, "%s: warmup succeeded after %d seconds\n", __func__, seconds); } } static void init_regs_for_scan(Genesys_Device& dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) { DBG_HELPER(dbg); debug_dump(DBG_info, dev.settings); auto session = dev.cmd_set->calculate_scan_session(&dev, sensor, dev.settings); if (dev.model->asic_type == AsicType::GL124 || dev.model->asic_type == AsicType::GL845 || dev.model->asic_type == AsicType::GL846 || dev.model->asic_type == AsicType::GL847) { /* Fast move to scan area: We don't move fast the whole distance since it would involve computing acceleration/deceleration distance for scan resolution. So leave a remainder for it so scan makes the final move tuning */ if (dev.settings.get_channels() * dev.settings.yres >= 600 && session.params.starty > 700) { scanner_move(dev, dev.model->default_method, static_cast(session.params.starty - 500), Direction::FORWARD); session.params.starty = 500; } compute_session(&dev, session, sensor); } dev.cmd_set->init_regs_for_scan_session(&dev, sensor, ®s, session); } // High-level start of scanning static void genesys_start_scan(Genesys_Device* dev, bool lamp_off) { DBG_HELPER(dbg); unsigned int steps, expected; /* since not all scanners are set to wait for head to park * we check we are not still parking before starting a new scan */ if (dev->parking) { sanei_genesys_wait_for_home(dev); } // disable power saving dev->cmd_set->save_power(dev, false); /* wait for lamp warmup : until a warmup for TRANSPARENCY is designed, skip * it when scanning from XPA. */ if (has_flag(dev->model->flags, ModelFlag::WARMUP) && (dev->settings.scan_method != ScanMethod::TRANSPARENCY_INFRARED)) { if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { scanner_move_to_ta(*dev); } genesys_warmup_lamp(dev); } /* set top left x and y values by scanning the internals if flatbed scanners */ if (!dev->model->is_sheetfed) { // TODO: check we can drop this since we cannot have the scanner's head wandering here dev->parking = false; dev->cmd_set->move_back_home(dev, true); } /* move to calibration area for transparency adapter */ if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { scanner_move_to_ta(*dev); } /* load document if needed (for sheetfed scanner for instance) */ if (dev->model->is_sheetfed) { dev->cmd_set->load_document(dev); } auto& sensor = sanei_genesys_find_sensor_for_write(dev, dev->settings.xres, dev->settings.get_channels(), dev->settings.scan_method); // send gamma tables. They have been set to device or user value // when setting option value */ dev->cmd_set->send_gamma_table(dev, sensor); /* try to use cached calibration first */ if (!genesys_restore_calibration (dev, sensor)) { // calibration : sheetfed scanners can't calibrate before each scan. // also don't run calibration for those scanners where all passes are disabled bool shading_disabled = has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION) && has_flag(dev->model->flags, ModelFlag::DISABLE_EXPOSURE_CALIBRATION) && has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION); if (!shading_disabled && !dev->model->is_sheetfed) { genesys_scanner_calibration(dev, sensor); genesys_save_calibration(dev, sensor); } else { DBG(DBG_warn, "%s: no calibration done\n", __func__); } } dev->cmd_set->wait_for_motor_stop(dev); if (dev->cmd_set->needs_home_before_init_regs_for_scan(dev)) { dev->cmd_set->move_back_home(dev, true); } if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { scanner_move_to_ta(*dev); } init_regs_for_scan(*dev, sensor, dev->reg); /* no lamp during scan */ if (lamp_off) { sanei_genesys_set_lamp_power(dev, sensor, dev->reg, false); } /* GL124 is using SHDAREA, so we have to wait for scan to be set up before * sending shading data */ if (dev->cmd_set->has_send_shading_data() && !has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION)) { genesys_send_shading_coefficient(dev, sensor); } // now send registers for scan dev->interface->write_registers(dev->reg); // start effective scan dev->cmd_set->begin_scan(dev, sensor, &dev->reg, true); if (is_testing_mode()) { dev->interface->test_checkpoint("start_scan"); return; } /*do we really need this? the valid data check should be sufficient -- pierre*/ /* waits for head to reach scanning position */ expected = dev->reg.get8(0x3d) * 65536 + dev->reg.get8(0x3e) * 256 + dev->reg.get8(0x3f); do { // wait some time between each test to avoid overloading USB and CPU dev->interface->sleep_ms(100); sanei_genesys_read_feed_steps (dev, &steps); } while (steps < expected); wait_until_buffer_non_empty(dev); // we wait for at least one word of valid scan data // this is also done in sanei_genesys_read_data_from_scanner -- pierre if (!dev->model->is_sheetfed) { do { dev->interface->sleep_ms(100); sanei_genesys_read_valid_words(dev, &steps); } while (steps < 1); } } /* this function does the effective data read in a manner that suits the scanner. It does data reordering and resizing if need. It also manages EOF and I/O errors, and line distance correction. Returns true on success, false on end-of-file. */ static void genesys_read_ordered_data(Genesys_Device* dev, SANE_Byte* destination, size_t* len) { DBG_HELPER(dbg); size_t bytes = 0; if (!dev->read_active) { *len = 0; throw SaneException("read is not active"); } DBG(DBG_info, "%s: frontend requested %zu bytes\n", __func__, *len); DBG(DBG_info, "%s: bytes_to_read=%zu, total_bytes_read=%zu\n", __func__, dev->total_bytes_to_read, dev->total_bytes_read); /* is there data left to scan */ if (dev->total_bytes_read >= dev->total_bytes_to_read) { /* issue park command immediately in case scanner can handle it * so we save time */ if (!dev->model->is_sheetfed && !has_flag(dev->model->flags, ModelFlag::MUST_WAIT) && !dev->parking) { dev->cmd_set->move_back_home(dev, false); dev->parking = true; } throw SaneException(SANE_STATUS_EOF, "nothing more to scan: EOF"); } if (is_testing_mode()) { if (dev->total_bytes_read + *len > dev->total_bytes_to_read) { *len = dev->total_bytes_to_read - dev->total_bytes_read; } dev->total_bytes_read += *len; } else { if (dev->model->is_sheetfed) { dev->cmd_set->detect_document_end(dev); } if (dev->total_bytes_read + *len > dev->total_bytes_to_read) { *len = dev->total_bytes_to_read - dev->total_bytes_read; } dev->pipeline_buffer.get_data(*len, destination); dev->total_bytes_read += *len; } /* end scan if all needed data have been read */ if(dev->total_bytes_read >= dev->total_bytes_to_read) { dev->cmd_set->end_scan(dev, &dev->reg, true); if (dev->model->is_sheetfed) { dev->cmd_set->eject_document (dev); } } DBG(DBG_proc, "%s: completed, %zu bytes read\n", __func__, bytes); } /* ------------------------------------------------------------------------ */ /* Start of higher level functions */ /* ------------------------------------------------------------------------ */ 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; } static std::size_t max_string_size(const std::vector& strings) { std::size_t max_size = 0; for (const auto& s : strings) { if (!s) { continue; } max_size = std::max(max_size, std::strlen(s)); } return max_size; } static unsigned pick_resolution(const std::vector& resolutions, unsigned resolution, const char* direction) { DBG_HELPER(dbg); if (resolutions.empty()) throw SaneException("Empty resolution list"); unsigned best_res = resolutions.front(); unsigned min_diff = abs_diff(best_res, resolution); for (auto it = std::next(resolutions.begin()); it != resolutions.end(); ++it) { unsigned curr_diff = abs_diff(*it, resolution); if (curr_diff < min_diff) { min_diff = curr_diff; best_res = *it; } } if (best_res != resolution) { DBG(DBG_warn, "%s: using resolution %d that is nearest to %d for direction %s\n", __func__, best_res, resolution, direction); } return best_res; } static Genesys_Settings calculate_scan_settings(Genesys_Scanner* s) { DBG_HELPER(dbg); const auto* dev = s->dev; Genesys_Settings settings; settings.scan_method = s->scan_method; settings.scan_mode = option_string_to_scan_color_mode(s->mode); settings.depth = s->bit_depth; if (settings.depth > 8) { settings.depth = 16; } else if (settings.depth < 8) { settings.depth = 1; } const auto& resolutions = dev->model->get_resolution_settings(settings.scan_method); settings.xres = pick_resolution(resolutions.resolutions_x, s->resolution, "X"); settings.yres = pick_resolution(resolutions.resolutions_y, s->resolution, "Y"); settings.tl_x = fixed_to_float(s->pos_top_left_x); settings.tl_y = fixed_to_float(s->pos_top_left_y); float br_x = fixed_to_float(s->pos_bottom_right_x); float br_y = fixed_to_float(s->pos_bottom_right_y); settings.lines = static_cast(((br_y - settings.tl_y) * settings.yres) / MM_PER_INCH); unsigned pixels_per_line = static_cast(((br_x - settings.tl_x) * settings.xres) / MM_PER_INCH); const auto& sensor = sanei_genesys_find_sensor(dev, settings.xres, settings.get_channels(), settings.scan_method); pixels_per_line = session_adjust_output_pixels(pixels_per_line, *dev, sensor, settings.xres, settings.yres, true); unsigned xres_factor = s->resolution / settings.xres; settings.pixels = pixels_per_line; settings.requested_pixels = pixels_per_line * xres_factor; if (s->color_filter == "Red") { settings.color_filter = ColorFilter::RED; } else if (s->color_filter == "Green") { settings.color_filter = ColorFilter::GREEN; } else if (s->color_filter == "Blue") { settings.color_filter = ColorFilter::BLUE; } else { settings.color_filter = ColorFilter::NONE; } // brightness and contrast only for for 8 bit scans if (s->bit_depth == 8) { settings.contrast = (s->contrast * 127) / 100; settings.brightness = (s->brightness * 127) / 100; } else { settings.contrast = 0; settings.brightness = 0; } settings.expiration_time = s->expiration_time; return settings; } static SANE_Parameters calculate_scan_parameters(const Genesys_Device& dev, const Genesys_Settings& settings) { DBG_HELPER(dbg); auto sensor = sanei_genesys_find_sensor(&dev, settings.xres, settings.get_channels(), settings.scan_method); auto session = dev.cmd_set->calculate_scan_session(&dev, sensor, settings); auto pipeline = build_image_pipeline(dev, session, 0, false); SANE_Parameters params; if (settings.scan_mode == ScanColorMode::GRAY) { params.format = SANE_FRAME_GRAY; } else { params.format = SANE_FRAME_RGB; } // only single-pass scanning supported params.last_frame = true; params.depth = settings.depth; params.lines = pipeline.get_output_height(); params.pixels_per_line = pipeline.get_output_width(); params.bytes_per_line = pipeline.get_output_row_bytes(); return params; } static void calc_parameters(Genesys_Scanner* s) { DBG_HELPER(dbg); s->dev->settings = calculate_scan_settings(s); s->params = calculate_scan_parameters(*s->dev, s->dev->settings); } static void create_bpp_list (Genesys_Scanner * s, const std::vector& bpp) { s->bpp_list[0] = bpp.size(); std::reverse_copy(bpp.begin(), bpp.end(), s->bpp_list + 1); } /** @brief this function initialize a gamma vector based on the ASIC: * Set up a default gamma table vector based on device description * gl646: 12 or 14 bits gamma table depending on ModelFlag::GAMMA_14BIT * gl84x: 16 bits * gl12x: 16 bits * @param scanner pointer to scanner session to get options * @param option option number of the gamma table to set */ static void init_gamma_vector_option (Genesys_Scanner * scanner, int option) { /* the option is inactive until the custom gamma control * is enabled */ scanner->opt[option].type = SANE_TYPE_INT; scanner->opt[option].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; scanner->opt[option].unit = SANE_UNIT_NONE; scanner->opt[option].constraint_type = SANE_CONSTRAINT_RANGE; if (scanner->dev->model->asic_type == AsicType::GL646) { if (has_flag(scanner->dev->model->flags, ModelFlag::GAMMA_14BIT)) { scanner->opt[option].size = 16384 * sizeof (SANE_Word); scanner->opt[option].constraint.range = &u14_range; } else { /* 12 bits gamma tables */ scanner->opt[option].size = 4096 * sizeof (SANE_Word); scanner->opt[option].constraint.range = &u12_range; } } else { /* other asics have 16 bits words gamma table */ scanner->opt[option].size = 256 * sizeof (SANE_Word); scanner->opt[option].constraint.range = &u16_range; } } /** * allocate a geometry range * @param size maximum size of the range * @return a pointer to a valid range or nullptr */ static SANE_Range create_range(float size) { SANE_Range range; range.min = float_to_fixed(0.0); range.max = float_to_fixed(size); range.quant = float_to_fixed(0.0); return range; } /** @brief generate calibration cache file nam * Generates the calibration cache file name to use. * Tries to store the cache in $HOME/.sane or * then fallbacks to $TMPDIR or TMP. The filename * uses the model name if only one scanner is plugged * else is uses the device name when several identical * scanners are in use. * @param currdev current scanner device * @return an allocated string containing a file name */ static std::string calibration_filename(Genesys_Device *currdev) { std::string ret; ret.resize(PATH_MAX); char filename[80]; unsigned int count; unsigned int i; /* first compute the DIR where we can store cache: * 1 - home dir * 2 - $TMPDIR * 3 - $TMP * 4 - tmp dir * 5 - temp dir * 6 - then resort to current dir */ char* ptr = std::getenv("HOME"); if (ptr == nullptr) { ptr = std::getenv("USERPROFILE"); } if (ptr == nullptr) { ptr = std::getenv("TMPDIR"); } if (ptr == nullptr) { ptr = std::getenv("TMP"); } /* now choose filename: * 1 - if only one scanner, name of the model * 2 - if several scanners of the same model, use device name, * replacing special chars */ count=0; /* count models of the same names if several scanners attached */ if(s_devices->size() > 1) { for (const auto& dev : *s_devices) { if (dev.vendorId == currdev->vendorId && dev.productId == currdev->productId) { count++; } } } if(count>1) { std::snprintf(filename, sizeof(filename), "%s.cal", currdev->file_name.c_str()); for(i=0;imodel->name); } /* build final final name : store dir + filename */ if (ptr == nullptr) { int size = std::snprintf(&ret.front(), ret.size(), "%s", filename); ret.resize(size); } else { int size = 0; #ifdef HAVE_MKDIR /* make sure .sane directory exists in existing store dir */ size = std::snprintf(&ret.front(), ret.size(), "%s%c.sane", ptr, PATH_SEP); ret.resize(size); mkdir(ret.c_str(), 0700); ret.resize(PATH_MAX); #endif size = std::snprintf(&ret.front(), ret.size(), "%s%c.sane%c%s", ptr, PATH_SEP, PATH_SEP, filename); ret.resize(size); } DBG(DBG_info, "%s: calibration filename >%s<\n", __func__, ret.c_str()); return ret; } static void set_resolution_option_values(Genesys_Scanner& s, bool reset_resolution_value) { auto resolutions = s.dev->model->get_resolutions(s.scan_method); s.opt_resolution_values.resize(resolutions.size() + 1, 0); s.opt_resolution_values[0] = resolutions.size(); std::copy(resolutions.begin(), resolutions.end(), s.opt_resolution_values.begin() + 1); s.opt[OPT_RESOLUTION].constraint.word_list = s.opt_resolution_values.data(); if (reset_resolution_value) { s.resolution = *std::min_element(resolutions.begin(), resolutions.end()); } } static void set_xy_range_option_values(Genesys_Scanner& s) { if (s.scan_method == ScanMethod::FLATBED) { s.opt_x_range = create_range(s.dev->model->x_size); s.opt_y_range = create_range(s.dev->model->y_size); } else { s.opt_x_range = create_range(s.dev->model->x_size_ta); s.opt_y_range = create_range(s.dev->model->y_size_ta); } s.opt[OPT_TL_X].constraint.range = &s.opt_x_range; s.opt[OPT_TL_Y].constraint.range = &s.opt_y_range; s.opt[OPT_BR_X].constraint.range = &s.opt_x_range; s.opt[OPT_BR_Y].constraint.range = &s.opt_y_range; s.pos_top_left_x = 0; s.pos_top_left_y = 0; s.pos_bottom_right_x = s.opt_x_range.max; s.pos_bottom_right_y = s.opt_y_range.max; } static void init_options(Genesys_Scanner* s) { DBG_HELPER(dbg); SANE_Int option; const Genesys_Model* model = s->dev->model; memset (s->opt, 0, sizeof (s->opt)); for (option = 0; option < NUM_OPTIONS; ++option) { s->opt[option].size = sizeof (SANE_Word); s->opt[option].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } 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].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; /* "Mode" group: */ s->opt[OPT_MODE_GROUP].name = "scanmode-group"; s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode"); s->opt[OPT_MODE_GROUP].desc = ""; s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MODE_GROUP].size = 0; s->opt[OPT_MODE_GROUP].cap = 0; s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* scan mode */ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; 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].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_MODE].size = max_string_size (mode_list); s->opt[OPT_MODE].constraint.string_list = mode_list; s->mode = SANE_VALUE_SCAN_MODE_GRAY; /* scan source */ s->opt_source_values.clear(); for (const auto& resolution_setting : model->resolutions) { for (auto method : resolution_setting.methods) { s->opt_source_values.push_back(scan_method_to_option_string(method)); } } s->opt_source_values.push_back(nullptr); s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE; s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE; s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE; s->opt[OPT_SOURCE].type = SANE_TYPE_STRING; s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_SOURCE].size = max_string_size(s->opt_source_values); s->opt[OPT_SOURCE].constraint.string_list = s->opt_source_values.data(); if (s->opt_source_values.size() < 2) { throw SaneException("No scan methods specified for scanner"); } s->scan_method = model->default_method; /* preview */ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; s->opt[OPT_PREVIEW].unit = SANE_UNIT_NONE; s->opt[OPT_PREVIEW].constraint_type = SANE_CONSTRAINT_NONE; s->preview = false; /* bit depth */ s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT; s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_BIT_DEPTH].size = sizeof (SANE_Word); s->opt[OPT_BIT_DEPTH].constraint.word_list = s->bpp_list; create_bpp_list (s, model->bpp_gray_values); s->bit_depth = model->bpp_gray_values[0]; // resolution s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; set_resolution_option_values(*s, true); /* "Geometry" group: */ s->opt[OPT_GEOMETRY_GROUP].name = SANE_NAME_GEOMETRY; s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry"); s->opt[OPT_GEOMETRY_GROUP].desc = ""; s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_GEOMETRY_GROUP].size = 0; s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; s->opt_x_range = create_range(model->x_size); s->opt_y_range = create_range(model->y_size); // scan area s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; s->opt[OPT_TL_X].unit = SANE_UNIT_MM; s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; s->opt[OPT_BR_X].unit = SANE_UNIT_MM; s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; set_xy_range_option_values(*s); /* "Enhancement" group: */ s->opt[OPT_ENHANCEMENT_GROUP].name = SANE_NAME_ENHANCEMENT; s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement"); s->opt[OPT_ENHANCEMENT_GROUP].desc = ""; s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_ENHANCEMENT_GROUP].size = 0; s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* custom-gamma table */ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL; s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_ADVANCED; s->custom_gamma = false; /* grayscale gamma vector */ s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR; s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR; s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR; init_gamma_vector_option (s, OPT_GAMMA_VECTOR); /* red gamma vector */ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R; init_gamma_vector_option (s, OPT_GAMMA_VECTOR_R); /* green gamma vector */ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G; init_gamma_vector_option (s, OPT_GAMMA_VECTOR_G); /* blue gamma vector */ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B; init_gamma_vector_option (s, OPT_GAMMA_VECTOR_B); /* currently, there are only gamma table options in this group, * so if the scanner doesn't support gamma table, disable the * whole group */ if (!has_flag(model->flags, ModelFlag::CUSTOM_GAMMA)) { s->opt[OPT_ENHANCEMENT_GROUP].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; DBG(DBG_info, "%s: custom gamma disabled\n", __func__); } /* software base image enhancements, these are consuming as many * memory than used by the full scanned image and may fail at high * resolution */ /* Software brightness */ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT; s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE; s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BRIGHTNESS].constraint.range = &(enhance_range); s->opt[OPT_BRIGHTNESS].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->brightness = 0; // disable by default /* Sowftware contrast */ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST; s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST; s->opt[OPT_CONTRAST].type = SANE_TYPE_INT; s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE; s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CONTRAST].constraint.range = &(enhance_range); s->opt[OPT_CONTRAST].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->contrast = 0; // disable by default /* "Extras" group: */ s->opt[OPT_EXTRAS_GROUP].name = "extras-group"; s->opt[OPT_EXTRAS_GROUP].title = SANE_I18N ("Extras"); s->opt[OPT_EXTRAS_GROUP].desc = ""; s->opt[OPT_EXTRAS_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_EXTRAS_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_EXTRAS_GROUP].size = 0; s->opt[OPT_EXTRAS_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* 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; /* true gray not yet supported for GL847 and GL124 scanners */ if (!model->is_cis || model->asic_type==AsicType::GL847 || model->asic_type==AsicType::GL124) { s->opt[OPT_COLOR_FILTER].size = max_string_size (color_filter_list); s->opt[OPT_COLOR_FILTER].constraint.string_list = color_filter_list; s->color_filter = s->opt[OPT_COLOR_FILTER].constraint.string_list[1]; } else { s->opt[OPT_COLOR_FILTER].size = max_string_size (cis_color_filter_list); s->opt[OPT_COLOR_FILTER].constraint.string_list = cis_color_filter_list; /* default to "None" ie true gray */ s->color_filter = s->opt[OPT_COLOR_FILTER].constraint.string_list[3]; } // no support for color filter for cis+gl646 scanners if (model->asic_type == AsicType::GL646 && model->is_cis) { DISABLE (OPT_COLOR_FILTER); } /* calibration store file name */ s->opt[OPT_CALIBRATION_FILE].name = "calibration-file"; s->opt[OPT_CALIBRATION_FILE].title = SANE_I18N ("Calibration file"); s->opt[OPT_CALIBRATION_FILE].desc = SANE_I18N ("Specify the calibration file to use"); s->opt[OPT_CALIBRATION_FILE].type = SANE_TYPE_STRING; s->opt[OPT_CALIBRATION_FILE].unit = SANE_UNIT_NONE; s->opt[OPT_CALIBRATION_FILE].size = PATH_MAX; s->opt[OPT_CALIBRATION_FILE].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; s->opt[OPT_CALIBRATION_FILE].constraint_type = SANE_CONSTRAINT_NONE; s->calibration_file.clear(); /* disable option if run as root */ #ifdef HAVE_GETUID if(geteuid()==0) { DISABLE (OPT_CALIBRATION_FILE); } #endif /* expiration time for calibration cache entries */ s->opt[OPT_EXPIRATION_TIME].name = "expiration-time"; s->opt[OPT_EXPIRATION_TIME].title = SANE_I18N ("Calibration cache expiration time"); s->opt[OPT_EXPIRATION_TIME].desc = SANE_I18N ("Time (in minutes) before a cached calibration expires. " "A value of 0 means cache is not used. A negative value means cache never expires."); s->opt[OPT_EXPIRATION_TIME].type = SANE_TYPE_INT; s->opt[OPT_EXPIRATION_TIME].unit = SANE_UNIT_NONE; s->opt[OPT_EXPIRATION_TIME].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_EXPIRATION_TIME].constraint.range = &expiration_range; s->expiration_time = 60; // 60 minutes by default /* 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"); s->opt[OPT_LAMP_OFF_TIME].desc = SANE_I18N ("The lamp will be turned off after the given time (in minutes). " "A value of 0 means, that the lamp won't be turned off."); s->opt[OPT_LAMP_OFF_TIME].type = SANE_TYPE_INT; s->opt[OPT_LAMP_OFF_TIME].unit = SANE_UNIT_NONE; s->opt[OPT_LAMP_OFF_TIME].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_LAMP_OFF_TIME].constraint.range = &time_range; s->lamp_off_time = 15; // 15 minutes /* turn lamp off during scan */ s->opt[OPT_LAMP_OFF].name = "lamp-off-scan"; s->opt[OPT_LAMP_OFF].title = SANE_I18N ("Lamp off during scan"); s->opt[OPT_LAMP_OFF].desc = SANE_I18N ("The lamp will be turned off during scan. "); s->opt[OPT_LAMP_OFF].type = SANE_TYPE_BOOL; s->opt[OPT_LAMP_OFF].unit = SANE_UNIT_NONE; s->opt[OPT_LAMP_OFF].constraint_type = SANE_CONSTRAINT_NONE; s->lamp_off = false; s->opt[OPT_SENSOR_GROUP].name = SANE_NAME_SENSORS; s->opt[OPT_SENSOR_GROUP].title = SANE_TITLE_SENSORS; s->opt[OPT_SENSOR_GROUP].desc = SANE_DESC_SENSORS; s->opt[OPT_SENSOR_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_SENSOR_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_SENSOR_GROUP].size = 0; s->opt[OPT_SENSOR_GROUP].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_SCAN_SW].name = SANE_NAME_SCAN; s->opt[OPT_SCAN_SW].title = SANE_TITLE_SCAN; s->opt[OPT_SCAN_SW].desc = SANE_DESC_SCAN; s->opt[OPT_SCAN_SW].type = SANE_TYPE_BOOL; s->opt[OPT_SCAN_SW].unit = SANE_UNIT_NONE; if (model->buttons & GENESYS_HAS_SCAN_SW) s->opt[OPT_SCAN_SW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else s->opt[OPT_SCAN_SW].cap = SANE_CAP_INACTIVE; /* SANE_NAME_FILE is not for buttons */ s->opt[OPT_FILE_SW].name = "file"; s->opt[OPT_FILE_SW].title = SANE_I18N ("File button"); s->opt[OPT_FILE_SW].desc = SANE_I18N ("File button"); s->opt[OPT_FILE_SW].type = SANE_TYPE_BOOL; s->opt[OPT_FILE_SW].unit = SANE_UNIT_NONE; if (model->buttons & GENESYS_HAS_FILE_SW) s->opt[OPT_FILE_SW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else s->opt[OPT_FILE_SW].cap = SANE_CAP_INACTIVE; s->opt[OPT_EMAIL_SW].name = SANE_NAME_EMAIL; s->opt[OPT_EMAIL_SW].title = SANE_TITLE_EMAIL; s->opt[OPT_EMAIL_SW].desc = SANE_DESC_EMAIL; s->opt[OPT_EMAIL_SW].type = SANE_TYPE_BOOL; s->opt[OPT_EMAIL_SW].unit = SANE_UNIT_NONE; if (model->buttons & GENESYS_HAS_EMAIL_SW) s->opt[OPT_EMAIL_SW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else s->opt[OPT_EMAIL_SW].cap = SANE_CAP_INACTIVE; s->opt[OPT_COPY_SW].name = SANE_NAME_COPY; s->opt[OPT_COPY_SW].title = SANE_TITLE_COPY; s->opt[OPT_COPY_SW].desc = SANE_DESC_COPY; s->opt[OPT_COPY_SW].type = SANE_TYPE_BOOL; s->opt[OPT_COPY_SW].unit = SANE_UNIT_NONE; if (model->buttons & GENESYS_HAS_COPY_SW) s->opt[OPT_COPY_SW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else s->opt[OPT_COPY_SW].cap = SANE_CAP_INACTIVE; s->opt[OPT_PAGE_LOADED_SW].name = SANE_NAME_PAGE_LOADED; s->opt[OPT_PAGE_LOADED_SW].title = SANE_TITLE_PAGE_LOADED; s->opt[OPT_PAGE_LOADED_SW].desc = SANE_DESC_PAGE_LOADED; s->opt[OPT_PAGE_LOADED_SW].type = SANE_TYPE_BOOL; s->opt[OPT_PAGE_LOADED_SW].unit = SANE_UNIT_NONE; if (model->buttons & GENESYS_HAS_PAGE_LOADED_SW) s->opt[OPT_PAGE_LOADED_SW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else s->opt[OPT_PAGE_LOADED_SW].cap = SANE_CAP_INACTIVE; /* OCR button */ s->opt[OPT_OCR_SW].name = "ocr"; s->opt[OPT_OCR_SW].title = SANE_I18N ("OCR button"); s->opt[OPT_OCR_SW].desc = SANE_I18N ("OCR button"); s->opt[OPT_OCR_SW].type = SANE_TYPE_BOOL; s->opt[OPT_OCR_SW].unit = SANE_UNIT_NONE; if (model->buttons & GENESYS_HAS_OCR_SW) s->opt[OPT_OCR_SW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else s->opt[OPT_OCR_SW].cap = SANE_CAP_INACTIVE; /* power button */ s->opt[OPT_POWER_SW].name = "power"; s->opt[OPT_POWER_SW].title = SANE_I18N ("Power button"); s->opt[OPT_POWER_SW].desc = SANE_I18N ("Power button"); s->opt[OPT_POWER_SW].type = SANE_TYPE_BOOL; s->opt[OPT_POWER_SW].unit = SANE_UNIT_NONE; if (model->buttons & GENESYS_HAS_POWER_SW) s->opt[OPT_POWER_SW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else s->opt[OPT_POWER_SW].cap = SANE_CAP_INACTIVE; /* extra button */ s->opt[OPT_EXTRA_SW].name = "extra"; s->opt[OPT_EXTRA_SW].title = SANE_I18N("Extra button"); s->opt[OPT_EXTRA_SW].desc = SANE_I18N("Extra button"); s->opt[OPT_EXTRA_SW].type = SANE_TYPE_BOOL; s->opt[OPT_EXTRA_SW].unit = SANE_UNIT_NONE; if (model->buttons & GENESYS_HAS_EXTRA_SW) s->opt[OPT_EXTRA_SW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else s->opt[OPT_EXTRA_SW].cap = SANE_CAP_INACTIVE; /* transparency/scan_film button */ s->opt[OPT_TRANSP_SW].name = "transparency"; s->opt[OPT_TRANSP_SW].title = SANE_I18N ("Transparency button"); s->opt[OPT_TRANSP_SW].desc = SANE_I18N ("Transparency button"); s->opt[OPT_TRANSP_SW].type = SANE_TYPE_BOOL; s->opt[OPT_TRANSP_SW].unit = SANE_UNIT_NONE; if (model->buttons & GENESYS_HAS_TRANSP_SW) s->opt[OPT_TRANSP_SW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else s->opt[OPT_TRANSP_SW].cap = SANE_CAP_INACTIVE; /* PDF special function button 1 */ s->opt[OPT_PDF1_SW].name = "pdf1"; s->opt[OPT_PDF1_SW].title = SANE_I18N ("PDF function button 1"); s->opt[OPT_PDF1_SW].desc = SANE_I18N ("PDF function button 1"); s->opt[OPT_PDF1_SW].type = SANE_TYPE_BOOL; s->opt[OPT_PDF1_SW].unit = SANE_UNIT_NONE; if (model->buttons & GENESYS_HAS_PDF1_SW) s->opt[OPT_PDF1_SW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else s->opt[OPT_PDF1_SW].cap = SANE_CAP_INACTIVE; /* PDF special function button 2 */ s->opt[OPT_PDF2_SW].name = "pdf2"; s->opt[OPT_PDF2_SW].title = SANE_I18N ("PDF function button 2"); s->opt[OPT_PDF2_SW].desc = SANE_I18N ("PDF function button 2"); s->opt[OPT_PDF2_SW].type = SANE_TYPE_BOOL; s->opt[OPT_PDF2_SW].unit = SANE_UNIT_NONE; if (model->buttons & GENESYS_HAS_PDF2_SW) s->opt[OPT_PDF2_SW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else s->opt[OPT_PDF2_SW].cap = SANE_CAP_INACTIVE; /* PDF special function button 3 */ s->opt[OPT_PDF3_SW].name = "pdf3"; s->opt[OPT_PDF3_SW].title = SANE_I18N ("PDF function button 3"); s->opt[OPT_PDF3_SW].desc = SANE_I18N ("PDF function button 3"); s->opt[OPT_PDF3_SW].type = SANE_TYPE_BOOL; s->opt[OPT_PDF3_SW].unit = SANE_UNIT_NONE; if (model->buttons & GENESYS_HAS_PDF3_SW) s->opt[OPT_PDF3_SW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else s->opt[OPT_PDF3_SW].cap = SANE_CAP_INACTIVE; /* PDF special function button 4 */ s->opt[OPT_PDF4_SW].name = "pdf4"; s->opt[OPT_PDF4_SW].title = SANE_I18N ("PDF function button 4"); s->opt[OPT_PDF4_SW].desc = SANE_I18N ("PDF function button 4"); s->opt[OPT_PDF4_SW].type = SANE_TYPE_BOOL; s->opt[OPT_PDF4_SW].unit = SANE_UNIT_NONE; if (model->buttons & GENESYS_HAS_PDF4_SW) s->opt[OPT_PDF4_SW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else s->opt[OPT_PDF4_SW].cap = SANE_CAP_INACTIVE; /* calibration needed */ s->opt[OPT_NEED_CALIBRATION_SW].name = "need-calibration"; s->opt[OPT_NEED_CALIBRATION_SW].title = SANE_I18N ("Needs calibration"); s->opt[OPT_NEED_CALIBRATION_SW].desc = SANE_I18N ("The scanner needs calibration for the current settings"); s->opt[OPT_NEED_CALIBRATION_SW].type = SANE_TYPE_BOOL; s->opt[OPT_NEED_CALIBRATION_SW].unit = SANE_UNIT_NONE; if (model->buttons & GENESYS_HAS_CALIBRATE) s->opt[OPT_NEED_CALIBRATION_SW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else s->opt[OPT_NEED_CALIBRATION_SW].cap = SANE_CAP_INACTIVE; /* button group */ s->opt[OPT_BUTTON_GROUP].name = "buttons"; s->opt[OPT_BUTTON_GROUP].title = SANE_I18N ("Buttons"); s->opt[OPT_BUTTON_GROUP].desc = ""; s->opt[OPT_BUTTON_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_BUTTON_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_BUTTON_GROUP].size = 0; s->opt[OPT_BUTTON_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* calibrate button */ s->opt[OPT_CALIBRATE].name = "calibrate"; s->opt[OPT_CALIBRATE].title = SANE_I18N ("Calibrate"); s->opt[OPT_CALIBRATE].desc = SANE_I18N ("Start calibration using special sheet"); s->opt[OPT_CALIBRATE].type = SANE_TYPE_BUTTON; s->opt[OPT_CALIBRATE].unit = SANE_UNIT_NONE; if (model->buttons & GENESYS_HAS_CALIBRATE) s->opt[OPT_CALIBRATE].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED | SANE_CAP_AUTOMATIC; else s->opt[OPT_CALIBRATE].cap = SANE_CAP_INACTIVE; /* clear calibration cache button */ s->opt[OPT_CLEAR_CALIBRATION].name = "clear-calibration"; s->opt[OPT_CLEAR_CALIBRATION].title = SANE_I18N ("Clear calibration"); s->opt[OPT_CLEAR_CALIBRATION].desc = SANE_I18N ("Clear calibration cache"); s->opt[OPT_CLEAR_CALIBRATION].type = SANE_TYPE_BUTTON; s->opt[OPT_CLEAR_CALIBRATION].unit = SANE_UNIT_NONE; s->opt[OPT_CLEAR_CALIBRATION].size = 0; s->opt[OPT_CLEAR_CALIBRATION].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_CLEAR_CALIBRATION].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; /* force calibration cache button */ s->opt[OPT_FORCE_CALIBRATION].name = "force-calibration"; s->opt[OPT_FORCE_CALIBRATION].title = SANE_I18N("Force calibration"); s->opt[OPT_FORCE_CALIBRATION].desc = SANE_I18N("Force calibration ignoring all and any calibration caches"); s->opt[OPT_FORCE_CALIBRATION].type = SANE_TYPE_BUTTON; s->opt[OPT_FORCE_CALIBRATION].unit = SANE_UNIT_NONE; s->opt[OPT_FORCE_CALIBRATION].size = 0; s->opt[OPT_FORCE_CALIBRATION].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_FORCE_CALIBRATION].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; // ignore offsets option s->opt[OPT_IGNORE_OFFSETS].name = "ignore-internal-offsets"; s->opt[OPT_IGNORE_OFFSETS].title = SANE_I18N("Ignore internal offsets"); s->opt[OPT_IGNORE_OFFSETS].desc = SANE_I18N("Acquires the image including the internal calibration areas of the scanner"); s->opt[OPT_IGNORE_OFFSETS].type = SANE_TYPE_BUTTON; s->opt[OPT_IGNORE_OFFSETS].unit = SANE_UNIT_NONE; s->opt[OPT_IGNORE_OFFSETS].size = 0; s->opt[OPT_IGNORE_OFFSETS].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_IGNORE_OFFSETS].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; calc_parameters(s); } static bool present; // this function is passed to C API, it must not throw static SANE_Status check_present (SANE_String_Const devname) noexcept { DBG_HELPER_ARGS(dbg, "%s detected.", devname); present = true; return SANE_STATUS_GOOD; } const UsbDeviceEntry& get_matching_usb_dev(std::uint16_t vendor_id, std::uint16_t product_id, std::uint16_t bcd_device) { for (auto& usb_dev : *s_usb_devices) { if (usb_dev.matches(vendor_id, product_id, bcd_device)) { return usb_dev; } } throw SaneException("vendor 0x%x product 0x%x (bcdDevice 0x%x) " "is not supported by this backend", vendor_id, product_id, bcd_device); } static Genesys_Device* attach_usb_device(const char* devname, std::uint16_t vendor_id, std::uint16_t product_id, std::uint16_t bcd_device) { const auto& usb_dev = get_matching_usb_dev(vendor_id, product_id, bcd_device); s_devices->emplace_back(); Genesys_Device* dev = &s_devices->back(); dev->file_name = devname; dev->vendorId = vendor_id; dev->productId = product_id; dev->model = &usb_dev.model(); dev->usb_mode = 0; // i.e. unset dev->already_initialized = false; return dev; } static bool s_attach_device_by_name_evaluate_bcd_device = false; static Genesys_Device* attach_device_by_name(SANE_String_Const devname, bool may_wait) { DBG_HELPER_ARGS(dbg, " devname: %s, may_wait = %d", devname, may_wait); if (!devname) { throw SaneException("devname must not be nullptr"); } for (auto& dev : *s_devices) { if (dev.file_name == devname) { DBG(DBG_info, "%s: device `%s' was already in device list\n", __func__, devname); return &dev; } } DBG(DBG_info, "%s: trying to open device `%s'\n", __func__, devname); UsbDevice usb_dev; usb_dev.open(devname); DBG(DBG_info, "%s: device `%s' successfully opened\n", __func__, devname); auto vendor_id = usb_dev.get_vendor_id(); auto product_id = usb_dev.get_product_id(); auto bcd_device = UsbDeviceEntry::BCD_DEVICE_NOT_SET; if (s_attach_device_by_name_evaluate_bcd_device) { // when the device is already known before scanning, we don't want to call get_bcd_device() // when iterating devices, as that will interfere with record/replay during testing. bcd_device = usb_dev.get_bcd_device(); } usb_dev.close(); /* KV-SS080 is an auxiliary device which requires a master device to be here */ if (vendor_id == 0x04da && product_id == 0x100f) { present = false; sanei_usb_find_devices(vendor_id, 0x1006, check_present); sanei_usb_find_devices(vendor_id, 0x1007, check_present); sanei_usb_find_devices(vendor_id, 0x1010, check_present); if (present == false) { throw SaneException("master device not present"); } } Genesys_Device* dev = attach_usb_device(devname, vendor_id, product_id, bcd_device); DBG(DBG_info, "%s: found %u flatbed scanner %u at %s\n", __func__, vendor_id, product_id, dev->file_name.c_str()); return dev; } // this function is passed to C API and must not throw static SANE_Status attach_one_device(SANE_String_Const devname) noexcept { DBG_HELPER(dbg); return wrap_exceptions_to_status_code(__func__, [=]() { attach_device_by_name(devname, false); }); } /* configuration framework functions */ // this function is passed to C API, it must not throw static SANE_Status config_attach_genesys(SANEI_Config __sane_unused__ *config, const char *devname, void __sane_unused__ *data) noexcept { /* the devname has been processed and is ready to be used * directly. Since the backend is an USB only one, we can * call sanei_usb_attach_matching_devices straight */ sanei_usb_attach_matching_devices (devname, attach_one_device); return SANE_STATUS_GOOD; } /* probes for scanner to attach to the backend */ static void probe_genesys_devices() { DBG_HELPER(dbg); if (is_testing_mode()) { attach_usb_device(get_testing_device_name().c_str(), get_testing_vendor_id(), get_testing_product_id(), get_testing_bcd_device()); return; } SANEI_Config config; // set configuration options structure : no option for this backend config.descriptors = nullptr; config.values = nullptr; config.count = 0; auto status = sanei_configure_attach(GENESYS_CONFIG_FILE, &config, config_attach_genesys, NULL); if (status == SANE_STATUS_ACCESS_DENIED) { dbg.vlog(DBG_error0, "Critical error: Couldn't access configuration file '%s'", GENESYS_CONFIG_FILE); } TIE(status); DBG(DBG_info, "%s: %zu devices currently attached\n", __func__, s_devices->size()); } /** * This should be changed if one of the substructures of Genesys_Calibration_Cache change, but it must be changed if there are changes that don't change size -- at least for now, as we store most of Genesys_Calibration_Cache as is. */ static const char* CALIBRATION_IDENT = "sane_genesys"; static const int CALIBRATION_VERSION = 32; bool read_calibration(std::istream& str, Genesys_Device::Calibration& calibration, const std::string& path) { DBG_HELPER(dbg); std::string ident; serialize(str, ident); if (ident != CALIBRATION_IDENT) { DBG(DBG_info, "%s: Incorrect calibration file '%s' header\n", __func__, path.c_str()); return false; } size_t version; serialize(str, version); if (version != CALIBRATION_VERSION) { DBG(DBG_info, "%s: Incorrect calibration file '%s' version\n", __func__, path.c_str()); return false; } calibration.clear(); serialize(str, calibration); return true; } /** * reads previously cached calibration data * from file defined in dev->calib_file */ static bool sanei_genesys_read_calibration(Genesys_Device::Calibration& calibration, const std::string& path) { DBG_HELPER(dbg); std::ifstream str; str.open(path); if (!str.is_open()) { DBG(DBG_info, "%s: Cannot open %s\n", __func__, path.c_str()); return false; } return read_calibration(str, calibration, path); } void write_calibration(std::ostream& str, Genesys_Device::Calibration& calibration) { std::string ident = CALIBRATION_IDENT; serialize(str, ident); size_t version = CALIBRATION_VERSION; serialize(str, version); serialize_newline(str); serialize(str, calibration); } static void write_calibration(Genesys_Device::Calibration& calibration, const std::string& path) { DBG_HELPER(dbg); std::ofstream str; str.open(path); if (!str.is_open()) { throw SaneException("Cannot open calibration for writing"); } write_calibration(str, calibration); } /* -------------------------- SANE API functions ------------------------- */ void sane_init_impl(SANE_Int * version_code, SANE_Auth_Callback authorize) { DBG_INIT (); DBG_HELPER_ARGS(dbg, "authorize %s null", authorize ? "!=" : "=="); DBG(DBG_init, "SANE Genesys backend from %s\n", PACKAGE_STRING); if (!is_testing_mode()) { #ifdef HAVE_LIBUSB DBG(DBG_init, "SANE Genesys backend built with libusb-1.0\n"); #endif #ifdef HAVE_LIBUSB_LEGACY DBG(DBG_init, "SANE Genesys backend built with libusb\n"); #endif } if (version_code) { *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); } if (!is_testing_mode()) { sanei_usb_init(); } s_scanners.init(); s_devices.init(); s_sane_devices.init(); s_sane_devices_data.init(); s_sane_devices_ptrs.init(); genesys_init_sensor_tables(); genesys_init_frontend_tables(); genesys_init_gpo_tables(); genesys_init_memory_layout_tables(); genesys_init_motor_tables(); genesys_init_usb_device_tables(); DBG(DBG_info, "%s: %s endian machine\n", __func__, #ifdef WORDS_BIGENDIAN "big" #else "little" #endif ); // cold-plug case :detection of already connected scanners s_attach_device_by_name_evaluate_bcd_device = false; probe_genesys_devices(); } SANE_GENESYS_API_LINKAGE SANE_Status sane_init(SANE_Int * version_code, SANE_Auth_Callback authorize) { return wrap_exceptions_to_status_code(__func__, [=]() { sane_init_impl(version_code, authorize); }); } void sane_exit_impl(void) { DBG_HELPER(dbg); if (!is_testing_mode()) { sanei_usb_exit(); } run_functions_at_backend_exit(); } SANE_GENESYS_API_LINKAGE void sane_exit() { catch_all_exceptions(__func__, [](){ sane_exit_impl(); }); } void sane_get_devices_impl(const SANE_Device *** device_list, SANE_Bool local_only) { DBG_HELPER_ARGS(dbg, "local_only = %s", local_only ? "true" : "false"); if (!is_testing_mode()) { // hot-plug case : detection of newly connected scanners */ sanei_usb_scan_devices(); } s_attach_device_by_name_evaluate_bcd_device = true; probe_genesys_devices(); s_sane_devices->clear(); s_sane_devices_data->clear(); s_sane_devices_ptrs->clear(); s_sane_devices->reserve(s_devices->size()); s_sane_devices_data->reserve(s_devices->size()); s_sane_devices_ptrs->reserve(s_devices->size() + 1); for (auto dev_it = s_devices->begin(); dev_it != s_devices->end();) { if (is_testing_mode()) { present = true; } else { present = false; sanei_usb_find_devices(dev_it->vendorId, dev_it->productId, check_present); } if (present) { s_sane_devices->emplace_back(); s_sane_devices_data->emplace_back(); auto& sane_device = s_sane_devices->back(); auto& sane_device_data = s_sane_devices_data->back(); sane_device_data.name = dev_it->file_name; sane_device.name = sane_device_data.name.c_str(); sane_device.vendor = dev_it->model->vendor; sane_device.model = dev_it->model->model; sane_device.type = "flatbed scanner"; s_sane_devices_ptrs->push_back(&sane_device); dev_it++; } else { dev_it = s_devices->erase(dev_it); } } s_sane_devices_ptrs->push_back(nullptr); *const_cast(device_list) = s_sane_devices_ptrs->data(); } SANE_GENESYS_API_LINKAGE SANE_Status sane_get_devices(const SANE_Device *** device_list, SANE_Bool local_only) { return wrap_exceptions_to_status_code(__func__, [=]() { sane_get_devices_impl(device_list, local_only); }); } static void sane_open_impl(SANE_String_Const devicename, SANE_Handle * handle) { DBG_HELPER_ARGS(dbg, "devicename = %s", devicename); Genesys_Device* dev = nullptr; /* devicename="" or devicename="genesys" are default values that use * first available device */ if (devicename[0] && strcmp ("genesys", devicename) != 0) { /* search for the given devicename in the device list */ for (auto& d : *s_devices) { if (d.file_name == devicename) { dev = &d; break; } } if (dev) { DBG(DBG_info, "%s: found `%s' in devlist\n", __func__, dev->file_name.c_str()); } else if (is_testing_mode()) { DBG(DBG_info, "%s: couldn't find `%s' in devlist, not attaching", __func__, devicename); } else { DBG(DBG_info, "%s: couldn't find `%s' in devlist, trying attach\n", __func__, devicename); dbg.status("attach_device_by_name"); dev = attach_device_by_name(devicename, true); dbg.clear(); } } else { // empty devicename or "genesys" -> use first device if (!s_devices->empty()) { dev = &s_devices->front(); DBG(DBG_info, "%s: empty devicename, trying `%s'\n", __func__, dev->file_name.c_str()); } } if (!dev) { throw SaneException("could not find the device to open: %s", devicename); } if (is_testing_mode()) { // during testing we need to initialize dev->model before test scanner interface is created // as that it needs to know what type of chip it needs to mimic. auto vendor_id = get_testing_vendor_id(); auto product_id = get_testing_product_id(); auto bcd_device = get_testing_bcd_device(); dev->model = &get_matching_usb_dev(vendor_id, product_id, bcd_device).model(); auto interface = std::unique_ptr{ new TestScannerInterface{dev, vendor_id, product_id, bcd_device}}; interface->set_checkpoint_callback(get_testing_checkpoint_callback()); dev->interface = std::move(interface); dev->interface->get_usb_device().open(dev->file_name.c_str()); } else { dev->interface = std::unique_ptr{new ScannerInterfaceUsb{dev}}; dbg.vstatus("open device '%s'", dev->file_name.c_str()); dev->interface->get_usb_device().open(dev->file_name.c_str()); dbg.clear(); auto bcd_device = dev->interface->get_usb_device().get_bcd_device(); dev->model = &get_matching_usb_dev(dev->vendorId, dev->productId, bcd_device).model(); } dbg.vlog(DBG_info, "Opened device %s", dev->model->name); if (has_flag(dev->model->flags, ModelFlag::UNTESTED)) { DBG(DBG_error0, "WARNING: Your scanner is not fully supported or at least \n"); DBG(DBG_error0, " had only limited testing. Please be careful and \n"); DBG(DBG_error0, " report any failure/success to \n"); DBG(DBG_error0, " sane-devel@alioth-lists.debian.net. Please provide as many\n"); DBG(DBG_error0, " details as possible, e.g. the exact name of your\n"); DBG(DBG_error0, " scanner and what does (not) work.\n"); } s_scanners->push_back(Genesys_Scanner()); auto* s = &s_scanners->back(); s->dev = dev; s->scanning = false; dev->parking = false; dev->read_active = false; dev->force_calibration = 0; dev->line_count = 0; *handle = s; if (!dev->already_initialized) { sanei_genesys_init_structs (dev); } dev->cmd_set = create_cmd_set(dev->model->asic_type); init_options(s); DBG_INIT(); // FIXME: we create sensor tables for the sensor, this should happen when we know which sensor // we will select dev->cmd_set->init(dev); // some hardware capabilities are detected through sensors dev->cmd_set->update_hardware_sensors (s); } SANE_GENESYS_API_LINKAGE SANE_Status sane_open(SANE_String_Const devicename, SANE_Handle* handle) { return wrap_exceptions_to_status_code(__func__, [=]() { sane_open_impl(devicename, handle); }); } void sane_close_impl(SANE_Handle handle) { DBG_HELPER(dbg); /* remove handle from list of open handles: */ auto it = s_scanners->end(); for (auto it2 = s_scanners->begin(); it2 != s_scanners->end(); it2++) { if (&*it2 == handle) { it = it2; break; } } if (it == s_scanners->end()) { DBG(DBG_error, "%s: invalid handle %p\n", __func__, handle); return; /* oops, not a handle we know about */ } auto* dev = it->dev; // eject document for sheetfed scanners if (dev->model->is_sheetfed) { catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); }); } else { // in case scanner is parking, wait for the head to reach home position if (dev->parking) { sanei_genesys_wait_for_home(dev); } } // enable power saving before leaving dev->cmd_set->save_power(dev, true); // here is the place to store calibration cache if (dev->force_calibration == 0 && !is_testing_mode()) { catch_all_exceptions(__func__, [&](){ write_calibration(dev->calibration_cache, dev->calib_file); }); } dev->already_initialized = false; dev->clear(); // LAMP OFF : same register across all the ASICs */ dev->interface->write_register(0x03, 0x00); catch_all_exceptions(__func__, [&](){ dev->interface->get_usb_device().clear_halt(); }); // we need this to avoid these ASIC getting stuck in bulk writes catch_all_exceptions(__func__, [&](){ dev->interface->get_usb_device().reset(); }); // not freeing dev because it's in the dev list catch_all_exceptions(__func__, [&](){ dev->interface->get_usb_device().close(); }); s_scanners->erase(it); } SANE_GENESYS_API_LINKAGE void sane_close(SANE_Handle handle) { catch_all_exceptions(__func__, [=]() { sane_close_impl(handle); }); } const SANE_Option_Descriptor * sane_get_option_descriptor_impl(SANE_Handle handle, SANE_Int option) { DBG_HELPER(dbg); Genesys_Scanner* s = reinterpret_cast(handle); if (static_cast(option) >= NUM_OPTIONS) { return nullptr; } DBG(DBG_io2, "%s: option = %s (%d)\n", __func__, s->opt[option].name, option); return s->opt + option; } SANE_GENESYS_API_LINKAGE const SANE_Option_Descriptor* sane_get_option_descriptor(SANE_Handle handle, SANE_Int option) { const SANE_Option_Descriptor* ret = nullptr; catch_all_exceptions(__func__, [&]() { ret = sane_get_option_descriptor_impl(handle, option); }); return ret; } static void print_option(DebugMessageHelper& dbg, const Genesys_Scanner& s, int option, void* val) { switch (s.opt[option].type) { case SANE_TYPE_INT: { dbg.vlog(DBG_proc, "value: %d", *reinterpret_cast(val)); return; } case SANE_TYPE_BOOL: { dbg.vlog(DBG_proc, "value: %s", *reinterpret_cast(val) ? "true" : "false"); return; } case SANE_TYPE_FIXED: { dbg.vlog(DBG_proc, "value: %f", fixed_to_float(*reinterpret_cast(val))); return; } case SANE_TYPE_STRING: { dbg.vlog(DBG_proc, "value: %s", reinterpret_cast(val)); return; } default: break; } dbg.log(DBG_proc, "value: (non-printable)"); } static void get_option_value(Genesys_Scanner* s, int option, void* val) { DBG_HELPER_ARGS(dbg, "option: %s (%d)", s->opt[option].name, option); auto* dev = s->dev; unsigned int i; SANE_Word* table = nullptr; std::vector gamma_table; unsigned option_size = 0; const Genesys_Sensor* sensor = nullptr; if (sanei_genesys_has_sensor(dev, dev->settings.xres, dev->settings.get_channels(), dev->settings.scan_method)) { sensor = &sanei_genesys_find_sensor(dev, dev->settings.xres, dev->settings.get_channels(), dev->settings.scan_method); } switch (option) { /* geometry */ case OPT_TL_X: *reinterpret_cast(val) = s->pos_top_left_x; break; case OPT_TL_Y: *reinterpret_cast(val) = s->pos_top_left_y; break; case OPT_BR_X: *reinterpret_cast(val) = s->pos_bottom_right_x; break; case OPT_BR_Y: *reinterpret_cast(val) = s->pos_bottom_right_y; break; /* word options: */ case OPT_NUM_OPTS: *reinterpret_cast(val) = NUM_OPTIONS; break; case OPT_RESOLUTION: *reinterpret_cast(val) = s->resolution; break; case OPT_BIT_DEPTH: *reinterpret_cast(val) = s->bit_depth; break; case OPT_PREVIEW: *reinterpret_cast(val) = s->preview; break; case OPT_LAMP_OFF: *reinterpret_cast(val) = s->lamp_off; break; case OPT_LAMP_OFF_TIME: *reinterpret_cast(val) = s->lamp_off_time; break; case OPT_CONTRAST: *reinterpret_cast(val) = s->contrast; break; case OPT_BRIGHTNESS: *reinterpret_cast(val) = s->brightness; break; case OPT_EXPIRATION_TIME: *reinterpret_cast(val) = s->expiration_time; break; case OPT_CUSTOM_GAMMA: *reinterpret_cast(val) = s->custom_gamma; break; /* string options: */ case OPT_MODE: std::strcpy(reinterpret_cast(val), s->mode.c_str()); break; case OPT_COLOR_FILTER: std::strcpy(reinterpret_cast(val), s->color_filter.c_str()); break; case OPT_CALIBRATION_FILE: std::strcpy(reinterpret_cast(val), s->calibration_file.c_str()); break; case OPT_SOURCE: std::strcpy(reinterpret_cast(val), scan_method_to_option_string(s->scan_method)); break; /* word array options */ case OPT_GAMMA_VECTOR: if (!sensor) throw SaneException("Unsupported scanner mode selected"); table = reinterpret_cast(val); if (s->color_filter == "Red") { gamma_table = get_gamma_table(dev, *sensor, GENESYS_RED); } else if (s->color_filter == "Blue") { gamma_table = get_gamma_table(dev, *sensor, GENESYS_BLUE); } else { gamma_table = get_gamma_table(dev, *sensor, GENESYS_GREEN); } option_size = s->opt[option].size / sizeof (SANE_Word); if (gamma_table.size() != option_size) { throw std::runtime_error("The size of the gamma tables does not match"); } for (i = 0; i < option_size; i++) { table[i] = gamma_table[i]; } break; case OPT_GAMMA_VECTOR_R: if (!sensor) throw SaneException("Unsupported scanner mode selected"); table = reinterpret_cast(val); gamma_table = get_gamma_table(dev, *sensor, GENESYS_RED); option_size = s->opt[option].size / sizeof (SANE_Word); if (gamma_table.size() != option_size) { throw std::runtime_error("The size of the gamma tables does not match"); } for (i = 0; i < option_size; i++) { table[i] = gamma_table[i]; } break; case OPT_GAMMA_VECTOR_G: if (!sensor) throw SaneException("Unsupported scanner mode selected"); table = reinterpret_cast(val); gamma_table = get_gamma_table(dev, *sensor, GENESYS_GREEN); option_size = s->opt[option].size / sizeof (SANE_Word); if (gamma_table.size() != option_size) { throw std::runtime_error("The size of the gamma tables does not match"); } for (i = 0; i < option_size; i++) { table[i] = gamma_table[i]; } break; case OPT_GAMMA_VECTOR_B: if (!sensor) throw SaneException("Unsupported scanner mode selected"); table = reinterpret_cast(val); gamma_table = get_gamma_table(dev, *sensor, GENESYS_BLUE); option_size = s->opt[option].size / sizeof (SANE_Word); if (gamma_table.size() != option_size) { throw std::runtime_error("The size of the gamma tables does not match"); } for (i = 0; i < option_size; i++) { table[i] = gamma_table[i]; } break; /* sensors */ case OPT_SCAN_SW: case OPT_FILE_SW: case OPT_EMAIL_SW: case OPT_COPY_SW: case OPT_PAGE_LOADED_SW: case OPT_OCR_SW: case OPT_POWER_SW: case OPT_EXTRA_SW: case OPT_TRANSP_SW: case OPT_PDF1_SW: case OPT_PDF2_SW: case OPT_PDF3_SW: case OPT_PDF4_SW: s->dev->cmd_set->update_hardware_sensors(s); *reinterpret_cast(val) = s->buttons[genesys_option_to_button(option)].read(); break; case OPT_NEED_CALIBRATION_SW: { if (!sensor) { throw SaneException("Unsupported scanner mode selected"); } // scanner needs calibration for current mode unless a matching calibration cache is // found bool result = true; auto session = dev->cmd_set->calculate_scan_session(dev, *sensor, dev->settings); for (auto& cache : dev->calibration_cache) { if (sanei_genesys_is_compatible_calibration(dev, session, &cache, false)) { *reinterpret_cast(val) = SANE_FALSE; } } *reinterpret_cast(val) = result; break; } default: DBG(DBG_warn, "%s: can't get unknown option %d\n", __func__, option); } print_option(dbg, *s, option, val); } /** @brief set calibration file value * Set calibration file value. Load new cache values from file if it exists, * else creates the file*/ static void set_calibration_value(Genesys_Scanner* s, const char* val) { DBG_HELPER(dbg); auto dev = s->dev; std::string new_calib_path = val; Genesys_Device::Calibration new_calibration; bool is_calib_success = false; catch_all_exceptions(__func__, [&]() { is_calib_success = sanei_genesys_read_calibration(new_calibration, new_calib_path); }); if (!is_calib_success) { return; } dev->calibration_cache = std::move(new_calibration); dev->calib_file = new_calib_path; s->calibration_file = new_calib_path; DBG(DBG_info, "%s: Calibration filename set to '%s':\n", __func__, new_calib_path.c_str()); } /* sets an option , called by sane_control_option */ static void set_option_value(Genesys_Scanner* s, int option, void *val, SANE_Int* myinfo) { DBG_HELPER_ARGS(dbg, "option: %s (%d)", s->opt[option].name, option); print_option(dbg, *s, option, val); auto* dev = s->dev; SANE_Word *table; unsigned int i; unsigned option_size = 0; switch (option) { case OPT_TL_X: s->pos_top_left_x = *reinterpret_cast(val); calc_parameters(s); *myinfo |= SANE_INFO_RELOAD_PARAMS; break; case OPT_TL_Y: s->pos_top_left_y = *reinterpret_cast(val); calc_parameters(s); *myinfo |= SANE_INFO_RELOAD_PARAMS; break; case OPT_BR_X: s->pos_bottom_right_x = *reinterpret_cast(val); calc_parameters(s); *myinfo |= SANE_INFO_RELOAD_PARAMS; break; case OPT_BR_Y: s->pos_bottom_right_y = *reinterpret_cast(val); calc_parameters(s); *myinfo |= SANE_INFO_RELOAD_PARAMS; break; case OPT_RESOLUTION: s->resolution = *reinterpret_cast(val); calc_parameters(s); *myinfo |= SANE_INFO_RELOAD_PARAMS; break; case OPT_LAMP_OFF: s->lamp_off = *reinterpret_cast(val); calc_parameters(s); *myinfo |= SANE_INFO_RELOAD_PARAMS; break; case OPT_PREVIEW: s->preview = *reinterpret_cast(val); calc_parameters(s); *myinfo |= SANE_INFO_RELOAD_PARAMS; break; case OPT_BRIGHTNESS: s->brightness = *reinterpret_cast(val); calc_parameters(s); *myinfo |= SANE_INFO_RELOAD_PARAMS; break; case OPT_CONTRAST: s->contrast = *reinterpret_cast(val); calc_parameters(s); *myinfo |= SANE_INFO_RELOAD_PARAMS; break; /* software enhancement functions only apply to 8 or 1 bits data */ case OPT_BIT_DEPTH: s->bit_depth = *reinterpret_cast(val); if(s->bit_depth>8) { DISABLE(OPT_CONTRAST); DISABLE(OPT_BRIGHTNESS); } else { ENABLE(OPT_CONTRAST); ENABLE(OPT_BRIGHTNESS); } calc_parameters(s); *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; break; case OPT_SOURCE: { auto scan_method = option_string_to_scan_method(reinterpret_cast(val)); if (s->scan_method != scan_method) { s->scan_method = scan_method; set_xy_range_option_values(*s); set_resolution_option_values(*s, false); *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; } break; } case OPT_MODE: { s->mode = reinterpret_cast(val); if (s->mode == SANE_VALUE_SCAN_MODE_GRAY) { if (dev->model->asic_type != AsicType::GL646 || !dev->model->is_cis) { ENABLE(OPT_COLOR_FILTER); } create_bpp_list(s, dev->model->bpp_gray_values); s->bit_depth = dev->model->bpp_gray_values[0]; } else { DISABLE(OPT_COLOR_FILTER); create_bpp_list(s, dev->model->bpp_color_values); s->bit_depth = dev->model->bpp_color_values[0]; } calc_parameters(s); /* if custom gamma, toggle gamma table options according to the mode */ if (s->custom_gamma) { if (s->mode == SANE_VALUE_SCAN_MODE_COLOR) { DISABLE (OPT_GAMMA_VECTOR); ENABLE (OPT_GAMMA_VECTOR_R); ENABLE (OPT_GAMMA_VECTOR_G); ENABLE (OPT_GAMMA_VECTOR_B); } else { ENABLE (OPT_GAMMA_VECTOR); DISABLE (OPT_GAMMA_VECTOR_R); DISABLE (OPT_GAMMA_VECTOR_G); DISABLE (OPT_GAMMA_VECTOR_B); } } *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; break; } case OPT_COLOR_FILTER: s->color_filter = reinterpret_cast(val); calc_parameters(s); break; case OPT_CALIBRATION_FILE: { if (dev->force_calibration == 0) { set_calibration_value(s, reinterpret_cast(val)); } break; } case OPT_LAMP_OFF_TIME: { if (*reinterpret_cast(val) != s->lamp_off_time) { s->lamp_off_time = *reinterpret_cast(val); dev->cmd_set->set_powersaving(dev, s->lamp_off_time); } break; } case OPT_EXPIRATION_TIME: { if (*reinterpret_cast(val) != s->expiration_time) { s->expiration_time = *reinterpret_cast(val); } break; } case OPT_CUSTOM_GAMMA: { *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; s->custom_gamma = *reinterpret_cast(val); if (s->custom_gamma) { if (s->mode == SANE_VALUE_SCAN_MODE_COLOR) { DISABLE (OPT_GAMMA_VECTOR); ENABLE (OPT_GAMMA_VECTOR_R); ENABLE (OPT_GAMMA_VECTOR_G); ENABLE (OPT_GAMMA_VECTOR_B); } else { ENABLE (OPT_GAMMA_VECTOR); DISABLE (OPT_GAMMA_VECTOR_R); DISABLE (OPT_GAMMA_VECTOR_G); DISABLE (OPT_GAMMA_VECTOR_B); } } else { DISABLE (OPT_GAMMA_VECTOR); DISABLE (OPT_GAMMA_VECTOR_R); DISABLE (OPT_GAMMA_VECTOR_G); DISABLE (OPT_GAMMA_VECTOR_B); for (auto& table : dev->gamma_override_tables) { table.clear(); } } break; } case OPT_GAMMA_VECTOR: { table = reinterpret_cast(val); option_size = s->opt[option].size / sizeof (SANE_Word); dev->gamma_override_tables[GENESYS_RED].resize(option_size); dev->gamma_override_tables[GENESYS_GREEN].resize(option_size); dev->gamma_override_tables[GENESYS_BLUE].resize(option_size); for (i = 0; i < option_size; i++) { dev->gamma_override_tables[GENESYS_RED][i] = table[i]; dev->gamma_override_tables[GENESYS_GREEN][i] = table[i]; dev->gamma_override_tables[GENESYS_BLUE][i] = table[i]; } break; } case OPT_GAMMA_VECTOR_R: { table = reinterpret_cast(val); option_size = s->opt[option].size / sizeof (SANE_Word); dev->gamma_override_tables[GENESYS_RED].resize(option_size); for (i = 0; i < option_size; i++) { dev->gamma_override_tables[GENESYS_RED][i] = table[i]; } break; } case OPT_GAMMA_VECTOR_G: { table = reinterpret_cast(val); option_size = s->opt[option].size / sizeof (SANE_Word); dev->gamma_override_tables[GENESYS_GREEN].resize(option_size); for (i = 0; i < option_size; i++) { dev->gamma_override_tables[GENESYS_GREEN][i] = table[i]; } break; } case OPT_GAMMA_VECTOR_B: { table = reinterpret_cast(val); option_size = s->opt[option].size / sizeof (SANE_Word); dev->gamma_override_tables[GENESYS_BLUE].resize(option_size); for (i = 0; i < option_size; i++) { dev->gamma_override_tables[GENESYS_BLUE][i] = table[i]; } break; } case OPT_CALIBRATE: { auto& sensor = sanei_genesys_find_sensor_for_write(dev, dev->settings.xres, dev->settings.get_channels(), dev->settings.scan_method); catch_all_exceptions(__func__, [&]() { dev->cmd_set->save_power(dev, false); genesys_scanner_calibration(dev, sensor); }); catch_all_exceptions(__func__, [&]() { dev->cmd_set->save_power(dev, true); }); *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; break; } case OPT_CLEAR_CALIBRATION: { dev->calibration_cache.clear(); // remove file unlink(dev->calib_file.c_str()); // signals that sensors will have to be read again *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; break; } case OPT_FORCE_CALIBRATION: { dev->force_calibration = 1; dev->calibration_cache.clear(); dev->calib_file.clear(); // signals that sensors will have to be read again *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; break; } case OPT_IGNORE_OFFSETS: { dev->ignore_offsets = true; break; } default: { DBG(DBG_warn, "%s: can't set unknown option %d\n", __func__, option); } } } /* sets and gets scanner option values */ void sane_control_option_impl(SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { Genesys_Scanner* s = reinterpret_cast(handle); auto action_str = (action == SANE_ACTION_GET_VALUE) ? "get" : (action == SANE_ACTION_SET_VALUE) ? "set" : (action == SANE_ACTION_SET_AUTO) ? "set_auto" : "unknown"; DBG_HELPER_ARGS(dbg, "action = %s, option = %s (%d)", action_str, s->opt[option].name, option); SANE_Word cap; SANE_Int myinfo = 0; if (info) { *info = 0; } if (s->scanning) { throw SaneException(SANE_STATUS_DEVICE_BUSY, "don't call this function while scanning (option = %s (%d))", s->opt[option].name, option); } if (option >= NUM_OPTIONS || option < 0) { throw SaneException("option %d >= NUM_OPTIONS || option < 0", option); } cap = s->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) { throw SaneException("option %d is inactive", option); } switch (action) { case SANE_ACTION_GET_VALUE: get_option_value(s, option, val); break; case SANE_ACTION_SET_VALUE: if (!SANE_OPTION_IS_SETTABLE (cap)) { throw SaneException("option %d is not settable", option); } TIE(sanei_constrain_value(s->opt + option, val, &myinfo)); set_option_value(s, option, val, &myinfo); break; case SANE_ACTION_SET_AUTO: throw SaneException("SANE_ACTION_SET_AUTO unsupported since no option " "has SANE_CAP_AUTOMATIC"); default: throw SaneException("unknown action %d for option %d", action, option); } if (info) *info = myinfo; } SANE_GENESYS_API_LINKAGE SANE_Status sane_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { return wrap_exceptions_to_status_code(__func__, [=]() { sane_control_option_impl(handle, option, action, val, info); }); } void sane_get_parameters_impl(SANE_Handle handle, SANE_Parameters* params) { DBG_HELPER(dbg); Genesys_Scanner* s = reinterpret_cast(handle); auto* dev = s->dev; /* don't recompute parameters once data reading is active, ie during scan */ if (!dev->read_active) { calc_parameters(s); } if (params) { *params = s->params; /* in the case of a sheetfed scanner, when full height is specified * we override the computed line number with -1 to signal that we * don't know the real document height. * We don't do that doing buffering image for digital processing */ if (dev->model->is_sheetfed && s->pos_bottom_right_y == s->opt[OPT_BR_Y].constraint.range->max) { params->lines = -1; } } debug_dump(DBG_proc, *params); } SANE_GENESYS_API_LINKAGE SANE_Status sane_get_parameters(SANE_Handle handle, SANE_Parameters* params) { return wrap_exceptions_to_status_code(__func__, [=]() { sane_get_parameters_impl(handle, params); }); } void sane_start_impl(SANE_Handle handle) { DBG_HELPER(dbg); Genesys_Scanner* s = reinterpret_cast(handle); auto* dev = s->dev; if (s->pos_top_left_x >= s->pos_bottom_right_x) { throw SaneException("top left x >= bottom right x"); } if (s->pos_top_left_y >= s->pos_bottom_right_y) { throw SaneException("top left y >= bottom right y"); } // fetch stored calibration if (dev->force_calibration == 0) { auto path = calibration_filename(dev); s->calibration_file = path; dev->calib_file = path; DBG(DBG_info, "%s: Calibration filename set to:\n", __func__); DBG(DBG_info, "%s: >%s<\n", __func__, dev->calib_file.c_str()); catch_all_exceptions(__func__, [&]() { sanei_genesys_read_calibration(dev->calibration_cache, dev->calib_file); }); } // First make sure we have a current parameter set. Some of the // parameters will be overwritten below, but that's OK. calc_parameters(s); genesys_start_scan(dev, s->lamp_off); s->scanning = true; } SANE_GENESYS_API_LINKAGE SANE_Status sane_start(SANE_Handle handle) { return wrap_exceptions_to_status_code(__func__, [=]() { sane_start_impl(handle); }); } // returns SANE_STATUS_GOOD if there are more data, SANE_STATUS_EOF otherwise SANE_Status sane_read_impl(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int* len) { DBG_HELPER(dbg); Genesys_Scanner* s = reinterpret_cast(handle); size_t local_len; if (!s) { throw SaneException("handle is nullptr"); } auto* dev = s->dev; if (!dev) { throw SaneException("dev is nullptr"); } if (!buf) { throw SaneException("buf is nullptr"); } if (!len) { throw SaneException("len is nullptr"); } *len = 0; if (!s->scanning) { throw SaneException(SANE_STATUS_CANCELLED, "scan was cancelled, is over or has not been initiated yet"); } DBG(DBG_proc, "%s: start, %d maximum bytes required\n", __func__, max_len); DBG(DBG_io2, "%s: bytes_to_read=%zu, total_bytes_read=%zu\n", __func__, dev->total_bytes_to_read, dev->total_bytes_read); if(dev->total_bytes_read>=dev->total_bytes_to_read) { DBG(DBG_proc, "%s: nothing more to scan: EOF\n", __func__); /* issue park command immediately in case scanner can handle it * so we save time */ if (!dev->model->is_sheetfed && !has_flag(dev->model->flags, ModelFlag::MUST_WAIT) && !dev->parking) { dev->cmd_set->move_back_home(dev, false); dev->parking = true; } return SANE_STATUS_EOF; } local_len = max_len; genesys_read_ordered_data(dev, buf, &local_len); *len = local_len; if (local_len > static_cast(max_len)) { dbg.log(DBG_error, "error: returning incorrect length"); } DBG(DBG_proc, "%s: %d bytes returned\n", __func__, *len); return SANE_STATUS_GOOD; } SANE_GENESYS_API_LINKAGE SANE_Status sane_read(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int* len) { return wrap_exceptions_to_status_code_return(__func__, [=]() { return sane_read_impl(handle, buf, max_len, len); }); } void sane_cancel_impl(SANE_Handle handle) { DBG_HELPER(dbg); Genesys_Scanner* s = reinterpret_cast(handle); auto* dev = s->dev; s->scanning = false; dev->read_active = false; // no need to end scan if we are parking the head if (!dev->parking) { dev->cmd_set->end_scan(dev, &dev->reg, true); } // park head if flatbed scanner if (!dev->model->is_sheetfed) { if (!dev->parking) { dev->cmd_set->move_back_home(dev, has_flag(dev->model->flags, ModelFlag::MUST_WAIT)); dev->parking = !has_flag(dev->model->flags, ModelFlag::MUST_WAIT); } } else { // in case of sheetfed scanners, we have to eject the document if still present dev->cmd_set->eject_document(dev); } // enable power saving mode unless we are parking .... if (!dev->parking) { dev->cmd_set->save_power(dev, true); } } SANE_GENESYS_API_LINKAGE void sane_cancel(SANE_Handle handle) { catch_all_exceptions(__func__, [=]() { sane_cancel_impl(handle); }); } void sane_set_io_mode_impl(SANE_Handle handle, SANE_Bool non_blocking) { DBG_HELPER_ARGS(dbg, "handle = %p, non_blocking = %s", handle, non_blocking == SANE_TRUE ? "true" : "false"); Genesys_Scanner* s = reinterpret_cast(handle); if (!s->scanning) { throw SaneException("not scanning"); } if (non_blocking) { throw SaneException(SANE_STATUS_UNSUPPORTED); } } SANE_GENESYS_API_LINKAGE SANE_Status sane_set_io_mode(SANE_Handle handle, SANE_Bool non_blocking) { return wrap_exceptions_to_status_code(__func__, [=]() { sane_set_io_mode_impl(handle, non_blocking); }); } void sane_get_select_fd_impl(SANE_Handle handle, SANE_Int* fd) { DBG_HELPER_ARGS(dbg, "handle = %p, fd = %p", handle, reinterpret_cast(fd)); Genesys_Scanner* s = reinterpret_cast(handle); if (!s->scanning) { throw SaneException("not scanning"); } throw SaneException(SANE_STATUS_UNSUPPORTED); } SANE_GENESYS_API_LINKAGE SANE_Status sane_get_select_fd(SANE_Handle handle, SANE_Int* fd) { return wrap_exceptions_to_status_code(__func__, [=]() { sane_get_select_fd_impl(handle, fd); }); } GenesysButtonName genesys_option_to_button(int option) { switch (option) { case OPT_SCAN_SW: return BUTTON_SCAN_SW; case OPT_FILE_SW: return BUTTON_FILE_SW; case OPT_EMAIL_SW: return BUTTON_EMAIL_SW; case OPT_COPY_SW: return BUTTON_COPY_SW; case OPT_PAGE_LOADED_SW: return BUTTON_PAGE_LOADED_SW; case OPT_OCR_SW: return BUTTON_OCR_SW; case OPT_POWER_SW: return BUTTON_POWER_SW; case OPT_EXTRA_SW: return BUTTON_EXTRA_SW; case OPT_TRANSP_SW: return BUTTON_TRANSP_SW; case OPT_PDF1_SW: return BUTTON_PDF1_SW; case OPT_PDF2_SW: return BUTTON_PDF2_SW; case OPT_PDF3_SW: return BUTTON_PDF3_SW; case OPT_PDF4_SW: return BUTTON_PDF4_SW; default: throw std::runtime_error("Unknown option to convert to button index"); } } } // namespace genesys backends-1.3.0/backend/genesys/genesys.h000066400000000000000000000124331456256263500202000ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2003, 2004 Henning Meier-Geinitz Copyright (C) 2005-2013 Stephane Voltz Copyright (C) 2006 Laurent Charpentier Copyright (C) 2009 Pierre Willenbrock 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, see . */ #ifndef GENESYS_H #define GENESYS_H #ifndef BACKEND_NAME # define BACKEND_NAME genesys #endif #include "low.h" #include #ifndef PATH_MAX # define PATH_MAX 1024 #endif #if defined(_WIN32) || defined(HAVE_OS2_H) # define PATH_SEP '\\' #else # define PATH_SEP '/' #endif #define ENABLE(OPTION) s->opt[OPTION].cap &= ~SANE_CAP_INACTIVE #define DISABLE(OPTION) s->opt[OPTION].cap |= SANE_CAP_INACTIVE #define IS_ACTIVE(OPTION) (((s->opt[OPTION].cap) & SANE_CAP_INACTIVE) == 0) #define GENESYS_CONFIG_FILE "genesys.conf" #ifndef SANE_I18N #define SANE_I18N(text) text #endif #define STR_FLATBED SANE_I18N("Flatbed") #define STR_TRANSPARENCY_ADAPTER SANE_I18N("Transparency Adapter") #define STR_TRANSPARENCY_ADAPTER_INFRARED SANE_I18N("Transparency Adapter Infrared") namespace genesys { /** List of SANE options */ enum Genesys_Option { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_MODE, OPT_SOURCE, OPT_PREVIEW, OPT_BIT_DEPTH, OPT_RESOLUTION, OPT_GEOMETRY_GROUP, OPT_TL_X, /* top-left x */ OPT_TL_Y, /* top-left y */ OPT_BR_X, /* bottom-right x */ OPT_BR_Y, /* bottom-right y */ /* advanced image enhancement options */ OPT_ENHANCEMENT_GROUP, OPT_CUSTOM_GAMMA, /* toggle to enable custom gamma tables */ OPT_GAMMA_VECTOR, OPT_GAMMA_VECTOR_R, OPT_GAMMA_VECTOR_G, OPT_GAMMA_VECTOR_B, OPT_BRIGHTNESS, OPT_CONTRAST, OPT_EXTRAS_GROUP, OPT_LAMP_OFF_TIME, OPT_LAMP_OFF, OPT_COLOR_FILTER, OPT_CALIBRATION_FILE, OPT_EXPIRATION_TIME, OPT_SENSOR_GROUP, OPT_SCAN_SW, OPT_FILE_SW, OPT_EMAIL_SW, OPT_COPY_SW, OPT_PAGE_LOADED_SW, OPT_OCR_SW, OPT_POWER_SW, OPT_EXTRA_SW, OPT_TRANSP_SW, OPT_PDF1_SW, OPT_PDF2_SW, OPT_PDF3_SW, OPT_PDF4_SW, OPT_NEED_CALIBRATION_SW, OPT_BUTTON_GROUP, OPT_CALIBRATE, OPT_CLEAR_CALIBRATION, OPT_FORCE_CALIBRATION, OPT_IGNORE_OFFSETS, /* must come last: */ NUM_OPTIONS }; enum GenesysButtonName : unsigned { BUTTON_SCAN_SW = 0, BUTTON_FILE_SW, BUTTON_EMAIL_SW, BUTTON_COPY_SW, BUTTON_PAGE_LOADED_SW, BUTTON_OCR_SW, BUTTON_POWER_SW, BUTTON_EXTRA_SW, BUTTON_TRANSP_SW, BUTTON_PDF1_SW, BUTTON_PDF2_SW, BUTTON_PDF3_SW, BUTTON_PDF4_SW, NUM_BUTTONS }; GenesysButtonName genesys_option_to_button(int option); class GenesysButton { public: void write(bool value) { if (value == value_) { return; } values_to_read_.push(value); value_ = value; } bool read() { if (values_to_read_.empty()) { return value_; } bool ret = values_to_read_.front(); values_to_read_.pop(); return ret; } private: bool value_ = false; std::queue values_to_read_; }; /** Scanner object. Should have better be called Session than Scanner */ struct Genesys_Scanner { Genesys_Scanner() = default; ~Genesys_Scanner() = default; // Next scanner in list struct Genesys_Scanner *next; // Low-level device object Genesys_Device* dev = nullptr; // SANE data // We are currently scanning bool scanning; // Option descriptors SANE_Option_Descriptor opt[NUM_OPTIONS]; std::vector opt_resolution_values; SANE_Range opt_x_range = {}; SANE_Range opt_y_range = {}; std::vector opt_source_values; // Option values SANE_Word bit_depth = 0; SANE_Word resolution = 0; bool preview = false; // TODO: currently not used bool lamp_off = false; SANE_Word lamp_off_time = 0; SANE_Word contrast = 0; SANE_Word brightness = 0; SANE_Word expiration_time = 0; bool custom_gamma = false; SANE_Word pos_top_left_y = 0; SANE_Word pos_top_left_x = 0; SANE_Word pos_bottom_right_y = 0; SANE_Word pos_bottom_right_x = 0; std::string mode, color_filter; // the value of the source option ScanMethod scan_method = ScanMethod::FLATBED; std::string calibration_file; // Button states GenesysButton buttons[NUM_BUTTONS]; // SANE Parameters SANE_Parameters params = {}; SANE_Int bpp_list[5] = {}; }; void write_calibration(std::ostream& str, Genesys_Device::Calibration& cache); bool read_calibration(std::istream& str, Genesys_Device::Calibration& cache, const std::string& path); } // namespace genesys #endif /* not GENESYS_H */ backends-1.3.0/backend/genesys/gl124.cpp000066400000000000000000001272671456256263500177230ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2010-2016 Stéphane Voltz 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, see . */ #define DEBUG_DECLARE_ONLY #include "gl124.h" #include "gl124_registers.h" #include "test_settings.h" #include namespace genesys { namespace gl124 { struct Gpio_layout { std::uint8_t r31; std::uint8_t r32; std::uint8_t r33; std::uint8_t r34; std::uint8_t r35; std::uint8_t r36; std::uint8_t r38; }; /** @brief gpio layout * describes initial gpio settings for a given model * registers 0x31 to 0x38 */ static Gpio_layout gpios[] = { /* LiDE 110 */ { /* 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x38 */ 0x9f, 0x59, 0x01, 0x80, 0x5f, 0x01, 0x00 }, /* LiDE 210 */ { 0x9f, 0x59, 0x01, 0x80, 0x5f, 0x01, 0x00 }, /* LiDE 120 */ { 0x9f, 0x53, 0x01, 0x80, 0x5f, 0x01, 0x00 }, }; /** @brief set all registers to default values . * This function is called only once at the beginning and * fills register startup values for registers reused across scans. * Those that are rarely modified or not modified are written * individually. * @param dev device structure holding register set to initialize */ static void gl124_init_registers (Genesys_Device * dev) { DBG_HELPER(dbg); dev->reg.clear(); // default to LiDE 110 dev->reg.init_reg(0x01, 0xa2); // + REG_0x01_SHDAREA dev->reg.init_reg(0x02, 0x90); dev->reg.init_reg(0x03, 0x50); dev->reg.init_reg(0x04, 0x03); dev->reg.init_reg(0x05, 0x00); if(dev->model->sensor_id == SensorId::CIS_CANON_LIDE_120) { dev->reg.init_reg(0x06, 0x50); dev->reg.init_reg(0x07, 0x00); } else { dev->reg.init_reg(0x03, 0x50 & ~REG_0x03_AVEENB); dev->reg.init_reg(0x06, 0x50 | REG_0x06_GAIN4); } dev->reg.init_reg(0x09, 0x00); dev->reg.init_reg(0x0a, 0xc0); dev->reg.init_reg(0x0b, 0x2a); dev->reg.init_reg(0x0c, 0x12); // SENSOR_DEF dev->reg.init_reg(0x11, 0x00); dev->reg.init_reg(0x12, 0x00); dev->reg.init_reg(0x13, 0x0f); dev->reg.init_reg(0x14, 0x00); dev->reg.init_reg(0x15, 0x80); dev->reg.init_reg(0x16, 0x10); // SENSOR_DEF dev->reg.init_reg(0x17, 0x04); // SENSOR_DEF dev->reg.init_reg(0x18, 0x00); // SENSOR_DEF dev->reg.init_reg(0x19, 0x01); // SENSOR_DEF dev->reg.init_reg(0x1a, 0x30); // SENSOR_DEF dev->reg.init_reg(0x1b, 0x00); // SENSOR_DEF dev->reg.init_reg(0x1c, 0x00); // SENSOR_DEF dev->reg.init_reg(0x1d, 0x01); // SENSOR_DEF dev->reg.init_reg(0x1e, 0x10); dev->reg.init_reg(0x1f, 0x00); dev->reg.init_reg(0x20, 0x15); // SENSOR_DEF dev->reg.init_reg(0x21, 0x00); if(dev->model->sensor_id != SensorId::CIS_CANON_LIDE_120) { dev->reg.init_reg(0x22, 0x02); } else { dev->reg.init_reg(0x22, 0x14); } dev->reg.init_reg(0x23, 0x00); dev->reg.init_reg(0x24, 0x00); dev->reg.init_reg(0x25, 0x00); dev->reg.init_reg(0x26, 0x0d); dev->reg.init_reg(0x27, 0x48); dev->reg.init_reg(0x28, 0x00); dev->reg.init_reg(0x29, 0x56); dev->reg.init_reg(0x2a, 0x5e); dev->reg.init_reg(0x2b, 0x02); dev->reg.init_reg(0x2c, 0x02); dev->reg.init_reg(0x2d, 0x58); dev->reg.init_reg(0x3b, 0x00); dev->reg.init_reg(0x3c, 0x00); dev->reg.init_reg(0x3d, 0x00); dev->reg.init_reg(0x3e, 0x00); dev->reg.init_reg(0x3f, 0x02); dev->reg.init_reg(0x40, 0x00); dev->reg.init_reg(0x41, 0x00); dev->reg.init_reg(0x42, 0x00); dev->reg.init_reg(0x43, 0x00); dev->reg.init_reg(0x44, 0x00); dev->reg.init_reg(0x45, 0x00); dev->reg.init_reg(0x46, 0x00); dev->reg.init_reg(0x47, 0x00); dev->reg.init_reg(0x48, 0x00); dev->reg.init_reg(0x49, 0x00); dev->reg.init_reg(0x4f, 0x00); dev->reg.init_reg(0x52, 0x00); // SENSOR_DEF dev->reg.init_reg(0x53, 0x02); // SENSOR_DEF dev->reg.init_reg(0x54, 0x04); // SENSOR_DEF dev->reg.init_reg(0x55, 0x06); // SENSOR_DEF dev->reg.init_reg(0x56, 0x04); // SENSOR_DEF dev->reg.init_reg(0x57, 0x04); // SENSOR_DEF dev->reg.init_reg(0x58, 0x04); // SENSOR_DEF dev->reg.init_reg(0x59, 0x04); // SENSOR_DEF dev->reg.init_reg(0x5a, 0x1a); // SENSOR_DEF dev->reg.init_reg(0x5b, 0x00); // SENSOR_DEF dev->reg.init_reg(0x5c, 0xc0); // SENSOR_DEF dev->reg.init_reg(0x5f, 0x00); dev->reg.init_reg(0x60, 0x02); dev->reg.init_reg(0x61, 0x00); // SENSOR_DEF dev->reg.init_reg(0x62, 0x00); dev->reg.init_reg(0x63, 0x00); dev->reg.init_reg(0x64, 0x00); dev->reg.init_reg(0x65, 0x00); dev->reg.init_reg(0x66, 0x00); dev->reg.init_reg(0x67, 0x00); dev->reg.init_reg(0x68, 0x00); dev->reg.init_reg(0x69, 0x00); dev->reg.init_reg(0x6a, 0x00); dev->reg.init_reg(0x6b, 0x00); dev->reg.init_reg(0x6c, 0x00); dev->reg.init_reg(0x6e, 0x00); dev->reg.init_reg(0x6f, 0x00); if (dev->model->sensor_id != SensorId::CIS_CANON_LIDE_120) { dev->reg.init_reg(0x6d, 0xd0); dev->reg.init_reg(0x71, 0x08); } else { dev->reg.init_reg(0x6d, 0x00); dev->reg.init_reg(0x71, 0x1f); } dev->reg.init_reg(0x70, 0x00); // SENSOR_DEF dev->reg.init_reg(0x71, 0x08); // SENSOR_DEF dev->reg.init_reg(0x72, 0x08); // SENSOR_DEF dev->reg.init_reg(0x73, 0x0a); // SENSOR_DEF // CKxMAP dev->reg.init_reg(0x74, 0x00); // SENSOR_DEF dev->reg.init_reg(0x75, 0x00); // SENSOR_DEF dev->reg.init_reg(0x76, 0x3c); // SENSOR_DEF dev->reg.init_reg(0x77, 0x00); // SENSOR_DEF dev->reg.init_reg(0x78, 0x00); // SENSOR_DEF dev->reg.init_reg(0x79, 0x9f); // SENSOR_DEF dev->reg.init_reg(0x7a, 0x00); // SENSOR_DEF dev->reg.init_reg(0x7b, 0x00); // SENSOR_DEF dev->reg.init_reg(0x7c, 0x55); // SENSOR_DEF dev->reg.init_reg(0x7d, 0x00); dev->reg.init_reg(0x7e, 0x08); dev->reg.init_reg(0x7f, 0x58); if (dev->model->sensor_id != SensorId::CIS_CANON_LIDE_120) { dev->reg.init_reg(0x80, 0x00); dev->reg.init_reg(0x81, 0x14); } else { dev->reg.init_reg(0x80, 0x00); dev->reg.init_reg(0x81, 0x10); } // STRPIXEL dev->reg.init_reg(0x82, 0x00); dev->reg.init_reg(0x83, 0x00); dev->reg.init_reg(0x84, 0x00); // ENDPIXEL dev->reg.init_reg(0x85, 0x00); dev->reg.init_reg(0x86, 0x00); dev->reg.init_reg(0x87, 0x00); dev->reg.init_reg(0x88, 0x00); // SENSOR_DEF dev->reg.init_reg(0x89, 0x65); // SENSOR_DEF dev->reg.init_reg(0x8a, 0x00); dev->reg.init_reg(0x8b, 0x00); dev->reg.init_reg(0x8c, 0x00); dev->reg.init_reg(0x8d, 0x00); dev->reg.init_reg(0x8e, 0x00); dev->reg.init_reg(0x8f, 0x00); dev->reg.init_reg(0x90, 0x00); dev->reg.init_reg(0x91, 0x00); dev->reg.init_reg(0x92, 0x00); dev->reg.init_reg(0x93, 0x00); // SENSOR_DEF dev->reg.init_reg(0x94, 0x14); // SENSOR_DEF dev->reg.init_reg(0x95, 0x30); // SENSOR_DEF dev->reg.init_reg(0x96, 0x00); // SENSOR_DEF dev->reg.init_reg(0x97, 0x90); // SENSOR_DEF dev->reg.init_reg(0x98, 0x01); // SENSOR_DEF dev->reg.init_reg(0x99, 0x1f); dev->reg.init_reg(0x9a, 0x00); dev->reg.init_reg(0x9b, 0x80); dev->reg.init_reg(0x9c, 0x80); dev->reg.init_reg(0x9d, 0x3f); dev->reg.init_reg(0x9e, 0x00); dev->reg.init_reg(0x9f, 0x00); dev->reg.init_reg(0xa0, 0x20); dev->reg.init_reg(0xa1, 0x30); dev->reg.init_reg(0xa2, 0x00); dev->reg.init_reg(0xa3, 0x20); dev->reg.init_reg(0xa4, 0x01); dev->reg.init_reg(0xa5, 0x00); dev->reg.init_reg(0xa6, 0x00); dev->reg.init_reg(0xa7, 0x08); dev->reg.init_reg(0xa8, 0x00); dev->reg.init_reg(0xa9, 0x08); dev->reg.init_reg(0xaa, 0x01); dev->reg.init_reg(0xab, 0x00); dev->reg.init_reg(0xac, 0x00); dev->reg.init_reg(0xad, 0x40); dev->reg.init_reg(0xae, 0x01); dev->reg.init_reg(0xaf, 0x00); dev->reg.init_reg(0xb0, 0x00); dev->reg.init_reg(0xb1, 0x40); dev->reg.init_reg(0xb2, 0x00); dev->reg.init_reg(0xb3, 0x09); dev->reg.init_reg(0xb4, 0x5b); dev->reg.init_reg(0xb5, 0x00); dev->reg.init_reg(0xb6, 0x10); dev->reg.init_reg(0xb7, 0x3f); dev->reg.init_reg(0xb8, 0x00); dev->reg.init_reg(0xbb, 0x00); dev->reg.init_reg(0xbc, 0xff); dev->reg.init_reg(0xbd, 0x00); dev->reg.init_reg(0xbe, 0x07); dev->reg.init_reg(0xc3, 0x00); dev->reg.init_reg(0xc4, 0x00); /* gamma dev->reg.init_reg(0xc5, 0x00); dev->reg.init_reg(0xc6, 0x00); dev->reg.init_reg(0xc7, 0x00); dev->reg.init_reg(0xc8, 0x00); dev->reg.init_reg(0xc9, 0x00); dev->reg.init_reg(0xca, 0x00); dev->reg.init_reg(0xcb, 0x00); dev->reg.init_reg(0xcc, 0x00); dev->reg.init_reg(0xcd, 0x00); dev->reg.init_reg(0xce, 0x00); */ if (dev->model->sensor_id == SensorId::CIS_CANON_LIDE_120) { dev->reg.init_reg(0xc5, 0x20); dev->reg.init_reg(0xc6, 0xeb); dev->reg.init_reg(0xc7, 0x20); dev->reg.init_reg(0xc8, 0xeb); dev->reg.init_reg(0xc9, 0x20); dev->reg.init_reg(0xca, 0xeb); } // memory layout /* dev->reg.init_reg(0xd0, 0x0a); dev->reg.init_reg(0xd1, 0x1f); dev->reg.init_reg(0xd2, 0x34); */ dev->reg.init_reg(0xd3, 0x00); dev->reg.init_reg(0xd4, 0x00); dev->reg.init_reg(0xd5, 0x00); dev->reg.init_reg(0xd6, 0x00); dev->reg.init_reg(0xd7, 0x00); dev->reg.init_reg(0xd8, 0x00); dev->reg.init_reg(0xd9, 0x00); // memory layout /* dev->reg.init_reg(0xe0, 0x00); dev->reg.init_reg(0xe1, 0x48); dev->reg.init_reg(0xe2, 0x15); dev->reg.init_reg(0xe3, 0x90); dev->reg.init_reg(0xe4, 0x15); dev->reg.init_reg(0xe5, 0x91); dev->reg.init_reg(0xe6, 0x2a); dev->reg.init_reg(0xe7, 0xd9); dev->reg.init_reg(0xe8, 0x2a); dev->reg.init_reg(0xe9, 0xad); dev->reg.init_reg(0xea, 0x40); dev->reg.init_reg(0xeb, 0x22); dev->reg.init_reg(0xec, 0x40); dev->reg.init_reg(0xed, 0x23); dev->reg.init_reg(0xee, 0x55); dev->reg.init_reg(0xef, 0x6b); dev->reg.init_reg(0xf0, 0x55); dev->reg.init_reg(0xf1, 0x6c); dev->reg.init_reg(0xf2, 0x6a); dev->reg.init_reg(0xf3, 0xb4); dev->reg.init_reg(0xf4, 0x6a); dev->reg.init_reg(0xf5, 0xb5); dev->reg.init_reg(0xf6, 0x7f); dev->reg.init_reg(0xf7, 0xfd); */ dev->reg.init_reg(0xf8, 0x01); // other value is 0x05 dev->reg.init_reg(0xf9, 0x00); dev->reg.init_reg(0xfa, 0x00); dev->reg.init_reg(0xfb, 0x00); dev->reg.init_reg(0xfc, 0x00); dev->reg.init_reg(0xff, 0x00); // fine tune upon device description const auto& sensor = sanei_genesys_find_sensor_any(dev); const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, sensor.full_resolution, 3, ScanMethod::FLATBED); sanei_genesys_set_dpihw(dev->reg, dpihw_sensor.register_dpihw); } /** @brief * Set register values of 'special' ti type frontend * Registers value are taken from the frontend register data * set. * @param dev device owning the AFE * @param set flag AFE_INIT to specify the AFE must be reset before writing data * */ static void gl124_set_ti_fe(Genesys_Device* dev, std::uint8_t set) { DBG_HELPER(dbg); int i; if (set == AFE_INIT) { dev->frontend = dev->frontend_initial; } // start writing to DAC dev->interface->write_fe_register(0x00, 0x80); // write values to analog frontend for (std::uint16_t addr = 0x01; addr < 0x04; addr++) { dev->interface->write_fe_register(addr, dev->frontend.regs.get_value(addr)); } dev->interface->write_fe_register(0x04, 0x00); /* these are not really sign for this AFE */ for (i = 0; i < 3; i++) { dev->interface->write_fe_register(0x05 + i, dev->frontend.regs.get_value(0x24 + i)); } if (dev->model->adc_id == AdcId::CANON_LIDE_120) { dev->interface->write_fe_register(0x00, 0x01); } else { dev->interface->write_fe_register(0x00, 0x11); } } // Set values of analog frontend void CommandSetGl124::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set) const { DBG_HELPER_ARGS(dbg, "%s", set == AFE_INIT ? "init" : set == AFE_SET ? "set" : set == AFE_POWER_SAVE ? "powersave" : "huh?"); (void) sensor; std::uint8_t val; if (set == AFE_INIT) { dev->frontend = dev->frontend_initial; } val = dev->interface->read_register(REG_0x0A); /* route to correct analog FE */ switch ((val & REG_0x0A_SIFSEL) >> REG_0x0AS_SIFSEL) { case 3: gl124_set_ti_fe(dev, set); break; case 0: case 1: case 2: default: throw SaneException("unsupported analog FE 0x%02x", val); } } static void gl124_init_motor_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, const MotorProfile& motor_profile, unsigned int scan_exposure_time, unsigned scan_yres, unsigned int scan_lines, unsigned int scan_dummy, unsigned int feed_steps, ScanColorMode scan_mode, ScanFlag flags) { DBG_HELPER(dbg); unsigned int lincnt, fast_dpi; unsigned int feedl,dist; std::uint32_t z1, z2; unsigned yres; unsigned min_speed; unsigned int linesel; DBG(DBG_info, "%s : scan_exposure_time=%d, scan_yres=%d, step_type=%d, scan_lines=%d, " "scan_dummy=%d, feed_steps=%d, scan_mode=%d, flags=%x\n", __func__, scan_exposure_time, scan_yres, static_cast(motor_profile.step_type), scan_lines, scan_dummy, feed_steps, static_cast(scan_mode), static_cast(flags)); /* enforce motor minimal scan speed * @TODO extend motor struct for this value */ if (scan_mode == ScanColorMode::COLOR_SINGLE_PASS) { min_speed = 900; } else { switch(dev->model->motor_id) { case MotorId::CANON_LIDE_110: min_speed = 600; break; case MotorId::CANON_LIDE_120: min_speed = 900; break; default: min_speed = 900; break; } } /* compute min_speed and linesel */ if(scan_yres 0 */ if(linesel==0) { linesel=1; yres=scan_yres*2; } } else { yres=scan_yres; linesel=0; } lincnt=scan_lines*(linesel+1); reg->set24(REG_LINCNT, lincnt); /* compute register 02 value */ std::uint8_t r02 = REG_0x02_NOTHOME; if (has_flag(flags, ScanFlag::AUTO_GO_HOME)) { r02 |= REG_0x02_AGOHOME; } if (has_flag(flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE) || (yres >= sensor.full_resolution)) { r02 |= REG_0x02_ACDCDIS; } if (has_flag(flags, ScanFlag::REVERSE)) { r02 |= REG_0x02_MTRREV; } reg->set8(REG_0x02, r02); sanei_genesys_set_motor_power(*reg, true); reg->set16(REG_SCANFED, 4); /* scan and backtracking slope table */ auto scan_table = create_slope_table(dev->model->asic_type, dev->motor, yres, scan_exposure_time, 1, motor_profile); scanner_send_slope_table(dev, sensor, SCAN_TABLE, scan_table.table); scanner_send_slope_table(dev, sensor, BACKTRACK_TABLE, scan_table.table); reg->set16(REG_STEPNO, scan_table.table.size()); /* fast table */ fast_dpi=yres; /* if (scan_mode != ScanColorMode::COLOR_SINGLE_PASS) { fast_dpi*=3; } */ auto fast_table = create_slope_table(dev->model->asic_type, dev->motor, fast_dpi, scan_exposure_time, 1, motor_profile); scanner_send_slope_table(dev, sensor, STOP_TABLE, fast_table.table); scanner_send_slope_table(dev, sensor, FAST_TABLE, fast_table.table); reg->set16(REG_FASTNO, fast_table.table.size()); reg->set16(REG_FSHDEC, fast_table.table.size()); reg->set16(REG_FMOVNO, fast_table.table.size()); /* subtract acceleration distance from feedl */ feedl=feed_steps; feedl <<= static_cast(motor_profile.step_type); dist = scan_table.table.size(); if (has_flag(flags, ScanFlag::FEEDING)) { dist *= 2; } /* get sure we don't use insane value */ if (dist < feedl) { feedl -= dist; } else { feedl = 0; } reg->set24(REG_FEEDL, feedl); /* doesn't seem to matter that much */ sanei_genesys_calculate_zmod(false, scan_exposure_time, scan_table.table, scan_table.table.size(), feedl, scan_table.table.size(), &z1, &z2); reg->set24(REG_Z1MOD, z1); reg->set24(REG_Z2MOD, z2); /* LINESEL */ reg->set8_mask(REG_0x1D, linesel, REG_0x1D_LINESEL); reg->set8(REG_0xA0, (static_cast(motor_profile.step_type) << REG_0xA0S_STEPSEL) | (static_cast(motor_profile.step_type) << REG_0xA0S_FSTPSEL)); reg->set16(REG_FMOVDEC, fast_table.table.size()); } static void gl124_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, unsigned int exposure_time, const ScanSession& session) { DBG_HELPER_ARGS(dbg, "exposure_time=%d", exposure_time); scanner_setup_sensor(*dev, sensor, *reg); dev->cmd_set->set_fe(dev, sensor, AFE_SET); /* enable shading */ regs_set_optical_off(dev->model->asic_type, *reg); if (has_flag(session.params.flags, ScanFlag::DISABLE_SHADING) || has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION)) { reg->find_reg(REG_0x01).value &= ~REG_0x01_DVDSET; } else { reg->find_reg(REG_0x01).value |= REG_0x01_DVDSET; } if ((dev->model->sensor_id != SensorId::CIS_CANON_LIDE_120) && (session.params.xres>=600)) { reg->find_reg(REG_0x03).value &= ~REG_0x03_AVEENB; } else { // BUG: the following is likely incorrect reg->find_reg(REG_0x03).value |= ~REG_0x03_AVEENB; } sanei_genesys_set_lamp_power(dev, sensor, *reg, !has_flag(session.params.flags, ScanFlag::DISABLE_LAMP)); // BW threshold dev->interface->write_register(REG_0x114, 0x7f); dev->interface->write_register(REG_0x115, 0x7f); /* monochrome / color scan */ switch (session.params.depth) { case 8: reg->find_reg(REG_0x04).value &= ~(REG_0x04_LINEART | REG_0x04_BITSET); break; case 16: reg->find_reg(REG_0x04).value &= ~REG_0x04_LINEART; reg->find_reg(REG_0x04).value |= REG_0x04_BITSET; break; } reg->find_reg(REG_0x04).value &= ~REG_0x04_FILTER; if (session.params.channels == 1) { switch (session.params.color_filter) { case ColorFilter::RED: reg->find_reg(REG_0x04).value |= 0x10; break; case ColorFilter::BLUE: reg->find_reg(REG_0x04).value |= 0x30; break; case ColorFilter::GREEN: reg->find_reg(REG_0x04).value |= 0x20; break; default: break; // should not happen } } const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, session.output_resolution, session.params.channels, session.params.scan_method); sanei_genesys_set_dpihw(*reg, dpihw_sensor.register_dpihw); if (should_enable_gamma(session, sensor)) { reg->find_reg(REG_0x05).value |= REG_0x05_GMMENB; } else { reg->find_reg(REG_0x05).value &= ~REG_0x05_GMMENB; } reg->set16(REG_DPISET, sensor.register_dpiset); reg->find_reg(REG_0x06).value |= REG_0x06_GAIN4; /* CIS scanners can do true gray by setting LEDADD */ /* we set up LEDADD only when asked */ if (dev->model->is_cis) { reg->find_reg(REG_0x60).value &= ~REG_0x60_LEDADD; if (session.enable_ledadd) { reg->find_reg(REG_0x60).value |= REG_0x60_LEDADD; std::uint32_t expmax = reg->get24(REG_EXPR); expmax = std::max(expmax, reg->get24(REG_EXPG)); expmax = std::max(expmax, reg->get24(REG_EXPB)); dev->reg.set24(REG_EXPR, expmax); dev->reg.set24(REG_EXPG, expmax); dev->reg.set24(REG_EXPB, expmax); } /* RGB weighting, REG_TRUER,G and B are to be set */ reg->find_reg(0x01).value &= ~REG_0x01_TRUEGRAY; if (session.enable_ledadd) { reg->find_reg(0x01).value |= REG_0x01_TRUEGRAY; dev->interface->write_register(REG_TRUER, 0x80); dev->interface->write_register(REG_TRUEG, 0x80); dev->interface->write_register(REG_TRUEB, 0x80); } } std::uint32_t pixel_endx = session.pixel_endx; if (pixel_endx == reg->get24(REG_SEGCNT)) { pixel_endx = 0; } reg->set24(REG_STRPIXEL, session.pixel_startx); reg->set24(REG_ENDPIXEL, pixel_endx); dev->line_count = 0; setup_image_pipeline(*dev, session); // MAXWD is expressed in 2 words unit // BUG: we shouldn't multiply by channels here reg->set24(REG_MAXWD, session.output_line_bytes_raw * session.params.channels * session.optical_resolution / session.full_resolution); reg->set24(REG_LPERIOD, exposure_time); reg->set16(REG_DUMMY, sensor.dummy_pixel); } void CommandSetGl124::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, const ScanSession& session) const { DBG_HELPER(dbg); session.assert_computed(); int exposure_time; int dummy = 0; int slope_dpi = 0; /* cis color scan is effectively a gray scan with 3 gray lines per color line and a FILTER of 0 */ if (dev->model->is_cis) { slope_dpi = session.params.yres * session.params.channels; } else { slope_dpi = session.params.yres; } if (has_flag(session.params.flags, ScanFlag::FEEDING)) { exposure_time = 2304; } else { exposure_time = sensor.exposure_lperiod; } const auto& motor_profile = get_motor_profile(dev->motor.profiles, exposure_time, session); DBG(DBG_info, "%s : exposure_time=%d pixels\n", __func__, exposure_time); DBG(DBG_info, "%s : scan_step_type=%d\n", __func__, static_cast(motor_profile.step_type)); /* we enable true gray for cis scanners only, and just when doing * scan since color calibration is OK for this mode */ // now _LOGICAL_ optical values used are known, setup registers gl124_init_optical_regs_scan(dev, sensor, reg, exposure_time, session); gl124_init_motor_regs_scan(dev, sensor, reg, motor_profile, exposure_time, slope_dpi, session.optical_line_count, dummy, session.params.starty, session.params.scan_mode, session.params.flags); /*** prepares data reordering ***/ dev->read_active = true; dev->session = session; dev->total_bytes_read = 0; dev->total_bytes_to_read = (size_t)session.output_line_bytes_requested * (size_t)session.params.lines; DBG(DBG_info, "%s: total bytes to send to frontend = %zu\n", __func__, dev->total_bytes_to_read); } ScanSession CommandSetGl124::calculate_scan_session(const Genesys_Device* dev, const Genesys_Sensor& sensor, const Genesys_Settings& settings) const { DBG(DBG_info, "%s ", __func__); debug_dump(DBG_info, settings); unsigned move_dpi = dev->motor.base_ydpi / 4; float move = dev->model->y_offset; move += dev->settings.tl_y; move = static_cast((move * move_dpi) / MM_PER_INCH); float start = dev->model->x_offset; start += settings.tl_x; start /= sensor.full_resolution / sensor.get_optical_resolution(); start = static_cast((start * settings.xres) / MM_PER_INCH); ScanSession session; session.params.xres = settings.xres; session.params.yres = settings.yres; session.params.startx = static_cast(start); session.params.starty = static_cast(move); session.params.pixels = settings.pixels; session.params.requested_pixels = settings.requested_pixels; session.params.lines = settings.lines; session.params.depth = settings.depth; session.params.channels = settings.get_channels(); session.params.scan_method = settings.scan_method; session.params.scan_mode = settings.scan_mode; session.params.color_filter = settings.color_filter; session.params.contrast_adjustment = dev->settings.contrast; session.params.brightness_adjustment = dev->settings.brightness; session.params.flags = ScanFlag::NONE; compute_session(dev, session, sensor); return session; } /** * for fast power saving methods only, like disabling certain amplifiers * @param dev device to use * @param enable true to set inot powersaving * */ void CommandSetGl124::save_power(Genesys_Device* dev, bool enable) const { (void) dev; DBG_HELPER_ARGS(dbg, "enable = %d", enable); } void CommandSetGl124::set_powersaving(Genesys_Device* dev, int delay /* in minutes */) const { DBG_HELPER_ARGS(dbg, "delay = %d", delay); dev->reg.find_reg(REG_0x03).value &= ~0xf0; if(delay<15) { dev->reg.find_reg(REG_0x03).value |= delay; } else { dev->reg.find_reg(REG_0x03).value |= 0x0f; } } /** @brief setup GPIOs for scan * Setup GPIO values to drive motor (or light) needed for the * target resolution * @param *dev device to set up * @param resolution dpi of the target scan */ void gl124_setup_scan_gpio(Genesys_Device* dev, int resolution) { DBG_HELPER(dbg); std::uint8_t val = dev->interface->read_register(REG_0x32); /* LiDE 110, 210 and 220 cases */ if(dev->model->gpio_id != GpioId::CANON_LIDE_120) { if(resolution>=dev->motor.base_ydpi/2) { val &= 0xf7; } else if(resolution>=dev->motor.base_ydpi/4) { val &= 0xef; } else { val |= 0x10; } } /* 120 : <=300 => 0x53 */ else { /* base_ydpi is 4800 */ if(resolution<=300) { val &= 0xf7; } else if(resolution<=600) { val |= 0x08; } else if(resolution<=1200) { val &= 0xef; val |= 0x08; } else { val &= 0xf7; } } val |= 0x02; dev->interface->write_register(REG_0x32, val); } // Send the low-level scan command // todo: is this that useful ? void CommandSetGl124::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, bool start_motor) const { DBG_HELPER(dbg); (void) sensor; (void) reg; // set up GPIO for scan gl124_setup_scan_gpio(dev,dev->settings.yres); scanner_clear_scan_and_feed_counts(*dev); // enable scan and motor std::uint8_t val = dev->interface->read_register(REG_0x01); val |= REG_0x01_SCAN; dev->interface->write_register(REG_0x01, val); scanner_start_action(*dev, start_motor); dev->advance_head_pos_by_session(ScanHeadId::PRIMARY); } // Send the stop scan command void CommandSetGl124::end_scan(Genesys_Device* dev, Genesys_Register_Set* reg, bool check_stop) const { (void) reg; DBG_HELPER_ARGS(dbg, "check_stop = %d", check_stop); if (!dev->model->is_sheetfed) { scanner_stop_action(*dev); } } /** Park head * Moves the slider to the home (top) position slowly * @param dev device to park * @param wait_until_home true to make the function waiting for head * to be home before returning, if fals returne immediately */ void CommandSetGl124::move_back_home(Genesys_Device* dev, bool wait_until_home) const { scanner_move_back_home(*dev, wait_until_home); } // init registers for shading calibration shading calibration is done at dpihw void CommandSetGl124::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { DBG_HELPER(dbg); unsigned channels = 3; unsigned resolution = sensor.shading_resolution; unsigned calib_lines = static_cast(dev->model->y_size_calib_mm * resolution / MM_PER_INCH); const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, dev->settings.scan_method); /* distance to move to reach white target at high resolution */ unsigned move=0; if (dev->settings.yres >= 1200) { move = static_cast(dev->model->y_offset_calib_white); move = static_cast((move * (dev->motor.base_ydpi/4)) / MM_PER_INCH); } ScanSession session; session.params.xres = resolution; session.params.yres = resolution; session.params.startx = 0; session.params.starty = move; session.params.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; session.params.lines = calib_lines; session.params.depth = 16; session.params.channels = channels; session.params.scan_method = dev->settings.scan_method; session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; session.params.color_filter = ColorFilter::RED; session.params.contrast_adjustment = dev->settings.contrast; session.params.brightness_adjustment = dev->settings.brightness; session.params.flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::DISABLE_BUFFER_FULL_MOVE; compute_session(dev, session, calib_sensor); try { init_regs_for_scan_session(dev, calib_sensor, ®s, session); } catch (...) { catch_all_exceptions(__func__, [&](){ sanei_genesys_set_motor_power(regs, false); }); throw; } sanei_genesys_set_motor_power(regs, false); dev->calib_session = session; } void CommandSetGl124::wait_for_motor_stop(Genesys_Device* dev) const { DBG_HELPER(dbg); auto status = scanner_read_status(*dev); std::uint8_t val40 = dev->interface->read_register(REG_0x100); if (!status.is_motor_enabled && (val40 & REG_0x100_MOTMFLG) == 0) { return; } do { dev->interface->sleep_ms(10); status = scanner_read_status(*dev); val40 = dev->interface->read_register(REG_0x100); } while (status.is_motor_enabled ||(val40 & REG_0x100_MOTMFLG)); dev->interface->sleep_ms(50); } /** * Send shading calibration data. The buffer is considered to always hold values * for all the channels. */ void CommandSetGl124::send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t* data, int size) const { DBG_HELPER_ARGS(dbg, "writing %d bytes of shading data", size); std::uint32_t addr, length, segcnt, pixels, i; std::uint8_t *ptr, *src; /* logical size of a color as seen by generic code of the frontend */ length = size / 3; std::uint32_t strpixel = dev->session.pixel_startx; std::uint32_t endpixel = dev->session.pixel_endx; segcnt = dev->reg.get24(REG_SEGCNT); /* turn pixel value into bytes 2x16 bits words */ strpixel*=2*2; /* 2 words of 2 bytes */ endpixel*=2*2; segcnt*=2*2; pixels=endpixel-strpixel; dev->interface->record_key_value("shading_start_pixel", std::to_string(strpixel)); dev->interface->record_key_value("shading_pixels", std::to_string(pixels)); dev->interface->record_key_value("shading_length", std::to_string(length)); dev->interface->record_key_value("shading_factor", std::to_string(sensor.shading_factor)); dev->interface->record_key_value("shading_segcnt", std::to_string(segcnt)); dev->interface->record_key_value("shading_segment_count", std::to_string(dev->session.segment_count)); DBG( DBG_io2, "%s: using chunks of %d bytes (%d shading data pixels)\n",__func__,length, length/4); std::vector buffer(pixels * dev->session.segment_count, 0); /* write actual red data */ for(i=0;i<3;i++) { /* copy data to work buffer and process it */ /* coefficient destination */ ptr = buffer.data(); /* iterate on both sensor segment */ for (unsigned x = 0; x < pixels; x += 4 * sensor.shading_factor) { /* coefficient source */ src=data+x+strpixel+i*length; /* iterate over all the segments */ for (unsigned s = 0; s < dev->session.segment_count; s++) { unsigned segnum = dev->session.segment_count > 1 ? sensor.segment_order[s] : 0; ptr[0+pixels*s]=src[0+segcnt*segnum]; ptr[1+pixels*s]=src[1+segcnt*segnum]; ptr[2+pixels*s]=src[2+segcnt*segnum]; ptr[3+pixels*s]=src[3+segcnt*segnum]; } /* next shading coefficient */ ptr+=4; } std::uint8_t val = dev->interface->read_register(0xd0+i); addr = val * 8192 + 0x10000000; dev->interface->write_ahb(addr, pixels * dev->session.segment_count, buffer.data()); } } /** @brief move to calibration area * This functions moves scanning head to calibration area * by doing a 600 dpi scan * @param dev scanner device */ void move_to_calibration_area(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) { (void) sensor; DBG_HELPER(dbg); unsigned resolution = 600; unsigned channels = 3; const auto& move_sensor = sanei_genesys_find_sensor(dev, resolution, channels, dev->settings.scan_method); /* initial calibration reg values */ regs = dev->reg; ScanSession session; session.params.xres = resolution; session.params.yres = resolution; session.params.startx = 0; session.params.starty = 0; session.params.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; session.params.lines = 1; session.params.depth = 8; session.params.channels = channels; session.params.scan_method = dev->settings.scan_method; session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; session.params.color_filter = dev->settings.color_filter; session.params.contrast_adjustment = dev->settings.contrast; session.params.brightness_adjustment = dev->settings.brightness; session.params.flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::SINGLE_LINE | ScanFlag::IGNORE_STAGGER_OFFSET | ScanFlag::IGNORE_COLOR_OFFSET; compute_session(dev, session, move_sensor); dev->cmd_set->init_regs_for_scan_session(dev, move_sensor, ®s, session); // write registers and scan data dev->interface->write_registers(regs); DBG (DBG_info, "%s: starting line reading\n", __func__); dev->cmd_set->begin_scan(dev, move_sensor, ®s, true); if (is_testing_mode()) { dev->interface->test_checkpoint("move_to_calibration_area"); scanner_stop_action(*dev); return; } auto image = read_unshuffled_image_from_scanner(dev, session, session.output_line_bytes); // stop scanning scanner_stop_action(*dev); if (dbg_log_image_data()) { write_tiff_file("gl124_movetocalarea.tiff", image); } } /* this function does the led calibration by scanning one line of the calibration area below scanner's top on white strip. -needs working coarse/gain */ SensorExposure CommandSetGl124::led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { return scanner_led_calibration(*dev, sensor, regs); } void CommandSetGl124::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { scanner_offset_calibration(*dev, sensor, regs); } void CommandSetGl124::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs, int dpi) const { scanner_coarse_gain_calibration(*dev, sensor, regs, dpi); } // wait for lamp warmup by scanning the same line until difference // between 2 scans is below a threshold void CommandSetGl124::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg) const { DBG_HELPER(dbg); *reg = dev->reg; auto flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::SINGLE_LINE | ScanFlag::IGNORE_STAGGER_OFFSET | ScanFlag::IGNORE_COLOR_OFFSET; if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { flags |= ScanFlag::USE_XPA; } ScanSession session; session.params.xres = sensor.full_resolution; session.params.yres = dev->motor.base_ydpi; session.params.startx = dev->model->x_size_calib_mm * sensor.full_resolution / MM_PER_INCH / 4; session.params.starty = 0; session.params.pixels = dev->model->x_size_calib_mm * sensor.full_resolution / MM_PER_INCH / 2; session.params.lines = 1; session.params.depth = dev->model->bpp_color_values.front(); session.params.channels = 3; session.params.scan_method = dev->settings.scan_method; session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; session.params.color_filter = dev->settings.color_filter; session.params.contrast_adjustment = dev->settings.contrast; session.params.brightness_adjustment = dev->settings.brightness; session.params.flags = flags; compute_session(dev, session, sensor); init_regs_for_scan_session(dev, sensor, reg, session); sanei_genesys_set_motor_power(*reg, false); } /** @brief default GPIO values * set up GPIO/GPOE for idle state * @param dev device to set up */ static void gl124_init_gpio(Genesys_Device* dev) { DBG_HELPER(dbg); int idx; /* per model GPIO layout */ if (dev->model->model_id == ModelId::CANON_LIDE_110) { idx = 0; } else if (dev->model->model_id == ModelId::CANON_LIDE_120) { idx = 2; } else { /* canon LiDE 210 and 220 case */ idx = 1; } dev->interface->write_register(REG_0x31, gpios[idx].r31); dev->interface->write_register(REG_0x32, gpios[idx].r32); dev->interface->write_register(REG_0x33, gpios[idx].r33); dev->interface->write_register(REG_0x34, gpios[idx].r34); dev->interface->write_register(REG_0x35, gpios[idx].r35); dev->interface->write_register(REG_0x36, gpios[idx].r36); dev->interface->write_register(REG_0x38, gpios[idx].r38); } /** * set memory layout by filling values in dedicated registers */ static void gl124_init_memory_layout(Genesys_Device* dev) { DBG_HELPER(dbg); apply_reg_settings_to_device_write_only(*dev, dev->memory_layout.regs); } /** * initialize backend and ASIC : registers, motor tables, and gamma tables * then ensure scanner's head is at home */ void CommandSetGl124::init(Genesys_Device* dev) const { DBG_INIT (); DBG_HELPER(dbg); sanei_genesys_asic_init(dev); } /* * * initialize ASIC from power on condition */ void CommandSetGl124::asic_boot(Genesys_Device* dev, bool cold) const { DBG_HELPER(dbg); // reset ASIC in case of cold boot if (cold) { dev->interface->write_register(0x0e, 0x01); dev->interface->write_register(0x0e, 0x00); } // enable GPOE 17 dev->interface->write_register(0x36, 0x01); // set GPIO 17 std::uint8_t val = dev->interface->read_register(0x33); val |= 0x01; dev->interface->write_register(0x33, val); // test CHKVER val = dev->interface->read_register(REG_0x100); if (val & REG_0x100_CHKVER) { val = dev->interface->read_register(0x00); DBG(DBG_info, "%s: reported version for genesys chip is 0x%02x\n", __func__, val); } /* Set default values for registers */ gl124_init_registers (dev); // Write initial registers dev->interface->write_registers(dev->reg); // tune reg 0B dev->interface->write_register(REG_0x0B, REG_0x0B_30MHZ | REG_0x0B_ENBDRAM | REG_0x0B_64M); dev->reg.remove_reg(0x0b); //set up end access dev->interface->write_0x8c(0x10, 0x0b); dev->interface->write_0x8c(0x13, 0x0e); /* CIS_LINE */ dev->reg.init_reg(0x08, REG_0x08_CIS_LINE); dev->interface->write_register(0x08, dev->reg.find_reg(0x08).value); // setup gpio gl124_init_gpio(dev); // setup internal memory layout gl124_init_memory_layout(dev); } void CommandSetGl124::update_hardware_sensors(Genesys_Scanner* s) const { /* do what is needed to get a new set of events, but try to not loose any of them. */ DBG_HELPER(dbg); std::uint8_t val = s->dev->interface->read_register(REG_0x31); /* TODO : for the next scanner special case, * add another per scanner button profile struct to avoid growing * hard-coded button mapping here. */ if ((s->dev->model->gpio_id == GpioId::CANON_LIDE_110) || (s->dev->model->gpio_id == GpioId::CANON_LIDE_120)) { s->buttons[BUTTON_SCAN_SW].write((val & 0x01) == 0); s->buttons[BUTTON_FILE_SW].write((val & 0x08) == 0); s->buttons[BUTTON_EMAIL_SW].write((val & 0x04) == 0); s->buttons[BUTTON_COPY_SW].write((val & 0x02) == 0); } else { /* LiDE 210 case */ s->buttons[BUTTON_EXTRA_SW].write((val & 0x01) == 0); s->buttons[BUTTON_SCAN_SW].write((val & 0x02) == 0); s->buttons[BUTTON_COPY_SW].write((val & 0x04) == 0); s->buttons[BUTTON_EMAIL_SW].write((val & 0x08) == 0); s->buttons[BUTTON_FILE_SW].write((val & 0x10) == 0); } } void CommandSetGl124::update_home_sensor_gpio(Genesys_Device& dev) const { DBG_HELPER(dbg); std::uint8_t val = dev.interface->read_register(REG_0x32); val &= ~REG_0x32_GPIO10; dev.interface->write_register(REG_0x32, val); } bool CommandSetGl124::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const { (void) dev; return true; } void CommandSetGl124::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const { sanei_genesys_send_gamma_table(dev, sensor); } void CommandSetGl124::load_document(Genesys_Device* dev) const { (void) dev; throw SaneException("not implemented"); } void CommandSetGl124::detect_document_end(Genesys_Device* dev) const { (void) dev; throw SaneException("not implemented"); } void CommandSetGl124::eject_document(Genesys_Device* dev) const { (void) dev; throw SaneException("not implemented"); } } // namespace gl124 } // namespace genesys backends-1.3.0/backend/genesys/gl124.h000066400000000000000000000077351456256263500173650ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2010-2016 Stéphane Voltz 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, see . */ #ifndef BACKEND_GENESYS_GL124_H #define BACKEND_GENESYS_GL124_H #include "genesys.h" #include "command_set_common.h" namespace genesys { namespace gl124 { class CommandSetGl124 : public CommandSetCommon { public: ~CommandSetGl124() override = default; bool needs_home_before_init_regs_for_scan(Genesys_Device* dev) const override; void init(Genesys_Device* dev) const override; void init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* regs) const override; void init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const override; void init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, const ScanSession& session) const override; void set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set) const override; void set_powersaving(Genesys_Device* dev, int delay) const override; void save_power(Genesys_Device* dev, bool enable) const override; void begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* regs, bool start_motor) const override; void end_scan(Genesys_Device* dev, Genesys_Register_Set* regs, bool check_stop) const override; void send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const override; void offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const override; void coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs, int dpi) const override; SensorExposure led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const override; void wait_for_motor_stop(Genesys_Device* dev) const override; void move_back_home(Genesys_Device* dev, bool wait_until_home) const override; void update_hardware_sensors(struct Genesys_Scanner* s) const override; void update_home_sensor_gpio(Genesys_Device& dev) const override; void load_document(Genesys_Device* dev) const override; void detect_document_end(Genesys_Device* dev) const override; void eject_document(Genesys_Device* dev) const override; void send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t* data, int size) const override; ScanSession calculate_scan_session(const Genesys_Device* dev, const Genesys_Sensor& sensor, const Genesys_Settings& settings) const override; void asic_boot(Genesys_Device* dev, bool cold) const override; }; enum SlopeTable { SCAN_TABLE = 0, // table 1 at 0x4000 BACKTRACK_TABLE = 1, // table 2 at 0x4800 STOP_TABLE = 2, // table 3 at 0x5000 FAST_TABLE = 3, // table 4 at 0x5800 HOME_TABLE = 4, // table 5 at 0x6000 }; } // namespace gl124 } // namespace genesys #endif // BACKEND_GENESYS_GL124_H backends-1.3.0/backend/genesys/gl124_registers.h000066400000000000000000000266571456256263500214600ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_GL124_REGISTERS_H #define BACKEND_GENESYS_GL124_REGISTERS_H #include namespace genesys { namespace gl124 { using RegAddr = std::uint16_t; using RegMask = std::uint8_t; using RegShift = unsigned; static constexpr RegAddr REG_0x01 = 0x01; static constexpr RegMask REG_0x01_CISSET = 0x80; static constexpr RegMask REG_0x01_DOGENB = 0x40; static constexpr RegMask REG_0x01_DVDSET = 0x20; static constexpr RegMask REG_0x01_STAGGER = 0x10; static constexpr RegMask REG_0x01_COMPENB = 0x08; static constexpr RegMask REG_0x01_TRUEGRAY = 0x04; static constexpr RegMask REG_0x01_SHDAREA = 0x02; static constexpr RegMask REG_0x01_SCAN = 0x01; static constexpr RegAddr REG_0x02 = 0x02; static constexpr RegMask REG_0x02_NOTHOME = 0x80; static constexpr RegMask REG_0x02_ACDCDIS = 0x40; static constexpr RegMask REG_0x02_AGOHOME = 0x20; static constexpr RegMask REG_0x02_MTRPWR = 0x10; static constexpr RegMask REG_0x02_FASTFED = 0x08; static constexpr RegMask REG_0x02_MTRREV = 0x04; static constexpr RegMask REG_0x02_HOMENEG = 0x02; static constexpr RegMask REG_0x02_LONGCURV = 0x01; static constexpr RegAddr REG_0x03 = 0x03; static constexpr RegMask REG_0x03_LAMPDOG = 0x80; static constexpr RegMask REG_0x03_AVEENB = 0x40; static constexpr RegMask REG_0x03_XPASEL = 0x20; static constexpr RegMask REG_0x03_LAMPPWR = 0x10; static constexpr RegMask REG_0x03_LAMPTIM = 0x0f; static constexpr RegAddr REG_0x04 = 0x04; static constexpr RegMask REG_0x04_LINEART = 0x80; static constexpr RegMask REG_0x04_BITSET = 0x40; static constexpr RegMask REG_0x04_FILTER = 0x30; static constexpr RegMask REG_0x04_AFEMOD = 0x07; static constexpr RegAddr REG_0x05 = 0x05; static constexpr RegMask REG_0x05_DPIHW = 0xc0; static constexpr RegMask REG_0x05_DPIHW_600 = 0x00; static constexpr RegMask REG_0x05_DPIHW_1200 = 0x40; static constexpr RegMask REG_0x05_DPIHW_2400 = 0x80; static constexpr RegMask REG_0x05_DPIHW_4800 = 0xc0; static constexpr RegMask REG_0x05_MTLLAMP = 0x30; static constexpr RegMask REG_0x05_GMMENB = 0x08; static constexpr RegMask REG_0x05_ENB20M = 0x04; static constexpr RegMask REG_0x05_MTLBASE = 0x03; static constexpr RegAddr REG_0x06 = 0x06; static constexpr RegMask REG_0x06_SCANMOD = 0xe0; static constexpr RegMask REG_0x06S_SCANMOD = 5; static constexpr RegMask REG_0x06_PWRBIT = 0x10; static constexpr RegMask REG_0x06_GAIN4 = 0x08; static constexpr RegMask REG_0x06_OPTEST = 0x07; static constexpr RegMask REG_0x07_LAMPSIM = 0x80; static constexpr RegMask REG_0x08_DRAM2X = 0x80; static constexpr RegMask REG_0x08_MPENB = 0x20; static constexpr RegMask REG_0x08_CIS_LINE = 0x10; static constexpr RegMask REG_0x08_IR2_ENB = 0x08; static constexpr RegMask REG_0x08_IR1_ENB = 0x04; static constexpr RegMask REG_0x08_ENB24M = 0x01; static constexpr RegMask REG_0x09_MCNTSET = 0xc0; static constexpr RegMask REG_0x09_EVEN1ST = 0x20; static constexpr RegMask REG_0x09_BLINE1ST = 0x10; static constexpr RegMask REG_0x09_BACKSCAN = 0x08; static constexpr RegMask REG_0x09_OUTINV = 0x04; static constexpr RegMask REG_0x09_SHORTTG = 0x02; static constexpr RegShift REG_0x09S_MCNTSET = 6; static constexpr RegShift REG_0x09S_CLKSET = 4; static constexpr RegAddr REG_0x0A = 0x0a; static constexpr RegMask REG_0x0A_SIFSEL = 0xc0; static constexpr RegShift REG_0x0AS_SIFSEL = 6; static constexpr RegMask REG_0x0A_SHEETFED = 0x20; static constexpr RegMask REG_0x0A_LPWMEN = 0x10; static constexpr RegAddr REG_0x0B = 0x0b; static constexpr RegMask REG_0x0B_DRAMSEL = 0x07; static constexpr RegMask REG_0x0B_16M = 0x01; static constexpr RegMask REG_0x0B_64M = 0x02; static constexpr RegMask REG_0x0B_128M = 0x03; static constexpr RegMask REG_0x0B_256M = 0x04; static constexpr RegMask REG_0x0B_512M = 0x05; static constexpr RegMask REG_0x0B_1G = 0x06; static constexpr RegMask REG_0x0B_ENBDRAM = 0x08; static constexpr RegMask REG_0x0B_RFHDIS = 0x10; static constexpr RegMask REG_0x0B_CLKSET = 0xe0; static constexpr RegMask REG_0x0B_24MHZ = 0x00; static constexpr RegMask REG_0x0B_30MHZ = 0x20; static constexpr RegMask REG_0x0B_40MHZ = 0x40; static constexpr RegMask REG_0x0B_48MHZ = 0x60; static constexpr RegMask REG_0x0B_60MHZ = 0x80; static constexpr RegAddr REG_0x0D = 0x0d; static constexpr RegMask REG_0x0D_MTRP_RDY = 0x80; static constexpr RegMask REG_0x0D_FULLSTP = 0x10; static constexpr RegMask REG_0x0D_CLRMCNT = 0x04; static constexpr RegMask REG_0x0D_CLRDOCJM = 0x02; static constexpr RegMask REG_0x0D_CLRLNCNT = 0x01; static constexpr RegAddr REG_0x0F = 0x0f; static constexpr RegMask REG_0x16_CTRLHI = 0x80; static constexpr RegMask REG_0x16_TOSHIBA = 0x40; static constexpr RegMask REG_0x16_TGINV = 0x20; static constexpr RegMask REG_0x16_CK1INV = 0x10; static constexpr RegMask REG_0x16_CK2INV = 0x08; static constexpr RegMask REG_0x16_CTRLINV = 0x04; static constexpr RegMask REG_0x16_CKDIS = 0x02; static constexpr RegMask REG_0x16_CTRLDIS = 0x01; static constexpr RegMask REG_0x17_TGMODE = 0xc0; static constexpr RegMask REG_0x17_SNRSYN = 0x0f; static constexpr RegAddr REG_0x18 = 0x18; static constexpr RegMask REG_0x18_CNSET = 0x80; static constexpr RegMask REG_0x18_DCKSEL = 0x60; static constexpr RegMask REG_0x18_CKTOGGLE = 0x10; static constexpr RegMask REG_0x18_CKDELAY = 0x0c; static constexpr RegMask REG_0x18_CKSEL = 0x03; static constexpr RegMask REG_0x1A_SW2SET = 0x80; static constexpr RegMask REG_0x1A_SW1SET = 0x40; static constexpr RegMask REG_0x1A_MANUAL3 = 0x02; static constexpr RegMask REG_0x1A_MANUAL1 = 0x01; static constexpr RegMask REG_0x1A_CK4INV = 0x08; static constexpr RegMask REG_0x1A_CK3INV = 0x04; static constexpr RegMask REG_0x1A_LINECLP = 0x02; static constexpr RegMask REG_0x1C_TBTIME = 0x07; static constexpr RegAddr REG_0x1D = 0x1d; static constexpr RegMask REG_0x1D_CK4LOW = 0x80; static constexpr RegMask REG_0x1D_CK3LOW = 0x40; static constexpr RegMask REG_0x1D_CK1LOW = 0x20; static constexpr RegMask REG_0x1D_LINESEL = 0x1f; static constexpr RegShift REG_0x1DS_LINESEL = 0; static constexpr RegAddr REG_0x1E = 0x1e; static constexpr RegMask REG_0x1E_WDTIME = 0xf0; static constexpr RegShift REG_0x1ES_WDTIME = 4; static constexpr RegAddr REG_0x30 = 0x30; static constexpr RegAddr REG_0x31 = 0x31; static constexpr RegAddr REG_0x32 = 0x32; static constexpr RegMask REG_0x32_GPIO16 = 0x80; static constexpr RegMask REG_0x32_GPIO15 = 0x40; static constexpr RegMask REG_0x32_GPIO14 = 0x20; static constexpr RegMask REG_0x32_GPIO13 = 0x10; static constexpr RegMask REG_0x32_GPIO12 = 0x08; static constexpr RegMask REG_0x32_GPIO11 = 0x04; static constexpr RegMask REG_0x32_GPIO10 = 0x02; static constexpr RegMask REG_0x32_GPIO9 = 0x01; static constexpr RegAddr REG_0x33 = 0x33; static constexpr RegAddr REG_0x34 = 0x34; static constexpr RegAddr REG_0x35 = 0x35; static constexpr RegAddr REG_0x36 = 0x36; static constexpr RegAddr REG_0x37 = 0x37; static constexpr RegAddr REG_0x38 = 0x38; static constexpr RegAddr REG_0x39 = 0x39; static constexpr RegAddr REG_0x60 = 0x60; static constexpr RegMask REG_0x60_LED4TG = 0x80; static constexpr RegMask REG_0x60_YENB = 0x40; static constexpr RegMask REG_0x60_YBIT = 0x20; static constexpr RegMask REG_0x60_ACYNCNRLC = 0x10; static constexpr RegMask REG_0x60_ENOFFSET = 0x08; static constexpr RegMask REG_0x60_LEDADD = 0x04; static constexpr RegMask REG_0x60_CK4ADC = 0x02; static constexpr RegMask REG_0x60_AUTOCONF = 0x01; static constexpr RegAddr REG_0x80 = 0x80; static constexpr RegAddr REG_0x81 = 0x81; static constexpr RegAddr REG_0xA0 = 0xa0; static constexpr RegMask REG_0xA0_FSTPSEL = 0x38; static constexpr RegShift REG_0xA0S_FSTPSEL = 3; static constexpr RegMask REG_0xA0_STEPSEL = 0x07; static constexpr RegShift REG_0xA0S_STEPSEL = 0; static constexpr RegAddr REG_0xA1 = 0xa1; static constexpr RegAddr REG_0xA2 = 0xa2; static constexpr RegAddr REG_0xA3 = 0xa3; static constexpr RegAddr REG_0xA4 = 0xa4; static constexpr RegAddr REG_0xA5 = 0xa5; static constexpr RegAddr REG_0xA6 = 0xa6; static constexpr RegAddr REG_0xA7 = 0xa7; static constexpr RegAddr REG_0xA8 = 0xa8; static constexpr RegAddr REG_0xA9 = 0xa9; static constexpr RegAddr REG_0xAA = 0xaa; static constexpr RegAddr REG_0xAB = 0xab; static constexpr RegAddr REG_0xAC = 0xac; static constexpr RegAddr REG_0xAD = 0xad; static constexpr RegAddr REG_0xAE = 0xae; static constexpr RegAddr REG_0xAF = 0xaf; static constexpr RegAddr REG_0xB0 = 0xb0; static constexpr RegAddr REG_0xB1 = 0xb1; static constexpr RegAddr REG_0xB2 = 0xb2; static constexpr RegMask REG_0xB2_Z1MOD = 0x1f; static constexpr RegAddr REG_0xB3 = 0xb3; static constexpr RegMask REG_0xB3_Z1MOD = 0xff; static constexpr RegAddr REG_0xB4 = 0xb4; static constexpr RegMask REG_0xB4_Z1MOD = 0xff; static constexpr RegAddr REG_0xB5 = 0xb5; static constexpr RegMask REG_0xB5_Z2MOD = 0x1f; static constexpr RegAddr REG_0xB6 = 0xb6; static constexpr RegMask REG_0xB6_Z2MOD = 0xff; static constexpr RegAddr REG_0xB7 = 0xb7; static constexpr RegMask REG_0xB7_Z2MOD = 0xff; static constexpr RegAddr REG_0x100 = 0x100; static constexpr RegMask REG_0x100_DOCSNR = 0x80; static constexpr RegMask REG_0x100_ADFSNR = 0x40; static constexpr RegMask REG_0x100_COVERSNR = 0x20; static constexpr RegMask REG_0x100_CHKVER = 0x10; static constexpr RegMask REG_0x100_DOCJAM = 0x08; static constexpr RegMask REG_0x100_HISPDFLG = 0x04; static constexpr RegMask REG_0x100_MOTMFLG = 0x02; static constexpr RegMask REG_0x100_DATAENB = 0x01; static constexpr RegAddr REG_0x114 = 0x114; static constexpr RegAddr REG_0x115 = 0x115; static constexpr RegAddr REG_LINCNT = 0x25; static constexpr RegAddr REG_MAXWD = 0x28; static constexpr RegAddr REG_DPISET = 0x2c; static constexpr RegAddr REG_FEEDL = 0x3d; static constexpr RegAddr REG_CK1MAP = 0x74; static constexpr RegAddr REG_CK3MAP = 0x77; static constexpr RegAddr REG_CK4MAP = 0x7a; static constexpr RegAddr REG_LPERIOD = 0x7d; static constexpr RegAddr REG_DUMMY = 0x80; static constexpr RegAddr REG_STRPIXEL = 0x82; static constexpr RegAddr REG_ENDPIXEL = 0x85; static constexpr RegAddr REG_EXPDMY = 0x88; static constexpr RegAddr REG_EXPR = 0x8a; static constexpr RegAddr REG_EXPG = 0x8d; static constexpr RegAddr REG_EXPB = 0x90; static constexpr RegAddr REG_SEGCNT = 0x93; static constexpr RegAddr REG_TG0CNT = 0x96; static constexpr RegAddr REG_SCANFED = 0xa2; static constexpr RegAddr REG_STEPNO = 0xa4; static constexpr RegAddr REG_FWDSTEP = 0xa6; static constexpr RegAddr REG_BWDSTEP = 0xa8; static constexpr RegAddr REG_FASTNO = 0xaa; static constexpr RegAddr REG_FSHDEC = 0xac; static constexpr RegAddr REG_FMOVNO = 0xae; static constexpr RegAddr REG_FMOVDEC = 0xb0; static constexpr RegAddr REG_Z1MOD = 0xb2; static constexpr RegAddr REG_Z2MOD = 0xb5; static constexpr RegAddr REG_TRUER = 0x110; static constexpr RegAddr REG_TRUEG = 0x111; static constexpr RegAddr REG_TRUEB = 0x112; } // namespace gl124 } // namespace genesys #endif // BACKEND_GENESYS_GL843_REGISTERS_H backends-1.3.0/backend/genesys/gl646.cpp000066400000000000000000003071261456256263500177260ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2003 Oliver Rauch Copyright (C) 2003, 2004 Henning Meier-Geinitz Copyright (C) 2004 Gerhard Jaeger Copyright (C) 2004-2013 Stéphane Voltz Copyright (C) 2005-2009 Pierre Willenbrock Copyright (C) 2007 Luke Copyright (C) 2011 Alexey Osipov for HP2400 description and tuning 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, see . */ #define DEBUG_DECLARE_ONLY #include "gl646.h" #include "gl646_registers.h" #include "test_settings.h" #include namespace genesys { namespace gl646 { namespace { constexpr unsigned CALIBRATION_LINES = 10; } // namespace static void write_control(Genesys_Device* dev, const Genesys_Sensor& sensor, int resolution); static void gl646_set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set, int dpi); static void simple_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, const ScanSession& session, bool move, std::vector& data, const char* test_identifier); /** * Send the stop scan command * */ static void end_scan_impl(Genesys_Device* dev, Genesys_Register_Set* reg, bool check_stop, bool eject); /** * master motor settings table entry */ struct Motor_Master { MotorId motor_id; unsigned dpi; unsigned channels; // settings StepType steptype; bool fastmod; // fast scanning bool fastfed; // fast fed slope tables SANE_Int mtrpwm; MotorSlope slope1; MotorSlope slope2; SANE_Int fwdbwd; // forward/backward steps }; /** * master motor settings, for a given motor and dpi, * it gives steps and speed information */ static Motor_Master motor_master[] = { /* HP3670 motor settings */ {MotorId::HP3670, 50, 3, StepType::HALF, false, true, 1, MotorSlope::create_from_steps(2329, 120, 229), MotorSlope::create_from_steps(3399, 337, 192), 192}, {MotorId::HP3670, 75, 3, StepType::FULL, false, true, 1, MotorSlope::create_from_steps(3429, 305, 200), MotorSlope::create_from_steps(3399, 337, 192), 192}, {MotorId::HP3670, 100, 3, StepType::HALF, false, true, 1, MotorSlope::create_from_steps(2905, 187, 143), MotorSlope::create_from_steps(3399, 337, 192), 192}, {MotorId::HP3670, 150, 3, StepType::HALF, false, true, 1, MotorSlope::create_from_steps(3429, 305, 73), MotorSlope::create_from_steps(3399, 337, 192), 192}, {MotorId::HP3670, 300, 3, StepType::HALF, false, true, 1, MotorSlope::create_from_steps(1055, 563, 11), MotorSlope::create_from_steps(3399, 337, 192), 192}, {MotorId::HP3670, 600, 3, StepType::FULL, false, true, 0, MotorSlope::create_from_steps(10687, 5126, 3), MotorSlope::create_from_steps(3399, 337, 192), 192}, {MotorId::HP3670,1200, 3, StepType::HALF, false, true, 0, MotorSlope::create_from_steps(15937, 6375, 3), MotorSlope::create_from_steps(3399, 337, 192), 192}, {MotorId::HP3670, 50, 1, StepType::HALF, false, true, 1, MotorSlope::create_from_steps(2329, 120, 229), MotorSlope::create_from_steps(3399, 337, 192), 192}, {MotorId::HP3670, 75, 1, StepType::FULL, false, true, 1, MotorSlope::create_from_steps(3429, 305, 200), MotorSlope::create_from_steps(3399, 337, 192), 192}, {MotorId::HP3670, 100, 1, StepType::HALF, false, true, 1, MotorSlope::create_from_steps(2905, 187, 143), MotorSlope::create_from_steps(3399, 337, 192), 192}, {MotorId::HP3670, 150, 1, StepType::HALF, false, true, 1, MotorSlope::create_from_steps(3429, 305, 73), MotorSlope::create_from_steps(3399, 337, 192), 192}, {MotorId::HP3670, 300, 1, StepType::HALF, false, true, 1, MotorSlope::create_from_steps(1055, 563, 11), MotorSlope::create_from_steps(3399, 337, 192), 192}, {MotorId::HP3670, 600, 1, StepType::FULL, false, true, 0, MotorSlope::create_from_steps(10687, 5126, 3), MotorSlope::create_from_steps(3399, 337, 192), 192}, {MotorId::HP3670,1200, 1, StepType::HALF, false, true, 0, MotorSlope::create_from_steps(15937, 6375, 3), MotorSlope::create_from_steps(3399, 337, 192), 192}, /* HP2400/G2410 motor settings base motor dpi = 600 */ {MotorId::HP2400, 50, 3, StepType::FULL, false, true, 63, MotorSlope::create_from_steps(8736, 601, 120), MotorSlope::create_from_steps(4905, 337, 192), 192}, {MotorId::HP2400, 100, 3, StepType::HALF, false, true, 63, MotorSlope::create_from_steps(8736, 601, 120), MotorSlope::create_from_steps(4905, 337, 192), 192}, {MotorId::HP2400, 150, 3, StepType::HALF, false, true, 63, MotorSlope::create_from_steps(15902, 902, 67), MotorSlope::create_from_steps(4905, 337, 192), 192}, {MotorId::HP2400, 300, 3, StepType::HALF, false, true, 63, MotorSlope::create_from_steps(16703, 2188, 32), MotorSlope::create_from_steps(4905, 337, 192), 192}, {MotorId::HP2400, 600, 3, StepType::FULL, false, true, 63, MotorSlope::create_from_steps(18761, 18761, 3), MotorSlope::create_from_steps(4905, 627, 192), 192}, {MotorId::HP2400,1200, 3, StepType::HALF, false, true, 63, MotorSlope::create_from_steps(43501, 43501, 3), MotorSlope::create_from_steps(4905, 627, 192), 192}, {MotorId::HP2400, 50, 1, StepType::FULL, false, true, 63, MotorSlope::create_from_steps(8736, 601, 120), MotorSlope::create_from_steps(4905, 337, 192), 192}, {MotorId::HP2400, 100, 1, StepType::HALF, false, true, 63, MotorSlope::create_from_steps(8736, 601, 120), MotorSlope::create_from_steps(4905, 337, 192), 192}, {MotorId::HP2400, 150, 1, StepType::HALF, false, true, 63, MotorSlope::create_from_steps(15902, 902, 67), MotorSlope::create_from_steps(4905, 337, 192), 192}, {MotorId::HP2400, 300, 1, StepType::HALF, false, true, 63, MotorSlope::create_from_steps(16703, 2188, 32), MotorSlope::create_from_steps(4905, 337, 192), 192}, {MotorId::HP2400, 600, 1, StepType::FULL, false, true, 63, MotorSlope::create_from_steps(18761, 18761, 3), MotorSlope::create_from_steps(4905, 337, 192), 192}, {MotorId::HP2400,1200, 1, StepType::HALF, false, true, 63, MotorSlope::create_from_steps(43501, 43501, 3), MotorSlope::create_from_steps(4905, 337, 192), 192}, /* XP 200 motor settings */ {MotorId::XP200, 75, 3, StepType::HALF, true, false, 0, MotorSlope::create_from_steps(6000, 2136, 4), MotorSlope::create_from_steps(12000, 1200, 8), 1}, {MotorId::XP200, 100, 3, StepType::HALF, true, false, 0, MotorSlope::create_from_steps(6000, 2850, 4), MotorSlope::create_from_steps(12000, 1200, 8), 1}, {MotorId::XP200, 200, 3, StepType::HALF, true, false, 0, MotorSlope::create_from_steps(6999, 5700, 4), MotorSlope::create_from_steps(12000, 1200, 8), 1}, {MotorId::XP200, 250, 3, StepType::HALF, true, false, 0, MotorSlope::create_from_steps(6999, 6999, 4), MotorSlope::create_from_steps(12000, 1200, 8), 1}, {MotorId::XP200, 300, 3, StepType::HALF, true, false, 0, MotorSlope::create_from_steps(13500, 13500, 4), MotorSlope::create_from_steps(12000, 1200, 8), 1}, {MotorId::XP200, 600, 3, StepType::HALF, true, true, 0, MotorSlope::create_from_steps(31998, 31998, 4), MotorSlope::create_from_steps(12000, 1200, 2), 1}, {MotorId::XP200, 75, 1, StepType::HALF, true, false, 0, MotorSlope::create_from_steps(6000, 2000, 4), MotorSlope::create_from_steps(12000, 1200, 8), 1}, {MotorId::XP200, 100, 1, StepType::HALF, true, false, 0, MotorSlope::create_from_steps(6000, 1300, 4), MotorSlope::create_from_steps(12000, 1200, 8), 1}, {MotorId::XP200, 200, 1, StepType::HALF, true, true, 0, MotorSlope::create_from_steps(6000, 3666, 4), MotorSlope::create_from_steps(12000, 1200, 8), 1}, {MotorId::XP200, 300, 1, StepType::HALF, true, false, 0, MotorSlope::create_from_steps(6500, 6500, 4), MotorSlope::create_from_steps(12000, 1200, 8), 1}, {MotorId::XP200, 600, 1, StepType::HALF, true, true, 0, MotorSlope::create_from_steps(24000, 24000, 4), MotorSlope::create_from_steps(12000, 1200, 2), 1}, /* HP scanjet 2300c */ {MotorId::HP2300, 75, 3, StepType::FULL, false, true, 63, MotorSlope::create_from_steps(8139, 560, 120), MotorSlope::create_from_steps(4905, 337, 120), 16}, {MotorId::HP2300, 150, 3, StepType::HALF, false, true, 63, MotorSlope::create_from_steps(7903, 543, 67), MotorSlope::create_from_steps(4905, 337, 120), 16}, {MotorId::HP2300, 300, 3, StepType::HALF, false, true, 63, MotorSlope::create_from_steps(2175, 1087, 3), MotorSlope::create_from_steps(4905, 337, 120), 16}, {MotorId::HP2300, 600, 3, StepType::HALF, false, true, 63, MotorSlope::create_from_steps(8700, 4350, 3), MotorSlope::create_from_steps(4905, 337, 120), 16}, {MotorId::HP2300,1200, 3, StepType::HALF, false, true, 63, MotorSlope::create_from_steps(17400, 8700, 3), MotorSlope::create_from_steps(4905, 337, 120), 16}, {MotorId::HP2300, 75, 1, StepType::FULL, false, true, 63, MotorSlope::create_from_steps(8139, 560, 120), MotorSlope::create_from_steps(4905, 337, 120), 16}, {MotorId::HP2300, 150, 1, StepType::HALF, false, true, 63, MotorSlope::create_from_steps(7903, 543, 67), MotorSlope::create_from_steps(4905, 337, 120), 16}, {MotorId::HP2300, 300, 1, StepType::HALF, false, true, 63, MotorSlope::create_from_steps(2175, 1087, 3), MotorSlope::create_from_steps(4905, 337, 120), 16}, {MotorId::HP2300, 600, 1, StepType::HALF, false, true, 63, MotorSlope::create_from_steps(8700, 4350, 3), MotorSlope::create_from_steps(4905, 337, 120), 16}, {MotorId::HP2300,1200, 1, StepType::HALF, false, true, 63, MotorSlope::create_from_steps(17400, 8700, 3), MotorSlope::create_from_steps(4905, 337, 120), 16}, /* non half ccd settings for 300 dpi {MotorId::HP2300, 300, 3, StepType::HALF, false, true, 63, MotorSlope::create_from_steps(5386, 2175, 44), MotorSlope::create_from_steps(4905, 337, 120), 16}, {MotorId::HP2300, 300, 1, StepType::HALF, false, true, 63, MotorSlope::create_from_steps(5386, 2175, 44), MotorSlope::create_from_steps(4905, 337, 120), 16}, */ /* MD5345/6471 motor settings */ /* vfinal=(exposure/(1200/dpi))/step_type */ {MotorId::MD_5345, 50, 3, StepType::HALF, false, true, 2, MotorSlope::create_from_steps(2500, 250, 255), MotorSlope::create_from_steps(2000, 300, 255), 64}, {MotorId::MD_5345, 75, 3, StepType::HALF, false, true, 2, MotorSlope::create_from_steps(2500, 343, 255), MotorSlope::create_from_steps(2000, 300, 255), 64}, {MotorId::MD_5345, 100, 3, StepType::HALF, false, true, 2, MotorSlope::create_from_steps(2500, 458, 255), MotorSlope::create_from_steps(2000, 300, 255), 64}, {MotorId::MD_5345, 150, 3, StepType::HALF, false, true, 2, MotorSlope::create_from_steps(2500, 687, 255), MotorSlope::create_from_steps(2000, 300, 255), 64}, {MotorId::MD_5345, 200, 3, StepType::HALF, false, true, 2, MotorSlope::create_from_steps(2500, 916, 255), MotorSlope::create_from_steps(2000, 300, 255), 64}, {MotorId::MD_5345, 300, 3, StepType::HALF, false, true, 2, MotorSlope::create_from_steps(2500, 1375, 255), MotorSlope::create_from_steps(2000, 300, 255), 64}, {MotorId::MD_5345, 400, 3, StepType::HALF, false, true, 0, MotorSlope::create_from_steps(2000, 1833, 32), MotorSlope::create_from_steps(2000, 300, 255), 32}, {MotorId::MD_5345, 500, 3, StepType::HALF, false, true, 0, MotorSlope::create_from_steps(2291, 2291, 32), MotorSlope::create_from_steps(2000, 300, 255), 32}, {MotorId::MD_5345, 600, 3, StepType::HALF, false, true, 0, MotorSlope::create_from_steps(2750, 2750, 32), MotorSlope::create_from_steps(2000, 300, 255), 32}, {MotorId::MD_5345, 1200, 3, StepType::QUARTER, false, true, 0, MotorSlope::create_from_steps(2750, 2750, 16), MotorSlope::create_from_steps(2000, 300, 255), 146}, {MotorId::MD_5345, 2400, 3, StepType::QUARTER, false, true, 0, MotorSlope::create_from_steps(5500, 5500, 16), MotorSlope::create_from_steps(2000, 300, 255), 146}, {MotorId::MD_5345, 50, 1, StepType::HALF, false, true, 2, MotorSlope::create_from_steps(2500, 250, 255), MotorSlope::create_from_steps(2000, 300, 255), 64}, {MotorId::MD_5345, 75, 1, StepType::HALF, false, true, 2, MotorSlope::create_from_steps(2500, 343, 255), MotorSlope::create_from_steps(2000, 300, 255), 64}, {MotorId::MD_5345, 100, 1, StepType::HALF, false, true, 2, MotorSlope::create_from_steps(2500, 458, 255), MotorSlope::create_from_steps(2000, 300, 255), 64}, {MotorId::MD_5345, 150, 1, StepType::HALF, false, true, 2, MotorSlope::create_from_steps(2500, 687, 255), MotorSlope::create_from_steps(2000, 300, 255), 64}, {MotorId::MD_5345, 200, 1, StepType::HALF, false, true, 2, MotorSlope::create_from_steps(2500, 916, 255), MotorSlope::create_from_steps(2000, 300, 255), 64}, {MotorId::MD_5345, 300, 1, StepType::HALF, false, true, 2, MotorSlope::create_from_steps(2500, 1375, 255), MotorSlope::create_from_steps(2000, 300, 255), 64}, {MotorId::MD_5345, 400, 1, StepType::HALF, false, true, 0, MotorSlope::create_from_steps(2000, 1833, 32), MotorSlope::create_from_steps(2000, 300, 255), 32}, {MotorId::MD_5345, 500, 1, StepType::HALF, false, true, 0, MotorSlope::create_from_steps(2291, 2291, 32), MotorSlope::create_from_steps(2000, 300, 255), 32}, {MotorId::MD_5345, 600, 1, StepType::HALF, false, true, 0, MotorSlope::create_from_steps(2750, 2750, 32), MotorSlope::create_from_steps(2000, 300, 255), 32}, {MotorId::MD_5345, 1200, 1, StepType::QUARTER, false, true, 0, MotorSlope::create_from_steps(2750, 2750, 16), MotorSlope::create_from_steps(2000, 300, 255), 146}, {MotorId::MD_5345, 2400, 1, StepType::QUARTER, false, true, 0, MotorSlope::create_from_steps(5500, 5500, 16), MotorSlope::create_from_steps(2000, 300, 255), 146}, /* 5500 guessed */ }; /** * reads value from gpio endpoint */ static void gl646_gpio_read(IUsbDevice& usb_dev, std::uint8_t* value) { DBG_HELPER(dbg); usb_dev.control_msg(REQUEST_TYPE_IN, REQUEST_REGISTER, GPIO_READ, INDEX, 1, value); } /** * writes the given value to gpio endpoint */ static void gl646_gpio_write(IUsbDevice& usb_dev, std::uint8_t value) { DBG_HELPER_ARGS(dbg, "(0x%02x)", value); usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, GPIO_WRITE, INDEX, 1, &value); } /** * writes the given value to gpio output enable endpoint */ static void gl646_gpio_output_enable(IUsbDevice& usb_dev, std::uint8_t value) { DBG_HELPER_ARGS(dbg, "(0x%02x)", value); usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, GPIO_OUTPUT_ENABLE, INDEX, 1, &value); } /** * stop scanner's motor * @param dev scanner's device */ static void gl646_stop_motor(Genesys_Device* dev) { DBG_HELPER(dbg); dev->interface->write_register(0x0f, 0x00); } /** * Returns the cksel values used by the required scan mode. * @param sensor id of the sensor * @param required required resolution * @param color true is color mode * @return cksel value for mode */ static int get_cksel(SensorId sensor_id, int required, unsigned channels) { for (const auto& sensor : *s_sensors) { // exit on perfect match if (sensor.sensor_id == sensor_id && sensor.resolutions.matches(required) && sensor.matches_channel_count(channels)) { unsigned cksel = sensor.ccd_pixels_per_system_pixel(); return cksel; } } DBG(DBG_error, "%s: failed to find match for %d dpi\n", __func__, required); /* fail safe fallback */ return 1; } void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* regs, const ScanSession& session) const { DBG_HELPER(dbg); session.assert_computed(); debug_dump(DBG_info, sensor); std::uint32_t move = session.params.starty; Motor_Master *motor = nullptr; std::uint32_t z1, z2; int feedl; /* for the given resolution, search for master * motor mode setting */ for (unsigned i = 0; i < sizeof (motor_master) / sizeof (Motor_Master); ++i) { if (dev->model->motor_id == motor_master[i].motor_id && motor_master[i].dpi == session.params.yres && motor_master[i].channels == session.params.channels) { motor = &motor_master[i]; } } if (motor == nullptr) { throw SaneException("unable to find settings for motor %d at %d dpi, color=%d", static_cast(dev->model->motor_id), session.params.yres, session.params.channels); } scanner_setup_sensor(*dev, sensor, *regs); /* now generate slope tables : we are not using generate_slope_table3 yet */ auto slope_table1 = create_slope_table_for_speed(motor->slope1, motor->slope1.max_speed_w, StepType::FULL, 1, 4, get_slope_table_max_size(AsicType::GL646)); auto slope_table2 = create_slope_table_for_speed(motor->slope2, motor->slope2.max_speed_w, StepType::FULL, 1, 4, get_slope_table_max_size(AsicType::GL646)); /* R01 */ /* now setup other registers for final scan (ie with shading enabled) */ /* watch dog + shading + scan enable */ regs->find_reg(0x01).value |= REG_0x01_DOGENB | REG_0x01_SCAN; if (dev->model->is_cis) { regs->find_reg(0x01).value |= REG_0x01_CISSET; } else { regs->find_reg(0x01).value &= ~REG_0x01_CISSET; } // if device has no calibration, don't enable shading correction if (has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION) || has_flag(session.params.flags, ScanFlag::DISABLE_SHADING)) { regs->find_reg(0x01).value &= ~REG_0x01_DVDSET; } else { regs->find_reg(0x01).value |= REG_0x01_DVDSET; } regs->find_reg(0x01).value &= ~REG_0x01_FASTMOD; if (motor->fastmod) { regs->find_reg(0x01).value |= REG_0x01_FASTMOD; } /* R02 */ /* allow moving when buffer full by default */ if (!dev->model->is_sheetfed) { dev->reg.find_reg(0x02).value &= ~REG_0x02_ACDCDIS; } else { dev->reg.find_reg(0x02).value |= REG_0x02_ACDCDIS; } /* setup motor power and direction */ sanei_genesys_set_motor_power(*regs, true); if (has_flag(session.params.flags, ScanFlag::REVERSE)) { regs->find_reg(0x02).value |= REG_0x02_MTRREV; } else { regs->find_reg(0x02).value &= ~REG_0x02_MTRREV; } /* fastfed enabled (2 motor slope tables) */ if (motor->fastfed) { regs->find_reg(0x02).value |= REG_0x02_FASTFED; } else { regs->find_reg(0x02).value &= ~REG_0x02_FASTFED; } /* step type */ regs->find_reg(0x02).value &= ~REG_0x02_STEPSEL; switch (motor->steptype) { case StepType::FULL: break; case StepType::HALF: regs->find_reg(0x02).value |= 1; break; case StepType::QUARTER: regs->find_reg(0x02).value |= 2; break; default: regs->find_reg(0x02).value |= 3; break; } if (dev->model->is_sheetfed || !has_flag(session.params.flags, ScanFlag::AUTO_GO_HOME)) { regs->find_reg(0x02).value &= ~REG_0x02_AGOHOME; } else { regs->find_reg(0x02).value |= REG_0x02_AGOHOME; } /* R03 */ regs->find_reg(0x03).value &= ~REG_0x03_AVEENB; // regs->find_reg(0x03).value |= REG_0x03_AVEENB; regs->find_reg(0x03).value &= ~REG_0x03_LAMPDOG; /* select XPA */ regs->find_reg(0x03).value &= ~REG_0x03_XPASEL; if ((session.params.flags & ScanFlag::USE_XPA) != ScanFlag::NONE) { regs->find_reg(0x03).value |= REG_0x03_XPASEL; } regs->state.is_xpa_on = (session.params.flags & ScanFlag::USE_XPA) != ScanFlag::NONE; /* R04 */ /* monochrome / color scan */ switch (session.params.depth) { case 8: regs->find_reg(0x04).value &= ~(REG_0x04_LINEART | REG_0x04_BITSET); break; case 16: regs->find_reg(0x04).value &= ~REG_0x04_LINEART; regs->find_reg(0x04).value |= REG_0x04_BITSET; break; } sanei_genesys_set_dpihw(*regs, sensor.full_resolution); /* gamma enable for scans */ if (has_flag(dev->model->flags, ModelFlag::GAMMA_14BIT)) { regs->find_reg(0x05).value |= REG_0x05_GMM14BIT; } if (!has_flag(session.params.flags, ScanFlag::DISABLE_GAMMA) && session.params.depth < 16) { regs->find_reg(REG_0x05).value |= REG_0x05_GMMENB; } else { regs->find_reg(REG_0x05).value &= ~REG_0x05_GMMENB; } /* true CIS gray if needed */ if (dev->model->is_cis && session.params.channels == 1 && session.params.color_filter == ColorFilter::NONE) { regs->find_reg(0x05).value |= REG_0x05_LEDADD; } else { regs->find_reg(0x05).value &= ~REG_0x05_LEDADD; } /* HP2400 1200dpi mode tuning */ if (dev->model->sensor_id == SensorId::CCD_HP2400) { /* reset count of dummy lines to zero */ regs->find_reg(0x1e).value &= ~REG_0x1E_LINESEL; if (session.params.xres >= 1200) { /* there must be one dummy line */ regs->find_reg(0x1e).value |= 1 & REG_0x1E_LINESEL; /* GPO12 need to be set to zero */ regs->find_reg(0x66).value &= ~0x20; } else { /* set GPO12 back to one */ regs->find_reg(0x66).value |= 0x20; } } /* motor steps used */ unsigned forward_steps = motor->fwdbwd; unsigned backward_steps = motor->fwdbwd; // the steps count must be different by at most 128, otherwise it's impossible to construct // a proper backtracking curve. We're using slightly lower limit to allow at least a minimum // distance between accelerations (forward_steps, backward_steps) if (slope_table1.table.size() > slope_table2.table.size() + 100) { slope_table2.expand_table(slope_table1.table.size() - 100, 1); } if (slope_table2.table.size() > slope_table1.table.size() + 100) { slope_table1.expand_table(slope_table2.table.size() - 100, 1); } if (slope_table1.table.size() >= slope_table2.table.size()) { backward_steps += (slope_table1.table.size() - slope_table2.table.size()) * 2; } else { forward_steps += (slope_table2.table.size() - slope_table1.table.size()) * 2; } if (forward_steps > 255) { if (backward_steps < (forward_steps - 255)) { throw SaneException("Can't set backtracking parameters without skipping image"); } backward_steps -= forward_steps - 255; } if (backward_steps > 255) { if (forward_steps < (backward_steps - 255)) { throw SaneException("Can't set backtracking parameters without skipping image"); } forward_steps -= backward_steps - 255; } regs->find_reg(0x21).value = slope_table1.table.size(); regs->find_reg(0x24).value = slope_table2.table.size(); regs->find_reg(0x22).value = forward_steps; regs->find_reg(0x23).value = backward_steps; /* CIS scanners read one line per color channel * since gray mode use 'add' we also read 3 channels even not in * color mode */ if (dev->model->is_cis) { regs->set24(REG_LINCNT, session.output_line_count * 3); } else { regs->set24(REG_LINCNT, session.output_line_count); } regs->set16(REG_STRPIXEL, session.pixel_startx); regs->set16(REG_ENDPIXEL, session.pixel_endx); regs->set24(REG_MAXWD, session.output_line_bytes); // FIXME: the incoming sensor is selected for incorrect resolution const auto& dpiset_sensor = sanei_genesys_find_sensor(dev, session.params.xres, session.params.channels, session.params.scan_method); regs->set16(REG_DPISET, dpiset_sensor.register_dpiset); regs->set16(REG_LPERIOD, sensor.exposure_lperiod); /* move distance must be adjusted to take into account the extra lines * read to reorder data */ feedl = move; if (session.num_staggered_lines + session.max_color_shift_lines > 0 && feedl != 0) { unsigned total_lines = session.max_color_shift_lines + session.num_staggered_lines; int feed_offset = (total_lines * dev->motor.base_ydpi) / motor->dpi; if (feedl > feed_offset) { feedl = feedl - feed_offset; } } /* we assume all scans are done with 2 tables */ /* feedl = feed_steps - fast_slope_steps*2 - (slow_slope_steps >> scan_step_type); */ /* but head has moved due to shading calibration => dev->scanhead_position_primary */ if (feedl > 0) { /* TODO clean up this when I'll fully understand. * for now, special casing each motor */ switch (dev->model->motor_id) { case MotorId::MD_5345: switch (motor->dpi) { case 200: feedl -= 70; break; case 300: feedl -= 70; break; case 400: feedl += 130; break; case 600: feedl += 160; break; case 1200: feedl += 160; break; case 2400: feedl += 180; break; default: break; } break; case MotorId::HP2300: switch (motor->dpi) { case 75: feedl -= 180; break; case 150: feedl += 0; break; case 300: feedl += 30; break; case 600: feedl += 35; break; case 1200: feedl += 45; break; default: break; } break; case MotorId::HP2400: switch (motor->dpi) { case 150: feedl += 150; break; case 300: feedl += 220; break; case 600: feedl += 260; break; case 1200: feedl += 280; /* 300 */ break; case 50: feedl += 0; break; case 100: feedl += 100; break; default: break; } break; /* theorical value */ default: { unsigned step_shift = static_cast(motor->steptype); if (motor->fastfed) { feedl = feedl - 2 * slope_table2.table.size() - (slope_table1.table.size() >> step_shift); } else { feedl = feedl - (slope_table1.table.size() >> step_shift); } break; } } /* security */ if (feedl < 0) feedl = 0; } regs->set24(REG_FEEDL, feedl); regs->find_reg(0x65).value = motor->mtrpwm; sanei_genesys_calculate_zmod(regs->find_reg(0x02).value & REG_0x02_FASTFED, sensor.exposure_lperiod, slope_table1.table, slope_table1.table.size(), move, motor->fwdbwd, &z1, &z2); /* no z1/z2 for sheetfed scanners */ if (dev->model->is_sheetfed) { z1 = 0; z2 = 0; } regs->set16(REG_Z1MOD, z1); regs->set16(REG_Z2MOD, z2); regs->find_reg(0x6b).value = slope_table2.table.size(); regs->find_reg(0x6c).value = (regs->find_reg(0x6c).value & REG_0x6C_TGTIME) | ((z1 >> 13) & 0x38) | ((z2 >> 16) & 0x07); write_control(dev, sensor, session.output_resolution); // setup analog frontend gl646_set_fe(dev, sensor, AFE_SET, session.output_resolution); setup_image_pipeline(*dev, session); dev->read_active = true; dev->session = session; dev->total_bytes_read = 0; dev->total_bytes_to_read = (size_t) session.output_line_bytes_requested * (size_t) session.params.lines; /* select color filter based on settings */ regs->find_reg(0x04).value &= ~REG_0x04_FILTER; if (session.params.channels == 1) { switch (session.params.color_filter) { case ColorFilter::RED: regs->find_reg(0x04).value |= 0x04; break; case ColorFilter::GREEN: regs->find_reg(0x04).value |= 0x08; break; case ColorFilter::BLUE: regs->find_reg(0x04).value |= 0x0c; break; default: break; } } scanner_send_slope_table(dev, sensor, 0, slope_table1.table); scanner_send_slope_table(dev, sensor, 1, slope_table2.table); } /** * Set all registers to default values after init * @param dev scannerr's device to set */ static void gl646_init_regs (Genesys_Device * dev) { int addr; DBG(DBG_proc, "%s\n", __func__); dev->reg.clear(); for (addr = 1; addr <= 0x0b; addr++) dev->reg.init_reg(addr, 0); for (addr = 0x10; addr <= 0x29; addr++) dev->reg.init_reg(addr, 0); for (addr = 0x2c; addr <= 0x39; addr++) dev->reg.init_reg(addr, 0); for (addr = 0x3d; addr <= 0x3f; addr++) dev->reg.init_reg(addr, 0); for (addr = 0x52; addr <= 0x5e; addr++) dev->reg.init_reg(addr, 0); for (addr = 0x60; addr <= 0x6d; addr++) dev->reg.init_reg(addr, 0); dev->reg.find_reg(0x01).value = 0x20 /*0x22 */ ; /* enable shading, CCD, color, 1M */ dev->reg.find_reg(0x02).value = 0x30 /*0x38 */ ; /* auto home, one-table-move, full step */ if (dev->model->motor_id == MotorId::MD_5345) { dev->reg.find_reg(0x02).value |= 0x01; // half-step } switch (dev->model->motor_id) { case MotorId::MD_5345: dev->reg.find_reg(0x02).value |= 0x01; /* half-step */ break; case MotorId::XP200: /* for this sheetfed scanner, no AGOHOME, nor backtracking */ dev->reg.find_reg(0x02).value = 0x50; break; default: break; } dev->reg.find_reg(0x03).value = 0x1f /*0x17 */ ; /* lamp on */ dev->reg.find_reg(0x04).value = 0x13 /*0x03 */ ; /* 8 bits data, 16 bits A/D, color, Wolfson fe *//* todo: according to spec, 0x0 is reserved? */ switch (dev->model->adc_id) { case AdcId::AD_XP200: dev->reg.find_reg(0x04).value = 0x12; break; default: /* Wolfson frontend */ dev->reg.find_reg(0x04).value = 0x13; break; } const auto& sensor = sanei_genesys_find_sensor_any(dev); dev->reg.find_reg(0x05).value = 0x00; /* 12 bits gamma, disable gamma, 24 clocks/pixel */ sanei_genesys_set_dpihw(dev->reg, sensor.full_resolution); if (has_flag(dev->model->flags, ModelFlag::GAMMA_14BIT)) { dev->reg.find_reg(0x05).value |= REG_0x05_GMM14BIT; } if (dev->model->adc_id == AdcId::AD_XP200) { dev->reg.find_reg(0x05).value |= 0x01; /* 12 clocks/pixel */ } if (dev->model->sensor_id == SensorId::CCD_HP2300) { dev->reg.find_reg(0x06).value = 0x00; // PWRBIT off, shading gain=4, normal AFE image capture } else { dev->reg.find_reg(0x06).value = 0x18; // PWRBIT on, shading gain=8, normal AFE image capture } scanner_setup_sensor(*dev, sensor, dev->reg); dev->reg.find_reg(0x1e).value = 0xf0; /* watch-dog time */ switch (dev->model->sensor_id) { case SensorId::CCD_HP2300: dev->reg.find_reg(0x1e).value = 0xf0; dev->reg.find_reg(0x1f).value = 0x10; dev->reg.find_reg(0x20).value = 0x20; break; case SensorId::CCD_HP2400: dev->reg.find_reg(0x1e).value = 0x80; dev->reg.find_reg(0x1f).value = 0x10; dev->reg.find_reg(0x20).value = 0x20; break; case SensorId::CCD_HP3670: dev->reg.find_reg(0x19).value = 0x2a; dev->reg.find_reg(0x1e).value = 0x80; dev->reg.find_reg(0x1f).value = 0x10; dev->reg.find_reg(0x20).value = 0x20; break; case SensorId::CIS_XP200: dev->reg.find_reg(0x1e).value = 0x10; dev->reg.find_reg(0x1f).value = 0x01; dev->reg.find_reg(0x20).value = 0x50; break; default: dev->reg.find_reg(0x1f).value = 0x01; dev->reg.find_reg(0x20).value = 0x50; break; } dev->reg.find_reg(0x21).value = 0x08 /*0x20 */ ; /* table one steps number for forward slope curve of the acc/dec */ dev->reg.find_reg(0x22).value = 0x10 /*0x08 */ ; /* steps number of the forward steps for start/stop */ dev->reg.find_reg(0x23).value = 0x10 /*0x08 */ ; /* steps number of the backward steps for start/stop */ dev->reg.find_reg(0x24).value = 0x08 /*0x20 */ ; /* table one steps number backward slope curve of the acc/dec */ dev->reg.find_reg(0x25).value = 0x00; /* scan line numbers (7000) */ dev->reg.find_reg(0x26).value = 0x00 /*0x1b */ ; dev->reg.find_reg(0x27).value = 0xd4 /*0x58 */ ; dev->reg.find_reg(0x28).value = 0x01; /* PWM duty for lamp control */ dev->reg.find_reg(0x29).value = 0xff; dev->reg.find_reg(0x2c).value = 0x02; /* set resolution (600 DPI) */ dev->reg.find_reg(0x2d).value = 0x58; dev->reg.find_reg(0x2e).value = 0x78; /* set black&white threshold high level */ dev->reg.find_reg(0x2f).value = 0x7f; /* set black&white threshold low level */ dev->reg.find_reg(0x30).value = 0x00; /* begin pixel position (16) */ dev->reg.find_reg(0x31).value = sensor.dummy_pixel /*0x10 */ ; /* TGW + 2*TG_SHLD + x */ dev->reg.find_reg(0x32).value = 0x2a /*0x15 */ ; /* end pixel position (5390) */ dev->reg.find_reg(0x33).value = 0xf8 /*0x0e */ ; /* TGW + 2*TG_SHLD + y */ dev->reg.find_reg(0x34).value = sensor.dummy_pixel; dev->reg.find_reg(0x35).value = 0x01 /*0x00 */ ; /* set maximum word size per line, for buffer full control (10800) */ dev->reg.find_reg(0x36).value = 0x00 /*0x2a */ ; dev->reg.find_reg(0x37).value = 0x00 /*0x30 */ ; dev->reg.find_reg(0x38).value = 0x2a; // line period (exposure time = 11000 pixels) */ dev->reg.find_reg(0x39).value = 0xf8; dev->reg.find_reg(0x3d).value = 0x00; /* set feed steps number of motor move */ dev->reg.find_reg(0x3e).value = 0x00; dev->reg.find_reg(0x3f).value = 0x01 /*0x00 */ ; dev->reg.find_reg(0x60).value = 0x00; /* Z1MOD, 60h:61h:(6D b5:b3), remainder for start/stop */ dev->reg.find_reg(0x61).value = 0x00; /* (21h+22h)/LPeriod */ dev->reg.find_reg(0x62).value = 0x00; /* Z2MODE, 62h:63h:(6D b2:b0), remainder for start scan */ dev->reg.find_reg(0x63).value = 0x00; /* (3Dh+3Eh+3Fh)/LPeriod for one-table mode,(21h+1Fh)/LPeriod */ dev->reg.find_reg(0x64).value = 0x00; /* motor PWM frequency */ dev->reg.find_reg(0x65).value = 0x00; /* PWM duty cycle for table one motor phase (63 = max) */ if (dev->model->motor_id == MotorId::MD_5345) { // PWM duty cycle for table one motor phase (63 = max) dev->reg.find_reg(0x65).value = 0x02; } for (const auto& reg : dev->gpo.regs) { dev->reg.set8(reg.address, reg.value); } switch (dev->model->motor_id) { case MotorId::HP2300: case MotorId::HP2400: dev->reg.find_reg(0x6a).value = 0x7f; /* table two steps number for acc/dec */ dev->reg.find_reg(0x6b).value = 0x78; /* table two steps number for acc/dec */ dev->reg.find_reg(0x6d).value = 0x7f; break; case MotorId::MD_5345: dev->reg.find_reg(0x6a).value = 0x42; /* table two fast moving step type, PWM duty for table two */ dev->reg.find_reg(0x6b).value = 0xff; /* table two steps number for acc/dec */ dev->reg.find_reg(0x6d).value = 0x41; /* select deceleration steps whenever go home (0), accel/decel stop time (31 * LPeriod) */ break; case MotorId::XP200: dev->reg.find_reg(0x6a).value = 0x7f; /* table two fast moving step type, PWM duty for table two */ dev->reg.find_reg(0x6b).value = 0x08; /* table two steps number for acc/dec */ dev->reg.find_reg(0x6d).value = 0x01; /* select deceleration steps whenever go home (0), accel/decel stop time (31 * LPeriod) */ break; case MotorId::HP3670: dev->reg.find_reg(0x6a).value = 0x41; /* table two steps number for acc/dec */ dev->reg.find_reg(0x6b).value = 0xc8; /* table two steps number for acc/dec */ dev->reg.find_reg(0x6d).value = 0x7f; break; default: dev->reg.find_reg(0x6a).value = 0x40; /* table two fast moving step type, PWM duty for table two */ dev->reg.find_reg(0x6b).value = 0xff; /* table two steps number for acc/dec */ dev->reg.find_reg(0x6d).value = 0x01; /* select deceleration steps whenever go home (0), accel/decel stop time (31 * LPeriod) */ break; } dev->reg.find_reg(0x6c).value = 0x00; /* period times for LPeriod, expR,expG,expB, Z1MODE, Z2MODE (one period time) */ } // Set values of Analog Device type frontend static void gl646_set_ad_fe(Genesys_Device* dev, std::uint8_t set) { DBG_HELPER(dbg); int i; if (set == AFE_INIT) { dev->frontend = dev->frontend_initial; // write them to analog frontend dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00)); dev->interface->write_fe_register(0x01, dev->frontend.regs.get_value(0x01)); } if (set == AFE_SET) { for (i = 0; i < 3; i++) { dev->interface->write_fe_register(0x02 + i, dev->frontend.get_gain(i)); } for (i = 0; i < 3; i++) { dev->interface->write_fe_register(0x05 + i, dev->frontend.get_offset(i)); } } /* if (set == AFE_POWER_SAVE) { dev->interface->write_fe_register(0x00, dev->frontend.reg[0] | 0x04); } */ } /** set up analog frontend * set up analog frontend * @param dev device to set up * @param set action from AFE_SET, AFE_INIT and AFE_POWERSAVE * @param dpi resolution of the scan since it affects settings */ static void gl646_wm_hp3670(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set, unsigned dpi) { DBG_HELPER(dbg); int i; switch (set) { case AFE_INIT: dev->interface->write_fe_register(0x04, 0x80); dev->interface->sleep_ms(200); dev->interface->write_register(0x50, 0x00); dev->frontend = dev->frontend_initial; dev->interface->write_fe_register(0x01, dev->frontend.regs.get_value(0x01)); dev->interface->write_fe_register(0x02, dev->frontend.regs.get_value(0x02)); gl646_gpio_output_enable(dev->interface->get_usb_device(), 0x07); break; case AFE_POWER_SAVE: dev->interface->write_fe_register(0x01, 0x06); dev->interface->write_fe_register(0x06, 0x0f); return; break; default: /* AFE_SET */ /* mode setup */ i = dev->frontend.regs.get_value(0x03); if (dpi > sensor.full_resolution / 2) { /* fe_reg_0x03 must be 0x12 for 1200 dpi in WOLFSON_HP3670. * WOLFSON_HP2400 in 1200 dpi mode works well with * fe_reg_0x03 set to 0x32 or 0x12 but not to 0x02 */ i = 0x12; } dev->interface->write_fe_register(0x03, i); /* offset and sign (or msb/lsb ?) */ for (i = 0; i < 3; i++) { dev->interface->write_fe_register(0x20 + i, dev->frontend.get_offset(i)); dev->interface->write_fe_register(0x24 + i, dev->frontend.regs.get_value(0x24 + i)); } // gain for (i = 0; i < 3; i++) { dev->interface->write_fe_register(0x28 + i, dev->frontend.get_gain(i)); } } } /** Set values of analog frontend * @param dev device to set * @param set action to execute * @param dpi dpi to setup the AFE */ static void gl646_set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set, int dpi) { DBG_HELPER_ARGS(dbg, "%s,%d", set == AFE_INIT ? "init" : set == AFE_SET ? "set" : set == AFE_POWER_SAVE ? "powersave" : "huh?", dpi); int i; std::uint8_t val; /* Analog Device type frontend */ std::uint8_t frontend_type = dev->reg.find_reg(0x04).value & REG_0x04_FESET; if (frontend_type == 0x02) { gl646_set_ad_fe(dev, set); return; } /* Wolfson type frontend */ if (frontend_type != 0x03) { throw SaneException("unsupported frontend type %d", frontend_type); } /* per frontend function to keep code clean */ switch (dev->model->adc_id) { case AdcId::WOLFSON_HP3670: case AdcId::WOLFSON_HP2400: gl646_wm_hp3670(dev, sensor, set, dpi); return; default: DBG(DBG_proc, "%s(): using old method\n", __func__); break; } /* initialize analog frontend */ if (set == AFE_INIT) { dev->frontend = dev->frontend_initial; // reset only done on init dev->interface->write_fe_register(0x04, 0x80); /* enable GPIO for some models */ if (dev->model->sensor_id == SensorId::CCD_HP2300) { val = 0x07; gl646_gpio_output_enable(dev->interface->get_usb_device(), val); } return; } // set fontend to power saving mode if (set == AFE_POWER_SAVE) { dev->interface->write_fe_register(0x01, 0x02); return; } /* here starts AFE_SET */ /* TODO : base this test on cfg reg3 or a CCD family flag to be created */ /* if (dev->model->ccd_type != SensorId::CCD_HP2300 && dev->model->ccd_type != SensorId::CCD_HP3670 && dev->model->ccd_type != SensorId::CCD_HP2400) */ { dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00)); dev->interface->write_fe_register(0x02, dev->frontend.regs.get_value(0x02)); } // start with reg3 dev->interface->write_fe_register(0x03, dev->frontend.regs.get_value(0x03)); switch (dev->model->sensor_id) { default: for (i = 0; i < 3; i++) { dev->interface->write_fe_register(0x24 + i, dev->frontend.regs.get_value(0x24 + i)); dev->interface->write_fe_register(0x28 + i, dev->frontend.get_gain(i)); dev->interface->write_fe_register(0x20 + i, dev->frontend.get_offset(i)); } break; /* just can't have it to work .... case SensorId::CCD_HP2300: case SensorId::CCD_HP2400: case SensorId::CCD_HP3670: dev->interface->write_fe_register(0x23, dev->frontend.get_offset(1)); dev->interface->write_fe_register(0x28, dev->frontend.get_gain(1)); break; */ } // end with reg1 dev->interface->write_fe_register(0x01, dev->frontend.regs.get_value(0x01)); } /** Set values of analog frontend * this this the public interface, the gl646 as to use one more * parameter to work effectively, hence the redirection * @param dev device to set * @param set action to execute */ void CommandSetGl646::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set) const { gl646_set_fe(dev, sensor, set, dev->settings.yres); } /** * enters or leaves power saving mode * limited to AFE for now. * @param dev scanner's device * @param enable true to enable power saving, false to leave it */ void CommandSetGl646::save_power(Genesys_Device* dev, bool enable) const { DBG_HELPER_ARGS(dbg, "enable = %d", enable); const auto& sensor = sanei_genesys_find_sensor_any(dev); if (enable) { // gl646_set_fe(dev, sensor, AFE_POWER_SAVE); } else { gl646_set_fe(dev, sensor, AFE_INIT, 0); } } void CommandSetGl646::set_powersaving(Genesys_Device* dev, int delay /* in minutes */) const { DBG_HELPER_ARGS(dbg, "delay = %d", delay); Genesys_Register_Set local_reg(Genesys_Register_Set::SEQUENTIAL); int rate, exposure_time, tgtime, time; local_reg.init_reg(0x01, dev->reg.get8(0x01)); // disable fastmode local_reg.init_reg(0x03, dev->reg.get8(0x03)); // Lamp power control local_reg.init_reg(0x05, dev->reg.get8(0x05) & ~REG_0x05_BASESEL); // 24 clocks/pixel local_reg.init_reg(0x38, 0x00); // line period low local_reg.init_reg(0x39, 0x00); //line period high local_reg.init_reg(0x6c, 0x00); // period times for LPeriod, expR,expG,expB, Z1MODE, Z2MODE if (!delay) local_reg.find_reg(0x03).value &= 0xf0; /* disable lampdog and set lamptime = 0 */ else if (delay < 20) local_reg.find_reg(0x03).value = (local_reg.get8(0x03) & 0xf0) | 0x09; /* enable lampdog and set lamptime = 1 */ else local_reg.find_reg(0x03).value = (local_reg.get8(0x03) & 0xf0) | 0x0f; /* enable lampdog and set lamptime = 7 */ time = delay * 1000 * 60; /* -> msec */ exposure_time = static_cast((time * 32000.0 / (24.0 * 64.0 * (local_reg.get8(0x03) & REG_0x03_LAMPTIM) * 1024.0) + 0.5)); /* 32000 = system clock, 24 = clocks per pixel */ rate = (exposure_time + 65536) / 65536; if (rate > 4) { rate = 8; tgtime = 3; } else if (rate > 2) { rate = 4; tgtime = 2; } else if (rate > 1) { rate = 2; tgtime = 1; } else { rate = 1; tgtime = 0; } local_reg.find_reg(0x6c).value |= tgtime << 6; exposure_time /= rate; if (exposure_time > 65535) exposure_time = 65535; local_reg.find_reg(0x38).value = exposure_time / 256; local_reg.find_reg(0x39).value = exposure_time & 255; dev->interface->write_registers(local_reg); } /** * loads document into scanner * currently only used by XP200 * bit2 (0x04) of gpio is paper event (document in/out) on XP200 * HOMESNR is set if no document in front of sensor, the sequence of events is * paper event -> document is in the sheet feeder * HOMESNR becomes 0 -> document reach sensor * HOMESNR becomes 1 ->document left sensor * paper event -> document is out */ void CommandSetGl646::load_document(Genesys_Device* dev) const { DBG_HELPER(dbg); // FIXME: sequential not really needed in this case Genesys_Register_Set regs(Genesys_Register_Set::SEQUENTIAL); unsigned count; /* no need to load document is flatbed scanner */ if (!dev->model->is_sheetfed) { DBG(DBG_proc, "%s: nothing to load\n", __func__); DBG(DBG_proc, "%s: end\n", __func__); return; } auto status = scanner_read_status(*dev); // home sensor is set if a document is inserted if (status.is_at_home) { /* if no document, waits for a paper event to start loading */ /* with a 60 seconde minutes timeout */ count = 0; std::uint8_t val = 0; do { gl646_gpio_read(dev->interface->get_usb_device(), &val); DBG(DBG_info, "%s: GPIO=0x%02x\n", __func__, val); if ((val & 0x04) != 0x04) { DBG(DBG_warn, "%s: no paper detected\n", __func__); } dev->interface->sleep_ms(200); count++; } while (((val & 0x04) != 0x04) && (count < 300)); /* 1 min time out */ if (count == 300) { throw SaneException(SANE_STATUS_NO_DOCS, "timeout waiting for document"); } } /* set up to fast move before scan then move until document is detected */ regs.init_reg(0x01, 0x90); /* AGOME, 2 slopes motor moving */ regs.init_reg(0x02, 0x79); /* motor feeding steps to 0 */ regs.init_reg(0x3d, 0); regs.init_reg(0x3e, 0); regs.init_reg(0x3f, 0); /* 50 fast moving steps */ regs.init_reg(0x6b, 50); /* set GPO */ regs.init_reg(0x66, 0x30); /* stesp NO */ regs.init_reg(0x21, 4); regs.init_reg(0x22, 1); regs.init_reg(0x23, 1); regs.init_reg(0x24, 4); /* generate slope table 2 */ auto slope_table = create_slope_table_for_speed(MotorSlope::create_from_steps(6000, 2400, 50), 2400, StepType::FULL, 1, 4, get_slope_table_max_size(AsicType::GL646)); // document loading: // send regs // start motor // wait e1 status to become e0 const auto& sensor = sanei_genesys_find_sensor_any(dev); scanner_send_slope_table(dev, sensor, 1, slope_table.table); dev->interface->write_registers(regs); scanner_start_action(*dev, true); count = 0; do { status = scanner_read_status(*dev); dev->interface->sleep_ms(200); count++; } while (status.is_motor_enabled && (count < 300)); if (count == 300) { throw SaneException(SANE_STATUS_JAMMED, "can't load document"); } /* when loading OK, document is here */ dev->document = true; /* set up to idle */ regs.set8(0x02, 0x71); regs.set8(0x3f, 1); regs.set8(0x6b, 8); dev->interface->write_registers(regs); } /** * detects end of document and adjust current scan * to take it into account * used by sheetfed scanners */ void CommandSetGl646::detect_document_end(Genesys_Device* dev) const { DBG_HELPER(dbg); std::uint8_t gpio; unsigned int bytes_left; // test for document presence scanner_read_print_status(*dev); gl646_gpio_read(dev->interface->get_usb_device(), &gpio); DBG(DBG_info, "%s: GPIO=0x%02x\n", __func__, gpio); /* detect document event. There one event when the document go in, * then another when it leaves */ if (dev->document && (gpio & 0x04) && (dev->total_bytes_read > 0)) { DBG(DBG_info, "%s: no more document\n", __func__); dev->document = false; /* adjust number of bytes to read: * total_bytes_to_read is the number of byte to send to frontend * total_bytes_read is the number of bytes sent to frontend * read_bytes_left is the number of bytes to read from the scanner */ DBG(DBG_io, "%s: total_bytes_to_read=%zu\n", __func__, dev->total_bytes_to_read); DBG(DBG_io, "%s: total_bytes_read =%zu\n", __func__, dev->total_bytes_read); // amount of data available from scanner is what to scan sanei_genesys_read_valid_words(dev, &bytes_left); unsigned lines_in_buffer = bytes_left / dev->session.output_line_bytes_raw; // we add the number of lines needed to read the last part of the document in unsigned lines_offset = static_cast( (dev->model->y_offset * dev->session.params.yres) / MM_PER_INCH); unsigned remaining_lines = lines_in_buffer + lines_offset; bytes_left = remaining_lines * dev->session.output_line_bytes_raw; if (bytes_left < dev->get_pipeline_source().remaining_bytes()) { dev->get_pipeline_source().set_remaining_bytes(bytes_left); dev->total_bytes_to_read = dev->total_bytes_read + bytes_left; } DBG(DBG_io, "%s: total_bytes_to_read=%zu\n", __func__, dev->total_bytes_to_read); DBG(DBG_io, "%s: total_bytes_read =%zu\n", __func__, dev->total_bytes_read); } } /** * eject document from the feeder * currently only used by XP200 * TODO we currently rely on AGOHOME not being set for sheetfed scanners, * maybe check this flag in eject to let the document being eject automatically */ void CommandSetGl646::eject_document(Genesys_Device* dev) const { DBG_HELPER(dbg); // FIXME: SEQUENTIAL not really needed in this case Genesys_Register_Set regs((Genesys_Register_Set::SEQUENTIAL)); unsigned count; std::uint8_t gpio; /* at the end there will be no more document */ dev->document = false; // first check for document event gl646_gpio_read(dev->interface->get_usb_device(), &gpio); DBG(DBG_info, "%s: GPIO=0x%02x\n", __func__, gpio); // test status : paper event + HOMESNR -> no more doc ? auto status = scanner_read_status(*dev); // home sensor is set when document is inserted if (status.is_at_home) { dev->document = false; DBG(DBG_info, "%s: no more document to eject\n", __func__); return; } // there is a document inserted, eject it dev->interface->write_register(0x01, 0xb0); /* wait for motor to stop */ do { dev->interface->sleep_ms(200); status = scanner_read_status(*dev); } while (status.is_motor_enabled); /* set up to fast move before scan then move until document is detected */ regs.init_reg(0x01, 0xb0); /* AGOME, 2 slopes motor moving , eject 'backward' */ regs.init_reg(0x02, 0x5d); /* motor feeding steps to 119880 */ regs.init_reg(0x3d, 1); regs.init_reg(0x3e, 0xd4); regs.init_reg(0x3f, 0x48); /* 60 fast moving steps */ regs.init_reg(0x6b, 60); /* set GPO */ regs.init_reg(0x66, 0x30); /* stesp NO */ regs.init_reg(0x21, 4); regs.init_reg(0x22, 1); regs.init_reg(0x23, 1); regs.init_reg(0x24, 4); /* generate slope table 2 */ auto slope_table = create_slope_table_for_speed(MotorSlope::create_from_steps(10000, 1600, 60), 1600, StepType::FULL, 1, 4, get_slope_table_max_size(AsicType::GL646)); // document eject: // send regs // start motor // wait c1 status to become c8 : HOMESNR and ~MOTFLAG // FIXME: sensor is not used. const auto& sensor = sanei_genesys_find_sensor_any(dev); scanner_send_slope_table(dev, sensor, 1, slope_table.table); dev->interface->write_registers(regs); scanner_start_action(*dev, true); /* loop until paper sensor tells paper is out, and till motor is running */ /* use a 30 timeout */ count = 0; do { status = scanner_read_status(*dev); dev->interface->sleep_ms(200); count++; } while (!status.is_at_home && (count < 150)); // read GPIO on exit gl646_gpio_read(dev->interface->get_usb_device(), &gpio); DBG(DBG_info, "%s: GPIO=0x%02x\n", __func__, gpio); } // Send the low-level scan command void CommandSetGl646::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, bool start_motor) const { DBG_HELPER(dbg); (void) sensor; // FIXME: SEQUENTIAL not really needed in this case Genesys_Register_Set local_reg(Genesys_Register_Set::SEQUENTIAL); local_reg.init_reg(0x03, reg->get8(0x03)); local_reg.init_reg(0x01, reg->get8(0x01) | REG_0x01_SCAN); if (start_motor) { local_reg.init_reg(0x0f, 0x01); } else { local_reg.init_reg(0x0f, 0x00); // do not start motor yet } dev->interface->write_registers(local_reg); dev->advance_head_pos_by_session(ScanHeadId::PRIMARY); } // Send the stop scan command static void end_scan_impl(Genesys_Device* dev, Genesys_Register_Set* reg, bool check_stop, bool eject) { DBG_HELPER_ARGS(dbg, "check_stop = %d, eject = %d", check_stop, eject); scanner_stop_action_no_move(*dev, *reg); unsigned wait_limit_seconds = 30; /* for sheetfed scanners, we may have to eject document */ if (dev->model->is_sheetfed) { if (eject && dev->document) { dev->cmd_set->eject_document(dev); } wait_limit_seconds = 3; } if (is_testing_mode()) { return; } dev->interface->sleep_ms(100); if (check_stop) { for (unsigned i = 0; i < wait_limit_seconds * 10; i++) { if (scanner_is_motor_stopped(*dev)) { return; } dev->interface->sleep_ms(100); } throw SaneException(SANE_STATUS_IO_ERROR, "could not stop motor"); } } // Send the stop scan command void CommandSetGl646::end_scan(Genesys_Device* dev, Genesys_Register_Set* reg, bool check_stop) const { end_scan_impl(dev, reg, check_stop, false); } /** * parks head * @param dev scanner's device * @param wait_until_home true if the function waits until head parked */ void CommandSetGl646::move_back_home(Genesys_Device* dev, bool wait_until_home) const { DBG_HELPER_ARGS(dbg, "wait_until_home = %d\n", wait_until_home); int i; int loop = 0; auto status = scanner_read_status(*dev); if (status.is_at_home) { DBG(DBG_info, "%s: end since already at home\n", __func__); dev->set_head_pos_zero(ScanHeadId::PRIMARY); return; } /* stop motor if needed */ if (status.is_motor_enabled) { gl646_stop_motor(dev); dev->interface->sleep_ms(200); } /* when scanhead is moving then wait until scanhead stops or timeout */ DBG(DBG_info, "%s: ensuring that motor is off\n", __func__); for (i = 400; i > 0; i--) { // do not wait longer than 40 seconds, count down to get i = 0 when busy status = scanner_read_status(*dev); if (!status.is_motor_enabled && status.is_at_home) { DBG(DBG_info, "%s: already at home and not moving\n", __func__); dev->set_head_pos_zero(ScanHeadId::PRIMARY); return; } if (!status.is_motor_enabled) { break; } dev->interface->sleep_ms(100); } if (!i) /* the loop counted down to 0, scanner still is busy */ { dev->set_head_pos_unknown(ScanHeadId::PRIMARY | ScanHeadId::SECONDARY); throw SaneException(SANE_STATUS_DEVICE_BUSY, "motor is still on: device busy"); } // setup for a backward scan of 65535 steps, with no actual data reading auto resolution = sanei_genesys_get_lowest_dpi(dev); const auto& sensor = sanei_genesys_find_sensor(dev, resolution, 3, dev->model->default_method); ScanSession session; session.params.xres = resolution; session.params.yres = resolution; session.params.startx = 0; session.params.starty = 65535; session.params.pixels = 600; session.params.lines = 1; session.params.depth = 8; session.params.channels = 3; session.params.scan_method = dev->model->default_method; session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; session.params.color_filter = ColorFilter::RED; session.params.contrast_adjustment = dev->settings.contrast; session.params.brightness_adjustment = dev->settings.brightness; session.params.flags = ScanFlag::REVERSE | ScanFlag::AUTO_GO_HOME | ScanFlag::DISABLE_GAMMA; if (dev->model->default_method == ScanMethod::TRANSPARENCY) { session.params.flags |= ScanFlag::USE_XPA; } compute_session(dev, session, sensor); init_regs_for_scan_session(dev, sensor, &dev->reg, session); /* backward , no actual data scanned TODO more setup flags to avoid this register manipulations ? */ regs_set_optical_off(dev->model->asic_type, dev->reg); // sets frontend gl646_set_fe(dev, sensor, AFE_SET, resolution); /* write scan registers */ try { dev->interface->write_registers(dev->reg); } catch (...) { DBG(DBG_error, "%s: failed to bulk write registers\n", __func__); } /* registers are restored to an iddl state, give up if no head to park */ if (dev->model->is_sheetfed) { return; } // starts scan { // this is effectively the same as dev->cmd_set->begin_scan(dev, sensor, &dev->reg, true); // except that we don't modify the head position calculations // FIXME: SEQUENTIAL not really needed in this case Genesys_Register_Set scan_local_reg(Genesys_Register_Set::SEQUENTIAL); scan_local_reg.init_reg(0x03, dev->reg.get8(0x03)); scan_local_reg.init_reg(0x01, dev->reg.get8(0x01) | REG_0x01_SCAN); scan_local_reg.init_reg(0x0f, 0x01); dev->interface->write_registers(scan_local_reg); } if (is_testing_mode()) { dev->interface->test_checkpoint("move_back_home"); dev->set_head_pos_zero(ScanHeadId::PRIMARY); return; } /* loop until head parked */ if (wait_until_home) { while (loop < 300) /* do not wait longer then 30 seconds */ { auto status = scanner_read_status(*dev); if (status.is_at_home) { DBG(DBG_info, "%s: reached home position\n", __func__); dev->interface->sleep_ms(500); dev->set_head_pos_zero(ScanHeadId::PRIMARY); return; } dev->interface->sleep_ms(100); ++loop; } // when we come here then the scanner needed too much time for this, so we better // stop the motor catch_all_exceptions(__func__, [&](){ gl646_stop_motor (dev); }); catch_all_exceptions(__func__, [&](){ end_scan_impl(dev, &dev->reg, true, false); }); dev->set_head_pos_unknown(ScanHeadId::PRIMARY | ScanHeadId::SECONDARY); throw SaneException(SANE_STATUS_IO_ERROR, "timeout while waiting for scanhead to go home"); } DBG(DBG_info, "%s: scanhead is still moving\n", __func__); } /** * init registers for shading calibration * we assume that scanner's head is on an area suiting shading calibration. * We scan a full scan width area by the shading line number for the device */ void CommandSetGl646::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { DBG_HELPER(dbg); (void) regs; /* fill settings for scan : always a color scan */ int channels = 3; unsigned cksel = get_cksel(dev->model->sensor_id, dev->settings.xres, channels); unsigned resolution = sensor.get_optical_resolution() / cksel; // FIXME: we select wrong calibration sensor const auto& calib_sensor = sanei_genesys_find_sensor(dev, dev->settings.xres, channels, dev->settings.scan_method); auto pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; unsigned calib_lines = static_cast(dev->model->y_size_calib_mm * resolution / MM_PER_INCH); ScanSession session; session.params.xres = resolution; session.params.yres = resolution; session.params.startx = 0; session.params.starty = 0; session.params.pixels = pixels; session.params.lines = calib_lines; session.params.depth = 16; session.params.channels = channels; session.params.scan_method = dev->settings.scan_method; session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; session.params.color_filter = dev->settings.color_filter; session.params.contrast_adjustment = dev->settings.contrast; session.params.brightness_adjustment = dev->settings.brightness; session.params.flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::IGNORE_COLOR_OFFSET | ScanFlag::IGNORE_STAGGER_OFFSET; if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) { session.params.flags |= ScanFlag::USE_XPA; } compute_session(dev, session, calib_sensor); dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session); dev->calib_session = session; /* no shading */ dev->reg.find_reg(0x02).value |= REG_0x02_ACDCDIS; /* ease backtracking */ dev->reg.find_reg(0x02).value &= ~REG_0x02_FASTFED; sanei_genesys_set_motor_power(dev->reg, false); } bool CommandSetGl646::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const { return dev->is_head_pos_known(ScanHeadId::PRIMARY) && dev->head_pos(ScanHeadId::PRIMARY) && dev->settings.scan_method == ScanMethod::FLATBED; } /** * this function send gamma table to ASIC */ void CommandSetGl646::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const { DBG_HELPER(dbg); int size; int address; int bits; if (has_flag(dev->model->flags, ModelFlag::GAMMA_14BIT)) { size = 16384; bits = 14; } else { size = 4096; bits = 12; } auto gamma = generate_gamma_buffer(dev, sensor, bits, size-1, size); /* table address */ switch (dev->reg.find_reg(0x05).value >> 6) { case 0: /* 600 dpi */ address = 0x09000; break; case 1: /* 1200 dpi */ address = 0x11000; break; case 2: /* 2400 dpi */ address = 0x20000; break; default: throw SaneException("invalid dpi"); } dev->interface->write_buffer(0x3c, address, gamma.data(), size * 2 * 3); } /** @brief this function does the led calibration. * this function does the led calibration by scanning one line of the calibration * area below scanner's top on white strip. The scope of this function is * currently limited to the XP200 */ SensorExposure CommandSetGl646::led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { DBG_HELPER(dbg); (void) regs; unsigned int i, j; int val; int avg[3], avga, avge; int turn; std::uint16_t expr, expg, expb; unsigned channels = dev->settings.get_channels(); ScanColorMode scan_mode = ScanColorMode::COLOR_SINGLE_PASS; if (dev->settings.scan_mode != ScanColorMode::COLOR_SINGLE_PASS) { scan_mode = ScanColorMode::GRAY; } // offset calibration is always done in color mode unsigned pixels = dev->model->x_size_calib_mm * sensor.full_resolution / MM_PER_INCH; ScanSession session; session.params.xres = sensor.full_resolution; session.params.yres = sensor.full_resolution; session.params.startx = 0; session.params.starty = 0; session.params.pixels = pixels; session.params.lines = 1; session.params.depth = 16; session.params.channels = channels; session.params.scan_method = dev->settings.scan_method; session.params.scan_mode = scan_mode; session.params.color_filter = ColorFilter::RED; session.params.contrast_adjustment = dev->settings.contrast; session.params.brightness_adjustment = dev->settings.brightness; session.params.flags = ScanFlag::DISABLE_SHADING; if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) { session.params.flags |= ScanFlag::USE_XPA; } compute_session(dev, session, sensor); // colors * bytes_per_color * scan lines unsigned total_size = pixels * channels * 2 * 1; std::vector line(total_size); /* we try to get equal bright leds here: loop: average per color adjust exposure times */ expr = sensor.exposure.red; expg = sensor.exposure.green; expb = sensor.exposure.blue; turn = 0; auto calib_sensor = sensor; bool acceptable = false; do { calib_sensor.exposure.red = expr; calib_sensor.exposure.green = expg; calib_sensor.exposure.blue = expb; DBG(DBG_info, "%s: starting first line reading\n", __func__); dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session); simple_scan(dev, calib_sensor, session, false, line, "led_calibration"); if (is_testing_mode()) { return calib_sensor.exposure; } if (dbg_log_image_data()) { char fn[30]; std::snprintf(fn, 30, "gl646_led_%02d.tiff", turn); write_tiff_file(fn, line.data(), 16, channels, pixels, 1); } acceptable = true; for (j = 0; j < channels; j++) { avg[j] = 0; for (i = 0; i < pixels; i++) { if (dev->model->is_cis) { val = line[i * 2 + j * 2 * pixels + 1] * 256 + line[i * 2 + j * 2 * pixels]; } else { val = line[i * 2 * channels + 2 * j + 1] * 256 + line[i * 2 * channels + 2 * j]; } avg[j] += val; } avg[j] /= pixels; } DBG(DBG_info, "%s: average: %d,%d,%d\n", __func__, avg[0], avg[1], avg[2]); acceptable = true; if (!acceptable) { avga = (avg[0] + avg[1] + avg[2]) / 3; expr = (expr * avga) / avg[0]; expg = (expg * avga) / avg[1]; expb = (expb * avga) / avg[2]; /* keep exposure time in a working window */ avge = (expr + expg + expb) / 3; if (avge > 0x2000) { expr = (expr * 0x2000) / avge; expg = (expg * 0x2000) / avge; expb = (expb * 0x2000) / avge; } if (avge < 0x400) { expr = (expr * 0x400) / avge; expg = (expg * 0x400) / avge; expb = (expb * 0x400) / avge; } } turn++; } while (!acceptable && turn < 100); DBG(DBG_info,"%s: acceptable exposure: 0x%04x,0x%04x,0x%04x\n", __func__, expr, expg, expb); // BUG: we don't store the result of the last iteration to the sensor return calib_sensor.exposure; } /** * average dark pixels of a scan */ static int dark_average(std::uint8_t * data, unsigned int pixels, unsigned int lines, unsigned int channels, unsigned int black) { unsigned int i, j, k, average, count; unsigned int avg[3]; std::uint8_t val; /* computes average value on black margin */ for (k = 0; k < channels; k++) { avg[k] = 0; count = 0; for (i = 0; i < lines; i++) { for (j = 0; j < black; j++) { val = data[i * channels * pixels + j + k]; avg[k] += val; count++; } } if (count) avg[k] /= count; DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, k, avg[k]); } average = 0; for (i = 0; i < channels; i++) average += avg[i]; average /= channels; DBG(DBG_info, "%s: average = %d\n", __func__, average); return average; } /** @brief calibration for AD frontend devices * we do simple scan until all black_pixels are higher than 0, * raising offset at each turn. */ static void ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor) { DBG_HELPER(dbg); (void) sensor; unsigned int channels; int pass = 0; unsigned adr, min; unsigned int bottom, black_pixels; channels = 3; // FIXME: maybe reuse `sensor` const auto& calib_sensor = sanei_genesys_find_sensor(dev, sensor.full_resolution, 3, ScanMethod::FLATBED); black_pixels = (calib_sensor.black_pixels * sensor.full_resolution) / calib_sensor.full_resolution; unsigned pixels = dev->model->x_size_calib_mm * sensor.full_resolution / MM_PER_INCH; unsigned lines = CALIBRATION_LINES; if (dev->model->is_cis) { lines = ((lines + 2) / 3) * 3; } ScanSession session; session.params.xres = sensor.full_resolution; session.params.yres = sensor.full_resolution; session.params.startx = 0; session.params.starty = 0; session.params.pixels = pixels; session.params.lines = lines; session.params.depth = 8; session.params.channels = 3; session.params.scan_method = dev->settings.scan_method; session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; session.params.color_filter = ColorFilter::RED; session.params.contrast_adjustment = dev->settings.contrast; session.params.brightness_adjustment = dev->settings.brightness; session.params.flags = ScanFlag::DISABLE_SHADING; if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) { session.params.flags |= ScanFlag::USE_XPA; } compute_session(dev, session, calib_sensor); /* scan first line of data with no gain */ dev->frontend.set_gain(0, 0); dev->frontend.set_gain(1, 0); dev->frontend.set_gain(2, 0); std::vector line; /* scan with no move */ bottom = 1; do { pass++; dev->frontend.set_offset(0, bottom); dev->frontend.set_offset(1, bottom); dev->frontend.set_offset(2, bottom); dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session); simple_scan(dev, calib_sensor, session, false, line, "ad_fe_offset_calibration"); if (is_testing_mode()) { return; } if (dbg_log_image_data()) { char title[30]; std::snprintf(title, 30, "gl646_offset%03d.tiff", static_cast(bottom)); write_tiff_file(title, line.data(), 8, channels, pixels, lines); } min = 0; for (unsigned y = 0; y < lines; y++) { for (unsigned x = 0; x < black_pixels; x++) { adr = (x + y * pixels) * channels; if (line[adr] > min) min = line[adr]; if (line[adr + 1] > min) min = line[adr + 1]; if (line[adr + 2] > min) min = line[adr + 2]; } } DBG(DBG_info, "%s: pass=%d, min=%d\n", __func__, pass, min); bottom++; } while (pass < 128 && min == 0); if (pass == 128) { throw SaneException(SANE_STATUS_INVAL, "failed to find correct offset"); } DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__, dev->frontend.get_offset(0), dev->frontend.get_offset(1), dev->frontend.get_offset(2)); } /** * This function does the offset calibration by scanning one line of the calibration * area below scanner's top. There is a black margin and the remaining is white. * genesys_search_start() must have been called so that the offsets and margins * are already known. * @param dev scanner's device */ void CommandSetGl646::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { DBG_HELPER(dbg); (void) regs; int pass = 0, avg; int topavg, bottomavg; int top, bottom, black_pixels; if (dev->model->adc_id == AdcId::AD_XP200) { ad_fe_offset_calibration(dev, sensor); return; } /* setup for a RGB scan, one full sensor's width line */ /* resolution is the one from the final scan */ unsigned resolution = dev->settings.xres; unsigned channels = 3; const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, ScanMethod::FLATBED); black_pixels = (calib_sensor.black_pixels * resolution) / calib_sensor.full_resolution; unsigned pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; unsigned lines = CALIBRATION_LINES; if (dev->model->is_cis) { lines = ((lines + 2) / 3) * 3; } ScanSession session; session.params.xres = resolution; session.params.yres = resolution; session.params.startx = 0; session.params.starty = 0; session.params.pixels = pixels; session.params.lines = lines; session.params.depth = 8; session.params.channels = channels; session.params.scan_method = dev->settings.scan_method; session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; session.params.color_filter = ColorFilter::RED; session.params.contrast_adjustment = dev->settings.contrast; session.params.brightness_adjustment = dev->settings.brightness; session.params.flags = ScanFlag::DISABLE_SHADING; if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) { session.params.flags |= ScanFlag::USE_XPA; } compute_session(dev, session, sensor); /* scan first line of data with no gain, but with offset from * last calibration */ dev->frontend.set_gain(0, 0); dev->frontend.set_gain(1, 0); dev->frontend.set_gain(2, 0); /* scan with no move */ bottom = 90; dev->frontend.set_offset(0, bottom); dev->frontend.set_offset(1, bottom); dev->frontend.set_offset(2, bottom); std::vector first_line, second_line; dev->cmd_set->init_regs_for_scan_session(dev, sensor, &dev->reg, session); simple_scan(dev, calib_sensor, session, false, first_line, "offset_first_line"); if (dbg_log_image_data()) { char title[30]; std::snprintf(title, 30, "gl646_offset%03d.tiff", bottom); write_tiff_file(title, first_line.data(), 8, channels, pixels, lines); } bottomavg = dark_average(first_line.data(), pixels, lines, channels, black_pixels); DBG(DBG_info, "%s: bottom avg=%d\n", __func__, bottomavg); /* now top value */ top = 231; dev->frontend.set_offset(0, top); dev->frontend.set_offset(1, top); dev->frontend.set_offset(2, top); dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session); simple_scan(dev, calib_sensor, session, false, second_line, "offset_second_line"); if (dbg_log_image_data()) { char title[30]; std::snprintf(title, 30, "gl646_offset%03d.tiff", top); write_tiff_file(title, second_line.data(), 8, channels, pixels, lines); } topavg = dark_average(second_line.data(), pixels, lines, channels, black_pixels); DBG(DBG_info, "%s: top avg=%d\n", __func__, topavg); if (is_testing_mode()) { return; } /* loop until acceptable level */ while ((pass < 32) && (top - bottom > 1)) { pass++; /* settings for new scan */ dev->frontend.set_offset(0, (top + bottom) / 2); dev->frontend.set_offset(1, (top + bottom) / 2); dev->frontend.set_offset(2, (top + bottom) / 2); // scan with no move dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session); simple_scan(dev, calib_sensor, session, false, second_line, "offset_calibration_i"); if (dbg_log_image_data()) { char title[30]; std::snprintf(title, 30, "gl646_offset%03d.tiff", dev->frontend.get_offset(1)); write_tiff_file(title, second_line.data(), 8, channels, pixels, lines); } avg = dark_average(second_line.data(), pixels, lines, channels, black_pixels); DBG(DBG_info, "%s: avg=%d offset=%d\n", __func__, avg, dev->frontend.get_offset(1)); /* compute new boundaries */ if (topavg == avg) { topavg = avg; top = dev->frontend.get_offset(1); } else { bottomavg = avg; bottom = dev->frontend.get_offset(1); } } DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__, dev->frontend.get_offset(0), dev->frontend.get_offset(1), dev->frontend.get_offset(2)); } /** * Alternative coarse gain calibration * this on uses the settings from offset_calibration. First scan moves so * we can go to calibration area for XPA. * @param dev device for scan * @param dpi resolutnio to calibrate at */ void CommandSetGl646::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs, int dpi) const { DBG_HELPER(dbg); (void) dpi; (void) sensor; (void) regs; float average[3]; char title[32]; /* setup for a RGB scan, one full sensor's width line */ /* resolution is the one from the final scan */ unsigned channels = 3; // BUG: the following comment is incorrect // we are searching a sensor resolution */ const auto& calib_sensor = sanei_genesys_find_sensor(dev, dev->settings.xres, channels, ScanMethod::FLATBED); unsigned pixels = 0; float start = 0; if (dev->settings.scan_method == ScanMethod::FLATBED) { pixels = dev->model->x_size_calib_mm * dev->settings.xres / MM_PER_INCH; } else { start = dev->model->x_offset_ta; pixels = static_cast( (dev->model->x_size_ta * dev->settings.xres) / MM_PER_INCH); } unsigned lines = CALIBRATION_LINES; // round up to multiple of 3 in case of CIS scanner if (dev->model->is_cis) { lines = ((lines + 2) / 3) * 3; } start = static_cast((start * dev->settings.xres) / MM_PER_INCH); ScanSession session; session.params.xres = dev->settings.xres; session.params.yres = dev->settings.xres; session.params.startx = static_cast(start); session.params.starty = 0; session.params.pixels = pixels; session.params.lines = lines; session.params.depth = 8; session.params.channels = channels; session.params.scan_method = dev->settings.scan_method; session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; session.params.color_filter = ColorFilter::RED; session.params.contrast_adjustment = dev->settings.contrast; session.params.brightness_adjustment = dev->settings.brightness; session.params.flags = ScanFlag::DISABLE_SHADING; if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) { session.params.flags |= ScanFlag::USE_XPA; } compute_session(dev, session, calib_sensor); /* start gain value */ dev->frontend.set_gain(0, 1); dev->frontend.set_gain(1, 1); dev->frontend.set_gain(2, 1); average[0] = 0; average[1] = 0; average[2] = 0; unsigned pass = 0; std::vector line; /* loop until each channel raises to acceptable level */ while (((average[0] < calib_sensor.gain_white_ref) || (average[1] < calib_sensor.gain_white_ref) || (average[2] < calib_sensor.gain_white_ref)) && (pass < 30)) { // scan with no move dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session); simple_scan(dev, calib_sensor, session, false, line, "coarse_gain_calibration"); if (dbg_log_image_data()) { std::sprintf(title, "gl646_gain%02d.tiff", pass); write_tiff_file(title, line.data(), 8, channels, pixels, lines); } pass++; // average high level for each channel and compute gain to reach the target code // we only use the central half of the CCD data for (unsigned k = 0; k < channels; k++) { // we find the maximum white value, so we can deduce a threshold // to average white values unsigned maximum = 0; for (unsigned i = 0; i < lines; i++) { for (unsigned j = 0; j < pixels; j++) { unsigned val = line[i * channels * pixels + j + k]; maximum = std::max(maximum, val); } } maximum = static_cast(maximum * 0.9); // computes white average average[k] = 0; unsigned count = 0; for (unsigned i = 0; i < lines; i++) { for (unsigned j = 0; j < pixels; j++) { // averaging only white points allow us not to care about dark margins unsigned val = line[i * channels * pixels + j + k]; if (val > maximum) { average[k] += val; count++; } } } average[k] = average[k] / count; // adjusts gain for the channel if (average[k] < calib_sensor.gain_white_ref) { dev->frontend.set_gain(k, dev->frontend.get_gain(k) + 1); } DBG(DBG_info, "%s: channel %d, average = %.2f, gain = %d\n", __func__, k, average[k], dev->frontend.get_gain(k)); } } DBG(DBG_info, "%s: gains=(%d,%d,%d)\n", __func__, dev->frontend.get_gain(0), dev->frontend.get_gain(1), dev->frontend.get_gain(2)); } /** * sets up the scanner's register for warming up. We scan 2 lines without moving. * */ void CommandSetGl646::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* local_reg) const { DBG_HELPER(dbg); (void) sensor; dev->frontend = dev->frontend_initial; unsigned resolution = 300; const auto& local_sensor = sanei_genesys_find_sensor(dev, resolution, 1, dev->settings.scan_method); // set up for a full width 2 lines gray scan without moving unsigned pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; ScanSession session; session.params.xres = resolution; session.params.yres = resolution; session.params.startx = 0; session.params.starty = 0; session.params.pixels = pixels; session.params.lines = 2; session.params.depth = dev->model->bpp_gray_values.front(); session.params.channels = 1; session.params.scan_method = dev->settings.scan_method; session.params.scan_mode = ScanColorMode::GRAY; session.params.color_filter = ColorFilter::RED; session.params.contrast_adjustment = 0; session.params.brightness_adjustment = 0; session.params.flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA; if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) { session.params.flags |= ScanFlag::USE_XPA; } compute_session(dev, session, local_sensor); dev->cmd_set->init_regs_for_scan_session(dev, local_sensor, &dev->reg, session); /* we are not going to move, so clear these bits */ dev->reg.find_reg(0x02).value &= ~REG_0x02_FASTFED; /* copy to local_reg */ *local_reg = dev->reg; /* turn off motor during this scan */ sanei_genesys_set_motor_power(*local_reg, false); // now registers are ok, write them to scanner gl646_set_fe(dev, local_sensor, AFE_SET, session.params.xres); } /* * * initialize ASIC : registers, motor tables, and gamma tables * then ensure scanner's head is at home * @param dev device description of the scanner to initialize */ void CommandSetGl646::init(Genesys_Device* dev) const { DBG_INIT(); DBG_HELPER(dbg); std::uint8_t val = 0; std::uint32_t addr = 0xdead; size_t len; // to detect real power up condition, we write to REG_0x41 with pwrbit set, then read it back. // When scanner is cold (just replugged) PWRBIT will be set in the returned value auto status = scanner_read_status(*dev); if (status.is_replugged) { DBG(DBG_info, "%s: device is cold\n", __func__); } else { DBG(DBG_info, "%s: device is hot\n", __func__); } const auto& sensor = sanei_genesys_find_sensor_any(dev); /* if scanning session hasn't been initialized, set it up */ if (!dev->already_initialized) { dev->dark_average_data.clear(); dev->white_average_data.clear(); dev->settings.color_filter = ColorFilter::GREEN; /* Set default values for registers */ gl646_init_regs (dev); // Init shading data sanei_genesys_init_shading_data(dev, sensor, dev->model->x_size_calib_mm * sensor.full_resolution / MM_PER_INCH); dev->initial_regs = dev->reg; } // execute physical unit init only if cold if (status.is_replugged) { DBG(DBG_info, "%s: device is cold\n", __func__); val = 0x04; dev->interface->get_usb_device().control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_INIT, INDEX, 1, &val); // ASIC reset dev->interface->write_register(0x0e, 0x00); dev->interface->sleep_ms(100); // Write initial registers dev->interface->write_registers(dev->reg); // send gamma tables if needed dev->cmd_set->send_gamma_table(dev, sensor); // Set powersaving(default = 15 minutes) dev->cmd_set->set_powersaving(dev, 15); } // Set analog frontend gl646_set_fe(dev, sensor, AFE_INIT, 0); /* GPO enabling for XP200 */ if (dev->model->sensor_id == SensorId::CIS_XP200) { dev->interface->write_register(0x68, dev->gpo.regs.get_value(0x68)); dev->interface->write_register(0x69, dev->gpo.regs.get_value(0x69)); // enable GPIO gl646_gpio_output_enable(dev->interface->get_usb_device(), 6); // writes 0 to GPIO gl646_gpio_write(dev->interface->get_usb_device(), 0); // clear GPIO enable gl646_gpio_output_enable(dev->interface->get_usb_device(), 0); dev->interface->write_register(0x66, 0x10); dev->interface->write_register(0x66, 0x00); dev->interface->write_register(0x66, 0x10); } /* MD6471/G2410 and XP200 read/write data from an undocumented memory area which * is after the second slope table */ if (dev->model->gpio_id != GpioId::HP3670 && dev->model->gpio_id != GpioId::HP2400) { switch (sensor.full_resolution) { case 600: addr = 0x08200; break; case 1200: addr = 0x10200; break; case 2400: addr = 0x1fa00; break; } sanei_genesys_set_buffer_address(dev, addr); sanei_usb_set_timeout (2 * 1000); len = 6; // for some reason, read fails here for MD6471, HP2300 and XP200 one time out of // 2 scanimage launches try { dev->interface->bulk_read_data(0x45, dev->control, len); } catch (...) { dev->interface->bulk_read_data(0x45, dev->control, len); } sanei_usb_set_timeout (30 * 1000); } else /* HP2400 and HP3670 case */ { dev->control[0] = 0x00; dev->control[1] = 0x00; dev->control[2] = 0x01; dev->control[3] = 0x00; dev->control[4] = 0x00; dev->control[5] = 0x00; } /* ensure head is correctly parked, and check lock */ if (!dev->model->is_sheetfed) { move_back_home(dev, true); } /* here session and device are initialized */ dev->already_initialized = true; } static void simple_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, const ScanSession& session, bool move, std::vector& data, const char* scan_identifier) { unsigned lines = session.output_line_count; if (!dev->model->is_cis) { lines++; } std::size_t size = lines * session.params.pixels; unsigned bpp = session.params.depth == 16 ? 2 : 1; size *= bpp * session.params.channels; data.clear(); data.resize(size); // initialize frontend gl646_set_fe(dev, sensor, AFE_SET, session.params.xres); // no watch dog for simple scan dev->reg.find_reg(0x01).value &= ~REG_0x01_DOGENB; /* one table movement for simple scan */ dev->reg.find_reg(0x02).value &= ~REG_0x02_FASTFED; if (!move) { sanei_genesys_set_motor_power(dev->reg, false); } /* no automatic go home when using XPA */ if (session.params.scan_method == ScanMethod::TRANSPARENCY) { dev->reg.find_reg(0x02).value &= ~REG_0x02_AGOHOME; } // write scan registers dev->interface->write_registers(dev->reg); // starts scan dev->cmd_set->begin_scan(dev, sensor, &dev->reg, move); if (is_testing_mode()) { dev->interface->test_checkpoint(scan_identifier); return; } wait_until_buffer_non_empty(dev, true); // now we're on target, we can read data sanei_genesys_read_data_from_scanner(dev, data.data(), size); /* in case of CIS scanner, we must reorder data */ if (dev->model->is_cis && session.params.scan_mode == ScanColorMode::COLOR_SINGLE_PASS) { auto pixels_count = session.params.pixels; std::vector buffer(pixels_count * 3 * bpp); if (bpp == 1) { for (unsigned y = 0; y < lines; y++) { // reorder line for (unsigned x = 0; x < pixels_count; x++) { buffer[x * 3] = data[y * pixels_count * 3 + x]; buffer[x * 3 + 1] = data[y * pixels_count * 3 + pixels_count + x]; buffer[x * 3 + 2] = data[y * pixels_count * 3 + 2 * pixels_count + x]; } // copy line back std::memcpy(data.data() + pixels_count * 3 * y, buffer.data(), pixels_count * 3); } } else { for (unsigned y = 0; y < lines; y++) { // reorder line auto pixels_count = session.params.pixels; for (unsigned x = 0; x < pixels_count; x++) { buffer[x * 6] = data[y * pixels_count * 6 + x * 2]; buffer[x * 6 + 1] = data[y * pixels_count * 6 + x * 2 + 1]; buffer[x * 6 + 2] = data[y * pixels_count * 6 + 2 * pixels_count + x * 2]; buffer[x * 6 + 3] = data[y * pixels_count * 6 + 2 * pixels_count + x * 2 + 1]; buffer[x * 6 + 4] = data[y * pixels_count * 6 + 4 * pixels_count + x * 2]; buffer[x * 6 + 5] = data[y * pixels_count * 6 + 4 * pixels_count + x * 2 + 1]; } // copy line back std::memcpy(data.data() + pixels_count * 6 * y, buffer.data(),pixels_count * 6); } } } // end scan , waiting the motor to stop if needed (if moving), but without ejecting doc end_scan_impl(dev, &dev->reg, true, false); } /** * update the status of the required sensor in the scanner session * the button fields are used to make events 'sticky' */ void CommandSetGl646::update_hardware_sensors(Genesys_Scanner* session) const { DBG_HELPER(dbg); Genesys_Device *dev = session->dev; std::uint8_t value; // do what is needed to get a new set of events, but try to not loose any of them. gl646_gpio_read(dev->interface->get_usb_device(), &value); DBG(DBG_io, "%s: GPIO=0x%02x\n", __func__, value); // scan button if (dev->model->buttons & GENESYS_HAS_SCAN_SW) { switch (dev->model->gpio_id) { case GpioId::XP200: session->buttons[BUTTON_SCAN_SW].write((value & 0x02) != 0); break; case GpioId::MD_5345: session->buttons[BUTTON_SCAN_SW].write(value == 0x16); break; case GpioId::HP2300: session->buttons[BUTTON_SCAN_SW].write(value == 0x6c); break; case GpioId::HP3670: case GpioId::HP2400: session->buttons[BUTTON_SCAN_SW].write((value & 0x20) == 0); break; default: throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type"); } } // email button if (dev->model->buttons & GENESYS_HAS_EMAIL_SW) { switch (dev->model->gpio_id) { case GpioId::MD_5345: session->buttons[BUTTON_EMAIL_SW].write(value == 0x12); break; case GpioId::HP3670: case GpioId::HP2400: session->buttons[BUTTON_EMAIL_SW].write((value & 0x08) == 0); break; default: throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type"); } } // copy button if (dev->model->buttons & GENESYS_HAS_COPY_SW) { switch (dev->model->gpio_id) { case GpioId::MD_5345: session->buttons[BUTTON_COPY_SW].write(value == 0x11); break; case GpioId::HP2300: session->buttons[BUTTON_COPY_SW].write(value == 0x5c); break; case GpioId::HP3670: case GpioId::HP2400: session->buttons[BUTTON_COPY_SW].write((value & 0x10) == 0); break; default: throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type"); } } // power button if (dev->model->buttons & GENESYS_HAS_POWER_SW) { switch (dev->model->gpio_id) { case GpioId::MD_5345: session->buttons[BUTTON_POWER_SW].write(value == 0x14); break; default: throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type"); } } // ocr button if (dev->model->buttons & GENESYS_HAS_OCR_SW) { switch (dev->model->gpio_id) { case GpioId::MD_5345: session->buttons[BUTTON_OCR_SW].write(value == 0x13); break; default: throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type"); } } // document detection if (dev->model->buttons & GENESYS_HAS_PAGE_LOADED_SW) { switch (dev->model->gpio_id) { case GpioId::XP200: session->buttons[BUTTON_PAGE_LOADED_SW].write((value & 0x04) != 0); break; default: throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type"); } } /* XPA detection */ if (dev->model->has_method(ScanMethod::TRANSPARENCY)) { switch (dev->model->gpio_id) { case GpioId::HP3670: case GpioId::HP2400: /* test if XPA is plugged-in */ if ((value & 0x40) == 0) { session->opt[OPT_SOURCE].cap &= ~SANE_CAP_INACTIVE; } else { session->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE; } break; default: throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type"); } } } void CommandSetGl646::update_home_sensor_gpio(Genesys_Device& dev) const { DBG_HELPER(dbg); (void) dev; } static void write_control(Genesys_Device* dev, const Genesys_Sensor& sensor, int resolution) { DBG_HELPER(dbg); std::uint8_t control[4]; std::uint32_t addr = 0xdead; /* 2300 does not write to 'control' */ if (dev->model->motor_id == MotorId::HP2300) { return; } /* MD6471/G2410/HP2300 and XP200 read/write data from an undocumented memory area which * is after the second slope table */ switch (sensor.full_resolution) { case 600: addr = 0x08200; break; case 1200: addr = 0x10200; break; case 2400: addr = 0x1fa00; break; default: throw SaneException("failed to compute control address"); } /* XP200 sets dpi, what other scanner put is unknown yet */ switch (dev->model->motor_id) { case MotorId::XP200: /* we put scan's dpi, not motor one */ control[0] = resolution & 0xff; control[1] = (resolution >> 8) & 0xff; control[2] = dev->control[4]; control[3] = dev->control[5]; break; case MotorId::HP3670: case MotorId::HP2400: case MotorId::MD_5345: default: control[0] = dev->control[2]; control[1] = dev->control[3]; control[2] = dev->control[4]; control[3] = dev->control[5]; break; } dev->interface->write_buffer(0x3c, addr, control, 4); } void CommandSetGl646::wait_for_motor_stop(Genesys_Device* dev) const { (void) dev; } void CommandSetGl646::send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t* data, int size) const { (void) dev; (void) sensor; (void) data; (void) size; throw SaneException("not implemented"); } ScanSession CommandSetGl646::calculate_scan_session(const Genesys_Device* dev, const Genesys_Sensor& sensor, const Genesys_Settings& settings) const { // compute distance to move float move = 0; if (!dev->model->is_sheetfed) { move = dev->model->y_offset; // add tl_y to base movement } move += settings.tl_y; if (move < 0) { DBG(DBG_error, "%s: overriding negative move value %f\n", __func__, move); move = 0; } move = static_cast((move * dev->motor.base_ydpi) / MM_PER_INCH); float start = settings.tl_x; if (settings.scan_method == ScanMethod::FLATBED) { start += dev->model->x_offset; } else { start += dev->model->x_offset_ta; } start = static_cast((start * settings.xres) / MM_PER_INCH); ScanSession session; session.params.xres = settings.xres; session.params.yres = settings.yres; session.params.startx = static_cast(start); session.params.starty = static_cast(move); session.params.pixels = settings.pixels; session.params.requested_pixels = settings.requested_pixels; session.params.lines = settings.lines; session.params.depth = settings.depth; session.params.channels = settings.get_channels(); session.params.scan_method = dev->settings.scan_method; session.params.scan_mode = settings.scan_mode; session.params.color_filter = settings.color_filter; session.params.contrast_adjustment = settings.contrast; session.params.brightness_adjustment = settings.brightness; session.params.flags = ScanFlag::AUTO_GO_HOME; if (settings.scan_method == ScanMethod::TRANSPARENCY) { session.params.flags |= ScanFlag::USE_XPA; } compute_session(dev, session, sensor); return session; } void CommandSetGl646::asic_boot(Genesys_Device *dev, bool cold) const { (void) dev; (void) cold; throw SaneException("not implemented"); } } // namespace gl646 } // namespace genesys backends-1.3.0/backend/genesys/gl646.h000066400000000000000000000100621456256263500173610ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2003-2004 Henning Meier-Geinitz Copyright (C) 2004-2005 Gerhard Jaeger Copyright (C) 2004-2013 Stéphane Voltz Copyright (C) 2005-2009 Pierre Willenbrock 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, see . */ #ifndef BACKEND_GENESYS_GL646_H #define BACKEND_GENESYS_GL646_H #include "genesys.h" #include "command_set_common.h" #include "motor.h" namespace genesys { namespace gl646 { class CommandSetGl646 : public CommandSetCommon { public: ~CommandSetGl646() override = default; bool needs_home_before_init_regs_for_scan(Genesys_Device* dev) const override; void init(Genesys_Device* dev) const override; void init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* regs) const override; void init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const override; void init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, const ScanSession& session) const override; void set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set) const override; void set_powersaving(Genesys_Device* dev, int delay) const override; void save_power(Genesys_Device* dev, bool enable) const override; void begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* regs, bool start_motor) const override; void end_scan(Genesys_Device* dev, Genesys_Register_Set* regs, bool check_stop) const override; void send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const override; void offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const override; void coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs, int dpi) const override; SensorExposure led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const override; void wait_for_motor_stop(Genesys_Device* dev) const override; void move_back_home(Genesys_Device* dev, bool wait_until_home) const override; void update_hardware_sensors(struct Genesys_Scanner* s) const override; void update_home_sensor_gpio(Genesys_Device& dev) const override; void load_document(Genesys_Device* dev) const override; void detect_document_end(Genesys_Device* dev) const override; void eject_document(Genesys_Device* dev) const override; void send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t* data, int size) const override; bool has_send_shading_data() const override { return false; } ScanSession calculate_scan_session(const Genesys_Device* dev, const Genesys_Sensor& sensor, const Genesys_Settings& settings) const override; void asic_boot(Genesys_Device* dev, bool cold) const override; }; } // namespace gl646 } // namespace genesys #endif // BACKEND_GENESYS_GL646_H backends-1.3.0/backend/genesys/gl646_registers.h000066400000000000000000000135321456256263500214550ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_GL646_REGISTERS_H #define BACKEND_GENESYS_GL646_REGISTERS_H #include namespace genesys { namespace gl646 { using RegAddr = std::uint16_t; using RegMask = std::uint8_t; using RegShift = unsigned; static constexpr RegAddr REG_0x01 = 0x01; static constexpr RegMask REG_0x01_CISSET = 0x80; static constexpr RegMask REG_0x01_DOGENB = 0x40; static constexpr RegMask REG_0x01_DVDSET = 0x20; static constexpr RegMask REG_0x01_FASTMOD = 0x10; static constexpr RegMask REG_0x01_COMPENB = 0x08; static constexpr RegMask REG_0x01_DRAMSEL = 0x04; static constexpr RegMask REG_0x01_SHDAREA = 0x02; static constexpr RegMask REG_0x01_SCAN = 0x01; static constexpr RegMask REG_0x02_NOTHOME = 0x80; static constexpr RegMask REG_0x02_ACDCDIS = 0x40; static constexpr RegMask REG_0x02_AGOHOME = 0x20; static constexpr RegMask REG_0x02_MTRPWR = 0x10; static constexpr RegMask REG_0x02_FASTFED = 0x08; static constexpr RegMask REG_0x02_MTRREV = 0x04; static constexpr RegMask REG_0x02_STEPSEL = 0x03; static constexpr RegMask REG_0x02_FULLSTEP = 0x00; static constexpr RegMask REG_0x02_HALFSTEP = 0x01; static constexpr RegMask REG_0x02_QUATERSTEP = 0x02; static constexpr RegMask REG_0x03_TG3 = 0x80; static constexpr RegMask REG_0x03_AVEENB = 0x40; static constexpr RegMask REG_0x03_XPASEL = 0x20; static constexpr RegMask REG_0x03_LAMPPWR = 0x10; static constexpr RegMask REG_0x03_LAMPDOG = 0x08; static constexpr RegMask REG_0x03_LAMPTIM = 0x07; static constexpr RegMask REG_0x04_LINEART = 0x80; static constexpr RegMask REG_0x04_BITSET = 0x40; static constexpr RegMask REG_0x04_ADTYPE = 0x30; static constexpr RegMask REG_0x04_FILTER = 0x0c; static constexpr RegMask REG_0x04_FESET = 0x03; static constexpr RegAddr REG_0x05 = 0x05; static constexpr RegMask REG_0x05_DPIHW = 0xc0; static constexpr RegMask REG_0x05_DPIHW_600 = 0x00; static constexpr RegMask REG_0x05_DPIHW_1200 = 0x40; static constexpr RegMask REG_0x05_DPIHW_2400 = 0x80; static constexpr RegMask REG_0x05_DPIHW_4800 = 0xc0; static constexpr RegMask REG_0x05_GMMTYPE = 0x30; static constexpr RegMask REG_0x05_GMM14BIT = 0x10; static constexpr RegMask REG_0x05_GMMENB = 0x08; static constexpr RegMask REG_0x05_LEDADD = 0x04; static constexpr RegMask REG_0x05_BASESEL = 0x03; static constexpr RegAddr REG_0x06 = 0x06; static constexpr RegMask REG_0x06_PWRBIT = 0x10; static constexpr RegMask REG_0x06_GAIN4 = 0x08; static constexpr RegMask REG_0x06_OPTEST = 0x07; static constexpr RegMask REG_0x07_DMASEL = 0x02; static constexpr RegMask REG_0x07_DMARDWR = 0x01; static constexpr RegMask REG_0x16_CTRLHI = 0x80; static constexpr RegMask REG_0x16_SELINV = 0x40; static constexpr RegMask REG_0x16_TGINV = 0x20; static constexpr RegMask REG_0x16_CK1INV = 0x10; static constexpr RegMask REG_0x16_CK2INV = 0x08; static constexpr RegMask REG_0x16_CTRLINV = 0x04; static constexpr RegMask REG_0x16_CKDIS = 0x02; static constexpr RegMask REG_0x16_CTRLDIS = 0x01; static constexpr RegMask REG_0x17_TGMODE = 0xc0; static constexpr RegMask REG_0x17_TGMODE_NO_DUMMY = 0x00; static constexpr RegMask REG_0x17_TGMODE_REF = 0x40; static constexpr RegMask REG_0x17_TGMODE_XPA = 0x80; static constexpr RegMask REG_0x17_TGW = 0x3f; static constexpr RegMask REG_0x18_CNSET = 0x80; static constexpr RegMask REG_0x18_DCKSEL = 0x60; static constexpr RegMask REG_0x18_CKTOGGLE = 0x10; static constexpr RegMask REG_0x18_CKDELAY = 0x0c; static constexpr RegMask REG_0x18_CKSEL = 0x03; static constexpr RegMask REG_0x1D_CKMANUAL = 0x80; static constexpr RegMask REG_0x1E_WDTIME = 0xf0; static constexpr RegMask REG_0x1E_LINESEL = 0x0f; static constexpr RegMask REG_0x41_PWRBIT = 0x80; static constexpr RegMask REG_0x41_BUFEMPTY = 0x40; static constexpr RegMask REG_0x41_FEEDFSH = 0x20; static constexpr RegMask REG_0x41_SCANFSH = 0x10; static constexpr RegMask REG_0x41_HOMESNR = 0x08; static constexpr RegMask REG_0x41_LAMPSTS = 0x04; static constexpr RegMask REG_0x41_FEBUSY = 0x02; static constexpr RegMask REG_0x41_MOTMFLG = 0x01; static constexpr RegMask REG_0x66_LOW_CURRENT = 0x10; static constexpr RegMask REG_0x6A_FSTPSEL = 0xc0; static constexpr RegMask REG_0x6A_FASTPWM = 0x3f; static constexpr RegMask REG_0x6C_TGTIME = 0xc0; static constexpr RegMask REG_0x6C_Z1MOD = 0x38; static constexpr RegMask REG_0x6C_Z2MOD = 0x07; static constexpr RegAddr REG_EXPR = 0x10; static constexpr RegAddr REG_EXPG = 0x12; static constexpr RegAddr REG_EXPB = 0x14; static constexpr RegAddr REG_SCANFED = 0x1f; static constexpr RegAddr REG_BUFSEL = 0x20; static constexpr RegAddr REG_LINCNT = 0x25; static constexpr RegAddr REG_DPISET = 0x2c; static constexpr RegAddr REG_STRPIXEL = 0x30; static constexpr RegAddr REG_ENDPIXEL = 0x32; static constexpr RegAddr REG_DUMMY = 0x34; static constexpr RegAddr REG_MAXWD = 0x35; static constexpr RegAddr REG_LPERIOD = 0x38; static constexpr RegAddr REG_FEEDL = 0x3d; static constexpr RegAddr REG_VALIDWORD = 0x42; static constexpr RegAddr REG_FEDCNT = 0x48; static constexpr RegAddr REG_SCANCNT = 0x4b; static constexpr RegAddr REG_Z1MOD = 0x60; static constexpr RegAddr REG_Z2MOD = 0x62; } // namespace gl646 } // namespace genesys #endif // BACKEND_GENESYS_GL646_REGISTERS_H backends-1.3.0/backend/genesys/gl841.cpp000066400000000000000000002315221456256263500177170ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2003 Oliver Rauch Copyright (C) 2003, 2004 Henning Meier-Geinitz Copyright (C) 2004 Gerhard Jaeger Copyright (C) 2004-2013 Stéphane Voltz Copyright (C) 2005 Philipp Schmid Copyright (C) 2005-2009 Pierre Willenbrock Copyright (C) 2006 Laurent Charpentier Copyright (C) 2010 Chris Berry and Michael Rickmann for Plustek Opticbook 3600 support 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, see . */ #define DEBUG_DECLARE_ONLY #include "gl841.h" #include "gl841_registers.h" #include "test_settings.h" #include namespace genesys { namespace gl841 { static int gl841_exposure_time(Genesys_Device *dev, const Genesys_Sensor& sensor, const MotorProfile& profile, float slope_dpi, int start, int used_pixels); /* * Set all registers to default values * (function called only once at the beginning) */ static void gl841_init_registers (Genesys_Device * dev) { DBG_HELPER(dbg); dev->reg.init_reg(0x01, 0x20); if (dev->model->is_cis) { dev->reg.find_reg(0x01).value |= REG_0x01_CISSET; } else { dev->reg.find_reg(0x01).value &= ~REG_0x01_CISSET; } if (dev->model->model_id == ModelId::CANON_LIDE_80) { dev->reg.init_reg(0x01, 0x82); } dev->reg.init_reg(0x02, 0x38); if (dev->model->model_id == ModelId::CANON_LIDE_80) { dev->reg.init_reg(0x02, 0x10); } dev->reg.init_reg(0x03, 0x5f); if (dev->model->model_id == ModelId::CANON_LIDE_80) { dev->reg.init_reg(0x03, 0x50); } dev->reg.init_reg(0x04, 0x10); if (dev->model->model_id == ModelId::PLUSTEK_OPTICPRO_3600) { dev->reg.init_reg(0x04, 0x22); } else if (dev->model->model_id == ModelId::CANON_LIDE_80) { dev->reg.init_reg(0x04, 0x02); } const auto& sensor = sanei_genesys_find_sensor_any(dev); dev->reg.init_reg(0x05, 0x00); // disable gamma, 24 clocks/pixel sanei_genesys_set_dpihw(dev->reg, sensor.register_dpihw); if (dev->model->model_id == ModelId::CANON_LIDE_80) { dev->reg.init_reg(0x05, 0x4c); } dev->reg.init_reg(0x06, 0x18); if (dev->model->model_id == ModelId::CANON_LIDE_80) { dev->reg.init_reg(0x06, 0x38); } if (dev->model->model_id == ModelId::VISIONEER_STROBE_XP300 || dev->model->model_id == ModelId::SYSCAN_DOCKETPORT_485 || dev->model->model_id == ModelId::DCT_DOCKETPORT_487 || dev->model->model_id == ModelId::SYSCAN_DOCKETPORT_685 || dev->model->model_id == ModelId::PLUSTEK_OPTICPRO_3600) { dev->reg.init_reg(0x06, 0xb8); } dev->reg.init_reg(0x07, 0x00); dev->reg.init_reg(0x08, 0x00); dev->reg.init_reg(0x09, 0x10); if (dev->model->model_id == ModelId::CANON_LIDE_80) { dev->reg.init_reg(0x09, 0x11); } if (dev->model->model_id == ModelId::VISIONEER_STROBE_XP300 || dev->model->model_id == ModelId::SYSCAN_DOCKETPORT_485 || dev->model->model_id == ModelId::DCT_DOCKETPORT_487 || dev->model->model_id == ModelId::SYSCAN_DOCKETPORT_685 || dev->model->model_id == ModelId::PLUSTEK_OPTICPRO_3600) { dev->reg.init_reg(0x09, 0x00); } dev->reg.init_reg(0x0a, 0x00); // EXPR[0:15], EXPG[0:15], EXPB[0:15]: Exposure time settings dev->reg.init_reg(0x10, 0x00); // SENSOR_DEF dev->reg.init_reg(0x11, 0x00); // SENSOR_DEF dev->reg.init_reg(0x12, 0x00); // SENSOR_DEF dev->reg.init_reg(0x13, 0x00); // SENSOR_DEF dev->reg.init_reg(0x14, 0x00); // SENSOR_DEF dev->reg.init_reg(0x15, 0x00); // SENSOR_DEF dev->reg.init_reg(0x16, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x17, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x18, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x19, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x1a, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x1b, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x1c, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x1d, 0x01); // SENSOR_DEF, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x1e, 0xf0); if (dev->model->model_id == ModelId::CANON_LIDE_80) { dev->reg.init_reg(0x1e, 0x10); } dev->reg.init_reg(0x1f, 0x01); if (dev->model->model_id == ModelId::CANON_LIDE_80) { dev->reg.init_reg(0x1f, 0x04); } dev->reg.init_reg(0x20, 0x20); dev->reg.init_reg(0x21, 0x01); dev->reg.init_reg(0x22, 0x01); dev->reg.init_reg(0x23, 0x01); dev->reg.init_reg(0x24, 0x01); dev->reg.init_reg(0x25, 0x00); dev->reg.init_reg(0x26, 0x00); dev->reg.init_reg(0x27, 0x00); dev->reg.init_reg(0x29, 0xff); dev->reg.init_reg(0x2c, 0x02); // DPISET: overwritten during scanner setup dev->reg.init_reg(0x2d, 0x58); // DPISET: overwritten during scanner setup dev->reg.init_reg(0x2e, 0x80); dev->reg.init_reg(0x2f, 0x80); dev->reg.init_reg(0x30, 0x00); // STRPIXEL: overwritten during scanner setup dev->reg.init_reg(0x31, 0x00); // STRPIXEL: overwritten during scanner setup dev->reg.init_reg(0x32, 0x00); // ENDPIXEL: overwritten during scanner setup dev->reg.init_reg(0x33, 0x00); // ENDPIXEL: overwritten during scanner setup dev->reg.init_reg(0x34, 0x00); // DUMMY: overwritten during scanner setup dev->reg.init_reg(0x35, 0x00); // MAXWD: overwritten during scanner setup dev->reg.init_reg(0x36, 0x00); // MAXWD: overwritten during scanner setup dev->reg.init_reg(0x37, 0x00); // MAXWD: overwritten during scanner setup dev->reg.init_reg(0x38, 0x4f); // LPERIOD: overwritten during scanner setup dev->reg.init_reg(0x39, 0xc1); // LPERIOD: overwritten during scanner setup dev->reg.init_reg(0x3d, 0x00); dev->reg.init_reg(0x3e, 0x00); dev->reg.init_reg(0x3f, 0x00); dev->reg.init_reg(0x52, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x53, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x54, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x55, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x56, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x57, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x58, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x59, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x5a, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below if (dev->model->model_id == ModelId::CANON_LIDE_80) { dev->reg.init_reg(0x5d, 0x20); dev->reg.init_reg(0x5e, 0x41); dev->reg.init_reg(0x5f, 0x40); dev->reg.init_reg(0x60, 0x00); dev->reg.init_reg(0x61, 0x00); dev->reg.init_reg(0x62, 0x00); dev->reg.init_reg(0x63, 0x00); dev->reg.init_reg(0x64, 0x00); dev->reg.init_reg(0x65, 0x00); dev->reg.init_reg(0x66, 0x00); dev->reg.init_reg(0x67, 0x40); dev->reg.init_reg(0x68, 0x40); dev->reg.init_reg(0x69, 0x20); dev->reg.init_reg(0x6a, 0x20); dev->reg.init_reg(0x6c, 0x00); dev->reg.init_reg(0x6d, 0x00); dev->reg.init_reg(0x6e, 0x00); dev->reg.init_reg(0x6f, 0x00); } else { for (unsigned addr = 0x5d; addr <= 0x6f; addr++) { dev->reg.init_reg(addr, 0); } dev->reg.init_reg(0x5e, 0x02); if (dev->model->model_id == ModelId::CANON_LIDE_60) { dev->reg.init_reg(0x66, 0xff); } } dev->reg.init_reg(0x70, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x71, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x72, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x73, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below if (dev->model->model_id == ModelId::CANON_LIDE_80) { dev->reg.init_reg(0x74, 0x00); dev->reg.init_reg(0x75, 0x01); dev->reg.init_reg(0x76, 0xff); dev->reg.init_reg(0x77, 0x00); dev->reg.init_reg(0x78, 0x0f); dev->reg.init_reg(0x79, 0xf0); dev->reg.init_reg(0x7a, 0xf0); dev->reg.init_reg(0x7b, 0x00); dev->reg.init_reg(0x7c, 0x1e); dev->reg.init_reg(0x7d, 0x11); dev->reg.init_reg(0x7e, 0x00); dev->reg.init_reg(0x7f, 0x50); dev->reg.init_reg(0x80, 0x00); dev->reg.init_reg(0x81, 0x00); dev->reg.init_reg(0x82, 0x0f); dev->reg.init_reg(0x83, 0x00); dev->reg.init_reg(0x84, 0x0e); dev->reg.init_reg(0x85, 0x00); dev->reg.init_reg(0x86, 0x0d); dev->reg.init_reg(0x87, 0x02); dev->reg.init_reg(0x88, 0x00); dev->reg.init_reg(0x89, 0x00); } else { for (unsigned addr = 0x74; addr <= 0x87; addr++) { dev->reg.init_reg(addr, 0); } } scanner_setup_sensor(*dev, sensor, dev->reg); // set up GPIO for (const auto& reg : dev->gpo.regs) { dev->reg.set8(reg.address, reg.value); } if (dev->model->gpio_id == GpioId::CANON_LIDE_35) { dev->reg.find_reg(0x6b).value |= REG_0x6B_GPO18; dev->reg.find_reg(0x6b).value &= ~REG_0x6B_GPO17; } if (dev->model->gpio_id == GpioId::XP300) { dev->reg.find_reg(0x6b).value |= REG_0x6B_GPO17; } if (dev->model->gpio_id == GpioId::DP685) { /* REG_0x6B_GPO18 lights on green led */ dev->reg.find_reg(0x6b).value |= REG_0x6B_GPO17 | REG_0x6B_GPO18; } if (dev->model->model_id == ModelId::CANON_LIDE_80) { // specific scanner settings, clock and gpio first dev->interface->write_register(REG_0x6B, 0x0c); dev->interface->write_register(0x06, 0x10); dev->interface->write_register(REG_0x6E, 0x6d); dev->interface->write_register(REG_0x6F, 0x80); dev->interface->write_register(REG_0x6B, 0x0e); dev->interface->write_register(REG_0x6C, 0x00); dev->interface->write_register(REG_0x6D, 0x8f); dev->interface->write_register(REG_0x6B, 0x0e); dev->interface->write_register(REG_0x6B, 0x0e); dev->interface->write_register(REG_0x6B, 0x0a); dev->interface->write_register(REG_0x6B, 0x02); dev->interface->write_register(REG_0x6B, 0x06); dev->interface->write_0x8c(0x10, 0x94); dev->interface->write_register(0x09, 0x10); } } static void gl841_set_lide80_fe(Genesys_Device* dev, std::uint8_t set) { DBG_HELPER(dbg); if (set == AFE_INIT) { dev->frontend = dev->frontend_initial; // BUG: the following code does not make sense. The addresses are different than AFE_SET // case dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00)); dev->interface->write_fe_register(0x03, dev->frontend.regs.get_value(0x01)); dev->interface->write_fe_register(0x06, dev->frontend.regs.get_value(0x02)); } if (set == AFE_SET) { dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00)); dev->interface->write_fe_register(0x06, dev->frontend.regs.get_value(0x20)); dev->interface->write_fe_register(0x03, dev->frontend.regs.get_value(0x28)); } } // Set values of Analog Device type frontend static void gl841_set_ad_fe(Genesys_Device* dev, std::uint8_t set) { DBG_HELPER(dbg); int i; if (dev->model->adc_id==AdcId::CANON_LIDE_80) { gl841_set_lide80_fe(dev, set); return; } if (set == AFE_INIT) { dev->frontend = dev->frontend_initial; // write them to analog frontend dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00)); dev->interface->write_fe_register(0x01, dev->frontend.regs.get_value(0x01)); for (i = 0; i < 6; i++) { dev->interface->write_fe_register(0x02 + i, 0x00); } } if (set == AFE_SET) { // write them to analog frontend dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00)); dev->interface->write_fe_register(0x01, dev->frontend.regs.get_value(0x01)); // Write fe 0x02 (red gain) dev->interface->write_fe_register(0x02, dev->frontend.get_gain(0)); // Write fe 0x03 (green gain) dev->interface->write_fe_register(0x03, dev->frontend.get_gain(1)); // Write fe 0x04 (blue gain) dev->interface->write_fe_register(0x04, dev->frontend.get_gain(2)); // Write fe 0x05 (red offset) dev->interface->write_fe_register(0x05, dev->frontend.get_offset(0)); // Write fe 0x06 (green offset) dev->interface->write_fe_register(0x06, dev->frontend.get_offset(1)); // Write fe 0x07 (blue offset) dev->interface->write_fe_register(0x07, dev->frontend.get_offset(2)); } } // Set values of analog frontend void CommandSetGl841::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set) const { DBG_HELPER_ARGS(dbg, "%s", set == AFE_INIT ? "init" : set == AFE_SET ? "set" : set == AFE_POWER_SAVE ? "powersave" : "huh?"); (void) sensor; /* Analog Device type frontend */ std::uint8_t frontend_type = dev->reg.find_reg(0x04).value & REG_0x04_FESET; if (frontend_type == 0x02) { gl841_set_ad_fe(dev, set); return; } if (frontend_type != 0x00) { throw SaneException("unsupported frontend type %d", frontend_type); } if (set == AFE_INIT) { dev->frontend = dev->frontend_initial; // reset only done on init dev->interface->write_fe_register(0x04, 0x80); } if (set == AFE_POWER_SAVE) { dev->interface->write_fe_register(0x01, 0x02); return; } /* todo : base this test on cfg reg3 or a CCD family flag to be created */ /*if (dev->model->ccd_type!=SensorId::CCD_HP2300 && dev->model->ccd_type!=SensorId::CCD_HP2400) */ { dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00)); dev->interface->write_fe_register(0x02, dev->frontend.regs.get_value(0x02)); } dev->interface->write_fe_register(0x01, dev->frontend.regs.get_value(0x01)); dev->interface->write_fe_register(0x03, dev->frontend.regs.get_value(0x03)); dev->interface->write_fe_register(0x06, dev->frontend.reg2[0]); dev->interface->write_fe_register(0x08, dev->frontend.reg2[1]); dev->interface->write_fe_register(0x09, dev->frontend.reg2[2]); for (unsigned i = 0; i < 3; i++) { dev->interface->write_fe_register(0x24 + i, dev->frontend.regs.get_value(0x24 + i)); dev->interface->write_fe_register(0x28 + i, dev->frontend.get_gain(i)); dev->interface->write_fe_register(0x20 + i, dev->frontend.get_offset(i)); } } // @brief turn off motor static void gl841_init_motor_regs_off(Genesys_Register_Set* reg, unsigned int scan_lines) { DBG_HELPER_ARGS(dbg, "scan_lines=%d", scan_lines); unsigned int feedl; feedl = 2; reg->set8(0x3d, (feedl >> 16) & 0xf); reg->set8(0x3e, (feedl >> 8) & 0xff); reg->set8(0x3f, feedl & 0xff); reg->find_reg(0x5e).value &= ~0xe0; reg->set8(0x25, (scan_lines >> 16) & 0xf); reg->set8(0x26, (scan_lines >> 8) & 0xff); reg->set8(0x27, scan_lines & 0xff); reg->set8(0x02, 0x00); reg->set8(0x67, 0x3f); reg->set8(0x68, 0x3f); reg->set8(REG_STEPNO, 1); reg->set8(REG_FASTNO, 1); reg->set8(0x69, 1); reg->set8(0x6a, 1); reg->set8(0x5f, 1); } /** @brief write motor table frequency * Write motor frequency data table. * @param dev device to set up motor * @param ydpi motor target resolution */ static void gl841_write_freq(Genesys_Device* dev, unsigned int ydpi) { DBG_HELPER(dbg); /**< fast table */ std::uint8_t tdefault[] = { 0x18, 0x36, 0x18, 0x36, 0x18, 0x36, 0x18, 0x36, 0x18, 0x36, 0x18, 0x36, 0x18, 0x36, 0x18, 0x36, 0x18, 0x36, 0x18, 0x36, 0x18, 0x36, 0x18, 0x36, 0x18, 0x36, 0x18, 0x36, 0x18, 0x36, 0x18, 0x36, 0x18, 0xb6, 0x18, 0xb6, 0x18, 0xb6, 0x18, 0xb6, 0x18, 0xb6, 0x18, 0xb6, 0x18, 0xb6, 0x18, 0xb6, 0x18, 0xb6, 0x18, 0xb6, 0x18, 0xb6, 0x18, 0xb6, 0x18, 0xb6, 0x18, 0xb6, 0x18, 0xb6, 0x18, 0xb6, 0x18, 0xf6, 0x18, 0xf6, 0x18, 0xf6, 0x18, 0xf6, 0x18, 0xf6, 0x18, 0xf6, 0x18, 0xf6, 0x18, 0xf6, 0x18, 0xf6, 0x18, 0xf6, 0x18, 0xf6, 0x18, 0xf6, 0x18, 0xf6, 0x18, 0xf6, 0x18, 0xf6, 0x18, 0xf6, 0x18, 0x76, 0x18, 0x76, 0x18, 0x76, 0x18, 0x76, 0x18, 0x76, 0x18, 0x76, 0x18, 0x76, 0x18, 0x76, 0x18, 0x76, 0x18, 0x76, 0x18, 0x76, 0x18, 0x76, 0x18, 0x76, 0x18, 0x76, 0x18, 0x76, 0x18, 0x76 }; std::uint8_t t1200[] = { 0xc7, 0x31, 0xc7, 0x31, 0xc7, 0x31, 0xc7, 0x31, 0xc7, 0x31, 0xc7, 0x31, 0xc7, 0x31, 0xc7, 0x31, 0xc0, 0x11, 0xc0, 0x11, 0xc0, 0x11, 0xc0, 0x11, 0xc0, 0x11, 0xc0, 0x11, 0xc0, 0x11, 0xc0, 0x11, 0xc7, 0xb1, 0xc7, 0xb1, 0xc7, 0xb1, 0xc7, 0xb1, 0xc7, 0xb1, 0xc7, 0xb1, 0xc7, 0xb1, 0xc7, 0xb1, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0xc7, 0xf1, 0xc7, 0xf1, 0xc7, 0xf1, 0xc7, 0xf1, 0xc7, 0xf1, 0xc7, 0xf1, 0xc7, 0xf1, 0xc7, 0xf1, 0xc0, 0x51, 0xc0, 0x51, 0xc0, 0x51, 0xc0, 0x51, 0xc0, 0x51, 0xc0, 0x51, 0xc0, 0x51, 0xc0, 0x51, 0xc7, 0x71, 0xc7, 0x71, 0xc7, 0x71, 0xc7, 0x71, 0xc7, 0x71, 0xc7, 0x71, 0xc7, 0x71, 0xc7, 0x71, 0x07, 0x20, 0x07, 0x20, 0x07, 0x20, 0x07, 0x20, 0x07, 0x20, 0x07, 0x20, 0x07, 0x20, 0x07, 0x20 }; std::uint8_t t300[] = { 0x08, 0x32, 0x08, 0x32, 0x08, 0x32, 0x08, 0x32, 0x08, 0x32, 0x08, 0x32, 0x08, 0x32, 0x08, 0x32, 0x00, 0x13, 0x00, 0x13, 0x00, 0x13, 0x00, 0x13, 0x00, 0x13, 0x00, 0x13, 0x00, 0x13, 0x00, 0x13, 0x08, 0xb2, 0x08, 0xb2, 0x08, 0xb2, 0x08, 0xb2, 0x08, 0xb2, 0x08, 0xb2, 0x08, 0xb2, 0x08, 0xb2, 0x0c, 0xa0, 0x0c, 0xa0, 0x0c, 0xa0, 0x0c, 0xa0, 0x0c, 0xa0, 0x0c, 0xa0, 0x0c, 0xa0, 0x0c, 0xa0, 0x08, 0xf2, 0x08, 0xf2, 0x08, 0xf2, 0x08, 0xf2, 0x08, 0xf2, 0x08, 0xf2, 0x08, 0xf2, 0x08, 0xf2, 0x00, 0xd3, 0x00, 0xd3, 0x00, 0xd3, 0x00, 0xd3, 0x00, 0xd3, 0x00, 0xd3, 0x00, 0xd3, 0x00, 0xd3, 0x08, 0x72, 0x08, 0x72, 0x08, 0x72, 0x08, 0x72, 0x08, 0x72, 0x08, 0x72, 0x08, 0x72, 0x08, 0x72, 0x0c, 0x60, 0x0c, 0x60, 0x0c, 0x60, 0x0c, 0x60, 0x0c, 0x60, 0x0c, 0x60, 0x0c, 0x60, 0x0c, 0x60 }; std::uint8_t t150[] = { 0x0c, 0x33, 0xcf, 0x33, 0xcf, 0x33, 0xcf, 0x33, 0xcf, 0x33, 0xcf, 0x33, 0xcf, 0x33, 0xcf, 0x33, 0x40, 0x14, 0x80, 0x15, 0x80, 0x15, 0x80, 0x15, 0x80, 0x15, 0x80, 0x15, 0x80, 0x15, 0x80, 0x15, 0x0c, 0xb3, 0xcf, 0xb3, 0xcf, 0xb3, 0xcf, 0xb3, 0xcf, 0xb3, 0xcf, 0xb3, 0xcf, 0xb3, 0xcf, 0xb3, 0x11, 0xa0, 0x16, 0xa0, 0x16, 0xa0, 0x16, 0xa0, 0x16, 0xa0, 0x16, 0xa0, 0x16, 0xa0, 0x16, 0xa0, 0x0c, 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, 0x40, 0xd4, 0x80, 0xd5, 0x80, 0xd5, 0x80, 0xd5, 0x80, 0xd5, 0x80, 0xd5, 0x80, 0xd5, 0x80, 0xd5, 0x0c, 0x73, 0xcf, 0x73, 0xcf, 0x73, 0xcf, 0x73, 0xcf, 0x73, 0xcf, 0x73, 0xcf, 0x73, 0xcf, 0x73, 0x11, 0x60, 0x16, 0x60, 0x16, 0x60, 0x16, 0x60, 0x16, 0x60, 0x16, 0x60, 0x16, 0x60, 0x16, 0x60 }; std::uint8_t *table; if(dev->model->motor_id == MotorId::CANON_LIDE_80) { switch(ydpi) { case 3600: case 1200: table=t1200; break; case 900: case 300: table=t300; break; case 450: case 150: table=t150; break; default: table=tdefault; } dev->interface->write_register(0x66, 0x00); dev->interface->write_gamma(0x28, 0xc000, table, 128); dev->interface->write_register(0x5b, 0x00); dev->interface->write_register(0x5c, 0x00); } } static void gl841_init_motor_regs_feed(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, unsigned int feed_steps,/*1/base_ydpi*/ ScanFlag flags) { DBG_HELPER_ARGS(dbg, "feed_steps=%d, flags=%x", feed_steps, static_cast(flags)); unsigned step_multiplier = 2; unsigned int feedl; /*number of scan lines to add in a scan_lines line*/ { std::vector table; table.resize(256, 0xffff); scanner_send_slope_table(dev, sensor, 0, table); scanner_send_slope_table(dev, sensor, 1, table); scanner_send_slope_table(dev, sensor, 2, table); scanner_send_slope_table(dev, sensor, 3, table); scanner_send_slope_table(dev, sensor, 4, table); } gl841_write_freq(dev, dev->motor.base_ydpi / 4); // FIXME: use proper scan session ScanSession session; session.params.yres = dev->motor.base_ydpi; session.params.scan_method = dev->model->default_method; const auto* fast_profile = get_motor_profile_ptr(dev->motor.fast_profiles, 0, session); if (fast_profile == nullptr) { fast_profile = get_motor_profile_ptr(dev->motor.profiles, 0, session); } auto fast_table = create_slope_table_fastest(dev->model->asic_type, step_multiplier, *fast_profile); // BUG: fast table is counted in base_ydpi / 4 feedl = feed_steps - fast_table.table.size() * 2; reg->set8(0x3d, (feedl >> 16) & 0xf); reg->set8(0x3e, (feedl >> 8) & 0xff); reg->set8(0x3f, feedl & 0xff); reg->find_reg(0x5e).value &= ~0xe0; reg->set8(0x25, 0); reg->set8(0x26, 0); reg->set8(0x27, 0); reg->find_reg(0x02).value &= ~0x01; /*LONGCURV OFF*/ reg->find_reg(0x02).value &= ~0x80; /*NOT_HOME OFF*/ reg->find_reg(0x02).value |= REG_0x02_MTRPWR; reg->find_reg(0x02).value &= ~0x08; if (has_flag(flags, ScanFlag::AUTO_GO_HOME)) { reg->find_reg(0x02).value |= 0x20; } else { reg->find_reg(0x02).value &= ~0x20; } reg->find_reg(0x02).value &= ~0x40; if (has_flag(flags, ScanFlag::REVERSE)) { reg->find_reg(0x02).value |= REG_0x02_MTRREV; } else { reg->find_reg(0x02).value &= ~REG_0x02_MTRREV; } scanner_send_slope_table(dev, sensor, 3, fast_table.table); reg->set8(0x67, 0x3f); reg->set8(0x68, 0x3f); reg->set8(REG_STEPNO, 1); reg->set8(REG_FASTNO, 1); reg->set8(0x69, 1); reg->set8(0x6a, fast_table.table.size() / step_multiplier); reg->set8(0x5f, 1); } static void gl841_init_motor_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, const ScanSession& session, Genesys_Register_Set* reg, const MotorProfile& motor_profile, unsigned int scan_exposure_time,/*pixel*/ unsigned scan_yres, // dpi, motor resolution unsigned int scan_lines,/*lines, scan resolution*/ unsigned int scan_dummy, // number of scan lines to add in a scan_lines line unsigned int feed_steps,/*1/base_ydpi*/ // maybe float for half/quarter step resolution? ScanFlag flags) { DBG_HELPER_ARGS(dbg, "scan_exposure_time=%d, scan_yres=%d, scan_step_type=%d, scan_lines=%d," " scan_dummy=%d, feed_steps=%d, flags=%x", scan_exposure_time, scan_yres, static_cast(motor_profile.step_type), scan_lines, scan_dummy, feed_steps, static_cast(flags)); unsigned step_multiplier = 2; unsigned int feedl; unsigned int min_restep = 0x20; /* we calculate both tables for SCAN. the fast slope step count depends on how many steps we need for slow acceleration and how much steps we are allowed to use. */ // At least in LiDE 50, 60 the fast movement table is counted in full steps. const auto* fast_profile = get_motor_profile_ptr(dev->motor.fast_profiles, 0, session); if (fast_profile == nullptr) { fast_profile = &motor_profile; } auto slow_table = create_slope_table(dev->model->asic_type, dev->motor, scan_yres, scan_exposure_time, step_multiplier, motor_profile); if (feed_steps < (slow_table.table.size() >> static_cast(motor_profile.step_type))) { /*TODO: what should we do here?? go back to exposure calculation?*/ feed_steps = slow_table.table.size() >> static_cast(motor_profile.step_type); } auto fast_table = create_slope_table_fastest(dev->model->asic_type, step_multiplier, *fast_profile); unsigned max_fast_slope_steps_count = step_multiplier; if (feed_steps > (slow_table.table.size() >> static_cast(motor_profile.step_type)) + 2) { max_fast_slope_steps_count = (feed_steps - (slow_table.table.size() >> static_cast(motor_profile.step_type))) / 2; } if (fast_table.table.size() > max_fast_slope_steps_count) { fast_table.slice_steps(max_fast_slope_steps_count, step_multiplier); } if ((feed_steps << static_cast(motor_profile.step_type)) < slow_table.table.size()) { feedl = 0; } else { feedl = (feed_steps << static_cast(motor_profile.step_type)) - slow_table.table.size(); } reg->set8(0x3d, (feedl >> 16) & 0xf); reg->set8(0x3e, (feedl >> 8) & 0xff); reg->set8(0x3f, feedl & 0xff); reg->find_reg(0x5e).value &= ~0xe0; reg->set8(0x25, (scan_lines >> 16) & 0xf); reg->set8(0x26, (scan_lines >> 8) & 0xff); reg->set8(0x27, scan_lines & 0xff); reg->find_reg(0x02).value = REG_0x02_MTRPWR; if (has_flag(flags, ScanFlag::REVERSE)) { reg->find_reg(0x02).value |= REG_0x02_MTRREV; } else { reg->find_reg(0x02).value &= ~REG_0x02_MTRREV; } reg->find_reg(0x02).value &= ~0x08; if (has_flag(flags, ScanFlag::AUTO_GO_HOME)) reg->find_reg(0x02).value |= 0x20; else reg->find_reg(0x02).value &= ~0x20; if (has_flag(flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE)) { reg->find_reg(0x02).value |= 0x40; } else { reg->find_reg(0x02).value &= ~0x40; } scanner_send_slope_table(dev, sensor, 0, slow_table.table); scanner_send_slope_table(dev, sensor, 1, slow_table.table); scanner_send_slope_table(dev, sensor, 2, slow_table.table); scanner_send_slope_table(dev, sensor, 3, fast_table.table); scanner_send_slope_table(dev, sensor, 4, fast_table.table); gl841_write_freq(dev, scan_yres); /* now reg 0x21 and 0x24 are available, we can calculate reg 0x22 and 0x23, reg 0x60-0x62 and reg 0x63-0x65 rule: 2*STEPNO+FWDSTEP=2*FASTNO+BWDSTEP */ /* steps of table 0*/ if (min_restep < slow_table.table.size() * 2 + 2) { min_restep = slow_table.table.size() * 2 + 2; } /* steps of table 1*/ if (min_restep < slow_table.table.size() * 2 + 2) { min_restep = slow_table.table.size() * 2 + 2; } /* steps of table 0*/ reg->set8(REG_FWDSTEP, min_restep - slow_table.table.size()*2); /* steps of table 1*/ reg->set8(REG_BWDSTEP, min_restep - slow_table.table.size()*2); /* for z1/z2: in dokumentation mentioned variables a-d: a = time needed for acceleration, table 1 b = time needed for reg 0x1f... wouldn't that be reg0x1f*exposure_time? c = time needed for acceleration, table 1 d = time needed for reg 0x22... wouldn't that be reg0x22*exposure_time? z1 = (c+d-1) % exposure_time z2 = (a+b-1) % exposure_time */ /* i don't see any effect of this. i can only guess that this will enhance sub-pixel accuracy z1 = (slope_0_time-1) % exposure_time; z2 = (slope_0_time-1) % exposure_time; */ reg->set24(REG_0x60, 0); reg->set24(REG_0x63, 0); reg->find_reg(REG_0x1E).value &= REG_0x1E_WDTIME; reg->find_reg(REG_0x1E).value |= scan_dummy; reg->set8(0x67, 0x3f | (static_cast(motor_profile.step_type) << 6)); reg->set8(0x68, 0x3f | (static_cast(fast_profile->step_type) << 6)); reg->set8(REG_STEPNO, slow_table.table.size() / step_multiplier); reg->set8(REG_FASTNO, slow_table.table.size() / step_multiplier); reg->set8(0x69, slow_table.table.size() / step_multiplier); reg->set8(0x6a, fast_table.table.size() / step_multiplier); reg->set8(0x5f, fast_table.table.size() / step_multiplier); } static void gl841_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, unsigned int exposure_time, const ScanSession& session) { DBG_HELPER_ARGS(dbg, "exposure_time=%d", exposure_time); dev->cmd_set->set_fe(dev, sensor, AFE_SET); /* gpio part.*/ if (dev->model->gpio_id == GpioId::CANON_LIDE_35) { if (session.params.xres <= 600) { reg->find_reg(REG_0x6C).value &= ~0x80; } else { reg->find_reg(REG_0x6C).value |= 0x80; } } if (dev->model->gpio_id == GpioId::CANON_LIDE_80) { if (session.params.xres <= 600) { reg->find_reg(REG_0x6C).value &= ~0x40; reg->find_reg(REG_0x6C).value |= 0x20; } else { reg->find_reg(REG_0x6C).value &= ~0x20; reg->find_reg(REG_0x6C).value |= 0x40; } } /* enable shading */ reg->find_reg(0x01).value |= REG_0x01_SCAN; if (has_flag(session.params.flags, ScanFlag::DISABLE_SHADING) || has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION)) { reg->find_reg(0x01).value &= ~REG_0x01_DVDSET; } else { reg->find_reg(0x01).value |= REG_0x01_DVDSET; } /* average looks better than deletion, and we are already set up to use one of the average enabled resolutions */ reg->find_reg(0x03).value |= REG_0x03_AVEENB; sanei_genesys_set_lamp_power(dev, sensor, *reg, !has_flag(session.params.flags, ScanFlag::DISABLE_LAMP)); /* BW threshold */ reg->set8(0x2e, 0x7f); reg->set8(0x2f, 0x7f); /* monochrome / color scan */ switch (session.params.depth) { case 8: reg->find_reg(0x04).value &= ~(REG_0x04_LINEART | REG_0x04_BITSET); break; case 16: reg->find_reg(0x04).value &= ~REG_0x04_LINEART; reg->find_reg(0x04).value |= REG_0x04_BITSET; break; } /* AFEMOD should depend on FESET, and we should set these * bits separately */ reg->find_reg(0x04).value &= ~(REG_0x04_FILTER | REG_0x04_AFEMOD); if (session.params.channels == 1) { switch (session.params.color_filter) { case ColorFilter::RED: reg->find_reg(0x04).value |= 0x14; break; case ColorFilter::GREEN: reg->find_reg(0x04).value |= 0x18; break; case ColorFilter::BLUE: reg->find_reg(0x04).value |= 0x1c; break; default: reg->find_reg(0x04).value |= 0x10; break; } } else { if (dev->model->sensor_id == SensorId::CCD_PLUSTEK_OPTICPRO_3600) { reg->find_reg(0x04).value |= 0x22; /* slow color pixel by pixel */ } else { reg->find_reg(0x04).value |= 0x10; /* color pixel by pixel */ } } /* CIS scanners can do true gray by setting LEDADD */ reg->find_reg(0x87).value &= ~REG_0x87_LEDADD; // enable gamma tables if (should_enable_gamma(session, sensor)) { reg->find_reg(REG_0x05).value |= REG_0x05_GMMENB; } else { reg->find_reg(REG_0x05).value &= ~REG_0x05_GMMENB; } /* sensor parameters */ scanner_setup_sensor(*dev, sensor, dev->reg); reg->set8(0x29, 255); /*<<<"magic" number, only suitable for cis*/ reg->set16(REG_DPISET, sensor.register_dpiset); reg->set16(REG_STRPIXEL, session.pixel_startx); reg->set16(REG_ENDPIXEL, session.pixel_endx); reg->set24(REG_MAXWD, session.output_line_bytes); reg->set16(REG_LPERIOD, exposure_time); reg->set8(0x34, sensor.dummy_pixel); } /** @brief compute exposure time * Compute exposure time for the device and the given scan resolution */ static int gl841_exposure_time(Genesys_Device *dev, const Genesys_Sensor& sensor, const MotorProfile& profile, float slope_dpi, int start, int used_pixels) { int led_exposure = 0; if (dev->model->is_cis) { unsigned dummy = dev->reg.find_reg(0x19).value; unsigned max_sensor_exposure = std::max({sensor.exposure.red, sensor.exposure.green, sensor.exposure.blue}); led_exposure = dummy + max_sensor_exposure; } return sanei_genesys_exposure_time2(dev, profile, slope_dpi, start + used_pixels,/*+tgtime? currently done in sanei_genesys_exposure_time2 with tgtime = 32 pixel*/ led_exposure); } void CommandSetGl841::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, const ScanSession& session) const { DBG_HELPER(dbg); session.assert_computed(); int move; int exposure_time; int slope_dpi = 0; int dummy = 0; /* dummy */ /* dummy lines: may not be useful, for instance 250 dpi works with 0 or 1 dummy line. Maybe the dummy line adds correctness since the motor runs slower (higher dpi) */ /* for cis this creates better aligned color lines: dummy \ scanned lines 0: R G B R ... 1: R G B - R ... 2: R G B - - R ... 3: R G B - - - R ... 4: R G B - - - - R ... 5: R G B - - - - - R ... 6: R G B - - - - - - R ... 7: R G B - - - - - - - R ... 8: R G B - - - - - - - - R ... 9: R G B - - - - - - - - - R ... 10: R G B - - - - - - - - - - R ... 11: R G B - - - - - - - - - - - R ... 12: R G B - - - - - - - - - - - - R ... 13: R G B - - - - - - - - - - - - - R ... 14: R G B - - - - - - - - - - - - - - R ... 15: R G B - - - - - - - - - - - - - - - R ... -- pierre */ dummy = 0; /* slope_dpi */ /* cis color scan is effectively a gray scan with 3 gray lines per color line and a FILTER of 0 */ if (dev->model->is_cis) { slope_dpi = session.params.yres* session.params.channels; } else { slope_dpi = session.params.yres; } slope_dpi = slope_dpi * (1 + dummy); const auto& motor_profile = get_motor_profile(dev->motor.profiles, 0, session); exposure_time = gl841_exposure_time(dev, sensor, motor_profile, slope_dpi, session.pixel_startx, session.optical_pixels); gl841_init_optical_regs_scan(dev, sensor, reg, exposure_time, session); move = session.params.starty; /* subtract current head position */ move -= (dev->head_pos(ScanHeadId::PRIMARY) * session.params.yres) / dev->motor.base_ydpi; if (move < 0) move = 0; /* round it */ /* the move is not affected by dummy -- pierre */ /* move = ((move + dummy) / (dummy + 1)) * (dummy + 1);*/ if (has_flag(session.params.flags, ScanFlag::SINGLE_LINE)) { gl841_init_motor_regs_off(reg, session.optical_line_count); } else { gl841_init_motor_regs_scan(dev, sensor, session, reg, motor_profile, exposure_time, slope_dpi, session.optical_line_count, dummy, move, session.params.flags); } setup_image_pipeline(*dev, session); dev->read_active = true; dev->session = session; dev->total_bytes_read = 0; dev->total_bytes_to_read = (size_t)session.output_line_bytes_requested * (size_t)session.params.lines; if (session.use_host_side_gray) { dev->total_bytes_to_read /= 3; } DBG(DBG_info, "%s: total bytes to send = %zu\n", __func__, dev->total_bytes_to_read); } ScanSession CommandSetGl841::calculate_scan_session(const Genesys_Device* dev, const Genesys_Sensor& sensor, const Genesys_Settings& settings) const { DBG_HELPER(dbg); debug_dump(DBG_info, settings); /* steps to move to reach scanning area: - first we move to physical start of scanning either by a fixed steps amount from the black strip or by a fixed amount from parking position, minus the steps done during shading calibration - then we move by the needed offset whitin physical scanning area assumption: steps are expressed at maximum motor resolution we need: float y_offset; float y_size; float y_offset_calib; mm_to_steps()=motor dpi / 2.54 / 10=motor dpi / MM_PER_INCH */ float move = dev->model->y_offset; move += dev->settings.tl_y; int move_dpi = dev->motor.base_ydpi; move = static_cast((move * move_dpi) / MM_PER_INCH); float start = dev->model->x_offset; start += dev->settings.tl_x; start = static_cast((start * dev->settings.xres) / MM_PER_INCH); ScanSession session; session.params.xres = dev->settings.xres; session.params.yres = dev->settings.yres; session.params.startx = static_cast(start); session.params.starty = static_cast(move); session.params.pixels = dev->settings.pixels; session.params.requested_pixels = dev->settings.requested_pixels; session.params.lines = dev->settings.lines; session.params.depth = dev->settings.depth; session.params.channels = dev->settings.get_channels(); session.params.scan_method = dev->settings.scan_method; session.params.scan_mode = dev->settings.scan_mode; session.params.color_filter = dev->settings.color_filter; session.params.contrast_adjustment = dev->settings.contrast; session.params.brightness_adjustment = dev->settings.brightness; session.params.flags = ScanFlag::NONE; compute_session(dev, session, sensor); return session; } // for fast power saving methods only, like disabling certain amplifiers void CommandSetGl841::save_power(Genesys_Device* dev, bool enable) const { DBG_HELPER_ARGS(dbg, "enable = %d", enable); const auto& sensor = sanei_genesys_find_sensor_any(dev); if (enable) { if (dev->model->gpio_id == GpioId::CANON_LIDE_35) { /* expect GPIO17 to be enabled, and GPIO9 to be disabled, while GPIO8 is disabled*/ /* final state: GPIO8 disabled, GPIO9 enabled, GPIO17 disabled, GPIO18 disabled*/ std::uint8_t val = dev->interface->read_register(REG_0x6D); dev->interface->write_register(REG_0x6D, val | 0x80); dev->interface->sleep_ms(1); /*enable GPIO9*/ val = dev->interface->read_register(REG_0x6C); dev->interface->write_register(REG_0x6C, val | 0x01); /*disable GPO17*/ val = dev->interface->read_register(REG_0x6B); dev->interface->write_register(REG_0x6B, val & ~REG_0x6B_GPO17); /*disable GPO18*/ val = dev->interface->read_register(REG_0x6B); dev->interface->write_register(REG_0x6B, val & ~REG_0x6B_GPO18); dev->interface->sleep_ms(1); val = dev->interface->read_register(REG_0x6D); dev->interface->write_register(REG_0x6D, val & ~0x80); } if (dev->model->gpio_id == GpioId::DP685) { std::uint8_t val = dev->interface->read_register(REG_0x6B); dev->interface->write_register(REG_0x6B, val & ~REG_0x6B_GPO17); dev->reg.find_reg(0x6b).value &= ~REG_0x6B_GPO17; dev->initial_regs.find_reg(0x6b).value &= ~REG_0x6B_GPO17; } set_fe(dev, sensor, AFE_POWER_SAVE); } else { if (dev->model->gpio_id == GpioId::CANON_LIDE_35) { /* expect GPIO17 to be enabled, and GPIO9 to be disabled, while GPIO8 is disabled*/ /* final state: GPIO8 enabled, GPIO9 disabled, GPIO17 enabled, GPIO18 enabled*/ std::uint8_t val = dev->interface->read_register(REG_0x6D); dev->interface->write_register(REG_0x6D, val | 0x80); dev->interface->sleep_ms(10); /*disable GPIO9*/ val = dev->interface->read_register(REG_0x6C); dev->interface->write_register(REG_0x6C, val & ~0x01); /*enable GPIO10*/ val = dev->interface->read_register(REG_0x6C); dev->interface->write_register(REG_0x6C, val | 0x02); /*enable GPO17*/ val = dev->interface->read_register(REG_0x6B); dev->interface->write_register(REG_0x6B, val | REG_0x6B_GPO17); dev->reg.find_reg(0x6b).value |= REG_0x6B_GPO17; dev->initial_regs.find_reg(0x6b).value |= REG_0x6B_GPO17; /*enable GPO18*/ val = dev->interface->read_register(REG_0x6B); dev->interface->write_register(REG_0x6B, val | REG_0x6B_GPO18); dev->reg.find_reg(0x6b).value |= REG_0x6B_GPO18; dev->initial_regs.find_reg(0x6b).value |= REG_0x6B_GPO18; } if (dev->model->gpio_id == GpioId::DP665 || dev->model->gpio_id == GpioId::DP685) { std::uint8_t val = dev->interface->read_register(REG_0x6B); dev->interface->write_register(REG_0x6B, val | REG_0x6B_GPO17); dev->reg.find_reg(0x6b).value |= REG_0x6B_GPO17; dev->initial_regs.find_reg(0x6b).value |= REG_0x6B_GPO17; } } } void CommandSetGl841::set_powersaving(Genesys_Device* dev, int delay /* in minutes */) const { DBG_HELPER_ARGS(dbg, "delay = %d", delay); // FIXME: SEQUENTIAL not really needed in this case Genesys_Register_Set local_reg(Genesys_Register_Set::SEQUENTIAL); int rate, exposure_time, tgtime, time; local_reg.init_reg(0x01, dev->reg.get8(0x01)); /* disable fastmode */ local_reg.init_reg(0x03, dev->reg.get8(0x03)); /* Lamp power control */ local_reg.init_reg(0x05, dev->reg.get8(0x05)); /*& ~REG_0x05_BASESEL*/; /* 24 clocks/pixel */ local_reg.init_reg(0x18, 0x00); // Set CCD type local_reg.init_reg(0x38, 0x00); local_reg.init_reg(0x39, 0x00); // period times for LPeriod, expR,expG,expB, Z1MODE, Z2MODE local_reg.init_reg(0x1c, dev->reg.get8(0x05) & ~REG_0x1C_TGTIME); if (!delay) { local_reg.find_reg(0x03).value = local_reg.find_reg(0x03).value & 0xf0; /* disable lampdog and set lamptime = 0 */ } else if (delay < 20) { local_reg.find_reg(0x03).value = (local_reg.find_reg(0x03).value & 0xf0) | 0x09; /* enable lampdog and set lamptime = 1 */ } else { local_reg.find_reg(0x03).value = (local_reg.find_reg(0x03).value & 0xf0) | 0x0f; /* enable lampdog and set lamptime = 7 */ } time = delay * 1000 * 60; /* -> msec */ exposure_time = static_cast(time * 32000.0 / (24.0 * 64.0 * (local_reg.find_reg(0x03).value & REG_0x03_LAMPTIM) * 1024.0) + 0.5); /* 32000 = system clock, 24 = clocks per pixel */ rate = (exposure_time + 65536) / 65536; if (rate > 4) { rate = 8; tgtime = 3; } else if (rate > 2) { rate = 4; tgtime = 2; } else if (rate > 1) { rate = 2; tgtime = 1; } else { rate = 1; tgtime = 0; } local_reg.find_reg(0x1c).value |= tgtime; exposure_time /= rate; if (exposure_time > 65535) exposure_time = 65535; local_reg.set8(0x38, exposure_time >> 8); local_reg.set8(0x39, exposure_time & 255); /* lowbyte */ dev->interface->write_registers(local_reg); } static bool gl841_get_paper_sensor(Genesys_Device* dev) { DBG_HELPER(dbg); std::uint8_t val = dev->interface->read_register(REG_0x6D); return (val & 0x1) == 0; } void CommandSetGl841::eject_document(Genesys_Device* dev) const { DBG_HELPER(dbg); Genesys_Register_Set local_reg; unsigned int init_steps; float feed_mm; int loop; if (!dev->model->is_sheetfed) { DBG(DBG_proc, "%s: there is no \"eject sheet\"-concept for non sheet fed\n", __func__); return; } local_reg.clear(); // FIXME: unused result scanner_read_status(*dev); scanner_stop_action(*dev); local_reg = dev->reg; regs_set_optical_off(dev->model->asic_type, local_reg); const auto& sensor = sanei_genesys_find_sensor_any(dev); gl841_init_motor_regs_feed(dev, sensor, &local_reg, 65536, ScanFlag::NONE); dev->interface->write_registers(local_reg); try { scanner_start_action(*dev, true); } catch (...) { catch_all_exceptions(__func__, [&]() { scanner_stop_action(*dev); }); // restore original registers catch_all_exceptions(__func__, [&]() { dev->interface->write_registers(dev->reg); }); throw; } if (is_testing_mode()) { dev->interface->test_checkpoint("eject_document"); scanner_stop_action(*dev); return; } if (gl841_get_paper_sensor(dev)) { DBG(DBG_info, "%s: paper still loaded\n", __func__); /* force document TRUE, because it is definitely present */ dev->document = true; dev->set_head_pos_zero(ScanHeadId::PRIMARY); loop = 300; while (loop > 0) /* do not wait longer then 30 seconds */ { if (!gl841_get_paper_sensor(dev)) { DBG(DBG_info, "%s: reached home position\n", __func__); break; } dev->interface->sleep_ms(100); --loop; } if (loop == 0) { // when we come here then the scanner needed too much time for this, so we better stop // the motor catch_all_exceptions(__func__, [&](){ scanner_stop_action(*dev); }); throw SaneException(SANE_STATUS_IO_ERROR, "timeout while waiting for scanhead to go home"); } } feed_mm = dev->model->eject_feed; if (dev->document) { feed_mm += dev->model->post_scan; } sanei_genesys_read_feed_steps(dev, &init_steps); /* now feed for extra steps */ loop = 0; while (loop < 300) /* do not wait longer then 30 seconds */ { unsigned int steps; sanei_genesys_read_feed_steps(dev, &steps); DBG(DBG_info, "%s: init_steps: %d, steps: %d\n", __func__, init_steps, steps); if (steps > init_steps + (feed_mm * dev->motor.base_ydpi) / MM_PER_INCH) { break; } dev->interface->sleep_ms(100); ++loop; } scanner_stop_action(*dev); dev->document = false; } void CommandSetGl841::update_home_sensor_gpio(Genesys_Device& dev) const { if (dev.model->gpio_id == GpioId::CANON_LIDE_35) { dev.interface->read_register(REG_0x6C); dev.interface->write_register(REG_0x6C, dev.gpo.regs.get_value(0x6c)); } if (dev.model->gpio_id == GpioId::CANON_LIDE_80) { dev.interface->read_register(REG_0x6B); dev.interface->write_register(REG_0x6B, REG_0x6B_GPO18 | REG_0x6B_GPO17); } } void CommandSetGl841::load_document(Genesys_Device* dev) const { DBG_HELPER(dbg); int loop = 300; while (loop > 0) /* do not wait longer then 30 seconds */ { if (gl841_get_paper_sensor(dev)) { DBG(DBG_info, "%s: document inserted\n", __func__); /* when loading OK, document is here */ dev->document = true; // give user some time to place document correctly dev->interface->sleep_ms(1000); break; } dev->interface->sleep_ms(100); --loop; } if (loop == 0) { // when we come here then the user needed to much time for this throw SaneException(SANE_STATUS_IO_ERROR, "timeout while waiting for document"); } } /** * detects end of document and adjust current scan * to take it into account * used by sheetfed scanners */ void CommandSetGl841::detect_document_end(Genesys_Device* dev) const { DBG_HELPER(dbg); bool paper_loaded = gl841_get_paper_sensor(dev); /* sheetfed scanner uses home sensor as paper present */ if (dev->document && !paper_loaded) { DBG(DBG_info, "%s: no more document\n", __func__); dev->document = false; /* we can't rely on total_bytes_to_read since the frontend * might have been slow to read data, so we re-evaluate the * amount of data to scan form the hardware settings */ unsigned scanned_lines = 0; try { sanei_genesys_read_scancnt(dev, &scanned_lines); } catch (...) { dev->total_bytes_to_read = dev->total_bytes_read; throw; } if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS && dev->model->is_cis) { scanned_lines /= 3; } std::size_t output_lines = dev->session.output_line_count; std::size_t offset_lines = static_cast( (dev->model->post_scan / MM_PER_INCH) * dev->settings.yres); std::size_t scan_end_lines = scanned_lines + offset_lines; std::size_t remaining_lines = dev->get_pipeline_source().remaining_bytes() / dev->session.output_line_bytes_raw; DBG(DBG_io, "%s: scanned_lines=%u\n", __func__, scanned_lines); DBG(DBG_io, "%s: scan_end_lines=%zu\n", __func__, scan_end_lines); DBG(DBG_io, "%s: output_lines=%zu\n", __func__, output_lines); DBG(DBG_io, "%s: remaining_lines=%zu\n", __func__, remaining_lines); if (scan_end_lines > output_lines) { auto skip_lines = scan_end_lines - output_lines; if (remaining_lines > skip_lines) { remaining_lines -= skip_lines; dev->get_pipeline_source().set_remaining_bytes(remaining_lines * dev->session.output_line_bytes_raw); dev->total_bytes_to_read -= skip_lines * dev->session.output_line_bytes_requested; } } } } // Send the low-level scan command // todo : is this that useful ? void CommandSetGl841::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, bool start_motor) const { DBG_HELPER(dbg); (void) sensor; // FIXME: SEQUENTIAL not really needed in this case Genesys_Register_Set local_reg(Genesys_Register_Set::SEQUENTIAL); std::uint8_t val; if (dev->model->gpio_id == GpioId::CANON_LIDE_80) { val = dev->interface->read_register(REG_0x6B); val = REG_0x6B_GPO18; dev->interface->write_register(REG_0x6B, val); } if (dev->model->model_id == ModelId::CANON_LIDE_50 || dev->model->model_id == ModelId::CANON_LIDE_60) { if (dev->session.params.yres >= 1200) { dev->interface->write_register(REG_0x6C, 0x82); } else { dev->interface->write_register(REG_0x6C, 0x02); } if (dev->session.params.yres >= 600) { dev->interface->write_register(REG_0x6B, 0x01); } else { dev->interface->write_register(REG_0x6B, 0x03); } } if (dev->model->sensor_id != SensorId::CCD_PLUSTEK_OPTICPRO_3600) { local_reg.init_reg(0x03, reg->get8(0x03) | REG_0x03_LAMPPWR); } else { // TODO PLUSTEK_3600: why ?? local_reg.init_reg(0x03, reg->get8(0x03)); } local_reg.init_reg(0x01, reg->get8(0x01) | REG_0x01_SCAN); local_reg.init_reg(0x0d, 0x01); // scanner_start_action(dev, start_motor) if (start_motor) { local_reg.init_reg(0x0f, 0x01); } else { // do not start motor yet local_reg.init_reg(0x0f, 0x00); } dev->interface->write_registers(local_reg); dev->advance_head_pos_by_session(ScanHeadId::PRIMARY); } // Send the stop scan command void CommandSetGl841::end_scan(Genesys_Device* dev, Genesys_Register_Set __sane_unused__* reg, bool check_stop) const { DBG_HELPER_ARGS(dbg, "check_stop = %d", check_stop); if (!dev->model->is_sheetfed) { scanner_stop_action(*dev); } } // Moves the slider to the home (top) position slowly void CommandSetGl841::move_back_home(Genesys_Device* dev, bool wait_until_home) const { scanner_move_back_home(*dev, wait_until_home); } // init registers for shading calibration void CommandSetGl841::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { DBG_HELPER(dbg); unsigned channels = 3; unsigned resolution = sensor.shading_resolution; const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, dev->settings.scan_method); unsigned calib_lines = static_cast(dev->model->y_size_calib_dark_white_mm * resolution / MM_PER_INCH); unsigned starty = static_cast(dev->model->y_offset_calib_dark_white_mm * dev->motor.base_ydpi / MM_PER_INCH); ScanSession session; session.params.xres = resolution; session.params.yres = resolution; session.params.startx = 0; session.params.starty = starty; session.params.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; session.params.lines = calib_lines; session.params.depth = 16; session.params.channels = channels; session.params.scan_method = dev->settings.scan_method; session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; session.params.color_filter = dev->settings.color_filter; session.params.contrast_adjustment = dev->settings.contrast; session.params.brightness_adjustment = dev->settings.brightness; session.params.flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA; compute_session(dev, session, calib_sensor); init_regs_for_scan_session(dev, calib_sensor, ®s, session); dev->calib_session = session; } // this function sends generic gamma table (ie linear ones) or the Sensor specific one if provided void CommandSetGl841::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const { DBG_HELPER(dbg); int size; size = 256; auto gamma = generate_gamma_buffer(dev, sensor, 16, 65535, size); dev->interface->write_gamma(0x28, 0x0000, gamma.data(), size * 2 * 3); } /* this function does the led calibration by scanning one line of the calibration area below scanner's top on white strip. -needs working coarse/gain */ SensorExposure CommandSetGl841::led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { return scanner_led_calibration(*dev, sensor, regs); } /** @brief calibration for AD frontend devices * offset calibration assumes that the scanning head is on a black area * For LiDE80 analog frontend * 0x0003 : is gain and belongs to [0..63] * 0x0006 : is offset * We scan a line with no gain until average offset reaches the target */ static void ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) { DBG_HELPER(dbg); int average; int turn; int top; int bottom; int target; /* don't impact 3600 behavior since we can't test it */ if (dev->model->sensor_id == SensorId::CCD_PLUSTEK_OPTICPRO_3600) { return; } unsigned resolution = sensor.shading_resolution; const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, 3, dev->settings.scan_method); unsigned num_pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; ScanSession session; session.params.xres = resolution; session.params.yres = dev->settings.yres; session.params.startx = 0; session.params.starty = 0; session.params.pixels = num_pixels; session.params.lines = 1; session.params.depth = 8; session.params.channels = 3; session.params.scan_method = dev->settings.scan_method; session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; session.params.color_filter = dev->settings.color_filter; session.params.contrast_adjustment = dev->settings.contrast; session.params.brightness_adjustment = dev->settings.brightness; session.params.flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::SINGLE_LINE | ScanFlag::IGNORE_STAGGER_OFFSET | ScanFlag::IGNORE_COLOR_OFFSET; compute_session(dev, session, calib_sensor); dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, ®s, session); // FIXME: we're reading twice as much data for no reason std::size_t total_size = session.output_line_bytes * 2; std::vector line(total_size); dev->frontend.set_gain(0, 0); dev->frontend.set_gain(1, 0); dev->frontend.set_gain(2, 0); /* loop on scan until target offset is reached */ turn=0; target=24; bottom=0; top=255; do { /* set up offset mid range */ dev->frontend.set_offset(0, (top + bottom) / 2); dev->frontend.set_offset(1, (top + bottom) / 2); dev->frontend.set_offset(2, (top + bottom) / 2); /* scan line */ DBG(DBG_info, "%s: starting line reading\n", __func__); dev->interface->write_registers(regs); dev->cmd_set->set_fe(dev, calib_sensor, AFE_SET); dev->cmd_set->begin_scan(dev, calib_sensor, ®s, true); if (is_testing_mode()) { dev->interface->test_checkpoint("ad_fe_offset_calibration"); scanner_stop_action(*dev); return; } sanei_genesys_read_data_from_scanner(dev, line.data(), total_size); scanner_stop_action(*dev); if (dbg_log_image_data()) { char fn[30]; std::snprintf(fn, 30, "gl841_offset_%02d.tiff", turn); write_tiff_file(fn, line.data(), 8, 3, num_pixels, 1); } /* search for minimal value */ average=0; for (std::size_t i = 0; i < total_size; i++) { average += line[i]; } average/=total_size; DBG(DBG_data, "%s: average=%d\n", __func__, average); /* if min value is above target, the current value becomes the new top * else it is the new bottom */ if(average>target) { top=(top+bottom)/2; } else { bottom=(top+bottom)/2; } turn++; } while ((top-bottom)>1 && turn < 100); DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__, dev->frontend.get_offset(0), dev->frontend.get_offset(1), dev->frontend.get_offset(2)); } /* this function does the offset calibration by scanning one line of the calibration area below scanner's top. There is a black margin and the remaining is white. this function expects the slider to be where? */ void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { DBG_HELPER(dbg); int off[3],offh[3],offl[3],off1[3],off2[3]; int min1[3],min2[3]; unsigned cmin[3],cmax[3]; int turn; int mintgt = 0x400; /* Analog Device fronted have a different calibration */ if ((dev->reg.find_reg(0x04).value & REG_0x04_FESET) == 0x02) { ad_fe_offset_calibration(dev, sensor, regs); return; } /* offset calibration is always done in color mode */ unsigned channels = 3; unsigned resolution = sensor.shading_resolution; const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, dev->settings.scan_method); ScanSession session; session.params.xres = resolution; session.params.yres = dev->settings.yres; session.params.startx = 0; session.params.starty = 0; session.params.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; session.params.lines = 1; session.params.depth = 16; session.params.channels = channels; session.params.scan_method = dev->settings.scan_method; session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; session.params.color_filter = dev->settings.color_filter; session.params.contrast_adjustment = dev->settings.contrast; session.params.brightness_adjustment = dev->settings.brightness; session.params.flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::SINGLE_LINE | ScanFlag::IGNORE_STAGGER_OFFSET | ScanFlag::IGNORE_COLOR_OFFSET | ScanFlag::DISABLE_LAMP; compute_session(dev, session, calib_sensor); init_regs_for_scan_session(dev, calib_sensor, ®s, session); /* scan first line of data with no offset nor gain */ /*WM8199: gain=0.73; offset=-260mV*/ /*okay. the sensor black level is now at -260mV. we only get 0 from AFE...*/ /* we should probably do real calibration here: * -detect acceptable offset with binary search * -calculate offset from this last version * * acceptable offset means * - few completely black pixels(<10%?) * - few completely white pixels(<10%?) * * final offset should map the minimum not completely black * pixel to 0(16 bits) * * this does account for dummy pixels at the end of ccd * this assumes slider is at black strip(which is not quite as black as "no * signal"). * */ dev->frontend.set_gain(0, 0); dev->frontend.set_gain(1, 0); dev->frontend.set_gain(2, 0); offh[0] = 0xff; offh[1] = 0xff; offh[2] = 0xff; offl[0] = 0x00; offl[1] = 0x00; offl[2] = 0x00; turn = 0; Image first_line; bool acceptable = false; do { dev->interface->write_registers(regs); for (unsigned j = 0; j < channels; j++) { off[j] = (offh[j]+offl[j])/2; dev->frontend.set_offset(j, off[j]); } dev->cmd_set->set_fe(dev, calib_sensor, AFE_SET); DBG(DBG_info, "%s: starting first line reading\n", __func__); dev->cmd_set->begin_scan(dev, calib_sensor, ®s, true); if (is_testing_mode()) { dev->interface->test_checkpoint("offset_calibration"); return; } first_line = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes); if (dbg_log_image_data()) { char fn[30]; std::snprintf(fn, 30, "gl841_offset1_%02d.tiff", turn); write_tiff_file(fn, first_line); } acceptable = true; for (unsigned ch = 0; ch < channels; ch++) { cmin[ch] = 0; cmax[ch] = 0; for (std::size_t x = 0; x < first_line.get_width(); x++) { auto value = first_line.get_raw_channel(x, 0, ch); if (value < 10) { cmin[ch]++; } if (value > 65525) { cmax[ch]++; } } /* TODO the DP685 has a black strip in the middle of the sensor * should be handled in a more elegant way , could be a bug */ if (dev->model->sensor_id == SensorId::CCD_DP685) { cmin[ch] -= 20; } if (cmin[ch] > first_line.get_width() / 100) { acceptable = false; if (dev->model->is_cis) offl[0] = off[0]; else offl[ch] = off[ch]; } if (cmax[ch] > first_line.get_width() / 100) { acceptable = false; if (dev->model->is_cis) offh[0] = off[0]; else offh[ch] = off[ch]; } } DBG(DBG_info,"%s: black/white pixels: %d/%d,%d/%d,%d/%d\n", __func__, cmin[0], cmax[0], cmin[1], cmax[1], cmin[2], cmax[2]); if (dev->model->is_cis) { offh[2] = offh[1] = offh[0]; offl[2] = offl[1] = offl[0]; } scanner_stop_action(*dev); turn++; } while (!acceptable && turn < 100); DBG(DBG_info,"%s: acceptable offsets: %d,%d,%d\n", __func__, off[0], off[1], off[2]); for (unsigned ch = 0; ch < channels; ch++) { off1[ch] = off[ch]; min1[ch] = 65536; for (std::size_t x = 0; x < first_line.get_width(); x++) { auto value = first_line.get_raw_channel(x, 0, ch); if (min1[ch] > value && value >= 10) { min1[ch] = value; } } } offl[0] = off[0]; offl[1] = off[0]; offl[2] = off[0]; turn = 0; Image second_line; do { for (unsigned j=0; j < channels; j++) { off[j] = (offh[j]+offl[j])/2; dev->frontend.set_offset(j, off[j]); } dev->cmd_set->set_fe(dev, calib_sensor, AFE_SET); DBG(DBG_info, "%s: starting second line reading\n", __func__); dev->interface->write_registers(regs); dev->cmd_set->begin_scan(dev, calib_sensor, ®s, true); second_line = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes); if (dbg_log_image_data()) { char fn[30]; std::snprintf(fn, 30, "gl841_offset2_%02d.tiff", turn); write_tiff_file(fn, second_line); } acceptable = true; for (unsigned ch = 0; ch < channels; ch++) { cmin[ch] = 0; cmax[ch] = 0; for (std::size_t x = 0; x < second_line.get_width(); x++) { auto value = second_line.get_raw_channel(x, 0, ch); if (value < 10) { cmin[ch]++; } if (value > 65525) { cmax[ch]++; } } if (cmin[ch] > second_line.get_width() / 100) { acceptable = false; if (dev->model->is_cis) offl[0] = off[0]; else offl[ch] = off[ch]; } if (cmax[ch] > second_line.get_width() / 100) { acceptable = false; if (dev->model->is_cis) offh[0] = off[0]; else offh[ch] = off[ch]; } } DBG(DBG_info, "%s: black/white pixels: %d/%d,%d/%d,%d/%d\n", __func__, cmin[0], cmax[0], cmin[1], cmax[1], cmin[2], cmax[2]); if (dev->model->is_cis) { offh[2] = offh[1] = offh[0]; offl[2] = offl[1] = offl[0]; } scanner_stop_action(*dev); turn++; } while (!acceptable && turn < 100); DBG(DBG_info, "%s: acceptable offsets: %d,%d,%d\n", __func__, off[0], off[1], off[2]); for (unsigned ch = 0; ch < channels; ch++) { off2[ch] = off[ch]; min2[ch] = 65536; for (std::size_t x = 0; x < second_line.get_width(); x++) { auto value = second_line.get_raw_channel(x, 0, ch); if (min2[ch] > value && value != 0) { min2[ch] = value; } } } DBG(DBG_info, "%s: first set: %d/%d,%d/%d,%d/%d\n", __func__, off1[0], min1[0], off1[1], min1[1], off1[2], min1[2]); DBG(DBG_info, "%s: second set: %d/%d,%d/%d,%d/%d\n", __func__, off2[0], min2[0], off2[1], min2[1], off2[2], min2[2]); /* calculate offset for each channel based on minimal pixel value min1 at offset off1 and minimal pixel value min2 at offset off2 to get min at off, values are linearly interpolated: min=real+off*fact min1=real+off1*fact min2=real+off2*fact fact=(min1-min2)/(off1-off2) real=min1-off1*(min1-min2)/(off1-off2) off=(min-min1+off1*(min1-min2)/(off1-off2))/((min1-min2)/(off1-off2)) off=(min*(off1-off2)+min1*off2-off1*min2)/(min1-min2) */ for (unsigned ch = 0; ch < channels; ch++) { if (min2[ch] - min1[ch] == 0) { /*TODO: try to avoid this*/ DBG(DBG_warn, "%s: difference too small\n", __func__); if (mintgt * (off1[ch] - off2[ch]) + min1[ch] * off2[ch] - min2[ch] * off1[ch] >= 0) { off[ch] = 0x0000; } else { off[ch] = 0xffff; } } else { off[ch] = (mintgt * (off1[ch] - off2[ch]) + min1[ch] * off2[ch] - min2[ch] * off1[ch])/(min1[ch]-min2[ch]); } if (off[ch] > 255) { off[ch] = 255; } if (off[ch] < 0) { off[ch] = 0; } dev->frontend.set_offset(ch, off[ch]); } DBG(DBG_info, "%s: final offsets: %d,%d,%d\n", __func__, off[0], off[1], off[2]); if (dev->model->is_cis) { if (off[0] < off[1]) off[0] = off[1]; if (off[0] < off[2]) off[0] = off[2]; dev->frontend.set_offset(0, off[0]); dev->frontend.set_offset(1, off[0]); dev->frontend.set_offset(2, off[0]); } if (channels == 1) { dev->frontend.set_offset(1, dev->frontend.get_offset(0)); dev->frontend.set_offset(2, dev->frontend.get_offset(0)); } } /* alternative coarse gain calibration this on uses the settings from offset_calibration and uses only one scanline */ /* with offset and coarse calibration we only want to get our input range into a reasonable shape. the fine calibration of the upper and lower bounds will be done with shading. */ void CommandSetGl841::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs, int dpi) const { scanner_coarse_gain_calibration(*dev, sensor, regs, dpi); } // wait for lamp warmup by scanning the same line until difference // between 2 scans is below a threshold void CommandSetGl841::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* local_reg) const { DBG_HELPER(dbg); int num_pixels = 4 * 300; *local_reg = dev->reg; /* okay.. these should be defaults stored somewhere */ dev->frontend.set_gain(0, 0); dev->frontend.set_gain(1, 0); dev->frontend.set_gain(2, 0); dev->frontend.set_offset(0, 0x80); dev->frontend.set_offset(1, 0x80); dev->frontend.set_offset(2, 0x80); auto flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::SINGLE_LINE | ScanFlag::IGNORE_STAGGER_OFFSET | ScanFlag::IGNORE_COLOR_OFFSET; if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { flags |= ScanFlag::USE_XPA; } ScanSession session; session.params.xres = sensor.full_resolution; session.params.yres = dev->settings.yres; session.params.startx = sensor.dummy_pixel; session.params.starty = 0; session.params.pixels = num_pixels; session.params.lines = 1; session.params.depth = dev->model->bpp_color_values.front(); session.params.channels = 3; session.params.scan_method = dev->settings.scan_method; session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; session.params.color_filter = dev->settings.color_filter; session.params.contrast_adjustment = 0; session.params.brightness_adjustment = 0; session.params.flags = flags; compute_session(dev, session, sensor); init_regs_for_scan_session(dev, sensor, local_reg, session); } /* * initialize ASIC : registers, motor tables, and gamma tables * then ensure scanner's head is at home */ void CommandSetGl841::init(Genesys_Device* dev) const { DBG_INIT(); DBG_HELPER(dbg); sanei_genesys_asic_init(dev); } void CommandSetGl841::update_hardware_sensors(Genesys_Scanner* s) const { DBG_HELPER(dbg); // do what is needed to get a new set of events, but try to not lose any of them. std::uint8_t val; if (s->dev->model->gpio_id == GpioId::CANON_LIDE_35 || s->dev->model->gpio_id == GpioId::CANON_LIDE_80) { val = s->dev->interface->read_register(REG_0x6D); s->buttons[BUTTON_SCAN_SW].write((val & 0x01) == 0); s->buttons[BUTTON_FILE_SW].write((val & 0x02) == 0); s->buttons[BUTTON_EMAIL_SW].write((val & 0x04) == 0); s->buttons[BUTTON_COPY_SW].write((val & 0x08) == 0); } if (s->dev->model->gpio_id == GpioId::XP300 || s->dev->model->gpio_id == GpioId::DP665 || s->dev->model->gpio_id == GpioId::DP685) { val = s->dev->interface->read_register(REG_0x6D); s->buttons[BUTTON_PAGE_LOADED_SW].write((val & 0x01) == 0); s->buttons[BUTTON_SCAN_SW].write((val & 0x02) == 0); } } /** * Send shading calibration data. The buffer is considered to always hold values * for all the channels. */ void CommandSetGl841::send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t* data, int size) const { DBG_HELPER_ARGS(dbg, "writing %d bytes of shading data", size); std::uint32_t length, x, pixels, i; /* old method if no SHDAREA */ if ((dev->reg.find_reg(0x01).value & REG_0x01_SHDAREA) == 0) { // Note that this requires the sensor pixel offset to be exactly the same as to start // reading from dummy_pixel + 1 position. dev->interface->write_buffer(0x3c, 0x0000, data, size); return; } /* data is whole line, we extract only the part for the scanned area */ length = static_cast(size / 3); // turn pixel value into bytes 2x16 bits words pixels = dev->session.pixel_endx - dev->session.pixel_startx; pixels *= 4; // shading pixel begin is start pixel minus start pixel during shading // calibration. Currently only cases handled are full and half ccd resolution. unsigned beginpixel = dev->session.params.startx * dev->session.optical_resolution / dev->session.params.xres; beginpixel *= 4; beginpixel /= sensor.shading_factor; dev->interface->record_key_value("shading_offset", std::to_string(beginpixel)); dev->interface->record_key_value("shading_pixels", std::to_string(pixels)); dev->interface->record_key_value("shading_length", std::to_string(length)); DBG(DBG_io2, "%s: using chunks of %d bytes (%d shading data pixels)\n", __func__, length, length/4); std::vector buffer(pixels, 0); /* write actual shading data contigously * channel by channel, starting at addr 0x0000 * */ for(i=0;i<3;i++) { /* copy data to work buffer and process it */ /* coefficient destination */ std::uint8_t* ptr = buffer.data(); /* iterate on both sensor segment, data has been averaged, * so is in the right order and we only have to copy it */ for(x=0;xinterface->write_buffer(0x3c, 0x5400 * i, buffer.data(), pixels); } } bool CommandSetGl841::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const { (void) dev; return true; } void CommandSetGl841::wait_for_motor_stop(Genesys_Device* dev) const { (void) dev; } void CommandSetGl841::asic_boot(Genesys_Device *dev, bool cold) const { // reset ASIC in case of cold boot if (cold) { dev->interface->write_register(0x0e, 0x01); dev->interface->write_register(0x0e, 0x00); } gl841_init_registers(dev); // Write initial registers dev->interface->write_registers(dev->reg); // FIXME: 0x0b is not set, but on all other backends we do set it // dev->reg.remove_reg(0x0b); if (dev->model->model_id == ModelId::CANON_LIDE_60) { dev->interface->write_0x8c(0x10, 0xa4); } // FIXME: we probably don't need this const auto& sensor = sanei_genesys_find_sensor_any(dev); dev->cmd_set->set_fe(dev, sensor, AFE_INIT); } } // namespace gl841 } // namespace genesys backends-1.3.0/backend/genesys/gl841.h000066400000000000000000000073651456256263500173720ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2011-2013 Stéphane Voltz 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, see . */ #include "genesys.h" #include "command_set_common.h" #ifndef BACKEND_GENESYS_GL841_H #define BACKEND_GENESYS_GL841_H namespace genesys { namespace gl841 { class CommandSetGl841 : public CommandSetCommon { public: ~CommandSetGl841() override = default; bool needs_home_before_init_regs_for_scan(Genesys_Device* dev) const override; void init(Genesys_Device* dev) const override; void init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* regs) const override; void init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const override; void init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, const ScanSession& session) const override; void set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set) const override; void set_powersaving(Genesys_Device* dev, int delay) const override; void save_power(Genesys_Device* dev, bool enable) const override; void begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* regs, bool start_motor) const override; void end_scan(Genesys_Device* dev, Genesys_Register_Set* regs, bool check_stop) const override; void send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const override; void offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const override; void coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs, int dpi) const override; SensorExposure led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const override; void wait_for_motor_stop(Genesys_Device* dev) const override; void move_back_home(Genesys_Device* dev, bool wait_until_home) const override; void update_hardware_sensors(struct Genesys_Scanner* s) const override; void update_home_sensor_gpio(Genesys_Device& dev) const override; void load_document(Genesys_Device* dev) const override; void detect_document_end(Genesys_Device* dev) const override; void eject_document(Genesys_Device* dev) const override; void send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t* data, int size) const override; ScanSession calculate_scan_session(const Genesys_Device* dev, const Genesys_Sensor& sensor, const Genesys_Settings& settings) const override; void asic_boot(Genesys_Device* dev, bool cold) const override; }; } // namespace gl841 } // namespace genesys #endif // BACKEND_GENESYS_GL841_H backends-1.3.0/backend/genesys/gl841_registers.h000066400000000000000000000225221456256263500214510ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_GL841_REGISTERS_H #define BACKEND_GENESYS_GL841_REGISTERS_H #include namespace genesys { namespace gl841 { using RegAddr = std::uint16_t; using RegMask = std::uint8_t; using RegShift = unsigned; static constexpr RegAddr REG_0x01 = 0x01; static constexpr RegMask REG_0x01_CISSET = 0x80; static constexpr RegMask REG_0x01_DOGENB = 0x40; static constexpr RegMask REG_0x01_DVDSET = 0x20; static constexpr RegMask REG_0x01_M16DRAM = 0x08; static constexpr RegMask REG_0x01_DRAMSEL = 0x04; static constexpr RegMask REG_0x01_SHDAREA = 0x02; static constexpr RegMask REG_0x01_SCAN = 0x01; static constexpr RegAddr REG_0x02 = 0x02; static constexpr RegMask REG_0x02_NOTHOME = 0x80; static constexpr RegMask REG_0x02_ACDCDIS = 0x40; static constexpr RegMask REG_0x02_AGOHOME = 0x20; static constexpr RegMask REG_0x02_MTRPWR = 0x10; static constexpr RegMask REG_0x02_FASTFED = 0x08; static constexpr RegMask REG_0x02_MTRREV = 0x04; static constexpr RegMask REG_0x02_HOMENEG = 0x02; static constexpr RegMask REG_0x02_LONGCURV = 0x01; static constexpr RegMask REG_0x03_LAMPDOG = 0x80; static constexpr RegMask REG_0x03_AVEENB = 0x40; static constexpr RegMask REG_0x03_XPASEL = 0x20; static constexpr RegMask REG_0x03_LAMPPWR = 0x10; static constexpr RegMask REG_0x03_LAMPTIM = 0x0f; static constexpr RegMask REG_0x04_LINEART = 0x80; static constexpr RegMask REG_0x04_BITSET = 0x40; static constexpr RegMask REG_0x04_AFEMOD = 0x30; static constexpr RegMask REG_0x04_FILTER = 0x0c; static constexpr RegMask REG_0x04_FESET = 0x03; static constexpr RegShift REG_0x04S_AFEMOD = 4; static constexpr RegAddr REG_0x05 = 0x05; static constexpr RegMask REG_0x05_DPIHW = 0xc0; static constexpr RegMask REG_0x05_DPIHW_600 = 0x00; static constexpr RegMask REG_0x05_DPIHW_1200 = 0x40; static constexpr RegMask REG_0x05_DPIHW_2400 = 0x80; static constexpr RegMask REG_0x05_MTLLAMP = 0x30; static constexpr RegMask REG_0x05_GMMENB = 0x08; static constexpr RegMask REG_0x05_MTLBASE = 0x03; static constexpr RegAddr REG_0x06 = 0x06; static constexpr RegMask REG_0x06_SCANMOD = 0xe0; static constexpr RegShift REG_0x06S_SCANMOD = 5; static constexpr RegMask REG_0x06_PWRBIT = 0x10; static constexpr RegMask REG_0x06_GAIN4 = 0x08; static constexpr RegMask REG_0x06_OPTEST = 0x07; static constexpr RegMask REG_0x07_SRAMSEL = 0x08; static constexpr RegMask REG_0x07_FASTDMA = 0x04; static constexpr RegMask REG_0x07_DMASEL = 0x02; static constexpr RegMask REG_0x07_DMARDWR = 0x01; static constexpr RegMask REG_0x08_DECFLAG = 0x40; static constexpr RegMask REG_0x08_GMMFFR = 0x20; static constexpr RegMask REG_0x08_GMMFFG = 0x10; static constexpr RegMask REG_0x08_GMMFFB = 0x08; static constexpr RegMask REG_0x08_GMMZR = 0x04; static constexpr RegMask REG_0x08_GMMZG = 0x02; static constexpr RegMask REG_0x08_GMMZB = 0x01; static constexpr RegMask REG_0x09_MCNTSET = 0xc0; static constexpr RegMask REG_0x09_CLKSET = 0x30; static constexpr RegMask REG_0x09_BACKSCAN = 0x08; static constexpr RegMask REG_0x09_ENHANCE = 0x04; static constexpr RegMask REG_0x09_SHORTTG = 0x02; static constexpr RegMask REG_0x09_NWAIT = 0x01; static constexpr RegShift REG_0x09S_MCNTSET = 6; static constexpr RegShift REG_0x09S_CLKSET = 4; static constexpr RegMask REG_0x0A_SRAMBUF = 0x01; static constexpr RegAddr REG_0x0D = 0x0d; static constexpr RegMask REG_0x0D_CLRLNCNT = 0x01; static constexpr RegMask REG_0x16_CTRLHI = 0x80; static constexpr RegMask REG_0x16_TOSHIBA = 0x40; static constexpr RegMask REG_0x16_TGINV = 0x20; static constexpr RegMask REG_0x16_CK1INV = 0x10; static constexpr RegMask REG_0x16_CK2INV = 0x08; static constexpr RegMask REG_0x16_CTRLINV = 0x04; static constexpr RegMask REG_0x16_CKDIS = 0x02; static constexpr RegMask REG_0x16_CTRLDIS = 0x01; static constexpr RegMask REG_0x17_TGMODE = 0xc0; static constexpr RegMask REG_0x17_TGMODE_NO_DUMMY = 0x00; static constexpr RegMask REG_0x17_TGMODE_REF = 0x40; static constexpr RegMask REG_0x17_TGMODE_XPA = 0x80; static constexpr RegMask REG_0x17_TGW = 0x3f; static constexpr RegShift REG_0x17S_TGW = 0; static constexpr RegMask REG_0x18_CNSET = 0x80; static constexpr RegMask REG_0x18_DCKSEL = 0x60; static constexpr RegMask REG_0x18_CKTOGGLE = 0x10; static constexpr RegMask REG_0x18_CKDELAY = 0x0c; static constexpr RegMask REG_0x18_CKSEL = 0x03; static constexpr RegMask REG_0x1A_MANUAL3 = 0x02; static constexpr RegMask REG_0x1A_MANUAL1 = 0x01; static constexpr RegMask REG_0x1A_CK4INV = 0x08; static constexpr RegMask REG_0x1A_CK3INV = 0x04; static constexpr RegMask REG_0x1A_LINECLP = 0x02; static constexpr RegMask REG_0x1C_TGTIME = 0x07; static constexpr RegMask REG_0x1D_CK4LOW = 0x80; static constexpr RegMask REG_0x1D_CK3LOW = 0x40; static constexpr RegMask REG_0x1D_CK1LOW = 0x20; static constexpr RegMask REG_0x1D_TGSHLD = 0x1f; static constexpr RegShift REG_0x1DS_TGSHLD = 0; static constexpr RegAddr REG_0x1E = 0x1e; static constexpr RegMask REG_0x1E_WDTIME = 0xf0; static constexpr RegShift REG_0x1ES_WDTIME = 4; static constexpr RegMask REG_0x1E_LINESEL = 0x0f; static constexpr RegShift REG_0x1ES_LINESEL = 0; static constexpr RegAddr REG_EXPR = 0x10; static constexpr RegAddr REG_EXPG = 0x12; static constexpr RegAddr REG_EXPB = 0x14; static constexpr RegAddr REG_STEPNO = 0x21; static constexpr RegAddr REG_FWDSTEP = 0x22; static constexpr RegAddr REG_BWDSTEP = 0x23; static constexpr RegAddr REG_FASTNO = 0x24; static constexpr RegAddr REG_LINCNT = 0x25; static constexpr RegAddr REG_DPISET = 0x2c; static constexpr RegAddr REG_STRPIXEL = 0x30; static constexpr RegAddr REG_ENDPIXEL = 0x32; static constexpr RegAddr REG_MAXWD = 0x35; static constexpr RegAddr REG_LPERIOD = 0x38; static constexpr RegAddr REG_0x40 = 0x40; static constexpr RegMask REG_0x40_HISPDFLG = 0x04; static constexpr RegMask REG_0x40_MOTMFLG = 0x02; static constexpr RegMask REG_0x40_DATAENB = 0x01; static constexpr RegMask REG_0x41_PWRBIT = 0x80; static constexpr RegMask REG_0x41_BUFEMPTY = 0x40; static constexpr RegMask REG_0x41_FEEDFSH = 0x20; static constexpr RegMask REG_0x41_SCANFSH = 0x10; static constexpr RegMask REG_0x41_HOMESNR = 0x08; static constexpr RegMask REG_0x41_LAMPSTS = 0x04; static constexpr RegMask REG_0x41_FEBUSY = 0x02; static constexpr RegMask REG_0x41_MOTORENB = 0x01; static constexpr RegMask REG_0x58_VSMP = 0xf8; static constexpr RegShift REG_0x58S_VSMP = 3; static constexpr RegMask REG_0x58_VSMPW = 0x07; static constexpr RegShift REG_0x58S_VSMPW = 0; static constexpr RegMask REG_0x59_BSMP = 0xf8; static constexpr RegShift REG_0x59S_BSMP = 3; static constexpr RegMask REG_0x59_BSMPW = 0x07; static constexpr RegShift REG_0x59S_BSMPW = 0; static constexpr RegMask REG_0x5A_ADCLKINV = 0x80; static constexpr RegMask REG_0x5A_RLCSEL = 0x40; static constexpr RegMask REG_0x5A_CDSREF = 0x30; static constexpr RegShift REG_0x5AS_CDSREF = 4; static constexpr RegMask REG_0x5A_RLC = 0x0f; static constexpr RegShift REG_0x5AS_RLC = 0; static constexpr RegMask REG_0x5E_DECSEL = 0xe0; static constexpr RegShift REG_0x5ES_DECSEL = 5; static constexpr RegMask REG_0x5E_STOPTIM = 0x1f; static constexpr RegShift REG_0x5ES_STOPTIM = 0; static constexpr RegAddr REG_0x60 = 0x60; static constexpr RegMask REG_0x60_ZIMOD = 0x1f; static constexpr RegMask REG_0x61_Z1MOD = 0xff; static constexpr RegMask REG_0x62_Z1MOD = 0xff; static constexpr RegAddr REG_0x63 = 0x63; static constexpr RegMask REG_0x63_Z2MOD = 0x1f; static constexpr RegMask REG_0x64_Z2MOD = 0xff; static constexpr RegMask REG_0x65_Z2MOD = 0xff; static constexpr RegMask REG_0x67_STEPSEL = 0xc0; static constexpr RegMask REG_0x67_FULLSTEP = 0x00; static constexpr RegMask REG_0x67_HALFSTEP = 0x40; static constexpr RegMask REG_0x67_QUATERSTEP = 0x80; static constexpr RegMask REG_0x67_MTRPWM = 0x3f; static constexpr RegMask REG_0x68_FSTPSEL = 0xc0; static constexpr RegMask REG_0x68_FULLSTEP = 0x00; static constexpr RegMask REG_0x68_HALFSTEP = 0x40; static constexpr RegMask REG_0x68_QUATERSTEP = 0x80; static constexpr RegMask REG_0x68_FASTPWM = 0x3f; static constexpr RegMask REG_0x6B_MULTFILM = 0x80; static constexpr RegMask REG_0x6B_GPOM13 = 0x40; static constexpr RegMask REG_0x6B_GPOM12 = 0x20; static constexpr RegMask REG_0x6B_GPOM11 = 0x10; static constexpr RegMask REG_0x6B_GPO18 = 0x02; static constexpr RegMask REG_0x6B_GPO17 = 0x01; static constexpr RegAddr REG_0x6B = 0x6b; static constexpr RegAddr REG_0x6C = 0x6c; static constexpr RegMask REG_0x6C_GPIOH = 0xff; static constexpr RegMask REG_0x6C_GPIOL = 0xff; static constexpr RegAddr REG_0x6D = 0x6d; static constexpr RegAddr REG_0x6E = 0x6e; static constexpr RegAddr REG_0x6F = 0x6f; static constexpr RegMask REG_0x87_LEDADD = 0x04; } // namespace gl841 } // namespace genesys #endif // BACKEND_GENESYS_GL841_REGISTERS_H backends-1.3.0/backend/genesys/gl842.cpp000066400000000000000000001137431456256263500177240ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2010-2013 Stéphane Voltz Copyright (C) 2020 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "gl842_registers.h" #include "gl842.h" #include "test_settings.h" #include #include namespace genesys { namespace gl842 { static void gl842_init_registers(Genesys_Device& dev) { // Within this function SENSOR_DEF marker documents that a register is part // of the sensors definition and the actual value is set in // gl842_setup_sensor(). DBG_HELPER(dbg); dev.reg.clear(); if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { dev.reg.init_reg(0x01, 0x00); dev.reg.init_reg(0x02, 0x78); dev.reg.init_reg(0x03, 0xbf); dev.reg.init_reg(0x04, 0x22); dev.reg.init_reg(0x05, 0x48); dev.reg.init_reg(0x06, 0xb8); dev.reg.init_reg(0x07, 0x00); dev.reg.init_reg(0x08, 0x00); dev.reg.init_reg(0x09, 0x00); dev.reg.init_reg(0x0a, 0x00); dev.reg.init_reg(0x0d, 0x01); } else if (dev.model->model_id == ModelId::CANON_LIDE_90) { dev.reg.init_reg(0x01, 0x82); dev.reg.init_reg(0x02, 0x10); dev.reg.init_reg(0x03, 0x60); dev.reg.init_reg(0x04, 0x10); dev.reg.init_reg(0x05, 0x8c); dev.reg.init_reg(0x06, 0x18); //dev.reg.init_reg(0x07, 0x00); dev.reg.init_reg(0x08, 0x00); dev.reg.init_reg(0x09, 0x21); dev.reg.init_reg(0x0a, 0x00); dev.reg.init_reg(0x0d, 0x00); } dev.reg.init_reg(0x10, 0x00); // exposure, overwritten in scanner_setup_sensor() below dev.reg.init_reg(0x11, 0x00); // exposure, overwritten in scanner_setup_sensor() below dev.reg.init_reg(0x12, 0x00); // exposure, overwritten in scanner_setup_sensor() below dev.reg.init_reg(0x13, 0x00); // exposure, overwritten in scanner_setup_sensor() below dev.reg.init_reg(0x14, 0x00); // exposure, overwritten in scanner_setup_sensor() below dev.reg.init_reg(0x15, 0x00); // exposure, overwritten in scanner_setup_sensor() below // CCD signal settings. dev.reg.init_reg(0x16, 0x00); // SENSOR_DEF dev.reg.init_reg(0x17, 0x00); // SENSOR_DEF dev.reg.init_reg(0x18, 0x00); // SENSOR_DEF // EXPDMY[0:7]: Exposure time of dummy lines. dev.reg.init_reg(0x19, 0x00); // SENSOR_DEF // Various CCD clock settings. dev.reg.init_reg(0x1a, 0x00); // SENSOR_DEF if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { dev.reg.init_reg(0x1b, 0x00); // SENSOR_DEF } dev.reg.init_reg(0x1c, 0x00); // SENSOR_DEF dev.reg.init_reg(0x1d, 0x00); // SENSOR_DEF dev.reg.init_reg(0x1e, 0x10); // WDTIME, LINESEL: setup during sensor and motor setup if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { dev.reg.init_reg(0x1f, 0x01); dev.reg.init_reg(0x20, 0x27); // BUFSEL: buffer full condition } else if (dev.model->model_id == ModelId::CANON_LIDE_90) { dev.reg.init_reg(0x1f, 0x02); dev.reg.init_reg(0x20, 0x02); // BUFSEL: buffer full condition } dev.reg.init_reg(0x21, 0x10); // STEPNO: set during motor setup dev.reg.init_reg(0x22, 0x10); // FWDSTEP: set during motor setup dev.reg.init_reg(0x23, 0x10); // BWDSTEP: set during motor setup dev.reg.init_reg(0x24, 0x10); // FASTNO: set during motor setup dev.reg.init_reg(0x25, 0x00); // LINCNT: set during motor setup dev.reg.init_reg(0x26, 0x00); // LINCNT: set during motor setup dev.reg.init_reg(0x27, 0x00); // LINCNT: set during motor setup dev.reg.init_reg(0x29, 0xff); // LAMPPWM dev.reg.init_reg(0x2c, 0x02); // DPISET: set during sensor setup dev.reg.init_reg(0x2d, 0x58); // DPISET: set during sensor setup dev.reg.init_reg(0x2e, 0x80); // BWHI: black/white low threshdold dev.reg.init_reg(0x2f, 0x80); // BWLOW: black/white low threshold dev.reg.init_reg(0x30, 0x00); // STRPIXEL: set during sensor setup dev.reg.init_reg(0x31, 0x49); // STRPIXEL: set during sensor setup dev.reg.init_reg(0x32, 0x53); // ENDPIXEL: set during sensor setup dev.reg.init_reg(0x33, 0xb9); // ENDPIXEL: set during sensor setup dev.reg.init_reg(0x34, 0x13); // DUMMY: SENSOR_DEF dev.reg.init_reg(0x35, 0x00); // MAXWD: set during scan setup dev.reg.init_reg(0x36, 0x40); // MAXWD: set during scan setup dev.reg.init_reg(0x37, 0x00); // MAXWD: set during scan setup dev.reg.init_reg(0x38, 0x2a); // LPERIOD: SENSOR_DEF dev.reg.init_reg(0x39, 0xf8); // LPERIOD: SENSOR_DEF dev.reg.init_reg(0x3d, 0x00); // FEEDL: set during motor setup dev.reg.init_reg(0x3e, 0x00); // FEEDL: set during motor setup dev.reg.init_reg(0x3f, 0x01); // FEEDL: set during motor setup dev.reg.init_reg(0x52, 0x00); // SENSOR_DEF dev.reg.init_reg(0x53, 0x00); // SENSOR_DEF dev.reg.init_reg(0x54, 0x00); // SENSOR_DEF dev.reg.init_reg(0x55, 0x00); // SENSOR_DEF dev.reg.init_reg(0x56, 0x00); // SENSOR_DEF dev.reg.init_reg(0x57, 0x00); // SENSOR_DEF dev.reg.init_reg(0x58, 0x00); // SENSOR_DEF dev.reg.init_reg(0x59, 0x00); // SENSOR_DEF dev.reg.init_reg(0x5a, 0x00); // SENSOR_DEF if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { dev.reg.init_reg(0x5e, 0x01); // DECSEL, STOPTIM } else if (dev.model->model_id == ModelId::CANON_LIDE_90) { dev.reg.init_reg(0x5e, 0x41); // DECSEL, STOPTIM dev.reg.init_reg(0x5d, 0x20); } dev.reg.init_reg(0x5f, 0x10); // FMOVDEC: set during motor setup dev.reg.init_reg(0x60, 0x00); // Z1MOD: overwritten during motor setup dev.reg.init_reg(0x61, 0x00); // Z1MOD: overwritten during motor setup dev.reg.init_reg(0x62, 0x00); // Z1MOD: overwritten during motor setup dev.reg.init_reg(0x63, 0x00); // Z2MOD: overwritten during motor setup dev.reg.init_reg(0x64, 0x00); // Z2MOD: overwritten during motor setup dev.reg.init_reg(0x65, 0x00); // Z2MOD: overwritten during motor setup if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { dev.reg.init_reg(0x67, 0x7f); // STEPSEL, MTRPWM: partially overwritten during motor setup dev.reg.init_reg(0x68, 0x7f); // FSTPSEL, FASTPWM: partially overwritten during motor setup } else if (dev.model->model_id == ModelId::CANON_LIDE_90) { dev.reg.init_reg(0x66, 0x00); // PHFREQ dev.reg.init_reg(0x67, 0x40); // STEPSEL, MTRPWM: partially overwritten during motor setup dev.reg.init_reg(0x68, 0x40); // FSTPSEL, FASTPWM: partially overwritten during motor setup } dev.reg.init_reg(0x69, 0x10); // FSHDEC: overwritten during motor setup dev.reg.init_reg(0x6a, 0x10); // FMOVNO: overwritten during motor setup // 0x6b, 0x6c, 0x6d, 0x6e, 0x6f - set according to gpio tables. See gl842_init_gpio. dev.reg.init_reg(0x70, 0x00); // SENSOR_DEF dev.reg.init_reg(0x71, 0x00); // SENSOR_DEF dev.reg.init_reg(0x72, 0x00); // SENSOR_DEF dev.reg.init_reg(0x73, 0x00); // SENSOR_DEF dev.reg.init_reg(0x74, 0x00); // SENSOR_DEF dev.reg.init_reg(0x75, 0x00); // SENSOR_DEF dev.reg.init_reg(0x76, 0x00); // SENSOR_DEF dev.reg.init_reg(0x77, 0x00); // SENSOR_DEF dev.reg.init_reg(0x78, 0x00); // SENSOR_DEF dev.reg.init_reg(0x79, 0x00); // SENSOR_DEF dev.reg.init_reg(0x7a, 0x00); // SENSOR_DEF dev.reg.init_reg(0x7b, 0x00); // SENSOR_DEF dev.reg.init_reg(0x7c, 0x00); // SENSOR_DEF dev.reg.init_reg(0x7d, 0x00); // SENSOR_DEF // 0x7e - set according to gpio tables. See gl842_init_gpio. dev.reg.init_reg(0x7f, 0x00); // SENSOR_DEF // VRHOME, VRMOVE, VRBACK, VRSCAN: Vref settings of the motor driver IC for // moving in various situations. dev.reg.init_reg(0x80, 0x00); // MOTOR_PROFILE if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { dev.reg.init_reg(0x81, 0x00); dev.reg.init_reg(0x82, 0x00); dev.reg.init_reg(0x83, 0x00); dev.reg.init_reg(0x84, 0x00); dev.reg.init_reg(0x85, 0x00); dev.reg.init_reg(0x86, 0x00); dev.reg.init_reg(0x87, 0x00); } else if (dev.model->model_id == ModelId::CANON_LIDE_90) { dev.reg.init_reg(0x7e, 0x00); dev.reg.init_reg(0x81, 0x00); dev.reg.init_reg(0x82, 0x0f); dev.reg.init_reg(0x83, 0x00); dev.reg.init_reg(0x84, 0x0e); dev.reg.init_reg(0x85, 0x00); dev.reg.init_reg(0x86, 0x0d); dev.reg.init_reg(0x87, 0x00); dev.reg.init_reg(0x88, 0x00); dev.reg.init_reg(0x89, 0x00); } const auto& sensor = sanei_genesys_find_sensor_any(&dev); sanei_genesys_set_dpihw(dev.reg, sensor.register_dpihw); scanner_setup_sensor(dev, sensor, dev.reg); } // Set values of analog frontend void CommandSetGl842::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set) const { DBG_HELPER_ARGS(dbg, "%s", set == AFE_INIT ? "init" : set == AFE_SET ? "set" : set == AFE_POWER_SAVE ? "powersave" : "huh?"); (void) sensor; if (set == AFE_INIT) { dev->frontend = dev->frontend_initial; } // check analog frontend type // FIXME: looks like we write to that register with initial data std::uint8_t fe_type = dev->interface->read_register(REG_0x04) & REG_0x04_FESET; if (fe_type == 2 || dev->model->model_id == ModelId::CANON_LIDE_90) { for (const auto& reg : dev->frontend.regs) { dev->interface->write_fe_register(reg.address, reg.value); } return; } if (fe_type != 0) { throw SaneException(SANE_STATUS_UNSUPPORTED, "unsupported frontend type %d", fe_type); } for (unsigned i = 1; i <= 3; i++) { dev->interface->write_fe_register(i, dev->frontend.regs.get_value(0x00 + i)); } for (const auto& reg : sensor.custom_fe_regs) { dev->interface->write_fe_register(reg.address, reg.value); } for (unsigned i = 0; i < 3; i++) { dev->interface->write_fe_register(0x20 + i, dev->frontend.get_offset(i)); } for (unsigned i = 0; i < 3; i++) { dev->interface->write_fe_register(0x28 + i, dev->frontend.get_gain(i)); } } static void gl842_init_motor_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, const ScanSession& session, Genesys_Register_Set* reg, const MotorProfile& motor_profile, unsigned int exposure, unsigned scan_yres, unsigned int scan_lines, unsigned int scan_dummy, unsigned int feed_steps, ScanFlag flags) { DBG_HELPER_ARGS(dbg, "exposure=%d, scan_yres=%d, step_type=%d, scan_lines=%d, scan_dummy=%d, " "feed_steps=%d, flags=%x", exposure, scan_yres, static_cast(motor_profile.step_type), scan_lines, scan_dummy, feed_steps, static_cast(flags)); unsigned step_multiplier = 2; bool use_fast_fed = false; if ((scan_yres >= 300 && feed_steps > 900) || (has_flag(flags, ScanFlag::FEEDING))) { use_fast_fed = true; } if (has_flag(dev->model->flags, ModelFlag::DISABLE_FAST_FEEDING)) { use_fast_fed = false; } reg->set24(REG_LINCNT, scan_lines); reg->set8(REG_0x02, 0); sanei_genesys_set_motor_power(*reg, true); std::uint8_t reg02 = reg->get8(REG_0x02); if (use_fast_fed) { reg02 |= REG_0x02_FASTFED; } else { reg02 &= ~REG_0x02_FASTFED; } // in case of automatic go home, move until home sensor if (has_flag(flags, ScanFlag::AUTO_GO_HOME)) { reg02 |= REG_0x02_AGOHOME | REG_0x02_NOTHOME; } // disable backtracking if needed if (has_flag(flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE) || (scan_yres >= 2400) || (scan_yres >= sensor.full_resolution)) { reg02 |= REG_0x02_ACDCDIS; } if (has_flag(flags, ScanFlag::REVERSE)) { reg02 |= REG_0x02_MTRREV; } else { reg02 &= ~REG_0x02_MTRREV; } reg->set8(REG_0x02, reg02); // scan and backtracking slope table auto scan_table = create_slope_table(dev->model->asic_type, dev->motor, scan_yres, exposure, step_multiplier, motor_profile); scanner_send_slope_table(dev, sensor, SCAN_TABLE, scan_table.table); scanner_send_slope_table(dev, sensor, BACKTRACK_TABLE, scan_table.table); scanner_send_slope_table(dev, sensor, STOP_TABLE, scan_table.table); reg->set8(REG_STEPNO, scan_table.table.size() / step_multiplier); reg->set8(REG_FASTNO, scan_table.table.size() / step_multiplier); reg->set8(REG_FSHDEC, scan_table.table.size() / step_multiplier); // fast table const auto* fast_profile = get_motor_profile_ptr(dev->motor.fast_profiles, 0, session); if (fast_profile == nullptr) { fast_profile = &motor_profile; } auto fast_table = create_slope_table_fastest(dev->model->asic_type, step_multiplier, *fast_profile); scanner_send_slope_table(dev, sensor, FAST_TABLE, fast_table.table); scanner_send_slope_table(dev, sensor, HOME_TABLE, fast_table.table); reg->set8(REG_FMOVNO, fast_table.table.size() / step_multiplier); if (motor_profile.motor_vref != -1 && fast_profile->motor_vref != 1) { std::uint8_t vref = 0; vref |= (motor_profile.motor_vref << REG_0x80S_TABLE1_NORMAL) & REG_0x80_TABLE1_NORMAL; vref |= (motor_profile.motor_vref << REG_0x80S_TABLE2_BACK) & REG_0x80_TABLE2_BACK; vref |= (fast_profile->motor_vref << REG_0x80S_TABLE4_FAST) & REG_0x80_TABLE4_FAST; vref |= (fast_profile->motor_vref << REG_0x80S_TABLE5_GO_HOME) & REG_0x80_TABLE5_GO_HOME; reg->set8(REG_0x80, vref); } // subtract acceleration distance from feedl unsigned feedl = feed_steps; feedl <<= static_cast(motor_profile.step_type); unsigned dist = scan_table.table.size() / step_multiplier; if (use_fast_fed) { dist += (fast_table.table.size() / step_multiplier) * 2; } // make sure when don't insane value : XXX STEF XXX in this case we should // fall back to single table move if (dist < feedl) { feedl -= dist; } else { feedl = 1; } reg->set24(REG_FEEDL, feedl); // doesn't seem to matter that much std::uint32_t z1, z2; sanei_genesys_calculate_zmod(use_fast_fed, exposure, scan_table.table, scan_table.table.size() / step_multiplier, feedl, scan_table.table.size() / step_multiplier, &z1, &z2); if (scan_yres > 600) { z1 = 0; z2 = 0; } reg->set24(REG_Z1MOD, z1); reg->set24(REG_Z2MOD, z2); reg->set8_mask(REG_0x1E, scan_dummy, 0x0f); reg->set8_mask(REG_0x67, static_cast(motor_profile.step_type) << REG_0x67S_STEPSEL, REG_0x67_STEPSEL); reg->set8_mask(REG_0x68, static_cast(fast_profile->step_type) << REG_0x68S_FSTPSEL, REG_0x68_FSTPSEL); // steps for STOP table reg->set8(REG_FMOVDEC, fast_table.table.size() / step_multiplier); } static void gl842_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, unsigned int exposure, const ScanSession& session) { DBG_HELPER(dbg); scanner_setup_sensor(*dev, sensor, *reg); dev->cmd_set->set_fe(dev, sensor, AFE_SET); // enable shading regs_set_optical_off(dev->model->asic_type, *reg); if (has_flag(session.params.flags, ScanFlag::DISABLE_SHADING) || has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION) || session.use_host_side_calib) { reg->find_reg(REG_0x01).value &= ~REG_0x01_DVDSET; } else { reg->find_reg(REG_0x01).value |= REG_0x01_DVDSET; } bool use_shdarea = true; if (use_shdarea) { reg->find_reg(REG_0x01).value |= REG_0x01_SHDAREA; } else { reg->find_reg(REG_0x01).value &= ~REG_0x01_SHDAREA; } if (dev->model->model_id == ModelId::CANON_8600F) { reg->find_reg(REG_0x03).value |= REG_0x03_AVEENB; } else { reg->find_reg(REG_0x03).value &= ~REG_0x03_AVEENB; } // FIXME: we probably don't need to set exposure to registers at this point. It was this way // before a refactor. sanei_genesys_set_lamp_power(dev, sensor, *reg, !has_flag(session.params.flags, ScanFlag::DISABLE_LAMP)); // select XPA reg->find_reg(REG_0x03).value &= ~REG_0x03_XPASEL; if (has_flag(session.params.flags, ScanFlag::USE_XPA)) { reg->find_reg(REG_0x03).value |= REG_0x03_XPASEL; } reg->state.is_xpa_on = has_flag(session.params.flags, ScanFlag::USE_XPA); // BW threshold reg->set8(REG_0x2E, 0x7f); reg->set8(REG_0x2F, 0x7f); // monochrome / color scan parameters std::uint8_t reg04 = reg->get8(REG_0x04); reg04 = reg04 & REG_0x04_FESET; switch (session.params.depth) { case 8: break; case 16: reg04 |= REG_0x04_BITSET; break; } if (session.params.channels == 1) { switch (session.params.color_filter) { case ColorFilter::RED: reg04 |= 0x14; break; case ColorFilter::BLUE: reg04 |= 0x1c; break; case ColorFilter::GREEN: reg04 |= 0x18; break; default: break; // should not happen } } else { switch (dev->frontend.layout.type) { case FrontendType::WOLFSON: // pixel by pixel reg04 |= 0x10; break; case FrontendType::ANALOG_DEVICES: // slow color pixel by pixel reg04 |= 0x20; break; default: throw SaneException("Invalid frontend type %d", static_cast(dev->frontend.layout.type)); } } reg->set8(REG_0x04, reg04); const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, session.output_resolution, session.params.channels, session.params.scan_method); sanei_genesys_set_dpihw(*reg, dpihw_sensor.register_dpihw); if (should_enable_gamma(session, sensor)) { reg->find_reg(REG_0x05).value |= REG_0x05_GMMENB; } else { reg->find_reg(REG_0x05).value &= ~REG_0x05_GMMENB; } reg->set16(REG_DPISET, sensor.register_dpiset); reg->set16(REG_STRPIXEL, session.pixel_startx); reg->set16(REG_ENDPIXEL, session.pixel_endx); if (dev->model->is_cis) { reg->set24(REG_MAXWD, session.output_line_bytes_raw * session.params.channels); } else { reg->set24(REG_MAXWD, session.output_line_bytes_raw); } unsigned tgtime = exposure / 65536 + 1; reg->set16(REG_LPERIOD, exposure / tgtime); reg->set8(REG_DUMMY, sensor.dummy_pixel); } void CommandSetGl842::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, const ScanSession& session) const { DBG_HELPER(dbg); session.assert_computed(); // we enable true gray for cis scanners only, and just when doing scan since color calibration // is OK for this mode int dummy = 0; /* slope_dpi */ /* cis color scan is effectively a gray scan with 3 gray lines per color line and a FILTER of 0 */ int slope_dpi = 0; if (dev->model->is_cis) { slope_dpi = session.params.yres * session.params.channels; } else { slope_dpi = session.params.yres; } slope_dpi = slope_dpi * (1 + dummy); int exposure = sensor.exposure_lperiod; if (exposure < 0) { throw std::runtime_error("Exposure not defined in sensor definition"); } if (dev->model->model_id == ModelId::CANON_LIDE_90) { exposure *= 2; } const auto& motor_profile = get_motor_profile(dev->motor.profiles, exposure, session); // now _LOGICAL_ optical values used are known, setup registers gl842_init_optical_regs_scan(dev, sensor, reg, exposure, session); gl842_init_motor_regs_scan(dev, sensor, session, reg, motor_profile, exposure, slope_dpi, session.optical_line_count, dummy, session.params.starty, session.params.flags); setup_image_pipeline(*dev, session); dev->read_active = true; dev->session = session; dev->total_bytes_read = 0; dev->total_bytes_to_read = (size_t)session.output_line_bytes_requested * (size_t)session.params.lines; } ScanSession CommandSetGl842::calculate_scan_session(const Genesys_Device* dev, const Genesys_Sensor& sensor, const Genesys_Settings& settings) const { DBG_HELPER(dbg); debug_dump(DBG_info, settings); ScanFlag flags = ScanFlag::NONE; float move = 0.0f; if (settings.scan_method == ScanMethod::TRANSPARENCY || settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { // note: scanner_move_to_ta() function has already been called and the sensor is at the // transparency adapter if (!dev->ignore_offsets) { move = dev->model->y_offset_ta - dev->model->y_offset_sensor_to_ta; } flags |= ScanFlag::USE_XPA; } else { if (!dev->ignore_offsets) { move = dev->model->y_offset; } } move += settings.tl_y; int move_dpi = dev->motor.base_ydpi; move = static_cast((move * move_dpi) / MM_PER_INCH); float start = 0.0f; if (settings.scan_method==ScanMethod::TRANSPARENCY || settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { start = dev->model->x_offset_ta; } else { start = dev->model->x_offset; } start = start + settings.tl_x; start = static_cast((start * settings.xres) / MM_PER_INCH); ScanSession session; session.params.xres = settings.xres; session.params.yres = settings.yres; session.params.startx = static_cast(start); session.params.starty = static_cast(move); session.params.pixels = settings.pixels; session.params.requested_pixels = settings.requested_pixels; session.params.lines = settings.lines; session.params.depth = settings.depth; session.params.channels = settings.get_channels(); session.params.scan_method = settings.scan_method; session.params.scan_mode = settings.scan_mode; session.params.color_filter = settings.color_filter; session.params.contrast_adjustment = settings.contrast; session.params.brightness_adjustment = settings.brightness; session.params.flags = flags; compute_session(dev, session, sensor); return session; } void CommandSetGl842::save_power(Genesys_Device* dev, bool enable) const { (void) dev; DBG_HELPER_ARGS(dbg, "enable = %d", enable); } void CommandSetGl842::set_powersaving(Genesys_Device* dev, int delay /* in minutes */) const { (void) dev; DBG_HELPER_ARGS(dbg, "delay = %d", delay); } void CommandSetGl842::eject_document(Genesys_Device* dev) const { (void) dev; DBG_HELPER(dbg); } void CommandSetGl842::load_document(Genesys_Device* dev) const { DBG_HELPER(dbg); (void) dev; } void CommandSetGl842::detect_document_end(Genesys_Device* dev) const { DBG_HELPER(dbg); (void) dev; throw SaneException(SANE_STATUS_UNSUPPORTED); } // Send the low-level scan command void CommandSetGl842::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, bool start_motor) const { DBG_HELPER(dbg); (void) sensor; if (reg->state.is_xpa_on && reg->state.is_lamp_on && !has_flag(dev->model->flags, ModelFlag::TA_NO_SECONDARY_LAMP)) { dev->cmd_set->set_xpa_lamp_power(*dev, true); } if (reg->state.is_xpa_on && !has_flag(dev->model->flags, ModelFlag::UTA_NO_SECONDARY_MOTOR)) { dev->cmd_set->set_motor_mode(*dev, *reg, MotorMode::PRIMARY_AND_SECONDARY); } if (dev->model->model_id == ModelId::CANON_LIDE_90) { if (has_flag(dev->session.params.flags, ScanFlag::REVERSE)) { dev->interface->write_register(REG_0x6B, 0x01); dev->interface->write_register(REG_0x6C, 0x02); } else { dev->interface->write_register(REG_0x6B, 0x03); switch (dev->session.params.xres) { case 150: dev->interface->write_register(REG_0x6C, 0x74); break; case 300: dev->interface->write_register(REG_0x6C, 0x38); break; case 600: dev->interface->write_register(REG_0x6C, 0x1c); break; case 1200: dev->interface->write_register(REG_0x6C, 0x2c); break; case 2400: dev->interface->write_register(REG_0x6C, 0x0c); break; default: break; } } dev->interface->sleep_ms(100); } scanner_clear_scan_and_feed_counts(*dev); // enable scan and motor std::uint8_t val = dev->interface->read_register(REG_0x01); val |= REG_0x01_SCAN; dev->interface->write_register(REG_0x01, val); scanner_start_action(*dev, start_motor); switch (reg->state.motor_mode) { case MotorMode::PRIMARY: { if (reg->state.is_motor_on) { dev->advance_head_pos_by_session(ScanHeadId::PRIMARY); } break; } case MotorMode::PRIMARY_AND_SECONDARY: { if (reg->state.is_motor_on) { dev->advance_head_pos_by_session(ScanHeadId::PRIMARY); dev->advance_head_pos_by_session(ScanHeadId::SECONDARY); } break; } case MotorMode::SECONDARY: { if (reg->state.is_motor_on) { dev->advance_head_pos_by_session(ScanHeadId::SECONDARY); } break; } } } void CommandSetGl842::end_scan(Genesys_Device* dev, Genesys_Register_Set* reg, bool check_stop) const { DBG_HELPER_ARGS(dbg, "check_stop = %d", check_stop); if (reg->state.is_xpa_on) { dev->cmd_set->set_xpa_lamp_power(*dev, false); } if (!dev->model->is_sheetfed) { scanner_stop_action(*dev); } } void CommandSetGl842::move_back_home(Genesys_Device* dev, bool wait_until_home) const { scanner_move_back_home(*dev, wait_until_home); } void CommandSetGl842::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { DBG_HELPER(dbg); int move; float calib_size_mm = 0; if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { calib_size_mm = dev->model->y_size_calib_ta_mm; } else { calib_size_mm = dev->model->y_size_calib_mm; } unsigned resolution = sensor.shading_resolution; unsigned channels = 3; const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, dev->settings.scan_method); unsigned calib_pixels = 0; unsigned calib_pixels_offset = 0; if (should_calibrate_only_active_area(*dev, dev->settings)) { float offset = dev->model->x_offset_ta; // FIXME: we should use resolution here offset = static_cast((offset * dev->settings.xres) / MM_PER_INCH); float size = dev->model->x_size_ta; size = static_cast((size * dev->settings.xres) / MM_PER_INCH); calib_pixels_offset = static_cast(offset); calib_pixels = static_cast(size); } else { calib_pixels_offset = 0; calib_pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; } ScanFlag flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::DISABLE_BUFFER_FULL_MOVE; if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { // note: scanner_move_to_ta() function has already been called and the sensor is at the // transparency adapter move = static_cast(dev->model->y_offset_calib_white_ta - dev->model->y_offset_sensor_to_ta); flags |= ScanFlag::USE_XPA; } else { move = static_cast(dev->model->y_offset_calib_white); } move = static_cast((move * resolution) / MM_PER_INCH); unsigned calib_lines = static_cast(calib_size_mm * resolution / MM_PER_INCH); ScanSession session; session.params.xres = resolution; session.params.yres = resolution; session.params.startx = calib_pixels_offset; session.params.starty = move; session.params.pixels = calib_pixels; session.params.lines = calib_lines; session.params.depth = 16; session.params.channels = channels; session.params.scan_method = dev->settings.scan_method; session.params.scan_mode = dev->settings.scan_mode; session.params.color_filter = dev->settings.color_filter; session.params.contrast_adjustment = dev->settings.contrast; session.params.brightness_adjustment = dev->settings.brightness; session.params.flags = flags; compute_session(dev, session, calib_sensor); init_regs_for_scan_session(dev, calib_sensor, ®s, session); dev->calib_session = session; } void CommandSetGl842::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const { DBG_HELPER(dbg); if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) return; // No gamma on this model unsigned size = 256; std::vector gamma(size * 2 * 3); std::vector rgamma = get_gamma_table(dev, sensor, GENESYS_RED); std::vector ggamma = get_gamma_table(dev, sensor, GENESYS_GREEN); std::vector bgamma = get_gamma_table(dev, sensor, GENESYS_BLUE); // copy sensor specific's gamma tables for (unsigned i = 0; i < size; i++) { gamma[i * 2 + size * 0 + 0] = rgamma[i] & 0xff; gamma[i * 2 + size * 0 + 1] = (rgamma[i] >> 8) & 0xff; gamma[i * 2 + size * 2 + 0] = ggamma[i] & 0xff; gamma[i * 2 + size * 2 + 1] = (ggamma[i] >> 8) & 0xff; gamma[i * 2 + size * 4 + 0] = bgamma[i] & 0xff; gamma[i * 2 + size * 4 + 1] = (bgamma[i] >> 8) & 0xff; } dev->interface->write_gamma(0x28, 0x0000, gamma.data(), size * 2 * 3); } SensorExposure CommandSetGl842::led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { return scanner_led_calibration(*dev, sensor, regs); } void CommandSetGl842::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { scanner_offset_calibration(*dev, sensor, regs); } void CommandSetGl842::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs, int dpi) const { scanner_coarse_gain_calibration(*dev, sensor, regs, dpi); } void CommandSetGl842::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg) const { DBG_HELPER(dbg); (void) sensor; unsigned channels = 3; unsigned resolution = dev->model->get_resolution_settings(dev->settings.scan_method) .get_nearest_resolution_x(600); const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, dev->settings.scan_method); unsigned num_pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH / 2; *reg = dev->reg; auto flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::SINGLE_LINE | ScanFlag::IGNORE_STAGGER_OFFSET | ScanFlag::IGNORE_COLOR_OFFSET; if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { flags |= ScanFlag::USE_XPA; } ScanSession session; session.params.xres = resolution; session.params.yres = resolution; session.params.startx = (num_pixels / 2) * resolution / calib_sensor.full_resolution; session.params.starty = 0; session.params.pixels = num_pixels; session.params.lines = 1; session.params.depth = dev->model->bpp_color_values.front(); session.params.channels = channels; session.params.scan_method = dev->settings.scan_method; session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; session.params.color_filter = dev->settings.color_filter; session.params.contrast_adjustment = 0; session.params.brightness_adjustment = 0; session.params.flags = flags; compute_session(dev, session, calib_sensor); init_regs_for_scan_session(dev, calib_sensor, reg, session); sanei_genesys_set_motor_power(*reg, false); } static void gl842_init_gpio(Genesys_Device* dev) { DBG_HELPER(dbg); apply_registers_ordered(dev->gpo.regs, { 0x6e, 0x6f }, [&](const GenesysRegisterSetting& reg) { dev->interface->write_register(reg.address, reg.value); }); } void CommandSetGl842::asic_boot(Genesys_Device* dev, bool cold) const { DBG_HELPER(dbg); if (cold) { dev->interface->write_register(0x0e, 0x01); dev->interface->write_register(0x0e, 0x00); } // setup initial register values gl842_init_registers(*dev); dev->interface->write_registers(dev->reg); if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { std::uint8_t data[32] = { 0xd0, 0x38, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x73, 0x63, 0x68, 0x69, 0x65, 0x6e, 0x00, }; dev->interface->write_buffer(0x3c, 0x010a00, data, 32); } if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { dev->interface->write_0x8c(0x10, 0x94); } if (dev->model->model_id == ModelId::CANON_LIDE_90) { dev->interface->write_0x8c(0x10, 0xd4); } // set RAM read address dev->interface->write_register(REG_0x2A, 0x00); dev->interface->write_register(REG_0x2B, 0x00); // setup gpio gl842_init_gpio(dev); dev->interface->sleep_ms(100); } void CommandSetGl842::init(Genesys_Device* dev) const { DBG_INIT(); DBG_HELPER(dbg); sanei_genesys_asic_init(dev); } void CommandSetGl842::update_hardware_sensors(Genesys_Scanner* s) const { DBG_HELPER(dbg); (void) s; } void CommandSetGl842::update_home_sensor_gpio(Genesys_Device& dev) const { DBG_HELPER(dbg); if (dev.model->model_id == ModelId::CANON_LIDE_90) { std::uint8_t val = dev.interface->read_register(REG_0x6C); val |= 0x02; dev.interface->write_register(REG_0x6C, val); } } /** * Send shading calibration data. The buffer is considered to always hold values * for all the channels. */ void CommandSetGl842::send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t* data, int size) const { DBG_HELPER(dbg); int offset = 0; unsigned length = size; if (dev->reg.get8(REG_0x01) & REG_0x01_SHDAREA) { offset = dev->session.params.startx * sensor.shading_resolution / dev->session.params.xres; length = dev->session.output_pixels * sensor.shading_resolution / dev->session.params.xres; offset += sensor.shading_pixel_offset; // 16 bit words, 2 words per color, 3 color channels length *= 2 * 2 * 3; offset *= 2 * 2 * 3; } else { offset += sensor.shading_pixel_offset * 2 * 2 * 3; } dev->interface->record_key_value("shading_offset", std::to_string(offset)); dev->interface->record_key_value("shading_length", std::to_string(length)); std::vector final_data(length, 0); unsigned count = 0; if (offset < 0) { count += (-offset); length -= (-offset); offset = 0; } if (static_cast(length) + offset > static_cast(size)) { length = size - offset; } for (unsigned i = 0; i < length; i++) { final_data[count++] = data[offset + i]; count++; } dev->interface->write_buffer(0x3c, 0, final_data.data(), count); } bool CommandSetGl842::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const { (void) dev; return true; } void CommandSetGl842::wait_for_motor_stop(Genesys_Device* dev) const { (void) dev; } } // namespace gl842 } // namespace genesys backends-1.3.0/backend/genesys/gl842.h000066400000000000000000000077351456256263500173740ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2010-2013 Stéphane Voltz 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, see . */ #include "genesys.h" #include "command_set_common.h" #ifndef BACKEND_GENESYS_GL842_H #define BACKEND_GENESYS_GL842_H namespace genesys { namespace gl842 { class CommandSetGl842 : public CommandSetCommon { public: ~CommandSetGl842() override = default; bool needs_home_before_init_regs_for_scan(Genesys_Device* dev) const override; void init(Genesys_Device* dev) const override; void init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* regs) const override; void init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const override; void init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, const ScanSession& session) const override; void set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set) const override; void set_powersaving(Genesys_Device* dev, int delay) const override; void save_power(Genesys_Device* dev, bool enable) const override; void begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* regs, bool start_motor) const override; void end_scan(Genesys_Device* dev, Genesys_Register_Set* regs, bool check_stop) const override; void send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const override; void offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const override; void coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs, int dpi) const override; SensorExposure led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const override; void wait_for_motor_stop(Genesys_Device* dev) const override; void move_back_home(Genesys_Device* dev, bool wait_until_home) const override; void update_hardware_sensors(struct Genesys_Scanner* s) const override; void update_home_sensor_gpio(Genesys_Device& dev) const override; void load_document(Genesys_Device* dev) const override; void detect_document_end(Genesys_Device* dev) const override; void eject_document(Genesys_Device* dev) const override; void send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t* data, int size) const override; ScanSession calculate_scan_session(const Genesys_Device* dev, const Genesys_Sensor& sensor, const Genesys_Settings& settings) const override; void asic_boot(Genesys_Device* dev, bool cold) const override; }; enum SlopeTable { SCAN_TABLE = 0, // table 1 at 0x4000 BACKTRACK_TABLE = 1, // table 2 at 0x4800 STOP_TABLE = 2, // table 3 at 0x5000 FAST_TABLE = 3, // table 4 at 0x5800 HOME_TABLE = 4, // table 5 at 0x6000 }; } // namespace gl842 } // namespace genesys #endif // BACKEND_GENESYS_GL842_H backends-1.3.0/backend/genesys/gl842_registers.h000066400000000000000000000233321456256263500214520ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_gl842_REGISTERS_H #define BACKEND_GENESYS_gl842_REGISTERS_H #include namespace genesys { namespace gl842 { using RegAddr = std::uint16_t; using RegMask = std::uint8_t; using RegShift = unsigned; static constexpr RegAddr REG_0x01 = 0x01; static constexpr RegMask REG_0x01_CISSET = 0x80; static constexpr RegMask REG_0x01_DOGENB = 0x40; static constexpr RegMask REG_0x01_DVDSET = 0x20; static constexpr RegMask REG_0x01_M15DRAM = 0x08; static constexpr RegMask REG_0x01_DRAMSEL = 0x04; static constexpr RegMask REG_0x01_SHDAREA = 0x02; static constexpr RegMask REG_0x01_SCAN = 0x01; static constexpr RegAddr REG_0x02 = 0x02; static constexpr RegMask REG_0x02_NOTHOME = 0x80; static constexpr RegMask REG_0x02_ACDCDIS = 0x40; static constexpr RegMask REG_0x02_AGOHOME = 0x20; static constexpr RegMask REG_0x02_MTRPWR = 0x10; static constexpr RegMask REG_0x02_FASTFED = 0x08; static constexpr RegMask REG_0x02_MTRREV = 0x04; static constexpr RegMask REG_0x02_HOMENEG = 0x02; static constexpr RegMask REG_0x02_LONGCURV = 0x01; static constexpr RegAddr REG_0x03 = 0x03; static constexpr RegMask REG_0x03_LAMPDOG = 0x80; static constexpr RegMask REG_0x03_AVEENB = 0x40; static constexpr RegMask REG_0x03_XPASEL = 0x20; static constexpr RegMask REG_0x03_LAMPPWR = 0x10; static constexpr RegMask REG_0x03_LAMPTIM = 0x0f; static constexpr RegAddr REG_0x04 = 0x04; static constexpr RegMask REG_0x04_LINEART = 0x80; static constexpr RegMask REG_0x04_BITSET = 0x40; static constexpr RegMask REG_0x04_AFEMOD = 0x30; static constexpr RegMask REG_0x04_FILTER = 0x0c; static constexpr RegMask REG_0x04_FESET = 0x03; static constexpr RegShift REG_0x04S_AFEMOD = 4; static constexpr RegAddr REG_0x05 = 0x05; static constexpr RegMask REG_0x05_DPIHW = 0xc0; static constexpr RegMask REG_0x05_DPIHW_600 = 0x00; static constexpr RegMask REG_0x05_DPIHW_1200 = 0x40; static constexpr RegMask REG_0x05_DPIHW_2400 = 0x80; static constexpr RegMask REG_0x05_DPIHW_4800 = 0xc0; static constexpr RegMask REG_0x05_MTLLAMP = 0x30; static constexpr RegMask REG_0x05_GMMENB = 0x08; static constexpr RegMask REG_0x05_MTLBASE = 0x03; static constexpr RegAddr REG_0x06 = 0x06; static constexpr RegMask REG_0x06_SCANMOD = 0xe0; static constexpr RegShift REG_0x06S_SCANMOD = 5; static constexpr RegMask REG_0x06_PWRBIT = 0x10; static constexpr RegMask REG_0x06_GAIN4 = 0x08; static constexpr RegMask REG_0x06_OPTEST = 0x07; static constexpr RegMask REG_0x08_DECFLAG = 0x40; static constexpr RegMask REG_0x08_GMMFFR = 0x20; static constexpr RegMask REG_0x08_GMMFFG = 0x10; static constexpr RegMask REG_0x08_GMMFFB = 0x08; static constexpr RegMask REG_0x08_GMMZR = 0x04; static constexpr RegMask REG_0x08_GMMZG = 0x02; static constexpr RegMask REG_0x08_GMMZB = 0x01; static constexpr RegMask REG_0x09_MCNTSET = 0xc0; static constexpr RegMask REG_0x09_CLKSET = 0x30; static constexpr RegMask REG_0x09_BACKSCAN = 0x08; static constexpr RegMask REG_0x09_ENHANCE = 0x04; static constexpr RegMask REG_0x09_SHORTTG = 0x02; static constexpr RegMask REG_0x09_NWAIT = 0x01; static constexpr RegAddr REG_0x0D = 0x0d; static constexpr RegMask REG_0x0D_CLRLNCNT = 0x01; static constexpr RegAddr REG_0x0F = 0x0f; static constexpr RegAddr REG_EXPR = 0x10; static constexpr RegAddr REG_EXPG = 0x12; static constexpr RegAddr REG_EXPB = 0x14; static constexpr RegMask REG_0x16_CTRLHI = 0x80; static constexpr RegMask REG_0x16_TOSHIBA = 0x40; static constexpr RegMask REG_0x16_TGINV = 0x20; static constexpr RegMask REG_0x16_CK1INV = 0x10; static constexpr RegMask REG_0x16_CK2INV = 0x08; static constexpr RegMask REG_0x16_CTRLINV = 0x04; static constexpr RegMask REG_0x16_CKDIS = 0x02; static constexpr RegMask REG_0x16_CTRLDIS = 0x01; static constexpr RegMask REG_0x17_TGMODE = 0xc0; static constexpr RegMask REG_0x17_TGMODE_NO_DUMMY = 0x00; static constexpr RegMask REG_0x17_TGMODE_REF = 0x40; static constexpr RegMask REG_0x17_TGMODE_XPA = 0x80; static constexpr RegMask REG_0x17_TGW = 0x3f; static constexpr RegAddr REG_0x18 = 0x18; static constexpr RegMask REG_0x18_CNSET = 0x80; static constexpr RegMask REG_0x18_DCKSEL = 0x60; static constexpr RegMask REG_0x18_CKTOGGLE = 0x10; static constexpr RegMask REG_0x18_CKDELAY = 0x0c; static constexpr RegMask REG_0x18_CKSEL = 0x03; static constexpr RegAddr REG_EXPDMY = 0x19; static constexpr RegAddr REG_0x1A = 0x1a; static constexpr RegMask REG_0x1A_MANUAL3 = 0x02; static constexpr RegMask REG_0x1A_MANUAL1 = 0x01; static constexpr RegMask REG_0x1A_CK4INV = 0x08; static constexpr RegMask REG_0x1A_CK3INV = 0x04; static constexpr RegMask REG_0x1A_LINECLP = 0x02; static constexpr RegAddr REG_0x1C = 0x1c; static constexpr RegMask REG_0x1C_TGTIME = 0x07; static constexpr RegMask REG_0x1D_CK4LOW = 0x80; static constexpr RegMask REG_0x1D_CK3LOW = 0x40; static constexpr RegMask REG_0x1D_CK1LOW = 0x20; static constexpr RegMask REG_0x1D_TGSHLD = 0x1f; static constexpr RegAddr REG_0x1E = 0x1e; static constexpr RegMask REG_0x1E_WDTIME = 0xf0; static constexpr RegShift REG_0x1ES_WDTIME = 4; static constexpr RegMask REG_0x1E_LINESEL = 0x0f; static constexpr RegShift REG_0x1ES_LINESEL = 0; static constexpr RegAddr REG_0x21 = 0x21; static constexpr RegAddr REG_STEPNO = 0x21; static constexpr RegAddr REG_FWDSTEP = 0x22; static constexpr RegAddr REG_BWDSTEP = 0x23; static constexpr RegAddr REG_FASTNO = 0x24; static constexpr RegAddr REG_LINCNT = 0x25; static constexpr RegAddr REG_0x29 = 0x29; static constexpr RegAddr REG_0x2A = 0x2a; static constexpr RegAddr REG_0x2B = 0x2b; static constexpr RegAddr REG_DPISET = 0x2c; static constexpr RegAddr REG_0x2E = 0x2e; static constexpr RegAddr REG_0x2F = 0x2f; static constexpr RegAddr REG_STRPIXEL = 0x30; static constexpr RegAddr REG_ENDPIXEL = 0x32; static constexpr RegAddr REG_DUMMY = 0x34; static constexpr RegAddr REG_MAXWD = 0x35; static constexpr RegAddr REG_LPERIOD = 0x38; static constexpr RegAddr REG_FEEDL = 0x3d; static constexpr RegAddr REG_0x40 = 0x40; static constexpr RegMask REG_0x40_HISPDFLG = 0x04; static constexpr RegMask REG_0x40_MOTMFLG = 0x02; static constexpr RegMask REG_0x40_DATAENB = 0x01; static constexpr RegMask REG_0x41_PWRBIT = 0x80; static constexpr RegMask REG_0x41_BUFEMPTY = 0x40; static constexpr RegMask REG_0x41_FEEDFSH = 0x20; static constexpr RegMask REG_0x41_SCANFSH = 0x10; static constexpr RegMask REG_0x41_HOMESNR = 0x08; static constexpr RegMask REG_0x41_LAMPSTS = 0x04; static constexpr RegMask REG_0x41_FEBUSY = 0x02; static constexpr RegMask REG_0x41_MOTORENB = 0x01; static constexpr RegMask REG_0x5A_ADCLKINV = 0x80; static constexpr RegMask REG_0x5A_RLCSEL = 0x40; static constexpr RegMask REG_0x5A_CDSREF = 0x30; static constexpr RegShift REG_0x5AS_CDSREF = 4; static constexpr RegMask REG_0x5A_RLC = 0x0f; static constexpr RegShift REG_0x5AS_RLC = 0; static constexpr RegAddr REG_0x5E = 0x5e; static constexpr RegMask REG_0x5E_DECSEL = 0xe0; static constexpr RegShift REG_0x5ES_DECSEL = 5; static constexpr RegMask REG_0x5E_STOPTIM = 0x1f; static constexpr RegShift REG_0x5ES_STOPTIM = 0; static constexpr RegAddr REG_FMOVDEC = 0x5f; static constexpr RegAddr REG_0x60 = 0x60; static constexpr RegMask REG_0x60_Z1MOD = 0x1f; static constexpr RegAddr REG_0x61 = 0x61; static constexpr RegMask REG_0x61_Z1MOD = 0xff; static constexpr RegAddr REG_0x62 = 0x62; static constexpr RegMask REG_0x62_Z1MOD = 0xff; static constexpr RegAddr REG_0x63 = 0x63; static constexpr RegMask REG_0x63_Z2MOD = 0x1f; static constexpr RegAddr REG_0x64 = 0x64; static constexpr RegMask REG_0x64_Z2MOD = 0xff; static constexpr RegAddr REG_0x65 = 0x65; static constexpr RegMask REG_0x65_Z2MOD = 0xff; static constexpr RegAddr REG_0x67 = 0x67; static constexpr RegAddr REG_0x68 = 0x68; static constexpr RegShift REG_0x67S_STEPSEL = 6; static constexpr RegMask REG_0x67_STEPSEL = 0xc0; static constexpr RegShift REG_0x68S_FSTPSEL = 6; static constexpr RegMask REG_0x68_FSTPSEL = 0xc0; static constexpr RegAddr REG_FSHDEC = 0x69; static constexpr RegAddr REG_FMOVNO = 0x6a; static constexpr RegAddr REG_0x6B = 0x6b; static constexpr RegMask REG_0x6B_MULTFILM = 0x80; static constexpr RegAddr REG_Z1MOD = 0x60; static constexpr RegAddr REG_Z2MOD = 0x63; static constexpr RegAddr REG_0x6C = 0x6c; static constexpr RegAddr REG_0x6D = 0x6d; static constexpr RegAddr REG_0x6E = 0x6e; static constexpr RegAddr REG_0x6F = 0x6f; static constexpr RegAddr REG_CK1MAP = 0x74; static constexpr RegAddr REG_CK3MAP = 0x77; static constexpr RegAddr REG_CK4MAP = 0x7a; static constexpr RegAddr REG_0x7E = 0x7e; static constexpr RegAddr REG_0x80 = 0x80; static constexpr RegMask REG_0x80_TABLE1_NORMAL = 0x03; static constexpr RegShift REG_0x80S_TABLE1_NORMAL = 0; static constexpr RegMask REG_0x80_TABLE2_BACK = 0x0c; static constexpr RegShift REG_0x80S_TABLE2_BACK = 2; static constexpr RegMask REG_0x80_TABLE4_FAST = 0x30; static constexpr RegShift REG_0x80S_TABLE4_FAST = 4; static constexpr RegMask REG_0x80_TABLE5_GO_HOME = 0xc0; static constexpr RegShift REG_0x80S_TABLE5_GO_HOME = 6; static constexpr RegMask REG_0x87_LEDADD = 0x04; } // namespace gl842 } // namespace genesys #endif // BACKEND_GENESYS_gl842_REGISTERS_H backends-1.3.0/backend/genesys/gl843.cpp000066400000000000000000001742021456256263500177220ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2010-2013 Stéphane Voltz 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, see . */ #define DEBUG_DECLARE_ONLY #include "gl843_registers.h" #include "gl843.h" #include "test_settings.h" #include #include namespace genesys { namespace gl843 { /** * compute the step multiplier used */ static int gl843_get_step_multiplier(Genesys_Register_Set* regs) { switch (regs->get8(REG_0x9D) & 0x0c) { case 0x04: return 2; case 0x08: return 4; default: return 1; } } /** @brief set all registers to default values . * This function is called only once at the beginning and * fills register startup values for registers reused across scans. * Those that are rarely modified or not modified are written * individually. * @param dev device structure holding register set to initialize */ static void gl843_init_registers (Genesys_Device * dev) { // Within this function SENSOR_DEF marker documents that a register is part // of the sensors definition and the actual value is set in // scanner_setup_sensor(). // 0x6c, 0x6d, 0x6e, 0x6f, 0xa6, 0xa7, 0xa8, 0xa9 are defined in the Gpo sensor struct DBG_HELPER(dbg); dev->reg.clear(); dev->reg.init_reg(0x01, 0x00); dev->reg.init_reg(0x02, 0x78); dev->reg.init_reg(0x03, 0x1f); if (dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::HP_SCANJET_4850C) { dev->reg.init_reg(0x03, 0x1d); } if (dev->model->model_id == ModelId::CANON_8400F) { dev->reg.init_reg(0x03, 0x1c); } dev->reg.init_reg(0x04, 0x10); if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { dev->reg.init_reg(0x04, 0x22); } // fine tune upon device description dev->reg.init_reg(0x05, 0x80); if (dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::HP_SCANJET_4850C) { dev->reg.init_reg(0x05, 0x08); } auto initial_scan_method = dev->model->default_method; if (dev->model->model_id == ModelId::CANON_4400F || dev->model->model_id == ModelId::CANON_8600F) { initial_scan_method = ScanMethod::TRANSPARENCY; } const auto& sensor = sanei_genesys_find_sensor_any(dev); const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, sensor.full_resolution, 3, initial_scan_method); sanei_genesys_set_dpihw(dev->reg, dpihw_sensor.register_dpihw); // TODO: on 8600F the windows driver turns off GAIN4 which is recommended dev->reg.init_reg(0x06, 0xd8); /* SCANMOD=110, PWRBIT and GAIN4 */ if (dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::HP_SCANJET_4850C) { dev->reg.init_reg(0x06, 0xd8); /* SCANMOD=110, PWRBIT and GAIN4 */ } if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I) { dev->reg.init_reg(0x06, 0xd0); } if (dev->model->model_id == ModelId::CANON_4400F || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { dev->reg.init_reg(0x06, 0xf0); /* SCANMOD=111, PWRBIT and no GAIN4 */ } dev->reg.init_reg(0x08, 0x00); dev->reg.init_reg(0x09, 0x00); dev->reg.init_reg(0x0a, 0x00); if (dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::HP_SCANJET_4850C) { dev->reg.init_reg(0x0a, 0x18); } if (dev->model->model_id == ModelId::CANON_8400F) { dev->reg.init_reg(0x0a, 0x10); } // This register controls clock and RAM settings and is further modified in // gl843_boot dev->reg.init_reg(0x0b, 0x6a); if (dev->model->model_id == ModelId::CANON_4400F) { dev->reg.init_reg(0x0b, 0x69); // 16M only } if (dev->model->model_id == ModelId::CANON_8600F) { dev->reg.init_reg(0x0b, 0x89); } if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I) { dev->reg.init_reg(0x0b, 0x2a); } if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { dev->reg.init_reg(0x0b, 0x4a); } if (dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::HP_SCANJET_4850C) { dev->reg.init_reg(0x0b, 0x69); } if (dev->model->model_id != ModelId::CANON_8400F && dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7200I && dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7300) { dev->reg.init_reg(0x0c, 0x00); } // EXPR[0:15], EXPG[0:15], EXPB[0:15]: Exposure time settings. dev->reg.init_reg(0x10, 0x00); // SENSOR_DEF dev->reg.init_reg(0x11, 0x00); // SENSOR_DEF dev->reg.init_reg(0x12, 0x00); // SENSOR_DEF dev->reg.init_reg(0x13, 0x00); // SENSOR_DEF dev->reg.init_reg(0x14, 0x00); // SENSOR_DEF dev->reg.init_reg(0x15, 0x00); // SENSOR_DEF if (dev->model->model_id == ModelId::CANON_4400F || dev->model->model_id == ModelId::CANON_8600F) { dev->reg.set16(REG_EXPR, 0x9c40); dev->reg.set16(REG_EXPG, 0x9c40); dev->reg.set16(REG_EXPB, 0x9c40); } if (dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::HP_SCANJET_4850C) { dev->reg.set16(REG_EXPR, 0x2c09); dev->reg.set16(REG_EXPG, 0x22b8); dev->reg.set16(REG_EXPB, 0x10f0); } // CCD signal settings. dev->reg.init_reg(0x16, 0x33); // SENSOR_DEF dev->reg.init_reg(0x17, 0x1c); // SENSOR_DEF dev->reg.init_reg(0x18, 0x10); // SENSOR_DEF // EXPDMY[0:7]: Exposure time of dummy lines. dev->reg.init_reg(0x19, 0x2a); // SENSOR_DEF // Various CCD clock settings. dev->reg.init_reg(0x1a, 0x04); // SENSOR_DEF dev->reg.init_reg(0x1b, 0x00); // SENSOR_DEF dev->reg.init_reg(0x1c, 0x20); // SENSOR_DEF dev->reg.init_reg(0x1d, 0x04); // SENSOR_DEF dev->reg.init_reg(0x1e, 0x10); if (dev->model->model_id == ModelId::CANON_4400F || dev->model->model_id == ModelId::CANON_8600F) { dev->reg.init_reg(0x1e, 0x20); } if (dev->model->model_id == ModelId::CANON_8400F) { dev->reg.init_reg(0x1e, 0xa0); } dev->reg.init_reg(0x1f, 0x01); if (dev->model->model_id == ModelId::CANON_8600F) { dev->reg.init_reg(0x1f, 0xff); } dev->reg.init_reg(0x20, 0x10); dev->reg.init_reg(0x21, 0x04); dev->reg.init_reg(0x22, 0x10); dev->reg.init_reg(0x23, 0x10); if (dev->model->model_id == ModelId::CANON_8600F) { dev->reg.init_reg(0x22, 0xc8); dev->reg.init_reg(0x23, 0xc8); } if (dev->model->model_id == ModelId::CANON_8400F) { dev->reg.init_reg(0x22, 0x50); dev->reg.init_reg(0x23, 0x50); } dev->reg.init_reg(0x24, 0x04); dev->reg.init_reg(0x25, 0x00); dev->reg.init_reg(0x26, 0x00); dev->reg.init_reg(0x27, 0x00); dev->reg.init_reg(0x2c, 0x02); dev->reg.init_reg(0x2d, 0x58); // BWHI[0:7]: high level of black and white threshold dev->reg.init_reg(0x2e, 0x80); // BWLOW[0:7]: low level of black and white threshold dev->reg.init_reg(0x2f, 0x80); dev->reg.init_reg(0x30, 0x00); dev->reg.init_reg(0x31, 0x14); dev->reg.init_reg(0x32, 0x27); dev->reg.init_reg(0x33, 0xec); // DUMMY: CCD dummy and optically black pixel count dev->reg.init_reg(0x34, 0x24); if (dev->model->model_id == ModelId::CANON_8600F) { dev->reg.init_reg(0x34, 0x14); } if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { dev->reg.init_reg(0x34, 0x3c); } // MAXWD: If available buffer size is less than 2*MAXWD words, then // "buffer full" state will be set. dev->reg.init_reg(0x35, 0x00); dev->reg.init_reg(0x36, 0xff); dev->reg.init_reg(0x37, 0xff); // LPERIOD: Line period or exposure time for CCD or CIS. dev->reg.init_reg(0x38, 0x55); // SENSOR_DEF dev->reg.init_reg(0x39, 0xf0); // SENSOR_DEF // FEEDL[0:24]: The number of steps of motor movement. dev->reg.init_reg(0x3d, 0x00); dev->reg.init_reg(0x3e, 0x00); dev->reg.init_reg(0x3f, 0x01); // Latch points for high and low bytes of R, G and B channels of AFE. If // multiple clocks per pixel are consumed, then the setting defines during // which clock the corresponding value will be read. // RHI[0:4]: The latch point for high byte of R channel. // RLOW[0:4]: The latch point for low byte of R channel. // GHI[0:4]: The latch point for high byte of G channel. // GLOW[0:4]: The latch point for low byte of G channel. // BHI[0:4]: The latch point for high byte of B channel. // BLOW[0:4]: The latch point for low byte of B channel. dev->reg.init_reg(0x52, 0x01); // SENSOR_DEF dev->reg.init_reg(0x53, 0x04); // SENSOR_DEF dev->reg.init_reg(0x54, 0x07); // SENSOR_DEF dev->reg.init_reg(0x55, 0x0a); // SENSOR_DEF dev->reg.init_reg(0x56, 0x0d); // SENSOR_DEF dev->reg.init_reg(0x57, 0x10); // SENSOR_DEF // VSMP[0:4]: The position of the image sampling pulse for AFE in cycles. // VSMPW[0:2]: The length of the image sampling pulse for AFE in cycles. dev->reg.init_reg(0x58, 0x1b); // SENSOR_DEF dev->reg.init_reg(0x59, 0x00); // SENSOR_DEF dev->reg.init_reg(0x5a, 0x40); // SENSOR_DEF // 0x5b-0x5c: GMMADDR[0:15] address for gamma or motor tables download // SENSOR_DEF // DECSEL[0:2]: The number of deceleration steps after touching home sensor // STOPTIM[0:4]: The stop duration between change of directions in // backtracking dev->reg.init_reg(0x5e, 0x23); if (dev->model->model_id == ModelId::CANON_4400F) { dev->reg.init_reg(0x5e, 0x3f); } if (dev->model->model_id == ModelId::CANON_8400F) { dev->reg.init_reg(0x5e, 0x85); } if (dev->model->model_id == ModelId::CANON_8600F) { dev->reg.init_reg(0x5e, 0x1f); } if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { dev->reg.init_reg(0x5e, 0x01); } //FMOVDEC: The number of deceleration steps in table 5 for auto-go-home dev->reg.init_reg(0x5f, 0x01); if (dev->model->model_id == ModelId::CANON_4400F) { dev->reg.init_reg(0x5f, 0xf0); } if (dev->model->model_id == ModelId::CANON_8600F) { dev->reg.init_reg(0x5f, 0xf0); } if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { dev->reg.init_reg(0x5f, 0x01); } // Z1MOD[0:20] dev->reg.init_reg(0x60, 0x00); dev->reg.init_reg(0x61, 0x00); dev->reg.init_reg(0x62, 0x00); // Z2MOD[0:20] dev->reg.init_reg(0x63, 0x00); dev->reg.init_reg(0x64, 0x00); dev->reg.init_reg(0x65, 0x00); // STEPSEL[0:1]. Motor movement step mode selection for tables 1-3 in // scanning mode. // MTRPWM[0:5]. Motor phase PWM duty cycle setting for tables 1-3 dev->reg.init_reg(0x67, 0x7f); // MOTOR_PROFILE // FSTPSEL[0:1]: Motor movement step mode selection for tables 4-5 in // command mode. // FASTPWM[5:0]: Motor phase PWM duty cycle setting for tables 4-5 dev->reg.init_reg(0x68, 0x7f); // MOTOR_PROFILE if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300) { dev->reg.init_reg(0x67, 0x80); dev->reg.init_reg(0x68, 0x80); } // FSHDEC[0:7]: The number of deceleration steps after scanning is finished // (table 3) dev->reg.init_reg(0x69, 0x01); // MOTOR_PROFILE // FMOVNO[0:7] The number of acceleration or deceleration steps for fast // moving (table 4) dev->reg.init_reg(0x6a, 0x04); // MOTOR_PROFILE // GPIO-related register bits dev->reg.init_reg(0x6b, 0x30); if (dev->model->model_id == ModelId::CANON_4400F || dev->model->model_id == ModelId::CANON_8600F) { dev->reg.init_reg(0x6b, 0x72); } if (dev->model->model_id == ModelId::CANON_8400F) { dev->reg.init_reg(0x6b, 0xb1); } if (dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::HP_SCANJET_4850C) { dev->reg.init_reg(0x6b, 0xf4); } if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { dev->reg.init_reg(0x6b, 0x31); } // 0x6c, 0x6d, 0x6e, 0x6f are set according to gpio tables. See // gl843_init_gpio. // RSH[0:4]: The position of rising edge of CCD RS signal in cycles // RSL[0:4]: The position of falling edge of CCD RS signal in cycles // CPH[0:4]: The position of rising edge of CCD CP signal in cycles. // CPL[0:4]: The position of falling edge of CCD CP signal in cycles dev->reg.init_reg(0x70, 0x01); // SENSOR_DEF dev->reg.init_reg(0x71, 0x03); // SENSOR_DEF dev->reg.init_reg(0x72, 0x04); // SENSOR_DEF dev->reg.init_reg(0x73, 0x05); // SENSOR_DEF if (dev->model->model_id == ModelId::CANON_4400F) { dev->reg.init_reg(0x70, 0x01); dev->reg.init_reg(0x71, 0x03); dev->reg.init_reg(0x72, 0x01); dev->reg.init_reg(0x73, 0x03); } if (dev->model->model_id == ModelId::CANON_8400F) { dev->reg.init_reg(0x70, 0x01); dev->reg.init_reg(0x71, 0x03); dev->reg.init_reg(0x72, 0x03); dev->reg.init_reg(0x73, 0x04); } if (dev->model->model_id == ModelId::CANON_8600F) { dev->reg.init_reg(0x70, 0x00); dev->reg.init_reg(0x71, 0x02); dev->reg.init_reg(0x72, 0x02); dev->reg.init_reg(0x73, 0x04); } if (dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::HP_SCANJET_4850C) { dev->reg.init_reg(0x70, 0x00); dev->reg.init_reg(0x71, 0x02); dev->reg.init_reg(0x72, 0x00); dev->reg.init_reg(0x73, 0x00); } // CK1MAP[0:17], CK3MAP[0:17], CK4MAP[0:17]: CCD clock bit mapping setting. dev->reg.init_reg(0x74, 0x00); // SENSOR_DEF dev->reg.init_reg(0x75, 0x00); // SENSOR_DEF dev->reg.init_reg(0x76, 0x3c); // SENSOR_DEF dev->reg.init_reg(0x77, 0x00); // SENSOR_DEF dev->reg.init_reg(0x78, 0x00); // SENSOR_DEF dev->reg.init_reg(0x79, 0x9f); // SENSOR_DEF dev->reg.init_reg(0x7a, 0x00); // SENSOR_DEF dev->reg.init_reg(0x7b, 0x00); // SENSOR_DEF dev->reg.init_reg(0x7c, 0x55); // SENSOR_DEF // various AFE settings dev->reg.init_reg(0x7d, 0x00); if (dev->model->model_id == ModelId::CANON_8400F) { dev->reg.init_reg(0x7d, 0x20); } // GPOLED[x]: LED vs GPIO settings dev->reg.init_reg(0x7e, 0x00); // BSMPDLY, VSMPDLY // LEDCNT[0:1]: Controls led blinking and its period dev->reg.init_reg(0x7f, 0x00); // VRHOME, VRMOVE, VRBACK, VRSCAN: Vref settings of the motor driver IC for // moving in various situations. dev->reg.init_reg(0x80, 0x00); // MOTOR_PROFILE if (dev->model->model_id == ModelId::CANON_4400F) { dev->reg.init_reg(0x80, 0x0c); } if (dev->model->model_id == ModelId::CANON_8400F) { dev->reg.init_reg(0x80, 0x28); } if (dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::HP_SCANJET_4850C) { dev->reg.init_reg(0x80, 0x50); } if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { dev->reg.init_reg(0x80, 0x0f); } if (dev->model->model_id != ModelId::CANON_4400F) { dev->reg.init_reg(0x81, 0x00); dev->reg.init_reg(0x82, 0x00); dev->reg.init_reg(0x83, 0x00); dev->reg.init_reg(0x84, 0x00); dev->reg.init_reg(0x85, 0x00); dev->reg.init_reg(0x86, 0x00); } dev->reg.init_reg(0x87, 0x00); if (dev->model->model_id == ModelId::CANON_4400F || dev->model->model_id == ModelId::CANON_8400F || dev->model->model_id == ModelId::CANON_8600F) { dev->reg.init_reg(0x87, 0x02); } // MTRPLS[0:7]: The width of the ADF motor trigger signal pulse. if (dev->model->model_id != ModelId::CANON_8400F && dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7200I && dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7300) { dev->reg.init_reg(0x94, 0xff); } // 0x95-0x97: SCANLEN[0:19]: Controls when paper jam bit is set in sheetfed // scanners. // ONDUR[0:15]: The duration of PWM ON phase for LAMP control // OFFDUR[0:15]: The duration of PWM OFF phase for LAMP control // both of the above are in system clocks if (dev->model->model_id == ModelId::CANON_8600F) { dev->reg.init_reg(0x98, 0x00); dev->reg.init_reg(0x99, 0x00); dev->reg.init_reg(0x9a, 0x00); dev->reg.init_reg(0x9b, 0x00); } if (dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::HP_SCANJET_4850C) { // TODO: move to set for scan dev->reg.init_reg(0x98, 0x03); dev->reg.init_reg(0x99, 0x30); dev->reg.init_reg(0x9a, 0x01); dev->reg.init_reg(0x9b, 0x80); } // RMADLY[0:1], MOTLAG, CMODE, STEPTIM, MULDMYLN, IFRS dev->reg.init_reg(0x9d, 0x04); if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { dev->reg.init_reg(0x9d, 0x00); } if (dev->model->model_id == ModelId::CANON_4400F || dev->model->model_id == ModelId::CANON_8400F || dev->model->model_id == ModelId::CANON_8600F || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I || dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::HP_SCANJET_4850C) { dev->reg.init_reg(0x9d, 0x08); // sets the multiplier for slope tables } // SEL3INV, TGSTIME[0:2], TGWTIME[0:2] if (dev->model->model_id != ModelId::CANON_8400F && dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7200I && dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7300) { dev->reg.init_reg(0x9e, 0x00); // SENSOR_DEF } if (dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7300) { dev->reg.init_reg(0xa2, 0x0f); } // RFHSET[0:4]: Refresh time of SDRAM in units of 2us if (dev->model->model_id == ModelId::CANON_4400F || dev->model->model_id == ModelId::CANON_8600F) { dev->reg.init_reg(0xa2, 0x1f); } // 0xa6-0xa9: controls gpio, see gl843_gpio_init // not documented if (dev->model->model_id != ModelId::CANON_4400F && dev->model->model_id != ModelId::CANON_8400F && dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7200I && dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7300) { dev->reg.init_reg(0xaa, 0x00); } // GPOM9, MULSTOP[0-2], NODECEL, TB3TB1, TB5TB2, FIX16CLK. if (dev->model->model_id != ModelId::CANON_8400F && dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7200I && dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7300) { dev->reg.init_reg(0xab, 0x50); } if (dev->model->model_id == ModelId::CANON_4400F) { dev->reg.init_reg(0xab, 0x00); } if (dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::CANON_8600F || dev->model->model_id == ModelId::HP_SCANJET_4850C) { dev->reg.init_reg(0xab, 0x40); } // VRHOME[3:2], VRMOVE[3:2], VRBACK[3:2]: Vref setting of the motor driver IC // for various situations. if (dev->model->model_id == ModelId::CANON_8600F || dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::HP_SCANJET_4850C) { dev->reg.init_reg(0xac, 0x00); } if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I) { std::uint8_t data[32] = { 0x8c, 0x8f, 0xc9, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x73, 0x63, 0x68, 0x69, 0x65, 0x6e, 0x00, }; dev->interface->write_buffer(0x3c, 0x3ff000, data, 32); } } static void gl843_set_ad_fe(Genesys_Device* dev) { for (const auto& reg : dev->frontend.regs) { dev->interface->write_fe_register(reg.address, reg.value); } } // Set values of analog frontend void CommandSetGl843::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set) const { DBG_HELPER_ARGS(dbg, "%s", set == AFE_INIT ? "init" : set == AFE_SET ? "set" : set == AFE_POWER_SAVE ? "powersave" : "huh?"); (void) sensor; if (set == AFE_INIT) { dev->frontend = dev->frontend_initial; } // check analog frontend type // FIXME: looks like we write to that register with initial data std::uint8_t fe_type = dev->interface->read_register(REG_0x04) & REG_0x04_FESET; if (fe_type == 2) { gl843_set_ad_fe(dev); return; } if (fe_type != 0) { throw SaneException(SANE_STATUS_UNSUPPORTED, "unsupported frontend type %d", fe_type); } for (unsigned i = 1; i <= 3; i++) { dev->interface->write_fe_register(i, dev->frontend.regs.get_value(0x00 + i)); } for (const auto& reg : sensor.custom_fe_regs) { dev->interface->write_fe_register(reg.address, reg.value); } for (unsigned i = 0; i < 3; i++) { dev->interface->write_fe_register(0x20 + i, dev->frontend.get_offset(i)); } if (dev->model->sensor_id == SensorId::CCD_KVSS080) { for (unsigned i = 0; i < 3; i++) { dev->interface->write_fe_register(0x24 + i, dev->frontend.regs.get_value(0x24 + i)); } } for (unsigned i = 0; i < 3; i++) { dev->interface->write_fe_register(0x28 + i, dev->frontend.get_gain(i)); } } static void gl843_init_motor_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, const ScanSession& session, Genesys_Register_Set* reg, const MotorProfile& motor_profile, unsigned int exposure, unsigned scan_yres, unsigned int scan_lines, unsigned int scan_dummy, unsigned int feed_steps, ScanFlag flags) { DBG_HELPER_ARGS(dbg, "exposure=%d, scan_yres=%d, step_type=%d, scan_lines=%d, scan_dummy=%d, " "feed_steps=%d, flags=%x", exposure, scan_yres, static_cast(motor_profile.step_type), scan_lines, scan_dummy, feed_steps, static_cast(flags)); unsigned feedl, dist; /* get step multiplier */ unsigned step_multiplier = gl843_get_step_multiplier (reg); bool use_fast_fed = false; if ((scan_yres >= 300 && feed_steps > 900) || (has_flag(flags, ScanFlag::FEEDING))) { use_fast_fed = true; } if (has_flag(dev->model->flags, ModelFlag::DISABLE_FAST_FEEDING)) { use_fast_fed = false; } reg->set24(REG_LINCNT, scan_lines); reg->set8(REG_0x02, 0); sanei_genesys_set_motor_power(*reg, true); std::uint8_t reg02 = reg->get8(REG_0x02); if (use_fast_fed) { reg02 |= REG_0x02_FASTFED; } else { reg02 &= ~REG_0x02_FASTFED; } // in case of automatic go home, move until home sensor if (has_flag(flags, ScanFlag::AUTO_GO_HOME)) { reg02 |= REG_0x02_AGOHOME | REG_0x02_NOTHOME; } /* disable backtracking */ if (has_flag(flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE) || (scan_yres>=2400 && dev->model->model_id != ModelId::CANON_4400F) || (scan_yres>=sensor.full_resolution)) { reg02 |= REG_0x02_ACDCDIS; } if (has_flag(flags, ScanFlag::REVERSE)) { reg02 |= REG_0x02_MTRREV; } else { reg02 &= ~REG_0x02_MTRREV; } reg->set8(REG_0x02, reg02); // scan and backtracking slope table auto scan_table = create_slope_table(dev->model->asic_type, dev->motor, scan_yres, exposure, step_multiplier, motor_profile); scanner_send_slope_table(dev, sensor, SCAN_TABLE, scan_table.table); scanner_send_slope_table(dev, sensor, BACKTRACK_TABLE, scan_table.table); scanner_send_slope_table(dev, sensor, STOP_TABLE, scan_table.table); reg->set8(REG_STEPNO, scan_table.table.size() / step_multiplier); reg->set8(REG_FASTNO, scan_table.table.size() / step_multiplier); reg->set8(REG_FSHDEC, scan_table.table.size() / step_multiplier); // fast table const auto* fast_profile = get_motor_profile_ptr(dev->motor.fast_profiles, 0, session); if (fast_profile == nullptr) { fast_profile = &motor_profile; } auto fast_table = create_slope_table_fastest(dev->model->asic_type, step_multiplier, *fast_profile); scanner_send_slope_table(dev, sensor, FAST_TABLE, fast_table.table); scanner_send_slope_table(dev, sensor, HOME_TABLE, fast_table.table); reg->set8(REG_FMOVNO, fast_table.table.size() / step_multiplier); if (motor_profile.motor_vref != -1 && fast_profile->motor_vref != 1) { std::uint8_t vref = 0; vref |= (motor_profile.motor_vref << REG_0x80S_TABLE1_NORMAL) & REG_0x80_TABLE1_NORMAL; vref |= (motor_profile.motor_vref << REG_0x80S_TABLE2_BACK) & REG_0x80_TABLE2_BACK; vref |= (fast_profile->motor_vref << REG_0x80S_TABLE4_FAST) & REG_0x80_TABLE4_FAST; vref |= (fast_profile->motor_vref << REG_0x80S_TABLE5_GO_HOME) & REG_0x80_TABLE5_GO_HOME; reg->set8(REG_0x80, vref); } /* subtract acceleration distance from feedl */ feedl=feed_steps; feedl <<= static_cast(motor_profile.step_type); dist = scan_table.table.size() / step_multiplier; if (use_fast_fed) { dist += (fast_table.table.size() / step_multiplier) * 2; } /* get sure when don't insane value : XXX STEF XXX in this case we should * fall back to single table move */ if (dist < feedl) { feedl -= dist; } else { feedl = 1; } reg->set24(REG_FEEDL, feedl); // doesn't seem to matter that much std::uint32_t z1, z2; sanei_genesys_calculate_zmod(use_fast_fed, exposure, scan_table.table, scan_table.table.size() / step_multiplier, feedl, scan_table.table.size() / step_multiplier, &z1, &z2); if(scan_yres>600) { z1=0; z2=0; } reg->set24(REG_Z1MOD, z1); reg->set24(REG_Z2MOD, z2); reg->set8_mask(REG_0x1E, scan_dummy, 0x0f); reg->set8_mask(REG_0x67, static_cast(motor_profile.step_type) << REG_0x67S_STEPSEL, 0xc0); reg->set8_mask(REG_0x68, static_cast(fast_profile->step_type) << REG_0x68S_FSTPSEL, 0xc0); // steps for STOP table reg->set8(REG_FMOVDEC, fast_table.table.size() / step_multiplier); if (dev->model->model_id == ModelId::PANASONIC_KV_SS080 || dev->model->model_id == ModelId::HP_SCANJET_4850C || dev->model->model_id == ModelId::HP_SCANJET_G4010 || dev->model->model_id == ModelId::HP_SCANJET_G4050 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { // FIXME: take this information from motor struct std::uint8_t reg_vref = reg->get8(0x80); reg_vref = 0x50; unsigned coeff = sensor.full_resolution / scan_yres; if (dev->model->motor_id == MotorId::KVSS080) { if (coeff >= 1) { reg_vref |= 0x05; } } else { switch (coeff) { case 4: reg_vref |= 0x0a; break; case 2: reg_vref |= 0x0f; break; case 1: reg_vref |= 0x0f; break; } } reg->set8(REG_0x80, reg_vref); } } /** @brief setup optical related registers * start and pixels are expressed in optical sensor resolution coordinate * space. * @param dev device to use * @param reg registers to set up * @param exposure exposure time to use * @param used_res scanning resolution used, may differ from * scan's one * @param start logical start pixel coordinate * @param pixels logical number of pixels to use * @param channels number of color channels used (1 or 3) * @param depth bit depth of the scan (1, 8 or 16 bits) * @param color_filter to choose the color channel used in gray scans * @param flags to drive specific settings such no calibration, XPA use ... */ static void gl843_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, unsigned int exposure, const ScanSession& session) { DBG_HELPER_ARGS(dbg, "exposure=%d", exposure); unsigned int tgtime; /**> exposure time multiplier */ /* tgtime */ tgtime = exposure / 65536 + 1; DBG(DBG_io2, "%s: tgtime=%d\n", __func__, tgtime); // sensor parameters scanner_setup_sensor(*dev, sensor, *reg); dev->cmd_set->set_fe(dev, sensor, AFE_SET); /* enable shading */ regs_set_optical_off(dev->model->asic_type, *reg); if (has_flag(session.params.flags, ScanFlag::DISABLE_SHADING) || has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION) || session.use_host_side_calib) { reg->find_reg(REG_0x01).value &= ~REG_0x01_DVDSET; } else { reg->find_reg(REG_0x01).value |= REG_0x01_DVDSET; } bool use_shdarea = false; if (dev->model->model_id == ModelId::CANON_4400F) { use_shdarea = session.params.xres <= 600; } else if (dev->model->model_id == ModelId::CANON_8400F) { use_shdarea = session.params.xres <= 400; } else if (dev->model->model_id == ModelId::CANON_8600F || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { use_shdarea = true; } else { use_shdarea = session.params.xres > 600; } if (use_shdarea) { reg->find_reg(REG_0x01).value |= REG_0x01_SHDAREA; } else { reg->find_reg(REG_0x01).value &= ~REG_0x01_SHDAREA; } if (dev->model->model_id == ModelId::CANON_8600F) { reg->find_reg(REG_0x03).value |= REG_0x03_AVEENB; } else { reg->find_reg(REG_0x03).value &= ~REG_0x03_AVEENB; } // FIXME: we probably don't need to set exposure to registers at this point. It was this way // before a refactor. sanei_genesys_set_lamp_power(dev, sensor, *reg, !has_flag(session.params.flags, ScanFlag::DISABLE_LAMP)); /* select XPA */ reg->find_reg(REG_0x03).value &= ~REG_0x03_XPASEL; if (has_flag(session.params.flags, ScanFlag::USE_XPA)) { reg->find_reg(REG_0x03).value |= REG_0x03_XPASEL; } reg->state.is_xpa_on = has_flag(session.params.flags, ScanFlag::USE_XPA); // BW threshold reg->set8(REG_0x2E, 0x7f); reg->set8(REG_0x2F, 0x7f); /* monochrome / color scan */ switch (session.params.depth) { case 8: reg->find_reg(REG_0x04).value &= ~(REG_0x04_LINEART | REG_0x04_BITSET); break; case 16: reg->find_reg(REG_0x04).value &= ~REG_0x04_LINEART; reg->find_reg(REG_0x04).value |= REG_0x04_BITSET; break; } reg->find_reg(REG_0x04).value &= ~(REG_0x04_FILTER | REG_0x04_AFEMOD); if (session.params.channels == 1) { switch (session.params.color_filter) { case ColorFilter::RED: reg->find_reg(REG_0x04).value |= 0x14; break; case ColorFilter::BLUE: reg->find_reg(REG_0x04).value |= 0x1c; break; case ColorFilter::GREEN: reg->find_reg(REG_0x04).value |= 0x18; break; default: break; // should not happen } } else { switch (dev->frontend.layout.type) { case FrontendType::WOLFSON: reg->find_reg(REG_0x04).value |= 0x10; // pixel by pixel break; case FrontendType::ANALOG_DEVICES: reg->find_reg(REG_0x04).value |= 0x20; // slow color pixel by pixel break; default: throw SaneException("Invalid frontend type %d", static_cast(dev->frontend.layout.type)); } } const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, session.output_resolution, session.params.channels, session.params.scan_method); sanei_genesys_set_dpihw(*reg, dpihw_sensor.register_dpihw); if (should_enable_gamma(session, sensor)) { reg->find_reg(REG_0x05).value |= REG_0x05_GMMENB; } else { reg->find_reg(REG_0x05).value &= ~REG_0x05_GMMENB; } reg->set16(REG_DPISET, sensor.register_dpiset); reg->set16(REG_STRPIXEL, session.pixel_startx); reg->set16(REG_ENDPIXEL, session.pixel_endx); /* MAXWD is expressed in 2 words unit */ /* nousedspace = (mem_bank_range * 1024 / 256 -1 ) * 4; */ // BUG: the division by optical and full resolution factor likely does not make sense reg->set24(REG_MAXWD, (session.output_line_bytes * session.optical_resolution / session.full_resolution) >> 1); reg->set16(REG_LPERIOD, exposure / tgtime); reg->set8(REG_DUMMY, sensor.dummy_pixel); } void CommandSetGl843::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, const ScanSession& session) const { DBG_HELPER(dbg); session.assert_computed(); int exposure; int slope_dpi = 0; int dummy = 0; /* we enable true gray for cis scanners only, and just when doing * scan since color calibration is OK for this mode */ dummy = 0; if (dev->model->model_id == ModelId::CANON_4400F && session.params.yres == 1200) { dummy = 1; } /* slope_dpi */ /* cis color scan is effectively a gray scan with 3 gray lines per color line and a FILTER of 0 */ if (dev->model->is_cis) slope_dpi = session.params.yres * session.params.channels; else slope_dpi = session.params.yres; slope_dpi = slope_dpi * (1 + dummy); /* scan_step_type */ exposure = sensor.exposure_lperiod; if (exposure < 0) { throw std::runtime_error("Exposure not defined in sensor definition"); } const auto& motor_profile = get_motor_profile(dev->motor.profiles, exposure, session); // now _LOGICAL_ optical values used are known, setup registers gl843_init_optical_regs_scan(dev, sensor, reg, exposure, session); gl843_init_motor_regs_scan(dev, sensor, session, reg, motor_profile, exposure, slope_dpi, session.optical_line_count, dummy, session.params.starty, session.params.flags); setup_image_pipeline(*dev, session); dev->read_active = true; dev->session = session; dev->total_bytes_read = 0; dev->total_bytes_to_read = (size_t)session.output_line_bytes_requested * (size_t)session.params.lines; DBG(DBG_info, "%s: total bytes to send = %zu\n", __func__, dev->total_bytes_to_read); } ScanSession CommandSetGl843::calculate_scan_session(const Genesys_Device* dev, const Genesys_Sensor& sensor, const Genesys_Settings& settings) const { DBG_HELPER(dbg); debug_dump(DBG_info, settings); ScanFlag flags = ScanFlag::NONE; float move = 0.0f; if (settings.scan_method == ScanMethod::TRANSPARENCY || settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { // note: scanner_move_to_ta() function has already been called and the sensor is at the // transparency adapter if (!dev->ignore_offsets) { move = dev->model->y_offset_ta - dev->model->y_offset_sensor_to_ta; } flags |= ScanFlag::USE_XPA; } else { if (!dev->ignore_offsets) { move = dev->model->y_offset; } } move += settings.tl_y; int move_dpi = dev->motor.base_ydpi; move = static_cast((move * move_dpi) / MM_PER_INCH); float start = 0.0f; if (settings.scan_method==ScanMethod::TRANSPARENCY || settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { start = dev->model->x_offset_ta; } else { start = dev->model->x_offset; } start = start + settings.tl_x; start = static_cast((start * settings.xres) / MM_PER_INCH); ScanSession session; session.params.xres = settings.xres; session.params.yres = settings.yres; session.params.startx = static_cast(start); session.params.starty = static_cast(move); session.params.pixels = settings.pixels; session.params.requested_pixels = settings.requested_pixels; session.params.lines = settings.lines; session.params.depth = settings.depth; session.params.channels = settings.get_channels(); session.params.scan_method = settings.scan_method; session.params.scan_mode = settings.scan_mode; session.params.color_filter = settings.color_filter; session.params.contrast_adjustment = settings.contrast; session.params.brightness_adjustment = settings.brightness; session.params.flags = flags; compute_session(dev, session, sensor); return session; } /** * for fast power saving methods only, like disabling certain amplifiers * @param dev device to use * @param enable true to set inot powersaving * */ void CommandSetGl843::save_power(Genesys_Device* dev, bool enable) const { DBG_HELPER_ARGS(dbg, "enable = %d", enable); // switch KV-SS080 lamp off if (dev->model->gpio_id == GpioId::KVSS080) { std::uint8_t val = dev->interface->read_register(REG_0x6C); if (enable) { val &= 0xef; } else { val |= 0x10; } dev->interface->write_register(REG_0x6C, val); } } void CommandSetGl843::set_powersaving(Genesys_Device* dev, int delay /* in minutes */) const { (void) dev; DBG_HELPER_ARGS(dbg, "delay = %d", delay); } static bool gl843_get_paper_sensor(Genesys_Device* dev) { DBG_HELPER(dbg); std::uint8_t val = dev->interface->read_register(REG_0x6D); return (val & 0x1) == 0; } void CommandSetGl843::eject_document(Genesys_Device* dev) const { (void) dev; DBG_HELPER(dbg); } void CommandSetGl843::load_document(Genesys_Device* dev) const { DBG_HELPER(dbg); (void) dev; } /** * detects end of document and adjust current scan * to take it into account * used by sheetfed scanners */ void CommandSetGl843::detect_document_end(Genesys_Device* dev) const { DBG_HELPER(dbg); bool paper_loaded = gl843_get_paper_sensor(dev); /* sheetfed scanner uses home sensor as paper present */ if (dev->document && !paper_loaded) { DBG(DBG_info, "%s: no more document\n", __func__); dev->document = false; unsigned scanned_lines = 0; catch_all_exceptions(__func__, [&](){ sanei_genesys_read_scancnt(dev, &scanned_lines); }); std::size_t output_lines = dev->session.output_line_count; std::size_t offset_lines = static_cast( (dev->model->post_scan * dev->session.params.yres) / MM_PER_INCH); std::size_t scan_end_lines = scanned_lines + offset_lines; std::size_t remaining_lines = dev->get_pipeline_source().remaining_bytes() / dev->session.output_line_bytes_raw; DBG(DBG_io, "%s: scanned_lines=%u\n", __func__, scanned_lines); DBG(DBG_io, "%s: scan_end_lines=%zu\n", __func__, scan_end_lines); DBG(DBG_io, "%s: output_lines=%zu\n", __func__, output_lines); DBG(DBG_io, "%s: remaining_lines=%zu\n", __func__, remaining_lines); if (scan_end_lines > output_lines) { auto skip_lines = scan_end_lines - output_lines; if (remaining_lines > skip_lines) { remaining_lines -= skip_lines; dev->get_pipeline_source().set_remaining_bytes(remaining_lines * dev->session.output_line_bytes_raw); dev->total_bytes_to_read -= skip_lines * dev->session.output_line_bytes_requested; } } } } // Send the low-level scan command void CommandSetGl843::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, bool start_motor) const { DBG_HELPER(dbg); (void) sensor; /* set up GPIO for scan */ switch(dev->model->gpio_id) { /* KV case */ case GpioId::KVSS080: dev->interface->write_register(REG_0xA9, 0x00); dev->interface->write_register(REG_0xA6, 0xf6); // blinking led dev->interface->write_register(0x7e, 0x04); break; case GpioId::G4050: case GpioId::G4010: dev->interface->write_register(REG_0xA7, 0xfe); dev->interface->write_register(REG_0xA8, 0x3e); dev->interface->write_register(REG_0xA9, 0x06); if ((reg->get8(0x05) & REG_0x05_DPIHW) == REG_0x05_DPIHW_600) { dev->interface->write_register(REG_0x6C, 0x20); dev->interface->write_register(REG_0xA6, 0x44); } else { dev->interface->write_register(REG_0x6C, 0x60); dev->interface->write_register(REG_0xA6, 0x46); } if (reg->state.is_xpa_on && reg->state.is_lamp_on) { dev->cmd_set->set_xpa_lamp_power(*dev, true); } if (reg->state.is_xpa_on) { dev->cmd_set->set_motor_mode(*dev, *reg, MotorMode::PRIMARY_AND_SECONDARY); } // blinking led dev->interface->write_register(REG_0x7E, 0x01); break; case GpioId::CANON_8400F: if (dev->session.params.xres == 3200) { GenesysRegisterSettingSet reg_settings = { { 0x6c, 0x00, 0x02 }, }; apply_reg_settings_to_device(*dev, reg_settings); } if (reg->state.is_xpa_on && reg->state.is_lamp_on) { dev->cmd_set->set_xpa_lamp_power(*dev, true); } if (reg->state.is_xpa_on) { dev->cmd_set->set_motor_mode(*dev, *reg, MotorMode::PRIMARY_AND_SECONDARY); } break; case GpioId::CANON_8600F: if (reg->state.is_xpa_on && reg->state.is_lamp_on) { dev->cmd_set->set_xpa_lamp_power(*dev, true); } if (reg->state.is_xpa_on) { dev->cmd_set->set_motor_mode(*dev, *reg, MotorMode::PRIMARY_AND_SECONDARY); } break; case GpioId::PLUSTEK_OPTICFILM_7200I: case GpioId::PLUSTEK_OPTICFILM_7300: case GpioId::PLUSTEK_OPTICFILM_7500I: { if (reg->state.is_xpa_on && reg->state.is_lamp_on) { dev->cmd_set->set_xpa_lamp_power(*dev, true); } break; } case GpioId::CANON_4400F: default: break; } scanner_clear_scan_and_feed_counts(*dev); // enable scan and motor std::uint8_t val = dev->interface->read_register(REG_0x01); val |= REG_0x01_SCAN; dev->interface->write_register(REG_0x01, val); scanner_start_action(*dev, start_motor); switch (reg->state.motor_mode) { case MotorMode::PRIMARY: { if (reg->state.is_motor_on) { dev->advance_head_pos_by_session(ScanHeadId::PRIMARY); } break; } case MotorMode::PRIMARY_AND_SECONDARY: { if (reg->state.is_motor_on) { dev->advance_head_pos_by_session(ScanHeadId::PRIMARY); dev->advance_head_pos_by_session(ScanHeadId::SECONDARY); } break; } case MotorMode::SECONDARY: { if (reg->state.is_motor_on) { dev->advance_head_pos_by_session(ScanHeadId::SECONDARY); } break; } } } // Send the stop scan command void CommandSetGl843::end_scan(Genesys_Device* dev, Genesys_Register_Set* reg, bool check_stop) const { DBG_HELPER_ARGS(dbg, "check_stop = %d", check_stop); // post scan gpio dev->interface->write_register(0x7e, 0x00); if (reg->state.is_xpa_on) { dev->cmd_set->set_xpa_lamp_power(*dev, false); } if (!dev->model->is_sheetfed) { scanner_stop_action(*dev); } } /** @brief Moves the slider to the home (top) position slowly * */ void CommandSetGl843::move_back_home(Genesys_Device* dev, bool wait_until_home) const { scanner_move_back_home(*dev, wait_until_home); } // init registers for shading calibration shading calibration is done at dpihw void CommandSetGl843::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { DBG_HELPER(dbg); int move; float calib_size_mm = 0; if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { calib_size_mm = dev->model->y_size_calib_ta_mm; } else { calib_size_mm = dev->model->y_size_calib_mm; } unsigned resolution = sensor.shading_resolution; unsigned channels = 3; const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, dev->settings.scan_method); unsigned calib_pixels = 0; unsigned calib_pixels_offset = 0; if (should_calibrate_only_active_area(*dev, dev->settings)) { float offset = dev->model->x_offset_ta; // FIXME: we should use resolution here offset = static_cast((offset * dev->settings.xres) / MM_PER_INCH); float size = dev->model->x_size_ta; size = static_cast((size * dev->settings.xres) / MM_PER_INCH); calib_pixels_offset = static_cast(offset); calib_pixels = static_cast(size); } else { calib_pixels_offset = 0; calib_pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; } ScanFlag flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::DISABLE_BUFFER_FULL_MOVE; if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { // note: scanner_move_to_ta() function has already been called and the sensor is at the // transparency adapter move = static_cast(dev->model->y_offset_calib_white_ta - dev->model->y_offset_sensor_to_ta); if (dev->model->model_id == ModelId::CANON_8600F && resolution == 2400) { move /= 2; } if (dev->model->model_id == ModelId::CANON_8600F && resolution == 4800) { move /= 4; } flags |= ScanFlag::USE_XPA; } else { move = static_cast(dev->model->y_offset_calib_white); } move = static_cast((move * resolution) / MM_PER_INCH); unsigned calib_lines = static_cast(calib_size_mm * resolution / MM_PER_INCH); ScanSession session; session.params.xres = resolution; session.params.yres = resolution; session.params.startx = calib_pixels_offset; session.params.starty = move; session.params.pixels = calib_pixels; session.params.lines = calib_lines; session.params.depth = 16; session.params.channels = channels; session.params.scan_method = dev->settings.scan_method; session.params.scan_mode = dev->settings.scan_mode; session.params.color_filter = dev->settings.color_filter; session.params.contrast_adjustment = dev->settings.contrast; session.params.brightness_adjustment = dev->settings.brightness; session.params.flags = flags; compute_session(dev, session, calib_sensor); init_regs_for_scan_session(dev, calib_sensor, ®s, session); dev->calib_session = session; } /** * This function sends gamma tables to ASIC */ void CommandSetGl843::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const { DBG_HELPER(dbg); int size; int i; size = 256; /* allocate temporary gamma tables: 16 bits words, 3 channels */ std::vector gamma(size * 2 * 3); std::vector rgamma = get_gamma_table(dev, sensor, GENESYS_RED); std::vector ggamma = get_gamma_table(dev, sensor, GENESYS_GREEN); std::vector bgamma = get_gamma_table(dev, sensor, GENESYS_BLUE); // copy sensor specific's gamma tables for (i = 0; i < size; i++) { gamma[i * 2 + size * 0 + 0] = rgamma[i] & 0xff; gamma[i * 2 + size * 0 + 1] = (rgamma[i] >> 8) & 0xff; gamma[i * 2 + size * 2 + 0] = ggamma[i] & 0xff; gamma[i * 2 + size * 2 + 1] = (ggamma[i] >> 8) & 0xff; gamma[i * 2 + size * 4 + 0] = bgamma[i] & 0xff; gamma[i * 2 + size * 4 + 1] = (bgamma[i] >> 8) & 0xff; } dev->interface->write_gamma(0x28, 0x0000, gamma.data(), size * 2 * 3); } /* this function does the led calibration by scanning one line of the calibration area below scanner's top on white strip. -needs working coarse/gain */ SensorExposure CommandSetGl843::led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { return scanner_led_calibration(*dev, sensor, regs); } void CommandSetGl843::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { scanner_offset_calibration(*dev, sensor, regs); } void CommandSetGl843::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs, int dpi) const { scanner_coarse_gain_calibration(*dev, sensor, regs, dpi); } // wait for lamp warmup by scanning the same line until difference // between 2 scans is below a threshold void CommandSetGl843::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg) const { DBG_HELPER(dbg); (void) sensor; unsigned channels = 3; unsigned resolution = dev->model->get_resolution_settings(dev->settings.scan_method) .get_nearest_resolution_x(600); const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, dev->settings.scan_method); unsigned num_pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH / 2; *reg = dev->reg; auto flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::SINGLE_LINE | ScanFlag::IGNORE_STAGGER_OFFSET | ScanFlag::IGNORE_COLOR_OFFSET; if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { flags |= ScanFlag::USE_XPA; } ScanSession session; session.params.xres = resolution; session.params.yres = resolution; session.params.startx = (num_pixels / 2) * resolution / calib_sensor.full_resolution; session.params.starty = 0; session.params.pixels = num_pixels; session.params.lines = 1; session.params.depth = dev->model->bpp_color_values.front(); session.params.channels = channels; session.params.scan_method = dev->settings.scan_method; session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; session.params.color_filter = dev->settings.color_filter; session.params.contrast_adjustment = 0; session.params.brightness_adjustment = 0; session.params.flags = flags; compute_session(dev, session, calib_sensor); init_regs_for_scan_session(dev, calib_sensor, reg, session); sanei_genesys_set_motor_power(*reg, false); } /** * set up GPIO/GPOE for idle state WRITE GPIO[17-21]= GPIO19 WRITE GPOE[17-21]= GPOE21 GPOE20 GPOE19 GPOE18 genesys_write_register(0xa8,0x3e) GPIO(0xa8)=0x3e */ static void gl843_init_gpio(Genesys_Device* dev) { DBG_HELPER(dbg); apply_registers_ordered(dev->gpo.regs, { 0x6e, 0x6f }, [&](const GenesysRegisterSetting& reg) { dev->interface->write_register(reg.address, reg.value); }); } /* * * initialize ASIC from power on condition */ void CommandSetGl843::asic_boot(Genesys_Device* dev, bool cold) const { DBG_HELPER(dbg); std::uint8_t val; if (cold) { dev->interface->write_register(0x0e, 0x01); dev->interface->write_register(0x0e, 0x00); } if(dev->usb_mode == 1) { val = 0x14; } else { val = 0x11; } dev->interface->write_0x8c(0x0f, val); // test CHKVER val = dev->interface->read_register(REG_0x40); if (val & REG_0x40_CHKVER) { val = dev->interface->read_register(0x00); DBG(DBG_info, "%s: reported version for genesys chip is 0x%02x\n", __func__, val); } /* Set default values for registers */ gl843_init_registers (dev); if (dev->model->model_id == ModelId::CANON_8600F) { // turns on vref control for maximum current of the motor driver dev->interface->write_register(REG_0x6B, 0x72); } else { dev->interface->write_register(REG_0x6B, 0x02); } // Write initial registers dev->interface->write_registers(dev->reg); // Enable DRAM by setting a rising edge on bit 3 of reg 0x0b val = dev->reg.find_reg(0x0b).value & REG_0x0B_DRAMSEL; val = (val | REG_0x0B_ENBDRAM); dev->interface->write_register(REG_0x0B, val); dev->reg.find_reg(0x0b).value = val; if (dev->model->model_id == ModelId::CANON_8400F) { dev->interface->write_0x8c(0x1e, 0x01); dev->interface->write_0x8c(0x10, 0xb4); dev->interface->write_0x8c(0x0f, 0x02); } else if (dev->model->model_id == ModelId::CANON_8600F) { dev->interface->write_0x8c(0x10, 0xc8); } else if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { dev->interface->write_0x8c(0x10, 0xd4); } else { dev->interface->write_0x8c(0x10, 0xb4); } /* CLKSET */ int clock_freq = REG_0x0B_48MHZ; switch (dev->model->model_id) { case ModelId::CANON_8600F: clock_freq = REG_0x0B_60MHZ; break; case ModelId::PLUSTEK_OPTICFILM_7200I: clock_freq = REG_0x0B_30MHZ; break; case ModelId::PLUSTEK_OPTICFILM_7300: case ModelId::PLUSTEK_OPTICFILM_7500I: clock_freq = REG_0x0B_40MHZ; break; default: break; } val = (dev->reg.find_reg(0x0b).value & ~REG_0x0B_CLKSET) | clock_freq; dev->interface->write_register(REG_0x0B, val); dev->reg.find_reg(0x0b).value = val; /* prevent further writings by bulk write register */ dev->reg.remove_reg(0x0b); // set RAM read address dev->interface->write_register(REG_0x29, 0x00); dev->interface->write_register(REG_0x2A, 0x00); dev->interface->write_register(REG_0x2B, 0x00); // setup gpio gl843_init_gpio(dev); dev->interface->sleep_ms(100); } /* * * initialize backend and ASIC : registers, motor tables, and gamma tables * then ensure scanner's head is at home */ void CommandSetGl843::init(Genesys_Device* dev) const { DBG_INIT (); DBG_HELPER(dbg); sanei_genesys_asic_init(dev); } void CommandSetGl843::update_hardware_sensors(Genesys_Scanner* s) const { DBG_HELPER(dbg); /* do what is needed to get a new set of events, but try to not lose any of them. */ std::uint8_t val = s->dev->interface->read_register(REG_0x6D); DBG(DBG_io, "%s: read buttons_gpio value=0x%x\n", __func__, (int)val); switch (s->dev->model->gpio_id) { case GpioId::KVSS080: s->buttons[BUTTON_SCAN_SW].write((val & 0x04) == 0); break; case GpioId::G4050: s->buttons[BUTTON_SCAN_SW].write((val & 0x01) == 0); s->buttons[BUTTON_FILE_SW].write((val & 0x02) == 0); s->buttons[BUTTON_EMAIL_SW].write((val & 0x04) == 0); s->buttons[BUTTON_COPY_SW].write((val & 0x08) == 0); break; case GpioId::G4010: s->buttons[BUTTON_FILE_SW].write((val & 0x01) == 0); s->buttons[BUTTON_COPY_SW].write((val & 0x04) == 0); s->buttons[BUTTON_TRANSP_SW].write((val & 0x40) == 0); s->buttons[BUTTON_SCAN_SW].write((val & 0x08) == 0); break; case GpioId::CANON_8400F: s->buttons[BUTTON_COPY_SW].write((val & 0x01) == 0); s->buttons[BUTTON_SCAN_SW].write((val & 0x02) == 0); s->buttons[BUTTON_FILE_SW].write((val & 0x04) == 0); s->buttons[BUTTON_EMAIL_SW].write((val & 0x08) == 0); break; case GpioId::CANON_4400F: s->buttons[BUTTON_COPY_SW].write((val & 0x68) == 0x28); s->buttons[BUTTON_TRANSP_SW].write((val & 0x68) == 0x20); s->buttons[BUTTON_EMAIL_SW].write((val & 0x68) == 0x08); s->buttons[BUTTON_PDF1_SW].write((val & 0x68) == 0x00); s->buttons[BUTTON_PDF2_SW].write((val & 0x68) == 0x60); s->buttons[BUTTON_PDF3_SW].write((val & 0x68) == 0x48); s->buttons[BUTTON_PDF4_SW].write((val & 0x68) == 0x40); break; default: break; } } void CommandSetGl843::update_home_sensor_gpio(Genesys_Device& dev) const { DBG_HELPER(dbg); (void) dev; } /** * Send shading calibration data. The buffer is considered to always hold values * for all the channels. */ void CommandSetGl843::send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t* data, int size) const { DBG_HELPER(dbg); std::uint32_t final_size, i; int count; int offset = 0; unsigned length = size; if (dev->reg.get8(REG_0x01) & REG_0x01_SHDAREA) { offset = dev->session.params.startx * sensor.shading_resolution / dev->session.params.xres; length = dev->session.output_pixels * sensor.shading_resolution / dev->session.params.xres; offset += sensor.shading_pixel_offset; // 16 bit words, 2 words per color, 3 color channels length *= 2 * 2 * 3; offset *= 2 * 2 * 3; } else { offset += sensor.shading_pixel_offset * 2 * 2 * 3; } dev->interface->record_key_value("shading_offset", std::to_string(offset)); dev->interface->record_key_value("shading_length", std::to_string(length)); /* compute and allocate size for final data */ final_size = ((length+251) / 252) * 256; DBG(DBG_io, "%s: final shading size=%04x (length=%d)\n", __func__, final_size, length); std::vector final_data(final_size, 0); /* copy regular shading data to the expected layout */ std::uint8_t* buffer = final_data.data(); count = 0; if (offset < 0) { count += (-offset); length -= (-offset); offset = 0; } if (static_cast(length) + offset > static_cast(size)) { length = size - offset; } /* loop over calibration data */ for (i = 0; i < length; i++) { buffer[count] = data[offset+i]; count++; if ((count % (256*2)) == (252*2)) { count += 4*2; } } dev->interface->write_buffer(0x3c, 0, final_data.data(), count); } bool CommandSetGl843::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const { (void) dev; return true; } void CommandSetGl843::wait_for_motor_stop(Genesys_Device* dev) const { (void) dev; } } // namespace gl843 } // namespace genesys backends-1.3.0/backend/genesys/gl843.h000066400000000000000000000077351456256263500173750ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2010-2013 Stéphane Voltz 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, see . */ #include "genesys.h" #include "command_set_common.h" #ifndef BACKEND_GENESYS_GL843_H #define BACKEND_GENESYS_GL843_H namespace genesys { namespace gl843 { class CommandSetGl843 : public CommandSetCommon { public: ~CommandSetGl843() override = default; bool needs_home_before_init_regs_for_scan(Genesys_Device* dev) const override; void init(Genesys_Device* dev) const override; void init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* regs) const override; void init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const override; void init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, const ScanSession& session) const override; void set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set) const override; void set_powersaving(Genesys_Device* dev, int delay) const override; void save_power(Genesys_Device* dev, bool enable) const override; void begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* regs, bool start_motor) const override; void end_scan(Genesys_Device* dev, Genesys_Register_Set* regs, bool check_stop) const override; void send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const override; void offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const override; void coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs, int dpi) const override; SensorExposure led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const override; void wait_for_motor_stop(Genesys_Device* dev) const override; void move_back_home(Genesys_Device* dev, bool wait_until_home) const override; void update_hardware_sensors(struct Genesys_Scanner* s) const override; void update_home_sensor_gpio(Genesys_Device& dev) const override; void load_document(Genesys_Device* dev) const override; void detect_document_end(Genesys_Device* dev) const override; void eject_document(Genesys_Device* dev) const override; void send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t* data, int size) const override; ScanSession calculate_scan_session(const Genesys_Device* dev, const Genesys_Sensor& sensor, const Genesys_Settings& settings) const override; void asic_boot(Genesys_Device* dev, bool cold) const override; }; enum SlopeTable { SCAN_TABLE = 0, // table 1 at 0x4000 BACKTRACK_TABLE = 1, // table 2 at 0x4800 STOP_TABLE = 2, // table 3 at 0x5000 FAST_TABLE = 3, // table 4 at 0x5800 HOME_TABLE = 4, // table 5 at 0x6000 }; } // namespace gl843 } // namespace genesys #endif // BACKEND_GENESYS_GL843_H backends-1.3.0/backend/genesys/gl843_registers.h000066400000000000000000000345141456256263500214570ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_GL843_REGISTERS_H #define BACKEND_GENESYS_GL843_REGISTERS_H #include namespace genesys { namespace gl843 { using RegAddr = std::uint16_t; using RegMask = std::uint8_t; using RegShift = unsigned; static constexpr RegAddr REG_0x01 = 0x01; static constexpr RegMask REG_0x01_CISSET = 0x80; static constexpr RegMask REG_0x01_DOGENB = 0x40; static constexpr RegMask REG_0x01_DVDSET = 0x20; static constexpr RegMask REG_0x01_STAGGER = 0x10; static constexpr RegMask REG_0x01_COMPENB = 0x08; static constexpr RegMask REG_0x01_TRUEGRAY = 0x04; static constexpr RegMask REG_0x01_SHDAREA = 0x02; static constexpr RegMask REG_0x01_SCAN = 0x01; static constexpr RegAddr REG_0x02 = 0x02; static constexpr RegMask REG_0x02_NOTHOME = 0x80; static constexpr RegMask REG_0x02_ACDCDIS = 0x40; static constexpr RegMask REG_0x02_AGOHOME = 0x20; static constexpr RegMask REG_0x02_MTRPWR = 0x10; static constexpr RegMask REG_0x02_FASTFED = 0x08; static constexpr RegMask REG_0x02_MTRREV = 0x04; static constexpr RegMask REG_0x02_HOMENEG = 0x02; static constexpr RegMask REG_0x02_LONGCURV = 0x01; static constexpr RegAddr REG_0x03 = 0x03; static constexpr RegMask REG_0x03_LAMPDOG = 0x80; static constexpr RegMask REG_0x03_AVEENB = 0x40; static constexpr RegMask REG_0x03_XPASEL = 0x20; static constexpr RegMask REG_0x03_LAMPPWR = 0x10; static constexpr RegMask REG_0x03_LAMPTIM = 0x0f; static constexpr RegAddr REG_0x04 = 0x04; static constexpr RegMask REG_0x04_LINEART = 0x80; static constexpr RegMask REG_0x04_BITSET = 0x40; static constexpr RegMask REG_0x04_AFEMOD = 0x30; static constexpr RegMask REG_0x04_FILTER = 0x0c; static constexpr RegMask REG_0x04_FESET = 0x03; static constexpr RegShift REG_0x04S_AFEMOD = 4; static constexpr RegAddr REG_0x05 = 0x05; static constexpr RegMask REG_0x05_DPIHW = 0xc0; static constexpr RegMask REG_0x05_DPIHW_600 = 0x00; static constexpr RegMask REG_0x05_DPIHW_1200 = 0x40; static constexpr RegMask REG_0x05_DPIHW_2400 = 0x80; static constexpr RegMask REG_0x05_DPIHW_4800 = 0xc0; static constexpr RegMask REG_0x05_MTLLAMP = 0x30; static constexpr RegMask REG_0x05_GMMENB = 0x08; static constexpr RegMask REG_0x05_MTLBASE = 0x03; static constexpr RegAddr REG_0x06 = 0x06; static constexpr RegMask REG_0x06_SCANMOD = 0xe0; static constexpr RegShift REG_0x06S_SCANMOD = 5; static constexpr RegMask REG_0x06_PWRBIT = 0x10; static constexpr RegMask REG_0x06_GAIN4 = 0x08; static constexpr RegMask REG_0x06_OPTEST = 0x07; static constexpr RegMask REG_0x07_LAMPSIM = 0x80; static constexpr RegMask REG_0x08_DECFLAG = 0x40; static constexpr RegMask REG_0x08_GMMFFR = 0x20; static constexpr RegMask REG_0x08_GMMFFG = 0x10; static constexpr RegMask REG_0x08_GMMFFB = 0x08; static constexpr RegMask REG_0x08_GMMZR = 0x04; static constexpr RegMask REG_0x08_GMMZG = 0x02; static constexpr RegMask REG_0x08_GMMZB = 0x01; static constexpr RegMask REG_0x09_MCNTSET = 0xc0; static constexpr RegMask REG_0x09_EVEN1ST = 0x20; static constexpr RegMask REG_0x09_BLINE1ST = 0x10; static constexpr RegMask REG_0x09_BACKSCAN = 0x08; static constexpr RegMask REG_0x09_ENHANCE = 0x04; static constexpr RegMask REG_0x09_SHORTTG = 0x02; static constexpr RegMask REG_0x09_NWAIT = 0x01; static constexpr RegShift REG_0x09S_MCNTSET = 6; static constexpr RegShift REG_0x09S_CLKSET = 4; static constexpr RegAddr REG_0x0B = 0x0b; static constexpr RegMask REG_0x0B_DRAMSEL = 0x07; static constexpr RegMask REG_0x0B_ENBDRAM = 0x08; static constexpr RegMask REG_0x0B_RFHDIS = 0x10; static constexpr RegMask REG_0x0B_CLKSET = 0xe0; static constexpr RegMask REG_0x0B_24MHZ = 0x00; static constexpr RegMask REG_0x0B_30MHZ = 0x20; static constexpr RegMask REG_0x0B_40MHZ = 0x40; static constexpr RegMask REG_0x0B_48MHZ = 0x60; static constexpr RegMask REG_0x0B_60MHZ = 0x80; static constexpr RegAddr REG_0x0D = 0x0d; static constexpr RegMask REG_0x0D_JAMPCMD = 0x80; static constexpr RegMask REG_0x0D_DOCCMD = 0x40; static constexpr RegMask REG_0x0D_CCDCMD = 0x20; static constexpr RegMask REG_0x0D_FULLSTP = 0x10; static constexpr RegMask REG_0x0D_SEND = 0x08; static constexpr RegMask REG_0x0D_CLRMCNT = 0x04; static constexpr RegMask REG_0x0D_CLRDOCJM = 0x02; static constexpr RegMask REG_0x0D_CLRLNCNT = 0x01; static constexpr RegAddr REG_0x0F = 0x0f; static constexpr RegAddr REG_EXPR = 0x10; static constexpr RegAddr REG_EXPG = 0x12; static constexpr RegAddr REG_EXPB = 0x14; static constexpr RegMask REG_0x16_CTRLHI = 0x80; static constexpr RegMask REG_0x16_TOSHIBA = 0x40; static constexpr RegMask REG_0x16_TGINV = 0x20; static constexpr RegMask REG_0x16_CK1INV = 0x10; static constexpr RegMask REG_0x16_CK2INV = 0x08; static constexpr RegMask REG_0x16_CTRLINV = 0x04; static constexpr RegMask REG_0x16_CKDIS = 0x02; static constexpr RegMask REG_0x16_CTRLDIS = 0x01; static constexpr RegMask REG_0x17_TGMODE = 0xc0; static constexpr RegMask REG_0x17_TGMODE_NO_DUMMY = 0x00; static constexpr RegMask REG_0x17_TGMODE_REF = 0x40; static constexpr RegMask REG_0x17_TGMODE_XPA = 0x80; static constexpr RegMask REG_0x17_TGW = 0x3f; static constexpr RegShift REG_0x17S_TGW = 0; static constexpr RegAddr REG_0x18 = 0x18; static constexpr RegMask REG_0x18_CNSET = 0x80; static constexpr RegMask REG_0x18_DCKSEL = 0x60; static constexpr RegMask REG_0x18_CKTOGGLE = 0x10; static constexpr RegMask REG_0x18_CKDELAY = 0x0c; static constexpr RegMask REG_0x18_CKSEL = 0x03; static constexpr RegAddr REG_EXPDMY = 0x19; static constexpr RegMask REG_0x1A_TGLSW2 = 0x80; static constexpr RegMask REG_0x1A_TGLSW1 = 0x40; static constexpr RegMask REG_0x1A_MANUAL3 = 0x02; static constexpr RegMask REG_0x1A_MANUAL1 = 0x01; static constexpr RegMask REG_0x1A_CK4INV = 0x08; static constexpr RegMask REG_0x1A_CK3INV = 0x04; static constexpr RegMask REG_0x1A_LINECLP = 0x02; static constexpr RegAddr REG_0x1C = 0x1c; static constexpr RegMask REG_0x1C_TGTIME = 0x07; static constexpr RegMask REG_0x1D_CK4LOW = 0x80; static constexpr RegMask REG_0x1D_CK3LOW = 0x40; static constexpr RegMask REG_0x1D_CK1LOW = 0x20; static constexpr RegMask REG_0x1D_TGSHLD = 0x1f; static constexpr RegShift REG_0x1DS_TGSHLD = 0; static constexpr RegAddr REG_0x1E = 0x1e; static constexpr RegMask REG_0x1E_WDTIME = 0xf0; static constexpr RegShift REG_0x1ES_WDTIME = 4; static constexpr RegMask REG_0x1E_LINESEL = 0x0f; static constexpr RegShift REG_0x1ES_LINESEL = 0; static constexpr RegAddr REG_0x21 = 0x21; static constexpr RegAddr REG_STEPNO = 0x21; static constexpr RegAddr REG_FWDSTEP = 0x22; static constexpr RegAddr REG_BWDSTEP = 0x23; static constexpr RegAddr REG_FASTNO = 0x24; static constexpr RegAddr REG_LINCNT = 0x25; static constexpr RegAddr REG_0x29 = 0x29; static constexpr RegAddr REG_0x2A = 0x2a; static constexpr RegAddr REG_0x2B = 0x2b; static constexpr RegAddr REG_DPISET = 0x2c; static constexpr RegAddr REG_0x2E = 0x2e; static constexpr RegAddr REG_0x2F = 0x2f; static constexpr RegAddr REG_STRPIXEL = 0x30; static constexpr RegAddr REG_ENDPIXEL = 0x32; static constexpr RegAddr REG_DUMMY = 0x34; static constexpr RegAddr REG_MAXWD = 0x35; static constexpr RegAddr REG_LPERIOD = 0x38; static constexpr RegAddr REG_FEEDL = 0x3d; static constexpr RegAddr REG_0x40 = 0x40; static constexpr RegMask REG_0x40_DOCSNR = 0x80; static constexpr RegMask REG_0x40_ADFSNR = 0x40; static constexpr RegMask REG_0x40_COVERSNR = 0x20; static constexpr RegMask REG_0x40_CHKVER = 0x10; static constexpr RegMask REG_0x40_DOCJAM = 0x08; static constexpr RegMask REG_0x40_HISPDFLG = 0x04; static constexpr RegMask REG_0x40_MOTMFLG = 0x02; static constexpr RegMask REG_0x40_DATAENB = 0x01; static constexpr RegMask REG_0x41_PWRBIT = 0x80; static constexpr RegMask REG_0x41_BUFEMPTY = 0x40; static constexpr RegMask REG_0x41_FEEDFSH = 0x20; static constexpr RegMask REG_0x41_SCANFSH = 0x10; static constexpr RegMask REG_0x41_HOMESNR = 0x08; static constexpr RegMask REG_0x41_LAMPSTS = 0x04; static constexpr RegMask REG_0x41_FEBUSY = 0x02; static constexpr RegMask REG_0x41_MOTORENB = 0x01; static constexpr RegMask REG_0x58_VSMP = 0xf8; static constexpr RegShift REG_0x58S_VSMP = 3; static constexpr RegMask REG_0x58_VSMPW = 0x07; static constexpr RegShift REG_0x58S_VSMPW = 0; static constexpr RegMask REG_0x59_BSMP = 0xf8; static constexpr RegShift REG_0x59S_BSMP = 3; static constexpr RegMask REG_0x59_BSMPW = 0x07; static constexpr RegShift REG_0x59S_BSMPW = 0; static constexpr RegMask REG_0x5A_ADCLKINV = 0x80; static constexpr RegMask REG_0x5A_RLCSEL = 0x40; static constexpr RegMask REG_0x5A_CDSREF = 0x30; static constexpr RegShift REG_0x5AS_CDSREF = 4; static constexpr RegMask REG_0x5A_RLC = 0x0f; static constexpr RegShift REG_0x5AS_RLC = 0; static constexpr RegAddr REG_0x5E = 0x5e; static constexpr RegMask REG_0x5E_DECSEL = 0xe0; static constexpr RegShift REG_0x5ES_DECSEL = 5; static constexpr RegMask REG_0x5E_STOPTIM = 0x1f; static constexpr RegShift REG_0x5ES_STOPTIM = 0; static constexpr RegAddr REG_FMOVDEC = 0x5f; static constexpr RegAddr REG_0x60 = 0x60; static constexpr RegMask REG_0x60_Z1MOD = 0x1f; static constexpr RegAddr REG_0x61 = 0x61; static constexpr RegMask REG_0x61_Z1MOD = 0xff; static constexpr RegAddr REG_0x62 = 0x62; static constexpr RegMask REG_0x62_Z1MOD = 0xff; static constexpr RegAddr REG_0x63 = 0x63; static constexpr RegMask REG_0x63_Z2MOD = 0x1f; static constexpr RegAddr REG_0x64 = 0x64; static constexpr RegMask REG_0x64_Z2MOD = 0xff; static constexpr RegAddr REG_0x65 = 0x65; static constexpr RegMask REG_0x65_Z2MOD = 0xff; static constexpr RegAddr REG_0x67 = 0x67; static constexpr RegAddr REG_0x68 = 0x68; static constexpr RegShift REG_0x67S_STEPSEL = 6; static constexpr RegMask REG_0x67_STEPSEL = 0xc0; static constexpr RegMask REG_0x67_FULLSTEP = 0x00; static constexpr RegMask REG_0x67_HALFSTEP = 0x20; static constexpr RegMask REG_0x67_EIGHTHSTEP = 0x60; static constexpr RegMask REG_0x67_16THSTEP = 0x80; static constexpr RegShift REG_0x68S_FSTPSEL = 6; static constexpr RegMask REG_0x68_FSTPSEL = 0xc0; static constexpr RegMask REG_0x68_FULLSTEP = 0x00; static constexpr RegMask REG_0x68_HALFSTEP = 0x20; static constexpr RegMask REG_0x68_EIGHTHSTEP = 0x60; static constexpr RegMask REG_0x68_16THSTEP = 0x80; static constexpr RegAddr REG_FSHDEC = 0x69; static constexpr RegAddr REG_FMOVNO = 0x6a; static constexpr RegAddr REG_0x6B = 0x6b; static constexpr RegMask REG_0x6B_MULTFILM = 0x80; static constexpr RegMask REG_0x6B_GPOM13 = 0x40; static constexpr RegMask REG_0x6B_GPOM12 = 0x20; static constexpr RegMask REG_0x6B_GPOM11 = 0x10; static constexpr RegMask REG_0x6B_GPOCK4 = 0x08; static constexpr RegMask REG_0x6B_GPOCP = 0x04; static constexpr RegMask REG_0x6B_GPOLEDB = 0x02; static constexpr RegMask REG_0x6B_GPOADF = 0x01; static constexpr RegAddr REG_0x6C = 0x6c; static constexpr RegMask REG_0x6C_GPIO16 = 0x80; static constexpr RegMask REG_0x6C_GPIO15 = 0x40; static constexpr RegMask REG_0x6C_GPIO14 = 0x20; static constexpr RegMask REG_0x6C_GPIO13 = 0x10; static constexpr RegMask REG_0x6C_GPIO12 = 0x08; static constexpr RegMask REG_0x6C_GPIO11 = 0x04; static constexpr RegMask REG_0x6C_GPIO10 = 0x02; static constexpr RegMask REG_0x6C_GPIO9 = 0x01; static constexpr RegMask REG_0x6C_GPIOH = 0xff; static constexpr RegMask REG_0x6C_GPIOL = 0xff; static constexpr RegAddr REG_Z1MOD = 0x60; static constexpr RegAddr REG_Z2MOD = 0x63; static constexpr RegAddr REG_0x6D = 0x6d; static constexpr RegAddr REG_0x6E = 0x6e; static constexpr RegAddr REG_0x6F = 0x6f; static constexpr RegAddr REG_CK1MAP = 0x74; static constexpr RegAddr REG_CK3MAP = 0x77; static constexpr RegAddr REG_CK4MAP = 0x7a; static constexpr RegAddr REG_0x7E = 0x7e; static constexpr RegAddr REG_0x80 = 0x80; static constexpr RegMask REG_0x80_TABLE1_NORMAL = 0x03; static constexpr RegShift REG_0x80S_TABLE1_NORMAL = 0; static constexpr RegMask REG_0x80_TABLE2_BACK = 0x0c; static constexpr RegShift REG_0x80S_TABLE2_BACK = 2; static constexpr RegMask REG_0x80_TABLE4_FAST = 0x30; static constexpr RegShift REG_0x80S_TABLE4_FAST = 4; static constexpr RegMask REG_0x80_TABLE5_GO_HOME = 0xc0; static constexpr RegShift REG_0x80S_TABLE5_GO_HOME = 6; static constexpr RegAddr REG_0x9D = 0x9d; static constexpr RegShift REG_0x9DS_STEPTIM = 2; static constexpr RegMask REG_0x87_LEDADD = 0x04; static constexpr RegAddr REG_0xA6 = 0xa6; static constexpr RegMask REG_0xA6_GPIO24 = 0x80; static constexpr RegMask REG_0xA6_GPIO23 = 0x40; static constexpr RegMask REG_0xA6_GPIO22 = 0x20; static constexpr RegMask REG_0xA6_GPIO21 = 0x10; static constexpr RegMask REG_0xA6_GPIO20 = 0x08; static constexpr RegMask REG_0xA6_GPIO19 = 0x04; static constexpr RegMask REG_0xA6_GPIO18 = 0x02; static constexpr RegMask REG_0xA6_GPIO17 = 0x01; static constexpr RegAddr REG_0xA7 = 0xa7; static constexpr RegMask REG_0xA7_GPOE24 = 0x80; static constexpr RegMask REG_0xA7_GPOE23 = 0x40; static constexpr RegMask REG_0xA7_GPOE22 = 0x20; static constexpr RegMask REG_0xA7_GPOE21 = 0x10; static constexpr RegMask REG_0xA7_GPOE20 = 0x08; static constexpr RegMask REG_0xA7_GPOE19 = 0x04; static constexpr RegMask REG_0xA7_GPOE18 = 0x02; static constexpr RegMask REG_0xA7_GPOE17 = 0x01; static constexpr RegAddr REG_0xA8 = 0xa8; static constexpr RegMask REG_0xA8_GPOE27 = 0x20; static constexpr RegMask REG_0xA8_GPOE26 = 0x10; static constexpr RegMask REG_0xA8_GPOE25 = 0x08; static constexpr RegMask REG_0xA8_GPO27 = 0x04; static constexpr RegMask REG_0xA8_GPO26 = 0x02; static constexpr RegMask REG_0xA8_GPO25 = 0x01; static constexpr RegAddr REG_0xA9 = 0xa9; static constexpr RegMask REG_0xA9_GPO33 = 0x20; static constexpr RegMask REG_0xA9_GPO32 = 0x10; static constexpr RegMask REG_0xA9_GPO31 = 0x08; static constexpr RegMask REG_0xA9_GPO30 = 0x04; static constexpr RegMask REG_0xA9_GPO29 = 0x02; static constexpr RegMask REG_0xA9_GPO28 = 0x01; } // namespace gl843 } // namespace genesys #endif // BACKEND_GENESYS_GL843_REGISTERS_H backends-1.3.0/backend/genesys/gl846.cpp000066400000000000000000001145251456256263500177270ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2012-2013 Stéphane Voltz 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, see . */ /** @file * * This file handles GL846 and GL845 ASICs since they are really close to each other. */ #define DEBUG_DECLARE_ONLY #include "gl846.h" #include "gl846_registers.h" #include "test_settings.h" #include namespace genesys { namespace gl846 { /** * compute the step multiplier used */ static int gl846_get_step_multiplier (Genesys_Register_Set * regs) { unsigned value = (regs->get8(0x9d) & 0x0f) >> 1; return 1 << value; } /** @brief set all registers to default values . * This function is called only once at the beginning and * fills register startup values for registers reused across scans. * Those that are rarely modified or not modified are written * individually. * @param dev device structure holding register set to initialize */ static void gl846_init_registers (Genesys_Device * dev) { DBG_HELPER(dbg); dev->reg.clear(); dev->reg.init_reg(0x01, 0x60); if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) { dev->reg.init_reg(0x01, 0x22); } dev->reg.init_reg(0x02, 0x38); dev->reg.init_reg(0x03, 0x03); if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) { dev->reg.init_reg(0x03, 0xbf); } dev->reg.init_reg(0x04, 0x22); dev->reg.init_reg(0x05, 0x60); if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) { dev->reg.init_reg(0x05, 0x48); } dev->reg.init_reg(0x06, 0x10); if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) { dev->reg.init_reg(0x06, 0xf0); } dev->reg.init_reg(0x08, 0x60); if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) { dev->reg.init_reg(0x08, 0x00); } dev->reg.init_reg(0x09, 0x00); dev->reg.init_reg(0x0a, 0x00); dev->reg.init_reg(0x0b, 0x8b); if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { dev->reg.init_reg(0x0b, 0x2a); } if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) { dev->reg.init_reg(0x0b, 0x4a); } dev->reg.init_reg(0x0c, 0x00); dev->reg.init_reg(0x0d, 0x00); dev->reg.init_reg(0x10, 0x00); // exposure, set during sensor setup dev->reg.init_reg(0x11, 0x00); // exposure, set during sensor setup dev->reg.init_reg(0x12, 0x00); // exposure, set during sensor setup dev->reg.init_reg(0x13, 0x00); // exposure, set during sensor setup dev->reg.init_reg(0x14, 0x00); // exposure, set during sensor setup dev->reg.init_reg(0x15, 0x00); // exposure, set during sensor setup dev->reg.init_reg(0x16, 0xbb); // SENSOR_DEF dev->reg.init_reg(0x17, 0x13); // SENSOR_DEF dev->reg.init_reg(0x18, 0x10); // SENSOR_DEF dev->reg.init_reg(0x19, 0x2a); // SENSOR_DEF dev->reg.init_reg(0x1a, 0x34); // SENSOR_DEF dev->reg.init_reg(0x1b, 0x00); // SENSOR_DEF dev->reg.init_reg(0x1c, 0x20); // SENSOR_DEF dev->reg.init_reg(0x1d, 0x06); // SENSOR_DEF dev->reg.init_reg(0x1e, 0xf0); // WDTIME, LINESEL: set during sensor and motor setup // SCANFED dev->reg.init_reg(0x1f, 0x01); if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400) { dev->reg.init_reg(0x1f, 0x00); } dev->reg.init_reg(0x20, 0x03); if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) { dev->reg.init_reg(0x20, 0x55); } dev->reg.init_reg(0x21, 0x10); // STEPNO: set during motor setup dev->reg.init_reg(0x22, 0x60); // FWDSTEP: set during motor setup dev->reg.init_reg(0x23, 0x60); // BWDSTEP: set during motor setup dev->reg.init_reg(0x24, 0x60); // FASTNO: set during motor setup dev->reg.init_reg(0x25, 0x00); // LINCNT: set during motor setup dev->reg.init_reg(0x26, 0x00); // LINCNT: set during motor setup dev->reg.init_reg(0x27, 0x00); // LINCNT: set during motor setup dev->reg.init_reg(0x2c, 0x00); // DPISET: set during sensor setup dev->reg.init_reg(0x2d, 0x00); // DPISET: set during sensor setup dev->reg.init_reg(0x2e, 0x80); // BWHI: set during sensor setup dev->reg.init_reg(0x2f, 0x80); // BWLOW: set during sensor setup dev->reg.init_reg(0x30, 0x00); // STRPIXEL: set during sensor setup dev->reg.init_reg(0x31, 0x00); // STRPIXEL: set during sensor setup dev->reg.init_reg(0x32, 0x00); // ENDPIXEL: set during sensor setup dev->reg.init_reg(0x33, 0x00); // ENDPIXEL: set during sensor setup // DUMMY: the number of CCD dummy pixels dev->reg.init_reg(0x34, 0x1f); if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) { dev->reg.init_reg(0x34, 0x14); } dev->reg.init_reg(0x35, 0x00); // MAXWD: set during scan setup dev->reg.init_reg(0x36, 0x40); // MAXWD: set during scan setup dev->reg.init_reg(0x37, 0x00); // MAXWD: set during scan setup dev->reg.init_reg(0x38, 0x2a); // LPERIOD: set during sensor setup dev->reg.init_reg(0x39, 0xf8); // LPERIOD: set during sensor setup dev->reg.init_reg(0x3d, 0x00); // FEEDL: set during motor setup dev->reg.init_reg(0x3e, 0x00); // FEEDL: set during motor setup dev->reg.init_reg(0x3f, 0x01); // FEEDL: set during motor setup dev->reg.init_reg(0x52, 0x02); // SENSOR_DEF dev->reg.init_reg(0x53, 0x04); // SENSOR_DEF dev->reg.init_reg(0x54, 0x06); // SENSOR_DEF dev->reg.init_reg(0x55, 0x08); // SENSOR_DEF dev->reg.init_reg(0x56, 0x0a); // SENSOR_DEF dev->reg.init_reg(0x57, 0x00); // SENSOR_DEF dev->reg.init_reg(0x58, 0x59); // SENSOR_DEF dev->reg.init_reg(0x59, 0x31); // SENSOR_DEF dev->reg.init_reg(0x5a, 0x40); // SENSOR_DEF // DECSEL, STEPTIM dev->reg.init_reg(0x5e, 0x1f); if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) { dev->reg.init_reg(0x5e, 0x01); } dev->reg.init_reg(0x5f, 0x01); // FMOVDEC: overwritten during motor setup dev->reg.init_reg(0x60, 0x00); // STEPSEL, Z1MOD: overwritten during motor setup dev->reg.init_reg(0x61, 0x00); // Z1MOD: overwritten during motor setup dev->reg.init_reg(0x62, 0x00); // Z1MOD: overwritten during motor setup dev->reg.init_reg(0x63, 0x00); // FSTPSEL, Z2MOD: overwritten during motor setup dev->reg.init_reg(0x64, 0x00); // Z2MOD: overwritten during motor setup dev->reg.init_reg(0x65, 0x00); // Z2MOD: overwritten during motor setup dev->reg.init_reg(0x67, 0x7f); // MTRPWM: overwritten during motor setup dev->reg.init_reg(0x68, 0x7f); // FASTPWM: overwritten during motor setup dev->reg.init_reg(0x69, 0x01); // FSHDEC: overwritten during motor setup dev->reg.init_reg(0x6a, 0x01); // FMOVNO: overwritten during motor setup // 0x6b, 0x6c, 0x6d, 0x6e, 0x6f - gpio dev->reg.init_reg(0x70, 0x01); // SENSOR_DEF dev->reg.init_reg(0x71, 0x00); // SENSOR_DEF dev->reg.init_reg(0x72, 0x02); // SENSOR_DEF dev->reg.init_reg(0x73, 0x01); // SENSOR_DEF dev->reg.init_reg(0x74, 0x00); // SENSOR_DEF dev->reg.init_reg(0x75, 0x00); // SENSOR_DEF dev->reg.init_reg(0x76, 0x00); // SENSOR_DEF dev->reg.init_reg(0x77, 0x00); // SENSOR_DEF dev->reg.init_reg(0x78, 0x00); // SENSOR_DEF dev->reg.init_reg(0x79, 0x3f); // SENSOR_DEF dev->reg.init_reg(0x7a, 0x00); // SENSOR_DEF dev->reg.init_reg(0x7b, 0x09); // SENSOR_DEF dev->reg.init_reg(0x7c, 0x99); // SENSOR_DEF dev->reg.init_reg(0x7d, 0x20); // SENSOR_DEF dev->reg.init_reg(0x7f, 0x05); if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) { dev->reg.init_reg(0x7f, 0x00); } dev->reg.init_reg(0x80, 0x4f); // overwritten during motor setup dev->reg.init_reg(0x87, 0x02); // SENSOR_DEF // MTRPLS: pulse width of ADF motor trigger signal dev->reg.init_reg(0x94, 0x00); if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { dev->reg.init_reg(0x94, 0xff); } if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { dev->reg.init_reg(0x98, 0x20); // ONDUR dev->reg.init_reg(0x99, 0x00); // ONDUR dev->reg.init_reg(0x9a, 0x90); // OFFDUR dev->reg.init_reg(0x9b, 0x00); // OFFDUR } dev->reg.init_reg(0x9d, 0x00); // contains STEPTIM if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { dev->reg.init_reg(0x9d, 0x04); } dev->reg.init_reg(0x9e, 0x00); if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { dev->reg.init_reg(0xa1, 0xe0); } // RFHSET (SDRAM refresh time) dev->reg.init_reg(0xa2, 0x1f); if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) { dev->reg.init_reg(0xa2, 0x0f); } // 0xa6, 0xa7 0xa8, 0xa9 - gpio // Various important settings: GPOM9, MULSTOP, NODECEL, TB3TB1, TB5TB2, FIX16CLK dev->reg.init_reg(0xab, 0xc0); if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) { dev->reg.init_reg(0xab, 0x01); } if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { dev->reg.init_reg(0xbb, 0x00); // FIXME: default is the same } if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { dev->reg.init_reg(0xbc, 0x0f); dev->reg.init_reg(0xdb, 0xff); } if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400) { dev->reg.init_reg(0xbe, 0x07); } // 0xd0, 0xd1, 0xd2 - SH0DWN, SH1DWN, SH2DWN - shading bank[0..2] for CCD. // Set during memory layout setup // [0xe0..0xf7] - image buffer addresses. Set during memory layout setup dev->reg.init_reg(0xf8, 0x05); // MAXSEL, MINSEL if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { dev->reg.init_reg(0xfe, 0x08); // MOTTGST, AUTO_O dev->reg.init_reg(0xff, 0x02); // AUTO_S } const auto& sensor = sanei_genesys_find_sensor_any(dev); const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, sensor.full_resolution, 3, dev->model->default_method); sanei_genesys_set_dpihw(dev->reg, dpihw_sensor.register_dpihw); } /** * Set register values of Analog Device type frontend * */ static void gl846_set_adi_fe(Genesys_Device* dev, std::uint8_t set) { DBG_HELPER(dbg); int i; // wait for FE to be ready auto status = scanner_read_status(*dev); while (status.is_front_end_busy) { dev->interface->sleep_ms(10); status = scanner_read_status(*dev); }; if (set == AFE_INIT) { dev->frontend = dev->frontend_initial; } // write them to analog frontend dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00)); dev->interface->write_fe_register(0x01, dev->frontend.regs.get_value(0x01)); for (i = 0; i < 3; i++) { dev->interface->write_fe_register(0x02 + i, dev->frontend.get_gain(i)); } for (i = 0; i < 3; i++) { dev->interface->write_fe_register(0x05 + i, dev->frontend.get_offset(i)); } } // Set values of analog frontend void CommandSetGl846::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set) const { DBG_HELPER_ARGS(dbg, "%s", set == AFE_INIT ? "init" : set == AFE_SET ? "set" : set == AFE_POWER_SAVE ? "powersave" : "huh?"); (void) sensor; // route to specific analog frontend setup std::uint8_t frontend_type = dev->reg.find_reg(0x04).value & REG_0x04_FESET; switch (frontend_type) { case 0x02: /* ADI FE */ gl846_set_adi_fe(dev, set); break; default: throw SaneException("unsupported frontend type %d", frontend_type); } } // @brief set up motor related register for scan static void gl846_init_motor_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, const ScanSession& session, Genesys_Register_Set* reg, const MotorProfile& motor_profile, unsigned int scan_exposure_time, unsigned scan_yres, unsigned int scan_lines, unsigned int scan_dummy, unsigned int feed_steps, ScanFlag flags) { DBG_HELPER_ARGS(dbg, "scan_exposure_time=%d, scan_yres=%d, step_type=%d, scan_lines=%d, " "scan_dummy=%d, feed_steps=%d, flags=%x", scan_exposure_time, scan_yres, static_cast(motor_profile.step_type), scan_lines, scan_dummy, feed_steps, static_cast(flags)); unsigned step_multiplier = gl846_get_step_multiplier(reg); reg->set24(REG_LINCNT, scan_lines); reg->set8(REG_0x02, 0); sanei_genesys_set_motor_power(*reg, true); std::uint8_t reg02 = reg->get8(REG_0x02); reg02 &= ~REG_0x02_FASTFED; if (has_flag(flags, ScanFlag::AUTO_GO_HOME)) { reg02 |= REG_0x02_AGOHOME | REG_0x02_NOTHOME; } if (has_flag(flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE) || (scan_yres>=sensor.full_resolution)) { reg02 |= REG_0x02_ACDCDIS; } if (has_flag(flags, ScanFlag::REVERSE)) { reg02 |= REG_0x02_MTRREV; } else { reg02 &= ~REG_0x02_MTRREV; } reg->set8(REG_0x02, reg02); // scan and backtracking slope table auto scan_table = create_slope_table(dev->model->asic_type, dev->motor, scan_yres, scan_exposure_time, step_multiplier, motor_profile); scanner_send_slope_table(dev, sensor, SCAN_TABLE, scan_table.table); scanner_send_slope_table(dev, sensor, BACKTRACK_TABLE, scan_table.table); scanner_send_slope_table(dev, sensor, STOP_TABLE, scan_table.table); reg->set8(REG_STEPNO, scan_table.table.size() / step_multiplier); reg->set8(REG_FASTNO, scan_table.table.size() / step_multiplier); reg->set8(REG_FSHDEC, scan_table.table.size() / step_multiplier); // fast table const auto* fast_profile = get_motor_profile_ptr(dev->motor.fast_profiles, 0, session); if (fast_profile == nullptr) { fast_profile = &motor_profile; } auto fast_table = create_slope_table_fastest(dev->model->asic_type, step_multiplier, *fast_profile); scanner_send_slope_table(dev, sensor, FAST_TABLE, fast_table.table); scanner_send_slope_table(dev, sensor, HOME_TABLE, fast_table.table); reg->set8(REG_FMOVNO, fast_table.table.size() / step_multiplier); reg->set8(REG_FMOVDEC, fast_table.table.size() / step_multiplier); if (motor_profile.motor_vref != -1 && fast_profile->motor_vref != 1) { std::uint8_t vref = 0; vref |= (motor_profile.motor_vref << REG_0x80S_TABLE1_NORMAL) & REG_0x80_TABLE1_NORMAL; vref |= (motor_profile.motor_vref << REG_0x80S_TABLE2_BACK) & REG_0x80_TABLE2_BACK; vref |= (fast_profile->motor_vref << REG_0x80S_TABLE4_FAST) & REG_0x80_TABLE4_FAST; vref |= (fast_profile->motor_vref << REG_0x80S_TABLE5_GO_HOME) & REG_0x80_TABLE5_GO_HOME; reg->set8(REG_0x80, vref); } unsigned feedl = feed_steps; unsigned dist = 0; feedl <<= static_cast(motor_profile.step_type); dist = scan_table.table.size(); if (has_flag(flags, ScanFlag::FEEDING)) { dist *= 2; } // check for overflow if (dist < feedl) { feedl -= dist; } else { feedl = 0; } reg->set24(REG_FEEDL, feedl); unsigned ccdlmt = (reg->get8(REG_0x0C) & REG_0x0C_CCDLMT) + 1; unsigned tgtime = 1 << (reg->get8(REG_0x1C) & REG_0x1C_TGTIME); /* hi res motor speed GPIO */ /* uint8_t effective = dev->interface->read_register(REG_0x6C); */ /* if quarter step, bipolar Vref2 */ /* XXX STEF XXX GPIO if (motor_profile.step_type > 1) { if (motor_profile.step_type < 3) { val = effective & ~REG_0x6C_GPIO13; } else { val = effective | REG_0x6C_GPIO13; } } else { val = effective; } dev->interface->write_register(REG_0x6C, val); */ /* effective scan */ /* effective = dev->interface->read_register(REG_0x6C); val = effective | REG_0x6C_GPIO10; dev->interface->write_register(REG_0x6C, val); */ unsigned min_restep = (scan_table.table.size() / step_multiplier) / 2 - 1; if (min_restep < 1) { min_restep = 1; } reg->set8(REG_FWDSTEP, min_restep); reg->set8(REG_BWDSTEP, min_restep); std::uint32_t z1, z2; sanei_genesys_calculate_zmod(false, scan_exposure_time * ccdlmt * tgtime, scan_table.table, scan_table.table.size(), feedl, min_restep * step_multiplier, &z1, &z2); reg->set24(REG_0x60, z1 | (static_cast(motor_profile.step_type) << (16 + REG_0x60S_STEPSEL))); reg->set24(REG_0x63, z2 | (static_cast(motor_profile.step_type) << (16 + REG_0x63S_FSTPSEL))); reg->set8_mask(REG_0x1E, scan_dummy, 0x0f); reg->set8(REG_0x67, 0x7f); reg->set8(REG_0x68, 0x7f); } /** @brief set up registers related to sensor * Set up the following registers 0x01 0x03 0x10-0x015 R/G/B exposures 0x19 EXPDMY 0x2e BWHI 0x2f BWLO 0x04 0x87 0x05 0x2c,0x2d DPISET 0x30,0x31 STRPIXEL 0x32,0x33 ENDPIXEL 0x35,0x36,0x37 MAXWD [25:2] (>>2) 0x38,0x39 LPERIOD 0x34 DUMMY */ static void gl846_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, unsigned int exposure_time, const ScanSession& session) { DBG_HELPER_ARGS(dbg, "exposure_time=%d", exposure_time); scanner_setup_sensor(*dev, sensor, *reg); dev->cmd_set->set_fe(dev, sensor, AFE_SET); /* enable shading */ regs_set_optical_off(dev->model->asic_type, *reg); reg->find_reg(REG_0x01).value |= REG_0x01_SHDAREA; if (has_flag(session.params.flags, ScanFlag::DISABLE_SHADING) || has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION) || session.use_host_side_calib) { reg->find_reg(REG_0x01).value &= ~REG_0x01_DVDSET; } else { reg->find_reg(REG_0x01).value |= REG_0x01_DVDSET; } reg->find_reg(REG_0x03).value &= ~REG_0x03_AVEENB; sanei_genesys_set_lamp_power(dev, sensor, *reg, !has_flag(session.params.flags, ScanFlag::DISABLE_LAMP)); reg->state.is_xpa_on = has_flag(session.params.flags, ScanFlag::USE_XPA); // BW threshold reg->set8(0x2e, 0x7f); reg->set8(0x2f, 0x7f); /* monochrome / color scan */ switch (session.params.depth) { case 8: reg->find_reg(REG_0x04).value &= ~(REG_0x04_LINEART | REG_0x04_BITSET); break; case 16: reg->find_reg(REG_0x04).value &= ~REG_0x04_LINEART; reg->find_reg(REG_0x04).value |= REG_0x04_BITSET; break; } reg->find_reg(REG_0x04).value &= ~(REG_0x04_FILTER | REG_0x04_AFEMOD); if (session.params.channels == 1) { switch (session.params.color_filter) { case ColorFilter::RED: reg->find_reg(REG_0x04).value |= 0x24; break; case ColorFilter::BLUE: reg->find_reg(REG_0x04).value |= 0x2c; break; case ColorFilter::GREEN: reg->find_reg(REG_0x04).value |= 0x28; break; default: break; // should not happen } } else { reg->find_reg(REG_0x04).value |= 0x20; // mono } const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, session.output_resolution, session.params.channels, session.params.scan_method); sanei_genesys_set_dpihw(*reg, dpihw_sensor.register_dpihw); if (should_enable_gamma(session, sensor)) { reg->find_reg(REG_0x05).value |= REG_0x05_GMMENB; } else { reg->find_reg(REG_0x05).value &= ~REG_0x05_GMMENB; } /* CIS scanners can do true gray by setting LEDADD */ /* we set up LEDADD only when asked */ if (dev->model->is_cis) { reg->find_reg(0x87).value &= ~REG_0x87_LEDADD; if (session.enable_ledadd) { reg->find_reg(0x87).value |= REG_0x87_LEDADD; } /* RGB weighting reg->find_reg(0x01).value &= ~REG_0x01_TRUEGRAY; if (session.enable_ledadd)) { reg->find_reg(0x01).value |= REG_0x01_TRUEGRAY; }*/ } reg->set16(REG_DPISET, sensor.register_dpiset); reg->set16(REG_STRPIXEL, session.pixel_startx); reg->set16(REG_ENDPIXEL, session.pixel_endx); setup_image_pipeline(*dev, session); /* MAXWD is expressed in 4 words unit */ // BUG: we shouldn't multiply by channels here reg->set24(REG_MAXWD, (session.output_line_bytes_raw * session.params.channels >> 2)); reg->set16(REG_LPERIOD, exposure_time); reg->set8(0x34, sensor.dummy_pixel); } void CommandSetGl846::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, const ScanSession& session) const { DBG_HELPER(dbg); session.assert_computed(); int exposure_time; int slope_dpi = 0; // FIXME: on cis scanners we may want to scan at reduced resolution int dummy = 0; /* slope_dpi */ /* cis color scan is effectively a gray scan with 3 gray lines per color line and a FILTER of 0 */ if (dev->model->is_cis) { slope_dpi = session.params.yres * session.params.channels; } else { slope_dpi = session.params.yres; } slope_dpi = slope_dpi * (1 + dummy); exposure_time = sensor.exposure_lperiod; const auto& motor_profile = get_motor_profile(dev->motor.profiles, exposure_time, session); /* we enable true gray for cis scanners only, and just when doing * scan since color calibration is OK for this mode */ gl846_init_optical_regs_scan(dev, sensor, reg, exposure_time, session); gl846_init_motor_regs_scan(dev, sensor, session, reg, motor_profile, exposure_time, slope_dpi, session.optical_line_count, dummy, session.params.starty, session.params.flags); /*** prepares data reordering ***/ dev->read_active = true; dev->session = session; dev->total_bytes_read = 0; dev->total_bytes_to_read = (size_t)session.output_line_bytes_requested * (size_t)session.params.lines; DBG(DBG_info, "%s: total bytes to send = %zu\n", __func__, dev->total_bytes_to_read); } ScanSession CommandSetGl846::calculate_scan_session(const Genesys_Device* dev, const Genesys_Sensor& sensor, const Genesys_Settings& settings) const { DBG(DBG_info, "%s ", __func__); debug_dump(DBG_info, settings); ScanFlag flags = ScanFlag::NONE; unsigned move_dpi = dev->motor.base_ydpi; float move = dev->model->y_offset; if (settings.scan_method == ScanMethod::TRANSPARENCY || settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { // note: scanner_move_to_ta() function has already been called and the sensor is at the // transparency adapter if (!dev->ignore_offsets) { move = dev->model->y_offset_ta - dev->model->y_offset_sensor_to_ta; } flags |= ScanFlag::USE_XPA; } else { if (!dev->ignore_offsets) { move = dev->model->y_offset; } } move = move + settings.tl_y; move = static_cast((move * move_dpi) / MM_PER_INCH); move -= dev->head_pos(ScanHeadId::PRIMARY); float start = dev->model->x_offset; if (settings.scan_method == ScanMethod::TRANSPARENCY || settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { start = dev->model->x_offset_ta; } else { start = dev->model->x_offset; } start = start + dev->settings.tl_x; start = static_cast((start * settings.xres) / MM_PER_INCH); ScanSession session; session.params.xres = settings.xres; session.params.yres = settings.yres; session.params.startx = static_cast(start); session.params.starty = static_cast(move); session.params.pixels = settings.pixels; session.params.requested_pixels = settings.requested_pixels; session.params.lines = settings.lines; session.params.depth = settings.depth; session.params.channels = settings.get_channels(); session.params.scan_method = settings.scan_method; session.params.scan_mode = settings.scan_mode; session.params.color_filter = settings.color_filter; session.params.contrast_adjustment = settings.contrast; session.params.brightness_adjustment = settings.brightness; // backtracking isn't handled well, so don't enable it session.params.flags = flags; compute_session(dev, session, sensor); return session; } // for fast power saving methods only, like disabling certain amplifiers void CommandSetGl846::save_power(Genesys_Device* dev, bool enable) const { (void) dev; DBG_HELPER_ARGS(dbg, "enable = %d", enable); } void CommandSetGl846::set_powersaving(Genesys_Device* dev, int delay /* in minutes */) const { (void) dev; DBG_HELPER_ARGS(dbg, "delay = %d", delay); } // Send the low-level scan command void CommandSetGl846::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, bool start_motor) const { DBG_HELPER(dbg); (void) sensor; if (reg->state.is_xpa_on && reg->state.is_lamp_on) { dev->cmd_set->set_xpa_lamp_power(*dev, true); } scanner_clear_scan_and_feed_counts(*dev); std::uint8_t val = dev->interface->read_register(REG_0x01); val |= REG_0x01_SCAN; dev->interface->write_register(REG_0x01, val); reg->set8(REG_0x01, val); scanner_start_action(*dev, start_motor); dev->advance_head_pos_by_session(ScanHeadId::PRIMARY); } // Send the stop scan command void CommandSetGl846::end_scan(Genesys_Device* dev, Genesys_Register_Set* reg, bool check_stop) const { (void) reg; DBG_HELPER_ARGS(dbg, "check_stop = %d", check_stop); if (reg->state.is_xpa_on) { dev->cmd_set->set_xpa_lamp_power(*dev, false); } if (!dev->model->is_sheetfed) { scanner_stop_action(*dev); } } // Moves the slider to the home (top) position slowly void CommandSetGl846::move_back_home(Genesys_Device* dev, bool wait_until_home) const { scanner_move_back_home(*dev, wait_until_home); } // init registers for shading calibration void CommandSetGl846::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { DBG_HELPER(dbg); unsigned move_dpi = dev->motor.base_ydpi; float calib_size_mm = 0; if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { calib_size_mm = dev->model->y_size_calib_ta_mm; } else { calib_size_mm = dev->model->y_size_calib_mm; } unsigned channels = 3; unsigned resolution = sensor.shading_resolution; const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, dev->settings.scan_method); float move = 0; ScanFlag flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::DISABLE_BUFFER_FULL_MOVE; if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { // note: scanner_move_to_ta() function has already been called and the sensor is at the // transparency adapter move = static_cast(dev->model->y_offset_calib_white_ta - dev->model->y_offset_sensor_to_ta); flags |= ScanFlag::USE_XPA; } else { move = static_cast(dev->model->y_offset_calib_white); } move = static_cast((move * move_dpi) / MM_PER_INCH); unsigned calib_lines = static_cast(calib_size_mm * resolution / MM_PER_INCH); ScanSession session; session.params.xres = resolution; session.params.yres = resolution; session.params.startx = 0; session.params.starty = static_cast(move); session.params.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; session.params.lines = calib_lines; session.params.depth = 16; session.params.channels = channels; session.params.scan_method = dev->settings.scan_method; session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; session.params.color_filter = dev->settings.color_filter; session.params.contrast_adjustment = dev->settings.contrast; session.params.brightness_adjustment = dev->settings.brightness; session.params.flags = flags; compute_session(dev, session, calib_sensor); init_regs_for_scan_session(dev, calib_sensor, ®s, session); /* we use ModelFlag::SHADING_REPARK */ dev->set_head_pos_zero(ScanHeadId::PRIMARY); dev->calib_session = session; } /** * Send shading calibration data. The buffer is considered to always hold values * for all the channels. */ void CommandSetGl846::send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t* data, int size) const { DBG_HELPER_ARGS(dbg, "writing %d bytes of shading data", size); std::uint32_t addr, i; unsigned length = static_cast(size / 3); // we're using SHDAREA, thus we only need to upload part of the line unsigned offset = dev->session.pixel_count_ratio.apply( dev->session.params.startx * sensor.full_resolution / dev->session.params.xres); unsigned pixels = dev->session.pixel_count_ratio.apply(dev->session.optical_pixels_raw); // turn pixel value into bytes 2x16 bits words offset *= 2 * 2; pixels *= 2 * 2; dev->interface->record_key_value("shading_offset", std::to_string(offset)); dev->interface->record_key_value("shading_pixels", std::to_string(pixels)); dev->interface->record_key_value("shading_length", std::to_string(length)); dev->interface->record_key_value("shading_factor", std::to_string(sensor.shading_factor)); std::vector buffer(pixels, 0); DBG(DBG_io2, "%s: using chunks of %d (0x%04x) bytes\n", __func__, pixels, pixels); /* base addr of data has been written in reg D0-D4 in 4K word, so AHB address * is 8192*reg value */ /* write actual color channel data */ for(i=0;i<3;i++) { /* build up actual shading data by copying the part from the full width one * to the one corresponding to SHDAREA */ std::uint8_t* ptr = buffer.data(); /* iterate on both sensor segment */ for (unsigned x = 0; x < pixels; x += 4 * sensor.shading_factor) { // coefficient source std::uint8_t* src = (data + offset + i * length) + x; /* coefficient copy */ ptr[0]=src[0]; ptr[1]=src[1]; ptr[2]=src[2]; ptr[3]=src[3]; /* next shading coefficient */ ptr+=4; } std::uint8_t val = dev->interface->read_register(0xd0+i); addr = val * 8192 + 0x10000000; dev->interface->write_ahb(addr, pixels, buffer.data()); } } /** @brief calibrates led exposure * Calibrate exposure by scanning a white area until the used exposure gives * data white enough. * @param dev device to calibrate */ SensorExposure CommandSetGl846::led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { return scanner_led_calibration(*dev, sensor, regs); } /** * set up GPIO/GPOE for idle state */ static void gl846_init_gpio(Genesys_Device* dev) { DBG_HELPER(dbg); apply_registers_ordered(dev->gpo.regs, { 0x6e, 0x6f }, [&](const GenesysRegisterSetting& reg) { dev->interface->write_register(reg.address, reg.value); }); } /** * set memory layout by filling values in dedicated registers */ static void gl846_init_memory_layout(Genesys_Device* dev) { DBG_HELPER(dbg); // prevent further writings by bulk write register dev->reg.remove_reg(0x0b); apply_reg_settings_to_device_write_only(*dev, dev->memory_layout.regs); } /* * * initialize ASIC from power on condition */ void CommandSetGl846::asic_boot(Genesys_Device* dev, bool cold) const { DBG_HELPER(dbg); std::uint8_t val; // reset ASIC if cold boot if (cold) { dev->interface->write_register(0x0e, 0x01); dev->interface->write_register(0x0e, 0x00); } if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { if (dev->usb_mode == 1) { val = 0x14; } else { val = 0x11; } dev->interface->write_0x8c(0x0f, val); } // test CHKVER val = dev->interface->read_register(REG_0x40); if (val & REG_0x40_CHKVER) { val = dev->interface->read_register(0x00); DBG(DBG_info, "%s: reported version for genesys chip is 0x%02x\n", __func__, val); } gl846_init_registers (dev); // Write initial registers dev->interface->write_registers(dev->reg); /* CIS_LINE */ if (dev->model->is_cis) { dev->reg.init_reg(0x08, REG_0x08_CIS_LINE); dev->interface->write_register(0x08, dev->reg.find_reg(0x08).value); } // set up clocks if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) { dev->interface->write_0x8c(0x10, 0x0c); dev->interface->write_0x8c(0x13, 0x0c); } else { dev->interface->write_0x8c(0x10, 0x0e); dev->interface->write_0x8c(0x13, 0x0e); } // setup gpio gl846_init_gpio(dev); // setup internal memory layout gl846_init_memory_layout(dev); dev->reg.init_reg(0xf8, 0x05); dev->interface->write_register(0xf8, dev->reg.find_reg(0xf8).value); } /** * initialize backend and ASIC : registers, motor tables, and gamma tables * then ensure scanner's head is at home */ void CommandSetGl846::init(Genesys_Device* dev) const { DBG_INIT (); DBG_HELPER(dbg); sanei_genesys_asic_init(dev); } void CommandSetGl846::update_hardware_sensors(Genesys_Scanner* s) const { DBG_HELPER(dbg); /* do what is needed to get a new set of events, but try to not lose any of them. */ std::uint8_t scan, file, email, copy; switch(s->dev->model->gpio_id) { default: scan=0x01; file=0x02; email=0x04; copy=0x08; } std::uint8_t val = s->dev->interface->read_register(REG_0x6D); s->buttons[BUTTON_SCAN_SW].write((val & scan) == 0); s->buttons[BUTTON_FILE_SW].write((val & file) == 0); s->buttons[BUTTON_EMAIL_SW].write((val & email) == 0); s->buttons[BUTTON_COPY_SW].write((val & copy) == 0); } void CommandSetGl846::update_home_sensor_gpio(Genesys_Device& dev) const { DBG_HELPER(dbg); std::uint8_t val = dev.interface->read_register(REG_0x6C); val |= 0x41; dev.interface->write_register(REG_0x6C, val); } void CommandSetGl846::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { scanner_offset_calibration(*dev, sensor, regs); } void CommandSetGl846::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs, int dpi) const { scanner_coarse_gain_calibration(*dev, sensor, regs, dpi); } bool CommandSetGl846::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const { (void) dev; return false; } void CommandSetGl846::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* regs) const { (void) dev; (void) sensor; (void) regs; throw SaneException("not implemented"); } void CommandSetGl846::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const { sanei_genesys_send_gamma_table(dev, sensor); } void CommandSetGl846::wait_for_motor_stop(Genesys_Device* dev) const { (void) dev; } void CommandSetGl846::load_document(Genesys_Device* dev) const { (void) dev; throw SaneException("not implemented"); } void CommandSetGl846::detect_document_end(Genesys_Device* dev) const { (void) dev; throw SaneException("not implemented"); } void CommandSetGl846::eject_document(Genesys_Device* dev) const { (void) dev; throw SaneException("not implemented"); } } // namespace gl846 } // namespace genesys backends-1.3.0/backend/genesys/gl846.h000066400000000000000000000077351456256263500174000ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2012-2013 Stéphane Voltz 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, see . */ #include "genesys.h" #include "command_set_common.h" #ifndef BACKEND_GENESYS_GL846_H #define BACKEND_GENESYS_GL846_H namespace genesys { namespace gl846 { class CommandSetGl846 : public CommandSetCommon { public: ~CommandSetGl846() override = default; bool needs_home_before_init_regs_for_scan(Genesys_Device* dev) const override; void init(Genesys_Device* dev) const override; void init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* regs) const override; void init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const override; void init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, const ScanSession& session) const override; void set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set) const override; void set_powersaving(Genesys_Device* dev, int delay) const override; void save_power(Genesys_Device* dev, bool enable) const override; void begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* regs, bool start_motor) const override; void end_scan(Genesys_Device* dev, Genesys_Register_Set* regs, bool check_stop) const override; void send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const override; void offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const override; void coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs, int dpi) const override; SensorExposure led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const override; void wait_for_motor_stop(Genesys_Device* dev) const override; void move_back_home(Genesys_Device* dev, bool wait_until_home) const override; void update_hardware_sensors(struct Genesys_Scanner* s) const override; void update_home_sensor_gpio(Genesys_Device& dev) const override; void load_document(Genesys_Device* dev) const override; void detect_document_end(Genesys_Device* dev) const override; void eject_document(Genesys_Device* dev) const override; void send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t* data, int size) const override; ScanSession calculate_scan_session(const Genesys_Device* dev, const Genesys_Sensor& sensor, const Genesys_Settings& settings) const override; void asic_boot(Genesys_Device* dev, bool cold) const override; }; enum SlopeTable { SCAN_TABLE = 0, // table 1 at 0x4000 BACKTRACK_TABLE = 1, // table 2 at 0x4800 STOP_TABLE = 2, // table 3 at 0x5000 FAST_TABLE = 3, // table 4 at 0x5800 HOME_TABLE = 4, // table 5 at 0x6000 }; } // namespace gl846 } // namespace genesys #endif // BACKEND_GENESYS_GL846_H backends-1.3.0/backend/genesys/gl846_registers.h000066400000000000000000000321641456256263500214610ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_GL846_REGISTERS_H #define BACKEND_GENESYS_GL846_REGISTERS_H #include namespace genesys { namespace gl846 { using RegAddr = std::uint16_t; using RegMask = std::uint8_t; using RegShift = unsigned; static constexpr RegAddr REG_0x01 = 0x01; static constexpr RegMask REG_0x01_CISSET = 0x80; static constexpr RegMask REG_0x01_DOGENB = 0x40; static constexpr RegMask REG_0x01_DVDSET = 0x20; static constexpr RegMask REG_0x01_STAGGER = 0x10; static constexpr RegMask REG_0x01_COMPENB = 0x08; static constexpr RegMask REG_0x01_TRUEGRAY = 0x04; static constexpr RegMask REG_0x01_SHDAREA = 0x02; static constexpr RegMask REG_0x01_SCAN = 0x01; static constexpr RegAddr REG_0x02 = 0x02; static constexpr RegMask REG_0x02_NOTHOME = 0x80; static constexpr RegMask REG_0x02_ACDCDIS = 0x40; static constexpr RegMask REG_0x02_AGOHOME = 0x20; static constexpr RegMask REG_0x02_MTRPWR = 0x10; static constexpr RegMask REG_0x02_FASTFED = 0x08; static constexpr RegMask REG_0x02_MTRREV = 0x04; static constexpr RegMask REG_0x02_HOMENEG = 0x02; static constexpr RegMask REG_0x02_LONGCURV = 0x01; static constexpr RegAddr REG_0x03 = 0x03; static constexpr RegMask REG_0x03_LAMPDOG = 0x80; static constexpr RegMask REG_0x03_AVEENB = 0x40; static constexpr RegMask REG_0x03_XPASEL = 0x20; static constexpr RegMask REG_0x03_LAMPPWR = 0x10; static constexpr RegMask REG_0x03_LAMPTIM = 0x0f; static constexpr RegAddr REG_0x04 = 0x04; static constexpr RegMask REG_0x04_LINEART = 0x80; static constexpr RegMask REG_0x04_BITSET = 0x40; static constexpr RegMask REG_0x04_AFEMOD = 0x30; static constexpr RegMask REG_0x04_FILTER = 0x0c; static constexpr RegMask REG_0x04_FESET = 0x03; static constexpr RegShift REG_0x04S_AFEMOD = 4; static constexpr RegAddr REG_0x05 = 0x05; static constexpr RegMask REG_0x05_DPIHW = 0xc0; static constexpr RegMask REG_0x05_DPIHW_600 = 0x00; static constexpr RegMask REG_0x05_DPIHW_1200 = 0x40; static constexpr RegMask REG_0x05_DPIHW_2400 = 0x80; static constexpr RegMask REG_0x05_DPIHW_4800 = 0xc0; static constexpr RegMask REG_0x05_MTLLAMP = 0x30; static constexpr RegMask REG_0x05_GMMENB = 0x08; static constexpr RegMask REG_0x05_MTLBASE = 0x03; static constexpr RegAddr REG_0x06 = 0x06; static constexpr RegMask REG_0x06_SCANMOD = 0xe0; static constexpr RegShift REG_0x06S_SCANMOD = 5; static constexpr RegMask REG_0x06_PWRBIT = 0x10; static constexpr RegMask REG_0x06_GAIN4 = 0x08; static constexpr RegMask REG_0x06_OPTEST = 0x07; static constexpr RegMask REG_0x07_LAMPSIM = 0x80; static constexpr RegMask REG_0x08_DRAM2X = 0x80; static constexpr RegMask REG_0x08_MPENB = 0x20; static constexpr RegMask REG_0x08_CIS_LINE = 0x10; static constexpr RegMask REG_0x08_IR1ENB = 0x08; static constexpr RegMask REG_0x08_IR2ENB = 0x04; static constexpr RegMask REG_0x08_ENB24M = 0x01; static constexpr RegMask REG_0x09_MCNTSET = 0xc0; static constexpr RegMask REG_0x09_EVEN1ST = 0x20; static constexpr RegMask REG_0x09_BLINE1ST = 0x10; static constexpr RegMask REG_0x09_BACKSCAN = 0x08; static constexpr RegMask REG_0x09_ENHANCE = 0x04; static constexpr RegMask REG_0x09_SHORTTG = 0x02; static constexpr RegMask REG_0x09_NWAIT = 0x01; static constexpr RegShift REG_0x09S_MCNTSET = 6; static constexpr RegShift REG_0x09S_CLKSET = 4; static constexpr RegAddr REG_0x0A_LPWMEN = 0x10; static constexpr RegAddr REG_0x0B = 0x0b; static constexpr RegMask REG_0x0B_DRAMSEL = 0x07; static constexpr RegMask REG_0x0B_ENBDRAM = 0x08; static constexpr RegMask REG_0x0B_RFHDIS = 0x10; static constexpr RegMask REG_0x0B_CLKSET = 0xe0; static constexpr RegMask REG_0x0B_24MHZ = 0x00; static constexpr RegMask REG_0x0B_30MHZ = 0x20; static constexpr RegMask REG_0x0B_40MHZ = 0x40; static constexpr RegMask REG_0x0B_48MHZ = 0x60; static constexpr RegMask REG_0x0B_60MHZ = 0x80; static constexpr RegAddr REG_0x0C = 0x0c; static constexpr RegMask REG_0x0C_CCDLMT = 0x0f; static constexpr RegAddr REG_0x0D = 0x0d; static constexpr RegMask REG_0x0D_SCSYNC = 0x40; static constexpr RegMask REG_0x0D_CLRERR = 0x20; static constexpr RegMask REG_0x0D_FULLSTP = 0x10; static constexpr RegMask REG_0x0D_SEND = 0x80; static constexpr RegMask REG_0x0D_CLRMCNT = 0x04; static constexpr RegMask REG_0x0D_CLRDOCJM = 0x02; static constexpr RegMask REG_0x0D_CLRLNCNT = 0x01; static constexpr RegAddr REG_0x0F = 0x0f; static constexpr RegMask REG_0x16_CTRLHI = 0x80; static constexpr RegMask REG_0x16_TOSHIBA = 0x40; static constexpr RegMask REG_0x16_TGINV = 0x20; static constexpr RegMask REG_0x16_CK1INV = 0x10; static constexpr RegMask REG_0x16_CK2INV = 0x08; static constexpr RegMask REG_0x16_CTRLINV = 0x04; static constexpr RegMask REG_0x16_CKDIS = 0x02; static constexpr RegMask REG_0x16_CTRLDIS = 0x01; static constexpr RegMask REG_0x17_TGMODE = 0xc0; static constexpr RegMask REG_0x17_TGMODE_NO_DUMMY = 0x00; static constexpr RegMask REG_0x17_TGMODE_REF = 0x40; static constexpr RegMask REG_0x17_TGMODE_XPA = 0x80; static constexpr RegMask REG_0x17_TGW = 0x3f; static constexpr RegAddr REG_0x17S_TGW = 0; static constexpr RegAddr REG_0x18 = 0x18; static constexpr RegMask REG_0x18_CNSET = 0x80; static constexpr RegMask REG_0x18_DCKSEL = 0x60; static constexpr RegMask REG_0x18_CKTOGGLE = 0x10; static constexpr RegMask REG_0x18_CKDELAY = 0x0c; static constexpr RegMask REG_0x18_CKSEL = 0x03; static constexpr RegMask REG_0x1A_SW2SET = 0x80; static constexpr RegMask REG_0x1A_SW1SET = 0x40; static constexpr RegMask REG_0x1A_MANUAL3 = 0x02; static constexpr RegMask REG_0x1A_MANUAL1 = 0x01; static constexpr RegMask REG_0x1A_CK4INV = 0x08; static constexpr RegMask REG_0x1A_CK3INV = 0x04; static constexpr RegMask REG_0x1A_LINECLP = 0x02; static constexpr RegAddr REG_0x1C = 0x1c; static constexpr RegMask REG_0x1C_TGTIME = 0x07; static constexpr RegMask REG_0x1D_CK4LOW = 0x80; static constexpr RegMask REG_0x1D_CK3LOW = 0x40; static constexpr RegMask REG_0x1D_CK1LOW = 0x20; static constexpr RegMask REG_0x1D_TGSHLD = 0x1f; static constexpr RegShift REG_0x1DS_TGSHLD = 0; static constexpr RegAddr REG_0x1E = 0x1e; static constexpr RegMask REG_0x1E_WDTIME = 0xf0; static constexpr RegShift REG_0x1ES_WDTIME = 4; static constexpr RegMask REG_0x1E_LINESEL = 0x0f; static constexpr RegShift REG_0x1ES_LINESEL = 0; static constexpr RegAddr REG_FEDCNT = 0x1f; static constexpr RegAddr REG_0x24 = 0x1c; static constexpr RegAddr REG_0x40 = 0x40; static constexpr RegMask REG_0x40_DOCSNR = 0x80; static constexpr RegMask REG_0x40_ADFSNR = 0x40; static constexpr RegMask REG_0x40_COVERSNR = 0x20; static constexpr RegMask REG_0x40_CHKVER = 0x10; static constexpr RegMask REG_0x40_DOCJAM = 0x08; static constexpr RegMask REG_0x40_HISPDFLG = 0x04; static constexpr RegMask REG_0x40_MOTMFLG = 0x02; static constexpr RegMask REG_0x40_DATAENB = 0x01; static constexpr RegMask REG_0x41_PWRBIT = 0x80; static constexpr RegMask REG_0x41_BUFEMPTY = 0x40; static constexpr RegMask REG_0x41_FEEDFSH = 0x20; static constexpr RegMask REG_0x41_SCANFSH = 0x10; static constexpr RegMask REG_0x41_HOMESNR = 0x08; static constexpr RegMask REG_0x41_LAMPSTS = 0x04; static constexpr RegMask REG_0x41_FEBUSY = 0x02; static constexpr RegMask REG_0x41_MOTORENB = 0x01; static constexpr RegMask REG_0x58_VSMP = 0xf8; static constexpr RegShift REG_0x58S_VSMP = 3; static constexpr RegMask REG_0x58_VSMPW = 0x07; static constexpr RegAddr REG_0x58S_VSMPW = 0; static constexpr RegMask REG_0x59_BSMP = 0xf8; static constexpr RegAddr REG_0x59S_BSMP = 3; static constexpr RegMask REG_0x59_BSMPW = 0x07; static constexpr RegShift REG_0x59S_BSMPW = 0; static constexpr RegMask REG_0x5A_ADCLKINV = 0x80; static constexpr RegMask REG_0x5A_RLCSEL = 0x40; static constexpr RegMask REG_0x5A_CDSREF = 0x30; static constexpr RegShift REG_0x5AS_CDSREF = 4; static constexpr RegMask REG_0x5A_RLC = 0x0f; static constexpr RegShift REG_0x5AS_RLC = 0; static constexpr RegMask REG_0x5E_DECSEL = 0xe0; static constexpr RegShift REG_0x5ES_DECSEL = 5; static constexpr RegMask REG_0x5E_STOPTIM = 0x1f; static constexpr RegShift REG_0x5ES_STOPTIM = 0; static constexpr RegAddr REG_0x60 = 0x60; static constexpr RegMask REG_0x60_Z1MOD = 0x1f; static constexpr RegAddr REG_0x61 = 0x61; static constexpr RegMask REG_0x61_Z1MOD = 0xff; static constexpr RegAddr REG_0x62 = 0x62; static constexpr RegMask REG_0x62_Z1MOD = 0xff; static constexpr RegAddr REG_0x63 = 0x63; static constexpr RegMask REG_0x63_Z2MOD = 0x1f; static constexpr RegAddr REG_0x64 = 0x64; static constexpr RegMask REG_0x64_Z2MOD = 0xff; static constexpr RegAddr REG_0x65 = 0x65; static constexpr RegMask REG_0x65_Z2MOD = 0xff; static constexpr RegShift REG_0x60S_STEPSEL = 5; static constexpr RegMask REG_0x60_STEPSEL = 0xe0; static constexpr RegMask REG_0x60_FULLSTEP = 0x00; static constexpr RegMask REG_0x60_HALFSTEP = 0x20; static constexpr RegMask REG_0x60_EIGHTHSTEP = 0x60; static constexpr RegMask REG_0x60_16THSTEP = 0x80; static constexpr RegShift REG_0x63S_FSTPSEL = 5; static constexpr RegMask REG_0x63_FSTPSEL = 0xe0; static constexpr RegMask REG_0x63_FULLSTEP = 0x00; static constexpr RegMask REG_0x63_HALFSTEP = 0x20; static constexpr RegMask REG_0x63_EIGHTHSTEP = 0x60; static constexpr RegMask REG_0x63_16THSTEP = 0x80; static constexpr RegAddr REG_0x67 = 0x67; static constexpr RegMask REG_0x67_MTRPWM = 0x80; static constexpr RegAddr REG_0x68 = 0x68; static constexpr RegMask REG_0x68_FASTPWM = 0x80; static constexpr RegAddr REG_0x6B = 0x6b; static constexpr RegMask REG_0x6B_MULTFILM = 0x80; static constexpr RegMask REG_0x6B_GPOM13 = 0x40; static constexpr RegMask REG_0x6B_GPOM12 = 0x20; static constexpr RegMask REG_0x6B_GPOM11 = 0x10; static constexpr RegMask REG_0x6B_GPO18 = 0x02; static constexpr RegMask REG_0x6B_GPO17 = 0x01; static constexpr RegAddr REG_0x6C = 0x6c; static constexpr RegMask REG_0x6C_GPIO16 = 0x80; static constexpr RegMask REG_0x6C_GPIO15 = 0x40; static constexpr RegMask REG_0x6C_GPIO14 = 0x20; static constexpr RegMask REG_0x6C_GPIO13 = 0x10; static constexpr RegMask REG_0x6C_GPIO12 = 0x08; static constexpr RegMask REG_0x6C_GPIO11 = 0x04; static constexpr RegMask REG_0x6C_GPIO10 = 0x02; static constexpr RegMask REG_0x6C_GPIO9 = 0x01; static constexpr RegMask REG_0x6C_GPIOH = 0xff; static constexpr RegMask REG_0x6C_GPIOL = 0xff; static constexpr RegAddr REG_0x6D = 0x6d; static constexpr RegAddr REG_0x6E = 0x6e; static constexpr RegAddr REG_0x6F = 0x6f; static constexpr RegAddr REG_0x7E = 0x7e; static constexpr RegAddr REG_0x80 = 0x80; static constexpr RegMask REG_0x80_TABLE1_NORMAL = 0x03; static constexpr RegShift REG_0x80S_TABLE1_NORMAL = 0; static constexpr RegMask REG_0x80_TABLE2_BACK = 0x0c; static constexpr RegShift REG_0x80S_TABLE2_BACK = 2; static constexpr RegMask REG_0x80_TABLE4_FAST = 0x30; static constexpr RegShift REG_0x80S_TABLE4_FAST = 4; static constexpr RegMask REG_0x80_TABLE5_GO_HOME = 0xc0; static constexpr RegShift REG_0x80S_TABLE5_GO_HOME = 6; static constexpr RegMask REG_0x87_ACYCNRLC = 0x10; static constexpr RegMask REG_0x87_ENOFFSET = 0x08; static constexpr RegMask REG_0x87_LEDADD = 0x04; static constexpr RegMask REG_0x87_CK4ADC = 0x02; static constexpr RegMask REG_0x87_AUTOCONF = 0x01; static constexpr RegAddr REG_0x9E = 0x9e; static constexpr RegAddr REG_0x9F = 0x9f; static constexpr RegAddr REG_0xA6 = 0xa6; static constexpr RegAddr REG_0xA7 = 0xa7; static constexpr RegAddr REG_0xA8 = 0xa8; static constexpr RegAddr REG_0xA9 = 0xa9; static constexpr RegAddr REG_0xAB = 0xab; static constexpr RegAddr REG_EXPR = 0x10; static constexpr RegAddr REG_EXPG = 0x12; static constexpr RegAddr REG_EXPB = 0x14; static constexpr RegAddr REG_EXPDMY = 0x19; static constexpr RegAddr REG_STEPNO = 0x21; static constexpr RegAddr REG_FWDSTEP = 0x22; static constexpr RegAddr REG_BWDSTEP = 0x23; static constexpr RegAddr REG_FASTNO = 0x24; static constexpr RegAddr REG_DPISET = 0x2c; static constexpr RegAddr REG_STRPIXEL = 0x30; static constexpr RegAddr REG_ENDPIXEL = 0x32; static constexpr RegAddr REG_LINCNT = 0x25; static constexpr RegAddr REG_MAXWD = 0x35; static constexpr RegAddr REG_LPERIOD = 0x38; static constexpr RegAddr REG_FEEDL = 0x3d; static constexpr RegAddr REG_FMOVDEC = 0x5f; static constexpr RegAddr REG_FSHDEC = 0x69; static constexpr RegAddr REG_FMOVNO = 0x6a; static constexpr RegAddr REG_CK1MAP = 0x74; static constexpr RegAddr REG_CK3MAP = 0x77; static constexpr RegAddr REG_CK4MAP = 0x7a; static constexpr RegAddr REG_0xF8 = 0xf8; static constexpr RegMask REG_0xF8_MAXSEL = 0xf0; static constexpr RegShift REG_0xF8_SMAXSEL = 4; static constexpr RegMask REG_0xF8_MINSEL = 0x0f; } // namespace gl846 } // namespace genesys #endif // BACKEND_GENESYS_GL846_REGISTERS_H backends-1.3.0/backend/genesys/gl847.cpp000066400000000000000000001251121456256263500177220ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2010-2013 Stéphane Voltz 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, see . */ #define DEBUG_DECLARE_ONLY #include "gl847.h" #include "gl847_registers.h" #include "test_settings.h" #include namespace genesys { namespace gl847 { /** * compute the step multiplier used */ static unsigned gl847_get_step_multiplier (Genesys_Register_Set * regs) { unsigned value = (regs->get8(0x9d) & 0x0f) >> 1; return 1 << value; } /** @brief set all registers to default values . * This function is called only once at the beginning and * fills register startup values for registers reused across scans. * Those that are rarely modified or not modified are written * individually. * @param dev device structure holding register set to initialize */ static void gl847_init_registers (Genesys_Device * dev) { DBG_HELPER(dbg); int lide700=0; std::uint8_t val; /* 700F class needs some different initial settings */ if (dev->model->model_id == ModelId::CANON_LIDE_700F) { lide700 = 1; } dev->reg.clear(); dev->reg.init_reg(0x01, 0x82); if (dev->model->model_id == ModelId::CANON_5600F) { dev->reg.init_reg(0x01, 0x40); } dev->reg.init_reg(0x02, 0x18); dev->reg.init_reg(0x03, 0x50); dev->reg.init_reg(0x04, 0x12); if (dev->model->model_id == ModelId::CANON_5600F) { dev->reg.init_reg(0x04, 0x20); } dev->reg.init_reg(0x05, 0x80); dev->reg.init_reg(0x06, 0x50); // FASTMODE + POWERBIT if (dev->model->model_id == ModelId::CANON_5600F) { dev->reg.init_reg(0x06, 0xf8); } dev->reg.init_reg(0x08, 0x10); if (dev->model->model_id == ModelId::CANON_5600F) { dev->reg.init_reg(0x08, 0x20); } dev->reg.init_reg(0x09, 0x01); if (dev->model->model_id == ModelId::CANON_5600F) { dev->reg.init_reg(0x09, 0x00); } dev->reg.init_reg(0x0a, 0x00); dev->reg.init_reg(0x0b, 0x01); if (dev->model->model_id == ModelId::CANON_5600F) { dev->reg.init_reg(0x0b, 0x6b); } dev->reg.init_reg(0x0c, 0x02); if (dev->model->model_id == ModelId::CANON_5600F) { dev->reg.init_reg(0x0c, 0x00); } // LED exposures dev->reg.init_reg(0x10, 0x00); // exposure, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x11, 0x00); // exposure, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x12, 0x00); // exposure, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x13, 0x00); // exposure, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x14, 0x00); // exposure, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x15, 0x00); // exposure, overwritten in scanner_setup_sensor() below dev->reg.init_reg(0x16, 0x10); // SENSOR_DEF dev->reg.init_reg(0x17, 0x08); // SENSOR_DEF dev->reg.init_reg(0x18, 0x00); // SENSOR_DEF dev->reg.init_reg(0x19, 0x50); // SENSOR_DEF dev->reg.init_reg(0x1a, 0x34); // SENSOR_DEF dev->reg.init_reg(0x1b, 0x00); // SENSOR_DEF dev->reg.init_reg(0x1c, 0x02); // SENSOR_DEF dev->reg.init_reg(0x1d, 0x04); // SENSOR_DEF dev->reg.init_reg(0x1e, 0x10); if (dev->model->model_id == ModelId::CANON_5600F) { dev->reg.init_reg(0x1e, 0xf0); } dev->reg.init_reg(0x1f, 0x04); dev->reg.init_reg(0x20, 0x02); // BUFSEL: buffer full condition dev->reg.init_reg(0x21, 0x10); // STEPNO: set during motor setup dev->reg.init_reg(0x22, 0x7f); // FWDSTEP: set during motor setup dev->reg.init_reg(0x23, 0x7f); // BWDSTEP: set during motor setup dev->reg.init_reg(0x24, 0x10); // FASTNO: set during motor setup dev->reg.init_reg(0x25, 0x00); // LINCNT: set during motor setup dev->reg.init_reg(0x26, 0x00); // LINCNT: set during motor setup dev->reg.init_reg(0x27, 0x00); // LINCNT: set during motor setup dev->reg.init_reg(0x2c, 0x09); // DPISET: set during sensor setup dev->reg.init_reg(0x2d, 0x60); // DPISET: set during sensor setup dev->reg.init_reg(0x2e, 0x80); // BWHI: black/white low threshdold dev->reg.init_reg(0x2f, 0x80); // BWLOW: black/white low threshold dev->reg.init_reg(0x30, 0x00); // STRPIXEL: set during sensor setup dev->reg.init_reg(0x31, 0x10); // STRPIXEL: set during sensor setup dev->reg.init_reg(0x32, 0x15); // ENDPIXEL: set during sensor setup dev->reg.init_reg(0x33, 0x0e); // ENDPIXEL: set during sensor setup dev->reg.init_reg(0x34, 0x40); // DUMMY: SENSOR_DEF dev->reg.init_reg(0x35, 0x00); // MAXWD: set during scan setup dev->reg.init_reg(0x36, 0x2a); // MAXWD: set during scan setup dev->reg.init_reg(0x37, 0x30); // MAXWD: set during scan setup dev->reg.init_reg(0x38, 0x2a); // LPERIOD: SENSOR_DEF dev->reg.init_reg(0x39, 0xf8); // LPERIOD: SENSOR_DEF dev->reg.init_reg(0x3d, 0x00); // FEEDL: set during motor setup dev->reg.init_reg(0x3e, 0x00); // FEEDL: set during motor setup dev->reg.init_reg(0x3f, 0x00); // FEEDL: set during motor setup dev->reg.init_reg(0x52, 0x03); // SENSOR_DEF dev->reg.init_reg(0x53, 0x07); // SENSOR_DEF dev->reg.init_reg(0x54, 0x00); // SENSOR_DEF dev->reg.init_reg(0x55, 0x00); // SENSOR_DEF dev->reg.init_reg(0x56, 0x00); // SENSOR_DEF dev->reg.init_reg(0x57, 0x00); // SENSOR_DEF dev->reg.init_reg(0x58, 0x2a); // SENSOR_DEF dev->reg.init_reg(0x59, 0xe1); // SENSOR_DEF dev->reg.init_reg(0x5a, 0x55); // SENSOR_DEF dev->reg.init_reg(0x5e, 0x41); // DECSEL, STOPTIM dev->reg.init_reg(0x5f, 0x40); // FMOVDEC: set during motor setup dev->reg.init_reg(0x60, 0x00); // Z1MOD: overwritten during motor setup dev->reg.init_reg(0x61, 0x21); // Z1MOD: overwritten during motor setup dev->reg.init_reg(0x62, 0x40); // Z1MOD: overwritten during motor setup dev->reg.init_reg(0x63, 0x00); // Z2MOD: overwritten during motor setup dev->reg.init_reg(0x64, 0x21); // Z2MOD: overwritten during motor setup dev->reg.init_reg(0x65, 0x40); // Z2MOD: overwritten during motor setup dev->reg.init_reg(0x67, 0x80); // STEPSEL, MTRPWM: overwritten during motor setup dev->reg.init_reg(0x68, 0x80); // FSTPSEL, FASTPWM: overwritten during motor setup dev->reg.init_reg(0x69, 0x20); // FSHDEC: overwritten during motor setup dev->reg.init_reg(0x6a, 0x20); // FMOVNO: overwritten during motor setup dev->reg.init_reg(0x74, 0x00); // SENSOR_DEF dev->reg.init_reg(0x75, 0x00); // SENSOR_DEF dev->reg.init_reg(0x76, 0x3c); // SENSOR_DEF dev->reg.init_reg(0x77, 0x00); // SENSOR_DEF dev->reg.init_reg(0x78, 0x00); // SENSOR_DEF dev->reg.init_reg(0x79, 0x9f); // SENSOR_DEF dev->reg.init_reg(0x7a, 0x00); // SENSOR_DEF dev->reg.init_reg(0x7b, 0x00); // SENSOR_DEF dev->reg.init_reg(0x7c, 0x55); // SENSOR_DEF dev->reg.init_reg(0x7d, 0x00); // NOTE: autoconf is a non working option dev->reg.init_reg(0x87, 0x02); // TODO: move to SENSOR_DEF dev->reg.init_reg(0x9d, 0x06); // RAMDLY, MOTLAG, CMODE, STEPTIM, IFRS dev->reg.init_reg(0xa2, 0x0f); // misc if (dev->model->model_id == ModelId::CANON_5600F) { dev->reg.init_reg(0xab, 0x31); dev->reg.init_reg(0xbb, 0x00); dev->reg.init_reg(0xbc, 0x0f); } dev->reg.init_reg(0xbd, 0x18); // misc dev->reg.init_reg(0xfe, 0x08); // misc if (dev->model->model_id == ModelId::CANON_5600F) { dev->reg.init_reg(0x9e, 0x00); // sensor reg, but not in SENSOR_DEF dev->reg.init_reg(0x9f, 0x00); // sensor reg, but not in SENSOR_DEF dev->reg.init_reg(0xaa, 0x00); // custom data dev->reg.init_reg(0xff, 0x00); } // gamma[0] and gamma[256] values dev->reg.init_reg(0xbe, 0x00); dev->reg.init_reg(0xc5, 0x00); dev->reg.init_reg(0xc6, 0x00); dev->reg.init_reg(0xc7, 0x00); dev->reg.init_reg(0xc8, 0x00); dev->reg.init_reg(0xc9, 0x00); dev->reg.init_reg(0xca, 0x00); /* LiDE 700 fixups */ if (lide700) { dev->reg.init_reg(0x5f, 0x04); dev->reg.init_reg(0x7d, 0x80); /* we write to these registers only once */ val=0; dev->interface->write_register(REG_0x7E, val); dev->interface->write_register(REG_0x9E, val); dev->interface->write_register(REG_0x9F, val); dev->interface->write_register(REG_0xAB, val); } const auto& sensor = sanei_genesys_find_sensor_any(dev); const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, sensor.full_resolution, 3, ScanMethod::FLATBED); sanei_genesys_set_dpihw(dev->reg, dpihw_sensor.register_dpihw); if (dev->model->model_id == ModelId::CANON_5600F) { scanner_setup_sensor(*dev, sensor, dev->reg); } } // Set values of analog frontend void CommandSetGl847::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set) const { DBG_HELPER_ARGS(dbg, "%s", set == AFE_INIT ? "init" : set == AFE_SET ? "set" : set == AFE_POWER_SAVE ? "powersave" : "huh?"); (void) sensor; if (dev->model->model_id != ModelId::CANON_5600F) { // FIXME: remove the following read dev->interface->read_register(REG_0x04); } // wait for FE to be ready auto status = scanner_read_status(*dev); while (status.is_front_end_busy) { dev->interface->sleep_ms(10); status = scanner_read_status(*dev); } if (set == AFE_INIT) { dev->frontend = dev->frontend_initial; } if (dev->model->model_id != ModelId::CANON_5600F) { // reset DAC (BUG: this does completely different thing on Analog Devices ADCs) dev->interface->write_fe_register(0x00, 0x80); } else { if (dev->frontend.layout.type == FrontendType::WOLFSON) { // reset DAC dev->interface->write_fe_register(0x04, 0xff); } } for (const auto& reg : dev->frontend.regs) { dev->interface->write_fe_register(reg.address, reg.value); } } static void gl847_write_motor_phase_table(Genesys_Device& dev, unsigned ydpi) { (void) ydpi; if (dev.model->model_id == ModelId::CANON_5600F) { std::vector phase_table = { 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x12, 0x00, 0x12, 0x00, 0x12, 0x00, 0x12, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x25, 0x00, 0x25, 0x00, 0x25, 0x00, 0x25, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, }; dev.interface->write_ahb(0x01000a00, phase_table.size(), phase_table.data()); } } // @brief set up motor related register for scan static void gl847_init_motor_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, const MotorProfile& motor_profile, unsigned int scan_exposure_time, unsigned scan_yres, unsigned int scan_lines, unsigned int scan_dummy, unsigned int feed_steps, ScanFlag flags) { DBG_HELPER_ARGS(dbg, "scan_exposure_time=%d, can_yres=%d, step_type=%d, scan_lines=%d, " "scan_dummy=%d, feed_steps=%d, flags=%x", scan_exposure_time, scan_yres, static_cast(motor_profile.step_type), scan_lines, scan_dummy, feed_steps, static_cast(flags)); unsigned step_multiplier = gl847_get_step_multiplier (reg); reg->set24(REG_LINCNT, scan_lines); reg->set8(REG_0x02, 0); sanei_genesys_set_motor_power(*reg, true); std::uint8_t reg02 = reg->get8(REG_0x02); reg02 &= ~REG_0x02_FASTFED; if (has_flag(flags, ScanFlag::AUTO_GO_HOME)) { reg02 |= REG_0x02_AGOHOME | REG_0x02_NOTHOME; } if (has_flag(flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE) || (scan_yres >= sensor.full_resolution)) { reg02 |= REG_0x02_ACDCDIS; } if (has_flag(flags, ScanFlag::REVERSE)) { reg02 |= REG_0x02_MTRREV; } else { reg02 &= ~REG_0x02_MTRREV; } reg->set8(REG_0x02, reg02); // scan and backtracking slope table auto scan_table = create_slope_table(dev->model->asic_type, dev->motor, scan_yres, scan_exposure_time, step_multiplier, motor_profile); scanner_send_slope_table(dev, sensor, SCAN_TABLE, scan_table.table); scanner_send_slope_table(dev, sensor, BACKTRACK_TABLE, scan_table.table); // fast table unsigned fast_dpi = sanei_genesys_get_lowest_ydpi(dev); // BUG: looks like for fast moves we use inconsistent step type StepType fast_step_type = motor_profile.step_type; if (static_cast(motor_profile.step_type) >= static_cast(StepType::QUARTER)) { fast_step_type = StepType::QUARTER; } MotorProfile fast_motor_profile = motor_profile; fast_motor_profile.step_type = fast_step_type; auto fast_table = create_slope_table(dev->model->asic_type, dev->motor, fast_dpi, scan_exposure_time, step_multiplier, fast_motor_profile); scanner_send_slope_table(dev, sensor, STOP_TABLE, fast_table.table); scanner_send_slope_table(dev, sensor, FAST_TABLE, fast_table.table); scanner_send_slope_table(dev, sensor, HOME_TABLE, fast_table.table); gl847_write_motor_phase_table(*dev, scan_yres); // correct move distance by acceleration and deceleration amounts unsigned feedl = feed_steps; unsigned dist = 0; feedl <<= static_cast(motor_profile.step_type); dist = scan_table.table.size(); if (has_flag(flags, ScanFlag::FEEDING)) { dist *= 2; } // check for overflow if (dist < feedl) { feedl -= dist; } else { feedl = 0; } reg->set24(REG_FEEDL, feedl); unsigned ccdlmt = (reg->get8(REG_0x0C) & REG_0x0C_CCDLMT) + 1; unsigned tgtime = 1 << (reg->get8(REG_0x1C) & REG_0x1C_TGTIME); // hi res motor speed GPIO std::uint8_t effective = dev->interface->read_register(REG_0x6C); // if quarter step, bipolar Vref2 std::uint8_t val = effective; if (motor_profile.step_type == StepType::QUARTER) { val = effective & ~REG_0x6C_GPIO13; } else if (static_cast(motor_profile.step_type) > static_cast(StepType::QUARTER)) { val = effective | REG_0x6C_GPIO13; } dev->interface->write_register(REG_0x6C, val); // effective scan effective = dev->interface->read_register(REG_0x6C); val = effective | REG_0x6C_GPIO10; dev->interface->write_register(REG_0x6C, val); unsigned min_restep = scan_table.table.size() / (2 * step_multiplier) - 1; if (min_restep < 1) { min_restep = 1; } reg->set8(REG_FWDSTEP, min_restep); reg->set8(REG_BWDSTEP, min_restep); std::uint32_t z1, z2; sanei_genesys_calculate_zmod(false, scan_exposure_time * ccdlmt * tgtime, scan_table.table, scan_table.table.size(), feedl, min_restep * step_multiplier, &z1, &z2); reg->set24(REG_0x60, z1 | (static_cast(motor_profile.step_type) << (16+REG_0x60S_STEPSEL))); reg->set24(REG_0x63, z2 | (static_cast(motor_profile.step_type) << (16+REG_0x63S_FSTPSEL))); reg->set8_mask(REG_0x1E, scan_dummy, 0x0f); reg->set8(REG_0x67, REG_0x67_MTRPWM); reg->set8(REG_0x68, REG_0x68_FASTPWM); reg->set8(REG_STEPNO, scan_table.table.size() / step_multiplier); reg->set8(REG_FASTNO, scan_table.table.size() / step_multiplier); reg->set8(REG_FSHDEC, scan_table.table.size() / step_multiplier); reg->set8(REG_FMOVNO, fast_table.table.size() / step_multiplier); reg->set8(REG_FMOVDEC, fast_table.table.size() / step_multiplier); } /** @brief set up registers related to sensor * Set up the following registers 0x01 0x03 0x10-0x015 R/G/B exposures 0x19 EXPDMY 0x2e BWHI 0x2f BWLO 0x04 0x87 0x05 0x2c,0x2d DPISET 0x30,0x31 STRPIXEL 0x32,0x33 ENDPIXEL 0x35,0x36,0x37 MAXWD [25:2] (>>2) 0x38,0x39 LPERIOD 0x34 DUMMY */ static void gl847_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, unsigned int exposure_time, const ScanSession& session) { DBG_HELPER_ARGS(dbg, "exposure_time=%d", exposure_time); scanner_setup_sensor(*dev, sensor, *reg); dev->cmd_set->set_fe(dev, sensor, AFE_SET); /* enable shading */ regs_set_optical_off(dev->model->asic_type, *reg); reg->find_reg(REG_0x01).value |= REG_0x01_SHDAREA; if (has_flag(session.params.flags, ScanFlag::DISABLE_SHADING) || has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION) || session.use_host_side_calib) { reg->find_reg(REG_0x01).value &= ~REG_0x01_DVDSET; } else { reg->find_reg(REG_0x01).value |= REG_0x01_DVDSET; } reg->find_reg(REG_0x03).value &= ~REG_0x03_AVEENB; reg->find_reg(REG_0x03).value &= ~REG_0x03_XPASEL; if (has_flag(session.params.flags, ScanFlag::USE_XPA)) { reg->find_reg(REG_0x03).value |= REG_0x03_XPASEL; } sanei_genesys_set_lamp_power(dev, sensor, *reg, !has_flag(session.params.flags, ScanFlag::DISABLE_LAMP)); reg->state.is_xpa_on = has_flag(session.params.flags, ScanFlag::USE_XPA); if (has_flag(session.params.flags, ScanFlag::USE_XPA)) { if (dev->model->model_id == ModelId::CANON_5600F) { regs_set_exposure(dev->model->asic_type, *reg, sanei_genesys_fixup_exposure({0, 0, 0})); } } // BW threshold reg->set8(0x2e, 0x7f); reg->set8(0x2f, 0x7f); /* monochrome / color scan */ switch (session.params.depth) { case 8: reg->find_reg(REG_0x04).value &= ~(REG_0x04_LINEART | REG_0x04_BITSET); break; case 16: reg->find_reg(REG_0x04).value &= ~REG_0x04_LINEART; reg->find_reg(REG_0x04).value |= REG_0x04_BITSET; break; } reg->find_reg(REG_0x04).value &= ~(REG_0x04_FILTER | REG_0x04_AFEMOD); if (session.params.channels == 1) { switch (session.params.color_filter) { case ColorFilter::RED: reg->find_reg(REG_0x04).value |= 0x14; break; case ColorFilter::BLUE: reg->find_reg(REG_0x04).value |= 0x1c; break; case ColorFilter::GREEN: reg->find_reg(REG_0x04).value |= 0x18; break; default: break; // should not happen } } else { if (dev->model->model_id == ModelId::CANON_5600F) { reg->find_reg(REG_0x04).value |= 0x20; } else { reg->find_reg(REG_0x04).value |= 0x10; // mono } } const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, session.output_resolution, session.params.channels, session.params.scan_method); sanei_genesys_set_dpihw(*reg, dpihw_sensor.register_dpihw); if (should_enable_gamma(session, sensor)) { reg->find_reg(REG_0x05).value |= REG_0x05_GMMENB; } else { reg->find_reg(REG_0x05).value &= ~REG_0x05_GMMENB; } /* CIS scanners can do true gray by setting LEDADD */ /* we set up LEDADD only when asked */ if (dev->model->is_cis) { reg->find_reg(0x87).value &= ~REG_0x87_LEDADD; if (session.enable_ledadd) { reg->find_reg(0x87).value |= REG_0x87_LEDADD; } /* RGB weighting reg->find_reg(0x01).value &= ~REG_0x01_TRUEGRAY; if (session.enable_ledadd) { reg->find_reg(0x01).value |= REG_0x01_TRUEGRAY; } */ } reg->set16(REG_DPISET, sensor.register_dpiset); reg->set16(REG_STRPIXEL, session.pixel_startx); reg->set16(REG_ENDPIXEL, session.pixel_endx); setup_image_pipeline(*dev, session); /* MAXWD is expressed in 4 words unit */ // BUG: we shouldn't multiply by channels here reg->set24(REG_MAXWD, (session.output_line_bytes_raw * session.params.channels >> 2)); reg->set16(REG_LPERIOD, exposure_time); reg->set8(0x34, sensor.dummy_pixel); } void CommandSetGl847::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, const ScanSession& session) const { DBG_HELPER(dbg); session.assert_computed(); int exposure_time; int slope_dpi = 0; int dummy = 0; if (dev->model->model_id == ModelId::CANON_LIDE_100 || dev->model->model_id == ModelId::CANON_LIDE_200 || dev->model->model_id == ModelId::CANON_LIDE_700F || dev->model->model_id == ModelId::HP_SCANJET_N6310) { dummy = 3 - session.params.channels; } /* slope_dpi */ /* cis color scan is effectively a gray scan with 3 gray lines per color line and a FILTER of 0 */ if (dev->model->is_cis) { slope_dpi = session.params.yres * session.params.channels; } else { slope_dpi = session.params.yres; } slope_dpi = slope_dpi * (1 + dummy); exposure_time = sensor.exposure_lperiod; const auto& motor_profile = get_motor_profile(dev->motor.profiles, exposure_time, session); /* we enable true gray for cis scanners only, and just when doing * scan since color calibration is OK for this mode */ gl847_init_optical_regs_scan(dev, sensor, reg, exposure_time, session); gl847_init_motor_regs_scan(dev, sensor, reg, motor_profile, exposure_time, slope_dpi, session.optical_line_count, dummy, session.params.starty, session.params.flags); dev->read_active = true; dev->session = session; dev->total_bytes_read = 0; dev->total_bytes_to_read = (size_t)session.output_line_bytes_requested * (size_t)session.params.lines; DBG(DBG_info, "%s: total bytes to send = %zu\n", __func__, dev->total_bytes_to_read); } ScanSession CommandSetGl847::calculate_scan_session(const Genesys_Device* dev, const Genesys_Sensor& sensor, const Genesys_Settings& settings) const { DBG(DBG_info, "%s ", __func__); debug_dump(DBG_info, settings); // backtracking isn't handled well, so don't enable it ScanFlag flags = ScanFlag::DISABLE_BUFFER_FULL_MOVE; /* Steps to move to reach scanning area: - first we move to physical start of scanning either by a fixed steps amount from the black strip or by a fixed amount from parking position, minus the steps done during shading calibration. - then we move by the needed offset whitin physical scanning area */ unsigned move_dpi = dev->motor.base_ydpi; float move = dev->model->y_offset; if (settings.scan_method == ScanMethod::TRANSPARENCY || settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { // note: scanner_move_to_ta() function has already been called and the sensor is at the // transparency adapter if (!dev->ignore_offsets) { move = dev->model->y_offset_ta - dev->model->y_offset_sensor_to_ta; } flags |= ScanFlag::USE_XPA; } else { if (!dev->ignore_offsets) { move = dev->model->y_offset; } } move = move + settings.tl_y; move = static_cast((move * move_dpi) / MM_PER_INCH); move -= dev->head_pos(ScanHeadId::PRIMARY); float start = dev->model->x_offset; if (settings.scan_method == ScanMethod::TRANSPARENCY || settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { start = dev->model->x_offset_ta; } else { start = dev->model->x_offset; } start = start + dev->settings.tl_x; start = static_cast((start * settings.xres) / MM_PER_INCH); ScanSession session; session.params.xres = settings.xres; session.params.yres = settings.yres; session.params.startx = static_cast(start); session.params.starty = static_cast(move); session.params.pixels = settings.pixels; session.params.requested_pixels = settings.requested_pixels; session.params.lines = settings.lines; session.params.depth = settings.depth; session.params.channels = settings.get_channels(); session.params.scan_method = settings.scan_method; session.params.scan_mode = settings.scan_mode; session.params.color_filter = settings.color_filter; session.params.contrast_adjustment = settings.contrast; session.params.brightness_adjustment = settings.brightness; session.params.flags = flags; compute_session(dev, session, sensor); return session; } // for fast power saving methods only, like disabling certain amplifiers void CommandSetGl847::save_power(Genesys_Device* dev, bool enable) const { DBG_HELPER_ARGS(dbg, "enable = %d", enable); (void) dev; } void CommandSetGl847::set_powersaving(Genesys_Device* dev, int delay /* in minutes */) const { (void) dev; DBG_HELPER_ARGS(dbg, "delay = %d", delay); } // Send the low-level scan command void CommandSetGl847::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, bool start_motor) const { DBG_HELPER(dbg); (void) sensor; std::uint8_t val; if (reg->state.is_xpa_on && reg->state.is_lamp_on) { dev->cmd_set->set_xpa_lamp_power(*dev, true); } if (dev->model->model_id == ModelId::HP_SCANJET_N6310 || dev->model->model_id == ModelId::CANON_LIDE_100 || dev->model->model_id == ModelId::CANON_LIDE_200) { val = dev->interface->read_register(REG_0x6C); val &= ~REG_0x6C_GPIO10; dev->interface->write_register(REG_0x6C, val); } if (dev->model->model_id == ModelId::CANON_5600F) { switch (dev->session.params.xres) { case 75: case 150: case 300: scanner_register_rw_bits(*dev, REG_0xA6, 0x04, 0x1c); break; case 600: scanner_register_rw_bits(*dev, REG_0xA6, 0x18, 0x1c); break; case 1200: scanner_register_rw_bits(*dev, REG_0xA6, 0x08, 0x1c); break; case 2400: scanner_register_rw_bits(*dev, REG_0xA6, 0x10, 0x1c); break; case 4800: scanner_register_rw_bits(*dev, REG_0xA6, 0x00, 0x1c); break; default: throw SaneException("Unexpected xres"); } dev->interface->write_register(0x6c, 0xf0); dev->interface->write_register(0x6b, 0x87); dev->interface->write_register(0x6d, 0x5f); } if (dev->model->model_id == ModelId::CANON_5600F) { scanner_clear_scan_and_feed_counts(*dev); } else { // FIXME: use scanner_clear_scan_and_feed_counts() val = REG_0x0D_CLRLNCNT; dev->interface->write_register(REG_0x0D, val); val = REG_0x0D_CLRMCNT; dev->interface->write_register(REG_0x0D, val); } val = dev->interface->read_register(REG_0x01); val |= REG_0x01_SCAN; dev->interface->write_register(REG_0x01, val); reg->set8(REG_0x01, val); scanner_start_action(*dev, start_motor); dev->advance_head_pos_by_session(ScanHeadId::PRIMARY); } // Send the stop scan command void CommandSetGl847::end_scan(Genesys_Device* dev, Genesys_Register_Set* reg, bool check_stop) const { (void) reg; DBG_HELPER_ARGS(dbg, "check_stop = %d", check_stop); if (reg->state.is_xpa_on) { dev->cmd_set->set_xpa_lamp_power(*dev, false); } if (!dev->model->is_sheetfed) { scanner_stop_action(*dev); } } void CommandSetGl847::move_back_home(Genesys_Device* dev, bool wait_until_home) const { scanner_move_back_home(*dev, wait_until_home); } // init registers for shading calibration void CommandSetGl847::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { DBG_HELPER(dbg); unsigned move_dpi = dev->motor.base_ydpi; float calib_size_mm = 0; if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { calib_size_mm = dev->model->y_size_calib_ta_mm; } else { calib_size_mm = dev->model->y_size_calib_mm; } unsigned channels = 3; unsigned resolution = sensor.shading_resolution; const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, dev->settings.scan_method); float move = 0; ScanFlag flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA | ScanFlag::DISABLE_BUFFER_FULL_MOVE; if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { // note: scanner_move_to_ta() function has already been called and the sensor is at the // transparency adapter move = dev->model->y_offset_calib_white_ta - dev->model->y_offset_sensor_to_ta; flags |= ScanFlag::USE_XPA; } else { move = dev->model->y_offset_calib_white; } move = static_cast((move * move_dpi) / MM_PER_INCH); unsigned calib_lines = static_cast(calib_size_mm * resolution / MM_PER_INCH); ScanSession session; session.params.xres = resolution; session.params.yres = resolution; session.params.startx = 0; session.params.starty = static_cast(move); session.params.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; session.params.lines = calib_lines; session.params.depth = 16; session.params.channels = channels; session.params.scan_method = dev->settings.scan_method; session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; session.params.color_filter = dev->settings.color_filter; session.params.contrast_adjustment = dev->settings.contrast; session.params.brightness_adjustment = dev->settings.brightness; session.params.flags = flags; compute_session(dev, session, calib_sensor); init_regs_for_scan_session(dev, calib_sensor, ®s, session); /* we use ModelFlag::SHADING_REPARK */ dev->set_head_pos_zero(ScanHeadId::PRIMARY); dev->calib_session = session; } /** * Send shading calibration data. The buffer is considered to always hold values * for all the channels. */ void CommandSetGl847::send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t* data, int size) const { DBG_HELPER_ARGS(dbg, "writing %d bytes of shading data", size); std::uint32_t addr, i; unsigned length = static_cast(size / 3); // we're using SHDAREA, thus we only need to upload part of the line unsigned offset = dev->session.pixel_count_ratio.apply( dev->session.params.startx * sensor.full_resolution / dev->session.params.xres); unsigned pixels = dev->session.pixel_count_ratio.apply(dev->session.optical_pixels_raw); // turn pixel value into bytes 2x16 bits words offset *= 2 * 2; pixels *= 2 * 2; dev->interface->record_key_value("shading_offset", std::to_string(offset)); dev->interface->record_key_value("shading_pixels", std::to_string(pixels)); dev->interface->record_key_value("shading_length", std::to_string(length)); dev->interface->record_key_value("shading_factor", std::to_string(sensor.shading_factor)); std::vector buffer(pixels, 0); DBG(DBG_io2, "%s: using chunks of %d (0x%04x) bytes\n", __func__, pixels, pixels); /* base addr of data has been written in reg D0-D4 in 4K word, so AHB address * is 8192*reg value */ if (dev->model->model_id == ModelId::CANON_5600F) { return; } /* write actual color channel data */ for(i=0;i<3;i++) { /* build up actual shading data by copying the part from the full width one * to the one corresponding to SHDAREA */ std::uint8_t* ptr = buffer.data(); // iterate on both sensor segment for (unsigned x = 0; x < pixels; x += 4 * sensor.shading_factor) { // coefficient source std::uint8_t* src = (data + offset + i * length) + x; /* coefficient copy */ ptr[0]=src[0]; ptr[1]=src[1]; ptr[2]=src[2]; ptr[3]=src[3]; /* next shading coefficient */ ptr+=4; } std::uint8_t val = dev->interface->read_register(0xd0+i); addr = val * 8192 + 0x10000000; dev->interface->write_ahb(addr, pixels, buffer.data()); } } /** @brief calibrates led exposure * Calibrate exposure by scanning a white area until the used exposure gives * data white enough. * @param dev device to calibrate */ SensorExposure CommandSetGl847::led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { return scanner_led_calibration(*dev, sensor, regs); } /** * set up GPIO/GPOE for idle state */ static void gl847_init_gpio(Genesys_Device* dev) { DBG_HELPER(dbg); if (dev->model->model_id == ModelId::CANON_5600F) { apply_registers_ordered(dev->gpo.regs, {0xa6, 0xa7, 0x6f, 0x6e}, [&](const GenesysRegisterSetting& reg) { dev->interface->write_register(reg.address, reg.value); }); } else { std::vector order1 = { 0xa7, 0xa6, 0x6e }; std::vector order2 = { 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0xa8, 0xa9 }; for (auto addr : order1) { dev->interface->write_register(addr, dev->gpo.regs.find_reg(addr).value); } dev->interface->write_register(REG_0x6C, 0x00); // FIXME: Likely not needed for (auto addr : order2) { dev->interface->write_register(addr, dev->gpo.regs.find_reg(addr).value); } for (const auto& reg : dev->gpo.regs) { if (std::find(order1.begin(), order1.end(), reg.address) != order1.end()) { continue; } if (std::find(order2.begin(), order2.end(), reg.address) != order2.end()) { continue; } dev->interface->write_register(reg.address, reg.value); } } } /** * set memory layout by filling values in dedicated registers */ static void gl847_init_memory_layout(Genesys_Device* dev) { DBG_HELPER(dbg); // FIXME: move to initial register list switch (dev->model->model_id) { case ModelId::CANON_LIDE_100: case ModelId::CANON_LIDE_200: dev->interface->write_register(REG_0x0B, 0x29); break; case ModelId::CANON_LIDE_700F: dev->interface->write_register(REG_0x0B, 0x2a); break; default: break; } // prevent further writings by bulk write register dev->reg.remove_reg(0x0b); apply_reg_settings_to_device_write_only(*dev, dev->memory_layout.regs); } /* * * initialize ASIC from power on condition */ void CommandSetGl847::asic_boot(Genesys_Device* dev, bool cold) const { DBG_HELPER(dbg); // reset ASIC if cold boot if (cold) { dev->interface->write_register(0x0e, 0x01); dev->interface->write_register(0x0e, 0x00); } // test CHKVER std::uint8_t val = dev->interface->read_register(REG_0x40); if (val & REG_0x40_CHKVER) { val = dev->interface->read_register(0x00); DBG(DBG_info, "%s: reported version for genesys chip is 0x%02x\n", __func__, val); } /* Set default values for registers */ gl847_init_registers (dev); // Write initial registers dev->interface->write_registers(dev->reg); if (dev->model->model_id != ModelId::CANON_5600F) { // Enable DRAM by setting a rising edge on bit 3 of reg 0x0b // The initial register write also powers on SDRAM val = dev->reg.find_reg(0x0b).value & REG_0x0B_DRAMSEL; val = (val | REG_0x0B_ENBDRAM); dev->interface->write_register(REG_0x0B, val); dev->reg.find_reg(0x0b).value = val; // TODO: remove this write dev->interface->write_register(0x08, dev->reg.find_reg(0x08).value); } // set up end access dev->interface->write_0x8c(0x10, 0x0b); dev->interface->write_0x8c(0x13, 0x0e); // setup gpio gl847_init_gpio(dev); // setup internal memory layout gl847_init_memory_layout (dev); if (dev->model->model_id != ModelId::CANON_5600F) { // FIXME: move to memory layout dev->reg.init_reg(0xf8, 0x01); dev->interface->write_register(0xf8, dev->reg.find_reg(0xf8).value); } } /** * initialize backend and ASIC : registers, motor tables, and gamma tables * then ensure scanner's head is at home */ void CommandSetGl847::init(Genesys_Device* dev) const { DBG_INIT (); DBG_HELPER(dbg); sanei_genesys_asic_init(dev); } void CommandSetGl847::update_hardware_sensors(Genesys_Scanner* s) const { DBG_HELPER(dbg); /* do what is needed to get a new set of events, but try to not lose any of them. */ std::uint8_t val; switch(s->dev->model->gpio_id) { case GpioId::CANON_LIDE_700F: val = s->dev->interface->read_register(REG_0x6D); DBG(DBG_io, "%s: read buttons_gpio value=0x%x\n", __func__, (int)val); s->buttons[BUTTON_SCAN_SW].write((val & 0x04) == 0); s->buttons[BUTTON_FILE_SW].write((val & 0x02) == 0); s->buttons[BUTTON_EMAIL_SW].write((val & 0x01) == 0); s->buttons[BUTTON_COPY_SW].write((val & 0x08) == 0); break; case GpioId::CANON_5600F: val = s->dev->interface->read_register(REG_0x6D); DBG(DBG_io, "%s: read buttons_gpio 0x6d value=0x%x\n", __func__, (int)val); s->buttons[BUTTON_SCAN_SW].write((val & 0x02) == 0); s->buttons[BUTTON_EMAIL_SW].write((val & 0x01) == 0); s->buttons[BUTTON_COPY_SW].write((val & 0x08) == 0); s->buttons[BUTTON_PDF4_SW].write((val & 0x04) == 0); val = s->dev->interface->read_register(REG_0xA6); DBG(DBG_io, "%s: read buttons_gpio 0xa6 value=0x%x\n", __func__, (int)val); s->buttons[BUTTON_PDF1_SW].write((val & 0x03) == 0x01); s->buttons[BUTTON_PDF2_SW].write((val & 0x03) == 0x02); val = s->dev->interface->read_register(REG_0x6C); DBG(DBG_io, "%s: read buttons_gpio 0x6c value=0x%x\n", __func__, (int)val); s->buttons[BUTTON_PDF3_SW].write((val & 0x80) == 0x00); break; default: val = s->dev->interface->read_register(REG_0x6D); DBG(DBG_io, "%s: read buttons_gpio value=0x%x\n", __func__, (int)val); s->buttons[BUTTON_SCAN_SW].write((val & 0x01) == 0); s->buttons[BUTTON_FILE_SW].write((val & 0x02) == 0); s->buttons[BUTTON_EMAIL_SW].write((val & 0x04) == 0); s->buttons[BUTTON_COPY_SW].write((val & 0x08) == 0); break; } } void CommandSetGl847::update_home_sensor_gpio(Genesys_Device& dev) const { DBG_HELPER(dbg); if (dev.model->gpio_id == GpioId::CANON_LIDE_700F) { std::uint8_t val = dev.interface->read_register(REG_0x6C); val &= ~REG_0x6C_GPIO10; dev.interface->write_register(REG_0x6C, val); } else { std::uint8_t val = dev.interface->read_register(REG_0x6C); val |= REG_0x6C_GPIO10; dev.interface->write_register(REG_0x6C, val); } } void CommandSetGl847::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const { scanner_offset_calibration(*dev, sensor, regs); } void CommandSetGl847::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs, int dpi) const { scanner_coarse_gain_calibration(*dev, sensor, regs, dpi); } bool CommandSetGl847::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const { (void) dev; return false; } void CommandSetGl847::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* regs) const { (void) dev; (void) sensor; (void) regs; throw SaneException("not implemented"); } void CommandSetGl847::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const { sanei_genesys_send_gamma_table(dev, sensor); } void CommandSetGl847::wait_for_motor_stop(Genesys_Device* dev) const { (void) dev; } void CommandSetGl847::load_document(Genesys_Device* dev) const { (void) dev; throw SaneException("not implemented"); } void CommandSetGl847::detect_document_end(Genesys_Device* dev) const { (void) dev; throw SaneException("not implemented"); } void CommandSetGl847::eject_document(Genesys_Device* dev) const { (void) dev; throw SaneException("not implemented"); } } // namespace gl847 } // namespace genesys backends-1.3.0/backend/genesys/gl847.h000066400000000000000000000077351456256263500174010ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2010-2013 Stéphane Voltz 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, see . */ #ifndef BACKEND_GENESYS_GL847_H #define BACKEND_GENESYS_GL847_H #include "genesys.h" #include "command_set_common.h" namespace genesys { namespace gl847 { class CommandSetGl847 : public CommandSetCommon { public: ~CommandSetGl847() override = default; bool needs_home_before_init_regs_for_scan(Genesys_Device* dev) const override; void init(Genesys_Device* dev) const override; void init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* regs) const override; void init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const override; void init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, const ScanSession& session) const override; void set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set) const override; void set_powersaving(Genesys_Device* dev, int delay) const override; void save_power(Genesys_Device* dev, bool enable) const override; void begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* regs, bool start_motor) const override; void end_scan(Genesys_Device* dev, Genesys_Register_Set* regs, bool check_stop) const override; void send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const override; void offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const override; void coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs, int dpi) const override; SensorExposure led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const override; void wait_for_motor_stop(Genesys_Device* dev) const override; void move_back_home(Genesys_Device* dev, bool wait_until_home) const override; void update_hardware_sensors(struct Genesys_Scanner* s) const override; void update_home_sensor_gpio(Genesys_Device& dev) const override; void load_document(Genesys_Device* dev) const override; void detect_document_end(Genesys_Device* dev) const override; void eject_document(Genesys_Device* dev) const override; void send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t* data, int size) const override; ScanSession calculate_scan_session(const Genesys_Device* dev, const Genesys_Sensor& sensor, const Genesys_Settings& settings) const override; void asic_boot(Genesys_Device* dev, bool cold) const override; }; enum SlopeTable { SCAN_TABLE = 0, // table 1 at 0x4000 BACKTRACK_TABLE = 1, // table 2 at 0x4800 STOP_TABLE = 2, // table 3 at 0x5000 FAST_TABLE = 3, // table 4 at 0x5800 HOME_TABLE = 4, // table 5 at 0x6000 }; } // namespace gl847 } // namespace genesys #endif // BACKEND_GENESYS_GL847_H backends-1.3.0/backend/genesys/gl847_registers.h000066400000000000000000000277411456256263500214670ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_GL847_REGISTERS_H #define BACKEND_GENESYS_GL847_REGISTERS_H #include namespace genesys { namespace gl847 { using RegAddr = std::uint16_t; using RegMask = std::uint8_t; using RegShift = unsigned; static constexpr RegAddr REG_0x01 = 0x01; static constexpr RegMask REG_0x01_CISSET = 0x80; static constexpr RegMask REG_0x01_DOGENB = 0x40; static constexpr RegMask REG_0x01_DVDSET = 0x20; static constexpr RegMask REG_0x01_STAGGER = 0x10; static constexpr RegMask REG_0x01_COMPENB = 0x08; static constexpr RegMask REG_0x01_TRUEGRAY = 0x04; static constexpr RegMask REG_0x01_SHDAREA = 0x02; static constexpr RegMask REG_0x01_SCAN = 0x01; static constexpr RegAddr REG_0x02 = 0x02; static constexpr RegMask REG_0x02_NOTHOME = 0x80; static constexpr RegMask REG_0x02_ACDCDIS = 0x40; static constexpr RegMask REG_0x02_AGOHOME = 0x20; static constexpr RegMask REG_0x02_MTRPWR = 0x10; static constexpr RegMask REG_0x02_FASTFED = 0x08; static constexpr RegMask REG_0x02_MTRREV = 0x04; static constexpr RegMask REG_0x02_HOMENEG = 0x02; static constexpr RegMask REG_0x02_LONGCURV = 0x01; static constexpr RegAddr REG_0x03 = 0x03; static constexpr RegMask REG_0x03_LAMPDOG = 0x80; static constexpr RegMask REG_0x03_AVEENB = 0x40; static constexpr RegMask REG_0x03_XPASEL = 0x20; static constexpr RegMask REG_0x03_LAMPPWR = 0x10; static constexpr RegMask REG_0x03_LAMPTIM = 0x0f; static constexpr RegAddr REG_0x04 = 0x04; static constexpr RegMask REG_0x04_LINEART = 0x80; static constexpr RegMask REG_0x04_BITSET = 0x40; static constexpr RegMask REG_0x04_AFEMOD = 0x30; static constexpr RegMask REG_0x04_FILTER = 0x0c; static constexpr RegMask REG_0x04_FESET = 0x03; static constexpr RegShift REG_0x04S_AFEMOD = 4; static constexpr RegAddr REG_0x05 = 0x05; static constexpr RegMask REG_0x05_DPIHW = 0xc0; static constexpr RegMask REG_0x05_DPIHW_600 = 0x00; static constexpr RegMask REG_0x05_DPIHW_1200 = 0x40; static constexpr RegMask REG_0x05_DPIHW_2400 = 0x80; static constexpr RegMask REG_0x05_DPIHW_4800 = 0xc0; static constexpr RegMask REG_0x05_MTLLAMP = 0x30; static constexpr RegMask REG_0x05_GMMENB = 0x08; static constexpr RegMask REG_0x05_MTLBASE = 0x03; static constexpr RegAddr REG_0x06 = 0x06; static constexpr RegMask REG_0x06_SCANMOD = 0xe0; static constexpr RegMask REG_0x06S_SCANMOD = 5; static constexpr RegMask REG_0x06_PWRBIT = 0x10; static constexpr RegMask REG_0x06_GAIN4 = 0x08; static constexpr RegMask REG_0x06_OPTEST = 0x07; static constexpr RegMask REG_0x07_LAMPSIM = 0x80; static constexpr RegMask REG_0x08_DRAM2X = 0x80; static constexpr RegMask REG_0x08_MPENB = 0x20; static constexpr RegMask REG_0x08_CIS_LINE = 0x10; static constexpr RegMask REG_0x08_IR1ENB = 0x08; static constexpr RegMask REG_0x08_IR2ENB = 0x04; static constexpr RegMask REG_0x08_ENB24M = 0x01; static constexpr RegMask REG_0x09_MCNTSET = 0xc0; static constexpr RegMask REG_0x09_EVEN1ST = 0x20; static constexpr RegMask REG_0x09_BLINE1ST = 0x10; static constexpr RegMask REG_0x09_BACKSCAN = 0x08; static constexpr RegMask REG_0x09_ENHANCE = 0x04; static constexpr RegMask REG_0x09_SHORTTG = 0x02; static constexpr RegMask REG_0x09_NWAIT = 0x01; static constexpr RegShift REG_0x09S_MCNTSET = 6; static constexpr RegShift REG_0x09S_CLKSET = 4; static constexpr RegMask REG_0x0A_LPWMEN = 0x10; static constexpr RegAddr REG_0x0B = 0x0b; static constexpr RegMask REG_0x0B_DRAMSEL = 0x07; static constexpr RegMask REG_0x0B_ENBDRAM = 0x08; static constexpr RegMask REG_0x0B_RFHDIS = 0x10; static constexpr RegMask REG_0x0B_CLKSET = 0xe0; static constexpr RegMask REG_0x0B_24MHZ = 0x00; static constexpr RegMask REG_0x0B_30MHZ = 0x20; static constexpr RegMask REG_0x0B_40MHZ = 0x40; static constexpr RegMask REG_0x0B_48MHZ = 0x60; static constexpr RegMask REG_0x0B_60MHZ = 0x80; static constexpr RegAddr REG_0x0C = 0x0c; static constexpr RegMask REG_0x0C_CCDLMT = 0x0f; static constexpr RegAddr REG_0x0D = 0x0d; static constexpr RegMask REG_0x0D_FULLSTP = 0x10; static constexpr RegMask REG_0x0D_SEND = 0x80; static constexpr RegMask REG_0x0D_CLRMCNT = 0x04; static constexpr RegMask REG_0x0D_CLRDOCJM = 0x02; static constexpr RegMask REG_0x0D_CLRLNCNT = 0x01; static constexpr RegAddr REG_0x0F = 0x0f; static constexpr RegMask REG_0x16_CTRLHI = 0x80; static constexpr RegMask REG_0x16_TOSHIBA = 0x40; static constexpr RegMask REG_0x16_TGINV = 0x20; static constexpr RegMask REG_0x16_CK1INV = 0x10; static constexpr RegMask REG_0x16_CK2INV = 0x08; static constexpr RegMask REG_0x16_CTRLINV = 0x04; static constexpr RegMask REG_0x16_CKDIS = 0x02; static constexpr RegMask REG_0x16_CTRLDIS = 0x01; static constexpr RegMask REG_0x17_TGMODE = 0xc0; static constexpr RegMask REG_0x17_TGMODE_NO_DUMMY = 0x00; static constexpr RegMask REG_0x17_TGMODE_REF = 0x40; static constexpr RegMask REG_0x17_TGMODE_XPA = 0x80; static constexpr RegMask REG_0x17_TGW = 0x3f; static constexpr RegMask REG_0x17S_TGW = 0; static constexpr RegAddr REG_0x18 = 0x18; static constexpr RegMask REG_0x18_CNSET = 0x80; static constexpr RegMask REG_0x18_DCKSEL = 0x60; static constexpr RegMask REG_0x18_CKTOGGLE = 0x10; static constexpr RegMask REG_0x18_CKDELAY = 0x0c; static constexpr RegMask REG_0x18_CKSEL = 0x03; static constexpr RegMask REG_0x1A_SW2SET = 0x80; static constexpr RegMask REG_0x1A_SW1SET = 0x40; static constexpr RegMask REG_0x1A_MANUAL3 = 0x02; static constexpr RegMask REG_0x1A_MANUAL1 = 0x01; static constexpr RegMask REG_0x1A_CK4INV = 0x08; static constexpr RegMask REG_0x1A_CK3INV = 0x04; static constexpr RegMask REG_0x1A_LINECLP = 0x02; static constexpr RegAddr REG_0x1C = 0x1c; static constexpr RegMask REG_0x1C_TGTIME = 0x07; static constexpr RegMask REG_0x1D_CK4LOW = 0x80; static constexpr RegMask REG_0x1D_CK3LOW = 0x40; static constexpr RegMask REG_0x1D_CK1LOW = 0x20; static constexpr RegMask REG_0x1D_TGSHLD = 0x1f; static constexpr RegMask REG_0x1DS_TGSHLD = 0; static constexpr RegAddr REG_0x1E = 0x1e; static constexpr RegMask REG_0x1E_WDTIME = 0xf0; static constexpr RegMask REG_0x1ES_WDTIME = 4; static constexpr RegMask REG_0x1E_LINESEL = 0x0f; static constexpr RegMask REG_0x1ES_LINESEL = 0; static constexpr RegAddr REG_FEDCNT = 0x1f; static constexpr RegAddr REG_0x24 = 0x1c; static constexpr RegAddr REG_0x40 = 0x40; static constexpr RegMask REG_0x40_CHKVER = 0x10; static constexpr RegMask REG_0x40_HISPDFLG = 0x04; static constexpr RegMask REG_0x40_MOTMFLG = 0x02; static constexpr RegMask REG_0x40_DATAENB = 0x01; static constexpr RegMask REG_0x41_PWRBIT = 0x80; static constexpr RegMask REG_0x41_BUFEMPTY = 0x40; static constexpr RegMask REG_0x41_FEEDFSH = 0x20; static constexpr RegMask REG_0x41_SCANFSH = 0x10; static constexpr RegMask REG_0x41_HOMESNR = 0x08; static constexpr RegMask REG_0x41_LAMPSTS = 0x04; static constexpr RegMask REG_0x41_FEBUSY = 0x02; static constexpr RegMask REG_0x41_MOTORENB = 0x01; static constexpr RegMask REG_0x58_VSMP = 0xf8; static constexpr RegShift REG_0x58S_VSMP = 3; static constexpr RegMask REG_0x58_VSMPW = 0x07; static constexpr RegShift REG_0x58S_VSMPW = 0; static constexpr RegMask REG_0x59_BSMP = 0xf8; static constexpr RegShift REG_0x59S_BSMP = 3; static constexpr RegMask REG_0x59_BSMPW = 0x07; static constexpr RegShift REG_0x59S_BSMPW = 0; static constexpr RegMask REG_0x5A_ADCLKINV = 0x80; static constexpr RegMask REG_0x5A_RLCSEL = 0x40; static constexpr RegMask REG_0x5A_CDSREF = 0x30; static constexpr RegShift REG_0x5AS_CDSREF = 4; static constexpr RegMask REG_0x5A_RLC = 0x0f; static constexpr RegShift REG_0x5AS_RLC = 0; static constexpr RegMask REG_0x5E_DECSEL = 0xe0; static constexpr RegShift REG_0x5ES_DECSEL = 5; static constexpr RegMask REG_0x5E_STOPTIM = 0x1f; static constexpr RegShift REG_0x5ES_STOPTIM = 0; static constexpr RegAddr REG_0x60 = 0x60; static constexpr RegMask REG_0x60_Z1MOD = 0x1f; static constexpr RegAddr REG_0x61 = 0x61; static constexpr RegMask REG_0x61_Z1MOD = 0xff; static constexpr RegAddr REG_0x62 = 0x62; static constexpr RegMask REG_0x62_Z1MOD = 0xff; static constexpr RegAddr REG_0x63 = 0x63; static constexpr RegMask REG_0x63_Z2MOD = 0x1f; static constexpr RegAddr REG_0x64 = 0x64; static constexpr RegMask REG_0x64_Z2MOD = 0xff; static constexpr RegAddr REG_0x65 = 0x65; static constexpr RegMask REG_0x65_Z2MOD = 0xff; static constexpr RegShift REG_0x60S_STEPSEL = 5; static constexpr RegMask REG_0x60_STEPSEL = 0xe0; static constexpr RegMask REG_0x60_FULLSTEP = 0x00; static constexpr RegMask REG_0x60_HALFSTEP = 0x20; static constexpr RegMask REG_0x60_EIGHTHSTEP = 0x60; static constexpr RegMask REG_0x60_16THSTEP = 0x80; static constexpr RegShift REG_0x63S_FSTPSEL = 5; static constexpr RegMask REG_0x63_FSTPSEL = 0xe0; static constexpr RegMask REG_0x63_FULLSTEP = 0x00; static constexpr RegMask REG_0x63_HALFSTEP = 0x20; static constexpr RegMask REG_0x63_EIGHTHSTEP = 0x60; static constexpr RegMask REG_0x63_16THSTEP = 0x80; static constexpr RegAddr REG_0x67 = 0x67; static constexpr RegMask REG_0x67_MTRPWM = 0x80; static constexpr RegAddr REG_0x68 = 0x68; static constexpr RegMask REG_0x68_FASTPWM = 0x80; static constexpr RegAddr REG_0x6B = 0x6b; static constexpr RegMask REG_0x6B_MULTFILM = 0x80; static constexpr RegMask REG_0x6B_GPOM13 = 0x40; static constexpr RegMask REG_0x6B_GPOM12 = 0x20; static constexpr RegMask REG_0x6B_GPOM11 = 0x10; static constexpr RegMask REG_0x6B_GPO18 = 0x02; static constexpr RegMask REG_0x6B_GPO17 = 0x01; static constexpr RegShift REG_0x6C = 0x6c; static constexpr RegMask REG_0x6C_GPIO16 = 0x80; static constexpr RegMask REG_0x6C_GPIO15 = 0x40; static constexpr RegMask REG_0x6C_GPIO14 = 0x20; static constexpr RegMask REG_0x6C_GPIO13 = 0x10; static constexpr RegMask REG_0x6C_GPIO12 = 0x08; static constexpr RegMask REG_0x6C_GPIO11 = 0x04; static constexpr RegMask REG_0x6C_GPIO10 = 0x02; static constexpr RegMask REG_0x6C_GPIO9 = 0x01; static constexpr RegMask REG_0x6C_GPIOH = 0xff; static constexpr RegMask REG_0x6C_GPIOL = 0xff; static constexpr RegAddr REG_0x6D = 0x6d; static constexpr RegAddr REG_0x6E = 0x6e; static constexpr RegAddr REG_0x6F = 0x6f; static constexpr RegAddr REG_0x7E = 0x7e; static constexpr RegMask REG_0x87_LEDADD = 0x04; static constexpr RegAddr REG_0x9E = 0x9e; static constexpr RegAddr REG_0x9F = 0x9f; static constexpr RegAddr REG_0xA6 = 0xa6; static constexpr RegAddr REG_0xA7 = 0xa7; static constexpr RegAddr REG_0xA8 = 0xa8; static constexpr RegAddr REG_0xA9 = 0xa9; static constexpr RegAddr REG_0xAB = 0xab; static constexpr RegAddr REG_EXPR = 0x10; static constexpr RegAddr REG_EXPG = 0x12; static constexpr RegAddr REG_EXPB = 0x14; static constexpr RegAddr REG_EXPDMY = 0x19; static constexpr RegAddr REG_STEPNO = 0x21; static constexpr RegAddr REG_FWDSTEP = 0x22; static constexpr RegAddr REG_BWDSTEP = 0x23; static constexpr RegAddr REG_FASTNO = 0x24; static constexpr RegAddr REG_DPISET = 0x2c; static constexpr RegAddr REG_STRPIXEL = 0x30; static constexpr RegAddr REG_ENDPIXEL = 0x32; static constexpr RegAddr REG_LINCNT = 0x25; static constexpr RegAddr REG_MAXWD = 0x35; static constexpr RegAddr REG_LPERIOD = 0x38; static constexpr RegAddr REG_FEEDL = 0x3d; static constexpr RegAddr REG_FMOVDEC = 0x5f; static constexpr RegAddr REG_FSHDEC = 0x69; static constexpr RegAddr REG_FMOVNO = 0x6a; static constexpr RegAddr REG_CK1MAP = 0x74; static constexpr RegAddr REG_CK3MAP = 0x77; static constexpr RegAddr REG_CK4MAP = 0x7a; } // namespace gl847 } // namespace genesys #endif // BACKEND_GENESYS_GL847_REGISTERS_H backends-1.3.0/backend/genesys/image.cpp000066400000000000000000000204661456256263500201450ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "image.h" #if defined(HAVE_TIFFIO_H) #include #endif #include namespace genesys { Image::Image() = default; Image::Image(std::size_t width, std::size_t height, PixelFormat format) : width_{width}, height_{height}, format_{format}, row_bytes_{get_pixel_row_bytes(format_, width_)} { data_.resize(get_row_bytes() * height); } std::uint8_t* Image::get_row_ptr(std::size_t y) { return data_.data() + row_bytes_ * y; } const std::uint8_t* Image::get_row_ptr(std::size_t y) const { return data_.data() + row_bytes_ * y; } Pixel Image::get_pixel(std::size_t x, std::size_t y) const { return get_pixel_from_row(get_row_ptr(y), x, format_); } void Image::set_pixel(std::size_t x, std::size_t y, const Pixel& pixel) { set_pixel_to_row(get_row_ptr(y), x, pixel, format_); } RawPixel Image::get_raw_pixel(std::size_t x, std::size_t y) const { return get_raw_pixel_from_row(get_row_ptr(y), x, format_); } std::uint16_t Image::get_raw_channel(std::size_t x, std::size_t y, unsigned channel) const { return get_raw_channel_from_row(get_row_ptr(y), x, channel, format_); } void Image::set_raw_pixel(std::size_t x, std::size_t y, const RawPixel& pixel) { set_raw_pixel_to_row(get_row_ptr(y), x, pixel, format_); } void Image::resize(std::size_t width, std::size_t height, PixelFormat format) { width_ = width; height_ = height; format_ = format; row_bytes_ = get_pixel_row_bytes(format_, width_); data_.resize(get_row_bytes() * height); } template void convert_pixel_row_impl2(const std::uint8_t* in_data, std::uint8_t* out_data, std::size_t count) { for (std::size_t i = 0; i < count; ++i) { Pixel pixel = get_pixel_from_row(in_data, i, SrcFormat); set_pixel_to_row(out_data, i, pixel, DstFormat); } } template void convert_pixel_row_impl(const std::uint8_t* in_data, std::uint8_t* out_data, PixelFormat out_format, std::size_t count) { switch (out_format) { case PixelFormat::I1: { convert_pixel_row_impl2(in_data, out_data, count); return; } case PixelFormat::RGB111: { convert_pixel_row_impl2(in_data, out_data, count); return; } case PixelFormat::I8: { convert_pixel_row_impl2(in_data, out_data, count); return; } case PixelFormat::RGB888: { convert_pixel_row_impl2(in_data, out_data, count); return; } case PixelFormat::BGR888: { convert_pixel_row_impl2(in_data, out_data, count); return; } case PixelFormat::I16: { convert_pixel_row_impl2(in_data, out_data, count); return; } case PixelFormat::RGB161616: { convert_pixel_row_impl2(in_data, out_data, count); return; } case PixelFormat::BGR161616: { convert_pixel_row_impl2(in_data, out_data, count); return; } default: throw SaneException("Unknown pixel format %d", static_cast(out_format)); } } void convert_pixel_row_format(const std::uint8_t* in_data, PixelFormat in_format, std::uint8_t* out_data, PixelFormat out_format, std::size_t count) { if (in_format == out_format) { std::memcpy(out_data, in_data, get_pixel_row_bytes(in_format, count)); return; } switch (in_format) { case PixelFormat::I1: { convert_pixel_row_impl(in_data, out_data, out_format, count); return; } case PixelFormat::RGB111: { convert_pixel_row_impl(in_data, out_data, out_format, count); return; } case PixelFormat::I8: { convert_pixel_row_impl(in_data, out_data, out_format, count); return; } case PixelFormat::RGB888: { convert_pixel_row_impl(in_data, out_data, out_format, count); return; } case PixelFormat::BGR888: { convert_pixel_row_impl(in_data, out_data, out_format, count); return; } case PixelFormat::I16: { convert_pixel_row_impl(in_data, out_data, out_format, count); return; } case PixelFormat::RGB161616: { convert_pixel_row_impl(in_data, out_data, out_format, count); return; } case PixelFormat::BGR161616: { convert_pixel_row_impl(in_data, out_data, out_format, count); return; } default: throw SaneException("Unknown pixel format %d", static_cast(in_format)); } } void write_tiff_file(const std::string& filename, const void* data, int depth, int channels, int pixels_per_line, int lines) { DBG_HELPER_ARGS(dbg, "depth=%d, channels=%d, ppl=%d, lines=%d", depth, channels, pixels_per_line, lines); #if defined(HAVE_TIFFIO_H) auto image = TIFFOpen(filename.c_str(), "w"); if (!image) { dbg.log(DBG_error, "Could not save debug image"); return; } TIFFSetField(image, TIFFTAG_IMAGEWIDTH, pixels_per_line); TIFFSetField(image, TIFFTAG_IMAGELENGTH, lines); TIFFSetField(image, TIFFTAG_BITSPERSAMPLE, depth); TIFFSetField(image, TIFFTAG_SAMPLESPERPIXEL, channels); if (channels > 1) { TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); } else { TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); } TIFFSetField(image, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField(image, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); std::size_t bytes_per_line = (pixels_per_line * channels * depth + 7) / 8; const std::uint8_t* data_ptr = reinterpret_cast(data); // we don't need to handle endian because libtiff will handle that for (int iline = 0; iline < lines; ++iline) { const auto* line_data = data_ptr + bytes_per_line * iline; TIFFWriteScanline(image, const_cast(line_data), iline, 0); } TIFFClose(image); #else dbg.log(DBG_error, "Backend has been built without TIFF library support. " "Debug images will not be saved"); #endif } bool is_supported_write_tiff_file_image_format(PixelFormat format) { switch (format) { case PixelFormat::I1: case PixelFormat::RGB111: case PixelFormat::I8: case PixelFormat::RGB888: case PixelFormat::I16: case PixelFormat::RGB161616: return true; default: return false; } } void write_tiff_file(const std::string& filename, const Image& image) { if (!is_supported_write_tiff_file_image_format(image.get_format())) { throw SaneException("Unsupported format %d", static_cast(image.get_format())); } write_tiff_file(filename, image.get_row_ptr(0), get_pixel_format_depth(image.get_format()), get_pixel_channels(image.get_format()), image.get_width(), image.get_height()); } } // namespace genesys backends-1.3.0/backend/genesys/image.h000066400000000000000000000046111456256263500176040ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_IMAGE_H #define BACKEND_GENESYS_IMAGE_H #include "image_pixel.h" #include namespace genesys { class Image { public: Image(); Image(std::size_t width, std::size_t height, PixelFormat format); std::size_t get_width() const { return width_; } std::size_t get_height() const { return height_; } PixelFormat get_format() const { return format_; } std::size_t get_row_bytes() const { return row_bytes_; } std::uint8_t* get_row_ptr(std::size_t y); const std::uint8_t* get_row_ptr(std::size_t y) const; Pixel get_pixel(std::size_t x, std::size_t y) const; void set_pixel(std::size_t x, std::size_t y, const Pixel& pixel); RawPixel get_raw_pixel(std::size_t x, std::size_t y) const; std::uint16_t get_raw_channel(std::size_t x, std::size_t y, unsigned channel) const; void set_raw_pixel(std::size_t x, std::size_t y, const RawPixel& pixel); void resize(std::size_t width, std::size_t height, PixelFormat format); private: std::size_t width_ = 0; std::size_t height_ = 0; PixelFormat format_ = PixelFormat::UNKNOWN; std::size_t row_bytes_ = 0; std::vector data_; }; void convert_pixel_row_format(const std::uint8_t* in_data, PixelFormat in_format, std::uint8_t* out_data, PixelFormat out_format, std::size_t count); void write_tiff_file(const std::string& filename, const void* data, int depth, int channels, int pixels_per_line, int lines); void write_tiff_file(const std::string& filename, const Image& image); } // namespace genesys #endif // ifndef BACKEND_GENESYS_IMAGE_H backends-1.3.0/backend/genesys/image_buffer.cpp000066400000000000000000000050001456256263500214610ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "image_buffer.h" #include "image.h" #include "utilities.h" namespace genesys { ImageBuffer::ImageBuffer(std::size_t size, ProducerCallback producer) : producer_{producer}, size_{size} { buffer_.resize(size_); } bool ImageBuffer::get_data(std::size_t size, std::uint8_t* out_data) { const std::uint8_t* out_data_end = out_data + size; auto copy_buffer = [&]() { std::size_t bytes_copy = std::min(out_data_end - out_data, available()); std::memcpy(out_data, buffer_.data() + buffer_offset_, bytes_copy); out_data += bytes_copy; buffer_offset_ += bytes_copy; }; // first, read remaining data from buffer if (available() > 0) { copy_buffer(); } if (out_data == out_data_end) { return true; } // now the buffer is empty and there's more data to be read bool got_data = true; do { buffer_offset_ = 0; std::size_t size_to_read = size_; if (remaining_size_ != BUFFER_SIZE_UNSET) { size_to_read = std::min(size_to_read, remaining_size_); remaining_size_ -= size_to_read; } std::size_t aligned_size_to_read = size_to_read; if (remaining_size_ == 0 && last_read_multiple_ != BUFFER_SIZE_UNSET) { aligned_size_to_read = align_multiple_ceil(size_to_read, last_read_multiple_); } got_data &= producer_(aligned_size_to_read, buffer_.data()); curr_size_ = size_to_read; copy_buffer(); if (remaining_size_ == 0 && out_data < out_data_end) { got_data = false; } } while (out_data < out_data_end && got_data); return got_data; } } // namespace genesys backends-1.3.0/backend/genesys/image_buffer.h000066400000000000000000000044751456256263500211450ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_IMAGE_BUFFER_H #define BACKEND_GENESYS_IMAGE_BUFFER_H #include "enums.h" #include "row_buffer.h" #include #include namespace genesys { // This class allows reading from row-based source in smaller or larger chunks of data class ImageBuffer { public: using ProducerCallback = std::function; static constexpr std::uint64_t BUFFER_SIZE_UNSET = std::numeric_limits::max(); ImageBuffer() {} ImageBuffer(std::size_t size, ProducerCallback producer); std::size_t available() const { return curr_size_ - buffer_offset_; } // allows adjusting the amount of data left so that we don't do a full size read from the // producer on the last iteration. Set to BUFFER_SIZE_UNSET to ignore buffer size. std::uint64_t remaining_size() const { return remaining_size_; } void set_remaining_size(std::uint64_t bytes) { remaining_size_ = bytes; } // May be used to force the last read to be rounded up of a certain number of bytes void set_last_read_multiple(std::uint64_t bytes) { last_read_multiple_ = bytes; } bool get_data(std::size_t size, std::uint8_t* out_data); private: ProducerCallback producer_; std::size_t size_ = 0; std::size_t curr_size_ = 0; std::uint64_t remaining_size_ = BUFFER_SIZE_UNSET; std::uint64_t last_read_multiple_ = BUFFER_SIZE_UNSET; std::size_t buffer_offset_ = 0; std::vector buffer_; }; } // namespace genesys #endif // BACKEND_GENESYS_IMAGE_BUFFER_H backends-1.3.0/backend/genesys/image_pipeline.cpp000066400000000000000000000742171456256263500220350ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "image_pipeline.h" #include "image.h" #include "low.h" #include #include namespace genesys { ImagePipelineNode::~ImagePipelineNode() {} bool ImagePipelineNodeCallableSource::get_next_row_data(std::uint8_t* out_data) { bool got_data = producer_(get_row_bytes(), out_data); if (!got_data) eof_ = true; return got_data; } ImagePipelineNodeBufferedCallableSource::ImagePipelineNodeBufferedCallableSource( std::size_t width, std::size_t height, PixelFormat format, std::size_t input_batch_size, ProducerCallback producer) : width_{width}, height_{height}, format_{format}, buffer_{input_batch_size, producer} { buffer_.set_remaining_size(height_ * get_row_bytes()); } bool ImagePipelineNodeBufferedCallableSource::get_next_row_data(std::uint8_t* out_data) { if (curr_row_ >= get_height()) { DBG(DBG_warn, "%s: reading out of bounds. Row %zu, height: %zu\n", __func__, curr_row_, get_height()); eof_ = true; return false; } bool got_data = true; got_data &= buffer_.get_data(get_row_bytes(), out_data); curr_row_++; if (!got_data) { eof_ = true; } return got_data; } ImagePipelineNodeArraySource::ImagePipelineNodeArraySource(std::size_t width, std::size_t height, PixelFormat format, std::vector data) : width_{width}, height_{height}, format_{format}, data_{std::move(data)}, next_row_{0} { auto size = get_row_bytes() * height_; if (data_.size() < size) { throw SaneException("The given array is too small (%zu bytes). Need at least %zu", data_.size(), size); } } bool ImagePipelineNodeArraySource::get_next_row_data(std::uint8_t* out_data) { if (next_row_ >= height_) { eof_ = true; return false; } auto row_bytes = get_row_bytes(); std::memcpy(out_data, data_.data() + row_bytes * next_row_, row_bytes); next_row_++; return true; } ImagePipelineNodeImageSource::ImagePipelineNodeImageSource(const Image& source) : source_{source} {} bool ImagePipelineNodeImageSource::get_next_row_data(std::uint8_t* out_data) { if (next_row_ >= get_height()) { return false; } std::memcpy(out_data, source_.get_row_ptr(next_row_), get_row_bytes()); next_row_++; return true; } bool ImagePipelineNodeFormatConvert::get_next_row_data(std::uint8_t* out_data) { auto src_format = source_.get_format(); if (src_format == dst_format_) { return source_.get_next_row_data(out_data); } buffer_.clear(); buffer_.resize(source_.get_row_bytes()); bool got_data = source_.get_next_row_data(buffer_.data()); convert_pixel_row_format(buffer_.data(), src_format, out_data, dst_format_, get_width()); return got_data; } ImagePipelineNodeDesegment::ImagePipelineNodeDesegment(ImagePipelineNode& source, std::size_t output_width, const std::vector& segment_order, std::size_t segment_pixels, std::size_t interleaved_lines, std::size_t pixels_per_chunk) : source_(source), output_width_{output_width}, segment_order_{segment_order}, segment_pixels_{segment_pixels}, interleaved_lines_{interleaved_lines}, pixels_per_chunk_{pixels_per_chunk}, buffer_{source_.get_row_bytes()} { DBG_HELPER_ARGS(dbg, "segment_count=%zu, segment_size=%zu, interleaved_lines=%zu, " "pixels_per_shunk=%zu", segment_order.size(), segment_pixels, interleaved_lines, pixels_per_chunk); if (source_.get_height() % interleaved_lines_ > 0) { throw SaneException("Height is not a multiple of the number of lines to interelave %zu/%zu", source_.get_height(), interleaved_lines_); } } ImagePipelineNodeDesegment::ImagePipelineNodeDesegment(ImagePipelineNode& source, std::size_t output_width, std::size_t segment_count, std::size_t segment_pixels, std::size_t interleaved_lines, std::size_t pixels_per_chunk) : source_(source), output_width_{output_width}, segment_pixels_{segment_pixels}, interleaved_lines_{interleaved_lines}, pixels_per_chunk_{pixels_per_chunk}, buffer_{source_.get_row_bytes()} { DBG_HELPER_ARGS(dbg, "segment_count=%zu, segment_size=%zu, interleaved_lines=%zu, " "pixels_per_shunk=%zu", segment_count, segment_pixels, interleaved_lines, pixels_per_chunk); segment_order_.resize(segment_count); std::iota(segment_order_.begin(), segment_order_.end(), 0); } bool ImagePipelineNodeDesegment::get_next_row_data(std::uint8_t* out_data) { bool got_data = true; buffer_.clear(); for (std::size_t i = 0; i < interleaved_lines_; ++i) { buffer_.push_back(); got_data &= source_.get_next_row_data(buffer_.get_row_ptr(i)); } if (!buffer_.is_linear()) { throw SaneException("Buffer is not linear"); } auto format = get_format(); auto segment_count = segment_order_.size(); const std::uint8_t* in_data = buffer_.get_row_ptr(0); std::size_t groups_count = output_width_ / (segment_order_.size() * pixels_per_chunk_); for (std::size_t igroup = 0; igroup < groups_count; ++igroup) { for (std::size_t isegment = 0; isegment < segment_count; ++isegment) { auto input_offset = igroup * pixels_per_chunk_; input_offset += segment_pixels_ * segment_order_[isegment]; auto output_offset = (igroup * segment_count + isegment) * pixels_per_chunk_; for (std::size_t ipixel = 0; ipixel < pixels_per_chunk_; ++ipixel) { auto pixel = get_raw_pixel_from_row(in_data, input_offset + ipixel, format); set_raw_pixel_to_row(out_data, output_offset + ipixel, pixel, format); } } } return got_data; } ImagePipelineNodeDeinterleaveLines::ImagePipelineNodeDeinterleaveLines( ImagePipelineNode& source, std::size_t interleaved_lines, std::size_t pixels_per_chunk) : ImagePipelineNodeDesegment(source, source.get_width() * interleaved_lines, interleaved_lines, source.get_width(), interleaved_lines, pixels_per_chunk) {} ImagePipelineNodeSwap16BitEndian::ImagePipelineNodeSwap16BitEndian(ImagePipelineNode& source) : source_(source), needs_swapping_{false} { if (get_pixel_format_depth(source_.get_format()) == 16) { needs_swapping_ = true; } else { DBG(DBG_info, "%s: this pipeline node does nothing for non 16-bit formats", __func__); } } bool ImagePipelineNodeSwap16BitEndian::get_next_row_data(std::uint8_t* out_data) { bool got_data = source_.get_next_row_data(out_data); if (needs_swapping_) { std::size_t pixels = get_row_bytes() / 2; for (std::size_t i = 0; i < pixels; ++i) { std::swap(*out_data, *(out_data + 1)); out_data += 2; } } return got_data; } ImagePipelineNodeInvert::ImagePipelineNodeInvert(ImagePipelineNode& source) : source_(source) { } bool ImagePipelineNodeInvert::get_next_row_data(std::uint8_t* out_data) { bool got_data = source_.get_next_row_data(out_data); auto num_values = get_width() * get_pixel_channels(source_.get_format()); auto depth = get_pixel_format_depth(source_.get_format()); switch (depth) { case 16: { auto* data = reinterpret_cast(out_data); for (std::size_t i = 0; i < num_values; ++i) { *data = 0xffff - *data; data++; } break; } case 8: { auto* data = out_data; for (std::size_t i = 0; i < num_values; ++i) { *data = 0xff - *data; data++; } break; } case 1: { auto* data = out_data; auto num_bytes = (num_values + 7) / 8; for (std::size_t i = 0; i < num_bytes; ++i) { *data = ~*data; data++; } break; } default: throw SaneException("Unsupported pixel depth"); } return got_data; } ImagePipelineNodeMergeMonoLinesToColor::ImagePipelineNodeMergeMonoLinesToColor( ImagePipelineNode& source, ColorOrder color_order) : source_(source), buffer_(source_.get_row_bytes()) { DBG_HELPER_ARGS(dbg, "color_order %d", static_cast(color_order)); output_format_ = get_output_format(source_.get_format(), color_order); } bool ImagePipelineNodeMergeMonoLinesToColor::get_next_row_data(std::uint8_t* out_data) { bool got_data = true; buffer_.clear(); for (unsigned i = 0; i < 3; ++i) { buffer_.push_back(); got_data &= source_.get_next_row_data(buffer_.get_row_ptr(i)); } const auto* row0 = buffer_.get_row_ptr(0); const auto* row1 = buffer_.get_row_ptr(1); const auto* row2 = buffer_.get_row_ptr(2); auto format = source_.get_format(); for (std::size_t x = 0, width = get_width(); x < width; ++x) { std::uint16_t ch0 = get_raw_channel_from_row(row0, x, 0, format); std::uint16_t ch1 = get_raw_channel_from_row(row1, x, 0, format); std::uint16_t ch2 = get_raw_channel_from_row(row2, x, 0, format); set_raw_channel_to_row(out_data, x, 0, ch0, output_format_); set_raw_channel_to_row(out_data, x, 1, ch1, output_format_); set_raw_channel_to_row(out_data, x, 2, ch2, output_format_); } return got_data; } PixelFormat ImagePipelineNodeMergeMonoLinesToColor::get_output_format(PixelFormat input_format, ColorOrder order) { switch (input_format) { case PixelFormat::I1: { if (order == ColorOrder::RGB) { return PixelFormat::RGB111; } break; } case PixelFormat::I8: { if (order == ColorOrder::RGB) { return PixelFormat::RGB888; } if (order == ColorOrder::BGR) { return PixelFormat::BGR888; } break; } case PixelFormat::I16: { if (order == ColorOrder::RGB) { return PixelFormat::RGB161616; } if (order == ColorOrder::BGR) { return PixelFormat::BGR161616; } break; } default: break; } throw SaneException("Unsupported format combidation %d %d", static_cast(input_format), static_cast(order)); } ImagePipelineNodeSplitMonoLines::ImagePipelineNodeSplitMonoLines(ImagePipelineNode& source) : source_(source), next_channel_{0} { output_format_ = get_output_format(source_.get_format()); } bool ImagePipelineNodeSplitMonoLines::get_next_row_data(std::uint8_t* out_data) { bool got_data = true; if (next_channel_ == 0) { buffer_.resize(source_.get_row_bytes()); got_data &= source_.get_next_row_data(buffer_.data()); } const auto* row = buffer_.data(); auto format = source_.get_format(); for (std::size_t x = 0, width = get_width(); x < width; ++x) { std::uint16_t ch = get_raw_channel_from_row(row, x, next_channel_, format); set_raw_channel_to_row(out_data, x, 0, ch, output_format_); } next_channel_ = (next_channel_ + 1) % 3; return got_data; } PixelFormat ImagePipelineNodeSplitMonoLines::get_output_format(PixelFormat input_format) { switch (input_format) { case PixelFormat::RGB111: return PixelFormat::I1; case PixelFormat::RGB888: case PixelFormat::BGR888: return PixelFormat::I8; case PixelFormat::RGB161616: case PixelFormat::BGR161616: return PixelFormat::I16; default: break; } throw SaneException("Unsupported input format %d", static_cast(input_format)); } ImagePipelineNodeMergeColorToGray::ImagePipelineNodeMergeColorToGray(ImagePipelineNode& source) : source_(source) { output_format_ = get_output_format(source_.get_format()); float red_mult = 0.2125f; float green_mult = 0.7154f; float blue_mult = 0.0721f; switch (get_pixel_format_color_order(source_.get_format())) { case ColorOrder::RGB: { ch0_mult_ = red_mult; ch1_mult_ = green_mult; ch2_mult_ = blue_mult; break; } case ColorOrder::BGR: { ch0_mult_ = blue_mult; ch1_mult_ = green_mult; ch2_mult_ = red_mult; break; } case ColorOrder::GBR: { ch0_mult_ = green_mult; ch1_mult_ = blue_mult; ch2_mult_ = red_mult; break; } default: throw SaneException("Unknown color order"); } temp_buffer_.resize(source_.get_row_bytes()); } bool ImagePipelineNodeMergeColorToGray::get_next_row_data(std::uint8_t* out_data) { auto* src_data = temp_buffer_.data(); bool got_data = source_.get_next_row_data(src_data); auto src_format = source_.get_format(); for (std::size_t x = 0, width = get_width(); x < width; ++x) { std::uint16_t ch0 = get_raw_channel_from_row(src_data, x, 0, src_format); std::uint16_t ch1 = get_raw_channel_from_row(src_data, x, 1, src_format); std::uint16_t ch2 = get_raw_channel_from_row(src_data, x, 2, src_format); float mono = ch0 * ch0_mult_ + ch1 * ch1_mult_ + ch2 * ch2_mult_; set_raw_channel_to_row(out_data, x, 0, static_cast(mono), output_format_); } return got_data; } PixelFormat ImagePipelineNodeMergeColorToGray::get_output_format(PixelFormat input_format) { switch (input_format) { case PixelFormat::RGB111: return PixelFormat::I1; case PixelFormat::RGB888: case PixelFormat::BGR888: return PixelFormat::I8; case PixelFormat::RGB161616: case PixelFormat::BGR161616: return PixelFormat::I16; default: break; } throw SaneException("Unsupported format %d", static_cast(input_format)); } ImagePipelineNodeComponentShiftLines::ImagePipelineNodeComponentShiftLines( ImagePipelineNode& source, unsigned shift_r, unsigned shift_g, unsigned shift_b) : source_(source), buffer_{source.get_row_bytes()} { DBG_HELPER_ARGS(dbg, "shifts={%d, %d, %d}", shift_r, shift_g, shift_b); switch (source.get_format()) { case PixelFormat::RGB111: case PixelFormat::RGB888: case PixelFormat::RGB161616: { channel_shifts_ = { shift_r, shift_g, shift_b }; break; } case PixelFormat::BGR888: case PixelFormat::BGR161616: { channel_shifts_ = { shift_b, shift_g, shift_r }; break; } default: throw SaneException("Unsupported input format %d", static_cast(source.get_format())); } extra_height_ = *std::max_element(channel_shifts_.begin(), channel_shifts_.end()); height_ = source_.get_height(); if (extra_height_ > height_) { height_ = 0; } else { height_ -= extra_height_; } } bool ImagePipelineNodeComponentShiftLines::get_next_row_data(std::uint8_t* out_data) { bool got_data = true; if (!buffer_.empty()) { buffer_.pop_front(); } while (buffer_.height() < extra_height_ + 1) { buffer_.push_back(); got_data &= source_.get_next_row_data(buffer_.get_back_row_ptr()); } auto format = get_format(); const auto* row0 = buffer_.get_row_ptr(channel_shifts_[0]); const auto* row1 = buffer_.get_row_ptr(channel_shifts_[1]); const auto* row2 = buffer_.get_row_ptr(channel_shifts_[2]); for (std::size_t x = 0, width = get_width(); x < width; ++x) { std::uint16_t ch0 = get_raw_channel_from_row(row0, x, 0, format); std::uint16_t ch1 = get_raw_channel_from_row(row1, x, 1, format); std::uint16_t ch2 = get_raw_channel_from_row(row2, x, 2, format); set_raw_channel_to_row(out_data, x, 0, ch0, format); set_raw_channel_to_row(out_data, x, 1, ch1, format); set_raw_channel_to_row(out_data, x, 2, ch2, format); } return got_data; } ImagePipelineNodePixelShiftLines::ImagePipelineNodePixelShiftLines( ImagePipelineNode& source, const std::vector& shifts) : source_(source), pixel_shifts_{shifts}, buffer_{get_row_bytes()} { extra_height_ = *std::max_element(pixel_shifts_.begin(), pixel_shifts_.end()); height_ = source_.get_height(); if (extra_height_ > height_) { height_ = 0; } else { height_ -= extra_height_; } } bool ImagePipelineNodePixelShiftLines::get_next_row_data(std::uint8_t* out_data) { bool got_data = true; if (!buffer_.empty()) { buffer_.pop_front(); } while (buffer_.height() < extra_height_ + 1) { buffer_.push_back(); got_data &= source_.get_next_row_data(buffer_.get_back_row_ptr()); } auto format = get_format(); auto shift_count = pixel_shifts_.size(); std::vector rows; rows.resize(shift_count, nullptr); for (std::size_t irow = 0; irow < shift_count; ++irow) { rows[irow] = buffer_.get_row_ptr(pixel_shifts_[irow]); } for (std::size_t x = 0, width = get_width(); x < width;) { for (std::size_t irow = 0; irow < shift_count && x < width; irow++, x++) { RawPixel pixel = get_raw_pixel_from_row(rows[irow], x, format); set_raw_pixel_to_row(out_data, x, pixel, format); } } return got_data; } ImagePipelineNodePixelShiftColumns::ImagePipelineNodePixelShiftColumns( ImagePipelineNode& source, const std::vector& shifts) : source_(source), pixel_shifts_{shifts} { width_ = source_.get_width(); extra_width_ = compute_pixel_shift_extra_width(width_, pixel_shifts_); if (extra_width_ > width_) { width_ = 0; } else { width_ -= extra_width_; } temp_buffer_.resize(source_.get_row_bytes()); } bool ImagePipelineNodePixelShiftColumns::get_next_row_data(std::uint8_t* out_data) { if (width_ == 0) { throw SaneException("Attempt to read zero-width line"); } bool got_data = source_.get_next_row_data(temp_buffer_.data()); auto format = get_format(); auto shift_count = pixel_shifts_.size(); for (std::size_t x = 0, width = get_width(); x < width; x += shift_count) { for (std::size_t ishift = 0; ishift < shift_count && x + ishift < width; ishift++) { RawPixel pixel = get_raw_pixel_from_row(temp_buffer_.data(), x + pixel_shifts_[ishift], format); set_raw_pixel_to_row(out_data, x + ishift, pixel, format); } } return got_data; } std::size_t compute_pixel_shift_extra_width(std::size_t source_width, const std::vector& shifts) { // we iterate across pixel shifts and find the pixel that needs the maximum shift according to // source_width. int group_size = shifts.size(); int non_filled_group = source_width % shifts.size(); int extra_width = 0; for (int i = 0; i < group_size; ++i) { int shift_groups = shifts[i] / group_size; int shift_rem = shifts[i] % group_size; if (shift_rem < non_filled_group) { shift_groups--; } extra_width = std::max(extra_width, shift_groups * group_size + non_filled_group - i); } return extra_width; } ImagePipelineNodeExtract::ImagePipelineNodeExtract(ImagePipelineNode& source, std::size_t offset_x, std::size_t offset_y, std::size_t width, std::size_t height) : source_(source), offset_x_{offset_x}, offset_y_{offset_y}, width_{width}, height_{height} { cached_line_.resize(source_.get_row_bytes()); } ImagePipelineNodeExtract::~ImagePipelineNodeExtract() {} ImagePipelineNodeScaleRows::ImagePipelineNodeScaleRows(ImagePipelineNode& source, std::size_t width) : source_(source), width_{width} { cached_line_.resize(source_.get_row_bytes()); } bool ImagePipelineNodeScaleRows::get_next_row_data(std::uint8_t* out_data) { auto src_width = source_.get_width(); auto dst_width = width_; bool got_data = source_.get_next_row_data(cached_line_.data()); const auto* src_data = cached_line_.data(); auto format = get_format(); auto channels = get_pixel_channels(format); if (src_width > dst_width) { // average std::uint32_t counter = src_width / 2; unsigned src_x = 0; for (unsigned dst_x = 0; dst_x < dst_width; dst_x++) { unsigned avg[3] = {0, 0, 0}; unsigned count = 0; while (counter < src_width && src_x < src_width) { counter += dst_width; for (unsigned c = 0; c < channels; c++) { avg[c] += get_raw_channel_from_row(src_data, src_x, c, format); } src_x++; count++; } counter -= src_width; for (unsigned c = 0; c < channels; c++) { set_raw_channel_to_row(out_data, dst_x, c, avg[c] / count, format); } } } else { // interpolate and copy pixels std::uint32_t counter = dst_width / 2; unsigned dst_x = 0; for (unsigned src_x = 0; src_x < src_width; src_x++) { unsigned avg[3] = {0, 0, 0}; for (unsigned c = 0; c < channels; c++) { avg[c] += get_raw_channel_from_row(src_data, src_x, c, format); } while ((counter < dst_width || src_x + 1 == src_width) && dst_x < dst_width) { counter += src_width; for (unsigned c = 0; c < channels; c++) { set_raw_channel_to_row(out_data, dst_x, c, avg[c], format); } dst_x++; } counter -= dst_width; } } return got_data; } bool ImagePipelineNodeExtract::get_next_row_data(std::uint8_t* out_data) { bool got_data = true; while (current_line_ < offset_y_) { got_data &= source_.get_next_row_data(cached_line_.data()); current_line_++; } if (current_line_ >= offset_y_ + source_.get_height()) { std::fill(out_data, out_data + get_row_bytes(), 0); current_line_++; return got_data; } // now we're sure that the following holds: // offset_y_ <= current_line_ < offset_y_ + source_.get_height()) got_data &= source_.get_next_row_data(cached_line_.data()); auto format = get_format(); auto x_src_width = source_.get_width() > offset_x_ ? source_.get_width() - offset_x_ : 0; x_src_width = std::min(x_src_width, width_); auto x_pad_after = width_ > x_src_width ? width_ - x_src_width : 0; if (get_pixel_format_depth(format) < 8) { // we need to copy pixels one-by-one as there's no per-bit addressing for (std::size_t i = 0; i < x_src_width; ++i) { auto pixel = get_raw_pixel_from_row(cached_line_.data(), i + offset_x_, format); set_raw_pixel_to_row(out_data, i, pixel, format); } for (std::size_t i = 0; i < x_pad_after; ++i) { set_raw_pixel_to_row(out_data, i + x_src_width, RawPixel{}, format); } } else { std::size_t bpp = get_pixel_format_depth(format) / 8; if (x_src_width > 0) { std::memcpy(out_data, cached_line_.data() + offset_x_ * bpp, x_src_width * bpp); } if (x_pad_after > 0) { std::fill(out_data + x_src_width * bpp, out_data + (x_src_width + x_pad_after) * bpp, 0); } } current_line_++; return got_data; } ImagePipelineNodeCalibrate::ImagePipelineNodeCalibrate(ImagePipelineNode& source, const std::vector& bottom, const std::vector& top, std::size_t x_start) : source_(source) { std::size_t size = 0; if (bottom.size() >= x_start && top.size() >= x_start) { size = std::min(bottom.size() - x_start, top.size() - x_start); } offset_.reserve(size); multiplier_.reserve(size); for (std::size_t i = 0; i < size; ++i) { offset_.push_back(bottom[i + x_start] / 65535.0f); multiplier_.push_back(65535.0f / (top[i + x_start] - bottom[i + x_start])); } } bool ImagePipelineNodeCalibrate::get_next_row_data(std::uint8_t* out_data) { bool ret = source_.get_next_row_data(out_data); auto format = get_format(); auto depth = get_pixel_format_depth(format); std::size_t max_value = 1; switch (depth) { case 8: max_value = 255; break; case 16: max_value = 65535; break; default: throw SaneException("Unsupported depth for calibration %d", depth); } unsigned channels = get_pixel_channels(format); std::size_t max_calib_i = offset_.size(); std::size_t curr_calib_i = 0; for (std::size_t x = 0, width = get_width(); x < width && curr_calib_i < max_calib_i; ++x) { for (unsigned ch = 0; ch < channels && curr_calib_i < max_calib_i; ++ch) { std::int32_t value = get_raw_channel_from_row(out_data, x, ch, format); float value_f = static_cast(value) / max_value; value_f = (value_f - offset_[curr_calib_i]) * multiplier_[curr_calib_i]; value_f = std::round(value_f * max_value); value = clamp(static_cast(value_f), 0, max_value); set_raw_channel_to_row(out_data, x, ch, value, format); curr_calib_i++; } } return ret; } ImagePipelineNodeDebug::ImagePipelineNodeDebug(ImagePipelineNode& source, const std::string& path) : source_(source), path_{path}, buffer_{source_.get_row_bytes()} {} ImagePipelineNodeDebug::~ImagePipelineNodeDebug() { catch_all_exceptions(__func__, [&]() { if (buffer_.empty()) return; auto format = get_format(); buffer_.linearize(); write_tiff_file(path_, buffer_.get_front_row_ptr(), get_pixel_format_depth(format), get_pixel_channels(format), get_width(), buffer_.height()); }); } bool ImagePipelineNodeDebug::get_next_row_data(std::uint8_t* out_data) { buffer_.push_back(); bool got_data = source_.get_next_row_data(out_data); std::memcpy(buffer_.get_back_row_ptr(), out_data, get_row_bytes()); return got_data; } std::size_t ImagePipelineStack::get_input_width() const { ensure_node_exists(); return nodes_.front()->get_width(); } std::size_t ImagePipelineStack::get_input_height() const { ensure_node_exists(); return nodes_.front()->get_height(); } PixelFormat ImagePipelineStack::get_input_format() const { ensure_node_exists(); return nodes_.front()->get_format(); } std::size_t ImagePipelineStack::get_input_row_bytes() const { ensure_node_exists(); return nodes_.front()->get_row_bytes(); } std::size_t ImagePipelineStack::get_output_width() const { ensure_node_exists(); return nodes_.back()->get_width(); } std::size_t ImagePipelineStack::get_output_height() const { ensure_node_exists(); return nodes_.back()->get_height(); } PixelFormat ImagePipelineStack::get_output_format() const { ensure_node_exists(); return nodes_.back()->get_format(); } std::size_t ImagePipelineStack::get_output_row_bytes() const { ensure_node_exists(); return nodes_.back()->get_row_bytes(); } void ImagePipelineStack::ensure_node_exists() const { if (nodes_.empty()) { throw SaneException("The pipeline does not contain any nodes"); } } void ImagePipelineStack::clear() { // we need to destroy the nodes back to front, so that the destructors still have valid // references to sources for (auto it = nodes_.rbegin(); it != nodes_.rend(); ++it) { it->reset(); } nodes_.clear(); } std::vector ImagePipelineStack::get_all_data() { auto row_bytes = get_output_row_bytes(); auto height = get_output_height(); std::vector ret; ret.resize(row_bytes * height); for (std::size_t i = 0; i < height; ++i) { get_next_row_data(ret.data() + row_bytes * i); } return ret; } Image ImagePipelineStack::get_image() { auto height = get_output_height(); Image ret; ret.resize(get_output_width(), height, get_output_format()); for (std::size_t i = 0; i < height; ++i) { get_next_row_data(ret.get_row_ptr(i)); } return ret; } } // namespace genesys backends-1.3.0/backend/genesys/image_pipeline.h000066400000000000000000000465641456256263500215060ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_IMAGE_PIPELINE_H #define BACKEND_GENESYS_IMAGE_PIPELINE_H #include "image.h" #include "image_pixel.h" #include "image_buffer.h" #include #include #include namespace genesys { class ImagePipelineNode { public: virtual ~ImagePipelineNode(); virtual std::size_t get_width() const = 0; virtual std::size_t get_height() const = 0; virtual PixelFormat get_format() const = 0; std::size_t get_row_bytes() const { return get_pixel_row_bytes(get_format(), get_width()); } virtual bool eof() const = 0; // returns true if the row was filled successfully, false otherwise (e.g. if not enough data // was available. virtual bool get_next_row_data(std::uint8_t* out_data) = 0; }; // A pipeline node that produces data from a callable class ImagePipelineNodeCallableSource : public ImagePipelineNode { public: using ProducerCallback = std::function; ImagePipelineNodeCallableSource(std::size_t width, std::size_t height, PixelFormat format, ProducerCallback producer) : producer_{producer}, width_{width}, height_{height}, format_{format} {} std::size_t get_width() const override { return width_; } std::size_t get_height() const override { return height_; } PixelFormat get_format() const override { return format_; } bool eof() const override { return eof_; } bool get_next_row_data(std::uint8_t* out_data) override; private: ProducerCallback producer_; std::size_t width_ = 0; std::size_t height_ = 0; PixelFormat format_ = PixelFormat::UNKNOWN; bool eof_ = false; }; // A pipeline node that produces data from a callable requesting fixed-size chunks. class ImagePipelineNodeBufferedCallableSource : public ImagePipelineNode { public: using ProducerCallback = std::function; ImagePipelineNodeBufferedCallableSource(std::size_t width, std::size_t height, PixelFormat format, std::size_t input_batch_size, ProducerCallback producer); std::size_t get_width() const override { return width_; } std::size_t get_height() const override { return height_; } PixelFormat get_format() const override { return format_; } bool eof() const override { return eof_; } bool get_next_row_data(std::uint8_t* out_data) override; std::size_t remaining_bytes() const { return buffer_.remaining_size(); } void set_remaining_bytes(std::size_t bytes) { buffer_.set_remaining_size(bytes); } void set_last_read_multiple(std::size_t bytes) { buffer_.set_last_read_multiple(bytes); } private: ProducerCallback producer_; std::size_t width_ = 0; std::size_t height_ = 0; PixelFormat format_ = PixelFormat::UNKNOWN; bool eof_ = false; std::size_t curr_row_ = 0; ImageBuffer buffer_; }; // A pipeline node that produces data from the given array. class ImagePipelineNodeArraySource : public ImagePipelineNode { public: ImagePipelineNodeArraySource(std::size_t width, std::size_t height, PixelFormat format, std::vector data); std::size_t get_width() const override { return width_; } std::size_t get_height() const override { return height_; } PixelFormat get_format() const override { return format_; } bool eof() const override { return eof_; } bool get_next_row_data(std::uint8_t* out_data) override; private: std::size_t width_ = 0; std::size_t height_ = 0; PixelFormat format_ = PixelFormat::UNKNOWN; bool eof_ = false; std::vector data_; std::size_t next_row_ = 0; }; /// A pipeline node that produces data from the given image class ImagePipelineNodeImageSource : public ImagePipelineNode { public: ImagePipelineNodeImageSource(const Image& source); std::size_t get_width() const override { return source_.get_width(); } std::size_t get_height() const override { return source_.get_height(); } PixelFormat get_format() const override { return source_.get_format(); } bool eof() const override { return next_row_ >= get_height(); } bool get_next_row_data(std::uint8_t* out_data) override; private: const Image& source_; std::size_t next_row_ = 0; }; // A pipeline node that converts between pixel formats class ImagePipelineNodeFormatConvert : public ImagePipelineNode { public: ImagePipelineNodeFormatConvert(ImagePipelineNode& source, PixelFormat dst_format) : source_(source), dst_format_{dst_format} {} ~ImagePipelineNodeFormatConvert() override = default; std::size_t get_width() const override { return source_.get_width(); } std::size_t get_height() const override { return source_.get_height(); } PixelFormat get_format() const override { return dst_format_; } bool eof() const override { return source_.eof(); } bool get_next_row_data(std::uint8_t* out_data) override; private: ImagePipelineNode& source_; PixelFormat dst_format_; std::vector buffer_; }; // A pipeline node that handles data that comes out of segmented sensors. Note that the width of // the output data does not necessarily match the input data width, because in many cases almost // all width of the image needs to be read in order to desegment it. class ImagePipelineNodeDesegment : public ImagePipelineNode { public: ImagePipelineNodeDesegment(ImagePipelineNode& source, std::size_t output_width, const std::vector& segment_order, std::size_t segment_pixels, std::size_t interleaved_lines, std::size_t pixels_per_chunk); ImagePipelineNodeDesegment(ImagePipelineNode& source, std::size_t output_width, std::size_t segment_count, std::size_t segment_pixels, std::size_t interleaved_lines, std::size_t pixels_per_chunk); ~ImagePipelineNodeDesegment() override = default; std::size_t get_width() const override { return output_width_; } std::size_t get_height() const override { return source_.get_height() / interleaved_lines_; } PixelFormat get_format() const override { return source_.get_format(); } bool eof() const override { return source_.eof(); } bool get_next_row_data(std::uint8_t* out_data) override; private: ImagePipelineNode& source_; std::size_t output_width_; std::vector segment_order_; std::size_t segment_pixels_ = 0; std::size_t interleaved_lines_ = 0; std::size_t pixels_per_chunk_ = 0; RowBuffer buffer_; }; // A pipeline node that deinterleaves data on multiple lines class ImagePipelineNodeDeinterleaveLines : public ImagePipelineNodeDesegment { public: ImagePipelineNodeDeinterleaveLines(ImagePipelineNode& source, std::size_t interleaved_lines, std::size_t pixels_per_chunk); }; // A pipeline that swaps bytes in 16-bit components and does nothing otherwise. class ImagePipelineNodeSwap16BitEndian : public ImagePipelineNode { public: ImagePipelineNodeSwap16BitEndian(ImagePipelineNode& source); std::size_t get_width() const override { return source_.get_width(); } std::size_t get_height() const override { return source_.get_height(); } PixelFormat get_format() const override { return source_.get_format(); } bool eof() const override { return source_.eof(); } bool get_next_row_data(std::uint8_t* out_data) override; private: ImagePipelineNode& source_; bool needs_swapping_ = false; }; class ImagePipelineNodeInvert : public ImagePipelineNode { public: ImagePipelineNodeInvert(ImagePipelineNode& source); std::size_t get_width() const override { return source_.get_width(); } std::size_t get_height() const override { return source_.get_height(); } PixelFormat get_format() const override { return source_.get_format(); } bool eof() const override { return source_.eof(); } bool get_next_row_data(std::uint8_t* out_data) override; private: ImagePipelineNode& source_; }; // A pipeline node that merges 3 mono lines into a color channel class ImagePipelineNodeMergeMonoLinesToColor : public ImagePipelineNode { public: ImagePipelineNodeMergeMonoLinesToColor(ImagePipelineNode& source, ColorOrder color_order); std::size_t get_width() const override { return source_.get_width(); } std::size_t get_height() const override { return source_.get_height() / 3; } PixelFormat get_format() const override { return output_format_; } bool eof() const override { return source_.eof(); } bool get_next_row_data(std::uint8_t* out_data) override; private: static PixelFormat get_output_format(PixelFormat input_format, ColorOrder order); ImagePipelineNode& source_; PixelFormat output_format_ = PixelFormat::UNKNOWN; RowBuffer buffer_; }; // A pipeline node that splits a color channel into 3 mono lines class ImagePipelineNodeSplitMonoLines : public ImagePipelineNode { public: ImagePipelineNodeSplitMonoLines(ImagePipelineNode& source); std::size_t get_width() const override { return source_.get_width(); } std::size_t get_height() const override { return source_.get_height() * 3; } PixelFormat get_format() const override { return output_format_; } bool eof() const override { return source_.eof(); } bool get_next_row_data(std::uint8_t* out_data) override; private: static PixelFormat get_output_format(PixelFormat input_format); ImagePipelineNode& source_; PixelFormat output_format_ = PixelFormat::UNKNOWN; std::vector buffer_; unsigned next_channel_ = 0; }; // A pipeline node that merges 3 mono lines into a gray channel class ImagePipelineNodeMergeColorToGray : public ImagePipelineNode { public: ImagePipelineNodeMergeColorToGray(ImagePipelineNode& source); std::size_t get_width() const override { return source_.get_width(); } std::size_t get_height() const override { return source_.get_height(); } PixelFormat get_format() const override { return output_format_; } bool eof() const override { return source_.eof(); } bool get_next_row_data(std::uint8_t* out_data) override; private: static PixelFormat get_output_format(PixelFormat input_format); ImagePipelineNode& source_; PixelFormat output_format_ = PixelFormat::UNKNOWN; float ch0_mult_ = 0; float ch1_mult_ = 0; float ch2_mult_ = 0; std::vector temp_buffer_; }; // A pipeline node that shifts colors across lines by the given offsets class ImagePipelineNodeComponentShiftLines : public ImagePipelineNode { public: ImagePipelineNodeComponentShiftLines(ImagePipelineNode& source, unsigned shift_r, unsigned shift_g, unsigned shift_b); std::size_t get_width() const override { return source_.get_width(); } std::size_t get_height() const override { return height_; } PixelFormat get_format() const override { return source_.get_format(); } bool eof() const override { return source_.eof(); } bool get_next_row_data(std::uint8_t* out_data) override; private: ImagePipelineNode& source_; std::size_t extra_height_ = 0; std::size_t height_ = 0; std::array channel_shifts_; RowBuffer buffer_; }; // A pipeline node that shifts pixels across lines by the given offsets (performs vertical // unstaggering) class ImagePipelineNodePixelShiftLines : public ImagePipelineNode { public: ImagePipelineNodePixelShiftLines(ImagePipelineNode& source, const std::vector& shifts); std::size_t get_width() const override { return source_.get_width(); } std::size_t get_height() const override { return height_; } PixelFormat get_format() const override { return source_.get_format(); } bool eof() const override { return source_.eof(); } bool get_next_row_data(std::uint8_t* out_data) override; private: ImagePipelineNode& source_; std::size_t extra_height_ = 0; std::size_t height_ = 0; std::vector pixel_shifts_; RowBuffer buffer_; }; // A pipeline node that shifts pixels across columns by the given offsets. Each row is divided // into pixel groups of shifts.size() pixels. For each output group starting at position xgroup, // the i-th pixel will be set to the input pixel at position xgroup + shifts[i]. class ImagePipelineNodePixelShiftColumns : public ImagePipelineNode { public: ImagePipelineNodePixelShiftColumns(ImagePipelineNode& source, const std::vector& shifts); std::size_t get_width() const override { return width_; } std::size_t get_height() const override { return source_.get_height(); } PixelFormat get_format() const override { return source_.get_format(); } bool eof() const override { return source_.eof(); } bool get_next_row_data(std::uint8_t* out_data) override; private: ImagePipelineNode& source_; std::size_t width_ = 0; std::size_t extra_width_ = 0; std::vector pixel_shifts_; std::vector temp_buffer_; }; // exposed for tests std::size_t compute_pixel_shift_extra_width(std::size_t source_width, const std::vector& shifts); // A pipeline node that extracts a sub-image from the image. Padding and cropping is done as needed. // The class can't pad to the left of the image currently, as only positive offsets are accepted. class ImagePipelineNodeExtract : public ImagePipelineNode { public: ImagePipelineNodeExtract(ImagePipelineNode& source, std::size_t offset_x, std::size_t offset_y, std::size_t width, std::size_t height); ~ImagePipelineNodeExtract() override; std::size_t get_width() const override { return width_; } std::size_t get_height() const override { return height_; } PixelFormat get_format() const override { return source_.get_format(); } bool eof() const override { return source_.eof(); } bool get_next_row_data(std::uint8_t* out_data) override; private: ImagePipelineNode& source_; std::size_t offset_x_ = 0; std::size_t offset_y_ = 0; std::size_t width_ = 0; std::size_t height_ = 0; std::size_t current_line_ = 0; std::vector cached_line_; }; // A pipeline node that scales rows to the specified width by using a point filter class ImagePipelineNodeScaleRows : public ImagePipelineNode { public: ImagePipelineNodeScaleRows(ImagePipelineNode& source, std::size_t width); std::size_t get_width() const override { return width_; } std::size_t get_height() const override { return source_.get_height(); } PixelFormat get_format() const override { return source_.get_format(); } bool eof() const override { return source_.eof(); } bool get_next_row_data(std::uint8_t* out_data) override; private: ImagePipelineNode& source_; std::size_t width_ = 0; std::vector cached_line_; }; // A pipeline node that mimics the calibration behavior on Genesys chips class ImagePipelineNodeCalibrate : public ImagePipelineNode { public: ImagePipelineNodeCalibrate(ImagePipelineNode& source, const std::vector& bottom, const std::vector& top, std::size_t x_start); std::size_t get_width() const override { return source_.get_width(); } std::size_t get_height() const override { return source_.get_height(); } PixelFormat get_format() const override { return source_.get_format(); } bool eof() const override { return source_.eof(); } bool get_next_row_data(std::uint8_t* out_data) override; private: ImagePipelineNode& source_; std::vector offset_; std::vector multiplier_; }; class ImagePipelineNodeDebug : public ImagePipelineNode { public: ImagePipelineNodeDebug(ImagePipelineNode& source, const std::string& path); ~ImagePipelineNodeDebug() override; std::size_t get_width() const override { return source_.get_width(); } std::size_t get_height() const override { return source_.get_height(); } PixelFormat get_format() const override { return source_.get_format(); } bool eof() const override { return source_.eof(); } bool get_next_row_data(std::uint8_t* out_data) override; private: ImagePipelineNode& source_; std::string path_; RowBuffer buffer_; }; class ImagePipelineStack { public: ImagePipelineStack() {} ImagePipelineStack(ImagePipelineStack&& other) { clear(); nodes_ = std::move(other.nodes_); } ImagePipelineStack& operator=(ImagePipelineStack&& other) { clear(); nodes_ = std::move(other.nodes_); return *this; } ~ImagePipelineStack() { clear(); } std::size_t get_input_width() const; std::size_t get_input_height() const; PixelFormat get_input_format() const; std::size_t get_input_row_bytes() const; std::size_t get_output_width() const; std::size_t get_output_height() const; PixelFormat get_output_format() const; std::size_t get_output_row_bytes() const; ImagePipelineNode& front() { return *(nodes_.front().get()); } bool eof() const { return nodes_.back()->eof(); } void clear(); template Node& push_first_node(Args&&... args) { if (!nodes_.empty()) { throw SaneException("Trying to append first node when there are existing nodes"); } nodes_.emplace_back(std::unique_ptr(new Node(std::forward(args)...))); return static_cast(*nodes_.back()); } template Node& push_node(Args&&... args) { ensure_node_exists(); nodes_.emplace_back(std::unique_ptr(new Node(*nodes_.back(), std::forward(args)...))); return static_cast(*nodes_.back()); } bool get_next_row_data(std::uint8_t* out_data) { return nodes_.back()->get_next_row_data(out_data); } std::vector get_all_data(); Image get_image(); private: void ensure_node_exists() const; std::vector> nodes_; }; } // namespace genesys #endif // ifndef BACKEND_GENESYS_IMAGE_PIPELINE_H backends-1.3.0/backend/genesys/image_pixel.cpp000066400000000000000000000455361456256263500213530ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "image.h" #include namespace genesys { struct PixelFormatDesc { PixelFormat format; unsigned depth; unsigned channels; ColorOrder order; }; const PixelFormatDesc s_known_pixel_formats[] = { { PixelFormat::I1, 1, 1, ColorOrder::RGB }, { PixelFormat::I8, 8, 1, ColorOrder::RGB }, { PixelFormat::I16, 16, 1, ColorOrder::RGB }, { PixelFormat::RGB111, 1, 3, ColorOrder::RGB }, { PixelFormat::RGB888, 8, 3, ColorOrder::RGB }, { PixelFormat::RGB161616, 16, 3, ColorOrder::RGB }, { PixelFormat::BGR888, 8, 3, ColorOrder::BGR }, { PixelFormat::BGR161616, 16, 3, ColorOrder::BGR }, }; ColorOrder get_pixel_format_color_order(PixelFormat format) { for (const auto& desc : s_known_pixel_formats) { if (desc.format == format) return desc.order; } throw SaneException("Unknown pixel format %d", static_cast(format)); } unsigned get_pixel_format_depth(PixelFormat format) { for (const auto& desc : s_known_pixel_formats) { if (desc.format == format) return desc.depth; } throw SaneException("Unknown pixel format %d", static_cast(format)); } unsigned get_pixel_channels(PixelFormat format) { for (const auto& desc : s_known_pixel_formats) { if (desc.format == format) return desc.channels; } throw SaneException("Unknown pixel format %d", static_cast(format)); } std::size_t get_pixel_row_bytes(PixelFormat format, std::size_t width) { std::size_t depth = get_pixel_format_depth(format) * get_pixel_channels(format); std::size_t total_bits = depth * width; return total_bits / 8 + ((total_bits % 8 > 0) ? 1 : 0); } std::size_t get_pixels_from_row_bytes(PixelFormat format, std::size_t row_bytes) { std::size_t depth = get_pixel_format_depth(format) * get_pixel_channels(format); return (row_bytes * 8) / depth; } PixelFormat create_pixel_format(unsigned depth, unsigned channels, ColorOrder order) { for (const auto& desc : s_known_pixel_formats) { if (desc.depth == depth && desc.channels == channels && desc.order == order) { return desc.format; } } throw SaneException("Unknown pixel format %d %d %d", depth, channels, static_cast(order)); } static inline unsigned read_bit(const std::uint8_t* data, std::size_t x) { return (data[x / 8] >> (7 - (x % 8))) & 0x1; } static inline void write_bit(std::uint8_t* data, std::size_t x, unsigned value) { value = (value & 0x1) << (7 - (x % 8)); std::uint8_t mask = 0x1 << (7 - (x % 8)); data[x / 8] = (data[x / 8] & ~mask) | (value & mask); } Pixel get_pixel_from_row(const std::uint8_t* data, std::size_t x, PixelFormat format) { switch (format) { case PixelFormat::I1: { std::uint16_t val = read_bit(data, x) ? 0xffff : 0x0000; return Pixel(val, val, val); } case PixelFormat::RGB111: { x *= 3; std::uint16_t r = read_bit(data, x) ? 0xffff : 0x0000; std::uint16_t g = read_bit(data, x + 1) ? 0xffff : 0x0000; std::uint16_t b = read_bit(data, x + 2) ? 0xffff : 0x0000; return Pixel(r, g, b); } case PixelFormat::I8: { std::uint16_t val = std::uint16_t(data[x]) | (data[x] << 8); return Pixel(val, val, val); } case PixelFormat::I16: { x *= 2; std::uint16_t val = std::uint16_t(data[x]) | (data[x + 1] << 8); return Pixel(val, val, val); } case PixelFormat::RGB888: { x *= 3; std::uint16_t r = std::uint16_t(data[x]) | (data[x] << 8); std::uint16_t g = std::uint16_t(data[x + 1]) | (data[x + 1] << 8); std::uint16_t b = std::uint16_t(data[x + 2]) | (data[x + 2] << 8); return Pixel(r, g, b); } case PixelFormat::BGR888: { x *= 3; std::uint16_t b = std::uint16_t(data[x]) | (data[x] << 8); std::uint16_t g = std::uint16_t(data[x + 1]) | (data[x + 1] << 8); std::uint16_t r = std::uint16_t(data[x + 2]) | (data[x + 2] << 8); return Pixel(r, g, b); } case PixelFormat::RGB161616: { x *= 6; std::uint16_t r = std::uint16_t(data[x]) | (data[x + 1] << 8); std::uint16_t g = std::uint16_t(data[x + 2]) | (data[x + 3] << 8); std::uint16_t b = std::uint16_t(data[x + 4]) | (data[x + 5] << 8); return Pixel(r, g, b); } case PixelFormat::BGR161616: { x *= 6; std::uint16_t b = std::uint16_t(data[x]) | (data[x + 1] << 8); std::uint16_t g = std::uint16_t(data[x + 2]) | (data[x + 3] << 8); std::uint16_t r = std::uint16_t(data[x + 4]) | (data[x + 5] << 8); return Pixel(r, g, b); } default: throw SaneException("Unknown pixel format %d", static_cast(format)); } } void set_pixel_to_row(std::uint8_t* data, std::size_t x, Pixel pixel, PixelFormat format) { switch (format) { case PixelFormat::I1: write_bit(data, x, pixel.r & 0x8000 ? 1 : 0); return; case PixelFormat::RGB111: { x *= 3; write_bit(data, x, pixel.r & 0x8000 ? 1 : 0); write_bit(data, x + 1,pixel.g & 0x8000 ? 1 : 0); write_bit(data, x + 2, pixel.b & 0x8000 ? 1 : 0); return; } case PixelFormat::I8: { float val = (pixel.r >> 8) * 0.3f; val += (pixel.g >> 8) * 0.59f; val += (pixel.b >> 8) * 0.11f; data[x] = static_cast(val); return; } case PixelFormat::I16: { x *= 2; float val = pixel.r * 0.3f; val += pixel.g * 0.59f; val += pixel.b * 0.11f; auto val16 = static_cast(val); data[x] = val16 & 0xff; data[x + 1] = (val16 >> 8) & 0xff; return; } case PixelFormat::RGB888: { x *= 3; data[x] = pixel.r >> 8; data[x + 1] = pixel.g >> 8; data[x + 2] = pixel.b >> 8; return; } case PixelFormat::BGR888: { x *= 3; data[x] = pixel.b >> 8; data[x + 1] = pixel.g >> 8; data[x + 2] = pixel.r >> 8; return; } case PixelFormat::RGB161616: { x *= 6; data[x] = pixel.r & 0xff; data[x + 1] = (pixel.r >> 8) & 0xff; data[x + 2] = pixel.g & 0xff; data[x + 3] = (pixel.g >> 8) & 0xff; data[x + 4] = pixel.b & 0xff; data[x + 5] = (pixel.b >> 8) & 0xff; return; } case PixelFormat::BGR161616: x *= 6; data[x] = pixel.b & 0xff; data[x + 1] = (pixel.b >> 8) & 0xff; data[x + 2] = pixel.g & 0xff; data[x + 3] = (pixel.g >> 8) & 0xff; data[x + 4] = pixel.r & 0xff; data[x + 5] = (pixel.r >> 8) & 0xff; return; default: throw SaneException("Unknown pixel format %d", static_cast(format)); } } RawPixel get_raw_pixel_from_row(const std::uint8_t* data, std::size_t x, PixelFormat format) { switch (format) { case PixelFormat::I1: return RawPixel(read_bit(data, x)); case PixelFormat::RGB111: { x *= 3; return RawPixel(read_bit(data, x) << 2 | (read_bit(data, x + 1) << 1) | (read_bit(data, x + 2))); } case PixelFormat::I8: return RawPixel(data[x]); case PixelFormat::I16: { x *= 2; return RawPixel(data[x], data[x + 1]); } case PixelFormat::RGB888: case PixelFormat::BGR888: { x *= 3; return RawPixel(data[x], data[x + 1], data[x + 2]); } case PixelFormat::RGB161616: case PixelFormat::BGR161616: { x *= 6; return RawPixel(data[x], data[x + 1], data[x + 2], data[x + 3], data[x + 4], data[x + 5]); } default: throw SaneException("Unknown pixel format %d", static_cast(format)); } } void set_raw_pixel_to_row(std::uint8_t* data, std::size_t x, RawPixel pixel, PixelFormat format) { switch (format) { case PixelFormat::I1: write_bit(data, x, pixel.data[0] & 0x1); return; case PixelFormat::RGB111: { x *= 3; write_bit(data, x, (pixel.data[0] >> 2) & 0x1); write_bit(data, x + 1, (pixel.data[0] >> 1) & 0x1); write_bit(data, x + 2, (pixel.data[0]) & 0x1); return; } case PixelFormat::I8: data[x] = pixel.data[0]; return; case PixelFormat::I16: { x *= 2; data[x] = pixel.data[0]; data[x + 1] = pixel.data[1]; return; } case PixelFormat::RGB888: case PixelFormat::BGR888: { x *= 3; data[x] = pixel.data[0]; data[x + 1] = pixel.data[1]; data[x + 2] = pixel.data[2]; return; } case PixelFormat::RGB161616: case PixelFormat::BGR161616: { x *= 6; data[x] = pixel.data[0]; data[x + 1] = pixel.data[1]; data[x + 2] = pixel.data[2]; data[x + 3] = pixel.data[3]; data[x + 4] = pixel.data[4]; data[x + 5] = pixel.data[5]; return; } default: throw SaneException("Unknown pixel format %d", static_cast(format)); } } std::uint16_t get_raw_channel_from_row(const std::uint8_t* data, std::size_t x, unsigned channel, PixelFormat format) { switch (format) { case PixelFormat::I1: return read_bit(data, x); case PixelFormat::RGB111: return read_bit(data, x * 3 + channel); case PixelFormat::I8: return data[x]; case PixelFormat::I16: { x *= 2; return data[x] | (data[x + 1] << 8); } case PixelFormat::RGB888: case PixelFormat::BGR888: return data[x * 3 + channel]; case PixelFormat::RGB161616: case PixelFormat::BGR161616: return data[x * 6 + channel * 2] | (data[x * 6 + channel * 2 + 1]) << 8; default: throw SaneException("Unknown pixel format %d", static_cast(format)); } } void set_raw_channel_to_row(std::uint8_t* data, std::size_t x, unsigned channel, std::uint16_t pixel, PixelFormat format) { switch (format) { case PixelFormat::I1: write_bit(data, x, pixel & 0x1); return; case PixelFormat::RGB111: { write_bit(data, x * 3 + channel, pixel & 0x1); return; } case PixelFormat::I8: data[x] = pixel; return; case PixelFormat::I16: { x *= 2; data[x] = pixel; data[x + 1] = pixel >> 8; return; } case PixelFormat::RGB888: case PixelFormat::BGR888: { x *= 3; data[x + channel] = pixel; return; } case PixelFormat::RGB161616: case PixelFormat::BGR161616: { x *= 6; data[x + channel * 2] = pixel; data[x + channel * 2 + 1] = pixel >> 8; return; } default: throw SaneException("Unknown pixel format %d", static_cast(format)); } } template Pixel get_pixel_from_row(const std::uint8_t* data, std::size_t x) { return get_pixel_from_row(data, x, Format); } template void set_pixel_to_row(std::uint8_t* data, std::size_t x, Pixel pixel) { set_pixel_to_row(data, x, pixel, Format); } template RawPixel get_raw_pixel_from_row(const std::uint8_t* data, std::size_t x) { return get_raw_pixel_from_row(data, x, Format); } template void set_raw_pixel_to_row(std::uint8_t* data, std::size_t x, RawPixel pixel) { set_raw_pixel_to_row(data, x, pixel, Format); } template std::uint16_t get_raw_channel_from_row(const std::uint8_t* data, std::size_t x, unsigned channel) { return get_raw_channel_from_row(data, x, channel, Format); } template void set_raw_channel_to_row(std::uint8_t* data, std::size_t x, unsigned channel, std::uint16_t pixel) { set_raw_channel_to_row(data, x, channel, pixel, Format); } template Pixel get_pixel_from_row(const std::uint8_t* data, std::size_t x); template Pixel get_pixel_from_row(const std::uint8_t* data, std::size_t x); template Pixel get_pixel_from_row(const std::uint8_t* data, std::size_t x); template Pixel get_pixel_from_row(const std::uint8_t* data, std::size_t x); template Pixel get_pixel_from_row(const std::uint8_t* data, std::size_t x); template Pixel get_pixel_from_row(const std::uint8_t* data, std::size_t x); template Pixel get_pixel_from_row(const std::uint8_t* data, std::size_t x); template Pixel get_pixel_from_row(const std::uint8_t* data, std::size_t x); template RawPixel get_raw_pixel_from_row(const std::uint8_t* data, std::size_t x); template RawPixel get_raw_pixel_from_row(const std::uint8_t* data, std::size_t x); template RawPixel get_raw_pixel_from_row(const std::uint8_t* data, std::size_t x); template RawPixel get_raw_pixel_from_row(const std::uint8_t* data, std::size_t x); template RawPixel get_raw_pixel_from_row(const std::uint8_t* data, std::size_t x); template RawPixel get_raw_pixel_from_row(const std::uint8_t* data, std::size_t x); template RawPixel get_raw_pixel_from_row(const std::uint8_t* data, std::size_t x); template RawPixel get_raw_pixel_from_row(const std::uint8_t* data, std::size_t x); template std::uint16_t get_raw_channel_from_row( const std::uint8_t* data, std::size_t x, unsigned channel); template std::uint16_t get_raw_channel_from_row( const std::uint8_t* data, std::size_t x, unsigned channel); template std::uint16_t get_raw_channel_from_row( const std::uint8_t* data, std::size_t x, unsigned channel); template std::uint16_t get_raw_channel_from_row( const std::uint8_t* data, std::size_t x, unsigned channel); template std::uint16_t get_raw_channel_from_row( const std::uint8_t* data, std::size_t x, unsigned channel); template std::uint16_t get_raw_channel_from_row( const std::uint8_t* data, std::size_t x, unsigned channel); template std::uint16_t get_raw_channel_from_row( const std::uint8_t* data, std::size_t x, unsigned channel); template std::uint16_t get_raw_channel_from_row (const std::uint8_t* data, std::size_t x, unsigned channel); template void set_pixel_to_row(std::uint8_t* data, std::size_t x, Pixel pixel); template void set_pixel_to_row(std::uint8_t* data, std::size_t x, Pixel pixel); template void set_pixel_to_row(std::uint8_t* data, std::size_t x, Pixel pixel); template void set_pixel_to_row(std::uint8_t* data, std::size_t x, Pixel pixel); template void set_pixel_to_row(std::uint8_t* data, std::size_t x, Pixel pixel); template void set_pixel_to_row(std::uint8_t* data, std::size_t x, Pixel pixel); template void set_pixel_to_row(std::uint8_t* data, std::size_t x, Pixel pixel); template void set_pixel_to_row(std::uint8_t* data, std::size_t x, Pixel pixel); template void set_raw_pixel_to_row(std::uint8_t* data, std::size_t x, RawPixel pixel); template void set_raw_pixel_to_row(std::uint8_t* data, std::size_t x, RawPixel pixel); template void set_raw_pixel_to_row(std::uint8_t* data, std::size_t x, RawPixel pixel); template void set_raw_pixel_to_row(std::uint8_t* data, std::size_t x, RawPixel pixel); template void set_raw_pixel_to_row(std::uint8_t* data, std::size_t x, RawPixel pixel); template void set_raw_pixel_to_row(std::uint8_t* data, std::size_t x, RawPixel pixel); template void set_raw_pixel_to_row(std::uint8_t* data, std::size_t x, RawPixel pixel); template void set_raw_pixel_to_row(std::uint8_t* data, std::size_t x, RawPixel pixel); template void set_raw_channel_to_row( std::uint8_t* data, std::size_t x, unsigned channel, std::uint16_t pixel); template void set_raw_channel_to_row( std::uint8_t* data, std::size_t x, unsigned channel, std::uint16_t pixel); template void set_raw_channel_to_row( std::uint8_t* data, std::size_t x, unsigned channel, std::uint16_t pixel); template void set_raw_channel_to_row( std::uint8_t* data, std::size_t x, unsigned channel, std::uint16_t pixel); template void set_raw_channel_to_row( std::uint8_t* data, std::size_t x, unsigned channel, std::uint16_t pixel); template void set_raw_channel_to_row( std::uint8_t* data, std::size_t x, unsigned channel, std::uint16_t pixel); template void set_raw_channel_to_row( std::uint8_t* data, std::size_t x, unsigned channel, std::uint16_t pixel); template void set_raw_channel_to_row( std::uint8_t* data, std::size_t x, unsigned channel, std::uint16_t pixel); } // namespace genesys backends-1.3.0/backend/genesys/image_pixel.h000066400000000000000000000105631456256263500210100ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_IMAGE_PIXEL_H #define BACKEND_GENESYS_IMAGE_PIXEL_H #include "enums.h" #include #include #include namespace genesys { // 16-bit values are in host endian enum class PixelFormat { UNKNOWN, I1, RGB111, I8, RGB888, BGR888, I16, RGB161616, BGR161616, }; struct Pixel { Pixel() = default; Pixel(std::uint16_t red, std::uint16_t green, std::uint16_t blue) : r{red}, g{green}, b{blue} {} std::uint16_t r = 0; std::uint16_t g = 0; std::uint16_t b = 0; bool operator==(const Pixel& other) const { return r == other.r && g == other.g && b == other.b; } }; struct RawPixel { RawPixel() = default; RawPixel(std::uint8_t d0) : data{d0, 0, 0, 0, 0, 0} {} RawPixel(std::uint8_t d0, std::uint8_t d1) : data{d0, d1, 0, 0, 0, 0} {} RawPixel(std::uint8_t d0, std::uint8_t d1, std::uint8_t d2) : data{d0, d1, d2, 0, 0, 0} {} RawPixel(std::uint8_t d0, std::uint8_t d1, std::uint8_t d2, std::uint8_t d3, std::uint8_t d4, std::uint8_t d5) : data{d0, d1, d2, d3, d4, d5} {} std::uint8_t data[6] = {}; bool operator==(const RawPixel& other) const { return std::equal(std::begin(data), std::end(data), std::begin(other.data)); } }; ColorOrder get_pixel_format_color_order(PixelFormat format); unsigned get_pixel_format_depth(PixelFormat format); unsigned get_pixel_channels(PixelFormat format); std::size_t get_pixel_row_bytes(PixelFormat format, std::size_t width); std::size_t get_pixels_from_row_bytes(PixelFormat format, std::size_t row_bytes); PixelFormat create_pixel_format(unsigned depth, unsigned channels, ColorOrder order); // retrieves or sets the logical pixel values in 16-bit range. Pixel get_pixel_from_row(const std::uint8_t* data, std::size_t x, PixelFormat format); void set_pixel_to_row(std::uint8_t* data, std::size_t x, Pixel pixel, PixelFormat format); // retrieves or sets the physical pixel values. The low bytes of the RawPixel are interpreted as // the retrieved values / values to set RawPixel get_raw_pixel_from_row(const std::uint8_t* data, std::size_t x, PixelFormat format); void set_raw_pixel_to_row(std::uint8_t* data, std::size_t x, RawPixel pixel, PixelFormat format); // retrieves or sets the physical value of specific channel of the pixel. The channels are numbered // in the same order as the pixel is laid out in memory, that is, whichever channel comes first // has the index 0. E.g. 0-th channel in RGB888 is the red byte, but in BGR888 is the blue byte. std::uint16_t get_raw_channel_from_row(const std::uint8_t* data, std::size_t x, unsigned channel, PixelFormat format); void set_raw_channel_to_row(std::uint8_t* data, std::size_t x, unsigned channel, std::uint16_t pixel, PixelFormat format); template Pixel get_pixel_from_row(const std::uint8_t* data, std::size_t x); template void set_pixel_to_row(std::uint8_t* data, std::size_t x, RawPixel pixel); template Pixel get_raw_pixel_from_row(const std::uint8_t* data, std::size_t x); template void set_raw_pixel_to_row(std::uint8_t* data, std::size_t x, RawPixel pixel); template std::uint16_t get_raw_channel_from_row(const std::uint8_t* data, std::size_t x, unsigned channel); template void set_raw_channel_to_row(std::uint8_t* data, std::size_t x, unsigned channel, std::uint16_t pixel); } // namespace genesys #endif // BACKEND_GENESYS_IMAGE_PIXEL_H backends-1.3.0/backend/genesys/low.cpp000066400000000000000000002157061456256263500176670ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2010-2013 Stéphane Voltz 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, see . */ #define DEBUG_DECLARE_ONLY #include "low.h" #include "assert.h" #include "test_settings.h" #include "gl124_registers.h" #include "gl646_registers.h" #include "gl841_registers.h" #include "gl842_registers.h" #include "gl843_registers.h" #include "gl846_registers.h" #include "gl847_registers.h" #include "gl646_registers.h" #include "gl124.h" #include "gl646.h" #include "gl841.h" #include "gl842.h" #include "gl843.h" #include "gl846.h" #include "gl847.h" #include "gl646.h" #include #include #include #include /* ------------------------------------------------------------------------ */ /* functions calling ASIC specific functions */ /* ------------------------------------------------------------------------ */ namespace genesys { std::unique_ptr create_cmd_set(AsicType asic_type) { switch (asic_type) { case AsicType::GL646: return std::unique_ptr(new gl646::CommandSetGl646{}); case AsicType::GL841: return std::unique_ptr(new gl841::CommandSetGl841{}); case AsicType::GL842: return std::unique_ptr(new gl842::CommandSetGl842{}); case AsicType::GL843: return std::unique_ptr(new gl843::CommandSetGl843{}); case AsicType::GL845: // since only a few reg bits differs we handle both together case AsicType::GL846: return std::unique_ptr(new gl846::CommandSetGl846{}); case AsicType::GL847: return std::unique_ptr(new gl847::CommandSetGl847{}); case AsicType::GL124: return std::unique_ptr(new gl124::CommandSetGl124{}); default: throw SaneException(SANE_STATUS_INVAL, "unknown ASIC type"); } } /* ------------------------------------------------------------------------ */ /* General IO and debugging functions */ /* ------------------------------------------------------------------------ */ void sanei_genesys_write_file(const char* filename, const std::uint8_t* data, std::size_t length) { DBG_HELPER(dbg); std::FILE* out = std::fopen(filename, "w"); if (!out) { throw SaneException("could not open %s for writing: %s", filename, strerror(errno)); } std::fwrite(data, 1, length, out); std::fclose(out); } /* ------------------------------------------------------------------------ */ /* Read and write RAM, registers and AFE */ /* ------------------------------------------------------------------------ */ unsigned sanei_genesys_get_bulk_max_size(AsicType asic_type) { /* Genesys supports 0xFE00 maximum size in general, wheraus GL646 supports 0xFFC0. We use 0xF000 because that's the packet limit in the Linux usbmon USB capture stack. By default it limits packet size to b_size / 5 where b_size is the size of the ring buffer. By default it's 300*1024, so the packet is limited 61440 without any visibility to acquiring software. */ if (asic_type == AsicType::GL124 || asic_type == AsicType::GL846 || asic_type == AsicType::GL847) { return 0xeff0; } return 0xf000; } // Set address for writing data void sanei_genesys_set_buffer_address(Genesys_Device* dev, std::uint32_t addr) { DBG_HELPER(dbg); if (dev->model->asic_type==AsicType::GL847 || dev->model->asic_type==AsicType::GL845 || dev->model->asic_type==AsicType::GL846 || dev->model->asic_type==AsicType::GL124) { DBG(DBG_warn, "%s: shouldn't be used for GL846+ ASICs\n", __func__); return; } DBG(DBG_io, "%s: setting address to 0x%05x\n", __func__, addr & 0xfffffff0); addr = addr >> 4; dev->interface->write_register(0x2b, (addr & 0xff)); addr = addr >> 8; dev->interface->write_register(0x2a, (addr & 0xff)); } /* ------------------------------------------------------------------------ */ /* Medium level functions */ /* ------------------------------------------------------------------------ */ Status scanner_read_status(Genesys_Device& dev) { DBG_HELPER(dbg); std::uint16_t address = 0; switch (dev.model->asic_type) { case AsicType::GL124: address = 0x101; break; case AsicType::GL646: case AsicType::GL841: case AsicType::GL842: case AsicType::GL843: case AsicType::GL845: case AsicType::GL846: case AsicType::GL847: address = 0x41; break; default: throw SaneException("Unsupported asic type"); } // same for all chips constexpr std::uint8_t PWRBIT = 0x80; constexpr std::uint8_t BUFEMPTY = 0x40; constexpr std::uint8_t FEEDFSH = 0x20; constexpr std::uint8_t SCANFSH = 0x10; constexpr std::uint8_t HOMESNR = 0x08; constexpr std::uint8_t LAMPSTS = 0x04; constexpr std::uint8_t FEBUSY = 0x02; constexpr std::uint8_t MOTORENB = 0x01; auto value = dev.interface->read_register(address); Status status; status.is_replugged = !(value & PWRBIT); status.is_buffer_empty = value & BUFEMPTY; status.is_feeding_finished = value & FEEDFSH; status.is_scanning_finished = value & SCANFSH; status.is_at_home = value & HOMESNR; status.is_lamp_on = value & LAMPSTS; status.is_front_end_busy = value & FEBUSY; status.is_motor_enabled = value & MOTORENB; if (DBG_LEVEL >= DBG_io) { debug_print_status(dbg, status); } return status; } Status scanner_read_reliable_status(Genesys_Device& dev) { DBG_HELPER(dbg); scanner_read_status(dev); dev.interface->sleep_ms(100); return scanner_read_status(dev); } void scanner_read_print_status(Genesys_Device& dev) { scanner_read_status(dev); } /** * decodes and prints content of status register * @param val value read from status register */ void debug_print_status(DebugMessageHelper& dbg, Status val) { std::stringstream str; str << val; dbg.vlog(DBG_info, "status=%s\n", str.str().c_str()); } void scanner_register_rw_clear_bits(Genesys_Device& dev, std::uint16_t address, std::uint8_t mask) { scanner_register_rw_bits(dev, address, 0x00, mask); } void scanner_register_rw_set_bits(Genesys_Device& dev, std::uint16_t address, std::uint8_t mask) { scanner_register_rw_bits(dev, address, mask, mask); } void scanner_register_rw_bits(Genesys_Device& dev, std::uint16_t address, std::uint8_t value, std::uint8_t mask) { auto reg_value = dev.interface->read_register(address); reg_value = (reg_value & ~mask) | (value & mask); dev.interface->write_register(address, reg_value); } /** read the number of valid words in scanner's RAM * ie registers 42-43-44 */ // candidate for moving into chip specific files? void sanei_genesys_read_valid_words(Genesys_Device* dev, unsigned int* words) { DBG_HELPER(dbg); switch (dev->model->asic_type) { case AsicType::GL124: *words = dev->interface->read_register(0x102) & 0x03; *words = *words * 256 + dev->interface->read_register(0x103); *words = *words * 256 + dev->interface->read_register(0x104); *words = *words * 256 + dev->interface->read_register(0x105); break; case AsicType::GL845: case AsicType::GL846: *words = dev->interface->read_register(0x42) & 0x02; *words = *words * 256 + dev->interface->read_register(0x43); *words = *words * 256 + dev->interface->read_register(0x44); *words = *words * 256 + dev->interface->read_register(0x45); break; case AsicType::GL847: *words = dev->interface->read_register(0x42) & 0x03; *words = *words * 256 + dev->interface->read_register(0x43); *words = *words * 256 + dev->interface->read_register(0x44); *words = *words * 256 + dev->interface->read_register(0x45); break; default: *words = dev->interface->read_register(0x44); *words += dev->interface->read_register(0x43) * 256; if (dev->model->asic_type == AsicType::GL646) { *words += ((dev->interface->read_register(0x42) & 0x03) * 256 * 256); } else { *words += ((dev->interface->read_register(0x42) & 0x0f) * 256 * 256); } } DBG(DBG_proc, "%s: %d words\n", __func__, *words); } /** read the number of lines scanned * ie registers 4b-4c-4d */ void sanei_genesys_read_scancnt(Genesys_Device* dev, unsigned int* words) { DBG_HELPER(dbg); if (dev->model->asic_type == AsicType::GL124) { *words = (dev->interface->read_register(0x10b) & 0x0f) << 16; *words += (dev->interface->read_register(0x10c) << 8); *words += dev->interface->read_register(0x10d); } else { *words = dev->interface->read_register(0x4d); *words += dev->interface->read_register(0x4c) * 256; if (dev->model->asic_type == AsicType::GL646) { *words += ((dev->interface->read_register(0x4b) & 0x03) * 256 * 256); } else { *words += ((dev->interface->read_register(0x4b) & 0x0f) * 256 * 256); } } DBG(DBG_proc, "%s: %d lines\n", __func__, *words); } /** @brief Check if the scanner's internal data buffer is empty * @param *dev device to test for data * @param *empty return value * @return empty will be set to true if there is no scanned data. **/ bool sanei_genesys_is_buffer_empty(Genesys_Device* dev) { DBG_HELPER(dbg); dev->interface->sleep_ms(1); auto status = scanner_read_status(*dev); if (status.is_buffer_empty) { /* fix timing issue on USB3 (or just may be too fast) hardware * spotted by John S. Weber */ dev->interface->sleep_ms(1); DBG(DBG_io2, "%s: buffer is empty\n", __func__); return true; } DBG(DBG_io, "%s: buffer is filled\n", __func__); return false; } void wait_until_buffer_non_empty(Genesys_Device* dev, bool check_status_twice) { // FIXME: reduce MAX_RETRIES once tests are updated const unsigned MAX_RETRIES = 100000; for (unsigned i = 0; i < MAX_RETRIES; ++i) { if (check_status_twice) { // FIXME: this only to preserve previous behavior, can be removed scanner_read_status(*dev); } bool empty = sanei_genesys_is_buffer_empty(dev); dev->interface->sleep_ms(10); if (!empty) return; } throw SaneException(SANE_STATUS_IO_ERROR, "failed to read data"); } void wait_until_has_valid_words(Genesys_Device* dev) { unsigned words = 0; unsigned sleep_time_ms = 10; for (unsigned wait_ms = 0; wait_ms < 70000; wait_ms += sleep_time_ms) { sanei_genesys_read_valid_words(dev, &words); if (words != 0) break; dev->interface->sleep_ms(sleep_time_ms); } if (words == 0) { throw SaneException(SANE_STATUS_IO_ERROR, "timeout, buffer does not get filled"); } } // Read data (e.g scanned image) from scan buffer void sanei_genesys_read_data_from_scanner(Genesys_Device* dev, std::uint8_t* data, size_t size) { DBG_HELPER_ARGS(dbg, "size = %zu bytes", size); if (size & 1) DBG(DBG_info, "WARNING %s: odd number of bytes\n", __func__); wait_until_has_valid_words(dev); dev->interface->bulk_read_data(0x45, data, size); } Image read_unshuffled_image_from_scanner(Genesys_Device* dev, const ScanSession& session, std::size_t total_bytes) { DBG_HELPER(dbg); auto format = create_pixel_format(session.params.depth, dev->model->is_cis ? 1 : session.params.channels, dev->model->line_mode_color_order); auto width = get_pixels_from_row_bytes(format, session.output_line_bytes_raw); auto height = session.optical_line_count; Image image(width, height, format); auto max_bytes = image.get_row_bytes() * height; if (total_bytes > max_bytes) { throw SaneException("Trying to read too much data %zu (max %zu)", total_bytes, max_bytes); } if (total_bytes != max_bytes) { DBG(DBG_info, "WARNING %s: trying to read not enough data (%zu, full fill %zu)\n", __func__, total_bytes, max_bytes); } sanei_genesys_read_data_from_scanner(dev, image.get_row_ptr(0), total_bytes); ImagePipelineStack pipeline; pipeline.push_first_node(image); if (session.segment_count > 1) { auto output_width = session.output_segment_pixel_group_count * session.segment_count; pipeline.push_node(output_width, dev->segment_order, session.conseq_pixel_dist, 1, 1); } if (session.params.depth == 16) { unsigned num_swaps = 0; if (has_flag(dev->model->flags, ModelFlag::SWAP_16BIT_DATA)) { num_swaps++; } #ifdef WORDS_BIGENDIAN num_swaps++; #endif if (num_swaps % 2 != 0) { dev->pipeline.push_node(); } } if (has_flag(dev->model->flags, ModelFlag::INVERT_PIXEL_DATA)) { pipeline.push_node(); } if (dev->model->is_cis && session.params.channels == 3) { pipeline.push_node(dev->model->line_mode_color_order); } if (session.use_host_side_gray) { pipeline.push_node(); } if (pipeline.get_output_format() == PixelFormat::BGR888) { pipeline.push_node(PixelFormat::RGB888); } if (pipeline.get_output_format() == PixelFormat::BGR161616) { pipeline.push_node(PixelFormat::RGB161616); } return pipeline.get_image(); } Image read_shuffled_image_from_scanner(Genesys_Device* dev, const ScanSession& session) { DBG_HELPER(dbg); std::size_t total_bytes = 0; std::size_t pixels_per_line = 0; if (dev->model->asic_type == AsicType::GL842 || dev->model->asic_type == AsicType::GL843 || dev->model->model_id == ModelId::CANON_5600F) { pixels_per_line = session.output_pixels; } else { // BUG: this selects incorrect pixel number pixels_per_line = session.params.pixels; } // FIXME: the current calculation is likely incorrect on non-GL843 implementations, // but this needs checking. Note the extra line when computing size. if (dev->model->asic_type == AsicType::GL842 || dev->model->asic_type == AsicType::GL843 || dev->model->model_id == ModelId::CANON_5600F) { total_bytes = session.output_total_bytes_raw; } else { total_bytes = session.params.channels * 2 * pixels_per_line * (session.params.lines + 1); } auto format = create_pixel_format(session.params.depth, dev->model->is_cis ? 1 : session.params.channels, dev->model->line_mode_color_order); // auto width = get_pixels_from_row_bytes(format, session.output_line_bytes_raw); auto width = pixels_per_line; auto height = session.params.lines + 1; // BUG: incorrect if (dev->model->asic_type == AsicType::GL842 || dev->model->asic_type == AsicType::GL843 || dev->model->model_id == ModelId::CANON_5600F) { height = session.optical_line_count; } Image image(width, height, format); auto max_bytes = image.get_row_bytes() * height; if (total_bytes > max_bytes) { throw SaneException("Trying to read too much data %zu (max %zu)", total_bytes, max_bytes); } if (total_bytes != max_bytes) { DBG(DBG_info, "WARNING %s: trying to read not enough data (%zu, full fill %zu)\n", __func__, total_bytes, max_bytes); } sanei_genesys_read_data_from_scanner(dev, image.get_row_ptr(0), total_bytes); ImagePipelineStack pipeline; pipeline.push_first_node(image); if (session.segment_count > 1) { auto output_width = session.output_segment_pixel_group_count * session.segment_count; pipeline.push_node(output_width, dev->segment_order, session.conseq_pixel_dist, 1, 1); } if (session.params.depth == 16) { unsigned num_swaps = 0; if (has_flag(dev->model->flags, ModelFlag::SWAP_16BIT_DATA)) { num_swaps++; } #ifdef WORDS_BIGENDIAN num_swaps++; #endif if (num_swaps % 2 != 0) { dev->pipeline.push_node(); } } if (has_flag(dev->model->flags, ModelFlag::INVERT_PIXEL_DATA)) { pipeline.push_node(); } if (dev->model->is_cis && session.params.channels == 3) { pipeline.push_node(dev->model->line_mode_color_order); } if (pipeline.get_output_format() == PixelFormat::BGR888) { pipeline.push_node(PixelFormat::RGB888); } if (pipeline.get_output_format() == PixelFormat::BGR161616) { pipeline.push_node(PixelFormat::RGB161616); } return pipeline.get_image(); } void sanei_genesys_read_feed_steps(Genesys_Device* dev, unsigned int* steps) { DBG_HELPER(dbg); if (dev->model->asic_type == AsicType::GL124) { *steps = (dev->interface->read_register(0x108) & 0x1f) << 16; *steps += (dev->interface->read_register(0x109) << 8); *steps += dev->interface->read_register(0x10a); } else { *steps = dev->interface->read_register(0x4a); *steps += dev->interface->read_register(0x49) * 256; if (dev->model->asic_type == AsicType::GL646) { *steps += ((dev->interface->read_register(0x48) & 0x03) * 256 * 256); } else if (dev->model->asic_type == AsicType::GL841) { *steps += ((dev->interface->read_register(0x48) & 0x0f) * 256 * 256); } else { *steps += ((dev->interface->read_register(0x48) & 0x1f) * 256 * 256); } } DBG(DBG_proc, "%s: %d steps\n", __func__, *steps); } void sanei_genesys_set_lamp_power(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs, bool set) { static const std::uint8_t REG_0x03_LAMPPWR = 0x10; if (set) { regs.find_reg(0x03).value |= REG_0x03_LAMPPWR; if (dev->model->asic_type == AsicType::GL841) { regs_set_exposure(dev->model->asic_type, regs, sanei_genesys_fixup_exposure(sensor.exposure)); regs.set8(0x19, 0x50); } if (dev->model->asic_type == AsicType::GL843) { regs_set_exposure(dev->model->asic_type, regs, sensor.exposure); } // we don't actually turn on lamp on infrared scan if ((dev->model->model_id == ModelId::CANON_8400F || dev->model->model_id == ModelId::CANON_8600F || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) && dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { regs.find_reg(0x03).value &= ~REG_0x03_LAMPPWR; } } else { regs.find_reg(0x03).value &= ~REG_0x03_LAMPPWR; if (dev->model->asic_type == AsicType::GL841) { regs_set_exposure(dev->model->asic_type, regs, sanei_genesys_fixup_exposure({0, 0, 0})); regs.set8(0x19, 0xff); } if (dev->model->model_id == ModelId::CANON_5600F) { regs_set_exposure(dev->model->asic_type, regs, sanei_genesys_fixup_exposure({0, 0, 0})); } } regs.state.is_lamp_on = set; } void sanei_genesys_set_motor_power(Genesys_Register_Set& regs, bool set) { static const std::uint8_t REG_0x02_MTRPWR = 0x10; if (set) { regs.find_reg(0x02).value |= REG_0x02_MTRPWR; } else { regs.find_reg(0x02).value &= ~REG_0x02_MTRPWR; } regs.state.is_motor_on = set; } bool should_enable_gamma(const ScanSession& session, const Genesys_Sensor& sensor) { if ((session.params.flags & ScanFlag::DISABLE_GAMMA) != ScanFlag::NONE) { return false; } if (session.params.depth == 16) { return false; } if (session.params.brightness_adjustment != 0 || session.params.contrast_adjustment != 0) { return true; } if (sensor.gamma[0] == 1.0f || sensor.gamma[1] == 1.0f || sensor.gamma[2] == 1.0f) { return false; } return true; } std::vector get_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor, int color) { if (!dev->gamma_override_tables[color].empty()) { return dev->gamma_override_tables[color]; } else { std::vector ret; sanei_genesys_create_default_gamma_table(dev, ret, sensor.gamma[color]); return ret; } } /** @brief generates gamma buffer to transfer * Generates gamma table buffer to send to ASIC. Applies * contrast and brightness if set. * @param dev device to set up * @param bits number of bits used by gamma * @param max value for gamma * @param size of the gamma table */ std::vector generate_gamma_buffer(Genesys_Device* dev, const Genesys_Sensor& sensor, int bits, int max, int size) { DBG_HELPER(dbg); // the gamma tables are 16 bits words and contain 3 channels std::vector gamma_buf(size * 2 * 3); std::vector rgamma = get_gamma_table(dev, sensor, GENESYS_RED); std::vector ggamma = get_gamma_table(dev, sensor, GENESYS_GREEN); std::vector bgamma = get_gamma_table(dev, sensor, GENESYS_BLUE); auto get_gamma_value = [](const std::vector& array, std::size_t index) -> std::uint16_t { if (index < array.size()) return array[index]; return 0xffff; }; auto set_gamma_buf_value = [](std::vector& array, std::size_t pos, std::uint16_t value) { array[pos * 2 + 0] = value & 0xff; array[pos * 2 + 1] = (value >> 8) & 0xff; }; if(dev->settings.contrast!=0 || dev->settings.brightness!=0) { std::vector lut(65536); sanei_genesys_load_lut(reinterpret_cast(lut.data()), bits, bits, 0, max, dev->settings.contrast, dev->settings.brightness); for (int i = 0; i < size; i++) { set_gamma_buf_value(gamma_buf, i + size * 0, lut[get_gamma_value(rgamma, i)]); set_gamma_buf_value(gamma_buf, i + size * 1, lut[get_gamma_value(ggamma, i)]); set_gamma_buf_value(gamma_buf, i + size * 2, lut[get_gamma_value(bgamma, i)]); } } else { for (int i = 0; i < size; i++) { set_gamma_buf_value(gamma_buf, i + size * 0, get_gamma_value(rgamma, i)); set_gamma_buf_value(gamma_buf, i + size * 1, get_gamma_value(ggamma, i)); set_gamma_buf_value(gamma_buf, i + size * 2, get_gamma_value(bgamma, i)); } } return gamma_buf; } /** @brief send gamma table to scanner * This function sends generic gamma table (ie ones built with * provided gamma) or the user defined one if provided by * fontend. Used by gl846+ ASICs * @param dev device to write to */ void sanei_genesys_send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) { DBG_HELPER(dbg); int size; int i; size = 256 + 1; auto gamma = generate_gamma_buffer(dev, sensor, 16, 65535, size); // loop sending gamma tables NOTE: 0x01000000 not 0x10000000 for (i = 0; i < 3; i++) { // clear corresponding GMM_N bit std::uint8_t val = dev->interface->read_register(0xbd); val &= ~(0x01 << i); dev->interface->write_register(0xbd, val); // clear corresponding GMM_F bit val = dev->interface->read_register(0xbe); val &= ~(0x01 << i); dev->interface->write_register(0xbe, val); // FIXME: currently the last word of each gamma table is not initialized, so to work around // unstable data, just set it to 0 which is the most likely value of uninitialized memory // (proper value is probably 0xff) gamma[size * 2 * i + size * 2 - 2] = 0; gamma[size * 2 * i + size * 2 - 1] = 0; /* set GMM_Z */ dev->interface->write_register(0xc5+2*i, gamma[size*2*i+1]); dev->interface->write_register(0xc6+2*i, gamma[size*2*i]); dev->interface->write_ahb(0x01000000 + 0x200 * i, (size-1) * 2, gamma.data() + i * size * 2+2); } } void compute_session_pixel_offsets(const Genesys_Device* dev, ScanSession& s, const Genesys_Sensor& sensor) { if (dev->model->asic_type == AsicType::GL646) { s.pixel_startx += s.output_startx * sensor.full_resolution / s.params.xres; s.pixel_endx = s.pixel_startx + s.optical_pixels * s.full_resolution / s.optical_resolution; } else if (dev->model->asic_type == AsicType::GL841 || dev->model->asic_type == AsicType::GL842 || dev->model->asic_type == AsicType::GL843 || dev->model->asic_type == AsicType::GL845 || dev->model->asic_type == AsicType::GL846 || dev->model->asic_type == AsicType::GL847) { unsigned startx_xres = s.optical_resolution; if (dev->model->model_id == ModelId::CANON_5600F || dev->model->model_id == ModelId::CANON_LIDE_90) { if (s.output_resolution == 1200) { startx_xres /= 2; } if (s.output_resolution >= 2400) { startx_xres /= 4; } } s.pixel_startx = (s.output_startx * startx_xres) / s.params.xres; s.pixel_endx = s.pixel_startx + s.optical_pixels_raw; } else if (dev->model->asic_type == AsicType::GL124) { s.pixel_startx = s.output_startx * sensor.full_resolution / s.params.xres; s.pixel_endx = s.pixel_startx + s.optical_pixels_raw; } // align pixels to correct boundary for unstaggering unsigned needed_x_alignment = std::max(s.stagger_x.size(), s.stagger_y.size()); unsigned aligned_pixel_startx = align_multiple_floor(s.pixel_startx, needed_x_alignment); s.pixel_endx -= s.pixel_startx - aligned_pixel_startx; s.pixel_startx = aligned_pixel_startx; s.pixel_startx = sensor.pixel_count_ratio.apply(s.pixel_startx); s.pixel_endx = sensor.pixel_count_ratio.apply(s.pixel_endx); if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) { s.pixel_startx = align_multiple_floor(s.pixel_startx, sensor.pixel_count_ratio.divisor()); s.pixel_endx = align_multiple_floor(s.pixel_endx, sensor.pixel_count_ratio.divisor()); } } unsigned session_adjust_output_pixels(unsigned output_pixels, const Genesys_Device& dev, const Genesys_Sensor& sensor, unsigned output_xresolution, unsigned output_yresolution, bool adjust_output_pixels) { bool adjust_optical_pixels = !adjust_output_pixels; if (dev.model->model_id == ModelId::CANON_5600F) { adjust_optical_pixels = true; adjust_output_pixels = true; } if (adjust_optical_pixels) { auto optical_resolution = sensor.get_optical_resolution(); // FIXME: better way would be to compute and return the required multiplier unsigned optical_pixels = (output_pixels * optical_resolution) / output_xresolution; if (dev.model->asic_type == AsicType::GL841 || dev.model->asic_type == AsicType::GL842) { optical_pixels = align_multiple_ceil(optical_pixels, 2); } if (dev.model->asic_type == AsicType::GL646 && output_xresolution == 400) { optical_pixels = align_multiple_floor(optical_pixels, 6); } if (dev.model->asic_type == AsicType::GL843) { // ensure the number of optical pixels is divisible by 2. // In quarter-CCD mode optical_pixels is 4x larger than the actual physical number optical_pixels = align_multiple_ceil(optical_pixels, 2 * sensor.full_resolution / optical_resolution); if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200 || dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I || dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I || dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) { optical_pixels = align_multiple_ceil(optical_pixels, 16); } } output_pixels = (optical_pixels * output_xresolution) / optical_resolution; } if (adjust_output_pixels) { // TODO: the following may no longer be needed but were applied historically. // we need an even pixels number // TODO invert test logic or generalize behaviour across all ASICs if (has_flag(dev.model->flags, ModelFlag::SIS_SENSOR) || dev.model->asic_type == AsicType::GL847 || dev.model->asic_type == AsicType::GL124 || dev.model->asic_type == AsicType::GL845 || dev.model->asic_type == AsicType::GL846 || dev.model->asic_type == AsicType::GL843) { if (output_xresolution <= 1200) { output_pixels = align_multiple_floor(output_pixels, 4); } else if (output_xresolution < output_yresolution) { // BUG: this is an artifact of the fact that the resolution was twice as large than // the actual resolution when scanning above the supported scanner X resolution output_pixels = align_multiple_floor(output_pixels, 8); } else { output_pixels = align_multiple_floor(output_pixels, 16); } } // corner case for true lineart for sensor with several segments or when xres is doubled // to match yres */ if (output_xresolution >= 1200 && ( dev.model->asic_type == AsicType::GL124 || dev.model->asic_type == AsicType::GL847 || dev.session.params.xres < dev.session.params.yres)) { if (output_xresolution < output_yresolution) { // FIXME: this is an artifact of the fact that the resolution was twice as large than // the actual resolution when scanning above the supported scanner X resolution output_pixels = align_multiple_floor(output_pixels, 8); } else { output_pixels = align_multiple_floor(output_pixels, 16); } } } return output_pixels; } void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Sensor& sensor) { DBG_HELPER(dbg); (void) dev; s.params.assert_valid(); if (s.params.depth != 8 && s.params.depth != 16) { throw SaneException("Unsupported depth setting %d", s.params.depth); } // compute optical and output resolutions s.full_resolution = sensor.full_resolution; s.optical_resolution = sensor.get_optical_resolution(); s.output_resolution = s.params.xres; s.pixel_count_ratio = sensor.pixel_count_ratio; if (s.output_resolution > s.optical_resolution) { throw std::runtime_error("output resolution higher than optical resolution"); } s.output_pixels = session_adjust_output_pixels(s.params.pixels, *dev, sensor, s.params.xres, s.params.yres, false); // Compute the number of optical pixels that will be acquired by the chip. // The necessary alignment requirements have already been computed by // get_session_output_pixels_multiplier s.optical_pixels = (s.output_pixels * s.optical_resolution) / s.output_resolution; if (static_cast(s.params.startx) + sensor.output_pixel_offset < 0) throw SaneException("Invalid sensor.output_pixel_offset"); s.output_startx = static_cast( static_cast(s.params.startx) + sensor.output_pixel_offset); if (has_flag(dev->model->flags, ModelFlag::HOST_SIDE_GRAY) && s.params.channels == 1 && s.params.color_filter == ColorFilter::NONE) { s.use_host_side_gray = true; s.params.channels = 3; s.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; } s.stagger_x = sensor.stagger_x; s.stagger_y = sensor.stagger_y; s.num_staggered_lines = 0; if (!has_flag(s.params.flags, ScanFlag::IGNORE_STAGGER_OFFSET)) { s.num_staggered_lines = s.stagger_y.max_shift() * s.params.yres / s.params.xres; } s.color_shift_lines_r = dev->model->ld_shift_r; s.color_shift_lines_g = dev->model->ld_shift_g; s.color_shift_lines_b = dev->model->ld_shift_b; if (dev->model->motor_id == MotorId::G4050 && s.params.yres > 600) { // it seems base_dpi of the G4050 motor is changed above 600 dpi s.color_shift_lines_r = (s.color_shift_lines_r * 3800) / dev->motor.base_ydpi; s.color_shift_lines_g = (s.color_shift_lines_g * 3800) / dev->motor.base_ydpi; s.color_shift_lines_b = (s.color_shift_lines_b * 3800) / dev->motor.base_ydpi; } s.color_shift_lines_r = (s.color_shift_lines_r * s.params.yres) / dev->motor.base_ydpi; s.color_shift_lines_g = (s.color_shift_lines_g * s.params.yres) / dev->motor.base_ydpi; s.color_shift_lines_b = (s.color_shift_lines_b * s.params.yres) / dev->motor.base_ydpi; s.max_color_shift_lines = 0; if (s.params.channels > 1 && !has_flag(s.params.flags, ScanFlag::IGNORE_COLOR_OFFSET)) { s.max_color_shift_lines = std::max(s.color_shift_lines_r, std::max(s.color_shift_lines_g, s.color_shift_lines_b)); } s.output_line_count = s.params.lines + s.max_color_shift_lines + s.num_staggered_lines; s.optical_line_count = dev->model->is_cis ? s.output_line_count * s.params.channels : s.output_line_count; s.output_channel_bytes = multiply_by_depth_ceil(s.output_pixels, s.params.depth); s.output_line_bytes = s.output_channel_bytes * s.params.channels; s.segment_count = sensor.get_segment_count(); s.optical_pixels_raw = s.optical_pixels; s.output_line_bytes_raw = s.output_line_bytes; s.conseq_pixel_dist = 0; // FIXME: Use ModelFlag::SIS_SENSOR if ((dev->model->asic_type == AsicType::GL845 || dev->model->asic_type == AsicType::GL846 || dev->model->asic_type == AsicType::GL847) && dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7400 && dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_8200I) { if (s.segment_count > 1) { s.conseq_pixel_dist = sensor.segment_size; // in case of multi-segments sensor, we have expand the scan area to sensor boundary if (dev->model->model_id == ModelId::CANON_5600F) { unsigned startx_xres = s.optical_resolution; if (dev->model->model_id == ModelId::CANON_5600F) { if (s.output_resolution == 1200) { startx_xres /= 2; } if (s.output_resolution >= 2400) { startx_xres /= 4; } } unsigned optical_startx = s.output_startx * startx_xres / s.params.xres; unsigned optical_endx = optical_startx + s.optical_pixels; unsigned multi_segment_size_output = s.segment_count * s.conseq_pixel_dist; unsigned multi_segment_size_optical = (multi_segment_size_output * s.optical_resolution) / s.output_resolution; optical_endx = align_multiple_ceil(optical_endx, multi_segment_size_optical); s.optical_pixels_raw = optical_endx - optical_startx; s.optical_pixels_raw = align_multiple_floor(s.optical_pixels_raw, 4 * s.optical_resolution / s.output_resolution); } else { // BUG: the following code will likely scan too much. Use the CANON_5600F approach unsigned extra_segment_scan_area = align_multiple_ceil(s.conseq_pixel_dist, 2); extra_segment_scan_area *= s.segment_count - 1; extra_segment_scan_area = s.pixel_count_ratio.apply_inverse(extra_segment_scan_area); s.optical_pixels_raw += extra_segment_scan_area; } } if (dev->model->model_id == ModelId::CANON_5600F) { auto output_pixels_raw = (s.optical_pixels_raw * s.output_resolution) / s.optical_resolution; auto output_channel_bytes_raw = multiply_by_depth_ceil(output_pixels_raw, s.params.depth); s.output_line_bytes_raw = output_channel_bytes_raw * s.params.channels; } else { s.output_line_bytes_raw = multiply_by_depth_ceil( (s.optical_pixels_raw * s.output_resolution) / sensor.full_resolution / s.segment_count, s.params.depth); } } if (dev->model->asic_type == AsicType::GL841 || dev->model->asic_type == AsicType::GL842) { if (dev->model->is_cis) { s.output_line_bytes_raw = s.output_channel_bytes; } } if (dev->model->asic_type == AsicType::GL124) { if (dev->model->is_cis) { s.output_line_bytes_raw = s.output_channel_bytes; } s.conseq_pixel_dist = s.output_pixels / (s.full_resolution / s.optical_resolution) / s.segment_count; } if (dev->model->asic_type == AsicType::GL842 || dev->model->asic_type == AsicType::GL843) { if (dev->model->is_cis) { if (s.segment_count > 1) { s.conseq_pixel_dist = sensor.segment_size; } } else { s.conseq_pixel_dist = s.output_pixels / s.segment_count; } } s.output_segment_pixel_group_count = 0; if (dev->model->asic_type == AsicType::GL124 || dev->model->asic_type == AsicType::GL842 || dev->model->asic_type == AsicType::GL843) { s.output_segment_pixel_group_count = s.output_pixels / (s.full_resolution / s.optical_resolution * s.segment_count); } if (dev->model->model_id == ModelId::CANON_LIDE_90) { s.output_segment_pixel_group_count = s.output_pixels / s.segment_count; } if (dev->model->asic_type == AsicType::GL845 || dev->model->asic_type == AsicType::GL846 || dev->model->asic_type == AsicType::GL847) { if (dev->model->model_id == ModelId::CANON_5600F) { s.output_segment_pixel_group_count = s.output_pixels / s.segment_count; } else { s.output_segment_pixel_group_count = s.pixel_count_ratio.apply(s.optical_pixels); } } s.output_line_bytes_requested = multiply_by_depth_ceil( s.params.get_requested_pixels() * s.params.channels, s.params.depth); s.output_total_bytes_raw = s.output_line_bytes_raw * s.output_line_count; s.output_total_bytes = s.output_line_bytes * s.output_line_count; if (dev->model->model_id == ModelId::CANON_LIDE_90) { s.output_total_bytes_raw *= s.params.channels; s.output_total_bytes *= s.params.channels; } s.buffer_size_read = s.output_line_bytes_raw * 64; compute_session_pixel_offsets(dev, s, sensor); s.shading_pixel_offset = sensor.shading_pixel_offset; if (dev->model->asic_type == AsicType::GL124 || dev->model->asic_type == AsicType::GL845 || dev->model->asic_type == AsicType::GL846) { s.enable_ledadd = (s.params.channels == 1 && dev->model->is_cis && s.params.color_filter == ColorFilter::NONE); } s.use_host_side_calib = sensor.use_host_side_calib; if (dev->model->asic_type == AsicType::GL841 || dev->model->asic_type == AsicType::GL842 || dev->model->asic_type == AsicType::GL843) { // no 16 bit gamma for this ASIC if (s.params.depth == 16) { s.params.flags |= ScanFlag::DISABLE_GAMMA; } } s.computed = true; DBG(DBG_info, "%s ", __func__); debug_dump(DBG_info, s); } ImagePipelineStack build_image_pipeline(const Genesys_Device& dev, const ScanSession& session, unsigned pipeline_index, bool log_image_data) { auto format = create_pixel_format(session.params.depth, dev.model->is_cis ? 1 : session.params.channels, dev.model->line_mode_color_order); auto depth = get_pixel_format_depth(format); auto width = get_pixels_from_row_bytes(format, session.output_line_bytes_raw); auto read_data_from_usb = [&dev](std::size_t size, std::uint8_t* data) { DBG(DBG_info, "read_data_from_usb: reading %zu bytes\n", size); auto begin = std::chrono::high_resolution_clock::now(); dev.interface->bulk_read_data(0x45, data, size); auto end = std::chrono::high_resolution_clock::now(); float us = std::chrono::duration_cast(end - begin).count(); float speed = size / us; // bytes/us == MB/s DBG(DBG_info, "read_data_from_usb: reading %zu bytes finished %f MB/s\n", size, speed); return true; }; auto debug_prefix = "gl_pipeline_" + std::to_string(pipeline_index); ImagePipelineStack pipeline; auto lines = session.optical_line_count; auto buffer_size = session.buffer_size_read; // At least GL841 requires reads to be aligned to 2 bytes and will fail on some devices on // certain circumstances. buffer_size = align_multiple_ceil(buffer_size, 2); auto& src_node = pipeline.push_first_node( width, lines, format, buffer_size, read_data_from_usb); src_node.set_last_read_multiple(2); if (log_image_data) { pipeline.push_node(debug_prefix + "_0_from_usb.tiff"); } if (session.segment_count > 1) { auto output_width = session.output_segment_pixel_group_count * session.segment_count; pipeline.push_node(output_width, dev.segment_order, session.conseq_pixel_dist, 1, 1); if (log_image_data) { pipeline.push_node(debug_prefix + "_1_after_desegment.tiff"); } } if (depth == 16) { unsigned num_swaps = 0; if (has_flag(dev.model->flags, ModelFlag::SWAP_16BIT_DATA)) { num_swaps++; } #ifdef WORDS_BIGENDIAN num_swaps++; #endif if (num_swaps % 2 != 0) { pipeline.push_node(); if (log_image_data) { pipeline.push_node(debug_prefix + "_2_after_swap.tiff"); } } } if (has_flag(dev.model->flags, ModelFlag::INVERT_PIXEL_DATA)) { pipeline.push_node(); if (log_image_data) { pipeline.push_node(debug_prefix + "_3_after_invert.tiff"); } } if (dev.model->is_cis && session.params.channels == 3) { pipeline.push_node(dev.model->line_mode_color_order); if (log_image_data) { pipeline.push_node(debug_prefix + "_4_after_merge_mono.tiff"); } } if (pipeline.get_output_format() == PixelFormat::BGR888) { pipeline.push_node(PixelFormat::RGB888); } if (pipeline.get_output_format() == PixelFormat::BGR161616) { pipeline.push_node(PixelFormat::RGB161616); } if (log_image_data) { pipeline.push_node(debug_prefix + "_5_after_format.tiff"); } if (session.max_color_shift_lines > 0 && session.params.channels == 3) { pipeline.push_node( session.color_shift_lines_r, session.color_shift_lines_g, session.color_shift_lines_b); if (log_image_data) { pipeline.push_node(debug_prefix + "_6_after_color_unshift.tiff"); } } if (!session.stagger_x.empty()) { // FIXME: the image will be scaled to requested pixel count without regard to the reduction // of image size in this step. pipeline.push_node(session.stagger_x.shifts()); if (log_image_data) { pipeline.push_node(debug_prefix + "_7_after_x_unstagger.tiff"); } } if (session.num_staggered_lines > 0) { pipeline.push_node(session.stagger_y.shifts()); if (log_image_data) { pipeline.push_node(debug_prefix + "_8_after_y_unstagger.tiff"); } } if (session.use_host_side_calib && !has_flag(dev.model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION) && !has_flag(session.params.flags, ScanFlag::DISABLE_SHADING)) { unsigned offset_pixels = session.params.startx + dev.calib_session.shading_pixel_offset; unsigned offset_bytes = offset_pixels * dev.calib_session.params.channels; pipeline.push_node(dev.dark_average_data, dev.white_average_data, offset_bytes); if (log_image_data) { pipeline.push_node(debug_prefix + "_9_after_calibrate.tiff"); } } if (session.use_host_side_gray) { pipeline.push_node(); if (log_image_data) { pipeline.push_node(debug_prefix + "_10_after_nogray.tiff"); } } if (pipeline.get_output_width() != session.params.get_requested_pixels()) { pipeline.push_node(session.params.get_requested_pixels()); } return pipeline; } void setup_image_pipeline(Genesys_Device& dev, const ScanSession& session) { static unsigned s_pipeline_index = 0; s_pipeline_index++; dev.pipeline = build_image_pipeline(dev, session, s_pipeline_index, dbg_log_image_data()); auto read_from_pipeline = [&dev](std::size_t size, std::uint8_t* out_data) { (void) size; // will be always equal to dev.pipeline.get_output_row_bytes() return dev.pipeline.get_next_row_data(out_data); }; dev.pipeline_buffer = ImageBuffer{dev.pipeline.get_output_row_bytes(), read_from_pipeline}; } std::uint8_t compute_frontend_gain_wolfson(float value, float target_value) { /* the flow of data through the frontend ADC is as follows (see e.g. WM8192 datasheet) input -> apply offset (o = i + 260mV * (DAC[7:0]-127.5)/127.5) -> -> apply gain (o = i * 208/(283-PGA[7:0]) -> ADC Here we have some input data that was acquired with zero gain (PGA==0). We want to compute gain such that the output would approach full ADC range (controlled by target_value). We want to solve the following for {PGA}: {value} = {input} * 208 / (283 - 0) {target_value} = {input} * 208 / (283 - {PGA}) The solution is the following equation: {PGA} = 283 * (1 - {value} / {target_value}) */ float gain = value / target_value; int code = static_cast(283 * (1 - gain)); return clamp(code, 0, 255); } std::uint8_t compute_frontend_gain_lide_80(float value, float target_value) { int code = static_cast((target_value / value) * 12); return clamp(code, 0, 255); } std::uint8_t compute_frontend_gain_wolfson_gl841(float value, float target_value) { // this code path is similar to what generic wolfson code path uses and uses similar constants, // but is likely incorrect. float inv_gain = target_value / value; inv_gain *= 0.69f; int code = static_cast(283 - 208 / inv_gain); return clamp(code, 0, 255); } std::uint8_t compute_frontend_gain_wolfson_gl846_gl847_gl124(float value, float target_value) { // this code path is similar to what generic wolfson code path uses and uses similar constants, // but is likely incorrect. float inv_gain = target_value / value; int code = static_cast(283 - 208 / inv_gain); return clamp(code, 0, 255); } std::uint8_t compute_frontend_gain_analog_devices(float value, float target_value) { /* The flow of data through the frontend ADC is as follows (see e.g. AD9826 datasheet) input -> apply offset (o = i + 300mV * (OFFSET[8] ? 1 : -1) * (OFFSET[7:0] / 127) -> apply gain (o = i * 6 / (1 + 5 * ( 63 - PGA[5:0] ) / 63 ) ) -> ADC We want to solve the following for {PGA}: {value} = {input} * 6 / (1 + 5 * ( 63 - 0) / 63 ) ) {target_value} = {input} * 6 / (1 + 5 * ( 63 - {PGA}) / 63 ) ) The solution is the following equation: {PGA} = (378 / 5) * ({target_value} - {value} / {target_value}) */ int code = static_cast((378.0f / 5.0f) * ((target_value - value) / target_value)); return clamp(code, 0, 63); } std::uint8_t compute_frontend_gain(float value, float target_value, FrontendType frontend_type) { switch (frontend_type) { case FrontendType::WOLFSON: return compute_frontend_gain_wolfson(value, target_value); case FrontendType::ANALOG_DEVICES: return compute_frontend_gain_analog_devices(value, target_value); case FrontendType::CANON_LIDE_80: return compute_frontend_gain_lide_80(value, target_value); case FrontendType::WOLFSON_GL841: return compute_frontend_gain_wolfson_gl841(value, target_value); case FrontendType::WOLFSON_GL846: case FrontendType::ANALOG_DEVICES_GL847: case FrontendType::WOLFSON_GL124: return compute_frontend_gain_wolfson_gl846_gl847_gl124(value, target_value); default: throw SaneException("Unknown frontend to compute gain for"); } } /** @brief initialize device * Initialize backend and ASIC : registers, motor tables, and gamma tables * then ensure scanner's head is at home. Designed for gl846+ ASICs. * Detects cold boot (ie first boot since device plugged) in this case * an extensice setup up is done at hardware level. * * @param dev device to initialize * @param max_regs umber of maximum used registers */ void sanei_genesys_asic_init(Genesys_Device* dev) { DBG_HELPER(dbg); std::uint8_t val; bool cold = true; // URB 16 control 0xc0 0x0c 0x8e 0x0b len 1 read 0x00 */ dev->interface->get_usb_device().control_msg(REQUEST_TYPE_IN, REQUEST_REGISTER, VALUE_GET_REGISTER, 0x00, 1, &val); DBG (DBG_io2, "%s: value=0x%02x\n", __func__, val); DBG (DBG_info, "%s: device is %s\n", __func__, (val & 0x08) ? "USB 1.0" : "USB2.0"); if (val & 0x08) { dev->usb_mode = 1; } else { dev->usb_mode = 2; } /* Check if the device has already been initialized and powered up. We read register 0x06 and check PWRBIT, if reset scanner has been freshly powered up. This bit will be set to later so that following reads can detect power down/up cycle */ if (!is_testing_mode()) { if (dev->interface->read_register(0x06) & 0x10) { cold = false; } } DBG (DBG_info, "%s: device is %s\n", __func__, cold ? "cold" : "warm"); /* don't do anything if backend is initialized and hardware hasn't been * replug */ if (dev->already_initialized && !cold) { DBG (DBG_info, "%s: already initialized, nothing to do\n", __func__); return; } // set up hardware and registers dev->cmd_set->asic_boot(dev, cold); /* now hardware part is OK, set up device struct */ dev->white_average_data.clear(); dev->dark_average_data.clear(); dev->settings.color_filter = ColorFilter::RED; dev->initial_regs = dev->reg; const auto& sensor = sanei_genesys_find_sensor_any(dev); // Set analog frontend dev->cmd_set->set_fe(dev, sensor, AFE_INIT); dev->already_initialized = true; // Move to home if needed if (dev->model->model_id == ModelId::CANON_8600F) { if (!dev->cmd_set->is_head_home(*dev, ScanHeadId::SECONDARY)) { dev->set_head_pos_unknown(ScanHeadId::SECONDARY); } if (!dev->cmd_set->is_head_home(*dev, ScanHeadId::PRIMARY)) { dev->set_head_pos_unknown(ScanHeadId::SECONDARY); } } dev->cmd_set->move_back_home(dev, true); // Set powersaving (default = 15 minutes) dev->cmd_set->set_powersaving(dev, 15); } void scanner_start_action(Genesys_Device& dev, bool start_motor) { DBG_HELPER(dbg); switch (dev.model->asic_type) { case AsicType::GL646: case AsicType::GL841: case AsicType::GL842: case AsicType::GL843: case AsicType::GL845: case AsicType::GL846: case AsicType::GL847: case AsicType::GL124: break; default: throw SaneException("Unsupported chip"); } if (start_motor) { dev.interface->write_register(0x0f, 0x01); } else { dev.interface->write_register(0x0f, 0); } } void sanei_genesys_set_dpihw(Genesys_Register_Set& regs, unsigned dpihw) { // same across GL646, GL841, GL843, GL846, GL847, GL124 const std::uint8_t REG_0x05_DPIHW_MASK = 0xc0; const std::uint8_t REG_0x05_DPIHW_600 = 0x00; const std::uint8_t REG_0x05_DPIHW_1200 = 0x40; const std::uint8_t REG_0x05_DPIHW_2400 = 0x80; const std::uint8_t REG_0x05_DPIHW_4800 = 0xc0; std::uint8_t dpihw_setting; switch (dpihw) { case 600: dpihw_setting = REG_0x05_DPIHW_600; break; case 1200: dpihw_setting = REG_0x05_DPIHW_1200; break; case 2400: dpihw_setting = REG_0x05_DPIHW_2400; break; case 4800: dpihw_setting = REG_0x05_DPIHW_4800; break; default: throw SaneException("Unknown dpihw value: %d", dpihw); } regs.set8_mask(0x05, dpihw_setting, REG_0x05_DPIHW_MASK); } void regs_set_exposure(AsicType asic_type, Genesys_Register_Set& regs, const SensorExposure& exposure) { switch (asic_type) { case AsicType::GL124: { regs.set24(gl124::REG_EXPR, exposure.red); regs.set24(gl124::REG_EXPG, exposure.green); regs.set24(gl124::REG_EXPB, exposure.blue); break; } case AsicType::GL646: { regs.set16(gl646::REG_EXPR, exposure.red); regs.set16(gl646::REG_EXPG, exposure.green); regs.set16(gl646::REG_EXPB, exposure.blue); break; } case AsicType::GL841: { regs.set16(gl841::REG_EXPR, exposure.red); regs.set16(gl841::REG_EXPG, exposure.green); regs.set16(gl841::REG_EXPB, exposure.blue); break; } case AsicType::GL842: { regs.set16(gl842::REG_EXPR, exposure.red); regs.set16(gl842::REG_EXPG, exposure.green); regs.set16(gl842::REG_EXPB, exposure.blue); break; } case AsicType::GL843: { regs.set16(gl843::REG_EXPR, exposure.red); regs.set16(gl843::REG_EXPG, exposure.green); regs.set16(gl843::REG_EXPB, exposure.blue); break; } case AsicType::GL845: case AsicType::GL846: { regs.set16(gl846::REG_EXPR, exposure.red); regs.set16(gl846::REG_EXPG, exposure.green); regs.set16(gl846::REG_EXPB, exposure.blue); break; } case AsicType::GL847: { regs.set16(gl847::REG_EXPR, exposure.red); regs.set16(gl847::REG_EXPG, exposure.green); regs.set16(gl847::REG_EXPB, exposure.blue); break; } default: throw SaneException("Unsupported asic"); } } void regs_set_optical_off(AsicType asic_type, Genesys_Register_Set& regs) { DBG_HELPER(dbg); switch (asic_type) { case AsicType::GL646: { regs.find_reg(gl646::REG_0x01).value &= ~gl646::REG_0x01_SCAN; break; } case AsicType::GL841: { regs.find_reg(gl841::REG_0x01).value &= ~gl841::REG_0x01_SCAN; break; } case AsicType::GL842: { regs.find_reg(gl842::REG_0x01).value &= ~gl842::REG_0x01_SCAN; break; } case AsicType::GL843: { regs.find_reg(gl843::REG_0x01).value &= ~gl843::REG_0x01_SCAN; break; } case AsicType::GL845: case AsicType::GL846: { regs.find_reg(gl846::REG_0x01).value &= ~gl846::REG_0x01_SCAN; break; } case AsicType::GL847: { regs.find_reg(gl847::REG_0x01).value &= ~gl847::REG_0x01_SCAN; break; } case AsicType::GL124: { regs.find_reg(gl124::REG_0x01).value &= ~gl124::REG_0x01_SCAN; break; } default: throw SaneException("Unsupported asic"); } } bool get_registers_gain4_bit(AsicType asic_type, const Genesys_Register_Set& regs) { switch (asic_type) { case AsicType::GL646: return static_cast(regs.get8(gl646::REG_0x06) & gl646::REG_0x06_GAIN4); case AsicType::GL841: return static_cast(regs.get8(gl841::REG_0x06) & gl841::REG_0x06_GAIN4); case AsicType::GL842: return static_cast(regs.get8(gl842::REG_0x06) & gl842::REG_0x06_GAIN4); case AsicType::GL843: return static_cast(regs.get8(gl843::REG_0x06) & gl843::REG_0x06_GAIN4); case AsicType::GL845: case AsicType::GL846: return static_cast(regs.get8(gl846::REG_0x06) & gl846::REG_0x06_GAIN4); case AsicType::GL847: return static_cast(regs.get8(gl847::REG_0x06) & gl847::REG_0x06_GAIN4); case AsicType::GL124: return static_cast(regs.get8(gl124::REG_0x06) & gl124::REG_0x06_GAIN4); default: throw SaneException("Unsupported chipset"); } } /** * Wait for the scanning head to park */ void sanei_genesys_wait_for_home(Genesys_Device* dev) { DBG_HELPER(dbg); /* clear the parking status whatever the outcome of the function */ dev->parking = false; if (is_testing_mode()) { return; } // read initial status, if head isn't at home and motor is on we are parking, so we wait. // gl847/gl124 need 2 reads for reliable results auto status = scanner_read_status(*dev); dev->interface->sleep_ms(10); status = scanner_read_status(*dev); if (status.is_at_home) { DBG (DBG_info, "%s: already at home\n", __func__); return; } unsigned timeout_ms = 200000; unsigned elapsed_ms = 0; do { dev->interface->sleep_ms(100); elapsed_ms += 100; status = scanner_read_status(*dev); } while (elapsed_ms < timeout_ms && !status.is_at_home); /* if after the timeout, head is still not parked, error out */ if (elapsed_ms >= timeout_ms && !status.is_at_home) { DBG (DBG_error, "%s: failed to reach park position in %dseconds\n", __func__, timeout_ms / 1000); throw SaneException(SANE_STATUS_IO_ERROR, "failed to reach park position"); } } const MotorProfile* get_motor_profile_ptr(const std::vector& profiles, unsigned exposure, const ScanSession& session) { int best_i = -1; for (unsigned i = 0; i < profiles.size(); ++i) { const auto& profile = profiles[i]; if (!profile.resolutions.matches(session.params.yres)) { continue; } if (!profile.scan_methods.matches(session.params.scan_method)) { continue; } if (profile.max_exposure == exposure) { return &profile; } if (profile.max_exposure == 0 || profile.max_exposure >= exposure) { if (best_i < 0) { // no match found yet best_i = i; } else { // test for better match if (profiles[i].max_exposure < profiles[best_i].max_exposure) { best_i = i; } } } } if (best_i < 0) { return nullptr; } return &profiles[best_i]; } const MotorProfile& get_motor_profile(const std::vector& profiles, unsigned exposure, const ScanSession& session) { const auto* profile = get_motor_profile_ptr(profiles, exposure, session); if (profile == nullptr) { throw SaneException("Motor slope is not configured"); } return *profile; } MotorSlopeTable create_slope_table(AsicType asic_type, const Genesys_Motor& motor, unsigned ydpi, unsigned exposure, unsigned step_multiplier, const MotorProfile& motor_profile) { unsigned target_speed_w = ((exposure * ydpi) / motor.base_ydpi); auto table = create_slope_table_for_speed(motor_profile.slope, target_speed_w, motor_profile.step_type, step_multiplier, 2 * step_multiplier, get_slope_table_max_size(asic_type)); return table; } MotorSlopeTable create_slope_table_fastest(AsicType asic_type, unsigned step_multiplier, const MotorProfile& motor_profile) { return create_slope_table_for_speed(motor_profile.slope, motor_profile.slope.max_speed_w, motor_profile.step_type, step_multiplier, 2 * step_multiplier, get_slope_table_max_size(asic_type)); } /** @brief returns the lowest possible ydpi for the device * Parses device entry to find lowest motor dpi. * @param dev device description * @return lowest motor resolution */ int sanei_genesys_get_lowest_ydpi(Genesys_Device *dev) { const auto& resolution_settings = dev->model->get_resolution_settings(dev->settings.scan_method); return resolution_settings.get_min_resolution_y(); } /** @brief returns the lowest possible dpi for the device * Parses device entry to find lowest motor or sensor dpi. * @param dev device description * @return lowest motor resolution */ int sanei_genesys_get_lowest_dpi(Genesys_Device *dev) { const auto& resolution_settings = dev->model->get_resolution_settings(dev->settings.scan_method); return std::min(resolution_settings.get_min_resolution_x(), resolution_settings.get_min_resolution_y()); } /** @brief check is a cache entry may be used * Compares current settings with the cache entry and return * true if they are compatible. * A calibration cache is compatible if color mode and x dpi match the user * requested scan. In the case of CIS scanners, dpi isn't a criteria. * flatbed cache entries are considered too old and then expires if they * are older than the expiration time option, forcing calibration at least once * then given time. */ bool sanei_genesys_is_compatible_calibration(Genesys_Device* dev, const ScanSession& session, const Genesys_Calibration_Cache* cache, bool for_overwrite) { DBG_HELPER(dbg); #ifdef HAVE_SYS_TIME_H struct timeval time; #endif bool compatible = true; const auto& dev_params = session.params; if (dev_params.scan_method != cache->params.scan_method) { dbg.vlog(DBG_io, "incompatible: scan_method %d vs. %d\n", static_cast(dev_params.scan_method), static_cast(cache->params.scan_method)); compatible = false; } if (dev_params.xres != cache->params.xres) { dbg.vlog(DBG_io, "incompatible: params.xres %d vs. %d\n", dev_params.xres, cache->params.xres); compatible = false; } if (dev_params.yres != cache->params.yres) { // exposure depends on selected sensor and we select the sensor according to yres dbg.vlog(DBG_io, "incompatible: params.yres %d vs. %d\n", dev_params.yres, cache->params.yres); compatible = false; } if (dev_params.channels != cache->params.channels) { // exposure depends on total number of pixels at least on gl841 dbg.vlog(DBG_io, "incompatible: params.channels %d vs. %d\n", dev_params.channels, cache->params.channels); compatible = false; } if (dev_params.startx != cache->params.startx) { // exposure depends on total number of pixels at least on gl841 dbg.vlog(DBG_io, "incompatible: params.startx %d vs. %d\n", dev_params.startx, cache->params.startx); compatible = false; } if (dev_params.pixels != cache->params.pixels) { // exposure depends on total number of pixels at least on gl841 dbg.vlog(DBG_io, "incompatible: params.pixels %d vs. %d\n", dev_params.pixels, cache->params.pixels); compatible = false; } if (!compatible) { DBG (DBG_proc, "%s: completed, non compatible cache\n", __func__); return false; } /* a cache entry expires after after expiration time for non sheetfed scanners */ /* this is not taken into account when overwriting cache entries */ #ifdef HAVE_SYS_TIME_H if (!for_overwrite && dev->settings.expiration_time >=0) { gettimeofday(&time, nullptr); if ((time.tv_sec - cache->last_calibration > dev->settings.expiration_time*60) && !dev->model->is_sheetfed && (dev->settings.scan_method == ScanMethod::FLATBED)) { DBG (DBG_proc, "%s: expired entry, non compatible cache\n", __func__); return false; } } #endif return true; } /** @brief build lookup table for digital enhancements * Function to build a lookup table (LUT), often used by scanners to implement brightness/contrast/gamma or by backends to speed binarization/thresholding offset and slope inputs are -127 to +127 slope rotates line around central input/output val, 0 makes horizontal line pos zero neg . x . . x . x . . x out . x .xxxxxxxxxxx . x . x . . x ....x....... ............ .......x.... in in in offset moves line vertically, and clamps to output range 0 keeps the line crossing the center of the table high low . xxxxxxxx . . x . out x . x . . x ............ xxxxxxxx.... in in out_min/max provide bounds on output values, useful when building thresholding lut. 0 and 255 are good defaults otherwise. * @param lut pointer where to store the generated lut * @param in_bits number of bits for in values * @param out_bits number of bits of out values * @param out_min minimal out value * @param out_max maximal out value * @param slope slope of the generated data * @param offset offset of the generated data */ void sanei_genesys_load_lut(unsigned char* lut, int in_bits, int out_bits, int out_min, int out_max, int slope, int offset) { DBG_HELPER(dbg); int i, j; double shift, rise; int max_in_val = (1 << in_bits) - 1; int max_out_val = (1 << out_bits) - 1; std::uint8_t* lut_p8 = lut; std::uint16_t* lut_p16 = reinterpret_cast(lut); /* slope is converted to rise per unit run: * first [-127,127] to [-.999,.999] * then to [-PI/4,PI/4] then [0,PI/2] * then take the tangent (T.O.A) * then multiply by the normal linear slope * because the table may not be square, i.e. 1024x256*/ auto pi_4 = M_PI / 4.0; rise = std::tan(static_cast(slope) / 128 * pi_4 + pi_4) * max_out_val / max_in_val; /* line must stay vertically centered, so figure * out vertical offset at central input value */ shift = static_cast(max_out_val) / 2 - (rise * max_in_val / 2); /* convert the user offset setting to scale of output * first [-127,127] to [-1,1] * then to [-max_out_val/2,max_out_val/2]*/ shift += static_cast(offset) / 127 * max_out_val / 2; for (i = 0; i <= max_in_val; i++) { j = static_cast(rise * i + shift); /* cap data to required range */ if (j < out_min) { j = out_min; } else if (j > out_max) { j = out_max; } /* copy result according to bit depth */ if (out_bits <= 8) { *lut_p8 = j; lut_p8++; } else { *lut_p16 = j; lut_p16++; } } } } // namespace genesys backends-1.3.0/backend/genesys/low.h000066400000000000000000000436571456256263500173400ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2003 Oliver Rauch Copyright (C) 2003, 2004 Henning Meier-Geinitz Copyright (C) 2004, 2005 Gerhard Jaeger Copyright (C) 2004-2013 Stéphane Voltz Copyright (C) 2005-2009 Pierre Willenbrock Copyright (C) 2006 Laurent Charpentier Parts of the structs have been taken from the gt68xx backend by Sergey Vlasov et al. 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, see . */ #ifndef GENESYS_LOW_H #define GENESYS_LOW_H #include "../include/sane/config.h" #include #include #include #include #include #include #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_MKDIR #include #include #endif #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_usb.h" #include "../include/_stdint.h" #include "device.h" #include "enums.h" #include "error.h" #include "fwd.h" #include "usb_device.h" #include "sensor.h" #include "serialize.h" #include "settings.h" #include "static_init.h" #include "status.h" #include "register.h" #include #include #include #include #include #include #include #include #include #include #include #define GENESYS_RED 0 #define GENESYS_GREEN 1 #define GENESYS_BLUE 2 #define GENESYS_HAS_NO_BUTTONS 0 /**< scanner has no supported button */ #define GENESYS_HAS_SCAN_SW (1 << 0) /**< scanner has SCAN button */ #define GENESYS_HAS_FILE_SW (1 << 1) /**< scanner has FILE button */ #define GENESYS_HAS_COPY_SW (1 << 2) /**< scanner has COPY button */ #define GENESYS_HAS_EMAIL_SW (1 << 3) /**< scanner has EMAIL button */ #define GENESYS_HAS_PAGE_LOADED_SW (1 << 4) /**< scanner has paper in detection */ #define GENESYS_HAS_OCR_SW (1 << 5) /**< scanner has OCR button */ #define GENESYS_HAS_POWER_SW (1 << 6) /**< scanner has power button */ #define GENESYS_HAS_CALIBRATE (1 << 7) /**< scanner has 'calibrate' software button to start calibration */ #define GENESYS_HAS_EXTRA_SW (1 << 8) /**< scanner has extra function button */ #define GENESYS_HAS_TRANSP_SW (1 << 9) /**< scanner has TRANSPARENCY/SCAN_FILM button */ #define GENESYS_HAS_PDF1_SW (1 << 10) /**< scanner has special PDF button 1 */ #define GENESYS_HAS_PDF2_SW (1 << 11) /**< scanner has special PDF button 2 */ #define GENESYS_HAS_PDF3_SW (1 << 12) /**< scanner has special PDF button 3 */ #define GENESYS_HAS_PDF4_SW (1 << 13) /**< scanner has special PDF button 4 */ /* USB control message values */ #define REQUEST_TYPE_IN (USB_TYPE_VENDOR | USB_DIR_IN) #define REQUEST_TYPE_OUT (USB_TYPE_VENDOR | USB_DIR_OUT) #define REQUEST_REGISTER 0x0c #define REQUEST_BUFFER 0x04 #define VALUE_BUFFER 0x82 #define VALUE_SET_REGISTER 0x83 #define VALUE_READ_REGISTER 0x84 #define VALUE_WRITE_REGISTER 0x85 #define VALUE_INIT 0x87 #define GPIO_OUTPUT_ENABLE 0x89 #define GPIO_READ 0x8a #define GPIO_WRITE 0x8b #define VALUE_BUF_ENDACCESS 0x8c #define VALUE_GET_REGISTER 0x8e #define INDEX 0x00 /* todo: used? #define VALUE_READ_STATUS 0x86 */ /* Read/write bulk data/registers */ #define BULK_OUT 0x01 #define BULK_IN 0x00 #define BULK_RAM 0x00 #define BULK_REGISTER 0x11 #define BULKOUT_MAXSIZE 0xF000 /* AFE values */ #define AFE_INIT 1 #define AFE_SET 2 #define AFE_POWER_SAVE 4 namespace genesys { class UsbDeviceEntry { public: static constexpr std::uint16_t BCD_DEVICE_NOT_SET = 0xffff; UsbDeviceEntry(std::uint16_t vendor_id, std::uint16_t product_id, const Genesys_Model& model) : vendor_{vendor_id}, product_{product_id}, bcd_device_{BCD_DEVICE_NOT_SET}, model_{model} {} UsbDeviceEntry(std::uint16_t vendor_id, std::uint16_t product_id, std::uint16_t bcd_device, const Genesys_Model& model) : vendor_{vendor_id}, product_{product_id}, bcd_device_{bcd_device}, model_{model} {} std::uint16_t vendor_id() const { return vendor_; } std::uint16_t product_id() const { return product_; } std::uint16_t bcd_device() const { return bcd_device_; } const Genesys_Model& model() const { return model_; } bool matches(std::uint16_t vendor_id, std::uint16_t product_id, std::uint16_t bcd_device) { if (vendor_ != vendor_id) return false; if (product_ != product_id) return false; if (bcd_device_ != BCD_DEVICE_NOT_SET && bcd_device != BCD_DEVICE_NOT_SET && bcd_device_ != bcd_device) { return false; } return true; } private: // USB vendor identifier std::uint16_t vendor_; // USB product identifier std::uint16_t product_; // USB bcdProduct identifier std::uint16_t bcd_device_; // Scanner model information Genesys_Model model_; }; /*--------------------------------------------------------------------------*/ /* common functions needed by low level specific functions */ /*--------------------------------------------------------------------------*/ std::unique_ptr create_cmd_set(AsicType asic_type); // reads the status of the scanner Status scanner_read_status(Genesys_Device& dev); // reads the status of the scanner reliably. This is done by reading the status twice. The first // read sometimes returns the home sensor as engaged when this is not true. Status scanner_read_reliable_status(Genesys_Device& dev); // reads and prints the scanner status void scanner_read_print_status(Genesys_Device& dev); void debug_print_status(DebugMessageHelper& dbg, Status status); void scanner_register_rw_clear_bits(Genesys_Device& dev, std::uint16_t address, std::uint8_t mask); void scanner_register_rw_set_bits(Genesys_Device& dev, std::uint16_t address, std::uint8_t mask); void scanner_register_rw_bits(Genesys_Device& dev, std::uint16_t address, std::uint8_t value, std::uint8_t mask); void sanei_genesys_write_ahb(Genesys_Device* dev, std::uint32_t addr, std::uint32_t size, std::uint8_t* data); extern void sanei_genesys_init_structs (Genesys_Device * dev); const Genesys_Sensor& sanei_genesys_find_sensor_any(const Genesys_Device* dev); const Genesys_Sensor& sanei_genesys_find_sensor(const Genesys_Device* dev, unsigned dpi, unsigned channels, ScanMethod scan_method); bool sanei_genesys_has_sensor(const Genesys_Device* dev, unsigned dpi, unsigned channels, ScanMethod scan_method); Genesys_Sensor& sanei_genesys_find_sensor_for_write(Genesys_Device* dev, unsigned dpi, unsigned channels, ScanMethod scan_method); std::vector> sanei_genesys_find_sensors_all(const Genesys_Device* dev, ScanMethod scan_method); std::vector> sanei_genesys_find_sensors_all_for_write(Genesys_Device* dev, ScanMethod scan_method); extern void sanei_genesys_init_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, int pixels_per_line); extern void sanei_genesys_read_valid_words(Genesys_Device* dev, unsigned int* steps); extern void sanei_genesys_read_scancnt(Genesys_Device* dev, unsigned int* steps); extern void sanei_genesys_read_feed_steps(Genesys_Device* dev, unsigned int* steps); void sanei_genesys_set_lamp_power(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs, bool set); void sanei_genesys_set_motor_power(Genesys_Register_Set& regs, bool set); bool should_enable_gamma(const ScanSession& session, const Genesys_Sensor& sensor); /** Calculates the values of the Z{1,2}MOD registers. They are a phase correction to synchronize with the line clock during acceleration and deceleration. two_table is true if moving is done by two tables, false otherwise. acceleration_steps is the number of steps for acceleration, i.e. the number written to REG_STEPNO. move_steps number of steps to move, i.e. the number written to REG_FEEDL. buffer_acceleration_steps, the number of steps for acceleration when buffer condition is met, i.e. the number written to REG_FWDSTEP. */ void sanei_genesys_calculate_zmod(bool two_table, std::uint32_t exposure_time, const std::vector& slope_table, unsigned acceleration_steps, unsigned move_steps, unsigned buffer_acceleration_steps, std::uint32_t* out_z1, std::uint32_t* out_z2); extern void sanei_genesys_set_buffer_address(Genesys_Device* dev, std::uint32_t addr); unsigned sanei_genesys_get_bulk_max_size(AsicType asic_type); SANE_Int sanei_genesys_exposure_time2(Genesys_Device* dev, const MotorProfile& profile, float ydpi, int endpixel, int led_exposure); void sanei_genesys_create_default_gamma_table(Genesys_Device* dev, std::vector& gamma_table, float gamma); std::vector get_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor, int color); void sanei_genesys_send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor); extern void sanei_genesys_stop_motor(Genesys_Device* dev); // moves the scan head by the specified steps at the motor base dpi void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, Direction direction); void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home); void scanner_move_back_home_ta(Genesys_Device& dev); /** Search for a full width black or white strip. This function searches for a black or white stripe across the scanning area. When searching backward, the searched area must completely be of the desired color since this area will be used for calibration which scans forward. @param dev scanner device @param forward true if searching forward, false if searching backward @param black true if searching for a black strip, false for a white strip */ void scanner_search_strip(Genesys_Device& dev, bool forward, bool black); bool should_calibrate_only_active_area(const Genesys_Device& dev, const Genesys_Settings& settings); void scanner_offset_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs); void scanner_coarse_gain_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs, unsigned dpi); SensorExposure scanner_led_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs); void scanner_clear_scan_and_feed_counts(Genesys_Device& dev); void scanner_send_slope_table(Genesys_Device* dev, const Genesys_Sensor& sensor, unsigned table_nr, const std::vector& slope_table); extern void sanei_genesys_write_file(const char* filename, const std::uint8_t* data, std::size_t length); void wait_until_buffer_non_empty(Genesys_Device* dev, bool check_status_twice = false); void sanei_genesys_read_data_from_scanner(Genesys_Device* dev, std::uint8_t* data, size_t size); Image read_unshuffled_image_from_scanner(Genesys_Device* dev, const ScanSession& session, std::size_t total_bytes); void regs_set_exposure(AsicType asic_type, Genesys_Register_Set& regs, const SensorExposure& exposure); void regs_set_optical_off(AsicType asic_type, Genesys_Register_Set& regs); void sanei_genesys_set_dpihw(Genesys_Register_Set& regs, unsigned dpihw); inline SensorExposure sanei_genesys_fixup_exposure(SensorExposure exposure) { exposure.red = std::max(1, exposure.red); exposure.green = std::max(1, exposure.green); exposure.blue = std::max(1, exposure.blue); return exposure; } bool get_registers_gain4_bit(AsicType asic_type, const Genesys_Register_Set& regs); extern void sanei_genesys_wait_for_home(Genesys_Device* dev); extern void sanei_genesys_asic_init(Genesys_Device* dev); void scanner_start_action(Genesys_Device& dev, bool start_motor); void scanner_stop_action(Genesys_Device& dev); void scanner_stop_action_no_move(Genesys_Device& dev, Genesys_Register_Set& regs); bool scanner_is_motor_stopped(Genesys_Device& dev); void scanner_setup_sensor(Genesys_Device& dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs); const MotorProfile* get_motor_profile_ptr(const std::vector& profiles, unsigned exposure, const ScanSession& session); const MotorProfile& get_motor_profile(const std::vector& profiles, unsigned exposure, const ScanSession& session); MotorSlopeTable create_slope_table(AsicType asic_type, const Genesys_Motor& motor, unsigned ydpi, unsigned exposure, unsigned step_multiplier, const MotorProfile& motor_profile); MotorSlopeTable create_slope_table_fastest(AsicType asic_type, unsigned step_multiplier, const MotorProfile& motor_profile); /** @brief find lowest motor resolution for the device. * Parses the resolution list for motor and * returns the lowest value. * @param dev for which to find the lowest motor resolution * @return the lowest available motor resolution for the device */ extern int sanei_genesys_get_lowest_ydpi(Genesys_Device *dev); /** @brief find lowest resolution for the device. * Parses the resolution list for motor and sensor and * returns the lowest value. * @param dev for which to find the lowest resolution * @return the lowest available resolution for the device */ extern int sanei_genesys_get_lowest_dpi(Genesys_Device *dev); bool sanei_genesys_is_compatible_calibration(Genesys_Device* dev, const ScanSession& session, const Genesys_Calibration_Cache* cache, bool for_overwrite); extern void sanei_genesys_load_lut(unsigned char* lut, int in_bits, int out_bits, int out_min, int out_max, int slope, int offset); std::vector generate_gamma_buffer(Genesys_Device* dev, const Genesys_Sensor& sensor, int bits, int max, int size); unsigned session_adjust_output_pixels(unsigned output_pixels, const Genesys_Device& dev, const Genesys_Sensor& sensor, unsigned output_xresolution, unsigned output_yresolution, bool adjust_output_pixels); void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Sensor& sensor); ImagePipelineStack build_image_pipeline(const Genesys_Device& dev, const ScanSession& session, unsigned pipeline_index, bool log_image_data); // sets up a image pipeline for device `dev` void setup_image_pipeline(Genesys_Device& dev, const ScanSession& session); std::uint8_t compute_frontend_gain(float value, float target_value, FrontendType frontend_type); /*---------------------------------------------------------------------------*/ /* ASIC specific functions declarations */ /*---------------------------------------------------------------------------*/ extern StaticInit> s_sensors; extern StaticInit> s_frontends; extern StaticInit> s_gpo; extern StaticInit> s_memory_layout; extern StaticInit> s_motors; extern StaticInit> s_usb_devices; void genesys_init_sensor_tables(); void genesys_init_frontend_tables(); void genesys_init_gpo_tables(); void genesys_init_memory_layout_tables(); void genesys_init_motor_tables(); void genesys_init_usb_device_tables(); void verify_sensor_tables(); void verify_usb_device_tables(); template void debug_dump(unsigned level, const T& value) { std::stringstream out; out << value; DBG(level, "%s\n", out.str().c_str()); } } // namespace genesys #endif /* not GENESYS_LOW_H */ backends-1.3.0/backend/genesys/motor.cpp000066400000000000000000000151241456256263500202160ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "low.h" #include "motor.h" #include "utilities.h" #include #include namespace genesys { unsigned MotorSlope::get_table_step_shifted(unsigned step, StepType step_type) const { // first two steps are always equal to the initial speed if (step < 2) { return initial_speed_w >> static_cast(step_type); } step--; float initial_speed_v = 1.0f / initial_speed_w; float speed_v = std::sqrt(initial_speed_v * initial_speed_v + 2 * acceleration * step); return static_cast(1.0f / speed_v) >> static_cast(step_type); } float compute_acceleration_for_steps(unsigned initial_w, unsigned max_w, unsigned steps) { float initial_speed_v = 1.0f / static_cast(initial_w); float max_speed_v = 1.0f / static_cast(max_w); return (max_speed_v * max_speed_v - initial_speed_v * initial_speed_v) / (2 * steps); } MotorSlope MotorSlope::create_from_steps(unsigned initial_w, unsigned max_w, unsigned steps) { MotorSlope slope; slope.initial_speed_w = initial_w; slope.max_speed_w = max_w; slope.acceleration = compute_acceleration_for_steps(initial_w, max_w, steps); return slope; } void MotorSlopeTable::slice_steps(unsigned count, unsigned step_multiplier) { if (count > table.size() || count < step_multiplier) { throw SaneException("Invalid steps count"); } count = align_multiple_floor(count, step_multiplier); table.resize(count); generate_pixeltime_sum(); } void MotorSlopeTable::expand_table(unsigned count, unsigned step_multiplier) { if (table.empty()) { throw SaneException("Can't expand empty table"); } count = align_multiple_ceil(count, step_multiplier); table.resize(table.size() + count, table.back()); generate_pixeltime_sum(); } void MotorSlopeTable::generate_pixeltime_sum() { pixeltime_sum_ = std::accumulate(table.begin(), table.end(), std::size_t{0}, std::plus()); } unsigned get_slope_table_max_size(AsicType asic_type) { switch (asic_type) { case AsicType::GL646: case AsicType::GL841: case AsicType::GL842: return 255; case AsicType::GL843: case AsicType::GL845: case AsicType::GL846: case AsicType::GL847: case AsicType::GL124: return 1024; default: throw SaneException("Unknown asic type"); } } MotorSlopeTable create_slope_table_for_speed(const MotorSlope& slope, unsigned target_speed_w, StepType step_type, unsigned steps_alignment, unsigned min_size, unsigned max_size) { DBG_HELPER_ARGS(dbg, "target_speed_w: %d, step_type: %d, steps_alignment: %d, min_size: %d", target_speed_w, static_cast(step_type), steps_alignment, min_size); MotorSlopeTable table; unsigned step_shift = static_cast(step_type); unsigned target_speed_shifted_w = target_speed_w >> step_shift; unsigned max_speed_shifted_w = slope.max_speed_w >> step_shift; if (target_speed_shifted_w < max_speed_shifted_w) { dbg.vlog(DBG_warn, "failed to reach target speed %d %d", target_speed_w, slope.max_speed_w); } if (target_speed_shifted_w >= std::numeric_limits::max()) { throw SaneException("Target motor speed is too low"); } unsigned final_speed = std::max(target_speed_shifted_w, max_speed_shifted_w); table.table.reserve(max_size); while (table.table.size() < max_size - 1) { unsigned current = slope.get_table_step_shifted(table.table.size(), step_type); if (current <= final_speed) { break; } table.table.push_back(current); } // make sure the target speed (or the max speed if target speed is too high) is present in // the table table.table.push_back(final_speed); // fill the table up to the specified size while (table.table.size() < max_size - 1 && (table.table.size() % steps_alignment != 0 || table.table.size() < min_size)) { table.table.push_back(table.table.back()); } table.generate_pixeltime_sum(); return table; } std::ostream& operator<<(std::ostream& out, const MotorSlope& slope) { out << "MotorSlope{\n" << " initial_speed_w: " << slope.initial_speed_w << '\n' << " max_speed_w: " << slope.max_speed_w << '\n' << " a: " << slope.acceleration << '\n' << '}'; return out; } std::ostream& operator<<(std::ostream& out, const MotorProfile& profile) { out << "MotorProfile{\n" << " max_exposure: " << profile.max_exposure << '\n' << " step_type: " << profile.step_type << '\n' << " motor_vref: " << profile.motor_vref << '\n' << " resolutions: " << format_indent_braced_list(4, profile.resolutions) << '\n' << " scan_methods: " << format_indent_braced_list(4, profile.scan_methods) << '\n' << " slope: " << format_indent_braced_list(4, profile.slope) << '\n' << '}'; return out; } std::ostream& operator<<(std::ostream& out, const Genesys_Motor& motor) { out << "Genesys_Motor{\n" << " id: " << motor.id << '\n' << " base_ydpi: " << motor.base_ydpi << '\n' << " profiles: " << format_indent_braced_list(4, format_vector_indent_braced(4, "MotorProfile", motor.profiles)) << '\n' << " fast_profiles: " << format_indent_braced_list(4, format_vector_indent_braced(4, "MotorProfile", motor.fast_profiles)) << '\n' << '}'; return out; } } // namespace genesys backends-1.3.0/backend/genesys/motor.h000066400000000000000000000144271456256263500176700ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_MOTOR_H #define BACKEND_GENESYS_MOTOR_H #include #include #include #include "enums.h" #include "sensor.h" #include "value_filter.h" namespace genesys { /* Describes a motor acceleration curve. Definitions: v - speed in steps per pixeltime w - speed in pixel times per step. w = 1 / v a - acceleration in steps per pixeltime squared s - distance travelled in steps t - time in pixeltime The physical mode defines the curve in physical quantities. We assume that the scanner head accelerates from standstill to the target speed uniformly. Then: v(t) = v(0) + a * t (2) Where `a` is acceleration, `t` is time. Also we can calculate the travelled distance `s`: s(t) = v(0) * t + a * t^2 / 2 (3) The actual motor slope is defined as the duration of each motor step. That means we need to define speed in terms of travelled distance. Solving (3) for `t` gives: sqrt( v(0)^2 + 2 * a * s ) - v(0) t(s) = --------------------------------- (4) a Combining (4) and (2) will yield: v(s) = sqrt( v(0)^2 + 2 * a * s ) (5) The data in the slope struct MotorSlope corresponds to the above in the following way: maximum_start_speed is `w(0) = 1/v(0)` maximum_speed is defines maximum speed which should not be exceeded minimum_steps is not used g is `a` Given the start and target speeds on a known motor curve, `a` can be computed as follows: v(t1)^2 - v(t0)^2 a = ----------------- (6) 2 * s Here `v(t0)` and `v(t1)` are the start and target speeds and `s` is the number of step required to reach the target speeds. */ struct MotorSlope { // initial speed in pixeltime per step unsigned initial_speed_w = 0; // max speed in pixeltime per step unsigned max_speed_w = 0; // maximum number of steps in the table unsigned max_step_count; // acceleration in steps per pixeltime squared. float acceleration = 0; unsigned get_table_step_shifted(unsigned step, StepType step_type) const; static MotorSlope create_from_steps(unsigned initial_w, unsigned max_w, unsigned steps); }; struct MotorSlopeTable { std::vector table; void slice_steps(unsigned count, unsigned step_multiplier); // expands the table by the given number of steps void expand_table(unsigned count, unsigned step_multiplier); std::uint64_t pixeltime_sum() const { return pixeltime_sum_; } void generate_pixeltime_sum(); private: std::uint64_t pixeltime_sum_ = 0; }; unsigned get_slope_table_max_size(AsicType asic_type); MotorSlopeTable create_slope_table_for_speed(const MotorSlope& slope, unsigned target_speed_w, StepType step_type, unsigned steps_alignment, unsigned min_size, unsigned max_size); std::ostream& operator<<(std::ostream& out, const MotorSlope& slope); struct MotorProfile { MotorProfile() = default; MotorProfile(const MotorSlope& a_slope, StepType a_step_type, unsigned a_max_exposure) : slope{a_slope}, step_type{a_step_type}, max_exposure{a_max_exposure} {} MotorSlope slope; StepType step_type = StepType::FULL; int motor_vref = -1; // the resolutions this profile is good for ValueFilterAny resolutions = VALUE_FILTER_ANY; // the scan method this profile is good for. If the list is empty, good for any method. ValueFilterAny scan_methods = VALUE_FILTER_ANY; unsigned max_exposure = 0; // 0 - any exposure }; std::ostream& operator<<(std::ostream& out, const MotorProfile& profile); struct Genesys_Motor { Genesys_Motor() = default; // id of the motor description MotorId id = MotorId::UNKNOWN; // motor base steps. Unit: 1/inch int base_ydpi = 0; // slopes to derive individual slopes from std::vector profiles; // slopes to derive individual slopes from for fast moving std::vector fast_profiles; MotorSlope& get_slope_with_step_type(StepType step_type) { for (auto& p : profiles) { if (p.step_type == step_type) return p.slope; } throw SaneException("No motor profile with step type"); } const MotorSlope& get_slope_with_step_type(StepType step_type) const { for (const auto& p : profiles) { if (p.step_type == step_type) return p.slope; } throw SaneException("No motor profile with step type"); } StepType max_step_type() const { if (profiles.empty()) { throw std::runtime_error("Profiles table is empty"); } StepType step_type = StepType::FULL; for (const auto& p : profiles) { step_type = static_cast( std::max(static_cast(step_type), static_cast(p.step_type))); } return step_type; } }; std::ostream& operator<<(std::ostream& out, const Genesys_Motor& motor); } // namespace genesys #endif // BACKEND_GENESYS_MOTOR_H backends-1.3.0/backend/genesys/register.h000066400000000000000000000340721456256263500203520ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_REGISTER_H #define BACKEND_GENESYS_REGISTER_H #include "enums.h" #include "utilities.h" #include #include #include #include #include #include #include namespace genesys { template struct Register { std::uint16_t address = 0; Value value = 0; }; using GenesysRegister = Register; template inline bool operator<(const Register& lhs, const Register& rhs) { return lhs.address < rhs.address; } struct GenesysRegisterSetState { bool is_lamp_on = false; bool is_xpa_on = false; bool is_motor_on = false; MotorMode motor_mode = MotorMode::PRIMARY; }; template class RegisterContainer { public: enum Options { SEQUENTIAL = 1 }; using RegisterType = Register; using ContainerType = std::vector; using iterator = typename ContainerType::iterator; using const_iterator = typename ContainerType::const_iterator; RegisterContainer() = default; RegisterContainer(Options opts) : RegisterContainer() { if ((opts & SEQUENTIAL) == SEQUENTIAL) { sorted_ = false; } } void init_reg(std::uint16_t address, Value default_value) { if (find_reg_index(address) >= 0) { set(address, default_value); return; } RegisterType reg; reg.address = address; reg.value = default_value; registers_.push_back(reg); if (sorted_) std::sort(registers_.begin(), registers_.end()); } bool has_reg(std::uint16_t address) const { return find_reg_index(address) >= 0; } void remove_reg(std::uint16_t address) { int i = find_reg_index(address); if (i < 0) { throw std::runtime_error("the register does not exist"); } registers_.erase(registers_.begin() + i); } RegisterType& find_reg(std::uint16_t address) { int i = find_reg_index(address); if (i < 0) { throw std::runtime_error("the register does not exist"); } return registers_[i]; } const RegisterType& find_reg(std::uint16_t address) const { int i = find_reg_index(address); if (i < 0) { throw std::runtime_error("the register does not exist"); } return registers_[i]; } void set(std::uint16_t address, Value value) { find_reg(address).value = value; } Value get(std::uint16_t address) const { return find_reg(address).value; } void reserve(std::size_t size) { registers_.reserve(size); } void clear() { registers_.clear(); } std::size_t size() const { return registers_.size(); } iterator begin() { return registers_.begin(); } const_iterator begin() const { return registers_.begin(); } iterator end() { return registers_.end(); } const_iterator end() const { return registers_.end(); } private: int find_reg_index(std::uint16_t address) const { if (!sorted_) { for (std::size_t i = 0; i < registers_.size(); i++) { if (registers_[i].address == address) { return i; } } return -1; } RegisterType search; search.address = address; auto it = std::lower_bound(registers_.begin(), registers_.end(), search); if (it == registers_.end()) return -1; if (it->address != address) return -1; return std::distance(registers_.begin(), it); } // registers are stored in a sorted vector bool sorted_ = true; std::vector registers_; }; template std::ostream& operator<<(std::ostream& out, const RegisterContainer& container) { StreamStateSaver state_saver{out}; out << "RegisterContainer{\n"; out << std::hex; out.fill('0'); for (const auto& reg : container) { unsigned address_width = sizeof(reg.address) * 2; unsigned value_width = sizeof(reg.value) * 2; out << " 0x" << std::setw(address_width) << static_cast(reg.address) << " = 0x" << std::setw(value_width) << static_cast(reg.value) << '\n'; } out << "}"; return out; } class Genesys_Register_Set { public: static constexpr unsigned MAX_REGS = 256; using ContainerType = RegisterContainer; using iterator = typename ContainerType::iterator; using const_iterator = typename ContainerType::const_iterator; // FIXME: this shouldn't live here, but in a separate struct that contains Genesys_Register_Set GenesysRegisterSetState state; enum Options { SEQUENTIAL = 1 }; Genesys_Register_Set() { registers_.reserve(MAX_REGS); } // by default the register set is sorted by address. In certain cases it's importand to send // the registers in certain order: use the SEQUENTIAL option for that Genesys_Register_Set(Options opts) : registers_{static_cast(opts)} { registers_.reserve(MAX_REGS); } const ContainerType& registers() const { return registers_; } void init_reg(std::uint16_t address, std::uint8_t default_value) { registers_.init_reg(address, default_value); } bool has_reg(std::uint16_t address) const { return registers_.has_reg(address); } void remove_reg(std::uint16_t address) { registers_.remove_reg(address); } GenesysRegister& find_reg(std::uint16_t address) { return registers_.find_reg(address); } const GenesysRegister& find_reg(std::uint16_t address) const { return registers_.find_reg(address); } GenesysRegister* find_reg_address(std::uint16_t address) { return &find_reg(address); } const GenesysRegister* find_reg_address(std::uint16_t address) const { return &find_reg(address); } void set8(std::uint16_t address, std::uint8_t value) { find_reg(address).value = value; } void set8_mask(std::uint16_t address, std::uint8_t value, std::uint8_t mask) { auto& reg = find_reg(address); reg.value = (reg.value & ~mask) | value; } void set16(std::uint16_t address, std::uint16_t value) { find_reg(address).value = (value >> 8) & 0xff; find_reg(address + 1).value = value & 0xff; } void set24(std::uint16_t address, std::uint32_t value) { find_reg(address).value = (value >> 16) & 0xff; find_reg(address + 1).value = (value >> 8) & 0xff; find_reg(address + 2).value = value & 0xff; } std::uint8_t get8(std::uint16_t address) const { return find_reg(address).value; } std::uint16_t get16(std::uint16_t address) const { return (find_reg(address).value << 8) | find_reg(address + 1).value; } std::uint32_t get24(std::uint16_t address) const { return (find_reg(address).value << 16) | (find_reg(address + 1).value << 8) | find_reg(address + 2).value; } void clear() { registers_.clear(); } std::size_t size() const { return registers_.size(); } iterator begin() { return registers_.begin(); } const_iterator begin() const { return registers_.begin(); } iterator end() { return registers_.end(); } const_iterator end() const { return registers_.end(); } private: // registers are stored in a sorted vector ContainerType registers_; }; inline std::ostream& operator<<(std::ostream& out, const Genesys_Register_Set& regs) { out << regs.registers(); return out; } template struct RegisterSetting { using ValueType = Value; using AddressType = std::uint16_t; RegisterSetting() = default; RegisterSetting(AddressType p_address, ValueType p_value) : address(p_address), value(p_value) {} RegisterSetting(AddressType p_address, ValueType p_value, ValueType p_mask) : address(p_address), value(p_value), mask(p_mask) {} AddressType address = 0; ValueType value = 0; ValueType mask = 0xff; bool operator==(const RegisterSetting& other) const { return address == other.address && value == other.value && mask == other.mask; } }; using GenesysRegisterSetting = RegisterSetting; using GenesysRegisterSetting16 = RegisterSetting; template void serialize(Stream& str, RegisterSetting& reg) { serialize(str, reg.address); serialize(str, reg.value); serialize(str, reg.mask); } template class RegisterSettingSet { public: using ValueType = Value; using SettingType = RegisterSetting; using AddressType = typename SettingType::AddressType; using container = std::vector; using iterator = typename container::iterator; using const_iterator = typename container::const_iterator; RegisterSettingSet() = default; RegisterSettingSet(std::initializer_list ilist) : registers_(ilist) {} iterator begin() { return registers_.begin(); } const_iterator begin() const { return registers_.begin(); } iterator end() { return registers_.end(); } const_iterator end() const { return registers_.end(); } SettingType& operator[](std::size_t i) { return registers_[i]; } const SettingType& operator[](std::size_t i) const { return registers_[i]; } std::size_t size() const { return registers_.size(); } bool empty() const { return registers_.empty(); } void clear() { registers_.clear(); } void push_back(SettingType reg) { registers_.push_back(reg); } void merge(const RegisterSettingSet& other) { for (const auto& reg : other) { set_value(reg.address, reg.value); } } bool has_reg(AddressType address) const { return find_reg_index(address) != -1; } SettingType& find_reg(AddressType address) { int i = find_reg_index(address); if (i < 0) { throw std::runtime_error("the register does not exist"); } return registers_[i]; } const SettingType& find_reg(AddressType address) const { int i = find_reg_index(address); if (i < 0) { throw std::runtime_error("the register does not exist"); } return registers_[i]; } ValueType get_value(AddressType address) const { int index = find_reg_index(address); if (index >= 0) { return registers_[index].value; } throw std::out_of_range("Unknown register"); } void set_value(AddressType address, ValueType value) { int index = find_reg_index(address); if (index >= 0) { registers_[index].value = value; return; } push_back(SettingType(address, value)); } template friend void serialize(std::istream& str, RegisterSettingSet& reg); template friend void serialize(std::ostream& str, RegisterSettingSet& reg); bool operator==(const RegisterSettingSet& other) const { return registers_ == other.registers_; } private: int find_reg_index(AddressType address) const { for (std::size_t i = 0; i < registers_.size(); i++) { if (registers_[i].address == address) { return i; } } return -1; } std::vector registers_; }; using GenesysRegisterSettingSet = RegisterSettingSet; using GenesysRegisterSettingSet16 = RegisterSettingSet; template std::ostream& operator<<(std::ostream& out, const RegisterSettingSet& container) { StreamStateSaver state_saver{out}; out << "RegisterSettingSet{\n"; out << std::hex; out.fill('0'); for (const auto& reg : container) { unsigned address_width = sizeof(reg.address) * 2; unsigned value_width = sizeof(reg.value) * 2; unsigned mask_width = sizeof(reg.mask) * 2; out << " 0x" << std::setw(address_width) << static_cast(reg.address) << " = 0x" << std::setw(value_width) << static_cast(reg.value) << " & 0x" << std::setw(mask_width) << static_cast(reg.mask) << '\n'; } out << "}"; return out; } template inline void serialize(std::istream& str, RegisterSettingSet& reg) { using AddressType = typename RegisterSetting::AddressType; reg.clear(); const std::size_t max_register_address = 1 << (sizeof(AddressType) * CHAR_BIT); serialize(str, reg.registers_, max_register_address); } template inline void serialize(std::ostream& str, RegisterSettingSet& reg) { serialize(str, reg.registers_); } template void apply_registers_ordered(const RegisterSettingSet& set, std::initializer_list order, F f) { for (std::uint16_t addr : order) { f(set.find_reg(addr)); } for (const auto& reg : set) { if (std::find(order.begin(), order.end(), reg.address) != order.end()) { continue; } f(reg); } } } // namespace genesys #endif // BACKEND_GENESYS_REGISTER_H backends-1.3.0/backend/genesys/register_cache.h000066400000000000000000000034031456256263500214670ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_REGISTER_CACHE_H #define BACKEND_GENESYS_REGISTER_CACHE_H #include "register.h" namespace genesys { template class RegisterCache { public: void update(std::uint16_t address, Value value) { if (regs_.has_reg(address)) { regs_.set(address, value); } else { regs_.init_reg(address, value); } } void update(const Genesys_Register_Set& regs) { for (const auto& reg : regs) { update(reg.address, reg.value); } } Value get(std::uint16_t address) const { return regs_.get(address); } private: RegisterContainer regs_; template friend std::ostream& operator<<(std::ostream& out, const RegisterCache& cache); }; template std::ostream& operator<<(std::ostream& out, const RegisterCache& cache) { out << cache.regs_; return out; } } // namespace genesys #endif // BACKEND_GENESYS_LINE_BUFFER_H backends-1.3.0/backend/genesys/row_buffer.h000066400000000000000000000113411456256263500206600ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_LINE_BUFFER_H #define BACKEND_GENESYS_LINE_BUFFER_H #include "error.h" #include #include #include #include namespace genesys { class RowBuffer { public: RowBuffer(std::size_t line_bytes) : row_bytes_{line_bytes} {} RowBuffer(const RowBuffer&) = default; RowBuffer& operator=(const RowBuffer&) = default; ~RowBuffer() = default; const std::uint8_t* get_row_ptr(std::size_t y) const { if (y >= height()) { throw SaneException("y %zu is out of range", y); } return data_.data() + row_bytes_ * get_row_index(y); } std::uint8_t* get_row_ptr(std::size_t y) { if (y >= height()) { throw SaneException("y %zu is out of range", y); } return data_.data() + row_bytes_ * get_row_index(y); } const std::uint8_t* get_front_row_ptr() const { return get_row_ptr(0); } std::uint8_t* get_front_row_ptr() { return get_row_ptr(0); } const std::uint8_t* get_back_row_ptr() const { return get_row_ptr(height() - 1); } std::uint8_t* get_back_row_ptr() { return get_row_ptr(height() - 1); } bool empty() const { return is_linear_ && first_ == last_; } bool full() { if (is_linear_) { return last_ == buffer_end_; } return first_ == last_; } bool is_linear() const { return is_linear_; } void linearize() { if (!is_linear_) { std::rotate(data_.begin(), data_.begin() + row_bytes_ * first_, data_.end()); last_ = height(); first_ = 0; is_linear_ = true; } } void pop_front() { if (empty()) { throw SaneException("Trying to pop out of empty() line buffer"); } first_++; if (first_ == last_) { first_ = 0; last_ = 0; is_linear_ = true; } else if (first_ == buffer_end_) { first_ = 0; is_linear_ = true; } } void push_front() { if (height() + 1 >= height_capacity()) { ensure_capacity(std::max(1, height() * 2)); } if (first_ == 0) { is_linear_ = false; first_ = buffer_end_; } first_--; } void pop_back() { if (empty()) { throw SaneException("Trying to pop out of empty() line buffer"); } if (last_ == 0) { last_ = buffer_end_; is_linear_ = true; } last_--; if (first_ == last_) { first_ = 0; last_ = 0; is_linear_ = true; } } void push_back() { if (height() + 1 >= height_capacity()) { ensure_capacity(std::max(1, height() * 2)); } if (last_ == buffer_end_) { is_linear_ = false; last_ = 0; } last_++; } std::size_t row_bytes() const { return row_bytes_; } std::size_t height() const { if (!is_linear_) { return last_ + buffer_end_ - first_; } return last_ - first_; } std::size_t height_capacity() const { return buffer_end_; } void clear() { first_ = 0; last_ = 0; } private: std::size_t get_row_index(std::size_t index) const { if (index >= buffer_end_ - first_) { return index - (buffer_end_ - first_); } return index + first_; } void ensure_capacity(std::size_t capacity) { if (capacity < height_capacity()) return; linearize(); data_.resize(capacity * row_bytes_); buffer_end_ = capacity; } private: std::size_t row_bytes_ = 0; std::size_t first_ = 0; std::size_t last_ = 0; std::size_t buffer_end_ = 0; bool is_linear_ = true; std::vector data_; }; } // namespace genesys #endif // BACKEND_GENESYS_LINE_BUFFER_H backends-1.3.0/backend/genesys/scanner_interface.cpp000066400000000000000000000016551456256263500225330ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "scanner_interface.h" namespace genesys { ScannerInterface::~ScannerInterface() = default; } // namespace genesys backends-1.3.0/backend/genesys/scanner_interface.h000066400000000000000000000056651456256263500222050ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_SCANNER_INTERFACE_H #define BACKEND_GENESYS_SCANNER_INTERFACE_H #include "fwd.h" #include #include #include #include namespace genesys { // Represents an interface through which all low level operations are performed. class ScannerInterface { public: virtual ~ScannerInterface(); virtual bool is_mock() const = 0; virtual std::uint8_t read_register(std::uint16_t address) = 0; virtual void write_register(std::uint16_t address, std::uint8_t value) = 0; virtual void write_registers(const Genesys_Register_Set& regs) = 0; virtual void write_0x8c(std::uint8_t index, std::uint8_t value) = 0; virtual void bulk_read_data(std::uint8_t addr, std::uint8_t* data, std::size_t size) = 0; virtual void bulk_write_data(std::uint8_t addr, std::uint8_t* data, std::size_t size) = 0; // GL646, GL841, GL843 have different ways to write to RAM and to gamma tables virtual void write_buffer(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, std::size_t size) = 0; virtual void write_gamma(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, std::size_t size) = 0; // GL845, GL846, GL847 and GL124 have a uniform way to write to RAM tables virtual void write_ahb(std::uint32_t addr, std::uint32_t size, std::uint8_t* data) = 0; virtual std::uint16_t read_fe_register(std::uint8_t address) = 0; virtual void write_fe_register(std::uint8_t address, std::uint16_t value) = 0; virtual IUsbDevice& get_usb_device() = 0; // sleeps the specified number of microseconds. Will not sleep if testing mode is enabled. virtual void sleep_us(unsigned microseconds) = 0; void sleep_ms(unsigned milliseconds) { sleep_us(milliseconds * 1000); } virtual void record_progress_message(const char* msg) = 0; virtual void record_slope_table(unsigned table_nr, const std::vector& steps) = 0; virtual void record_key_value(const std::string& key, const std::string& value) = 0; virtual void test_checkpoint(const std::string& name) = 0; }; } // namespace genesys #endif backends-1.3.0/backend/genesys/scanner_interface_usb.cpp000066400000000000000000000361051456256263500234020ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "scanner_interface_usb.h" #include "low.h" namespace genesys { ScannerInterfaceUsb::~ScannerInterfaceUsb() = default; ScannerInterfaceUsb::ScannerInterfaceUsb(Genesys_Device* dev) : dev_{dev} {} bool ScannerInterfaceUsb::is_mock() const { return false; } std::uint8_t ScannerInterfaceUsb::read_register(std::uint16_t address) { DBG_HELPER(dbg); std::uint8_t value = 0; if (dev_->model->asic_type == AsicType::GL847 || dev_->model->asic_type == AsicType::GL845 || dev_->model->asic_type == AsicType::GL846 || dev_->model->asic_type == AsicType::GL124) { std::uint8_t value2x8[2]; std::uint16_t address16 = 0x22 + (address << 8); std::uint16_t usb_value = VALUE_GET_REGISTER; if (address > 0xff) { usb_value |= 0x100; } usb_dev_.control_msg(REQUEST_TYPE_IN, REQUEST_BUFFER, usb_value, address16, 2, value2x8); // check usb link status if (value2x8[1] != 0x55) { throw SaneException(SANE_STATUS_IO_ERROR, "invalid read, scanner unplugged?"); } DBG(DBG_io, "%s (0x%02x, 0x%02x) completed\n", __func__, address, value2x8[0]); value = value2x8[0]; } else { if (address > 0xff) { throw SaneException("Invalid register address 0x%04x", address); } std::uint8_t address8 = address & 0xff; usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_SET_REGISTER, INDEX, 1, &address8); usb_dev_.control_msg(REQUEST_TYPE_IN, REQUEST_REGISTER, VALUE_READ_REGISTER, INDEX, 1, &value); } return value; } void ScannerInterfaceUsb::write_register(std::uint16_t address, std::uint8_t value) { DBG_HELPER_ARGS(dbg, "address: 0x%04x, value: 0x%02x", static_cast(address), static_cast(value)); if (dev_->model->asic_type == AsicType::GL847 || dev_->model->asic_type == AsicType::GL845 || dev_->model->asic_type == AsicType::GL846 || dev_->model->asic_type == AsicType::GL124) { std::uint8_t buffer[2]; buffer[0] = address & 0xff; buffer[1] = value; std::uint16_t usb_value = VALUE_SET_REGISTER; if (address > 0xff) { usb_value |= 0x100; } usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, usb_value, INDEX, 2, buffer); } else { if (address > 0xff) { throw SaneException("Invalid register address 0x%04x", address); } std::uint8_t address8 = address & 0xff; usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_SET_REGISTER, INDEX, 1, &address8); usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_WRITE_REGISTER, INDEX, 1, &value); } DBG(DBG_io, "%s (0x%02x, 0x%02x) completed\n", __func__, address, value); } void ScannerInterfaceUsb::write_registers(const Genesys_Register_Set& regs) { DBG_HELPER(dbg); if (dev_->model->asic_type == AsicType::GL646 || dev_->model->asic_type == AsicType::GL841) { std::uint8_t outdata[8]; std::vector buffer; buffer.reserve(regs.size() * 2); /* copy registers and values in data buffer */ for (const auto& r : regs) { buffer.push_back(r.address); buffer.push_back(r.value); } DBG(DBG_io, "%s (elems= %zu, size = %zu)\n", __func__, regs.size(), buffer.size()); if (dev_->model->asic_type == AsicType::GL646) { outdata[0] = BULK_OUT; outdata[1] = BULK_REGISTER; outdata[2] = 0x00; outdata[3] = 0x00; outdata[4] = (buffer.size() & 0xff); outdata[5] = ((buffer.size() >> 8) & 0xff); outdata[6] = ((buffer.size() >> 16) & 0xff); outdata[7] = ((buffer.size() >> 24) & 0xff); usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, VALUE_BUFFER, INDEX, sizeof(outdata), outdata); size_t write_size = buffer.size(); usb_dev_.bulk_write(buffer.data(), &write_size); } else { for (std::size_t i = 0; i < regs.size();) { std::size_t c = regs.size() - i; if (c > 32) /*32 is max on GL841. checked that.*/ c = 32; usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, VALUE_SET_REGISTER, INDEX, c * 2, buffer.data() + i * 2); i += c; } } } else { for (const auto& r : regs) { write_register(r.address, r.value); } } DBG(DBG_io, "%s: wrote %zu registers\n", __func__, regs.size()); } void ScannerInterfaceUsb::write_0x8c(std::uint8_t index, std::uint8_t value) { DBG_HELPER_ARGS(dbg, "0x%02x,0x%02x", index, value); usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_BUF_ENDACCESS, index, 1, &value); } static void bulk_read_data_send_header(UsbDevice& usb_dev, AsicType asic_type, size_t size) { DBG_HELPER(dbg); std::uint8_t outdata[8]; if (asic_type == AsicType::GL124 || asic_type == AsicType::GL845 || asic_type == AsicType::GL846 || asic_type == AsicType::GL847) { // hard coded 0x10000000 address outdata[0] = 0; outdata[1] = 0; outdata[2] = 0; outdata[3] = 0x10; } else if (asic_type == AsicType::GL841 || asic_type == AsicType::GL842 || asic_type == AsicType::GL843) { outdata[0] = BULK_IN; outdata[1] = BULK_RAM; outdata[2] = 0x82; // outdata[3] = 0x00; } else { outdata[0] = BULK_IN; outdata[1] = BULK_RAM; outdata[2] = 0x00; outdata[3] = 0x00; } /* data size to transfer */ outdata[4] = (size & 0xff); outdata[5] = ((size >> 8) & 0xff); outdata[6] = ((size >> 16) & 0xff); outdata[7] = ((size >> 24) & 0xff); usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, VALUE_BUFFER, 0x00, sizeof(outdata), outdata); } void ScannerInterfaceUsb::bulk_read_data(std::uint8_t addr, std::uint8_t* data, std::size_t size) { // currently supported: GL646, GL841, GL843, GL845, GL846, GL847, GL124 DBG_HELPER(dbg); unsigned is_addr_used = 1; unsigned has_header_before_each_chunk = 0; if (dev_->model->asic_type == AsicType::GL124 || dev_->model->asic_type == AsicType::GL845 || dev_->model->asic_type == AsicType::GL846 || dev_->model->asic_type == AsicType::GL847) { is_addr_used = 0; has_header_before_each_chunk = 1; } if (is_addr_used) { DBG(DBG_io, "%s: requesting %zu bytes from 0x%02x addr\n", __func__, size, addr); } else { DBG(DBG_io, "%s: requesting %zu bytes\n", __func__, size); } if (size == 0) return; if (is_addr_used) { usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_SET_REGISTER, 0x00, 1, &addr); } std::size_t target_size = size; std::size_t max_in_size = sanei_genesys_get_bulk_max_size(dev_->model->asic_type); if (!has_header_before_each_chunk) { bulk_read_data_send_header(usb_dev_, dev_->model->asic_type, size); } // loop until computed data size is read while (target_size > 0) { std::size_t block_size = std::min(target_size, max_in_size); if (has_header_before_each_chunk) { bulk_read_data_send_header(usb_dev_, dev_->model->asic_type, block_size); } DBG(DBG_io2, "%s: trying to read %zu bytes of data\n", __func__, block_size); usb_dev_.bulk_read(data, &block_size); DBG(DBG_io2, "%s: read %zu bytes, %zu remaining\n", __func__, block_size, target_size - block_size); target_size -= block_size; data += block_size; } } void ScannerInterfaceUsb::bulk_write_data(std::uint8_t addr, std::uint8_t* data, std::size_t len) { DBG_HELPER_ARGS(dbg, "writing %zu bytes", len); // supported: GL646, GL841, GL843 std::size_t size; std::uint8_t outdata[8]; usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_SET_REGISTER, INDEX, 1, &addr); std::size_t max_out_size = sanei_genesys_get_bulk_max_size(dev_->model->asic_type); while (len) { if (len > max_out_size) size = max_out_size; else size = len; if (dev_->model->asic_type == AsicType::GL841) { outdata[0] = BULK_OUT; outdata[1] = BULK_RAM; // both 0x82 and 0x00 works on GL841. outdata[2] = 0x82; outdata[3] = 0x00; } else { outdata[0] = BULK_OUT; outdata[1] = BULK_RAM; // 8600F uses 0x82, but 0x00 works too. 8400F uses 0x02 for certain transactions. outdata[2] = 0x00; outdata[3] = 0x00; } outdata[4] = (size & 0xff); outdata[5] = ((size >> 8) & 0xff); outdata[6] = ((size >> 16) & 0xff); outdata[7] = ((size >> 24) & 0xff); usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, VALUE_BUFFER, 0x00, sizeof(outdata), outdata); usb_dev_.bulk_write(data, &size); DBG(DBG_io2, "%s: wrote %zu bytes, %zu remaining\n", __func__, size, len - size); len -= size; data += size; } } void ScannerInterfaceUsb::write_buffer(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, std::size_t size) { DBG_HELPER_ARGS(dbg, "type: 0x%02x, addr: 0x%08x, size: 0x%08zx", type, addr, size); if (dev_->model->asic_type != AsicType::GL646 && dev_->model->asic_type != AsicType::GL841 && dev_->model->asic_type != AsicType::GL842 && dev_->model->asic_type != AsicType::GL843) { throw SaneException("Unsupported transfer mode"); } if (dev_->model->asic_type == AsicType::GL843) { write_register(0x2b, ((addr >> 4) & 0xff)); write_register(0x2a, ((addr >> 12) & 0xff)); write_register(0x29, ((addr >> 20) & 0xff)); } else { write_register(0x2b, ((addr >> 4) & 0xff)); write_register(0x2a, ((addr >> 12) & 0xff)); } bulk_write_data(type, data, size); } void ScannerInterfaceUsb::write_gamma(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, std::size_t size) { DBG_HELPER_ARGS(dbg, "type: 0x%02x, addr: 0x%08x, size: 0x%08zx", type, addr, size); if (dev_->model->asic_type != AsicType::GL841 && dev_->model->asic_type != AsicType::GL842 && dev_->model->asic_type != AsicType::GL843) { throw SaneException("Unsupported transfer mode"); } write_register(0x5b, ((addr >> 12) & 0xff)); write_register(0x5c, ((addr >> 4) & 0xff)); bulk_write_data(type, data, size); if (dev_->model->asic_type == AsicType::GL842 || dev_->model->asic_type == AsicType::GL843) { // it looks like we need to reset the address so that subsequent buffer operations work. // Most likely the MTRTBL register is to blame. write_register(0x5b, 0); write_register(0x5c, 0); } } void ScannerInterfaceUsb::write_ahb(std::uint32_t addr, std::uint32_t size, std::uint8_t* data) { DBG_HELPER_ARGS(dbg, "address: 0x%08x, size: %d", static_cast(addr), static_cast(size)); if (dev_->model->asic_type != AsicType::GL845 && dev_->model->asic_type != AsicType::GL846 && dev_->model->asic_type != AsicType::GL847 && dev_->model->asic_type != AsicType::GL124) { throw SaneException("Unsupported transfer type"); } std::uint8_t outdata[8]; outdata[0] = addr & 0xff; outdata[1] = ((addr >> 8) & 0xff); outdata[2] = ((addr >> 16) & 0xff); outdata[3] = ((addr >> 24) & 0xff); outdata[4] = (size & 0xff); outdata[5] = ((size >> 8) & 0xff); outdata[6] = ((size >> 16) & 0xff); outdata[7] = ((size >> 24) & 0xff); // write addr and size for AHB usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, VALUE_BUFFER, 0x01, 8, outdata); std::size_t max_out_size = sanei_genesys_get_bulk_max_size(dev_->model->asic_type); // write actual data std::size_t written = 0; do { std::size_t block_size = std::min(size - written, max_out_size); usb_dev_.bulk_write(data + written, &block_size); written += block_size; } while (written < size); } std::uint16_t ScannerInterfaceUsb::read_fe_register(std::uint8_t address) { DBG_HELPER(dbg); Genesys_Register_Set reg; reg.init_reg(0x50, address); // set up read address write_registers(reg); // read data std::uint16_t value = read_register(0x46) << 8; value |= read_register(0x47); DBG(DBG_io, "%s (0x%02x, 0x%04x)\n", __func__, address, value); return value; } void ScannerInterfaceUsb::write_fe_register(std::uint8_t address, std::uint16_t value) { DBG_HELPER_ARGS(dbg, "0x%02x, 0x%04x", address, value); Genesys_Register_Set reg(Genesys_Register_Set::SEQUENTIAL); reg.init_reg(0x51, address); if (dev_->model->asic_type == AsicType::GL124) { reg.init_reg(0x5d, (value / 256) & 0xff); reg.init_reg(0x5e, value & 0xff); } else { reg.init_reg(0x3a, (value / 256) & 0xff); reg.init_reg(0x3b, value & 0xff); } write_registers(reg); } IUsbDevice& ScannerInterfaceUsb::get_usb_device() { return usb_dev_; } void ScannerInterfaceUsb::sleep_us(unsigned microseconds) { if (sanei_usb_is_replay_mode_enabled()) { return; } usleep(microseconds); } void ScannerInterfaceUsb::record_progress_message(const char* msg) { sanei_usb_testing_record_message(msg); } void ScannerInterfaceUsb::record_slope_table(unsigned table_nr, const std::vector& steps) { (void) table_nr; (void) steps; } void ScannerInterfaceUsb::record_key_value(const std::string& key, const std::string& value) { (void) key; (void) value; } void ScannerInterfaceUsb::test_checkpoint(const std::string& name) { (void) name; } } // namespace genesys backends-1.3.0/backend/genesys/scanner_interface_usb.h000066400000000000000000000050561456256263500230500ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_SCANNER_INTERFACE_USB_H #define BACKEND_GENESYS_SCANNER_INTERFACE_USB_H #include "scanner_interface.h" #include "usb_device.h" namespace genesys { class ScannerInterfaceUsb : public ScannerInterface { public: ScannerInterfaceUsb(Genesys_Device* dev); ~ScannerInterfaceUsb() override; bool is_mock() const override; std::uint8_t read_register(std::uint16_t address) override; void write_register(std::uint16_t address, std::uint8_t value) override; void write_registers(const Genesys_Register_Set& regs) override; void write_0x8c(std::uint8_t index, std::uint8_t value) override; void bulk_read_data(std::uint8_t addr, std::uint8_t* data, std::size_t size) override; void bulk_write_data(std::uint8_t addr, std::uint8_t* data, std::size_t size) override; void write_buffer(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, std::size_t size) override; void write_gamma(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, std::size_t size) override; void write_ahb(std::uint32_t addr, std::uint32_t size, std::uint8_t* data) override; std::uint16_t read_fe_register(std::uint8_t address) override; void write_fe_register(std::uint8_t address, std::uint16_t value) override; IUsbDevice& get_usb_device() override; void sleep_us(unsigned microseconds) override; void record_progress_message(const char* msg) override; void record_slope_table(unsigned table_nr, const std::vector& steps) override; void record_key_value(const std::string& key, const std::string& value) override; void test_checkpoint(const std::string& name) override; private: Genesys_Device* dev_; UsbDevice usb_dev_; }; } // namespace genesys #endif backends-1.3.0/backend/genesys/sensor.cpp000066400000000000000000000132051456256263500203650ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "sensor.h" #include "utilities.h" #include namespace genesys { std::ostream& operator<<(std::ostream& out, const StaggerConfig& config) { if (config.shifts().empty()) { out << "StaggerConfig{}"; return out; } out << "StaggerConfig{ " << config.shifts().front(); for (auto it = std::next(config.shifts().begin()); it != config.shifts().end(); ++it) { out << ", " << *it; } out << " }"; return out; } std::ostream& operator<<(std::ostream& out, const FrontendType& type) { switch (type) { case FrontendType::UNKNOWN: out << "UNKNOWN"; break; case FrontendType::WOLFSON: out << "WOLFSON"; break; case FrontendType::ANALOG_DEVICES: out << "ANALOG_DEVICES"; break; case FrontendType::CANON_LIDE_80: out << "CANON_LIDE_80"; break; case FrontendType::WOLFSON_GL841: out << "WOLFSON_GL841"; break; case FrontendType::WOLFSON_GL846: out << "WOLFSON_GL846"; break; case FrontendType::ANALOG_DEVICES_GL847: out << "ANALOG_DEVICES_GL847"; break; case FrontendType::WOLFSON_GL124: out << "WOLFSON_GL124"; break; default: out << "(unknown value)"; } return out; } std::ostream& operator<<(std::ostream& out, const GenesysFrontendLayout& layout) { StreamStateSaver state_saver{out}; out << "GenesysFrontendLayout{\n" << " type: " << layout.type << '\n' << std::hex << " offset_addr[0]: " << layout.offset_addr[0] << '\n' << " offset_addr[1]: " << layout.offset_addr[1] << '\n' << " offset_addr[2]: " << layout.offset_addr[2] << '\n' << " gain_addr[0]: " << layout.gain_addr[0] << '\n' << " gain_addr[1]: " << layout.gain_addr[1] << '\n' << " gain_addr[2]: " << layout.gain_addr[2] << '\n' << '}'; return out; } std::ostream& operator<<(std::ostream& out, const Genesys_Frontend& frontend) { StreamStateSaver state_saver{out}; out << "Genesys_Frontend{\n" << " id: " << frontend.id << '\n' << " regs: " << format_indent_braced_list(4, frontend.regs) << '\n' << std::hex << " reg2[0]: " << frontend.reg2[0] << '\n' << " reg2[1]: " << frontend.reg2[1] << '\n' << " reg2[2]: " << frontend.reg2[2] << '\n' << " layout: " << format_indent_braced_list(4, frontend.layout) << '\n' << '}'; return out; } std::ostream& operator<<(std::ostream& out, const SensorExposure& exposure) { out << "SensorExposure{\n" << " red: " << exposure.red << '\n' << " green: " << exposure.green << '\n' << " blue: " << exposure.blue << '\n' << '}'; return out; } std::ostream& operator<<(std::ostream& out, const Genesys_Sensor& sensor) { out << "Genesys_Sensor{\n" << " sensor_id: " << static_cast(sensor.sensor_id) << '\n' << " full_resolution: " << sensor.full_resolution << '\n' << " optical_resolution: " << sensor.get_optical_resolution() << '\n' << " resolutions: " << format_indent_braced_list(4, sensor.resolutions) << '\n' << " channels: " << format_vector_unsigned(4, sensor.channels) << '\n' << " method: " << sensor.method << '\n' << " register_dpihw: " << sensor.register_dpihw << '\n' << " register_dpiset: " << sensor.register_dpiset << '\n' << " shading_factor: " << sensor.shading_factor << '\n' << " shading_pixel_offset: " << sensor.shading_pixel_offset << '\n' << " pixel_count_ratio: " << sensor.pixel_count_ratio << '\n' << " output_pixel_offset: " << sensor.output_pixel_offset << '\n' << " black_pixels: " << sensor.black_pixels << '\n' << " dummy_pixel: " << sensor.dummy_pixel << '\n' << " fau_gain_white_ref: " << sensor.fau_gain_white_ref << '\n' << " gain_white_ref: " << sensor.gain_white_ref << '\n' << " exposure: " << format_indent_braced_list(4, sensor.exposure) << '\n' << " exposure_lperiod: " << sensor.exposure_lperiod << '\n' << " segment_size: " << sensor.segment_size << '\n' << " segment_order: " << format_indent_braced_list(4, format_vector_unsigned(4, sensor.segment_order)) << '\n' << " stagger_x: " << sensor.stagger_x << '\n' << " stagger_y: " << sensor.stagger_y << '\n' << " use_host_side_calib: " << sensor.use_host_side_calib << '\n' << " custom_regs: " << format_indent_braced_list(4, sensor.custom_regs) << '\n' << " custom_fe_regs: " << format_indent_braced_list(4, sensor.custom_fe_regs) << '\n' << " gamma.red: " << sensor.gamma[0] << '\n' << " gamma.green: " << sensor.gamma[1] << '\n' << " gamma.blue: " << sensor.gamma[2] << '\n' << "}"; return out; } } // namespace genesys backends-1.3.0/backend/genesys/sensor.h000066400000000000000000000313511456256263500200340ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_SENSOR_H #define BACKEND_GENESYS_SENSOR_H #include "enums.h" #include "register.h" #include "serialize.h" #include "value_filter.h" #include #include namespace genesys { template struct AssignableArray : public std::array { AssignableArray() = default; AssignableArray(const AssignableArray&) = default; AssignableArray& operator=(const AssignableArray&) = default; AssignableArray& operator=(std::initializer_list init) { if (init.size() != std::array::size()) throw std::runtime_error("An array of incorrect size assigned"); std::copy(init.begin(), init.end(), std::array::begin()); return *this; } }; class StaggerConfig { public: StaggerConfig() = default; explicit StaggerConfig(std::initializer_list shifts) : shifts_{shifts} { } std::size_t max_shift() const { if (shifts_.empty()) { return 0; } return *std::max_element(shifts_.begin(), shifts_.end()); } bool empty() const { return shifts_.empty(); } std::size_t size() const { return shifts_.size(); } const std::vector& shifts() const { return shifts_; } bool operator==(const StaggerConfig& other) const { return shifts_ == other.shifts_; } private: std::vector shifts_; template friend void serialize(Stream& str, StaggerConfig& x); }; template void serialize(Stream& str, StaggerConfig& x) { serialize(str, x.shifts_); } std::ostream& operator<<(std::ostream& out, const StaggerConfig& config); enum class FrontendType : unsigned { UNKNOWN = 0, WOLFSON, ANALOG_DEVICES, CANON_LIDE_80, WOLFSON_GL841, // old code path, likely wrong calculation WOLFSON_GL846, // old code path, likely wrong calculation ANALOG_DEVICES_GL847, // old code path, likely wrong calculation WOLFSON_GL124, // old code path, likely wrong calculation }; inline void serialize(std::istream& str, FrontendType& x) { unsigned value; serialize(str, value); x = static_cast(value); } inline void serialize(std::ostream& str, FrontendType& x) { unsigned value = static_cast(x); serialize(str, value); } std::ostream& operator<<(std::ostream& out, const FrontendType& type); struct GenesysFrontendLayout { FrontendType type = FrontendType::UNKNOWN; std::array offset_addr = {}; std::array gain_addr = {}; bool operator==(const GenesysFrontendLayout& other) const { return type == other.type && offset_addr == other.offset_addr && gain_addr == other.gain_addr; } }; template void serialize(Stream& str, GenesysFrontendLayout& x) { serialize(str, x.type); serialize_newline(str); serialize(str, x.offset_addr); serialize_newline(str); serialize(str, x.gain_addr); } std::ostream& operator<<(std::ostream& out, const GenesysFrontendLayout& layout); /** @brief Data structure to set up analog frontend. The analog frontend converts analog value from image sensor to digital value. It has its own control registers which are set up with this structure. The values are written using fe_write_data. */ struct Genesys_Frontend { Genesys_Frontend() = default; // id of the frontend description AdcId id = AdcId::UNKNOWN; // all registers of the frontend. Note that the registers can hold 9-bit values RegisterSettingSet regs; // extra control registers std::array reg2 = {}; GenesysFrontendLayout layout; void set_offset(unsigned which, std::uint16_t value) { regs.set_value(layout.offset_addr[which], value); } void set_gain(unsigned which, std::uint16_t value) { regs.set_value(layout.gain_addr[which], value); } std::uint16_t get_offset(unsigned which) const { return regs.get_value(layout.offset_addr[which]); } std::uint16_t get_gain(unsigned which) const { return regs.get_value(layout.gain_addr[which]); } bool operator==(const Genesys_Frontend& other) const { return id == other.id && regs == other.regs && reg2 == other.reg2 && layout == other.layout; } }; std::ostream& operator<<(std::ostream& out, const Genesys_Frontend& frontend); template void serialize(Stream& str, Genesys_Frontend& x) { serialize(str, x.id); serialize_newline(str); serialize(str, x.regs); serialize_newline(str); serialize(str, x.reg2); serialize_newline(str); serialize(str, x.layout); } struct SensorExposure { std::uint16_t red = 0; std::uint16_t green = 0; std::uint16_t blue = 0; SensorExposure() = default; SensorExposure(std::uint16_t r, std::uint16_t g, std::uint16_t b) : red{r}, green{g}, blue{b} {} bool operator==(const SensorExposure& other) const { return red == other.red && green == other.green && blue == other.blue; } }; std::ostream& operator<<(std::ostream& out, const SensorExposure& exposure); struct Genesys_Sensor { Genesys_Sensor() = default; ~Genesys_Sensor() = default; // id of the sensor description SensorId sensor_id = SensorId::UNKNOWN; // sensor resolution in CCD pixels. Note that we may read more than one CCD pixel per logical // pixel, see ccd_pixels_per_system_pixel() unsigned full_resolution = 0; // sensor resolution in pixel values that are read by the chip. Many scanners make low // resolutions faster by configuring the timings in such a way that 1/2 or 1/4 of pixel values // that are read. If zero, then it is equal to `full_resolution`. unsigned optical_resolution = 0; // the resolution list that the sensor is usable at. ValueFilterAny resolutions = VALUE_FILTER_ANY; // the channel list that the sensor is usable at std::vector channels = { 1, 3 }; // the scan method used with the sensor ScanMethod method = ScanMethod::FLATBED; // The scanner may be setup to use a custom dpihw that does not correspond to any actual // resolution. The value zero does not set the override. unsigned register_dpihw = 0; // The scanner may be setup to use a custom dpiset value that does not correspond to any actual // resolution. The value zero does not set the override. unsigned register_dpiset = 0; // The resolution to use for shading calibration unsigned shading_resolution = 0; // How many real pixels correspond to one shading pixel that is sent to the scanner unsigned shading_factor = 1; // How many pixels the shading data is offset to the right from the acquired data. Calculated // in shading resolution. int shading_pixel_offset = 0; // This defines the ratio between logical pixel coordinates and the pixel coordinates sent to // the scanner. Ratio pixel_count_ratio = Ratio{1, 1}; // The offset in pixels in terms of scan resolution that needs to be applied to scan position. int output_pixel_offset = 0; int black_pixels = 0; // value of the dummy register int dummy_pixel = 0; // TA CCD target code (reference gain) int fau_gain_white_ref = 0; // CCD target code (reference gain) int gain_white_ref = 0; // red, green and blue initial exposure values SensorExposure exposure; int exposure_lperiod = -1; // the number of pixels in a single segment. This is counted in output resolution. unsigned segment_size = 0; // the order of the segments, if any, for the sensor. If the sensor is not segmented or uses // only single segment, this array can be empty // only on gl843 std::vector segment_order; // some CCDs use multiple arrays of pixels for double or quadruple resolution. This can result // in the following effects on the output: // - every n-th column may be shifted in a vertical direction. // - the columns themselves may be reordered in arbitrary order and may require shifting // in X direction. StaggerConfig stagger_x; StaggerConfig stagger_y; // True if calibration should be performed on host-side bool use_host_side_calib = false; GenesysRegisterSettingSet custom_regs; GenesysRegisterSettingSet custom_fe_regs; // red, green and blue gamma coefficient for default gamma tables AssignableArray gamma; unsigned get_optical_resolution() const { if (optical_resolution != 0) return optical_resolution; return full_resolution; } // how many CCD pixels are processed per system pixel time. This corresponds to CKSEL + 1 unsigned ccd_pixels_per_system_pixel() const { // same on GL646, GL841, GL843, GL846, GL847, GL124 constexpr unsigned REG_CKSEL = 0x03; return (custom_regs.get_value(0x18) & REG_CKSEL) + 1; } bool matches_channel_count(unsigned count) const { return std::find(channels.begin(), channels.end(), count) != channels.end(); } unsigned get_segment_count() const { if (segment_order.size() < 2) return 1; return segment_order.size(); } bool operator==(const Genesys_Sensor& other) const { return sensor_id == other.sensor_id && full_resolution == other.full_resolution && optical_resolution == other.optical_resolution && resolutions == other.resolutions && method == other.method && shading_resolution == other.shading_resolution && shading_factor == other.shading_factor && shading_pixel_offset == other.shading_pixel_offset && pixel_count_ratio == other.pixel_count_ratio && output_pixel_offset == other.output_pixel_offset && black_pixels == other.black_pixels && dummy_pixel == other.dummy_pixel && fau_gain_white_ref == other.fau_gain_white_ref && gain_white_ref == other.gain_white_ref && exposure == other.exposure && exposure_lperiod == other.exposure_lperiod && segment_size == other.segment_size && segment_order == other.segment_order && stagger_x == other.stagger_x && stagger_y == other.stagger_y && use_host_side_calib == other.use_host_side_calib && custom_regs == other.custom_regs && custom_fe_regs == other.custom_fe_regs && gamma == other.gamma; } }; template void serialize(Stream& str, Genesys_Sensor& x) { serialize(str, x.sensor_id); serialize(str, x.full_resolution); serialize(str, x.resolutions); serialize(str, x.method); serialize(str, x.shading_resolution); serialize(str, x.shading_factor); serialize(str, x.shading_pixel_offset); serialize(str, x.output_pixel_offset); serialize(str, x.pixel_count_ratio); serialize(str, x.black_pixels); serialize(str, x.dummy_pixel); serialize(str, x.fau_gain_white_ref); serialize(str, x.gain_white_ref); serialize_newline(str); serialize(str, x.exposure.blue); serialize(str, x.exposure.green); serialize(str, x.exposure.red); serialize(str, x.exposure_lperiod); serialize_newline(str); serialize(str, x.segment_size); serialize_newline(str); serialize(str, x.segment_order); serialize_newline(str); serialize(str, x.stagger_x); serialize_newline(str); serialize(str, x.stagger_y); serialize_newline(str); serialize(str, x.use_host_side_calib); serialize_newline(str); serialize(str, x.custom_regs); serialize_newline(str); serialize(str, x.custom_fe_regs); serialize_newline(str); serialize(str, x.gamma); serialize_newline(str); } std::ostream& operator<<(std::ostream& out, const Genesys_Sensor& sensor); } // namespace genesys #endif // BACKEND_GENESYS_SENSOR_H backends-1.3.0/backend/genesys/serialize.h000066400000000000000000000112441456256263500205110ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_SERIALIZE_H #define BACKEND_GENESYS_SERIALIZE_H #include "error.h" #include #include #include #include #include namespace genesys { // it would be best to use something like boost.serialization inline void serialize_newline(std::ostream& str) { str << '\n'; } inline void serialize_newline(std::istream& str) { (void) str; } inline void serialize(std::ostream& str, bool x) { str << static_cast(x) << " "; } inline void serialize(std::istream& str, bool& x) { unsigned v; str >> v; x = v; } inline void serialize(std::ostream& str, char x) { str << static_cast(x) << " "; } inline void serialize(std::istream& str, char& x) { int v; str >> v; x = v; } inline void serialize(std::ostream& str, unsigned char x) { str << static_cast(x) << " "; } inline void serialize(std::istream& str, unsigned char& x) { unsigned v; str >> v; x = v; } inline void serialize(std::ostream& str, signed char x) { str << static_cast(x) << " "; } inline void serialize(std::istream& str, signed char& x) { int v; str >> v; x = v; } inline void serialize(std::ostream& str, short x) { str << x << " "; } inline void serialize(std::istream& str, short& x) { str >> x; } inline void serialize(std::ostream& str, unsigned short x) { str << x << " "; } inline void serialize(std::istream& str, unsigned short& x) { str >> x; } inline void serialize(std::ostream& str, int x) { str << x << " "; } inline void serialize(std::istream& str, int& x) { str >> x; } inline void serialize(std::ostream& str, unsigned int x) { str << x << " "; } inline void serialize(std::istream& str, unsigned int& x) { str >> x; } inline void serialize(std::ostream& str, long x) { str << x << " "; } inline void serialize(std::istream& str, long& x) { str >> x; } inline void serialize(std::ostream& str, unsigned long x) { str << x << " "; } inline void serialize(std::istream& str, unsigned long& x) { str >> x; } inline void serialize(std::ostream& str, long long x) { str << x << " "; } inline void serialize(std::istream& str, long long& x) { str >> x; } inline void serialize(std::ostream& str, unsigned long long x) { str << x << " "; } inline void serialize(std::istream& str, unsigned long long& x) { str >> x; } inline void serialize(std::ostream& str, float x) { str << x << " "; } inline void serialize(std::istream& str, float& x) { str >> x; } inline void serialize(std::ostream& str, double x) { str << x << " "; } inline void serialize(std::istream& str, double& x) { str >> x; } inline void serialize(std::ostream& str, const std::string& x) { str << x << " "; } inline void serialize(std::istream& str, std::string& x) { str >> x; } template void serialize(std::ostream& str, std::vector& x) { serialize(str, x.size()); serialize_newline(str); for (auto& item : x) { serialize(str, item); serialize_newline(str); } } template void serialize(std::istream& str, std::vector& x, size_t max_size = std::numeric_limits::max()) { size_t new_size; serialize(str, new_size); if (new_size > max_size) { throw SaneException("Too large std::vector to deserialize"); } x.reserve(new_size); for (size_t i = 0; i < new_size; ++i) { T item; serialize(str, item); x.push_back(item); } } template void serialize(std::ostream& str, std::array& x) { serialize(str, x.size()); serialize_newline(str); for (auto& item : x) { serialize(str, item); serialize_newline(str); } } template void serialize(std::istream& str, std::array& x) { size_t new_size; serialize(str, new_size); if (new_size > Size) { throw SaneException("Incorrect std::array size to deserialize"); } for (auto& item : x) { serialize(str, item); } } } // namespace genesys #endif backends-1.3.0/backend/genesys/settings.cpp000066400000000000000000000171751456256263500207260ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "settings.h" #include "utilities.h" #include namespace genesys { std::ostream& operator<<(std::ostream& out, const Genesys_Settings& settings) { StreamStateSaver state_saver{out}; out << "Genesys_Settings{\n" << " xres: " << settings.xres << " yres: " << settings.yres << '\n' << " lines: " << settings.lines << '\n' << " pixels per line (actual): " << settings.pixels << '\n' << " pixels per line (requested): " << settings.requested_pixels << '\n' << " depth: " << settings.depth << '\n'; auto prec = out.precision(); out.precision(3); out << " tl_x: " << settings.tl_x << " tl_y: " << settings.tl_y << '\n'; out.precision(prec); out << " scan_mode: " << settings.scan_mode << '\n' << '}'; return out; } std::ostream& operator<<(std::ostream& out, const SetupParams& params) { StreamStateSaver state_saver{out}; bool reverse = has_flag(params.flags, ScanFlag::REVERSE); out << "SetupParams{\n" << " xres: " << params.xres << " startx: " << params.startx << " pixels per line (actual): " << params.pixels << " pixels per line (requested): " << params.requested_pixels << '\n' << " yres: " << params.yres << " lines: " << params.lines << " starty: " << params.starty << (reverse ? " (reverse)" : "") << '\n' << " depth: " << params.depth << '\n' << " channels: " << params.channels << '\n' << " scan_mode: " << params.scan_mode << '\n' << " color_filter: " << params.color_filter << '\n' << " contrast_adjustment: " << params.contrast_adjustment << '\n' << " brightness_adjustment: " << params.brightness_adjustment << '\n' << " flags: " << params.flags << '\n' << "}"; return out; } bool ScanSession::operator==(const ScanSession& other) const { return params == other.params && computed == other.computed && full_resolution == other.full_resolution && optical_resolution == other.optical_resolution && optical_pixels == other.optical_pixels && optical_pixels_raw == other.optical_pixels_raw && optical_line_count == other.optical_line_count && output_resolution == other.output_resolution && output_startx == other.output_startx && output_pixels == other.output_pixels && output_channel_bytes == other.output_channel_bytes && output_line_bytes == other.output_line_bytes && output_line_bytes_raw == other.output_line_bytes_raw && output_line_bytes_requested == other.output_line_bytes_requested && output_line_count == other.output_line_count && output_total_bytes_raw == other.output_total_bytes_raw && output_total_bytes == other.output_total_bytes && num_staggered_lines == other.num_staggered_lines && max_color_shift_lines == other.max_color_shift_lines && color_shift_lines_r == other.color_shift_lines_r && color_shift_lines_g == other.color_shift_lines_g && color_shift_lines_b == other.color_shift_lines_b && stagger_x == other.stagger_x && stagger_y == other.stagger_y && segment_count == other.segment_count && pixel_startx == other.pixel_startx && pixel_endx == other.pixel_endx && pixel_count_ratio == other.pixel_count_ratio && conseq_pixel_dist == other.conseq_pixel_dist && output_segment_pixel_group_count == other.output_segment_pixel_group_count && output_segment_start_offset == other.output_segment_start_offset && shading_pixel_offset == other.shading_pixel_offset && buffer_size_read == other.buffer_size_read && enable_ledadd == other.enable_ledadd && use_host_side_calib == other.use_host_side_calib && use_host_side_gray == other.use_host_side_gray; } std::ostream& operator<<(std::ostream& out, const ScanSession& session) { out << "ScanSession{\n" << " computed: " << session.computed << '\n' << " full_resolution: " << session.full_resolution << '\n' << " optical_resolution: " << session.optical_resolution << '\n' << " optical_pixels: " << session.optical_pixels << '\n' << " optical_pixels_raw: " << session.optical_pixels_raw << '\n' << " optical_line_count: " << session.optical_line_count << '\n' << " output_resolution: " << session.output_resolution << '\n' << " output_startx: " << session.output_startx << '\n' << " output_pixels: " << session.output_pixels << '\n' << " output_line_bytes: " << session.output_line_bytes << '\n' << " output_line_bytes_raw: " << session.output_line_bytes_raw << '\n' << " output_line_count: " << session.output_line_count << '\n' << " num_staggered_lines: " << session.num_staggered_lines << '\n' << " color_shift_lines_r: " << session.color_shift_lines_r << '\n' << " color_shift_lines_g: " << session.color_shift_lines_g << '\n' << " color_shift_lines_b: " << session.color_shift_lines_b << '\n' << " max_color_shift_lines: " << session.max_color_shift_lines << '\n' << " enable_ledadd: " << session.enable_ledadd << '\n' << " stagger_x: " << session.stagger_x << '\n' << " stagger_y: " << session.stagger_y << '\n' << " segment_count: " << session.segment_count << '\n' << " pixel_startx: " << session.pixel_startx << '\n' << " pixel_endx: " << session.pixel_endx << '\n' << " pixel_count_ratio: " << session.pixel_count_ratio << '\n' << " conseq_pixel_dist: " << session.conseq_pixel_dist << '\n' << " output_segment_pixel_group_count: " << session.output_segment_pixel_group_count << '\n' << " shading_pixel_offset: " << session.shading_pixel_offset << '\n' << " buffer_size_read: " << session.buffer_size_read << '\n' << " enable_ledadd: " << session.enable_ledadd << '\n' << " use_host_side_calib: " << session.use_host_side_calib << '\n' << " use_host_side_gray: " << session.use_host_side_gray << '\n' << " params: " << format_indent_braced_list(4, session.params) << '\n' << "}"; return out; } std::ostream& operator<<(std::ostream& out, const SANE_Parameters& params) { out << "SANE_Parameters{\n" << " format: " << static_cast(params.format) << '\n' << " last_frame: " << params.last_frame << '\n' << " bytes_per_line: " << params.bytes_per_line << '\n' << " pixels_per_line: " << params.pixels_per_line << '\n' << " lines: " << params.lines << '\n' << " depth: " << params.depth << '\n' << '}'; return out; } } // namespace genesys backends-1.3.0/backend/genesys/settings.h000066400000000000000000000310501456256263500203570ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_SETTINGS_H #define BACKEND_GENESYS_SETTINGS_H #include "enums.h" #include "serialize.h" #include "utilities.h" #include "sensor.h" namespace genesys { struct Genesys_Settings { ScanMethod scan_method = ScanMethod::FLATBED; ScanColorMode scan_mode = ScanColorMode::LINEART; // horizontal dpi unsigned xres = 0; // vertical dpi unsigned yres = 0; //x start on scan table in mm float tl_x = 0; // y start on scan table in mm float tl_y = 0; // number of lines at scan resolution unsigned int lines = 0; // number of pixels expected from the scanner unsigned int pixels = 0; // number of pixels expected by the frontend unsigned requested_pixels = 0; // bit depth of the scan unsigned int depth = 0; ColorFilter color_filter = ColorFilter::NONE; // value for contrast enhancement in the [-100..100] range int contrast = 0; // value for brightness enhancement in the [-100..100] range int brightness = 0; // cache entries expiration time int expiration_time = 0; unsigned get_channels() const { if (scan_mode == ScanColorMode::COLOR_SINGLE_PASS) return 3; return 1; } }; std::ostream& operator<<(std::ostream& out, const Genesys_Settings& settings); struct SetupParams { static constexpr unsigned NOT_SET = std::numeric_limits::max(); static constexpr unsigned NOT_SET_I = std::numeric_limits::max(); // resolution in x direction unsigned xres = NOT_SET; // resolution in y direction unsigned yres = NOT_SET; // start pixel in X direction, from dummy_pixel + 1. Counted in terms of xres. unsigned startx = NOT_SET; // start pixel in Y direction, counted according to base_ydpi unsigned starty = NOT_SET; // the number of pixels in X direction. Counted in terms of xres. // Note that each logical pixel may correspond to more than one CCD pixel, see CKSEL and // GenesysSensor::ccd_pixels_per_system_pixel() unsigned pixels = NOT_SET; // the number of pixels in the X direction as requested by the frontend. This will be different // from `pixels` if the X resolution requested by the frontend is different than the actual // resolution. This is only needed to compute dev->total_bytes_to_read. If 0, then the value // is the same as pixels. // TODO: move the computation of total_bytes_to_read to a higher layer. unsigned requested_pixels = 0; // the number of pixels in Y direction unsigned lines = NOT_SET; // the depth of the scan in bits. Allowed are 1, 8, 16 unsigned depth = NOT_SET; // the number of channels unsigned channels = NOT_SET; ScanMethod scan_method = static_cast(NOT_SET); ScanColorMode scan_mode = static_cast(NOT_SET); ColorFilter color_filter = static_cast(NOT_SET); // the values for contrast and brightness adjustment in the range of [-100..100] int contrast_adjustment = NOT_SET_I; int brightness_adjustment = NOT_SET_I; ScanFlag flags = ScanFlag::NONE; unsigned get_requested_pixels() const { if (requested_pixels != 0) { return requested_pixels; } return pixels; } void assert_valid() const { if (xres == NOT_SET || yres == NOT_SET || startx == NOT_SET || starty == NOT_SET || pixels == NOT_SET || lines == NOT_SET ||depth == NOT_SET || channels == NOT_SET || scan_method == static_cast(NOT_SET) || scan_mode == static_cast(NOT_SET) || color_filter == static_cast(NOT_SET) || contrast_adjustment == NOT_SET_I || brightness_adjustment == NOT_SET_I) { throw std::runtime_error("SetupParams are not valid"); } } bool operator==(const SetupParams& other) const { return xres == other.xres && yres == other.yres && startx == other.startx && starty == other.starty && pixels == other.pixels && requested_pixels == other.requested_pixels && lines == other.lines && depth == other.depth && channels == other.channels && scan_method == other.scan_method && scan_mode == other.scan_mode && color_filter == other.color_filter && contrast_adjustment == other.contrast_adjustment && brightness_adjustment == other.brightness_adjustment && flags == other.flags; } }; std::ostream& operator<<(std::ostream& out, const SetupParams& params); template void serialize(Stream& str, SetupParams& x) { serialize(str, x.xres); serialize(str, x.yres); serialize(str, x.startx); serialize(str, x.starty); serialize(str, x.pixels); serialize(str, x.requested_pixels); serialize(str, x.lines); serialize(str, x.depth); serialize(str, x.channels); serialize(str, x.scan_method); serialize(str, x.scan_mode); serialize(str, x.color_filter); serialize(str, x.contrast_adjustment); serialize(str, x.brightness_adjustment); serialize(str, x.flags); } struct ScanSession { SetupParams params; // whether the session setup has been computed via compute_session() bool computed = false; // specifies the full resolution of the sensor that is being used. unsigned full_resolution = 0; // the optical resolution of the sensor that is being used. unsigned optical_resolution = 0; // the number of pixels at the optical resolution, not including segmentation overhead. unsigned optical_pixels = 0; // the number of pixels at the optical resolution, including segmentation overhead. // only on gl846, g847 unsigned optical_pixels_raw = 0; // the number of optical scan lines. Equal to output_line_count on CCD scanners. unsigned optical_line_count = 0; // the resolution of the output data. unsigned output_resolution = 0; // the offset in pixels from the beginning of output data unsigned output_startx = 0; // the number of pixels in output data (after desegmentation) unsigned output_pixels = 0; // the number of bytes in the output of a channel of a single line (after desegmentation) unsigned output_channel_bytes = 0; // the number of bytes in the output of a single line (after desegmentation) unsigned output_line_bytes = 0; // the number of bytes per line in the output data from the scanner (before desegmentation) // Equal to output_line_bytes if sensor does not have segments unsigned output_line_bytes_raw = 0; // the number of bytes per line as requested by the frontend unsigned output_line_bytes_requested = 0; // the number of lines in the output of the scanner. This must be larger than the user // requested number due to line staggering and color channel shifting. unsigned output_line_count = 0; // the total number of bytes to read from the scanner (before desegmentation) unsigned output_total_bytes_raw = 0; // the total number of bytes to read from the scanner (after desegmentation) unsigned output_total_bytes = 0; // the number of staggered lines (i.e. lines that overlap during scanning due to line being // thinner than the CCD element). Computed according to stagger_y. unsigned num_staggered_lines = 0; // the number of lines that color channels shift due to different physical positions of // different color channels. unsigned max_color_shift_lines = 0; // actual line shift of the red color unsigned color_shift_lines_r = 0; // actual line shift of the green color unsigned color_shift_lines_g = 0; // actual line shift of the blue color unsigned color_shift_lines_b = 0; // The shifts that need to be applied to the output pixels in x direction. StaggerConfig stagger_x; // The shifts that need to be applied to the output pixels in y direction. StaggerConfig stagger_y; // the number of scanner segments used in the current scan unsigned segment_count = 1; // the physical pixel positions that are sent to the registers unsigned pixel_startx = 0; unsigned pixel_endx = 0; /* The following defines the ratio between logical pixel count and pixel count setting sent to the scanner. The ratio is affected by the following: - Certain scanners just like to multiply the pixel number by a multiplier that depends on the resolution. - The sensor may be configured to output one value per multiple physical pixels - The scanner will automatically average the pixels that come from the sensor using a certain ratio. */ Ratio pixel_count_ratio = Ratio{1, 1}; // Distance in pixels between consecutive pixels, e.g. between odd and even pixels. Note that // the number of segments can be large. // only on gl124, gl846, gl847 unsigned conseq_pixel_dist = 0; // The number of "even" pixels to scan. This corresponds to the number of pixels that will be // scanned from a single segment // only on gl124, gl846, gl847 unsigned output_segment_pixel_group_count = 0; // The number of bytes to skip at start of line during desegmentation. // Currently it's always zero. unsigned output_segment_start_offset = 0; // How many pixels the shading data is offset to the right from the acquired data. Calculated // in shading resolution. int shading_pixel_offset = 0; // the size of the read buffer. size_t buffer_size_read = 0; // whether to enable ledadd functionality bool enable_ledadd = false; // whether calibration should be performed host-side bool use_host_side_calib = false; // whether gray scanning should be performed host-side (scan as color and merge to gray) bool use_host_side_gray = false; void assert_computed() const { if (!computed) { throw std::runtime_error("ScanSession is not computed"); } } bool operator==(const ScanSession& other) const; }; std::ostream& operator<<(std::ostream& out, const ScanSession& session); template void serialize(Stream& str, ScanSession& x) { serialize(str, x.params); serialize_newline(str); serialize(str, x.computed); serialize(str, x.full_resolution); serialize(str, x.optical_resolution); serialize(str, x.optical_pixels); serialize(str, x.optical_pixels_raw); serialize(str, x.optical_line_count); serialize(str, x.output_resolution); serialize(str, x.output_startx); serialize(str, x.output_pixels); serialize(str, x.output_channel_bytes); serialize(str, x.output_line_bytes); serialize(str, x.output_line_bytes_raw); serialize(str, x.output_line_bytes_requested); serialize(str, x.output_line_count); serialize(str, x.output_total_bytes_raw); serialize(str, x.output_total_bytes); serialize(str, x.num_staggered_lines); serialize(str, x.max_color_shift_lines); serialize(str, x.color_shift_lines_r); serialize(str, x.color_shift_lines_g); serialize(str, x.color_shift_lines_b); serialize(str, x.stagger_x); serialize(str, x.stagger_y); serialize(str, x.segment_count); serialize(str, x.pixel_startx); serialize(str, x.pixel_endx); serialize(str, x.pixel_count_ratio); serialize(str, x.conseq_pixel_dist); serialize(str, x.output_segment_pixel_group_count); serialize(str, x.output_segment_start_offset); serialize(str, x.shading_pixel_offset); serialize(str, x.buffer_size_read); serialize(str, x.enable_ledadd); serialize(str, x.use_host_side_calib); serialize(str, x.use_host_side_gray); } std::ostream& operator<<(std::ostream& out, const SANE_Parameters& params); } // namespace genesys #endif // BACKEND_GENESYS_SETTINGS_H backends-1.3.0/backend/genesys/static_init.cpp000066400000000000000000000030761456256263500213730ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "static_init.h" #include namespace genesys { static std::unique_ptr>> s_functions_run_at_backend_exit; void add_function_to_run_at_backend_exit(const std::function& function) { if (!s_functions_run_at_backend_exit) s_functions_run_at_backend_exit.reset(new std::vector>()); s_functions_run_at_backend_exit->push_back(std::move(function)); } void run_functions_at_backend_exit() { if (s_functions_run_at_backend_exit) { for (auto it = s_functions_run_at_backend_exit->rbegin(); it != s_functions_run_at_backend_exit->rend(); ++it) { (*it)(); } s_functions_run_at_backend_exit.reset(); } } } // namespace genesys backends-1.3.0/backend/genesys/static_init.h000066400000000000000000000035421456256263500210360ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_STATIC_INIT_H #define BACKEND_GENESYS_STATIC_INIT_H #include #include namespace genesys { void add_function_to_run_at_backend_exit(const std::function& function); // calls functions added via add_function_to_run_at_backend_exit() in reverse order of being // added. void run_functions_at_backend_exit(); template class StaticInit { public: StaticInit() = default; StaticInit(const StaticInit&) = delete; StaticInit& operator=(const StaticInit&) = delete; template void init(Args&& ... args) { ptr_ = std::unique_ptr(new T(std::forward(args)...)); add_function_to_run_at_backend_exit([this](){ deinit(); }); } void deinit() { ptr_.reset(); } const T* operator->() const { return ptr_.get(); } T* operator->() { return ptr_.get(); } const T& operator*() const { return *ptr_.get(); } T& operator*() { return *ptr_.get(); } private: std::unique_ptr ptr_; }; } // namespace genesys #endif // BACKEND_GENESYS_STATIC_INIT_H backends-1.3.0/backend/genesys/status.cpp000066400000000000000000000032501456256263500203760ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "status.h" #include namespace genesys { std::ostream& operator<<(std::ostream& out, Status status) { out << "Status{\n" << " replugged: " << (status.is_replugged ? "yes" : "no") << '\n' << " is_buffer_empty: " << (status.is_buffer_empty ? "yes" : "no") << '\n' << " is_feeding_finished: " << (status.is_feeding_finished ? "yes" : "no") << '\n' << " is_scanning_finished: " << (status.is_scanning_finished ? "yes" : "no") << '\n' << " is_at_home: " << (status.is_at_home ? "yes" : "no") << '\n' << " is_lamp_on: " << (status.is_lamp_on ? "yes" : "no") << '\n' << " is_front_end_busy: " << (status.is_front_end_busy ? "yes" : "no") << '\n' << " is_motor_enabled: " << (status.is_motor_enabled ? "yes" : "no") << '\n' << "}\n"; return out; } } // namespace genesys backends-1.3.0/backend/genesys/status.h000066400000000000000000000025041456256263500200440ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_STATUS_H #define BACKEND_GENESYS_STATUS_H #include namespace genesys { /// Represents the scanner status register struct Status { bool is_replugged = false; bool is_buffer_empty = false; bool is_feeding_finished = false; bool is_scanning_finished = false; bool is_at_home = false; bool is_lamp_on = false; bool is_front_end_busy = false; bool is_motor_enabled = false; }; std::ostream& operator<<(std::ostream& out, Status status); } // namespace genesys #endif // BACKEND_GENESYS_STATUS_H backends-1.3.0/backend/genesys/tables_frontend.cpp000066400000000000000000000416701456256263500222340ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "low.h" namespace genesys { StaticInit> s_frontends; void genesys_init_frontend_tables() { s_frontends.init(); GenesysFrontendLayout wolfson_layout; wolfson_layout.type = FrontendType::WOLFSON; wolfson_layout.offset_addr = { 0x20, 0x21, 0x22 }; wolfson_layout.gain_addr = { 0x28, 0x29, 0x2a }; GenesysFrontendLayout analog_devices; analog_devices.type = FrontendType::ANALOG_DEVICES; analog_devices.offset_addr = { 0x05, 0x06, 0x07 }; analog_devices.gain_addr = { 0x02, 0x03, 0x04 }; Genesys_Frontend fe; fe.id = AdcId::WOLFSON_UMAX; fe.layout = wolfson_layout; fe.regs = { { 0x00, 0x00 }, { 0x01, 0x03 }, { 0x02, 0x05 }, { 0x03, 0x11 }, { 0x20, 0x80 }, { 0x21, 0x80 }, { 0x22, 0x80 }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 }, { 0x28, 0x02 }, { 0x29, 0x02 }, { 0x2a, 0x02 }, }; fe.reg2 = {0x00, 0x00, 0x00}; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::WOLFSON_ST12; fe.layout = wolfson_layout; fe.regs = { { 0x00, 0x00 }, { 0x01, 0x03 }, { 0x02, 0x05 }, { 0x03, 0x03 }, { 0x20, 0xc8 }, { 0x21, 0xc8 }, { 0x22, 0xc8 }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 }, { 0x28, 0x04 }, { 0x29, 0x04 }, { 0x2a, 0x04 }, }; fe.reg2 = {0x00, 0x00, 0x00}; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::WOLFSON_ST24; fe.layout = wolfson_layout; fe.regs = { { 0x00, 0x00 }, { 0x01, 0x03 }, { 0x02, 0x05 }, { 0x03, 0x21 }, { 0x20, 0xc8 }, { 0x21, 0xc8 }, { 0x22, 0xc8 }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 }, { 0x28, 0x06 }, { 0x29, 0x06 }, { 0x2a, 0x06 }, }; fe.reg2 = {0x00, 0x00, 0x00}; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::WOLFSON_5345; fe.layout = wolfson_layout; fe.regs = { { 0x00, 0x00 }, { 0x01, 0x03 }, { 0x02, 0x05 }, { 0x03, 0x12 }, { 0x20, 0xb8 }, { 0x21, 0xb8 }, { 0x22, 0xb8 }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 }, { 0x28, 0x04 }, { 0x29, 0x04 }, { 0x2a, 0x04 }, }; fe.reg2 = {0x00, 0x00, 0x00}; s_frontends->push_back(fe); // reg3=0x02 for 50-600 dpi, 0x32 (0x12 also works well) at 1200 fe = Genesys_Frontend(); fe.id = AdcId::WOLFSON_HP2400; fe.layout = wolfson_layout; fe.regs = { { 0x00, 0x00 }, { 0x01, 0x03 }, { 0x02, 0x05 }, { 0x03, 0x02 }, { 0x20, 0xb4 }, { 0x21, 0xb6 }, { 0x22, 0xbc }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 }, { 0x28, 0x06 }, { 0x29, 0x09 }, { 0x2a, 0x08 }, }; fe.reg2 = {0x00, 0x00, 0x00}; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::WOLFSON_HP2300; fe.layout = wolfson_layout; fe.regs = { { 0x00, 0x00 }, { 0x01, 0x03 }, { 0x02, 0x04 }, { 0x03, 0x02 }, { 0x20, 0xbe }, { 0x21, 0xbe }, { 0x22, 0xbe }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 }, { 0x28, 0x04 }, { 0x29, 0x04 }, { 0x2a, 0x04 }, }; fe.reg2 = {0x00, 0x00, 0x00}; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::CANON_LIDE_35; fe.layout = wolfson_layout; fe.layout.type = FrontendType::WOLFSON_GL841; fe.regs = { { 0x00, 0x00 }, { 0x01, 0x3d }, { 0x02, 0x08 }, { 0x03, 0x00 }, { 0x20, 0xe1 }, { 0x21, 0xe1 }, { 0x22, 0xe1 }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 }, { 0x28, 0x93 }, { 0x29, 0x93 }, { 0x2a, 0x93 }, }; fe.reg2 = {0x00, 0x19, 0x06}; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::CANON_LIDE_90; fe.layout = wolfson_layout; fe.layout.type = FrontendType::WOLFSON; fe.regs = { { 0x01, 0x23 }, { 0x02, 0x07 }, { 0x03, 0x29 }, { 0x06, 0x0d }, { 0x08, 0x00 }, { 0x09, 0x16 }, { 0x20, 0x4d }, { 0x21, 0x4d }, { 0x22, 0x4d }, { 0x23, 0x4d }, { 0x28, 0x14 }, { 0x29, 0x14 }, { 0x2a, 0x14 }, { 0x2b, 0x14 }, }; fe.reg2 = {0x00, 0x00, 0x00}; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::AD_XP200; fe.layout = wolfson_layout; fe.regs = { { 0x00, 0x58 }, { 0x01, 0x80 }, { 0x02, 0x00 }, { 0x03, 0x00 }, { 0x20, 0x09 }, { 0x21, 0x09 }, { 0x22, 0x09 }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 }, { 0x28, 0x09 }, { 0x29, 0x09 }, { 0x2a, 0x09 }, }; fe.reg2 = {0x00, 0x00, 0x00}; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::WOLFSON_XP300; fe.layout = wolfson_layout; fe.layout.type = FrontendType::WOLFSON_GL841; fe.regs = { { 0x00, 0x00 }, { 0x01, 0x35 }, { 0x02, 0x20 }, { 0x03, 0x14 }, { 0x20, 0xe1 }, { 0x21, 0xe1 }, { 0x22, 0xe1 }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 }, { 0x28, 0x93 }, { 0x29, 0x93 }, { 0x2a, 0x93 }, }; fe.reg2 = {0x07, 0x00, 0x00}; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::WOLFSON_HP3670; fe.layout = wolfson_layout; fe.regs = { { 0x00, 0x00 }, { 0x01, 0x03 }, { 0x02, 0x05 }, { 0x03, 0x32 }, { 0x20, 0xba }, { 0x21, 0xb8 }, { 0x22, 0xb8 }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 }, { 0x28, 0x06 }, { 0x29, 0x05 }, { 0x2a, 0x04 }, }; fe.reg2 = {0x00, 0x00, 0x00}; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::WOLFSON_DSM600; fe.layout = wolfson_layout; fe.layout.type = FrontendType::WOLFSON_GL841; fe.regs = { { 0x00, 0x00 }, { 0x01, 0x35 }, { 0x02, 0x20 }, { 0x03, 0x14 }, { 0x20, 0x85 }, { 0x21, 0x85 }, { 0x22, 0x85 }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 }, { 0x28, 0xa0 }, { 0x29, 0xa0 }, { 0x2a, 0xa0 }, }; fe.reg2 = {0x07, 0x00, 0x00}; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::CANON_LIDE_200; fe.layout = analog_devices; fe.layout.type = FrontendType::ANALOG_DEVICES_GL847; fe.regs = { { 0x00, 0x9d }, { 0x01, 0x91 }, { 0x02, 0x32 }, { 0x03, 0x04 }, { 0x04, 0x00 }, { 0x05, 0x00 }, { 0x06, 0x3f }, { 0x07, 0x00 }, }; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::CANON_LIDE_700F; fe.layout = analog_devices; fe.layout.type = FrontendType::ANALOG_DEVICES_GL847; fe.regs = { { 0x00, 0x9d }, { 0x01, 0x9e }, { 0x02, 0x2f }, { 0x03, 0x04 }, { 0x04, 0x00 }, { 0x05, 0x00 }, { 0x06, 0x3f }, { 0x07, 0x00 }, }; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::KVSS080; fe.layout = wolfson_layout; fe.regs = { { 0x00, 0x00 }, { 0x01, 0x23 }, { 0x02, 0x24 }, { 0x03, 0x0f }, { 0x20, 0x80 }, { 0x21, 0x80 }, { 0x22, 0x80 }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 }, { 0x28, 0x4b }, { 0x29, 0x4b }, { 0x2a, 0x4b }, }; fe.reg2 = {0x00,0x00,0x00}; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::G4050; fe.layout = wolfson_layout; fe.regs = { { 0x00, 0x00 }, { 0x01, 0x23 }, { 0x02, 0x24 }, { 0x03, 0x1f }, { 0x20, 0x45 }, { 0x21, 0x45 }, { 0x22, 0x45 }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 }, { 0x28, 0x4b }, { 0x29, 0x4b }, { 0x2a, 0x4b }, }; fe.reg2 = {0x00,0x00,0x00}; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::CANON_LIDE_110; fe.layout = wolfson_layout; fe.layout.type = FrontendType::WOLFSON_GL124; fe.regs = { { 0x00, 0x80 }, { 0x01, 0x8a }, { 0x02, 0x23 }, { 0x03, 0x4c }, { 0x20, 0x00 }, { 0x21, 0x00 }, { 0x22, 0x00 }, { 0x24, 0x00 }, { 0x25, 0xca }, { 0x26, 0x94 }, { 0x28, 0x00 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, }; fe.reg2 = {0x00, 0x00, 0x00}; s_frontends->push_back(fe); /** @brief GL124 special case * for GL124 based scanners, this struct is "abused" * in fact the fields are map like below to AFE registers * (from Texas Instrument or alike ?) */ fe = Genesys_Frontend(); fe.id = AdcId::CANON_LIDE_120; fe.layout = wolfson_layout; fe.layout.type = FrontendType::WOLFSON_GL124; fe.regs = { { 0x00, 0x80 }, { 0x01, 0xa3 }, { 0x02, 0x2b }, { 0x03, 0x4c }, { 0x20, 0x00 }, { 0x21, 0x00 }, { 0x22, 0x00 }, { 0x24, 0x00 }, // actual address 0x05 { 0x25, 0xca }, // actual address 0x06 { 0x26, 0x95 }, // actual address 0x07 { 0x28, 0x00 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, }; fe.reg2 = {0x00, 0x00, 0x00}; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::PLUSTEK_OPTICPRO_3600; fe.layout = wolfson_layout; fe.regs = { { 0x00, 0x70 }, { 0x01, 0x80 }, { 0x02, 0x00 }, { 0x03, 0x00 }, { 0x20, 0x00 }, { 0x21, 0x00 }, { 0x22, 0x00 }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 }, { 0x28, 0x3f }, { 0x29, 0x3d }, { 0x2a, 0x3d }, }; fe.reg2 = {0x00, 0x00, 0x00}; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::PLUSTEK_OPTICFILM_7200; fe.layout = analog_devices; fe.regs = { { 0x00, 0xf8 }, { 0x01, 0x80 }, { 0x02, 0x2e }, { 0x03, 0x17 }, { 0x04, 0x20 }, { 0x05, 0x0109 }, { 0x06, 0x01 }, { 0x07, 0x0104 }, }; fe.reg2 = {0x00, 0x00, 0x00}; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::PLUSTEK_OPTICFILM_7200I; fe.layout = analog_devices; fe.regs = { { 0x00, 0xf8 }, { 0x01, 0x80 }, { 0x02, 0x0a }, { 0x03, 0x06 }, { 0x04, 0x0f }, { 0x05, 0x56 }, { 0x06, 0x64 }, { 0x07, 0x56 }, }; fe.reg2 = {0x00, 0x00, 0x00}; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::PLUSTEK_OPTICFILM_7300; fe.layout = analog_devices; fe.regs = { { 0x00, 0xf8 }, { 0x01, 0x80 }, { 0x02, 0x10 }, { 0x03, 0x06 }, { 0x04, 0x06 }, { 0x05, 0x09 }, { 0x06, 0x0a }, { 0x07, 0x0102 }, }; fe.reg2 = {0x00, 0x00, 0x00}; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::PLUSTEK_OPTICFILM_7400; fe.layout = analog_devices; fe.regs = { { 0x00, 0xf8 }, { 0x01, 0x80 }, { 0x02, 0x1f }, { 0x03, 0x14 }, { 0x04, 0x19 }, { 0x05, 0x1b }, { 0x06, 0x1e }, { 0x07, 0x0e }, }; fe.reg2 = {0x00, 0x00, 0x00}; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::PLUSTEK_OPTICFILM_7500I; fe.layout = analog_devices; fe.regs = { { 0x00, 0xf8 }, { 0x01, 0x80 }, { 0x02, 0x1d }, { 0x03, 0x17 }, { 0x04, 0x13 }, { 0x05, 0x00 }, { 0x06, 0x00 }, { 0x07, 0x0111 }, }; fe.reg2 = {0x00, 0x00, 0x00}; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::PLUSTEK_OPTICFILM_8200I; fe.layout = analog_devices; fe.regs = { { 0x00, 0xf8 }, { 0x01, 0x80 }, { 0x02, 0x28 }, { 0x03, 0x20 }, { 0x04, 0x28 }, { 0x05, 0x2f }, { 0x06, 0x2d }, { 0x07, 0x23 }, }; fe.reg2 = {0x00, 0x00, 0x00}; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::CANON_4400F; fe.layout = wolfson_layout; fe.regs = { { 0x00, 0x00 }, { 0x01, 0x23 }, { 0x02, 0x24 }, { 0x03, 0x2f }, { 0x20, 0x6d }, { 0x21, 0x67 }, { 0x22, 0x5b }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 }, { 0x28, 0xd8 }, { 0x29, 0xd1 }, { 0x2a, 0xb9 }, }; fe.reg2 = {0x00, 0x00, 0x00}; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::CANON_5600F; fe.layout = wolfson_layout; fe.regs = { { 0x01, 0x23 }, { 0x02, 0x24 }, { 0x03, 0x2f }, { 0x06, 0x00 }, { 0x08, 0x00 }, { 0x09, 0x00 }, { 0x20, 0x60 }, { 0x21, 0x60 }, { 0x22, 0x60 }, { 0x28, 0x77 }, { 0x29, 0x77 }, { 0x2a, 0x77 }, }; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::CANON_8400F; fe.layout = wolfson_layout; fe.regs = { { 0x00, 0x00 }, { 0x01, 0x23 }, { 0x02, 0x24 }, { 0x03, 0x0f }, { 0x20, 0x60 }, { 0x21, 0x5c }, { 0x22, 0x6c }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 }, { 0x28, 0x8a }, { 0x29, 0x9f }, { 0x2a, 0xc2 }, }; fe.reg2 = {0x00, 0x00, 0x00}; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::CANON_8600F; fe.layout = wolfson_layout; fe.regs = { { 0x00, 0x00 }, { 0x01, 0x23 }, { 0x02, 0x24 }, { 0x03, 0x2f }, { 0x20, 0x67 }, { 0x21, 0x69 }, { 0x22, 0x68 }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 }, { 0x28, 0xdb }, { 0x29, 0xda }, { 0x2a, 0xd7 }, }; fe.reg2 = { 0x00, 0x00, 0x00 }; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::IMG101; fe.layout = wolfson_layout; fe.layout.type = FrontendType::WOLFSON_GL846; fe.regs = { { 0x00, 0x78 }, { 0x01, 0xf0 }, { 0x02, 0x00 }, { 0x03, 0x00 }, { 0x20, 0x00 }, { 0x21, 0x00 }, { 0x22, 0x00 }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 }, { 0x28, 0x00 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, }; fe.reg2 = {0x00, 0x00, 0x00}; s_frontends->push_back(fe); fe = Genesys_Frontend(); fe.id = AdcId::PLUSTEK_OPTICBOOK_3800; fe.layout = wolfson_layout; fe.layout.type = FrontendType::WOLFSON_GL846; fe.regs = { { 0x00, 0x78 }, { 0x01, 0xf0 }, { 0x02, 0x00 }, { 0x03, 0x00 }, { 0x20, 0x00 }, { 0x21, 0x00 }, { 0x22, 0x00 }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 }, { 0x28, 0x00 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, }; fe.reg2 = {0x00, 0x00, 0x00}; s_frontends->push_back(fe); /* reg0: control 74 data, 70 no data * reg3: offset * reg6: gain * reg0 , reg3, reg6 */ fe = Genesys_Frontend(); fe.id = AdcId::CANON_LIDE_80; fe.layout = wolfson_layout; fe.layout.type = FrontendType::CANON_LIDE_80; fe.regs = { { 0x00, 0x70 }, { 0x01, 0x16 }, { 0x02, 0x60 }, { 0x03, 0x00 }, { 0x20, 0x00 }, { 0x21, 0x00 }, { 0x22, 0x00 }, { 0x24, 0x00 }, { 0x25, 0x00 }, { 0x26, 0x00 }, { 0x28, 0x00 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, }; fe.reg2 = {0x00, 0x00, 0x00}; s_frontends->push_back(fe); } } // namespace genesys backends-1.3.0/backend/genesys/tables_gpo.cpp000066400000000000000000000236431456256263500212020ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "low.h" namespace genesys { StaticInit> s_gpo; void genesys_init_gpo_tables() { s_gpo.init(); Genesys_Gpo gpo; gpo.id = GpioId::UMAX; gpo.regs = { { 0x66, 0x11 }, { 0x67, 0x00 }, { 0x68, 0x51 }, { 0x69, 0x20 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::ST12; gpo.regs = { { 0x66, 0x11 }, { 0x67, 0x00 }, { 0x68, 0x51 }, { 0x69, 0x20 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::ST24; gpo.regs = { { 0x66, 0x00 }, { 0x67, 0x00 }, { 0x68, 0x51 }, { 0x69, 0x20 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::MD_5345; // bits 11-12 are for bipolar V-ref input voltage gpo.regs = { { 0x66, 0x30 }, { 0x67, 0x18 }, { 0x68, 0xa0 }, { 0x69, 0x18 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::HP2400; gpo.regs = { { 0x66, 0x30 }, { 0x67, 0x00 }, { 0x68, 0x31 }, { 0x69, 0x00 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::HP2300; gpo.regs = { { 0x66, 0x00 }, { 0x67, 0x00 }, { 0x68, 0x00 }, { 0x69, 0x00 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::CANON_LIDE_35; gpo.regs = { { 0x6c, 0x02 }, { 0x6d, 0x80 }, { 0x6e, 0xef }, { 0x6f, 0x80 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::CANON_LIDE_90; gpo.regs = { { 0x6b, 0x03 }, { 0x6c, 0x74 }, { 0x6d, 0x80 }, { 0x6e, 0x7f }, { 0x6f, 0xe0 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::XP200; gpo.regs = { { 0x66, 0x30 }, { 0x67, 0x00 }, { 0x68, 0xb0 }, { 0x69, 0x00 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::HP3670; gpo.regs = { { 0x66, 0x00 }, { 0x67, 0x00 }, { 0x68, 0x00 }, { 0x69, 0x00 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::XP300; gpo.regs = { { 0x6c, 0x09 }, { 0x6d, 0xc6 }, { 0x6e, 0xbb }, { 0x6f, 0x00 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::DP665; gpo.regs = { { 0x6c, 0x18 }, { 0x6d, 0x00 }, { 0x6e, 0xbb }, { 0x6f, 0x00 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::DP685; gpo.regs = { { 0x6c, 0x3f }, { 0x6d, 0x46 }, { 0x6e, 0xfb }, { 0x6f, 0x00 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::CANON_LIDE_200; gpo.regs = { { 0x6b, 0x02 }, { 0x6c, 0xf9 }, // 0xfb when idle , 0xf9/0xe9 (1200) when scanning { 0x6d, 0x20 }, { 0x6e, 0xff }, { 0x6f, 0x00 }, { 0xa6, 0x04 }, { 0xa7, 0x04 }, { 0xa8, 0x00 }, { 0xa9, 0x00 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::CANON_LIDE_700F; gpo.regs = { { 0x6b, 0x06 }, { 0x6c, 0xdb }, { 0x6d, 0xff }, { 0x6e, 0xff }, { 0x6f, 0x80 }, { 0xa6, 0x15 }, { 0xa7, 0x07 }, { 0xa8, 0x20 }, { 0xa9, 0x10 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::KVSS080; gpo.regs = { { 0x6c, 0xf5 }, { 0x6d, 0x20 }, { 0x6e, 0x7e }, { 0x6f, 0xa1 }, { 0xa6, 0x06 }, { 0xa7, 0x0f }, { 0xa8, 0x00 }, { 0xa9, 0x08 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::G4050; gpo.regs = { { 0x6c, 0x20 }, { 0x6d, 0x00 }, { 0x6e, 0xfc }, { 0x6f, 0x00 }, { 0xa6, 0x08 }, { 0xa7, 0x1e }, { 0xa8, 0x3e }, { 0xa9, 0x06 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::G4010; gpo.regs = { { 0x6c, 0x20 }, { 0x6d, 0x00 }, { 0x6e, 0xfc }, { 0x6f, 0x00 }, { 0xa6, 0x08 }, { 0xa7, 0x1e }, { 0xa8, 0x3e }, { 0xa9, 0x06 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::HP_N6310; gpo.regs = { { 0x6c, 0xa3 }, { 0x6d, 0x00 }, { 0x6e, 0x7f }, { 0x6f, 0x00 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::CANON_LIDE_110; gpo.regs = { { 0x6c, 0xfb }, { 0x6d, 0x20 }, { 0x6e, 0xff }, { 0x6f, 0x00 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::CANON_LIDE_120; gpo.regs = { { 0x6c, 0xfb }, { 0x6d, 0x20 }, { 0x6e, 0xff }, { 0x6f, 0x00 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::CANON_LIDE_210; gpo.regs = { { 0x6c, 0xfb }, { 0x6d, 0x20 }, { 0x6e, 0xff }, { 0x6f, 0x00 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::PLUSTEK_OPTICPRO_3600; gpo.regs = { { 0x6c, 0x02 }, { 0x6d, 0x00 }, { 0x6e, 0x1e }, { 0x6f, 0x80 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::PLUSTEK_OPTICFILM_7200; gpo.regs = { { 0x6b, 0x33 }, { 0x6c, 0x00 }, { 0x6d, 0x80 }, { 0x6e, 0x0c }, { 0x6f, 0x80 }, { 0x7e, 0x00 } }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::PLUSTEK_OPTICFILM_7200I; gpo.regs = { { 0x6c, 0x4c }, { 0x6d, 0x80 }, { 0x6e, 0x4c }, { 0x6f, 0x80 }, { 0xa6, 0x00 }, { 0xa7, 0x07 }, { 0xa8, 0x20 }, { 0xa9, 0x01 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::PLUSTEK_OPTICFILM_7300; gpo.regs = { { 0x6c, 0x4c }, { 0x6d, 0x00 }, { 0x6e, 0x4c }, { 0x6f, 0x80 }, { 0xa6, 0x00 }, { 0xa7, 0x07 }, { 0xa8, 0x20 }, { 0xa9, 0x01 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::PLUSTEK_OPTICFILM_7400; gpo.regs = { { 0x6b, 0x30 }, { 0x6c, 0x4c }, { 0x6d, 0x80 }, { 0x6e, 0x4c }, { 0x6f, 0x80 }, { 0xa6, 0x00 }, { 0xa7, 0x07 }, { 0xa8, 0x20 }, { 0xa9, 0x01 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::PLUSTEK_OPTICFILM_7500I; gpo.regs = { { 0x6c, 0x4c }, { 0x6d, 0x00 }, { 0x6e, 0x4c }, { 0x6f, 0x80 }, { 0xa6, 0x00 }, { 0xa7, 0x07 }, { 0xa8, 0x20 }, { 0xa9, 0x01 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::PLUSTEK_OPTICFILM_8200I; gpo.regs = { { 0x6b, 0x30 }, { 0x6c, 0x4c }, { 0x6d, 0x80 }, { 0x6e, 0x4c }, { 0x6f, 0x80 }, { 0xa6, 0x00 }, { 0xa7, 0x07 }, { 0xa8, 0x20 }, { 0xa9, 0x01 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::CANON_4400F; gpo.regs = { { 0x6c, 0x01 }, { 0x6d, 0x7f }, { 0x6e, 0xff }, { 0x6f, 0x00 }, { 0xa6, 0x00 }, { 0xa7, 0xff }, { 0xa8, 0x07 }, { 0xa9, 0x00 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::CANON_5600F; gpo.regs = { { 0x6b, 0x87 }, { 0x6c, 0xf0 }, { 0x6d, 0x5f }, { 0x6e, 0x7f }, { 0x6f, 0xa0 }, { 0xa6, 0x07 }, { 0xa7, 0x1c }, { 0xa8, 0x00 }, { 0xa9, 0x04 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::CANON_8400F; gpo.regs = { { 0x6c, 0x9a }, { 0x6d, 0xdf }, { 0x6e, 0xfe }, { 0x6f, 0x60 }, { 0xa6, 0x00 }, { 0xa7, 0x03 }, { 0xa8, 0x00 }, { 0xa9, 0x02 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::CANON_8600F; gpo.regs = { { 0x6c, 0x20 }, { 0x6d, 0x7c }, { 0x6e, 0xff }, { 0x6f, 0x00 }, { 0xa6, 0x00 }, { 0xa7, 0xff }, { 0xa8, 0x00 }, { 0xa9, 0x00 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::IMG101; gpo.regs = { { 0x6b, 0x72 }, { 0x6c, 0x1f }, { 0x6d, 0xa4 }, { 0x6e, 0x13 }, { 0x6f, 0xa7 }, { 0xa6, 0x11 }, { 0xa7, 0xff }, { 0xa8, 0x19 }, { 0xa9, 0x05 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::PLUSTEK_OPTICBOOK_3800; gpo.regs = { { 0x6b, 0x30 }, { 0x6c, 0x01 }, { 0x6d, 0x80 }, { 0x6e, 0x2d }, { 0x6f, 0x80 }, { 0xa6, 0x0c }, { 0xa7, 0x8f }, { 0xa8, 0x08 }, { 0xa9, 0x04 }, }; s_gpo->push_back(gpo); gpo = Genesys_Gpo(); gpo.id = GpioId::CANON_LIDE_80; gpo.regs = { { 0x6c, 0x28 }, { 0x6d, 0x90 }, { 0x6e, 0x75 }, { 0x6f, 0x80 }, }; s_gpo->push_back(gpo); } } // namespace genesys backends-1.3.0/backend/genesys/tables_memory_layout.cpp000066400000000000000000000147161456256263500233230ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2020 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "low.h" namespace genesys { StaticInit> s_memory_layout; void genesys_init_memory_layout_tables() { s_memory_layout.init(); MemoryLayout ml; ml.models = { ModelId::CANON_IMAGE_FORMULA_101 }; // FIXME: this scanner does not set all required registers ml.regs = { { 0xe0, 0x00 }, { 0xe1, 0xb0 }, { 0xe2, 0x05 }, { 0xe3, 0xe7 }, { 0xe4, 0x05 }, { 0xe5, 0xe8 }, { 0xe6, 0x0b }, { 0xe7, 0x1f }, { 0xe8, 0x0b }, { 0xe9, 0x20 }, }; s_memory_layout->push_back(ml); ml = MemoryLayout(); ml.models = { ModelId::PLUSTEK_OPTICBOOK_3800 }; // FIXME: this scanner does not set all required registers ml.regs = { { 0xe0, 0x00 }, { 0xe1, 0x68 }, { 0xe2, 0x03 }, { 0xe3, 0x00 }, { 0xe4, 0x03 }, { 0xe5, 0x01 }, { 0xe6, 0x05 }, { 0xe7, 0x99 }, { 0xe8, 0x05 }, { 0xe9, 0x9a }, }; s_memory_layout->push_back(ml); ml = MemoryLayout(); ml.models = { ModelId::PLUSTEK_OPTICFILM_7400, ModelId::PLUSTEK_OPTICFILM_8200I }; ml.regs = { { 0x81, 0x6d }, { 0x82, 0x00 }, { 0x83, 0x00 }, { 0x84, 0x00 }, { 0x85, 0x00 }, { 0x86, 0x00 }, { 0xd0, 0x0a }, { 0xd1, 0x0a }, { 0xd2, 0x0a }, { 0xe0, 0x00 }, { 0xe1, 0x68 }, { 0xe2, 0x03 }, { 0xe3, 0x00 }, { 0xe4, 0x03 }, { 0xe5, 0x01 }, { 0xe6, 0x05 }, { 0xe7, 0x99 }, { 0xe8, 0x05 }, { 0xe9, 0x9a }, { 0xea, 0x08 }, { 0xeb, 0x32 }, { 0xec, 0x08 }, { 0xed, 0x33 }, { 0xee, 0x0a }, { 0xef, 0xcb }, { 0xf0, 0x0a }, { 0xf1, 0xcc }, { 0xf2, 0x0d }, { 0xf3, 0x64 }, { 0xf4, 0x0d }, { 0xf5, 0x65 }, { 0xf6, 0x0f }, { 0xf7, 0xfd }, }; s_memory_layout->push_back(ml); /* On GL847 and GL124, the values of the base address for shading data must be multiplied by 8192=0x4000 to give address on AHB On GL847 and GL124, the values of the base address for scanned data must be multiplied by 1024*2=0x0800 to give address on AHB */ ml = MemoryLayout(); ml.models = { ModelId::CANON_5600F }; ml.regs = { { 0xd0, 0x0a }, { 0xe0, 0x01 }, { 0xe1, 0x2c }, { 0xe2, 0x06 }, { 0xe3, 0x4e }, { 0xe4, 0x06 }, { 0xe5, 0x4f }, { 0xe6, 0x0b }, { 0xe7, 0x71 }, { 0xe8, 0x0b }, { 0xe9, 0x72 }, { 0xea, 0x10 }, { 0xeb, 0x94 }, { 0xec, 0x10 }, { 0xed, 0x95 }, { 0xee, 0x15 }, { 0xef, 0xb7 }, { 0xf0, 0x15 }, { 0xf1, 0xb8 }, { 0xf2, 0x1a }, { 0xf3, 0xda }, { 0xf4, 0x1a }, { 0xf5, 0xdb }, { 0xf6, 0x1f }, { 0xf7, 0xfd }, { 0xf8, 0x05 } }; s_memory_layout->push_back(ml); ml = MemoryLayout(); ml.models = { ModelId::CANON_LIDE_100 }; ml.regs = { { 0xd0, 0x0a }, { 0xd1, 0x15 }, { 0xd2, 0x20 }, { 0xe0, 0x00 }, { 0xe1, 0xac }, { 0xe2, 0x02 }, { 0xe3, 0x55 }, { 0xe4, 0x02 }, { 0xe5, 0x56 }, { 0xe6, 0x03 }, { 0xe7, 0xff }, { 0xe8, 0x00 }, { 0xe9, 0xac }, { 0xea, 0x02 }, { 0xeb, 0x55 }, { 0xec, 0x02 }, { 0xed, 0x56 }, { 0xee, 0x03 }, { 0xef, 0xff }, { 0xf0, 0x00 }, { 0xf1, 0xac }, { 0xf2, 0x02 }, { 0xf3, 0x55 }, { 0xf4, 0x02 }, { 0xf5, 0x56 }, { 0xf6, 0x03 }, { 0xf7, 0xff }, }; s_memory_layout->push_back(ml); ml = MemoryLayout(); ml.models = { ModelId::CANON_LIDE_200 }; ml.regs = { { 0xd0, 0x0a }, { 0xd1, 0x1f }, { 0xd2, 0x34 }, { 0xe0, 0x01 }, { 0xe1, 0x24 }, { 0xe2, 0x02 }, { 0xe3, 0x91 }, { 0xe4, 0x02 }, { 0xe5, 0x92 }, { 0xe6, 0x03 }, { 0xe7, 0xff }, { 0xe8, 0x01 }, { 0xe9, 0x24 }, { 0xea, 0x02 }, { 0xeb, 0x91 }, { 0xec, 0x02 }, { 0xed, 0x92 }, { 0xee, 0x03 }, { 0xef, 0xff }, { 0xf0, 0x01 }, { 0xf1, 0x24 }, { 0xf2, 0x02 }, { 0xf3, 0x91 }, { 0xf4, 0x02 }, { 0xf5, 0x92 }, { 0xf6, 0x03 }, { 0xf7, 0xff }, }; s_memory_layout->push_back(ml); ml = MemoryLayout(); ml.models = { ModelId::CANON_LIDE_700F }; ml.regs = { { 0xd0, 0x0a }, { 0xd1, 0x33 }, { 0xd2, 0x5c }, { 0xe0, 0x02 }, { 0xe1, 0x14 }, { 0xe2, 0x09 }, { 0xe3, 0x09 }, { 0xe4, 0x09 }, { 0xe5, 0x0a }, { 0xe6, 0x0f }, { 0xe7, 0xff }, { 0xe8, 0x02 }, { 0xe9, 0x14 }, { 0xea, 0x09 }, { 0xeb, 0x09 }, { 0xec, 0x09 }, { 0xed, 0x0a }, { 0xee, 0x0f }, { 0xef, 0xff }, { 0xf0, 0x02 }, { 0xf1, 0x14 }, { 0xf2, 0x09 }, { 0xf3, 0x09 }, { 0xf4, 0x09 }, { 0xf5, 0x0a }, { 0xf6, 0x0f }, { 0xf7, 0xff }, }; s_memory_layout->push_back(ml); ml = MemoryLayout(); ml.models = { ModelId::CANON_LIDE_110, ModelId::CANON_LIDE_120 }; ml.regs = { { 0xd0, 0x0a }, { 0xd1, 0x15 }, { 0xd2, 0x20 }, { 0xe0, 0x00 }, { 0xe1, 0xac }, { 0xe2, 0x08 }, { 0xe3, 0x55 }, { 0xe4, 0x08 }, { 0xe5, 0x56 }, { 0xe6, 0x0f }, { 0xe7, 0xff }, { 0xe8, 0x00 }, { 0xe9, 0xac }, { 0xea, 0x08 }, { 0xeb, 0x55 }, { 0xec, 0x08 }, { 0xed, 0x56 }, { 0xee, 0x0f }, { 0xef, 0xff }, { 0xf0, 0x00 }, { 0xf1, 0xac }, { 0xf2, 0x08 }, { 0xf3, 0x55 }, { 0xf4, 0x08 }, { 0xf5, 0x56 }, { 0xf6, 0x0f }, { 0xf7, 0xff }, }; s_memory_layout->push_back(ml); ml = MemoryLayout(); ml.models = { ModelId::CANON_LIDE_210, ModelId::CANON_LIDE_220 }; ml.regs = { { 0xd0, 0x0a }, { 0xd1, 0x1f }, { 0xd2, 0x34 }, { 0xe0, 0x01 }, { 0xe1, 0x24 }, { 0xe2, 0x08 }, { 0xe3, 0x91 }, { 0xe4, 0x08 }, { 0xe5, 0x92 }, { 0xe6, 0x0f }, { 0xe7, 0xff }, { 0xe8, 0x01 }, { 0xe9, 0x24 }, { 0xea, 0x08 }, { 0xeb, 0x91 }, { 0xec, 0x08 }, { 0xed, 0x92 }, { 0xee, 0x0f }, { 0xef, 0xff }, { 0xf0, 0x01 }, { 0xf1, 0x24 }, { 0xf2, 0x08 }, { 0xf3, 0x91 }, { 0xf4, 0x08 }, { 0xf5, 0x92 }, { 0xf6, 0x0f }, { 0xf7, 0xff }, }; s_memory_layout->push_back(ml); } } // namespace genesys backends-1.3.0/backend/genesys/tables_model.cpp000066400000000000000000002456021456256263500215160ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2003 Oliver Rauch Copyright (C) 2003-2005 Henning Meier-Geinitz Copyright (C) 2004, 2005 Gerhard Jaeger Copyright (C) 2004-2013 Stéphane Voltz Copyright (C) 2005-2009 Pierre Willenbrock Copyright (C) 2007 Luke Copyright (C) 2010 Jack McGill Copyright (C) 2010 Andrey Loginov , xerox travelscan device entry Copyright (C) 2010 Chris Berry and Michael Rickmann for Plustek Opticbook 3600 support Copyright (C) 2019 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "low.h" namespace genesys { StaticInit> s_usb_devices; void genesys_init_usb_device_tables() { /* Guidelines on calibration area sizes ------------------------------------ on many scanners scanning a single line takes around 10ms. In order not to take excessive amount of time, the sizes of the calibration area are limited as follows: 2400 dpi or less: 4mm (would take ~4 seconds on 2400 dpi) 4800 dpi or less: 3mm (would take ~6 seconds on 4800 dpi) anything more: 2mm (would take ~7 seconds on 9600 dpi) Optional properties ------------------- All fields of the Genesys_Model class are defined even if they use default value, with the following exceptions: If the scanner does not have ScanMethod::TRANSPARENCY or ScanMethod::TRANSPARENCY_INFRARED, the following properties are optional: model.x_offset_ta = 0.0; model.y_offset_ta = 0.0; model.x_size_ta = 0.0; model.y_size_ta = 0.0; model.y_offset_sensor_to_ta = 0.0; model.y_offset_calib_white_ta = 0.0; model.y_size_calib_ta_mm = 0.0; If the scanner does not have ModelFlag::DARK_WHITE_CALIBRATION, then the following properties are optional: model.y_offset_calib_dark_white_mm = 0.0; model.y_size_calib_dark_white_mm = 0.0; */ s_usb_devices.init(); Genesys_Model model; model.name = "umax-astra-4500"; model.vendor = "UMAX"; model.model = "Astra 4500"; model.model_id = ModelId::UMAX_ASTRA_4500; model.asic_type = AsicType::GL646; model.resolutions = { { { ScanMethod::FLATBED }, { 1200, 600, 300, 150, 75 }, { 2400, 1200, 600, 300, 150, 75 } } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 3.5; model.y_offset = 7.5; model.x_size = 218.0; model.y_size = 299.0; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 1.0; model.x_size_calib_mm = 228.6; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 8; model.ld_shift_b = 16; model.line_mode_color_order = ColorOrder::BGR; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_UMAX; model.adc_id = AdcId::WOLFSON_UMAX; model.gpio_id = GpioId::UMAX; model.motor_id = MotorId::UMAX; model.flags = ModelFlag::UNTESTED; model.buttons = GENESYS_HAS_NO_BUTTONS; model.search_lines = 200; s_usb_devices->emplace_back(0x0638, 0x0a10, model); model = Genesys_Model(); model.name = "canon-lide-50"; model.vendor = "Canon"; model.model = "LiDE 35/40/50"; model.model_id = ModelId::CANON_LIDE_50; model.asic_type = AsicType::GL841; model.resolutions = { { { ScanMethod::FLATBED }, { 1200, 600, 300, 200, 150, 75 }, { 2400, 1200, 600, 300, 200, 150, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 0.42; model.y_offset = 7.9; model.x_size = 218.0; model.y_size = 299.0; model.y_offset_calib_white = 3.0; model.y_size_calib_mm = 3.0; model.y_offset_calib_dark_white_mm = 1.0; model.y_size_calib_dark_white_mm = 6.0; model.x_size_calib_mm = 220.13334; model.x_offset_calib_black = 0.0; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 0; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = true; model.is_sheetfed = false; model.sensor_id = SensorId::CIS_CANON_LIDE_35; model.adc_id = AdcId::CANON_LIDE_35; model.gpio_id = GpioId::CANON_LIDE_35; model.motor_id = MotorId::CANON_LIDE_35; model.flags = ModelFlag::DARK_WHITE_CALIBRATION | ModelFlag::HOST_SIDE_GRAY | ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_COPY_SW; model.search_lines = 400; s_usb_devices->emplace_back(0x04a9, 0x2213, model); model = Genesys_Model(); model.name = "panasonic-kv-ss080"; model.vendor = "Panasonic"; model.model = "KV-SS080"; model.model_id = ModelId::PANASONIC_KV_SS080; model.asic_type = AsicType::GL843; model.resolutions = { { { ScanMethod::FLATBED }, { 600, /* 500, 400,*/ 300, 200, 150, 100, 75 }, { 1200, 600, /* 500, 400, */ 300, 200, 150, 100, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 7.2; model.y_offset = 14.7; model.x_size = 217.7; model.y_size = 300.0; model.y_offset_calib_white = 9.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 227.584; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 8; model.ld_shift_b = 16; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_KVSS080; model.adc_id = AdcId::KVSS080; model.gpio_id = GpioId::KVSS080; model.motor_id = MotorId::KVSS080; model.flags = ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_SCAN_SW; model.search_lines = 100; s_usb_devices->emplace_back(0x04da, 0x100f, model); model = Genesys_Model(); model.name = "hewlett-packard-scanjet-4850c"; model.vendor = "Hewlett Packard"; model.model = "ScanJet 4850C"; model.model_id = ModelId::HP_SCANJET_4850C; model.asic_type = AsicType::GL843; model.resolutions = { { { ScanMethod::FLATBED }, { 2400, 1200, 600, 400, 300, 200, 150, 100 }, { 2400, 1200, 600, 400, 300, 200, 150, 100 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 7.9; model.y_offset = 10.0; model.x_size = 219.6; model.y_size = 314.5; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 226.9067; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 24; model.ld_shift_b = 48; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_HP_4850C; model.adc_id = AdcId::G4050; model.gpio_id = GpioId::G4050; model.motor_id = MotorId::G4050; model.flags = ModelFlag::WARMUP | ModelFlag::SHADING_REPARK | ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW; model.search_lines = 100; s_usb_devices->emplace_back(0x03f0, 0x1b05, model); model = Genesys_Model(); model.name = "hewlett-packard-scanjet-g4010"; model.vendor = "Hewlett Packard"; model.model = "ScanJet G4010"; model.model_id = ModelId::HP_SCANJET_G4010; model.asic_type = AsicType::GL843; model.resolutions = { { { ScanMethod::FLATBED }, { 2400, 1200, 600, 400, 300, 200, 150, 100 }, { 2400, 1200, 600, 400, 300, 200, 150, 100 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 8.0; model.y_offset = 13.00; model.x_size = 217.9; model.y_size = 315.0; model.y_offset_calib_white = 3.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 226.9067; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 24; model.ld_shift_b = 48; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_G4050; model.adc_id = AdcId::G4050; model.gpio_id = GpioId::G4010; model.motor_id = MotorId::G4050; model.flags = ModelFlag::WARMUP | ModelFlag::DARK_CALIBRATION | ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_TRANSP_SW; model.search_lines = 100; s_usb_devices->emplace_back(0x03f0, 0x4505, model); model = Genesys_Model(); model.name = "hewlett-packard-scanjet-g4050"; model.vendor = "Hewlett Packard"; model.model = "ScanJet G4050"; model.model_id = ModelId::HP_SCANJET_G4050; model.asic_type = AsicType::GL843; model.resolutions = { { { ScanMethod::FLATBED }, { 2400, 1200, 600, 400, 300, 200, 150, 100 }, { 2400, 1200, 600, 400, 300, 200, 150, 100 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 8.0; model.y_offset = 10.00; model.x_size = 217.9; model.y_size = 315.0; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 226.9067; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 24; model.ld_shift_b = 48; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_G4050; model.adc_id = AdcId::G4050; model.gpio_id = GpioId::G4050; model.motor_id = MotorId::G4050; model.flags = ModelFlag::WARMUP | ModelFlag::DARK_CALIBRATION | ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW; model.search_lines = 100; s_usb_devices->emplace_back(0x03f0, 0x4605, model); model = Genesys_Model(); model.name = "canon-canoscan-4400f"; model.vendor = "Canon"; model.model = "Canoscan 4400f"; model.model_id = ModelId::CANON_4400F; model.asic_type = AsicType::GL843; model.resolutions = { { { ScanMethod::FLATBED }, { 1200, 600, 300 }, { 1200, 600, 300 }, }, { { ScanMethod::TRANSPARENCY }, { 4800, 2400, 1200 }, { 9600, 4800, 2400, 1200 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 6.0; model.y_offset = 10.00; model.x_size = 215.9; model.y_size = 297.0; model.y_offset_calib_white = 2.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 241.3; model.x_offset_ta = 115.0; model.y_offset_ta = 37.0; model.x_size_ta = 35.0; model.y_size_ta = 230.0; model.y_offset_sensor_to_ta = 23.0; model.y_offset_calib_white_ta = 24.0; model.y_size_calib_ta_mm = 2.0; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 96; model.ld_shift_g = 48; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_CANON_4400F; model.adc_id = AdcId::CANON_4400F; model.gpio_id = GpioId::CANON_4400F; model.motor_id = MotorId::CANON_4400F; model.flags = ModelFlag::WARMUP | ModelFlag::DARK_CALIBRATION | ModelFlag::CUSTOM_GAMMA | ModelFlag::SHADING_REPARK | ModelFlag::UTA_NO_SECONDARY_MOTOR; model.buttons = GENESYS_HAS_TRANSP_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_PDF1_SW | GENESYS_HAS_PDF2_SW | GENESYS_HAS_PDF3_SW | GENESYS_HAS_PDF4_SW; model.search_lines = 100; s_usb_devices->emplace_back(0x04a9, 0x2228, model); model = Genesys_Model(); model.name = "canon-canoscan-8400f"; model.vendor = "Canon"; model.model = "Canoscan 8400f"; model.model_id = ModelId::CANON_8400F; model.asic_type = AsicType::GL843; model.resolutions = { { { ScanMethod::FLATBED }, { 3200, 1600, 800, 400 }, { 3200, 1600, 800, 400 }, }, { { ScanMethod::TRANSPARENCY }, { 3200, 1600, 800, 400 }, { 3200, 1600, 800, 400 }, }, { { ScanMethod::TRANSPARENCY_INFRARED }, { 3200, 1600, 800, 400 }, { 3200, 1600, 800, 400 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 5.5; model.y_offset = 17.00; model.x_size = 219.9; model.y_size = 300.0; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 3.0; model.x_offset_calib_black = 10.0; model.x_size_calib_mm = 225.425; model.x_offset_ta = 75.0; model.y_offset_ta = 45.00; model.x_size_ta = 75.0; model.y_size_ta = 230.0; model.y_offset_sensor_to_ta = 22.0; model.y_offset_calib_white_ta = 25.0; model.y_size_calib_ta_mm = 3.0; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 24; model.ld_shift_b = 48; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_CANON_8400F; model.adc_id = AdcId::CANON_8400F; model.gpio_id = GpioId::CANON_8400F; model.motor_id = MotorId::CANON_8400F; model.flags = ModelFlag::WARMUP | ModelFlag::DARK_CALIBRATION | ModelFlag::CUSTOM_GAMMA | ModelFlag::SHADING_REPARK; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW; model.search_lines = 100; s_usb_devices->emplace_back(0x04a9, 0x221e, model); model = Genesys_Model(); model.name = "canon-canoscan-8600f"; model.vendor = "Canon"; model.model = "Canoscan 8600f"; model.model_id = ModelId::CANON_8600F; model.asic_type = AsicType::GL843; model.resolutions = { { { ScanMethod::FLATBED }, { 1200, 600, 300 }, { 1200, 600, 300 }, }, { { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED }, { 4800, 2400, 1200, 600, 300 }, { 4800, 2400, 1200, 600, 300 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 24.0; model.y_offset = 10.0; model.x_size = 216.0; model.y_size = 297.0; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 8.0; model.x_size_calib_mm = 240.70734; model.x_offset_ta = 97.0; model.y_offset_ta = 38.5; model.x_size_ta = 70.0; model.y_size_ta = 230.0; model.y_offset_sensor_to_ta = 23.0; model.y_offset_calib_white_ta = 25.5; model.y_size_calib_ta_mm = 3.0; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 48; model.ld_shift_b = 96; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_CANON_8600F; model.adc_id = AdcId::CANON_8600F; model.gpio_id = GpioId::CANON_8600F; model.motor_id = MotorId::CANON_8600F; model.flags = ModelFlag::WARMUP | ModelFlag::DARK_CALIBRATION | ModelFlag::CUSTOM_GAMMA | ModelFlag::SHADING_REPARK; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW; model.search_lines = 100; s_usb_devices->emplace_back(0x04a9, 0x2229, model); model = Genesys_Model(); model.name = "canon-lide-100"; model.vendor = "Canon"; model.model = "LiDE 100"; model.model_id = ModelId::CANON_LIDE_100; model.asic_type = AsicType::GL847; model.resolutions = { { { ScanMethod::FLATBED }, { 2400, 1200, 600, 300, 200, 150 }, { 4800, 2400, 1200, 600, 300, 200, 150 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 1.1; model.y_offset = 8.3; model.x_size = 216.07; model.y_size = 299.0; model.y_offset_calib_white = 0.4233334; model.y_size_calib_mm = 3.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 217.4241; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 0; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = true; model.is_sheetfed = false; model.sensor_id = SensorId::CIS_CANON_LIDE_100; model.adc_id = AdcId::CANON_LIDE_200; model.gpio_id = GpioId::CANON_LIDE_200; model.motor_id = MotorId::CANON_LIDE_100; model.flags = ModelFlag::SIS_SENSOR | ModelFlag::DARK_CALIBRATION | ModelFlag::SHADING_REPARK | ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_FILE_SW; model.search_lines = 400; s_usb_devices->emplace_back(0x04a9, 0x1904, model); model = Genesys_Model(); model.name = "canon-lide-110"; model.vendor = "Canon"; model.model = "LiDE 110"; model.model_id = ModelId::CANON_LIDE_110; model.asic_type = AsicType::GL124; model.resolutions = { { { ScanMethod::FLATBED }, { 2400, 1200, 600, /* 400,*/ 300, 150, 100, 75 }, { 4800, 2400, 1200, 600, /* 400,*/ 300, 150, 100, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 2.2; model.y_offset = 9.0; model.x_size = 216.70; model.y_size = 300.0; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 3.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 218.7787; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 0; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = true; model.is_sheetfed = false; model.sensor_id = SensorId::CIS_CANON_LIDE_110; model.adc_id = AdcId::CANON_LIDE_110; model.gpio_id = GpioId::CANON_LIDE_110; model.motor_id = MotorId::CANON_LIDE_110; model.flags = ModelFlag::DARK_CALIBRATION | ModelFlag::SHADING_REPARK | ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_FILE_SW; model.search_lines = 400; s_usb_devices->emplace_back(0x04a9, 0x1909, model); model = Genesys_Model(); model.name = "canon-lide-120"; model.vendor = "Canon"; model.model = "LiDE 120"; model.model_id = ModelId::CANON_LIDE_120; model.asic_type = AsicType::GL124; model.resolutions = { { { ScanMethod::FLATBED }, { 2400, 1200, 600, 300, 150, 100, 75 }, { 4800, 2400, 1200, 600, 300, 150, 100, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 0.0; model.y_offset = 8.0; model.x_size = 216.0; model.y_size = 300.0; model.y_offset_calib_white = 1.0; model.y_size_calib_mm = 3.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 216.0694; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 0; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = true; model.is_sheetfed = false; model.sensor_id = SensorId::CIS_CANON_LIDE_120; model.adc_id = AdcId::CANON_LIDE_120; model.gpio_id = GpioId::CANON_LIDE_120; model.motor_id = MotorId::CANON_LIDE_120; model.flags = ModelFlag::DARK_CALIBRATION | ModelFlag::SHADING_REPARK | ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_FILE_SW; model.search_lines = 400; s_usb_devices->emplace_back(0x04a9, 0x190e, model); model = Genesys_Model(); model.name = "canon-lide-210"; model.vendor = "Canon"; model.model = "LiDE 210"; model.model_id = ModelId::CANON_LIDE_210; model.asic_type = AsicType::GL124; model.resolutions = { { { ScanMethod::FLATBED }, { 4800, 2400, 1200, 600, /* 400,*/ 300, 150, 100, 75 }, { 4800, 2400, 1200, 600, /* 400,*/ 300, 150, 100, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 2.1; model.y_offset = 8.7; model.x_size = 216.70; model.y_size = 297.5; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 3.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 218.7787; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 0; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = true; model.is_sheetfed = false; model.sensor_id = SensorId::CIS_CANON_LIDE_210; model.adc_id = AdcId::CANON_LIDE_110; model.gpio_id = GpioId::CANON_LIDE_210; model.motor_id = MotorId::CANON_LIDE_210; model.flags = ModelFlag::DARK_CALIBRATION | ModelFlag::SHADING_REPARK | ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_EXTRA_SW; model.search_lines = 400; s_usb_devices->emplace_back(0x04a9, 0x190a, model); model = Genesys_Model(); model.name = "canon-lide-220"; model.vendor = "Canon"; model.model = "LiDE 220"; model.model_id = ModelId::CANON_LIDE_220; model.asic_type = AsicType::GL124; // or a compatible one model.resolutions = { { { ScanMethod::FLATBED }, { 4800, 2400, 1200, 600, 300, 150, 100, 75 }, { 4800, 2400, 1200, 600, 300, 150, 100, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 2.1; model.y_offset = 8.7; model.x_size = 216.70; model.y_size = 297.5; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 3.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 218.7787; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 0; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = true; model.is_sheetfed = false; model.sensor_id = SensorId::CIS_CANON_LIDE_220; model.adc_id = AdcId::CANON_LIDE_110; model.gpio_id = GpioId::CANON_LIDE_210; model.motor_id = MotorId::CANON_LIDE_210; model.flags = ModelFlag::DARK_CALIBRATION | ModelFlag::SHADING_REPARK | ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_EXTRA_SW; model.search_lines = 400; s_usb_devices->emplace_back(0x04a9, 0x190f, model); model = Genesys_Model(); model.name = "canon-canoscan-5600f"; model.vendor = "Canon"; model.model = "CanoScan 5600F"; model.model_id = ModelId::CANON_5600F; model.asic_type = AsicType::GL847; model.resolutions = { { { ScanMethod::FLATBED, ScanMethod::TRANSPARENCY }, { 4800, 2400, 1200, 600, 300, /*150*/ }, { 4800, 2400, 1200, 600, 300, /*150*/ }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 1.5; model.y_offset = 10.4; model.x_size = 219.00; model.y_size = 305.0; model.y_offset_calib_white = 2.0; model.y_size_calib_mm = 2.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 220.5; model.x_offset_ta = 93.0; model.y_offset_ta = 42.4; model.x_size_ta = 35.0; model.y_size_ta = 230.0; model.y_offset_sensor_to_ta = 0; model.y_offset_calib_white_ta = 21.4; model.y_size_calib_ta_mm = 1.0; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 32; model.ld_shift_b = 64; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_CANON_5600F; model.adc_id = AdcId::CANON_5600F; model.gpio_id = GpioId::CANON_5600F; model.motor_id = MotorId::CANON_5600F; model.flags = ModelFlag::SIS_SENSOR | ModelFlag::INVERT_PIXEL_DATA | ModelFlag::DISABLE_ADC_CALIBRATION | ModelFlag::DISABLE_EXPOSURE_CALIBRATION | ModelFlag::HOST_SIDE_CALIBRATION_COMPLETE_SCAN | ModelFlag::DARK_CALIBRATION | ModelFlag::SHADING_REPARK | ModelFlag::UTA_NO_SECONDARY_MOTOR | ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_PDF1_SW | GENESYS_HAS_PDF2_SW | GENESYS_HAS_PDF3_SW | GENESYS_HAS_PDF4_SW; model.search_lines = 400; s_usb_devices->emplace_back(0x04a9, 0x1906, model); model = Genesys_Model(); model.name = "canon-lide-700f"; model.vendor = "Canon"; model.model = "LiDE 700F"; model.model_id = ModelId::CANON_LIDE_700F; model.asic_type = AsicType::GL847; model.resolutions = { { // FIXME: support 2400 ad 4800 dpi { ScanMethod::FLATBED }, { 1200, 600, 300, 200, 150, 100, 75 }, { 1200, 600, 300, 200, 150, 100, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 3.1; model.y_offset = 8.1; model.x_size = 216.07; model.y_size = 297.0; model.y_offset_calib_white = 0.4233334; model.y_size_calib_mm = 3.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 219.6254; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 0; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = true; model.is_sheetfed = false; model.sensor_id = SensorId::CIS_CANON_LIDE_700F; model.adc_id = AdcId::CANON_LIDE_700F; model.gpio_id = GpioId::CANON_LIDE_700F; model.motor_id = MotorId::CANON_LIDE_700; model.flags = ModelFlag::SIS_SENSOR | ModelFlag::DARK_CALIBRATION | ModelFlag::SHADING_REPARK | ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_FILE_SW; model.search_lines = 400; s_usb_devices->emplace_back(0x04a9, 0x1907, model); model = Genesys_Model(); model.name = "canon-lide-200"; model.vendor = "Canon"; model.model = "LiDE 200"; model.model_id = ModelId::CANON_LIDE_200; model.asic_type = AsicType::GL847; model.resolutions = { { { ScanMethod::FLATBED }, { 4800, 2400, 1200, 600, 300, 200, 150 }, { 4800, 2400, 1200, 600, 300, 200, 150 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 1.1; model.y_offset = 8.3; model.x_size = 216.07; model.y_size = 299.0; model.y_offset_calib_white = 0.4233334; model.y_size_calib_mm = 3.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 217.4241; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 0; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = true; model.is_sheetfed = false; model.sensor_id = SensorId::CIS_CANON_LIDE_200; model.adc_id = AdcId::CANON_LIDE_200; model.gpio_id = GpioId::CANON_LIDE_200; model.motor_id = MotorId::CANON_LIDE_200; model.flags = ModelFlag::SIS_SENSOR | ModelFlag::DARK_CALIBRATION | ModelFlag::SHADING_REPARK | ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_FILE_SW; model.search_lines = 400; s_usb_devices->emplace_back(0x04a9, 0x1905, model); model = Genesys_Model(); model.name = "canon-lide-60"; model.vendor = "Canon"; model.model = "LiDE 60"; model.model_id = ModelId::CANON_LIDE_60; model.asic_type = AsicType::GL841; model.resolutions = { { { ScanMethod::FLATBED }, { 1200, 600, 300, 150, 75 }, { 2400, 1200, 600, 300, 150, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 0.42; model.y_offset = 7.9; model.x_size = 218.0; model.y_size = 299.0; model.y_offset_calib_white = 3.0; model.y_size_calib_mm = 3.0; model.y_offset_calib_dark_white_mm = 1.0; model.y_size_calib_dark_white_mm = 6.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 220.13334; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 0; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = true; model.is_sheetfed = false; model.sensor_id = SensorId::CIS_CANON_LIDE_60; model.adc_id = AdcId::CANON_LIDE_35; model.gpio_id = GpioId::CANON_LIDE_35; model.motor_id = MotorId::CANON_LIDE_60; model.flags = ModelFlag::DARK_WHITE_CALIBRATION | ModelFlag::HOST_SIDE_GRAY | ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_COPY_SW | GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_EMAIL_SW; model.search_lines = 400; s_usb_devices->emplace_back(0x04a9, 0x221c, model); model = Genesys_Model(); model.name = "canon-lide-80"; model.vendor = "Canon"; model.model = "LiDE 80"; model.model_id = ModelId::CANON_LIDE_80; model.asic_type = AsicType::GL841; model.resolutions = { { { ScanMethod::FLATBED }, { 1200, 600, 300, 150, 100, 75 }, { 2400, 1200, 600, 300, 150, 100, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 0.42; model.y_offset = 7.90; model.x_size = 216.07; model.y_size = 299.0; model.y_offset_calib_white = 4.5; model.y_size_calib_mm = 3.0; model.y_offset_calib_dark_white_mm = 1.0; model.y_size_calib_dark_white_mm = 6.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 216.7467; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 0; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = true; model.is_sheetfed = false; model.sensor_id = SensorId::CIS_CANON_LIDE_80; model.adc_id = AdcId::CANON_LIDE_80; model.gpio_id = GpioId::CANON_LIDE_80; model.motor_id = MotorId::CANON_LIDE_80; model.flags = ModelFlag::DARK_WHITE_CALIBRATION | ModelFlag::HOST_SIDE_GRAY | ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_COPY_SW; model.search_lines = 400; s_usb_devices->emplace_back(0x04a9, 0x2214, model); model = Genesys_Model(); model.name = "canon-lide-90"; model.vendor = "Canon"; model.model = "LiDE 90"; model.model_id = ModelId::CANON_LIDE_90; model.asic_type = AsicType::GL842; model.resolutions = { { { ScanMethod::FLATBED }, { 2400, 1200, 600, 300 }, { 2400, 1200, 600, 300 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 3.50; model.y_offset = 9.0; model.x_size = 219.0; model.y_size = 299.0; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 2.0; model.y_offset_calib_dark_white_mm = 0.0; model.y_size_calib_dark_white_mm = 0.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 221.5; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 0; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = true; model.is_sheetfed = false; model.sensor_id = SensorId::CIS_CANON_LIDE_90; model.adc_id = AdcId::CANON_LIDE_90; model.gpio_id = GpioId::CANON_LIDE_90; model.motor_id = MotorId::CANON_LIDE_90; model.flags = ModelFlag::DISABLE_ADC_CALIBRATION | ModelFlag::HOST_SIDE_CALIBRATION_COMPLETE_SCAN | ModelFlag::USE_CONSTANT_FOR_DARK_CALIBRATION | ModelFlag::DISABLE_FAST_FEEDING | ModelFlag::SHADING_REPARK | ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_COPY_SW; model.search_lines = 400; s_usb_devices->emplace_back(0x04a9, 0x1900, model); model = Genesys_Model(); model.name = "hewlett-packard-scanjet-2300c"; model.vendor = "Hewlett Packard"; model.model = "ScanJet 2300c"; model.model_id = ModelId::HP_SCANJET_2300C; model.asic_type = AsicType::GL646; model.resolutions = { { { ScanMethod::FLATBED }, { 600, 300, 150, 75 }, { 1200, 600, 300, 150, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 6.5; model.y_offset = 8; model.x_size = 215.9; model.y_size = 295.0; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 1.0; model.x_size_calib_mm = 227.2454; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 32; model.ld_shift_g = 16; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_HP2300; model.adc_id = AdcId::WOLFSON_HP2300; model.gpio_id = GpioId::HP2300; model.motor_id = MotorId::HP2300; model.flags = ModelFlag::GAMMA_14BIT | ModelFlag::DARK_CALIBRATION | ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW; model.search_lines = 132; s_usb_devices->emplace_back(0x03f0, 0x0901, model); model = Genesys_Model(); model.name = "hewlett-packard-scanjet-2400c"; model.vendor = "Hewlett Packard"; model.model = "ScanJet 2400c"; model.model_id = ModelId::HP_SCANJET_2400C; model.asic_type = AsicType::GL646; model.resolutions = { { { ScanMethod::FLATBED }, { 1200, 600, 300, 150, 100, 50 }, { 1200, 600, 300, 150, 100, 50 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 6.5; model.y_offset = 2.5; model.x_size = 220.0; model.y_size = 297.2; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 2.0; // FIXME: check if white area is really so small model.x_offset_calib_black = 1.0; model.x_size_calib_mm = 230.1241; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 24; model.ld_shift_b = 48; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_HP2400; model.adc_id = AdcId::WOLFSON_HP2400; model.gpio_id = GpioId::HP2400; model.motor_id = MotorId::HP2400; model.flags = ModelFlag::GAMMA_14BIT | ModelFlag::DARK_CALIBRATION | ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_SCAN_SW; model.search_lines = 132; s_usb_devices->emplace_back(0x03f0, 0x0a01, model); model = Genesys_Model(); model.name = "visioneer-strobe-xp200"; model.vendor = "Visioneer"; model.model = "Strobe XP200"; model.model_id = ModelId::VISIONEER_STROBE_XP200; model.asic_type = AsicType::GL646; model.resolutions = { { { ScanMethod::FLATBED }, { 600, 300, 200, 100, 75 }, { 600, 300, 200, 100, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 0.5; model.y_offset = 16.0; model.x_size = 215.9; model.y_size = 297.2; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 220.1334; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 0; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = true; model.is_sheetfed = true; model.sensor_id = SensorId::CIS_XP200; model.adc_id = AdcId::AD_XP200; model.gpio_id = GpioId::XP200; model.motor_id = MotorId::XP200; model.flags = ModelFlag::GAMMA_14BIT | ModelFlag::CUSTOM_GAMMA | ModelFlag::DARK_CALIBRATION; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE; model.search_lines = 132; s_usb_devices->emplace_back(0x04a7, 0x0426, model); model = Genesys_Model(); model.name = "hewlett-packard-scanjet-3670"; model.vendor = "Hewlett Packard"; model.model = "ScanJet 3670"; model.model_id = ModelId::HP_SCANJET_3670; model.asic_type = AsicType::GL646; model.resolutions = { { { ScanMethod::FLATBED }, { 1200, 600, 300, 150, 100, 75, 50 }, { 1200, 600, 300, 150, 100, 75, 50 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 8.5; model.y_offset = 11.0; model.x_size = 215.9; model.y_size = 300.0; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 1.0; model.x_size_calib_mm = 230.1241; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 24; model.ld_shift_b = 48; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_HP3670; model.adc_id = AdcId::WOLFSON_HP3670; model.gpio_id = GpioId::HP3670; model.motor_id = MotorId::HP3670; model.flags = ModelFlag::WARMUP | ModelFlag::GAMMA_14BIT | ModelFlag::DARK_CALIBRATION | ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_SCAN_SW; model.search_lines = 200; s_usb_devices->emplace_back(0x03f0, 0x1405, model); model = Genesys_Model(); model.name = "plustek-opticpro-st12"; model.vendor = "Plustek"; model.model = "OpticPro ST12"; model.model_id = ModelId::PLUSTEK_OPTICPRO_ST12; model.asic_type = AsicType::GL646; model.resolutions = { { { ScanMethod::FLATBED }, { 600, 300, 150, 75 }, { 1200, 600, 300, 150, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 3.5; model.y_offset = 7.5; model.x_size = 218.0; model.y_size = 299.0; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 1.0; model.x_size_calib_mm = 229.2774; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 8; model.ld_shift_b = 16; model.line_mode_color_order = ColorOrder::BGR; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_ST12; model.adc_id = AdcId::WOLFSON_ST12; model.gpio_id = GpioId::ST12; model.motor_id = MotorId::UMAX; model.flags = ModelFlag::UNTESTED | ModelFlag::GAMMA_14BIT; model.buttons = GENESYS_HAS_NO_BUTTONS; model.search_lines = 200; s_usb_devices->emplace_back(0x07b3, 0x0600, model); model = Genesys_Model(); model.name = "plustek-opticpro-st24"; model.vendor = "Plustek"; model.model = "OpticPro ST24"; model.model_id = ModelId::PLUSTEK_OPTICPRO_ST24; model.asic_type = AsicType::GL646; model.resolutions = { { { ScanMethod::FLATBED }, { 1200, 600, 300, 150, 75 }, { 2400, 1200, 600, 300, 150, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 3.5; model.y_offset = 7.5; // FIXME: incorrect, needs updating model.x_size = 218.0; model.y_size = 299.0; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 1.0; model.x_size_calib_mm = 228.6; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 8; model.ld_shift_b = 16; model.line_mode_color_order = ColorOrder::BGR; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_ST24; model.adc_id = AdcId::WOLFSON_ST24; model.gpio_id = GpioId::ST24; model.motor_id = MotorId::ST24; model.flags = ModelFlag::UNTESTED | ModelFlag::GAMMA_14BIT | ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_NO_BUTTONS; model.search_lines = 200; s_usb_devices->emplace_back(0x07b3, 0x0601, model); model = Genesys_Model(); model.name = "medion-md5345-model"; model.vendor = "Medion"; model.model = "MD5345/MD6228/MD6471"; model.model_id = ModelId::MEDION_MD5345; model.asic_type = AsicType::GL646; model.resolutions = { { { ScanMethod::FLATBED }, { 1200, 600, 400, 300, 200, 150, 100, 75, 50 }, { 2400, 1200, 600, 400, 300, 200, 150, 100, 75, 50 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 0.30; model.y_offset = 4.0; // FIXME: incorrect, needs updating model.x_size = 220.0; model.y_size = 296.4; model.y_offset_calib_white = 0.00; model.y_size_calib_mm = 2.0; model.x_offset_calib_black = 0.00; model.x_size_calib_mm = 230.1241; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 96; model.ld_shift_g = 48; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_5345; model.adc_id = AdcId::WOLFSON_5345; model.gpio_id = GpioId::MD_5345; model.motor_id = MotorId::MD_5345; model.flags = ModelFlag::WARMUP | ModelFlag::GAMMA_14BIT | ModelFlag::DARK_CALIBRATION | ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_POWER_SW | GENESYS_HAS_OCR_SW | GENESYS_HAS_SCAN_SW; model.search_lines = 200; s_usb_devices->emplace_back(0x0461, 0x0377, model); model = Genesys_Model(); model.name = "visioneer-strobe-xp300"; model.vendor = "Visioneer"; model.model = "Strobe XP300"; model.model_id = ModelId::VISIONEER_STROBE_XP300; model.asic_type = AsicType::GL841; model.resolutions = { { { ScanMethod::FLATBED }, { 600, 300, 150, 75 }, { 600, 300, 150, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 0.0; model.y_offset = 1.0; model.x_size = 435.0; model.y_size = 511; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 433.4934; model.post_scan = 26.5; // this is larger than needed -- accounts for second sensor head, which is a calibration item model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 0; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = true; model.is_sheetfed = true; model.sensor_id = SensorId::CCD_XP300; model.adc_id = AdcId::WOLFSON_XP300; model.gpio_id = GpioId::XP300; model.motor_id = MotorId::XP300; model.flags = ModelFlag::DARK_CALIBRATION | ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE; model.search_lines = 400; s_usb_devices->emplace_back(0x04a7, 0x0474, model); model = Genesys_Model(); model.name = "syscan-docketport-665"; model.vendor = "Syscan/Ambir"; model.model = "DocketPORT 665"; model.model_id = ModelId::SYSCAN_DOCKETPORT_665; model.asic_type = AsicType::GL841; model.resolutions = { { { ScanMethod::FLATBED }, { 600, 300, 150, 75 }, { 1200, 600, 300, 150, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 0.0; model.y_offset = 0.0; model.x_size = 108.0; model.y_size = 511; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 105.664; model.post_scan = 17.5; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 0; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = true; model.is_sheetfed = true; model.sensor_id = SensorId::CCD_DP665; model.adc_id = AdcId::WOLFSON_XP300; model.gpio_id = GpioId::DP665; model.motor_id = MotorId::DP665; model.flags = ModelFlag::DARK_CALIBRATION | ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE; model.search_lines = 400; s_usb_devices->emplace_back(0x0a82, 0x4803, model); model = Genesys_Model(); model.name = "visioneer-roadwarrior"; model.vendor = "Visioneer"; model.model = "Readwarrior"; model.model_id = ModelId::VISIONEER_ROADWARRIOR; model.asic_type = AsicType::GL841; model.resolutions = { { { ScanMethod::FLATBED }, { 600, 300, 150, 75 }, { 1200, 600, 300, 150, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 0.0; model.y_offset = 0.0; model.x_size = 220.0; model.y_size = 511; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 220.1334; model.post_scan = 16.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 0; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = true; model.is_sheetfed = true; model.sensor_id = SensorId::CCD_ROADWARRIOR; model.adc_id = AdcId::WOLFSON_XP300; model.gpio_id = GpioId::DP665; model.motor_id = MotorId::ROADWARRIOR; model.flags = ModelFlag::CUSTOM_GAMMA | ModelFlag::DARK_CALIBRATION; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE; model.search_lines = 400; s_usb_devices->emplace_back(0x04a7, 0x0494, model); model = Genesys_Model(); model.name = "syscan-docketport-465"; model.vendor = "Syscan"; model.model = "DocketPORT 465"; model.model_id = ModelId::SYSCAN_DOCKETPORT_465; model.asic_type = AsicType::GL841; model.resolutions = { { { ScanMethod::FLATBED }, { 600, 300, 150, 75 }, { 1200, 600, 300, 150, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 0.0; model.y_offset = 0.0; model.x_size = 220.0; model.y_size = 511; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 220.1334; model.post_scan = 16.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 0; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = true; model.is_sheetfed = true; model.sensor_id = SensorId::CCD_ROADWARRIOR; model.adc_id = AdcId::WOLFSON_XP300; model.gpio_id = GpioId::DP665; model.motor_id = MotorId::ROADWARRIOR; model.flags = ModelFlag::DISABLE_ADC_CALIBRATION | ModelFlag::DISABLE_EXPOSURE_CALIBRATION | ModelFlag::DISABLE_SHADING_CALIBRATION | ModelFlag::CUSTOM_GAMMA | ModelFlag::UNTESTED; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW; model.search_lines = 400; s_usb_devices->emplace_back(0x0a82, 0x4802, model); model = Genesys_Model(); model.name = "visioneer-xp100-revision3"; model.vendor = "Visioneer"; model.model = "XP100 Revision 3"; model.model_id = ModelId::VISIONEER_STROBE_XP100_REVISION3; model.asic_type = AsicType::GL841; model.resolutions = { { { ScanMethod::FLATBED }, { 600, 300, 150, 75 }, { 1200, 600, 300, 150, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 0.0; model.y_offset = 0.0; model.x_size = 220.0; model.y_size = 511; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 220.1334; model.post_scan = 16.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 0; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = true; model.is_sheetfed = true; model.sensor_id = SensorId::CCD_ROADWARRIOR; model.adc_id = AdcId::WOLFSON_XP300; model.gpio_id = GpioId::DP665; model.motor_id = MotorId::ROADWARRIOR; model.flags = ModelFlag::CUSTOM_GAMMA | ModelFlag::DARK_CALIBRATION; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE; model.search_lines = 400; s_usb_devices->emplace_back(0x04a7, 0x049b, model); model = Genesys_Model(); model.name = "pentax-dsmobile-600"; model.vendor = "Pentax"; model.model = "DSmobile 600"; model.model_id = ModelId::PENTAX_DSMOBILE_600; model.asic_type = AsicType::GL841; model.resolutions = { { { ScanMethod::FLATBED }, { 600, 300, 150, 75 }, { 1200, 600, 300, 150, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 0.0; model.y_offset = 0.0; model.x_size = 220.0; model.y_size = 511; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 220.1334; model.post_scan = 16.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 0; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = true; model.is_sheetfed = true; model.sensor_id = SensorId::CCD_DSMOBILE600; model.adc_id = AdcId::WOLFSON_DSM600; model.gpio_id = GpioId::DP665; model.motor_id = MotorId::DSMOBILE_600; model.flags = ModelFlag::CUSTOM_GAMMA | ModelFlag::DARK_CALIBRATION; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE; model.search_lines = 400; s_usb_devices->emplace_back(0x0a17, 0x3210, model); // clone, only usb id is different s_usb_devices->emplace_back(0x04f9, 0x2038, model); model = Genesys_Model(); model.name = "syscan-docketport-467"; model.vendor = "Syscan"; model.model = "DocketPORT 467"; model.model_id = ModelId::SYSCAN_DOCKETPORT_467; model.asic_type = AsicType::GL841; model.resolutions = { { { ScanMethod::FLATBED }, { 600, 300, 150, 75 }, { 1200, 600, 300, 150, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 0.0; model.y_offset = 0.0; model.x_size = 220.0; model.y_size = 511; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 220.1334; model.post_scan = 16.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 0; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = true; model.is_sheetfed = true; model.sensor_id = SensorId::CCD_DSMOBILE600; model.adc_id = AdcId::WOLFSON_DSM600; model.gpio_id = GpioId::DP665; model.motor_id = MotorId::DSMOBILE_600; model.flags = ModelFlag::CUSTOM_GAMMA | ModelFlag::DARK_CALIBRATION; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE; model.search_lines = 400; s_usb_devices->emplace_back(0x1dcc, 0x4812, model); model = Genesys_Model(); model.name = "syscan-docketport-685"; model.vendor = "Syscan/Ambir"; model.model = "DocketPORT 685"; model.model_id = ModelId::SYSCAN_DOCKETPORT_685; model.asic_type = AsicType::GL841; model.resolutions = { { { ScanMethod::FLATBED }, { 600, 300, 150, 75 }, { 600, 300, 150, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 0.0; model.y_offset = 1.0; model.x_size = 212.0; model.y_size = 500; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 212.5134; model.post_scan = 26.5; // this is larger than needed -- accounts for second sensor head, which is a calibration item model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 0; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = true; model.is_sheetfed = true; model.sensor_id = SensorId::CCD_DP685; model.adc_id = AdcId::WOLFSON_DSM600; model.gpio_id = GpioId::DP685; model.motor_id = MotorId::XP300; model.flags = ModelFlag::CUSTOM_GAMMA | ModelFlag::DARK_CALIBRATION; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE; model.search_lines = 400; s_usb_devices->emplace_back(0x0a82, 0x480c, model); model = Genesys_Model(); model.name = "syscan-docketport-485"; model.vendor = "Syscan/Ambir"; model.model = "DocketPORT 485"; model.model_id = ModelId::SYSCAN_DOCKETPORT_485; model.asic_type = AsicType::GL841; model.resolutions = { { { ScanMethod::FLATBED }, { 600, 300, 150, 75 }, { 600, 300, 150, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 0.0; model.y_offset = 1.0; model.x_size = 435.0; model.y_size = 511; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 433.4934; model.post_scan = 26.5; // this is larger than needed -- accounts for second sensor head, which is a calibration item model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 0; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = true; model.is_sheetfed = true; model.sensor_id = SensorId::CCD_XP300; model.adc_id = AdcId::WOLFSON_XP300; model.gpio_id = GpioId::XP300; model.motor_id = MotorId::XP300; model.flags = ModelFlag::CUSTOM_GAMMA | ModelFlag::DARK_CALIBRATION; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE; model.search_lines = 400; s_usb_devices->emplace_back(0x0a82, 0x4800, model); model = Genesys_Model(); model.name = "dct-docketport-487"; model.vendor = "DCT"; model.model = "DocketPORT 487"; model.model_id = ModelId::DCT_DOCKETPORT_487; model.asic_type = AsicType::GL841; model.resolutions = { { { ScanMethod::FLATBED }, { 600, 300, 150, 75 }, { 600, 300, 150, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 0.0; model.y_offset = 1.0; model.x_size = 435.0; model.y_size = 511; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 433.4934; model.post_scan = 26.5; // this is larger than needed -- accounts for second sensor head, which is a calibration item model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 0; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = true; model.is_sheetfed = true; model.sensor_id = SensorId::CCD_DOCKETPORT_487; model.adc_id = AdcId::WOLFSON_XP300; model.gpio_id = GpioId::XP300; model.motor_id = MotorId::XP300; model.flags = ModelFlag::DARK_CALIBRATION | ModelFlag::CUSTOM_GAMMA | ModelFlag::UNTESTED; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE; model.search_lines = 400; s_usb_devices->emplace_back(0x1dcc, 0x4810, model); model = Genesys_Model(); model.name = "visioneer-7100-model"; model.vendor = "Visioneer"; model.model = "OneTouch 7100"; model.model_id = ModelId::VISIONEER_7100; model.asic_type = AsicType::GL646; model.resolutions = { { { ScanMethod::FLATBED }, { 1200, 600, 400, 300, 200, 150, 100, 75, 50 }, { 2400, 1200, 600, 400, 300, 200, 150, 100, 75, 50 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 4.00; model.y_offset = 5.0; // FIXME: incorrect, needs updating model.x_size = 215.9; model.y_size = 296.4; model.y_offset_calib_white = 0.00; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 0.00; model.x_size_calib_mm = 230.1241; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 96; model.ld_shift_g = 48; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_5345; model.adc_id = AdcId::WOLFSON_5345; model.gpio_id = GpioId::MD_5345; model.motor_id = MotorId::MD_5345; model.flags = ModelFlag::WARMUP | ModelFlag::GAMMA_14BIT | ModelFlag::DARK_CALIBRATION | ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_POWER_SW | GENESYS_HAS_OCR_SW | GENESYS_HAS_SCAN_SW; model.search_lines = 200; s_usb_devices->emplace_back(0x04a7, 0x0229, model); model = Genesys_Model(); model.name = "xerox-2400-model"; model.vendor = "Xerox"; model.model = "OneTouch 2400"; model.model_id = ModelId::XEROX_2400; model.asic_type = AsicType::GL646; model.resolutions = { { { ScanMethod::FLATBED }, { 1200, 600, 400, 300, 200, 150, 100, 75, 50 }, { 2400, 1200, 600, 400, 300, 200, 150, 100, 75, 50 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 4.00; model.y_offset = 5.0; // FIXME: incorrect, needs updating model.x_size = 215.9; model.y_size = 296.4; model.y_offset_calib_white = 0.00; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 0.00; model.x_size_calib_mm = 230.1241; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 96; model.ld_shift_g = 48; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_5345; model.adc_id = AdcId::WOLFSON_5345; model.gpio_id = GpioId::MD_5345; model.motor_id = MotorId::MD_5345; model.flags = ModelFlag::WARMUP | ModelFlag::GAMMA_14BIT | ModelFlag::DARK_CALIBRATION | ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_POWER_SW | GENESYS_HAS_OCR_SW | GENESYS_HAS_SCAN_SW; model.search_lines = 200; s_usb_devices->emplace_back(0x0461, 0x038b, model); model = Genesys_Model(); model.name = "xerox-travelscanner"; model.vendor = "Xerox"; model.model = "Travelscanner 100"; model.model_id = ModelId::XEROX_TRAVELSCANNER_100; model.asic_type = AsicType::GL841; model.resolutions = { { { ScanMethod::FLATBED }, { 600, 300, 150, 75 }, { 1200, 600, 300, 150, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 4.0; model.y_offset = 0.0; model.x_size = 220.0; model.y_size = 511; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 220.1334; model.post_scan = 16.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 0; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = true; model.is_sheetfed = true; model.sensor_id = SensorId::CCD_ROADWARRIOR; model.adc_id = AdcId::WOLFSON_XP300; model.gpio_id = GpioId::DP665; model.motor_id = MotorId::ROADWARRIOR; model.flags = ModelFlag::CUSTOM_GAMMA | ModelFlag::DARK_CALIBRATION; model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE; model.search_lines = 400; s_usb_devices->emplace_back(0x04a7, 0x04ac, model); model = Genesys_Model(); model.name = "plustek-opticbook-3600"; model.vendor = "PLUSTEK"; model.model = "OpticBook 3600"; model.model_id = ModelId::PLUSTEK_OPTICPRO_3600; model.asic_type = AsicType::GL841; model.resolutions = { { { ScanMethod::FLATBED }, { /*1200,*/ 600, 400, 300, 200, 150, 100, 75 }, { /*2400,*/ 1200, 600, 400, 300, 200, 150, 100, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 0.42; model.y_offset = 6.75; model.x_size = 216.0; model.y_size = 297.0; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 213.7834; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 24; model.ld_shift_b = 48; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_PLUSTEK_OPTICPRO_3600; model.adc_id = AdcId::PLUSTEK_OPTICPRO_3600; model.gpio_id = GpioId::PLUSTEK_OPTICPRO_3600; model.motor_id = MotorId::PLUSTEK_OPTICPRO_3600; model.flags = ModelFlag::UNTESTED | // not fully working yet ModelFlag::CUSTOM_GAMMA | ModelFlag::DARK_CALIBRATION; model.buttons = GENESYS_HAS_NO_BUTTONS; model.search_lines = 200; s_usb_devices->emplace_back(0x07b3, 0x0900, model); model = Genesys_Model(); model.name = "plustek-opticfilm-7200"; model.vendor = "PLUSTEK"; model.model = "OpticFilm 7200"; model.model_id = ModelId::PLUSTEK_OPTICFILM_7200; model.asic_type = AsicType::GL842; model.resolutions = { { { ScanMethod::TRANSPARENCY }, { 7200, 3600, 1800, 900 }, { 7200, 3600, 1800, 900 }, } }; model.bpp_gray_values = { 16 }; model.bpp_color_values = { 16 }; model.default_method = ScanMethod::TRANSPARENCY; model.x_offset = 0.0; model.y_offset = 0.0; model.x_size = 36.0; model.y_size = 44.0; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 0.0; model.x_offset_calib_black = 6.5; model.x_size_calib_mm = 35.9834; model.x_offset_ta = 0.7f; model.y_offset_ta = 28.0; model.x_size_ta = 36.0; model.y_size_ta = 25.0; model.y_offset_sensor_to_ta = 0.0; model.y_offset_calib_black_ta = 6.5; model.y_offset_calib_white_ta = 0.0; model.y_size_calib_ta_mm = 2.0; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 12; model.ld_shift_b = 24; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7200; model.adc_id = AdcId::PLUSTEK_OPTICFILM_7200; model.gpio_id = GpioId::PLUSTEK_OPTICFILM_7200; model.motor_id = MotorId::PLUSTEK_OPTICFILM_7200; model.flags = ModelFlag::WARMUP | ModelFlag::CUSTOM_GAMMA | ModelFlag::DARK_CALIBRATION | ModelFlag::SHADING_REPARK; model.search_lines = 200; s_usb_devices->emplace_back(0x07b3, 0x0807, model); model = Genesys_Model(); model.name = "plustek-opticfilm-7200i"; model.vendor = "PLUSTEK"; model.model = "OpticFilm 7200i"; model.model_id = ModelId::PLUSTEK_OPTICFILM_7200I; model.asic_type = AsicType::GL843; model.resolutions = { { { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED }, { 7200, 3600, 1800, 900 }, { 7200, 3600, 1800, 900 }, } }; model.bpp_gray_values = { 16 }; model.bpp_color_values = { 16 }; model.default_method = ScanMethod::TRANSPARENCY; model.x_offset = 0.0; model.y_offset = 0.0; model.x_size = 36.0; model.y_size = 44.0; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 0.0; model.x_offset_calib_black = 6.5; model.x_size_calib_mm = 35.9834; model.x_offset_ta = 0.0; model.y_offset_ta = 29.0; model.x_size_ta = 36.0; model.y_size_ta = 24.0; model.y_offset_sensor_to_ta = 0.0; model.y_offset_calib_black_ta = 6.5; model.y_offset_calib_white_ta = 0.0; model.y_size_calib_ta_mm = 2.0; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 12; model.ld_shift_b = 24; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7200I; model.adc_id = AdcId::PLUSTEK_OPTICFILM_7200I; model.gpio_id = GpioId::PLUSTEK_OPTICFILM_7200I; model.motor_id = MotorId::PLUSTEK_OPTICFILM_7200I; model.flags = ModelFlag::WARMUP | ModelFlag::CUSTOM_GAMMA | ModelFlag::DARK_CALIBRATION | ModelFlag::SHADING_REPARK | ModelFlag::SWAP_16BIT_DATA; model.search_lines = 200; s_usb_devices->emplace_back(0x07b3, 0x0c04, model); // same as 7200i, just without the infrared channel model.name = "plustek-opticfilm-7200-v2"; model.model = "OpticFilm 7200 v2"; model.resolutions = { { { ScanMethod::TRANSPARENCY }, { 7200, 3600, 1800, 900 }, { 7200, 3600, 1800, 900 }, } }; s_usb_devices->emplace_back(0x07b3, 0x0c07, model); model = Genesys_Model(); model.name = "plustek-opticfilm-7300"; model.vendor = "PLUSTEK"; model.model = "OpticFilm 7300"; model.model_id = ModelId::PLUSTEK_OPTICFILM_7300; model.asic_type = AsicType::GL843; model.resolutions = { { { ScanMethod::TRANSPARENCY }, { 7200, 3600, 1800, 900 }, { 7200, 3600, 1800, 900 }, } }; model.bpp_gray_values = { 16 }; model.bpp_color_values = { 16 }; model.default_method = ScanMethod::TRANSPARENCY; model.x_offset = 0.0; model.y_offset = 0.0; model.x_size = 36.0; model.y_size = 44.0; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 0.0; model.x_offset_calib_black = 6.5; model.x_size_calib_mm = 35.9834; model.x_offset_ta = 0.0; model.y_offset_ta = 29.0; model.x_size_ta = 36.0; model.y_size_ta = 24.0; model.y_offset_sensor_to_ta = 0.0; model.y_offset_calib_black_ta = 6.5; model.y_offset_calib_white_ta = 0.0; model.y_size_calib_ta_mm = 2.0; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 12; model.ld_shift_b = 24; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7300; model.adc_id = AdcId::PLUSTEK_OPTICFILM_7300; model.gpio_id = GpioId::PLUSTEK_OPTICFILM_7300; model.motor_id = MotorId::PLUSTEK_OPTICFILM_7300; model.flags = ModelFlag::WARMUP | ModelFlag::CUSTOM_GAMMA | ModelFlag::DARK_CALIBRATION | ModelFlag::SHADING_REPARK; model.search_lines = 200; s_usb_devices->emplace_back(0x07b3, 0x0c12, model); // same as 7300, same USB ID as 7400-v2 model.name = "plustek-opticfilm-7400-v1"; model.model = "OpticFilm 7400 (v1)"; s_usb_devices->emplace_back(0x07b3, 0x0c3a, 0x0400, model); model = Genesys_Model(); model.name = "plustek-opticfilm-7400-v2"; model.vendor = "PLUSTEK"; model.model = "OpticFilm 7400 (v2)"; model.model_id = ModelId::PLUSTEK_OPTICFILM_7400; model.asic_type = AsicType::GL845; model.resolutions = { { { ScanMethod::TRANSPARENCY }, { 7200, 3600, 2400, 1200, 600 }, { 7200, 3600, 2400, 1200, 600 }, } }; model.bpp_gray_values = { 16 }; model.bpp_color_values = { 16 }; model.default_method = ScanMethod::TRANSPARENCY; model.x_offset = 0.0; model.y_offset = 0.0; model.x_size = 36.0; model.y_size = 44.0; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 0.0; model.x_offset_calib_black = 6.5; model.x_size_calib_mm = 36.83; model.x_offset_ta = 0.5; model.y_offset_ta = 29.0; model.x_size_ta = 36.33; model.y_size_ta = 25.0; model.y_offset_sensor_to_ta = 0.0; model.y_offset_calib_black_ta = 6.5; model.y_offset_calib_white_ta = 0.0; model.y_size_calib_ta_mm = 2.0; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 12; model.ld_shift_b = 24; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7400; model.adc_id = AdcId::PLUSTEK_OPTICFILM_7400; model.gpio_id = GpioId::PLUSTEK_OPTICFILM_7400; model.motor_id = MotorId::PLUSTEK_OPTICFILM_7400; model.flags = ModelFlag::CUSTOM_GAMMA | ModelFlag::DARK_CALIBRATION | ModelFlag::SHADING_REPARK; model.search_lines = 200; s_usb_devices->emplace_back(0x07b3, 0x0c3a, 0x0605, model); // same as 7400-v2 model.name = "plustek-opticfilm-8100"; model.model = "OpticFilm 8100"; s_usb_devices->emplace_back(0x07b3, 0x130c, model); model = Genesys_Model(); model.name = "plustek-opticfilm-7500i"; model.vendor = "PLUSTEK"; model.model = "OpticFilm 7500i"; model.model_id = ModelId::PLUSTEK_OPTICFILM_7500I; model.asic_type = AsicType::GL843; model.resolutions = { { { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED }, { 7200, 3600, 1800, 900 }, { 7200, 3600, 1800, 900 }, } }; model.bpp_gray_values = { 16 }; model.bpp_color_values = { 16 }; model.default_method = ScanMethod::TRANSPARENCY; model.x_offset = 0.0; model.y_offset = 0.0; model.x_size = 36.0; model.y_size = 44.0; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 0.0; model.x_offset_calib_black = 6.5; model.x_offset_ta = 0.0; model.y_offset_ta = 29.0; model.x_size_ta = 36.0; model.y_size_ta = 24.0; model.x_size_calib_mm = 35.9834; model.y_offset_sensor_to_ta = 0.0; model.y_offset_calib_black_ta = 6.5; model.y_offset_calib_white_ta = 0.0; model.y_size_calib_ta_mm = 2.0; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 12; model.ld_shift_b = 24; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7500I; model.adc_id = AdcId::PLUSTEK_OPTICFILM_7500I; model.gpio_id = GpioId::PLUSTEK_OPTICFILM_7500I; model.motor_id = MotorId::PLUSTEK_OPTICFILM_7500I; model.flags = ModelFlag::WARMUP | ModelFlag::CUSTOM_GAMMA | ModelFlag::DARK_CALIBRATION | ModelFlag::SHADING_REPARK; model.search_lines = 200; s_usb_devices->emplace_back(0x07b3, 0x0c13, model); // same as 7500i model.name = "plustek-opticfilm-7600i-v1"; model.model = "OpticFilm 7600i (v1)"; s_usb_devices->emplace_back(0x07b3, 0x0c3b, 0x0400, model); model = Genesys_Model(); model.name = "plustek-opticfilm-8200i"; model.vendor = "PLUSTEK"; model.model = "OpticFilm 8200i"; model.model_id = ModelId::PLUSTEK_OPTICFILM_8200I; model.asic_type = AsicType::GL845; model.resolutions = { { { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED }, { 7200, 3600, 1800, 900 }, { 7200, 3600, 1800, 900 }, } }; model.bpp_gray_values = { 16 }; model.bpp_color_values = { 16 }; model.default_method = ScanMethod::TRANSPARENCY; model.x_offset = 0.0; model.y_offset = 0.0; model.x_size = 36.0; model.y_size = 44.0; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 0.0; model.x_offset_calib_black = 6.5; model.x_size_calib_mm = 36.83; model.x_offset_ta = 0.5; model.y_offset_ta = 28.5; model.x_size_ta = 36.33; model.y_size_ta = 25.0; model.y_offset_sensor_to_ta = 0.0; model.y_offset_calib_black_ta = 6.5; model.y_offset_calib_white_ta = 0.0; model.y_size_calib_ta_mm = 2.0; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 12; model.ld_shift_b = 24; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_8200I; model.adc_id = AdcId::PLUSTEK_OPTICFILM_8200I; model.gpio_id = GpioId::PLUSTEK_OPTICFILM_8200I; model.motor_id = MotorId::PLUSTEK_OPTICFILM_8200I; model.flags = ModelFlag::CUSTOM_GAMMA | ModelFlag::DARK_CALIBRATION | ModelFlag::SHADING_REPARK; model.search_lines = 200; s_usb_devices->emplace_back(0x07b3, 0x130d, model); // same as 8200i model.name = "plustek-opticfilm-7600i-v2"; model.model = "OpticFilm 7600i (v2)"; s_usb_devices->emplace_back(0x07b3, 0x0c3b, 0x0605, model); model = Genesys_Model(); model.name = "hewlett-packard-scanjet-N6310"; model.vendor = "Hewlett Packard"; model.model = "ScanJet N6310"; model.model_id = ModelId::HP_SCANJET_N6310; model.asic_type = AsicType::GL847; model.resolutions = { { { ScanMethod::FLATBED }, { 2400, 1200, 600, 400, 300, 200, 150, 100, 75 }, { 2400, 1200, 600, 400, 300, 200, 150, 100, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 6; model.y_offset = 2; model.x_size = 216; model.y_size = 511; model.y_offset_calib_white = 0.0; model.y_size_calib_mm = 4.0; // FIXME: y_offset is liely incorrect model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 452.12; model.post_scan = 0; model.eject_feed = 0; model.ld_shift_r = 0; model.ld_shift_g = 0; model.ld_shift_b = 0; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_HP_N6310; model.adc_id = AdcId::CANON_LIDE_200; // Not defined yet for N6310 model.gpio_id = GpioId::HP_N6310; model.motor_id = MotorId::CANON_LIDE_200; // Not defined yet for N6310 model.flags = ModelFlag::UNTESTED | ModelFlag::GAMMA_14BIT | ModelFlag::DARK_CALIBRATION | ModelFlag::CUSTOM_GAMMA | ModelFlag::DISABLE_ADC_CALIBRATION | ModelFlag::DISABLE_EXPOSURE_CALIBRATION | ModelFlag::DISABLE_SHADING_CALIBRATION; model.buttons = GENESYS_HAS_NO_BUTTONS; model.search_lines = 100; s_usb_devices->emplace_back(0x03f0, 0x4705, model); model = Genesys_Model(); model.name = "plustek-opticbook-3800"; model.vendor = "PLUSTEK"; model.model = "OpticBook 3800"; model.model_id = ModelId::PLUSTEK_OPTICBOOK_3800; model.asic_type = AsicType::GL845; model.resolutions = { { { ScanMethod::FLATBED }, { 1200, 600, 300, 150, 100, 75 }, { 1200, 600, 300, 150, 100, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 7.2; model.y_offset = 14.7; model.x_size = 217.7; model.y_size = 300.0; model.y_offset_calib_white = 9.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 215.9; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 24; model.ld_shift_b = 48; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_PLUSTEK_OPTICBOOK_3800; model.adc_id = AdcId::PLUSTEK_OPTICBOOK_3800; model.gpio_id = GpioId::PLUSTEK_OPTICBOOK_3800; model.motor_id = MotorId::PLUSTEK_OPTICBOOK_3800; model.flags = ModelFlag::CUSTOM_GAMMA; model.buttons = GENESYS_HAS_NO_BUTTONS; // TODO there are 4 buttons to support model.search_lines = 100; s_usb_devices->emplace_back(0x07b3, 0x1300, model); model = Genesys_Model(); model.name = "canon-image-formula-101"; model.vendor = "Canon"; model.model = "Image Formula 101"; model.model_id = ModelId::CANON_IMAGE_FORMULA_101; model.asic_type = AsicType::GL846; model.resolutions = { { { ScanMethod::FLATBED }, { 1200, 600, 300, 150, 100, 75 }, { 1200, 600, 300, 150, 100, 75 }, } }; model.bpp_gray_values = { 8, 16 }; model.bpp_color_values = { 8, 16 }; model.x_offset = 7.2; model.y_offset = 14.7; model.x_size = 217.7; model.y_size = 300.0; model.y_offset_calib_white = 9.0; model.y_size_calib_mm = 4.0; model.x_offset_calib_black = 0.0; model.x_size_calib_mm = 228.6; model.post_scan = 0.0; model.eject_feed = 0.0; model.ld_shift_r = 0; model.ld_shift_g = 24; model.ld_shift_b = 48; model.line_mode_color_order = ColorOrder::RGB; model.is_cis = false; model.is_sheetfed = false; model.sensor_id = SensorId::CCD_IMG101; model.adc_id = AdcId::IMG101; model.gpio_id = GpioId::IMG101; model.motor_id = MotorId::IMG101; model.flags = ModelFlag::CUSTOM_GAMMA | ModelFlag::UNTESTED; model.buttons = GENESYS_HAS_NO_BUTTONS ; model.search_lines = 100; s_usb_devices->emplace_back(0x1083, 0x162e, model); } void verify_usb_device_tables() { for (const auto& device : *s_usb_devices) { const auto& model = device.model(); if (model.x_size_calib_mm == 0.0f) { throw SaneException("Calibration width can't be zero"); } if (model.has_method(ScanMethod::FLATBED)) { if (model.y_size_calib_mm == 0.0f) { throw SaneException("Calibration size can't be zero"); } } if (model.has_method(ScanMethod::TRANSPARENCY) || model.has_method(ScanMethod::TRANSPARENCY_INFRARED)) { if (model.y_size_calib_ta_mm == 0.0f) { throw SaneException("Calibration size can't be zero"); } } } } } // namespace genesys backends-1.3.0/backend/genesys/tables_motor.cpp000066400000000000000000000557021456256263500215560ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "low.h" namespace genesys { StaticInit> s_motors; void genesys_init_motor_tables() { s_motors.init(); MotorProfile profile; Genesys_Motor motor; motor.id = MotorId::UMAX; motor.base_ydpi = 2400; motor.profiles.push_back({MotorSlope::create_from_steps(11000, 3000, 128), StepType::FULL, 0}); motor.profiles.push_back({MotorSlope::create_from_steps(11000, 3000, 128), StepType::HALF, 0}); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::MD_5345; // MD5345/6228/6471 motor.base_ydpi = 2400; motor.profiles.push_back({MotorSlope::create_from_steps(2000, 1375, 128), StepType::FULL, 0}); motor.profiles.push_back({MotorSlope::create_from_steps(2000, 1375, 128), StepType::HALF, 0}); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::ST24; motor.base_ydpi = 2400; motor.profiles.push_back({MotorSlope::create_from_steps(2289, 2100, 128), StepType::FULL, 0}); motor.profiles.push_back({MotorSlope::create_from_steps(2289, 2100, 128), StepType::HALF, 0}); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::HP3670; motor.base_ydpi = 1200; motor.profiles.push_back({MotorSlope::create_from_steps(11000, 3000, 128), StepType::FULL, 0}); motor.profiles.push_back({MotorSlope::create_from_steps(11000, 3000, 128), StepType::HALF, 0}); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::HP2400; motor.base_ydpi = 1200; motor.profiles.push_back({MotorSlope::create_from_steps(11000, 3000, 128), StepType::FULL, 0}); motor.profiles.push_back({MotorSlope::create_from_steps(11000, 3000, 128), StepType::HALF, 0}); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::HP2300; motor.base_ydpi = 1200; motor.profiles.push_back({MotorSlope::create_from_steps(3200, 1200, 128), StepType::FULL, 0}); motor.profiles.push_back({MotorSlope::create_from_steps(3200, 1200, 128), StepType::HALF, 0}); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::CANON_LIDE_35; motor.base_ydpi = 1200; profile = MotorProfile{MotorSlope::create_from_steps(3500, 1300, 150), StepType::HALF, 0}; profile.resolutions = { 75, 150, 200, 300, 600 }; motor.profiles.push_back(profile); profile = MotorProfile{MotorSlope::create_from_steps(3500, 1300, 150), StepType::QUARTER, 0}; profile.resolutions = { 1200, 2400 }; motor.profiles.push_back(profile); profile = MotorProfile{MotorSlope::create_from_steps(3500, 1400, 150), StepType::FULL, 0}; profile.resolutions = { 75, 150, 200, 300 }; motor.fast_profiles.push_back(profile); profile = MotorProfile{MotorSlope::create_from_steps(6000, 3000, 100), StepType::FULL, 0}; profile.resolutions = { 600, 1200, 2400 }; motor.fast_profiles.push_back(profile); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::CANON_LIDE_60; motor.base_ydpi = 1200; profile = MotorProfile{MotorSlope::create_from_steps(3500, 1400, 150), StepType::HALF, 0}; motor.profiles.push_back(profile); profile = MotorProfile{MotorSlope::create_from_steps(3500, 1400, 150), StepType::FULL, 0}; profile.resolutions = { 75, 150, 300 }; motor.fast_profiles.push_back(profile); profile = MotorProfile{MotorSlope::create_from_steps(6000, 3000, 100), StepType::FULL, 0}; profile.resolutions = { 600, 1200, 2400 }; motor.fast_profiles.push_back(profile); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::CANON_LIDE_90; motor.base_ydpi = 1200; profile = {MotorSlope::create_from_steps(8000, 3000, 200), StepType::FULL, 0}; profile.resolutions = { 150, 300 }; motor.profiles.push_back(profile); profile = {MotorSlope::create_from_steps(7000, 3000, 200), StepType::HALF, 0}; profile.resolutions = { 600, 1200 }; motor.profiles.push_back(profile); profile = {MotorSlope::create_from_steps(7000, 3000, 200), StepType::QUARTER, 0}; profile.resolutions = { 2400 }; motor.profiles.push_back(profile); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::XP200; motor.base_ydpi = 600; motor.profiles.push_back({MotorSlope::create_from_steps(3500, 1300, 60), StepType::FULL, 0}); motor.profiles.push_back({MotorSlope::create_from_steps(3500, 1300, 60), StepType::HALF, 0}); motor.fast_profiles.push_back({MotorSlope::create_from_steps(3500, 1300, 60), StepType::FULL, 0}); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::XP300; motor.base_ydpi = 300; // works best with GPIO10, GPIO14 off profile = MotorProfile{MotorSlope::create_from_steps(3700, 3700, 2), StepType::FULL, 0}; profile.resolutions = {}; // used during fast moves motor.profiles.push_back(profile); // FIXME: this motor profile is useless profile = MotorProfile{MotorSlope::create_from_steps(11000, 11000, 2), StepType::HALF, 0}; profile.resolutions = {75, 150, 300, 600}; motor.profiles.push_back(profile); profile = MotorProfile{MotorSlope::create_from_steps(3700, 3700, 2), StepType::FULL, 0}; motor.fast_profiles.push_back(profile); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::DP665; motor.base_ydpi = 750; profile = MotorProfile{MotorSlope::create_from_steps(3000, 2500, 10), StepType::FULL, 0}; profile.resolutions = {75, 150}; motor.profiles.push_back(profile); // FIXME: this motor profile is useless profile = MotorProfile{MotorSlope::create_from_steps(11000, 11000, 2), StepType::HALF, 0}; profile.resolutions = {300, 600, 1200}; motor.profiles.push_back(profile); profile = MotorProfile{MotorSlope::create_from_steps(3000, 2500, 10), StepType::FULL, 0}; motor.fast_profiles.push_back(profile); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::ROADWARRIOR; motor.base_ydpi = 750; profile = MotorProfile{MotorSlope::create_from_steps(3000, 2600, 10), StepType::FULL, 0}; profile.resolutions = {75, 150}; motor.profiles.push_back(profile); // FIXME: this motor profile is useless profile = MotorProfile{MotorSlope::create_from_steps(11000, 11000, 2), StepType::HALF, 0}; profile.resolutions = {300, 600, 1200}; motor.profiles.push_back(profile); profile = MotorProfile{MotorSlope::create_from_steps(3000, 2600, 10), StepType::FULL, 0}; motor.fast_profiles.push_back(profile); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::DSMOBILE_600; motor.base_ydpi = 750; profile = MotorProfile{MotorSlope::create_from_steps(6666, 3700, 8), StepType::FULL, 0}; profile.resolutions = {75, 150}; motor.profiles.push_back(profile); profile = MotorProfile{MotorSlope::create_from_steps(6666, 3700, 8), StepType::HALF, 0}; profile.resolutions = {300, 600, 1200}; motor.profiles.push_back(profile); profile = MotorProfile{MotorSlope::create_from_steps(6666, 3700, 8), StepType::FULL, 0}; motor.fast_profiles.push_back(profile); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::CANON_LIDE_100; motor.base_ydpi = 1200; motor.profiles.push_back({MotorSlope::create_from_steps(46876, 864, 255), StepType::HALF, 1432}); motor.profiles.push_back({MotorSlope::create_from_steps(46876, 864, 279), StepType::QUARTER, 2712}); motor.profiles.push_back({MotorSlope::create_from_steps(31680, 864, 247), StepType::EIGHTH, 5280}); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::CANON_LIDE_200; motor.base_ydpi = 1200; motor.profiles.push_back({MotorSlope::create_from_steps(46876, 864, 255), StepType::HALF, 1432}); motor.profiles.push_back({MotorSlope::create_from_steps(46876, 864, 279), StepType::QUARTER, 2712}); motor.profiles.push_back({MotorSlope::create_from_steps(31680, 864, 247), StepType::EIGHTH, 5280}); motor.profiles.push_back({MotorSlope::create_from_steps(31680, 864, 247), StepType::EIGHTH, 10416}); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::CANON_LIDE_700; motor.base_ydpi = 1200; motor.profiles.push_back({MotorSlope::create_from_steps(46876, 534, 255), StepType::HALF, 1424}); motor.profiles.push_back({MotorSlope::create_from_steps(46876, 534, 255), StepType::HALF, 1504}); motor.profiles.push_back({MotorSlope::create_from_steps(46876, 2022, 127), StepType::HALF, 2696}); motor.profiles.push_back({MotorSlope::create_from_steps(46876, 534, 255), StepType::HALF, 2848}); motor.profiles.push_back({MotorSlope::create_from_steps(46876, 15864, 2), StepType::EIGHTH, 10576}); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::KVSS080; motor.base_ydpi = 1200; motor.profiles.push_back({MotorSlope::create_from_steps(44444, 500, 489), StepType::HALF, 8000}); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::G4050; motor.base_ydpi = 2400; motor.profiles.push_back({MotorSlope::create_from_steps(7842, 320, 602), StepType::HALF, 8016}); motor.profiles.push_back({MotorSlope::create_from_steps(9422, 254, 1004), StepType::HALF, 15624}); motor.profiles.push_back({MotorSlope::create_from_steps(28032, 2238, 604), StepType::HALF, 56064}); motor.profiles.push_back({MotorSlope::create_from_steps(42752, 1706, 610), StepType::QUARTER, 42752}); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::CANON_4400F; motor.base_ydpi = 2400; profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(28597 * 2, 727 * 2, 200); profile.step_type = StepType::QUARTER; profile.motor_vref = 1; profile.resolutions = { 300, 600 }; motor.profiles.push_back(std::move(profile)); profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(28597 * 2, 727 * 2, 200); profile.step_type = StepType::QUARTER; profile.motor_vref = 0; profile.resolutions = { 1200, 2400, 4800, 9600 }; motor.profiles.push_back(std::move(profile)); profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(28597 * 2, 279 * 2, 1000); profile.step_type = StepType::QUARTER; profile.motor_vref = 0; motor.fast_profiles.push_back(std::move(profile)); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::CANON_5600F; motor.base_ydpi = 2400; // FIXME: real limit is 134, but for some reason the motor can't acquire that speed. profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(2500 * 2, 134 * 2, 1000); profile.step_type = StepType::HALF; profile.motor_vref = 0; profile.resolutions = { 75, 150 }; motor.profiles.push_back(std::move(profile)); profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(2500 * 2, 200 * 2, 1000); profile.step_type = StepType::QUARTER; profile.motor_vref = 0; profile.resolutions = { 300, 600, 1200, 2400, 4800 }; motor.profiles.push_back(std::move(profile)); profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(2500 * 2, 200 * 2, 1000); profile.step_type = StepType::QUARTER; profile.motor_vref = 0; motor.fast_profiles.push_back(std::move(profile)); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::CANON_8400F; motor.base_ydpi = 1600; profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(20202 * 4, 333 * 4, 100); profile.step_type = StepType::QUARTER; profile.motor_vref = 0; profile.resolutions = VALUE_FILTER_ANY; profile.scan_methods = { ScanMethod::FLATBED }; motor.profiles.push_back(std::move(profile)); profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(65535 * 4, 333 * 4, 100); profile.step_type = StepType::QUARTER; profile.motor_vref = 2; profile.resolutions = VALUE_FILTER_ANY; profile.scan_methods = { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED }; motor.profiles.push_back(std::move(profile)); profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(65535 * 4, 333 * 4, 200); profile.step_type = StepType::QUARTER; profile.motor_vref = 2; profile.resolutions = VALUE_FILTER_ANY; profile.scan_methods = VALUE_FILTER_ANY; motor.fast_profiles.push_back(std::move(profile)); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::CANON_8600F; motor.base_ydpi = 2400; profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(54612, 1500, 219); profile.step_type = StepType::QUARTER; profile.motor_vref = 3; profile.resolutions = { 300, 600 }; profile.scan_methods = { ScanMethod::FLATBED }; motor.profiles.push_back(std::move(profile)); profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(54612, 1500, 219); profile.step_type = StepType::QUARTER; profile.motor_vref = 2; profile.resolutions = { 1200, 2400 }; profile.scan_methods = { ScanMethod::FLATBED }; motor.profiles.push_back(std::move(profile)); profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(54612, 1500, 219); profile.step_type = StepType::QUARTER; profile.motor_vref = 2; profile.resolutions = { 4800 }; profile.scan_methods = { ScanMethod::FLATBED }; motor.profiles.push_back(std::move(profile)); profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(54612, 1500, 219); profile.step_type = StepType::QUARTER; profile.motor_vref = 2; profile.resolutions = { 300, 600 }; profile.scan_methods = { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED }; motor.profiles.push_back(std::move(profile)); profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(54612, 1500, 219); profile.step_type = StepType::QUARTER; profile.motor_vref = 1; profile.resolutions = { 1200, 2400 }; profile.scan_methods = { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED }; motor.profiles.push_back(std::move(profile)); profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(54612, 1500, 219); profile.step_type = StepType::QUARTER; profile.motor_vref = 0; profile.resolutions = { 4800 }; profile.scan_methods = { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED }; motor.profiles.push_back(std::move(profile)); profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(59240, 582, 1020); profile.step_type = StepType::QUARTER; profile.motor_vref = 2; motor.fast_profiles.push_back(std::move(profile)); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::CANON_LIDE_110; motor.base_ydpi = 4800; motor.profiles.push_back({MotorSlope::create_from_steps(62496, 335, 255), StepType::FULL, 2768}); motor.profiles.push_back({MotorSlope::create_from_steps(62496, 335, 469), StepType::HALF, 5360}); motor.profiles.push_back({MotorSlope::create_from_steps(62496, 2632, 3), StepType::HALF, 10528}); motor.profiles.push_back({MotorSlope::create_from_steps(62496, 10432, 3), StepType::QUARTER, 20864}); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::CANON_LIDE_120; motor.base_ydpi = 4800; motor.profiles.push_back({MotorSlope::create_from_steps(62496, 864, 127), StepType::FULL, 4608}); motor.profiles.push_back({MotorSlope::create_from_steps(62496, 1338, 63), StepType::HALF, 5360}); motor.profiles.push_back({MotorSlope::create_from_steps(62464, 2632, 3), StepType::QUARTER, 10528}); motor.profiles.push_back({MotorSlope::create_from_steps(62592, 10432, 5), StepType::QUARTER, 20864}); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::CANON_LIDE_210; motor.base_ydpi = 4800; motor.profiles.push_back({MotorSlope::create_from_steps(62496, 335, 255), StepType::FULL, 2768}); motor.profiles.push_back({MotorSlope::create_from_steps(62496, 335, 469), StepType::HALF, 5360}); motor.profiles.push_back({MotorSlope::create_from_steps(62496, 2632, 3), StepType::HALF, 10528}); motor.profiles.push_back({MotorSlope::create_from_steps(62496, 10432, 4), StepType::QUARTER, 20864}); motor.profiles.push_back({MotorSlope::create_from_steps(62496, 10432, 4), StepType::EIGHTH, 41536}); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::PLUSTEK_OPTICPRO_3600; motor.base_ydpi = 1200; profile = MotorProfile{MotorSlope::create_from_steps(3500, 1300, 60), StepType::FULL, 0}; profile.resolutions = {75, 100, 150, 200}; motor.profiles.push_back(profile); // FIXME: this motor profile is almost useless profile = MotorProfile{MotorSlope::create_from_steps(3500, 3250, 60), StepType::HALF, 0}; profile.resolutions = {300, 400, 600, 1200}; motor.profiles.push_back(profile); profile = MotorProfile{MotorSlope::create_from_steps(3500, 1300, 60), StepType::FULL, 0}; motor.fast_profiles.push_back(profile); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::PLUSTEK_OPTICFILM_7200; motor.base_ydpi = 3600; profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(20000 * 2, 600 * 2, 200); profile.step_type = StepType::HALF; profile.motor_vref = 0; motor.profiles.push_back(std::move(profile)); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::PLUSTEK_OPTICFILM_7200I; motor.base_ydpi = 3600; profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(34722 * 2, 454 * 2, 40); profile.step_type = StepType::HALF; profile.motor_vref = 3; motor.profiles.push_back(std::move(profile)); profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(34722 * 2, 454 * 2, 40); profile.step_type = StepType::HALF; profile.motor_vref = 0; motor.fast_profiles.push_back(std::move(profile)); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::PLUSTEK_OPTICFILM_7300; motor.base_ydpi = 3600; profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(56818 * 4, 454 * 4, 30); profile.step_type = StepType::QUARTER; profile.motor_vref = 3; motor.profiles.push_back(std::move(profile)); profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(56818 * 4, 454 * 4, 30); profile.step_type = StepType::QUARTER; profile.motor_vref = 0; motor.fast_profiles.push_back(std::move(profile)); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::PLUSTEK_OPTICFILM_7400; motor.base_ydpi = 3600; profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(64102 * 4, 400 * 4, 30); profile.step_type = StepType::QUARTER; profile.motor_vref = 3; motor.profiles.push_back(profile); motor.fast_profiles.push_back(profile); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::PLUSTEK_OPTICFILM_7500I; motor.base_ydpi = 3600; profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(56818 * 4, 454 * 4, 30); profile.step_type = StepType::QUARTER; profile.motor_vref = 3; motor.profiles.push_back(std::move(profile)); profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(56818 * 4, 454 * 4, 30); profile.step_type = StepType::QUARTER; profile.motor_vref = 0; motor.fast_profiles.push_back(std::move(profile)); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::PLUSTEK_OPTICFILM_8200I; motor.base_ydpi = 3600; profile = MotorProfile(); profile.slope = MotorSlope::create_from_steps(64102 * 4, 400 * 4, 100); profile.step_type = StepType::QUARTER; profile.motor_vref = 3; motor.profiles.push_back(profile); motor.fast_profiles.push_back(profile); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::IMG101; motor.base_ydpi = 600; motor.profiles.push_back({MotorSlope::create_from_steps(22000, 1000, 1017), StepType::HALF, 11000}); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::PLUSTEK_OPTICBOOK_3800; motor.base_ydpi = 600; motor.profiles.push_back({MotorSlope::create_from_steps(22000, 1000, 1017), StepType::HALF, 11000}); s_motors->push_back(std::move(motor)); motor = Genesys_Motor(); motor.id = MotorId::CANON_LIDE_80; motor.base_ydpi = 2400; motor.profiles.push_back({MotorSlope::create_from_steps(9560, 1912, 31), StepType::FULL, 0}); s_motors->push_back(std::move(motor)); } } // namespace genesys backends-1.3.0/backend/genesys/tables_sensor.cpp000066400000000000000000005573111456256263500217320ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "low.h" #include namespace genesys { StaticInit> s_sensors; void genesys_init_sensor_tables() { s_sensors.init(); Genesys_Sensor sensor; sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_UMAX; // gl646 sensor.full_resolution = 1200; sensor.black_pixels = 48; sensor.dummy_pixel = 64; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 230; sensor.exposure = { 0x0000, 0x0000, 0x0000 }; sensor.custom_regs = { { 0x08, 0x01 }, { 0x09, 0x03 }, { 0x0a, 0x05 }, { 0x0b, 0x07 }, { 0x16, 0x33 }, { 0x17, 0x05 }, { 0x18, 0x31 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x02 }, { 0x52, 0x13 }, { 0x53, 0x17 }, { 0x54, 0x03 }, { 0x55, 0x07 }, { 0x56, 0x0b }, { 0x57, 0x0f }, { 0x58, 0x23 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 }, }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned register_dpiset; int output_pixel_offset; }; CustomSensorSettings custom_settings[] = { { { 75 }, 150, 4 }, { { 150 }, 300, 8 }, { { 300 }, 600, 16 }, { { 600 }, 1200, 32 }, { { 1200 }, 2400, 64 }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.register_dpiset = setting.register_dpiset; sensor.output_pixel_offset = setting.output_pixel_offset; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_ST12; // gl646 sensor.full_resolution = 600; sensor.black_pixels = 48; sensor.dummy_pixel = 85; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 230; sensor.exposure = { 0x0000, 0x0000, 0x0000 }; sensor.custom_regs = { { 0x08, 0x02 }, { 0x09, 0x00 }, { 0x0a, 0x06 }, { 0x0b, 0x04 }, { 0x16, 0x2b }, { 0x17, 0x08 }, { 0x18, 0x20 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x0c }, { 0x1d, 0x03 }, { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 }, }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; int output_pixel_offset; }; CustomSensorSettings custom_settings[] = { { { 75 }, 10 }, { { 150 }, 21 }, { { 300 }, 42 }, { { 600 }, 85 }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.register_dpiset = setting.resolutions.values()[0]; sensor.output_pixel_offset = setting.output_pixel_offset; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_ST24; // gl646 sensor.full_resolution = 1200; sensor.black_pixels = 48; sensor.dummy_pixel = 64; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 230; sensor.exposure = { 0x0000, 0x0000, 0x0000 }; sensor.custom_regs = { { 0x08, 0x0e }, { 0x09, 0x0c }, { 0x0a, 0x00 }, { 0x0b, 0x0c }, { 0x16, 0x33 }, { 0x17, 0x08 }, { 0x18, 0x31 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x02 }, { 0x52, 0x17 }, { 0x53, 0x03 }, { 0x54, 0x07 }, { 0x55, 0x0b }, { 0x56, 0x0f }, { 0x57, 0x13 }, { 0x58, 0x03 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 }, }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned register_dpiset; int output_pixel_offset; }; CustomSensorSettings custom_settings[] = { { { 75 }, 150, 4 }, { { 150 }, 300, 8 }, { { 300 }, 600, 16 }, { { 600 }, 1200, 32 }, { { 1200 }, 2400, 64 }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.register_dpiset = setting.register_dpiset; sensor.output_pixel_offset = setting.output_pixel_offset; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_5345; // gl646 sensor.full_resolution = 1200; sensor.black_pixels = 48; sensor.dummy_pixel = 16; sensor.fau_gain_white_ref = 190; sensor.gain_white_ref = 190; sensor.exposure = { 0x0000, 0x0000, 0x0000 }; sensor.gamma = { 2.38f, 2.35f, 2.34f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned optical_resolution; unsigned register_dpiset; unsigned exposure_lperiod; Ratio pixel_count_ratio; int output_pixel_offset; StaggerConfig stagger_y; // FIXME: may be incorrect GenesysRegisterSettingSet custom_regs; }; CustomSensorSettings custom_settings[] = { { { 50 }, 600, 100, 12000, Ratio{1, 2}, 0, StaggerConfig{}, { { 0x08, 0x00 }, { 0x09, 0x05 }, { 0x0a, 0x06 }, { 0x0b, 0x08 }, { 0x16, 0x0b }, { 0x17, 0x0a }, { 0x18, 0x28 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x03 }, { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } } }, { { 75 }, 600, 150, 11000, Ratio{1, 2}, 1, StaggerConfig{}, { { 0x08, 0x00 }, { 0x09, 0x05 }, { 0x0a, 0x06 }, { 0x0b, 0x08 }, { 0x16, 0x0b }, { 0x17, 0x0a }, { 0x18, 0x28 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x03 }, { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } } }, { { 100 }, 600, 200, 11000, Ratio{1, 2}, 1, StaggerConfig{}, { { 0x08, 0x00 }, { 0x09, 0x05 }, { 0x0a, 0x06 }, { 0x0b, 0x08 }, { 0x16, 0x0b }, { 0x17, 0x0a }, { 0x18, 0x28 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x03 }, { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } } }, { { 150 }, 600, 300, 11000, Ratio{1, 2}, 2, StaggerConfig{}, { { 0x08, 0x00 }, { 0x09, 0x05 }, { 0x0a, 0x06 }, { 0x0b, 0x08 }, { 0x16, 0x0b }, { 0x17, 0x0a }, { 0x18, 0x28 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x03 }, { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } } }, { { 200 }, 600, 400, 11000, Ratio{1, 2}, 2, StaggerConfig{}, { { 0x08, 0x00 }, { 0x09, 0x05 }, { 0x0a, 0x06 }, { 0x0b, 0x08 }, { 0x16, 0x0b }, { 0x17, 0x0a }, { 0x18, 0x28 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x03 }, { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } } }, { { 300 }, 600, 600, 11000, Ratio{1, 2}, 4, StaggerConfig{}, { { 0x08, 0x00 }, { 0x09, 0x05 }, { 0x0a, 0x06 }, { 0x0b, 0x08 }, { 0x16, 0x0b }, { 0x17, 0x0a }, { 0x18, 0x28 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x03 }, { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } } }, { { 400 }, 600, 800, 11000, Ratio{1, 2}, 5, StaggerConfig{}, { { 0x08, 0x00 }, { 0x09, 0x05 }, { 0x0a, 0x06 }, { 0x0b, 0x08 }, { 0x16, 0x0b }, { 0x17, 0x0a }, { 0x18, 0x28 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x03 }, { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } } }, { { 600 }, 600, 1200, 11000, Ratio{1, 2}, 8, StaggerConfig{}, { { 0x08, 0x00 }, { 0x09, 0x05 }, { 0x0a, 0x06 }, { 0x0b, 0x08 }, { 0x16, 0x0b }, { 0x17, 0x0a }, { 0x18, 0x28 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x03 }, { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } } }, { { 1200 }, 1200, 1200, 11000, Ratio{1, 1}, 16, StaggerConfig{4, 0}, { { 0x08, 0x0d }, { 0x09, 0x0f }, { 0x0a, 0x11 }, { 0x0b, 0x13 }, { 0x16, 0x0b }, { 0x17, 0x0a }, { 0x18, 0x30 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x03 }, { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x0b }, { 0x55, 0x0f }, { 0x56, 0x13 }, { 0x57, 0x17 }, { 0x58, 0x23 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } } }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.optical_resolution = setting.optical_resolution; sensor.register_dpiset = setting.register_dpiset; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.pixel_count_ratio = setting.pixel_count_ratio; sensor.output_pixel_offset = setting.output_pixel_offset; sensor.stagger_y = setting.stagger_y; sensor.custom_regs = setting.custom_regs; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_HP2400; // gl646 sensor.full_resolution = 1200; sensor.black_pixels = 48; sensor.dummy_pixel = 15; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 200; sensor.exposure = { 0x0000, 0x0000, 0x0000 }; sensor.gamma = { 2.1f, 2.1f, 2.1f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned register_dpiset; unsigned exposure_lperiod; Ratio pixel_count_ratio; int output_pixel_offset; StaggerConfig stagger_y; GenesysRegisterSettingSet custom_regs; }; CustomSensorSettings custom_settings[] = { { { 50 }, 200, 7211, Ratio{1, 4}, 0, StaggerConfig{}, { { 0x08, 0x14 }, { 0x09, 0x15 }, { 0x0a, 0x00 }, { 0x0b, 0x00 }, { 0x16, 0xbf }, { 0x17, 0x08 }, { 0x18, 0x3f }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x02 }, { 0x52, 0x0b }, { 0x53, 0x0f }, { 0x54, 0x13 }, { 0x55, 0x17 }, { 0x56, 0x03 }, { 0x57, 0x07 }, { 0x58, 0x63 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } } }, { { 100 }, 400, 7211, Ratio{1, 4}, 1, StaggerConfig{}, { { 0x08, 0x14 }, { 0x09, 0x15 }, { 0x0a, 0x00 }, { 0x0b, 0x00 }, { 0x16, 0xbf }, { 0x17, 0x08 }, { 0x18, 0x3f }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x02 }, { 0x52, 0x0b }, { 0x53, 0x0f }, { 0x54, 0x13 }, { 0x55, 0x17 }, { 0x56, 0x03 }, { 0x57, 0x07 }, { 0x58, 0x63 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } } }, { { 150 }, 600, 7211, Ratio{1, 4}, 1, StaggerConfig{}, { { 0x08, 0x14 }, { 0x09, 0x15 }, { 0x0a, 0x00 }, { 0x0b, 0x00 }, { 0x16, 0xbf }, { 0x17, 0x08 }, { 0x18, 0x3f }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x02 }, { 0x52, 0x0b }, { 0x53, 0x0f }, { 0x54, 0x13 }, { 0x55, 0x17 }, { 0x56, 0x03 }, { 0x57, 0x07 }, { 0x58, 0x63 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } } }, { { 300 }, 1200, 8751, Ratio{1, 4}, 3, StaggerConfig{}, { { 0x08, 0x14 }, { 0x09, 0x15 }, { 0x0a, 0x00 }, { 0x0b, 0x00 }, { 0x16, 0xbf }, { 0x17, 0x08 }, { 0x18, 0x3f }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x02 }, { 0x52, 0x0b }, { 0x53, 0x0f }, { 0x54, 0x13 }, { 0x55, 0x17 }, { 0x56, 0x03 }, { 0x57, 0x07 }, { 0x58, 0x63 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } } }, { { 600 }, 1200, 18760, Ratio{1, 2}, 7, StaggerConfig{}, { { 0x08, 0x0e }, { 0x09, 0x0f }, { 0x0a, 0x00 }, { 0x0b, 0x00 }, { 0x16, 0xbf }, { 0x17, 0x08 }, { 0x18, 0x31 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x02 }, { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x0b }, { 0x55, 0x0f }, { 0x56, 0x13 }, { 0x57, 0x17 }, { 0x58, 0x23 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } } }, { { 1200 }, 1200, 21749, Ratio{1, 1}, 15, StaggerConfig{4, 0}, { { 0x08, 0x02 }, { 0x09, 0x04 }, { 0x0a, 0x00 }, { 0x0b, 0x00 }, { 0x16, 0xbf }, { 0x17, 0x08 }, { 0x18, 0x30 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0xc0 }, { 0x1d, 0x42 }, { 0x52, 0x0b }, { 0x53, 0x0f }, { 0x54, 0x13 }, { 0x55, 0x17 }, { 0x56, 0x03 }, { 0x57, 0x07 }, { 0x58, 0x63 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, { 0x5b, 0x00 }, { 0x5c, 0x0e }, { 0x5d, 0x00 }, { 0x5e, 0x00 } } }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.register_dpiset = setting.register_dpiset; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.pixel_count_ratio = setting.pixel_count_ratio; sensor.output_pixel_offset = setting.output_pixel_offset; sensor.stagger_y = setting.stagger_y; sensor.custom_regs = setting.custom_regs; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_HP2300; // gl646 sensor.full_resolution = 600; sensor.black_pixels = 48; sensor.dummy_pixel = 20; sensor.fau_gain_white_ref = 180; sensor.gain_white_ref = 180; sensor.exposure = { 0x0000, 0x0000, 0x0000 }; sensor.gamma = { 2.1f, 2.1f, 2.1f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned optical_resolution; unsigned register_dpiset; unsigned exposure_lperiod; Ratio pixel_count_ratio; int output_pixel_offset; GenesysRegisterSettingSet custom_regs; }; CustomSensorSettings custom_settings[] = { { { 75 }, 300, 150, 4480, Ratio{1, 2}, 2, { { 0x08, 0x16 }, { 0x09, 0x00 }, { 0x0a, 0x01 }, { 0x0b, 0x03 }, { 0x16, 0xb7 }, { 0x17, 0x0a }, { 0x18, 0x20 }, { 0x19, 0x2a }, { 0x1a, 0x6a }, { 0x1b, 0x8a }, { 0x1c, 0x00 }, { 0x1d, 0x85 }, { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, { 0x5b, 0x06 }, { 0x5c, 0x0b }, { 0x5d, 0x10 }, { 0x5e, 0x16 } } }, { { 150 }, 300, 300, 4350, Ratio{1, 2}, 5, { { 0x08, 0x16 }, { 0x09, 0x00 }, { 0x0a, 0x01 }, { 0x0b, 0x03 }, { 0x16, 0xb7 }, { 0x17, 0x0a }, { 0x18, 0x20 }, { 0x19, 0x2a }, { 0x1a, 0x6a }, { 0x1b, 0x8a }, { 0x1c, 0x00 }, { 0x1d, 0x85 }, { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, { 0x5b, 0x06 }, { 0x5c, 0x0b }, { 0x5d, 0x10 }, { 0x5e, 0x16 } } }, { { 300 }, 300, 600, 4350, Ratio{1, 2}, 10, { { 0x08, 0x16 }, { 0x09, 0x00 }, { 0x0a, 0x01 }, { 0x0b, 0x03 }, { 0x16, 0xb7 }, { 0x17, 0x0a }, { 0x18, 0x20 }, { 0x19, 0x2a }, { 0x1a, 0x6a }, { 0x1b, 0x8a }, { 0x1c, 0x00 }, { 0x1d, 0x85 }, { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, { 0x5b, 0x06 }, { 0x5c, 0x0b }, { 0x5d, 0x10 }, { 0x5e, 0x16 } } }, { { 600 }, 600, 600, 8700, Ratio{1, 1}, 20, { { 0x08, 0x01 }, { 0x09, 0x03 }, { 0x0a, 0x04 }, { 0x0b, 0x06 }, { 0x16, 0xb7 }, { 0x17, 0x0a }, { 0x18, 0x20 }, { 0x19, 0x2a }, { 0x1a, 0x6a }, { 0x1b, 0x8a }, { 0x1c, 0x00 }, { 0x1d, 0x05 }, { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, { 0x5b, 0x06 }, { 0x5c, 0x0b }, { 0x5d, 0x10 }, { 0x5e, 0x16 } } }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.optical_resolution = setting.optical_resolution; sensor.register_dpiset = setting.register_dpiset; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.pixel_count_ratio = setting.pixel_count_ratio; sensor.output_pixel_offset = setting.output_pixel_offset; sensor.custom_regs = setting.custom_regs; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CIS_CANON_LIDE_35; // gl841 sensor.full_resolution = 1200; sensor.register_dpihw = 1200; sensor.black_pixels = 87; sensor.dummy_pixel = 87; sensor.fau_gain_white_ref = 100; sensor.gain_white_ref = 100; sensor.exposure = { 0x0400, 0x0400, 0x0400 }; sensor.custom_regs = { { 0x16, 0x00 }, { 0x17, 0x02 }, { 0x18, 0x00 }, { 0x19, 0x50 }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x02 }, { 0x52, 0x05 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x3a }, { 0x59, 0x03 }, { 0x5a, 0x40 }, { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 0x00 }, }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned optical_resolution; unsigned register_dpiset; unsigned shading_resolution; int output_pixel_offset; }; CustomSensorSettings custom_settings[] = { { { 75 }, 600, 150, 600, 11 }, { { 100 }, 600, 200, 600, 14 }, { { 150 }, 600, 300, 600, 22 }, { { 200 }, 600, 400, 600, 29 }, { { 300 }, 600, 600, 600, 44 }, { { 600 }, 600, 1200, 600, 88 }, { { 1200 }, 1200, 1200, 1200, 88 }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.optical_resolution = setting.optical_resolution; sensor.register_dpiset = setting.register_dpiset; sensor.shading_resolution = setting.shading_resolution; sensor.output_pixel_offset = setting.output_pixel_offset; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CIS_CANON_LIDE_60; // gl841 sensor.full_resolution = 1200; sensor.register_dpihw = 1200; sensor.black_pixels = 87; sensor.dummy_pixel = 87; sensor.fau_gain_white_ref = 100; sensor.gain_white_ref = 100; sensor.exposure = { 0x0400, 0x0400, 0x0400 }; sensor.custom_regs = { { 0x16, 0x00 }, { 0x17, 0x01 }, { 0x18, 0x00 }, { 0x19, 0x50 }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x02 }, { 0x52, 0x05 }, { 0x53, 0x07 }, { 0x54, 0x03 }, { 0x55, 0x05 }, { 0x56, 0x02 }, { 0x57, 0x05 }, { 0x58, 0x3a }, { 0x59, 0x03 }, { 0x5a, 0x40 }, { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 0x00 }, }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned optical_resolution; unsigned register_dpiset; unsigned shading_resolution; int output_pixel_offset; }; CustomSensorSettings custom_settings[] = { { { 75 }, 600, 150, 600, 11 }, { { 100 }, 600, 200, 600, 14 }, { { 150 }, 600, 300, 600, 22 }, { { 200 }, 600, 400, 600, 29 }, { { 300 }, 600, 600, 600, 44 }, { { 600 }, 600, 1200, 600, 88 }, { { 1200 }, 1200, 1200, 1200, 88 }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.optical_resolution = setting.optical_resolution; sensor.register_dpiset = setting.register_dpiset; sensor.shading_resolution = setting.shading_resolution; sensor.output_pixel_offset = setting.output_pixel_offset; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CIS_CANON_LIDE_90; // gl842 sensor.full_resolution = 2400; sensor.black_pixels = 20; sensor.dummy_pixel = 253; sensor.fau_gain_white_ref = 150; sensor.gain_white_ref = 150; sensor.use_host_side_calib = true; sensor.custom_regs = { { 0x16, 0x20 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0xff }, { 0x1a, 0x24 }, { 0x1c, 0x00 }, { 0x1d, 0x04 }, { 0x52, 0x02 }, { 0x53, 0x04 }, { 0x54, 0x02 }, { 0x55, 0x04 }, { 0x56, 0x02 }, { 0x57, 0x04 }, { 0x58, 0x0a }, { 0x59, 0x71 }, { 0x5a, 0x55 }, { 0x70, 0x00 }, { 0x71, 0x05 }, { 0x72, 0x07 }, { 0x73, 0x09 }, { 0x74, 0x00 }, { 0x75, 0x01 }, { 0x76, 0xff }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x3f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x1e }, { 0x7d, 0x11 }, { 0x7f, 0x50 } }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned optical_resolution; unsigned register_dpihw; unsigned register_dpiset; unsigned shading_resolution; unsigned shading_factor; int output_pixel_offset; SensorExposure exposure; unsigned exposure_lperiod; unsigned segment_size; std::vector segment_order; }; CustomSensorSettings custom_settings[] = { { { 300 }, 300, 600, 600, 300, 2, 280, { 955, 1235, 675 }, 6500, 5152, std::vector{} }, { { 600 }, 600, 600, 600, 600, 1, 250, { 1655, 2075, 1095 }, 6536, 5152, std::vector{} }, { { 1200 }, 1200, 1200, 1200, 1200, 1, 500, { 3055, 4175, 1935 }, 12688, 5152, {0, 1} }, { { 2400 }, 2400, 2400, 2400, 2400, 1, 1000, { 5855, 7535, 3615 }, 21500, 5152, {0, 1, 2, 3} }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.optical_resolution = setting.optical_resolution; sensor.register_dpihw = setting.register_dpihw; sensor.register_dpiset = setting.register_dpiset; sensor.shading_resolution = setting.shading_resolution; sensor.shading_factor = setting.shading_factor; sensor.output_pixel_offset = setting.output_pixel_offset; sensor.exposure = setting.exposure; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.segment_size = setting.segment_size; sensor.segment_order = setting.segment_order; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CIS_XP200; // gl646 sensor.full_resolution = 600; sensor.black_pixels = 5; sensor.dummy_pixel = 38; sensor.fau_gain_white_ref = 200; sensor.gain_white_ref = 200; sensor.exposure = { 0x1450, 0x0c80, 0x0a28 }; sensor.custom_regs = { { 0x08, 0x06 }, { 0x09, 0x07 }, { 0x0a, 0x0a }, { 0x0b, 0x04 }, { 0x16, 0x24 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x2a }, { 0x1a, 0x0a }, { 0x1b, 0x0a }, { 0x1c, 0x00 }, { 0x1d, 0x11 }, { 0x52, 0x08 }, { 0x53, 0x02 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x1a }, { 0x59, 0x51 }, { 0x5a, 0x00 }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } }; sensor.gamma = { 2.1f, 2.1f, 2.1f }; { struct CustomSensorSettings { ValueFilterAny resolutions; std::vector channels; unsigned exposure_lperiod; SensorExposure exposure; int output_pixel_offset; }; CustomSensorSettings custom_settings[] = { { { 75 }, { 3 }, 5700, { 0x1644, 0x0c80, 0x092e }, 4 }, { { 100 }, { 3 }, 5700, { 0x1644, 0x0c80, 0x092e }, 6 }, { { 200 }, { 3 }, 5700, { 0x1644, 0x0c80, 0x092e }, 12 }, { { 300 }, { 3 }, 9000, { 0x1644, 0x0c80, 0x092e }, 19 }, { { 600 }, { 3 }, 16000, { 0x1644, 0x0c80, 0x092e }, 38 }, { { 75 }, { 1 }, 16000, { 0x050a, 0x0fa0, 0x1010 }, 4 }, { { 100 }, { 1 }, 7800, { 0x050a, 0x0fa0, 0x1010 }, 6 }, { { 200 }, { 1 }, 11000, { 0x050a, 0x0fa0, 0x1010 }, 12 }, { { 300 }, { 1 }, 13000, { 0x050a, 0x0fa0, 0x1010 }, 19 }, { { 600 }, { 1 }, 24000, { 0x050a, 0x0fa0, 0x1010 }, 38 }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.channels = setting.channels; sensor.register_dpiset = setting.resolutions.values()[0]; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.exposure = setting.exposure; sensor.output_pixel_offset = setting.output_pixel_offset; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_HP3670; // gl646 sensor.full_resolution = 1200; sensor.black_pixels = 48; sensor.dummy_pixel = 16; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 200; sensor.exposure = { 0, 0, 0 }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned register_dpiset; unsigned exposure_lperiod; Ratio pixel_count_ratio; int output_pixel_offset; StaggerConfig stagger_y; GenesysRegisterSettingSet custom_regs; }; CustomSensorSettings custom_settings[] = { { { 50 }, 200, 5758, Ratio{1, 4}, 0, StaggerConfig{}, { { 0x08, 0x00 }, { 0x09, 0x0a }, { 0x0a, 0x0b }, { 0x0b, 0x0d }, { 0x16, 0x33 }, { 0x17, 0x07 }, { 0x18, 0x33 }, { 0x19, 0x2a }, { 0x1a, 0x02 }, { 0x1b, 0x13 }, { 0x1c, 0xc0 }, { 0x1d, 0x43 }, { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x15 }, { 0x5a, 0xc1 }, { 0x5b, 0x05 }, { 0x5c, 0x0a }, { 0x5d, 0x0f }, { 0x5e, 0x00 } } }, { { 75 }, 300, 4879, Ratio{1, 4}, 1, StaggerConfig{}, { { 0x08, 0x00 }, { 0x09, 0x0a }, { 0x0a, 0x0b }, { 0x0b, 0x0d }, { 0x16, 0x33 }, { 0x17, 0x07 }, { 0x18, 0x33 }, { 0x19, 0x2a }, { 0x1a, 0x02 }, { 0x1b, 0x13 }, { 0x1c, 0xc0 }, { 0x1d, 0x43 }, { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x15 }, { 0x5a, 0xc1 }, { 0x5b, 0x05 }, { 0x5c, 0x0a }, { 0x5d, 0x0f }, { 0x5e, 0x00 } } }, { { 100 }, 400, 4487, Ratio{1, 4}, 1, StaggerConfig{}, { { 0x08, 0x00 }, { 0x09, 0x0a }, { 0x0a, 0x0b }, { 0x0b, 0x0d }, { 0x16, 0x33 }, { 0x17, 0x07 }, { 0x18, 0x33 }, { 0x19, 0x2a }, { 0x1a, 0x02 }, { 0x1b, 0x13 }, { 0x1c, 0xc0 }, { 0x1d, 0x43 }, { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x15 }, { 0x5a, 0xc1 }, { 0x5b, 0x05 }, { 0x5c, 0x0a }, { 0x5d, 0x0f }, { 0x5e, 0x00 } } }, { { 150 }, 600, 4879, Ratio{1, 4}, 2, StaggerConfig{}, { { 0x08, 0x00 }, { 0x09, 0x0a }, { 0x0a, 0x0b }, { 0x0b, 0x0d }, { 0x16, 0x33 }, { 0x17, 0x07 }, { 0x18, 0x33 }, { 0x19, 0x2a }, { 0x1a, 0x02 }, { 0x1b, 0x13 }, { 0x1c, 0xc0 }, { 0x1d, 0x43 }, { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x15 }, { 0x5a, 0xc1 }, { 0x5b, 0x05 }, { 0x5c, 0x0a }, { 0x5d, 0x0f }, { 0x5e, 0x00 } } }, { { 300 }, 1200, 4503, Ratio{1, 4}, 4, StaggerConfig{}, { { 0x08, 0x00 }, { 0x09, 0x0a }, { 0x0a, 0x0b }, { 0x0b, 0x0d }, { 0x16, 0x33 }, { 0x17, 0x07 }, { 0x18, 0x33 }, { 0x19, 0x2a }, { 0x1a, 0x02 }, { 0x1b, 0x13 }, { 0x1c, 0xc0 }, { 0x1d, 0x43 }, { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x15 }, { 0x5a, 0xc1 }, { 0x5b, 0x05 }, { 0x5c, 0x0a }, { 0x5d, 0x0f }, { 0x5e, 0x00 } } }, { { 600 }, 1200, 10251, Ratio{1, 2}, 8, StaggerConfig{}, { { 0x08, 0x00 }, { 0x09, 0x05 }, { 0x0a, 0x06 }, { 0x0b, 0x08 }, { 0x16, 0x33 }, { 0x17, 0x07 }, { 0x18, 0x31 }, { 0x19, 0x2a }, { 0x1a, 0x02 }, { 0x1b, 0x0e }, { 0x1c, 0xc0 }, { 0x1d, 0x43 }, { 0x52, 0x0b }, { 0x53, 0x0f }, { 0x54, 0x13 }, { 0x55, 0x17 }, { 0x56, 0x03 }, { 0x57, 0x07 }, { 0x58, 0x63 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, { 0x5b, 0x02 }, { 0x5c, 0x0e }, { 0x5d, 0x00 }, { 0x5e, 0x00 } } }, { { 1200 }, 1200, 12750, Ratio{1, 1}, 16, StaggerConfig{4, 0}, { { 0x08, 0x0d }, { 0x09, 0x0f }, { 0x0a, 0x11 }, { 0x0b, 0x13 }, { 0x16, 0x2b }, { 0x17, 0x07 }, { 0x18, 0x30 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0xc0 }, { 0x1d, 0x43 }, { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x0b }, { 0x55, 0x0f }, { 0x56, 0x13 }, { 0x57, 0x17 }, { 0x58, 0x23 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } } }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.register_dpiset = setting.register_dpiset; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.pixel_count_ratio = setting.pixel_count_ratio; sensor.output_pixel_offset = setting.output_pixel_offset; sensor.stagger_y = setting.stagger_y; sensor.custom_regs = setting.custom_regs; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_DP665; // gl841 sensor.full_resolution = 600; sensor.register_dpihw = 600; sensor.shading_resolution = 600; sensor.black_pixels = 27; sensor.dummy_pixel = 27; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 200; sensor.exposure = { 0x1100, 0x1100, 0x1100 }; sensor.custom_regs = { { 0x16, 0x00 }, { 0x17, 0x02 }, { 0x18, 0x04 }, { 0x19, 0x50 }, { 0x1a, 0x10 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x02 }, { 0x52, 0x04 }, { 0x53, 0x05 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x54 }, { 0x59, 0x03 }, { 0x5a, 0x00 }, { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 0x00 }, }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned register_dpiset; int output_pixel_offset; }; CustomSensorSettings custom_settings[] = { { { 75 }, 75, 1 }, { { 150 }, 150, 3 }, { { 300 }, 300, 7 }, { { 600 }, 600, 14 }, { { 1200 }, 1200, 28 }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.register_dpiset = setting.register_dpiset; sensor.output_pixel_offset = setting.output_pixel_offset; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_ROADWARRIOR; // gl841 sensor.full_resolution = 600; sensor.register_dpihw = 600; sensor.shading_resolution = 600; sensor.black_pixels = 27; sensor.dummy_pixel = 27; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 200; sensor.exposure = { 0x1100, 0x1100, 0x1100 }; sensor.custom_regs = { { 0x16, 0x00 }, { 0x17, 0x02 }, { 0x18, 0x04 }, { 0x19, 0x50 }, { 0x1a, 0x10 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x02 }, { 0x52, 0x04 }, { 0x53, 0x05 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x54 }, { 0x59, 0x03 }, { 0x5a, 0x00 }, { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 0x00 }, }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned register_dpiset; int output_pixel_offset; }; CustomSensorSettings custom_settings[] = { { { 75 }, 75, 1 }, { { 150 }, 150, 3 }, { { 300 }, 300, 7 }, { { 600 }, 600, 14 }, { { 1200 }, 1200, 28 }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.register_dpiset = setting.register_dpiset; sensor.output_pixel_offset = setting.output_pixel_offset; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_DSMOBILE600; // gl841 sensor.full_resolution = 600; sensor.register_dpihw = 600; sensor.shading_resolution = 600; sensor.black_pixels = 28; sensor.dummy_pixel = 28; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 200; sensor.exposure = { 0x1544, 0x1544, 0x1544 }; sensor.custom_regs = { { 0x16, 0x00 }, { 0x17, 0x02 }, { 0x18, 0x04 }, { 0x19, 0x50 }, { 0x1a, 0x10 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x02 }, { 0x52, 0x04 }, { 0x53, 0x05 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x54 }, { 0x59, 0x03 }, { 0x5a, 0x00 }, { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 0x00 }, }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned register_dpiset; int output_pixel_offset; }; CustomSensorSettings custom_settings[] = { { { 75 }, 75, 3 }, { { 150 }, 150, 7 }, { { 300 }, 300, 14 }, { { 600 }, 600, 29 }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.register_dpiset = setting.register_dpiset; sensor.output_pixel_offset = setting.output_pixel_offset; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_XP300; // gl841 sensor.full_resolution = 600; sensor.register_dpihw = 1200; // FIXME: could be incorrect, but previous code used this value sensor.shading_resolution = 600; sensor.black_pixels = 27; sensor.dummy_pixel = 27; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 200; sensor.exposure = { 0x1100, 0x1100, 0x1100 }; sensor.custom_regs = { { 0x16, 0x00 }, { 0x17, 0x02 }, { 0x18, 0x04 }, { 0x19, 0x50 }, { 0x1a, 0x10 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x02 }, { 0x52, 0x04 }, { 0x53, 0x05 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x54 }, { 0x59, 0x03 }, { 0x5a, 0x00 }, { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 0x00 }, }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned register_dpiset; int output_pixel_offset; }; CustomSensorSettings custom_settings[] = { { { 75 }, 150, 3 }, { { 150 }, 300, 7 }, { { 300 }, 600, 14 }, { { 600 }, 1200, 28 }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.register_dpiset = setting.register_dpiset; sensor.output_pixel_offset = setting.output_pixel_offset; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_DOCKETPORT_487; // gl841 sensor.full_resolution = 600; sensor.register_dpihw = 600; sensor.shading_resolution = 600; sensor.black_pixels = 27; sensor.dummy_pixel = 27; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 200; sensor.exposure = { 0x1100, 0x1100, 0x1100 }; sensor.custom_regs = { { 0x16, 0x00 }, { 0x17, 0x02 }, { 0x18, 0x04 }, { 0x19, 0x50 }, { 0x1a, 0x10 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x02 }, { 0x52, 0x04 }, { 0x53, 0x05 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x54 }, { 0x59, 0x03 }, { 0x5a, 0x00 }, { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 0x00 }, }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned register_dpiset; int output_pixel_offset; }; CustomSensorSettings custom_settings[] = { { { 75 }, 150, 3 }, { { 150 }, 300, 7 }, { { 300 }, 600, 14 }, { { 600 }, 600, 28 }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.register_dpiset = setting.register_dpiset; sensor.output_pixel_offset = setting.output_pixel_offset; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_DP685; // gl841 sensor.full_resolution = 600; sensor.register_dpihw = 600; sensor.shading_resolution = 600; sensor.full_resolution = 600; sensor.black_pixels = 27; sensor.dummy_pixel = 27; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 200; sensor.exposure = { 0x1100, 0x1100, 0x1100 }; sensor.custom_regs = { { 0x16, 0x00 }, { 0x17, 0x02 }, { 0x18, 0x04 }, { 0x19, 0x50 }, { 0x1a, 0x10 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x02 }, { 0x52, 0x04 }, { 0x53, 0x05 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x54 }, { 0x59, 0x03 }, { 0x5a, 0x00 }, { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 0x00 }, }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned register_dpiset; int output_pixel_offset; }; CustomSensorSettings custom_settings[] = { { { 75 }, 75, 3 }, { { 150 }, 150, 6 }, { { 300 }, 300, 13 }, { { 600 }, 600, 27 }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.register_dpiset = setting.register_dpiset; sensor.output_pixel_offset = setting.output_pixel_offset; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CIS_CANON_LIDE_200; // gl847 sensor.full_resolution = 4800; sensor.black_pixels = 87*4; sensor.dummy_pixel = 16*4; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 200; sensor.exposure = { 0x0000, 0x0000, 0x0000 }; sensor.gamma = { 2.2f, 2.2f, 2.2f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned register_dpihw; int exposure_lperiod; SensorExposure exposure; Ratio pixel_count_ratio; unsigned shading_factor; int output_pixel_offset; unsigned segment_size; std::vector segment_order; GenesysRegisterSettingSet custom_regs; }; CustomSensorSettings custom_settings[] = { // Note: Windows driver uses 1424 lperiod and enables dummy line (0x17) { { 150 }, 600, 2848, { 304, 203, 180 }, Ratio{1, 8}, 4, 80, 5136, std::vector{}, { { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, // Note: Windows driver uses 1424 lperiod and enables dummy line (0x17) { { 200 }, 600, 2848, { 304, 203, 180 }, Ratio{1, 8}, 3, 106, 5136, std::vector{}, { { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, // Note: Windows driver uses 788 lperiod and enables dummy line (0x17) { { 300 }, 600, 1424, { 304, 203, 180 }, Ratio{1, 8}, 2, 160, 5136, std::vector{}, { { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, // Note: Windows driver uses 788 lperiod and enables dummy line (0x17) { { 400 }, 600, 1424, { 304, 203, 180 }, Ratio{1, 8}, 1, 213, 5136, std::vector{}, { { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, { { 600 }, 600, 1432, { 492, 326, 296 }, Ratio{1, 8}, 1, 320, 5136, std::vector{}, { { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, { { 1200 }, 1200, 2712, { 935, 592, 538 }, Ratio{1, 8}, 1, 640, 5136, { 0, 1 }, { { 0x16, 0x10 }, { 0x17, 0x08 }, { 0x18, 0x00 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, { { 2400 }, 2400, 5280, { 1777, 1125, 979 }, Ratio{1, 8}, 1, 1280, 5136, { 0, 2, 1, 3 }, { { 0x16, 0x10 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, { { 4800 }, 4800, 10416, { 3377, 2138, 1780 }, Ratio{1, 8}, 1, 2560, 5136, { 0, 2, 4, 6, 1, 3, 5, 7 }, { { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } } }; for (const auto& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.register_dpihw = setting.register_dpihw; sensor.register_dpiset = setting.resolutions.values()[0]; sensor.shading_resolution = setting.register_dpihw; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.exposure = setting.exposure; sensor.pixel_count_ratio = setting.pixel_count_ratio; sensor.shading_factor = setting.shading_factor; sensor.output_pixel_offset = setting.output_pixel_offset; sensor.segment_size = setting.segment_size; sensor.segment_order = setting.segment_order; sensor.custom_regs = setting.custom_regs; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CIS_CANON_LIDE_700F; // gl847 sensor.full_resolution = 4800; sensor.black_pixels = 73*8; // black pixels 73 at 600 dpi sensor.dummy_pixel = 16*8; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 200; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned register_dpihw; int exposure_lperiod; SensorExposure exposure; Ratio pixel_count_ratio; unsigned shading_factor; int output_pixel_offset; unsigned segment_size; std::vector segment_order; GenesysRegisterSettingSet custom_regs; }; CustomSensorSettings custom_settings[] = { { { 75 }, 600, 2848, { 465, 310, 239 }, Ratio{1, 8}, 8, 48, 5187, std::vector{}, { { 0x16, 0x10 }, { 0x17, 0x0c }, { 0x18, 0x00 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x87 }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0xf9 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, { { 100 }, 600, 2848, { 465, 310, 239 }, Ratio{1, 8}, 6, 64, 5187, std::vector{}, { { 0x16, 0x10 }, { 0x17, 0x0c }, { 0x18, 0x00 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x87 }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0xf9 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, { { 150 }, 600, 2848, { 465, 310, 239 }, Ratio{1, 8}, 4, 96, 5187, std::vector{}, { { 0x16, 0x10 }, { 0x17, 0x0c }, { 0x18, 0x00 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x87 }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0xf9 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, { { 200 }, 600, 2848, { 465, 310, 239 }, Ratio{1, 8}, 3, 128, 5187, std::vector{}, { { 0x16, 0x10 }, { 0x17, 0x0c }, { 0x18, 0x00 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x87 }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0xf9 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, { { 300 }, 600, 1424, { 465, 310, 239 }, Ratio{1, 8}, 2, 192, 5187, std::vector{}, { { 0x16, 0x10 }, { 0x17, 0x0c }, { 0x18, 0x00 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x87 }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0xf9 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, { { 600 }, 600, 1504, { 465, 310, 239 }, Ratio{1, 8}, 1, 384, 5187, std::vector{}, { { 0x16, 0x10 }, { 0x17, 0x0c }, { 0x18, 0x00 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x87 }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0xf9 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, { { 1200 }, 1200, 2696, { 1464, 844, 555 }, Ratio{1, 8}, 1, 768, 5187, { 0, 1 }, { { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x87 }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0xf9 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, { { 2400 }, 2400, 10576, { 2798, 1558, 972 }, Ratio{1, 8}, 1, 1536, 5187, { 0, 1, 2, 3 }, { { 0x16, 0x10 }, { 0x17, 0x08 }, { 0x18, 0x00 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x87 }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0xf9 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, { { 4800 }, 4800, 10576, { 2798, 1558, 972 }, Ratio{1, 8}, 1, 3072, 5187, { 0, 1, 4, 5, 2, 3, 6, 7 }, { { 0x16, 0x10 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x87 }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0xf9 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } } }; for (const auto& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.register_dpihw = setting.register_dpihw; sensor.register_dpiset = setting.resolutions.values()[0]; sensor.shading_resolution = setting.register_dpihw; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.exposure = setting.exposure; sensor.pixel_count_ratio = setting.pixel_count_ratio; sensor.shading_factor = setting.shading_factor; sensor.output_pixel_offset = setting.output_pixel_offset; sensor.segment_size = setting.segment_size; sensor.segment_order = setting.segment_order; sensor.custom_regs = setting.custom_regs; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CIS_CANON_LIDE_100; // gl847 sensor.full_resolution = 2400; sensor.black_pixels = 87*4; sensor.dummy_pixel = 16*4; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 200; sensor.exposure = { 0x01c1, 0x0126, 0x00e5 }; sensor.gamma = { 2.2f, 2.2f, 2.2f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned register_dpihw; int exposure_lperiod; SensorExposure exposure; Ratio pixel_count_ratio; unsigned shading_factor; int output_pixel_offset; unsigned segment_size; std::vector segment_order; GenesysRegisterSettingSet custom_regs; }; CustomSensorSettings custom_settings[] = { { { 150 }, 600, 2304, { 423, 294, 242 }, Ratio{1, 4}, 4, 80, 5136, std::vector{}, { { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, { { 200 }, 600, 2304, { 423, 294, 242 }, Ratio{1, 4}, 3, 106, 5136, std::vector{}, { { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, { { 300 }, 600, 1728, { 423, 294, 242 }, Ratio{1, 4}, 2, 160, 5136, std::vector{}, { { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, { { 600 }, 600, 1432, { 423, 294, 242 }, Ratio{1, 4}, 1, 320, 5136, std::vector{}, { { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, }, }, { { 1200 }, 1200, 2712, { 791, 542, 403 }, Ratio{1, 4}, 1, 640, 5136, {0, 1}, { { 0x16, 0x10 }, { 0x17, 0x08 }, { 0x18, 0x00 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } }, { { 2400 }, 2400, 5280, { 1504, 1030, 766 }, Ratio{1, 4}, 1, 1280, 5136, {0, 2, 1, 3}, { { 0x16, 0x10 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, } } }; for (const auto& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.register_dpihw = setting.register_dpihw; sensor.register_dpiset = setting.resolutions.values()[0]; sensor.shading_resolution = setting.register_dpihw; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.exposure = setting.exposure; sensor.pixel_count_ratio = setting.pixel_count_ratio; sensor.shading_factor = setting.shading_factor; sensor.output_pixel_offset = setting.output_pixel_offset; sensor.segment_size = setting.segment_size; sensor.segment_order = setting.segment_order; sensor.custom_regs = setting.custom_regs; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_KVSS080; // gl843 sensor.full_resolution = 600; sensor.register_dpihw = 600; sensor.shading_resolution = 600; sensor.black_pixels = 38; sensor.dummy_pixel = 38; sensor.fau_gain_white_ref = 160; sensor.gain_white_ref = 160; sensor.exposure = { 0x0000, 0x0000, 0x0000 }; sensor.exposure_lperiod = 8000; sensor.custom_regs = { { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x00 }, { 0x77, 0x00 }, { 0x78, 0xff }, { 0x79, 0xff }, { 0x7a, 0x03 }, { 0x7b, 0xff }, { 0x7c, 0xff }, { 0x0c, 0x00 }, { 0x70, 0x01 }, { 0x71, 0x03 }, { 0x9e, 0x00 }, { 0xaa, 0x00 }, { 0x16, 0x33 }, { 0x17, 0x1c }, { 0x18, 0x00 }, { 0x19, 0x2a }, { 0x1a, 0x2c }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x04 }, { 0x52, 0x0c }, { 0x53, 0x0f }, { 0x54, 0x00 }, { 0x55, 0x03 }, { 0x56, 0x06 }, { 0x57, 0x09 }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0xc0 }, { 0x7d, 0x90 }, }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned register_dpiset; Ratio pixel_count_ratio; int output_pixel_offset; }; CustomSensorSettings custom_settings[] = { { { 75 }, 75, Ratio{1, 1}, 4 }, { { 100 }, 100, Ratio{1, 1}, 6 }, { { 150 }, 150, Ratio{1, 1}, 9 }, { { 200 }, 200, Ratio{1, 1}, 12 }, { { 300 }, 300, Ratio{1, 1}, 19 }, { { 600 }, 600, Ratio{1, 1}, 38 }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.register_dpiset = setting.register_dpiset; sensor.pixel_count_ratio = setting.pixel_count_ratio; sensor.output_pixel_offset = setting.output_pixel_offset; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_G4050; // gl843 sensor.full_resolution = 4800; sensor.black_pixels = 50*8; // 31 at 600 dpi dummy_pixels 58 at 1200 sensor.dummy_pixel = 58; sensor.fau_gain_white_ref = 160; sensor.gain_white_ref = 160; sensor.exposure = { 0x2c09, 0x22b8, 0x10f0 }; sensor.custom_regs = {}; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned register_dpihw; unsigned register_dpiset; int exposure_lperiod; ScanMethod method; Ratio pixel_count_ratio; int output_pixel_offset; StaggerConfig stagger_y; // FIXME: may be incorrect GenesysRegisterSettingSet extra_custom_regs; }; GenesysRegisterSettingSet regs_100_to_600 = { { 0x0c, 0x00 }, { 0x16, 0x33 }, { 0x17, 0x0c }, { 0x18, 0x00 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x08 }, { 0x52, 0x0b }, { 0x53, 0x0e }, { 0x54, 0x11 }, { 0x55, 0x02 }, { 0x56, 0x05 }, { 0x57, 0x08 }, { 0x58, 0x63 }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x74, 0x00 }, { 0x75, 0x01 }, { 0x76, 0xff }, { 0x77, 0x03 }, { 0x78, 0xff }, { 0x79, 0xff }, { 0x7a, 0x03 }, { 0x7b, 0xff }, { 0x7c, 0xff }, { 0x7d, 0x90 }, { 0x9e, 0x00 }, { 0xaa, 0x00 }, }; GenesysRegisterSettingSet regs_1200 = { { 0x0c, 0x20 }, { 0x16, 0x3b }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x38 }, { 0x1b, 0x10 }, { 0x1c, 0x00 }, { 0x1d, 0x08 }, { 0x52, 0x02 }, { 0x53, 0x05 }, { 0x54, 0x08 }, { 0x55, 0x0b }, { 0x56, 0x0e }, { 0x57, 0x11 }, { 0x58, 0x1b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x08 }, { 0x71, 0x0c }, { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff }, { 0x77, 0x00 }, { 0x78, 0x01 }, { 0x79, 0xff }, { 0x7a, 0x00 }, { 0x7b, 0x01 }, { 0x7c, 0xff }, { 0x7d, 0x90 }, { 0x9e, 0xc0 }, { 0xaa, 0x05 }, }; GenesysRegisterSettingSet regs_2400 = { { 0x0c, 0x20 }, { 0x16, 0x3b }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x38 }, { 0x1b, 0x10 }, { 0x1c, 0xc0 }, { 0x1d, 0x08 }, { 0x52, 0x02 }, { 0x53, 0x05 }, { 0x54, 0x08 }, { 0x55, 0x0b }, { 0x56, 0x0e }, { 0x57, 0x11 }, { 0x58, 0x1b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x08 }, { 0x71, 0x0a }, { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 }, { 0x7d, 0x90 }, { 0x9e, 0xc0 }, { 0xaa, 0x05 }, }; GenesysRegisterSettingSet regs_4800 = { { 0x0c, 0x21 }, { 0x16, 0x3b }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x38 }, { 0x1b, 0x10 }, { 0x1c, 0xc1 }, { 0x1d, 0x08 }, { 0x52, 0x02 }, { 0x53, 0x05 }, { 0x54, 0x08 }, { 0x55, 0x0b }, { 0x56, 0x0e }, { 0x57, 0x11 }, { 0x58, 0x1b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x08 }, { 0x71, 0x0a }, { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 }, { 0x7d, 0x90 }, { 0x9e, 0xc0 }, { 0xaa, 0x07 }, }; GenesysRegisterSettingSet regs_ta_any = { { 0x0c, 0x00 }, { 0x16, 0x33 }, { 0x17, 0x4c }, { 0x18, 0x01 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x08 }, { 0x52, 0x0e }, { 0x53, 0x11 }, { 0x54, 0x02 }, { 0x55, 0x05 }, { 0x56, 0x08 }, { 0x57, 0x0b }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0xc0 }, { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x74, 0x00 }, { 0x75, 0x1c }, { 0x76, 0x7f }, { 0x77, 0x03 }, { 0x78, 0xff }, { 0x79, 0xff }, { 0x7a, 0x03 }, { 0x7b, 0xff }, { 0x7c, 0xff }, { 0x7d, 0x90 }, { 0x9e, 0x00 }, { 0xaa, 0x00 }, }; CustomSensorSettings custom_settings[] = { { { 100 }, 600, 100, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 1, StaggerConfig{}, regs_100_to_600 }, { { 150 }, 600, 150, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 1, StaggerConfig{}, regs_100_to_600 }, { { 200 }, 600, 200, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 2, StaggerConfig{}, regs_100_to_600 }, { { 300 }, 600, 300, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 3, StaggerConfig{}, regs_100_to_600 }, { { 400 }, 600, 400, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 4, StaggerConfig{}, regs_100_to_600 }, { { 600 }, 600, 600, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 7, StaggerConfig{}, regs_100_to_600 }, { { 1200 }, 1200, 1200, 56064, ScanMethod::FLATBED, Ratio{1, 4}, 14, StaggerConfig{}, regs_1200 }, { { 2400 }, 2400, 2400, 56064, ScanMethod::FLATBED, Ratio{1, 2}, 29, StaggerConfig{4, 0}, regs_2400 }, { { 4800 }, 4800, 4800, 42752, ScanMethod::FLATBED, Ratio{1, 1}, 58, StaggerConfig{8, 0}, regs_4800 }, { { 100, 150, 200, 300, 400, 600, 1200 }, 600, 600, 15624, ScanMethod::TRANSPARENCY, Ratio{1, 1}, 58, StaggerConfig{}, regs_ta_any }, // FIXME: may be incorrect { { 2400 }, 600, 600, 15624, ScanMethod::TRANSPARENCY, Ratio{1, 1}, 58, StaggerConfig{4, 0}, regs_ta_any }, // FIXME: may be incorrect { { 4800 }, 600, 600, 15624, ScanMethod::TRANSPARENCY, Ratio{1, 1}, 58, StaggerConfig{8, 0}, regs_ta_any }, // FIXME: may be incorrect }; auto base_custom_regs = sensor.custom_regs; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.register_dpihw = setting.register_dpihw; sensor.register_dpiset = setting.register_dpiset; sensor.shading_resolution = setting.register_dpihw; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.method = setting.method; sensor.pixel_count_ratio = setting.pixel_count_ratio; sensor.output_pixel_offset = setting.output_pixel_offset; sensor.stagger_y = setting.stagger_y; sensor.custom_regs = base_custom_regs; sensor.custom_regs.merge(setting.extra_custom_regs); s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_HP_4850C; // gl843 sensor.full_resolution = 4800; sensor.black_pixels = 100; sensor.dummy_pixel = 58; sensor.fau_gain_white_ref = 160; sensor.gain_white_ref = 160; sensor.exposure = { 0x2c09, 0x22b8, 0x10f0 }; sensor.custom_regs = {}; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned register_dpihw; unsigned register_dpiset; int exposure_lperiod; ScanMethod method; Ratio pixel_count_ratio; int output_pixel_offset; int shading_pixel_offset; StaggerConfig stagger_y; // FIXME: review, may be incorrect GenesysRegisterSettingSet extra_custom_regs; }; GenesysRegisterSettingSet regs_100_to_600 = { { 0x0c, 0x00 }, { 0x16, 0x33 }, { 0x17, 0x0c }, { 0x18, 0x00 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x08 }, { 0x52, 0x0b }, { 0x53, 0x0e }, { 0x54, 0x11 }, { 0x55, 0x02 }, { 0x56, 0x05 }, { 0x57, 0x08 }, { 0x58, 0x63 }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x74, 0x00 }, { 0x75, 0x01 }, { 0x76, 0xff }, { 0x77, 0x03 }, { 0x78, 0xff }, { 0x79, 0xff }, { 0x7a, 0x03 }, { 0x7b, 0xff }, { 0x7c, 0xff }, { 0x7d, 0x90 }, { 0x9e, 0x00 }, { 0xaa, 0x00 }, }; GenesysRegisterSettingSet regs_1200 = { { 0x0c, 0x20 }, { 0x16, 0x3b }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x38 }, { 0x1b, 0x10 }, { 0x1c, 0x00 }, { 0x1d, 0x08 }, { 0x52, 0x02 }, { 0x53, 0x05 }, { 0x54, 0x08 }, { 0x55, 0x0b }, { 0x56, 0x0e }, { 0x57, 0x11 }, { 0x58, 0x1b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x08 }, { 0x71, 0x0c }, { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff }, { 0x77, 0x00 }, { 0x78, 0x01 }, { 0x79, 0xff }, { 0x7a, 0x00 }, { 0x7b, 0x01 }, { 0x7c, 0xff }, { 0x7d, 0x90 }, { 0x9e, 0xc0 }, { 0xaa, 0x05 }, }; GenesysRegisterSettingSet regs_2400 = { { 0x0c, 0x20 }, { 0x16, 0x3b }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x38 }, { 0x1b, 0x10 }, { 0x1c, 0xc0 }, { 0x1d, 0x08 }, { 0x52, 0x02 }, { 0x53, 0x05 }, { 0x54, 0x08 }, { 0x55, 0x0b }, { 0x56, 0x0e }, { 0x57, 0x11 }, { 0x58, 0x1b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x08 }, { 0x71, 0x0a }, { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 }, { 0x7d, 0x90 }, { 0x9e, 0xc0 }, { 0xaa, 0x05 }, }; GenesysRegisterSettingSet regs_4800 = { { 0x0c, 0x21 }, { 0x16, 0x3b }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x38 }, { 0x1b, 0x10 }, { 0x1c, 0xc1 }, { 0x1d, 0x08 }, { 0x52, 0x02 }, { 0x53, 0x05 }, { 0x54, 0x08 }, { 0x55, 0x0b }, { 0x56, 0x0e }, { 0x57, 0x11 }, { 0x58, 0x1b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x08 }, { 0x71, 0x0a }, { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 }, { 0x7d, 0x90 }, { 0x9e, 0xc0 }, { 0xaa, 0x07 }, }; GenesysRegisterSettingSet regs_ta_any = { { 0x0c, 0x00 }, { 0x16, 0x33 }, { 0x17, 0x4c }, { 0x18, 0x01 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x08 }, { 0x52, 0x0e }, { 0x53, 0x11 }, { 0x54, 0x02 }, { 0x55, 0x05 }, { 0x56, 0x08 }, { 0x57, 0x0b }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0xc0 }, { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x74, 0x00 }, { 0x75, 0x1c }, { 0x76, 0x7f }, { 0x77, 0x03 }, { 0x78, 0xff }, { 0x79, 0xff }, { 0x7a, 0x03 }, { 0x7b, 0xff }, { 0x7c, 0xff }, { 0x7d, 0x90 }, { 0x9e, 0x00 }, { 0xaa, 0x00 }, }; CustomSensorSettings custom_settings[] = { { { 100 }, 600, 100, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 1, 50, StaggerConfig{}, regs_100_to_600 }, { { 150 }, 600, 150, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 1, 50, StaggerConfig{}, regs_100_to_600 }, { { 200 }, 600, 200, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 2, 50, StaggerConfig{}, regs_100_to_600 }, { { 300 }, 600, 300, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 3, 50, StaggerConfig{}, regs_100_to_600 }, { { 400 }, 600, 400, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 4, 50, StaggerConfig{}, regs_100_to_600 }, { { 600 }, 600, 600, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 7, 50, StaggerConfig{}, regs_100_to_600 }, { { 1200 }, 1200, 1200, 56064, ScanMethod::FLATBED, Ratio{1, 4}, 14, 0, StaggerConfig{}, regs_1200 }, { { 2400 }, 2400, 2400, 56064, ScanMethod::FLATBED, Ratio{1, 2}, 29, 0, StaggerConfig{0, 4}, regs_2400 }, { { 4800 }, 4800, 4800, 42752, ScanMethod::FLATBED, Ratio{1, 1}, 58, 0, StaggerConfig{0, 8}, regs_4800 }, { { 100, 150, 200, 300, 400, 600, 1200}, 600, 600, 15624, ScanMethod::TRANSPARENCY, Ratio{1, 1}, 58, 0, StaggerConfig{}, regs_ta_any }, // FIXME: review { { 2400 }, 600, 600, 15624, ScanMethod::TRANSPARENCY, Ratio{1, 1}, 58, 0, StaggerConfig{0, 4}, regs_ta_any }, // FIXME: review { { 4800 }, 600, 600, 15624, ScanMethod::TRANSPARENCY, Ratio{1, 1}, 58, 0, StaggerConfig{0, 8}, regs_ta_any }, // FIXME: review }; auto base_custom_regs = sensor.custom_regs; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.register_dpihw = setting.register_dpihw; sensor.register_dpiset = setting.register_dpiset; sensor.shading_resolution = setting.register_dpihw; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.method = setting.method; sensor.pixel_count_ratio = setting.pixel_count_ratio; sensor.output_pixel_offset = setting.output_pixel_offset; sensor.shading_pixel_offset = setting.shading_pixel_offset; sensor.stagger_y = setting.stagger_y; sensor.custom_regs = base_custom_regs; sensor.custom_regs.merge(setting.extra_custom_regs); s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_CANON_4400F; // gl843 sensor.full_resolution = 4800; sensor.register_dpihw = 4800; sensor.black_pixels = 50*8; // 31 at 600 dpi, 58 at 1200 dpi sensor.dummy_pixel = 20; sensor.fau_gain_white_ref = 160; sensor.gain_white_ref = 160; sensor.exposure = { 0x9c40, 0x9c40, 0x9c40 }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned optical_resolution; unsigned register_dpiset; int exposure_lperiod; bool use_host_side_calib; int output_pixel_offset; std::vector methods; StaggerConfig stagger_y; GenesysRegisterSettingSet extra_custom_regs; GenesysRegisterSettingSet extra_custom_fe_regs; }; CustomSensorSettings custom_settings[] = { { { 300 }, 1200, 1200, 11640, false, 197, { ScanMethod::FLATBED }, StaggerConfig{}, { { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, { 0x52, 0x0a }, { 0x53, 0x0d }, { 0x54, 0x00 }, { 0x55, 0x03 }, { 0x56, 0x06 }, { 0x57, 0x08 }, { 0x58, 0x5b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x01 }, { 0x73, 0x03 }, { 0x74, 0x00 }, { 0x75, 0xf8 }, { 0x76, 0x38 }, { 0x77, 0x00 }, { 0x78, 0xfc }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0xa4 }, { 0x9e, 0x2d }, }, {} }, { { 600 }, 1200, 2400, 11640, false, 392, { ScanMethod::FLATBED }, StaggerConfig{}, { { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, { 0x52, 0x0a }, { 0x53, 0x0d }, { 0x54, 0x00 }, { 0x55, 0x03 }, { 0x56, 0x06 }, { 0x57, 0x08 }, { 0x58, 0x5b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x01 }, { 0x73, 0x03 }, { 0x74, 0x00 }, { 0x75, 0xf8 }, { 0x76, 0x38 }, { 0x77, 0x00 }, { 0x78, 0xfc }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0xa4 }, { 0x9e, 0x2d }, }, {} }, { { 1200 }, 1200, 4800, 11640, false, 794, { ScanMethod::FLATBED }, StaggerConfig{}, { { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, { 0x52, 0x0a }, { 0x53, 0x0d }, { 0x54, 0x00 }, { 0x55, 0x03 }, { 0x56, 0x06 }, { 0x57, 0x08 }, { 0x58, 0x5b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x01 }, { 0x73, 0x03 }, { 0x74, 0x00 }, { 0x75, 0xf8 }, { 0x76, 0x38 }, { 0x77, 0x00 }, { 0x78, 0xfc }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0xa4 }, { 0x9e, 0x2d }, }, {} }, { { 1200 }, 1200, 4800, 33300, true, 5, { ScanMethod::TRANSPARENCY }, StaggerConfig{}, { { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, { 0x52, 0x0a }, { 0x53, 0x0d }, { 0x54, 0x00 }, { 0x55, 0x03 }, { 0x56, 0x06 }, { 0x57, 0x08 }, { 0x58, 0x5b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x00 }, { 0x73, 0x02 }, { 0x74, 0x00 }, { 0x75, 0xf8 }, { 0x76, 0x38 }, { 0x77, 0x00 }, { 0x78, 0xfc }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0xa4 }, { 0x9e, 0x2d }, }, {} }, { { 2400 }, 2400, 4800, 33300, true, 10, { ScanMethod::TRANSPARENCY }, StaggerConfig{}, { { 0x16, 0x13 }, { 0x17, 0x15 }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x01 }, { 0x1d, 0x75 }, { 0x52, 0x0b }, { 0x53, 0x0d }, { 0x54, 0x00 }, { 0x55, 0x03 }, { 0x56, 0x06 }, { 0x57, 0x09 }, { 0x58, 0x53 }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x02 }, { 0x73, 0x04 }, { 0x74, 0x00 }, { 0x75, 0xff }, { 0x76, 0x00 }, { 0x77, 0x00 }, { 0x78, 0xff }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x54 }, { 0x7c, 0x92 }, { 0x9e, 0x2d }, }, { { 0x03, 0x1f }, } }, { { 4800 }, 4800, 4800, 33300, true, -2063, { ScanMethod::TRANSPARENCY }, StaggerConfig{0, 8}, { { 0x16, 0x13 }, { 0x17, 0x15 }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x61 }, { 0x1d, 0x75 }, { 0x52, 0x02 }, { 0x53, 0x05 }, { 0x54, 0x08 }, { 0x55, 0x0b }, { 0x56, 0x0d }, { 0x57, 0x0f }, { 0x58, 0x1b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x08 }, { 0x71, 0x0a }, { 0x72, 0x0a }, { 0x73, 0x0c }, { 0x74, 0x00 }, { 0x75, 0xff }, { 0x76, 0xff }, { 0x77, 0x00 }, { 0x78, 0xff }, { 0x79, 0xff }, { 0x7a, 0x00 }, { 0x7b, 0x54 }, { 0x7c, 0x92 }, { 0x9e, 0x2d }, }, {} } }; for (const CustomSensorSettings& setting : custom_settings) { for (auto method : setting.methods) { for (auto resolution : setting.resolutions.values()) { sensor.resolutions = { resolution }; sensor.optical_resolution = setting.optical_resolution; sensor.register_dpiset = setting.register_dpiset; sensor.shading_resolution = resolution; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.output_pixel_offset = setting.output_pixel_offset; sensor.use_host_side_calib = setting.use_host_side_calib; sensor.method = method; sensor.stagger_y = setting.stagger_y; sensor.custom_regs = setting.extra_custom_regs; sensor.custom_fe_regs = setting.extra_custom_fe_regs; s_sensors->push_back(sensor); } } } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_CANON_5600F; // gl847 sensor.full_resolution = 4800; sensor.register_dpihw = 4800; sensor.black_pixels = 50*8; sensor.dummy_pixel = 10; sensor.fau_gain_white_ref = 160; sensor.gain_white_ref = 160; sensor.exposure = { 0x9c40, 0x9c40, 0x9c40 }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; sensor.use_host_side_calib = true; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned optical_resolution; unsigned register_dpihw; unsigned register_dpiset; int exposure_lperiod; SensorExposure exposure; Ratio pixel_count_ratio; int output_pixel_offset; unsigned segment_size; std::vector segment_order; StaggerConfig stagger_x; StaggerConfig stagger_y; GenesysRegisterSettingSet custom_regs; }; CustomSensorSettings custom_settings[] = { { { 150 }, 2400, 600, 300, 4288, { 3983/2, 3983/2, 3983/2 }, Ratio{1, 8}, 10, 5418, std::vector{}, StaggerConfig{}, StaggerConfig{}, { { 0x16, 0x00 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x10 }, { 0x1c, 0x08 }, { 0x1d, 0x02 }, { 0x52, 0x0e }, { 0x53, 0x00 }, { 0x54, 0x02 }, { 0x55, 0x04 }, { 0x56, 0x06 }, { 0x57, 0x08 }, { 0x58, 0x52 }, { 0x59, 0x3a }, { 0x5a, 0x40 }, { 0x74, 0x00 }, { 0x75, 0x33 }, { 0x76, 0x33 }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x87, 0x00 }, } }, { { 300 }, 2400, 600, 600, 5472, { 4558/2, 4558/2, 4558/2 }, Ratio{1, 8}, 110, 5418, std::vector{}, StaggerConfig{}, StaggerConfig{}, { { 0x16, 0x00 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x10 }, { 0x1c, 0x08 }, { 0x1d, 0x02 }, { 0x52, 0x0e }, { 0x53, 0x00 }, { 0x54, 0x02 }, { 0x55, 0x04 }, { 0x56, 0x06 }, { 0x57, 0x08 }, { 0x58, 0x52 }, { 0x59, 0x3a }, { 0x5a, 0x40 }, { 0x74, 0x00 }, { 0x75, 0x33 }, { 0x76, 0x33 }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x87, 0x00 }, } }, { { 600 }, 2400, 600, 600, 10944, { 8701/2, 8701/2, 8701/2 }, Ratio{1, 4}, 155, 5418, std::vector{}, StaggerConfig{}, StaggerConfig{}, { { 0x16, 0x00 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x10 }, { 0x1c, 0x08 }, { 0x1d, 0x02 }, { 0x52, 0x02 }, { 0x53, 0x04 }, { 0x54, 0x06 }, { 0x55, 0x08 }, { 0x56, 0x0a }, { 0x57, 0x0c }, { 0x58, 0x72 }, { 0x59, 0x5a }, { 0x5a, 0x40 }, { 0x74, 0x00 }, { 0x75, 0x33 }, { 0x76, 0x33 }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x87, 0x00 }, } }, { { 1200 }, 2400, 1200, 1200, 29120, { 17120/2, 17120/2, 17120/2 }, Ratio{1, 2}, 295, 5418, { 1, 0 }, StaggerConfig{}, StaggerConfig{}, { { 0x16, 0x00 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x10 }, { 0x1c, 0x08 }, { 0x1d, 0x02 }, { 0x52, 0x02 }, { 0x53, 0x04 }, { 0x54, 0x06 }, { 0x55, 0x08 }, { 0x56, 0x0a }, { 0x57, 0x0c }, { 0x58, 0x72 }, { 0x59, 0x5a }, { 0x5a, 0x40 }, { 0x74, 0x00 }, { 0x75, 0x33 }, { 0x76, 0x33 }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x87, 0x00 }, } }, { { 2400 }, 2400, 2400, 2400, 43776, { 36725/2, 36725/2, 36725/2 }, Ratio{1, 1}, 600, 5418, { 0, 1, 2, 3 }, StaggerConfig{10, 15, 4, 9, 14, 19, 8, 13}, StaggerConfig{}, { { 0x16, 0x00 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x10 }, { 0x1c, 0x08 }, { 0x1d, 0x02 }, { 0x52, 0x02 }, { 0x53, 0x04 }, { 0x54, 0x06 }, { 0x55, 0x08 }, { 0x56, 0x0a }, { 0x57, 0x0c }, { 0x58, 0x72 }, { 0x59, 0x5a }, { 0x5a, 0x40 }, { 0x74, 0x00 }, { 0x75, 0x33 }, { 0x76, 0x33 }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x87, 0x00 }, } }, { { 4800 }, 4800, 4800, 4800, 43776, { 36725/2, 36725/2, 36725/2 }, Ratio{1, 1}, 1000, 10784, { 0, 1, 2, 3 }, StaggerConfig{5, 9, 6, 10, 3, 7, 16, 20, 13, 17, 14, 18, 11, 15, 24, 28}, StaggerConfig{6, 0}, { { 0x16, 0x00 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x10 }, { 0x1c, 0x08 }, { 0x1d, 0x02 }, { 0x52, 0x0a }, { 0x53, 0x0c }, { 0x54, 0x0e }, { 0x55, 0x00 }, { 0x56, 0x02 }, { 0x57, 0x04 }, { 0x58, 0x32 }, { 0x59, 0x1a }, { 0x5a, 0x40 }, { 0x74, 0x00 }, { 0x75, 0x33 }, { 0x76, 0x33 }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x87, 0x00 }, } } }; for (const auto& setting : custom_settings) { for (auto method : { ScanMethod::FLATBED, ScanMethod::TRANSPARENCY }) { sensor.method = method; sensor.resolutions = setting.resolutions; sensor.optical_resolution = setting.optical_resolution; sensor.register_dpihw = setting.register_dpihw; sensor.register_dpiset = setting.register_dpiset; sensor.shading_resolution = setting.resolutions.values().front(); sensor.exposure_lperiod = setting.exposure_lperiod; sensor.exposure = setting.exposure; sensor.pixel_count_ratio = setting.pixel_count_ratio; sensor.output_pixel_offset = setting.output_pixel_offset; sensor.segment_size = setting.segment_size; sensor.segment_order = setting.segment_order; sensor.stagger_x = setting.stagger_x; sensor.stagger_y = setting.stagger_y; sensor.custom_regs = setting.custom_regs; s_sensors->push_back(sensor); } } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_CANON_8400F; // gl843 sensor.full_resolution = 3200; sensor.register_dpihw = 4800; sensor.black_pixels = 50*8; // 31 at 600 dpi, 58 at 1200 dpi sensor.dummy_pixel = 20; sensor.fau_gain_white_ref = 160; sensor.gain_white_ref = 160; sensor.exposure = { 0x9c40, 0x9c40, 0x9c40 }; sensor.custom_regs = {}; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned register_dpiset; Ratio pixel_count_ratio; int exposure_lperiod; int output_pixel_offset; int shading_pixel_offset; std::vector methods; StaggerConfig stagger_y; GenesysRegisterSettingSet extra_custom_regs; GenesysRegisterSettingSet custom_fe_regs; }; CustomSensorSettings custom_settings[] = { { { 400 }, 2400, Ratio{1, 4}, 7200, 2, 0, { ScanMethod::FLATBED }, StaggerConfig{}, { { 0x16, 0x33 }, { 0x17, 0x0c }, { 0x18, 0x13 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x84 }, { 0x1e, 0xa0 }, { 0x52, 0x0d }, { 0x53, 0x10 }, { 0x54, 0x01 }, { 0x55, 0x04 }, { 0x56, 0x07 }, { 0x57, 0x0a }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x01 }, { 0x71, 0x02 }, { 0x72, 0x03 }, { 0x73, 0x04 }, { 0x74, 0x00 }, { 0x75, 0x0e }, { 0x76, 0x3f }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x01 }, { 0x7b, 0xb6 }, { 0x7c, 0xdb }, { 0x80, 0x2a }, }, {} }, { { 800 }, 4800, Ratio{1, 4}, 7200, 5, 13, { ScanMethod::FLATBED }, StaggerConfig{}, { { 0x16, 0x33 }, { 0x17, 0x0c }, { 0x18, 0x13 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x84 }, { 0x1e, 0xa0 }, { 0x52, 0x0d }, { 0x53, 0x10 }, { 0x54, 0x01 }, { 0x55, 0x04 }, { 0x56, 0x07 }, { 0x57, 0x0a }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x01 }, { 0x71, 0x02 }, { 0x72, 0x03 }, { 0x73, 0x04 }, { 0x74, 0x00 }, { 0x75, 0x0e }, { 0x76, 0x3f }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x01 }, { 0x7b, 0xb6 }, { 0x7c, 0xdb }, { 0x80, 0x20 }, }, {} }, { { 1600 }, 4800, Ratio{1, 2}, 14400, 10, 8, { ScanMethod::FLATBED }, StaggerConfig{}, { { 0x16, 0x33 }, { 0x17, 0x0c }, { 0x18, 0x11 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x84 }, { 0x1e, 0xa1 }, { 0x52, 0x0b }, { 0x53, 0x0e }, { 0x54, 0x11 }, { 0x55, 0x02 }, { 0x56, 0x05 }, { 0x57, 0x08 }, { 0x58, 0x63 }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x00 }, { 0x71, 0x01 }, { 0x72, 0x02 }, { 0x73, 0x03 }, { 0x74, 0x00 }, { 0x75, 0x01 }, { 0x76, 0xff }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x02 }, { 0x7b, 0x49 }, { 0x7c, 0x24 }, { 0x80, 0x28 }, }, { { 0x03, 0x1f }, } }, { { 3200 }, 4800, Ratio{1, 1}, 28800, 20, -2, { ScanMethod::FLATBED }, StaggerConfig{0, 6}, { { 0x16, 0x33 }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x84 }, { 0x1e, 0xa1 }, { 0x52, 0x02 }, { 0x53, 0x05 }, { 0x54, 0x08 }, { 0x55, 0x0b }, { 0x56, 0x0e }, { 0x57, 0x11 }, { 0x58, 0x1b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x09 }, { 0x71, 0x0a }, { 0x72, 0x0b }, { 0x73, 0x0c }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x00 }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x02 }, { 0x7b, 0x49 }, { 0x7c, 0x24 }, { 0x80, 0x2b }, }, { { 0x03, 0x1f }, }, }, { { 400 }, 2400, Ratio{1, 4}, 14400, 2, 0, { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED }, StaggerConfig{}, { { 0x16, 0x33 }, { 0x17, 0x0c }, { 0x18, 0x13 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x84 }, { 0x1e, 0xa0 }, { 0x52, 0x0d }, { 0x53, 0x10 }, { 0x54, 0x01 }, { 0x55, 0x04 }, { 0x56, 0x07 }, { 0x57, 0x0a }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x01 }, { 0x71, 0x02 }, { 0x72, 0x03 }, { 0x73, 0x04 }, { 0x74, 0x00 }, { 0x75, 0x0e }, { 0x76, 0x3f }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x01 }, { 0x7b, 0xb6 }, { 0x7c, 0xdb }, { 0x80, 0x20 }, }, {} }, { { 800 }, 4800, Ratio{1, 4}, 14400, 5, 13, { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED }, StaggerConfig{}, { { 0x16, 0x33 }, { 0x17, 0x0c }, { 0x18, 0x13 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x84 }, { 0x1e, 0xa0 }, { 0x52, 0x0d }, { 0x53, 0x10 }, { 0x54, 0x01 }, { 0x55, 0x04 }, { 0x56, 0x07 }, { 0x57, 0x0a }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x00 }, { 0x71, 0x01 }, { 0x72, 0x02 }, { 0x73, 0x03 }, { 0x74, 0x00 }, { 0x75, 0x0e }, { 0x76, 0x3f }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x01 }, { 0x7b, 0xb6 }, { 0x7c, 0xdb }, { 0x80, 0x20 }, }, {} }, { { 1600 }, 4800, Ratio{1, 2}, 28800, 10, 8, { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED }, StaggerConfig{}, { { 0x16, 0x33 }, { 0x17, 0x0c }, { 0x18, 0x11 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x84 }, { 0x1e, 0xa0 }, { 0x52, 0x0b }, { 0x53, 0x0e }, { 0x54, 0x11 }, { 0x55, 0x02 }, { 0x56, 0x05 }, { 0x57, 0x08 }, { 0x58, 0x63 }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x00 }, { 0x71, 0x01 }, { 0x72, 0x02 }, { 0x73, 0x03 }, { 0x74, 0x00 }, { 0x75, 0x01 }, { 0x76, 0xff }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x02 }, { 0x7b, 0x49 }, { 0x7c, 0x24 }, { 0x80, 0x29 }, }, { { 0x03, 0x1f }, }, }, { { 3200 }, 4800, Ratio{1, 1}, 28800, 20, 10, { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED }, StaggerConfig{0, 6}, { { 0x16, 0x33 }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x84 }, { 0x1e, 0xa0 }, { 0x52, 0x02 }, { 0x53, 0x05 }, { 0x54, 0x08 }, { 0x55, 0x0b }, { 0x56, 0x0e }, { 0x57, 0x11 }, { 0x58, 0x1b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x09 }, { 0x71, 0x0a }, { 0x72, 0x0b }, { 0x73, 0x0c }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x00 }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x02 }, { 0x7b, 0x49 }, { 0x7c, 0x24 }, { 0x80, 0x2b }, }, { { 0x03, 0x1f }, }, }, }; for (const CustomSensorSettings& setting : custom_settings) { for (auto method : setting.methods) {for (auto resolution : setting.resolutions.values()) { sensor.resolutions = { resolution }; sensor.shading_resolution = resolution; sensor.register_dpiset = setting.register_dpiset; sensor.pixel_count_ratio = setting.pixel_count_ratio; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.output_pixel_offset = setting.output_pixel_offset; sensor.shading_pixel_offset = setting.shading_pixel_offset; sensor.method = method; sensor.stagger_y = setting.stagger_y; sensor.custom_regs = setting.extra_custom_regs; sensor.custom_fe_regs = setting.custom_fe_regs; s_sensors->push_back(sensor); } } } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_CANON_8600F; // gl843 sensor.full_resolution = 4800; sensor.register_dpihw = 4800; sensor.black_pixels = 31; sensor.dummy_pixel = 20; sensor.fau_gain_white_ref = 160; sensor.gain_white_ref = 160; sensor.exposure = { 0x9c40, 0x9c40, 0x9c40 }; sensor.custom_regs = {}; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned optical_resolution; unsigned register_dpiset; int exposure_lperiod; int output_pixel_offset; std::vector methods; StaggerConfig stagger_y; GenesysRegisterSettingSet extra_custom_regs; GenesysRegisterSettingSet custom_fe_regs; }; CustomSensorSettings custom_settings[] = { { { 300 }, 1200, 1200, 24000, 1, { ScanMethod::FLATBED }, StaggerConfig{}, { { 0x0c, 0x00 }, { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, { 0x52, 0x0c }, { 0x53, 0x0f }, { 0x54, 0x00 }, { 0x55, 0x03 }, { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x02 }, { 0x73, 0x04 }, { 0x56, 0x06 }, { 0x57, 0x09 }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x74, 0x03 }, { 0x75, 0xf0 }, { 0x76, 0xf0 }, { 0x77, 0x03 }, { 0x78, 0xfe }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0x49 }, { 0x9e, 0x2d }, { 0xaa, 0x00 }, }, {}, }, { { 600 }, 1200, 2400, 24000, 2, { ScanMethod::FLATBED }, StaggerConfig{}, { { 0x0c, 0x00 }, { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, { 0x52, 0x0c }, { 0x53, 0x0f }, { 0x54, 0x00 }, { 0x55, 0x03 }, { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x02 }, { 0x73, 0x04 }, { 0x56, 0x06 }, { 0x57, 0x09 }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x74, 0x03 }, { 0x75, 0xf0 }, { 0x76, 0xf0 }, { 0x77, 0x03 }, { 0x78, 0xfe }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0x49 }, { 0x9e, 0x2d }, { 0xaa, 0x00 }, }, {}, }, { { 1200 }, 1200, 4800, 24000, 5, { ScanMethod::FLATBED }, StaggerConfig{}, { { 0x0c, 0x00 }, { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, { 0x52, 0x0c }, { 0x53, 0x0f }, { 0x54, 0x00 }, { 0x55, 0x03 }, { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x02 }, { 0x73, 0x04 }, { 0x56, 0x06 }, { 0x57, 0x09 }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x74, 0x03 }, { 0x75, 0xf0 }, { 0x76, 0xf0 }, { 0x77, 0x03 }, { 0x78, 0xfe }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0x49 }, { 0x9e, 0x2d }, { 0xaa, 0x00 }, }, {}, }, { { 300 }, 1200, 1200, 45000, 6, { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED }, StaggerConfig{}, { { 0x0c, 0x00 }, { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, { 0x52, 0x0c }, { 0x53, 0x0f }, { 0x54, 0x00 }, { 0x55, 0x03 }, { 0x56, 0x06 }, { 0x57, 0x09 }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x02 }, { 0x73, 0x04 }, { 0x74, 0x03 }, { 0x75, 0xf0 }, { 0x76, 0xf0 }, { 0x77, 0x03 }, { 0x78, 0xfe }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0x49 }, { 0x9e, 0x2d }, { 0xaa, 0x00 }, }, {}, }, { { 600 }, 1200, 2400, 45000, 11, { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED }, StaggerConfig{}, { { 0x0c, 0x00 }, { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, { 0x52, 0x0c }, { 0x53, 0x0f }, { 0x54, 0x00 }, { 0x55, 0x03 }, { 0x56, 0x06 }, { 0x57, 0x09 }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x02 }, { 0x73, 0x04 }, { 0x74, 0x03 }, { 0x75, 0xf0 }, { 0x76, 0xf0 }, { 0x77, 0x03 }, { 0x78, 0xfe }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0x49 }, { 0x9e, 0x2d }, { 0xaa, 0x00 }, }, {}, }, { { 1200 }, 1200, 4800, 45000, 23, { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED }, StaggerConfig{}, { { 0x0c, 0x00 }, { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, { 0x52, 0x0c }, { 0x53, 0x0f }, { 0x54, 0x00 }, { 0x55, 0x03 }, { 0x56, 0x06 }, { 0x57, 0x09 }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x02 }, { 0x73, 0x04 }, { 0x74, 0x03 }, { 0x75, 0xf0 }, { 0x76, 0xf0 }, { 0x77, 0x03 }, { 0x78, 0xfe }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0x49 }, { 0x9e, 0x2d }, { 0xaa, 0x00 }, }, {}, }, { { 2400 }, 2400, 4800, 45000, 10, { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED }, StaggerConfig{}, { { 0x0c, 0x00 }, { 0x16, 0x13 }, { 0x17, 0x15 }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x01 }, { 0x1d, 0x75 }, { 0x52, 0x0c }, { 0x53, 0x0f }, { 0x54, 0x00 }, { 0x55, 0x03 }, { 0x56, 0x06 }, { 0x57, 0x09 }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x02 }, { 0x73, 0x04 }, { 0x74, 0x03 }, { 0x75, 0xfe }, { 0x76, 0x00 }, { 0x77, 0x03 }, { 0x78, 0xfe }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0x49 }, { 0x9e, 0x2d }, { 0xaa, 0x00 }, }, {}, }, { { 4800 }, 4800, 4800, 45000, -1982, { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED }, StaggerConfig{8, 0}, { { 0x0c, 0x00 }, { 0x16, 0x13 }, { 0x17, 0x15 }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x61 }, { 0x1d, 0x75 }, { 0x52, 0x03 }, { 0x53, 0x06 }, { 0x54, 0x09 }, { 0x55, 0x0c }, { 0x56, 0x0f }, { 0x57, 0x00 }, { 0x58, 0x23 }, { 0x59, 0x00 }, { 0x5a, 0x40 }, { 0x70, 0x0a }, { 0x71, 0x0c }, { 0x72, 0x0c }, { 0x73, 0x0e }, { 0x74, 0x03 }, { 0x75, 0xff }, { 0x76, 0xff }, { 0x77, 0x03 }, { 0x78, 0xff }, { 0x79, 0xff }, { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0x49 }, { 0x9e, 0x2d }, { 0xaa, 0x00 }, }, { { 0x03, 0x1f }, }, }, }; for (const CustomSensorSettings& setting : custom_settings) { for (auto method : setting.methods) { for (auto resolution : setting.resolutions.values()) { sensor.resolutions = { resolution }; sensor.optical_resolution = setting.optical_resolution; sensor.register_dpiset = setting.register_dpiset; sensor.shading_resolution = resolution; sensor.output_pixel_offset = setting.output_pixel_offset; sensor.method = method; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.stagger_y = setting.stagger_y; sensor.custom_regs = setting.extra_custom_regs; sensor.custom_fe_regs = setting.custom_fe_regs; s_sensors->push_back(sensor); } } } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_HP_N6310; // gl847 sensor.full_resolution = 2400; sensor.black_pixels = 96; sensor.dummy_pixel = 26; sensor.pixel_count_ratio = Ratio{1, 4}; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 230; sensor.exposure = { 0x0000, 0x0000, 0x0000 }; sensor.custom_regs = { { 0x16, 0x33 }, { 0x17, 0x0c }, { 0x18, 0x02 }, { 0x19, 0x2a }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x08 }, { 0x52, 0x0b }, { 0x53, 0x0e }, { 0x54, 0x11 }, { 0x55, 0x02 }, { 0x56, 0x05 }, { 0x57, 0x08 }, { 0x58, 0x63 }, { 0x59, 0x00 }, { 0x5a, 0x40 }, }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned register_dpihw; unsigned shading_factor; int output_pixel_offset; }; CustomSensorSettings custom_settings[] = { { { 75 }, 600, 8, 4 }, { { 100 }, 600, 6, 5 }, { { 150 }, 600, 4, 8 }, { { 200 }, 600, 3, 10 }, { { 300 }, 600, 2, 16 }, { { 600 }, 600, 1, 32 }, { { 1200 }, 1200, 1, 64 }, { { 2400 }, 2400, 1, 128 }, }; auto base_custom_regs = sensor.custom_regs; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.register_dpiset = setting.resolutions.values()[0]; sensor.register_dpihw = setting.register_dpihw; sensor.shading_resolution = setting.register_dpihw; sensor.shading_factor = setting.shading_factor; sensor.output_pixel_offset = setting.output_pixel_offset; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CIS_CANON_LIDE_110; // gl124 sensor.full_resolution = 2400; sensor.black_pixels = 87; sensor.dummy_pixel = 16; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 200; sensor.exposure = { 0x0000, 0x0000, 0x0000 }; sensor.gamma = { 2.2f, 2.2f, 2.2f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned optical_resolution; unsigned register_dpihw; unsigned register_dpiset; unsigned shading_resolution; int exposure_lperiod; SensorExposure exposure; Ratio pixel_count_ratio; unsigned shading_factor; std::vector segment_order; GenesysRegisterSettingSet custom_regs; }; CustomSensorSettings custom_settings[] = { { { 75 }, 1200, 600, 150, 300, 4608, { 462, 609, 453 }, Ratio{1, 4}, 4, std::vector{}, { { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c }, { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, { 0x61, 0x20 }, { 0x70, 0x06 }, { 0x71, 0x08 }, { 0x72, 0x08 }, { 0x73, 0x0a }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x00 }, { 0x89, 0x65 }, { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 }, { 0x96, 0x00 }, { 0x97, 0x9a }, { 0x98, 0x21 }, } }, { { 100 }, 1200, 600, 200, 300, 4608, { 462, 609, 453 }, Ratio{1, 4}, 3, std::vector{}, { { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c }, { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, { 0x61, 0x20 }, { 0x70, 0x06 }, { 0x71, 0x08 }, { 0x72, 0x08 }, { 0x73, 0x0a }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x00 }, { 0x89, 0x65 }, { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 }, { 0x96, 0x00 }, { 0x97, 0x9a }, { 0x98, 0x21 }, } }, { { 150 }, 1200, 600, 300, 300, 4608, { 462, 609, 453 }, Ratio{1, 4}, 2, std::vector{}, { { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c }, { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, { 0x61, 0x20 }, { 0x70, 0x06 }, { 0x71, 0x08 }, { 0x72, 0x08 }, { 0x73, 0x0a }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x00 }, { 0x89, 0x65 }, { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 }, { 0x96, 0x00 }, { 0x97, 0x9a }, { 0x98, 0x21 }, } }, { { 300 }, 1200, 600, 600, 300, 4608, { 462, 609, 453 }, Ratio{1, 4}, 1, std::vector{}, { { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x0c }, { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, { 0x61, 0x20 }, { 0x70, 0x06 }, { 0x71, 0x08 }, { 0x72, 0x08 }, { 0x73, 0x0a }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x00 }, { 0x89, 0x65 }, { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 }, { 0x96, 0x00 }, { 0x97, 0x9a }, { 0x98, 0x21 }, } }, { { 600 }, 2400, 600, 600, 600, 5360, { 823, 1117, 805 }, Ratio{1, 4}, 1, std::vector{}, { { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x0a }, { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, { 0x61, 0x20 }, { 0x70, 0x06 }, { 0x71, 0x08 }, { 0x72, 0x08 }, { 0x73, 0x0a }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x00 }, { 0x89, 0x65 }, { 0x93, 0x00 }, { 0x94, 0x14 }, { 0x95, 0x30 }, { 0x96, 0x00 }, { 0x97, 0xa3 }, { 0x98, 0x21 }, }, }, { { 1200 }, 2400, 1200, 1200, 1200, 10528, { 6071, 6670, 6042 }, Ratio{1, 4}, 1, { 0, 1 }, { { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 },{ 0x20, 0x08 }, { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, { 0x61, 0x20 }, { 0x70, 0x06 }, { 0x71, 0x08 }, { 0x72, 0x08 }, { 0x73, 0x0a }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x12 }, { 0x89, 0x47 }, { 0x93, 0x00 }, { 0x94, 0x14 }, { 0x95, 0x30 }, { 0x96, 0x00 }, { 0x97, 0xa3 }, { 0x98, 0x22 }, } }, { { 2400 }, 2400, 2400, 2400, 2400, 20864, { 7451, 8661, 7405 }, Ratio{1, 4}, 1, { 0, 2, 1, 3 }, { { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x06 }, { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, { 0x61, 0x20 }, { 0x70, 0x06 }, { 0x71, 0x08 }, { 0x72, 0x08 }, { 0x73, 0x0a }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x12 }, { 0x89, 0x47 }, { 0x93, 0x00 }, { 0x94, 0x14 }, { 0x95, 0x30 }, { 0x96, 0x00 }, { 0x97, 0xa3 }, { 0x98, 0x24 }, } } }; for (const auto& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.optical_resolution = setting.optical_resolution; sensor.register_dpihw = setting.register_dpihw; sensor.register_dpiset = setting.register_dpiset; sensor.shading_resolution = setting.shading_resolution; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.exposure = setting.exposure; sensor.pixel_count_ratio = setting.pixel_count_ratio; sensor.shading_factor = setting.shading_factor; sensor.segment_order = setting.segment_order; sensor.custom_regs = setting.custom_regs; sensor.channels = { 1 }; sensor.custom_regs.set_value(0x0c, 0x10); s_sensors->push_back(sensor); sensor.channels = { 3 }; sensor.custom_regs.set_value(0x0c, 0x12); s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CIS_CANON_LIDE_120; // gl124 sensor.full_resolution = 2400; sensor.black_pixels = 87; sensor.dummy_pixel = 16; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 200; sensor.exposure = { 0x0000, 0x0000, 0x0000 }; sensor.gamma = { 2.2f, 2.2f, 2.2f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned optical_resolution; unsigned register_dpihw; unsigned register_dpiset; unsigned shading_resolution; int exposure_lperiod; SensorExposure exposure; Ratio pixel_count_ratio; unsigned shading_factor; std::vector segment_order; GenesysRegisterSettingSet custom_regs; }; CustomSensorSettings custom_settings[] = { { { 75 }, 1200, 600, 150, 300, 4608, { 1244, 1294, 1144 }, Ratio{1, 4}, 4, std::vector{}, { { 0x16, 0x15 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x02 }, { 0x52, 0x04 }, { 0x53, 0x06 }, { 0x54, 0x00 }, { 0x55, 0x02 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x3a }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x61, 0x20 }, { 0x70, 0x00 }, { 0x71, 0x1f }, { 0x72, 0x08 }, { 0x73, 0x0a }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x00 }, { 0x89, 0x5e }, { 0x93, 0x00 }, { 0x94, 0x09 }, { 0x95, 0xf8 }, { 0x96, 0x00 }, { 0x97, 0x70 }, { 0x98, 0x21 }, }, }, { { 100 }, 1200, 600, 200, 300, 4608, { 1244, 1294, 1144 }, Ratio{1, 4}, 3, std::vector{}, { { 0x16, 0x15 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x02 }, { 0x52, 0x04 }, { 0x53, 0x06 }, { 0x54, 0x00 }, { 0x55, 0x02 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x3a }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x61, 0x20 }, { 0x70, 0x00 }, { 0x71, 0x1f }, { 0x72, 0x08 }, { 0x73, 0x0a }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x00 }, { 0x89, 0x5e }, { 0x93, 0x00 }, { 0x94, 0x09 }, { 0x95, 0xf8 }, { 0x96, 0x00 }, { 0x97, 0x70 }, { 0x98, 0x21 }, }, }, { { 150 }, 1200, 600, 300, 300, 4608, { 1244, 1294, 1144 }, Ratio{1, 4}, 2, std::vector{}, { { 0x16, 0x15 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x02 }, { 0x52, 0x04 }, { 0x53, 0x06 }, { 0x54, 0x00 }, { 0x55, 0x02 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x3a }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x61, 0x20 }, { 0x70, 0x00 }, { 0x71, 0x1f }, { 0x72, 0x08 }, { 0x73, 0x0a }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x00 }, { 0x89, 0x5e }, { 0x93, 0x00 }, { 0x94, 0x09 }, { 0x95, 0xf8 }, { 0x96, 0x00 }, { 0x97, 0x70 }, { 0x98, 0x21 }, }, }, { { 300 }, 1200, 600, 600, 300, 4608, { 1244, 1294, 1144 }, Ratio{1, 4}, 1, std::vector{}, { { 0x16, 0x15 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x02 }, { 0x52, 0x04 }, { 0x53, 0x06 }, { 0x54, 0x00 }, { 0x55, 0x02 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x3a }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x61, 0x20 }, { 0x70, 0x00 }, { 0x71, 0x1f }, { 0x72, 0x08 }, { 0x73, 0x0a }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x00 }, { 0x89, 0x5e }, { 0x93, 0x00 }, { 0x94, 0x09 }, { 0x95, 0xf8 }, { 0x96, 0x00 }, { 0x97, 0x70 }, { 0x98, 0x21 }, }, }, { { 600 }, 2400, 600, 600, 600, 5360, { 2394, 2444, 2144 }, Ratio{1, 4}, 1, std::vector{}, { { 0x16, 0x11 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x02 }, { 0x52, 0x04 }, { 0x53, 0x06 }, { 0x54, 0x00 }, { 0x55, 0x02 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x3a }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x61, 0x20 }, { 0x70, 0x1f }, { 0x71, 0x1f }, { 0x72, 0x08 }, { 0x73, 0x0a }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x00 }, { 0x89, 0x5e }, { 0x93, 0x00 }, { 0x94, 0x13 }, { 0x95, 0xf0 }, { 0x96, 0x00 }, { 0x97, 0x8b }, { 0x98, 0x21 }, }, }, { { 1200 }, 2400, 1200, 1200, 1200, 10528, { 4694, 4644, 4094 }, Ratio{1, 2}, 1, std::vector{}, { { 0x16, 0x15 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x02 }, { 0x52, 0x04 }, { 0x53, 0x06 }, { 0x54, 0x00 }, { 0x55, 0x02 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x3a }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x61, 0x20 }, { 0x70, 0x1f }, { 0x71, 0x1f }, { 0x72, 0x08 }, { 0x73, 0x0a }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x00 }, { 0x89, 0x5e }, { 0x93, 0x00 }, { 0x94, 0x27 }, { 0x95, 0xe0 }, { 0x96, 0x00 }, { 0x97, 0xc0 }, { 0x98, 0x21 }, }, }, { { 2400 }, 2400, 2400, 2400, 2400, 20864, { 8944, 8144, 7994 }, Ratio{1, 1}, 1, std::vector{}, { { 0x16, 0x11 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x02 }, { 0x52, 0x04 }, { 0x53, 0x06 }, { 0x54, 0x00 }, { 0x55, 0x02 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x3a }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x61, 0x20 }, { 0x70, 0x00 }, { 0x71, 0x1f }, { 0x72, 0x08 }, { 0x73, 0x0a }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x00 }, { 0x89, 0x5e }, { 0x93, 0x00 }, { 0x94, 0x4f }, { 0x95, 0xc0 }, { 0x96, 0x01 }, { 0x97, 0x2a }, { 0x98, 0x21 }, } }, }; for (const auto& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.optical_resolution = setting.optical_resolution; sensor.register_dpihw = setting.register_dpihw; sensor.register_dpiset = setting.register_dpiset; sensor.shading_resolution = setting.shading_resolution; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.exposure = setting.exposure; sensor.pixel_count_ratio = setting.pixel_count_ratio; sensor.shading_factor = setting.shading_factor; sensor.segment_order = setting.segment_order; sensor.custom_regs = setting.custom_regs; sensor.channels = { 1 }; sensor.custom_regs.set_value(0x0c, 0x10); s_sensors->push_back(sensor); sensor.channels = { 3 }; sensor.custom_regs.set_value(0x0c, 0x12); s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CIS_CANON_LIDE_210; // gl124 sensor.full_resolution = 4800; sensor.black_pixels = 87; sensor.dummy_pixel = 16; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 200; sensor.exposure = { 0x0000, 0x0000, 0x0000 }; sensor.gamma = { 2.2f, 2.2f, 2.2f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned optical_resolution; unsigned register_dpihw; unsigned register_dpiset; unsigned shading_resolution; int exposure_lperiod; SensorExposure exposure; Ratio pixel_count_ratio; unsigned shading_factor; std::vector segment_order; GenesysRegisterSettingSet custom_regs; }; CustomSensorSettings custom_settings[] = { { { 75 }, 2400, 600, 150, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 4, std::vector{}, { // { 0x16, 0x00 }, // FIXME: check if default value is different { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c }, { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, { 0x61, 0x20 }, // { 0x70, 0x00 }, // FIXME: check if default value is different { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x00 }, { 0x89, 0x65 }, { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 }, { 0x96, 0x00 }, { 0x97, 0x9a }, { 0x98, 0x21 }, } }, { { 100 }, 2400, 600, 200, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 3, std::vector{}, { // { 0x16, 0x00 }, // FIXME: check if default value is different { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c }, { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, { 0x61, 0x20 }, // { 0x70, 0x00 }, // FIXME: check if default value is different { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x00 }, { 0x89, 0x65 }, { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 }, { 0x96, 0x00 }, { 0x97, 0x9a }, { 0x98, 0x21 }, } }, { { 150 }, 2400, 600, 300, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 2, std::vector{}, { // { 0x16, 0x00 }, // FIXME: check if default value is different { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c }, { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, { 0x61, 0x20 }, // { 0x70, 0x00 }, // FIXME: check if default value is different { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x00 }, { 0x89, 0x65 }, { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 }, { 0x96, 0x00 }, { 0x97, 0x9a }, { 0x98, 0x21 }, } }, { { 300 }, 2400, 600, 600, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 1, std::vector{}, { // { 0x16, 0x00 }, // FIXME: check if default value is different { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c }, { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, { 0x61, 0x20 }, // { 0x70, 0x00 }, // FIXME: check if default value is different { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x00 }, { 0x89, 0x65 }, { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 }, { 0x96, 0x00 }, { 0x97, 0x9a }, { 0x98, 0x21 }, } }, { { 600 }, 4800, 600, 600, 600, 5360, { 388, 574, 393 }, Ratio{1, 8}, 1, std::vector{}, { // { 0x16, 0x00 }, // FIXME: check if default value is different { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0a }, { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, { 0x61, 0x20 }, // { 0x70, 0x00 }, // FIXME: check if default value is different { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x00 }, { 0x89, 0x65 }, { 0x93, 0x00 }, { 0x94, 0x14 }, { 0x95, 0x30 }, { 0x96, 0x00 }, { 0x97, 0xa3 }, { 0x98, 0x21 }, } }, { { 1200 }, 4800, 1200, 1200, 1200, 10528, { 388, 574, 393 }, Ratio{1, 8}, 1, {0, 1}, { // { 0x16, 0x00 }, // FIXME: check if default value is different { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x08 }, { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, { 0x61, 0x20 }, // { 0x70, 0x00 }, // FIXME: check if default value is different { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x00 }, { 0x89, 0x65 }, { 0x93, 0x00 }, { 0x94, 0x14 }, { 0x95, 0x30 }, { 0x96, 0x00 }, { 0x97, 0xa3 }, { 0x98, 0x22 }, }, }, { { 2400 }, 4800, 2400, 2400, 2400, 20864, { 6839, 8401, 6859 }, Ratio{1, 8}, 1, {0, 2, 1, 3}, { // { 0x16, 0x00 }, // FIXME: check if default value is different { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x06 }, { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, { 0x61, 0x20 }, // { 0x70, 0x00 }, // FIXME: check if default value is different { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x12 }, { 0x89, 0x47 }, { 0x93, 0x00 }, { 0x94, 0x14 }, { 0x95, 0x30 }, { 0x96, 0x00 }, { 0x97, 0xa3 }, { 0x98, 0x24 }, }, }, { { 4800 }, 4800, 4800, 4800, 4800, 41536, { 9735, 14661, 11345 }, Ratio{1, 8}, 1, { 0, 2, 4, 6, 1, 3, 5, 7 }, { // { 0x16, 0x00 }, // FIXME: check if default value is different { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x04 }, { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, { 0x61, 0x20 }, // { 0x70, 0x00 }, // FIXME: check if default value is different { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x12 }, { 0x89, 0x47 }, { 0x93, 0x00 }, { 0x94, 0x14 }, { 0x95, 0x30 }, { 0x96, 0x00 }, { 0x97, 0xa5 }, { 0x98, 0x28 }, }, } }; for (const auto& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.optical_resolution = setting.optical_resolution; sensor.register_dpihw = setting.register_dpihw; sensor.register_dpiset = setting.register_dpiset; sensor.shading_resolution = setting.shading_resolution; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.exposure = setting.exposure; sensor.pixel_count_ratio = setting.pixel_count_ratio; sensor.shading_factor = setting.shading_factor; sensor.segment_order = setting.segment_order; sensor.custom_regs = setting.custom_regs; sensor.channels = { 1 }; sensor.custom_regs.set_value(0x0c, 0x10); s_sensors->push_back(sensor); sensor.channels = { 3 }; sensor.custom_regs.set_value(0x0c, 0x12); s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CIS_CANON_LIDE_220; // gl124 sensor.full_resolution = 4800; sensor.black_pixels = 87; sensor.dummy_pixel = 16; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 200; sensor.exposure = { 0x0000, 0x0000, 0x0000 }; sensor.gamma = { 2.2f, 2.2f, 2.2f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned optical_resolution; unsigned register_dpihw; unsigned register_dpiset; unsigned shading_resolution; int exposure_lperiod; SensorExposure exposure; Ratio pixel_count_ratio; unsigned shading_factor; std::vector segment_order; GenesysRegisterSettingSet custom_regs; }; CustomSensorSettings custom_settings[] = { { { 75 }, 2400, 600, 150, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 4, std::vector{}, { // { 0x16, 0x00 }, // FIXME: check if default value is different { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c }, { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, { 0x61, 0x20 }, // { 0x70, 0x00 }, // FIXME: check if default value is different { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x00 }, { 0x89, 0x65 }, { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 }, { 0x96, 0x00 }, { 0x97, 0x9a }, { 0x98, 0x21 }, } }, { { 100 }, 2400, 600, 200, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 3, std::vector{}, { // { 0x16, 0x00 }, // FIXME: check if default value is different { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c }, { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, { 0x61, 0x20 }, // { 0x70, 0x00 }, // FIXME: check if default value is different { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x00 }, { 0x89, 0x65 }, { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 }, { 0x96, 0x00 }, { 0x97, 0x9a }, { 0x98, 0x21 }, } }, { { 150 }, 2400, 600, 300, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 2, std::vector{}, { // { 0x16, 0x00 }, // FIXME: check if default value is different { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c }, { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, { 0x61, 0x20 }, // { 0x70, 0x00 }, // FIXME: check if default value is different { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x00 }, { 0x89, 0x65 }, { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 }, { 0x96, 0x00 }, { 0x97, 0x9a }, { 0x98, 0x21 }, } }, { { 300 }, 2400, 600, 600, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 1, std::vector{}, { // { 0x16, 0x00 }, // FIXME: check if default value is different { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c }, { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, { 0x61, 0x20 }, // { 0x70, 0x00 }, // FIXME: check if default value is different { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x00 }, { 0x89, 0x65 }, { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 }, { 0x96, 0x00 }, { 0x97, 0x9a }, { 0x98, 0x21 }, } }, { { 600 }, 4800, 600, 600, 600, 5360, { 388, 574, 393 }, Ratio{1, 8}, 1, std::vector{}, { // { 0x16, 0x00 }, // FIXME: check if default value is different { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0a }, { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, { 0x61, 0x20 }, // { 0x70, 0x00 }, // FIXME: check if default value is different { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x00 }, { 0x89, 0x65 }, { 0x93, 0x00 }, { 0x94, 0x14 }, { 0x95, 0x30 }, { 0x96, 0x00 }, { 0x97, 0xa3 }, { 0x98, 0x21 }, } }, { { 1200 }, 4800, 1200, 1200, 1200, 10528, { 388, 574, 393 }, Ratio{1, 8}, 1, {0, 1}, { // { 0x16, 0x00 }, // FIXME: check if default value is different { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x08 }, { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, { 0x61, 0x20 }, // { 0x70, 0x00 }, // FIXME: check if default value is different { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x00 }, { 0x89, 0x65 }, { 0x93, 0x00 }, { 0x94, 0x14 }, { 0x95, 0x30 }, { 0x96, 0x00 }, { 0x97, 0xa3 }, { 0x98, 0x22 }, } }, { { 2400 }, 4800, 2400, 2400, 2400, 20864, { 6839, 8401, 6859 }, Ratio{1, 8}, 1, {0, 2, 1, 3}, { // { 0x16, 0x00 }, // FIXME: check if default value is different { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x06 }, { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, { 0x61, 0x20 }, // { 0x70, 0x00 }, // FIXME: check if default value is different { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x12 }, { 0x89, 0x47 }, { 0x93, 0x00 }, { 0x94, 0x14 }, { 0x95, 0x30 }, { 0x96, 0x00 }, { 0x97, 0xa3 }, { 0x98, 0x24 }, }, }, { { 4800 }, 4800, 4800, 4800, 4800, 41536, { 9735, 14661, 11345 }, Ratio{1, 8}, 1, { 0, 2, 4, 6, 1, 3, 5, 7 }, { // { 0x16, 0x00 }, // FIXME: check if default value is different { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x04 }, { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, { 0x61, 0x20 }, // { 0x70, 0x00 }, // FIXME: check if default value is different { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x88, 0x12 }, { 0x89, 0x47 }, { 0x93, 0x00 }, { 0x94, 0x14 }, { 0x95, 0x30 }, { 0x96, 0x00 }, { 0x97, 0xa5 }, { 0x98, 0x28 }, }, } }; for (const auto& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.optical_resolution = setting.optical_resolution; sensor.register_dpihw = setting.register_dpihw; sensor.register_dpiset = setting.register_dpiset; sensor.shading_resolution = setting.shading_resolution; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.exposure = setting.exposure; sensor.pixel_count_ratio = setting.pixel_count_ratio; sensor.shading_factor = setting.shading_factor; sensor.segment_order = setting.segment_order; sensor.custom_regs = setting.custom_regs; sensor.channels = { 1 }; sensor.custom_regs.set_value(0x0c, 0x10); s_sensors->push_back(sensor); sensor.channels = { 3 }; sensor.custom_regs.set_value(0x0c, 0x12); s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICPRO_3600; // gl841 sensor.full_resolution = 1200; sensor.black_pixels = 87; sensor.dummy_pixel = 87; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 230; sensor.exposure = { 0x0000, 0x0000, 0x0000 }; sensor.custom_regs = { { 0x16, 0x33 }, { 0x17, 0x0b }, { 0x18, 0x11 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0xc4 }, { 0x52, 0x07 }, { 0x53, 0x0a }, { 0x54, 0x0c }, { 0x55, 0x00 }, { 0x56, 0x02 }, { 0x57, 0x06 }, { 0x58, 0x22 }, { 0x59, 0x69 }, { 0x5a, 0x40 }, { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 0x00 }, }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned optical_resolution; unsigned register_dpihw; unsigned register_dpiset; int output_pixel_offset; }; CustomSensorSettings custom_settings[] = { { { 75 }, 600, 600, 150, 11 }, { { 100 }, 600, 600, 200, 14 }, { { 150 }, 600, 600, 300, 22 }, { { 200 }, 600, 600, 400, 29 }, { { 300 }, 600, 600, 600, 44 }, { { 600 }, 600, 600, 1200, 88 }, { { 1200 }, 1200, 1200, 1200, 88 }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.optical_resolution = setting.optical_resolution; sensor.register_dpihw = setting.register_dpihw; sensor.register_dpiset = setting.register_dpiset; sensor.shading_resolution = setting.register_dpihw; sensor.output_pixel_offset = setting.output_pixel_offset; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7200; // gl842 sensor.full_resolution = 7200; sensor.register_dpihw = 1200; sensor.black_pixels = 88; // TODO sensor.dummy_pixel = 19; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 230; sensor.exposure = { 0x2b00, 0x2b00, 0x2b00 }; sensor.exposure_lperiod = 0x694e; sensor.use_host_side_calib = true; sensor.custom_regs = { { 0x16, 0x3b }, { 0x17, 0x4b }, { 0x18, 0x10 }, { 0x19, 0x00 }, { 0x1a, 0x24 }, { 0x1b, 0x00 }, { 0x1c, 0x40 }, { 0x1d, 0x84 }, { 0x52, 0x09 }, { 0x53, 0x0c }, { 0x54, 0x0e }, { 0x55, 0x02 }, { 0x56, 0x04 }, { 0x57, 0x07 }, { 0x58, 0x22 }, { 0x59, 0x69 }, { 0x5a, 0xc0 }, { 0x70, 0x08 }, { 0x71, 0x09 }, { 0x72, 0x0b }, { 0x73, 0x0c }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x00 }, { 0x77, 0x00 }, { 0x78, 0x7f }, { 0x79, 0xff }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 }, { 0x7d, 0x00 }, { 0x7f, 0x01 } }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; ScanMethod method; Ratio pixel_count_ratio; int output_pixel_offset; unsigned register_dpiset; StaggerConfig stagger_y; }; CustomSensorSettings custom_settings[] = { { { 900 }, ScanMethod::TRANSPARENCY, Ratio{8, 8}, 2, 150, StaggerConfig{} }, { { 1800 }, ScanMethod::TRANSPARENCY, Ratio{4, 4}, 10, 300, StaggerConfig{} }, { { 3600 }, ScanMethod::TRANSPARENCY, Ratio{2, 2}, 10, 600, StaggerConfig{} }, { { 7200 }, ScanMethod::TRANSPARENCY, Ratio{1, 1}, 20, 1200, StaggerConfig{0, 4} }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.method = setting.method; sensor.shading_resolution = setting.resolutions.values().front(); sensor.pixel_count_ratio = setting.pixel_count_ratio; sensor.output_pixel_offset = setting.output_pixel_offset; sensor.register_dpiset = setting.register_dpiset; sensor.stagger_y = setting.stagger_y; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7200I; // gl843 sensor.full_resolution = 7200; sensor.register_dpihw = 1200; sensor.black_pixels = 88; // TODO sensor.dummy_pixel = 20; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 230; sensor.exposure = { 0x0000, 0x0000, 0x0000 }; sensor.use_host_side_calib = true; sensor.custom_regs = { { 0x08, 0x00 }, { 0x09, 0x00 }, { 0x0a, 0x00 }, { 0x16, 0x23 }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x21 }, { 0x1d, 0x84 }, { 0x52, 0x0a }, { 0x53, 0x0d }, { 0x54, 0x10 }, { 0x55, 0x01 }, { 0x56, 0x04 }, { 0x57, 0x07 }, { 0x58, 0x3a }, { 0x59, 0x81 }, { 0x5a, 0xc0 }, { 0x70, 0x0a }, { 0x71, 0x0b }, { 0x72, 0x0c }, { 0x73, 0x0d }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x00 }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 }, }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; ScanMethod method; unsigned shading_resolution; Ratio pixel_count_ratio; int output_pixel_offset; unsigned exposure_lperiod; unsigned register_dpiset; StaggerConfig stagger_y; GenesysRegisterSettingSet custom_fe_regs; }; CustomSensorSettings custom_settings[] = { { { 900 }, ScanMethod::TRANSPARENCY, 900, Ratio{8, 8}, 2, 0x2538, 150, StaggerConfig{}, {} }, { { 1800 }, ScanMethod::TRANSPARENCY, 1800, Ratio{4, 4}, 5, 0x2538, 300, StaggerConfig{}, {} }, { { 3600 }, ScanMethod::TRANSPARENCY, 3600, Ratio{2, 2}, 10, 0x2538, 600, StaggerConfig{}, {} }, { { 7200 }, ScanMethod::TRANSPARENCY, 7200, Ratio{1, 1}, 20, 0x19c8, 1200, StaggerConfig{4, 0}, { { 0x02, 0x1b }, { 0x03, 0x14 }, { 0x04, 0x20 }, } }, { { 900 }, ScanMethod::TRANSPARENCY_INFRARED, 900, Ratio{8, 8}, 2, 0x1f54, 150, StaggerConfig{}, {} }, { { 1800 }, ScanMethod::TRANSPARENCY_INFRARED, 1800, Ratio{4, 4}, 5, 0x1f54, 300, StaggerConfig{}, {} }, { { 3600 }, ScanMethod::TRANSPARENCY_INFRARED, 3600, Ratio{2, 2}, 10, 0x1f54, 600, StaggerConfig{}, {}}, { { 7200 }, ScanMethod::TRANSPARENCY_INFRARED, 7200, Ratio{1, 1}, 20, 0x1f54, 1200, StaggerConfig{4, 0}, {} }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.method = setting.method; sensor.shading_resolution = setting.shading_resolution; sensor.pixel_count_ratio = setting.pixel_count_ratio; sensor.output_pixel_offset = setting.output_pixel_offset; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.register_dpiset = setting.register_dpiset; sensor.stagger_y = setting.stagger_y; sensor.custom_fe_regs = setting.custom_fe_regs; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7300; // gl843 sensor.full_resolution = 7200; sensor.method = ScanMethod::TRANSPARENCY; sensor.register_dpihw = 1200; sensor.black_pixels = 88; // TODO sensor.dummy_pixel = 20; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 230; sensor.exposure = { 0x0000, 0x0000, 0x0000 }; sensor.exposure_lperiod = 0x2f44; sensor.use_host_side_calib = true; sensor.custom_regs = { { 0x08, 0x00 }, { 0x09, 0x00 }, { 0x0a, 0x00 }, { 0x16, 0x27 }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x84 }, { 0x52, 0x0a }, { 0x53, 0x0d }, { 0x54, 0x0f }, { 0x55, 0x01 }, { 0x56, 0x04 }, { 0x57, 0x07 }, { 0x58, 0x31 }, { 0x59, 0x79 }, { 0x5a, 0xc0 }, { 0x70, 0x0c }, { 0x71, 0x0d }, { 0x72, 0x0e }, { 0x73, 0x0f }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x00 }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 }, }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned shading_resolution; Ratio pixel_count_ratio; int output_pixel_offset; unsigned register_dpiset; StaggerConfig stagger_y; }; CustomSensorSettings custom_settings[] = { { { 900 }, 900, Ratio{8, 8}, 2, 150, StaggerConfig{} }, { { 1800 }, 1800, Ratio{4, 4}, 5, 300, StaggerConfig{} }, { { 3600 }, 3600, Ratio{2, 2}, 10, 600, StaggerConfig{} }, { { 7200 }, 7200, Ratio{1, 1}, 20, 1200, StaggerConfig{4, 0} }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.shading_resolution = setting.shading_resolution; sensor.pixel_count_ratio = setting.pixel_count_ratio; sensor.output_pixel_offset = setting.output_pixel_offset; sensor.register_dpiset = setting.register_dpiset; sensor.stagger_y = setting.stagger_y; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7400; // gl845 sensor.full_resolution = 7200; sensor.method = ScanMethod::TRANSPARENCY; sensor.register_dpihw = 1200; sensor.black_pixels = 88; // TODO sensor.dummy_pixel = 20; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 230; sensor.exposure = { 0x0000, 0x0000, 0x0000 }; sensor.exposure_lperiod = 14000; sensor.use_host_side_calib = true; sensor.custom_regs = { { 0x08, 0x00 }, { 0x09, 0x00 }, { 0x0a, 0x00 }, { 0x16, 0x27 }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x84 }, { 0x52, 0x09 }, { 0x53, 0x0d }, { 0x54, 0x0f }, { 0x55, 0x01 }, { 0x56, 0x04 }, { 0x57, 0x07 }, { 0x58, 0x31 }, { 0x59, 0x79 }, { 0x5a, 0xc0 }, { 0x70, 0x0a }, { 0x71, 0x0b }, { 0x72, 0x0c }, { 0x73, 0x0d }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x00 }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 }, { 0x7d, 0x00 }, { 0x87, 0x00 }, }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned register_dpiset; int output_pixel_offset; StaggerConfig stagger_y; }; CustomSensorSettings custom_settings[] = { { { 600 }, 100, 10, StaggerConfig{} }, { { 1200 }, 200, 20, StaggerConfig{} }, { { 2400 }, 400, 40, StaggerConfig{} }, { { 3600 }, 600, 60, StaggerConfig{} }, { { 7200 }, 1200, 120, StaggerConfig{4, 0} }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.shading_resolution = setting.resolutions.values()[0]; sensor.register_dpiset = setting.register_dpiset; sensor.output_pixel_offset = setting.output_pixel_offset; sensor.stagger_y = setting.stagger_y; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7500I; // gl843 sensor.full_resolution = 7200; sensor.register_dpihw = 1200; sensor.black_pixels = 88; // TODO sensor.dummy_pixel = 20; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 230; sensor.exposure = { 0x0000, 0x0000, 0x0000 }; sensor.use_host_side_calib = true; sensor.custom_regs = { { 0x08, 0x00 }, { 0x09, 0x00 }, { 0x0a, 0x00 }, { 0x16, 0x27 }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x84 }, { 0x52, 0x0a }, { 0x53, 0x0d }, { 0x54, 0x0f }, { 0x55, 0x01 }, { 0x56, 0x04 }, { 0x57, 0x07 }, { 0x58, 0x31 }, { 0x59, 0x79 }, { 0x5a, 0xc0 }, { 0x70, 0x0c }, { 0x71, 0x0d }, { 0x72, 0x0e }, { 0x73, 0x0f }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x00 }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 }, }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; ScanMethod method; unsigned shading_resolution; Ratio pixel_count_ratio; int output_pixel_offset; unsigned exposure_lperiod; unsigned register_dpiset; StaggerConfig stagger_y; }; CustomSensorSettings custom_settings[] = { { { 900 }, ScanMethod::TRANSPARENCY, 900, Ratio{8, 8}, 2, 0x2f44, 150, StaggerConfig{} }, { { 1800 }, ScanMethod::TRANSPARENCY, 1800, Ratio{4, 4}, 5, 0x2f44, 300, StaggerConfig{} }, { { 3600 }, ScanMethod::TRANSPARENCY, 3600, Ratio{2, 2}, 10, 0x2f44, 600, StaggerConfig{} }, { { 7200 }, ScanMethod::TRANSPARENCY, 7200, Ratio{1, 1}, 20, 0x2f44, 1200, StaggerConfig{4, 0} }, { { 900 }, ScanMethod::TRANSPARENCY_INFRARED, 900, Ratio{8, 8}, 2, 0x2af8, 150, StaggerConfig{} }, { { 1800 }, ScanMethod::TRANSPARENCY_INFRARED, 1800, Ratio{4, 4}, 5, 0x2af8, 300, StaggerConfig{} }, { { 3600 }, ScanMethod::TRANSPARENCY_INFRARED, 3600, Ratio{2, 2}, 10, 0x2af8, 600, StaggerConfig{} }, { { 7200 }, ScanMethod::TRANSPARENCY_INFRARED, 7200, Ratio{1, 1}, 20, 0x2af8, 1200, StaggerConfig{4, 0} }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.method = setting.method; sensor.shading_resolution = setting.shading_resolution; sensor.pixel_count_ratio = setting.pixel_count_ratio; sensor.output_pixel_offset = setting.output_pixel_offset; sensor.exposure_lperiod = setting.exposure_lperiod; sensor.register_dpiset = setting.register_dpiset; sensor.stagger_y = setting.stagger_y; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_8200I; // gl845 sensor.full_resolution = 7200; sensor.method = ScanMethod::TRANSPARENCY; sensor.register_dpihw = 1200; sensor.black_pixels = 88; // TODO sensor.dummy_pixel = 20; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 230; sensor.exposure = { 0x0000, 0x0000, 0x0000 }; sensor.exposure_lperiod = 14000; sensor.use_host_side_calib = true; sensor.custom_regs = { { 0x08, 0x00 }, { 0x09, 0x00 }, { 0x0a, 0x00 }, { 0x16, 0x27 }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x84 }, { 0x52, 0x09 }, { 0x53, 0x0d }, { 0x54, 0x0f }, { 0x55, 0x01 }, { 0x56, 0x04 }, { 0x57, 0x07 }, { 0x58, 0x31 }, { 0x59, 0x79 }, { 0x5a, 0xc0 }, { 0x70, 0x0a }, { 0x71, 0x0b }, { 0x72, 0x0c }, { 0x73, 0x0d }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x00 }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 }, { 0x7d, 0x00 }, { 0x87, 0x00 }, }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; ScanMethod method; unsigned register_dpiset; int output_pixel_offset; StaggerConfig stagger_y; }; CustomSensorSettings custom_settings[] = { { { 900 }, ScanMethod::TRANSPARENCY, 150, 15, StaggerConfig{} }, { { 1800 }, ScanMethod::TRANSPARENCY, 300, 30, StaggerConfig{} }, { { 3600 }, ScanMethod::TRANSPARENCY, 600, 60, StaggerConfig{} }, { { 7200 }, ScanMethod::TRANSPARENCY, 1200, 120, StaggerConfig{4, 0} }, { { 900 }, ScanMethod::TRANSPARENCY_INFRARED, 150, 15, StaggerConfig{} }, { { 1800 }, ScanMethod::TRANSPARENCY_INFRARED, 300, 30, StaggerConfig{} }, { { 3600 }, ScanMethod::TRANSPARENCY_INFRARED, 600, 60, StaggerConfig{} }, { { 7200 }, ScanMethod::TRANSPARENCY_INFRARED, 1200, 120, StaggerConfig{4, 0} }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.method = setting.method; sensor.shading_resolution = setting.resolutions.values()[0]; sensor.register_dpiset = setting.register_dpiset; sensor.output_pixel_offset = setting.output_pixel_offset; sensor.stagger_y = setting.stagger_y; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_IMG101; // gl846 sensor.resolutions = { 75, 100, 150, 300, 600, 1200 }; sensor.exposure_lperiod = 11000; sensor.segment_size = 5136; sensor.segment_order = {0, 1}; sensor.full_resolution = 1200; sensor.black_pixels = 31; sensor.dummy_pixel = 31; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 200; sensor.exposure = { 0x0000, 0x0000, 0x0000 }; sensor.custom_regs = { { 0x16, 0xbb }, { 0x17, 0x13 }, { 0x18, 0x10 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x06 }, { 0x52, 0x02 }, { 0x53, 0x04 }, { 0x54, 0x06 }, { 0x55, 0x08 }, { 0x56, 0x0a }, { 0x57, 0x00 }, { 0x58, 0x59 }, { 0x59, 0x31 }, { 0x5a, 0x40 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, }; sensor.gamma = { 1.7f, 1.7f, 1.7f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned register_dpihw; Ratio pixel_count_ratio; unsigned shading_factor; GenesysRegisterSettingSet extra_custom_regs; }; CustomSensorSettings custom_settings[] = { { { 75 }, 600, Ratio{1, 4}, 8, { { 0x7e, 0x00 } } }, { { 100 }, 600, Ratio{1, 4}, 6, { { 0x7e, 0x00 } } }, { { 150 }, 600, Ratio{1, 4}, 4, { { 0x7e, 0x00 } } }, { { 300 }, 600, Ratio{1, 4}, 2, { { 0x7e, 0x00 } } }, { { 600 }, 600, Ratio{1, 4}, 1, { { 0x7e, 0x01 } } }, { { 1200 }, 1200, Ratio{1, 2}, 1, { { 0x7e, 0x01 } } }, }; auto base_custom_regs = sensor.custom_regs; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.register_dpihw = setting.register_dpihw; sensor.register_dpiset = setting.resolutions.values()[0]; sensor.shading_resolution = setting.register_dpihw; sensor.pixel_count_ratio = setting.pixel_count_ratio; sensor.shading_factor = setting.shading_factor; sensor.custom_regs = base_custom_regs; sensor.custom_regs.merge(setting.extra_custom_regs); s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICBOOK_3800; // gl845 sensor.resolutions = { 75, 100, 150, 300, 600, 1200 }; sensor.exposure_lperiod = 11000; sensor.full_resolution = 1200; sensor.black_pixels = 31; sensor.dummy_pixel = 31; sensor.fau_gain_white_ref = 210; sensor.gain_white_ref = 200; sensor.exposure = { 0, 0, 0 }; sensor.custom_regs = { { 0x16, 0xbb }, { 0x17, 0x13 }, { 0x18, 0x10 }, { 0x19, 0xff }, { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x06 }, { 0x52, 0x02 }, { 0x53, 0x04 }, { 0x54, 0x06 }, { 0x55, 0x08 }, { 0x56, 0x0a }, { 0x57, 0x00 }, { 0x58, 0x59 }, { 0x59, 0x31 }, { 0x5a, 0x40 }, { 0x70, 0x01 }, { 0x71, 0x00 }, { 0x72, 0x02 }, { 0x73, 0x01 }, { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c }, { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x7d, 0x20 }, { 0x87, 0x02 }, }; sensor.gamma = { 1.7f, 1.7f, 1.7f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned register_dpihw; Ratio pixel_count_ratio; unsigned shading_factor; }; CustomSensorSettings custom_settings[] = { { { 75 }, 600, Ratio{1, 2}, 8 }, { { 100 }, 600, Ratio{1, 2}, 6 }, { { 150 }, 600, Ratio{1, 2}, 4 }, { { 300 }, 600, Ratio{1, 2}, 2 }, { { 600 }, 600, Ratio{1, 2}, 1 }, { { 1200 }, 1200, Ratio{1, 1}, 1 }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.register_dpihw = setting.register_dpihw; sensor.register_dpiset = setting.resolutions.values()[0]; sensor.shading_resolution = setting.register_dpihw; sensor.pixel_count_ratio = setting.pixel_count_ratio; sensor.shading_factor = setting.shading_factor; s_sensors->push_back(sensor); } } sensor = Genesys_Sensor(); sensor.sensor_id = SensorId::CIS_CANON_LIDE_80; // gl841 sensor.full_resolution = 1200; // real hardware limit is 2400 sensor.register_dpihw = 1200; sensor.black_pixels = 20; sensor.dummy_pixel = 6; sensor.fau_gain_white_ref = 150; sensor.gain_white_ref = 150; // maps to 0x70-0x73 for GL841 sensor.exposure = { 0x1000, 0x1000, 0x0500 }; sensor.custom_regs = { { 0x16, 0x00 }, { 0x17, 0x01 }, { 0x18, 0x00 }, { 0x19, 0x06 }, { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x04 }, { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x29 }, { 0x59, 0x69 }, { 0x5a, 0x55 }, { 0x70, 0x00 }, { 0x71, 0x05 }, { 0x72, 0x07 }, { 0x73, 0x09 }, }; sensor.gamma = { 1.0f, 1.0f, 1.0f }; { struct CustomSensorSettings { ValueFilterAny resolutions; unsigned optical_resolution; unsigned register_dpiset; unsigned shading_resolution; unsigned shading_factor; int output_pixel_offset; }; CustomSensorSettings custom_settings[] = { { { 75 }, 600, 150, 600, 8, 2 }, { { 100 }, 600, 200, 600, 6, 3 }, { { 150 }, 600, 300, 600, 4, 4 }, { { 200 }, 600, 400, 600, 3, 6 }, { { 300 }, 600, 600, 600, 2, 9 }, { { 600 }, 600, 1200, 600, 1, 17 }, { { 1200 }, 1200, 1200, 1200, 1, 35 }, }; for (const CustomSensorSettings& setting : custom_settings) { sensor.resolutions = setting.resolutions; sensor.optical_resolution = setting.optical_resolution; sensor.register_dpiset = setting.register_dpiset; sensor.shading_resolution = setting.shading_resolution; sensor.shading_factor = setting.shading_factor; sensor.output_pixel_offset = setting.output_pixel_offset; s_sensors->push_back(sensor); } } } void verify_sensor_tables() { std::map sensor_to_asic; for (const auto& device : *s_usb_devices) { sensor_to_asic[device.model().sensor_id] = device.model().asic_type; } for (const auto& sensor : *s_sensors) { if (sensor_to_asic.count(sensor.sensor_id) == 0) { throw SaneException("Unknown asic for sensor"); } auto asic_type = sensor_to_asic[sensor.sensor_id]; if (sensor.full_resolution == 0) { throw SaneException("full_resolution is not defined"); } if (sensor.register_dpiset == 0) { throw SaneException("register_dpiset is not defined"); } if (asic_type != AsicType::GL646) { if (sensor.register_dpihw == 0) { throw SaneException("register_dpihw is not defined"); } if (sensor.shading_resolution == 0) { throw SaneException("shading_resolution is not defined"); } } if (asic_type == AsicType::GL841) { auto required_registers = { 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x70, 0x71, 0x72, 0x73, }; for (auto address : required_registers) { if (!sensor.custom_regs.has_reg(address)) { throw SaneException("Required register is not present"); } } } if (asic_type == AsicType::GL842) { auto required_registers = { 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1c, 0x1d, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7f }; for (auto address : required_registers) { if (!sensor.custom_regs.has_reg(address)) { throw SaneException("Required register is not present"); } } } } } } // namespace genesys backends-1.3.0/backend/genesys/test_scanner_interface.cpp000066400000000000000000000127441456256263500235730ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "test_scanner_interface.h" #include "device.h" #include namespace genesys { TestScannerInterface::TestScannerInterface(Genesys_Device* dev, std::uint16_t vendor_id, std::uint16_t product_id, std::uint16_t bcd_device) : dev_{dev}, usb_dev_{vendor_id, product_id, bcd_device} { // initialize status registers if (dev_->model->asic_type == AsicType::GL124) { write_register(0x101, 0x00); } else { write_register(0x41, 0x00); } if (dev_->model->asic_type == AsicType::GL841 || dev_->model->asic_type == AsicType::GL842 || dev_->model->asic_type == AsicType::GL843 || dev_->model->asic_type == AsicType::GL845 || dev_->model->asic_type == AsicType::GL846 || dev_->model->asic_type == AsicType::GL847) { write_register(0x40, 0x00); } // initialize other registers that we read on init if (dev_->model->asic_type == AsicType::GL124) { write_register(0x33, 0x00); write_register(0xbd, 0x00); write_register(0xbe, 0x00); write_register(0x100, 0x00); } if (dev_->model->asic_type == AsicType::GL845 || dev_->model->asic_type == AsicType::GL846 || dev_->model->asic_type == AsicType::GL847) { write_register(0xbd, 0x00); write_register(0xbe, 0x00); write_register(0xd0, 0x00); write_register(0xd1, 0x01); write_register(0xd2, 0x02); write_register(0xd3, 0x03); write_register(0xd4, 0x04); write_register(0xd5, 0x05); write_register(0xd6, 0x06); write_register(0xd7, 0x07); write_register(0xd8, 0x08); write_register(0xd9, 0x09); } } TestScannerInterface::~TestScannerInterface() = default; bool TestScannerInterface::is_mock() const { return true; } std::uint8_t TestScannerInterface::read_register(std::uint16_t address) { return cached_regs_.get(address); } void TestScannerInterface::write_register(std::uint16_t address, std::uint8_t value) { cached_regs_.update(address, value); } void TestScannerInterface::write_registers(const Genesys_Register_Set& regs) { cached_regs_.update(regs); } void TestScannerInterface::write_0x8c(std::uint8_t index, std::uint8_t value) { (void) index; (void) value; } void TestScannerInterface::bulk_read_data(std::uint8_t addr, std::uint8_t* data, std::size_t size) { (void) addr; std::memset(data, 0, size); } void TestScannerInterface::bulk_write_data(std::uint8_t addr, std::uint8_t* data, std::size_t size) { (void) addr; (void) data; (void) size; } void TestScannerInterface::write_buffer(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, std::size_t size) { (void) type; (void) addr; (void) data; (void) size; } void TestScannerInterface::write_gamma(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, std::size_t size) { (void) type; (void) addr; (void) data; (void) size; } void TestScannerInterface::write_ahb(std::uint32_t addr, std::uint32_t size, std::uint8_t* data) { (void) addr; (void) size; (void) data; } std::uint16_t TestScannerInterface::read_fe_register(std::uint8_t address) { return cached_fe_regs_.get(address); } void TestScannerInterface::write_fe_register(std::uint8_t address, std::uint16_t value) { cached_fe_regs_.update(address, value); } IUsbDevice& TestScannerInterface::get_usb_device() { return usb_dev_; } void TestScannerInterface::sleep_us(unsigned microseconds) { (void) microseconds; } void TestScannerInterface::record_slope_table(unsigned table_nr, const std::vector& steps) { slope_tables_[table_nr] = steps; } std::map>& TestScannerInterface::recorded_slope_tables() { return slope_tables_; } void TestScannerInterface::record_progress_message(const char* msg) { last_progress_message_ = msg; } const std::string& TestScannerInterface::last_progress_message() const { return last_progress_message_; } void TestScannerInterface::record_key_value(const std::string& key, const std::string& value) { key_values_[key] = value; } std::map& TestScannerInterface::recorded_key_values() { return key_values_; } void TestScannerInterface::test_checkpoint(const std::string& name) { if (checkpoint_callback_) { checkpoint_callback_(*dev_, *this, name); } } void TestScannerInterface::set_checkpoint_callback(TestCheckpointCallback callback) { checkpoint_callback_ = callback; } } // namespace genesys backends-1.3.0/backend/genesys/test_scanner_interface.h000066400000000000000000000067241456256263500232410ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_TEST_SCANNER_INTERFACE_H #define BACKEND_GENESYS_TEST_SCANNER_INTERFACE_H #include "scanner_interface.h" #include "register_cache.h" #include "test_usb_device.h" #include "test_settings.h" #include namespace genesys { class TestScannerInterface : public ScannerInterface { public: TestScannerInterface(Genesys_Device* dev, std::uint16_t vendor_id, std::uint16_t product_id, std::uint16_t bcd_device); ~TestScannerInterface() override; bool is_mock() const override; const RegisterCache& cached_regs() const { return cached_regs_; } const RegisterCache& cached_fe_regs() const { return cached_fe_regs_; } std::uint8_t read_register(std::uint16_t address) override; void write_register(std::uint16_t address, std::uint8_t value) override; void write_registers(const Genesys_Register_Set& regs) override; void write_0x8c(std::uint8_t index, std::uint8_t value) override; void bulk_read_data(std::uint8_t addr, std::uint8_t* data, std::size_t size) override; void bulk_write_data(std::uint8_t addr, std::uint8_t* data, std::size_t size) override; void write_buffer(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, std::size_t size) override; void write_gamma(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, std::size_t size) override; void write_ahb(std::uint32_t addr, std::uint32_t size, std::uint8_t* data) override; std::uint16_t read_fe_register(std::uint8_t address) override; void write_fe_register(std::uint8_t address, std::uint16_t value) override; IUsbDevice& get_usb_device() override; void sleep_us(unsigned microseconds) override; void record_progress_message(const char* msg) override; const std::string& last_progress_message() const; void record_slope_table(unsigned table_nr, const std::vector& steps) override; std::map>& recorded_slope_tables(); void record_key_value(const std::string& key, const std::string& value) override; std::map& recorded_key_values(); void test_checkpoint(const std::string& name) override; void set_checkpoint_callback(TestCheckpointCallback callback); private: Genesys_Device* dev_; RegisterCache cached_regs_; RegisterCache cached_fe_regs_; TestUsbDevice usb_dev_; TestCheckpointCallback checkpoint_callback_; std::map> slope_tables_; std::string last_progress_message_; std::map key_values_; }; } // namespace genesys #endif backends-1.3.0/backend/genesys/test_settings.cpp000066400000000000000000000042511456256263500217540ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "test_settings.h" namespace genesys { namespace { bool s_testing_mode = false; std::uint16_t s_vendor_id = 0; std::uint16_t s_product_id = 0; std::uint16_t s_bcd_device = 0; TestCheckpointCallback s_checkpoint_callback; } // namespace bool is_testing_mode() { return s_testing_mode; } void disable_testing_mode() { s_testing_mode = false; s_vendor_id = 0; s_product_id = 0; s_bcd_device = 0; } void enable_testing_mode(std::uint16_t vendor_id, std::uint16_t product_id, std::uint16_t bcd_device, TestCheckpointCallback checkpoint_callback) { s_testing_mode = true; s_vendor_id = vendor_id; s_product_id = product_id; s_bcd_device = bcd_device; s_checkpoint_callback = checkpoint_callback; } std::uint16_t get_testing_vendor_id() { return s_vendor_id; } std::uint16_t get_testing_product_id() { return s_product_id; } std::uint16_t get_testing_bcd_device() { return s_bcd_device; } std::string get_testing_device_name() { std::string name; unsigned max_size = 50; name.resize(max_size); name.resize(std::snprintf(&name.front(), max_size, "test device:0x%04x:0x%04x", s_vendor_id, s_product_id)); return name; } TestCheckpointCallback get_testing_checkpoint_callback() { return s_checkpoint_callback; } } // namespace genesys backends-1.3.0/backend/genesys/test_settings.h000066400000000000000000000033311456256263500214170ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_TEST_SETTINGS_H #define BACKEND_GENESYS_TEST_SETTINGS_H #include "scanner_interface.h" #include "register_cache.h" #include "test_usb_device.h" #include namespace genesys { using TestCheckpointCallback = std::function; bool is_testing_mode(); void disable_testing_mode(); void enable_testing_mode(std::uint16_t vendor_id, std::uint16_t product_id, std::uint16_t bcd_device, TestCheckpointCallback checkpoint_callback); std::uint16_t get_testing_vendor_id(); std::uint16_t get_testing_product_id(); std::uint16_t get_testing_bcd_device(); std::string get_testing_device_name(); TestCheckpointCallback get_testing_checkpoint_callback(); } // namespace genesys #endif // BACKEND_GENESYS_TEST_SETTINGS_H backends-1.3.0/backend/genesys/test_usb_device.cpp000066400000000000000000000054331456256263500222270ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "test_usb_device.h" #include "low.h" namespace genesys { TestUsbDevice::TestUsbDevice(std::uint16_t vendor, std::uint16_t product, std::uint16_t bcd_device) : vendor_{vendor}, product_{product}, bcd_device_{bcd_device} { } TestUsbDevice::~TestUsbDevice() { if (is_open()) { DBG(DBG_error, "TestUsbDevice not closed; closing automatically"); close(); } } void TestUsbDevice::open(const char* dev_name) { DBG_HELPER(dbg); if (is_open()) { throw SaneException("device already open"); } name_ = dev_name; is_open_ = true; } void TestUsbDevice::clear_halt() { DBG_HELPER(dbg); assert_is_open(); } void TestUsbDevice::reset() { DBG_HELPER(dbg); assert_is_open(); } void TestUsbDevice::close() { DBG_HELPER(dbg); assert_is_open(); is_open_ = false; name_ = ""; } std::uint16_t TestUsbDevice::get_vendor_id() { DBG_HELPER(dbg); assert_is_open(); return vendor_; } std::uint16_t TestUsbDevice::get_product_id() { DBG_HELPER(dbg); assert_is_open(); return product_; } std::uint16_t TestUsbDevice::get_bcd_device() { DBG_HELPER(dbg); assert_is_open(); return bcd_device_; } void TestUsbDevice::control_msg(int rtype, int reg, int value, int index, int length, std::uint8_t* data) { (void) reg; (void) value; (void) index; DBG_HELPER(dbg); assert_is_open(); if (rtype == REQUEST_TYPE_IN) { std::memset(data, 0, length); } } void TestUsbDevice::bulk_read(std::uint8_t* buffer, std::size_t* size) { DBG_HELPER(dbg); assert_is_open(); std::memset(buffer, 0, *size); } void TestUsbDevice::bulk_write(const std::uint8_t* buffer, std::size_t* size) { (void) buffer; (void) size; DBG_HELPER(dbg); assert_is_open(); } void TestUsbDevice::assert_is_open() const { if (!is_open()) { throw SaneException("device not open"); } } } // namespace genesys backends-1.3.0/backend/genesys/test_usb_device.h000066400000000000000000000037371456256263500217010ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_TEST_USB_DEVICE_H #define BACKEND_GENESYS_TEST_USB_DEVICE_H #include "usb_device.h" namespace genesys { class TestUsbDevice : public IUsbDevice { public: TestUsbDevice(std::uint16_t vendor, std::uint16_t product, std::uint16_t bcd_device); ~TestUsbDevice() override; bool is_open() const override { return is_open_; } const std::string& name() const override { return name_; } void open(const char* dev_name) override; void clear_halt() override; void reset() override; void close() override; std::uint16_t get_vendor_id() override; std::uint16_t get_product_id() override; std::uint16_t get_bcd_device() override; void control_msg(int rtype, int reg, int value, int index, int length, std::uint8_t* data) override; void bulk_read(std::uint8_t* buffer, std::size_t* size) override; void bulk_write(const std::uint8_t* buffer, std::size_t* size) override; private: void assert_is_open() const; std::string name_; bool is_open_ = false; std::uint16_t vendor_ = 0; std::uint16_t product_ = 0; std::uint16_t bcd_device_ = 0; }; } // namespace genesys #endif // BACKEND_GENESYS_TEST_USB_DEVICE_H backends-1.3.0/backend/genesys/usb_device.cpp000066400000000000000000000065571456256263500212000ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #define DEBUG_DECLARE_ONLY #include "usb_device.h" namespace genesys { IUsbDevice::~IUsbDevice() = default; UsbDevice::~UsbDevice() { if (is_open()) { DBG(DBG_error, "UsbDevice not closed; closing automatically"); close(); } } void UsbDevice::open(const char* dev_name) { DBG_HELPER(dbg); if (is_open()) { throw SaneException("device already open"); } int device_num = 0; dbg.status("open device"); TIE(sanei_usb_open(dev_name, &device_num)); name_ = dev_name; device_num_ = device_num; is_open_ = true; } void UsbDevice::clear_halt() { DBG_HELPER(dbg); assert_is_open(); TIE(sanei_usb_clear_halt(device_num_)); } void UsbDevice::reset() { DBG_HELPER(dbg); assert_is_open(); TIE(sanei_usb_reset(device_num_)); } void UsbDevice::close() { DBG_HELPER(dbg); assert_is_open(); // we can't do much if closing fails, so we close the device on our side regardless of the // function succeeds int device_num = device_num_; set_not_open(); sanei_usb_close(device_num); } std::uint16_t UsbDevice::get_vendor_id() { DBG_HELPER(dbg); assert_is_open(); int vendor = 0; int product = 0; TIE(sanei_usb_get_vendor_product(device_num_, &vendor, &product)); return static_cast(vendor); } std::uint16_t UsbDevice::get_product_id() { DBG_HELPER(dbg); assert_is_open(); int vendor = 0; int product = 0; TIE(sanei_usb_get_vendor_product(device_num_, &vendor, &product)); return static_cast(product); } std::uint16_t UsbDevice::get_bcd_device() { DBG_HELPER(dbg); assert_is_open(); sanei_usb_dev_descriptor desc; TIE(sanei_usb_get_descriptor(device_num_, &desc)); return desc.bcd_dev; } void UsbDevice::control_msg(int rtype, int reg, int value, int index, int length, std::uint8_t* data) { DBG_HELPER(dbg); assert_is_open(); TIE(sanei_usb_control_msg(device_num_, rtype, reg, value, index, length, data)); } void UsbDevice::bulk_read(std::uint8_t* buffer, std::size_t* size) { DBG_HELPER(dbg); assert_is_open(); TIE(sanei_usb_read_bulk(device_num_, buffer, size)); } void UsbDevice::bulk_write(const std::uint8_t* buffer, std::size_t* size) { DBG_HELPER(dbg); assert_is_open(); TIE(sanei_usb_write_bulk(device_num_, buffer, size)); } void UsbDevice::assert_is_open() const { if (!is_open()) { throw SaneException("device not open"); } } void UsbDevice::set_not_open() { device_num_ = 0; is_open_ = false; name_ = ""; } } // namespace genesys backends-1.3.0/backend/genesys/usb_device.h000066400000000000000000000054041456256263500206330ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_USB_DEVICE_H #define BACKEND_GENESYS_USB_DEVICE_H #include "error.h" #include "../include/sane/sanei_usb.h" #include #include namespace genesys { class IUsbDevice { public: IUsbDevice() = default; IUsbDevice(const IUsbDevice& other) = delete; IUsbDevice& operator=(const IUsbDevice&) = delete; virtual ~IUsbDevice(); virtual bool is_open() const = 0; virtual const std::string& name() const = 0; virtual void open(const char* dev_name) = 0; virtual void clear_halt() = 0; virtual void reset() = 0; virtual void close() = 0; virtual std::uint16_t get_vendor_id() = 0; virtual std::uint16_t get_product_id() = 0; virtual std::uint16_t get_bcd_device() = 0; virtual void control_msg(int rtype, int reg, int value, int index, int length, std::uint8_t* data) = 0; virtual void bulk_read(std::uint8_t* buffer, std::size_t* size) = 0; virtual void bulk_write(const std::uint8_t* buffer, std::size_t* size) = 0; }; class UsbDevice : public IUsbDevice { public: UsbDevice() = default; ~UsbDevice() override; bool is_open() const override { return is_open_; } const std::string& name() const override { return name_; } void open(const char* dev_name) override; void clear_halt() override; void reset() override; void close() override; std::uint16_t get_vendor_id() override; std::uint16_t get_product_id() override; std::uint16_t get_bcd_device() override; void control_msg(int rtype, int reg, int value, int index, int length, std::uint8_t* data) override; void bulk_read(std::uint8_t* buffer, std::size_t* size) override; void bulk_write(const std::uint8_t* buffer, std::size_t* size) override; private: void assert_is_open() const; void set_not_open(); std::string name_; bool is_open_ = false; int device_num_ = 0; }; } // namespace genesys #endif // BACKEND_GENESYS_USB_DEVICE_H backends-1.3.0/backend/genesys/utilities.h000066400000000000000000000164311456256263500205400ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_UTILITIES_H #define BACKEND_GENESYS_UTILITIES_H #include "error.h" #include #include #include #include #include namespace genesys { // just like SANE_FIX and SANE_UNFIX except that the conversion is done by a function and argument // precision is handled correctly inline SANE_Word double_to_fixed(double v) { return static_cast(v * (1 << SANE_FIXED_SCALE_SHIFT)); } inline SANE_Word float_to_fixed(float v) { return static_cast(v * (1 << SANE_FIXED_SCALE_SHIFT)); } inline float fixed_to_float(SANE_Word v) { return static_cast(v) / (1 << SANE_FIXED_SCALE_SHIFT); } inline double fixed_to_double(SANE_Word v) { return static_cast(v) / (1 << SANE_FIXED_SCALE_SHIFT); } template inline T abs_diff(T a, T b) { if (a < b) { return b - a; } else { return a - b; } } inline std::uint64_t align_multiple_floor(std::uint64_t x, std::uint64_t multiple) { if (multiple == 0) { return x; } return (x / multiple) * multiple; } inline std::uint64_t align_multiple_ceil(std::uint64_t x, std::uint64_t multiple) { if (multiple == 0) { return x; } return ((x + multiple - 1) / multiple) * multiple; } inline std::uint64_t multiply_by_depth_ceil(std::uint64_t pixels, std::uint64_t depth) { if (depth == 1) { return (pixels / 8) + ((pixels % 8) ? 1 : 0); } else { return pixels * (depth / 8); } } template inline T clamp(const T& value, const T& lo, const T& hi) { if (value < lo) return lo; if (value > hi) return hi; return value; } template void compute_array_percentile_approx(T* result, const T* data, std::size_t line_count, std::size_t elements_per_line, float percentile) { if (line_count == 0) { throw SaneException("invalid line count"); } if (line_count == 1) { std::copy(data, data + elements_per_line, result); return; } std::vector column_elems; column_elems.resize(line_count, 0); std::size_t select_elem = std::min(static_cast(line_count * percentile), line_count - 1); auto select_it = column_elems.begin() + select_elem; for (std::size_t ix = 0; ix < elements_per_line; ++ix) { for (std::size_t iy = 0; iy < line_count; ++iy) { column_elems[iy] = data[iy * elements_per_line + ix]; } std::nth_element(column_elems.begin(), select_it, column_elems.end()); *result++ = *select_it; } } class Ratio { public: Ratio() : multiplier_{1}, divisor_{1} { } Ratio(unsigned multiplier, unsigned divisor) : multiplier_{multiplier}, divisor_{divisor} { } unsigned multiplier() const { return multiplier_; } unsigned divisor() const { return divisor_; } unsigned apply(unsigned arg) const { return static_cast(arg) * multiplier_ / divisor_; } int apply(int arg) const { return static_cast(arg) * multiplier_ / divisor_; } float apply(float arg) const { return arg * multiplier_ / divisor_; } unsigned apply_inverse(unsigned arg) const { return static_cast(arg) * divisor_ / multiplier_; } int apply_inverse(int arg) const { return static_cast(arg) * divisor_ / multiplier_; } float apply_inverse(float arg) const { return arg * divisor_ / multiplier_; } bool operator==(const Ratio& other) const { return multiplier_ == other.multiplier_ && divisor_ == other.divisor_; } private: unsigned multiplier_; unsigned divisor_; template friend void serialize(Stream& str, Ratio& x); }; template void serialize(Stream& str, Ratio& x) { serialize(str, x.multiplier_); serialize(str, x.divisor_); } inline std::ostream& operator<<(std::ostream& out, const Ratio& ratio) { out << ratio.multiplier() << "/" << ratio.divisor(); return out; } template class BasicStreamStateSaver { public: explicit BasicStreamStateSaver(std::basic_ios& stream) : stream_(stream) { flags_ = stream_.flags(); width_ = stream_.width(); precision_ = stream_.precision(); fill_ = stream_.fill(); } ~BasicStreamStateSaver() { stream_.flags(flags_); stream_.width(width_); stream_.precision(precision_); stream_.fill(fill_); } BasicStreamStateSaver(const BasicStreamStateSaver&) = delete; BasicStreamStateSaver& operator=(const BasicStreamStateSaver&) = delete; private: std::basic_ios& stream_; std::ios_base::fmtflags flags_; std::streamsize width_ = 0; std::streamsize precision_ = 0; Char fill_ = ' '; }; using StreamStateSaver = BasicStreamStateSaver>; template std::string format_indent_braced_list(unsigned indent, const T& x) { std::string indent_str(indent, ' '); std::ostringstream out; out << x; auto formatted_str = out.str(); if (formatted_str.empty()) { return formatted_str; } std::string out_str; for (std::size_t i = 0; i < formatted_str.size(); ++i) { out_str += formatted_str[i]; if (formatted_str[i] == '\n' && i < formatted_str.size() - 1 && formatted_str[i + 1] != '\n') { out_str += indent_str; } } return out_str; } template std::string format_vector_unsigned(unsigned indent, const std::vector& arg) { std::ostringstream out; std::string indent_str(indent, ' '); out << "std::vector{ "; for (const auto& el : arg) { out << indent_str << static_cast(el) << "\n"; } out << "}"; return out.str(); } template std::string format_vector_indent_braced(unsigned indent, const char* type, const std::vector& arg) { if (arg.empty()) { return "{}"; } std::string indent_str(indent, ' '); std::stringstream out; out << "std::vector<" << type << ">{\n"; for (const auto& item : arg) { out << indent_str << format_indent_braced_list(indent, item) << '\n'; } out << "}"; return out.str(); } } // namespace genesys #endif // BACKEND_GENESYS_UTILITIES_H backends-1.3.0/backend/genesys/value_filter.h000066400000000000000000000065741456256263500212150ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2020 Povilas Kanapickas 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, see . */ #ifndef BACKEND_GENESYS_VALUE_FILTER_H #define BACKEND_GENESYS_VALUE_FILTER_H #include #include #include #include namespace genesys { struct AnyTag {}; constexpr AnyTag VALUE_FILTER_ANY{}; template class ValueFilterAny { public: ValueFilterAny() : matches_any_{false} {} ValueFilterAny(AnyTag) : matches_any_{true} {} ValueFilterAny(std::initializer_list values) : matches_any_{false}, values_{values} {} bool matches(T value) const { if (matches_any_) return true; auto it = std::find(values_.begin(), values_.end(), value); return it != values_.end(); } bool operator==(const ValueFilterAny& other) const { return matches_any_ == other.matches_any_ && values_ == other.values_; } bool matches_any() const { return matches_any_; } const std::vector& values() const { return values_; } private: bool matches_any_ = false; std::vector values_; template friend void serialize(Stream& str, ValueFilterAny& x); }; template std::ostream& operator<<(std::ostream& out, const ValueFilterAny& values) { if (values.matches_any()) { out << "ANY"; return out; } out << format_vector_indent_braced(4, "", values.values()); return out; } template void serialize(Stream& str, ValueFilterAny& x) { serialize(str, x.matches_any_); serialize_newline(str); serialize(str, x.values_); } template class ValueFilter { public: ValueFilter() = default; ValueFilter(std::initializer_list values) : values_{values} {} bool matches(T value) const { auto it = std::find(values_.begin(), values_.end(), value); return it != values_.end(); } bool operator==(const ValueFilter& other) const { return values_ == other.values_; } const std::vector& values() const { return values_; } private: std::vector values_; template friend void serialize(Stream& str, ValueFilter& x); }; template std::ostream& operator<<(std::ostream& out, const ValueFilter& values) { if (values.values().empty()) { out << "(none)"; return out; } out << format_vector_indent_braced(4, "", values.values()); return out; } template void serialize(Stream& str, ValueFilter& x) { serialize_newline(str); serialize(str, x.values_); } } // namespace genesys #endif // BACKEND_GENESYS_VALUE_FILTER_H backends-1.3.0/backend/gphoto2.c000066400000000000000000001403531456256263500164260ustar00rootroot00000000000000/* Please note! Although intended to support multiple camera types * it's been tested with only cameras I have access to: the Kodak DC240 * and the Directory Browse "camera." I'm very interested * in learning what it would take to support more cameras. In * particular, the current incarnation will only support cameras * that directly generate jpeg files. * * Please report successes or failures using this backend! * * However, having said that, I've already found it to be quite useful * even in its current form - one reason is that gphoto2 provides access * to the camera via USB which is not supported by the regular DC240 * backend and is dramatically faster than the serial port. */ /*************************************************************************** * _S_A_N_E - Scanner Access Now Easy. gphoto2.c 03/12/01 - Peter Fales Based on the dc210 driver, (C) 1998 Brian J. Murrell (which is based on dc25 driver (C) 1998 by Peter Fales) This file (C) 2001 by Peter Fales 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, see . 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 digital cameras supported by the gphoto2 libraries. THIS IS EXTREMELY ALPHA CODE! USE AT YOUR OWN RISK!! (feedback to: gphoto2-devel@fales-lorenz.net) This backend is based somewhat on the dc25 backend included in this package by Peter Fales, and the dc210 backend by Brian J. Murrell ***************************************************************************/ #include "../include/sane/config.h" #include #include #include #include #include #include #include "../include/sane/sanei_jpeg.h" #include #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #define BACKEND_NAME gphoto2 #include "../include/sane/sanei_backend.h" /* PSF 1/12/02 - gphoto2.h does a #include of config.h. We don't have * config.h by that name (we call it sane/config.h), so the #undef of * HAVE_CONFIG_H will cause it to skip that. */ #undef HAVE_CONFIG_H #include "gphoto2.h" #include #include #define CHECK_RET(f) {int res = f; if (res < 0) {DBG (1,"ERROR: %s\n", gp_result_as_string (res)); return (SANE_STATUS_INVAL);}} #ifndef PATH_MAX # define PATH_MAX 1024 #endif #define MAGIC (void *)0xab730324 #define GPHOTO2_CONFIG_FILE "gphoto2.conf" static SANE_Bool is_open = 0; /* Options selected by frontend: */ static SANE_Bool gphoto2_opt_thumbnails; /* Read thumbnails */ static SANE_Bool gphoto2_opt_snap; /* Take new picture */ static SANE_Bool gphoto2_opt_lowres; /* Set low resolution */ static SANE_Bool gphoto2_opt_erase; /* Erase after downloading */ static SANE_Bool gphoto2_opt_autoinc; /* Increment image number */ static SANE_Bool dumpinquiry; /* Dump status info */ /* Used for jpeg decompression */ static struct jpeg_decompress_struct cinfo; static djpeg_dest_ptr dest_mgr = NULL; static SANE_Int highres_height = 960, highres_width = 1280; static SANE_Int thumb_height = 120, thumb_width = 160; static SANE_String TopFolder; /* Fixed part of path strings */ static SANE_Int SubDirs = 1; /* Search for Sub directories */ static GPHOTO2 Cam_data; /* Other camera data */ static SANE_Range image_range = { 0, 0, 0 }; static SANE_String *folder_list; static SANE_Int current_folder = 0; static SANE_Option_Descriptor sod[] = { { SANE_NAME_NUM_OPTIONS, SANE_TITLE_NUM_OPTIONS, SANE_DESC_NUM_OPTIONS, SANE_TYPE_INT, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} } , #define GPHOTO2_OPT_IMAGE_SELECTION 1 { "", "Image Selection", "Selection of the image to load.", SANE_TYPE_GROUP, SANE_UNIT_NONE, 0, 0, SANE_CONSTRAINT_NONE, {NULL} } , #define GPHOTO2_OPT_FOLDER 2 { "folder", "Folder", "Select folder within camera", SANE_TYPE_STRING, SANE_UNIT_NONE, 256, SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_STRING_LIST, {NULL} } , #define GPHOTO2_OPT_IMAGE_NUMBER 3 { "image", "Image Number", "Select Image Number to load from camera", SANE_TYPE_INT, SANE_UNIT_NONE, 4, SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_RANGE, {(SANE_String_Const *) & image_range} /* this is ANSI conformant! */ } , #define GPHOTO2_OPT_THUMBS 4 { "thumbs", "Load Thumbnail", "Load the image as thumbnail.", SANE_TYPE_BOOL, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} } , #define GPHOTO2_OPT_SNAP 5 { "snap", "Snap new picture", "Take new picture and download it", SANE_TYPE_BOOL, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT /* | SANE_CAP_ADVANCED */ , SANE_CONSTRAINT_NONE, {NULL} } , #define GPHOTO2_OPT_LOWRES 6 { "lowres", "Low Resolution", "Resolution of new picture or selected image (must be manually specified)", SANE_TYPE_BOOL, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE /* Until we figure out how to support it */ /* | SANE_CAP_ADVANCED */ , SANE_CONSTRAINT_NONE, {NULL} } , #define GPHOTO2_OPT_ERASE 7 { "erase", "Erase", "Erase the picture after downloading", SANE_TYPE_BOOL, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} } , #define GPHOTO2_OPT_DEFAULT 8 { "default-enhancements", "Defaults", "Set default values for enhancement controls.", SANE_TYPE_BUTTON, SANE_UNIT_NONE, 0, SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} } , #define GPHOTO2_OPT_INIT_GPHOTO2 9 { "camera-init", "Re-establish Communications", "Re-establish communications with camera (in case of timeout, etc.)", SANE_TYPE_BUTTON, SANE_UNIT_NONE, 0, SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} } , #define GPHOTO2_OPT_AUTOINC 10 { "autoinc", "Auto Increment", "Increment image number after each scan", SANE_TYPE_BOOL, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED, SANE_CONSTRAINT_NONE, {NULL} } , }; static SANE_Parameters parms = { SANE_FRAME_RGB, 0, 0, /* Number of bytes returned per scan line: */ 0, /* Number of pixels per scan line. */ 0, /* Number of lines for the current scan. */ 8, /* Number of bits per sample. */ }; CameraList *dir_list; Camera *camera; /* Buffer to hold line currently being processed by sane_read */ static SANE_Byte *linebuffer = NULL; static SANE_Int linebuffer_size = 0; static SANE_Int linebuffer_index = 0; /* used for setting up commands */ static SANE_Char cmdbuf[256]; /* Structures used by gphoto2 API */ static CameraAbilities abilities; static CameraFile *data_file; static const unsigned char *data_ptr; static unsigned long data_file_total_size, data_file_current_index; static SANE_Int hack_fd; #include #include /* Device select/open/close */ static SANE_Device dev[] = { { "0", "Gphoto2", "Supported", "still camera"}, }; static const SANE_Device *devlist[] = { dev + 0, 0 }; /* * debug_func - called for gphoto2 debugging output (if enabled) */ static void #ifdef GPLOGFUNC_NO_VARGS debug_func (GPLogLevel level, const char *domain, const char *message, void __sane_unused__ * data) #else debug_func (GPLogLevel level, const char *domain, const char *format, va_list args, void __sane_unused__ * data) #endif { if (level == GP_LOG_ERROR) DBG (0, "%s(ERROR): ", domain); else DBG (0, "%s(%i): ", domain, level); #ifdef GPLOGFUNC_NO_VARGS DBG (0, "%s", message); #else sanei_debug_msg (0, DBG_LEVEL, STRINGIFY (BACKEND_NAME), format, args); #endif DBG (0, "\n"); } /* * init_gphoto2() - Initialize interface to camera using gphoto2 API */ static SANE_Int init_gphoto2 (void) { CameraList *list; GPPortInfoList *il; GPPortInfo info; SANE_Int n, m, port; CameraAbilitiesList *al; gp_log (GP_LOG_VERBOSE, "SANE", "Initializing\n"); if (!Cam_data.camera_name) { DBG (0, "init_gphoto2: Camera name not specified in config file\n"); return SANE_STATUS_INVAL; } if (camera) { /* * We get here if re-initializing the camera: either because * the user clicked the "re-establish" button, or we need to * recalculate the number of photos after taking a picture. * We must release the old camera before starting over. */ CHECK_RET (gp_camera_unref (camera)); } CHECK_RET (gp_camera_new (&camera)); CHECK_RET (gp_abilities_list_new (&al)); CHECK_RET (gp_abilities_list_load (al, NULL)); CHECK_RET (m = gp_abilities_list_lookup_model (al, (char *) Cam_data.camera_name)); CHECK_RET (gp_abilities_list_get_abilities (al, m, &abilities)); CHECK_RET (gp_abilities_list_free (al)); CHECK_RET (gp_camera_set_abilities (camera, abilities)); if (!Cam_data.port) { DBG (0, "init_gphoto2: Camera port not specified in config file\n"); return SANE_STATUS_INVAL; } CHECK_RET (gp_port_info_list_new (&il)); CHECK_RET (gp_port_info_list_load (il)); if (strcmp (Cam_data.port, "Browse") != 0) { CHECK_RET (port = gp_port_info_list_lookup_path (il, Cam_data.port)); CHECK_RET (gp_port_info_list_get_info (il, port, &info)); CHECK_RET (gp_camera_set_port_info (camera, info)); gp_port_info_list_free (il); } for (n = 0; abilities.speed[n]; n++) { if (abilities.speed[n] == Cam_data.speed) { break; } } if (abilities.speed[n] == 0 && !strncmp (Cam_data.port, "serial:", 7)) { DBG (0, "%s: error: %d is not a valid speed for this camers. Use \"gphoto2 --camera \"%s\" --abilities\" for list.\n", "init_gphoto2", Cam_data.speed, Cam_data.camera_name); return SANE_STATUS_INVAL; } DBG (4, "init_gphoto2: about to initialize port\n"); /* * Setting of speed only makes sense for serial ports. gphoto2 * knows that and will complain if we try to set the speed for * ports other than serial ones. Because we are paranoid here and * check every single error message returned by gphoto2, we need * to make sure that we have a serial port. */ if (Cam_data.speed && !strncmp (Cam_data.port, "serial:", 7)) { /* * Not sure why we need this hack. The API keeps opening/closing * the port, and that seems to confuse the camera. Holding * the port open seems to fix it. */ if ((hack_fd = open (Cam_data.port + 7, O_RDONLY)) < 0) { return SANE_STATUS_INVAL; } #ifdef HAVE_USLEEP usleep (200); #else sleep (1); #endif CHECK_RET (gp_camera_set_port_speed (camera, Cam_data.speed)); } CHECK_RET (gp_camera_init (camera, NULL)); if (!(abilities.operations & GP_OPERATION_CAPTURE_IMAGE)) { DBG (20, "init_gphoto2: Camera does not support image capture\n"); sod[GPHOTO2_OPT_SNAP].cap |= SANE_CAP_INACTIVE; } if (!(abilities.file_operations & GP_FILE_OPERATION_PREVIEW)) { DBG (20, "init_gphoto2: Camera does not support image preview\n"); sod[GPHOTO2_OPT_THUMBS].cap |= SANE_CAP_INACTIVE; } if (!(abilities.file_operations & GP_FILE_OPERATION_DELETE)) { DBG (20, "init_gphoto2: Camera does not support image deletion\n"); sod[GPHOTO2_OPT_ERASE].cap |= SANE_CAP_INACTIVE; } DBG (4, "init_gphoto2: about to get folders\n"); CHECK_RET (gp_list_new (&list)); CHECK_RET (gp_camera_folder_list_folders (camera, TopFolder, list, NULL)); n = gp_list_count (list); if (n < 0) { DBG (0, "init_gphoto2: Unable to get file list\n"); return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } /* * close_gphoto2() - Shutdown camera interface */ static void close_gphoto2 (void) { /* * Put the camera back to 9600 baud */ if (gp_camera_unref (camera)) { DBG (1, "close_gphoto2: error: could not close device\n"); } camera = NULL; close (hack_fd); } /* * get_info() - Get overall information about camera: folder names, * number of pictures, etc. */ SANE_Int get_info (void) { SANE_String_Const val; SANE_Int n; if (Cam_data.pic_taken == 0) { sod[GPHOTO2_OPT_IMAGE_NUMBER].cap |= SANE_CAP_INACTIVE; image_range.min = 0; image_range.max = 0; } else { sod[GPHOTO2_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE; image_range.min = 1; image_range.max = Cam_data.pic_taken; } if (SubDirs) { n = read_dir (TopFolder, 0); } else { n = 1; } /* If we've already got a folder_list, free it up before starting * the new one */ if (folder_list != NULL) { int tmp; for (tmp = 0; folder_list[tmp]; tmp++) { free (folder_list[tmp]); } free (folder_list); } folder_list = (SANE_String *) malloc ((n + 1) * sizeof (SANE_String_Const *)); if (SubDirs) { for (n = 0; n < gp_list_count (dir_list); n++) { gp_list_get_name (dir_list, n, &val); folder_list[n] = strdup (val); if (strchr ((const char *) folder_list[n], ' ')) { *strchr ((const char *) folder_list[n], ' ') = '\0'; } } if (n == 0) { folder_list[n++] = (SANE_String) strdup (""); } } else { n = 0; folder_list[n++] = "N/A"; } folder_list[n] = NULL; sod[GPHOTO2_OPT_FOLDER].constraint.string_list = (SANE_String_Const *) folder_list; Cam_data.pic_taken = 0; Cam_data.pic_left = 1; /* Just a guess! */ return SANE_STATUS_GOOD; } /* * erase() - erase file from camera corresponding to * current picture number. Does not update any of the other * backend data structures. */ static SANE_Int erase (void) { SANE_String_Const filename; if (SubDirs) { sprintf (cmdbuf, "%s/%s", (char *) TopFolder, (const char *) folder_list[current_folder]); } else { strcpy (cmdbuf, TopFolder); } CHECK_RET (gp_list_get_name (dir_list, Cam_data.current_picture_number - 1, &filename)); CHECK_RET (gp_camera_file_delete (camera, cmdbuf, filename, NULL)); return SANE_STATUS_GOOD; } /* * change_res() - FIXME: Would like to set resolution, but haven't figure * out how to control that yet. */ static SANE_Int change_res (SANE_Byte res) { return (res - res); } /* * sane_init() - Initialization function from SANE API. Initialize some * data structures, verify that all the necessary config information * is present, and initialize gphoto2 */ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize) { SANE_Int n, entries; SANE_Char f[] = "sane_init"; SANE_Char dev_name[PATH_MAX], *p; SANE_Char buf[256]; CameraAbilitiesList *al; size_t len; FILE *fp; DBG_INIT (); DBG (1, "GPHOTO2 Backend\n"); if (getenv ("GP_DEBUG")) { gp_log_add_func (atoi (getenv ("GP_DEBUG")), debug_func, NULL); } if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); fp = sanei_config_open (GPHOTO2_CONFIG_FILE); if (!fp) { /* Earlier versions why would try to keep going with compiled in * defaults if the config file is missing. But, now we have so * options and combinations of options, that success without a config * file is unlikely. So, give and return failure */ DBG (0, "warning: %s: missing config file '%s'\n" "If you aren't using gphoto2, you should disable it in dll.conf.\n" "If you do want to use gphoto2, you'll need to install the config\n" "file in %s.\n", f, GPHOTO2_CONFIG_FILE, GPHOTO2_CONFIG_FILE); return SANE_STATUS_INVAL; } else { while (sanei_config_read (dev_name, sizeof (dev_name), fp)) { dev_name[sizeof (dev_name) - 1] = '\0'; DBG (20, "%s: config- %s\n", f, dev_name); if (dev_name[0] == '#') continue; /* ignore line comments */ len = strlen (dev_name); if (!len) continue; /* ignore empty lines */ if (strncmp (dev_name, "port=", 5) == 0) { GPPortInfoList *list; GPPortInfo info; int result; p = dev_name + 5; if (p) Cam_data.port = strdup (p); DBG (20, "Config file port=%s\n", Cam_data.port); /* Validate port */ CHECK_RET (gp_port_info_list_new (&list)); result = gp_port_info_list_load (list); if (result < 0) { gp_port_info_list_free (list); return SANE_STATUS_INVAL; } entries = gp_port_info_list_count (list); if (entries < 0) { gp_port_info_list_free (list); return SANE_STATUS_INVAL; } for (n = 0; n < entries; n++) { #ifdef HAVE_GP_PORT_INFO_GET_PATH char *info_path = NULL; #endif result = gp_port_info_list_get_info (list, n, &info); if (result < 0) { gp_port_info_list_free (list); return SANE_STATUS_INVAL; } #ifdef HAVE_GP_PORT_INFO_GET_PATH gp_port_info_get_path (info, &info_path); if (strcmp (Cam_data.port, info_path) == 0) #else if (strcmp (Cam_data.port, info.path) == 0) #endif { break; } } if (n == entries) { DBG (0, "%s: error: %s is not a valid gphoto2 port. Use \"gphoto2 --list-ports\" for list.\n", "init_gphoto2", Cam_data.port); return SANE_STATUS_INVAL; } } else if (strncmp (dev_name, "camera=", 7) == 0) { Cam_data.camera_name = strdup (dev_name + 7); DBG (20, "Config file camera=%s\n", Cam_data.camera_name); sprintf (buf, "Image selection - %s", Cam_data.camera_name); CHECK_RET (gp_abilities_list_new (&al)); CHECK_RET (gp_abilities_list_load (al, NULL)); CHECK_RET (entries = gp_abilities_list_count (al)); for (n = 0; n < entries; n++) { CHECK_RET (gp_abilities_list_get_abilities (al, n, &abilities)); if (strcmp (Cam_data.camera_name, abilities.model) == 0) { break; } } if (n == entries) { DBG (0, "%s: error: %s is not a valid camera type. Use \"gphoto2 --list-cameras\" for list.\n", f, Cam_data.camera_name); return SANE_STATUS_INVAL; } /* Special case: Force port to special value for the * "Directory Browse" camera - overriding anything in * the config file - or more likely when not specified * in the config file. */ if (strcmp (Cam_data.camera_name, "Directory Browse") == 0) { Cam_data.port = "Browse"; } sod[GPHOTO2_OPT_IMAGE_SELECTION].title = strdup (buf); } else if (strcmp (dev_name, "dumpinquiry") == 0) { dumpinquiry = SANE_TRUE; } else if (strncmp (dev_name, "speed=", 6) == 0) { sscanf (&dev_name[6], "%d", &Cam_data.speed); DBG (20, "Config file speed=%u\n", Cam_data.speed); } else if (strncmp (dev_name, "resolution=", 11) == 0) { sscanf (&dev_name[11], "%dx%d", &highres_width, &highres_height); DBG (20, "Config file resolution=%ux%u\n", highres_width, highres_height); } else if (strncmp (dev_name, "thumb_resolution=", 17) == 0) { sscanf (&dev_name[17], "%dx%d", &thumb_width, &thumb_height); DBG (20, "Config file thumb_resolution=%ux%u\n", thumb_width, thumb_height); } else if (strncmp (dev_name, "topfolder=", 10) == 0) { /* Make sure TopFolder is non-null */ if (strlen (dev_name) > 10) { TopFolder = strdup (&dev_name[10]); DBG (20, "Config file topfolder=%s\n", TopFolder); } } else if (strncmp (dev_name, "subdirs=", 8) == 0) { SubDirs = atoi (&dev_name[8]); if (SubDirs == 0) { sod[GPHOTO2_OPT_FOLDER].cap |= SANE_CAP_INACTIVE; } DBG (20, "Config file subdirs=%d\n", SubDirs); } } fclose (fp); } DBG (3, "sane_init: about to init_gphoto2\n"); if (init_gphoto2 () != SANE_STATUS_GOOD) return SANE_STATUS_INVAL; dev[0].name = strdup (Cam_data.port); DBG (3, "sane_init: about to get_info\n"); if (get_info () != SANE_STATUS_GOOD) { DBG (1, "error: could not get info\n"); close_gphoto2 (); return SANE_STATUS_INVAL; } /* load the current images array */ DBG (3, "sane_init: about to get_pictures_info\n"); get_pictures_info (); if (Cam_data.pic_taken == 0) { Cam_data.current_picture_number = 0; parms.bytes_per_line = 0; parms.pixels_per_line = 0; parms.lines = 0; } else { Cam_data.current_picture_number = 1; /* OLD: set_res (Cam_data.Pictures[Cam_data.current_picture_number - 1].low_res); */ set_res (gphoto2_opt_lowres); } if (dumpinquiry) { SANE_Int x = 0; DBG (0, "\nCamera information:\n~~~~~~~~~~~~~~~~~\n\n"); DBG (0, "Model : %s\n", abilities.model); DBG (0, "Pictures : %d\n", Cam_data.pic_taken); DBG (0, "Serial port support : %s\n", (abilities.port & GP_PORT_SERIAL) ? "yes" : "no"); DBG (0, "USB support : %s\n", (abilities.port & GP_PORT_USB) ? "yes" : "no"); if (abilities.speed[0] != 0) { DBG (0, "Transfer speeds supported :\n"); do { DBG (0, " : %i\n", abilities.speed[x]); x++; } while (abilities.speed[x] != 0); } DBG (0, "Capture choices :\n"); if (abilities.operations & GP_OPERATION_CAPTURE_IMAGE) DBG (0, " : Image\n"); if (abilities.operations & GP_OPERATION_CAPTURE_VIDEO) DBG (0, " : Video\n"); if (abilities.operations & GP_OPERATION_CAPTURE_AUDIO) DBG (0, " : Audio\n"); if (abilities.operations & GP_OPERATION_CAPTURE_PREVIEW) DBG (0, " : Preview\n"); DBG (0, "Configuration support : %s\n", abilities.operations & GP_OPERATION_CONFIG ? "yes" : "no"); DBG (0, "Delete files on camera support : %s\n", abilities. file_operations & GP_FILE_OPERATION_DELETE ? "yes" : "no"); DBG (0, "File preview (thumbnail) support : %s\n", abilities. file_operations & GP_FILE_OPERATION_PREVIEW ? "yes" : "no"); DBG (0, "File upload support : %s\n", abilities. folder_operations & GP_FOLDER_OPERATION_PUT_FILE ? "yes" : "no"); } return SANE_STATUS_GOOD; } /* * sane_exit() - Required by SANE API. */ void sane_exit (void) { close_gphoto2 (); } /* * sane_get_devices() - From SANE API */ SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only) { DBG (127, "sane_get_devices called\n"); *device_list = devlist; return SANE_STATUS_GOOD; } /* * sane_open() - From SANE API */ SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { SANE_Int i; DBG (127, "sane_open for device %s\n", devicename); if (!devicename[0]) { i = 0; } else { for (i = 0; i < NELEMS (dev); ++i) { if (strcmp (devicename, dev[i].name) == 0) { break; } } } if (i >= NELEMS (dev)) { return SANE_STATUS_INVAL; } if (is_open) { return SANE_STATUS_DEVICE_BUSY; } is_open = 1; *handle = MAGIC; DBG (4, "sane_open: pictures taken=%d\n", Cam_data.pic_taken); return SANE_STATUS_GOOD; } /* * sane_close() - From SANE API */ void sane_close (SANE_Handle handle) { DBG (127, "sane_close called\n"); if (handle == MAGIC) is_open = 0; DBG (127, "sane_close returning\n"); } /* * sane_get_option_descriptor() - From SANE API */ const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { if (handle != MAGIC || !is_open) return NULL; /* wrong device */ if (option < 0 || option >= NELEMS (sod)) return NULL; return &sod[option]; } static SANE_Int myinfo = 0; /* * sane_control_option() - From SANE API */ SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int * info) { SANE_Status status; if (option < 0 || option >= NELEMS (sod)) return SANE_STATUS_INVAL; /* Unknown option ... */ /* Need to put this DBG line after the range check on option */ DBG (127, "control_option(handle=%p,opt=%s,act=%s,val=%p,info=%p)\n", handle, sod[option].title, (action == SANE_ACTION_SET_VALUE ? "SET" : (action == SANE_ACTION_GET_VALUE ? "GET" : "SETAUTO")), value, (void *) info); if (handle != MAGIC || !is_open) return SANE_STATUS_INVAL; /* Unknown handle ... */ if (option < 0 || option >= NELEMS (sod)) return SANE_STATUS_INVAL; /* Unknown option ... */ switch (action) { case SANE_ACTION_SET_VALUE: /* Can't set disabled options */ if (!SANE_OPTION_IS_ACTIVE (sod[option].cap)) { return (SANE_STATUS_INVAL); } /* initialize info to zero - we'll OR in various values later */ if (info) *info = 0; status = sanei_constrain_value (sod + option, value, &myinfo); if (status != SANE_STATUS_GOOD) { DBG (2, "Constraint error in control_option\n"); return status; } switch (option) { case GPHOTO2_OPT_IMAGE_NUMBER: if (*(SANE_Word *) value <= Cam_data.pic_taken) Cam_data.current_picture_number = *(SANE_Word *) value; else Cam_data.current_picture_number = Cam_data.pic_taken; /* * Setting a new image number could change image size (if * we supported that - which we hope to do someday! */ myinfo |= SANE_INFO_RELOAD_PARAMS; /* get the image's resolution, unless the camera has no * pictures yet */ if (Cam_data.pic_taken != 0) { /* OLD: set_res (Cam_data. Pictures[Cam_data.current_picture_number - 1].low_res); */ set_res (gphoto2_opt_lowres); } break; case GPHOTO2_OPT_THUMBS: gphoto2_opt_thumbnails = !!*(SANE_Word *) value; /* Thumbnail forces an image size change: */ myinfo |= SANE_INFO_RELOAD_PARAMS; if (Cam_data.pic_taken != 0) { /* OLD: set_res (Cam_data. Pictures[Cam_data.current_picture_number - 1].low_res); */ set_res (gphoto2_opt_lowres); } break; case GPHOTO2_OPT_SNAP: switch (*(SANE_Bool *) value) { case SANE_TRUE: gphoto2_opt_snap = SANE_TRUE; break; case SANE_FALSE: gphoto2_opt_snap = SANE_FALSE; break; default: return SANE_STATUS_INVAL; } /* Snap forces new image size and changes image range */ myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; /* if we are snapping a new one */ if (gphoto2_opt_snap) { /* activate the resolution setting */ /* Until we figure out how to do this sod[GPHOTO2_OPT_LOWRES].cap &= ~SANE_CAP_INACTIVE; */ /* and de-activate the image number selector */ sod[GPHOTO2_OPT_IMAGE_NUMBER].cap |= SANE_CAP_INACTIVE; } else { /* deactivate the resolution setting */ sod[GPHOTO2_OPT_LOWRES].cap |= SANE_CAP_INACTIVE; /* and activate the image number selector, if there are * pictures available */ if (Cam_data.current_picture_number) { sod[GPHOTO2_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE; } } /* set params according to resolution settings */ set_res (gphoto2_opt_lowres); break; case GPHOTO2_OPT_LOWRES: gphoto2_opt_lowres = !!*(SANE_Word *) value; /* Lowres potentially changes image size */ myinfo |= SANE_INFO_RELOAD_PARAMS; /* FIXME - change the number of pictures left depending on resolution perhaps just call get_info again? */ set_res (gphoto2_opt_lowres); break; case GPHOTO2_OPT_ERASE: gphoto2_opt_erase = !!*(SANE_Word *) value; break; case GPHOTO2_OPT_AUTOINC: gphoto2_opt_autoinc = !!*(SANE_Word *) value; break; case GPHOTO2_OPT_FOLDER: DBG (1, "FIXME set folder not implemented yet\n"); break; case GPHOTO2_OPT_DEFAULT: gphoto2_opt_thumbnails = 0; gphoto2_opt_snap = 0; /* deactivate the resolution setting */ sod[GPHOTO2_OPT_LOWRES].cap |= SANE_CAP_INACTIVE; /* and activate the image number selector */ sod[GPHOTO2_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE; myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; DBG (1, "FIXME: Set all defaults here!\n"); break; case GPHOTO2_OPT_INIT_GPHOTO2: if (init_gphoto2 () != SANE_STATUS_GOOD) { return SANE_STATUS_INVAL; } if (get_info () != SANE_STATUS_GOOD) { DBG (1, "error: could not get info\n"); close_gphoto2 (); return SANE_STATUS_INVAL; } /* load the current images array */ get_pictures_info (); myinfo |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; break; default: return SANE_STATUS_INVAL; } break; case SANE_ACTION_GET_VALUE: /* Can't return status for disabled options */ if (!SANE_OPTION_IS_ACTIVE (sod[option].cap)) { return (SANE_STATUS_INVAL); } switch (option) { case 0: *(SANE_Word *) value = NELEMS (sod); break; case GPHOTO2_OPT_IMAGE_NUMBER: *(SANE_Word *) value = Cam_data.current_picture_number; break; case GPHOTO2_OPT_THUMBS: *(SANE_Word *) value = gphoto2_opt_thumbnails; break; case GPHOTO2_OPT_SNAP: *(SANE_Word *) value = gphoto2_opt_snap; break; case GPHOTO2_OPT_LOWRES: *(SANE_Word *) value = gphoto2_opt_lowres; break; case GPHOTO2_OPT_ERASE: *(SANE_Word *) value = gphoto2_opt_erase; break; case GPHOTO2_OPT_AUTOINC: *(SANE_Word *) value = gphoto2_opt_autoinc; break; case GPHOTO2_OPT_FOLDER: if (folder_list == NULL) { return SANE_STATUS_INVAL; } strncpy ((char *) value, (const char *) folder_list[current_folder], 256); break; default: return SANE_STATUS_INVAL; } break; case SANE_ACTION_SET_AUTO: switch (option) { default: return SANE_STATUS_UNSUPPORTED; /* We are DUMB */ } } if (info && action == SANE_ACTION_SET_VALUE) { *info = myinfo; myinfo = 0; } return SANE_STATUS_GOOD; } /* * sane_get_parameters() - From SANE API */ SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { SANE_Int rc = SANE_STATUS_GOOD; DBG (127, "sane_get_params called, wid=%d,height=%d\n", parms.pixels_per_line, parms.lines); if (handle != MAGIC || !is_open) rc = SANE_STATUS_INVAL; /* Unknown handle ... */ parms.last_frame = SANE_TRUE; /* Have no idea what this does */ *params = parms; DBG (127, "sane_get_params return %d\n", rc); return rc; } typedef struct { struct jpeg_source_mgr pub; JOCTET *buffer; } my_source_mgr; typedef my_source_mgr *my_src_ptr; METHODDEF (void) jpeg_init_source (j_decompress_ptr __sane_unused__ cinfo) { /* nothing to do */ } METHODDEF (boolean) jpeg_fill_input_buffer (j_decompress_ptr cinfo) { int n; my_src_ptr src = (my_src_ptr) cinfo->src; if (data_file_current_index + 512 > data_file_total_size) { n = data_file_total_size - data_file_current_index; } else { n = 512; } memcpy (src->buffer, data_ptr + data_file_current_index, n); data_file_current_index += n; src->pub.next_input_byte = src->buffer; src->pub.bytes_in_buffer = n; return TRUE; } METHODDEF (void) jpeg_skip_input_data (j_decompress_ptr cinfo, long num_bytes) { my_src_ptr src = (my_src_ptr) cinfo->src; if (num_bytes > 0) { while (num_bytes > (long) src->pub.bytes_in_buffer) { num_bytes -= (long) src->pub.bytes_in_buffer; (void) jpeg_fill_input_buffer (cinfo); } } src->pub.next_input_byte += (size_t) num_bytes; src->pub.bytes_in_buffer -= (size_t) num_bytes; } METHODDEF (void) jpeg_term_source (j_decompress_ptr __sane_unused__ cinfo) { /* no work necessary here */ } /* * sane_start() - From SANE API */ SANE_Status sane_start (SANE_Handle handle) { SANE_String_Const filename, mime_type; DBG (127, "sane_start called\n"); if (handle != MAGIC || !is_open || (Cam_data.current_picture_number == 0 && gphoto2_opt_snap == SANE_FALSE)) return SANE_STATUS_INVAL; /* Unknown handle ... */ if (Cam_data.scanning) return SANE_STATUS_EOF; /* * This shouldn't normally happen, but we allow it as a special case * when batch/autoinc are in effect. The first illegal picture number * terminates the scan */ if (Cam_data.current_picture_number > Cam_data.pic_taken) { return SANE_STATUS_INVAL; } if (gphoto2_opt_snap) { /* * Don't allow picture unless there is room in the * camera. */ if (Cam_data.pic_left == 0) { DBG (3, "No room to store new picture\n"); return SANE_STATUS_INVAL; } if (snap_pic () == SANE_STATUS_INVAL) { DBG (1, "Failed to snap new picture\n"); return SANE_STATUS_INVAL; } } DBG (4, "sane_start: about to get file\n"); CHECK_RET (gp_file_new (&data_file)); if (SubDirs) { sprintf (cmdbuf, "%s/%s", (char *) TopFolder, (const char *) folder_list[current_folder]); } else { strcpy (cmdbuf, TopFolder); } CHECK_RET (gp_list_get_name (dir_list, Cam_data.current_picture_number - 1, &filename)); CHECK_RET (gp_camera_file_get (camera, cmdbuf, filename, gphoto2_opt_thumbnails ? GP_FILE_TYPE_PREVIEW : GP_FILE_TYPE_NORMAL, data_file, NULL)); CHECK_RET (gp_file_get_mime_type (data_file, &mime_type)); if (strcmp (GP_MIME_JPEG, mime_type) != 0) { DBG (0, "FIXME - Only jpeg files currently supported, can't do %s for file %s/%s\n", mime_type, cmdbuf, filename); return SANE_STATUS_INVAL; } CHECK_RET (gp_file_get_data_and_size (data_file, (const char **)&data_ptr, &data_file_total_size)); if ( converter_init (handle) != SANE_STATUS_GOOD ) return SANE_STATUS_INVAL; /* Check if a linebuffer has been allocated. If we had one * previously, free it up and allocate one for (possibly) new * size. parms.bytes_per_line is set by converter_init() */ if (linebuffer == NULL) { linebuffer = malloc (parms.bytes_per_line); } else { free (linebuffer); linebuffer = malloc (parms.bytes_per_line); } if (linebuffer == NULL) { return SANE_STATUS_INVAL; } Cam_data.scanning = SANE_TRUE; /* don't overlap scan requests */ return SANE_STATUS_GOOD; } /* * sane_read() - From SANE API */ SANE_Status sane_read (SANE_Handle __sane_unused__ handle, SANE_Byte * data, SANE_Int max_length, SANE_Int * length) { if (Cam_data.scanning == SANE_FALSE) { return SANE_STATUS_INVAL; } /* If there is anything in the buffer, satisfy the read from there */ if (linebuffer_size && linebuffer_index < linebuffer_size) { *length = linebuffer_size - linebuffer_index; if (*length > max_length) { *length = max_length; } memcpy (data, linebuffer + linebuffer_index, *length); linebuffer_index += *length; return SANE_STATUS_GOOD; } if (converter_scan_complete ()) { SANE_Status retval; *length = 0; retval = converter_do_scan_complete_cleanup (); if (retval != SANE_STATUS_GOOD) { return retval; } } *length = converter_fill_buffer (); linebuffer_size = *length; linebuffer_index = 0; if (*length > max_length) { *length = max_length; } memcpy (data, linebuffer + linebuffer_index, *length); linebuffer_index += *length; return SANE_STATUS_GOOD; } /* * sane_cancel() - From SANE API */ void sane_cancel (SANE_Handle __sane_unused__ handle) { if (Cam_data.scanning) { Cam_data.scanning = SANE_FALSE; /* done with scan */ } else DBG (4, "sane_cancel: not scanning - nothing to do\n"); } /* * sane_set_io_mode() - From SANE API */ SANE_Status sane_set_io_mode (SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking) { /* sane_set_io_mode() is only valid during a scan */ if (Cam_data.scanning) { if (non_blocking == SANE_FALSE) { return SANE_STATUS_GOOD; } else { return SANE_STATUS_UNSUPPORTED; } } else { /* We aren't currently scanning */ return SANE_STATUS_INVAL; } } /* * sane_get_select_fd() - From SANE API */ SANE_Status sane_get_select_fd (SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ * fd) { return SANE_STATUS_UNSUPPORTED; } /* * get_pictures_info - load information about all pictures currently in * camera: Mainly the mapping of picture number * to picture name. We'ld like to get other * information such as image size, but the API * doesn't provide any support for that. */ static PictureInfo * get_pictures_info (void) { SANE_Char f[] = "get_pictures_info"; SANE_Char path[256]; SANE_Int num_pictures; SANE_Int p; PictureInfo *pics; if (Cam_data.Pictures) { free (Cam_data.Pictures); Cam_data.Pictures = NULL; } strcpy (path, TopFolder); if (SubDirs) { if (folder_list[current_folder] != NULL) { strcat (path, "/"); strcat (path, (const char *) folder_list[current_folder]); } } num_pictures = read_dir (path, 1); Cam_data.pic_taken = num_pictures; if (num_pictures > 0) { sod[GPHOTO2_OPT_IMAGE_NUMBER].cap &= ~SANE_CAP_INACTIVE; image_range.min = 1; image_range.max = num_pictures; } if ((pics = (PictureInfo *) malloc (Cam_data.pic_taken * sizeof (PictureInfo))) == NULL) { DBG (1, "%s: error: allocate memory for pictures array\n", f); return NULL; } for (p = 0; p < Cam_data.pic_taken; p++) { if (get_picture_info (pics + p, p) == -1) { free (pics); return NULL; } } Cam_data.Pictures = pics; return pics; } /* * get_picture_info() - get info about picture p. Currently we have no * way to get information about a picture beyond it's name. */ static SANE_Int get_picture_info (PictureInfo * pic, SANE_Int p) { SANE_Char f[] = "get_picture_info"; const char *name; DBG (4, "%s: info for pic #%d\n", f, p); gp_list_get_name (dir_list, p, &name); DBG (4, "Name is %s\n", name); read_info (name); pic->low_res = SANE_FALSE; return 0; } /* * snap_pic - take a picture (and call get_pictures_info to re-create * the directory related data structures) */ static SANE_Status snap_pic (void) { SANE_Char f[] = "snap_pic"; CameraFilePath path; /* make sure camera is set to our settings state */ if (change_res (gphoto2_opt_lowres) == -1) { DBG (1, "%s: Failed to set resolution\n", f); return SANE_STATUS_INVAL; } /* * This is needed when the camera has no files and the first picture * is taken. I guess it's because a folder needs to be created and * the filesystem doesn't know about it. */ if (Cam_data.pic_taken == 0) { gp_filesystem_reset (camera->fs); } CHECK_RET (gp_camera_capture (camera, GP_CAPTURE_IMAGE, &path, NULL)); /* Can't just increment picture count, because if the camera has * zero pictures we may not know the folder name. Start over * with get_info and get_pictures_info. (We didn't have the call * to init_gphoto2() here before, but that was causing us to not * see the new image - need to use a biggger hammer to get it to * re-read the camera directory */ if (init_gphoto2 () != SANE_STATUS_GOOD) { return SANE_STATUS_INVAL; } if (get_info () != SANE_STATUS_GOOD) { DBG (1, "error: could not get info\n"); close_gphoto2 (); return SANE_STATUS_INVAL; } if (get_pictures_info () == NULL) { DBG (1, "%s: Failed to get new picture info\n", f); /* FIXME - I guess we should try to erase the image here */ return SANE_STATUS_INVAL; } sod[GPHOTO2_OPT_IMAGE_NUMBER].cap |= SANE_CAP_INACTIVE; Cam_data.current_picture_number = Cam_data.pic_taken; myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; } /* * read_dir - read a list of file names from the specified directory * and create a linked list of file name entries in * alphabetical order. The first entry in the list will * be "picture #1", etc. */ static SANE_Int read_dir (SANE_String dir, SANE_Bool read_files) { SANE_Int retval = 0; SANE_Char f[] = "read_dir"; /* Free up current list */ if (dir_list != NULL) { if (gp_list_free (dir_list) < 0) { DBG (0, "%s: error: gp_list_free failed\n", f); } dir_list = NULL; } if (gp_list_new (&dir_list) < 0) { DBG (0, "%s: error: gp_list_new failed\n", f); } if (read_files) { CHECK_RET (gp_camera_folder_list_files (camera, dir, dir_list, NULL)); } else { CHECK_RET (gp_camera_folder_list_folders (camera, dir, dir_list, NULL)); } retval = gp_list_count (dir_list); return retval; } /* * read_info - read the info block from camera for the specified file * NOT YET SUPPORTED - If it were we could use it to do things * like update the image size parameters displayed by the GUI */ static SANE_Int read_info (SANE_String_Const fname) { SANE_Char path[256]; strcpy (path, "\\DCIM\\"); strcat (path, (const char *) folder_list[current_folder]); strcat (path, "\\"); strcat (path, fname); return 0; } /* * set_res - set picture size depending on resolution settings */ static void set_res (SANE_Int __sane_unused__ lowres) { if (gphoto2_opt_thumbnails) { parms.bytes_per_line = THUMB_WIDTH * 3; parms.pixels_per_line = THUMB_WIDTH; parms.lines = THUMB_HEIGHT; } else { parms.bytes_per_line = HIGHRES_WIDTH * 3; parms.pixels_per_line = HIGHRES_WIDTH; parms.lines = HIGHRES_HEIGHT; } } /* * converter_do_scan_complete_cleanup - do everything that needs to be * once a "scan" has been completed: Unref the file, Erase the image, * and increment image number to point to next picture. */ static SANE_Status converter_do_scan_complete_cleanup (void) { CameraList *tmp_list; SANE_Int i; SANE_String_Const filename; gp_file_unref (data_file); if (gphoto2_opt_erase) { DBG (127, "sane_read bp%d, erase image\n", __LINE__); if (erase () == -1) { DBG (1, "Failed to erase memory\n"); return SANE_STATUS_INVAL; } if (SubDirs) { sprintf (cmdbuf, "%s/%s", (char *) TopFolder, (const char *) folder_list[current_folder]); } else { strcpy (cmdbuf, TopFolder); } CHECK_RET (gp_list_get_name (dir_list, Cam_data.current_picture_number - 1, &filename)); Cam_data.pic_taken--; Cam_data.pic_left++; if (Cam_data.current_picture_number > Cam_data.pic_taken) { Cam_data.current_picture_number = Cam_data.pic_taken; } image_range.max--; if (image_range.max == 0) { sod[GPHOTO2_OPT_IMAGE_NUMBER].cap |= SANE_CAP_INACTIVE; } myinfo |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; /* Too bad we don't have an API function for deleting a * list item. Instead, we copy all the entries in the * current list, skipping over the deleted entry, and then * replace the current list with the new list. */ gp_list_new (&tmp_list); for (i = 0; i < gp_list_count (dir_list); i++) { SANE_String_Const tfilename; CHECK_RET (gp_list_get_name (dir_list, i, &tfilename)); /* If not the one to delete, copy to the new list */ if (strcmp (tfilename, filename) != 0) { CHECK_RET (gp_list_append (tmp_list, tfilename, NULL)); } } gp_list_free (dir_list); dir_list = tmp_list; } if (gphoto2_opt_autoinc) { if (Cam_data.current_picture_number <= Cam_data.pic_taken) { Cam_data.current_picture_number++; myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; /* get the image's resolution */ /* OLD: set_res (Cam_data.Pictures[Cam_data.current_picture_number - 1]. low_res); */ set_res (gphoto2_opt_lowres); } DBG (4, "Increment count to %d (total %d)\n", Cam_data.current_picture_number, Cam_data.pic_taken); } return SANE_STATUS_EOF; } /* * converter_fill_buffer - Fill line buffer with next input line from image. * Currently assumes jpeg, but this is where we would put the switch * to handle other image types. */ static SANE_Int converter_fill_buffer (void) { /* * FIXME: Current implementation reads one scan line at a time. Part * of the reason for this is in the original code is to give the frontend * a chance to update * the progress marker periodically. Since the gphoto2 * driver sucks in the whole image before decoding it, perhaps we could * come up with a simpler implementation. */ SANE_Int lines = 1; (void) jpeg_read_scanlines (&cinfo, dest_mgr->buffer, lines); (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, lines, (char *) linebuffer); return cinfo.output_width * cinfo.output_components * lines; } /* * converter_scan_complete - Check if all the data for the image has been read. * Currently assumes jpeg, but this is where we would put the * switch to handle other image types. */ static SANE_Bool converter_scan_complete (void) { if (cinfo.output_scanline >= cinfo.output_height) { return SANE_TRUE; } else { return SANE_FALSE; } } /* * converter_init - Initialize image conversion data. * Currently assumes jpeg, but this is where we would put the * switch to handle other image types. */ static SANE_Status converter_init (SANE_Handle handle) { struct jpeg_error_mgr jerr; my_src_ptr src; data_file_current_index = 0; /* Basic check to see if this is really a jpeg file */ if ( data_ptr[0] != 0xff || data_ptr[1] != 0xd8 ) { sane_cancel(handle); exit(1); return SANE_STATUS_INVAL; } cinfo.err = jpeg_std_error (&jerr); jpeg_create_decompress (&cinfo); cinfo.src = (struct jpeg_source_mgr *) (*cinfo.mem-> alloc_small) ((j_common_ptr) & cinfo, JPOOL_PERMANENT, sizeof (my_source_mgr)); src = (my_src_ptr) cinfo.src; src->buffer = (JOCTET *) (*cinfo.mem->alloc_small) ((j_common_ptr) & cinfo, JPOOL_PERMANENT, 1024 * sizeof (JOCTET)); src->pub.init_source = jpeg_init_source; src->pub.fill_input_buffer = jpeg_fill_input_buffer; src->pub.skip_input_data = jpeg_skip_input_data; src->pub.resync_to_restart = jpeg_resync_to_restart; /* default */ src->pub.term_source = jpeg_term_source; src->pub.bytes_in_buffer = 0; src->pub.next_input_byte = NULL; (void) jpeg_read_header (&cinfo, TRUE); dest_mgr = sanei_jpeg_jinit_write_ppm (&cinfo); (void) jpeg_start_decompress (&cinfo); parms.bytes_per_line = cinfo.output_width * 3; /* 3 colors */ parms.pixels_per_line = cinfo.output_width; parms.lines = cinfo.output_height; linebuffer_size = 0; linebuffer_index = 0; return(SANE_STATUS_GOOD); } backends-1.3.0/backend/gphoto2.conf.in000066400000000000000000000021641456256263500175330ustar00rootroot00000000000000# Interface port where the camera is connected # This should be one of the values returned by "gphoto2 --list-ports", # such # as serial:/dev/ttyS6 or usb: port=serial:/dev/ttyd1 # Port speed. This should be one of the values returned by # "gphoto2 --abilities" speed=115200 # Name of camera. This should be one of the values returned by # "gphoto2 --list-cameras" camera=Kodak DC240 # Prints some extra information during the init phase. dumpinquiry # The resolution should be the maximum resolution supported by the # camera. It's not really used for much, since the actual size will be # reported by the camera when the download starts. But it may be useful # for the frontend to have a clue prior to the download. (e.g. it # may want to create an image window, or report the maximum file size. # Width x Height. resolution=1280x960 # Thumbnail resolutions - ditto thumb_resolution=160x120 # top-level (fixed) folder directory in camera. Backend assumes # that there is one variable directory under this (e.g. 100DC240) # which will be read from the camera, and all the images in the # camera are under that. topfolder=/DCIM backends-1.3.0/backend/gphoto2.h000066400000000000000000000114461456256263500164330ustar00rootroot00000000000000/*************************************************************************** * SANE - Scanner Access Now Easy. gphoto2.h 03/12/01 - Peter Fales Based on the dc210 driver, (C) 1998 Brian J. Murrell (which is based on dc25 driver (C) 1998 by Peter Fales) This file (C) 2001 by Peter Fales 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, see . 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 the Kodak DC-240 digital camera. THIS IS EXTREMELY ALPHA CODE! USE AT YOUR OWN RISK!! (feedback to: gphoto2-devel@fales-lorenz.net) This backend is based somewhat on the dc25 backend included in this package by Peter Fales, and the dc210 backend by Brian J. Murrell ***************************************************************************/ #include #include #include #include #include #include #ifndef TRUE #define TRUE (1==1) #endif #ifndef FALSE #define FALSE (!TRUE) #endif #ifndef NULL #define NULL 0L #endif typedef struct picture_info { int low_res; int size; } PictureInfo; typedef struct GPHOTO2_s { SANE_String port; /* the port name it's on */ SANE_Int speed; /* current port speed */ SANE_String camera_name; SANE_Bool scanning; /* currently scanning an image? */ SANE_Byte model; SANE_Byte ver_major; SANE_Byte ver_minor; SANE_Int pic_taken; SANE_Int pic_left; struct { unsigned int low_res:1; unsigned int low_batt:1; } flags; PictureInfo *Pictures; /* array of pictures */ SANE_Int current_picture_number; /* picture being operated on */ } GPHOTO2; typedef struct gphoto2_info_s { SANE_Byte model; SANE_Byte ver_major; SANE_Byte ver_minor; SANE_Int pic_taken; SANE_Int pic_left; struct { SANE_Int low_res:1; SANE_Int low_batt:1; } flags; } Gphoto2Info, *Gphoto2InfoPtr; static SANE_Int get_info (void); #define HIGH_RES 0 #define LOW_RES 1 #define HIGHRES_WIDTH highres_width #define HIGHRES_HEIGHT highres_height #define LOWRES_WIDTH lowres_width #define LOWRES_HEIGHT lowres_height #define THUMB_WIDTH thumb_width #define THUMB_HEIGHT thumb_height /* * External definitions */ extern char *__progname; /* Defined in /usr/lib/crt0.o */ struct cam_dirent { SANE_Char name[11]; SANE_Byte attr; SANE_Byte create_time[2]; SANE_Byte creat_date[2]; long size; }; struct cam_dirlist { SANE_Char name[48]; struct cam_dirlist *next; }; #include FILE *sanei_config_open (const char *filename); static SANE_Int init_gphoto2 (void); static void close_gphoto2 (void); static PictureInfo *get_pictures_info (void); static SANE_Int get_picture_info (PictureInfo * pic, SANE_Int p); static SANE_Status snap_pic (void); char *sanei_config_read (char *str, int n, FILE * stream); static SANE_Int read_dir (SANE_String dir, SANE_Bool read_files); static void set_res (SANE_Int lowres); static SANE_Int read_info (SANE_String_Const fname); static SANE_Status converter_do_scan_complete_cleanup (void); static SANE_Int converter_fill_buffer (void); static SANE_Bool converter_scan_complete (void); static SANE_Status converter_init (SANE_Handle handle); backends-1.3.0/backend/gt68xx.c000066400000000000000000002226361456256263500162210ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2002 Sergey Vlasov Copyright (C) 2002 - 2007 Henning Geinitz Copyright (C) 2009 Stéphane Voltz for sheetfed calibration code. 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, see . 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. */ /* * SANE backend for Grandtech GT-6801 and GT-6816 based scanners */ #include "../include/sane/config.h" #define BUILD 84 #define MAX_DEBUG #define WARMUP_TIME 60 #define CALIBRATION_HEIGHT 2.5 #define SHORT_TIMEOUT (1 * 1000) #define LONG_TIMEOUT (30 * 1000) /* Use a reader process if possible (usually faster) */ #if defined (HAVE_SYS_SHM_H) && (!defined (USE_PTHREAD)) && (!defined (HAVE_OS2_H)) #define USE_FORK #define SHM_BUFFERS 10 #endif #define TUNE_CALIBRATOR /* Send coarse white or black calibration to stdout */ #if 0 #define SAVE_WHITE_CALIBRATION #endif #if 0 #define SAVE_BLACK_CALIBRATION #endif /* Debug calibration, print total brightness of the scanned image */ #if 0 #define DEBUG_BRIGHTNESS #endif /* Debug calibration, print black mark values */ #if 0 #define DEBUG_BLACK #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../include/_stdint.h" #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #define BACKEND_NAME gt68xx #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_config.h" #ifndef SANE_I18N #define SANE_I18N(text) text #endif #include "gt68xx.h" #include "gt68xx_high.c" #include "gt68xx_devices.c" static SANE_Int num_devices = 0; static GT68xx_Device *first_dev = 0; static GT68xx_Scanner *first_handle = 0; static const SANE_Device **devlist = 0; /* Array of newly attached devices */ static GT68xx_Device **new_dev = 0; /* Length of new_dev array */ static SANE_Int new_dev_len = 0; /* Number of entries allocated for new_dev */ static SANE_Int new_dev_alloced = 0; /* Is this computer little-endian ?*/ SANE_Bool little_endian; SANE_Bool debug_options = SANE_FALSE; static SANE_String_Const mode_list[] = { SANE_VALUE_SCAN_MODE_COLOR, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_LINEART, 0 }; static SANE_String_Const gray_mode_list[] = { GT68XX_COLOR_RED, GT68XX_COLOR_GREEN, GT68XX_COLOR_BLUE, 0 }; static SANE_String_Const source_list[] = { SANE_I18N ("Flatbed"), SANE_I18N ("Transparency Adapter"), 0 }; static SANE_Range x_range = { SANE_FIX (0.0), /* minimum */ SANE_FIX (216.0), /* maximum */ SANE_FIX (0.0) /* quantization */ }; static SANE_Range y_range = { SANE_FIX (0.0), /* minimum */ SANE_FIX (299.0), /* maximum */ SANE_FIX (0.0) /* quantization */ }; static SANE_Range gamma_range = { SANE_FIX (0.01), /* minimum */ SANE_FIX (5.0), /* maximum */ SANE_FIX (0.01) /* quantization */ }; static const SANE_Range u8_range = { 0, /* minimum */ 255, /* maximum */ 0 /* quantization */ }; /* Test if this machine is little endian (from coolscan.c) */ static SANE_Bool calc_little_endian (void) { SANE_Int testvalue = 255; uint8_t *firstbyte = (uint8_t *) & testvalue; if (*firstbyte == 255) return SANE_TRUE; return SANE_FALSE; } 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; } static SANE_Status get_afe_values (SANE_String_Const cp, GT68xx_AFE_Parameters * afe) { SANE_Char *word, *end; int i; for (i = 0; i < 6; i++) { cp = sanei_config_get_string (cp, &word); if (word && *word) { long int long_value; errno = 0; long_value = strtol (word, &end, 0); if (end == word) { DBG (5, "get_afe_values: can't parse %d. parameter `%s'\n", i + 1, word); free (word); word = 0; return SANE_STATUS_INVAL; } else if (errno) { DBG (5, "get_afe_values: can't parse %d. parameter `%s' " "(%s)\n", i + 1, word, strerror (errno)); free (word); word = 0; return SANE_STATUS_INVAL; } else if (long_value < 0) { DBG (5, "get_afe_values: %d. parameter < 0 (%d)\n", i + 1, (int) long_value); free (word); word = 0; return SANE_STATUS_INVAL; } else if (long_value > 0x3f) { DBG (5, "get_afe_values: %d. parameter > 0x3f (%d)\n", i + 1, (int) long_value); free (word); word = 0; return SANE_STATUS_INVAL; } else { DBG (5, "get_afe_values: %d. parameter set to 0x%02x\n", i + 1, (int) long_value); switch (i) { case 0: afe->r_offset = (SANE_Byte) long_value; break; case 1: afe->r_pga = (SANE_Byte) long_value; break; case 2: afe->g_offset = (SANE_Byte) long_value; break; case 3: afe->g_pga = (SANE_Byte) long_value; break; case 4: afe->b_offset = (SANE_Byte) long_value; break; case 5: afe->b_pga = (SANE_Byte) long_value; break; } free (word); word = 0; } } else { DBG (5, "get_afe_values: option `afe' needs 6 parameters\n"); return SANE_STATUS_INVAL; } } return SANE_STATUS_GOOD; } static SANE_Status setup_scan_request (GT68xx_Scanner * s, GT68xx_Scan_Request * scan_request) { if (s->dev->model->flags & GT68XX_FLAG_MIRROR_X) scan_request->x0 = s->opt[OPT_TL_X].constraint.range->max - s->val[OPT_BR_X].w; else scan_request->x0 = s->val[OPT_TL_X].w; scan_request->y0 = s->val[OPT_TL_Y].w; scan_request->xs = s->val[OPT_BR_X].w - s->val[OPT_TL_X].w; scan_request->ys = s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w; if (s->val[OPT_FULL_SCAN].w == SANE_TRUE) { scan_request->x0 -= s->dev->model->x_offset; scan_request->y0 -= (s->dev->model->y_offset); scan_request->xs += s->dev->model->x_offset; scan_request->ys += s->dev->model->y_offset; } scan_request->xdpi = s->val[OPT_RESOLUTION].w; if (scan_request->xdpi > s->dev->model->optical_xdpi) scan_request->xdpi = s->dev->model->optical_xdpi; scan_request->ydpi = s->val[OPT_RESOLUTION].w; if (IS_ACTIVE (OPT_BIT_DEPTH) && !s->val[OPT_PREVIEW].w) scan_request->depth = s->val[OPT_BIT_DEPTH].w; else scan_request->depth = 8; if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0) scan_request->color = SANE_TRUE; else scan_request->color = SANE_FALSE; if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) == 0) { SANE_Int xs = SANE_UNFIX (scan_request->xs) * scan_request->xdpi / MM_PER_INCH + 0.5; if (xs % 8) { scan_request->xs = SANE_FIX ((xs - (xs % 8)) * MM_PER_INCH / scan_request->xdpi); DBG (5, "setup_scan_request: lineart mode, %d pixels %% 8 = %d\n", xs, xs % 8); } } scan_request->calculate = SANE_FALSE; scan_request->lamp = SANE_TRUE; scan_request->mbs = SANE_FALSE; if (strcmp (s->val[OPT_SOURCE].s, "Transparency Adapter") == 0) scan_request->use_ta = SANE_TRUE; else scan_request->use_ta = SANE_FALSE; return SANE_STATUS_GOOD; } static SANE_Status calc_parameters (GT68xx_Scanner * s) { SANE_String val; SANE_Status status = SANE_STATUS_GOOD; GT68xx_Scan_Request scan_request; GT68xx_Scan_Parameters scan_params; DBG (5, "calc_parameters: start\n"); val = s->val[OPT_MODE].s; s->params.last_frame = SANE_TRUE; if (strcmp (val, SANE_VALUE_SCAN_MODE_GRAY) == 0 || strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) == 0) s->params.format = SANE_FRAME_GRAY; else /* Color */ s->params.format = SANE_FRAME_RGB; setup_scan_request (s, &scan_request); scan_request.calculate = SANE_TRUE; status = gt68xx_device_setup_scan (s->dev, &scan_request, SA_SCAN, &scan_params); if (status != SANE_STATUS_GOOD) { DBG (1, "calc_parameters: gt68xx_device_setup_scan returned: %s\n", sane_strstatus (status)); return status; } if (strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) == 0) s->params.depth = 1; else s->params.depth = scan_params.depth; s->params.lines = scan_params.pixel_ys; s->params.pixels_per_line = scan_params.pixel_xs; /* Inflate X if necessary */ if (s->val[OPT_RESOLUTION].w > s->dev->model->optical_xdpi) s->params.pixels_per_line *= (s->val[OPT_RESOLUTION].w / s->dev->model->optical_xdpi); s->params.bytes_per_line = s->params.pixels_per_line; if (s->params.depth > 8) { s->params.depth = 16; s->params.bytes_per_line *= 2; } else if (s->params.depth == 1) s->params.bytes_per_line /= 8; if (s->params.format == SANE_FRAME_RGB) s->params.bytes_per_line *= 3; DBG (5, "calc_parameters: exit\n"); return status; } static SANE_Status create_bpp_list (GT68xx_Scanner * s, SANE_Int * bpp) { int count; for (count = 0; bpp[count] != 0; count++) ; s->bpp_list[0] = count; for (count = 0; bpp[count] != 0; count++) { s->bpp_list[s->bpp_list[0] - count] = bpp[count]; } return SANE_STATUS_GOOD; } static SANE_Status init_options (GT68xx_Scanner * s) { SANE_Int option, count; SANE_Status status; SANE_Word *dpi_list; GT68xx_Model *model = s->dev->model; SANE_Bool has_ta = SANE_FALSE; DBG (5, "init_options: start\n"); memset (s->opt, 0, sizeof (s->opt)); memset (s->val, 0, sizeof (s->val)); for (option = 0; option < NUM_OPTIONS; ++option) { s->opt[option].size = sizeof (SANE_Word); s->opt[option].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } 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].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* "Mode" group: */ s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode"); s->opt[OPT_MODE_GROUP].desc = ""; s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MODE_GROUP].size = 0; s->opt[OPT_MODE_GROUP].cap = 0; s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* scan mode */ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; 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].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_MODE].size = max_string_size (mode_list); s->opt[OPT_MODE].constraint.string_list = mode_list; s->val[OPT_MODE].s = strdup (SANE_VALUE_SCAN_MODE_GRAY); /* scan mode */ s->opt[OPT_GRAY_MODE_COLOR].name = "gray-mode-color"; s->opt[OPT_GRAY_MODE_COLOR].title = SANE_I18N ("Gray mode color"); s->opt[OPT_GRAY_MODE_COLOR].desc = SANE_I18N ("Selects which scan color is used " "gray mode (default: green)."); s->opt[OPT_GRAY_MODE_COLOR].type = SANE_TYPE_STRING; s->opt[OPT_GRAY_MODE_COLOR].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_GRAY_MODE_COLOR].size = max_string_size (gray_mode_list); s->opt[OPT_GRAY_MODE_COLOR].constraint.string_list = gray_mode_list; s->val[OPT_GRAY_MODE_COLOR].s = strdup (GT68XX_COLOR_GREEN); /* scan source */ s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE; s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE; s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE; s->opt[OPT_SOURCE].type = SANE_TYPE_STRING; s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_SOURCE].size = max_string_size (source_list); s->opt[OPT_SOURCE].constraint.string_list = source_list; s->val[OPT_SOURCE].s = strdup ("Flatbed"); status = gt68xx_device_get_ta_status (s->dev, &has_ta); if (status != SANE_STATUS_GOOD || !has_ta) DISABLE (OPT_SOURCE); /* preview */ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; s->opt[OPT_PREVIEW].unit = SANE_UNIT_NONE; s->opt[OPT_PREVIEW].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_PREVIEW].w = SANE_FALSE; /* lamp on */ s->opt[OPT_LAMP_OFF_AT_EXIT].name = SANE_NAME_LAMP_OFF_AT_EXIT; s->opt[OPT_LAMP_OFF_AT_EXIT].title = SANE_TITLE_LAMP_OFF_AT_EXIT; s->opt[OPT_LAMP_OFF_AT_EXIT].desc = SANE_DESC_LAMP_OFF_AT_EXIT; s->opt[OPT_LAMP_OFF_AT_EXIT].type = SANE_TYPE_BOOL; s->opt[OPT_LAMP_OFF_AT_EXIT].unit = SANE_UNIT_NONE; s->opt[OPT_LAMP_OFF_AT_EXIT].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_LAMP_OFF_AT_EXIT].w = SANE_TRUE; if (s->dev->model->is_cis && !(s->dev->model->flags & GT68XX_FLAG_CIS_LAMP)) DISABLE (OPT_LAMP_OFF_AT_EXIT); /* bit depth */ s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT; s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_BIT_DEPTH].size = sizeof (SANE_Word); s->opt[OPT_BIT_DEPTH].constraint.word_list = 0; s->opt[OPT_BIT_DEPTH].constraint.word_list = s->bpp_list; RIE (create_bpp_list (s, s->dev->model->bpp_gray_values)); s->val[OPT_BIT_DEPTH].w = 8; if (s->opt[OPT_BIT_DEPTH].constraint.word_list[0] < 2) DISABLE (OPT_BIT_DEPTH); /* resolution */ for (count = 0; model->ydpi_values[count] != 0; count++) ; dpi_list = malloc ((count + 1) * sizeof (SANE_Word)); if (!dpi_list) return SANE_STATUS_NO_MEM; dpi_list[0] = count; for (count = 0; model->ydpi_values[count] != 0; count++) dpi_list[dpi_list[0] - count] = model->ydpi_values[count]; s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_RESOLUTION].constraint.word_list = dpi_list; s->val[OPT_RESOLUTION].w = 300; /* backtrack */ s->opt[OPT_BACKTRACK].name = SANE_NAME_BACKTRACK; s->opt[OPT_BACKTRACK].title = SANE_TITLE_BACKTRACK; s->opt[OPT_BACKTRACK].desc = SANE_DESC_BACKTRACK; s->opt[OPT_BACKTRACK].type = SANE_TYPE_BOOL; s->val[OPT_BACKTRACK].w = SANE_FALSE; /* "Debug" group: */ s->opt[OPT_DEBUG_GROUP].title = SANE_I18N ("Debugging Options"); s->opt[OPT_DEBUG_GROUP].desc = ""; s->opt[OPT_DEBUG_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_DEBUG_GROUP].size = 0; s->opt[OPT_DEBUG_GROUP].cap = 0; s->opt[OPT_DEBUG_GROUP].constraint_type = SANE_CONSTRAINT_NONE; if (!debug_options) DISABLE (OPT_DEBUG_GROUP); /* auto warmup */ s->opt[OPT_AUTO_WARMUP].name = "auto-warmup"; s->opt[OPT_AUTO_WARMUP].title = SANE_I18N ("Automatic warmup"); s->opt[OPT_AUTO_WARMUP].desc = SANE_I18N ("Warm-up until the lamp's brightness is constant " "instead of insisting on 60 seconds warm-up time."); s->opt[OPT_AUTO_WARMUP].type = SANE_TYPE_BOOL; s->opt[OPT_AUTO_WARMUP].unit = SANE_UNIT_NONE; s->opt[OPT_AUTO_WARMUP].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_AUTO_WARMUP].w = SANE_TRUE; if ((s->dev->model->is_cis && !(s->dev->model->flags & GT68XX_FLAG_CIS_LAMP)) || !debug_options) DISABLE (OPT_AUTO_WARMUP); /* full scan */ s->opt[OPT_FULL_SCAN].name = "full-scan"; s->opt[OPT_FULL_SCAN].title = SANE_I18N ("Full scan"); s->opt[OPT_FULL_SCAN].desc = SANE_I18N ("Scan the complete scanning area including calibration strip. " "Be careful. Don't select the full height. For testing only."); s->opt[OPT_FULL_SCAN].type = SANE_TYPE_BOOL; s->opt[OPT_FULL_SCAN].unit = SANE_UNIT_NONE; s->opt[OPT_FULL_SCAN].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_FULL_SCAN].w = SANE_FALSE; if (!debug_options) DISABLE (OPT_FULL_SCAN); /* coarse calibration */ s->opt[OPT_COARSE_CAL].name = "coarse-calibration"; s->opt[OPT_COARSE_CAL].title = SANE_I18N ("Coarse calibration"); s->opt[OPT_COARSE_CAL].desc = SANE_I18N ("Setup gain and offset for scanning automatically. If this " "option is disabled, options for setting the analog frontend " "parameters manually are provided. This option is enabled " "by default. For testing only."); s->opt[OPT_COARSE_CAL].type = SANE_TYPE_BOOL; s->opt[OPT_COARSE_CAL].unit = SANE_UNIT_NONE; s->opt[OPT_COARSE_CAL].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_COARSE_CAL].w = SANE_TRUE; if (!debug_options) DISABLE (OPT_COARSE_CAL); if (s->dev->model->flags & GT68XX_FLAG_SHEET_FED) { s->val[OPT_COARSE_CAL].w = SANE_FALSE; DISABLE (OPT_COARSE_CAL); } /* coarse calibration only once */ s->opt[OPT_COARSE_CAL_ONCE].name = "coarse-calibration-once"; s->opt[OPT_COARSE_CAL_ONCE].title = SANE_I18N ("Coarse calibration for first scan only"); s->opt[OPT_COARSE_CAL_ONCE].desc = SANE_I18N ("Coarse calibration is only done for the first scan. Works " "with most scanners and can save scanning time. If the image " "brightness is different with each scan, disable this option. " "For testing only."); s->opt[OPT_COARSE_CAL_ONCE].type = SANE_TYPE_BOOL; s->opt[OPT_COARSE_CAL_ONCE].unit = SANE_UNIT_NONE; s->opt[OPT_COARSE_CAL_ONCE].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_COARSE_CAL_ONCE].w = SANE_FALSE; if (!debug_options) DISABLE (OPT_COARSE_CAL_ONCE); if (s->dev->model->flags & GT68XX_FLAG_SHEET_FED) DISABLE (OPT_COARSE_CAL_ONCE); /* calibration */ s->opt[OPT_QUALITY_CAL].name = SANE_NAME_QUALITY_CAL; s->opt[OPT_QUALITY_CAL].title = SANE_TITLE_QUALITY_CAL; s->opt[OPT_QUALITY_CAL].desc = SANE_TITLE_QUALITY_CAL; s->opt[OPT_QUALITY_CAL].type = SANE_TYPE_BOOL; s->opt[OPT_QUALITY_CAL].unit = SANE_UNIT_NONE; s->opt[OPT_QUALITY_CAL].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_QUALITY_CAL].w = SANE_TRUE; if (!debug_options) DISABLE (OPT_QUALITY_CAL); /* we disable image correction for scanners that can't calibrate */ if ((s->dev->model->flags & GT68XX_FLAG_SHEET_FED) &&(!(s->dev->model->flags & GT68XX_FLAG_HAS_CALIBRATE))) { s->val[OPT_QUALITY_CAL].w = SANE_FALSE; DISABLE (OPT_QUALITY_CAL); } /* backtrack lines */ s->opt[OPT_BACKTRACK_LINES].name = "backtrack-lines"; s->opt[OPT_BACKTRACK_LINES].title = SANE_I18N ("Backtrack lines"); s->opt[OPT_BACKTRACK_LINES].desc = SANE_I18N ("Number of lines the scan slider moves back when backtracking " "occurs. That happens when the scanner scans faster than the " "computer can receive the data. Low values cause faster scans " "but increase the risk of omitting lines."); s->opt[OPT_BACKTRACK_LINES].type = SANE_TYPE_INT; s->opt[OPT_BACKTRACK_LINES].unit = SANE_UNIT_NONE; s->opt[OPT_BACKTRACK_LINES].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BACKTRACK_LINES].constraint.range = &u8_range; if (s->dev->model->is_cis && !(s->dev->model->flags & GT68XX_FLAG_SHEET_FED)) s->val[OPT_BACKTRACK_LINES].w = 0x10; else s->val[OPT_BACKTRACK_LINES].w = 0x3f; if (!debug_options) DISABLE (OPT_BACKTRACK_LINES); /* "Enhancement" group: */ s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement"); s->opt[OPT_ENHANCEMENT_GROUP].desc = ""; s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_ENHANCEMENT_GROUP].cap = 0; s->opt[OPT_ENHANCEMENT_GROUP].size = 0; s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* internal gamma value */ s->opt[OPT_GAMMA_VALUE].name = "gamma-value"; s->opt[OPT_GAMMA_VALUE].title = SANE_I18N ("Gamma value"); s->opt[OPT_GAMMA_VALUE].desc = SANE_I18N ("Sets the gamma value of all channels."); s->opt[OPT_GAMMA_VALUE].type = SANE_TYPE_FIXED; s->opt[OPT_GAMMA_VALUE].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VALUE].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VALUE].constraint.range = &gamma_range; s->opt[OPT_GAMMA_VALUE].cap |= SANE_CAP_EMULATED; s->val[OPT_GAMMA_VALUE].w = s->dev->gamma_value; /* 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_INT; s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE; s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_THRESHOLD].constraint.range = &u8_range; s->val[OPT_THRESHOLD].w = 128; DISABLE (OPT_THRESHOLD); /* "Geometry" group: */ s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry"); s->opt[OPT_GEOMETRY_GROUP].desc = ""; s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_GEOMETRY_GROUP].size = 0; s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; x_range.max = model->x_size; y_range.max = model->y_size; /* top-left x */ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; s->opt[OPT_TL_X].unit = SANE_UNIT_MM; s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_X].constraint.range = &x_range; s->val[OPT_TL_X].w = 0; /* top-left y */ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_Y].constraint.range = &y_range; s->val[OPT_TL_Y].w = 0; /* bottom-right x */ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; s->opt[OPT_BR_X].unit = SANE_UNIT_MM; s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_X].constraint.range = &x_range; s->val[OPT_BR_X].w = x_range.max; /* bottom-right y */ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_Y].constraint.range = &y_range; s->val[OPT_BR_Y].w = y_range.max; /* sensor group */ s->opt[OPT_SENSOR_GROUP].name = SANE_NAME_SENSORS; s->opt[OPT_SENSOR_GROUP].title = SANE_TITLE_SENSORS; s->opt[OPT_SENSOR_GROUP].desc = SANE_DESC_SENSORS; s->opt[OPT_SENSOR_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_SENSOR_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* calibration needed */ s->opt[OPT_NEED_CALIBRATION_SW].name = "need-calibration"; s->opt[OPT_NEED_CALIBRATION_SW].title = SANE_I18N ("Needs calibration"); s->opt[OPT_NEED_CALIBRATION_SW].desc = SANE_I18N ("The scanner needs calibration for the current settings"); s->opt[OPT_NEED_CALIBRATION_SW].type = SANE_TYPE_BOOL; s->opt[OPT_NEED_CALIBRATION_SW].unit = SANE_UNIT_NONE; if (s->dev->model->flags & GT68XX_FLAG_HAS_CALIBRATE) s->opt[OPT_NEED_CALIBRATION_SW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else s->opt[OPT_NEED_CALIBRATION_SW].cap = SANE_CAP_INACTIVE; s->val[OPT_NEED_CALIBRATION_SW].b = 0; /* document present sensor */ s->opt[OPT_PAGE_LOADED_SW].name = SANE_NAME_PAGE_LOADED; s->opt[OPT_PAGE_LOADED_SW].title = SANE_TITLE_PAGE_LOADED; s->opt[OPT_PAGE_LOADED_SW].desc = SANE_DESC_PAGE_LOADED; s->opt[OPT_PAGE_LOADED_SW].type = SANE_TYPE_BOOL; s->opt[OPT_PAGE_LOADED_SW].unit = SANE_UNIT_NONE; if (s->dev->model->command_set->document_present) s->opt[OPT_PAGE_LOADED_SW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; else s->opt[OPT_PAGE_LOADED_SW].cap = SANE_CAP_INACTIVE; s->val[OPT_PAGE_LOADED_SW].b = 0; /* button group */ s->opt[OPT_BUTTON_GROUP].name = "Buttons"; s->opt[OPT_BUTTON_GROUP].title = SANE_I18N ("Buttons"); s->opt[OPT_BUTTON_GROUP].desc = SANE_I18N ("Buttons"); s->opt[OPT_BUTTON_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_BUTTON_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* calibrate button */ s->opt[OPT_CALIBRATE].name = "calibrate"; s->opt[OPT_CALIBRATE].title = SANE_I18N ("Calibrate"); s->opt[OPT_CALIBRATE].desc = SANE_I18N ("Start calibration using special sheet"); s->opt[OPT_CALIBRATE].type = SANE_TYPE_BUTTON; s->opt[OPT_CALIBRATE].unit = SANE_UNIT_NONE; if (s->dev->model->flags & GT68XX_FLAG_HAS_CALIBRATE) s->opt[OPT_CALIBRATE].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED | SANE_CAP_AUTOMATIC; else s->opt[OPT_CALIBRATE].cap = SANE_CAP_INACTIVE; s->val[OPT_CALIBRATE].b = 0; /* clear calibration cache button */ s->opt[OPT_CLEAR_CALIBRATION].name = "clear"; s->opt[OPT_CLEAR_CALIBRATION].title = SANE_I18N ("Clear calibration"); s->opt[OPT_CLEAR_CALIBRATION].desc = SANE_I18N ("Clear calibration cache"); s->opt[OPT_CLEAR_CALIBRATION].type = SANE_TYPE_BUTTON; s->opt[OPT_CLEAR_CALIBRATION].unit = SANE_UNIT_NONE; if (s->dev->model->flags & GT68XX_FLAG_HAS_CALIBRATE) s->opt[OPT_CLEAR_CALIBRATION].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED | SANE_CAP_AUTOMATIC; else s->opt[OPT_CLEAR_CALIBRATION].cap = SANE_CAP_INACTIVE; s->val[OPT_CLEAR_CALIBRATION].b = 0; RIE (calc_parameters (s)); DBG (5, "init_options: exit\n"); return SANE_STATUS_GOOD; } static SANE_Status attach (SANE_String_Const devname, GT68xx_Device ** devp, SANE_Bool may_wait) { GT68xx_Device *dev; SANE_Status status; DBG (5, "attach: start: devp %s NULL, may_wait = %d\n", devp ? "!=" : "==", may_wait); if (!devname) { DBG (1, "attach: devname == NULL\n"); return SANE_STATUS_INVAL; } for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->file_name, devname) == 0) { if (devp) *devp = dev; dev->missing = SANE_FALSE; DBG (4, "attach: device `%s' was already in device list\n", devname); return SANE_STATUS_GOOD; } } DBG (4, "attach: trying to open device `%s'\n", devname); RIE (gt68xx_device_new (&dev)); status = gt68xx_device_open (dev, devname); if (status == SANE_STATUS_GOOD) DBG (4, "attach: device `%s' successfully opened\n", devname); else { DBG (4, "attach: couldn't open device `%s': %s\n", devname, sane_strstatus (status)); gt68xx_device_free (dev); if (devp) *devp = 0; return status; } if (!gt68xx_device_is_configured (dev)) { GT68xx_Model *model = NULL; DBG (2, "attach: Warning: device `%s' is not listed in device table\n", devname); DBG (2, "attach: If you have manually added it, use override in gt68xx.conf\n"); gt68xx_device_get_model ("unknown-scanner", &model); status = gt68xx_device_set_model (dev, model); if (status != SANE_STATUS_GOOD) { DBG (4, "attach: couldn't set model: %s\n", sane_strstatus (status)); gt68xx_device_free (dev); if (devp) *devp = 0; return status; } dev->manual_selection = SANE_TRUE; } dev->file_name = strdup (devname); dev->missing = SANE_FALSE; if (!dev->file_name) return SANE_STATUS_NO_MEM; DBG (2, "attach: found %s flatbed scanner %s at %s\n", dev->model->vendor, dev->model->model, dev->file_name); ++num_devices; dev->next = first_dev; first_dev = dev; if (devp) *devp = dev; gt68xx_device_close (dev); DBG (5, "attach: exit\n"); return SANE_STATUS_GOOD; } static SANE_Status attach_one_device (SANE_String_Const devname) { GT68xx_Device *dev; SANE_Status status; RIE (attach (devname, &dev, SANE_FALSE)); if (dev) { /* Keep track of newly attached devices so we can set options as necessary. */ if (new_dev_len >= new_dev_alloced) { new_dev_alloced += 4; if (new_dev) new_dev = realloc (new_dev, new_dev_alloced * sizeof (new_dev[0])); else new_dev = malloc (new_dev_alloced * sizeof (new_dev[0])); if (!new_dev) { DBG (1, "attach_one_device: out of memory\n"); return SANE_STATUS_NO_MEM; } } new_dev[new_dev_len++] = dev; } return SANE_STATUS_GOOD; } #if defined(_WIN32) || defined(HAVE_OS2_H) # define PATH_SEP "\\" #else # define PATH_SEP "/" #endif static SANE_Status download_firmware_file (GT68xx_Device * dev) { SANE_Status status = SANE_STATUS_GOOD; SANE_Byte *buf = NULL; int size = -1; SANE_Char filename[PATH_MAX], dirname[PATH_MAX], basename[PATH_MAX]; FILE *f; if (strncmp (dev->model->firmware_name, PATH_SEP, 1) != 0) { /* probably filename only */ snprintf (filename, sizeof(filename), "%s%s%s%s%s%s%s", STRINGIFY (PATH_SANE_DATA_DIR), PATH_SEP, "sane", PATH_SEP, "gt68xx", PATH_SEP, dev->model->firmware_name); snprintf (dirname, sizeof(dirname), "%s%s%s%s%s", STRINGIFY (PATH_SANE_DATA_DIR), PATH_SEP, "sane", PATH_SEP, "gt68xx"); strncpy (basename, dev->model->firmware_name, sizeof(basename) - 1); basename[sizeof(basename) - 1] = '\0'; } else { /* absolute path */ char *pos; strncpy (filename, dev->model->firmware_name, sizeof(filename) - 1); filename[sizeof(filename) - 1] = '\0'; strncpy (dirname, dev->model->firmware_name, sizeof(dirname) - 1); dirname[sizeof(dirname) - 1] = '\0'; pos = strrchr (dirname, PATH_SEP[0]); if (pos) pos[0] = '\0'; strncpy (basename, pos + 1, sizeof(basename) - 1); basename[sizeof(basename) - 1] = '\0'; } /* first, try to open with exact case */ DBG (5, "download_firmware: trying %s\n", filename); f = fopen (filename, "rb"); if (!f) { /* and now any case */ DIR *dir; struct dirent *direntry; DBG (5, "download_firmware_file: Couldn't open firmware file `%s': %s\n", filename, strerror (errno)); dir = opendir (dirname); if (!dir) { DBG (5, "download_firmware: couldn't open directory `%s': %s\n", dirname, strerror (errno)); status = SANE_STATUS_INVAL; } if (status == SANE_STATUS_GOOD) { do { direntry = readdir (dir); if (direntry && (strncasecmp (direntry->d_name, basename, PATH_MAX) == 0)) { int len = snprintf (filename, sizeof(filename), "%s%s%s", dirname, PATH_SEP, direntry->d_name); if ((len < 0) || (len >= (int) sizeof(filename))) { DBG (5, "download_firmware: filepath `%s%s%s' too long\n", dirname, PATH_SEP, direntry->d_name); status = SANE_STATUS_INVAL; } break; } } while (direntry != 0); if (direntry == 0) { DBG (5, "download_firmware: file `%s' not found\n", filename); status = SANE_STATUS_INVAL; } closedir (dir); } if (status == SANE_STATUS_GOOD) { DBG (5, "download_firmware: trying %s\n", filename); f = fopen (filename, "rb"); if (!f) { DBG (5, "download_firmware_file: Couldn't open firmware file `%s': %s\n", filename, strerror (errno)); status = SANE_STATUS_INVAL; } } if (status != SANE_STATUS_GOOD) { DBG (0, "Couldn't open firmware file (`%s'): %s\n", filename, strerror (errno)); } } if (status == SANE_STATUS_GOOD) { fseek (f, 0, SEEK_END); size = ftell (f); fseek (f, 0, SEEK_SET); if (size == -1) { DBG (1, "download_firmware_file: error getting size of " "firmware file \"%s\": %s\n", filename, strerror (errno)); status = SANE_STATUS_INVAL; } } if (status == SANE_STATUS_GOOD) { DBG (5, "firmware size: %d\n", size); buf = (SANE_Byte *) malloc (size); if (!buf) { DBG (1, "download_firmware_file: cannot allocate %d bytes " "for firmware\n", size); status = SANE_STATUS_NO_MEM; } } if (status == SANE_STATUS_GOOD) { int bytes_read = fread (buf, 1, size, f); if (bytes_read != size) { DBG (1, "download_firmware_file: problem reading firmware " "file \"%s\": %s\n", filename, strerror (errno)); status = SANE_STATUS_INVAL; } } if (f) fclose (f); if (status == SANE_STATUS_GOOD) { status = gt68xx_device_download_firmware (dev, buf, size); if (status != SANE_STATUS_GOOD) { DBG (1, "download_firmware_file: firmware download failed: %s\n", sane_strstatus (status)); } } if (buf) free (buf); return status; } /** probe for gt68xx devices * This function scan usb and try to attached to scanner * configured in gt68xx.conf . */ static SANE_Status probe_gt68xx_devices(void) { SANE_Char line[PATH_MAX]; SANE_Char *word; SANE_String_Const cp; SANE_Int linenumber; GT68xx_Device *dev; FILE *fp; /* set up for no new devices detected at first */ new_dev = 0; new_dev_len = 0; new_dev_alloced = 0; /* mark already detected devices as missing, during device probe * detected devices will clear this flag */ dev = first_dev; while(dev!=NULL) { dev->missing = SANE_TRUE; dev = dev->next; } fp = sanei_config_open (GT68XX_CONFIG_FILE); if (!fp) { /* default to /dev/usb/scanner instead of insisting on config file */ DBG (3, "sane_init: couldn't open config file `%s': %s. Using " "/dev/usb/scanner directly\n", GT68XX_CONFIG_FILE, strerror (errno)); attach ("/dev/usb/scanner", 0, SANE_FALSE); return SANE_STATUS_GOOD; } little_endian = calc_little_endian (); DBG (5, "sane_init: %s endian machine\n", little_endian ? "little" : "big"); linenumber = 0; DBG (4, "sane_init: reading config file `%s'\n", GT68XX_CONFIG_FILE); while (sanei_config_read (line, sizeof (line), fp)) { word = 0; linenumber++; cp = sanei_config_get_string (line, &word); if (!word || cp == line) { DBG (6, "sane_init: config file line %d: ignoring empty line\n", linenumber); if (word) free (word); continue; } if (word[0] == '#') { DBG (6, "sane_init: config file line %d: ignoring comment line\n", linenumber); free (word); continue; } if (strcmp (word, "firmware") == 0) { free (word); word = 0; cp = sanei_config_get_string (cp, &word); if (word) { int i; for (i = 0; i < new_dev_len; i++) { new_dev[i]->model->firmware_name = word; DBG (5, "sane_init: device %s: firmware will be loaded " "from %s\n", new_dev[i]->model->name, new_dev[i]->model->firmware_name); } if (i == 0) { DBG (5, "sane_init: firmware %s can't be loaded, set device " "first\n", word); free (word); } } else { DBG (3, "sane_init: option `firmware' needs a parameter\n"); } } else if (strcmp (word, "vendor") == 0) { free (word); word = 0; cp = sanei_config_get_string (cp, &word); if (word) { int i; for (i = 0; i < new_dev_len; i++) { new_dev[i]->model->vendor = word; DBG (5, "sane_init: device %s: vendor name set to %s\n", new_dev[i]->model->name, new_dev[i]->model->vendor); } if (i == 0) { DBG (5, "sane_init: can't set vendor name %s, set device " "first\n", word); free (word); } } else { DBG (3, "sane_init: option `vendor' needs a parameter\n"); } } else if (strcmp (word, "model") == 0) { free (word); word = 0; cp = sanei_config_get_string (cp, &word); if (word) { int i; for (i = 0; i < new_dev_len; i++) { new_dev[i]->model->model = word; DBG (5, "sane_init: device %s: model name set to %s\n", new_dev[i]->model->name, new_dev[i]->model->model); } if (i == 0) { DBG (5, "sane_init: can't set model name %s, set device " "first\n", word); free (word); } } else { DBG (3, "sane_init: option `model' needs a parameter\n"); } } else if (strcmp (word, "override") == 0) { free (word); word = 0; cp = sanei_config_get_string (cp, &word); if (word) { int i; for (i = 0; i < new_dev_len; i++) { SANE_Status status; GT68xx_Device *dev = new_dev[i]; GT68xx_Model *model; if (gt68xx_device_get_model (word, &model) == SANE_TRUE) { status = gt68xx_device_set_model (dev, model); if (status != SANE_STATUS_GOOD) DBG (1, "sane_init: couldn't override model: %s\n", sane_strstatus (status)); else DBG (5, "sane_init: new model set to %s\n", dev->model->name); } else { DBG (1, "sane_init: override: model %s not found\n", word); } } if (i == 0) DBG (5, "sane_init: can't override model to %s, set device " "first\n", word); free (word); } else { DBG (3, "sane_init: option `override' needs a parameter\n"); } } else if (strcmp (word, "afe") == 0) { GT68xx_AFE_Parameters afe = {0, 0, 0, 0, 0, 0}; SANE_Status status; free (word); word = 0; status = get_afe_values (cp, &afe); if (status == SANE_STATUS_GOOD) { int i; for (i = 0; i < new_dev_len; i++) { new_dev[i]->model->afe_params = afe; DBG (5, "sane_init: device %s: setting new afe values\n", new_dev[i]->model->name); } if (i == 0) DBG (5, "sane_init: can't set afe values, set device first\n"); } else DBG (3, "sane_init: can't set afe values\n"); } else { new_dev_len = 0; DBG (4, "sane_init: config file line %d: trying to attach `%s'\n", linenumber, line); sanei_usb_attach_matching_devices (line, attach_one_device); if (word) free (word); word = 0; } } if (new_dev_alloced > 0) { new_dev_len = new_dev_alloced = 0; free (new_dev); } fclose (fp); return SANE_STATUS_GOOD; } /* -------------------------- SANE API functions ------------------------- */ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { SANE_Status status; DBG_INIT (); #ifdef DBG_LEVEL if (DBG_LEVEL > 0) { DBG (5, "sane_init: debug options are enabled, handle with care\n"); debug_options = SANE_TRUE; } #endif DBG (2, "SANE GT68xx backend version %d.%d build %d from %s\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD, PACKAGE_STRING); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); DBG (5, "sane_init: authorize %s null\n", authorize ? "!=" : "=="); sanei_usb_init (); num_devices = 0; first_dev = 0; first_handle = 0; devlist = 0; new_dev = 0; new_dev_len = 0; new_dev_alloced = 0; status = probe_gt68xx_devices (); DBG (5, "sane_init: exit\n"); return status; } void sane_exit (void) { GT68xx_Device *dev, *next; DBG (5, "sane_exit: start\n"); sanei_usb_exit(); for (dev = first_dev; dev; dev = next) { next = dev->next; gt68xx_device_free (dev); } first_dev = 0; first_handle = 0; if (devlist) free (devlist); devlist = 0; DBG (5, "sane_exit: exit\n"); } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { GT68xx_Device *dev; SANE_Int dev_num; DBG (5, "sane_get_devices: start: local_only = %s\n", local_only == SANE_TRUE ? "true" : "false"); /* hot-plug case : detection of newly connected scanners */ sanei_usb_scan_devices (); probe_gt68xx_devices (); if (devlist) free (devlist); devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); if (!devlist) return SANE_STATUS_NO_MEM; dev_num = 0; dev = first_dev; while(dev!=NULL) { SANE_Device *sane_device; /* don't return devices that have been unplugged */ if(dev->missing==SANE_FALSE) { sane_device = malloc (sizeof (*sane_device)); if (!sane_device) return SANE_STATUS_NO_MEM; sane_device->name = dev->file_name; sane_device->vendor = dev->model->vendor; sane_device->model = dev->model->model; sane_device->type = strdup ("flatbed scanner"); devlist[dev_num] = sane_device; dev_num++; } /* next device */ dev = dev->next; } devlist[dev_num] = 0; *device_list = devlist; DBG (5, "sane_get_devices: exit\n"); return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { GT68xx_Device *dev; SANE_Status status; GT68xx_Scanner *s; SANE_Bool power_ok; DBG (5, "sane_open: start (devicename = `%s')\n", devicename); if (devicename[0]) { /* test for gt68xx short hand name */ if(strcmp(devicename,"gt68xx")!=0) { for (dev = first_dev; dev; dev = dev->next) if (strcmp (dev->file_name, devicename) == 0) break; if (!dev) { DBG (5, "sane_open: couldn't find `%s' in devlist, trying attach\n", devicename); RIE (attach (devicename, &dev, SANE_TRUE)); } else DBG (5, "sane_open: found `%s' in devlist\n", dev->model->name); } else { dev = first_dev; if (dev) { devicename = dev->file_name; DBG (5, "sane_open: default empty devicename, using first device `%s'\n", devicename); } } } else { /* empty devicname -> use first device */ dev = first_dev; if (dev) { devicename = dev->file_name; DBG (5, "sane_open: empty devicename, trying `%s'\n", devicename); } } if (!dev) return SANE_STATUS_INVAL; RIE (gt68xx_device_open (dev, devicename)); RIE (gt68xx_device_activate (dev)); if (dev->model->flags & GT68XX_FLAG_UNTESTED) { DBG (0, "WARNING: Your scanner is not fully supported or at least \n"); DBG (0, " had only limited testing. Please be careful and \n"); DBG (0, " report any failure/success to \n"); DBG (0, " sane-devel@alioth-lists.debian.net. Please provide as many\n"); DBG (0, " details as possible, e.g. the exact name of your\n"); DBG (0, " scanner and what does (not) work.\n"); } if (dev->manual_selection) { DBG (0, "WARNING: You have manually added the ids of your scanner \n"); DBG (0, " to gt68xx.conf. Please use an appropriate override \n"); DBG (0, " for your scanner. Use extreme care and switch off \n"); DBG (0, " the scanner immediately if you hear unusual noise. \n"); DBG (0, " Please report any success to \n"); DBG (0, " sane-devel@alioth-lists.debian.net. Please provide as many\n"); DBG (0, " details as possible, e.g. the exact name of your\n"); DBG (0, " scanner, ids, settings etc.\n"); if (strcmp (dev->model->name, "unknown-scanner") == 0) { GT68xx_USB_Device_Entry *entry; DBG (0, "ERROR: You haven't chosen an override in gt68xx.conf. Please use \n"); DBG (0, " one of the following: \n"); for (entry = gt68xx_usb_device_list; entry->model; ++entry) { if (strcmp (entry->model->name, "unknown-scanner") != 0) DBG (0, " %s\n", entry->model->name); } return SANE_STATUS_UNSUPPORTED; } } /* The firmware check is disabled by default because it may confuse some scanners: So the firmware is loaded every time. */ #if 0 RIE (gt68xx_device_check_firmware (dev, &firmware_loaded)); firmware_loaded = SANE_FALSE; if (firmware_loaded) DBG (3, "sane_open: firmware already loaded, skipping load\n"); else RIE (download_firmware_file (dev)); /* RIE (gt68xx_device_check_firmware (dev, &firmware_loaded)); */ if (!firmware_loaded) { DBG (1, "sane_open: firmware still not loaded? Proceeding anyway\n"); /* return SANE_STATUS_IO_ERROR; */ } #else RIE (download_firmware_file (dev)); #endif RIE (gt68xx_device_get_id (dev)); if (!(dev->model->flags & GT68XX_FLAG_NO_STOP)) RIE (gt68xx_device_stop_scan (dev)); RIE (gt68xx_device_get_power_status (dev, &power_ok)); if (power_ok) { DBG (5, "sane_open: power ok\n"); } else { DBG (0, "sane_open: power control failure: check power plug!\n"); return SANE_STATUS_IO_ERROR; } RIE (gt68xx_scanner_new (dev, &s)); RIE (gt68xx_device_lamp_control (s->dev, SANE_TRUE, SANE_FALSE)); gettimeofday (&s->lamp_on_time, 0); /* insert newly opened handle into list of open handles: */ s->next = first_handle; first_handle = s; *handle = s; s->scanning = SANE_FALSE; s->first_scan = SANE_TRUE; s->gamma_table = 0; s->calibrated = SANE_FALSE; RIE (init_options (s)); dev->gray_mode_color = 0x02; /* try to restore calibration from file */ if((s->dev->model->flags & GT68XX_FLAG_HAS_CALIBRATE)) { /* error restoring calibration is non blocking */ gt68xx_read_calibration(s); } DBG (5, "sane_open: exit\n"); return SANE_STATUS_GOOD; } void sane_close (SANE_Handle handle) { GT68xx_Scanner *prev, *s; GT68xx_Device *dev; DBG (5, "sane_close: start\n"); /* remove handle from list of open handles: */ prev = 0; for (s = first_handle; s; s = s->next) { if (s == handle) break; prev = s; } if (!s) { DBG (5, "close: invalid handle %p\n", handle); return; /* oops, not a handle we know about */ } if (prev) prev->next = s->next; else first_handle = s->next; if (s->val[OPT_LAMP_OFF_AT_EXIT].w == SANE_TRUE) gt68xx_device_lamp_control (s->dev, SANE_FALSE, SANE_FALSE); dev = s->dev; free (s->val[OPT_MODE].s); free (s->val[OPT_GRAY_MODE_COLOR].s); free (s->val[OPT_SOURCE].s); free (dev->file_name); free ((void *)(size_t)s->opt[OPT_RESOLUTION].constraint.word_list); gt68xx_scanner_free (s); gt68xx_device_fix_descriptor (dev); gt68xx_device_deactivate (dev); gt68xx_device_close (dev); DBG (5, "sane_close: exit\n"); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { GT68xx_Scanner *s = handle; if ((unsigned) option >= NUM_OPTIONS) return 0; DBG (5, "sane_get_option_descriptor: option = %s (%d)\n", s->opt[option].name, option); return s->opt + option; } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { GT68xx_Scanner *s = handle; SANE_Status status = SANE_STATUS_GOOD; SANE_Word cap; SANE_Int myinfo = 0; DBG (5, "sane_control_option: start: action = %s, option = %s (%d)\n", (action == SANE_ACTION_GET_VALUE) ? "get" : (action == SANE_ACTION_SET_VALUE) ? "set" : (action == SANE_ACTION_SET_AUTO) ? "set_auto" : "unknown", s->opt[option].name, option); if (info) *info = 0; if (s->scanning) { DBG (1, "sane_control_option: don't call this function while " "scanning (option = %s (%d))\n", s->opt[option].name, option); return SANE_STATUS_DEVICE_BUSY; } if (option >= NUM_OPTIONS || option < 0) { DBG (1, "sane_control_option: option %d >= NUM_OPTIONS || option < 0\n", option); return SANE_STATUS_INVAL; } cap = s->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) { DBG (2, "sane_control_option: option %d is inactive\n", option); return SANE_STATUS_INVAL; } if (action == SANE_ACTION_GET_VALUE) { switch (option) { /* word options: */ case OPT_NUM_OPTS: case OPT_RESOLUTION: case OPT_BIT_DEPTH: case OPT_FULL_SCAN: case OPT_COARSE_CAL: case OPT_COARSE_CAL_ONCE: case OPT_QUALITY_CAL: case OPT_BACKTRACK: case OPT_BACKTRACK_LINES: case OPT_PREVIEW: case OPT_LAMP_OFF_AT_EXIT: case OPT_AUTO_WARMUP: case OPT_GAMMA_VALUE: case OPT_THRESHOLD: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: *(SANE_Word *) val = s->val[option].w; break; /* string options: */ case OPT_MODE: case OPT_GRAY_MODE_COLOR: case OPT_SOURCE: strcpy (val, s->val[option].s); break; case OPT_NEED_CALIBRATION_SW: *(SANE_Bool *) val = !s->calibrated; break; case OPT_PAGE_LOADED_SW: s->dev->model->command_set->document_present (s->dev, val); break; default: DBG (2, "sane_control_option: can't get unknown option %d\n", option); } } else if (action == SANE_ACTION_SET_VALUE) { if (!SANE_OPTION_IS_SETTABLE (cap)) { DBG (2, "sane_control_option: option %d is not settable\n", option); return SANE_STATUS_INVAL; } status = sanei_constrain_value (s->opt + option, val, &myinfo); if (status != SANE_STATUS_GOOD) { DBG (2, "sane_control_option: sanei_constrain_value returned %s\n", sane_strstatus (status)); return status; } switch (option) { case OPT_RESOLUTION: case OPT_BIT_DEPTH: case OPT_FULL_SCAN: case OPT_PREVIEW: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: s->val[option].w = *(SANE_Word *) val; RIE (calc_parameters (s)); myinfo |= SANE_INFO_RELOAD_PARAMS; break; case OPT_LAMP_OFF_AT_EXIT: case OPT_AUTO_WARMUP: case OPT_COARSE_CAL_ONCE: case OPT_BACKTRACK_LINES: case OPT_QUALITY_CAL: case OPT_GAMMA_VALUE: case OPT_THRESHOLD: s->val[option].w = *(SANE_Word *) val; break; case OPT_GRAY_MODE_COLOR: if (strcmp (s->val[option].s, val) != 0) { /* something changed */ if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); } break; case OPT_SOURCE: if (strcmp (s->val[option].s, val) != 0) { /* something changed */ if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); if (strcmp (s->val[option].s, "Transparency Adapter") == 0) { RIE (gt68xx_device_lamp_control (s->dev, SANE_FALSE, SANE_TRUE)); x_range.max = s->dev->model->x_size_ta; y_range.max = s->dev->model->y_size_ta; } else { RIE (gt68xx_device_lamp_control (s->dev, SANE_TRUE, SANE_FALSE)); x_range.max = s->dev->model->x_size; y_range.max = s->dev->model->y_size; } s->first_scan = SANE_TRUE; myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; gettimeofday (&s->lamp_on_time, 0); } break; case OPT_MODE: if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); if (strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_LINEART) == 0) { ENABLE (OPT_THRESHOLD); DISABLE (OPT_BIT_DEPTH); ENABLE (OPT_GRAY_MODE_COLOR); } else { DISABLE (OPT_THRESHOLD); if (strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_GRAY) == 0) { RIE (create_bpp_list (s, s->dev->model->bpp_gray_values)); ENABLE (OPT_GRAY_MODE_COLOR); } else { RIE (create_bpp_list (s, s->dev->model->bpp_color_values)); DISABLE (OPT_GRAY_MODE_COLOR); } if (s->bpp_list[0] < 2) DISABLE (OPT_BIT_DEPTH); else ENABLE (OPT_BIT_DEPTH); } RIE (calc_parameters (s)); myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; break; case OPT_COARSE_CAL: s->val[option].w = *(SANE_Word *) val; if (s->val[option].w == SANE_TRUE) { ENABLE (OPT_COARSE_CAL_ONCE); s->first_scan = SANE_TRUE; } else { DISABLE (OPT_COARSE_CAL_ONCE); } myinfo |= SANE_INFO_RELOAD_OPTIONS; break; case OPT_BACKTRACK: s->val[option].w = *(SANE_Word *) val; if (s->val[option].w == SANE_TRUE) ENABLE (OPT_BACKTRACK_LINES); else DISABLE (OPT_BACKTRACK_LINES); myinfo |= SANE_INFO_RELOAD_OPTIONS; break; case OPT_CALIBRATE: status = gt68xx_sheetfed_scanner_calibrate (s); myinfo |= SANE_INFO_RELOAD_OPTIONS; break; case OPT_CLEAR_CALIBRATION: gt68xx_clear_calibration (s); myinfo |= SANE_INFO_RELOAD_OPTIONS; break; default: DBG (2, "sane_control_option: can't set unknown option %d\n", option); } } else { DBG (2, "sane_control_option: unknown action %d for option %d\n", action, option); return SANE_STATUS_INVAL; } if (info) *info = myinfo; DBG (5, "sane_control_option: exit\n"); return status; } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { GT68xx_Scanner *s = handle; SANE_Status status; DBG (5, "sane_get_parameters: start\n"); RIE (calc_parameters (s)); if (params) *params = s->params; DBG (4, "sane_get_parameters: format=%d, last_frame=%d, lines=%d\n", s->params.format, s->params.last_frame, s->params.lines); DBG (4, "sane_get_parameters: pixels_per_line=%d, bytes per line=%d\n", s->params.pixels_per_line, s->params.bytes_per_line); DBG (3, "sane_get_parameters: pixels %dx%dx%d\n", s->params.pixels_per_line, s->params.lines, 1 << s->params.depth); DBG (5, "sane_get_parameters: exit\n"); return SANE_STATUS_GOOD; } SANE_Status sane_start (SANE_Handle handle) { GT68xx_Scanner *s = handle; GT68xx_Scan_Request scan_request; GT68xx_Scan_Parameters scan_params; SANE_Status status; SANE_Int i, gamma_size; unsigned int *buffer_pointers[3]; SANE_Bool document; DBG (5, "sane_start: start\n"); /* First make sure we have a current parameter set. Some of the parameters will be overwritten below, but that's OK. */ RIE (calc_parameters (s)); if (s->val[OPT_TL_X].w >= s->val[OPT_BR_X].w) { DBG (0, "sane_start: top left x >= bottom right x --- exiting\n"); return SANE_STATUS_INVAL; } if (s->val[OPT_TL_Y].w >= s->val[OPT_BR_Y].w) { DBG (0, "sane_start: top left y >= bottom right y --- exiting\n"); return SANE_STATUS_INVAL; } if (strcmp (s->val[OPT_GRAY_MODE_COLOR].s, GT68XX_COLOR_BLUE) == 0) s->dev->gray_mode_color = 0x01; else if (strcmp (s->val[OPT_GRAY_MODE_COLOR].s, GT68XX_COLOR_GREEN) == 0) s->dev->gray_mode_color = 0x02; else s->dev->gray_mode_color = 0x03; setup_scan_request (s, &scan_request); if (!s->first_scan && s->val[OPT_COARSE_CAL_ONCE].w == SANE_TRUE) s->auto_afe = SANE_FALSE; else s->auto_afe = s->val[OPT_COARSE_CAL].w; s->dev->gamma_value = s->val[OPT_GAMMA_VALUE].w; gamma_size = s->params.depth == 16 ? 65536 : 256; s->gamma_table = malloc (sizeof (SANE_Int) * gamma_size); if (!s->gamma_table) { DBG (1, "sane_start: couldn't malloc %d bytes for gamma table\n", gamma_size); return SANE_STATUS_NO_MEM; } for (i = 0; i < gamma_size; i++) { s->gamma_table[i] = (gamma_size - 1) * pow (((double) i + 1) / (gamma_size), 1.0 / SANE_UNFIX (s->dev->gamma_value)) + 0.5; if (s->gamma_table[i] > (gamma_size - 1)) s->gamma_table[i] = (gamma_size - 1); if (s->gamma_table[i] < 0) s->gamma_table[i] = 0; #if 0 printf ("%d %d\n", i, s->gamma_table[i]); #endif } if(!(s->dev->model->flags & GT68XX_FLAG_HAS_CALIBRATE)) { s->calib = s->val[OPT_QUALITY_CAL].w; } if (!(s->dev->model->flags & GT68XX_FLAG_NO_STOP)) RIE (gt68xx_device_stop_scan (s->dev)); if (!(s->dev->model->flags & GT68XX_FLAG_SHEET_FED)) RIE (gt68xx_device_carriage_home (s->dev)); gt68xx_scanner_wait_for_positioning (s); gettimeofday (&s->start_time, 0); if (s->val[OPT_BACKTRACK].w == SANE_TRUE) scan_request.backtrack = SANE_TRUE; else { if (s->val[OPT_RESOLUTION].w >= s->dev->model->ydpi_no_backtrack) scan_request.backtrack = SANE_FALSE; else scan_request.backtrack = SANE_TRUE; } if (scan_request.backtrack) scan_request.backtrack_lines = s->val[OPT_BACKTRACK_LINES].w; else scan_request.backtrack_lines = 0; /* don't call calibration for scanners that use sheetfed_calibrate */ if(!(s->dev->model->flags & GT68XX_FLAG_HAS_CALIBRATE)) { RIE (gt68xx_scanner_calibrate (s, &scan_request)); } else { s->calib = s->calibrated; } /* is possible, wait for document to be inserted before scanning */ /* wait for 5 secondes max */ if (s->dev->model->flags & GT68XX_FLAG_SHEET_FED && s->dev->model->command_set->document_present) { i=0; do { RIE(s->dev->model->command_set->document_present(s->dev,&document)); if(document==SANE_FALSE) { i++; sleep(1); } } while ((i<5) && (document==SANE_FALSE)); if(document==SANE_FALSE) { DBG (4, "sane_start: no document detected after %d s\n",i); return SANE_STATUS_NO_DOCS; } } /* some sheetfed scanners need a special operation to move * paper before starting real scan */ if (s->dev->model->flags & GT68XX_FLAG_SHEET_FED) { RIE (gt68xx_sheetfed_move_to_scan_area (s, &scan_request)); } /* restore calibration */ if( (s->dev->model->flags & GT68XX_FLAG_HAS_CALIBRATE) &&(s->calibrated == SANE_TRUE)) { /* compute scan parameters */ scan_request.calculate = SANE_TRUE; gt68xx_device_setup_scan (s->dev, &scan_request, SA_SCAN, &scan_params); /* restore settings from calibration stored */ memcpy(s->dev->afe,&(s->afe_params), sizeof(GT68xx_AFE_Parameters)); RIE (gt68xx_assign_calibration (s, scan_params)); scan_request.calculate = SANE_FALSE; } /* send scan request to the scanner */ RIE (gt68xx_scanner_start_scan (s, &scan_request, &scan_params)); for (i = 0; i < scan_params.overscan_lines; ++i) RIE (gt68xx_scanner_read_line (s, buffer_pointers)); DBG (4, "sane_start: wanted: dpi=%d, x=%.1f, y=%.1f, width=%.1f, " "height=%.1f, color=%s\n", scan_request.xdpi, SANE_UNFIX (scan_request.x0), SANE_UNFIX (scan_request.y0), SANE_UNFIX (scan_request.xs), SANE_UNFIX (scan_request.ys), scan_request.color ? "color" : "gray"); s->line = 0; s->byte_count = s->reader->params.pixel_xs; s->total_bytes = 0; s->first_scan = SANE_FALSE; #ifdef DEBUG_BRIGHTNESS s->average_white = 0; s->max_white = 0; s->min_black = 255; #endif s->scanning = SANE_TRUE; DBG (5, "sane_start: exit\n"); return SANE_STATUS_GOOD; } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { GT68xx_Scanner *s = handle; SANE_Status status; static unsigned int *buffer_pointers[3]; SANE_Int inflate_x; SANE_Bool lineart; SANE_Int i, color, colors; if (!s) { DBG (1, "sane_read: handle is null!\n"); return SANE_STATUS_INVAL; } if (!buf) { DBG (1, "sane_read: buf is null!\n"); return SANE_STATUS_INVAL; } if (!len) { DBG (1, "sane_read: len is null!\n"); return SANE_STATUS_INVAL; } *len = 0; if (!s->scanning) { DBG (3, "sane_read: scan was cancelled, is over or has not been " "initiated yet\n"); return SANE_STATUS_CANCELLED; } DBG (5, "sane_read: start (line %d of %d, byte_count %d of %d)\n", s->line, s->reader->params.pixel_ys, s->byte_count, s->reader->params.pixel_xs); if (s->line >= s->reader->params.pixel_ys && s->byte_count >= s->reader->params.pixel_xs) { DBG (4, "sane_read: nothing more to scan: EOF\n"); gt68xx_scanner_stop_scan(s); return SANE_STATUS_EOF; } inflate_x = s->val[OPT_RESOLUTION].w / s->dev->model->optical_xdpi; if (inflate_x > 1) DBG (5, "sane_read: inflating x by factor %d\n", inflate_x); else inflate_x = 1; lineart = (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) == 0) ? SANE_TRUE : SANE_FALSE; if (s->reader->params.color) colors = 3; else colors = 1; while ((*len) < max_len) { if (s->byte_count >= s->reader->params.pixel_xs) { if (s->line >= s->reader->params.pixel_ys) { DBG (4, "sane_read: scan complete: %d bytes, %d total\n", *len, s->total_bytes); return SANE_STATUS_GOOD; } DBG (5, "sane_read: getting line %d of %d\n", s->line, s->reader->params.pixel_ys); RIE (gt68xx_scanner_read_line (s, buffer_pointers)); s->line++; s->byte_count = 0; /* Apply gamma */ for (color = 0; color < colors; color++) for (i = 0; i < s->reader->pixels_per_line; i++) { if (s->reader->params.depth > 8) buffer_pointers[color][i] = s->gamma_table[buffer_pointers[color][i]]; else buffer_pointers[color][i] = (s->gamma_table[buffer_pointers[color][i] >> 8] << 8) + (s->gamma_table[buffer_pointers[color][i] >> 8]); } /* mirror lines */ if (s->dev->model->flags & GT68XX_FLAG_MIRROR_X) { unsigned int swap; for (color = 0; color < colors; color++) { for (i = 0; i < s->reader->pixels_per_line / 2; i++) { swap = buffer_pointers[color][i]; buffer_pointers[color][i] = buffer_pointers[color][s->reader->pixels_per_line - 1 - i]; buffer_pointers[color][s->reader->pixels_per_line - 1 - i] = swap; } } } } if (lineart) { SANE_Int bit; SANE_Byte threshold = s->val[OPT_THRESHOLD].w; buf[*len] = 0; for (bit = 7; bit >= 0; bit--) { SANE_Byte is_black = (((buffer_pointers[0][s->byte_count] >> 8) & 0xff) > threshold) ? 0 : 1; buf[*len] |= (is_black << bit); if ((7 - bit) % inflate_x == (inflate_x - 1)) s->byte_count++; } } else if (s->reader->params.color) { /* color */ if (s->reader->params.depth > 8) { SANE_Int color = (s->total_bytes / 2) % 3; if ((s->total_bytes % 2) == 0) { if (little_endian) buf[*len] = buffer_pointers[color][s->byte_count] & 0xff; else buf[*len] = (buffer_pointers[color][s->byte_count] >> 8) & 0xff; } else { if (little_endian) buf[*len] = (buffer_pointers[color][s->byte_count] >> 8) & 0xff; else buf[*len] = buffer_pointers[color][s->byte_count] & 0xff; if (s->total_bytes % (inflate_x * 6) == (inflate_x * 6 - 1)) s->byte_count++; } } else { SANE_Int color = s->total_bytes % 3; buf[*len] = (buffer_pointers[color][s->byte_count] >> 8) & 0xff; if (s->total_bytes % (inflate_x * 3) == (inflate_x * 3 - 1)) s->byte_count++; #ifdef DEBUG_BRIGHTNESS s->average_white += buf[*len]; s->max_white = (buf[*len] > s->max_white) ? buf[*len] : s->max_white; s->min_black = (buf[*len] < s->min_black) ? buf[*len] : s->min_black; #endif } } else { /* gray */ if (s->reader->params.depth > 8) { if ((s->total_bytes % 2) == 0) { if (little_endian) buf[*len] = buffer_pointers[0][s->byte_count] & 0xff; else buf[*len] = (buffer_pointers[0][s->byte_count] >> 8) & 0xff; } else { if (little_endian) buf[*len] = (buffer_pointers[0][s->byte_count] >> 8) & 0xff; else buf[*len] = buffer_pointers[0][s->byte_count] & 0xff; if (s->total_bytes % (2 * inflate_x) == (2 * inflate_x - 1)) s->byte_count++; } } else { buf[*len] = (buffer_pointers[0][s->byte_count] >> 8) & 0xff; if (s->total_bytes % inflate_x == (inflate_x - 1)) s->byte_count++; } } (*len)++; s->total_bytes++; } DBG (4, "sane_read: exit (line %d of %d, byte_count %d of %d, %d bytes, " "%d total)\n", s->line, s->reader->params.pixel_ys, s->byte_count, s->reader->params.pixel_xs, *len, s->total_bytes); return SANE_STATUS_GOOD; } void sane_cancel (SANE_Handle handle) { GT68xx_Scanner *s = handle; DBG (5, "sane_cancel: start\n"); if (s->scanning) { s->scanning = SANE_FALSE; if (s->total_bytes != (s->params.bytes_per_line * s->params.lines)) DBG (1, "sane_cancel: warning: scanned %d bytes, expected %d " "bytes\n", s->total_bytes, s->params.bytes_per_line * s->params.lines); else { struct timeval now; int secs; gettimeofday (&now, 0); secs = now.tv_sec - s->start_time.tv_sec; DBG (3, "sane_cancel: scan finished, scanned %d bytes in %d seconds\n", s->total_bytes, secs); #ifdef DEBUG_BRIGHTNESS DBG (1, "sane_cancel: average white: %d, max_white=%d, min_black=%d\n", s->average_white / s->total_bytes, s->max_white, s->min_black); #endif } /* some scanners don't like this command when cancelling a scan */ sanei_usb_set_timeout (SHORT_TIMEOUT); gt68xx_device_fix_descriptor (s->dev); gt68xx_scanner_stop_scan (s); sanei_usb_set_timeout (LONG_TIMEOUT); if (s->dev->model->flags & GT68XX_FLAG_SHEET_FED) { gt68xx_device_paperfeed (s->dev); } else { sanei_usb_set_timeout (SHORT_TIMEOUT); gt68xx_scanner_wait_for_positioning (s); sanei_usb_set_timeout (LONG_TIMEOUT); gt68xx_device_carriage_home (s->dev); } if (s->gamma_table) { free (s->gamma_table); s->gamma_table = 0; } } else { DBG (4, "sane_cancel: scan has not been initiated yet, " "or it is already aborted\n"); } DBG (5, "sane_cancel: exit\n"); return; } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { GT68xx_Scanner *s = handle; DBG (5, "sane_set_io_mode: handle = %p, non_blocking = %s\n", handle, non_blocking == SANE_TRUE ? "true" : "false"); if (!s->scanning) { DBG (1, "sane_set_io_mode: not scanning\n"); return SANE_STATUS_INVAL; } if (non_blocking) return SANE_STATUS_UNSUPPORTED; return SANE_STATUS_GOOD; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { GT68xx_Scanner *s = handle; DBG (5, "sane_get_select_fd: handle = %p, fd = %p\n", handle, (void *) fd); if (!s->scanning) { DBG (1, "sane_get_select_fd: not scanning\n"); return SANE_STATUS_INVAL; } return SANE_STATUS_UNSUPPORTED; } /* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */ backends-1.3.0/backend/gt68xx.conf.in000066400000000000000000000174161456256263500173270ustar00rootroot00000000000000 # gt68xx.conf: Configuration file for GT68XX based scanners # 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 optionally 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 (e+), 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" # Artec Ultima 2000e+: #override "artec-ultima-2000" #firmware "ePlus2k.usb" #vendor "Artec" #model "Ultima 2000e+" # 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 OpticPro S12 and Nortek MyScan1200 usb 0x07b3 0x040b #vendor "Nortek" #model "MyScan 1200" ############################################################################## # Autodetect Plustek OpticPro S24 usb 0x07b3 0x040e ############################################################################## # Autodetect Plustek OpticSlim M12 and NeatReceipts Scanalizer Professional 2.5 usb 0x07b3 0x0412 #vendor "NeatReceipts" #model "Scanalizer Professional 2.5" ############################################################################## # Iriscan Express 2 usb 0x07b3 0x045f ############################################################################## # Autodetect NeatReceipts Mobile Scanner usb 0x07b3 0x0462 ############################################################################## # Autodetect Plustek OpticSlim 500 Plus usb 0x07b3 0x046e override "plustek-opticslim-500plus" ############################################################################## # Autodetect Plustek OpticSlim 1200 usb 0x07b3 0x0413 ############################################################################## # Autodetect Plustek OpticSlim 2400 usb 0x07b3 0x0422 ############################################################################## # Autodetect Plustek OpticSlim 2400 plus usb 0x07b3 0x0454 model "OpticSlim 2400 Plus" override "plustek-opticslim-2400" ############################################################################## # Autodetect Genius Colorpage SF600 usb 0x0458 0x2021 ############################################################################## # 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 Genius Color Slim 1200 usb 0x0458 0x201E ############################################################################## # Autodetect Visioneer OneTouch 7300 usb 0x04a7 0x0444 ############################################################################## backends-1.3.0/backend/gt68xx.h000066400000000000000000000041411456256263500162130ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2002 Sergey Vlasov 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, see . 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 GT68XX_H #define GT68XX_H #include #include "gt68xx_high.h" #define ENABLE(OPTION) s->opt[OPTION].cap &= ~SANE_CAP_INACTIVE #define DISABLE(OPTION) s->opt[OPTION].cap |= SANE_CAP_INACTIVE #define IS_ACTIVE(OPTION) (((s->opt[OPTION].cap) & SANE_CAP_INACTIVE) == 0) #define GT68XX_CONFIG_FILE "gt68xx.conf" #endif /* not GT68XX_H */ backends-1.3.0/backend/gt68xx_devices.c000066400000000000000000002411721456256263500177170ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2002 Sergey Vlasov Copyright (C) 2002 - 2007 Henning Geinitz 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, see . 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. */ /* Scanner-specific data */ #include "gt68xx_low.h" #include "gt68xx_generic.c" #include "gt68xx_gt6801.c" #include "gt68xx_gt6816.c" static GT68xx_Command_Set mustek_gt6816_command_set = { "mustek-gt6816", /* Name of this command set */ 0x40, /* Request type */ 0x01, /* Request */ 0x200c, /* Memory read - wValue */ 0x200b, /* Memory write - wValue */ 0x2010, /* Send normal command - wValue */ 0x3f40, /* Send normal command - wIndex */ 0x2011, /* Receive normal result - wValue */ 0x3f00, /* Receive normal result - wIndex */ 0x2012, /* Send small command - wValue */ 0x3f40, /* Send small command - wIndex */ 0x2013, /* Receive small result - wValue */ 0x3f00, /* Receive small result - wIndex */ /* activate */ NULL, /* deactivate */ NULL, gt6816_check_firmware, gt6816_download_firmware, gt6816_get_power_status, gt6816_get_ta_status, gt6816_lamp_control, gt6816_is_moving, gt68xx_generic_move_relative, gt6816_carriage_home, /* gt68xx_generic_paperfeed */ NULL, gt68xx_generic_start_scan, gt68xx_generic_read_scanned_data, gt6816_stop_scan, gt68xx_generic_setup_scan, gt68xx_generic_set_afe, gt68xx_generic_set_exposure_time, gt68xx_generic_get_id, /* gt68xx_generic_move_paper */ NULL, /* gt6816_document_present */ NULL }; static GT68xx_Command_Set mustek_gt6816_sheetfed_command_set = { "mustek-gt6816", /* Name of this command set */ 0x40, /* Request type */ 0x01, /* Request */ 0x200c, /* Memory read - wValue */ 0x200b, /* Memory write - wValue */ 0x2010, /* Send normal command - wValue */ 0x3f40, /* Send normal command - wIndex */ 0x2011, /* Receive normal result - wValue */ 0x3f00, /* Receive normal result - wIndex */ 0x2012, /* Send small command - wValue */ 0x3f40, /* Send small command - wIndex */ 0x2013, /* Receive small result - wValue */ 0x3f00, /* Receive small result - wIndex */ /* activate */ NULL, /* deactivate */ NULL, gt6816_check_firmware, gt6816_download_firmware, gt6816_get_power_status, gt6816_get_ta_status, gt6816_lamp_control, gt6816_is_moving, gt68xx_generic_move_relative, /* gt6816_carriage_home */ NULL, gt68xx_generic_paperfeed, gt68xx_generic_start_scan, gt68xx_generic_read_scanned_data, gt6801_stop_scan, gt68xx_generic_setup_scan, gt68xx_generic_set_afe, gt68xx_generic_set_exposure_time, gt68xx_generic_get_id, gt68xx_generic_move_paper, gt6816_document_present }; static GT68xx_Command_Set mustek_gt6801_command_set = { "mustek-gt6801", 0x40, /* Request type */ 0x01, /* Request */ 0x200a, /* Memory read - wValue */ 0x2009, /* Memory write - wValue */ 0x2010, /* Send normal command - wValue */ 0x3f40, /* Send normal command - wIndex */ 0x2011, /* Receive normal result - wValue */ 0x3f00, /* Receive normal result - wIndex */ 0x2012, /* Send small command - wValue */ 0x3f40, /* Send small command - wIndex */ 0x2013, /* Receive small result - wValue */ 0x3f00, /* Receive small result - wIndex */ /* activate */ NULL, /* deactivate */ NULL, gt6801_check_firmware, gt6801_download_firmware, gt6801_get_power_status, /* get_ta_status (FIXME: implement this) */ NULL, gt6801_lamp_control, gt6801_is_moving, /* gt68xx_generic_move_relative *** to be tested */ NULL, gt6801_carriage_home, /* gt68xx_generic_paperfeed */ NULL, gt68xx_generic_start_scan, gt68xx_generic_read_scanned_data, gt6801_stop_scan, gt68xx_generic_setup_scan, gt68xx_generic_set_afe, gt68xx_generic_set_exposure_time, gt68xx_generic_get_id, /* gt68xx_generic_move_paper */ NULL, /* gt6816_document_present */ NULL }; static GT68xx_Command_Set plustek_gt6801_command_set = { "plustek-gt6801", 0x40, /* Request type */ 0x04, /* Request */ 0x200a, /* Memory read - wValue */ 0x2009, /* Memory write - wValue */ 0x2010, /* Send normal command - wValue */ 0x3f40, /* Send normal command - wIndex */ 0x2011, /* Receive normal result - wValue */ 0x3f00, /* Receive normal result - wIndex */ 0x2012, /* Send small command - wValue */ 0x3f40, /* Send small command - wIndex */ 0x2013, /* Receive small result - wValue */ 0x3f00, /* Receive small result - wIndex */ /* activate */ NULL, /* deactivate */ NULL, gt6801_check_plustek_firmware, gt6801_download_firmware, gt6801_get_power_status, /* get_ta_status (FIXME: implement this) */ NULL, gt6801_lamp_control, gt6801_is_moving, /* gt68xx_generic_move_relative *** to be tested */ NULL, gt6801_carriage_home, /* gt68xx_generic_paperfeed */ NULL, gt68xx_generic_start_scan, gt68xx_generic_read_scanned_data, gt6801_stop_scan, gt68xx_generic_setup_scan, gt68xx_generic_set_afe, /* set_exposure_time */ NULL, gt68xx_generic_get_id, /* gt68xx_generic_move_paper */ NULL, /* gt6816_document_present */ NULL }; static GT68xx_Model unknown_model = { "unknown-scanner", /* Name */ "unknown manufacturer", /* Device vendor string */ "unknown device -- use override to select", /* Device model name */ "unknown", /* 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_FALSE, /* Use base_ydpi for all resolutions */ {600, 300, 150, 75, 50, 0}, /* possible x-resolutions */ {1200, 600, 300, 150, 75, 50, 0}, /* possible y-resolutions */ {8, 0}, /* possible depths in gray mode */ {8, 0}, /* possible depths in color mode */ SANE_FIX (0.0), /* Start of scan area in mm (x) */ SANE_FIX (13.0), /* Start of scan area in mm (y) */ SANE_FIX (200.0), /* Size of scan area in mm (x) */ SANE_FIX (280.0), /* Size of scan area in mm (y) */ SANE_FIX (9.0), /* Start of white strip in mm (y) */ SANE_FIX (0.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, 0, 0, /* RGB CCD Line-distance correction in pixel */ 0, /* CCD distcance for CCD with 6 lines) */ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */ {0x14, 0x07, 0x14, 0x07, 0x14, 0x07}, /* Default offset/gain */ {0x157, 0x157, 0x157}, /* Default exposure parameters */ SANE_FIX (2.0), /* Default gamma value */ SANE_TRUE, /* Is this a CIS scanner? */ GT68XX_FLAG_NO_STOP /* Which flags are needed for this scanner? */ /* Standard values for unknown scanner */ }; static GT68xx_Model mustek_2400ta_model = { "mustek-bearpaw-2400-ta", /* Name */ "Mustek", /* Device vendor string */ "BearPaw 2400 TA", /* Device model name */ "A2fw.usb", /* Name of the firmware file */ SANE_FALSE, /* Dynamic allocation flag */ &mustek_gt6816_command_set, /* Command set used by this scanner */ 1200, /* maximum optical sensor resolution */ 2400, /* maximum motor resolution */ 1200, /* base x-res used to calculate geometry */ 1200, /* base y-res used to calculate geometry */ 1200, /* if ydpi is equal or higher, disable backtracking */ SANE_FALSE, /* Use base_ydpi for all resolutions */ {1200, 600, 300, 200, 100, 50, 0}, /* possible x-resolutions */ {2400, 1200, 600, 300, 200, 100, 50, 0}, /* possible y-resolutions */ {16, 12, 8, 0}, /* possible depths in gray mode */ {16, 12, 8, 0}, /* possible depths in color mode */ SANE_FIX (3.67), /* Start of scan area in mm (x) */ SANE_FIX (7.4), /* Start of scan area in mm (y) */ SANE_FIX (219.0), /* Size of scan area in mm (x) */ SANE_FIX (298.0), /* Size of scan area in mm (y) */ SANE_FIX (0.0), /* Start of white strip in mm (y) */ SANE_FIX (0.635), /* Start of black mark in mm (x) */ SANE_FIX (94.0), /* Start of scan area in TA mode in mm (x) */ SANE_FIX (107.0), /* Start of scan area in TA mode in mm (y) */ SANE_FIX (37.0), /* Size of scan area in TA mode in mm (x) */ SANE_FIX (165.0), /* Size of scan area in TA mode in mm (y) */ SANE_FIX (95.0), /* Start of white strip in TA mode in mm (y) */ 0, 24, 48, /* RGB CCD Line-distance correction in pixel */ 0, /* CCD distcance for CCD with 6 lines) */ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */ {0x2a, 0x0c, 0x2e, 0x06, 0x2d, 0x07}, /* 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_SCAN_FROM_HOME /* Which flags are needed for this scanner? */ /* flatbed values tested */ }; static GT68xx_Model mustek_2400taplus_model = { "mustek-bearpaw-2400-ta-plus", /* Name */ "Mustek", /* Device vendor string */ "BearPaw 2400 TA Plus", /* Device model name */ "A2Dfw.usb", /* Name of the firmware file */ SANE_FALSE, /* Dynamic allocation flag */ &mustek_gt6816_command_set, /* Command set used by this scanner */ 1200, /* maximum optical sensor resolution */ 2400, /* maximum motor resolution */ 600, /* base x-res used to calculate geometry */ 1200, /* base y-res used to calculate geometry */ 1200, /* if ydpi is equal or higher, disable backtracking */ SANE_FALSE, /* Use base_ydpi for all resolutions */ {1200, 600, 300, 100, 50, 0}, /* possible x-resolutions */ {2400, 1200, 600, 300, 100, 50, 0}, /* possible y-resolutions */ {8, 0}, /* possible depths in gray mode */ {16, 12, 8, 0}, /* possible depths in color mode */ SANE_FIX (7.41), /* Start of scan area in mm (x) */ SANE_FIX (7.4), /* Start of scan area in mm (y) */ SANE_FIX (217.5), /* Size of scan area in mm (x) */ SANE_FIX (298.0), /* Size of scan area in mm (y) */ SANE_FIX (0.0), /* Start of white strip in mm (y) */ SANE_FIX (5.0), /* Start of black mark in mm (x) */ SANE_FIX (94.0), /* Start of scan area in TA mode in mm (x) */ SANE_FIX (107.0) /* Start of scan area in TA mode in mm (y) */ , SANE_FIX (37.0), /* Size of scan area in TA mode in mm (x) */ SANE_FIX (165.0), /* Size of scan area in TA mode in mm (y) */ SANE_FIX (95.0), /* Start of white strip in TA mode in mm (y) */ 0, 48, 96, /* RGB CCD Line-distance correction in pixel */ 8, /* CCD distcance for CCD with 6 lines) */ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */ {0x2a, 0x0c, 0x2e, 0x06, 0x2d, 0x07}, /* 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_USE_OPTICAL_X | GT68XX_FLAG_SCAN_FROM_HOME /* Which flags are needed for this scanner? */ /* Setup and tested */ }; static GT68xx_Model mustek_2448taplus_model = { "mustek-bearpaw-2448-ta-plus", /* Name */ "Mustek", /* Device vendor string */ "BearPaw 2448 TA Plus", /* Device model name */ "A2Nfw.usb", /* Name of the firmware file */ SANE_FALSE, /* Dynamic allocation flag */ &mustek_gt6816_command_set, /* Command set used by this scanner */ 1200, /* maximum optical sensor resolution */ 2400, /* maximum motor resolution */ 600, /* base x-res used to calculate geometry */ 1200, /* base y-res used to calculate geometry */ 1200, /* if ydpi is equal or higher, disable backtracking */ SANE_FALSE, /* Use base_ydpi for all resolutions */ {1200, 600, 300, 200, 100, 0}, /* possible x-resolutions */ {2400, 1200, 600, 300, 200, 100, 0}, /* possible y-resolutions */ {16, 12, 8, 0}, /* possible depths in gray mode */ {16, 12, 8, 0}, /* possible depths in color mode */ SANE_FIX (7.41), /* Start of scan area in mm (x) */ SANE_FIX (7.4), /* Start of scan area in mm (y) */ SANE_FIX (216.3), /* Size of scan area in mm (x) */ SANE_FIX (297.5), /* Size of scan area in mm (y) */ SANE_FIX (0.0), /* Start of white strip in mm (y) */ SANE_FIX (4.0), /* Start of black mark in mm (x) */ SANE_FIX (94.0), /* Start of scan area in TA mode in mm (x) */ SANE_FIX (105.0), /* Start of scan area in TA mode in mm (y) */ SANE_FIX (36.0), /* Size of scan area in TA mode in mm (x) */ SANE_FIX (165.0), /* Size of scan area in TA mode in mm (y) */ SANE_FIX (95.0), /* Start of white strip in TA mode in mm (y) */ 0, 48, 96, /* RGB CCD Line-distance correction in pixel */ 8, /* CCD distcance for CCD with 6 lines) */ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */ {0x2a, 0x0c, 0x2e, 0x06, 0x2d, 0x07}, /* 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_NO_STOP /* Which flags are needed for this scanner? */ /* Based on data from Jakub Dvořák . */ }; static GT68xx_Model mustek_1200ta_model = { "mustek-bearpaw-1200-ta", /* Name */ "Mustek", /* Device vendor string */ "BearPaw 1200 TA", /* Device model name */ "A1fw.usb", /* 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, 150, 75, 50, 0}, /* possible x-resolutions */ {1200, 600, 300, 150, 75, 50, 0}, /* possible y-resolutions */ {16, 12, 8, 0}, /* possible depths in gray mode */ {16, 12, 8, 0}, /* possible depths in color mode */ SANE_FIX (8.4), /* Start of scan area in mm (x) */ SANE_FIX (7.5), /* Start of scan area in mm (y) */ SANE_FIX (220.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 (6.9), /* Start of black mark in mm (x) */ SANE_FIX (98.0), /* Start of scan area in TA mode in mm (x) */ SANE_FIX (108.0), /* Start of scan area in TA mode in mm (y) */ SANE_FIX (37.0), /* Size of scan area in TA mode in mm (x) */ SANE_FIX (163.0), /* Size of scan area in TA mode in mm (y) */ SANE_FIX (95.0), /* Start of white strip in TA mode in mm (y) */ 32, 16, 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 */ {0x2a, 0x0c, 0x2e, 0x06, 0x2d, 0x07}, /* Default offset/gain */ {0x157, 0x157, 0x157}, /* Default exposure parameters */ SANE_FIX (2.0), /* Default gamma value */ SANE_FALSE, /* Is this a CIS scanner? */ 0 /* Which flags are needed for this scanner? */ /* Setup for 1200 TA */ }; static GT68xx_Model mustek_1200cuplus_model = { "mustek-bearpaw-1200-cu-plus", /* Name */ "Mustek", /* Device vendor string */ "Bearpaw 1200 CU Plus", /* Device model name */ "PS1Dfw.usb", /* 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_FALSE, /* Use base_ydpi for all resolutions */ {600, 300, 150, 75, 50, 0}, /* possible x-resolutions */ {1200, 600, 300, 150, 75, 50, 0}, /* possible y-resolutions */ {16, 12, 8, 0}, /* possible depths in gray mode */ {16, 12, 8, 0}, /* possible depths in color mode */ SANE_FIX (0.0), /* Start of scan area in mm (x) */ SANE_FIX (13.0), /* Start of scan area in mm (y) */ SANE_FIX (217.0), /* Size of scan area in mm (x) */ SANE_FIX (299.0), /* Size of scan area in mm (y) */ SANE_FIX (9.0), /* Start of white strip in mm (y) */ SANE_FIX (0.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, 0, 0, /* RGB CCD Line-distance correction in pixel */ 0, /* CCD distcance for CCD with 6 lines) */ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */ {0x14, 0x07, 0x14, 0x07, 0x14, 0x07}, /* Default offset/gain */ {0x157, 0x157, 0x157}, /* Default exposure parameters */ SANE_FIX (2.0), /* Default gamma value */ SANE_TRUE, /* Is this a CIS scanner? */ GT68XX_FLAG_NO_STOP /* Which flags are needed for this scanner? */ /* Tested by Hamersky Robert r.hamersky at utanet.at */ }; static GT68xx_Model mustek_1200cuplus2_model = { "mustek-bearpaw-1200-cu-plus-2", /* Name */ "Mustek", /* Device vendor string */ "Bearpaw 1200 CU Plus", /* Device model name */ "PS1Gfw.usb", /* 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_FALSE, /* Use base_ydpi for all resolutions */ {600, 300, 150, 75, 50, 0}, /* possible x-resolutions */ {1200, 600, 300, 150, 75, 50, 0}, /* possible y-resolutions */ {16, 12, 8, 0}, /* possible depths in gray mode */ {16, 12, 8, 0}, /* possible depths in color mode */ SANE_FIX (4.0), /* Start of scan area in mm (x) */ SANE_FIX (13.0), /* Start of scan area in mm (y) */ SANE_FIX (217.0), /* Size of scan area in mm (x) */ SANE_FIX (299.0), /* Size of scan area in mm (y) */ SANE_FIX (5.0), /* Start of white strip in mm (y) */ SANE_FIX (0.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, 0, 0, /* RGB CCD Line-distance correction in pixel */ 0, /* CCD distcance for CCD with 6 lines) */ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */ {0x19, 0x03, 0x1b, 0x05, 0x19, 0x03}, /* Default offset/gain */ {0xb0, 0xb0, 0xb0}, /* Default exposure parameters */ SANE_FIX (1.5), /* Default gamma value */ SANE_TRUE, /* Is this a CIS scanner? */ GT68XX_FLAG_NO_STOP /* Which flags are needed for this scanner? */ /* Tested by hmg */ }; static GT68xx_Model mustek_2400cuplus_model = { "mustek-bearpaw-2400-cu-plus", /* Name */ "Mustek", /* Device vendor string */ "BearPaw 2400 CU Plus", /* Device model name */ "PS2Dfw.usb", /* Name of the firmware file */ SANE_FALSE, /* Dynamic allocation flag */ &mustek_gt6816_command_set, /* Command set used by this scanner */ 1200, /* maximum optical sensor resolution */ 2400, /* maximum motor resolution */ 1200, /* base x-res used to calculate geometry */ 1200, /* base y-res used to calculate geometry */ 1200, /* if ydpi is equal or higher, disable backtracking */ SANE_FALSE, /* Use base_ydpi for all resolutions */ {1200, 600, 300, 200, 150, 100, 50, 0}, /* possible x-resolutions */ {2400, 1200, 600, 300, 200, 150, 100, 50, 0}, /* possible y-resolutions */ {16, 12, 8, 0}, /* possible depths in gray mode */ {16, 12, 8, 0}, /* possible depths in color mode */ SANE_FIX (2.0), /* Start of scan area in mm (x) */ SANE_FIX (13.0), /* Start of scan area in mm (y) */ SANE_FIX (217.0), /* Size of scan area in mm (x) */ SANE_FIX (300.0), /* Size of scan area in mm (y) */ SANE_FIX (4.0), /* Start of white strip in mm (y) */ SANE_FIX (0.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, 0, 0, /* RGB CCD Line-distance correction in pixel */ 0, /* CCD distcance for CCD with 6 lines) */ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */ {0x15, 0x08, 0x12, 0x05, 0x12, 0x05}, /* Default offset/gain */ {0x2a0, 0x1ab, 0x10d}, /* Default exposure parameters */ SANE_FIX (1.5), /* Default gamma value */ SANE_TRUE, /* Is this a CIS scanner? */ GT68XX_FLAG_NO_STOP /* Which flags are needed for this scanner? */ /* Setup and tested */ }; /* Seems that Mustek ScanExpress 1200 UB Plus, the Mustek BearPaw 1200 CU * and lots of other scanners have the same USB identifier. */ static GT68xx_Model mustek_1200cu_model = { "mustek-bearpaw-1200-cu", /* Name */ "Mustek", /* Device vendor string */ "BearPaw 1200 CU", /* Device model name */ "PS1fw.usb", /* Name of the firmware file */ SANE_FALSE, /* Dynamic allocation flag */ &mustek_gt6801_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 */ 600, /* if ydpi is equal or higher, disable backtracking */ SANE_TRUE, /* Use base_ydpi for all resolutions */ {600, 300, 150, 75, 50, 0}, /* possible x-resolutions */ {1200, 600, 300, 150, 75, 50, 0}, /* possible y-resolutions */ {12, 8, 0}, /* possible depths in gray mode */ {12, 8, 0}, /* possible depths in color mode */ SANE_FIX (0.0), /* Start of scan area in mm (x) */ SANE_FIX (13.0), /* Start of scan area in mm (y) */ SANE_FIX (217.0), /* Size of scan area in mm (x) */ SANE_FIX (299.0), /* Size of scan area in mm (y) */ SANE_FIX (4.2), /* Start of white strip in mm (y) */ SANE_FIX (0.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, 0, 0, /* RGB CCD Line-distance correction in pixel */ 0, /* CCD distcance for CCD with 6 lines) */ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */ {0x13, 0x04, 0x15, 0x06, 0x0f, 0x02}, /* Default offset/gain */ {0x150, 0x150, 0x150}, /* Default exposure parameters */ SANE_FIX (2.0), /* Default gamma value */ SANE_TRUE, /* Is this a CIS scanner? */ 0 /* Which flags are needed for this scanner? */ /* Setup and tested */ }; static GT68xx_Model mustek_scanexpress1200ubplus_model = { "mustek-scanexpress-1200-ub-plus", /* Name */ "Mustek", /* Device vendor string */ "ScanExpress 1200 UB Plus", /* Device model name */ "SBfw.usb", /* Name of the firmware file */ SANE_FALSE, /* Dynamic allocation flag */ &mustek_gt6801_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, 150, 75, 50, 0}, /* possible x-resolutions */ {1200, 600, 300, 150, 75, 50, 0}, /* possible y-resolutions */ {12, 8, 0}, /* possible depths in gray mode */ {12, 8, 0}, /* possible depths in color mode */ SANE_FIX (0.0), /* Start of scan area in mm (x) */ SANE_FIX (15.5), /* Start of scan area in mm (y) */ SANE_FIX (217.0), /* Size of scan area in mm (x) */ SANE_FIX (299.0), /* Size of scan area in mm (y) */ SANE_FIX (6.5), /* Start of white strip in mm (y) */ SANE_FIX (0.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, 0, 0, /* RGB CCD Line-distance correction in pixel */ 0, /* CCD distcance for CCD with 6 lines) */ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */ {0x0f, 0x01, 0x15, 0x06, 0x13, 0x04}, /* Default offset/gain */ {0x157, 0x157, 0x157}, /* Default exposure parameters */ SANE_FIX (2.0), /* Default gamma value */ SANE_TRUE, /* Is this a CIS scanner? */ 0 /* Which flags are needed for this scanner? */ /* Setup and tested */ }; static GT68xx_Model mustek_scanexpress1248ub_model = { "mustek-scanexpress-1248-ub", /* Name */ "Mustek", /* Device vendor string */ "ScanExpress 1248 UB", /* Device model name */ "SBSfw.usb", /* 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_FALSE, /* Use base_ydpi for all resolutions */ {600, 300, 150, 75, 50, 0}, /* possible x-resolutions */ {1200, 600, 300, 150, 75, 50, 0}, /* possible y-resolutions */ {16, 12, 8, 0}, /* possible depths in gray mode */ {16, 12, 8, 0}, /* possible depths in color mode */ SANE_FIX (4.0), /* Start of scan area in mm (x) */ SANE_FIX (14.5), /* Start of scan area in mm (y) */ SANE_FIX (217.0), /* Size of scan area in mm (x) */ SANE_FIX (299.0), /* Size of scan area in mm (y) */ SANE_FIX (9.0), /* Start of white strip in mm (y) */ SANE_FIX (0.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, 0, 0, /* RGB CCD Line-distance correction in pixel */ 0, /* CCD distcance for CCD with 6 lines) */ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */ {0x1e, 0x08, 0x21, 0x0d, 0x1b, 0x05}, /* Default offset/gain */ {0x50, 0x50, 0x50}, /* Default exposure parameters */ SANE_FIX (1.5), /* Default gamma value */ SANE_TRUE, /* Is this a CIS scanner? */ GT68XX_FLAG_NO_STOP /* Which flags are needed for this scanner? */ /* tested by hmg */ }; static GT68xx_Model artec_ultima2000_model = { "artec-ultima-2000", /* Name */ "Artec", /* Device vendor string */ "Ultima 2000", /* Device model name */ "Gt680xfw.usb", /* Name of the firmware file */ SANE_FALSE, /* Dynamic allocation flag */ &mustek_gt6801_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, 200, 150, 100, 75, 50, 0}, /* possible x-resolutions */ {600, 300, 200, 150, 100, 75, 50, 0}, /* possible y-resolutions */ {12, 8, 0}, /* possible depths in gray mode */ {12, 8, 0}, /* possible depths in color mode */ SANE_FIX (2.0), /* Start of scan area in mm (x) */ SANE_FIX (7.0), /* 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 (0.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, 0, 0, /* RGB CCD Line-distance correction in pixel */ 0, /* CCD distcance for CCD with 6 lines) */ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */ {0x0f, 0x01, 0x15, 0x06, 0x13, 0x04}, /* Default offset/gain */ {0x157, 0x157, 0x157}, /* Default exposure parameters */ SANE_FIX (2.0), /* Default gamma value */ SANE_TRUE, /* Is this a CIS scanner? */ GT68XX_FLAG_MIRROR_X | GT68XX_FLAG_MOTOR_HOME | GT68XX_FLAG_OFFSET_INV /* Which flags are needed for this scanner? */ /* Setup for Cytron TCM MD 9385 */ }; static GT68xx_Model plustek_opticslim500plus_model = { "plustek-opticslim-500plus", /* Name */ "Plustek", /* Device vendor string */ "OpticSlim 500 Plus", /* Device model name */ "cis3R5B1.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_FALSE, /* Use base_ydpi for all resolutions */ {600, 300, 150, 75, 50, 0}, /* possible x-resolutions */ {1200, 600, 300, 150, 75, 50, 0},/* possible y-resolutions */ {16, 8, 0}, /* possible depths in gray mode */ {16, 8, 0}, /* possible depths in color mode */ SANE_FIX (1.0), /* Start of scan area in mm (x) */ SANE_FIX (9.5), /* Start of scan area in mm (y) */ SANE_FIX (218.0), /* Size of scan area in mm (x) */ SANE_FIX (142.0), /* Size of scan area in mm (y) */ SANE_FIX (0.0), /* Start of white strip in mm (y) */ SANE_FIX (0.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, 0, 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 */ {0x32, 0x02, 0x26, 0x07, 0x26, 0x09}, /* Default offset/gain */ {0x127, 0x127, 0x127}, /* Default exposure parameters */ SANE_FIX (1.5), /* Default gamma value */ SANE_TRUE, /* Is this a CIS scanner? */ GT68XX_FLAG_NO_CALIBRATE /* Which flags are needed for this scanner? */ }; static GT68xx_Model mustek_2400cu_model = { "mustek-bearpaw-2400-cu", /* Name */ "Mustek", /* Device vendor string */ "BearPaw 2400 CU", /* Device model name */ "PS2fw.usb", /* Name of the firmware file */ SANE_FALSE, /* Dynamic allocation flag */ &mustek_gt6801_command_set, /* Command set used by this scanner */ 1200, /* maximum optical sensor resolution */ 2400, /* maximum motor resolution */ 1200, /* base x-res used to calculate geometry */ 1200, /* base y-res used to calculate geometry */ 1200, /* if ydpi is equal or higher, disable backtracking */ SANE_TRUE, /* Use base_ydpi for all resolutions */ {1200, 600, 300, 150, 100, 50, 0}, /* possible x-resolutions */ {2400, 1200, 600, 300, 150, 100, 50, 0}, /* possible y-resolutions */ {12, 8, 0}, /* possible depths in gray mode */ {12, 8, 0}, /* possible depths in color mode */ SANE_FIX (0.0), /* Start of scan area in mm (x) */ SANE_FIX (13.5), /* Start of scan area in mm (y) */ SANE_FIX (215.0), /* Size of scan area in mm (x) */ SANE_FIX (296.9), /* Size of scan area in mm (y) */ SANE_FIX (5.5), /* Start of white strip in mm (y) */ SANE_FIX (0.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.9), /* 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, 0, 0, /* RGB CCD Line-distance correction in pixel */ 0, /* CCD distcance for CCD with 6 lines) */ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */ {0x0f, 0x01, 0x15, 0x06, 0x13, 0x04}, /* Default offset/gain */ {0x157, 0x157, 0x157}, /* Default exposure parameters */ SANE_FIX (2.0), /* Default gamma value */ SANE_TRUE, /* Is this a CIS scanner? */ 0 /* Which flags are needed for this scanner? */ /* basically tested, details may need tweaking */ }; static GT68xx_Model mustek_scanexpress2400usb_model = { "mustek-scanexpress-2400-usb", /* Name */ "Mustek", /* Device vendor string */ "ScanExpress 2400 USB", /* Device model name */ "P9fw.usb", /* Name of the firmware file */ SANE_FALSE, /* Dynamic allocation flag */ &mustek_gt6801_command_set, /* Command set used by this scanner */ 1200, /* maximum optical sensor resolution */ 1200, /* maximum motor resolution */ 1200, /* base x-res used to calculate geometry */ 1200, /* 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, 100, 50, 0}, /* possible x-resolutions */ {1200, 600, 300, 150, 100, 50, 0}, /* possible y-resolutions */ {12, 8, 0}, /* possible depths in gray mode */ {12, 8, 0}, /* possible depths in color mode */ SANE_FIX (5.0), /* Start of scan area in mm (x) */ SANE_FIX (12.0), /* Start of scan area in mm (y) */ SANE_FIX (224.0), /* Size of scan area in mm (x) */ SANE_FIX (300.0), /* Size of scan area in mm (y) */ SANE_FIX (0.0), /* Start of white strip in mm (y) */ SANE_FIX (7.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) */ 24, 12, 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 */ {0x12, 0x06, 0x0e, 0x03, 0x19, 0x25}, /* 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_SE_2400 /* Which flags are needed for this scanner? */ /* only partly tested, from "Fan Dan" */ }; static GT68xx_Model mustek_a3usb_model = { "mustek-scanexpress-a3-usb", /* Name */ "Mustek", /* Device vendor string */ "ScanExpress A3 USB", /* Device model name */ "A32fw.usb", /* Name of the firmware file */ SANE_FALSE, /* Dynamic allocation flag */ &mustek_gt6816_command_set, /* Command set used by this scanner */ 300, /* maximum optical sensor resolution */ 600, /* maximum motor resolution */ 300, /* base x-res used to calculate geometry */ 300, /* base y-res used to calculate geometry */ 1200, /* if ydpi is equal or higher, disable backtracking */ SANE_TRUE, /* Use base_ydpi for all resolutions */ {300, 150, 75, 50, 0}, /* possible x-resolutions */ {600, 300, 150, 75, 50, 0}, /* possible y-resolutions */ {12, 8, 0}, /* possible depths in gray mode */ {12, 8, 0}, /* possible depths in color mode */ SANE_FIX (7.0), /* Start of scan area in mm (x) */ SANE_FIX (11.5), /* Start of scan area in mm (y) */ SANE_FIX (297.0), /* Size of scan area in mm (x) */ SANE_FIX (433.0), /* Size of scan area in mm (y) */ SANE_FIX (2.4), /* Start of white strip in mm (y) */ SANE_FIX (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, 5, 5, /* RGB CCD Line-distance correction in pixel */ 0, /* CCD distcance for CCD with 6 lines) */ COLOR_ORDER_RGB, /* Order of the CCD/CIS colors */ {0x14, 0x05, 0x12, 0x05, 0x17, 0x0c}, /* Default offset/gain */ {0x157, 0x157, 0x157}, /* Default exposure parameters */ SANE_FIX (1.5), /* Default gamma value */ SANE_TRUE, /* Is this a CIS scanner? */ GT68XX_FLAG_CIS_LAMP /* Which flags are needed for this scanner? */ /* Tested by hmg. This scanner is a bit strange as it uses a CIS sensor but it also has a lamp. So the lamp needs to be heated but CIS mode must be used for scanning and calibration. There is no TA for that scanner */ }; static GT68xx_Model lexmark_x73_model = { "lexmark-x73", /* Name */ "Lexmark", /* Device vendor string */ "X73", /* Device model name */ "OSLO3071b2.usb", /* 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, 150, 75, 0}, /* possible x-resolutions */ {600, 300, 150, 75, 0}, /* possible y-resolutions */ {16, 12, 8, 0}, /* possible depths in gray mode */ {16, 12, 8, 0}, /* possible depths in color mode */ SANE_FIX (6.519), /* Start of scan area in mm (x) */ SANE_FIX (12.615), /* Start of scan area in mm (y) */ SANE_FIX (220.0), /* Size of scan area in mm (x) */ SANE_FIX (297.0), /* Size of scan area in mm (y) */ SANE_FIX (1.0), /* Start of white strip in mm (y) */ SANE_FIX (0.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) */ 32, 16, 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 */ {0x14, 0x07, 0x14, 0x07, 0x14, 0x07}, /* Default offset/gain */ {0x157, 0x157, 0x157}, /* Default exposure parameters */ SANE_FIX (2.0), /* Default gamma value */ SANE_FALSE, /* Is this a CIS scanner? */ 0 /* Which flags are needed for this scanner? */ /* When using automatic gain pictures are too dark. Only some ad hoc tests for lexmark x70 were done so far. WARNING: Don't use the Full scan option with the above settings, otherwise the sensor may bump at the end of the sledge and the scanner may be damaged! */ }; static GT68xx_Model plustek_op1248u_model = { "plustek-op1248u", /* Name */ "Plustek", /* Device vendor string */ "OpticPro 1248U", /* Device model name */ "ccd548.fw", /* Name of the firmware file */ SANE_FALSE, /* Dynamic allocation flag */ &plustek_gt6801_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 (216.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_BGR, /* Order of the CCD/CIS colors */ {0x1e, 0x24, 0x1d, 0x1f, 0x1d, 0x20}, /* 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 /* Which flags are needed for this scanner? */ /* tested */ }; static GT68xx_Model plustek_u16b_model = { "plustek-u16b", /* Name */ "Plustek", /* Device vendor string */ "OpticPro U16B", /* Device model name */ "ccd68861.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 */ {16, 8, 0}, /* possible depths in gray mode */ {16, 12, 8, 0}, /* possible depths in color mode */ SANE_FIX (5.5), /* Start of scan area in mm (x) */ SANE_FIX (8.5), /* Start of scan area in mm (y) */ SANE_FIX (216.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_BGR, /* Order of the CCD/CIS colors */ {0x18, 0x16, 0x16, 0x0f, 0x17, 0x11}, /* 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_NO_POWER_STATUS | GT68XX_FLAG_NO_LINEMODE /* Which flags are needed for this scanner? */ /* Tested with a U16B by Henning Meier-Geinitz. 600 dpi is maximum vertically. Line mode does not work. That's a hardware/firmware 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_OFFSET_INV | GT68XX_FLAG_ALWAYS_LINEMODE /* Which flags are needed for this scanner? */ /* Seems to work */ }; 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 /* Which flags are needed for this scanner? */ /* Works (tested by Filip Kaluza). Based on genius Colorpage Vivid 1200 X. */ }; static GT68xx_Model genius_vivid4_model = { "genius-colorpage-vivid4", /* Name */ "Genius", /* Device vendor string */ "ColorPage Vivid 4", /* Device model name */ "ccd68861.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 */ {16, 8, 0}, /* possible depths in gray mode */ {16, 12, 8, 0}, /* possible depths in color mode */ SANE_FIX (5.5), /* Start of scan area in mm (x) */ SANE_FIX (8.5), /* Start of scan area in mm (y) */ SANE_FIX (216.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_BGR, /* Order of the CCD/CIS colors */ {0x18, 0x16, 0x16, 0x0f, 0x17, 0x11}, /* 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_NO_POWER_STATUS | GT68XX_FLAG_NO_LINEMODE /* Which flags are needed for this scanner? */ /* This scanner seems to be very similar to Plustelk U16B and is reported to work. */ }; static GT68xx_Model genius_vivid3x_model = { "genius-colorpage-vivid3x", /* Name */ "Genius", /* Device vendor string */ "Colorpage Vivid3x", /* Device model name */ "ccd548.fw", /* Name of the firmware file */ SANE_FALSE, /* Dynamic allocation flag */ &plustek_gt6801_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_BGR, /* 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_OFFSET_INV | GT68XX_FLAG_ALWAYS_LINEMODE /* Which flags are needed for this scanner? */ /* Tested to some degree, based on the Plustek OpticPro 1248U */ }; static GT68xx_Model genius_vivid4x_model = { "genius-colorpage-vivid4x", /* Name */ "Genius", /* Device vendor string */ "Colorpage Vivid4x", /* 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_OFFSET_INV | GT68XX_FLAG_ALWAYS_LINEMODE /* Which flags are needed for this scanner? */ /* Is reported to work, copied from 3x, some values from Claudio Filho */ }; static GT68xx_Model genius_vivid4xe_model = { "genius-colorpage-vivid4xe", /* Name */ "Genius", /* Device vendor string */ "Colorpage Vivid4xe", /* 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_OFFSET_INV | GT68XX_FLAG_ALWAYS_LINEMODE /* Which flags are needed for this scanner? */ /* tested a bit */ }; static GT68xx_Model genius_vivid3xe_model = { "genius-colorpage-vivid3xe", /* Name */ "Genius", /* Device vendor string */ "Colorpage Vivid3xe", /* Device model name */ "ccd548.fw", /* Name of the firmware file */ SANE_FALSE, /* Dynamic allocation flag */ &plustek_gt6801_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_BGR, /* 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_OFFSET_INV | GT68XX_FLAG_ALWAYS_LINEMODE /* Which flags are needed for this scanner? */ /* mostly untested, based on the Genius Vivid3x */ }; static GT68xx_Model genius_vivid1200x_model = { "genius-colorpage-vivid-1200-x", /* Name */ "Genius", /* Device vendor string */ "Colorpage Vivid 1200 X", /* 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 /* Which flags are needed for this scanner? */ /* Tested. */ }; static GT68xx_Model genius_vivid1200xe_model = { "genius-colorpage-vivid-1200-xe", /* Name */ "Genius", /* Device vendor string */ "Colorpage Vivid 1200 XE", /* 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 /* Which flags are needed for this scanner? */ /* Tested by hmg */ }; static GT68xx_Model iriscan_express_2_model = { "iriscan-express-2", /* Name */ "Iris", /* Device vendor string */ "Iriscan Express 2", /* Device model name */ "cism216.fw", /* Name of the firmware file */ SANE_FALSE, /* Dynamic allocation flag */ &mustek_gt6816_sheetfed_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_FALSE, /* Use base_ydpi for all resolutions */ /* values based on analyze of the firmware */ {600, 400, 300, 200, 150, 100, 50, 0}, /* possible x-resolutions */ {1200, 600, 400, 300, 200, 150, 100, 50, 0}, /* possible y-resolutions */ {16, 8, 0}, /* possible depths in gray mode */ {16, 8, 0}, /* possible depths in color mode */ SANE_FIX (0.0), /* Start of scan area in mm (x) */ SANE_FIX (1.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 (18.0), /* Start of white strip in mm (y) */ SANE_FIX (21.72), /* 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, 0, 0, /* RGB CCD Line-distance correction in pixel */ 0, /* CCD distcance for CCD with 6 lines) */ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */ {0x22, 0x0c, 0x22, 0x0a, 0x24, 0x09}, /* Default offset/gain */ {0x157, 0x157, 0x157}, /* Default exposure parameters */ SANE_FIX (2.0), /* Default gamma value */ SANE_TRUE, /* Is this a CIS scanner? */ GT68XX_FLAG_NO_POWER_STATUS | GT68XX_FLAG_SHEET_FED | GT68XX_FLAG_OFFSET_INV | GT68XX_FLAG_HAS_CALIBRATE }; static GT68xx_Model plustek_opticslim_m12_model = { "plustek-opticslim-m12", /* Name */ "Plustek", /* Device vendor string */ "OpticSlim M12", /* Device model name */ "cism216.fw", /* Name of the firmware file */ SANE_FALSE, /* Dynamic allocation flag */ &mustek_gt6816_sheetfed_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_FALSE, /* Use base_ydpi for all resolutions */ {600, 400, 300, 200, 150, 100, 50, 0}, /* possible x-resolutions */ {1200, 600, 400, 300, 200, 150, 100, 50, 0}, /* possible y-resolutions */ {16, 8, 0}, /* possible depths in gray mode */ {16, 8, 0}, /* possible depths in color mode */ SANE_FIX (0.0), /* Start of scan area in mm (x) */ SANE_FIX (1.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 (18.0), /* Start of white strip in mm (y) */ SANE_FIX (21.72), /* 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, 0, 0, /* RGB CCD Line-distance correction in pixel */ 0, /* CCD distcance for CCD with 6 lines) */ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */ {0x24, 0x0a, 0x23, 0x0f, 0x23, 0x0b}, /* Default offset/gain */ {0x157, 0x157, 0x157}, /* Default exposure parameters */ SANE_FIX (2.0), /* Default gamma value */ SANE_TRUE, /* Is this a CIS scanner? */ GT68XX_FLAG_NO_POWER_STATUS | GT68XX_FLAG_SHEET_FED | GT68XX_FLAG_OFFSET_INV | GT68XX_FLAG_HAS_CALIBRATE }; static GT68xx_Model genius_sf600_model = { "genius-SF600", /* Name */ "Genius", /* Device vendor string */ "ColorPage SF600", /* Device model name */ "cism216.fw", /* Name of the firmware file */ SANE_FALSE, /* Dynamic allocation flag */ &mustek_gt6816_sheetfed_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_FALSE, /* Use base_ydpi for all resolutions */ {600, 300, 200, 150, 100, 0}, /* possible x-resolutions */ {600, 300, 200, 150, 100, 0}, /* possible y-resolutions */ {16, 8, 0}, /* possible depths in gray mode */ {16, 8, 0}, /* possible depths in color mode */ SANE_FIX (0.0), /* Start of scan area in mm (x) */ SANE_FIX (1.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 (18.0), /* Start of white strip in mm (y) */ SANE_FIX (21.72), /* 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, 0, 0, /* RGB CCD Line-distance correction in pixel */ 0, /* CCD distcance for CCD with 6 lines) */ COLOR_ORDER_BGR, /* Order of the CCD/CIS colors */ {0x24, 0x0a, 0x23, 0x0f, 0x23, 0x0b}, /* Default offset/gain */ {0x157, 0x157, 0x157}, /* Default exposure parameters */ SANE_FIX (2.0), /* Default gamma value */ SANE_TRUE, /* Is this a CIS scanner? */ GT68XX_FLAG_NO_POWER_STATUS | GT68XX_FLAG_UNTESTED | GT68XX_FLAG_SHEET_FED | GT68XX_FLAG_OFFSET_INV | GT68XX_FLAG_HAS_CALIBRATE | GT68XX_FLAG_NO_STOP }; /* Untested but should work according to Ryan Reading . Based on Plustek M12 */ static GT68xx_Model plustek_opticslim1200_model = { "plustek-opticslim-1200", /* Name */ "Plustek", /* Device vendor string */ "OpticSlim 1200", /* Device model name */ "cism216.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_FALSE, /* Use base_ydpi for all resolutions */ {600, 300, 150, 75, 50, 0}, /* possible x-resolutions */ {1200, 600, 300, 150, 75, 50, 0}, /* possible y-resolutions */ {16, 8, 0}, /* possible depths in gray mode */ {16, 8, 0}, /* possible depths in color mode */ SANE_FIX (1.0), /* Start of scan area in mm (x) */ SANE_FIX (9.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 (140.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, 0, 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 */ {0x15, 0x09, 0x18, 0x11, 0x16, 0x0c}, /* Default offset/gain */ {0x157, 0x157, 0x157}, /* Default exposure parameters */ SANE_FIX (2.0), /* Default gamma value */ SANE_TRUE, /* Is this a CIS scanner? */ 0 }; static GT68xx_Model plustek_opticslim2400_model = { "plustek-opticslim-2400", /* Name */ "Plustek", /* Device vendor string */ "OpticSlim 2400", /* Device model name */ "cis3R5B1.fw", /* Name of the firmware file */ SANE_FALSE, /* Dynamic allocation flag */ &mustek_gt6816_command_set, /* Command set used by this scanner */ 1200, /* maximum optical sensor resolution */ 2400, /* maximum motor resolution */ 1200, /* base x-res used to calculate geometry */ 1200, /* base y-res used to calculate geometry */ 1200, /* if ydpi is equal or higher, disable backtracking */ SANE_FALSE, /* Use base_ydpi for all resolutions */ {1200, 600, 300, 150, 100, 50, 0}, /* possible x-resolutions */ {1200, 600, 300, 150, 100, 50, 0}, /* possible y-resolutions */ {12, 8, 0}, /* possible depths in gray mode */ {12, 8, 0}, /* possible depths in color mode */ SANE_FIX (1.0), /* Start of scan area in mm (x) */ SANE_FIX (9.5), /* Start of scan area in mm (y) */ SANE_FIX (217.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 (0.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, 0, 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 */ {0x15, 0x09, 0x18, 0x11, 0x16, 0x0c}, /* Default offset/gain */ {0x300, 0x300, 0x300}, /* Default exposure parameters */ SANE_FIX (1.5), /* Default gamma value */ SANE_TRUE, /* Is this a CIS scanner? */ 0 /* By Detlef Gausepohl . Fixed and tested by hmg. */ }; static GT68xx_Model visioneer_onetouch_7300_model = { "visioneer-onetouch-7300", /* Name */ "Visioneer", /* Device vendor string */ "OneTouch 7300", /* Device model name */ "Cis3r5b1.fw", /* Name of the firmware file */ SANE_FALSE, /* Dynamic allocation flag */ &mustek_gt6816_command_set, /* Command set used by this scanner */ 1200, /* maximum optical sensor resolution */ 1200, /* maximum motor resolution */ 1200, /* base x-res used to calculate geometry */ 1200, /* base y-res used to calculate geometry */ 1200, /* if ydpi is equal or higher, disable backtracking */ SANE_FALSE, /* Use base_ydpi for all resolutions */ {1200, 600, 300, 150, 100, 50, 0}, /* possible x-resolutions */ {1200, 600, 300, 150, 100, 50, 0}, /* possible y-resolutions */ {12, 8, 0}, /* possible depths in gray mode */ {12, 8, 0}, /* possible depths in color mode */ SANE_FIX (1.0), /* Start of scan area in mm (x) */ SANE_FIX (9.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 (140.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, 0, 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 */ {0x15, 0x09, 0x18, 0x11, 0x16, 0x0c}, /* Default offset/gain */ {0x80, 0x80, 0x80}, /* Default exposure parameters */ SANE_FIX (1.5), /* Default gamma value */ SANE_TRUE, /* Is this a CIS scanner? */ 0 }; /* Tested by Jason Novek. Based on Plustek OpticSlim 2400. */ static GT68xx_Model genius_colorpageslim_1200_model = { "genius-colorpageslim-1200", /* Name */ "Genius", /* Device vendor string */ "ColorPage Slim 1200", /* Device model name */ "Cis3r5b1.fw", /* Name of the firmware file */ SANE_FALSE, /* Dynamic allocation flag */ &mustek_gt6816_command_set, /* Command set used by this scanner */ 1200, /* maximum optical sensor resolution */ 2400, /* maximum motor resolution */ 1200, /* base x-res used to calculate geometry */ 1200, /* base y-res used to calculate geometry */ 1200, /* if ydpi is equal or higher, disable backtracking */ SANE_FALSE, /* Use base_ydpi for all resolutions */ {1200, 600, 300, 150, 100, 50, 0}, /* possible x-resolutions */ {2400,1200, 600, 300, 150, 100, 50, 0}, /* possible y-resolutions */ {16, 12, 8, 0}, /* possible depths in gray mode */ {16, 12, 8, 0}, /* possible depths in color mode */ SANE_FIX (0.5), /* Start of scan area in mm (x) */ SANE_FIX (8.0), /* 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 (9.5), /* 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 (0.0), /* Size of scan area in TA mode in mm (x) */ SANE_FIX (0.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, 0, 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 */ {0x19, 0x1a, 0x18, 0x14, 0x18, 0x12}, /* Default offset/gain */ {0x548, 0x513, 0x48d}, /* Default exposure parameters */ SANE_FIX (1.5), /* Default gamma value */ SANE_TRUE, /* Is this a CIS scanner? */ GT68XX_FLAG_ALWAYS_LINEMODE | GT68XX_FLAG_SE_2400 }; /* tested by Aleksey Nedorezov */ static GT68xx_USB_Device_Entry gt68xx_usb_device_list[] = { {0x10000, 0x10000, &unknown_model}, /* used for yet unknown scanners */ {0x055f, 0x0218, &mustek_2400ta_model}, {0x055f, 0x0219, &mustek_2400taplus_model}, {0x055f, 0x021c, &mustek_1200cuplus_model}, {0x055f, 0x021b, &mustek_1200cuplus2_model}, {0x055f, 0x021d, &mustek_2400cuplus_model}, {0x055f, 0x021e, &mustek_1200ta_model}, {0x055f, 0x021f, &mustek_scanexpress1248ub_model}, {0x05d8, 0x4002, &mustek_1200cu_model}, {0x05d8, 0x4002, &mustek_scanexpress1200ubplus_model}, /* manual override */ {0x05d8, 0x4002, &artec_ultima2000_model}, /* manual override */ {0x05d8, 0x4002, &mustek_2400cu_model}, /* manual override */ {0x05d8, 0x4002, &mustek_scanexpress2400usb_model}, /* manual override */ {0x055f, 0x0210, &mustek_a3usb_model}, {0x055f, 0x021a, &mustek_2448taplus_model}, {0x043d, 0x002d, &lexmark_x73_model}, {0x07b3, 0x0400, &plustek_op1248u_model}, {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, 0x046e, &plustek_opticslim500plus_model}, {0x07b3, 0x0413, &plustek_opticslim1200_model}, {0x07b3, 0x0422, &plustek_opticslim2400_model}, {0x07b3, 0x045f, &iriscan_express_2_model}, {0x0458, 0x2011, &genius_vivid3x_model}, {0x0458, 0x2014, &genius_vivid4_model}, {0x0458, 0x2017, &genius_vivid3xe_model}, {0x0458, 0x201a, &genius_vivid4xe_model}, {0x0458, 0x201b, &genius_vivid4x_model}, {0x0458, 0x201d, &genius_vivid1200x_model}, {0x0458, 0x201f, &genius_vivid1200xe_model}, {0x0458, 0x2021, &genius_sf600_model}, {0x04a7, 0x0444, &visioneer_onetouch_7300_model}, {0x0458, 0x201E, &genius_colorpageslim_1200_model}, {0, 0, NULL} }; backends-1.3.0/backend/gt68xx_generic.c000066400000000000000000000442661456256263500177160ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2002 Sergey Vlasov Copyright (C) 2005-2007 Henning Geinitz 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, see . 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. */ /** @file * @brief GT68xx commands common for most GT68xx-based scanners. */ #include "gt68xx_generic.h" SANE_Status gt68xx_generic_move_relative (GT68xx_Device * dev, SANE_Int distance) { GT68xx_Packet req; memset (req, 0, sizeof (req)); if (distance >= 0) req[0] = 0x14; else { req[0] = 0x15; distance = -distance; } req[1] = 0x01; req[2] = LOBYTE (distance); req[3] = HIBYTE (distance); return gt68xx_device_req (dev, req, req); } SANE_Status gt68xx_generic_start_scan (GT68xx_Device * dev) { GT68xx_Packet req; SANE_Status status; memset (req, 0, sizeof (req)); req[0] = 0x43; req[1] = 0x01; RIE (gt68xx_device_req (dev, req, req)); RIE (gt68xx_device_check_result (req, 0x43)); return SANE_STATUS_GOOD; } SANE_Status gt68xx_generic_read_scanned_data (GT68xx_Device * dev, SANE_Bool * ready) { SANE_Status status; GT68xx_Packet req; memset (req, 0, sizeof (req)); req[0] = 0x35; req[1] = 0x01; RIE (gt68xx_device_req (dev, req, req)); *ready = SANE_FALSE; if (req[0] == 0) *ready = SANE_TRUE; return SANE_STATUS_GOOD; } static SANE_Byte gt68xx_generic_fix_gain (SANE_Int gain) { if (gain < 0) gain = 0; else if (gain > 31) gain += 12; else if (gain > 51) gain = 63; return gain; } static SANE_Byte gt68xx_generic_fix_offset (SANE_Int offset) { if (offset < 0) offset = 0; else if (offset > 63) offset = 63; return offset; } SANE_Status gt68xx_generic_set_afe (GT68xx_Device * dev, GT68xx_AFE_Parameters * params) { GT68xx_Packet req; memset (req, 0, sizeof (req)); req[0] = 0x22; req[1] = 0x01; req[2] = gt68xx_generic_fix_offset (params->r_offset); req[3] = gt68xx_generic_fix_gain (params->r_pga); req[4] = gt68xx_generic_fix_offset (params->g_offset); req[5] = gt68xx_generic_fix_gain (params->g_pga); req[6] = gt68xx_generic_fix_offset (params->b_offset); req[7] = gt68xx_generic_fix_gain (params->b_pga); DBG (6, "gt68xx_generic_set_afe: real AFE: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", req[2], req[3], req[4], req[5], req[6], req[7]); return gt68xx_device_req (dev, req, req); } SANE_Status gt68xx_generic_set_exposure_time (GT68xx_Device * dev, GT68xx_Exposure_Parameters * params) { GT68xx_Packet req; SANE_Status status; memset (req, 0, sizeof (req)); req[0] = 0x76; req[1] = 0x01; req[2] = req[6] = req[10] = 0x04; req[4] = LOBYTE (params->r_time); req[5] = HIBYTE (params->r_time); req[8] = LOBYTE (params->g_time); req[9] = HIBYTE (params->g_time); req[12] = LOBYTE (params->b_time); req[13] = HIBYTE (params->b_time); DBG (6, "gt68xx_generic_set_exposure_time: 0x%03x 0x%03x 0x%03x\n", params->r_time, params->g_time, params->b_time); RIE (gt68xx_device_req (dev, req, req)); RIE (gt68xx_device_check_result (req, 0x76)); return SANE_STATUS_GOOD; } SANE_Status gt68xx_generic_get_id (GT68xx_Device * dev) { GT68xx_Packet req; SANE_Status status; memset (req, 0, sizeof (req)); req[0] = 0x2e; req[1] = 0x01; RIE (gt68xx_device_req (dev, req, req)); RIE (gt68xx_device_check_result (req, 0x2e)); DBG (2, "get_id: vendor id=0x%04X, product id=0x%04X, DID=0x%08X, FID=0x%04X\n", req[2] + (req[3] << 8), req[4] + (req[5] << 8), req[6] + (req[7] << 8) + (req[8] << 16) + (req[9] << 24), req[10] + (req[11] << 8)); return SANE_STATUS_GOOD; } SANE_Status gt68xx_generic_paperfeed (GT68xx_Device * dev) { GT68xx_Packet req; SANE_Status status; memset (req, 0, sizeof (req)); req[0] = 0x83; req[1] = 0x01; RIE (gt68xx_device_req (dev, req, req)); return SANE_STATUS_GOOD; } #define MAX_PIXEL_MODE 15600 SANE_Status gt68xx_generic_setup_scan (GT68xx_Device * dev, GT68xx_Scan_Request * request, GT68xx_Scan_Action action, GT68xx_Scan_Parameters * params) { SANE_Status status; GT68xx_Model *model; SANE_Int xdpi, ydpi; SANE_Bool color; SANE_Int depth; SANE_Int pixel_x0, pixel_y0, pixel_xs, pixel_ys; SANE_Int pixel_align; SANE_Int abs_x0, abs_y0, abs_xs, abs_ys, base_xdpi, base_ydpi; SANE_Int scan_xs, scan_ys, scan_bpl; SANE_Int bits_per_line; SANE_Byte color_mode_code; SANE_Bool line_mode; SANE_Int overscan_lines; SANE_Fixed x0, y0, xs, ys; SANE_Bool backtrack = SANE_FALSE; DBG (6, "gt6816_setup_scan: enter (action=%s)\n", action == SA_CALIBRATE ? "calibrate" : action == SA_CALIBRATE_ONE_LINE ? "calibrate one line" : action == SA_SCAN ? "scan" : "calculate only"); model = dev->model; xdpi = request->xdpi; ydpi = request->ydpi; color = request->color; depth = request->depth; base_xdpi = model->base_xdpi; base_ydpi = model->base_ydpi; if (xdpi > model->base_xdpi) base_xdpi = model->optical_xdpi; /* Special fixes */ if ((dev->model->flags & GT68XX_FLAG_USE_OPTICAL_X) && xdpi <= 50) base_xdpi = model->optical_xdpi; if ((dev->model->flags & GT68XX_FLAG_SCAN_FROM_HOME) && !request->use_ta && action == SA_SCAN) request->mbs = SANE_TRUE; if (!model->constant_ydpi) { if (ydpi > model->base_ydpi) base_ydpi = model->optical_ydpi; } DBG (6, "gt68xx_generic_setup_scan: base_xdpi=%d, base_ydpi=%d\n", base_xdpi, base_ydpi); switch (action) { case SA_CALIBRATE_ONE_LINE: { x0 = request->x0; if (request->use_ta) y0 = model->y_offset_calib_ta; else y0 = model->y_offset_calib; ys = SANE_FIX (1.0 * MM_PER_INCH / ydpi); /* one line */ xs = request->xs; depth = 8; break; } case SA_CALIBRATE: { if (request->use_ta) { if (dev->model->flags & GT68XX_FLAG_MIRROR_X) x0 = request->x0 - model->x_offset_ta; else x0 = request->x0 + model->x_offset_ta; if (request->mbs) y0 = model->y_offset_calib_ta; else y0 = 0; } else { if (dev->model->flags & GT68XX_FLAG_MIRROR_X) x0 = request->x0 - model->x_offset; else x0 = request->x0 + model->x_offset; if (request->mbs) y0 = model->y_offset_calib; else y0 = 0; } ys = SANE_FIX (CALIBRATION_HEIGHT); xs = request->xs; break; } case SA_SCAN: { SANE_Fixed x_offset, y_offset; if (strcmp (dev->model->command_set->name, "mustek-gt6816") != 0) request->mbs = SANE_TRUE; /* always go home for gt6801 scanners */ if (request->use_ta) { x_offset = model->x_offset_ta; if (request->mbs) y_offset = model->y_offset_ta; else { y_offset = model->y_offset_ta - model->y_offset_calib_ta - SANE_FIX (CALIBRATION_HEIGHT); if ((request->y0 + y_offset) < 0) { y_offset = model->y_offset_ta; request->mbs = SANE_TRUE; } } } else { x_offset = model->x_offset; if (request->mbs) y_offset = model->y_offset; else { y_offset = model->y_offset - model->y_offset_calib - SANE_FIX (CALIBRATION_HEIGHT); if ((request->y0 + y_offset) < 0) { y_offset = model->y_offset; request->mbs = SANE_TRUE; } } } if (dev->model->flags & GT68XX_FLAG_MIRROR_X) x0 = request->x0 - x_offset; else x0 = request->x0 + x_offset; y0 = request->y0 + y_offset; if (y0 < 0) y0 = 0; ys = request->ys; xs = request->xs; backtrack = request->backtrack; break; } default: DBG (1, "gt68xx_generic_setup_scan: invalid action=%d\n", (int) action); return SANE_STATUS_INVAL; } pixel_x0 = SANE_UNFIX (x0) * xdpi / MM_PER_INCH + 0.5; pixel_y0 = SANE_UNFIX (y0) * ydpi / MM_PER_INCH + 0.5; pixel_ys = SANE_UNFIX (ys) * ydpi / MM_PER_INCH + 0.5; pixel_xs = SANE_UNFIX (xs) * xdpi / MM_PER_INCH + 0.5; DBG (6, "gt68xx_generic_setup_scan: xdpi=%d, ydpi=%d\n", xdpi, ydpi); DBG (6, "gt68xx_generic_setup_scan: color=%s, depth=%d\n", color ? "TRUE" : "FALSE", depth); DBG (6, "gt68xx_generic_setup_scan: pixel_x0=%d, pixel_y0=%d\n", pixel_x0, pixel_y0); DBG (6, "gt68xx_generic_setup_scan: pixel_xs=%d, pixel_ys=%d\n", pixel_xs, pixel_ys); color_mode_code = 0x80; if (color) color_mode_code |= (1 << 2); else color_mode_code |= dev->gray_mode_color; if (depth > 12) color_mode_code |= (1 << 5); else if (depth > 8) { color_mode_code &= 0x7f; color_mode_code |= (1 << 4); } DBG (6, "gt68xx_generic_setup_scan: color_mode_code = 0x%02X\n", color_mode_code); overscan_lines = 0; params->ld_shift_r = params->ld_shift_g = params->ld_shift_b = 0; params->ld_shift_double = 0; /* Line distance correction is required for color scans. */ if (action == SA_SCAN && color) { SANE_Int optical_ydpi = model->optical_ydpi; SANE_Int ld_shift_r = model->ld_shift_r; SANE_Int ld_shift_g = model->ld_shift_g; SANE_Int ld_shift_b = model->ld_shift_b; SANE_Int max_ld = MAX (MAX (ld_shift_r, ld_shift_g), ld_shift_b); overscan_lines = max_ld * ydpi / optical_ydpi; params->ld_shift_r = ld_shift_r * ydpi / optical_ydpi; params->ld_shift_g = ld_shift_g * ydpi / optical_ydpi; params->ld_shift_b = ld_shift_b * ydpi / optical_ydpi; params->ld_shift_double = 0; DBG (6, "gt68xx_generic_setup_scan: overscan=%d, ld=%d/%d/%d\n", overscan_lines, params->ld_shift_r, params->ld_shift_g, params->ld_shift_b); } /* Used for CCD scanners with 6 instead of 3 CCD lines */ if (action == SA_SCAN && xdpi >= model->optical_xdpi && model->ld_shift_double > 0) { params->ld_shift_double = model->ld_shift_double * ydpi / model->optical_ydpi; if (color) overscan_lines += (params->ld_shift_double * 3); else overscan_lines += params->ld_shift_double; DBG (6, "gt68xx_generic_setup_scan: overscan=%d, ld double=%d\n", overscan_lines, params->ld_shift_double); } abs_x0 = pixel_x0 * base_xdpi / xdpi; abs_y0 = pixel_y0 * base_ydpi / ydpi; DBG (6, "gt68xx_generic_setup_scan: abs_x0=%d, abs_y0=%d\n", abs_x0, abs_y0); params->double_column = abs_x0 & 1; /* Calculate minimum number of pixels which span an integral multiple of 64 * bytes. */ pixel_align = 32; /* best case for depth = 16 */ while ((depth * pixel_align) % (64 * 8) != 0) pixel_align *= 2; DBG (6, "gt68xx_generic_setup_scan: pixel_align=%d\n", pixel_align); if (pixel_xs % pixel_align == 0) scan_xs = pixel_xs; else scan_xs = (pixel_xs / pixel_align + 1) * pixel_align; scan_ys = pixel_ys + overscan_lines; if ((xdpi != base_xdpi) && (strcmp (dev->model->command_set->name, "mustek-gt6816") != 0)) abs_xs = (scan_xs - 1) * base_xdpi / xdpi; /* gt6801 */ else abs_xs = scan_xs * base_xdpi / xdpi; /* gt6816 */ if (action == SA_CALIBRATE_ONE_LINE) abs_ys = 2; else abs_ys = scan_ys * base_ydpi / ydpi; DBG (6, "gt68xx_generic_setup_scan: abs_xs=%d, abs_ys=%d\n", abs_xs, abs_ys); if (model->flags & GT68XX_FLAG_NO_LINEMODE) { line_mode = SANE_FALSE; DBG (6, "gt68xx_generic_setup_scan: using pixel mode (GT68XX_FLAG_NO_LINEMODE)\n"); } else if (model->is_cis && !(model->flags & GT68XX_FLAG_CIS_LAMP)) { line_mode = SANE_TRUE; DBG (6, "gt68xx_generic_setup_scan: using line mode (CIS)\n"); } else if (model->flags & GT68XX_FLAG_ALWAYS_LINEMODE) { line_mode = SANE_TRUE; DBG (6, "gt68xx_generic_setup_scan: using line mode (GT68XX_FLAG_ALWAYS_LINEMODE)\n"); } else { SANE_Int max_bpl = xdpi * 3 * depth * (SANE_UNFIX (model->x_size) - SANE_UNFIX (model->x_offset)) / MM_PER_INCH / 8; line_mode = SANE_FALSE; if (!color) { DBG (6, "gt68xx_generic_setup_scan: using line mode for monochrome scan\n"); line_mode = SANE_TRUE; } else if (max_bpl > MAX_PIXEL_MODE) { DBG (6, "gt68xx_generic_setup_scan: max_bpl = %d > %d: forcing line mode\n", max_bpl, MAX_PIXEL_MODE); line_mode = SANE_TRUE; } else DBG (6, "gt68xx_generic_setup_scan: max_bpl = %d <= %d: using pixel mode\n", max_bpl, MAX_PIXEL_MODE); } bits_per_line = depth * scan_xs; if (color && !line_mode) bits_per_line *= 3; if (bits_per_line % 8) /* impossible */ { DBG (0, "gt68xx_generic_setup_scan: BUG: unaligned bits_per_line=%d\n", bits_per_line); return SANE_STATUS_INVAL; } scan_bpl = bits_per_line / 8; if (scan_bpl % 64) /* impossible */ { DBG (0, "gt68xx_generic_setup_scan: BUG: unaligned scan_bpl=%d\n", scan_bpl); return SANE_STATUS_INVAL; } if (color) if (line_mode || dev->model->flags & GT68XX_FLAG_SE_2400) scan_ys *= 3; DBG (6, "gt68xx_generic_setup_scan: scan_xs=%d, scan_ys=%d\n", scan_xs, scan_ys); DBG (6, "gt68xx_generic_setup_scan: scan_bpl=%d\n", scan_bpl); if (!request->calculate) { GT68xx_Packet req; SANE_Byte motor_mode_1, motor_mode_2; if (scan_bpl > (16 * 1024)) { DBG (0, "gt68xx_generic_setup_scan: scan_bpl=%d, too large\n", scan_bpl); return SANE_STATUS_NO_MEM; } if ((dev->model->flags & GT68XX_FLAG_NO_LINEMODE) && line_mode && color) { DBG (0, "gt68xx_generic_setup_scan: the scanner's memory is too small for " "that combination of resolution, dpi and width\n"); return SANE_STATUS_NO_MEM; } DBG (6, "gt68xx_generic_setup_scan: backtrack=%d\n", backtrack); motor_mode_1 = (request->mbs ? 0 : 1) << 1; motor_mode_1 |= (request->mds ? 0 : 1) << 2; motor_mode_1 |= (request->mas ? 0 : 1) << 0; motor_mode_1 |= (backtrack ? 1 : 0) << 3; motor_mode_2 = (request->lamp ? 0 : 1) << 0; motor_mode_2 |= (line_mode ? 0 : 1) << 2; if ((action != SA_SCAN) && (strcmp (dev->model->command_set->name, "mustek-gt6816") == 0)) motor_mode_2 |= 1 << 3; DBG (6, "gt68xx_generic_setup_scan: motor_mode_1 = 0x%02X, motor_mode_2 = 0x%02X\n", motor_mode_1, motor_mode_2); /* Fill in the setup command */ memset (req, 0, sizeof (req)); req[0x00] = 0x20; req[0x01] = 0x01; req[0x02] = LOBYTE (abs_y0); req[0x03] = HIBYTE (abs_y0); req[0x04] = LOBYTE (abs_ys); req[0x05] = HIBYTE (abs_ys); req[0x06] = LOBYTE (abs_x0); req[0x07] = HIBYTE (abs_x0); req[0x08] = LOBYTE (abs_xs); req[0x09] = HIBYTE (abs_xs); req[0x0a] = color_mode_code; if (model->is_cis && !(model->flags & GT68XX_FLAG_CIS_LAMP)) req[0x0b] = 0x60; else req[0x0b] = 0x20; req[0x0c] = LOBYTE (xdpi); req[0x0d] = HIBYTE (xdpi); req[0x0e] = 0x12; /* ??? 0x12 */ req[0x0f] = 0x00; /* ??? 0x00 */ req[0x10] = LOBYTE (scan_bpl); req[0x11] = HIBYTE (scan_bpl); req[0x12] = LOBYTE (scan_ys); req[0x13] = HIBYTE (scan_ys); req[0x14] = motor_mode_1; req[0x15] = motor_mode_2; req[0x16] = LOBYTE (ydpi); req[0x17] = HIBYTE (ydpi); if (backtrack) req[0x18] = request->backtrack_lines; else req[0x18] = 0x00; status = gt68xx_device_req (dev, req, req); if (status != SANE_STATUS_GOOD) { DBG (3, "gt68xx_generic_setup_scan: setup request failed: %s\n", sane_strstatus (status)); return status; } RIE (gt68xx_device_check_result (req, 0x20)); } /* Fill in calculated values */ params->xdpi = xdpi; params->ydpi = ydpi; params->depth = depth; params->color = color; params->pixel_xs = pixel_xs; params->pixel_ys = pixel_ys; params->scan_xs = scan_xs; params->scan_ys = scan_ys; params->scan_bpl = scan_bpl; params->line_mode = line_mode; params->overscan_lines = overscan_lines; params->pixel_x0 = pixel_x0; DBG (6, "gt68xx_generic_setup_scan: leave: ok\n"); return SANE_STATUS_GOOD; } SANE_Status gt68xx_generic_move_paper (GT68xx_Device * dev, GT68xx_Scan_Request * request) { GT68xx_Packet req; SANE_Status status; SANE_Int ydpi; SANE_Int pixel_y0; SANE_Int abs_y0, base_ydpi; GT68xx_Model *model = dev->model; ydpi = request->ydpi; base_ydpi = model->base_ydpi; if (ydpi > model->base_ydpi) ydpi = base_ydpi; pixel_y0 = SANE_UNFIX ((request->y0 + model->y_offset)) * ydpi / MM_PER_INCH + 0.5; abs_y0 = pixel_y0 * base_ydpi / ydpi; DBG (6, "gt68xx_generic_move_paper: base_ydpi=%d\n", base_ydpi); DBG (6, "gt68xx_generic_move_paper: ydpi=%d\n", ydpi); DBG (6, "gt68xx_generic_move_paper: abs_y0=%d\n", abs_y0); /* paper move request */ memset (req, 0, sizeof (req)); req[0] = 0x82; req[1] = 0x01; req[2] = LOBYTE (abs_y0); req[3] = HIBYTE (abs_y0); RIE (gt68xx_device_req (dev, req, req)); DBG (6, "gt68xx_generic_move_paper: leave: ok\n"); return SANE_STATUS_GOOD; } backends-1.3.0/backend/gt68xx_generic.h000066400000000000000000000053361456256263500177160ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2002 Sergey Vlasov 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, see . 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 GT68XX_GENERIC_H #define GT68XX_GENERIC_H #include "gt68xx_low.h" static SANE_Status gt68xx_generic_move_relative (GT68xx_Device * dev, SANE_Int distance); static SANE_Status gt68xx_generic_start_scan (GT68xx_Device * dev); static SANE_Status gt68xx_generic_read_scanned_data (GT68xx_Device * dev, SANE_Bool * ready); static SANE_Status gt68xx_generic_set_afe (GT68xx_Device * dev, GT68xx_AFE_Parameters * params); static SANE_Status gt68xx_generic_set_exposure_time (GT68xx_Device * dev, GT68xx_Exposure_Parameters * params); static SANE_Status gt68xx_generic_get_id (GT68xx_Device * dev); static SANE_Status gt68xx_generic_paperfeed (GT68xx_Device * dev); static SANE_Status gt68xx_generic_setup_scan (GT68xx_Device * dev, GT68xx_Scan_Request * request, GT68xx_Scan_Action action, GT68xx_Scan_Parameters * params); static SANE_Status gt68xx_generic_move_paper (GT68xx_Device * dev, GT68xx_Scan_Request * request); #endif /* not GT68XX_GENERIC_H */ backends-1.3.0/backend/gt68xx_gt6801.c000066400000000000000000000161041456256263500172210ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2002 Sergey Vlasov GT6801 support by Andreas Nowack Copyright (C) 2002-2007 Henning Geinitz 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, see . 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. */ /** @file * @brief Implementation of the GT6801 specific functions. */ #include "gt68xx_gt6801.h" /* doesn't work with plustek scanner */ SANE_Status gt6801_check_firmware (GT68xx_Device * dev, SANE_Bool * loaded) { SANE_Status status; GT68xx_Packet req; memset (req, 0, sizeof (req)); req[0] = 0x50; req[1] = 0x01; req[2] = 0x80; RIE (gt68xx_device_req (dev, req, req)); if (req[0] == 0x00 && req[1] == 0x50) *loaded = SANE_TRUE; else *loaded = SANE_FALSE; return SANE_STATUS_GOOD; } /* doesn't work with at least cytron scanner */ SANE_Status gt6801_check_plustek_firmware (GT68xx_Device * dev, SANE_Bool * loaded) { SANE_Status status; GT68xx_Packet req; memset (req, 0, sizeof (req)); req[0] = 0x73; req[1] = 0x01; status = gt68xx_device_small_req (dev, req, req); if (status != SANE_STATUS_GOOD) { /* Assume that firmware is not loaded */ *loaded = SANE_FALSE; return SANE_STATUS_GOOD; } /* check for correct answer */ if ((req[0] == 0) && (req[1] == 0x12) && (req[3] == 0x80)) /* req[3] is 0 if fw is not loaded, if loaded it's 0x80 * at least on my 1284u (gerhard@gjaeger.de) * * With the Genius scanners req[3] is 0x02/0x82. (hmg) */ *loaded = SANE_TRUE; else *loaded = SANE_FALSE; /* Until I find out if testing for req[3] & 0x80 is save, load the firmware every time */ *loaded = SANE_FALSE; return SANE_STATUS_GOOD; } #define MAX_DOWNLOAD_BLOCK_SIZE 64 SANE_Status gt6801_download_firmware (GT68xx_Device * dev, SANE_Byte * data, SANE_Word size) { SANE_Status status; SANE_Byte download_buf[MAX_DOWNLOAD_BLOCK_SIZE]; SANE_Byte check_buf[MAX_DOWNLOAD_BLOCK_SIZE]; SANE_Byte *block; SANE_Word addr, bytes_left; GT68xx_Packet boot_req; SANE_Word block_size = MAX_DOWNLOAD_BLOCK_SIZE; CHECK_DEV_ACTIVE (dev, "gt6801_download_firmware"); for (addr = 0; addr < size; addr += block_size) { bytes_left = size - addr; if (bytes_left > block_size) block = data + addr; else { memset (download_buf, 0, block_size); memcpy (download_buf, data + addr, bytes_left); block = download_buf; } RIE (gt68xx_device_memory_write (dev, addr, block_size, block)); RIE (gt68xx_device_memory_read (dev, 0x3f00, block_size, check_buf)); /* * For GT6816 this was: * if (memcmp (block, check_buf, block_size) != 0) ... * Apparently the GT6801 does something different... * * hmg: For my BP 1200 CU the result is 00 09 so maybe only the 0 is * relevant? */ if ((check_buf[0] != 0) && (check_buf[1] != 0x40)) { DBG (3, "gt6801_download_firmware: mismatch at block 0x%0x\n", addr); return SANE_STATUS_IO_ERROR; } } memset (boot_req, 0, sizeof (boot_req)); boot_req[0] = 0x69; boot_req[1] = 0x01; boot_req[2] = 0xc0; boot_req[3] = 0x1c; RIE (gt68xx_device_req (dev, boot_req, boot_req)); #if 0 /* hmg: the following isn't in my log: */ memset (boot_req, 0, sizeof (boot_req)); /* I don't know if this is needed */ boot_req[0] = 0x01; boot_req[1] = 0x01; RIE (gt68xx_device_small_req (dev, boot_req, boot_req)); #endif return SANE_STATUS_GOOD; } SANE_Status gt6801_get_power_status (GT68xx_Device * dev, SANE_Bool * power_ok) { SANE_Status status; GT68xx_Packet req; memset (req, 0, sizeof (req)); req[0] = 0x10; req[1] = 0x01; RIE (gt68xx_device_req (dev, req, req)); /* I don't know what power_ok = SANE_FALSE looks like... */ /* hmg: let's assume it's different from the usual 00 10 */ if (gt68xx_device_check_result (req, 0x10) == SANE_STATUS_GOOD) *power_ok = SANE_TRUE; else *power_ok = SANE_FALSE; return SANE_STATUS_GOOD; } SANE_Status gt6801_lamp_control (GT68xx_Device * dev, SANE_Bool fb_lamp, SANE_Bool ta_lamp) { if (!dev->model->is_cis) { GT68xx_Packet req; memset (req, 0, sizeof (req)); req[0] = 0x25; req[1] = 0x01; req[2] = 0; if (fb_lamp) req[2] |= 0x01; if (ta_lamp) req[2] |= 0x02; return gt68xx_device_req (dev, req, req); } return SANE_STATUS_GOOD; } SANE_Status gt6801_is_moving (GT68xx_Device * dev, SANE_Bool * moving) { /* this seems not to be supported by the scanner */ (void) dev; *moving = SANE_FALSE; return SANE_STATUS_GOOD; } SANE_Status gt6801_carriage_home (GT68xx_Device * dev) { GT68xx_Packet req; SANE_Status status; memset (req, 0, sizeof (req)); if (dev->model->flags & GT68XX_FLAG_MOTOR_HOME) { req[0] = 0x34; req[1] = 0x01; status = gt68xx_device_req (dev, req, req); } else { req[0] = 0x12; req[1] = 0x01; if ((status = gt68xx_device_req (dev, req, req)) == SANE_STATUS_GOOD) { RIE (gt68xx_device_check_result (req, 0x12)); memset (req, 0, sizeof (req)); req[0] = 0x24; req[1] = 0x01; status = gt68xx_device_req (dev, req, req); RIE (gt68xx_device_check_result (req, 0x24)); } } return status; } SANE_Status gt6801_stop_scan (GT68xx_Device * dev) { GT68xx_Packet req; SANE_Status status; memset (req, 0, sizeof (req)); req[0] = 0x42; req[1] = 0x01; RIE (gt68xx_device_req (dev, req, req)); RIE (gt68xx_device_check_result (req, 0x42)); return SANE_STATUS_GOOD; } backends-1.3.0/backend/gt68xx_gt6801.h000066400000000000000000000050661456256263500172330ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2002 Sergey Vlasov GT6801 support by Andreas Nowack 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, see . 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 GT68XX_GT6801_H #define GT68XX_GT6801_H static SANE_Status gt6801_check_firmware (GT68xx_Device * dev, SANE_Bool * loaded); static SANE_Status gt6801_check_plustek_firmware (GT68xx_Device * dev, SANE_Bool * loaded); static SANE_Status gt6801_download_firmware (GT68xx_Device * dev, SANE_Byte * data, SANE_Word size); static SANE_Status gt6801_get_power_status (GT68xx_Device * dev, SANE_Bool * power_ok); static SANE_Status gt6801_lamp_control (GT68xx_Device * dev, SANE_Bool fb_lamp, SANE_Bool ta_lamp); static SANE_Status gt6801_is_moving (GT68xx_Device * dev, SANE_Bool * moving); static SANE_Status gt6801_carriage_home (GT68xx_Device * dev); static SANE_Status gt6801_stop_scan (GT68xx_Device * dev); #endif /* not GT68XX_GT6801_H */ backends-1.3.0/backend/gt68xx_gt6816.c000066400000000000000000000143331456256263500172310ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2002 Sergey Vlasov Copyright (C) 2002-2007 Henning Geinitz 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, see . 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. */ /** @file * @brief Implementation of the gt6816 specific functions. */ #include "gt68xx_gt6816.h" SANE_Status gt6816_check_firmware (GT68xx_Device * dev, SANE_Bool * loaded) { SANE_Status status; GT68xx_Packet req; memset (req, 0, sizeof (req)); req[0] = 0x70; req[1] = 0x01; status = gt68xx_device_small_req (dev, req, req); if (status != SANE_STATUS_GOOD) { /* Assume that firmware is not loaded because without firmware, we need 64 bytes for the result, not 8 */ *loaded = SANE_FALSE; return SANE_STATUS_GOOD; } /* check anyway */ if (req[0] == 0x00 && req[1] == 0x70 && req[2] == 0xff) *loaded = SANE_TRUE; else *loaded = SANE_FALSE; return SANE_STATUS_GOOD; } #define MAX_DOWNLOAD_BLOCK_SIZE 64 SANE_Status gt6816_download_firmware (GT68xx_Device * dev, SANE_Byte * data, SANE_Word size) { SANE_Status status; SANE_Byte download_buf[MAX_DOWNLOAD_BLOCK_SIZE]; SANE_Byte check_buf[MAX_DOWNLOAD_BLOCK_SIZE]; SANE_Byte *block; SANE_Word addr, bytes_left; GT68xx_Packet boot_req; SANE_Word block_size = MAX_DOWNLOAD_BLOCK_SIZE; CHECK_DEV_ACTIVE (dev, "gt6816_download_firmware"); for (addr = 0; addr < size; addr += block_size) { bytes_left = size - addr; if (bytes_left > block_size) block = data + addr; else { memset (download_buf, 0, block_size); memcpy (download_buf, data + addr, bytes_left); block = download_buf; } RIE (gt68xx_device_memory_write (dev, addr, block_size, block)); RIE (gt68xx_device_memory_read (dev, addr, block_size, check_buf)); if (memcmp (block, check_buf, block_size) != 0) { DBG (3, "gt6816_download_firmware: mismatch at block 0x%0x\n", addr); return SANE_STATUS_IO_ERROR; } } memset (boot_req, 0, sizeof (boot_req)); boot_req[0] = 0x69; boot_req[1] = 0x01; boot_req[2] = LOBYTE (addr); boot_req[3] = HIBYTE (addr); RIE (gt68xx_device_req (dev, boot_req, boot_req)); return SANE_STATUS_GOOD; } SANE_Status gt6816_get_power_status (GT68xx_Device * dev, SANE_Bool * power_ok) { SANE_Status status; GT68xx_Packet req; memset (req, 0, sizeof (req)); req[0] = 0x3f; req[1] = 0x01; RIE (gt68xx_device_req (dev, req, req)); if ((req[0] == 0x00 && req[1] == 0x3f && req[2] == 0x01) || (dev->model->flags & GT68XX_FLAG_NO_POWER_STATUS)) *power_ok = SANE_TRUE; else *power_ok = SANE_FALSE; return SANE_STATUS_GOOD; } SANE_Status gt6816_get_ta_status (GT68xx_Device * dev, SANE_Bool * ta_attached) { SANE_Status status; GT68xx_Packet req; memset (req, 0, sizeof (req)); req[0] = 0x28; req[1] = 0x01; RIE (gt68xx_device_req (dev, req, req)); if (req[0] == 0x00 && req[1] == 0x28 && (req[8] & 0x01) != 0 && !dev->model->is_cis) *ta_attached = SANE_TRUE; else *ta_attached = SANE_FALSE; return SANE_STATUS_GOOD; } SANE_Status gt6816_lamp_control (GT68xx_Device * dev, SANE_Bool fb_lamp, SANE_Bool ta_lamp) { GT68xx_Packet req; memset (req, 0, sizeof (req)); req[0] = 0x25; req[1] = 0x01; req[2] = 0; if (fb_lamp) req[2] |= 0x01; if (ta_lamp) req[2] |= 0x02; return gt68xx_device_req (dev, req, req); } SANE_Status gt6816_is_moving (GT68xx_Device * dev, SANE_Bool * moving) { SANE_Status status; GT68xx_Packet req; memset (req, 0, sizeof (req)); req[0] = 0x17; req[1] = 0x01; RIE (gt68xx_device_req (dev, req, req)); if (req[0] == 0x00 && req[1] == 0x17) { if (req[2] == 0 && (req[3] == 0 || req[3] == 2)) *moving = SANE_FALSE; else *moving = SANE_TRUE; } else return SANE_STATUS_IO_ERROR; return SANE_STATUS_GOOD; } SANE_Status gt6816_carriage_home (GT68xx_Device * dev) { GT68xx_Packet req; memset (req, 0, sizeof (req)); req[0] = 0x24; req[1] = 0x01; return gt68xx_device_req (dev, req, req); } SANE_Status gt6816_stop_scan (GT68xx_Device * dev) { GT68xx_Packet req; memset (req, 0, sizeof (req)); req[0] = 0x41; req[1] = 0x01; return gt68xx_device_small_req (dev, req, req); } SANE_Status gt6816_document_present (GT68xx_Device * dev, SANE_Bool * present) { SANE_Status status; GT68xx_Packet req; memset (req, 0, sizeof (req)); req[0] = 0x59; req[1] = 0x01; RIE (gt68xx_device_req (dev, req, req)); if (req[0] == 0x00 && req[1] == 0x59) { if (req[2] == 0) *present = SANE_FALSE; else *present = SANE_TRUE; } else return SANE_STATUS_IO_ERROR; return SANE_STATUS_GOOD; } backends-1.3.0/backend/gt68xx_gt6816.h000066400000000000000000000051161456256263500172350ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2002 Sergey Vlasov 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, see . 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 GT68XX_GT6816_H #define GT68XX_GT6816_H static SANE_Status gt6816_check_firmware (GT68xx_Device * dev, SANE_Bool * loaded); static SANE_Status gt6816_download_firmware (GT68xx_Device * dev, SANE_Byte * data, SANE_Word size); static SANE_Status gt6816_get_power_status (GT68xx_Device * dev, SANE_Bool * power_ok); static SANE_Status gt6816_get_ta_status (GT68xx_Device * dev, SANE_Bool * ta_attached); static SANE_Status gt6816_lamp_control (GT68xx_Device * dev, SANE_Bool fb_lamp, SANE_Bool ta_lamp); static SANE_Status gt6816_is_moving (GT68xx_Device * dev, SANE_Bool * moving); static SANE_Status gt6816_carriage_home (GT68xx_Device * dev); static SANE_Status gt6816_stop_scan (GT68xx_Device * dev); static SANE_Status gt6816_document_present (GT68xx_Device * dev, SANE_Bool * present); #endif /* not GT68XX_GT6816_H */ backends-1.3.0/backend/gt68xx_high.c000066400000000000000000002266621456256263500172230ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2002 Sergey Vlasov AFE offset/gain setting by David Stevenson Copyright (C) 2002 - 2007 Henning Geinitz Copyright (C) 2009 Stéphane Voltz for sheetfed calibration code. 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, see . 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 "gt68xx_high.h" #include "gt68xx_mid.c" #include #include static SANE_Status gt68xx_afe_ccd_auto (GT68xx_Scanner * scanner, GT68xx_Scan_Request * request); static SANE_Status gt68xx_afe_cis_auto (GT68xx_Scanner * scanner); SANE_Status gt68xx_calibrator_new (SANE_Int width, SANE_Int white_level, GT68xx_Calibrator ** cal_return) { GT68xx_Calibrator *cal; SANE_Int i; DBG (4, "gt68xx_calibrator_new: enter: width=%d, white_level=%d\n", width, white_level); *cal_return = 0; if (width <= 0) { DBG (5, "gt68xx_calibrator_new: invalid width=%d\n", width); return SANE_STATUS_INVAL; } cal = (GT68xx_Calibrator *) malloc (sizeof (GT68xx_Calibrator)); if (!cal) { DBG (5, "gt68xx_calibrator_new: no memory for GT68xx_Calibrator\n"); return SANE_STATUS_NO_MEM; } cal->k_white = NULL; cal->k_black = NULL; cal->white_line = NULL; cal->black_line = NULL; cal->width = width; cal->white_level = white_level; cal->white_count = 0; cal->black_count = 0; #ifdef TUNE_CALIBRATOR cal->min_clip_count = cal->max_clip_count = 0; #endif /* TUNE_CALIBRATOR */ cal->k_white = (unsigned int *) malloc (width * sizeof (unsigned int)); cal->k_black = (unsigned int *) malloc (width * sizeof (unsigned int)); cal->white_line = (double *) malloc (width * sizeof (double)); cal->black_line = (double *) malloc (width * sizeof (double)); if (!cal->k_white || !cal->k_black || !cal->white_line || !cal->black_line) { DBG (5, "gt68xx_calibrator_new: no memory for calibration data\n"); gt68xx_calibrator_free (cal); return SANE_STATUS_NO_MEM; } for (i = 0; i < width; ++i) { cal->k_white[i] = 0; cal->k_black[i] = 0; cal->white_line[i] = 0.0; cal->black_line[i] = 0.0; } *cal_return = cal; DBG (5, "gt68xx_calibrator_new: leave: ok\n"); return SANE_STATUS_GOOD; } SANE_Status gt68xx_calibrator_free (GT68xx_Calibrator * cal) { DBG (5, "gt68xx_calibrator_free: enter\n"); if (!cal) { DBG (5, "gt68xx_calibrator_free: cal==NULL\n"); return SANE_STATUS_INVAL; } #ifdef TUNE_CALIBRATOR DBG (4, "gt68xx_calibrator_free: min_clip_count=%d, max_clip_count=%d\n", cal->min_clip_count, cal->max_clip_count); #endif /* TUNE_CALIBRATOR */ if (cal->k_white) { free (cal->k_white); cal->k_white = NULL; } if (cal->k_black) { free (cal->k_black); cal->k_black = NULL; } if (cal->white_line) { free (cal->white_line); cal->white_line = NULL; } if (cal->black_line) { free (cal->black_line); cal->black_line = NULL; } free (cal); DBG (5, "gt68xx_calibrator_free: leave: ok\n"); return SANE_STATUS_GOOD; } SANE_Status gt68xx_calibrator_add_white_line (GT68xx_Calibrator * cal, unsigned int *line) { SANE_Int i; SANE_Int width = cal->width; SANE_Int sum = 0; cal->white_count++; for (i = 0; i < width; ++i) { cal->white_line[i] += line[i]; sum += line[i]; #ifdef SAVE_WHITE_CALIBRATION printf ("%c", line[i] >> 8); #endif } if (sum / width / 256 < 0x50) DBG (1, "gt68xx_calibrator_add_white_line: WARNING: dark calibration line: " "%2d medium white: 0x%02x\n", cal->white_count - 1, sum / width / 256); else DBG (5, "gt68xx_calibrator_add_white_line: line: %2d medium white: 0x%02x\n", cal->white_count - 1, sum / width / 256); return SANE_STATUS_GOOD; } SANE_Status gt68xx_calibrator_eval_white (GT68xx_Calibrator * cal, double factor) { SANE_Int i; SANE_Int width = cal->width; for (i = 0; i < width; ++i) { cal->white_line[i] = cal->white_line[i] / cal->white_count * factor; } return SANE_STATUS_GOOD; } SANE_Status gt68xx_calibrator_add_black_line (GT68xx_Calibrator * cal, unsigned int *line) { SANE_Int i; SANE_Int width = cal->width; SANE_Int sum = 0; cal->black_count++; for (i = 0; i < width; ++i) { cal->black_line[i] += line[i]; sum += line[i]; #ifdef SAVE_BLACK_CALIBRATION printf ("%c", line[i] >> 8); #endif } DBG (5, "gt68xx_calibrator_add_black_line: line: %2d medium black: 0x%02x\n", cal->black_count - 1, sum / width / 256); return SANE_STATUS_GOOD; } SANE_Status gt68xx_calibrator_eval_black (GT68xx_Calibrator * cal, double factor) { SANE_Int i; SANE_Int width = cal->width; for (i = 0; i < width; ++i) { cal->black_line[i] = cal->black_line[i] / cal->black_count - factor; } return SANE_STATUS_GOOD; } SANE_Status gt68xx_calibrator_finish_setup (GT68xx_Calibrator * cal) { #ifdef TUNE_CALIBRATOR double ave_black = 0.0; double ave_diff = 0.0; #endif /* TUNE_CALIBRATOR */ int i; int width = cal->width; unsigned int max_value = 65535; for (i = 0; i < width; ++i) { unsigned int white = cal->white_line[i]; unsigned int black = cal->black_line[i]; unsigned int diff = (white > black) ? white - black : 1; if (diff > max_value) diff = max_value; cal->k_white[i] = diff; cal->k_black[i] = black; #ifdef TUNE_CALIBRATOR ave_black += black; ave_diff += diff; #endif /* TUNE_CALIBRATOR */ } #ifdef TUNE_CALIBRATOR ave_black /= width; ave_diff /= width; DBG (4, "gt68xx_calibrator_finish_setup: ave_black=%f, ave_diff=%f\n", ave_black, ave_diff); #endif /* TUNE_CALIBRATOR */ return SANE_STATUS_GOOD; } SANE_Status gt68xx_calibrator_process_line (GT68xx_Calibrator * cal, unsigned int *line) { int i; int width = cal->width; unsigned int white_level = cal->white_level; for (i = 0; i < width; ++i) { unsigned int src_value = line[i]; unsigned int black = cal->k_black[i]; unsigned int value; if (src_value > black) { value = (src_value - black) * white_level / cal->k_white[i]; if (value > 0xffff) { value = 0xffff; #ifdef TUNE_CALIBRATOR cal->max_clip_count++; #endif /* TUNE_CALIBRATOR */ } } else { value = 0; #ifdef TUNE_CALIBRATOR if (src_value < black) cal->min_clip_count++; #endif /* TUNE_CALIBRATOR */ } line[i] = value; } return SANE_STATUS_GOOD; } SANE_Status gt68xx_scanner_new (GT68xx_Device * dev, GT68xx_Scanner ** scanner_return) { GT68xx_Scanner *scanner; int i; *scanner_return = NULL; scanner = (GT68xx_Scanner *) malloc (sizeof (GT68xx_Scanner)); if (!scanner) { DBG (5, "gt68xx_scanner_new: no memory for GT68xx_Scanner\n"); return SANE_STATUS_NO_MEM; } scanner->dev = dev; scanner->reader = NULL; scanner->cal_gray = NULL; scanner->cal_r = NULL; scanner->cal_g = NULL; scanner->cal_b = NULL; for(i=0;icalibrations[i].dpi = 0; scanner->calibrations[i].red = NULL; scanner->calibrations[i].green = NULL; scanner->calibrations[i].blue = NULL; scanner->calibrations[i].gray = NULL; } *scanner_return = scanner; return SANE_STATUS_GOOD; } static void gt68xx_scanner_free_calibrators (GT68xx_Scanner * scanner) { if (scanner->cal_gray) { gt68xx_calibrator_free (scanner->cal_gray); scanner->cal_gray = NULL; } if (scanner->cal_r) { gt68xx_calibrator_free (scanner->cal_r); scanner->cal_r = NULL; } if (scanner->cal_g) { gt68xx_calibrator_free (scanner->cal_g); scanner->cal_g = NULL; } if (scanner->cal_b) { gt68xx_calibrator_free (scanner->cal_b); scanner->cal_b = NULL; } } SANE_Status gt68xx_scanner_free (GT68xx_Scanner * scanner) { int i; if (!scanner) { DBG (5, "gt68xx_scanner_free: scanner==NULL\n"); return SANE_STATUS_INVAL; } if (scanner->reader) { gt68xx_line_reader_free (scanner->reader); scanner->reader = NULL; } gt68xx_scanner_free_calibrators (scanner); /* free in memory calibration data */ for (i = 0; i < MAX_RESOLUTIONS; i++) { scanner->calibrations[i].dpi = 0; if (scanner->calibrations[i].red != NULL) { gt68xx_calibrator_free (scanner->calibrations[i].red); } if (scanner->calibrations[i].green != NULL) { gt68xx_calibrator_free (scanner->calibrations[i].green); } if (scanner->calibrations[i].blue != NULL) { gt68xx_calibrator_free (scanner->calibrations[i].blue); } if (scanner->calibrations[i].gray != NULL) { gt68xx_calibrator_free (scanner->calibrations[i].gray); } } free (scanner); return SANE_STATUS_GOOD; } static SANE_Status gt68xx_scanner_wait_for_positioning (GT68xx_Scanner * scanner) { SANE_Status status; SANE_Bool moving; SANE_Int status_count = 0; usleep (100000); /* needed by the BP 2400 CU Plus? */ while (SANE_TRUE) { status = gt68xx_device_is_moving (scanner->dev, &moving); if (status == SANE_STATUS_GOOD) { if (!moving) break; } else { if (status_count > 9) { DBG (1, "gt68xx_scanner_wait_for_positioning: error count too high!\n"); return status; } status_count++; DBG(3, "gt68xx_scanner_wait_for_positioning: ignored error\n"); } usleep (100000); } return SANE_STATUS_GOOD; } static SANE_Status gt68xx_scanner_internal_start_scan (GT68xx_Scanner * scanner) { SANE_Status status; SANE_Bool ready; SANE_Int repeat_count; status = gt68xx_scanner_wait_for_positioning (scanner); if (status != SANE_STATUS_GOOD) { DBG (5, "gt68xx_scanner_internal_start_scan: gt68xx_scanner_wait_for_positioning error: %s\n", sane_strstatus (status)); return status; } status = gt68xx_device_start_scan (scanner->dev); if (status != SANE_STATUS_GOOD) { DBG (5, "gt68xx_scanner_internal_start_scan: gt68xx_device_start_scan error: %s\n", sane_strstatus (status)); return status; } for (repeat_count = 0; repeat_count < 30 * 100; ++repeat_count) { status = gt68xx_device_read_scanned_data (scanner->dev, &ready); if (status != SANE_STATUS_GOOD) { DBG (5, "gt68xx_scanner_internal_start_scan: gt68xx_device_read_scanned_data error: %s\n", sane_strstatus (status)); return status; } if (ready) break; usleep (10000); } if (!ready) { DBG (5, "gt68xx_scanner_internal_start_scan: scanner still not ready - giving up\n"); return SANE_STATUS_DEVICE_BUSY; } status = gt68xx_device_read_start (scanner->dev); if (status != SANE_STATUS_GOOD) { DBG (5, "gt68xx_scanner_internal_start_scan: gt68xx_device_read_start error: %s\n", sane_strstatus (status)); return status; } return SANE_STATUS_GOOD; } static SANE_Status gt68xx_scanner_start_scan_extended (GT68xx_Scanner * scanner, GT68xx_Scan_Request * request, GT68xx_Scan_Action action, GT68xx_Scan_Parameters * params) { SANE_Status status; GT68xx_AFE_Parameters afe = *scanner->dev->afe; status = gt68xx_scanner_wait_for_positioning (scanner); if (status != SANE_STATUS_GOOD) { DBG (5, "gt68xx_scanner_start_scan_extended: gt68xx_scanner_wait_for_positioning error: %s\n", sane_strstatus (status)); return status; } status = gt68xx_device_setup_scan (scanner->dev, request, action, params); if (status != SANE_STATUS_GOOD) { DBG (5, "gt68xx_scanner_start_scan_extended: gt68xx_device_setup_scan failed: %s\n", sane_strstatus (status)); return status; } status = gt68xx_line_reader_new (scanner->dev, params, action == SA_SCAN ? SANE_TRUE : SANE_FALSE, &scanner->reader); if (status != SANE_STATUS_GOOD) { DBG (5, "gt68xx_scanner_start_scan_extended: gt68xx_line_reader_new failed: %s\n", sane_strstatus (status)); return status; } if (scanner->dev->model->is_cis && !((scanner->dev->model->flags & GT68XX_FLAG_SHEET_FED) && scanner->calibrated == SANE_FALSE)) { status = gt68xx_device_set_exposure_time (scanner->dev, scanner->dev->exposure); if (status != SANE_STATUS_GOOD) { DBG (5, "gt68xx_scanner_start_scan_extended: gt68xx_device_set_exposure_time failed: %s\n", sane_strstatus (status)); return status; } } status = gt68xx_device_set_afe (scanner->dev, &afe); if (status != SANE_STATUS_GOOD) { DBG (5, "gt68xx_scanner_start_scan_extended: gt68xx_device_set_afe failed: %s\n", sane_strstatus (status)); return status; } status = gt68xx_scanner_internal_start_scan (scanner); if (status != SANE_STATUS_GOOD) { DBG (5, "gt68xx_scanner_start_scan_extended: gt68xx_scanner_internal_start_scan failed: %s\n", sane_strstatus (status)); return status; } return SANE_STATUS_GOOD; } static SANE_Status gt68xx_scanner_create_calibrator (GT68xx_Scan_Parameters * params, GT68xx_Calibrator ** cal_return) { return gt68xx_calibrator_new (params->pixel_xs, 65535, cal_return); } static SANE_Status gt68xx_scanner_create_color_calibrators (GT68xx_Scanner * scanner, GT68xx_Scan_Parameters * params) { SANE_Status status; if (!scanner->cal_r) { status = gt68xx_scanner_create_calibrator (params, &scanner->cal_r); if (status != SANE_STATUS_GOOD) return status; } if (!scanner->cal_g) { status = gt68xx_scanner_create_calibrator (params, &scanner->cal_g); if (status != SANE_STATUS_GOOD) return status; } if (!scanner->cal_b) { status = gt68xx_scanner_create_calibrator (params, &scanner->cal_b); if (status != SANE_STATUS_GOOD) return status; } return SANE_STATUS_GOOD; } static SANE_Status gt68xx_scanner_create_gray_calibrators (GT68xx_Scanner * scanner, GT68xx_Scan_Parameters * params) { SANE_Status status; if (!scanner->cal_gray) { status = gt68xx_scanner_create_calibrator (params, &scanner->cal_gray); if (status != SANE_STATUS_GOOD) return status; } return SANE_STATUS_GOOD; } static SANE_Status gt68xx_scanner_calibrate_color_white_line (GT68xx_Scanner * scanner, unsigned int **buffer_pointers) { gt68xx_calibrator_add_white_line (scanner->cal_r, buffer_pointers[0]); gt68xx_calibrator_add_white_line (scanner->cal_g, buffer_pointers[1]); gt68xx_calibrator_add_white_line (scanner->cal_b, buffer_pointers[2]); return SANE_STATUS_GOOD; } static SANE_Status gt68xx_scanner_calibrate_gray_white_line (GT68xx_Scanner * scanner, unsigned int **buffer_pointers) { gt68xx_calibrator_add_white_line (scanner->cal_gray, buffer_pointers[0]); return SANE_STATUS_GOOD; } static SANE_Status gt68xx_scanner_calibrate_color_black_line (GT68xx_Scanner * scanner, unsigned int **buffer_pointers) { gt68xx_calibrator_add_black_line (scanner->cal_r, buffer_pointers[0]); gt68xx_calibrator_add_black_line (scanner->cal_g, buffer_pointers[1]); gt68xx_calibrator_add_black_line (scanner->cal_b, buffer_pointers[2]); return SANE_STATUS_GOOD; } static SANE_Status gt68xx_scanner_calibrate_gray_black_line (GT68xx_Scanner * scanner, unsigned int **buffer_pointers) { gt68xx_calibrator_add_black_line (scanner->cal_gray, buffer_pointers[0]); return SANE_STATUS_GOOD; } SANE_Status gt68xx_scanner_calibrate (GT68xx_Scanner * scanner, GT68xx_Scan_Request * request) { SANE_Status status; GT68xx_Scan_Parameters params; GT68xx_Scan_Request req; SANE_Int i; unsigned int *buffer_pointers[3]; GT68xx_AFE_Parameters *afe = scanner->dev->afe; GT68xx_Exposure_Parameters *exposure = scanner->dev->exposure; memcpy (&req, request, sizeof (req)); gt68xx_scanner_free_calibrators (scanner); if (scanner->auto_afe) { if (scanner->dev->model->is_cis) status = gt68xx_afe_cis_auto (scanner); else status = gt68xx_afe_ccd_auto (scanner, request); if (status != SANE_STATUS_GOOD) { DBG (5, "gt68xx_scanner_calibrate: gt68xx_afe_*_auto failed: %s\n", sane_strstatus (status)); return status; } req.mbs = SANE_FALSE; } else req.mbs = SANE_TRUE; DBG (3, "afe 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", afe->r_offset, afe->r_pga, afe->g_offset, afe->g_pga, afe->b_offset, afe->b_pga); DBG (3, "exposure 0x%02x 0x%02x 0x%02x\n", exposure->r_time, exposure->g_time, exposure->b_time); if (!scanner->calib) return SANE_STATUS_GOOD; req.mds = SANE_TRUE; req.mas = SANE_FALSE; if (scanner->dev->model->is_cis && !(scanner->dev->model->flags & GT68XX_FLAG_CIS_LAMP)) req.color = SANE_TRUE; if (req.use_ta) { req.lamp = SANE_FALSE; status = gt68xx_device_lamp_control (scanner->dev, SANE_FALSE, SANE_TRUE); } else { req.lamp = SANE_TRUE; status = gt68xx_device_lamp_control (scanner->dev, SANE_TRUE, SANE_FALSE); } status = gt68xx_scanner_start_scan_extended (scanner, &req, SA_CALIBRATE, ¶ms); if (status != SANE_STATUS_GOOD) { DBG (5, "gt68xx_scanner_calibrate: gt68xx_scanner_start_scan_extended failed: %s\n", sane_strstatus (status)); return status; } if (params.color) { status = gt68xx_scanner_create_color_calibrators (scanner, ¶ms); } else { status = gt68xx_scanner_create_gray_calibrators (scanner, ¶ms); } #if defined(SAVE_WHITE_CALIBRATION) || defined(SAVE_BLACK_CALIBRATION) printf ("P5\n%d %d\n255\n", params.pixel_xs, params.pixel_ys * 3); #endif for (i = 0; i < params.pixel_ys; ++i) { status = gt68xx_line_reader_read (scanner->reader, buffer_pointers); if (status != SANE_STATUS_GOOD) { DBG (5, "gt68xx_scanner_calibrate: gt68xx_line_reader_read failed: %s\n", sane_strstatus (status)); return status; } if (params.color) status = gt68xx_scanner_calibrate_color_white_line (scanner, buffer_pointers); else status = gt68xx_scanner_calibrate_gray_white_line (scanner, buffer_pointers); if (status != SANE_STATUS_GOOD) { DBG (5, "gt68xx_scanner_calibrate: calibration failed: %s\n", sane_strstatus (status)); return status; } } gt68xx_scanner_stop_scan (scanner); if (params.color) { gt68xx_calibrator_eval_white (scanner->cal_r, 1); gt68xx_calibrator_eval_white (scanner->cal_g, 1); gt68xx_calibrator_eval_white (scanner->cal_b, 1); } else { gt68xx_calibrator_eval_white (scanner->cal_gray, 1); } req.mbs = SANE_FALSE; req.mds = SANE_FALSE; req.mas = SANE_FALSE; req.lamp = SANE_FALSE; status = gt68xx_device_lamp_control (scanner->dev, SANE_FALSE, SANE_FALSE); if (status != SANE_STATUS_GOOD) { DBG (5, "gt68xx_scanner_calibrate: gt68xx_device_lamp_control failed: %s\n", sane_strstatus (status)); return status; } if (!scanner->dev->model->is_cis || (scanner->dev->model->flags & GT68XX_FLAG_CIS_LAMP)) usleep (500000); status = gt68xx_scanner_start_scan_extended (scanner, &req, SA_CALIBRATE, ¶ms); if (status != SANE_STATUS_GOOD) { DBG (5, "gt68xx_scanner_calibrate: gt68xx_scanner_start_scan_extended failed: %s\n", sane_strstatus (status)); return status; } for (i = 0; i < params.pixel_ys; ++i) { status = gt68xx_line_reader_read (scanner->reader, buffer_pointers); if (status != SANE_STATUS_GOOD) { DBG (5, "gt68xx_scanner_calibrate: gt68xx_line_reader_read failed: %s\n", sane_strstatus (status)); return status; } if (params.color) status = gt68xx_scanner_calibrate_color_black_line (scanner, buffer_pointers); else status = gt68xx_scanner_calibrate_gray_black_line (scanner, buffer_pointers); if (status != SANE_STATUS_GOOD) { DBG (5, "gt68xx_scanner_calibrate: calibration failed: %s\n", sane_strstatus (status)); return status; } } gt68xx_scanner_stop_scan (scanner); if (req.use_ta) status = gt68xx_device_lamp_control (scanner->dev, SANE_FALSE, SANE_TRUE); else status = gt68xx_device_lamp_control (scanner->dev, SANE_TRUE, SANE_FALSE); if (status != SANE_STATUS_GOOD) { DBG (5, "gt68xx_scanner_calibrate: gt68xx_device_lamp_control failed: %s\n", sane_strstatus (status)); return status; } if (!scanner->dev->model->is_cis) usleep (500000); if (params.color) { gt68xx_calibrator_eval_black (scanner->cal_r, 0.0); gt68xx_calibrator_eval_black (scanner->cal_g, 0.0); gt68xx_calibrator_eval_black (scanner->cal_b, 0.0); gt68xx_calibrator_finish_setup (scanner->cal_r); gt68xx_calibrator_finish_setup (scanner->cal_g); gt68xx_calibrator_finish_setup (scanner->cal_b); } else { gt68xx_calibrator_eval_black (scanner->cal_gray, 0.0); gt68xx_calibrator_finish_setup (scanner->cal_gray); } return SANE_STATUS_GOOD; } SANE_Status gt68xx_scanner_start_scan (GT68xx_Scanner * scanner, GT68xx_Scan_Request * request, GT68xx_Scan_Parameters * params) { request->mbs = SANE_FALSE; /* don't go home before real scan */ request->mds = SANE_TRUE; request->mas = SANE_FALSE; if (request->use_ta) { gt68xx_device_lamp_control (scanner->dev, SANE_FALSE, SANE_TRUE); request->lamp = SANE_FALSE; } else { gt68xx_device_lamp_control (scanner->dev, SANE_TRUE, SANE_FALSE); request->lamp = SANE_TRUE; } if (!scanner->dev->model->is_cis) sleep (2); return gt68xx_scanner_start_scan_extended (scanner, request, SA_SCAN, params); } SANE_Status gt68xx_scanner_read_line (GT68xx_Scanner * scanner, unsigned int **buffer_pointers) { SANE_Status status; status = gt68xx_line_reader_read (scanner->reader, buffer_pointers); if (status != SANE_STATUS_GOOD) { DBG (5, "gt68xx_scanner_read_line: gt68xx_line_reader_read failed: %s\n", sane_strstatus (status)); return status; } if (scanner->calib) { if (scanner->reader->params.color) { gt68xx_calibrator_process_line (scanner->cal_r, buffer_pointers[0]); gt68xx_calibrator_process_line (scanner->cal_g, buffer_pointers[1]); gt68xx_calibrator_process_line (scanner->cal_b, buffer_pointers[2]); } else { if (scanner->dev->model->is_cis && !(scanner->dev->model->flags & GT68XX_FLAG_CIS_LAMP)) { if (strcmp (scanner->val[OPT_GRAY_MODE_COLOR].s, GT68XX_COLOR_BLUE) == 0) gt68xx_calibrator_process_line (scanner->cal_b, buffer_pointers[0]); else if (strcmp (scanner->val[OPT_GRAY_MODE_COLOR].s, GT68XX_COLOR_GREEN) == 0) gt68xx_calibrator_process_line (scanner->cal_g, buffer_pointers[0]); else gt68xx_calibrator_process_line (scanner->cal_r, buffer_pointers[0]); } else { gt68xx_calibrator_process_line (scanner->cal_gray, buffer_pointers[0]); } } } return SANE_STATUS_GOOD; } SANE_Status gt68xx_scanner_stop_scan (GT68xx_Scanner * scanner) { if (scanner->reader) { gt68xx_line_reader_free (scanner->reader); scanner->reader = NULL; } return gt68xx_device_stop_scan (scanner->dev); } /************************************************************************/ /* */ /* AFE offset/gain automatic configuration */ /* */ /************************************************************************/ typedef struct GT68xx_Afe_Values GT68xx_Afe_Values; struct GT68xx_Afe_Values { SANE_Int black; /* minimum black (0-255) */ SANE_Int white; /* maximum white (0-255) */ SANE_Int total_white; /* average white of the complete line (0-65536) */ SANE_Int calwidth; SANE_Int callines; SANE_Int max_width; SANE_Int scan_dpi; SANE_Fixed start_black; SANE_Int offset_direction; SANE_Int coarse_black; SANE_Int coarse_white; }; /************************************************************************/ /* CCD scanners */ /************************************************************************/ /** Calculate average black and maximum white * * This function is used for CCD scanners. The black mark to the left is used * for the calculation of average black. The remaining calibration strip * is used for searching the segment whose white average is the highest. * * @param values AFE values * @param buffer scanned line */ static void gt68xx_afe_ccd_calc (GT68xx_Afe_Values * values, unsigned int *buffer) { SANE_Int start_black; SANE_Int end_black; SANE_Int start_white; SANE_Int end_white; SANE_Int i; SANE_Int max_black = 0; SANE_Int min_black = 255; SANE_Int max_white = 0; SANE_Int total_white = 0; /* set size of black mark and white segments */ start_black = SANE_UNFIX (values->start_black) * values->scan_dpi / MM_PER_INCH; end_black = start_black + 1.0 * values->scan_dpi / MM_PER_INCH; /* 1 mm */ /* 5mm after mark */ start_white = end_black + 5.0 * values->scan_dpi / MM_PER_INCH; end_white = values->calwidth; DBG (5, "gt68xx_afe_ccd_calc: dpi=%d, start_black=%d, end_black=%d, start_white=%d, end_white=%d\n", values->scan_dpi, start_black, end_black, start_white, end_white); /* calc min and max black value */ for (i = start_black; i < end_black; i++) { if ((SANE_Int) (buffer[i] >> 8) < min_black) min_black = (buffer[i] >> 8); if ((SANE_Int) (buffer[i] >> 8) > max_black) max_black = (buffer[i] >> 8); #ifdef DEBUG_BLACK if ((buffer[i] >> 8) > 15) fprintf (stderr, "+"); else if ((buffer[i] >> 8) < 5) fprintf (stderr, "-"); else fprintf (stderr, "_"); #endif } #ifdef DEBUG_BLACK fprintf (stderr, "\n"); #endif for (i = start_white; i < end_white; ++i) { if ((SANE_Int) (buffer[i] >> 8) > max_white) max_white = (buffer[i] >> 8); total_white += buffer[i]; } values->total_white = total_white / (end_white - start_white); values->black = min_black; values->white = max_white; if (values->white < 50 || values->black > 150 || values->white - values->black < 30 || max_black - min_black > 15) DBG (1, "gt68xx_afe_ccd_calc: WARNING: max_white %3d min_black %3d max_black %3d\n", values->white, values->black, max_black); else DBG (5, "gt68xx_afe_ccd_calc: max_white %3d min_black %3d max_black %3d\n", values->white, values->black, max_black); } static SANE_Bool gt68xx_afe_ccd_adjust_offset_gain (SANE_String_Const color_name, GT68xx_Afe_Values * values, unsigned int *buffer, SANE_Byte * offset, SANE_Byte * pga, SANE_Byte * old_offset, SANE_Byte * old_pga) { SANE_Int black_low = values->coarse_black, black_high = black_low + 10; SANE_Int white_high = values->coarse_white, white_low = white_high - 10; SANE_Bool done = SANE_TRUE; SANE_Byte local_pga = *pga; SANE_Byte local_offset = *offset; gt68xx_afe_ccd_calc (values, buffer); #if 0 /* test all offset values */ local_offset++; done = SANE_FALSE; goto finish; #endif if (values->white > white_high) { if (values->black > black_high) local_offset += values->offset_direction; else if (values->black < black_low) local_pga--; else { local_offset += values->offset_direction; local_pga--; } done = SANE_FALSE; goto finish; } else if (values->white < white_low) { if (values->black < black_low) local_offset -= values->offset_direction; else if (values->black > black_high) local_pga++; else { local_offset -= values->offset_direction; local_pga++; } done = SANE_FALSE; goto finish; } if (values->black > black_high) { if (values->white > white_high) local_offset += values->offset_direction; else if (values->white < white_low) local_pga++; else { local_offset += values->offset_direction; local_pga++; } done = SANE_FALSE; goto finish; } else if (values->black < black_low) { if (values->white < white_low) local_offset -= values->offset_direction; else if (values->white > white_high) local_pga--; else { local_offset -= values->offset_direction; local_pga--; } done = SANE_FALSE; goto finish; } finish: if ((local_pga == *pga) && (local_offset == *offset)) done = SANE_TRUE; if ((local_pga == *old_pga) && (local_offset == *old_offset)) done = SANE_TRUE; *old_pga = *pga; *old_offset = *offset; DBG (4, "%5s white=%3d, black=%3d, offset=%2d, gain=%2d, old offs=%2d, " "old gain=%2d, total_white=%5d %s\n", color_name, values->white, values->black, local_offset, local_pga, *offset, *pga, values->total_white, done ? "DONE " : ""); *pga = local_pga; *offset = local_offset; return done; } /* Wait for lamp to give stable brightness */ static SANE_Status gt68xx_wait_lamp_stable (GT68xx_Scanner * scanner, GT68xx_Scan_Parameters * params, GT68xx_Scan_Request *request, unsigned int *buffer_pointers[3], GT68xx_Afe_Values *values, SANE_Bool dont_move) { SANE_Status status = SANE_STATUS_GOOD; SANE_Int last_white = 0; SANE_Bool first = SANE_TRUE; SANE_Bool message_printed = SANE_FALSE; struct timeval now, start_time; int secs_lamp_on, secs_start; int increase = -5; gettimeofday (&start_time, 0); do { usleep (200000); if (!first && dont_move) { request->mbs = SANE_FALSE; request->mds = SANE_FALSE; } first = SANE_FALSE; /* read line */ status = gt68xx_scanner_start_scan_extended (scanner, request, SA_CALIBRATE_ONE_LINE, params); if (status != SANE_STATUS_GOOD) { DBG (3, "gt68xx_wait_lamp_stable: gt68xx_scanner_start_scan_extended " "failed: %s\n", sane_strstatus (status)); return status; } status = gt68xx_line_reader_read (scanner->reader, buffer_pointers); if (status != SANE_STATUS_GOOD) { DBG (3, "gt68xx_wait_lamp_stable: gt68xx_line_reader_read failed: %s\n", sane_strstatus (status)); return status; } gt68xx_scanner_stop_scan (scanner); gt68xx_afe_ccd_calc (values, buffer_pointers[0]); DBG (4, "gt68xx_wait_lamp_stable: this white = %d, last white = %d\n", values->total_white, last_white); gettimeofday (&now, 0); 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_start > 5) { DBG (0, "Please wait for lamp warm-up\n"); message_printed = SANE_TRUE; } if (scanner->val[OPT_AUTO_WARMUP].w == SANE_TRUE) { if (scanner->dev->model->flags & GT68XX_FLAG_CIS_LAMP) { if (values->total_white <= (last_white - 20)) increase--; if (values->total_white >= last_white) increase++; if (increase > 0 && (values->total_white <= (last_white + 20)) && values->total_white != 0) break; } else { if ((values->total_white <= (last_white + 20)) && values->total_white != 0) break; /* lamp is warmed up */ } } last_white = values->total_white; } while (secs_lamp_on <= WARMUP_TIME); DBG (3, "gt68xx_wait_lamp_stable: Lamp is stable after %d secs (%d secs total)\n", secs_start, secs_lamp_on); return status; } /** Select best AFE gain and offset parameters. * * This function must be called before the main scan to choose the best values * for the AFE gains and offsets. It performs several one-line scans of the * calibration strip. * * @param scanner Scanner object. * @param orig_request Scan parameters. * * @returns * - #SANE_STATUS_GOOD - gain and offset setting completed successfully * - other error value - failure of some internal function */ static SANE_Status gt68xx_afe_ccd_auto (GT68xx_Scanner * scanner, GT68xx_Scan_Request * orig_request) { SANE_Status status; GT68xx_Scan_Parameters params; GT68xx_Scan_Request request; int i; GT68xx_Afe_Values values; unsigned int *buffer_pointers[3]; GT68xx_AFE_Parameters *afe = scanner->dev->afe, old_afe; SANE_Bool gray_done = SANE_FALSE; SANE_Bool red_done = SANE_FALSE, green_done = SANE_FALSE, blue_done = SANE_FALSE; values.offset_direction = 1; if (scanner->dev->model->flags & GT68XX_FLAG_OFFSET_INV) values.offset_direction = -1; memset (&old_afe, 255, sizeof (old_afe)); request.x0 = SANE_FIX (0.0); request.xs = scanner->dev->model->x_size; request.xdpi = 300; request.ydpi = 300; request.depth = 8; request.color = orig_request->color; /* request.color = SANE_TRUE; */ request.mas = SANE_FALSE; request.mbs = SANE_FALSE; request.mds = SANE_TRUE; request.calculate = SANE_FALSE; request.use_ta = orig_request->use_ta; if (orig_request->use_ta) { gt68xx_device_lamp_control (scanner->dev, SANE_FALSE, SANE_TRUE); request.lamp = SANE_FALSE; } else { gt68xx_device_lamp_control (scanner->dev, SANE_TRUE, SANE_FALSE); request.lamp = SANE_TRUE; } /* read line */ status = gt68xx_scanner_start_scan_extended (scanner, &request, SA_CALIBRATE_ONE_LINE, ¶ms); if (status != SANE_STATUS_GOOD) { DBG (3, "gt68xx_afe_ccd_auto: gt68xx_scanner_start_scan_extended failed: %s\n", sane_strstatus (status)); return status; } values.scan_dpi = params.xdpi; values.calwidth = params.pixel_xs; values.max_width = (params.pixel_xs * scanner->dev->model->optical_xdpi) / params.xdpi; if (orig_request->use_ta) values.start_black = SANE_FIX (20.0); else values.start_black = scanner->dev->model->x_offset_mark; values.coarse_black = 1; values.coarse_white = 254; request.mds = SANE_FALSE; DBG (5, "gt68xx_afe_ccd_auto: scan_dpi=%d, calwidth=%d, max_width=%d, " "start_black=%.1f mm\n", values.scan_dpi, values.calwidth, values.max_width, SANE_UNFIX (values.start_black)); status = gt68xx_line_reader_read (scanner->reader, buffer_pointers); if (status != SANE_STATUS_GOOD) { DBG (3, "gt68xx_afe_ccd_auto: gt68xx_line_reader_read failed: %s\n", sane_strstatus (status)); return status; } gt68xx_scanner_stop_scan (scanner); status = gt68xx_wait_lamp_stable (scanner, ¶ms, &request, buffer_pointers, &values, SANE_FALSE); if (status != SANE_STATUS_GOOD) { DBG (1, "gt68xx_afe_ccd_auto: gt68xx_wait_lamp_stable failed %s\n", sane_strstatus (status)); return status; } i = 0; do { i++; /* read line */ status = gt68xx_scanner_start_scan_extended (scanner, &request, SA_CALIBRATE_ONE_LINE, ¶ms); if (status != SANE_STATUS_GOOD) { DBG (3, "gt68xx_afe_ccd_auto: gt68xx_scanner_start_scan_extended failed: %s\n", sane_strstatus (status)); return status; } status = gt68xx_line_reader_read (scanner->reader, buffer_pointers); if (status != SANE_STATUS_GOOD) { DBG (3, "gt68xx_afe_ccd_auto: gt68xx_line_reader_read failed: %s\n", sane_strstatus (status)); return status; } if (params.color) { /* red */ if (!red_done) red_done = gt68xx_afe_ccd_adjust_offset_gain ("red", &values, buffer_pointers[0], &afe->r_offset, &afe->r_pga, &old_afe.r_offset, &old_afe.r_pga); /* green */ if (!green_done) green_done = gt68xx_afe_ccd_adjust_offset_gain ("green", &values, buffer_pointers[1], &afe->g_offset, &afe->g_pga, &old_afe.g_offset, &old_afe.g_pga); /* blue */ if (!blue_done) blue_done = gt68xx_afe_ccd_adjust_offset_gain ("blue", &values, buffer_pointers[2], &afe->b_offset, &afe->b_pga, &old_afe.b_offset, &old_afe.b_pga); } else { if (strcmp (scanner->val[OPT_GRAY_MODE_COLOR].s, GT68XX_COLOR_BLUE) == 0) { gray_done = gt68xx_afe_ccd_adjust_offset_gain ("gray", &values, buffer_pointers[0], &afe->b_offset, &afe->b_pga, &old_afe.b_offset, &old_afe.b_pga); } else if (strcmp (scanner->val[OPT_GRAY_MODE_COLOR].s, GT68XX_COLOR_GREEN) == 0) { gray_done = gt68xx_afe_ccd_adjust_offset_gain ("gray", &values, buffer_pointers[0], &afe->g_offset, &afe->g_pga, &old_afe.g_offset, &old_afe.g_pga); afe->r_offset = afe->b_offset = 0x20; afe->r_pga = afe->b_pga = 0x18; } else { gray_done = gt68xx_afe_ccd_adjust_offset_gain ("gray", &values, buffer_pointers[0], &afe->r_offset, &afe->r_pga, &old_afe.r_offset, &old_afe.r_pga); } } gt68xx_scanner_stop_scan (scanner); } while (((params.color && (!red_done || !green_done || !blue_done)) || (!params.color && !gray_done)) && i < 100); return status; } /************************************************************************/ /* CIS scanners */ /************************************************************************/ static void gt68xx_afe_cis_calc_black (GT68xx_Afe_Values * values, unsigned int *black_buffer) { SANE_Int start_black; SANE_Int end_black; SANE_Int i, j; SANE_Int min_black = 255; SANE_Int average = 0; start_black = 0; end_black = values->calwidth; /* find min average black value */ for (i = start_black; i < end_black; ++i) { SANE_Int avg_black = 0; for (j = 0; j < values->callines; j++) avg_black += (*(black_buffer + i + j * values->calwidth) >> 8); avg_black /= values->callines; average += avg_black; if (avg_black < min_black) min_black = avg_black; } values->black = min_black; average /= (end_black - start_black); DBG (5, "gt68xx_afe_cis_calc_black: min_black=0x%02x, average_black=0x%02x\n", values->black, average); } static void gt68xx_afe_cis_calc_white (GT68xx_Afe_Values * values, unsigned int *white_buffer) { SANE_Int start_white; SANE_Int end_white; SANE_Int i, j; SANE_Int max_white = 0; start_white = 0; end_white = values->calwidth; values->total_white = 0; /* find max average white value */ for (i = start_white; i < end_white; ++i) { SANE_Int avg_white = 0; for (j = 0; j < values->callines; j++) { avg_white += (*(white_buffer + i + j * values->calwidth) >> 8); values->total_white += (*(white_buffer + i + j * values->calwidth)); } avg_white /= values->callines; if (avg_white > max_white) max_white = avg_white; } values->white = max_white; values->total_white /= (values->callines * (end_white - start_white)); DBG (5, "gt68xx_afe_cis_calc_white: max_white=0x%02x, average_white=0x%02x\n", values->white, values->total_white >> 8); } static SANE_Bool gt68xx_afe_cis_adjust_gain_offset (SANE_String_Const color_name, GT68xx_Afe_Values * values, unsigned int *black_buffer, unsigned int *white_buffer, GT68xx_AFE_Parameters * afe, GT68xx_AFE_Parameters * old_afe) { SANE_Byte *offset, *old_offset, *gain, *old_gain; SANE_Int o, g; SANE_Int black_low = values->coarse_black, black_high = black_low + 10; SANE_Int white_high = values->coarse_white, white_low = white_high - 10; SANE_Bool done = SANE_TRUE; gt68xx_afe_cis_calc_black (values, black_buffer); gt68xx_afe_cis_calc_white (values, white_buffer); if (strcmp (color_name, "red") == 0) { offset = &(afe->r_offset); old_offset = &old_afe->r_offset; gain = &afe->r_pga; old_gain = &old_afe->r_pga; } else if (strcmp (color_name, "green") == 0) { offset = &afe->g_offset; old_offset = &old_afe->g_offset; gain = &afe->g_pga; old_gain = &old_afe->g_pga; } else { offset = &afe->b_offset; old_offset = &old_afe->b_offset; gain = &afe->b_pga; old_gain = &old_afe->b_pga; } o = *offset; g = *gain; if (values->white > white_high) { if (values->black > black_high) o -= values->offset_direction; else if (values->black < black_low) g--; else { o -= values->offset_direction; g--; } done = SANE_FALSE; goto finish; } else if (values->white < white_low) { if (values->black < black_low) o += values->offset_direction; else if (values->black > black_high) g++; else { o += values->offset_direction; g++; } done = SANE_FALSE; goto finish; } if (values->black > black_high) { if (values->white > white_high) o -= values->offset_direction; else if (values->white < white_low) g++; else { o -= values->offset_direction; g++; } done = SANE_FALSE; goto finish; } else if (values->black < black_low) { if (values->white < white_low) o += values->offset_direction; else if (values->white > white_high) g--; else { o += values->offset_direction; g--; } done = SANE_FALSE; goto finish; } finish: if (g < 0) g = 0; if (g > 48) g = 48; if (o < 0) o = 0; if (o > 64) o = 64; if ((g == *gain) && (o == *offset)) done = SANE_TRUE; if ((g == *old_gain) && (o == *old_offset)) done = SANE_TRUE; *old_gain = *gain; *old_offset = *offset; DBG (4, "%5s white=%3d, black=%3d, offset=0x%02X, gain=0x%02X, old offs=0x%02X, " "old gain=0x%02X, total_white=%5d %s\n", color_name, values->white, values->black, o, g, *offset, *gain, values->total_white, done ? "DONE " : ""); *gain = g; *offset = o; return done; } static SANE_Bool gt68xx_afe_cis_adjust_exposure (SANE_String_Const color_name, GT68xx_Afe_Values * values, unsigned int *white_buffer, SANE_Int border, SANE_Int * exposure_time) { SANE_Int exposure_change = 0; gt68xx_afe_cis_calc_white (values, white_buffer); if (values->white < border) { exposure_change = ((border - values->white) * 1); (*exposure_time) += exposure_change; DBG (4, "%5s: white = %3d, total_white=%5d (exposure too low) --> exposure += %d (=0x%03x)\n", color_name, values->white, values->total_white, exposure_change, *exposure_time); return SANE_FALSE; } else if (values->white > border + 5) { exposure_change = -((values->white - (border + 5)) * 1); (*exposure_time) += exposure_change; DBG (4, "%5s: white = %3d, total_white=%5d (exposure too high) --> exposure -= %d (=0x%03x)\n", color_name, values->white, values->total_white, exposure_change, *exposure_time); return SANE_FALSE; } else { DBG (4, "%5s: white = %3d, total_white=%5d (exposure ok=0x%03x)\n", color_name, values->white, values->total_white, *exposure_time); } return SANE_TRUE; } static SANE_Status gt68xx_afe_cis_read_lines (GT68xx_Afe_Values * values, GT68xx_Scanner * scanner, SANE_Bool lamp, SANE_Bool first, unsigned int *r_buffer, unsigned int *g_buffer, unsigned int *b_buffer) { SANE_Status status; int line; unsigned int *buffer_pointers[3]; GT68xx_Scan_Request request; GT68xx_Scan_Parameters params; request.x0 = SANE_FIX (0.0); request.xs = scanner->dev->model->x_size; request.xdpi = 300; request.ydpi = 300; request.depth = 8; request.color = SANE_TRUE; request.mas = SANE_FALSE; request.calculate = SANE_FALSE; request.use_ta = SANE_FALSE; if (first) /* go to start position */ { request.mbs = SANE_TRUE; request.mds = SANE_TRUE; } else { request.mbs = SANE_FALSE; request.mds = SANE_FALSE; } request.lamp = lamp; if (!r_buffer) /* First, set the size parameters */ { request.calculate = SANE_TRUE; RIE (gt68xx_device_setup_scan (scanner->dev, &request, SA_CALIBRATE_ONE_LINE, ¶ms)); values->scan_dpi = params.xdpi; values->calwidth = params.pixel_xs; values->callines = params.pixel_ys; values->start_black = scanner->dev->model->x_offset_mark; return SANE_STATUS_GOOD; } if (first && (scanner->dev->model->flags & GT68XX_FLAG_CIS_LAMP)) { if (request.use_ta) { gt68xx_device_lamp_control (scanner->dev, SANE_FALSE, SANE_TRUE); request.lamp = SANE_FALSE; } else { gt68xx_device_lamp_control (scanner->dev, SANE_TRUE, SANE_FALSE); request.lamp = SANE_TRUE; } status = gt68xx_wait_lamp_stable (scanner, ¶ms, &request, buffer_pointers, values, SANE_TRUE); if (status != SANE_STATUS_GOOD) { DBG (1, "gt68xx_afe_cis_read_lines: gt68xx_wait_lamp_stable failed %s\n", sane_strstatus (status)); return status; } request.mbs = SANE_FALSE; request.mds = SANE_FALSE; } status = gt68xx_scanner_start_scan_extended (scanner, &request, SA_CALIBRATE_ONE_LINE, ¶ms); if (status != SANE_STATUS_GOOD) { DBG (5, "gt68xx_afe_cis_read_lines: gt68xx_scanner_start_scan_extended failed: %s\n", sane_strstatus (status)); return status; } values->scan_dpi = params.xdpi; values->calwidth = params.pixel_xs; values->callines = params.pixel_ys; values->coarse_black = 2; values->coarse_white = 253; if (r_buffer && g_buffer && b_buffer) for (line = 0; line < values->callines; line++) { status = gt68xx_line_reader_read (scanner->reader, buffer_pointers); if (status != SANE_STATUS_GOOD) { DBG (5, "gt68xx_afe_cis_read_lines: gt68xx_line_reader_read failed: %s\n", sane_strstatus (status)); return status; } memcpy (r_buffer + values->calwidth * line, buffer_pointers[0], values->calwidth * sizeof (unsigned int)); memcpy (g_buffer + values->calwidth * line, buffer_pointers[1], values->calwidth * sizeof (unsigned int)); memcpy (b_buffer + values->calwidth * line, buffer_pointers[2], values->calwidth * sizeof (unsigned int)); } status = gt68xx_scanner_stop_scan (scanner); if (status != SANE_STATUS_GOOD) { DBG (5, "gt68xx_afe_cis_read_lines: gt68xx_scanner_stop_scan failed: %s\n", sane_strstatus (status)); return status; } return SANE_STATUS_GOOD; } static SANE_Status gt68xx_afe_cis_auto (GT68xx_Scanner * scanner) { SANE_Status status; int total_count, exposure_count; GT68xx_Afe_Values values; GT68xx_AFE_Parameters *afe = scanner->dev->afe, old_afe; GT68xx_Exposure_Parameters *exposure = scanner->dev->exposure; SANE_Int red_done, green_done, blue_done; SANE_Bool first = SANE_TRUE; unsigned int *r_gbuffer = 0, *g_gbuffer = 0, *b_gbuffer = 0; unsigned int *r_obuffer = 0, *g_obuffer = 0, *b_obuffer = 0; DBG (5, "gt68xx_afe_cis_auto: start\n"); if (scanner->dev->model->flags & GT68XX_FLAG_NO_CALIBRATE) { return SANE_STATUS_GOOD; } memset (&old_afe, 255, sizeof (old_afe)); /* Start with the preset exposure settings */ memcpy (scanner->dev->exposure, &scanner->dev->model->exposure, sizeof (*scanner->dev->exposure)); RIE (gt68xx_afe_cis_read_lines (&values, scanner, SANE_FALSE, SANE_FALSE, r_gbuffer, g_gbuffer, b_gbuffer)); r_gbuffer = malloc (values.calwidth * values.callines * sizeof (unsigned int)); g_gbuffer = malloc (values.calwidth * values.callines * sizeof (unsigned int)); b_gbuffer = malloc (values.calwidth * values.callines * sizeof (unsigned int)); r_obuffer = malloc (values.calwidth * values.callines * sizeof (unsigned int)); g_obuffer = malloc (values.calwidth * values.callines * sizeof (unsigned int)); b_obuffer = malloc (values.calwidth * values.callines * sizeof (unsigned int)); if (!r_gbuffer || !g_gbuffer || !b_gbuffer || !r_obuffer || !g_obuffer || !b_obuffer) return SANE_STATUS_NO_MEM; total_count = 0; red_done = green_done = blue_done = SANE_FALSE; old_afe.r_offset = old_afe.g_offset = old_afe.b_offset = 255; old_afe.r_pga = old_afe.g_pga = old_afe.b_pga = 255; do { values.offset_direction = 1; if (scanner->dev->model->flags & GT68XX_FLAG_OFFSET_INV) values.offset_direction = -1; RIE (gt68xx_afe_cis_read_lines (&values, scanner, SANE_FALSE, first, r_obuffer, g_obuffer, b_obuffer)); RIE (gt68xx_afe_cis_read_lines (&values, scanner, SANE_TRUE, SANE_FALSE, r_gbuffer, g_gbuffer, b_gbuffer)); if (!red_done) red_done = gt68xx_afe_cis_adjust_gain_offset ("red", &values, r_obuffer, r_gbuffer, afe, &old_afe); if (!green_done) green_done = gt68xx_afe_cis_adjust_gain_offset ("green", &values, g_obuffer, g_gbuffer, afe, &old_afe); if (!blue_done) blue_done = gt68xx_afe_cis_adjust_gain_offset ("blue", &values, b_obuffer, b_gbuffer, afe, &old_afe); total_count++; first = SANE_FALSE; } while (total_count < 100 && (!red_done || !green_done || !blue_done)); if (!red_done || !green_done || !blue_done) DBG (0, "gt68xx_afe_cis_auto: setting AFE reached limit\n"); /* Exposure time */ exposure_count = 0; red_done = green_done = blue_done = SANE_FALSE; do { /* read white line */ RIE (gt68xx_afe_cis_read_lines (&values, scanner, SANE_TRUE, SANE_FALSE, r_gbuffer, g_gbuffer, b_gbuffer)); if (!red_done) red_done = gt68xx_afe_cis_adjust_exposure ("red", &values, r_gbuffer, 245, &exposure->r_time); if (!green_done) green_done = gt68xx_afe_cis_adjust_exposure ("green", &values, g_gbuffer, 245, &exposure->g_time); if (!blue_done) blue_done = gt68xx_afe_cis_adjust_exposure ("blue", &values, b_gbuffer, 245, &exposure->b_time); exposure_count++; total_count++; } while ((!red_done || !green_done || !blue_done) && exposure_count < 50); if (!red_done || !green_done || !blue_done) DBG (0, "gt68xx_afe_cis_auto: setting exposure reached limit\n"); /* store afe calibration when needed */ if(scanner->dev->model->flags & GT68XX_FLAG_HAS_CALIBRATE) { memcpy(&(scanner->afe_params), afe, sizeof(GT68xx_AFE_Parameters)); scanner->exposure_params.r_time=exposure->r_time; scanner->exposure_params.g_time=exposure->g_time; scanner->exposure_params.b_time=exposure->b_time; } free (r_gbuffer); free (g_gbuffer); free (b_gbuffer); free (r_obuffer); free (g_obuffer); free (b_obuffer); DBG (4, "gt68xx_afe_cis_auto: total_count: %d\n", total_count); return SANE_STATUS_GOOD; } /** @brief create and copy calibrator * Creates a calibrator of the given width and copy data from reference * to initialize it * @param calibator pointer to the calibrator to create * @param reference calibrator with reference data to copy * @param width the width in pixels of the calibrator * @param offset offset in pixels when copying data from reference * @return SANE_STATUS_GOOD and a filled calibrator if enough memory */ static SANE_Status gt68xx_calibrator_create_copy (GT68xx_Calibrator ** calibrator, GT68xx_Calibrator * reference, int width, int offset) { SANE_Status status; int i; if (reference == NULL) { DBG (1, "gt68xx_calibrator_create_copy: NULL reference, skipping...\n"); *calibrator = NULL; return SANE_STATUS_GOOD; } /* check for reference overflow */ if(width+offset>reference->width) { DBG (1, "gt68xx_calibrator_create_copy: required with and offset exceed reference width\n"); return SANE_STATUS_INVAL; } status = gt68xx_calibrator_new (width, 65535, calibrator); if (status != SANE_STATUS_GOOD) { DBG (1, "gt68xx_calibrator_create_copy: failed to create calibrator: %s\n", sane_strstatus (status)); return status; } for(i=0;ik_white[i]=reference->k_white[i+offset]; (*calibrator)->k_black[i]=reference->k_black[i+offset]; (*calibrator)->white_line[i]=reference->white_line[i+offset]; (*calibrator)->black_line[i]=reference->black_line[i+offset]; } return status; } static SANE_Status gt68xx_sheetfed_move_to_scan_area (GT68xx_Scanner * scanner, GT68xx_Scan_Request * request) { SANE_Status status; if (!(scanner->dev->model->flags & GT68XX_FLAG_SHEET_FED) || scanner->dev->model->command_set->move_paper == NULL) return SANE_STATUS_GOOD; /* send move paper command */ RIE (scanner->dev->model->command_set->move_paper (scanner->dev, request)); /* wait until paper is set to the desired position */ return gt68xx_scanner_wait_for_positioning (scanner); } /**< number of consecutive white line to detect a white area */ #define WHITE_LINES 2 /** @brief calibrate sheet fed scanner * This function calibrates sheet fed scanner by scanning a calibration * target (which may be a blank page). It first move to a white area then * does afe and exposure calibration. Then it scans white lines to get data * for shading correction. * @param scanner structure describing the frontend session and the device * @return SANE_STATUS_GOOD is everything goes right, SANE_STATUS_INVAL * otherwise. */ static SANE_Status gt68xx_sheetfed_scanner_calibrate (GT68xx_Scanner * scanner) { SANE_Status status; GT68xx_Scan_Request request; GT68xx_Scan_Parameters params; int count, i, x, y, white; unsigned int *buffer_pointers[3]; #ifdef DEBUG_CALIBRATION FILE *fcal; char title[50]; #endif DBG (3, "gt68xx_sheetfed_scanner_calibrate: start.\n"); /* clear calibration if needed */ gt68xx_scanner_free_calibrators (scanner); for (i = 0; i < MAX_RESOLUTIONS; i++) { if(scanner->calibrations[i].red!=NULL) { gt68xx_calibrator_free (scanner->calibrations[i].red); } if(scanner->calibrations[i].green!=NULL) { gt68xx_calibrator_free (scanner->calibrations[i].green); } if(scanner->calibrations[i].blue!=NULL) { gt68xx_calibrator_free (scanner->calibrations[i].blue); } if(scanner->calibrations[i].gray!=NULL) { gt68xx_calibrator_free (scanner->calibrations[i].gray); } } scanner->calibrated = SANE_FALSE; /* find minimum horizontal resolution */ request.xdpi = 9600; for (i = 0; scanner->dev->model->xdpi_values[i] != 0; i++) { if (scanner->dev->model->xdpi_values[i] < request.xdpi) { request.xdpi = scanner->dev->model->xdpi_values[i]; request.ydpi = scanner->dev->model->xdpi_values[i]; } } /* move to white area SA_CALIBRATE uses its own y0/ys fixed values */ request.x0 = 0; request.y0 = scanner->dev->model->y_offset_calib; request.xs = scanner->dev->model->x_size; request.depth = 8; request.color = SANE_FALSE; request.mbs = SANE_TRUE; request.mds = SANE_TRUE; request.mas = SANE_FALSE; request.lamp = SANE_TRUE; request.calculate = SANE_FALSE; request.use_ta = SANE_FALSE; request.backtrack = SANE_FALSE; request.backtrack_lines = 0; /* skip start of calibration sheet */ status = gt68xx_sheetfed_move_to_scan_area (scanner, &request); if (status != SANE_STATUS_GOOD) { DBG (1, "gt68xx_sheetfed_scanner_calibrate: failed to skip start of calibration sheet %s\n", sane_strstatus (status)); return status; } status = gt68xx_device_lamp_control (scanner->dev, SANE_FALSE, SANE_TRUE); if (status != SANE_STATUS_GOOD) { DBG (1, "gt68xx_sheetfed_scanner_calibrate: gt68xx_device_lamp_control returned %s\n", sane_strstatus (status)); return status; } /* loop until we find a white area to calibrate on */ i = 0; request.y0 = 0; do { /* start scan */ status = gt68xx_scanner_start_scan_extended (scanner, &request, SA_CALIBRATE, ¶ms); if (status != SANE_STATUS_GOOD) { DBG (1, "gt68xx_sheetfed_scanner_calibrate: gt68xx_scanner_start_scan_extended returned %s\n", sane_strstatus (status)); return status; } /* loop until we find WHITE_LINES consecutive white lines or we reach and of area */ white = 0; y = 0; do { status = gt68xx_line_reader_read (scanner->reader, buffer_pointers); if (status != SANE_STATUS_GOOD) { DBG (1, "gt68xx_sheetfed_scanner_calibrate: gt68xx_line_reader_read returned %s\n", sane_strstatus (status)); gt68xx_scanner_stop_scan (scanner); return status; } /* check for white line */ count = 0; for (x = 0; x < params.pixel_xs; x++) { if (((buffer_pointers[0][x] >> 8) & 0xff) > 50) { count++; } } /* line is white if 93% is above black level */ if ((100 * count) / params.pixel_xs < 93) { white = 0; } else { white++; } y++; } while ((white < WHITE_LINES) && (y < params.pixel_ys)); /* end scan */ gt68xx_scanner_stop_scan (scanner); i++; } while (i < 20 && white < WHITE_LINES); /* check if we found a white area */ if (white != WHITE_LINES) { DBG (1, "gt68xx_sheetfed_scanner_calibrate: didn't find a white area\n"); return SANE_STATUS_INVAL; } /* now do calibration */ scanner->auto_afe = SANE_TRUE; scanner->calib = SANE_TRUE; /* loop at each possible xdpi to create calibrators */ i = 0; while (scanner->dev->model->xdpi_values[i] > 0) { request.xdpi = scanner->dev->model->xdpi_values[i]; request.ydpi = scanner->dev->model->xdpi_values[i]; request.x0 = 0; request.y0 = 0; request.xs = scanner->dev->model->x_size; request.color = SANE_FALSE; request.mbs = SANE_FALSE; request.mds = SANE_TRUE; request.mas = SANE_FALSE; request.lamp = SANE_TRUE; request.calculate = SANE_FALSE; request.use_ta = SANE_FALSE; request.backtrack = SANE_FALSE; request.backtrack_lines = 0; /* calibrate in color */ request.color = SANE_TRUE; status = gt68xx_scanner_calibrate (scanner, &request); if (status != SANE_STATUS_GOOD) { DBG (1, "gt68xx_sheetfed_scanner_calibrate: gt68xx_scanner_calibrate returned %s\n", sane_strstatus (status)); return status; } /* since auto afe is done at a fixed resolution, we don't need to * do each each time, once is enough */ scanner->auto_afe = SANE_FALSE; /* allocate and save per dpi calibrators */ scanner->calibrations[i].dpi = request.xdpi; /* recompute params */ request.calculate = SANE_TRUE; gt68xx_device_setup_scan (scanner->dev, &request, SA_SCAN, ¶ms); scanner->calibrations[i].pixel_x0 = params.pixel_x0; status = gt68xx_calibrator_create_copy (&(scanner->calibrations[i].red), scanner->cal_r, scanner->cal_r->width, 0); if (status != SANE_STATUS_GOOD) { DBG (1, "gt68xx_sheetfed_scanner_calibrate: failed to create red calibrator: %s\n", sane_strstatus (status)); return status; } status = gt68xx_calibrator_create_copy (&(scanner->calibrations[i].green), scanner->cal_g, scanner->cal_g->width, 0); if (status != SANE_STATUS_GOOD) { DBG (1, "gt68xx_sheetfed_scanner_calibrate: failed to create green calibrator: %s\n", sane_strstatus (status)); return status; } status = gt68xx_calibrator_create_copy (&(scanner->calibrations[i].blue), scanner->cal_b, scanner->cal_b->width, 0); if (status != SANE_STATUS_GOOD) { DBG (1, "gt68xx_sheetfed_scanner_calibrate: failed to create blue calibrator: %s\n", sane_strstatus (status)); return status; } /* calibrate in gray */ request.color = SANE_FALSE; status = gt68xx_scanner_calibrate (scanner, &request); if (status != SANE_STATUS_GOOD) { DBG (1, "gt68xx_sheetfed_scanner_calibrate: gt68xx_scanner_calibrate returned %s\n", sane_strstatus (status)); return status; } if (scanner->cal_gray) { status = gt68xx_calibrator_create_copy (&(scanner->calibrations[i].gray), scanner->cal_gray, scanner->cal_gray->width, 0); if (status != SANE_STATUS_GOOD) { DBG (1, "gt68xx_sheetfed_scanner_calibrate: failed to create gray calibrator: %s\n", sane_strstatus (status)); return status; } } #ifdef DEBUG_CALIBRATION sprintf (title, "cal-%03d-red.pnm", scanner->calibrations[i].dpi); fcal = fopen (title, "wb"); if (fcal != NULL) { fprintf (fcal, "P5\n%d 1\n255\n", params.pixel_xs); for (x = 0; x < params.pixel_xs; x++) fputc ((scanner->calibrations[i].red->k_white[x] >> 8) & 0xff, fcal); fclose (fcal); } sprintf (title, "cal-%03d-green.pnm", scanner->calibrations[i].dpi); fcal = fopen (title, "wb"); if (fcal != NULL) { fprintf (fcal, "P5\n%d 1\n255\n", params.pixel_xs); for (x = 0; x < params.pixel_xs; x++) fputc ((scanner->calibrations[i].green->k_white[x] >> 8) & 0xff, fcal); fclose (fcal); } sprintf (title, "cal-%03d-blue.pnm", scanner->calibrations[i].dpi); fcal = fopen (title, "wb"); if (fcal != NULL) { fprintf (fcal, "P5\n%d 1\n255\n", params.pixel_xs); for (x = 0; x < params.pixel_xs; x++) fputc ((scanner->calibrations[i].blue->k_white[x] >> 8) & 0xff, fcal); fclose (fcal); } #endif /* next resolution */ i++; } scanner->calibrated = SANE_TRUE; /* eject calibration target from feeder */ gt68xx_device_paperfeed (scanner->dev); /* save calibration to file */ gt68xx_write_calibration (scanner); DBG (3, "gt68xx_sheetfed_scanner_calibrate: end.\n"); return SANE_STATUS_GOOD; } /** @brief assign calibration for scan * This function creates the calibrators and set up afe for the requested * scan. It uses calibration data that has been created by * gt68xx_sheetfed_scanner_calibrate. * @param scanner structure describing the frontend session and the device * @return SANE_STATUS_GOOD is everything goes right, SANE_STATUS_INVAL * otherwise. */ static SANE_Status gt68xx_assign_calibration (GT68xx_Scanner * scanner, GT68xx_Scan_Parameters params) { int i, dpi, offset; SANE_Status status = SANE_STATUS_GOOD; DBG (3, "gt68xx_assign_calibration: start.\n"); dpi = params.xdpi; DBG (4, "gt68xx_assign_calibration: searching calibration for %d dpi\n", dpi); /* search matching dpi */ i = 0; while (scanner->calibrations[i].dpi > 0 && scanner->calibrations[i].dpi != dpi) { i++; } /* check if found a match */ if (scanner->calibrations[i].dpi == 0) { DBG (4, "gt68xx_assign_calibration: failed to find calibration for %d dpi\n", dpi); return SANE_STATUS_INVAL; } DBG (4, "gt68xx_assign_calibration: using entry %d for %d dpi\n", i, dpi); DBG (5, "gt68xx_assign_calibration: using scan_parameters: pixel_x0=%d, pixel_xs=%d \n", params.pixel_x0, params.pixel_xs); /* AFE/exposure data copy */ memcpy (scanner->dev->afe, &(scanner->afe_params), sizeof (GT68xx_AFE_Parameters)); scanner->dev->exposure->r_time = scanner->exposure_params.r_time; scanner->dev->exposure->g_time = scanner->exposure_params.g_time; scanner->dev->exposure->b_time = scanner->exposure_params.b_time; /* free calibrators if needed */ gt68xx_scanner_free_calibrators (scanner); /* TODO compute offset based on the x0 value from scan_request */ offset = params.pixel_x0 - scanner->calibrations[i].pixel_x0; /* calibrator allocation and copy */ if (scanner->calibrations[i].red!=NULL) { status = gt68xx_calibrator_create_copy (&(scanner->cal_r), scanner->calibrations[i].red, params.pixel_xs, offset); if (status != SANE_STATUS_GOOD) { DBG (1, "gt68xx_assign_calibration: failed to create calibrator: %s\n", sane_strstatus (status)); return status; } } if (scanner->calibrations[i].green!=NULL) { status = gt68xx_calibrator_create_copy (&(scanner->cal_g), scanner->calibrations[i].green, params.pixel_xs, offset); if (status != SANE_STATUS_GOOD) { DBG (1, "gt68xx_assign_calibration: failed to create calibrator: %s\n", sane_strstatus (status)); return status; } } if (scanner->calibrations[i].blue!=NULL) { status = gt68xx_calibrator_create_copy (&(scanner->cal_b), scanner->calibrations[i].blue, params.pixel_xs, offset); if (status != SANE_STATUS_GOOD) { DBG (1, "gt68xx_assign_calibration: failed to create calibrator: %s\n", sane_strstatus (status)); return status; } } if (scanner->calibrations[i].gray!=NULL) { status = gt68xx_calibrator_create_copy (&(scanner->cal_gray), scanner->calibrations[i].gray, params.pixel_xs, offset); if (status != SANE_STATUS_GOOD) { DBG (1, "gt68xx_assign_calibration: failed to create calibrator: %s\n", sane_strstatus (status)); return status; } } DBG (3, "gt68xx_assign_calibration: end.\n"); return status; } static char *gt68xx_calibration_file(GT68xx_Scanner * scanner) { char *ptr=NULL; char tmp_str[PATH_MAX]; ptr=getenv("HOME"); if(ptr!=NULL) { sprintf (tmp_str, "%s/.sane/gt68xx-%s.cal", ptr, scanner->dev->model->name); } else { ptr=getenv("TMPDIR"); if(ptr!=NULL) { sprintf (tmp_str, "%s/gt68xx-%s.cal", ptr, scanner->dev->model->name); } else { sprintf (tmp_str, "/tmp/gt68xx-%s.cal", scanner->dev->model->name); } } DBG(5,"gt68xx_calibration_file: using >%s< for calibration file name\n",tmp_str); return strdup(tmp_str); } static SANE_Status gt68xx_clear_calibration (GT68xx_Scanner * scanner) { char *fname; int i; if (scanner->calibrated == SANE_FALSE) return SANE_STATUS_GOOD; /* clear file */ fname = gt68xx_calibration_file (scanner); unlink (fname); free (fname); /* free calibrators */ for (i = 0; i < MAX_RESOLUTIONS && scanner->calibrations[i].dpi > 0; i++) { scanner->calibrations[i].dpi = 0; if (scanner->calibrations[i].red) gt68xx_calibrator_free (scanner->calibrations[i].red); if (scanner->calibrations[i].green) gt68xx_calibrator_free (scanner->calibrations[i].green); if (scanner->calibrations[i].blue) gt68xx_calibrator_free (scanner->calibrations[i].blue); if (scanner->calibrations[i].gray) gt68xx_calibrator_free (scanner->calibrations[i].gray); } /* reset flags */ scanner->calibrated = SANE_FALSE; scanner->val[OPT_QUALITY_CAL].w = SANE_FALSE; scanner->val[OPT_NEED_CALIBRATION_SW].w = SANE_TRUE; DBG (5, "gt68xx_clear_calibration: done\n"); return SANE_STATUS_GOOD; } static SANE_Status gt68xx_write_calibration (GT68xx_Scanner * scanner) { char *fname; FILE *fcal; int i; SANE_Int nullwidth = 0; if (scanner->calibrated == SANE_FALSE) { return SANE_STATUS_GOOD; } /* open file */ fname = gt68xx_calibration_file (scanner); fcal = fopen (fname, "wb"); free (fname); if (fcal == NULL) { DBG (1, "gt68xx_write_calibration: failed to open calibration file for writing %s\n", strerror (errno)); return SANE_STATUS_IO_ERROR; } /* TODO we save check endianness and word alignment in case of a home * directory used trough different archs */ fwrite (&(scanner->afe_params), sizeof (GT68xx_AFE_Parameters), 1, fcal); fwrite (&(scanner->exposure_params), sizeof (GT68xx_Exposure_Parameters), 1, fcal); for (i = 0; i < MAX_RESOLUTIONS && scanner->calibrations[i].dpi > 0; i++) { DBG (1, "gt68xx_write_calibration: saving %d dpi calibration\n", scanner->calibrations[i].dpi); fwrite (&(scanner->calibrations[i].dpi), sizeof (SANE_Int), 1, fcal); fwrite (&(scanner->calibrations[i].pixel_x0), sizeof (SANE_Int), 1, fcal); fwrite (&(scanner->calibrations[i].red->width), sizeof (SANE_Int), 1, fcal); fwrite (&(scanner->calibrations[i].red->white_level), sizeof (SANE_Int), 1, fcal); fwrite (scanner->calibrations[i].red->k_white, sizeof (unsigned int), scanner->calibrations[i].red->width, fcal); fwrite (scanner->calibrations[i].red->k_black, sizeof (unsigned int), scanner->calibrations[i].red->width, fcal); fwrite (scanner->calibrations[i].red->white_line, sizeof (double), scanner->calibrations[i].red->width, fcal); fwrite (scanner->calibrations[i].red->black_line, sizeof (double), scanner->calibrations[i].red->width, fcal); fwrite (&(scanner->calibrations[i].green->width), sizeof (SANE_Int), 1, fcal); fwrite (&(scanner->calibrations[i].green->white_level), sizeof (SANE_Int), 1, fcal); fwrite (scanner->calibrations[i].green->k_white, sizeof (unsigned int), scanner->calibrations[i].green->width, fcal); fwrite (scanner->calibrations[i].green->k_black, sizeof (unsigned int), scanner->calibrations[i].green->width, fcal); fwrite (scanner->calibrations[i].green->white_line, sizeof (double), scanner->calibrations[i].green->width, fcal); fwrite (scanner->calibrations[i].green->black_line, sizeof (double), scanner->calibrations[i].green->width, fcal); fwrite (&(scanner->calibrations[i].blue->width), sizeof (SANE_Int), 1, fcal); fwrite (&(scanner->calibrations[i].blue->white_level), sizeof (SANE_Int), 1, fcal); fwrite (scanner->calibrations[i].blue->k_white, sizeof (unsigned int), scanner->calibrations[i].blue->width, fcal); fwrite (scanner->calibrations[i].blue->k_black, sizeof (unsigned int), scanner->calibrations[i].blue->width, fcal); fwrite (scanner->calibrations[i].blue->white_line, sizeof (double), scanner->calibrations[i].blue->width, fcal); fwrite (scanner->calibrations[i].blue->black_line, sizeof (double), scanner->calibrations[i].blue->width, fcal); if (scanner->calibrations[i].gray != NULL) { fwrite (&(scanner->calibrations[i].gray->width), sizeof (SANE_Int), 1, fcal); fwrite (&(scanner->calibrations[i].gray->white_level), sizeof (SANE_Int), 1, fcal); fwrite (scanner->calibrations[i].gray->k_white, sizeof (unsigned int), scanner->calibrations[i].gray->width, fcal); fwrite (scanner->calibrations[i].gray->k_black, sizeof (unsigned int), scanner->calibrations[i].gray->width, fcal); fwrite (scanner->calibrations[i].gray->white_line, sizeof (double), scanner->calibrations[i].gray->width, fcal); fwrite (scanner->calibrations[i].gray->black_line, sizeof (double), scanner->calibrations[i].gray->width, fcal); } else { fwrite (&nullwidth, sizeof (SANE_Int), 1, fcal); } } DBG (5, "gt68xx_write_calibration: wrote %d calibrations\n", i); fclose (fcal); return SANE_STATUS_GOOD; } static SANE_Status gt68xx_read_calibration (GT68xx_Scanner * scanner) { char *fname; FILE *fcal; int i; SANE_Int width, level; scanner->calibrated = SANE_FALSE; fname = gt68xx_calibration_file (scanner); fcal = fopen (fname, "rb"); free (fname); if (fcal == NULL) { DBG (1, "gt68xx_read_calibration: failed to open calibration file for reading %s\n", strerror (errno)); return SANE_STATUS_IO_ERROR; } /* TODO we should check endianness and word alignment in case of a home * directory used trough different archs */ /* TODO check for errors */ fread (&(scanner->afe_params), sizeof (GT68xx_AFE_Parameters), 1, fcal); fread (&(scanner->exposure_params), sizeof (GT68xx_Exposure_Parameters), 1, fcal); /* loop on calibrators */ i = 0; fread (&(scanner->calibrations[i].dpi), sizeof (SANE_Int), 1, fcal); while (!feof (fcal) && scanner->calibrations[i].dpi > 0) { fread (&(scanner->calibrations[i].pixel_x0), sizeof (SANE_Int), 1, fcal); fread (&width, sizeof (SANE_Int), 1, fcal); fread (&level, sizeof (SANE_Int), 1, fcal); gt68xx_calibrator_new (width, level, &(scanner->calibrations[i].red)); fread (scanner->calibrations[i].red->k_white, sizeof (unsigned int), width, fcal); fread (scanner->calibrations[i].red->k_black, sizeof (unsigned int), width, fcal); fread (scanner->calibrations[i].red->white_line, sizeof (double), width, fcal); fread (scanner->calibrations[i].red->black_line, sizeof (double), width, fcal); fread (&width, sizeof (SANE_Int), 1, fcal); fread (&level, sizeof (SANE_Int), 1, fcal); gt68xx_calibrator_new (width, level, &(scanner->calibrations[i].green)); fread (scanner->calibrations[i].green->k_white, sizeof (unsigned int), width, fcal); fread (scanner->calibrations[i].green->k_black, sizeof (unsigned int), width, fcal); fread (scanner->calibrations[i].green->white_line, sizeof (double), width, fcal); fread (scanner->calibrations[i].green->black_line, sizeof (double), width, fcal); fread (&width, sizeof (SANE_Int), 1, fcal); fread (&level, sizeof (SANE_Int), 1, fcal); gt68xx_calibrator_new (width, level, &(scanner->calibrations[i].blue)); fread (scanner->calibrations[i].blue->k_white, sizeof (unsigned int), width, fcal); fread (scanner->calibrations[i].blue->k_black, sizeof (unsigned int), width, fcal); fread (scanner->calibrations[i].blue->white_line, sizeof (double), width, fcal); fread (scanner->calibrations[i].blue->black_line, sizeof (double), width, fcal); fread (&width, sizeof (SANE_Int), 1, fcal); if (width > 0) { fread (&level, sizeof (SANE_Int), 1, fcal); gt68xx_calibrator_new (width, level, &(scanner->calibrations[i].gray)); fread (scanner->calibrations[i].gray->k_white, sizeof (unsigned int), width, fcal); fread (scanner->calibrations[i].gray->k_black, sizeof (unsigned int), width, fcal); fread (scanner->calibrations[i].gray->white_line, sizeof (double), width, fcal); fread (scanner->calibrations[i].gray->black_line, sizeof (double), width, fcal); } /* prepare for nex resolution */ i++; fread (&(scanner->calibrations[i].dpi), sizeof (SANE_Int), 1, fcal); } DBG (5, "gt68xx_read_calibration: read %d calibrations\n", i); fclose (fcal); scanner->val[OPT_QUALITY_CAL].w = SANE_TRUE; scanner->val[OPT_NEED_CALIBRATION_SW].w = SANE_FALSE; scanner->calibrated = SANE_TRUE; return SANE_STATUS_GOOD; } /* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */ backends-1.3.0/backend/gt68xx_high.h000066400000000000000000000277241456256263500172260ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2002 Sergey Vlasov Copyright (C) 2002-2007 Henning Geinitz 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, see . 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 GT68XX_HIGH_H #define GT68XX_HIGH_H #include "gt68xx_mid.h" typedef struct GT68xx_Calibrator GT68xx_Calibrator; typedef struct GT68xx_Calibration GT68xx_Calibration; typedef struct GT68xx_Scanner GT68xx_Scanner; /** Calibration data for one channel. */ struct GT68xx_Calibrator { unsigned int *k_white; /**< White point vector */ unsigned int *k_black; /**< Black point vector */ double *white_line; /**< White average */ double *black_line; /**< Black average */ SANE_Int width; /**< Image width */ SANE_Int white_level; /**< Desired white level */ SANE_Int white_count; /**< Number of white lines scanned */ SANE_Int black_count; /**< Number of black lines scanned */ #ifdef TUNE_CALIBRATOR SANE_Int min_clip_count; /**< Count of too low values */ SANE_Int max_clip_count; /**< Count of too high values */ #endif /* TUNE_CALIBRATOR */ }; /** Calibration data for a given resolution */ struct GT68xx_Calibration { SANE_Int dpi; /**< optical horizontal dpi used to build the calibration data */ SANE_Int pixel_x0; /**< x start position used at calibration time */ GT68xx_Calibrator *gray; /**< Calibrator for grayscale data */ GT68xx_Calibrator *red; /**< Calibrator for the red channel */ GT68xx_Calibrator *green; /**< Calibrator for the green channel */ GT68xx_Calibrator *blue; /**< Calibrator for the blue channel */ }; /** Create a new calibrator for one (color or mono) channel. * * @param width Image width in pixels. * @param white_level Desired white level (65535 max). * @param cal_return Returned pointer to the created calibrator object. * * @return * - SANE_STATUS_GOOD - the calibrator object was created. * - SANE_STATUS_INVAL - invalid parameters. * - SANE_STATUS_NO_MEM - not enough memory to create the object. */ static SANE_Status gt68xx_calibrator_new (SANE_Int width, SANE_Int white_level, GT68xx_Calibrator ** cal_return); /** Destroy the channel calibrator object. * * @param cal Calibrator object. */ static SANE_Status gt68xx_calibrator_free (GT68xx_Calibrator * cal); /** Add a white calibration line to the calibrator. * * This function should be called after scanning each white calibration line. * The line width must be equal to the value passed to gt68xx_calibrator_new(). * * @param cal Calibrator object. * @param line Pointer to the line data. * * @return * - #SANE_STATUS_GOOD - the line data was processed successfully. */ static SANE_Status gt68xx_calibrator_add_white_line (GT68xx_Calibrator * cal, unsigned int *line); /** Calculate the white point for the calibrator. * * This function should be called when all white calibration lines have been * scanned. After doing this, gt68xx_calibrator_add_white_line() should not be * called again for this calibrator. * * @param cal Calibrator object. * @param factor White point correction factor. * * @return * - #SANE_STATUS_GOOD - the white point was calculated successfully. */ static SANE_Status gt68xx_calibrator_eval_white (GT68xx_Calibrator * cal, double factor); /** Add a black calibration line to the calibrator. * * This function should be called after scanning each black calibration line. * The line width must be equal to the value passed to gt68xx_calibrator_new(). * * @param cal Calibrator object. * @param line Pointer to the line data. * * @return * - #SANE_STATUS_GOOD - the line data was processed successfully. */ static SANE_Status gt68xx_calibrator_add_black_line (GT68xx_Calibrator * cal, unsigned int *line); /** Calculate the black point for the calibrator. * * This function should be called when all black calibration lines have been * scanned. After doing this, gt68xx_calibrator_add_black_line() should not be * called again for this calibrator. * * @param cal Calibrator object. * @param factor Black point correction factor. * * @return * - #SANE_STATUS_GOOD - the white point was calculated successfully. */ static SANE_Status gt68xx_calibrator_eval_black (GT68xx_Calibrator * cal, double factor); /** Finish the calibrator setup and prepare for real scanning. * * This function must be called after gt68xx_calibrator_eval_white() and * gt68xx_calibrator_eval_black(). * * @param cal Calibrator object. * * @return * - #SANE_STATUS_GOOD - the calibrator setup completed successfully. */ static SANE_Status gt68xx_calibrator_finish_setup (GT68xx_Calibrator * cal); /** Process the image line through the calibrator. * * This function must be called only after gt68xx_calibrator_finish_setup(). * The image line is modified in place. * * @param cal Calibrator object. * @param line Pointer to the image line data. * * @return * - #SANE_STATUS_GOOD - the image line was processed successfully. */ static SANE_Status gt68xx_calibrator_process_line (GT68xx_Calibrator * cal, unsigned int *line); /** List of SANE options */ enum GT68xx_Option { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_MODE, OPT_GRAY_MODE_COLOR, OPT_SOURCE, OPT_PREVIEW, OPT_BIT_DEPTH, OPT_RESOLUTION, OPT_LAMP_OFF_AT_EXIT, OPT_BACKTRACK, OPT_DEBUG_GROUP, OPT_AUTO_WARMUP, OPT_FULL_SCAN, OPT_COARSE_CAL, OPT_COARSE_CAL_ONCE, OPT_QUALITY_CAL, OPT_BACKTRACK_LINES, OPT_ENHANCEMENT_GROUP, OPT_GAMMA_VALUE, OPT_THRESHOLD, OPT_GEOMETRY_GROUP, OPT_TL_X, /* top-left x */ OPT_TL_Y, /* top-left y */ OPT_BR_X, /* bottom-right x */ OPT_BR_Y, /* bottom-right y */ OPT_SENSOR_GROUP, OPT_NEED_CALIBRATION_SW, /* signals calibration is needed */ OPT_PAGE_LOADED_SW, /* signals that a document is inserted in feeder */ OPT_BUTTON_GROUP, OPT_CALIBRATE, /* button option to trigger call to sheetfed calibration */ OPT_CLEAR_CALIBRATION, /* clear calibration */ /* must come last: */ NUM_OPTIONS }; /** Scanner object. */ struct GT68xx_Scanner { struct GT68xx_Scanner *next; /**< Next scanner in list */ GT68xx_Device *dev; /**< Low-level device object */ GT68xx_Line_Reader *reader; /**< Line reader object */ GT68xx_Calibrator *cal_gray; /**< Calibrator for grayscale data */ GT68xx_Calibrator *cal_r; /**< Calibrator for the red channel */ GT68xx_Calibrator *cal_g; /**< Calibrator for the green channel */ GT68xx_Calibrator *cal_b; /**< Calibrator for the blue channel */ /* SANE data */ SANE_Bool scanning; /**< We are currently scanning */ SANE_Option_Descriptor opt[NUM_OPTIONS]; /**< Option descriptors */ Option_Value val[NUM_OPTIONS]; /**< Option values */ SANE_Parameters params; /**< SANE Parameters */ SANE_Int line; /**< Current line */ SANE_Int total_bytes; /**< Bytes already transmitted */ SANE_Int byte_count; /**< Bytes transmitted in this line */ SANE_Bool calib; /**< Apply calibration data */ SANE_Bool auto_afe; /**< Use automatic gain/offset */ SANE_Bool first_scan; /**< Is this the first scan? */ struct timeval lamp_on_time; /**< Time when the lamp was turned on */ struct timeval start_time; /**< Time when the scan was started */ SANE_Int bpp_list[5]; /**< */ SANE_Int *gamma_table; /**< Gray gamma table */ #ifdef DEBUG_BRIGHTNESS SANE_Int average_white; /**< For debugging brightness problems */ SANE_Int max_white; SANE_Int min_black; #endif /** SANE_TRUE when the scanner has been calibrated */ SANE_Bool calibrated; /** per horizontal resolution calibration data */ GT68xx_Calibration calibrations[MAX_RESOLUTIONS]; /* AFE and exposure settings */ GT68xx_AFE_Parameters afe_params; GT68xx_Exposure_Parameters exposure_params; }; /** Create a new scanner object. * * @param dev Low-level device object. * @param scanner_return Returned pointer to the created scanner object. */ static SANE_Status gt68xx_scanner_new (GT68xx_Device * dev, GT68xx_Scanner ** scanner_return); /** Destroy the scanner object. * * The low-level device object is not destroyed. * * @param scanner Scanner object. */ static SANE_Status gt68xx_scanner_free (GT68xx_Scanner * scanner); /** Calibrate the scanner before the main scan. * * @param scanner Scanner object. * @param request Scan request data. * @param use_autogain Enable automatic offset/gain control */ static SANE_Status gt68xx_scanner_calibrate (GT68xx_Scanner * scanner, GT68xx_Scan_Request * request); /** Start scanning the image. * * This function does not perform calibration - it needs to be performed before * by calling gt68xx_scanner_calibrate(). * * @param scanner Scanner object. * @param request Scan request data. * @param params Returned scan parameters (calculated from the request). */ static SANE_Status gt68xx_scanner_start_scan (GT68xx_Scanner * scanner, GT68xx_Scan_Request * request, GT68xx_Scan_Parameters * params); /** Read one image line from the scanner. * * This function can be called only during the scan - after calling * gt68xx_scanner_start_scan() and before calling gt68xx_scanner_stop_scan(). * * @param scanner Scanner object. * @param buffer_pointers Array of pointers to the image lines. */ static SANE_Status gt68xx_scanner_read_line (GT68xx_Scanner * scanner, unsigned int **buffer_pointers); /** Stop scanning the image. * * This function must be called to finish the scan started by * gt68xx_scanner_start_scan(). It may be called before all lines are read to * cancel the scan prematurely. * * @param scanner Scanner object. */ static SANE_Status gt68xx_scanner_stop_scan (GT68xx_Scanner * scanner); /** Save calibration data to file * * This function stores in memory calibration data created at calibration * time into file * @param scanner Scanner object. * @return SANE_STATUS_GOOD when successful */ static SANE_Status gt68xx_write_calibration (GT68xx_Scanner * scanner); /** Read calibration data from file * * This function sets in memory calibration data from data saved into file. * * @param scanner Scanner object. * @return SANE_STATUS_GOOD when successful */ static SANE_Status gt68xx_read_calibration (GT68xx_Scanner * scanner); #endif /* not GT68XX_HIGH_H */ /* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */ backends-1.3.0/backend/gt68xx_low.c000066400000000000000000000730301456256263500170720ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2002 Sergey Vlasov Copyright (C) 2002 - 2007 Henning Geinitz 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, see . 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. */ /** @file * @brief Implementation of the low-level scanner interface functions. */ #include "gt68xx_low.h" #include "../include/sane/sane.h" #include "../include/sane/sanei_debug.h" #include "../include/sane/sanei_usb.h" #include #include #ifdef USE_FORK #include #include #include "gt68xx_shm_channel.c" #endif /** Check that the device pointer is not NULL. * * @param dev Pointer to the device object (GT68xx_Device). * @param func_name Function name (for use in debug messages). */ #define CHECK_DEV_NOT_NULL(dev, func_name) \ do { \ IF_DBG( \ if (!(dev)) \ { \ DBG (0, "BUG: NULL device\n"); \ return SANE_STATUS_INVAL; \ } \ ) \ } while (SANE_FALSE) /** Check that the device is open. * * @param dev Pointer to the device object (GT68xx_Device). * @param func_name Function name (for use in debug messages). */ #define CHECK_DEV_OPEN(dev, func_name) \ do { \ IF_DBG( \ CHECK_DEV_NOT_NULL ((dev), (func_name)); \ if ((dev)->fd == -1) \ { \ DBG (0, "%s: BUG: device %p not open\n", (func_name), \ ((void *) dev)); \ return SANE_STATUS_INVAL; \ } \ ) \ } while (SANE_FALSE) /** Check that the device is open and active. * * @param dev Pointer to the device (GT68xx_Device). * @param func_name Function name (for use in debug messages). */ #define CHECK_DEV_ACTIVE(dev, func_name) \ do { \ IF_DBG( \ CHECK_DEV_OPEN ((dev), (func_name)); \ if (!(dev)->active) \ { \ DBG (0, "%s: BUG: device %p not active\n", (func_name), \ ((void *) dev)); \ return SANE_STATUS_INVAL; \ } \ ) \ } while (SANE_FALSE) #ifndef NDEBUG /** Dump a request packet for debugging purposes. * * @param prefix String printed before the packet contents. * @param req The request packet to be dumped. */ static void dump_req (SANE_String_Const prefix, GT68xx_Packet req) { int i; char buf[GT68XX_PACKET_SIZE * 3 + 1]; for (i = 0; i < GT68XX_PACKET_SIZE; ++i) sprintf (buf + i * 3, " %02x", req[i]); DBG (8, "%s%s\n", prefix, buf); } #endif /* not NDEBUG */ /** Dump a request packet if the debug level is at 8 or above. * * @param prefix String printed before the packet contents. * @param req The request packet to be dumped. */ #define DUMP_REQ(prefix, req) \ do { IF_DBG( if (DBG_LEVEL >= 8) dump_req ((prefix), (req)); ) } while (0) SANE_Status gt68xx_device_new (GT68xx_Device ** dev_return) { GT68xx_Device *dev; DBG (7, "gt68xx_device_new: enter\n"); if (!dev_return) return SANE_STATUS_INVAL; dev = (GT68xx_Device *) malloc (sizeof (GT68xx_Device)); if (!dev) { DBG (3, "gt68xx_device_new: couldn't malloc %lu bytes for device\n", (u_long) sizeof (GT68xx_Device)); *dev_return = 0; return SANE_STATUS_NO_MEM; } *dev_return = dev; memset (dev, 0, sizeof (GT68xx_Device)); dev->fd = -1; dev->active = SANE_FALSE; dev->model = NULL; dev->command_set_private = NULL; dev->read_buffer = NULL; dev->read_buffer_size = 32768; dev->manual_selection = SANE_FALSE; dev->scan_started = SANE_FALSE; #ifdef USE_FORK dev->shm_channel = NULL; #endif /* USE_FORK */ DBG (7, "gt68xx_device_new:: leave: ok\n"); return SANE_STATUS_GOOD; } SANE_Status gt68xx_device_free (GT68xx_Device * dev) { DBG (7, "gt68xx_device_free: enter: dev=%p\n", (void *) dev); if (dev) { if (dev->active) gt68xx_device_deactivate (dev); if (dev->fd != -1) gt68xx_device_close (dev); if (dev->model && dev->model->allocated) { DBG (7, "gt68xx_device_free: freeing model data %p\n", (void *) dev->model); free (dev->model); } DBG (7, "gt68xx_device_free: freeing dev\n"); free (dev); } DBG (7, "gt68xx_device_free: leave: ok\n"); return SANE_STATUS_GOOD; } static GT68xx_USB_Device_Entry * gt68xx_find_usb_device_entry (SANE_Word vendor, SANE_Word product) { GT68xx_USB_Device_Entry *entry; for (entry = gt68xx_usb_device_list; entry->model; ++entry) { if (vendor == entry->vendor && product == entry->product) return entry; } return NULL; } static SANE_Status gt68xx_device_identify (GT68xx_Device * dev) { SANE_Status status; SANE_Word vendor, product; GT68xx_USB_Device_Entry *entry; CHECK_DEV_OPEN (dev, "gt68xx_device_identify"); status = sanei_usb_get_vendor_product (dev->fd, &vendor, &product); if (status != SANE_STATUS_GOOD) { DBG (3, "gt68xx_device_identify: error getting USB id: %s\n", sane_strstatus (status)); return status; } entry = gt68xx_find_usb_device_entry (vendor, product); if (entry) { dev->model = entry->model; } else { dev->model = NULL; DBG (3, "gt68xx_device_identify: unknown USB device (vendor 0x%04x, " "product 0x%04x)\n", vendor, product); return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } SANE_Status gt68xx_device_open (GT68xx_Device * dev, const char *dev_name) { SANE_Status status; SANE_Int fd; DBG (7, "gt68xx_device_open: enter: dev=%p\n", (void *) dev); CHECK_DEV_NOT_NULL (dev, "gt68xx_device_open"); if (dev->fd != -1) { DBG (3, "gt68xx_device_open: device already open\n"); return SANE_STATUS_INVAL; } status = sanei_usb_open (dev_name, &fd); if (status != SANE_STATUS_GOOD) { DBG (3, "gt68xx_device_open: sanei_usb_open failed: %s\n", sane_strstatus (status)); return status; } dev->fd = fd; if (!dev->model) gt68xx_device_identify (dev); DBG (7, "gt68xx_device_open: leave: ok\n"); return SANE_STATUS_GOOD; } SANE_Status gt68xx_device_close (GT68xx_Device * dev) { DBG (7, "gt68xx_device_close: enter: dev=%p\n", (void *) dev); CHECK_DEV_OPEN (dev, "gt68xx_device_close"); if (dev->active) gt68xx_device_deactivate (dev); sanei_usb_close (dev->fd); dev->fd = -1; DBG (7, "gt68xx_device_close: leave: ok\n"); return SANE_STATUS_GOOD; } SANE_Bool gt68xx_device_is_configured (GT68xx_Device * dev) { if (dev && dev->model && dev->model->command_set) return SANE_TRUE; else return SANE_FALSE; } SANE_Status gt68xx_device_set_model (GT68xx_Device * dev, GT68xx_Model * model) { if (dev->active) { DBG (3, "gt68xx_device_set_model: device already active\n"); return SANE_STATUS_INVAL; } if (dev->model && dev->model->allocated) free (dev->model); dev->model = model; return SANE_STATUS_GOOD; } static SANE_Bool gt68xx_device_get_model (SANE_String name, GT68xx_Model ** model) { GT68xx_USB_Device_Entry *entry; for (entry = gt68xx_usb_device_list; entry->model; ++entry) { if (strcmp (name, entry->model->name) == 0) { *model = entry->model; return SANE_TRUE; } } return SANE_FALSE; } SANE_Status gt68xx_device_activate (GT68xx_Device * dev) { SANE_Status status; CHECK_DEV_OPEN (dev, "gt68xx_device_activate"); if (dev->active) { DBG (3, "gt68xx_device_activate: device already active\n"); return SANE_STATUS_INVAL; } if (!gt68xx_device_is_configured (dev)) { DBG (3, "gt68xx_device_activate: device is not configured\n"); return SANE_STATUS_INVAL; } DBG (7, "gt68xx_device_activate: model \"%s\"\n", dev->model->name); if (dev->model->command_set->activate) { status = (*dev->model->command_set->activate) (dev); if (status != SANE_STATUS_GOOD) { DBG (3, "gt68xx_device_activate: command-set-specific " "activate failed: %s\n", sane_strstatus (status)); return status; } } dev->afe = malloc (sizeof (*dev->afe)); dev->exposure = malloc (sizeof (*dev->exposure)); if (!dev->afe || !dev->exposure) return SANE_STATUS_NO_MEM; memcpy (dev->afe, &dev->model->afe_params, sizeof (*dev->afe)); memcpy (dev->exposure, &dev->model->exposure, sizeof (*dev->exposure)); dev->gamma_value = dev->model->default_gamma_value; dev->active = SANE_TRUE; return SANE_STATUS_GOOD; } SANE_Status gt68xx_device_deactivate (GT68xx_Device * dev) { SANE_Status status = SANE_STATUS_GOOD; CHECK_DEV_ACTIVE (dev, "gt68xx_device_deactivate"); if (dev->read_active) gt68xx_device_read_finish (dev); if (dev->model->command_set->deactivate) { status = (*dev->model->command_set->deactivate) (dev); if (status != SANE_STATUS_GOOD) { DBG (3, "gt68xx_device_deactivate: command set-specific deactivate failed: %s\n", sane_strstatus (status)); /* proceed with deactivate anyway */ } } if (dev->afe) free (dev->afe); dev->afe = 0; if (dev->exposure) free (dev->exposure); dev->exposure = 0; dev->active = SANE_FALSE; return status; } SANE_Status gt68xx_device_memory_write (GT68xx_Device * dev, SANE_Word addr, SANE_Word size, SANE_Byte * data) { SANE_Status status; DBG (8, "gt68xx_device_memory_write: dev=%p, addr=0x%x, size=0x%x, data=%p\n", (void *) dev, addr, size, (void *) data); CHECK_DEV_ACTIVE (dev, "gt68xx_device_memory_write"); status = sanei_usb_control_msg (dev->fd, 0x40, dev->model->command_set->request, dev->model->command_set->memory_write_value, addr, size, data); if (status != SANE_STATUS_GOOD) { DBG (3, "gt68xx_device_memory_write: sanei_usb_control_msg failed: %s\n", sane_strstatus (status)); } return status; } SANE_Status gt68xx_device_memory_read (GT68xx_Device * dev, SANE_Word addr, SANE_Word size, SANE_Byte * data) { SANE_Status status; DBG (8, "gt68xx_device_memory_read: dev=%p, addr=0x%x, size=0x%x, data=%p\n", (void *) dev, addr, size, (void *) data); CHECK_DEV_ACTIVE (dev, "gt68xx_device_memory_read"); status = sanei_usb_control_msg (dev->fd, 0xc0, dev->model->command_set->request, dev->model->command_set->memory_read_value, addr, size, data); if (status != SANE_STATUS_GOOD) { DBG (3, "gt68xx_device_memory_read: sanei_usb_control_msg failed: %s\n", sane_strstatus (status)); } return status; } static SANE_Status gt68xx_device_generic_req (GT68xx_Device * dev, SANE_Byte request_type, SANE_Word request, SANE_Word cmd_value, SANE_Word cmd_index, SANE_Word res_value, SANE_Word res_index, GT68xx_Packet cmd, GT68xx_Packet res, size_t res_size) { SANE_Status status; DBG (7, "gt68xx_device_generic_req: command=0x%02x\n", cmd[0]); DUMP_REQ (">>", cmd); CHECK_DEV_ACTIVE (dev, "gt68xx_device_generic_req"); status = sanei_usb_control_msg (dev->fd, request_type, request, cmd_value, cmd_index, GT68XX_PACKET_SIZE, cmd); if (status != SANE_STATUS_GOOD) { DBG (3, "gt68xx_device_generic_req: writing command failed: %s\n", sane_strstatus (status)); return status; } memset (res, 0, sizeof (GT68xx_Packet)); status = sanei_usb_control_msg (dev->fd, request_type | 0x80, request, res_value, res_index, res_size, res); if (status != SANE_STATUS_GOOD) { DBG (3, "gt68xx_device_generic_req: reading response failed: %s\n", sane_strstatus (status)); return status; } DUMP_REQ ("<<", res); return status; } SANE_Status gt68xx_device_req (GT68xx_Device * dev, GT68xx_Packet cmd, GT68xx_Packet res) { GT68xx_Command_Set *command_set = dev->model->command_set; return gt68xx_device_generic_req (dev, command_set->request_type, command_set->request, command_set->send_cmd_value, command_set->send_cmd_index, command_set->recv_res_value, command_set->recv_res_index, cmd, res, GT68XX_PACKET_SIZE); } SANE_Status gt68xx_device_small_req (GT68xx_Device * dev, GT68xx_Packet cmd, GT68xx_Packet res) { GT68xx_Command_Set *command_set = dev->model->command_set; GT68xx_Packet fixed_cmd; int i; for (i = 0; i < 8; ++i) memcpy (fixed_cmd + i * 8, cmd, 8); return gt68xx_device_generic_req (dev, command_set->request_type, command_set->request, command_set->send_small_cmd_value, command_set->send_small_cmd_index, command_set->recv_small_res_value, command_set->recv_small_res_index, fixed_cmd, res, 0x08); } SANE_Status gt68xx_device_download_firmware (GT68xx_Device * dev, SANE_Byte * data, SANE_Word size) { CHECK_DEV_ACTIVE (dev, "gt68xx_device_download_firmware"); if (dev->model->command_set->download_firmware) return (*dev->model->command_set->download_firmware) (dev, data, size); else return SANE_STATUS_UNSUPPORTED; } SANE_Status gt68xx_device_get_power_status (GT68xx_Device * dev, SANE_Bool * power_ok) { CHECK_DEV_ACTIVE (dev, "gt68xx_device_get_power_status"); if (dev->model->command_set->get_power_status) return (*dev->model->command_set->get_power_status) (dev, power_ok); else return SANE_STATUS_UNSUPPORTED; } SANE_Status gt68xx_device_get_ta_status (GT68xx_Device * dev, SANE_Bool * ta_attached) { CHECK_DEV_ACTIVE (dev, "gt68xx_device_get_ta_status"); if (dev->model->command_set->get_ta_status) return (*dev->model->command_set->get_ta_status) (dev, ta_attached); else return SANE_STATUS_UNSUPPORTED; } SANE_Status gt68xx_device_lamp_control (GT68xx_Device * dev, SANE_Bool fb_lamp, SANE_Bool ta_lamp) { CHECK_DEV_ACTIVE (dev, "gt68xx_device_lamp_control"); if (dev->model->command_set->lamp_control) return (*dev->model->command_set->lamp_control) (dev, fb_lamp, ta_lamp); else return SANE_STATUS_UNSUPPORTED; } SANE_Status gt68xx_device_is_moving (GT68xx_Device * dev, SANE_Bool * moving) { CHECK_DEV_ACTIVE (dev, "gt68xx_device_is_moving"); if (dev->model->command_set->is_moving) return (*dev->model->command_set->is_moving) (dev, moving); else return SANE_STATUS_UNSUPPORTED; } /* currently not used */ #if 0 static SANE_Status gt68xx_device_move_relative (GT68xx_Device * dev, SANE_Int distance) { CHECK_DEV_ACTIVE (dev, "gt68xx_device_move_relative"); if (dev->model->command_set->move_relative) return (*dev->model->command_set->move_relative) (dev, distance); else return SANE_STATUS_UNSUPPORTED; } #endif SANE_Status gt68xx_device_carriage_home (GT68xx_Device * dev) { CHECK_DEV_ACTIVE (dev, "gt68xx_device_carriage_home"); if (dev->model->command_set->carriage_home) return (*dev->model->command_set->carriage_home) (dev); else return SANE_STATUS_UNSUPPORTED; } SANE_Status gt68xx_device_paperfeed (GT68xx_Device * dev) { CHECK_DEV_ACTIVE (dev, "gt68xx_device_paperfeed"); if (dev->model->command_set->paperfeed) return (*dev->model->command_set->paperfeed) (dev); else return SANE_STATUS_UNSUPPORTED; } SANE_Status gt68xx_device_start_scan (GT68xx_Device * dev) { CHECK_DEV_ACTIVE (dev, "gt68xx_device_start_scan"); if (dev->model->command_set->start_scan) { if (!dev->scan_started) { dev->scan_started = SANE_TRUE; return (*dev->model->command_set->start_scan) (dev); } return SANE_STATUS_DEVICE_BUSY; } else return SANE_STATUS_UNSUPPORTED; } SANE_Status gt68xx_device_read_scanned_data (GT68xx_Device * dev, SANE_Bool * ready) { CHECK_DEV_ACTIVE (dev, "gt68xx_device_read_scanned_data"); if (dev->model->command_set->read_scanned_data) return (*dev->model->command_set->read_scanned_data) (dev, ready); else return SANE_STATUS_UNSUPPORTED; } SANE_Status gt68xx_device_setup_scan (GT68xx_Device * dev, GT68xx_Scan_Request * request, GT68xx_Scan_Action action, GT68xx_Scan_Parameters * params) { CHECK_DEV_ACTIVE (dev, "gt68xx_device_setup_scan"); if (dev->model->command_set->setup_scan) return (*dev->model->command_set->setup_scan) (dev, request, action, params); else return SANE_STATUS_UNSUPPORTED; } SANE_Status gt68xx_device_set_afe (GT68xx_Device * dev, GT68xx_AFE_Parameters * params) { CHECK_DEV_ACTIVE (dev, "gt68xx_device_set_afe"); if (dev->model->command_set->set_afe) return (*dev->model->command_set->set_afe) (dev, params); else return SANE_STATUS_UNSUPPORTED; } SANE_Status gt68xx_device_set_exposure_time (GT68xx_Device * dev, GT68xx_Exposure_Parameters * params) { CHECK_DEV_ACTIVE (dev, "gt68xx_device_set_exposure_time"); if (dev->model->command_set->set_exposure_time) return (*dev->model->command_set->set_exposure_time) (dev, params); else return SANE_STATUS_UNSUPPORTED; } SANE_Status gt68xx_device_stop_scan (GT68xx_Device * dev) { CHECK_DEV_ACTIVE (dev, "gt68xx_device_stop_scan"); if (dev->model->command_set->stop_scan) { if (dev->scan_started) { dev->scan_started = SANE_FALSE; return (*dev->model->command_set->stop_scan) (dev); } return SANE_STATUS_GOOD; // Essentially a NOP. } else return SANE_STATUS_UNSUPPORTED; } SANE_Status gt68xx_device_read_raw (GT68xx_Device * dev, SANE_Byte * buffer, size_t * size) { SANE_Status status; CHECK_DEV_ACTIVE (dev, "gt68xx_device_read_raw"); DBG (7, "gt68xx_device_read_raw: enter: size=%lu\n", (unsigned long) *size); status = sanei_usb_read_bulk (dev->fd, buffer, size); if (status != SANE_STATUS_GOOD) { DBG (3, "gt68xx_device_read_raw: bulk read failed: %s\n", sane_strstatus (status)); return status; } DBG (7, "gt68xx_device_read_raw: leave: size=%lu\n", (unsigned long) *size); return SANE_STATUS_GOOD; } SANE_Status gt68xx_device_set_read_buffer_size (GT68xx_Device * dev, size_t buffer_size) { CHECK_DEV_NOT_NULL (dev, "gt68xx_device_set_read_buffer_size"); if (dev->read_active) { DBG (3, "gt68xx_device_set_read_buffer_size: BUG: read already " "active\n"); return SANE_STATUS_INVAL; } buffer_size = (buffer_size + 63UL) & ~63UL; if (buffer_size > 0) { dev->requested_buffer_size = buffer_size; return SANE_STATUS_GOOD; } DBG (3, "gt68xx_device_set_read_buffer_size: bad buffer size\n"); return SANE_STATUS_INVAL; } SANE_Status gt68xx_device_read_prepare (GT68xx_Device * dev, size_t expected_count, SANE_Bool final_scan) { size_t buffer_size; CHECK_DEV_ACTIVE (dev, "gt68xx_device_read_prepare"); if (dev->read_active) { DBG (3, "gt68xx_device_read_prepare: read already active\n"); return SANE_STATUS_INVAL; } DBG (5, "gt68xx_device_read_prepare: total size: %lu bytes\n", (unsigned long) expected_count); buffer_size = dev->requested_buffer_size; DBG (5, "gt68xx_device_read_prepare: requested buffer size: %lu\n", (unsigned long) buffer_size); if (buffer_size > expected_count) { buffer_size = (expected_count + 63UL) & ~63UL; } DBG (5, "gt68xx_device_read_prepare: real size: %lu\n", (unsigned long) buffer_size); dev->read_buffer_size = buffer_size; dev->read_buffer = (SANE_Byte *) malloc (buffer_size); if (!dev->read_buffer) { DBG (3, "gt68xx_device_read_prepare: not enough memory for the read buffer (%lu bytes)\n", (unsigned long) buffer_size); return SANE_STATUS_NO_MEM; } dev->read_active = SANE_TRUE; dev->final_scan = final_scan; dev->read_pos = dev->read_bytes_in_buffer = 0; dev->read_bytes_left = expected_count; return SANE_STATUS_GOOD; } #ifdef USE_FORK static SANE_Status gt68xx_reader_process (GT68xx_Device * dev) { SANE_Status status = SANE_STATUS_GOOD; SANE_Int buffer_id; SANE_Byte *buffer_addr; size_t size; SANE_Int line = 0; size_t read_bytes_left = dev->read_bytes_left; shm_channel_writer_init (dev->shm_channel); while (read_bytes_left > 0) { status = shm_channel_writer_get_buffer (dev->shm_channel, &buffer_id, &buffer_addr); if (status != SANE_STATUS_GOOD) break; DBG (9, "gt68xx_reader_process: buffer %d: get\n", buffer_id); size = dev->read_buffer_size; DBG (9, "gt68xx_reader_process: buffer %d: trying to read %lu bytes " "(%lu bytes left, line %d)\n", buffer_id, (unsigned long) size, (unsigned long) read_bytes_left, line); status = gt68xx_device_read_raw (dev, buffer_addr, &size); if (status != SANE_STATUS_GOOD) break; DBG (9, "gt68xx_reader_process: buffer %d: read %lu bytes (line %d)\n", buffer_id, (unsigned long) size, line); status = shm_channel_writer_put_buffer (dev->shm_channel, buffer_id, size); if (status != SANE_STATUS_GOOD) break; DBG (9, "gt68xx_reader_process: buffer %d: put\n", buffer_id); read_bytes_left -= size; line++; } DBG (9, "gt68xx_reader_process: finished, now sleeping\n"); if (status != SANE_STATUS_GOOD) return status; sleep (5 * 60); /* wait until we are killed (or timeout) */ shm_channel_writer_close (dev->shm_channel); return status; } static SANE_Status gt68xx_device_read_start_fork (GT68xx_Device * dev) { SANE_Status status; int pid; if (dev->shm_channel) { DBG (3, "gt68xx_device_read_start_fork: BUG: shm_channel already created\n"); return SANE_STATUS_INVAL; } status = shm_channel_new (dev->read_buffer_size, SHM_BUFFERS, &dev->shm_channel); if (status != SANE_STATUS_GOOD) { DBG (3, "gt68xx_device_read_start_fork: cannot create shared memory channel: " "%s\n", sane_strstatus (status)); dev->shm_channel = NULL; return status; } pid = fork (); if (pid == -1) { DBG (3, "gt68xx_device_read_start_fork: cannot fork: %s\n", strerror (errno)); shm_channel_free (dev->shm_channel); dev->shm_channel = NULL; return SANE_STATUS_NO_MEM; } if (pid == 0) { /* Child process */ status = gt68xx_reader_process (dev); _exit (status); } else { /* Parent process */ dev->reader_pid = pid; shm_channel_reader_init (dev->shm_channel); shm_channel_reader_start (dev->shm_channel); return SANE_STATUS_GOOD; } } #endif /* USE_FORK */ static SANE_Status gt68xx_device_read_start (GT68xx_Device * dev) { CHECK_DEV_ACTIVE (dev, "gt68xx_device_read_start"); #ifdef USE_FORK /* Don't fork a separate process for every calibration scan. */ if (dev->final_scan) return gt68xx_device_read_start_fork (dev); #endif /* USE_FORK */ return SANE_STATUS_GOOD; } SANE_Status gt68xx_device_read (GT68xx_Device * dev, SANE_Byte * buffer, size_t * size) { SANE_Status status; size_t byte_count = 0; size_t left_to_read = *size; size_t transfer_size, block_size, raw_block_size; #ifdef USE_FORK SANE_Int buffer_id; SANE_Byte *buffer_addr; SANE_Int buffer_bytes; #endif /* USE_FORK */ CHECK_DEV_ACTIVE (dev, "gt68xx_device_read"); if (!dev->read_active) { DBG (3, "gt68xx_device_read: read not active\n"); return SANE_STATUS_INVAL; } while (left_to_read > 0) { if (dev->read_bytes_in_buffer == 0) { block_size = dev->read_buffer_size; if (block_size > dev->read_bytes_left) block_size = dev->read_bytes_left; if (block_size == 0) break; raw_block_size = (block_size + 63UL) & ~63UL; DBG (7, "gt68xx_device_read: trying to read %ld bytes\n", (long) raw_block_size); #ifdef USE_FORK if (dev->shm_channel) { status = shm_channel_reader_get_buffer (dev->shm_channel, &buffer_id, &buffer_addr, &buffer_bytes); if (status == SANE_STATUS_GOOD && buffer_addr != NULL) { DBG (9, "gt68xx_device_read: buffer %d: get\n", buffer_id); memcpy (dev->read_buffer, buffer_addr, buffer_bytes); shm_channel_reader_put_buffer (dev->shm_channel, buffer_id); DBG (9, "gt68xx_device_read: buffer %d: put\n", buffer_id); } } else #endif /* USE_FORK */ status = gt68xx_device_read_raw (dev, dev->read_buffer, &raw_block_size); if (status != SANE_STATUS_GOOD) { DBG (3, "gt68xx_device_read: read failed\n"); return status; } dev->read_pos = 0; dev->read_bytes_in_buffer = block_size; dev->read_bytes_left -= block_size; } transfer_size = left_to_read; if (transfer_size > dev->read_bytes_in_buffer) transfer_size = dev->read_bytes_in_buffer; if (transfer_size > 0) { memcpy (buffer, dev->read_buffer + dev->read_pos, transfer_size); dev->read_pos += transfer_size; dev->read_bytes_in_buffer -= transfer_size; byte_count += transfer_size; left_to_read -= transfer_size; buffer += transfer_size; } } *size = byte_count; if (byte_count == 0) return SANE_STATUS_EOF; else return SANE_STATUS_GOOD; } SANE_Status gt68xx_device_read_finish (GT68xx_Device * dev) { SANE_Status status = SANE_STATUS_GOOD; CHECK_DEV_ACTIVE (dev, "gt68xx_device_read_finish"); if (!dev->read_active) { DBG (3, "gt68xx_device_read_finish: read not active\n"); return SANE_STATUS_INVAL; } DBG (7, "gt68xx_device_read_finish: read_bytes_left = %ld\n", (long) dev->read_bytes_left); #ifdef USE_FORK if (dev->reader_pid != 0) { int pid_status; /* usleep (100000); */ DBG (7, "gt68xx_device_read_finish: trying to kill reader process\n"); kill (dev->reader_pid, SIGKILL); waitpid (dev->reader_pid, &pid_status, 0); if (WIFEXITED (pid_status)) status = WEXITSTATUS (pid_status); DBG (7, "gt68xx_device_read_finish: reader process killed\n"); dev->reader_pid = 0; } if (dev->shm_channel) { shm_channel_free (dev->shm_channel); dev->shm_channel = NULL; } #endif /* USE_FORK */ free (dev->read_buffer); dev->read_buffer = NULL; dev->read_active = SANE_FALSE; DBG (7, "gt68xx_device_read_finish: exit (%s)\n", sane_strstatus (status)); return status; } static SANE_Status gt68xx_device_check_result (GT68xx_Packet res, SANE_Byte command) { if (res[0] != 0) { DBG (1, "gt68xx_device_check_result: result was %2X %2X " "(expected: %2X %2X)\n", res[0], res[1], 0, command); return SANE_STATUS_IO_ERROR; } /* The Gt681xfw.usb firmware doesn't return the command byte in the second byte, so we can't rely on that test */ if (res[1] != command) DBG (5, "gt68xx_device_check_result: warning: result was %2X %2X " "(expected: %2X %2X)\n", res[0], res[1], 0, command); return SANE_STATUS_GOOD; } SANE_Status gt68xx_device_get_id (GT68xx_Device * dev) { CHECK_DEV_ACTIVE (dev, "gt68xx_device_get_id"); if (dev->model->command_set->get_id) return (*dev->model->command_set->get_id) (dev); else return SANE_STATUS_UNSUPPORTED; } static void gt68xx_device_fix_descriptor (GT68xx_Device * dev) { SANE_Byte data[8]; sanei_usb_control_msg (dev->fd, 0x80, 0x06, 0x01 << 8, 0, 8, data); } /* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */ backends-1.3.0/backend/gt68xx_low.h000066400000000000000000001100211456256263500170670ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2002 Sergey Vlasov Copyright (C) 2002 - 2007 Henning Geinitz 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, see . 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 GT68XX_LOW_H #define GT68XX_LOW_H /** @file * @brief Low-level scanner interface functions. */ #include "../include/sane/sane.h" #include #ifdef USE_FORK #include #include "gt68xx_shm_channel.h" #endif #ifdef NDEBUG #undef MAX_DEBUG #endif /* calculate the minimum/maximum values */ #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MAX(a, b) (((a) > (b)) ? (a) : (b)) /* return the lower/upper 8 bits of a 16 bit word */ #define HIBYTE(w) ((SANE_Byte)(((SANE_Word)(w) >> 8) & 0xFF)) #define LOBYTE(w) ((SANE_Byte)(w)) /* return if an error occurred while the function was called */ #ifdef MAX_DEBUG # define RIE(function) \ do \ { \ status = function; \ if (status != SANE_STATUS_GOOD) \ { \ DBG (7, "%s: %s: %s\n", __func__, STRINGIFY(function), \ sane_strstatus (status)); \ return status; \ } \ } \ while (SANE_FALSE) #else # define RIE(function) \ do { status = function; \ if (status != SANE_STATUS_GOOD) return status; \ } while (SANE_FALSE) #endif /* Flags */ #define GT68XX_FLAG_MIRROR_X (1 << 0) /* CIS unit mounted the other way round? */ #define GT68XX_FLAG_MOTOR_HOME (1 << 1) /* Use motor_home command (0x34) */ #define GT68XX_FLAG_OFFSET_INV (1 << 2) /* Offset control is inverted */ #define GT68XX_FLAG_UNTESTED (1 << 3) /* Print a warning for these scanners */ #define GT68XX_FLAG_SE_2400 (1 << 4) /* Special quirks for SE 2400USB */ #define GT68XX_FLAG_NO_STOP (1 << 5) /* Don't call stop_scan before the scan */ #define GT68XX_FLAG_CIS_LAMP (1 << 6) /* CIS sensor with lamp */ #define GT68XX_FLAG_NO_POWER_STATUS (1 << 7) /* get_power_status_doesn't work */ #define GT68XX_FLAG_NO_LINEMODE (1 << 8) /* Linemode does not work with this scanner */ #define GT68XX_FLAG_SCAN_FROM_HOME (1 << 9) /* Move home after calibration */ #define GT68XX_FLAG_USE_OPTICAL_X (1 << 10) /* Use optical xdpi for 50 dpi and below */ #define GT68XX_FLAG_ALWAYS_LINEMODE (1 << 11) /* Linemode must be used for any resolution */ #define GT68XX_FLAG_SHEET_FED (1 << 12) /* we have a sheet fed scanner */ #define GT68XX_FLAG_HAS_CALIBRATE (1 << 13) /* for sheet fed scanners that be calibrated with an calibration sheet */ #define GT68XX_FLAG_NO_CALIBRATE (1 << 14) /* don't calibrate, because calibration is broken */ /* Forward typedefs */ typedef struct GT68xx_USB_Device_Entry GT68xx_USB_Device_Entry; typedef struct GT68xx_Command_Set GT68xx_Command_Set; typedef struct GT68xx_Model GT68xx_Model; typedef struct GT68xx_Device GT68xx_Device; typedef struct GT68xx_Scan_Request GT68xx_Scan_Request; typedef struct GT68xx_Scan_Parameters GT68xx_Scan_Parameters; typedef struct GT68xx_AFE_Parameters GT68xx_AFE_Parameters; typedef struct GT68xx_Exposure_Parameters GT68xx_Exposure_Parameters; typedef enum GT68xx_Color_Order { COLOR_ORDER_RGB, COLOR_ORDER_BGR } GT68xx_Color_Order; #define GT68XX_COLOR_RED SANE_I18N ("Red") #define GT68XX_COLOR_GREEN SANE_I18N ("Green") #define GT68XX_COLOR_BLUE SANE_I18N ("Blue") /** Scan action code (purpose of the scan). * * The scan action code affects various scanning mode fields in the setup * command. */ typedef enum GT68xx_Scan_Action { SA_CALIBRATE, /**< Calibration scan, no offsets, no LD correction */ SA_CALIBRATE_ONE_LINE, /**< Same, but only one line for auto AFE */ SA_SCAN /**< Normal scan */ } GT68xx_Scan_Action; /** USB device list entry. */ struct GT68xx_USB_Device_Entry { SANE_Word vendor; /**< USB vendor identifier */ SANE_Word product; /**< USB product identifier */ GT68xx_Model *model; /**< Scanner model information */ }; #define MAX_SCANNERS 50 /* Looks like gcc doesn't like declarations without specificating the number of array elements at least when --enable-warnings is active */ /** List of all supported devices. * * This is an array of GT68xx_USB_Device_Entry structures which describe * USB devices supported by this backend. The array is terminated by an * entry with model = NULL. * */ static GT68xx_USB_Device_Entry gt68xx_usb_device_list[MAX_SCANNERS]; /** GT68xx analog front-end (AFE) parameters. */ struct GT68xx_AFE_Parameters { SANE_Byte r_offset; /**< Red channel offset */ SANE_Byte r_pga; /**< Red channel PGA gain */ SANE_Byte g_offset; /**< Green channel offset (also used for mono) */ SANE_Byte g_pga; /**< Green channel PGA gain (also used for mono) */ SANE_Byte b_offset; /**< Blue channel offset */ SANE_Byte b_pga; /**< Blue channel PGA gain */ }; /** GT68xx exposure time parameters. */ struct GT68xx_Exposure_Parameters { SANE_Int r_time; /**< Red exposure time */ SANE_Int g_time; /**< Red exposure time */ SANE_Int b_time; /**< Red exposure time */ }; /** * Scanner command set description. * * This description contains parts which are common to all scanners with the * same command set, but may have different optical resolution and other * parameters. */ struct GT68xx_Command_Set { /** @name Identification */ /*@{ */ /** Name of this command set */ SANE_String_Const name; /*@} */ /** @name USB request parameters * * These values are used in the USB control transfer parameters (wValue and * wIndex fields, as in the USB specification). */ /*@{ */ SANE_Byte request_type; /**< Request type (should be 0x40, vendor spec) */ SANE_Byte request; /**< Vendor spec request (0x01 or 0x04) */ SANE_Word memory_read_value; /**< Memory read - wValue */ SANE_Word memory_write_value; /**< Memory write - wValue */ SANE_Word send_cmd_value; /**< Send normal command - wValue */ SANE_Word send_cmd_index; /**< Send normal command - wIndex */ SANE_Word recv_res_value; /**< Receive normal result - wValue */ SANE_Word recv_res_index; /**< Receive normal result - wIndex */ SANE_Word send_small_cmd_value; /**< Send small command - wValue */ SANE_Word send_small_cmd_index; /**< Send small command - wIndex */ SANE_Word recv_small_res_value; /**< Receive small result - wValue */ SANE_Word recv_small_res_index; /**< Receive small result - wIndex */ /*@} */ /** @name Activation/deactivation hooks * * These hooks can be used to perform additional actions when the device is * activated and deactivated. */ /*@{ */ /** Activate the device. * * This function may allocate a command-set-specific data structure and place * the pointer to it into the GT68xx_Device::command_set_private field. */ SANE_Status (*activate) (GT68xx_Device * dev); /** Deactivate the device. * * If the activate function has allocated a command-set-specific data * structure, this function must free all corresponding resources and set the * GT68xx_Device::command_set_private pointer to #NULL. */ SANE_Status (*deactivate) (GT68xx_Device * dev); /*@} */ /** @name Low-level command implementation functions * * These functions should implement the corresponding commands for the * particular command set. If a function cannot be implemented because there * is no corresponding command in the command set, the function pointer * should be set to #NULL; the wrapper will return #SANE_STATUS_UNSUPPORTED * in this case. */ /*@{ */ /** Check whether the firmware is already downloaded. * * @param dev Device object. * @param loaded Returned firmware status: * - #SANE_TRUE - the firmware is already loaded. * - #SANE_FALSE - the firmware is not loaded. */ SANE_Status (*check_firmware) (GT68xx_Device * dev, SANE_Bool * loaded); /** Download the firmware */ SANE_Status (*download_firmware) (GT68xx_Device * dev, SANE_Byte * data, SANE_Word size); /** Check whether the external power supply is connected. * * @param dev Device object. * @param power_ok Returned power status: * - #SANE_TRUE - the external power supply is connected, or the scanner does * not need external power. * - #SANE_FALSE - the external power supply is not connected, so the scanner * will not work. */ SANE_Status (*get_power_status) (GT68xx_Device * dev, SANE_Bool * power_ok); /** Check whether a transparency adapter is attached to the scanner. * * @param dev Device object. * @param ta_attached Returned transparency adapter status: * - #SANE_TRUE - the transparency adapter is connected. * - #SANE_FALSE - the transparency adapter is not connected. * * @return * - #SANE_STATUS_GOOD - transparency adapter status was checked * successfully; check @a *ta_attached for the result. * - #SANE_STATUS_UNSUPPORTED - this scanner model does not support the * transparency adapter. * */ SANE_Status (*get_ta_status) (GT68xx_Device * dev, SANE_Bool * ta_attached); /** Turn the lamps in the scanner and/or the transparency adapter on or off. * * @param dev Device object. * @param fb_lamp #SANE_TRUE turns on the flatbed lamp. * @param ta_lamp #SANE_TRUE turns on the transparency adapter lamp. * * @return * - #SANE_STATUS_GOOD - the command completed successfully. * - #SANE_STATUS_UNSUPPORTED - unsupported request was made (like attempt to * turn on the TA lamp on a scanner which does not support TA). */ SANE_Status (*lamp_control) (GT68xx_Device * dev, SANE_Bool fb_lamp, SANE_Bool ta_lamp); /** Check whether the scanner carriage is still moving. * * @param dev Device object. * @param moving Returned state of the scanner: * - #SANE_TRUE - the scanner carriage is still moving. * - #SANE_FALSE - the scanner carriage has stopped. * * @return * - #SANE_STATUS_GOOD - the command completed successfully, the status in @a * *moving is valid. */ SANE_Status (*is_moving) (GT68xx_Device * dev, SANE_Bool * moving); /** Move the scanner carriage by the specified number of steps. * * @param dev Device object. * @param distance Number of steps to move (positive to move forward, * negative to move backward). The measurement unit is model-dependent; * number of steps per inch is found in the GT68xx_Model::base_ydpi field. * * @return * - #SANE_STATUS_GOOD - the command completed successfully; the movement is * started. Call gt68xx_device_is_moving() periodically to determine when * the movement is complete. */ SANE_Status (*move_relative) (GT68xx_Device * dev, SANE_Int distance); /** Move the scanner carriage to the home position. * * @param dev Device object. */ SANE_Status (*carriage_home) (GT68xx_Device * dev); /** Eject the paper at the end of the scan. * * @param dev Device object. */ SANE_Status (*paperfeed) (GT68xx_Device * dev); /** Start scanning the image. * * @param dev Device object. */ SANE_Status (*start_scan) (GT68xx_Device * dev); /** Start reading the scanned image data from the scanner. * * @param dev Device object. * */ SANE_Status (*read_scanned_data) (GT68xx_Device * dev, SANE_Bool * ready); /** Stop scanning the image and reading the data. */ SANE_Status (*stop_scan) (GT68xx_Device * dev); /** Set parameters for the next scan. */ SANE_Status (*setup_scan) (GT68xx_Device * dev, GT68xx_Scan_Request * request, GT68xx_Scan_Action action, GT68xx_Scan_Parameters * params); SANE_Status (*set_afe) (GT68xx_Device * dev, GT68xx_AFE_Parameters * params); SANE_Status (*set_exposure_time) (GT68xx_Device * dev, GT68xx_Exposure_Parameters * params); /** Get the vendor, product and some more ids from the scanner */ SANE_Status (*get_id) (GT68xx_Device * dev); /** Move the paper by the amount of y offset needed to reach scan area * * @param dev Device object. * @param request scan request used to compute move to reach scan area */ SANE_Status (*move_paper) (GT68xx_Device * dev, GT68xx_Scan_Request * request); /** Detect if a document is inserted in the feeder * * @param dev Device object. * @param present */ SANE_Status (*document_present) (GT68xx_Device * dev, SANE_Bool *present); /*@} */ }; #define MAX_RESOLUTIONS 12 #define MAX_DPI 4 /** Model-specific scanner data. */ struct GT68xx_Model { /** @name Identification */ /*@{ */ /** A single lowercase word to be used in the configuration file. */ SANE_String_Const name; /** Device vendor string. */ SANE_String_Const vendor; /** Device model name. */ SANE_String_Const model; /** Name of the firmware file. */ SANE_String_Const firmware_name; /** Dynamic allocation flag. * * This flag must be set to SANE_TRUE if the structure is dynamically * allocated; in this case the structure will be freed when the GT68xx_Device * is freed. */ SANE_Bool allocated; /*@} */ /** @name Scanner model parameters */ /*@{ */ GT68xx_Command_Set *command_set; SANE_Int optical_xdpi; /* maximum resolution in x-direction */ SANE_Int optical_ydpi; /* maximum resolution in y-direction */ SANE_Int base_xdpi; /* x-resolution used to calculate geometry */ SANE_Int base_ydpi; /* y-resolution used to calculate geometry */ SANE_Int ydpi_no_backtrack; /* if ydpi is equal or higher, disable backtracking */ SANE_Bool constant_ydpi; /* Use base_ydpi for all resolutions */ SANE_Int xdpi_values[MAX_RESOLUTIONS]; /* possible x resolutions */ SANE_Int ydpi_values[MAX_RESOLUTIONS]; /* possible y resolutions */ SANE_Int bpp_gray_values[MAX_DPI]; /* possible depths in gray mode */ SANE_Int bpp_color_values[MAX_DPI]; /* possible depths in color mode */ SANE_Fixed x_offset; /* Start of scan area in mm */ SANE_Fixed y_offset; /* Start of scan area in mm */ SANE_Fixed x_size; /* Size of scan area in mm */ SANE_Fixed y_size; /* Size of scan area in mm */ SANE_Fixed y_offset_calib; /* Start of white strip in mm */ SANE_Fixed x_offset_mark; /* Start of black mark in mm */ SANE_Fixed x_offset_ta; /* Start of scan area in TA mode in mm */ SANE_Fixed y_offset_ta; /* Start of scan area in TA mode in mm */ SANE_Fixed x_size_ta; /* Size of scan area in TA mode in mm */ SANE_Fixed y_size_ta; /* Size of scan area in TA mode in mm */ SANE_Fixed y_offset_calib_ta; /* Start of white strip in TA mode in mm */ /* Line-distance correction (in pixel at optical_ydpi) for CCD scanners */ SANE_Int ld_shift_r; /* red */ SANE_Int ld_shift_g; /* green */ SANE_Int ld_shift_b; /* blue */ SANE_Int ld_shift_double; /* distance between two CCD lines of one color (only applicable for CCD with 6 lines) */ GT68xx_Color_Order line_mode_color_order; /* Order of the CCD/CIS colors */ GT68xx_AFE_Parameters afe_params; /* Default offset/gain */ GT68xx_Exposure_Parameters exposure; /* Default exposure parameters */ SANE_Fixed default_gamma_value; /* Default gamma value */ SANE_Bool is_cis; /* Is this a CIS or CCD scanner? */ SANE_Word flags; /* Which hacks are needed for this scanner? */ /*@} */ }; /** GT68xx device instance. * */ struct GT68xx_Device { /** Device file descriptor. */ int fd; /** Device activation flag. */ SANE_Bool active; /** Device missing to flag devices that are unplugged * after sane_init and before sane_exit */ SANE_Bool missing; /** Scanner model data. */ GT68xx_Model *model; /** Pointer to command-set-specific data. */ void *command_set_private; GT68xx_AFE_Parameters *afe; GT68xx_Exposure_Parameters *exposure; SANE_Fixed gamma_value; SANE_Bool read_active; SANE_Bool final_scan; SANE_Byte *read_buffer; size_t requested_buffer_size; size_t read_buffer_size; size_t read_pos; size_t read_bytes_in_buffer; size_t read_bytes_left; SANE_Byte gray_mode_color; SANE_Bool manual_selection; SANE_Bool scan_started; #ifdef USE_FORK Shm_Channel *shm_channel; pid_t reader_pid; #endif /* USE_FORK */ /** Pointer to next device */ struct GT68xx_Device *next; /** Device file name */ SANE_String file_name; }; /** Parameters for the high-level scan request. * * These parameters describe the scan request sent by the SANE frontend. */ struct GT68xx_Scan_Request { SANE_Fixed x0; /**< Left boundary */ SANE_Fixed y0; /**< Top boundary */ SANE_Fixed xs; /**< Width */ SANE_Fixed ys; /**< Height */ SANE_Int xdpi; /**< Horizontal resolution */ SANE_Int ydpi; /**< Vertical resolution */ SANE_Int depth; /**< Number of bits per channel */ SANE_Bool color; /**< Color mode flag */ SANE_Bool mbs; /**< Move before scan */ SANE_Bool mds; /**< Move during scan */ SANE_Bool mas; /**< Move after scan */ SANE_Bool lamp; /**< Lamp on/off */ SANE_Bool calculate; /**< Don't scan, only calculate parameters */ SANE_Bool use_ta; /**< Use the tansparency adapter */ SANE_Bool backtrack; /**< Enable backtracking */ SANE_Bool backtrack_lines; /**< How many lines to backtrack */ }; /** Scan parameters for gt68xx_device_setup_scan(). * * These parameters describe a low-level scan request; many such requests are * executed during calibration, and they need to have parameters separate from * the main request (GT68xx_Scan_Request). */ struct GT68xx_Scan_Parameters { SANE_Int xdpi; /**< Horizontal resolution */ SANE_Int ydpi; /**< Vertical resolution */ SANE_Int depth; /**< Number of bits per channel */ SANE_Bool color; /**< Color mode flag */ SANE_Int pixel_xs; /**< Logical width in pixels */ SANE_Int pixel_ys; /**< Logical height in pixels */ SANE_Int scan_xs; /**< Physical width in pixels */ SANE_Int scan_ys; /**< Physical height in pixels */ SANE_Int scan_bpl; /**< Number of bytes per scan line */ SANE_Bool line_mode; /**< Use line mode instead of pixel mode */ SANE_Int overscan_lines; /**< Number of extra scan lines */ SANE_Int ld_shift_r; SANE_Int ld_shift_g; SANE_Int ld_shift_b; SANE_Int ld_shift_double; SANE_Int double_column; SANE_Int pixel_x0; /**< x start position */ }; #define GT68XX_PACKET_SIZE 64 typedef SANE_Byte GT68xx_Packet[GT68XX_PACKET_SIZE]; /** Create a new GT68xx_Device object. * * The newly created device object is in the closed state. * * @param dev_return Returned pointer to the created device object. * * @return * - #SANE_STATUS_GOOD - the device object was created. * - #SANE_STATUS_NO_MEM - not enough system resources to create the object. */ static SANE_Status gt68xx_device_new (GT68xx_Device ** dev_return); /** Destroy the device object and release all associated resources. * * If the device was active, it will be deactivated; if the device was open, it * will be closed. * * @param dev Device object. * * @return * - #SANE_STATUS_GOOD - success. */ static SANE_Status gt68xx_device_free (GT68xx_Device * dev); /** Open the scanner device. * * This function opens the device special file @a dev_name and tries to detect * the device model by its USB ID. * * If the device is detected successfully (its USB ID is found in the supported * device list), this function sets the appropriate model parameters. * * If the USB ID is not recognized, the device remains unconfigured; an attempt * to activate it will fail unless gt68xx_device_set_model() is used to force * the parameter set. Note that the open is considered to be successful in * this case. * * @param dev Device object. * @param dev_name Scanner device name. * * @return * - #SANE_STATUS_GOOD - the device was opened successfully (it still may be * unconfigured). */ static SANE_Status gt68xx_device_open (GT68xx_Device * dev, const char *dev_name); /** Close the scanner device. * * @param dev Device object. */ static SANE_Status gt68xx_device_close (GT68xx_Device * dev); /** Check if the device is configured. * * A device is considered configured when it has a model parameters structure * (GT68xx_Model) and a command set (GT68xx_Command_Set). Normally these * parameters are assigned automatically by gt68xx_device_open(), if the device * is known. If the USB ID of the device is not found in the list, or the OS * does not support identification of USB devices, the device will be * unconfigured after opening. * * @param dev Device object. * * @return * - #SANE_TRUE - device is configured and can be activated. * - #SANE_FALSE - device is not configured; attempt to activate it will fail. */ static SANE_Bool gt68xx_device_is_configured (GT68xx_Device * dev); /** Change the device model structure. * * This function can be used to change all model-dependent parameters at once * by supplying a whole model data structure. The model may be changed only * when the device is not active. * * If the device already had a model structure which was dynamically allocated, * the old structure is freed. * * If the new model structure @a model is dynamically allocated, the device @a * dev takes ownership of it: @a model will be freed when @a dev is destroyed * or gt68xx_device_set_model() is called again. * * @param dev Device object. * @param model Device model data. * * @return * - #SANE_STATUS_GOOD - model successfully changed. * - #SANE_STATUS_INVAL - invalid request (attempt to change model when the * device is already active, or not yet opened). */ static SANE_Status gt68xx_device_set_model (GT68xx_Device * dev, GT68xx_Model * model); /** Get model by name. * * This function can be used to find a model by its name. * * @param name Device model name. * @param model Device model data. * * @return * - #SANE_TRUE - model successfully found. * - #SANE_FALSE - model not found. */ static SANE_Bool gt68xx_device_get_model (SANE_String name, GT68xx_Model ** model); #if 0 /** Create a new private copy of the model data for this device. * * Normally the model data structures can be shared between several devices. * If the program needs to modify some model parameters for a device, it must * call this function to make a private copy of parameters. This private copy * will be automatically freed when the device is destroyed. * * @param dev Device object. * * @return * - #SANE_STATUS_GOOD - a private copy was made successfully. * - #SANE_STATUS_INVAL - invalid request (the device was already active, or * not yet opened). * - #SANE_STATUS_NO_MEM - not enough memory for copy of the model parameters. */ static SANE_Status gt68xx_device_unshare_model (GT68xx_Device * dev); #endif /** Activate the device. * * The device must be activated before performing any I/O operations with it. * All device model parameters must be configured before activation; it is * impossible to change them after the device is active. * * This function might need to acquire resources (it calls * GT68xx_Command_Set::activate). These resources will be released when * gt68xx_device_deactivate() is called. * * @param dev Device object. * * @return * - #SANE_STATUS_GOOD - device activated successfully. * - #SANE_STATUS_INVAL - invalid request (attempt to activate a closed or * unconfigured device). */ static SANE_Status gt68xx_device_activate (GT68xx_Device * dev); /** Deactivate the device. * * This function reverses the action of gt68xx_device_activate(). * * @param dev Device object. * * @return * - #SANE_STATUS_GOOD - device deactivated successfully. * - #SANE_STATUS_INVAL - invalid request (the device was not activated). */ static SANE_Status gt68xx_device_deactivate (GT68xx_Device * dev); /** Write a data block to the GT68xx memory. * * @param dev Device object. * @param addr Start address in the GT68xx memory. * @param size Size of the data block in bytes. * @param data Data block to write. * * @return * - #SANE_STATUS_GOOD - success. * - #SANE_STATUS_IO_ERROR - a communication error occurred. * * @warning * @a size must be a multiple of 64 (at least with GT6816), otherwise the * scanner (and possibly the entire USB bus) will lock up. */ static SANE_Status gt68xx_device_memory_write (GT68xx_Device * dev, SANE_Word addr, SANE_Word size, SANE_Byte * data); /** Read a data block from the GT68xx memory. * * @param dev Device object. * @param addr Start address in the GT68xx memory. * @param size Size of the data block in bytes. * @param data Buffer for the read data. * * @return * - #SANE_STATUS_GOOD - success. * - #SANE_STATUS_IO_ERROR - a communication error occurred. * * @warning * @a size must be a multiple of 64 (at least with GT6816), otherwise the * scanner (and possibly the entire USB bus) will lock up. */ static SANE_Status gt68xx_device_memory_read (GT68xx_Device * dev, SANE_Word addr, SANE_Word size, SANE_Byte * data); /** Execute a control command. * * @param dev Device object. * @param cmd Command packet. * @param res Result packet (may point to the same buffer as @a cmd). * * @return * - #SANE_STATUS_GOOD - success. * - #SANE_STATUS_IO_ERROR - a communication error occurred. */ static SANE_Status gt68xx_device_req (GT68xx_Device * dev, GT68xx_Packet cmd, GT68xx_Packet res); /** Execute a "small" control command. * * @param dev Device object. * @param cmd Command packet; only first 8 bytes are used. * @param res Result packet (may point to the same buffer as @a cmd). * * @return * - #SANE_STATUS_GOOD - success. * - #SANE_STATUS_IO_ERROR - a communication error occurred. */ static SANE_Status gt68xx_device_small_req (GT68xx_Device * dev, GT68xx_Packet cmd, GT68xx_Packet res); #if 0 /** Check whether the firmware is downloaded into the scanner. * * @param dev Device object. * @param loaded Returned firmware status: * - #SANE_TRUE - the firmware is already loaded. * - #SANE_FALSE - the firmware is not loaded. * * @return * - #SANE_STATUS_GOOD - success. * - #SANE_STATUS_IO_ERROR - a communication error occurred. */ static SANE_Status gt68xx_device_check_firmware (GT68xx_Device * dev, SANE_Bool * loaded); #endif static SANE_Status gt68xx_device_download_firmware (GT68xx_Device * dev, SANE_Byte * data, SANE_Word size); /** Check whether the external power supply is connected. * * @param dev Device object. * @param power_ok Returned power status: * - #SANE_TRUE - the external power supply is connected, or the scanner does * not need external power. * - #SANE_FALSE - the external power supply is not connected, so the scanner * will not work. * * @return * - #SANE_STATUS_GOOD - success. * - #SANE_STATUS_IO_ERROR - a communication error occurred. */ static SANE_Status gt68xx_device_get_power_status (GT68xx_Device * dev, SANE_Bool * power_ok); /** Check whether the transparency adapter is connected. * * @param dev Device object. * @param ta_attached Returned TA status: * - #SANE_TRUE - the TA is connected. * - #SANE_FALSE - the TA is not connected. * * @return * - #SANE_STATUS_GOOD - success. * - #SANE_STATUS_UNSUPPORTED - the scanner does not support TA connection. * - #SANE_STATUS_IO_ERROR - a communication error occurred. */ static SANE_Status gt68xx_device_get_ta_status (GT68xx_Device * dev, SANE_Bool * ta_attached); /** Turn the lamps in the scanner and/or the transparency adapter on or off. * * @param dev Device object. * @param fb_lamp #SANE_TRUE turns on the flatbed lamp. * @param ta_lamp #SANE_TRUE turns on the transparency adapter lamp. * * @return * - #SANE_STATUS_GOOD - success. * - #SANE_STATUS_IO_ERROR - a communication error occurred. * - #SANE_STATUS_UNSUPPORTED - unsupported request was made (like attempt to * turn on the TA lamp on a scanner which does not support TA). */ static SANE_Status gt68xx_device_lamp_control (GT68xx_Device * dev, SANE_Bool fb_lamp, SANE_Bool ta_lamp); /** Check whether the scanner carriage is still moving. * * @param dev Device object. * @param moving Returned state of the scanner: * - #SANE_TRUE - the scanner carriage is still moving. * - #SANE_FALSE - the scanner carriage has stopped. * * @return * - #SANE_STATUS_GOOD - success; the status in @a *moving is valid. * - #SANE_STATUS_IO_ERROR - a communication error occurred. */ static SANE_Status gt68xx_device_is_moving (GT68xx_Device * dev, SANE_Bool * moving); #if 0 /** Move the scanner carriage by the specified number of steps. * * @param dev Device object. * @param distance Number of steps to move (positive to move forward, negative * to move backward). The measurement unit is model-dependent; number of steps * per inch is found in the GT68xx_Model::base_ydpi field. * * @return * - #SANE_STATUS_GOOD - success; the movement is started. Call * gt68xx_device_is_moving() periodically to determine when the movement is * complete. * - #SANE_STATUS_IO_ERROR - a communication error occurred. */ static SANE_Status gt68xx_device_move_relative (GT68xx_Device * dev, SANE_Int distance); #endif /** Move the scanner carriage to the home position. * * This function starts moving the scanner carriage to the home position, but * deos not wait for finishing the movement. To determine when the carriage * returned to the home position, the program should periodically call * gt68xx_device_is_moving(). * * @param dev Device object. * * @return * - #SANE_STATUS_GOOD - success; the movement is started. * - #SANE_STATUS_IO_ERROR - a communication error occurred. */ static SANE_Status gt68xx_device_carriage_home (GT68xx_Device * dev); /** Eject the paper after the end of scanning. * * * @param dev Device object. * * @return * - #SANE_STATUS_GOOD - success; the movement is started. * - #SANE_STATUS_IO_ERROR - a communication error occurred. */ static SANE_Status gt68xx_device_paperfeed (GT68xx_Device * dev); /** Start scanning the image. * * This function initiates scanning with parameters set by * gt68xx_device_setup_scan() (which should be called before). In particular, * it starts the carriage movement to the start of the scanning window. * * After calling this function, gt68xx_device_read_scanned_data() should be * called repeatedly until the scanner signals that it is ready to deliver the * data. * * @param dev Device object. * * @return * - #SANE_STATUS_GOOD - success. * - #SANE_STATUS_IO_ERROR - a communication error occurred. */ static SANE_Status gt68xx_device_start_scan (GT68xx_Device * dev); /** Start reading the scanned image data. * * This function should be used only after gt68xx_device_start_scan(). It * should be called repeatedly until @a *ready flag becomes true, at which * point reading the image data should be started using * gt68xx_device_read_prepare(). * * @param dev Device object. * @param ready Returned status of the scanner: * - #SANE_TRUE - the scanner is ready to send data. * - #SANE_FALSE - the scanner is not ready (e.g., the carriage has not reached * the start of the scanning window). * * @return * - #SANE_STATUS_GOOD - success; the value in @a *ready is valid. * - #SANE_STATUS_IO_ERROR - a communication error occurred. */ static SANE_Status gt68xx_device_read_scanned_data (GT68xx_Device * dev, SANE_Bool * ready); /** Stop scanning the image. * * This function should be used after reading all image data from the scanner, * or in the middle of scan to abort the scanning process. * * @param dev Device object. * * @return * - #SANE_STATUS_GOOD - success. * - #SANE_STATUS_IO_ERROR - a communication error occurred. */ static SANE_Status gt68xx_device_stop_scan (GT68xx_Device * dev); /** Set parameters for the next scan. * * This function calculates the hardware-dependent scanning parameters and, * unless @a calculate_only is set, sends the command to prepare for scanning * with the specified window and parameters. * * @param dev Device object. * @param request High-level scanning request. * @param action Action code describing the phase of calibration or scanning * process. * @param params Returned structure with hardware-dependent scanning * parameters. * * @return * - #SANE_STATUS_GOOD - success. * - #SANE_STATUS_UNSUPPORTED - the requested scanning parameters in @a request * are not supported by hardware. * - #SANE_STATUS_INVAL - some of the parameters in @a request, or the @a * action code, are completely invalid. * - #SANE_STATUS_IO_ERROR - a communication error occurred. */ static SANE_Status gt68xx_device_setup_scan (GT68xx_Device * dev, GT68xx_Scan_Request * request, GT68xx_Scan_Action action, GT68xx_Scan_Parameters * params); /** Configure the analog front-end (AFE) of the GT68xx. * * @param dev Device object. * @param params AFE parameters. * * @return * - #SANE_STATUS_GOOD - success. * - #SANE_STATUS_IO_ERROR - a communication error occurred. */ static SANE_Status gt68xx_device_set_afe (GT68xx_Device * dev, GT68xx_AFE_Parameters * params); static SANE_Status gt68xx_device_set_exposure_time (GT68xx_Device * dev, GT68xx_Exposure_Parameters * params); /** Read raw data from the bulk-in scanner pipe. * * @param dev Device object. * @param buffer Buffer for the read data. * @param size Pointer to the variable which must be set to the requested data * size before call. After completion this variable will hold the number of * bytes actually read. * * @return * - #SANE_STATUS_GOOD - success. * - #SANE_STATUS_IO_ERROR - a communication error occurred. */ static SANE_Status gt68xx_device_read_raw (GT68xx_Device * dev, SANE_Byte * buffer, size_t * size); static SANE_Status gt68xx_device_set_read_buffer_size (GT68xx_Device * dev, size_t buffer_size); static SANE_Status gt68xx_device_read_prepare (GT68xx_Device * dev, size_t expected_count, SANE_Bool final_scan); static SANE_Status gt68xx_device_read (GT68xx_Device * dev, SANE_Byte * buffer, size_t * size); static SANE_Status gt68xx_device_read_finish (GT68xx_Device * dev); /** Make sure that the result of a command is ok. * * @param res Result packet from the last command * @param command Command * * @return * - #SANE_STATUS_GOOD - success. * - #SANE_STATUS_IO_ERROR - the command wasn't successful */ static SANE_Status gt68xx_device_check_result (GT68xx_Packet res, SANE_Byte command); static SANE_Status gt68xx_device_get_id (GT68xx_Device * dev); /** Read the device descriptor of the scanner. * * This function should be called before closing the device to make sure * that the device descriptor is properly stored in the scanner's memory. * If that's not done, the next try to get the config descriptor will * result in a corrupted descriptor. * * @param dev device */ static void gt68xx_device_fix_descriptor (GT68xx_Device * dev); #endif /* not GT68XX_LOW_H */ /* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */ backends-1.3.0/backend/gt68xx_mid.c000066400000000000000000001070111456256263500170370ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2002 Sergey Vlasov Copyright (C) 2002-2007 Henning Geinitz 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, see . 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 "gt68xx_mid.h" #include "gt68xx_low.c" /** @file * @brief Image data unpacking. */ static SANE_Status gt68xx_delay_buffer_init (GT68xx_Delay_Buffer * delay, SANE_Int pixels_per_line, SANE_Int delay_count) { SANE_Int bytes_per_line; SANE_Int line_count, i; if (pixels_per_line <= 0) { DBG (3, "gt68xx_delay_buffer_init: BUG: pixels_per_line=%d\n", pixels_per_line); return SANE_STATUS_INVAL; } if (delay_count < 0) { DBG (3, "gt68xx_delay_buffer_init: BUG: delay_count=%d\n", delay_count); return SANE_STATUS_INVAL; } bytes_per_line = pixels_per_line * sizeof (unsigned int); delay->line_count = line_count = delay_count + 1; delay->read_index = 0; delay->write_index = delay_count; delay->mem_block = (SANE_Byte *) malloc (bytes_per_line * line_count); if (!delay->mem_block) { DBG (3, "gt68xx_delay_buffer_init: no memory for delay block\n"); return SANE_STATUS_NO_MEM; } /* make sure that we will see if one of the uninitialized lines get displayed */ for (i = 0; i < bytes_per_line * line_count; i++) delay->mem_block[i] = i % 256; delay->lines = (unsigned int **) malloc (sizeof (unsigned int *) * line_count); if (!delay->lines) { free (delay->mem_block); DBG (3, "gt68xx_delay_buffer_init: no memory for delay line pointers\n"); return SANE_STATUS_NO_MEM; } for (i = 0; i < line_count; ++i) delay->lines[i] = (unsigned int *) (delay->mem_block + i * bytes_per_line); return SANE_STATUS_GOOD; } static SANE_Status gt68xx_delay_buffer_done (GT68xx_Delay_Buffer * delay) { if (delay->lines) { free (delay->lines); delay->lines = NULL; } if (delay->mem_block) { free (delay->mem_block); delay->mem_block = NULL; } return SANE_STATUS_GOOD; } #define DELAY_BUFFER_WRITE_PTR(delay) ( (delay)->lines[(delay)->write_index] ) #define DELAY_BUFFER_SELECT_PTR(delay,dist) \ ((delay)->lines[((delay)->read_index + (dist)) % (delay)->line_count]) #define DELAY_BUFFER_READ_PTR(delay) ( (delay)->lines[(delay)->read_index ] ) #define DELAY_BUFFER_STEP(delay) \ do \ { \ (delay)->read_index = ((delay)->read_index + 1) % (delay)->line_count; \ (delay)->write_index = ((delay)->write_index + 1) % (delay)->line_count; \ } \ while (SANE_FALSE) static inline void unpack_8_mono (SANE_Byte * src, unsigned int *dst, SANE_Int pixels_per_line) { for (; pixels_per_line > 0; ++src, ++dst, --pixels_per_line) { *dst = (((unsigned int) *src) << 8) | *src; } } static inline void unpack_8_rgb (SANE_Byte * src, unsigned int *dst, SANE_Int pixels_per_line) { for (; pixels_per_line > 0; src += 3, ++dst, --pixels_per_line) { *dst = (((unsigned int) *src) << 8) | *src; } } /* 12-bit routines use the fact that pixels_per_line is aligned */ static inline void unpack_12_le_mono (SANE_Byte * src, unsigned int *dst, SANE_Int pixels_per_line) { for (; pixels_per_line > 0; src += 3, dst += 2, pixels_per_line -= 2) { dst[0] = ((((unsigned int) (src[1] & 0x0f)) << 12) | (((unsigned int) src[0]) << 4) | (src[1] & 0x0f)); dst[1] = ((((unsigned int) src[2]) << 8) | (src[1] & 0xf0) | (((unsigned int) src[2]) >> 0x04)); } } static inline void unpack_12_le_rgb (SANE_Byte * src, unsigned int *dst1, unsigned int *dst2, unsigned int *dst3, SANE_Int pixels_per_line) { for (; pixels_per_line > 0; pixels_per_line -= 2) { *dst1++ = ((((unsigned int) (src[1] & 0x0f)) << 12) | (((unsigned int) src[0]) << 4) | (src[1] & 0x0f)); *dst2++ = ((((unsigned int) src[2]) << 8) | (src[1] & 0xf0) | (((unsigned int) src[2]) >> 0x04)); src += 3; *dst3++ = ((((unsigned int) (src[1] & 0x0f)) << 12) | (((unsigned int) src[0]) << 4) | (src[1] & 0x0f)); *dst1++ = ((((unsigned int) src[2]) << 8) | (src[1] & 0xf0) | (((unsigned int) src[2]) >> 0x04)); src += 3; *dst2++ = ((((unsigned int) (src[1] & 0x0f)) << 12) | (((unsigned int) src[0]) << 4) | (src[1] & 0x0f)); *dst3++ = ((((unsigned int) src[2]) << 8) | (src[1] & 0xf0) | (((unsigned int) src[2]) >> 0x04)); src += 3; } } static inline void unpack_16_le_mono (SANE_Byte * src, unsigned int *dst, SANE_Int pixels_per_line) { for (; pixels_per_line > 0; src += 2, dst++, --pixels_per_line) { *dst = (((unsigned int) src[1]) << 8) | src[0]; } } static inline void unpack_16_le_rgb (SANE_Byte * src, unsigned int *dst, SANE_Int pixels_per_line) { for (; pixels_per_line > 0; src += 6, ++dst, --pixels_per_line) { *dst = (((unsigned int) src[1]) << 8) | src[0]; } } static SANE_Status line_read_gray_8 (GT68xx_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; unsigned int *buffer; size = reader->params.scan_bpl; RIE (gt68xx_device_read (reader->dev, reader->pixel_buffer, &size)); buffer = DELAY_BUFFER_READ_PTR (&reader->g_delay); buffer_pointers_return[0] = buffer; unpack_8_mono (reader->pixel_buffer, buffer, reader->pixels_per_line); return SANE_STATUS_GOOD; } static SANE_Status line_read_gray_double_8 (GT68xx_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; unsigned int *buffer; int i; size = reader->params.scan_bpl; RIE (gt68xx_device_read (reader->dev, reader->pixel_buffer, &size)); unpack_8_mono (reader->pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->g_delay), reader->pixels_per_line); buffer = DELAY_BUFFER_READ_PTR (&reader->g_delay); for (i = reader->params.double_column; i < reader->pixels_per_line; i += 2) buffer[i] = DELAY_BUFFER_WRITE_PTR (&reader->g_delay)[i]; buffer_pointers_return[0] = buffer; DELAY_BUFFER_STEP (&reader->g_delay); return SANE_STATUS_GOOD; } static SANE_Status line_read_gray_12 (GT68xx_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; unsigned int *buffer; size = reader->params.scan_bpl; RIE (gt68xx_device_read (reader->dev, reader->pixel_buffer, &size)); buffer = DELAY_BUFFER_READ_PTR (&reader->g_delay); buffer_pointers_return[0] = buffer; unpack_12_le_mono (reader->pixel_buffer, buffer, reader->pixels_per_line); return SANE_STATUS_GOOD; } static SANE_Status line_read_gray_double_12 (GT68xx_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; unsigned int *buffer; int i; size = reader->params.scan_bpl; RIE (gt68xx_device_read (reader->dev, reader->pixel_buffer, &size)); unpack_12_le_mono (reader->pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->g_delay), reader->pixels_per_line); buffer = DELAY_BUFFER_READ_PTR (&reader->g_delay); for (i = reader->params.double_column; i < reader->pixels_per_line; i += 2) buffer[i] = DELAY_BUFFER_WRITE_PTR (&reader->g_delay)[i]; buffer_pointers_return[0] = buffer; DELAY_BUFFER_STEP (&reader->g_delay); return SANE_STATUS_GOOD; } static SANE_Status line_read_gray_16 (GT68xx_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; unsigned int *buffer; size = reader->params.scan_bpl; RIE (gt68xx_device_read (reader->dev, reader->pixel_buffer, &size)); buffer = DELAY_BUFFER_READ_PTR (&reader->g_delay); buffer_pointers_return[0] = buffer; unpack_16_le_mono (reader->pixel_buffer, buffer, reader->pixels_per_line); return SANE_STATUS_GOOD; } static SANE_Status line_read_gray_double_16 (GT68xx_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; unsigned int *buffer; int i; size = reader->params.scan_bpl; RIE (gt68xx_device_read (reader->dev, reader->pixel_buffer, &size)); unpack_16_le_mono (reader->pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->g_delay), reader->pixels_per_line); buffer = DELAY_BUFFER_READ_PTR (&reader->g_delay); for (i = reader->params.double_column; i < reader->pixels_per_line; i += 2) buffer[i] = DELAY_BUFFER_WRITE_PTR (&reader->g_delay)[i]; buffer_pointers_return[0] = buffer; DELAY_BUFFER_STEP (&reader->g_delay); return SANE_STATUS_GOOD; } static SANE_Status line_read_rgb_8_line_mode (GT68xx_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; SANE_Int pixels_per_line; SANE_Byte *pixel_buffer = reader->pixel_buffer; size = reader->params.scan_bpl * 3; RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size)); pixels_per_line = reader->pixels_per_line; unpack_8_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->r_delay), pixels_per_line); pixel_buffer += reader->params.scan_bpl; unpack_8_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->g_delay), pixels_per_line); pixel_buffer += reader->params.scan_bpl; unpack_8_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->b_delay), pixels_per_line); buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay); buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay); buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay); DELAY_BUFFER_STEP (&reader->r_delay); DELAY_BUFFER_STEP (&reader->g_delay); DELAY_BUFFER_STEP (&reader->b_delay); return SANE_STATUS_GOOD; } static SANE_Status line_read_rgb_double_8_line_mode (GT68xx_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; SANE_Int pixels_per_line; SANE_Byte *pixel_buffer = reader->pixel_buffer; int i; size = reader->params.scan_bpl * 3; RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size)); pixels_per_line = reader->pixels_per_line; unpack_8_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->r_delay), pixels_per_line); pixel_buffer += reader->params.scan_bpl; unpack_8_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->g_delay), pixels_per_line); pixel_buffer += reader->params.scan_bpl; unpack_8_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->b_delay), pixels_per_line); buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay); buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay); buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay); for (i = reader->params.double_column; i < reader->pixels_per_line; i += 2) { DELAY_BUFFER_READ_PTR (&reader->r_delay)[i] = DELAY_BUFFER_SELECT_PTR (&reader->r_delay, reader->params.ld_shift_double)[i]; DELAY_BUFFER_READ_PTR (&reader->g_delay)[i] = DELAY_BUFFER_SELECT_PTR (&reader->g_delay, reader->params.ld_shift_double)[i]; DELAY_BUFFER_READ_PTR (&reader->b_delay)[i] = DELAY_BUFFER_SELECT_PTR (&reader->b_delay, reader->params.ld_shift_double)[i]; } DELAY_BUFFER_STEP (&reader->r_delay); DELAY_BUFFER_STEP (&reader->g_delay); DELAY_BUFFER_STEP (&reader->b_delay); return SANE_STATUS_GOOD; } static SANE_Status line_read_bgr_8_line_mode (GT68xx_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; SANE_Int pixels_per_line; SANE_Byte *pixel_buffer = reader->pixel_buffer; size = reader->params.scan_bpl * 3; RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size)); pixels_per_line = reader->pixels_per_line; unpack_8_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->b_delay), pixels_per_line); pixel_buffer += reader->params.scan_bpl; unpack_8_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->g_delay), pixels_per_line); pixel_buffer += reader->params.scan_bpl; unpack_8_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->r_delay), pixels_per_line); buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay); buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay); buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay); DELAY_BUFFER_STEP (&reader->r_delay); DELAY_BUFFER_STEP (&reader->g_delay); DELAY_BUFFER_STEP (&reader->b_delay); return SANE_STATUS_GOOD; } static SANE_Status line_read_rgb_12_line_mode (GT68xx_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; SANE_Int pixels_per_line; SANE_Byte *pixel_buffer = reader->pixel_buffer; size = reader->params.scan_bpl * 3; RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size)); pixels_per_line = reader->pixels_per_line; unpack_12_le_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->r_delay), pixels_per_line); pixel_buffer += reader->params.scan_bpl; unpack_12_le_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->g_delay), pixels_per_line); pixel_buffer += reader->params.scan_bpl; unpack_12_le_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->b_delay), pixels_per_line); buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay); buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay); buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay); DELAY_BUFFER_STEP (&reader->r_delay); DELAY_BUFFER_STEP (&reader->g_delay); DELAY_BUFFER_STEP (&reader->b_delay); return SANE_STATUS_GOOD; } static SANE_Status line_read_rgb_double_12_line_mode (GT68xx_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; SANE_Int pixels_per_line; SANE_Byte *pixel_buffer = reader->pixel_buffer; int i; size = reader->params.scan_bpl * 3; RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size)); pixels_per_line = reader->pixels_per_line; unpack_12_le_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->r_delay), pixels_per_line); pixel_buffer += reader->params.scan_bpl; unpack_12_le_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->g_delay), pixels_per_line); pixel_buffer += reader->params.scan_bpl; unpack_12_le_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->b_delay), pixels_per_line); buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay); buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay); buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay); for (i = reader->params.double_column; i < reader->pixels_per_line; i += 2) { DELAY_BUFFER_READ_PTR (&reader->r_delay)[i] = DELAY_BUFFER_SELECT_PTR (&reader->r_delay, reader->params.ld_shift_double)[i]; DELAY_BUFFER_READ_PTR (&reader->g_delay)[i] = DELAY_BUFFER_SELECT_PTR (&reader->g_delay, reader->params.ld_shift_double)[i]; DELAY_BUFFER_READ_PTR (&reader->b_delay)[i] = DELAY_BUFFER_SELECT_PTR (&reader->b_delay, reader->params.ld_shift_double)[i]; } DELAY_BUFFER_STEP (&reader->r_delay); DELAY_BUFFER_STEP (&reader->g_delay); DELAY_BUFFER_STEP (&reader->b_delay); return SANE_STATUS_GOOD; } static SANE_Status line_read_rgb_16_line_mode (GT68xx_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; SANE_Int pixels_per_line; SANE_Byte *pixel_buffer = reader->pixel_buffer; size = reader->params.scan_bpl * 3; RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size)); pixels_per_line = reader->pixels_per_line; unpack_16_le_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->r_delay), pixels_per_line); pixel_buffer += reader->params.scan_bpl; unpack_16_le_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->g_delay), pixels_per_line); pixel_buffer += reader->params.scan_bpl; unpack_16_le_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->b_delay), pixels_per_line); buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay); buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay); buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay); DELAY_BUFFER_STEP (&reader->r_delay); DELAY_BUFFER_STEP (&reader->g_delay); DELAY_BUFFER_STEP (&reader->b_delay); return SANE_STATUS_GOOD; } static SANE_Status line_read_rgb_double_16_line_mode (GT68xx_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; SANE_Int pixels_per_line; SANE_Byte *pixel_buffer = reader->pixel_buffer; int i; size = reader->params.scan_bpl * 3; RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size)); pixels_per_line = reader->pixels_per_line; unpack_16_le_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->r_delay), pixels_per_line); pixel_buffer += reader->params.scan_bpl; unpack_16_le_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->g_delay), pixels_per_line); pixel_buffer += reader->params.scan_bpl; unpack_16_le_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->b_delay), pixels_per_line); buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay); buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay); buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay); for (i = reader->params.double_column; i < reader->pixels_per_line; i += 2) { DELAY_BUFFER_READ_PTR (&reader->r_delay)[i] = DELAY_BUFFER_SELECT_PTR (&reader->r_delay, reader->params.ld_shift_double)[i]; DELAY_BUFFER_READ_PTR (&reader->g_delay)[i] = DELAY_BUFFER_SELECT_PTR (&reader->g_delay, reader->params.ld_shift_double)[i]; DELAY_BUFFER_READ_PTR (&reader->b_delay)[i] = DELAY_BUFFER_SELECT_PTR (&reader->b_delay, reader->params.ld_shift_double)[i]; } DELAY_BUFFER_STEP (&reader->r_delay); DELAY_BUFFER_STEP (&reader->g_delay); DELAY_BUFFER_STEP (&reader->b_delay); return SANE_STATUS_GOOD; } static SANE_Status line_read_bgr_12_line_mode (GT68xx_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; SANE_Int pixels_per_line; SANE_Byte *pixel_buffer = reader->pixel_buffer; size = reader->params.scan_bpl * 3; RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size)); pixels_per_line = reader->pixels_per_line; unpack_12_le_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->b_delay), pixels_per_line); pixel_buffer += reader->params.scan_bpl; unpack_12_le_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->g_delay), pixels_per_line); pixel_buffer += reader->params.scan_bpl; unpack_12_le_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->r_delay), pixels_per_line); buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay); buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay); buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay); DELAY_BUFFER_STEP (&reader->r_delay); DELAY_BUFFER_STEP (&reader->g_delay); DELAY_BUFFER_STEP (&reader->b_delay); return SANE_STATUS_GOOD; } static SANE_Status line_read_bgr_16_line_mode (GT68xx_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; SANE_Int pixels_per_line; SANE_Byte *pixel_buffer = reader->pixel_buffer; size = reader->params.scan_bpl * 3; RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size)); pixels_per_line = reader->pixels_per_line; unpack_16_le_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->b_delay), pixels_per_line); pixel_buffer += reader->params.scan_bpl; unpack_16_le_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->g_delay), pixels_per_line); pixel_buffer += reader->params.scan_bpl; unpack_16_le_mono (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->r_delay), pixels_per_line); buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay); buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay); buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay); DELAY_BUFFER_STEP (&reader->r_delay); DELAY_BUFFER_STEP (&reader->g_delay); DELAY_BUFFER_STEP (&reader->b_delay); return SANE_STATUS_GOOD; } static SANE_Status line_read_rgb_8_pixel_mode (GT68xx_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; SANE_Int pixels_per_line; SANE_Byte *pixel_buffer = reader->pixel_buffer; size = reader->params.scan_bpl; RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size)); pixels_per_line = reader->pixels_per_line; unpack_8_rgb (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->r_delay), pixels_per_line); ++pixel_buffer; unpack_8_rgb (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->g_delay), pixels_per_line); ++pixel_buffer; unpack_8_rgb (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->b_delay), pixels_per_line); buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay); buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay); buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay); DELAY_BUFFER_STEP (&reader->r_delay); DELAY_BUFFER_STEP (&reader->g_delay); DELAY_BUFFER_STEP (&reader->b_delay); return SANE_STATUS_GOOD; } static SANE_Status line_read_rgb_12_pixel_mode (GT68xx_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; SANE_Byte *pixel_buffer = reader->pixel_buffer; size = reader->params.scan_bpl; RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size)); unpack_12_le_rgb (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->r_delay), DELAY_BUFFER_WRITE_PTR (&reader->g_delay), DELAY_BUFFER_WRITE_PTR (&reader->b_delay), reader->pixels_per_line); buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay); buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay); buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay); DELAY_BUFFER_STEP (&reader->r_delay); DELAY_BUFFER_STEP (&reader->g_delay); DELAY_BUFFER_STEP (&reader->b_delay); return SANE_STATUS_GOOD; } static SANE_Status line_read_rgb_16_pixel_mode (GT68xx_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; SANE_Int pixels_per_line; SANE_Byte *pixel_buffer = reader->pixel_buffer; size = reader->params.scan_bpl; RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size)); pixels_per_line = reader->pixels_per_line; unpack_16_le_rgb (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->r_delay), pixels_per_line); pixel_buffer += 2; unpack_16_le_rgb (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->g_delay), pixels_per_line); pixel_buffer += 2; unpack_16_le_rgb (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->b_delay), pixels_per_line); buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay); buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay); buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay); DELAY_BUFFER_STEP (&reader->r_delay); DELAY_BUFFER_STEP (&reader->g_delay); DELAY_BUFFER_STEP (&reader->b_delay); return SANE_STATUS_GOOD; } static SANE_Status line_read_bgr_8_pixel_mode (GT68xx_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; SANE_Int pixels_per_line; SANE_Byte *pixel_buffer = reader->pixel_buffer; size = reader->params.scan_bpl; RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size)); pixels_per_line = reader->pixels_per_line; unpack_8_rgb (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->b_delay), pixels_per_line); ++pixel_buffer; unpack_8_rgb (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->g_delay), pixels_per_line); ++pixel_buffer; unpack_8_rgb (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->r_delay), pixels_per_line); buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay); buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay); buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay); DELAY_BUFFER_STEP (&reader->r_delay); DELAY_BUFFER_STEP (&reader->g_delay); DELAY_BUFFER_STEP (&reader->b_delay); return SANE_STATUS_GOOD; } static SANE_Status line_read_bgr_12_pixel_mode (GT68xx_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; SANE_Byte *pixel_buffer = reader->pixel_buffer; size = reader->params.scan_bpl; RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size)); unpack_12_le_rgb (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->b_delay), DELAY_BUFFER_WRITE_PTR (&reader->g_delay), DELAY_BUFFER_WRITE_PTR (&reader->r_delay), reader->pixels_per_line); buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay); buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay); buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay); DELAY_BUFFER_STEP (&reader->r_delay); DELAY_BUFFER_STEP (&reader->g_delay); DELAY_BUFFER_STEP (&reader->b_delay); return SANE_STATUS_GOOD; } static SANE_Status line_read_bgr_16_pixel_mode (GT68xx_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; size_t size; SANE_Int pixels_per_line; SANE_Byte *pixel_buffer = reader->pixel_buffer; size = reader->params.scan_bpl; RIE (gt68xx_device_read (reader->dev, pixel_buffer, &size)); pixels_per_line = reader->pixels_per_line; unpack_16_le_rgb (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->b_delay), pixels_per_line); pixel_buffer += 2; unpack_16_le_rgb (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->g_delay), pixels_per_line); pixel_buffer += 2; unpack_16_le_rgb (pixel_buffer, DELAY_BUFFER_WRITE_PTR (&reader->r_delay), pixels_per_line); buffer_pointers_return[0] = DELAY_BUFFER_READ_PTR (&reader->r_delay); buffer_pointers_return[1] = DELAY_BUFFER_READ_PTR (&reader->g_delay); buffer_pointers_return[2] = DELAY_BUFFER_READ_PTR (&reader->b_delay); DELAY_BUFFER_STEP (&reader->r_delay); DELAY_BUFFER_STEP (&reader->g_delay); DELAY_BUFFER_STEP (&reader->b_delay); return SANE_STATUS_GOOD; } static SANE_Status gt68xx_line_reader_init_delays (GT68xx_Line_Reader * reader) { SANE_Status status; if (reader->params.color) { status = gt68xx_delay_buffer_init (&reader->r_delay, reader->params.scan_xs, reader->params.ld_shift_r + reader->params.ld_shift_double); if (status != SANE_STATUS_GOOD) return status; status = gt68xx_delay_buffer_init (&reader->g_delay, reader->params.scan_xs, reader->params.ld_shift_g + reader->params.ld_shift_double); if (status != SANE_STATUS_GOOD) { gt68xx_delay_buffer_done (&reader->r_delay); return status; } status = gt68xx_delay_buffer_init (&reader->b_delay, reader->params.scan_xs, reader->params.ld_shift_b + reader->params.ld_shift_double); if (status != SANE_STATUS_GOOD) { gt68xx_delay_buffer_done (&reader->g_delay); gt68xx_delay_buffer_done (&reader->r_delay); return status; } } else { status = gt68xx_delay_buffer_init (&reader->g_delay, reader->params.scan_xs, reader->params.ld_shift_double); if (status != SANE_STATUS_GOOD) return status; } reader->delays_initialized = SANE_TRUE; return SANE_STATUS_GOOD; } static void gt68xx_line_reader_free_delays (GT68xx_Line_Reader * reader) { if (reader->delays_initialized) { if (reader->params.color) { gt68xx_delay_buffer_done (&reader->b_delay); gt68xx_delay_buffer_done (&reader->g_delay); gt68xx_delay_buffer_done (&reader->r_delay); } else { gt68xx_delay_buffer_done (&reader->g_delay); } reader->delays_initialized = SANE_FALSE; } } SANE_Status gt68xx_line_reader_new (GT68xx_Device * dev, GT68xx_Scan_Parameters * params, SANE_Bool final_scan, GT68xx_Line_Reader ** reader_return) { SANE_Status status; GT68xx_Line_Reader *reader; SANE_Int image_size; SANE_Int scan_bpl_full; DBG (6, "gt68xx_line_reader_new: enter\n"); *reader_return = NULL; reader = (GT68xx_Line_Reader *) malloc (sizeof (GT68xx_Line_Reader)); if (!reader) { DBG (3, "gt68xx_line_reader_new: cannot allocate GT68xx_Line_Reader\n"); return SANE_STATUS_NO_MEM; } memset (reader, 0, sizeof (GT68xx_Line_Reader)); reader->dev = dev; memcpy (&reader->params, params, sizeof (GT68xx_Scan_Parameters)); reader->pixel_buffer = 0; reader->delays_initialized = SANE_FALSE; reader->read = NULL; status = gt68xx_line_reader_init_delays (reader); if (status != SANE_STATUS_GOOD) { DBG (3, "gt68xx_line_reader_new: cannot allocate line buffers: %s\n", sane_strstatus (status)); free (reader); reader = NULL; return status; } reader->pixels_per_line = reader->params.pixel_xs; if (!reader->params.color) { if (reader->params.depth == 8) { if (reader->params.ld_shift_double > 0) reader->read = line_read_gray_double_8; else reader->read = line_read_gray_8; } else if (reader->params.depth == 12) { if (reader->params.ld_shift_double > 0) reader->read = line_read_gray_double_12; else reader->read = line_read_gray_12; } else if (reader->params.depth == 16) { if (reader->params.ld_shift_double > 0) reader->read = line_read_gray_double_16; else reader->read = line_read_gray_16; } } else if (reader->params.line_mode) { if (reader->params.depth == 8) { if (dev->model->line_mode_color_order == COLOR_ORDER_RGB) { if (reader->params.ld_shift_double > 0) reader->read = line_read_rgb_double_8_line_mode; else reader->read = line_read_rgb_8_line_mode; } else if (dev->model->line_mode_color_order == COLOR_ORDER_BGR) reader->read = line_read_bgr_8_line_mode; } else if (reader->params.depth == 12) { if (dev->model->line_mode_color_order == COLOR_ORDER_RGB) { if (reader->params.ld_shift_double > 0) reader->read = line_read_rgb_double_12_line_mode; else reader->read = line_read_rgb_12_line_mode; } else if (dev->model->line_mode_color_order == COLOR_ORDER_BGR) reader->read = line_read_bgr_12_line_mode; } else if (reader->params.depth == 16) { if (dev->model->line_mode_color_order == COLOR_ORDER_RGB) { if (reader->params.ld_shift_double > 0) reader->read = line_read_rgb_double_16_line_mode; else reader->read = line_read_rgb_16_line_mode; } else if (dev->model->line_mode_color_order == COLOR_ORDER_BGR) reader->read = line_read_bgr_16_line_mode; } } else { if (reader->params.depth == 8) { if (dev->model->line_mode_color_order == COLOR_ORDER_RGB) reader->read = line_read_rgb_8_pixel_mode; else if (dev->model->line_mode_color_order == COLOR_ORDER_BGR) reader->read = line_read_bgr_8_pixel_mode; } else if (reader->params.depth == 12) { if (dev->model->line_mode_color_order == COLOR_ORDER_RGB) reader->read = line_read_rgb_12_pixel_mode; else if (dev->model->line_mode_color_order == COLOR_ORDER_BGR) reader->read = line_read_bgr_12_pixel_mode; } else if (reader->params.depth == 16) { if (dev->model->line_mode_color_order == COLOR_ORDER_RGB) reader->read = line_read_rgb_16_pixel_mode; else if (dev->model->line_mode_color_order == COLOR_ORDER_BGR) reader->read = line_read_bgr_16_pixel_mode; } } if (reader->read == NULL) { DBG (3, "gt68xx_line_reader_new: unsupported bit depth (%d)\n", reader->params.depth); gt68xx_line_reader_free_delays (reader); free (reader); reader = NULL; return SANE_STATUS_UNSUPPORTED; } scan_bpl_full = reader->params.scan_bpl; if (reader->params.color && reader->params.line_mode) scan_bpl_full *= 3; reader->pixel_buffer = malloc (scan_bpl_full); if (!reader->pixel_buffer) { DBG (3, "gt68xx_line_reader_new: cannot allocate pixel buffer\n"); gt68xx_line_reader_free_delays (reader); free (reader); reader = NULL; return SANE_STATUS_NO_MEM; } gt68xx_device_set_read_buffer_size (reader->dev, scan_bpl_full /* * 200 */ ); image_size = reader->params.scan_bpl * reader->params.scan_ys; status = gt68xx_device_read_prepare (reader->dev, image_size, final_scan); if (status != SANE_STATUS_GOOD) { DBG (3, "gt68xx_line_reader_new: gt68xx_device_read_prepare failed: %s\n", sane_strstatus (status)); free (reader->pixel_buffer); gt68xx_line_reader_free_delays (reader); free (reader); reader = NULL; return status; } DBG (6, "gt68xx_line_reader_new: leave: ok\n"); *reader_return = reader; return SANE_STATUS_GOOD; } SANE_Status gt68xx_line_reader_free (GT68xx_Line_Reader * reader) { SANE_Status status; DBG (6, "gt68xx_line_reader_free: enter\n"); if (reader == NULL) { DBG (3, "gt68xx_line_reader_free: already freed\n"); DBG (6, "gt68xx_line_reader_free: leave\n"); return SANE_STATUS_INVAL; } gt68xx_line_reader_free_delays (reader); if (reader->pixel_buffer) { free (reader->pixel_buffer); reader->pixel_buffer = NULL; } status = gt68xx_device_read_finish (reader->dev); if (status != SANE_STATUS_GOOD) { DBG (3, "gt68xx_line_reader_free: gt68xx_device_read_finish failed: %s\n", sane_strstatus (status)); } free (reader); reader = NULL; DBG (6, "gt68xx_line_reader_free: leave\n"); return status; } SANE_Status gt68xx_line_reader_read (GT68xx_Line_Reader * reader, unsigned int **buffer_pointers_return) { SANE_Status status; status = (*reader->read) (reader, buffer_pointers_return); return status; } /* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */ backends-1.3.0/backend/gt68xx_mid.h000066400000000000000000000120471456256263500170500ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2002 Sergey Vlasov 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, see . 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 GT68XX_MID_H #define GT68XX_MID_H /** @file * @brief Image data unpacking. */ #include "gt68xx_low.h" #include "../include/sane/sane.h" typedef struct GT68xx_Delay_Buffer GT68xx_Delay_Buffer; typedef struct GT68xx_Line_Reader GT68xx_Line_Reader; struct GT68xx_Delay_Buffer { SANE_Int line_count; SANE_Int read_index; SANE_Int write_index; unsigned int **lines; SANE_Byte *mem_block; }; /** * Object for reading image data line by line, with line distance correction. * * This object handles reading the image data from the scanner line by line and * converting it to internal format. Internally each image sample is * represented as unsigned int value, scaled to 16-bit range * (0-65535). For color images the data for each primary color is stored as * separate lines. */ struct GT68xx_Line_Reader { GT68xx_Device *dev; /**< Low-level interface object */ GT68xx_Scan_Parameters params; /**< Scan parameters */ #if 0 /** Number of bytes in the returned scanlines */ SANE_Int bytes_per_line; /** Number of bytes per pixel in the returned scanlines */ SANE_Int bytes_per_pixel; #endif /** Number of pixels in the returned scanlines */ SANE_Int pixels_per_line; SANE_Byte *pixel_buffer; GT68xx_Delay_Buffer r_delay; GT68xx_Delay_Buffer g_delay; GT68xx_Delay_Buffer b_delay; SANE_Bool delays_initialized; SANE_Status (*read) (GT68xx_Line_Reader * reader, unsigned int **buffer_pointers_return); }; /** * Create a new GT68xx_Line_Reader object. * * @param dev The low-level scanner interface object. * @param params Scan parameters prepared by gt68xx_device_setup_scan(). * @param final_scan SANE_TRUE for the final scan, SANE_FALSE for * calibration scans. * @param reader_return Location for the returned object. * * @return * - SANE_STATUS_GOOD - on success * - SANE_STATUS_NO_MEM - cannot allocate memory for object or buffers * - other error values - failure of some internal functions */ static SANE_Status gt68xx_line_reader_new (GT68xx_Device * dev, GT68xx_Scan_Parameters * params, SANE_Bool final_scan, GT68xx_Line_Reader ** reader_return); /** * Destroy the GT68xx_Line_Reader object. * * @param reader The GT68xx_Line_Reader object to destroy. */ static SANE_Status gt68xx_line_reader_free (GT68xx_Line_Reader * reader); /** * Read a scanline from the GT68xx_Line_Reader object. * * @param reader The GT68xx_Line_Reader object. * @param buffer_pointers_return Array of pointers to image lines (1 or 3 * elements) * * This function reads a full scanline from the device, unpacks it to internal * buffers and returns pointer to these buffers in @a * buffer_pointers_return[i]. For monochrome scan, only @a * buffer_pointers_return[0] is filled; for color scan, elements 0, 1, 2 are * filled with pointers to red, green, and blue data. The returned pointers * are valid until the next call to gt68xx_line_reader_read(), or until @a * reader is destroyed. * * @return * - SANE_STATUS_GOOD - read completed successfully * - other error value - an error occurred */ static SANE_Status gt68xx_line_reader_read (GT68xx_Line_Reader * reader, unsigned int **buffer_pointers_return); #endif /* not GT68XX_MID_H */ /* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */ backends-1.3.0/backend/gt68xx_shm_channel.c000066400000000000000000000477161456256263500205640ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2002 Sergey Vlasov 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, see . 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. */ /** @file * @brief Shared memory channel implementation. */ #include "gt68xx_shm_channel.h" #include #include #include #include #include #include #ifndef SHM_R #define SHM_R 0 #endif #ifndef SHM_W #define SHM_W 0 #endif /** Shared memory channel. * */ struct Shm_Channel { SANE_Int buf_size; /**< Size of each buffer */ SANE_Int buf_count; /**< Number of buffers */ void *shm_area; /**< Address of shared memory area */ SANE_Byte **buffers; /**< Array of pointers to buffers */ SANE_Int *buffer_bytes; /**< Array of buffer byte counts */ int writer_put_pipe[2]; /**< Notification pipe from writer */ int reader_put_pipe[2]; /**< Notification pipe from reader */ }; /** Dummy union to find out the needed alignment */ union Shm_Channel_Align { int i; long l; void *ptr; void (*func_ptr) (void); double d; }; /** Check if shm_channel is valid */ #define SHM_CHANNEL_CHECK(shm_channel, func_name) \ do { \ if ((shm_channel) == NULL) \ { \ DBG (3, "%s: BUG: shm_channel==NULL\n", (func_name)); \ return SANE_STATUS_INVAL; \ } \ } while (SANE_FALSE) /** Alignment for shared memory contents */ #define SHM_CHANNEL_ALIGNMENT (sizeof (union Shm_Channel_Align)) /** Align the given size up to a multiple of the given alignment */ #define SHM_CHANNEL_ROUND_UP(size, align) \ ( ((size) % (align)) ? ((size)/(align) + 1)*(align) : (size) ) /** Align the size using SHM_CHANNEL_ALIGNMENT */ #define SHM_CHANNEL_ALIGN(size) \ SHM_CHANNEL_ROUND_UP((size_t) (size), SHM_CHANNEL_ALIGNMENT) /** Close a file descriptor if it is currently open. * * This function checks if the file descriptor is not -1, and sets it to -1 * after close (so that it will not be closed twice). * * @param fd_var Pointer to a variable holding the file descriptor. */ static void shm_channel_fd_safe_close (int *fd_var) { if (*fd_var != -1) { close (*fd_var); *fd_var = -1; } } static SANE_Status shm_channel_fd_set_close_on_exec (int fd) { long value; value = fcntl (fd, F_GETFD, 0L); if (value == -1) return SANE_STATUS_IO_ERROR; if (fcntl (fd, F_SETFD, value | FD_CLOEXEC) == -1) return SANE_STATUS_IO_ERROR; return SANE_STATUS_GOOD; } #if 0 static SANE_Status shm_channel_fd_set_non_blocking (int fd, SANE_Bool non_blocking) { long value; value = fcntl (fd, F_GETFL, 0L); if (value == -1) return SANE_STATUS_IO_ERROR; if (non_blocking) value |= O_NONBLOCK; else value &= ~O_NONBLOCK; if (fcntl (fd, F_SETFL, value) == -1) return SANE_STATUS_IO_ERROR; return SANE_STATUS_GOOD; } #endif /** Create a new shared memory channel. * * This function should be called before the fork to set up the shared memory. * * @param buf_size Size of each shared memory buffer in bytes. * @param buf_count Number of shared memory buffers (up to 255). * @param shm_channel_return Returned shared memory channel object. */ SANE_Status shm_channel_new (SANE_Int buf_size, SANE_Int buf_count, Shm_Channel ** shm_channel_return) { Shm_Channel *shm_channel; void *shm_area; SANE_Byte *shm_data; int shm_buffer_bytes_size, shm_buffer_size; int shm_size; int shm_id; int i; if (buf_size <= 0) { DBG (3, "shm_channel_new: invalid buf_size=%d\n", buf_size); return SANE_STATUS_INVAL; } if (buf_count <= 0 || buf_count > 255) { DBG (3, "shm_channel_new: invalid buf_count=%d\n", buf_count); return SANE_STATUS_INVAL; } if (!shm_channel_return) { DBG (3, "shm_channel_new: BUG: shm_channel_return==NULL\n"); return SANE_STATUS_INVAL; } *shm_channel_return = NULL; shm_channel = (Shm_Channel *) malloc (sizeof (Shm_Channel)); if (!shm_channel) { DBG (3, "shm_channel_new: no memory for Shm_Channel\n"); return SANE_STATUS_NO_MEM; } shm_channel->buf_size = buf_size; shm_channel->buf_count = buf_count; shm_channel->shm_area = NULL; shm_channel->buffers = NULL; shm_channel->buffer_bytes = NULL; shm_channel->writer_put_pipe[0] = shm_channel->writer_put_pipe[1] = -1; shm_channel->reader_put_pipe[0] = shm_channel->reader_put_pipe[1] = -1; shm_channel->buffers = (SANE_Byte **) malloc (sizeof (SANE_Byte *) * buf_count); if (!shm_channel->buffers) { DBG (3, "shm_channel_new: no memory for buffer pointers\n"); shm_channel_free (shm_channel); return SANE_STATUS_NO_MEM; } if (pipe (shm_channel->writer_put_pipe) == -1) { DBG (3, "shm_channel_new: cannot create writer put pipe: %s\n", strerror (errno)); shm_channel_free (shm_channel); return SANE_STATUS_NO_MEM; } if (pipe (shm_channel->reader_put_pipe) == -1) { DBG (3, "shm_channel_new: cannot create reader put pipe: %s\n", strerror (errno)); shm_channel_free (shm_channel); return SANE_STATUS_NO_MEM; } shm_channel_fd_set_close_on_exec (shm_channel->reader_put_pipe[0]); shm_channel_fd_set_close_on_exec (shm_channel->reader_put_pipe[1]); shm_channel_fd_set_close_on_exec (shm_channel->writer_put_pipe[0]); shm_channel_fd_set_close_on_exec (shm_channel->writer_put_pipe[1]); shm_buffer_bytes_size = SHM_CHANNEL_ALIGN (sizeof (SANE_Int) * buf_count); shm_buffer_size = SHM_CHANNEL_ALIGN (buf_size); shm_size = shm_buffer_bytes_size + buf_count * shm_buffer_size; shm_id = shmget (IPC_PRIVATE, shm_size, IPC_CREAT | SHM_R | SHM_W); if (shm_id == -1) { DBG (3, "shm_channel_new: cannot create shared memory segment: %s\n", strerror (errno)); shm_channel_free (shm_channel); return SANE_STATUS_NO_MEM; } shm_area = shmat (shm_id, NULL, 0); if (shm_area == (void *) -1) { DBG (3, "shm_channel_new: cannot attach to shared memory segment: %s\n", strerror (errno)); shmctl (shm_id, IPC_RMID, NULL); shm_channel_free (shm_channel); return SANE_STATUS_NO_MEM; } if (shmctl (shm_id, IPC_RMID, NULL) == -1) { DBG (3, "shm_channel_new: cannot remove shared memory segment id: %s\n", strerror (errno)); shmdt (shm_area); shmctl (shm_id, IPC_RMID, NULL); shm_channel_free (shm_channel); return SANE_STATUS_NO_MEM; } shm_channel->shm_area = shm_area; shm_channel->buffer_bytes = (SANE_Int *) shm_area; shm_data = ((SANE_Byte *) shm_area) + shm_buffer_bytes_size; for (i = 0; i < shm_channel->buf_count; ++i) { shm_channel->buffers[i] = shm_data; shm_data += shm_buffer_size; } *shm_channel_return = shm_channel; return SANE_STATUS_GOOD; } /** Close the shared memory channel and release associated resources. * * @param shm_channel Shared memory channel object. */ SANE_Status shm_channel_free (Shm_Channel * shm_channel) { SHM_CHANNEL_CHECK (shm_channel, "shm_channel_free"); if (shm_channel->shm_area) { shmdt (shm_channel->shm_area); shm_channel->shm_area = NULL; } if (shm_channel->buffers) { free (shm_channel->buffers); shm_channel->buffers = NULL; } shm_channel_fd_safe_close (&shm_channel->reader_put_pipe[0]); shm_channel_fd_safe_close (&shm_channel->reader_put_pipe[1]); shm_channel_fd_safe_close (&shm_channel->writer_put_pipe[0]); shm_channel_fd_safe_close (&shm_channel->writer_put_pipe[1]); return SANE_STATUS_GOOD; } /** Initialize the shared memory channel in the writer process. * * This function should be called after the fork in the process which will * write data to the channel. * * @param shm_channel Shared memory channel object. */ SANE_Status shm_channel_writer_init (Shm_Channel * shm_channel) { SHM_CHANNEL_CHECK (shm_channel, "shm_channel_writer_init"); shm_channel_fd_safe_close (&shm_channel->writer_put_pipe[0]); shm_channel_fd_safe_close (&shm_channel->reader_put_pipe[1]); return SANE_STATUS_GOOD; } /** Get a free shared memory buffer for writing. * * This function may block waiting for a free buffer (if the reader process * does not process the data fast enough). * * After successful call to this function the writer process should fill the * buffer with the data and pass the buffer identifier from @a buffer_id_return * to shm_channel_writer_put_buffer() to give the buffer to the reader process. * * @param shm_channel Shared memory channel object. * @param buffer_id_return Returned buffer identifier. * @param buffer_addr_return Returned buffer address. * * @return * - SANE_STATUS_GOOD - a free buffer was available (or became available after * waiting for it); @a buffer_id_return and @a buffer_addr_return are filled * with valid values. * - SANE_STATUS_EOF - the reader process has closed its half of the channel. * - SANE_STATUS_IO_ERROR - an I/O error occurred. */ SANE_Status shm_channel_writer_get_buffer (Shm_Channel * shm_channel, SANE_Int * buffer_id_return, SANE_Byte ** buffer_addr_return) { SANE_Byte buf_index; int bytes_read; SHM_CHANNEL_CHECK (shm_channel, "shm_channel_writer_get_buffer"); do bytes_read = read (shm_channel->reader_put_pipe[0], &buf_index, 1); while (bytes_read == -1 && errno == EINTR); if (bytes_read == 1) { SANE_Int index = buf_index; if (index < shm_channel->buf_count) { *buffer_id_return = index; *buffer_addr_return = shm_channel->buffers[index]; return SANE_STATUS_GOOD; } } *buffer_id_return = -1; *buffer_addr_return = NULL; if (bytes_read == 0) return SANE_STATUS_EOF; else return SANE_STATUS_IO_ERROR; } /** Pass a filled shared memory buffer to the reader process. * * @param shm_channel Shared memory channel object. * @param buffer_id Buffer identifier from shm_channel_writer_put_buffer(). * @param buffer_bytes Number of data bytes in the buffer. * * @return * - SANE_STATUS_GOOD - the buffer was successfully queued. * - SANE_STATUS_IO_ERROR - the reader process has closed its half of the * channel, or another I/O error occurred. */ SANE_Status shm_channel_writer_put_buffer (Shm_Channel * shm_channel, SANE_Int buffer_id, SANE_Int buffer_bytes) { SANE_Byte buf_index; int bytes_written; SHM_CHANNEL_CHECK (shm_channel, "shm_channel_writer_put_buffer"); if (buffer_id < 0 || buffer_id >= shm_channel->buf_count) { DBG (3, "shm_channel_writer_put_buffer: BUG: buffer_id=%d\n", buffer_id); return SANE_STATUS_INVAL; } shm_channel->buffer_bytes[buffer_id] = buffer_bytes; buf_index = (SANE_Byte) buffer_id; do bytes_written = write (shm_channel->writer_put_pipe[1], &buf_index, 1); while ((bytes_written == 0) || (bytes_written == -1 && errno == EINTR)); if (bytes_written == 1) return SANE_STATUS_GOOD; else return SANE_STATUS_IO_ERROR; } /** Close the writing half of the shared memory channel. * * @param shm_channel Shared memory channel object. */ SANE_Status shm_channel_writer_close (Shm_Channel * shm_channel) { SHM_CHANNEL_CHECK (shm_channel, "shm_channel_writer_close"); shm_channel_fd_safe_close (&shm_channel->writer_put_pipe[1]); return SANE_STATUS_GOOD; } /** Initialize the shared memory channel in the reader process. * * This function should be called after the fork in the process which will * read data from the channel. * * @param shm_channel Shared memory channel object. */ SANE_Status shm_channel_reader_init (Shm_Channel * shm_channel) { SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_init"); shm_channel_fd_safe_close (&shm_channel->writer_put_pipe[1]); /* Don't close reader_put_pipe[0] here. Otherwise, if the channel writer * process dies early, this process might get SIGPIPE - and I don't want to * mess with signals in the main process. */ /* shm_channel_fd_safe_close (&shm_channel->reader_put_pipe[0]); */ return SANE_STATUS_GOOD; } #if 0 /** Set non-blocking or blocking mode for the reading half of the shared memory * channel. * * @param shm_channel Shared memory channel object. * @param non_blocking SANE_TRUE to make the channel non-blocking, SANE_FALSE * to set blocking mode. * * @return * - SANE_STATUS_GOOD - the requested mode was set successfully. * - SANE_STATUS_IO_ERROR - error setting the requested mode. */ SANE_Status shm_channel_reader_set_io_mode (Shm_Channel * shm_channel, SANE_Bool non_blocking) { SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_set_io_mode"); return shm_channel_fd_set_non_blocking (shm_channel->writer_put_pipe[0], non_blocking); } /** Get the file descriptor which will signal when some data is available in * the shared memory channel. * * The returned file descriptor can be used in select() or poll(). When one of * these functions signals that the file descriptor is ready for reading, * shm_channel_reader_get_buffer() should return some data without blocking. * * @param shm_channel Shared memory channel object. * @param fd_return The returned file descriptor. * * @return * - SANE_STATUS_GOOD - the file descriptor was returned. */ SANE_Status shm_channel_reader_get_select_fd (Shm_Channel * shm_channel, SANE_Int * fd_return) { SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_get_select_fd"); *fd_return = shm_channel->writer_put_pipe[0]; return SANE_STATUS_GOOD; } #endif /** Start reading from the shared memory channel. * * A newly initialized shared memory channel is stopped - the writer process * will block on shm_channel_writer_get_buffer(). This function will pass all * available buffers to the writer process, starting the transfer through the * channel. * * @param shm_channel Shared memory channel object. */ SANE_Status shm_channel_reader_start (Shm_Channel * shm_channel) { int i, bytes_written; SANE_Byte buffer_id; SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_start"); for (i = 0; i < shm_channel->buf_count; ++i) { buffer_id = i; do bytes_written = write (shm_channel->reader_put_pipe[1], &buffer_id, 1); while ((bytes_written == 0) || (bytes_written == -1 && errno == EINTR)); if (bytes_written == -1) { DBG (3, "shm_channel_reader_start: write error at buffer %d: %s\n", i, strerror (errno)); return SANE_STATUS_IO_ERROR; } } return SANE_STATUS_GOOD; } /** Get the next shared memory buffer passed from the writer process. * * If the channel was not set to non-blocking mode, this function will block * until the data buffer arrives from the writer process. In non-blocking mode * this function will place NULL in @a *buffer_addr_return and return * SANE_STATUS_GOOD if a buffer is not available immediately. * * After successful completion of this function (return value is * SANE_STATUS_GOOD and @a *buffer_addr_return is not NULL) the reader process * should process the data in the buffer and then call * shm_channel_reader_put_buffer() to release the buffer. * * @param shm_channel Shared memory channel object. * @param buffer_id_return Returned buffer identifier. * @param buffer_addr_return Returned buffer address. * @param buffer_bytes_return Returned number of data bytes in the buffer. * * @return * - SANE_STATUS_GOOD - no error. If the channel was in non-blocking mode, @a * *buffer_id_return may be NULL, indicating that no data was available. * Otherwise, @a *buffer_id_return, @a *buffer_addr_return and @a * *buffer_bytes return are filled with valid values. * - SANE_STATUS_EOF - the writer process has closed its half of the channel. * - SANE_STATUS_IO_ERROR - an I/O error occurred. */ SANE_Status shm_channel_reader_get_buffer (Shm_Channel * shm_channel, SANE_Int * buffer_id_return, SANE_Byte ** buffer_addr_return, SANE_Int * buffer_bytes_return) { SANE_Byte buf_index; int bytes_read; SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_get_buffer"); do bytes_read = read (shm_channel->writer_put_pipe[0], &buf_index, 1); while (bytes_read == -1 && errno == EINTR); if (bytes_read == 1) { SANE_Int index = buf_index; if (index < shm_channel->buf_count) { *buffer_id_return = index; *buffer_addr_return = shm_channel->buffers[index]; *buffer_bytes_return = shm_channel->buffer_bytes[index]; return SANE_STATUS_GOOD; } } *buffer_id_return = -1; *buffer_addr_return = NULL; *buffer_bytes_return = 0; if (bytes_read == 0) return SANE_STATUS_EOF; else return SANE_STATUS_IO_ERROR; } /** Release a shared memory buffer received by the reader process. * * This function must be called after shm_channel_reader_get_buffer() to * release the buffer and make it available for transferring the next portion * of data. * * After calling this function the reader process must not access the buffer * contents; any data which may be needed later should be copied into some * other place beforehand. * * @param shm_channel Shared memory channel object. * @param buffer_id Buffer identifier from shm_channel_reader_get_buffer(). * * @return * - SANE_STATUS_GOOD - the buffer was successfully released. * - SANE_STATUS_IO_ERROR - the writer process has closed its half of the * channel, or an unexpected I/O error occurred. */ SANE_Status shm_channel_reader_put_buffer (Shm_Channel * shm_channel, SANE_Int buffer_id) { SANE_Byte buf_index; int bytes_written; SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_put_buffer"); if (buffer_id < 0 || buffer_id >= shm_channel->buf_count) { DBG (3, "shm_channel_reader_put_buffer: BUG: buffer_id=%d\n", buffer_id); return SANE_STATUS_INVAL; } buf_index = (SANE_Byte) buffer_id; do bytes_written = write (shm_channel->reader_put_pipe[1], &buf_index, 1); while ((bytes_written == 0) || (bytes_written == -1 && errno == EINTR)); if (bytes_written == 1) return SANE_STATUS_GOOD; else return SANE_STATUS_IO_ERROR; } #if 0 /** Close the reading half of the shared memory channel. * * @param shm_channel Shared memory channel object. */ SANE_Status shm_channel_reader_close (Shm_Channel * shm_channel) { SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_close"); shm_channel_fd_safe_close (&shm_channel->reader_put_pipe[1]); return SANE_STATUS_GOOD; } #endif /* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */ backends-1.3.0/backend/gt68xx_shm_channel.h000066400000000000000000000066241456256263500205620ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2002 Sergey Vlasov 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, see . 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 GT68XX_SHM_CHANNEL_H #define GT68XX_SHM_CHANNEL_H /** @file * @brief Shared memory channel support. */ #include "../include/sane/sane.h" typedef struct Shm_Channel Shm_Channel; static SANE_Status shm_channel_new (SANE_Int buf_size, SANE_Int buf_count, Shm_Channel ** shm_channel_return); static SANE_Status shm_channel_free (Shm_Channel * shm_channel); static SANE_Status shm_channel_writer_init (Shm_Channel * shm_channel); static SANE_Status shm_channel_writer_get_buffer (Shm_Channel * shm_channel, SANE_Int * buffer_id_return, SANE_Byte ** buffer_addr_return); static SANE_Status shm_channel_writer_put_buffer (Shm_Channel * shm_channel, SANE_Int buffer_id, SANE_Int buffer_bytes); static SANE_Status shm_channel_writer_close (Shm_Channel * shm_channel); static SANE_Status shm_channel_reader_init (Shm_Channel * shm_channel); #if 0 static SANE_Status shm_channel_reader_set_io_mode (Shm_Channel * shm_channel, SANE_Bool non_blocking); static SANE_Status shm_channel_reader_get_select_fd (Shm_Channel * shm_channel, SANE_Int * fd_return); #endif static SANE_Status shm_channel_reader_start (Shm_Channel * shm_channel); static SANE_Status shm_channel_reader_get_buffer (Shm_Channel * shm_channel, SANE_Int * buffer_id_return, SANE_Byte ** buffer_addr_return, SANE_Int * buffer_bytes_return); static SANE_Status shm_channel_reader_put_buffer (Shm_Channel * shm_channel, SANE_Int buffer_id); #if 0 static SANE_Status shm_channel_reader_close (Shm_Channel * shm_channel); #endif #endif /* not GT68XX_SHM_CHANNEL_H */ /* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */ backends-1.3.0/backend/hp-accessor.c000066400000000000000000000517611456256263500172570ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1997 Geoffrey T. Dairiki 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, see . 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 is part of a SANE backend for HP Scanners supporting HP Scanner Control Language (SCL). */ /* #define STUBS extern int sanei_debug_hp; */ #define DEBUG_DECLARE_ONLY #include "../include/sane/config.h" #include "../include/sane/sanei_backend.h" #include "../include/lassert.h" #include #include #include "hp.h" #include "hp-option.h" #include "hp-accessor.h" #include "hp-device.h" #define DATA_SIZE_INCREMENT (1024) /* * class HpData */ struct hp_data_s { hp_byte_t * buf; size_t bufsiz; size_t length; hp_bool_t frozen; }; static void hp_data_resize (HpData this, size_t newsize) { if (this->bufsiz != newsize) { assert(!this->frozen); this->buf = sanei_hp_realloc(this->buf, newsize); assert(this->buf); this->bufsiz = newsize; } } static void hp_data_freeze (HpData this) { hp_data_resize(this, this->length); this->frozen = 1; } static size_t hp_data_alloc (HpData this, size_t sz) { size_t newsize = this->bufsiz; size_t offset = this->length; /* * mike@easysw.com: * * The following code is REQUIRED so that pointers, etc. aren't * misaligned. This causes MAJOR problems on all SPARC, ALPHA, * and MIPS processors, and possibly others. * * The workaround is to ensure that all allocations are in multiples * of 8 bytes. */ sz = (sz + sizeof (long) - 1) & ~(sizeof (long) - 1); while (newsize < this->length + sz) newsize += DATA_SIZE_INCREMENT; hp_data_resize(this, newsize); this->length += sz; return offset; } static void * hp_data_data (HpData this, size_t offset) { assert(offset < this->length); return (char *)this->buf + offset; } HpData sanei_hp_data_new (void) { return sanei_hp_allocz(sizeof(struct hp_data_s)); } HpData sanei_hp_data_dup (HpData orig) { HpData new; hp_data_freeze(orig); if (!( new = sanei_hp_memdup(orig, sizeof(*orig)) )) return 0; if (!(new->buf = sanei_hp_memdup(orig->buf, orig->bufsiz))) { sanei_hp_free(new); return 0; } return new; } void sanei_hp_data_destroy (HpData this) { sanei_hp_free(this->buf); sanei_hp_free(this); } /* * class HpAccessor */ typedef const struct hp_accessor_type_s * HpAccessorType; typedef struct hp_accessor_s * _HpAccessor; struct hp_accessor_s { HpAccessorType type; size_t data_offset; size_t data_size; }; struct hp_accessor_type_s { SANE_Status (*get)(HpAccessor this, HpData data, void * valp); SANE_Status (*set)(HpAccessor this, HpData data, void * valp); int (*getint)(HpAccessor this, HpData data); void (*setint)(HpAccessor this, HpData data, int val); }; SANE_Status sanei_hp_accessor_get (HpAccessor this, HpData data, void * valp) { if (!this->type->get) return SANE_STATUS_INVAL; return (*this->type->get)(this, data, valp); } SANE_Status sanei_hp_accessor_set (HpAccessor this, HpData data, void * valp) { if (!this->type->set) return SANE_STATUS_INVAL; return (*this->type->set)(this, data, valp); } int sanei_hp_accessor_getint (HpAccessor this, HpData data) { assert (this->type->getint); return (*this->type->getint)(this, data); } void sanei_hp_accessor_setint (HpAccessor this, HpData data, int val) { assert (this->type->setint); (*this->type->setint)(this, data, val); } const void * sanei_hp_accessor_data (HpAccessor this, HpData data) { return hp_data_data(data, this->data_offset); } void * sanei__hp_accessor_data (HpAccessor this, HpData data) { return hp_data_data(data, this->data_offset); } size_t sanei_hp_accessor_size (HpAccessor this) { return this->data_size; } HpAccessor sanei_hp_accessor_new (HpData data, size_t sz) { static const struct hp_accessor_type_s type = { 0, 0, 0, 0 }; _HpAccessor new = sanei_hp_alloc(sizeof(*new)); new->type = &type; new->data_offset = hp_data_alloc(data, new->data_size = sz); return new; } /* * class HpAccessorInt */ #define hp_accessor_int_s hp_accessor_s typedef const struct hp_accessor_int_s * HpAccessorInt; typedef struct hp_accessor_int_s * _HpAccessorInt; static SANE_Status hp_accessor_int_get (HpAccessor this, HpData data, void * valp) { *(SANE_Int*)valp = *(int *)hp_data_data(data, this->data_offset); return SANE_STATUS_GOOD; } static SANE_Status hp_accessor_int_set (HpAccessor this, HpData data, void * valp) { *(int *)hp_data_data(data, this->data_offset) = *(SANE_Int*)valp; return SANE_STATUS_GOOD; } static int hp_accessor_int_getint (HpAccessor this, HpData data) { return *(int *)hp_data_data(data, this->data_offset); } static void hp_accessor_int_setint (HpAccessor this, HpData data, int val) { *(int *)hp_data_data(data, this->data_offset) = val; } HpAccessor sanei_hp_accessor_int_new (HpData data) { static const struct hp_accessor_type_s type = { hp_accessor_int_get, hp_accessor_int_set, hp_accessor_int_getint, hp_accessor_int_setint }; _HpAccessorInt new = sanei_hp_alloc(sizeof(*new)); new->type = &type; new->data_offset = hp_data_alloc(data, new->data_size = sizeof(int)); return (HpAccessor)new; } /* * class HpAccessorBool */ #define hp_accessor_bool_s hp_accessor_s typedef const struct hp_accessor_bool_s * HpAccessorBool; typedef struct hp_accessor_bool_s * _HpAccessorBool; static SANE_Status hp_accessor_bool_get (HpAccessor this, HpData data, void * valp) { int val = *(int *)hp_data_data(data, this->data_offset); *(SANE_Bool*)valp = val ? SANE_TRUE : SANE_FALSE; return SANE_STATUS_GOOD; } static SANE_Status hp_accessor_bool_set (HpAccessor this, HpData data, void * valp) { int * datap = hp_data_data(data, this->data_offset); *datap = *(SANE_Bool*)valp == SANE_FALSE ? 0 : 1; return SANE_STATUS_GOOD; } HpAccessor sanei_hp_accessor_bool_new (HpData data) { static const struct hp_accessor_type_s type = { hp_accessor_bool_get, hp_accessor_bool_set, hp_accessor_int_getint, hp_accessor_int_setint }; _HpAccessorBool new = sanei_hp_alloc(sizeof(*new)); new->type = &type; new->data_offset = hp_data_alloc(data, new->data_size = sizeof(int)); return (HpAccessor)new; } /* * class HpAccessorFixed */ #define hp_accessor_fixed_s hp_accessor_s typedef const struct hp_accessor_fixed_s * HpAccessorFixed; typedef struct hp_accessor_fixed_s * _HpAccessorFixed; static SANE_Status hp_accessor_fixed_get (HpAccessor this, HpData data, void * valp) { *(SANE_Fixed*)valp = *(SANE_Fixed *)hp_data_data(data, this->data_offset); return SANE_STATUS_GOOD; } static SANE_Status hp_accessor_fixed_set (HpAccessor this, HpData data, void * valp) { *(SANE_Fixed *)hp_data_data(data, this->data_offset) = *(SANE_Fixed*)valp; return SANE_STATUS_GOOD; } HpAccessor sanei_hp_accessor_fixed_new (HpData data) { static const struct hp_accessor_type_s type = { hp_accessor_fixed_get, hp_accessor_fixed_set, 0, 0 }; _HpAccessorFixed new = sanei_hp_alloc(sizeof(*new)); new->type = &type; new->data_offset = hp_data_alloc(data, new->data_size = sizeof(SANE_Fixed)); return (HpAccessor)new; } /* * class HpAccessorChoice */ typedef struct hp_accessor_choice_s * _HpAccessorChoice; struct hp_accessor_choice_s { HpAccessorType type; size_t data_offset; size_t data_size; HpChoice choices; SANE_String_Const * strlist; }; static SANE_Status hp_accessor_choice_get (HpAccessor this, HpData data, void * valp) { HpChoice choice = *(HpChoice *)hp_data_data(data, this->data_offset); strcpy(valp, choice->name); return SANE_STATUS_GOOD; } static SANE_Status hp_accessor_choice_set (HpAccessor _this, HpData data, void * valp) { HpAccessorChoice this = (HpAccessorChoice)_this; HpChoice choice; SANE_String_Const * strlist = this->strlist; for (choice = this->choices; choice; choice = choice->next) { /* Skip choices which aren't in strlist. */ if (!*strlist || strcmp(*strlist, choice->name) != 0) continue; strlist++; if (strcmp((const char *)valp, choice->name) == 0) { *(HpChoice *)hp_data_data(data, this->data_offset) = choice; return SANE_STATUS_GOOD; } } return SANE_STATUS_INVAL; } static int hp_accessor_choice_getint (HpAccessor this, HpData data) { HpChoice choice = *(HpChoice *)hp_data_data(data, this->data_offset); return choice->val; } static void hp_accessor_choice_setint (HpAccessor _this, HpData data, int val) { HpAccessorChoice this = (HpAccessorChoice)_this; HpChoice choice; HpChoice first_choice = 0; SANE_String_Const * strlist = this->strlist; for (choice = this->choices; choice; choice = choice->next) { /* Skip choices which aren't in strlist. */ if (!*strlist || strcmp(*strlist, choice->name) != 0) continue; strlist++; if (!first_choice) first_choice = choice; /* First enabled choice */ if (choice->val == val) { *(HpChoice *)hp_data_data(data, this->data_offset) = choice; return; } } if (first_choice) *(HpChoice *)hp_data_data(data, this->data_offset) = first_choice; else assert(!"No choices to choose from?"); } SANE_Int sanei_hp_accessor_choice_maxsize (HpAccessorChoice this) { HpChoice choice; SANE_Int size = 0; for (choice = this->choices; choice; choice = choice->next) if ((SANE_Int)strlen(choice->name) >= size) size = strlen(choice->name) + 1; return size; } SANE_String_Const * sanei_hp_accessor_choice_strlist (HpAccessorChoice this, HpOptSet optset, HpData data, const HpDeviceInfo *info) { if (optset) { int old_val = hp_accessor_choice_getint((HpAccessor)this, data); HpChoice choice; size_t count = 0; for (choice = this->choices; choice; choice = choice->next) if (sanei_hp_choice_isEnabled(choice, optset, data, info)) this->strlist[count++] = choice->name; this->strlist[count] = 0; hp_accessor_choice_setint((HpAccessor)this, data, old_val); } return this->strlist; } HpAccessor sanei_hp_accessor_choice_new (HpData data, HpChoice choices, hp_bool_t may_change) { static const struct hp_accessor_type_s type = { hp_accessor_choice_get, hp_accessor_choice_set, hp_accessor_choice_getint, hp_accessor_choice_setint }; HpChoice choice; size_t count = 0; _HpAccessorChoice this; if ( may_change ) data->frozen = 0; for (choice = choices; choice; choice = choice->next) count++; this = sanei_hp_alloc(sizeof(*this) + (count+1) * sizeof(*this->strlist)); if (!this) return 0; this->type = &type; this->data_offset = hp_data_alloc(data, this->data_size = sizeof(HpChoice)); this->choices = choices; this->strlist = (SANE_String_Const *)(this + 1); count = 0; for (choice = this->choices; choice; choice = choice->next) this->strlist[count++] = choice->name; this->strlist[count] = 0; return (HpAccessor)this; } /* * class HpAccessorVector */ typedef struct hp_accessor_vector_s * _HpAccessorVector; struct hp_accessor_vector_s { HpAccessorType type; size_t data_offset; size_t data_size; unsigned short mask; unsigned short length; unsigned short offset; short stride; unsigned short (*unscale)(HpAccessorVector this, SANE_Fixed fval); SANE_Fixed (*scale)(HpAccessorVector this, unsigned short val); SANE_Fixed fmin; SANE_Fixed fmax; }; unsigned sanei_hp_accessor_vector_length (HpAccessorVector this) { return this->length; } SANE_Fixed sanei_hp_accessor_vector_minval (HpAccessorVector this) { return this->fmin; } SANE_Fixed sanei_hp_accessor_vector_maxval (HpAccessorVector this) { return this->fmax; } static unsigned short _v_get (HpAccessorVector this, const unsigned char * data) { unsigned short val; if (this->mask <= 255) val = data[0]; else #ifndef NotOrig val = (data[0] << 8) + data[1]; #else val = (data[1] << 8) + data[0]; #endif return val & this->mask; } static void _v_set (HpAccessorVector this, unsigned char * data, unsigned short val) { val &= this->mask; if (this->mask <= 255) { data[0] = (unsigned char)val; } else { #ifndef NotOrig data[1] = (unsigned char)val; data[0] = (unsigned char)(val >> 8); #else data[0] = (unsigned char)val; data[1] = (unsigned char)(val >> 8); #endif } } static SANE_Status hp_accessor_vector_get (HpAccessor _this, HpData d, void * valp) { HpAccessorVector this = (HpAccessorVector)_this; SANE_Fixed * ptr = valp; const SANE_Fixed * end = ptr + this->length; const unsigned char * data = hp_data_data(d, this->data_offset); data += this->offset; while (ptr < end) { *ptr++ = (*this->scale)(this, _v_get(this, data)); data += this->stride; } return SANE_STATUS_GOOD; } static SANE_Status hp_accessor_vector_set (HpAccessor _this, HpData d, void * valp) { HpAccessorVector this = (HpAccessorVector)_this; SANE_Fixed * ptr = valp; const SANE_Fixed * end = ptr + this->length; unsigned char * data = hp_data_data(d, this->data_offset); data += this->offset; while (ptr < end) { if (*ptr < this->fmin) *ptr = this->fmin; if (*ptr > this->fmax) *ptr = this->fmax; _v_set(this, data, (*this->unscale)(this, *ptr++)); data += this->stride; } return SANE_STATUS_GOOD; } static unsigned short _vector_unscale (HpAccessorVector this, SANE_Fixed fval) { unsigned short max_val = this->mask; return (fval * max_val + SANE_FIX(0.5)) / SANE_FIX(1.0); } static SANE_Fixed _vector_scale (HpAccessorVector this, unsigned short val) { unsigned short max_val = this->mask; return (SANE_FIX(1.0) * val + max_val / 2) / max_val; } HpAccessor sanei_hp_accessor_vector_new (HpData data, unsigned length, unsigned depth) { static const struct hp_accessor_type_s type = { hp_accessor_vector_get, hp_accessor_vector_set, 0, 0 }; unsigned width = depth > 8 ? 2 : 1; _HpAccessorVector new = sanei_hp_alloc(sizeof(*new)); if (!new) return 0; assert(depth > 0 && depth <= 16); assert(length > 0); new->type = &type; new->data_size = length * width; new->data_offset = hp_data_alloc(data, new->data_size); new->mask = ((unsigned)1 << depth) - 1; new->length = length; new->offset = 0; new->stride = width; new->scale = _vector_scale; new->unscale = _vector_unscale; new->fmin = SANE_FIX(0.0); new->fmax = SANE_FIX(1.0); return (HpAccessor)new; } static unsigned short _gamma_vector_unscale (HpAccessorVector __sane_unused__ this, SANE_Fixed fval) { unsigned short unscaled = fval / SANE_FIX(1.0); if (unscaled > 255) unscaled = 255; unscaled = 255 - unscaled; /* Don't know why. But this is how it works. */ return unscaled; } static SANE_Fixed _gamma_vector_scale (HpAccessorVector __sane_unused__ this, unsigned short val) { SANE_Fixed scaled; val = 255-val; /* Don't know why. But this is how it works. */ scaled = val * SANE_FIX(1.0); return scaled; } HpAccessor sanei_hp_accessor_gamma_vector_new (HpData data, unsigned length, unsigned depth) { _HpAccessorVector this = ( (_HpAccessorVector) sanei_hp_accessor_vector_new(data, length, depth) ); if (!this) return 0; this->offset += this->stride * (this->length - 1); this->stride = -this->stride; this->scale = _gamma_vector_scale; this->unscale = _gamma_vector_unscale; this->fmin = SANE_FIX(0.0); this->fmax = SANE_FIX(255.0); return (HpAccessor)this; } static unsigned short _matrix_vector_unscale (HpAccessorVector this, SANE_Fixed fval) { unsigned short max_val = this->mask >> 1; unsigned short sign_bit = this->mask & ~max_val; unsigned short sign = 0; if (fval == SANE_FIX(1.0)) return sign_bit; if (fval < 0) { sign = sign_bit; fval = -fval; } return sign | ((fval * max_val + this->fmax / 2) / this->fmax); } static SANE_Fixed _matrix_vector_scale (HpAccessorVector this, unsigned short val) { unsigned short max_val = this->mask >> 1; unsigned short sign_bit = this->mask & ~max_val; SANE_Fixed fval; if (val == sign_bit) return SANE_FIX(1.0); fval = (this->fmax * (val & max_val) + max_val / 2) / max_val; if ((val & sign_bit) != 0) fval = -fval; return fval; } HpAccessor sanei_hp_accessor_matrix_vector_new (HpData data, unsigned length, unsigned depth) { _HpAccessorVector this = ( (_HpAccessorVector) sanei_hp_accessor_vector_new(data, length, depth) ); if (!this) return 0; this->scale = _matrix_vector_scale; this->unscale = _matrix_vector_unscale; this->fmax = depth == 10 ? SANE_FIX(4.0) : SANE_FIX(2.0); this->fmax *= (this->mask >> 1); this->fmax >>= (depth - 1); this->fmin = - this->fmax; return (HpAccessor)this; } HpAccessor sanei_hp_accessor_subvector_new (HpAccessorVector super, unsigned nchan, unsigned chan) { _HpAccessorVector this = sanei_hp_memdup(super, sizeof(*this)); if (!this) return 0; assert(chan < nchan); assert(this->length % nchan == 0); this->length /= nchan; if (this->stride < 0) this->offset += (nchan - chan - 1) * this->stride; else this->offset += chan * this->stride; this->stride *= nchan; return (HpAccessor)this; } /* * class HpAccessorGeometry */ typedef const struct hp_accessor_geometry_s * HpAccessorGeometry; typedef struct hp_accessor_geometry_s * _HpAccessorGeometry; struct hp_accessor_geometry_s { HpAccessorType type; size_t data_offset; size_t data_size; HpAccessor this; HpAccessor other; hp_bool_t is_br; HpAccessor resolution; }; static SANE_Status hp_accessor_geometry_set (HpAccessor _this, HpData data, void * _valp) { HpAccessorGeometry this = (HpAccessorGeometry)_this; SANE_Fixed * valp = _valp; SANE_Fixed limit; sanei_hp_accessor_get(this->other, data, &limit); if (this->is_br ? *valp < limit : *valp > limit) *valp = limit; return sanei_hp_accessor_set(this->this, data, valp); } static int _to_devpixels (SANE_Fixed val_mm, SANE_Fixed mm_per_pix) { assert(val_mm >= 0); return (val_mm + mm_per_pix / 2) / mm_per_pix; } static int hp_accessor_geometry_getint (HpAccessor _this, HpData data) { HpAccessorGeometry this = (HpAccessorGeometry)_this; SANE_Fixed this_val, other_val; int res = sanei_hp_accessor_getint(this->resolution, data); SANE_Fixed mm_per_pix = (SANE_FIX(MM_PER_INCH) + res / 2) / res; assert(res > 0); sanei_hp_accessor_get(this->this, data, &this_val); if (this->is_br) { /* Convert to extent. */ sanei_hp_accessor_get(this->other, data, &other_val); assert(this_val >= other_val && other_val >= 0); return (_to_devpixels(this_val, mm_per_pix) - _to_devpixels(other_val, mm_per_pix) + 1); } return _to_devpixels(this_val, mm_per_pix); } /* * we should implement hp_accessor_geometry_setint, but we don't * need it yet... */ HpAccessor sanei_hp_accessor_geometry_new (HpAccessor val, HpAccessor lim, hp_bool_t is_br, HpAccessor resolution) { static const struct hp_accessor_type_s type = { hp_accessor_fixed_get, hp_accessor_geometry_set, hp_accessor_geometry_getint, 0 }; _HpAccessorGeometry new = sanei_hp_alloc(sizeof(*new)); new->type = &type; new->data_offset = val->data_offset; new->data_size = val->data_size; new->this = val; new->other = lim; new->is_br = is_br; new->resolution = resolution; return (HpAccessor)new; } backends-1.3.0/backend/hp-accessor.h000066400000000000000000000100011456256263500172420ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1997 Geoffrey T. Dairiki 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, see . 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 is part of a SANE backend for HP Scanners supporting HP Scanner Control Language (SCL). */ #ifndef HP_ACCESSOR_H_INCLUDED #define HP_ACCESSOR_H_INCLUDED #include "hp.h" HpData sanei_hp_data_new (void); HpData sanei_hp_data_dup (HpData orig); void sanei_hp_data_destroy (HpData this); HpAccessor sanei_hp_accessor_new (HpData data, size_t size); HpAccessor sanei_hp_accessor_int_new (HpData data); HpAccessor sanei_hp_accessor_bool_new (HpData data); HpAccessor sanei_hp_accessor_fixed_new (HpData data); HpAccessor sanei_hp_accessor_choice_new(HpData data, HpChoice choices, hp_bool_t may_change); HpAccessor sanei_hp_accessor_vector_new(HpData data, unsigned length, unsigned depth); HpAccessor sanei_hp_accessor_gamma_vector_new(HpData data, unsigned length, unsigned depth); HpAccessor sanei_hp_accessor_matrix_vector_new(HpData data, unsigned length, unsigned depth); HpAccessor sanei_hp_accessor_subvector_new(HpAccessorVector super, unsigned nchan, unsigned chan); HpAccessor sanei_hp_accessor_geometry_new (HpAccessor val, HpAccessor lim, hp_bool_t is_br, HpAccessor res); SANE_Status sanei_hp_accessor_get (HpAccessor this, HpData data, void * valp); SANE_Status sanei_hp_accessor_set (HpAccessor this, HpData data, void * valp); int sanei_hp_accessor_getint(HpAccessor this, HpData data); void sanei_hp_accessor_setint(HpAccessor this, HpData data, int v); const void *sanei_hp_accessor_data (HpAccessor this, HpData data); void * sanei__hp_accessor_data (HpAccessor this, HpData data); size_t sanei_hp_accessor_size (HpAccessor this); unsigned sanei_hp_accessor_vector_length (HpAccessorVector this); SANE_Fixed sanei_hp_accessor_vector_minval (HpAccessorVector this); SANE_Fixed sanei_hp_accessor_vector_maxval (HpAccessorVector this); SANE_Int sanei_hp_accessor_choice_maxsize (HpAccessorChoice this); SANE_String_Const * sanei_hp_accessor_choice_strlist (HpAccessorChoice this, HpOptSet optset, HpData data, const HpDeviceInfo *info); #endif /* HP_ACCESSOR_H_INCLUDED */ backends-1.3.0/backend/hp-device.c000066400000000000000000000336021456256263500167060ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1997 Geoffrey T. Dairiki 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, see . 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 is part of a SANE backend for HP Scanners supporting HP Scanner Control Language (SCL). */ /*#define STUBS extern int sanei_debug_hp;*/ #define DEBUG_DECLARE_ONLY #include "../include/sane/config.h" #include #include #include "../include/lassert.h" #include "hp-device.h" #include "hp-accessor.h" #include "hp-option.h" #include "hp-scsi.h" #include "hp-scl.h" /* Mark an scl-command to be simulated */ SANE_Status sanei_hp_device_simulate_set (const char *devname, HpScl scl, int flag) {HpDeviceInfo *info; int inqid; info = sanei_hp_device_info_get ( devname ); if (!info) return SANE_STATUS_INVAL; inqid = SCL_INQ_ID(scl)-HP_SCL_INQID_MIN; info->simulate.sclsimulate[inqid] = flag; DBG(3, "hp_device_simulate_set: %d set to %ssimulated\n", inqid+HP_SCL_INQID_MIN, flag ? "" : "not "); return SANE_STATUS_GOOD; } /* Clear all simulation flags */ void sanei_hp_device_simulate_clear (const char *devname) {HpDeviceInfo *info; info = sanei_hp_device_info_get ( devname ); if (!info) return; memset (&(info->simulate.sclsimulate[0]), 0, sizeof (info->simulate.sclsimulate)); info->simulate.gamma_simulate = 0; } /* Get simulate flag for an scl-command */ hp_bool_t sanei_hp_device_simulate_get (const char *devname, HpScl scl) {HpDeviceInfo *info; int inqid; info = sanei_hp_device_info_get ( devname ); if (!info) return 0; inqid = SCL_INQ_ID(scl)-HP_SCL_INQID_MIN; return info->simulate.sclsimulate[inqid]; } SANE_Status sanei_hp_device_support_get (const char *devname, HpScl scl, int *minval, int *maxval) {HpDeviceInfo *info; HpSclSupport *sclsupport; int inqid; /* #define HP_TEST_SIMULATE */ #ifdef HP_TEST_SIMULATE if (scl == SCL_BRIGHTNESS) return SANE_STATUS_UNSUPPORTED; if (scl == SCL_CONTRAST) return SANE_STATUS_UNSUPPORTED; if (scl == SCL_DOWNLOAD_TYPE) { *minval = 2; *maxval = 14; return SANE_STATUS_GOOD; } #endif info = sanei_hp_device_info_get ( devname ); if (!info) return SANE_STATUS_INVAL; inqid = SCL_INQ_ID(scl)-HP_SCL_INQID_MIN; sclsupport = &(info->sclsupport[inqid]); if ( !(sclsupport->checked) ) return SANE_STATUS_INVAL; if ( !(sclsupport->is_supported) ) return SANE_STATUS_UNSUPPORTED; if (minval) *minval = sclsupport->minval; if (maxval) *maxval = sclsupport->maxval; return SANE_STATUS_GOOD; } /* Update the list of supported commands */ SANE_Status sanei_hp_device_support_probe (HpScsi scsi) {HpDeviceInfo *info; HpSclSupport *sclsupport; SANE_Status status; int k, val, inqid; static HpScl sclprobe[] = /* The commands that should be probed */ { SCL_AUTO_BKGRND, SCL_COMPRESSION, SCL_DOWNLOAD_TYPE, SCL_X_SCALE, SCL_Y_SCALE, SCL_DATA_WIDTH, SCL_INVERSE_IMAGE, SCL_BW_DITHER, SCL_CONTRAST, SCL_BRIGHTNESS, #ifdef SCL_SHARPENING SCL_SHARPENING, #endif SCL_MIRROR_IMAGE, SCL_X_RESOLUTION, SCL_Y_RESOLUTION, SCL_OUTPUT_DATA_TYPE, SCL_PRELOAD_ADF, SCL_MEDIA, SCL_X_EXTENT, SCL_Y_EXTENT, SCL_X_POS, SCL_Y_POS, SCL_SPEED, SCL_FILTER, SCL_TONE_MAP, SCL_MATRIX, SCL_UNLOAD, SCL_CHANGE_DOC, SCL_ADF_BFEED }; enum hp_device_compat_e compat; DBG(1, "hp_device_support_probe: Check supported commands for %s\n", sanei_hp_scsi_devicename (scsi) ); info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); assert (info); memset (&(info->sclsupport[0]), 0, sizeof (info->sclsupport)); for (k = 0; k < (int)(sizeof (sclprobe) / sizeof (sclprobe[0])); k++) { inqid = SCL_INQ_ID(sclprobe[k])-HP_SCL_INQID_MIN; sclsupport = &(info->sclsupport[inqid]); status = sanei_hp_scl_inquire (scsi, sclprobe[k], &val, &(sclsupport->minval), &(sclsupport->maxval)); sclsupport->is_supported = (status == SANE_STATUS_GOOD); sclsupport->checked = 1; /* The OfficeJets seem to ignore brightness and contrast settings, * so we'll pretend they're not supported at all. */ if (((sclprobe[k]==SCL_BRIGHTNESS) || (sclprobe[k]==SCL_CONTRAST)) && (sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD) && (compat & HP_COMPAT_OJ_1150C)) { sclsupport->is_supported=0; } if (sclsupport->is_supported) { DBG(1, "hp_device_support_probe: %d supported (%d..%d, %d)\n", inqid+HP_SCL_INQID_MIN, sclsupport->minval, sclsupport->maxval, val); } else { DBG(1, "hp_device_support_probe: %d not supported\n", inqid+HP_SCL_INQID_MIN); } } return SANE_STATUS_GOOD; } SANE_Status sanei_hp_device_probe_model (enum hp_device_compat_e *compat, HpScsi scsi, int *model_num, const char **model_name) { static struct { HpScl cmd; int model_num; const char * model; enum hp_device_compat_e flag; } probes[] = { { SCL_HP_MODEL_1, 1, "ScanJet Plus", HP_COMPAT_PLUS }, { SCL_HP_MODEL_2, 2, "ScanJet IIc", HP_COMPAT_2C }, { SCL_HP_MODEL_3, 3, "ScanJet IIp", HP_COMPAT_2P }, { SCL_HP_MODEL_4, 4, "ScanJet IIcx", HP_COMPAT_2CX }, { SCL_HP_MODEL_5, 5, "ScanJet 3c/4c/6100C", HP_COMPAT_4C }, { SCL_HP_MODEL_6, 6, "ScanJet 3p", HP_COMPAT_3P }, { SCL_HP_MODEL_8, 8, "ScanJet 4p", HP_COMPAT_4P }, { SCL_HP_MODEL_9, 9, "ScanJet 5p/4100C/5100C", HP_COMPAT_5P }, { SCL_HP_MODEL_10,10,"PhotoSmart Photo Scanner", HP_COMPAT_PS }, { SCL_HP_MODEL_11,11,"OfficeJet 1150C", HP_COMPAT_OJ_1150C }, { SCL_HP_MODEL_12,12,"OfficeJet 1170C or later", HP_COMPAT_OJ_1170C }, { SCL_HP_MODEL_14,14,"ScanJet 62x0C", HP_COMPAT_6200C }, { SCL_HP_MODEL_16,15,"ScanJet 5200C", HP_COMPAT_5200C }, { SCL_HP_MODEL_17,17,"ScanJet 63x0C", HP_COMPAT_6300C } }; int i; char buf[8]; SANE_Status status; static char *last_device = NULL; static enum hp_device_compat_e last_compat; static int last_model_num = -1; static const char *last_model_name = "Model Unknown"; assert(scsi); DBG(1, "probe_scanner: Probing %s\n", sanei_hp_scsi_devicename (scsi)); if (last_device != NULL) /* Look if we already probed the device */ { if (strcmp (last_device, sanei_hp_scsi_devicename (scsi)) == 0) { DBG(3, "probe_scanner: use cached compatibility flags\n"); *compat = last_compat; if (model_num) *model_num = last_model_num; if (model_name) *model_name = last_model_name; return SANE_STATUS_GOOD; } sanei_hp_free (last_device); last_device = NULL; } *compat = 0; last_model_num = -1; last_model_name = "Model Unknown"; for (i = 0; i < (int)(sizeof(probes)/sizeof(probes[0])); i++) { DBG(1,"probing %s\n",probes[i].model); if (!FAILED( status = sanei_hp_scl_upload(scsi, probes[i].cmd, buf, sizeof(buf)) )) { DBG(1, "probe_scanner: %s compatible (%5s)\n", probes[i].model, buf); last_model_name = probes[i].model; /* Some scanners have different responses */ if (probes[i].model_num == 9) { if (strncmp (buf, "5110A", 5) == 0) last_model_name = "ScanJet 5p"; else if (strncmp (buf, "5190A", 5) == 0) last_model_name = "ScanJet 5100C"; else if (strncmp (buf, "6290A", 5) == 0) last_model_name = "ScanJet 4100C"; } *compat |= probes[i].flag; last_model_num = probes[i].model_num; } else if (!UNSUPPORTED( status )) return status; /* SCL inquiry failed */ } /* Save values for next call */ last_device = sanei_hp_strdup (sanei_hp_scsi_devicename (scsi)); last_compat = *compat; if (model_num) *model_num = last_model_num; if (model_name) *model_name = last_model_name; return SANE_STATUS_GOOD; } SANE_Status sanei_hp_device_probe (enum hp_device_compat_e *compat, HpScsi scsi) { return sanei_hp_device_probe_model (compat, scsi, 0, 0); } hp_bool_t sanei_hp_device_compat (HpDevice this, enum hp_device_compat_e which) { return (this->compat & which) != 0; } static SANE_Status hp_nonscsi_device_new (HpDevice * newp, const char * devname, HpConnect connect) { HpDevice this; HpScsi scsi; SANE_Status status; const char * model_name = "ScanJet"; if (FAILED( sanei_hp_nonscsi_new(&scsi, devname, connect) )) { DBG(1, "%s: Can't open nonscsi device\n", devname); return SANE_STATUS_INVAL; /* Can't open device */ } /* reset scanner; returns all parameters to defaults */ if (FAILED( sanei_hp_scl_reset(scsi) )) { DBG(1, "hp_nonscsi_device_new: SCL reset failed\n"); sanei_hp_scsi_destroy(scsi,1); return SANE_STATUS_IO_ERROR; } /* Things seem okay, allocate new device */ this = sanei_hp_allocz(sizeof(*this)); this->data = sanei_hp_data_new(); if (!this || !this->data) return SANE_STATUS_NO_MEM; this->sanedev.name = sanei_hp_strdup(devname); if (!this->sanedev.name) return SANE_STATUS_NO_MEM; this->sanedev.vendor = "Hewlett-Packard"; this->sanedev.type = "flatbed scanner"; status = sanei_hp_device_probe_model (&(this->compat), scsi, 0, &model_name); if (!FAILED(status)) { sanei_hp_device_support_probe (scsi); status = sanei_hp_optset_new(&(this->options), scsi, this); } sanei_hp_scsi_destroy(scsi,1); if (!model_name) model_name = "ScanJet"; this->sanedev.model = sanei_hp_strdup (model_name); if (!this->sanedev.model) return SANE_STATUS_NO_MEM; if (FAILED(status)) { DBG(1, "hp_nonscsi_device_new: %s: probe failed (%s)\n", devname, sane_strstatus(status)); sanei_hp_data_destroy(this->data); sanei_hp_free((void *)this->sanedev.name); sanei_hp_free((void *)this->sanedev.model); sanei_hp_free(this); return status; } DBG(1, "hp_nonscsi_device_new: %s: found HP ScanJet model %s\n", devname, this->sanedev.model); *newp = this; return SANE_STATUS_GOOD; } SANE_Status sanei_hp_device_new (HpDevice * newp, const char * devname) { HpDevice this; HpScsi scsi; HpConnect connect; SANE_Status status; char * str; DBG(3, "sanei_hp_device_new: %s\n", devname); connect = sanei_hp_get_connect (devname); if ( connect != HP_CONNECT_SCSI ) return hp_nonscsi_device_new (newp, devname, connect); if (FAILED( sanei_hp_scsi_new(&scsi, devname) )) { DBG(1, "%s: Can't open scsi device\n", devname); return SANE_STATUS_INVAL; /* Can't open device */ } if (sanei_hp_scsi_inq(scsi)[0] != 0x03 || memcmp(sanei_hp_scsi_vendor(scsi), "HP ", 8) != 0) { DBG(1, "%s: does not seem to be an HP scanner\n", devname); sanei_hp_scsi_destroy(scsi,1); return SANE_STATUS_INVAL; } /* reset scanner; returns all parameters to defaults */ if (FAILED( sanei_hp_scl_reset(scsi) )) { DBG(1, "sanei_hp_device_new: SCL reset failed\n"); sanei_hp_scsi_destroy(scsi,1); return SANE_STATUS_IO_ERROR; } /* Things seem okay, allocate new device */ this = sanei_hp_allocz(sizeof(*this)); this->data = sanei_hp_data_new(); if (!this || !this->data) return SANE_STATUS_NO_MEM; this->sanedev.name = sanei_hp_strdup(devname); str = sanei_hp_strdup(sanei_hp_scsi_model(scsi)); if (!this->sanedev.name || !str) return SANE_STATUS_NO_MEM; this->sanedev.model = str; if ((str = strchr(str, ' ')) != 0) *str = '\0'; this->sanedev.vendor = "Hewlett-Packard"; this->sanedev.type = "flatbed scanner"; status = sanei_hp_device_probe(&(this->compat), scsi); if (!FAILED(status)) { sanei_hp_device_support_probe (scsi); status = sanei_hp_optset_new(&this->options, scsi, this); } sanei_hp_scsi_destroy(scsi,1); if (FAILED(status)) { DBG(1, "sanei_hp_device_new: %s: probe failed (%s)\n", devname, sane_strstatus(status)); sanei_hp_data_destroy(this->data); sanei_hp_free((void *)this->sanedev.name); sanei_hp_free((void *)this->sanedev.model); sanei_hp_free(this); return status; } DBG(1, "sanei_hp_device_new: %s: found HP ScanJet model %s\n", devname, this->sanedev.model); *newp = this; return SANE_STATUS_GOOD; } const SANE_Device * sanei_hp_device_sanedevice (HpDevice this) { return &this->sanedev; } backends-1.3.0/backend/hp-device.h000066400000000000000000000072421456256263500167140ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1997 Geoffrey T. Dairiki 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, see . 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 is part of a SANE backend for HP Scanners supporting HP Scanner Control Language (SCL). */ #ifndef HP_DEVICE_INCLUDED #define HP_DEVICE_INCLUDED #include "hp.h" enum hp_device_compat_e { HP_COMPAT_PLUS = 1 << 0, /* HP ScanJet Plus */ HP_COMPAT_2C = 1 << 1, HP_COMPAT_2P = 1 << 2, HP_COMPAT_2CX = 1 << 3, HP_COMPAT_4C = 1 << 4, /* also 3c, 6100C */ HP_COMPAT_3P = 1 << 5, HP_COMPAT_4P = 1 << 6, HP_COMPAT_5P = 1 << 7, /* also 4100C, 5100C */ HP_COMPAT_5100C = 1 << 8, /* redundant with 5p */ HP_COMPAT_PS = 1 << 9, /* HP PhotoSmart Photo Scanner */ HP_COMPAT_OJ_1150C = 1 << 10, HP_COMPAT_OJ_1170C = 1 << 11, /* also later OfficeJets */ HP_COMPAT_6200C = 1 << 12, HP_COMPAT_5200C = 1 << 13, HP_COMPAT_6300C = 1 << 14 }; struct hp_device_s { HpData data; HpOptSet options; SANE_Device sanedev; enum hp_device_compat_e compat; }; SANE_Status sanei_hp_device_new (HpDevice * new, const char * devname); const SANE_Device * sanei_hp_device_sanedevice (HpDevice this); void sanei_hp_device_simulate_clear (const char *devname); SANE_Status sanei_hp_device_simulate_set (const char *devname, HpScl scl, int flag); SANE_Status sanei_hp_device_support_get (const char *devname, HpScl scl, int *minval, int *maxval); SANE_Status sanei_hp_device_support_probe (HpScsi scsi); SANE_Status sanei_hp_device_probe_model (enum hp_device_compat_e *compat, HpScsi scsi, int *model_num, const char **model_name); SANE_Status sanei_hp_device_probe (enum hp_device_compat_e *compat, HpScsi scsi); hp_bool_t sanei_hp_device_compat (HpDevice this, enum hp_device_compat_e c); #endif /* HP_DEVICE_INCLUDED */ backends-1.3.0/backend/hp-handle.c000066400000000000000000000526101456256263500167020ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1997 Geoffrey T. Dairiki 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, see . 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 is part of a SANE backend for HP Scanners supporting HP Scanner Control Language (SCL). */ /* #define STUBS extern int sanei_debug_hp; */ #define DEBUG_DECLARE_ONLY #include "../include/sane/config.h" #ifdef HAVE_UNISTD_H # include #endif #include #include #include "../include/lassert.h" #include #include #include #include "hp-handle.h" #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_thread.h" #include "hp-device.h" #include "hp-option.h" #include "hp-accessor.h" #include "hp-scsi.h" #include "hp-scl.h" struct hp_handle_s { HpData data; HpDevice dev; SANE_Parameters scan_params; SANE_Pid reader_pid; int child_forked; /* Flag if we used fork() or not */ size_t bytes_left; int pipe_read_fd; sigset_t sig_set; sig_atomic_t cancelled; /* These data are used by the child */ HpScsi scsi; HpProcessData procdata; int pipe_write_fd; }; static hp_bool_t hp_handle_isScanning (HpHandle this) { return this->reader_pid != 0; } /* * reader thread. Used when threads are used */ static int reader_thread (void *data) { struct hp_handle_s *this = (struct hp_handle_s *) data; struct SIGACTION act; SANE_Status status; DBG (1, "reader_thread: thread started\n" " parameters: scsi = 0x%08lx, pipe_write_fd = %d\n", (long) this->scsi, this->pipe_write_fd); memset(&act, 0, sizeof(act)); sigaction(SIGTERM, &act, 0); DBG (1, "Starting sanei_hp_scsi_pipeout()\n"); status = sanei_hp_scsi_pipeout (this->scsi, this->pipe_write_fd, &(this->procdata)); DBG (1, "sanei_hp_scsi_pipeout finished with %s\n", sane_strstatus (status)); close (this->pipe_write_fd); this->pipe_write_fd = -1; sanei_hp_scsi_destroy (this->scsi, 0); return status; } /* * reader process. Used when forking child. */ static int reader_process (void *data) { struct hp_handle_s *this = (struct hp_handle_s *) data; struct SIGACTION sa; SANE_Status status; /* Here we are in a forked child. The thread will not come up to here. */ /* Forked child must close read end of pipe */ close (this->pipe_read_fd); this->pipe_read_fd = -1; memset(&sa, 0, sizeof(sa)); sa.sa_handler = SIG_DFL; sigaction(SIGTERM, &sa, 0); sigdelset(&(this->sig_set), SIGTERM); sigprocmask(SIG_SETMASK, &(this->sig_set), 0); /* not closing writing end of pipe gives an infinite loop on Digital UNIX */ status = sanei_hp_scsi_pipeout (this->scsi, this->pipe_write_fd, &(this->procdata)); close (this->pipe_write_fd); this->pipe_write_fd = -1; DBG(3,"reader_process: Exiting child (%s)\n",sane_strstatus(status)); return (status); } static SANE_Status hp_handle_startReader (HpHandle this, HpScsi scsi) { int fds[2]; sigset_t old_set; assert(this->reader_pid == 0); this->cancelled = 0; this->pipe_write_fd = this->pipe_read_fd = -1; if (pipe(fds)) return SANE_STATUS_IO_ERROR; sigfillset(&(this->sig_set)); sigprocmask(SIG_BLOCK, &(this->sig_set), &old_set); this->scsi = scsi; this->pipe_write_fd = fds[1]; this->pipe_read_fd = fds[0]; /* Will child be forked ? */ this->child_forked = sanei_thread_is_forked (); /* Start a thread or fork a child. None of them will return here. */ /* Returning means to be in the parent or thread/fork failed */ this->reader_pid = sanei_thread_begin (this->child_forked ? reader_process : reader_thread, (void *) this); if (this->reader_pid != 0) { /* Here we are in the parent */ sigprocmask(SIG_SETMASK, &old_set, 0); if ( this->child_forked ) { /* After fork(), parent must close writing end of pipe */ DBG(3, "hp_handle_startReader: parent closes write end of pipe\n"); close (this->pipe_write_fd); this->pipe_write_fd = -1; } if (!sanei_thread_is_valid (this->reader_pid)) { if ( !this->child_forked ) { close (this->pipe_write_fd); this->pipe_write_fd = -1; } close (this->pipe_read_fd); this->pipe_read_fd = -1; DBG(1, "hp_handle_startReader: fork() failed\n"); return SANE_STATUS_IO_ERROR; } DBG(1, "start_reader: reader process %ld started\n", (long) this->reader_pid); return SANE_STATUS_GOOD; } DBG(3, "Unexpected return from sanei_thread_begin()\n"); return SANE_STATUS_INVAL; } static SANE_Status hp_handle_stopScan (HpHandle this) { HpScsi scsi; this->cancelled = 0; this->bytes_left = 0; if (this->reader_pid) { int info; DBG(3, "hp_handle_stopScan: killing child (%ld)\n", (long) this->reader_pid); sanei_thread_kill (this->reader_pid); sanei_thread_waitpid(this->reader_pid, &info); DBG(1, "hp_handle_stopScan: child %s = %d\n", WIFEXITED(info) ? "exited, status" : "signalled, signal", WIFEXITED(info) ? WEXITSTATUS(info) : WTERMSIG(info)); close(this->pipe_read_fd); this->reader_pid = 0; if ( !FAILED( sanei_hp_scsi_new(&scsi, this->dev->sanedev.name)) ) { if (WIFSIGNALED(info)) { /* sanei_hp_scl_set(scsi, SCL_CLEAR_ERRORS, 0); sanei_hp_scl_errcheck(scsi); */ sanei_hp_scl_reset(scsi); } sanei_hp_scsi_destroy(scsi,0); } } else { DBG(3, "hp_handle_stopScan: no pid for child\n"); } return SANE_STATUS_GOOD; } static SANE_Status hp_handle_uploadParameters (HpHandle this, HpScsi scsi, int *scan_depth, hp_bool_t *soft_invert, hp_bool_t *out8) { SANE_Parameters * p = &this->scan_params; int data_width; enum hp_device_compat_e compat; assert(scsi); *soft_invert = 0; *out8 = 0; p->last_frame = SANE_TRUE; /* inquire resulting size of image after setting it up */ RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_PIXELS_PER_LINE, &p->pixels_per_line,0,0) ); RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_BYTES_PER_LINE, &p->bytes_per_line,0,0) ); RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_NUMBER_OF_LINES, &p->lines,0,0)); RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_DATA_WIDTH, &data_width,0,0)); switch (sanei_hp_optset_scanmode(this->dev->options, this->data)) { case HP_SCANMODE_LINEART: /* Lineart */ case HP_SCANMODE_HALFTONE: /* Halftone */ p->format = SANE_FRAME_GRAY; p->depth = 1; *scan_depth = 1; /* The OfficeJets don't seem to handle SCL_INVERSE_IMAGE, so we'll * have to invert in software. */ if ((sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD) && (compat & HP_COMPAT_OJ_1150C)) { *soft_invert=1; } break; case HP_SCANMODE_GRAYSCALE: /* Grayscale */ p->format = SANE_FRAME_GRAY; p->depth = (data_width > 8) ? 16 : 8; *scan_depth = data_width; /* 8 bit output forced ? */ if ( *scan_depth > 8 ) { *out8 = sanei_hp_optset_output_8bit (this->dev->options, this->data); DBG(1,"hp_handle_uploadParameters: out8=%d\n", (int)*out8); if (*out8) { p->depth = 8; p->bytes_per_line /= 2; } } break; case HP_SCANMODE_COLOR: /* RGB */ p->format = SANE_FRAME_RGB; p->depth = (data_width > 24) ? 16 : 8; *scan_depth = data_width / 3; /* 8 bit output forced ? */ if ( *scan_depth > 8 ) { *out8 = sanei_hp_optset_output_8bit (this->dev->options, this->data); DBG(1,"hp_handle_uploadParameters: out8=%d\n", (int)*out8); if (*out8) { p->depth = 8; p->bytes_per_line /= 2; } } /* HP PhotoSmart does not invert when depth > 8. Lets do it by software */ if ( (*scan_depth > 8) && (sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD) && (compat & HP_COMPAT_PS) ) *soft_invert = 1; DBG(1, "hp_handle_uploadParameters: data width %d\n", data_width); break; default: assert(!"Aack"); return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } HpHandle sanei_hp_handle_new (HpDevice dev) { HpHandle new = sanei_hp_allocz(sizeof(*new)); if (!new) return 0; if (!(new->data = sanei_hp_data_dup(dev->data))) { sanei_hp_free(new); return 0; } new->dev = dev; return new; } void sanei_hp_handle_destroy (HpHandle this) { HpScsi scsi=0; DBG(3,"sanei_hp_handle_destroy: stop scan\n"); hp_handle_stopScan(this); if (sanei_hp_scsi_new(&scsi,this->dev->sanedev.name)==SANE_STATUS_GOOD && scsi) { sanei_hp_scsi_destroy(scsi,1); } sanei_hp_data_destroy(this->data); sanei_hp_free(this); } const SANE_Option_Descriptor * sanei_hp_handle_saneoption (HpHandle this, SANE_Int optnum) { if (this->cancelled) { DBG(1, "sanei_hp_handle_saneoption: cancelled. Stop scan\n"); hp_handle_stopScan(this); } return sanei_hp_optset_saneoption(this->dev->options, this->data, optnum); } SANE_Status sanei_hp_handle_control(HpHandle this, SANE_Int optnum, SANE_Action action, void *valp, SANE_Int *info) { SANE_Status status; HpScsi scsi; hp_bool_t immediate; if (this->cancelled) { DBG(1, "sanei_hp_handle_control: cancelled. Stop scan\n"); RETURN_IF_FAIL( hp_handle_stopScan(this) ); } if (hp_handle_isScanning(this)) return SANE_STATUS_DEVICE_BUSY; RETURN_IF_FAIL( sanei_hp_scsi_new(&scsi, this->dev->sanedev.name) ); immediate = sanei_hp_optset_isImmediate(this->dev->options, optnum); status = sanei_hp_optset_control(this->dev->options, this->data, optnum, action, valp, info, scsi, immediate); sanei_hp_scsi_destroy ( scsi,0 ); return status; } SANE_Status sanei_hp_handle_getParameters (HpHandle this, SANE_Parameters *params) { SANE_Status status; if (!params) return SANE_STATUS_GOOD; if (this->cancelled) { DBG(1, "sanei_hp_handle_getParameters: cancelled. Stop scan\n"); RETURN_IF_FAIL( hp_handle_stopScan(this) ); } if (hp_handle_isScanning(this)) { *params = this->scan_params; return SANE_STATUS_GOOD; } status = sanei_hp_optset_guessParameters(this->dev->options, this->data, params); #ifdef INQUIRE_AFTER_SCAN /* Photosmart: this gives the correct number of lines when doing an update of the SANE parameters right after a preview */ if (!strcmp("C5100A", this->dev->sanedev.model)) { HpScsi scsi; SANE_Parameters * p = &this->scan_params; if (!FAILED( sanei_hp_scsi_new(&scsi, this->dev->sanedev.name) )) { RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_NUMBER_OF_LINES, &p->lines,0,0)); sanei_hp_scsi_destroy(scsi,0); *params = this->scan_params; } } #endif return status; } SANE_Status sanei_hp_handle_startScan (HpHandle this) { SANE_Status status; HpScsi scsi; HpScl scl; HpProcessData *procdata = &(this->procdata); int adfscan; /* FIXME: setup preview mode stuff? */ if (hp_handle_isScanning(this)) { DBG(3,"sanei_hp_handle_startScan: Stop current scan\n"); RETURN_IF_FAIL( hp_handle_stopScan(this) ); } RETURN_IF_FAIL( sanei_hp_scsi_new(&scsi, this->dev->sanedev.name) ); status = sanei_hp_optset_download(this->dev->options, this->data, scsi); if (!FAILED(status)) status = hp_handle_uploadParameters(this, scsi, &(procdata->bits_per_channel), &(procdata->invert), &(procdata->out8)); if (FAILED(status)) { sanei_hp_scsi_destroy(scsi,0); return status; } procdata->mirror_vertical = sanei_hp_optset_mirror_vert (this->dev->options, this->data, scsi); DBG(1, "start: %s to mirror image vertically\n", procdata->mirror_vertical ? "Request" : "No request" ); scl = sanei_hp_optset_scan_type (this->dev->options, this->data); adfscan = (scl == SCL_ADF_SCAN); /* For ADF scan we should check if there is paper available */ if ( adfscan ) {int adfstat = 0; int can_check_paper = 0; int is_flatbed = 0; int minval, maxval; /* For ADF-support, we have three different types of scanners: * ScanJet, ScanJet+, IIp, 3p: * scroll feed, no support for inquire paper in ADF, unload document * and preload document * IIc, IIcx, 3c, 4c, 6100C, 4p: * flatbed, no support for preload document * 5100C, 5200C, 6200C, 6300C: * scroll feed. * For all scroll feed types, we use the usual scan window command. * For flatbed types, use a sequence of special commands. */ /* Check the IIp group */ if ( (sanei_hp_device_support_get (this->dev->sanedev.name, SCL_UNLOAD, &minval, &maxval) != SANE_STATUS_GOOD ) && (sanei_hp_device_support_get (this->dev->sanedev.name, SCL_CHANGE_DOC, &minval, &maxval) != SANE_STATUS_GOOD ) ) { DBG(3, "start: Request for ADF scan without support of unload doc\n"); DBG(3, " and change doc. Seems to be something like a IIp.\n"); DBG(3, " Use standard scan window command.\n"); scl = SCL_START_SCAN; can_check_paper = 0; is_flatbed = 0; } /* else if ( sanei_hp_device_support_get (this->dev->sanedev.name, SCL_PRELOAD_ADF, &minval, &maxval) != SANE_STATUS_GOOD ) */ else if ( sanei_hp_is_flatbed_adf (scsi) ) { DBG(3, "start: Request for ADF scan without support of preload doc.\n"); DBG(3, " Seems to be a flatbed ADF.\n"); DBG(3, " Use ADF scan window command.\n"); can_check_paper = 1; is_flatbed = 1; } else { DBG(3, "start: Request for ADF scan with support of preload doc.\n"); DBG(3, " Seems to be a scroll feed ADF.\n"); DBG(3, " Use standard scan window command.\n"); scl = SCL_START_SCAN; can_check_paper = 1; is_flatbed = 0; } /* Check if the ADF is ready */ if ( sanei_hp_scl_inquire(scsi, SCL_ADF_READY, &adfstat, 0, 0) != SANE_STATUS_GOOD ) { DBG(1, "start: Error checking if ADF is ready\n"); sanei_hp_scsi_destroy(scsi,0); return SANE_STATUS_UNSUPPORTED; } if ( adfstat != 1 ) { DBG(1, "start: ADF is not ready. Finished.\n"); sanei_hp_scsi_destroy(scsi,0); return SANE_STATUS_NO_DOCS; } /* Check paper in ADF */ if ( can_check_paper ) { if ( sanei_hp_scl_inquire(scsi, SCL_ADF_BIN, &adfstat, 0, 0) != SANE_STATUS_GOOD ) { DBG(1, "start: Error checking if paper in ADF\n"); sanei_hp_scsi_destroy(scsi,0); return SANE_STATUS_UNSUPPORTED; } if ( adfstat != 1 ) { DBG(1, "start: No paper in ADF bin. Finished.\n"); sanei_hp_scsi_destroy(scsi,0); return SANE_STATUS_NO_DOCS; } if ( is_flatbed ) { if ( sanei_hp_scl_set(scsi, SCL_CHANGE_DOC, 0) != SANE_STATUS_GOOD ) { DBG(1, "start: Error changing document\n"); sanei_hp_scsi_destroy(scsi,0); return SANE_STATUS_UNSUPPORTED; } } } } DBG(1, "start: %s to mirror image vertically\n", procdata->mirror_vertical ? "Request" : "No request" ); this->bytes_left = ( this->scan_params.bytes_per_line * this->scan_params.lines ); DBG(1, "start: %d pixels per line, %d bytes per line, %d lines high\n", this->scan_params.pixels_per_line, this->scan_params.bytes_per_line, this->scan_params.lines); procdata->bytes_per_line = (int)this->scan_params.bytes_per_line; if (procdata->out8) { procdata->bytes_per_line *= 2; DBG(1,"(scanner will send %d bytes per line, 8 bit output forced)\n", procdata->bytes_per_line); } procdata->lines = this->scan_params.lines; /* Wait for front-panel button push ? */ status = sanei_hp_optset_start_wait(this->dev->options, this->data); if (status) /* Wait for front button push ? Start scan in reader process */ { procdata->startscan = scl; status = SANE_STATUS_GOOD; } else { procdata->startscan = 0; status = sanei_hp_scl_startScan(scsi, scl); } if (!FAILED( status )) { status = hp_handle_startReader(this, scsi); } /* Close SCSI-connection in forked environment */ if (this->child_forked) sanei_hp_scsi_destroy(scsi,0); return status; } SANE_Status sanei_hp_handle_read (HpHandle this, void * buf, size_t *lengthp) { ssize_t nread; SANE_Status status; DBG(3, "sanei_hp_handle_read: trying to read %lu bytes\n", (unsigned long) *lengthp); if (!hp_handle_isScanning(this)) { DBG(1, "sanei_hp_handle_read: not scanning\n"); return SANE_STATUS_INVAL; } if (this->cancelled) { DBG(1, "sanei_hp_handle_read: cancelled. Stop scan\n"); RETURN_IF_FAIL( hp_handle_stopScan(this) ); return SANE_STATUS_CANCELLED; } if (*lengthp == 0) return SANE_STATUS_GOOD; if (*lengthp > this->bytes_left) *lengthp = this->bytes_left; if ((nread = read(this->pipe_read_fd, buf, *lengthp)) < 0) { *lengthp = 0; if (errno == EAGAIN) return SANE_STATUS_GOOD; DBG(1, "sanei_hp_handle_read: read from pipe: %s. Stop scan\n", strerror(errno)); hp_handle_stopScan(this); return SANE_STATUS_IO_ERROR; } this->bytes_left -= (*lengthp = nread); if (nread > 0) { DBG(3, "sanei_hp_handle_read: read %lu bytes\n", (unsigned long) nread); return SANE_STATUS_GOOD; } DBG(1, "sanei_hp_handle_read: EOF from pipe. Stop scan\n"); status = this->bytes_left ? SANE_STATUS_IO_ERROR : SANE_STATUS_EOF; RETURN_IF_FAIL( hp_handle_stopScan(this) ); /* Switch off lamp and check unload after scan */ if (status == SANE_STATUS_EOF) { const HpDeviceInfo *hpinfo; HpScsi scsi; if ( sanei_hp_scsi_new(&scsi, this->dev->sanedev.name) == SANE_STATUS_GOOD ) { hpinfo = sanei_hp_device_info_get ( this->dev->sanedev.name ); if ( hpinfo ) { if ( hpinfo->unload_after_scan ) sanei_hp_scl_set(scsi, SCL_UNLOAD, 0); } sanei_hp_scsi_destroy(scsi,0); } } return status; } void sanei_hp_handle_cancel (HpHandle this) { this->cancelled = 1; /* The OfficeJet K series may not deliver enough data. */ /* Therefore the read might not return until it is interrupted. */ DBG(3,"sanei_hp_handle_cancel: compat flags: 0x%04x\n", (int)this->dev->compat); if ( (this->reader_pid) && (this->dev->compat & HP_COMPAT_OJ_1150C) ) { DBG(3,"sanei_hp_handle_cancel: send SIGTERM to child (%ld)\n", (long) this->reader_pid); sanei_thread_kill(this->reader_pid); } } SANE_Status sanei_hp_handle_setNonblocking (HpHandle this, hp_bool_t non_blocking) { if (!hp_handle_isScanning(this)) return SANE_STATUS_INVAL; if (this->cancelled) { DBG(3,"sanei_hp_handle_setNonblocking: cancelled. Stop scan\n"); RETURN_IF_FAIL( hp_handle_stopScan(this) ); return SANE_STATUS_CANCELLED; } if (fcntl(this->pipe_read_fd, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0) return SANE_STATUS_IO_ERROR; return SANE_STATUS_GOOD; } SANE_Status sanei_hp_handle_getPipefd (HpHandle this, SANE_Int *fd) { if (! hp_handle_isScanning(this)) return SANE_STATUS_INVAL; if (this->cancelled) { DBG(3,"sanei_hp_handle_getPipefd: cancelled. Stop scan\n"); RETURN_IF_FAIL( hp_handle_stopScan(this) ); return SANE_STATUS_CANCELLED; } *fd = this->pipe_read_fd; return SANE_STATUS_GOOD; } backends-1.3.0/backend/hp-handle.h000066400000000000000000000053661456256263500167150ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1997 Geoffrey T. Dairiki 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, see . 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 is part of a SANE backend for HP Scanners supporting HP Scanner Control Language (SCL). */ #ifndef HP_HANDLE_INCLUDED #define HP_HANDLE_INCLUDED #include "hp.h" HpHandle sanei_hp_handle_new (HpDevice dev); void sanei_hp_handle_destroy (HpHandle this); const SANE_Option_Descriptor * sanei_hp_handle_saneoption (HpHandle this, SANE_Int optnum); SANE_Status sanei_hp_handle_control(HpHandle this, SANE_Int optnum, SANE_Action action, void *valp, SANE_Int *info); SANE_Status sanei_hp_handle_getParameters (HpHandle this, SANE_Parameters *params); SANE_Status sanei_hp_handle_startScan (HpHandle this); SANE_Status sanei_hp_handle_read (HpHandle this, void * buf, size_t *lengthp); void sanei_hp_handle_cancel (HpHandle this); SANE_Status sanei_hp_handle_setNonblocking (HpHandle this, hp_bool_t non_blocking); SANE_Status sanei_hp_handle_getPipefd (HpHandle this, SANE_Int *fd); #endif /* HP_HANDLE_INCLUDED */ backends-1.3.0/backend/hp-hpmem.c000066400000000000000000000073501456256263500165560ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1997 Geoffrey T. Dairiki 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, see . 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 is part of a SANE backend for HP Scanners supporting HP Scanner Control Language (SCL). */ /* #define STUBS extern int sanei_debug_hp;*/ #define DEBUG_DECLARE_ONLY #include "../include/sane/config.h" #include #include #include "../include/lassert.h" #include "hp.h" typedef struct hp_alloc_s Alloc; struct hp_alloc_s { Alloc * prev; Alloc * next; hp_byte_t buf[1]; }; static Alloc head[] = {{ head, head, {0} }}; #define DATA_OFFSET (head->buf - (hp_byte_t *)head) #define VOID_TO_ALLOCP(p) ((Alloc *)((hp_byte_t *)(p) - DATA_OFFSET)) #define ALLOCSIZE(sz) (sz + DATA_OFFSET) void * sanei_hp_alloc (size_t sz) { Alloc * new = malloc(ALLOCSIZE(sz)); if (!new) return 0; (new->next = head->next)->prev = new; (new->prev = head)->next = new; return new->buf; } void * sanei_hp_allocz (size_t sz) { void * new = sanei_hp_alloc(sz); if (!new) return 0; memset(new, 0, sz); return new; } void * sanei_hp_memdup (const void * src, size_t sz) { char * new = sanei_hp_alloc(sz); if (!new) return 0; return memcpy(new, src, sz); } char * sanei_hp_strdup (const char * str) { return sanei_hp_memdup(str, strlen(str) + 1); } void * sanei_hp_realloc (void * ptr, size_t sz) { if (ptr) { Alloc * old = VOID_TO_ALLOCP(ptr); Alloc copy = *old; Alloc * new = realloc(old, ALLOCSIZE(sz)); if (!new) return 0; if (new != old) (new->prev = copy.prev)->next = (new->next = copy.next)->prev = new; return new->buf; } else return sanei_hp_alloc(sz); } void sanei_hp_free (void * ptr) { Alloc * old = VOID_TO_ALLOCP(ptr); assert(old && old != head); (old->next->prev = old->prev)->next = old->next; old->next = old->prev = 0; /* so we can puke on multiple free's */ free(old); } void sanei_hp_free_all (void) { Alloc * ptr; Alloc * next; for (ptr = head->next; ptr != head; ptr = next) { next = ptr->next; free(ptr); } head->next = head->prev = head; } backends-1.3.0/backend/hp-option.c000066400000000000000000003617771456256263500170000ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1997 Geoffrey T. Dairiki 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, see . 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 is part of a SANE backend for HP Scanners supporting HP Scanner Control Language (SCL). */ /* Revision 1.13 2005/04/13 12:50:07 ellert-guest Add missing SANE_I18N, Regenerate .po files accordingly, Update Swedish translations Revision 1.12 2003/10/09 19:32:50 kig-guest Bug #300241: fix inverse image on 3c/4c/6100C at 10 bit depth */ /* pwd.h not available ? */ #if (defined(__IBMC__) || defined(__IBMCPP__)) #ifndef _AIX # define SANE_HOME_HP "SANE_HOME_HP" #endif #endif /* To be done: don't reallocate choice accessors */ /* #define HP_ALLOC_CHOICEACC_ONCE 1 */ /* #define HP_EXPERIMENTAL */ /* #define STUBS extern int sanei_debug_hp; */ #define DEBUG_DECLARE_ONLY #include "../include/sane/config.h" #include "../include/sane/sanei_backend.h" #include "../include/lalloca.h" #include #include #include "../include/lassert.h" #ifndef SANE_HOME_HP #include #endif #include #include #include #include #include "../include/sane/saneopts.h" #include "../include/sane/sanei.h" #include "hp.h" #include "hp-option.h" #include "hp-accessor.h" #include "hp-scsi.h" #include "hp-scl.h" #include "hp-device.h" /* FIXME: descriptors should be static? */ typedef SANE_Option_Descriptor * _HpSaneOption; typedef struct hp_option_descriptor_s * _HpOptionDescriptor; typedef struct hp_option_s * _HpOption; typedef struct hp_data_info_s * HpDataInfo; typedef struct hp_data_info_s * _HpDataInfo; typedef HpAccessor HpAccessorOptd; static hp_bool_t hp_optset_isEnabled (HpOptSet this, HpData data, const char *name, const HpDeviceInfo *info); static HpOption hp_optset_get (HpOptSet this, HpOptionDescriptor optd); static HpOption hp_optset_getByName (HpOptSet this, const char * name); static SANE_Status hp_download_calib_file (HpScsi scsi); static SANE_Status hp_probe_parameter_support_table (enum hp_device_compat_e compat, HpScl scl, int value); #define HP_EOL -9999 /* Don't need requiries for commands that are probed */ #define HP_PROBE_SCL_COMMAND 1 /* Scale factor for vectors (gtk seems not to like vectors/curves * in y-range 0.0,...,1.0) */ #define HP_VECTOR_SCALE (256.0) /* * */ struct hp_option_s { HpOptionDescriptor descriptor; HpAccessorOptd optd_acsr; HpAccessor data_acsr; void * extra; }; struct hp_option_descriptor_s { const char * name; const char * title; const char * desc; SANE_Value_Type type; SANE_Unit unit; SANE_Int cap; enum hp_device_compat_e requires; /* model dependent support flags */ /* probe for option support */ SANE_Status (*probe) (_HpOption this, HpScsi scsi, HpOptSet optset, HpData data); SANE_Status (*program) (HpOption this, HpScsi scsi, HpOptSet optset, HpData data); hp_bool_t (*enable) (HpOption this, HpOptSet optset, HpData data, const HpDeviceInfo *info); hp_bool_t has_global_effect; hp_bool_t affects_scan_params; hp_bool_t program_immediate; hp_bool_t suppress_for_scan; hp_bool_t may_change; /* This stuff should really be in a subclasses: */ HpScl scl_command; int minval, maxval, startval; /* for simulation */ HpChoice choices; }; struct hp_data_info_s { HpScl scl; }; static const struct hp_option_descriptor_s NUM_OPTIONS[1], PREVIEW_MODE[1], SCAN_MODE[1], SCAN_RESOLUTION[1], CUSTOM_GAMMA[1], GAMMA_VECTOR_8x8[1], #ifdef ENABLE_7x12_TONEMAPS GAMMA_VECTOR_7x12[1], RGB_TONEMAP[1], GAMMA_VECTOR_R[1], GAMMA_VECTOR_G[1], GAMMA_VECTOR_B[1], #endif HALFTONE_PATTERN[1], MEDIA[1], OUT8[1], BIT_DEPTH[1], SCAN_SOURCE[1], #ifdef FAKE_COLORSEP_MATRIXES SEPMATRIX[1], #endif MATRIX_TYPE[1]; /* Check if a certain scanner model supports a command with a given parameter * value. The function returns SANE_STATUS_GOOD if the command and the * value is found in the support table of that scanner. * It returns SANE_STATUS_UNSUPPORTED if the command is found in the support * table of that scanner, but the value is not included in the table. * It returns SANE_STATUS_EOF if there is no information about that command * and that scanner in the support table. */ static SANE_Status hp_probe_parameter_support_table (enum hp_device_compat_e compat, HpScl scl, int value) {int k, j; char *eptr; static int photosmart_output_type[] = /* HP Photosmart: only b/w, gray, color is supported */ { HP_COMPAT_PS, SCL_OUTPUT_DATA_TYPE, 0, 4, 5, HP_EOL }; static int *support_table[] = { photosmart_output_type }; eptr = getenv ("SANE_HP_CHK_TABLE"); if ((eptr != NULL) && (*eptr == '0')) return SANE_STATUS_EOF; for (k = 0; k < (int)(sizeof (support_table)/sizeof (support_table[0])); k++) { if ((scl == support_table[k][1]) && (support_table[k][0] & compat)) { for (j = 2; support_table[k][j] != HP_EOL; j++) if (support_table[k][j] == value) return SANE_STATUS_GOOD; return SANE_STATUS_UNSUPPORTED; } } return SANE_STATUS_EOF; } /* * class HpChoice */ typedef struct hp_choice_s * _HpChoice; static hp_bool_t hp_choice_isSupported (HpChoice choice, int minval, int maxval) { return ( choice->is_emulated || ( choice->val >= minval && choice->val <= maxval ) ); } static hp_bool_t hp_probed_choice_isSupported (HpScsi scsi, HpScl scl, HpChoice choice, int minval, int maxval) { hp_bool_t isSupported; SANE_Status status; enum hp_device_compat_e compat; if ( choice->is_emulated ) { DBG(3, "probed_choice: value %d is emulated\n", choice->val); return ( 1 ); } if ( choice->val < minval || choice->val > maxval ) { DBG(3, "probed_choice: value %d out of range (%d,%d)\n", choice->val, minval, maxval); return ( 0 ); } if (sanei_hp_device_probe (&compat, scsi) != SANE_STATUS_GOOD) { DBG(1, "probed_choice: Could not get compatibilities for scanner\n"); return ( 0 ); } status = hp_probe_parameter_support_table (compat, scl, choice->val); if (status == SANE_STATUS_GOOD) { DBG(3, "probed_choice: command/value found in support table\n"); return ( 1 ); } else if (status == SANE_STATUS_UNSUPPORTED) { DBG(3, "probed_choice: command found in support table, but value n.s.\n"); return ( 0 ); } /* Not in the support table. Try to inquire */ /* Fix me: It seems that the scanner does not raise a parameter error */ /* after specifying an unsupported command-value. */ sanei_hp_scl_clearErrors (scsi); sanei_hp_scl_set (scsi, scl, choice->val); isSupported = ( sanei_hp_scl_errcheck (scsi) == SANE_STATUS_GOOD ); DBG(3, "probed_choice: value %d %s\n", choice->val, isSupported ? "supported" : "not supported"); return isSupported; } hp_bool_t sanei_hp_choice_isEnabled (HpChoice this, HpOptSet optset, HpData data, const HpDeviceInfo *info) { if (!this->enable) return 1; return (*this->enable)(this, optset, data, info); } static hp_bool_t _cenable_incolor (HpChoice __sane_unused__ this, HpOptSet optset, HpData data, const HpDeviceInfo __sane_unused__ *info) { return sanei_hp_optset_scanmode(optset, data) == HP_SCANMODE_COLOR; } static hp_bool_t _cenable_notcolor (HpChoice __sane_unused__ this, HpOptSet optset, HpData data, const HpDeviceInfo __sane_unused__ *info) { return sanei_hp_optset_scanmode(optset, data) != HP_SCANMODE_COLOR; } /* * class HpAccessorOptd */ static HpAccessorOptd hp_accessor_optd_new (HpData data) { return sanei_hp_accessor_new(data, sizeof(SANE_Option_Descriptor)); } static _HpSaneOption hp_accessor_optd_data (HpAccessorOptd this, HpData data) { return sanei__hp_accessor_data(this, data); } /* * class OptionDescriptor */ static SANE_Status hp_option_descriptor_probe (HpOptionDescriptor desc, HpScsi scsi, HpOptSet optset, HpData data, HpOption * newoptp) { _HpOption new; SANE_Status status; _HpSaneOption optd; new = sanei_hp_alloc(sizeof(*new)); new->descriptor = desc; if (!(new->optd_acsr = hp_accessor_optd_new(data))) return SANE_STATUS_NO_MEM; new->data_acsr = 0; optd = hp_accessor_optd_data(new->optd_acsr, data); memset(optd, 0, sizeof(*optd)); optd->name = desc->name; optd->title = desc->title; optd->desc = desc->desc; optd->type = desc->type; optd->unit = desc->unit; optd->cap = desc->cap; /* * Probe function will set optd->size, optd->constraint_type, * and optd->constraint. * and also new->accessor * and possibly new->extra */ if (desc->probe) { if (FAILED( status = (*desc->probe)(new, scsi, optset, data) )) { /* hp_accessor_optd_destoy(new->optd_acsr) */ sanei_hp_free(new); return status; } } *newoptp = new; return SANE_STATUS_GOOD; } /* * class Option */ static HpSaneOption hp_option_saneoption (HpOption this, HpData data) { return hp_accessor_optd_data(this->optd_acsr, data); } static _HpSaneOption _hp_option_saneoption (HpOption this, HpData data) { return hp_accessor_optd_data(this->optd_acsr, data); } static SANE_Status hp_option_download (HpOption this, HpData data, HpOptSet optset, HpScsi scsi) { HpScl scl = this->descriptor->scl_command; int value; if (IS_SCL_CONTROL(scl)) { value = sanei_hp_accessor_getint(this->data_acsr, data); if ( (scl == SCL_DATA_WIDTH) && (sanei_hp_optset_scanmode (optset, data) == HP_SCANMODE_COLOR) ) { value *= 3; } return sanei_hp_scl_set(scsi, scl, value); } else if (IS_SCL_DATA_TYPE(scl)) return sanei_hp_scl_download(scsi, scl, sanei_hp_accessor_data(this->data_acsr, data), sanei_hp_accessor_size(this->data_acsr)); assert(!scl); return SANE_STATUS_INVAL; } static SANE_Status hp_option_upload (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { HpScl scl = this->descriptor->scl_command; int val; if (IS_SCL_CONTROL(scl)) { RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, &val, 0, 0) ); if ( (scl == SCL_DATA_WIDTH) && (sanei_hp_optset_scanmode (optset, data) == HP_SCANMODE_COLOR) ) { val /= 3; } sanei_hp_accessor_setint(this->data_acsr, data, val); return SANE_STATUS_GOOD; } else if (IS_SCL_DATA_TYPE(scl)) return sanei_hp_scl_upload(scsi, scl, sanei__hp_accessor_data(this->data_acsr, data), sanei_hp_accessor_size(this->data_acsr)); assert(!scl); return SANE_STATUS_INVAL; } static SANE_Status hp_option_program (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { const HpDeviceInfo *info; DBG(10, "hp_option_program: name=%s, enable=0x%08lx, program=0x%08lx\n", this->descriptor->name, (long)this->descriptor->enable, (long)this->descriptor->program); /* Replaced by flag suppress_for_scan * if (this->descriptor->program_immediate) * { * DBG(10, "hp_option_program: is program_immediate. Dont program now.\n"); * return SANE_STATUS_GOOD; * } */ if (!this->descriptor->program) return SANE_STATUS_GOOD; info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); if (this->descriptor->enable && !(*this->descriptor->enable)(this, optset, data, info)) return SANE_STATUS_GOOD; return (*this->descriptor->program)(this, scsi, optset, data); } static SANE_Status hp_option_get (HpOption this, HpData data, void * valp) { if (!this->data_acsr) return SANE_STATUS_INVAL; return sanei_hp_accessor_get(this->data_acsr, data, valp); } static hp_bool_t _values_are_equal (HpOption this, HpData data, const void * val1, const void * val2) { HpSaneOption optd = hp_option_saneoption(this, data); if (optd->type == SANE_TYPE_STRING) return strncmp((const char *)val1, (const char *)val2, optd->size) == 0; else return memcmp(val1, val2, optd->size) == 0; } static hp_bool_t hp_option_isImmediate (HpOption this) { return ( this->descriptor->program_immediate && this->descriptor->program ); } static SANE_Status hp_option_imm_set (HpOptSet optset, HpOption this, HpData data, void * valp, SANE_Int * info, HpScsi scsi) { HpSaneOption optd = hp_option_saneoption(this, data); hp_byte_t * old_val = alloca(optd->size); SANE_Status status; assert (this->descriptor->program_immediate && this->descriptor->program); if (!SANE_OPTION_IS_SETTABLE(optd->cap)) return SANE_STATUS_INVAL; DBG(10,"hp_option_imm_set: %s\n", this->descriptor->name); if ( this->descriptor->type == SANE_TYPE_BUTTON ) { status = (*this->descriptor->program)(this, scsi, optset, data); if ( !FAILED(status) && info ) { if (this->descriptor->has_global_effect) *info |= SANE_INFO_RELOAD_OPTIONS; if (this->descriptor->affects_scan_params) *info |= SANE_INFO_RELOAD_PARAMS; } return status; } if ( !this->data_acsr ) return SANE_STATUS_INVAL; if (!old_val) return SANE_STATUS_NO_MEM; if (FAILED( status = sanei_constrain_value(optd, valp, info) )) { DBG(1, "option_imm_set: %s: constrain_value failed :%s\n", this->descriptor->name, sane_strstatus(status)); return status; } RETURN_IF_FAIL( sanei_hp_accessor_get(this->data_acsr, data, old_val) ); if (_values_are_equal(this, data, old_val, valp)) { DBG(3, "option_imm_set: value unchanged\n"); return SANE_STATUS_GOOD; } if (info) memcpy(old_val, valp, optd->size); /* Save requested value */ RETURN_IF_FAIL( sanei_hp_accessor_set(this->data_acsr, data, valp) ); if ( this->descriptor->type == SANE_TYPE_STRING ) RETURN_IF_FAIL( (*this->descriptor->program)(this, scsi, optset, data) ); if (info) { if (!_values_are_equal(this, data, old_val, valp)) *info |= SANE_INFO_INEXACT; if (this->descriptor->has_global_effect) *info |= SANE_INFO_RELOAD_OPTIONS; if (this->descriptor->affects_scan_params) *info |= SANE_INFO_RELOAD_PARAMS; } return SANE_STATUS_GOOD; } static SANE_Status hp_option_set (HpOption this, HpData data, void * valp, SANE_Int * info) { HpSaneOption optd = hp_option_saneoption(this, data); hp_byte_t * old_val = alloca(optd->size); SANE_Status status; char sval[64]; if (!SANE_OPTION_IS_SETTABLE(optd->cap) || !this->data_acsr) return SANE_STATUS_INVAL; if (!old_val) return SANE_STATUS_NO_MEM; sval[0] = '\0'; if (this->descriptor->type == SANE_TYPE_INT) sprintf (sval," value=%d", *(int*)valp); DBG(10,"hp_option_set: %s%s\n", this->descriptor->name, sval); if (FAILED( status = sanei_constrain_value(optd, valp, info) )) { DBG(1, "option_set: %s: constrain_value failed :%s\n", this->descriptor->name, sane_strstatus(status)); return status; } RETURN_IF_FAIL( sanei_hp_accessor_get(this->data_acsr, data, old_val) ); if (_values_are_equal(this, data, old_val, valp)) { DBG(3, "option_set: %s: value unchanged\n",this->descriptor->name); return SANE_STATUS_GOOD; } if (info) memcpy(old_val, valp, optd->size); /* Save requested value */ RETURN_IF_FAIL( sanei_hp_accessor_set(this->data_acsr, data, valp) ); if (info) { if (!_values_are_equal(this, data, old_val, valp)) *info |= SANE_INFO_INEXACT; if (this->descriptor->has_global_effect) *info |= SANE_INFO_RELOAD_OPTIONS; if (this->descriptor->affects_scan_params) *info |= SANE_INFO_RELOAD_PARAMS; DBG(3, "option_set: %s: info=0x%lx\n",this->descriptor->name, (long)*info); } return SANE_STATUS_GOOD; } static int hp_option_getint (HpOption this, HpData data) { return sanei_hp_accessor_getint(this->data_acsr, data); } static SANE_Status hp_option_imm_control (HpOptSet optset, HpOption this, HpData data, SANE_Action action, void * valp, SANE_Int *infop, HpScsi scsi) { HpSaneOption optd = hp_option_saneoption(this, data); if (!SANE_OPTION_IS_ACTIVE(optd->cap)) return SANE_STATUS_INVAL; switch (action) { case SANE_ACTION_GET_VALUE: return hp_option_get(this, data, valp); case SANE_ACTION_SET_VALUE: return hp_option_imm_set(optset, this, data, valp, infop, scsi); case SANE_ACTION_SET_AUTO: default: return SANE_STATUS_INVAL; } } static SANE_Status hp_option_control (HpOption this, HpData data, SANE_Action action, void * valp, SANE_Int *infop) { HpSaneOption optd = hp_option_saneoption(this, data); if (!SANE_OPTION_IS_ACTIVE(optd->cap)) return SANE_STATUS_INVAL; switch (action) { case SANE_ACTION_GET_VALUE: return hp_option_get(this, data, valp); case SANE_ACTION_SET_VALUE: return hp_option_set(this, data, valp, infop); case SANE_ACTION_SET_AUTO: default: return SANE_STATUS_INVAL; } } static void hp_option_reprogram (HpOption this, HpOptSet optset, HpData data, HpScsi scsi) { if (this->descriptor->may_change) { DBG(5, "hp_option_reprogram: %s\n", this->descriptor->name); hp_option_program (this, scsi, optset, data); } } static void hp_option_reprobe (HpOption this, HpOptSet optset, HpData data, HpScsi scsi) { if (this->descriptor->may_change) { DBG(5, "hp_option_reprobe: %s\n", this->descriptor->name); (*this->descriptor->probe)((_HpOption)this, scsi, optset, data); } } static void hp_option_updateEnable (HpOption this, HpOptSet optset, HpData data, const HpDeviceInfo *info) { hp_bool_t (*f)(HpOption, HpOptSet, HpData, const HpDeviceInfo *) = this->descriptor->enable; _HpSaneOption optd = _hp_option_saneoption(this, data); if (!f || (*f)(this, optset, data, info)) optd->cap &= ~SANE_CAP_INACTIVE; else optd->cap |= SANE_CAP_INACTIVE; } static hp_bool_t hp_option_isInternal (HpOption this) { return this->descriptor->name[0] == '_'; } /* * Option probe functions */ static SANE_Status _set_range (HpOption opt, HpData data, SANE_Word min, SANE_Word quant, SANE_Word max) { _HpSaneOption optd = _hp_option_saneoption(opt, data); SANE_Range * range = sanei_hp_alloc(sizeof(*range)); /* FIXME: leak? */ if (! range) return SANE_STATUS_NO_MEM; range->min = min; range->max = max; range->quant = quant; optd->constraint.range = range; optd->constraint_type = SANE_CONSTRAINT_RANGE; return SANE_STATUS_GOOD; } static void _set_size (HpOption opt, HpData data, SANE_Int size) { _hp_option_saneoption(opt, data)->size = size; } /* #ifdef HP_EXPERIMENTAL */ static SANE_Status _probe_int (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { HpScl scl = this->descriptor->scl_command; int minval, maxval; int val = 0; assert(scl); RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, &val, &minval, &maxval) ); if (minval >= maxval) return SANE_STATUS_UNSUPPORTED; /* If we don't have an accessor, get one */ if (!this->data_acsr) { if (!(this->data_acsr = sanei_hp_accessor_int_new(data))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, val); _set_size(this, data, sizeof(SANE_Int)); return _set_range(this, data, minval, 1, maxval); } /* #endif */ static SANE_Status _probe_int_brightness (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { HpScl scl = this->descriptor->scl_command; int minval, maxval; int val = 0; hp_bool_t simulate; assert(scl); simulate = ( sanei_hp_device_support_get ( sanei_hp_scsi_devicename (scsi), scl, 0, 0) != SANE_STATUS_GOOD ); if ( simulate ) { val = this->descriptor->startval; minval = this->descriptor->minval; maxval = this->descriptor->maxval; } else { RETURN_IF_FAIL ( sanei_hp_scl_inquire(scsi,scl,&val,&minval,&maxval) ); } if (minval >= maxval) return SANE_STATUS_UNSUPPORTED; /* If we don't have an accessor, get one */ if (!this->data_acsr) { if (!(this->data_acsr = sanei_hp_accessor_int_new(data))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, val); _set_size(this, data, sizeof(SANE_Int)); return _set_range(this, data, minval, 1, maxval); } static SANE_Status _probe_resolution (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { int minval, maxval, min2, max2; int val = 0, val2; int quant = 1; enum hp_device_compat_e compat; /* Check for supported resolutions in both directions */ RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_X_RESOLUTION, &val, &minval, &maxval) ); RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_Y_RESOLUTION, &val2, &min2, &max2)); if ( min2 > minval ) minval = min2; if ( max2 < maxval ) maxval = max2; if (minval >= maxval) return SANE_STATUS_UNSUPPORTED; /* If we don't have an accessor, get one */ if (!this->data_acsr) { if (!(this->data_acsr = sanei_hp_accessor_int_new(data))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, val); _set_size(this, data, sizeof(SANE_Int)); /* The HP OfficeJet Pro 1150C crashes the scan head when scanning at * resolutions less than 42 dpi. Set a safe minimum resolution. * Hopefully 50 dpi is safe enough. */ if ((sanei_hp_device_probe(&compat,scsi)==SANE_STATUS_GOOD) && ((compat&(HP_COMPAT_OJ_1150C|HP_COMPAT_OJ_1170C))==HP_COMPAT_OJ_1150C)) { if (minval<50) minval=50; } /* HP Photosmart scanner does not allow scanning at arbitrary resolutions */ /* for slides/negatives. Must be multiple of 300 dpi. Set quantization. */ if ( (sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD) && (compat & HP_COMPAT_PS) ) { int val, mi, ma; if ( (sanei_hp_scl_inquire(scsi, SCL_MEDIA, &val, &mi, &ma) == SANE_STATUS_GOOD) && ((val == HP_MEDIA_SLIDE) || (val == HP_MEDIA_NEGATIVE)) ) { quant = 300; minval = (minval+quant-1)/quant; minval *= quant; maxval = (maxval+quant-1)/quant; maxval *= quant; } } DBG(5, "_probe_resolution: set range %d..%d, quant=%d\n",minval,maxval,quant); return _set_range(this, data, minval, quant, maxval); } static SANE_Status _probe_bool (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { HpScl scl = this->descriptor->scl_command; int val = 0; if (scl) RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, &val, 0, 0) ); /* If we don't have an accessor, get one */ if (!this->data_acsr) { if (!(this->data_acsr = sanei_hp_accessor_bool_new(data))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, val); _set_size(this, data, sizeof(SANE_Bool)); return SANE_STATUS_GOOD; } static SANE_Status _probe_change_doc (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) {SANE_Status status; int cap = 0; DBG(2, "probe_change_doc: inquire ADF capability\n"); status = sanei_hp_scl_inquire(scsi, SCL_ADF_CAPABILITY, &cap, 0, 0); if ( (status != SANE_STATUS_GOOD) || (cap == 0)) return SANE_STATUS_UNSUPPORTED; DBG(2, "probe_change_doc: check if change document is supported\n"); status = sanei_hp_scl_inquire(scsi, SCL_CHANGE_DOC, &cap, 0, 0); if ( status != SANE_STATUS_GOOD ) return SANE_STATUS_UNSUPPORTED; /* If we don't have an accessor, get one */ if (!this->data_acsr) { if (!(this->data_acsr = sanei_hp_accessor_bool_new(data))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, cap); _set_size(this, data, sizeof(SANE_Bool)); return SANE_STATUS_GOOD; } /* The OfficeJets support SCL_UNLOAD even when no ADF is installed, so * this function was added to check for SCL_ADF_CAPABILITY, similar to * _probe_change_doc(), to hide the unnecessary "Unload" button on * non-ADF OfficeJets. */ static SANE_Status _probe_unload (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) {SANE_Status status; int cap = 0; DBG(2, "probe_unload: inquire ADF capability\n"); status = sanei_hp_scl_inquire(scsi, SCL_ADF_CAPABILITY, &cap, 0, 0); if ( (status != SANE_STATUS_GOOD) || (cap == 0)) return SANE_STATUS_UNSUPPORTED; DBG(2, "probe_unload: check if unload is supported\n"); status = sanei_hp_scl_inquire(scsi, SCL_UNLOAD, &cap, 0, 0); if ( status != SANE_STATUS_GOOD ) return SANE_STATUS_UNSUPPORTED; /* If we don't have an accessor, get one */ if (!this->data_acsr) { if (!(this->data_acsr = sanei_hp_accessor_bool_new(data))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, cap); _set_size(this, data, sizeof(SANE_Bool)); return SANE_STATUS_GOOD; } static SANE_Status _probe_calibrate (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { int val = 0; /* Always false */ int minval, maxval; int media; int download_calib_file = 1; enum hp_device_compat_e compat; /* The OfficeJets don't seem to support calibration, so we'll * remove it from the option list to reduce frontend clutter. */ if ((sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD) && (compat & HP_COMPAT_OJ_1150C)) { return SANE_STATUS_UNSUPPORTED; } /* If we have a Photosmart scanner, we only download the calibration file */ /* when medium is set to prints */ media = -1; if (sanei_hp_scl_inquire(scsi, SCL_MEDIA, &val, &minval, &maxval) == SANE_STATUS_GOOD) media = val; /* 3: prints, 2: slides, 1: negatives */ if ( (sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD) && (compat & HP_COMPAT_PS) && (media != HP_MEDIA_PRINT)) download_calib_file = 0; /* Recalibrate can not be probed, because it has no inquire ID. */ /* And the desired ID of 10963 does not work. So we have to trust */ /* the evaluated HP model number. */ /* If we don't have an accessor, get one */ if (!this->data_acsr) { if (!(this->data_acsr = sanei_hp_accessor_bool_new(data))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, val); _set_size(this, data, sizeof(SANE_Bool)); /* Try to download calibration map */ if (download_calib_file) hp_download_calib_file ( scsi ); return SANE_STATUS_GOOD; } static HpChoice _make_choice_list (HpChoice choice, int minval, int maxval) { static struct hp_choice_s bad = { 0, 0, 0, 0, 0 }; /* FIXME: hack */ /* FIXME: Another memory leak */ if (!choice->name) return 0; else if (hp_choice_isSupported(choice, minval, maxval)) { _HpChoice new = sanei_hp_memdup(choice, sizeof(*new)); if (!new) return &bad; new->next = _make_choice_list(choice + 1, minval, maxval); return new; } else return _make_choice_list(choice + 1, minval, maxval); } static HpChoice _make_probed_choice_list (HpScsi scsi, HpScl scl, HpChoice choice, int minval, int maxval) { static struct hp_choice_s bad = { 0, 0, 0, 0, 0 }; /* FIXME: hack */ /* FIXME: Another memory leak */ if (!choice->name) return 0; else if (hp_probed_choice_isSupported(scsi, scl, choice, minval, maxval)) { _HpChoice new = sanei_hp_memdup(choice, sizeof(*new)); if (!new) return &bad; new->next = _make_probed_choice_list(scsi, scl, choice + 1, minval, maxval); return new; } else return _make_probed_choice_list(scsi, scl, choice + 1, minval, maxval); } static void _set_stringlist (HpOption this, HpData data, SANE_String_Const * strlist) { _HpSaneOption optd = _hp_option_saneoption(this, data); optd->constraint.string_list = strlist; optd->constraint_type = SANE_CONSTRAINT_STRING_LIST; } static SANE_Status _probe_choice (_HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { HpScl scl = this->descriptor->scl_command; int minval, maxval, val; HpChoice choices; const HpDeviceInfo *info; enum hp_device_compat_e compat; RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, &val, &minval, &maxval) ); DBG(3, "choice_option_probe: '%s': val, min, max = %d, %d, %d\n", this->descriptor->name, val, minval, maxval); info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); /* Datawidth needs a special handling. The choicelist consists of */ /* values of bits per sample. But the minval/maxval uses bits per pixel */ if ( scl == SCL_DATA_WIDTH ) { enum hp_scanmode_e scanmode = sanei_hp_optset_scanmode (optset, data); /* The data width inquiries seem not to work properly on PhotoSmart */ /* Sometimes they report just 24 bits, but support 30 bits too. */ /* Sometimes they report min/max to be 24/8. Assume they all support */ /* at least 10 bits per channel for RGB. Grayscale is only supported */ /* with 8 bits. */ if ( (sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD) && (compat & HP_COMPAT_PS)) { if (scanmode == HP_SCANMODE_GRAYSCALE) { minval = 8; if (maxval < 8) maxval = 8; } else if (scanmode == HP_SCANMODE_COLOR) { minval = 24; if (maxval < 30) maxval = 30; } DBG(1, "choice_option_probe: set max. datawidth to %d for photosmart\n", maxval); } if ( scanmode == HP_SCANMODE_COLOR ) { minval /= 3; if ( minval <= 0) minval = 1; maxval /= 3; if ( maxval <= 0) maxval = 1; val /= 3; if (val <= 0) val = 1; } #if 0 /* The OfficeJets claim to support >8 bits per color, but it may not * work on some models. This code (if not commented out) disables it. */ if ((sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD) && (compat & HP_COMPAT_OJ_1150C)) { if (maxval>8) maxval=8; } #endif } choices = _make_choice_list(this->descriptor->choices, minval, maxval); if (choices && !choices->name) /* FIXME: hack */ return SANE_STATUS_NO_MEM; if (!choices) return SANE_STATUS_UNSUPPORTED; /* If no accessor, create one here. */ #ifdef HP_ALLOC_CHOICEACC_ONCE if (!(this->data_acsr)) #endif this->data_acsr = sanei_hp_accessor_choice_new(data, choices, this->descriptor->may_change); if (!(this->data_acsr)) return SANE_STATUS_NO_MEM; sanei_hp_accessor_setint(this->data_acsr, data, val); _set_stringlist(this, data, sanei_hp_accessor_choice_strlist((HpAccessorChoice)this->data_acsr, 0, 0, info)); _set_size(this, data, sanei_hp_accessor_choice_maxsize((HpAccessorChoice)this->data_acsr)); return SANE_STATUS_GOOD; } static SANE_Status _probe_each_choice (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { HpScl scl = this->descriptor->scl_command; int minval, maxval, val; HpChoice choices; const HpDeviceInfo *info; RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, &val, &minval, &maxval) ); DBG(3, "choice_option_probe_each: '%s': val, min, max = %d, %d, %d\n", this->descriptor->name, val, minval, maxval); DBG(3, "choice_option_probe_each: test all values for '%s' separately\n", this->descriptor->name); info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); choices = _make_probed_choice_list(scsi, scl, this->descriptor->choices, minval, maxval); DBG(3, "choice_option_probe_each: restore previous value %d for '%s'\n", val, this->descriptor->name); /* Restore current value */ RETURN_IF_FAIL( sanei_hp_scl_set(scsi, scl, val) ); if (choices && !choices->name) /* FIXME: hack */ return SANE_STATUS_NO_MEM; if (!choices) return SANE_STATUS_UNSUPPORTED; /* If we don't have an accessor, get one */ #ifdef HP_ALLOC_CHOICEACC_ONCE if (!this->data_acsr) #endif { if (!(this->data_acsr = sanei_hp_accessor_choice_new(data, choices, this->descriptor->may_change ))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, val); _set_stringlist(this, data, sanei_hp_accessor_choice_strlist((HpAccessorChoice)this->data_acsr, 0, 0, info)); _set_size(this, data, sanei_hp_accessor_choice_maxsize((HpAccessorChoice)this->data_acsr)); return SANE_STATUS_GOOD; } /* pseudo probe for exposure times in Photosmart */ static SANE_Status _probe_ps_exposure_time (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { int minval = 0, maxval = 9, val = 0; HpChoice choices; const HpDeviceInfo *info; choices = _make_choice_list(this->descriptor->choices, minval, maxval); if (choices && !choices->name) /* FIXME: hack */ return SANE_STATUS_NO_MEM; info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); /* If we don't have an accessor, get one */ #ifdef HP_ALLOC_CHOICEACC_ONCE if (!this->data_acsr) #endif { if (!(this->data_acsr = sanei_hp_accessor_choice_new(data, choices, this->descriptor->may_change ))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, val); _set_stringlist(this, data, sanei_hp_accessor_choice_strlist((HpAccessorChoice)this->data_acsr, 0, 0, info)); _set_size(this, data, sanei_hp_accessor_choice_maxsize((HpAccessorChoice)this->data_acsr)); return SANE_STATUS_GOOD; } /* probe scan type (normal, adf, xpa) */ static SANE_Status _probe_scan_type (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { int val; int numchoices = 0; HpChoice choices; SANE_Status status; const HpDeviceInfo *info; struct hp_choice_s scan_types[4]; struct hp_choice_s nch = { 0, 0, 0, 0, 0 }; enum hp_device_compat_e compat; /* We always have normal scan mode */ scan_types[numchoices++] = this->descriptor->choices[0]; if ( sanei_hp_device_probe (&compat, scsi) != SANE_STATUS_GOOD ) compat = 0; /* Inquire ADF Capability. PhotoSmart scanner reports ADF capability, */ /* but it makes no sense. */ if ((compat & HP_COMPAT_PS) == 0) { status = sanei_hp_scl_inquire(scsi, SCL_ADF_CAPABILITY, &val, 0, 0); if ( (status == SANE_STATUS_GOOD) && (val == 1) ) { scan_types[numchoices++] = this->descriptor->choices[1]; } } /* Inquire XPA capability is supported only by IIcx and 6100c/4c/3c. */ /* But more devices support XPA scan window. So don't inquire XPA cap. */ if ( compat & ( HP_COMPAT_2CX | HP_COMPAT_4C | HP_COMPAT_4P | HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_6200C) && !(compat&HP_COMPAT_OJ_1150C) ) { scan_types[numchoices++] = this->descriptor->choices[2]; } /* Only normal scan type available ? No need to display choice */ if (numchoices <= 1) return SANE_STATUS_UNSUPPORTED; scan_types[numchoices] = nch; val = 0; choices = _make_choice_list(scan_types, 0, numchoices); if (choices && !choices->name) /* FIXME: hack */ return SANE_STATUS_NO_MEM; info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); /* If we don't have an accessor, get one */ #ifdef HP_ALLOC_CHOICEACC_ONCE if (!this->data_acsr) #endif { if (!(this->data_acsr = sanei_hp_accessor_choice_new(data, choices, this->descriptor->may_change ))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, val); _set_stringlist(this, data, sanei_hp_accessor_choice_strlist((HpAccessorChoice)this->data_acsr, 0, 0, info)); _set_size(this, data, sanei_hp_accessor_choice_maxsize((HpAccessorChoice)this->data_acsr)); return SANE_STATUS_GOOD; } static SANE_Status _probe_mirror_horiz (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { HpScl scl = this->descriptor->scl_command; int minval, maxval, val, sec_dir; HpChoice choices; const HpDeviceInfo *info; RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, &val, &minval, &maxval) ); DBG(3, "probe_mirror_horiz: '%s': val, min, max = %d, %d, %d\n", this->descriptor->name, val, minval, maxval); /* Look if the device supports the (?) inquire secondary scan-direction */ if ( sanei_hp_scl_inquire(scsi, SCL_SECONDARY_SCANDIR, &sec_dir, 0, 0) == SANE_STATUS_GOOD ) minval = HP_MIRROR_HORIZ_CONDITIONAL; info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); choices = _make_choice_list(this->descriptor->choices, minval, maxval); if (choices && !choices->name) /* FIXME: hack */ return SANE_STATUS_NO_MEM; if (!choices) return SANE_STATUS_UNSUPPORTED; /* If we don't have an accessor, get one */ #ifdef HP_ALLOC_CHOICEACC_ONCE if (!this->data_acsr) #endif { if (!(this->data_acsr = sanei_hp_accessor_choice_new(data, choices, this->descriptor->may_change ))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, val); _set_stringlist(this, data, sanei_hp_accessor_choice_strlist((HpAccessorChoice)this->data_acsr, 0, 0, info)); _set_size(this, data, sanei_hp_accessor_choice_maxsize((HpAccessorChoice)this->data_acsr)); return SANE_STATUS_GOOD; } static SANE_Status _probe_mirror_vert (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { int minval = HP_MIRROR_VERT_OFF, maxval = HP_MIRROR_VERT_ON, val = HP_MIRROR_VERT_OFF; int sec_dir; HpChoice choices; const HpDeviceInfo *info; info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); /* Look if the device supports the (?) inquire secondary scan-direction */ if ( sanei_hp_scl_inquire(scsi, SCL_SECONDARY_SCANDIR, &sec_dir, 0, 0) == SANE_STATUS_GOOD ) maxval = HP_MIRROR_VERT_CONDITIONAL; choices = _make_choice_list(this->descriptor->choices, minval, maxval); if (choices && !choices->name) /* FIXME: hack */ return SANE_STATUS_NO_MEM; if (!choices) return SANE_STATUS_UNSUPPORTED; /* If we don't have an accessor, get one */ #ifdef HP_ALLOC_CHOICEACC_ONCE if (!this->data_acsr) #endif { if (!(this->data_acsr = sanei_hp_accessor_choice_new(data, choices, this->descriptor->may_change ))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, val); _set_stringlist(this, data, sanei_hp_accessor_choice_strlist((HpAccessorChoice)this->data_acsr, 0, 0, info)); _set_size(this, data, sanei_hp_accessor_choice_maxsize((HpAccessorChoice)this->data_acsr)); return SANE_STATUS_GOOD; } static SANE_Status _probe_front_button(_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { int val = 0; if ( sanei_hp_scl_inquire(scsi, SCL_FRONT_BUTTON, &val, 0, 0) != SANE_STATUS_GOOD ) return SANE_STATUS_UNSUPPORTED; _set_size(this, data, sizeof(SANE_Bool)); /* If we don't have an accessor, get one */ if (!this->data_acsr) { if ( !(this->data_acsr = sanei_hp_accessor_bool_new(data)) ) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, 0); return SANE_STATUS_GOOD; } static SANE_Status _probe_geometry (_HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { HpScl scl = this->descriptor->scl_command; hp_bool_t is_tl = 0; hp_bool_t active_xpa = sanei_hp_is_active_xpa ( scsi ); int minval, maxval; SANE_Fixed fval; /* There might have been a reason for inquiring the extent */ /* by using the maxval of the position. But this does not work */ /* when scanning from ADF. The Y-pos is then inquired with -1..0. */ /* First try to get the values with SCL_X/Y_POS. If this is not ok, */ /* use SCL_X/Y_EXTENT */ if (scl == SCL_X_EXTENT) { scl = SCL_X_POS; } else if (scl == SCL_Y_EXTENT) { scl = SCL_Y_POS; } else is_tl = 1; RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, 0, &minval, &maxval) ); if (minval >= maxval) return SANE_STATUS_INVAL; /* Bad maximum value for extent-inquiry ? */ if ( (!is_tl) && (maxval <= 0) ) { scl = (scl == SCL_X_POS) ? SCL_X_EXTENT : SCL_Y_EXTENT; RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, 0, &minval, &maxval) ); if (minval >= maxval) return SANE_STATUS_INVAL; } if ((scl == SCL_X_EXTENT) || (scl == SCL_Y_EXTENT)) { /* Max. extent is larger than max. position. Reduce extent */ maxval--; DBG(3, "probe_geometry: Inquiry by extent. Reduced maxval to %lu\n", (unsigned long)maxval); } /* Need a new accessor ? */ if (!this->data_acsr) { if (!(this->data_acsr = sanei_hp_accessor_fixed_new(data))) return SANE_STATUS_NO_MEM; } /* The active xpa is only 5x5 inches */ if ( (!is_tl) && active_xpa && (sanei_hp_optset_scan_type (optset, data) == SCL_XPA_SCAN) ) { DBG(3,"Set maxval to 1500 because of active XPA\n"); maxval = 1500; } fval = is_tl ? SANE_FIX(0.0) : maxval * SANE_FIX(MM_PER_DEVPIX); RETURN_IF_FAIL( sanei_hp_accessor_set(this->data_acsr, data, &fval) ); _set_size(this, data, sizeof(SANE_Fixed)); return _set_range(this, data, minval * SANE_FIX(MM_PER_DEVPIX), 1, maxval * SANE_FIX(MM_PER_DEVPIX)); } static SANE_Status _probe_download_type (HpScl scl, HpScsi scsi) { SANE_Status status; sanei_hp_scl_clearErrors (scsi); sanei_hp_scl_set (scsi, SCL_DOWNLOAD_TYPE, SCL_INQ_ID(scl)); status = sanei_hp_scl_errcheck (scsi); DBG(3, "probe_download_type: Download type %d %ssupported\n", SCL_INQ_ID(scl), (status == SANE_STATUS_GOOD) ? "" : "not "); return status; } static SANE_Status _probe_custom_gamma (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { HpScl scl = this->descriptor->scl_command; HpScl scl_tonemap = SCL_8x8TONE_MAP; SANE_Status status; hp_bool_t simulate; int val = 0, minval, maxval; int id = SCL_INQ_ID(scl_tonemap); /* Check if download type supported */ status = sanei_hp_device_support_get ( sanei_hp_scsi_devicename (scsi), SCL_DOWNLOAD_TYPE, &minval, &maxval); simulate = (status != SANE_STATUS_GOOD) || (id < minval) || (id > maxval); if (simulate) { DBG(3, "probe_custom_gamma: Download type 2 not supported. Simulate\n"); } else { RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, &val, 0, 0) ); } /* If we don't have an accessor, get one */ if (!this->data_acsr) { if (!(this->data_acsr = sanei_hp_accessor_bool_new(data))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, val); _set_size(this, data, sizeof(SANE_Bool)); return SANE_STATUS_GOOD; } static SANE_Status _probe_vector (_HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { static struct vector_type_s { HpScl scl; unsigned length, depth; HpAccessor (*creator)(HpData data, unsigned length, unsigned depth); } types[] = { { SCL_8x8TONE_MAP, 256, 8, sanei_hp_accessor_gamma_vector_new }, #ifdef ENABLE_7x12_TONEMAPS { SCL_BW7x12TONE_MAP, 129, 12, sanei_hp_accessor_gamma_vector_new }, { SCL_7x12TONE_MAP, 3 * 129, 12, sanei_hp_accessor_gamma_vector_new }, #endif #ifdef ENABLE_16x16_DITHERS { SCL_BW16x16DITHER, 256, 8, sanei_hp_accessor_vector_new }, #endif { SCL_BW8x8DITHER, 64, 8, sanei_hp_accessor_vector_new }, { SCL_8x9MATRIX_COEFF, 9, 8, sanei_hp_accessor_matrix_vector_new }, #ifdef ENABLE_10BIT_MATRIXES { SCL_10x9MATRIX_COEFF, 9, 10, sanei_hp_accessor_matrix_vector_new }, { SCL_10x3MATRIX_COEFF, 3, 10, sanei_hp_accessor_matrix_vector_new }, #endif { 0, 0, 0, 0 } }; static struct subvector_type_s { HpOptionDescriptor desc; unsigned nchan, chan; HpOptionDescriptor super; } subvec_types[] = { #ifdef ENABLE_7x12_TONEMAPS { GAMMA_VECTOR_R, 3, 0, RGB_TONEMAP }, { GAMMA_VECTOR_G, 3, 1, RGB_TONEMAP }, { GAMMA_VECTOR_B, 3, 2, RGB_TONEMAP }, #endif { 0, 0, 0, 0 }, }; HpScl scl = this->descriptor->scl_command; HpAccessorVector vec; if (scl) { struct vector_type_s *type; for (type = types; type->scl; type++) if (type->scl == scl) break; assert(type->scl); RETURN_IF_FAIL ( _probe_download_type (scl, scsi) ); /* If we don't have an accessor, get one */ #ifdef HP_ALLOC_CHOICEACC_ONCE if (!this->data_acsr) #endif { this->data_acsr = (*type->creator)(data, type->length, type->depth); } } else { struct subvector_type_s *type; HpOption super; for (type = subvec_types; type->desc; type++) if (type->desc == this->descriptor) break; assert(type->desc); super = hp_optset_get(optset, type->super); assert(super); /* If we don't have an accessor, get one */ #ifdef HP_ALLOC_CHOICEACC_ONCE if (!this->data_acsr) #endif { this->data_acsr = sanei_hp_accessor_subvector_new( (HpAccessorVector) super->data_acsr, type->nchan, type->chan); } } if (!this->data_acsr) return SANE_STATUS_NO_MEM; vec = (HpAccessorVector)this->data_acsr; _set_size(this, data, sizeof(SANE_Fixed) * sanei_hp_accessor_vector_length(vec)); return _set_range(this, data, sanei_hp_accessor_vector_minval(vec), 1, sanei_hp_accessor_vector_maxval(vec)); } static SANE_Status _probe_gamma_vector (_HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { SANE_Fixed * buf; int i; size_t size, length; RETURN_IF_FAIL( _probe_vector(this, scsi, optset, data) ); /* Initialize to linear map */ size = hp_option_saneoption(this, data)->size; if (!(buf = alloca(size))) return SANE_STATUS_NO_MEM; length = size / sizeof(SANE_Fixed); for (i = 0; i < (int)length; i++) buf[i] = (SANE_FIX(HP_VECTOR_SCALE* 1.0) * i + (length-1) / 2) / length; return sanei_hp_accessor_set(this->data_acsr, data, buf); } static SANE_Status _probe_horiz_dither (_HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { int dim = 8; size_t size; int i, j; SANE_Fixed * buf; if (this->descriptor->scl_command == SCL_BW16x16DITHER) dim = 16; RETURN_IF_FAIL( _probe_vector(this, scsi, optset, data) ); /* Select vertical dither pattern, and upload it */ RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_BW_DITHER, HP_DITHER_VERTICAL) ); RETURN_IF_FAIL( hp_option_upload(this, scsi, optset, data) ); /* Flip it to get a horizontal dither pattern */ size = hp_option_saneoption(this, data)->size; assert(size == dim * dim * sizeof(SANE_Fixed)); if (!(buf = alloca(size))) return SANE_STATUS_NO_MEM; #define SWAP_FIXED(x,y) do { SANE_Fixed tmp = x; x = y; y = tmp; } while(0) RETURN_IF_FAIL( sanei_hp_accessor_get(this->data_acsr, data, buf) ); for (i = 0; i < dim; i++) for (j = i + 1; j < dim; j++) SWAP_FIXED(buf[i * dim + j], buf[j * dim + i]); return sanei_hp_accessor_set(this->data_acsr, data, buf); } static SANE_Status _probe_matrix (_HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { RETURN_IF_FAIL( _probe_vector(this, scsi, optset, data) ); /* Initial value: select RGB matrix, and upload it. */ RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_MATRIX, HP_MATRIX_RGB) ); return hp_option_upload(this, scsi, optset, data); } static SANE_Status _probe_num_options (_HpOption this, HpScsi __sane_unused__ scsi, HpOptSet __sane_unused__ optset, HpData data) { /* If we don't have an accessor, get one */ if (!this->data_acsr) { if (!(this->data_acsr = sanei_hp_accessor_int_new(data))) return SANE_STATUS_NO_MEM; } _set_size(this, data, sizeof(SANE_Int)); return SANE_STATUS_GOOD; } static SANE_Status _probe_devpix (_HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { HpScl scl = this->descriptor->scl_command; int resolution; if (FAILED( sanei_hp_scl_inquire(scsi, scl, &resolution, 0, 0) )) { DBG(1, "probe_devpix: inquiry failed, assume 300 ppi\n"); resolution = 300; } if (!this->data_acsr) { if (!(this->data_acsr = sanei_hp_accessor_int_new(data))) return SANE_STATUS_NO_MEM; } sanei_hp_accessor_setint(this->data_acsr, data, resolution); _set_size(this, data, sizeof(SANE_Int)); return SANE_STATUS_GOOD; } /* * Simulate functions */ static SANE_Status _simulate_brightness (HpOption this, HpData data, HpScsi scsi) { int k, val, newval; unsigned char *brightness_map; HpDeviceInfo *info; info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); assert (info); val = sanei_hp_accessor_getint(this->data_acsr, data); DBG(3, "simulate_brightness: value = %d\n", val); /* Update brightness map in info structure */ brightness_map = &(info->simulate.brightness_map[0]); val *= 2; /* A value of 127 should give a totally white image */ for (k = 0; k < 256; k++) { newval = k + val; if (newval < 0) newval = 0; else if (newval > 255) newval = 255; brightness_map[k] = (unsigned char)newval; } return SANE_STATUS_GOOD; } static int hp_contrast (int x, int g) {int y = 0; if (g < -127) g = -127; else if (g > 127) g = 127; if (x < 0) x = 0; else if (x > 255) x = 255; if (g == 0) { y = x; } else if (g < 0) { g = -g; y = x * (255 - 2*g); y = y/255 + g; } else { if (x <= g) y = 0; else if (x >= 255-g) y = 255; else { y = (x - g)*255; y /= (255 - 2*g); } } return y; } static SANE_Status _simulate_contrast (HpOption this, HpData data, HpScsi scsi) { int k, val, newval; unsigned char *contrast_map; HpDeviceInfo *info; info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); assert (info); val = sanei_hp_accessor_getint(this->data_acsr, data); DBG(3, "simulate_contrast: value = %d\n", val); /* Update contrast map in info structure */ contrast_map = &(info->simulate.contrast_map[0]); for (k = 0; k < 256; k++) { newval = hp_contrast (k, val); if (newval < 0) newval = 0; else if (newval > 255) newval = 255; contrast_map[k] = (unsigned char)newval; } return SANE_STATUS_GOOD; } /* * Option download functions */ static SANE_Status _program_generic (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { return hp_option_download(this, data, optset, scsi); } static SANE_Status _program_geometry (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { /* #define HP_LIMIT_ADF_WINDOW */ #ifndef HP_LIMIT_ADF_WINDOW return hp_option_download(this, data, optset, scsi); #else HpScl scl = this->descriptor->scl_command; int value; SANE_Status Status; if (sanei_hp_optset_scan_type (optset, data) != SCL_ADF_SCAN) return hp_option_download(this, data, optset, scsi); /* ADF may crash when scanning only a window ? */ if ( (scl == SCL_X_POS) || (scl == SCL_Y_POS) ) { value = 0; DBG(3,"program_geometry: set %c-pos to %d\n", (scl == SCL_X_POS) ? 'x' : 'y', value); } else if ( scl == SCL_X_EXTENT ) { value = 2550; DBG(3,"program_geometry: set x-extent to %d\n", value); } else { value = 4200; DBG(3,"program_geometry: set y-extent to %d\n", value); } Status = sanei_hp_scl_set(scsi, scl, value); return Status; #endif } static SANE_Status _program_data_width (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { HpScl scl = this->descriptor->scl_command; int value = sanei_hp_accessor_getint(this->data_acsr, data); SANE_Status status; if ( sanei_hp_optset_scanmode (optset, data) == HP_SCANMODE_COLOR ) { value *= 3; if (value < 24) { DBG(3,"program_data_width: map datawith from %d to 24\n", (int)value); value = 24; } } status = sanei_hp_scl_set(scsi, scl, value); return status; } static SANE_Status _program_generic_simulate (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { HpScl scl = this->descriptor->scl_command; const char *devname = sanei_hp_scsi_devicename (scsi); int simulate; /* Check if command is supported */ simulate = ( sanei_hp_device_support_get (devname, scl, 0, 0) != SANE_STATUS_GOOD ); /* Save simulate flag */ sanei_hp_device_simulate_set (devname, scl, simulate); if ( !simulate ) /* Let the device do it */ return hp_option_download(this, data, optset, scsi); DBG(3, "program_generic: %lu not programmed. Will be simulated\n", (unsigned long)(SCL_INQ_ID(scl))); switch (scl) { case SCL_BRIGHTNESS: _simulate_brightness (this, data, scsi); break; case SCL_CONTRAST: _simulate_contrast (this, data,scsi); break; default: DBG(1, "program_generic: No simulation for %lu\n", (unsigned long)(SCL_INQ_ID(scl))); break; } return SANE_STATUS_GOOD; } static SANE_Status _simulate_custom_gamma (HpOption gvector, HpScsi scsi, HpData data) { size_t size = sanei_hp_accessor_size(gvector->data_acsr); const unsigned char *vector_data = (const unsigned char *)sanei_hp_accessor_data(gvector->data_acsr, data); HpDeviceInfo *info; int k, newval; DBG(3,"program_custom_gamma_simulate: save gamma map\n"); if (size != 256) { DBG(1,"program_custom_gamma_simulate: size of vector is %d.\ Should be 256.\n", (int)size); return SANE_STATUS_INVAL; } RETURN_IF_FAIL (sanei_hp_scl_set(scsi, SCL_TONE_MAP, 0)); info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); info->simulate.gamma_simulate = 1; for (k = 0; k < 256; k++) { newval = 255 - vector_data[255-k]; if (newval < 0) newval = 0; else if (newval > 255) newval = 255; info->simulate.gamma_map[k] = newval; } return SANE_STATUS_GOOD; } static SANE_Status _program_tonemap (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { hp_bool_t use_custom_map = hp_option_getint(this, data); HpOption gvector = 0; int type = 0; if (!use_custom_map) return sanei_hp_scl_set(scsi, SCL_TONE_MAP, 0); #ifdef ENABLE_7x12_TONEMAPS /* Try to find the appropriate 5P style tonemap. */ if (sanei_hp_optset_scanmode(optset, data) == HP_SCANMODE_COLOR) { type = -1; gvector = hp_optset_get(optset, RGB_TONEMAP); } else { type = -2; gvector = hp_optset_get(optset, GAMMA_VECTOR_7x12); } #endif /* If that failed, just use 8x8 tonemap */ if (!gvector) { HpScl scl_tonemap = SCL_8x8TONE_MAP; hp_bool_t simulate; int id = SCL_INQ_ID(scl_tonemap); int minval, maxval; SANE_Status status; type = -1; gvector = hp_optset_get(optset, GAMMA_VECTOR_8x8); /* Check if download type supported */ status = sanei_hp_device_support_get ( sanei_hp_scsi_devicename (scsi), SCL_DOWNLOAD_TYPE, &minval, &maxval); simulate = (status != SANE_STATUS_GOOD) || (id < minval) || (id > maxval); if (simulate) return _simulate_custom_gamma (gvector, scsi, data); } assert(gvector != 0); RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_TONE_MAP, type) ); return hp_option_download(gvector, data, optset, scsi); } static SANE_Status _program_dither (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { enum hp_dither_type_e type = hp_option_getint(this, data); HpOption dither; switch (type) { case HP_DITHER_CUSTOM: dither = hp_optset_getByName(optset, SANE_NAME_HALFTONE_PATTERN); assert(dither != 0); break; case HP_DITHER_HORIZONTAL: dither = hp_optset_getByName(optset, HP_NAME_HORIZONTAL_DITHER); type = HP_DITHER_CUSTOM; assert(dither != 0); break; default: dither = 0; break; } RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_BW_DITHER, type) ); if (!dither) return SANE_STATUS_GOOD; return hp_option_download(dither, data, optset, scsi); } #ifdef FAKE_COLORSEP_MATRIXES static HpOption _get_sepmatrix (HpOptSet optset, HpData data, enum hp_matrix_type_e type) { SANE_Fixed buf[9]; HpOption matrix = hp_optset_get(optset, SEPMATRIX); memset(buf, 0, sizeof(buf)); if (type == HP_MATRIX_RED) buf[1] = SANE_FIX(1.0); else if (type == HP_MATRIX_GREEN) buf[4] = SANE_FIX(1.0); else if (type == HP_MATRIX_BLUE) buf[7] = SANE_FIX(1.0); else { assert(!"Bad colorsep type"); return 0; } sanei_hp_accessor_set(matrix->data_acsr, data, buf); return matrix; } #endif static SANE_Status _program_matrix (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { enum hp_matrix_type_e type = hp_option_getint(this, data); HpOption matrix = 0; if (type == HP_MATRIX_AUTO) return SANE_STATUS_GOOD; /* Default to matrix set by mode */ /* Download custom matrix, if we need it. */ if (type == HP_MATRIX_CUSTOM) { matrix = hp_optset_getByName(optset, SANE_NAME_MATRIX_RGB); assert(matrix); } #ifdef FAKE_COLORSEP_MATRIXES else if (type == HP_MATRIX_RED || type == HP_MATRIX_BLUE || type == HP_MATRIX_GREEN) { matrix = _get_sepmatrix(optset, data, type); type = HP_MATRIX_CUSTOM; assert(matrix); } #else else if (type == HP_MATRIX_GREEN) type = HP_MATRIX_PASS; #endif RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_MATRIX, type) ); if (matrix) RETURN_IF_FAIL( hp_option_download(matrix, data, optset, scsi) ); return SANE_STATUS_GOOD; } static SANE_Status _program_resolution (HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { int xresolution = hp_option_getint(this, data); int yresolution = xresolution; int xscale = 100, yscale = 100; #ifdef FIX_PHOTOSMART int minval, maxval, media; enum hp_device_compat_e compat; /* HP Photosmart scanner has problems with scanning slides/negatives */ /* at arbitrary resolutions. The following tests did not work: */ /* xres = yres = next lower multiple of 300, xscale = yscale > 100: */ /* xres = yres = next higher multiple of 300, xscale = yscale < 100: */ /* xres = next lower multiple of 300, xscale > 100 */ /* xres = next higher multiple of 300, xscale < 100 */ /* yres = next lower multiple of 300, yscale > 100 */ /* yres = next higher multiple of 300, yscale < 100 */ /* The image extent was ok, but the content was stretched in y-direction */ if (xresolution > 300) { if ( (sanei_hp_device_probe (&compat, scsi) == SANE_STATUS_GOOD) && (compat & HP_COMPAT_PS) && (sanei_hp_scl_inquire(scsi, SCL_MEDIA, &media, &minval, &maxval) == SANE_STATUS_GOOD) && ((media == HP_MEDIA_SLIDE) || (media == HP_MEDIA_NEGATIVE))) {int next_resolution; next_resolution = (xresolution % 300) * 300; if (next_resolution < 300) next_resolution = 300; yresolution = next_resolution; yscale = (int)(100.0 * xresolution / yresolution); } } #endif RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_X_SCALE, xscale) ); RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_Y_SCALE, yscale) ); RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_X_RESOLUTION, xresolution) ); RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_Y_RESOLUTION, yresolution) ); return SANE_STATUS_GOOD; } static char * get_home_dir (void) { #ifdef SANE_HOME_HP return getenv (SANE_HOME_HP); #else struct passwd *pw; pw = getpwuid (getuid ()); /* Look if we can find our home directory */ return pw ? pw->pw_dir : NULL; #endif } static char * get_calib_filename (HpScsi scsi) { char *homedir; char *calib_filename, *cf; const char *devname = sanei_hp_scsi_devicename (scsi); int name_len; homedir = get_home_dir (); /* Look if we can find our home directory */ if (!homedir) return NULL; name_len = strlen (homedir) + 33; if ( devname ) name_len += strlen (devname); calib_filename = sanei_hp_allocz (name_len); if (!calib_filename) return NULL; strcpy (calib_filename, homedir); strcat (calib_filename, "/.sane/calib-hp"); if ( devname && devname[0] ) /* Replace '/' by "+-" */ { cf = calib_filename + strlen (calib_filename); *(cf++) = ':'; while (*devname) { if (*devname == '/') *(cf++) = '+', *(cf++) = '-'; else *(cf++) = *devname; devname++; } } strcat (calib_filename, ".dat"); return calib_filename; } static SANE_Status read_calib_file (int *nbytes, char **calib_data, HpScsi scsi) { SANE_Status status = SANE_STATUS_GOOD; char *calib_filename; FILE *calib_file; int err, c1, c2, c3, c4; *nbytes = 0; *calib_data = NULL; calib_filename = get_calib_filename ( scsi ); if (!calib_filename) return SANE_STATUS_NO_MEM; calib_file = fopen (calib_filename, "rb"); if ( calib_file ) { err = ((c1 = getc (calib_file)) == EOF); err |= ((c2 = getc (calib_file)) == EOF); err |= ((c3 = getc (calib_file)) == EOF); err |= ((c4 = getc (calib_file)) == EOF); *nbytes = (c1 << 24) | (c2 << 16) | (c3 << 8) | c4; if ( err ) { DBG(1, "read_calib_file: Error reading calibration data size\n"); status = SANE_STATUS_EOF; } else { *calib_data = sanei_hp_alloc ( *nbytes ); if ( !*calib_data ) { status = SANE_STATUS_NO_MEM; } else { err |= ((int)fread (*calib_data,1,*nbytes,calib_file) != *nbytes); if ( err ) { DBG(1, "read_calib_file: Error reading calibration data\n"); sanei_hp_free ( *calib_data ); status = SANE_STATUS_EOF; } } } fclose ( calib_file ); } else { DBG(1, "read_calib_file: Error opening calibration file %s\ for reading\n", calib_filename); status = SANE_STATUS_EOF; } sanei_hp_free (calib_filename); return ( status ); } static SANE_Status write_calib_file (int nbytes, char *data, HpScsi scsi) { SANE_Status status = SANE_STATUS_GOOD; char *calib_filename; int err; FILE *calib_file; calib_filename = get_calib_filename ( scsi ); if (!calib_filename) return SANE_STATUS_NO_MEM; calib_file = fopen (calib_filename, "wb"); if ( calib_file ) { err = (putc ((nbytes >> 24) & 0xff, calib_file) == EOF); err |= (putc ((nbytes >> 16) & 0xff, calib_file) == EOF); err |= (putc ((nbytes >> 8) & 0xff, calib_file) == EOF); err |= (putc (nbytes & 0xff, calib_file) == EOF); err |= ((int)fwrite (data, 1, nbytes, calib_file) != nbytes); fclose (calib_file); if ( err ) { DBG(1, "write_calib_file: Error writing calibration data\n"); unlink (calib_filename); status = SANE_STATUS_EOF; } } else { DBG(1, "write_calib_file: Error opening calibration file %s\ for writing\n", calib_filename); status = SANE_STATUS_EOF; } sanei_hp_free (calib_filename); return (status); } static SANE_Status _program_media (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { int req_media, minval, maxval, current_media; HpScl scl = this->descriptor->scl_command; req_media = sanei_hp_accessor_getint(this->data_acsr, data); RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, scl, ¤t_media, &minval, &maxval) ); if (current_media == req_media) return SANE_STATUS_GOOD; /* Unload scanner */ RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_UNLOAD, 0) ); /* Select new media */ RETURN_IF_FAIL( hp_option_download(this, data, optset, scsi)); /* Update support list */ sanei_hp_device_support_probe (scsi); if (req_media == HP_MEDIA_PRINT) hp_download_calib_file (scsi); return SANE_STATUS_GOOD; } static SANE_Status _program_unload_after_scan (HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { HpDeviceInfo *info; info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); assert (info); info->unload_after_scan = sanei_hp_accessor_getint(this->data_acsr, data); DBG(3,"program_unload_after_scan: flag = %lu\n", (unsigned long)info->unload_after_scan); return SANE_STATUS_GOOD; } static SANE_Status _program_lamp_off (HpOption __sane_unused__ this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData __sane_unused__ data) { DBG(3,"program_lamp_off: shut off lamp\n"); return sanei_hp_scl_set(scsi, SCL_LAMPTEST, 0); } static SANE_Status _program_scan_type (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { int req_scan_type; req_scan_type = sanei_hp_accessor_getint(this->data_acsr, data); if ( req_scan_type == HP_SCANTYPE_XPA ) { enum hp_scanmode_e scan_mode = sanei_hp_optset_scanmode(optset, data); static unsigned char xpa_matrix_coeff[] = { 0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x80 }; static unsigned char xpa_tone_map[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xfe,0x0f, 0xe1,0x0f,0xd3,0x0f,0xc4,0x0f,0xb5,0x0f,0xa6,0x0f,0x97,0x0f,0x88,0x0f,0x79,0x0f, 0x6a,0x0f,0x5b,0x0f,0x4b,0x0f,0x3c,0x0f,0x2c,0x0f,0x1d,0x0f,0x0d,0x0e,0xfe,0x0e, 0xee,0x0e,0xde,0x0e,0xce,0x0e,0xbe,0x0e,0xae,0x0e,0x9e,0x0e,0x8e,0x0e,0x7d,0x0e, 0x6d,0x0e,0x5c,0x0e,0x4c,0x0e,0x3b,0x0e,0x2a,0x0e,0x19,0x0e,0x08,0x0d,0xf7,0x0d, 0xe6,0x0d,0xd5,0x0d,0xc4,0x0d,0xb2,0x0d,0xa1,0x0d,0x8f,0x0d,0x7d,0x0d,0x6b,0x0d, 0x59,0x0d,0x47,0x0d,0x35,0x0d,0x22,0x0d,0x10,0x0c,0xfd,0x0c,0xeb,0x0c,0xd8,0x0c, 0xc5,0x0c,0xb2,0x0c,0x9e,0x0c,0x8b,0x0c,0x77,0x0c,0x64,0x0c,0x50,0x0c,0x3c,0x0c, 0x28,0x0c,0x14,0x0b,0xff,0x0b,0xeb,0x0b,0xd6,0x0b,0xc1,0x0b,0xac,0x0b,0x96,0x0b, 0x81,0x0b,0x6b,0x0b,0x55,0x0b,0x3f,0x0b,0x29,0x0b,0x12,0x0a,0xfc,0x0a,0xe5,0x0a, 0xce,0x0a,0xb6,0x0a,0x9e,0x0a,0x87,0x0a,0x6e,0x0a,0x56,0x0a,0x3d,0x0a,0x24,0x0a, 0x0b,0x09,0xf1,0x09,0xd8,0x09,0xbd,0x09,0xa3,0x09,0x88,0x09,0x6d,0x09,0x51,0x09, 0x35,0x09,0x19,0x08,0xfc,0x08,0xdf,0x08,0xc1,0x08,0xa3,0x08,0x84,0x08,0x65,0x08, 0x45,0x08,0x24,0x08,0x03,0x07,0xe1,0x07,0xbe,0x07,0x9b,0x07,0x78,0x07,0x53,0x07, 0x2d,0x07,0x07,0x06,0xdf,0x06,0xb7,0x06,0x8d,0x06,0x62,0x06,0x36,0x06,0x07,0x05, 0xd8,0x05,0xa6,0x05,0x72,0x05,0x3c,0x04,0xfc,0x04,0x7c,0x03,0xfc,0x03,0x7c,0x02, 0xfc,0x02,0x7c,0x01,0xfc,0x01,0x7c,0x00,0xfc,0x00,0x7c,0x00,0x00,0x0f,0xfe,0x0f, 0xe1,0x0f,0xd3,0x0f,0xc4,0x0f,0xb5,0x0f,0xa6,0x0f,0x97,0x0f,0x88,0x0f,0x79,0x0f, 0x6a,0x0f,0x5b,0x0f,0x4b,0x0f,0x3c,0x0f,0x2c,0x0f,0x1d,0x0f,0x0d,0x0e,0xfe,0x0e, 0xee,0x0e,0xde,0x0e,0xce,0x0e,0xbe,0x0e,0xae,0x0e,0x9e,0x0e,0x8e,0x0e,0x7d,0x0e, 0x6d,0x0e,0x5c,0x0e,0x4c,0x0e,0x3b,0x0e,0x2a,0x0e,0x19,0x0e,0x08,0x0d,0xf7,0x0d, 0xe6,0x0d,0xd5,0x0d,0xc4,0x0d,0xb2,0x0d,0xa1,0x0d,0x8f,0x0d,0x7d,0x0d,0x6b,0x0d, 0x59,0x0d,0x47,0x0d,0x35,0x0d,0x22,0x0d,0x10,0x0c,0xfd,0x0c,0xeb,0x0c,0xd8,0x0c, 0xc5,0x0c,0xb2,0x0c,0x9e,0x0c,0x8b,0x0c,0x77,0x0c,0x64,0x0c,0x50,0x0c,0x3c,0x0c, 0x28,0x0c,0x14,0x0b,0xff,0x0b,0xeb,0x0b,0xd6,0x0b,0xc1,0x0b,0xac,0x0b,0x96,0x0b, 0x81,0x0b,0x6b,0x0b,0x55,0x0b,0x3f,0x0b,0x29,0x0b,0x12,0x0a,0xfc,0x0a,0xe5,0x0a, 0xce,0x0a,0xb6,0x0a,0x9e,0x0a,0x87,0x0a,0x6e,0x0a,0x56,0x0a,0x3d,0x0a,0x24,0x0a, 0x0b,0x09,0xf1,0x09,0xd8,0x09,0xbd,0x09,0xa3,0x09,0x88,0x09,0x6d,0x09,0x51,0x09, 0x35,0x09,0x19,0x08,0xfc,0x08,0xdf,0x08,0xc1,0x08,0xa3,0x08,0x84,0x08,0x65,0x08, 0x45,0x08,0x24,0x08,0x03,0x07,0xe1,0x07,0xbe,0x07,0x9b,0x07,0x78,0x07,0x53,0x07, 0x2d,0x07,0x07,0x06,0xdf,0x06,0xb7,0x06,0x8d,0x06,0x62,0x06,0x36,0x06,0x07,0x05, 0xd8,0x05,0xa6,0x05,0x72,0x05,0x3c,0x04,0xfc,0x04,0x7c,0x03,0xfc,0x03,0x7c,0x02, 0xfc,0x02,0x7c,0x01,0xfc,0x01,0x7c,0x00,0xfc,0x00,0x7c,0x00,0x00,0x0f,0xfe,0x0f, 0xe1,0x0f,0xd3,0x0f,0xc4,0x0f,0xb5,0x0f,0xa6,0x0f,0x97,0x0f,0x88,0x0f,0x79,0x0f, 0x6a,0x0f,0x5b,0x0f,0x4b,0x0f,0x3c,0x0f,0x2c,0x0f,0x1d,0x0f,0x0d,0x0e,0xfe,0x0e, 0xee,0x0e,0xde,0x0e,0xce,0x0e,0xbe,0x0e,0xae,0x0e,0x9e,0x0e,0x8e,0x0e,0x7d,0x0e, 0x6d,0x0e,0x5c,0x0e,0x4c,0x0e,0x3b,0x0e,0x2a,0x0e,0x19,0x0e,0x08,0x0d,0xf7,0x0d, 0xe6,0x0d,0xd5,0x0d,0xc4,0x0d,0xb2,0x0d,0xa1,0x0d,0x8f,0x0d,0x7d,0x0d,0x6b,0x0d, 0x59,0x0d,0x47,0x0d,0x35,0x0d,0x22,0x0d,0x10,0x0c,0xfd,0x0c,0xeb,0x0c,0xd8,0x0c, 0xc5,0x0c,0xb2,0x0c,0x9e,0x0c,0x8b,0x0c,0x77,0x0c,0x64,0x0c,0x50,0x0c,0x3c,0x0c, 0x28,0x0c,0x14,0x0b,0xff,0x0b,0xeb,0x0b,0xd6,0x0b,0xc1,0x0b,0xac,0x0b,0x96,0x0b, 0x81,0x0b,0x6b,0x0b,0x55,0x0b,0x3f,0x0b,0x29,0x0b,0x12,0x0a,0xfc,0x0a,0xe5,0x0a, 0xce,0x0a,0xb6,0x0a,0x9e,0x0a,0x87,0x0a,0x6e,0x0a,0x56,0x0a,0x3d,0x0a,0x24,0x0a, 0x0b,0x09,0xf1,0x09,0xd8,0x09,0xbd,0x09,0xa3,0x09,0x88,0x09,0x6d,0x09,0x51,0x09, 0x35,0x09,0x19,0x08,0xfc,0x08,0xdf,0x08,0xc1,0x08,0xa3,0x08,0x84,0x08,0x65,0x08, 0x45,0x08,0x24,0x08,0x03,0x07,0xe1,0x07,0xbe,0x07,0x9b,0x07,0x78,0x07,0x53,0x07, 0x2d,0x07,0x07,0x06,0xdf,0x06,0xb7,0x06,0x8d,0x06,0x62,0x06,0x36,0x06,0x07,0x05, 0xd8,0x05,0xa6,0x05,0x72,0x05,0x3c,0x04,0xfc,0x04,0x7c,0x03,0xfc,0x03,0x7c,0x02, 0xfc,0x02,0x7c,0x01,0xfc,0x01,0x7c,0x00,0xfc,0x00,0x7c,0x00,0x00 }; sanei_hp_scl_set(scsi, SCL_RESERVED1, 0); /* don't know */ sanei_hp_scl_set(scsi, SCL_10952, 0); /* Calibration mode */ if ( sanei_hp_is_active_xpa (scsi) && ( (scan_mode==HP_SCANMODE_COLOR) || (scan_mode==HP_SCANMODE_GRAYSCALE)) ) { DBG (3,"program_scan_type: set tone map for active XPA\n"); sanei_hp_scl_download (scsi, SCL_10x9MATRIX_COEFF, xpa_matrix_coeff, sizeof (xpa_matrix_coeff)); sanei_hp_scl_set(scsi, SCL_MATRIX, -1); /* Set matrix coefficient */ sanei_hp_scl_download (scsi, SCL_7x12TONE_MAP, xpa_tone_map, sizeof (xpa_tone_map)); sanei_hp_scl_set(scsi, SCL_TONE_MAP, -1); /* Select tone map */ } } return SANE_STATUS_GOOD; } static SANE_Status _program_change_doc (HpOption __sane_unused__ this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData __sane_unused__ data) { int istat; DBG(2, "program_change_doc: inquire ADF ready\n"); RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_ADF_READY, &istat, 0, 0) ); if ( istat != 1 ) /* ADF not ready */ { DBG(2, "program_change_doc: ADF not ready\n"); return SANE_STATUS_INVAL; } DBG(2, "program_change_doc: inquire paper in ADF\n"); RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_ADF_BIN, &istat, 0, 0) ); if ( istat == 0 ) /* Nothing in ADF BIN */ { DBG(2, "program_change_doc: nothing in ADF BIN. Just Unload.\n"); return sanei_hp_scl_set(scsi, SCL_UNLOAD, 0); } DBG(2, "program_change_doc: Clear errors and change document.\n"); RETURN_IF_FAIL( sanei_hp_scl_clearErrors (scsi) ); RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_CHANGE_DOC, 0) ); return sanei_hp_scl_errcheck (scsi); } static SANE_Status _program_unload (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { hp_bool_t adfscan = ( sanei_hp_optset_scan_type (optset, data) == SCL_ADF_SCAN ); /* If we have an ADF, try to see if it is ready to unload */ if (adfscan) {int val; if ( sanei_hp_scl_inquire(scsi, SCL_ADF_RDY_UNLOAD, &val, 0, 0) == SANE_STATUS_GOOD ) { DBG(3, "program_unload: ADF is%sready to unload\n", val ? " " : " not "); } else { DBG(3, "program_unload: Command 'Ready to unload' not supported\n"); } } return hp_option_download(this, data, optset, scsi); } static SANE_Status _program_calibrate (HpOption __sane_unused__ this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData __sane_unused__ data) { struct passwd *pw; SANE_Status status = SANE_STATUS_GOOD; size_t calib_size; char *calib_buf; RETURN_IF_FAIL ( sanei_hp_scl_calibrate(scsi) ); /* Start calibration */ pw = getpwuid (getuid ()); /* Look if we can find our home directory */ if (!pw) return SANE_STATUS_GOOD; DBG(3, "_program_calibrate: Read calibration data\n"); RETURN_IF_FAIL ( sanei_hp_scl_upload_binary (scsi, SCL_CALIB_MAP, &calib_size, &calib_buf) ); DBG(3, "_program_calibrate: Got %lu bytes of calibration data\n", (unsigned long) calib_size); write_calib_file (calib_size, calib_buf, scsi); sanei_hp_free (calib_buf); return (status); } /* The exposure time of the HP Photosmart can be changed by overwriting * some headers of the calibration data. The scanner uses a slower stepping * speed for higher exposure times */ static SANE_Status _program_ps_exposure_time (HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { SANE_Status status = SANE_STATUS_GOOD; size_t calib_size = 0; char *calib_buf = NULL; int i; int option = hp_option_getint(this, data); static char *exposure[] = {"\x00\x64\x00\x64\x00\x64", /* 100% */ "\x00\x7d\x00\x7d\x00\x7d", /* 125% */ "\x00\x96\x00\x96\x00\x96", /* 150% */ "\x00\xaf\x00\xaf\x00\xaf", /* 175% */ "\x00\xc0\x00\xc0\x00\xc0", /* 200% */ "\x00\xe1\x00\xe1\x00\xe1", /* 225% */ "\x00\xfa\x00\xfa\x00\xfa", /* 250% */ "\x01\x13\x01\x13\x01\x13", /* 275% */ "\x01\x24\x01\x24\x01\x24", /* 300% */ "\x00\x64\x00\xc0\x01\x24"}; /* Negatives */ /* Negatives get some extra blue to penetrate the orange mask and less red to not saturate the red channel; R:G:B = 100:200:300 */ /* We don't use the 100% case. It may cause mechanical problems */ if ((option < 1) || (option > 9)) return 0; RETURN_IF_FAIL ( sanei_hp_scl_upload_binary (scsi, SCL_CALIB_MAP, &calib_size, &calib_buf) ); DBG(3, "_program_ps_exposure_time: Got %lu bytes of calibration data\n", (unsigned long) calib_size); for (i = 0; i < 6; i++) calib_buf[24 + i] = exposure[option][i]; status = sanei_hp_scl_download ( scsi, SCL_CALIB_MAP, calib_buf, (size_t)calib_size); /* see what the scanner did to our alterations */ /* * RETURN_IF_FAIL ( sanei_hp_scl_upload_binary (scsi, SCL_CALIB_MAP, * &calib_size, &calib_buf) ); * * for (i = 0; i < 9; i++) * DBG(1, ">%x ", (unsigned char) calib_buf[24 + i]); */ sanei_hp_free (calib_buf); return (status); } static SANE_Status _program_scanmode (HpOption this, HpScsi scsi, HpOptSet optset, HpData data) { enum hp_scanmode_e new_mode = hp_option_getint(this, data); int invert = 0; int fw_invert = 0; /* Flag: does firmware do inversion ? */ int is_model_4c = 0; enum hp_device_compat_e compat; hp_bool_t disable_xpa = ( sanei_hp_optset_scan_type (optset, data) != SCL_XPA_SCAN ); /* Seems that models 3c/4c/6100C invert image data at 10 bit by themself. */ /* So we must not invert it by the invert command. */ if ( (sanei_hp_device_probe (&compat,scsi) == SANE_STATUS_GOOD) && (compat & HP_COMPAT_4C) ) { is_model_4c = 1; DBG(3, "program_scanmode: model 3c/4c/6100C recognized\n"); } if (is_model_4c) { const HpDeviceInfo *info; int data_width; HpOption option; int is_preview = 0; /* Preview uses maximum 8 bit. So we don't need to check data width */ option = hp_optset_getByName (optset, SANE_NAME_PREVIEW); if ( option ) is_preview = hp_option_getint (option, data); info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); if ( (!is_preview) && hp_optset_isEnabled (optset, data, SANE_NAME_BIT_DEPTH, info)) { data_width = sanei_hp_optset_data_width (optset, data); if ((data_width == 10) || (data_width == 30)) { fw_invert = 1; DBG(3, "program_scanmode: firmware is doing inversion\n"); } } } /* Disabling XPA resets some settings in the scanner. */ /* Scanmode is the first we program. So set XPA prior to scanmode */ DBG(3, "program_scanmode: disable XPA = %d\n", (int)disable_xpa); sanei_hp_scl_set(scsi, SCL_XPA_DISABLE, disable_xpa); RETURN_IF_FAIL( hp_option_download(this, data, optset, scsi) ); switch (new_mode) { case HP_SCANMODE_GRAYSCALE: /* Make sure that it is not b/w. Correct data width will be set later */ RETURN_IF_FAIL( sanei_hp_scl_set(scsi, SCL_DATA_WIDTH, 8) ); invert = 1; if (fw_invert) invert = 0; /* For active XPA we use a tone map. Dont invert */ if ( (!disable_xpa) && sanei_hp_is_active_xpa (scsi) ) invert = 0; break; case HP_SCANMODE_COLOR: invert = 1; if (fw_invert) invert = 0; /* For active XPA we use a tone map. Dont invert */ if ( (!disable_xpa) && sanei_hp_is_active_xpa (scsi) ) invert = 0; break; default: break; } return sanei_hp_scl_set(scsi, SCL_INVERSE_IMAGE, invert); } static SANE_Status _program_mirror_horiz (HpOption this, HpScsi scsi, HpOptSet __sane_unused__ optset, HpData data) { int sec_dir, mirror = hp_option_getint(this, data); if ( mirror == HP_MIRROR_HORIZ_CONDITIONAL ) { RETURN_IF_FAIL( sanei_hp_scl_inquire(scsi, SCL_SECONDARY_SCANDIR, &sec_dir, 0, 0) ); mirror = (sec_dir == 1); } return sanei_hp_scl_set(scsi, SCL_MIRROR_IMAGE, mirror); } /* * Option enable predicates */ static hp_bool_t _enable_choice (HpOption this, HpOptSet optset, HpData data, const HpDeviceInfo *info) { SANE_String_Const * strlist = sanei_hp_accessor_choice_strlist((HpAccessorChoice) this->data_acsr, optset, data, info); _set_stringlist(this, data, strlist); assert(strlist[0]); return strlist[0] != 0; } #ifdef ENABLE_7x12_TONEMAPS static hp_bool_t _enable_rgb_maps (HpOption this, HpOptSet optset, HpData data, const HpDeviceInfo *info) { HpOption cgam = hp_optset_get(optset, CUSTOM_GAMMA); return (cgam && hp_option_getint(cgam, data) && sanei_hp_optset_scanmode(optset, data) == HP_SCANMODE_COLOR); } #endif static hp_bool_t _enable_mono_map (HpOption __sane_unused__ this, HpOptSet optset, HpData data, const HpDeviceInfo __sane_unused__ *info) { HpOption cgam = hp_optset_get(optset, CUSTOM_GAMMA); return (cgam && hp_option_getint(cgam, data) && ( sanei_hp_optset_scanmode(optset, data) != HP_SCANMODE_COLOR || ! hp_optset_getByName(optset, SANE_NAME_GAMMA_VECTOR_R) )); } static hp_bool_t _enable_rgb_matrix (HpOption __sane_unused__ this, HpOptSet optset, HpData data, const HpDeviceInfo __sane_unused__ *info) { HpOption type = hp_optset_get(optset, MATRIX_TYPE); return type && hp_option_getint(type, data) == HP_MATRIX_CUSTOM; } static hp_bool_t _enable_brightness (HpOption this, HpOptSet optset, HpData data, const HpDeviceInfo *info) { HpOption cgam = hp_optset_get(optset, CUSTOM_GAMMA); HpScl scl = this->descriptor->scl_command; int simulate; simulate = ( sanei_hp_device_support_get ( info->devname, scl, 0, 0 ) != SANE_STATUS_GOOD ); /* If brightness is simulated, we only do it for gray/color */ if ( simulate ) {HpOption mode = hp_optset_get(optset, SCAN_MODE); int val = hp_option_getint (mode, data); int disable; disable = (val != HP_SCANMODE_GRAYSCALE) && (val != HP_SCANMODE_COLOR); if (disable) { if ( cgam ) /* Disable custom gamma. */ { val = 0; hp_option_set (cgam, data, &val, 0); } return 0; } } return !cgam || !hp_option_getint(cgam, data); } static hp_bool_t _enable_autoback (HpOption __sane_unused__ this, HpOptSet optset, HpData data, const HpDeviceInfo __sane_unused__ *info) { return sanei_hp_optset_scanmode(optset, data) == HP_SCANMODE_LINEART; } static hp_bool_t _enable_custom_gamma (HpOption this, HpOptSet optset, HpData data, const HpDeviceInfo *info) { HpScl scl_tonemap = SCL_8x8TONE_MAP; int id = SCL_INQ_ID(scl_tonemap); int simulate, minval, maxval; SANE_Status status; /* Check if download type supported */ status = sanei_hp_device_support_get ( info->devname, SCL_DOWNLOAD_TYPE, &minval, &maxval); simulate = (status != SANE_STATUS_GOOD) || (id < minval) || (id > maxval); /* If custom gamma is simulated, we only do it for gray/color */ if ( simulate ) {HpOption mode = hp_optset_get(optset, SCAN_MODE); int val; if ( mode ) { val = hp_option_getint (mode, data); if ((val != HP_SCANMODE_GRAYSCALE) && (val != HP_SCANMODE_COLOR)) { val = 0; hp_option_set (this, data, &val, 0); return 0; } } } return 1; } static hp_bool_t _enable_halftone (HpOption __sane_unused__ this, HpOptSet optset, HpData data, const HpDeviceInfo __sane_unused__ *info) { return sanei_hp_optset_scanmode(optset, data) == HP_SCANMODE_HALFTONE; } static hp_bool_t _enable_halftonevec (HpOption __sane_unused__ this, HpOptSet optset, HpData data, const HpDeviceInfo __sane_unused__ *info) { if (sanei_hp_optset_scanmode(optset, data) == HP_SCANMODE_HALFTONE) { HpOption dither = hp_optset_get(optset, HALFTONE_PATTERN); return dither && hp_option_getint(dither, data) == HP_DITHER_CUSTOM; } return 0; } static hp_bool_t _enable_data_width (HpOption __sane_unused__ this, HpOptSet optset, HpData data, const HpDeviceInfo __sane_unused__ *info) {enum hp_scanmode_e mode; mode = sanei_hp_optset_scanmode (optset, data); return ( (mode == HP_SCANMODE_GRAYSCALE) || (mode == HP_SCANMODE_COLOR) ); } static hp_bool_t _enable_out8 (HpOption __sane_unused__ this, HpOptSet optset, HpData data, const HpDeviceInfo *info) { if (hp_optset_isEnabled (optset, data, SANE_NAME_BIT_DEPTH, info)) { int data_width = sanei_hp_optset_data_width (optset, data); return (((data_width > 8) && (data_width <= 16)) || (data_width > 24)); } return 0; } static hp_bool_t _enable_calibrate (HpOption __sane_unused__ this, HpOptSet optset, HpData data, const HpDeviceInfo __sane_unused__ *info) { HpOption media = hp_optset_get(optset, MEDIA); /* If we don't have the media button, we should have calibrate */ if ( !media ) return 1; return hp_option_getint(media, data) == HP_MEDIA_PRINT; } static SANE_Status hp_download_calib_file (HpScsi scsi) { int nbytes; char *calib_data; SANE_Status status; RETURN_IF_FAIL ( read_calib_file ( &nbytes, &calib_data, scsi ) ); DBG(3, "hp_download_calib_file: Got %d bytes calibration data\n", nbytes); status = sanei_hp_scl_download ( scsi, SCL_CALIB_MAP, calib_data, (size_t) nbytes); sanei_hp_free ( calib_data ); DBG(3, "hp_download_calib_file: download %s\n", (status == SANE_STATUS_GOOD) ? "successful" : "failed"); return status; } /* * The actual option descriptors. */ #if (defined(__IBMC__) || defined(__IBMCPP__)) #ifndef _AIX #define INT INT #endif #endif #define SCANNER_OPTION(name,type,unit) \ PASTE(SANE_NAME_,name), \ PASTE(SANE_TITLE_,name), \ PASTE(SANE_DESC_,name), \ PASTE(SANE_TYPE_,type), \ PASTE(SANE_UNIT_,unit), \ SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT #define CONSTANT_OPTION(name,type,unit) \ PASTE(SANE_NAME_,name), \ PASTE(SANE_TITLE_,name), \ PASTE(SANE_DESC_,name), \ PASTE(SANE_TYPE_,type), \ PASTE(SANE_UNIT_,unit), \ SANE_CAP_SOFT_DETECT #define INTERNAL_OPTION(name,type,unit) \ PASTE(HP_NAME_,name), "", "", \ PASTE(SANE_TYPE_,type), \ PASTE(SANE_UNIT_,unit), \ SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT #define OPTION_GROUP(name) "", name, "", SANE_TYPE_GROUP, SANE_UNIT_NONE, 0 #define ADVANCED_GROUP(name) \ "", name, "", SANE_TYPE_GROUP, SANE_UNIT_NONE, SANE_CAP_ADVANCED #define REQUIRES(req) req #define NO_REQUIRES REQUIRES(0) static const struct hp_option_descriptor_s NUM_OPTIONS[1] = {{ CONSTANT_OPTION(NUM_OPTIONS, INT, NONE), NO_REQUIRES, _probe_num_options, 0,0,0,0,0,0,0,0,0,0,0,0 /* for gcc-s sake */ }}; static const struct hp_option_descriptor_s SCAN_MODE_GROUP[1] = {{ OPTION_GROUP(SANE_I18N("Scan Mode")), 0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* for gcc-s sake */ }}; /* Preview stuff */ static const struct hp_option_descriptor_s PREVIEW_MODE[1] = {{ SCANNER_OPTION(PREVIEW, BOOL, NONE), NO_REQUIRES, _probe_bool, 0,0,0,0,0,0,0,0,0,0,0,0 /* for gcc-s sake */ }}; static const struct hp_choice_s _scanmode_choices[] = { { HP_SCANMODE_LINEART, SANE_VALUE_SCAN_MODE_LINEART, 0, 0, 0 }, { HP_SCANMODE_HALFTONE, SANE_VALUE_SCAN_MODE_HALFTONE, 0, 0, 0 }, { HP_SCANMODE_GRAYSCALE, SANE_VALUE_SCAN_MODE_GRAY, 0, 0, 0 }, { HP_SCANMODE_COLOR, SANE_VALUE_SCAN_MODE_COLOR, 0, 0, 0 }, { 0, 0, 0, 0, 0 } }; static const struct hp_option_descriptor_s SCAN_MODE[1] = {{ SCANNER_OPTION(SCAN_MODE, STRING, NONE), NO_REQUIRES, _probe_each_choice, _program_scanmode, 0, 1, 1, 1, 0, 0, SCL_OUTPUT_DATA_TYPE, 0, 0, 0, _scanmode_choices }}; static const struct hp_option_descriptor_s SCAN_RESOLUTION[1] = {{ SCANNER_OPTION(SCAN_RESOLUTION, INT, DPI), NO_REQUIRES, _probe_resolution, _program_resolution, 0, 0, 1, 0, 0, 1, SCL_X_RESOLUTION, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s DEVPIX_RESOLUTION[1] = {{ INTERNAL_OPTION(DEVPIX_RESOLUTION, INT, DPI), NO_REQUIRES, _probe_devpix, 0, 0, 0, 0, 0, 0, 1, SCL_DEVPIX_RESOLUTION, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s ENHANCEMENT_GROUP[1] = {{ OPTION_GROUP(SANE_I18N("Enhancement")), 0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* for gcc-s sake */ }}; static const struct hp_option_descriptor_s BRIGHTNESS[1] = {{ SCANNER_OPTION(BRIGHTNESS, INT, NONE), NO_REQUIRES, _probe_int_brightness, _program_generic_simulate, _enable_brightness, 0, 0, 0, 0, 0, SCL_BRIGHTNESS, -127, 127, 0, 0 }}; static const struct hp_option_descriptor_s CONTRAST[1] = {{ SCANNER_OPTION(CONTRAST, INT, NONE), NO_REQUIRES, _probe_int_brightness, _program_generic_simulate, _enable_brightness, 0, 0, 0, 0, 0, SCL_CONTRAST, -127, 127, 0, 0 }}; #ifdef SCL_SHARPENING static const struct hp_option_descriptor_s SHARPENING[1] = {{ SCANNER_OPTION(SHARPENING, INT, NONE), NO_REQUIRES, _probe_int, _program_generic, 0, 0, 0, 0, 0, 0, SCL_SHARPENING, -127, 127, 0, 0 }}; #endif static const struct hp_option_descriptor_s AUTO_THRESHOLD[1] = {{ SCANNER_OPTION(AUTO_THRESHOLD, BOOL, NONE), NO_REQUIRES, _probe_bool, _program_generic, _enable_autoback, 0, 0, 0, 0, 0, SCL_AUTO_BKGRND, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s ADVANCED_GROUP[1] = {{ ADVANCED_GROUP(SANE_I18N("Advanced Options")), 0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* for gcc-s sake */ }}; /* FIXME: make this a choice? (BW or RGB custom) */ static const struct hp_option_descriptor_s CUSTOM_GAMMA[1] = {{ SCANNER_OPTION(CUSTOM_GAMMA, BOOL, NONE), NO_REQUIRES, _probe_custom_gamma, _program_tonemap, _enable_custom_gamma, 1, 0, 0, 0, 0, SCL_TONE_MAP, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s GAMMA_VECTOR_8x8[1] = {{ SCANNER_OPTION(GAMMA_VECTOR, FIXED, NONE), NO_REQUIRES, _probe_gamma_vector, 0, _enable_mono_map, 0, 0, 0, 0, 0, SCL_8x8TONE_MAP, 0, 0, 0, 0 }}; #ifdef ENABLE_7x12_TONEMAPS static const struct hp_option_descriptor_s GAMMA_VECTOR_7x12[1] = {{ SCANNER_OPTION(GAMMA_VECTOR, FIXED, NONE), REQUIRES(HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_PS | HP_COMPAT_6200C |HP_COMPAT_5200C|HP_COMPAT_6300C), _probe_gamma_vector, 0, _enable_mono_map, 0, 0, 0, 0, 0, SCL_BW7x12TONE_MAP, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s RGB_TONEMAP[1] = {{ INTERNAL_OPTION(RGB_TONEMAP, FIXED, NONE), REQUIRES(HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_PS | HP_COMPAT_6200C |HP_COMPAT_5200C|HP_COMPAT_6300C), _probe_gamma_vector, 0, 0, 0, 0, 0, 0, 0, SCL_7x12TONE_MAP, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s GAMMA_VECTOR_R[1] = {{ SCANNER_OPTION(GAMMA_VECTOR_R, FIXED, NONE), REQUIRES(HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_PS | HP_COMPAT_6200C |HP_COMPAT_5200C|HP_COMPAT_6300C), _probe_gamma_vector, 0, _enable_rgb_maps, 0,0,0,0,0,0,0,0,0,0 }}; static const struct hp_option_descriptor_s GAMMA_VECTOR_G[1] = {{ SCANNER_OPTION(GAMMA_VECTOR_G, FIXED, NONE), REQUIRES(HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_PS | HP_COMPAT_6200C |HP_COMPAT_5200C|HP_COMPAT_6300C), _probe_gamma_vector, 0, _enable_rgb_maps, 0,0,0,0,0,0,0,0,0,0 }}; static const struct hp_option_descriptor_s GAMMA_VECTOR_B[1] = {{ SCANNER_OPTION(GAMMA_VECTOR_B, FIXED, NONE), REQUIRES(HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_PS | HP_COMPAT_6200C |HP_COMPAT_5200C|HP_COMPAT_6300C), _probe_gamma_vector, 0, _enable_rgb_maps, 0,0,0,0,0,0,0,0,0,0 }}; #endif static const struct hp_choice_s _halftone_choices[] = { { HP_DITHER_COARSE, SANE_I18N("Coarse"), 0, 0, 0 }, { HP_DITHER_FINE, SANE_I18N("Fine"), 0, 0, 0 }, { HP_DITHER_BAYER, SANE_I18N("Bayer"), 0, 0, 0 }, { HP_DITHER_VERTICAL, SANE_I18N("Vertical"), 0, 0, 0 }, { HP_DITHER_HORIZONTAL, SANE_I18N("Horizontal"), 0, 1, 0 }, { HP_DITHER_CUSTOM, SANE_I18N("Custom"), 0, 0, 0 }, { 0, 0, 0, 0, 0 } }; static const struct hp_option_descriptor_s HALFTONE_PATTERN[1] = {{ SCANNER_OPTION(HALFTONE_PATTERN, STRING, NONE), NO_REQUIRES, _probe_each_choice, _program_dither, _enable_halftone, 1, 0, 0, 0, 0, SCL_BW_DITHER, 0, 0, 0, _halftone_choices }}; /* FIXME: Halftone dimension? */ #ifdef ENABLE_16X16_DITHERS static const struct hp_option_descriptor_s HALFTONE_PATTERN_16x16[1] = {{ SCANNER_OPTION(HALFTONE_PATTERN, FIXED, NONE), REQUIRES(HP_COMPAT_5P | HP_COMPAT_4P | HP_COMPAT_4C | HP_COMPAT_5100C | HP_COMPAT_6200C | HP_COMPAT_5200C | HP_COMPAT_6300C), _probe_horiz_dither, 0, _enable_halftonevec, 0, 0, 0, 0, 0, SCL_BW16x16DITHER }}; static const struct hp_option_descriptor_s HORIZONTAL_DITHER_16x16[1] = {{ INTERNAL_OPTION(HORIZONTAL_DITHER, FIXED, NONE), REQUIRES(HP_COMPAT_5P | HP_COMPAT_4P | HP_COMPAT_4C | HP_COMPAT_5100C | HP_COMPAT_6200C | HP_COMPAT_5200C | HP_COMPAT_6300C), _probe_horiz_dither, 0, 0, 0, 0, 0, 0, 0, SCL_BW16x16DITHER }}; #endif static const struct hp_option_descriptor_s HALFTONE_PATTERN_8x8[1] = {{ SCANNER_OPTION(HALFTONE_PATTERN, FIXED, NONE), NO_REQUIRES, _probe_horiz_dither, 0, _enable_halftonevec, 0, 0, 0, 0, 0, SCL_BW8x8DITHER, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s HORIZONTAL_DITHER_8x8[1] = {{ INTERNAL_OPTION(HORIZONTAL_DITHER, FIXED, NONE), NO_REQUIRES, _probe_horiz_dither, 0, 0, 0, 0, 0, 0, 0, SCL_BW8x8DITHER, 0, 0, 0, 0 }}; static const struct hp_choice_s _matrix_choices[] = { { HP_MATRIX_AUTO, SANE_I18N("Auto"), 0, 1, 0 }, { HP_MATRIX_RGB, SANE_I18N("NTSC RGB"), _cenable_incolor, 0, 0 }, { HP_MATRIX_XPA_RGB, SANE_I18N("XPA RGB"), _cenable_incolor, 0, 0 }, { HP_MATRIX_PASS, SANE_I18N("Pass-through"), _cenable_incolor, 0, 0 }, { HP_MATRIX_BW, SANE_I18N("NTSC Gray"), _cenable_notcolor, 0, 0 }, { HP_MATRIX_XPA_BW, SANE_I18N("XPA Gray"), _cenable_notcolor, 0, 0 }, { HP_MATRIX_RED, SANE_I18N("Red"), _cenable_notcolor, 0, 0 }, { HP_MATRIX_GREEN, SANE_I18N("Green"), _cenable_notcolor, 1, 0 }, { HP_MATRIX_BLUE, SANE_I18N("Blue"), _cenable_notcolor, 0, 0 }, #ifdef ENABLE_CUSTOM_MATRIX { HP_MATRIX_CUSTOM, SANE_I18N("Custom"), 0, 0, 0 }, #endif { 0, 0, 0, 0, 0 } }; static const struct hp_option_descriptor_s MATRIX_TYPE[1] = {{ SCANNER_OPTION(MATRIX_TYPE, STRING, NONE), NO_REQUIRES, _probe_each_choice, _program_matrix, _enable_choice, 1, 0, 0, 0, 0, SCL_MATRIX, 0, 0, 0, _matrix_choices }}; static const struct hp_option_descriptor_s MATRIX_RGB[1] = {{ SCANNER_OPTION(MATRIX_RGB, FIXED, NONE), NO_REQUIRES, _probe_matrix, 0, _enable_rgb_matrix, 0, 0, 0, 0, 0, SCL_8x9MATRIX_COEFF, 0, 0, 0, 0 }}; #ifdef FAKE_COLORSEP_MATRIXES static const struct hp_option_descriptor_s SEPMATRIX[1] = {{ INTERNAL_OPTION(SEPMATRIX, FIXED, NONE), NO_REQUIRES, _probe_vector, 0, 0, 0, 0, 0, 0, 0, SCL_8x9MATRIX_COEFF, 0, 0, 0, 0 }}; #endif #ifdef ENABLE_10BIT_MATRIXES static const struct hp_option_descriptor_s MATRIX_RGB10[1] = {{ SCANNER_OPTION(MATRIX_RGB, FIXED, NONE), REQUIRES(HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_6200C | HP_COMPAT_5200C | HP_COMPAT_6300C), _probe_matrix, 0, _enable_rgb_matrix, 0, 0, 0, 0, 0, SCL_10x9MATRIX_COEFF, 0, 0, 0, 0 }}; #endif #ifdef NotYetSupported static const struct hp_option_descriptor_s BWMATRIX_GRAY10[1] = {{ SCANNER_OPTION(MATRIX_GRAY, FIXED, NONE), REQUIRES(HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_6200C | HP_COMPAT_5200C | HP_COMPAT_6300C), _probe_matrix, 0, _enable_gray_matrix, 0, 0, 0, 0, 0, SCL_10x3MATRIX_COEFF, 0, 0, 0, 0 }}; #endif static const struct hp_choice_s _scan_speed_choices[] = { { 0, SANE_I18N("Auto"), 0, 0, 0 }, { 1, SANE_I18N("Slow"), 0, 0, 0 }, { 2, SANE_I18N("Normal"), 0, 0, 0 }, { 3, SANE_I18N("Fast"), 0, 0, 0 }, { 4, SANE_I18N("Extra Fast"), 0, 0, 0 }, { 0, 0, 0, 0, 0 } }; static const struct hp_option_descriptor_s SCAN_SPEED[1] = {{ SCANNER_OPTION(SCAN_SPEED, STRING, NONE), NO_REQUIRES, _probe_each_choice, _program_generic, 0, 0, 0, 0, 0, 1, SCL_SPEED, 0, 0, 0, _scan_speed_choices }}; static const struct hp_choice_s _smoothing_choices[] = { { 0, SANE_I18N("Auto"), 0, 0, 0 }, { 3, SANE_I18N("Off"), 0, 0, 0 }, { 1, SANE_I18N("2-pixel"), 0, 0, 0 }, { 2, SANE_I18N("4-pixel"), 0, 0, 0 }, { 4, SANE_I18N("8-pixel"), 0, 0, 0 }, { 0, 0, 0, 0, 0 } }; static const struct hp_option_descriptor_s SMOOTHING[1] = {{ SCANNER_OPTION(SMOOTHING, STRING, NONE), NO_REQUIRES, _probe_each_choice, _program_generic, 0, 0, 0, 0, 0, 0, SCL_FILTER, 0, 0, 0, _smoothing_choices }}; static const struct hp_choice_s _media_choices[] = { { HP_MEDIA_PRINT, SANE_I18N("Print"), 0, 0, 0 }, { HP_MEDIA_SLIDE, SANE_I18N("Slide"), 0, 0, 0 }, { HP_MEDIA_NEGATIVE, SANE_I18N("Film-strip"), 0, 0, 0 }, { 0, 0, 0, 0, 0 } }; static const struct hp_option_descriptor_s MEDIA[1] = {{ SCANNER_OPTION(MEDIA, STRING, NONE), NO_REQUIRES, _probe_choice, _program_media, 0, 1, 1, 1, 1, 0, SCL_MEDIA, 0, 0, 0, _media_choices }}; static const struct hp_choice_s _data_widths[] = { {1, "1", 0, 0, 0}, {8, "8", 0, 0, 0}, {10, "10", 0, 0, 0}, {12, "12", 0, 0, 0}, {14, "14", 0, 0, 0}, {16, "16", 0, 0, 0}, {0, 0, 0, 0, 0} }; static const struct hp_option_descriptor_s BIT_DEPTH[1] = {{ SCANNER_OPTION(BIT_DEPTH, STRING, NONE), NO_REQUIRES, _probe_choice, _program_data_width, _enable_data_width, 1, 1, 1, 0, 1, SCL_DATA_WIDTH, 0, 0, 0, _data_widths }}; static const struct hp_option_descriptor_s OUT8[1] = { { SCANNER_OPTION(OUTPUT_8BIT, BOOL, NONE), NO_REQUIRES, /* enum hp_device_compat_e requires */ _probe_bool, /* SANE_Status (*probe)() */ 0, /* SANE_Status (*program)() */ _enable_out8, /* hp_bool_t (*enable)() */ 0, /* hp_bool_t has_global_effect */ 0, /* hp_bool_t affects_scan_params */ 0, /* hp_bool_t program_immediate */ 0, /* hp_bool_t suppress_for_scan */ 0, /* hp_bool_t may_change */ 0, /* HpScl scl_command */ 0, /* int minval */ 0, /* int maxval */ 0, /* int startval */ 0 /* HpChoice choices */ } }; /* The 100% setting may cause problems within the scanner */ static const struct hp_choice_s _ps_exposure_times[] = { /* {0, "100%", 0, 0, 0}, */ { 0, SANE_I18N("Default"), 0, 0, 0 }, {1, "125%", 0, 0, 0}, {2, "150%", 0, 0, 0}, {3, "175%", 0, 0, 0}, {4, "200%", 0, 0, 0}, {5, "225%", 0, 0, 0}, {6, "250%", 0, 0, 0}, {7, "275%", 0, 0, 0}, {8, "300%", 0, 0, 0}, {9, SANE_I18N("Negative"), 0, 0, 0}, {0, 0, 0, 0, 0} }; /* Photosmart exposure time */ static const struct hp_option_descriptor_s PS_EXPOSURE_TIME[1] = {{ SCANNER_OPTION(PS_EXPOSURE_TIME, STRING, NONE), REQUIRES( HP_COMPAT_PS ), _probe_ps_exposure_time, _program_ps_exposure_time, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _ps_exposure_times }}; /* Normal, ADF or XPA scanning. Because scanning from ADF can change */ /* the extent of the scanning area, this option is marked to change */ /* global settings. The user should switch to ADF scanning after */ /* placing paper in the ADF. */ static const struct hp_choice_s _scan_types[] = { { HP_SCANTYPE_NORMAL, SANE_I18N("Normal"), 0, 0, 0 }, { HP_SCANTYPE_ADF, SANE_I18N("ADF"), 0, 0, 0 }, { HP_SCANTYPE_XPA, SANE_I18N("XPA"), 0, 0, 0 }, {0, 0, 0, 0, 0 } }; static const struct hp_option_descriptor_s SCAN_SOURCE[1] = {{ SCANNER_OPTION(SCAN_SOURCE, STRING, NONE), NO_REQUIRES, _probe_scan_type, _program_scan_type, 0, 1, 1, 1, 0, 0, SCL_START_SCAN, 0, 0, 0, _scan_types }}; /* Unload after is only necessary for PhotoScanner */ static const struct hp_option_descriptor_s UNLOAD_AFTER_SCAN[1] = {{ SCANNER_OPTION(UNLOAD_AFTER_SCAN, BOOL, NONE), REQUIRES(HP_COMPAT_PS), _probe_bool, _program_unload_after_scan, 0, 0, 0, 0, 1, 0, SCL_UNLOAD, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s CHANGE_DOC[1] = {{ SCANNER_OPTION(CHANGE_DOC, BUTTON, NONE), NO_REQUIRES, _probe_change_doc, _program_change_doc, 0, 1, 1, 1, 1, 0, SCL_CHANGE_DOC, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s UNLOAD[1] = {{ SCANNER_OPTION(UNLOAD, BUTTON, NONE), NO_REQUIRES, _probe_unload, _program_unload, 0, 0, 0, 1, 1, 0, SCL_UNLOAD, 0, 0, 0, 0 }}; /* There is no inquire ID-for the calibrate command. */ /* So here we need the requiries. */ static const struct hp_option_descriptor_s CALIBRATE[1] = {{ SCANNER_OPTION(CALIBRATE, BUTTON, NONE), REQUIRES(HP_COMPAT_PS), _probe_calibrate, _program_calibrate, _enable_calibrate, 0, 0, 1, 1, 0, SCL_CALIBRATE, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s GEOMETRY_GROUP[1] = {{ ADVANCED_GROUP(SANE_I18N("Geometry")), 0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* for gcc-s sake */ }}; static const struct hp_option_descriptor_s SCAN_TL_X[1] = {{ SCANNER_OPTION(SCAN_TL_X, FIXED, MM), NO_REQUIRES, _probe_geometry, _program_geometry, 0, 0, 1, 0, 0, 1, SCL_X_POS, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s SCAN_TL_Y[1] = {{ SCANNER_OPTION(SCAN_TL_Y, FIXED, MM), NO_REQUIRES, _probe_geometry, _program_geometry, 0, 0, 1, 0, 0, 1, SCL_Y_POS, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s SCAN_BR_X[1] = {{ SCANNER_OPTION(SCAN_BR_X, FIXED, MM), NO_REQUIRES, _probe_geometry, _program_geometry, 0, 0, 1, 0, 0, 1, SCL_X_EXTENT, 0, 0, 0, 0 }}; static const struct hp_option_descriptor_s SCAN_BR_Y[1] = {{ SCANNER_OPTION(SCAN_BR_Y, FIXED, MM), NO_REQUIRES, _probe_geometry, _program_geometry, 0, 0, 1, 0, 0, 1, SCL_Y_EXTENT, 0, 0, 0, 0 }}; static const struct hp_choice_s _mirror_horiz_choices[] = { { HP_MIRROR_HORIZ_OFF, SANE_I18N("Off"), 0, 0, 0 }, { HP_MIRROR_HORIZ_ON, SANE_I18N("On"), 0, 0, 0 }, { HP_MIRROR_HORIZ_CONDITIONAL, SANE_I18N("Conditional"), 0, 0, 0 }, { 0, 0, 0, 0, 0 } }; static const struct hp_option_descriptor_s MIRROR_HORIZ[1] = {{ SCANNER_OPTION(MIRROR_HORIZ, STRING, NONE), NO_REQUIRES, _probe_mirror_horiz, _program_mirror_horiz, 0, 0, 0, 0, 0, 0, SCL_MIRROR_IMAGE, 0, 0, 0, _mirror_horiz_choices }}; static const struct hp_choice_s _mirror_vert_choices[] = { { HP_MIRROR_VERT_OFF, SANE_I18N("Off"), 0, 0, 0 }, { HP_MIRROR_VERT_ON, SANE_I18N("On"), 0, 0, 0 }, { HP_MIRROR_VERT_CONDITIONAL, SANE_I18N("Conditional"), 0, 0, 0 }, { 0, 0, 0, 0, 0 } }; static const struct hp_option_descriptor_s MIRROR_VERT[1] = {{ SCANNER_OPTION(MIRROR_VERT, STRING, NONE), NO_REQUIRES, _probe_mirror_vert, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _mirror_vert_choices }}; static const struct hp_option_descriptor_s BUTTON_WAIT[1] = { { SCANNER_OPTION(BUTTON_WAIT, BOOL, NONE), NO_REQUIRES, /* enum hp_device_compat_e requires */ _probe_front_button, /* SANE_Status (*probe)() */ 0, /* SANE_Status (*program)() */ 0, /* hp_bool_t (*enable)() */ 0, /* hp_bool_t has_global_effect */ 0, /* hp_bool_t affects_scan_params */ 0, /* hp_bool_t program_immediate */ 0, /* hp_bool_t suppress_for_scan */ 0, /* hp_bool_t may_change */ 0, /* HpScl scl_command */ 0, /* int minval */ 0, /* int maxval */ 0, /* int startval */ 0 /* HpChoice choices */ } }; static const struct hp_option_descriptor_s LAMP_OFF[1] = { { SCANNER_OPTION(LAMP_OFF, BUTTON, NONE), /* Lamp off instruction not supported by Photosmart */ REQUIRES( HP_COMPAT_PLUS | HP_COMPAT_2C | HP_COMPAT_2P | HP_COMPAT_2CX | HP_COMPAT_4C | HP_COMPAT_3P | HP_COMPAT_4P | HP_COMPAT_5P | HP_COMPAT_5100C | HP_COMPAT_6200C | HP_COMPAT_5200C | HP_COMPAT_6300C), /* enum hp_device_compat_e requires */ _probe_bool, /* SANE_Status (*probe)() */ _program_lamp_off, /* SANE_Status (*program)() */ 0, /* hp_bool_t (*enable)() */ 0, /* hp_bool_t has_global_effect */ 0, /* hp_bool_t affects_scan_params */ 1, /* hp_bool_t program_immediate */ 1, /* hp_bool_t suppress_for_scan */ 0, /* hp_bool_t may_change */ SCL_LAMPTEST, /* HpScl scl_command */ 0, /* int minval */ 0, /* int maxval */ 0, /* int startval */ 0 /* HpChoice choices */ } }; #ifdef HP_EXPERIMENTAL static const struct hp_choice_s _range_choices[] = { { 0, "0", 0, 0, 0 }, { 1, "1", 0, 0, 0 }, { 2, "2", 0, 0, 0 }, { 3, "3", 0, 0, 0 }, { 4, "4", 0, 0, 0 }, { 5, "5", 0, 0, 0 }, { 6, "6", 0, 0, 0 }, { 7, "7", 0, 0, 0 }, { 8, "8", 0, 0, 0 }, { 9, "9", 0, 0, 0 }, { 0, 0, 0, 0, 0 } }; static const struct hp_option_descriptor_s EXPERIMENT_GROUP[1] = {{ ADVANCED_GROUP(SANE_I18N("Experiment")) 0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* for gcc-s sake */ }}; static const struct hp_option_descriptor_s PROBE_10470[1] = {{ SCANNER_OPTION(10470, STRING, NONE), NO_REQUIRES, _probe_each_choice, _program_generic, 0, 0, 0, 0, 0, 0, SCL_10470, 0, 0, 0, _range_choices }}; static const struct hp_option_descriptor_s PROBE_10485[1] = {{ SCANNER_OPTION(10485, STRING, NONE), NO_REQUIRES, _probe_each_choice, _program_generic, 0, 0, 0, 0, 0, 0, SCL_10485, 0, 0, 0, _range_choices }}; static const struct hp_option_descriptor_s PROBE_10952[1] = {{ SCANNER_OPTION(10952, STRING, NONE), NO_REQUIRES, _probe_each_choice, _program_generic, 0, 0, 0, 0, 0, 0, SCL_10952, 0, 0, 0, _range_choices }}; static const struct hp_option_descriptor_s PROBE_10967[1] = {{ SCANNER_OPTION(10967, INT, NONE), NO_REQUIRES, _probe_int, _program_generic, 0, 0, 0, 0, 0, 0, SCL_10967, 0, 0, 0, 0 }}; #endif static HpOptionDescriptor hp_options[] = { NUM_OPTIONS, SCAN_MODE_GROUP, PREVIEW_MODE, SCAN_MODE, SCAN_RESOLUTION, DEVPIX_RESOLUTION, ENHANCEMENT_GROUP, BRIGHTNESS, CONTRAST, #ifdef SCL_SHARPENING SHARPENING, #endif AUTO_THRESHOLD, ADVANCED_GROUP, CUSTOM_GAMMA, #ifdef ENABLE_7x12_TONEMAPS GAMMA_VECTOR_7x12, RGB_TONEMAP, GAMMA_VECTOR_R, GAMMA_VECTOR_G, GAMMA_VECTOR_B, #endif GAMMA_VECTOR_8x8, MATRIX_TYPE, #ifdef FAKE_COLORSEP_MATRIXES SEPMATRIX, #endif #ifdef ENABLE_10BIT_MATRIXES MATRIX_RGB10, /* FIXME: unsupported: MATRIX_GRAY10, */ #endif MATRIX_RGB, HALFTONE_PATTERN, #ifdef ENABLE_16X16_DITHERS HALFTONE_PATTERN_16x16, HORIZONTAL_DITHER_16x16, #endif HALFTONE_PATTERN_8x8, HORIZONTAL_DITHER_8x8, SCAN_SPEED, SMOOTHING, MEDIA, PS_EXPOSURE_TIME, BIT_DEPTH, OUT8, SCAN_SOURCE, BUTTON_WAIT, LAMP_OFF, UNLOAD_AFTER_SCAN, CHANGE_DOC, UNLOAD, CALIBRATE, GEOMETRY_GROUP, SCAN_TL_X, SCAN_TL_Y, SCAN_BR_X, SCAN_BR_Y, MIRROR_HORIZ, MIRROR_VERT, #ifdef HP_EXPERIMENTAL EXPERIMENT_GROUP, PROBE_10470, PROBE_10485, PROBE_10952, PROBE_10967, #endif 0 }; /* * class HpOptSet */ struct hp_optset_s { #define OPTION_LIST_MAX sizeof(hp_options)/sizeof(hp_options[0]) HpOption options[OPTION_LIST_MAX]; size_t num_sane_opts; size_t num_opts; /* Magic accessors to get coord in actual scan pixels: */ HpAccessor tl_x, tl_y, br_x, br_y; }; static HpOption hp_optset_get (HpOptSet this, HpOptionDescriptor optd) { HpOption * optp = this->options; int i = this->num_opts; while (i--) { if ((*optp)->descriptor == optd) return *optp; optp++; } return 0; } static HpOption hp_optset_getByIndex (HpOptSet this, int optnum) { if ((optnum < 0) || (optnum >= (int)this->num_sane_opts)) return 0; return this->options[optnum]; } static HpOption hp_optset_getByName (HpOptSet this, const char * name) { HpOption * optp = this->options; int i = this->num_opts; while (i--) { if (strcmp((*optp)->descriptor->name, name) == 0) return *optp; optp++; } return 0; } static _HpOption _hp_optset_get (HpOptSet this, HpOptionDescriptor opt) { /* Cast away const-ness */ return (_HpOption) hp_optset_get(this, opt); } enum hp_scanmode_e sanei_hp_optset_scanmode (HpOptSet this, HpData data) { HpOption mode = hp_optset_get(this, SCAN_MODE); assert(mode); return hp_option_getint(mode, data); } hp_bool_t sanei_hp_optset_output_8bit (HpOptSet this, HpData data) { HpOption option_out8; int out8; option_out8 = hp_optset_get(this, OUT8); if (option_out8) { out8 = hp_option_getint(option_out8, data); return out8; } return 0; } /* Returns the data width that is send to the scanner, depending */ /* on the scanmode. (b/w: 1, gray: 8..12, color: 24..36 */ int sanei_hp_optset_data_width (HpOptSet this, HpData data) { enum hp_scanmode_e mode = sanei_hp_optset_scanmode (this, data); int datawidth = 0; HpOption opt_dwidth; switch (mode) { case HP_SCANMODE_LINEART: case HP_SCANMODE_HALFTONE: datawidth = 1; break; case HP_SCANMODE_GRAYSCALE: opt_dwidth = hp_optset_get(this, BIT_DEPTH); if (opt_dwidth) datawidth = hp_option_getint (opt_dwidth, data); else datawidth = 8; break; case HP_SCANMODE_COLOR: opt_dwidth = hp_optset_get(this, BIT_DEPTH); if (opt_dwidth) datawidth = 3 * hp_option_getint (opt_dwidth, data); else datawidth = 24; break; } return datawidth; } hp_bool_t sanei_hp_optset_mirror_vert (HpOptSet this, HpData data, HpScsi scsi) { HpOption mode; int mirror, sec_dir; mode = hp_optset_get(this, MIRROR_VERT); assert(mode); mirror = hp_option_getint(mode, data); if (mirror == HP_MIRROR_VERT_CONDITIONAL) { mirror = HP_MIRROR_VERT_OFF; if ( ( sanei_hp_scl_inquire(scsi, SCL_SECONDARY_SCANDIR, &sec_dir, 0, 0) == SANE_STATUS_GOOD ) && ( sec_dir == 1 ) ) mirror = HP_MIRROR_VERT_ON; } return mirror == HP_MIRROR_VERT_ON; } hp_bool_t sanei_hp_optset_start_wait(HpOptSet this, HpData data) { HpOption mode; int wait; if ((mode = hp_optset_get(this, BUTTON_WAIT)) == 0) return(0); wait = hp_option_getint(mode, data); return(wait); } HpScl sanei_hp_optset_scan_type (HpOptSet this, HpData data) { HpOption mode; HpScl scl = SCL_START_SCAN; int scantype; mode = hp_optset_get(this, SCAN_SOURCE); if (mode) { scantype = hp_option_getint(mode, data); DBG(5, "sanei_hp_optset_scan_type: scantype=%d\n", scantype); switch (scantype) { case 1: scl = SCL_ADF_SCAN; break; case 2: scl = SCL_XPA_SCAN; break; default: scl = SCL_START_SCAN; break; } } return scl; } static void hp_optset_add (HpOptSet this, HpOption opt) { assert(this->num_opts < OPTION_LIST_MAX); /* * Keep internal options at the end of the list. */ if (hp_option_isInternal(opt)) this->options[this->num_opts] = opt; else { if (this->num_opts != this->num_sane_opts) memmove(&this->options[this->num_sane_opts + 1], &this->options[this->num_sane_opts], ( (this->num_opts - this->num_sane_opts) * sizeof(*this->options) )); this->options[this->num_sane_opts++] = opt; } this->num_opts++; } static SANE_Status hp_optset_fix_geometry_options (HpOptSet this) { _HpOption tl_x = _hp_optset_get(this, SCAN_TL_X); _HpOption tl_y = _hp_optset_get(this, SCAN_TL_Y); _HpOption br_x = _hp_optset_get(this, SCAN_BR_X); _HpOption br_y = _hp_optset_get(this, SCAN_BR_Y); HpOption scanres = hp_optset_get(this, SCAN_RESOLUTION); HpOption devpix = hp_optset_get(this, DEVPIX_RESOLUTION); HpAccessor tl_xa, tl_ya, br_xa, br_ya; assert(tl_x && tl_y && br_x && br_y); /* Geometry options missing */ tl_xa = tl_x->data_acsr; tl_ya = tl_y->data_acsr; br_xa = br_x->data_acsr; br_ya = br_y->data_acsr; assert(tl_xa && tl_ya && br_xa && br_ya); assert(scanres->data_acsr && devpix->data_acsr); /* Magic accessors that will read out in device pixels */ tl_x->data_acsr = sanei_hp_accessor_geometry_new(tl_xa, br_xa, 0, devpix->data_acsr); tl_y->data_acsr = sanei_hp_accessor_geometry_new(tl_ya, br_ya, 0, devpix->data_acsr); br_x->data_acsr = sanei_hp_accessor_geometry_new(br_xa, tl_xa, 1, devpix->data_acsr); br_y->data_acsr = sanei_hp_accessor_geometry_new(br_ya, tl_ya, 1, devpix->data_acsr); if (!tl_x->data_acsr || !tl_y->data_acsr || !br_x->data_acsr || !br_y->data_acsr) return SANE_STATUS_NO_MEM; /* Magic accessors that will read out in scan pixels */ this->tl_x = sanei_hp_accessor_geometry_new(tl_xa, br_xa, 0, scanres->data_acsr); this->tl_y = sanei_hp_accessor_geometry_new(tl_ya, br_ya, 0, scanres->data_acsr); this->br_x = sanei_hp_accessor_geometry_new(br_xa, tl_xa, 1, scanres->data_acsr); this->br_y = sanei_hp_accessor_geometry_new(br_ya, tl_ya, 1, scanres->data_acsr); if (!this->tl_x || !this->tl_y || !this->br_x || !this->br_y) return SANE_STATUS_NO_MEM; return SANE_STATUS_GOOD; } static void hp_optset_reprogram (HpOptSet this, HpData data, HpScsi scsi) { int i; DBG(5, "hp_optset_reprogram: %lu options\n", (unsigned long) this->num_opts); for (i = 0; i < (int)this->num_opts; i++) hp_option_reprogram(this->options[i], this, data, scsi); DBG(5, "hp_optset_reprogram: finished\n"); } static void hp_optset_reprobe (HpOptSet this, HpData data, HpScsi scsi) { int i; DBG(5, "hp_optset_reprobe: %lu options\n", (unsigned long) this->num_opts); for (i = 0; i < (int)this->num_opts; i++) hp_option_reprobe(this->options[i], this, data, scsi); DBG(5, "hp_optset_reprobe: finished\n"); } static void hp_optset_updateEnables (HpOptSet this, HpData data, const HpDeviceInfo *info) { int i; DBG(5, "hp_optset_updateEnables: %lu options\n", (unsigned long) this->num_opts); for (i = 0; i < (int)this->num_opts; i++) hp_option_updateEnable(this->options[i], this, data, info); } static hp_bool_t hp_optset_isEnabled (HpOptSet this, HpData data, const char *name, const HpDeviceInfo *info) { HpOption optpt; optpt = hp_optset_getByName (this, name); if (!optpt) /* Not found ? Not enabled */ return 0; if (!(optpt->descriptor->enable)) /* No enable necessary ? Enabled */ return 1; return (*optpt->descriptor->enable)(optpt, this, data, info); } /* This function is only called from sanei_hp_handle_startScan() */ SANE_Status sanei_hp_optset_download (HpOptSet this, HpData data, HpScsi scsi) { int i, errcount = 0; DBG(3, "Start downloading parameters to scanner\n"); /* Reset scanner to wake it up */ /* Reset would switch off XPA lamp and switch on scanner lamp. */ /* Only do a reset if not in active XPA mode */ if ( (sanei_hp_optset_scan_type (this, data) != SCL_XPA_SCAN) || (!sanei_hp_is_active_xpa (scsi)) ) { RETURN_IF_FAIL(sanei_hp_scl_reset (scsi)); } RETURN_IF_FAIL(sanei_hp_scl_clearErrors (scsi)); sanei_hp_device_simulate_clear ( sanei_hp_scsi_devicename (scsi) ); for (i = 0; i < (int)this->num_opts; i++) { if ( (this->options[i])->descriptor->suppress_for_scan ) { DBG(3,"sanei_hp_optset_download: %s suppressed for scan\n", (this->options[i])->descriptor->name); } else { RETURN_IF_FAIL( hp_option_program(this->options[i], scsi, this, data) ); if ( sanei_hp_scl_errcheck (scsi) != SANE_STATUS_GOOD ) { errcount++; DBG(3, "Option %s generated scanner error\n", this->options[i]->descriptor->name); RETURN_IF_FAIL(sanei_hp_scl_clearErrors (scsi)); } } } DBG(3, "Downloading parameters finished.\n"); /* Check preview */ {HpOption option; int is_preview, data_width; const HpDeviceInfo *info; option = hp_optset_getByName (this, SANE_NAME_PREVIEW); if ( option ) { is_preview = hp_option_getint (option, data); if ( is_preview ) { /* For preview we only use 8 bit per channel */ DBG(3, "sanei_hp_optset_download: Set up preview options\n"); info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); if (hp_optset_isEnabled (this, data, SANE_NAME_BIT_DEPTH, info)) { data_width = sanei_hp_optset_data_width (this, data); if (data_width > 24) { sanei_hp_scl_set(scsi, SCL_DATA_WIDTH, 24); } else if ((data_width > 8) && (data_width <= 16)) { sanei_hp_scl_set(scsi, SCL_DATA_WIDTH, 8); } } } } } return SANE_STATUS_GOOD; } SANE_Status sanei_hp_optset_new(HpOptSet * newp, HpScsi scsi, HpDevice dev) { HpOptionDescriptor * ptr; HpOptSet this = sanei_hp_allocz(sizeof(*this)); SANE_Status status; HpOption option; const HpDeviceInfo *info; if (!this) return SANE_STATUS_NO_MEM; /* FIXME: more DBG's */ for (ptr = hp_options; *ptr; ptr++) { HpOptionDescriptor desc = *ptr; DBG(8, "sanei_hp_optset_new: %s\n", desc->name); if (desc->requires && !sanei_hp_device_compat(dev, desc->requires)) continue; if (desc->type != SANE_TYPE_GROUP && hp_optset_getByName(this, desc->name)) continue; status = hp_option_descriptor_probe(desc, scsi, this, dev->data, &option); if (UNSUPPORTED(status)) continue; if (FAILED(status)) { DBG(1, "Option '%s': probe failed: %s\n", desc->name, sane_strstatus(status)); sanei_hp_free(this); return status; } hp_optset_add(this, option); } /* Set NUM_OPTIONS */ assert(this->options[0]->descriptor == NUM_OPTIONS); sanei_hp_accessor_setint(this->options[0]->data_acsr, dev->data, this->num_sane_opts); /* Now for some kludges */ status = hp_optset_fix_geometry_options(this); if (FAILED(status)) { sanei_hp_free(this); return status; } info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); hp_optset_updateEnables(this, dev->data, info); *newp = this; return SANE_STATUS_GOOD; } hp_bool_t sanei_hp_optset_isImmediate (HpOptSet this, int optnum) { HpOption opt = hp_optset_getByIndex(this, optnum); if (!opt) return 0; return hp_option_isImmediate (opt); } SANE_Status sanei_hp_optset_control (HpOptSet this, HpData data, int optnum, SANE_Action action, void * valp, SANE_Int *infop, HpScsi scsi, hp_bool_t immediate) { HpOption opt = hp_optset_getByIndex(this, optnum); SANE_Int my_info = 0, my_val = 0; DBG(3,"sanei_hp_optset_control: %s\n", opt ? opt->descriptor->name : ""); if (infop) *infop = 0; else infop = &my_info; if (!opt) return SANE_STATUS_INVAL; /* There are problems with SANE_ACTION_GET_VALUE and valp == 0. */ /* Check if we really need valp. */ if ((action == SANE_ACTION_GET_VALUE) && (!valp)) { /* Options without a value ? */ if ( (opt->descriptor->type == SANE_TYPE_BUTTON) || (opt->descriptor->type == SANE_TYPE_GROUP)) { valp = &my_val; /* Just simulate a return value locally. */ } else /* Others must return a value. So this is invalid */ { DBG(1, "sanei_hp_optset_control: get value, but valp == 0\n"); return SANE_STATUS_INVAL; } } if (immediate) RETURN_IF_FAIL( hp_option_imm_control(this, opt, data, action, valp, infop, scsi) ); else RETURN_IF_FAIL( hp_option_control(opt, data, action, valp, infop ) ); if ((*infop & SANE_INFO_RELOAD_OPTIONS) != 0) {const HpDeviceInfo *info; DBG(3,"sanei_hp_optset_control: reprobe\n"); /* At first we try to reprogram the parameters that may have changed */ /* by an option that had a global effect. This is necessary to */ /* specify options in an arbitrary order. Example: */ /* Changing scan mode resets scan resolution in the scanner. */ /* If resolution is set from the API before scan mode, we must */ /* reprogram the resolution afterwards. */ hp_optset_reprogram(this, data, scsi); hp_optset_reprobe(this, data, scsi); info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); hp_optset_updateEnables(this, data, info); } return SANE_STATUS_GOOD; } SANE_Status sanei_hp_optset_guessParameters (HpOptSet this, HpData data, SANE_Parameters * p) { /* These are magic accessors which actually get the extent, not the * absolute position... */ int xextent = sanei_hp_accessor_getint(this->br_x, data); int yextent = sanei_hp_accessor_getint(this->br_y, data); int data_width; assert(xextent > 0 && yextent > 0); p->last_frame = SANE_TRUE; p->pixels_per_line = xextent; p->lines = yextent; switch (sanei_hp_optset_scanmode(this, data)) { case HP_SCANMODE_LINEART: /* Lineart */ case HP_SCANMODE_HALFTONE: /* Halftone */ p->format = SANE_FRAME_GRAY; p->depth = 1; p->bytes_per_line = (p->pixels_per_line + 7) / 8; break; case HP_SCANMODE_GRAYSCALE: /* Grayscale */ p->format = SANE_FRAME_GRAY; p->depth = 8; p->bytes_per_line = p->pixels_per_line; if ( !sanei_hp_optset_output_8bit (this, data) ) { data_width = sanei_hp_optset_data_width (this, data); if ( data_width > 8 ) { p->depth *= 2; p->bytes_per_line *= 2; } } break; case HP_SCANMODE_COLOR: /* RGB */ p->format = SANE_FRAME_RGB; p->depth = 8; p->bytes_per_line = 3 * p->pixels_per_line; if ( !sanei_hp_optset_output_8bit (this, data) ) { data_width = sanei_hp_optset_data_width (this, data); if ( data_width > 24 ) { p->depth *= 2; p->bytes_per_line *= 2; } } break; default: assert(!"Bad scan mode?"); return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } const SANE_Option_Descriptor * sanei_hp_optset_saneoption (HpOptSet this, HpData data, int optnum) { HpOption opt = hp_optset_getByIndex(this, optnum); if (!opt) return 0; return hp_option_saneoption(opt, data); } backends-1.3.0/backend/hp-option.h000066400000000000000000000231111456256263500167560ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1997 Geoffrey T. Dairiki 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, see . 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 is part of a SANE backend for HP Scanners supporting HP Scanner Control Language (SCL). */ #ifndef HP_OPTION_H_INCLUDED #define HP_OPTION_H_INCLUDED #include "hp.h" /* * Non-standard SANE options * * FIXME: this should become standard */ #ifndef SANE_NAME_SHARPENING # define SANE_NAME_SHARPENING "sharpening" # define SANE_TITLE_SHARPENING SANE_I18N("Sharpening") # define SANE_DESC_SHARPENING SANE_I18N("Set sharpening value.") #endif #ifndef SANE_NAME_AUTO_THRESHOLD # define SANE_NAME_AUTO_THRESHOLD "auto-threshold" # define SANE_TITLE_AUTO_THRESHOLD SANE_I18N("Auto Threshold") # define SANE_DESC_AUTO_THRESHOLD \ SANE_I18N("Enable automatic determination of threshold for line-art scans.") #endif #ifndef SANE_NAME_SMOOTHING # define SANE_NAME_SMOOTHING "smoothing" # define SANE_TITLE_SMOOTHING SANE_I18N("Smoothing") # define SANE_DESC_SMOOTHING SANE_I18N("Select smoothing filter.") #endif #ifndef SANE_NAME_UNLOAD_AFTER_SCAN # define SANE_NAME_UNLOAD_AFTER_SCAN "unload-after-scan" # define SANE_TITLE_UNLOAD_AFTER_SCAN SANE_I18N("Unload media after scan") # define SANE_DESC_UNLOAD_AFTER_SCAN SANE_I18N("Unloads the media after a scan.") #endif #ifndef SANE_NAME_CHANGE_DOC # define SANE_NAME_CHANGE_DOC "change-document" # define SANE_TITLE_CHANGE_DOC SANE_I18N("Change document") # define SANE_DESC_CHANGE_DOC SANE_I18N("Change Document.") #endif #ifndef SANE_NAME_UNLOAD # define SANE_NAME_UNLOAD "unload" # define SANE_TITLE_UNLOAD SANE_I18N("Unload") # define SANE_DESC_UNLOAD SANE_I18N("Unload Document.") #endif #ifndef SANE_NAME_CALIBRATE # define SANE_NAME_CALIBRATE "calibrate" # define SANE_TITLE_CALIBRATE SANE_I18N("Calibrate") # define SANE_DESC_CALIBRATE SANE_I18N("Start calibration process.") #endif #ifndef SANE_NAME_MEDIA # define SANE_NAME_MEDIA "media-type" # define SANE_TITLE_MEDIA SANE_I18N("Media") # define SANE_DESC_MEDIA SANE_I18N("Set type of media.") #endif #ifndef SANE_NAME_PS_EXPOSURE_TIME # define SANE_NAME_PS_EXPOSURE_TIME "ps-exposure-time" # define SANE_TITLE_PS_EXPOSURE_TIME SANE_I18N("Exposure time") # define SANE_DESC_PS_EXPOSURE_TIME \ SANE_I18N("A longer exposure time lets the scanner\ collect more light. Suggested use is 175% for prints,\ 150% for normal slides and \"Negative\" for\ negative film. For dark (underexposed) images you can increase this value.") #endif #ifndef SANE_NAME_MATRIX_TYPE # define SANE_NAME_MATRIX_TYPE "matrix-type" # define SANE_TITLE_MATRIX_TYPE SANE_I18N("Color Matrix") /* FIXME: better description */ # define SANE_DESC_MATRIX_TYPE SANE_I18N("Set the scanner's color matrix.") #endif #ifndef SANE_NAME_MATRIX_RGB # define SANE_NAME_MATRIX_RGB "matrix-rgb" # define SANE_TITLE_MATRIX_RGB SANE_I18N("Color Matrix") # define SANE_DESC_MATRIX_RGB SANE_I18N("Custom color matrix.") #endif #ifndef SANE_NAME_MATRIX_GRAY # define SANE_NAME_MATRIX_GRAY "matrix-gray" # define SANE_TITLE_MATRIX_GRAY SANE_I18N("Mono Color Matrix") # define SANE_DESC_MATRIX_GRAY SANE_I18N("Custom color matrix for grayscale scans.") #endif #ifndef SANE_NAME_MIRROR_HORIZ # define SANE_NAME_MIRROR_HORIZ "mirror-horizontal" # define SANE_TITLE_MIRROR_HORIZ SANE_I18N("Mirror horizontal") # define SANE_DESC_MIRROR_HORIZ SANE_I18N("Mirror image horizontally.") #endif #ifndef SANE_NAME_MIRROR_VERT # define SANE_NAME_MIRROR_VERT "mirror-vertical" # define SANE_TITLE_MIRROR_VERT SANE_I18N("Mirror vertical") # define SANE_DESC_MIRROR_VERT SANE_I18N("Mirror image vertically.") #endif #ifndef SANE_NAME_UPDATE # define SANE_NAME_UPDATE "update-options" # define SANE_TITLE_UPDATE SANE_I18N("Update options") # define SANE_DESC_UPDATE SANE_I18N("Update options.") #endif #ifndef SANE_NAME_OUTPUT_8BIT # define SANE_NAME_OUTPUT_8BIT "output-8bit" # define SANE_TITLE_OUTPUT_8BIT SANE_I18N("8 bit output") # define SANE_DESC_OUTPUT_8BIT \ SANE_I18N("Use bit depth greater eight internally,\ but output only eight bits.") #endif #ifndef SANE_NAME_BUTTON_WAIT # define SANE_NAME_BUTTON_WAIT "button-wait" # define SANE_TITLE_BUTTON_WAIT SANE_I18N("Front button wait") # define SANE_DESC_BUTTON_WAIT SANE_I18N("Wait to scan for front-panel button push.") # define HP_BUTTON_WAIT_NO 0 # define HP_BUTTON_WAIT_YES 1 #endif #ifndef SANE_NAME_LAMP_OFF # define SANE_NAME_LAMP_OFF "lamp-off" # define SANE_TITLE_LAMP_OFF SANE_I18N("Shut off lamp") # define SANE_DESC_LAMP_OFF SANE_I18N("Shut off scanner lamp.") # define HP_LAMP_OFF_NO 0 # define HP_LAMP_OFF_YES 1 #endif /* Some test stuff to see what undocumented SCL-commands do */ # define SANE_NAME_10470 "10470" # define SANE_TITLE_10470 "10470" # define SANE_DESC_10470 "10470." # define SANE_NAME_10485 "10485" # define SANE_TITLE_10485 "10485" # define SANE_DESC_10485 "10485." # define SANE_NAME_10952 "10952" # define SANE_TITLE_10952 "10952" # define SANE_DESC_10952 "10952." # define SANE_NAME_10967 "10967" # define SANE_TITLE_10967 "10967" # define SANE_DESC_10967 "10967." /* * Internal option names which are not presented to the SANE frontend. */ #define HP_NAME_HORIZONTAL_DITHER "__hdither__" #define HP_NAME_DEVPIX_RESOLUTION "__devpix_resolution__" #ifdef ENABLE_7x12_TONEMAPS # define HP_NAME_RGB_TONEMAP "__rgb_tonemap__" #endif #ifdef FAKE_COLORSEP_MATRIXES # define HP_NAME_SEPMATRIX "__sepmatrix__" #endif struct hp_choice_s { int val; const char *name; hp_bool_t (*enable)(HpChoice this, HpOptSet optset, HpData data, const HpDeviceInfo *info); hp_bool_t is_emulated:1; HpChoice next; }; enum hp_scanmode_e { HP_SCANMODE_LINEART = 0, HP_SCANMODE_HALFTONE = 3, HP_SCANMODE_GRAYSCALE = 4, HP_SCANMODE_COLOR = 5 }; enum hp_scantype_e { HP_SCANTYPE_NORMAL = 0, HP_SCANTYPE_ADF = 1, HP_SCANTYPE_XPA = 2 }; enum hp_dither_type_e { HP_DITHER_CUSTOM = -1, HP_DITHER_COARSE = 0, HP_DITHER_FINE = 1, HP_DITHER_BAYER = 2, HP_DITHER_VERTICAL = 3, HP_DITHER_HORIZONTAL }; enum hp_matrix_type_e { HP_MATRIX_AUTO = -256, HP_MATRIX_GREEN = -257, HP_MATRIX_CUSTOM_BW = -2, HP_MATRIX_CUSTOM = -1, HP_MATRIX_RGB = 0, HP_MATRIX_BW = 1, HP_MATRIX_PASS = 2, HP_MATRIX_RED = 3, HP_MATRIX_BLUE = 4, HP_MATRIX_XPA_RGB = 5, HP_MATRIX_XPA_BW = 6 }; enum hp_mirror_horiz_e { HP_MIRROR_HORIZ_CONDITIONAL = -256, HP_MIRROR_HORIZ_OFF = 0, HP_MIRROR_HORIZ_ON = 1 }; enum hp_mirror_vert_e { HP_MIRROR_VERT_OFF = -258, HP_MIRROR_VERT_ON = -257, HP_MIRROR_VERT_CONDITIONAL = -256 }; enum hp_media_e { HP_MEDIA_NEGATIVE = 1, HP_MEDIA_SLIDE = 2, HP_MEDIA_PRINT = 3 }; hp_bool_t sanei_hp_choice_isEnabled (HpChoice this, HpOptSet optset, HpData data, const HpDeviceInfo *info); SANE_Status sanei_hp_optset_new(HpOptSet * newp, HpScsi scsi, HpDevice dev); SANE_Status sanei_hp_optset_download (HpOptSet this, HpData data, HpScsi scsi); SANE_Status sanei_hp_optset_control (HpOptSet this, HpData data, int optnum, SANE_Action action, void * valp, SANE_Int *infop, HpScsi scsi, hp_bool_t immediate); SANE_Status sanei_hp_optset_guessParameters (HpOptSet this, HpData data, SANE_Parameters * p); enum hp_scanmode_e sanei_hp_optset_scanmode (HpOptSet this, HpData data); hp_bool_t sanei_hp_optset_output_8bit (HpOptSet this, HpData data); int sanei_hp_optset_data_width (HpOptSet this, HpData data); hp_bool_t sanei_hp_optset_isImmediate (HpOptSet this, int optnum); hp_bool_t sanei_hp_optset_mirror_vert (HpOptSet this, HpData data, HpScsi scsi); hp_bool_t sanei_hp_optset_start_wait(HpOptSet this, HpData data); HpScl sanei_hp_optset_scan_type (HpOptSet this, HpData data); const SANE_Option_Descriptor * sanei_hp_optset_saneoption (HpOptSet this, HpData data, int optnum); #endif /* HP_OPTION_H_INCLUDED */ backends-1.3.0/backend/hp-scl.c000066400000000000000000001507221456256263500162330ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1997 Geoffrey T. Dairiki 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, see . 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 is part of a SANE backend for HP Scanners supporting HP Scanner Control Language (SCL). */ /* Revision 1.15 2008/03/28 14:37:36 kitno-guest add usleep to improve usb performance, from jim a t meyering d o t net Revision 1.14 2004-10-04 18:09:05 kig-guest Rename global function hp_init_openfd to sanei_hp_init_openfd Revision 1.13 2004/03/27 13:52:39 kig-guest Keep USB-connection open (was problem with Linux 2.6.x) Revision 1.12 2003/10/09 19:34:57 kig-guest Redo when TEST UNIT READY failed Redo when read returns with 0 bytes (non-SCSI only) */ /* #define STUBS extern int sanei_debug_hp;*/ #define DEBUG_DECLARE_ONLY #include "../include/sane/config.h" #include "../include/lalloca.h" /* Must be first */ #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include #include #include "../include/lassert.h" #include #include #include #include #include "../include/sane/sanei_scsi.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_pio.h" #include "hp.h" #include "../include/sane/sanei_backend.h" #include "hp-option.h" #include "hp-scsi.h" #include "hp-scl.h" #include "hp-device.h" #define HP_SCSI_INQ_LEN (36) #define HP_SCSI_CMD_LEN (6) #define HP_SCSI_BUFSIZ (HP_SCSI_MAX_WRITE + HP_SCSI_CMD_LEN) #define HP_MAX_OPEN_FD 16 static struct hp_open_fd_s /* structure to save info about open file descriptor */ { char *devname; HpConnect connect; int fd; } asHpOpenFd[HP_MAX_OPEN_FD]; /* * */ struct hp_scsi_s { int fd; char * devname; /* Output buffering */ hp_byte_t buf[HP_SCSI_BUFSIZ]; hp_byte_t * bufp; hp_byte_t inq_data[HP_SCSI_INQ_LEN]; }; #define HP_TMP_BUF_SIZE (1024*4) #define HP_WR_BUF_SIZE (1024*4) typedef struct { HpProcessData procdata; int outfd; const unsigned char *map; unsigned char *image_buf; /* Buffer to store complete image (if req.) */ unsigned char *image_ptr; int image_buf_size; unsigned char *tmp_buf; /* Buffer for scan data to get even number of bytes */ int tmp_buf_size; int tmp_buf_len; unsigned char wr_buf[HP_WR_BUF_SIZE]; unsigned char *wr_ptr; int wr_buf_size; int wr_left; } PROCDATA_HANDLE; /* Initialize structure where we remember out open file descriptors */ void sanei_hp_init_openfd () {int iCount; memset (asHpOpenFd, 0, sizeof (asHpOpenFd)); for (iCount = 0; iCount < HP_MAX_OPEN_FD; iCount++) asHpOpenFd[iCount].fd = -1; } /* Look if the device is still open */ static SANE_Status hp_GetOpenDevice (const char *devname, HpConnect connect, int *pfd) {int iCount; for (iCount = 0; iCount < HP_MAX_OPEN_FD; iCount++) { if (!asHpOpenFd[iCount].devname) continue; if ( (strcmp (asHpOpenFd[iCount].devname, devname) == 0) && (asHpOpenFd[iCount].connect == connect) ) { if (pfd) *pfd = asHpOpenFd[iCount].fd; DBG(3, "hp_GetOpenDevice: device %s is open with fd=%d\n", devname, asHpOpenFd[iCount].fd); return SANE_STATUS_GOOD; } } DBG(3, "hp_GetOpenDevice: device %s not open\n", devname); return SANE_STATUS_INVAL; } /* Add an open file descriptor. This also decides */ /* if we keep a connection open or not. */ static SANE_Status hp_AddOpenDevice (const char *devname, HpConnect connect, int fd) {int iCount, iKeepOpen; static int iInitKeepFlags = 1; /* The default values which connections to keep open or not */ static int iKeepOpenSCSI = 0; static int iKeepOpenUSB = 1; static int iKeepOpenDevice = 0; static int iKeepOpenPIO = 0; if (iInitKeepFlags) /* Change the defaults by environment */ {char *eptr; iInitKeepFlags = 0; eptr = getenv ("SANE_HP_KEEPOPEN_SCSI"); if ( (eptr != NULL) && ((*eptr == '0') || (*eptr == '1')) ) iKeepOpenSCSI = (*eptr == '1'); eptr = getenv ("SANE_HP_KEEPOPEN_USB"); if ( (eptr != NULL) && ((*eptr == '0') || (*eptr == '1')) ) iKeepOpenUSB = (*eptr == '1'); eptr = getenv ("SANE_HP_KEEPOPEN_DEVICE"); if ( (eptr != NULL) && ((*eptr == '0') || (*eptr == '1')) ) iKeepOpenDevice = (*eptr == '1'); eptr = getenv ("SANE_HP_KEEPOPEN_PIO"); if ( (eptr != NULL) && ((*eptr == '0') || (*eptr == '1')) ) iKeepOpenPIO = (*eptr == '1'); } /* Look if we should keep it open or not */ iKeepOpen = 0; switch (connect) { case HP_CONNECT_SCSI: iKeepOpen = iKeepOpenSCSI; break; case HP_CONNECT_PIO : iKeepOpen = iKeepOpenPIO; break; case HP_CONNECT_USB : iKeepOpen = iKeepOpenUSB; break; case HP_CONNECT_DEVICE : iKeepOpen = iKeepOpenDevice; break; case HP_CONNECT_RESERVE: break; } if (!iKeepOpen) { DBG(3, "hp_AddOpenDevice: %s should not be kept open\n", devname); return SANE_STATUS_INVAL; } for (iCount = 0; iCount < HP_MAX_OPEN_FD; iCount++) { if (!asHpOpenFd[iCount].devname) /* Is this entry free ? */ { asHpOpenFd[iCount].devname = sanei_hp_strdup (devname); if (!asHpOpenFd[iCount].devname) return SANE_STATUS_NO_MEM; DBG(3, "hp_AddOpenDevice: added device %s with fd=%d\n", devname, fd); asHpOpenFd[iCount].connect = connect; asHpOpenFd[iCount].fd = fd; return SANE_STATUS_GOOD; } } DBG(3, "hp_AddOpenDevice: %s not added\n", devname); return SANE_STATUS_NO_MEM; } /* Check if we have remembered an open file descriptor */ static SANE_Status hp_IsOpenFd (int fd, HpConnect connect) {int iCount; for (iCount = 0; iCount < HP_MAX_OPEN_FD; iCount++) { if ( (asHpOpenFd[iCount].devname != NULL) && (asHpOpenFd[iCount].fd == fd) && (asHpOpenFd[iCount].connect == connect) ) { DBG(3, "hp_IsOpenFd: %d is open\n", fd); return SANE_STATUS_GOOD; } } DBG(3, "hp_IsOpenFd: %d not open\n", fd); return SANE_STATUS_INVAL; } static SANE_Status hp_RemoveOpenFd (int fd, HpConnect connect) {int iCount; for (iCount = 0; iCount < HP_MAX_OPEN_FD; iCount++) { if ( (asHpOpenFd[iCount].devname != NULL) && (asHpOpenFd[iCount].fd == fd) && (asHpOpenFd[iCount].connect == connect) ) { sanei_hp_free (asHpOpenFd[iCount].devname); asHpOpenFd[iCount].devname = NULL; DBG(3, "hp_RemoveOpenFd: removed %d\n", asHpOpenFd[iCount].fd); asHpOpenFd[iCount].fd = -1; return SANE_STATUS_GOOD; } } DBG(3, "hp_RemoveOpenFd: %d not removed\n", fd); return SANE_STATUS_INVAL; } static SANE_Status hp_nonscsi_write (HpScsi this, hp_byte_t *data, size_t len, HpConnect connect) {int n = -1; size_t loc_len; SANE_Status status = SANE_STATUS_GOOD; if (len <= 0) return SANE_STATUS_GOOD; switch (connect) { case HP_CONNECT_DEVICE: /* direct device-io */ n = write (this->fd, data, len); break; case HP_CONNECT_PIO: /* Use sanepio interface */ n = sanei_pio_write (this->fd, data, len); break; case HP_CONNECT_USB: /* Not supported */ loc_len = len; status = sanei_usb_write_bulk ((SANE_Int)this->fd, data, &loc_len); n = loc_len; break; case HP_CONNECT_RESERVE: n = -1; break; default: n = -1; break; } if (n == 0) return SANE_STATUS_EOF; else if (n < 0) return SANE_STATUS_IO_ERROR; return status; } static SANE_Status hp_nonscsi_read (HpScsi this, hp_byte_t *data, size_t *len, HpConnect connect, int __sane_unused__ isResponse) {int n = -1; static int retries = -1; size_t save_len = *len; SANE_Status status = SANE_STATUS_GOOD; if (*len <= 0) return SANE_STATUS_GOOD; if (retries < 0) /* Read environment */ {char *eptr = getenv ("SANE_HP_RDREDO"); retries = 1; /* Set default value */ if (eptr != NULL) { if (sscanf (eptr, "%d", &retries) != 1) retries = 1; /* Restore default */ else if (retries < 0) retries = 0; /* Allow no retries here */ } } for (;;) /* Retry on EOF */ { switch (connect) { case HP_CONNECT_DEVICE: n = read (this->fd, data, *len); break; case HP_CONNECT_PIO: n = sanei_pio_read (this->fd, data, *len); break; case HP_CONNECT_USB: status = sanei_usb_read_bulk((SANE_Int)this->fd, (SANE_Byte *)data, len); n = *len; break; case HP_CONNECT_RESERVE: n = -1; break; default: n = -1; break; } if ((n != 0) || (retries <= 0)) break; retries--; usleep (100*1000); /* sleep 0.1 seconds */ *len = save_len; /* Restore value */ } if (n == 0) return SANE_STATUS_EOF; else if (n < 0) return SANE_STATUS_IO_ERROR; *len = n; return status; } static SANE_Status hp_nonscsi_open (const char *devname, int *fd, HpConnect connect) {int lfd, flags; SANE_Int dn; SANE_Status status = SANE_STATUS_INVAL; #ifdef _O_RDWR flags = _O_RDWR; #else flags = O_RDWR; #endif #ifdef _O_EXCL flags |= _O_EXCL; #else flags |= O_EXCL; #endif #ifdef _O_BINARY flags |= _O_BINARY; #endif #ifdef O_BINARY flags |= O_BINARY; #endif switch (connect) { case HP_CONNECT_DEVICE: lfd = open (devname, flags); if (lfd < 0) { DBG(1, "hp_nonscsi_open: open device %s failed (%s)\n", devname, strerror (errno) ); status = (errno == EACCES) ? SANE_STATUS_ACCESS_DENIED : SANE_STATUS_INVAL; } else status = SANE_STATUS_GOOD; break; case HP_CONNECT_PIO: status = sanei_pio_open (devname, &lfd); break; case HP_CONNECT_USB: DBG(17, "hp_nonscsi_open: open usb with \"%s\"\n", devname); status = sanei_usb_open (devname, &dn); lfd = (int)dn; break; case HP_CONNECT_RESERVE: status = SANE_STATUS_INVAL; break; default: status = SANE_STATUS_INVAL; break; } if (status != SANE_STATUS_GOOD) { DBG(1, "hp_nonscsi_open: open device %s failed\n", devname); } else { DBG(17,"hp_nonscsi_open: device %s opened, fd=%d\n", devname, lfd); } if (fd) *fd = lfd; return status; } static void hp_nonscsi_close (int fd, HpConnect connect) { switch (connect) { case HP_CONNECT_DEVICE: close (fd); break; case HP_CONNECT_PIO: sanei_pio_close (fd); break; case HP_CONNECT_USB: sanei_usb_close (fd); break; case HP_CONNECT_RESERVE: break; default: break; } DBG(17,"hp_nonscsi_close: closed fd=%d\n", fd); } SANE_Status sanei_hp_nonscsi_new (HpScsi * newp, const char * devname, HpConnect connect) { HpScsi new; SANE_Status status; int iAlreadyOpen = 0; new = sanei_hp_allocz(sizeof(*new)); if (!new) return SANE_STATUS_NO_MEM; /* Is the device already open ? */ if ( hp_GetOpenDevice (devname, connect, &new->fd) == SANE_STATUS_GOOD ) { iAlreadyOpen = 1; } else { status = hp_nonscsi_open(devname, &new->fd, connect); if (FAILED(status)) { DBG(1, "nonscsi_new: open failed (%s)\n", sane_strstatus(status)); sanei_hp_free(new); return SANE_STATUS_IO_ERROR; } } /* For SCSI-devices we would have the inquire command here */ memcpy (new->inq_data, "\003zzzzzzzHP ------ R000", sizeof (new->inq_data)); new->bufp = new->buf + HP_SCSI_CMD_LEN; new->devname = sanei_hp_alloc ( strlen ( devname ) + 1 ); if ( new->devname ) strcpy (new->devname, devname); *newp = new; /* Remember the open device */ if (!iAlreadyOpen) hp_AddOpenDevice (devname, connect, new->fd); return SANE_STATUS_GOOD; } static void hp_scsi_close (HpScsi this, int completely) {HpConnect connect; DBG(3, "scsi_close: closing fd %ld\n", (long)this->fd); connect = sanei_hp_scsi_get_connect (this); if (!completely) /* May we keep the device open ? */ { if ( hp_IsOpenFd (this->fd, connect) == SANE_STATUS_GOOD ) { DBG(3, "scsi_close: not closing. Keep open\n"); return; } } assert(this->fd >= 0); if (connect != HP_CONNECT_SCSI) hp_nonscsi_close (this->fd, connect); else sanei_scsi_close (this->fd); DBG(3,"scsi_close: really closed\n"); /* Remove a remembered open device */ hp_RemoveOpenFd (this->fd, connect); } SANE_Status sanei_hp_scsi_new (HpScsi * newp, const char * devname) { static hp_byte_t inq_cmd[] = { 0x12, 0, 0, 0, HP_SCSI_INQ_LEN, 0}; static hp_byte_t tur_cmd[] = { 0x00, 0, 0, 0, 0, 0}; size_t inq_len = HP_SCSI_INQ_LEN; HpScsi new; HpConnect connect; SANE_Status status; int iAlreadyOpen = 0; connect = sanei_hp_get_connect (devname); if (connect != HP_CONNECT_SCSI) return sanei_hp_nonscsi_new (newp, devname, connect); new = sanei_hp_allocz(sizeof(*new)); if (!new) return SANE_STATUS_NO_MEM; /* Is the device still open ? */ if ( hp_GetOpenDevice (devname, connect, &new->fd) == SANE_STATUS_GOOD ) { iAlreadyOpen = 1; } else { status = sanei_scsi_open(devname, &new->fd, 0, 0); if (FAILED(status)) { DBG(1, "scsi_new: open failed (%s)\n", sane_strstatus(status)); sanei_hp_free(new); return SANE_STATUS_IO_ERROR; } } DBG(3, "scsi_inquire: sending INQUIRE\n"); status = sanei_scsi_cmd(new->fd, inq_cmd, 6, new->inq_data, &inq_len); if (FAILED(status)) { DBG(1, "scsi_inquire: inquiry failed: %s\n", sane_strstatus(status)); sanei_scsi_close(new->fd); sanei_hp_free(new); return status; } {char vendor[9], model[17], rev[5]; memset (vendor, 0, sizeof (vendor)); memset (model, 0, sizeof (model)); memset (rev, 0, sizeof (rev)); memcpy (vendor, new->inq_data + 8, 8); memcpy (model, new->inq_data + 16, 16); memcpy (rev, new->inq_data + 32, 4); DBG(3, "vendor=%s, model=%s, rev=%s\n", vendor, model, rev); } DBG(3, "scsi_new: sending TEST_UNIT_READY\n"); status = sanei_scsi_cmd(new->fd, tur_cmd, 6, 0, 0); if (FAILED(status)) { DBG(1, "hp_scsi_open: test unit ready failed (%s)\n", sane_strstatus(status)); usleep (500*1000); /* Wait 0.5 seconds */ DBG(3, "scsi_new: sending TEST_UNIT_READY second time\n"); status = sanei_scsi_cmd(new->fd, tur_cmd, 6, 0, 0); } if (FAILED(status)) { DBG(1, "hp_scsi_open: test unit ready failed (%s)\n", sane_strstatus(status)); sanei_scsi_close(new->fd); sanei_hp_free(new); return status; /* Fix problem with non-scanner devices */ } new->bufp = new->buf + HP_SCSI_CMD_LEN; new->devname = sanei_hp_alloc ( strlen ( devname ) + 1 ); if ( new->devname ) strcpy (new->devname, devname); *newp = new; /* Remember the open device */ if (!iAlreadyOpen) hp_AddOpenDevice (devname, connect, new->fd); return SANE_STATUS_GOOD; } /* The "completely" parameter was added for OfficeJet support. * For JetDirect connections, closing and re-opening the scan * channel is very time consuming. Also, the OfficeJet G85 * unloads a loaded document in the ADF when the scan channel * gets closed. The solution is to "completely" destroy the * connection, including closing and deallocating the PTAL * channel, when initially probing the device in hp-device.c, * but leave it open while the frontend is actually using the * device (from hp-handle.c), and "completely" destroy it when * the frontend closes its handle. */ void sanei_hp_scsi_destroy (HpScsi this,int completely) { /* Moved to hp_scsi_close(): * assert(this->fd >= 0); * DBG(3, "scsi_close: closing fd %d\n", this->fd); */ hp_scsi_close (this, completely); if ( this->devname ) sanei_hp_free (this->devname); sanei_hp_free(this); } hp_byte_t * sanei_hp_scsi_inq (HpScsi this) { return this->inq_data; } const char * sanei_hp_scsi_vendor (HpScsi this) { static char buf[9]; memcpy(buf, sanei_hp_scsi_inq(this) + 8, 8); buf[8] = '\0'; return buf; } const char * sanei_hp_scsi_model (HpScsi this) { static char buf[17]; memcpy(buf, sanei_hp_scsi_inq(this) + 16, 16); buf[16] = '\0'; return buf; } const char * sanei_hp_scsi_devicename (HpScsi this) { return this->devname; } hp_bool_t sanei_hp_is_active_xpa (HpScsi scsi) {HpDeviceInfo *info; int model_num; info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); if (info->active_xpa < 0) { model_num = sanei_hp_get_max_model (scsi); info->active_xpa = (model_num >= 17); DBG(5,"sanei_hp_is_active_xpa: model=%d, active_xpa=%d\n", model_num, info->active_xpa); } return info->active_xpa; } int sanei_hp_get_max_model (HpScsi scsi) {HpDeviceInfo *info; info = sanei_hp_device_info_get ( sanei_hp_scsi_devicename (scsi) ); if (info->max_model < 0) {enum hp_device_compat_e compat; int model_num; if ( sanei_hp_device_probe_model ( &compat, scsi, &model_num, 0) == SANE_STATUS_GOOD ) info->max_model = model_num; } return info->max_model; } int sanei_hp_is_flatbed_adf (HpScsi scsi) {int model = sanei_hp_get_max_model (scsi); return ((model == 2) || (model == 4) || (model == 5) || (model == 8)); } HpConnect sanei_hp_get_connect (const char *devname) {const HpDeviceInfo *info; HpConnect connect = HP_CONNECT_SCSI; int got_connect_type = 0; info = sanei_hp_device_info_get (devname); if (!info) { DBG(1, "sanei_hp_get_connect: Could not get info for %s. Assume SCSI\n", devname); connect = HP_CONNECT_SCSI; } else if ( !(info->config_is_up) ) { DBG(1, "sanei_hp_get_connect: Config not initialized for %s. Assume SCSI\n", devname); connect = HP_CONNECT_SCSI; } else { connect = info->config.connect; got_connect_type = info->config.got_connect_type; } /* Beware of using a USB-device as a SCSI-device (not 100% perfect) */ if ((connect == HP_CONNECT_SCSI) && !got_connect_type) {int maybe_usb; maybe_usb = ( strstr (devname, "usb") || strstr (devname, "uscanner") || strstr (devname, "ugen")); if (maybe_usb) {static int print_warning = 1; if (print_warning) { print_warning = 0; DBG(1,"sanei_hp_get_connect: WARNING\n"); DBG(1," Device %s assumed to be SCSI, but device name\n",devname); DBG(1," looks like USB. Will continue with USB.\n"); DBG(1," If you really want it as SCSI, add the following\n"); DBG(1," to your file .../etc/sane.d/hp.conf:\n"); DBG(1," %s\n", devname); DBG(1," option connect-scsi\n"); DBG(1," The same warning applies to other device names containing\n"); DBG(1," \"usb\", \"uscanner\" or \"ugen\".\n"); } connect = HP_CONNECT_DEVICE; } } return connect; } HpConnect sanei_hp_scsi_get_connect (HpScsi this) { return sanei_hp_get_connect (sanei_hp_scsi_devicename (this)); } static SANE_Status hp_scsi_flush (HpScsi this) { hp_byte_t * data = this->buf + HP_SCSI_CMD_LEN; size_t len = this->bufp - data; HpConnect connect; assert(len < HP_SCSI_MAX_WRITE); if (len == 0) return SANE_STATUS_GOOD; this->bufp = this->buf; DBG(16, "scsi_flush: writing %lu bytes:\n", (unsigned long) len); DBGDUMP(16, data, len); *this->bufp++ = 0x0A; *this->bufp++ = 0; *this->bufp++ = len >> 16; *this->bufp++ = len >> 8; *this->bufp++ = len; *this->bufp++ = 0; connect = sanei_hp_scsi_get_connect (this); if (connect == HP_CONNECT_SCSI) return sanei_scsi_cmd (this->fd, this->buf, HP_SCSI_CMD_LEN + len, 0, 0); else return hp_nonscsi_write (this, this->buf+HP_SCSI_CMD_LEN, len, connect); } static size_t hp_scsi_room (HpScsi this) { return this->buf + HP_SCSI_BUFSIZ - this->bufp; } static SANE_Status hp_scsi_need (HpScsi this, size_t need) { assert(need < HP_SCSI_MAX_WRITE); if (need > hp_scsi_room(this)) RETURN_IF_FAIL( hp_scsi_flush(this) ); return SANE_STATUS_GOOD; } static SANE_Status hp_scsi_write (HpScsi this, const void *data, size_t len) { if ( len < HP_SCSI_MAX_WRITE ) { RETURN_IF_FAIL( hp_scsi_need(this, len) ); memcpy(this->bufp, data, len); this->bufp += len; } else {size_t maxwrite = HP_SCSI_MAX_WRITE - 16; const char *c_data = (const char *)data; while ( len > 0 ) { if ( maxwrite > len ) maxwrite = len; RETURN_IF_FAIL( hp_scsi_write(this, c_data, maxwrite) ); c_data += maxwrite; len -= maxwrite; } } return SANE_STATUS_GOOD; } static SANE_Status hp_scsi_scl(HpScsi this, HpScl scl, int val) { char group = tolower(SCL_GROUP_CHAR(scl)); char param = toupper(SCL_PARAM_CHAR(scl)); int count; assert(IS_SCL_CONTROL(scl) || IS_SCL_COMMAND(scl)); assert(isprint(group) && isprint(param)); RETURN_IF_FAIL( hp_scsi_need(this, 10) ); /* Don't try to optimize SCL-commands like using *a1b0c5T */ /* Some scanners have problems with it (e.g. HP Photosmart Photoscanner */ /* with window position/extent, resolution) */ count = sprintf((char *)this->bufp, "\033*%c%d%c", group, val, param); this->bufp += count; assert(count > 0 && this->bufp < this->buf + HP_SCSI_BUFSIZ); return hp_scsi_flush(this); } /* Read it bytewise */ static SANE_Status hp_scsi_read_slow (HpScsi this, void * dest, size_t *len) {static hp_byte_t read_cmd[6] = { 0x08, 0, 0, 0, 0, 0 }; size_t leftover = *len; SANE_Status status = SANE_STATUS_GOOD; unsigned char *start_dest = (unsigned char *)dest; unsigned char *next_dest = start_dest; DBG(16, "hp_scsi_read_slow: Start reading %d bytes bytewise\n", (int)*len); while (leftover > 0) /* Until we got all the bytes */ {size_t one = 1; read_cmd[2] = 0; read_cmd[3] = 0; read_cmd[4] = 1; /* Read one byte */ status = sanei_scsi_cmd (this->fd, read_cmd, sizeof(read_cmd), next_dest, &one); if ((status != SANE_STATUS_GOOD) || (one != 1)) { DBG(250,"hp_scsi_read_slow: Reading byte %d: status=%s, len=%d\n", (int)(next_dest-start_dest), sane_strstatus(status), (int)one); } if (status != SANE_STATUS_GOOD) break; /* Finish on error */ next_dest++; leftover--; } *len = next_dest-start_dest; /* This is the number of bytes we got */ DBG(16, "hp_scsi_read_slow: Got %d bytes\n", (int)*len); if ((status != SANE_STATUS_GOOD) && (*len > 0)) { DBG(16, "We got some data. Ignore the error \"%s\"\n", sane_strstatus(status)); status = SANE_STATUS_GOOD; } return status; } /* The OfficeJets tend to return inquiry responses containing array * data in two packets. The added "isResponse" parameter tells * whether we should keep reading until we get * a well-formed response. Naturally, this parameter would be zero * when reading scan data. */ static SANE_Status hp_scsi_read (HpScsi this, void * dest, size_t *len, int isResponse) { HpConnect connect; RETURN_IF_FAIL( hp_scsi_flush(this) ); connect = sanei_hp_scsi_get_connect (this); if (connect == HP_CONNECT_SCSI) {int read_bytewise = 0; if (*len <= 32) /* Is it a candidate for reading bytewise ? */ {const HpDeviceInfo *info; info = sanei_hp_device_info_get (sanei_hp_scsi_devicename (this)); if ((info != NULL) && (info->config_is_up) && info->config.dumb_read) read_bytewise = 1; } if ( ! read_bytewise ) {static hp_byte_t read_cmd[6] = { 0x08, 0, 0, 0, 0, 0 }; read_cmd[2] = *len >> 16; read_cmd[3] = *len >> 8; read_cmd[4] = *len; RETURN_IF_FAIL( sanei_scsi_cmd (this->fd, read_cmd, sizeof(read_cmd), dest, len) ); } else { RETURN_IF_FAIL (hp_scsi_read_slow (this, dest, len)); } } else { RETURN_IF_FAIL( hp_nonscsi_read (this, dest, len, connect, isResponse) ); } DBG(16, "scsi_read: %lu bytes:\n", (unsigned long) *len); DBGDUMP(16, dest, *len); return SANE_STATUS_GOOD; } static int signal_caught = 0; static void signal_catcher (int sig) { DBG(1,"signal_catcher(sig=%d): old signal_caught=%d\n",sig,signal_caught); if (!signal_caught) signal_caught = sig; } static void hp_data_map (register const unsigned char *map, register int count, register unsigned char *data) { if (count <= 0) return; while (count--) { *data = map[*data]; data++; } } static const unsigned char * hp_get_simulation_map (const char *devname, const HpDeviceInfo *info) { hp_bool_t sim_gamma, sim_brightness, sim_contrast; int k, ind; const unsigned char *map = NULL; static unsigned char map8x8[256]; sim_gamma = info->simulate.gamma_simulate; sim_brightness = sanei_hp_device_simulate_get (devname, SCL_BRIGHTNESS); sim_contrast = sanei_hp_device_simulate_get (devname, SCL_CONTRAST); if ( sim_gamma ) { map = &(info->simulate.gamma_map[0]); } else if ( sim_brightness && sim_contrast ) { for (k = 0; k < 256; k++) { ind = info->simulate.contrast_map[k]; map8x8[k] = info->simulate.brightness_map[ind]; } map = &(map8x8[0]); } else if ( sim_brightness ) map = &(info->simulate.brightness_map[0]); else if ( sim_contrast ) map = &(info->simulate.contrast_map[0]); return map; } /* Check the native byte order on the local machine */ static hp_bool_t is_lowbyte_first_byteorder (void) {unsigned short testvar = 1; unsigned char *testptr = (unsigned char *)&testvar; if (sizeof (unsigned short) == 2) return (testptr[0] == 1); else if (sizeof (unsigned short) == 4) return ((testptr[0] == 1) || (testptr[2] == 1)); else return ( (testptr[0] == 1) || (testptr[2] == 1) || (testptr[4] == 1) || (testptr[6] == 1)); } /* The SANE standard defines that 2-byte data must use the full 16 bit range. * Byte order returned by the backend must be native byte order. * Scaling to 16 bit and byte order is achieved by hp_scale_to_16bit. * for >8 bits data, take the two data bytes and scale their content * to the full 16 bit range, using * scaled = unscaled << (newlen - oldlen) + * unscaled >> (oldlen - (newlen - oldlen)), * with newlen=16 and oldlen the original bit depth. */ static void hp_scale_to_16bit(int count, register unsigned char *data, int depth, hp_bool_t invert) { register unsigned int tmp; register unsigned int mask; register hp_bool_t lowbyte_first = is_lowbyte_first_byteorder (); unsigned int shift1 = 16 - depth; unsigned int shift2 = 2*depth - 16; int k; if (count <= 0) return; mask = 1; for (k = 1; k < depth; k++) mask |= (1 << k); if (lowbyte_first) { while (count--) { tmp = ((((unsigned int)data[0])<<8) | ((unsigned int)data[1])) & mask; tmp = (tmp << shift1) + (tmp >> shift2); if (invert) tmp = ~tmp; *data++ = tmp & 255U; *data++ = (tmp >> 8) & 255U; } } else /* Highbyte first */ { while (count--) { tmp = ((((unsigned int)data[0])<<8) | ((unsigned int)data[1])) & mask; tmp = (tmp << shift1) + (tmp >> shift2); if (invert) tmp = ~tmp; *data++ = (tmp >> 8) & 255U; *data++ = tmp & 255U; } } } static void hp_scale_to_8bit(int count, register unsigned char *data, int depth, hp_bool_t invert) { register unsigned int tmp, mask; register hp_bool_t lowbyte_first = is_lowbyte_first_byteorder (); unsigned int shift1 = depth-8; int k; unsigned char *dataout = data; if ((count <= 0) || (shift1 <= 0)) return; mask = 1; for (k = 1; k < depth; k++) mask |= (1 << k); if (lowbyte_first) { while (count--) { tmp = ((((unsigned int)data[0])<<8) | ((unsigned int)data[1])) & mask; tmp >>= shift1; if (invert) tmp = ~tmp; *(dataout++) = tmp & 255U; data += 2; } } else /* Highbyte first */ { while (count--) { tmp = ((((unsigned int)data[0])<<8) | ((unsigned int)data[1])) & mask; tmp >>= shift1; if (invert) tmp = ~tmp; *(dataout++) = tmp & 255U; data += 2; } } } static void hp_soft_invert(int count, register unsigned char *data) { while (count>0) { *data = ~(*data); data++; count--; } } static PROCDATA_HANDLE * process_data_init (HpProcessData *procdata, const unsigned char *map, int outfd, hp_bool_t use_imgbuf) {PROCDATA_HANDLE *ph = sanei_hp_alloc (sizeof (PROCDATA_HANDLE)); int tsz; if (ph == NULL) return NULL; memset (ph, 0, sizeof (*ph)); memcpy (&(ph->procdata), procdata, sizeof (*procdata)); procdata = &(ph->procdata); tsz = (HP_TMP_BUF_SIZE <= 0) ? procdata->bytes_per_line : HP_TMP_BUF_SIZE; ph->tmp_buf = sanei_hp_alloc (tsz); if (ph->tmp_buf == NULL) { sanei_hp_free (ph); return NULL; } ph->tmp_buf_size = tsz; ph->tmp_buf_len = 0; ph->map = map; ph->outfd = outfd; if ( procdata->mirror_vertical || use_imgbuf) { tsz = procdata->lines*procdata->bytes_per_line; if (procdata->out8) tsz /= 2; ph->image_ptr = ph->image_buf = sanei_hp_alloc (tsz); if ( !ph->image_buf ) { procdata->mirror_vertical = 0; ph->image_buf_size = 0; DBG(1, "process_scanline_init: Not enough memory to mirror image\n"); } else ph->image_buf_size = tsz; } ph->wr_ptr = ph->wr_buf; ph->wr_buf_size = ph->wr_left = sizeof (ph->wr_buf); return ph; } static SANE_Status process_data_write (PROCDATA_HANDLE *ph, unsigned char *data, int nbytes) {int ncopy; if (ph == NULL) return SANE_STATUS_INVAL; /* Fill up write buffer */ ncopy = ph->wr_left; if (ncopy > nbytes) ncopy = nbytes; memcpy (ph->wr_ptr, data, ncopy); ph->wr_ptr += ncopy; ph->wr_left -= ncopy; data += ncopy; nbytes -= ncopy; if ( ph->wr_left > 0 ) /* Did not fill up the write buffer ? Finished */ return SANE_STATUS_GOOD; DBG(12, "process_data_write: write %d bytes\n", ph->wr_buf_size); /* Don't write data if we got a signal in the meantime */ if ( signal_caught || (write (ph->outfd, ph->wr_buf, ph->wr_buf_size) != ph->wr_buf_size)) { DBG(1, "process_data_write: write failed: %s\n", signal_caught ? "signal caught" : strerror(errno)); return SANE_STATUS_IO_ERROR; } ph->wr_ptr = ph->wr_buf; ph->wr_left = ph->wr_buf_size; /* For large amount of data write it from data-buffer */ while ( nbytes > ph->wr_buf_size ) { if ( signal_caught || (write (ph->outfd, data, ph->wr_buf_size) != ph->wr_buf_size)) { DBG(1, "process_data_write: write failed: %s\n", signal_caught ? "signal caught" : strerror(errno)); return SANE_STATUS_IO_ERROR; } nbytes -= ph->wr_buf_size; data += ph->wr_buf_size; } if ( nbytes > 0 ) /* Something left ? Save it to (empty) write buffer */ { memcpy (ph->wr_ptr, data, nbytes); ph->wr_ptr += nbytes; ph->wr_left -= nbytes; } return SANE_STATUS_GOOD; } static SANE_Status process_scanline (PROCDATA_HANDLE *ph, unsigned char *linebuf, int bytes_per_line) {int out_bytes_per_line = bytes_per_line; HpProcessData *procdata; if (ph == NULL) return SANE_STATUS_INVAL; procdata = &(ph->procdata); if ( ph->map ) hp_data_map (ph->map, bytes_per_line, linebuf); if (procdata->bits_per_channel > 8) { if (procdata->out8) { hp_scale_to_8bit( bytes_per_line/2, linebuf, procdata->bits_per_channel, procdata->invert); out_bytes_per_line /= 2; } else { hp_scale_to_16bit( bytes_per_line/2, linebuf, procdata->bits_per_channel, procdata->invert); } } else if (procdata->invert) { hp_soft_invert(bytes_per_line,linebuf); } if ( ph->image_buf ) { DBG(5, "process_scanline: save in memory\n"); if ( ph->image_ptr+out_bytes_per_line-1 <= ph->image_buf+ph->image_buf_size-1 ) { memcpy(ph->image_ptr, linebuf, out_bytes_per_line); ph->image_ptr += out_bytes_per_line; } else { DBG(1, "process_scanline: would exceed image buffer\n"); } } else /* Save scanlines in a bigger buffer. */ { /* Otherwise we will get performance problems */ RETURN_IF_FAIL ( process_data_write (ph, linebuf, out_bytes_per_line) ); } return SANE_STATUS_GOOD; } static SANE_Status process_data (PROCDATA_HANDLE *ph, unsigned char *read_ptr, int nread) {int bytes_left; if (nread <= 0) return SANE_STATUS_GOOD; if (ph == NULL) return SANE_STATUS_INVAL; if ( ph->tmp_buf_len > 0 ) /* Something left ? */ { bytes_left = ph->tmp_buf_size - ph->tmp_buf_len; if (nread < bytes_left) /* All to buffer ? */ { memcpy (ph->tmp_buf+ph->tmp_buf_len, read_ptr, nread); ph->tmp_buf_len += nread; return SANE_STATUS_GOOD; } memcpy (ph->tmp_buf+ph->tmp_buf_len, read_ptr, bytes_left); read_ptr += bytes_left; nread -= bytes_left; RETURN_IF_FAIL ( process_scanline (ph, ph->tmp_buf, ph->tmp_buf_size) ); ph->tmp_buf_len = 0; } while (nread > 0) { if (nread >= ph->tmp_buf_size) { RETURN_IF_FAIL ( process_scanline (ph, read_ptr, ph->tmp_buf_size) ); read_ptr += ph->tmp_buf_size; nread -= ph->tmp_buf_size; } else { memcpy (ph->tmp_buf, read_ptr, nread); ph->tmp_buf_len = nread; nread = 0; } } return SANE_STATUS_GOOD; } static SANE_Status process_data_flush (PROCDATA_HANDLE *ph) {SANE_Status status = SANE_STATUS_GOOD; HpProcessData *procdata; unsigned char *image_data; size_t image_len; int num_lines, bytes_per_line; int nbytes; if (ph == NULL) return SANE_STATUS_INVAL; if ( ph->tmp_buf_len > 0 ) process_scanline (ph, ph->tmp_buf, ph->tmp_buf_len); if ( ph->wr_left != ph->wr_buf_size ) /* Something in write buffer ? */ { nbytes = ph->wr_buf_size - ph->wr_left; if ( signal_caught || (write (ph->outfd, ph->wr_buf, nbytes) != nbytes)) { DBG(1, "process_data_flush: write failed: %s\n", signal_caught ? "signal caught" : strerror(errno)); return SANE_STATUS_IO_ERROR; } ph->wr_ptr = ph->wr_buf; ph->wr_left = ph->wr_buf_size; } procdata = &(ph->procdata); if ( ph->image_buf ) { bytes_per_line = procdata->bytes_per_line; if (procdata->out8) bytes_per_line /= 2; image_len = (size_t) (ph->image_ptr - ph->image_buf); num_lines = ((int)(image_len + bytes_per_line-1)) / bytes_per_line; DBG(3, "process_data_finish: write %d bytes from memory...\n", (int)image_len); if ( procdata->mirror_vertical ) { image_data = ph->image_buf + (num_lines-1) * bytes_per_line; while (num_lines > 0 ) { if ( signal_caught || (write(ph->outfd, image_data, bytes_per_line) != bytes_per_line)) { DBG(1,"process_data_finish: write from memory failed: %s\n", signal_caught ? "signal caught" : strerror(errno)); status = SANE_STATUS_IO_ERROR; break; } num_lines--; image_data -= bytes_per_line; } } else { image_data = ph->image_buf; while (num_lines > 0 ) { if ( signal_caught || (write(ph->outfd, image_data, bytes_per_line) != bytes_per_line)) { DBG(1,"process_data_finish: write from memory failed: %s\n", signal_caught ? "signal caught" : strerror(errno)); status = SANE_STATUS_IO_ERROR; break; } num_lines--; image_data += bytes_per_line; } } } return status; } static void process_data_finish (PROCDATA_HANDLE *ph) { DBG(12, "process_data_finish called\n"); if (ph == NULL) return; if (ph->image_buf != NULL) sanei_hp_free (ph->image_buf); sanei_hp_free (ph->tmp_buf); sanei_hp_free (ph); } SANE_Status sanei_hp_scsi_pipeout (HpScsi this, int outfd, HpProcessData *procdata) { /* We will catch these signals, and rethrow them after cleaning up, * anything not in this list, we will ignore. */ static int kill_sig[] = { SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGPIPE, SIGALRM, SIGTERM, SIGUSR1, SIGUSR2, SIGBUS, #ifdef SIGSTKFLT SIGSTKFLT, #endif #ifdef SIGIO SIGIO, #else # ifdef SIGPOLL SIGPOLL, # endif #endif #ifdef SIGXCPU SIGXCPU, #endif #ifdef SIGXFSZ SIGXFSZ, #endif #ifdef SIGVTALRM SIGVTALRM, #endif #ifdef SIGPWR SIGPWR, #endif }; #define HP_NSIGS (sizeof(kill_sig)/sizeof(kill_sig[0])) struct SIGACTION old_handler[HP_NSIGS]; struct SIGACTION sa; sigset_t old_set, sig_set; int i; int bits_per_channel = procdata->bits_per_channel; #define HP_PIPEBUF 32768 SANE_Status status = SANE_STATUS_GOOD; struct { size_t len; void * id; hp_byte_t cmd[6]; hp_byte_t data[HP_PIPEBUF]; } buf[2], *req = NULL; int reqs_completed = 0; int reqs_issued = 0; char *image_buf = 0; char *read_buf = 0; const HpDeviceInfo *info; const char *devname = sanei_hp_scsi_devicename (this); int enable_requests = 1; int enable_image_buffering = 0; const unsigned char *map = NULL; HpConnect connect; PROCDATA_HANDLE *ph = NULL; size_t count = procdata->lines * procdata->bytes_per_line; RETURN_IF_FAIL( hp_scsi_flush(this) ); connect = sanei_hp_get_connect (devname); info = sanei_hp_device_info_get (devname); assert (info); if ( info->config_is_up ) { enable_requests = info->config.use_scsi_request; enable_image_buffering = info->config.use_image_buffering; } else { enable_requests = 0; } if (connect != HP_CONNECT_SCSI) enable_requests = 0; /* Currently we can only simulate 8 bits mapping */ if (bits_per_channel == 8) map = hp_get_simulation_map (devname, info); sigfillset(&sig_set); sigprocmask(SIG_BLOCK, &sig_set, &old_set); memset(&sa, 0, sizeof(sa)); sa.sa_handler = signal_catcher; sigfillset(&sa.sa_mask); sigemptyset(&sig_set); for (i = 0; i < (int)(HP_NSIGS); i++) { sigaction(kill_sig[i], &sa, &old_handler[i]); sigaddset(&sig_set, kill_sig[i]); } signal_caught = 0; sigprocmask(SIG_UNBLOCK, &sig_set, 0); /* Wait for front button push ? */ if ( procdata->startscan ) { for (;;) {int val = 0; if (signal_caught) goto quit; sanei_hp_scl_inquire (this, SCL_FRONT_BUTTON, &val, 0, 0); if (val) break; usleep ((unsigned long)333*1000); /* Wait 1/3 second */ } status = sanei_hp_scl_startScan (this, procdata->startscan); if (status != SANE_STATUS_GOOD ) { DBG(1, "do_read: Error starting scan in reader process\n"); goto quit; } } ph = process_data_init (procdata, map, outfd, enable_image_buffering); if ( ph == NULL ) { DBG(1, "do_read: Error with process_data_init()\n"); goto quit; } DBG(1, "do_read: Start reading data from scanner\n"); if (enable_requests) /* Issue SCSI-requests ? */ { while (count > 0 || reqs_completed < reqs_issued) { while (count > 0 && reqs_issued < reqs_completed + 2) { req = buf + (reqs_issued++ % 2); req->len = HP_PIPEBUF; if (count < req->len) req->len = count; count -= req->len; req->cmd[0] = 0x08; req->cmd[1] = 0; req->cmd[2] = req->len >> 16; req->cmd[3] = req->len >> 8; req->cmd[4] = req->len; req->cmd[5] = 0; DBG(3, "do_read: entering request to read %lu bytes\n", (unsigned long) req->len); status = sanei_scsi_req_enter(this->fd, req->cmd, 6, req->data, &req->len, &req->id); if (status != SANE_STATUS_GOOD) { DBG(1, "do_read: Error from scsi_req_enter: %s\n", sane_strstatus(status)); goto quit; } if (signal_caught) goto quit; } if (signal_caught) goto quit; assert(reqs_completed < reqs_issued); req = buf + (reqs_completed++ % 2); DBG(3, "do_read: waiting for data\n"); status = sanei_scsi_req_wait(req->id); if (status != SANE_STATUS_GOOD) { DBG(1, "do_read: Error from scsi_req_wait: %s\n", sane_strstatus(status)); goto quit; } if (signal_caught) goto quit; status = process_data (ph, (unsigned char *)req->data, (int)req->len); if ( status != SANE_STATUS_GOOD ) { DBG(1,"do_read: Error in process_data\n"); goto quit; } } } else /* Read directly */ { read_buf = sanei_hp_alloc ( HP_PIPEBUF ); if (!read_buf) { DBG(1, "do_read: not enough memory for read buffer\n"); goto quit; } while (count > 0) {size_t nread; if (signal_caught) goto quit; DBG(5, "do_read: %lu bytes left to read\n", (unsigned long)count); nread = HP_PIPEBUF; if (nread > count) nread = count; DBG(3, "do_read: try to read data (%lu bytes)\n", (unsigned long)nread); status = hp_scsi_read (this, read_buf, &nread, 0); if (status != SANE_STATUS_GOOD) { DBG(1, "do_read: Error from scsi_read: %s\n",sane_strstatus(status)); goto quit; } DBG(3, "do_read: got %lu bytes\n", (unsigned long)nread); if (nread <= 0) { DBG(1, "do_read: Nothing read\n"); continue; } status = process_data (ph, (unsigned char *)read_buf, (int)nread); if ( status != SANE_STATUS_GOOD ) { DBG(1,"do_read: Error in process_data\n"); goto quit; } count -= nread; } } process_data_flush (ph); quit: process_data_finish (ph); if ( image_buf ) sanei_hp_free ( image_buf ); if ( read_buf ) sanei_hp_free ( read_buf ); if (enable_requests && (reqs_completed < reqs_issued)) { DBG(1, "do_read: cleaning up leftover requests\n"); while (reqs_completed < reqs_issued) { req = buf + (reqs_completed++ % 2); sanei_scsi_req_wait(req->id); } } sigfillset(&sig_set); sigprocmask(SIG_BLOCK, &sig_set, 0); for (i = 0; i < (int)(HP_NSIGS); i++) sigaction(kill_sig[i], &old_handler[i], 0); sigprocmask(SIG_SETMASK, &old_set, 0); if (signal_caught) { DBG(1, "do_read: caught signal %d\n", signal_caught); raise(signal_caught); return SANE_STATUS_CANCELLED; } return status; } /* * */ static SANE_Status _hp_scl_inq (HpScsi scsi, HpScl scl, HpScl inq_cmnd, void *valp, size_t *lengthp) { size_t bufsize = 16 + (lengthp ? *lengthp: 0); char * buf = alloca(bufsize); char expect[16], expect_char; int val, count; SANE_Status status; if (!buf) return SANE_STATUS_NO_MEM; /* Flush data before sending inquiry. */ /* Otherwise scanner might not generate a response. */ RETURN_IF_FAIL( hp_scsi_flush (scsi)) ; RETURN_IF_FAIL( hp_scsi_scl(scsi, inq_cmnd, SCL_INQ_ID(scl)) ); usleep (1000); /* 500 works, too, but not 100 */ status = hp_scsi_read(scsi, buf, &bufsize, 1); if (FAILED(status)) { DBG(1, "scl_inq: read failed (%s)\n", sane_strstatus(status)); return status; } if (SCL_PARAM_CHAR(inq_cmnd) == 'R') expect_char = 'p'; else expect_char = tolower(SCL_PARAM_CHAR(inq_cmnd) - 1); count = sprintf(expect, "\033*s%d%c", SCL_INQ_ID(scl), expect_char); if (memcmp(buf, expect, count) != 0) { DBG(1, "scl_inq: malformed response: expected '%s', got '%.*s'\n", expect, count, buf); return SANE_STATUS_IO_ERROR; } buf += count; if (buf[0] == 'N') { /* null response */ DBG(3, "scl_inq: parameter %d unsupported\n", SCL_INQ_ID(scl)); return SANE_STATUS_UNSUPPORTED; } if (sscanf(buf, "%d%n", &val, &count) != 1) { DBG(1, "scl_inq: malformed response: expected int, got '%.8s'\n", buf); return SANE_STATUS_IO_ERROR; } buf += count; expect_char = lengthp ? 'W' : 'V'; if (*buf++ != expect_char) { DBG(1, "scl_inq: malformed response: expected '%c', got '%.4s'\n", expect_char, buf - 1); return SANE_STATUS_IO_ERROR; } if (!lengthp) *(int *)valp = val; /* Get integer value */ else { if (val > (int)*lengthp) { DBG(1, "scl_inq: inquiry returned %d bytes, expected <= %lu\n", val, (unsigned long) *lengthp); return SANE_STATUS_IO_ERROR; } *lengthp = val; memcpy(valp, buf , *lengthp); /* Get binary data */ } return SANE_STATUS_GOOD; } SANE_Status sanei_hp_scl_upload_binary (HpScsi scsi, HpScl scl, size_t *lengthhp, char **bufhp) { size_t bufsize = 16, sv; char * buf = alloca(bufsize); char * bufstart = buf; char * hpdata; char expect[16], expect_char; int n, val, count; SANE_Status status; if (!buf) return SANE_STATUS_NO_MEM; assert ( IS_SCL_DATA_TYPE (scl) ); /* Flush data before sending inquiry. */ /* Otherwise scanner might not generate a response. */ RETURN_IF_FAIL( hp_scsi_flush (scsi)) ; RETURN_IF_FAIL( hp_scsi_scl(scsi, SCL_UPLOAD_BINARY_DATA, SCL_INQ_ID(scl)) ); status = hp_scsi_read(scsi, buf, &bufsize, 0); if (FAILED(status)) { DBG(1, "scl_upload_binary: read failed (%s)\n", sane_strstatus(status)); return status; } expect_char = 't'; count = sprintf(expect, "\033*s%d%c", SCL_INQ_ID(scl), expect_char); if (memcmp(buf, expect, count) != 0) { DBG(1, "scl_upload_binary: malformed response: expected '%s', got '%.*s'\n", expect, count, buf); return SANE_STATUS_IO_ERROR; } buf += count; if (buf[0] == 'N') { /* null response */ DBG(1, "scl_upload_binary: parameter %d unsupported\n", SCL_INQ_ID(scl)); return SANE_STATUS_UNSUPPORTED; } if (sscanf(buf, "%d%n", &val, &count) != 1) { DBG(1, "scl_inq: malformed response: expected int, got '%.8s'\n", buf); return SANE_STATUS_IO_ERROR; } buf += count; expect_char = 'W'; if (*buf++ != expect_char) { DBG(1, "scl_inq: malformed response: expected '%c', got '%.4s'\n", expect_char, buf - 1); return SANE_STATUS_IO_ERROR; } *lengthhp = val; *bufhp = hpdata = sanei_hp_alloc ( val ); if (!hpdata) return SANE_STATUS_NO_MEM; if (buf < bufstart + bufsize) { n = bufsize - (buf - bufstart); if (n > val) n = val; memcpy (hpdata, buf, n); hpdata += n; val -= n; } status = SANE_STATUS_GOOD; if ( val > 0 ) { sv = val; status = hp_scsi_read(scsi, hpdata, &sv, 0); if (status != SANE_STATUS_GOOD) sanei_hp_free ( *bufhp ); } return status; } SANE_Status sanei_hp_scl_set(HpScsi scsi, HpScl scl, int val) { RETURN_IF_FAIL( hp_scsi_scl(scsi, scl, val) ); #ifdef PARANOID RETURN_IF_FAIL( sanei_hp_scl_errcheck(scsi) ); #endif return SANE_STATUS_GOOD; } SANE_Status sanei_hp_scl_inquire(HpScsi scsi, HpScl scl, int * valp, int * minp, int * maxp) { HpScl inquiry = ( IS_SCL_CONTROL(scl) ? SCL_INQUIRE_PRESENT_VALUE : SCL_INQUIRE_DEVICE_PARAMETER ); assert(IS_SCL_CONTROL(scl) || IS_SCL_PARAMETER(scl)); assert(IS_SCL_CONTROL(scl) || (!minp && !maxp)); if (valp) RETURN_IF_FAIL( _hp_scl_inq(scsi, scl, inquiry, valp, 0) ); if (minp) RETURN_IF_FAIL( _hp_scl_inq(scsi, scl, SCL_INQUIRE_MINIMUM_VALUE, minp, 0) ); if (maxp) RETURN_IF_FAIL( _hp_scl_inq(scsi, scl, SCL_INQUIRE_MAXIMUM_VALUE, maxp, 0) ); return SANE_STATUS_GOOD; } #ifdef _HP_NOT_USED static SANE_Status hp_scl_get_bounds(HpScsi scsi, HpScl scl, int * minp, int * maxp) { assert(IS_SCL_CONTROL(scl)); RETURN_IF_FAIL( _hp_scl_inq(scsi, scl, SCL_INQUIRE_MINIMUM_VALUE, minp, 0) ); return _hp_scl_inq(scsi, scl, SCL_INQUIRE_MAXIMUM_VALUE, maxp, 0); } #endif #ifdef _HP_NOT_USED static SANE_Status hp_scl_get_bounds_and_val(HpScsi scsi, HpScl scl, int * minp, int * maxp, int * valp) { assert(IS_SCL_CONTROL(scl)); RETURN_IF_FAIL( _hp_scl_inq(scsi, scl, SCL_INQUIRE_MINIMUM_VALUE, minp, 0) ); RETURN_IF_FAIL( _hp_scl_inq(scsi, scl, SCL_INQUIRE_MAXIMUM_VALUE, maxp, 0) ); return _hp_scl_inq(scsi, scl, SCL_INQUIRE_PRESENT_VALUE, valp, 0); } #endif SANE_Status sanei_hp_scl_download(HpScsi scsi, HpScl scl, const void * valp, size_t len) { assert(IS_SCL_DATA_TYPE(scl)); sanei_hp_scl_clearErrors ( scsi ); RETURN_IF_FAIL( hp_scsi_need(scsi, 16) ); RETURN_IF_FAIL( hp_scsi_scl(scsi, SCL_DOWNLOAD_TYPE, SCL_INQ_ID(scl)) ); /* Download type not supported ? */ RETURN_IF_FAIL( sanei_hp_scl_errcheck(scsi) ); RETURN_IF_FAIL( hp_scsi_scl(scsi, SCL_DOWNLOAD_LENGTH, len) ); RETURN_IF_FAIL( hp_scsi_write(scsi, valp, len) ); #ifdef PARANOID RETURN_IF_FAIL( sanei_hp_scl_errcheck(scsi) ); #endif return SANE_STATUS_GOOD; } SANE_Status sanei_hp_scl_upload(HpScsi scsi, HpScl scl, void * valp, size_t len) { size_t nread = len; HpScl inquiry = ( IS_SCL_DATA_TYPE(scl) ? SCL_UPLOAD_BINARY_DATA : SCL_INQUIRE_DEVICE_PARAMETER ); assert(IS_SCL_DATA_TYPE(scl) || IS_SCL_PARAMETER(scl)); RETURN_IF_FAIL( _hp_scl_inq(scsi, scl, inquiry, valp, &nread) ); if (IS_SCL_PARAMETER(scl) && nread < len) ((char *)valp)[nread] = '\0'; else if (len != nread) { DBG(1, "scl_upload: requested %lu bytes, got %lu\n", (unsigned long) len, (unsigned long) nread); return SANE_STATUS_IO_ERROR; } return SANE_STATUS_GOOD; } SANE_Status sanei_hp_scl_calibrate(HpScsi scsi) { RETURN_IF_FAIL( hp_scsi_scl(scsi, SCL_CALIBRATE, 0) ); return hp_scsi_flush(scsi); } SANE_Status sanei_hp_scl_startScan(HpScsi scsi, HpScl scl) { char *msg = ""; if (scl == SCL_ADF_SCAN) msg = " (ADF)"; else if (scl == SCL_XPA_SCAN) msg = " (XPA)"; else scl = SCL_START_SCAN; DBG(1, "sanei_hp_scl_startScan: Start scan%s\n", msg); /* For active XPA we must not use XPA scan */ if ((scl == SCL_XPA_SCAN) && sanei_hp_is_active_xpa (scsi)) { DBG(3,"Map XPA scan to scan because of active XPA\n"); scl = SCL_START_SCAN; } RETURN_IF_FAIL( hp_scsi_scl(scsi, scl, 0) ); return hp_scsi_flush(scsi); } SANE_Status sanei_hp_scl_reset(HpScsi scsi) { RETURN_IF_FAIL( hp_scsi_write(scsi, "\033E", 2) ); RETURN_IF_FAIL( hp_scsi_flush(scsi) ); return sanei_hp_scl_errcheck(scsi); } SANE_Status sanei_hp_scl_clearErrors(HpScsi scsi) { RETURN_IF_FAIL( hp_scsi_flush(scsi) ); RETURN_IF_FAIL( hp_scsi_write(scsi, "\033*oE", 4) ); return hp_scsi_flush(scsi); } static const char * hp_scl_strerror (int errnum) { static const char * errlist[] = { "Command Format Error", "Unrecognized Command", "Parameter Error", "Illegal Window", "Scaling Error", "Dither ID Error", "Tone Map ID Error", "Lamp Error", "Matrix ID Error", "Cal Strip Param Error", "Gross Calibration Error" }; if (errnum >= 0 && errnum < (int)(sizeof(errlist)/sizeof(errlist[0]))) return errlist[errnum]; else switch(errnum) { case 1024: return "ADF Paper Jam"; case 1025: return "Home Position Missing"; case 1026: return "Paper Not Loaded"; default: return "??Unknown Error??"; } } /* Check for SCL errors */ SANE_Status sanei_hp_scl_errcheck (HpScsi scsi) { int errnum; int nerrors; SANE_Status status; status = sanei_hp_scl_inquire(scsi, SCL_CURRENT_ERROR_STACK, &nerrors,0,0); if (!FAILED(status) && nerrors) status = sanei_hp_scl_inquire(scsi, SCL_OLDEST_ERROR, &errnum,0,0); if (FAILED(status)) { DBG(1, "scl_errcheck: Can't read SCL error stack: %s\n", sane_strstatus(status)); return SANE_STATUS_IO_ERROR; } if (nerrors) { DBG(1, "Scanner issued SCL error: (%d) %s\n", errnum, hp_scl_strerror(errnum)); sanei_hp_scl_clearErrors (scsi); return SANE_STATUS_IO_ERROR; } return SANE_STATUS_GOOD; } backends-1.3.0/backend/hp-scl.h000066400000000000000000000164521456256263500162410ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1997 Geoffrey T. Dairiki 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, see . 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 is part of a SANE backend for HP Scanners supporting HP Scanner Control Language (SCL). */ #ifndef HP_SCL_INCLUDED #define HP_SCL_INCLUDED #define HP_SCL_PACK(id, group, char) \ ((SANE_Word)(id) << 16 | ((group) & 0xFF) << 8 | ((char) & 0xFF)) #define SCL_INQ_ID(code) ((code) >> 16) #define SCL_GROUP_CHAR(code) ((char)(((code) >> 8) & 0xFF)) #define SCL_PARAM_CHAR(code) ((char)((code) & 0xFF)) #define HP_SCL_CONTROL(id,g,c) HP_SCL_PACK(id,g,c) #define HP_SCL_COMMAND(g,c) HP_SCL_PACK(0,g,c) #define HP_SCL_PARAMETER(id) HP_SCL_PACK(id, 0, 0) #define HP_SCL_DATA_TYPE(id) HP_SCL_PACK(id, 1, 0) #define IS_SCL_CONTROL(scl) (SCL_INQ_ID(scl) && SCL_PARAM_CHAR(scl)) #define IS_SCL_COMMAND(scl) (!SCL_INQ_ID(scl) && SCL_PARAM_CHAR(scl)) #define IS_SCL_PARAMETER(scl) (SCL_INQ_ID(scl) && !SCL_PARAM_CHAR(scl)) #define IS_SCL_DATA_TYPE(scl) (SCL_GROUP_CHAR(scl) == '\001') #define SCL_AUTO_BKGRND HP_SCL_CONTROL(10307, 'a', 'B') #define SCL_COMPRESSION HP_SCL_CONTROL(10308, 'a', 'C') #define SCL_DOWNLOAD_TYPE HP_SCL_CONTROL(10309, 'a', 'D') #define SCL_X_SCALE HP_SCL_CONTROL(10310, 'a', 'E') #define SCL_Y_SCALE HP_SCL_CONTROL(10311, 'a', 'F') #define SCL_DATA_WIDTH HP_SCL_CONTROL(10312, 'a', 'G') #define SCL_INVERSE_IMAGE HP_SCL_CONTROL(10314, 'a', 'I') #define SCL_BW_DITHER HP_SCL_CONTROL(10315, 'a', 'J') #define SCL_CONTRAST HP_SCL_CONTROL(10316, 'a', 'K') #define SCL_BRIGHTNESS HP_SCL_CONTROL(10317, 'a', 'L') #define SCL_MIRROR_IMAGE HP_SCL_CONTROL(10318, 'a', 'M') #define SCL_SHARPENING HP_SCL_CONTROL(10319, 'a', 'N') #define SCL_RESERVED1 HP_SCL_CONTROL(10320, 'a', 'O') #define SCL_X_RESOLUTION HP_SCL_CONTROL(10323, 'a', 'R') #define SCL_Y_RESOLUTION HP_SCL_CONTROL(10324, 'a', 'S') #define SCL_OUTPUT_DATA_TYPE HP_SCL_CONTROL(10325, 'a', 'T') #define SCL_DOWNLOAD_LENGTH HP_SCL_CONTROL(10328, 'a', 'W') #define SCL_PRELOAD_ADF HP_SCL_CONTROL(10468, 'f', 'C') #define SCL_MEDIA HP_SCL_CONTROL(10469, 'f', 'D') #define SCL_10470 HP_SCL_CONTROL(10470, 'f', 'E') #define SCL_LAMPTEST HP_SCL_CONTROL(10477, 'f', 'L') #define SCL_X_EXTENT HP_SCL_CONTROL(10481, 'f', 'P') #define SCL_Y_EXTENT HP_SCL_CONTROL(10482, 'f', 'Q') #define SCL_START_SCAN HP_SCL_COMMAND('f', 'S') #define SCL_10485 HP_SCL_CONTROL(10485, 'f', 'T') #define SCL_10488 HP_SCL_CONTROL(10488, 'f', 'W') #define SCL_X_POS HP_SCL_CONTROL(10489, 'f', 'X') #define SCL_Y_POS HP_SCL_CONTROL(10490, 'f', 'Y') #define SCL_XPA_SCAN HP_SCL_COMMAND('u', 'D') #define SCL_SPEED HP_SCL_CONTROL(10950, 'u', 'E') #define SCL_FILTER HP_SCL_CONTROL(10951, 'u', 'F') #define SCL_10952 HP_SCL_CONTROL(10952, 'u', 'G') #define SCL_XPA_DISABLE HP_SCL_CONTROL(10953, 'u', 'H') #define SCL_TONE_MAP HP_SCL_CONTROL(10956, 'u', 'K') #define SCL_CALIBRATE HP_SCL_COMMAND('u', 'R') #define SCL_ADF_SCAN HP_SCL_COMMAND('u', 'S') #define SCL_MATRIX HP_SCL_CONTROL(10965, 'u', 'T') #define SCL_UNLOAD HP_SCL_CONTROL(10966, 'u', 'U') #define SCL_10967 HP_SCL_CONTROL(10967, 'u', 'V') #define SCL_CHANGE_DOC HP_SCL_CONTROL(10969, 'u', 'X') #define SCL_ADF_BFEED HP_SCL_CONTROL(10970, 'u', 'Y') /* Clear Errors does not follow command syntax Esc*o0E, it is only Esc*oE */ /* #define SCL_CLEAR_ERRORS HP_SCL_COMMAND('o', 'E') */ #define SCL_INQUIRE_PRESENT_VALUE HP_SCL_COMMAND('s', 'R') #define SCL_INQUIRE_MINIMUM_VALUE HP_SCL_COMMAND('s', 'L') #define SCL_INQUIRE_MAXIMUM_VALUE HP_SCL_COMMAND('s', 'H') #define SCL_INQUIRE_DEVICE_PARAMETER HP_SCL_COMMAND('s', 'E') #define SCL_UPLOAD_BINARY_DATA HP_SCL_COMMAND('s', 'U') #define SCL_HP_MODEL_1 HP_SCL_PARAMETER(3) #define SCL_HP_MODEL_2 HP_SCL_PARAMETER(10) #define SCL_HP_MODEL_3 HP_SCL_PARAMETER(9) #define SCL_HP_MODEL_4 HP_SCL_PARAMETER(11) #define SCL_HP_MODEL_5 HP_SCL_PARAMETER(12) #define SCL_HP_MODEL_6 HP_SCL_PARAMETER(14) #define SCL_HP_MODEL_8 HP_SCL_PARAMETER(15) #define SCL_HP_MODEL_9 HP_SCL_PARAMETER(16) #define SCL_HP_MODEL_10 HP_SCL_PARAMETER(17) #define SCL_HP_MODEL_11 HP_SCL_PARAMETER(18) #define SCL_HP_MODEL_12 HP_SCL_PARAMETER(19) #define SCL_HP_MODEL_14 HP_SCL_PARAMETER(21) #define SCL_HP_MODEL_16 HP_SCL_PARAMETER(31) #define SCL_HP_MODEL_17 HP_SCL_PARAMETER(32) #define SCL_ADF_CAPABILITY HP_SCL_PARAMETER(24) #define SCL_ADF_BIN HP_SCL_PARAMETER(25) #define SCL_ADF_RDY_UNLOAD HP_SCL_PARAMETER(27) #define SCL_CURRENT_ERROR_STACK HP_SCL_PARAMETER(257) #define SCL_CURRENT_ERROR HP_SCL_PARAMETER(259) #define SCL_OLDEST_ERROR HP_SCL_PARAMETER(261) #define SCL_PIXELS_PER_LINE HP_SCL_PARAMETER(1024) #define SCL_BYTES_PER_LINE HP_SCL_PARAMETER(1025) #define SCL_NUMBER_OF_LINES HP_SCL_PARAMETER(1026) #define SCL_ADF_READY HP_SCL_PARAMETER(1027) #define SCL_DEVPIX_RESOLUTION HP_SCL_PARAMETER(1028) #define SCL_AUTO_SPEED HP_SCL_PARAMETER(1040) #define SCL_FRONT_BUTTON HP_SCL_PARAMETER(1044) #define SCL_PRELOADED HP_SCL_PARAMETER(1045) /* The following is not documented */ #define SCL_SECONDARY_SCANDIR HP_SCL_PARAMETER(1047) #define SCL_BW8x8DITHER HP_SCL_DATA_TYPE(0) #define SCL_8x8TONE_MAP HP_SCL_DATA_TYPE(1) #define SCL_8x9MATRIX_COEFF HP_SCL_DATA_TYPE(2) #define SCL_8x8DITHER HP_SCL_DATA_TYPE(3) #define SCL_CAL_STRIP HP_SCL_DATA_TYPE(4) #define SCL_BW16x16DITHER HP_SCL_DATA_TYPE(5) #define SCL_10x8TONE_MAP HP_SCL_DATA_TYPE(6) #define SCL_10x3MATRIX_COEFF HP_SCL_DATA_TYPE(8) #define SCL_10x9MATRIX_COEFF HP_SCL_DATA_TYPE(9) #define SCL_7x12TONE_MAP HP_SCL_DATA_TYPE(10) #define SCL_BW7x12TONE_MAP HP_SCL_DATA_TYPE(11) #define SCL_RGB_GAINS HP_SCL_DATA_TYPE(11) #define SCL_CALIB_MAP HP_SCL_DATA_TYPE(14) #endif /* HP_SCL_INCLUDED */ backends-1.3.0/backend/hp-scsi.h000066400000000000000000000065371456256263500164240ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1997 Geoffrey T. Dairiki 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, see . 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 is part of a SANE backend for HP Scanners supporting HP Scanner Control Language (SCL). */ #ifndef HP_SCSI_INCLUDED #define HP_SCSI_INCLUDED #define HP_SCSI_MAX_WRITE (2048) SANE_Status sanei_hp_nonscsi_new (HpScsi * newp, const char * devname, HpConnect connect); SANE_Status sanei_hp_scsi_new (HpScsi * newp, const char * devname); void sanei_hp_scsi_destroy (HpScsi this,int completely); hp_byte_t * sanei_hp_scsi_inq (HpScsi this); const char *sanei_hp_scsi_model (HpScsi this); const char *sanei_hp_scsi_vendor (HpScsi this); const char *sanei_hp_scsi_devicename (HpScsi this); SANE_Status sanei_hp_scsi_pipeout (HpScsi this, int outfd, HpProcessData *pdescr); SANE_Status sanei_hp_scl_calibrate (HpScsi scsi); SANE_Status sanei_hp_scl_startScan (HpScsi scsi, HpScl scl); SANE_Status sanei_hp_scl_reset (HpScsi scsi); SANE_Status sanei_hp_scl_clearErrors (HpScsi scsi); SANE_Status sanei_hp_scl_errcheck (HpScsi scsi); SANE_Status sanei_hp_scl_upload_binary (HpScsi scsi, HpScl scl, size_t *lengthhp, char **bufhp); SANE_Status sanei_hp_scl_set (HpScsi scsi, HpScl scl, int val); SANE_Status sanei_hp_scl_inquire(HpScsi scsi, HpScl scl, int * valp, int * minp, int * maxp); SANE_Status sanei_hp_scl_upload (HpScsi scsi, HpScl scl, void * buf, size_t sz); SANE_Status sanei_hp_scl_download (HpScsi scsi, HpScl scl, const void * buf, size_t sz); #endif /* HP_SCSI_INCLUDED */ backends-1.3.0/backend/hp.README000066400000000000000000000046731456256263500161720ustar00rootroot00000000000000Fri Dec 5 18:10:52 PST 1997 Geoffrey T. Dairiki Here is a new HP backend. It's name is, appropriately, ``newhp''. It is still in the pre-alpha stages but mostly works on my ScanJet 5P. It is completely untested on other scanners, but might hopefully work on ScanJet models IIp, IIc, IIcx, 3p, 3c, 4c, 4p, and 5p. I've only really tested this with xscanimage. Some features included in this backend which aren't in the old HP driver are: Support for custom tonemaps (gamma-maps). For some reason I can't get the 10-bit tonemaps working with my 5p, so for you only get a single tonemap (it still works in color mode, but you don't get individual control over each R/G/B channel.) You can enable the scanjets "Auto Threshold" mode when scanning line-art. I can't notice that it does much though.... Added an option to control the scan speed. Added an option to select the dither pattern used in half-tone mode. I don't think that custom dither patterns are working correctly, and in any case, with the current front-ends, there is no convenient way to set a custom dither pattern.... Added an option to select the color correction matrixes. This also seems to be flaky. Again the current front-ends do not provide a decent way to adjust the custom color matrix. Tue Feb 24 20:00:00 UT+1 1998 Peter Kirchgessner Picked up the sources because they did not find their way to the SANE-package up to now. And because I want to have a better support for the HP Photosmart Photoscanner. Renamed the backend from newhp to xhp just to get a better acceptance. Added an option to perform calibration. Upload and download calibration data Added options for horizontal/vertical mirroring. Vertical mirroring is performed by loading image completely into memory before sending to frontend. Uses SCSI-flushing more often because otherwise the scanner did not accept all of the commands. Test support of commands to be independent from inquired model number. Wed Mar 28 10:00:00 UT+1 1998 Bring it on its way to the SANE-project. Geoffrey accepted to add licence information to the files as long as no one has to pay for the code. Sun Jun 07 15:00:00 UT+1 1998 Added unload button Fixed problem with custom gamma table Sat Nov 14 14:00:00 UT+1 1998 Show calibrate button on Photosmart only for print media Add HP 6200 C Suppress halftone mode on photosmart (it is not supported) backends-1.3.0/backend/hp.TODO000066400000000000000000000026551456256263500160000ustar00rootroot00000000000000These are Geoffreys TODOs: Fix ifdef ENABLE_'s Add emulation for color separation matrixes? Gamma plot initialization? Dither pattern option not working right. More use of FAILED macro , maybe SUCCESS macro ? Convert to single .h file? /** FIXME: ToDo * * Split up hp_scsi_open -- don't need full probe every time? * or if probing at least make sure answer is constant. * * Stale flage so don't download unchanged parameters? * * Fix or disable dither stuff. * * Add controls * * Preview mode: fast, grayscale instead of halftone. * Separate X and Y resolutions + bind button. * 10x8 tone maps? * Sharpening? * Matrixes - fix color separtion? * Dither pattern? COlor dither pattern? * * check return values (of maybe others)? * get rid of overly verbose DBG's * add some DBG's */ New TODOs: Find a way to set matrices in the frontend (gtk-table with sliders ?) Find a way to set dither matrices in the frontend (See the ENABLE_...-macros in hp.h to enable these features) Fix problem with performance of reader-process. Communication through the pipe might be too slow and make the scanner repeating several strips. Fix various problems with PhotoScanner: - rubbish on bottom of scanned image - too few data that is received on bottom of scanned image for PhotoScanner - scan time for slides at 2400 dpi is twice as long as under M$-Windows Support 10/12-bits for greyscale backends-1.3.0/backend/hp.c000066400000000000000000000656541456256263500154650ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1997 Geoffrey T. Dairiki Support for HP PhotoSmart Photoscanner by Peter Kirchgessner 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, see . 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 is part of a SANE backend for HP Scanners supporting HP Scanner Control Language (SCL). */ static char *hp_backend_version = "1.06"; /* Changes: V 1.06: Revision 1.22 2008/11/26 21:21:25 kitno-guest * backend/ *.[ch]: nearly every backend used V_MAJOR instead of SANE_CURRENT_MAJOR in sane_init() * backend/snapscan.c: remove EXPECTED_VERSION check since new SANE standard is forward compatible Revision 1.21 2004-10-04 18:09:05 kig-guest Rename global function hp_init_openfd to sanei_hp_init_openfd Revision 1.20 2004/03/27 13:52:39 kig-guest Keep USB-connection open (was problem with Linux 2.6.x) V 1.05: Revision 1.19 2003/10/24 17:26:07 kig-guest Use new sanei-thread-interface Revision 1.18 2003/10/09 19:37:29 kig-guest Redo when TEST UNIT READY failed Redo when read returns with 0 bytes (non-SCSI only) Bug #300241: fix inverse image on 3c/4c/6100C at 10 bit depth Revision 1.17 2003/10/06 19:54:07 kig-guest Bug #300248: correct "Negatives" to "Negative" in option description V 1.04, 24-Jul-2003, PK (peter@kirchgessner.net) - Add internationalization V 1.03, 14-Apr-2003, PK (peter@kirchgessner.net) - check valp in call of sane_control_option() V 1.02, 02-Feb-2003, PK (peter@kirchgessner.net) - add OS/2-support by Franz Bakan V 1.01, 06-Dec-2002, PK (peter@kirchgessner.net) - add option dumb-read to work around problems with BusLogic SCSI driver (error during device I/O) V 1.00, 17-Nov-2002, PK (peter@kirchgessner.net) - add libusb support V 0.96, 05-Aug-2002, PK (peter@kirchgessner.net) - check USB device names V 0.95, 07-Jul-2001, PK (peter@kirchgessner.net) - add support for active XPA - check if paper in ADF for ADF scan - add option lamp off - remove some really unused parameters V 0.94, 31-Dec-2000, PK (peter@kirchgessner.net) - always switch off lamp after scan V 0.93, 04-Dec-2000, PK (peter@kirchgessner.net) - fix problem with ADF-support on ScanJet 6350 (and maybe others) V 0.92, 03-Oct-2000, Rupert W. Curwen (rcurwen@uk.research.att.com): - try to not allocate accessors twice (only for accessors that have fixed length) - fix problem with leaving connection open for some error conditions V 0.91, 04-Sep-2000, David Paschal (paschal@rcsis.com): - Added support for flatbed HP OfficeJets - (PK) fix problem with cancel preview V 0.90, 02-Sep-2000, PK: - fix timing problem between killing child and writing to pipe - change fprintf(stderr,...) to DBG - change include to "sane.." in hp.h - change handling of options that have global effects. i.e. if option scanmode is received (has global effect), all options that "may change" are send to the scanner again. This fixes a problem that --resolution specified infront of --mode on command line of scanimage was ignored. NOTE: This change does not allow to specify --depth 12 infront of --mode color, because --depth is only enabled with --mode color. - add depth greater 8 bits for mode grayscale - add option for 8 bit output but 10/12 bit scanning V 0.88, 25-Jul-2000, PK: - remove inlines V 0.88, 20-Jul-2000, PK: - Use sanei_config_read() - don't write chars < 32 to DBG V 0.88, 09-Jul-2000, PK: - Add front button support by Chris S. Cowles, Houston, Texas, c_cowles@ieee.org V 0.87, 28-Jun-2000, PK: - ADF-support for ScanJet IIp - Return error SANE_STATUS_NO_DOCS if no paper in ADF V 0.86, 12-Feb-2000, PK: - fix gcc warnings - fix problems with bitdepths > 8 - allow hp_data_resize to be called with newsize==bufsiz (Jens Heise, ) - add option enable-image-buffering V 0.85, 30-Jan-2000, PK: - correct and enhance data widths > 8 (Ewald de Wit ) - enable data width for all scanners - PhotoSmart: exposure "Off" changed to "Default" - PhotoSmart: even if max. datawidth 24 is reported, allow 30 bits. - change keyword -data-width to -depth and use value for bits per sample - change keyword -halftone-type to -halftone-pattern - change keyword -scantype to -source - fix problem with multiple definition of sanei_debug_hp V 0.83, 04-Jul-99, PK: - reset scanner before downloading parameters (fixes problem with sleep mode of scanners) - fix problem with coredump if non-scanner HP SCSI devices are connected (CDR) - option scan-from-adf replaced by scantype normal/adf/xpa - change value "Film strip" to "Film-strip" for option --media-type - PhotoScanner: allow only scanning at multiple of 300 dpi for scanning slides/film strips. This also fixes a problem with the preview which uses arbitrary resolutions. - Marian Szebenyi: close pipe (endless loop on Digital UNIX) V 0.82, 28-Feb-99, Ewald de Wit : - add options 'exposure time' and 'data width' V 0.81, 11-Jan-99, PK: - occasionally 'scan from ADF' was active for Photoscanner V 0.80, 10-Jan-99, PK: - fix problem with scan size for ADF-scan (thanks to Christop Biardzki for tests) - add option "unload after scan" for HP PhotoScanner - no blanks in command line options - fix problem with segmentation fault for scanimage -d hp:/dev/sga with /dev/sga not included in hp.conf V 0.72, 25-Dec-98, PK: - add patches from mike@easysw.com to fix problems: - core dumps by memory alignment - config file to accept matching devices (scsi HP) - add simulation for brightness/contrast/custom gamma table if not supported by scanner - add configuration options for connect-... V 0.72c, 04-Dec-98, PK: - use sanei_pio - try ADF support V 0.72b, 29-Nov-98 James Carter , PK: - try to add parallel scanner support V 0.71, 14-Nov-98 PK: - add HP 6200 C - cleanup hp_scsi_s structure - show calibrate button on photoscanner only for print media - suppress halftone mode on photoscanner - add media selection for photoscanner V 0.70, 26-Jul-98 PK: - Rename global symbols to sanei_... Change filenames to hp-... Use backend name hp V 0.65, 18-Jul-98 PK: - Dont use pwd.h for VACPP-Compiler to get home-directory, check $SANE_HOME_XHP instead V 0.64, 12-Jul-98 PK: - only download calibration file for media = 1 (prints) - Changes for VACPP-Compiler (check macros __IBMC__, __IBMCPP__) V 0.63, 07-Jun-98 PK: - fix problem with custom gamma table - Add unload button V 0.62, 25-May-98 PK: - make it compilable under sane V 0.73 V 0.61, 28-Mar-98, Peter Kirchgessner : - Add support for HP PhotoSmart Photoscanner - Use more inquiries to see what the scanner supports - Add options: calibrate/Mirror horizontal+vertical - Upload/download calibration data */ #define VERSIO 8 #include "../include/sane/config.h" #include "hp.h" #include /* #include */ /* #include "../include/sane/sane.h" */ #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_thread.h" /* #include "../include/sane/sanei_debug.h" */ #include "hp-device.h" #include "hp-handle.h" #ifndef PATH_MAX # define PATH_MAX 1024 #endif #ifndef NDEBUG #include void sanei_hp_dbgdump (const void * bufp, size_t len) { const hp_byte_t *buf = bufp; int offset = 0; int i; char line[128], pt[32]; for (offset = 0; offset < (int)len; offset += 16) { sprintf (line," 0x%04X ", offset); for (i = offset; i < offset + 16 && i < (int)len; i++) { sprintf (pt," %02X", buf[i]); strcat (line, pt); } while (i++ < offset + 16) strcat (line, " "); strcat (line, " "); for (i = offset; i < offset + 16 && i < (int)len; i++) { sprintf (pt, "%c", isprint(buf[i]) ? buf[i] : '.'); strcat (line, pt); } DBG(16,"%s\n",line); } } #endif typedef struct info_list_el_s * HpDeviceInfoList; struct info_list_el_s { HpDeviceInfoList next; HpDeviceInfo info; }; typedef struct device_list_el_s * HpDeviceList; struct device_list_el_s { HpDeviceList next; HpDevice dev; }; /* Global state */ static struct hp_global_s { hp_bool_t is_up; hp_bool_t config_read; const SANE_Device ** devlist; HpDeviceList device_list; HpDeviceList handle_list; HpDeviceInfoList infolist; HpDeviceConfig config; } global; /* Get the info structure for a device. If not available in global list */ /* add new entry and return it */ static HpDeviceInfo * hp_device_info_create (const char *devname) { HpDeviceInfoList *infolist = &(global.infolist); HpDeviceInfoList infolistelement; HpDeviceInfo *info; int k, found; if (!global.is_up) return 0; found = 0; infolistelement = 0; info = 0; while (*infolist) { infolistelement = *infolist; info = &(infolistelement->info); if (strcmp (info->devname, devname) == 0) /* Already in list ? */ { found = 1; break; } infolist = &(infolistelement->next); } if (found) /* Clear old entry */ { memset (infolistelement, 0, sizeof (*infolistelement)); } else /* New element */ { infolistelement = (HpDeviceInfoList) sanei_hp_allocz (sizeof (*infolistelement)); if (!infolistelement) return 0; info = &(infolistelement->info); *infolist = infolistelement; } k = sizeof (info->devname); strncpy (info->devname, devname, k); info->devname[k-1] = '\0'; info->max_model = -1; info->active_xpa = -1; return info; } static void hp_init_config (HpDeviceConfig *config) { if (config) { config->connect = HP_CONNECT_SCSI; config->use_scsi_request = 1; config->use_image_buffering = 0; config->got_connect_type = 0; config->dumb_read = 0; } } static HpDeviceConfig * hp_global_config_get (void) { if (!global.is_up) return 0; return &(global.config); } static SANE_Status hp_device_config_add (const char *devname) { HpDeviceInfo *info; HpDeviceConfig *config; info = hp_device_info_create (devname); if (!info) return SANE_STATUS_INVAL; config = hp_global_config_get (); if (config) { memcpy (&(info->config), config, sizeof (info->config)); info->config_is_up = 1; } else /* Initialize with default configuration */ { DBG(3, "hp_device_config_add: No configuration found for device %s.\n\tUseing default\n", devname); hp_init_config (&(info->config)); info->config_is_up = 1; } return SANE_STATUS_GOOD; } HpDeviceInfo * sanei_hp_device_info_get (const char *devname) { HpDeviceInfoList *infolist; HpDeviceInfoList infolistelement; HpDeviceInfo *info; int retries = 1; if (!global.is_up) { DBG(17, "sanei_hp_device_info_get: global.is_up = %d\n", (int)global.is_up); return 0; } DBG(250, "sanei_hp_device_info_get: searching %s\n", devname); do { infolist = &(global.infolist); while (*infolist) { infolistelement = *infolist; info = &(infolistelement->info); DBG(250, "sanei_hp_device_info_get: check %s\n", info->devname); if (strcmp (info->devname, devname) == 0) /* Found ? */ { return info; } infolist = &(infolistelement->next); } /* No configuration found. Assume default */ DBG(1, "hp_device_info_get: device %s not configured. Using default\n", devname); if (hp_device_config_add (devname) != SANE_STATUS_GOOD) return 0; } while (retries-- > 0); return 0; } HpDevice sanei_hp_device_get (const char *devname) { HpDeviceList ptr; for (ptr = global.device_list; ptr; ptr = ptr->next) if (strcmp(sanei_hp_device_sanedevice(ptr->dev)->name, devname) == 0) return ptr->dev; return 0; } static void hp_device_info_remove (void) { HpDeviceInfoList next, infolistelement = global.infolist; if (!global.is_up) return; while (infolistelement) { next = infolistelement->next; sanei_hp_free (infolistelement); infolistelement = next; } } static SANE_Status hp_device_list_add (HpDeviceList * list, HpDevice dev) { HpDeviceList new = sanei_hp_alloc(sizeof(*new)); if (!new) return SANE_STATUS_NO_MEM; while (*list) list = &(*list)->next; *list = new; new->next = 0; new->dev = dev; return SANE_STATUS_GOOD; } static SANE_Status hp_device_list_remove (HpDeviceList * list, HpDevice dev) { HpDeviceList old; while (*list && (*list)->dev != dev) list = &(*list)->next; if (!*list) return SANE_STATUS_INVAL; old = *list; *list = (*list)->next; sanei_hp_free(old); return SANE_STATUS_GOOD; } static SANE_Status hp_handle_list_add (HpDeviceList * list, HpHandle h) { return hp_device_list_add(list, (HpDevice)h); } static SANE_Status hp_handle_list_remove (HpDeviceList * list, HpHandle h) { return hp_device_list_remove(list, (HpDevice)h); } static SANE_Status hp_init (void) { memset(&global, 0, sizeof(global)); global.is_up++; DBG(3, "hp_init: global.is_up = %d\n", (int)global.is_up); return SANE_STATUS_GOOD; } static void hp_destroy (void) { if (global.is_up) { /* Close open handles */ while (global.handle_list) sane_close(global.handle_list->dev); /* Remove device infos */ hp_device_info_remove (); sanei_hp_free_all(); global.is_up = 0; DBG(3, "hp_destroy: global.is_up = %d\n", (int)global.is_up); } } static SANE_Status hp_get_dev (const char *devname, HpDevice* devp) { HpDeviceList ptr; HpDevice new; const HpDeviceInfo *info; char *connect; HpConnect hp_connect; SANE_Status status; for (ptr = global.device_list; ptr; ptr = ptr->next) if (strcmp(sanei_hp_device_sanedevice(ptr->dev)->name, devname) == 0) { if (devp) *devp = ptr->dev; return SANE_STATUS_GOOD; } info = sanei_hp_device_info_get (devname); hp_connect = info->config.connect; if (hp_connect == HP_CONNECT_SCSI) connect = "scsi"; else if (hp_connect == HP_CONNECT_DEVICE) connect = "device"; else if (hp_connect == HP_CONNECT_PIO) connect = "pio"; else if (hp_connect == HP_CONNECT_USB) connect = "usb"; else if (hp_connect == HP_CONNECT_RESERVE) connect = "reserve"; else connect = "unknown"; DBG(3, "hp_get_dev: New device %s, connect-%s, scsi-request=%lu\n", devname, connect, (unsigned long)info->config.use_scsi_request); status = sanei_hp_device_new (&new, devname); if (status != SANE_STATUS_GOOD) return status; if (devp) *devp = new; RETURN_IF_FAIL( hp_device_list_add(&global.device_list, new) ); return SANE_STATUS_GOOD; } static SANE_Status hp_attach (const char *devname) { DBG(7,"hp_attach: \"%s\"\n", devname); hp_device_config_add (devname); return hp_get_dev (devname, 0); } static void hp_attach_matching_devices (HpDeviceConfig *config, const char *devname) { static int usb_initialized = 0; if (strncmp (devname, "usb", 3) == 0) { config->connect = HP_CONNECT_USB; config->use_scsi_request = 0; DBG(1,"hp_attach_matching_devices: usb attach matching \"%s\"\n",devname); if (!usb_initialized) { sanei_usb_init (); usb_initialized = 1; } sanei_usb_attach_matching_devices (devname, hp_attach); } else { DBG(1, "hp_attach_matching_devices: attach matching %s\n", devname); sanei_config_attach_matching_devices (devname, hp_attach); } } static SANE_Status hp_read_config (void) { FILE * fp; char buf[PATH_MAX], arg1[PATH_MAX], arg2[PATH_MAX], arg3[PATH_MAX]; int nl, nargs; HpDeviceConfig *config, df_config, dev_config; hp_bool_t is_df_config; char cu_device[PATH_MAX]; if (!global.is_up) return SANE_STATUS_INVAL; if (global.config_read) return SANE_STATUS_GOOD; /* The default config will keep options set up until the first device is specified */ hp_init_config (&df_config); config = &df_config; is_df_config = 1; cu_device[0] = '\0'; DBG(1, "hp_read_config: hp backend v%s starts reading config file\n", hp_backend_version); if ((fp = sanei_config_open(HP_CONFIG_FILE)) != 0) { while (sanei_config_read(buf, sizeof(buf), fp)) { char *dev_name; nl = strlen (buf); while (nl > 0) { nl--; if ( (buf[nl] == ' ') || (buf[nl] == '\t') || (buf[nl] == '\r') || (buf[nl] == '\n')) buf[nl] = '\0'; else break; } DBG(1, "hp_read_config: processing line <%s>\n", buf); nargs = sscanf (buf, "%s%s%s", arg1, arg2, arg3); if ((nargs <= 0) || (arg1[0] == '#')) continue; /* Option to process ? */ if ((strcmp (arg1, "option") == 0) && (nargs >= 2)) { if (strcmp (arg2, "connect-scsi") == 0) { config->connect = HP_CONNECT_SCSI; config->got_connect_type = 1; } else if (strcmp (arg2, "connect-device") == 0) { config->connect = HP_CONNECT_DEVICE; config->got_connect_type = 1; config->use_scsi_request = 0; } else if (strcmp (arg2, "connect-pio") == 0) { config->connect = HP_CONNECT_PIO; config->got_connect_type = 1; config->use_scsi_request = 0; } else if (strcmp (arg2, "connect-usb") == 0) { config->connect = HP_CONNECT_USB; config->got_connect_type = 1; config->use_scsi_request = 0; } else if (strcmp (arg2, "connect-reserve") == 0) { config->connect = HP_CONNECT_RESERVE; config->got_connect_type = 1; config->use_scsi_request = 0; } else if (strcmp (arg2, "disable-scsi-request") == 0) { config->use_scsi_request = 0; } else if (strcmp (arg2, "enable-image-buffering") == 0) { config->use_image_buffering = 1; } else if (strcmp (arg2, "dumb-read") == 0) { config->dumb_read = 1; } else { DBG(1,"hp_read_config: Invalid option %s\n", arg2); } } else /* No option. This is the start of a new device */ { if (is_df_config) /* Did we only read default configurations ? */ { is_df_config = 0; /* Stop reading default config */ /* Initialize device config with default-config */ memcpy (&dev_config, &df_config, sizeof (dev_config)); config = &dev_config; /* Start reading a device config */ } if (cu_device[0] != '\0') /* Did we work on a device ? */ { memcpy (hp_global_config_get(), &dev_config,sizeof (dev_config)); hp_attach_matching_devices (hp_global_config_get(), cu_device); cu_device[0] = '\0'; } /* Initialize new device with default config */ memcpy (&dev_config, &df_config, sizeof (dev_config)); /* Cut off leading blanks of device name */ dev_name = buf+strspn (buf, " \t\n\r"); strcpy (cu_device, dev_name); /* Save the device name */ } } if (cu_device[0] != '\0') /* Did we work on a device ? */ { memcpy (hp_global_config_get (), &dev_config, sizeof (dev_config)); DBG(1, "hp_read_config: attach %s\n", cu_device); hp_attach_matching_devices (hp_global_config_get (), cu_device); cu_device[0] = '\0'; } fclose (fp); DBG(1, "hp_read_config: reset to default config\n"); memcpy (hp_global_config_get (), &df_config, sizeof (df_config)); } else { /* default to /dev/scanner instead of insisting on config file */ char *dev_name = "/dev/scanner"; memcpy (hp_global_config_get (), &df_config, sizeof (df_config)); hp_attach_matching_devices (hp_global_config_get (), dev_name); } global.config_read++; return SANE_STATUS_GOOD; } static SANE_Status hp_update_devlist (void) { HpDeviceList devp; const SANE_Device **devlist; int count = 0; RETURN_IF_FAIL( hp_read_config() ); if (global.devlist) sanei_hp_free(global.devlist); for (devp = global.device_list; devp; devp = devp->next) count++; if (!(devlist = sanei_hp_alloc((count + 1) * sizeof(*devlist)))) return SANE_STATUS_NO_MEM; global.devlist = devlist; for (devp = global.device_list; devp; devp = devp->next) *devlist++ = sanei_hp_device_sanedevice(devp->dev); *devlist = 0; return SANE_STATUS_GOOD; } /* * */ SANE_Status sane_init (SANE_Int *version_code, SANE_Auth_Callback __sane_unused__ authorize) {SANE_Status status; DBG_INIT(); DBG(3, "sane_init called\n"); sanei_thread_init (); sanei_hp_init_openfd (); hp_destroy(); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, VERSIO); status = hp_init(); DBG(3, "sane_init will finish with %s\n", sane_strstatus (status)); return status; } void sane_exit (void) { DBG(3, "sane_exit called\n"); hp_destroy(); DBG(3, "sane_exit will finish\n"); } SANE_Status sane_get_devices (const SANE_Device ***device_list, SANE_Bool __sane_unused__ local_only) { DBG(3, "sane_get_devices called\n"); RETURN_IF_FAIL( hp_update_devlist() ); *device_list = global.devlist; DBG(3, "sane_get_devices will finish with %s\n", sane_strstatus (SANE_STATUS_GOOD)); return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle *handle) { HpDevice dev = 0; HpHandle h; DBG(3, "sane_open called\n"); RETURN_IF_FAIL( hp_read_config() ); if (devicename[0]) RETURN_IF_FAIL( hp_get_dev(devicename, &dev) ); else { /* empty devicname -> use first device */ if (global.device_list) dev = global.device_list->dev; } if (!dev) return SANE_STATUS_INVAL; if (!(h = sanei_hp_handle_new(dev))) return SANE_STATUS_NO_MEM; RETURN_IF_FAIL( hp_handle_list_add(&global.handle_list, h) ); *handle = h; DBG(3, "sane_open will finish with %s\n", sane_strstatus (SANE_STATUS_GOOD)); return SANE_STATUS_GOOD; } void sane_close (SANE_Handle handle) { HpHandle h = handle; DBG(3, "sane_close called\n"); if (!FAILED( hp_handle_list_remove(&global.handle_list, h) )) sanei_hp_handle_destroy(h); DBG(3, "sane_close will finish\n"); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int optnum) { HpHandle h = handle; const SANE_Option_Descriptor *optd; DBG(10, "sane_get_option_descriptor called\n"); optd = sanei_hp_handle_saneoption(h, optnum); DBG(10, "sane_get_option_descriptor will finish\n"); return optd; } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int optnum, SANE_Action action, void *valp, SANE_Int *info) { HpHandle h = handle; SANE_Status status; DBG(10, "sane_control_option called\n"); status = sanei_hp_handle_control(h, optnum, action, valp, info); DBG(10, "sane_control_option will finish with %s\n", sane_strstatus (status)); return status; } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters *params) { HpHandle h = handle; SANE_Status status; DBG(10, "sane_get_parameters called\n"); status = sanei_hp_handle_getParameters(h, params); DBG(10, "sane_get_parameters will finish with %s\n", sane_strstatus (status)); return status; } SANE_Status sane_start (SANE_Handle handle) { HpHandle h = handle; SANE_Status status; DBG(3, "sane_start called\n"); status = sanei_hp_handle_startScan(h); DBG(3, "sane_start will finish with %s\n", sane_strstatus (status)); return status; } SANE_Status sane_read (SANE_Handle handle, SANE_Byte *buf, SANE_Int max_len, SANE_Int *len) { HpHandle h = handle; size_t length = max_len; SANE_Status status; DBG(16, "sane_read called\n"); status = sanei_hp_handle_read(h, buf, &length); *len = length; DBG(16, "sane_read will finish with %s\n", sane_strstatus (status)); return status; } void sane_cancel (SANE_Handle handle) { HpHandle h = handle; DBG(3, "sane_cancel called\n"); sanei_hp_handle_cancel(h); DBG(3, "sane_cancel will finish\n"); } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { HpHandle h = handle; SANE_Status status; DBG(3, "sane_set_io_mode called\n"); status = sanei_hp_handle_setNonblocking(h, non_blocking); DBG(3, "sane_set_io_mode will finish with %s\n", sane_strstatus (status)); return status; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int *fd) { HpHandle h = handle; SANE_Status status; DBG(10, "sane_get_select_fd called\n"); status = sanei_hp_handle_getPipefd(h, fd); DBG(10, "sane_get_select_fd will finish with %s\n", sane_strstatus (status)); return status; } backends-1.3.0/backend/hp.conf.in000066400000000000000000000007611456256263500165610ustar00rootroot00000000000000scsi 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 backends-1.3.0/backend/hp.h000066400000000000000000000150061456256263500154540ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1997 Geoffrey T. Dairiki 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, see . 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 is part of a SANE backend for HP Scanners supporting HP Scanner Control Language (SCL). */ #ifndef HP_H_INCLUDED #define HP_H_INCLUDED #include #include #include "../include/sane/sane.h" #undef BACKEND_NAME #define BACKEND_NAME hp #define DEBUG_NOT_STATIC #include "../include/sane/sanei_debug.h" /* FIXME: these should be options? */ #undef ENABLE_7x12_TONEMAPS #define ENABLE_16x16_DITHERS #define ENABLE_10BIT_MATRIXES #define FAKE_COLORSEP_MATRIXES #undef ENABLE_CUSTOM_MATRIX #define HP_CONFIG_FILE STRINGIFY(BACKEND_NAME) ".conf" #define DEVPIX_PER_INCH 300.0 #define MM_PER_DEVPIX (MM_PER_INCH / DEVPIX_PER_INCH) /* * Macros for testing return values of functions which * return SANE_Status types. */ #define FAILED(status) (status != SANE_STATUS_GOOD) #define UNSUPPORTED(status) (status == SANE_STATUS_UNSUPPORTED) #define RETURN_IF_FAIL(try) do { \ SANE_Status status = (try); \ if (FAILED(status)) \ return status; \ } while (0) #define CATCH_RET_FAIL(try, catch) do { \ SANE_Status status = (try); \ if (FAILED(status)) { (catch); return status; } \ } while (0) #ifndef DBG_LEVEL #define DBG_LEVEL PASTE(sanei_debug_, BACKEND_NAME) #endif #ifndef NDEBUG # define DBGDUMP(level, buf, size) \ do { if (DBG_LEVEL >= (level)) sanei_hp_dbgdump(buf, size); } while (0) #else # define DBGDUMP(level, buf, size) #endif /* * */ typedef unsigned int hp_bool_t; typedef unsigned char hp_byte_t; typedef enum { HP_CONNECT_SCSI, HP_CONNECT_DEVICE, HP_CONNECT_PIO, HP_CONNECT_USB, HP_CONNECT_RESERVE } HpConnect; typedef struct { HpConnect connect; hp_bool_t got_connect_type; hp_bool_t use_scsi_request; hp_bool_t use_image_buffering; hp_bool_t dumb_read; } HpDeviceConfig; #define HP_SCL_INQID_MIN 10306 #define HP_SCL_INQID_MAX 10971 typedef struct { hp_bool_t checked, is_supported; int minval, maxval; } HpSclSupport; typedef struct { /* Flags for simulated commands */ hp_bool_t sclsimulate[HP_SCL_INQID_MAX - HP_SCL_INQID_MIN + 1]; hp_bool_t gamma_simulate; unsigned char brightness_map[256]; /* Map to simulate brightness level */ unsigned char contrast_map[256]; /* Map to simulate contrast level */ unsigned char gamma_map[256]; /* Map to simulate custom gamma table */ } HpSimulate; /* Information about a connected device */ typedef struct { char devname[64]; /* unique device name */ hp_bool_t config_is_up; /* flag if config-struct is valid */ HpDeviceConfig config; /* device configuration*/ /* List of command support */ HpSclSupport sclsupport[HP_SCL_INQID_MAX - HP_SCL_INQID_MIN + 1]; HpSimulate simulate; /* Info about simulations */ hp_bool_t unload_after_scan; int active_xpa; int max_model; } HpDeviceInfo; HpDeviceInfo *sanei_hp_device_info_get (const char *dev_name); /* hp-scl.c */ #if INT_MAX > 30000 typedef int HpScl; #else typedef long int HpScl; #endif void sanei_hp_init_openfd (void); typedef struct { int lines; int bytes_per_line; /* as received from scanner */ int bits_per_channel; hp_bool_t out8; /* This flag is set and only set, when data with */ /* depths > 8 is to be mapped to 8 bit output. */ hp_bool_t mirror_vertical; hp_bool_t invert; HpScl startscan; } HpProcessData; /* hp-option.c */ typedef SANE_Option_Descriptor * HpSaneOption; typedef const struct hp_choice_s * HpChoice; typedef struct hp_option_s * HpOption; typedef const struct hp_option_descriptor_s * HpOptionDescriptor; typedef struct hp_optset_s * HpOptSet; /* hp-accessor.c */ typedef struct hp_data_s * HpData; typedef struct hp_accessor_s * HpAccessor; typedef struct hp_accessor_vector_s * HpAccessorVector; typedef struct hp_accessor_choice_s * HpAccessorChoice; /* hp-device.c */ typedef struct hp_device_s * HpDevice; hp_bool_t sanei_hp_device_simulate_get (const char *devname, HpScl scl); HpDevice sanei_hp_device_get (const char *dev_name); /* hp-handle.c */ typedef struct hp_handle_s * HpHandle; /* hp-scsi.c */ typedef struct hp_scsi_s * HpScsi; /* hp-scl.c */ hp_bool_t sanei_hp_is_active_xpa (HpScsi scsi); int sanei_hp_get_max_model (HpScsi scsi); int sanei_hp_is_flatbed_adf (HpScsi scsi); HpConnect sanei_hp_get_connect (const char *devname); HpConnect sanei_hp_scsi_get_connect (HpScsi this); /* hp.c */ #ifndef NDEBUG void sanei_hp_dbgdump (const void * bufp, size_t len); #endif /* hp-hpmem.c */ void * sanei_hp_alloc(size_t sz); void * sanei_hp_allocz(size_t sz); void * sanei_hp_memdup(const void * src, size_t sz); char * sanei_hp_strdup(const char * str); void * sanei_hp_realloc(void * ptr, size_t sz); void sanei_hp_free(void * ptr); void sanei_hp_free_all(void); #endif /* HP_H_INCLUDED */ backends-1.3.0/backend/hp3500.c000066400000000000000000003114121456256263500157570ustar00rootroot00000000000000/* 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, see . 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 "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_LIBC_H # include /* NeXTStep/OpenStep */ #endif #include "../include/sane/sane.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_thread.h" #include "../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 DEBUG 1 #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, OPT_MODE_GROUP, OPT_MODE, OPT_BRIGHTNESS, OPT_CONTRAST, OPT_GAMMA, 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; SANE_Pid reader_pid; int resolution; int mode; 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; int brightness; int contrast; double gamma; SANE_Option_Descriptor opt[NUM_OPTIONS]; SANE_Device sane; }; struct hp3500_write_info { struct hp3500_data *scanner; int bytesleft; }; typedef struct detailed_calibration_data { unsigned char const *channeldata[3]; unsigned resolution_divisor; } detailed_calibration_data; 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[] = { 9, 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 const SANE_Range range_brightness = { 0, 255, 0 }; static const SANE_Range range_contrast = { 0, 255, 0 }; static const SANE_Range range_gamma = { SANE_FIX (0.2), SANE_FIX(4.0), SANE_FIX(0.01) }; #define HP3500_COLOR_SCAN 0 #define HP3500_GRAY_SCAN 1 #define HP3500_LINEART_SCAN 2 #define HP3500_TOTAL_SCANS 3 static char const *scan_mode_list[HP3500_TOTAL_SCANS + 1] = { 0 }; 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); static size_t max_string_size(char const **); /* * 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) { (void) 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 (SANE_CURRENT_MAJOR, SANE_CURRENT_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 defaults 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 = 200; 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); scanner->mode = 0; scanner->brightness = 128; scanner->contrast = 64; scanner->gamma = 2.2; 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; int i; /* 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; case OPT_MODE: strcpy ((SANE_Char *) val, scan_mode_list[scanner->mode]); return SANE_STATUS_GOOD; case OPT_CONTRAST: *(SANE_Word *) val = scanner->contrast; return SANE_STATUS_GOOD; case OPT_GAMMA: *(SANE_Word *) val = SANE_FIX(scanner->gamma); return SANE_STATUS_GOOD; case OPT_BRIGHTNESS: *(SANE_Word *) val = scanner->brightness; 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; case OPT_MODE: for (i = 0; scan_mode_list[i]; ++i) { if (!strcmp ((SANE_Char const *) val, scan_mode_list[i])) { DBG (10, "Setting scan mode to %s (request: %s)\n", scan_mode_list[i], (SANE_Char const *) val); scanner->mode = i; return SANE_STATUS_GOOD; } } /* Impossible */ return SANE_STATUS_INVAL; case OPT_BRIGHTNESS: scanner->brightness = *(SANE_Word *) val; return SANE_STATUS_GOOD; case OPT_CONTRAST: scanner->contrast = *(SANE_Word *) val; return SANE_STATUS_GOOD; case OPT_GAMMA: scanner->gamma = SANE_UNFIX(*(SANE_Word *) val); 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 (!sanei_thread_is_valid (scanner->reader_pid)) { 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 = (scanner->mode == HP3500_COLOR_SCAN) ? SANE_FRAME_RGB : SANE_FRAME_GRAY; params->depth = (scanner->mode == HP3500_LINEART_SCAN) ? 1 : 8; 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 operation 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 = (SANE_Pid) -1; 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->type = SANE_TYPE_INT; 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 = SANE_I18N ("Geometry"); opt->desc = SANE_I18N ("Geometry Group"); 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; if (!scan_mode_list[0]) { scan_mode_list[HP3500_COLOR_SCAN] = SANE_VALUE_SCAN_MODE_COLOR; scan_mode_list[HP3500_GRAY_SCAN] = SANE_VALUE_SCAN_MODE_GRAY; scan_mode_list[HP3500_LINEART_SCAN] = SANE_VALUE_SCAN_MODE_LINEART; scan_mode_list[HP3500_TOTAL_SCANS] = 0; } opt = scanner->opt + OPT_MODE_GROUP; opt->title = SANE_I18N ("Scan Mode Group"); opt->desc = SANE_I18N ("Scan Mode Group"); opt->type = SANE_TYPE_GROUP; opt->constraint_type = SANE_CONSTRAINT_NONE; 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; opt->size = max_string_size(scan_mode_list); opt->constraint_type = SANE_CONSTRAINT_STRING_LIST; opt->constraint.string_list = (SANE_String_Const *) scan_mode_list; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; 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->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &range_brightness; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; 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->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &range_contrast; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; opt = scanner->opt + OPT_GAMMA; opt->name = SANE_NAME_ANALOG_GAMMA; opt->title = SANE_TITLE_ANALOG_GAMMA; opt->desc = SANE_DESC_ANALOG_GAMMA; opt->type = SANE_TYPE_FIXED; opt->unit = SANE_UNIT_NONE; opt->constraint_type = SANE_CONSTRAINT_RANGE; opt->constraint.range = &range_gamma; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; return SANE_STATUS_GOOD; } static void do_reset (struct hp3500_data *scanner) { (void) scanner; /* kill warning */ } static void do_cancel (struct hp3500_data *scanner) { if (sanei_thread_is_valid (scanner->reader_pid)) { if (sanei_thread_kill (scanner->reader_pid) == 0) { int exit_status; sanei_thread_waitpid (scanner->reader_pid, &exit_status); } sanei_thread_invalidate (scanner->reader_pid); } 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; if (scanner->mode == HP3500_LINEART_SCAN) scanner->bytes_per_scan_line = (scanner->scan_width_pixels + 7) / 8; else if (scanner->mode == HP3500_GRAY_SCAN) scanner->bytes_per_scan_line = scanner->scan_width_pixels; else 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 const *data_, int readbytes, void *readdata) { int len = 4 + bytes; unsigned char *buffer; unsigned char const *data = data_; /* We add "bytes" here to account for the possibility that all of the * data bytes are 0xaa and hence require a following 0x00 byte. */ if (command_bytes_outstanding + len + bytes > 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++ = command; *buffer++ = reg; *buffer++ = count >> 8; *buffer++ = count; while (bytes--) { *buffer++ = *data; if (*data++ == 0xaa) { *buffer++ = 0; ++len; } } command_bytes_outstanding += len; 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_) { unsigned char *data = (unsigned char *) data_; /* The number of bytes passed in could be much larger than we can transmit * (0xffc0) bytes. With 0xaa escapes it could be even larger. Accordingly * we need to count the 0xaa escapes and write in chunks if the number of * bytes would otherwise exceed a limit (I have used 0xf000 as the limit). */ while (bytes > 0) { int now = 0; int bufsize = 0; while (now < bytes && bufsize < 0xf000) { int i; /* Try to avoid writing part pages */ for (i = 0; i < 32 && now < bytes; ++i) { ++bufsize; if (data[now++] == 0xaa) ++bufsize; } } if (rt_send_command_immediate (RTCMD_WRITESRAM, 0, now, now, data, 0, 0) < 0) return -1; bytes -= now; data += now; } return 0; } static int rt_read_sram (int bytes, void *data_) { unsigned char *data = (unsigned char *) data_; while (bytes > 0) { int now = (bytes > 0xf000) ? 0xf000 : bytes; if (rt_send_command_immediate (RTCMD_READSRAM, 0, bytes, 0, 0, bytes, data) < 0) return -1; bytes -= now; data += now; } return 0; } 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) { 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; 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 r3ab[2]; char r10; char r58; if (rt_read_register_immediate (0x3a, 1, r3ab) < 0 || rt_read_register_immediate (0x10, 1, &r10) < 0 || rt_read_register_immediate (0x58, 1, &r58) < 0) return -1; r3ab[0] |= 0x80; r3ab[1] = 0x40; r10 |= 0x01; r58 &= 0x0f; if (rt_set_register_immediate (0x3a, 2, r3ab) < 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[0x02] = redoffset1; regs[0x05] = redoffset2; regs[0x08] = redgain; regs[0x03] = greenoffset1; regs[0x06] = greenoffset2; regs[0x09] = greengain; regs[0x04] = blueoffset1; regs[0x07] = blueoffset2; regs[0x0a] = bluegain; return 0; } static int rt_set_calibration_addresses (unsigned char *regs, unsigned redaddr, unsigned greenaddr, unsigned blueaddr, unsigned endaddr, unsigned width) { unsigned endpage = (endaddr + 31) / 32; unsigned scanline_pages = ((width + 1) * 3 + 31) / 32; /* Red, green and blue detailed calibration addresses */ regs[0x84] = redaddr; regs[0x8e] = (regs[0x8e] & 0x0f) | ((redaddr >> 4) & 0xf0); rt_set_value_lsbfirst (regs, 0x85, 2, greenaddr); rt_set_value_lsbfirst (regs, 0x87, 2, blueaddr); /* I don't know what the next three are used for, but each buffer commencing * at 0x80 and 0x82 needs to hold a full scan line. */ rt_set_value_lsbfirst (regs, 0x80, 2, endpage); rt_set_value_lsbfirst (regs, 0x82, 2, endpage + scanline_pages); rt_set_value_lsbfirst (regs, 0x89, 2, endpage + scanline_pages * 2); /* I don't know what this is, but it seems to be a number of pages that can hold * 16 complete scan lines, but not calculated as an offset from any other page */ rt_set_value_lsbfirst (regs, 0x51, 2, (48 * (width + 1) + 31) / 32); /* I don't know what this is either, but this is what the Windows driver does */ rt_set_value_lsbfirst (regs, 0x8f, 2, 0x1c00); 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_set_data_feed_off (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) { /* RGBRGB instead of RRRRR...GGGGG...BBBB */ 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) { int base_resolution = 300; if (regs[0x2d] & 0x20) base_resolution *= 2; if (regs[0xd3] & 0x08) base_resolution *= 2; regs[0x7a] = base_resolution / 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; } /* This is what we want as the initial registers, not what they * are at power on time. In particular 13 bytes at 0x10 are * different, and the byte at 0x94 is different. */ static unsigned char initial_regs[] = { /* 0x00 */ 0xf5, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08 */ 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, /* 0x10 */ 0x81, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, /* 0x18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, /* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x19, /* 0x30 */ 0xd0, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38 */ 0x00, 0x00, 0xa0, 0x37, 0xff, 0x0f, 0x00, 0x00, /* 0x40 */ 0x80, 0x00, 0x00, 0x00, 0x8c, 0x76, 0x00, 0x00, /* 0x48 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ 0x20, 0xbc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58 */ 0x1d, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ 0x5e, 0xea, 0x5f, 0xea, 0x00, 0x80, 0x64, 0x00, /* 0x68 */ 0x00, 0x00, 0x00, 0x00, 0x84, 0x04, 0x00, 0x00, /* 0x70 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78 */ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80 */ 0x0f, 0x02, 0x4b, 0x02, 0x00, 0xec, 0x19, 0xd8, /* 0x88 */ 0x2d, 0x87, 0x02, 0xff, 0x3f, 0x78, 0x60, 0x00, /* 0x90 */ 0x1c, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, /* 0x98 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0 */ 0x00, 0x00, 0x00, 0x0c, 0x27, 0x64, 0x00, 0x00, /* 0xa8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0 */ 0x12, 0x08, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00, /* 0xb8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, /* 0xc8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0 */ 0xff, 0xbf, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, /* 0xd8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0 */ 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; #define RT_NORMAL_TG 0 #define RT_DOUBLE_TG 1 #define RT_TRIPLE_TG 2 #define RT_DDOUBLE_TG 3 #define RT_300_TG 4 #define RT_150_TG 5 #define RT_TEST_TG 6 static struct tg_info__ { int tg_cph0p; int tg_crsp; int tg_cclpp; int tg_cph0s; int tg_cdss1; int tg_cdsc1; int tg_cdss2; int tg_cdsc2; } tg_info[] = { /* CPH CCD Shifting Clock * 0P ??? Perhaps CCD rising edge position * 0S ??? * CRS Reset CCD Clock * P ??? Perhaps CCD falling edge position * CCLP CCD Clamp Clock * P ??? * CDS ??? * S1 ??? * S2 ??? * C1 ??? * C2 ??? */ /*CPH0P CRSP CCLPP CPH0S CDSS1 CDSC1 CDSS2 CDSC2 */ { 0x01FFE0, 0x3c0000, 0x003000, 1, 0xb, 0xd, 0x00, 0x01}, /* NORMAL */ { 0x7ff800, 0xf00000, 0x01c000, 0, 0xb, 0xc, 0x14, 0x15}, /* DOUBLE */ { 0x033fcc, 0x300000, 0x060000, 1, 0x8, 0xa, 0x00, 0x01}, /* TRIPLE */ { 0x028028, 0x300000, 0x060000, 1, 0x8, 0xa, 0x00, 0x01}, /* DDOUBLE */ { 0x7ff800, 0x030000, 0x060000, 0, 0xa, 0xc, 0x17, 0x01}, /* 300 */ { 0x7fc700, 0x030000, 0x060000, 0, 0x7, 0x9, 0x17, 0x01}, /* 150 */ { 0x7ff800, 0x300000, 0x060000, 0, 0xa, 0xc, 0x17, 0x01}, /* TEST */ }; 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 d3_bit_3_value; int tg; int step_size; }; /* The TG value sets seem to affect the exposure time: * At 200dpi: * NORMAL gets higher values than DOUBLE * DDOUBLE gives a crazy spike in the data * TRIPLE gives a black result * TEST gives a black result * 300 gives a black result * 150 gives a black result */ static struct resolution_parameters resparms[] = { /* Acceptable values for stepsz are: * 0x157b 0xabd, 0x55e, 0x2af, 0x157, 0xab, 0x55 */ /* My values - all work */ /*res r39 rC3 rC6 freq cph0s rgo gbo intra mmcm d3 tg stepsz */ {1200, 3, 6, 4, 2, 1, 22, 22, 4, 2, 1, RT_NORMAL_TG, 0x157b}, {600, 15, 6, 4, 1, 1, 9, 10, 0, 2, 1, RT_NORMAL_TG, 0x055e}, {400, 3, 1, 4, 1, 1, 6, 6, 1, 2, 1, RT_NORMAL_TG, 0x157b}, {300, 15, 3, 4, 1, 1, 5, 4, 0, 2, 1, RT_NORMAL_TG, 0x02af}, {200, 7, 1, 4, 1, 1, 3, 3, 0, 2, 1, RT_NORMAL_TG, 0x055e}, {150, 15, 3, 1, 1, 1, 2, 2, 0, 2, 1, RT_NORMAL_TG, 0x02af}, {100, 3, 1, 3, 1, 1, 1, 1, 0, 2, 1, RT_NORMAL_TG, 0x0abd}, {75, 15, 3, 3, 1, 1, 1, 1, 0, 2, 1, RT_NORMAL_TG, 0x02af}, {50, 15, 1, 1, 1, 1, 0, 0, 0, 2, 1, RT_NORMAL_TG, 0x055e}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; struct dcalibdata { unsigned char *buffers[3]; int pixelsperrow; int pixelnow; int channelnow; int firstrowdone; }; static void dump_registers (unsigned char const *); static int rts8801_rewind (void) { unsigned char regs[255]; int n; int tg_setting = RT_DOUBLE_TG; 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_direction_rewind (regs); rt_set_step_size (regs, 0x55); regs[0x39] = 3; regs[0xc3] = (regs[0xc3] & 0xf8) | 0x86; regs[0xc6] = (regs[0xc6] & 0xf8) | 4; rt_set_horizontal_resolution (regs, 25); 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_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_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 (DBG_LEVEL >= 5) dump_registers (regs); rt_set_all_registers (regs); rt_set_one_register (0x2c, regs[0x2c]); 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 unsigned get_lsbfirst_int (unsigned char const *p, int n) { unsigned value = *p++; int shift = 8; while (--n) { unsigned now = *p++; value |= now << shift; shift += 8; } return value; } static int convert_c6 (int i) { switch (i) { case 3: return 1; case 1: return 2; case 4: return 4; } return -1; } static void dump_registers (unsigned char const *regs) { int i = 0; long pixels; DBG (5, "Scan commencing with registers:\n"); while (i < 255) { int j = 0; char buffer[80]; buffer[0] = 0; sprintf (buffer + strlen (buffer), "%02x:", i); while (j < 8) { sprintf (buffer + strlen (buffer), " %02x", regs[i++]); j++; } sprintf (buffer + strlen (buffer), " -"); while (j++ < 16 && i < 255) sprintf (buffer + strlen (buffer), " %02x", regs[i++]); DBG (5, " %s\n", buffer); } DBG (5, " Position:\n"); DBG (5, " Distance without scanning: %u\n", get_lsbfirst_int (regs + 0x60, 2)); DBG (5, " Total distance: %u\n", get_lsbfirst_int (regs + 0x62, 2)); DBG (5, " Scanning distance: %u\n", get_lsbfirst_int (regs + 0x62, 2) - get_lsbfirst_int (regs + 0x60, 2)); DBG (5, " Direction: %s\n", (regs[0xc6] & 0x08) ? "forward" : "rewind"); DBG (5, " Motor: %s\n", (regs[0xc3] & 0x80) ? "enabled" : "disabled"); if (regs[0x7a]) DBG (5, " X range: %u-%u\n", get_lsbfirst_int (regs + 0x66, 2) / regs[0x7a], get_lsbfirst_int (regs + 0x6c, 2) / regs[0x7a]); DBG (5, " TG Info:\n"); DBG (5, " CPH0P: %06x\n", get_lsbfirst_int (regs + 0xf0, 3)); DBG (5, " CRSP: %06x\n", get_lsbfirst_int (regs + 0xf9, 3)); DBG (5, " CCLPP: %06x\n", get_lsbfirst_int (regs + 0xfc, 3)); DBG (5, " CPH0S: %d\n", (regs[0x2d] & 0x20) ? 1 : 0); DBG (5, " CDSS1: %02x\n", regs[0x28] & 0x1f); DBG (5, " CDSC1: %02x\n", regs[0x29] & 0x1f); DBG (5, " CDSS2: %02x\n", regs[0x2a] & 0x1f); DBG (5, " CDSC2: %02x\n", regs[0x2b] & 0x1f); DBG (5, " Resolution specific:\n"); if (!regs[0x7a]) DBG (5, " Horizontal resolution: Denominator is zero!\n"); else DBG (5, " Horizontal resolution: %u\n", 300 * ((regs[0x2d] & 0x20) ? 2 : 1) * ((regs[0xd3] & 0x08) ? 2 : 1) / regs[0x7a]); DBG (5, " Derived vertical resolution: %u\n", 400 * (regs[0xc3] & 0x1f) * convert_c6 (regs[0xc6] & 0x7) / (regs[0x39] + 1)); DBG (5, " Register D3:3 %u\n", (regs[0xd3] & 0x08) ? 1 : 0); DBG (5, " Register 39: %u\n", regs[0x39]); DBG (5, " Register C3:0-5: %u\n", regs[0xc3] & 0x1f); DBG (5, " Register C6:0-2: %u\n", regs[0xc6] & 0x7); DBG (5, " Motor movement clock multiplier: %u\n", regs[0x40] >> 6); DBG (5, " Step Size: %04x\n", get_lsbfirst_int (regs + 0xe2, 2)); DBG (5, " Frequency: %u\n", regs[0x64] & 0xf); DBG (5, " Colour registers\n"); DBG (5, " Register 2F: %02x\n", regs[0x2f]); DBG (5, " Register 2C: %02x\n", regs[0x2c]); if (regs[0x7a]) { DBG (5, " Scan data estimates:\n"); pixels = (long) (get_lsbfirst_int (regs + 0x62, 2) - get_lsbfirst_int (regs + 0x60, 2)) * (long) (get_lsbfirst_int (regs + 0x6c, 2) - get_lsbfirst_int (regs + 0x66, 2)) / regs[0x7a]; DBG (5, " Pixels: %ld\n", pixels); DBG (5, " Bytes at 24BPP: %ld\n", pixels * 3); DBG (5, " Bytes at 1BPP: %ld\n", pixels / 8); } DBG (5, "\n"); } 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; } #if 0 static void sram_dump_byte(FILE *fp, unsigned char const *left, unsigned leftstart, unsigned leftlimit, unsigned char const *right, unsigned rightstart, unsigned rightlimit, unsigned idx) { unsigned ridx = rightstart + idx; unsigned lidx = leftstart + idx; putc(' ', fp); if (rightstart < rightlimit && leftstart < leftlimit && left[lidx] != right[ridx]) fputs("", fp); if (leftstart < leftlimit) fprintf(fp, "%02x", left[lidx]); else fputs(" ", fp); if (rightstart < rightlimit && leftstart < leftlimit && left[lidx] != right[ridx]) fputs("", fp); } static void dump_sram_to_file(char const *fname, unsigned char const *expected, unsigned end_calibration_offset) { FILE *fp = fopen(fname, "w"); rt_set_sram_page(0); if (fp) { unsigned char buf[1024]; unsigned loc = 0; fprintf(fp, "
\n");
      while (loc < end_calibration_offset)
        {
          unsigned byte = 0;

          rt_read_sram(1024, buf);

          while (byte < 1024)
            {
              unsigned idx = 0;

              fprintf(fp, "%06x:", loc);
              do
                {
		  sram_dump_byte(fp, buf, byte, 1024, expected, loc, end_calibration_offset, idx);
                } while (++idx & 0x7);
              fprintf(fp, " -");
              do
                {
		  sram_dump_byte(fp, buf, byte, 1024, expected, loc, end_calibration_offset, idx);
                } while (++idx & 0x7);

              idx = 0;
              fputs("     ", fp);

              do
                {
                  sram_dump_byte(fp, expected, loc, end_calibration_offset, buf, byte, 1024, idx);
                } while (++idx & 0x7);
              fprintf(fp, " -");
              do
                {
                  sram_dump_byte(fp, expected, loc, end_calibration_offset, buf, byte, 1024, idx);
                } while (++idx & 0x7);


              fputs("\n", fp);
              byte += 16;
              loc += 16;
            }
        }
      fprintf(fp, "
"); fclose(fp); } } #endif static int rts8801_doscan (unsigned width, unsigned height, 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, int merged_channels, double *postprocess_offsets, double *postprocess_gains) { unsigned rowbytes = 0; unsigned output_rowbytes = 0; unsigned channels = 0; unsigned total_rows = 0; unsigned char *row_buffer; unsigned char *output_buffer; unsigned buffered_rows; int rows_to_begin; int rowbuffer_bytes; int n; unsigned rownow = 0; unsigned bytenow = 0; unsigned char *channel_data[3][2]; unsigned i; unsigned j; int result = 0; unsigned rows_supplied = 0; (void) calib_info; /* Kill warning */ if (cancelled_scan) return -1; rt_start_moving (); channels = 3; rowbytes = width * 3; switch (colour) { case HP3500_GRAY_SCAN: output_rowbytes = width; break; case HP3500_COLOR_SCAN: output_rowbytes = rowbytes; break; case HP3500_LINEART_SCAN: output_rowbytes = (width + 7) / 8; 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 = (unsigned char *) malloc (rowbuffer_bytes); output_buffer = (unsigned 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; if (merged_channels) channel_data[i][1 - oddfirst] = row_buffer + rowbytes * j + i; else 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) { unsigned char buffer[0xffc0]; if (n > 0xffc0) n = 0xffc0; else if ((n > 1) && (n & 1)) --n; if (rt_get_data (n, buffer) >= 0) { unsigned 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) { unsigned char *outnow = output_buffer; unsigned x; for (i = x = 0; x < width; ++x, i += merged_channels ? channels : 1) { for (j = 0; j < channels; ++j) { unsigned pix = (unsigned char) channel_data[j][i & 1][i]; if (postprocess_gains && postprocess_offsets) { int ppidx = j * width + x; pix = constrain ( pix * postprocess_gains[ppidx] - postprocess_offsets[ppidx], 0, 255); } *outnow++ = pix; } } if (colour == HP3500_GRAY_SCAN || colour == HP3500_LINEART_SCAN) { unsigned char const *in_now = output_buffer; int bit = 7; outnow = output_buffer; for (i = 0; i < width; ++i) { if (colour == HP3500_GRAY_SCAN) { *outnow++ = ((unsigned) in_now[0] * 2989 + (unsigned) in_now[1] * 5870 + (unsigned) in_now[2] * 1140) / 10000; } else { if (bit == 7) *outnow = ((in_now[1] < 0x80) ? 0x80 : 0); else if (in_now[1] < 0x80) *outnow |= (1 << bit); if (bit == 0) { ++outnow; bit = 7; } else { --bit; } } in_now += 3; } } if (rows_supplied++ < height && !((*cbfunc) (params, output_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 (30, "total_rows = %d\r", 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 #define RTS8801_F_LAMP_OFF 2 #define RTS8801_F_NO_DISPLACEMENTS 4 #define RTS8801_F_ODDX 8 static int find_resolution_index (unsigned resolution) { int res = 0; for (res = 0; resparms[res].resolution != resolution; ++res) { if (!resparms[res].resolution) return -1; } return res; } 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, int red_calib_offset, int green_calib_offset, int blue_calib_offset, int end_calib_offset, double *postprocess_offsets, double *postprocess_gains) { int ires, jres; int tg_setting; unsigned char regs[256]; unsigned char offdutytime; int result; int scan_frequency; unsigned intra_channel_offset; unsigned red_green_offset; unsigned green_blue_offset; unsigned total_offsets; ires = find_resolution_index (xresolution); jres = find_resolution_index (yresolution); if (ires < 0 || jres < 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); rt_set_direction_forwards (regs); rt_set_stop_when_rewound (regs, 0); rt_set_calibration_addresses (regs, 0, 0, 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 */ regs[0x40] &= 0xc0; if (red_calib_offset >= 0 && green_calib_offset >= 0 && blue_calib_offset >= 0) { rt_set_calibration_addresses (regs, red_calib_offset, green_calib_offset, blue_calib_offset, end_calib_offset, w); regs[0x40] |= 0x2f; } else if (end_calib_offset >= 0) { rt_set_calibration_addresses (regs, 0x600, 0x600, 0x600, end_calib_offset, w); } rt_set_channel (regs, RT_CHANNEL_ALL); rt_set_single_channel_scanning (regs, 0); rt_set_merge_channels (regs, 1); rt_set_colour_mode (regs, 1); rt_set_last_sram_page (regs, (local_sram_size - 1) >> 5); scan_frequency = resparms[jres].scan_frequency; rt_set_cph0s (regs, resparms[ires].cph0s); if (resparms[ires].d3_bit_3_value) regs[0xd3] |= 0x08; else regs[0xd3] &= 0xf7; if (flags & RTS8801_F_SUPPRESS_MOVEMENT) regs[0xc3] &= 0x7f; regs[0xb2] &= 0xf7; rt_set_horizontal_resolution (regs, xresolution); rt_set_scanline_start (regs, x * (1200 / xresolution) / (resparms[ires].cph0s ? 1 : 2) / (resparms[ires].d3_bit_3_value ? 1 : 2)); rt_set_scanline_end (regs, (x + w) * (1200 / xresolution) / (resparms[ires].cph0s ? 1 : 2) / (resparms[ires].d3_bit_3_value ? 1 : 2)); if (flags & RTS8801_F_NO_DISPLACEMENTS) { red_green_offset = green_blue_offset = intra_channel_offset = 0; } else { red_green_offset = resparms[jres].red_green_offset; green_blue_offset = resparms[jres].green_blue_offset; intra_channel_offset = resparms[jres].intra_channel_offset; } total_offsets = red_green_offset + green_blue_offset + intra_channel_offset; if (y > total_offsets + 2) y -= total_offsets; h += total_offsets; if (yresolution > 75 && !(flags & RTS8801_F_SUPPRESS_MOVEMENT)) { int rmres = find_resolution_index (50); if (rmres >= 0) { int factor = yresolution / 50; int fastres = y / factor; int remainder = y % factor; while (remainder < 2) { --fastres; remainder += factor; } if (fastres >= 3) { y = remainder; rt_set_noscan_distance(regs, fastres * resparms[rmres].scan_frequency - 2); rt_set_total_distance(regs, fastres * resparms[rmres].scan_frequency - 1); rt_set_scan_frequency(regs, 1); tg_setting = resparms[rmres].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[rmres].step_size); rt_set_motor_movement_clock_multiplier (regs, resparms[rmres]. 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); regs[0x39] = resparms[rmres].reg_39_value; regs[0xc3] = (regs[0xc3] & 0xf8) | resparms[rmres].reg_c3_value; regs[0xc6] = (regs[0xc6] & 0xf8) | resparms[rmres].reg_c6_value; rt_set_data_feed_off (regs); rt_set_all_registers (regs); rt_set_one_register (0x2c, regs[0x2c]); if (DBG_LEVEL >= 5) dump_registers (regs); rt_start_moving (); while (rt_is_moving ()); } } } rt_set_noscan_distance (regs, y * scan_frequency - 1); rt_set_total_distance (regs, scan_frequency * (y + h) - 1); rt_set_scan_frequency (regs, scan_frequency); 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_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); 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_data_feed_on (regs); rt_set_all_registers (regs); rt_set_one_register (0x2c, regs[0x2c]); if (DBG_LEVEL >= 5) dump_registers (regs); result = rts8801_doscan (w, h, colour, red_green_offset, green_blue_offset, intra_channel_offset, cbfunc, param, (x & 1), calib_info, (regs[0x2f] & 0x04) != 0, postprocess_offsets, postprocess_gains); return result; } static int accumfunc (struct dcalibdata *dcd, int bytes, char *data) { unsigned char *c = (unsigned char *) data; while (bytes > 0) { if (dcd->firstrowdone) dcd->buffers[dcd->channelnow][dcd->pixelnow - dcd->pixelsperrow] = *c; if (++dcd->channelnow >= 3) { dcd->channelnow = 0; if (++dcd->pixelnow == dcd->pixelsperrow) ++dcd->firstrowdone; } c++; bytes--; } return 1; } static int calcmedian (unsigned char const *data, int pixel, int pixels_per_row, int elements) { int tallies[256]; int i; int elemstogo = elements / 2; memset (tallies, 0, sizeof (tallies)); data += pixel; for (i = 0; i < elements; ++i) { ++tallies[*data]; data += pixels_per_row; } i = 0; while (elemstogo - tallies[i] > 0) elemstogo -= tallies[i++]; return i; } 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; } 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 do_warmup = 1; #define DETAILED_PASS_COUNT 3 #define DETAILED_PASS_OFFSETS 0 #define DETAILED_PASS_GAINS_FIRSTPASS 1 #define DETAILED_PASS_GAINS_SECONDPASS 2 static int rts8801_scan (unsigned x, unsigned y, unsigned w, unsigned h, unsigned resolution, unsigned colour, unsigned brightness, unsigned contrast, rts8801_callback cbfunc, void *param, double gamma) { unsigned char calib_info[9]; unsigned char calibbuf[2400]; struct dcalibdata dcd; struct calibdata cd; unsigned char *detail_buffer = 0; int iCalibY; int iCalibTarget; int iMoveFlags = 0; unsigned aiBestOffset[6]; int aiPassed[6]; int i; unsigned j; int k; int calibration_size; unsigned char *pDetailedCalib; int red_calibration_offset; int green_calibration_offset; int blue_calibration_offset; int end_calibration_offset; int base_resolution; int resolution_divisor; int resolution_index; int detailed_calibration_rows = 50; unsigned char *tdetail_buffer; int pass; int onechanged; double *postprocess_gains; double *postprocess_offsets; int needs_postprocessed_calibration = 0; double contrast_adjust = (double) contrast / 64; int brightness_adjust = brightness - 0x80; /* 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 (25); /* Basic calibration */ DBG (10, "Calibrating (stage 1)\n"); calib_info[2] = calib_info[5] = calib_info[8] = 1; iCalibY = (resolution == 25) ? 1 : 2; iCalibTarget = 550; rt_turn_off_lamp(); for (i = 0; i < 6; ++i) { aiBestOffset[i] = 0xbf; aiPassed[i] = 0; } do { DBG (30, "Initial calibration pass commences\n"); onechanged = 0; for (i = 0; i < 3; ++i) { calib_info[i * 3] = aiBestOffset[i]; calib_info[i * 3 + 1] = aiBestOffset[i + 3]; } cd.buffer = calibbuf; cd.space = sizeof (calibbuf); DBG (30, "Commencing scan for initial calibration pass\n"); rts8801_fullscan (1401, iCalibY, 100, 2, 400, resolution, HP3500_COLOR_SCAN, (rts8801_callback) storefunc, &cd, calib_info, iMoveFlags, -1, -1, -1, -1, 0, 0); DBG (30, "Completed scan for initial calibration pass\n"); iMoveFlags = RTS8801_F_SUPPRESS_MOVEMENT | RTS8801_F_NO_DISPLACEMENTS; iCalibY = 2; for (i = 0; i < 6; ++i) { int sum; if (aiBestOffset[i] >= 255 || aiPassed[i] > 2) continue; sum = sum_channel (calibbuf + i, 50, 1); DBG (20, "channel[%d] sum = %d (target %d)\n", i, sum, iCalibTarget); if (sum < iCalibTarget) { onechanged = 1; ++aiBestOffset[i]; } else { ++aiPassed[i]; } } DBG (30, "Initial calibration pass completed\n"); } while (onechanged); DBG (20, "Offsets calculated\n"); rt_turn_on_lamp(); usleep(500000); tdetail_buffer = (unsigned char *) malloc (w * 3 * detailed_calibration_rows); for (i = 0; i < 3; ++i) { calib_info[i * 3 + 2] = 1; aiPassed[i] = 0; } do { struct dcalibdata dcdt; dcdt.buffers[0] = tdetail_buffer; dcdt.buffers[1] = (tdetail_buffer + w * detailed_calibration_rows); dcdt.buffers[2] = (dcdt.buffers[1] + w * detailed_calibration_rows); dcdt.pixelsperrow = w; dcdt.pixelnow = dcdt.channelnow = dcdt.firstrowdone = 0; DBG (20, "Scanning for part 2 of initial calibration\n"); rts8801_fullscan (x, 4, w, detailed_calibration_rows + 1, resolution, resolution, HP3500_COLOR_SCAN, (rts8801_callback) accumfunc, &dcdt, calib_info, RTS8801_F_SUPPRESS_MOVEMENT | RTS8801_F_NO_DISPLACEMENTS, -1, -1, -1, -1, 0, 0); DBG (20, "Scan for part 2 of initial calibration completed\n"); onechanged = 0; for (i = 0; i < 3; ++i) { int largest = 1; if (aiPassed[i] > 2 || calib_info[i * 3 + 2] >= 63) continue; for (j = 0; j < w; ++j) { int val = calcmedian (dcdt.buffers[i], j, w, detailed_calibration_rows); if (val > largest) largest = val; } if (largest < 0xe0) { ++calib_info[i * 3 + 2]; onechanged = 1; } else { ++aiPassed[i]; } } } while (onechanged); 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]); } DBG (20, "Gain factors calculated\n"); /* Stage 2 calibration */ DBG (10, "Calibrating (stage 2)\n"); detail_buffer = (unsigned char *) malloc (w * 3 * detailed_calibration_rows); dcd.buffers[0] = detail_buffer; dcd.buffers[1] = (detail_buffer + w * detailed_calibration_rows); dcd.buffers[2] = (dcd.buffers[1] + w * detailed_calibration_rows); dcd.pixelsperrow = w; /* And now for the detailed calibration */ resolution_index = find_resolution_index (resolution); base_resolution = 300; if (resparms[resolution_index].cph0s) base_resolution *= 2; if (resparms[resolution_index].d3_bit_3_value) base_resolution *= 2; resolution_divisor = base_resolution / resolution; calibration_size = w * resolution_divisor * 6 + 1568 + 96; red_calibration_offset = 0x600; green_calibration_offset = red_calibration_offset + w * resolution_divisor * 2; blue_calibration_offset = green_calibration_offset + w * resolution_divisor * 2; end_calibration_offset = blue_calibration_offset + w * resolution_divisor * 2; pDetailedCalib = (unsigned char *) malloc (calibration_size); memset (pDetailedCalib, 0, calibration_size); for (i = 0; i < 3; ++i) { int idx = (i == 0) ? red_calibration_offset : (i == 1) ? green_calibration_offset : blue_calibration_offset; for (j = 0; j < 256; j++) { /* Gamma table - appears to be 256 byte pairs for each input * range (so the first entry cover inputs in the range 0 to 1, * the second 1 to 2, and so on), mapping that input range * (including the fractional parts within it) to an output * range. */ pDetailedCalib[i * 512 + j * 2] = j; pDetailedCalib[i * 512 + j * 2 + 1] = j; } for (j = 0; j < w; ++j) { for (k = 0; k < resolution_divisor; ++k) { pDetailedCalib[idx++] = 0; pDetailedCalib[idx++] = 0x80; } } } rt_set_sram_page (0); rt_set_one_register (0x93, r93setting); rt_write_sram (calibration_size, pDetailedCalib); postprocess_gains = (double *) malloc(sizeof(double) * 3 * w); postprocess_offsets = (double *) malloc(sizeof(double) * 3 * w); for (pass = 0; pass < DETAILED_PASS_COUNT; ++pass) { int ppidx = 0; DBG (10, "Performing detailed calibration scan %d\n", pass); switch (pass) { case DETAILED_PASS_OFFSETS: rt_turn_off_lamp(); usleep(500000); /* To be sure it has gone off */ break; case DETAILED_PASS_GAINS_FIRSTPASS: rt_turn_on_lamp(); usleep(500000); /* Give the lamp time to settle */ break; } dcd.pixelnow = dcd.channelnow = dcd.firstrowdone = 0; rts8801_fullscan (x, iCalibY, w, detailed_calibration_rows + 1, resolution, resolution, HP3500_COLOR_SCAN, (rts8801_callback) accumfunc, &dcd, calib_info, RTS8801_F_SUPPRESS_MOVEMENT | RTS8801_F_NO_DISPLACEMENTS, red_calibration_offset, green_calibration_offset, blue_calibration_offset, end_calibration_offset, 0, 0); DBG (10, " Detailed calibration scan %d completed\n", pass); for (i = 0; i < 3; ++i) { int idx = (i == 0) ? red_calibration_offset : (i == 1) ? green_calibration_offset : blue_calibration_offset; for (j = 0; j < w; ++j) { double multnow = 0x80; int offnow = 0; /* This seems to be the approach for reg 0x40 & 0x3f == 0x27, which allows detailed * calibration to return either higher or lower values. */ { double denom1 = calcmedian (dcd.buffers[i], j, w, detailed_calibration_rows); switch (pass) { case DETAILED_PASS_OFFSETS: /* The offset is the number needed to be subtracted from "black" at detailed gain = 0x80, * which is the value we started with. For the next round, pull the gain down to 0x20. Our * next scan is a test scan to confirm the offset works. */ multnow = 0x20; offnow = denom1; break; case DETAILED_PASS_GAINS_FIRSTPASS: multnow = 128.0 / denom1 * 0x20; /* Then bring it up to whatever we need to hit 192 */ if (multnow > 255) multnow = 255; offnow = pDetailedCalib[idx]; break; case DETAILED_PASS_GAINS_SECONDPASS: multnow = 255.0 / denom1 * contrast_adjust * pDetailedCalib[idx+1]; /* And finally to 255 */ offnow = pDetailedCalib[idx] - brightness_adjust * 0x80 / multnow; if (offnow < 0) { postprocess_offsets[ppidx] = multnow * offnow / 0x80; offnow = 0; needs_postprocessed_calibration = 1; } else if (offnow > 255) { postprocess_offsets[ppidx] = multnow * (offnow - 255) / 0x80; offnow = 255; needs_postprocessed_calibration = 1; } else { postprocess_offsets[ppidx] = 0; } if (multnow > 255) { postprocess_gains[ppidx] = multnow / 255; multnow = 255; needs_postprocessed_calibration = 1; } else { postprocess_gains[ppidx] = 1.0; } break; } } if (offnow > 255) offnow = 255; for (k = 0; k < resolution_divisor; ++k) { pDetailedCalib[idx++] = offnow; /* Subtract this value from the result at gains = 0x80*/ pDetailedCalib[idx++] = multnow; /* Then multiply by this value divided by 0x80 */ } ++ppidx; } } if (pass == DETAILED_PASS_GAINS_SECONDPASS) { /* Build gamma table */ unsigned char *redgamma = pDetailedCalib; unsigned char *greengamma = redgamma + 512; unsigned char *bluegamma = greengamma + 512; double val; double invgamma = 1.0l / gamma; *redgamma++ = *bluegamma++ = *greengamma++ = 0; /* The windows driver does a linear interpolation for the next 19 boundaries */ val = pow (20.0l / 255, invgamma) * 255; for (j = 1; j <= 20; ++j) { *redgamma++ = *bluegamma++ = *greengamma++ = val * j / 20 + 0.5; *redgamma++ = *bluegamma++ = *greengamma++ = val * j / 20 + 0.5; } for (; j <= 255; ++j) { val = pow((double) j / 255, invgamma) * 255; *redgamma++ = *bluegamma++ = *greengamma++ = val + 0.5; *redgamma++ = *bluegamma++ = *greengamma++ = val + 0.5; } *redgamma++ = *bluegamma++ = *greengamma++ = 255; } DBG (10, "\n"); rt_set_sram_page (0); rt_set_one_register (0x93, r93setting); rt_write_sram (calibration_size, pDetailedCalib); } /* 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, red_calibration_offset, green_calibration_offset, blue_calibration_offset, end_calibration_offset, needs_postprocessed_calibration ? postprocess_offsets : 0, needs_postprocessed_calibration ? postprocess_gains : 0); rt_turn_off_lamp (); rts8801_rewind (); rt_set_powersave_mode (1); if (pDetailedCalib) free (pDetailedCalib); if (detail_buffer) free (detail_buffer); if (tdetail_buffer) free(tdetail_buffer); if (postprocess_gains) free(postprocess_gains); if (postprocess_offsets) free(postprocess_offsets); return 0; } static int writefunc (struct hp3500_write_info *winfo, int bytes, char *data) { static int warned = 0; if (bytes > winfo->bytesleft) { if (!warned) { warned = 1; DBG (1, "Overflow protection triggered\n"); rt_stop_moving (); } bytes = winfo->bytesleft; if (!bytes) return 0; } winfo->bytesleft -= bytes; return write (winfo->scanner->pipe_w, data, bytes) == bytes; } #ifdef _POSIX_SOURCE static void sigtermHandler (int signal) { (void) signal; /* get rid of compiler warning */ cancelled_scan = 1; } #endif 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; struct hp3500_write_info winfo; int status; 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; if (getenv ("HP3500_NOWARMUP") && atoi (getenv ("HP3500_NOWARMUP")) > 0) do_warmup = 0; udh = scanner->sfd; cancelled_scan = 0; winfo.scanner = scanner; winfo.bytesleft = scanner->bytes_per_scan_line * scanner->scan_height_pixels; if (getenv ("HP3500_SLEEP")) { int seconds = atoi (getenv ("HP3500_SLEEP")); DBG (1, "Backend process %d sleeping for %d seconds\n", getpid (), seconds); sleep (seconds); } DBG (10, "Scanning at %ddpi, mode=%s\n", scanner->resolution, scan_mode_list[scanner->mode]); 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, scanner->mode, scanner->brightness, scanner->contrast, (rts8801_callback) writefunc, &winfo, scanner->gamma) >= 0) status = SANE_STATUS_GOOD; status = SANE_STATUS_IO_ERROR; close (scanner->pipe_w); return status; } static size_t max_string_size (char 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; } backends-1.3.0/backend/hp3900.c000066400000000000000000000045251456256263500157670ustar00rootroot00000000000000/* HP Scanjet 3900 series - Stand-alone/SANE Backend controller Copyright (C) 2005-2008 Jonathan Bravo Lopez 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, see . 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. */ /* Backend info */ #define BACKEND_NAME hp3900 #define BACKEND_VRSN "0.12" #define BACKEND_AUTHOR "Jonathan Bravo Lopez (JKD)" #define BACKEND_EMAIL "jkdsoft@gmail.com" #define BACKEND_URL "http://jkdsoftware.dyndns.org" #define BACKEND_LICENSE "General Public License (GPL)" /* if you want to compile this one as a sane backend then comment next line */ /* Caution: Sources included in SANE project don't provide hp3900_stdalone.c */ /*#define STANDALONE*/ #ifdef STANDALONE #include "hp3900_stdalone.c" #else #include "hp3900_sane.c" #endif backends-1.3.0/backend/hp3900.conf.in000066400000000000000000000006131456256263500170710ustar00rootroot00000000000000# # Configuration file for the hp3900 backend # # HP Scanjet 3800 usb 0x03f0 0x2605 # HP Scanjet 3970c usb 0x03f0 0x2305 # HP Scanjet 4070 Photosmart usb 0x03f0 0x2405 # HP Scanjet 4370 usb 0x03f0 0x4105 # HP Scanjet G2710 usb 0x03f0 0x2805 # HP Scanjet G3010 usb 0x03f0 0x4205 # HP Scanjet G3110 usb 0x03f0 0x4305 # UMAX Astra 4900/4950 usb 0x06dc 0x0020 # BenQ 5550 usb 0x04a5 0x2211 backends-1.3.0/backend/hp3900_config.c000066400000000000000000014773331456256263500173300ustar00rootroot00000000000000/* HP Scanjet 3900 series - RTS8822 internal config Copyright (C) 2005-2009 Jonathan Bravo Lopez 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, see . 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. */ /* returns device model according to given product and vendor id's */ static SANE_Int cfg_device_get(SANE_Int product, SANE_Int vendor); /* returns information and capabilities about selected chipset model */ static SANE_Int cfg_chipset_get(SANE_Int model, struct st_chip *chipset); /* returns the chipset model for each scanner */ static SANE_Int cfg_chipset_model_get(SANE_Int device); /* buttons for each scanner */ static SANE_Int cfg_buttons_get(struct st_buttons *reg); /* area constraints for each scanner */ static SANE_Int cfg_constrains_get(struct st_constrains *constrain); /* spectrum clock generator for each scanner */ static SANE_Int cfg_sscg_get(SANE_Int *enable, SANE_Int *mode, SANE_Int *clock); /* motor general configuration for each scanner */ static SANE_Int cfg_motor_get(struct st_motorcfg *reg); /* motor resource for each scanner */ static SANE_Byte *cfg_motor_resource_get(SANE_Byte *size); /* sensor general configuration for each scanner */ static SANE_Int cfg_sensor_get(struct st_sensorcfg *reg); /* reference voltages for each scanner */ static void cfg_refvoltages_get(SANE_Int sensortype, SANE_Byte *vrts, SANE_Byte *vrms, SANE_Byte *vrbs); static void hp3800_refvoltages(SANE_Int usb, SANE_Int sensor, SANE_Byte *vrts, SANE_Byte *vrms, SANE_Byte *vrbs); static void hp3970_refvoltages(SANE_Int usb, SANE_Int sensor, SANE_Byte *vrts, SANE_Byte *vrms, SANE_Byte *vrbs); /* offset calibration start and length */ static void cfg_offset_get(SANE_Int sensortype, SANE_Int resolution, SANE_Int scantype, SANE_Int *left, SANE_Int *width); static void ua4900_offset(SANE_Int resolution, SANE_Int scantype, SANE_Int *left, SANE_Int *width); static void hp3800_offset(SANE_Int resolution, SANE_Int scantype, SANE_Int *left, SANE_Int *width); static void hp3970_offset(SANE_Int sensor, SANE_Int resolution, SANE_Int scantype, SANE_Int *left, SANE_Int *width); static void hp4370_offset(SANE_Int resolution, SANE_Int scantype, SANE_Int *left, SANE_Int *width); /* autoref configuration */ static void cfg_autoref_get(struct st_autoref *reg); /* autoref start effective pixel */ static SANE_Int cfg_effectivepixel_get(SANE_Int sensortype, SANE_Int resolution); static SANE_Int ua4900_effectivepixel(SANE_Int resolution); static SANE_Int hp3800_effectivepixel(SANE_Int resolution); static SANE_Int hp3970_effectivepixel(SANE_Int sensor, SANE_Int resolution); static SANE_Int hp4370_effectivepixel(SANE_Int resolution); /* default values for gain and offset */ static SANE_Int cfg_gainoffset_get(SANE_Int sensortype, struct st_gain_offset *reg); static SANE_Int bq5550_gainoffset(SANE_Int usb, struct st_gain_offset *myreg); static SANE_Int ua4900_gainoffset(SANE_Int usb, struct st_gain_offset *myreg); static SANE_Int hp3800_gainoffset(SANE_Int usb, struct st_gain_offset *myreg); static SANE_Int hp3970_gainoffset(SANE_Int usb, SANE_Int sensor, struct st_gain_offset *myreg); static SANE_Int hp4370_gainoffset(SANE_Int usb, struct st_gain_offset *myreg); /* values to detect optimum pulse-width modulation */ static SANE_Int cfg_checkstable_get(SANE_Int lamp, struct st_checkstable *check); static SANE_Int ua4900_checkstable(SANE_Int lamp, struct st_checkstable *check); static SANE_Int hp3800_checkstable(SANE_Int lamp, struct st_checkstable *check); static SANE_Int hp3970_checkstable(SANE_Int lamp, struct st_checkstable *check); static SANE_Int hp4370_checkstable(SANE_Int lamp, struct st_checkstable *check); /* fixed pulse-width modulation values */ static SANE_Int cfg_fixedpwm_get(SANE_Int sensortype, SANE_Int scantype); static SANE_Int ua4900_fixedpwm(SANE_Int scantype, SANE_Int usb); static SANE_Int hp3800_fixedpwm(SANE_Int scantype, SANE_Int usb); static SANE_Int hp3970_fixedpwm(SANE_Int scantype, SANE_Int usb, SANE_Int sensor); static SANE_Int hp4370_fixedpwm(SANE_Int scantype, SANE_Int usb); /* virtual origin (ser and ler references) */ static void cfg_vrefs_get(SANE_Int sensortype, SANE_Int res, SANE_Int *ser, SANE_Int *ler); static void hp3800_vrefs(SANE_Int res, SANE_Int *ser, SANE_Int *ler); static void hp3970_vrefs(SANE_Int usb, SANE_Int sensor, SANE_Int res, SANE_Int *ser, SANE_Int *ler); static void hp4370_vrefs(SANE_Int res, SANE_Int *ser, SANE_Int *ler); /* scanmodes supported by each scanner */ static SANE_Int cfg_scanmode_get(SANE_Int sensortype, SANE_Int sm, struct st_scanmode *mymode); static SANE_Int bq5550_scanmodes(SANE_Int usb, SANE_Int sm, struct st_scanmode *mymode); static SANE_Int ua4900_scanmodes(SANE_Int usb, SANE_Int sm, struct st_scanmode *mymode); static SANE_Int hp3800_scanmodes(SANE_Int usb, SANE_Int sm, struct st_scanmode *mymode); static SANE_Int hp3970_scanmodes(SANE_Int usb, SANE_Int ccd, SANE_Int sm, struct st_scanmode *mymode); static SANE_Int hp4370_scanmodes(SANE_Int usb, SANE_Int sm, struct st_scanmode *mymode); /* timing values for ccd sensors */ static SANE_Int cfg_timing_get(SANE_Int sensortype, SANE_Int tm, struct st_timing *reg); static SANE_Int bq5550_timing_get(SANE_Int tm, struct st_timing *reg); static SANE_Int ua4900_timing_get(SANE_Int tm, struct st_timing *reg); static SANE_Int hp3800_timing_get(SANE_Int tm, struct st_timing *reg); static SANE_Int hp3970_timing_get(SANE_Int sensortype, SANE_Int tm, struct st_timing *reg); static SANE_Int hp4370_timing_get(SANE_Int tm, struct st_timing *reg); /* motor movements */ static SANE_Int cfg_motormove_get(SANE_Int sensortype, SANE_Int mm, struct st_motormove *reg); static SANE_Int bq5550_motormove(SANE_Int item, struct st_motormove *reg); static SANE_Int hp3800_motormove(SANE_Int item, struct st_motormove *reg); static SANE_Int hp3970_motormove(SANE_Int usb, SANE_Int ccd, SANE_Int item, struct st_motormove *reg); /* motor curves */ static SANE_Int *cfg_motorcurve_get(void); static SANE_Int *bq5550_motor(void); static SANE_Int *hp3800_motor(void); static SANE_Int *hp3970_motor(void); static SANE_Int *hp4370_motor(void); /* shading cut values */ static void cfg_shading_cut_get(SANE_Int sensortype, SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue); static void ua4900_shading_cut(SANE_Int usb, SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue); static void hp3800_shading_cut(SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue); static void hp3970_shading_cut(SANE_Int usb, SANE_Int ccd, SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue); static void hp4370_shading_cut(SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue); /* wrefs values */ static void cfg_wrefs_get(SANE_Int sensortype, SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue); static void ua4900_wrefs(SANE_Int usb, SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue); static void hp3800_wrefs(SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue); static void hp3970_wrefs(SANE_Int usb, SANE_Int ccd, SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue); static void hp4370_wrefs(SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue); /* DEPRECATED FUNCTIONS !!!!!!!!!!!!!!!!!!! */ static int get_value(int section, int option, int defvalue, int file); /* HP Scanjet 3800 */ static int hp3800_calibreflective(int option, int defvalue); static int hp3800_calibtransparent(int option, int defvalue); static int hp3800_calibnegative(int option, int defvalue); static int srt_hp3800_scanparam_get(int option, int defvalue); /* UMAX Astra 4900 */ static int ua4900_calibreflective(int option, int defvalue); static int ua4900_calibtransparent(int option, int defvalue); static int ua4900_calibnegative(int option, int defvalue); /* HP Scanjet 3970 */ static int hp3970_calibreflective(int option, int defvalue); static int hp3970_calibtransparent(int option, int defvalue); static int hp3970_calibnegative(int option, int defvalue); static int srt_hp3970_scanparam_get(int file, int option, int defvalue); static int srt_hp3970_platform_get(int option, int defvalue); /* HP Scanjet 4370 */ static int hp4370_calibreflective(int option, int defvalue); static int hp4370_calibtransparent(int option, int defvalue); static int hp4370_calibnegative(int option, int defvalue); static int srt_hp4370_scanparam_get(int file, int option, int defvalue); /* HP Scanjet g3110 */ static int hpg3110_calibnegative(int option, int defvalue); static int hpg3110_calibtransparent(int option, int defvalue); /* ----- Implementation ----- */ /* DEPRECATED enumerations */ enum ConfigFiles { FITCALIBRATE=0, T_RTINIFILE, T_USB1INIFILE, S_RTINIFILE, S_USB1INIFILE }; enum fcsec6 { CALIBREFLECTIVE = 0, CALIBTRANSPARENT, CALIBNEGATIVEFILM, SCANINFO, SCAN_CALI, WSTRIPXPOS, WSTRIPYPOS, BSTRIPXPOS, BSTRIPYPOS, BREFR, BREFG, BREFB, REFBITDEPTH, OFFSETHEIGHT, OFFSETNSIGMA, OFFSETTARGETMAX, OFFSETTARGETMIN, OFFSETAVGTARGETR, OFFSETAVGTARGETG, OFFSETAVGTARGETB, ADCOFFEVENODD, CALIBOFFSET1ON, ADCOFFQUICKWAY, ADCOFFPREDICTSTART, ADCOFFPREDICTEND, OFFSETTUNESTEP1, OFFSETBOUNDARYRATIO1, OFFSETAVGRATIO1, OFFSETEVEN1R, OFFSETEVEN1G, OFFSETEVEN1B, OFFSETODD1R, OFFSETODD1G, OFFSETODD1B, ADCOFFPREDICTR, ADCOFFPREDICTG, ADCOFFPREDICTB, ADCOFFEVEN1R_1ST, ADCOFFEVEN1G_1ST, ADCOFFEVEN1B_1ST, ADCOFFODD1R_1ST, ADCOFFODD1G_1ST, ADCOFFODD1B_1ST, PEAKR, PEAKG, PEAKB, MINR, MING, MINB, CALIBOFFSET2ON, OFFSETTUNESTEP2, OFFSETBOUNDARYRATIO2, OFFSETAVGRATIO2, OFFSETEVEN2R, OFFSETEVEN2G, OFFSETEVEN2B, OFFSETODD2R, OFFSETODD2G, OFFSETODD2B, GAINHEIGHT, GAINTARGETFACTOR, CALIBPAGON, HIPAGR, HIPAGG, HIPAGB, LOPAGR, LOPAGG, LOPAGB, PAGR, PAGG, PAGB, CALIBGAIN1ON, GAIN1R, GAIN1G, GAIN1B, CALIBGAIN2ON, GAIN2R, GAIN2G, GAIN2B, TOTSHADING, BSHADINGON, BSHADINGHEIGHT, BSHADINGPREDIFFR, BSHADINGPREDIFFG, BSHADINGPREDIFFB, BSHADINGDEFCUTOFF, WSHADINGON, WSHADINGHEIGHT, WSHADINGPREDIFFR, WSHADINGPREDIFFG, WSHADINGPREDIFFB, PARKHOMEAFTERCALIB, SHADINGTIME_16BIT, SHADOWTIME_16BIT, SHADINGTIME_8BIT, SHADOWTIME_8BIT, PREVIEWDPI, FIRSTDCOFFSETEVEN0, FIRSTDCOFFSETODD0, FIRSTDCOFFSETEVEN1, FIRSTDCOFFSETODD1, FIRSTDCOFFSETEVEN2, FIRSTDCOFFSETODD2, CALIBOFFSET10N, CALIBOFFSET20N, CALIBGAIN10N, CALIBGAIN20N, ARRANGELINE, COMPRESSION, TA_X_START, TA_Y_START, DPIGAINCONTROL600, DPIGAINCONTROL_TA600, DPIGAINCONTROL_NEG600, CRVS, MLOCK, ENABLEWARMUP, NMAXTARGET, NMINTARGET, NMAXTARGETTA, NMINTARGETTA, NMAXTARGETNEG, NMINTARGETNEG, STABLEDIFF, DELTAPWM, PWMLAMPLEVEL, TMAPWMDUTY, PAG1, PAG2, PAG3, LEFTLEADING, WAVE_XSTART, WAVE_S575_XDUMMY_2400, WAVE_S575_XDUMMY_1200, WAVE_S575_XDUMMY_600, ODD_DCOFFSET11, ODD_DCOFFSET12, ODD_DCOFFSET13, ODD_DCOFFSET21, ODD_DCOFFSET22, ODD_DCOFFSET23, EVEN_DCOFFSET11, EVEN_DCOFFSET12, EVEN_DCOFFSET13, EVEN_DCOFFSET21, EVEN_DCOFFSET22, EVEN_DCOFFSET23, DCGAIN11, DCGAIN12, DCGAIN13, DCGAIN21, DCGAIN22, DCGAIN23, CRYSTALFREQ, PGA1, PGA2, PGA3, VGAGAIN11, VGAGAIN12, VGAGAIN13, DCSTEPEVEN1, DCSTEPODD1, DCSTEPEVEN2, DCSTEPODD2, DCSTEPEVEN3, DCSTEPODD3, FIRSTDCOFFSETEVEN11, FIRSTDCOFFSETODD11, FIRSTDCOFFSETEVEN12, FIRSTDCOFFSETODD12, FIRSTDCOFFSETEVEN13, FIRSTDCOFFSETODD13, DCOFFSETEVEN11, DCOFFSETODD11, DCOFFSETEVEN12, DCOFFSETODD12, DCOFFSETEVEN13, DCOFFSETODD13, SHADINGBASE, SHADINGFACT1, SHADINGFACT2, SHADINGFACT3, PIXELDARKLEVEL, EXPOSURETIME, SCANYSTART, SCANYLINES, BINARYTHRESHOLDH, BINARYTHRESHOLDL, CLOSETIME, PLATFORM, SCAN_PARAM, USB1_PWM, USB2_PWM, WAVETEST, DMA_PARAM, TRUE_GRAY_PARAM, CALI_PARAM, DUMMYLINE, MEXPT1, MEXPT2, MEXPT3, EXPT1, EXPT2, EXPT3, STARTPOS, LINEDARLAMPOFF, MCLKIOC }; static SANE_Int cfg_device_get(SANE_Int product, SANE_Int vendor) { struct st_myreg { SANE_Int vendor, product, device; }; struct st_myreg myreg[] = { /*vendor, prodct, device */ { 0x4a5, 0x2211, BQ5550 }, /* BenQ 5550 */ { 0x6dc, 0x0020, UA4900 }, /* UMAX Astra 4900 */ { 0x3f0, 0x2605, HP3800 }, /* HP Scanjet 3800 */ { 0x3f0, 0x2805, HPG2710}, /* HP Scanjet G2710 */ { 0x3f0, 0x2305, HP3970 }, /* HP Scanjet 3970c */ { 0x3f0, 0x2405, HP4070 }, /* HP Scanjet 4070 Photosmart */ { 0x3f0, 0x4105, HP4370 }, /* HP Scanjet 4370 */ { 0x3f0, 0x4205, HPG3010}, /* HP Scanjet G3010 */ { 0x3f0, 0x4305, HPG3110} /* HP Scanjet G3010 */ }; SANE_Int rst = -1; /* default */ SANE_Int a; SANE_Int count = sizeof(myreg) / sizeof(struct st_myreg); for (a = 0; a < count; a++) { if ((vendor == myreg[a].vendor)&&(product == myreg[a].product)) { rst = myreg[a].device; break; } } return rst; } static SANE_Int cfg_chipset_model_get(SANE_Int device) { /* returns the chipset model for each scanner */ struct st_myreg { SANE_Int device, chipset; }; struct st_myreg myreg[] = { /*device , chipset */ { HP3800 , RTS8822BL_03A }, { HPG2710, RTS8822BL_03A }, { BQ5550 , RTS8823L_01E }, { UA4900 , RTS8822L_01H }, { HP3970 , RTS8822L_01H }, { HP4070 , RTS8822L_01H }, { HP4370 , RTS8822L_02A }, { HPG3010, RTS8822L_02A }, { HPG3110, RTS8822L_02A } }; SANE_Int rst = RTS8822L_01H; /* default */ SANE_Int a; SANE_Int count = sizeof(myreg) / sizeof(struct st_myreg); for (a = 0; a < count; a++) { if (device == myreg[a].device) { rst = myreg[a].chipset; break; } } return rst; } static SANE_Int cfg_chipset_get(SANE_Int model, struct st_chip *chipset) { /* returns info and capabilities of selected chipset */ SANE_Int rst = ERROR; if (chipset != NULL) { struct st_chip data[] = { /* model , capabilities, name */ {RTS8823L_01E , 0 , "RTS8823L-01E" }, {RTS8822BL_03A, CAP_EEPROM , "RTS8822BL-03A"}, {RTS8822L_02A , CAP_EEPROM , "RTS8822L-02A" }, {RTS8822L_01H , CAP_EEPROM , "RTS8822L-01H" } }; SANE_Int a; for (a = 0; a < 4; a++) { if (model == data[a].model) { /* model found, fill information */ chipset->model = data[a].model; chipset->capabilities = data[a].capabilities; chipset->name = strdup(data[a].name); if (chipset->name != NULL) rst = OK; break; } } } return rst; } /** SEC: Device's Buttons ---------- */ static SANE_Int cfg_buttons_get(struct st_buttons *reg) { /* buttons for each scanner */ SANE_Int rst = ERROR; if (reg != NULL) { struct st_myreg { SANE_Int device; struct st_buttons value; }; struct st_myreg myreg[] = { /*device, {count, {btn1, btn2, btn3, btn4, btn5, btn6)} */ { BQ5550 , {3 , {0x01, 0x02, 0x08, -1, -1, -1}}}, { UA4900 , {4 , {0x04, 0x08, 0x02, 0x01, -1, -1}}}, { HP3800 , {3 , {0x01, 0x02, 0x04, -1, -1, -1}}}, { HPG2710, {3 , {0x01, 0x02, 0x04, -1, -1, -1}}}, { HP3970 , {4 , {0x04, 0x08, 0x02, 0x01, -1, -1}}}, { HP4070 , {4 , {0x04, 0x08, 0x02, 0x01, -1, -1}}}, { HP4370 , {4 , {0x04, 0x08, 0x02, 0x01, -1, -1}}}, { HPG3010, {4 , {0x04, 0x08, 0x02, 0x01, -1, -1}}}, { HPG3110, {4 , {0x04, 0x08, 0x02, 0x01, -1, -1}}} }; SANE_Int a; SANE_Int count = sizeof(myreg) / sizeof(struct st_myreg); for (a = 0; a < count; a++) { if (RTS_Debug->dev_model == myreg[a].device) { memcpy(reg, &myreg[a].value, sizeof(struct st_buttons)); rst = OK; break; } } } return rst; } /** SEC: Spectrum clock generator ---------- */ static SANE_Int cfg_sscg_get(SANE_Int *enable, SANE_Int *mode, SANE_Int *clock) { SANE_Int rst = ERROR; if ((enable != NULL)&&(mode != NULL)&&(clock != NULL)) { struct st_myreg { SANE_Int device; SANE_Int value[3]; }; struct st_myreg myreg[] = { /*device, {enable, mode, clock} */ { BQ5550, {1 , 1, 1}}, { UA4900, {1 , 1, 0}}, { HP3800, {1 , 1, 0}}, {HPG2710, {1 , 1, 0}}, { HP3970, {1 , 1, 0}}, { HP4070, {1 , 1, 0}}, { HP4370, {1 , 1, 0}}, {HPG3010, {1 , 1, 0}}, {HPG3110, {1 , 1, 0}} }; SANE_Int a; SANE_Int count = sizeof(myreg) / sizeof(struct st_myreg); /* default values */ *enable = 0; *mode = 0; *clock = 3; for (a = 0; a < count; a++) { if (RTS_Debug->dev_model == myreg[a].device) { *enable = myreg[a].value[0]; *mode = myreg[a].value[1]; *clock = myreg[a].value[2]; rst = OK; break; } } } return rst; } /** SEC: Motors ---------- */ static SANE_Int cfg_motor_get(struct st_motorcfg *reg) { SANE_Int rst = ERROR; if (reg != NULL) { struct st_myreg { SANE_Int device; struct st_motorcfg motor; }; struct st_myreg myreg[] = { /*device, {type , res, freq, speed, basemove, highmove, parkmove, change}} */ { BQ5550, {MT_OUTPUTSTATE, 1200, 30, 800, 1, 0, 0, TRUE}}, { UA4900, {MT_OUTPUTSTATE, 2400, 30, 800, 1, 0, 0, TRUE}}, { HP3800, {MT_OUTPUTSTATE, 1200, 30, 800, 1, 0, 0, TRUE}}, {HPG2710, {MT_OUTPUTSTATE, 1200, 30, 800, 1, 0, 0, TRUE}}, { HP3970, {MT_OUTPUTSTATE, 2400, 30, 800, 1, 0, 0, TRUE}}, { HP4070, {MT_OUTPUTSTATE, 2400, 30, 800, 1, 0, 0, TRUE}}, { HP4370, {MT_OUTPUTSTATE, 2400, 30, 800, 1, 0, 0, TRUE}}, {HPG3010, {MT_OUTPUTSTATE, 2400, 30, 800, 1, 0, 0, TRUE}}, {HPG3110, {MT_OUTPUTSTATE, 2400, 30, 800, 1, 0, 0, TRUE}} }; SANE_Int a; SANE_Int count = sizeof(myreg) / sizeof(struct st_myreg); /* default values */ memset(reg, 0, sizeof(struct st_motorcfg)); reg->type = -1; for (a = 0; a < count; a++) { if (RTS_Debug->dev_model == myreg[a].device) { memcpy(reg, &myreg[a].motor, sizeof(struct st_motorcfg)); rst = OK; break; } } } return rst; } /** SEC: Sensors ---------- */ static SANE_Int cfg_sensor_get(struct st_sensorcfg *reg) { SANE_Int rst = ERROR; if (reg != NULL) { struct st_myreg { SANE_Int device; struct st_sensorcfg sensor; }; struct st_myreg myreg[] = { /*device, {type , name , resolution, {chnl_colors }, {chnl_gray }, {rgb_order }, line_dist, evenodd_dist} */ { BQ5550, {CCD_SENSOR, -1, 1200 , {CL_BLUE, CL_GREEN, CL_RED }, {CL_GREEN, 0}, {CL_BLUE, CL_GREEN, CL_RED }, 24 , 4 }}, { UA4900, {CIS_SENSOR, SNYS575, 2400 , {CL_RED , CL_GREEN, CL_BLUE}, {CL_RED , 0}, {CL_RED , CL_GREEN, CL_BLUE}, 24 , 0 }}, { HP3800, {CCD_SENSOR, TCD2905, 2400 , {CL_RED , CL_GREEN, CL_BLUE}, {CL_RED , 0}, {CL_RED , CL_GREEN, CL_BLUE}, 64 , 8 }}, {HPG2710, {CCD_SENSOR, TCD2905, 2400 , {CL_RED , CL_GREEN, CL_BLUE}, {CL_RED , 0}, {CL_RED , CL_GREEN, CL_BLUE}, 64 , 8 }}, { HP3970, {CCD_SENSOR, TCD2952, 2400 , {CL_RED , CL_GREEN, CL_BLUE}, {CL_RED , 0}, {CL_RED , CL_GREEN, CL_BLUE}, 24 , 4 }}, { HP4070, {CCD_SENSOR, TCD2952, 2400 , {CL_RED , CL_GREEN, CL_BLUE}, {CL_RED , 0}, {CL_RED , CL_GREEN, CL_BLUE}, 24 , 4 }}, { HP4370, {CCD_SENSOR, TCD2958, 4800 , {CL_RED , CL_GREEN, CL_BLUE}, {CL_RED , 0}, {CL_RED , CL_GREEN, CL_BLUE}, 128 , 6 }}, {HPG3010, {CCD_SENSOR, TCD2958, 4800 , {CL_RED , CL_GREEN, CL_BLUE}, {CL_RED , 0}, {CL_RED , CL_GREEN, CL_BLUE}, 128 , 6 }}, {HPG3110, {CCD_SENSOR, TCD2958, 4800 , {CL_RED , CL_GREEN, CL_BLUE}, {CL_RED , 0}, {CL_RED , CL_GREEN, CL_BLUE}, 128 , 6 }} }; SANE_Int a; SANE_Int count = sizeof(myreg) / sizeof(struct st_myreg); /* default values */ memset(reg, 0, sizeof(struct st_sensorcfg)); reg->type = -1; for (a = 0; a < count; a++) { if (RTS_Debug->dev_model == myreg[a].device) { memcpy(reg, &myreg[a].sensor, sizeof(struct st_sensorcfg)); rst = OK; break; } } } return rst; } /** SEC: Reference voltages ---------- */ static void hp3800_refvoltages(SANE_Int usb, SANE_Int sensor, SANE_Byte *vrts, SANE_Byte *vrms, SANE_Byte *vrbs) { /* this function returns top, middle and bottom reference voltages for each scanner */ struct st_reg { SANE_Int usb; SANE_Int sensor; SANE_Byte values[3]; }; struct st_reg myreg[] = { /* usb, sensor , {vrts, vrms, vrbs} */ {USB20, CCD_SENSOR, { 2, 3, 2}}, {USB11, CCD_SENSOR, { 2, 3, 2}}, }; if ((vrts != NULL)&&(vrms != NULL)&&(vrbs != NULL)) { SANE_Int a; SANE_Int count = sizeof(myreg) / sizeof(struct st_reg); *vrts = *vrms = *vrbs = 0; for (a = 0; a < count; a++) { if ((myreg[a].usb == usb)&&(myreg[a].sensor == sensor)) { *vrts = myreg[a].values[0]; *vrms = myreg[a].values[1]; *vrbs = myreg[a].values[2]; } } } } static void hp3970_refvoltages(SANE_Int usb, SANE_Int sensor, SANE_Byte *vrts, SANE_Byte *vrms, SANE_Byte *vrbs) { /* this function returns top, middle and bottom reference voltages for each scanner */ struct st_reg { SANE_Int usb; SANE_Int sensor; SANE_Byte values[3]; }; struct st_reg myreg[] = { /* usb, sensor , {vrts, vrms, vrbs} */ {USB20, CCD_SENSOR, { 0, 0, 0}}, {USB11, CCD_SENSOR, { 0, 0, 0}}, {USB20, CIS_SENSOR, { 0, 0, 0}}, {USB11, CIS_SENSOR, { 0, 0, 0}} }; if ((vrts != NULL)&&(vrms != NULL)&&(vrbs != NULL)) { SANE_Int a; SANE_Int count = sizeof(myreg) / sizeof(struct st_reg); *vrts = *vrms = *vrbs = 0; for (a = 0; a < count; a++) { if ((myreg[a].usb == usb)&&(myreg[a].sensor == sensor)) { *vrts = myreg[a].values[0]; *vrms = myreg[a].values[1]; *vrbs = myreg[a].values[2]; } } } } static void cfg_refvoltages_get(SANE_Int sensortype, SANE_Byte *vrts, SANE_Byte *vrms, SANE_Byte *vrbs) { /* this function returns top, middle and bottom reference voltages for each scanner */ switch(RTS_Debug->dev_model) { case HP3800: case HPG2710: hp3800_refvoltages(RTS_Debug->usbtype, sensortype, vrts, vrms, vrbs); break; default: /* at this momment all analyzed scanners have the same values */ hp3970_refvoltages(RTS_Debug->usbtype, sensortype, vrts, vrms, vrbs); break; } } /** SEC: Calibration Offset ---------- */ static void hp3800_offset(SANE_Int resolution, SANE_Int scantype, SANE_Int *left, SANE_Int *width) { /* this function provides left coordinate and width to calculate offset Sensor = Toshiba T2905 */ struct st_ofst { SANE_Int left; SANE_Int width; }; struct st_reg { SANE_Int resolution; struct st_ofst values[3]; }; struct st_reg myreg[] = { /*res , {ref(L,W), tma(L,W), neg(L,W)} */ { 2400, {{15, 20}, {15, 20}, {15, 20}}}, { 1200, {{10, 10}, {10, 10}, {10, 10}}}, { 600, {{ 2, 10}, { 5, 10}, { 5, 10}}}, { 300, {{ 1, 5}, { 1, 5}, { 1, 5}}}, { 150, {{ 0, 3}, { 0, 3}, { 0, 3}}} }; if ((left != NULL)&&(width != NULL)) { SANE_Int a; SANE_Int count = sizeof(myreg) / sizeof(struct st_reg); for (a = 0; a < count; a++) { if (myreg[a].resolution == resolution) { scantype--; *left = myreg[a].values[scantype].left; *width = myreg[a].values[scantype].width; break; } } } } static void hp3970_offset(SANE_Int sensor, SANE_Int resolution, SANE_Int scantype, SANE_Int *left, SANE_Int *width) { /* this function provides left coordinate and width to calculate offset */ struct st_ofst { SANE_Int left; SANE_Int width; }; struct st_reg { SANE_Int sensor; SANE_Int resolution; struct st_ofst values[3]; }; struct st_reg myreg[] = { /* sensor , res , {ref(L,W), tma(L,W), neg(L,W)} */ {CCD_SENSOR, 2400, {{16, 20}, {16, 20}, {16, 20}}}, {CCD_SENSOR, 1200, {{16, 10}, {16, 10}, {16, 10}}}, {CCD_SENSOR, 600, {{15, 10}, {15, 10}, {15, 10}}}, {CCD_SENSOR, 300, {{ 7, 5}, { 7, 5}, { 7, 5}}}, {CCD_SENSOR, 200, {{ 7, 3}, { 7, 3}, { 7, 3}}}, {CCD_SENSOR, 100, {{ 3, 3}, { 3, 3}, { 3, 3}}}, /* sensor , res , {ref(L,W), tma(L,W), neg(L,W)} */ {CIS_SENSOR, 2400, {{84, 20}, {84, 20}, {84, 20}}}, {CIS_SENSOR, 1200, {{54, 10}, {54, 10}, {54, 10}}}, {CIS_SENSOR, 600, {{28, 10}, {28, 10}, {28, 10}}}, {CIS_SENSOR, 300, {{15, 5}, {15, 5}, {15, 5}}}, {CIS_SENSOR, 200, {{ 5, 3}, { 5, 3}, { 5, 3}}}, {CIS_SENSOR, 100, {{ 2, 3}, { 2, 3}, { 2, 3}}} }; if ((left != NULL)&&(width != NULL)) { SANE_Int a; SANE_Int count = sizeof(myreg) / sizeof(struct st_reg); for (a = 0; a < count; a++) { if ((myreg[a].sensor == sensor)&&(myreg[a].resolution == resolution)) { scantype--; *left = myreg[a].values[scantype].left; *width = myreg[a].values[scantype].width; break; } } } } static void hp4370_offset(SANE_Int resolution, SANE_Int scantype, SANE_Int *left, SANE_Int *width) { /* this function provides left coordinate and width to calculate offset */ struct st_ofst { SANE_Int left; SANE_Int width; }; struct st_reg { SANE_Int resolution; struct st_ofst values[3]; }; struct st_reg myreg[] = { /* res, {ref(L,W), tma(L,W), neg(L,W)} */ { 4800, {{42, 20}, {42, 20}, {52, 26}}}, { 2400, {{14, 20}, {14, 20}, {14, 26}}}, { 1200, {{ 8, 14}, { 8, 14}, { 8, 14}}}, { 600, {{ 4, 8}, { 4, 8}, { 4, 8}}}, { 300, {{ 2, 4}, { 2, 4}, { 2, 4}}}, { 150, {{ 1, 2}, { 1, 2}, { 1, 2}}} }; if ((left != NULL)&&(width != NULL)) { SANE_Int a; SANE_Int count = sizeof(myreg) / sizeof(struct st_reg); for (a = 0; a < count; a++) { if (myreg[a].resolution == resolution) { scantype--; *left = myreg[a].values[scantype].left; *width = myreg[a].values[scantype].width; break; } } } } static void ua4900_offset(SANE_Int resolution, SANE_Int scantype, SANE_Int *left, SANE_Int *width) { /* this function provides left coordinate and width to calculate offset */ struct st_ofst { SANE_Int left; SANE_Int width; }; struct st_reg { SANE_Int resolution; struct st_ofst values[3]; }; struct st_reg myreg[] = { /* res , {ref(L,W), tma(L,W), neg(L,W)} */ { 2400, {{20, 20}, {16, 20}, {16, 20}}}, { 1200, {{20, 10}, {10, 10}, {10, 10}}}, { 600, {{ 7, 10}, {15, 10}, {15, 10}}}, { 300, {{ 5, 10}, { 7, 8}, { 7, 8}}}, { 200, {{ 2, 10}, { 7, 6}, { 7, 6}}}, { 100, {{ 0, 10}, { 3, 4}, { 3, 4}}} }; if ((left != NULL)&&(width != NULL)) { SANE_Int a; SANE_Int count = sizeof(myreg) / sizeof(struct st_reg); for (a = 0; a < count; a++) { if (myreg[a].resolution == resolution) { scantype--; *left = myreg[a].values[scantype].left; *width = myreg[a].values[scantype].width; break; } } } } static void cfg_offset_get(SANE_Int sensortype, SANE_Int resolution, SANE_Int scantype, SANE_Int *left, SANE_Int *width) { switch(RTS_Debug->dev_model) { case UA4900: ua4900_offset(resolution, scantype, left, width); break; case HP3800: case HPG2710: hp3800_offset(resolution, scantype, left, width); break; case HPG3010: case HPG3110: case HP4370: hp4370_offset(resolution, scantype, left, width); break; default: hp3970_offset(sensortype, resolution, scantype, left, width); break; } } /** SEC: Device constraints ---------- */ static SANE_Int cfg_constrains_get(struct st_constrains *constrain) { SANE_Int rst = ERROR; struct st_reg { SANE_Int device; struct st_constrains constrain; }; struct st_reg reg[] = { /* constraints are set in millimeters */ /*device , reflective , negative , transparent */ /* , {{left, width, top, height}, {left, width, top, height}, {left, width, top, height}}}, */ { BQ5550 , {{ 0, 220, 0, 300}, { 88, 42, 0, 83}, { 88, 42, 0, 83}}}, { HP3800 , {{ 0, 220, 0, 300}, { 89, 45, 0, 85}, { 89, 45, 0, 100}}}, {HPG2710 , {{ 0, 220, 0, 300}, { 89, 45, 0, 85}, { 89, 45, 0, 100}}}, { HP3970 , {{ 0, 220, 0, 300}, { 88, 42, 0, 83}, { 88, 42, 0, 83}}}, { HP4070 , {{ 0, 220, 0, 300}, { 58, 99, 0, 197}, { 58, 99, 0, 197}}}, { HP4370 , {{ 0, 220, 0, 300}, { 90, 45, 0, 85}, { 90, 45, 0, 100}}}, { UA4900 , {{ 0, 220, 0, 300}, { 88, 42, 0, 83}, { 88, 42, 0, 83}}}, {HPG3010 , {{ 0, 220, 0, 300}, { 90, 45, 0, 85}, { 89, 45, 0, 100}}}, {HPG3110 , {{ 0, 220, 0, 300}, { 92, 28, 0, 190}, { 85, 44, 0, 190}}} }; if (constrain != NULL) { SANE_Int a; SANE_Int count = sizeof(reg) / sizeof(struct st_reg); for (a = 0; a < count; a++) { if (reg[a].device == RTS_Debug->dev_model) { memcpy(constrain, ®[a].constrain, sizeof(struct st_constrains)); rst = OK; break; } } } return rst; } /** SEC: Motor resource ------------------- */ static SANE_Byte *cfg_motor_resource_get(SANE_Byte *size) { /* this function returns the proper motor resources for a given device */ SANE_Byte *rst = NULL; /* Until now, resource size is always 32 bytes */ rst = (SANE_Byte *)malloc(sizeof(SANE_Byte) * 32); if (size != NULL) *size = 0; if (rst != NULL) { memset(rst, 0, sizeof(SANE_Byte) * 32); switch(RTS_Debug->dev_model) { case BQ5550: { SANE_Byte Resource[] = {0xff, 0xb4, 0xb0, 0xd4, 0xd0, 0x70, 0x50, 0x54, 0x30, 0x34, 0x14, 0x38, 0x18, 0x0c, 0x08, 0x28, 0x04, 0x24, 0x20, 0x44, 0x40, 0xe0, 0xc0, 0xc4, 0xa0, 0xa4, 0x84, 0xa8, 0x88, 0x9c, 0x98, 0xb8}; memcpy(rst, &Resource, sizeof(SANE_Byte) * 32); if (size != NULL) *size = 32; } break; default: { SANE_Byte Resource[] = {0xff, 0x90, 0xb0, 0xd4, 0xd0, 0x70, 0x50, 0x54, 0x30, 0x10, 0x14, 0x38, 0x18, 0x0c, 0x08, 0x28, 0x04, 0x00, 0x20, 0x44, 0x40, 0xe0, 0xc0, 0xc4, 0xa0, 0x80, 0x84, 0xa8, 0x88, 0x9c, 0x98, 0xb8}; memcpy(rst, &Resource, sizeof(SANE_Byte) * 32); if (size != NULL) *size = 32; } break; } } return rst; } /** SEC: Auto reference position ---------- */ static void cfg_autoref_get(struct st_autoref *reg) { if (reg != NULL) { struct st_reg { SANE_Int device; struct st_autoref value; }; struct st_reg myreg[] = { /* x and y offsets are based on 2400 dpi */ /* device, { type , x , y , resolution, extern_boundary}*/ { BQ5550 , {REF_NONE , -40, -40, 600 , 40}}, { UA4900 , {REF_NONE , -40, -40, 600 , 40}}, { HP3800 , {REF_TAKEFROMSCANNER, 88, 624, 600 , 40}}, {HPG2710 , {REF_TAKEFROMSCANNER, 88, 624, 600 , 40}}, { HP3970 , {REF_TAKEFROMSCANNER, 88, 717, 600 , 40}}, { HP4070 , {REF_TAKEFROMSCANNER, 88, 717, 600 , 40}}, { HP4370 , {REF_TAKEFROMSCANNER, 88, 717, 600 , 40}}, {HPG3010 , {REF_TAKEFROMSCANNER, 88, 717, 600 , 40}}, {HPG3110 , {REF_TAKEFROMSCANNER, 88, 717, 600 , 40}} }; SANE_Int a; SANE_Int count = sizeof(myreg) / sizeof(struct st_reg); for (a = 0; a < count; a++) { if (myreg[a].device == RTS_Debug->dev_model) { memcpy(reg, &myreg[a].value, sizeof(struct st_autoref)); break; } } } } static SANE_Int hp3800_effectivepixel(SANE_Int resolution) { struct st_reg { SANE_Int resolution; SANE_Int pixel; }; struct st_reg reg[] = { /* res , pixel */ { 2400, 134 }, { 1200, 134 }, { 600, 230 }, { 300, 172 }, { 200, 230 }, { 100, 230 } }; SANE_Int a; SANE_Int count = sizeof(reg) / sizeof(struct st_reg); SANE_Int rst = 230; /* default */ for (a = 0; a < count; a++) { if (reg[a].resolution == resolution) { rst = reg[a].pixel; break; } } return rst; } static SANE_Int hp3970_effectivepixel(SANE_Int sensor, SANE_Int resolution) { struct st_reg { SANE_Int resolution; SANE_Int pixel[2]; }; struct st_reg reg[] = { /* res , {Toshiba, sony}} */ { 2400, {134 , 218 }}, { 1200, {134 , 240 }}, { 600, {230 , 242 }}, { 300, {160 , 172 }}, { 200, {230 , 242 }}, { 100, {230 , 242 }} }; SANE_Int a; SANE_Int count = sizeof(reg) / sizeof(struct st_reg); SANE_Int rst = 230; /* default */ for (a = 0; a < count; a++) { if (reg[a].resolution == resolution) { rst = (sensor == CCD_SENSOR)? reg[a].pixel[0] : reg[a].pixel[1]; break; } } return rst; } static SANE_Int hp4370_effectivepixel(SANE_Int resolution) { struct st_reg { SANE_Int resolution; SANE_Int pixel; }; struct st_reg reg[] = { /* res , pxl */ { 4800, 134}, { 2400, 134}, { 1200, 134}, { 600, 230}, { 300, 230}, { 150, 230} }; SANE_Int a; SANE_Int count = sizeof(reg) / sizeof(struct st_reg); SANE_Int rst = 230; /* default */ for (a = 0; a < count; a++) { if (reg[a].resolution == resolution) { rst = reg[a].pixel; break; } } return rst; } static SANE_Int ua4900_effectivepixel(SANE_Int resolution) { struct st_reg { SANE_Int resolution; SANE_Int pixel; }; struct st_reg reg[] = { /* res , pixel */ { 2400, 134 }, { 1200, 134 }, { 600, 230 }, { 300, 172 }, { 200, 230 }, { 100, 230 } }; SANE_Int a; SANE_Int count = sizeof(reg) / sizeof(struct st_reg); SANE_Int rst = 230; /* default */ for (a = 0; a < count; a++) { if (reg[a].resolution == resolution) { rst = reg[a].pixel; break; } } return rst; } static SANE_Int cfg_effectivepixel_get(SANE_Int sensortype, SANE_Int resolution) { SANE_Int rst; switch(RTS_Debug->dev_model) { case UA4900: rst = ua4900_effectivepixel(resolution); break; case HP3800: case HPG2710: rst = hp3800_effectivepixel(resolution); break; case HP4370: case HPG3010: case HPG3110: rst = hp4370_effectivepixel(resolution); break; default: rst = hp3970_effectivepixel(sensortype, resolution); break; } return rst; } /** SEC: Gain and offset values ---------- */ static SANE_Int bq5550_gainoffset(SANE_Int usb, struct st_gain_offset *myreg) { struct st_reg { SANE_Int usb; struct st_gain_offset values; }; struct st_reg reg[] = { /* usb , {{edcg1 }, {edcg2 }, {odcg1 }, {odcg2 }, {pag }, {vgag1 }, {vgag2 }}} */ { USB20, {{264, 264, 264}, {0, 0, 0}, {262, 262, 262}, {0, 0, 0}, {3, 3, 3}, {27, 27, 27}, {4, 4, 4}}}, { USB11, {{264, 264, 264}, {0, 0, 0}, {262, 262, 262}, {0, 0, 0}, {3, 3, 3}, {27, 27, 27}, {4, 4, 4}}} }; SANE_Int rst = ERROR; if (myreg != NULL) { SANE_Int a; SANE_Int count = sizeof(reg) / sizeof(struct st_reg); for (a = 0; a < count; a++) { if (reg[a].usb == usb) { memcpy(myreg, ®[a].values, sizeof(struct st_gain_offset)); rst = OK; break; } } } return rst; } static SANE_Int hp3800_gainoffset(SANE_Int usb, struct st_gain_offset *myreg) { struct st_reg { SANE_Int usb; struct st_gain_offset values; }; struct st_reg reg[] = { /* usb , {{edcg1 }, {edcg2 }, {odcg1 }, {odcg2 }, {pag }, {vgag1 }, {vgag2 }}} */ { USB20, {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {2, 2, 2}, {4, 4, 4}, {4, 4, 4}}}, { USB11, {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {2, 2, 2}, {4, 4, 4}, {4, 4, 4}}} }; SANE_Int rst = ERROR; if (myreg != NULL) { SANE_Int a; SANE_Int count = sizeof(reg) / sizeof(struct st_reg); for (a = 0; a < count; a++) { if (reg[a].usb == usb) { memcpy(myreg, ®[a].values, sizeof(struct st_gain_offset)); rst = OK; break; } } } return rst; } static SANE_Int hp3970_gainoffset(SANE_Int usb, SANE_Int sensor, struct st_gain_offset *myreg) { struct st_reg { SANE_Int usb; SANE_Int sensor; struct st_gain_offset values; }; struct st_reg reg[] = { /* usb , sensor , {{edcg1 }, {edcg2 }, {odcg1 }, {odcg2 }, {pag }, {vgag1 }, {vgag2 }}} */ { USB20, CCD_SENSOR, {{280, 266, 286}, {0, 0, 0}, {280, 266, 286}, {0, 0, 0}, {3, 3, 3}, {7, 4, 4}, {7, 4, 4}}}, { USB11, CCD_SENSOR, {{280, 266, 286}, {0, 0, 0}, {280, 266, 286}, {0, 0, 0}, {3, 3, 3}, {7, 4, 4}, {7, 4, 4}}}, { USB20, CIS_SENSOR, {{280, 266, 286}, {0, 0, 0}, {280, 266, 286}, {0, 0, 0}, {3, 3, 3}, {7, 4, 4}, {7, 4, 4}}}, { USB11, CIS_SENSOR, {{280, 266, 286}, {0, 0, 0}, {280, 266, 286}, {0, 0, 0}, {3, 3, 3}, {7, 4, 4}, {7, 4, 4}}} }; SANE_Int rst = ERROR; if (myreg != NULL) { SANE_Int a; SANE_Int count = sizeof(reg) / sizeof(struct st_reg); for (a = 0; count < 4; a++) { if ((reg[a].usb == usb)&&(reg[a].sensor == sensor)) { memcpy(myreg, ®[a].values, sizeof(struct st_gain_offset)); rst = OK; break; } } } return rst; } static SANE_Int hp4370_gainoffset(SANE_Int usb, struct st_gain_offset *myreg) { struct st_reg { SANE_Int usb; struct st_gain_offset values; }; struct st_reg reg[] = { /* usb , {{edcg1 }, {edcg2 }, {odcg1 }, {odcg2 }, {pag }, {vgag1 }, {vgag2 }} */ { USB20, {{280, 266, 286}, {0, 0, 0}, {280, 266, 286}, {0, 0, 0}, {3, 3, 3}, {7, 4, 4}, {7, 4, 4}}}, { USB11, {{280, 266, 286}, {0, 0, 0}, {280, 266, 286}, {0, 0, 0}, {3, 3, 3}, {7, 4, 4}, {7, 4, 4}}} }; SANE_Int rst = ERROR; if (myreg != NULL) { SANE_Int a; SANE_Int count = sizeof(reg) / sizeof(struct st_reg); for (a = 0; a < count; a++) { if (reg[a].usb == usb) { memcpy(myreg, ®[a].values, sizeof(struct st_gain_offset)); rst = OK; break; } } } return rst; } static SANE_Int ua4900_gainoffset(SANE_Int usb, struct st_gain_offset *myreg) { struct st_reg { SANE_Int usb; struct st_gain_offset values; }; struct st_reg reg[] = { /* usb , {{edcg1 }, {edcg2 }, {odcg1 }, {odcg2 }, {pag }, {vgag1 }, {vgag2 }}} */ { USB20, {{321, 321, 321}, {0, 0, 0}, {321, 321, 321}, {0, 0, 0}, {0, 0, 0}, {24, 21, 19}, {8, 8, 8}}}, { USB11, {{321, 321, 321}, {0, 0, 0}, {321, 321, 321}, {0, 0, 0}, {0, 0, 0}, {16, 16, 16}, {4, 4, 4}}} }; SANE_Int rst = ERROR; if (myreg != NULL) { SANE_Int a; SANE_Int count = sizeof(reg) / sizeof(struct st_reg); for (a = 0; a < count; a++) { if (reg[a].usb == usb) { memcpy(myreg, ®[a].values, sizeof(struct st_gain_offset)); rst = OK; break; } } } return rst; } static SANE_Int cfg_gainoffset_get(SANE_Int sensortype, struct st_gain_offset *reg) { SANE_Int rst; switch(RTS_Debug->dev_model) { case BQ5550: rst = bq5550_gainoffset(RTS_Debug->usbtype, reg); break; case UA4900: rst = ua4900_gainoffset(RTS_Debug->usbtype, reg); break; case HP3800: case HPG2710: rst = hp3800_gainoffset(RTS_Debug->usbtype, reg); break; case HP4370: case HPG3010: case HPG3110: rst = hp4370_gainoffset(RTS_Debug->usbtype, reg); break; default: rst = hp3970_gainoffset(RTS_Debug->usbtype, sensortype, reg); break; } return rst; } /** SEC: Pulse-width modulation check stable ---------- */ static SANE_Int hp3800_checkstable(SANE_Int lamp, struct st_checkstable *check) { struct st_reg { SANE_Int lamp; struct st_checkstable values; }; struct st_reg reg[] = { /* lamp , { diff, interval, tottime } */ { 0 , { 100., 200, 10000}}, { FLB_LAMP, { 100., 200, 10000}}, { TMA_LAMP, { 100., 200, 10000}} }; SANE_Int rst = ERROR; SANE_Int a; SANE_Int count = sizeof(reg) / sizeof(struct st_reg); for (a = 0; a < count; a++) { if (reg[a].lamp == lamp) { memcpy(check, ®[a].values, sizeof(struct st_checkstable)); rst = OK; break; } } return rst; } static SANE_Int hp3970_checkstable(SANE_Int lamp, struct st_checkstable *check) { struct st_reg { SANE_Int lamp; struct st_checkstable values; }; struct st_reg reg[] = { /* lamp , { diff, interval, tottime } */ { 0 , {1000., 200, 5000}}, { FLB_LAMP, { 500., 200, 5000}}, { TMA_LAMP, { 500., 200, 5000}} }; SANE_Int rst = ERROR; SANE_Int a; SANE_Int count = sizeof(reg) / sizeof(struct st_reg); for (a = 0; a < count; a++) { if (reg[a].lamp == lamp) { memcpy(check, ®[a].values, sizeof(struct st_checkstable)); rst = OK; break; } } return rst; } static SANE_Int hp4370_checkstable(SANE_Int lamp, struct st_checkstable *check) { struct st_reg { SANE_Int lamp; struct st_checkstable values; }; struct st_reg reg[] = { /* lamp , { diff, interval, tottime } */ { 0 , { 100., 200, 5000}}, { FLB_LAMP, { 300., 200, 5000}}, { TMA_LAMP, { 0., 200, 25000}} }; SANE_Int rst = ERROR; SANE_Int a; SANE_Int count = sizeof(reg) / sizeof(struct st_reg); for (a = 0; a < count; a++) { if (reg[a].lamp == lamp) { memcpy(check, ®[a].values, sizeof(struct st_checkstable)); rst = OK; break; } } return rst; } static SANE_Int ua4900_checkstable(SANE_Int lamp, struct st_checkstable *check) { struct st_reg { SANE_Int lamp; struct st_checkstable values; }; struct st_reg reg[] = { /* lamp , { diff, interval, tottime } */ { 0 , { 100., 200, 5000}}, { FLB_LAMP, { 10., 200, 5000}}, { TMA_LAMP, { 10., 200, 25000}} }; SANE_Int rst = ERROR; SANE_Int a; SANE_Int count = sizeof(reg) / sizeof(struct st_reg); for (a = 0; a < count; a++) { if (reg[a].lamp == lamp) { memcpy(check, ®[a].values, sizeof(struct st_checkstable)); rst = OK; break; } } return rst; } static SANE_Int cfg_checkstable_get(SANE_Int lamp, struct st_checkstable *check) { SANE_Int rst; switch(RTS_Debug->dev_model) { case UA4900: rst = ua4900_checkstable(lamp, check); break; case HP3800: case HPG2710: rst = hp3800_checkstable(lamp, check); break; case HP4370: case HPG3010: case HPG3110: rst = hp4370_checkstable(lamp, check); break; default: rst = hp3970_checkstable(lamp, check); break; } return rst; } /** SEC: Fixed pulse-width modulation values ---------- */ static SANE_Int hp3800_fixedpwm(SANE_Int scantype, SANE_Int usb) { struct st_reg { SANE_Int usb; SANE_Int pwm[3]; }; struct st_reg reg[] = { /* usb , { ST_NORMAL, ST_TA, ST_NEG} */ { USB20, { 0, 0, 0}}, { USB11, { 0, 0, 0}} }; SANE_Int a, rst = 0x16; SANE_Int count = sizeof(reg) / sizeof(struct st_reg); for (a = 0; a < count; a++) { if (reg[a].usb == usb) { if ((scantype < ST_NORMAL)||(scantype > ST_NEG)) scantype = ST_NORMAL; rst = reg[a].pwm[scantype - 1]; break; } } return rst; } static SANE_Int hp3970_fixedpwm(SANE_Int scantype, SANE_Int usb, SANE_Int sensor) { struct st_reg { SANE_Int usb; SANE_Int sensor; SANE_Int pwm[3]; }; struct st_reg reg[] = { /* usb , sensor , { ST_NORMAL, ST_TA, ST_NEG} */ { USB20, CCD_SENSOR, { 22, 22, 22}}, { USB11, CCD_SENSOR, { 22, 22, 22}}, { USB20, CIS_SENSOR, { 22, 22, 22}}, { USB11, CIS_SENSOR, { 22, 22, 22}} }; SANE_Int a, rst = 0x16; SANE_Int count = sizeof(reg) / sizeof(struct st_reg); for (a = 0; a < count; a++) { if ((reg[a].usb == usb)&&(reg[a].sensor == sensor)) { if ((scantype < ST_NORMAL)||(scantype > ST_NEG)) scantype = ST_NORMAL; rst = reg[a].pwm[scantype - 1]; break; } } return rst; } static SANE_Int hp4370_fixedpwm(SANE_Int scantype, SANE_Int usb) { struct st_reg { SANE_Int usb; SANE_Int pwm[3]; }; struct st_reg reg[] = { /* usb , { ST_NORMAL, ST_TA, ST_NEG} */ { USB20, { 20, 28, 28}}, { USB11, { 20, 28, 28}} }; SANE_Int a, rst = 0x16; SANE_Int count = sizeof(reg) / sizeof(struct st_reg); for (a = 0; a < count; a++) { if (reg[a].usb == usb) { if ((scantype < ST_NORMAL)||(scantype > ST_NEG)) scantype = ST_NORMAL; rst = reg[a].pwm[scantype - 1]; break; } } return rst; } static SANE_Int ua4900_fixedpwm(SANE_Int scantype, SANE_Int usb) { struct st_reg { SANE_Int usb; SANE_Int pwm[3]; }; struct st_reg reg[] = { /* usb , { ST_NORMAL, ST_TA, ST_NEG} */ { USB20, { 20, 28, 28}}, { USB11, { 20, 28, 28}} }; SANE_Int a, rst = 0x16; SANE_Int count = sizeof(reg) / sizeof(struct st_reg); for (a = 0; a < count; a++) { if (reg[a].usb == usb) { if ((scantype < ST_NORMAL)||(scantype > ST_NEG)) scantype = ST_NORMAL; rst = reg[a].pwm[scantype - 1]; break; } } return rst; } static SANE_Int cfg_fixedpwm_get(SANE_Int sensortype, SANE_Int scantype) { SANE_Int rst; switch(RTS_Debug->dev_model) { case UA4900: rst = ua4900_fixedpwm(scantype, RTS_Debug->usbtype); break; case HP3800: case HPG2710: rst = hp3800_fixedpwm(scantype, RTS_Debug->usbtype); break; case HP4370: case HPG3010: case HPG3110: rst = hp4370_fixedpwm(scantype, RTS_Debug->usbtype); break; default: rst = hp3970_fixedpwm(scantype, RTS_Debug->usbtype, sensortype); break; } return rst; } /** SEC: Fixed reference positions ---------- */ static void cfg_vrefs_get(SANE_Int sensortype, SANE_Int res, SANE_Int *ser, SANE_Int *ler) { switch(RTS_Debug->dev_model) { case HP3800: case HPG2710: hp3800_vrefs(res, ser, ler); break; case HP4370: case HPG3010: case HPG3110: hp4370_vrefs(res, ser, ler); break; default: hp3970_vrefs(RTS_Debug->usbtype, sensortype, res, ser, ler); break; } } static void hp3800_vrefs(SANE_Int res, SANE_Int *ser, SANE_Int *ler) { struct st_reg { SANE_Int resolution; SANE_Int vref[2]; }; struct st_reg reg[] = { /* res, { ser, ler} */ { 150, { 25, 50}}, { 300, { 50, 101}}, { 600, { 102, 202}}, { 1200, { 204, 404}}, { 2400, { 408, 808}} }; if ((ser != NULL)&&(ler != NULL)) { SANE_Int a; SANE_Int count = sizeof(reg) / sizeof(struct st_reg); /* values by default */ *ser = *ler = 0; for (a = 0; a < count; a++) { if (reg[a].resolution == res) { *ser = reg[a].vref[0]; *ler = reg[a].vref[1]; break; } } } } static void hp3970_vrefs(SANE_Int usb, SANE_Int sensor, SANE_Int res, SANE_Int *ser, SANE_Int *ler) { struct st_reg { SANE_Int usb; SANE_Int sensor; SANE_Int resolution; SANE_Int vref[2]; }; /* I think these references should be the same in all usb versions and in all sensor types but windows driver has some different values in some cases. */ struct st_reg reg[] = { /* usb , sensor , res, { ser, ler} */ { USB20, CCD_SENSOR, 100, { 28, 60}}, { USB20, CCD_SENSOR, 200, { 37, 117}}, { USB20, CCD_SENSOR, 300, { 52, 162}}, { USB20, CCD_SENSOR, 600, { 103, 344}}, { USB20, CCD_SENSOR, 1200, { 156, 660}}, { USB20, CCD_SENSOR, 2400, { 309, 1262}}, /* usb , sensor , res, { ser, ler} */ { USB11, CCD_SENSOR, 100, { 28, 60}}, { USB11, CCD_SENSOR, 200, { 37, 117}}, { USB11, CCD_SENSOR, 300, { 52, 162}}, { USB11, CCD_SENSOR, 600, { 103, 344}}, { USB11, CCD_SENSOR, 1200, { 156, 660}}, { USB11, CCD_SENSOR, 2400, { 309, 1262}}, /* usb , sensor , res, { ser, ler} */ { USB20, CIS_SENSOR, 100, { 15, 60}}, { USB20, CIS_SENSOR, 200, { 39, 117}}, { USB20, CIS_SENSOR, 300, { 56, 161}}, { USB20, CIS_SENSOR, 600, { 108, 342}}, { USB20, CIS_SENSOR, 1200, { 221, 642}}, { USB20, CIS_SENSOR, 2400, { 407, 1285}}, /* usb , sensor , res, { ser, ler} */ { USB11, CIS_SENSOR, 100, { 15, 60}}, { USB11, CIS_SENSOR, 200, { 39, 117}}, { USB11, CIS_SENSOR, 300, { 56, 161}}, { USB11, CIS_SENSOR, 600, { 108, 342}}, { USB11, CIS_SENSOR, 1200, { 221, 642}}, { USB11, CIS_SENSOR, 2400, { 407, 1285}} }; if ((ser != NULL)&&(ler != NULL)) { SANE_Int a; SANE_Int count = sizeof(reg) / sizeof(struct st_reg); /* values by default */ *ser = *ler = 0; for (a = 0; a < count; a++) { if ((reg[a].usb == usb)&&(reg[a].sensor == sensor)&&(reg[a].resolution == res)) { *ser = reg[a].vref[0]; *ler = reg[a].vref[1]; break; } } } } static void hp4370_vrefs(SANE_Int res, SANE_Int *ser, SANE_Int *ler) { struct st_reg { SANE_Int resolution; SANE_Int vref[2]; }; struct st_reg reg[] = { /* res, { ser, ler} */ { 150, { 31, 81}}, { 300, { 61, 162}}, { 600, { 122, 324}}, { 1200, { 244, 648}}, { 2400, { 488, 1256}}, { 4800, { 976, 2512}} }; if ((ser != NULL)&&(ler != NULL)) { SANE_Int a; SANE_Int count = sizeof(reg) / sizeof(struct st_reg); /* values by default */ *ser = *ler = 0; for (a = 0; a < count; a++) { if (reg[a].resolution == res) { *ser = reg[a].vref[0]; *ler = reg[a].vref[1]; break; } } } } /** SEC: Motor movements ---------- */ static SANE_Int cfg_motormove_get(SANE_Int sensortype, SANE_Int item, struct st_motormove *reg) { SANE_Int rst = ERROR; switch(RTS_Debug->dev_model) { case BQ5550: rst = bq5550_motormove(item, reg); break; case HP3800: case HPG2710: rst = hp3800_motormove(item, reg); break; default: rst = hp3970_motormove(RTS_Debug->usbtype, sensortype, item, reg); break; } return rst; } static SANE_Int bq5550_motormove(SANE_Int item, struct st_motormove *reg) { SANE_Int rst = ERROR; /* data is the same in all usb types and sensors so those args aren't needed */ if (reg != NULL) { struct st_motormove mv[] = { /* systemclock, ctpc, steptype , motorcurve } */ { 0x05 , 4059, STT_HALF , 0 }, { 0x02 , 1200, STT_QUART, -1 } }; rst = OK; if ((item < 2)&&(item > -1)) memcpy(reg, &mv[item], sizeof(struct st_motormove)); else rst = ERROR; } return rst; } static SANE_Int hp3800_motormove(SANE_Int item, struct st_motormove *reg) { SANE_Int rst = ERROR; /* data is the same in all usb types and sensors so those args aren't needed */ if (reg != NULL) { struct st_motormove mv[] = { /* systemclock, ctpc, steptype, motorcurve } */ { 0x04 , 1991, STT_HALF, 2 }, { 0x02 , 1991, STT_HALF, -1 } }; rst = OK; if ((item < 2)&&(item > -1)) memcpy(reg, &mv[item], sizeof(struct st_motormove)); else rst = ERROR; } return rst; } static SANE_Int hp3970_motormove(SANE_Int usb, SANE_Int ccd, SANE_Int item, struct st_motormove *reg) { SANE_Int rst = ERROR; struct st_mtmove { SANE_Int usbtype; SANE_Int sensor; struct st_motormove move; }; if (reg != NULL) { struct st_mtmove mv[] = { /* usb, sensor , {systemclock, ctpc, steptype, motorcurve } */ {USB20, CCD_SENSOR, {0x02 , 6431, STT_HALF, 1 }}, {USB20, CCD_SENSOR, {0x02 , 2000, STT_HALF, -1 }}, {USB20, CIS_SENSOR, {0x02 , 6431, STT_HALF, 1 }}, {USB20, CIS_SENSOR, {0x02 , 2000, STT_HALF, -1 }}, {USB11, CCD_SENSOR, {0x02 , 6431, STT_HALF, 1 }}, {USB11, CCD_SENSOR, {0x02 , 2000, STT_HALF, -1 }}, {USB11, CIS_SENSOR, {0x02 , 6431, STT_HALF, 1 }}, {USB11, CIS_SENSOR, {0x02 , 2000, STT_HALF, -1 }} }; if (item < 2) { SANE_Int a, count = 0; SANE_Int total = sizeof(mv) / sizeof(struct st_mtmove); for (a = 0; a < total; a++) { if ((mv[a].usbtype == usb)&&(mv[a].sensor == ccd)) { if (item == count) { memcpy(reg, &mv[a].move, sizeof(struct st_motormove)); rst = OK; break; } else count++; } } } } return rst; } /** SEC: Scanning modes ---------- */ static SANE_Int cfg_scanmode_get(SANE_Int sensortype, SANE_Int sm, struct st_scanmode *mymode) { SANE_Int rst = ERROR; switch(RTS_Debug->dev_model) { case BQ5550: rst = bq5550_scanmodes(RTS_Debug->usbtype, sm, mymode); break; case UA4900: rst = ua4900_scanmodes(RTS_Debug->usbtype, sm, mymode); break; case HP3800: case HPG2710: rst = hp3800_scanmodes(RTS_Debug->usbtype, sm, mymode); break; case HP4370: case HPG3010: case HPG3110: rst = hp4370_scanmodes(RTS_Debug->usbtype, sm, mymode); break; default: /* hp3970 hp4070 */ rst = hp3970_scanmodes(RTS_Debug->usbtype, sensortype, sm, mymode); break; } return rst; } static SANE_Int hp3970_scanmodes(SANE_Int usb, SANE_Int ccd, SANE_Int sm, struct st_scanmode *mymode) { struct st_modes { SANE_Int usb; SANE_Int sensor; struct st_scanmode mode; }; struct st_modes reg[] = { /* usb, sensor , {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB20, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x04 , 24499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x05 , 14667, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 600, 0x02 , 2 , PIXEL_RATE, 0x04 , 5499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 300, 0x03 , 3 , PIXEL_RATE, 0x04 , 2751, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 200, 0x04 , 4 , PIXEL_RATE, 0x04 , 2255, 1856 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 100, 0x04 , 4 , PIXEL_RATE, 0x04 , 2255, 1856 , STT_HALF, 0x02 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x04 , 24499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x05 , 14667, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 600, 0x07 , 2 , LINE_RATE , 0x04 , 5499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 300, 0x08 , 3 , LINE_RATE , 0x04 , 2751, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 200, 0x09 , 4 , LINE_RATE , 0x04 , 2255, 1856 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 100, 0x09 , 4 , LINE_RATE , 0x04 , 2255, 1856 , STT_HALF, 0x02 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, sensor , {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB20, CCD_SENSOR, {ST_TA , CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x04 , 25599, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_TA , CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x03 , 10899, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_TA , CM_COLOR , 600, 0x02 , -1 , PIXEL_RATE, 0x01 , 5487, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_TA , CM_COLOR , 300, 0x03 , 3 , PIXEL_RATE, 0x04 , 8351, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_TA , CM_COLOR , 200, 0x04 , 4 , PIXEL_RATE, 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_TA , CM_COLOR , 100, 0x04 , 4 , PIXEL_RATE, 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_TA , CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x04 , 25599, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_TA , CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x03 , 10899, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_TA , CM_GRAY , 600, 0x07 , -1 , LINE_RATE , 0x01 , 5487, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_TA , CM_GRAY , 300, 0x08 , 3 , LINE_RATE , 0x04 , 8351, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_TA , CM_GRAY , 200, 0x09 , 4 , LINE_RATE , 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_TA , CM_GRAY , 100, 0x09 , 4 , LINE_RATE , 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, sensor , {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB20, CCD_SENSOR, {ST_NEG , CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x02 , 76799, 256 , STT_FULL, 0x00 , {25599, 51199, 0}, {25599, 25599, 76799}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_NEG , CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x03 , 36467, 256 , STT_FULL, 0x00 , {12155, 24311, 0}, {12155, 12155, 36467}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_NEG , CM_COLOR , 600, 0x02 , -1 , PIXEL_RATE, 0x02 , 16463, 256 , STT_FULL, 0x00 , { 5487, 10975, 0}, { 5487, 5487, 16463}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_NEG , CM_COLOR , 300, 0x03 , -1 , PIXEL_RATE, 0x02 , 8351, 256 , STT_HALF, 0x00 , { 2783, 5567, 0}, { 2783, 2783, 8351}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_NEG , CM_COLOR , 200, 0x04 , -1 , PIXEL_RATE, 0x02 , 6191, 256 , STT_HALF, 0x00 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_NEG , CM_COLOR , 100, 0x04 , -1 , PIXEL_RATE, 0x02 , 6191, 256 , STT_HALF, 0x02 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_NEG , CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x02 , 76799, 256 , STT_FULL, 0x00 , {25599, 51199, 0}, {25599, 25599, 76799}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_NEG , CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x03 , 36467, 256 , STT_FULL, 0x00 , {12155, 24311, 0}, {12155, 12155, 36467}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_NEG , CM_GRAY , 600, 0x07 , -1 , LINE_RATE , 0x02 , 16463, 256 , STT_FULL, 0x00 , { 5487, 10975, 0}, { 5487, 5487, 16463}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_NEG , CM_GRAY , 300, 0x08 , -1 , LINE_RATE , 0x02 , 8351, 256 , STT_HALF, 0x00 , { 2783, 5567, 0}, { 2783, 2783, 8351}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_NEG , CM_GRAY , 200, 0x09 , -1 , LINE_RATE , 0x02 , 6191, 256 , STT_HALF, 0x00 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CCD_SENSOR, {ST_NEG , CM_GRAY , 100, 0x09 , -1 , LINE_RATE , 0x02 , 6191, 256 , STT_HALF, 0x02 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, sensor , {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB20, CIS_SENSOR, {ST_NORMAL, CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x04 , 24499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_NORMAL, CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x05 , 14667, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_NORMAL, CM_COLOR , 600, 0x02 , 2 , PIXEL_RATE, 0x04 , 5499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_NORMAL, CM_COLOR , 300, 0x03 , 3 , PIXEL_RATE, 0x04 , 2751, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_NORMAL, CM_COLOR , 200, 0x04 , 4 , PIXEL_RATE, 0x04 , 2255, 1856 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_NORMAL, CM_COLOR , 100, 0x04 , 4 , PIXEL_RATE, 0x04 , 2255, 1856 , STT_HALF, 0x02 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_NORMAL, CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x04 , 24499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_NORMAL, CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x05 , 14667, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_NORMAL, CM_GRAY , 600, 0x07 , 2 , LINE_RATE , 0x04 , 5499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_NORMAL, CM_GRAY , 300, 0x08 , 3 , LINE_RATE , 0x04 , 2751, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_NORMAL, CM_GRAY , 200, 0x09 , 4 , LINE_RATE , 0x04 , 2255, 1856 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_NORMAL, CM_GRAY , 100, 0x09 , 4 , LINE_RATE , 0x04 , 2255, 1856 , STT_HALF, 0x02 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, sensor , {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB20, CIS_SENSOR, {ST_TA , CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x04 , 25599, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_TA , CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x03 , 10899, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_TA , CM_COLOR , 600, 0x02 , -1 , PIXEL_RATE, 0x01 , 5487, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_TA , CM_COLOR , 300, 0x03 , 3 , PIXEL_RATE, 0x04 , 8351, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_TA , CM_COLOR , 200, 0x04 , 4 , PIXEL_RATE, 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_TA , CM_COLOR , 100, 0x04 , 4 , PIXEL_RATE, 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_TA , CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x04 , 25599, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_TA , CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x03 , 10899, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_TA , CM_GRAY , 600, 0x07 , -1 , LINE_RATE , 0x01 , 5487, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_TA , CM_GRAY , 300, 0x08 , 3 , LINE_RATE , 0x04 , 8351, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_TA , CM_GRAY , 200, 0x09 , 4 , LINE_RATE , 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_TA , CM_GRAY , 100, 0x09 , 4 , LINE_RATE , 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, sensor , {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB20, CIS_SENSOR, {ST_NEG , CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x02 , 76799, 256 , STT_FULL, 0x00 , {25599, 51199, 0}, {25599, 25599, 76799}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_NEG , CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x03 , 36467, 256 , STT_FULL, 0x00 , {12155, 24311, 0}, {12155, 12155, 36467}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_NEG , CM_COLOR , 600, 0x02 , -1 , PIXEL_RATE, 0x02 , 16463, 256 , STT_FULL, 0x00 , { 5487, 10975, 0}, { 5487, 5487, 16463}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_NEG , CM_COLOR , 300, 0x03 , -1 , PIXEL_RATE, 0x02 , 8351, 256 , STT_HALF, 0x00 , { 2783, 5567, 0}, { 2783, 2783, 8351}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_NEG , CM_COLOR , 200, 0x04 , -1 , PIXEL_RATE, 0x02 , 6191, 256 , STT_HALF, 0x00 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_NEG , CM_COLOR , 100, 0x04 , -1 , PIXEL_RATE, 0x02 , 6191, 256 , STT_HALF, 0x02 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_NEG , CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x02 , 76799, 256 , STT_FULL, 0x00 , {25599, 51199, 0}, {25599, 25599, 76799}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_NEG , CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x03 , 36467, 256 , STT_FULL, 0x00 , {12155, 24311, 0}, {12155, 12155, 36467}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_NEG , CM_GRAY , 600, 0x07 , -1 , LINE_RATE , 0x02 , 16463, 256 , STT_FULL, 0x00 , { 5487, 10975, 0}, { 5487, 5487, 16463}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_NEG , CM_GRAY , 300, 0x08 , -1 , LINE_RATE , 0x02 , 8351, 256 , STT_HALF, 0x00 , { 2783, 5567, 0}, { 2783, 2783, 8351}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_NEG , CM_GRAY , 200, 0x09 , -1 , LINE_RATE , 0x02 , 6191, 256 , STT_HALF, 0x00 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, CIS_SENSOR, {ST_NEG , CM_GRAY , 100, 0x09 , -1 , LINE_RATE , 0x02 , 6191, 256 , STT_HALF, 0x02 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, sensor , {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB11, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x03 , 21499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x04 , 14667, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 600, 0x02 , -1 , PIXEL_RATE, 0x04 , 21999, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 300, 0x03 , 3 , PIXEL_RATE, 0x04 , 10727, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 200, 0x04 , 4 , PIXEL_RATE, 0x04 , 5591, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 100, 0x04 , 4 , PIXEL_RATE, 0x04 , 2255, 1856 , STT_HALF, 0x02 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x02 , 21499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x04 , 14667, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 600, 0x07 , 2 , LINE_RATE , 0x04 , 6999, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 300, 0x08 , 3 , LINE_RATE , 0x04 , 4671, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 200, 0x09 , 4 , LINE_RATE , 0x04 , 2255, 1856 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 100, 0x09 , 4 , LINE_RATE , 0x04 , 2255, 1856 , STT_HALF, 0x02 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, sensor , {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB11, CCD_SENSOR, {ST_TA , CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x04 , 25599, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_TA , CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x03 , 10899, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_TA , CM_COLOR , 600, 0x02 , -1 , PIXEL_RATE, 0x01 , 5487, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_TA , CM_COLOR , 300, 0x03 , 3 , PIXEL_RATE, 0x04 , 8351, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_TA , CM_COLOR , 200, 0x04 , 4 , PIXEL_RATE, 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_TA , CM_COLOR , 100, 0x04 , 4 , PIXEL_RATE, 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_TA , CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x04 , 25599, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_TA , CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x03 , 10899, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_TA , CM_GRAY , 600, 0x07 , -1 , LINE_RATE , 0x01 , 5487, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_TA , CM_GRAY , 300, 0x08 , 3 , LINE_RATE , 0x04 , 8351, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_TA , CM_GRAY , 200, 0x09 , 4 , LINE_RATE , 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_TA , CM_GRAY , 100, 0x09 , 4 , LINE_RATE , 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, sensor , {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB11, CCD_SENSOR, {ST_NEG , CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x02 , 76799, 256 , STT_FULL, 0x00 , {25599, 51199, 0}, {25599, 25599, 76799}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NEG , CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x03 , 36467, 256 , STT_FULL, 0x00 , {12155, 24311, 0}, {12155, 12155, 36467}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NEG , CM_COLOR , 600, 0x02 , -1 , PIXEL_RATE, 0x02 , 16463, 256 , STT_FULL, 0x00 , { 5487, 10975, 0}, { 5487, 5487, 16463}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NEG , CM_COLOR , 300, 0x03 , -1 , PIXEL_RATE, 0x02 , 8351, 256 , STT_HALF, 0x00 , { 2783, 5567, 0}, { 2783, 2783, 8351}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NEG , CM_COLOR , 200, 0x04 , -1 , PIXEL_RATE, 0x02 , 6191, 256 , STT_HALF, 0x00 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NEG , CM_COLOR , 100, 0x04 , -1 , PIXEL_RATE, 0x02 , 6191, 256 , STT_HALF, 0x02 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NEG , CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x02 , 76799, 256 , STT_FULL, 0x00 , {25599, 51199, 0}, {25599, 25599, 76799}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NEG , CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x03 , 36467, 256 , STT_FULL, 0x00 , {12155, 24311, 0}, {12155, 12155, 36467}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NEG , CM_GRAY , 600, 0x07 , -1 , LINE_RATE , 0x02 , 16463, 256 , STT_FULL, 0x00 , { 5487, 10975, 0}, { 5487, 5487, 16463}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NEG , CM_GRAY , 300, 0x08 , -1 , LINE_RATE , 0x02 , 8351, 256 , STT_HALF, 0x00 , { 2783, 5567, 0}, { 2783, 2783, 8351}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NEG , CM_GRAY , 200, 0x09 , -1 , LINE_RATE , 0x02 , 6191, 256 , STT_HALF, 0x00 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NEG , CM_GRAY , 100, 0x09 , -1 , LINE_RATE , 0x02 , 6191, 256 , STT_HALF, 0x02 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, sensor , {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB11, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x02 , 21499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x03 , 14667, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 600, 0x02 , -1 , PIXEL_RATE, 0x04 , 21999, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 300, 0x03 , 3 , PIXEL_RATE, 0x04 , 10727, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 200, 0x04 , 4 , PIXEL_RATE, 0x04 , 5591, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NORMAL, CM_COLOR , 100, 0x04 , 4 , PIXEL_RATE, 0x04 , 2255, 1856 , STT_HALF, 0x02 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x02 , 21499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x04 , 14667, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 600, 0x07 , 2 , LINE_RATE , 0x04 , 6999, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 300, 0x08 , 3 , LINE_RATE , 0x04 , 4671, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 200, 0x09 , 4 , LINE_RATE , 0x04 , 2255, 1856 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NORMAL, CM_GRAY , 100, 0x09 , 4 , LINE_RATE , 0x04 , 2255, 1856 , STT_HALF, 0x02 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, sensor , {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB11, CCD_SENSOR, {ST_TA , CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x04 , 25599, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_TA , CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x03 , 10899, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_TA , CM_COLOR , 600, 0x02 , -1 , PIXEL_RATE, 0x01 , 5487, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_TA , CM_COLOR , 300, 0x03 , 3 , PIXEL_RATE, 0x04 , 8351, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_TA , CM_COLOR , 200, 0x04 , 4 , PIXEL_RATE, 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_TA , CM_COLOR , 100, 0x04 , 4 , PIXEL_RATE, 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_TA , CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x04 , 25599, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_TA , CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x03 , 10899, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_TA , CM_GRAY , 600, 0x07 , -1 , LINE_RATE , 0x01 , 5487, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_TA , CM_GRAY , 300, 0x08 , 3 , LINE_RATE , 0x04 , 8351, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_TA , CM_GRAY , 200, 0x09 , 4 , LINE_RATE , 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_TA , CM_GRAY , 100, 0x09 , 4 , LINE_RATE , 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, sensor , {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB11, CCD_SENSOR, {ST_NEG , CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x02 , 76799, 256 , STT_FULL, 0x00 , {25599, 51199, 0}, {25599, 25599, 76799}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NEG , CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x03 , 36467, 256 , STT_FULL, 0x00 , {12155, 24311, 0}, {12155, 12155, 36467}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NEG , CM_COLOR , 600, 0x02 , -1 , PIXEL_RATE, 0x02 , 16463, 256 , STT_FULL, 0x00 , { 5487, 10975, 0}, { 5487, 5487, 16463}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NEG , CM_COLOR , 300, 0x03 , -1 , PIXEL_RATE, 0x02 , 8351, 256 , STT_HALF, 0x00 , { 2783, 5567, 0}, { 2783, 2783, 8351}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NEG , CM_COLOR , 200, 0x04 , -1 , PIXEL_RATE, 0x02 , 6191, 256 , STT_HALF, 0x00 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NEG , CM_COLOR , 100, 0x04 , -1 , PIXEL_RATE, 0x02 , 6191, 256 , STT_HALF, 0x02 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CIS_SENSOR, {ST_NEG , CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x02 , 76799, 256 , STT_FULL, 0x00 , {25599, 51199, 0}, {25599, 25599, 76799}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CIS_SENSOR, {ST_NEG , CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x03 , 36467, 256 , STT_FULL, 0x00 , {12155, 24311, 0}, {12155, 12155, 36467}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NEG , CM_GRAY , 600, 0x07 , -1 , LINE_RATE , 0x02 , 16463, 256 , STT_FULL, 0x00 , { 5487, 10975, 0}, { 5487, 5487, 16463}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NEG , CM_GRAY , 300, 0x08 , -1 , LINE_RATE , 0x02 , 8351, 256 , STT_HALF, 0x00 , { 2783, 5567, 0}, { 2783, 2783, 8351}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NEG , CM_GRAY , 200, 0x09 , -1 , LINE_RATE , 0x02 , 6191, 256 , STT_HALF, 0x00 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, CCD_SENSOR, {ST_NEG , CM_GRAY , 100, 0x09 , -1 , LINE_RATE , 0x02 , 6191, 256 , STT_HALF, 0x02 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }} }; SANE_Int rst = ERROR; if (mymode != NULL) { SANE_Int a; SANE_Int total = sizeof(reg) / sizeof(struct st_modes); SANE_Int count = 0; struct st_modes *md; for (a = 0; a < total; a++) { md = ®[a]; if ((md->usb == usb)&&(md->sensor == ccd)) { if (count == sm) { memcpy(mymode, &md->mode, sizeof(struct st_scanmode)); rst = OK; break; } count++; } } } return rst; } static SANE_Int hp4370_scanmodes(SANE_Int usb, SANE_Int sm, struct st_scanmode *mymode) { struct st_modes { SANE_Int usb; struct st_scanmode mode; }; struct st_modes reg[] = { /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB20, {ST_NORMAL, CM_COLOR , 4800, 0x00 , -1 , PIXEL_RATE, 0x05 , 47799, 256 , STT_HALF, 0x00 , {23899, 23899, 23899}, {23899, 23899, 23899}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_COLOR , 2400, 0x01 , -1 , PIXEL_RATE, 0x05 , 31849, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_COLOR , 1200, 0x02 , -1 , PIXEL_RATE, 0x05 , 15999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_COLOR , 600, 0x03 , 2 , PIXEL_RATE, 0x05 , 7999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_COLOR , 300, 0x04 , 3 , PIXEL_RATE, 0x04 , 2799, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_COLOR , 150, 0x04 , 4 , PIXEL_RATE, 0x04 , 2799, 1856 , STT_HALF, 0x01 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_GRAY , 4800, 0x05 , -1 , LINE_RATE , 0x05 , 47799, 256 , STT_HALF, 0x00 , {23899, 23899, 23899}, {23899, 23899, 23899}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_GRAY , 2400, 0x06 , -1 , LINE_RATE , 0x05 , 31849, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_GRAY , 1200, 0x07 , -1 , LINE_RATE , 0x05 , 15999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_GRAY , 600, 0x08 , 2 , LINE_RATE , 0x05 , 7999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_GRAY , 300, 0x09 , 3 , LINE_RATE , 0x04 , 2799, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_GRAY , 150, 0x09 , 4 , LINE_RATE , 0x04 , 2799, 1856 , STT_HALF, 0x01 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB20, {ST_TA , CM_COLOR , 4800, 0x0A , -1 , PIXEL_RATE, 0x05 , 53999, 256 , STT_HALF, 0x00 , {26999, 26999, 26999}, {26999, 26999, 26999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_COLOR , 2400, 0x01 , -1 , PIXEL_RATE, 0x05 , 35999, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_COLOR , 1200, 0x02 , -1 , PIXEL_RATE, 0x05 , 17999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_COLOR , 600, 0x03 , 2 , PIXEL_RATE, 0x05 , 8999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_COLOR , 300, 0x04 , 3 , PIXEL_RATE, 0x04 , 2959, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_COLOR , 150, 0x04 , 4 , PIXEL_RATE, 0x04 , 2959, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_GRAY , 4800, 0x0B , -1 , LINE_RATE , 0x05 , 53999, 256 , STT_HALF, 0x00 , {26999, 26999, 26999}, {26999, 26999, 26999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_GRAY , 2400, 0x06 , -1 , LINE_RATE , 0x05 , 35999, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_GRAY , 1200, 0x07 , -1 , LINE_RATE , 0x05 , 17999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_GRAY , 600, 0x08 , 3 , LINE_RATE , 0x05 , 8999, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_GRAY , 300, 0x09 , 4 , LINE_RATE , 0x04 , 2959, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_GRAY , 150, 0x09 , 4 , LINE_RATE , 0x05 , 2959, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB20, {ST_NEG , CM_COLOR , 4800, 0x0C , -1 , PIXEL_RATE, 0x05 , 60599, 256 , STT_HALF, 0x00 , {30299, 30299, 30299}, {30299, 30299, 30299}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_COLOR , 2400, 0x01 , -1 , PIXEL_RATE, 0x05 , 145799, 256 , STT_FULL, 0x00 , {48599, 97199, 0}, {48599, 48599, 145799}, 0 , 1 , -1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_COLOR , 1200, 0x02 , -1 , PIXEL_RATE, 0x05 , 89999, 256 , STT_FULL, 0x00 , {29999, 59999, 0}, {29999, 29999, 89999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_COLOR , 600, 0x03 , -1 , PIXEL_RATE, 0x05 , 45999, 256 , STT_HALF, 0x00 , {15333, 30666, 0}, {15333, 15333, 45999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_COLOR , 300, 0x04 , -1 , PIXEL_RATE, 0x04 , 14879, 256 , STT_HALF, 0x00 , { 4959, 9919, 0}, { 4959, 4959, 14879}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_COLOR , 150, 0x04 , 3 , PIXEL_RATE, 0x04 , 14879, 256 , STT_HALF, 0x00 , { 4959, 9919, 0}, { 4959, 4959, 14879}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_GRAY , 4800, 0x0D , -1 , LINE_RATE , 0x05 , 60599, 256 , STT_FULL, 0x00 , {30299, 30299, 30299}, {30299, 30299, 30299}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_GRAY , 2400, 0x06 , -1 , LINE_RATE , 0x05 ,145799, 256 , STT_FULL, 0x00 , {48599, 97199, 0}, {48599, 48599, 145799}, 0 , 1 , -1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_GRAY , 1200, 0x07 , -1 , LINE_RATE , 0x05 , 89999, 256 , STT_FULL, 0x00 , {29999, 59999, 0}, {29999, 29999, 89999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_GRAY , 600, 0x08 , -1 , LINE_RATE , 0x05 , 45999, 256 , STT_HALF, 0x00 , {15333, 30666, 0}, {15333, 15333, 45999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_GRAY , 300, 0x09 , -1 , LINE_RATE , 0x04 , 14879, 256 , STT_HALF, 0x00 , { 4959, 9919, 0}, { 4959, 4959, 14879}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_GRAY , 150, 0x09 , 3 , LINE_RATE , 0x04 , 14879, 256 , STT_HALF, 0x00 , { 4959, 9919, 0}, { 4959, 4959, 14879}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB11, {ST_NORMAL, CM_COLOR , 4800, 0x00 , -1 , PIXEL_RATE, 0x05 , 47799, 64 , STT_HALF, 0x00 , {23899, 23899, 23899}, {23899, 23899, 23899}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NORMAL, CM_COLOR , 2400, 0x01 , -1 , PIXEL_RATE, 0x05 , 31849, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NORMAL, CM_COLOR , 1200, 0x02 , -1 , PIXEL_RATE, 0x05 , 15999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NORMAL, CM_COLOR , 600, 0x03 , 2 , PIXEL_RATE, 0x05 , 7999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NORMAL, CM_COLOR , 300, 0x04 , 3 , PIXEL_RATE, 0x04 , 2799, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NORMAL, CM_COLOR , 150, 0x04 , 4 , PIXEL_RATE, 0x04 , 2799, 1856 , STT_HALF, 0x01 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NORMAL, CM_GRAY , 4800, 0x05 , -1 , LINE_RATE , 0x05 , 47799, 64 , STT_HALF, 0x00 , {23899, 23899, 23899}, {23899, 23899, 23899}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NORMAL, CM_GRAY , 2400, 0x06 , -1 , LINE_RATE , 0x05 , 31849, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NORMAL, CM_GRAY , 1200, 0x07 , -1 , LINE_RATE , 0x05 , 15999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NORMAL, CM_GRAY , 600, 0x08 , 2 , LINE_RATE , 0x05 , 7999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NORMAL, CM_GRAY , 300, 0x09 , 3 , LINE_RATE , 0x04 , 2799, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NORMAL, CM_GRAY , 150, 0x09 , 4 , LINE_RATE , 0x04 , 2799, 1856 , STT_HALF, 0x01 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB11, {ST_TA , CM_COLOR , 4800, 0x0A , -1 , PIXEL_RATE, 0x05 , 53999, 64 , STT_HALF, 0x00 , {26999, 26999, 26999}, {26999, 26999, 26999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_TA , CM_COLOR , 2400, 0x01 , -1 , PIXEL_RATE, 0x05 , 35999, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_TA , CM_COLOR , 1200, 0x02 , -1 , PIXEL_RATE, 0x05 , 17999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_TA , CM_COLOR , 600, 0x03 , 2 , PIXEL_RATE, 0x05 , 8999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_TA , CM_COLOR , 300, 0x04 , 3 , PIXEL_RATE, 0x04 , 2959, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_TA , CM_COLOR , 150, 0x04 , 4 , PIXEL_RATE, 0x04 , 2959, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_TA , CM_GRAY , 4800, 0x0B , -1 , LINE_RATE , 0x05 , 53999, 64 , STT_HALF, 0x00 , {26999, 26999, 26999}, {26999, 26999, 26999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_TA , CM_GRAY , 2400, 0x06 , -1 , LINE_RATE , 0x05 , 35999, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_TA , CM_GRAY , 1200, 0x07 , -1 , LINE_RATE , 0x05 , 17999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_TA , CM_GRAY , 600, 0x08 , 3 , LINE_RATE , 0x05 , 8999, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_TA , CM_GRAY , 300, 0x09 , 4 , LINE_RATE , 0x04 , 2959, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_TA , CM_GRAY , 150, 0x09 , 4 , LINE_RATE , 0x05 , 2959, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB11, {ST_NEG , CM_COLOR , 4800, 0x0C , -1 , PIXEL_RATE, 0x05 , 60599, 64 , STT_HALF, 0x00 , {30299, 30299, 30299}, {30299, 30299, 30299}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NEG , CM_COLOR , 2400, 0x01 , -1 , PIXEL_RATE, 0x05 ,145799, 256 , STT_FULL, 0x00 , {48599, 97199, 0}, {48599, 48599,145799}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NEG , CM_COLOR , 1200, 0x02 , -1 , PIXEL_RATE, 0x05 , 89999, 256 , STT_FULL, 0x00 , {29999, 59999, 0}, {29999, 29999, 89999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NEG , CM_COLOR , 600, 0x03 , -1 , PIXEL_RATE, 0x05 , 45999, 256 , STT_HALF, 0x00 , {15333, 30666, 0}, {15333, 15333, 45999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NEG , CM_COLOR , 300, 0x04 , -1 , PIXEL_RATE, 0x04 , 14879, 256 , STT_HALF, 0x00 , { 4959, 9919, 0}, { 4959, 4959, 14879}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NEG , CM_COLOR , 150, 0x04 , 3 , PIXEL_RATE, 0x04 , 14879, 256 , STT_HALF, 0x00 , { 4959, 9919, 0}, { 4959, 4959, 14879}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NEG , CM_GRAY , 4800, 0x0D , -1 , LINE_RATE , 0x05 , 60599, 64 , STT_FULL, 0x00 , {30299, 30299, 30299}, {30299, 30299, 30299}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NEG , CM_GRAY , 2400, 0x06 , -1 , LINE_RATE , 0x05 ,145799, 256 , STT_FULL, 0x00 , {48599, 97199, 0}, {48599, 48599,145799}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NEG , CM_GRAY , 1200, 0x07 , -1 , LINE_RATE , 0x05 , 89999, 256 , STT_FULL, 0x00 , {29999, 59999, 0}, {29999, 29999, 59999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NEG , CM_GRAY , 600, 0x08 , -1 , LINE_RATE , 0x05 , 45999, 256 , STT_HALF, 0x00 , {15333, 30666, 0}, {15333, 15333, 45999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NEG , CM_GRAY , 300, 0x09 , -1 , LINE_RATE , 0x04 , 14879, 256 , STT_HALF, 0x00 , { 4959, 9919, 0}, { 4959, 4959, 14879}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NEG , CM_GRAY , 150, 0x09 , 3 , LINE_RATE , 0x04 , 14879, 256 , STT_HALF, 0x00 , { 4959, 9919, 0}, { 4959, 4959, 14879}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }} }; SANE_Int rst = ERROR; if (mymode != NULL) { SANE_Int a; SANE_Int total = sizeof(reg) / sizeof(struct st_modes); SANE_Int count = 0; struct st_modes *md; for (a = 0; a < total; a++) { md = ®[a]; if (md->usb == usb) { if (count == sm) { memcpy(mymode, &md->mode, sizeof(struct st_scanmode)); rst = OK; break; } count++; } } } return rst; } static SANE_Int hp3800_scanmodes(SANE_Int usb, SANE_Int sm, struct st_scanmode *mymode) { struct st_modes { SANE_Int usb; struct st_scanmode mode; }; struct st_modes reg[] = { /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB20, {ST_NORMAL, CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x05 , 23999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x05 , 23999, 96 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_COLOR , 600, 0x02 , -1 , PIXEL_RATE, 0x04 , 7999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_COLOR , 300, 0x03 , 9 , PIXEL_RATE, 0x04 , 4999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_COLOR , 150, 0x03 , 10 , PIXEL_RATE, 0x04 , 2999, 1024 , STT_HALF, 0x01 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x05 , 23999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x05 , 23999, 96 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_GRAY , 600, 0x07 , -1 , LINE_RATE , 0x04 , 7999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_GRAY , 300, 0x08 , 9 , LINE_RATE , 0x04 , 4999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_GRAY , 150, 0x08 , 10 , LINE_RATE , 0x04 , 2999, 1024 , STT_HALF, 0x01 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB20, {ST_TA , CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x05 , 23999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x05 , 23999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_COLOR , 600, 0x09 , -1 , PIXEL_RATE, 0x04 , 5999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_COLOR , 300, 0x03 , 3 , PIXEL_RATE, 0x04 , 2999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_COLOR , 150, 0x03 , 10 , PIXEL_RATE, 0x04 , 2999, 1024 , STT_HALF, 0x01 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x05 , 23999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x05 , 23999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_GRAY , 600, 0x07 , -1 , LINE_RATE , 0x04 , 5999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_GRAY , 300, 0x08 , 3 , LINE_RATE , 0x04 , 2999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_GRAY , 150, 0x08 , 10 , LINE_RATE , 0x04 , 2999, 1024 , STT_HALF, 0x01 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB20, {ST_NEG , CM_COLOR , 2400, 0x0A , -1 , PIXEL_RATE, 0x04 ,127999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, {127999,127999,127999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_COLOR , 1200, 0x0B , -1 , PIXEL_RATE, 0x05 ,127999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, {127999,127999,127999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_COLOR , 600, 0x0C , -1 , PIXEL_RATE, 0x04 , 31999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 31999, 31999, 31999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_COLOR , 300, 0x0D , -1 , PIXEL_RATE, 0x04 , 15999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 15999, 15999, 15999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_COLOR , 150, 0x0D , 10 , PIXEL_RATE, 0x04 , 15999, 1024 , STT_HALF, 0x01 , { 0, 0, 0}, { 15999, 15999, 15999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_GRAY , 2400, 0x0F , -1 , LINE_RATE , 0x05 ,127999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, {127999,127999,127999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_GRAY , 1200, 0x10 , -1 , LINE_RATE , 0x05 ,127999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, {127999,127999,127999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_GRAY , 600, 0x11 , -1 , LINE_RATE , 0x04 , 31999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 31999, 31999, 31999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_GRAY , 300, 0x12 , -1 , LINE_RATE , 0x04 , 15999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 15999, 15999, 15999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_GRAY , 150, 0x12 , 10 , LINE_RATE , 0x04 , 15999, 1024 , STT_HALF, 0x01 , { 0, 0, 0}, { 15999, 15999, 15999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB11, {ST_NORMAL, CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x05 , 23999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 2 , 5 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NORMAL, CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x05 , 23999, 96 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 2 , 3 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NORMAL, CM_COLOR , 600, 0x02 , -1 , PIXEL_RATE, 0x04 , 7999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NORMAL, CM_COLOR , 300, 0x03 , 9 , PIXEL_RATE, 0x04 , 4999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NORMAL, CM_COLOR , 150, 0x03 , 10 , PIXEL_RATE, 0x04 , 2999, 1024 , STT_HALF, 0x01 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NORMAL, CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x05 , 23999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 2 , 2 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NORMAL, CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x05 , 23999, 96 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 2 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NORMAL, CM_GRAY , 600, 0x07 , -1 , LINE_RATE , 0x04 , 7999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NORMAL, CM_GRAY , 300, 0x08 , 9 , LINE_RATE , 0x04 , 4999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NORMAL, CM_GRAY , 150, 0x08 , 10 , LINE_RATE , 0x04 , 2999, 1024 , STT_HALF, 0x01 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB11, {ST_TA , CM_COLOR , 2400, 0x00 , -1 , PIXEL_RATE, 0x05 , 23999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_TA , CM_COLOR , 1200, 0x01 , -1 , PIXEL_RATE, 0x05 , 23999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_TA , CM_COLOR , 600, 0x09 , -1 , PIXEL_RATE, 0x04 , 5999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_TA , CM_COLOR , 300, 0x03 , 3 , PIXEL_RATE, 0x04 , 2999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_TA , CM_COLOR , 150, 0x03 , 10 , PIXEL_RATE, 0x04 , 2999, 1024 , STT_HALF, 0x01 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_TA , CM_GRAY , 2400, 0x05 , -1 , LINE_RATE , 0x05 , 23999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_TA , CM_GRAY , 1200, 0x06 , -1 , LINE_RATE , 0x05 , 23999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_TA , CM_GRAY , 600, 0x07 , -1 , LINE_RATE , 0x04 , 5999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_TA , CM_GRAY , 300, 0x08 , 3 , LINE_RATE , 0x04 , 2999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_TA , CM_GRAY , 150, 0x08 , 10 , LINE_RATE , 0x04 , 2999, 1024 , STT_HALF, 0x01 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB11, {ST_NEG , CM_COLOR , 2400, 0x0A , -1 , PIXEL_RATE, 0x04 ,127999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, {127999,127999,127999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NEG , CM_COLOR , 1200, 0x0B , -1 , PIXEL_RATE, 0x05 ,127999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, {127999,127999,127999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NEG , CM_COLOR , 600, 0x0C , -1 , PIXEL_RATE, 0x04 , 31999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 31999, 31999, 31999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NEG , CM_COLOR , 300, 0x0D , -1 , PIXEL_RATE, 0x04 , 15999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 15999, 15999, 15999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NEG , CM_COLOR , 150, 0x0D , 10 , PIXEL_RATE, 0x04 , 15999, 1024 , STT_HALF, 0x01 , { 0, 0, 0}, { 15999, 15999, 15999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NEG , CM_GRAY , 2400, 0x0F , -1 , LINE_RATE , 0x05 ,127999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, {127999,127999,127999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NEG , CM_GRAY , 1200, 0x10 , -1 , LINE_RATE , 0x05 ,127999, 96 , STT_HALF, 0x00 , { 0, 0, 0}, {127999,127999,127999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NEG , CM_GRAY , 600, 0x11 , -1 , LINE_RATE , 0x04 , 31999, 128 , STT_HALF, 0x00 , { 0, 0, 0}, { 31999, 31999, 31999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NEG , CM_GRAY , 300, 0x12 , -1 , LINE_RATE , 0x04 , 15999, 256 , STT_HALF, 0x00 , { 0, 0, 0}, { 15999, 15999, 15999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB11, {ST_NEG , CM_GRAY , 150, 0x12 , 10 , LINE_RATE , 0x04 , 15999, 1024 , STT_HALF, 0x01 , { 0, 0, 0}, { 15999, 15999, 15999}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }} }; SANE_Int rst = ERROR; if (mymode != NULL) { SANE_Int a; SANE_Int total = sizeof(reg) / sizeof(struct st_modes); SANE_Int count = 0; struct st_modes *md; for (a = 0; a < total; a++) { md = ®[a]; if (md->usb == usb) { if (count == sm) { memcpy(mymode, &md->mode, sizeof(struct st_scanmode)); rst = OK; break; } count++; } } } return rst; } static SANE_Int bq5550_scanmodes(SANE_Int usb, SANE_Int sm, struct st_scanmode *mymode) { struct st_modes { SANE_Int usb; struct st_scanmode mode; }; struct st_modes reg[] = { /* usb, {scantype , colormode , res , timing, curve, samplerate, clock, ctpc , backstp, steptype , dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB20, {ST_NORMAL, CM_COLOR , 1200, 0x00 , -1 , PIXEL_RATE, 0x05 , 12999, 10 , STT_QUART, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 3 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_COLOR , 600, 0x01 , -1 , PIXEL_RATE, 0x05 , 6999, 32 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 5 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_COLOR , 300, 0x02 , 0 , PIXEL_RATE, 0x05 , 5599, 128 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 3 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_COLOR , 150, 0x02 , 0 , PIXEL_RATE, 0x05 , 6439, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 2 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_COLOR , 100, 0x02 , 0 , PIXEL_RATE, 0x05 , 5759, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_GRAY , 1200, 0x03 , -1 , LINE_RATE , 0x05 , 12999, 10 , STT_QUART, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 3 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_GRAY , 600, 0x04 , -1 , LINE_RATE , 0x05 , 7199, 32 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 2 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_GRAY , 300, 0x05 , 0 , LINE_RATE , 0x05 , 5599, 128 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_GRAY , 150, 0x05 , 0 , LINE_RATE , 0x05 , 6239, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_GRAY , 100, 0x05 , 0 , LINE_RATE , 0x05 , 5759, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_LINEART, 1200, 0x03 , -1 , LINE_RATE , 0x05 , 12999, 10 , STT_QUART, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 3 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_LINEART, 600, 0x04 , -1 , LINE_RATE , 0x05 , 7199, 32 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 2 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_LINEART, 300, 0x05 , 0 , LINE_RATE , 0x05 , 5599, 128 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_LINEART, 150, 0x05 , 0 , LINE_RATE , 0x05 , 6239, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_LINEART, 100, 0x05 , 0 , LINE_RATE , 0x05 , 5759, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, {scantype , colormode , res , timing, curve, samplerate, clock, ctpc , backstp, steptype , dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB20, {ST_TA , CM_COLOR , 1200, 0x00 , -1 , PIXEL_RATE, 0x05 , 9899, 10 , STT_QUART, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_COLOR , 600, 0x01 , -1 , PIXEL_RATE, 0x05 , 9999, 32 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_COLOR , 300, 0x02 , 0 , PIXEL_RATE, 0x05 , 4799, 128 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_COLOR , 150, 0x02 , 0 , PIXEL_RATE, 0x05 , 4959, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_COLOR , 100, 0x02 , 0 , PIXEL_RATE, 0x05 , 5059, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_GRAY , 1200, 0x03 , -1 , LINE_RATE , 0x05 , 9899, 10 , STT_QUART, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_GRAY , 600, 0x04 , -1 , LINE_RATE , 0x05 , 9999, 32 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_GRAY , 300, 0x05 , 0 , LINE_RATE , 0x05 , 4799, 128 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_GRAY , 150, 0x05 , 0 , LINE_RATE , 0x05 , 4959, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_GRAY , 100, 0x05 , 0 , LINE_RATE , 0x05 , 5059, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_LINEART, 1200, 0x03 , -1 , LINE_RATE , 0x05 , 9899, 10 , STT_QUART, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_LINEART, 600, 0x04 , -1 , LINE_RATE , 0x05 , 9999, 32 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_LINEART, 300, 0x05 , 0 , LINE_RATE , 0x05 , 4799, 128 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_LINEART, 150, 0x05 , 0 , LINE_RATE , 0x05 , 4959, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_LINEART, 100, 0x05 , 0 , LINE_RATE , 0x05 , 5059, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, {scantype , colormode , res , timing, curve, samplerate, clock, ctpc , backstp, steptype , dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB20, {ST_NEG , CM_COLOR , 1200, 0x00 , -1 , PIXEL_RATE, 0x05 , 51899, 10 , STT_QUART, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_COLOR , 600, 0x01 , -1 , PIXEL_RATE, 0x05 , 51799, 32 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_COLOR , 300, 0x02 , 0 , PIXEL_RATE, 0x05 , 25899, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_COLOR , 150, 0x02 , 0 , PIXEL_RATE, 0x05 , 25899, 128 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_COLOR , 100, 0x02 , 0 , PIXEL_RATE, 0x05 , 25499, 128 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_GRAY , 1200, 0x03 , -1 , LINE_RATE , 0x05 , 51899, 10 , STT_QUART, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_GRAY , 600, 0x04 , -1 , LINE_RATE , 0x05 , 51799, 32 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_GRAY , 300, 0x05 , 0 , LINE_RATE , 0x05 , 25899, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_GRAY , 150, 0x05 , 0 , LINE_RATE , 0x05 , 25899, 128 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_GRAY , 100, 0x05 , 0 , LINE_RATE , 0x05 , 25499, 128 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_LINEART, 1200, 0x03 , -1 , LINE_RATE , 0x05 , 51899, 10 , STT_QUART, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_LINEART, 600, 0x04 , -1 , LINE_RATE , 0x05 , 51799, 32 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_LINEART, 300, 0x05 , 0 , LINE_RATE , 0x05 , 25899, 128 , STT_FULL , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_LINEART, 150, 0x05 , 0 , LINE_RATE , 0x05 , 25899, 128 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_LINEART, 100, 0x05 , 0 , LINE_RATE , 0x05 , 25499, 128 , STT_HALF , 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }} }; SANE_Int rst = ERROR; /* silence compiler */ (void) usb; if (mymode != NULL) { SANE_Int a; SANE_Int total = sizeof(reg) / sizeof(struct st_modes); SANE_Int count = 0; struct st_modes *md; for (a = 0; a < total; a++) { md = ®[a]; if (count == sm) { memcpy(mymode, &md->mode, sizeof(struct st_scanmode)); rst = OK; break; } count++; } } return rst; } static SANE_Int ua4900_scanmodes(SANE_Int usb, SANE_Int sm, struct st_scanmode *mymode) { struct st_modes { SANE_Int usb; struct st_scanmode mode; }; struct st_modes reg[] = { /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB20, {ST_NORMAL, CM_COLOR , 1200, 0x00 , -1 , PIXEL_RATE, 0x05 , 14667, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_COLOR , 600, 0x01 , 2 , PIXEL_RATE, 0x04 , 5499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_COLOR , 300, 0x02 , 3 , PIXEL_RATE, 0x04 , 2751, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_COLOR , 200, 0x03 , 4 , PIXEL_RATE, 0x04 , 2255, 1856 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_COLOR , 100, 0x04 , 4 , PIXEL_RATE, 0x04 , 2255, 1856 , STT_HALF, 0x02 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_GRAY , 1200, 0x05 , -1 , LINE_RATE , 0x05 , 14667, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_GRAY , 600, 0x06 , 2 , LINE_RATE , 0x04 , 5499, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_GRAY , 300, 0x07 , 3 , LINE_RATE , 0x04 , 2751, 768 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_GRAY , 200, 0x08 , 4 , LINE_RATE , 0x04 , 2255, 1856 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NORMAL, CM_GRAY , 100, 0x09 , 4 , LINE_RATE , 0x04 , 2255, 1856 , STT_HALF, 0x02 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB20, {ST_TA , CM_COLOR , 1200, 0x00 , -1 , PIXEL_RATE, 0x03 , 10899, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_COLOR , 600, 0x01 , -1 , PIXEL_RATE, 0x01 , 5487, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_COLOR , 300, 0x02 , 3 , PIXEL_RATE, 0x04 , 8351, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_COLOR , 200, 0x03 , 4 , PIXEL_RATE, 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_COLOR , 100, 0x04 , 4 , PIXEL_RATE, 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_GRAY , 1200, 0x05 , -1 , LINE_RATE , 0x03 , 10899, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_GRAY , 600, 0x06 , -1 , LINE_RATE , 0x01 , 5487, 256 , STT_FULL, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_GRAY , 300, 0x07 , 3 , LINE_RATE , 0x04 , 8351, 512 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_GRAY , 200, 0x08 , 4 , LINE_RATE , 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_TA , CM_GRAY , 100, 0x09 , 4 , LINE_RATE , 0x04 , 7343, 1024 , STT_HALF, 0x00 , { 0, 0, 0}, { 0, 0, 0}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, /* usb, {scantype , colormode, res , timing, curve, samplerate, clock, ctpc , backstp, steptype, dummyline, {expt }, {mexpt }, motorplus, mexpt16, mexptfull, mexposure, mri , msi , mmtir, mmtirh, skips } */ {USB20, {ST_NEG , CM_COLOR , 1200, 0x00 , -1 , PIXEL_RATE, 0x03 , 36467, 256 , STT_FULL, 0x00 , {12155, 24311, 0}, {12155, 12155, 36467}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_COLOR , 600, 0x01 , -1 , PIXEL_RATE, 0x02 , 16463, 256 , STT_FULL, 0x00 , { 5487, 10975, 0}, { 5487, 5487, 16463}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_COLOR , 300, 0x02 , -1 , PIXEL_RATE, 0x02 , 8351, 256 , STT_HALF, 0x00 , { 2783, 5567, 0}, { 2783, 2783, 8351}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_COLOR , 200, 0x03 , -1 , PIXEL_RATE, 0x02 , 6191, 256 , STT_HALF, 0x00 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_COLOR , 100, 0x04 , -1 , PIXEL_RATE, 0x02 , 6191, 256 , STT_HALF, 0x02 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_GRAY , 1200, 0x05 , -1 , LINE_RATE , 0x03 , 36467, 256 , STT_FULL, 0x00 , {12155, 24311, 0}, {12155, 12155, 36467}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_GRAY , 600, 0x06 , -1 , LINE_RATE , 0x02 , 16463, 256 , STT_FULL, 0x00 , { 5487, 10975, 0}, { 5487, 5487, 16463}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_GRAY , 300, 0x07 , -1 , LINE_RATE , 0x02 , 8351, 256 , STT_HALF, 0x00 , { 2783, 5567, 0}, { 2783, 2783, 8351}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_GRAY , 200, 0x08 , -1 , LINE_RATE , 0x02 , 6191, 256 , STT_HALF, 0x00 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }}, {USB20, {ST_NEG , CM_GRAY , 100, 0x09 , -1 , LINE_RATE , 0x02 , 6191, 256 , STT_HALF, 0x02 , { 2063, 4127, 0}, { 2063, 2063, 6191}, 0 , 1 , 1 , 0x01 , 0x01, 0x10, 0x02 , 0x02 , 0x00 }} }; SANE_Int rst = ERROR; if (mymode != NULL) { SANE_Int a; SANE_Int total = sizeof(reg) / sizeof(struct st_modes); SANE_Int count = 0; struct st_modes *md; for (a = 0; a < total; a++) { md = ®[a]; if (md->usb == usb) { if (count == sm) { memcpy(mymode, &md->mode, sizeof(struct st_scanmode)); rst = OK; break; } count++; } } } return rst; } /** SEC: Calibration wreferences ---------- */ static void hp3970_wrefs(SANE_Int usb, SANE_Int ccd, SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue) { struct st_wref { SANE_Int usb; SANE_Int sensor; SANE_Int depth; SANE_Int res; SANE_Int transparent[3]; SANE_Int negative[3]; }; struct st_wref wrefs[] = { /*usb , sensor , depth, res , {transparent }, {negative} */ {USB20, CCD_SENSOR, 8 , 2400, { 78, 78, 68}, {120, 136, 157}}, {USB20, CCD_SENSOR, 8 , 1200, { 78, 78, 68}, {120, 136, 157}}, {USB20, CCD_SENSOR, 8 , 600 , { 79, 80, 70}, {119, 136, 157}}, {USB20, CCD_SENSOR, 8 , 300 , { 79, 80, 70}, {119, 136, 157}}, {USB20, CCD_SENSOR, 8 , 200 , { 79, 80, 70}, {119, 136, 157}}, {USB20, CCD_SENSOR, 8 , 100 , { 79, 80, 70}, {119, 136, 157}}, {USB20, CCD_SENSOR, 16 , 2400, { 81, 81, 71}, {119, 137, 158}}, {USB20, CCD_SENSOR, 16 , 1200, { 81, 81, 71}, {119, 137, 158}}, {USB20, CCD_SENSOR, 16 , 600 , { 81, 82, 72}, {119, 137, 158}}, {USB20, CCD_SENSOR, 16 , 300 , { 81, 82, 72}, {119, 137, 158}}, {USB20, CCD_SENSOR, 16 , 200 , { 81, 82, 72}, {119, 137, 158}}, {USB20, CCD_SENSOR, 16 , 100 , { 81, 82, 72}, {119, 137, 158}}, /*usb , sensor , depth, res , {transparent }, {negative } */ {USB20, CIS_SENSOR, 8 , 2400, { 94, 85, 78}, { 94, 85, 78}}, {USB20, CIS_SENSOR, 8 , 1200, { 83, 82, 75}, {140, 155, 155}}, {USB20, CIS_SENSOR, 8 , 600 , { 84, 84, 76}, {145, 155, 165}}, {USB20, CIS_SENSOR, 8 , 300 , {146, 166, 166}, {146, 166, 166}}, {USB20, CIS_SENSOR, 8 , 200 , {145, 310, 160}, {145, 310, 160}}, {USB20, CIS_SENSOR, 8 , 100 , {140, 300, 155}, {140, 300, 155}}, {USB20, CIS_SENSOR, 16 , 2400, { 94, 85, 78}, { 94, 85, 78}}, {USB20, CIS_SENSOR, 16 , 1200, { 83, 82, 75}, {140, 155, 155}}, {USB20, CIS_SENSOR, 16 , 600 , { 84, 84, 76}, {145, 155, 165}}, {USB20, CIS_SENSOR, 16 , 300 , {146, 166, 166}, {146, 166, 166}}, {USB20, CIS_SENSOR, 16 , 200 , {145, 310, 160}, {145, 310, 160}}, {USB20, CIS_SENSOR, 16 , 100 , {140, 300, 155}, {140, 300, 155}}, /*usb , sensor , depth, res , {transparent }, {negative } */ {USB11, CCD_SENSOR, 8 , 2400, { 78, 78, 68}, {120, 137, 158}}, {USB11, CCD_SENSOR, 8 , 1200, { 78, 78, 68}, {120, 137, 158}}, {USB11, CCD_SENSOR, 8 , 600 , { 79, 79, 70}, {120, 137, 158}}, {USB11, CCD_SENSOR, 8 , 300 , { 79, 79, 70}, {120, 137, 158}}, {USB11, CCD_SENSOR, 8 , 200 , { 79, 79, 70}, {120, 137, 158}}, {USB11, CCD_SENSOR, 8 , 100 , { 79, 79, 70}, {120, 137, 158}}, {USB11, CCD_SENSOR, 16 , 2400, { 80, 80, 70}, {120, 137, 158}}, {USB11, CCD_SENSOR, 16 , 1200, { 80, 80, 70}, {120, 137, 158}}, {USB11, CCD_SENSOR, 16 , 600 , { 80, 81, 71}, {120, 137, 158}}, {USB11, CCD_SENSOR, 16 , 300 , { 80, 81, 71}, {120, 137, 158}}, {USB11, CCD_SENSOR, 16 , 200 , { 80, 81, 71}, {120, 137, 158}}, {USB11, CCD_SENSOR, 16 , 100 , { 80, 81, 71}, {120, 137, 158}}, /*usb , sensor , depth, res , {transparent }, {negative} */ {USB11, CIS_SENSOR, 8 , 2400, { 94, 85, 78}, {94 , 85 , 78}}, {USB11, CIS_SENSOR, 8 , 1200, { 83, 82, 75}, {140, 155, 155}}, {USB11, CIS_SENSOR, 8 , 600 , { 84, 84, 76}, {145, 155, 165}}, {USB11, CIS_SENSOR, 8 , 300 , {146, 166, 166}, {146, 166, 166}}, {USB11, CIS_SENSOR, 8 , 200 , {145, 310, 160}, {145, 310, 160}}, {USB11, CIS_SENSOR, 8 , 100 , {140, 300, 155}, {140, 300, 155}}, {USB11, CIS_SENSOR, 16 , 2400, { 94, 85, 78}, { 94, 85, 78}}, {USB11, CIS_SENSOR, 16 , 1200, { 83, 82, 75}, {140, 155, 155}}, {USB11, CIS_SENSOR, 16 , 600 , { 84, 84, 76}, {145, 155, 165}}, {USB11, CIS_SENSOR, 16 , 300 , {146, 166, 166}, {146, 166, 166}}, {USB11, CIS_SENSOR, 16 , 200 , {145, 310, 160}, {145, 310, 160}}, {USB11, CIS_SENSOR, 16 , 100 , {140, 300, 155}, {140, 300, 155}} }; struct st_wref *rf; *red = *green = *blue = 0x50; if (res <= 100) res = 100; else if (res <= 200) res = 200; else if (res <= 300) res = 300; else if (res <= 600) res = 600; else if (res <= 1200) res = 1200; else res = 2400; if (scantype != ST_NORMAL) { SANE_Int a; SANE_Int count = sizeof(wrefs) / sizeof(struct st_wref); for (a = 0; a < count; a++) { rf = &wrefs[a]; if ((rf->usb == usb)&&(rf->sensor == ccd)&&(rf->depth == depth)&&(rf->res == res)) { switch(scantype) { case ST_NEG: *red = rf->negative[CL_RED]; *green = rf->negative[CL_GREEN]; *blue = rf->negative[CL_BLUE]; break; case ST_TA: *red = rf->transparent[CL_RED]; *green = rf->transparent[CL_GREEN]; *blue = rf->transparent[CL_BLUE]; break; } break; } } } else { /* reflective mode */ *red = 233; *green = 230; *blue = 222; } } static void ua4900_wrefs(SANE_Int usb, SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue) { struct st_wref { SANE_Int usb; SANE_Int depth; SANE_Int res; SANE_Int transparent[3]; SANE_Int negative[3]; }; struct st_wref wrefs[] = { /*usb , depth, res , {transparent }, {negative } */ {USB20, 8 , 1200, {136, 131, 121}, {215, 210, 260}}, {USB20, 8 , 600 , {133, 129, 118}, {219, 215, 264}}, {USB20, 8 , 300 , {133, 129, 119}, {218, 215, 261}}, {USB20, 8 , 200 , {133, 130, 119}, {218, 215, 261}}, {USB20, 8 , 100 , {132, 128, 118}, {218, 215, 261}}, {USB20, 16 , 1200, {264, 263, 266}, { 0, 0, 0}}, {USB20, 16 , 600 , {264, 265, 268}, { 0, 0, 0}}, {USB20, 16 , 300 , {264, 260, 263}, { 0, 0, 0}}, {USB20, 16 , 200 , {257, 255, 253}, { 0, 0, 0}}, {USB20, 16 , 100 , {258, 259, 256}, { 0, 0, 0}}, /*usb , depth, res , {transparent }, {negative } */ {USB11, 8 , 1200, {135, 130, 119}, {220, 215, 264}}, {USB11, 8 , 600 , {132, 128, 117}, {217, 213, 259}}, {USB11, 8 , 300 , {132, 128, 117}, {216, 211, 259}}, {USB11, 8 , 200 , {132, 128, 118}, {215, 352, 259}}, {USB11, 8 , 100 , {266, 264, 264}, {215, 352, 259}}, {USB11, 16 , 1200, {264, 263, 266}, { 0, 0, 0}}, {USB11, 16 , 600 , {257, 256, 253}, { 0, 0, 0}}, {USB11, 16 , 300 , {259, 252, 254}, { 0, 0, 0}}, {USB11, 16 , 200 , {257, 255, 253}, { 0, 0, 0}}, {USB11, 16 , 100 , {258, 259, 256}, { 0, 0, 0}} }; struct st_wref *rf; *red = *green = *blue = 0x50; if (res <= 100) res = 100; else if (res <= 200) res = 200; else if (res <= 300) res = 300; else if (res <= 600) res = 600; else res = 1200; if (scantype != ST_NORMAL) { SANE_Int a; SANE_Int count = sizeof(wrefs) / sizeof(struct st_wref); for (a = 0; a < count; a++) { rf = &wrefs[a]; if ((rf->usb == usb)&&(rf->depth == depth)&&(rf->res == res)) { switch(scantype) { case ST_NEG: *red = rf->negative[CL_RED]; *green = rf->negative[CL_GREEN]; *blue = rf->negative[CL_BLUE]; break; case ST_TA: *red = rf->transparent[CL_RED]; *green = rf->transparent[CL_GREEN]; *blue = rf->transparent[CL_BLUE]; break; } break; } } } else { /* reflective mode */ *red = 233; *green = 230; *blue = 222; } } static void hp3800_wrefs(SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue) { /* in this scanner, values are the same in all usb versions and depths */ struct st_wref { SANE_Int res; SANE_Int transparent[3]; SANE_Int negative[3]; }; struct st_wref wrefs[] = { /* res , {transparent }, {negative } */ { 2400, {276, 279, 243}, { 98, 162, 229}}, { 1200, {276, 279, 243}, { 98, 162, 229}}, { 600, {276, 279, 243}, {100, 162, 229}}, { 300, {276, 279, 243}, {100, 162, 229}}, { 150, {276, 279, 243}, {100, 162, 229}}, }; struct st_wref *rf; *red = *green = *blue = 0x50; if (res <= 150) res = 150; else if (res <= 300) res = 300; else if (res <= 600) res = 600; else if (res <= 1200) res = 1200; else res = 2400; if (scantype != ST_NORMAL) { SANE_Int a; SANE_Int count = sizeof(wrefs) / sizeof(struct st_wref); for (a = 0; a < count; a++) { rf = &wrefs[a]; if (rf->res == res) { switch(scantype) { case ST_NEG: *red = rf->negative[CL_RED]; *green = rf->negative[CL_GREEN]; *blue = rf->negative[CL_BLUE]; break; case ST_TA: *red = rf->transparent[CL_RED]; *green = rf->transparent[CL_GREEN]; *blue = rf->transparent[CL_BLUE]; break; } break; } } } else { /* reflective mode */ *red = 248; *green = 250; *blue = 248; } } static void hp4370_wrefs(SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue) { struct st_wref { SANE_Int res; SANE_Int transparent[3]; SANE_Int negative[3]; }; /* values are the same in all usb versions and depths */ struct st_wref wrefs[] = { /* res, {transparent }, {negative} */ { 4800, { 93, 93, 82}, {156, 308, 454}}, { 2400, { 93, 93, 82}, {156, 156, 153}}, { 1200, { 93, 93, 82}, {156, 156, 153}}, { 600, { 93, 93, 82}, {155, 155, 152}}, { 300, { 94, 94, 83}, {155, 155, 152}}, { 150, { 86, 87, 77}, {148, 145, 138}} }; struct st_wref *rf; SANE_Int a; *red = *green = *blue = 0x50; if (res <= 150) res = 150; else if (res <= 300) res = 300; else if (res <= 600) res = 600; else if (res <= 1200) res = 1200; else if (res <= 2400) res = 2400; else res = 4800; if (scantype != ST_NORMAL) { SANE_Int count = sizeof(wrefs) / sizeof(struct st_wref); for (a = 0; a < count; a++) { rf = &wrefs[a]; if (rf->res == res) { switch(scantype) { case ST_NEG: *red = rf->negative[CL_RED]; *green = rf->negative[CL_GREEN]; *blue = rf->negative[CL_BLUE]; break; case ST_TA: *red = rf->transparent[CL_RED]; *green = rf->transparent[CL_GREEN]; *blue = rf->transparent[CL_BLUE]; break; } break; } } } else { /* reflective mode */ *red = 233; *green = 232; *blue = 223; } } static void cfg_wrefs_get(SANE_Int sensortype, SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue) { switch(RTS_Debug->dev_model) { case UA4900: ua4900_wrefs(RTS_Debug->usbtype, depth, res, scantype, red, green, blue); break; case HP3800: case HPG2710: hp3800_wrefs(res, scantype, red, green, blue); break; case HP4370: case HPG3010: case HPG3110: hp4370_wrefs(res, scantype, red, green, blue); break; default: /* hp3970 and hp4070 */ hp3970_wrefs(RTS_Debug->usbtype, sensortype, depth, res, scantype, red, green, blue); break; } } static void hp3800_shading_cut(SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue) { /* values are the same in all usb versions and depths */ struct st_cut { SANE_Int res; SANE_Int reflective[3]; SANE_Int transparent[3]; SANE_Int negative[3]; }; struct st_cut cuts[] = { /* res, {reflective }, {transparent }, {negative } */ { 2400, { -6, -6, -6}, {-75, -75, -75}, {440, -300, -250}}, { 1200, { -6, -6, -6}, {-75, -75, -75}, {440, -300, -250}}, { 600, { 10, 9, 12}, {-75, -75, -75}, {440, -300, -250}}, { 300, { -6, -6, -6}, {-75, -75, -75}, {440, -300, -250}}, { 150, { -6, -6, -6}, {-75, -75, -75}, {440, -300, -250}} }; struct st_cut *ct; SANE_Int a; SANE_Int count = sizeof(cuts) / sizeof(struct st_cut); *red = *green = *blue = 0; if (res <= 150) res = 150; else if (res <= 300) res = 300; else if (res <= 600) res = 600; else if (res <= 1200) res = 1200; else res = 2400; for (a = 0; a < count; a++) { ct = &cuts[a]; if (ct->res == res) { switch(scantype) { case ST_NEG: *red = ct->negative[CL_RED]; *green = ct->negative[CL_GREEN]; *blue = ct->negative[CL_BLUE]; break; case ST_TA: *red = ct->transparent[CL_RED]; *green = ct->transparent[CL_GREEN]; *blue = ct->transparent[CL_BLUE]; break; default: /* reflective */ *red = ct->reflective[CL_RED]; *green = ct->reflective[CL_GREEN]; *blue = ct->reflective[CL_BLUE]; break; } break; } } } static void hp3970_shading_cut(SANE_Int usb, SANE_Int ccd, SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue) { struct st_cut { SANE_Int usb; SANE_Int sensor; SANE_Int depth; SANE_Int res; SANE_Int reflective[3]; SANE_Int transparent[3]; SANE_Int negative[3]; }; struct st_cut cuts[] = { /*usb , sensor , depth, res , {reflective }, {transparent }, {negative} */ {USB20, CCD_SENSOR, 8 , 2400, {-15, -15, -15}, { 0, 0, 0}, {63, 0, 3}}, {USB20, CCD_SENSOR, 8 , 1200, {-15, -15, -15}, { 0, 0, 0}, {63, 0, 5}}, {USB20, CCD_SENSOR, 8 , 600 , {-15, -15, -15}, { 0, 0, 0}, {75, 0, 5}}, {USB20, CCD_SENSOR, 8 , 300 , {-15, -15, -15}, { 0, 0, 0}, {75, 0, 5}}, {USB20, CCD_SENSOR, 8 , 200 , {-15, -15, -15}, { 0, 0, 0}, {75, 0, 5}}, {USB20, CCD_SENSOR, 8 , 100 , {-15, -15, -15}, { 0, 0, 0}, {75, 0, 5}}, {USB20, CCD_SENSOR, 16 , 2400, {-15, -15, -15}, { -7, -7, -7}, {43, 0, 3}}, {USB20, CCD_SENSOR, 16 , 1200, {-15, -15, -15}, {-15, -15, -15}, {63, 0, 5}}, {USB20, CCD_SENSOR, 16 , 600 , {-15, -15, -15}, {-15, -15, -15}, {75, 0, 5}}, {USB20, CCD_SENSOR, 16 , 300 , {-15, -15, -15}, {-15, -15, -15}, {75, 0, 5}}, {USB20, CCD_SENSOR, 16 , 200 , {-15, -15, -15}, {-15, -15, -15}, {75, 0, 5}}, {USB20, CCD_SENSOR, 16 , 100 , {-15, -15, -15}, {-15, -15, -15}, {75, 0, 5}}, /*usb , sensor , depth, res , {reflective }, {transparent }, {negative} */ {USB20, CIS_SENSOR, 8 , 2400, {-15, -15, -15}, {-68, -37, -43}, { 0, 0, 0}}, {USB20, CIS_SENSOR, 8 , 1200, {-15, -15, -15}, {-20, -20, -28}, { 0, 0, 0}}, {USB20, CIS_SENSOR, 8 , 600 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}}, {USB20, CIS_SENSOR, 8 , 300 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}}, {USB20, CIS_SENSOR, 8 , 200 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}}, {USB20, CIS_SENSOR, 8 , 100 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}}, {USB20, CIS_SENSOR, 16 , 2400, {-15, -15, -15}, {-68, -37, -43}, { 0, 0, 0}}, {USB20, CIS_SENSOR, 16 , 1200, {-15, -15, -15}, {-20, -20, -28}, { 0, 0, 0}}, {USB20, CIS_SENSOR, 16 , 600 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}}, {USB20, CIS_SENSOR, 16 , 300 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}}, {USB20, CIS_SENSOR, 16 , 200 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}}, {USB20, CIS_SENSOR, 16 , 100 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}}, /*usb , sensor , depth, res , {reflective }, {transparent }, {negative} */ {USB11, CCD_SENSOR, 8 , 2400, {-15, -15, -15}, { 0, 0, 0}, {63, 0, 3}}, {USB11, CCD_SENSOR, 8 , 1200, {-15, -15, -15}, { 0, 0, 0}, {63, 0, 5}}, {USB11, CCD_SENSOR, 8 , 600 , {-15, -15, -15}, { 0, 0, 0}, {50, 0, 5}}, {USB11, CCD_SENSOR, 8 , 300 , {-15, -15, -15}, { 0, 0, 0}, {50, 0, 5}}, {USB11, CCD_SENSOR, 8 , 200 , {-15, -15, -15}, { 0, 0, 0}, {50, 0, 5}}, {USB11, CCD_SENSOR, 8 , 100 , {-15, -15, -15}, { 0, 0, 0}, {50, 0, 5}}, {USB11, CCD_SENSOR, 16 , 2400, {-15, -15, -15}, { -7, -7, -7}, {43, 0, 3}}, {USB11, CCD_SENSOR, 16 , 1200, {-15, -15, -15}, {-15, -15, -15}, {63, 0, 5}}, {USB11, CCD_SENSOR, 16 , 600 , {-15, -15, -15}, {-15, -15, -15}, {50, 0, 5}}, {USB11, CCD_SENSOR, 16 , 300 , {-15, -15, -15}, {-15, -15, -15}, {50, 0, 5}}, {USB11, CCD_SENSOR, 16 , 200 , {-15, -15, -15}, {-15, -15, -15}, {50, 0, 5}}, {USB11, CCD_SENSOR, 16 , 100 , {-15, -15, -15}, {-15, -15, -15}, {50, 0, 5}}, /*usb , sensor , depth, res , {reflective }, {transparent }, {negative} */ {USB11, CIS_SENSOR, 8 , 2400, {-15, -15, -15}, {-68, -37, -43}, { 0, 0, 0}}, {USB11, CIS_SENSOR, 8 , 1200, {-15, -15, -15}, {-20, -20, -28}, { 0, 0, 0}}, {USB11, CIS_SENSOR, 8 , 600 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}}, {USB11, CIS_SENSOR, 8 , 300 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}}, {USB11, CIS_SENSOR, 8 , 200 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}}, {USB11, CIS_SENSOR, 8 , 100 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}}, {USB11, CIS_SENSOR, 16 , 2400, {-15, -15, -15}, {-68, -37, -43}, { 0, 0, 0}}, {USB11, CIS_SENSOR, 16 , 1200, {-15, -15, -15}, {-20, -20, -28}, { 0, 0, 0}}, {USB11, CIS_SENSOR, 16 , 600 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}}, {USB11, CIS_SENSOR, 16 , 300 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}}, {USB11, CIS_SENSOR, 16 , 200 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}}, {USB11, CIS_SENSOR, 16 , 100 , {-15, -15, -15}, { 0, 0, 0}, { 0, 0, 0}} }; struct st_cut *ct; SANE_Int a; SANE_Int count = sizeof(cuts) / sizeof(struct st_cut); *red = *green = *blue = 0; if (res <= 100) res = 100; else if (res <= 200) res = 200; else if (res <= 300) res = 300; else if (res <= 600) res = 600; else if (res <= 1200) res = 1200; else res = 2400; for (a = 0; a < count; a++) { ct = &cuts[a]; if ((ct->usb == usb)&&(ct->sensor == ccd)&&(ct->depth == depth)&&(ct->res == res)) { switch(scantype) { case ST_NEG: *red = ct->negative[CL_RED]; *green = ct->negative[CL_GREEN]; *blue = ct->negative[CL_BLUE]; break; case ST_TA: *red = ct->transparent[CL_RED]; *green = ct->transparent[CL_GREEN]; *blue = ct->transparent[CL_BLUE]; break; default: /* reflective */ *red = ct->reflective[CL_RED]; *green = ct->reflective[CL_GREEN]; *blue = ct->reflective[CL_BLUE]; break; } break; } } } static void hp4370_shading_cut(SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue) { struct st_cut { SANE_Int depth; SANE_Int res; SANE_Int reflective[3]; SANE_Int transparent[3]; SANE_Int negative[3]; }; /* values are the same in all usb versions for each depth */ struct st_cut cuts[] = { /* depth, res , {reflective }, {transparent }, {negative } */ { 8 , 4800, { 0, 0, 0}, { -3, -3, -3}, {35, -10, -7}}, { 8 , 2400, {-15, -15, -15}, { -3, -3, -3}, {35, -10, -7}}, { 8 , 1200, {-15, -15, -15}, { -3, -3, -3}, {35, -10, -22}}, { 8 , 600 , {-15, -15, -15}, { -2, -2, -2}, {38, -10, -22}}, { 8 , 300 , {-15, -15, -15}, { -2, -2, -2}, {38, -10, -22}}, { 8 , 150 , {-15, -15, -15}, { 0, 0, 0}, {50, -23, -14}}, { 16 , 4800, { 0, 0, 0}, { -3, -3, -3}, {35, -10, -7}}, { 16 , 2400, {-15, -15, -15}, { -3, -3, -3}, {35, -10, -7}}, { 16 , 1200, {-15, -15, -15}, { -3, -3, -3}, {35, -10, -22}}, { 16 , 600 , {-15, -15, -15}, { -2, -2, -2}, {38, -10, -22}}, { 16 , 300 , {-15, -15, -15}, { -2, -2, -2}, {38, -10, -22}}, { 16 , 150 , {-15, -15, -15}, {-15, -15, -15}, {40, -15, -5}} }; struct st_cut *ct; SANE_Int a; SANE_Int count = sizeof(cuts) / sizeof(struct st_cut); *red = *green = *blue = 0; if (res <= 150) res = 150; else if (res <= 300) res = 300; else if (res <= 600) res = 600; else if (res <= 1200) res = 1200; else if (res <= 2400) res = 2400; else res = 4800; for (a = 0; a < count; a++) { ct = &cuts[a]; if ((ct->depth == depth)&&(ct->res == res)) { switch(scantype) { case ST_NEG: *red = ct->negative[CL_RED]; *green = ct->negative[CL_GREEN]; *blue = ct->negative[CL_BLUE]; break; case ST_TA: *red = ct->transparent[CL_RED]; *green = ct->transparent[CL_GREEN]; *blue = ct->transparent[CL_BLUE]; break; default: /* reflective */ *red = ct->reflective[CL_RED]; *green = ct->reflective[CL_GREEN]; *blue = ct->reflective[CL_BLUE]; break; } break; } } } static void ua4900_shading_cut(SANE_Int usb, SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue) { struct st_cut { SANE_Int usb; SANE_Int depth; SANE_Int res; SANE_Int reflective[3]; SANE_Int transparent[3]; SANE_Int negative[3]; }; struct st_cut cuts[] = { /*usb , depth, res , {reflective }, {transparent }, {negative } */ {USB20, 8 , 1200, {-15, -17, -13}, { 6, 0, 0}, {110, 15, 250}}, {USB20, 8 , 600 , {-16, -15, -15}, { 6, 7, 5}, {105, 11, 20}}, {USB20, 8 , 300 , {-15, -15, -15}, { 6, 7, 5}, {105, 10, 25}}, {USB20, 8 , 200 , {-15, -15, -15}, { 5, 5, 5}, {110, 15, 32}}, {USB20, 8 , 100 , {-15, -15, -15}, { 5, 5, 5}, {110, 15, 32}}, {USB20, 16 , 1200, {-15, -15, -15}, { 5, 5, 5}, { 0, 0, 0}}, {USB20, 16 , 600 , {-15, -15, -15}, { 5, 5, 5}, { 0, 0, 0}}, {USB20, 16 , 300 , {-15, -15, -15}, { 5, 5, 5}, { 0, 0, 0}}, {USB20, 16 , 200 , {-15, -15, -15}, { 5, 5, 5}, { 0, 0, 0}}, {USB20, 16 , 100 , {-15, -15, -15}, { 5, 5, 5}, { 0, 0, 0}}, /*usb , depth, res , {reflective }, {transparent }, {negative } */ {USB11, 8 , 1200, {-15, -17, -13}, { 5, 5, 5}, {105, 12, 23}}, {USB11, 8 , 600 , {-17, -15, -16}, { 8, 8, 8}, {105, 12, 20}}, {USB11, 8 , 300 , {-15, -15, -15}, { 8, 8, 8}, {105, 15, 25}}, {USB11, 8 , 200 , {-15, -15, -15}, { 8, 8, 8}, {105, 40, 25}}, {USB11, 8 , 100 , {-15, -15, -15}, { 8, 8, 8}, {-20, 50, 23}}, {USB11, 16 , 1200, {-15, -15, -15}, { 5, 5, 5}, { 0, 0, 0}}, {USB11, 16 , 600 , {-15, -15, -15}, { 5, 5, 5}, { 0, 0, 0}}, {USB11, 16 , 300 , {-15, -15, -15}, { 5, 5, 5}, { 0, 0, 0}}, {USB11, 16 , 200 , {-15, -15, -15}, { 5, 5, 5}, { 0, 0, 0}}, {USB11, 16 , 100 , {-15, -15, -15}, { 5, 5, 5}, { 0, 0, 0}} }; struct st_cut *ct; SANE_Int a; SANE_Int count = sizeof(cuts) / sizeof(struct st_cut); *red = *green = *blue = 0; if (res <= 100) res = 100; else if (res <= 200) res = 200; else if (res <= 300) res = 300; else if (res <= 600) res = 600; else res = 1200; for (a = 0; a < count; a++) { ct = &cuts[a]; if ((ct->usb == usb)&&(ct->depth == depth)&&(ct->res == res)) { switch(scantype) { case ST_NEG: *red = ct->negative[CL_RED]; *green = ct->negative[CL_GREEN]; *blue = ct->negative[CL_BLUE]; break; case ST_TA: *red = ct->transparent[CL_RED]; *green = ct->transparent[CL_GREEN]; *blue = ct->transparent[CL_BLUE]; break; default: /* reflective */ *red = ct->reflective[CL_RED]; *green = ct->reflective[CL_GREEN]; *blue = ct->reflective[CL_BLUE]; break; } break; } } } static void cfg_shading_cut_get(SANE_Int sensortype, SANE_Int depth, SANE_Int res, SANE_Int scantype, SANE_Int *red, SANE_Int *green, SANE_Int *blue) { switch(RTS_Debug->dev_model) { case UA4900: ua4900_shading_cut(RTS_Debug->usbtype, depth, res, scantype, red, green, blue); break; case HP3800: case HPG2710: hp3800_shading_cut(res, scantype, red, green, blue); break; case HP4370: case HPG3010: case HPG3110: hp4370_shading_cut(depth, res, scantype, red, green, blue); break; default: /* hp3970 and hp4070 */ hp3970_shading_cut(RTS_Debug->usbtype, sensortype, depth, res, scantype, red, green, blue); break; } } /** SEC: Sensor Timings ---------- */ /* ccd timing values for bq5550 scanner */ static SANE_Int bq5550_timing_get(SANE_Int tm, struct st_timing *reg) { SANE_Int rst = ERROR; if ((tm < 7)&&(reg != NULL)) { /* bq5550 sensor timing values for each resolution and color mode */ struct st_timing data[] = { /* res , cnpp, {cvtrp 1 2 3 }, cvtrw, cvtrfpw, cvtrbpw, {{cphp1 , cphp2 , cphps, cphge, cphgo}, {cphp1 , cphp2 , cphps, cphge, cphgo}, {cphp1 , cphp2 , cphps, cphge, cphgo}, {cphp1 , cphp2 , cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}}, cphbp2s, cphbp2e, clamps, clampe , {cdss1, cdss2}, {cdsc1, cdsc2}, {cdscs1, cdscs2}, {adcclkp 1 y 2 }, adcclkp2e */ {0x04B0, 0x23, {0x00, 0x00, 0x00}, 0x16 , 0x02 , 0x02 , {{67645734915., 0. , 0x00 , 0x01 , 0x00 }, {132120576. , 0. , 0x00 , 0x01 , 0x00 }, {68585259519., 0. , 0x01 , 0x01 , 0x00 }, {134217216. , 0. , 0x01 , 0x01 , 0x01 }, {68585259519., 0. , 0x01 , 0x01 , 0x00 }, {134217216. , 0. , 0x01 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x0F , 0x1F }, {0x12 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00}, {0x0258, 0x23, {0x00, 0x00, 0x00}, 0x16 , 0x02 , 0x02 , {{66571993091., 0. , 0x00 , 0x01 , 0x00 }, {132120576. , 0. , 0x00 , 0x01 , 0x00 }, {131071. , 0. , 0x00 , 0x01 , 0x00 }, {68719345664., 0. , 0x00 , 0x01 , 0x01 }, {131071. , 0. , 0x00 , 0x01 , 0x00 }, {68719345664., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x10 , 0x1F }, {0x13 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00}, {0x012C, 0x23, {0x00, 0x00, 0x00}, 0x16 , 0x02 , 0x02 , {{67645734915., 0. , 0x00 , 0x01 , 0x00 }, {125829120. , 0. , 0x00 , 0x01 , 0x00 }, {67654057987., 0. , 0x00 , 0x01 , 0x00 }, {1065418748. , 0. , 0x00 , 0x01 , 0x01 }, {16383. , 0. , 0x00 , 0x01 , 0x00 }, {68182605824., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x10 , 0x1F }, {0x13 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00}, {0x04B0, 0x23, {0x00, 0x00, 0x00}, 0x16 , 0x02 , 0x02 , {{67645734915., 0. , 0x00 , 0x01 , 0x00 }, {132120576. , 0. , 0x00 , 0x01 , 0x00 }, {68585259519., 0. , 0x01 , 0x01 , 0x00 }, {134217216. , 0. , 0x01 , 0x01 , 0x01 }, {68585259519., 0. , 0x01 , 0x01 , 0x00 }, {134217216. , 0. , 0x01 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x0F , 0x1F }, {0x12 , 0x22 }, {0x00 , 0x00 }, {262140. , 0. }, 0x00}, {0x0258, 0x23, {0x00, 0x00, 0x00}, 0x16 , 0x02 , 0x02 , {{66571993091., 0. , 0x00 , 0x01 , 0x00 }, {132120576. , 0. , 0x00 , 0x01 , 0x00 }, {131071. , 0. , 0x00 , 0x01 , 0x00 }, {68719345664., 0. , 0x00 , 0x01 , 0x01 }, {131071. , 0. , 0x00 , 0x01 , 0x00 }, {68719345664., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x10 , 0x1F }, {0x13 , 0x22 }, {0x00 , 0x00 }, {262140. , 0. }, 0x00}, {0x012C, 0x23, {0x00, 0x00, 0x00}, 0x16 , 0x02 , 0x02 , {{67645734915., 0. , 0x00 , 0x01 , 0x00 }, {125829120. , 0. , 0x00 , 0x01 , 0x00 }, {67654057987., 0. , 0x00 , 0x01 , 0x00 }, {1065418748. , 0. , 0x00 , 0x01 , 0x01 }, {16383. , 0. , 0x00 , 0x01 , 0x00 }, {68182605824., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x10 , 0x1F }, {0x13 , 0x22 }, {0x00 , 0x00 }, {524284. , 0. }, 0x00} }; memcpy(reg, &data[tm], sizeof(struct st_timing)); rst = OK; } return rst; } /* ccd timing values for hp3800 scanner */ static SANE_Int hp3800_timing_get(SANE_Int tm, struct st_timing *reg) { SANE_Int rst = ERROR; if ((tm < 20)&&(reg != NULL)) { /* Toshiba T2905 sensor timing values for each resolution and color mode */ struct st_timing data[] = { /* res , cnpp, {cvtrp 1 2 3 }, cvtrw, cvtrfpw, cvtrbpw, {{cphp1 , cphp2 , cphps, cphge, cphgo}, {cphp1 , cphp2 , cphps, cphge, cphgo}, {cphp1 , cphp2 , cphps, cphge, cphgo}, {cphp1 , cphp2 , cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}}, cphbp2s, cphbp2e, clamps, clampe , {cdss1, cdss2}, {cdsc1, cdsc2}, {cdscs1, cdscs2}, {adcclkp 1 y 2 }, adcclkp2e */ {0x0960, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{8589934588. , 0. , 0x00 , 0x01 , 0x01 }, {68719476732., 0. , 0x00 , 0x01 , 0x01 }, {134217216. , 0. , 0x01 , 0x01 , 0x00 }, {68585259519., 0. , 0x01 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x01 , -1 , {0x0F , 0x20 }, {0x11 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00}, {0x04B0, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{64692944895., 0. , 0x00 , 0x01 , 0x01 }, {66571993087., 0. , 0x00 , 0x01 , 0x01 }, {524287. , 0. , 0x00 , 0x01 , 0x00 }, {68718952448., 0. , 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x01 , -1 , {0x10 , 0x20 }, {0x11 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00}, {0x0258, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{68719476732., 0. , 0x00 , 0x01 , 0x01 }, {68719476733., 0. , 0x00 , 0x01 , 0x01 }, {1069563840. , 0. , 0x00 , 0x01 , 0x00 }, {67649912895., 0. , 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x01 , -1 , {0x02 , 0x21 }, {0x03 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00}, {0x012C, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{68719476732., 0. , 0x00 , 0x01 , 0x01 }, {68719476733., 0. , 0x00 , 0x01 , 0x01 }, {477225440. , 0. , 0x00 , 0x01 , 0x00 }, {68242251295., 0. , 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x01 , -1 , {0x02 , 0x21 }, {0x04 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00}, {0x0096, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{68719476732., 0. , 0x00 , 0x01 , 0x01 }, {68719476733., 0. , 0x00 , 0x01 , 0x01 }, {13743895344., 0. , 0x00 , 0x01 , 0x00 }, {54975581391., 0. , 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x01 , -1 , {0x01 , 0x21 }, {0x02 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00}, {0x0960, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{8589934588. , 0. , 0x00 , 0x01 , 0x01 }, {68719476732., 0. , 0x00 , 0x01 , 0x01 }, {134217216. , 0. , 0x01 , 0x01 , 0x00 }, {68585259519., 0. , 0x01 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x01 , -1 , {0x0F , 0x20 }, {0x11 , 0x22 }, {0x00 , 0x00 }, {1048572. , 0. }, 0x00}, {0x04B0, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{64692944895., 0. , 0x00 , 0x01 , 0x01 }, {66571993087., 0. , 0x00 , 0x01 , 0x01 }, {524287. , 0. , 0x00 , 0x01 , 0x00 }, {68718952448., 0. , 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x01 , -1 , {0x10 , 0x20 }, {0x11 , 0x22 }, {0x00 , 0x00 }, {1048572. , 0. }, 0x00}, {0x0258, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{68719476732., 0. , 0x00 , 0x01 , 0x01 }, {68719476733., 0. , 0x00 , 0x01 , 0x01 }, {1069563840. , 0. , 0x00 , 0x01 , 0x00 }, {67649912895., 0. , 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x01 , -1 , {0x02 , 0x21 }, {0x03 , 0x22 }, {0x00 , 0x00 }, {1048572. , 0. }, 0x00}, {0x012C, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{68719476732., 0. , 0x00 , 0x01 , 0x01 }, {68719476733., 0. , 0x00 , 0x01 , 0x01 }, {477225440. , 0. , 0x00 , 0x01 , 0x00 }, {68242251295., 0. , 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x01 , -1 , {0x02 , 0x21 }, {0x04 , 0x22 }, {0x00 , 0x00 }, {1048572. , 0. }, 0x00}, {0x0258, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{68719476732., 0. , 0x00 , 0x01 , 0x01 }, {68719476733., 0. , 0x00 , 0x01 , 0x01 }, {266346464. , 0. , 0x00 , 0x01 , 0x00 }, {68453130271., 0. , 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x01 , -1 , {0x04 , 0x21 }, {0x06 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00}, {0x0960, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{8589934588. , 8589934588. , 0x00 , 0x01 , 0x01 }, {68719476732., 68719476732., 0x00 , 0x01 , 0x01 }, {134217216. , 134217216. , 0x01 , 0x01 , 0x00 }, {68585259519., 68585259519., 0x01 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0xFF , 0x80FF , 0x01 , -1 , {0x0F , 0x20 }, {0x11 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00}, {0x04B0, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{64692944895., 64692944895., 0x00 , 0x01 , 0x01 }, {66571993087., 66571993087., 0x00 , 0x01 , 0x01 }, {524287. , 524287. , 0x00 , 0x01 , 0x00 }, {68718952448., 68718952448., 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0xFF , 0x80FF , 0x01 , -1 , {0x10 , 0x20 }, {0x11 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00}, {0x0258, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{68719476732., 68719476732., 0x00 , 0x01 , 0x01 }, {68719476733., 68719476733., 0x00 , 0x01 , 0x01 }, {534781920. , 534781920. , 0x00 , 0x01 , 0x00 }, {68184694815., 68184694815., 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0xFF , 0x20FF , 0x01 , -1 , {0x05 , 0x21 }, {0x07 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00}, {0x012C, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{68719476732., 68719476732., 0x00 , 0x01 , 0x01 }, {68719476733., 68719476733., 0x00 , 0x01 , 0x01 }, {477225440. , 477225440. , 0x00 , 0x01 , 0x00 }, {68242251295., 68242251295., 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0xFF , 0x11FF , 0x01 , -1 , {0x02 , 0x21 }, {0x04 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00}, {0x0096, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{68719476732., 68719476732., 0x00 , 0x01 , 0x01 }, {68719476733., 68719476733., 0x00 , 0x01 , 0x01 }, {13743895344., 13743895344., 0x00 , 0x01 , 0x00 }, {54975581391., 54975581391., 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0xFF , 0x110F , 0x01 , -1 , {0x01 , 0x21 }, {0x02 , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00}, {0x0960, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{8589934588. , 8589934588. , 0x00 , 0x01 , 0x01 }, {68719476732., 68719476732., 0x00 , 0x01 , 0x01 }, {134217216. , 134217216. , 0x01 , 0x01 , 0x00 }, {68585259519., 68585259519., 0x01 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0xFF , 0x80FF , 0x01 , -1 , {0x0F , 0x20 }, {0x11 , 0x22 }, {0x00 , 0x00 }, {1048572. , 0. }, 0x00}, {0x04B0, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{64692944895., 64692944895., 0x00 , 0x01 , 0x01 }, {66571993087., 66571993087., 0x00 , 0x01 , 0x01 }, {524287. , 524287. , 0x00 , 0x01 , 0x00 }, {68718952448., 68718952448., 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0xFF , 0x80FF , 0x01 , -1 , {0x10 , 0x20 }, {0x11 , 0x22 }, {0x00 , 0x00 }, {1048572. , 0. }, 0x00}, {0x0258, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{68719476732., 68719476732., 0x00 , 0x01 , 0x01 }, {68719476733., 68719476733., 0x00 , 0x01 , 0x01 }, {534781920. , 534781920. , 0x00 , 0x01 , 0x00 }, {68184694815., 68184694815., 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0xFF , 0x20FF , 0x01 , -1 , {0x05 , 0x21 }, {0x07 , 0x22 }, {0x00 , 0x00 }, {1048572. , 0. }, 0x00}, {0x012C, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{68719476732., 68719476732., 0x00 , 0x01 , 0x01 }, {68719476733., 68719476733., 0x00 , 0x01 , 0x01 }, {477225440. , 477225440. , 0x00 , 0x01 , 0x00 }, {68242251295., 68242251295., 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0xFF , 0x110F , 0x01 , -1 , {0x02 , 0x21 }, {0x04 , 0x22 }, {0x00 , 0x00 }, {1048572. , 0. }, 0x00}, {0x0096, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{68719476732., 68719476732., 0x00 , 0x01 , 0x01 }, {68719476733., 68719476733., 0x00 , 0x01 , 0x01 }, {13743895344., 13743895344., 0x00 , 0x01 , 0x00 }, {54975581391., 54975581391., 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0xFF , 0x110F , 0x01 , -1 , {0x01 , 0x21 }, {0x02 , 0x22 }, {0x00 , 0x00 }, {1048572. , 0. }, 0x00}, }; memcpy(reg, &data[tm], sizeof(struct st_timing)); rst = OK; } return rst; } /* ccd timing values for hp3970 scanner */ static SANE_Int hp3970_timing_get(SANE_Int sensortype, SANE_Int tm, struct st_timing *reg) { SANE_Int rst = ERROR; if ((tm < 12)&&(reg != NULL)) { rst = OK; if (sensortype == CCD_SENSOR) { /* Toshiba T2952 sensor timing values for each resolution and color mode */ struct st_timing data[] = { /* res , cnpp, {cvtrp 1 2 3 }, cvtrw, cvtrfpw, cvtrbpw, {{cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}}, cphbp2s, cphbp2e, clamps, clampe , {cdss1, cdss2}, {cdsc1, cdsc2}, {cdscs1, cdscs2}, {adcclkp 1 y 2 }, adcclkp2e */ {0x0960, 0x0B, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {34376515584., 0. , 0x00 , 0x01 , 0x00 }, {8455716864. , 0. , 0x01 , 0x01 , 0x00 }, {60246982656., 0. , 0x01 , 0x01 , 0x01 }, {68719214592., 0. , 0x00 , 0x01 , 0x01 }, {68719214592., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x24000, {0x04 , 0x09 }, {0x06 , 0x0B }, {0x00 , 0x00 }, {27481079808., 0. }, 0x00}, {0x04B0, 0x1A, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {64424511232., 0. , 0x00 , 0x01 , 0x00 }, {68711088128., 0. , 0x00 , 0x01 , 0x00 }, {8388352. , 0. , 0x00 , 0x01 , 0x01 }, {68719476480., 0. , 0x00 , 0x01 , 0x01 }, {68719476480., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x08 , 0x15 }, {0x0C , 0x18 }, {0x00 , 0x00 }, {33351135232., 33351135232.}, 0x01}, {0x0258, 0x17, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {60129570816., 0. , 0x00 , 0x01 , 0x00 }, {68719472640., 0. , 0x00 , 0x01 , 0x01 }, {68719472640., 0. , 0x00 , 0x01 , 0x01 }, {16773120. , 0. , 0x00 , 0x01 , 0x00 }, {68702699520., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x09 , 0x12 }, {0x0C , 0x14 }, {0x00 , 0x00 }, {64677150720., 0. }, 0x00}, {0x012C, 0x17, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {51539611648., 0. , 0x00 , 0x01 , 0x00 }, {68719472640., 0. , 0x00 , 0x01 , 0x01 }, {68719472640., 0. , 0x00 , 0x01 , 0x01 }, {1057222656. , 0. , 0x00 , 0x01 , 0x00 }, {67662249984., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x04 , 0x15 }, {0x06 , 0x17 }, {0x00 , 0x00 }, {8084643840. , 0. }, 0x00}, {0x00C8, 0x1D, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {68182605888., 0. , 0x00 , 0x01 , 0x00 }, {68719476720., 0. , 0x00 , 0x01 , 0x01 }, {68719476720., 0. , 0x00 , 0x01 , 0x01 }, {16659267072., 0. , 0x00 , 0x01 , 0x00 }, {52060209600., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x0A , 0x1B }, {0x0C , 0x1D }, {0x00 , 0x00 }, {4164816768. , 0. }, 0x00}, {0x0960, 0x0B, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {34376515584., 0. , 0x00 , 0x01 , 0x00 }, {8455716864. , 0. , 0x01 , 0x01 , 0x00 }, {60246982656., 0. , 0x01 , 0x01 , 0x01 }, {68719214592., 0. , 0x00 , 0x01 , 0x01 }, {68719214592., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x24000, {0x04 , 0x09 }, {0x06 , 0x0B }, {0x00 , 0x00 }, {2113929216. , 0. }, 0x00}, {0x04B0, 0x1A, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {64424511232., 0. , 0x00 , 0x01 , 0x00 }, {68711088128., 0. , 0x00 , 0x01 , 0x00 }, {8388352. , 0. , 0x00 , 0x01 , 0x01 }, {68719476480., 0. , 0x00 , 0x01 , 0x01 }, {68719476480., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x08 , 0x15 }, {0x0C , 0x18 }, {0x00 , 0x00 }, {67104768. , 67104768. }, 0x01}, {0x0258, 0x17, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {60129570816., 0. , 0x00 , 0x01 , 0x00 }, {68719472640., 0. , 0x00 , 0x01 , 0x01 }, {68719472640., 0. , 0x00 , 0x01 , 0x01 }, {16773120. , 0. , 0x00 , 0x01 , 0x00 }, {68702699520., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x09 , 0x12 }, {0x0C , 0x14 }, {0x00 , 0x00 }, {268369920. , 0. }, 0x00}, {0x012C, 0x17, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {51539611648., 0. , 0x00 , 0x01 , 0x00 }, {68719472640., 0. , 0x00 , 0x01 , 0x01 }, {68719472640., 0. , 0x00 , 0x01 , 0x01 }, {1057222656. , 0. , 0x00 , 0x01 , 0x00 }, {67662249984., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x04 , 0x15 }, {0x06 , 0x17 }, {0x00 , 0x00 }, {33546240. , 0. }, 0x00}, {0x00C8, 0x1D, {0x00, 0x00, 0x00}, 0x1E , 0x01 , 0x0F , {{68585258944., 0. , 0x00 , 0x01 , 0x00 }, {536870784. , 0. , 0x00 , 0x01 , 0x00 }, {16659267072., 0. , 0x00 , 0x01 , 0x00 }, {52060209600., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x01 , 0x00 }, {0. , 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x0A , 0x1B }, {0x0C , 0x1C }, {0x00 , 0x00 }, {4194176. , 0. }, 0x00}, {0x0064, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {34359738368., 0. , 0x00 , 0x01 , 0x00 }, {68719476735., 0. , 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x01 , 0x01 }, {7635497415. , 0. , 0x00 , 0x01 , 0x00 }, {61083979320., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x02 , 0x22 }, {0x03 , 0x23 }, {0x00 , 0x00 }, {2114445438. , 0. }, 0x00}, {0x0064, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {34359738368., 0. , 0x00 , 0x01 , 0x00 }, {68719476735., 0. , 0x00 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x01 , 0x01 }, {7635497415. , 0. , 0x00 , 0x01 , 0x00 }, {61083979320., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x02 , 0x22 }, {0x03 , 0x23 }, {0x00 , 0x00 }, {524286. , 0. }, 0x00} }; memcpy(reg, &data[tm], sizeof(struct st_timing)); } else { /* Sony S575 sensor timing values for each resolution and color mode I haven't found any hp3970 scanner using sony sensor but windows drivers support this case */ struct st_timing data[] = { /* res , cnpp, {cvtrp1 2 3 }, cvtrw, cvtrfpw, cvtrbpw, {{cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}}, cphbp2s, cphbp2e, clamps, clampe , {cdss1, cdss2}, {cdsc1, cdsc2}, {cdscs1, cdscs2}, {adcclkp 1 y 2 }, adcclkp2e */ {0x0960, 0x0B, {0x00, 0x00, 0x00}, 0x1E , 0x01 , 0x0F , {{60112764928., 0. , 0x00 , 0x01 , 0x00 }, {34326183936., 0. , 0x00 , 0x01 , 0x00 }, {1056964608. , 0. , 0x01 , 0x01 , 0x00 }, {67645734912., 0. , 0x01 , 0x01 , 0x01 }, {1056964608. , 0. , 0x00 , 0x01 , 0x00 }, {67645734912., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x24000, {0x05 , 0x09 }, {0x07 , 0x0b }, {0x00 , 0x00 }, {27481079808., 0. }, 0x00}, {0x04B0, 0x1A, {0x00, 0x00, 0x00}, 0x1E , 0x01 , 0x0F , {{68316823296., 0. , 0x00 , 0x01 , 0x00 }, {42949672704., 0. , 0x00 , 0x01 , 0x00 }, {4194048. , 0. , 0x00 , 0x01 , 0x00 }, {68715282432., 0. , 0x00 , 0x01 , 0x01 }, {4194048. , 0. , 0x00 , 0x01 , 0x00 }, {68715282432., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x0B , 0x17 }, {0x0D , 0x19 }, {0x00 , 0x00 }, {16675567616., 16675567616.}, 0x01}, {0x0258, 0x17, {0x00, 0x00, 0x00}, 0x1E , 0x01 , 0x0F , {{67914166272., 0. , 0x00 , 0x01 , 0x00 }, {42949668864., 0. , 0x00 , 0x01 , 0x00 }, {16773120. , 0. , 0x00 , 0x01 , 0x00 }, {68702699520., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x01 , 0x00 }, {0. , 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x09 , 0x15 }, {0x0B , 0x17 }, {0x00 , 0x00 }, {8084643840. , 0. }, 0x00}, {0x012C, 0x17, {0x00, 0x00, 0x00}, 0x1E , 0x01 , 0x0F , {{60129538048., 0. , 0x00 , 0x01 , 0x00 }, {34359730176., 0. , 0x00 , 0x01 , 0x00 }, {1057222656. , 0. , 0x00 , 0x01 , 0x00 }, {67662249984., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x01 , 0x00 }, {0. , 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x04 , 0x15 }, {0x06 , 0x17 }, {0x00 , 0x00 }, {8084643840. , 0. }, 0x00}, {0x00C8, 0x1D, {0x00, 0x00, 0x00}, 0x1E , 0x01 , 0x0F , {{68585258944., 0. , 0x00 , 0x01 , 0x00 }, {536870784. , 0. , 0x00 , 0x01 , 0x00 }, {16659267072., 0. , 0x00 , 0x01 , 0x00 }, {52060209600., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x01 , 0x00 }, {0. , 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x0A , 0x1A }, {0x0C , 0x1C }, {0x00 , 0x00 }, {8329633536. , 0. }, 0x00}, {0x0960, 0x0B, {0x00, 0x00, 0x00}, 0x1E , 0x01 , 0x0F , {{60112764928., 0. , 0x00 , 0x01 , 0x00 }, {34326183936., 0. , 0x00 , 0x01 , 0x00 }, {1056964608. , 0. , 0x01 , 0x01 , 0x00 }, {67645734912., 0. , 0x01 , 0x01 , 0x01 }, {1056964608. , 0. , 0x00 , 0x01 , 0x00 }, {67645734912., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x24000, {0x05 , 0x09 }, {0x07 , 0x0B }, {0x00 , 0x00 }, {2113929216. , 0. }, 0x00}, {0x04B0, 0x1A, {0x00, 0x00, 0x00}, 0x1E , 0x01 , 0x0F , {{68316823296., 0. , 0x00 , 0x01 , 0x00 }, {42949672704., 0. , 0x00 , 0x01 , 0x00 }, {4194048. , 0. , 0x00 , 0x01 , 0x00 }, {68715282432., 0. , 0x00 , 0x01 , 0x01 }, {4194048. , 0. , 0x00 , 0x01 , 0x00 }, {68715282432., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x0B , 0x17 }, {0x0D , 0x19 }, {0x00 , 0x00 }, {33552384. , 33552384. }, 0x01}, {0x0258, 0x17, {0x00, 0x00, 0x00}, 0x1E , 0x01 , 0x0F , {{67914166272., 0. , 0x00 , 0x01 , 0x00 }, {42949668864., 0. , 0x00 , 0x01 , 0x00 }, {16773120. , 0. , 0x00 , 0x01 , 0x00 }, {68702699520., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x01 , 0x00 }, {0. , 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x09 , 0x15 }, {0x0B , 0x17 }, {0x00 , 0x00 }, {33546240. , 0. }, 0x00}, {0x012C, 0x17, {0x00, 0x00, 0x00}, 0x1E , 0x01 , 0x0F , {{60129538048., 0. , 0x00 , 0x01 , 0x00 }, {34359730176., 0. , 0x00 , 0x01 , 0x00 }, {34359730176., 0. , 0x00 , 0x01 , 0x00 }, {67662249984., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x01 , 0x00 }, {0. , 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x04 , 0x15 }, {0x06 , 0x17 }, {0x00 , 0x00 }, {33546240. , 0. }, 0x00}, {0x00C8, 0x1D, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {68182605888., 0. , 0x00 , 0x01 , 0x00 }, {68719476720., 0. , 0x00 , 0x01 , 0x01 }, {68719476720., 0. , 0x00 , 0x01 , 0x01 }, {16659267072., 0. , 0x00 , 0x01 , 0x00 }, {52060209600., 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x0A , 0x1A }, {0x0C , 0x1D }, {0x00 , 0x00 }, {4194176. , 0. }, 0x00}, {0x0064, 0x23, {0x00, 0x00, 0x00}, 0x1E , 0x01 , 0x0F , {{51539607551., 0. , 0x00 , 0x01 , 0x00 }, {34359738366., 0. , 0x00 , 0x01 , 0x00 }, {7635497415. , 0. , 0x00 , 0x01 , 0x00 }, {61083979320., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x01 , 0x00 }, {0. , 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x02700, {0x02 , 0x22 }, {0x03 , 0x23 }, {0x00 , 0x00 }, {2114445438. , 0. }, 0x00}, {0x0064, 0x23, {0x00, 0x00, 0x00}, 0x1E , 0x01 , 0x0F , {{51539607551., 0. , 0x00 , 0x01 , 0x00 }, {34359738366., 0. , 0x00 , 0x01 , 0x00 }, {7635497415. , 0. , 0x00 , 0x01 , 0x00 }, {61083979320., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x01 , 0x00 }, {0. , 0. , 0x00 , 0x01 , 0x01 }}, 0x00 , 0x00 , 0x01 , 0x02700, {0x02 , 0x22 }, {0x03 , 0x23 }, {0x00 , 0x00 }, {524286. , 0. }, 0x00} }; memcpy(reg, &data[tm], sizeof(struct st_timing)); } } return rst; } /* ccd timing values for hp4370 scanner */ static SANE_Int hp4370_timing_get(SANE_Int tm, struct st_timing *reg) { SANE_Int rst = ERROR; if ((reg != NULL)&&(tm < 14)) { /* Toshiba T2958 sensor timing values for each resolution and color mode */ struct st_timing data[] = { /* res , cnpp, {cvtrp 1 2 3 }, cvtrw, cvtrfpw, cvtrbpw, {{cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2 , cphps, cphge, cphgo}, {cphp1 , cphp2 , cphps, cphge, cphgo}, {cphp1 , cphp2 , cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}}, cphbp2s, cphbp2e, clamps, clampe , {cdss1, cdss2}, {cdsc1, cdsc2}, {cdscs1, cdscs2}, {adcclkp 1 y 2 }, adcclkp2e */ {0x12C0, 0x17, {0x00, 0x00, 0x00}, 0x0F , 0x05 , 0x0E , {{0. , 0. , 0x00 , 0x01 , 0x01 }, {17179869183., 17179869183., 0x00 , 0x01 , 0x01 }, {1073479680. , 1073479680., 0x01 , 0x01 , 0x00 }, {67645997055., 67645997055., 0x01 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x5D5B , 0xBAB7 , 0x1A , -1 , {0x08 , 0x15 }, {0x0A , 0x17 }, {0x00 , 0x00 }, {8084644321. , 8084644321. }, 0x00}, {0x0960, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x05 , 0x0E , {{0. , 0. , 0x00 , 0x01 , 0x01 }, {17179869182., 0. , 0x00 , 0x01 , 0x01 }, {268434432. , 0. , 0x01 , 0x01 , 0x00 }, {68451042303., 0. , 0x01 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x00 , 0x00 }, {0. , 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x1A , -1 , {0x07 , 0x18 }, {0x0B , 0x1E }, {0x00 , 0x00 }, {67662254016., 0. }, 0x00}, {0x04B0, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {68719475967., 0. , 0x00 , 0x01 , 0x01 }, {1048572. , 0. , 0x00 , 0x01 , 0x01 }, {68718428163., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x01 , 0x00 }, {0. , 0. , 0x00 , 0x01 , 0x00 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x1F , 0x17 }, {0x21 , 0x19 }, {0x01 , 0x01 }, {34888349727., 0. }, 0x00}, {0x0258, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {68719476731., 0. , 0x00 , 0x01 , 0x01 }, {4261672960. , 0. , 0x00 , 0x01 , 0x00 }, {64457803775., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x00 , 0x00 }, {0. , 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x1A , 0x05500, {0x00 , 0x1F }, {0x03 , 0x21 }, {0x00 , 0x00 }, {8457781752. , 0. }, 0x00}, {0x012C, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {51808043007., 0. , 0x00 , 0x01 , 0x01 }, {8084644320. , 0. , 0x00 , 0x01 , 0x00 }, {60634832415., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x00 , 0x00 }, {0. , 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x1A , 0x05500, {0x09 , 0x20 }, {0x0A , 0x22 }, {0x00 , 0x00 }, {4228890876. , 0. }, 0x00}, {0x12C0, 0x17, {0x00, 0x00, 0x00}, 0x0F , 0x05 , 0x0E , {{0. , 0. , 0x00 , 0x01 , 0x01 }, {17179869183., 17179869183., 0x00 , 0x01 , 0x01 }, {1073479680. , 1073479680., 0x01 , 0x01 , 0x00 }, {67645997055., 67645997055., 0x01 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x5D5B , 0xBAB7 , 0x1A , -1 , {0x08 , 0x15 }, {0x0A , 0x17 }, {0x00 , 0x00 }, {33546240. , 33546240. }, 0x00}, {0x0960, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x05 , 0x0E , {{0. , 0. , 0x00 , 0x01 , 0x01 }, {17179869182., 0. , 0x00 , 0x01 , 0x01 }, {268434432. , 0. , 0x01 , 0x01 , 0x00 }, {68451042303., 0. , 0x01 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x00 , 0x00 }, {0. , 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x1A , -1 , {0x07 , 0x18 }, {0x0B , 0x1E }, {0x00 , 0x00 }, {16777152. , 0. }, 0x00}, {0x04B0, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {68719475967., 0. , 0x00 , 0x01 , 0x01 }, {1048572. , 0. , 0x00 , 0x01 , 0x01 }, {68718428163., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x01 , 0x00 }, {0. , 0. , 0x00 , 0x01 , 0x00 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x1F , 0x17 }, {0x21 , 0x19 }, {0x01 , 0x01 }, {536868864. , 0. }, 0x00}, {0x0258, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {68719476731., 0. , 0x00 , 0x01 , 0x01 }, {4261672960. , 0. , 0x00 , 0x01 , 0x00 }, {64457803775., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x00 , 0x00 }, {0. , 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x1A , 0x05500, {0x00 , 0x1F }, {0x03 , 0x21 }, {0x00 , 0x00 }, {2097144. , 0. }, 0x00}, {0x012C, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x04 , 0x04 , {{0. , 0. , 0x00 , 0x01 , 0x00 }, {51808043007., 0. , 0x00 , 0x01 , 0x01 }, {8084644320. , 0. , 0x00 , 0x01 , 0x00 }, {60634832415., 0. , 0x00 , 0x01 , 0x01 }, {0. , 0. , 0x00 , 0x00 , 0x00 }, {0. , 0. , 0x00 , 0x00 , 0x00 }}, 0x00 , 0x00 , 0x1A , 0x05500, {0x09 , 0x20 }, {0x0A , 0x22 }, {0x00 , 0x00 }, {1048572. , 0. }, 0x00}, {0x12C0, 0x17, {0x00, 0x00, 0x00}, 0x0F , 0x05 , 0x0E , {{0. , 0. , 0x00 , 0x01 , 0x01 }, {17179869183., 17179869183., 0x00 , 0x01 , 0x01 }, {1073479680. , 1073479680., 0x01 , 0x01 , 0x00 }, {67645997055., 67645997055., 0x01 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x6977 , 0xD2EF , 0x1A , -1 , {0x07 , 0x15 }, {0x0A , 0x17 }, {0x00 , 0x00 }, {8084644321. , 8084644321. }, 0x00}, {0x12C0, 0x17, {0x00, 0x00, 0x00}, 0x0F , 0x05 , 0x0E , {{0. , 0. , 0x00 , 0x01 , 0x01 }, {17179869183., 17179869183., 0x00 , 0x01 , 0x01 }, {1073479680. , 1073479680., 0x01 , 0x01 , 0x00 }, {67645997055., 67645997055., 0x01 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x6977 , 0xD2EF , 0x1A , -1 , {0x07 , 0x15 }, {0x0A , 0x17 }, {0x00 , 0x00 }, {33546240. , 33546240. }, 0x00}, {0x12C0, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x05 , 0x0E , {{0. , 0. , 0x00 , 0x01 , 0x01 }, {8589934591. , 8589934591. , 0x00 , 0x01 , 0x01 }, {134217216. , 134217216. , 0x01 , 0x01 , 0x00 }, {68585259519., 68585259519., 0x01 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x765B , 0xECB7 , 0x1A , -1 , {0x07 , 0x1B }, {0x0D , 0x21 }, {0x00 , 0x00 }, {8457781752. , 8457781752. }, 0x00}, {0x12C0, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x05 , 0x0E , {{0. , 0. , 0x00 , 0x01 , 0x01 }, {8589934591. , 8589934591. , 0x00 , 0x01 , 0x01 }, {134217216. , 134217216. , 0x01 , 0x01 , 0x00 }, {68585259519., 68585259519., 0x01 , 0x01 , 0x01 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }, {68719476735., 0. , 0x00 , 0x00 , 0x00 }}, 0x765B , 0xECB7 , 0x1A , -1 , {0x07 , 0x1B }, {0x0D , 0x21 }, {0x00 , 0x00 }, {2097144. , 2097144. }, 0x00} }; memcpy(reg, &data[tm], sizeof(struct st_timing)); rst = OK; } return rst; } /* ccd timing values for ua4900 scanner */ static SANE_Int ua4900_timing_get(SANE_Int tm, struct st_timing *reg) { SANE_Int rst = ERROR; if ((tm < 10)&&(reg != NULL)) { /* Sony S575 sensor timing values for each resolution and color mode */ struct st_timing data[] = { /* res , cnpp, {cvtrp1 2 3 }, cvtrw, cvtrfpw, cvtrbpw, {{cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}, {cphp1 , cphp2, cphps, cphge, cphgo}}, cphbp2s, cphbp2e, clamps, clampe , {cdss1, cdss2}, {cdsc1, cdsc2}, {cdscs1, cdscs2}, {adcclkp 1 y 2 }, adcclkp2e */ {0x04b0, 0x23, {0x00, 0x00, 0x00}, 0x1E , 0x03 , 0x03 , {{ 1073737728., 0. , 0x01 , 0x01 , 0x00 }, {67645739007., 0. , 0x01 , 0x01 , 0x01 }, {67645739007., 0. , 0x01 , 0x01 , 0x01 }, { 1073737728., 0. , 0x01 , 0x01 , 0x00 }, {25769803776., 0. , 0x00 , 0x01 , 0x00 }, { 62., 0. , 0x00 , 0x01 , 0x00 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x08 , 0x1c }, {0x0A , 0x1e }, {0x00 , 0x00 }, {67662254016., 0. }, 0x00}, {0x0258, 0x23, {0x00, 0x00, 0x00}, 0x0F , 0x03 , 0x03 , {{ 262143., 0. , 0x00 , 0x01 , 0x00 }, {68719214592., 0. , 0x00 , 0x01 , 0x01 }, {68719214592., 0. , 0x00 , 0x01 , 0x01 }, { 262143., 3. , 0x00 , 0x01 , 0x00 }, { 2080374784., 0. , 0x00 , 0x01 , 0x00 }, {64424509455., 0. , 0x00 , 0x01 , 0x00 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x10 , 0x1d }, {0x12 , 0x1f }, {0x00 , 0x00 }, {33831127008., 0. }, 0x00}, {0x012c, 0x23, {0x00, 0x00, 0x00}, 0x1E , 0x03 , 0x03 , {{ 32767., 0. , 0x00 , 0x01 , 0x00 }, {68182605824., 0. , 0x00 , 0x01 , 0x01 }, { 2130837500., 0. , 0x00 , 0x01 , 0x01 }, {66588639235., 0. , 0x00 , 0x01 , 0x00 }, { 117440512., 0. , 0x00 , 0x01 , 0x00 }, {68182605825., 0. , 0x00 , 0x01 , 0x00 }}, 0x00 , 0x00 , 0x01 , 0x02700, {0x12 , 0x1f }, {0x14 , 0x21 }, {0x00 , 0x00 }, { 8457781752., 0. }, 0x00}, {0x00c8, 0x23, {0x00, 0x00, 0x00}, 0x1E , 0x03 , 0x03 , {{51539608575., 0. , 0x00 , 0x01 , 0x00 }, {16642998272., 0. , 0x00 , 0x01 , 0x01 }, { 2082408447., 0. , 0x00 , 0x01 , 0x01 }, {66637068288., 0. , 0x00 , 0x01 , 0x00 }, { 14680064., 0. , 0x00 , 0x01 , 0x00 }, {68652367872., 0. , 0x00 , 0x01 , 0x00 }}, 0x00 , 0x00 , 0x01 , 0x02700, {0x10 , 0x1f }, {0x12 , 0x21 }, {0x00 , 0x00 }, { 8457781752., 0. }, 0x00}, {0x0064, 0x23, {0x00, 0x00, 0x00}, 0x1E , 0x03 , 0x03 , {{51539608575., 0. , 0x00 , 0x01 , 0x00 }, {16642998272., 0. , 0x00 , 0x01 , 0x01 }, { 2082408447., 0. , 0x00 , 0x01 , 0x01 }, {66637068288., 0. , 0x00 , 0x01 , 0x00 }, { 14680064., 0. , 0x00 , 0x01 , 0x00 }, {68652367872., 0. , 0x00 , 0x01 , 0x00 }}, 0x00 , 0x00 , 0x01 , 0x02700, {0x10 , 0x1f }, {0x12 , 0x21 }, {0x00 , 0x00 }, { 8457781752., 0. }, 0x00}, {0x04b0, 0x23, {0x00, 0x00, 0x00}, 0x1E , 0x03 , 0x03 , {{ 268434432., 0. , 0x01 , 0x01 , 0x00 }, {68451042303., 0. , 0x01 , 0x01 , 0x01 }, {68451042303., 0. , 0x01 , 0x01 , 0x01 }, { 268434432., 0. , 0x01 , 0x01 , 0x00 }, {51539607552., 0. , 0x00 , 0x01 , 0x00 }, {30. , 0. , 0x00 , 0x01 , 0x00 }}, 0x00 , 0x00 , 0x01 , 0x11000, {0x08 , 0x1d }, {0x09 , 0x1e }, {0x00 , 0x00 }, {16777152. , 0. }, 0x00}, {0x0258, 0x1d, {0x00, 0x00, 0x00}, 0x0f , 0x03 , 0x03 , {{ 2097088., 0. , 0x00 , 0x01 , 0x00 }, {68717379632., 0. , 0x00 , 0x01 , 0x01 }, {68717379632., 0. , 0x00 , 0x01 , 0x01 }, { 2097088., 3. , 0x00 , 0x01 , 0x00 }, { 1879048192., 0. , 0x00 , 0x01 , 0x00 }, {60129542592., 0. , 0x00 , 0x01 , 0x00 }}, 0x00 , 0x00 , 0x01 , 0x05500, {0x0d , 0x16 }, {0x0f , 0x18 }, {0x00 , 0x00 }, {134213632. , 0. }, 0x00}, {0x012c, 0x23, {0x00, 0x00, 0x00}, 0x1E , 0x03 , 0x03 , {{64424510463., 0. , 0x00 , 0x01 , 0x00 }, { 4278190080., 0. , 0x00 , 0x01 , 0x01 }, { 267390975., 0. , 0x00 , 0x01 , 0x01 }, {68452085760., 0. , 0x00 , 0x01 , 0x00 }, { 6291456., 0. , 0x00 , 0x01 , 0x00 }, {68652367872., 0. , 0x00 , 0x01 , 0x00 }}, 0x00 , 0x00 , 0x01 , 0x02700, {0x15 , 0x1f }, {0x17 , 0x21 }, {0x00 , 0x00 }, {2097144. , 0. }, 0x00}, {0x00C8, 0x23, {0x00, 0x00, 0x00}, 0x1E , 0x03 , 0x03 , {{51539608575., 0. , 0x00 , 0x01 , 0x00 }, {16642998272., 0. , 0x00 , 0x01 , 0x01 }, { 2082408447., 0. , 0x00 , 0x01 , 0x01 }, {66637068288., 0. , 0x00 , 0x01 , 0x00 }, { 14680064., 0. , 0x00 , 0x01 , 0x00 }, {68652367872., 0. , 0x00 , 0x01 , 0x00 }}, 0x00 , 0x00 , 0x01 , 0x02700, {0x17 , 0x1f }, {0x19 , 0x21 }, {0x00 , 0x00 }, {2097144. , 0. }, 0x00}, {0x0064, 0x23, {0x00, 0x00, 0x00}, 0x1E , 0x03 , 0x03 , {{51539608575., 0. , 0x00 , 0x01 , 0x00 }, {16642998272., 0. , 0x00 , 0x01 , 0x01 }, { 2082408447., 0. , 0x00 , 0x01 , 0x01 }, {66637068288., 0. , 0x00 , 0x01 , 0x00 }, { 14680064., 0. , 0x00 , 0x01 , 0x00 }, {68652367872., 0. , 0x00 , 0x01 , 0x00 }}, 0x00 , 0x00 , 0x01 , 0x02700, {0x17 , 0x1f }, {0x19 , 0x21 }, {0x00 , 0x00 }, {2097144. , 0. }, 0x00} }; memcpy(reg, &data[tm], sizeof(struct st_timing)); rst = OK; } return rst; } static SANE_Int cfg_timing_get(SANE_Int sensortype, SANE_Int tm, struct st_timing *reg) { /* return timing values for current device */ SANE_Int rst = ERROR; switch(RTS_Debug->dev_model) { case BQ5550: rst = bq5550_timing_get(tm, reg); break; case UA4900: rst = ua4900_timing_get(tm, reg); break; case HP4370: case HPG3010: case HPG3110: rst = hp4370_timing_get(tm, reg); break; case HP3800: case HPG2710: rst = hp3800_timing_get(tm, reg); break; default: /* hp3970 and hp4070 */ rst = hp3970_timing_get(sensortype, tm, reg); break; } return rst; } /** SEC: Motor curves ---------- */ static SANE_Int *bq5550_motor() { SANE_Int *rst = NULL; SANE_Int steps[] = { /* motorcurve 1 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN, 20000 ,6323 ,4095 ,3270 ,2823 ,2533 ,2318 ,2151 ,2016 ,1905 ,1811 ,1730 ,1660 ,1599 ,1544 ,1494 ,1450 ,1409 ,1373 ,1339 ,1307 ,1278 ,1251 ,1225 ,1201 ,1179 ,1158 ,1138 ,1119 ,1101 ,1084 ,1067 ,1052 ,1037 ,1023 ,1009 ,996 ,984 ,972 ,960 ,949 ,938 ,928 ,918 ,909 ,899 ,890 ,882 ,873 ,865 ,857 ,849 ,842 ,835 ,828 ,821 ,814 ,807 ,801 ,795 ,789 ,783 ,777 ,772 ,766 ,761 ,756 ,751 ,746 ,741 ,736 ,731 ,727 ,722 ,718 ,714 ,709 ,705 ,701 ,697 ,693 ,690 ,686 ,682 ,678 ,675 ,671 ,668 ,665 ,661 ,658 ,655 ,652 ,649 ,645 ,642 ,640 ,637 ,634 ,631 ,628 ,625 ,623 ,620 ,617 ,615 ,612 ,610 ,607 ,605 ,602 ,600 ,597 ,595 ,593 ,591 ,588 ,586 ,584 ,582 ,580 ,577 ,575 ,573 ,571 ,569 ,567 ,565 ,563 ,562 ,560 ,558 ,556 ,554 ,552 ,550 ,549 ,547 ,545 ,543 ,542 ,540 ,538 ,537 ,535 ,534 ,532 ,530 ,529 ,527 ,526 ,524 ,523 ,521 ,520 ,518 ,517 ,515 ,514 ,513 ,511 ,510 ,508 ,507 ,506 ,504 ,503 ,502 ,501 ,499 ,498 ,497 ,495 ,494 ,493 ,492 ,491 ,489 ,488 ,487 ,486 ,485 ,484 ,482 ,481 ,480 ,479 ,478 ,477 ,476 ,475 ,474 ,473 ,472 ,470 ,469 ,468 ,467 ,466 ,465 ,464 ,463 ,462 ,461 ,460 ,460 ,459 ,458 ,457 ,456 ,455 ,454 ,453 ,452 ,451 ,450 ,449 ,449 ,448 ,447 ,446 ,445 ,444 ,443 ,443 ,442 ,441 ,440 ,439 ,438 ,438 ,437 ,436 ,435 ,434 ,434 ,433 ,432 ,431 ,431 ,430 ,429 ,428 ,428 ,427 ,426 ,425 ,425 ,424 ,423 ,422 ,422 ,421 ,420 ,420 ,419 ,418 ,418 ,417 ,416 ,416 ,415 ,414 ,414 ,413 ,412 ,412 ,411 ,410 ,410 ,409 ,408 ,408 ,407 ,407 ,406 ,405 ,405 ,404 ,404 ,403 ,402 ,402 ,401 ,401 ,400 ,399 ,399 ,398 ,398 ,397 ,397 ,396 ,395 ,395 ,394 ,394 ,393 ,393 ,392 ,392 ,391 ,390 ,390 ,389 ,389 ,388 ,388 ,387 ,387 ,386 ,386 ,385 ,385 ,384 ,384 ,383 ,383 ,382 ,382 ,381 ,381 ,380 ,380 ,379 ,379 ,378 ,378 ,377 ,377 ,377 ,376 ,376 ,375 ,375 ,374 ,374 ,373 ,373 ,372 ,372 ,372 ,371 ,371 ,370 ,370 ,369 ,369 ,368 ,368 ,368 ,367 ,367 ,366 ,366 ,365 ,365 ,365 ,364 ,364 ,363 ,363 ,363 ,362 ,362 ,361 ,361 ,361 ,360 ,360 ,359 ,359 ,359 ,358 ,358 ,357 ,357 ,357 ,356 ,356 ,356 ,355 ,355 ,354 ,354 ,354 ,353 ,353 ,353 ,352 ,352 ,351 ,351 ,351 ,350 ,350 ,350 ,349 ,349 ,349 ,348 ,348 ,348 ,347 ,347 ,347 ,346 ,346 ,346 ,345 ,345 ,345 ,344 ,344 ,344 ,343 ,343 ,343 ,342 ,342 ,342 ,341 ,341 ,341 ,340 ,340 ,340 ,339 ,339 ,339 ,338 ,338 ,338 ,337 ,337 ,337 ,337 ,336 ,336 ,336 ,335 ,335 ,335 ,334 ,334 ,334 ,334 ,333 ,333 ,333 ,332 ,332 ,332 ,332 ,331 ,331 ,331 ,330 ,330 ,330 ,330 ,329 ,329 ,329 ,328 ,328 ,328 ,328 ,327 ,327 ,327 ,326 ,326 ,326 ,326 ,325 ,325 ,325 ,325 ,324 ,324 ,324 ,324 ,323 ,323 ,323 ,323 ,322 ,322 ,322 ,321 ,321 ,321 ,321 ,320 ,320 ,320 ,320 ,319 ,319 ,319 ,319 ,318 ,318 ,318 ,318 ,317 ,317 ,317 ,317 ,317 ,316 ,316 ,316 ,316 ,315 ,315 ,315 ,315 ,314 ,314 ,314 ,314 ,313 ,313 ,313 ,313 ,313 ,312 ,312 ,312 ,312 ,311 ,311 ,311 ,311 ,311 ,310 ,310 ,310 ,310 ,309 ,309 ,309 ,309 ,309 ,308 ,308 ,308 ,308 ,307 ,307 ,307 ,307 ,307 ,306 ,306 ,306 ,306 ,306 ,305 ,305 ,305 ,305 ,305 ,304 ,304 ,304 ,304 ,303 ,303 ,303 ,303 ,303 ,302 ,302 ,302 ,302 ,302 ,301 ,301 ,301 ,301 ,301 ,300 ,300 ,300 ,300 ,300 ,300 ,299 ,299 ,299 ,299 ,299 ,298 ,298 ,298 ,298 ,298 ,297 ,297 ,297 ,297 ,297 ,297 ,296 ,296 ,296 ,296 ,296 ,295 ,295 ,295 ,295 ,295 ,295 ,294 ,294 ,294 ,294 ,294 ,293 ,293 ,293 ,293 ,293 ,293 ,292 ,292 ,292 ,292 ,292 ,292 ,291 ,291 ,291 ,291 ,291 ,290 ,290 ,290 ,290 ,290 ,290 ,289 ,289 ,289 ,289 ,289 ,289 ,288 ,288 ,288 ,288 ,288 ,288 ,288 ,287 ,287 ,287 ,287 ,287 ,287 ,286 ,286 ,286 ,286 ,286 ,286 ,285 ,285 ,285 ,285 ,285 ,285, 0, ACC_CURVE,CRV_SMEARING , 4000, 3877, 3377, 2777, 2127, 1724, 1449, 1250, 0, DEC_CURVE,CRV_NORMALSCAN, 479 ,500 ,547 ,607 ,682 ,766 ,865 ,950 ,1040 ,1200 ,1369 ,1580 ,1810 ,2087 ,2500 ,3048 ,3877 ,4818 ,5822 ,6896, 0, DEC_CURVE,CRV_SMEARING , 1250, 1449, 1724, 2127, 2777, 3377, 3877, 4000, 0, DEC_CURVE,CRV_BUFFERFULL, 479 ,500 ,547 ,607 ,682 ,766 ,865 ,950 ,1040 ,1200 ,1369 ,1580 ,1810 ,2087 ,2500 ,3048 ,3877 ,4818 ,5822 ,6896, 0, -1 }; rst = (SANE_Int *)malloc(sizeof(steps)); if (rst != NULL) memcpy(rst, &steps, sizeof(steps)); return rst; } static SANE_Int *hp4370_motor() { SANE_Int *rst = NULL; SANE_Int steps[] = { /* motorcurve 1 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN, 2000, 1984, 1968, 1953, 1937, 1921, 1906, 1890, 1874, 1859, 1843, 1827, 1812, 1796, 1781, 1765, 1749, 1734, 1715, 1700, 1684, 1669, 1653, 1637, 1622, 1606, 1590, 1572, 1556, 1541, 1525, 1510, 1494, 1478, 1463, 1447, 1431, 1416, 1400, 1384, 1366, 1351, 1335, 1319, 1304, 1288, 1272, 1257, 1241, 1225, 1210, 1194, 1179, 1160, 1145, 1129, 1113, 1098, 1082, 1066, 1051, 1035, 1017, 1001, 986, 970, 954, 939, 923, 907, 892, 876, 861, 845, 829, 814, 798, 782, 767, 749, 0, ACC_CURVE,CRV_PARKHOME , 4705, 2913, 2253, 1894, 1662, 1498, 1374, 1276, 1196, 1129, 1073, 1024, 981, 944, 910, 880, 852, 827, 804, 783, 764, 746, 729, 713, 699, 685, 672, 659, 648, 637, 626, 616, 607, 598, 589, 581, 573, 565, 558, 551, 544, 538, 532, 526, 520, 514, 509, 503, 500, 0, ACC_CURVE,CRV_SMEARING , 200, 12, 14, 16, 0, DEC_CURVE,CRV_NORMALSCAN, 749, 1166, 1583, 2000, 0, DEC_CURVE,CRV_PARKHOME , 500, 503, 509, 514, 520, 526, 532, 538, 544, 551, 558, 565, 573, 581, 589, 598, 607, 616, 626, 637, 648, 659, 672, 685, 699, 713, 729, 746, 764, 783, 804, 827, 852, 880, 910, 944, 981, 1024, 1073, 1129, 1196, 1276, 1374, 1498, 1662, 1894, 2253, 2913, 4705, 0, DEC_CURVE,CRV_SMEARING , 300, 234, 167, 100, 0, DEC_CURVE,CRV_BUFFERFULL, 1100, 867, 633, 400, 0, -2, /* motorcurve 2 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN, 4705, 2664, 2061, 1732, 1521, 1370, 1257, 1167, 1094, 1033, 982, 937, 898, 864, 833, 805, 780, 757, 736, 717, 699, 683, 667, 653, 640, 627, 615, 604, 593, 583, 574, 564, 556, 547, 540, 532, 525, 518, 511, 505, 499, 493, 487, 481, 476, 471, 466, 461, 456, 452, 447, 443, 439, 435, 431, 427, 424, 420, 417, 413, 410, 407, 403, 400, 397, 394, 391, 389, 386, 383, 381, 378, 375, 373, 371, 368, 366, 364, 361, 359, 357, 355, 353, 351, 349, 347, 345, 343, 341, 339, 338, 336, 334, 332, 331, 329, 327, 326, 324, 323, 321, 320, 318, 317, 315, 314, 312, 311, 310, 308, 307, 306, 304, 303, 302, 301, 299, 298, 297, 296, 295, 293, 292, 291, 290, 289, 288, 287, 286, 285, 284, 283, 282, 281, 280, 279, 278, 277, 276, 275, 274, 273, 272, 271, 270, 270, 269, 268, 267, 266, 265, 264, 264, 263, 262, 261, 260, 260, 259, 258, 257, 257, 256, 255, 255, 254, 253, 252, 252, 251, 250, 250, 249, 248, 248, 247, 246, 246, 245, 244, 244, 243, 242, 242, 241, 241, 240, 239, 239, 238, 238, 237, 237, 236, 235, 235, 234, 234, 233, 233, 232, 232, 231, 231, 230, 230, 229, 229, 228, 227, 227, 227, 226, 226, 225, 225, 224, 224, 223, 223, 222, 222, 221, 221, 220, 220, 219, 219, 219, 218, 218, 217, 217, 216, 216, 216, 215, 215, 214, 214, 214, 213, 213, 212, 212, 212, 211, 211, 210, 210, 210, 209, 209, 208, 208, 208, 207, 207, 207, 206, 206, 206, 205, 205, 204, 204, 204, 203, 203, 203, 202, 202, 202, 201, 201, 201, 200, 200, 200, 199, 199, 199, 198, 198, 198, 197, 197, 197, 197, 196, 196, 196, 195, 195, 195, 194, 194, 194, 194, 193, 193, 193, 192, 192, 192, 192, 191, 191, 191, 190, 190, 190, 190, 189, 189, 189, 188, 188, 188, 188, 187, 187, 187, 187, 186, 186, 186, 186, 185, 185, 185, 185, 184, 184, 184, 184, 183, 183, 183, 183, 182, 182, 182, 182, 181, 181, 181, 181, 181, 180, 180, 180, 180, 179, 179, 179, 179, 178, 178, 178, 178, 178, 177, 177, 177, 177, 177, 176, 176, 176, 176, 175, 175, 175, 175, 175, 174, 174, 174, 174, 174, 173, 173, 173, 173, 173, 172, 172, 172, 172, 172, 171, 171, 171, 0, ACC_CURVE,CRV_PARKHOME , 4705, 2913, 2253, 1894, 1662, 1498, 1374, 1276, 1196, 1129, 1073, 1024, 981, 944, 910, 880, 852, 827, 804, 783, 764, 746, 729, 713, 699, 685, 672, 659, 648, 637, 626, 616, 607, 598, 589, 581, 573, 565, 558, 551, 544, 538, 532, 526, 520, 514, 509, 503, 500, 0, ACC_CURVE,CRV_SMEARING , 200, 12, 14, 16, 0, DEC_CURVE,CRV_NORMALSCAN, 171, 172, 172, 173, 174, 174, 175, 176, 176, 177, 178, 178, 179, 180, 181, 181, 182, 183, 184, 184, 185, 186, 187, 188, 189, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, 211, 213, 214, 215, 217, 218, 219, 221, 222, 224, 225, 227, 228, 230, 232, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255, 258, 260, 262, 265, 268, 270, 273, 276, 279, 282, 285, 289, 292, 296, 299, 303, 307, 311, 316, 320, 325, 330, 335, 341, 347, 353, 359, 366, 373, 381, 390, 398, 408, 418, 429, 441, 455, 469, 485, 503, 523, 545, 571, 601, 636, 678, 730, 796, 883, 1005, 1195, 1544, 4705, 0, DEC_CURVE,CRV_PARKHOME , 500, 503, 509, 514, 520, 526, 532, 538, 544, 551, 558, 565, 573, 581, 589, 598, 607, 616, 626, 637, 648, 659, 672, 685, 699, 713, 729, 746, 764, 783, 804, 827, 852, 880, 910, 944, 981, 1024, 1073, 1129, 1196, 1276, 1374, 1498, 1662, 1894, 2253, 2913, 4705, 0, DEC_CURVE,CRV_SMEARING , 300, 234, 167, 100, 0, DEC_CURVE,CRV_BUFFERFULL, 1100, 867, 633, 400, 0, -2, /* motorcurve 3 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN, 5360, 3655, 2855, 2422, 2142, 1944, 1795, 1678, 1582, 1503, 1434, 1374, 0, ACC_CURVE,CRV_PARKHOME , 5360, 3362, 2628, 2229, 1973, 1791, 1654, 1547, 1458, 1384, 1319, 1264, 1214, 1170, 1131, 1096, 1063, 1034, 1006, 981, 958, 937, 916, 897, 880, 863, 847, 832, 818, 805, 792, 780, 769, 758, 747, 737, 727, 718, 709, 700, 692, 684, 677, 669, 662, 655, 648, 642, 636, 629, 624, 618, 612, 607, 602, 596, 591, 587, 582, 577, 573, 568, 564, 560, 556, 552, 548, 544, 540, 537, 533, 530, 526, 523, 520, 516, 513, 510, 507, 504, 501, 498, 496, 493, 490, 488, 485, 482, 480, 477, 475, 472, 470, 468, 466, 463, 461, 459, 457, 455, 453, 450, 448, 446, 444, 443, 441, 439, 437, 435, 433, 431, 430, 428, 426, 425, 423, 421, 420, 418, 416, 415, 413, 412, 410, 409, 407, 406, 405, 403, 402, 400, 399, 398, 396, 395, 394, 392, 391, 390, 389, 387, 386, 385, 384, 382, 381, 380, 379, 378, 377, 376, 374, 373, 372, 371, 370, 369, 368, 367, 366, 365, 364, 363, 362, 361, 360, 359, 358, 357, 356, 355, 354, 353, 353, 352, 351, 350, 349, 348, 0, ACC_CURVE,CRV_SMEARING , 5360, 3362, 2628, 2229, 1973, 1791, 1654, 1547, 0, DEC_CURVE,CRV_NORMALSCAN, 1374, 1434, 1503, 1582, 1678, 1795, 1944, 2142, 2422, 2855, 3655, 5360, 0, DEC_CURVE,CRV_PARKHOME , 348, 351, 353, 356, 359, 362, 365, 368, 371, 374, 378, 381, 385, 389, 392, 396, 400, 405, 409, 413, 418, 423, 428, 433, 439, 444, 450, 457, 463, 470, 477, 485, 493, 501, 510, 520, 530, 540, 552, 564, 577, 591, 607, 624, 642, 662, 684, 709, 737, 769, 805, 847, 897, 958, 1034, 1131, 1264, 1458, 1791, 2628, 5360, 0, DEC_CURVE,CRV_SMEARING , 1547, 1654, 1791, 1973, 2229, 2628, 3362, 5360, 0, DEC_CURVE,CRV_BUFFERFULL, 1374, 1434, 1503, 1582, 1678, 1795, 1944, 2142, 2422, 2855, 3655, 5360, 0, -2, /* motorcurve 4 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN, 4705, 2664, 2061, 1732, 1521, 1370, 1257, 1167, 1094, 1033, 982, 937, 898, 864, 833, 805, 780, 757, 736, 717, 699, 683, 667, 653, 640, 627, 615, 604, 593, 583, 574, 564, 556, 547, 540, 532, 525, 518, 511, 505, 499, 493, 487, 481, 476, 471, 466, 461, 456, 452, 447, 443, 439, 435, 431, 427, 424, 420, 417, 413, 410, 407, 403, 400, 397, 394, 391, 389, 386, 383, 381, 378, 375, 373, 371, 368, 366, 364, 361, 359, 357, 355, 353, 351, 349, 347, 345, 343, 341, 339, 338, 336, 334, 332, 331, 329, 327, 326, 324, 323, 321, 320, 318, 317, 315, 314, 312, 311, 310, 308, 307, 306, 304, 303, 302, 301, 299, 298, 297, 296, 295, 293, 292, 291, 290, 289, 288, 287, 286, 285, 284, 283, 282, 281, 280, 279, 278, 277, 276, 275, 274, 273, 272, 271, 270, 270, 269, 268, 267, 266, 265, 264, 264, 263, 262, 261, 260, 260, 259, 258, 257, 257, 256, 255, 255, 254, 253, 252, 252, 251, 250, 250, 249, 248, 248, 247, 246, 246, 245, 244, 244, 243, 242, 242, 241, 241, 240, 239, 239, 238, 238, 237, 237, 236, 235, 235, 234, 234, 233, 233, 232, 232, 231, 231, 230, 230, 229, 229, 228, 227, 227, 227, 226, 226, 225, 225, 224, 224, 223, 223, 222, 222, 221, 221, 220, 220, 219, 219, 219, 218, 218, 217, 217, 216, 216, 216, 215, 215, 214, 214, 214, 213, 213, 212, 212, 212, 211, 211, 210, 210, 210, 209, 209, 208, 208, 208, 207, 207, 207, 206, 206, 206, 205, 205, 204, 204, 204, 203, 203, 203, 202, 202, 202, 201, 201, 201, 200, 200, 200, 199, 199, 199, 198, 198, 198, 197, 197, 197, 197, 196, 196, 196, 195, 195, 195, 194, 194, 194, 194, 193, 193, 193, 192, 192, 192, 192, 191, 191, 191, 190, 190, 190, 190, 189, 189, 189, 188, 188, 188, 188, 187, 187, 187, 187, 186, 186, 186, 186, 185, 185, 185, 185, 184, 184, 184, 184, 183, 183, 183, 183, 182, 182, 182, 182, 181, 181, 181, 181, 181, 180, 180, 180, 180, 179, 179, 179, 179, 178, 178, 178, 178, 178, 177, 177, 177, 177, 177, 176, 176, 176, 176, 175, 175, 175, 175, 175, 174, 174, 174, 174, 174, 173, 173, 173, 173, 173, 172, 172, 172, 172, 172, 171, 171, 171, 0, ACC_CURVE,CRV_PARKHOME , 4705, 2888, 2234, 1878, 1648, 1485, 1362, 1265, 1186, 1120, 1064, 1016, 973, 936, 903, 873, 845, 821, 798, 777, 758, 740, 723, 708, 693, 679, 666, 654, 643, 632, 621, 612, 602, 593, 585, 576, 569, 561, 554, 547, 540, 534, 528, 522, 516, 510, 505, 499, 494, 490, 485, 480, 476, 471, 467, 463, 459, 455, 451, 448, 444, 440, 437, 434, 430, 427, 424, 421, 418, 415, 412, 409, 407, 404, 401, 399, 396, 394, 391, 389, 387, 384, 382, 380, 378, 376, 374, 371, 369, 367, 366, 364, 362, 360, 358, 356, 354, 353, 351, 349, 348, 346, 344, 343, 341, 340, 338, 337, 335, 334, 332, 331, 329, 328, 327, 325, 324, 323, 321, 320, 319, 318, 316, 315, 314, 313, 312, 311, 309, 308, 307, 306, 305, 304, 303, 302, 301, 300, 299, 298, 297, 296, 295, 294, 293, 292, 291, 290, 289, 288, 287, 286, 285, 285, 284, 283, 282, 281, 280, 279, 279, 278, 277, 276, 275, 275, 274, 273, 272, 272, 271, 270, 269, 269, 268, 267, 267, 266, 265, 264, 264, 263, 262, 262, 261, 260, 260, 259, 259, 258, 257, 257, 256, 255, 255, 254, 254, 253, 252, 252, 251, 251, 250, 249, 249, 248, 248, 247, 247, 246, 246, 245, 245, 244, 243, 243, 242, 242, 241, 241, 240, 240, 239, 239, 238, 238, 237, 237, 237, 236, 236, 235, 235, 234, 234, 233, 233, 232, 232, 231, 231, 231, 230, 230, 229, 229, 228, 228, 228, 227, 227, 226, 226, 225, 225, 225, 224, 224, 223, 223, 223, 222, 222, 222, 221, 221, 220, 220, 220, 219, 219, 219, 218, 218, 217, 217, 217, 216, 216, 216, 215, 215, 215, 214, 214, 214, 213, 213, 213, 212, 212, 212, 211, 211, 211, 210, 210, 210, 209, 209, 209, 208, 208, 208, 207, 207, 207, 207, 206, 206, 206, 205, 205, 205, 204, 204, 204, 204, 203, 203, 203, 202, 202, 202, 202, 201, 201, 201, 200, 200, 200, 200, 199, 199, 199, 199, 198, 198, 198, 198, 197, 197, 197, 196, 196, 196, 196, 195, 195, 195, 195, 194, 194, 194, 194, 193, 193, 193, 193, 192, 192, 192, 192, 192, 191, 191, 191, 191, 190, 190, 190, 190, 189, 189, 189, 189, 189, 188, 188, 188, 188, 187, 187, 187, 187, 187, 186, 186, 186, 186, 186, 185, 185, 185, 185, 184, 184, 184, 184, 184, 183, 183, 183, 183, 183, 182, 182, 182, 182, 182, 181, 181, 181, 181, 181, 180, 180, 180, 180, 180, 180, 179, 179, 179, 179, 179, 178, 178, 178, 178, 178, 177, 177, 177, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 175, 175, 174, 174, 174, 174, 174, 174, 173, 173, 173, 173, 173, 173, 172, 172, 172, 172, 172, 172, 171, 171, 171, 171, 0, ACC_CURVE,CRV_SMEARING , 4705, 3056, 2724, 2497, 1498, 1498, 1374, 1276, 1196, 1130, 1073, 1025, 982, 944, 911, 880, 853, 828, 805, 784, 764, 746, 730, 714, 699, 685, 675, 0, DEC_CURVE,CRV_NORMALSCAN, 171, 172, 172, 173, 174, 174, 175, 176, 176, 177, 178, 178, 179, 180, 181, 181, 182, 183, 184, 184, 185, 186, 187, 188, 189, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, 211, 213, 214, 215, 217, 218, 219, 221, 222, 224, 225, 227, 228, 230, 232, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255, 258, 260, 262, 265, 268, 270, 273, 276, 279, 282, 285, 289, 292, 296, 299, 303, 307, 311, 316, 320, 325, 330, 335, 341, 347, 353, 359, 366, 373, 381, 390, 398, 408, 418, 429, 441, 455, 469, 485, 503, 523, 545, 571, 601, 636, 678, 730, 796, 883, 1005, 1195, 1544, 4705, 0, DEC_CURVE,CRV_PARKHOME , 171, 172, 172, 173, 173, 174, 174, 175, 175, 176, 176, 177, 177, 178, 179, 179, 180, 180, 181, 182, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, 192, 192, 193, 194, 195, 195, 196, 197, 198, 199, 199, 200, 201, 202, 203, 204, 205, 206, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 217, 218, 219, 220, 221, 222, 223, 225, 226, 227, 228, 230, 231, 232, 234, 235, 237, 238, 240, 241, 243, 244, 246, 247, 249, 251, 253, 254, 256, 258, 260, 262, 264, 266, 268, 271, 273, 275, 278, 280, 282, 285, 288, 290, 293, 296, 299, 302, 305, 309, 312, 316, 319, 323, 327, 331, 336, 340, 345, 350, 355, 360, 365, 371, 377, 384, 391, 398, 406, 414, 422, 432, 441, 452, 463, 476, 489, 504, 520, 538, 558, 580, 605, 633, 667, 706, 752, 810, 883, 979, 1116, 1326, 1714, 4705, 0, DEC_CURVE,CRV_SMEARING , 675, 685, 699, 714, 730, 746, 764, 784, 805, 828, 853, 880, 911, 944, 982, 1025, 1073, 1130, 1196, 1276, 1374, 1498, 1498, 2497, 2724, 3056, 4705, 0, DEC_CURVE,CRV_BUFFERFULL, 171, 172, 172, 173, 174, 174, 175, 176, 176, 177, 178, 178, 179, 180, 181, 181, 182, 183, 184, 184, 185, 186, 187, 188, 189, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, 211, 213, 214, 215, 217, 218, 219, 221, 222, 224, 225, 227, 228, 230, 232, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255, 258, 260, 262, 265, 268, 270, 273, 276, 279, 282, 285, 289, 292, 296, 299, 303, 307, 311, 316, 320, 325, 330, 335, 341, 347, 353, 359, 366, 373, 381, 390, 398, 408, 418, 429, 441, 455, 469, 485, 503, 523, 545, 571, 601, 636, 678, 730, 796, 883, 1005, 1195, 1544, 4705, 0, -2, /* motorcurve 5 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN, 3763, 3763, 3763, 3763, 3763, 3763, 2444, 2178, 1997, 1198, 1198, 1098, 1020, 956, 903, 858, 819, 785, 754, 727, 703, 681, 662, 644, 626, 610, 596, 582, 571, 558, 547, 537, 527, 518, 509, 500, 492, 485, 478, 471, 464, 458, 452, 446, 440, 435, 430, 425, 420, 415, 410, 407, 402, 398, 394, 391, 386, 383, 380, 376, 373, 369, 366, 364, 360, 357, 355, 352, 349, 347, 344, 341, 338, 337, 334, 332, 329, 328, 325, 323, 321, 319, 317, 315, 313, 311, 310, 308, 306, 304, 302, 301, 299, 297, 295, 294, 293, 291, 290, 288, 286, 285, 284, 283, 281, 280, 278, 277, 275, 275, 274, 272, 271, 270, 268, 267, 266, 265, 264, 263, 262, 261, 259, 258, 257, 257, 256, 255, 254, 253, 251, 250, 249, 248, 248, 247, 246, 245, 244, 243, 243, 242, 241, 240, 239, 239, 238, 237, 236, 235, 235, 234, 233, 232, 231, 230, 230, 230, 229, 228, 228, 227, 226, 225, 225,224, 223, 222, 222, 221, 221, 221, 220, 219, 219, 218, 217, 217, 216, 215, 215, 214, 213, 213, 212, 212, 212, 211, 211, 210, 209, 209, 208, 208, 207, 207, 206, 206, 205, 204, 204, 203, 203, 203, 203, 202, 202, 201, 201, 200, 200, 199, 199, 198, 198, 197, 197, 196, 196, 195, 195, 194, 194, 194, 194, 194, 193, 193, 192, 192, 191, 191, 190, 190, 190, 189, 189, 188, 188, 188, 187, 187, 186, 186, 185, 185, 185, 185, 185, 185, 184, 184, 183, 183, 183, 182, 182, 182, 181, 181, 180, 180, 180, 179, 179, 179, 178, 178, 178, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 174, 174, 174, 173, 173, 173, 172, 172, 172, 171, 171, 171, 171, 170, 170, 170, 169, 169, 169, 169, 168, 168, 168, 167, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 165, 165, 165, 165, 164, 164, 164, 163, 163, 163, 163, 162, 162, 162, 162, 161, 161, 161, 161, 160, 160, 160, 160, 159, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 156, 156, 156, 156, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 153, 153, 153, 153, 152, 152, 152, 152, 152, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 148, 148, 148, 148, 148, 147, 147, 147, 147, 147, 147, 146, 146, 146, 146, 146, 145, 145, 145, 145, 145, 145, 144, 144, 144, 144, 144, 144, 143, 143, 143, 143, 143, 143, 142, 142, 142, 142, 142, 142, 141, 141, 141, 141, 141, 141, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 139, 139, 139, 139, 139, 139, 139, 138, 138, 138, 138, 138, 138, 138, 137, 137, 137, 137, 137, 137, 137, 136, 136, 136, 136, 136, 136, 136, 135, 135, 135, 135, 135, 135, 135, 134, 134, 134, 134, 134, 134, 134, 134, 133, 133, 133, 133, 133, 133, 133, 133, 132, 132, 132, 132, 132, 132, 132, 132, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 130, 130, 130, 130, 130, 130, 130, 130, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 127, 127, 127, 127, 127, 127, 127, 127, 127, 126, 126, 126, 126, 126, 126, 126, 126, 126, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 101, 101, 101, 101, 101, 101, 101, 101, 101,101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 0, ACC_CURVE,CRV_PARKHOME , 3763, 2330, 1803, 1515, 1330, 1198, 1099, 1021, 957, 904, 859, 819, 785, 755, 728, 704, 682, 662, 644, 626, 611, 597, 583, 571, 559, 548, 537, 527, 518, 509, 501, 493, 485, 478, 472, 464, 458, 453, 446, 441, 436, 430, 425, 420, 416, 411, 407, 402, 399, 394, 391, 387, 383, 380, 376, 374, 370, 366, 364, 361, 358, 355, 352, 349, 347, 344, 342, 339, 337, 335, 332, 330, 328, 326, 323, 321, 320, 318, 315, 313, 311, 310, 308, 306, 304, 302, 301, 300, 298, 296, 294, 293, 292, 290, 289, 287, 285, 284, 283, 282, 280, 279, 277, 276, 275, 274, 273, 271, 270, 269, 267, 266, 266, 265, 263, 262, 261, 260, 259, 258, 257, 256, 255, 254, 253, 252, 251, 250, 249, 248, 248, 247, 246, 245, 244, 243, 242, 241, 240, 239, 239, 239, 238, 237, 236, 235, 234, 233, 233, 232, 231, 230, 230, 230, 229, 228, 227, 227, 226, 225, 224, 224, 223, 222, 221, 221, 221, 220, 220, 219, 218, 218, 217, 216, 216, 215, 214, 214, 213, 213, 212, 212, 212, 211, 211, 210, 209, 209, 208, 208, 207, 207, 206, 205, 205, 204, 204, 203, 203, 203, 203, 202, 202, 201, 201, 200, 200, 199, 199, 198, 198, 197, 197, 196, 196, 195, 195, 194, 194, 194, 194, 194, 193, 193, 192, 192, 191, 191, 191, 190, 190, 189, 189, 188, 188, 188, 187, 187, 186, 186, 186, 185, 185, 185, 185, 185, 184, 184, 184, 183, 183, 182, 182, 182, 181, 181, 181, 180, 180, 180, 179, 179, 178, 178, 178, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 174, 174, 174, 174, 173, 173, 173, 172, 172, 172, 171, 171, 171, 170, 170, 170, 170, 169, 169, 169, 168, 168, 168, 168, 167, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 165, 165, 165, 165, 164, 164, 164, 163, 163, 163, 163, 162, 162, 162, 162, 161, 161, 161, 161, 160, 160, 160, 160, 160, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 156, 156, 156, 156, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 153, 153, 153, 153, 153, 152, 152, 152, 152, 152, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 148, 148, 148, 148, 148, 148, 147, 147, 147, 147, 147, 147, 146, 146, 146, 146, 146, 145, 145, 145, 145, 145, 145, 144, 144, 144, 144, 144, 144, 143, 143, 143, 143, 143, 143, 142, 142, 142, 142, 142, 142, 141, 141, 141, 141, 141, 141, 141, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 139, 139, 139, 139, 139, 139, 139, 138, 0, ACC_CURVE,CRV_SMEARING , 3763, 2330, 1803, 1515, 1330, 1198, 1099, 1021, 957, 904, 859, 819, 785, 755, 728, 704, 682, 662, 644, 626, 611, 597, 583, 571, 559, 548, 537, 527, 518, 509, 501, 493, 485, 478, 472, 464, 458, 453, 446, 441, 436, 430, 425, 420, 416, 411, 407, 402, 399, 394, 391, 387, 383, 380, 376, 374, 370, 366, 364, 361, 358, 355, 352, 349, 347, 344, 342, 339, 337, 335, 332, 330, 328, 326, 323, 321, 320, 318, 315, 313, 311, 310, 308, 306, 304, 302, 301, 300, 298, 296, 294, 293, 292, 290, 289, 287, 285, 284, 283, 282, 280, 279, 277, 276, 275, 274, 273, 271, 270, 269, 267, 266, 266, 265, 263, 262, 261, 260, 259, 258, 257, 256, 255, 254, 253, 252, 251, 250, 249, 248, 248, 247, 246, 245, 244, 243, 242, 241, 240, 239, 239, 239, 238, 237, 236, 235, 234, 233, 233, 232, 231, 230, 230, 230, 229, 228, 227, 227, 226, 225, 224, 224, 223, 222, 221, 221, 221, 220, 220, 219, 218, 218, 217, 216, 216, 215, 214, 214, 213, 213, 212, 212, 212, 211, 211, 210, 209, 209, 208, 208, 207, 207, 206, 205, 205, 204, 204, 203, 203, 203, 203, 202, 202, 201, 201, 200, 200, 199, 199, 198, 198, 197, 197, 196, 196, 195, 195, 194, 194, 194, 194, 194, 193, 193, 192, 192, 191, 191, 191, 190, 190, 189, 189, 188, 188, 188, 187, 187, 186, 186, 186, 185, 185, 185, 185, 185, 184, 184, 184, 183, 183, 182, 182, 182, 181, 181, 181, 180, 180, 180, 179, 179, 178, 178, 178, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 174, 174, 174, 174, 173, 173, 173, 172, 172, 172, 171, 171, 171, 170, 170, 170, 170, 169, 169, 169, 168, 168, 168, 168, 167, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 165, 165, 165, 165, 164, 164, 164, 163, 163, 163, 163, 162, 162, 162, 162, 161, 161, 161, 161, 160, 160, 160, 160, 160, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 156, 156, 156, 156, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 153, 153, 153, 153, 153, 152, 152, 152, 152, 152, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 148, 148, 148, 148, 148, 148, 147, 147, 147, 147, 147, 147, 146, 146, 146, 146, 146, 145, 145, 145, 145, 145, 145, 144, 144, 144, 144, 144, 144, 143, 143, 143, 143, 143, 143, 142, 142, 142, 142, 142, 142, 141, 141, 141, 141, 141, 141, 141, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 139, 139, 139, 139, 139, 139, 139, 138, 0, DEC_CURVE,CRV_NORMALSCAN, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 278, 280, 282, 284, 286, 288, 290, 292, 294, 296, 298, 300, 302, 304, 306, 308, 310, 312, 314, 316, 318, 320, 322, 324, 326, 328, 330, 332, 334, 336, 338, 340, 342, 345, 348, 351, 354, 357, 360, 363, 366, 369, 372, 375, 378, 381, 384, 387, 391, 395, 399, 403, 407, 411, 415, 419, 422, 425, 429, 433, 437, 441, 445, 449, 453, 458, 463, 468, 473, 478, 483, 489, 495, 501, 507, 514, 521, 528, 536, 544, 553, 562, 572, 582, 593, 604, 616, 629, 643, 658, 674, 692, 711, 732, 755, 781, 810, 843, 880, 923, 974,1035, 1110, 1205, 1331, 1510, 1796, 2368, 3400, 4922, 0, DEC_CURVE,CRV_PARKHOME , 138, 142, 146, 149, 153, 158, 162, 167, 171, 176, 180, 186, 193, 202, 209, 216, 223, 232, 243, 254, 266, 279, 292, 306, 320, 335, 337, 351, 367, 380, 396, 414, 437, 464, 493, 520, 549, 583, 611, 644, 675, 711, 755, 785, 819, 859, 904, 957, 1021, 1099, 1198, 1330, 1515, 1803, 2330, 3763, 0, DEC_CURVE,CRV_SMEARING , 138, 139, 139, 139, 139, 139, 139, 139, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 141, 141, 141, 141, 141, 141, 141, 142, 142, 142, 142, 142, 142, 143, 143, 143, 143, 143, 143, 144, 144, 144, 144, 144, 144, 145, 145, 145, 145, 145, 145, 146, 146, 146, 146, 146, 147, 147, 147, 147, 147, 147, 148, 148, 148, 148, 148, 148, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 150, 150, 150, 150, 150, 151, 151, 151, 151, 151, 152, 152, 152, 152, 152, 153, 153, 153, 153, 153, 154, 154, 154, 154, 154, 155, 155, 155, 155, 155, 156, 156, 156, 156, 157, 157, 157, 157, 157, 158, 158, 158, 158, 158, 158, 158, 158, 159, 159, 159, 159, 160, 160, 160, 160, 160, 161, 161, 161, 161, 162, 162, 162, 162, 163, 163, 163, 163, 164, 164, 164, 165, 165, 165, 165, 166, 166, 166, 166, 167, 167, 167, 167, 167, 167, 167, 168, 168, 168, 168, 169, 169, 169, 170, 170, 170, 170, 171, 171, 171, 172, 172, 172, 173, 173, 173, 174, 174, 174, 174, 175, 175, 175, 176, 176, 176, 176, 176, 176, 177, 177, 177, 178, 178, 178, 179, 179, 180, 180, 180, 181, 181, 181, 182, 182, 182, 183, 183, 184, 184, 184, 185, 185, 185, 185, 185, 186, 186, 186, 187, 187, 188, 188, 188, 189, 189, 190, 190, 191, 191, 191, 192, 192, 193, 193, 194, 194, 194, 194, 194, 195, 195, 196, 196, 197, 197, 198, 198, 199, 199, 200, 200, 201, 201, 202, 202, 203, 203, 203, 203, 204, 204, 205, 205, 206, 207, 207, 208, 208, 209, 209, 210, 211, 211, 212, 212, 212, 213, 213, 214, 214, 215, 216, 216, 217, 218, 218, 219, 220, 220, 221, 221, 221, 222, 223, 224, 224, 225, 226, 227, 227, 228, 229, 230, 230, 230, 231, 232, 233, 233, 234, 235, 236, 237, 238, 239, 239, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 265, 266, 266, 267, 269, 270, 271, 273, 274, 275, 276, 277, 279, 280, 282, 283, 284, 285, 287, 289, 290, 292, 293, 294, 296, 298, 300, 301, 302, 304, 306, 308, 310, 311, 313, 315, 318, 320, 321, 323, 326, 328, 330, 332, 335, 337, 339, 342, 344, 347, 349, 352, 355, 358, 361, 364, 366, 370, 374, 376, 380, 383, 387, 391, 394, 399, 402, 407, 411, 416, 420, 425, 430, 436, 441, 446, 453, 458, 464, 472, 478, 485, 493, 501, 509, 518, 527, 537, 548, 559, 571, 583, 597, 611, 626, 644, 662, 682, 704, 728, 755, 785, 819, 859, 904, 957, 1021, 1099, 1198, 1330, 1515, 1803, 2330, 3763, 0, DEC_CURVE,CRV_BUFFERFULL, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 94, 94, 94, 95, 95, 95, 95, 95, 96, 96, 97, 97, 98, 98, 99, 99, 100, 100, 101, 101, 102, 102, 103, 104, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 113, 115, 117, 119, 121, 122, 124, 127, 130, 132, 135, 139, 142, 146, 149, 153, 158, 162, 167, 171, 176, 180, 186, 193, 202, 209, 216, 223, 232, 243, 254, 266, 279, 292, 306, 320, 335, 337, 351, 367, 380, 396, 414, 437, 464, 493, 520, 549, 583, 611, 644, 675, 711, 755, 785, 819, 859, 904, 957, 1021, 1099, 1198, 1330, 1515, 1803, 2330, 3763, 0, -2, /* motorcurve 6 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN, 4705, 2664, 2061, 1732, 1521, 1370, 1257, 1167, 1094, 1033, 982, 937, 898, 864, 833, 805, 780, 757, 736, 717, 699, 683, 667, 653, 640, 627, 615, 604, 593, 583, 574, 564, 556, 547, 540, 532, 525, 518, 511, 505, 499, 493, 487, 481, 476, 471, 466, 461, 456, 452, 447, 443, 439, 435, 431, 427, 424, 420, 417, 413, 410, 407, 403, 400, 397, 394, 391, 389, 386, 383, 381, 378, 375, 373, 371, 368, 366, 364, 361, 359, 357, 355, 353, 351, 349, 347, 345, 343, 341, 339, 338, 336, 334, 332, 331, 329, 327, 326, 324, 323, 321, 320, 318, 317, 315, 314, 312, 311, 310, 308, 307, 306, 304, 303, 302, 301, 299, 298, 297, 296, 295, 293, 292, 291, 290, 289, 288, 287, 286, 285, 284, 283, 282, 281, 280, 279, 278, 277, 276, 275, 274, 273, 272, 271, 270, 270, 269, 268, 267, 266, 265, 264, 264, 263, 262, 261, 260, 260, 259, 258, 257, 257, 256, 255, 255, 254, 253, 252, 252, 251, 250, 250, 249, 248, 248, 247, 246, 246, 245, 244, 244, 243, 242, 242, 241, 241, 240, 239, 239, 238, 238, 237, 237, 236, 235, 235, 234, 234, 233, 233, 232, 232, 231, 231, 230, 230, 229, 229, 228, 227, 227, 227, 226, 226, 225, 225, 224, 224, 223, 223, 222, 222, 221, 221, 220, 220, 219, 219, 219, 218, 218, 217, 217, 216, 216, 216, 215, 215, 214, 214, 214, 213, 213, 212, 212, 212, 211, 211, 210, 210, 210, 209, 209, 208, 208, 208, 207, 207, 207, 206, 206, 206, 205, 205, 204, 204, 204, 203, 203, 203, 202, 202, 202, 201, 201, 201, 200, 200, 200, 199, 199, 199, 198, 198, 198, 197, 197, 197, 197, 196, 196, 196, 195, 195, 195, 194, 194, 194, 194, 193, 193, 193, 192, 192, 192, 192, 191, 191, 191, 190, 190, 190, 190, 189, 189, 189, 188, 188, 188, 188, 187, 187, 187, 187, 186, 186, 186, 186, 185, 185, 185, 185, 184, 184, 184, 184, 183, 183, 183, 183, 182, 182, 182, 182, 181, 181, 181, 181, 181, 180, 180, 180, 180, 179, 179, 179, 179, 178, 178, 178, 178, 178, 177, 177, 177, 177, 177, 176, 176, 176, 176, 175, 175, 175, 175, 175, 174, 174, 174, 174, 174, 173, 173, 173, 173, 173, 172, 172, 172, 172, 172, 171, 171, 171, 0, ACC_CURVE,CRV_PARKHOME , 4705, 2888, 2234, 1878, 1648, 1485, 1362, 1265, 1186, 1120, 1064, 1016, 973, 936, 903, 873, 845, 821, 798, 777, 758, 740, 723, 708, 693, 679, 666, 654, 643, 632, 621, 612, 602, 593, 585, 576, 569, 561, 554, 547, 540, 534, 528, 522, 516, 510, 505, 499, 494, 490, 485, 480, 476, 471, 467, 463, 459, 455, 451, 448, 444, 440, 437, 434, 430, 427, 424, 421, 418, 415, 412, 409, 407, 404, 401, 399, 396, 394, 391, 389, 387, 384, 382, 380, 378, 376, 374, 371, 369, 367, 366, 364, 362, 360, 358, 356, 354, 353, 351, 349, 348, 346, 344, 343, 341, 340, 338, 337, 335, 334, 332, 331, 329, 328, 327, 325, 324, 323, 321, 320, 319, 318, 316, 315, 314, 313, 312, 311, 309, 308, 307, 306, 305, 304, 303, 302, 301, 300, 299, 298, 297, 296, 295, 294, 293, 292, 291, 290, 289, 288, 287, 286, 285, 285, 284, 283, 282, 281, 280, 279, 279, 278, 277, 276, 275, 275, 274, 273, 272, 272, 271, 270, 269, 269, 268, 267, 267, 266, 265, 264, 264, 263, 262, 262, 261, 260, 260, 259, 259, 258, 257, 257, 256, 255, 255, 254, 254, 253, 252, 252, 251, 251, 250, 249, 249, 248, 248, 247, 247, 246, 246, 245, 245, 244, 243, 243, 242, 242, 241, 241, 240, 240, 239, 239, 238, 238, 237, 237, 237, 236, 236, 235, 235, 234, 234, 233, 233, 232, 232, 231, 231, 231, 230, 230, 229, 229, 228, 228, 228, 227, 227, 226, 226, 225, 225, 225, 224, 224, 223, 223, 223, 222, 222, 222, 221, 221, 220, 220, 220, 219, 219, 219, 218, 218, 217, 217, 217, 216, 216, 216, 215, 215, 215, 214, 214, 214, 213, 213, 213, 212, 212, 212, 211, 211, 211, 210, 210, 210, 209, 209, 209, 208, 208, 208, 207, 207, 207, 207, 206, 206, 206, 205, 205, 205, 204, 204, 204, 204, 203, 203, 203, 202, 202, 202, 202, 201, 201, 201, 200, 200, 200, 200, 199, 199, 199, 199, 198, 198, 198, 198, 197, 197, 197, 196, 196, 196, 196, 195, 195, 195, 195, 194, 194, 194, 194, 193, 193, 193, 193, 192, 192, 192, 192, 192, 191, 191, 191, 191, 190, 190, 190, 190, 189, 189, 189, 189, 189, 188, 188, 188, 188, 187, 187, 187, 187, 187, 186, 186, 186, 186, 186, 185, 185, 185, 185, 184, 184, 184, 184, 184, 183, 183, 183, 183, 183, 182, 182, 182, 182, 182, 181, 181, 181, 181, 181, 180, 180, 180, 180, 180, 180, 179, 179, 179, 179, 179, 178, 178, 178, 178, 178, 177, 177, 177, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 175, 175, 174, 174, 174, 174, 174, 174, 173, 173, 173, 173, 173, 173, 172, 172, 172, 172, 172, 172, 171, 171, 171, 171, 0, ACC_CURVE,CRV_SMEARING , 4705, 3056, 2724, 2497, 1498, 1498, 1374, 1276, 1196, 1130, 1073, 1025, 982, 944, 911, 880, 853, 828, 805, 784, 764, 746, 730, 714, 699, 685, 675, 0, DEC_CURVE,CRV_NORMALSCAN, 523, 545, 571, 601, 636, 678, 730, 796, 883, 1005, 1195, 1544, 4705, 0, DEC_CURVE,CRV_PARKHOME , 171, 172, 172, 173, 173, 174, 174, 175, 175, 176, 176, 177, 177, 178, 179, 179, 180, 180, 181, 182, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, 192, 192, 193, 194, 195, 195, 196, 197, 198, 199, 199, 200, 201, 202, 203, 204, 205, 206, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 217, 218, 219, 220, 221, 222, 223, 225, 226, 227, 228, 230, 231, 232, 234, 235, 237, 238, 240, 241, 243, 244, 246, 247, 249, 251, 253, 254, 256, 258, 260, 262, 264, 266, 268, 271, 273, 275, 278, 280, 282, 285, 288, 290, 293, 296, 299, 302, 305, 309, 312, 316, 319, 323, 327, 331, 336, 340, 345, 350, 355, 360, 365, 371, 377, 384, 391, 398, 406, 414, 422, 432, 441, 452, 463, 476, 489, 504, 520, 538, 558, 580, 605, 633, 667, 706, 752, 810, 883, 979, 1116, 1326, 1714, 4705, 0, DEC_CURVE,CRV_SMEARING , 675, 685, 699, 714, 730, 746, 764, 784, 805, 828, 853, 880, 911, 944, 982, 1025, 1073, 1130, 1196, 1276, 1374, 1498, 1498, 2497, 2724, 3056, 4705, 0, DEC_CURVE,CRV_BUFFERFULL, 171, 172, 172, 173, 174, 174, 175, 176, 176, 177, 178, 178, 179, 180, 181, 181, 182, 183, 184, 184, 185, 186, 187, 188, 189, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, 211, 213, 214, 215, 217, 218, 219, 221, 222, 224, 225, 227, 228, 230, 232, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255, 258, 260, 262, 265, 268, 270, 273, 276, 279, 282, 285, 289, 292, 296, 299, 303, 307, 311, 316, 320, 325, 330, 335, 341, 347, 353, 359, 366, 373, 381, 390, 398, 408, 418, 429, 441, 455, 469, 485, 503, 523, 545, 571, 601, 636, 678, 730, 796, 883, 1005, 1195, 1544, 4705, 0, -2, /* motorcurve 7 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN, 3763, 3763, 3763, 3763, 3763, 3763, 2444, 2178, 1997, 1198, 1198, 1098, 1020, 956, 903, 858, 819, 785, 754, 727, 703, 681, 662, 644, 626, 610, 596, 582, 571, 558, 547, 537, 527, 518, 509, 500, 492, 485, 478, 471, 464, 458, 452, 446, 440, 435, 430, 425, 420, 415, 410, 407, 402, 398, 394, 391, 386, 383, 380, 376, 373, 369, 366, 364, 360, 357, 355, 352, 349, 347, 344, 341, 338, 337, 334, 332, 329, 328, 325, 323, 321, 319, 317, 315, 313, 311, 310, 308, 306, 304, 302, 301, 299, 297, 295, 294, 293, 291, 290, 288, 286, 285, 284, 283, 281, 280, 278, 277, 275, 275, 274, 272, 271, 270, 268, 267, 266, 265, 264, 263, 262, 261, 259, 258, 257, 257, 256, 255, 254, 253, 251, 250, 249, 248, 248, 247, 246, 245, 244, 243, 243, 242, 241, 240, 239, 239, 238, 237, 236, 235, 235, 234, 233, 232, 231, 230, 230, 230, 229, 228, 228, 227, 226, 225, 225,224, 223, 222, 222, 221, 221, 221, 220, 219, 219, 218, 217, 217, 216, 215, 215, 214, 213, 213, 212, 212, 212, 211, 211, 210, 209, 209, 208, 208, 207, 207, 206, 206, 205, 204, 204, 203, 203, 203, 203, 202, 202, 201, 201, 200, 200, 199, 199, 198, 198, 197, 197, 196, 196, 195, 195, 194, 194, 194, 194, 194, 193, 193, 192, 192, 191, 191, 190, 190, 190, 189, 189, 188, 188, 188, 187, 187, 186, 186, 185, 185, 185, 185, 185, 185, 184, 184, 183, 183, 183, 182, 182, 182, 181, 181, 180, 180, 180, 179, 179, 179, 178, 178, 178, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 174, 174, 174, 173, 173, 173, 172, 172, 172, 171, 171, 171, 171, 170, 170, 170, 169, 169, 169, 169, 168, 168, 168, 167, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 165, 165, 165, 165, 164, 164, 164, 163, 163, 163, 163, 162, 162, 162, 162, 161, 161, 161, 161, 160, 160, 160, 160, 159, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 156, 156, 156, 156, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 153, 153, 153, 153, 152, 152, 152, 152, 152, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 148, 148, 148, 148, 148, 147, 147, 147, 147, 147, 147, 146, 146, 146, 146, 146, 145, 145, 145, 145, 145, 145, 144, 144, 144, 144, 144, 144, 143, 143, 143, 143, 143, 143, 142, 142, 142, 142, 142, 142, 141, 141, 141, 141, 141, 141, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 139, 139, 139, 139, 139, 139, 139, 138, 138, 138, 138, 138, 138, 138, 137, 137, 137, 137, 137, 137, 137, 136, 136, 136, 136, 136, 136, 136, 135, 135, 135, 135, 135, 135, 135, 134, 134, 134, 134, 134, 134, 134, 134, 133, 133, 133, 133, 133, 133, 133, 133, 132, 132, 132, 132, 132, 132, 132, 132, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 130, 130, 130, 130, 130, 130, 130, 130, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 127, 127, 127, 127, 127, 127, 127, 127, 127, 126, 126, 126, 126, 126, 126, 126, 126, 126, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 101, 101, 101, 101, 101, 101, 101, 101, 101,101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 0, ACC_CURVE,CRV_PARKHOME , 3763, 2330, 1803, 1515, 1330, 1198, 1099, 1021, 957, 904, 859, 819, 785, 755, 728, 704, 682, 662, 644, 626, 611, 597, 583, 571, 559, 548, 537, 527, 518, 509, 501, 493, 485, 478, 472, 464, 458, 453, 446, 441, 436, 430, 425, 420, 416, 411, 407, 402, 399, 394, 391, 387, 383, 380, 376, 374, 370, 366, 364, 361, 358, 355, 352, 349, 347, 344, 342, 339, 337, 335, 332, 330, 328, 326, 323, 321, 320, 318, 315, 313, 311, 310, 308, 306, 304, 302, 301, 300, 298, 296, 294, 293, 292, 290, 289, 287, 285, 284, 283, 282, 280, 279, 277, 276, 275, 274, 273, 271, 270, 269, 267, 266, 266, 265, 263, 262, 261, 260, 259, 258, 257, 256, 255, 254, 253, 252, 251, 250, 249, 248, 248, 247, 246, 245, 244, 243, 242, 241, 240, 239, 239, 239, 238, 237, 236, 235, 234, 233, 233, 232, 231, 230, 230, 230, 229, 228, 227, 227, 226, 225, 224, 224, 223, 222, 221, 221, 221, 220, 220, 219, 218, 218, 217, 216, 216, 215, 214, 214, 213, 213, 212, 212, 212, 211, 211, 210, 209, 209, 208, 208, 207, 207, 206, 205, 205, 204, 204, 203, 203, 203, 203, 202, 202, 201, 201, 200, 200, 199, 199, 198, 198, 197, 197, 196, 196, 195, 195, 194, 194, 194, 194, 194, 193, 193, 192, 192, 191, 191, 191, 190, 190, 189, 189, 188, 188, 188, 187, 187, 186, 186, 186, 185, 185, 185, 185, 185, 184, 184, 184, 183, 183, 182, 182, 182, 181, 181, 181, 180, 180, 180, 179, 179, 178, 178, 178, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 174, 174, 174, 174, 173, 173, 173, 172, 172, 172, 171, 171, 171, 170, 170, 170, 170, 169, 169, 169, 168, 168, 168, 168, 167, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 165, 165, 165, 165, 164, 164, 164, 163, 163, 163, 163, 162, 162, 162, 162, 161, 161, 161, 161, 160, 160, 160, 160, 160, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 156, 156, 156, 156, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 153, 153, 153, 153, 153, 152, 152, 152, 152, 152, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 148, 148, 148, 148, 148, 148, 147, 147, 147, 147, 147, 147, 146, 146, 146, 146, 146, 145, 145, 145, 145, 145, 145, 144, 144, 144, 144, 144, 144, 143, 143, 143, 143, 143, 143, 142, 142, 142, 142, 142, 142, 141, 141, 141, 141, 141, 141, 141, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 139, 139, 139, 139, 139, 139, 139, 138, 0, ACC_CURVE,CRV_SMEARING , 3763, 2330, 1803, 1515, 1330, 1198, 1099, 1021, 957, 904, 859, 819, 785, 755, 728, 704, 682, 662, 644, 626, 611, 597, 583, 571, 559, 548, 537, 527, 518, 509, 501, 493, 485, 478, 472, 464, 458, 453, 446, 441, 436, 430, 425, 420, 416, 411, 407, 402, 399, 394, 391, 387, 383, 380, 376, 374, 370, 366, 364, 361, 358, 355, 352, 349, 347, 344, 342, 339, 337, 335, 332, 330, 328, 326, 323, 321, 320, 318, 315, 313, 311, 310, 308, 306, 304, 302, 301, 300, 298, 296, 294, 293, 292, 290, 289, 287, 285, 284, 283, 282, 280, 279, 277, 276, 275, 274, 273, 271, 270, 269, 267, 266, 266, 265, 263, 262, 261, 260, 259, 258, 257, 256, 255, 254, 253, 252, 251, 250, 249, 248, 248, 247, 246, 245, 244, 243, 242, 241, 240, 239, 239, 239, 238, 237, 236, 235, 234, 233, 233, 232, 231, 230, 230, 230, 229, 228, 227, 227, 226, 225, 224, 224, 223, 222, 221, 221, 221, 220, 220, 219, 218, 218, 217, 216, 216, 215, 214, 214, 213, 213, 212, 212, 212, 211, 211, 210, 209, 209, 208, 208, 207, 207, 206, 205, 205, 204, 204, 203, 203, 203, 203, 202, 202, 201, 201, 200, 200, 199, 199, 198, 198, 197, 197, 196, 196, 195, 195, 194, 194, 194, 194, 194, 193, 193, 192, 192, 191, 191, 191, 190, 190, 189, 189, 188, 188, 188, 187, 187, 186, 186, 186, 185, 185, 185, 185, 185, 184, 184, 184, 183, 183, 182, 182, 182, 181, 181, 181, 180, 180, 180, 179, 179, 178, 178, 178, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 174, 174, 174, 174, 173, 173, 173, 172, 172, 172, 171, 171, 171, 170, 170, 170, 170, 169, 169, 169, 168, 168, 168, 168, 167, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 165, 165, 165, 165, 164, 164, 164, 163, 163, 163, 163, 162, 162, 162, 162, 161, 161, 161, 161, 160, 160, 160, 160, 160, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 156, 156, 156, 156, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 153, 153, 153, 153, 153, 152, 152, 152, 152, 152, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 148, 148, 148, 148, 148, 148, 147, 147, 147, 147, 147, 147, 146, 146, 146, 146, 146, 145, 145, 145, 145, 145, 145, 144, 144, 144, 144, 144, 144, 143, 143, 143, 143, 143, 143, 142, 142, 142, 142, 142, 142, 141, 141, 141, 141, 141, 141, 141, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 139, 139, 139, 139, 139, 139, 139, 138, 0, DEC_CURVE,CRV_NORMALSCAN, 522, 522, 528, 536, 544, 553, 562, 572, 582, 593, 604, 616, 629, 643, 658, 674, 692, 711, 732, 755, 781, 810, 843, 880, 923, 974, 1035, 1110, 1205, 1331, 1510, 1796, 2368, 3400, 4922, 0, DEC_CURVE,CRV_PARKHOME , 138, 142, 146, 149, 153, 158, 162, 167, 171, 176, 180, 186, 193, 202, 209, 216, 223, 232, 243, 254, 266, 279, 292, 306, 320, 335, 337, 351, 367, 380, 396, 414, 437, 464, 493, 520, 549, 583, 611, 644, 675, 711, 755, 785, 819, 859, 904, 957, 1021, 1099, 1198, 1330, 1515, 1803, 2330, 3763, 0, DEC_CURVE,CRV_SMEARING , 138, 139, 139, 139, 139, 139, 139, 139, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 141, 141, 141, 141, 141, 141, 141, 142, 142, 142, 142, 142, 142, 143, 143, 143, 143, 143, 143, 144, 144, 144, 144, 144, 144, 145, 145, 145, 145, 145, 145, 146, 146, 146, 146, 146, 147, 147, 147, 147, 147, 147, 148, 148, 148, 148, 148, 148, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 150, 150, 150, 150, 150, 151, 151, 151, 151, 151, 152, 152, 152, 152, 152, 153, 153, 153, 153, 153, 154, 154, 154, 154, 154, 155, 155, 155, 155, 155, 156, 156, 156, 156, 157, 157, 157, 157, 157, 158, 158, 158, 158, 158, 158, 158, 158, 159, 159, 159, 159, 160, 160, 160, 160, 160, 161, 161, 161, 161, 162, 162, 162, 162, 163, 163, 163, 163, 164, 164, 164, 165, 165, 165, 165, 166, 166, 166, 166, 167, 167, 167, 167, 167, 167, 167, 168, 168, 168, 168, 169, 169, 169, 170, 170, 170, 170, 171, 171, 171, 172, 172, 172, 173, 173, 173, 174, 174, 174, 174, 175, 175, 175, 176, 176, 176, 176, 176, 176, 177, 177, 177, 178, 178, 178, 179, 179, 180, 180, 180, 181, 181, 181, 182, 182, 182, 183, 183, 184, 184, 184, 185, 185, 185, 185, 185, 186, 186, 186, 187, 187, 188, 188, 188, 189, 189, 190, 190, 191, 191, 191, 192, 192, 193, 193, 194, 194, 194, 194, 194, 195, 195, 196, 196, 197, 197, 198, 198, 199, 199, 200, 200, 201, 201, 202, 202, 203, 203, 203, 203, 204, 204, 205, 205, 206, 207, 207, 208, 208, 209, 209, 210, 211, 211, 212, 212, 212, 213, 213, 214, 214, 215, 216, 216, 217, 218, 218, 219, 220, 220, 221, 221, 221, 222, 223, 224, 224, 225, 226, 227, 227, 228, 229, 230, 230, 230, 231, 232, 233, 233, 234, 235, 236, 237, 238, 239, 239, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 265, 266, 266, 267, 269, 270, 271, 273, 274, 275, 276, 277, 279, 280, 282, 283, 284, 285, 287, 289, 290, 292, 293, 294, 296, 298, 300, 301, 302, 304, 306, 308, 310, 311, 313, 315, 318, 320, 321, 323, 326, 328, 330, 332, 335, 337, 339, 342, 344, 347, 349, 352, 355, 358, 361, 364, 366, 370, 374, 376, 380, 383, 387, 391, 394, 399, 402, 407, 411, 416, 420, 425, 430, 436, 441, 446, 453, 458, 464, 472, 478, 485, 493, 501, 509, 518, 527, 537, 548, 559, 571, 583, 597, 611, 626, 644, 662, 682, 704, 728, 755, 785, 819, 859, 904, 957, 1021, 1099, 1198, 1330, 1515, 1803, 2330, 3763, 0, DEC_CURVE,CRV_BUFFERFULL, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 94, 94, 94, 95, 95, 95, 95, 95, 96, 96, 97, 97, 98, 98, 99, 99, 100, 100, 101, 101, 102, 102, 103, 104, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 113, 115, 117, 119, 121, 122, 124, 127, 130, 132, 135, 139, 142, 146, 149, 153, 158, 162, 167, 171, 176, 180, 186, 193, 202, 209, 216, 223, 232, 243, 254, 266, 279, 292, 306, 320, 335, 337, 351, 367, 380, 396, 414, 437, 464, 493, 520, 549, 583, 611, 644, 675, 711, 755, 785, 819, 859, 904, 957, 1021, 1099, 1198, 1330, 1515, 1803, 2330, 3763, 0, -2, /* motorcurve 8 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN, 1046, 1046, 1046, 1046, 1046, 1046, 647, 501, 421, 370, 333, 305, 284, 266, 251, 239, 228, 218, 210, 202, 196, 190, 184, 179, 174, 170, 166, 162, 159, 155, 152, 149, 147, 144, 142, 139, 137, 135, 133, 131, 129, 127, 126, 124, 123, 121, 120, 118, 117, 116, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 100, 99, 98, 97, 96, 96, 95, 94, 94, 93, 92, 92, 91, 91, 90, 89, 89, 88, 88, 87, 87, 86, 86, 85, 85, 84, 84, 83, 83, 82, 82, 82, 81, 81, 80, 80, 79, 79, 79, 78, 78, 78, 77, 77, 76, 76, 76, 75, 75, 75, 74, 74, 74, 74, 73, 73, 73, 72, 72, 72, 71, 71, 71, 71, 70, 70, 70, 70, 69, 69, 69, 69, 68, 68, 68, 68, 67, 67, 67, 67, 66, 66, 66, 66, 65, 65, 65, 65, 64, 64, 64, 64, 63, 63, 63, 63, 62, 62, 62, 62, 61, 61, 61, 61, 61, 60, 60, 60, 60, 60, 60, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 58, 58, 58, 57, 57, 57, 57, 57, 57, 57, 56, 56, 56, 56, 56, 56, 56, 56, 55, 55, 55, 55, 55, 55, 55, 55, 54, 54, 54, 54, 54, 54, 54, 54, 53, 53, 53, 53, 53, 53, 53, 53, 52, 52, 52, 52, 52, 52, 52, 52, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 23, 23, 0, ACC_CURVE,CRV_PARKHOME , 1045, 678, 604, 554, 332, 332, 304, 283, 265, 250, 238, 227, 217, 209, 201, 195, 189, 183, 178,173, 169, 165, 161, 158, 154, 151, 148, 146, 143, 141, 138, 136, 134, 132, 130, 128, 126, 125, 123, 122, 120, 119, 117, 116, 115, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103,102, 101, 100, 99, 99, 98, 97, 96, 95, 95, 94, 93, 93, 92, 91, 91, 90, 90, 89, 88, 88, 87, 87, 86, 86, 85, 85, 84, 84, 83, 83, 82, 82, 81, 81, 81, 80, 80, 79, 79, 78, 78, 78, 77, 77, 77, 76, 76, 75, 75, 75, 74, 74, 74, 73, 73, 73, 73, 72, 72, 72, 71, 71, 71, 70, 70, 70, 70, 69, 69, 69, 69, 68, 68, 68, 68, 67, 67, 67, 67, 66, 66, 66, 66, 65, 65, 65, 65, 65, 64, 64, 64, 64, 64, 63, 63, 63, 63, 63, 62, 62, 62, 62, 62, 61, 61, 61, 61, 61, 61, 60, 60, 60, 60, 60, 60, 59, 59, 59, 59, 59, 59, 58, 58, 58, 58, 58, 58, 58, 57, 57, 57, 57, 57, 57, 57, 56, 56, 56, 56, 56, 56, 56, 55, 55, 55, 55, 55, 55, 55, 55, 54, 54, 54, 54, 54, 54, 54, 54, 53, 53, 53, 53, 53, 53, 53, 53, 53, 52, 52, 52, 52, 52, 52, 52, 52, 52, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 39, 0, ACC_CURVE,CRV_SMEARING , 1045, 678, 604, 554, 332, 332, 304, 283, 265, 250, 238, 227, 217, 209, 201, 195, 189, 183, 178,173, 169, 165, 161, 158, 154, 151, 148, 146, 143, 141, 138, 136, 134, 132, 130, 128, 126, 125, 123, 122, 120, 119, 117, 116, 115, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103,102, 101, 100, 99, 99, 98, 97, 96, 95, 95, 94, 93, 93, 92, 91, 91, 90, 90, 89, 88, 88, 87, 87, 86, 86, 85, 85, 84, 84, 83, 83, 82, 82, 81, 81, 81, 80, 80, 79, 79, 78, 78, 78, 77, 77, 77, 76, 76, 75, 75, 75, 74, 74, 74, 73, 73, 73, 73, 72, 72, 72, 71, 71, 71, 70, 70, 70, 70, 69, 69, 69, 69, 68, 68, 68, 68, 67, 67, 67, 67, 66, 66, 66, 66, 65, 65, 65, 65, 65, 64, 64, 64, 64, 64, 63, 63, 63, 63, 63, 62, 62, 62, 62, 62, 61, 61, 61, 61, 61, 61, 60, 60, 60, 60, 60, 60, 59, 59, 59, 59, 59, 59, 58, 58, 58, 58, 58, 58, 58, 57, 57, 57, 57, 57, 57, 57, 56, 56, 56, 56, 56, 56, 56, 55, 55, 55, 55, 55, 55, 55, 55, 54, 54, 54, 54, 54, 54, 54, 54, 53, 53, 53, 53, 53, 53, 53, 53, 53, 52, 52, 52, 52, 52, 52, 52, 52, 52, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 39, 0, DEC_CURVE,CRV_NORMALSCAN, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 36, 37, 38, 38, 39, 40, 41, 42, 43, 45, 46, 47, 48, 50, 51, 53, 54, 57, 59, 61, 63, 65, 68, 71, 75, 78, 82, 86, 90, 94, 94, 98, 103, 106, 111, 116, 122, 130, 138, 145, 153, 163, 171, 180, 188, 198, 211, 219, 228, 239, 252, 267, 284, 306, 334, 370, 422, 502, 648, 1045, 0, DEC_CURVE,CRV_PARKHOME , 39, 40, 41, 42, 43, 45, 46, 47, 48, 50, 51, 53, 54, 57, 59, 61, 63, 65, 68, 71, 75, 78, 82, 86, 90, 94, 94, 98, 103, 106, 111, 116, 122, 130, 138, 145, 153, 163, 171, 180, 188, 198, 211, 219, 228, 239, 252, 267, 284, 306, 334, 370, 422, 502, 648, 1045, 0, DEC_CURVE,CRV_SMEARING , 39, 39, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 57, 57, 58, 58, 58, 58, 58, 58, 58, 59, 59, 59, 59, 59, 59, 59, 60, 60, 60, 60, 60, 60, 60, 61, 61, 61, 61, 61, 61, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, 66, 66, 66, 66, 66, 67, 67, 67, 67, 67, 68, 68, 68, 68, 69, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, 72, 72, 72, 73, 73, 73, 73, 74, 74, 74, 75, 75, 75, 76, 76, 76, 77, 77, 77, 78, 78, 78, 79, 79, 79, 80, 80, 81, 81, 81, 82, 82, 83, 83, 84, 84, 84, 85, 85, 86, 86, 87, 87, 88, 88, 89, 90, 90, 91, 91, 92, 93, 93, 94, 94, 95, 96, 96, 97, 98, 99, 99, 100, 101, 102, 103, 104, 105, 105, 106, 107, 108, 109, 110, 112, 113, 114, 115, 116, 118, 119, 120, 122, 123, 125, 127, 128, 130, 132, 134, 136, 138, 140, 142, 145, 147, 150, 153, 156, 159, 163, 167, 171, 175, 180, 185, 190, 196, 203, 211, 219, 228, 239, 252, 267, 284, 306, 334, 370, 422, 502, 648, 1045, 0, DEC_CURVE,CRV_BUFFERFULL, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 36, 37, 38, 38, 39, 40, 41, 42, 43, 45, 46, 47, 48, 50, 51, 53, 54, 57, 59, 61, 63, 65, 68, 71, 75, 78, 82, 86, 90, 94, 94, 98, 103, 106, 111, 116, 122, 130, 138, 145, 153, 163, 171, 180, 188, 198, 211, 219, 228, 239, 252, 267, 284, 306, 334, 370, 422, 502, 648, 1045, 0, -1 }; rst = (SANE_Int *)malloc(sizeof(steps)); if (rst != NULL) memcpy(rst, &steps, sizeof(steps)); return rst; } static SANE_Int *hp3970_motor() { SANE_Int *rst = NULL; SANE_Int steps[] = { /* motorcurve 1 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN, 2000, 1984, 1968, 1953, 1937, 1921, 1906, 1890, 1874, 1859, 1843, 1827, 1812, 1796, 1781, 1765, 1749, 1734, 1715, 1700, 1684, 1669, 1653, 1637, 1622, 1606, 1590, 1572, 1556, 1541, 1525, 1510, 1494, 1478, 1463, 1447, 1431, 1416, 1400, 1384, 1366, 1351, 1335, 1319, 1304, 1288, 1272, 1257, 1241, 1225, 1210, 1194, 1179, 1160, 1145, 1129, 1113, 1098, 1082, 1066, 1051, 1035, 1017, 1001, 986, 970, 954, 939, 923, 907, 892, 876, 861, 845, 829, 814, 798, 782, 767, 749, 0, ACC_CURVE,CRV_PARKHOME , 4705, 2913, 2253, 1894, 1662, 1498, 1374, 1276, 1196, 1129, 1073, 1024, 981, 944, 910, 880, 852, 827, 804, 783, 764, 746, 729, 713, 699, 685, 672, 659, 648, 637, 626, 616, 607, 598, 589, 581, 573, 565, 558, 551, 544, 538, 532, 526, 520, 514, 509, 503, 500, 0, ACC_CURVE,CRV_SMEARING , 200, 12, 14, 16, 0, DEC_CURVE,CRV_NORMALSCAN, 749, 1166, 1583, 2000, 0, DEC_CURVE,CRV_PARKHOME , 500, 503, 509, 514, 520, 526, 532, 538, 544, 551, 558, 565, 573, 581, 589, 598, 607, 616, 626, 637, 648, 659, 672, 685, 699, 713, 729, 746, 764, 783, 804, 827, 852, 880, 910, 944, 981, 1024, 1073, 1129, 1196, 1276, 1374, 1498, 1662, 1894, 2253, 2913, 4705, 0, DEC_CURVE,CRV_SMEARING , 300, 234, 167, 100, 0, DEC_CURVE,CRV_BUFFERFULL, 1100, 867, 633, 400, 0, -2, /* motorcurve 2 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN, 4705, 2664, 2061, 1732, 1521, 1370, 1257, 1167, 1094, 1033, 982, 937, 898, 864, 833, 805, 780, 757, 736, 717, 699, 683, 667, 653, 640, 627, 615, 604, 593, 583, 574, 564, 556, 547, 540, 532, 525, 518, 511, 505, 499, 493, 487, 481, 476, 471, 466, 461, 456, 452, 447, 443, 439, 435, 431, 427, 424, 420, 417, 413, 410, 407, 403, 400, 397, 394, 391, 389, 386, 383, 381, 378, 375, 373, 371, 368, 366, 364, 361, 359, 357, 355, 353, 351, 349, 347, 345, 343, 341, 339, 338, 336, 334, 332, 331, 329, 327, 326, 324, 323, 321, 320, 318, 317, 315, 314, 312, 311, 310, 308, 307, 306, 304, 303, 302, 301, 299, 298, 297, 296, 295, 293, 292, 291, 290, 289, 288, 287, 286, 285, 284, 283, 282, 281, 280, 279, 278, 277, 276, 275, 274, 273, 272, 271, 270, 270, 269, 268, 267, 266, 265, 264, 264, 263, 262, 261, 260, 260, 259, 258, 257, 257, 256, 255, 255, 254, 253, 252, 252, 251, 250, 250, 249, 248, 248, 247, 246, 246, 245, 244, 244, 243, 242, 242, 241, 241, 240, 239, 239, 238, 238, 237, 237, 236, 235, 235, 234, 234, 233, 233, 232, 232, 231, 231, 230, 230, 229, 229, 228, 227, 227, 227, 226, 226, 225, 225, 224, 224, 223, 223, 222, 222, 221, 221, 220, 220, 219, 219, 219, 218, 218, 217, 217, 216, 216, 216, 215, 215, 214, 214, 214, 213, 213, 212, 212, 212, 211, 211, 210, 210, 210, 209, 209, 208, 208, 208, 207, 207, 207, 206, 206, 206, 205, 205, 204, 204, 204, 203, 203, 203, 202, 202, 202, 201, 201, 201, 200, 200, 200, 199, 199, 199, 198, 198, 198, 197, 197, 197, 197, 196, 196, 196, 195, 195, 195, 194, 194, 194, 194, 193, 193, 193, 192, 192, 192, 192, 191, 191, 191, 190, 190, 190, 190, 189, 189, 189, 188, 188, 188, 188, 187, 187, 187, 187, 186, 186, 186, 186, 185, 185, 185, 185, 184, 184, 184, 184, 183, 183, 183, 183, 182, 182, 182, 182, 181, 181, 181, 181, 181, 180, 180, 180, 180, 179, 179, 179, 179, 178, 178, 178, 178, 178, 177, 177, 177, 177, 177, 176, 176, 176, 176, 175, 175, 175, 175, 175, 174, 174, 174, 174, 174, 173, 173, 173, 173, 173, 172, 172, 172, 172, 172, 171, 171, 171, 0, ACC_CURVE,CRV_PARKHOME , 4705, 2913, 2253, 1894, 1662, 1498, 1374, 1276, 1196, 1129, 1073, 1024, 981, 944, 910, 880, 852, 827, 804, 783, 764, 746, 729, 713, 699, 685, 672, 659, 648, 637, 626, 616, 607, 598, 589, 581, 573, 565, 558, 551, 544, 538, 532, 526, 520, 514, 509, 503, 500, 0, ACC_CURVE,CRV_SMEARING , 200, 12, 14, 16, 0, DEC_CURVE,CRV_NORMALSCAN, 171, 172, 172, 173, 174, 174, 175, 176, 176, 177, 178, 178, 179, 180, 181, 181, 182, 183, 184, 184, 185, 186, 187, 188, 189, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, 211, 213, 214, 215, 217, 218, 219, 221, 222, 224, 225, 227, 228, 230, 232, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255, 258, 260, 262, 265, 268, 270, 273, 276, 279, 282, 285, 289, 292, 296, 299, 303, 307, 311, 316, 320, 325, 330, 335, 341, 347, 353, 359, 366, 373, 381, 390, 398, 408, 418, 429, 441, 455, 469, 485, 503, 523, 545, 571, 601, 636, 678, 730, 796, 883, 1005, 1195, 1544, 4705, 0, DEC_CURVE,CRV_PARKHOME , 500, 503, 509, 514, 520, 526, 532, 538, 544, 551, 558, 565, 573, 581, 589, 598, 607, 616, 626, 637, 648, 659, 672, 685, 699, 713, 729, 746, 764, 783, 804, 827, 852, 880, 910, 944, 981, 1024, 1073, 1129, 1196, 1276, 1374, 1498, 1662, 1894, 2253, 2913, 4705, 0, DEC_CURVE,CRV_SMEARING , 300, 234, 167, 100, 0, DEC_CURVE,CRV_BUFFERFULL, 1100, 867, 633, 400, 0, -2, /* motorcurve 3 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN, 5360, 3655, 2855, 2422, 2142, 1944, 1795, 1678, 1582, 1503, 1434, 1374, 0, ACC_CURVE,CRV_PARKHOME , 5360, 3362, 2628, 2229, 1973, 1791, 1654, 1547, 1458, 1384, 1319, 1264, 1214, 1170, 1131, 1096, 1063, 1034, 1006, 981, 958, 937, 916, 897, 880, 863, 847, 832, 818, 805, 792, 780, 769, 758, 747, 737, 727, 718, 709, 700, 692, 684, 677, 669, 662, 655, 648, 642, 636, 629, 624, 618, 612, 607, 602, 596, 591, 587, 582, 577, 573, 568, 564, 560, 556, 552, 548, 544, 540, 537, 533, 530, 526, 523, 520, 516, 513, 510, 507, 504, 501, 498, 496, 493, 490, 488, 485, 482, 480, 477, 475, 472, 470, 468, 466, 463, 461, 459, 457, 455, 453, 450, 448, 446, 444, 443, 441, 439, 437, 435, 433, 431, 430, 428, 426, 425, 423, 421, 420, 418, 416, 415, 413, 412, 410, 409, 407, 406, 405, 403, 402, 400, 399, 398, 396, 395, 394, 392, 391, 390, 389, 387, 386, 385, 384, 382, 381, 380, 379, 378, 377, 376, 374, 373, 372, 371, 370, 369, 368, 367, 366, 365, 364, 363, 362, 361, 360, 359, 358, 357, 356, 355, 354, 353, 353, 352, 351, 350, 349, 348, 0, ACC_CURVE,CRV_SMEARING , 5360, 3362, 2628, 2229, 1973, 1791, 1654, 1547, 0, DEC_CURVE,CRV_NORMALSCAN, 1374, 1434, 1503, 1582, 1678, 1795, 1944, 2142, 2422, 2855, 3655, 5360, 0, DEC_CURVE,CRV_PARKHOME , 348, 351, 353, 356, 359, 362, 365, 368, 371, 374, 378, 381, 385, 389, 392, 396, 400, 405, 409, 413, 418, 423, 428, 433, 439, 444, 450, 457, 463, 470, 477, 485, 493, 501, 510, 520, 530, 540, 552, 564, 577, 591, 607, 624, 642, 662, 684, 709, 737, 769, 805, 847, 897, 958, 1034, 1131, 1264, 1458, 1791, 2628, 5360, 0, DEC_CURVE,CRV_SMEARING , 1547, 1654, 1791, 1973, 2229, 2628, 3362, 5360, 0, DEC_CURVE,CRV_BUFFERFULL, 1374, 1434, 1503, 1582, 1678, 1795, 1944, 2142, 2422, 2855, 3655, 5360, 0, -2, /* motorcurve 4 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN, 4705, 2664, 2061, 1732, 1521, 1370, 1257, 1167, 1094, 1033, 982, 937, 898, 864, 833, 805, 780, 757, 736, 717, 699, 683, 667, 653, 640, 627, 615, 604, 593, 583, 574, 564, 556, 547, 540, 532, 525, 518, 511, 505, 499, 493, 487, 481, 476, 471, 466, 461, 456, 452, 447, 443, 439, 435, 431, 427, 424, 420, 417, 413, 410, 407, 403, 400, 397, 394, 391, 389, 386, 383, 381, 378, 375, 373, 371, 368, 366, 364, 361, 359, 357, 355, 353, 351, 349, 347, 345, 343, 341, 339, 338, 336, 334, 332, 331, 329, 327, 326, 324, 323, 321, 320, 318, 317, 315, 314, 312, 311, 310, 308, 307, 306, 304, 303, 302, 301, 299, 298, 297, 296, 295, 293, 292, 291, 290, 289, 288, 287, 286, 285, 284, 283, 282, 281, 280, 279, 278, 277, 276, 275, 274, 273, 272, 271, 270, 270, 269, 268, 267, 266, 265, 264, 264, 263, 262, 261, 260, 260, 259, 258, 257, 257, 256, 255, 255, 254, 253, 252, 252, 251, 250, 250, 249, 248, 248, 247, 246, 246, 245, 244, 244, 243, 242, 242, 241, 241, 240, 239, 239, 238, 238, 237, 237, 236, 235, 235, 234, 234, 233, 233, 232, 232, 231, 231, 230, 230, 229, 229, 228, 227, 227, 227, 226, 226, 225, 225, 224, 224, 223, 223, 222, 222, 221, 221, 220, 220, 219, 219, 219, 218, 218, 217, 217, 216, 216, 216, 215, 215, 214, 214, 214, 213, 213, 212, 212, 212, 211, 211, 210, 210, 210, 209, 209, 208, 208, 208, 207, 207, 207, 206, 206, 206, 205, 205, 204, 204, 204, 203, 203, 203, 202, 202, 202, 201, 201, 201, 200, 200, 200, 199, 199, 199, 198, 198, 198, 197, 197, 197, 197, 196, 196, 196, 195, 195, 195, 194, 194, 194, 194, 193, 193, 193, 192, 192, 192, 192, 191, 191, 191, 190, 190, 190, 190, 189, 189, 189, 188, 188, 188, 188, 187, 187, 187, 187, 186, 186, 186, 186, 185, 185, 185, 185, 184, 184, 184, 184, 183, 183, 183, 183, 182, 182, 182, 182, 181, 181, 181, 181, 181, 180, 180, 180, 180, 179, 179, 179, 179, 178, 178, 178, 178, 178, 177, 177, 177, 177, 177, 176, 176, 176, 176, 175, 175, 175, 175, 175, 174, 174, 174, 174, 174, 173, 173, 173, 173, 173, 172, 172, 172, 172, 172, 171, 171, 171, 0, ACC_CURVE,CRV_PARKHOME , 4705, 2888, 2234, 1878, 1648, 1485, 1362, 1265, 1186, 1120, 1064, 1016, 973, 936, 903, 873, 845, 821, 798, 777, 758, 740, 723, 708, 693, 679, 666, 654, 643, 632, 621, 612, 602, 593, 585, 576, 569, 561, 554, 547, 540, 534, 528, 522, 516, 510, 505, 499, 494, 490, 485, 480, 476, 471, 467, 463, 459, 455, 451, 448, 444, 440, 437, 434, 430, 427, 424, 421, 418, 415, 412, 409, 407, 404, 401, 399, 396, 394, 391, 389, 387, 384, 382, 380, 378, 376, 374, 371, 369, 367, 366, 364, 362, 360, 358, 356, 354, 353, 351, 349, 348, 346, 344, 343, 341, 340, 338, 337, 335, 334, 332, 331, 329, 328, 327, 325, 324, 323, 321, 320, 319, 318, 316, 315, 314, 313, 312, 311, 309, 308, 307, 306, 305, 304, 303, 302, 301, 300, 299, 298, 297, 296, 295, 294, 293, 292, 291, 290, 289, 288, 287, 286, 285, 285, 284, 283, 282, 281, 280, 279, 279, 278, 277, 276, 275, 275, 274, 273, 272, 272, 271, 270, 269, 269, 268, 267, 267, 266, 265, 264, 264, 263, 262, 262, 261, 260, 260, 259, 259, 258, 257, 257, 256, 255, 255, 254, 254, 253, 252, 252, 251, 251, 250, 249, 249, 248, 248, 247, 247, 246, 246, 245, 245, 244, 243, 243, 242, 242, 241, 241, 240, 240, 239, 239, 238, 238, 237, 237, 237, 236, 236, 235, 235, 234, 234, 233, 233, 232, 232, 231, 231, 231, 230, 230, 229, 229, 228, 228, 228, 227, 227, 226, 226, 225, 225, 225, 224, 224, 223, 223, 223, 222, 222, 222, 221, 221, 220, 220, 220, 219, 219, 219, 218, 218, 217, 217, 217, 216, 216, 216, 215, 215, 215, 214, 214, 214, 213, 213, 213, 212, 212, 212, 211, 211, 211, 210, 210, 210, 209, 209, 209, 208, 208, 208, 207, 207, 207, 207, 206, 206, 206, 205, 205, 205, 204, 204, 204, 204, 203, 203, 203, 202, 202, 202, 202, 201, 201, 201, 200, 200, 200, 200, 199, 199, 199, 199, 198, 198, 198, 198, 197, 197, 197, 196, 196, 196, 196, 195, 195, 195, 195, 194, 194, 194, 194, 193, 193, 193, 193, 192, 192, 192, 192, 192, 191, 191, 191, 191, 190, 190, 190, 190, 189, 189, 189, 189, 189, 188, 188, 188, 188, 187, 187, 187, 187, 187, 186, 186, 186, 186, 186, 185, 185, 185, 185, 184, 184, 184, 184, 184, 183, 183, 183, 183, 183, 182, 182, 182, 182, 182, 181, 181, 181, 181, 181, 180, 180, 180, 180, 180, 180, 179, 179, 179, 179, 179, 178, 178, 178, 178, 178, 177, 177, 177, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 175, 175, 174, 174, 174, 174, 174, 174, 173, 173, 173, 173, 173, 173, 172, 172, 172, 172, 172, 172, 171, 171, 171, 171, 0, ACC_CURVE,CRV_SMEARING , 4705, 3056, 2724, 2497, 1498, 1498, 1374, 1276, 1196, 1130, 1073, 1025, 982, 944, 911, 880, 853, 828, 805, 784, 764, 746, 730, 714, 699, 685, 675, 0, DEC_CURVE,CRV_NORMALSCAN, 171, 172, 172, 173, 174, 174, 175, 176, 176, 177, 178, 178, 179, 180, 181, 181, 182, 183, 184, 184, 185, 186, 187, 188, 189, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, 211, 213, 214, 215, 217, 218, 219, 221, 222, 224, 225, 227, 228, 230, 232, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255, 258, 260, 262, 265, 268, 270, 273, 276, 279, 282, 285, 289, 292, 296, 299, 303, 307, 311, 316, 320, 325, 330, 335, 341, 347, 353, 359, 366, 373, 381, 390, 398, 408, 418, 429, 441, 455, 469, 485, 503, 523, 545, 571, 601, 636, 678, 730, 796, 883, 1005, 1195, 1544, 4705, 0, DEC_CURVE,CRV_PARKHOME , 171, 172, 172, 173, 173, 174, 174, 175, 175, 176, 176, 177, 177, 178, 179, 179, 180, 180, 181, 182, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, 192, 192, 193, 194, 195, 195, 196, 197, 198, 199, 199, 200, 201, 202, 203, 204, 205, 206, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 217, 218, 219, 220, 221, 222, 223, 225, 226, 227, 228, 230, 231, 232, 234, 235, 237, 238, 240, 241, 243, 244, 246, 247, 249, 251, 253, 254, 256, 258, 260, 262, 264, 266, 268, 271, 273, 275, 278, 280, 282, 285, 288, 290, 293, 296, 299, 302, 305, 309, 312, 316, 319, 323, 327, 331, 336, 340, 345, 350, 355, 360, 365, 371, 377, 384, 391, 398, 406, 414, 422, 432, 441, 452, 463, 476, 489, 504, 520, 538, 558, 580, 605, 633, 667, 706, 752, 810, 883, 979, 1116, 1326, 1714, 4705, 0, DEC_CURVE,CRV_SMEARING , 675, 685, 699, 714, 730, 746, 764, 784, 805, 828, 853, 880, 911, 944, 982, 1025, 1073, 1130, 1196, 1276, 1374, 1498, 1498, 2497, 2724, 3056, 4705, 0, DEC_CURVE,CRV_BUFFERFULL, 171, 172, 172, 173, 174, 174, 175, 176, 176, 177, 178, 178, 179, 180, 181, 181, 182, 183, 184, 184, 185, 186, 187, 188, 189, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 207, 208, 209, 210, 211, 213, 214, 215, 217, 218, 219, 221, 222, 224, 225, 227, 228, 230, 232, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255, 258, 260, 262, 265, 268, 270, 273, 276, 279, 282, 285, 289, 292, 296, 299, 303, 307, 311, 316, 320, 325, 330, 335, 341, 347, 353, 359, 366, 373, 381, 390, 398, 408, 418, 429, 441, 455, 469, 485, 503, 523, 545, 571, 601, 636, 678, 730, 796, 883, 1005, 1195, 1544, 4705, 0, -2, /* motorcurve 5 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN, 3763, 3763, 3763, 3763, 3763, 3763, 2444, 2178, 1997, 1198, 1198, 1098, 1020, 956, 903, 858, 819, 785, 754, 727, 703, 681, 662, 644, 626, 610, 596, 582, 571, 558, 547, 537, 527, 518, 509, 500, 492, 485, 478, 471, 464, 458, 452, 446, 440, 435, 430, 425, 420, 415, 410, 407, 402, 398, 394, 391, 386, 383, 380, 376, 373, 369, 366, 364, 360, 357, 355, 352, 349, 347, 344, 341, 338, 337, 334, 332, 329, 328, 325, 323, 321, 319, 317, 315, 313, 311, 310, 308, 306, 304, 302, 301, 299, 297, 295, 294, 293, 291, 290, 288, 286, 285, 284, 283, 281, 280, 278, 277, 275, 275, 274, 272, 271, 270, 268, 267, 266, 265, 264, 263, 262, 261, 259, 258, 257, 257, 256, 255, 254, 253, 251, 250, 249, 248, 248, 247, 246, 245, 244, 243, 243, 242, 241, 240, 239, 239, 238, 237, 236, 235, 235, 234, 233, 232, 231, 230, 230, 230, 229, 228, 228, 227, 226, 225, 225,224, 223, 222, 222, 221, 221, 221, 220, 219, 219, 218, 217, 217, 216, 215, 215, 214, 213, 213, 212, 212, 212, 211, 211, 210, 209, 209, 208, 208, 207, 207, 206, 206, 205, 204, 204, 203, 203, 203, 203, 202, 202, 201, 201, 200, 200, 199, 199, 198, 198, 197, 197, 196, 196, 195, 195, 194, 194, 194, 194, 194, 193, 193, 192, 192, 191, 191, 190, 190, 190, 189, 189, 188, 188, 188, 187, 187, 186, 186, 185, 185, 185, 185, 185, 185, 184, 184, 183, 183, 183, 182, 182, 182, 181, 181, 180, 180, 180, 179, 179, 179, 178, 178, 178, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 174, 174, 174, 173, 173, 173, 172, 172, 172, 171, 171, 171, 171, 170, 170, 170, 169, 169, 169, 169, 168, 168, 168, 167, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 165, 165, 165, 165, 164, 164, 164, 163, 163, 163, 163, 162, 162, 162, 162, 161, 161, 161, 161, 160, 160, 160, 160, 159, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 156, 156, 156, 156, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 153, 153, 153, 153, 152, 152, 152, 152, 152, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 148, 148, 148, 148, 148, 147, 147, 147, 147, 147, 147, 146, 146, 146, 146, 146, 145, 145, 145, 145, 145, 145, 144, 144, 144, 144, 144, 144, 143, 143, 143, 143, 143, 143, 142, 142, 142, 142, 142, 142, 141, 141, 141, 141, 141, 141, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 139, 139, 139, 139, 139, 139, 139, 138, 138, 138, 138, 138, 138, 138, 137, 137, 137, 137, 137, 137, 137, 136, 136, 136, 136, 136, 136, 136, 135, 135, 135, 135, 135, 135, 135, 134, 134, 134, 134, 134, 134, 134, 134, 133, 133, 133, 133, 133, 133, 133, 133, 132, 132, 132, 132, 132, 132, 132, 132, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131, 130, 130, 130, 130, 130, 130, 130, 130, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 127, 127, 127, 127, 127, 127, 127, 127, 127, 126, 126, 126, 126, 126, 126, 126, 126, 126, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 101, 101, 101, 101, 101, 101, 101, 101, 101,101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 0, ACC_CURVE,CRV_PARKHOME , 3763, 2330, 1803, 1515, 1330, 1198, 1099, 1021, 957, 904, 859, 819, 785, 755, 728, 704, 682, 662, 644, 626, 611, 597, 583, 571, 559, 548, 537, 527, 518, 509, 501, 493, 485, 478, 472, 464, 458, 453, 446, 441, 436, 430, 425, 420, 416, 411, 407, 402, 399, 394, 391, 387, 383, 380, 376, 374, 370, 366, 364, 361, 358, 355, 352, 349, 347, 344, 342, 339, 337, 335, 332, 330, 328, 326, 323, 321, 320, 318, 315, 313, 311, 310, 308, 306, 304, 302, 301, 300, 298, 296, 294, 293, 292, 290, 289, 287, 285, 284, 283, 282, 280, 279, 277, 276, 275, 274, 273, 271, 270, 269, 267, 266, 266, 265, 263, 262, 261, 260, 259, 258, 257, 256, 255, 254, 253, 252, 251, 250, 249, 248, 248, 247, 246, 245, 244, 243, 242, 241, 240, 239, 239, 239, 238, 237, 236, 235, 234, 233, 233, 232, 231, 230, 230, 230, 229, 228, 227, 227, 226, 225, 224, 224, 223, 222, 221, 221, 221, 220, 220, 219, 218, 218, 217, 216, 216, 215, 214, 214, 213, 213, 212, 212, 212, 211, 211, 210, 209, 209, 208, 208, 207, 207, 206, 205, 205, 204, 204, 203, 203, 203, 203, 202, 202, 201, 201, 200, 200, 199, 199, 198, 198, 197, 197, 196, 196, 195, 195, 194, 194, 194, 194, 194, 193, 193, 192, 192, 191, 191, 191, 190, 190, 189, 189, 188, 188, 188, 187, 187, 186, 186, 186, 185, 185, 185, 185, 185, 184, 184, 184, 183, 183, 182, 182, 182, 181, 181, 181, 180, 180, 180, 179, 179, 178, 178, 178, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 174, 174, 174, 174, 173, 173, 173, 172, 172, 172, 171, 171, 171, 170, 170, 170, 170, 169, 169, 169, 168, 168, 168, 168, 167, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 165, 165, 165, 165, 164, 164, 164, 163, 163, 163, 163, 162, 162, 162, 162, 161, 161, 161, 161, 160, 160, 160, 160, 160, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 156, 156, 156, 156, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 153, 153, 153, 153, 153, 152, 152, 152, 152, 152, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 148, 148, 148, 148, 148, 148, 147, 147, 147, 147, 147, 147, 146, 146, 146, 146, 146, 145, 145, 145, 145, 145, 145, 144, 144, 144, 144, 144, 144, 143, 143, 143, 143, 143, 143, 142, 142, 142, 142, 142, 142, 141, 141, 141, 141, 141, 141, 141, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 139, 139, 139, 139, 139, 139, 139, 138, 0, ACC_CURVE,CRV_SMEARING , 3763, 2330, 1803, 1515, 1330, 1198, 1099, 1021, 957, 904, 859, 819, 785, 755, 728, 704, 682, 662, 644, 626, 611, 597, 583, 571, 559, 548, 537, 527, 518, 509, 501, 493, 485, 478, 472, 464, 458, 453, 446, 441, 436, 430, 425, 420, 416, 411, 407, 402, 399, 394, 391, 387, 383, 380, 376, 374, 370, 366, 364, 361, 358, 355, 352, 349, 347, 344, 342, 339, 337, 335, 332, 330, 328, 326, 323, 321, 320, 318, 315, 313, 311, 310, 308, 306, 304, 302, 301, 300, 298, 296, 294, 293, 292, 290, 289, 287, 285, 284, 283, 282, 280, 279, 277, 276, 275, 274, 273, 271, 270, 269, 267, 266, 266, 265, 263, 262, 261, 260, 259, 258, 257, 256, 255, 254, 253, 252, 251, 250, 249, 248, 248, 247, 246, 245, 244, 243, 242, 241, 240, 239, 239, 239, 238, 237, 236, 235, 234, 233, 233, 232, 231, 230, 230, 230, 229, 228, 227, 227, 226, 225, 224, 224, 223, 222, 221, 221, 221, 220, 220, 219, 218, 218, 217, 216, 216, 215, 214, 214, 213, 213, 212, 212, 212, 211, 211, 210, 209, 209, 208, 208, 207, 207, 206, 205, 205, 204, 204, 203, 203, 203, 203, 202, 202, 201, 201, 200, 200, 199, 199, 198, 198, 197, 197, 196, 196, 195, 195, 194, 194, 194, 194, 194, 193, 193, 192, 192, 191, 191, 191, 190, 190, 189, 189, 188, 188, 188, 187, 187, 186, 186, 186, 185, 185, 185, 185, 185, 184, 184, 184, 183, 183, 182, 182, 182, 181, 181, 181, 180, 180, 180, 179, 179, 178, 178, 178, 177, 177, 177, 176, 176, 176, 176, 176, 176, 175, 175, 175, 174, 174, 174, 174, 173, 173, 173, 172, 172, 172, 171, 171, 171, 170, 170, 170, 170, 169, 169, 169, 168, 168, 168, 168, 167, 167, 167, 167, 167, 167, 167, 166, 166, 166, 166, 165, 165, 165, 165, 164, 164, 164, 163, 163, 163, 163, 162, 162, 162, 162, 161, 161, 161, 161, 160, 160, 160, 160, 160, 159, 159, 159, 159, 158, 158, 158, 158, 158, 158, 158, 158, 157, 157, 157, 157, 157, 156, 156, 156, 156, 155, 155, 155, 155, 155, 154, 154, 154, 154, 154, 153, 153, 153, 153, 153, 152, 152, 152, 152, 152, 151, 151, 151, 151, 151, 150, 150, 150, 150, 150, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 148, 148, 148, 148, 148, 148, 147, 147, 147, 147, 147, 147, 146, 146, 146, 146, 146, 145, 145, 145, 145, 145, 145, 144, 144, 144, 144, 144, 144, 143, 143, 143, 143, 143, 143, 142, 142, 142, 142, 142, 142, 141, 141, 141, 141, 141, 141, 141, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 139, 139, 139, 139, 139, 139, 139, 138, 0, DEC_CURVE,CRV_NORMALSCAN, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 87, 87, 87, 87, 87, 87, 87, 87, 88, 88, 88, 88, 88, 88, 88, 88, 89, 89, 89, 89, 89, 89, 89, 90, 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 92, 92, 92, 92, 93, 93, 93, 93, 94, 94, 94, 95, 95, 95, 95, 95, 96, 96, 97, 97, 98, 98, 99, 99, 100, 100, 101, 101, 102, 102, 103, 104, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 113, 115, 117, 119, 121, 122, 124, 127, 130, 132, 135, 139, 142, 146, 149, 153, 158, 162, 167, 171, 176, 180, 186, 193, 202, 209, 216, 223, 232, 243, 254, 266, 279, 292, 306, 320, 335, 337, 351, 367, 380, 396, 414, 437, 464, 493, 520, 549, 583, 611, 644, 675, 711, 755, 785, 819, 859, 904, 957, 1021, 1099, 1198, 1330, 1515, 1803, 2330, 3763, 0, DEC_CURVE,CRV_PARKHOME , 138, 142, 146, 149, 153, 158, 162, 167, 171, 176, 180, 186, 193, 202, 209, 216, 223, 232, 243, 254, 266, 279, 292, 306, 320, 335, 337, 351, 367, 380, 396, 414, 437, 464, 493, 520, 549, 583, 611, 644, 675, 711, 755, 785, 819, 859, 904, 957, 1021, 1099, 1198, 1330, 1515, 1803, 2330, 3763, 0, DEC_CURVE,CRV_SMEARING , 138, 139, 139, 139, 139, 139, 139, 139, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 141, 141, 141, 141, 141, 141, 141, 142, 142, 142, 142, 142, 142, 143, 143, 143, 143, 143, 143, 144, 144, 144, 144, 144, 144, 145, 145, 145, 145, 145, 145, 146, 146, 146, 146, 146, 147, 147, 147, 147, 147, 147, 148, 148, 148, 148, 148, 148, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 150, 150, 150, 150, 150, 151, 151, 151, 151, 151, 152, 152, 152, 152, 152, 153, 153, 153, 153, 153, 154, 154, 154, 154, 154, 155, 155, 155, 155, 155, 156, 156, 156, 156, 157, 157, 157, 157, 157, 158, 158, 158, 158, 158, 158, 158, 158, 159, 159, 159, 159, 160, 160, 160, 160, 160, 161, 161, 161, 161, 162, 162, 162, 162, 163, 163, 163, 163, 164, 164, 164, 165, 165, 165, 165, 166, 166, 166, 166, 167, 167, 167, 167, 167, 167, 167, 168, 168, 168, 168, 169, 169, 169, 170, 170, 170, 170, 171, 171, 171, 172, 172, 172, 173, 173, 173, 174, 174, 174, 174, 175, 175, 175, 176, 176, 176, 176, 176, 176, 177, 177, 177, 178, 178, 178, 179, 179, 180, 180, 180, 181, 181, 181, 182, 182, 182, 183, 183, 184, 184, 184, 185, 185, 185, 185, 185, 186, 186, 186, 187, 187, 188, 188, 188, 189, 189, 190, 190, 191, 191, 191, 192, 192, 193, 193, 194, 194, 194, 194, 194, 195, 195, 196, 196, 197, 197, 198, 198, 199, 199, 200, 200, 201, 201, 202, 202, 203, 203, 203, 203, 204, 204, 205, 205, 206, 207, 207, 208, 208, 209, 209, 210, 211, 211, 212, 212, 212, 213, 213, 214, 214, 215, 216, 216, 217, 218, 218, 219, 220, 220, 221, 221, 221, 222, 223, 224, 224, 225, 226, 227, 227, 228, 229, 230, 230, 230, 231, 232, 233, 233, 234, 235, 236, 237, 238, 239, 239, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 265, 266, 266, 267, 269, 270, 271, 273, 274, 275, 276, 277, 279, 280, 282, 283, 284, 285, 287, 289, 290, 292, 293, 294, 296, 298, 300, 301, 302, 304, 306, 308, 310, 311, 313, 315, 318, 320, 321, 323, 326, 328, 330, 332, 335, 337, 339, 342, 344, 347, 349, 352, 355, 358, 361, 364, 366, 370, 374, 376, 380, 383, 387, 391, 394, 399, 402, 407, 411, 416, 420, 425, 430, 436, 441, 446, 453, 458, 464, 472, 478, 485, 493, 501, 509, 518, 527, 537, 548, 559, 571, 583, 597, 611, 626, 644, 662, 682, 704, 728, 755, 785, 819, 859, 904, 957, 1021, 1099, 1198, 1330, 1515, 1803, 2330, 3763, 0, DEC_CURVE,CRV_BUFFERFULL, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 94, 94, 94, 95, 95, 95, 95, 95, 96, 96, 97, 97, 98, 98, 99, 99, 100, 100, 101, 101, 102, 102, 103, 104, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 113, 115, 117, 119, 121, 122, 124, 127, 130, 132, 135, 139, 142, 146, 149, 153, 158, 162, 167, 171, 176, 180, 186, 193, 202, 209, 216, 223, 232, 243, 254, 266, 279, 292, 306, 320, 335, 337, 351, 367, 380, 396, 414, 437, 464, 493, 520, 549, 583, 611, 644, 675, 711, 755, 785, 819, 859, 904, 957, 1021, 1099, 1198, 1330, 1515, 1803, 2330, 3763, 0, -2, /* motorcurve 6 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN, 23999, 0, ACC_CURVE,CRV_PARKHOME , 5360, 3362, 2628, 2229, 1973, 1791, 1654, 1547, 1458, 1384, 1319, 1264, 1214, 1170, 1131, 1096, 1063, 1034, 1006, 981, 958, 937, 916, 897, 880, 863, 847, 832, 818, 805, 792, 780, 769, 758, 747, 737, 727, 718, 709, 700, 692, 0, ACC_CURVE,CRV_SMEARING , 23999, 0, DEC_CURVE,CRV_NORMALSCAN, 23999, 0, DEC_CURVE,CRV_PARKHOME , 692, 700, 709, 718, 727, 737, 747, 758, 769, 780, 792, 805, 818, 832, 847, 863, 880, 897, 916, 937, 958, 981, 1006, 1034, 1063, 1096, 1131, 1170, 1214, 1264, 1319, 1384, 1458, 1547, 1654, 1791, 1973, 2229, 2628, 3362, 5360, 0, DEC_CURVE,CRV_SMEARING , 23999, 0, DEC_CURVE,CRV_BUFFERFULL, 23999, 0, -2, /* motorcurve 7 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN, 6667, 0, ACC_CURVE,CRV_PARKHOME , 5360, 3362, 2628, 2229, 1973, 1791, 1654, 1547, 1458, 1384, 1319, 1264, 1214, 1170, 1131, 1096, 1063, 1034, 1006, 981, 958, 937, 916, 897, 880, 863, 847, 832, 818, 805, 792, 780, 769, 758, 747, 737, 727, 718, 709, 700, 692, 0, ACC_CURVE,CRV_SMEARING , 6667, 0, DEC_CURVE,CRV_NORMALSCAN, 6667, 0, DEC_CURVE,CRV_PARKHOME , 692, 700, 709, 718, 727, 737, 747, 758, 769, 780, 792, 805, 818, 832, 847, 863, 880, 897, 916, 937, 958, 981, 1006, 1034, 1063, 1096, 1131, 1170, 1214, 1264, 1319, 1384, 1458, 1547, 1654, 1791, 1973, 2229, 2628, 3362, 5360, 0, DEC_CURVE,CRV_SMEARING , 6667, 0, DEC_CURVE,CRV_BUFFERFULL, 6667, 0, -2, /* motorcurve 8 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN, 1046, 1046, 1046, 1046, 1046, 1046, 647, 501, 421, 370, 333, 305, 284, 266, 251, 239, 228, 218, 210, 202, 196, 190, 184, 179, 174, 170, 166, 162, 159, 155, 152, 149, 147, 144, 142, 139, 137, 135, 133, 131, 129, 127, 126, 124, 123, 121, 120, 118, 117, 116, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 100, 99, 98, 97, 96, 96, 95, 94, 94, 93, 92, 92, 91, 91, 90, 89, 89, 88, 88, 87, 87, 86, 86, 85, 85, 84, 84, 83, 83, 82, 82, 82, 81, 81, 80, 80, 79, 79, 79, 78, 78, 78, 77, 77, 76, 76, 76, 75, 75, 75, 74, 74, 74, 74, 73, 73, 73, 72, 72, 72, 71, 71, 71, 71, 70, 70, 70, 70, 69, 69, 69, 69, 68, 68, 68, 68, 67, 67, 67, 67, 66, 66, 66, 66, 65, 65, 65, 65, 64, 64, 64, 64, 63, 63, 63, 63, 62, 62, 62, 62, 61, 61, 61, 61, 61, 60, 60, 60, 60, 60, 60, 59, 59, 59, 59, 59, 59, 59, 58, 58, 58, 58, 58, 58, 57, 57, 57, 57, 57, 57, 57, 56, 56, 56, 56, 56, 56, 56, 56, 55, 55, 55, 55, 55, 55, 55, 55, 54, 54, 54, 54, 54, 54, 54, 54, 53, 53, 53, 53, 53, 53, 53, 53, 52, 52, 52, 52, 52, 52, 52, 52, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 23, 23, 0, ACC_CURVE,CRV_PARKHOME , 1045, 678, 604, 554, 332, 332, 304, 283, 265, 250, 238, 227, 217, 209, 201, 195, 189, 183, 178,173, 169, 165, 161, 158, 154, 151, 148, 146, 143, 141, 138, 136, 134, 132, 130, 128, 126, 125, 123, 122, 120, 119, 117, 116, 115, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103,102, 101, 100, 99, 99, 98, 97, 96, 95, 95, 94, 93, 93, 92, 91, 91, 90, 90, 89, 88, 88, 87, 87, 86, 86, 85, 85, 84, 84, 83, 83, 82, 82, 81, 81, 81, 80, 80, 79, 79, 78, 78, 78, 77, 77, 77, 76, 76, 75, 75, 75, 74, 74, 74, 73, 73, 73, 73, 72, 72, 72, 71, 71, 71, 70, 70, 70, 70, 69, 69, 69, 69, 68, 68, 68, 68, 67, 67, 67, 67, 66, 66, 66, 66, 65, 65, 65, 65, 65, 64, 64, 64, 64, 64, 63, 63, 63, 63, 63, 62, 62, 62, 62, 62, 61, 61, 61, 61, 61, 61, 60, 60, 60, 60, 60, 60, 59, 59, 59, 59, 59, 59, 58, 58, 58, 58, 58, 58, 58, 57, 57, 57, 57, 57, 57, 57, 56, 56, 56, 56, 56, 56, 56, 55, 55, 55, 55, 55, 55, 55, 55, 54, 54, 54, 54, 54, 54, 54, 54, 53, 53, 53, 53, 53, 53, 53, 53, 53, 52, 52, 52, 52, 52, 52, 52, 52, 52, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 39, 0, ACC_CURVE,CRV_SMEARING , 1045, 678, 604, 554, 332, 332, 304, 283, 265, 250, 238, 227, 217, 209, 201, 195, 189, 183, 178,173, 169, 165, 161, 158, 154, 151, 148, 146, 143, 141, 138, 136, 134, 132, 130, 128, 126, 125, 123, 122, 120, 119, 117, 116, 115, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103,102, 101, 100, 99, 99, 98, 97, 96, 95, 95, 94, 93, 93, 92, 91, 91, 90, 90, 89, 88, 88, 87, 87, 86, 86, 85, 85, 84, 84, 83, 83, 82, 82, 81, 81, 81, 80, 80, 79, 79, 78, 78, 78, 77, 77, 77, 76, 76, 75, 75, 75, 74, 74, 74, 73, 73, 73, 73, 72, 72, 72, 71, 71, 71, 70, 70, 70, 70, 69, 69, 69, 69, 68, 68, 68, 68, 67, 67, 67, 67, 66, 66, 66, 66, 65, 65, 65, 65, 65, 64, 64, 64, 64, 64, 63, 63, 63, 63, 63, 62, 62, 62, 62, 62, 61, 61, 61, 61, 61, 61, 60, 60, 60, 60, 60, 60, 59, 59, 59, 59, 59, 59, 58, 58, 58, 58, 58, 58, 58, 57, 57, 57, 57, 57, 57, 57, 56, 56, 56, 56, 56, 56, 56, 55, 55, 55, 55, 55, 55, 55, 55, 54, 54, 54, 54, 54, 54, 54, 54, 53, 53, 53, 53, 53, 53, 53, 53, 53, 52, 52, 52, 52, 52, 52, 52, 52, 52, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 39, 0, DEC_CURVE,CRV_NORMALSCAN, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 36, 37, 38, 38, 39, 40, 41, 42, 43, 45, 46, 47, 48, 50, 51, 53, 54, 57, 59, 61, 63, 65, 68, 71, 75, 78, 82, 86, 90, 94, 94, 98, 103, 106, 111, 116, 122, 130, 138, 145, 153, 163, 171, 180, 188, 198, 211, 219, 228, 239, 252, 267, 284, 306, 334, 370, 422, 502, 648, 1045, 0, DEC_CURVE,CRV_PARKHOME , 39, 40, 41, 42, 43, 45, 46, 47, 48, 50, 51, 53, 54, 57, 59, 61, 63, 65, 68, 71, 75, 78, 82, 86, 90, 94, 94, 98, 103, 106, 111, 116, 122, 130, 138, 145, 153, 163, 171, 180, 188, 198, 211, 219, 228, 239, 252, 267, 284, 306, 334, 370, 422, 502, 648, 1045, 0, DEC_CURVE,CRV_SMEARING , 39, 39, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 57, 57, 58, 58, 58, 58, 58, 58, 58, 59, 59, 59, 59, 59, 59, 59, 60, 60, 60, 60, 60, 60, 60, 61, 61, 61, 61, 61, 61, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, 66, 66, 66, 66, 66, 67, 67, 67, 67, 67, 68, 68, 68, 68, 69, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, 72, 72, 72, 73, 73, 73, 73, 74, 74, 74, 75, 75, 75, 76, 76, 76, 77, 77, 77, 78, 78, 78, 79, 79, 79, 80, 80, 81, 81, 81, 82, 82, 83, 83, 84, 84, 84, 85, 85, 86, 86, 87, 87, 88, 88, 89, 90, 90, 91, 91, 92, 93, 93, 94, 94, 95, 96, 96, 97, 98, 99, 99, 100, 101, 102, 103, 104, 105, 105, 106, 107, 108, 109, 110, 112, 113, 114, 115, 116, 118, 119, 120, 122, 123, 125, 127, 128, 130, 132, 134, 136, 138, 140, 142, 145, 147, 150, 153, 156, 159, 163, 167, 171, 175, 180, 185, 190, 196, 203, 211, 219, 228, 239, 252, 267, 284, 306, 334, 370, 422, 502, 648, 1045, 0, DEC_CURVE,CRV_BUFFERFULL, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 36, 37, 38, 38, 39, 40, 41, 42, 43, 45, 46, 47, 48, 50, 51, 53, 54, 57, 59, 61, 63, 65, 68, 71, 75, 78, 82, 86, 90, 94, 94, 98, 103, 106, 111, 116, 122, 130, 138, 145, 153, 163, 171, 180, 188, 198, 211, 219, 228, 239, 252, 267, 284, 306, 334, 370, 422, 502, 648, 1045, 0, -1 }; rst = (SANE_Int *)malloc(sizeof(steps)); if (rst != NULL) memcpy(rst, &steps, sizeof(steps)); return rst; } static SANE_Int *hp3800_motor() { SANE_Int *rst = NULL; SANE_Int steps[] = { /* motorcurve 1 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN,2000,1984,1968,1953,1937,1921,1906,1890,1874,1859,1843,1827,1812,1796,1781,1765,1749,1734,1715,1700,1684,1669,1653,1637,1622,1606,1590,1572,1556,1541,1525,1510,1494,1478,1463,1447,1431,1416,1400,1384,1366,1351,1335,1319,1304,1288,1272,1257,1241,1225,1210,1194,1179,1160,1145,1129,1113,1098,1082,1066,1051,1035,1017,1001,986,970,954,939,923,907,892,876,861,845,829,814,798,782,767,749, 0, ACC_CURVE,CRV_PARKHOME ,4705,2913,2253,1894,1662,1498,1374,1276,1196,1129,1073,1024,981,944,910,880,852,827,804,783,764,746,729,713,699,685,672,659,648,637,626,616,607,598,589,581,573,565,558,551,544,538,532,526,520,514,509,503,500, 0, ACC_CURVE,CRV_SMEARING ,200,12,14,16, 0, DEC_CURVE,CRV_NORMALSCAN,749,1166,1583,2000, 0, DEC_CURVE,CRV_PARKHOME ,500,503,509,514,520,526,532,538,544,551,558,565,573,581,589,598,607,616,626,637,648,659,672,685,699,713,729,746,764,783,804,827,852,880,910,944,981,1024,1073,1129,1196,1276,1374,1498,1662,1894,2253,2913,4705, 0, DEC_CURVE,CRV_SMEARING ,300,234,167,100, 0, DEC_CURVE,CRV_BUFFERFULL,1100,867,633,400, 0, -2, /* motorcurve 2 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN,4705,2664,2061,1732,1521,1370,1257,1167,1094,1033,982,937,898,864,833,805,780,757,736,717,699,683,667,653,640,627,615,604,593,583,574,564,556,547,540,532,525,518,511,505,499,493,487,481,476,471,466,461,456,452,447,443,439,435,431,427,424,420,417,413,410,407,403,400,397,394,391,389,386,383,381,378,375,373,371,368,366,364,361,359,357,355,353,351,349,347,345,343,341,339,338,336,334,332,331,329,327,326,324,323,321,320,318,317,315,314,312,311,310,308,307,306,304,303,302,301,299,298,297,296,295,293,292,291,290,289,288,287,286,285,284,283,282,281,280,279,278,277,276,275,274,273,272,271,270,270,269,268,267,266,265,264,264,263,262,261,260,260,259,258,257,257,256,255,255,254,253,252,252,251,250,250,249,248,248,247,246,246,245,244,244,243,242,242,241,241,240,239,239,238,238,237,237,236,235,235,234,234,233,233,232,232,231,231,230,230,229,229,228,227,227,227,226,226,225,225,224,224,223,223,222,222,221,221,220,220,219,219,219,218,218,217,217,216,216,216,215,215,214,214,214,213,213,212,212,212,211,211,210,210,210,209,209,208,208,208,207,207,207,206,206,206,205,205,204,204,204,203,203,203,202,202,202,201,201,201,200,200,200,199,199,199,198,198,198,197,197,197,197,196,196,196,195,195,195,194,194,194,194,193,193,193,192,192,192,192,191,191,191,190,190,190,190,189,189,189,188,188,188,188,187,187,187,187,186,186,186,186,185,185,185,185,184,184,184,184,183,183,183,183,182,182,182,182,181,181,181,181,181,180,180,180,180,179,179,179,179,178,178,178,178,178,177,177,177,177,177,176,176,176,176,175,175,175,175,175,174,174,174,174,174,173,173,173,173,173,172,172,172,172,172,171,171,171, 0, ACC_CURVE,CRV_PARKHOME ,4705,2913,2253,1894,1662,1498,1374,1276,1196,1129,1073,1024,981,944,910,880,852,827,804,783,764,746,729,713,699,685,672,659,648,637,626,616,607,598,589,581,573,565,558,551,544,538,532,526,520,514,509,503,500, 0, ACC_CURVE,CRV_SMEARING ,200,12,14,16, 0, DEC_CURVE,CRV_NORMALSCAN,171,172,172,173,174,174,175,176,176,177,178,178,179,180,181,181,182,183,184,184,185,186,187,188,189,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,207,208,209,210,211,213,214,215,217,218,219,221,222,224,225,227,228,230,232,233,235,237,239,241,243,245,247,249,251,253,255,258,260,262,265,268,270,273,276,279,282,285,289,292,296,299,303,307,311,316,320,325,330,335,341,347,353,359,366,373,381,390,398,408,418,429,441,455,469,485,503,523,545,571,601,636,678,730,796,883,1005,1195,1544,4705, 0, DEC_CURVE,CRV_PARKHOME ,500,503,509,514,520,526,532,538,544,551,558,565,573,581,589,598,607,616,626,637,648,659,672,685,699,713,729,746,764,783,804,827,852,880,910,944,981,1024,1073,1129,1196,1276,1374,1498,1662,1894,2253,2913,4705, 0, DEC_CURVE,CRV_SMEARING ,300,234,167,100, 0, DEC_CURVE,CRV_BUFFERFULL,1100,867,633,400, 0, -2, /* motorcurve 3 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN,5360,3655,2855,2422,2142,1944,1795,1678,1582,1503,1434,1374, 0, ACC_CURVE,CRV_PARKHOME ,5360,3362,2628,2229,1973,1791,1654,1547,1458,1384,1319,1264,1214,1170,1131,1096,1063,1034,1006,981,958,937,916,897,880,863,847,832,818,805,792,780,769,758,747,737,727,718,709,700,692,684,677,669,662,655,648,642,636,629,624,618,612,607,602,596,591,587,582,577,573,568,564,560,556,552,548,544,540,537,533,530,526,523,520,516,513,510,507,504,501,498,496,493,490,488,485,482,480,477,475,472,470,468,466,463,461,459,457,455,453,450,448,446,444,443,441,439,437,435,433,431,430,428,426,425,423,421,420,418,416,415,413,412,410,409,407,406,405,403,402,400,399,398,396,395,394,392,391,390,389,387,386,385,384,382,381,380,379,378,377,376,374,373,372,371,370,369,368,367,366,365,364,363,362,361,360,359,358,357,356,355,354,353,353,352,351,350,349,348, 0, ACC_CURVE,CRV_SMEARING ,5360,3362,2628,2229,1973,1791,1654,1547, 0, DEC_CURVE,CRV_NORMALSCAN,1374,1434,1503,1582,1678,1795,1944,2142,2422,2855,3655,5360, 0, DEC_CURVE,CRV_PARKHOME ,348,351,353,356,359,362,365,368,371,374,378,381,385,389,392,396,400,405,409,413,418,423,428,433,439,444,450,457,463,470,477,485,493,501,510,520,530,540,552,564,577,591,607,624,642,662,684,709,737,769,805,847,897,958,1034,1131,1264,1458,1791,2628,5360, 0, DEC_CURVE,CRV_SMEARING ,1547,1654,1791,1973,2229,2628,3362,5360, 0, DEC_CURVE,CRV_BUFFERFULL,1374,1434,1503,1582,1678,1795,1944,2142,2422,2855,3655,5360, 0, -2, /* motorcurve 4 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN,4705,2664,2061,1732,1521,1370,1257,1167,1094,1033,982,937,898,864,833,805,780,757,736,717,699,683,667,653,640,627,615,604,593,583,574,564,556,547,540,532,525,518,511,505,499,493,487,481,476,471,466,461,456,452,447,443,439,435,431,427,424,420,417,413,410,407,403,400,397,394,391,389,386,383,381,378,375,373,371,368,366,364,361,359,357,355,353,351,349,347,345,343,341,339,338,336,334,332,331,329,327,326,324,323,321,320,318,317,315,314,312,311,310,308,307,306,304,303,302,301,299,298,297,296,295,293,292,291,290,289,288,287,286,285,284,283,282,281,280,279,278,277,276,275,274,273,272,271,270,270,269,268,267,266,265,264,264,263,262,261,260,260,259,258,257,257,256,255,255,254,253,252,252,251,250,250,249,248,248,247,246,246,245,244,244,243,242,242,241,241,240,239,239,238,238,237,237,236,235,235,234,234,233,233,232,232,231,231,230,230,229,229,228,227,227,227,226,226,225,225,224,224,223,223,222,222,221,221,220,220,219,219,219,218,218,217,217,216,216,216,215,215,214,214,214,213,213,212,212,212,211,211,210,210,210,209,209,208,208,208,207,207,207,206,206,206,205,205,204,204,204,203,203,203,202,202,202,201,201,201,200,200,200,199,199,199,198,198,198,197,197,197,197,196,196,196,195,195,195,194,194,194,194,193,193,193,192,192,192,192,191,191,191,190,190,190,190,189,189,189,188,188,188,188,187,187,187,187,186,186,186,186,185,185,185,185,184,184,184,184,183,183,183,183,182,182,182,182,181,181,181,181,181,180,180,180,180,179,179,179,179,178,178,178,178,178,177,177,177,177,177,176,176,176,176,175,175,175,175,175,174,174,174,174,174,173,173,173,173,173,172,172,172,172,172,171,171,171, 0, ACC_CURVE,CRV_PARKHOME ,4705,2888,2234,1878,1648,1485,1362,1265,1186,1120,1064,1016,973,936,903,873,845,821,798,777,758,740,723,708,693,679,666,654,643,632,621,612,602,593,585,576,569,561,554,547,540,534,528,522,516,510,505,499,494,490,485,480,476,471,467,463,459,455,451,448,444,440,437,434,430,427,424,421,418,415,412,409,407,404,401,399,396,394,391,389,387,384,382,380,378,376,374,371,369,367,366,364,362,360,358,356,354,353,351,349,348,346,344,343,341,340,338,337,335,334,332,331,329,328,327,325,324,323,321,320,319,318,316,315,314,313,312,311,309,308,307,306,305,304,303,302,301,300,299,298,297,296,295,294,293,292,291,290,289,288,287,286,285,285,284,283,282,281,280,279,279,278,277,276,275,275,274,273,272,272,271,270,269,269,268,267,267,266,265,264,264,263,262,262,261,260,260,259,259,258,257,257,256,255,255,254,254,253,252,252,251,251,250,249,249,248,248,247,247,246,246,245,245,244,243,243,242,242,241,241,240,240,239,239,238,238,237,237,237,236,236,235,235,234,234,233,233,232,232,231,231,231,230,230,229,229,228,228,228,227,227,226,226,225,225,225,224,224,223,223,223,222,222,222,221,221,220,220,220,219,219,219,218,218,217,217,217,216,216,216,215,215,215,214,214,214,213,213,213,212,212,212,211,211,211,210,210,210,209,209,209,208,208,208,207,207,207,207,206,206,206,205,205,205,204,204,204,204,203,203,203,202,202,202,202,201,201,201,200,200,200,200,199,199,199,199,198,198,198,198,197,197,197,196,196,196,196,195,195,195,195,194,194,194,194,193,193,193,193,192,192,192,192,192,191,191,191,191,190,190,190,190,189,189,189,189,189,188,188,188,188,187,187,187,187,187,186,186,186,186,186,185,185,185,185,184,184,184,184,184,183,183,183,183,183,182,182,182,182,182,181,181,181,181,181,180,180,180,180,180,180,179,179,179,179,179,178,178,178,178,178,177,177,177,177,177,177,176,176,176,176,176,176,175,175,175,175,175,174,174,174,174,174,174,173,173,173,173,173,173,172,172,172,172,172,172,171,171,171,171, 0, ACC_CURVE,CRV_SMEARING ,4705,3056,2724,2497,1498,1498,1374,1276,1196,1130,1073,1025,982,944,911,880,853,828,805,784,764,746,730,714,699,685,675, 0, DEC_CURVE,CRV_NORMALSCAN,171,172,172,173,174,174,175,176,176,177,178,178,179,180,181,181,182,183,184,184,185,186,187,188,189,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,207,208,209,210,211,213,214,215,217,218,219,221,222,224,225,227,228,230,232,233,235,237,239,241,243,245,247,249,251,253,255,258,260,262,265,268,270,273,276,279,282,285,289,292,296,299,303,307,311,316,320,325,330,335,341,347,353,359,366,373,381,390,398,408,418,429,441,455,469,485,503,523,545,571,601,636,678,730,796,883,1005,1195,1544,4705, 0, DEC_CURVE,CRV_PARKHOME ,171,172,172,173,173,174,174,175,175,176,176,177,177,178,179,179,180,180,181,182,182,183,183,184,185,185,186,187,187,188,189,189,190,191,192,192,193,194,195,195,196,197,198,199,199,200,201,202,203,204,205,206,206,207,208,209,210,211,212,213,214,215,217,218,219,220,221,222,223,225,226,227,228,230,231,232,234,235,237,238,240,241,243,244,246,247,249,251,253,254,256,258,260,262,264,266,268,271,273,275,278,280,282,285,288,290,293,296,299,302,305,309,312,316,319,323,327,331,336,340,345,350,355,360,365,371,377,384,391,398,406,414,422,432,441,452,463,476,489,504,520,538,558,580,605,633,667,706,752,810,883,979,1116,1326,1714,4705, 0, DEC_CURVE,CRV_SMEARING ,675,685,699,714,730,746,764,784,805,828,853,880,911,944,982,1025,1073,1130,1196,1276,1374,1498,1498,2497,2724,3056,4705, 0, DEC_CURVE,CRV_BUFFERFULL,171,172,172,173,174,174,175,176,176,177,178,178,179,180,181,181,182,183,184,184,185,186,187,188,189,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,207,208,209,210,211,213,214,215,217,218,219,221,222,224,225,227,228,230,232,233,235,237,239,241,243,245,247,249,251,253,255,258,260,262,265,268,270,273,276,279,282,285,289,292,296,299,303,307,311,316,320,325,330,335,341,347,353,359,366,373,381,390,398,408,418,429,441,455,469,485,503,523,545,571,601,636,678,730,796,883,1005,1195,1544,4705, 0, -2, /* motorcurve 5 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN,3763,3763,3763,3763,3763,3763,2444,2178,1997,1198,1198,1098,1020,956,903,858,819,785,754,727,703,681,662,644,626,610,596,582,571,558,547,537,527,518,509,500,492,485,478,471,464,458,452,446,440,435,430,425,420,415,410,407,402,398,394,391,386,383,380,376,373,369,366,364,360,357,355,352,349,347,344,341,338,337,334,332,329,328,325,323,321,319,317,315,313,311,310,308,306,304,302,301,299,297,295,294,293,291,290,288,286,285,284,283,281,280,278,277,275,275,274,272,271,270,268,267,266,265,264,263,262,261,259,258,257,257,256,255,254,253,251,250,249,248,248,247,246,245,244,243,243,242,241,240,239,239,238,237,236,235,235,234,233,232,231,230,230,230,229,228,228,227,226,225,225,224,223,222,222,221,221,221,220,219,219,218,217,217,216,215,215,214,213,213,212,212,212,211,211,210,209,209,208,208,207,207,206,206,205,204,204,203,203,203,203,202,202,201,201,200,200,199,199,198,198,197,197,196,196,195,195,194,194,194,194,194,193,193,192,192,191,191,190,190,190,189,189,188,188,188,187,187,186,186,185,185,185,185,185,185,184,184,183,183,183,182,182,182,181,181,180,180,180,179,179,179,178,178,178,177,177,177,176,176,176,176,176,176,175,175,175,174,174,174,173,173,173,172,172,172,171,171,171,171,170,170,170,169,169,169,169,168,168,168,167,167,167,167,167,167,167,166,166,166,166,165,165,165,165,164,164,164,163,163,163,163,162,162,162,162,161,161,161,161,160,160,160,160,159,159,159,159,159,158,158,158,158,158,158,158,158,157,157,157,157,157,156,156,156,156,155,155,155,155,155,154,154,154,154,154,153,153,153,153,152,152,152,152,152,151,151,151,151,151,150,150,150,150,150,149,149,149,149,149,149,149,149,149,149,149,148,148,148,148,148,147,147,147,147,147,147,146,146,146,146,146,145,145,145,145,145,145,144,144,144,144,144,144,143,143,143,143,143,143,142,142,142,142,142,142,141,141,141,141,141,141,140,140,140,140,140,140,140,140,140,140,140,140,140,139,139,139,139,139,139,139,138,138,138,138,138,138,138,137,137,137,137,137,137,137,136,136,136,136,136,136,136,135,135,135,135,135,135,135,134,134,134,134,134,134,134,134,133,133,133,133,133,133,133,133,132,132,132,132,132,132,132,132,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,130,130,130,130,130,130,130,130,129,129,129,129,129,129,129,129,129,128,128,128,128,128,128,128,128,128,127,127,127,127,127,127,127,127,127,126,126,126,126,126,126,126,126,126,125,125,125,125,125,125,125,125,125,125,124,124,124,124,124,124,124,124,124,124,123,123,123,123,123,123,123,123,123,123,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,121,121,121,121,121,121,121,121,121,121,121,120,120,120,120,120,120,120,120,120,120,120,119,119,119,119,119,119,119,119,119,119,119,119,118,118,118,118,118,118,118,118,118,118,118,118,117,117,117,117,117,117,117,117,117,117,117,117,116,116,116,116,116,116,116,116,116,116,116,116,115,115,115,115,115,115,115,115,115,115,115,115,115,114,114,114,114,114,114,114,114,114,114,114,114,114,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,111,111,111,111,111,111,111,111,111,111,111,111,111,111,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,104,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93, 0, ACC_CURVE,CRV_PARKHOME ,3763,2330,1803,1515,1330,1198,1099,1021,957,904,859,819,785,755,728,704,682,662,644,626,611,597,583,571,559,548,537,527,518,509,501,493,485,478,472,464,458,453,446,441,436,430,425,420,416,411,407,402,399,394,391,387,383,380,376,374,370,366,364,361,358,355,352,349,347,344,342,339,337,335,332,330,328,326,323,321,320,318,315,313,311,310,308,306,304,302,301,300,298,296,294,293,292,290,289,287,285,284,283,282,280,279,277,276,275,274,273,271,270,269,267,266,266,265,263,262,261,260,259,258,257,256,255,254,253,252,251,250,249,248,248,247,246,245,244,243,242,241,240,239,239,239,238,237,236,235,234,233,233,232,231,230,230,230,229,228,227,227,226,225,224,224,223,222,221,221,221,220,220,219,218,218,217,216,216,215,214,214,213,213,212,212,212,211,211,210,209,209,208,208,207,207,206,205,205,204,204,203,203,203,203,202,202,201,201,200,200,199,199,198,198,197,197,196,196,195,195,194,194,194,194,194,193,193,192,192,191,191,191,190,190,189,189,188,188,188,187,187,186,186,186,185,185,185,185,185,184,184,184,183,183,182,182,182,181,181,181,180,180,180,179,179,178,178,178,177,177,177,176,176,176,176,176,176,175,175,175,174,174,174,174,173,173,173,172,172,172,171,171,171,170,170,170,170,169,169,169,168,168,168,168,167,167,167,167,167,167,167,166,166,166,166,165,165,165,165,164,164,164,163,163,163,163,162,162,162,162,161,161,161,161,160,160,160,160,160,159,159,159,159,158,158,158,158,158,158,158,158,157,157,157,157,157,156,156,156,156,155,155,155,155,155,154,154,154,154,154,153,153,153,153,153,152,152,152,152,152,151,151,151,151,151,150,150,150,150,150,149,149,149,149,149,149,149,149,149,149,148,148,148,148,148,148,147,147,147,147,147,147,146,146,146,146,146,145,145,145,145,145,145,144,144,144,144,144,144,143,143,143,143,143,143,142,142,142,142,142,142,141,141,141,141,141,141,141,140,140,140,140,140,140,140,140,140,140,140,140,140,139,139,139,139,139,139,139,138, 0, ACC_CURVE,CRV_SMEARING ,3763,2330,1803,1515,1330,1198,1099,1021,957,904,859,819,785,755,728,704,682,662,644,626,611,597,583,571,559,548,540, 0, DEC_CURVE,CRV_NORMALSCAN,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,87,87,87,87,87,87,87,87,88,88,88,88,88,88,88,88,89,89,89,89,89,89,89,90,90,90,90,90,90,91,91,91,91,91,92,92,92,92,93,93,93,93,94,94,94,95,95,95,95,95,96,96,97,97,98,98,99,99,100,100,101,101,102,102,103,104,104,105,106,107,108,109,110,111,112,113,113,115,117,119,121,122,124,127,130,132,135,139,142,146,149,153,158,162,167,171,176,180,186,193,202,209,216,223,232,243,254,266,279,292,306,320,335,337,351,367,380,396,414,437,464,493,520,549,583,611,644,675,711,755,785,819,859,904,957,1021,1099,1198,1330,1515,1803,2330,3763, 0, DEC_CURVE,CRV_PARKHOME ,138,142,146,149,153,158,162,167,171,176,180,186,193,202,209,216,223,232,243,254,266,279,292,306,320,335,337,351,367,380,396,414,437,464,493,520,549,583,611,644,675,711,755,785,819,859,904,957,1021,1099,1198,1330,1515,1803,2330,3763, 0, DEC_CURVE,CRV_SMEARING ,138,139,139,139,139,139,139,139,140,140,140,140,140,140,140,140,140,140,140,140,140,141,141,141,141,141,141,141,142,142,142,142,142,142,143,143,143,143,143,143,144,144,144,144,144,144,145,145,145,145,145,145,146,146,146,146,146,147,147,147,147,147,147,148,148,148,148,148,148,149,149,149,149,149,149,149,149,149,149,150,150,150,150,150,151,151,151,151,151,152,152,152,152,152,153,153,153,153,153,154,154,154,154,154,155,155,155,155,155,156,156,156,156,157,157,157,157,157,158,158,158,158,158,158,158,158,159,159,159,159,160,160,160,160,160,161,161,161,161,162,162,162,162,163,163,163,163,164,164,164,165,165,165,165,166,166,166,166,167,167,167,167,167,167,167,168,168,168,168,169,169,169,170,170,170,170,171,171,171,172,172,172,173,173,173,174,174,174,174,175,175,175,176,176,176,176,176,176,177,177,177,178,178,178,179,179,180,180,180,181,181,181,182,182,182,183,183,184,184,184,185,185,185,185,185,186,186,186,187,187,188,188,188,189,189,190,190,191,191,191,192,192,193,193,194,194,194,194,194,195,195,196,196,197,197,198,198,199,199,200,200,201,201,202,202,203,203,203,203,204,204,205,205,206,207,207,208,208,209,209,210,211,211,212,212,212,213,213,214,214,215,216,216,217,218,218,219,220,220,221,221,221,222,223,224,224,225,226,227,227,228,229,230,230,230,231,232,233,233,234,235,236,237,238,239,239,239,240,241,242,243,244,245,246,247,248,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,265,266,266,267,269,270,271,273,274,275,276,277,279,280,282,283,284,285,287,289,290,292,293,294,296,298,300,301,302,304,306,308,310,311,313,315,318,320,321,323,326,328,330,332,335,337,339,342,344,347,349,352,355,358,361,364,366,370,374,376,380,383,387,391,394,399,402,407,411,416,420,425,430,436,441,446,453,458,464,472,478,485,493,501,509,518,527,537,548,559,571,583,597,611,626,644,662,682,704,728,755,785,819,859,904,957,1021,1099,1198,1330,1515,1803,2330,3763, 0, DEC_CURVE,CRV_BUFFERFULL,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,94,94,94,95,95,95,95,95,96,96,97,97,98,98,99,99,100,100,101,101,102,102,103,104,104,105,106,107,108,109,110,111,112,113,113,115,117,119,121,122,124,127,130,132,135,139,142,146,149,153,158,162,167,171,176,180,186,193,202,209,216,223,232,243,254,266,279,292,306,320,335,337,351,367,380,396,414,437,464,493,520,549,583,611,644,675,711,755,785,819,859,904,957,1021,1099,1198,1330,1515,1803,2330,3763, 0, -2, /* motorcurve 6 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN,23999, 0, ACC_CURVE,CRV_PARKHOME ,5360,3362,2628,2229,1973,1791,1654,1547,1458,1384,1319,1264,1214,1170,1131,1096,1063,1034,1006,981,958,937,916,897,880,863,847,832,818,805,792,780,769,758,747,737,727,718,709,700,692, 0, ACC_CURVE,CRV_SMEARING ,23999, 0, DEC_CURVE,CRV_NORMALSCAN,23999, 0, DEC_CURVE,CRV_PARKHOME ,692,700,709,718,727,737,747,758,769,780,792,805,818,832,847,863,880,897,916,937,958,981,1006,1034,1063,1096,1131,1170,1214,1264,1319,1384,1458,1547,1654,1791,1973,2229,2628,3362,5360, 0, DEC_CURVE,CRV_SMEARING ,23999, 0, DEC_CURVE,CRV_BUFFERFULL,23999, 0, -2, /* motorcurve 7 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN,6667, 0, ACC_CURVE,CRV_PARKHOME ,5360,3362,2628,2229,1973,1791,1654,1547,1458,1384,1319,1264,1214,1170,1131,1096,1063,1034,1006,981,958,937,916,897,880,863,847,832,818,805,792,780,769,758,747,737,727,718,709,700,692, 0, ACC_CURVE,CRV_SMEARING ,6667, 0, DEC_CURVE,CRV_NORMALSCAN,6667, 0, DEC_CURVE,CRV_PARKHOME ,692,700,709,718,727,737,747,758,769,780,792,805,818,832,847,863,880,897,916,937,958,981,1006,1034,1063,1096,1131,1170,1214,1264,1319,1384,1458,1547,1654,1791,1973,2229,2628,3362,5360, 0, DEC_CURVE,CRV_SMEARING ,6667, 0, DEC_CURVE,CRV_BUFFERFULL,6667, 0, -2, /* motorcurve 8 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN,1046,1046,1046,1046,1046,1046,647,501,421,370,333,305,284,266,251,239,228,218,210,202,196,190,184,179,174,170,166,162,159,155,152,149,147,144,142,139,137,135,133,131,129,127,126,124,123,121,120,118,117,116,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,100,99,98,97,96,96,95,94,94,93,92,92,91,91,90,89,89,88,88,87,87,86,86,85,85,84,84,83,83,82,82,82,81,81,80,80,79,79,79,78,78,78,77,77,76,76,76,75,75,75,74,74,74,74,73,73,73,72,72,72,71,71,71,71,70,70,70,70,69,69,69,69,68,68,68,68,67,67,67,67,66,66,66,66,65,65,65,65,64,64,64,64,63,63,63,63,62,62,62,62,61,61,61,61,61,60,60,60,60,60,60,59,59,59,59,59,59,59,58,58,58,58,58,58,57,57,57,57,57,57,57,56,56,56,56,56,56,56,56,55,55,55,55,55,55,55,55,54,54,54,54,54,54,54,54,53,53,53,53,53,53,53,53,52,52,52,52,52,52,52,52,51,51,51,51,51,51,51,51,51,51,51,51,50,50,50,50,50,50,50,50,50,50,50,49,49,49,49,49,49,49,49,49,49,49,49,48,48,48,48,48,48,48,48,48,48,48,48,47,47,47,47,47,47,47,47,47,47,47,47,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,23,23, 0, ACC_CURVE,CRV_PARKHOME ,1045,678,604,554,332,332,304,283,265,250,238,227,217,209,201,195,189,183,178,173,169,165,161,158,154,151,148,146,143,141,138,136,134,132,130,128,126,125,123,122,120,119,117,116,115,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,99,98,97,96,95,95,94,93,93,92,91,91,90,90,89,88,88,87,87,86,86,85,85,84,84,83,83,82,82,81,81,81,80,80,79,79,78,78,78,77,77,77,76,76,75,75,75,74,74,74,73,73,73,73,72,72,72,71,71,71,70,70,70,70,69,69,69,69,68,68,68,68,67,67,67,67,66,66,66,66,65,65,65,65,65,64,64,64,64,64,63,63,63,63,63,62,62,62,62,62,61,61,61,61,61,61,60,60,60,60,60,60,59,59,59,59,59,59,58,58,58,58,58,58,58,57,57,57,57,57,57,57,56,56,56,56,56,56,56,55,55,55,55,55,55,55,55,54,54,54,54,54,54,54,54,53,53,53,53,53,53,53,53,53,52,52,52,52,52,52,52,52,52,51,51,51,51,51,51,51,51,51,51,50,50,50,50,50,50,50,50,50,50,50,49,49,49,49,49,49,49,49,49,49,49,48,48,48,48,48,48,48,48,48,48,48,48,47,47,47,47,47,47,47,47,47,47,47,47,47,46,46,46,46,46,46,46,46,46,46,46,46,46,46,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,39, 0, ACC_CURVE,CRV_SMEARING ,1045,678,604,554,332,332,304,283,265,250,238,227,217,209,201,195,189,183,178,173,169,165,161,158,154,151,148,146,143,141,138,136,134,132,130,128,126,125,123,122,120,119,117,116,115,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,99,98,97,96,95,95,94,93,93,92,91,91,90,90,89,88,88,87,87,86,86,85,85,84,84,83,83,82,82,81,81,81,80,80,79,79,78,78,78,77,77,77,76,76,75,75,75,74,74,74,73,73,73,73,72,72,72,71,71,71,70,70,70,70,69,69,69,69,68,68,68,68,67,67,67,67,66,66,66,66,65,65,65,65,65,64,64,64,64,64,63,63,63,63,63,62,62,62,62,62,61,61,61,61,61,61,60,60,60,60,60,60,59,59,59,59,59,59,58,58,58,58,58,58,58,57,57,57,57,57,57,57,56,56,56,56,56,56,56,55,55,55,55,55,55,55,55,54,54,54,54,54,54,54,54,53,53,53,53,53,53,53,53,53,52,52,52,52,52,52,52,52,52,51,51,51,51,51,51,51,51,51,51,50,50,50,50,50,50,50,50,50,50,50,49,49,49,49,49,49,49,49,49,49,49,48,48,48,48,48,48,48,48,48,48,48,48,47,47,47,47,47,47,47,47,47,47,47,47,47,46,46,46,46,46,46,46,46,46,46,46,46,46,46,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,39, 0, DEC_CURVE,CRV_NORMALSCAN,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,30,30,30,30,31,31,31,31,32,32,32,32,33,33,34,34,35,35,36,37,38,38,39,40,41,42,43,45,46,47,48,50,51,53,54,57,59,61,63,65,68,71,75,78,82,86,90,94,94,98,103,106,111,116,122,130,138,145,153,163,171,180,188,198,211,219,228,239,252,267,284,306,334,370,422,502,648,1045, 0, DEC_CURVE,CRV_PARKHOME ,39,40,41,42,43,45,46,47,48,50,51,53,54,57,59,61,63,65,68,71,75,78,82,86,90,94,94,98,103,106,111,116,122,130,138,145,153,163,171,180,188,198,211,219,228,239,252,267,284,306,334,370,422,502,648,1045, 0, DEC_CURVE,CRV_SMEARING ,39,39,39,39,39,39,39,39,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,48,48,48,48,48,48,48,48,48,48,48,48,48,48,49,49,49,49,49,49,49,49,49,49,49,49,49,50,50,50,50,50,50,50,50,50,50,50,50,51,51,51,51,51,51,51,51,51,51,51,52,52,52,52,52,52,52,52,52,52,53,53,53,53,53,53,53,53,53,53,54,54,54,54,54,54,54,54,54,55,55,55,55,55,55,55,55,55,56,56,56,56,56,56,56,56,57,57,57,57,57,57,57,57,58,58,58,58,58,58,58,59,59,59,59,59,59,59,60,60,60,60,60,60,60,61,61,61,61,61,61,62,62,62,62,62,62,63,63,63,63,63,64,64,64,64,64,65,65,65,65,65,66,66,66,66,66,67,67,67,67,67,68,68,68,68,69,69,69,69,70,70,70,70,71,71,71,71,72,72,72,73,73,73,73,74,74,74,75,75,75,76,76,76,77,77,77,78,78,78,79,79,79,80,80,81,81,81,82,82,83,83,84,84,84,85,85,86,86,87,87,88,88,89,90,90,91,91,92,93,93,94,94,95,96,96,97,98,99,99,100,101,102,103,104,105,105,106,107,108,109,110,112,113,114,115,116,118,119,120,122,123,125,127,128,130,132,134,136,138,140,142,145,147,150,153,156,159,163,167,171,175,180,185,190,196,203,211,219,228,239,252,267,284,306,334,370,422,502,648,1045, 0, DEC_CURVE,CRV_BUFFERFULL,1045,648,502,422,370,334,306,284,267,252,239,228,219,211,198,188,180,171,163,153,145,138,130,122,116,111,106,103,98,94,94,90,86,82,78,75,71,68,65,63,61,59,57,54,53,51,50,48,47,46,45,43,42,41,40,39,38,38,37,36,35,35,34,34,33,33,32,32,32,32,31,31,31,31,30,30,30,30,29,29,29,29,29,29,29,28,28,28,28,28,28,28,28,27,27,27,27,27,27,27,27,27,27,27,27,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, 0, -2, /* motorcurve 9 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN,3136,3136,3136,3136,3136,3136,2036,1815,1664,998,998,915,850,797,752,715,682,654,628,606,586,568,551,536,522,509,497,485,475,465,456,447,439,431,424,417,410,404,398,392,387,381,376,371,367,362,358,354,350,346,342,338,335,331,328,325,322,319,316,313,310,308,305,302,300,298,295,293,291,288,286,284,282,280,278,276,274,272,271,269,267,265,264,262,261,259,257,256,254,253,252,250,249,247,246,245,244,242,241,240,239,237,236,235,234,233,232,231,230,228,227,226,225,224,223,222,221,221,220,219,218,217,216,215,214,213,213,212,211,210,209,209,208,207,206,206,205,204,203,203,202,201,200,200,199,198,198,197,196,196,195,195,194,193,193,192,192,191,190,190,189,189,188,187,187,186,186,185,185,184,184,183,183,182,182,181,181,180,180,179,179,178,178,177,177,176,176,175,175,175,174,174,173,173,172,172,172,171,171,170,170,170,169,169,168,168,168,167,167,166, 0, ACC_CURVE,CRV_PARKHOME ,3136,3136,3136,3136,3136,3136,2036,1815,1664,998,998,915,850,797,752,715,682,654,628,606,586,568,551,536,522,509,497,485,475,465,456,447,439,431,424,417,410,404,398,392,387,381,376,371,367,362,358,354,350,346,342,338,335,331,328,325,322,319,316,313,310,308,305,302,300,298,295,293,291,288,286,284,282,280,278,276,274,272,271,269,267,265,264,262,261,259,257,256,254,253,252,250,249,247,246,245,244,242,241,240,239,237,236,235,234,233,232,231,230,228,227,226,225,224,223,222,221,221,220,219,218,217,216,215,214,213,213,212,211,210,209,209,208,207,206,206,205,204,203,203,202,201,200,200,199,198,198,197,196,196,195,195,194,193,193,192,192,191,190,190,189,189,188,187,187,186,186,185,185,184,184,183,183,182,182,181,181,180,180,179,179,178,178,177,177,176,176,175,175,175,174,174,173,173,172,172,172,171,171,170,170,170,169,169,168,168,168,167,167,166,166,166, 0, ACC_CURVE,CRV_SMEARING ,3136,3136,3136,3136,3136,3136,2036,1815,1664,998,998,915,850,797,752,715,682,654,628,606,586,568,551,536,522,509,497,485,475,465,456,455, 0, DEC_CURVE,CRV_NORMALSCAN,455,456,465,475,485,497,509,522,536,551,568,586,606,628,654,682,715,752,797,850,915,998,998,1664,1815,2036,3136,3136,3136,3136,3136,3136, 0, DEC_CURVE,CRV_PARKHOME ,455,456,465,475,485,497,509,522,536,551,568,586,606,628,654,682,715,752,797,850,915,998,998,1664,1815,2036,3136,3136,3136,3136,3136,3136, 0, DEC_CURVE,CRV_SMEARING ,455,456,465,475,485,497,509,522,536,551,568,586,606,628,654,682,715,752,797,850,915,998,998,1664,1815,2036,3136,3136,3136,3136,3136,3136, 0, DEC_CURVE,CRV_BUFFERFULL,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,127,127,128,128,129,129,130,131,132,133,133,134,135,136,137,138,139,140,141,143,144,145,146,147,149,150,151,153,154,156,157,159,161,162,164,166,168,170,172,174,176,179,181,184,186,189,192,195,198,202,206,209,213,218,222,227,233,239,245,252,259,267,276,282,286,291,298,305,310,319,325,331,342,354,362,376,387,404,417,431,456,475,509,551,586,654,715,850,998,1664,3136,3136, 0, -2, /* motorcurve 10 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN,3136,3136,3136,3136,3136,3136,2036,1815,1664,998,998,915,850,797,752,715,682,654,628,606,586,568,551,536,522,509,497,485,475,465,456,447,439,431,424,417,410,404,398,392,387,381,376, 0, ACC_CURVE,CRV_PARKHOME ,3136,3136,3136,3136,3136,3136,2036,1815,1664,998,998,915,850,797,752,715,682,654,628,606,586,568,551,536,522,509,497,485,475,465,456,447,439,431,424,417,410,404,398,392,387,381,376,371,367,362,358,354,350,346,342,338,335,331,328,325,322,319,316,313,310,308,305,302,300,298,295,293,291,288,286,284,282,280,278,276,274,272,271,269,267,265,264,262,261,259,257,256,254,253,252,250,249,247,246,245,244,242,241,240,239,237,236,235,234,233,232,231,230,228,227,226,225,224,223,222,221,221,220,219,218,217,216,215,214,213,213,212,211,210,209,209,208,207,206,206,205,204,203,203,202,201,200,200,199,198,198,197,196,196,195,195,194,193,193,192,192,191,190,190,189,189,188,187,187,186,186,185,185,184,184,183,183,182,182,181,181,180,180,179,179,178,178,177,177,176,176,175,175,175,174,174,173,173,172,172,172,171,171,170,170,170,169,169,168,168,168,167,167,166, 0, ACC_CURVE,CRV_SMEARING ,3136,3136,3136,3136,3136,3136,2036,1815,1664,998,998,915,850,797,752,715,682,654,628,606,586,568,551,536,522,509,497,485,475,465,456,455, 0, DEC_CURVE,CRV_NORMALSCAN,381,392,404,417,431,447,465,485,508,535,566,604,650,709,787,897,1067,2227,3136, 0, DEC_CURVE,CRV_PARKHOME ,455,456,465,475,485,497,509,522,536,551,568,586,606,628,654,682,715,752,797,850,915,998,998,1664,1815,2036,3136, 0, DEC_CURVE,CRV_SMEARING ,455,456,465,475,485,497,509,522,536,551,568,586,606,628,654,682,715,752,797,850,915,998,998,1664,1815,2036,3136, 0, DEC_CURVE,CRV_BUFFERFULL,381,392,404,417,431,447,465,485,508,535,566,604,650,709,787,897,1067,2227,3136, 0, -2, /* motorcurve 11 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN,3136,3136,3136,3136,3136,3136,2036,1815,1664,998,998,915,850,797,752,715,682,654,628,606,586,568,551,536,522,509,497,485,475,465,456,447,439,431,424,417,410,404,398,392,387,381,376,371,367,362,358,354,350,346,342,338,335,331,328,325,322,319,316,313,310,308,305,302,300,298,295,293,291,288,286,284,282,280,278,276,274,272,271,269,267,265,264,262,261,259,257,256,254,253,252,250,249,247,246,245,244,242,241,240,239,237,236,235,234,233,232,231,230,228,227,226,225,224,223,222,221,221,220,219,218,217,216,215,214,213,213,212,211,210,209,209,208,207,206,206,205,204,203,203,202,201,200,200,199,198,198,197,196,196,195,195,194,193,193,192,192,191,190,190,189,189,188,187,187,186,186,185,185,184,184,183,183,182,182,181,181,180,180,179,179,178,178,177,177,176,176,175,175,175,174,174,173,173,172,172,172,171,171,170,170,170,169,169,168,168,168,167,167,166, 0, ACC_CURVE,CRV_PARKHOME ,3136,3136,3136,3136,3136,3136,2036,1815,1664,998,998,915,850,797,752,715,682,654,628,606,586,568,551,536,522,509,497,485,475,465,456,447,439,431,424,417,410,404,398,392,387,381,376,371,367,362,358,354,350,346,342,338,335,331,328,325,322,319,316,313,310,308,305,302,300,298,295,293,291,288,286,284,282,280,278,276,274,272,271,269,267,265,264,262,261,259,257,256,254,253,252,250,249,247,246,245,244,242,241,240,239,237,236,235,234,233,232,231,230,228,227,226,225,224,223,222,221,221,220,219,218,217,216,215,214,213,213,212,211,210,209,209,208,207,206,206,205,204,203,203,202,201,200,200,199,198,198,197,196,196,195,195,194,193,193,192,192,191,190,190,189,189,188,187,187,186,186,185,185,184,184,183,183,182,182,181,181,180,180,179,179,178,178,177,177,176,176,175,175,175,174,174,173,173,172,172,172,171,171,170,170,170,169,169,168,168,168,167,167,166, 0, ACC_CURVE,CRV_SMEARING ,3136,3136,3136,3136,3136,3136,2036,1815,1664,998,998,915,850,797,752,715,682,654,628,606,586,568,551,536,522,509,497,485,475,465,456,447,439,431,424,417,410,404,398,392,387,381,376,371,367,362,358,354,350,346,342,338,335,331,328,325,322,319,316,313,310,308,305,302,300,298,295,293,291,288,286,284,282,280,278,276,274,272,271,269,267,265,264,262,261,259,257,256,254,253,252,250,249,247,246,245,244,242,241,240,239,237,236,235,234,233,232,231,230,228,227,226,225,224,223,222,221,221,220,219,218,217,216,215,214,213,213,212,211,210,209,209,208,207,206,206,205,204,203,203,202,201,200,200,199,198,198,197,196,196,195,195,194,193,193,192,192,191,190,190,189,189,188,187,187,186,186,185,185,184,184,183,183,182,182,181,181,180,180,179,179,178,178,177,177,176,176,175,175,175,174,174,173,173,172,172,172,171,171,170,170,170,169,169,168,168,168,167,167,166, 0, DEC_CURVE,CRV_NORMALSCAN,167,168,169,170,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,190,191,192,193,194,196,197,198,200,201,203,204,206,207,209,211,212,214,216,218,219,221,223,225,227,230,232,234,236,239,241,244,247,249,252,255,258,261,265,268,272,275,279,283,288,292,297,302,307,313,318,325,331,338,346,353,362,371,381,392,404,417,431,447,465,485,508,535,566,604,650,709,787,897,1067,2227,3136, 0, DEC_CURVE,CRV_PARKHOME ,455,456,465,475,485,497,509,522,536,551,568,586,606,628,654,682,715,752,797,850,915,998,998,1664,1815,2036,3136, 0, DEC_CURVE,CRV_SMEARING ,455,456,465,475,485,497,509,522,536,551,568,586,606,628,654,682,715,752,797,850,915,998,998,1664,1815,2036,3136, 0, DEC_CURVE,CRV_BUFFERFULL,167,168,169,170,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,190,191,192,193,194,196,197,198,200,201,203,204,206,207,209,211,212,214,216,218,219,221,223,225,227,230,232,234,236,239,241,244,247,249,252,255,258,261,265,268,272,275,279,283,288,292,297,302,307,313,318,325,331,338,346,353,362,371,381,392,404,417,431,447,465,485,508,535,566,604,650,709,787,897,1067,2227,3136, 0, -2, /* motorcurve 12 */ 1, 1, 1, 0, /* mri, msi, skiplinecount, motorbackstep */ ACC_CURVE,CRV_NORMALSCAN,3136,3136,3136,3136,3136,3136,2036,1815,1664,998,998,915,850,797,752,715,682,654,628,606,586,568,551,536,522,509,497,485,475,465,456,447,439,431,424,417,410,404,398,392,387,381,376,371,367,362,358,354,350,346,342,338,335,331,328,325,322,319,316,313,310,308,305,302,300,298,295,293,291,288,286,284,282,280,278,276,274,272,271,269,267,265,264,262,261,259,257,256,254,253,252,250,249,247,246,245,244,242,241,240,239,237,236,235,234,233,232,231,230,228,227,226,225,224,223,222,221,221,220,219,218,217,216,215,214,213,213,212,211,210,209,209,208,207,206,206,205,204,203,203,202,201,200,200,199,198,198,197,196,196,195,195,194,193,193,192,192,191,190,190,189,189,188,187,187,186,186,185,185,184,184,183,183,182,182,181,181,180,180,179,179,178,178,177,177,176,176,175,175,175,174,174,173,173,172,172,172,171,171,170,170,170,169,169,168,168,168,167,167,166,166,166,165,165,165,164,164,163,163,163,162,162,162,161,161,161,160,160,160,159,159,159,158,158,158,157,157,157,156,156,156,156,155,155,155,154,154,154,153,153,153,153,152,152,152,151,151,151,151,150,150,150,150,149,149,149,148,148,148,148,147,147,147,147,146,146,146,146,145,145,145,145,144,144,144,144,144,143,143,143,143,142,142,142,142,141,141,141,141,141,140,140,140,140,140,139,139,139,139,138,138,138,138,138,137,137,137,137,137,136,136,136,136,136,135,135,135,135,135,135,134,134,134,134,134,133,133,133,133,133,133,132,132,132,132,132,131,131,131,131,131,131,130,130,130,130,130,130,129,129,129,129,129,129,128,128,128,128,128,128,127,127,127,127,127,127,127,126,126,126,126,126,126,125, 0, ACC_CURVE,CRV_PARKHOME ,3136,3136,3136,3136,3136,3136,2036,1815,1664,998,998,915,850,797,752,715,682,654,628,606,586,568,551,536,522,509,497,485,475,465,456,447,439,431,424,417,410,404,398,392,387,381,376,371,367,362,358,354,350,346,342,338,335,331,328,325,322,319,316,313,310,308,305,302,300,298,295,293,291,288,286,284,282,280,278,276,274,272,271,269,267,265,264,262,261,259,257,256,254,253,252,250,249,247,246,245,244,242,241,240,239,237,236,235,234,233,232,231,230,228,227,226,225,224,223,222,221,221,220,219,218,217,216,215,214,213,213,212,211,210,209,209,208,207,206,206,205,204,203,203,202,201,200,200,199,198,198,197,196,196,195,195,194,193,193,192,192,191,190,190,189,189,188,187,187,186,186,185,185,184,184,183,183,182,182,181,181,180,180,179,179,178,178,177,177,176,176,175,175,175,174,174,173,173,172,172,172,171,171,170,170,170,169,169,168,168,168,167,167,166, 0, ACC_CURVE,CRV_SMEARING ,3136,3136,3136,3136,3136,3136,2036,1815,1664,998,998,915,850,797,752,715,682,654,628,606,586,568,551,536,522,509,497,485,475,465,456,455, 0, DEC_CURVE,CRV_NORMALSCAN,110,110,110,110,110,110,110,110,111,111,111,111,111,112,112,112,112,112,113,113,113,113,114,114,114,114,115,115,115,115,115,116,116,116,116,117,117,117,117,118,118,118,118,119,119,119,120,120,120,120,121,121,121,121,122,122,122,123,123,123,124,124,124,124,125,125,125,126,126,126,127,127,127,128,128,128,129,129,129,130,130,130,131,131,132,132,132,133,133,133,134,134,135,135,135,136,136,137,137,138,138,138,139,139,140,140,141,141,142,142,142,143,143,144,144,145,145,146,146,147,148,148,149,149,150,150,151,151,152,153,153,154,154,155,156,156,157,158,158,159,160,160,161,162,163,163,164,165,166,166,167,168,169,170,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,190,191,192,193,194,196,197,198,200,201,203,204,206,207,209,211,212,214,216,218,219,221,223,225,227,230,232,234,236,239,241,244,247,249,252,255,258,261,265,268,272,275,279,283,288,292,297,302,307,313,318,325,331,338,346,353,362,371,381,392,404,417,431,447,465,485,508,535,566,604,650,709,787,897,1067,2227,3136, 0, DEC_CURVE,CRV_PARKHOME ,455,456,465,475,485,497,509,522,536,551,568,586,606,628,654,682,715,752,797,850,915,998,998,1664,1815,2036,3136, 0, DEC_CURVE,CRV_SMEARING ,455,456,465,475,485,497,509,522,536,551,568,586,606,628,654,682,715,752,797,850,915,998,998,1664,1815,2036,3136, 0, DEC_CURVE,CRV_BUFFERFULL,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,127,127,128,128,129,129,130,131,132,133,133,134,135,136,137,138,139,140,141,143,144,145,146,147,149,150,151,153,154,156,157,159,161,162,164,166,168,170,172,174,176,179,181,184,186,189,192,195,198,202,206,209,213,218,222,227,233,239,245,252,259,267,276,282,286,291,298,305,310,319,325,331,342,354,362,376,387,404,417,431,456,475,509,551,586,654,715,850,998,1664,3136, 0, -1 }; rst = (SANE_Int *)malloc(sizeof(steps)); if (rst != NULL) memcpy(rst, &steps, sizeof(steps)); return rst; } static SANE_Int *cfg_motorcurve_get() { /* returns motor setting buffer for a device */ SANE_Int *rst = NULL; switch(RTS_Debug->dev_model) { case BQ5550: rst = bq5550_motor(); break; case HP3800: case HPG2710: rst = hp3800_motor(); break; case HP4370: case HPG3010: case HPG3110: rst = hp4370_motor(); break; default: rst = hp3970_motor(); break; } return rst; } /* DEPRECATED functions */ static int ua4900_calibreflective(int option, int defvalue) { int rst = defvalue; switch(option) { case WSTRIPXPOS: rst = 0; break; case WSTRIPYPOS: rst = 0; break; case BSTRIPXPOS: rst = 0; break; case BSTRIPYPOS: rst = 0; break; case BREFR: rst = 10; break; case BREFG: rst = 10; break; case BREFB: rst = 10; break; case REFBITDEPTH: rst = 8; break; case OFFSETHEIGHT: rst = 10; break; case OFFSETNSIGMA: rst = 2; break; case OFFSETTARGETMAX: rst = 50; break; case OFFSETTARGETMIN: rst = 2; break; case OFFSETAVGTARGETR: rst = 10; break; case OFFSETAVGTARGETG: rst = 10; break; case OFFSETAVGTARGETB: rst = 10; break; case ADCOFFEVENODD: rst = 1; break; case CALIBOFFSET1ON: rst = 2; break; case ADCOFFQUICKWAY: rst = 1; break; case ADCOFFPREDICTSTART: rst = 300; break; case ADCOFFPREDICTEND: rst = 500; break; case OFFSETTUNESTEP1: rst = 5; break; case OFFSETBOUNDARYRATIO1: rst = 100; break; case OFFSETAVGRATIO1: rst = 100; break; case OFFSETEVEN1R: rst = 310; break; case OFFSETODD1R: rst = 310; break; case OFFSETEVEN1G: rst = 313; break; case OFFSETODD1G: rst = 313; break; case OFFSETEVEN1B: rst = 319; break; case OFFSETODD1B: rst = 319; break; case ADCOFFPREDICTR: rst = 321; break; case ADCOFFPREDICTG: rst = 321; break; case ADCOFFPREDICTB: rst = 321; break; case ADCOFFEVEN1R_1ST: rst = 344; break; case ADCOFFODD1R_1ST: rst = 344; break; case ADCOFFEVEN1G_1ST: rst = 328; break; case ADCOFFODD1G_1ST: rst = 328; break; case ADCOFFEVEN1B_1ST: rst = 341; break; case ADCOFFODD1B_1ST: rst = 341; break; case PEAKR: rst = 122; break; case PEAKG: rst = 122; break; case PEAKB: rst = 122; break; case MINR: rst = 50; break; case MING: rst = 50; break; case MINB: rst = 50; break; case CALIBOFFSET2ON: rst = 0; break; case OFFSETTUNESTEP2: rst = 1; break; case OFFSETBOUNDARYRATIO2: rst = 100; break; case OFFSETAVGRATIO2: rst = 100; break; case OFFSETEVEN2R: rst = 0; break; case OFFSETODD2R: rst = 0; break; case OFFSETEVEN2G: rst = 0; break; case OFFSETODD2G: rst = 0; break; case OFFSETEVEN2B: rst = 0; break; case OFFSETODD2B: rst = 0; break; case GAINHEIGHT: rst = 10; break; case GAINTARGETFACTOR: rst = 90; break; case CALIBPAGON: rst = 0; break; case PAGR: rst = 3; break; case PAGG: rst = 3; break; case PAGB: rst = 3; break; case CALIBGAIN1ON: rst = 1; break; case GAIN1R: rst = 8; break; case GAIN1G: rst = 8; break; case GAIN1B: rst = 8; break; case CALIBGAIN2ON: rst = 0; break; case GAIN2R: rst = 8; break; case GAIN2G: rst = 8; break; case GAIN2B: rst = 8; break; case TOTSHADING: rst = 0; break; case BSHADINGON: rst = -2; break; case BSHADINGHEIGHT: rst = 10; break; case BSHADINGPREDIFFR: rst = 2; break; case BSHADINGPREDIFFG: rst = 1; break; case BSHADINGPREDIFFB: rst = 2; break; case BSHADINGDEFCUTOFF: rst = 0; break; case WSHADINGON: rst = 3; break; case WSHADINGHEIGHT: rst = 15; break; case WSHADINGPREDIFFR: rst = -1; break; case WSHADINGPREDIFFG: rst = -1; break; case WSHADINGPREDIFFB: rst = -1; break; } return rst; } static int hp3800_calibreflective(int option, int defvalue) { int rst = defvalue; switch(option) { case WSTRIPXPOS: rst = 0; break; case WSTRIPYPOS: rst = 0; break; case BSTRIPXPOS: rst = 0; break; case BSTRIPYPOS: rst = 0; break; case BREFR: rst = 10; break; case BREFG: rst = 10; break; case BREFB: rst = 10; break; case REFBITDEPTH: rst = 8; break; case OFFSETHEIGHT: rst = 10; break; case OFFSETNSIGMA: rst = 2; break; case OFFSETTARGETMAX: rst = 50; break; case OFFSETTARGETMIN: rst = 2; break; case OFFSETAVGTARGETR: rst = 10; break; case OFFSETAVGTARGETG: rst = 10; break; case OFFSETAVGTARGETB: rst = 10; break; case ADCOFFEVENODD: rst = 1; break; case CALIBOFFSET1ON: rst = 2; break; case ADCOFFQUICKWAY: rst = 1; break; case ADCOFFPREDICTSTART: rst = 300; break; case ADCOFFPREDICTEND: rst = 500; break; case OFFSETTUNESTEP1: rst = 5; break; case OFFSETBOUNDARYRATIO1: rst = 100; break; case OFFSETAVGRATIO1: rst = 100; break; case OFFSETEVEN1R: rst = 310; break; case OFFSETODD1R: rst = 310; break; case OFFSETEVEN1G: rst = 317; break; case OFFSETODD1G: rst = 317; break; case OFFSETEVEN1B: rst = 293; break; case OFFSETODD1B: rst = 293; break; case ADCOFFPREDICTR: rst = 500; break; case ADCOFFPREDICTG: rst = 500; break; case ADCOFFPREDICTB: rst = 500; break; case ADCOFFEVEN1R_1ST: rst = 128; break; case ADCOFFODD1R_1ST: rst = 128; break; case ADCOFFEVEN1G_1ST: rst = 128; break; case ADCOFFODD1G_1ST: rst = 128; break; case ADCOFFEVEN1B_1ST: rst = 128; break; case ADCOFFODD1B_1ST: rst = 128; break; case PEAKR: rst = 104; break; case PEAKG: rst = 111; break; case PEAKB: rst = 105; break; case MINR: rst = 50; break; case MING: rst = 56; break; case MINB: rst = 57; break; case CALIBOFFSET2ON: rst = 0; break; case OFFSETTUNESTEP2: rst = 1; break; case OFFSETBOUNDARYRATIO2: rst = 100; break; case OFFSETAVGRATIO2: rst = 100; break; case OFFSETEVEN2R: rst = 0; break; case OFFSETODD2R: rst = 0; break; case OFFSETEVEN2G: rst = 0; break; case OFFSETODD2G: rst = 0; break; case OFFSETEVEN2B: rst = 0; break; case OFFSETODD2B: rst = 0; break; case GAINHEIGHT: rst = 10; break; case GAINTARGETFACTOR: rst = 80; break; case CALIBPAGON: rst = 0; break; case PAGR: rst = 3; break; case PAGG: rst = 3; break; case PAGB: rst = 3; break; case CALIBGAIN1ON: rst = 1; break; case GAIN1R: rst = 23; break; case GAIN1G: rst = 19; break; case GAIN1B: rst = 0; break; case CALIBGAIN2ON: rst = 0; break; case GAIN2R: rst = 4; break; case GAIN2G: rst = 4; break; case GAIN2B: rst = 4; break; case TOTSHADING: rst = 0; break; case BSHADINGON: rst = -3; break; case BSHADINGHEIGHT: rst = 20; break; case BSHADINGPREDIFFR: rst = 2; break; case BSHADINGPREDIFFG: rst = 2; break; case BSHADINGPREDIFFB: rst = 2; break; case BSHADINGDEFCUTOFF: rst = 0; break; case WSHADINGON: rst = 3; break; case WSHADINGHEIGHT: rst = 24; break; case WSHADINGPREDIFFR: rst = -1; break; case WSHADINGPREDIFFG: rst = -1; break; case WSHADINGPREDIFFB: rst = -1; break; } return rst; } static int hp3970_calibreflective(int option, int defvalue) { int rst = defvalue; switch(option) { case WSTRIPXPOS: rst = 0; break; case WSTRIPYPOS: rst = 0; break; case BSTRIPXPOS: rst = 0; break; case BSTRIPYPOS: rst = 0; break; case BREFR: rst = 10; break; case BREFG: rst = 10; break; case BREFB: rst = 10; break; case REFBITDEPTH: rst = 8; break; case OFFSETHEIGHT: rst = 10; break; case OFFSETNSIGMA: rst = 2; break; case OFFSETTARGETMAX: rst = 50; break; case OFFSETTARGETMIN: rst = 2; break; case OFFSETAVGTARGETR: rst = 10; break; case OFFSETAVGTARGETG: rst = 10; break; case OFFSETAVGTARGETB: rst = 10; break; case ADCOFFEVENODD: rst = 1; break; case CALIBOFFSET1ON: rst = 2; break; case ADCOFFQUICKWAY: rst = 1; break; case ADCOFFPREDICTSTART: rst = 300; break; case ADCOFFPREDICTEND: rst = 500; break; case OFFSETTUNESTEP1: rst = 5; break; case OFFSETBOUNDARYRATIO1: rst = 100; break; case OFFSETAVGRATIO1: rst = 100; break; case OFFSETEVEN1R: rst = 327; break; case OFFSETODD1R: rst = 327; break; case OFFSETEVEN1G: rst = 315; break; case OFFSETODD1G: rst = 315; break; case OFFSETEVEN1B: rst = 322; break; case OFFSETODD1B: rst = 322; break; case ADCOFFPREDICTR: rst = 322; break; case ADCOFFPREDICTG: rst = 310; break; case ADCOFFPREDICTB: rst = 322; break; case ADCOFFEVEN1R_1ST: rst = 344; break; case ADCOFFODD1R_1ST: rst = 344; break; case ADCOFFEVEN1G_1ST: rst = 328; break; case ADCOFFODD1G_1ST: rst = 328; break; case ADCOFFEVEN1B_1ST: rst = 341; break; case ADCOFFODD1B_1ST: rst = 341; break; case PEAKR: rst = 82; break; case PEAKG: rst = 117; break; case PEAKB: rst = 116; break; case MINR: rst = 37; break; case MING: rst = 51; break; case MINB: rst = 53; break; case CALIBOFFSET2ON: rst = 0; break; case OFFSETTUNESTEP2: rst = 1; break; case OFFSETBOUNDARYRATIO2: rst = 100; break; case OFFSETAVGRATIO2: rst = 100; break; case OFFSETEVEN2R: rst = 0; break; case OFFSETODD2R: rst = 0; break; case OFFSETEVEN2G: rst = 0; break; case OFFSETODD2G: rst = 0; break; case OFFSETEVEN2B: rst = 0; break; case OFFSETODD2B: rst = 0; break; case GAINHEIGHT: rst = 10; break; case GAINTARGETFACTOR: rst = 90; break; case CALIBPAGON: rst = 0; break; case PAGR: rst = 3; break; case PAGG: rst = 3; break; case PAGB: rst = 3; break; case CALIBGAIN1ON: rst = 1; break; case GAIN1R: rst = 28; break; case GAIN1G: rst = 22; break; case GAIN1B: rst = 21; break; case CALIBGAIN2ON: rst = 0; break; case GAIN2R: rst = 4; break; case GAIN2G: rst = 4; break; case GAIN2B: rst = 4; break; case TOTSHADING: rst = 0; break; case BSHADINGON: rst = -2; break; case BSHADINGHEIGHT: rst = 10; break; case BSHADINGPREDIFFR: rst = 2; break; case BSHADINGPREDIFFG: rst = 2; break; case BSHADINGPREDIFFB: rst = 2; break; case BSHADINGDEFCUTOFF: rst = 0; break; case WSHADINGON: rst = 3; break; case WSHADINGHEIGHT: rst = 24; break; case WSHADINGPREDIFFR: rst = -1; break; case WSHADINGPREDIFFG: rst = -1; break; case WSHADINGPREDIFFB: rst = -1; break; } return rst; } static int hp4370_calibreflective(int option, int defvalue) { int rst = defvalue; switch(option) { case WSTRIPXPOS: rst = 0; break; case WSTRIPYPOS: rst = 0; break; case BSTRIPXPOS: rst = 0; break; case BSTRIPYPOS: rst = 0; break; case BREFR: rst = 10; break; case BREFG: rst = 10; break; case BREFB: rst = 10; break; case REFBITDEPTH: rst = 8; break; case OFFSETHEIGHT: rst = 10; break; case OFFSETNSIGMA: rst = 2; break; case OFFSETTARGETMAX: rst = 50; break; case OFFSETTARGETMIN: rst = 2; break; case OFFSETAVGTARGETR: rst = 10; break; case OFFSETAVGTARGETG: rst = 10; break; case OFFSETAVGTARGETB: rst = 10; break; case ADCOFFEVENODD: rst = 1; break; case CALIBOFFSET1ON: rst = 2; break; case ADCOFFQUICKWAY: rst = 1; break; case ADCOFFPREDICTSTART: rst = 300; break; case ADCOFFPREDICTEND: rst = 500; break; case OFFSETTUNESTEP1: rst = 5; break; case OFFSETBOUNDARYRATIO1: rst = 100; break; case OFFSETAVGRATIO1: rst = 100; break; case OFFSETEVEN1R: rst = 305; break; case OFFSETODD1R: rst = 3305; break; case OFFSETEVEN1G: rst = 313; break; case OFFSETODD1G: rst = 313; break; case OFFSETEVEN1B: rst = 317; break; case OFFSETODD1B: rst = 317; break; case ADCOFFPREDICTR: rst = 500; break; case ADCOFFPREDICTG: rst = 500; break; case ADCOFFPREDICTB: rst = 500; break; case ADCOFFEVEN1R_1ST: rst = 344; break; case ADCOFFODD1R_1ST: rst = 344; break; case ADCOFFEVEN1G_1ST: rst = 328; break; case ADCOFFODD1G_1ST: rst = 328; break; case ADCOFFEVEN1B_1ST: rst = 341; break; case ADCOFFODD1B_1ST: rst = 341; break; case PEAKR: rst = 159; break; case PEAKG: rst = 191; break; case PEAKB: rst = 191; break; case MINR: rst = 146; break; case MING: rst = 180; break; case MINB: rst = 179; break; case CALIBOFFSET2ON: rst = 0; break; case OFFSETTUNESTEP2: rst = 1; break; case OFFSETBOUNDARYRATIO2: rst = 100; break; case OFFSETAVGRATIO2: rst = 100; break; case OFFSETEVEN2R: rst = 0; break; case OFFSETODD2R: rst = 0; break; case OFFSETEVEN2G: rst = 0; break; case OFFSETODD2G: rst = 0; break; case OFFSETEVEN2B: rst = 0; break; case OFFSETODD2B: rst = 0; break; case GAINHEIGHT: rst = 10; break; case GAINTARGETFACTOR: rst = 80; break; case CALIBPAGON: rst = 0; break; case HIPAGR: rst = 3; break; case HIPAGG: rst = 0; break; case HIPAGB: rst = 0; break; case PAGR: rst = 3; break; case PAGG: rst = 3; break; case PAGB: rst = 3; break; case LOPAGR: rst = 3; break; case LOPAGG: rst = 3; break; case LOPAGB: rst = 3; break; case CALIBGAIN1ON: rst = 1; break; case GAIN1R: rst = 10; break; case GAIN1G: rst = 2; break; case GAIN1B: rst = 1; break; case CALIBGAIN2ON: rst = 0; break; case GAIN2R: rst = 4; break; case GAIN2G: rst = 4; break; case GAIN2B: rst = 4; break; case TOTSHADING: rst = 0; break; case BSHADINGON: rst = -2; break; case BSHADINGHEIGHT: rst = 10; break; case BSHADINGPREDIFFR: rst = 2; break; case BSHADINGPREDIFFG: rst = 2; break; case BSHADINGPREDIFFB: rst = 2; break; case BSHADINGDEFCUTOFF: rst = 0; break; case WSHADINGON: rst = 3; break; case WSHADINGHEIGHT: rst = 24; break; case WSHADINGPREDIFFR: rst = -1; break; case WSHADINGPREDIFFG: rst = -1; break; case WSHADINGPREDIFFB: rst = -1; break; } return rst; } static int fc_calibreflective(int option, int defvalue) { int rst; switch(RTS_Debug->dev_model) { case UA4900: rst = ua4900_calibreflective(option, defvalue); break; case HPG2710: case HP3800: rst = hp3800_calibreflective(option, defvalue); break; case HPG3010: case HPG3110: case HP4370: rst = hp4370_calibreflective(option, defvalue); break; default : rst = hp3970_calibreflective(option, defvalue); break; } return rst; } static int ua4900_calibtransparent(int option, int defvalue) { int rst = defvalue; switch(option) { case WSTRIPXPOS: rst = 0; break; case WSTRIPYPOS: rst = 12100; break; case BSTRIPXPOS: rst = 0; break; case BSTRIPYPOS: rst = 0; break; case BREFR: rst = 2; break; case BREFG: rst = 2; break; case BREFB: rst = 2; break; case REFBITDEPTH: rst = 8; break; case OFFSETHEIGHT: rst = 10; break; case OFFSETNSIGMA: rst = 2; break; case OFFSETTARGETMAX: rst = 50; break; case OFFSETTARGETMIN: rst = 2; break; case OFFSETAVGTARGETR: rst = 13; break; case OFFSETAVGTARGETG: rst = 13; break; case OFFSETAVGTARGETB: rst = 13; break; case ADCOFFEVENODD: rst = 1; break; case CALIBOFFSET1ON: rst = 2; break; case ADCOFFQUICKWAY: rst = 1; break; case ADCOFFPREDICTSTART: rst = 200; break; case ADCOFFPREDICTEND: rst = 500; break; case OFFSETTUNESTEP1: rst = 5; break; case OFFSETBOUNDARYRATIO1: rst = 100; break; case OFFSETAVGRATIO1: rst = 100; break; case OFFSETEVEN1R: rst = 321; break; case OFFSETODD1R: rst = 321; break; case OFFSETEVEN1G: rst = 321; break; case OFFSETODD1G: rst = 321; break; case OFFSETEVEN1B: rst = 321; break; case OFFSETODD1B: rst = 321; break; case ADCOFFPREDICTR: rst = 333; break; case ADCOFFPREDICTG: rst = 313; break; case ADCOFFPREDICTB: rst = 317; break; case ADCOFFEVEN1R_1ST: rst = 69; break; case ADCOFFODD1R_1ST: rst = 69; break; case ADCOFFEVEN1G_1ST: rst = 87; break; case ADCOFFODD1G_1ST: rst = 87; break; case ADCOFFEVEN1B_1ST: rst = 106; break; case ADCOFFODD1B_1ST: rst = 106; break; case PEAKR: rst = 177; break; case PEAKG: rst = 177; break; case PEAKB: rst = 177; break; case MINR: rst = 136; break; case MING: rst = 136; break; case MINB: rst = 136; break; case CALIBOFFSET2ON: rst = 0; break; case OFFSETTUNESTEP2: rst = 1; break; case OFFSETBOUNDARYRATIO2: rst = 100; break; case OFFSETAVGRATIO2: rst = 100; break; case OFFSETEVEN2R: rst = 0; break; case OFFSETODD2R: rst = 0; break; case OFFSETEVEN2G: rst = 0; break; case OFFSETODD2G: rst = 0; break; case OFFSETEVEN2B: rst = 0; break; case OFFSETODD2B: rst = 0; break; case GAINHEIGHT: rst = 30; break; case GAINTARGETFACTOR: rst = 90; break; case CALIBPAGON: rst = 0; break; case PAGR: rst = 0; break; case PAGG: rst = 0; break; case PAGB: rst = 0; break; case CALIBGAIN1ON: rst = 1; break; case GAIN1R: rst = 24; break; case GAIN1G: rst = 21; break; case GAIN1B: rst = 19; break; case CALIBGAIN2ON: rst = 0; break; case GAIN2R: rst = 8; break; case GAIN2G: rst = 8; break; case GAIN2B: rst = 8; break; case TOTSHADING: rst = 0; break; case BSHADINGON: rst = 2; break; case BSHADINGHEIGHT: rst = 10; break; case BSHADINGPREDIFFR: rst = 2; break; case BSHADINGPREDIFFG: rst = 2; break; case BSHADINGPREDIFFB: rst = 2; break; case BSHADINGDEFCUTOFF: rst = 0; break; case WSHADINGON: rst = 3; break; case WSHADINGHEIGHT: rst = 24; break; case WSHADINGPREDIFFR: rst = -1; break; case WSHADINGPREDIFFG: rst = -1; break; case WSHADINGPREDIFFB: rst = -1; break; } return rst; } static int hp3800_calibtransparent(int option, int defvalue) { int rst = defvalue; switch(option) { case WSTRIPXPOS: rst = 0; break; case WSTRIPYPOS: rst = 4155; break; case BSTRIPXPOS: rst = 0; break; case BSTRIPYPOS: rst = 0; break; case BREFR: rst = 2; break; case BREFG: rst = 2; break; case BREFB: rst = 2; break; case REFBITDEPTH: rst = 8; break; case OFFSETHEIGHT: rst = 10; break; case OFFSETNSIGMA: rst = 2; break; case OFFSETTARGETMAX: rst = 50; break; case OFFSETTARGETMIN: rst = 2; break; case OFFSETAVGTARGETR: rst = 10; break; case OFFSETAVGTARGETG: rst = 10; break; case OFFSETAVGTARGETB: rst = 10; break; case ADCOFFEVENODD: rst = 1; break; case CALIBOFFSET1ON: rst = 2; break; case ADCOFFQUICKWAY: rst = 1; break; case ADCOFFPREDICTSTART: rst = 200; break; case ADCOFFPREDICTEND: rst = 500; break; case OFFSETTUNESTEP1: rst = 5; break; case OFFSETBOUNDARYRATIO1: rst = 100; break; case OFFSETAVGRATIO1: rst = 100; break; case OFFSETEVEN1R: rst = 310; break; case OFFSETODD1R: rst = 310; break; case OFFSETEVEN1G: rst = 299; break; case OFFSETODD1G: rst = 299; break; case OFFSETEVEN1B: rst = 309; break; case OFFSETODD1B: rst = 309; break; case ADCOFFPREDICTR: rst = 333; break; case ADCOFFPREDICTG: rst = 313; break; case ADCOFFPREDICTB: rst = 317; break; case ADCOFFEVEN1R_1ST: rst = 69; break; case ADCOFFODD1R_1ST: rst = 69; break; case ADCOFFEVEN1G_1ST: rst = 87; break; case ADCOFFODD1G_1ST: rst = 87; break; case ADCOFFEVEN1B_1ST: rst = 106; break; case ADCOFFODD1B_1ST: rst = 106; break; case PEAKR: rst = 67; break; case PEAKG: rst = 61; break; case PEAKB: rst = 57; break; case MINR: rst = 10; break; case MING: rst = 10; break; case MINB: rst = 10; break; case CALIBOFFSET2ON: rst = 0; break; case OFFSETTUNESTEP2: rst = 1; break; case OFFSETBOUNDARYRATIO2: rst = 100; break; case OFFSETAVGRATIO2: rst = 100; break; case OFFSETEVEN2R: rst = 0; break; case OFFSETODD2R: rst = 0; break; case OFFSETEVEN2G: rst = 0; break; case OFFSETODD2G: rst = 0; break; case OFFSETEVEN2B: rst = 0; break; case OFFSETODD2B: rst = 0; break; case GAINHEIGHT: rst = 30; break; case GAINTARGETFACTOR: rst = 90; break; case CALIBPAGON: rst = 0; break; case PAGR: rst = 3; break; case PAGG: rst = 3; break; case PAGB: rst = 2; break; case CALIBGAIN1ON: rst = 1; break; case GAIN1R: rst = 9; break; case GAIN1G: rst = 12; break; case GAIN1B: rst = 10; break; case CALIBGAIN2ON: rst = 0; break; case GAIN2R: rst = 4; break; case GAIN2G: rst = 4; break; case GAIN2B: rst = 4; break; case TOTSHADING: rst = 0; break; case BSHADINGON: rst = -3; break; case BSHADINGHEIGHT: rst = 30; break; case BSHADINGPREDIFFR: rst = 2; break; case BSHADINGPREDIFFG: rst = 2; break; case BSHADINGPREDIFFB: rst = 2; break; case BSHADINGDEFCUTOFF: rst = 0; break; case WSHADINGON: rst = 3; break; case WSHADINGHEIGHT: rst = 24; break; case WSHADINGPREDIFFR: rst = -1; break; case WSHADINGPREDIFFG: rst = -1; break; case WSHADINGPREDIFFB: rst = -1; break; } return rst; } static int hp3970_calibtransparent(int option, int defvalue) { int rst = defvalue; switch(option) { case WSTRIPXPOS: rst = 0; break; case WSTRIPYPOS: rst = 7500; break; case BSTRIPXPOS: rst = 0; break; case BSTRIPYPOS: rst = 0; break; case BREFR: rst = 2; break; case BREFG: rst = 2; break; case BREFB: rst = 2; break; case REFBITDEPTH: rst = 8; break; case OFFSETHEIGHT: rst = 10; break; case OFFSETNSIGMA: rst = 2; break; case OFFSETTARGETMAX: rst = 50; break; case OFFSETTARGETMIN: rst = 2; break; case OFFSETAVGTARGETR: rst = 10; break; case OFFSETAVGTARGETG: rst = 10; break; case OFFSETAVGTARGETB: rst = 10; break; case ADCOFFEVENODD: rst = 1; break; case CALIBOFFSET1ON: rst = 2; break; case ADCOFFQUICKWAY: rst = 1; break; case ADCOFFPREDICTSTART: rst = 200; break; case ADCOFFPREDICTEND: rst = 500; break; case OFFSETTUNESTEP1: rst = 5; break; case OFFSETBOUNDARYRATIO1: rst = 100; break; case OFFSETAVGRATIO1: rst = 100; break; case OFFSETEVEN1R: rst = 323; break; case OFFSETODD1R: rst = 323; break; case OFFSETEVEN1G: rst = 327; break; case OFFSETODD1G: rst = 327; break; case OFFSETEVEN1B: rst = 327; break; case OFFSETODD1B: rst = 327; break; case ADCOFFPREDICTR: rst = 333; break; case ADCOFFPREDICTG: rst = 313; break; case ADCOFFPREDICTB: rst = 317; break; case ADCOFFEVEN1R_1ST: rst = 69; break; case ADCOFFODD1R_1ST: rst = 69; break; case ADCOFFEVEN1G_1ST: rst = 87; break; case ADCOFFODD1G_1ST: rst = 87; break; case ADCOFFEVEN1B_1ST: rst = 106; break; case ADCOFFODD1B_1ST: rst = 106; break; case PEAKR: rst = 42; break; case PEAKG: rst = 55; break; case PEAKB: rst = 53; break; case MINR: rst = 31; break; case MING: rst = 39; break; case MINB: rst = 38; break; case CALIBOFFSET2ON: rst = 0; break; case OFFSETTUNESTEP2: rst = 1; break; case OFFSETBOUNDARYRATIO2: rst = 100; break; case OFFSETAVGRATIO2: rst = 100; break; case OFFSETEVEN2R: rst = 0; break; case OFFSETODD2R: rst = 0; break; case OFFSETEVEN2G: rst = 0; break; case OFFSETODD2G: rst = 0; break; case OFFSETEVEN2B: rst = 0; break; case OFFSETODD2B: rst = 0; break; case GAINHEIGHT: rst = 30; break; case GAINTARGETFACTOR: rst = 90; break; case CALIBPAGON: rst = 0; break; case PAGR: rst = 3; break; case PAGG: rst = 3; break; case PAGB: rst = 2; break; case CALIBGAIN1ON: rst = 1; break; case GAIN1R: rst = 21; break; case GAIN1G: rst = 14; break; case GAIN1B: rst = 12; break; case CALIBGAIN2ON: rst = 0; break; case GAIN2R: rst = 4; break; case GAIN2G: rst = 4; break; case GAIN2B: rst = 4; break; case TOTSHADING: rst = 0; break; case BSHADINGON: rst = 1; break; case BSHADINGHEIGHT: rst = 30; break; case BSHADINGPREDIFFR: rst = 2; break; case BSHADINGPREDIFFG: rst = 2; break; case BSHADINGPREDIFFB: rst = 2; break; case BSHADINGDEFCUTOFF: rst = 0; break; case WSHADINGON: rst = 3; break; case WSHADINGHEIGHT: rst = 24; break; case WSHADINGPREDIFFR: rst = -1; break; case WSHADINGPREDIFFG: rst = -1; break; case WSHADINGPREDIFFB: rst = -1; break; } return rst; } static int hp4370_calibtransparent(int option, int defvalue) { int rst = defvalue; switch(option) { case WSTRIPXPOS: rst = 0; break; case WSTRIPYPOS: rst = 6580; break; case BSTRIPXPOS: rst = 0; break; case BSTRIPYPOS: rst = 0; break; case BREFR: rst = 2; break; case BREFG: rst = 2; break; case BREFB: rst = 2; break; case REFBITDEPTH: rst = 8; break; case OFFSETHEIGHT: rst = 10; break; case OFFSETNSIGMA: rst = 2; break; case OFFSETTARGETMAX: rst = 50; break; case OFFSETTARGETMIN: rst = 2; break; case OFFSETAVGTARGETR: rst = 10; break; case OFFSETAVGTARGETG: rst = 10; break; case OFFSETAVGTARGETB: rst = 10; break; case ADCOFFEVENODD: rst = 1; break; case CALIBOFFSET1ON: rst = 2; break; case ADCOFFQUICKWAY: rst = 1; break; case ADCOFFPREDICTSTART: rst = 200; break; case ADCOFFPREDICTEND: rst = 500; break; case OFFSETTUNESTEP1: rst = 5; break; case OFFSETBOUNDARYRATIO1: rst = 100; break; case OFFSETAVGRATIO1: rst = 100; break; case OFFSETEVEN1R: rst = 315; break; case OFFSETODD1R: rst = 321; break; case OFFSETEVEN1G: rst = 321; break; case OFFSETODD1G: rst = 324; break; case OFFSETEVEN1B: rst = 324; break; case OFFSETODD1B: rst = 327; break; case ADCOFFPREDICTR: rst = 333; break; case ADCOFFPREDICTG: rst = 313; break; case ADCOFFPREDICTB: rst = 317; break; case ADCOFFEVEN1R_1ST: rst = 69; break; case ADCOFFODD1R_1ST: rst = 69; break; case ADCOFFEVEN1G_1ST: rst = 87; break; case ADCOFFODD1G_1ST: rst = 87; break; case ADCOFFEVEN1B_1ST: rst = 106; break; case ADCOFFODD1B_1ST: rst = 106; break; case PEAKR: rst = 62; break; case PEAKG: rst = 66; break; case PEAKB: rst = 54; break; case MINR: rst = 42; break; case MING: rst = 45; break; case MINB: rst = 45; break; case CALIBOFFSET2ON: rst = 0; break; case OFFSETTUNESTEP2: rst = 1; break; case OFFSETBOUNDARYRATIO2: rst = 100; break; case OFFSETAVGRATIO2: rst = 100; break; case OFFSETEVEN2R: rst = 0; break; case OFFSETODD2R: rst = 0; break; case OFFSETEVEN2G: rst = 0; break; case OFFSETODD2G: rst = 0; break; case OFFSETEVEN2B: rst = 0; break; case OFFSETODD2B: rst = 0; break; case GAINHEIGHT: rst = 30; break; case GAINTARGETFACTOR: rst = 80; break; case CALIBPAGON: rst = 0; break; case HIPAGR: rst = 3; break; case HIPAGG: rst = 3; break; case HIPAGB: rst = 2; break; case LOPAGR: rst = 3; break; case LOPAGG: rst = 3; break; case LOPAGB: rst = 2; break; case PAGR: rst = 3; break; case PAGG: rst = 3; break; case PAGB: rst = 2; break; case CALIBGAIN1ON: rst = 1; break; case GAIN1R: rst = 11; break; case GAIN1G: rst = 9; break; case GAIN1B: rst = 12; break; case CALIBGAIN2ON: rst = 0; break; case GAIN2R: rst = 4; break; case GAIN2G: rst = 4; break; case GAIN2B: rst = 4; break; case TOTSHADING: rst = 0; break; case BSHADINGON: rst = -2; break; case BSHADINGHEIGHT: rst = 30; break; case BSHADINGPREDIFFR: rst = 2; break; case BSHADINGPREDIFFG: rst = 2; break; case BSHADINGPREDIFFB: rst = 2; break; case BSHADINGDEFCUTOFF: rst = 0; break; case WSHADINGON: rst = 3; break; case WSHADINGHEIGHT: rst = 24; break; case WSHADINGPREDIFFR: rst = -1; break; case WSHADINGPREDIFFG: rst = -1; break; case WSHADINGPREDIFFB: rst = -1; break; } return rst; } static int hpg3110_calibtransparent(int option, int defvalue) { int rst = defvalue; switch(option) { case WSTRIPXPOS: rst = 0; break; case WSTRIPYPOS: rst = 5100; break; case BSTRIPXPOS: rst = 0; break; case BSTRIPYPOS: rst = 0; break; case BREFR: rst = 2; break; case BREFG: rst = 2; break; case BREFB: rst = 2; break; case REFBITDEPTH: rst = 8; break; case OFFSETHEIGHT: rst = 10; break; case OFFSETNSIGMA: rst = 2; break; case OFFSETTARGETMAX: rst = 50; break; case OFFSETTARGETMIN: rst = 2; break; case OFFSETAVGTARGETR: rst = 10; break; case OFFSETAVGTARGETG: rst = 10; break; case OFFSETAVGTARGETB: rst = 10; break; case ADCOFFEVENODD: rst = 1; break; case CALIBOFFSET1ON: rst = 2; break; case ADCOFFQUICKWAY: rst = 1; break; case ADCOFFPREDICTSTART: rst = 200; break; case ADCOFFPREDICTEND: rst = 500; break; case OFFSETTUNESTEP1: rst = 5; break; case OFFSETBOUNDARYRATIO1: rst = 100; break; case OFFSETAVGRATIO1: rst = 100; break; case OFFSETEVEN1R: rst = 315; break; case OFFSETODD1R: rst = 321; break; case OFFSETEVEN1G: rst = 321; break; case OFFSETODD1G: rst = 324; break; case OFFSETEVEN1B: rst = 324; break; case OFFSETODD1B: rst = 327; break; case ADCOFFPREDICTR: rst = 333; break; case ADCOFFPREDICTG: rst = 313; break; case ADCOFFPREDICTB: rst = 317; break; case ADCOFFEVEN1R_1ST: rst = 69; break; case ADCOFFODD1R_1ST: rst = 69; break; case ADCOFFEVEN1G_1ST: rst = 87; break; case ADCOFFODD1G_1ST: rst = 87; break; case ADCOFFEVEN1B_1ST: rst = 106; break; case ADCOFFODD1B_1ST: rst = 106; break; case PEAKR: rst = 62; break; case PEAKG: rst = 66; break; case PEAKB: rst = 54; break; case MINR: rst = 42; break; case MING: rst = 45; break; case MINB: rst = 45; break; case CALIBOFFSET2ON: rst = 0; break; case OFFSETTUNESTEP2: rst = 1; break; case OFFSETBOUNDARYRATIO2: rst = 100; break; case OFFSETAVGRATIO2: rst = 100; break; case OFFSETEVEN2R: rst = 0; break; case OFFSETODD2R: rst = 0; break; case OFFSETEVEN2G: rst = 0; break; case OFFSETODD2G: rst = 0; break; case OFFSETEVEN2B: rst = 0; break; case OFFSETODD2B: rst = 0; break; case GAINHEIGHT: rst = 30; break; case GAINTARGETFACTOR: rst = 80; break; case CALIBPAGON: rst = 0; break; case HIPAGR: rst = 3; break; case HIPAGG: rst = 3; break; case HIPAGB: rst = 2; break; case LOPAGR: rst = 3; break; case LOPAGG: rst = 3; break; case LOPAGB: rst = 2; break; case PAGR: rst = 3; break; case PAGG: rst = 3; break; case PAGB: rst = 2; break; case CALIBGAIN1ON: rst = 1; break; case GAIN1R: rst = 11; break; case GAIN1G: rst = 9; break; case GAIN1B: rst = 12; break; case CALIBGAIN2ON: rst = 0; break; case GAIN2R: rst = 4; break; case GAIN2G: rst = 4; break; case GAIN2B: rst = 4; break; case TOTSHADING: rst = 0; break; case BSHADINGON: rst = -2; break; case BSHADINGHEIGHT: rst = 30; break; case BSHADINGPREDIFFR: rst = 2; break; case BSHADINGPREDIFFG: rst = 2; break; case BSHADINGPREDIFFB: rst = 2; break; case BSHADINGDEFCUTOFF: rst = 0; break; case WSHADINGON: rst = 3; break; case WSHADINGHEIGHT: rst = 24; break; case WSHADINGPREDIFFR: rst = -1; break; case WSHADINGPREDIFFG: rst = -1; break; case WSHADINGPREDIFFB: rst = -1; break; } return rst; } static int fc_calibtransparent(int option, int defvalue) { int rst; switch(RTS_Debug->dev_model) { case UA4900: rst = ua4900_calibtransparent(option, defvalue); break; case HPG2710: case HP3800: rst = hp3800_calibtransparent(option, defvalue); break; case HPG3010: case HP4370: rst = hp4370_calibtransparent(option, defvalue); break; case HPG3110: rst = hpg3110_calibtransparent(option, defvalue); break; default : rst = hp3970_calibtransparent(option, defvalue); break; } return rst; } static int ua4900_calibnegative(int option, int defvalue) { int rst = defvalue; switch(option) { case WSTRIPXPOS: rst = 0; break; case WSTRIPYPOS: rst = 12100; break; case BSTRIPXPOS: rst = 0; break; case BSTRIPYPOS: rst = 0; break; case BREFR: rst = 2; break; case BREFG: rst = 2; break; case BREFB: rst = 2; break; case REFBITDEPTH: rst = 8; break; case OFFSETHEIGHT: rst = 10; break; case OFFSETNSIGMA: rst = 2; break; case OFFSETTARGETMAX: rst = 50; break; case OFFSETTARGETMIN: rst = 2; break; case OFFSETAVGTARGETR: rst = 5; break; case OFFSETAVGTARGETG: rst = 5; break; case OFFSETAVGTARGETB: rst = 5; break; case ADCOFFEVENODD: rst = 1; break; case CALIBOFFSET1ON: rst = 2; break; case ADCOFFQUICKWAY: rst = 1; break; case ADCOFFPREDICTSTART: rst = 200; break; case ADCOFFPREDICTEND: rst = 500; break; case OFFSETTUNESTEP1: rst = 5; break; case OFFSETBOUNDARYRATIO1: rst = 100; break; case OFFSETAVGRATIO1: rst = 100; break; case OFFSETEVEN1R: rst = 283; break; case OFFSETODD1R: rst = 283; break; case OFFSETEVEN1G: rst = 279; break; case OFFSETODD1G: rst = 279; break; case OFFSETEVEN1B: rst = 295; break; case OFFSETODD1B: rst = 295; break; case ADCOFFPREDICTR: rst = 333; break; case ADCOFFPREDICTG: rst = 313; break; case ADCOFFPREDICTB: rst = 317; break; case ADCOFFEVEN1R_1ST: rst = 69; break; case ADCOFFODD1R_1ST: rst = 69; break; case ADCOFFEVEN1G_1ST: rst = 87; break; case ADCOFFODD1G_1ST: rst = 87; break; case ADCOFFEVEN1B_1ST: rst = 106; break; case ADCOFFODD1B_1ST: rst = 106; break; case PEAKR: rst = 113; break; case PEAKG: rst = 145; break; case PEAKB: rst = 126; break; case MINR: rst = 80; break; case MING: rst = 105; break; case MINB: rst = 96; break; case CALIBOFFSET2ON: rst = 0; break; case OFFSETTUNESTEP2: rst = 1; break; case OFFSETBOUNDARYRATIO2: rst = 100; break; case OFFSETAVGRATIO2: rst = 100; break; case OFFSETEVEN2R: rst = 0; break; case OFFSETODD2R: rst = 0; break; case OFFSETEVEN2G: rst = 0; break; case OFFSETODD2G: rst = 0; break; case OFFSETEVEN2B: rst = 0; break; case OFFSETODD2B: rst = 0; break; case GAINHEIGHT: rst = 10; break; case GAINTARGETFACTOR: rst = 90; break; case CALIBPAGON: rst = 0; break; case PAGR: rst = 0; break; case PAGG: rst = 0; break; case PAGB: rst = 0; break; case CALIBGAIN1ON: rst = 1; break; case GAIN1R: rst = 8; break; case GAIN1G: rst = 8; break; case GAIN1B: rst = 8; break; case CALIBGAIN2ON: rst = 0; break; case GAIN2R: rst = 8; break; case GAIN2G: rst = 8; break; case GAIN2B: rst = 8; break; case TOTSHADING: rst = 0; break; case BSHADINGON: rst = 2; break; case BSHADINGHEIGHT: rst = 10; break; case BSHADINGPREDIFFR: rst = 2; break; case BSHADINGPREDIFFG: rst = 2; break; case BSHADINGPREDIFFB: rst = 2; break; case BSHADINGDEFCUTOFF: rst = 0; break; case WSHADINGON: rst = 3; break; case WSHADINGHEIGHT: rst = 24; break; case WSHADINGPREDIFFR: rst = -1; break; case WSHADINGPREDIFFG: rst = -1; break; case WSHADINGPREDIFFB: rst = -1; break; } return rst; } static int hp3800_calibnegative(int option, int defvalue) { int rst = defvalue; switch(option) { case WSTRIPXPOS: rst = 0; break; case WSTRIPYPOS: rst = 4155; break; case BSTRIPXPOS: rst = 0; break; case BSTRIPYPOS: rst = 0; break; case BREFR: rst = 10; break; case BREFG: rst = 10; break; case BREFB: rst = 10; break; case REFBITDEPTH: rst = 8; break; case OFFSETHEIGHT: rst = 10; break; case OFFSETNSIGMA: rst = 2; break; case OFFSETTARGETMAX: rst = 50; break; case OFFSETTARGETMIN: rst = 2; break; case OFFSETAVGTARGETR: rst = 10; break; case OFFSETAVGTARGETG: rst = 10; break; case OFFSETAVGTARGETB: rst = 10; break; case ADCOFFEVENODD: rst = 1; break; case CALIBOFFSET1ON: rst = 2; break; case ADCOFFQUICKWAY: rst = 1; break; case ADCOFFPREDICTSTART: rst = 200; break; case ADCOFFPREDICTEND: rst = 500; break; case OFFSETTUNESTEP1: rst = 5; break; case OFFSETBOUNDARYRATIO1: rst = 100; break; case OFFSETAVGRATIO1: rst = 100; break; case OFFSETEVEN1R: rst = 315; break; case OFFSETODD1R: rst = 315; break; case OFFSETEVEN1G: rst = 307; break; case OFFSETODD1G: rst = 304; break; case OFFSETEVEN1B: rst = 309; break; case OFFSETODD1B: rst = 309; break; case ADCOFFPREDICTR: rst = 333; break; case ADCOFFPREDICTG: rst = 313; break; case ADCOFFPREDICTB: rst = 317; break; case ADCOFFEVEN1R_1ST: rst = 69; break; case ADCOFFODD1R_1ST: rst = 69; break; case ADCOFFEVEN1G_1ST: rst = 87; break; case ADCOFFODD1G_1ST: rst = 87; break; case ADCOFFEVEN1B_1ST: rst = 106; break; case ADCOFFODD1B_1ST: rst = 106; break; case PEAKR: rst = 37; break; case PEAKG: rst = 234; break; case PEAKB: rst = 202; break; case MINR: rst = 32; break; case MING: rst = 195; break; case MINB: rst = 166; break; case CALIBOFFSET2ON: rst = 0; break; case OFFSETTUNESTEP2: rst = 1; break; case OFFSETBOUNDARYRATIO2: rst = 100; break; case OFFSETAVGRATIO2: rst = 100; break; case OFFSETEVEN2R: rst = 0; break; case OFFSETODD2R: rst = 0; break; case OFFSETEVEN2G: rst = 0; break; case OFFSETODD2G: rst = 0; break; case OFFSETEVEN2B: rst = 0; break; case OFFSETODD2B: rst = 0; break; case GAINHEIGHT: rst = 30; break; case GAINTARGETFACTOR: rst = 90; break; case CALIBPAGON: rst = 0; break; case PAGR: rst = 3; break; case PAGG: rst = 3; break; case PAGB: rst = 3; break; case CALIBGAIN1ON: rst = 1; break; case GAIN1R: rst = 30; break; case GAIN1G: rst = 3; break; case GAIN1B: rst = 0; break; case CALIBGAIN2ON: rst = 0; break; case GAIN2R: rst = 4; break; case GAIN2G: rst = 4; break; case GAIN2B: rst = 4; break; case TOTSHADING: rst = 0; break; case BSHADINGON: rst = -2; break; case BSHADINGHEIGHT: rst = 30; break; case BSHADINGPREDIFFR: rst = 2; break; case BSHADINGPREDIFFG: rst = 2; break; case BSHADINGPREDIFFB: rst = 2; break; case BSHADINGDEFCUTOFF: rst = 0; break; case WSHADINGON: rst = 3; break; case WSHADINGHEIGHT: rst = 24; break; case WSHADINGPREDIFFR: rst = -1; break; case WSHADINGPREDIFFG: rst = -1; break; case WSHADINGPREDIFFB: rst = -1; break; } return rst; } static int hp3970_calibnegative(int option, int defvalue) { int rst = defvalue; switch(option) { case WSTRIPXPOS: rst = 0; break; case WSTRIPYPOS: rst = 7500; break; case BSTRIPXPOS: rst = 0; break; case BSTRIPYPOS: rst = 0; break; case BREFR: rst = 10; break; case BREFG: rst = 10; break; case BREFB: rst = 10; break; case REFBITDEPTH: rst = 8; break; case OFFSETHEIGHT: rst = 10; break; case OFFSETNSIGMA: rst = 2; break; case OFFSETTARGETMAX: rst = 50; break; case OFFSETTARGETMIN: rst = 2; break; case OFFSETAVGTARGETR: rst = 10; break; case OFFSETAVGTARGETG: rst = 10; break; case OFFSETAVGTARGETB: rst = 10; break; case ADCOFFEVENODD: rst = 1; break; case CALIBOFFSET1ON: rst = 2; break; case ADCOFFQUICKWAY: rst = 1; break; case ADCOFFPREDICTSTART: rst = 200; break; case ADCOFFPREDICTEND: rst = 500; break; case OFFSETTUNESTEP1: rst = 5; break; case OFFSETBOUNDARYRATIO1: rst = 100; break; case OFFSETAVGRATIO1: rst = 100; break; case OFFSETEVEN1R: rst = 294; break; case OFFSETODD1R: rst = 294; break; case OFFSETEVEN1G: rst = 276; break; case OFFSETODD1G: rst = 276; break; case OFFSETEVEN1B: rst = 266; break; case OFFSETODD1B: rst = 266; break; case ADCOFFPREDICTR: rst = 333; break; case ADCOFFPREDICTG: rst = 313; break; case ADCOFFPREDICTB: rst = 317; break; case ADCOFFEVEN1R_1ST: rst = 69; break; case ADCOFFODD1R_1ST: rst = 69; break; case ADCOFFEVEN1G_1ST: rst = 87; break; case ADCOFFODD1G_1ST: rst = 87; break; case ADCOFFEVEN1B_1ST: rst = 106; break; case ADCOFFODD1B_1ST: rst = 106; break; case PEAKR: rst = 33; break; case PEAKG: rst = 75; break; case PEAKB: rst = 105; break; case MINR: rst = 19; break; case MING: rst = 34; break; case MINB: rst = 42; break; case CALIBOFFSET2ON: rst = 0; break; case OFFSETTUNESTEP2: rst = 1; break; case OFFSETBOUNDARYRATIO2: rst = 100; break; case OFFSETAVGRATIO2: rst = 100; break; case OFFSETEVEN2R: rst = 0; break; case OFFSETODD2R: rst = 0; break; case OFFSETEVEN2G: rst = 0; break; case OFFSETODD2G: rst = 0; break; case OFFSETEVEN2B: rst = 0; break; case OFFSETODD2B: rst = 0; break; case GAINHEIGHT: rst = 30; break; case GAINTARGETFACTOR: rst = 90; break; case CALIBPAGON: rst = 0; break; case PAGR: rst = 3; break; case PAGG: rst = 3; break; case PAGB: rst = 3; break; case CALIBGAIN1ON: rst = 1; break; case GAIN1R: rst = 23; break; case GAIN1G: rst = 18; break; case GAIN1B: rst = 23; break; case CALIBGAIN2ON: rst = 0; break; case GAIN2R: rst = 4; break; case GAIN2G: rst = 4; break; case GAIN2B: rst = 4; break; case TOTSHADING: rst = 0; break; case BSHADINGON: rst = 1; break; case BSHADINGHEIGHT: rst = 30; break; case BSHADINGPREDIFFR: rst = 2; break; case BSHADINGPREDIFFG: rst = 2; break; case BSHADINGPREDIFFB: rst = 2; break; case BSHADINGDEFCUTOFF: rst = 0; break; case WSHADINGON: rst = 3; break; case WSHADINGHEIGHT: rst = 24; break; case WSHADINGPREDIFFR: rst = -1; break; case WSHADINGPREDIFFG: rst = -1; break; case WSHADINGPREDIFFB: rst = -1; break; } return rst; } static int hpg3110_calibnegative(int option, int defvalue) { int rst = defvalue; switch(option) { case WSTRIPXPOS: rst = 0; break; case WSTRIPYPOS: rst = 5100; break; case BSTRIPXPOS: rst = 0; break; case BSTRIPYPOS: rst = 0; break; case BREFR: rst = 10; break; case BREFG: rst = 10; break; case BREFB: rst = 10; break; case REFBITDEPTH: rst = 8; break; case OFFSETHEIGHT: rst = 10; break; case OFFSETNSIGMA: rst = 2; break; case OFFSETTARGETMAX: rst = 50; break; case OFFSETTARGETMIN: rst = 2; break; case OFFSETAVGTARGETR: rst = 10; break; case OFFSETAVGTARGETG: rst = 10; break; case OFFSETAVGTARGETB: rst = 10; break; case ADCOFFEVENODD: rst = 1; break; case CALIBOFFSET1ON: rst = 2; break; case ADCOFFQUICKWAY: rst = 1; break; case ADCOFFPREDICTSTART: rst = 200; break; case ADCOFFPREDICTEND: rst = 500; break; case OFFSETTUNESTEP1: rst = 5; break; case OFFSETBOUNDARYRATIO1: rst = 100; break; case OFFSETAVGRATIO1: rst = 100; break; case OFFSETEVEN1R: rst = 308; break; case OFFSETODD1R: rst = 308; break; case OFFSETEVEN1G: rst = 317; break; case OFFSETODD1G: rst = 317; break; case OFFSETEVEN1B: rst = 319; break; case OFFSETODD1B: rst = 319; break; case ADCOFFPREDICTR: rst = 333; break; case ADCOFFPREDICTG: rst = 313; break; case ADCOFFPREDICTB: rst = 317; break; case ADCOFFEVEN1R_1ST: rst = 69; break; case ADCOFFODD1R_1ST: rst = 69; break; case ADCOFFEVEN1G_1ST: rst = 87; break; case ADCOFFODD1G_1ST: rst = 87; break; case ADCOFFEVEN1B_1ST: rst = 106; break; case ADCOFFODD1B_1ST: rst = 106; break; case PEAKR: rst = 116; break; case PEAKG: rst = 126; break; case PEAKB: rst = 102; break; case MINR: rst = 103; break; case MING: rst = 112; break; case MINB: rst = 80; break; case CALIBOFFSET2ON: rst = 0; break; case OFFSETTUNESTEP2: rst = 1; break; case OFFSETBOUNDARYRATIO2: rst = 100; break; case OFFSETAVGRATIO2: rst = 100; break; case OFFSETEVEN2R: rst = 0; break; case OFFSETODD2R: rst = 0; break; case OFFSETEVEN2G: rst = 0; break; case OFFSETODD2G: rst = 0; break; case OFFSETEVEN2B: rst = 0; break; case OFFSETODD2B: rst = 0; break; case GAINHEIGHT: rst = 30; break; case GAINTARGETFACTOR: rst = 80; break; case CALIBPAGON: rst = 0; break; case HIPAGR: rst = 3; break; case HIPAGG: rst = 3; break; case HIPAGB: rst = 3; break; case LOPAGR: rst = 3; break; case LOPAGG: rst = 3; break; case LOPAGB: rst = 3; break; case PAGR: rst = 3; break; case PAGG: rst = 3; break; case PAGB: rst = 3; break; case CALIBGAIN1ON: rst = 1; break; case GAIN1R: rst = 6; break; case GAIN1G: rst = 1; break; case GAIN1B: rst = 7; break; case CALIBGAIN2ON: rst = 0; break; case GAIN2R: rst = 4; break; case GAIN2G: rst = 4; break; case GAIN2B: rst = 4; break; case TOTSHADING: rst = 0; break; case BSHADINGON: rst = -2; break; case BSHADINGHEIGHT: rst = 30; break; case BSHADINGPREDIFFR: rst = 2; break; case BSHADINGPREDIFFG: rst = 2; break; case BSHADINGPREDIFFB: rst = 2; break; case BSHADINGDEFCUTOFF: rst = 0; break; case WSHADINGON: rst = 3; break; case WSHADINGHEIGHT: rst = 24; break; case WSHADINGPREDIFFR: rst = -1; break; case WSHADINGPREDIFFG: rst = -1; break; case WSHADINGPREDIFFB: rst = -1; break; } return rst; } static int hp4370_calibnegative(int option, int defvalue) { int rst = defvalue; switch(option) { case WSTRIPXPOS: rst = 0; break; case WSTRIPYPOS: rst = 6580; break; case BSTRIPXPOS: rst = 0; break; case BSTRIPYPOS: rst = 0; break; case BREFR: rst = 10; break; case BREFG: rst = 10; break; case BREFB: rst = 10; break; case REFBITDEPTH: rst = 8; break; case OFFSETHEIGHT: rst = 10; break; case OFFSETNSIGMA: rst = 2; break; case OFFSETTARGETMAX: rst = 50; break; case OFFSETTARGETMIN: rst = 2; break; case OFFSETAVGTARGETR: rst = 10; break; case OFFSETAVGTARGETG: rst = 10; break; case OFFSETAVGTARGETB: rst = 10; break; case ADCOFFEVENODD: rst = 1; break; case CALIBOFFSET1ON: rst = 2; break; case ADCOFFQUICKWAY: rst = 1; break; case ADCOFFPREDICTSTART: rst = 200; break; case ADCOFFPREDICTEND: rst = 500; break; case OFFSETTUNESTEP1: rst = 5; break; case OFFSETBOUNDARYRATIO1: rst = 100; break; case OFFSETAVGRATIO1: rst = 100; break; case OFFSETEVEN1R: rst = 308; break; case OFFSETODD1R: rst = 308; break; case OFFSETEVEN1G: rst = 317; break; case OFFSETODD1G: rst = 317; break; case OFFSETEVEN1B: rst = 319; break; case OFFSETODD1B: rst = 319; break; case ADCOFFPREDICTR: rst = 333; break; case ADCOFFPREDICTG: rst = 313; break; case ADCOFFPREDICTB: rst = 317; break; case ADCOFFEVEN1R_1ST: rst = 69; break; case ADCOFFODD1R_1ST: rst = 69; break; case ADCOFFEVEN1G_1ST: rst = 87; break; case ADCOFFODD1G_1ST: rst = 87; break; case ADCOFFEVEN1B_1ST: rst = 106; break; case ADCOFFODD1B_1ST: rst = 106; break; case PEAKR: rst = 116; break; case PEAKG: rst = 126; break; case PEAKB: rst = 102; break; case MINR: rst = 103; break; case MING: rst = 112; break; case MINB: rst = 80; break; case CALIBOFFSET2ON: rst = 0; break; case OFFSETTUNESTEP2: rst = 1; break; case OFFSETBOUNDARYRATIO2: rst = 100; break; case OFFSETAVGRATIO2: rst = 100; break; case OFFSETEVEN2R: rst = 0; break; case OFFSETODD2R: rst = 0; break; case OFFSETEVEN2G: rst = 0; break; case OFFSETODD2G: rst = 0; break; case OFFSETEVEN2B: rst = 0; break; case OFFSETODD2B: rst = 0; break; case GAINHEIGHT: rst = 30; break; case GAINTARGETFACTOR: rst = 80; break; case CALIBPAGON: rst = 0; break; case HIPAGR: rst = 3; break; case HIPAGG: rst = 3; break; case HIPAGB: rst = 3; break; case LOPAGR: rst = 3; break; case LOPAGG: rst = 3; break; case LOPAGB: rst = 3; break; case PAGR: rst = 3; break; case PAGG: rst = 3; break; case PAGB: rst = 3; break; case CALIBGAIN1ON: rst = 1; break; case GAIN1R: rst = 6; break; case GAIN1G: rst = 1; break; case GAIN1B: rst = 7; break; case CALIBGAIN2ON: rst = 0; break; case GAIN2R: rst = 4; break; case GAIN2G: rst = 4; break; case GAIN2B: rst = 4; break; case TOTSHADING: rst = 0; break; case BSHADINGON: rst = -2; break; case BSHADINGHEIGHT: rst = 30; break; case BSHADINGPREDIFFR: rst = 2; break; case BSHADINGPREDIFFG: rst = 2; break; case BSHADINGPREDIFFB: rst = 2; break; case BSHADINGDEFCUTOFF: rst = 0; break; case WSHADINGON: rst = 3; break; case WSHADINGHEIGHT: rst = 24; break; case WSHADINGPREDIFFR: rst = -1; break; case WSHADINGPREDIFFG: rst = -1; break; case WSHADINGPREDIFFB: rst = -1; break; } return rst; } static int fc_calibnegative(int option, int defvalue) { int rst; switch(RTS_Debug->dev_model) { case UA4900: rst = ua4900_calibnegative(option, defvalue); break; case HPG2710: case HP3800: rst = hp3800_calibnegative(option, defvalue); break; case HPG3010: case HP4370: rst = hp4370_calibnegative(option, defvalue); break; case HPG3110: rst = hpg3110_calibnegative(option, defvalue); break; default : rst = hp3970_calibnegative(option, defvalue); break; } return rst; } static int fc_scaninfo_get(int option, int defvalue) { int value[] = {1, 0, 0, 0, 0, 100}; int ua4900_value[] = {1, 0xcdcdcdcd, 0xcdcdcdcd, 0xcdcdcdcd, 0xcdcdcdcd, 100}; int rst = defvalue; int *myvalue = NULL; switch(RTS_Debug->dev_model) { case UA4900: myvalue = ua4900_value; break; default: myvalue = value; break; } switch(option) { case PARKHOMEAFTERCALIB: rst = myvalue[0]; break; case SHADINGTIME_16BIT: rst = myvalue[1]; break; case SHADOWTIME_16BIT: rst = myvalue[2]; break; case SHADINGTIME_8BIT: rst = myvalue[3]; break; case SHADOWTIME_8BIT: rst = myvalue[4]; break; case PREVIEWDPI: rst = myvalue[5]; break; } return rst; } /* fitcalibrate */ static int fitcalibrate_get(int section, int option, int defvalue) { int rst = defvalue; switch(section) { case CALIBREFLECTIVE: rst = fc_calibreflective(option, defvalue); break; case CALIBTRANSPARENT: rst = fc_calibtransparent(option, defvalue); break; case CALIBNEGATIVEFILM: rst = fc_calibnegative(option, defvalue); break; case SCANINFO: rst = fc_scaninfo_get(option, defvalue); break; } return rst; } static int srt_hp3800_scanparam_get(int option, int defvalue) { int rst = defvalue; /* t_rtinifile */ int value3[] = {1, 0, 0, 0, 1, 12, 0, 1, 170, 140, 40, 30, 40, 30, 1500, 20, 0, 36, 0}; int *value = value3; if (value != NULL) switch(option) { case ARRANGELINE: rst = value[0]; break; case COMPRESSION: rst = value[1]; break; case TA_X_START: rst = value[2]; break; case TA_Y_START: rst = value[3]; break; case DPIGAINCONTROL600: rst = value[4]; break; case CRVS: rst = value[5]; break; case MLOCK: rst = value[6]; break; case ENABLEWARMUP: rst = value[7]; break; case NMAXTARGET: rst = value[8]; break; case NMINTARGET: rst = value[9]; break; case NMAXTARGETTA: rst = value[10]; break; case NMINTARGETTA: rst = value[11]; break; case NMAXTARGETNEG: rst = value[12]; break; case NMINTARGETNEG: rst = value[13]; break; case STABLEDIFF: rst = value[14]; break; case DELTAPWM: rst = value[15]; break; case PWMLAMPLEVEL: rst = value[16]; break; case TMAPWMDUTY: rst = value[17]; break; case LEFTLEADING: rst = value[18]; break; } return rst; } static int srt_hp3970_scanparam_get(int file, int option, int defvalue) { int rst = defvalue; /* s_rtinifile */ int value1[] = {1, 0, 150, 0, 1, 6, 0, 0, 170, 140, 40, 30, 40, 30, 1500, 20, 0, 36, 360}; /* s_usb1inifile */ int value2[] = {1, 0, 150, 0, 1, 6, 0, 0, 170, 140, 40, 30, 40, 30, 1500, 20, 0, 36, 360}; /* t_rtinifile */ int value3[] = {1, 0, 150, 0, 1, 12, 0, 0, 170, 140, 40, 30, 40, 30, 1500, 20, 0, 36, 0}; /* t_usb1inifile */ int value4[] = {1, 0, 150, 0, 1, 12, 0, 0, 170, 140, 40, 30, 40, 30, 1500, 20, 0, 36, 0}; int *value = NULL; switch(file) { case S_RTINIFILE: value = value1; break; case S_USB1INIFILE: value = value2; break; case T_RTINIFILE: value = value3; break; case T_USB1INIFILE: value = value4; break; } if (value != NULL) switch(option) { case ARRANGELINE: rst = value[0]; break; case COMPRESSION: rst = value[1]; break; case TA_X_START: rst = value[2]; break; case TA_Y_START: rst = value[3]; break; case DPIGAINCONTROL600: rst = value[4]; break; case CRVS: rst = value[5]; break; case MLOCK: rst = value[6]; break; case ENABLEWARMUP: rst = value[7]; break; case NMAXTARGET: rst = value[8]; break; case NMINTARGET: rst = value[9]; break; case NMAXTARGETTA: rst = value[10]; break; case NMINTARGETTA: rst = value[11]; break; case NMAXTARGETNEG: rst = value[12]; break; case NMINTARGETNEG: rst = value[13]; break; case STABLEDIFF: rst = value[14]; break; case DELTAPWM: rst = value[15]; break; case PWMLAMPLEVEL: rst = value[16]; break; case TMAPWMDUTY: rst = value[17]; break; case LEFTLEADING: rst = value[18]; break; } return rst; } static int srt_hp4370_scanparam_get(int file, int option, int defvalue) { /* s_rtinifile */ int value1[] = {1, 0, 150, 0, 1, 6, 0, 0, 170, 140, 40, 30, 40, 30, 1500, 20, 0, 36, 360}; /* s_usb1inifile */ int value2[] = {1, 0, 150, 0, 1, 6, 0, 0, 170, 140, 40, 30, 40, 30, 1500, 20, 0, 36, 360}; /* t_rtinifile */ int value3[] = {1, 0, 150, 0, 1, 12, 0, 0, 170, 140, 40, 30, 40, 30, 1500, 20, 0, 36, 0}; /* t_usb1inifile */ int value4[] = {1, 0, 150, 0, 1, 12, 0, 0, 170, 140, 40, 30, 40, 30, 1500, 20, 0, 36, 0}; int *value = NULL; int rst = defvalue; switch(file) { case S_RTINIFILE: value = value1; break; case S_USB1INIFILE: value = value2; break; case T_RTINIFILE: value = value3; break; case T_USB1INIFILE: value = value4; break; } if (value != NULL) switch(option) { case ARRANGELINE: rst = value[0]; break; case COMPRESSION: rst = value[1]; break; case TA_X_START: rst = value[2]; break; case TA_Y_START: rst = value[3]; break; case DPIGAINCONTROL600: rst = value[4]; break; case CRVS: rst = value[5]; break; case MLOCK: rst = value[6]; break; case ENABLEWARMUP: rst = value[7]; break; case NMAXTARGET: rst = value[8]; break; case NMINTARGET: rst = value[9]; break; case NMAXTARGETTA: rst = value[10]; break; case NMINTARGETTA: rst = value[11]; break; case NMAXTARGETNEG: rst = value[12]; break; case NMINTARGETNEG: rst = value[13]; break; case STABLEDIFF: rst = value[14]; break; case DELTAPWM: rst = value[15]; break; case PWMLAMPLEVEL: rst = value[16]; break; case TMAPWMDUTY: rst = value[17]; break; case LEFTLEADING: rst = value[18]; break; } return rst; } static int srt_scancali_get(int file, int option, int defvalue) { int rst = defvalue; /* s_rtinifile */ int value1[] = {3, 3, 3, 14, 4, 4, 41, 41, 42, 41, 41, 42, 91, 91, 53, 53, 48, 48, 104, 104, 59, 59, 64,64}; /* s_usb1inifile */ int value2[] = {3, 3, 3, 14, 4, 4, 41, 41, 42, 41, 41, 42, 91, 91, 53, 53, 48, 48, 104, 104, 59, 59, 64, 64}; /* t_rtinifile*/ int value3[] = {3, 3, 3, 14, 4, 4, 41, 41, 42, 41, 41, 42, 270, 270, 511, 511, 511, 511, 270, 270, 511, 511, 511, 511}; /* t_usb1inifile*/ int value4[] = {3, 3, 3, 14, 4, 4, 41, 41, 42, 41, 41, 42, 270, 270, 511, 511, 511, 511, 270, 270, 511, 511, 511, 511}; int *value = NULL; switch(file) { case S_RTINIFILE: value = value1; break; case S_USB1INIFILE: value = value2; break; case T_RTINIFILE: value = value3; break; case T_USB1INIFILE: value = value4; break; } if (value != NULL) switch(option) { case PGA1: rst = value[0]; break; case PGA2: rst = value[1]; break; case PGA3: rst = value[2]; break; case VGAGAIN11: rst = value[3]; break; case VGAGAIN12: rst = value[4]; break; case VGAGAIN13: rst = value[5]; break; case DCSTEPEVEN1: rst = value[6]; break; case DCSTEPODD1: rst = value[7]; break; case DCSTEPEVEN2: rst = value[8]; break; case DCSTEPODD2: rst = value[9]; break; case DCSTEPEVEN3: rst = value[10]; break; case DCSTEPODD3: rst = value[11]; break; case FIRSTDCOFFSETEVEN11: rst = value[12]; break; case FIRSTDCOFFSETODD11: rst = value[13]; break; case FIRSTDCOFFSETEVEN12: rst = value[14]; break; case FIRSTDCOFFSETODD12: rst = value[15]; break; case FIRSTDCOFFSETEVEN13: rst = value[16]; break; case FIRSTDCOFFSETODD13: rst = value[17]; break; case DCOFFSETEVEN11: rst = value[18]; break; case DCOFFSETODD11: rst = value[19]; break; case DCOFFSETEVEN12: rst = value[20]; break; case DCOFFSETODD12: rst = value[21]; break; case DCOFFSETEVEN13: rst = value[22]; break; case DCOFFSETODD13: rst = value[23]; break; } return rst; } static int srt_truegrayparam_get(int file, int option, int defvalue) { int rst = defvalue; /* s_rtinifile */ int value1[] = {100, 30, 59, 11}; /* s_usb1inifile */ int value2[] = {100, 30, 59, 11}; /* t_rtinifile */ int value3[] = {100, 30, 59, 11}; /* t_usb1inifile */ int value4[] = {100, 30, 59, 11}; int *value = NULL; switch(file) { case S_RTINIFILE: value = value1; break; case S_USB1INIFILE: value = value2; break; case T_RTINIFILE: value = value3; break; case T_USB1INIFILE: value = value4; break; } if (value != NULL) switch(option) { case SHADINGBASE: rst = value[0]; break; case SHADINGFACT1: rst = value[1]; break; case SHADINGFACT2: rst = value[2]; break; case SHADINGFACT3: rst = value[3]; break; } return rst; } static int srt_caliparam_get(int file, int option, int defvalue) { int rst = defvalue; /* s_rtinifile */ int value1[] = {0xffff}; /* s_usb1inifile */ int value2[] = {0xffff}; /* t_rtinifile */ int value3[] = {0xffff}; /* t_usb1inifile */ int value4[] = {0xffff}; int *value = NULL; switch(file) { case S_RTINIFILE: value = value1; break; case S_USB1INIFILE: value = value2; break; case T_RTINIFILE: value = value3; break; case T_USB1INIFILE: value = value4; break; } if (value != NULL) switch(option) { case PIXELDARKLEVEL: rst = value[0]; break; } return rst; } static int srt_hp3800_platform_get(int option, int defvalue) { /* s_rtinifile*/ int value1[] = {100, 99, 1214636}; int *value = value1; int rst = defvalue; if (value != NULL) { switch(option) { case BINARYTHRESHOLDH: rst = value[0]; break; case BINARYTHRESHOLDL: rst = value[1]; break; case CLOSETIME: rst = value[2]; break; } } return rst; } static int srt_hp3970_platform_get(int option, int defvalue) { /* s_rtinifile*/ int value1[] = {128, 127, 1214636}; int *value = value1; int rst = defvalue; if (value != NULL) { switch(option) { case BINARYTHRESHOLDH: rst = value[0]; break; case BINARYTHRESHOLDL: rst = value[1]; break; case CLOSETIME: rst = value[2]; break; } } return rst; } static int srt_ua4900_platform_get(int option, int defvalue) { int value1[] = {128, 127, 1214636}; int *value = value1; int rst = defvalue; if (value != NULL) { switch(option) { case BINARYTHRESHOLDH: rst = value[0]; break; case BINARYTHRESHOLDL: rst = value[1]; break; case CLOSETIME: rst = value[2]; break; } } return rst; } static int srt_hp4370_platform_get(int option, int defvalue) { /* t_rtinifile */ int value3[] = {128, 127, 1214636}; int *value = value3; int rst = defvalue; if (value != NULL) { switch(option) { case BINARYTHRESHOLDH: rst = value[0]; break; case BINARYTHRESHOLDL: rst = value[1]; break; case CLOSETIME: rst = value[2]; break; } } return rst; } static int srt_scaninfo_get(int file, int option, int defvalue) { int rst = defvalue; int value1[] = {0, 0, 0, 0}; int value2[] = {0, 0, 0, 0}; int value3[] = {0, 0, 0, 0}; int value4[] = {0, 0, 0, 0}; int *value = NULL; switch(file) { case S_RTINIFILE: value = value1; break; case S_USB1INIFILE: value = value2; break; case T_RTINIFILE: value = value3; break; case T_USB1INIFILE: value = value4; break; } if (value != NULL) { switch(option) { case SHADINGTIME_16BIT: rst = value[0]; break; case SHADOWTIME_16BIT: rst = value[1]; break; case SHADINGTIME_8BIT: rst = value[2]; break; case SHADOWTIME_8BIT: rst = value[3]; break; } } return rst; } static int srt_sec_get(int file, int section, int option, int defvalue) { int rst = defvalue; switch(section) { case SCAN_PARAM: switch(RTS_Debug->dev_model) { case HPG2710: case HP3800: rst = srt_hp3800_scanparam_get(option, defvalue); break; case HPG3010: case HPG3110: case HP4370: rst = srt_hp4370_scanparam_get(file, option, defvalue); break; default : rst = srt_hp3970_scanparam_get(file, option, defvalue); break; } break; case SCAN_CALI: rst = srt_scancali_get(file, option, defvalue); break; case TRUE_GRAY_PARAM: rst = srt_truegrayparam_get(file, option, defvalue); break; case CALI_PARAM: rst = srt_caliparam_get(file, option, defvalue); break; case PLATFORM: switch(RTS_Debug->dev_model) { case HPG2710: case HP3800: rst = srt_hp3800_platform_get(option, defvalue); break; case UA4900: rst = srt_ua4900_platform_get(option, defvalue); break; case HPG3010: case HPG3110: case HP4370: rst = srt_hp4370_platform_get(option, defvalue); break; default : rst = srt_hp3970_platform_get(option, defvalue); break; } break; case SCANINFO: rst = srt_scaninfo_get(file, option, defvalue); break; } return rst; } static int get_value(int section, int option, int defvalue, int file) { int rst = defvalue; switch(file) { case FITCALIBRATE: rst = fitcalibrate_get(section, option, defvalue); break; case S_RTINIFILE: case S_USB1INIFILE: case T_RTINIFILE: case T_USB1INIFILE: rst = srt_sec_get(file, section, option, defvalue); break; } return rst; } backends-1.3.0/backend/hp3900_debug.c000066400000000000000000001741051456256263500171370ustar00rootroot00000000000000/* HP Scanjet 3900 series - Debugging functions for standalone Copyright (C) 2005-2008 Jonathan Bravo Lopez 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, see . 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. */ /* debugging level messages */ #define DBG_ERR 0x00 /* Only important errors */ #define DBG_VRB 0x01 /* verbose messages */ #define DBG_FNC 0x02 /* Function names and parameters */ #define DBG_CTL 0x03 /* USB Ctl data */ #define DBG_BLK 0x04 /* USB Bulk data */ #include #ifdef HAVE_TIFFIO_H #include /* dbg_tiff_save */ #endif /* headers */ static void dump_shading (struct st_calibration *myCalib); static char *dbg_scantype (SANE_Int type); static void dbg_scanmodes (struct st_device *dev); static void dbg_motorcurves (struct st_device *dev); static void dbg_motormoves (struct st_device *dev); static void dbg_hwdcfg (struct st_hwdconfig *params); static void dbg_ScanParams (struct st_scanparams *params); static void dbg_calibtable (struct st_gain_offset *params); static char *dbg_colour (SANE_Int colour); static void dbg_motorcfg (struct st_motorcfg *motorcfg); static void dbg_buttons (struct st_buttons *buttons); static void dbg_sensor (struct st_sensorcfg *sensor); static void dbg_timing (struct st_timing *mt); static void dbg_sensorclock (struct st_cph *cph); static void dbg_tiff_save (char *sFile, SANE_Int width, SANE_Int height, SANE_Int depth, SANE_Int colortype, SANE_Int res_x, SANE_Int res_y, SANE_Byte * buffer, SANE_Int size); static void dbg_autoref (struct st_scanparams *scancfg, SANE_Byte * pattern, SANE_Int ser1, SANE_Int ser2, SANE_Int ler); #ifdef developing static void dbg_buffer (SANE_Int level, char *title, SANE_Byte * buffer, SANE_Int size, SANE_Int start); static void dbg_registers (SANE_Byte * buffer); #endif #ifdef STANDALONE /* implementation */ int DBG_LEVEL = 0; static void DBG (int level, const char *msg, ...) { va_list ap; va_start (ap, msg); if (level <= DBG_LEVEL) vfprintf (stderr, msg, ap); va_end (ap); } #endif /* debugging functions */ static void dump_shading (struct st_calibration *myCalib) { if (myCalib != NULL) { SANE_Int colour, a; FILE *shadingfile[3]; shadingfile[0] = fopen ("RShading.txt", "w"); shadingfile[1] = fopen ("GShading.txt", "w"); shadingfile[2] = fopen ("BShading.txt", "w"); for (colour = 0; colour < 3; colour++) { if (shadingfile[colour] != NULL) { for (a = 0; a < myCalib->shadinglength; a++) fprintf (shadingfile[colour], "%04i: %04x %04x\n", a, (unsigned int) myCalib->white_shading[colour][a], (unsigned int) myCalib->black_shading[colour][a]); fclose (shadingfile[colour]); } } } } static char * dbg_scantype (SANE_Int type) { switch (type) { case ST_NORMAL: return "ST_NORMAL"; break; case ST_TA: return "ST_TA"; break; case ST_NEG: return "ST_NEG"; break; default: return "Unknown"; break; } } static void dbg_sensorclock (struct st_cph *cph) { if (cph != NULL) { DBG (DBG_FNC, " -> cph->p1 = %f\n", cph->p1); DBG (DBG_FNC, " -> cph->p2 = %f\n", cph->p2); DBG (DBG_FNC, " -> cph->ps = %i\n", cph->ps); DBG (DBG_FNC, " -> cph->ge = %i\n", cph->ge); DBG (DBG_FNC, " -> cph->go = %i\n", cph->go); } else DBG (DBG_FNC, " -> cph is NULL\n"); } static void dbg_timing (struct st_timing *mt) { if (mt != NULL) { DBG (DBG_FNC, " -> mt->cdss[0] = %i\n", _B0 (mt->cdss[0])); DBG (DBG_FNC, " -> mt->cdsc[0] = %i\n", _B0 (mt->cdsc[0])); DBG (DBG_FNC, " -> mt->cdss[1] = %i\n", _B0 (mt->cdss[1])); DBG (DBG_FNC, " -> mt->cdsc[1] = %i\n", _B0 (mt->cdsc[1])); DBG (DBG_FNC, " -> mt->cnpp = %i\n", _B0 (mt->cnpp)); DBG (DBG_FNC, " -> mt->cvtrp0 = %i\n", _B0 (mt->cvtrp[0])); DBG (DBG_FNC, " -> mt->cvtrp1 = %i\n", _B0 (mt->cvtrp[1])); DBG (DBG_FNC, " -> mt->cvtrp2 = %i\n", _B0 (mt->cvtrp[2])); DBG (DBG_FNC, " -> mt->cvtrfpw = %i\n", _B0 (mt->cvtrfpw)); DBG (DBG_FNC, " -> mt->cvtrbpw = %i\n", _B0 (mt->cvtrbpw)); DBG (DBG_FNC, " -> mt->cvtrw = %i\n", _B0 (mt->cvtrw)); DBG (DBG_FNC, " -> mt->clamps = 0x%08x\n", mt->clamps); DBG (DBG_FNC, " -> mt->clampe = 0x%08x\n", mt->clampe); DBG (DBG_FNC, " -> mt->adcclkp0 = %f\n", mt->adcclkp[0]); DBG (DBG_FNC, " -> mt->adcclkp1 = %f\n", mt->adcclkp[1]); DBG (DBG_FNC, " -> mt->adcclkp2e = %i\n", mt->adcclkp2e); DBG (DBG_FNC, " -> mt->cphbp2s = %i\n", mt->cphbp2s); DBG (DBG_FNC, " -> mt->cphbp2e = %i\n", mt->cphbp2e); } else DBG (DBG_FNC, " -> mt is NULL\n"); } static void dbg_sensor (struct st_sensorcfg *sensor) { if (sensor != NULL) { DBG (DBG_FNC, " -> type, name, res , {chn_color }, {chn_gray}, {rgb_order }, line_dist, evnodd_dist\n"); DBG (DBG_FNC, " -> ----, ----, --- , {--, --, --}, {--, -- }, {--, --, --}, ---------, -----------\n"); DBG (DBG_FNC, " -> %4i, %4i, %4i, {%2i, %2i, %2i}, {%2i, %2i }, {%2i, %2i, %2i}, %9i, %11i\n", sensor->type, sensor->name, sensor->resolution, sensor->channel_color[0], sensor->channel_color[1], sensor->channel_color[2], sensor->channel_gray[0], sensor->channel_gray[1], sensor->rgb_order[0], sensor->rgb_order[1], sensor->rgb_order[2], sensor->line_distance, sensor->evenodd_distance); } else DBG (DBG_FNC, " -> sensor is NULL\n"); } static void dbg_buttons (struct st_buttons *buttons) { if (buttons != NULL) { DBG (DBG_FNC, " -> count, btn1, btn2, btn3, btn4, btn5, btn6\n"); DBG (DBG_FNC, " -> -----, ----, ----, ----, ----, ----, ----\n"); DBG (DBG_FNC, " -> %5i, %4i, %4i, %4i, %4i, %4i, %4i\n", buttons->count, buttons->mask[0], buttons->mask[1], buttons->mask[2], buttons->mask[3], buttons->mask[4], buttons->mask[5]); } else DBG (DBG_FNC, " -> buttons is NULL\n"); } static void dbg_scanmodes (struct st_device *dev) { if (dev->scanmodes_count > 0) { SANE_Int a; struct st_scanmode *reg; DBG (DBG_FNC, " -> ##, ST , CM , RES , TM, CV, SR, CLK, CTPC , BKS , STT, DML, { Exposure times }, { Max exposure times }, MP , MExp16, MExpF, MExp, MRI, MSI, MMTIR, MMTIRH, SK\n"); DBG (DBG_FNC, " -> --, ---------, ----------, --- , --, --, --, ---, ------, ----, ---, ---, {------ ------ ------}, {------ ------ ------}, ---, ------, -----, ----, ---, ---, -----, ------, --\n"); for (a = 0; a < dev->scanmodes_count; a++) { reg = dev->scanmodes[a]; if (reg != NULL) { DBG (DBG_FNC, " -> %2i, %9s, %10s, %4i, %2i, %2i, %2i, %3i, %6i, %4i, %3i, %3i, {%6i, %6i, %6i}, {%6i, %6i, %6i}, %3i, %6i, %5i, %4i, %3i, %3i, %5i, %6i, %2i\n", a, dbg_scantype (reg->scantype), dbg_colour (reg->colormode), reg->resolution, reg->timing, reg->motorcurve, reg->samplerate, reg->systemclock, reg->ctpc, reg->motorbackstep, reg->scanmotorsteptype, reg->dummyline, reg->expt[0], reg->expt[1], reg->expt[2], reg->mexpt[0], reg->mexpt[1], reg->mexpt[2], reg->motorplus, reg->multiexposurefor16bitmode, reg->multiexposureforfullspeed, reg->multiexposure, reg->mri, reg->msi, reg->mmtir, reg->mmtirh, reg->skiplinecount); } } } } static void dbg_motorcurves (struct st_device *dev) { if (dev->mtrsetting != NULL) { struct st_motorcurve *mtc; SANE_Int a = 0; while (a < dev->mtrsetting_count) { DBG (DBG_FNC, " -> Motorcurve %2i: ", a); mtc = dev->mtrsetting[a]; if (mtc != NULL) { DBG (DBG_FNC, "mri=%i msi=%i skip=%i bckstp=%i\n", mtc->mri, mtc->msi, mtc->skiplinecount, mtc->motorbackstep); if (mtc->curve_count > 0) { char *sdata = (char *) malloc (256); if (sdata != NULL) { char *sline = (char *) malloc (256); if (sline != NULL) { SANE_Int count; struct st_curve *crv; DBG (DBG_FNC, " -> ##, dir, type , count, from, to , steps\n"); DBG (DBG_FNC, " -> --, ---, ----------, -----, ----, ----, -----\n"); count = 0; while (count < mtc->curve_count) { memset (sline, 0, 256); snprintf (sdata, 256, " -> %02i, ", count); strcat (sline, sdata); crv = mtc->curve[count]; if (crv != NULL) { if (crv->crv_speed == ACC_CURVE) strcat (sline, "ACC, "); else strcat (sline, "DEC, "); switch (crv->crv_type) { case CRV_NORMALSCAN: strcat (sline, "NORMALSCAN, "); break; case CRV_PARKHOME: strcat (sline, "PARKHOME , "); break; case CRV_SMEARING: strcat (sline, "SMEARING , "); break; case CRV_BUFFERFULL: strcat (sline, "BUFFERFULL, "); break; default: snprintf (sdata, 256, "unknown %2i, ", crv->crv_type); strcat (sline, sdata); break; } snprintf (sdata, 256, "%5i, ", crv->step_count); strcat (sline, sdata); if (crv->step_count > 0) { SANE_Int stpcount = 0; snprintf (sdata, 256, "%4i, %4i| ", crv->step[0], crv->step[crv->step_count - 1]); strcat (sline, sdata); while (stpcount < crv->step_count) { if (stpcount == 10) { strcat (sline, "..."); break; } if (stpcount > 0) strcat (sline, ", "); snprintf (sdata, 256, "%4i", crv->step[stpcount]); strcat (sline, sdata); stpcount++; } strcat (sline, "\n"); } else strcat (sline, "NONE\n"); } else strcat (sline, "NULL ...\n"); DBG (DBG_FNC, "%s", sline); count++; } free (sline); } free (sdata); } } } else DBG (DBG_FNC, "NULL\n"); a++; } } } static void dbg_motormoves (struct st_device *dev) { if (dev->motormove_count > 0) { SANE_Int a; struct st_motormove *reg; DBG (DBG_FNC, " -> ##, CLK, CTPC, STT, CV\n"); DBG (DBG_FNC, " -> --, ---, ----, ---, --\n"); for (a = 0; a < dev->motormove_count; a++) { reg = dev->motormove[a]; if (reg != NULL) { DBG (DBG_FNC, " -> %2i, %3i, %4i, %3i, %2i\n", a, reg->systemclock, reg->ctpc, reg->scanmotorsteptype, reg->motorcurve); } } } } static void dbg_hwdcfg (struct st_hwdconfig *params) { if (params != NULL) { DBG (DBG_FNC, " -> Low level config:\n"); DBG (DBG_FNC, " -> startpos = %i\n", params->startpos); DBG (DBG_FNC, " -> arrangeline = %s\n", (params->arrangeline == FIX_BY_SOFT) ? "FIX_BY_SOFT" : (params->arrangeline == FIX_BY_HARD) ? "FIX_BY_HARD" : "FIX_BY_NONE"); DBG (DBG_FNC, " -> scantype = %s\n", dbg_scantype (params->scantype)); DBG (DBG_FNC, " -> compression = %i\n", params->compression); DBG (DBG_FNC, " -> use_gamma_tables = %i\n", params->use_gamma_tables); DBG (DBG_FNC, " -> gamma_tablesize = %i\n", params->gamma_tablesize); DBG (DBG_FNC, " -> white_shading = %i\n", params->white_shading); DBG (DBG_FNC, " -> black_shading = %i\n", params->black_shading); DBG (DBG_FNC, " -> unk3 = %i\n", params->unk3); DBG (DBG_FNC, " -> motorplus = %i\n", params->motorplus); DBG (DBG_FNC, " -> static_head = %i\n", params->static_head); DBG (DBG_FNC, " -> motor_direction = %s\n", (params->motor_direction == MTR_FORWARD) ? "FORWARD" : "BACKWARD"); DBG (DBG_FNC, " -> dummy_scan = %i\n", params->dummy_scan); DBG (DBG_FNC, " -> highresolution = %i\n", params->highresolution); DBG (DBG_FNC, " -> sensorevenodddistance = %i\n", params->sensorevenodddistance); DBG (DBG_FNC, " -> calibrate = %i\n", params->calibrate); } } static void dbg_ScanParams (struct st_scanparams *params) { if (params != NULL) { DBG (DBG_FNC, " -> Scan params:\n"); DBG (DBG_FNC, " -> colormode = %s\n", dbg_colour (params->colormode)); DBG (DBG_FNC, " -> depth = %i\n", params->depth); DBG (DBG_FNC, " -> samplerate = %i\n", params->samplerate); DBG (DBG_FNC, " -> timing = %i\n", params->timing); DBG (DBG_FNC, " -> channel = %i\n", params->channel); DBG (DBG_FNC, " -> sensorresolution = %i\n", params->sensorresolution); DBG (DBG_FNC, " -> resolution_x = %i\n", params->resolution_x); DBG (DBG_FNC, " -> resolution_y = %i\n", params->resolution_y); DBG (DBG_FNC, " -> left = %i\n", params->coord.left); DBG (DBG_FNC, " -> width = %i\n", params->coord.width); DBG (DBG_FNC, " -> top = %i\n", params->coord.top); DBG (DBG_FNC, " -> height = %i\n", params->coord.height); DBG (DBG_FNC, " -> shadinglength = %i\n", params->shadinglength); DBG (DBG_FNC, " -> v157c = %i\n", params->v157c); DBG (DBG_FNC, " -> bytesperline = %i\n", params->bytesperline); DBG (DBG_FNC, " -> expt = %i\n", params->expt); DBG (DBG_FNC, " *> startpos = %i\n", params->startpos); DBG (DBG_FNC, " *> leftleading = %i\n", params->leftleading); DBG (DBG_FNC, " *> ser = %i\n", params->ser); DBG (DBG_FNC, " *> ler = %i\n", params->ler); DBG (DBG_FNC, " *> scantype = %s\n", dbg_scantype (params->scantype)); } } static void dbg_calibtable (struct st_gain_offset *params) { if (params != NULL) { DBG (DBG_FNC, " -> Calib table:\n"); DBG (DBG_FNC, " -> type R G B\n"); DBG (DBG_FNC, " -> ----- --- --- ---B\n"); DBG (DBG_FNC, " -> edcg1 = %3i , %3i , %3i\n", params->edcg1[0], params->edcg1[1], params->edcg1[2]); DBG (DBG_FNC, " -> edcg2 = %3i , %3i , %3i\n", params->edcg2[0], params->edcg2[1], params->edcg2[2]); DBG (DBG_FNC, " -> odcg1 = %3i , %3i , %3i\n", params->odcg1[0], params->odcg1[1], params->odcg1[2]); DBG (DBG_FNC, " -> odcg2 = %3i , %3i , %3i\n", params->odcg2[0], params->odcg2[1], params->odcg2[2]); DBG (DBG_FNC, " -> pag = %3i , %3i , %3i\n", params->pag[0], params->pag[1], params->pag[2]); DBG (DBG_FNC, " -> vgag1 = %3i , %3i , %3i\n", params->vgag1[0], params->vgag1[1], params->vgag1[2]); DBG (DBG_FNC, " -> vgag2 = %3i , %3i , %3i\n", params->vgag2[0], params->vgag2[1], params->vgag2[2]); } } static char * dbg_colour (SANE_Int colour) { switch (colour) { case CM_COLOR: return "CM_COLOR"; break; case CM_GRAY: return "CM_GRAY"; break; case CM_LINEART: return "CM_LINEART"; break; default: return "Unknown"; break; } } static void dbg_motorcfg (struct st_motorcfg *motorcfg) { if (motorcfg != NULL) { DBG (DBG_FNC, " -> type, res , freq, speed, base, high, park, change\n"); DBG (DBG_FNC, " -> ----, --- , ----, -----, ----, ----, ----, ------\n"); DBG (DBG_FNC, " -> %4i, %4i, %4i, %5i, %4i, %4i, %4i, %6i\n", motorcfg->type, motorcfg->resolution, motorcfg->pwmfrequency, motorcfg->basespeedpps, motorcfg->basespeedmotormove, motorcfg->highspeedmotormove, motorcfg->parkhomemotormove, motorcfg->changemotorcurrent); } } static void dbg_tiff_save (char *sFile, SANE_Int width, SANE_Int height, SANE_Int depth, SANE_Int colortype, SANE_Int res_x, SANE_Int res_y, SANE_Byte * buffer, SANE_Int size) { #ifdef HAVE_TIFFIO_H if (buffer != NULL) { char *path = getenv ("HOME"); if (path != NULL) { char filename[512]; TIFF *image; if (snprintf (filename, 512, "%s/%s", path, sFile) > 0) { /* Open the TIFF file */ if ((image = TIFFOpen (filename, "w")) != NULL) { char desc[256]; SANE_Int spp = (colortype == CM_GRAY) ? 1 : 3; SANE_Int ct = (colortype == CM_GRAY) ? PHOTOMETRIC_MINISBLACK : PHOTOMETRIC_RGB; snprintf (desc, 256, "Created with hp3900 %s", BACKEND_VRSN); /* We need to set some values for basic tags before we can add any data */ TIFFSetField (image, TIFFTAG_IMAGEWIDTH, width); TIFFSetField (image, TIFFTAG_IMAGELENGTH, height); TIFFSetField (image, TIFFTAG_BITSPERSAMPLE, depth); TIFFSetField (image, TIFFTAG_SAMPLESPERPIXEL, spp); TIFFSetField (image, TIFFTAG_PHOTOMETRIC, ct); TIFFSetField (image, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB); TIFFSetField (image, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField (image, TIFFTAG_XRESOLUTION, (double) res_x); TIFFSetField (image, TIFFTAG_YRESOLUTION, (double) res_y); TIFFSetField (image, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH); TIFFSetField (image, TIFFTAG_IMAGEDESCRIPTION, desc); /* Write the information to the file */ TIFFWriteRawStrip (image, 0, buffer, size); TIFFClose (image); } } else DBG (DBG_ERR, "- dbg_tiff_save: Error generating filename\n"); } else DBG (DBG_ERR, "- dbg_tiff_save: Environment HOME variable does not exist\n"); } #else /* silent gcc */ (void) sFile; (void) width; (void) height; (void) depth; (void) colortype; (void) res_x; (void) res_y; (void) buffer; (void) size; DBG (DBG_ERR, "- dbg_tiff_save: tiffio not supported\n"); #endif } static void dbg_autoref (struct st_scanparams *scancfg, SANE_Byte * pattern, SANE_Int ser1, SANE_Int ser2, SANE_Int ler) { /* this function generates post-autoref.tiff */ SANE_Byte *img = malloc (sizeof (SANE_Byte) * (scancfg->coord.width * scancfg->coord.height * 3)); if (img != NULL) { SANE_Int c, value; /* generate image from 1 gray channel to 3 color channels */ for (c = 0; c < (scancfg->coord.width * scancfg->coord.height); c++) { value = *(pattern + c); *(img + (3 * c)) = value; *(img + (3 * c) + 1) = value; *(img + (3 * c) + 2) = value; } for (c = 0; c < scancfg->coord.height; c++) { /* line for first SER */ if (c < (ler + 5)) { *(img + (scancfg->coord.width * c * 3) + (3 * ser1)) = 0; *(img + (scancfg->coord.width * c * 3) + (3 * ser1) + 1) = 255; *(img + (scancfg->coord.width * c * 3) + (3 * ser1) + 2) = 0; } /* line for second SER */ if (c > (ler - 5)) { *(img + (scancfg->coord.width * c * 3) + (3 * ser2)) = 90; *(img + (scancfg->coord.width * c * 3) + (3 * ser2) + 1) = 90; *(img + (scancfg->coord.width * c * 3) + (3 * ser2) + 2) = 255; } /* vertical lines of the pointer */ if ((c > (ler - 5)) && (c < (ler + 5))) { if ((ser2 - 5) >= 0) { *(img + (scancfg->coord.width * c * 3) + (3 * (ser2 - 5))) = 255; *(img + (scancfg->coord.width * c * 3) + (3 * (ser2 - 5)) + 1) = 255; *(img + (scancfg->coord.width * c * 3) + (3 * (ser2 - 5)) + 2) = 0; } if ((ser2 + 5) < scancfg->coord.width) { *(img + (scancfg->coord.width * c * 3) + (3 * (ser2 + 5))) = 255; *(img + (scancfg->coord.width * c * 3) + (3 * (ser2 + 5)) + 1) = 255; *(img + (scancfg->coord.width * c * 3) + (3 * (ser2 + 5)) + 2) = 0; } } } /* line for first LER */ for (c = 0; c < scancfg->coord.width; c++) { if ((c > (ser1 - 5)) && (c < (ser2 + 5))) { if (c != (ser2 - 5)) { *(img + (scancfg->coord.width * ler * 3) + (3 * c)) = 255; *(img + (scancfg->coord.width * ler * 3) + (3 * c) + 1) = 90; *(img + (scancfg->coord.width * ler * 3) + (3 * c) + 2) = 90; } /* horizontal lines of the pointer */ if ((c > (ser2 - 5)) && (c < (ser2 + 5))) { if ((ler - 5) >= 0) { *(img + (scancfg->coord.width * (ler - 5) * 3) + (3 * c)) = 255; *(img + (scancfg->coord.width * (ler - 5) * 3) + (3 * c) + 1) = 255; *(img + (scancfg->coord.width * (ler - 5) * 3) + (3 * c) + 2) = 0; } if ((ler + 5) < scancfg->coord.height) { *(img + (scancfg->coord.width * (ler + 5) * 3) + (3 * c)) = 255; *(img + (scancfg->coord.width * (ler + 5) * 3) + (3 * c) + 1) = 255; *(img + (scancfg->coord.width * (ler + 5) * 3) + (3 * c) + 2) = 0; } } } } dbg_tiff_save ("post-autoref.tiff", scancfg->coord.width, scancfg->coord.height, 8, CM_COLOR, scancfg->resolution_x, scancfg->resolution_y, img, scancfg->coord.height * scancfg->coord.width * 3); /* free generated image */ free (img); } } #ifdef developing static void dbg_buffer (SANE_Int level, char *title, SANE_Byte * buffer, SANE_Int size, SANE_Int start) { if (level <= DBG_LEVEL) { DBG (level, "%s ", title); if ((size > 0) && (buffer != NULL)) { SANE_Int cont, data, offset = 0; SANE_Int col = 0; char text[9]; char *sline = NULL; char *sdata = NULL; sline = (char *) malloc (81); if (sline != NULL) { sdata = (char *) malloc (81); if (sdata != NULL) { for (cont = 0; cont < size; cont++) { if (col == 0) { if (cont == 0) snprintf (sline, 80, " BF: "); else snprintf (sline, 80, " "); memset (&text, 0, sizeof (text)); } data = _B0 (buffer[cont]); text[col] = (data > 31) ? data : '·'; snprintf (sdata, 80, "%02x ", data); sline = strcat (sline, sdata); col++; offset++; if (col == 8) { col = 0; snprintf (sdata, 80, " : %s : 0x%04x\n", text, start + offset - 8); sline = strcat (sline, sdata); DBG (level, "%s", sline); memset (sline, 0, 81); } } if (col > 0) { for (cont = col; cont < 8; cont++) { snprintf (sdata, 80, "-- "); sline = strcat (sline, sdata); offset++; } snprintf (sdata, 80, " : %s : 0x%04x\n", text, start + offset - 8); sline = strcat (sline, sdata); DBG (level, "%s", sline); memset (sline, 0, 81); } free (sdata); } free (sline); } } else DBG (level, " BF: Empty buffer\n"); } } static void dbg_registers (SANE_Byte * buffer) { /* buffer size must be RT_BUFFER_LEN bytes */ /*SANE_Int iValue, iValue2; double dValue; DBG(DBG_FNC, "\n----------------------------------------------------\n"); DBG(DBG_FNC, """RTS8822 Control Registers Info""\nAddress Info\n------- ----\n"); iValue = data_lsb_get(&buffer[0x000], 1); DBG(DBG_FNC, "\n0x0000"); DBG(DBG_FNC, " bit[0..3] = systemclock: 0x%02x\n", iValue & 0x0f); DBG(DBG_FNC, " bit[4] = 0x%02x : MLOCK\n", (iValue >> 4) & 1); DBG(DBG_FNC, " bit[5] = 0x%02x : Bit to reset scanner\n", (iValue >> 5) & 1); DBG(DBG_FNC, " bit[6] = 0x%02x : ?\n", (iValue >> 6) & 1); DBG(DBG_FNC, " bit[7] = 0x%02x : RTS_IsExecuting\n", (iValue >> 7) & 1); iValue = data_lsb_get(&buffer[0x001], 1); DBG(DBG_FNC, "0x0001 bit[0] = 0x%02x : ?\n", iValue & 1); DBG(DBG_FNC, " bit[1] = 0x%02x : (is 1 if has motorcurves)\n", (iValue >> 1) & 1); DBG(DBG_FNC, " bit[2] = 0x%02x : ?\n", (iValue >> 2) & 1); DBG(DBG_FNC, " bit[3] = 0x%02x : ?\n", (iValue >> 3) & 1); DBG(DBG_FNC, " bit[4] = 0x%02x : dummy scan\n", (iValue >> 4) & 1); DBG(DBG_FNC, " bit[5..7] = 0x%02x : ?\n", (iValue >> 5) & 7); dbg_buffer(DBG_FNC, "\n0x0002", &buffer[0x002], 0x0e, 0x02); iValue = data_lsb_get(&buffer[0x010], 1); DBG(DBG_FNC, "\n0x0010 bit[0..4] = 0x%02x : cvrs\n", iValue & 0x1f); DBG(DBG_FNC, " bit[5] = 0x%02x : Enable CCD\n", ((iValue >> 5) & 1)); DBG(DBG_FNC, " bit[6] = 0x%02x : Enable CCD channel 1\n", ((iValue >> 6) & 1)); DBG(DBG_FNC, " bit[7] = 0x%02x : Enable CCD channel 2\n", ((iValue >> 7) & 1)); iValue = data_lsb_get(&buffer[0x011], 1); DBG(DBG_FNC, "\n0x0011 bit[0..6] = ?: 0x%02x\n", iValue & 0x3f); DBG(DBG_FNC, " bit[7] = 0x%02x : sensor type (CCD=0|CIS=1)\n", (iValue >> 7) & 1); iValue = data_lsb_get(&buffer[0x012], 1); DBG(DBG_FNC, "0x0012 bit[0..5] = 0x%02x [0x%02x,0x%02x,0x%02x] rgb channel order\n", (iValue & 0x3f), (iValue >> 4) & 3, (iValue >> 2) & 3, iValue & 3); DBG(DBG_FNC, " bit[6..7] = channels_per_dot : 0x%02x\n", (iValue >> 6) & 3); iValue = data_lsb_get(&buffer[0x013], 1); DBG(DBG_FNC, "\n0x0013"); DBG(DBG_FNC, " bit[0..1] = Pre-Amplifier Gain[RED] : 0x%02x\n", iValue & 3); DBG(DBG_FNC, " bit[2..3] = Pre-Amplifier Gain[GREEN] : 0x%02x\n", (iValue >> 2) & 3); DBG(DBG_FNC, " bit[4..5] = Pre-Amplifier Gain[BLUE] : 0x%02x\n", (iValue >> 4) & 3); DBG(DBG_FNC, " bit[6] = ? : 0x%02x\n", (iValue >> 6) & 1); DBG(DBG_FNC, " bit[7] = Enable CCD channel 3: : 0x%02x\n", (iValue >> 7) & 1); iValue = data_lsb_get(&buffer[0x014], 1); DBG(DBG_FNC, "\n0x0014"); DBG(DBG_FNC, " bit[0..4] = Variable Gain Amplifier 1 [RED] : 0x%02x\n", iValue & 0x1f); DBG(DBG_FNC, " bit[5..7] = Top Reference Voltage: 0x%02x\n", (iValue >> 5) & 3); iValue = data_lsb_get(&buffer[0x015], 1); DBG(DBG_FNC, "0x0015"); DBG(DBG_FNC, " bit[0..4] = Variable Gain Amplifier 1 [GREEN] : 0x%02x\n", iValue & 0x1f); DBG(DBG_FNC, " bit[5..7] = Middle Reference Voltage: 0x%02x\n", (iValue >> 5) & 3); iValue = data_lsb_get(&buffer[0x016], 1); DBG(DBG_FNC, "0x0016"); DBG(DBG_FNC, " bit[0..4] = Variable Gain Amplifier 1 [BLUE] : 0x%02x\n", iValue & 0x1f); DBG(DBG_FNC, " bit[5..7] = Bottom Reference Voltage: 0x%02x\n", (iValue >> 5) & 3); iValue = data_lsb_get(&buffer[0x017], 1); DBG(DBG_FNC, "0x0017"); DBG(DBG_FNC, " bit[0..4] = Variable Gain Amplifier 2 [RED] : 0x%02x\n", iValue & 0x1f); DBG(DBG_FNC, " bit[5..7] = Top Reference Voltage: 0x%02x\n", (iValue >> 5) & 3); iValue = data_lsb_get(&buffer[0x018], 1); DBG(DBG_FNC, "0x0018"); DBG(DBG_FNC, " bit[0..4] = Variable Gain Amplifier 2 [GREEN] : 0x%02x\n", iValue & 0x1f); DBG(DBG_FNC, " bit[5..7] = Middle Reference Voltage: 0x%02x\n", (iValue >> 5) & 3); iValue = data_lsb_get(&buffer[0x019], 1); DBG(DBG_FNC, "0x0019"); DBG(DBG_FNC, " bit[0..4] = Variable Gain Amplifier 2 [BLUE] : 0x%02x\n", iValue & 0x1f); DBG(DBG_FNC, " bit[5..7] = Bottom Reference Voltage: 0x%02x\n", (iValue >> 5) & 3); iValue = data_lsb_get(&buffer[0x01a], 1); iValue2 = data_lsb_get(&buffer[0x01b], 1); DBG(DBG_FNC, "\n0x001a-0x001b\n"); DBG(DBG_FNC, " Red Even offset 1: 0x%02x\n", ((iValue2 & 0x80) << 1) | iValue); DBG(DBG_FNC, " Red Even offset 2: 0x%02x\n", iValue2 & 0x3f); iValue = data_lsb_get(&buffer[0x01c], 1); iValue2 = data_lsb_get(&buffer[0x01d], 1); DBG(DBG_FNC, "0x001c-0x001d\n"); DBG(DBG_FNC, " Red Odd offset 1: 0x%02x\n", ((iValue2 & 0x80) << 1) | iValue); DBG(DBG_FNC, " Red Odd offset 2: 0x%02x\n", iValue2 & 0x3f); iValue = data_lsb_get(&buffer[0x01e], 1); iValue2 = data_lsb_get(&buffer[0x01f], 1); DBG(DBG_FNC, "0x001e-0x001f\n"); DBG(DBG_FNC, " Green Even offset 1: 0x%02x\n", ((iValue2 & 0x80) << 1) | iValue); DBG(DBG_FNC, " Green Even offset 2: 0x%02x\n", iValue2 & 0x3f); iValue = data_lsb_get(&buffer[0x020], 1); iValue2 = data_lsb_get(&buffer[0x021], 1); DBG(DBG_FNC, "0x0020-0x0021\n"); DBG(DBG_FNC, " Green Odd offset 1: 0x%02x\n", ((iValue2 & 0x80) << 1) | iValue); DBG(DBG_FNC, " Green Odd offset 2: 0x%02x\n", iValue2 & 0x3f); iValue = data_lsb_get(&buffer[0x022], 1); iValue2 = data_lsb_get(&buffer[0x023], 1); DBG(DBG_FNC, "0x0022-0x0023\n"); DBG(DBG_FNC, " Blue Even offset 1: 0x%02x\n", ((iValue2 & 0x80) << 1) | iValue); DBG(DBG_FNC, " Blue Even offset 2: 0x%02x\n", iValue2 & 0x3f); iValue = data_lsb_get(&buffer[0x024], 1); iValue2 = data_lsb_get(&buffer[0x025], 1); DBG(DBG_FNC, "0x0024-0x0025\n"); DBG(DBG_FNC, " Blue Odd offset 1: 0x%02x\n", ((iValue2 & 0x80) << 1) | iValue); DBG(DBG_FNC, " Blue Odd offset 2: 0x%02x\n", iValue2 & 0x3f); dbg_buffer(DBG_FNC, "\n0x0026", &buffer[0x026], 0x03, 0x26); iValue = data_lsb_get(&buffer[0x029], 1); DBG(DBG_FNC, "\n0x0029"); DBG(DBG_FNC, " First connection to scanner? : 0x%02x\n", iValue); dbg_buffer(DBG_FNC, "\n0x002a", &buffer[0x02a], 0x06, 0x2a); DBG(DBG_FNC, "\nExposure times:\n"); iValue = data_lsb_get(&buffer[0x030], 3); DBG(DBG_FNC, "0x0030 Line exposure time : %i us\n", iValue); iValue = data_lsb_get(&buffer[0x033], 3); DBG(DBG_FNC, "\n0x0033 mexpts[RED] : %i us\n", iValue); iValue = data_lsb_get(&buffer[0x036], 3); DBG(DBG_FNC, "0x0036 expts[RED] : %i us\n", iValue); iValue = data_lsb_get(&buffer[0x039], 3); DBG(DBG_FNC, "0x0039 mexpts[GREEN]: %i us\n", iValue); iValue = data_lsb_get(&buffer[0x03c], 3); DBG(DBG_FNC, "0x003c expts[GREEN]: %i us\n", iValue); iValue = data_lsb_get(&buffer[0x03f], 3); DBG(DBG_FNC, "0x003f mexpts[BLUE] : %i us\n", iValue); iValue = data_lsb_get(&buffer[0x042], 3); DBG(DBG_FNC, "0x0042 expts[BLUE] : %i us\n", iValue); iValue = data_lsb_get(&buffer[0x045], 1); DBG(DBG_FNC, "\n0x0045 bit[0..4] = timing.cvtrfpw: 0x%02x\n", iValue & 0x1f); DBG(DBG_FNC, " bit[5] = timing.cvtrp[2]: 0x%02x\n", (iValue >> 5) & 1); DBG(DBG_FNC, " bit[6] = timing.cvtrp[1]: 0x%02x\n", (iValue >> 6) & 1); DBG(DBG_FNC, " bit[7] = timing.cvtrp[0]: 0x%02x\n", (iValue >> 7) & 1); iValue = data_lsb_get(&buffer[0x046], 1); DBG(DBG_FNC, "0x0046"); DBG(DBG_FNC, " bit[0..4] = timing.cvtrbpw: 0x%02x\n", iValue & 0x1f); DBG(DBG_FNC, " bit[5..7] = ?: 0x%02x\n", (iValue >> 5) & 3); iValue = data_lsb_get(&buffer[0x047], 1); DBG(DBG_FNC, "0x0047"); DBG(DBG_FNC, " timing.cvtrw: 0x%02x\n", iValue); iValue = data_lsb_get(&buffer[0x04c], 0x01) & 0x0f; dValue = iValue * pow(2, 32); iValue = data_lsb_get(&buffer[0x04a], 0x02); dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x048], 0x02); DBG(DBG_FNC, "\n0x0048 Linear image sensor clock 1\n"); DBG(DBG_FNC, " bit[0..35] = timing.cph0p1: %.0f.\n", dValue); iValue = data_lsb_get(&buffer[0x04c], 0x01); DBG(DBG_FNC, " bit[36] = timing.cph0go: 0x%02x\n", (iValue >> 4) & 1); DBG(DBG_FNC, " bit[37] = timing.cph0ge: 0x%02x\n", (iValue >> 5) & 1); DBG(DBG_FNC, " bit[38] = timing.cph0ps: 0x%02x\n", (iValue >> 6) & 1); DBG(DBG_FNC, " bit[39] = ?: 0x%02x\n", (iValue >> 7) & 1); iValue = data_lsb_get(&buffer[0x051], 0x01) & 0x0f; dValue = iValue * pow(2, 32); iValue = data_lsb_get(&buffer[0x04f], 0x02); dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x04d], 0x02); DBG(DBG_FNC, "0x004d"); DBG(DBG_FNC, " bit[0..35] = timing.cph0p2: %.0f.\n", dValue); iValue = data_lsb_get(&buffer[0x056], 1) & 0x0f; dValue = iValue * pow(2, 32); iValue = data_lsb_get(&buffer[0x054], 2); dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x052], 2); DBG(DBG_FNC, "\n0x0052 Linear image sensor clock 2\n"); DBG(DBG_FNC, " bit[0..35] = timing.cph1p1: %.0f.\n", dValue); iValue = data_lsb_get(&buffer[0x056], 1); DBG(DBG_FNC, " bit[36] = timing.cph1go: 0x%02x\n", (iValue >> 4) & 1); DBG(DBG_FNC, " bit[37] = timing.cph1ge: 0x%02x\n", (iValue >> 5) & 1); DBG(DBG_FNC, " bit[38] = timing.cph1ps: 0x%02x\n", (iValue >> 6) & 1); DBG(DBG_FNC, " bit[39] = ?: 0x%02x\n", (iValue >> 7) & 1); iValue = data_lsb_get(&buffer[0x05b], 0x01) & 0x0f; dValue = iValue * pow(2, 32); iValue = data_lsb_get(&buffer[0x059], 0x02); dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x057], 0x02); DBG(DBG_FNC, "0x0057"); DBG(DBG_FNC, " bit[0..35] = timing.cph1p2: %.0f.\n", dValue); iValue = data_lsb_get(&buffer[0x05b], 0x01); DBG(DBG_FNC, " bits[36..39] = %02x\n", (iValue >> 0x04) & 0x0f); DBG(DBG_FNC, " bit[36] = ?: %02x\n", (iValue >> 0x04) & 0x01); DBG(DBG_FNC, " bit[37] = ?: %02x\n", (iValue >> 0x05) & 0x01); DBG(DBG_FNC, " bit[38] = ?: %02x\n", (iValue >> 0x06) & 0x01); DBG(DBG_FNC, " bit[39] = ?: %02x\n", (iValue >> 0x07) & 0x01); iValue = data_lsb_get(&buffer[0x060], 0x01) & 0x0f; dValue = iValue * pow(2, 32); iValue = data_lsb_get(&buffer[0x05e], 0x02); dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x05c], 0x02); DBG(DBG_FNC, "\n0x005c Linear Image Sensor Clock 3\n"); DBG(DBG_FNC, " bit[0..35] = timing.cph2p1: %.0f.\n", dValue); iValue = data_lsb_get(&buffer[0x060], 0x01); DBG(DBG_FNC, " bit[36] = timing.cph2go: 0x%02x\n", (iValue >> 0x04) & 0x01); DBG(DBG_FNC, " bit[37] = timing.cph2ge: 0x%02x\n", (iValue >> 0x05) & 0x01); DBG(DBG_FNC, " bit[38] = timing.cph2ps: 0x%02x\n", (iValue >> 0x06) & 0x01); DBG(DBG_FNC, " bit[39] = ?: 0x%02x\n", (iValue >> 0x07) & 0x01); iValue = data_lsb_get(&buffer[0x065], 0x01) & 0x0f; dValue = iValue * pow(2, 32); iValue = data_lsb_get(&buffer[0x063], 0x02); dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x061], 0x02); DBG(DBG_FNC, "0x0061"); DBG(DBG_FNC, " bit[0..35] = timing.cph2p2: %.0f.\n", dValue); iValue = data_lsb_get(&buffer[0x065], 0x01); DBG(DBG_FNC, " bits[36..39] = 0x%02x\n", (iValue >> 0x04) & 0x0f); DBG(DBG_FNC, " bit[36] = ?: 0x%02x\n", (iValue >> 0x04) & 0x01); DBG(DBG_FNC, " bit[37] = ?: 0x%02x\n", (iValue >> 0x05) & 0x01); DBG(DBG_FNC, " bit[38] = ?: 0x%02x\n", (iValue >> 0x06) & 0x01); DBG(DBG_FNC, " bit[39] = ?: 0x%02x\n", (iValue >> 0x07) & 0x01); iValue = data_lsb_get(&buffer[0x06a], 0x01) & 0x0f; dValue = iValue * pow(2, 32); iValue = data_lsb_get(&buffer[0x068], 0x02); dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x066], 0x02); DBG(DBG_FNC, "\n0x0066 Linear Image Sensor Clock 4\n"); DBG(DBG_FNC, " bit[0..35] = timing.cph3p1: %.0f.\n", dValue); iValue = data_lsb_get(&buffer[0x06a], 0x01); DBG(DBG_FNC, " bit[36] = timing.cph3go: 0x%02x\n", (iValue >> 0x04) & 0x01); DBG(DBG_FNC, " bit[37] = timing.cph3ge: 0x%02x\n", (iValue >> 0x05) & 0x01); DBG(DBG_FNC, " bit[38] = timing.cph3ps: 0x%02x\n", (iValue >> 0x06) & 0x01); DBG(DBG_FNC, " bit[39] = ?: 0x%02x\n", (iValue >> 0x07) & 0x01); iValue = data_lsb_get(&buffer[0x06f], 0x01) & 0x0f; dValue = iValue * pow(2, 32); iValue = data_lsb_get(&buffer[0x06d], 0x02); dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x06b], 0x02); DBG(DBG_FNC, "0x006b"); DBG(DBG_FNC, " bit[0..35] = timing.cph3p2: %.0f.\n", dValue); iValue = data_lsb_get(&buffer[0x06f], 0x01); DBG(DBG_FNC, " bits[36..39] = 0x%02x\n", (iValue >> 0x04) & 0x0f); DBG(DBG_FNC, " bit[36] = ?: 0x%02x\n", (iValue >> 0x04) & 0x01); DBG(DBG_FNC, " bit[37] = ?: 0x%02x\n", (iValue >> 0x05) & 0x01); DBG(DBG_FNC, " bit[38] = ?: 0x%02x\n", (iValue >> 0x06) & 0x01); DBG(DBG_FNC, " bit[39] = ?: 0x%02x\n", (iValue >> 0x07) & 0x01); iValue = data_lsb_get(&buffer[0x074], 0x01) & 0x0f; dValue = iValue * pow(2, 32); iValue = data_lsb_get(&buffer[0x072], 0x02); dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x070], 0x02); DBG(DBG_FNC, "\n0x0070 Linear Image Sensor Clock 5\n"); DBG(DBG_FNC, " bit[0..35] = timing.cph4p1: %.0f.\n", dValue); iValue = data_lsb_get(&buffer[0x074], 0x01); DBG(DBG_FNC, " bit[36] = timing.cph4go: 0x%02x\n", (iValue >> 0x04) & 0x01); DBG(DBG_FNC, " bit[37] = timing.cph4ge: 0x%02x\n", (iValue >> 0x05) & 0x01); DBG(DBG_FNC, " bit[38] = timing.cph4ps: 0x%02x\n", (iValue >> 0x06) & 0x01); DBG(DBG_FNC, " bit[39] = ?: 0x%02x\n", (iValue >> 0x07) & 0x01); iValue = data_lsb_get(&buffer[0x079], 0x01) & 0x0f; dValue = iValue * pow(2, 32); iValue = data_lsb_get(&buffer[0x077], 0x02); dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x075], 0x02); DBG(DBG_FNC, "0x0075"); DBG(DBG_FNC, " bit[0..35] = timing.cph4p2: %.0f.\n", dValue); iValue = data_lsb_get(&buffer[0x079], 0x01); DBG(DBG_FNC, " bits[36..39] = 0x%02x\n", (iValue >> 0x04) & 0x0f); DBG(DBG_FNC, " bit[36] = ?: 0x%02x\n", (iValue >> 0x04) & 0x01); DBG(DBG_FNC, " bit[37] = ?: 0x%02x\n", (iValue >> 0x05) & 0x01); DBG(DBG_FNC, " bit[38] = ?: 0x%02x\n", (iValue >> 0x06) & 0x01); DBG(DBG_FNC, " bit[39] = ?: 0x%02x\n", (iValue >> 0x07) & 0x01); iValue = data_lsb_get(&buffer[0x07e], 0x01) & 0x0f; dValue = iValue * pow(2, 32); iValue = data_lsb_get(&buffer[0x07c], 0x02); dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x07a], 0x02); DBG(DBG_FNC, "\n0x007a Linear Image Sensor Clock 6\n"); DBG(DBG_FNC, " bit[0..35] = timing.cph5p1: %.0f.\n", dValue); iValue = data_lsb_get(&buffer[0x07e], 0x01); DBG(DBG_FNC, " bit[36] = timing.cph5go: 0x%02x\n", (iValue >> 0x04) & 0x01); DBG(DBG_FNC, " bit[37] = timing.cph5ge: 0x%02x\n", (iValue >> 0x05) & 0x01); DBG(DBG_FNC, " bit[38] = timing.cph5ps: 0x%02x\n", (iValue >> 0x06) & 0x01); DBG(DBG_FNC, " bit[39] = ?: 0x%02x\n", (iValue >> 0x07) & 0x01); iValue = data_lsb_get(&buffer[0x083], 0x01) & 0x0f; dValue = iValue * pow(2, 32); iValue = data_lsb_get(&buffer[0x081], 0x02); dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x07f], 0x02); DBG(DBG_FNC, "0x007f"); DBG(DBG_FNC, " bit[0..35] = timing.cph5p2: %.0f.\n", dValue); iValue = data_lsb_get(&buffer[0x083], 0x01); DBG(DBG_FNC, " bits[36..39] = 0x%02x\n", (iValue >> 0x04) & 0x0f); DBG(DBG_FNC, " bit[36] = ?: 0x%02x\n", (iValue >> 0x04) & 0x01); DBG(DBG_FNC, " bit[37] = ?: 0x%02x\n", (iValue >> 0x05) & 0x01); DBG(DBG_FNC, " bit[38] = ?: 0x%02x\n", (iValue >> 0x06) & 0x01); DBG(DBG_FNC, " bit[39] = ?: 0x%02x\n", (iValue >> 0x07) & 0x01); iValue = data_lsb_get(&buffer[0x084], 3); DBG(DBG_FNC, "\n0x0084"); DBG(DBG_FNC, " timing.cphbp2s : 0x%06x\n", iValue); iValue = data_lsb_get(&buffer[0x087], 3); DBG(DBG_FNC, "0x0087"); DBG(DBG_FNC, " timing.cphbp2e : 0x%06x\n", iValue); iValue = data_lsb_get(&buffer[0x08a], 3); DBG(DBG_FNC, "0x008a"); DBG(DBG_FNC, " timing.clamps : 0x%08x\n", iValue); iValue = data_lsb_get(&buffer[0x08d], 3); DBG(DBG_FNC, "0x008d"); DBG(DBG_FNC, " timing.clampe or cphbp2e : 0x%08x\n", iValue); iValue = data_lsb_get(&buffer[0x092], 0x01); DBG(DBG_FNC, "\n0x0092 Correlated-Double-Sample 1\n"); DBG(DBG_FNC, " bit[0..5] = timing.cdss[0]: 0x%02x\n", iValue & 0x3f); DBG(DBG_FNC, " bit[6..7] = ?: 0x%02x\n", (iValue >> 6) & 3); iValue = data_lsb_get(&buffer[0x093], 0x01); DBG(DBG_FNC, "0x0093"); DBG(DBG_FNC, " bit[0..5] = timing.cdsc[0]: 0x%02x\n", iValue & 0x3f); DBG(DBG_FNC, " bit[6..7] = ?: 0x%02x\n", (iValue >> 6) & 3); iValue = data_lsb_get(&buffer[0x094], 0x01); DBG(DBG_FNC, "\n0x0094 Correlated-Double-Sample 2\n"); DBG(DBG_FNC, " bit[0..5] = timing.cdss[1]: 0x%02x\n", iValue & 0x3f); DBG(DBG_FNC, " bit[6..7] = ?: 0x%02x\n", (iValue >> 6) & 3); iValue = data_lsb_get(&buffer[0x095], 0x01); DBG(DBG_FNC, "0x0095"); DBG(DBG_FNC, " bit[0..5] = timing.cdsc[1]: 0x%02x\n", iValue & 0x3f); DBG(DBG_FNC, " bit[6..7] = ?: 0x%02x\n", (iValue >> 6) & 3); iValue = data_lsb_get(&buffer[0x096], 0x01); DBG(DBG_FNC, "0x0096"); DBG(DBG_FNC, " bit[0..5] = timing.cnpp: 0x%02x\n", iValue & 0x3f); DBG(DBG_FNC, " bit[6..7] = ?: 0x%02x\n", (iValue >> 6) & 3); iValue = data_lsb_get(&buffer[0x09b], 0x01) & 0x0f; dValue = iValue * pow(2, 32); iValue = data_lsb_get(&buffer[0x099], 0x02); dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x097], 0x02); DBG(DBG_FNC, "\n0x0097 Analog to Digital Converter clock 1\n"); DBG(DBG_FNC, " bit[0..35] = timing.adcclkp[0]: %.0f.\n", dValue); iValue = data_lsb_get(&buffer[0x09b], 0x01); DBG(DBG_FNC, " bits[36..39] = 0x%02x\n", (iValue >> 0x04) & 0x0f); DBG(DBG_FNC, " bit[36] = ?: 0x%02x\n", (iValue >> 0x04) & 0x01); DBG(DBG_FNC, " bit[37] = ?: 0x%02x\n", (iValue >> 0x05) & 0x01); DBG(DBG_FNC, " bit[38] = ?: 0x%02x\n", (iValue >> 0x06) & 0x01); DBG(DBG_FNC, " bit[39] = ?: 0x%02x\n", (iValue >> 0x07) & 0x01); dbg_buffer(DBG_FNC, "\n0x009c CIS sensor 1", &buffer[0x09c], 0x06, 0x9c); dbg_buffer(DBG_FNC, "0x00a2 CIS sensor 2", &buffer[0x0a2], 0x06, 0xa2); dbg_buffer(DBG_FNC, "0x00a8 CIS sensor 3", &buffer[0x0a8], 0x06, 0xa8); iValue = data_lsb_get(&buffer[0x0ae], 0x01); DBG(DBG_FNC, "\n0x00ae"); DBG(DBG_FNC, " bit[0..5] = ?: 0x%02x\n", iValue & 0x3f); DBG(DBG_FNC, " bit[6..7] = ?: 0x%02x\n", (iValue >> 6) & 3); iValue = data_lsb_get(&buffer[0x0af], 0x01); DBG(DBG_FNC, "0x00af"); DBG(DBG_FNC, " bit[0..2] = ?: 0x%02x\n", iValue & 7); DBG(DBG_FNC, " bit[3..7] = ?: 0x%02x\n", (iValue >> 3) & 0x1f); iValue = data_lsb_get(&buffer[0x0b0], 2); DBG(DBG_FNC, "\n0x00b0"); DBG(DBG_FNC, " Left : 0x%04x\n", iValue); iValue = data_lsb_get(&buffer[0x0b2], 2); DBG(DBG_FNC, "0x00b2"); DBG(DBG_FNC, " Right: 0x%04x\n", iValue); dbg_buffer(DBG_FNC, "\n0x00b4", &buffer[0x0b4], 12, 0xb4); iValue = data_lsb_get(&buffer[0x0c0], 0x01); DBG(DBG_FNC, "\n0x00c0"); DBG(DBG_FNC, " bit[0..4] = resolution ratio: 0x%02x\n", iValue & 0x1f); DBG(DBG_FNC, " bit[5..7] = ?: 0x%02x\n", (iValue >> 5) & 7); iValue = data_lsb_get(&buffer[0x0c5], 0x01) & 0x0f; dValue = iValue * pow(2, 32); iValue = data_lsb_get(&buffer[0x0c3], 0x02); dValue = dValue + (iValue * pow(2, 16)) + data_lsb_get(&buffer[0x0c1], 2); DBG(DBG_FNC, "\n0x00c1 Analog to Digital Converter clock 2\n"); DBG(DBG_FNC, " bit[0..35] = timing.adcclkp[1]: %.0f.\n", dValue); iValue = data_lsb_get(&buffer[0x0c5], 0x01); DBG(DBG_FNC, " bits[36..39] = 0x%02x\n", (iValue >> 0x04) & 0x0f); DBG(DBG_FNC, " bit[36] = ?: 0x%02x (equal to bit[32])\n", (iValue >> 0x04) & 0x01); DBG(DBG_FNC, " bit[37] = ?: 0x%02x\n", (iValue >> 0x05) & 0x01); DBG(DBG_FNC, " bit[38] = ?: 0x%02x\n", (iValue >> 0x06) & 0x01); DBG(DBG_FNC, " bit[39] = ?: 0x%02x\n", (iValue >> 0x07) & 0x01); dbg_buffer(DBG_FNC, "\n0x00c6", &buffer[0x0c6], 0x0a, 0xc6); iValue = ((buffer[0x0d4] & 0x0f) << 0x10) + data_lsb_get(&buffer[0x0d0], 0x02); DBG(DBG_FNC, "\n0x00d0"); DBG(DBG_FNC, " Top : 0x%04x\n", iValue); iValue = ((buffer[0x0d4] & 0xf0) << 0x06)+ data_lsb_get(&buffer[0x0d2], 0x02); DBG(DBG_FNC, "x00d2"); DBG(DBG_FNC, " Down: 0x%04x\n", iValue); iValue = _B0(buffer[0x0d5]); DBG(DBG_FNC, "0x00d5"); DBG(DBG_FNC, " ?: 0x%04x\n", iValue); iValue = data_lsb_get(&buffer[0x0d6], 1); DBG(DBG_FNC, "\n0x00d6"); DBG(DBG_FNC, " bit[0..3] = ? : 0x%02x\n", iValue & 0xf); DBG(DBG_FNC, " bit[4..7] = dummyline: 0x%02x\n", (iValue >> 4) & 0xf); iValue = data_lsb_get(&buffer[0x0d7], 0x01); DBG(DBG_FNC, "\n0x00d7"); DBG(DBG_FNC, " bit[0..5] = motor pwm frequency: 0x%02x\n", iValue & 0x3f); DBG(DBG_FNC, " bit[6] = ?: 0x%02x\n", (iValue >> 6) & 1); DBG(DBG_FNC, " bit[7] = motor type: 0x%02x ", (iValue >> 7) & 1); if (((iValue >> 7) & 1) == MT_OUTPUTSTATE) DBG(DBG_FNC, ": Output state machine\n"); else DBG(DBG_FNC, "On-Chip PWM\n"); iValue = data_lsb_get(&buffer[0x0d8], 0x01); DBG(DBG_FNC, "\n0x00d8"); DBG(DBG_FNC, " bit[0..5] = ?: 0x%02x\n", iValue & 0x3f); DBG(DBG_FNC, " bit[6] = scantype (0=Normal|1=TMA) : 0x%02x\n", (iValue >> 6) & 1); DBG(DBG_FNC, " bit[7] = enable head movement : 0x%02x :", (iValue >> 7) & 1); iValue = data_lsb_get(&buffer[0x0d9], 0x01); DBG(DBG_FNC, "\n0x00d9"); DBG(DBG_FNC, " bit[0..2] = ?: 0x%02x\n", iValue & 7); DBG(DBG_FNC, " bit[3] = ?: 0x%02x\n", (iValue >> 3) & 1); DBG(DBG_FNC, " bit[4..6] = motor step type: 0x%02x: ", (iValue >> 4) & 7); switch((iValue >> 4) & 7) { case 0: DBG(DBG_FNC, "full (1)\n"); break; case 1: DBG(DBG_FNC, "half (1/2)\n"); break; case 2: DBG(DBG_FNC, "quart (1/4)\n"); break; case 3: DBG(DBG_FNC, "(1/8)\n"); break; default: DBG(DBG_FNC, "unknown\n"); break; } DBG(DBG_FNC, " bit[7] = Motor direction: 0x%02x = ", (iValue >> 7) & 1); if (((iValue >> 7) & 1) == 0) DBG(DBG_FNC, "Backward\n"); else DBG(DBG_FNC, "Forward\n"); iValue = data_lsb_get(&buffer[0x0dd], 0x01); DBG(DBG_FNC, "\n0x00da"); DBG(DBG_FNC, " msi = 0x%03x\n", ((iValue & 3) << 8) + data_lsb_get(&buffer[0x0da], 1)); DBG(DBG_FNC, "0x00db"); DBG(DBG_FNC, " motorbackstep1 = 0x%03x\n", ((iValue & 0x0c) << 6) + data_lsb_get(&buffer[0x0db], 1)); DBG(DBG_FNC, "0x00dc"); DBG(DBG_FNC, " motorbackstep2 = 0x%03x\n", ((iValue & 0x30) << 4) + data_lsb_get(&buffer[0x0dc], 1)); iValue = data_lsb_get(&buffer[0x0dd], 0x01); DBG(DBG_FNC, "0x00dd"); DBG(DBG_FNC, " bit[7] = Motor enabled?: 0x%02x = ", (iValue >> 7) & 1); if (((iValue >> 7) & 1) == 0) DBG(DBG_FNC, "Yes\n"); else DBG(DBG_FNC, "No\n"); iValue = data_lsb_get(&buffer[0x0de], 0x02); DBG(DBG_FNC, "\n0x00de"); DBG(DBG_FNC, " bit[00..11] = ?: 0x%02x\n", iValue & 0xfff); DBG(DBG_FNC, " bit[12..15] = ?: 0x%02x\n", (iValue >> 12) & 0x0f); iValue = data_lsb_get(&buffer[0x0df], 0x01); DBG(DBG_FNC, "\n0x00df"); DBG(DBG_FNC, " bit[0..3] = ?: 0x%02x\n", iValue & 0x0f); DBG(DBG_FNC, " bit[4] = has_motorcurves?: 0x%02x\n", (iValue >> 4) & 0x01); DBG(DBG_FNC, " bit[5..7] = ?: 0x%02x\n", (iValue >> 5) & 7); iValue = data_lsb_get(&buffer[0x0e0], 1); DBG(DBG_FNC, "\n0x00e0 step size - 1 : 0x%02x\n", iValue); iValue = data_lsb_get(&buffer[0x0e1], 3); DBG(DBG_FNC, "\n0x00e1 0x%06x : last step of accurve.normalscan table\n", iValue); iValue = data_lsb_get(&buffer[0x0e4], 3); DBG(DBG_FNC, "0x00e4 0x%06x : last step of accurve.smearing table\n", iValue); iValue = data_lsb_get(&buffer[0x0e7], 3); DBG(DBG_FNC, "0x00e7 0x%06x : last step of accurve.parkhome table\n", iValue); iValue = data_lsb_get(&buffer[0x0ea], 3); DBG(DBG_FNC, "0x00ea 0x%06x : last step of deccurve.scanbufferfull table\n", iValue); iValue = data_lsb_get(&buffer[0x0ed], 3); DBG(DBG_FNC, "0x00ed 0x%06x : last step of deccurve.normalscan table\n", iValue); iValue = data_lsb_get(&buffer[0x0f0], 3); DBG(DBG_FNC, "0x00f0 0x%06x : last step of deccurve.smearing table\n", iValue); iValue = data_lsb_get(&buffer[0x0f3], 3); DBG(DBG_FNC, "0x00f3 0x%06x : last step of deccurve.parkhome table\n", iValue); iValue = data_lsb_get(&buffer[0x0f6], 2); DBG(DBG_FNC, "\n0x00f6 bit[00..13] = 0x%04x : ptr to accurve.normalscan step table\n", iValue & 0x3fff); DBG(DBG_FNC, " bit[14..15] = 0x%04x : ?\n",(iValue >> 14) & 3); iValue = data_lsb_get(&buffer[0x0f8], 2); DBG(DBG_FNC, "0x00f8"); DBG(DBG_FNC, " bit[00..13] = 0x%04x : ptr to deccurve.scanbufferfull step table\n", iValue & 0x3fff); DBG(DBG_FNC, " bit[14..15] = 0x%04x : ?\n",(iValue >> 14) & 3); iValue = data_lsb_get(&buffer[0x0fa], 2); DBG(DBG_FNC, "0x00fa"); DBG(DBG_FNC, " bit[00..13] = 0x%04x : ptr to accurve.smearing step table\n", iValue & 0x3fff); DBG(DBG_FNC, " bit[14..15] = 0x%04x : ?\n",(iValue >> 14) & 3); iValue = data_lsb_get(&buffer[0x0fc], 2); DBG(DBG_FNC, "0x00fc"); DBG(DBG_FNC, " bit[00..13] = 0x%04x : ptr to deccurve.smearing step table\n", iValue & 0x3fff); DBG(DBG_FNC, " bit[14..15] = 0x%04x : ?\n",(iValue >> 14) & 3); iValue = data_lsb_get(&buffer[0x0fe], 2); DBG(DBG_FNC, "0x00fe"); DBG(DBG_FNC, " bit[00..13] = 0x%04x : ptr to deccurve.normalscan step table\n", iValue & 0x3fff); DBG(DBG_FNC, " bit[14..15] = 0x%04x : ?\n",(iValue >> 14) & 3); iValue = data_lsb_get(&buffer[0x100], 2); DBG(DBG_FNC, "0x0100"); DBG(DBG_FNC, " bit[00..13] = 0x%04x : ptr to accurve.parkhome step table\n", iValue & 0x3fff); DBG(DBG_FNC, " bit[14..15] = 0x%04x : ?\n",(iValue >> 14) & 3); iValue = data_lsb_get(&buffer[0x102], 2); DBG(DBG_FNC, "0x0102"); DBG(DBG_FNC, " bit[00..13] = 0x%04x : ptr to deccurve.parkhome step table\n", iValue & 0x3fff); DBG(DBG_FNC, " bit[14..15] = 0x%04x : ?\n",(iValue >> 14) & 3); dbg_buffer(DBG_FNC, "\n0x0104 Motor resource", &buffer[0x104], 0x20, 0x104); dbg_buffer(DBG_FNC, "\n0x0124", &buffer[0x124], 0x22, 0x124); iValue = data_lsb_get(&buffer[0x146], 1); DBG(DBG_FNC, "\n0x0146"); DBG(DBG_FNC, " bit[0..3] = Lamp pulse-width modulation frequency : 0x%02x\n", iValue & 0xf); DBG(DBG_FNC, " bit[4] = timer enabled? : 0x%02x\n", (iValue >> 4) & 1); DBG(DBG_FNC, " bit[5] = ? : 0x%02x\n", (iValue >> 5) & 1); DBG(DBG_FNC, " bit[6] = lamp turned on? : 0x%02x\n", (iValue >> 6) & 1); DBG(DBG_FNC, " bit[7] = sensor type : 0x%02x ", (iValue >> 7) & 1); if (((iValue >> 7) & 1) != 0) DBG(DBG_FNC, "CCD\n"); else DBG(DBG_FNC, "CIS\n"); iValue = data_lsb_get(&buffer[0x147], 1); DBG(DBG_FNC, "\n0x0147"); DBG(DBG_FNC, " time to turn off lamp = 0x%02x (minutes * 2.682163611980331)\n", iValue); iValue = data_lsb_get(&buffer[0x148], 1); DBG(DBG_FNC, "\n0x0148"); DBG(DBG_FNC, " bit[0..5] = Lamp pulse-width modulation duty cycle : 0x%02x\n", iValue & 0x3f); DBG(DBG_FNC, " bit[6..7] = ? : 0x%02x\n",(iValue >> 6) & 3); iValue = data_lsb_get(&buffer[0x149], 1); DBG(DBG_FNC, "\n0x0149"); DBG(DBG_FNC, " bit[0..5] = even_odd_distance : 0x%02x\n", iValue & 0x3f); DBG(DBG_FNC, " bit[6..7] = ? : 0x%02x\n",(iValue >> 6) & 3); iValue = data_lsb_get(&buffer[0x14a], 1); DBG(DBG_FNC, "0x014a"); DBG(DBG_FNC, " bit[0..5] = sensor line distance : 0x%02x\n", iValue & 0x3f); DBG(DBG_FNC, " bit[6..7] = ?: 0x%02x\n",(iValue >> 6) & 3); iValue = data_lsb_get(&buffer[0x14b], 1); DBG(DBG_FNC, "0x014b"); DBG(DBG_FNC, " bit[0..5] = sensor line distance + even_odd_distance: 0x%02x\n", iValue & 0x3f); DBG(DBG_FNC, " bit[6..7] = ?: 0x%02x\n",(iValue >> 6) & 3); iValue = data_lsb_get(&buffer[0x14c], 1); DBG(DBG_FNC, "0x014c"); DBG(DBG_FNC, " bit[0..5] = sensor line distance * 2: 0x%02x\n", iValue & 0x3f); DBG(DBG_FNC, " bit[6..7] = ?: 0x%02x\n", (iValue >> 6) & 3); iValue = data_lsb_get(&buffer[0x14d], 1); DBG(DBG_FNC, "0x014d"); DBG(DBG_FNC, " bit[0..5] = (sensor line distance * 2) + even_odd_distance: 0x%02x\n", iValue & 0x3f); DBG(DBG_FNC, " bit[6..7] = ?: 0x%02x\n", (iValue >> 6) & 3); iValue = data_lsb_get(&buffer[0x14e], 1); DBG(DBG_FNC, "\n0x014e"); DBG(DBG_FNC, " bit[0..3] = ?: 0x%02x\n", iValue & 0xf); DBG(DBG_FNC, " bit[4] = ?: 0x%02x\n", (iValue >> 4) & 1); DBG(DBG_FNC, " bit[5..7] = ?: 0x%02x\n", (iValue >> 5) & 7); dbg_buffer(DBG_FNC, "\n0x014f", &buffer[0x14f], 0x05, 0x14f); iValue = data_lsb_get(&buffer[0x154], 1); DBG(DBG_FNC, "\n0x0154"); DBG(DBG_FNC, " bit[0..3] = ?: 0x%02x\n", iValue & 0xf); DBG(DBG_FNC, " bit[4..5] = ?: 0x%02x\n", (iValue >> 4) & 3); DBG(DBG_FNC, " bit[6..7] = ?: 0x%02x\n", (iValue >> 6) & 7); iValue = data_lsb_get(&buffer[0x155], 1); DBG(DBG_FNC, "\n0x0155"); DBG(DBG_FNC, " bit[0..3] = ?: 0x%02x\n", iValue & 0x0f); DBG(DBG_FNC, " bit[4] = 0x%02x : ", (iValue >> 4) & 1); if (((iValue >> 4) & 1) == 0) DBG(DBG_FNC, "flb lamp\n"); else DBG(DBG_FNC, "tma lamp\n"); DBG(DBG_FNC, " bit[5..7] = ? : 0x%02x\n", (iValue >> 5) & 7); dbg_buffer(DBG_FNC, "\n0x0156", &buffer[0x156], 0x02, 0x156); iValue = data_lsb_get(&buffer[0x158], 1); DBG(DBG_FNC, "\n0x0158"); DBG(DBG_FNC, " bit[0..3] = %02x : Scanner buttons ", iValue & 0x0f); if ((iValue & 0x0f) == 0x0f) DBG(DBG_FNC, "enabled\n"); else DBG(DBG_FNC, "disabled\n"); DBG(DBG_FNC, " bit[4..7] = ? : 0x%02x\n", (iValue >> 4) & 0x0f); dbg_buffer(DBG_FNC, "\n0x0159", &buffer[0x159], 11, 0x159); iValue = data_lsb_get(&buffer[0x164], 1); DBG(DBG_FNC, "\n0x0164"); DBG(DBG_FNC, " bit[0..6] = ?: 0x%02x\n", iValue & 0x3f); DBG(DBG_FNC, " bit[7] = ? : 0x%02x\n", (iValue >> 7) & 1); dbg_buffer(DBG_FNC, "\n0x0165", &buffer[0x165], 3, 0x165); iValue = data_lsb_get(&buffer[0x168], 1); DBG(DBG_FNC, "\n0x0168 Buttons status : 0x%02x\n", iValue); DBG(DBG_FNC, " bit[0] = button 1 : 0x%02x\n", iValue & 1); DBG(DBG_FNC, " bit[1] = button 2 : 0x%02x\n", (iValue >> 1) & 1); DBG(DBG_FNC, " bit[2] = button 4 : 0x%02x\n", (iValue >> 2) & 1); DBG(DBG_FNC, " bit[3] = button 3 : 0x%02x\n", (iValue >> 3) & 1); DBG(DBG_FNC, " bit[4] = button ? : 0x%02x\n", (iValue >> 4) & 1); DBG(DBG_FNC, " bit[5] = button ? : 0x%02x\n", (iValue >> 5) & 1); DBG(DBG_FNC, " bit[6] = ? : 0x%02x\n", (iValue >> 6) & 1); DBG(DBG_FNC, " bit[7] = ? : 0x%02x\n", (iValue >> 7) & 1); iValue = data_lsb_get(&buffer[0x169], 1); DBG(DBG_FNC, "\n0x0169", iValue); DBG(DBG_FNC, " bit[0] = ? : 0x%02x\n", iValue & 1); DBG(DBG_FNC, " bit[1] = tma attached? : 0x%02x\n", (iValue >> 1) & 1); DBG(DBG_FNC, " bit[2..7] = ? : 0x%02x\n", (iValue >> 2) & 0x3f); iValue = data_lsb_get(&buffer[0x16a], 1); DBG(DBG_FNC, "\n0x016a Buttons status 2: 0x%02x\n", iValue); DBG(DBG_FNC, " bit[0] = button 1 : 0x%02x\n", iValue & 1); DBG(DBG_FNC, " bit[1] = button 2 : 0x%02x\n", (iValue >> 1) & 1); DBG(DBG_FNC, " bit[2] = button 4 : 0x%02x\n", (iValue >> 2) & 1); DBG(DBG_FNC, " bit[3] = button 3 : 0x%02x\n", (iValue >> 3) & 1); DBG(DBG_FNC, " bit[4] = button ? : 0x%02x\n", (iValue >> 4) & 1); DBG(DBG_FNC, " bit[5] = button ? : 0x%02x\n", (iValue >> 5) & 1); DBG(DBG_FNC, " bit[6] = ? : 0x%02x\n", (iValue >> 6) & 1); DBG(DBG_FNC, " bit[7] = ? : 0x%02x\n", (iValue >> 7) & 1); dbg_buffer(DBG_FNC, "\n0x016b", &buffer[0x16b], 4, 0x16b); iValue = data_lsb_get(&buffer[0x16f], 1); DBG(DBG_FNC, "\n0x016f"); DBG(DBG_FNC, " bit[0..5] = ? : 0x%02x\n", iValue & 0x3f); DBG(DBG_FNC, " bit[6] = is lamp at home? : 0x%02x\n", (iValue >> 6) & 1); DBG(DBG_FNC, " bit[7] = ?: %02x\n", (iValue >> 7) & 1); dbg_buffer(DBG_FNC, "\n0x0170", &buffer[0x170], 0x17, 0x170); iValue = data_lsb_get(&buffer[0x187], 1); DBG(DBG_FNC, "\n0x0187"); DBG(DBG_FNC, " bit[0..3] = ? : 0x%02x\n", iValue & 0xf); DBG(DBG_FNC, " bit[4..7] = mclkioc : 0x%02x\n", (iValue >> 4) & 0xf); dbg_buffer(DBG_FNC, "\n0x0188", &buffer[0x188], 0x16, 0x188); iValue = data_lsb_get(&buffer[0x19e], 2); DBG(DBG_FNC, "\n0x019e"); DBG(DBG_FNC, " binary threshold low : 0x%04x\n", (iValue >> 8) + ((iValue << 8) & 0xff00)); iValue = data_lsb_get(&buffer[0x1a0], 2); DBG(DBG_FNC, "\n0x01a0"); DBG(DBG_FNC, " binary threshold high : 0x%04x\n", (iValue >> 8) + ((iValue << 8) & 0xff00)); dbg_buffer(DBG_FNC, "\n0x01a2", &buffer[0x1a2], 0x12, 0x1a2); iValue = data_lsb_get(&buffer[0x1b4], 2); DBG(DBG_FNC, "\n0x01b4"); DBG(DBG_FNC, " bit[00..13] = Ptr to red gamma table (table_size * 0) : 0x%04x\n", (iValue & 0x3fff)); DBG(DBG_FNC, " bit[14..15] = ? : 0x%02x\n", (iValue >> 14) & 3); iValue = data_lsb_get(&buffer[0x1b6], 2); DBG(DBG_FNC, "0x01b6"); DBG(DBG_FNC, " bit[00..13] = Ptr to green gamma table (table_size * 1) : 0x%04x\n", (iValue & 0x3fff)); DBG(DBG_FNC, " bit[14..15] = ? : 0x%02x\n", (iValue >> 14) & 3); iValue = data_lsb_get(&buffer[0x1b8], 2); DBG(DBG_FNC, "0x01b8"); DBG(DBG_FNC, " bit[00..13] = Ptr to blue gamma table (table_size * 2) : 0x%04x\n", (iValue & 0x3fff)); DBG(DBG_FNC, " bit[14..15] = ? : 0x%02x\n", (iValue >> 14) & 3); iValue = data_lsb_get(&buffer[0x1ba], 1); DBG(DBG_FNC, "\n0x01ba"); DBG(DBG_FNC, " ? : 0x%02x\n", iValue); iValue = data_lsb_get(&buffer[0x1bb], 2); DBG(DBG_FNC, "0x01bb"); DBG(DBG_FNC, " ? : 0x%04x\n", iValue + ((data_lsb_get(&buffer[0x1bf], 1) & 1) << 16)); iValue = data_lsb_get(&buffer[0x1bd], 2); DBG(DBG_FNC, "0x01bd"); DBG(DBG_FNC, " ? : 0x%04x\n", iValue + (((data_lsb_get(&buffer[0x1bf], 1) >> 1) & 3) << 16)); iValue = data_lsb_get(&buffer[0x1c0], 3); DBG(DBG_FNC, "0x01c0"); DBG(DBG_FNC, " bit[0..19] = ? : 0x%06x\n", iValue & 0xfffff); iValue = data_lsb_get(&buffer[0x1bf], 2); DBG(DBG_FNC, "\n0x01bf"); DBG(DBG_FNC, " bit[3..4] = ? : 0x%02x\n", (iValue >> 3) & 3); DBG(DBG_FNC, " bit[5..7] = ? : 0x%02x\n", (iValue >> 5) & 7); iValue = data_lsb_get(&buffer[0x1c2], 3); DBG(DBG_FNC, "\n0x01c2"); DBG(DBG_FNC, " bit[4..23] = ? : 0x%06x\n", ((iValue >> 8) & 0xffff) + (((iValue >> 4) & 0xf) << 16)); iValue = data_lsb_get(&buffer[0x1c5], 3); DBG(DBG_FNC, "0x01c5"); DBG(DBG_FNC, " bit[00..19] = ? : 0x%06x\n", iValue & 0xfffff); DBG(DBG_FNC, " bit[20..23] = ? : 0x%02x\n", (iValue >> 20) & 0xf); dbg_buffer(DBG_FNC, "\n0x01c8", &buffer[0x1c8], 7, 0x1c8); iValue = data_lsb_get(&buffer[0x1cf], 3); DBG(DBG_FNC, "\n0x01cf"); DBG(DBG_FNC, " bit[0] = ? : 0x%02x\n", iValue & 1); DBG(DBG_FNC, " bit[1] = shading base (0 = 0x4000|1= 0x2000) : 0x%02x\n", (iValue >> 1) & 1); DBG(DBG_FNC, " bit[2] = white shading correction : 0x%02x\n", (iValue >> 2) & 1); DBG(DBG_FNC, " bit[3] = black shading correction : 0x%02x\n", (iValue >> 3) & 1); DBG(DBG_FNC, " bit[4..5] = 0x%02x : ", (iValue >> 4) & 3); switch ((iValue >> 4) & 3) { case 0: DBG(DBG_FNC, "8 bits per channel"); break; case 1: DBG(DBG_FNC, "12 bits per channel"); break; case 2: DBG(DBG_FNC, "16 bits per channel"); break; case 3: DBG(DBG_FNC, "lineart mode"); break; } DBG(DBG_FNC, "\n"); DBG(DBG_FNC, " bit[6] = samplerate: 0x%02x ", (iValue >> 6) & 1); if (((iValue >> 6) & 1) == PIXEL_RATE) DBG(DBG_FNC, "PIXEL_RATE\n"); else DBG(DBG_FNC, "LINE_RATE\n"); DBG(DBG_FNC, " bit[7] = ? : 0x%02x\n", (iValue >> 7) & 1); iValue = data_lsb_get(&buffer[0x1d0], 1); DBG(DBG_FNC, "\n0x01d0"); DBG(DBG_FNC, " bit[0] = 0x%02x\n", iValue & 1); DBG(DBG_FNC, " bit[1] = 0x%02x\n", (iValue >> 1) & 1); DBG(DBG_FNC, " bit[2..3] = gamma table size : 0x%02x ", (iValue >> 2) & 3); switch ((iValue >> 2) & 3) { case 0: DBG(DBG_FNC, "bit[0] + 0x100") ;break; case 1: DBG(DBG_FNC, "bit[0] + 0x400") ;break; case 2: DBG(DBG_FNC, "bit[0] + 0x1000") ;break; } DBG(DBG_FNC, "\n"); DBG(DBG_FNC, " bit[4..5] = ? : 0x%02x\n", (iValue >> 4) & 3); DBG(DBG_FNC, " bit[6] = use gamma tables? : 0x%02x\n", (iValue >> 6) & 1); DBG(DBG_FNC, " bit[7] = ? : 0x%02x\n", (iValue >> 7) & 1); dbg_buffer(DBG_FNC, "\n0x01d1", &buffer[0x1d1], 0x430, 0x1d1); DBG(DBG_FNC, "----------------------------------------------------\n\n"); */ /*exit(0); */ } #endif backends-1.3.0/backend/hp3900_rts8822.c000066400000000000000000013257741456256263500172200ustar00rootroot00000000000000/* HP Scanjet 3900 series - RTS8822 Core Copyright (C) 2005-2013 Jonathan Bravo Lopez 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, see . 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 code is still a bit ugly due to it's the result of applying reverse engineering techniques to windows driver. So at this moment what you see is exactly what windows driver does. And so many global vars exist that will be erased when driver is entirely debugged. There are some variables with unknown purpose. So they have no meaning name in form v+address. I hope to change their names when driver is debugged completely. */ #ifndef RTS8822_CORE #define RTS8822_CORE #define GetTickCount() (time(0) * 1000) #define min(A,B) (((A)<(B)) ? (A) : (B)) #define max(A,B) (((A)>(B)) ? (A) : (B)) #define PIXEL_TO_MM(_pixel_, _dpi_) ((_pixel_) * 25.4 / (_dpi_)) #define MM_TO_PIXEL(_mm_, _dpi_) ((_mm_) * (_dpi_) / 25.4) #include #include #include /* memset() */ #include /* clock() */ #include /* truncf() */ #include /* tolower() */ #include /* usleep() */ #include #include "hp3900_types.c" #include "hp3900_debug.c" #include "hp3900_config.c" #include "hp3900_usb.c" /*-------------------- Exported function headers --------------------*/ #ifdef developing static SANE_Int hp4370_prueba (struct st_device *dev); static void prueba (SANE_Byte * a); void shadingtest1 (struct st_device *dev, SANE_Byte * Regs, struct st_calibration *myCalib); static SANE_Int Calib_test (struct st_device *dev, SANE_Byte * Regs, struct st_calibration *myCalib, struct st_scanparams *scancfg); static SANE_Int Calib_BlackShading_jkd (struct st_device *dev, SANE_Byte * Regs, struct st_calibration *myCalib, struct st_scanparams *scancfg); #endif /*static void show_diff(struct st_device *dev, SANE_Byte *original);*/ /* functions to allocate and free space for a device */ static struct st_device *RTS_Alloc (void); static void RTS_Free (struct st_device *dev); /* Scanner level commands */ static SANE_Int RTS_Scanner_Init (struct st_device *dev); static SANE_Int RTS_Scanner_SetParams (struct st_device *dev, struct params *param); static SANE_Int RTS_Scanner_StartScan (struct st_device *dev); static void RTS_Scanner_StopScan (struct st_device *dev, SANE_Int wait); static void RTS_Scanner_End (struct st_device *dev); /* loading configuration functions */ static SANE_Int Load_Buttons (struct st_device *dev); static SANE_Int Load_Chipset (struct st_device *dev); static SANE_Int Load_Config (struct st_device *dev); static SANE_Int Load_Constrains (struct st_device *dev); static SANE_Int Load_Motor (struct st_device *dev); static SANE_Int Load_MotorCurves (struct st_device *dev); static SANE_Int Load_Motormoves (struct st_device *dev); static SANE_Int Load_Scanmodes (struct st_device *dev); static SANE_Int Load_Sensor (struct st_device *dev); static SANE_Int Load_Timings (struct st_device *dev); /* freeing configuration functions */ static void Free_Buttons (struct st_device *dev); static void Free_Chipset (struct st_device *dev); static void Free_Config (struct st_device *dev); static void Free_Constrains (struct st_device *dev); static void Free_Motor (struct st_device *dev); static void Free_MotorCurves (struct st_device *dev); static void Free_Motormoves (struct st_device *dev); static void Free_Scanmodes (struct st_device *dev); static void Free_Sensor (struct st_device *dev); static void Free_Timings (struct st_device *dev); static void Free_Vars (void); /* Functions to manage data */ static SANE_Byte data_bitget (SANE_Byte * address, SANE_Int mask); static void data_bitset (SANE_Byte * address, SANE_Int mask, SANE_Byte data); static SANE_Int data_lsb_get (SANE_Byte * address, SANE_Int size); static void data_lsb_set (SANE_Byte * address, SANE_Int data, SANE_Int size); static void data_msb_set (SANE_Byte * address, SANE_Int data, SANE_Int size); static void data_wide_bitset (SANE_Byte * address, SANE_Int mask, SANE_Int data); static SANE_Int data_swap_endianess (SANE_Int address, SANE_Int size); static SANE_Int Device_get (SANE_Int product, SANE_Int vendor); /* Chipset related commands */ static SANE_Int Chipset_ID (struct st_device *dev); static SANE_Int Chipset_Name (struct st_device *dev, char *name, SANE_Int size); static SANE_Int Chipset_Reset (struct st_device *dev); /* Initializing functions */ static SANE_Int Init_Registers (struct st_device *dev); static SANE_Int Init_USBData (struct st_device *dev); static SANE_Int Init_Vars (void); /* scanmode functions */ static SANE_Int Scanmode_fitres (struct st_device *dev, SANE_Int scantype, SANE_Int colormode, SANE_Int resolution); static SANE_Int Scanmode_maxres (struct st_device *dev, SANE_Int scantype, SANE_Int colormode); static SANE_Int Scanmode_minres (struct st_device *dev, SANE_Int scantype, SANE_Int colormode); /* Chipset management useful commands*/ static SANE_Int RTS_USBType (struct st_device *dev); static SANE_Byte RTS_Sensor_Type (USB_Handle usb_handle); static void RTS_DebugInit (void); static SANE_Int RTS_Enable_CCD (struct st_device *dev, SANE_Byte * Regs, SANE_Int channels); /* DMA management commands */ static SANE_Int RTS_DMA_Cancel (struct st_device *dev); static SANE_Int RTS_DMA_CheckType (struct st_device *dev, SANE_Byte * Regs); static SANE_Int RTS_DMA_Enable_Read (struct st_device *dev, SANE_Int dmacs, SANE_Int size, SANE_Int options); static SANE_Int RTS_DMA_Enable_Write (struct st_device *dev, SANE_Int dmacs, SANE_Int size, SANE_Int options); static SANE_Int RTS_DMA_Read (struct st_device *dev, SANE_Int dmacs, SANE_Int options, SANE_Int size, SANE_Byte * buffer); static SANE_Int RTS_DMA_Reset (struct st_device *dev); static SANE_Int RTS_DMA_SetType (struct st_device *dev, SANE_Byte * Regs, SANE_Byte ramtype); static SANE_Int RTS_DMA_WaitReady (struct st_device *dev, SANE_Int msecs); static SANE_Int RTS_DMA_Write (struct st_device *dev, SANE_Int dmacs, SANE_Int options, SANE_Int size, SANE_Byte * buffer); /* EEPROM management commands */ static SANE_Int RTS_EEPROM_ReadByte (USB_Handle usb_handle, SANE_Int address, SANE_Byte * data); static SANE_Int RTS_EEPROM_ReadInteger (USB_Handle usb_handle, SANE_Int address, SANE_Int * data); static SANE_Int RTS_EEPROM_ReadWord (USB_Handle usb_handle, SANE_Int address, SANE_Int * data); static SANE_Int RTS_EEPROM_WriteBuffer (USB_Handle usb_handle, SANE_Int address, SANE_Byte * data, SANE_Int size); static SANE_Int RTS_EEPROM_WriteByte (USB_Handle usb_handle, SANE_Int address, SANE_Byte data); static SANE_Int RTS_EEPROM_WriteInteger (USB_Handle usb_handle, SANE_Int address, SANE_Int data); static SANE_Int RTS_EEPROM_WriteWord (USB_Handle usb_handle, SANE_Int address, SANE_Int data); static SANE_Int RTS_Execute (struct st_device *dev); static SANE_Int RTS_Warm_Reset (struct st_device *dev); static SANE_Byte RTS_IsExecuting (struct st_device *dev, SANE_Byte * Regs); static SANE_Int RTS_GetScanmode (struct st_device *dev, SANE_Int scantype, SANE_Int colormode, SANE_Int resolution); static SANE_Int RTS_GetImage (struct st_device *dev, SANE_Byte * Regs, struct st_scanparams *scancfg, struct st_gain_offset *gain_offset, SANE_Byte * buffer, struct st_calibration *myCalib, SANE_Int options, SANE_Int gainmode); static SANE_Int RTS_GetImage_GetBuffer (struct st_device *dev, double dSize, SANE_Byte * buffer, double *transferred); static SANE_Int RTS_GetImage_Read (struct st_device *dev, SANE_Byte * buffer, struct st_scanparams *myvar, struct st_hwdconfig *hwdcfg); static SANE_Int RTS_isTmaAttached (struct st_device *dev); /* functions to wait for a process tp finish */ static SANE_Int RTS_WaitInitEnd (struct st_device *dev, SANE_Int msecs); static SANE_Int RTS_WaitScanEnd (struct st_device *dev, SANE_Int msecs); /* functions to read/write control registers */ static SANE_Int RTS_ReadRegs (USB_Handle usb_handle, SANE_Byte * buffer); static SANE_Int RTS_WriteRegs (USB_Handle usb_handle, SANE_Byte * buffer); /* functions to manage the scan counter */ static SANE_Int RTS_ScanCounter_Inc (struct st_device *dev); static SANE_Int RTS_ScanCounter_Get (struct st_device *dev); /* functions to setup control registers */ static SANE_Int RTS_Setup (struct st_device *dev, SANE_Byte * Regs, struct st_scanparams *myvar, struct st_hwdconfig *hwdcfg, struct st_gain_offset *gain_offset); static void RTS_Setup_Arrangeline (struct st_device *dev, struct st_hwdconfig *hwdcfg, SANE_Int colormode); static void RTS_Setup_Channels (struct st_device *dev, SANE_Byte * Regs, struct st_scanparams *scancfg, SANE_Int mycolormode); static void RTS_Setup_Coords (SANE_Byte * Regs, SANE_Int iLeft, SANE_Int iTop, SANE_Int width, SANE_Int height); static SANE_Int RTS_Setup_Depth (SANE_Byte * Regs, struct st_scanparams *scancfg, SANE_Int mycolormode); static void RTS_Setup_Exposure_Times (SANE_Byte * Regs, struct st_scanparams *scancfg, struct st_scanmode *sm); static void RTS_Setup_GainOffset (SANE_Byte * Regs, struct st_gain_offset *gain_offset); static void RTS_Setup_Gamma (SANE_Byte * Regs, struct st_hwdconfig *lowcfg); static SANE_Int RTS_Setup_Line_Distances (struct st_device *dev, SANE_Byte * Regs, struct st_scanparams *scancfg, struct st_hwdconfig *hwdcfg, SANE_Int mycolormode, SANE_Int arrangeline); static SANE_Int RTS_Setup_Motor (struct st_device *dev, SANE_Byte * Regs, struct st_scanparams *myvar, SANE_Int somevalue); static void RTS_Setup_RefVoltages (struct st_device *dev, SANE_Byte * Regs); static void RTS_Setup_SensorTiming (struct st_device *dev, SANE_Int mytiming, SANE_Byte * Regs); static void RTS_Setup_Shading (SANE_Byte * Regs, struct st_scanparams *scancfg, struct st_hwdconfig *hwdcfg, SANE_Int bytes_per_line); static SANE_Int Scan_Start (struct st_device *dev); static void SetLock (USB_Handle usb_handle, SANE_Byte * Regs, SANE_Byte Enable); static SANE_Int fn3330 (struct st_device *dev, SANE_Byte * Regs, struct st_cal2 *calbuffers, SANE_Int sensorchannelcolor, SANE_Int * tablepos, SANE_Int data); static SANE_Int fn3560 (USHORT * table, struct st_cal2 *calbuffers, SANE_Int * tablepos); static SANE_Int fn3730 (struct st_device *dev, struct st_cal2 *calbuffers, SANE_Byte * Regs, USHORT * table, SANE_Int sensorchannelcolor, SANE_Int data); static SANE_Int Reading_CreateBuffers (struct st_device *dev); static SANE_Int Reading_DestroyBuffers (struct st_device *dev); static SANE_Int Reading_BufferSize_Get (struct st_device *dev, SANE_Byte channels_per_dot, SANE_Int channel_size); static SANE_Int Reading_BufferSize_Notify (struct st_device *dev, SANE_Int data, SANE_Int size); static SANE_Int Reading_Wait (struct st_device *dev, SANE_Byte Channels_per_dot, SANE_Byte Channel_size, SANE_Int size, SANE_Int * last_amount, SANE_Int seconds, SANE_Byte op); static SANE_Int Read_Image (struct st_device *dev, SANE_Int buffer_size, SANE_Byte * buffer, SANE_Int * transferred); static SANE_Int Read_ResizeBlock (struct st_device *dev, SANE_Byte * buffer, SANE_Int buffer_size, SANE_Int * transferred); static SANE_Int Read_Block (struct st_device *dev, SANE_Int buffer_size, SANE_Byte * buffer, SANE_Int * transferred); static SANE_Int Read_NonColor_Block (struct st_device *dev, SANE_Byte * buffer, SANE_Int buffer_size, SANE_Byte ColorMode, SANE_Int * transferred); /* Ref functions */ static SANE_Int Refs_Analyze_Pattern (struct st_scanparams *scancfg, SANE_Byte * scanned_pattern, SANE_Int * ler1, SANE_Int ler1order, SANE_Int * ser1, SANE_Int ser1order); static SANE_Int Refs_Counter_Inc (struct st_device *dev); static SANE_Byte Refs_Counter_Load (struct st_device *dev); static SANE_Int Refs_Counter_Save (struct st_device *dev, SANE_Byte data); static SANE_Int Refs_Detect (struct st_device *dev, SANE_Byte * Regs, SANE_Int resolution_x, SANE_Int resolution_y, SANE_Int * x, SANE_Int * y); static SANE_Int Refs_Load (struct st_device *dev, SANE_Int * x, SANE_Int * y); static SANE_Int Refs_Save (struct st_device *dev, SANE_Int left_leading, SANE_Int start_pos); static SANE_Int Refs_Set (struct st_device *dev, SANE_Byte * Regs, struct st_scanparams *myscan); /* Coordinates' constrains functions */ static SANE_Int Constrains_Check (struct st_device *dev, SANE_Int Resolution, SANE_Int scantype, struct st_coords *mycoords); static struct st_coords *Constrains_Get (struct st_device *dev, SANE_Byte scantype); /* Gain and offset functions */ static SANE_Int GainOffset_Clear (struct st_device *dev); static SANE_Int GainOffset_Get (struct st_device *dev); static SANE_Int GainOffset_Save (struct st_device *dev, SANE_Int * offset, SANE_Byte * gain); static SANE_Int GainOffset_Counter_Inc (struct st_device *dev, SANE_Int * arg1); static SANE_Byte GainOffset_Counter_Load (struct st_device *dev); static SANE_Int GainOffset_Counter_Save (struct st_device *dev, SANE_Byte data); /* Gamma functions*/ static SANE_Int Gamma_AllocTable (SANE_Byte * table); static SANE_Int Gamma_Apply (struct st_device *dev, SANE_Byte * Regs, struct st_scanparams *scancfg, struct st_hwdconfig *hwdcfg, struct st_gammatables *mygamma); static void Gamma_FreeTables (void); static SANE_Int Gamma_SendTables (struct st_device *dev, SANE_Byte * Regs, SANE_Byte * gammatable, SANE_Int size); static SANE_Int Gamma_GetTables (struct st_device *dev, SANE_Byte * Gamma_buffer); /* Lamp functions */ static SANE_Byte Lamp_GetGainMode (struct st_device *dev, SANE_Int resolution, SANE_Byte scantype); static void Lamp_SetGainMode (struct st_device *dev, SANE_Byte * Regs, SANE_Int resolution, SANE_Byte gainmode); static SANE_Int Lamp_PWM_DutyCycle_Get (struct st_device *dev, SANE_Int * data); static SANE_Int Lamp_PWM_DutyCycle_Set (struct st_device *dev, SANE_Int duty_cycle); static SANE_Int Lamp_PWM_Setup (struct st_device *dev, SANE_Int lamp); static SANE_Int Lamp_PWM_use (struct st_device *dev, SANE_Int enable); static SANE_Int Lamp_PWM_CheckStable (struct st_device *dev, SANE_Int resolution, SANE_Int lamp); static SANE_Int Lamp_PWM_Save (struct st_device *dev, SANE_Int fixedpwm); static SANE_Int Lamp_PWM_SaveStatus (struct st_device *dev, SANE_Byte status); static SANE_Int Lamp_Status_Get (struct st_device *dev, SANE_Byte * flb_lamp, SANE_Byte * tma_lamp); static SANE_Int Lamp_Status_Set (struct st_device *dev, SANE_Byte * Regs, SANE_Int turn_on, SANE_Int lamp); static SANE_Int Lamp_Status_Timer_Set (struct st_device *dev, SANE_Int minutes); static SANE_Int Lamp_Warmup (struct st_device *dev, SANE_Byte * Regs, SANE_Int lamp, SANE_Int resolution); /* Head related functions */ static SANE_Int Head_IsAtHome (struct st_device *dev, SANE_Byte * Regs); static SANE_Int Head_ParkHome (struct st_device *dev, SANE_Int bWait, SANE_Int movement); static SANE_Int Head_Relocate (struct st_device *dev, SANE_Int speed, SANE_Int direction, SANE_Int ypos); /* Motor functions */ static SANE_Byte *Motor_AddStep (SANE_Byte * steps, SANE_Int * bwriten, SANE_Int step); static SANE_Int Motor_Change (struct st_device *dev, SANE_Byte * buffer, SANE_Byte value); static SANE_Int Motor_GetFromResolution (SANE_Int resolution); static SANE_Int Motor_Move (struct st_device *dev, SANE_Byte * Regs, struct st_motormove *mymotor, struct st_motorpos *mtrpos); static void Motor_Release (struct st_device *dev); static SANE_Int Motor_Setup_Steps (struct st_device *dev, SANE_Byte * Regs, SANE_Int mysetting); static SANE_Int Motor_Curve_Equal (struct st_device *dev, SANE_Int motorsetting, SANE_Int direction, SANE_Int curve1, SANE_Int curve2); static void Motor_Curve_Free (struct st_motorcurve **motorcurves, SANE_Int * mtc_count); static struct st_curve *Motor_Curve_Get (struct st_device *dev, SANE_Int motorcurve, SANE_Int direction, SANE_Int itype); static struct st_motorcurve **Motor_Curve_Parse (SANE_Int * mtc_count, SANE_Int * buffer); /* Functions to arrange scanned lines */ static SANE_Int Arrange_Colour (struct st_device *dev, SANE_Byte * buffer, SANE_Int buffer_size, SANE_Int * transferred); static SANE_Int Arrange_Compose (struct st_device *dev, SANE_Byte * buffer, SANE_Int buffer_size, SANE_Int * transferred); static SANE_Int Arrange_NonColour (struct st_device *dev, SANE_Byte * buffer, SANE_Int buffer_size, SANE_Int * transferred); /* Composing RGB triplet functions */ static void Triplet_Gray (SANE_Byte * pPointer1, SANE_Byte * pPointer2, SANE_Byte * buffer, SANE_Int channels_count); static void Triplet_Lineart (SANE_Byte * pPointer1, SANE_Byte * pPointer2, SANE_Byte * buffer, SANE_Int channels_count); static void Triplet_Compose_Order (struct st_device *dev, SANE_Byte * pRed, SANE_Byte * pGreen, SANE_Byte * pBlue, SANE_Byte * buffer, SANE_Int dots); static void Triplet_Compose_HRes (SANE_Byte * pPointer1, SANE_Byte * pPointer2, SANE_Byte * pPointer3, SANE_Byte * pPointer4, SANE_Byte * pPointer5, SANE_Byte * pPointer6, SANE_Byte * buffer, SANE_Int Width); static void Triplet_Compose_LRes (SANE_Byte * pRed, SANE_Byte * pGreen, SANE_Byte * pBlue, SANE_Byte * buffer, SANE_Int dots); static void Triplet_Colour_Order (struct st_device *dev, SANE_Byte * pRed, SANE_Byte * pGreen, SANE_Byte * pBlue, SANE_Byte * buffer, SANE_Int Width); static void Triplet_Colour_HRes (SANE_Byte * pRed1, SANE_Byte * pGreen1, SANE_Byte * pBlue1, SANE_Byte * pRed2, SANE_Byte * pGreen2, SANE_Byte * pBlue2, SANE_Byte * buffer, SANE_Int Width); static void Triplet_Colour_LRes (SANE_Int Width, SANE_Byte * Buffer, SANE_Byte * pChannel1, SANE_Byte * pChannel2, SANE_Byte * pChannel3); /* Timing functions */ static SANE_Int Timing_SetLinearImageSensorClock (SANE_Byte * Regs, struct st_cph *cph); /* Functions used to resize retrieved image */ static SANE_Int Resize_Start (struct st_device *dev, SANE_Int * transferred); static SANE_Int Resize_CreateBuffers (struct st_device *dev, SANE_Int size1, SANE_Int size2, SANE_Int size3); static SANE_Int Resize_DestroyBuffers (struct st_device *dev); static SANE_Int Resize_Increase (SANE_Byte * to_buffer, SANE_Int to_resolution, SANE_Int to_width, SANE_Byte * from_buffer, SANE_Int from_resolution, SANE_Int from_width, SANE_Int myresize_mode); static SANE_Int Resize_Decrease (SANE_Byte * to_buffer, SANE_Int to_resolution, SANE_Int to_width, SANE_Byte * from_buffer, SANE_Int from_resolution, SANE_Int from_width, SANE_Int myresize_mode); /* Scanner buttons support */ static SANE_Int Buttons_Count (struct st_device *dev); static SANE_Int Buttons_Enable (struct st_device *dev); static SANE_Int Buttons_Order (struct st_device *dev, SANE_Int mask); static SANE_Int Buttons_Status (struct st_device *dev); static SANE_Int Buttons_Released (struct st_device *dev); /* Calibration functions */ static SANE_Int Calib_CreateBuffers (struct st_device *dev, struct st_calibration *buffer, SANE_Int my14b4); static SANE_Int Calib_CreateFixedBuffers (void); static void Calib_FreeBuffers (struct st_calibration *caltables); static void Calib_LoadCut (struct st_device *dev, struct st_scanparams *scancfg, SANE_Int scantype, struct st_calibration_config *calibcfg); static SANE_Int Calib_AdcGain (struct st_device *dev, struct st_calibration_config *calibcfg, SANE_Int arg2, SANE_Int gainmode); static SANE_Int Calib_AdcOffsetRT (struct st_device *dev, struct st_calibration_config *calibcfg, SANE_Int value); static SANE_Int Calib_BlackShading (struct st_device *dev, struct st_calibration_config *calibcfg, struct st_calibration *myCalib, SANE_Int gainmode); static SANE_Int Calib_BWShading (struct st_calibration_config *calibcfg, struct st_calibration *myCalib, SANE_Int gainmode); static SANE_Int Calib_WhiteShading_3 (struct st_device *dev, struct st_calibration_config *calibcfg, struct st_calibration *myCalib, SANE_Int gainmode); static void Calibrate_Free (struct st_cal2 *calbuffers); static SANE_Int Calibrate_Malloc (struct st_cal2 *calbuffers, SANE_Byte * Regs, struct st_calibration *myCalib, SANE_Int somelength); static SANE_Int Calib_ReadTable (struct st_device *dev, SANE_Byte * table, SANE_Int size, SANE_Int data); static SANE_Int Calib_WriteTable (struct st_device *dev, SANE_Byte * table, SANE_Int size, SANE_Int data); static SANE_Int Calib_LoadConfig (struct st_device *dev, struct st_calibration_config *calibcfg, SANE_Int scantype, SANE_Int resolution, SANE_Int bitmode); static SANE_Int Calib_PAGain (struct st_device *dev, struct st_calibration_config *calibcfg, SANE_Int gainmode); static SANE_Int Calibration (struct st_device *dev, SANE_Byte * Regs, struct st_scanparams *scancfg, struct st_calibration *myCalib, SANE_Int value); /* function for white shading correction */ static SANE_Int WShading_Calibrate (struct st_device *dev, SANE_Byte * Regs, struct st_calibration *myCalib, struct st_scanparams *scancfg); static void WShading_Emulate (SANE_Byte * buffer, SANE_Int * chnptr, SANE_Int size, SANE_Int depth); /* functions for shading calibration */ static SANE_Int Shading_apply (struct st_device *dev, SANE_Byte * Regs, struct st_scanparams *myvar, struct st_calibration *myCalib); static SANE_Int Shading_black_apply (struct st_device *dev, SANE_Byte * Regs, SANE_Int channels, struct st_calibration *myCalib, struct st_cal2 *calbuffers); static SANE_Int Shading_white_apply (struct st_device *dev, SANE_Byte * Regs, SANE_Int channels, struct st_calibration *myCalib, struct st_cal2 *calbuffers); /* Spread-Spectrum Clock Generator functions */ static SANE_Int SSCG_Enable (struct st_device *dev); static void Split_into_12bit_channels (SANE_Byte * destino, SANE_Byte * fuente, SANE_Int size); static SANE_Int Scan_Read_BufferA (struct st_device *dev, SANE_Int buffer_size, SANE_Int arg2, SANE_Byte * pBuffer, SANE_Int * bytes_transferred); static SANE_Int Bulk_Operation (struct st_device *dev, SANE_Byte op, SANE_Int buffer_size, SANE_Byte * buffer, SANE_Int * transferred); static SANE_Int Get_PAG_Value (SANE_Byte scantype, SANE_Byte color); static SANE_Int GetOneLineInfo (struct st_device *dev, SANE_Int resolution, SANE_Int * maximus, SANE_Int * minimus, double *average); static SANE_Int Load_StripCoords (SANE_Int scantype, SANE_Int * ypos, SANE_Int * xpos); /*static SANE_Int Free_Fixed_CalBuffer(void);*/ static SANE_Int SetMultiExposure (struct st_device *dev, SANE_Byte * Regs); static void Set_E950_Mode (struct st_device *dev, SANE_Byte mode); static SANE_Int LoadImagingParams (struct st_device *dev, SANE_Int inifile); static SANE_Int SetScanParams (struct st_device *dev, SANE_Byte * Regs, struct st_scanparams *scancfg, struct st_hwdconfig *hwdcfg); static SANE_Int IsScannerLinked (struct st_device *dev); static SANE_Int Read_FE3E (struct st_device *dev, SANE_Byte * destino); static double get_shrd (double value, SANE_Int desp); static char get_byte (double value); /*static SANE_Int RTS8822_GetRegisters(SANE_Byte *buffer);*/ /* ----------------- Implementation ------------------*/ static void RTS_Free (struct st_device *dev) { /* this function frees space of devices's variable */ if (dev != NULL) { /* next function shouldn't be necessary but I can NOT assure that other programmers will call Free_Config before this function */ Free_Config (dev); if (dev->init_regs != NULL) free (dev->init_regs); if (dev->Resize != NULL) free (dev->Resize); if (dev->Reading != NULL) free (dev->Reading); if (dev->scanning != NULL) free (dev->scanning); if (dev->status != NULL) free (dev->status); free (dev); } } static struct st_device * RTS_Alloc () { /* this function allocates space for device's variable */ struct st_device *dev = NULL; dev = malloc (sizeof (struct st_device)); if (dev != NULL) { SANE_Int rst = OK; memset (dev, 0, sizeof (struct st_device)); /* initial registers */ dev->init_regs = malloc (sizeof (SANE_Byte) * RT_BUFFER_LEN); if (dev->init_regs != NULL) memset (dev->init_regs, 0, sizeof (SANE_Byte) * RT_BUFFER_LEN); else rst = ERROR; if (rst == OK) { dev->scanning = malloc (sizeof (struct st_scanning)); if (dev->scanning != NULL) memset (dev->scanning, 0, sizeof (struct st_scanning)); else rst = ERROR; } if (rst == OK) { dev->Reading = malloc (sizeof (struct st_readimage)); if (dev->Reading != NULL) memset (dev->Reading, 0, sizeof (struct st_readimage)); else rst = ERROR; } if (rst == OK) { dev->Resize = malloc (sizeof (struct st_resize)); if (dev->Resize != NULL) memset (dev->Resize, 0, sizeof (struct st_resize)); else rst = ERROR; } if (rst == OK) { dev->status = malloc (sizeof (struct st_status)); if (dev->status != NULL) memset (dev->status, 0, sizeof (struct st_status)); else rst = ERROR; } /* if something fails, free space */ if (rst != OK) { RTS_Free (dev); dev = NULL; } } return dev; } static void RTS_Scanner_End (struct st_device *dev) { Gamma_FreeTables (); Free_Config (dev); Free_Vars (); } static SANE_Int Device_get (SANE_Int product, SANE_Int vendor) { return cfg_device_get (product, vendor); } static SANE_Int RTS_Scanner_Init (struct st_device *dev) { SANE_Int rst; DBG (DBG_FNC, "> RTS_Scanner_Init:\n"); DBG (DBG_FNC, "> Backend version: %s\n", BACKEND_VRSN); rst = ERROR; /* gets usb type of this scanner if it's not already set by user */ if (RTS_Debug->usbtype == -1) RTS_Debug->usbtype = RTS_USBType (dev); if (RTS_Debug->usbtype != ERROR) { DBG (DBG_FNC, " -> Chipset model ID: %i\n", Chipset_ID (dev)); Chipset_Reset (dev); if (Load_Config (dev) == OK) { if (IsScannerLinked (dev) == OK) { Set_E950_Mode (dev, 0); Gamma_AllocTable (NULL); rst = OK; } else Free_Config (dev); } } return rst; } static SANE_Int RTS_WriteRegs (USB_Handle usb_handle, SANE_Byte * buffer) { SANE_Int rst = ERROR; if (buffer != NULL) rst = Write_Buffer (usb_handle, 0xe800, buffer, RT_BUFFER_LEN * sizeof (SANE_Byte)); return rst; } static SANE_Int RTS_ReadRegs (USB_Handle usb_handle, SANE_Byte * buffer) { SANE_Int rst = ERROR; if (buffer != NULL) rst = Read_Buffer (usb_handle, 0xe800, buffer, RT_BUFFER_LEN * sizeof (SANE_Byte)); return rst; } static void SetLock (USB_Handle usb_handle, SANE_Byte * Regs, SANE_Byte Enable) { SANE_Byte lock; DBG (DBG_FNC, "+ SetLock(*Regs, Enable=%i):\n", Enable); if (Regs == NULL) { if (Read_Byte (usb_handle, 0xee00, &lock) != OK) lock = 0; } else lock = Regs[0x600]; if (Enable == FALSE) lock &= 0xfb; else lock |= 4; if (Regs != NULL) Regs[0x600] = lock; Write_Byte (usb_handle, 0xee00, lock); DBG (DBG_FNC, "- SetLock\n"); } static void Set_E950_Mode (struct st_device *dev, SANE_Byte mode) { SANE_Int data; DBG (DBG_FNC, "+ Set_E950_Mode(mode=%i):\n", mode); if (Read_Word (dev->usb_handle, 0xe950, &data) == OK) { data = (mode == 0) ? data & 0xffbf : data | 0x40; Write_Word (dev->usb_handle, 0xe950, data); } DBG (DBG_FNC, "- Set_E950_Mode\n"); } static struct st_curve * Motor_Curve_Get (struct st_device *dev, SANE_Int motorcurve, SANE_Int direction, SANE_Int itype) { struct st_curve *rst = NULL; if (dev != NULL) { if ((dev->mtrsetting != NULL) && (motorcurve < dev->mtrsetting_count)) { struct st_motorcurve *mtc = dev->mtrsetting[motorcurve]; if (mtc != NULL) { if ((mtc->curve != NULL) && (mtc->curve_count > 0)) { struct st_curve *crv; SANE_Int a = 0; while (a < mtc->curve_count) { /* get each curve */ crv = mtc->curve[a]; if (crv != NULL) { /* check direction and type */ if ((crv->crv_speed == direction) && (crv->crv_type == itype)) { /* found ! */ rst = crv; break; } } a++; } } } } } return rst; } static SANE_Int Motor_Curve_Equal (struct st_device *dev, SANE_Int motorsetting, SANE_Int direction, SANE_Int curve1, SANE_Int curve2) { /* compares two curves of the same direction returns TRUE if both buffers are equal */ SANE_Int rst = FALSE; struct st_curve *crv1 = Motor_Curve_Get (dev, motorsetting, direction, curve1); struct st_curve *crv2 = Motor_Curve_Get (dev, motorsetting, direction, curve2); if ((crv1 != NULL) && (crv2 != NULL)) { if (crv1->step_count == crv2->step_count) { rst = TRUE; if (crv1->step_count > 0) { SANE_Int a = 0; while ((a < crv1->step_count) && (rst == TRUE)) { rst = (crv1->step[a] == crv2->step[a]) ? TRUE : FALSE; a++; } } } } return rst; } static struct st_motorcurve ** Motor_Curve_Parse (SANE_Int * mtc_count, SANE_Int * buffer) { /* this function parses motorcurve buffer to get all motor settings */ struct st_motorcurve **rst = NULL; *mtc_count = 0; if (buffer != NULL) { /* phases: -1 : null phase 0 : -3 : initial config */ struct st_motorcurve *mtc = NULL; SANE_Int phase; phase = -1; while (*buffer != -1) { if (*buffer == -2) { /* end of motorcurve */ /* complete any opened phase */ /* close phase */ phase = -1; } else { /* step */ if (phase == -1) { /* new motorcurve */ phase = 0; mtc = (struct st_motorcurve *) malloc (sizeof (struct st_motorcurve)); if (mtc != NULL) { *mtc_count += 1; rst = realloc (rst, sizeof (struct st_motorcurve **) * *mtc_count); if (rst != NULL) { rst[*mtc_count - 1] = mtc; memset (mtc, 0, sizeof (struct st_motorcurve)); phase = -3; /* initial config */ } else { /* memory error */ *mtc_count = 0; break; } } else break; /* some error */ } if (mtc != NULL) { switch (phase) { case -3: /* initial config */ mtc->mri = *(buffer); mtc->msi = *(buffer + 1); mtc->skiplinecount = *(buffer + 2); mtc->motorbackstep = *(buffer + 3); buffer += 3; phase = -4; break; case -4: /**/ { /* create new step curve */ struct st_curve *curve = malloc (sizeof (struct st_curve)); if (curve != NULL) { /* add to step curve list */ mtc->curve = (struct st_curve **) realloc (mtc->curve, sizeof (struct st_curve **) * (mtc-> curve_count + 1)); if (mtc->curve != NULL) { mtc->curve_count++; mtc->curve[mtc->curve_count - 1] = curve; memset (curve, 0, sizeof (struct st_curve)); /* read crv speed and type */ curve->crv_speed = *buffer; curve->crv_type = *(buffer + 1); buffer += 2; /* get length of step buffer */ while (*(buffer + curve->step_count) != 0) curve->step_count++; if (curve->step_count > 0) { /* allocate step buffer */ curve->step = (SANE_Int *) malloc (sizeof (SANE_Int) * curve->step_count); if (curve->step != NULL) { memcpy (curve->step, buffer, sizeof (SANE_Int) * curve->step_count); buffer += curve->step_count; } else curve->step_count = 0; } } else { mtc->curve_count = 0; free (curve); } } else break; } break; } } } buffer++; } } return rst; } static void Motor_Curve_Free (struct st_motorcurve **motorcurves, SANE_Int * mtc_count) { if ((motorcurves != NULL) && (mtc_count != NULL)) { struct st_motorcurve *mtc; struct st_curve *curve; while (*mtc_count > 0) { mtc = motorcurves[*mtc_count - 1]; if (mtc != NULL) { if (mtc->curve != NULL) { while (mtc->curve_count > 0) { curve = mtc->curve[mtc->curve_count - 1]; if (curve != NULL) { if (curve->step != NULL) free (curve->step); free (curve); } mtc->curve_count--; } } free (mtc); } *mtc_count -= 1; } free (motorcurves); } } static SANE_Byte RTS_Sensor_Type (USB_Handle usb_handle) { /* Returns sensor type 01 = CCD 00 = CIS */ SANE_Int a, b, c; SANE_Byte rst; DBG (DBG_FNC, "+ RTS_Sensor_Type:\n"); a = b = c = 0; /* Save data first */ Read_Word (usb_handle, 0xe950, &a); Read_Word (usb_handle, 0xe956, &b); /* Enables GPIO 0xe950 writing directly 0x13ff */ Write_Word (usb_handle, 0xe950, 0x13ff); /* Sets GPIO 0xe956 writing 0xfcf0 */ Write_Word (usb_handle, 0xe956, 0xfcf0); /* Makes a sleep of 200 ms */ usleep (1000 * 200); /* Get GPIO 0xe968 */ Read_Word (usb_handle, 0xe968, &c); /* Restore data */ Write_Word (usb_handle, 0xe950, a); Write_Word (usb_handle, 0xe956, b); rst = ((_B1 (c) & 1) == 0) ? CCD_SENSOR : CIS_SENSOR; DBG (DBG_FNC, "- RTS_Sensor_Type: %s\n", (rst == CCD_SENSOR) ? "CCD" : "CIS"); return rst; } static void Free_Scanmodes (struct st_device *dev) { DBG (DBG_FNC, "> Free_Scanmodes\n"); if (dev->scanmodes != NULL) { if (dev->scanmodes_count > 0) { SANE_Int a; for (a = 0; a < dev->scanmodes_count; a++) if (dev->scanmodes[a] != NULL) free (dev->scanmodes[a]); } free (dev->scanmodes); dev->scanmodes = NULL; } dev->scanmodes_count = 0; } static SANE_Int Load_Scanmodes (struct st_device *dev) { SANE_Int rst = OK; SANE_Int a, b; struct st_scanmode reg, *mode; DBG (DBG_FNC, "> Load_Scanmodes\n"); if ((dev->scanmodes != NULL) || (dev->scanmodes_count > 0)) Free_Scanmodes (dev); a = 0; while ((cfg_scanmode_get (dev->sensorcfg->type, a, ®) == OK) && (rst == OK)) { mode = (struct st_scanmode *) malloc (sizeof (struct st_scanmode)); if (mode != NULL) { memcpy (mode, ®, sizeof (struct st_scanmode)); for (b = 0; b < 3; b++) { if (mode->mexpt[b] == 0) { mode->mexpt[b] = mode->ctpc; if (mode->multiexposure != 1) mode->expt[b] = mode->ctpc; } } mode->ctpc = ((mode->ctpc + 1) * mode->multiexposure) - 1; dev->scanmodes = (struct st_scanmode **) realloc (dev->scanmodes, (dev->scanmodes_count + 1) * sizeof (struct st_scanmode **)); if (dev->scanmodes != NULL) { dev->scanmodes[dev->scanmodes_count] = mode; dev->scanmodes_count++; } else rst = ERROR; } else rst = ERROR; a++; } if (rst == ERROR) Free_Scanmodes (dev); DBG (DBG_FNC, " -> Found %i scanmodes\n", dev->scanmodes_count); dbg_scanmodes (dev); return rst; } static void Free_Config (struct st_device *dev) { DBG (DBG_FNC, "+ Free_Config\n"); /* free buttons */ Free_Buttons (dev); /* free motor general configuration */ Free_Motor (dev); /* free sensor main configuration */ Free_Sensor (dev); /* free ccd sensor timing tables */ Free_Timings (dev); /* free motor curves */ Free_MotorCurves (dev); /* free motor movements */ Free_Motormoves (dev); /* free scan modes */ Free_Scanmodes (dev); /* free constrains */ Free_Constrains (dev); /* free chipset configuration */ Free_Chipset (dev); DBG (DBG_FNC, "- Free_Config\n"); } static void Free_Chipset (struct st_device *dev) { DBG (DBG_FNC, "> Free_Chipset\n"); if (dev->chipset != NULL) { if (dev->chipset->name != NULL) free (dev->chipset->name); free (dev->chipset); dev->chipset = NULL; } } static SANE_Int Load_Chipset (struct st_device *dev) { SANE_Int rst = ERROR; DBG (DBG_FNC, "> Load_Chipset\n"); if (dev->chipset != NULL) Free_Chipset (dev); dev->chipset = malloc (sizeof (struct st_chip)); if (dev->chipset != NULL) { SANE_Int model; memset (dev->chipset, 0, sizeof (struct st_chip)); /* get chipset model of selected scanner */ model = cfg_chipset_model_get (RTS_Debug->dev_model); /* get configuration for selected chipset */ rst = cfg_chipset_get (model, dev->chipset); } /* if rst == ERROR may be related to allocating space for chipset name */ return rst; } static SANE_Int Load_Config (struct st_device *dev) { DBG (DBG_FNC, "+ Load_Config\n"); /* load chipset configuration */ Load_Chipset (dev); /* load scanner's buttons */ Load_Buttons (dev); /* load scanner area constrains */ Load_Constrains (dev); /* load motor general configuration */ Load_Motor (dev); /* load sensor main configuration */ Load_Sensor (dev); if (dev->sensorcfg->type == -1) /* get sensor from gpio */ dev->sensorcfg->type = RTS_Sensor_Type (dev->usb_handle); /* load ccd sensor timing tables */ Load_Timings (dev); /* load motor curves */ Load_MotorCurves (dev); /* load motor movements */ Load_Motormoves (dev); /* load scan modes */ Load_Scanmodes (dev); /* deprecated */ if (dev->sensorcfg->type == CCD_SENSOR) /* ccd */ usbfile = (RTS_Debug->usbtype == USB20) ? T_RTINIFILE : T_USB1INIFILE; else /* cis */ usbfile = (RTS_Debug->usbtype == USB20) ? S_RTINIFILE : S_USB1INIFILE; scantype = ST_NORMAL; pwmlamplevel = get_value (SCAN_PARAM, PWMLAMPLEVEL, 1, usbfile); arrangeline2 = get_value (SCAN_PARAM, ARRANGELINE, FIX_BY_HARD, usbfile); shadingbase = get_value (TRUE_GRAY_PARAM, SHADINGBASE, 3, usbfile); shadingfact[0] = get_value (TRUE_GRAY_PARAM, SHADINGFACT1, 1, usbfile); shadingfact[1] = get_value (TRUE_GRAY_PARAM, SHADINGFACT2, 1, usbfile); shadingfact[2] = get_value (TRUE_GRAY_PARAM, SHADINGFACT3, 1, usbfile); LoadImagingParams (dev, usbfile); DBG (DBG_FNC, "- Load_Config\n"); return OK; } static SANE_Int LoadImagingParams (struct st_device *dev, SANE_Int inifile) { DBG (DBG_FNC, "> LoadImagingParams(inifile='%i'):\n", inifile); scan.startpos = get_value (SCAN_PARAM, STARTPOS, 0, inifile); scan.leftleading = get_value (SCAN_PARAM, LEFTLEADING, 0, inifile); arrangeline = get_value (SCAN_PARAM, ARRANGELINE, FIX_BY_HARD, inifile); compression = get_value (SCAN_PARAM, COMPRESSION, 0, inifile); /* get default gain and offset values */ cfg_gainoffset_get (dev->sensorcfg->type, default_gain_offset); linedarlampoff = get_value (CALI_PARAM, LINEDARLAMPOFF, 0, inifile); pixeldarklevel = get_value (CALI_PARAM, PIXELDARKLEVEL, 0x00ffff, inifile); binarythresholdh = get_value (PLATFORM, BINARYTHRESHOLDH, 0x80, inifile); binarythresholdl = get_value (PLATFORM, BINARYTHRESHOLDL, 0x7f, inifile); return OK; } static SANE_Int Arrange_Colour (struct st_device *dev, SANE_Byte * buffer, SANE_Int buffer_size, SANE_Int * transferred) { /* 05F0FA78 04EC00D8 /CALL to Assumed StdFunc2 from hpgt3970.04EC00D3 05F0FA7C 05D10048 |Arg1 = 05D10048 05F0FA80 0000F906 \Arg2 = 0000F906 */ SANE_Int mydistance; SANE_Int Lines_Count; SANE_Int space; SANE_Int rst = OK; SANE_Int c; struct st_scanning *scn; DBG (DBG_FNC, "> Arrange_Colour(*buffer, buffer_size=%i, *transferred)\n", buffer_size); /* this is just to make code more legible */ scn = dev->scanning; if (scn->imagebuffer == NULL) { if (dev->sensorcfg->type == CCD_SENSOR) mydistance = (dev->sensorcfg->line_distance * scan2.resolution_y) / dev->sensorcfg->resolution; else mydistance = 0; /*aafa */ if (mydistance != 0) { scn->bfsize = (scn->arrange_hres == TRUE) ? scn->arrange_sensor_evenodd_dist : 0; scn->bfsize = (scn->bfsize + (mydistance * 2) + 1) * line_size; } else scn->bfsize = line_size * 2; /*ab3c */ space = (((scn->bfsize / line_size) * bytesperline) > scn->bfsize) ? (scn->bfsize / line_size) * bytesperline : scn->bfsize; scn->imagebuffer = (SANE_Byte *) malloc (space * sizeof (SANE_Byte)); if (scn->imagebuffer == NULL) return ERROR; scn->imagepointer = scn->imagebuffer; if (Read_Block (dev, scn->bfsize, scn->imagebuffer, transferred) != OK) return ERROR; scn->arrange_orderchannel = FALSE; scn->channel_size = (scan2.depth == 8) ? 1 : 2; /* Calculate channel displacements */ for (c = CL_RED; c <= CL_BLUE; c++) { if (mydistance == 0) { /*ab9b */ if (scn->arrange_hres == FALSE) { if ((((dev->sensorcfg->line_distance * scan2.resolution_y) * 2) / dev->sensorcfg->resolution) == 1) scn->arrange_orderchannel = TRUE; if (scn->arrange_orderchannel == TRUE) scn->desp[c] = ((dev->sensorcfg->rgb_order[c] / 2) * line_size) + (scn->channel_size * c); else scn->desp[c] = scn->channel_size * c; } } else { /*ac32 */ scn->desp[c] = (dev->sensorcfg->rgb_order[c] * (mydistance * line_size)) + (scn->channel_size * c); if (scn->arrange_hres == TRUE) { scn->desp1[c] = scn->desp[c]; scn->desp2[c] = ((scn->channel_size * 3) + scn->desp[c]) + (scn->arrange_sensor_evenodd_dist * line_size); } } } /*ace2 */ for (c = CL_RED; c <= CL_BLUE; c++) { if (scn->arrange_hres == TRUE) { scn->pColour2[c] = scn->imagebuffer + scn->desp2[c]; scn->pColour1[c] = scn->imagebuffer + scn->desp1[c]; } else scn->pColour[c] = scn->imagebuffer + scn->desp[c]; } } /*ad91 */ Lines_Count = buffer_size / line_size; while (Lines_Count > 0) { if (scn->arrange_orderchannel == FALSE) { if (scn->arrange_hres == TRUE) Triplet_Colour_HRes (scn->pColour1[CL_RED], scn->pColour1[CL_GREEN], scn->pColour1[CL_BLUE], scn->pColour2[CL_RED], scn->pColour2[CL_GREEN], scn->pColour2[CL_BLUE], buffer, line_size / (scn->channel_size * 3)); else Triplet_Colour_LRes (line_size / (scn->channel_size * 3), buffer, scn->pColour[CL_RED], scn->pColour[CL_GREEN], scn->pColour[CL_BLUE]); } else Triplet_Colour_Order (dev, scn->pColour[CL_RED], scn->pColour[CL_GREEN], scn->pColour[CL_BLUE], buffer, line_size / (scn->channel_size * 3)); scn->arrange_size -= bytesperline; if (scn->arrange_size < 0) v15bc--; buffer += line_size; Lines_Count--; if (Lines_Count == 0) { if ((scn->arrange_size | v15bc) == 0) return OK; } if (Read_Block (dev, line_size, scn->imagepointer, transferred) == ERROR) return ERROR; /* Update displacements */ for (c = CL_RED; c <= CL_BLUE; c++) { if (scn->arrange_hres == TRUE) { /*aeb7 */ scn->desp2[c] = (scn->desp2[c] + line_size) % scn->bfsize; scn->desp1[c] = (scn->desp1[c] + line_size) % scn->bfsize; scn->pColour2[c] = scn->imagebuffer + scn->desp2[c]; scn->pColour1[c] = scn->imagebuffer + scn->desp1[c]; } else { /*af86 */ scn->desp[c] = (scn->desp[c] + line_size) % scn->bfsize; scn->pColour[c] = scn->imagebuffer + scn->desp[c]; } } /*aff3 */ scn->imagepointer += line_size; if (scn->imagepointer >= (scn->imagebuffer + scn->bfsize)) scn->imagepointer = scn->imagebuffer; } return rst; } static SANE_Int RTS_Scanner_SetParams (struct st_device *dev, struct params *param) { SANE_Int rst = ERROR; DBG (DBG_FNC, "+ RTS_Scanner_SetParams:\n"); DBG (DBG_FNC, "-> param->resolution_x=%i\n", param->resolution_x); DBG (DBG_FNC, "-> param->resolution_y=%i\n", param->resolution_y); DBG (DBG_FNC, "-> param->left =%i\n", param->coords.left); DBG (DBG_FNC, "-> param->width =%i\n", param->coords.width); DBG (DBG_FNC, "-> param->top =%i\n", param->coords.top); DBG (DBG_FNC, "-> param->height =%i\n", param->coords.height); DBG (DBG_FNC, "-> param->colormode =%s\n", dbg_colour (param->colormode)); DBG (DBG_FNC, "-> param->scantype =%s\n", dbg_scantype (param->scantype)); DBG (DBG_FNC, "-> param->depth =%i\n", param->depth); DBG (DBG_FNC, "-> param->channel =%i\n", param->channel); /* validate area size to scan */ if ((param->coords.width != 0) && (param->coords.height != 0)) { SANE_Byte mybuffer[1]; struct st_hwdconfig hwdcfg; /* setting coordinates */ memcpy (&scan.coord, ¶m->coords, sizeof (struct st_coords)); /* setting resolution */ scan.resolution_x = param->resolution_x; scan.resolution_y = param->resolution_y; /* setting colormode and depth */ scan.colormode = param->colormode; scan.depth = (param->colormode == CM_LINEART) ? 8 : param->depth; /* setting color channel for non color scans */ scan.channel = _B0 (param->channel); arrangeline = FIX_BY_HARD; if ((scan.resolution_x == 2400) || ((scan.resolution_x == 4800))) { if (scan.colormode != CM_COLOR) { if (scan.colormode == CM_GRAY) { if (scan.channel == 3) arrangeline = FIX_BY_SOFT; } } else arrangeline = FIX_BY_SOFT; } /* setting scan type */ if ((param->scantype > 0) && (param->scantype < 4)) scan.scantype = param->scantype; else scan.scantype = ST_NORMAL; /* setting scanner lamp */ data_bitset (&dev->init_regs[0x146], 0x40, ((dev->sensorcfg->type == CIS_SENSOR) ? 0 : 1)); /* turn on appropriate lamp */ if (scan.scantype == ST_NORMAL) Lamp_Status_Set (dev, NULL, TRUE, FLB_LAMP); else Lamp_Status_Set (dev, NULL, TRUE, TMA_LAMP); mybuffer[0] = 0; if (RTS_IsExecuting (dev, mybuffer) == FALSE) RTS_WriteRegs (dev->usb_handle, dev->init_regs); if (scan.depth == 16) compression = FALSE; /* resetting low level config */ memset (&hwdcfg, 0, sizeof (struct st_hwdconfig)); /* setting low level config */ hwdcfg.scantype = scan.scantype; hwdcfg.calibrate = mybuffer[0]; hwdcfg.arrangeline = arrangeline; /*1 */ hwdcfg.highresolution = (scan.resolution_x > 1200) ? TRUE : FALSE; hwdcfg.sensorevenodddistance = dev->sensorcfg->evenodd_distance; SetScanParams (dev, dev->init_regs, &scan, &hwdcfg); scan.shadinglength = (((scan.sensorresolution * 17) / 2) + 3) & 0xfffffffc; rst = OK; } DBG (DBG_FNC, "- RTS_Scanner_SetParams: %i\n", rst); return rst; } static SANE_Int SetScanParams (struct st_device *dev, SANE_Byte * Regs, struct st_scanparams *scancfg, struct st_hwdconfig *hwdcfg) { struct st_coords mycoords; SANE_Int mycolormode; SANE_Int myvalue; SANE_Int mymode; SANE_Int channel_size; SANE_Int channel_count; SANE_Int dots_per_block; SANE_Int aditional_dots; DBG (DBG_FNC, "+ SetScanParams:\n"); dbg_ScanParams (scancfg); dbg_hwdcfg (hwdcfg); memset (&mycoords, 0, sizeof (struct st_coords)); /* Copy scancfg to scan2 */ memcpy (&scan2, scancfg, sizeof (struct st_scanparams)); mycolormode = scancfg->colormode; myvalue = scancfg->colormode; scantype = hwdcfg->scantype; if (scancfg->colormode == CM_LINEART) scan2.depth = 8; if ((scancfg->colormode != CM_COLOR) && (scancfg->channel == 3)) /*channel = 0x00 */ { if (scancfg->colormode == CM_GRAY) { mycolormode = (hwdcfg->arrangeline != FIX_BY_SOFT) ? 3 : CM_COLOR; } else mycolormode = 3; myvalue = mycolormode; } dev->Resize->resolution_x = scancfg->resolution_x; dev->Resize->resolution_y = scancfg->resolution_y; mymode = RTS_GetScanmode (dev, hwdcfg->scantype, myvalue, scancfg->resolution_x); /*0x0b */ if (mymode == -1) { /* Non supported resolution. We will resize image after scanning */ SANE_Int fitres; fitres = Scanmode_fitres (dev, hwdcfg->scantype, scancfg->colormode, scancfg->resolution_x); if (fitres != -1) { /* supported resolution found */ dev->Resize->type = RSZ_DECREASE; } else { dev->Resize->type = RSZ_INCREASE; fitres = Scanmode_maxres (dev, hwdcfg->scantype, scancfg->colormode); } scan2.resolution_x = fitres; scan2.resolution_y = fitres; mymode = RTS_GetScanmode (dev, hwdcfg->scantype, myvalue, scan2.resolution_x); if (mymode == -1) return ERROR; imageheight = scancfg->coord.height; dev->Resize->towidth = scancfg->coord.width; /* Calculate coords for new resolution */ mycoords.left = (scan2.resolution_x * scancfg->coord.left) / dev->Resize->resolution_x; mycoords.width = (scan2.resolution_x * scancfg->coord.width) / dev->Resize->resolution_x; mycoords.top = (scan2.resolution_y * scancfg->coord.top) / dev->Resize->resolution_y; mycoords.height = ((scan2.resolution_y * scancfg->coord.height) / dev->Resize->resolution_y) + 2; switch (scan2.colormode) { case CM_GRAY: if ((dev->scanmodes[mymode]->samplerate == PIXEL_RATE) && (mycolormode != 3)) dev->Resize->towidth *= 2; channel_size = (scan2.depth == 8) ? 1 : 2; dev->Resize->mode = (scan2.depth == 8) ? RSZ_GRAYL : RSZ_GRAYH; dev->Resize->bytesperline = dev->Resize->towidth * channel_size; break; case CM_LINEART: if (dev->scanmodes[mymode]->samplerate == PIXEL_RATE) dev->Resize->towidth *= 2; dev->Resize->mode = RSZ_LINEART; dev->Resize->bytesperline = (dev->Resize->towidth + 7) / 8; break; default: /*CM_COLOR */ channel_count = 3; channel_size = (scan2.depth == 8) ? 1 : 2; dev->Resize->mode = (scan2.depth == 8) ? RSZ_COLOURL : RSZ_COLOURH; dev->Resize->bytesperline = scancfg->coord.width * (channel_count * channel_size); break; } } else { /* Supported scanmode */ dev->Resize->type = RSZ_NONE; scan2.resolution_x = scancfg->resolution_x; scan2.resolution_y = scancfg->resolution_y; mycoords.left = scancfg->coord.left; mycoords.top = scancfg->coord.top; mycoords.width = scancfg->coord.width; mycoords.height = scancfg->coord.height; } scancfg->timing = dev->scanmodes[mymode]->timing; scan2.sensorresolution = dev->timings[scancfg->timing]->sensorresolution; if ((scantype > 0) && (scantype < 5)) scan2.shadinglength = (((scan2.sensorresolution * 17) / 2) + 3) & 0xfffffffc; scancfg->sensorresolution = scan2.sensorresolution; scancfg->shadinglength = scan2.shadinglength; dev->scanning->arrange_compression = ((mycolormode != CM_LINEART) && (scan2.depth <= 8)) ? hwdcfg->compression : FALSE; if ((arrangeline2 == FIX_BY_HARD) || (mycolormode == CM_LINEART)) arrangeline2 = mycolormode; /*¿? */ else if ((mycolormode == CM_GRAY) && (hwdcfg->highresolution == FALSE)) arrangeline2 = 0; if (hwdcfg->highresolution == FALSE) { /* resolution < 1200 dpi */ dev->scanning->arrange_hres = FALSE; dev->scanning->arrange_sensor_evenodd_dist = 0; } else { /* resolution > 1200 dpi */ dev->scanning->arrange_hres = TRUE; dev->scanning->arrange_sensor_evenodd_dist = hwdcfg->sensorevenodddistance; } /* with must be adjusted to fit in the dots count per block */ aditional_dots = 0; if (mycolormode != CM_LINEART) { dots_per_block = ((scan2.resolution_x > 2400) && (scancfg->samplerate == PIXEL_RATE)) ? 8 : 4; /* fit width */ if ((mycoords.width % dots_per_block) != 0) { aditional_dots = dots_per_block - (mycoords.width % dots_per_block); mycoords.width += aditional_dots; } } else { /* Lineart */ dots_per_block = 32 - (mycoords.width & 0x1f); if (dots_per_block < 32) { mycoords.width += dots_per_block; aditional_dots = (dots_per_block / 8); } } DBG (DBG_FNC, " -> dots_per_block: %i\n", dots_per_block); DBG (DBG_FNC, " -> aditional_dots: %i\n", aditional_dots); if (mycolormode == CM_LINEART) { bytesperline = (dev->scanmodes[mymode]->samplerate == PIXEL_RATE) ? mycoords.width / 4 : mycoords.width / 8; imagewidth3 = bytesperline; lineart_width = bytesperline * 8; line_size = bytesperline - aditional_dots; dev->Resize->fromwidth = line_size * 8; } else { /*4510 */ switch (mycolormode) { case CM_COLOR: channel_count = 3; break; case 3: channel_count = 1; break; case CM_GRAY: channel_count = (dev->scanmodes[mymode]->samplerate == PIXEL_RATE) ? 2 : 1; /*1 */ break; } channel_size = (scan2.depth == 8) ? 1 : 2; bytesperline = mycoords.width * (channel_count * channel_size); imagewidth3 = bytesperline / channel_count; lineart_width = imagewidth3 / channel_size; line_size = bytesperline - (aditional_dots * (channel_count * channel_size)); dev->Resize->fromwidth = line_size / (channel_count * channel_size); } imagesize = mycoords.height * bytesperline; v15b4 = 0; dev->scanning->arrange_size = imagesize; v15bc = 0; /* set resolution ratio */ data_bitset (&Regs[0xc0], 0x1f, scancfg->sensorresolution / scancfg->resolution_x); scancfg->coord.left = mycoords.left; scancfg->coord.top = mycoords.top; scancfg->coord.width = mycoords.width; scancfg->coord.height = mycoords.height; scancfg->resolution_x = scan2.resolution_x; scancfg->resolution_y = scan2.resolution_y; myvalue = (dev->Resize->type == RSZ_NONE) ? line_size : dev->Resize->bytesperline; scancfg->bytesperline = bytesperline; scancfg->v157c = myvalue; if (scan.colormode != CM_COLOR) { if (mycolormode == CM_COLOR) scancfg->v157c = (scancfg->v157c / 3); } if (scan.colormode == CM_LINEART) { if (mycolormode == 3) { scancfg->v157c = (scancfg->v157c + 7) / 8; scancfg->bytesperline = (scancfg->bytesperline + 7) / 8; } } DBG (DBG_FNC, "- SetScanParams:\n"); return OK; } static SANE_Int GainOffset_Counter_Save (struct st_device *dev, SANE_Byte data) { SANE_Int rst = OK; DBG (DBG_FNC, "> GainOffset_Counter_Save(data=%i):\n", data); /* check if chipset supports accessing eeprom */ if ((dev->chipset->capabilities & CAP_EEPROM) != 0) { data = min (data, 0x0f); rst = RTS_EEPROM_WriteByte (dev->usb_handle, 0x0077, data); } return rst; } static SANE_Int GainOffset_Counter_Inc (struct st_device *dev, SANE_Int * arg1) { SANE_Byte count; SANE_Int rst; DBG (DBG_FNC, "+ GainOffset_Counter_Inc:\n"); /* check if chipset supports accessing eeprom */ if ((dev->chipset->capabilities & CAP_EEPROM) != 0) { count = GainOffset_Counter_Load (dev); if ((count >= 0x0f) || (GainOffset_Get (dev) != OK)) { offset[CL_BLUE] = offset[CL_GREEN] = offset[CL_RED] = 0; gain[CL_BLUE] = gain[CL_GREEN] = gain[CL_RED] = 0; count = 0; } else { count++; if (arg1 != NULL) *arg1 = 1; } rst = GainOffset_Counter_Save (dev, count); } else rst = OK; DBG (DBG_FNC, "- GainOffset_Counter_Inc: %i\n", rst); return rst; } static SANE_Int GainOffset_Get (struct st_device *dev) { SANE_Int a, data, rst; SANE_Byte checksum; DBG (DBG_FNC, "+ GainOffset_Get:\n"); checksum = 0; /* check if chipset supports accessing eeprom */ if ((dev->chipset->capabilities & CAP_EEPROM) != 0) { /* get current checksum */ if (RTS_EEPROM_ReadByte (dev->usb_handle, 0x76, &checksum) == OK) { rst = OK; /* read gain and offset values from EEPROM */ for (a = CL_RED; a <= CL_BLUE; a++) { if (RTS_EEPROM_ReadWord (dev->usb_handle, 0x70 + (2 * a), &data) == ERROR) { rst = ERROR; break; } else offset[a] = data; } /* check checksum */ checksum = _B0 (checksum + offset[CL_GREEN] + offset[CL_BLUE] + offset[CL_RED]); } else rst = ERROR; } else rst = ERROR; /* extract gain and offset values */ if ((rst == OK) && (checksum == 0x5b)) { for (a = CL_RED; a <= CL_BLUE; a++) { gain[a] = (offset[a] >> 9) & 0x1f; offset[a] &= 0x01ff; } } else { /* null values, let's reset them */ for (a = CL_RED; a <= CL_BLUE; a++) { gain[a] = 0; offset[a] = 0; } rst = ERROR; } DBG (DBG_FNC, "-> Preview gainR=%i, gainG=%i, gainB=%i, offsetR=%i, offsetG=%i, offsetB=%i\n", gain[CL_RED], gain[CL_GREEN], gain[CL_BLUE], offset[CL_RED], offset[CL_GREEN], offset[CL_BLUE]); DBG (DBG_FNC, "- GainOffset_Get: %i\n", rst); return rst; } static SANE_Int Scanmode_maxres (struct st_device *dev, SANE_Int scantype, SANE_Int colormode) { /* returns position in scanmodes table where data fits with given arguments */ SANE_Int rst = 0; SANE_Int a; struct st_scanmode *reg; for (a = 0; a < dev->scanmodes_count; a++) { reg = dev->scanmodes[a]; if (reg != NULL) { if ((reg->scantype == scantype) && (reg->colormode == colormode)) rst = max (rst, reg->resolution); /* found ! */ } } if (rst == 0) { /* There isn't any mode for these arguments. Most devices doesn't support specific setup to scan in lineart mode so they use gray colormode. Lets check this case */ if (colormode == CM_LINEART) rst = Scanmode_maxres (dev, scantype, CM_GRAY); } DBG (DBG_FNC, "> Scanmode_maxres(scantype=%s, colormode=%s): %i\n", dbg_scantype (scantype), dbg_colour (colormode), rst); return rst; } static SANE_Int Scanmode_minres (struct st_device *dev, SANE_Int scantype, SANE_Int colormode) { /* returns position in scanmodes table where data fits with given arguments */ SANE_Int rst, a; struct st_scanmode *reg; rst = Scanmode_maxres (dev, scantype, colormode); for (a = 0; a < dev->scanmodes_count; a++) { reg = dev->scanmodes[a]; if (reg != NULL) { if ((reg->scantype == scantype) && (reg->colormode == colormode)) rst = min (rst, reg->resolution); /* found ! */ } } if (rst == 0) { /* There isn't any mode for these arguments. Most devices doesn't support specific setup to scan in lineart mode so they use gray colormode. Lets check this case */ if (colormode == CM_LINEART) rst = Scanmode_minres (dev, scantype, CM_GRAY); } DBG (DBG_FNC, "> Scanmode_minres(scantype=%s, colormode=%s): %i\n", dbg_scantype (scantype), dbg_colour (colormode), rst); return rst; } static SANE_Int Scanmode_fitres (struct st_device *dev, SANE_Int scantype, SANE_Int colormode, SANE_Int resolution) { /* returns a supported resolution */ SANE_Int rst; SANE_Int a, nullres; struct st_scanmode *reg; nullres = Scanmode_maxres (dev, scantype, colormode) + 1; rst = nullres; for (a = 0; a < dev->scanmodes_count; a++) { reg = dev->scanmodes[a]; if (reg != NULL) { if ((reg->scantype == scantype) && (reg->colormode == colormode)) { if ((reg->resolution < rst) && (resolution <= reg->resolution)) rst = reg->resolution; } } } if (rst == nullres) { /* There isn't any mode for these arguments. Most devices doesn't support specific setup to scan in lineart mode so they use gray colormode. Lets check this case */ if (colormode != CM_LINEART) { /* at this point, given resolution is bigger than maximum supported resolution */ rst = -1; } else rst = Scanmode_minres (dev, scantype, CM_GRAY); } DBG (DBG_FNC, "> Scanmode_fitres(scantype=%s, colormode=%s, resolution=%i): %i\n", dbg_scantype (scantype), dbg_colour (colormode), resolution, rst); return rst; } static SANE_Int RTS_GetScanmode (struct st_device *dev, SANE_Int scantype, SANE_Int colormode, SANE_Int resolution) { /* returns position in scanmodes table where data fits with given arguments */ SANE_Int rst = -1; SANE_Int a; struct st_scanmode *reg; for (a = 0; a < dev->scanmodes_count; a++) { reg = dev->scanmodes[a]; if (reg != NULL) { if ((reg->scantype == scantype) && (reg->colormode == colormode) && (reg->resolution == resolution)) { /* found ! */ rst = a; break; } } } if (rst == -1) { /* There isn't any mode for these arguments. May be given resolution isn't supported by chipset. Most devices doesn't support specific setup to scan in lineart mode so they use gray colormode. Lets check this case */ if ((colormode == CM_LINEART) || (colormode == 3)) rst = RTS_GetScanmode (dev, scantype, CM_GRAY, resolution); } DBG (DBG_FNC, "> RTS_GetScanmode(scantype=%s, colormode=%s, resolution=%i): %i\n", dbg_scantype (scantype), dbg_colour (colormode), resolution, rst); return rst; } static void Free_Motor (struct st_device *dev) { /* this function releases space for stepper motor */ DBG (DBG_FNC, "> Free_Motor\n"); if (dev->motorcfg != NULL) { free (dev->motorcfg); dev->motorcfg = NULL; } } static SANE_Int Load_Motor (struct st_device *dev) { /* this function loads general configuration for motor */ SANE_Int rst = ERROR; DBG (DBG_FNC, "> Load_Motor\n"); if (dev->motorcfg != NULL) Free_Motor (dev); dev->motorcfg = malloc (sizeof (struct st_motorcfg)); if (dev->motorcfg != NULL) { rst = cfg_motor_get (dev->motorcfg); dbg_motorcfg (dev->motorcfg); } return rst; } static void Free_Sensor (struct st_device *dev) { /* this function releases space for ccd sensor */ DBG (DBG_FNC, "> Free_Sensor\n"); if (dev->sensorcfg != NULL) { free (dev->sensorcfg); dev->sensorcfg = NULL; } } static void Free_Buttons (struct st_device *dev) { /* this function releases space for buttons */ DBG (DBG_FNC, "> Free_Buttons\n"); if (dev->buttons != NULL) { free (dev->buttons); dev->buttons = NULL; } } static SANE_Int Load_Buttons (struct st_device *dev) { /* this function loads configuration for ccd sensor */ SANE_Int rst = ERROR; DBG (DBG_FNC, "> Load_Buttons\n"); if (dev->buttons != NULL) Free_Buttons (dev); dev->buttons = malloc (sizeof (struct st_buttons)); if (dev->buttons != NULL) { rst = cfg_buttons_get (dev->buttons); dbg_buttons (dev->buttons); } return rst; } static SANE_Int Load_Sensor (struct st_device *dev) { /* this function loads configuration for ccd sensor */ SANE_Int rst = ERROR; DBG (DBG_FNC, "> Load_Sensor\n"); if (dev->sensorcfg != NULL) Free_Sensor (dev); dev->sensorcfg = malloc (sizeof (struct st_sensorcfg)); if (dev->sensorcfg != NULL) { rst = cfg_sensor_get (dev->sensorcfg); dbg_sensor (dev->sensorcfg); } return rst; } static void Free_Timings (struct st_device *dev) { /* this function frees all ccd sensor timing tables */ DBG (DBG_FNC, "> Free_Timings\n"); if (dev->timings != NULL) { if (dev->timings_count > 0) { SANE_Int a; for (a = 0; a < dev->timings_count; a++) if (dev->timings[a] != NULL) free (dev->timings[a]); dev->timings_count = 0; } free (dev->timings); dev->timings = NULL; } } static SANE_Int Load_Timings (struct st_device *dev) { SANE_Int rst = OK; SANE_Int a; struct st_timing reg, *tmg; DBG (DBG_FNC, "> Load_Timings\n"); if (dev->timings != NULL) Free_Timings (dev); a = 0; while ((cfg_timing_get (dev->sensorcfg->type, a, ®) == OK) && (rst == OK)) { tmg = (struct st_timing *) malloc (sizeof (struct st_timing)); if (tmg != NULL) { memcpy (tmg, ®, sizeof (struct st_timing)); dev->timings_count++; dev->timings = (struct st_timing **) realloc (dev->timings, sizeof (struct st_timing **) * dev->timings_count); if (dev->timings == NULL) { rst = ERROR; dev->timings_count = 0; } else dev->timings[dev->timings_count - 1] = tmg; } else rst = ERROR; a++; } if (rst == ERROR) Free_Timings (dev); DBG (DBG_FNC, " -> Found %i timing registers\n", dev->timings_count); return rst; } static SANE_Int IsScannerLinked (struct st_device *dev) { SANE_Int var2; SANE_Byte lamp; DBG (DBG_FNC, "+ IsScannerLinked:\n"); Read_FE3E (dev, &v1619); Init_USBData (dev); scan.scantype = ST_NORMAL; RTS_WaitInitEnd (dev, 0x30000); lamp = FLB_LAMP; /* Comprobar si es la primera conexión con el escaner */ if (Read_Word (dev->usb_handle, 0xe829, &var2) == OK) { SANE_Int firstconnection; #ifdef STANDALONE firstconnection = TRUE; #else firstconnection = (var2 == 0) ? TRUE : FALSE; #endif if (firstconnection == TRUE) { /* primera conexión */ SANE_Byte flb_lamp, tma_lamp; flb_lamp = 0; tma_lamp = 0; Lamp_Status_Get (dev, &flb_lamp, &tma_lamp); if ((flb_lamp == 0) && (tma_lamp != 0)) lamp = TMA_LAMP; /*Clear GainOffset count */ GainOffset_Clear (dev); GainOffset_Counter_Save (dev, 0); /* Clear AutoRef count */ Refs_Counter_Save (dev, 0); Buttons_Enable (dev); Lamp_Status_Timer_Set (dev, 13); } else lamp = (_B0 (var2) == 0) ? FLB_LAMP : TMA_LAMP; } if (RTS_Warm_Reset (dev) != OK) return ERROR; Head_ParkHome (dev, TRUE, dev->motorcfg->parkhomemotormove); Lamp_Status_Timer_Set (dev, 13); /* Use fixed pwm? */ if (RTS_Debug->use_fixed_pwm != FALSE) { Lamp_PWM_Save (dev, cfg_fixedpwm_get (dev->sensorcfg->type, ST_NORMAL)); /* Lets enable using fixed pwm */ Lamp_PWM_SaveStatus (dev, TRUE); } Lamp_PWM_Setup (dev, lamp); DBG (DBG_FNC, "- IsScannerLinked:\n"); return OK; } static SANE_Int Lamp_PWM_SaveStatus (struct st_device *dev, SANE_Byte status) { SANE_Byte mypwm; SANE_Int rst = OK; DBG (DBG_FNC, "+ Lamp_PWM_SaveStatus(status=%i):\n", status); /* check if chipset supports accessing eeprom */ if ((dev->chipset->capabilities & CAP_EEPROM) != 0) { rst = ERROR; if (RTS_EEPROM_ReadByte (dev->usb_handle, 0x007b, &mypwm) == OK) { mypwm = (status == FALSE) ? mypwm & 0x7f : mypwm | 0x80; if (RTS_EEPROM_WriteByte (dev->usb_handle, 0x007b, mypwm) == OK) rst = OK; } } DBG (DBG_FNC, "- Lamp_PWM_SaveStatus: %i\n", rst); return rst; } static SANE_Int Lamp_PWM_Save (struct st_device *dev, SANE_Int fixedpwm) { SANE_Int rst; DBG (DBG_FNC, "+ Lamp_PWM_Save(fixedpwm=%i):\n", fixedpwm); /* check if chipset supports accessing eeprom */ if ((dev->chipset->capabilities & CAP_EEPROM) != 0) rst = RTS_EEPROM_WriteByte (dev->usb_handle, 0x7b, ((fixedpwm << 2) | 0x80)); else rst = OK; DBG (DBG_FNC, "- Lamp_PWM_Save: %i\n", rst); return rst; } static SANE_Int Lamp_PWM_Setup (struct st_device *dev, SANE_Int lamp) { SANE_Int rst = OK; DBG (DBG_FNC, "+ Lamp_PWM_Setup(lamp=%s):\n", (lamp == FLB_LAMP) ? "FLB_LAMP" : "TMA_LAMP"); if (Lamp_PWM_use (dev, 1) == OK) { SANE_Int fixedpwm, currentpwd; currentpwd = 0; fixedpwm = cfg_fixedpwm_get (dev->sensorcfg->type, (lamp == FLB_LAMP) ? ST_NORMAL : ST_TA); if (Lamp_PWM_DutyCycle_Get (dev, ¤tpwd) == OK) { /* set duty cycle if current one is different */ if (currentpwd != fixedpwm) rst = Lamp_PWM_DutyCycle_Set (dev, fixedpwm); } else rst = Lamp_PWM_DutyCycle_Set (dev, fixedpwm); } DBG (DBG_FNC, "- Lamp_PWM_Setup: %i\n", rst); return rst; } static SANE_Int Lamp_PWM_DutyCycle_Get (struct st_device *dev, SANE_Int * data) { SANE_Byte a; SANE_Int rst = ERROR; DBG (DBG_FNC, "+ Lamp_PWM_DutyCycle_Get:\n"); if (Read_Byte (dev->usb_handle, 0xe948, &a) == OK) { *data = a & 0x3f; rst = OK; } DBG (DBG_FNC, "- Lamp_PWM_DutyCycle_Get = %i: %i\n", *data, rst); return rst; } static SANE_Int Lamp_PWM_DutyCycle_Set (struct st_device *dev, SANE_Int duty_cycle) { SANE_Byte *Regs; SANE_Int rst; DBG (DBG_FNC, "+ Lamp_PWM_DutyCycle_Set(duty_cycle=%i):\n", duty_cycle); rst = ERROR; Regs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte)); if (Regs != NULL) { if (RTS_ReadRegs (dev->usb_handle, Regs) == OK) { data_bitset (&Regs[0x148], 0x3f, duty_cycle); if (pwmlamplevel == 0) { data_bitset (&Regs[0x148], 0x40, 0); Regs[0x1e0] |= ((duty_cycle >> 1) & 0x40); } data_bitset (&dev->init_regs[0x148], 0x7f, Regs[0x148]); data_bitset (&dev->init_regs[0x1e0], 0x3f, Regs[0x1e0]); Write_Byte (dev->usb_handle, 0xe948, Regs[0x0148]); rst = Write_Byte (dev->usb_handle, 0xe9e0, Regs[0x01e0]); } free (Regs); } DBG (DBG_FNC, "- Lamp_PWM_DutyCycle_Set: %i\n", rst); return rst; } static SANE_Int Head_ParkHome (struct st_device *dev, SANE_Int bWait, SANE_Int movement) { SANE_Int rst = ERROR; SANE_Byte *Regs; DBG (DBG_FNC, "+ Head_ParkHome(bWait=%i, movement=%i):\n", bWait, movement); Regs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte)); if (Regs != NULL) { rst = OK; memcpy (Regs, dev->init_regs, RT_BUFFER_LEN * sizeof (SANE_Byte)); /* Lets wait if it's required when is already executing */ if (bWait != FALSE) { if (RTS_WaitScanEnd (dev, 0x3a98) != OK) { DBG (DBG_FNC, " -> Head_ParkHome: RTS_WaitScanEnd Timeout\n"); rst = ERROR; /* timeout */ } } else { if (RTS_IsExecuting (dev, Regs) == FALSE) { DBG (DBG_FNC, " -> Head_ParkHome: RTS_IsExecuting = 0, exiting function\n"); rst = ERROR; /* if NOT executing */ } } /* Check if lamp is at home */ if ((rst == OK) && (Head_IsAtHome (dev, Regs) == FALSE)) { struct st_motormove mymotor; struct st_motorpos mtrpos; DBG (DBG_FNC, "-> Head_ParkHome: Lamp is not at home, lets move\n"); /* it isn't */ dev->status->parkhome = TRUE; if ((movement != -1) && (movement < dev->motormove_count)) { memcpy (&mymotor, dev->motormove[movement], sizeof (struct st_motormove)); } else { /* debug this code. Shouldn't have any relationship with offsets */ if (default_gain_offset->edcg2[CL_BLUE] < 4) mymotor.scanmotorsteptype = default_gain_offset->edcg2[CL_BLUE]; mymotor.ctpc = default_gain_offset->odcg2[1]; mymotor.systemclock = default_gain_offset->edcg2[1]; /*? */ } mtrpos.options = MTR_ENABLED | MTR_BACKWARD; mtrpos.v12e448 = 0x01; mtrpos.v12e44c = 0x00; mtrpos.coord_y = 0x4e20; Motor_Move (dev, Regs, &mymotor, &mtrpos); /* Should we wait? */ if (bWait != FALSE) rst = RTS_WaitScanEnd (dev, 15000); dev->status->parkhome = FALSE; } free (Regs); } DBG (DBG_FNC, "- Head_ParkHome: %i:\n", rst); return rst; } static SANE_Int Motor_Move (struct st_device *dev, SANE_Byte * Regs, struct st_motormove *mymotor, struct st_motorpos *mtrpos) { SANE_Byte *cpRegs; SANE_Int rst; DBG (DBG_FNC, "+ Motor_Move:\n"); rst = ERROR; cpRegs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte)); if (cpRegs != NULL) { SANE_Int data, v12dcf8, coord_y, step_type; memcpy (cpRegs, Regs, RT_BUFFER_LEN * sizeof (SANE_Byte)); v12dcf8 = 0; /* resolution = 1 dpi */ data_bitset (&cpRegs[0xc0], 0x1f, 1); /*---xxxxx*/ /* set motor step type */ data_bitset (&cpRegs[0xd9], 0x70, mymotor->scanmotorsteptype); /*-xxx----*/ /* set motor direction (polarity) */ data_bitset (&cpRegs[0xd9], 0x80, mtrpos->options >> 3); /*e------- */ /* next value doesn't seem to have any effect */ data_bitset (&cpRegs[0xd9], 0x0f, mtrpos->options); /*----efgh*/ /* 0 enable/1 disable motor */ data_bitset (&cpRegs[0xdd], 0x80, mtrpos->options >> 4); /*d------- */ /* next value doesn't seem to have any effect */ data_bitset (&cpRegs[0xdd], 0x40, mtrpos->options >> 4); /*-d------*/ switch (mymotor->scanmotorsteptype) { case STT_OCT: step_type = 8; break; case STT_QUART: step_type = 4; break; case STT_HALF: step_type = 2; break; case STT_FULL: step_type = 1; break; default: step_type = 0; break; /* shouldn't be used */ } coord_y = (mtrpos->coord_y * step_type) & 0xffff; if (coord_y < 2) coord_y = 2; /* Sets dummyline to 1 */ data_bitset (&cpRegs[0xd6], 0xf0, 1); /* set step_size - 1 */ cpRegs[0xe0] = 0; cpRegs[0x01] &= 0xf9; cpRegs[0x01] |= (mtrpos->v12e448 & 1) << 2; /* set dummy scan */ data_bitset (&cpRegs[0x01], 0x10, 1); /*---x----*/ /* set samplerate */ data_bitset (&cpRegs[0x1cf], 0x40, PIXEL_RATE); /*-x------*/ /* unknown data */ data_bitset (&cpRegs[0x1cf], 0x80, 1); /*x------- */ /* sets one channel per color */ data_bitset (&cpRegs[0x12], 0x3f, 0); /* channel */ data_bitset (&cpRegs[0x12], 0xc0, 1); /* 1 channel */ /* timing cnpp */ data_bitset (&cpRegs[0x96], 0x3f, 0x0b); /*--001011*/ /* set systemclock */ data_bitset (&cpRegs[0x00], 0x0f, mymotor->systemclock); /*----xxxx*/ /* set last step of accurve.smearing table to 2 */ data_lsb_set (&cpRegs[0xe4], 2, 3); /* set last step of deccurve.scanbufferfull table to 16 */ data_lsb_set (&Regs[0xea], 0x10, 3); /* set last step of deccurve.normalscan table to 16 */ data_lsb_set (&Regs[0xed], 0x10, 3); /* set last step of deccurve.smearing table to 16 */ data_lsb_set (&Regs[0xf0], 0x10, 3); /* set last step of deccurve.parkhome table to 16 */ data_lsb_set (&Regs[0xf3], 0x10, 3); /* set msi */ cpRegs[0xda] = 2; cpRegs[0xdd] &= 0xfc; /* set if motor has motorcurves */ data_bitset (&cpRegs[0xdf], 0x10, ((mymotor->motorcurve != -1) ? 1 : 0)); if (mymotor->motorcurve != -1) { struct st_curve *crv; /* set last step of accurve.normalscan table */ crv = Motor_Curve_Get (dev, mymotor->motorcurve, ACC_CURVE, CRV_NORMALSCAN); if (crv != NULL) data_lsb_set (&cpRegs[0xe1], crv->step[crv->step_count - 1], 3); DBG (DBG_FNC, " -> Setting up stepper motor using motorcurve %i\n", mymotor->motorcurve); v12dcf8 = Motor_Setup_Steps (dev, cpRegs, mymotor->motorcurve); /* set step_size - 1 */ cpRegs[0xe0] = 0; crv = Motor_Curve_Get (dev, mymotor->motorcurve, DEC_CURVE, CRV_NORMALSCAN); if (crv != NULL) coord_y -= (v12dcf8 + crv->step_count); /* set line exposure time */ data_lsb_set (&cpRegs[0x30], mymotor->ctpc, 3); /* set last step of accurve.smearing table */ data_lsb_set (&cpRegs[0xe4], 0, 3); } else { /* Setting some motor step */ SANE_Int some_step; switch (Regs[0x00] & 0x0f) { case 0x00: some_step = 0x00895440; break; /* 3 x 0x2DC6C0 */ case 0x08: case 0x01: some_step = 0x00b71b00; break; /* 4 x 0x2DC6C0 */ case 0x02: some_step = 0x0112a880; break; /* 6 x 0x2DC6C0 */ case 0x0a: case 0x03: some_step = 0x016e3600; break; /* 8 x 0x2DC6C0 */ case 0x04: some_step = 0x02255100; break; /* 12 x 0x2DC6C0 */ case 0x0c: some_step = 0x02dc6c00; break; /* 16 x 0x2DC6C0 */ case 0x05: some_step = 0x044aa200; break; /* 24 x 0x2DC6C0 */ case 0x0d: some_step = 0x05b8d800; break; /* 32 x 0x2DC6C0 */ case 0x09: some_step = 0x00f42400; break; case 0x0b: some_step = 0x01e84800; break; /* = case 9 * 2 */ default: some_step = 0x0478f7f8; break; } /* divide by timing.cnpp */ some_step /= ((cpRegs[0x96] & 0x3f) + 1); if (mymotor->ctpc > 0) some_step /= mymotor->ctpc; /* set line exposure time */ data_lsb_set (&cpRegs[0x30], some_step, 3); /* set last step of accurve.normalscan table */ data_lsb_set (&cpRegs[0xe1], some_step, 3); } /* Setting coords */ RTS_Setup_Coords (cpRegs, 100, coord_y - 1, 800, 1); /* enable head movement */ data_bitset (&cpRegs[0xd8], 0x80, 1); /* release motor before executing */ Motor_Release (dev); RTS_Warm_Reset (dev); /* action! */ data = RTS_WriteRegs (dev->usb_handle, cpRegs); if (data == OK) RTS_Execute (dev); /* wait 10 seconds */ RTS_WaitScanEnd (dev, 10000); rst = (data != OK) ? v12dcf8 : RTS_WaitScanEnd (dev, 20000); free (cpRegs); } DBG (DBG_FNC, "- Motor_Move: %i\n", rst); return rst; } static void Free_Motormoves (struct st_device *dev) { DBG (DBG_FNC, "> Free_Motormoves\n"); if (dev->motormove != NULL) { SANE_Int a; struct st_motormove *ms; for (a = 0; a < dev->motormove_count; a++) { ms = dev->motormove[a]; if (ms != NULL) free (ms); } free (dev->motormove); dev->motormove = NULL; } dev->motormove_count = 0; } static void Free_MotorCurves (struct st_device *dev) { DBG (DBG_FNC, "> Free_MotorCurves\n"); if (dev->mtrsetting != NULL) Motor_Curve_Free (dev->mtrsetting, &dev->mtrsetting_count); dev->mtrsetting = NULL; dev->mtrsetting_count = 0; } static SANE_Int Load_MotorCurves (struct st_device *dev) { SANE_Int rst = ERROR; SANE_Int *mtc = NULL; if (dev->mtrsetting != NULL) Free_MotorCurves (dev); DBG (DBG_FNC, "> Load_MotorCurves\n"); /* get motor settings buffer for this device */ mtc = cfg_motorcurve_get (); if (mtc != NULL) { /* parse buffer to get all motorcurves */ dev->mtrsetting = Motor_Curve_Parse (&dev->mtrsetting_count, mtc); if (dev->mtrsetting != NULL) rst = OK; } if (rst != ERROR) { DBG (DBG_FNC, " -> Found %i motor settings\n", dev->mtrsetting_count); dbg_motorcurves (dev); } else DBG (DBG_ERR, "- Load_MotorCurves error!!\n"); return rst; } static SANE_Int Load_Motormoves (struct st_device *dev) { SANE_Int rst = OK; SANE_Int a; struct st_motormove reg, *mm; DBG (DBG_FNC, "> Load_Motormoves\n"); /* if there is already any movement loaded let's free it */ if (dev->motormove != NULL) Free_Motormoves (dev); a = 0; while ((cfg_motormove_get (dev->sensorcfg->type, a, ®) != ERROR) && (rst == OK)) { dev->motormove_count++; dev->motormove = (struct st_motormove **) realloc (dev->motormove, sizeof (struct st_motormove **) * dev->motormove_count); if (dev->motormove != NULL) { mm = (struct st_motormove *) malloc (sizeof (struct st_motormove)); if (mm != NULL) { memcpy (mm, ®, sizeof (struct st_motormove)); dev->motormove[dev->motormove_count - 1] = mm; } else rst = ERROR; } else rst = ERROR; a++; } if (rst == ERROR) Free_Motormoves (dev); DBG (DBG_FNC, " -> Found %i motormoves\n", dev->motormove_count); dbg_motormoves (dev); return rst; } static SANE_Byte * Motor_AddStep (SANE_Byte * steps, SANE_Int * bwriten, SANE_Int step) { steps = (SANE_Byte *) realloc (steps, sizeof (SANE_Byte) * (*bwriten + 3)); if (steps != NULL) { data_msb_set (&steps[*bwriten], step, 3); *bwriten += 3; } else *bwriten = 0; return steps; } static SANE_Int Motor_Setup_Steps (struct st_device *dev, SANE_Byte * Regs, SANE_Int mysetting) { SANE_Int varx10, cont, last_acc_step, varx20, stepbuffer_size, mystep, bwriten; SANE_Int myvar, var1, myvalor, mybwriten; struct st_curve *mycurve; SANE_Byte *steps; DBG (DBG_FNC, "+ Motor_Setup_Steps(*Regs, motorsetting=%i):\n", mysetting); varx10 = 0; cont = 0; varx20 = 0; stepbuffer_size = (v15f8 << 4) & 0xffff; steps = NULL; bwriten = 0; deccurvecount = 0; acccurvecount = 0; last_acc_step = 0; /* mycurve points to acc normalscan steps table */ mycurve = Motor_Curve_Get (dev, mysetting, ACC_CURVE, CRV_NORMALSCAN); if (mycurve != NULL) { /* acccurvecount has the number of steps in acc normalscan table */ acccurvecount = mycurve->step_count; /* get last acccurve step from acc.normalscan step table */ last_acc_step = data_lsb_get (&Regs[0xe1], 3); /* sets pointer to acc.normalscan step table */ data_wide_bitset (&Regs[0xf6], 0x3fff, stepbuffer_size); /* Separate each step in three bytes */ if (mycurve->step_count > 0) for (cont = 0; cont < mycurve->step_count; cont++) { mystep = mycurve->step[cont]; if (mystep <= last_acc_step) { acccurvecount = cont; break; } varx20 += mystep + 1; steps = Motor_AddStep (steps, &bwriten, mystep); } } if (acccurvecount == 0) { /* Write one step (last_acc_step + 0x01) to buffer */ acccurvecount++; varx20 += (last_acc_step + 1) + 1; steps = Motor_AddStep (steps, &bwriten, last_acc_step + 1); } /* write another step (last_acc_step) */ acccurvecount++; varx20 += last_acc_step + 1; steps = Motor_AddStep (steps, &bwriten, last_acc_step); /* get line exposure time */ myvar = data_lsb_get (&Regs[0x30], 3) + 1; var1 = (varx20 + myvar - 1) / myvar; var1 = ((var1 * myvar) + mycurve->step[0] - varx20) - 0x0d; if (steps != NULL) data_msb_set (&steps[0], var1, 3); /* dec.scanbufferfull step table */ /* set pointer to next table */ stepbuffer_size += (acccurvecount * 3); data_wide_bitset (&Regs[0xf8], 0x3fff, stepbuffer_size); /* set last step of deccurve.scanbufferfull table */ mycurve = Motor_Curve_Get (dev, mysetting, DEC_CURVE, CRV_BUFFERFULL); deccurvecount = mycurve->step_count; data_lsb_set (&Regs[0xea], mycurve->step[mycurve->step_count - 1], 3); /* write another step mycurve->step_count,cont,last_acc_step */ deccurvecount++; steps = Motor_AddStep (steps, &bwriten, last_acc_step); /* Separate each step in three bytes */ if (mycurve->step_count > 1) for (cont = 0; cont < (mycurve->step_count - 1); cont++) { mystep = mycurve->step[cont]; if (mystep > last_acc_step) steps = Motor_AddStep (steps, &bwriten, mystep); else deccurvecount--; } myvalor = dev->mtrsetting[mysetting]->motorbackstep; if (myvalor > 0) { SANE_Int step_size = _B0 (Regs[0xe0]) + 1; myvalor = ((myvalor - deccurvecount) - acccurvecount) + 2; varx10 = myvalor; myvalor /= step_size; myvalor *= step_size; var1 = mycurve->step[mycurve->step_count - 1]; /* last deccurve step */ if (last_acc_step >= var1) var1 = last_acc_step + 1; deccurvecount += (varx10 - myvalor); myvalor = varx10 - myvalor; } else myvalor = varx10; if (myvalor > 0) for (cont = myvalor; cont > 0; cont--) steps = Motor_AddStep (steps, &bwriten, var1 - 1); /* write another step , bwriten tiene 4b */ steps = Motor_AddStep (steps, &bwriten, var1); /* acc.smearing step table */ if (Motor_Curve_Get (dev, mysetting, ACC_CURVE, CRV_SMEARING) != NULL) { /* acc.smearing curve enabled */ if (Motor_Curve_Equal (dev, mysetting, ACC_CURVE, CRV_SMEARING, CRV_NORMALSCAN) == TRUE) { /* acc.smearing pointer points to acc.normalscan table */ data_wide_bitset (&Regs[0xfa], 0x3fff, data_lsb_get (&Regs[0xf6], 2)); /* last step of acc.smearing table is the same as acc.normalscan */ data_lsb_set (&Regs[0xe4], data_lsb_get (&Regs[0xe1], 3), 3); } else { /* set pointer to next step table */ stepbuffer_size += (deccurvecount * 3); data_wide_bitset (&Regs[0xfa], 0x3fff, stepbuffer_size); /* set last step of acc.smearing table */ mycurve = Motor_Curve_Get (dev, mysetting, ACC_CURVE, CRV_SMEARING); if (mycurve != NULL) { smearacccurvecount = mycurve->step_count; data_lsb_set (&Regs[0xe4], mycurve->step[mycurve->step_count - 1], 3); /* generate acc.smearing table */ if (mycurve->step_count > 0) for (cont = 0; cont < mycurve->step_count; cont++) steps = Motor_AddStep (steps, &bwriten, mycurve->step[cont]); } } } else { /* acc.smearing curve disabled */ data_wide_bitset (&Regs[0xfa], 0x3fff, 0); } /* dec.smearing */ if (Motor_Curve_Get (dev, mysetting, DEC_CURVE, CRV_SMEARING) != NULL) { /* dec.smearing curve enabled */ if (Motor_Curve_Equal (dev, mysetting, DEC_CURVE, CRV_SMEARING, CRV_BUFFERFULL) == TRUE) { /* dec.smearing pointer points to dec.scanbufferfull table */ data_wide_bitset (&Regs[0x00fc], 0x3fff, data_lsb_get (&Regs[0x00f8], 2)); /* last step of dec.smearing table is the same as dec.scanbufferfull */ data_lsb_set (&Regs[0x00f0], data_lsb_get (&Regs[0x00ea], 3), 3); } else { /* set pointer to next step table */ if (mycurve != NULL) stepbuffer_size += (mycurve->step_count * 3); data_wide_bitset (&Regs[0xfc], 0x3fff, stepbuffer_size); /* set last step of dec.smearing table */ mycurve = Motor_Curve_Get (dev, mysetting, DEC_CURVE, CRV_SMEARING); if (mycurve != NULL) { smeardeccurvecount = mycurve->step_count; data_lsb_set (&Regs[0xf0], mycurve->step[mycurve->step_count - 1], 3); /* generate dec.smearing table */ if (mycurve->step_count > 0) for (cont = 0; cont < mycurve->step_count; cont++) steps = Motor_AddStep (steps, &bwriten, mycurve->step[cont]); } } } else { /* dec.smearing curve disabled */ data_wide_bitset (&Regs[0x00fc], 0x3fff, 0); } /* dec.normalscan */ if (Motor_Curve_Get (dev, mysetting, DEC_CURVE, CRV_NORMALSCAN) != NULL) { /* dec.normalscan enabled */ if (Motor_Curve_Equal (dev, mysetting, DEC_CURVE, CRV_NORMALSCAN, CRV_BUFFERFULL) == TRUE) { /* dec.normalscan pointer points to dec.scanbufferfull table */ data_wide_bitset (&Regs[0xfe], 0x3fff, data_lsb_get (&Regs[0xf8], 2)); /* last step of dec.normalscan table is the same as dec.scanbufferfull */ data_lsb_set (&Regs[0xed], data_lsb_get (&Regs[0xea], 3), 3); } else if (Motor_Curve_Equal (dev, mysetting, DEC_CURVE, CRV_NORMALSCAN, CRV_SMEARING) == TRUE) { /* dec.normalscan pointer points to dec.smearing table */ data_wide_bitset (&Regs[0xfe], 0x3fff, data_lsb_get (&Regs[0xfc], 2)); /* last step of dec.normalscan table is the same as dec.smearing */ data_lsb_set (&Regs[0xed], data_lsb_get (&Regs[0xf0], 3), 3); } else { /* set pointer to next step table */ if (mycurve != NULL) stepbuffer_size += (mycurve->step_count * 3); data_wide_bitset (&Regs[0xfe], 0x3fff, stepbuffer_size); /* set last step of dec.normalscan table */ mycurve = Motor_Curve_Get (dev, mysetting, DEC_CURVE, CRV_NORMALSCAN); if (mycurve != NULL) { data_lsb_set (&Regs[0xed], mycurve->step[mycurve->step_count - 1], 3); /* generate dec.normalscan table */ if (mycurve->step_count > 0) for (cont = 0; cont < mycurve->step_count; cont++) steps = Motor_AddStep (steps, &bwriten, mycurve->step[cont]); } } } else { /* dec.normalscan disabled */ data_wide_bitset (&Regs[0xfe], 0x3fff, 0); } /* acc.parkhome */ if (Motor_Curve_Get (dev, mysetting, ACC_CURVE, CRV_PARKHOME) != NULL) { /* parkhome curve enabled */ if (Motor_Curve_Equal (dev, mysetting, ACC_CURVE, CRV_PARKHOME, CRV_NORMALSCAN) == TRUE) { /* acc.parkhome pointer points to acc.normalscan table */ data_wide_bitset (&Regs[0x100], 0x3fff, data_lsb_get (&Regs[0xf6], 2)); /* last step of acc.parkhome table is the same as acc.normalscan */ data_lsb_set (&Regs[0xe7], data_lsb_get (&Regs[0xe1], 3), 3); } else if (Motor_Curve_Equal (dev, mysetting, ACC_CURVE, CRV_PARKHOME, CRV_SMEARING) == TRUE) { /* acc.parkhome pointer points to acc.smearing table */ data_wide_bitset (&Regs[0x100], 0x3fff, data_lsb_get (&Regs[0xfa], 2)); /* last step of acc.parkhome table is the same as acc.smearing */ data_lsb_set (&Regs[0xe7], data_lsb_get (&Regs[0xe4], 3), 3); } else { /* set pointer to next step table */ if (mycurve != NULL) stepbuffer_size += (mycurve->step_count * 3); data_wide_bitset (&Regs[0x100], 0x3fff, stepbuffer_size); /* set last step of acc.parkhome table */ mycurve = Motor_Curve_Get (dev, mysetting, ACC_CURVE, CRV_PARKHOME); if (mycurve != NULL) { data_lsb_set (&Regs[0xe7], mycurve->step[mycurve->step_count - 1], 3); /* generate acc.parkhome table */ if (mycurve->step_count > 0) for (cont = 0; cont < mycurve->step_count; cont++) steps = Motor_AddStep (steps, &bwriten, mycurve->step[cont]); } } } else { /* parkhome curve is disabled */ /* acc.parkhome pointer points to 0 */ data_wide_bitset (&Regs[0x100], 0x3fff, 0); data_lsb_set (&Regs[0xe7], 16, 3); } /* dec.parkhome */ if (Motor_Curve_Get (dev, mysetting, DEC_CURVE, CRV_PARKHOME) != NULL) { /* parkhome curve enabled */ if (Motor_Curve_Equal (dev, mysetting, DEC_CURVE, CRV_PARKHOME, CRV_BUFFERFULL) == TRUE) { /* dec.parkhome pointer points to dec.scanbufferfull table */ data_wide_bitset (&Regs[0x102], 0x3fff, data_lsb_get (&Regs[0xf8], 2)); /* last step of dec.parkhome table is the same as dec.scanbufferfull */ data_lsb_set (&Regs[0xf3], data_lsb_get (&Regs[0xe4], 3), 3); } else if (Motor_Curve_Equal (dev, mysetting, DEC_CURVE, CRV_PARKHOME, CRV_SMEARING) == TRUE) { /* dec.parkhome pointer points to dec.smearing table */ data_wide_bitset (&Regs[0x102], 0x3fff, data_lsb_get (&Regs[0xfe], 2)); /* last step of dec.parkhome table is the same as dec.smearing */ data_lsb_set (&Regs[0xf3], data_lsb_get (&Regs[0xf0], 3), 3); } else if (Motor_Curve_Equal (dev, mysetting, DEC_CURVE, CRV_PARKHOME, CRV_NORMALSCAN) == TRUE) { /* dec.parkhome pointer points to dec.normalscan table */ data_wide_bitset (&Regs[0x102], 0x3fff, data_lsb_get (&Regs[0xfe], 2)); /* last step of dec.parkhome table is the same as dec.normalscan */ data_lsb_set (&Regs[0xf3], data_lsb_get (&Regs[0xed], 3), 3); } else { /* set pointer to next step table */ if (mycurve != NULL) stepbuffer_size += (mycurve->step_count * 3); data_wide_bitset (&Regs[0x102], 0x3fff, stepbuffer_size); /* set last step of dec.parkhome table */ mycurve = Motor_Curve_Get (dev, mysetting, DEC_CURVE, CRV_PARKHOME); if (mycurve != NULL) { data_lsb_set (&Regs[0xf3], mycurve->step[mycurve->step_count - 1], 3); /* generate dec.parkhome table */ if (mycurve->step_count > 0) for (cont = 0; cont < mycurve->step_count; cont++) steps = Motor_AddStep (steps, &bwriten, mycurve->step[cont]); } } } else { /* parkhome curve is disabled */ /* dec.parkhome pointer points to 0 */ data_wide_bitset (&Regs[0x102], 0x3fff, 0); data_lsb_set (&Regs[0xf3], 16, 3); } mybwriten = bwriten & 0x8000000f; if (mybwriten < 0) mybwriten = ((mybwriten - 1) | 0xfffffff0) + 1; if (mybwriten != 0) bwriten = (((bwriten & 0xffff) >> 0x04) + 1) << 0x04; bwriten = bwriten & 0xffff; /* display table */ DBG (DBG_FNC, " -> Direction Type Offset Last step\n"); DBG (DBG_FNC, " -> --------- -------------- ------ ---------\n"); DBG (DBG_FNC, " -> ACC_CURVE CRV_NORMALSCAN %6i %6i\n", data_lsb_get (&Regs[0x0f6], 2) & 0x3fff, data_lsb_get (&Regs[0x0e1], 3)); DBG (DBG_FNC, " -> ACC_CURVE CRV_SMEARING %6i %6i\n", data_lsb_get (&Regs[0x0fa], 2) & 0x3fff, data_lsb_get (&Regs[0x0e4], 3)); DBG (DBG_FNC, " -> ACC_CURVE CRV_PARKHOME %6i %6i\n", data_lsb_get (&Regs[0x100], 2) & 0x3fff, data_lsb_get (&Regs[0x0e7], 3)); DBG (DBG_FNC, " -> DEC_CURVE CRV_NORMALSCAN %6i %6i\n", data_lsb_get (&Regs[0x0fe], 2) & 0x3fff, data_lsb_get (&Regs[0x0ed], 3)); DBG (DBG_FNC, " -> DEC_CURVE CRV_SMEARING %6i %6i\n", data_lsb_get (&Regs[0x0fc], 2) & 0x3fff, data_lsb_get (&Regs[0x0f0], 3)); DBG (DBG_FNC, " -> DEC_CURVE CRV_PARKHOME %6i %6i\n", data_lsb_get (&Regs[0x102], 2) & 0x3fff, data_lsb_get (&Regs[0x0f3], 3)); DBG (DBG_FNC, " -> DEC_CURVE CRV_BUFFERFULL %6i %6i\n", data_lsb_get (&Regs[0x0f8], 2) & 0x3fff, data_lsb_get (&Regs[0x0ea], 3)); RTS_Warm_Reset (dev); /* send motor steps */ if (steps != NULL) { if (bwriten > 0) { /* lock */ SetLock (dev->usb_handle, Regs, TRUE); /* send steps */ RTS_DMA_Write (dev, 0x0000, v15f8, bwriten, steps); /* unlock */ SetLock (dev->usb_handle, Regs, FALSE); } free (steps); } DBG (DBG_FNC, "- Motor_Setup_Steps: %i\n", acccurvecount); return acccurvecount; } static SANE_Int Lamp_PWM_use (struct st_device *dev, SANE_Int enable) { SANE_Byte a, b; SANE_Int rst = ERROR; DBG (DBG_FNC, "+ Lamp_PWM_use(enable=%i):\n", enable); if (Read_Byte (dev->usb_handle, 0xe948, &a) == OK) { if (Read_Byte (dev->usb_handle, 0xe9e0, &b) == OK) { if (enable != 0) { if (pwmlamplevel != 0x00) { b |= 0x80; dev->init_regs[0x01e0] &= 0x3f; dev->init_regs[0x01e0] |= 0x80; } else { a |= 0x40; b &= 0x3f; dev->init_regs[0x0148] |= 0x40; dev->init_regs[0x01e0] &= 0x3f; } } else { b &= 0x7f; a &= 0xbf; } if (Write_Byte (dev->usb_handle, 0xe948, a) == OK) rst = Write_Byte (dev->usb_handle, 0xe9e0, b); } } DBG (DBG_FNC, "- Lamp_PWM_use: %i\n", rst); return rst; } static SANE_Int SSCG_Enable (struct st_device *dev) { SANE_Int rst; SANE_Int sscg; SANE_Byte data1, data2; SANE_Int enable, mode, clock; DBG (DBG_FNC, "+ SSCG_Enable:\n"); rst = cfg_sscg_get (&enable, &mode, &clock); if (rst == OK) { if ((Read_Byte (dev->usb_handle, 0xfe3a, &data1) == OK) && (Read_Byte (dev->usb_handle, 0xfe39, &data2) == OK)) { if (enable != FALSE) { /* clock values: 0=0.25%; 1=0.50%; 2=0.75%; 3=1.00% */ data2 = (mode == 0) ? data2 & 0x7f : data2 | 0x80; sscg = (data1 & 0xf3) | (((clock & 0x03) | 0x04) << 0x02); sscg = (sscg << 8) | data2; } else sscg = ((data1 & 0xef) << 8) | _B0 (data2); rst = Write_Word (dev->usb_handle, 0xfe39, sscg); } else rst = ERROR; } DBG (DBG_FNC, "- SSCG_Enable: %i\n", rst); return rst; } static void RTS_Setup_RefVoltages (struct st_device *dev, SANE_Byte * Regs) { /* this function sets top, midle and bottom reference voltages */ DBG (DBG_FNC, "> RTS_Setup_RefVoltages\n"); if (Regs != NULL) { SANE_Byte vrts, vrms, vrbs; cfg_refvoltages_get (dev->sensorcfg->type, &vrts, &vrms, &vrbs); /* Top Reference Voltage */ data_bitset (&Regs[0x14], 0xe0, vrts); /*xxx----- */ /* Middle Reference Voltage */ data_bitset (&Regs[0x15], 0xe0, vrms); /*xxx----- */ /* Bottom Reference Voltage */ data_bitset (&Regs[0x16], 0xe0, vrbs); /*xxx----- */ } } static SANE_Int Init_USBData (struct st_device *dev) { SANE_Byte data; SANE_Byte *resource; DBG (DBG_FNC, "+ Init_USBData:\n"); if (Read_Byte (dev->usb_handle, 0xf8ff, &data) != OK) return ERROR; data = (data | 1); if (Write_Byte (dev->usb_handle, 0xf8ff, data) != OK) return ERROR; if (SSCG_Enable (dev) != OK) return ERROR; Init_Registers (dev); /* Gamma table size = 0x100 */ data_bitset (&dev->init_regs[0x1d0], 0x30, 0x00); /*--00----*/ /* Set 3 channels_per_dot */ data_bitset (&dev->init_regs[0x12], 0xc0, 0x03); /*xx------ */ /* systemclock */ data_bitset (&dev->init_regs[0x00], 0x0f, 0x05); /*----xxxx*/ /* timing cnpp */ data_bitset (&dev->init_regs[0x96], 0x3f, 0x17); /*--xxxxxx*/ /* set sensor_channel_color_order */ data_bitset (&dev->init_regs[0x60a], 0x7f, 0x24); /*-xxxxxxx*/ /* set crvs */ data_bitset (&dev->init_regs[0x10], 0x1f, get_value (SCAN_PARAM, CRVS, 7, usbfile)); /*---xxxxx*/ /* set reference voltages */ RTS_Setup_RefVoltages (dev, dev->init_regs); dev->init_regs[0x11] |= 0x10; data_bitset (&dev->init_regs[0x26], 0x70, 5); /*-101----*/ dev->init_regs[0x185] = 0x88; dev->init_regs[0x186] = 0x88; /* SDRAM clock */ data = get_value (SCAN_PARAM, MCLKIOC, 8, usbfile); data_bitset (&dev->init_regs[0x187], 0x0f, 0x08); /*----xxxx*/ data_bitset (&dev->init_regs[0x187], 0xf0, data); /*xxxx---- */ data--; if (data < 7) { switch (data) { case 0: data |= 0xc0; break; case 1: data |= 0xa0; break; case 2: data |= 0xe0; break; case 3: data |= 0x90; break; case 4: data |= 0xd0; break; case 5: data |= 0xb0; break; case 6: data = (data & 0x0f); break; } dev->init_regs[0x187] = _B0 (data); } data_bitset (&dev->init_regs[0x26], 0x0f, 0); /*----0000*/ dev->init_regs[0x27] &= 0x3f; dev->init_regs[0x29] = 0x24; dev->init_regs[0x2a] = 0x10; dev->init_regs[0x150] = 0xff; dev->init_regs[0x151] = 0x13; dev->init_regs[0x156] = 0xf0; dev->init_regs[0x157] = 0xfd; if (dev->motorcfg->changemotorcurrent != FALSE) Motor_Change (dev, dev->init_regs, 3); dev->init_regs[0xde] = 0; data_bitset (&dev->init_regs[0xdf], 0x0f, 0); /* loads motor resource for this dev */ resource = cfg_motor_resource_get (&data); if ((resource != NULL) && (data > 1)) memcpy (&dev->init_regs[0x104], resource, data); /* this bit is set but I don't know its purpose */ dev->init_regs[0x01] |= 0x40; /*-1------*/ dev->init_regs[0x124] = 0x94; /* release motor */ Motor_Release (dev); DBG (DBG_FNC, "- Init_USBData:\n"); return OK; } static SANE_Int Init_Registers (struct st_device *dev) { SANE_Int rst = OK; DBG (DBG_FNC, "+ Init_Registers:\n"); /* Lee dev->init_regs */ memset (dev->init_regs, 0, RT_BUFFER_LEN); RTS_ReadRegs (dev->usb_handle, dev->init_regs); Read_FE3E (dev, &v1619); if (dev->sensorcfg->type == CCD_SENSOR) { /* CCD sensor */ data_bitset (&dev->init_regs[0x11], 0xc0, 0); /*xx------ */ data_bitset (&dev->init_regs[0x146], 0x80, 1); /*x------- */ data_bitset (&dev->init_regs[0x146], 0x40, 1); /*-x------*/ } else { /* CIS sensor */ data_bitset (&dev->init_regs[0x146], 0x80, 0); /*0------- */ data_bitset (&dev->init_regs[0x146], 0x40, 0); /*-0------*/ data_bitset (&dev->init_regs[0x11], 0xc0, 2); /*xx------ */ data_bitset (&dev->init_regs[0xae], 0x3f, 0x14); /*--xxxxxx*/ data_bitset (&dev->init_regs[0xaf], 0x07, 1); /*-----xxx*/ dev->init_regs[0x9c] = dev->init_regs[0xa2] = dev->init_regs[0xa8] = (RTS_Debug->dev_model != UA4900) ? 1 : 0; dev->init_regs[0x9d] = dev->init_regs[0xa3] = dev->init_regs[0xa9] = 0; dev->init_regs[0x9e] = dev->init_regs[0xa4] = dev->init_regs[0xaa] = 0; dev->init_regs[0x9f] = dev->init_regs[0xa5] = dev->init_regs[0xab] = 0; dev->init_regs[0xa0] = dev->init_regs[0xa6] = dev->init_regs[0xac] = 0; dev->init_regs[0xa1] = dev->init_regs[0xa7] = dev->init_regs[0xad] = (RTS_Debug->dev_model != UA4900) ? 0x80 : 0; } /* disable CCD channels */ data_bitset (&dev->init_regs[0x10], 0xe0, 0); /*xxx----- */ data_bitset (&dev->init_regs[0x13], 0x80, 0); /*x------- */ /* enable timer to switch off lamp */ data_bitset (&dev->init_regs[0x146], 0x10, 1); /*---x----*/ /* set time to switch off lamp */ dev->init_regs[0x147] = 0xff; /* set last acccurve step */ data_lsb_set (&dev->init_regs[0xe1], 0x2af8, 3); /* set msi 0x02 */ dev->init_regs[0xda] = 0x02; data_bitset (&dev->init_regs[0xdd], 0x03, 0); /*------xx*/ /* set binary threshold high and low in little endian */ data_lsb_set (&dev->init_regs[0x19e], binarythresholdl, 2); data_lsb_set (&dev->init_regs[0x1a0], binarythresholdh, 2); data_bitset (&dev->init_regs[0x01], 0x08, 0); /*----x---*/ data_bitset (&dev->init_regs[0x16f], 0x40, 0); /*-x------*/ dev->init_regs[0x0bf] = (dev->init_regs[0x00bf] & 0xe0) | 0x20; dev->init_regs[0x163] = (dev->init_regs[0x0163] & 0x3f) | 0x40; data_bitset (&dev->init_regs[0xd6], 0x0f, 8); /*----xxxx*/ data_bitset (&dev->init_regs[0x164], 0x80, 1); /*x------- */ dev->init_regs[0x0bc] = 0x00; dev->init_regs[0x0bd] = 0x00; dev->init_regs[0x165] = (dev->init_regs[0x0165] & 0x3f) | 0x80; dev->init_regs[0x0ed] = 0x10; dev->init_regs[0x0be] = 0x00; dev->init_regs[0x0d5] = 0x00; dev->init_regs[0xee] = 0x00; dev->init_regs[0xef] = 0x00; dev->init_regs[0xde] = 0xff; /* set bit[4] has_curves = 0 | bit[0..3] unknown = 0 */ data_bitset (&dev->init_regs[0xdf], 0x10, 0); /*---x----*/ data_bitset (&dev->init_regs[0xdf], 0x0f, 0); /*----xxxx*/ /* Set motor type */ data_bitset (&dev->init_regs[0xd7], 0x80, dev->motorcfg->type); /*x------- */ if (dev->motorcfg->type == MT_ONCHIP_PWM) { data_bitset (&dev->init_regs[0x14e], 0x10, 1); /*---x----*/ /* set motorpwmfrequency */ data_bitset (&dev->init_regs[0xd7], 0x3f, dev->motorcfg->pwmfrequency); /*--xxxxxx*/ } dev->init_regs[0x600] &= 0xfb; dev->init_regs[0x1d8] |= 0x08; v160c_block_size = 0x04; mem_total = 0x80000; /* check and setup installed ram */ RTS_DMA_CheckType (dev, dev->init_regs); rst = RTS_DMA_WaitReady (dev, 1500); DBG (DBG_FNC, "- Init_Registers: %i\n", rst); return rst; } static SANE_Int Read_FE3E (struct st_device *dev, SANE_Byte * destino) { SANE_Int rst; DBG (DBG_FNC, "+ Read_FE3E:\n"); rst = ERROR; if (destino != NULL) { SANE_Byte data; if (Read_Byte (dev->usb_handle, 0xfe3e, &data) == 0) { *destino = data; rst = OK; DBG (DBG_FNC, " -> %02x\n", _B0 (data)); } } DBG (DBG_FNC, "- Read_FE3E: %i\n", rst); return rst; } static SANE_Int Head_IsAtHome (struct st_device *dev, SANE_Byte * Regs) { SANE_Int rst; DBG (DBG_FNC, "+ Head_IsAtHome:\n"); /* if returns TRUE, lamp is at home. Otherwise it returns FALSE */ rst = 0; if (Regs != NULL) { SANE_Byte data; if (Read_Byte (dev->usb_handle, 0xe96f, &data) == OK) { Regs[0x16f] = _B0 (data); rst = (data >> 6) & 1; } } rst = (rst == 1) ? TRUE : FALSE; DBG (DBG_FNC, "- Head_IsAtHome: %s\n", (rst == TRUE) ? "Yes" : "No"); return rst; } static SANE_Byte RTS_IsExecuting (struct st_device *dev, SANE_Byte * Regs) { SANE_Byte rst; DBG (DBG_FNC, "+ RTS_IsExecuting:\n"); rst = 0; if (Regs != NULL) { SANE_Byte data; if (Read_Byte (dev->usb_handle, 0xe800, &data) == OK) { Regs[0x00] = data; rst = (data >> 7) & 1; } } DBG (DBG_FNC, "- RTS_IsExecuting: %i\n", rst); return rst; } static SANE_Int RTS_WaitScanEnd (struct st_device *dev, SANE_Int msecs) { SANE_Byte data; SANE_Int rst; DBG (DBG_FNC, "+ RTS_WaitScanEnd(msecs=%i):\n", msecs); /* returns 0 if ok or timeout returns -1 if fails */ rst = ERROR; if (Read_Byte (dev->usb_handle, 0xe800, &data) == OK) { long ticks = GetTickCount () + msecs; rst = OK; while (((data & 0x80) != 0) && (ticks > GetTickCount ()) && (rst == OK)) { rst = Read_Byte (dev->usb_handle, 0xe800, &data); } } DBG (DBG_FNC, "- RTS_WaitScanEnd: Ending with rst=%i\n", rst); return rst; } static SANE_Int RTS_Enable_CCD (struct st_device *dev, SANE_Byte * Regs, SANE_Int channels) { SANE_Int rst = ERROR; DBG (DBG_FNC, "+ RTS_Enable_CCD(*Regs, arg2=%i):\n", channels); if (Read_Buffer (dev->usb_handle, 0xe810, &Regs[0x10], 4) == OK) { data_bitset (&Regs[0x10], 0xe0, channels); /*xxx----- */ data_bitset (&Regs[0x13], 0x80, channels >> 3); /*x------- */ Write_Buffer (dev->usb_handle, 0xe810, &Regs[0x10], 4); rst = OK; } DBG (DBG_FNC, "- RTS_Enable_CCD: %i\n", rst); return rst; } static SANE_Int RTS_Warm_Reset (struct st_device *dev) { SANE_Byte data; SANE_Int rst; DBG (DBG_FNC, "+ RTS_Warm_Reset:\n"); rst = ERROR; if (Read_Byte (dev->usb_handle, 0xe800, &data) == OK) { data = (data & 0x3f) | 0x40; /*01------ */ if (Write_Byte (dev->usb_handle, 0xe800, data) == OK) { data &= 0xbf; /*-0------*/ rst = Write_Byte (dev->usb_handle, 0xe800, data); } } DBG (DBG_FNC, "- RTS_Warm_Reset: %i\n", rst); return rst; } static SANE_Int Lamp_Status_Timer_Set (struct st_device *dev, SANE_Int minutes) { SANE_Byte MyBuffer[2]; SANE_Int rst; DBG (DBG_FNC, "+ Lamp_Status_Timer_Set(minutes=%i):\n", minutes); MyBuffer[0] = dev->init_regs[0x0146] & 0xef; MyBuffer[1] = dev->init_regs[0x0147]; if (minutes > 0) { double rst, op2; minutes = _B0 (minutes); op2 = 2.682163611980331; MyBuffer[0x00] |= 0x10; rst = (minutes * op2); MyBuffer[0x01] = (SANE_Byte) floor (rst); } dev->init_regs[0x147] = MyBuffer[1]; dev->init_regs[0x146] = (dev->init_regs[0x146] & 0xef) | (MyBuffer[0] & 0x10); rst = Write_Word (dev->usb_handle, 0xe946, (SANE_Int) ((MyBuffer[1] << 8) + MyBuffer[0])); DBG (DBG_FNC, "- Lamp_Status_Timer_Set: %i\n", rst); return rst; } static SANE_Int Buttons_Enable (struct st_device *dev) { SANE_Int data, rst; DBG (DBG_FNC, "+ Buttons_Enable:\n"); if (Read_Word (dev->usb_handle, 0xe958, &data) == OK) { data |= 0x0f; rst = Write_Word (dev->usb_handle, 0xe958, data); } else rst = ERROR; DBG (DBG_FNC, "- Buttons_Enable: %i\n", rst); return rst; } static SANE_Int Buttons_Count (struct st_device *dev) { SANE_Int rst = 0; /* This chipset supports up to six buttons */ if (dev->buttons != NULL) rst = dev->buttons->count; DBG (DBG_FNC, "> Buttons_Count: %i\n", rst); return rst; } static SANE_Int Buttons_Status (struct st_device *dev) { SANE_Int rst = -1; SANE_Byte data; DBG (DBG_FNC, "+ Buttons_Status\n"); /* Each bit is 1 if button is not pressed, and 0 if it is pressed This chipset supports up to six buttons */ if (Read_Byte (dev->usb_handle, 0xe968, &data) == OK) rst = _B0 (data); DBG (DBG_FNC, "- Buttons_Status: %i\n", rst); return rst; } static SANE_Int Buttons_Released (struct st_device *dev) { SANE_Int rst = -1; SANE_Byte data; DBG (DBG_FNC, "+ Buttons_Released\n"); /* Each bit is 1 if button is released, until reading this register. Then entire register is cleared. This chipset supports up to six buttons */ if (Read_Byte (dev->usb_handle, 0xe96a, &data) == OK) rst = _B0 (data); DBG (DBG_FNC, "- Buttons_Released: %i\n", rst); return rst; } static SANE_Int Buttons_Order (struct st_device *dev, SANE_Int mask) { /* this is a way to order each button according to its bit in register 0xe968 */ SANE_Int rst = -1; if (dev->buttons != NULL) { SANE_Int a; for (a = 0; a < 6; a++) { if (dev->buttons->mask[a] == mask) { rst = a; break; } } } return rst; } static SANE_Int GainOffset_Clear (struct st_device *dev) { SANE_Int rst = OK; DBG (DBG_FNC, "+ GainOffset_Clear:\n"); /* clear offsets */ offset[CL_RED] = offset[CL_GREEN] = offset[CL_BLUE] = 0; /* save offsets */ /* check if chipset supports accessing eeprom */ if ((dev->chipset->capabilities & CAP_EEPROM) != 0) { SANE_Int a; for (a = CL_RED; a <= CL_BLUE; a++) RTS_EEPROM_WriteWord (dev->usb_handle, 0x70 + (2 * a), 0); /* update checksum */ rst = RTS_EEPROM_WriteByte (dev->usb_handle, 0x76, 0); } DBG (DBG_FNC, "- GainOffset_Clear: %i\n", rst); return rst; } static SANE_Int Lamp_Status_Get (struct st_device *dev, SANE_Byte * flb_lamp, SANE_Byte * tma_lamp) { /* The only reason that I think windows driver uses two variables to get which lamp is switched on instead of one variable is that some chipset model could have both lamps switched on, so I'm maintaining such var */ SANE_Int rst = ERROR; /* default */ DBG (DBG_FNC, "+ Lamp_Status_Get:\n"); if ((flb_lamp != NULL) && (tma_lamp != NULL)) { SANE_Int data1; SANE_Byte data2; if (Read_Byte (dev->usb_handle, 0xe946, &data2) == OK) { if (Read_Word (dev->usb_handle, 0xe954, &data1) == OK) { rst = OK; *flb_lamp = 0; *tma_lamp = 0; switch (dev->chipset->model) { case RTS8822BL_03A: *flb_lamp = ((data2 & 0x40) != 0) ? 1 : 0; *tma_lamp = (((data2 & 0x20) != 0) && ((data1 & 0x10) != 0)) ? 1 : 0; break; default: if ((_B1 (data1) & 0x10) == 0) *flb_lamp = (data2 >> 6) & 1; else *tma_lamp = (data2 >> 6) & 1; break; } } } } DBG (DBG_FNC, "- Lamp_Status_Get: rst=%i flb=%i tma=%i\n", rst, _B0 (*flb_lamp), _B0 (*tma_lamp)); return rst; } static SANE_Int RTS_DMA_WaitReady (struct st_device *dev, SANE_Int msecs) { SANE_Byte data; SANE_Int rst; long mytime; DBG (DBG_FNC, "+ RTS_DMA_WaitReady(msecs=%i):\n", msecs); rst = OK; mytime = GetTickCount () + msecs; while ((mytime > GetTickCount ()) && (rst == OK)) { if (Read_Byte (dev->usb_handle, 0xef09, &data) == OK) { if ((data & 1) == 0) usleep (1000 * 100); else break; } else rst = ERROR; } DBG (DBG_FNC, "- RTS_DMA_WaitReady: %i\n", rst); return rst; } static SANE_Int RTS_WaitInitEnd (struct st_device *dev, SANE_Int msecs) { SANE_Byte data; SANE_Int rst; long mytime; DBG (DBG_FNC, "+ RTS_WaitInitEnd(msecs=%i):\n", msecs); rst = OK; mytime = GetTickCount () + msecs; while ((mytime > GetTickCount ()) && (rst == OK)) { if (Read_Byte (dev->usb_handle, 0xf910, &data) == OK) { if ((data & 8) == 0) usleep (1000 * 100); else break; } else rst = ERROR; } DBG (DBG_FNC, "- RTS_WaitInitEnd: %i\n", rst); return rst; } static SANE_Int Motor_Change (struct st_device *dev, SANE_Byte * buffer, SANE_Byte value) { SANE_Int data, rst; DBG (DBG_FNC, "+ Motor_Change(*buffer, value=%i):\n", value); if (Read_Word (dev->usb_handle, 0xe954, &data) == OK) { data &= 0xcf; /*--00----*/ value--; switch (value) { case 2: data |= 0x30; break; /*--11----*/ case 1: data |= 0x20; break; /*--10----*/ case 0: data |= 0x10; break; /*--01----*/ } buffer[0x154] = _B0 (data); rst = Write_Byte (dev->usb_handle, 0xe954, buffer[0x154]); } else rst = ERROR; DBG (DBG_FNC, "- Motor_Change: %i\n", rst); return rst; } static SANE_Int RTS_DMA_Read (struct st_device *dev, SANE_Int dmacs, SANE_Int options, SANE_Int size, SANE_Byte * buffer) { SANE_Int rst = ERROR; DBG (DBG_FNC, "+ RTS_DMA_Read(dmacs=%04x, options=%04x, size=%i., *buffer):\n", dmacs, options, size); /* is there any buffer to send? */ if ((buffer != NULL) && (size > 0)) { /* reset dma */ if (RTS_DMA_Reset (dev) == OK) { /* prepare dma to read */ if (RTS_DMA_Enable_Read (dev, dmacs, size, options) == OK) { SANE_Int transferred; rst = Bulk_Operation (dev, BLK_READ, size, buffer, &transferred); } } } DBG (DBG_FNC, "- RTS_DMA_Read(): %i\n", rst); return rst; } static SANE_Int RTS_DMA_Write (struct st_device *dev, SANE_Int dmacs, SANE_Int options, SANE_Int size, SANE_Byte * buffer) { SANE_Int rst = ERROR; DBG (DBG_FNC, "+ RTS_DMA_Write(dmacs=%04x, options=%04x, size=%i., *buffer):\n", dmacs, options, size); /* is there any buffer to send? */ if ((buffer != NULL) && (size > 0)) { /* reset dma */ if (RTS_DMA_Reset (dev) == OK) { /* prepare dma to write */ if (RTS_DMA_Enable_Write (dev, dmacs, size, options) == OK) { SANE_Int transferred; SANE_Byte *check_buffer; check_buffer = (SANE_Byte *) malloc (size); if (check_buffer != NULL) { /* if some transfer fails we try again until ten times */ SANE_Int a; for (a = 10; a > 0; a--) { /* send buffer */ Bulk_Operation (dev, BLK_WRITE, size, buffer, &transferred); /* prepare dma to read */ if (RTS_DMA_Enable_Read (dev, dmacs, size, options) == OK) { SANE_Int b = 0, diff = FALSE; /* read buffer */ Bulk_Operation (dev, BLK_READ, size, check_buffer, &transferred); /* check buffers */ while ((b < size) && (diff == FALSE)) { if (buffer[b] == check_buffer[b]) b++; else diff = TRUE; } /* if buffers are equal we can break loop */ if (diff == TRUE) { /* cancel dma */ RTS_DMA_Cancel (dev); /* prepare dma to write buffer again */ if (RTS_DMA_Enable_Write (dev, dmacs, size, options) != OK) break; } else { /* everything went ok */ rst = OK; break; } } else break; } /* free check buffer */ free (check_buffer); } else { /* for some reason it's not possible to allocate space to check sent buffer so we just write data */ Bulk_Operation (dev, BLK_WRITE, size, buffer, &transferred); rst = OK; } } } } DBG (DBG_FNC, "- RTS_DMA_Write(): %i\n", rst); return rst; } static SANE_Int RTS_DMA_CheckType (struct st_device *dev, SANE_Byte * Regs) { /* This function tries to detect what kind of RAM supports chipset */ /* Returns a value between 0 and 4. -1 means error */ SANE_Int rst = -1; DBG (DBG_FNC, "+ RTS_DMA_CheckType(*Regs):\n"); if (Regs != NULL) { SANE_Byte *out_buffer; /* define buffer to send */ out_buffer = (SANE_Byte *) malloc (sizeof (SANE_Byte) * 2072); if (out_buffer != NULL) { SANE_Byte *in_buffer; /* define incoming buffer */ in_buffer = (SANE_Byte *) malloc (sizeof (SANE_Byte) * 2072); if (in_buffer != NULL) { SANE_Int a, b, diff; /* fill outgoing buffer with a known pattern */ b = 0; for (a = 0; a < 2072; a++) { out_buffer[a] = b; b++; if (b == 0x61) b = 0; } /* let's send buffer with different ram types and compare incoming buffer until getting the right type */ for (a = 4; a >= 0; a--) { /* set ram type */ if (RTS_DMA_SetType (dev, Regs, a) != OK) break; /* wait 1500 milliseconds */ if (RTS_DMA_WaitReady (dev, 1500) == OK) { /* reset dma */ RTS_DMA_Reset (dev); /* write buffer */ RTS_DMA_Write (dev, 0x0004, 0x102, 2072, out_buffer); /* now read buffer */ RTS_DMA_Read (dev, 0x0004, 0x102, 2072, in_buffer); /* check buffers */ b = 0; diff = FALSE; while ((b < 2072) && (diff == FALSE)) { if (out_buffer[b] == in_buffer[b]) b++; else diff = TRUE; } /* if buffers are equal */ if (diff == FALSE) { SANE_Int data = 0; /* buffers are equal so we've found the right ram type */ memset (out_buffer, 0, 0x20); for (b = 0; b < 0x20; b += 2) out_buffer[b] = b; /* write buffer */ if (RTS_DMA_Write (dev, 0x0004, 0x0000, 0x20, out_buffer) == OK) { SANE_Int c = 0; diff = TRUE; do { c++; for (b = 1; b < 0x20; b += 2) out_buffer[b] = c; if (RTS_DMA_Write (dev, 0x0004, (_B0 (c) << 0x11) >> 0x04, 0x20, out_buffer) == OK) { if (RTS_DMA_Read (dev, 0x0004, 0x0000, 0x20, in_buffer) == OK) { b = 0; diff = FALSE; while ((b < 0x20) && (diff == FALSE)) { if (out_buffer[b] == in_buffer[b]) b++; else diff = TRUE; } if (diff == FALSE) data = c << 7; } } } while ((c < 0x80) && (diff == TRUE)); } switch (data) { case 16384: Regs[0x708] &= 0x1f; Regs[0x708] |= 0x80; break; case 8192: Regs[0x708] &= 0x1f; Regs[0x708] |= 0x60; break; case 4096: Regs[0x708] &= 0x1f; Regs[0x708] |= 0x40; break; case 2048: Regs[0x708] &= 0x1f; Regs[0x708] |= 0x20; break; case 1024: Regs[0x708] &= 0x1f; data = 0x200; break; case 128: Regs[0x708] &= 0x1f; break; } DBG (DBG_FNC, " -> data1 = 0x%08x\n", (data * 4) * 1024); DBG (DBG_FNC, " -> data2 = 0x%08x\n", data * 1024); DBG (DBG_FNC, " -> type = 0x%04x\n", Regs[0x708] >> 5); RTS_DMA_SetType (dev, Regs, Regs[0x708] >> 5); rst = OK; break; } } else break; } free (in_buffer); } free (out_buffer); } } DBG (DBG_FNC, "- RTS_DMA_CheckType(): %i\n", rst); return rst; } static SANE_Int RTS_DMA_SetType (struct st_device *dev, SANE_Byte * Regs, SANE_Byte ramtype) { SANE_Int rst = ERROR; DBG (DBG_FNC, "+ RTS_DMA_SetType(*Regs, ramtype=%i):\n", ramtype); if (Regs != NULL) { data_bitset (&Regs[0x708], 0x08, 0); /*----0---*/ if (Write_Byte (dev->usb_handle, 0xef08, Regs[0x708]) == OK) { data_bitset (&Regs[0x708], 0xe0, ramtype); if (Write_Byte (dev->usb_handle, 0xef08, Regs[0x708]) == OK) { data_bitset (&Regs[0x708], 0x08, 1); /*----1---*/ rst = Write_Byte (dev->usb_handle, 0xef08, Regs[0x708]); } } } DBG (DBG_FNC, "- RTS_DMA_SetType: %i\n", rst); return rst; } static void Motor_Release (struct st_device *dev) { SANE_Byte data = 0; DBG (DBG_FNC, "+ Motor_Release:\n"); if (Read_Byte (dev->usb_handle, 0xe8d9, &data) == OK) { data |= 4; Write_Byte (dev->usb_handle, 0xe8d9, data); } DBG (DBG_FNC, "- Motor_Release:\n"); } static SANE_Byte GainOffset_Counter_Load (struct st_device *dev) { SANE_Byte data = 0x0f; DBG (DBG_FNC, "+ GainOffset_Counter_Load:\n"); /* check if chipset supports accessing eeprom */ if ((dev->chipset->capabilities & CAP_EEPROM) != 0) if (RTS_EEPROM_ReadByte (dev->usb_handle, 0x77, &data) != OK) data = 0x0f; DBG (DBG_FNC, "- GainOffset_Counter_Load: %i\n", _B0 (data)); return data; } static SANE_Int RTS_Execute (struct st_device *dev) { SANE_Byte e813, e800; SANE_Int ret; DBG (DBG_FNC, "+ RTS_Execute:\n"); e813 = 0; e800 = 0; ret = ERROR; if (Read_Byte (dev->usb_handle, 0xe800, &e800) == OK) { if (Read_Byte (dev->usb_handle, 0xe813, &e813) == OK) { e813 &= 0xbf; if (Write_Byte (dev->usb_handle, 0xe813, e813) == OK) { e800 |= 0x40; if (Write_Byte (dev->usb_handle, 0xe800, e800) == OK) { e813 |= 0x40; if (Write_Byte (dev->usb_handle, 0xe813, e813) == OK) { e800 &= 0xbf; if (Write_Byte (dev->usb_handle, 0xe800, e800) == OK) { usleep (1000 * 100); e800 |= 0x80; ret = Write_Byte (dev->usb_handle, 0xe800, e800); } } } } } } DBG (DBG_FNC, "- RTS_Execute: %i\n", ret); return ret; } static SANE_Int RTS_isTmaAttached (struct st_device *dev) { SANE_Int rst; DBG (DBG_FNC, "+ RTS_isTmaAttached:\n"); /* returns 0 if Tma is attached. Otherwise 1 */ if (Read_Word (dev->usb_handle, 0xe968, &rst) == OK) { rst = ((_B1 (rst) & 2) != 0) ? FALSE : TRUE; } else rst = TRUE; DBG (DBG_FNC, "- RTS_isTmaAttached: %s\n", (rst == TRUE) ? "Yes" : "No"); return rst; } static SANE_Int Gamma_AllocTable (SANE_Byte * table) { SANE_Int C; SANE_Int rst = OK; hp_gamma->depth = 8; for (C = 0; C < 3; C++) if (hp_gamma->table[C] == NULL) hp_gamma->table[C] = malloc (sizeof (SANE_Byte) * 256); if ((hp_gamma->table[CL_RED] != NULL) && (hp_gamma->table[CL_GREEN] != NULL) && (hp_gamma->table[CL_BLUE] != NULL)) { /* All tables allocated */ for (C = 0; C < 256; C++) { if ((table != NULL) && (RTS_Debug->EnableGamma == TRUE)) { /* fill gamma tables with user defined values */ hp_gamma->table[CL_RED][C] = table[C]; hp_gamma->table[CL_GREEN][C] = table[0x100 + C]; hp_gamma->table[CL_BLUE][C] = table[0x200 + C]; } else { hp_gamma->table[CL_RED][C] = C; hp_gamma->table[CL_GREEN][C] = C; hp_gamma->table[CL_BLUE][C] = C; } } /* Locate threshold of bw */ for (C = 0; C < 256; C++) if (hp_gamma->table[CL_RED][C] != 0) break; bw_threshold = C - 1; } else { /* Some alloc failed */ rst = ERROR; Gamma_FreeTables (); } DBG (DBG_FNC, "> Gamma_AllocTable: %i >> bw_threshold = %i\n", rst, bw_threshold); return rst; } static SANE_Int Gamma_Apply (struct st_device *dev, SANE_Byte * Regs, struct st_scanparams *scancfg, struct st_hwdconfig *hwdcfg, struct st_gammatables *mygamma) { SANE_Int rst = OK; DBG (DBG_FNC, "+ Gamma_Apply(*Regs, *scancfg, *hwdcfg, *mygamma):\n"); dbg_ScanParams (scancfg); if (hwdcfg->use_gamma_tables == FALSE) { DBG (DBG_FNC, "-> Gamma tables are not used\n"); v1600 = NULL; v1604 = NULL; v1608 = NULL; } else { /*390b */ SANE_Int table_size, buffersize, c; SANE_Byte channels, *gammabuffer; DBG (DBG_FNC, "-> Using gamma tables\n"); /* get channels count */ channels = 3; /* default */ if (scancfg->colormode != CM_COLOR) { if (scancfg->channel != 3) { if (scancfg->colormode != 3) channels = (scancfg->samplerate == PIXEL_RATE) ? 2 : 1; } } /* get size for gamma tables */ switch (mygamma->depth & 0x0c) { case 0: table_size = 0x100 + (mygamma->depth & 1); break; case 4: table_size = 0x400 + (mygamma->depth & 1); break; case 8: table_size = 0x1000 + (mygamma->depth & 1); break; default: table_size = 2; break; } /* allocate space for gamma buffer */ buffersize = table_size * channels; gammabuffer = (SANE_Byte *) malloc (buffersize * sizeof (SANE_Byte)); if (gammabuffer != NULL) { /* update gamma pointers for each channel */ v1600 = (SANE_Byte *) & mygamma->table[CL_RED]; v1604 = (SANE_Byte *) & mygamma->table[CL_GREEN]; v1608 = (SANE_Byte *) & mygamma->table[CL_BLUE]; /* include gamma tables into one buffer */ for (c = 0; c < channels; c++) memcpy (gammabuffer + (c * table_size), mygamma->table[c], table_size); /* send gamma buffer to scanner */ Write_Byte (dev->usb_handle, 0xee0b, Regs[0x060b] & 0xaf); rst = Gamma_SendTables (dev, Regs, gammabuffer, buffersize); Write_Byte (dev->usb_handle, 0xee0b, Regs[0x060b]); /* free gamma buffer */ free (gammabuffer); } else rst = ERROR; } return rst; } static SANE_Int Refs_Analyze_Pattern (struct st_scanparams *scancfg, SANE_Byte * scanned_pattern, SANE_Int * ler1, SANE_Int ler1order, SANE_Int * ser1, SANE_Int ser1order) { SANE_Int buffersize, xpos, ypos, coord, cnt, chn_size, dist, rst; double *color_sum, *color_dif, diff_max; SANE_Int vector[3]; DBG (DBG_FNC, "+ Refs_Analyze_Pattern(depth=%i, width=%i, height=%i, *scanned_pattern, *ler1, ler1order=%i, *ser1, ser1order=%i)\n", scancfg->depth, scancfg->coord.width, scancfg->coord.height, ler1order, ser1order); rst = ERROR; /* by default */ dist = 5; /* distance to compare */ chn_size = (scancfg->depth > 8) ? 2 : 1; buffersize = max (scancfg->coord.width, scancfg->coord.height); color_sum = (double *) malloc (sizeof (double) * buffersize); if (color_sum != NULL) { color_dif = (double *) malloc (sizeof (double) * buffersize); if (color_dif != NULL) { /*-------- 1st SER -------- */ coord = 1; if ((scancfg->coord.width - dist) > 1) { /* clear buffers */ memset (color_sum, 0, sizeof (double) * buffersize); memset (color_dif, 0, sizeof (double) * buffersize); for (xpos = 0; xpos < scancfg->coord.width; xpos++) { for (ypos = 0; ypos < 20; ypos++) color_sum[xpos] += data_lsb_get (scanned_pattern + (scancfg->coord.width * ypos) + xpos, chn_size); } diff_max = (ser1order != 0) ? color_sum[0] - color_sum[1] : color_sum[1] - color_sum[0]; color_dif[0] = diff_max; cnt = 1; do { color_dif[cnt] = (ser1order != 0) ? color_sum[cnt] - color_sum[cnt + dist] : color_sum[cnt + dist] - color_sum[cnt]; if ((color_dif[cnt] >= 0) && (color_dif[cnt] > diff_max)) { /*d4df */ diff_max = color_dif[cnt]; if (fabs (color_dif[cnt] - color_dif[cnt - 1]) > fabs (color_dif[coord] - color_dif[coord - 1])) coord = cnt; } cnt++; } while (cnt < (scancfg->coord.width - dist)); } vector[0] = coord + dist; /*-------- 1st LER -------- */ coord = 1; if ((scancfg->coord.height - dist) > 1) { /* clear buffers */ memset (color_sum, 0, sizeof (double) * buffersize); memset (color_dif, 0, sizeof (double) * buffersize); for (ypos = 0; ypos < scancfg->coord.height; ypos++) { for (xpos = vector[0]; xpos < scancfg->coord.width - dist; xpos++) color_sum[ypos] += data_lsb_get (scanned_pattern + (scancfg->coord.width * ypos) + xpos, chn_size); } diff_max = (ler1order != 0) ? color_sum[0] - color_sum[1] : color_sum[1] - color_sum[0]; color_dif[0] = diff_max; cnt = 1; do { color_dif[cnt] = (ler1order != 0) ? color_sum[cnt] - color_sum[cnt + dist] : color_sum[cnt + dist] - color_sum[cnt]; if ((color_dif[cnt] >= 0) && (color_dif[cnt] > diff_max)) { diff_max = color_dif[cnt]; if (fabs (color_dif[cnt] - color_dif[cnt - 1]) > fabs (color_dif[coord] - color_dif[coord - 1])) coord = cnt; } cnt++; } while (cnt < (scancfg->coord.height - dist)); } vector[1] = coord + dist; /*-------- 1st LER -------- */ if ((scancfg->coord.width - dist) > 1) { /* clear buffers */ memset (color_sum, 0, sizeof (double) * buffersize); memset (color_dif, 0, sizeof (double) * buffersize); for (xpos = 0; xpos < scancfg->coord.width; xpos++) { for (ypos = coord + 4; ypos < scancfg->coord.height; ypos++) color_sum[xpos] += data_lsb_get (scanned_pattern + (scancfg->coord.width * ypos) + xpos, chn_size); } diff_max = (ser1order != 0) ? color_sum[0] - color_sum[1] : color_sum[1] - color_sum[0]; color_dif[0] = diff_max; cnt = 1; do { color_dif[cnt] = (ser1order != 0) ? color_sum[cnt] - color_sum[cnt + dist] : color_sum[cnt + dist] - color_sum[cnt]; if ((color_dif[cnt] >= 0) && (color_dif[cnt] > diff_max)) { diff_max = color_dif[cnt]; if (fabs (color_dif[cnt] - color_dif[cnt - 1]) > fabs (color_dif[coord] - color_dif[coord - 1])) coord = cnt; } cnt++; } while (cnt < (scancfg->coord.width - dist)); } vector[2] = coord + dist; /* save image */ if (RTS_Debug->SaveCalibFile != FALSE) dbg_autoref (scancfg, scanned_pattern, vector[0], vector[2], vector[1]); /* assign values detected */ if (ser1 != NULL) *ser1 = vector[2]; if (ler1 != NULL) *ler1 = vector[1]; /* show values */ DBG (DBG_FNC, " -> Vectors found: x1=%i, x2=%i, y=%i\n", vector[0], vector[2], vector[1]); rst = OK; free (color_dif); } free (color_sum); } DBG (DBG_FNC, "- Refs_Analyze_Pattern: %i\n", rst); return rst; } static double get_shrd (double value, SANE_Int desp) { if (desp <= 0x40) return value / pow (2, desp); else return 0; } static char get_byte (double value) { unsigned int data; double temp; if (value > 0xffffffff) { temp = floor (get_shrd (value, 0x20)); temp *= pow (2, 32); value -= temp; } data = (unsigned int) value; data = _B0 (data); return data; } static SANE_Int Timing_SetLinearImageSensorClock (SANE_Byte * Regs, struct st_cph *cph) { SANE_Int rst = ERROR; DBG (DBG_FNC, "+ Timing_SetLinearImageSensorClock(SANE_Byte *Regs, struct st_cph *cph)\n"); dbg_sensorclock (cph); if ((Regs != NULL) && (cph != NULL)) { Regs[0x00] = get_byte (cph->p1); Regs[0x01] = get_byte (get_shrd (cph->p1, 0x08)); Regs[0x02] = get_byte (get_shrd (cph->p1, 0x10)); Regs[0x03] = get_byte (get_shrd (cph->p1, 0x18)); Regs[0x04] &= 0x80; Regs[0x04] |= ((get_byte (get_shrd (cph->p1, 0x20))) & 0x0f); Regs[0x04] |= ((cph->ps & 1) << 6); Regs[0x04] |= ((cph->ge & 1) << 5); Regs[0x04] |= ((cph->go & 1) << 4); Regs[0x05] = get_byte (cph->p2); Regs[0x06] = get_byte (get_shrd (cph->p2, 0x08)); Regs[0x07] = get_byte (get_shrd (cph->p2, 0x10)); Regs[0x08] = get_byte (get_shrd (cph->p2, 0x18)); Regs[0x09] &= 0xf0; Regs[0x09] |= ((get_byte (get_shrd (cph->p2, 0x20))) & 0x0f); rst = OK; } DBG (DBG_FNC, "- Timing_SetLinearImageSensorClock: %i\n", rst); return rst; } static void RTS_Setup_SensorTiming (struct st_device *dev, SANE_Int mytiming, SANE_Byte * Regs) { DBG (DBG_FNC, "+ RTS_Setup_SensorTiming(mytiming=%i, *Regs):\n", mytiming); if ((Regs != NULL) && (mytiming < dev->timings_count)) { struct st_timing *mt = dev->timings[mytiming]; if (mt != NULL) { dbg_timing (mt); /* Correlated-Double-Sample 1 & 2 */ data_bitset (&Regs[0x92], 0x3f, mt->cdss[0]); data_bitset (&Regs[0x93], 0x3f, mt->cdsc[0]); data_bitset (&Regs[0x94], 0x3f, mt->cdss[1]); data_bitset (&Regs[0x95], 0x3f, mt->cdsc[1]); data_bitset (&Regs[0x96], 0x3f, mt->cnpp); /* Linear image sensor transfer gates */ data_bitset (&Regs[0x45], 0x80, mt->cvtrp[0]); data_bitset (&Regs[0x45], 0x40, mt->cvtrp[1]); data_bitset (&Regs[0x45], 0x20, mt->cvtrp[2]); data_bitset (&Regs[0x45], 0x1f, mt->cvtrfpw); data_bitset (&Regs[0x46], 0x1f, mt->cvtrbpw); data_lsb_set (&Regs[0x47], mt->cvtrw, 1); data_lsb_set (&Regs[0x84], mt->cphbp2s, 3); data_lsb_set (&Regs[0x87], mt->cphbp2e, 3); data_lsb_set (&Regs[0x8a], mt->clamps, 3); data_lsb_set (&Regs[0x8d], mt->clampe, 3); if (dev->chipset->model == RTS8822L_02A) { if (mt->clampe == -1) data_lsb_set (&Regs[0x8d], mt->cphbp2e, 3); } Regs[0x97] = get_byte (mt->adcclkp[0]); Regs[0x98] = get_byte (get_shrd (mt->adcclkp[0], 0x08)); Regs[0x99] = get_byte (get_shrd (mt->adcclkp[0], 0x10)); Regs[0x9a] = get_byte (get_shrd (mt->adcclkp[0], 0x18)); Regs[0x9b] &= 0xf0; Regs[0x9b] |= ((get_byte (get_shrd (mt->adcclkp[0], 0x20))) & 0x0f); Regs[0xc1] = get_byte (mt->adcclkp[1]); Regs[0xc2] = get_byte (get_shrd (mt->adcclkp[1], 0x08)); Regs[0xc3] = get_byte (get_shrd (mt->adcclkp[1], 0x10)); Regs[0xc4] = get_byte (get_shrd (mt->adcclkp[1], 0x18)); Regs[0xc5] &= 0xe0; Regs[0xc5] |= ((get_byte (get_shrd (mt->adcclkp[1], 0x20))) & 0x0f); /* bit(4) = bit(0) */ Regs[0xc5] |= ((mt->adcclkp2e & 1) << 4); Timing_SetLinearImageSensorClock (&Regs[0x48], &mt->cph[0]); Timing_SetLinearImageSensorClock (&Regs[0x52], &mt->cph[1]); Timing_SetLinearImageSensorClock (&Regs[0x5c], &mt->cph[2]); Timing_SetLinearImageSensorClock (&Regs[0x66], &mt->cph[3]); Timing_SetLinearImageSensorClock (&Regs[0x70], &mt->cph[4]); Timing_SetLinearImageSensorClock (&Regs[0x7a], &mt->cph[5]); } } } static SANE_Int Motor_GetFromResolution (SANE_Int resolution) { SANE_Int ret; ret = 3; if (RTS_Debug->usbtype != USB11) { if (scan.scantype != ST_NORMAL) { /* scantype is ST_NEG or ST_TA */ if (resolution >= 600) ret = 0; } else if (resolution >= 1200) ret = 0; } else if (resolution >= 600) ret = 0; DBG (DBG_FNC, "> Motor_GetFromResolution(resolution=%i): %i\n", resolution, ret); return ret; } static SANE_Int SetMultiExposure (struct st_device *dev, SANE_Byte * Regs) { SANE_Int iValue, myctpc; DBG (DBG_FNC, "> SetMultiExposure:\n"); /* set motor has no curves */ data_bitset (&Regs[0xdf], 0x10, 0); /*---0----*/ /* select case systemclock */ switch (Regs[0x00] & 0x0f) { case 0x00: iValue = 0x00895440; break; /* 3 x 0x2DC6C0 */ case 0x08: case 0x01: iValue = 0x00b71b00; break; /* 4 x 0x2DC6C0 */ case 0x02: iValue = 0x0112a880; break; /* 6 x 0x2DC6C0 */ case 0x0a: case 0x03: iValue = 0x016e3600; break; /* 8 x 0x2DC6C0 */ case 0x04: iValue = 0x02255100; break; /* 12 x 0x2DC6C0 */ case 0x0c: iValue = 0x02dc6c00; break; /* 16 x 0x2DC6C0 */ case 0x05: iValue = 0x044aa200; break; /* 24 x 0x2DC6C0 */ case 0x0d: iValue = 0x05b8d800; break; /* 32 x 0x2DC6C0 */ case 0x09: iValue = 0x00f42400; break; case 0x0b: iValue = 0x01e84800; break; /* = case 9 * 2 */ default: iValue = 0x0478f7f8; break; } /* divide by timing.cnpp */ iValue /= ((Regs[0x96] & 0x3f) + 1); iValue /= dev->motorcfg->basespeedpps; /* get line exposure time */ myctpc = data_lsb_get (&Regs[0x30], 3) + 1; DBG (DBG_FNC, "CTPC -- SetMultiExposure -- 1 =%i\n", myctpc); /* if last step of accurve.normalscan table is lower than iValue ... */ if (data_lsb_get (&Regs[0xe1], 3) < iValue) { SANE_Int traget; SANE_Int step_size = _B0 (Regs[0xe0]) + 1; /* set exposure time [RED] if zero */ if (data_lsb_get (&Regs[0x36], 3) == 0) data_lsb_set (&Regs[0x36], myctpc - 1, 3); /* set exposure time [GREEN] if zero */ if (data_lsb_get (&Regs[0x3c], 3) == 0) data_lsb_set (&Regs[0x3c], myctpc - 1, 3); /* set exposure time [BLUE] if zero */ if (data_lsb_get (&Regs[0x42], 3) == 0) data_lsb_set (&Regs[0x42], myctpc - 1, 3); iValue = (iValue + 1) * step_size; /* update line exposure time */ traget = (((myctpc + iValue - 1) / myctpc) * myctpc); data_lsb_set (&Regs[0x30], traget - 1, 3); traget = (traget / step_size) - 1; data_lsb_set (&Regs[0x00e1], traget, 3); } /* 8300 */ return OK; } static SANE_Int data_lsb_get (SANE_Byte * address, SANE_Int size) { SANE_Int ret = 0; if ((address != NULL) && (size > 0) && (size < 5)) { SANE_Int a; SANE_Byte b; size--; for (a = size; a >= 0; a--) { b = address[a]; ret = (ret << 8) + b; } } return ret; } static SANE_Byte data_bitget (SANE_Byte * address, SANE_Int mask) { SANE_Int desp = 0; if (mask & 1); else if (mask & 2) desp = 1; else if (mask & 4) desp = 2; else if (mask & 8) desp = 3; else if (mask & 16) desp = 4; else if (mask & 32) desp = 5; else if (mask & 64) desp = 6; else if (mask & 128) desp = 7; return (*address & mask) >> desp; } static void data_bitset (SANE_Byte * address, SANE_Int mask, SANE_Byte data) { /* This function fills mask bits of just a byte with bits given in data */ if (mask & 1); else if (mask & 2) data <<= 1; else if (mask & 4) data <<= 2; else if (mask & 8) data <<= 3; else if (mask & 16) data <<= 4; else if (mask & 32) data <<= 5; else if (mask & 64) data <<= 6; else if (mask & 128) data <<= 7; *address = (*address & (0xff - mask)) | (data & mask); } static void data_wide_bitset (SANE_Byte * address, SANE_Int mask, SANE_Int data) { /* Setting bytes bit per bit mask is 4 bytes size Example: data = 0111010111 mask = 00000000 11111111 11000000 00000000 rst = 00000000 01110101 11000000 00000000 */ SANE_Int mymask, started = FALSE; if ((address != NULL) && (mask != 0)) { while (mask != 0) { mymask = _B0 (mask); if (started == FALSE) { if (mymask != 0) { SANE_Int a, myvalue; for (a = 0; a < 8; a++) if ((mymask & (1 << a)) != 0) break; myvalue = _B0 (data << a); myvalue >>= a; data_bitset (address, mymask, myvalue); data >>= (8 - a); started = TRUE; } } else { data_bitset (address, mymask, _B0 (data)); data >>= 8; } address++; mask >>= 8; } } } static void data_lsb_set (SANE_Byte * address, SANE_Int data, SANE_Int size) { if ((address != NULL) && (size > 0) && (size < 5)) { SANE_Int a; for (a = 0; a < size; a++) { address[a] = _B0 (data); data >>= 8; } } } static void data_msb_set (SANE_Byte * address, SANE_Int data, SANE_Int size) { if ((address != NULL) && (size > 0) && (size < 5)) { SANE_Int a; for (a = size - 1; a >= 0; a--) { address[a] = _B0 (data); data >>= 8; } } } static SANE_Int data_swap_endianess (SANE_Int address, SANE_Int size) { SANE_Int rst = 0; if ((size > 0) && (size < 5)) { SANE_Int a; for (a = 0; a < size; a++) { rst = (rst << 8) | _B0 (address); address >>= 8; } } return rst; } static void Lamp_SetGainMode (struct st_device *dev, SANE_Byte * Regs, SANE_Int resolution, SANE_Byte gainmode) { DBG (DBG_FNC, "> Lamp_SetGainMode(*Regs, resolution=%i, gainmode=%i):\n", resolution, gainmode); if (dev->chipset->model == RTS8822L_02A) { /* hp4370 */ SANE_Int data1, data2; data1 = data_lsb_get (&Regs[0x154], 2) & 0xfe7f; data2 = data_lsb_get (&Regs[0x156], 2); switch (resolution) { case 4800: data2 |= 0x40; data1 &= 0xffbf; break; case 100: case 150: case 200: case 300: case 600: case 1200: case 2400: data1 |= 0x40; data2 &= 0xffbf; break; } data_lsb_set (&Regs[0x154], data1, 2); data_lsb_set (&Regs[0x156], data2, 2); } else { /* hp3970 hp4070 ua4900 */ SANE_Int data; data = data_lsb_get (&Regs[0x154], 2) & 0xfe7f; data = (gainmode == FALSE) ? data | 0x0040 : data & 0xffbf; switch (resolution) { case 100: case 200: case 300: case 600: data |= 0x0100; break; case 2400: data |= 0x0180; break; case 1200: if (dev->sensorcfg->type == CIS_SENSOR) data |= 0x80; else if (dev->sensorcfg->type == CCD_SENSOR) data |= 0x0180; break; } data_lsb_set (&Regs[0x0154], data, 2); } } static SANE_Int RTS_Scanner_StartScan (struct st_device *dev) { SANE_Int rst = ERROR; /* default */ SANE_Int data; DBG (DBG_FNC, "+ RTS_Scanner_StartScan():\n"); v14b4 = 1; /* TEMPORAL */ data = 0; Lamp_PWM_DutyCycle_Get (dev, &data); data = _B0 (data); DBG (DBG_FNC, "-> Pwm used = %i\n", data); /* windows driver saves pwm used, in file usbfile Section [SCAN_PARAM], field PwmUsed */ dev->status->cancel = FALSE; if (Scan_Start (dev) == OK) { SANE_Int transferred; rst = OK; if (dev->scanning->imagebuffer != NULL) { free (dev->scanning->imagebuffer); dev->scanning->imagebuffer = NULL; } SetLock (dev->usb_handle, NULL, (scan2.depth == 16) ? FALSE : TRUE); /* Reservamos los buffers necesarios para leer la imagen */ Reading_CreateBuffers (dev); if (dev->Resize->type != RSZ_NONE) Resize_Start (dev, &transferred); /* 6729 */ RTS_ScanCounter_Inc (dev); } DBG (DBG_FNC, "- RTS_Scanner_StartScan: %i\n", rst); return rst; } static void Triplet_Gray (SANE_Byte * pPointer1, SANE_Byte * pPointer2, SANE_Byte * buffer, SANE_Int channels_count) { /* pPointer1 = FAB8 pPointer2 = FABC buffer = FAC0 channels_count = FAC4 */ SANE_Int value; SANE_Int channel_size; DBG (DBG_FNC, "> Triplet_Gray(*pPointer1, *pPointer2, *buffer, channels_count=%i)\n", channels_count); channel_size = (scan2.depth > 8) ? 2 : 1; channels_count = channels_count / 2; while (channels_count > 0) { value = data_lsb_get (pPointer1, channel_size); data_lsb_set (buffer, value, channel_size); value = data_lsb_get (pPointer2, channel_size); data_lsb_set (buffer + channel_size, value, channel_size); pPointer1 += 2 * channel_size; pPointer2 += 2 * channel_size; buffer += 2 * channel_size; channels_count--; } } static void Triplet_Lineart (SANE_Byte * pPointer1, SANE_Byte * pPointer2, SANE_Byte * buffer, SANE_Int channels_count) { /* Composing colour in lineart mode */ SANE_Int dots_count = 0; SANE_Int channel; SANE_Byte mask; SANE_Byte value; SANE_Int C; DBG (DBG_FNC, "> Triplet_Lineart(*pPointer1, *pPointer2, *buffer, channels_count=%i)\n", channels_count); if (channels_count > 0) { dots_count = (channels_count + 1) / 2; while (dots_count > 0) { mask = 0x80; channel = 2; do { value = 0; for (C = 4; C > 0; C--) { value = (value << 2) + (((*pPointer2 & mask) << 1) | (*pPointer1 & mask)); mask = mask >> 1; } *buffer = value; buffer++; channel--; } while (channel > 0); pPointer2 += 2; pPointer1 += 2; dots_count--; } } } static SANE_Int Arrange_NonColour (struct st_device *dev, SANE_Byte * buffer, SANE_Int buffer_size, SANE_Int * transferred) { /* buffer : fadc buffer_size : fae0 */ SANE_Int lines_count = 0; /* ebp */ SANE_Int channels_count = 0; /* fadc pisa buffer */ SANE_Int rst = ERROR; struct st_scanning *scn; DBG (DBG_FNC, "+ Arrange_NonColour(*buffer, buffer_size=%i, *transferred):\n", buffer_size); /* this is just to make code more legible */ scn = dev->scanning; if (scn->imagebuffer == NULL) { if ((scn->arrange_hres == TRUE) || (scan2.colormode == CM_LINEART)) { scn->bfsize = (scn->arrange_sensor_evenodd_dist + 1) * line_size; scn->imagebuffer = (SANE_Byte *) malloc (scn->bfsize * sizeof (SANE_Byte)); if (scn->imagebuffer != NULL) { if (Read_Block (dev, scn->bfsize, scn->imagebuffer, transferred) == OK) { scn->channel_size = (scan2.depth == 8) ? 1 : 2; scn->desp1[CL_RED] = 0; scn->desp2[CL_RED] = scn->channel_size + (scn->arrange_sensor_evenodd_dist * line_size); scn->pColour2[CL_RED] = scn->imagebuffer + scn->desp2[CL_RED]; scn->pColour1[CL_RED] = scn->imagebuffer + scn->desp1[CL_RED]; rst = OK; } } } } else rst = OK; /* b0f4 */ if (rst == OK) { scn->imagepointer = scn->imagebuffer; lines_count = buffer_size / line_size; channels_count = line_size / scn->channel_size; while (lines_count > 0) { if (scan2.colormode == CM_LINEART) Triplet_Lineart (scn->pColour1[CL_RED], scn->pColour2[CL_RED], buffer, channels_count); else Triplet_Gray (scn->pColour1[CL_RED], scn->pColour2[CL_RED], buffer, channels_count); buffer += line_size; scn->arrange_size -= bytesperline; lines_count--; if (lines_count == 0) { if ((scn->arrange_size | v15bc) == 0) break; } rst = Read_Block (dev, line_size, scn->imagepointer, transferred); if (rst != OK) break; if (scn->arrange_hres == TRUE) { scn->desp2[CL_RED] = (line_size + scn->desp2[CL_RED]) % scn->bfsize; scn->desp1[CL_RED] = (line_size + scn->desp1[CL_RED]) % scn->bfsize; scn->pColour2[CL_RED] = scn->imagebuffer + scn->desp2[CL_RED]; scn->pColour1[CL_RED] = scn->imagebuffer + scn->desp1[CL_RED]; } /* b21d */ scn->imagepointer += line_size; if (scn->imagepointer >= (scn->imagebuffer + scn->bfsize)) scn->imagepointer = scn->imagebuffer; } } /* 2246 */ DBG (DBG_FNC, "- Arrange_NonColour(*transferred=%i): %i\n", *transferred, rst); return rst; } static SANE_Int Resize_Decrease (SANE_Byte * to_buffer, SANE_Int to_resolution, SANE_Int to_width, SANE_Byte * from_buffer, SANE_Int from_resolution, SANE_Int from_width, SANE_Int myresize_mode) { /* to_buffer = FAC8 = 0x236200 to_resolution = FACC = 0x4b to_width = FAD0 = 0x352 from_buffer = FAD4 = 0x235460 from_resolution = FAD8 = 0x64 from_width = FADC = 0x46d myresize_mode = FAE0 = 1 */ SANE_Int rst = ERROR; SANE_Int channels = 0; /* fac8 */ SANE_Int depth = 0; /* fae0 */ SANE_Int color[3] = { 0, 0, 0 }; /* fab8 | fabc | fac0 */ SANE_Int to_pos = 0; /* fad4 */ SANE_Int rescont = 0; SANE_Int from_pos = 0; /* fab4 */ SANE_Int C; SANE_Int smres = 0; /* fab0 */ SANE_Int value; SANE_Int channel_size; to_resolution = to_resolution & 0xffff; from_resolution = from_resolution & 0xffff; DBG (DBG_FNC, "+ Resize_Decrease(*to_buffer, to_resolution=%i, to_width=%i, *from_buffer, from_resolution=%i, from_width=%i, myresize_mode=%i):\n", to_resolution, to_width, from_resolution, from_width, myresize_mode); if (myresize_mode != RSZ_LINEART) { switch (myresize_mode) { case RSZ_GRAYL: channels = 1; depth = 8; break; case RSZ_COLOURL: channels = 3; depth = 8; break; case RSZ_COLOURH: channels = 3; depth = 16; break; case RSZ_GRAYH: channels = 1; depth = 16; break; } channel_size = (depth > 8) ? 2 : 1; to_pos = 0; rescont = 0; while (to_pos < to_width) { from_pos++; if (from_pos > from_width) from_buffer -= (((depth + 7) / 8) * channels); rescont += to_resolution; if (rescont < from_resolution) { /* Adds 3 color channel values */ for (C = 0; C < channels; C++) { color[C] += data_lsb_get (from_buffer, channel_size) * to_resolution; from_buffer += channel_size; } } else { /* fc3c */ to_pos++; smres = to_resolution - (rescont - from_resolution); for (C = 0; C < channels; C++) { value = ((data_lsb_get (from_buffer, channel_size) * smres) + color[C]) / from_resolution; data_lsb_set (to_buffer, value, channel_size); color[C] = data_lsb_get (from_buffer, channel_size) * (rescont - from_resolution); to_buffer += channel_size; from_buffer += channel_size; } rescont -= from_resolution; } } rst = OK; } else { /* fd60 */ SANE_Int bit, pos, desp, rescont2; *to_buffer = 0; bit = 0; pos = 0; desp = 0; rescont = 0; rescont2 = 0; if (to_width > 0) { do { if (bit == 8) { /* fda6 */ bit = 0; to_buffer++; *to_buffer = 0; } rescont += to_resolution; if (rescont < from_resolution) { if ((*from_buffer & (0x80 >> desp)) != 0) rescont2 += to_resolution; } else { /*fdd5 */ pos++; rescont -= from_resolution; if ((*from_buffer & (0x80 >> desp)) != 0) /*fdee */ rescont2 += (to_resolution - rescont); if (rescont2 > (to_resolution / 2)) /* fe00 */ *to_buffer = _B0 (*to_buffer | (0x80 >> bit)); rescont2 = ((*from_buffer & (0x80 >> desp)) != 0) ? rescont : 0; bit++; } /* fe2f */ desp++; if (desp == 8) { desp = 0; from_buffer++; } } while (pos < to_width); } else rst = OK; } DBG (DBG_FNC, "- Resize_Decrease: %i\n", rst); return rst; } static SANE_Int Resize_Increase (SANE_Byte * to_buffer, SANE_Int to_resolution, SANE_Int to_width, SANE_Byte * from_buffer, SANE_Int from_resolution, SANE_Int from_width, SANE_Int myresize_mode) { /* to_buffer = FAC8 = 0x2353f0 to_resolution = FACC = 0x4b to_width = FAD0 = 0x352 from_buffer = FAD4 = 0x234650 from_resolution = FAD8 = 0x64 from_width = FADC = 0x46d myresize_mode = FAE0 = 1 */ SANE_Int rst = ERROR; SANE_Int desp; /* fac0 */ SANE_Byte *myp2; /* faac */ SANE_Int mywidth; /* fab4 fab8 */ SANE_Int mychannels; /* fabc */ SANE_Int channels = 0; /* faa4 */ SANE_Int depth = 0; /* faa8 */ SANE_Int pos = 0; /* fae0 */ SANE_Int rescount; SANE_Int val6 = 0; SANE_Int val7 = 0; SANE_Int value; /**/ DBG (DBG_FNC, "+ Resize_Increase(*to_buffer, to_resolution=%i, to_width=%i, *from_buffer, from_resolution=%i, from_width=%i, myresize_mode=%i):\n", to_resolution, to_width, from_resolution, from_width, myresize_mode); if (myresize_mode != RSZ_LINEART) { switch (myresize_mode) { case RSZ_GRAYL: channels = 1; depth = 8; break; case RSZ_COLOURL: channels = 3; depth = 8; break; case RSZ_COLOURH: channels = 3; depth = 16; break; case RSZ_GRAYH: channels = 1; depth = 16; break; } if (channels > 0) { SANE_Byte channel_size; SANE_Byte *p_dst; /* fac8 */ SANE_Byte *p_src; /* fad4 */ desp = to_buffer - from_buffer; myp2 = from_buffer; channel_size = (depth == 8) ? 1 : 2; for (mychannels = 0; mychannels < channels; mychannels++) { pos = 0; rescount = (from_resolution / 2) + to_resolution; p_src = myp2; p_dst = myp2 + desp; /* f938 */ val7 = data_lsb_get (p_src, channel_size); if (to_width > 0) { for (mywidth = 0; mywidth < to_width; mywidth++) { if (rescount >= to_resolution) { rescount -= to_resolution; val6 = val7; pos++; if (pos < from_width) { p_src += (channels * channel_size); val7 = data_lsb_get (p_src, channel_size); } } /*f9a5 */ data_lsb_set (p_dst, ((((to_resolution - rescount) * val6) + (val7 * rescount)) / to_resolution), channel_size); rescount += from_resolution; p_dst += (channels * channel_size); } } myp2 += channel_size; } rst = OK; } else rst = OK; } else { /* RSZ_LINEART mode */ /* fa02 */ /* to_buffer = FAC8 = 0x2353f0 to_resolution = FACC = 0x4b to_width = FAD0 = 0x352 from_buffer = FAD4 = 0x234650 from_resolution = FAD8 = 0x64 from_width = FADC = 0x46d myresize_mode = FAE0 = 1 */ SANE_Int myres2; /* fac8 */ SANE_Int sres; SANE_Int lfae0; SANE_Int lfad8; SANE_Int myres; SANE_Int cont = 1; SANE_Int someval; SANE_Int bit; /*lfaa8 */ myres2 = from_resolution; sres = (myres2 / 2) + to_resolution; value = _B0 (*from_buffer); bit = 0; lfae0 = 0; lfad8 = value >> 7; someval = lfad8; *to_buffer = 0; if (to_width > 0) { myres = to_resolution; to_resolution = myres / 2; do { if (sres >= myres) { sres -= myres; lfae0++; cont++; lfad8 = someval; if (lfae0 < from_width) { if (cont == 8) { cont = 0; from_buffer++; } bit = (((0x80 >> cont) & *from_buffer) != 0) ? 1 : 0; } } /*faa6 */ if ((((myres - sres) * lfad8) + (bit * sres)) > to_resolution) *to_buffer |= (0x80 >> bit); bit++; if (bit == 8) { bit = 0; to_buffer++; *to_buffer = 0; } to_width--; sres += myres2; } while (to_width > 0); rst = OK; } } DBG (DBG_FNC, "- Resize_Increase: %i\n", rst); return rst; } static SANE_Int Resize_Start (struct st_device *dev, SANE_Int * transferred) { SANE_Int rst = ERROR; struct st_resize *rz = dev->Resize; DBG (DBG_FNC, "+ Resize_Start(*transferred):\n"); if (Resize_CreateBuffers (dev, line_size, rz->bytesperline, rz->bytesperline) == ERROR) return ERROR; if (arrangeline2 == FIX_BY_SOFT) { /* fee0 */ if (scan2.colormode == CM_COLOR) rst = Arrange_Colour (dev, rz->v3624, line_size, transferred); else rst = Arrange_NonColour (dev, rz->v3624, line_size, transferred); } else rst = Read_Block (dev, line_size, rz->v3624, transferred); /* ff03 */ /* Redimensionado */ switch (rz->type) { case RSZ_DECREASE: /* ff1b */ Resize_Decrease (rz->v3628, rz->resolution_x, rz->towidth, rz->v3624, scan2.resolution_x, rz->fromwidth, rz->mode); break; case RSZ_INCREASE: /* ff69 */ rz->rescount = 0; Resize_Increase (rz->v3628, rz->resolution_x, rz->towidth, rz->v3624, scan2.resolution_x, rz->fromwidth, rz->mode); if (arrangeline2 == FIX_BY_SOFT) { /* ffb1 */ if (scan2.colormode == CM_COLOR) rst = Arrange_Colour (dev, rz->v3624, line_size, transferred); else rst = Arrange_NonColour (dev, rz->v3624, line_size, transferred); } else rst = Read_Block (dev, line_size, rz->v3624, transferred); /* ffe0 */ /* fff2 */ Resize_Increase (rz->v362c, rz->resolution_x, rz->towidth, rz->v3624, scan2.resolution_x, rz->fromwidth, rz->mode); break; } /* 002a */ DBG (DBG_FNC, "- Resize_Start(*transferred=%i): %i\n", *transferred, rst); return rst; } static SANE_Int Resize_CreateBuffers (struct st_device *dev, SANE_Int size1, SANE_Int size2, SANE_Int size3) { SANE_Int rst = ERROR; struct st_resize *rz = dev->Resize; rz->v3624 = (SANE_Byte *) malloc ((size1 + 0x40) * sizeof (SANE_Byte)); rz->v3628 = (SANE_Byte *) malloc ((size2 + 0x40) * sizeof (SANE_Byte)); rz->v362c = (SANE_Byte *) malloc ((size3 + 0x40) * sizeof (SANE_Byte)); if ((rz->v3624 == NULL) || (rz->v3628 == NULL) || (rz->v362c == NULL)) Resize_DestroyBuffers (dev); else rst = OK; DBG (DBG_FNC, "> Resize_CreateBuffers(size1=%i, size2=%i, size3=%i): %i\n", size1, size2, size3, rst); return rst; } static SANE_Int Resize_DestroyBuffers (struct st_device *dev) { struct st_resize *rz = dev->Resize; if (rz->v3624 != NULL) free (rz->v3624); if (rz->v3628 != NULL) free (rz->v3628); if (rz->v362c != NULL) free (rz->v362c); rz->v3624 = NULL; rz->v3628 = NULL; rz->v362c = NULL; return OK; } static SANE_Int Reading_DestroyBuffers (struct st_device *dev) { DBG (DBG_FNC, "> Reading_DestroyBuffers():\n"); if (dev->Reading->DMABuffer != NULL) free (dev->Reading->DMABuffer); if (dev->scanning->imagebuffer != NULL) { free (dev->scanning->imagebuffer); dev->scanning->imagebuffer = NULL; } memset (dev->Reading, 0, sizeof (struct st_readimage)); return OK; } static SANE_Int Gamma_SendTables (struct st_device *dev, SANE_Byte * Regs, SANE_Byte * gammatable, SANE_Int size) { SANE_Int rst = ERROR; DBG (DBG_FNC, "+ Gamma_SendTables(*Regs, *gammatable, size=%i):\n", size); if ((gammatable != NULL) && (size > 0)) { SANE_Int transferred; SANE_Int first_table; SANE_Int cont = 0; SANE_Int retry = TRUE; SANE_Byte *mybuffer; /* lock */ SetLock (dev->usb_handle, Regs, TRUE); first_table = (data_lsb_get (&Regs[0x1b4], 2) & 0x3fff) >> 4; mybuffer = (SANE_Byte *) malloc (sizeof (SANE_Byte) * size); if (mybuffer != NULL) { /* Try to send buffer during 10 seconds */ long tick = GetTickCount () + 10000; while ((retry == TRUE) && (tick > GetTickCount ())) { retry = FALSE; /* Operation type 0x14 */ if (IWrite_Word (dev->usb_handle, 0x0000, 0x0014, 0x0800) == OK) { /* Send size to write */ if (RTS_DMA_Enable_Write (dev, 0x0000, size, first_table) == OK) { /* Send data */ if (Bulk_Operation (dev, BLK_WRITE, size, gammatable, &transferred) == OK) { /* Send size to read */ if (RTS_DMA_Enable_Read (dev, 0x0000, size, first_table) == OK) { /* Retrieve data */ if (Bulk_Operation (dev, BLK_READ, size, mybuffer, &transferred) == OK) { /* Check data */ while ((cont < size) && (retry == FALSE)) { if (mybuffer[cont] != gammatable[cont]) retry = TRUE; cont++; } if (retry == FALSE) rst = OK; } } } } } } free (mybuffer); } /* unlock */ SetLock (dev->usb_handle, Regs, FALSE); } DBG (DBG_FNC, "- Gamma_SendTables: %i\n", rst); return rst; } static SANE_Int Gamma_GetTables (struct st_device *dev, SANE_Byte * Gamma_buffer) { SANE_Int rst = ERROR; DBG (DBG_FNC, "+ Gamma_GetTables(SANE_Byte *Gamma_buffer):\n"); if (Gamma_buffer == NULL) return ERROR; /* Operation type 0x14 */ if (IWrite_Word (dev->usb_handle, 0x0000, 0x0014, 0x0800) == 0x00) { SANE_Int size = 768; if (RTS_DMA_Enable_Read (dev, 0x0000, size, 0) == OK) { SANE_Int transferred = 0; usleep (1000 * 500); /* Read buffer */ rst = Bulk_Operation (dev, BLK_READ, size, Gamma_buffer, &transferred); } } DBG (DBG_FNC, "- Gamma_GetTables: %i\n", rst); return rst; } static void Gamma_FreeTables () { SANE_Int c; DBG (DBG_FNC, "> Gamma_FreeTables()\n"); for (c = 0; c < 3; c++) { if (hp_gamma->table[c] != NULL) { free (hp_gamma->table[c]); hp_gamma->table[c] = NULL; } } use_gamma_tables = FALSE; } static void RTS_Scanner_StopScan (struct st_device *dev, SANE_Int wait) { SANE_Byte data; DBG (DBG_FNC, "+ RTS_Scanner_StopScan():\n"); data = 0; Reading_DestroyBuffers (dev); Resize_DestroyBuffers (dev); RTS_DMA_Reset (dev); data_bitset (&dev->init_regs[0x60b], 0x10, 0); data_bitset (&dev->init_regs[0x60a], 0x40, 0); if (Write_Buffer (dev->usb_handle, 0xee0a, &dev->init_regs[0x60a], 2) == OK) Motor_Change (dev, dev->init_regs, 3); usleep (1000 * 200); if (wait == FALSE) { Read_Byte (dev->usb_handle, 0xe801, &data); if ((data & 0x02) == 0) { if (Head_IsAtHome (dev, dev->init_regs) == FALSE) { /* clear execution bit */ data_bitset (&dev->init_regs[0x00], 0x80, 0); Write_Byte (dev->usb_handle, 0x00, dev->init_regs[0x00]); Head_ParkHome (dev, TRUE, dev->motorcfg->parkhomemotormove); } } } else { /*66a1 */ /* clear execution bit */ data_bitset (&dev->init_regs[0x00], 0x80, 0); Write_Byte (dev->usb_handle, 0x00, dev->init_regs[0x00]); if (Head_IsAtHome (dev, dev->init_regs) == FALSE) Head_ParkHome (dev, TRUE, dev->motorcfg->parkhomemotormove); } /*66e0 */ RTS_Enable_CCD (dev, dev->init_regs, 0); Lamp_Status_Timer_Set (dev, 13); DBG (DBG_FNC, "- RTS_Scanner_StopScan()\n"); } static SANE_Int Reading_CreateBuffers (struct st_device *dev) { SANE_Byte data; SANE_Int mybytesperline; SANE_Int mybuffersize, a, b; DBG (DBG_FNC, "+ Reading_CreateBuffers():\n"); data = 0; /* Gets BinarythresholdH */ if (Read_Byte (dev->usb_handle, 0xe9a1, &data) == OK) binarythresholdh = data; mybytesperline = (scan2.depth == 12) ? (bytesperline * 3) / 4 : bytesperline; dev->Reading->Max_Size = 0xfc00; dev->Reading->DMAAmount = 0; a = (RTS_Debug->dmabuffersize / 63); b = (((RTS_Debug->dmabuffersize - a) / 2) + a) >> 0x0f; mybuffersize = ((b << 6) - b) << 10; if (mybuffersize < 0x1f800) mybuffersize = 0x1f800; dev->Reading->DMABufferSize = mybuffersize; /*3FFC00 4193280 */ do { dev->Reading->DMABuffer = (SANE_Byte *) malloc (dev->Reading->DMABufferSize * sizeof (SANE_Byte)); if (dev->Reading->DMABuffer != NULL) break; dev->Reading->DMABufferSize -= dev->Reading->Max_Size; } while (dev->Reading->DMABufferSize >= dev->Reading->Max_Size); /* 6003 */ dev->Reading->Starting = TRUE; dev->Reading->Size4Lines = (mybytesperline > dev->Reading->Max_Size) ? mybytesperline : (dev->Reading->Max_Size / mybytesperline) * mybytesperline; dev->Reading->ImageSize = imagesize; read_v15b4 = v15b4; DBG (DBG_FNC, "- Reading_CreateBuffers():\n"); return OK; } static SANE_Int RTS_ScanCounter_Inc (struct st_device *dev) { /* Keep a count of the number of scans done by this scanner */ SANE_Int idata; DBG (DBG_FNC, "+ RTS_ScanCounter_Inc():\n"); /* check if chipset supports accessing eeprom */ if ((dev->chipset->capabilities & CAP_EEPROM) != 0) { SANE_Byte cdata = 0; SANE_Byte somebuffer[26]; switch (dev->chipset->model) { case RTS8822L_02A: case RTS8822BL_03A: /* value is 4 bytes size starting from address 0x21 in msb format */ if (RTS_EEPROM_ReadInteger (dev->usb_handle, 0x21, &idata) == OK) { idata = data_swap_endianess (idata, 4) + 1; idata = data_swap_endianess (idata, 4); RTS_EEPROM_WriteInteger (dev->usb_handle, 0x21, idata); } break; default: /* value is 4 bytes size starting from address 0x21 in lsb format */ memset (&somebuffer, 0, sizeof (somebuffer)); somebuffer[4] = 0x0c; RTS_EEPROM_ReadInteger (dev->usb_handle, 0x21, &idata); data_lsb_set (&somebuffer[0], idata + 1, 4); RTS_EEPROM_ReadByte (dev->usb_handle, 0x003a, &cdata); somebuffer[25] = cdata; RTS_EEPROM_WriteBuffer (dev->usb_handle, 0x21, somebuffer, 0x1a); break; } } DBG (DBG_FNC, "- RTS_ScanCounter_Inc()\n"); return OK; } static SANE_Int RTS_ScanCounter_Get (struct st_device *dev) { /* Returns the number of scans done by this scanner */ SANE_Int idata = 0; DBG (DBG_FNC, "+ RTS_ScanCounter_Get():\n"); /* check if chipset supports accessing eeprom */ if ((dev->chipset->capabilities & CAP_EEPROM) != 0) { RTS_EEPROM_ReadInteger (dev->usb_handle, 0x21, &idata); switch (dev->chipset->model) { case RTS8822L_02A: case RTS8822BL_03A: /* value is 4 bytes size starting from address 0x21 in msb format */ idata = data_swap_endianess (idata, 4); break; default: /* RTS8822L_01H */ /* value is 4 bytes size starting from address 0x21 in lsb format */ idata &= 0xffffffff; break; } } DBG (DBG_FNC, "- RTS_ScanCounter_Get(): %i\n", idata); return idata; } static SANE_Int Read_Image (struct st_device *dev, SANE_Int buffer_size, SANE_Byte * buffer, SANE_Int * transferred) { SANE_Int rst; SANE_Byte mycolormode; DBG (DBG_FNC, "+ Read_Image(buffer_size=%i, *buffer, *transferred):\n", buffer_size); *transferred = 0; mycolormode = scan2.colormode; rst = ERROR; if ((scan2.colormode != CM_COLOR) && (scan2.channel == 3)) mycolormode = 3; if (dev->Resize->type == RSZ_NONE) { if (arrangeline == FIX_BY_SOFT) { switch (mycolormode) { case CM_COLOR: rst = Arrange_Colour (dev, buffer, buffer_size, transferred); break; case 3: rst = Arrange_Compose (dev, buffer, buffer_size, transferred); break; default: rst = Arrange_NonColour (dev, buffer, buffer_size, transferred); break; } } else rst = Read_Block (dev, buffer_size, buffer, transferred); /*00fe */ } else rst = Read_ResizeBlock (dev, buffer, buffer_size, transferred); /*010d */ DBG (DBG_FNC, "- Read_Image(*transferred=%i): %i\n", *transferred, rst); return rst; } static SANE_Int Arrange_Compose (struct st_device *dev, SANE_Byte * buffer, SANE_Int buffer_size, SANE_Int * transferred) { /* fnb250 0600FA7C 05E10048 buffer 0600FA80 0000F906 buffer_size */ SANE_Byte *mybuffer = buffer; /* fa7c */ SANE_Int mydistance; /*ebp */ SANE_Int mydots; /*fa74 */ SANE_Int channel_size; SANE_Int c; struct st_scanning *scn; /*mywidth = fa70 */ DBG (DBG_FNC, "+ Arrange_Compose(*buffer, buffer_size=%i, *transferred):\n", buffer_size); channel_size = (scan2.depth == 8) ? 1 : 2; /* this is just to make code more legible */ scn = dev->scanning; if (scn->imagebuffer == NULL) { if (dev->sensorcfg->type == CCD_SENSOR) mydistance = (dev->sensorcfg->line_distance * scan2.resolution_y) / dev->sensorcfg->resolution; else mydistance = 0; if (mydistance != 0) { scn->bfsize = (scn->arrange_hres == TRUE) ? scn->arrange_sensor_evenodd_dist : 0; scn->bfsize = line_size * (scn->bfsize + (mydistance * 2) + 1); } else scn->bfsize = line_size * 2; /*b2f0 */ scn->imagebuffer = (SANE_Byte *) malloc (scn->bfsize * sizeof (SANE_Byte)); if (scn->imagebuffer == NULL) return ERROR; scn->imagepointer = scn->imagebuffer; if (Read_Block (dev, scn->bfsize, scn->imagebuffer, transferred) == ERROR) return ERROR; /* Calculate channel displacements */ scn->arrange_orderchannel = FALSE; for (c = CL_RED; c <= CL_BLUE; c++) { if (mydistance == 0) { /*b34e */ if (scn->arrange_hres == FALSE) { if ((((dev->sensorcfg->line_distance * scan2.resolution_y) * 2) / dev->sensorcfg->resolution) == 1) scn->arrange_orderchannel = TRUE; if (scn->arrange_orderchannel == TRUE) scn->desp[c] = ((dev->sensorcfg->rgb_order[c] / 2) * line_size) + (channel_size * c); else scn->desp[c] = channel_size * c; } } else { /*b3e3 */ scn->desp[c] = (dev->sensorcfg->rgb_order[c] * (mydistance * line_size)) + (channel_size * c); if (scn->arrange_hres == TRUE) { /*b43b */ scn->desp1[c] = scn->desp[c]; scn->desp2[c] = ((channel_size * 3) + scn->desp1[c]) + (scn->arrange_sensor_evenodd_dist * line_size); }; } } for (c = CL_RED; c <= CL_BLUE; c++) { if (scn->arrange_hres == TRUE) { scn->pColour2[c] = scn->imagebuffer + scn->desp2[c]; scn->pColour1[c] = scn->imagebuffer + scn->desp1[c]; } else scn->pColour[c] = scn->imagebuffer + scn->desp[c]; } } /*b545 */ buffer_size /= line_size; mydots = line_size / (channel_size * 3); while (buffer_size > 0) { if (scn->arrange_orderchannel == FALSE) { /*b5aa */ if (scn->arrange_hres == TRUE) Triplet_Compose_HRes (scn->pColour1[CL_RED], scn->pColour1[CL_GREEN], scn->pColour1[CL_BLUE], scn->pColour2[CL_RED], scn->pColour2[CL_GREEN], scn->pColour2[CL_BLUE], mybuffer, mydots); else Triplet_Compose_LRes (scn->pColour[CL_RED], scn->pColour[CL_GREEN], scn->pColour[CL_BLUE], mybuffer, mydots); } else Triplet_Compose_Order (dev, scn->pColour[CL_RED], scn->pColour[CL_GREEN], scn->pColour[CL_BLUE], mybuffer, mydots); /*b5f8 */ mybuffer += line_size; scn->arrange_size -= bytesperline; if (scn->arrange_size < 0) v15bc--; buffer_size--; if (buffer_size == 0) { if ((scn->arrange_size | v15bc) == 0) return OK; } /*b63f */ if (Read_Block (dev, line_size, scn->imagepointer, transferred) == ERROR) return ERROR; for (c = CL_RED; c <= CL_BLUE; c++) { if (scn->arrange_hres == TRUE) { /*b663 */ scn->desp2[c] = (scn->desp2[c] + line_size) % scn->bfsize; scn->desp1[c] = (scn->desp1[c] + line_size) % scn->bfsize; scn->pColour2[c] = scn->imagebuffer + scn->desp2[c]; scn->pColour1[c] = scn->imagebuffer + scn->desp1[c]; } else { /*b74a */ scn->desp[c] = (scn->desp[c] + line_size) % scn->bfsize; scn->pColour[c] = scn->imagebuffer + scn->desp[c]; } } /*b7be */ scn->imagepointer += line_size; if (scn->imagepointer >= (scn->imagebuffer + scn->bfsize)) scn->imagepointer = scn->imagebuffer; } return OK; } static void Triplet_Compose_HRes (SANE_Byte * pRed1, SANE_Byte * pGreen1, SANE_Byte * pBlue1, SANE_Byte * pRed2, SANE_Byte * pGreen2, SANE_Byte * pBlue2, SANE_Byte * buffer, SANE_Int Width) { SANE_Int Value; SANE_Int Channel_size; SANE_Int max_value; DBG (DBG_FNC, "> Triplet_Compose_HRes(*pRed1, *pGreen1, *pBlue1, *pRed2 *pGreen2, *pBlue2, *buffer, Width=%i):\n", Width); Width /= 2; Channel_size = (scan2.depth > 8) ? 2 : 1; max_value = (1 << scan2.depth) - 1; while (Width > 0) { Value = data_lsb_get (pRed1, Channel_size) + data_lsb_get (pGreen1, Channel_size) + data_lsb_get (pBlue1, Channel_size); Value = min (Value, max_value); if (v1600 != NULL) { if (scan2.depth > 8) Value = *(v1600 + (Value >> 8)) | _B0 (Value); else Value = *(v1600 + Value); } data_lsb_set (buffer, Value, Channel_size); buffer += Channel_size; Value = data_lsb_get (pRed2, Channel_size) + data_lsb_get (pGreen2, Channel_size) + data_lsb_get (pBlue2, Channel_size); Value = min (Value, max_value); if (v1600 != NULL) { if (scan2.depth > 8) Value = *(v1600 + (Value >> 8)) | _B0 (Value); else Value = *(v1600 + Value); } data_lsb_set (buffer, Value, Channel_size); buffer += Channel_size; pRed1 += 6 * Channel_size; pGreen1 += 6 * Channel_size; pBlue1 += 6 * Channel_size; pRed2 += 6 * Channel_size; pGreen2 += 6 * Channel_size; pBlue2 += 6 * Channel_size; Width--; } } static void Triplet_Compose_Order (struct st_device *dev, SANE_Byte * pRed, SANE_Byte * pGreen, SANE_Byte * pBlue, SANE_Byte * buffer, SANE_Int dots) { SANE_Int Value; DBG (DBG_FNC, "> Triplet_Compose_Order(*pRed, *pGreen, *pBlue, *buffer, dots=%i):\n", dots); if (scan2.depth > 8) { /* c0fe */ dots = dots / 2; while (dots > 0) { Value = min (data_lsb_get (pRed, 2) + data_lsb_get (pGreen, 2) + data_lsb_get (pBlue, 2), 0xffff); if (v1600 != NULL) Value = (*(v1600 + (Value >> 8)) << 8) | _B0 (Value); data_lsb_set (buffer, Value, 2); buffer += 2; pRed += 6; pGreen += 6; pBlue += 6; dots--; } } else { SANE_Byte *myp1, *myp2, *myp3; if (dev->sensorcfg->rgb_order[CL_RED] == 1) { myp1 = pRed; myp2 = pGreen; myp3 = pBlue; } else if (dev->sensorcfg->rgb_order[CL_GREEN] == 1) { myp1 = pGreen; myp2 = pRed; myp3 = pBlue; } else { myp1 = pBlue; myp2 = pRed; myp3 = pGreen; } while (dots > 0) { Value = min (((*myp1 + *(line_size + myp1)) / 2) + *myp2 + *myp3, 0xff); *buffer = (v1600 == NULL) ? _B0 (Value) : *(v1600 + Value); buffer++; myp1 += 3; myp2 += 3; myp3 += 3; dots--; } } } static void Triplet_Compose_LRes (SANE_Byte * pRed, SANE_Byte * pGreen, SANE_Byte * pBlue, SANE_Byte * buffer, SANE_Int dots) { SANE_Int Value; SANE_Int Channel_size; SANE_Int max_value; DBG (DBG_FNC, "> Triplet_Compose_LRes(*pRed, *pGreen, *pBlue, *buffer, dots=%i):\n", dots); Channel_size = (scan2.depth > 8) ? 2 : 1; max_value = (1 << scan2.depth) - 1; /*bf59 */ while (dots > 0) { Value = data_lsb_get (pRed, Channel_size) + data_lsb_get (pGreen, Channel_size) + data_lsb_get (pBlue, Channel_size); Value = min (Value, max_value); if (v1600 != NULL) { if (scan2.depth > 8) Value = (*(v1600 + (Value >> 8)) << 8) | _B0 (Value); else Value = _B0 (*(v1600 + Value)); } data_lsb_set (buffer, Value, Channel_size); buffer += Channel_size; pRed += Channel_size * 3; pGreen += Channel_size * 3; pBlue += Channel_size * 3; dots--; } } static void Triplet_Colour_Order (struct st_device *dev, SANE_Byte * pRed, SANE_Byte * pGreen, SANE_Byte * pBlue, SANE_Byte * buffer, SANE_Int Width) { SANE_Int Value; DBG (DBG_FNC, "> Triplet_Colour_Order(*pRed, *pGreen, *pBlue, *buffer, Width=%i):\n", Width); if (scan2.depth > 8) { Width = Width / 2; while (Width > 0) { Value = data_lsb_get (pRed, 2); data_lsb_set (buffer, Value, 2); Value = data_lsb_get (pGreen, 2); data_lsb_set (buffer + 2, Value, 2); Value = data_lsb_get (pBlue, 2); data_lsb_set (buffer + 4, Value, 2); pRed += 6; pGreen += 6; pBlue += 6; buffer += 6; Width--; } } else { SANE_Int Colour; if (dev->sensorcfg->rgb_order[CL_RED] == 1) Colour = CL_RED; else if (dev->sensorcfg->rgb_order[CL_GREEN] == 1) Colour = CL_GREEN; else Colour = CL_BLUE; while (Width > 0) { switch (Colour) { case CL_RED: *buffer = (*pRed + *(pRed + line_size)) / 2; *(buffer + 1) = *pGreen; *(buffer + 2) = *pBlue; break; case CL_GREEN: *buffer = *pRed; *(buffer + 1) = ((*pGreen + *(pGreen + line_size)) / 2); *(buffer + 2) = *pBlue; break; case CL_BLUE: *buffer = *pRed; *(buffer + 1) = *pGreen; *(buffer + 2) = ((*pBlue + *(pBlue + line_size)) / 2); break; } pRed += 3; pGreen += 3; pBlue += 3; buffer += 3; Width--; } } } static void Triplet_Colour_HRes (SANE_Byte * pRed1, SANE_Byte * pGreen1, SANE_Byte * pBlue1, SANE_Byte * pRed2, SANE_Byte * pGreen2, SANE_Byte * pBlue2, SANE_Byte * buffer, SANE_Int Width) { SANE_Int Value; SANE_Int channel_size; SANE_Int c; SANE_Byte *pPointers[6]; pPointers[0] = pRed1; pPointers[1] = pGreen1; pPointers[2] = pBlue1; pPointers[3] = pRed2; pPointers[4] = pGreen2; pPointers[5] = pBlue2; DBG (DBG_FNC, "> Triplet_Colour_HRes(*pRed1, *pGreen1, *pBlue1, *pRed2, *pGreen2, *pBlue2, *buffer, Width=%i):\n", Width); channel_size = (scan2.depth > 8) ? 2 : 1; Width = Width / 2; while (Width > 0) { for (c = 0; c < 6; c++) { Value = data_lsb_get (pPointers[c], channel_size); data_lsb_set (buffer, Value, channel_size); pPointers[c] += (6 * channel_size); buffer += (channel_size); } Width--; } } static void Triplet_Colour_LRes (SANE_Int Width, SANE_Byte * Buffer, SANE_Byte * pChannel1, SANE_Byte * pChannel2, SANE_Byte * pChannel3) { /* 05F0FA4C 04EBAE4A /CALL to Assumed StdFunc6 from hpgt3970.04EBAE45 05F0FA50 00234FF8 |Arg1 = 00234FF8 pChannel3 05F0FA54 002359EF |Arg2 = 002359EF pChannel2 05F0FA58 002363E6 |Arg3 = 002363E6 pChannel1 05F0FA5C 05D10048 |Arg4 = 05D10048 Buffer 05F0FA60 00000352 |Arg5 = 00000352 Width */ /* Esta funcion une los tres canales de color en un triplete Inicialmente cada color está separado en 3 buffers apuntados por pChannel1 ,2 y 3 */ SANE_Int Value; SANE_Int channel_size; SANE_Int c; SANE_Byte *pChannels[3]; pChannels[0] = pChannel3; pChannels[1] = pChannel2; pChannels[2] = pChannel1; DBG (DBG_FNC, "> Triplet_Colour_LRes(Width=%i, *Buffer2, *p1, *p2, *p3):\n", Width); channel_size = (scan2.depth > 8) ? 2 : 1; while (Width > 0) { /* ba74 */ for (c = 0; c < 3; c++) { Value = data_lsb_get (pChannels[c], channel_size); data_lsb_set (Buffer, Value, channel_size); pChannels[c] += channel_size; Buffer += channel_size; } Width--; } } static SANE_Int Read_ResizeBlock (struct st_device *dev, SANE_Byte * buffer, SANE_Int buffer_size, SANE_Int * transferred) { /*The Beach buffer = FA7C 05E30048 buffer_size = FA80 0000F906 */ SANE_Int rst = ERROR; /* fa68 */ SANE_Int lfa54; SANE_Int lfa58; SANE_Byte *pP1; /* fa5c */ SANE_Byte *pP2; /* fa60 */ SANE_Int bOk; struct st_resize *rz = dev->Resize; /* fa74 = Resize->resolution_y */ /* fa70 = Resize->resolution_x */ /* fa64 = scan2.resolution_y */ /* fa6c = scan2.resolution_x */ DBG (DBG_FNC, "+ Read_ResizeBlock(*buffer, buffer_size=%i, *transferred):\n", buffer_size); if (rz->type == RSZ_DECREASE) { lfa58 = 0; do { bOk = 1; if (arrangeline2 == FIX_BY_SOFT) { if (scan2.colormode == CM_COLOR) rst = Arrange_Colour (dev, rz->v3624, line_size, transferred); else rst = Arrange_NonColour (dev, rz->v3624, line_size, transferred); } else rst = Read_Block (dev, line_size, rz->v3624, transferred); /*f2df */ Resize_Decrease (rz->v362c, rz->resolution_x, rz->towidth, rz->v3624, scan2.resolution_x, rz->fromwidth, rz->mode); rz->rescount += rz->resolution_y; if (rz->rescount > scan2.resolution_y) { /*f331 */ rz->rescount -= scan2.resolution_y; if (scan2.depth == 8) { /* f345 */ pP1 = rz->v3628; pP2 = rz->v362c; if (rz->mode == RSZ_LINEART) { /* f36b */ SANE_Int bit = 0; SANE_Byte *pP3 = rz->v362c; SANE_Int value; *buffer = 0; lfa54 = 0; while (lfa54 < rz->towidth) { if (bit == 8) { buffer++; *buffer = 0; pP1++; bit = 0; pP3++; } value = ((*pP1 & (0x80 >> bit)) != 0) ? rz->rescount : 0; if ((*pP3 & (0x80 >> bit)) != 0) value += (scan2.resolution_y - rz->rescount); if (value > rz->resolution_y) *buffer |= (0x80 >> bit); bit++; lfa54++; } } else { /* f414 */ lfa54 = 0; while (lfa54 < rz->bytesperline) { *buffer = _B0 ((((scan2.resolution_y - rz->rescount) * *pP2) + (*pP1 * rz->rescount)) / scan2.resolution_y); pP1++; pP2++; buffer++; lfa54++; } } } else { /* f47d */ lfa54 = 0; pP1 = rz->v3628; pP2 = rz->v362c; if ((rz->bytesperline & 0xfffffffe) > 0) { SANE_Int value; do { value = (((scan2.resolution_y - rz->rescount) * data_lsb_get (pP2, 2)) + (data_lsb_get (pP1, 2) * rz->rescount)) / scan2.resolution_y; data_lsb_set (buffer, value, 2); buffer += 2; pP1 += 2; pP2 += 2; lfa54++; } while (lfa54 < (rz->bytesperline / 2)); } } } else bOk = 0; /* f4fd f502 */ pP1 = rz->v3628; /* swap pointers */ rz->v3628 = rz->v362c; rz->v362c = pP1; } while (bOk == 0); } else { /*f530 */ SANE_Int lfa68; SANE_Int transferred; SANE_Int channel_size; rz->rescount += scan2.resolution_y; lfa58 = 0; if (rz->rescount > rz->resolution_y) { lfa68 = 1; rz->rescount -= rz->resolution_y; } else lfa68 = 0; pP1 = rz->v3628; pP2 = rz->v362c; if (rz->mode == RSZ_LINEART) { /*f592 */ *buffer = 0; if (rz->towidth > 0) { SANE_Int mask, mres; /* lfa60 = rz->resolution_y */ /* lfa7c = rz->resolution_y / 2 */ for (lfa54 = 0; lfa54 < rz->towidth; lfa54++) { mask = 0x80 >> lfa58; mres = ((mask & *pP1) != 0) ? rz->rescount : 0; if ((mask & *pP2) != 0) mres += (rz->resolution_y - rz->rescount); if (mres > (rz->resolution_y / 2)) *buffer = *buffer | mask; lfa58++; if (lfa58 == 8) { lfa58 = 0; buffer++; pP1++; pP2++; *buffer = 0; } } } } else { /*f633 */ channel_size = (scan2.depth > 8) ? 2 : 1; if (rz->rescount < scan2.resolution_y) { if (rz->bytesperline != 0) { SANE_Int value; for (lfa54 = 0; lfa54 < rz->bytesperline; lfa54++) { value = (((scan2.resolution_y - rz->rescount) * data_lsb_get (pP2, channel_size)) + (rz->rescount * data_lsb_get (pP1, channel_size))) / scan2.resolution_y; data_lsb_set (buffer, value, channel_size); pP1 += channel_size; pP2 += channel_size; buffer += channel_size; } } } else memcpy (buffer, rz->v3628, rz->bytesperline); /*f6a8 */ } /*f736 */ if (lfa68 != 0) { SANE_Byte *temp; if (arrangeline2 == FIX_BY_SOFT) { /*f74b */ if (scan2.colormode == CM_COLOR) rst = Arrange_Colour (dev, rz->v3624, line_size, &transferred); else rst = Arrange_NonColour (dev, rz->v3624, line_size, &transferred); } else rst = Read_Block (dev, line_size, rz->v3624, &transferred); /*f77a */ /*f78c */ /* swap buffers */ temp = rz->v3628; rz->v3628 = rz->v362c; rz->v362c = temp; Resize_Increase (temp, rz->resolution_x, rz->towidth, rz->v3624, scan2.resolution_x, rz->fromwidth, rz->mode); } else rst = OK; } DBG (DBG_FNC, "- Read_ResizeBlock(*transferred=%i): %i\n", *transferred, rst); return rst; } static void Split_into_12bit_channels (SANE_Byte * destino, SANE_Byte * fuente, SANE_Int size) { /* Each letter represents a bit abcdefgh 12345678 lmnopqrs << before splitting [efgh1234 0000abcd] [lmnopqrs 00005678] << after splitting, in memory [0000abcd efgh1234] [00005678 lmnopqrs] << resulting channels */ DBG (DBG_FNC, "> Split_into_12bit_channels(*destino, *fuente, size=%i\n", size); if ((destino != NULL) && (fuente != NULL)) { if ((size - (size & 0x03)) != 0) { SANE_Int C; C = (size - (size & 0x03) + 3) / 4; do { *destino = _B0 ((*(fuente + 1) >> 4) + (*fuente << 4)); *(destino + 1) = _B0 (*fuente >> 4); *(destino + 2) = _B0 (*(fuente + 2)); *(destino + 3) = *(fuente + 1) & 0x0f; destino += 4; fuente += 3; C--; } while (C > 0); } /**/ if ((size & 0x03) != 0) { *destino = _B0 ((*(fuente + 1) >> 4) + (*fuente << 4)); *(destino + 1) = _B0 (*fuente >> 4); } } } static SANE_Int Read_NonColor_Block (struct st_device *dev, SANE_Byte * buffer, SANE_Int buffer_size, SANE_Byte ColorMode, SANE_Int * transferred) { /* FA50 05DA0048 buffer FA54 0000F906 buffer_size FA58 00 ColorMode */ SANE_Int rst = OK; SANE_Int lfa38 = 0; SANE_Byte *gamma = v1600; SANE_Int block_bytes_per_line; SANE_Int mysize; SANE_Byte *mybuffer; DBG (DBG_FNC, "+ Read_NonColor_Block(*buffer, buffer_size=%i, ColorMode=%s):\n", buffer_size, dbg_colour (ColorMode)); if (ColorMode != CM_GRAY) { /* Lineart mode */ if ((lineart_width & 7) != 0) lfa38 = 8 - (lineart_width & 7); block_bytes_per_line = (lineart_width + 7) / 8; } else block_bytes_per_line = line_size; /*61b2 */ mysize = (buffer_size / block_bytes_per_line) * bytesperline; mybuffer = (SANE_Byte *) malloc (mysize * sizeof (SANE_Byte)); /*fa40 */ if (mybuffer != NULL) { SANE_Int LinesCount; SANE_Int mysize4lines; SANE_Byte *pBuffer = buffer; SANE_Byte *pImage = NULL; /* fa30 */ SANE_Int puntero; SANE_Int value; do { mysize4lines = (mysize <= dev->Reading->Size4Lines) ? mysize : dev->Reading->Size4Lines; LinesCount = mysize4lines / bytesperline; if (ColorMode == CM_GRAY) { if (scan2.depth == 12) { /* 633b */ /*GRAY Bit mode 12 */ rst = Scan_Read_BufferA (dev, (mysize4lines * 3) / 4, 0, mybuffer, transferred); if (rst == OK) { pImage = mybuffer; pBuffer += LinesCount * block_bytes_per_line; while (LinesCount > 0) { Split_into_12bit_channels (mybuffer, pImage, line_size); pImage += (bytesperline * 3) / 4; LinesCount--; } } else break; } else { /* grayscale 8 and 16 bits */ SANE_Int channel_size; rst = Scan_Read_BufferA (dev, mysize4lines, 0, mybuffer, transferred); if (rst == OK) { channel_size = (scan2.depth > 8) ? 2 : 1; pImage = mybuffer; /* No gamma tables */ while (LinesCount > 0) { if (line_size > 0) { puntero = 0; do { value = data_lsb_get (pImage + puntero, channel_size); if (gamma != NULL) value += *gamma << (8 * (channel_size - 1)); data_lsb_set (pBuffer, value, channel_size); pBuffer += channel_size; puntero += channel_size; } while (puntero < line_size); } pImage += bytesperline; LinesCount--; } } else break; } } else { /*6429 */ /* LINEART */ SANE_Int desp; rst = Scan_Read_BufferA (dev, mysize4lines, 0, mybuffer, transferred); if (rst == OK) { pImage = mybuffer; while (LinesCount > 0) { if (lineart_width > 0) { desp = 0; do { if ((desp % 7) == 0) *pBuffer = 0; /* making a byte bit per bit */ *pBuffer = *pBuffer << 1; /* bit 1 if data is under thresholdh value */ if (*(pImage + desp) >= binarythresholdh) /* binarythresholdh = 0x0c */ *pBuffer = *pBuffer | 1; desp++; if ((desp % 7) == 0) pBuffer++; } while (desp < lineart_width); } if (lfa38 != 0) { *pBuffer = (*pBuffer << lfa38); pBuffer++; } /* 64b0 */ pImage += bytesperline; LinesCount--; } } else break; } /* 64c0 */ mysize -= mysize4lines; } while ((mysize > 0) && (dev->status->cancel == FALSE)); free (mybuffer); } else rst = ERROR; DBG (DBG_FNC, "- Read_NonColor_Block(*transferred=%i): %i\n", *transferred, rst); return rst; } static SANE_Int Read_Block (struct st_device *dev, SANE_Int buffer_size, SANE_Byte * buffer, SANE_Int * transferred) { /* SANE_Int buffer_size fa80 SANE_Byte *buffer fa7c */ /* scan2: 04F0155C 01 08 00 02 03 00 58 02 ..X 04F01564 58 02 58 02 C5 00 00 00 XXÅ... 04F0156C B4 07 00 00 8B 01 00 00 ´..‹.. 04F01574 10 06 00 00 EC 13 00 00 ..ì.. 04F0157C B2 07 00 00 B4 07 00 00 ²..´.. 04F01584 CF 08 00 00 Ï.. arrangeline2 = 1 */ SANE_Int rst, LinesCount; SANE_Int mysize; SANE_Byte *readbuffer = NULL; SANE_Byte *pImage = NULL; DBG (DBG_FNC, "+ Read_Block(buffer_size=%i, *buffer):\n", buffer_size); rst = ERROR; *transferred = 0; if ((scan2.colormode != CM_COLOR) && (scan2.channel == 3) && (arrangeline2 != FIX_BY_SOFT)) { /*6510 */ return Read_NonColor_Block (dev, buffer, buffer_size, scan2.colormode, transferred); } /*6544 */ mysize = (buffer_size / line_size) * bytesperline; readbuffer = (SANE_Byte *) malloc (mysize * sizeof (SANE_Byte)); pImage = buffer; if (readbuffer != NULL) { do { buffer_size = (dev->Reading->Size4Lines < mysize) ? dev->Reading->Size4Lines : mysize; LinesCount = buffer_size / bytesperline; if (scan2.depth == 12) { rst = Scan_Read_BufferA (dev, buffer_size, 0, readbuffer, transferred); if (rst == OK) { if (LinesCount > 0) { SANE_Byte *destino, *fuente; destino = buffer; fuente = readbuffer; do { Split_into_12bit_channels (destino, fuente, line_size); destino += line_size; fuente += (bytesperline * 3) / 4; LinesCount--; } while (LinesCount > 0); } } else break; } else { /*65d9 */ rst = Scan_Read_BufferA (dev, buffer_size, 0, readbuffer, transferred); if (rst == OK) { memcpy (pImage, readbuffer, *transferred); /* apply white shading correction */ if ((RTS_Debug->wshading == TRUE) && (scan2.scantype == ST_NORMAL)) WShading_Emulate (pImage, &wshading->ptr, *transferred, scan2.depth); pImage += *transferred; } else break; } /*6629 */ mysize -= buffer_size; } while ((mysize > 0) && (dev->status->cancel == FALSE)); free (readbuffer); } DBG (DBG_FNC, "- Read_Block(*transferred=%i): %i\n", *transferred, rst); return rst; } static SANE_Int Scan_Read_BufferA (struct st_device *dev, SANE_Int buffer_size, SANE_Int arg2, SANE_Byte * pBuffer, SANE_Int * bytes_transferred) { SANE_Int rst = OK; SANE_Byte *ptBuffer = NULL; SANE_Byte *ptImg = NULL; struct st_readimage *rd = dev->Reading; DBG (DBG_FNC, "+ Scan_Read_BufferA(buffer_size=%i, arg2, *pBuffer, *bytes_transferred):\n", buffer_size); (void) arg2; /* silence gcc */ *bytes_transferred = 0; if (pBuffer != NULL) { ptBuffer = pBuffer; while ((buffer_size > 0) && (rst == OK) && (dev->status->cancel == FALSE)) { /* Check if we've already started */ if (rd->Starting == TRUE) { /* Get channels per dot and channel's size in bytes */ SANE_Byte data; rd->Channels_per_dot = 1; if (Read_Byte (dev->usb_handle, 0xe812, &data) == OK) { data = data >> 6; if (data != 0) rd->Channels_per_dot = data; } rd->Channel_size = 1; if (Read_Byte (dev->usb_handle, 0xee0b, &data) == OK) if (((data & 0x40) != 0) && ((data & 0x08) == 0)) rd->Channel_size = 2; rd->RDStart = rd->DMABuffer; rd->RDSize = 0; rd->DMAAmount = 0; rd->Starting = FALSE; } /* Is there any data to read from scanner? */ if ((rd->ImageSize > 0) && (rd->RDSize == 0)) { /* Try to read from scanner all possible data to fill DMABuffer */ if (rd->RDSize < rd->DMABufferSize) { SANE_Int iAmount, dofree; /* Check if we have already notify buffer size */ if (rd->DMAAmount <= 0) { /* Initially I suppose that I can read all image */ iAmount = min (rd->ImageSize, rd->Max_Size); rd->DMAAmount = ((RTS_Debug->dmasetlength * 2) / iAmount) * iAmount; rd->DMAAmount = min (rd->DMAAmount, rd->ImageSize); Reading_BufferSize_Notify (dev, 0, rd->DMAAmount); iAmount = min (iAmount, rd->DMABufferSize - rd->RDSize); } else { iAmount = min (rd->DMAAmount, rd->ImageSize); iAmount = min (iAmount, rd->Max_Size); } /* Allocate buffer to read image if it's necessary */ if ((rd->RDSize == 0) && (iAmount <= buffer_size)) { ptImg = ptBuffer; dofree = FALSE; } else { ptImg = (SANE_Byte *) malloc (iAmount * sizeof (SANE_Byte)); dofree = TRUE; } if (ptImg != NULL) { /* We must wait for scanner to get data */ SANE_Int opStatus, sc; sc = (iAmount < rd->Max_Size) ? TRUE : FALSE; opStatus = Reading_Wait (dev, rd->Channels_per_dot, rd->Channel_size, iAmount, &rd->Bytes_Available, 60, sc); /* If something fails, perhaps we can read some bytes... */ if (opStatus != OK) { if (rd->Bytes_Available > 0) iAmount = rd->Bytes_Available; else rst = ERROR; } if (rst == OK) { /* Try to read from scanner */ SANE_Int transferred = 0; opStatus = Bulk_Operation (dev, BLK_READ, iAmount, ptImg, &transferred); DBG (DBG_FNC, "> Scan_Read_BufferA: Bulk read %i bytes\n", transferred); /*if something fails may be we can read some bytes */ iAmount = (SANE_Int) transferred; if (iAmount != 0) { /* Lets copy data into DMABuffer if it's necessary */ if (ptImg != ptBuffer) { SANE_Byte *ptDMABuffer; ptDMABuffer = rd->RDStart + rd->RDSize; if ((ptDMABuffer - rd->DMABuffer) >= rd->DMABufferSize) ptDMABuffer -= rd->DMABufferSize; if ((ptDMABuffer + iAmount) >= (rd->DMABuffer + rd->DMABufferSize)) { SANE_Int rest = iAmount - (rd->DMABufferSize - (ptDMABuffer - rd->DMABuffer)); memcpy (ptDMABuffer, ptImg, iAmount - rest); memcpy (rd->DMABuffer, ptImg + (iAmount - rest), rest); } else memcpy (ptDMABuffer, ptImg, iAmount); rd->RDSize += iAmount; } else { *bytes_transferred += iAmount; buffer_size -= iAmount; } rd->DMAAmount -= iAmount; rd->ImageSize -= iAmount; } else rst = ERROR; } /* Lets free buffer */ if (dofree == TRUE) { free (ptImg); ptImg = NULL; } } else rst = ERROR; } } /* is there any data read from scanner? */ if (rd->RDSize > 0) { /* Add to the given buffer as many bytes as possible */ SANE_Int iAmount; iAmount = min (buffer_size, rd->RDSize); if ((rd->RDStart + iAmount) >= (rd->DMABuffer + rd->DMABufferSize)) { SANE_Int rest = rd->DMABufferSize - (rd->RDStart - rd->DMABuffer); memcpy (ptBuffer, rd->RDStart, rest); memcpy (ptBuffer + rest, rd->DMABuffer, iAmount - rest); rd->RDStart = rd->DMABuffer + (iAmount - rest); } else { memcpy (ptBuffer, rd->RDStart, iAmount); rd->RDStart += iAmount; } ptBuffer += iAmount; rd->RDSize -= iAmount; buffer_size -= iAmount; *bytes_transferred += iAmount; /* if there isn't any data in DMABuffer we can point RDStart to the beginning of DMABuffer */ if (rd->RDSize == 0) rd->RDStart = rd->DMABuffer; } /* in case of all data is read we return OK with bytes_transferred = 0 */ if ((*bytes_transferred == 0) || ((rd->RDSize == 0) && (rd->ImageSize == 0))) break; } if (rst == ERROR) RTS_DMA_Cancel (dev); } DBG (DBG_FNC, "-> *bytes_transferred=%i\n", *bytes_transferred); DBG (DBG_FNC, "-> Reading->ImageSize=%i\n", rd->ImageSize); DBG (DBG_FNC, "-> Reading->DMAAmount=%i\n", rd->DMAAmount); DBG (DBG_FNC, "-> Reading->RDSize =%i\n", rd->RDSize); DBG (DBG_FNC, "- Scan_Read_BufferA: %i\n", rst); return rst; } static SANE_Int Reading_BufferSize_Get (struct st_device *dev, SANE_Byte channels_per_dot, SANE_Int channel_size) { /* returns the amount of bytes in scanner's buffer ready to be read */ SANE_Int rst; DBG (DBG_FNC, "+ Reading_BufferSize_Get(channels_per_dot=%i, channel_size=%i):\n", channels_per_dot, channel_size); rst = 0; if (channel_size > 0) { SANE_Int myAmount; if (channels_per_dot < 1) { /* read channels per dot from registers */ if (Read_Byte (dev->usb_handle, 0xe812, &channels_per_dot) == OK) channels_per_dot = _B0 (channels_per_dot >> 6); if (channels_per_dot == 0) channels_per_dot++; } if (Read_Integer (dev->usb_handle, 0xef16, &myAmount) == OK) rst = ((channels_per_dot * 32) / channel_size) * myAmount; } DBG (DBG_FNC, "- Reading_BufferSize_Get: %i bytes\n", rst); return rst; } static SANE_Int Lamp_Warmup (struct st_device *dev, SANE_Byte * Regs, SANE_Int lamp, SANE_Int resolution) { SANE_Int rst = OK; DBG (DBG_FNC, "+ Lamp_Warmup(*Regs, lamp=%i, resolution=%i)\n", lamp, resolution); if (Regs != NULL) { SANE_Byte flb_lamp, tma_lamp; SANE_Int overdrivetime; Lamp_Status_Get (dev, &flb_lamp, &tma_lamp); /* ensure that selected lamp is switched on */ if (lamp == FLB_LAMP) { overdrivetime = RTS_Debug->overdrive_flb; if (flb_lamp == 0) { /* FLB-Lamp is turned off, lets turn on */ Lamp_Status_Set (dev, Regs, TRUE, FLB_LAMP); waitforpwm = TRUE; } } else { /* is tma device attached to scanner ? */ if (RTS_isTmaAttached (dev) == TRUE) { overdrivetime = RTS_Debug->overdrive_ta; if (tma_lamp == 0) { /* tma lamp is turned off */ Lamp_Status_Set (dev, Regs, FALSE, TMA_LAMP); waitforpwm = TRUE; } } else rst = ERROR; } /* perform warmup process */ if (rst == OK) { Lamp_PWM_Setup (dev, lamp); if (waitforpwm == TRUE) { /*Lamp_PWM_DutyCycle_Set(dev, (lamp == TMA_LAMP)? 0x0e : 0x00); */ if (RTS_Debug->warmup == TRUE) { long ticks = GetTickCount () + overdrivetime; DBG (DBG_VRB, "- Lamp Warmup process. Please wait...\n"); dev->status->warmup = TRUE; while (GetTickCount () <= ticks) usleep (1000 * 200); Lamp_PWM_CheckStable (dev, resolution, lamp); } else DBG (DBG_VRB, "- Lamp Warmup process disabled.\n"); } /*Lamp_PWM_Setup(dev, lamp); if (waitforpwm == TRUE) { if (RTS_Debug->warmup == TRUE) Lamp_PWM_CheckStable(dev, resolution, lamp); waitforpwm = FALSE; } */ } } else rst = ERROR; dev->status->warmup = FALSE; DBG (DBG_FNC, "- Lamp_Warmup: %i\n", rst); return rst; } static SANE_Int Scan_Start (struct st_device *dev) { SANE_Int rst; DBG (DBG_FNC, "+ Scan_Start:\n"); rst = ERROR; if (RTS_Enable_CCD (dev, dev->init_regs, 0x0f) == OK) { SANE_Byte Regs[RT_BUFFER_LEN], mlock; SANE_Int ypos, xpos, runb1; struct st_scanparams scancfg; struct st_hwdconfig hwdcfg; struct st_calibration myCalib; long tick; memcpy (&Regs, dev->init_regs, RT_BUFFER_LEN * sizeof (SANE_Byte)); memcpy (&scancfg, &scan, sizeof (struct st_scanparams)); dbg_ScanParams (&scancfg); /* reserva buffer 6 dwords en fa84-fa9f */ memset (&hwdcfg, 0, sizeof (struct st_hwdconfig)); /* wait till lamp is at home (should use timeout windows driver doesn't use it) */ tick = GetTickCount () + 10000; while ((Head_IsAtHome (dev, Regs) == FALSE) && (tick > GetTickCount ())); if (v14b4 != 0) { SANE_Int lfaa0 = 0; if (GainOffset_Counter_Inc (dev, &lfaa0) != OK) return 0x02; } tick = GetTickCount (); /* set margin references */ Refs_Set (dev, Regs, &scancfg); /* locate head to right position */ Load_StripCoords (scantype, &ypos, &xpos); if (ypos != 0) Head_Relocate (dev, dev->motorcfg->parkhomemotormove, MTR_FORWARD, ypos); /* perform lamp warmup */ if (Lamp_Warmup (dev, Regs, (scancfg.scantype == ST_NORMAL) ? FLB_LAMP : TMA_LAMP, scan.resolution_x) == ERROR) return ERROR; /* Calibration process */ /*592c */ if (Calib_CreateBuffers (dev, &myCalib, v14b4) != OK) return ERROR; /*5947 */ /* if (Calib_BlackShading_jkd(dev, Regs, &myCalib, &scancfg) == OK) Head_ParkHome(dev, TRUE, dev->motorcfg->parkhomemotormove); */ /* if (Calib_test(dev, Regs, &myCalib, &scancfg) == OK ) Head_ParkHome(dev, TRUE, dev->motorcfg->parkhomemotormove); */ /* Calibrate White shading correction */ if ((RTS_Debug->wshading == TRUE) && (scan.scantype == ST_NORMAL)) if (WShading_Calibrate (dev, Regs, &myCalib, &scancfg) == OK) Head_ParkHome (dev, TRUE, dev->motorcfg->parkhomemotormove); hwdcfg.calibrate = RTS_Debug->calibrate; if (RTS_Debug->calibrate != 0) { /* Let's calibrate */ if ((scancfg.colormode != CM_COLOR) && (scancfg.channel == 3)) scancfg.colormode = CM_COLOR; hwdcfg.arrangeline = 0; if (scan.scantype == ST_NORMAL) { /* Calibration for reflective type */ /*59e3 */ memcpy (&Regs, dev->init_regs, RT_BUFFER_LEN * sizeof (SANE_Byte)); if (Calibration (dev, Regs, &scancfg, &myCalib, 0) != OK) { if (v14b4 == 0) Calib_FreeBuffers (&myCalib); return ERROR; } } else { /*59ed */ /* Calibration for negative/slide type */ } /*5af1 */ if (RTS_Debug->ScanWhiteBoard != FALSE) { Head_ParkHome (dev, TRUE, dev->motorcfg->basespeedmotormove); scan.ler = 1; } scancfg.colormode = scan.colormode; } else { /*5b1e */ /*Don't calibrate */ if (scan.scantype == ST_NORMAL) { Lamp_Status_Set (dev, NULL, TRUE, FLB_LAMP); } else { if ((scan.scantype == ST_TA) || (scan.scantype == ST_NEG)) { /*SANE_Int ta_y_start; */ Lamp_Status_Set (dev, NULL, FALSE, TMA_LAMP); /*ta_y_start = get_value(SCAN_PARAM, TA_Y_START, 0x2508, usbfile); ta_y_start += (((((scan.coord.top * 3) * 5) * 5) * 32) / scancfg.resolution_x); if (ta_y_start >= 500) { Head_Relocate(dev, dev->motorcfg->highspeedmotormove, MTR_FORWARD, ta_y_start); scancfg.coord.top = 1; scan.ler = 1; } else { / *5ba9* / if (ta_y_start > 0) { Head_Relocate(dev, dev->motorcfg->basespeedmotormove, MTR_FORWARD, ta_y_start); scancfg.coord.top = 1; scan.ler = 1; } } */ } } } /*5bd0 */ usleep (1000 * 200); hwdcfg.scantype = scan.scantype; hwdcfg.motor_direction = MTR_FORWARD; /* Set Origin */ if ((scan.scantype >= ST_NORMAL) || (scan.scantype <= ST_NEG)) { scancfg.coord.left += scan.ser; scancfg.coord.top += scan.ler; } hwdcfg.sensorevenodddistance = dev->sensorcfg->evenodd_distance; hwdcfg.highresolution = (scancfg.resolution_x <= 1200) ? FALSE : TRUE; /*5c55 */ /* if (RTS_Debug->calibrate == FALSE) { SANE_Int mytop = (((scancfg.coord.top * 5) * 5) * 16) / scancfg.resolution_y; if ((scancfg.resolution_y <= 150)&&(mytop < 300)) { scancfg.coord.top = scancfg.resolution_y / 4; } else { if (mytop < 100) scancfg.coord.top = scancfg.resolution_y / 12; } } */ /*5cd9 */ if (compression != FALSE) hwdcfg.compression = TRUE; /* setting arrangeline option */ hwdcfg.arrangeline = arrangeline; if (scancfg.resolution_x == 2400) { /* 5cfa */ if (scancfg.colormode != CM_COLOR) { if ((scancfg.colormode == CM_GRAY) && (scancfg.channel == 3)) hwdcfg.arrangeline = FIX_BY_SOFT; } else hwdcfg.arrangeline = FIX_BY_SOFT; } /*5d12 */ if (dev->sensorcfg->type == CCD_SENSOR) { /*5d3a */ scancfg.coord.left += 24; switch (scancfg.resolution_x) { case 1200: scancfg.coord.left -= 63; break; case 2400: scancfg.coord.left -= 127; break; } } else { /*5d5a */ /* CIS sensor */ /*5d6d */ scancfg.coord.left += 50; switch (scancfg.resolution_x) { case 1200: scancfg.coord.left -= 63; break; case 2400: scancfg.coord.left -= 127; break; } } /* 5d92 */ DBG (DBG_FNC, " ->Scan_Start xStart=%i, xExtent=%i\n", scancfg.coord.left, scancfg.coord.width); runb1 = 1; if (scan.scantype == ST_NORMAL) { /*5db7 */ if ((scancfg.resolution_x == 1200) || (scancfg.resolution_x == 2400)) { /*5e41 */ if ((scancfg.resolution_y / 10) > scancfg.coord.top) runb1 = 0; } else { if ((scancfg.resolution_x == 600) && (RTS_Debug->usbtype == USB11) && (scancfg.colormode == CM_COLOR)) { /*5ded */ if ((scancfg.resolution_y / 10) > scancfg.coord.top) runb1 = 0; } else { if ((scancfg.resolution_x == 600) || (scancfg.resolution_x == 300)) { /*5e11 */ if (scancfg.resolution_y > scancfg.coord.top) runb1 = 0; } else runb1 = 0; } } } else { /*5e7c *//* entra aquí */ if ((scancfg.resolution_y / 10) > scancfg.coord.top) runb1 = 0; } /*5eb1 */ if (runb1 == 1) /*entra */ { SANE_Int val1 = scancfg.coord.top - (scancfg.resolution_y / 10); scancfg.coord.top -= val1; Head_Relocate (dev, dev->motorcfg->highspeedmotormove, MTR_FORWARD, (dev->motorcfg->resolution / scancfg.resolution_y) * val1); /*x168 */ } /*5efe */ if (RTS_Debug->calibrate != FALSE) { if (use_gamma_tables != FALSE) { hwdcfg.use_gamma_tables = TRUE; hp_gamma->depth = 0; } /*5f24 */ hwdcfg.white_shading = TRUE; hwdcfg.black_shading = TRUE; hwdcfg.unk3 = 0; RTS_Setup (dev, Regs, &scancfg, &hwdcfg, &calibdata->gain_offset); myCalib.shading_type = 0; myCalib.shadinglength = min (myCalib.shadinglength, scan.shadinglength); if (scancfg.colormode != CM_COLOR) { if ((scancfg.channel > 0) && (scancfg.channel < 3)) myCalib.WRef[0] = myCalib.WRef[scancfg.channel]; } RTS_WriteRegs (dev->usb_handle, Regs); /* apply gamma if required */ Gamma_Apply (dev, Regs, &scancfg, &hwdcfg, hp_gamma); Shading_apply (dev, Regs, &scancfg, &myCalib); /* Save to file? */ if (RTS_Debug->DumpShadingData != FALSE) dump_shading (&myCalib); /*5ff9 */ } else RTS_Setup (dev, Regs, &scancfg, &hwdcfg, default_gain_offset); /*602a */ RTS_Debug->calibrate = hwdcfg.calibrate; binarythresholdh = bw_threshold; binarythresholdl = bw_threshold; DBG (DBG_FNC, "> bw threshold -- hi=%i, lo=%i\n", binarythresholdh, binarythresholdl); /* set threshold high */ data_lsb_set (&Regs[0x1a0], binarythresholdh, 2); /* set threshold low */ data_lsb_set (&Regs[0x19e], binarythresholdl, 2); /* if has motorcurves... */ if ((Regs[0xdf] & 0x10) != 0) data_bitset (&Regs[0x01], 0x02, 1); /* Set MLOCK */ mlock = get_value (SCAN_PARAM, MLOCK, 0, usbfile) & 1; data_bitset (&Regs[0x00], 0x10, mlock); /*---x----*/ if (dev->motorcfg->changemotorcurrent != FALSE) Motor_Change (dev, Regs, Motor_GetFromResolution (scancfg.resolution_x)); /* set gain control mode */ Lamp_SetGainMode (dev, Regs, scancfg.resolution_x, Lamp_GetGainMode (dev, scancfg.resolution_x, scan.scantype)); RTS_WaitScanEnd (dev, 15000); if (v14b4 == 0) Calib_FreeBuffers (&myCalib); /* release motor */ Motor_Release (dev); #ifdef developing /* prueba(Regs); dbg_registers(Regs);*/ /*WShading_Calibrate(dev, Regs, &myCalib, &scancfg); */ /*shadingtest1(dev, Regs, &myCalib); */ #endif if (RTS_Warm_Reset (dev) == OK) { RTS_WriteRegs (dev->usb_handle, Regs); usleep (1000 * 500); if (RTS_Execute (dev) == OK) { Lamp_Status_Timer_Set (dev, 0); /* Let scanner some time to store some data */ if ((dev->chipset->model == RTS8822L_02A) && (scancfg.resolution_x > 2400)) usleep (1000 * 5000); rst = OK; } } } DBG (DBG_FNC, "- Scan_Start: %i\n", rst); return rst; } static SANE_Int RTS_Setup_Motor (struct st_device *dev, SANE_Byte * Regs, struct st_scanparams *scancfg, SANE_Int somevalue) { SANE_Int rst = ERROR; /* default */ DBG (DBG_FNC, "+ RTS_Setup_Motor(*Regs, *scancfg, somevalue=%i):\n", somevalue); dbg_ScanParams (scancfg); if ((Regs != NULL) && (scancfg != NULL)) { SANE_Int colormode, mymode; colormode = ((scancfg->colormode != CM_COLOR) && (scancfg->channel == 3)) ? 3 : scancfg->colormode; mymode = RTS_GetScanmode (dev, scantype, colormode, scancfg->resolution_x); if (mymode != -1) { SANE_Int mbs[2] = { 0 }; /* motor back steps */ SANE_Int step_size, step_type, dummyline, myvalue, lf02c; struct st_scanmode *sm; sm = dev->scanmodes[mymode]; /* set motor step type */ data_bitset (&Regs[0xd9], 0x70, sm->scanmotorsteptype); /*-xxx----*/ /* set motor direction (polarity) */ data_bitset (&Regs[0xd9], 0x80, somevalue >> 3); /*e------- */ /* next value doesn't seem to have any effect */ data_bitset (&Regs[0xd9], 0x0f, somevalue); /*----efgh*/ /* 0 enable/1 disable motor */ data_bitset (&Regs[0xdd], 0x80, somevalue >> 4); /*d------- */ /* next value doesn't seem to have any effect */ data_bitset (&Regs[0xdd], 0x40, somevalue >> 4); /*-d------*/ switch (sm->scanmotorsteptype) { case STT_OCT: step_type = 8; break; case STT_QUART: step_type = 4; break; case STT_HALF: step_type = 2; break; default: step_type = 1; break; /* STT_FULL */ } /* set dummy lines */ dummyline = sm->dummyline; if (dummyline == 0) dummyline++; data_bitset (&Regs[0xd6], 0xf0, dummyline); /*xxxx---- */ /* Set if motor has curves */ data_bitset (&Regs[0xdf], 0x10, ((sm->motorcurve != -1) ? 1 : 0)); /*---x----*/ /* set last step of deccurve.scanbufferfull table to 16 */ data_lsb_set (&Regs[0xea], 0x10, 3); /* set last step of deccurve.normalscan table to 16 */ data_lsb_set (&Regs[0xed], 0x10, 3); /* set last step of deccurve.smearing table to 16 */ data_lsb_set (&Regs[0xf0], 0x10, 3); /* set last step of deccurve.parkhome table to 16 */ data_lsb_set (&Regs[0xf3], 0x10, 3); /* set step size */ step_size = _B0 ((dev->motorcfg->resolution * step_type) / (dummyline * scancfg->resolution_y)); data_lsb_set (&Regs[0xe0], step_size - 1, 1); /* set line exposure time */ myvalue = data_lsb_get (&Regs[0x30], 3); myvalue += ((myvalue + 1) % step_size); data_lsb_set (&Regs[0x30], myvalue, 3); /* set last step of accurve.normalscan table */ myvalue = ((myvalue + 1) / step_size) - 1; data_lsb_set (&Regs[0xe1], myvalue, 3); /* 42b30eb */ lf02c = 0; if (sm->motorcurve != -1) { if (sm->motorcurve < dev->mtrsetting_count) { struct st_motorcurve *ms = dev->mtrsetting[sm->motorcurve]; ms->motorbackstep = sm->motorbackstep; } DBG (DBG_FNC, " -> Setting up step motor using motorcurve %i\n", sm->motorcurve); lf02c = Motor_Setup_Steps (dev, Regs, sm->motorcurve); /* set motor back steps */ mbs[1] = sm->motorbackstep; if (mbs[1] >= (smeardeccurvecount + smearacccurvecount)) mbs[0] = mbs[1] - (smeardeccurvecount + smearacccurvecount) + 2; else mbs[0] = 0; if (mbs[1] >= (deccurvecount + acccurvecount)) mbs[1] -= (deccurvecount + acccurvecount) + 2; else mbs[1] = 0; } else { /* this scanner hasn't got any motorcurve */ /* set last step of accurve.smearing table (same as accurve.normalscan) */ data_lsb_set (&Regs[0xe4], myvalue, 3); /* set last step of accurve.parkhome table (same as accurve.normalscan) */ data_lsb_set (&Regs[0xe7], myvalue, 3); /* both motorbacksteps are equal */ mbs[0] = sm->motorbackstep; mbs[1] = sm->motorbackstep; } /* show msi and motorbacksteps */ DBG (DBG_FNC, " -> msi = %i\n", sm->msi); DBG (DBG_FNC, " -> motorbackstep1 = %i\n", mbs[0]); DBG (DBG_FNC, " -> motorbackstep2 = %i\n", mbs[1]); /* set msi */ data_bitset (&Regs[0xda], 0xff, _B0 (sm->msi)); /*xxxxxxxx */ data_bitset (&Regs[0xdd], 0x03, _B1 (sm->msi)); /*------xx*/ /* set motorbackstep (a) */ data_bitset (&Regs[0xdb], 0xff, _B0 (mbs[0])); /*xxxxxxxx */ data_bitset (&Regs[0xdd], 0x0c, _B1 (mbs[0])); /*----xx--*/ /* set motorbackstep (b) */ data_bitset (&Regs[0xdc], 0xff, _B0 (mbs[1])); /*xxxxxxxx */ data_bitset (&Regs[0xdd], 0x30, _B1 (mbs[1])); /*--xx----*/ /* 328b */ /* get dummy lines count */ dummyline = data_bitget (&Regs[0xd6], 0xf0); myvalue = scancfg->coord.top * (dummyline * step_size); if (lf02c >= myvalue) scancfg->coord.top = 1; else scancfg->coord.top -= (lf02c / (dummyline * step_size)) - 1; rst = lf02c; /* Result from Motor_Setup_Steps */ } } DBG (DBG_FNC, "- RTS_Setup_Motor: %i\n", rst); return rst; } static void RTS_Setup_Exposure_Times (SANE_Byte * Regs, struct st_scanparams *scancfg, struct st_scanmode *sm) { DBG (DBG_FNC, "> RTS_Setup_Exposure_Times\n"); if ((sm != NULL) && (Regs != NULL) && (scancfg != NULL)) { SANE_Int myexpt[3], linexpt, a; /* calculate line exposure time */ linexpt = sm->ctpc + 1; if (RTS_Debug->usbtype == USB11) linexpt *= sm->multiexposureforfullspeed; if (scancfg->depth > 8) linexpt *= sm->multiexposurefor16bitmode; linexpt--; /* generate exposure times for each channel color */ for (a = CL_RED; a <= CL_BLUE; a++) { if ((linexpt > sm->mexpt[a]) && (sm->expt[a] == 0)) sm->expt[a] = sm->mexpt[a]; myexpt[a] = (sm->expt[a] == 0) ? sm->mexpt[a] : sm->expt[a]; } /* save exposure times */ DBG (DBG_FNC, "-> Exposure times : %04x, %04x, %04x\n", sm->expt[0], sm->expt[1], sm->expt[2]); data_lsb_set (&Regs[0x36], sm->expt[CL_RED], 3); data_lsb_set (&Regs[0x3c], sm->expt[CL_GREEN], 3); data_lsb_set (&Regs[0x42], sm->expt[CL_BLUE], 3); /* save maximum exposure times */ DBG (DBG_FNC, "-> Maximum exposure times: %04x, %04x, %04x\n", sm->mexpt[0], sm->mexpt[1], sm->mexpt[2]); data_lsb_set (&Regs[0x33], sm->mexpt[CL_RED], 3); data_lsb_set (&Regs[0x39], sm->mexpt[CL_GREEN], 3); data_lsb_set (&Regs[0x3f], sm->mexpt[CL_BLUE], 3); /* save line exposure time */ data_lsb_set (&Regs[0x30], linexpt, 3); /* scancfg->expt = lowest value */ scancfg->expt = min (min (myexpt[1], myexpt[2]), myexpt[0]); } } static SANE_Int RTS_Setup_Line_Distances (struct st_device *dev, SANE_Byte * Regs, struct st_scanparams *scancfg, struct st_hwdconfig *hwdcfg, SANE_Int mycolormode, SANE_Int arrangeline) { SANE_Int iLineDistance = 0; if (arrangeline == FIX_BY_HARD) { /* we don't need to arrange retrieved line */ SANE_Int mylinedistance, myevenodddist; mylinedistance = (dev->sensorcfg->line_distance * scancfg->resolution_y) / dev->sensorcfg->resolution; if (hwdcfg->highresolution == TRUE) myevenodddist = (hwdcfg->sensorevenodddistance * scancfg->resolution_y) / dev->sensorcfg->resolution; else myevenodddist = 0; data_bitset (&Regs[0x149], 0x3f, myevenodddist); data_bitset (&Regs[0x14a], 0x3f, mylinedistance); data_bitset (&Regs[0x14b], 0x3f, mylinedistance + myevenodddist); data_bitset (&Regs[0x14c], 0x3f, mylinedistance * 2); data_bitset (&Regs[0x14d], 0x3f, (mylinedistance * 2) + myevenodddist); } else { /* arrange retrieved line */ data_bitset (&Regs[0x149], 0x3f, 0); data_bitset (&Regs[0x14a], 0x3f, 0); data_bitset (&Regs[0x14b], 0x3f, 0); data_bitset (&Regs[0x14c], 0x3f, 0); data_bitset (&Regs[0x14d], 0x3f, 0); if (arrangeline == FIX_BY_SOFT) { if (hwdcfg->highresolution == FALSE) { if (mycolormode == CM_COLOR) { iLineDistance = (dev->sensorcfg->line_distance * scan2.resolution_y) * 2; iLineDistance = (iLineDistance / dev->sensorcfg->resolution) + 1; if (iLineDistance < 2) iLineDistance = 2; } } else { /* bcc */ if (mycolormode == CM_COLOR) iLineDistance = ((dev->sensorcfg->line_distance * 2) + hwdcfg->sensorevenodddistance) * scan2.resolution_y; else iLineDistance = dev->sensorcfg->line_distance * scan2.resolution_y; iLineDistance = (iLineDistance / dev->sensorcfg->resolution) + 1; if (iLineDistance < 2) iLineDistance = 2; } /* c25 */ iLineDistance &= 0xffff; v15b4 = (iLineDistance > 0) ? 1 : 0; imagesize += iLineDistance * bytesperline; } } DBG (DBG_FNC, "> RTS_Setup_Line_Distances(*Regs, *scancfg, *hwdcfg, mycolormode=%i, arrangeline=%i): %i\n", mycolormode, arrangeline, iLineDistance); return iLineDistance; } static SANE_Int RTS_Setup_Depth (SANE_Byte * Regs, struct st_scanparams *scancfg, SANE_Int mycolormode) { /* channels_per_line = channels_per_dot * scan.width bytes_per_line = channels_per_line * bits_per_channel */ SANE_Int bytes_per_line = 0; if ((scancfg != NULL) && (Regs != NULL)) { SANE_Int channels_per_line = data_bitget (&Regs[0x12], 0xc0) * scancfg->coord.width; bytes_per_line = channels_per_line; /* set bits per channel in shading correction's register (0x1cf) */ if (mycolormode == CM_LINEART) { /* lineart mode */ bytes_per_line = (bytes_per_line + 7) / 8; data_bitset (&Regs[0x1cf], 0x30, 3); /*--11----*/ } else { /*f0c */ switch (scancfg->depth) { case 16: /* 16 bits per channel */ bytes_per_line *= 2; data_bitset (&Regs[0x1cf], 0x30, 2); /*--10----*/ break; case 12: /* 12 bits per channel */ bytes_per_line *= 2; data_bitset (&Regs[0x1cf], 0x30, 1); /*--01----*/ break; default: /* 8 bits per channel */ data_bitset (&Regs[0x1cf], 0x30, 0); /*--00----*/ break; } } } return bytes_per_line; } static void RTS_Setup_Shading (SANE_Byte * Regs, struct st_scanparams *scancfg, struct st_hwdconfig *hwdcfg, SANE_Int bytes_per_line) { DBG (DBG_FNC, "> RTS_Setup_Shading(*Regs, *scancfg, *hwdcfg, bytes_per_line=%i)\n", bytes_per_line); if ((Regs != NULL) && (hwdcfg != NULL)) { SANE_Int dots_count, myvalue, myvalue2, mem_available, resolution_ratio, sensor_line_distance; SANE_Int channels, table_size; resolution_ratio = Regs[0x0c0] & 0x1f; /* 50de */ data_bitset (&Regs[0x1bf], 0x18, hwdcfg->unk3); /*---xx---*/ /* Enable black shading correction ? */ data_bitset (&Regs[0x1cf], 0x08, hwdcfg->black_shading); /*----x---*/ /* Enable white shading correction ? */ data_bitset (&Regs[0x1cf], 0x04, hwdcfg->white_shading); /*-----x--*/ if ((hwdcfg->white_shading != FALSE) && (hwdcfg->black_shading != FALSE) && (hwdcfg->unk3 != 0)) data_bitset (&Regs[0x1cf], 0x04, 0); /*-----x--*/ table_size = 0; /* if hwdcfg->black_shading */ if ((Regs[0x1cf] & 8) != 0) table_size = (resolution_ratio * scancfg->coord.width) * 2; /* black shading buffer size? */ /* if hwdcfg->white_shading */ if ((Regs[0x1cf] & 4) != 0) table_size += (resolution_ratio * scancfg->coord.width) * 2; /* white shading buffer size? */ /* Regs 0x1ba, 0x1bb, 0x1bd, 0x1c0 seem to be 4 pointers to some buffer related to shading correction */ Regs[0x1ba] = 0x00; table_size = (table_size + v160c_block_size - 1) / v160c_block_size; table_size = ((table_size + 15) / 16) + 16; Regs[0x1bf] &= 0xfe; Regs[0x1bb] = _B0 (table_size); Regs[0x1bc] = _B1 (table_size); Regs[0x1bf] |= _B2 (table_size) & 1; /*-------x*/ Regs[0x1bf] &= 0xf9; Regs[0x1bd] = _B0 (table_size * 2); Regs[0x1be] = _B1 (table_size * 2); Regs[0x1bf] |= (_B2 (table_size * 2) & 3) << 1; /*-----xx-*/ data_wide_bitset (&Regs[0x1c0], 0xfffff, table_size * 3); mem_available = mem_total - ((table_size * 3) * 16); sensor_line_distance = Regs[0x14a] & 0x3f; /* select case channels_per_dot */ channels = data_lsb_get (&Regs[0x12], 1) >> 6; switch (channels) { case 3: /* 3 channels per dot */ /* 528d */ dots_count = bytes_per_line / 3; /* 882 */ myvalue = (((sensor_line_distance + 1) * dots_count) + v160c_block_size - 1) / v160c_block_size; myvalue2 = myvalue; mem_available = (mem_available - (myvalue * 3) + 2) / 3; myvalue += (table_size * 3) * 8; myvalue = ((myvalue * 2) + mem_available); data_bitset (&Regs[0x1c2], 0xf0, _B2 ((myvalue / 16) + 1)); /* 4 higher bits xxxx---- */ data_wide_bitset (&Regs[0x1c3], 0xffff, (myvalue / 16) + 1); /* 16 lower bits */ myvalue = myvalue + myvalue2 + mem_available; data_wide_bitset (&Regs[0x1c5], 0xfffff, (myvalue / 16) + 1); break; case 2: /* 2 channels per dot */ dots_count = bytes_per_line / 2; myvalue = (((sensor_line_distance + 1) * dots_count) + v160c_block_size - 1) / v160c_block_size; mem_available = ((mem_available - myvalue) + 1) / 2; myvalue += (((table_size * 3) + mem_available) / 16) + 1; data_bitset (&Regs[0x1c2], 0xf0, _B2 (myvalue)); /* 4 higher bits xxxx---- */ data_wide_bitset (&Regs[0x1c3], 0xffff, myvalue); /* 16 lower bits */ break; default: dots_count = bytes_per_line; break; } Regs[0x01c7] &= 0x0f; Regs[0x01c8] = _B0 ((mem_total - 1) / 16); Regs[0x01c9] = _B1 ((mem_total - 1) / 16); Regs[0x01c7] |= (_B2 ((mem_total - 1) / 16) & 0x0f) << 4; mem_available -= (dots_count + v160c_block_size - 1) / v160c_block_size; mem_available /= 16; Regs[0x0712] &= 0x0f; Regs[0x0710] = _B0 (mem_available); Regs[0x0711] = _B1 (mem_available); Regs[0x0712] |= _B0 (_B2 (mem_available) << 4); /*xxxx---- */ Regs[0x0713] = 0x00; Regs[0x0714] = 0x10; Regs[0x0715] &= 0xf0; } } static void RTS_Setup_Arrangeline (struct st_device *dev, struct st_hwdconfig *hwdcfg, SANE_Int colormode) { dev->scanning->arrange_compression = (colormode == CM_LINEART) ? FALSE : hwdcfg->compression; if ((colormode == CM_LINEART) || ((colormode == CM_GRAY) && (hwdcfg->highresolution == FALSE))) arrangeline2 = 0; else arrangeline2 = hwdcfg->arrangeline; dev->scanning->arrange_hres = hwdcfg->highresolution; dev->scanning->arrange_sensor_evenodd_dist = (hwdcfg->highresolution == FALSE) ? 0 : hwdcfg->sensorevenodddistance; } static void RTS_Setup_Channels (struct st_device *dev, SANE_Byte * Regs, struct st_scanparams *scancfg, SANE_Int mycolormode) { DBG (DBG_FNC, "> RTS_Setup_Channels(colormode=%i)\n", mycolormode); if ((scancfg != NULL) && (Regs != NULL)) { if ((mycolormode != CM_COLOR) && (mycolormode != 3)) { /* CM_GRAY || CM_LINEART */ if (scancfg->samplerate == LINE_RATE) { /* Setting channels_per_dot to 1 */ data_bitset (&Regs[0x12], 0xc0, 1); /*01------ */ /* setting one rgb_channel_order */ data_bitset (&Regs[0x12], 0x03, dev->sensorcfg->rgb_order[scancfg->channel]); /*------xx*/ /* set sensor_channel_color_order */ data_bitset (&Regs[0x60a], 0x3f, 6); /*--xxxxxx*/ /* set samplerate */ data_bitset (&Regs[0x1cf], 0x40, PIXEL_RATE); /*-x------*/ /* set unknown data */ data_bitset (&Regs[0x1cf], 0x80, 1); /*x------- */ if (scancfg->channel == dev->sensorcfg->rgb_order[1]) { /* mexpts[CL_RED] = mexpts[CL_GREEN] */ data_lsb_set (&Regs[0x33], data_lsb_get (&Regs[0x39], 3), 3); /* expts[CL_RED] = expts[CL_GREEN] */ data_lsb_set (&Regs[0x36], data_lsb_get (&Regs[0x3c], 3), 3); } else if (scancfg->channel == dev->sensorcfg->rgb_order[2]) { /* mexpts[CL_RED] = mexpts[CL_BLUE] */ data_lsb_set (&Regs[0x33], data_lsb_get (&Regs[0x3f], 3), 3); /* expts[CL_RED] = expts[CL_BLUE] */ data_lsb_set (&Regs[0x36], data_lsb_get (&Regs[0x42], 3), 3); } } else { /* e01 */ /* setting channels_per_dot to 2 */ data_bitset (&Regs[0x12], 0xc0, 2); /* set two channel color order */ data_bitset (&Regs[0x12], 0x03, dev->sensorcfg->channel_gray[0]); /*------xx*/ data_bitset (&Regs[0x12], 0x0c, dev->sensorcfg->channel_gray[1]); /*----xx--*/ /* set samplerate */ data_bitset (&Regs[0x1cf], 0x40, LINE_RATE); /* set unknown data */ data_bitset (&Regs[0x1cf], 0x80, 1); } } else { /* CM_COLOR || 3 */ /* e42 */ /* setting channels_per_dot to 3 */ data_bitset (&Regs[0x12], 0xc0, 3); /* setting samplerate */ data_bitset (&Regs[0x1cf], 0x40, scancfg->samplerate); /* set unknown data */ data_bitset (&Regs[0x1cf], 0x80, 0); /* set sensor chanel_color_order */ data_bitset (&Regs[0x60a], 0x03, dev->sensorcfg->channel_color[2]); /*------xx*/ data_bitset (&Regs[0x60a], 0x0c, dev->sensorcfg->channel_color[1]); /*----xx--*/ data_bitset (&Regs[0x60a], 0x30, dev->sensorcfg->channel_color[0]); /*--xx----*/ /* set rgb_channel_order */ data_bitset (&Regs[0x12], 0x03, dev->sensorcfg->rgb_order[0]); /*------xx*/ data_bitset (&Regs[0x12], 0x0c, dev->sensorcfg->rgb_order[1]); /*----xx--*/ data_bitset (&Regs[0x12], 0x30, dev->sensorcfg->rgb_order[2]); /*--xx----*/ } } } static SANE_Int RTS_Setup (struct st_device *dev, SANE_Byte * Regs, struct st_scanparams *scancfg, struct st_hwdconfig *hwdcfg, struct st_gain_offset *gain_offset) { SANE_Int rst = ERROR; /* default */ SANE_Int lSMode; SANE_Byte mycolormode; DBG (DBG_FNC, "+ RTS_Setup:\n"); dbg_ScanParams (scancfg); dbg_hwdcfg (hwdcfg); mycolormode = scancfg->colormode; if (scancfg->colormode != CM_COLOR) { if (scancfg->colormode == CM_LINEART) scancfg->depth = 8; if (scancfg->channel == 3) { if (scancfg->colormode == CM_GRAY) mycolormode = (hwdcfg->arrangeline != FIX_BY_SOFT) ? 3 : CM_COLOR; else mycolormode = 3; } } /* 42b47d6 */ memcpy (&scan2, scancfg, sizeof (struct st_scanparams)); scantype = hwdcfg->scantype; lSMode = RTS_GetScanmode (dev, scantype, mycolormode, scancfg->resolution_x); if (lSMode >= 0) { struct st_scanmode *sm = dev->scanmodes[lSMode]; if (sm != NULL) { SANE_Int dummyline, iLineDistance, resolution_ratio, bytes_per_line; struct st_coords rts_coords; iLineDistance = 0; scancfg->timing = sm->timing; scancfg->sensorresolution = dev->timings[scancfg->timing]->sensorresolution; scancfg->shadinglength = (((scancfg->sensorresolution * 17) / 2) + 3) & 0xfffffffc; scancfg->samplerate = sm->samplerate; hwdcfg->motorplus = sm->motorplus; /* set systemclock */ data_bitset (&Regs[0x00], 0x0f, sm->systemclock); /* setting exposure times */ RTS_Setup_Exposure_Times (Regs, scancfg, sm); /* setting arranges */ RTS_Setup_Arrangeline (dev, hwdcfg, mycolormode); /* set up line distances */ iLineDistance = RTS_Setup_Line_Distances (dev, Regs, scancfg, hwdcfg, mycolormode, arrangeline); /* 4c67 */ /* setup channel colors */ RTS_Setup_Channels (dev, Regs, scancfg, mycolormode); /* setup depth */ bytes_per_line = RTS_Setup_Depth (Regs, scancfg, mycolormode); /* f61 */ /* Set resolution ratio */ resolution_ratio = (scancfg->sensorresolution / scancfg->resolution_x) & 0x1f; data_bitset (&Regs[0xc0], 0x1f, resolution_ratio); /* set sensor timing values */ RTS_Setup_SensorTiming (dev, scancfg->timing, Regs); data_bitset (&Regs[0xd8], 0x40, ((scantype == ST_NORMAL) ? 0 : 1)); /*-x------*/ /* Use static head ? */ data_bitset (&Regs[0xd8], 0x80, ((hwdcfg->static_head == FALSE) ? 1 : 0)); /*x------- */ /* Setting up gamma */ RTS_Setup_Gamma (Regs, hwdcfg); /* setup shading correction */ RTS_Setup_Shading (Regs, scancfg, hwdcfg, bytes_per_line); /* setup stepper motor */ hwdcfg->startpos = RTS_Setup_Motor (dev, Regs, scancfg, hwdcfg->motor_direction | MTR_ENABLED); /* set coordinates */ dummyline = data_bitget (&Regs[0xd6], 0xf0); if (scancfg->coord.left == 0) scancfg->coord.left++; if (scancfg->coord.top == 0) scancfg->coord.top++; rts_coords.left = scancfg->coord.left * resolution_ratio; rts_coords.width = scancfg->coord.width * resolution_ratio; rts_coords.top = scancfg->coord.top * dummyline; rts_coords.height = ((Regs[0x14d] & 0x3f) + scancfg->coord.height + iLineDistance) * dummyline; if ((rts_coords.left & 1) == 0) rts_coords.left++; RTS_Setup_Coords (Regs, rts_coords.left, rts_coords.top, rts_coords.width, rts_coords.height); data_bitset (&Regs[0x01], 0x06, 0); /*-----xx-*/ /* dummy_scan? */ data_bitset (&Regs[0x01], 0x10, hwdcfg->dummy_scan); /*---x----*/ data_bitset (&Regs[0x163], 0xc0, 1); /*xx------ */ if (dev->scanning->arrange_compression != FALSE) { Regs[0x60b] &= 0x8f; data_bitset (&Regs[0x60b], 0x10, 1); /*-001----*/ } else data_bitset (&Regs[0x60b], 0x7f, 0); /*-0000000*/ if (mycolormode == 3) { SANE_Int channels_per_line; /* Set channels_per_line = channels_per_dot * scan_width */ channels_per_line = data_bitget (&Regs[0x12], 0xc0) * scancfg->coord.width; data_wide_bitset (&Regs[0x060c], 0x3ffff, channels_per_line); /* Sets 16 bits per channel */ data_bitset (&Regs[0x1cf], 0x30, 2); /*--10----*/ Regs[0x60b] |= 0x40; if (v1619 == 0x21) { dev->scanning->arrange_compression = FALSE; data_bitset (&Regs[0x60b], 0x10, 0); /*---0----*/ } switch (scancfg->depth) { case 8: case 16: Regs[0x060b] &= 0xf3; break; case 12: Regs[0x060b] = (Regs[0x060b] & 0xfb) | 0x08; break; } if (scancfg->colormode == CM_LINEART) data_bitset (&Regs[0x60b], 0x0c, 0); /* disable gamma correction ¿? */ data_bitset (&Regs[0x1d0], 0x40, 0); } /* 5683 */ /* Set calibration table */ RTS_Setup_GainOffset (Regs, gain_offset); rst = OK; } } DBG (DBG_FNC, "- RTS_Setup: %i\n", rst); return rst; } static void RTS_Setup_Coords (SANE_Byte * Regs, SANE_Int iLeft, SANE_Int iTop, SANE_Int width, SANE_Int height) { DBG (DBG_FNC, "> RTS_Setup_Coords(*Regs, iLeft=%i, iTop=%i, width=%i, height=%i)\n", iLeft, iTop, width, height); if (Regs != NULL) { /* Set Left coord */ data_lsb_set (&Regs[0xb0], iLeft, 2); /* Set Right coord */ data_lsb_set (&Regs[0xb2], iLeft + width, 2); /* Set Top coord */ data_lsb_set (&Regs[0xd0], iTop, 2); data_bitset (&Regs[0xd4], 0x0f, _B2 (iTop)); /* Set Down coord */ data_lsb_set (&Regs[0xd2], iTop + height, 2); data_bitset (&Regs[0xd4], 0xf0, _B2 (iTop + height)); } } static void RTS_Setup_GainOffset (SANE_Byte * Regs, struct st_gain_offset *gain_offset) { SANE_Byte fake[] = { 0x19, 0x15, 0x19, 0x64, 0x64, 0x64, 0x74, 0xc0, 0x74, 0xc0, 0x6d, 0xc0, 0x6d, 0xc0, 0x5f, 0xc0, 0x5f, 0xc0 }; DBG (DBG_FNC, "> RTS_Setup_GainOffset(*Regs, *gain_offset)\n"); dbg_calibtable (gain_offset); if ((Regs != NULL) && (gain_offset != NULL)) { if (RTS_Debug->calibrate == FALSE) { data_bitset (&Regs[0x13], 0x03, gain_offset->pag[CL_RED]); /*------xx*/ data_bitset (&Regs[0x13], 0x0c, gain_offset->pag[CL_GREEN]); /*----xx--*/ data_bitset (&Regs[0x13], 0x30, gain_offset->pag[CL_BLUE]); /*--xx----*/ memcpy (&Regs[0x14], &fake, 18); } else { SANE_Int a; for (a = CL_RED; a <= CL_BLUE; a++) { /* Offsets */ Regs[0x1a + (a * 4)] = _B0 (gain_offset->edcg1[a]); Regs[0x1b + (a * 4)] = ((gain_offset->edcg1[a] >> 1) & 0x80) | (gain_offset-> edcg2[a] & 0x7f); Regs[0x1c + (a * 4)] = _B0 (gain_offset->odcg1[a]); Regs[0x1d + (a * 4)] = ((gain_offset->odcg1[a] >> 1) & 0x80) | (gain_offset-> odcg2[a] & 0x7f); /* Variable Gain Amplifier */ data_bitset (&Regs[0x14 + a], 0x1f, gain_offset->vgag1[a]); data_bitset (&Regs[0x17 + a], 0x1f, gain_offset->vgag2[a]); } data_bitset (&Regs[0x13], 0x03, gain_offset->pag[CL_RED]); /*------xx*/ data_bitset (&Regs[0x13], 0x0c, gain_offset->pag[CL_GREEN]); /*----xx--*/ data_bitset (&Regs[0x13], 0x30, gain_offset->pag[CL_BLUE]); /*--xx----*/ } } } static void Calibrate_Free (struct st_cal2 *calbuffers) { DBG (DBG_FNC, "> Calibrate_Free(*calbuffers)\n"); if (calbuffers != NULL) { SANE_Int c; if (calbuffers->table2 != NULL) { free (calbuffers->table2); calbuffers->table2 = NULL; } for (c = 0; c < 4; c++) { if (calbuffers->tables[c] != NULL) { free (calbuffers->tables[c]); calbuffers->tables[c] = NULL; } } calbuffers->shadinglength1 = 0; calbuffers->tables_size = 0; calbuffers->shadinglength3 = 0; } } static SANE_Int Calibrate_Malloc (struct st_cal2 *calbuffers, SANE_Byte * Regs, struct st_calibration *myCalib, SANE_Int somelength) { SANE_Int myshadinglength, pos; SANE_Int rst; if ((calbuffers != NULL) && (Regs != NULL) && (myCalib != NULL)) { if ((Regs[0x1bf] & 0x18) == 0) { if ((((Regs[0x1cf] >> 1) & Regs[0x1cf]) & 0x04) != 0) calbuffers->table_count = 2; else calbuffers->table_count = 4; } else calbuffers->table_count = 4; /*365d */ myshadinglength = myCalib->shadinglength * 2; calbuffers->shadinglength1 = min (myshadinglength, somelength); if ((myshadinglength % somelength) != 0) calbuffers->tables_size = (myshadinglength >= somelength) ? somelength * 2 : somelength; else calbuffers->tables_size = somelength; if (myshadinglength >= somelength) { calbuffers->shadinglength1 = (myshadinglength % calbuffers->shadinglength1) + calbuffers->shadinglength1; calbuffers->shadinglength3 = ((myCalib->shadinglength * 2) / somelength) - 1; } else calbuffers->shadinglength3 = 0; calbuffers->shadinglength3 = (somelength / 16) * calbuffers->shadinglength3; rst = OK; for (pos = 0; pos < calbuffers->table_count; pos++) { calbuffers->tables[pos] = (USHORT *) malloc (calbuffers->tables_size * sizeof (USHORT)); if (calbuffers->tables[pos] == NULL) { rst = ERROR; break; } } if (rst == OK) { calbuffers->table2 = (USHORT *) malloc (calbuffers->tables_size * sizeof (USHORT)); if (calbuffers->table2 == NULL) rst = ERROR; } if (rst != OK) Calibrate_Free (calbuffers); } else rst = ERROR; DBG (DBG_FNC, "> Calibrate_Malloc(*calbuffers, *Regs, *myCalib, somelength=%i): %i\n", somelength, rst); return rst; } static SANE_Int fn3560 (USHORT * table, struct st_cal2 *calbuffers, SANE_Int * tablepos) { /*05FEF974 001F99B0 |table = 001F99B0 05FEF978 05FEFA08 |calbuffers->tables[0] = 05FEFA08 05FEF97C 000000A0 |calbuffers->shadinglength3 = 000000A0 05FEF980 00000348 |calbuffers->shadinglength1 = 00000348 05FEF984 04F01502 |calbuffers->table_count = 04F01502 05FEF988 05FEF998 \Arg6 = 05FEF998 */ if (table != NULL) { SANE_Int pos[4] = { 0, 0, 0, 0 }; /*f960 f964 f968 f96c */ SANE_Int usetable = 0; SANE_Int a; SANE_Int mylength3 = calbuffers->shadinglength1; /*f97c */ SANE_Byte *pPointer = (SANE_Byte *) (table + (calbuffers->shadinglength3 * 16)); DBG (DBG_FNC, "> fn3560(*table, *calbuffers, *tablepos)\n"); if (mylength3 > 0) { do { if (calbuffers->tables[usetable] != NULL) { if (mylength3 <= 16) { if (mylength3 > 0) { do { *(calbuffers->tables[usetable] + pos[usetable]) = _B0 (*pPointer); pPointer++; pos[usetable]++; mylength3--; } while (mylength3 > 0); } break; } for (a = 0; a < 16; a++) { *(calbuffers->tables[usetable] + pos[usetable]) = _B0 (*pPointer); pPointer++; pos[usetable]++; } } mylength3 -= 16; usetable++; if (usetable == calbuffers->table_count) usetable = 0; } while (mylength3 > 0); } /*35f8 */ if (calbuffers->table_count > 0) { /* Return position of each table */ memcpy (tablepos, pos, sizeof (SANE_Int) * 4); } } return OK; } static SANE_Int Calib_WriteTable (struct st_device *dev, SANE_Byte * table, SANE_Int size, SANE_Int data) { SANE_Int rst = ERROR; DBG (DBG_FNC, "+ Calib_WriteTable(*table, size=%i):\n", size); if ((table != NULL) && (size > 0)) { SANE_Int transferred; if (RTS_DMA_Reset (dev) == OK) { /* Send size to write */ if (RTS_DMA_Enable_Write (dev, 0x0004, size, data) == OK) /* Send data */ rst = Bulk_Operation (dev, BLK_WRITE, size, table, &transferred); } } DBG (DBG_FNC, "- Calib_WriteTable: %i\n", rst); return rst; } static SANE_Int Calib_ReadTable (struct st_device *dev, SANE_Byte * table, SANE_Int size, SANE_Int data) { SANE_Int rst = ERROR; DBG (DBG_FNC, "+ Calib_ReadTable(*table, size=%i):\n", size); if ((table != NULL) && (size > 0)) { SANE_Int transferred; if (RTS_DMA_Reset (dev) == OK) { /* Send size to read */ if (RTS_DMA_Enable_Read (dev, 0x0004, size, data) == OK) /* Retrieve data */ rst = Bulk_Operation (dev, BLK_READ, size, table, &transferred); } } DBG (DBG_FNC, "- Calib_ReadTable: %i\n", rst); return rst; } static SANE_Int fn3330 (struct st_device *dev, SANE_Byte * Regs, struct st_cal2 *calbuffers, SANE_Int sensorchannelcolor, SANE_Int * tablepos, SANE_Int data) { /*05EEF968 04F0F7F8 |Regs = 04F0F7F8 05EEF96C 02DEC838 |calbuffers->table2 = 02DEC838 05EEF970 05EEFA08 |calbuffers->tables[] = 05EEFA08 05EEF974 00000000 |sensorchannelcolor = 00000000 05EEF978 000000A0 |calbuffers->shadinglength3 = 000000A0 05EEF97C 00000400 |calbuffers->tables_size = 00000400 05EEF980 05EEF998 |&pos = 05EEF998 05EEF984 00221502 |calbuffers->table_count = 00221502 05EEF988 00000000 \data = 00000000 */ SANE_Int table_count = calbuffers->table_count; /*f960 */ SANE_Int schcolor = _B0 (sensorchannelcolor); SANE_Int a = 0; SANE_Int tablelength = calbuffers->shadinglength3 / table_count; /*f954 */ SANE_Int val_color = 0; /*f974 */ SANE_Int val_lineart = 0; /*f978 */ SANE_Int val_gray = 0; /*ebx */ SANE_Int value4 = 0; /*ebp */ SANE_Int size; SANE_Int rst = OK; DBG (DBG_FNC, "+ fn3330(*Regs, *calbuffers, sensorchannelcolor=%i, *tablepos, data=%i):\n", sensorchannelcolor, data); if (calbuffers->table_count > 0) { do { if (calbuffers->table_count == 2) { /*338c */ if (a != 0) { /*3394 */ if (_B0 (data) == 0) { val_color = 0x100000; val_lineart = 0x100000; val_gray = 0x200000; } else { /*343a */ val_color = 0x300000; val_lineart = 0x300000; val_gray = 0; } } else { /*33be */ if (_B0 (data) == 0) { val_color = 0; val_lineart = 0; val_gray = 0x300000; } else { /*342a */ val_color = 0x200000; val_lineart = 0x200000; val_gray = 0x100000; } } } else { /*33d5 */ switch (a) { case 0: val_color = 0; val_lineart = 0; val_gray = 0x300000; break; case 1: val_color = 0x200000; val_lineart = 0x200000; val_gray = 0x100000; break; case 2: val_color = 0x100000; val_lineart = 0x100000; val_gray = 0x200000; break; case 3: val_color = 0x300000; val_lineart = 0x300000; val_gray = 0; break; } } /*3449 */ switch (schcolor) { case CM_LINEART: size = (((Regs[0x1bf] >> 1) & 3) << 0x10) | (Regs[0x1be] << 0x08) | Regs[0x1bd]; value4 = (tablelength + size) | val_lineart; break; case CM_GRAY: size = ((Regs[0x1bf] & 1) << 0x10) | (Regs[0x1bc] << 0x08) | Regs[0x1bb]; value4 = (tablelength + size) | val_gray; break; default: size = _B0 (Regs[0x1ba]); value4 = (tablelength + size) | val_color; break; } if (Calib_ReadTable (dev, (SANE_Byte *) calbuffers->table2, calbuffers->tables_size, value4) != OK) { rst = ERROR; break; } memcpy (calbuffers->tables[a], calbuffers->table2, tablepos[a]); if (tablepos[a + 1] == 0) break; a++; } while (a < calbuffers->table_count); } DBG (DBG_FNC, "- fn3330: %i\n", rst); return rst; } static SANE_Int fn3730 (struct st_device *dev, struct st_cal2 *calbuffers, SANE_Byte * Regs, USHORT * table, SANE_Int sensorchannelcolor, SANE_Int data) { /*05FEF9AC |calbuffers = 05FEF9F8 05FEF9B0 |Regs = 04EFF7F8 05FEF9B4 |table = 001F99B0 05FEF9B8 |sensorchannelcolor = 00000000 05FEF9BC |data = 00000000 */ SANE_Int pos[4] = { 0, 0, 0, 0 }; /*f998 f99c f9a0 f9a4 */ SANE_Int rst; DBG (DBG_FNC, "+ fn3730(*calbuffers, *Regs, *table, sensorchannelcolor=%i, data=%i):\n", sensorchannelcolor, data); fn3560 (table, calbuffers, pos); rst = fn3330 (dev, Regs, calbuffers, sensorchannelcolor, pos, data); DBG (DBG_FNC, "- fn3730: %i\n", rst); return rst; } static SANE_Int Shading_white_apply (struct st_device *dev, SANE_Byte * Regs, SANE_Int channels, struct st_calibration *myCalib, struct st_cal2 *calbuffers) { SANE_Int rst = OK; DBG (DBG_FNC, "+ Shading_white_apply(channels=%i)\n", channels); /*3e7f */ Calibrate_Malloc (calbuffers, Regs, myCalib, (RTS_Debug->usbtype == USB20) ? 0x200 : 0x40); if (channels > 0) { /*int a; */ SANE_Int chnl; SANE_Int pos; /*fa2c */ SANE_Int transferred; rst = ERROR; for (chnl = 0; chnl < channels; chnl++) { /*for (a = 0; a < myCalib->shadinglength; a++) myCalib->black_shading[chnl][a] = 0x2000; */ /* 11 tries */ for (pos = 0; pos <= 10; pos++) { /* Send size to write */ if (RTS_DMA_Enable_Write (dev, dev->sensorcfg->channel_color[chnl] | 0x14, myCalib->shadinglength, 0) == OK) /* Send data */ Bulk_Operation (dev, BLK_WRITE, myCalib->shadinglength * sizeof (USHORT), (SANE_Byte *) & myCalib-> white_shading[chnl][myCalib->first_position - 1], &transferred); /*3df7 */ if (fn3730 (dev, calbuffers, Regs, &myCalib->white_shading[chnl][myCalib->first_position - 1], dev->sensorcfg->channel_color[chnl], 1) == OK) { rst = OK; break; } RTS_DMA_Cancel (dev); } } } Calibrate_Free (calbuffers); DBG (DBG_FNC, "- Shading_white_apply: %i\n", rst); return OK; } static SANE_Int Shading_black_apply (struct st_device *dev, SANE_Byte * Regs, SANE_Int channels, struct st_calibration *myCalib, struct st_cal2 *calbuffers) { SANE_Int rst = OK; DBG (DBG_FNC, "+ Shading_black_apply(channels=%i)\n", channels); /* 3d79 */ Calibrate_Malloc (calbuffers, Regs, myCalib, (RTS_Debug->usbtype == USB20) ? 0x200 : 0x40); if (channels > 0) { /*int a; */ SANE_Int chnl; SANE_Int pos; /*fa2c */ SANE_Int transferred; rst = ERROR; for (chnl = 0; chnl < channels; chnl++) { /* 11 tries */ /*for (a = 0; a < myCalib->shadinglength; a++) myCalib->black_shading[chnl][a] = 0x2000; */ for (pos = 0; pos <= 10; pos++) { /* Send size to write */ if (RTS_DMA_Enable_Write (dev, dev->sensorcfg->channel_color[chnl] | 0x10, myCalib->shadinglength, 0) == OK) /* Send data */ Bulk_Operation (dev, BLK_WRITE, myCalib->shadinglength * sizeof (USHORT), (SANE_Byte *) & myCalib-> black_shading[chnl][myCalib->first_position - 1], &transferred); /*3df7 */ if (fn3730 (dev, calbuffers, Regs, &myCalib->black_shading[chnl][myCalib->first_position - 1], dev->sensorcfg->channel_color[chnl], 0) == OK) { rst = OK; break; } RTS_DMA_Cancel (dev); } } } /*3e62 */ Calibrate_Free (calbuffers); DBG (DBG_FNC, "- Shading_black_apply: %i\n", rst); return OK; } static SANE_Int Shading_apply (struct st_device *dev, SANE_Byte * Regs, struct st_scanparams *myvar, struct st_calibration *myCalib) { /* Regs f1bc myvar f020 hwdcfg e838 arg4 e81c myCalib e820 */ SANE_Int rst; /* lf9e0 */ SANE_Int myfact; /* e820 */ SANE_Int shadata; SANE_Byte channels; /* f9d4 */ SANE_Int myShadingBase; /* e818 */ char lf9d1; char lf9d0; DBG (DBG_FNC, "+ Shading_apply(*Regs, *myvar, *mygamma, *myCalib):\n"); dbg_ScanParams (myvar); lf9d0 = (Regs[0x60b] >> 6) & 1; lf9d1 = (Regs[0x60b] >> 4) & 1; Regs[0x060b] &= 0xaf; rst = Write_Byte (dev->usb_handle, 0xee0b, Regs[0x060b]); if (rst == OK) { SANE_Byte colormode = myvar->colormode; /*fa24 */ SANE_Int le7cc, le7d8; struct st_cal2 calbuffers; /* f9f8 */ if (colormode != CM_COLOR) { if (myvar->channel != 3) { if (colormode != 3) channels = (myvar->samplerate == PIXEL_RATE) ? 2 : 1; else channels = 3; } else { colormode = 3; channels = 3; } } else channels = 3; /* White shading formula : 2000H x Target / (Wn-Dn) = White Gain data ----- for 8 times system White shading formula : 4000H x Target / (Wn-Dn) = White Gain data ----- for 4 times system For example : Target = 3FFFH Wn = 2FFFH Dn = 0040H and 8 times system operation then White Gain = 2000H x 3FFFH / (2FFFH-0040H) = 2AE4H (1.34033 times) */ /* 3aad */ if (colormode == 3) { /* SANE_Int pos; SANE_Int colour; myShadingBase = shadingbase; for (colour = 0; colour < channels; colour++) { if (myCalib->white_shading[colour] != NULL) { myfact = shadingfact[colour]; if (myCalib->shadinglength > 0) { for (pos = myCalib->first_position - 1; pos < myCalib->shadinglength; pos++) myCalib->white_shading[colour][pos] = (myCalib->white_shading[colour][pos] * myfact) / myShadingBase; } } else break; } */ } /* 3b3b */ if (myCalib->shading_enabled != FALSE) { /* 3b46 */ SANE_Int colour, pos; le7cc = shadingbase; le7d8 = shadingbase; DBG (DBG_FNC, "-> Shading type: %i\n", myCalib->shading_type); for (colour = 0; colour < channels; colour++) { if (colormode == 3) le7cc = shadingfact[colour]; myShadingBase = ((Regs[0x1cf] & 2) != 0) ? 0x2000 : 0x4000; myfact = myCalib->WRef[colour] * myShadingBase; if (myCalib->shading_type == 2) { /*3bd8 */ if ((myCalib->black_shading[colour] != NULL) && (myCalib->white_shading[colour] != NULL)) { for (pos = myCalib->first_position - 1; pos < myCalib->shadinglength; pos++) { if (myCalib->white_shading[colour][pos] == 0) shadata = myShadingBase; else shadata = myfact / myCalib->white_shading[colour][pos]; shadata = min ((shadata * le7cc) / le7d8, 0xff00); myCalib->black_shading[colour][pos] &= 0xff; myCalib->black_shading[colour][pos] |= shadata & 0xff00; } } else break; } else { /*3c63 */ if (myCalib->shading_type == 3) { /*3c68 */ if (myCalib->black_shading[colour] != NULL) { for (pos = myCalib->first_position - 1; pos < myCalib->shadinglength; pos++) { if (myCalib->black_shading[colour][pos] == 0) shadata = myShadingBase; else shadata = myfact / myCalib->black_shading[colour][pos]; shadata = min ((shadata * le7cc) / le7d8, 0xffc0); myCalib->black_shading[colour][pos] &= 0x3f; myCalib->black_shading[colour][pos] |= shadata & 0xffc0; } } else break; } else { /*3ce3 */ if (myCalib->white_shading[colour] != NULL) { for (pos = 0; pos < myCalib->shadinglength; pos++) { if (myCalib->white_shading[colour][pos] == 0) shadata = myShadingBase; else shadata = myfact / myCalib->white_shading[colour][pos]; shadata = min ((shadata * le7cc) / le7d8, 0xffff); myCalib->white_shading[colour][pos] = shadata; } } else break; } } } } /*3d4c */ memset (&calbuffers, 0, sizeof (struct st_cal2)); /* If black shading correction is enabled ... */ if ((Regs[0x1cf] & 8) != 0) Shading_black_apply (dev, Regs, channels, myCalib, &calbuffers); /*3e6e */ /* If white shading correction is enabled ... */ if ((Regs[0x1cf] & 4) != 0) Shading_white_apply (dev, Regs, channels, myCalib, &calbuffers); /* 3f74 */ if (rst == 0) { data_bitset (&Regs[0x60b], 0x40, lf9d0); /*-x------*/ data_bitset (&Regs[0x60b], 0x10, lf9d1); /*---x----*/ rst = Write_Byte (dev->usb_handle, 0xee0b, Regs[0x060b]); } } /*3fb5 */ DBG (DBG_FNC, "- Shading_apply: %i\n", rst); return rst; } static SANE_Int Bulk_Operation (struct st_device *dev, SANE_Byte op, SANE_Int buffer_size, SANE_Byte * buffer, SANE_Int * transferred) { SANE_Int iTransferSize, iBytesToTransfer, iPos, rst, iBytesTransfered; DBG (DBG_FNC, "+ Bulk_Operation(op=%s, buffer_size=%i, buffer):\n", ((op & 0x01) != 0) ? "READ" : "WRITE", buffer_size); iBytesToTransfer = buffer_size; iPos = 0; rst = OK; iBytesTransfered = 0; if (transferred != NULL) *transferred = 0; iTransferSize = min (buffer_size, RTS_Debug->dmatransfersize); if (op != 0) { /* Lectura */ do { iTransferSize = min (iTransferSize, iBytesToTransfer); iBytesTransfered = Read_Bulk (dev->usb_handle, &buffer[iPos], iTransferSize); if (iBytesTransfered < 0) { rst = ERROR; break; } else { if (transferred != NULL) *transferred += iBytesTransfered; } iPos += iTransferSize; iBytesToTransfer -= iTransferSize; } while (iBytesToTransfer > 0); } else { /* Escritura */ do { iTransferSize = min (iTransferSize, iBytesToTransfer); if (Write_Bulk (dev->usb_handle, &buffer[iPos], iTransferSize) != OK) { rst = ERROR; break; } else { if (transferred != NULL) *transferred += iTransferSize; } iPos += iTransferSize; iBytesToTransfer -= iTransferSize; } while (iBytesToTransfer > 0); } DBG (DBG_FNC, "- Bulk_Operation: %i\n", rst); return rst; } static SANE_Int Reading_BufferSize_Notify (struct st_device *dev, SANE_Int data, SANE_Int size) { SANE_Int rst; DBG (DBG_FNC, "+ Reading_BufferSize_Notify(data=%i, size=%i):\n", data, size); rst = RTS_DMA_Enable_Read (dev, 0x0008, size, data); DBG (DBG_FNC, "- Reading_BufferSize_Notify: %i\n", rst); return rst; } static SANE_Int Reading_Wait (struct st_device *dev, SANE_Byte Channels_per_dot, SANE_Byte Channel_size, SANE_Int size, SANE_Int * last_amount, SANE_Int seconds, SANE_Byte op) { SANE_Int rst; SANE_Byte cTimeout, executing; SANE_Int lastAmount, myAmount; long tick; DBG (DBG_FNC, "+ Reading_Wait(Channels_per_dot=%i, Channel_size=%i, size=%i, *last_amount, seconds=%i, op=%i):\n", Channels_per_dot, Channel_size, size, seconds, op); rst = OK; cTimeout = FALSE; lastAmount = 0; myAmount = Reading_BufferSize_Get (dev, Channels_per_dot, Channel_size); if (myAmount < size) { /* Wait until scanner fills its buffer */ if (seconds == 0) seconds = 10; tick = GetTickCount () + (seconds * 1000); while (cTimeout == FALSE) { myAmount = Reading_BufferSize_Get (dev, Channels_per_dot, Channel_size); /* check special case */ if (op == TRUE) { if (((myAmount + 0x450) > size) || (RTS_IsExecuting (dev, &executing) == FALSE)) break; } if (myAmount < size) { /* Check timeout */ if (myAmount == lastAmount) { /* we are in timeout? */ if (tick < GetTickCount ()) { /* TIMEOUT */ rst = ERROR; cTimeout = TRUE; } else usleep (100 * 1000); } else { /* Amount increased, update tick */ lastAmount = myAmount; tick = GetTickCount () + (seconds * 1000); } } else { lastAmount = myAmount; break; /* buffer full */ } } } if (last_amount != NULL) *last_amount = myAmount; DBG (DBG_FNC, "- Reading_Wait: %i , last_amount=%i\n", rst, myAmount); return rst; } static SANE_Int RTS_GetImage_GetBuffer (struct st_device *dev, double dSize, char unsigned *buffer, double *transferred) { SANE_Int rst = ERROR; SANE_Int itransferred; double dtransferred = 0; DBG (DBG_FNC, "+ RTS_GetImage_GetBuffer(dSize=%f, buffer, transferred):\n", dSize); rst = OK; dSize /= 2; if (dSize > 0) { SANE_Int myLength; SANE_Int iPos = 0; do { itransferred = 0; myLength = (dSize <= RTS_Debug->dmasetlength) ? dSize : RTS_Debug->dmasetlength; if (myLength > 0x1ffe0) myLength = 0x1ffe0; rst = ERROR; if (Reading_Wait (dev, 0, 1, myLength * 2, NULL, 5, FALSE) == OK) { if (Reading_BufferSize_Notify (dev, 0, myLength * 2) == OK) rst = Bulk_Operation (dev, BLK_READ, myLength * 2, &buffer[iPos], &itransferred); } if (rst != OK) break; iPos += itransferred; dSize -= itransferred; dtransferred += itransferred * 2; } while (dSize > 0); } /* Return bytes transferred */ if (transferred != NULL) *transferred = dtransferred; if (rst != OK) RTS_DMA_Cancel (dev); DBG (DBG_FNC, "- RTS_GetImage_GetBuffer: %i\n", rst); return rst; } static SANE_Int RTS_GetImage_Read (struct st_device *dev, SANE_Byte * buffer, struct st_scanparams *scancfg, struct st_hwdconfig *hwdcfg) { /*buffer f80c = esp+14 scancfg f850 = esp+18 hwdcfg faac = */ SANE_Int rst = ERROR; DBG (DBG_FNC, "+ RTS_GetImage_Read(buffer, scancfg, hwdcfg):\n"); if (buffer != NULL) { double dSize = scancfg->bytesperline * scancfg->coord.height; SANE_Byte exfn; if (scancfg->depth == 12) dSize = (dSize * 3) / 4; /*3ff6 */ exfn = 1; if (hwdcfg != NULL) if (hwdcfg->compression != FALSE) exfn = 0; if (exfn != 0) { double transferred; rst = RTS_GetImage_GetBuffer (dev, dSize, buffer, &transferred); } if (rst == OK) RTS_WaitScanEnd (dev, 1500); } DBG (DBG_FNC, "- RTS_GetImage_Read: %i\n", rst); return rst; } static SANE_Int RTS_GetImage (struct st_device *dev, SANE_Byte * Regs, struct st_scanparams *scancfg, struct st_gain_offset *gain_offset, SANE_Byte * buffer, struct st_calibration *myCalib, SANE_Int options, SANE_Int gaincontrol) { /* 42b8e10 */ SANE_Int rst = ERROR; /* default */ DBG (DBG_FNC, "+ RTS_GetImage(*Regs, *scancfg, *gain_offset, *buffer, myCalib, options=0x%08x, gaincontrol=%i):\n", options, gaincontrol); dbg_ScanParams (scancfg); /* validate arguments */ if ((Regs != NULL) && (scancfg != NULL)) { if ((scancfg->coord.width != 0) && (scancfg->coord.height != 0)) { struct st_scanparams *myscancfg; /* let's make a copy of scan config */ myscancfg = (struct st_scanparams *) malloc (sizeof (struct st_scanparams)); if (myscancfg != NULL) { struct st_hwdconfig *hwdcfg; memcpy (myscancfg, scancfg, sizeof (struct st_scanparams)); /* Allocate space for low level config */ hwdcfg = (struct st_hwdconfig *) malloc (sizeof (struct st_hwdconfig)); if (hwdcfg != NULL) { memset (hwdcfg, 0, sizeof (struct st_hwdconfig)); if (((options & 2) != 0) || ((_B1 (options) & 1) != 0)) { /* switch off lamp */ data_bitset (&Regs[0x146], 0x40, 0); Write_Byte (dev->usb_handle, 0xe946, Regs[0x146]); usleep (1000 * ((v14b4 == 0) ? 500 : 300)); } hwdcfg->scantype = scan.scantype; hwdcfg->use_gamma_tables = ((options & OP_USE_GAMMA) != 0) ? 1 : 0; hwdcfg->white_shading = ((options & OP_WHITE_SHAD) != 0) ? 1 : 0; hwdcfg->black_shading = ((options & OP_BLACK_SHAD) != 0) ? 1 : 0; hwdcfg->motor_direction = ((options & OP_BACKWARD) != 0) ? MTR_BACKWARD : MTR_FORWARD; hwdcfg->compression = ((options & OP_COMPRESSION) != 0) ? 1 : 0; hwdcfg->static_head = ((options & OP_STATIC_HEAD) != 0) ? 1 : 0; hwdcfg->dummy_scan = (buffer == NULL) ? TRUE : FALSE; hwdcfg->arrangeline = 0; hwdcfg->highresolution = (myscancfg->resolution_x > 1200) ? TRUE : FALSE; hwdcfg->unk3 = 0; /* Set Left coord */ myscancfg->coord.left += ((dev->sensorcfg->type == CCD_SENSOR) ? 24 : 50); switch (myscancfg->resolution_x) { case 1200: myscancfg->coord.left -= 63; break; case 2400: myscancfg->coord.left -= 126; break; } if (myscancfg->coord.left < 0) myscancfg->coord.left = 0; RTS_Setup (dev, Regs, myscancfg, hwdcfg, gain_offset); /* Setting exposure time */ switch (scan.scantype) { case ST_NORMAL: if (scan.resolution_x == 100) { SANE_Int iValue; SANE_Byte *myRegs; myRegs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte)); if (myRegs != NULL) { memset (myRegs, 0, RT_BUFFER_LEN * sizeof (SANE_Byte)); RTS_Setup (dev, myRegs, &scan, hwdcfg, gain_offset); iValue = data_lsb_get (&myRegs[0x30], 3); data_lsb_set (&Regs[0x30], iValue, 3); /*Copy myregisters mexpts to Regs mexpts */ iValue = data_lsb_get (&myRegs[0x33], 3); data_lsb_set (&Regs[0x33], iValue, 3); iValue = data_lsb_get (&myRegs[0x39], 3); data_lsb_set (&Regs[0x39], iValue, 3); iValue = data_lsb_get (&myRegs[0x3f], 3); data_lsb_set (&Regs[0x3f], iValue, 3); free (myRegs); } } break; case ST_NEG: { SANE_Int myvalue; /* Setting exposure times for Negative scans */ data_lsb_set (&Regs[0x30], myscancfg->expt, 3); data_lsb_set (&Regs[0x33], myscancfg->expt, 3); data_lsb_set (&Regs[0x39], myscancfg->expt, 3); data_lsb_set (&Regs[0x3f], myscancfg->expt, 3); data_lsb_set (&Regs[0x36], 0, 3); data_lsb_set (&Regs[0x3c], 0, 3); data_lsb_set (&Regs[0x42], 0, 3); myvalue = ((myscancfg->expt + 1) / (data_lsb_get (&Regs[0xe0], 1) + 1)) - 1; data_lsb_set (&Regs[0xe1], myvalue, 3); } break; } /* 91a0 */ if (myscancfg->resolution_y > 600) { options |= 0x20000000; if (options != 0) /* Always true ... */ SetMultiExposure (dev, Regs); else myscancfg->coord.top += hwdcfg->startpos; } else SetMultiExposure (dev, Regs); /* 91e2 */ RTS_WriteRegs (dev->usb_handle, Regs); if (myCalib != NULL) Shading_apply (dev, Regs, myscancfg, myCalib); if (dev->motorcfg->changemotorcurrent != FALSE) Motor_Change (dev, Regs, Motor_GetFromResolution (myscancfg-> resolution_x)); /* mlock = 0 */ data_bitset (&Regs[0x00], 0x10, 0); data_wide_bitset (&Regs[0xde], 0xfff, 0); /* release motor */ Motor_Release (dev); if (RTS_Warm_Reset (dev) == OK) { rst = OK; SetLock (dev->usb_handle, Regs, (myscancfg->depth == 16) ? FALSE : TRUE); /* set gain control */ Lamp_SetGainMode (dev, Regs, myscancfg->resolution_x, gaincontrol); /* send registers to scanner */ if (RTS_WriteRegs (dev->usb_handle, Regs) == OK) { /* execute! */ if (RTS_Execute (dev) == OK) RTS_GetImage_Read (dev, buffer, myscancfg, hwdcfg); /*92e7 */ } /*92fc */ SetLock (dev->usb_handle, Regs, FALSE); if ((options & 0x200) != 0) { /* switch on lamp */ data_bitset (&Regs[0x146], 0x40, 1); Write_Byte (dev->usb_handle, 0xe946, Regs[0x146]); /* Wait 3 seconds */ usleep (1000 * 3000); } /*9351 */ if (dev->motorcfg->changemotorcurrent == TRUE) Motor_Change (dev, dev->init_regs, 3); } /* free low level configuration */ free (hwdcfg); } /* free scanning configuration */ free (myscancfg); } } } DBG (DBG_FNC, "- RTS_GetImage: %i\n", rst); return rst; } static SANE_Int Refs_Detect (struct st_device *dev, SANE_Byte * Regs, SANE_Int resolution_x, SANE_Int resolution_y, SANE_Int * x, SANE_Int * y) { SANE_Int rst = ERROR; /* default */ DBG (DBG_FNC, "+ Refs_Detect(*Regs, resolution_x=%i, resolution_y=%i):\n", resolution_x, resolution_y); if ((x != NULL) && (y != NULL)) { SANE_Byte *image; struct st_scanparams scancfg; *x = *y = 0; /* default */ /* set configuration to scan a little area at the top-left corner */ memset (&scancfg, 0, sizeof (struct st_scanparams)); scancfg.depth = 8; scancfg.colormode = CM_GRAY; scancfg.channel = CL_RED; scancfg.resolution_x = resolution_x; scancfg.resolution_y = resolution_y; scancfg.coord.left = 4; scancfg.coord.width = (resolution_x * 3) / 10; scancfg.coord.top = 1; scancfg.coord.height = (resolution_y * 4) / 10; scancfg.shadinglength = (resolution_x * 17) / 2; scancfg.bytesperline = scancfg.coord.width; /* allocate space to store image */ image = (SANE_Byte *) malloc ((scancfg.coord.height * scancfg.coord.width) * sizeof (SANE_Byte)); if (image != NULL) { struct st_gain_offset gain_offset; SANE_Int gaincontrol, pwmlamplevel_backup, C; gaincontrol = 0; if (RTS_Debug->use_fixed_pwm == FALSE) { /* 3877 */ gaincontrol = Lamp_GetGainMode (dev, resolution_x, ST_NORMAL); /* scan.scantype */ pwmlamplevel = 0; Lamp_PWM_use (dev, 1); Lamp_PWM_DutyCycle_Set (dev, (gaincontrol == 0) ? 0x12 : 0x26); /* Enciende flb lamp */ Lamp_Status_Set (dev, NULL, TRUE, FLB_LAMP); usleep (1000 * 2000); } /* 38d6 */ pwmlamplevel_backup = pwmlamplevel; pwmlamplevel = 0; Lamp_PWM_use (dev, 1); memset (&gain_offset, 0, sizeof (struct st_gain_offset)); for (C = CL_RED; C <= CL_BLUE; C++) { gain_offset.pag[C] = 3; gain_offset.vgag1[C] = 4; gain_offset.vgag2[C] = 4; } /* perform lamp warmup */ Lamp_Warmup (dev, Regs, FLB_LAMP, resolution_x); /* retrieve image from scanner */ if (RTS_GetImage (dev, Regs, &scancfg, &gain_offset, image, 0, 0x20000000, gaincontrol) == OK) { SANE_Int ser1, ler1; /* same image to disk if required by user */ if (RTS_Debug->SaveCalibFile != FALSE) { dbg_tiff_save ("pre-autoref.tiff", scancfg.coord.width, scancfg.coord.height, scancfg.depth, CM_GRAY, scancfg.resolution_x, scancfg.resolution_y, image, scancfg.coord.height * scancfg.coord.width); } /* calculate reference position */ if (Refs_Analyze_Pattern (&scancfg, image, &ler1, 1, &ser1, 0) == OK) { *y = scancfg.coord.top + ler1; *x = scancfg.coord.left + ser1; rst = OK; } } free (image); pwmlamplevel = pwmlamplevel_backup; } DBG (DBG_FNC, " -> Detected refs: x=%i , y=%i\n", *x, *y); } DBG (DBG_FNC, "- Refs_Detect: %i\n", rst); return rst; } static SANE_Int Refs_Set (struct st_device *dev, SANE_Byte * Regs, struct st_scanparams *scancfg) { SANE_Int rst; SANE_Int y, x; struct st_autoref refcfg; DBG (DBG_FNC, "+ Refs_Set(*Regs, *scancfg):\n"); dbg_ScanParams (scancfg); rst = OK; /* get fixed references for given resolution */ cfg_vrefs_get (dev->sensorcfg->type, scancfg->resolution_x, &scan.ler, &scan.ser); scan.leftleading = scan.ser; scan.startpos = scan.ler; /* get auto reference configuration */ cfg_autoref_get (&refcfg); if (refcfg.type != REF_NONE) { /* if reference counter is == 0 perform auto detection */ if (Refs_Counter_Load (dev) == 0) { DBG (DBG_FNC, " -> Refs_Set - Autodetection mandatory (counter == 0)\n"); refcfg.type = REF_AUTODETECT; } switch (refcfg.type) { case REF_AUTODETECT: /* try to autodetect references scanning a little area */ if (Refs_Detect (dev, Regs, refcfg.resolution, refcfg.resolution, &x, &y) == OK) Refs_Save (dev, x, y); else rst = ERROR; Head_ParkHome (dev, TRUE, dev->motorcfg->parkhomemotormove); break; case REF_TAKEFROMSCANNER: /* Try to get values from scanner */ if (Refs_Load (dev, &x, &y) == ERROR) { if (Refs_Detect (dev, Regs, refcfg.resolution, refcfg.resolution, &x, &y) == OK) Refs_Save (dev, x, y); else rst = ERROR; Head_ParkHome (dev, TRUE, dev->motorcfg->parkhomemotormove); } break; } if (rst == OK) { /* values are based on resolution given by refcfg.resolution. offset_x and y are based on 2400 dpi so convert values to that dpi before adding offsets and then return to resolution given by user */ x *= (2400 / refcfg.resolution); y *= (2400 / refcfg.resolution); scan.leftleading = x; scan.startpos = y; scan.ser = ((x + refcfg.offset_x) * scancfg->resolution_x) / 2400; scan.ler = ((y + refcfg.offset_y) * scancfg->resolution_y) / 2400; DBG (DBG_FNC, " -> After SEROffset and LEROffset, xoffset = %i, yoffset =%i\n", scan.ser, scan.ler); } /* increase refs counter */ Refs_Counter_Inc (dev); } DBG (DBG_FNC, "- Refs_Set: %i\n", rst); return rst; } static SANE_Int Lamp_Status_Set (struct st_device *dev, SANE_Byte * Regs, SANE_Int turn_on, SANE_Int lamp) { SANE_Int rst = ERROR; /* default */ SANE_Byte freevar = FALSE; DBG (DBG_FNC, "+ Lamp_Status_Set(*Regs, turn_on=%i->%s, lamp=%s)\n", turn_on, ((((lamp - 1) | turn_on) & 1) == 1) ? "Yes" : "No", (lamp == FLB_LAMP) ? "FLB_LAMP" : "TMA_LAMP"); if (Regs == NULL) { Regs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte)); if (Regs != NULL) freevar = TRUE; } if (Regs != NULL) { RTS_ReadRegs (dev->usb_handle, Regs); /* next op depends on chipset */ switch (dev->chipset->model) { case RTS8822BL_03A: /* register 0xe946 has 2 bits and each one refers one lamp 0x40: FLB_LAMP | 0x20 : TMA_LAMP if both were enabled both lamps would be switched on */ data_bitset (&Regs[0x146], 0x20, ((lamp == TMA_LAMP) && (turn_on == TRUE)) ? 1 : 0); /* TMA */ data_bitset (&Regs[0x146], 0x40, ((lamp == FLB_LAMP) && (turn_on == TRUE)) ? 1 : 0); /* FLB */ data_bitset (&Regs[0x155], 0x10, (lamp != FLB_LAMP) ? 1 : 0); break; default: /* the other chipsets only use one bit to indicate when a lamp is switched on or not being bit 0x10 in 0xe955 who decides which lamp is affected */ /* switch on lamp? yes if TMA_LAMP, else whatever turn_on says */ data_bitset (&Regs[0x146], 0x40, ((lamp - 1) | turn_on)); /* what lamp must be switched on? */ if ((Regs[0x146] & 0x40) != 0) data_bitset (&Regs[0x155], 0x10, (lamp != FLB_LAMP) ? 1 : 0); break; } /*42b8cd1 */ /* switch on/off lamp */ /*dev->init_regs[0x0146] = (dev->init_regs[0x146] & 0xbf) | (Regs[0x146] & 0x40); */ dev->init_regs[0x0146] = (dev->init_regs[0x146] & 0x9f) | (Regs[0x146] & 0x60); /*-xx-----*/ /* Which lamp */ dev->init_regs[0x0155] = Regs[0x0155]; Write_Byte (dev->usb_handle, 0xe946, Regs[0x0146]); usleep (1000 * 200); Write_Buffer (dev->usb_handle, 0xe954, &Regs[0x0154], 2); } if (freevar != FALSE) { free (Regs); Regs = NULL; } DBG (DBG_FNC, "- Lamp_Status_Set: %i\n", rst); return rst; } static SANE_Int Get_PAG_Value (SANE_Byte scantype, SANE_Byte color) { SANE_Int rst, iType, iColor; switch (scantype) { case ST_NEG: iType = CALIBNEGATIVEFILM; break; case ST_TA: iType = CALIBTRANSPARENT; break; case ST_NORMAL: iType = CALIBREFLECTIVE; break; default: iType = CALIBREFLECTIVE; break; } switch (color) { case CL_BLUE: iColor = PAGB; break; case CL_GREEN: iColor = PAGG; break; case CL_RED: iColor = PAGR; break; default: iColor = PAGR; break; } rst = get_value (iType, iColor, 1, FITCALIBRATE); DBG (DBG_FNC, "> Get_PAG_Value(scantype=%s, color=%i): %i\n", dbg_scantype (scantype), color, rst); return rst; } static SANE_Byte Lamp_GetGainMode (struct st_device *dev, SANE_Int resolution, SANE_Byte scantype) { SANE_Byte ret; SANE_Int mygain, iValue; switch (scantype) { case ST_TA: ret = 0; iValue = DPIGAINCONTROL_TA600; break; case ST_NEG: ret = 1; iValue = DPIGAINCONTROL_NEG600; break; default: /* Reflective */ ret = 1; iValue = DPIGAINCONTROL600; break; } mygain = get_value (SCAN_PARAM, iValue, ret, usbfile); ret = 0; /* */ if (scantype == ST_NORMAL) { if (dev->chipset->model == RTS8822L_02A) { switch (resolution) { case 100: case 150: case 300: case 600: case 1200: case 2400: case 4800: ret = ((RTS_Debug->usbtype != USB11) && (mygain != 0)) ? 1 : 0; break; } } else { switch (resolution) { case 100: case 200: case 300: case 600: if (RTS_Debug->usbtype != USB11) ret = (mygain != 0) ? 1 : 0; else ret = (resolution == 100) ? 1 : 0; break; case 1200: case 2400: ret = 0; break; } } } else if (scantype == ST_TA) { switch (resolution) { /*hp3970 */ case 100: case 200: /*common */ case 300: case 600: case 1200: case 2400: /*hp4370 */ case 150: case 4800: ret = ((RTS_Debug->usbtype != USB11) && (mygain != 0)) ? 1 : 0; break; } } else { /* ST_NEG */ switch (resolution) { case 100: case 200: case 300: case 600: ret = ((RTS_Debug->usbtype != USB11) && (mygain != 0)) ? 1 : 0; break; case 1200: case 2400: case 4800: /*hp4370 */ ret = 0; break; } } DBG (DBG_FNC, "> Lamp_GetGainMode(resolution=%i, scantype=%s): %i\n", resolution, dbg_scantype (scantype), ret); return ret; } static SANE_Int GetOneLineInfo (struct st_device *dev, SANE_Int resolution, SANE_Int * maximus, SANE_Int * minimus, double *average) { SANE_Int rst = ERROR; DBG (DBG_FNC, "+ GetOneLineInfo(resolution=%i, *maximus, *minimus, *average):\n", resolution); /* Check parameters */ if ((maximus != NULL) && (minimus != NULL) && (average != NULL)) { SANE_Byte *Regs, *image; SANE_Int a, gainmode; struct st_gain_offset gain_offset; struct st_scanparams scancfg; Regs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte)); if (Regs != NULL) { /* Copy scanner registers */ memcpy (Regs, dev->init_regs, RT_BUFFER_LEN * sizeof (SANE_Byte)); /* Setting some registers */ for (a = 0x192; a <= 0x19d; a++) Regs[a] = 0; /* Create calibration table */ for (a = CL_RED; a <= CL_BLUE; a++) { gain_offset.edcg1[a] = 256; gain_offset.edcg2[a] = 0; gain_offset.odcg1[a] = 256; gain_offset.odcg2[a] = 0; gain_offset.vgag1[a] = 4; gain_offset.vgag2[a] = 4; gain_offset.pag[a] = Get_PAG_Value (scan.scantype, a); } RTS_GetScanmode (dev, scantype, 0, resolution); /* Setting scanning params */ memset (&scancfg, 0, sizeof (struct st_scanparams)); scancfg.colormode = CM_COLOR; scancfg.resolution_x = resolution; scancfg.resolution_y = resolution; scancfg.coord.left = 100; scancfg.coord.width = (resolution * 8.5) - 100; scancfg.coord.top = 1; scancfg.coord.height = 1; scancfg.depth = 8; scancfg.shadinglength = resolution * 8.5; scancfg.v157c = scancfg.coord.width * 3; scancfg.bytesperline = scancfg.v157c; /* Reserve buffer for line */ image = (SANE_Byte *) malloc (((scancfg.coord.width * 0x21) * 3) * sizeof (SANE_Byte)); if (image != NULL) { gainmode = Lamp_GetGainMode (dev, resolution & 0xffff, scan.scantype); if (RTS_GetImage (dev, Regs, &scancfg, &gain_offset, image, 0, OP_STATIC_HEAD, gainmode) != ERROR) { /* Read all image to take max min and average colours */ SANE_Byte *pointer1 = image; SANE_Byte *pointer2; SANE_Byte *pointer3; SANE_Int cmin[3]; /* min values */ SANE_Int cmax[3]; /* max values */ double cave[3]; /* average values */ SANE_Int mysize; if (scancfg.colormode != CM_GRAY) { pointer2 = image; pointer3 = image; } else { pointer2 = image + 1; pointer3 = image + 2; } for (a = CL_RED; a <= CL_BLUE; a++) { cmin[a] = 255; cmax[a] = 0; cave[a] = 0; } if (scancfg.coord.height > 0) { SANE_Int y, x; SANE_Byte *mypointer; SANE_Byte color; SANE_Int desp[3]; desp[CL_RED] = pointer1 - pointer3; desp[CL_GREEN] = pointer2 - pointer3; desp[CL_BLUE] = 0; for (y = 0; y < scancfg.coord.height; y++) { if (scancfg.coord.width > 0) { mypointer = pointer3; for (x = 0; x < scancfg.coord.width; x++) { for (a = CL_RED; a <= CL_BLUE; a++) { /* Take colour values */ color = *(mypointer + desp[a]); /* Take max values for each color */ cmax[a] = max (cmax[a], color); /* Take min values for each color */ cmin[a] = min (cmin[a], color); /* Average */ cave[a] += color; } mypointer += 3; } } /* point to the pixel that is below */ pointer1 += scancfg.coord.width * 3; pointer2 += scancfg.coord.width * 3; pointer3 += scancfg.coord.width * 3; } } mysize = scancfg.coord.height * scancfg.coord.width; if (mysize < 1) mysize = 1; for (a = CL_RED; a <= CL_BLUE; a++) { maximus[a] = cmax[a]; minimus[a] = cmin[a]; average[a] = cave[a] / mysize; } DBG (DBG_FNC, " -> GetOneLineInfo: max r=%3i g=%3i b=%3i\n", maximus[CL_RED], maximus[CL_GREEN], maximus[CL_BLUE]); DBG (DBG_FNC, " -> min r=%3i g=%3i b=%3i\n", minimus[CL_RED], minimus[CL_GREEN], minimus[CL_BLUE]); DBG (DBG_FNC, " -> avg r=%3.0f g=%3.0f b=%3.0f\n", average[CL_RED], average[CL_GREEN], average[CL_BLUE]); rst = OK; } free (image); } free (Regs); } } DBG (DBG_FNC, "- GetOneLineInfo: %i\n", rst); return OK; } static SANE_Int Lamp_PWM_CheckStable (struct st_device *dev, SANE_Int resolution, SANE_Int lamp) { struct st_checkstable check; SANE_Int rst; DBG (DBG_FNC, "+ Lamp_PWM_CheckStable(resolution=%i, lamp=%i):\n", resolution, lamp); rst = cfg_checkstable_get (lamp, &check); if (rst == OK) { SANE_Int maximus[3] = { 0 }; SANE_Int minimus[3] = { 0 }; double average[3] = { 0 }; SANE_Int maxbigger; SANE_Int last_colour = 0; double diff = check.diff * 0.01; long tottime = GetTickCount () + check.tottime; while (GetTickCount () <= tottime) { rst = GetOneLineInfo (dev, resolution, maximus, minimus, average); if (rst == OK) { /* Takes maximal colour value */ maxbigger = max (maximus[CL_GREEN], max (maximus[CL_BLUE], maximus[CL_RED])); /*breaks when colour intensity increases 'diff' or lower */ if (abs (maxbigger - last_colour) < diff) { DBG (DBG_FNC, " -> PWM is ready\n"); break; } last_colour = maxbigger; } usleep (1000 * check.interval); } } DBG (DBG_FNC, "- Lamp_PWM_CheckStable: %i\n", rst); return OK; } static SANE_Byte Refs_Counter_Load (struct st_device *dev) { SANE_Byte data = 15; DBG (DBG_FNC, "+ Refs_Counter_Load:\n"); /* check if chipset supports accessing eeprom */ if ((dev->chipset->capabilities & CAP_EEPROM) != 0) if (RTS_EEPROM_ReadByte (dev->usb_handle, 0x78, &data) != OK) data = 15; DBG (DBG_FNC, "- Refs_Counter_Load: %i\n", _B0 (data)); return data; } static SANE_Int Refs_Counter_Save (struct st_device *dev, SANE_Byte data) { SANE_Int rst = OK; DBG (DBG_FNC, "+ Refs_Counter_Save(data=%i):\n", data); /* check if chipset supports accessing eeprom */ if ((dev->chipset->capabilities & CAP_EEPROM) != 0) { if (data > 15) data = 15; rst = RTS_EEPROM_WriteByte (dev->usb_handle, 0x78, data); } DBG (DBG_FNC, "- Refs_Counter_Save: %i\n", rst); return rst; } static SANE_Int Refs_Counter_Inc (struct st_device *dev) { SANE_Byte data; DBG (DBG_FNC, "+ Refs_Counter_Inc:\n"); data = Refs_Counter_Load (dev) + 1; if (data >= 15) data = 0; Refs_Counter_Save (dev, data); DBG (DBG_FNC, "- Refs_Counter_Inc() : Count=%i\n", data); return OK; } static SANE_Int Load_StripCoords (SANE_Int scantype, SANE_Int * ypos, SANE_Int * xpos) { SANE_Int iType; switch (scantype) { case 3: iType = CALIBNEGATIVEFILM; break; case 2: iType = CALIBTRANSPARENT; break; default: iType = CALIBREFLECTIVE; break; } *xpos = get_value (iType, WSTRIPXPOS, 0, FITCALIBRATE); *ypos = get_value (iType, WSTRIPYPOS, 0, FITCALIBRATE); DBG (DBG_FNC, "> Load_StripCoords(scantype=%s): ypos=%i, xpos=%i\n", dbg_scantype (scantype), *ypos, *xpos); return OK; } static SANE_Int Head_Relocate (struct st_device *dev, SANE_Int speed, SANE_Int direction, SANE_Int ypos) { SANE_Int rst; SANE_Byte *Regs; DBG (DBG_FNC, "+ Head_Relocate(speed=%i, direction=%i, ypos=%i):\n", speed, direction, ypos); rst = ERROR; Regs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte)); if (Regs != NULL) { struct st_motormove mymotor; struct st_motorpos mtrpos; memset (&mymotor, 0, sizeof (struct st_motormove)); memcpy (Regs, dev->init_regs, RT_BUFFER_LEN * sizeof (SANE_Byte)); if (speed < dev->motormove_count) memcpy (&mymotor, dev->motormove[speed], sizeof (struct st_motormove)); /*83fe */ mtrpos.coord_y = ypos; mtrpos.options = MTR_ENABLED | ((direction == MTR_BACKWARD) ? MTR_BACKWARD : MTR_FORWARD); mtrpos.v12e448 = 0; mtrpos.v12e44c = 1; Motor_Move (dev, Regs, &mymotor, &mtrpos); /* waits 15 seconds */ RTS_WaitScanEnd (dev, 15000); free (Regs); rst = OK; } DBG (DBG_FNC, "- Head_Relocate: %i\n", rst); return rst; } static SANE_Int Calib_CreateFixedBuffers () { SANE_Byte channel; SANE_Int ret; DBG (DBG_FNC, "> Calib_CreateFixedBuffers()\n"); ret = OK; channel = 0; while ((channel < 3) && (ret == OK)) { /* First table */ if (fixed_black_shading[channel] == NULL) fixed_black_shading[channel] = (USHORT *) malloc (0x7f8 * sizeof (USHORT)); if (fixed_black_shading[channel] != NULL) memset (fixed_black_shading[channel], 0, 0x7f8 * sizeof (USHORT)); else ret = ERROR; /* Second table */ if (fixed_white_shading[channel] == NULL) fixed_white_shading[channel] = (USHORT *) malloc (0x7f8 * sizeof (USHORT)); if (fixed_white_shading[channel] != NULL) memset (fixed_white_shading[channel], 0, 0x7f8 * sizeof (USHORT)); else ret = ERROR; channel++; } return ret; } static SANE_Int Calib_CreateBuffers (struct st_device *dev, struct st_calibration *buffer, SANE_Int my14b4) { SANE_Int ebp, ret, channel; ret = ERROR; (void) dev; buffer->shadinglength = scan.coord.width; ebp = 0x14; if (my14b4 != 0) { /* 673d */ if (Calib_CreateFixedBuffers () == OK) { for (channel = 0; channel < 3; channel++) { buffer->white_shading[channel] = fixed_white_shading[channel]; buffer->black_shading[channel] = fixed_black_shading[channel]; } ret = OK; } } else { /* 677f */ SANE_Int pos; channel = 0; while ((channel < 3) && (ret == OK)) { buffer->black_shading[channel] = (USHORT *) malloc (ebp + (buffer->shadinglength * sizeof (USHORT))); buffer->white_shading[channel] = (USHORT *) malloc (ebp + (buffer->shadinglength * sizeof (USHORT))); if ((buffer->black_shading[channel] != NULL) && (buffer->white_shading[channel] != NULL)) { for (pos = 0; pos < buffer->shadinglength; pos++) { buffer->black_shading[channel][pos] = 0x00; buffer->white_shading[channel][pos] = 0x4000; } ret = OK; } else Calib_FreeBuffers (buffer); channel++; } } DBG (DBG_FNC, "> Calib_CreateBuffers: *buffer, my14b4=%i): %i\n", my14b4, ret); return ret; } static void Calib_FreeBuffers (struct st_calibration *caltables) { DBG (DBG_FNC, "> Calib_FreeBuffers(*caltables)\n"); if (caltables != NULL) { SANE_Int channel; for (channel = 0; channel < 3; channel++) { if (caltables->black_shading[channel] != NULL) { free (caltables->black_shading[channel]); caltables->black_shading[channel] = NULL; } if (caltables->white_shading[channel] != NULL) { free (caltables->white_shading[channel]); caltables->white_shading[channel] = NULL; } } } } static SANE_Int Calib_LoadConfig (struct st_device *dev, struct st_calibration_config *calibcfg, SANE_Int scantype, SANE_Int resolution, SANE_Int bitmode) { SANE_Int section, a; struct st_autoref refcfg; DBG (DBG_FNC, "> Calib_LoadConfig(*calibcfg, scantype=%s, resolution=%i, bitmode=%i)\n", dbg_scantype (scantype), resolution, bitmode); switch (scantype) { case ST_NEG: section = CALIBNEGATIVEFILM; break; case ST_TA: section = CALIBTRANSPARENT; break; default: section = CALIBREFLECTIVE; break; } calibcfg->WStripXPos = get_value (section, WSTRIPXPOS, 0, FITCALIBRATE); calibcfg->WStripYPos = get_value (section, WSTRIPYPOS, 0, FITCALIBRATE); calibcfg->BStripXPos = get_value (section, BSTRIPXPOS, 0, FITCALIBRATE); calibcfg->BStripYPos = get_value (section, WSTRIPYPOS, 0, FITCALIBRATE); /* get calibration wrefs */ cfg_wrefs_get (dev->sensorcfg->type, bitmode, resolution, scantype, &calibcfg->WRef[CL_RED], &calibcfg->WRef[CL_GREEN], &calibcfg->WRef[CL_BLUE]); /* 4913 */ for (a = CL_RED; a <= CL_BLUE; a++) { WRef[a] = _B0 (calibcfg->WRef[a]); calibcfg->BRef[a] = get_value (section, BREFR + a, 10, FITCALIBRATE); calibcfg->OffsetEven1[a] = get_value (section, OFFSETEVEN1R + a, 256, FITCALIBRATE); calibcfg->OffsetEven2[a] = get_value (section, OFFSETEVEN2R + a, 0, FITCALIBRATE); calibcfg->OffsetOdd1[a] = get_value (section, OFFSETODD1R + a, 256, FITCALIBRATE); calibcfg->OffsetOdd2[a] = get_value (section, OFFSETODD2R + a, 0, FITCALIBRATE); } calibcfg->RefBitDepth = _B0 (get_value (section, REFBITDEPTH, 8, FITCALIBRATE)); calibcfg->CalibOffset10n = _B0 (get_value (section, CALIBOFFSET10N, 3, FITCALIBRATE)); calibcfg->CalibOffset20n = _B0 (get_value (section, CALIBOFFSET20N, 0, FITCALIBRATE)); calibcfg->OffsetHeight = get_value (section, OFFSETHEIGHT, 10, FITCALIBRATE); /* 4ae9 */ /* get left coordinate and length to calibrate offset */ cfg_offset_get (dev->sensorcfg->type, resolution, scantype, &calibcfg->OffsetPixelStart, &calibcfg->OffsetNPixel); /*4c49 */ calibcfg->OffsetNSigma = get_value (section, OFFSETNSIGMA, 2, FITCALIBRATE); calibcfg->OffsetTargetMax = get_value (section, OFFSETTARGETMAX, 0x32, FITCALIBRATE) * 0.01; calibcfg->OffsetTargetMin = get_value (section, OFFSETTARGETMIN, 2, FITCALIBRATE) * 0.01; calibcfg->OffsetBoundaryRatio1 = get_value (section, OFFSETBOUNDARYRATIO1, 0x64, FITCALIBRATE) * 0.01; calibcfg->OffsetBoundaryRatio2 = get_value (section, OFFSETBOUNDARYRATIO2, 0x64, FITCALIBRATE) * 0.01; calibcfg->OffsetAvgRatio1 = get_value (section, OFFSETAVGRATIO1, 0x64, FITCALIBRATE) * 0.01; calibcfg->OffsetAvgRatio2 = get_value (section, OFFSETAVGRATIO2, 0x64, FITCALIBRATE) * 0.01; calibcfg->AdcOffQuickWay = get_value (section, ADCOFFQUICKWAY, 1, FITCALIBRATE); calibcfg->AdcOffPredictStart = get_value (section, ADCOFFPREDICTSTART, 0xc8, FITCALIBRATE); calibcfg->AdcOffPredictEnd = get_value (section, ADCOFFPREDICTEND, 0x1f4, FITCALIBRATE); calibcfg->AdcOffEvenOdd = get_value (section, ADCOFFEVENODD, 1, FITCALIBRATE); calibcfg->OffsetTuneStep1 = _B0 (get_value (section, OFFSETTUNESTEP1, 1, FITCALIBRATE)); calibcfg->OffsetTuneStep2 = _B0 (get_value (section, OFFSETTUNESTEP2, 1, FITCALIBRATE)); calibcfg->CalibGain10n = get_value (section, CALIBGAIN10N, 1, FITCALIBRATE); calibcfg->CalibGain20n = get_value (section, CALIBGAIN20N, 0, FITCALIBRATE); calibcfg->CalibPAGOn = get_value (section, CALIBPAGON, 0, FITCALIBRATE); for (a = CL_RED; a <= CL_BLUE; a++) { calibcfg->OffsetAvgTarget[a] = _B0 (get_value (section, OFFSETAVGTARGETR + a, 0x0d, FITCALIBRATE)); calibcfg->PAG[a] = get_value (section, PAGR + a, 3, FITCALIBRATE); calibcfg->Gain1[a] = get_value (section, GAIN1R + a, 4, FITCALIBRATE); calibcfg->Gain2[a] = get_value (section, GAIN2R + a, 4, FITCALIBRATE); calibcfg->WShadingPreDiff[a] = get_value (section, WSHADINGPREDIFFR + a, -1, FITCALIBRATE); calibcfg->BShadingPreDiff[a] = get_value (section, BSHADINGPREDIFFR + a, 2, FITCALIBRATE); } calibcfg->GainHeight = get_value (section, GAINHEIGHT, 0x1e, FITCALIBRATE); calibcfg->GainTargetFactor = get_value (section, GAINTARGETFACTOR, 0x5a, FITCALIBRATE) * 0.01; calibcfg->TotShading = get_value (section, TOTSHADING, 0, FITCALIBRATE); /* White shading */ calibcfg->WShadingOn = get_value (section, WSHADINGON, 3, FITCALIBRATE); calibcfg->WShadingHeight = get_value (section, WSHADINGHEIGHT, 0x18, FITCALIBRATE); /* Black shading */ calibcfg->BShadingOn = get_value (section, BSHADINGON, 2, FITCALIBRATE); calibcfg->BShadingHeight = get_value (section, BSHADINGHEIGHT, 0x1e, FITCALIBRATE); calibcfg->BShadingDefCutOff = get_value (section, BSHADINGDEFCUTOFF, 0, FITCALIBRATE); refcfg.extern_boundary = 0; cfg_autoref_get (&refcfg); calibcfg->ExternBoundary = refcfg.extern_boundary * 0.01; calibcfg->EffectivePixel = cfg_effectivepixel_get (dev->sensorcfg->type, resolution); return OK; } static SANE_Int Calib_AdcGain (struct st_device *dev, struct st_calibration_config *calibcfg, SANE_Int arg2, SANE_Int gaincontrol) { /* 0606F8E0 04F60738 |Arg1 = 04F60738 0606F8E4 0606F90C |Arg2 = 0606F90C calibcfg 0606F8E8 00000001 |Arg3 = 00000001 arg2 0606F8EC 00000001 \Arg4 = 00000001 gaincontrol */ SANE_Int rst = ERROR; SANE_Byte *myRegs; /*f1c0 */ DBG (DBG_FNC, "+ Calib_AdcGain(*calibcfg, arg2=%i, gaincontrol=%i)\n", arg2, gaincontrol); myRegs = (SANE_Byte *) malloc (sizeof (SANE_Byte) * RT_BUFFER_LEN); if (myRegs != NULL) { struct st_scanparams *scancfg; /*f17c */ SANE_Int bytes_per_line, bytes_to_next_colour, bytes_per_pixel; /* get register values to perform adc gain calibration */ memcpy (myRegs, &calibdata->Regs, sizeof (SANE_Byte) * RT_BUFFER_LEN); scancfg = (struct st_scanparams *) malloc (sizeof (struct st_scanparams)); if (scancfg != NULL) { SANE_Byte *image, *pgain, *pcalgain; /* get proper scan configuration */ memcpy (scancfg, &calibdata->scancfg, sizeof (struct st_scanparams)); /* set gain control type */ Lamp_SetGainMode (dev, myRegs, scancfg->resolution_x, gaincontrol); /* 8-bit depth */ scancfg->depth = 8; /* set coordinates */ if ((scan.scantype > 0) && (scan.scantype < 4)) scancfg->coord.left += scan.ser; if ((scancfg->coord.width & 1) == 0) scancfg->coord.width++; scancfg->coord.top = 1; scancfg->coord.height = calibcfg->OffsetHeight; /* three more values to read image data after getting image from scanner */ switch (scancfg->colormode) { case CM_GRAY: case CM_LINEART: bytes_to_next_colour = 0; bytes_per_pixel = 1; bytes_per_line = scancfg->coord.width; break; default: /* CM_COLOR */ /* c027 */ bytes_to_next_colour = 1; bytes_per_line = scancfg->coord.width * 3; if (scancfg->samplerate == LINE_RATE) { bytes_to_next_colour = scancfg->coord.width; bytes_per_pixel = 1; } else bytes_per_pixel = 3; break; } /*7fc7 */ scancfg->v157c = bytes_per_line; scancfg->bytesperline = bytes_per_line; /* select type of gain parameters to set */ if (arg2 != 0) { pgain = calibdata->gain_offset.vgag1; pcalgain = calibcfg->Gain1; } else { /*7ff2 */ pgain = calibdata->gain_offset.vgag2; pcalgain = calibcfg->Gain2; } /*8002 */ /* Allocate space for image | size = 132912 */ image = (SANE_Byte *) malloc (sizeof (SANE_Byte) * ((scancfg->coord.height + 16) * bytes_per_line)); if (image != NULL) { /* Lets read image */ if (RTS_GetImage (dev, myRegs, scancfg, &calibdata->gain_offset, image, NULL, OP_STATIC_HEAD, gaincontrol) == OK) { SANE_Int a; SANE_Int vmin[3], vmax[3]; double dval[3] = { 0.0 }; /*f1a8 f1b0 f1b8 */ SANE_Byte *pimage = image; /* initialize values */ for (a = CL_RED; a <= CL_BLUE; a++) { calibcfg->unk1[a] = 0; calibcfg->unk2[a] = 0xff; vmin[a] = 0xff; vmax[a] = 0; } /* process image data */ if (scancfg->coord.width > 0) { /*8104 */ SANE_Int pos, myheight /*f164 */ ; SANE_Int chn_sum[3]; for (pos = scancfg->coord.width; pos > 0; pos--) { chn_sum[CL_RED] = chn_sum[CL_GREEN] = chn_sum[CL_BLUE] = 0; if (scancfg->coord.height > 0) for (myheight = 0; myheight < scancfg->coord.height; myheight++) for (a = CL_RED; a <= CL_BLUE; a++) chn_sum[a] += *(pimage + (bytes_per_line * myheight) + (bytes_to_next_colour * a)); /*816e */ for (a = CL_RED; a <= CL_BLUE; a++) { vmin[a] = min (vmin[a], chn_sum[a] / scancfg->coord.height); vmax[a] = max (vmax[a], chn_sum[a] / scancfg->coord.height); calibcfg->unk1[a] = max (calibcfg->unk1[a], vmax[a]); calibcfg->unk2[a] = min (calibcfg->unk1[a], vmin[a]); dval[a] += vmax[a] & 0xffff; } pimage += bytes_per_pixel; } } /*82b0 */ dval[CL_RED] /= scancfg->coord.width; dval[CL_GREEN] /= scancfg->coord.width; dval[CL_BLUE] /= scancfg->coord.width; DBG (DBG_FNC, " -> adcgain (av/l): r=%f, g=%f, b=%f\n", dval[CL_RED], dval[CL_GREEN], dval[CL_BLUE]); DBG (DBG_FNC, " -> (max ): R=%i, G=%i, B=%i\n", calibcfg->unk1[CL_RED], calibcfg->unk1[CL_GREEN], calibcfg->unk1[CL_BLUE]); DBG (DBG_FNC, " -> (min ): r=%i, g=%i, b=%i\n", calibcfg->unk2[CL_RED], calibcfg->unk2[CL_GREEN], calibcfg->unk2[CL_BLUE]); if (scancfg->colormode == CM_COLOR) { /*8353 */ double dvalue; SANE_Int ival; for (a = CL_RED; a <= CL_BLUE; a++) { dvalue = ((((calibcfg->WRef[a] * (1 << scancfg->depth)) * calibcfg->GainTargetFactor) * 0.00390625) / dval[a]) * ((44 - pgain[a]) / 40); if (dvalue > 0.9090909090909091) { /*83d7 */ dvalue = min (44 - (40 / dvalue), 31); ival = dvalue; pgain[a] = _B0 (ival); pcalgain[a] = _B0 (ival); } else { pgain[a] = 0; pcalgain[a] = 0; } } } else { /*843c */ /*falta codigo */ double dvalue; SANE_Int ival; dvalue = ((44 - pgain[CL_RED]) / 40) * ((((1 << scancfg->depth) * calibcfg->WRef[scancfg-> channel]) * 0.9) * 0.00390625) / 17.08509389671362; for (a = CL_RED; a <= CL_BLUE; a++) { if (dvalue > 0.9090909090909091) { dvalue = min (44 - (40 / dvalue), 31); ival = dvalue; pgain[a] = _B0 (ival); pcalgain[a] = _B0 (ival); } else { /*84e3 */ pgain[a] = 0; pcalgain[a] = 0; } } } /*84fa */ /* Save buffer */ if (RTS_Debug->SaveCalibFile != FALSE) { dbg_tiff_save ("adcgain.tiff", scancfg->coord.width, scancfg->coord.height, scancfg->depth, CM_COLOR, scancfg->resolution_x, scancfg->resolution_y, image, (scancfg->coord.height + 16) * bytes_per_line); } /* check if peak values are above offset average target + 5 */ for (a = CL_RED; a <= CL_BLUE; a++) if (calibcfg->unk1[a] >= calibcfg->OffsetAvgTarget[a] + 5) { rst = OK; break; } } free (image); } free (scancfg); } free (myRegs); } /* v14b8 = (rst == OK)? 0: 1; */ /* show */ dbg_calibtable (&calibdata->gain_offset); DBG (DBG_FNC, "- Calib_AdcGain: %i\n", rst); return rst; } static SANE_Int GainOffset_Save (struct st_device *dev, SANE_Int * offset, SANE_Byte * gain) { SANE_Int rst = OK; DBG (DBG_FNC, "+ GainOffset_Save(*offset, *gain):\n"); /* check if chipset supports accessing eeprom */ if ((dev->chipset->capabilities & CAP_EEPROM) != 0) { if ((offset != NULL) && (gain != NULL)) { SANE_Int a, crc, value; crc = 0x5B; for (a = CL_RED; a <= CL_BLUE; a++) { value = (*gain << 9) | *offset; crc = _B0 (abs (crc - _B0 (value))); rst = RTS_EEPROM_WriteWord (dev->usb_handle, 0x70 + (a * 2), value); } if (rst == OK) rst = RTS_EEPROM_WriteByte (dev->usb_handle, 0x76, crc); } else rst = ERROR; } DBG (DBG_FNC, "- GainOffset_Save: %i\n", rst); return rst; } static SANE_Int Calib_PAGain (struct st_device *dev, struct st_calibration_config *calibcfg, SANE_Int gainmode) { SANE_Byte *Regs; struct st_scanparams *scancfg; SANE_Int channel_size; SANE_Int bytes_to_next_colour = 0; SANE_Int bytes_per_pixel = 0; SANE_Int length = 0; SANE_Byte *image; double rst; SANE_Int ret = ERROR; DBG (DBG_FNC, "+ Calib_PAGain(*calibcfg, gainmode=%i)\n", gainmode); Regs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte)); if (Regs != NULL) { scancfg = (struct st_scanparams *) malloc (sizeof (struct st_scanparams)); if (scancfg != NULL) { memcpy (Regs, &calibdata->Regs, RT_BUFFER_LEN * sizeof (SANE_Byte)); memcpy (scancfg, &calibdata->scancfg, sizeof (struct st_scanparams)); if (scan.scantype == ST_NORMAL) { /* bfa5 */ scancfg->coord.left = scan.ser; scancfg->coord.width = (scancfg->sensorresolution * 17) / 2; } else { scancfg->coord.left = scan.ser + v0750; scancfg->coord.width = (scancfg->sensorresolution * 3) / 2; } /* bfca */ if ((scancfg->coord.width & 1) == 1) scancfg->coord.width++; scancfg->coord.top = 1; scancfg->coord.height = calibcfg->OffsetHeight; channel_size = (scancfg->depth > 8) ? 2 : 1; switch (scancfg->colormode) { case CM_GRAY: case CM_LINEART: bytes_to_next_colour = 0; bytes_per_pixel = 1; length = channel_size * scancfg->coord.width; break; default: /* CM_COLOR */ /* c027 */ bytes_to_next_colour = 1; length = (channel_size * scancfg->coord.width) * 3; if (scancfg->samplerate == LINE_RATE) { bytes_to_next_colour = scancfg->coord.width; bytes_per_pixel = 1; } else bytes_per_pixel = 3; break; } /* c070 */ scancfg->v157c = length; image = (SANE_Byte *) malloc ((scancfg->coord.height * length) * sizeof (SANE_Byte)); if (image != NULL) { ret = RTS_GetImage (dev, Regs, scancfg, &calibdata->gain_offset, image, 0, OP_STATIC_HEAD, gainmode); if (ret == OK) { /* 429c105 */ SANE_Int a; SANE_Byte *ptr[3]; SANE_Int vmin[3] = { 255, 255, 255 }; /* f16c|f16e|f170 */ SANE_Int vmax[3] = { 0, 0, 0 }; /* f164|f166|f168 */ SANE_Int total[3]; ptr[CL_RED] = image; ptr[CL_GREEN] = image + bytes_to_next_colour; ptr[CL_BLUE] = image + (bytes_to_next_colour * 2); if (scancfg->coord.width > 0) { SANE_Int pos, b; for (pos = 0; pos < scancfg->coord.width; pos++) { total[CL_BLUE] = 0; total[CL_GREEN] = 0; total[CL_RED] = 0; for (a = 0; a < scancfg->coord.height; a++) { for (b = CL_RED; b <= CL_BLUE; b++) total[b] += *(ptr[b] + ((scancfg->coord.height - a) * length)); } /* c1a5 */ for (a = CL_RED; a <= CL_BLUE; a++) { total[a] /= scancfg->coord.height; vmin[a] = min (vmin[a], total[a]); vmax[a] = max (vmax[a], total[a]); ptr[a] += bytes_per_pixel; } } } /* 429c234 */ for (a = CL_RED; a <= CL_BLUE; a++) { rst = (calibcfg->WRef[a] * calibcfg->GainTargetFactor) / vmax[a]; if (rst <= 1.5) { if (rst <= 1.286) { if (rst <= 1.125) calibdata->gain_offset.pag[a] = 0; else calibdata->gain_offset.pag[a] = 1; } else calibdata->gain_offset.pag[a] = 2; } else calibdata->gain_offset.pag[a] = 3; } } free (image); } free (scancfg); } free (Regs); } DBG (DBG_FNC, "- Calib_PAGain: %i\n", ret); return ret; } static SANE_Int Chipset_ID (struct st_device *dev) { SANE_Int ret; if (Read_Word (dev->usb_handle, 0xfe3c, &ret) == OK) ret = _B0 (ret); else ret = 0; DBG (DBG_FNC, "> Chipset_ID(): %i\n", ret); return ret; } static SANE_Int Chipset_Name (struct st_device *dev, char *name, SANE_Int size) { SANE_Int rst = ERROR; if (name != NULL) { strncpy (name, dev->chipset->name, size); rst = OK; } return rst; } static SANE_Int Refs_Load (struct st_device *dev, SANE_Int * x, SANE_Int * y) { SANE_Int ret = OK; DBG (DBG_FNC, "+ Refs_Load:\n"); *y = *x = 0; /* check if chipset supports accessing eeprom */ if ((dev->chipset->capabilities & CAP_EEPROM) != 0) { SANE_Int data; ret = ERROR; if (RTS_EEPROM_ReadWord (dev->usb_handle, 0x6a, &data) == OK) { *x = data; if (RTS_EEPROM_ReadWord (dev->usb_handle, 0x6c, &data) == OK) { *y = data; if (RTS_EEPROM_ReadWord (dev->usb_handle, 0x6e, &data) == OK) { if ((_B0 (*y + *x + data)) == 0x5a) ret = OK; } } } } DBG (DBG_FNC, "- Refs_Load(y=%i, x=%i) : %i\n", *y, *x, ret); return ret; } static SANE_Int Refs_Save (struct st_device *dev, SANE_Int left_leading, SANE_Int start_pos) { SANE_Int ret = OK; DBG (DBG_FNC, "+ Refs_Save(left_leading=%i, start_pos=%i)\n", left_leading, start_pos); /* check if chipset supports accessing eeprom */ if ((dev->chipset->capabilities & CAP_EEPROM) != 0) { ret = ERROR; if (RTS_EEPROM_WriteWord (dev->usb_handle, 0x6a, left_leading) == OK) { if (RTS_EEPROM_WriteWord (dev->usb_handle, 0x6c, start_pos) == OK) { SANE_Byte data = _B0 (0x5a - (start_pos + left_leading)); ret = RTS_EEPROM_WriteByte (dev->usb_handle, 0x6e, data); } } } DBG (DBG_FNC, "- Refs_Save: %i\n", ret); return ret; } static SANE_Int Calib_AdcOffsetRT (struct st_device *dev, struct st_calibration_config *calibcfg, SANE_Int value) { /* 05EFF8E4 04F10738 |Arg1 = 04F10738 05EFF8E8 05EFF90C |Arg2 = 05EFF90C calibcfg 05EFF8EC 00000001 \Arg3 = 00000001 value */ SANE_Byte Regs[RT_BUFFER_LEN]; /*f1c4 */ SANE_Int channels_per_dot; /*f108 */ struct st_scanparams scancfg; /*f18c */ SANE_Int *pedcg; /*f114 */ SANE_Int *podcg; /*f118 */ SANE_Int *poffseteven; /*f130 */ SANE_Int *poffsetodd; /*f128 */ SANE_Int channel; SANE_Int avgtarget[3]; /*f1b8 f1bc f1c0 */ SANE_Byte *scanbuffer; /*f0f8 */ SANE_Int scan_options; /*f12c */ SANE_Int highresolution; /*f144 */ double dbValues[3] = { 0, 0, 0 }; /*f148 f14c f150 */ SANE_Int do_loop; /*f0ec */ SANE_Int gainmode; SANE_Byte *ptr; /*f0f4 */ SANE_Byte *mvgag; /*f0e4 *//*f10c */ USHORT wvalues[9]; /*0856 0858 085a 085c 085e 0860 0862 0864 0866 */ SANE_Int imgcount = 0; /* myoffsetnpixel = f120 */ /* desp f0e8 & f140 */ DBG (DBG_FNC, "+ Calib_AdcOffsetRT(*calibcfg, value=%i)\n", value); memcpy (&Regs, &calibdata->Regs, RT_BUFFER_LEN * sizeof (SANE_Byte)); memcpy (&scancfg, &calibdata->scancfg, sizeof (struct st_scanparams)); channels_per_dot = (calibdata->scancfg.colormode == CM_COLOR) ? 3 : 1; if (value != 0) { pedcg = &calibdata->gain_offset.edcg1[CL_RED]; podcg = &calibdata->gain_offset.odcg1[CL_RED]; poffseteven = &calibcfg->OffsetEven1[CL_RED]; poffsetodd = &calibcfg->OffsetOdd1[CL_RED]; } else { /*c37c */ pedcg = &calibdata->gain_offset.edcg2[CL_RED]; podcg = &calibdata->gain_offset.odcg2[CL_RED]; poffseteven = &calibcfg->OffsetEven2[CL_RED]; poffsetodd = &calibcfg->OffsetOdd2[CL_RED]; } /*c3a4 */ scancfg.coord.left = calibcfg->OffsetPixelStart; if (channels_per_dot > 0) { for (channel = 0; channel < channels_per_dot; channel++) { avgtarget[channel] = calibcfg->OffsetAvgTarget[channel] << 8; if (avgtarget[channel] == 0) avgtarget[channel] = 0x80; } } /* set image coordinates to scan */ scancfg.coord.width = calibcfg->OffsetNPixel; if ((scancfg.coord.width & 1) == 0) scancfg.coord.width++; scancfg.bytesperline = channels_per_dot * scancfg.coord.width; scancfg.coord.top = 1; scancfg.coord.height = calibcfg->OffsetHeight; scancfg.depth = 8; /* allocate memory to store image */ scanbuffer = (SANE_Byte *) malloc ((scancfg.bytesperline * calibcfg->OffsetHeight) * sizeof (SANE_Byte)); if (scanbuffer == NULL) return ERROR; /*42ac477 */ scan_options = (linedarlampoff == 1) ? 1 : 0x101; highresolution = (scancfg.sensorresolution >= 1200) ? TRUE : FALSE; do { if (channels_per_dot > 0) { for (channel = 0; channel < channels_per_dot; channel++) dbValues[channel] = (40 / (44 - calibdata->gain_offset.vgag2[channel])) * (40 / (44 - calibdata-> gain_offset. vgag1 [channel])); } /*429c50f */ /* Get Image */ gainmode = Lamp_GetGainMode (dev, scancfg.resolution_x, scan.scantype); if (RTS_GetImage (dev, Regs, &scancfg, &calibdata->gain_offset, scanbuffer, 0, scan_options, gainmode) != OK) { free (scanbuffer); return ERROR; } /*429c55f */ /* Save retrieved image */ if (RTS_Debug->SaveCalibFile != FALSE) { char fname[30]; imgcount++; if (snprintf (fname, 30, "adcoffset_rt%i.tiff", imgcount) > 0) dbg_tiff_save (fname, scancfg.coord.width, scancfg.coord.height, scancfg.depth, CM_COLOR, scancfg.resolution_x, scancfg.resolution_y, scanbuffer, scancfg.bytesperline * calibcfg->OffsetHeight); } /*429c5a5 */ do_loop = FALSE; if (highresolution == TRUE) { /* f0fc = f0e4 = channel */ SANE_Int lf104; SANE_Int *mydcg; /*f0f4 */ USHORT *mywvalue; /*ebp */ SANE_Byte is_ready[6]; /*f174 f178 f17c f180 f184 f18c */ SANE_Byte *ptr_ready; /*f11c */ SANE_Int colour; for (channel = 0; channel < 6; channel++) is_ready[channel] = FALSE; if (channels_per_dot <= 0) break; ptr = scanbuffer; mvgag = (SANE_Byte *) calibdata->gain_offset.vgag1; for (channel = 0; channel < channels_per_dot; channel++) { for (lf104 = 0; lf104 < 2; lf104++) { if (lf104 == 0) { mywvalue = &wvalues[channel]; mydcg = pedcg; ptr_ready = &is_ready[0]; } else { /*1645 */ mywvalue = &wvalues[3]; mydcg = podcg; ptr_ready = &is_ready[3]; } /*1658 */ if (ptr_ready[channel] == FALSE) { colour = 0; if (lf104 < calibcfg->OffsetNPixel) { SANE_Int dot; for (dot = 0; dot < (calibcfg->OffsetNPixel - lf104 + 1) / 2; dot++) colour += scanbuffer[mvgag[(lf104 * channels_per_dot)] + (dot * (channels_per_dot * 2))]; } /*c6b2 */ colour = colour << 8; if (colour == 0) { /*c6b9 */ if (mydcg[channel] != 0x1ff) { /*c6d5 */ mydcg[channel] = 0x1ff; do_loop = TRUE; } else ptr_ready[channel] = TRUE; } else { SANE_Int myesi; SANE_Int d; /*c6e8 */ if (*mywvalue == 0) mywvalue += 2; colour /= (calibcfg->OffsetNPixel / 2); if (colour >= avgtarget[channel]) { colour -= avgtarget[channel]; myesi = 0; } else { colour = avgtarget[channel] - colour; myesi = 1; } d = mydcg[channel]; if (d < 0x100) d = 0xff - d; if (myesi != 0) { /*c76e */ if ((d + colour) > 0x1ff) { if (*mvgag > 0) { *mvgag = *mvgag - 1; do_loop = TRUE; } else ptr_ready[channel] = TRUE; /*c7a0 */ } else d += colour; } else { /*c7ad */ if (colour > d) { if (*mvgag > 0) { *mvgag = *mvgag - 1; do_loop = TRUE; } else ptr_ready[channel] = TRUE; } else d -= colour; } /*c7dd */ mydcg[channel] = (d < 0x100) ? 0x100 - d : d; } dbg_calibtable (&calibdata->gain_offset); } } /*c804 */ mvgag++; } } else { /* Low resolution */ SANE_Byte is_ready[3]; SANE_Int colour; /*429c845 */ for (channel = 0; channel < channels_per_dot; channel++) is_ready[channel] = FALSE; if (channels_per_dot <= 0) break; ptr = scanbuffer; mvgag = (SANE_Byte *) calibdata->gain_offset.vgag1; for (channel = 0; channel < channels_per_dot; channel++) { if (is_ready[channel] == FALSE) { colour = 0; if (calibcfg->OffsetNPixel > 0) { SANE_Int dot; /* Take one channel colour values from offsetnpixel pixels */ for (dot = 0; dot < calibcfg->OffsetNPixel; dot++) colour += *(ptr + (dot * channels_per_dot)); } colour <<= 8; if (colour == 0) { if (pedcg[channel] != 0x1ff) { do_loop = TRUE; podcg[channel] = 0x1ff; pedcg[channel] = 0x1ff; } else is_ready[channel] = TRUE; } else { /*c8f7 */ SANE_Int myesi; SANE_Int op1, op2, op3; /* Get colour average */ colour /= calibcfg->OffsetNPixel; /* get absolute difference with avgtarget */ myesi = (colour > avgtarget[channel]) ? 0 : 1; colour = abs (avgtarget[channel] - colour); if (scancfg.resolution_x > 600) { /*c923 */ if (wvalues[channel + 3] == 0) wvalues[channel + 3]++; if (wvalues[channel] == 0) wvalues[channel]++; op3 = max (wvalues[channel], wvalues[channel + 3]); } else { if (wvalues[channel + 6] == 0) wvalues[channel + 6]++; op3 = wvalues[channel + 6]; } /*c9d3 */ op1 = (SANE_Int) (colour / (dbValues[channel] * op3)); op2 = (pedcg[channel] < 0x100) ? pedcg[channel] - 0xff : pedcg[channel]; if (myesi != 0) { /*c9f5 */ if (((op2 + op1) & 0xffff) > 0x1ff) { if (*mvgag != 0) { do_loop = TRUE; *mvgag = *mvgag - 1; } else is_ready[channel] = TRUE; } else op2 += op1; } else { /*ca31 */ if (op1 > op2) { if (*mvgag > 0) { do_loop = TRUE; *mvgag = *mvgag - 1; } else is_ready[channel] = TRUE; } else op2 -= op1; } /*ca54 */ if (op2 < 0x100) op2 = 0x100 - op2; pedcg[channel] = op2; podcg[channel] = op2; } } /*ca6f */ ptr++; mvgag++; dbg_calibtable (&calibdata->gain_offset); } } } while (do_loop != FALSE); /*429cad1 */ for (channel = 0; channel < 3; channel++) { poffseteven[channel] = (pedcg[channel] < 0x100) ? 0xff - pedcg[channel] : pedcg[channel]; poffsetodd[channel] = (podcg[channel] < 0x100) ? 0xff - podcg[channel] : podcg[channel]; } free (scanbuffer); return OK; } static void Calib_LoadCut (struct st_device *dev, struct st_scanparams *scancfg, SANE_Int scantype, struct st_calibration_config *calibcfg) { double mylong; /*ee78 */ double mylong2; /**/ SANE_Int channel[3]; SANE_Int a; cfg_shading_cut_get (dev->sensorcfg->type, scancfg->depth, scancfg->resolution_x, scantype, &channel[0], &channel[1], &channel[2]); mylong = 1 << scancfg->depth; for (a = CL_RED; a <= CL_BLUE; a++) { mylong2 = channel[a]; calibcfg->ShadingCut[a] = (mylong * mylong2) * 0.000390625; } } static SANE_Int Calib_BWShading (struct st_calibration_config *calibcfg, struct st_calibration *myCalib, SANE_Int gainmode) { /* 0603F8E4 0603F90C |Arg1 = 0603F90C calibcfg 0603F8E8 0603FAAC |Arg2 = 0603FAAC myCalib 0603F8EC 00000001 \Arg3 = 00000001 gainmode */ /*falta codigo */ /*silence gcc */ (void) calibcfg; (void) myCalib; (void) gainmode; return OK; } static SANE_Int Calib_WhiteShading_3 (struct st_device *dev, struct st_calibration_config *calibcfg, struct st_calibration *myCalib, SANE_Int gainmode) { /* 05EDF8E0 04F00738 |Arg1 = 04F00738 05EDF8E4 05EDF90C |Arg2 = 05EDF90C calibcfg 05EDF8E8 05EDFAAC |Arg3 = 05EDFAAC myCalib 05EDF8EC 00000001 \Arg4 = 00000001 gainmode */ SANE_Byte *myRegs; /*f1bc */ struct st_scanparams scancfg; /*f170 */ SANE_Int myWidth; /*f14c */ SANE_Int lf168, bytes_per_pixel; SANE_Int bytes_per_line; /**/ SANE_Int a; double lf1a4[3]; SANE_Int otherheight; /*f150 */ SANE_Int otherheight2; SANE_Int lf12c; SANE_Int lf130; double *buffer1; /*f138 */ double *buffer2; /*f144 */ SANE_Byte *scanbuffer; /*f164 */ SANE_Byte *ptr; /*f148 */ SANE_Int position; /*f140 */ SANE_Int lf13c, myHeight; SANE_Int myESI, myEDI; SANE_Int channel; /*f134 */ double myst; double sumatorio; SANE_Int rst; DBG (DBG_FNC, "> Calib_WhiteShading3(*calibcfg, *myCalib, gainmode=%i)\n", gainmode); myRegs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte)); memcpy (myRegs, &calibdata->Regs, RT_BUFFER_LEN * sizeof (SANE_Byte)); memcpy (&scancfg, &calibdata->scancfg, sizeof (struct st_scanparams)); Lamp_SetGainMode (dev, myRegs, scancfg.resolution_x, gainmode); rst = OK; scancfg.resolution_y = 200; switch (scan.scantype) { case ST_NORMAL: /*a184 */ scancfg.coord.left += scan.ser; scancfg.coord.width &= 0xffff; break; case ST_TA: case ST_NEG: scancfg.coord.left += scan.ser; break; } /*a11b */ if ((scancfg.coord.width & 1) != 0) scancfg.coord.width++; scancfg.coord.top = 1; scancfg.coord.height = calibcfg->WShadingHeight; switch (scancfg.colormode) { case CM_GRAY: case CM_LINEART: myWidth = scancfg.coord.width; lf168 = 0; bytes_per_line = ((scancfg.depth + 7) / 8) * myWidth; bytes_per_pixel = 1; break; default: /* CM_COLOR */ myWidth = scancfg.coord.width * 3; bytes_per_line = ((scancfg.depth + 7) / 8) * myWidth; lf168 = (scancfg.samplerate == LINE_RATE) ? scancfg.coord.width : 1; bytes_per_pixel = (scancfg.samplerate == PIXEL_RATE) ? 3 : 1; break; } /*a1e8 */ scancfg.v157c = bytes_per_line; scancfg.bytesperline = bytes_per_line; for (a = 0; a < 3; a++) lf1a4[a] = (calibcfg->WRef[a] * (1 << scancfg.depth)) >> 8; /* debug this code because if it's correct, lf130 and lf12c are always 2 */ otherheight = calibcfg->WShadingHeight - 3; otherheight -= (otherheight - 4); otherheight2 = otherheight / 2; otherheight -= otherheight2; lf130 = otherheight2; lf12c = otherheight; buffer1 = (double *) malloc (otherheight * sizeof (double)); if (buffer1 == NULL) return ERROR; buffer2 = (double *) malloc (otherheight * sizeof (double)); if (buffer2 == NULL) { free (buffer1); return ERROR; } scanbuffer = (SANE_Byte *) malloc (((scancfg.coord.height + 16) * bytes_per_line) * sizeof (SANE_Byte)); if (scanbuffer == NULL) { free (buffer1); free (buffer2); return ERROR; } /* Scan image */ myCalib->shading_enabled = FALSE; rst = RTS_GetImage (dev, myRegs, &scancfg, &calibdata->gain_offset, scanbuffer, myCalib, 0x20000080, gainmode); for (a = 0; a < 3; a++) myCalib->WRef[a] *= ((1 << scancfg.depth) >> 8); if (rst == ERROR) { free (buffer1); free (buffer2); free (scanbuffer); return ERROR; } if (scancfg.depth > 8) { /*a6d9 */ position = 0; sumatorio = 0; if (myWidth > 0) { do { switch (scancfg.colormode) { case CM_GRAY: case CM_LINEART: channel = 0; lf13c = position; break; default: /*CM_COLOR */ if (scancfg.samplerate == PIXEL_RATE) { /* pixel rate */ channel = position % bytes_per_pixel; lf13c = position / bytes_per_pixel; } else { /* line rate */ channel = position / lf168; lf13c = position % lf168; } break; } /*a743 */ if (lf130 > 0) memset (buffer1, 0, lf130 * sizeof (double)); /*a761 */ if (lf12c > 0) { for (a = 0; a < lf12c; a++) buffer2[a] = (1 << scancfg.depth) - 1.0; } /*a78f */ myESI = 0; myEDI = 0; ptr = scanbuffer + (position * 2); myHeight = 0; if (otherheight > 0) { do { myst = 0; for (a = 0; a < 4; a++) myst += data_lsb_get (ptr + (a * (myWidth * 2)), 2); myEDI = 0; myst = myst * 0.25; if (myHeight < (otherheight - 4)) { if (myst < buffer2[myESI]) { buffer2[myESI] = myst; if (lf12c > 0) { for (a = 0; a < lf12c; a++) if (buffer2[myESI] < buffer2[a]) myESI = a; } } /*a820 */ if (myst >= buffer1[myEDI]) { buffer1[myEDI] = myst; if (lf130 > 0) { for (a = 0; a < lf130; a++) if (buffer1[myEDI] >= buffer1[a]) myEDI = a; } } sumatorio += myst; } else { /*a853 */ if (myHeight == (otherheight - 4)) { if (lf12c > 0) { for (a = 0; a < lf12c; a++) if (buffer2[myESI] >= buffer2[a]) myESI = a; } if (lf130 > 0) { for (a = 0; a < lf130; a++) if (buffer1[myEDI] < buffer1[a]) myEDI = a; } } /*a895 */ if (myst >= buffer2[myESI]) { /*a89c */ sumatorio -= buffer2[myESI]; sumatorio += myst; buffer2[myESI] = myst; if (lf12c > 0) { for (a = 0; a < lf12c; a++) if (buffer2[myESI] >= buffer2[a]) myESI = a; } } else { if (myst < buffer1[myEDI]) { sumatorio -= buffer1[myEDI]; sumatorio += myst; buffer1[myEDI] = myst; if (lf130 > 0) { for (a = 0; a < lf130; a++) if (buffer1[myEDI] < buffer1[a]) myEDI = a; } } } } /*a901 */ ptr += (myWidth * 2); myHeight++; } while (myHeight < otherheight); } /*a924 */ scancfg.ser = 0; scancfg.startpos = otherheight - 4; sumatorio = sumatorio / scancfg.startpos; if (myCalib->shading_enabled != FALSE) { /*a94a */ myCalib->white_shading[channel][lf13c] = (unsigned short) sumatorio; } else { /*a967 */ if ((scancfg.colormode != CM_GRAY) && (scancfg.colormode != CM_LINEART)) sumatorio /= lf1a4[channel]; else sumatorio /= lf1a4[scancfg.channel]; sumatorio = min (sumatorio * 0x4000, 65535); if (myRegs[0x1bf] != 0x18) myCalib->black_shading[channel][lf13c] |= (0x140 - ((((myRegs[0x1bf] >> 3) & 3) * 3) << 6)) & ((int) sumatorio); else myCalib->white_shading[channel][lf13c] = (unsigned short) sumatorio; } /*a9fd */ position++; } while (position < myWidth); } } else { /*a6d9 */ position = 0; sumatorio = 0; if (myWidth > 0) { do { switch (scancfg.colormode) { case CM_GRAY: case CM_LINEART: channel = 0; lf13c = position; break; default: /*CM_COLOR */ if (scancfg.samplerate == PIXEL_RATE) { channel = position % bytes_per_pixel; lf13c = position / bytes_per_pixel; } else { channel = position / lf168; lf13c = position % lf168; } break; } /*a743 */ if (lf130 > 0) memset (buffer1, 0, lf130 * sizeof (double)); /*a761 */ if (lf12c > 0) { for (a = 0; a < lf12c; a++) buffer2[a] = (1 << scancfg.depth) - 1.0; } /*a78f */ myESI = 0; myEDI = 0; ptr = scanbuffer + position; myHeight = 0; if (otherheight > 0) { do { myst = 0; for (a = 0; a < 4; a++) myst += *(ptr + (a * myWidth)); myEDI = 0; myst *= 0.25; if (myHeight < (otherheight - 4)) { if (myst < buffer2[myESI]) { buffer2[myESI] = myst; if (lf12c > 0) { for (a = 0; a < lf12c; a++) if (buffer2[myESI] < buffer2[a]) myESI = a; } } /*a820 */ if (myst >= buffer1[myEDI]) { buffer1[myEDI] = myst; if (lf130 > 0) { for (a = 0; a < lf130; a++) if (buffer1[myEDI] >= buffer1[a]) myEDI = a; } } sumatorio += myst; } else { /*a853 */ if (myHeight == (otherheight - 4)) { if (lf12c > 0) { for (a = 0; a < lf12c; a++) if (buffer2[myESI] >= buffer2[a]) myESI = a; } if (lf130 > 0) { for (a = 0; a < lf130; a++) if (buffer1[myEDI] < buffer1[a]) myEDI = a; } } /*a895 */ if (myst >= buffer2[myESI]) { /*a89c */ sumatorio -= buffer2[myESI]; sumatorio += myst; buffer2[myESI] = myst; if (lf12c > 0) { for (a = 0; a < lf12c; a++) if (buffer2[myESI] >= buffer2[a]) myESI = a; } } else { if (myst < buffer1[myEDI]) { sumatorio -= buffer1[myEDI]; sumatorio += myst; buffer1[myEDI] = myst; if (lf130 > 0) { for (a = 0; a < lf130; a++) if (buffer1[myEDI] < buffer1[a]) myEDI = a; } } } } /*a901 */ ptr += myWidth; myHeight++; } while (myHeight < otherheight); } /*a924 */ scancfg.ser = 0; scancfg.startpos = otherheight - 4; sumatorio /= scancfg.startpos; if (myCalib->shading_enabled != FALSE) { /*a94a */ myCalib->white_shading[channel][lf13c] = (unsigned short) sumatorio; } else { /*a967 */ if ((scancfg.colormode != CM_GRAY) && (scancfg.colormode != CM_LINEART)) sumatorio /= lf1a4[channel]; else sumatorio /= lf1a4[scancfg.channel]; sumatorio = min (sumatorio * 0x4000, 65535); if (myRegs[0x1bf] != 0x18) myCalib->black_shading[channel][lf13c] |= (0x140 - ((((myRegs[0x1bf] >> 3) & 0x03) * 3) << 6)) & ((int) sumatorio); else myCalib->white_shading[channel][lf13c] = (unsigned short) sumatorio; } /*a9fd */ position++; } while (position < myWidth); } } /*aa12 */ if (RTS_Debug->SaveCalibFile != FALSE) { dbg_tiff_save ("whiteshading3.tiff", scancfg.coord.width, scancfg.coord.height, scancfg.depth, CM_COLOR, scancfg.resolution_x, scancfg.resolution_y, scanbuffer, (scancfg.coord.height + 16) * bytes_per_line); } free (buffer1); free (buffer2); free (scanbuffer); return OK; } static SANE_Int Calib_BlackShading (struct st_device *dev, struct st_calibration_config *calibcfg, struct st_calibration *myCalib, SANE_Int gainmode) { /* gainmode f8ec myCalib f8e8 calibcfg f8e4 */ SANE_Byte *myRegs; /*f1bc */ struct st_scanparams scancfg; /*f020 */ double shadingprediff[6]; /*f08c f094 f09c f0a4 f0ac f0b4 */ double mylong; /*f018 */ double maxvalue; /*eff8 */ double sumatorio = 0.0; double myst; SANE_Int rst; SANE_Int a; SANE_Int mheight; /*efe0 */ SANE_Int current_line; /*efe4 */ SANE_Int bytes_per_line; /*efd8 */ SANE_Int position; /*lefcc */ SANE_Int leff0, lf010, lefd0; SANE_Byte *buffer; /*efd4 */ SANE_Byte buff2[256]; /*F0BC */ SANE_Int buff3[0x8000]; SANE_Byte *ptr; /*f008 */ SANE_Int my14b4; /*f008 pisa ptr */ SANE_Int biggest; /*bx */ SANE_Int lowest; /*dx */ SANE_Int lefdc; SANE_Int channel; SANE_Int smvalues[3]; /*f04c f04e f050 */ double dbvalue[6]; /*lf05c lf060, lf064 lf068, lf06c lf070, lf074 lf078, lf07c lf080, lf084 lf088 */ DBG (DBG_FNC, "> Calib_BlackShading(*calibcfg, *myCalib, gainmode=%i)\n", gainmode); rst = OK; myRegs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte)); memcpy (myRegs, &calibdata->Regs, RT_BUFFER_LEN * sizeof (SANE_Byte)); memcpy (&scancfg, &calibdata->scancfg, sizeof (struct st_scanparams)); Lamp_SetGainMode (dev, myRegs, scancfg.resolution_x, gainmode); for (a = CL_RED; a <= CL_BLUE; a++) shadingprediff[a + 3] = calibcfg->BShadingPreDiff[a]; if ((scan.scantype >= ST_NORMAL) && (scan.scantype <= ST_NEG)) scancfg.coord.left += scan.ser; if ((scancfg.coord.width & 1) != 0) scancfg.coord.width++; scancfg.coord.top = 1; scancfg.depth = 8; scancfg.coord.height = calibcfg->BShadingHeight; if (scancfg.colormode != CM_COLOR) { bytes_per_line = scancfg.coord.width; leff0 = 0; lf010 = 1; } else { /*876c */ bytes_per_line = scancfg.coord.width * 3; if (scancfg.samplerate == LINE_RATE) { leff0 = scancfg.coord.width; lf010 = 1; } else { leff0 = 1; lf010 = 3; } } scancfg.v157c = bytes_per_line; scancfg.bytesperline = bytes_per_line; mylong = 1 << (16 - scancfg.depth); if ((myRegs[0x1bf] & 0x18) != 0) mylong /= 1 << (((myRegs[0x1bf] >> 5) & 3) + 4); lefd0 = ((((myRegs[0x1bf] >> 3) & 2) << 8) - ((((myRegs[0x1bf] >> 3) & 1) * 3) << 6)) - 1; if (scancfg.depth >= 8) maxvalue = ((1 << (scancfg.depth - 8)) << 8) - 1; else maxvalue = (256 / (1 << (8 - scancfg.depth))) - 1; Calib_LoadCut (dev, &scancfg, scan.scantype, calibcfg); for (a = CL_RED; a <= CL_BLUE; a++) shadingprediff[a] = calibcfg->ShadingCut[a]; if (calibcfg->BShadingOn == -1) { SANE_Int b, d; double e; for (a = CL_RED; a <= CL_BLUE; a++) { myst = max (shadingprediff[a], 0); myst = (maxvalue >= myst) ? shadingprediff[a] : maxvalue; shadingprediff[a] = max (myst, 0); } b = 0; while (b < bytes_per_line) { if (scancfg.colormode != CM_COLOR) { channel = 0; d = b; } else { if (scancfg.samplerate == PIXEL_RATE) { channel = (b % lf010) & 0xffff; d = (b / lf010) & 0xffff; } else { channel = (b / leff0) & 0xffff; d = (b % leff0) & 0xffff; } } /*89d0 */ e = min (lefd0, mylong * shadingprediff[channel]); myCalib->black_shading[channel][d] |= (unsigned short) e & 0xffff; b++; } return OK; } /* Allocate buffer to read image */ mheight = scancfg.coord.height; buffer = (SANE_Byte *) malloc (((scancfg.coord.height + 16) * bytes_per_line) * sizeof (SANE_Byte)); if (buffer == NULL) return ERROR; /* Turn off lamp */ Lamp_Status_Set (dev, NULL, FALSE, FLB_LAMP); usleep (200 * 1000); /* Scan image */ myCalib->shading_enabled = FALSE; rst = RTS_GetImage (dev, myRegs, &scancfg, &calibdata->gain_offset, buffer, myCalib, 0x101, gainmode); if (rst == ERROR) { /*8ac2 */ free (buffer); memcpy (&calibdata->Regs, myRegs, RT_BUFFER_LEN * sizeof (SANE_Byte)); free (myRegs); return ERROR; } /* myRegs isn't going to be used anymore */ free (myRegs); myRegs = NULL; /* Turn on lamp again */ if (scan.scantype != ST_NORMAL) { Lamp_Status_Set (dev, NULL, FALSE, TMA_LAMP); usleep (1000 * 1000); } else Lamp_Status_Set (dev, NULL, TRUE, FLB_LAMP); /* Save buffer */ if (RTS_Debug->SaveCalibFile != FALSE) { dbg_tiff_save ("blackshading.tiff", scancfg.coord.width, scancfg.coord.height, scancfg.depth, CM_COLOR, scancfg.resolution_x, scancfg.resolution_y, buffer, (scancfg.coord.height + 16) * bytes_per_line); } if (scancfg.depth > 8) { /*8bb2 */ memset (&dbvalue, 0, 6 * sizeof (double)); position = 0; if (bytes_per_line > 0) { do { memset (&buff3, 0, 0x8000 * sizeof (SANE_Int)); sumatorio = 0; ptr = buffer + position; current_line = 0; biggest = 0; lowest = (int) maxvalue; /* Toma los valores de una columna */ if (mheight > 0) { SANE_Int value; do { value = data_lsb_get (ptr, 2); biggest = max (biggest, value); if (current_line < mheight) { sumatorio += value; lowest = min (lowest, value); biggest = max (biggest, value); buff3[value]++; } else { /*8cab */ if (value > lowest) { buff3[lowest]--; buff3[value]++; sumatorio += value; sumatorio -= lowest; if (buff3[lowest] != 0) { do { lowest++; } while (buff3[lowest] == 0); } } } /*8d0b */ ptr += (bytes_per_line * 2); current_line++; } while (current_line < mheight); } /*8d27 */ sumatorio /= mheight; if (scancfg.colormode != CM_COLOR) { channel = 0; lefdc = position; } else { /*8d5f */ if (scancfg.samplerate == PIXEL_RATE) { channel = position % lf010; lefdc = (position / lf010) & 0xffff; } else { channel = position / leff0; lefdc = position % leff0; } } dbvalue[channel] += sumatorio; if ((scancfg.colormode == CM_GRAY) || (scancfg.colormode == CM_LINEART)) sumatorio += shadingprediff[scancfg.channel]; else sumatorio += shadingprediff[channel]; myst = min (max (0, sumatorio), maxvalue); dbvalue[channel + 3] = myst; if ((calibcfg->BShadingOn == 1) || (calibcfg->BShadingOn == 2)) { if (calibcfg->BShadingOn == 2) { myst -= calibcfg->BRef[channel] * (1 << (scancfg.depth - 8)); myst = max (myst, 0); } /*8e6d */ myst *= mylong; myCalib->black_shading[channel][lefdc] = min (myst, lefd0); } position++; } while (position < bytes_per_line); } } else { /*8eb6 */ memset (&dbvalue, 0, 6 * sizeof (double)); position = 0; if (bytes_per_line > 0) { do { memset (&buff2, 0, 256 * sizeof (SANE_Byte)); sumatorio = 0; /* ptr points to the next position of the first line */ ptr = buffer + position; biggest = 0; lowest = (int) maxvalue; current_line = 0; /* Toma los valores de una columna */ if (mheight > 0) { my14b4 = v14b4; do { biggest = max (biggest, *ptr); if (my14b4 == 0) { /*8fd7 */ if (current_line < mheight) { sumatorio += *ptr; lowest = min (lowest, *ptr); biggest = max (biggest, *ptr); buff2[*ptr]++; } else { /*9011 */ if (*ptr > lowest) { buff2[lowest]--; buff2[*ptr]++; sumatorio += *ptr; sumatorio -= lowest; if (buff2[lowest] != 0) { do { lowest++; } while (buff2[lowest] == 0); } } } } else sumatorio += *ptr; /*9067 */ /* Point to the next pixel under current line */ ptr += bytes_per_line; current_line++; } while (current_line < mheight); } /*908a */ /* Calculates average of each column */ sumatorio = sumatorio / mheight; if (scancfg.colormode != CM_COLOR) { channel = 0; lefdc = position; } else { /*90c5 */ if (scancfg.samplerate == PIXEL_RATE) { channel = position % lf010; lefdc = (position / lf010) & 0xffff; } else { /*90fb */ channel = position / leff0; lefdc = position % leff0; } } /*911f */ dbvalue[channel] += sumatorio; if ((scancfg.colormode == CM_GRAY) || (scancfg.colormode == CM_LINEART)) sumatorio += shadingprediff[scancfg.channel]; else sumatorio += shadingprediff[channel]; /*9151 */ myst = min (max (0, sumatorio), maxvalue); /*9198 */ if (position >= 3) { double myst2; myst -= dbvalue[channel + 3]; myst2 = myst; myst = min (myst, shadingprediff[channel + 3]); my14b4 = -shadingprediff[channel + 3]; if (myst >= my14b4) myst = min (myst2, shadingprediff[channel + 3]); else myst = my14b4; myst += dbvalue[channel + 3]; } /*9203 */ dbvalue[channel + 3] = myst; switch (calibcfg->BShadingOn) { case 1: myCalib->black_shading[channel][lefdc] |= (unsigned short) (((int) myst & 0xff) << 8) & 0xffff; break; case 2: /*9268 */ my14b4 = calibcfg->BRef[channel] / (1 << (8 - scancfg.depth)); myst -= my14b4; myst = max (myst, 0); myst *= mylong; myCalib->black_shading[channel][lefdc] = min (myst, lefd0); break; } /*92d8 */ position++; } while (position < bytes_per_line); } } /*9306 */ if (calibcfg->BShadingOn == -2) { for (a = 0; a < 3; a++) { dbvalue[a] = (dbvalue[a] / scancfg.coord.width) + calibcfg->ShadingCut[a]; if (dbvalue[a] < 0) dbvalue[a] = 0; smvalues[a] = min ((int) (dbvalue[a] + 0.5) & 0xffff, maxvalue); } if (scancfg.coord.width > 0) { SANE_Int b, c; for (c = 0; c < scancfg.coord.width; c++) for (b = 0; b < 3; b++) myCalib->black_shading[b][c] |= (unsigned short) min (smvalues[b] * mylong, lefd0); } } /*9425 */ free (buffer); return OK; } static SANE_Int Calibration (struct st_device *dev, SANE_Byte * Regs, struct st_scanparams *scancfg, struct st_calibration *myCalib, SANE_Int value) { /*//SANE_Int Calibration([fa20]char *Regs, [fa24]struct st_scanparams *scancfg, [fa28]struct st_calibration myCalib, [fa2c]SANE_Int value) */ struct st_calibration_config calibcfg; /* f90c */ SANE_Int a; SANE_Byte gainmode; SANE_Int lf900; DBG (DBG_FNC, "> Calibration\n"); dbg_ScanParams (scancfg); (void) value; /*silence gcc */ memcpy (&calibdata->Regs, Regs, sizeof (SANE_Byte) * RT_BUFFER_LEN); /*4213be8 */ memset (&calibcfg, 0x30, sizeof (struct st_calibration_config)); Calib_LoadConfig (dev, &calibcfg, scan.scantype, scancfg->resolution_x, scancfg->depth); memset (&calibdata->gain_offset, 0, sizeof (struct st_gain_offset)); /*[42b3654] */ for (a = CL_RED; a <= CL_BLUE; a++) { myCalib->WRef[a] = calibcfg.WRef[a]; calibdata->gain_offset.edcg1[a] = 256; calibdata->gain_offset.odcg1[a] = 256; calibdata->gain_offset.vgag1[a] = 4; calibdata->gain_offset.vgag2[a] = 4; /*3654|3656|3658 365a|365c|365e 3660|3662|3664 3666|3668|366a 366c|366d|366e 366f|3670|3671 3672|3673|3674 */ } memcpy (&calibdata->scancfg, scancfg, sizeof (struct st_scanparams)); gainmode = Lamp_GetGainMode (dev, scancfg->resolution_x, scan.scantype); /* [lf904] = 1 */ /* 3cf3 */ myCalib->first_position = 1; myCalib->shading_type = 0; if (calibdata->scancfg.colormode == CM_LINEART) { calibdata->scancfg.colormode = CM_GRAY; calibcfg.GainTargetFactor = 1.3; } lf900 = OK; if (calibcfg.CalibPAGOn != 0) { if (Calib_PAGain (dev, &calibcfg, gainmode) != 0) lf900 = ERROR; /*ERROR*/} else { /*3da7 */ if ((calibdata->scancfg.colormode != CM_GRAY) && (calibdata->scancfg.colormode != CM_LINEART)) { for (a = CL_RED; a <= CL_BLUE; a++) calibdata->gain_offset.pag[a] = calibcfg.PAG[a]; } else { /* 3dd3 */ /* Default PAGain */ if (calibdata->scancfg.channel > 2) calibdata->scancfg.channel = 0; for (a = CL_RED; a <= CL_BLUE; a++) calibdata->gain_offset.pag[a] = calibcfg.PAG[calibdata->scancfg.channel]; } } /* 3e01 */ if (calibcfg.CalibOffset10n != 0) /*==2*/ { /*v14b4=1 offset[CL_RED]=0x174 offset[CL_GREEN]=0x16d offset[CL_BLUE]=0x160 */ if ((v14b4 != 0) && (offset[CL_RED] != 0) && (offset[CL_GREEN] != 0) && (offset[CL_BLUE] != 0)) { for (a = CL_RED; a <= CL_BLUE; a++) { calibdata->gain_offset.edcg1[a] = offset[a]; calibdata->gain_offset.odcg1[a] = offset[a]; } } else { /* 3e84 */ if ((calibcfg.CalibOffset10n > 0) && (calibcfg.CalibOffset10n < 4)) { /*if (calibcfg.CalibOffset10n != 0) */ if (calibcfg.CalibOffset10n == 3) { lf900 = Calib_AdcOffsetRT (dev, &calibcfg, 1); } else { /* 3eb2 */ /*falta codigo */ } } } } else { /* 3faf */ for (a = CL_RED; a <= CL_BLUE; a++) { calibdata->gain_offset.edcg1[a] = abs (calibcfg.OffsetEven1[a] - 0x100); calibdata->gain_offset.odcg1[a] = abs (calibcfg.OffsetOdd1[a] - 0x100); } } /* 3f13 3f0b */ if ((gainmode != 0) && (calibcfg.CalibGain10n != 0)) { /*gain[CL_RED]=0x17 gain[CL_GREEN]=0x12 gain[CL_BLUE]=0x17 */ if ((v14b4 != 0) && (gain[CL_RED] != 0) && (gain[CL_GREEN] != 0) && (gain[CL_BLUE] != 0)) { for (a = CL_RED; a <= CL_BLUE; a++) calibdata->gain_offset.vgag1[a] = gain[a]; } else { /*4025 */ lf900 = Calib_AdcGain (dev, &calibcfg, 1, gainmode); if ((v14b4 != 0) && (lf900 == OK)) GainOffset_Save (dev, &calibdata->gain_offset.edcg1[0], &calibdata->gain_offset.vgag1[0]); } } else { /*4089 */ for (a = CL_RED; a <= CL_BLUE; a++) calibdata->gain_offset.vgag1[a] = calibcfg.Gain1[a]; } /*40a5 */ if ((gainmode != 0) && (calibcfg.CalibOffset20n != 0)) { switch (calibcfg.CalibOffset20n) { case 3: lf900 = Calib_AdcOffsetRT (dev, &calibcfg, 2); break; } /*4140 */ /*falta codigo */ } else { /*4162 */ for (a = CL_RED; a <= CL_BLUE; a++) { calibdata->gain_offset.edcg2[a] = abs (calibcfg.OffsetEven2[a] - 0x40); calibdata->gain_offset.odcg2[a] = abs (calibcfg.OffsetOdd2[a] - 0x40); } } /*41d6 */ if ((gainmode != 0) && (calibcfg.CalibGain20n != 0)) { lf900 = Calib_AdcGain (dev, &calibcfg, 0, gainmode); } else { /*423c */ for (a = CL_RED; a <= CL_BLUE; a++) calibdata->gain_offset.vgag2[a] = calibcfg.Gain2[a]; } /*4258 */ if (calibcfg.TotShading != 0) { lf900 = Calib_BWShading (&calibcfg, myCalib, gainmode); /*falta codigo */ } else { /*428f */ if (gainmode != 0) { if (calibcfg.BShadingOn != 0) lf900 = Calib_BlackShading (dev, &calibcfg, myCalib, gainmode); /*42fd */ if ((lf900 != ERROR) && (calibcfg.WShadingOn != 0)) { switch (calibcfg.WShadingOn) { default: break; case 3: lf900 = Calib_WhiteShading_3 (dev, &calibcfg, myCalib, gainmode); break; case 2: break; } } else myCalib->shading_enabled = FALSE; } else myCalib->shading_enabled = FALSE; } /*43ca */ memcpy (&myCalib->gain_offset, &calibdata->gain_offset, sizeof (struct st_gain_offset)); memcpy (&mitabla2, &calibdata->gain_offset, sizeof (struct st_gain_offset)); /*4424 */ /* Park home after calibration */ if (get_value (SCANINFO, PARKHOMEAFTERCALIB, TRUE, FITCALIBRATE) == FALSE) scan.ler -= calibcfg.WShadingHeight; else Head_ParkHome (dev, TRUE, dev->motorcfg->parkhomemotormove); return OK; } /*static void show_diff(struct st_device *dev, SANE_Byte *original) { SANE_Byte *buffer = (SANE_Byte *)malloc(RT_BUFFER_LEN * sizeof(SANE_Byte)); SANE_Int a; if ((buffer == NULL)||(original == NULL)) return; if (RTS_ReadRegs(dev->usb_handle, buffer) != OK) { free(buffer); return; } for (a = 0; a < RT_BUFFER_LEN; a++) { if ((original[a] & 0xff) != (buffer[a] & 0xff)) { printf("%5i: %i -> %i\n", a, original[a] & 0xff, buffer[a] & 0xff); original[a] = buffer[a] & 0xff; } } free(buffer); } */ static SANE_Int Load_Constrains (struct st_device *dev) { SANE_Int rst = ERROR; if (dev->constrains != NULL) Free_Constrains (dev); DBG (DBG_FNC, "> Load_Constrains\n"); dev->constrains = (struct st_constrains *) malloc (sizeof (struct st_constrains)); if (dev->constrains != NULL) { cfg_constrains_get (dev->constrains); rst = OK; } return rst; } static SANE_Int Constrains_Check (struct st_device *dev, SANE_Int Resolution, SANE_Int scantype, struct st_coords *mycoords) { /* Constrains: 100 dpi 850 x 1170 | 164 x 327 300 dpi 2550 x 3510 600 dpi 5100 x 7020 1200 dpi 10200 x 14040 */ SANE_Int rst = ERROR; if (dev->constrains != NULL) { struct st_coords coords; struct st_coords *mc; if ((scantype < ST_NORMAL) || (scantype > ST_NEG)) scantype = ST_NORMAL; switch (scantype) { case ST_TA: mc = &dev->constrains->slide; break; case ST_NEG: mc = &dev->constrains->negative; break; default: mc = &dev->constrains->reflective; break; } coords.left = MM_TO_PIXEL (mc->left, Resolution); coords.width = MM_TO_PIXEL (mc->width, Resolution); coords.top = MM_TO_PIXEL (mc->top, Resolution); coords.height = MM_TO_PIXEL (mc->height, Resolution); /* Check left and top */ if (mycoords->left < 0) mycoords->left = 0; mycoords->left += coords.left; if (mycoords->top < 0) mycoords->top = 0; mycoords->top += coords.top; /* Check width and height */ if ((mycoords->width < 0) || (mycoords->width > coords.width)) mycoords->width = coords.width; if ((mycoords->height < 0) || (mycoords->height > coords.height)) mycoords->height = coords.height; rst = OK; } DBG (DBG_FNC, "> Constrains_Check: Source=%s, Res=%i, LW=(%i,%i), TH=(%i,%i): %i\n", dbg_scantype (scantype), Resolution, mycoords->left, mycoords->width, mycoords->top, mycoords->height, rst); return rst; } static struct st_coords * Constrains_Get (struct st_device *dev, SANE_Byte scantype) { static struct st_coords *rst = NULL; if (dev->constrains != NULL) { switch (scantype) { case ST_TA: rst = &dev->constrains->slide; break; case ST_NEG: rst = &dev->constrains->negative; break; default: rst = &dev->constrains->reflective; break; } } return rst; } static void Free_Constrains (struct st_device *dev) { DBG (DBG_FNC, "> Free_Constrains\n"); if (dev->constrains != NULL) { free (dev->constrains); dev->constrains = NULL; } } static void RTS_DebugInit () { /* Default values */ RTS_Debug->dev_model = HP3970; RTS_Debug->DumpShadingData = FALSE; RTS_Debug->SaveCalibFile = FALSE; RTS_Debug->ScanWhiteBoard = FALSE; RTS_Debug->EnableGamma = TRUE; RTS_Debug->use_fixed_pwm = TRUE; RTS_Debug->dmatransfersize = 0x80000; RTS_Debug->dmasetlength = 0x7c0000; RTS_Debug->dmabuffersize = 0x400000; RTS_Debug->usbtype = -1; /* Lamp settings */ RTS_Debug->overdrive_flb = 10000; /* msecs */ RTS_Debug->overdrive_ta = 10000; /* msecs */ RTS_Debug->warmup = TRUE; /* Calibration settings */ RTS_Debug->calibrate = FALSE; RTS_Debug->wshading = TRUE; } static void RTS_Setup_Gamma (SANE_Byte * Regs, struct st_hwdconfig *hwdcfg) { DBG (DBG_FNC, "> RTS_Setup_Gamma(*Regs, *hwdcfg)\n"); if ((hwdcfg != NULL) && (Regs != NULL)) { if (hwdcfg->use_gamma_tables != FALSE) { SANE_Int table_size; /* set set table size */ data_bitset (&Regs[0x1d0], 0x0f, hwdcfg->gamma_tablesize); /* enable gamma correction */ data_bitset (&Regs[0x1d0], 0x40, 1); switch (Regs[0x1d0] & 0x0c) { case 0: table_size = (Regs[0x1d0] & 1) | 0x0100; break; case 4: table_size = (Regs[0x1d0] & 1) | 0x0400; break; case 8: table_size = (Regs[0x1d0] & 1) | 0x1000; break; default: table_size = hwdcfg->startpos & 0xffff; break; } /* 5073 */ /* points to red gamma table */ data_wide_bitset (&Regs[0x1b4], 0x3fff, 0); /* points to green gamma table */ data_wide_bitset (&Regs[0x1b6], 0x3fff, table_size); /* points to blue gamma table */ data_wide_bitset (&Regs[0x1b8], 0x3fff, table_size * 2); v15f8 = (((table_size * 3) + 15) / 16) & 0xffff; } else { /* disable gamma correction */ data_bitset (&Regs[0x1d0], 0x40, 0); v15f8 = 0; } } } static SANE_Int RTS_USBType (struct st_device *dev) { /* Gets USB type of this scanner */ SANE_Int rst = ERROR; SANE_Byte data; DBG (DBG_FNC, "+ RTS_USBType\n"); if (Read_Byte (dev->usb_handle, 0xfe11, &data) == OK) rst = (data & 1); DBG (DBG_FNC, "- RTS_USBType(void): %s\n", (rst == USB11) ? "USB1.1" : "USB2.0"); return rst; } static SANE_Int Init_Vars (void) { SANE_Int rst = OK; hp_gamma = malloc (sizeof (struct st_gammatables)); if (hp_gamma != NULL) memset (hp_gamma, 0, sizeof (struct st_gammatables)); else rst = ERROR; if (rst == OK) { RTS_Debug = malloc (sizeof (struct st_debug_opts)); if (RTS_Debug != NULL) memset (RTS_Debug, 0, sizeof (struct st_debug_opts)); else rst = ERROR; } if (rst == OK) { default_gain_offset = malloc (sizeof (struct st_gain_offset)); if (default_gain_offset != NULL) memset (default_gain_offset, 0, sizeof (struct st_gain_offset)); else rst = ERROR; } if (rst == OK) { calibdata = malloc (sizeof (struct st_calibration_data)); if (calibdata != NULL) memset (calibdata, 0, sizeof (struct st_calibration_data)); else rst = ERROR; } if (rst == OK) { wshading = malloc (sizeof (struct st_shading)); if (wshading != NULL) memset (wshading, 0, sizeof (struct st_shading)); else rst = ERROR; } waitforpwm = TRUE; use_gamma_tables = TRUE; if (rst == OK) RTS_DebugInit (); else Free_Vars (); return rst; } static void Free_Vars (void) { if (RTS_Debug != NULL) { free (RTS_Debug); RTS_Debug = NULL; } if (hp_gamma != NULL) { free (hp_gamma); hp_gamma = NULL; } if (calibdata != NULL) { free (calibdata); calibdata = NULL; } if (wshading != NULL) { if (wshading->rates != NULL) free (wshading->rates); free (wshading); wshading = NULL; } if (default_gain_offset != NULL) { free (default_gain_offset); default_gain_offset = NULL; } } static SANE_Int Chipset_Reset (struct st_device *dev) { SANE_Int rst; DBG (DBG_FNC, "+ Chipset_Reset:\n"); /* I've found two ways to reset chipset. Next one will stay commented rst = ERROR; if (Read_Byte(dev->usb_handle, 0xe800, &data) == OK) { data |= 0x20; if (Write_Byte(dev->usb_handle, 0xe800, data) == OK) { data &= 0xdf; rst = Write_Byte(dev->usb_handle, 0xe800, data); } } */ rst = IWrite_Buffer (dev->usb_handle, 0x0000, NULL, 0, 0x0801); DBG (DBG_FNC, "- Chipset_Reset: %i\n", rst); return rst; } static SANE_Int RTS_DMA_Enable_Read (struct st_device *dev, SANE_Int dmacs, SANE_Int size, SANE_Int options) { SANE_Int rst = ERROR; SANE_Byte buffer[6]; DBG (DBG_FNC, "+ RTS_DMA_Enable_Read(dmacs=0x%04x, size=%i, options=0x%06x)\n", dmacs, size, options); data_msb_set (&buffer[0], options, 3); /* buffer size divided by 2 (words count) */ data_lsb_set (&buffer[3], size / 2, 3); rst = IWrite_Buffer (dev->usb_handle, dmacs, buffer, 6, 0x0400); DBG (DBG_FNC, "- RTS_DMA_Enable_Read: %i\n", rst); return rst; } static SANE_Int RTS_DMA_Enable_Write (struct st_device *dev, SANE_Int dmacs, SANE_Int size, SANE_Int options) { SANE_Int rst = ERROR; SANE_Byte buffer[6]; DBG (DBG_FNC, "+ RTS_DMA_Enable_Write(dmacs=0x%04x, size=%i, options=0x%06x)\n", dmacs, size, options); data_msb_set (&buffer[0], options, 3); /* buffer size divided by 2 (words count) */ data_lsb_set (&buffer[3], size / 2, 3); rst = IWrite_Buffer (dev->usb_handle, dmacs, buffer, 6, 0x0401); DBG (DBG_FNC, "- RTS_DMA_Enable_Write: %i\n", rst); return rst; } static SANE_Int RTS_DMA_Cancel (struct st_device *dev) { SANE_Int rst; DBG (DBG_FNC, "+ RTS_DMA_Cancel:\n"); rst = IWrite_Word (dev->usb_handle, 0x0000, 0, 0x0600); DBG (DBG_FNC, "- RTS_DMA_Cancel: %i\n", rst); return rst; } static SANE_Int RTS_DMA_Reset (struct st_device *dev) { SANE_Int rst; DBG (DBG_FNC, "+ RTS_DMA_Reset:\n"); rst = IWrite_Word (dev->usb_handle, 0x0000, 0x0000, 0x0800); DBG (DBG_FNC, "- RTS_DMA_Reset: %i\n", rst); return rst; } static SANE_Int RTS_EEPROM_WriteByte (USB_Handle usb_handle, SANE_Int address, SANE_Byte data) { SANE_Int rst; DBG (DBG_FNC, "+ RTS_EEPROM_WriteByte(address=%04x, data=%i):\n", address, data); rst = IWrite_Byte (usb_handle, address, data, 0x200, 0x200); DBG (DBG_FNC, "- RTS_EEPROM_WriteByte: %i\n", rst); return rst; } static SANE_Int RTS_EEPROM_ReadWord (USB_Handle usb_handle, SANE_Int address, SANE_Int * data) { SANE_Int rst; DBG (DBG_FNC, "+ RTS_EEPROM_ReadWord(address=%04x, data):\n", address); rst = IRead_Word (usb_handle, address, data, 0x200); DBG (DBG_FNC, "- RTS_EEPROM_ReadWord: %i\n", rst); return rst; } static SANE_Int RTS_EEPROM_ReadByte (USB_Handle usb_handle, SANE_Int address, SANE_Byte * data) { SANE_Int rst; DBG (DBG_FNC, "+ RTS_EEPROM_ReadByte(address=%04x, data):\n", address); rst = IRead_Byte (usb_handle, address, data, 0x200); DBG (DBG_FNC, "- RTS_EEPROM_ReadByte: %i\n", rst); return rst; } static SANE_Int RTS_EEPROM_WriteWord (USB_Handle usb_handle, SANE_Int address, SANE_Int data) { SANE_Int rst; DBG (DBG_FNC, "+ RTS_EEPROM_WriteWord(address=%04x, data=%i):\n", address, data); rst = IWrite_Word (usb_handle, address, data, 0x0200); DBG (DBG_FNC, "- RTS_EEPROM_WriteWord: %i\n", rst); return rst; } static SANE_Int RTS_EEPROM_ReadInteger (USB_Handle usb_handle, SANE_Int address, SANE_Int * data) { SANE_Int rst; DBG (DBG_FNC, "+ RTS_EEPROM_ReadInteger(address=%04x, data):\n", address); rst = IRead_Integer (usb_handle, address, data, 0x200); DBG (DBG_FNC, "- RTS_EEPROM_ReadInteger: %i\n", rst); return rst; } static SANE_Int RTS_EEPROM_WriteInteger (USB_Handle usb_handle, SANE_Int address, SANE_Int data) { SANE_Int rst; DBG (DBG_FNC, "+ RTS_EEPROM_WriteInteger(address=%04x, data):\n", address); rst = IWrite_Integer (usb_handle, address, data, 0x200); DBG (DBG_FNC, "- RTS_EEPROM_WriteInteger: %i\n", rst); return rst; } static SANE_Int RTS_EEPROM_WriteBuffer (USB_Handle usb_handle, SANE_Int address, SANE_Byte * data, SANE_Int size) { SANE_Int rst; DBG (DBG_FNC, "+ RTS_EEPROM_WriteBuffer(address=%04x, data, size=%i):\n", address, size); rst = IWrite_Buffer (usb_handle, address, data, size, 0x200); DBG (DBG_FNC, "- RTS_EEPROM_WriteBuffer: %i\n", rst); return rst; } static void WShading_Emulate (SANE_Byte * buffer, SANE_Int * chnptr, SANE_Int size, SANE_Int depth) { if ((wshading->rates != NULL) && (chnptr != NULL)) { if (*chnptr < wshading->count) { double maxvalue, chncolor; SANE_Int chnsize; SANE_Int pos; SANE_Int icolor; maxvalue = (1 << depth) - 1; chnsize = (depth > 8) ? 2 : 1; pos = 0; while (pos < size) { /* get channel color */ chncolor = data_lsb_get (buffer + pos, chnsize); /* apply shading coefficient */ chncolor *= wshading->rates[*chnptr]; /* care about limits */ chncolor = min (chncolor, maxvalue); /* save color */ icolor = chncolor; data_lsb_set (buffer + pos, icolor, chnsize); *chnptr = *chnptr + 1; if (*chnptr >= wshading->count) *chnptr = 0; pos += chnsize; } } } } static SANE_Int WShading_Calibrate (struct st_device *dev, SANE_Byte * Regs, struct st_calibration *myCalib, struct st_scanparams *scancfg) { struct st_calibration_config *calibcfg; struct st_gain_offset myCalibTable; struct st_scanparams *myscancfg; SANE_Byte *myRegs; /*f1bc */ SANE_Int bytes_per_line; /**/ SANE_Int x, y, a, C; SANE_Byte *pattern; /*f164 */ double sumatorio; SANE_Int gainmode; SANE_Int rst; SANE_Byte *avg_colors; DBG (DBG_FNC, "> WShading_Calibrate(*myCalib)\n"); memset (&myCalibTable, 0, sizeof (struct st_gain_offset)); for (C = CL_RED; C <= CL_BLUE; C++) { myCalibTable.pag[C] = 3; myCalibTable.vgag1[C] = 4; myCalibTable.vgag2[C] = 4; } calibcfg = (struct st_calibration_config *) malloc (sizeof (struct st_calibration_config)); memset (calibcfg, 0x30, sizeof (struct st_calibration_config)); myscancfg = (struct st_scanparams *) malloc (sizeof (struct st_scanparams)); memcpy (myscancfg, scancfg, sizeof (struct st_scanparams)); myRegs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte)); memcpy (myRegs, Regs, RT_BUFFER_LEN * sizeof (SANE_Byte)); Calib_LoadConfig (dev, calibcfg, scan.scantype, myscancfg->resolution_x, myscancfg->depth); gainmode = Lamp_GetGainMode (dev, myscancfg->resolution_x, scan.scantype); Lamp_SetGainMode (dev, myRegs, myscancfg->resolution_x, gainmode); rst = OK; switch (scan.scantype) { case ST_NORMAL: /*a184 */ myscancfg->coord.left += scan.ser; myscancfg->coord.width &= 0xffff; break; case ST_TA: case ST_NEG: myscancfg->coord.left += scan.ser; break; } /*a11b */ if ((myscancfg->coord.width & 1) != 0) myscancfg->coord.width++; myscancfg->coord.top = 1; myscancfg->coord.height = calibcfg->WShadingHeight; myscancfg->sensorresolution = 0; bytes_per_line = myscancfg->coord.width * (((myscancfg->colormode == CM_COLOR) ? 3 : 1) * ((myscancfg->depth > 8) ? 2 : 1)); /*a1e8 */ myscancfg->v157c = bytes_per_line; myscancfg->bytesperline = bytes_per_line; /* allocate space for pattern */ pattern = (SANE_Byte *) malloc (((myscancfg->coord.height) * bytes_per_line) * sizeof (SANE_Byte)); if (pattern == NULL) return ERROR; /* Scan image */ myCalib->shading_enabled = FALSE; rst = RTS_GetImage (dev, myRegs, myscancfg, &myCalibTable, pattern, myCalib, 0x20000000, gainmode); if (rst != ERROR) { SANE_Int chn; double colors[3] = { 0, 0, 0 }; double prueba; SANE_Int data; SANE_Int bytes_per_channel; bytes_per_channel = (myscancfg->depth > 8) ? 2 : 1; avg_colors = (SANE_Byte *) malloc (sizeof (SANE_Byte) * bytes_per_line); if (avg_colors != NULL) { wshading->ptr = 0; wshading->count = bytes_per_line / bytes_per_channel; if (wshading->rates != NULL) { free (wshading->rates); wshading->rates = NULL; } wshading->rates = (double *) malloc (sizeof (double) * wshading->count); chn = 0; for (x = 0; x < wshading->count; x++) { sumatorio = 0; for (y = 0; y < myscancfg->coord.height; y++) { data = data_lsb_get (pattern + ((x * bytes_per_channel) + (bytes_per_line * y)), bytes_per_channel); sumatorio += data; } sumatorio /= myscancfg->coord.height; a = sumatorio; colors[chn] = max (colors[chn], sumatorio); chn++; if (chn > 2) chn = 0; data_lsb_set (avg_colors + (x * bytes_per_channel), a, bytes_per_channel); } DBG (DBG_FNC, " -> max colors RGB= %f %f %f\n", colors[0], colors[1], colors[2]); chn = 0; for (x = 0; x < wshading->count; x++) { data = data_lsb_get (avg_colors + (x * bytes_per_channel), bytes_per_channel); prueba = data; *(wshading->rates + x) = colors[chn] / prueba; chn++; if (chn > 2) chn = 0; } } if (RTS_Debug->SaveCalibFile != FALSE) { dbg_tiff_save ("whiteshading_jkd.tiff", myscancfg->coord.width, myscancfg->coord.height, myscancfg->depth, CM_COLOR, scancfg->resolution_x, scancfg->resolution_y, pattern, (myscancfg->coord.height) * bytes_per_line); } #ifdef developing { FILE *archivo; char texto[1024]; /* apply correction to the pattern to see the result */ chn = 0; for (x = 0; x < myscancfg->coord.height * wshading->count; x++) { data = data_lsb_get (pattern + (x * bytes_per_channel), bytes_per_channel); sumatorio = data; sumatorio *= wshading->rates[chn]; if (sumatorio > ((1 << myscancfg->depth) - 1)) sumatorio = (1 << myscancfg->depth) - 1; a = sumatorio; data_lsb_set (pattern + (x * bytes_per_channel), a, bytes_per_channel); chn++; if (chn == wshading->count) chn = 0; } /* save corrected pattern */ dbg_tiff_save ("onwhiteshading_jkd.tiff", myscancfg->coord.width, myscancfg->coord.height, myscancfg->depth, CM_COLOR, scancfg->resolution_x, scancfg->resolution_y, pattern, (myscancfg->coord.height) * bytes_per_line); /* export coefficients */ archivo = fopen ("wShading.txt", "w"); for (x = 0; x < wshading->count; x++) { snprintf (texto, 1024, "%f", wshading->rates[x]); fprintf (archivo, "%s\n", texto); } fclose (archivo); } #endif } free (pattern); return OK; } #ifdef developing static SANE_Int motor_pos (struct st_device *dev, SANE_Byte * Regs, struct st_calibration *myCalib, struct st_scanparams *scancfg) { struct st_calibration_config *calibcfg; struct st_gain_offset myCalibTable; struct st_scanparams *myscancfg; SANE_Byte *myRegs; /*f1bc */ SANE_Int bytes_per_line; /**/ SANE_Int a, C; SANE_Byte *scanbuffer; /*f164 */ SANE_Int gainmode; SANE_Int rst; DBG (DBG_FNC, "> Calib_test(*myCalib)\n"); memset (&myCalibTable, 0, sizeof (struct st_gain_offset)); calibcfg = (struct st_calibration_config *) malloc (sizeof (struct st_calibration_config)); memset (calibcfg, 0x30, sizeof (struct st_calibration_config)); myscancfg = (struct st_scanparams *) malloc (sizeof (struct st_scanparams)); memcpy (myscancfg, scancfg, sizeof (struct st_scanparams)); myRegs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte)); memcpy (myRegs, Regs, RT_BUFFER_LEN * sizeof (SANE_Byte)); Calib_LoadConfig (dev, calibcfg, scan.scantype, myscancfg->resolution_x, myscancfg->depth); gainmode = Lamp_GetGainMode (dev, myscancfg->resolution_x, scan.scantype); Lamp_SetGainMode (dev, myRegs, myscancfg->resolution_x, gainmode); rst = OK; switch (scan.scantype) { case ST_NORMAL: /*a184 */ myscancfg->coord.left += scan.ser; myscancfg->coord.width &= 0xffff; break; case ST_TA: case ST_NEG: myscancfg->coord.left += scan.ser; break; } /*a11b */ if ((myscancfg->coord.width & 1) != 0) myscancfg->coord.width++; myscancfg->coord.top = 100; myscancfg->coord.height = 30; bytes_per_line = myscancfg->coord.width * 3; /*a1e8 */ myscancfg->v157c = bytes_per_line; myscancfg->bytesperline = bytes_per_line; scanbuffer = (SANE_Byte *) malloc (((myscancfg->coord.height + 16) * bytes_per_line) * sizeof (SANE_Byte)); if (scanbuffer == NULL) return ERROR; /* Scan image */ myCalib->shading_enabled = FALSE; /*Head_Relocate(dev, dev->motorcfg->highspeedmotormove, MTR_FORWARD, 5500); */ for (a = 0; a < 10; a++) { for (C = CL_RED; C <= CL_BLUE; C++) { myCalibTable.pag[C] = 3; myCalibTable.vgag1[C] = 4; myCalibTable.vgag2[C] = 4; myCalibTable.edcg1[C] = a * 20; } dbg_ScanParams (myscancfg); Head_Relocate (dev, dev->motorcfg->highspeedmotormove, MTR_FORWARD, 5000); rst = RTS_GetImage (dev, myRegs, myscancfg, &myCalibTable, scanbuffer, myCalib, 0x20000000, gainmode); Head_ParkHome (dev, TRUE, dev->motorcfg->parkhomemotormove); if (rst != ERROR) { char name[30]; snprintf (name, 30, "calibtest-%i.tiff", a); dbg_tiff_save (name, myscancfg->coord.width, myscancfg->coord.height, myscancfg->depth, CM_COLOR, myscancfg->resolution_x, myscancfg->resolution_y, scanbuffer, (myscancfg->coord.height + 16) * bytes_per_line); } } free (scanbuffer); exit (0); return OK; } static SANE_Int hp4370_prueba (struct st_device *dev) { SANE_Int rst; SANE_Int data = 0x0530, a; SANE_Int transferred; SANE_Byte buffer[512]; for (a = 0; a < 256; a++) data_lsb_set (buffer + (a * 2), 0x9d7, 2); rst = IWrite_Word (dev->usb_handle, 0x0000, data, 0x0800); RTS_DMA_Enable_Write (dev, 0x4, 512, 0); Bulk_Operation (dev, BLK_WRITE, 512, buffer, &transferred); return rst; } static SANE_Int Calib_BlackShading_jkd (struct st_device *dev, SANE_Byte * Regs, struct st_calibration *myCalib, struct st_scanparams *scancfg) { struct st_calibration_config *calibcfg; struct st_gain_offset myCalibTable; struct st_scanparams *myscancfg; SANE_Byte *myRegs; /*f1bc */ SANE_Int bytes_per_line; /**/ SANE_Int x, y, a, C; SANE_Byte *scanbuffer; /*f164 */ double sumatorio; SANE_Int gainmode; SANE_Int rst; DBG (DBG_FNC, "> Calib_BlackShading_jkd(*myCalib)\n"); memset (&myCalibTable, 0, sizeof (struct st_gain_offset)); for (C = CL_RED; C <= CL_BLUE; C++) { myCalibTable.pag[C] = 3; myCalibTable.vgag1[C] = 4; myCalibTable.vgag2[C] = 4; } calibcfg = (struct st_calibration_config *) malloc (sizeof (struct st_calibration_config)); memset (calibcfg, 0x30, sizeof (struct st_calibration_config)); myscancfg = (struct st_scanparams *) malloc (sizeof (struct st_scanparams)); memcpy (myscancfg, scancfg, sizeof (struct st_scanparams)); myRegs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte)); memcpy (myRegs, Regs, RT_BUFFER_LEN * sizeof (SANE_Byte)); Calib_LoadConfig (dev, calibcfg, scan.scantype, myscancfg->resolution_x, myscancfg->depth); gainmode = Lamp_GetGainMode (dev, myscancfg->resolution_x, scan.scantype); Lamp_SetGainMode (dev, myRegs, myscancfg->resolution_x, gainmode); rst = OK; switch (scan.scantype) { case ST_NORMAL: /*a184 */ myscancfg->coord.left += scan.ser; myscancfg->coord.width &= 0xffff; break; case ST_TA: case ST_NEG: myscancfg->coord.left += scan.ser; break; } /*a11b */ if ((myscancfg->coord.width & 1) != 0) myscancfg->coord.width++; myscancfg->coord.top = 1; myscancfg->coord.height = calibcfg->BShadingHeight; bytes_per_line = myscancfg->coord.width * 3; /*a1e8 */ myscancfg->v157c = bytes_per_line; myscancfg->bytesperline = bytes_per_line; scanbuffer = (SANE_Byte *) malloc (((myscancfg->coord.height + 16) * bytes_per_line) * sizeof (SANE_Byte)); if (scanbuffer == NULL) return ERROR; /* Turn off lamp */ Lamp_Status_Set (dev, NULL, FALSE, FLB_LAMP); usleep (200 * 1000); /* Scan image */ myCalib->shading_enabled = FALSE; rst = RTS_GetImage (dev, myRegs, myscancfg, &myCalibTable, scanbuffer, myCalib, 0x101, gainmode); /* Turn on lamp again */ if (scan.scantype != ST_NORMAL) { Lamp_Status_Set (dev, NULL, FALSE, TMA_LAMP); usleep (1000 * 1000); } else Lamp_Status_Set (dev, NULL, TRUE, FLB_LAMP); if (rst != ERROR) { jkd_black = (SANE_Byte *) malloc (bytes_per_line); if (jkd_black != NULL) { jkd_blackbpl = bytes_per_line; for (x = 0; x < bytes_per_line; x++) { sumatorio = 0; for (y = 0; y < myscancfg->coord.height + 16; y++) sumatorio += scanbuffer[x + (bytes_per_line * y)]; sumatorio /= myscancfg->coord.height + 16; a = sumatorio; *(jkd_black + x) = _B0 (a); } } /*if (RTS_Debug->SaveCalibFile != FALSE) */ { dbg_tiff_save ("blackshading_jkd.tiff", myscancfg->coord.width, myscancfg->coord.height, myscancfg->depth, CM_COLOR, myscancfg->resolution_x, myscancfg->resolution_y, scanbuffer, (myscancfg->coord.height + 16) * bytes_per_line); } } free (scanbuffer); return OK; } static SANE_Int Calib_test (struct st_device *dev, SANE_Byte * Regs, struct st_calibration *myCalib, struct st_scanparams *scancfg) { struct st_calibration_config *calibcfg; struct st_gain_offset myCalibTable; struct st_scanparams *myscancfg; SANE_Byte *myRegs; /*f1bc */ SANE_Int bytes_per_line; /**/ SANE_Int a, C; SANE_Byte *scanbuffer; /*f164 */ SANE_Int gainmode; SANE_Int rst; DBG (DBG_FNC, "> Calib_test(*myCalib)\n"); memset (&myCalibTable, 0, sizeof (struct st_gain_offset)); calibcfg = (struct st_calibration_config *) malloc (sizeof (struct st_calibration_config)); memset (calibcfg, 0x30, sizeof (struct st_calibration_config)); myscancfg = (struct st_scanparams *) malloc (sizeof (struct st_scanparams)); memcpy (myscancfg, scancfg, sizeof (struct st_scanparams)); myRegs = (SANE_Byte *) malloc (RT_BUFFER_LEN * sizeof (SANE_Byte)); memcpy (myRegs, Regs, RT_BUFFER_LEN * sizeof (SANE_Byte)); Calib_LoadConfig (dev, calibcfg, scan.scantype, myscancfg->resolution_x, myscancfg->depth); gainmode = Lamp_GetGainMode (dev, myscancfg->resolution_x, scan.scantype); Lamp_SetGainMode (dev, myRegs, myscancfg->resolution_x, gainmode); rst = OK; switch (scan.scantype) { case ST_NORMAL: /*a184 */ myscancfg->coord.left += scan.ser; myscancfg->coord.width &= 0xffff; break; case ST_TA: case ST_NEG: myscancfg->coord.left += scan.ser; break; } /*a11b */ if ((myscancfg->coord.width & 1) != 0) myscancfg->coord.width++; myscancfg->coord.top = 100; myscancfg->coord.height = 30; bytes_per_line = myscancfg->coord.width * 3; /*a1e8 */ myscancfg->v157c = bytes_per_line; myscancfg->bytesperline = bytes_per_line; scanbuffer = (SANE_Byte *) malloc (((myscancfg->coord.height + 16) * bytes_per_line) * sizeof (SANE_Byte)); if (scanbuffer == NULL) return ERROR; /* Scan image */ myCalib->shading_enabled = FALSE; /*Head_Relocate(dev, dev->motorcfg->highspeedmotormove, MTR_FORWARD, 5500); */ for (a = 0; a < 10; a++) { for (C = CL_RED; C <= CL_BLUE; C++) { myCalibTable.pag[C] = 3; myCalibTable.vgag1[C] = 4; myCalibTable.vgag2[C] = 4; myCalibTable.edcg1[C] = a * 20; } Head_Relocate (dev, dev->motorcfg->highspeedmotormove, MTR_FORWARD, 5000); rst = RTS_GetImage (dev, myRegs, myscancfg, &myCalibTable, scanbuffer, myCalib, 0x20000000, gainmode); Head_ParkHome (dev, TRUE, dev->motorcfg->parkhomemotormove); if (rst != ERROR) { char name[30]; snprintf (name, 30, "calibtest-%i.tiff", a); dbg_tiff_save (name, myscancfg->coord.width, myscancfg->coord.height, myscancfg->depth, CM_COLOR, myscancfg->resolution_x, myscancfg->resolution_y, scanbuffer, (myscancfg->coord.height + 16) * bytes_per_line); } } free (scanbuffer); exit (0); return OK; } static void prueba (SANE_Byte * a) { /* SANE_Byte p[] = {}; */ /*int z = 69; */ /*(a + 11) = 0x0; */ /*a[1] = a[1] | 0x40; */ /*memcpy(a, &p, sizeof(p)); */ /*memcpy(a + 0x12, p, 10); */ /*a[0x146] &= 0xdf; */ } void shadingtest1 (struct st_device *dev, SANE_Byte * Regs, struct st_calibration *myCalib) { USHORT *buffer; int a; int bit[2]; DBG (DBG_FNC, "+ shadingtest1(*Regs, *myCalib):\n"); if ((Regs == NULL) || (myCalib == NULL)) return; RTS_DMA_Reset (dev); bit[0] = (Regs[0x60b] >> 6) & 1; bit[1] = (Regs[0x60b] >> 4) & 1; Regs[0x060b] &= 0xaf; Write_Byte (dev->usb_handle, 0xee0b, Regs[0x060b]); Regs[0x1cf] = 0; /* reset config. By default black shading disabled and pixel-rate */ /*Regs[0x1cf] |= 2; shadingbase 0x2000 */ Regs[0x1cf] |= 4; /* White shading enabled */ Regs[0x1cf] |= 0x20; /* 16 bits per channel */ Write_Byte (dev->usb_handle, 0xe9cf, Regs[0x01cf]); buffer = (USHORT *) malloc (sizeof (USHORT) * myCalib->shadinglength); DBG (DBG_FNC, " -> shading length = %i\n", myCalib->shadinglength); /* fill buffer */ for (a = 0; a < myCalib->shadinglength; a++) buffer[a] = RTS_Debug->shd + (a * 500); for (a = 0; a < 3; a++) { RTS_DMA_Write (dev, a | 0x14, 0, sizeof (USHORT) * myCalib->shadinglength, (SANE_Byte *) buffer); } data_bitset (&Regs[0x60b], 0x40, bit[0]); /*-x------*/ data_bitset (&Regs[0x60b], 0x10, bit[1]); /*---x----*/ Write_Byte (dev->usb_handle, 0xee0b, Regs[0x060b]); DBG (DBG_FNC, "- shadingtest1\n"); } #endif #endif /* RTS8822_CORE */ backends-1.3.0/backend/hp3900_sane.c000066400000000000000000002133021456256263500167700ustar00rootroot00000000000000/* HP Scanjet 3900 series - SANE Backend controller Copyright (C) 2005-2009 Jonathan Bravo Lopez 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, see . 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. */ /* Backend Code for SANE*/ #define HP3900_CONFIG_FILE "hp3900.conf" #define GAMMA_DEFAULT 1.0 #include "../include/sane/config.h" #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_config.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_debug.h" #include "hp3900_rts8822.c" struct st_convert { SANE_Int colormode; SANE_Int depth; SANE_Int threshold; SANE_Int negative; SANE_Int real_depth; }; /* options enumerator */ typedef enum { opt_begin = 0, grp_geometry, opt_tlx, opt_tly, opt_brx, opt_bry, opt_resolution, /* gamma tables */ opt_gamma_red, opt_gamma_green, opt_gamma_blue, opt_scantype, opt_colormode, opt_depth, opt_threshold, /* debugging options */ grp_debug, opt_model, opt_negative, opt_nogamma, opt_nowshading, opt_realdepth, opt_emulategray, opt_nowarmup, opt_dbgimages, opt_reset, /* device information */ grp_info, opt_chipname, opt_chipid, opt_scancount, opt_infoupdate, /* supported buttons. RTS8822 supports up to 6 buttons */ grp_sensors, opt_button_0, opt_button_1, opt_button_2, opt_button_3, opt_button_4, opt_button_5, opt_count } EOptionIndex; /* linked list of SANE_Device structures */ typedef struct TDevListEntry { struct TDevListEntry *pNext; SANE_Device dev; char *devname; } TDevListEntry; typedef struct { char *pszVendor; char *pszName; } TScannerModel; typedef union { SANE_Word w; SANE_Word *wa; /* word array */ SANE_String s; } TOptionValue; typedef struct { SANE_Int model; SANE_Option_Descriptor aOptions[opt_count]; TOptionValue aValues[opt_count]; struct params ScanParams; /* lists */ SANE_String_Const *list_colormodes; SANE_Int *list_depths; SANE_String_Const *list_models; SANE_Int *list_resolutions; SANE_String_Const *list_sources; SANE_Word *aGammaTable[3]; /* a 16-to-16 bit color lookup table */ SANE_Range rng_gamma; /* reading image */ SANE_Byte *image; SANE_Byte *rest; SANE_Int rest_amount; SANE_Int mylin; /* conversion settings */ struct st_convert cnv; /* ranges */ SANE_Range rng_threshold; SANE_Range rng_horizontal; SANE_Range rng_vertical; SANE_Int scan_count; SANE_Int fScanning; /* TRUE if actively scanning */ } TScanner; /* functions to manage backend's options */ static void options_init (TScanner * scanner); static void options_free (TScanner * scanner); /* devices listing */ static SANE_Int _ReportDevice (TScannerModel * pModel, const char *pszDeviceName); static SANE_Status attach_one_device (SANE_String_Const devname); /* capabilities */ static SANE_Status bknd_colormodes (TScanner * scanner, SANE_Int model); static void bknd_constrains (TScanner * scanner, SANE_Int source, SANE_Int type); static SANE_Status bknd_depths (TScanner * scanner, SANE_Int model); static SANE_Status bknd_info (TScanner * scanner); static SANE_Status bknd_models (TScanner * scanner); static SANE_Status bknd_resolutions (TScanner * scanner, SANE_Int model); static SANE_Status bknd_sources (TScanner * scanner, SANE_Int model); /* conversions */ static void Color_Negative (SANE_Byte * buffer, SANE_Int size, SANE_Int depth); static void Color_to_Gray (SANE_Byte * buffer, SANE_Int size, SANE_Int depth); static void Gray_to_Lineart (SANE_Byte * buffer, SANE_Int size, SANE_Int threshold); static void Depth_16_to_8 (SANE_Byte * from_buffer, SANE_Int size, SANE_Byte * to_buffer); /* gamma functions */ static void gamma_apply (TScanner * s, SANE_Byte * buffer, SANE_Int size, SANE_Int depth); static SANE_Int gamma_create (TScanner * s, double gamma); static void gamma_free (TScanner * s); static SANE_Int Get_Colormode (SANE_String colormode); static SANE_Int Get_Model (SANE_String model); static SANE_Int Get_Source (SANE_String source); static SANE_Int GetUSB_device_model (SANE_String_Const name); static size_t max_string_size (const SANE_String_Const strings[]); static SANE_Status get_button_status (TScanner * s); /* reading buffers */ static SANE_Status img_buffers_alloc (TScanner * scanner, SANE_Int size); static SANE_Status img_buffers_free (TScanner * scanner); static SANE_Status option_get (TScanner * scanner, SANE_Int optid, void *result); static SANE_Status option_set (TScanner * scanner, SANE_Int optid, void *value, SANE_Int * pInfo); static void Set_Coordinates (SANE_Int scantype, SANE_Int resolution, struct st_coords *coords); static SANE_Int set_ScannerModel (SANE_Int proposed, SANE_Int product, SANE_Int vendor); static void Silent_Compile (void); static SANE_Status Translate_coords (struct st_coords *coords); /* SANE functions */ void sane_cancel (SANE_Handle h); void sane_close (SANE_Handle h); SANE_Status sane_control_option (SANE_Handle h, SANE_Int n, SANE_Action Action, void *pVal, SANE_Int * pInfo); void sane_exit (void); SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only); const SANE_Option_Descriptor *sane_get_option_descriptor (SANE_Handle h, SANE_Int n); SANE_Status sane_get_parameters (SANE_Handle h, SANE_Parameters * p); SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd); SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize); SANE_Status sane_open (SANE_String_Const name, SANE_Handle * h); SANE_Status sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len); SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking); SANE_Status sane_start (SANE_Handle h); /* variables */ static struct st_device *device = NULL; static TDevListEntry *_pFirstSaneDev = 0; static SANE_Int iNumSaneDev = 0; static const SANE_Device **_pSaneDevList = 0; /* Own functions */ static SANE_Status bknd_resolutions (TScanner * scanner, SANE_Int model) { SANE_Status rst = SANE_STATUS_INVAL; DBG (DBG_FNC, "> bknd_resolutions(*scanner, model=%i)\n", model); if (scanner != NULL) { SANE_Int *res = NULL; switch (model) { case BQ5550: case UA4900: { SANE_Int myres[] = { 8, 50, 75, 100, 150, 200, 300, 600, 1200 }; res = (SANE_Int *) malloc (sizeof (myres)); if (res != NULL) memcpy (res, &myres, sizeof (myres)); } break; case HPG2710: case HP3800: { /* 1200 and 2400 dpi are disabled until problems are solved */ SANE_Int myres[] = { 7, 50, 75, 100, 150, 200, 300, 600 }; res = (SANE_Int *) malloc (sizeof (myres)); if (res != NULL) memcpy (res, &myres, sizeof (myres)); } break; case HP4370: case HPG3010: case HPG3110: { SANE_Int myres[] = { 10, 50, 75, 100, 150, 200, 300, 600, 1200, 2400, 4800 }; res = (SANE_Int *) malloc (sizeof (myres)); if (res != NULL) memcpy (res, &myres, sizeof (myres)); } break; default: /* HP3970 & HP4070 & UA4900 */ { SANE_Int myres[] = { 9, 50, 75, 100, 150, 200, 300, 600, 1200, 2400 }; res = (SANE_Int *) malloc (sizeof (myres)); if (res != NULL) memcpy (res, &myres, sizeof (myres)); } break; } if (res != NULL) { if (scanner->list_resolutions != NULL) free (scanner->list_resolutions); scanner->list_resolutions = res; rst = SANE_STATUS_GOOD; } } return rst; } static SANE_Status bknd_models (TScanner * scanner) { SANE_Status rst = SANE_STATUS_INVAL; DBG (DBG_FNC, "> bknd_models:\n"); if (scanner != NULL) { SANE_String_Const *model = NULL; /* at this moment all devices use the same list */ SANE_String_Const mymodel[] = { "HP3800", "HP3970", "HP4070", "HP4370", "UA4900", "HPG3010", "BQ5550", "HPG2710", "HPG3110", 0 }; /* allocate space to save list */ model = (SANE_String_Const *) malloc (sizeof (mymodel)); if (model != NULL) memcpy (model, &mymodel, sizeof (mymodel)); if (model != NULL) { /* free previous list */ if (scanner->list_models != NULL) free (scanner->list_models); /* set new list */ scanner->list_models = model; rst = SANE_STATUS_GOOD; } } return rst; } static SANE_Status bknd_colormodes (TScanner * scanner, SANE_Int model) { SANE_Status rst = SANE_STATUS_INVAL; DBG (DBG_FNC, "> bknd_colormodes(*scanner, model=%i)\n", model); if (scanner != NULL) { SANE_String_Const *colormode = NULL; /* at this moment all devices use the same list */ SANE_String_Const mycolormode[] = { SANE_VALUE_SCAN_MODE_COLOR, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_LINEART, 0 }; /* silence gcc */ (void) model; colormode = (SANE_String_Const *) malloc (sizeof (mycolormode)); if (colormode != NULL) memcpy (colormode, &mycolormode, sizeof (mycolormode)); if (colormode != NULL) { if (scanner->list_colormodes != NULL) free (scanner->list_colormodes); scanner->list_colormodes = colormode; rst = SANE_STATUS_GOOD; } } return rst; } static SANE_Status bknd_sources (TScanner * scanner, SANE_Int model) { SANE_Status rst = SANE_STATUS_INVAL; DBG (DBG_FNC, "> bknd_sources(*scanner, model=%i)\n", model); if (scanner != NULL) { SANE_String_Const *source = NULL; switch (model) { case UA4900: { SANE_String_Const mysource[] = { SANE_I18N ("Flatbed"), 0 }; source = (SANE_String_Const *) malloc (sizeof (mysource)); if (source != NULL) memcpy (source, &mysource, sizeof (mysource)); } break; default: /* hp3970, hp4070, hp4370 and others */ { SANE_String_Const mysource[] = { SANE_I18N ("Flatbed"), SANE_I18N ("Slide"), SANE_I18N ("Negative"), 0 }; source = (SANE_String_Const *) malloc (sizeof (mysource)); if (source != NULL) memcpy (source, &mysource, sizeof (mysource)); } break; } if (source != NULL) { if (scanner->list_sources != NULL) free (scanner->list_sources); scanner->list_sources = source; rst = SANE_STATUS_GOOD; } } return rst; } static SANE_Status bknd_depths (TScanner * scanner, SANE_Int model) { SANE_Status rst = SANE_STATUS_INVAL; DBG (DBG_FNC, "> bknd_depths(*scanner, model=%i\n", model); if (scanner != NULL) { SANE_Int *depth = NULL; /* at this moment all devices use the same list */ SANE_Int mydepth[] = { 2, 8, 16 }; /*{3, 8, 12, 16}; */ /* silence gcc */ (void) model; depth = (SANE_Int *) malloc (sizeof (mydepth)); if (depth != NULL) memcpy (depth, &mydepth, sizeof (mydepth)); if (depth != NULL) { if (scanner->list_depths != NULL) free (scanner->list_depths); scanner->list_depths = depth; rst = SANE_STATUS_GOOD; } } return rst; } static SANE_Status bknd_info (TScanner * scanner) { SANE_Status rst = SANE_STATUS_INVAL; DBG (DBG_FNC, "> bknd_info(*scanner)"); if (scanner != NULL) { char data[256]; /* update chipset name */ Chipset_Name (device, data, 255); if (scanner->aValues[opt_chipname].s != NULL) { free (scanner->aValues[opt_chipname].s); scanner->aValues[opt_chipname].s = NULL; } scanner->aValues[opt_chipname].s = strdup (data); scanner->aOptions[opt_chipname].size = strlen (data) + 1; /* update chipset id */ scanner->aValues[opt_chipid].w = Chipset_ID (device); /* update scans counter */ scanner->aValues[opt_scancount].w = RTS_ScanCounter_Get (device); rst = SANE_STATUS_GOOD; } return rst; } static SANE_Int GetUSB_device_model (SANE_String_Const name) { SANE_Int usbid, model; /* default model is unknown */ model = -1; /* open usb device */ if (sanei_usb_open (name, &usbid) == SANE_STATUS_GOOD) { SANE_Int vendor, product; if (sanei_usb_get_vendor_product (usbid, &vendor, &product) == SANE_STATUS_GOOD) model = Device_get (product, vendor); sanei_usb_close (usbid); } return model; } static void Silent_Compile (void) { /* There are some functions in hp3900_rts8822.c that aren't used yet. To avoid compilation warnings we will use them here */ SANE_Byte a = 1; if (a == 0) { Buttons_Status (device); Calib_WriteTable (device, NULL, 0, 0); Gamma_GetTables (device, NULL); } } static void bknd_constrains (TScanner * scanner, SANE_Int source, SANE_Int type) { struct st_coords *coords = Constrains_Get (device, source); if ((coords != NULL) && (scanner != NULL)) { switch (type) { case 1: /* Y */ scanner->rng_vertical.max = coords->height; break; default: /* X */ scanner->rng_horizontal.max = coords->width; break; } } } static SANE_Status img_buffers_free (TScanner * scanner) { if (scanner != NULL) { if (scanner->image != NULL) { free (scanner->image); scanner->image = NULL; } if (scanner->rest != NULL) { free (scanner->rest); scanner->rest = NULL; } scanner->rest_amount = 0; } return SANE_STATUS_GOOD; } static SANE_Status img_buffers_alloc (TScanner * scanner, SANE_Int size) { SANE_Status rst; /* default result at this point */ rst = SANE_STATUS_INVAL; if (scanner != NULL) { /* default result at this point */ rst = SANE_STATUS_NO_MEM; /* free previous allocs */ img_buffers_free (scanner); scanner->image = (SANE_Byte *) malloc (size * sizeof (SANE_Byte)); if (scanner->image != NULL) { scanner->rest = (SANE_Byte *) malloc (size * sizeof (SANE_Byte)); if (scanner->rest != NULL) rst = SANE_STATUS_GOOD; /* ok !! */ } if (rst != SANE_STATUS_GOOD) img_buffers_free (scanner); } return rst; } static SANE_Int set_ScannerModel (SANE_Int proposed, SANE_Int product, SANE_Int vendor) { /* This function will set the device behaviour */ SANE_Int current = Device_get (product, vendor); char *sdevname[10] = { "Unknown", "HP3970", "HP4070", "HP4370", "UA4900", "HP3800", "HPG3010", "BQ5550", "HPG2710", "HPG3110" }; DBG (DBG_FNC, "> set_ScannerModel(proposed=%i, product=%04x, vendor=%04x)\n", proposed, product, vendor); if (proposed < 0) { if ((current < 0) || (current >= DEVSCOUNT)) { DBG (DBG_VRB, " -> Unknown device. Defaulting to HP3970...\n"); RTS_Debug->dev_model = HP3970; } else { RTS_Debug->dev_model = current; DBG (DBG_VRB, " -> Device model is %s\n", sdevname[current + 1]); } } else { if (proposed < DEVSCOUNT) { RTS_Debug->dev_model = proposed; DBG (DBG_VRB, " -> Device %s , treating as %s ...\n", sdevname[current + 1], sdevname[proposed + 1]); } else { if ((current >= 0) && (current < DEVSCOUNT)) { RTS_Debug->dev_model = current; DBG (DBG_VRB, " -> Device not supported. Defaulting to %s ...\n", sdevname[current + 1]); } else { RTS_Debug->dev_model = HP3970; DBG (DBG_VRB, "-> Device not supported. Defaulting to HP3970...\n"); } } } return OK; } static void Set_Coordinates (SANE_Int scantype, SANE_Int resolution, struct st_coords *coords) { struct st_coords *limits = Constrains_Get (device, scantype); DBG (DBG_FNC, "> Set_Coordinates(res=%i, *coords):\n", resolution); if (coords->left == -1) coords->left = 0; if (coords->width == -1) coords->width = limits->width; if (coords->top == -1) coords->top = 0; if (coords->height == -1) coords->height = limits->height; DBG (DBG_FNC, " -> Coords [MM] : xy(%i, %i) wh(%i, %i)\n", coords->left, coords->top, coords->width, coords->height); coords->left = MM_TO_PIXEL (coords->left, resolution); coords->width = MM_TO_PIXEL (coords->width, resolution); coords->top = MM_TO_PIXEL (coords->top, resolution); coords->height = MM_TO_PIXEL (coords->height, resolution); DBG (DBG_FNC, " -> Coords [px] : xy(%i, %i) wh(%i, %i)\n", coords->left, coords->top, coords->width, coords->height); Constrains_Check (device, resolution, scantype, coords); DBG (DBG_FNC, " -> Coords [check]: xy(%i, %i) wh(%i, %i)\n", coords->left, coords->top, coords->width, coords->height); } static void Color_Negative (SANE_Byte * buffer, SANE_Int size, SANE_Int depth) { if (buffer != NULL) { SANE_Int a; SANE_Int max_value = (1 << depth) - 1; if (depth > 8) { USHORT *sColor = (void *) buffer; for (a = 0; a < size / 2; a++) { *sColor = max_value - *sColor; sColor++; } } else { for (a = 0; a < size; a++) *(buffer + a) = max_value - *(buffer + a); } } } static SANE_Status get_button_status (TScanner * s) { if (s != NULL) { SANE_Int a, b, status, btn; b = 1; status = Buttons_Released (device) & 63; for (a = 0; a < 6; a++) { if ((status & b) != 0) { btn = Buttons_Order (device, b); if (btn != -1) s->aValues[opt_button_0 + btn].w = SANE_TRUE; } b <<= 1; } } return SANE_STATUS_GOOD; } static void Depth_16_to_8 (SANE_Byte * from_buffer, SANE_Int size, SANE_Byte * to_buffer) { if ((from_buffer != NULL) && (to_buffer != NULL)) { SANE_Int a, b; a = 1; b = 0; while (a < size) { *(to_buffer + b) = *(from_buffer + a); a += 2; b++; } } } static void Gray_to_Lineart (SANE_Byte * buffer, SANE_Int size, SANE_Int threshold) { /* code provided by tobias leutwein */ if (buffer != NULL) { SANE_Byte toBufferByte; SANE_Int fromBufferPos_i = 0; SANE_Int toBufferPos_i = 0; SANE_Int bitPos_i; while (fromBufferPos_i < size) { toBufferByte = 0; for (bitPos_i = 7; bitPos_i != (-1); bitPos_i--) { if ((fromBufferPos_i < size) && (buffer[fromBufferPos_i] < threshold)) toBufferByte |= (1u << bitPos_i); fromBufferPos_i++; } buffer[toBufferPos_i] = toBufferByte; toBufferPos_i++; } } } static void Color_to_Gray (SANE_Byte * buffer, SANE_Int size, SANE_Int depth) { /* converts 3 color channel into 1 gray channel of specified bit depth */ if (buffer != NULL) { SANE_Int c, chn, chn_size; SANE_Byte *ptr_src = NULL; SANE_Byte *ptr_dst = NULL; float data, chn_data; float coef[3] = { 0.299, 0.587, 0.114 }; /* coefficients per channel */ chn_size = (depth > 8) ? 2 : 1; ptr_src = (void *) buffer; ptr_dst = (void *) buffer; for (c = 0; c < size / (3 * chn_size); c++) { data = 0.; /* get, apply coeffs and sum channels */ for (chn = 0; chn < 3; chn++) { chn_data = data_lsb_get (ptr_src + (chn * chn_size), chn_size); data += (chn_data * coef[chn]); } /* save result */ data_lsb_set (ptr_dst, (SANE_Int) data, chn_size); ptr_src += 3 * chn_size; /* next triplet */ ptr_dst += chn_size; } } } static void gamma_free (TScanner * s) { DBG (DBG_FNC, "> gamma_free()\n"); if (s != NULL) { /* Destroy gamma tables */ SANE_Int a; for (a = CL_RED; a <= CL_BLUE; a++) { if (s->aGammaTable[a] != NULL) { free (s->aGammaTable[a]); s->aGammaTable[a] = NULL; } } } } static SANE_Int gamma_create (TScanner * s, double gamma) { SANE_Int rst = ERROR; /* by default */ DBG (DBG_FNC, "> gamma_create(*s)\n"); if (s != NULL) { SANE_Int a; double value, c; /* default result */ rst = OK; /* destroy previous gamma tables */ gamma_free (s); /* check gamma value */ if (gamma < 0) gamma = GAMMA_DEFAULT; /* allocate space for 16 bit gamma tables */ for (a = CL_RED; a <= CL_BLUE; a++) { s->aGammaTable[a] = malloc (65536 * sizeof (SANE_Word)); if (s->aGammaTable[a] == NULL) { rst = ERROR; break; } } if (rst == OK) { /* fill tables */ for (a = 0; a < 65536; a++) { value = (a / (65536. - 1)); value = pow (value, (1. / gamma)); value = value * (65536. - 1); c = (SANE_Int) value; if (c > (65536. - 1)) c = (65536. - 1); else if (c < 0) c = 0; s->aGammaTable[CL_RED][a] = c; s->aGammaTable[CL_GREEN][a] = c; s->aGammaTable[CL_BLUE][a] = c; } } else gamma_free (s); } return rst; } static void gamma_apply (TScanner * s, SANE_Byte * buffer, SANE_Int size, SANE_Int depth) { if ((s != NULL) && (buffer != NULL)) { SANE_Int c; SANE_Int dot_size = 3 * ((depth > 8) ? 2 : 1); SANE_Byte *pColor = buffer; USHORT *sColor = (void *) buffer; if ((s->aGammaTable[CL_RED] != NULL) && (s->aGammaTable[CL_GREEN] != NULL) && (s->aGammaTable[CL_BLUE] != NULL)) { for (c = 0; c < size / dot_size; c++) { if (depth > 8) { *sColor = s->aGammaTable[CL_RED][*sColor]; *(sColor + 1) = s->aGammaTable[CL_GREEN][*(sColor + 1)]; *(sColor + 2) = s->aGammaTable[CL_BLUE][*(sColor + 2)]; sColor += 3; } else { /* 8 bits gamma */ *pColor = (s->aGammaTable[CL_RED][*pColor * 256] >> 8) & 0xff; *(pColor + 1) = (s-> aGammaTable[CL_GREEN][*(pColor + 1) * 256] >> 8) & 0xff; *(pColor + 2) = (s-> aGammaTable[CL_BLUE][*(pColor + 2) * 256] >> 8) & 0xff; pColor += 3; } } } } } static SANE_Int Get_Model (SANE_String model) { SANE_Int rst; if (strcmp (model, "HP3800") == 0) rst = HP3800; else if (strcmp (model, "HPG2710") == 0) rst = HPG2710; else if (strcmp (model, "HP3970") == 0) rst = HP3970; else if (strcmp (model, "HP4070") == 0) rst = HP4070; else if (strcmp (model, "HP4370") == 0) rst = HP4370; else if (strcmp (model, "HPG3010") == 0) rst = HPG3010; else if (strcmp (model, "HPG3110") == 0) rst = HPG3110; else if (strcmp (model, "UA4900") == 0) rst = UA4900; else if (strcmp (model, "BQ5550") == 0) rst = BQ5550; else rst = HP3970; /* default */ return rst; } static SANE_Int Get_Source (SANE_String source) { SANE_Int rst; if (strcmp (source, SANE_I18N ("Flatbed")) == 0) rst = ST_NORMAL; else if (strcmp (source, SANE_I18N ("Slide")) == 0) rst = ST_TA; else if (strcmp (source, SANE_I18N ("Negative")) == 0) rst = ST_NEG; else rst = ST_NORMAL; /* default */ return rst; } static SANE_Int Get_Colormode (SANE_String colormode) { SANE_Int rst; if (strcmp (colormode, SANE_VALUE_SCAN_MODE_COLOR) == 0) rst = CM_COLOR; else if (strcmp (colormode, SANE_VALUE_SCAN_MODE_GRAY) == 0) rst = CM_GRAY; else if (strcmp (colormode, SANE_VALUE_SCAN_MODE_LINEART) == 0) rst = CM_LINEART; else rst = CM_COLOR; /* default */ return rst; } static SANE_Status Translate_coords (struct st_coords *coords) { SANE_Int data; DBG (DBG_FNC, "> Translate_coords(*coords)\n"); if ((coords->left < 0) || (coords->top < 0) || (coords->width < 0) || (coords->height < 0)) return SANE_STATUS_INVAL; if (coords->width < coords->left) { data = coords->left; coords->left = coords->width; coords->width = data; } if (coords->height < coords->top) { data = coords->top; coords->top = coords->height; coords->height = data; } coords->width -= coords->left; coords->height -= coords->top; if (coords->width == 0) coords->width++; if (coords->height == 0) coords->height++; return SANE_STATUS_GOOD; } static size_t max_string_size (const SANE_String_Const strings[]) { size_t size, max_size = 0; SANE_Int i; DBG (DBG_FNC, "> max_string_size:\n"); for (i = 0; strings[i]; ++i) { size = strlen (strings[i]) + 1; if (size > max_size) max_size = size; } return max_size; } static void options_free (TScanner * scanner) { /* frees all information contained in controls */ DBG (DBG_FNC, "> options_free\n"); if (scanner != NULL) { SANE_Int i; SANE_Option_Descriptor *pDesc; TOptionValue *pVal; /* free gamma tables */ gamma_free (scanner); /* free lists */ if (scanner->list_resolutions != NULL) free (scanner->list_resolutions); if (scanner->list_depths != NULL) free (scanner->list_depths); if (scanner->list_sources != NULL) free (scanner->list_sources); if (scanner->list_colormodes != NULL) free (scanner->list_colormodes); if (scanner->list_models != NULL) free (scanner->list_models); /* free values in certain controls */ for (i = opt_begin; i < opt_count; i++) { pDesc = &scanner->aOptions[i]; pVal = &scanner->aValues[i]; if (pDesc->type == SANE_TYPE_STRING) { if (pVal->s != NULL) free (pVal->s); } } } } static void options_init (TScanner * scanner) { /* initializes all controls */ DBG (DBG_FNC, "> options_init\n"); if (scanner != NULL) { SANE_Int i; SANE_Option_Descriptor *pDesc; TOptionValue *pVal; /* set gamma */ gamma_create (scanner, 1.0); /* color conversion */ scanner->cnv.colormode = -1; scanner->cnv.negative = FALSE; scanner->cnv.threshold = 40; scanner->cnv.real_depth = FALSE; scanner->cnv.depth = -1; /* setting threshold */ scanner->rng_threshold.min = 0; scanner->rng_threshold.max = 255; scanner->rng_threshold.quant = 0; /* setting gamma range (16 bits depth) */ scanner->rng_gamma.min = 0; scanner->rng_gamma.max = 65535; scanner->rng_gamma.quant = 0; /* setting default horizontal constraint in millimeters */ scanner->rng_horizontal.min = 0; scanner->rng_horizontal.max = 220; scanner->rng_horizontal.quant = 1; /* setting default vertical constraint in millimeters */ scanner->rng_vertical.min = 0; scanner->rng_vertical.max = 300; scanner->rng_vertical.quant = 1; /* allocate option lists */ bknd_info (scanner); bknd_colormodes (scanner, RTS_Debug->dev_model); bknd_depths (scanner, RTS_Debug->dev_model); bknd_models (scanner); bknd_resolutions (scanner, RTS_Debug->dev_model); bknd_sources (scanner, RTS_Debug->dev_model); /* By default preview scan */ scanner->ScanParams.scantype = ST_NORMAL; scanner->ScanParams.colormode = CM_COLOR; scanner->ScanParams.resolution_x = 75; scanner->ScanParams.resolution_y = 75; scanner->ScanParams.coords.left = 0; scanner->ScanParams.coords.top = 0; scanner->ScanParams.coords.width = 220; scanner->ScanParams.coords.height = 300; scanner->ScanParams.depth = 8; scanner->ScanParams.channel = 0; for (i = opt_begin; i < opt_count; i++) { pDesc = &scanner->aOptions[i]; pVal = &scanner->aValues[i]; /* defaults */ pDesc->name = ""; pDesc->title = ""; pDesc->desc = ""; pDesc->type = SANE_TYPE_INT; pDesc->unit = SANE_UNIT_NONE; pDesc->size = sizeof (SANE_Word); pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->cap = 0; switch (i) { case opt_begin: pDesc->title = SANE_TITLE_NUM_OPTIONS; pDesc->desc = SANE_DESC_NUM_OPTIONS; pDesc->cap = SANE_CAP_SOFT_DETECT; pVal->w = (SANE_Word) opt_count; break; case grp_geometry: pDesc->name = SANE_NAME_GEOMETRY; pDesc->title = SANE_TITLE_GEOMETRY; pDesc->desc = SANE_DESC_GEOMETRY; pDesc->type = SANE_TYPE_GROUP; pDesc->unit = SANE_UNIT_NONE; pDesc->size = 0; pDesc->cap = 0; pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.range = 0; pVal->w = 0; break; case opt_tlx: 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 = &scanner->rng_horizontal; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->w = 0; break; case opt_tly: 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 = &scanner->rng_vertical; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->w = 0; break; case opt_brx: 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 = &scanner->rng_horizontal; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->w = scanner->rng_horizontal.max; break; case opt_bry: 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 = &scanner->rng_vertical; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->w = scanner->rng_vertical.max; break; case opt_resolution: 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 = scanner->list_resolutions; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->w = scanner->list_resolutions[1]; break; case opt_gamma_red: pDesc->name = SANE_NAME_GAMMA_VECTOR_R; pDesc->title = SANE_TITLE_GAMMA_VECTOR_R; pDesc->desc = SANE_DESC_GAMMA_VECTOR_R; pDesc->size = scanner->rng_gamma.max * sizeof (SANE_Word); pDesc->unit = SANE_UNIT_NONE; pDesc->constraint_type = SANE_CONSTRAINT_RANGE; pDesc->constraint.range = &scanner->rng_gamma; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->wa = scanner->aGammaTable[CL_RED]; break; case opt_gamma_green: pDesc->name = SANE_NAME_GAMMA_VECTOR_G; pDesc->title = SANE_TITLE_GAMMA_VECTOR_G; pDesc->desc = SANE_DESC_GAMMA_VECTOR_G; pDesc->size = scanner->rng_gamma.max * sizeof (SANE_Word); pDesc->unit = SANE_UNIT_NONE; pDesc->constraint_type = SANE_CONSTRAINT_RANGE; pDesc->constraint.range = &scanner->rng_gamma; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->wa = scanner->aGammaTable[CL_GREEN]; break; case opt_gamma_blue: pDesc->name = SANE_NAME_GAMMA_VECTOR_B; pDesc->title = SANE_TITLE_GAMMA_VECTOR_B; pDesc->desc = SANE_DESC_GAMMA_VECTOR_B; pDesc->size = scanner->rng_gamma.max * sizeof (SANE_Word); pDesc->unit = SANE_UNIT_NONE; pDesc->constraint_type = SANE_CONSTRAINT_RANGE; pDesc->constraint.range = &scanner->rng_gamma; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->wa = scanner->aGammaTable[CL_BLUE]; break; case opt_scantype: pDesc->name = SANE_NAME_SCAN_SOURCE; pDesc->title = SANE_TITLE_SCAN_SOURCE; pDesc->desc = SANE_DESC_SCAN_SOURCE; pDesc->type = SANE_TYPE_STRING; pDesc->size = max_string_size (scanner->list_sources); pDesc->constraint_type = SANE_CONSTRAINT_STRING_LIST; pDesc->constraint.string_list = scanner->list_sources; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->s = strdup (scanner->list_sources[0]); break; case opt_colormode: 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 = max_string_size (scanner->list_colormodes); pDesc->constraint_type = SANE_CONSTRAINT_STRING_LIST; pDesc->constraint.string_list = scanner->list_colormodes; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->s = strdup (scanner->list_colormodes[0]); break; case opt_depth: pDesc->name = SANE_NAME_BIT_DEPTH; pDesc->title = SANE_TITLE_BIT_DEPTH; pDesc->desc = SANE_DESC_BIT_DEPTH; pDesc->type = SANE_TYPE_INT; pDesc->unit = SANE_UNIT_BIT; pDesc->constraint_type = SANE_CONSTRAINT_WORD_LIST; pDesc->constraint.word_list = scanner->list_depths; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->w = scanner->list_depths[1]; break; case opt_threshold: pDesc->name = SANE_NAME_THRESHOLD; pDesc->title = SANE_TITLE_THRESHOLD; pDesc->desc = SANE_DESC_THRESHOLD; pDesc->type = SANE_TYPE_INT; pDesc->unit = SANE_UNIT_NONE; pDesc->constraint_type = SANE_CONSTRAINT_RANGE; pDesc->constraint.range = &scanner->rng_threshold; pDesc->cap |= SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE; pVal->w = 0x80; break; /* debugging options */ case grp_debug: pDesc->name = "grp_debug"; pDesc->title = SANE_I18N ("Debugging Options"); pDesc->desc = ""; pDesc->type = SANE_TYPE_GROUP; pDesc->unit = SANE_UNIT_NONE; pDesc->size = 0; pDesc->cap = SANE_CAP_ADVANCED; pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.range = 0; pVal->w = 0; break; case opt_model: pDesc->name = "opt_model"; pDesc->title = SANE_I18N ("Scanner model"); pDesc->desc = SANE_I18N ("Allows one to test device behavior with other supported models"); pDesc->type = SANE_TYPE_STRING; pDesc->size = max_string_size (scanner->list_models); pDesc->constraint_type = SANE_CONSTRAINT_STRING_LIST; pDesc->constraint.string_list = scanner->list_models; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->s = strdup (scanner->list_models[0]); break; case opt_negative: pDesc->name = "opt_negative"; pDesc->title = SANE_I18N ("Negative"); pDesc->desc = SANE_I18N ("Image colors will be inverted"); pDesc->type = SANE_TYPE_BOOL; pDesc->unit = SANE_UNIT_NONE; pDesc->size = sizeof (SANE_Word); pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.range = 0; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; pVal->w = SANE_FALSE; break; case opt_nogamma: pDesc->name = "opt_nogamma"; pDesc->title = SANE_I18N ("Disable gamma correction"); pDesc->desc = SANE_I18N ("Gamma correction will be disabled"); pDesc->type = SANE_TYPE_BOOL; pDesc->unit = SANE_UNIT_NONE; pDesc->size = sizeof (SANE_Word); pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.range = 0; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; pVal->w = SANE_FALSE; break; case opt_nowshading: pDesc->name = "opt_nowshading"; pDesc->title = SANE_I18N ("Disable white shading correction"); pDesc->desc = SANE_I18N ("White shading correction will be disabled"); pDesc->type = SANE_TYPE_BOOL; pDesc->unit = SANE_UNIT_NONE; pDesc->size = sizeof (SANE_Word); pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.range = 0; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; pVal->w = SANE_FALSE; break; case opt_nowarmup: pDesc->name = "opt_nowarmup"; pDesc->title = SANE_I18N ("Skip warmup process"); pDesc->desc = SANE_I18N ("Warmup process will be disabled"); pDesc->type = SANE_TYPE_BOOL; pDesc->unit = SANE_UNIT_NONE; pDesc->size = sizeof (SANE_Word); pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.range = 0; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; pVal->w = SANE_FALSE; break; case opt_realdepth: pDesc->name = "opt_realdepth"; pDesc->title = SANE_I18N ("Force real depth"); pDesc->desc = SANE_I18N ("If gamma is enabled, scans are always made in 16 bits depth to improve image quality and then converted to the selected depth. This option avoids depth emulation."); pDesc->type = SANE_TYPE_BOOL; pDesc->unit = SANE_UNIT_NONE; pDesc->size = sizeof (SANE_Word); pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.range = 0; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; pVal->w = SANE_FALSE; break; case opt_emulategray: pDesc->name = "opt_emulategray"; pDesc->title = SANE_I18N ("Emulate Grayscale"); pDesc->desc = SANE_I18N ("If enabled, image will be scanned in color mode and then converted to grayscale by software. This may improve image quality in some circumstances."); pDesc->type = SANE_TYPE_BOOL; pDesc->unit = SANE_UNIT_NONE; pDesc->size = sizeof (SANE_Word); pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.range = 0; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; pVal->w = SANE_FALSE; break; case opt_dbgimages: pDesc->name = "opt_dbgimages"; pDesc->title = SANE_I18N ("Save debugging images"); pDesc->desc = SANE_I18N ("If enabled, some images involved in scanner processing are saved to analyze them."); pDesc->type = SANE_TYPE_BOOL; pDesc->unit = SANE_UNIT_NONE; pDesc->size = sizeof (SANE_Word); pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.range = 0; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; pVal->w = SANE_FALSE; break; case opt_reset: pDesc->name = "opt_reset"; pDesc->title = SANE_I18N ("Reset chipset"); pDesc->desc = SANE_I18N ("Resets chipset data"); pDesc->type = SANE_TYPE_BUTTON; pDesc->unit = SANE_UNIT_NONE; pDesc->size = 0; pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.string_list = 0; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_SELECT; pVal->w = 0; break; /* device information */ case grp_info: pDesc->name = "grp_info"; pDesc->title = SANE_I18N ("Information"); pDesc->desc = ""; pDesc->type = SANE_TYPE_GROUP; pDesc->unit = SANE_UNIT_NONE; pDesc->size = 0; pDesc->cap = 0; pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.range = 0; pVal->w = 0; break; case opt_chipname: pDesc->name = "opt_chipname"; pDesc->title = SANE_I18N ("Chipset name"); pDesc->desc = SANE_I18N ("Shows chipset name used in device."); pDesc->type = SANE_TYPE_STRING; pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT; pVal->s = strdup (SANE_I18N ("Unknown")); pDesc->size = strlen(pVal->s) + 1; break; case opt_chipid: pDesc->name = "opt_chipid"; pDesc->title = SANE_I18N ("Chipset ID"); pDesc->desc = SANE_I18N ("Shows the chipset ID"); pDesc->type = SANE_TYPE_INT; pDesc->unit = SANE_UNIT_NONE; pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT; pVal->w = -1; break; case opt_scancount: pDesc->name = "opt_scancount"; pDesc->title = SANE_I18N ("Scan counter"); pDesc->desc = SANE_I18N ("Shows the number of scans made by scanner"); pDesc->type = SANE_TYPE_INT; pDesc->unit = SANE_UNIT_NONE; pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT; pVal->w = -1; break; case opt_infoupdate: pDesc->name = "opt_infoupdate"; pDesc->title = SANE_I18N ("Update information"); pDesc->desc = SANE_I18N ("Updates information about device"); pDesc->type = SANE_TYPE_BUTTON; pDesc->unit = SANE_UNIT_NONE; pDesc->size = 0; pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.string_list = 0; pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_SELECT; pVal->w = 0; break; /* buttons support */ case grp_sensors: pDesc->name = SANE_NAME_SENSORS; pDesc->title = SANE_TITLE_SENSORS; pDesc->desc = SANE_DESC_SENSORS; pDesc->type = SANE_TYPE_GROUP; pDesc->unit = SANE_UNIT_NONE; pDesc->size = 0; pDesc->cap = 0; pDesc->constraint_type = SANE_CONSTRAINT_NONE; pDesc->constraint.range = 0; pVal->w = 0; break; case opt_button_0: case opt_button_1: case opt_button_2: case opt_button_3: case opt_button_4: case opt_button_5: { char name[12]; char title[128]; sprintf (name, "button %d", i - opt_button_0); sprintf (title, "Scanner button %d", i - opt_button_0); pDesc->name = strdup (name); pDesc->title = strdup (title); pDesc->desc = SANE_I18N ("This option reflects a front panel scanner button"); pDesc->type = SANE_TYPE_BOOL; pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; if (i - opt_button_0 >= Buttons_Count (device)) pDesc->cap |= SANE_CAP_INACTIVE; pDesc->unit = SANE_UNIT_NONE; pDesc->size = sizeof (SANE_Word); pDesc->constraint_type = SANE_CONSTRAINT_NONE; pVal->w = SANE_FALSE; } break; } } } } static SANE_Int _ReportDevice (TScannerModel * pModel, const char *pszDeviceName) { SANE_Int rst = ERROR; TDevListEntry *pNew, *pDev; DBG (DBG_FNC, "> _ReportDevice:\n"); pNew = malloc (sizeof (TDevListEntry)); if (pNew != NULL) { rst = OK; /* add new element to the end of the list */ if (_pFirstSaneDev != NULL) { /* Add at the end of existing list */ for (pDev = _pFirstSaneDev; pDev->pNext; pDev = pDev->pNext); pDev->pNext = pNew; } else _pFirstSaneDev = pNew; /* fill in new element */ pNew->pNext = NULL; pNew->devname = (char *) strdup (pszDeviceName); pNew->dev.name = pNew->devname; pNew->dev.vendor = pModel->pszVendor; pNew->dev.model = pModel->pszName; pNew->dev.type = SANE_I18N ("flatbed scanner"); iNumSaneDev++; } return rst; } static SANE_Status attach_one_device (SANE_String_Const devname) { static TScannerModel sModel; DBG (DBG_FNC, "> attach_one_device(devname=%s)\n", devname); switch (GetUSB_device_model (devname)) { case HP3800: sModel.pszVendor = (char *) strdup ("Hewlett-Packard"); sModel.pszName = (char *) strdup ("Scanjet 3800"); break; case HPG2710: sModel.pszVendor = (char *) strdup ("Hewlett-Packard"); sModel.pszName = (char *) strdup ("Scanjet G2710"); break; case HP3970: sModel.pszVendor = (char *) strdup ("Hewlett-Packard"); sModel.pszName = (char *) strdup ("Scanjet 3970"); break; case HP4070: sModel.pszVendor = (char *) strdup ("Hewlett-Packard"); sModel.pszName = (char *) strdup ("Scanjet 4070 Photosmart"); break; case HP4370: sModel.pszVendor = (char *) strdup ("Hewlett-Packard"); sModel.pszName = (char *) strdup ("Scanjet 4370"); break; case HPG3010: sModel.pszVendor = (char *) strdup ("Hewlett-Packard"); sModel.pszName = (char *) strdup ("Scanjet G3010"); break; case HPG3110: sModel.pszVendor = (char *) strdup ("Hewlett-Packard"); sModel.pszName = (char *) strdup ("Scanjet G3110"); break; case UA4900: sModel.pszVendor = (char *) strdup ("UMAX"); sModel.pszName = (char *) strdup ("Astra 4900"); break; case BQ5550: sModel.pszVendor = (char *) strdup ("BenQ"); sModel.pszName = (char *) strdup ("5550"); break; default: sModel.pszVendor = (char *) strdup ("Unknown"); sModel.pszName = (char *) strdup ("RTS8822 chipset based"); break; } _ReportDevice (&sModel, devname); return SANE_STATUS_GOOD; } /* Sane default functions */ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { FILE *conf_fp; /* Config file stream */ SANE_Char line[PATH_MAX]; SANE_Char *str = NULL; SANE_String_Const proper_str; SANE_Int nline = 0; /* Initialize debug */ DBG_INIT (); DBG (DBG_FNC, "> sane_init\n"); /* silence gcc */ (void) authorize; /* Initialize usb */ sanei_usb_init (); /* Parse config file */ conf_fp = sanei_config_open (HP3900_CONFIG_FILE); if (conf_fp) { while (sanei_config_read (line, sizeof (line), conf_fp)) { nline++; if (str) free (str); proper_str = sanei_config_get_string (line, &str); /* Discards white lines and comments */ if ((str != NULL) && (proper_str != line) && (str[0] != '#')) { /* If line's not blank or a comment, then it's the device * filename or a usb directive. */ sanei_usb_attach_matching_devices (line, attach_one_device); } } fclose (conf_fp); } else { /* default */ DBG (DBG_VRB, "- %s not found. Looking for hardcoded usb ids ...\n", HP3900_CONFIG_FILE); sanei_usb_attach_matching_devices ("usb 0x03f0 0x2605", attach_one_device); /* HP3800 */ sanei_usb_attach_matching_devices ("usb 0x03f0 0x2805", attach_one_device); /* HPG2710 */ sanei_usb_attach_matching_devices ("usb 0x03f0 0x2305", attach_one_device); /* HP3970 */ sanei_usb_attach_matching_devices ("usb 0x03f0 0x2405", attach_one_device); /* HP4070 */ sanei_usb_attach_matching_devices ("usb 0x03f0 0x4105", attach_one_device); /* HP4370 */ sanei_usb_attach_matching_devices ("usb 0x03f0 0x4205", attach_one_device); /* HPG3010 */ sanei_usb_attach_matching_devices ("usb 0x03f0 0x4305", attach_one_device); /* HPG3110 */ sanei_usb_attach_matching_devices ("usb 0x06dc 0x0020", attach_one_device); /* UA4900 */ sanei_usb_attach_matching_devices ("usb 0x04a5 0x2211", attach_one_device); /* BQ5550 */ } /* Return backend version */ if (version_code != NULL) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); return SANE_STATUS_GOOD; } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { SANE_Status rst = SANE_STATUS_GOOD; (void) local_only; if (_pSaneDevList) free (_pSaneDevList); _pSaneDevList = malloc (sizeof (*_pSaneDevList) * (iNumSaneDev + 1)); if (_pSaneDevList != NULL) { TDevListEntry *pDev; SANE_Int i = 0; for (pDev = _pFirstSaneDev; pDev; pDev = pDev->pNext) _pSaneDevList[i++] = &pDev->dev; _pSaneDevList[i++] = 0; /* last entry is 0 */ *device_list = _pSaneDevList; } else rst = SANE_STATUS_NO_MEM; DBG (DBG_FNC, "> sane_get_devices: %i\n", rst); return rst; } SANE_Status sane_open (SANE_String_Const name, SANE_Handle * h) { TScanner *s; SANE_Status rst; /* check the name */ if (strlen (name) == 0) /* default to first available device */ name = _pFirstSaneDev->dev.name; /* allocate space for RTS environment */ device = RTS_Alloc (); if (device != NULL) { /* Open device */ rst = sanei_usb_open (name, &device->usb_handle); if (rst == SANE_STATUS_GOOD) { /* Allocating memory for device */ s = malloc (sizeof (TScanner)); if (s != NULL) { memset (s, 0, sizeof (TScanner)); /* Initializing RTS */ if (Init_Vars () == OK) { SANE_Int vendor, product; /* Setting device model */ if (sanei_usb_get_vendor_product (device->usb_handle, &vendor, &product) == SANE_STATUS_GOOD) s->model = Device_get (product, vendor); else s->model = HP3970; set_ScannerModel (s->model, product, vendor); /* Initialize device */ if (RTS_Scanner_Init (device) == OK) { /* silencing unused functions */ Silent_Compile (); /* initialize backend options */ options_init (s); *h = s; /* everything went ok */ rst = SANE_STATUS_GOOD; } else { free ((void *) s); rst = SANE_STATUS_INVAL; } } else rst = SANE_STATUS_NO_MEM; } else rst = SANE_STATUS_NO_MEM; } } else rst = SANE_STATUS_NO_MEM; DBG (DBG_FNC, "> sane_open(name=%s): %i\n", name, rst); return rst; } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle h, SANE_Int n) { SANE_Option_Descriptor *rst = NULL; if ((n >= opt_begin) && (n < opt_count)) { TScanner *s = (TScanner *) h; rst = &s->aOptions[n]; } DBG (DBG_FNC, "> SANE_Option_Descriptor(handle, n=%i): %i\n", n, (rst == NULL) ? -1 : 0); return rst; } static SANE_Status option_get (TScanner * scanner, SANE_Int optid, void *result) { /* This function returns value contained in selected option */ DBG (DBG_FNC, "> option_get(optid=%i)\n", optid); if ((scanner != NULL) && (result != NULL)) { switch (optid) { /* SANE_Word */ case opt_begin: /* null */ case opt_reset: /* null */ case opt_negative: case opt_nogamma: case opt_nowshading: case opt_emulategray: case opt_dbgimages: case opt_nowarmup: case opt_realdepth: case opt_depth: case opt_resolution: case opt_threshold: case opt_brx: case opt_tlx: case opt_bry: case opt_tly: *(SANE_Word *) result = scanner->aValues[optid].w; break; /* SANE_Int */ case opt_chipid: case opt_scancount: *(SANE_Int *) result = scanner->aValues[optid].w; break; /* SANE_Word array */ case opt_gamma_red: case opt_gamma_green: case opt_gamma_blue: memcpy (result, scanner->aValues[optid].wa, scanner->aOptions[optid].size); break; /* String */ case opt_colormode: case opt_scantype: case opt_model: case opt_chipname: strncpy (result, scanner->aValues[optid].s, scanner->aOptions[optid].size); ((char*)result)[scanner->aOptions[optid].size-1] = '\0'; break; /* scanner buttons */ case opt_button_0: get_button_status (scanner); // fall through case opt_button_1: case opt_button_2: case opt_button_3: case opt_button_4: case opt_button_5: /* copy the button state */ *(SANE_Word *) result = scanner->aValues[optid].w; /* clear the button state */ scanner->aValues[optid].w = SANE_FALSE; break; } } return SANE_STATUS_GOOD; } static SANE_Status option_set (TScanner * scanner, SANE_Int optid, void *value, SANE_Int * pInfo) { SANE_Status rst; DBG (DBG_FNC, "> option_set(optid=%i)\n", optid); rst = SANE_STATUS_INVAL; if (scanner != NULL) { if (scanner->fScanning == FALSE) { SANE_Int info = 0; rst = SANE_STATUS_GOOD; switch (optid) { case opt_brx: case opt_tlx: case opt_bry: case opt_tly: case opt_depth: case opt_nogamma: case opt_nowshading: case opt_nowarmup: case opt_negative: case opt_emulategray: case opt_dbgimages: case opt_threshold: case opt_resolution: info |= SANE_INFO_RELOAD_PARAMS; scanner->aValues[optid].w = *(SANE_Word *) value; break; case opt_gamma_red: case opt_gamma_green: case opt_gamma_blue: memcpy (scanner->aValues[optid].wa, value, scanner->aOptions[optid].size); break; case opt_scantype: if (strcmp (scanner->aValues[optid].s, value) != 0) { struct st_coords *coords; SANE_Int source; if (scanner->aValues[optid].s) free (scanner->aValues[optid].s); scanner->aValues[optid].s = strdup (value); source = Get_Source (scanner->aValues[opt_scantype].s); coords = Constrains_Get (device, source); if (coords != NULL) { bknd_constrains (scanner, source, 0); bknd_constrains (scanner, source, 1); scanner->aValues[opt_tlx].w = 0; scanner->aValues[opt_tly].w = 0; scanner->aValues[opt_brx].w = coords->width; scanner->aValues[opt_bry].w = coords->height; } info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; } break; case opt_colormode: if (strcmp (scanner->aValues[optid].s, value) != 0) { if (scanner->aValues[optid].s) free (scanner->aValues[optid].s); scanner->aValues[optid].s = strdup (value); if (Get_Colormode (scanner->aValues[optid].s) == CM_LINEART) scanner->aOptions[opt_threshold].cap &= ~SANE_CAP_INACTIVE; else scanner->aOptions[opt_threshold].cap |= SANE_CAP_INACTIVE; info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; } break; case opt_model: if (strcmp (scanner->aValues[optid].s, value) != 0) { SANE_Int model; if (scanner->aValues[optid].s) free (scanner->aValues[optid].s); scanner->aValues[optid].s = strdup (value); model = Get_Model (scanner->aValues[optid].s); if (model != RTS_Debug->dev_model) { SANE_Int source; struct st_coords *coords; /* free configuration of last model */ Free_Config (device); /* set new model */ RTS_Debug->dev_model = model; /* and load configuration of current model */ Load_Config (device); /* update options according to selected device */ bknd_info (scanner); bknd_colormodes (scanner, model); bknd_depths (scanner, model); bknd_resolutions (scanner, model); bknd_sources (scanner, model); /* updating lists */ scanner->aOptions[opt_colormode].size = max_string_size (scanner->list_colormodes); scanner->aOptions[opt_colormode].constraint. string_list = scanner->list_colormodes; scanner->aOptions[opt_depth].constraint.word_list = scanner->list_depths; scanner->aOptions[opt_resolution].constraint.word_list = scanner->list_resolutions; scanner->aOptions[opt_scantype].size = max_string_size (scanner->list_sources); scanner->aOptions[opt_scantype].constraint.string_list = scanner->list_sources; /* default values */ if (scanner->aValues[opt_colormode].s != NULL) free (scanner->aValues[opt_colormode].s); if (scanner->aValues[opt_scantype].s != NULL) free (scanner->aValues[opt_scantype].s); scanner->aValues[opt_colormode].s = strdup (scanner->list_colormodes[0]); scanner->aValues[opt_scantype].s = strdup (scanner->list_sources[0]); scanner->aValues[opt_resolution].w = scanner->list_resolutions[1]; scanner->aValues[opt_depth].w = scanner->list_depths[1]; source = Get_Source (scanner->aValues[opt_scantype].s); coords = Constrains_Get (device, source); if (coords != NULL) { bknd_constrains (scanner, source, 0); bknd_constrains (scanner, source, 1); scanner->aValues[opt_tlx].w = 0; scanner->aValues[opt_tly].w = 0; scanner->aValues[opt_brx].w = coords->width; scanner->aValues[opt_bry].w = coords->height; } } info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; } break; case opt_reset: Chipset_Reset (device); break; case opt_realdepth: scanner->aValues[optid].w = (scanner->cnv.real_depth == TRUE) ? SANE_TRUE : SANE_FALSE; break; case opt_infoupdate: if (bknd_info (scanner) == SANE_STATUS_GOOD) info |= SANE_INFO_RELOAD_OPTIONS; break; default: rst = SANE_STATUS_INVAL; break; } if (pInfo != NULL) *pInfo = info; } } return rst; } SANE_Status sane_control_option (SANE_Handle h, SANE_Int n, SANE_Action Action, void *pVal, SANE_Int * pInfo) { TScanner *scanner; SANE_Status rst; DBG (DBG_FNC, "> sane_control_option\n"); scanner = (TScanner *) h; switch (Action) { case SANE_ACTION_GET_VALUE: rst = option_get (scanner, n, pVal); break; case SANE_ACTION_SET_VALUE: rst = option_set (scanner, n, pVal, pInfo); break; case SANE_ACTION_SET_AUTO: rst = SANE_STATUS_UNSUPPORTED; break; default: rst = SANE_STATUS_INVAL; break; } return rst; } SANE_Status sane_get_parameters (SANE_Handle h, SANE_Parameters * p) { SANE_Status rst = SANE_STATUS_INVAL; TScanner *s = (TScanner *) h; DBG (DBG_FNC, "+ sane_get_parameters:"); if (s != NULL) { struct st_coords coords; SANE_Int res, source, depth, colormode, frameformat, bpl; /* first do some checks */ /* colormode */ colormode = Get_Colormode (s->aValues[opt_colormode].s); /* frameformat */ frameformat = (colormode == CM_COLOR) ? SANE_FRAME_RGB : SANE_FRAME_GRAY; /* depth */ depth = (colormode == CM_LINEART) ? 1 : s->aValues[opt_depth].w; /* scan type */ source = Get_Source (s->aValues[opt_scantype].s); /* resolution */ res = s->aValues[opt_resolution].w; /* image coordinates in millimeters */ coords.left = s->aValues[opt_tlx].w; coords.top = s->aValues[opt_tly].w; coords.width = s->aValues[opt_brx].w; coords.height = s->aValues[opt_bry].w; /* validate coords */ if (Translate_coords (&coords) == SANE_STATUS_GOOD) { Set_Coordinates (source, res, &coords); if (colormode != CM_LINEART) { bpl = coords.width * ((depth > 8) ? 2 : 1); if (colormode == CM_COLOR) bpl *= 3; /* three channels */ } else bpl = (coords.width + 7) / 8; /* return the data */ p->format = frameformat; p->last_frame = SANE_TRUE; p->depth = depth; p->lines = coords.height; p->pixels_per_line = coords.width; p->bytes_per_line = bpl; DBG (DBG_FNC, " -> Depth : %i\n", depth); DBG (DBG_FNC, " -> Height: %i\n", coords.height); DBG (DBG_FNC, " -> Width : %i\n", coords.width); DBG (DBG_FNC, " -> BPL : %i\n", bpl); rst = SANE_STATUS_GOOD; } } DBG (DBG_FNC, "- sane_get_parameters: %i\n", rst); return rst; } SANE_Status sane_start (SANE_Handle h) { SANE_Status rst = SANE_STATUS_INVAL; TScanner *s; DBG (DBG_FNC, "+ sane_start\n"); s = (TScanner *) h; if (s != NULL) { struct st_coords coords; SANE_Int res, source, colormode, depth, channel; /* first do some checks */ /* Get Scan type */ source = Get_Source (s->aValues[opt_scantype].s); /* Check if scanner supports slides and negatives in case selected source is tma */ if (!((source != ST_NORMAL) && (RTS_isTmaAttached (device) == FALSE))) { /* Get depth */ depth = s->aValues[opt_depth].w; /* Get color mode */ colormode = Get_Colormode (s->aValues[opt_colormode].s); /* Emulating certain color modes */ if (colormode == CM_LINEART) { /* emulate lineart */ s->cnv.colormode = CM_LINEART; colormode = CM_GRAY; depth = 8; } else if ((colormode == CM_GRAY) && (s->aValues[opt_emulategray].w == SANE_TRUE)) { /* emulate grayscale */ s->cnv.colormode = CM_GRAY; colormode = CM_COLOR; } else s->cnv.colormode = -1; /* setting channel for colormodes different than CM_COLOR */ channel = (colormode != CM_COLOR) ? 1 : 0; /* negative colors */ s->cnv.negative = (s->aValues[opt_negative].w == SANE_TRUE) ? TRUE : FALSE; /* Get threshold */ s->cnv.threshold = s->aValues[opt_threshold].w; /* Get resolution */ res = s->aValues[opt_resolution].w; /* set depth emulation */ if (s->cnv.colormode == CM_LINEART) s->cnv.real_depth = TRUE; else s->cnv.real_depth = (s->aValues[opt_realdepth].w == SANE_TRUE) ? TRUE : FALSE; /* use gamma? */ RTS_Debug->EnableGamma = (s->aValues[opt_nogamma].w == SANE_TRUE) ? FALSE : TRUE; /* disable white shading correction? */ RTS_Debug->wshading = (s->aValues[opt_nowshading].w == SANE_TRUE) ? FALSE : TRUE; /* skip warmup process? */ RTS_Debug->warmup = (s->aValues[opt_nowarmup].w == SANE_TRUE) ? FALSE : TRUE; /* save debugging images? */ RTS_Debug->SaveCalibFile = (s->aValues[opt_dbgimages].w == SANE_TRUE) ? TRUE : FALSE; /* Get image coordinates in millimeters */ coords.left = s->aValues[opt_tlx].w; coords.top = s->aValues[opt_tly].w; coords.width = s->aValues[opt_brx].w; coords.height = s->aValues[opt_bry].w; /* Validate coords */ if (Translate_coords (&coords) == SANE_STATUS_GOOD) { /* Stop previusly started scan */ RTS_Scanner_StopScan (device, TRUE); s->ScanParams.scantype = source; s->ScanParams.colormode = colormode; s->ScanParams.resolution_x = res; s->ScanParams.resolution_y = res; s->ScanParams.channel = channel; memcpy (&s->ScanParams.coords, &coords, sizeof (struct st_coords)); Set_Coordinates (source, res, &s->ScanParams.coords); /* emulating depth? */ if ((s->cnv.real_depth == FALSE) && (depth < 16) && (RTS_Debug->EnableGamma == TRUE)) { /* In order to improve image quality, we will scan at 16bits if we are using gamma correction */ s->cnv.depth = depth; s->ScanParams.depth = 16; } else { s->ScanParams.depth = depth; s->cnv.depth = -1; } /* set scanning parameters */ if (RTS_Scanner_SetParams (device, &s->ScanParams) == OK) { /* Start scanning process */ if (RTS_Scanner_StartScan (device) == OK) { /* Allocate buffer to read one line */ s->mylin = 0; rst = img_buffers_alloc (s, bytesperline); } } } } else rst = SANE_STATUS_COVER_OPEN; } DBG (DBG_FNC, "- sane_start: %i\n", rst); return rst; } SANE_Status sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len) { SANE_Status rst = SANE_STATUS_GOOD; TScanner *s = (TScanner *) h; DBG (DBG_FNC, "+ sane_read\n"); if ((s != NULL) && (buf != NULL) && (len != NULL)) { /* nothing has been read at the moment */ *len = 0; /* if we read all the lines return EOF */ if ((s->mylin == s->ScanParams.coords.height) || (device->status->cancel == TRUE)) { rst = (device->status->cancel == TRUE) ? SANE_STATUS_CANCELLED : SANE_STATUS_EOF; RTS_Scanner_StopScan (device, FALSE); img_buffers_free (s); } else { SANE_Int emul_len, emul_maxlen; SANE_Int thwidth, transferred, bufflength; SANE_Byte *buffer, *pbuffer; emul_len = 0; if (s->cnv.depth != -1) emul_maxlen = maxlen * (s->ScanParams.depth / s->cnv.depth); else emul_maxlen = maxlen; /* if grayscale emulation is enabled check that retrieved data is multiple of three */ if (s->cnv.colormode == CM_GRAY) { SANE_Int chn_size, rest; chn_size = (s->ScanParams.depth > 8) ? 2 : 1; rest = emul_maxlen % (3 * chn_size); if (rest != 0) emul_maxlen -= rest; } /* this is important to keep lines alignment in lineart mode */ if (s->cnv.colormode == CM_LINEART) emul_maxlen = s->ScanParams.coords.width; /* if we are emulating depth, we scan at 16bit when frontend waits for 8bit data. Next buffer will be used to retrieve data from scanner prior to convert to 8 bits depth */ buffer = (SANE_Byte *) malloc (emul_maxlen * sizeof (SANE_Byte)); if (buffer != NULL) { pbuffer = buffer; /* get bytes per line */ if (s->ScanParams.colormode != CM_LINEART) { thwidth = s->ScanParams.coords.width * ((s->ScanParams.depth > 8) ? 2 : 1); if (s->ScanParams.colormode == CM_COLOR) thwidth *= 3; /* three channels */ } else thwidth = (s->ScanParams.coords.width + 7) / 8; /* read as many lines the buffer may contain and while there are lines to be read */ while ((emul_len < emul_maxlen) && (s->mylin < s->ScanParams.coords.height)) { /* Is there any data waiting for being passed ? */ if (s->rest_amount != 0) { /* copy to buffer as many bytes as we can */ bufflength = min (emul_maxlen - emul_len, s->rest_amount); memcpy (pbuffer, s->rest, bufflength); emul_len += bufflength; pbuffer += bufflength; s->rest_amount -= bufflength; if (s->rest_amount == 0) s->mylin++; } else { /* read from scanner up to one line */ if (Read_Image (device, bytesperline, s->image, &transferred) != OK) { /* error, exit function */ rst = SANE_STATUS_EOF; break; } /* is there any data? */ if (transferred != 0) { /* copy to buffer as many bytes as we can */ bufflength = min (emul_maxlen - emul_len, thwidth); memcpy (pbuffer, s->image, bufflength); emul_len += bufflength; pbuffer += bufflength; /* the rest will be copied to s->rest buffer */ if (bufflength < thwidth) { s->rest_amount = thwidth - bufflength; memcpy (s->rest, s->image + bufflength, s->rest_amount); } else s->mylin++; } else break; } } /* while */ /* process buffer before sending to frontend */ if ((emul_len > 0) && (rst != SANE_STATUS_EOF)) { /* at this point ... buffer : contains retrieved image emul_len: contains size in bytes of retrieved image after this code ... buf : will contain postprocessed image len : will contain size in bytes of postprocessed image */ /* apply gamma if necessary */ if (RTS_Debug->EnableGamma == TRUE) gamma_apply (s, buffer, emul_len, s->ScanParams.depth); /* if we are scanning negatives, let's invert colors */ if (s->ScanParams.scantype == ST_NEG) { if (s->cnv.negative == FALSE) Color_Negative (buffer, emul_len, s->ScanParams.depth); } else if (s->cnv.negative != FALSE) Color_Negative (buffer, emul_len, s->ScanParams.depth); /* emulating grayscale ? */ if (s->cnv.colormode == CM_GRAY) { Color_to_Gray (buffer, emul_len, s->ScanParams.depth); emul_len /= 3; } /* emulating depth */ if (s->cnv.depth != -1) { switch (s->cnv.depth) { /* case 1: treated separately as lineart */ /*case 12: in the future */ case 8: Depth_16_to_8 (buffer, emul_len, buffer); emul_len /= 2; break; } } /* lineart mode ? */ if (s->cnv.colormode == CM_LINEART) { /* I didn't see any scanner supporting lineart mode. Windows drivers scan in grayscale and then convert image to lineart so let's perform conversion */ SANE_Int rest = emul_len % 8; Gray_to_Lineart (buffer, emul_len, s->cnv.threshold); emul_len /= 8; if (rest > 0) emul_len++; } /* copy postprocessed image */ *len = emul_len; memcpy (buf, buffer, *len); } free (buffer); } } } else rst = SANE_STATUS_EOF; DBG (DBG_FNC, "- sane_read: %s\n", sane_strstatus (rst)); return rst; } void sane_cancel (SANE_Handle h) { DBG (DBG_FNC, "> sane_cancel\n"); /* silence gcc */ (void) h; device->status->cancel = TRUE; } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { DBG (DBG_FNC, "> sane_set_io_mode\n"); /* silence gcc */ (void) handle; (void) non_blocking; return SANE_STATUS_UNSUPPORTED; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { DBG (DBG_FNC, "> sane_get_select_fd\n"); /* silence gcc */ (void) handle; (void) fd; return SANE_STATUS_UNSUPPORTED; } void sane_close (SANE_Handle h) { TScanner *scanner = (TScanner *) h; DBG (DBG_FNC, "- sane_close...\n"); /* stop previous scans */ RTS_Scanner_StopScan (device, TRUE); /* close usb */ sanei_usb_close (device->usb_handle); /* free scanner internal variables */ RTS_Scanner_End (device); /* free RTS environment */ RTS_Free (device); /* free backend variables */ if (scanner != NULL) { options_free (scanner); img_buffers_free (scanner); } } void sane_exit (void) { /* free device list memory */ if (_pSaneDevList) { TDevListEntry *pDev, *pNext; for (pDev = _pFirstSaneDev; pDev; pDev = pNext) { pNext = pDev->pNext; /* pDev->dev.name is the same pointer that pDev->devname */ free (pDev->devname); free (pDev); } _pFirstSaneDev = NULL; free (_pSaneDevList); _pSaneDevList = NULL; } } backends-1.3.0/backend/hp3900_types.c000066400000000000000000000447601456256263500172200ustar00rootroot00000000000000/* HP Scanjet 3900 series - Structures and global variables Copyright (C) 2005-2009 Jonathan Bravo Lopez 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, see . 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. */ /* devices */ #define DEVSCOUNT 0x09 /* Number of scanners supported by this backend */ #define HP3970 0x00 /* rts8822l-01H HP Scanjet 3970 */ #define HP4070 0x01 /* rts8822l-01H HP Scanjet 4070 */ #define HP4370 0x02 /* rts8822l-02A HP Scanjet 4370 */ #define UA4900 0x03 /* rts8822l-01H UMAX Astra 4900 */ #define HP3800 0x04 /* rts8822bl-03A HP Scanjet 3800 */ #define HPG3010 0x05 /* rts8822l-02A HP Scanjet G3010 */ #define BQ5550 0x06 /* rts8823l-01E BenQ 5550 */ #define HPG2710 0x07 /* rts8822bl-03A HP Scanjet G2710 */ #define HPG3110 0x08 /* rts8822l-02A HP Scanjet G3110 */ /* chipset models */ #define RTS8822L_01H 0x00 #define RTS8822L_02A 0x01 #define RTS8822BL_03A 0x02 #define RTS8823L_01E 0x03 /* chipset capabilities */ #define CAP_EEPROM 0x01 /* acceleration types */ #define ACC_CURVE 0x00 #define DEC_CURVE 0x01 /* curve types */ #define CRV_NORMALSCAN 0x00 #define CRV_PARKHOME 0x01 #define CRV_SMEARING 0x02 #define CRV_BUFFERFULL 0x03 /* Sample rates */ #define PIXEL_RATE 0x00 #define LINE_RATE 0x01 /* motor types */ #define MT_OUTPUTSTATE 0x00 #define MT_ONCHIP_PWM 0x01 /* motor step types */ #define STT_FULL 0x00 /* 90 degrees */ #define STT_HALF 0x01 /* 45 degrees */ #define STT_QUART 0x02 /* 22.5 degrees */ #define STT_OCT 0x03 /* 11.25 degrees */ /* motor options */ #define MTR_BACKWARD 0x00 #define MTR_FORWARD 0x08 #define MTR_ENABLED 0x00 #define MTR_DISABLED 0x10 /* sensors */ #define CCD_SENSOR 0x01 #define CIS_SENSOR 0x00 /* sony sensor models */ #define SNYS575 0x00 /* toshiba sensor models */ #define TCD2952 0x01 #define TCD2958 0x02 #define TCD2905 0x03 /* usb types */ #define USB20 0x01 #define USB11 0x00 /* scan types */ #define ST_NEG 0x03 #define ST_TA 0x02 #define ST_NORMAL 0x01 /* colour modes */ #define CM_COLOR 0x00 #define CM_GRAY 0x01 #define CM_LINEART 0x02 /* colour channels */ #define CL_RED 0x00 #define CL_GREEN 0x01 #define CL_BLUE 0x02 /* lamp types */ #define FLB_LAMP 0x01 #define TMA_LAMP 0x02 #define IST_NORMAL 0x00 #define IST_TA 0x01 #define IST_NEG 0x02 #define ICM_GRAY 0x00 #define ICM_LINEART 0x01 #define ICM_COLOR 0x02 #define TRUE 0x01 #define FALSE 0x00 /* function results */ #define OK 0x00 #define ERROR -1 #define RT_BUFFER_LEN 0x71a #define FIX_BY_HARD 0x01 #define FIX_BY_SOFT 0x02 #define REF_AUTODETECT 0x02 #define REF_TAKEFROMSCANNER 0x01 #define REF_NONE 0x00 /* bulk operations */ #define BLK_WRITE 0x00 #define BLK_READ 0x01 /* constants for resizing functions */ #define RSZ_NONE 0x00 #define RSZ_DECREASE 0x01 #define RSZ_INCREASE 0x02 #define RSZ_GRAYL 0x00 #define RSZ_COLOURL 0x01 #define RSZ_COLOURH 0x02 #define RSZ_LINEART 0x03 #define RSZ_GRAYH 0x04 /* Macros for managing data */ #define _B0(x) ((SANE_Byte)((x) & 0xFF)) #define _B1(x) ((SANE_Byte)((x) >> 0x08)) #define _B2(x) ((SANE_Byte)((x) >> 0x10)) #define _B3(x) ((SANE_Byte)((x) >> 0x18)) /* operation constants used in RTS_GetImage */ #define OP_STATIC_HEAD 0x00000001 #define OP_COMPRESSION 0x00000004 #define OP_BACKWARD 0x00000010 #define OP_WHITE_SHAD 0x00000020 #define OP_USE_GAMMA 0x00000040 #define OP_BLACK_SHAD 0x00000080 #define OP_LAMP_ON 0x00000200 /* data types */ typedef unsigned short USHORT; #ifdef STANDALONE /* Stand-alone*/ #define SANE_STATUS_GOOD 0x00 typedef unsigned char SANE_Byte; typedef int SANE_Int; typedef usb_dev_handle *USB_Handle; #else /* SANE backend */ typedef SANE_Int USB_Handle; #endif /* structures */ struct st_debug_opts { /* device capabilities */ SANE_Int dev_model; SANE_Byte SaveCalibFile; SANE_Byte DumpShadingData; SANE_Byte ScanWhiteBoard; SANE_Byte EnableGamma; SANE_Byte use_fixed_pwm; SANE_Int dmabuffersize; SANE_Int dmatransfersize; SANE_Int dmasetlength; SANE_Int usbtype; SANE_Int calibrate; SANE_Int wshading; SANE_Int overdrive_flb; SANE_Int overdrive_ta; SANE_Byte warmup; SANE_Int shd; }; struct st_chip { SANE_Int model; SANE_Int capabilities; char *name; }; struct st_shading { double *rates; SANE_Int count; SANE_Int ptr; }; struct st_scanning { SANE_Byte *imagebuffer; SANE_Byte *imagepointer; SANE_Int bfsize; SANE_Int channel_size; /* arrange line related variables */ SANE_Int arrange_hres; SANE_Int arrange_compression; SANE_Int arrange_sensor_evenodd_dist; SANE_Int arrange_orderchannel; SANE_Int arrange_size; /* Pointers to each channel colour */ SANE_Byte *pColour[3]; SANE_Byte *pColour1[3]; SANE_Byte *pColour2[3]; /* Channel displacements */ SANE_Int desp[3]; SANE_Int desp1[3]; SANE_Int desp2[3]; }; struct st_resize { SANE_Byte mode; SANE_Int type; SANE_Int fromwidth; SANE_Int towidth; SANE_Int bytesperline; SANE_Int rescount; SANE_Int resolution_x; SANE_Int resolution_y; SANE_Byte *v3624; SANE_Byte *v3628; SANE_Byte *v362c; }; struct st_gammatables { SANE_Int depth; /*0=0x100| 4=0x400 |8=0x1000 */ SANE_Byte *table[3]; }; struct st_readimage { SANE_Int Size4Lines; SANE_Byte Starting; SANE_Byte *DMABuffer; SANE_Int DMABufferSize; SANE_Byte *RDStart; SANE_Int RDSize; SANE_Int DMAAmount; SANE_Int Channel_size; SANE_Byte Channels_per_dot; SANE_Int ImageSize; SANE_Int Bytes_Available; SANE_Int Max_Size; SANE_Byte Cancel; }; struct st_gain_offset { /* 32 bytes 08be|08e0|3654 red green blue */ SANE_Int edcg1[3]; /* 08e0|08e2|08e4 *//*Even offset 1 */ SANE_Int edcg2[3]; /* 08e6|08e8|08ea *//*Even offset 2 */ SANE_Int odcg1[3]; /* 08ec|08ee|08f0 *//*Odd offset 1 */ SANE_Int odcg2[3]; /* 08f2|08f4|08f6 *//*Odd offset 2 */ SANE_Byte pag[3]; /* 08f8|08f9|08fa */ SANE_Byte vgag1[3]; /* 08fb|08fc|08fd */ SANE_Byte vgag2[3]; /* 08fe|08ff|0900 */ }; struct st_calibration_config { SANE_Int WStripXPos; SANE_Int WStripYPos; SANE_Int BStripXPos; SANE_Int BStripYPos; SANE_Int WRef[3]; SANE_Int BRef[3]; SANE_Byte RefBitDepth; double OffsetTargetMax; double OffsetTargetMin; double OffsetBoundaryRatio1; double OffsetBoundaryRatio2; double OffsetAvgRatio1; double OffsetAvgRatio2; SANE_Int CalibOffset10n; SANE_Int CalibOffset20n; SANE_Int AdcOffEvenOdd; SANE_Int AdcOffQuickWay; SANE_Int OffsetEven1[3]; SANE_Int OffsetOdd1[3]; SANE_Int OffsetEven2[3]; SANE_Int OffsetOdd2[3]; SANE_Byte OffsetHeight; SANE_Int OffsetPixelStart; SANE_Int OffsetNPixel; SANE_Byte OffsetNSigma; SANE_Int AdcOffPredictStart; SANE_Int AdcOffPredictEnd; SANE_Byte OffsetAvgTarget[3]; SANE_Byte OffsetTuneStep1; SANE_Byte OffsetTuneStep2; double GainTargetFactor; SANE_Int CalibGain10n; SANE_Int CalibGain20n; SANE_Int CalibPAGOn; SANE_Int GainHeight; SANE_Int unk1[3]; SANE_Int unk2[3]; SANE_Byte PAG[3]; SANE_Byte Gain1[3]; SANE_Byte Gain2[3]; /* White Shading */ SANE_Int WShadingOn; SANE_Int WShadingHeight; SANE_Int WShadingPreDiff[3]; SANE_Int unknown; /*?? */ double ShadingCut[3]; /* Black Shading */ SANE_Int BShadingOn; SANE_Int BShadingHeight; SANE_Int BShadingDefCutOff; SANE_Int BShadingPreDiff[3]; double ExternBoundary; SANE_Int EffectivePixel; SANE_Byte TotShading; }; struct st_calibration { /* faac */ struct st_gain_offset gain_offset; /* 0..35 */ USHORT *white_shading[3]; /* +36 +40 +44 */ USHORT *black_shading[3]; /* +48 +52 +56 */ SANE_Int WRef[3]; /* +60 +62 +64 */ SANE_Byte shading_type; /* +66 */ SANE_Byte shading_enabled; /* +67 */ SANE_Int first_position; /* +68 */ SANE_Int shadinglength; /* +72 */ }; struct st_cal2 { /* f9f8 35 bytes */ SANE_Int table_count; /* +0 f9f8 */ SANE_Int shadinglength1; /* +4 f9fc */ SANE_Int tables_size; /* +8 fa00 */ SANE_Int shadinglength3; /* +12 fa04 */ USHORT *tables[4]; /* +16+20+24+28 fa08 fa0c fa10 fa14 */ USHORT *table2; /* +32 fa18 */ }; struct st_coords { SANE_Int left; SANE_Int width; SANE_Int top; SANE_Int height; }; struct params { SANE_Int scantype; SANE_Int colormode; SANE_Int resolution_x; SANE_Int resolution_y; struct st_coords coords; SANE_Int depth; SANE_Int channel; }; struct st_constrains { struct st_coords reflective; struct st_coords negative; struct st_coords slide; }; struct st_scanparams /* 44 bytes size */ { /* 760-78b|155c-1587|fa58-fa83|f0c4 */ SANE_Byte colormode; /* [+00] 760 */ SANE_Byte depth; /* [+01] 761 */ SANE_Byte samplerate; /* [+02] 762 */ SANE_Byte timing; /* [+03] 763 */ SANE_Int channel; /* [+04] 764 */ SANE_Int sensorresolution; /* [+06] 766 */ SANE_Int resolution_x; /* [+08] 768 */ SANE_Int resolution_y; /* [+10] 76a */ struct st_coords coord; /* [+12] left */ /* [+16] width */ /* [+20] top */ /* [+24] height */ SANE_Int shadinglength; /* [+28] 77c */ SANE_Int v157c; /* [+32] 780 */ SANE_Int bytesperline; /* [+36] 784 */ SANE_Int expt; /* [+40] 788 */ SANE_Int startpos; /* [+44] 78c */ SANE_Int leftleading; /* [+46] 78e */ SANE_Int ser; /* [+48] 790 */ SANE_Int ler; /* [+52] 794 */ SANE_Int scantype; /* [+58] 79a */ }; struct st_hwdconfig /* 28 bytes size */ { /* fa84-fa9f|f0ac-f0c7|e838-e853|f3a4-f3bf */ SANE_Int startpos; /* +0 */ /* +1..7 */ SANE_Byte arrangeline; /* +8 */ SANE_Byte scantype; /* +9 */ SANE_Byte compression; /* +10 */ SANE_Byte use_gamma_tables; /* +11 */ SANE_Byte gamma_tablesize; /* +12 */ SANE_Byte white_shading; /* +13 */ SANE_Byte black_shading; /* +14 */ SANE_Byte unk3; /* +15 */ SANE_Byte motorplus; /* +16 */ SANE_Byte static_head; /* +17 */ SANE_Byte motor_direction; /* +18 */ SANE_Byte dummy_scan; /* +19 */ SANE_Byte highresolution; /* +20 */ SANE_Byte sensorevenodddistance; /* +21 */ /* +22..23 */ SANE_Int calibrate; /* +24 */ }; struct st_calibration_data { SANE_Byte Regs[RT_BUFFER_LEN]; struct st_scanparams scancfg; struct st_gain_offset gain_offset; }; struct st_cph { double p1; double p2; SANE_Byte ps; SANE_Byte ge; SANE_Byte go; }; struct st_timing { SANE_Int sensorresolution; SANE_Byte cnpp; SANE_Byte cvtrp[3]; /* 3 transfer gates */ SANE_Byte cvtrw; SANE_Byte cvtrfpw; SANE_Byte cvtrbpw; struct st_cph cph[6]; /* Linear Image Sensor Clocks */ SANE_Int cphbp2s; SANE_Int cphbp2e; SANE_Int clamps; SANE_Int clampe; SANE_Byte cdss[2]; SANE_Byte cdsc[2]; SANE_Byte cdscs[2]; /* Toshiba T958 ccd from hp4370 */ double adcclkp[2]; SANE_Int adcclkp2e; }; struct st_scanmode { SANE_Int scantype; SANE_Int colormode; SANE_Int resolution; SANE_Byte timing; SANE_Int motorcurve; SANE_Byte samplerate; SANE_Byte systemclock; SANE_Int ctpc; SANE_Int motorbackstep; SANE_Byte scanmotorsteptype; SANE_Byte dummyline; SANE_Int expt[3]; SANE_Int mexpt[3]; SANE_Int motorplus; SANE_Int multiexposurefor16bitmode; SANE_Int multiexposureforfullspeed; SANE_Int multiexposure; SANE_Int mri; SANE_Int msi; SANE_Int mmtir; SANE_Int mmtirh; SANE_Int skiplinecount; }; struct st_motormove { SANE_Byte systemclock; SANE_Int ctpc; SANE_Byte scanmotorsteptype; SANE_Int motorcurve; }; struct st_motorpos { SANE_Int coord_y; SANE_Byte options; SANE_Int v12e448; SANE_Int v12e44c; }; struct st_find_edge { SANE_Int exposuretime; SANE_Int scanystart; SANE_Int scanylines; SANE_Int findlermethod; SANE_Int findlerstart; SANE_Int findlerend; SANE_Int checkoffsetser; SANE_Int findserchecklines; SANE_Int findserstart; SANE_Int findserend; SANE_Int findsermethod; SANE_Int offsettoser; SANE_Int offsettoler; }; struct st_curve { SANE_Int crv_speed; /* acceleration or deceleration */ SANE_Int crv_type; SANE_Int step_count; SANE_Int *step; }; struct st_motorcurve { SANE_Int mri; SANE_Int msi; SANE_Int skiplinecount; SANE_Int motorbackstep; SANE_Int curve_count; struct st_curve **curve; }; struct st_checkstable { double diff; SANE_Int interval; long tottime; }; struct st_sensorcfg { SANE_Int type; SANE_Int name; SANE_Int resolution; SANE_Int channel_color[3]; SANE_Int channel_gray[2]; SANE_Int rgb_order[3]; SANE_Int line_distance; SANE_Int evenodd_distance; }; struct st_autoref { SANE_Byte type; SANE_Int offset_x; SANE_Int offset_y; SANE_Int resolution; SANE_Int extern_boundary; }; struct st_motorcfg { SANE_Byte type; SANE_Int resolution; SANE_Byte pwmfrequency; SANE_Int basespeedpps; SANE_Int basespeedmotormove; SANE_Int highspeedmotormove; SANE_Int parkhomemotormove; SANE_Byte changemotorcurrent; }; struct st_buttons { SANE_Int count; SANE_Int mask[6]; /* up to 6 buttons */ }; struct st_status { SANE_Byte warmup; SANE_Byte parkhome; SANE_Byte cancel; }; struct st_device { /* next var handles usb device, used for every usb operations */ USB_Handle usb_handle; /* next buffer will contain initial state registers of the chipset */ SANE_Byte *init_regs; /* next structure will contain information and capabilities about chipset */ struct st_chip *chipset; /* next structure will contain general configuration of stepper motor */ struct st_motorcfg *motorcfg; /* next structure will contain general configuration of ccd sensor */ struct st_sensorcfg *sensorcfg; /* next structure will contain all ccd timing values */ SANE_Int timings_count; struct st_timing **timings; /* next structure will contain all possible motor movements */ SANE_Int motormove_count; struct st_motormove **motormove; /* next structure will contain all motorcurve values */ SANE_Int mtrsetting_count; struct st_motorcurve **mtrsetting; /* next structure will contain all possible scanning modes for one scanner */ SANE_Int scanmodes_count; struct st_scanmode **scanmodes; /* next structure contains constrain values for one scanner */ struct st_constrains *constrains; /* next structure contains supported buttons and their order */ struct st_buttons *buttons; /* next structure will be used to resize scanned image */ struct st_resize *Resize; /* next structure will be used while reading image from device */ struct st_readimage *Reading; /* next structure will be used to arrange color channels while scanning */ struct st_scanning *scanning; /* next structure will contain some status which can be requested */ struct st_status *status; }; /* Unknown vars */ SANE_Int v14b4 = 0; SANE_Byte *v1600 = NULL; /* tabla */ SANE_Byte *v1604 = NULL; /* tabla */ SANE_Byte *v1608 = NULL; /* tabla */ SANE_Byte v160c_block_size; SANE_Int mem_total; SANE_Byte v1619; SANE_Int v15f8; SANE_Int acccurvecount; /* counter used y MotorSetup */ SANE_Int deccurvecount; /* counter used y MotorSetup */ SANE_Int smearacccurvecount; /* counter used y MotorSetup */ SANE_Int smeardeccurvecount; /* counter used y MotorSetup */ /* Known vars */ SANE_Int offset[3]; SANE_Byte gain[3]; static SANE_Int usbfile = -1; SANE_Int scantype; SANE_Byte pwmlamplevel; SANE_Byte arrangeline; SANE_Byte binarythresholdh; SANE_Byte binarythresholdl; SANE_Byte shadingbase; SANE_Byte shadingfact[3]; SANE_Byte arrangeline; SANE_Int compression; SANE_Byte linedarlampoff; SANE_Int pixeldarklevel; SANE_Int bw_threshold = 0x00; /* SetScanParams */ struct st_scanparams scan; struct st_scanparams scan2; SANE_Int bytesperline; /* width * (3 colors [RGB]) */ SANE_Int imagewidth3; SANE_Int lineart_width; SANE_Int imagesize; /* bytesperline * coords.height */ SANE_Int imageheight; SANE_Int line_size; SANE_Int v15b4; SANE_Int v15bc; SANE_Int waitforpwm; SANE_Byte WRef[3]; USHORT *fixed_black_shading[3] = { NULL, NULL, NULL }; USHORT *fixed_white_shading[3] = { NULL, NULL, NULL }; /* Calibration */ struct st_gain_offset mitabla2; /* calibration table */ SANE_Int v0750; static SANE_Byte use_gamma_tables; SANE_Int read_v15b4 = 0; SANE_Int v35b8 = 0; SANE_Int arrangeline2; SANE_Int v07c0 = 0; /* next structure contains coefficients for white shading correction */ struct st_shading *wshading; struct st_gammatables *hp_gamma; struct st_gain_offset *default_gain_offset; struct st_calibration_data *calibdata; struct st_debug_opts *RTS_Debug; /* testing */ SANE_Byte *jkd_black = NULL; SANE_Int jkd_blackbpl; backends-1.3.0/backend/hp3900_usb.c000066400000000000000000000323031456256263500166330ustar00rootroot00000000000000/* HP Scanjet 3900 series - USB layer Copyright (C) 2005-2008 Jonathan Bravo Lopez 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, see . 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 USBLAYER #define USBLAYER #define TIMEOUT 1000 #define BLK_READ_EP 0x81 #define BLK_WRITE_EP 0x02 SANE_Int dataline_count = 0; /* USB layer commands */ static SANE_Int usb_ctl_write (USB_Handle usb_handle, SANE_Int address, SANE_Byte * buffer, SANE_Int size, SANE_Int index); static SANE_Int usb_ctl_read (USB_Handle usb_handle, SANE_Int address, SANE_Byte * buffer, SANE_Int size, SANE_Int index); /* Higher level commands*/ static SANE_Int IRead_Byte (USB_Handle usb_handle, SANE_Int address, SANE_Byte * data, SANE_Int index); static SANE_Int IRead_Word (USB_Handle usb_handle, SANE_Int address, SANE_Int * data, SANE_Int index); static SANE_Int IRead_Integer (USB_Handle usb_handle, SANE_Int address, SANE_Int * data, SANE_Int index); static SANE_Int IRead_Buffer (USB_Handle usb_handle, SANE_Int address, SANE_Byte * buffer, SANE_Int size, SANE_Int index); static SANE_Int IWrite_Byte (USB_Handle usb_handle, SANE_Int address, SANE_Byte data, SANE_Int index1, SANE_Int index2); static SANE_Int IWrite_Word (USB_Handle usb_handle, SANE_Int address, SANE_Int data, SANE_Int index); static SANE_Int IWrite_Integer (USB_Handle usb_handle, SANE_Int address, SANE_Int data, SANE_Int index); static SANE_Int IWrite_Buffer (USB_Handle usb_handle, SANE_Int address, SANE_Byte * buffer, SANE_Int size, SANE_Int index); static SANE_Int Read_Byte (USB_Handle usb_handle, SANE_Int address, SANE_Byte * data); static SANE_Int Read_Word (USB_Handle usb_handle, SANE_Int address, SANE_Int * data); static SANE_Int Read_Integer (USB_Handle usb_handle, SANE_Int address, SANE_Int * data); static SANE_Int Read_Buffer (USB_Handle usb_handle, SANE_Int address, SANE_Byte * buffer, SANE_Int size); static SANE_Int Read_Bulk (USB_Handle usb_handle, SANE_Byte * buffer, size_t size); static SANE_Int Write_Byte (USB_Handle usb_handle, SANE_Int address, SANE_Byte data); static SANE_Int Write_Word (USB_Handle usb_handle, SANE_Int address, SANE_Int data); /*static SANE_Int Write_Integer (USB_Handle usb_handle, SANE_Int address, SANE_Int data);*/ static SANE_Int Write_Buffer (USB_Handle usb_handle, SANE_Int address, SANE_Byte * buffer, SANE_Int size); static SANE_Int Write_Bulk (USB_Handle usb_handle, SANE_Byte * buffer, SANE_Int size); static SANE_Int show_buffer (SANE_Int level, SANE_Byte * buffer, SANE_Int size); /* Implementation */ static SANE_Int IWrite_Byte (USB_Handle usb_handle, SANE_Int address, SANE_Byte data, SANE_Int index1, SANE_Int index2) { SANE_Int rst = ERROR; SANE_Byte buffer[2] = { 0x00, 0x00 }; if (usb_ctl_read (usb_handle, address + 1, buffer, 0x02, index1) == 2) { buffer[1] = (buffer[0] & 0xff); buffer[0] = (data & 0xff); if (usb_ctl_write (usb_handle, address, buffer, 0x02, index2) == 2) rst = OK; } return rst; } static SANE_Int IWrite_Word (USB_Handle usb_handle, SANE_Int address, SANE_Int data, SANE_Int index) { SANE_Int rst = ERROR; SANE_Byte buffer[2]; buffer[0] = (data & 0xff); buffer[1] = ((data >> 8) & 0xff); if (usb_ctl_write (usb_handle, address, buffer, 0x02, index) == 2) rst = OK; return rst; } static SANE_Int IWrite_Integer (USB_Handle usb_handle, SANE_Int address, SANE_Int data, SANE_Int index) { SANE_Int rst = ERROR; SANE_Byte buffer[4]; buffer[0] = (data & 0xff); buffer[1] = ((data >> 8) & 0xff); buffer[2] = ((data >> 16) & 0xff); buffer[3] = ((data >> 24) & 0xff); if (usb_ctl_write (usb_handle, address, buffer, 0x04, index) == 4) rst = OK; return rst; } static SANE_Int IWrite_Buffer (USB_Handle usb_handle, SANE_Int address, SANE_Byte * buffer, SANE_Int size, SANE_Int index) { SANE_Int ret = ERROR; if (!((buffer == NULL) && (size > 0))) if (usb_ctl_write (usb_handle, address, buffer, size, index) == size) ret = OK; return ret; } static SANE_Int IRead_Byte (USB_Handle usb_handle, SANE_Int address, SANE_Byte * data, SANE_Int index) { SANE_Byte buffer[2] = { 0x00, 0x00 }; SANE_Int ret = ERROR; if (data != NULL) if (usb_ctl_read (usb_handle, address, buffer, 0x02, index) == 2) { *data = (SANE_Byte) (buffer[0] & 0xff); ret = OK; } return ret; } static SANE_Int IRead_Word (USB_Handle usb_handle, SANE_Int address, SANE_Int * data, SANE_Int index) { SANE_Byte buffer[2] = { 0x00, 0x00 }; SANE_Int ret = ERROR; if (data != NULL) if (usb_ctl_read (usb_handle, address, buffer, 0x02, index) == 2) { *data = ((buffer[1] << 8) & 0xffff) + (buffer[0] & 0xff); ret = OK; } return ret; } static SANE_Int IRead_Integer (USB_Handle usb_handle, SANE_Int address, SANE_Int * data, SANE_Int index) { SANE_Byte buffer[4] = { 0x00, 0x00, 0x00, 0x00 }; SANE_Int ret = ERROR; if (data != NULL) { *data = 0; if (usb_ctl_read (usb_handle, address, buffer, 0x04, index) == 4) { SANE_Int C; for (C = 3; C >= 0; C--) *data = ((*data << 8) + (buffer[C] & 0xff)) & 0xffffffff; ret = OK; } } return ret; } static SANE_Int IRead_Buffer (USB_Handle usb_handle, SANE_Int address, SANE_Byte * buffer, SANE_Int size, SANE_Int index) { SANE_Int ret = ERROR; if (buffer != NULL) if (usb_ctl_read (usb_handle, address, buffer, size, index) == size) ret = OK; return ret; } static SANE_Int Write_Byte (USB_Handle usb_handle, SANE_Int address, SANE_Byte data) { return IWrite_Byte (usb_handle, address, data, 0x100, 0); } static SANE_Int Write_Word (USB_Handle usb_handle, SANE_Int address, SANE_Int data) { return IWrite_Word (usb_handle, address, data, 0); } /*static SANE_Int Write_Integer(USB_Handle usb_handle, SANE_Int address, SANE_Int data) { return IWrite_Integer(usb_handle, address, data, 0); }*/ static SANE_Int Write_Buffer (USB_Handle usb_handle, SANE_Int address, SANE_Byte * buffer, SANE_Int size) { return IWrite_Buffer (usb_handle, address, buffer, size, 0); } static SANE_Int Read_Byte (USB_Handle usb_handle, SANE_Int address, SANE_Byte * data) { return IRead_Byte (usb_handle, address, data, 0x100); } static SANE_Int Read_Word (USB_Handle usb_handle, SANE_Int address, SANE_Int * data) { return IRead_Word (usb_handle, address, data, 0x100); } static SANE_Int Read_Integer (USB_Handle usb_handle, SANE_Int address, SANE_Int * data) { return IRead_Integer (usb_handle, address, data, 0x100); } static SANE_Int Read_Buffer (USB_Handle usb_handle, SANE_Int address, SANE_Byte * buffer, SANE_Int size) { return IRead_Buffer (usb_handle, address, buffer, size, 0x100); } static SANE_Int Write_Bulk (USB_Handle usb_handle, SANE_Byte * buffer, SANE_Int size) { SANE_Int rst = ERROR; if (buffer != NULL) { dataline_count++; DBG (DBG_CTL, "%06i BLK DO: %i. bytes\n", dataline_count, size); show_buffer (4, buffer, size); #ifdef STANDALONE if (usb_handle != NULL) if (usb_bulk_write (usb_handle, BLK_WRITE_EP, (char *) buffer, size, TIMEOUT) == size) rst = OK; #else if (usb_handle != -1) { size_t mysize = size; if (sanei_usb_write_bulk (usb_handle, buffer, &mysize) == SANE_STATUS_GOOD) rst = OK; } #endif } if (rst != OK) DBG (DBG_CTL, " : Write_Bulk error\n"); return rst; } static SANE_Int Read_Bulk (USB_Handle usb_handle, SANE_Byte * buffer, size_t size) { SANE_Int rst = ERROR; if (buffer != NULL) { dataline_count++; DBG (DBG_CTL, "%06i BLK DI: Buffer length = %lu. bytes\n", dataline_count, (u_long) size); #ifdef STANDALONE if (usb_handle != NULL) rst = usb_bulk_read (usb_handle, BLK_READ_EP, (char *) buffer, size, TIMEOUT); #else if (usb_handle != -1) if (sanei_usb_read_bulk (usb_handle, buffer, &size) == SANE_STATUS_GOOD) rst = size; #endif } if (rst < 0) DBG (DBG_CTL, " : Read_Bulk error\n"); else show_buffer (4, buffer, rst); return rst; } static SANE_Int usb_ctl_write (USB_Handle usb_handle, SANE_Int address, SANE_Byte * buffer, SANE_Int size, SANE_Int index) { SANE_Int rst = ERROR; dataline_count++; DBG (DBG_CTL, "%06i CTL DO: 40 04 %04x %04x %04x\n", dataline_count, address & 0xffff, index, size); show_buffer (DBG_CTL, buffer, size); #ifdef STANDALONE if (usb_handle != NULL) rst = usb_control_msg (usb_handle, 0x40, /* Request type */ 0x04, /* Request */ address, /* Value */ index, /* Index */ (char *) buffer, /* Buffer */ size, /* Size */ TIMEOUT); #else if (usb_handle != -1) { if (sanei_usb_control_msg (usb_handle, 0x40, /* Request type */ 0x04, /* Request */ address, /* Value */ index, /* Index */ size, /* Size */ buffer) /* Buffer */ == SANE_STATUS_GOOD) rst = size; else rst = -1; } #endif if (rst < 0) DBG (DBG_CTL, " : Error, returned %i\n", rst); return rst; } static SANE_Int usb_ctl_read (USB_Handle usb_handle, SANE_Int address, SANE_Byte * buffer, SANE_Int size, SANE_Int index) { SANE_Int rst; rst = ERROR; dataline_count++; DBG (DBG_CTL, "%06i CTL DI: c0 04 %04x %04x %04x\n", dataline_count, address & 0xffff, index, size); #ifdef STANDALONE if (usb_handle != NULL) rst = usb_control_msg (usb_handle, 0xc0, /* Request type */ 0x04, /* Request */ address, /* Value */ index, /* Index */ (char *) buffer, /* Buffer */ size, /* Size */ TIMEOUT); #else if (usb_handle != -1) { if (sanei_usb_control_msg (usb_handle, 0xc0, /* Request type */ 0x04, /* Request */ address, /* Value */ index, /* Index */ size, /* Size */ buffer) /* Buffer */ == SANE_STATUS_GOOD) rst = size; else rst = -1; } #endif if (rst < 0) DBG (DBG_CTL, " : Error, returned %i\n", rst); else show_buffer (DBG_CTL, buffer, rst); return rst; } static SANE_Int show_buffer (SANE_Int level, SANE_Byte * buffer, SANE_Int size) { if (DBG_LEVEL >= level) { char *sline = NULL; char *sdata = NULL; SANE_Int cont, data, offset = 0, col = 0; if ((size > 0) && (buffer != NULL)) { sline = (char *) malloc (256); if (sline != NULL) { sdata = (char *) malloc (256); if (sdata != NULL) { memset (sline, 0, 256); for (cont = 0; cont < size; cont++) { if (col == 0) { if (cont == 0) snprintf (sline, 255, " BF: "); else snprintf (sline, 255, " "); } data = (buffer[cont] & 0xff); snprintf (sdata, 255, "%02x ", data); sline = strcat (sline, sdata); col++; offset++; if (col == 8) { col = 0; snprintf (sdata, 255, " : %i\n", offset - 8); sline = strcat (sline, sdata); DBG (level, "%s", sline); memset (sline, 0, 256); } } if (col > 0) { for (cont = col; cont < 8; cont++) { snprintf (sdata, 255, "-- "); sline = strcat (sline, sdata); offset++; } snprintf (sdata, 255, " : %i\n", offset - 8); sline = strcat (sline, sdata); DBG (level, "%s", sline); memset (sline, 0, 256); } free (sdata); } free (sline); } } else DBG (level, " BF: Empty buffer\n"); } return OK; } #endif /*USBLAYER*/ backends-1.3.0/backend/hp4200.c000066400000000000000000002245771456256263500157740ustar00rootroot00000000000000/* Copyright (C) 2000 by Adrian Perez Jorge 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, see . */ /* Developers: Adrian Perez Jorge (APJ) - Creator of the original HP4200C backend code. adrianpj@easynews.com Andrew John Lewis (AJL) - lewi0235@tc.umn.edu Arnar Mar Hrafnkelsson (AMH) - addi@umich.edu Frank Zago some cleanups and integration into SANE Henning Meier-Geinitz more cleanups, bug fixes TODO: - support more scanning resolutions. - support different color depths. - support gray and lineart. - improve scanning speed. Compute scanning parameters based on the image size and the scanner-to-host bandwidth. - improve image quality. - fix problem concerning mangled images */ #define BUILD 2 #define BACKEND_NAME hp4200 #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/sanei_debug.h" #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_pv8630.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_backend.h" #include "hp4200.h" #include "hp4200_lm9830.c" #define HP4200_CONFIG_FILE "hp4200.conf" /*--------------------------------------------------------------------------*/ #if 0 /* Some of these resolution need work in color shifting. */ static const SANE_Int dpi_list[] = { 8, 50, 75, 100, 150, 200, 300, 400, 600 }; #else static const SANE_Int dpi_list[] = { 4, 75, 150, 300, 600 }; #endif static SANE_Range x_range = { SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0 }; static SANE_Range y_range = { SANE_FIX (0), SANE_FIX (11.75 * MM_PER_INCH), 0 }; static const SANE_Range u8_range = { 0, 255, 0 }; struct coarse_t { int min_red; int min_green; int min_blue; int max_red; int max_green; int max_blue; int red_gain; int red_offset; int green_gain; int green_offset; int blue_gain; int blue_offset; }; static const double hdpi_mapping[8] = { 1, 1.5, 2, 3, 4, 6, 8, 12 }; static HP4200_Device *first_device = NULL; /* device list head */ static int n_devices = 0; /* the device count */ static const SANE_Device **devlist = NULL; static unsigned char getreg (HP4200_Scanner * s, unsigned char reg) { unsigned char reg_value; if ((reg > 0x08) && (reg < 0x5b)) return (unsigned char) LOBYTE (s->regs[reg]); else { lm9830_read_register (s->fd, reg, ®_value); return reg_value; } } static void setreg (HP4200_Scanner * s, unsigned char reg, unsigned char reg_value) { s->regs[reg] = reg_value; /* dirty bit should be clear with this */ if ((reg < 0x08) || (reg > 0x5b)) { lm9830_write_register (s->fd, reg, reg_value); } } static void setbits (HP4200_Scanner * s, unsigned char reg, unsigned char bitmap) { s->regs[reg] = (s->regs[reg] & 0xff) | bitmap; if ((reg < 0x08) || (reg > 0x5b)) { lm9830_write_register (s->fd, reg, LOBYTE (s->regs[reg])); } } static void clearbits (HP4200_Scanner * s, unsigned char reg, unsigned char mask) { s->regs[reg] = (s->regs[reg] & ~mask) & 0xff; if ((reg < 0x08) || (reg > 0x5b)) { lm9830_write_register (s->fd, reg, LOBYTE (s->regs[reg])); } } static int cache_write (HP4200_Scanner * s) { int i; #ifdef DEBUG_REG_CACHE int counter = 0; #endif DBG (DBG_proc, "Writing registers\n"); for (i = 0; i < 0x80; i++) if (!(s->regs[i] & 0x100)) { /* modified register */ #ifdef DEBUG_REG_CACHE fprintf (stderr, "%.2x", i); if (counter == 8) fprintf (stderr, "\n"); else fprintf (stderr, ", "); counter = (counter + 1) % 9; #endif lm9830_write_register (s->fd, i, s->regs[i]); s->regs[i] |= 0x100; /* register is updated */ } return 0; } /* * HP4200-dependent register initialization. */ static int hp4200_init_registers (HP4200_Scanner * s) { /* set up hardware parameters */ s->hw_parms.crystal_frequency = 48000000; s->hw_parms.SRAM_size = 128; /* Kb */ s->hw_parms.scan_area_width = 5100; /* pixels */ s->hw_parms.scan_area_length = 11; /* inches */ s->hw_parms.min_pixel_data_buffer_limit = 1024; /* bytes */ s->hw_parms.sensor_line_separation = 4; /* lines */ s->hw_parms.sensor_max_integration_time = 12; /* milliseconds */ s->hw_parms.home_sensor = 2; s->hw_parms.sensor_resolution = 1; /* 600 dpi */ s->hw_parms.motor_full_steps_per_inch = 300; s->hw_parms.motor_max_speed = 1.4; /* inches/second */ s->hw_parms.num_tr_pulses = 1; s->hw_parms.guard_band_duration = 1; s->hw_parms.pulse_duration = 3; s->hw_parms.fsteps_25_speed = 3; s->hw_parms.fsteps_50_speed = 3; s->hw_parms.target_value.red = 1000; s->hw_parms.target_value.green = 1000; s->hw_parms.target_value.blue = 1000; { int i; /* * we are using a cache-like data structure so registers whose * values were written to the lm9830 and aren't volatile, have * bit 0x100 activated. This bit must be cleared if you want the * value to be written to the chip once cache_write() is called. */ /* clears the registers cache */ memset (s->regs, 0, sizeof (s->regs)); /* * registers 0x00 - 0x07 are non-cacheable/volatile, so don't * read the values using the cache. Instead use direct functions * to read/write registers. */ for (i = 0; i < 0x08; i++) s->regs[i] = 0x100; } setreg (s, 0x70, 0x70); /* noise filter */ setreg (s, 0x0b, INPUT_SIGNAL_POLARITY_NEGATIVE | CDS_ON | SENSOR_STANDARD | SENSOR_RESOLUTION_600 | LINE_SKIPPING_COLOR_PHASE_DELAY (0)); setreg (s, 0x0c, PHI1_POLARITY_POSITIVE | PHI2_POLARITY_POSITIVE | RS_POLARITY_POSITIVE | CP1_POLARITY_POSITIVE | CP2_POLARITY_POSITIVE | TR1_POLARITY_NEGATIVE | TR2_POLARITY_NEGATIVE); setreg (s, 0x0d, PHI1_ACTIVE | PHI2_ACTIVE | RS_ACTIVE | CP1_ACTIVE | CP2_OFF | TR1_ACTIVE | TR2_OFF | NUMBER_OF_TR_PULSES (s->hw_parms.num_tr_pulses)); setreg (s, 0x0e, TR_PULSE_DURATION (s->hw_parms.pulse_duration) | TR_PHI1_GUARDBAND_DURATION (s->hw_parms.guard_band_duration)); /* for pixel rate timing */ setreg (s, 0x0f, 6); setreg (s, 0x10, 23); setreg (s, 0x11, 1); setreg (s, 0x12, 3); setreg (s, 0x13, 3); /* 0 */ setreg (s, 0x14, 5); /* 0 */ setreg (s, 0x15, 0); setreg (s, 0x16, 0); setreg (s, 0x17, 11); setreg (s, 0x18, 2); /* 1 */ setreg (s, 0x19, CIS_TR1_TIMING_OFF | FAKE_OPTICAL_BLACK_PIXELS_OFF); setreg (s, 0x1a, 0); setreg (s, 0x1b, 0); setreg (s, 0x1c, 0x0d); setreg (s, 0x1d, 0x21); setreg (s, 0x27, TR_RED_DROP (0) | TR_GREEN_DROP (0) | TR_BLUE_DROP (0)); setreg (s, 0x28, 0x00); setreg (s, 0x29, ILLUMINATION_MODE (1)); setreg (s, 0x2a, HIBYTE (0)); /* 0 */ setreg (s, 0x2b, LOBYTE (0)); /* 0 */ setreg (s, 0x2c, HIBYTE (16383)); setreg (s, 0x2d, LOBYTE (16383)); setreg (s, 0x2e, HIBYTE (2)); /* 2 */ setreg (s, 0x2f, LOBYTE (2)); /* 1 */ setreg (s, 0x30, HIBYTE (0)); setreg (s, 0x31, LOBYTE (0)); setreg (s, 0x32, HIBYTE (0)); setreg (s, 0x33, LOBYTE (0)); setreg (s, 0x34, HIBYTE (32)); setreg (s, 0x35, LOBYTE (32)); setreg (s, 0x36, HIBYTE (48)); setreg (s, 0x37, LOBYTE (48)); setreg (s, 0x42, EPP_MODE | PPORT_DRIVE_CURRENT (3)); setreg (s, 0x43, RAM_SIZE_128 | SRAM_DRIVER_CURRENT (3) | SRAM_BANDWIDTH_8 | SCANNING_FULL_DUPLEX); setreg (s, 0x45, MICRO_STEPPING | CURRENT_SENSING_PHASES (2) | PHASE_A_POLARITY_POSITIVE | PHASE_B_POLARITY_POSITIVE | STEPPER_MOTOR_OUTPUT); setreg (s, 0x4a, HIBYTE (100)); setreg (s, 0x4b, LOBYTE (100)); setreg (s, 0x4c, HIBYTE (0)); setreg (s, 0x4d, LOBYTE (0)); /* resume scan threshold */ setreg (s, 0x4f, 64); /* steps to reverse */ setreg (s, 0x50, 40); setreg (s, 0x51, ACCELERATION_PROFILE_STOPPED (3) | ACCELERATION_PROFILE_25P (s->hw_parms.fsteps_25_speed) | ACCELERATION_PROFILE_50P (s->hw_parms.fsteps_50_speed)); setreg (s, 0x54, NON_REVERSING_EXTRA_LINES (0) | FIRST_LINE_TO_PROCESS (0)); setreg (s, 0x55, KICKSTART_STEPS (0) | HOLD_CURRENT_TIMEOUT (2)); /* stepper PWM frequency */ setreg (s, 0x56, 8); /* stepper pwm duty cycle */ setreg (s, 0x57, 23); setreg (s, 0x58, PAPER_SENSOR_1_POLARITY_HIGH | PAPER_SENSOR_1_TRIGGER_EDGE | PAPER_SENSOR_1_NO_STOP_SCAN | PAPER_SENSOR_2_POLARITY_HIGH | PAPER_SENSOR_2_TRIGGER_EDGE | PAPER_SENSOR_2_STOP_SCAN); setreg (s, 0x59, MISCIO_1_TYPE_OUTPUT | MISCIO_1_POLARITY_HIGH | MISCIO_1_TRIGGER_EDGE | MISCIO_1_OUTPUT_STATE_HIGH | MISCIO_2_TYPE_OUTPUT | MISCIO_2_POLARITY_HIGH | MISCIO_2_TRIGGER_EDGE | MISCIO_2_OUTPUT_STATE_HIGH); return 0; } #ifdef DEBUG static int dump_register_cache (HP4200_Scanner * s) { int i; for (i = 0; i < 0x80; i++) { fprintf (stderr, "%.2x:0x%.2x", i, s->regs[i]); if ((i + 1) % 8) fprintf (stderr, ", "); else fprintf (stderr, "\n"); } fputs ("", stderr); return 0; } #endif /* * returns the scanner head to home position */ static int hp4200_goto_home (HP4200_Scanner * s) { unsigned char cmd_reg; unsigned char status_reg; unsigned char old_paper_sensor_reg; cmd_reg = getreg (s, 0x07); if (cmd_reg != 2) { unsigned char paper_sensor_reg; unsigned char sensor_bit[2] = { 0x02, 0x10 }; /* sensor head is not returning */ /* let's see if it's already at home */ /* first put paper (head) sensor level sensitive */ paper_sensor_reg = getreg (s, 0x58); old_paper_sensor_reg = paper_sensor_reg; paper_sensor_reg &= ~sensor_bit[s->hw_parms.home_sensor - 1]; setreg (s, 0x58, paper_sensor_reg); cache_write (s); /* if the scan head is not at home then move motor backwards */ status_reg = getreg (s, 0x02); setreg (s, 0x58, old_paper_sensor_reg); cache_write (s); if (!(status_reg & s->hw_parms.home_sensor)) { setreg (s, 0x07, 0x08); usleep (10 * 1000); setreg (s, 0x07, 0x00); usleep (10 * 1000); setreg (s, 0x07, 0x02); } } return 0; } #define HP4200_CHECK_INTERVAL 1000 /* usecs between status checks */ static int hp4200_wait_homed (HP4200_Scanner * s) { unsigned char cmd_reg; cmd_reg = getreg (s, 0x07); while (cmd_reg != 0) { usleep (HP4200_CHECK_INTERVAL); cmd_reg = getreg (s, 0x07); } return 0; } static int compute_fastfeed_step_size (unsigned long crystal_freq, int mclk, float max_speed, int steps_per_inch, int color_mode) { int aux; int r; if (color_mode == 0) r = 24; else r = 8; aux = floor (crystal_freq / ((double) mclk * max_speed * 4.0 * steps_per_inch * r)); if (aux < 2) aux = 2; return aux; } static SANE_Status read_available_data (HP4200_Scanner * s, SANE_Byte * buffer, size_t * byte_count) { SANE_Status status; unsigned char scankb1; unsigned char scankb2; size_t to_read; size_t really_read; size_t chunk; assert (buffer != NULL); *byte_count = 0; do { scankb1 = getreg (s, 0x01); scankb2 = getreg (s, 0x01); if (s->aborted_by_user) return SANE_STATUS_CANCELLED; } while ((scankb1 != scankb2) || (scankb1 < 12)); to_read = scankb1 * 1024; while (to_read) { if (s->aborted_by_user) return SANE_STATUS_CANCELLED; chunk = (to_read > 0xffff) ? 0xffff : to_read; sanei_pv8630_write_byte (s->fd, PV8630_REPPADDRESS, 0x00); sanei_pv8630_prep_bulkread (s->fd, chunk); really_read = chunk; if ((status = sanei_usb_read_bulk (s->fd, buffer, &really_read)) != SANE_STATUS_GOOD) { DBG (DBG_error, "sanei_usb_read_bulk failed (%s)\n", sane_strstatus (status)); return status; } if (really_read > to_read) { DBG (DBG_error, "USB stack read more bytes than requested!\n"); return SANE_STATUS_IO_ERROR; } *byte_count += really_read; buffer += really_read; to_read -= really_read; #ifdef DEBUG fprintf (stderr, "read %zu bytes\n", really_read); #endif } return SANE_STATUS_GOOD; } #ifdef unused static int compute_datalink_bandwidth (HP4200_Scanner * s) { int line_size; int pause_limit; unsigned int color_mode; /* * Line size for 8 bpp, the entire scan area width (plus the * status byte) at optical resolution. */ if (s->user_parms.color) { line_size = 3 * s->hw_parms.scan_area_width + 1; color_mode = 0; setreg (s, 0x26, color_mode); /* 3 channel pixel rate color */ } else { line_size = s->hw_parms.scan_area_width + 1; color_mode = 4; setreg (s, 0x26, 0x08 | color_mode); /* 1 channel mode A (green) */ } setreg (s, 0x09, (3 << 3)); /* h-divider = 1, 8 bpp */ { int first_white_pixel; unsigned int line_end; first_white_pixel = s->hw_parms.sensor_pixel_end - 10; line_end = first_white_pixel + s->hw_parms.scan_area_width; if (line_end > (s->hw_parms.sensor_num_pixels - 20)) line_end = s->hw_parms.sensor_num_pixels - 20; setreg (s, 0x1c, HIBYTE (s->hw_parms.sensor_pixel_start)); setreg (s, 0x1d, LOBYTE (s->hw_parms.sensor_pixel_end)); setreg (s, 0x1e, HIBYTE (first_white_pixel)); setreg (s, 0x1f, LOBYTE (first_white_pixel)); setreg (s, 0x20, HIBYTE (s->hw_parms.sensor_num_pixels)); setreg (s, 0x21, LOBYTE (s->hw_parms.sensor_num_pixels)); setreg (s, 0x22, getreg (s, 0x1e)); setreg (s, 0x23, getreg (s, 0x1f)); setreg (s, 0x24, HIBYTE (line_end)); setreg (s, 0x25, LOBYTE (line_end)); } /* * During transfer rate calculation don't forward scanner sensor. * Stay in the calibration region. */ setreg (s, 0x4f, 0); clearbits (s, 0x45, 0x10); /* * Pause the scan when memory is full. */ pause_limit = s->hw_parms.SRAM_size - (line_size / 1024) - 1; setreg (s, 0x4e, pause_limit & 0xff); s->mclk = compute_min_mclk (s->hw_parms.SRAM_bandwidth, s->hw_parms.crystal_frequency); /* * Set step size to fast speed. */ { int step_size; step_size = compute_fastfeed_step_size (s->hw_parms.crystal_frequency, s->mclk, s->hw_parms.scan_bar_max_speed, s->hw_parms.motor_full_steps_per_inch, color_mode); setreg (s, 0x46, HIBYTE (step_size)); setreg (s, 0x47, LOBYTE (step_size)); setreg (s, 0x48, HIBYTE (step_size)); setreg (s, 0x49, LOBYTE (step_size)); } cache_write (s); /* dump_register_cache (s); */ /* * scan during 1 sec. aprox. */ setreg (s, 0x07, 0x08); setreg (s, 0x07, 0x03); { struct timeval tv_before; struct timeval tv_after; int elapsed_time_ms = 0; long bytes_read_total; SANE_Byte *buffer; buffer = malloc (2 * 98304); /* check this */ if (!buffer) { DBG (DBG_error, "compute_datalink_bandwidth: malloc failed\n"); return 0; } bytes_read_total = 0; gettimeofday (&tv_before, NULL); do { size_t bytes_read; SANE_Status status; status = read_available_data (s, buffer, &bytes_read); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "read_available_data failed (%s)\n", sane_strstatus (status)); return 0; } bytes_read_total += bytes_read; gettimeofday (&tv_after, NULL); elapsed_time_ms = (tv_after.tv_sec - tv_before.tv_sec) * 1000; elapsed_time_ms += (tv_after.tv_usec - tv_before.tv_usec) / 1000; } while (elapsed_time_ms < 1000); setreg (s, 0x07, 0x00); free (buffer); s->msrd_parms.datalink_bandwidth = bytes_read_total / (elapsed_time_ms / 1000); #ifdef DEBUG fprintf (stderr, "PC Transfer rate = %d bytes/sec. (%ld/%d)\n", s->msrd_parms.datalink_bandwidth, bytes_read_total, elapsed_time_ms); #endif } return 0; } #endif static void compute_first_gain_offset (int target, int max, int min, int *gain, int *offset, int *max_gain, int *min_offset) { *gain = (int) 15.0 *(target / (max - min) - 0.933); *offset = (int) (-1.0 * min / (512.0 * 0.0195)); if (*gain >= 32) { *gain = (int) 15.0 *(target / 3.0 / (max - min) - 0.933); *offset = (int) -3.0 * min / (512.0 * 0.0195); } if (*gain < 0) *gain = 0; else if (*gain > 63) *gain = 63; if (*offset < -31) *offset = -31; else if (*offset > 31) *offset = 31; *max_gain = 63; *min_offset = -31; } #define DATA_PORT_READ (1 << 5) #define DATA_PORT_WRITE 0 static int write_gamma (HP4200_Scanner * s) { int color; int i; unsigned char gamma[1024]; unsigned char read_gamma[1024]; int retval; size_t to_read; size_t to_write; for (color = 0; color < 3; color++) { for (i = 0; i < 1024; i++) gamma[i] = s->user_parms.gamma[color][i]; setreg (s, 0x03, color << 1); setreg (s, 0x04, DATA_PORT_WRITE); setreg (s, 0x05, 0x00); sanei_pv8630_write_byte (s->fd, PV8630_REPPADDRESS, 0x06); sanei_pv8630_prep_bulkwrite (s->fd, sizeof (gamma)); to_write = sizeof (gamma); sanei_usb_write_bulk (s->fd, gamma, &to_write); /* check if gamma vector was correctly written */ setreg (s, 0x03, color << 1); setreg (s, 0x04, DATA_PORT_READ); setreg (s, 0x05, 0x00); sanei_pv8630_write_byte (s->fd, PV8630_REPPADDRESS, 0x06); sanei_pv8630_prep_bulkread (s->fd, sizeof (read_gamma)); to_read = sizeof (read_gamma); sanei_usb_read_bulk (s->fd, read_gamma, &to_read); retval = memcmp (read_gamma, gamma, sizeof (read_gamma)); if (retval != 0) { DBG (DBG_error, "error: color %d has bad gamma table\n", color); } #ifdef DEBUG else fprintf (stderr, "color %d gamma table is good\n", color); #endif } return 0; } static int write_default_offset_gain (HP4200_Scanner * s, SANE_Byte * gain_offset, int size, int color) { SANE_Byte *check_data; int retval; size_t to_read; size_t to_write; setreg (s, 0x03, (color << 1) | 1); setreg (s, 0x04, DATA_PORT_WRITE); setreg (s, 0x05, 0x00); sanei_pv8630_write_byte (s->fd, PV8630_REPPADDRESS, 0x06); sanei_pv8630_prep_bulkwrite (s->fd, size); to_write = size; sanei_usb_write_bulk (s->fd, gain_offset, &to_write); check_data = malloc (size); setreg (s, 0x03, (color << 1) | 1); setreg (s, 0x04, DATA_PORT_READ); setreg (s, 0x05, 0x00); sanei_pv8630_write_byte (s->fd, PV8630_REPPADDRESS, 0x06); sanei_pv8630_prep_bulkread (s->fd, size); to_read = size; sanei_usb_read_bulk (s->fd, check_data, &to_read); retval = memcmp (gain_offset, check_data, size); free (check_data); if (retval != 0) { DBG (DBG_error, "error: color %d has bad gain/offset table\n", color); } #ifdef DEBUG else fprintf (stderr, "color %d gain/offset table is good\n", color); #endif return 0; } static int compute_gain_offset (int target, int max, int min, int *gain, int *offset, int *max_gain, int *min_offset) { int gain_stable; int is_unstable; gain_stable = 1; /* unless the opposite is said */ is_unstable = 0; if (max > target) { if (*gain > 0) { (*gain)--; *max_gain = *gain; gain_stable = 0; is_unstable |= 1; } else { DBG (DBG_error, "error: integration time too long.\n"); return -1; } } else { if (*gain < *max_gain) { (*gain)++; gain_stable = 0; is_unstable |= 1; } } if (min == 0) { if (*offset < 31) { (*offset)++; if (gain_stable) *min_offset = *offset; is_unstable |= 1; } else { DBG (DBG_error, "error: max static has pixel value == 0\n"); return -1; } } else { if (*offset > *min_offset) { (*offset)--; is_unstable |= 1; } } return is_unstable; } static int compute_bytes_per_line (int width_in_pixels, unsigned char hdpi_code, unsigned char pixel_packing, unsigned char data_mode, unsigned char AFE_operation, int m) { const int dpi_qot_mul[] = { 1, 2, 1, 1, 1, 1, 1, 1 }; const int dpi_qot_div[] = { 1, 3, 2, 3, 4, 6, 8, 12 }; int pixels_per_line; int bytes_per_line; int pixels_per_byte; int status_bytes; const int pixels_per_byte_mapping[] = { 8, 4, 2, 1 }; assert (hdpi_code <= 7); pixels_per_line = (width_in_pixels * dpi_qot_mul[hdpi_code]) / dpi_qot_div[hdpi_code]; if ((width_in_pixels * dpi_qot_mul[hdpi_code]) % dpi_qot_div[hdpi_code]) pixels_per_line++; status_bytes = (m == 0) ? 1 : m; if (data_mode == 1) pixels_per_byte = 1; /* should be 0.5 but later bytes_per_line will be multiplied by 2, and also the number of status bytes, that in this case should be 2. umm.. maybe this should be done in the cleaner way. */ else { assert (pixel_packing <= 3); pixels_per_byte = pixels_per_byte_mapping[pixel_packing]; } switch (AFE_operation) { case PIXEL_RATE_3_CHANNELS: bytes_per_line = ((pixels_per_line * 3) / pixels_per_byte) + status_bytes; break; case MODEA_1_CHANNEL: bytes_per_line = (pixels_per_line / pixels_per_byte) + status_bytes; break; default: /* Not implemented! (yet?) and not used. * This case should not happen. */ assert (0); } if (data_mode == 1) /* see big note above */ bytes_per_line *= 2; return bytes_per_line; } static int compute_pause_limit (hardware_parameters_t * hw_parms, int bytes_per_line) { int coef_size; const int coef_mapping[] = { 16, 32 }; int pause_limit; coef_size = coef_mapping[hw_parms->sensor_resolution & 0x01]; pause_limit = hw_parms->SRAM_size - coef_size - (bytes_per_line / 1024) - 1; if (pause_limit > 2) pause_limit -= 2; return pause_limit; } static int compute_dpd (HP4200_Scanner * s, int step_size, int line_end) { int tr, dpd; tr = 1 /* color mode */ * (line_end + ((s->hw_parms.num_tr_pulses + 1) * (2 * s->hw_parms.guard_band_duration + s->hw_parms.pulse_duration + 1) + 3 - s->hw_parms.num_tr_pulses)); if (tr == 0) return 0; dpd = (((s->hw_parms.fsteps_25_speed * 4) + (s->hw_parms.fsteps_50_speed * 2) + s->hw_parms.steps_to_reverse) * 4 * step_size) % tr; dpd = tr - dpd; return dpd; } static SANE_Status read_required_bytes (HP4200_Scanner * s, int required, SANE_Byte * buffer) { unsigned char scankb1; unsigned char scankb2; size_t to_read; size_t really_read; size_t chunk; SANE_Status status; assert (buffer != NULL); while (required) { do { scankb1 = getreg (s, 0x01); scankb2 = getreg (s, 0x01); if (s->aborted_by_user) return SANE_STATUS_CANCELLED; } while ((scankb1 != scankb2) || (scankb1 < 12)); to_read = min (required, (scankb1 * 1024)); while (to_read) { if (s->aborted_by_user) return SANE_STATUS_CANCELLED; chunk = (to_read > 0xffff) ? 0xffff : to_read; sanei_pv8630_write_byte (s->fd, PV8630_REPPADDRESS, 0x00); sanei_pv8630_prep_bulkread (s->fd, chunk); really_read = chunk; if ((status = sanei_usb_read_bulk (s->fd, buffer, &really_read)) != SANE_STATUS_GOOD) { DBG (DBG_error, "sanei_usb_read_bulk failed (%s)\n", sane_strstatus (status)); return status; } if (really_read > chunk) { DBG (DBG_error, "USB stack read more bytes than requested!\n"); return SANE_STATUS_IO_ERROR; } buffer += really_read; required -= really_read; to_read -= really_read; } } return SANE_STATUS_GOOD; } static SANE_Status scanner_buffer_init (scanner_buffer_t * sb, int size_in_kb) { sb->size = size_in_kb * 1024 + 3; sb->buffer = malloc (sb->size); if (!sb->buffer) return SANE_STATUS_NO_MEM; sb->num_bytes = 0; sb->data_ptr = sb->buffer; return SANE_STATUS_GOOD; } static SANE_Status scanner_buffer_read (HP4200_Scanner * s) { SANE_Status status; size_t num_bytes_read_now; assert (s->scanner_buffer.num_bytes <= 3); memcpy (s->scanner_buffer.buffer, s->scanner_buffer.data_ptr, 3); status = read_available_data (s, s->scanner_buffer.buffer + s->scanner_buffer.num_bytes, &num_bytes_read_now); s->scanner_buffer.data_ptr = s->scanner_buffer.buffer; s->scanner_buffer.num_bytes += num_bytes_read_now; return status; } #define OFFSET_CODE_SIGN(off) (((off) < 0) ? (-(off) & 0x1f) | 0x20 : (off)) #define OFFSET_DECODE_SIGN(off) (((off) & 0x20) ? -(off & 0x1f) : (off)) static SANE_Status do_coarse_calibration (HP4200_Scanner * s, struct coarse_t *coarse) { SANE_Status status; unsigned char *cal_line = NULL; unsigned char *cal_line_ptr; int cal_line_size; /* local scanning params */ int active_pixels_start; int line_end; int data_pixels_start; int data_pixels_end; int dpd; int step_size; int ff_step_size; char steps_to_reverse; char line_rate_color; int vdpi; /* vertical dots per inch */ int hdpi_code; int calibrated; int first_time; int red_offset = 0; int green_offset = 0; int blue_offset = 0; int red_gain = 1; int green_gain = 1; int blue_gain = 1; int min_red_offset = -31; int min_green_offset = -31; int min_blue_offset = -31; int max_red_gain = 63; int max_green_gain = 63; int max_blue_gain = 63; int max_red; int min_red; int max_green; int min_green; int max_blue; int min_blue; static char me[] = "do_coarse_calibration"; DBG (DBG_proc, "%s\n", me); setreg (s, 0x07, 0x00); usleep (10 * 1000); vdpi = 150; hdpi_code = 0; active_pixels_start = 0x40; line_end = 0x2ee0; s->mclk_div = 2; data_pixels_start = 0x40; data_pixels_end = (int) (data_pixels_start + s->hw_parms.scan_area_width); data_pixels_end = min (data_pixels_end, line_end - 20); cal_line_size = s->hw_parms.scan_area_width * 3 * 2 + 2; setreg (s, 0x1e, HIBYTE (active_pixels_start)); setreg (s, 0x1f, LOBYTE (active_pixels_start)); setreg (s, 0x20, HIBYTE (line_end)); setreg (s, 0x21, LOBYTE (line_end)); setreg (s, 0x22, HIBYTE (data_pixels_start)); setreg (s, 0x23, LOBYTE (data_pixels_start)); setreg (s, 0x24, HIBYTE (data_pixels_end)); setreg (s, 0x25, LOBYTE (data_pixels_end)); setreg (s, 0x26, PIXEL_RATE_3_CHANNELS | GRAY_CHANNEL_RED | TR_RED (0) | TR_GREEN (0) | TR_BLUE (0)); setreg (s, 0x08, (s->mclk_div - 1) * 2); setreg (s, 0x09, hdpi_code | PIXEL_PACKING (3) | DATAMODE (1)); setreg (s, 0x0a, 0); /* reserved and strange register */ setreg (s, 0x38, red_offset); setreg (s, 0x39, green_offset); setreg (s, 0x3a, blue_offset); setreg (s, 0x3b, red_gain); setreg (s, 0x3c, green_gain); setreg (s, 0x3d, blue_gain); setreg (s, 0x5e, 0x80); setreg (s, 0x3e, 0x00); /* 1.5:1, 6/10 bits, 2*fixed */ setreg (s, 0x3f, 0x00); setreg (s, 0x40, 0x00); setreg (s, 0x41, 0x00); setreg (s, 0x4e, 0x5b - 0x3c); /* max Kb to pause */ setreg (s, 0x4f, 0x02); /* min Kb to resume */ line_rate_color = 1; step_size = (vdpi * line_end * line_rate_color) / (4 * s->hw_parms.motor_full_steps_per_inch); dpd = compute_dpd (s, step_size, line_end); /* 0x0ada; */ #ifdef DEBUG fprintf (stderr, "dpd = %d\n", dpd); #endif setreg (s, 0x52, HIBYTE (dpd)); setreg (s, 0x53, LOBYTE (dpd)); setreg (s, 0x46, HIBYTE (step_size)); setreg (s, 0x47, LOBYTE (step_size)); ff_step_size = compute_fastfeed_step_size (s->hw_parms.crystal_frequency, s->mclk_div, s->hw_parms.motor_max_speed, s->hw_parms.motor_full_steps_per_inch, 0); /* 0x0190; */ setreg (s, 0x48, HIBYTE (ff_step_size)); setreg (s, 0x49, LOBYTE (ff_step_size)); setreg (s, 0x4b, 0x15); steps_to_reverse = 0x3f; setreg (s, 0x50, steps_to_reverse); setreg (s, 0x51, 0x15); /* accel profile */ /* this is to stay the motor stopped */ clearbits (s, 0x45, (1 << 4)); cache_write (s); calibrated = 0; first_time = 1; cal_line = malloc (cal_line_size + 1024); do { unsigned char cmd_reg; /* resets the lm9830 before start scanning */ setreg (s, 0x07, 0x08); do { setreg (s, 0x07, 0x03); cmd_reg = getreg (s, 0x07); } while (cmd_reg != 0x03); cal_line_ptr = cal_line; status = read_required_bytes (s, cal_line_size, cal_line_ptr); if (status != SANE_STATUS_GOOD) goto done; setreg (s, 0x07, 0x00); { unsigned int i; min_red = max_red = (cal_line[0] * 256 + cal_line[1]) >> 2; min_green = max_green = (cal_line[2] * 256 + cal_line[3]) >> 2; min_blue = max_blue = (cal_line[4] * 256 + cal_line[5]) >> 2; for (i = 6; i < (s->hw_parms.scan_area_width * 3 * 2); i += 6) { int value; value = cal_line[i] * 256 + cal_line[i + 1]; value >>= 2; if (value > max_red) max_red = value; value = cal_line[i + 2] * 256 + cal_line[i + 3]; value >>= 2; if (value > max_green) max_green = value; value = cal_line[i + 4] * 256 + cal_line[i + 5]; value >>= 2; if (value > max_blue) max_blue = value; value = cal_line[i] * 256 + cal_line[i + 1]; value >>= 2; if (value < min_red) min_red = value; value = cal_line[i + 2] * 256 + cal_line[i + 3]; value >>= 2; if (value < min_green) min_green = value; value = cal_line[i + 4] * 256 + cal_line[i + 5]; value >>= 2; if (value < min_blue) min_blue = value; } #ifdef DEBUG fprintf (stderr, "max_red:%d max_green:%d max_blue:%d\n", max_red, max_green, max_blue); fprintf (stderr, "min_red:%d min_green:%d min_blue:%d\n", min_red, min_green, min_blue); #endif if (first_time) { first_time = 0; compute_first_gain_offset (s->hw_parms.target_value.red, max_red, min_red, &red_gain, &red_offset, &max_red_gain, &min_red_offset); compute_first_gain_offset (s->hw_parms.target_value.green, max_green, min_green, &green_gain, &green_offset, &max_green_gain, &min_green_offset); compute_first_gain_offset (s->hw_parms.target_value.blue, max_blue, min_blue, &blue_gain, &blue_offset, &max_blue_gain, &min_blue_offset); } else { int retval; /* this code should check return value -1 for error */ retval = compute_gain_offset (s->hw_parms.target_value.red, max_red, min_red, &red_gain, &red_offset, &max_red_gain, &min_red_offset); if (retval < 0) break; retval |= compute_gain_offset (s->hw_parms.target_value.green, max_green, min_green, &green_gain, &green_offset, &max_green_gain, &min_green_offset); if (retval < 0) break; retval |= compute_gain_offset (s->hw_parms.target_value.blue, max_blue, min_blue, &blue_gain, &blue_offset, &max_blue_gain, &min_blue_offset); if (retval < 0) break; calibrated = !retval; } setreg (s, 0x3b, red_gain); setreg (s, 0x3c, green_gain); setreg (s, 0x3d, blue_gain); setreg (s, 0x38, OFFSET_CODE_SIGN (red_offset)); setreg (s, 0x39, OFFSET_CODE_SIGN (green_offset)); setreg (s, 0x3a, OFFSET_CODE_SIGN (blue_offset)); #ifdef DEBUG fprintf (stderr, "%d, %d, %d %d, %d, %d\n", red_gain, green_gain, blue_gain, red_offset, green_offset, blue_offset); #endif cache_write (s); } } while (!calibrated); coarse->min_red = min_red; coarse->min_green = min_green; coarse->min_blue = min_blue; coarse->max_red = max_red; coarse->max_green = max_green; coarse->max_blue = max_blue; coarse->red_gain = red_gain; coarse->green_gain = green_gain; coarse->blue_gain = blue_gain; coarse->red_offset = red_offset; coarse->green_offset = green_offset; coarse->blue_offset = blue_offset; status = SANE_STATUS_GOOD; done: if (cal_line) free (cal_line); return status; } static int compute_corr_code (int average, int min_color, int range, int target) { int value; int corr_code; value = average - min_color; if (value > 0) corr_code = (int) (range * ((double) target / (double) value - 1.0) + 0.5); else corr_code = 0; if (corr_code < 0) corr_code = 0; else if (corr_code > 2048) corr_code = 0; else if (corr_code > 1023) corr_code = 1023; return corr_code; } static int compute_hdpi_code (int hres) { int hdpi_code; /* Calculate the horizontal DPI code based on the requested horizontal resolution. Defaults to 150dpi. */ switch (hres) { case 600: hdpi_code = 0; break; case 400: hdpi_code = 1; break; case 300: hdpi_code = 2; break; case 200: hdpi_code = 3; break; case 150: hdpi_code = 4; break; case 100: hdpi_code = 5; break; case 75: hdpi_code = 6; break; case 50: hdpi_code = 7; break; default: hdpi_code = 4; } return hdpi_code; } static SANE_Status do_fine_calibration (HP4200_Scanner * s, struct coarse_t *coarse) { SANE_Status status; unsigned char *cal_line; unsigned char *cal_line_ptr; int *average; SANE_Byte red_gain_offset[5460 * 2]; SANE_Byte green_gain_offset[5460 * 2]; SANE_Byte blue_gain_offset[5460 * 2]; int *corr_red = NULL; int *corr_green = NULL; int *corr_blue = NULL; int registro[30][5460 * 3]; int cal_line_size; /* local scanning params */ int active_pixels_start; int line_end; int line_length; int data_pixels_start; int data_pixels_end; int dpd; int step_size; int ff_step_size; char steps_to_reverse; char hdpi_div; char line_rate_color; int vdpi; /* vertical dots per inch */ int hdpi_code; int calibrated; int lines_to_process; static char me[] = "do_fine_calibration"; DBG (DBG_proc, "%s\n", me); setreg (s, 0x07, 0x00); usleep (10 * 1000); vdpi = 150; hdpi_code = compute_hdpi_code (s->user_parms.horizontal_resolution); /* figure out which horizontal divider to use based on the calculated horizontal dpi code */ hdpi_div = hdpi_mapping[hdpi_code]; active_pixels_start = 0x40; line_end = 0x2ee0; line_length = s->user_parms.image_width * hdpi_div; s->mclk_div = 2; data_pixels_start = 0x72 + s->runtime_parms.first_pixel * hdpi_div; data_pixels_end = (int) (data_pixels_start + s->user_parms.image_width * hdpi_div); data_pixels_end = min (data_pixels_end, line_end - 20); cal_line_size = line_length * 3 * 2 + 2; setreg (s, 0x1e, HIBYTE (active_pixels_start)); setreg (s, 0x1f, LOBYTE (active_pixels_start)); setreg (s, 0x20, HIBYTE (line_end)); setreg (s, 0x21, LOBYTE (line_end)); setreg (s, 0x22, HIBYTE (data_pixels_start)); setreg (s, 0x23, LOBYTE (data_pixels_start)); setreg (s, 0x24, HIBYTE (data_pixels_end)); setreg (s, 0x25, LOBYTE (data_pixels_end)); setreg (s, 0x26, PIXEL_RATE_3_CHANNELS | GRAY_CHANNEL_RED | TR_RED (0) | TR_GREEN (0) | TR_BLUE (0)); setreg (s, 0x08, (s->mclk_div - 1) * 2); setreg (s, 0x09, 0 | PIXEL_PACKING (3) | DATAMODE (1)); setreg (s, 0x0a, 0); /* reserved and strange register */ setreg (s, 0x38, 1); setreg (s, 0x39, 1); setreg (s, 0x3a, 1); setreg (s, 0x3b, coarse->red_gain); setreg (s, 0x3c, coarse->green_gain); setreg (s, 0x3d, coarse->blue_gain); setreg (s, 0x5e, 0x80); setreg (s, 0x3e, 0x00); /* 1.5:1, 6/10 bits, 2*fixed */ setreg (s, 0x3f, 0x00); setreg (s, 0x40, 0x00); setreg (s, 0x41, 0x00); setreg (s, 0x4e, 0x5b - 0x3c); /* max Kb to pause */ setreg (s, 0x4f, 0x02); /* min Kb to resume */ line_rate_color = 1; step_size = (vdpi * line_end * line_rate_color) / (4 * s->hw_parms.motor_full_steps_per_inch); dpd = compute_dpd (s, step_size, line_end); /* 0x0ada; */ #ifdef DEBUG fprintf (stderr, "dpd = %d\n", dpd); #endif setreg (s, 0x52, HIBYTE (dpd)); setreg (s, 0x53, LOBYTE (dpd)); setreg (s, 0x46, HIBYTE (step_size)); setreg (s, 0x47, LOBYTE (step_size)); ff_step_size = compute_fastfeed_step_size (s->hw_parms.crystal_frequency, s->mclk_div, s->hw_parms.motor_max_speed, s->hw_parms.motor_full_steps_per_inch, 0); /* 0x0190; */ setreg (s, 0x48, HIBYTE (ff_step_size)); setreg (s, 0x49, LOBYTE (ff_step_size)); setreg (s, 0x4b, 0x15); steps_to_reverse = 0x3f; setreg (s, 0x50, steps_to_reverse); setreg (s, 0x51, 0x15); /* accel profile */ /* this is to activate the motor */ setbits (s, 0x45, (1 << 4)); lines_to_process = 8 * step_size * 4 / line_end; if (lines_to_process < 1) lines_to_process = 1; #ifdef DEBUG fprintf (stderr, "lines to process = %d\n", lines_to_process); #endif setreg (s, 0x58, 0); cache_write (s); calibrated = 0; cal_line = malloc (cal_line_size + 1024); average = malloc (sizeof (int) * line_length * 3); memset (average, 0, sizeof (int) * line_length * 3); { int i; for (i = 0; i < 12; i++) { memset (registro[i], 0, 5460 * 3 * sizeof(int)); } } /* resets the lm9830 before start scanning */ setreg (s, 0x07, 0x08); setreg (s, 0x07, 0x03); usleep (100); do { cal_line_ptr = cal_line; status = read_required_bytes (s, cal_line_size, cal_line_ptr); if (status != SANE_STATUS_GOOD) goto done; { int i, j; if (calibrated == 0) for (j = 0, i = 0; i < (line_length * 3); i++, j += 2) { average[i] = (cal_line[j] * 256 + cal_line[j + 1]) >> 2; registro[calibrated][i] = average[i]; } else for (j = 0, i = 0; i < (line_length * 3); i++, j += 2) { int value; value = (cal_line[j] * 256 + cal_line[j + 1]) >> 2; average[i] += value; average[i] /= 2; registro[calibrated][i] = value; } } calibrated++; } while (calibrated < lines_to_process); lm9830_write_register (s->fd, 0x07, 0x00); usleep (10 * 1000); #if 0 { int i; int j = 0; do { for (i = 3; (i + 6) < (line_length * 3); i += 3) { average[i] = (2 * average[i - 3] + average[i] + 2 * average[i + 3]) / 5; average[i + 1] = (2 * average[i - 2] + average[i + 1] + 2 * average[i + 4]) / 5; average[i + 2] = (2 * average[i - 1] + average[i + 2] + 2 * average[i + 5]) / 5; } j++; } while (j < 3); } #endif { int i; int max_red; int min_red; int max_green; int min_green; int max_blue; int min_blue; min_red = max_red = average[0]; min_green = max_green = average[1]; min_blue = max_blue = average[2]; for (i = 3; i < (line_length * 3); i += 3) { int value; value = average[i]; if (value > max_red) max_red = value; value = average[i + 1]; if (value > max_green) max_green = value; value = average[i + 2]; if (value > max_blue) max_blue = value; value = average[i]; if (value < min_red) min_red = value; value = average[i + 1]; if (value < min_green) min_green = value; value = average[i + 2]; if (value < min_blue) min_blue = value; } #ifdef DEBUG fprintf (stderr, "max_red:%d max_green:%d max_blue:%d\n", max_red, max_green, max_blue); fprintf (stderr, "min_red:%d min_green:%d min_blue:%d\n", min_red, min_green, min_blue); #endif /* do fine calibration */ { int min_white_red; int min_white_green; int min_white_blue; double ratio; int range; double aux; int min_white_err; int j; min_white_red = min_white_green = min_white_blue = 0x3ff; for (i = 0; i < (line_length * 3); i += 3) { int value; value = average[i] - coarse->min_red; if ((value > 0) && (value < min_white_red)) min_white_red = value; value = average[i + 1] - coarse->min_green; if ((value > 0) && (value < min_white_green)) min_white_green = value; value = average[i + 2] - coarse->min_blue; if ((value > 0) && (value < min_white_blue)) min_white_blue = value; } ratio = 0; min_white_err = 0x3ff; aux = (double) s->hw_parms.target_value.red / min_white_red; if (aux > ratio) ratio = aux; if (min_white_err > min_white_red) min_white_err = min_white_red; aux = (double) s->hw_parms.target_value.green / min_white_green; if (aux > ratio) ratio = aux; if (min_white_err > min_white_green) min_white_err = min_white_green; aux = (double) s->hw_parms.target_value.blue / min_white_blue; if (aux > ratio) ratio = aux; if (min_white_err > min_white_blue) min_white_err = min_white_blue; #ifdef DEBUG fprintf (stderr, "min_white_err = %d, ratio = %f\n", min_white_err, ratio); #endif if (ratio <= 1.5) range = 2048; else if (ratio <= 2.0) range = 1024; else range = 512; corr_red = malloc (sizeof (int) * line_length); corr_green = malloc (sizeof (int) * line_length); corr_blue = malloc (sizeof (int) * line_length); for (i = 0, j = 0; i < (line_length * 3); i += 3, j++) { corr_red[j] = compute_corr_code (average[i], coarse->min_red, range, s->hw_parms.target_value.red); corr_green[j] = compute_corr_code (average[i + 1], coarse->min_green, range, s->hw_parms.target_value.green); corr_blue[j] = compute_corr_code (average[i + 2], coarse->min_blue, range, s->hw_parms.target_value.blue); } #ifdef DEBUG { FILE *kaka; int i; kaka = fopen ("corr.raw", "w"); for (i = 0; i < line_length; i++) { fprintf (kaka, "%d %d %d %d %d %d ", corr_red[i], corr_green[i], corr_blue[i], average[3 * i], average[3 * i + 1], average[3 * i + 2]); fprintf (kaka, "%d %d %d %d %d %d %d %d %d ", registro[0][3 * i], registro[0][3 * i + 1], registro[0][3 * i + 2], registro[1][3 * i], registro[1][3 * i + 1], registro[1][3 * i + 2], registro[2][3 * i], registro[2][3 * i + 1], registro[2][3 * i + 2]); fprintf (kaka, "%d %d %d %d %d %d %d %d %d\n", registro[3][3 * i], registro[3][3 * i + 1], registro[3][3 * i + 2], registro[4][3 * i], registro[4][3 * i + 1], registro[4][3 * i + 2], registro[5][3 * i], registro[5][3 * i + 1], registro[5][3 * i + 2]); } fclose (kaka); } #endif { int max_black; int use_six_eight_bits; max_black = max (coarse->min_red, coarse->min_green); max_black = max (max_black, coarse->min_blue); use_six_eight_bits = (max_black < 64); if (use_six_eight_bits) { setreg (s, 0x3e, (1 << 4) | (1 << 3) | (1024 / range)); } else { setreg (s, 0x3e, (1 << 4) | (1 << 3) | (1 << 2) | (1024 / range)); } memset (red_gain_offset, 0, sizeof (red_gain_offset)); memset (green_gain_offset, 0, sizeof (green_gain_offset)); memset (blue_gain_offset, 0, sizeof (blue_gain_offset)); for (i = 0, j = (data_pixels_start - active_pixels_start) * 2; i < line_length; i++, j += 2) { if (use_six_eight_bits) { red_gain_offset[j] = (coarse->min_red << 2) | ((corr_red[i] >> 8) & 0x03); red_gain_offset[j + 1] = corr_red[i] & 0xff; green_gain_offset[j] = (coarse->min_green << 2) | ((corr_green[i] >> 8) & 0x03); green_gain_offset[j + 1] = corr_green[i] & 0xff; blue_gain_offset[j] = (coarse->min_blue << 2) | ((corr_blue[i] >> 8) & 0x03); blue_gain_offset[j + 1] = corr_blue[i] & 0xff; } else { red_gain_offset[j] = coarse->min_red; red_gain_offset[j + 1] = corr_red[j] >> 2; green_gain_offset[j] = coarse->min_green; green_gain_offset[j + 1] = corr_green[j] >> 2; blue_gain_offset[j] = coarse->min_blue; blue_gain_offset[j + 1] = corr_blue[j] >> 2; } } write_default_offset_gain (s, red_gain_offset, 5460 * 2, 0); write_default_offset_gain (s, green_gain_offset, 5460 * 2, 1); write_default_offset_gain (s, blue_gain_offset, 5460 * 2, 2); } } } status = SANE_STATUS_GOOD; done: if (corr_red) free (corr_red); if (corr_green) free (corr_green); if (corr_blue) free (corr_blue); if (cal_line) free (cal_line); if (average) free (average); return status; } static void ciclic_buffer_init_offset_correction (ciclic_buffer_t * cb, int vres) { cb->blue_idx = 0; switch (vres) { case 600: cb->green_idx = 4; cb->red_idx = 8; cb->first_good_line = 8; break; case 400: cb->green_idx = 3; cb->red_idx = 6; cb->first_good_line = 6; break; case 300: cb->green_idx = 2; cb->red_idx = 4; cb->first_good_line = 4; break; case 200: cb->blue_idx = 0; cb->green_idx = 1; cb->red_idx = 2; cb->first_good_line = 4; break; case 150: cb->green_idx = 1; cb->red_idx = 2; cb->first_good_line = 2; break; case 75: cb->green_idx = 1; cb->red_idx = 2; cb->first_good_line = 2; break; default: cb->green_idx = 0; cb->red_idx = 0; cb->first_good_line = 0; break; } cb->buffer_position = cb->buffer_ptrs[cb->first_good_line]; } static SANE_Status ciclic_buffer_init (ciclic_buffer_t * cb, SANE_Int bytes_per_line, int vres, int status_bytes) { cb->good_bytes = 0; cb->num_lines = 12; cb->size = bytes_per_line * cb->num_lines; cb->can_consume = cb->size + cb->num_lines * status_bytes; cb->buffer = malloc (cb->size); if (!cb->buffer) return SANE_STATUS_NO_MEM; { int i; unsigned char *buffer; unsigned char **ptrs; ptrs = cb->buffer_ptrs = (unsigned char **) malloc (sizeof (unsigned char *) * cb->num_lines); if (!cb->buffer_ptrs) return SANE_STATUS_NO_MEM; buffer = cb->buffer; for (i = 0; i < cb->num_lines; i++) { ptrs[i] = buffer; buffer += bytes_per_line; } } cb->current_line = 0; cb->pixel_position = 0; ciclic_buffer_init_offset_correction (cb, vres); return SANE_STATUS_GOOD; } static int prepare_for_a_scan (HP4200_Scanner * s) { /* local scanning params */ int active_pixels_start; int line_end; int data_pixels_start; int data_pixels_end; int ff_step_size; int dpd; int step_size; char steps_to_reverse; char hdpi_div; char line_rate_color; int hdpi_code; unsigned char pixel_packing; unsigned char data_mode; unsigned char AFE_operation; int pause_limit; int n = 0, m = 0; setreg (s, 0x07, 0x00); usleep (10 * 1000); hdpi_code = compute_hdpi_code (s->user_parms.horizontal_resolution); /* figure out which horizontal divider to use based on the calculated horizontal dpi code */ hdpi_div = hdpi_mapping[hdpi_code]; /* image_width is set to the correct number of pixels by calling fxn. This might be the reason we can't do high res full width scans though...not sure. */ /*s->user_parms.image_width /= 4; */ active_pixels_start = 0x40; line_end = 0x2ee0; /* 2ee0 */ s->mclk_div = 2; data_pixels_start = 0x72 + s->runtime_parms.first_pixel * hdpi_div; data_pixels_end = (int) (data_pixels_start + s->user_parms.image_width * hdpi_div); data_pixels_end = min (data_pixels_end, line_end - 20); setreg (s, 0x1e, HIBYTE (active_pixels_start)); setreg (s, 0x1f, LOBYTE (active_pixels_start)); setreg (s, 0x20, HIBYTE (line_end)); setreg (s, 0x21, LOBYTE (line_end)); setreg (s, 0x22, HIBYTE (data_pixels_start)); setreg (s, 0x23, LOBYTE (data_pixels_start)); setreg (s, 0x24, HIBYTE (data_pixels_end)); setreg (s, 0x25, LOBYTE (data_pixels_end)); AFE_operation = PIXEL_RATE_3_CHANNELS; setreg (s, 0x26, AFE_operation | GRAY_CHANNEL_RED | TR_RED (0) | TR_GREEN (0) | TR_BLUE (0)); setreg (s, 0x08, (s->mclk_div - 1) * 2); pixel_packing = 3; data_mode = 0; setreg (s, 0x09, hdpi_code | PIXEL_PACKING (pixel_packing) | DATAMODE (data_mode)); setreg (s, 0x0a, 0); /* reserved and strange register */ setreg (s, 0x5c, 0x00); setreg (s, 0x5d, 0x00); setreg (s, 0x5e, 0x00); if (s->user_parms.vertical_resolution == 1200) { /* 1 out of 2 */ n = 1; m = 2; } setreg (s, 0x44, (256 - n) & 0xff); setreg (s, 0x5a, m); s->runtime_parms.status_bytes = (m == 0) ? 1 : m; if (data_mode == 1) s->runtime_parms.status_bytes *= 2; s->runtime_parms.scanner_line_size = compute_bytes_per_line (data_pixels_end - data_pixels_start, hdpi_code, pixel_packing, data_mode, AFE_operation, m); pause_limit = compute_pause_limit (&(s->hw_parms), s->runtime_parms.scanner_line_size); #ifdef DEBUG fprintf (stderr, "scanner_line_size = %d\npause_limit = %d\n", s->runtime_parms.scanner_line_size, pause_limit); #endif setreg (s, 0x4e, pause_limit); /* max Kb to pause */ setreg (s, 0x4f, 0x02); /* min Kb to resume */ line_rate_color = 1; step_size = (s->user_parms.vertical_resolution * line_end * line_rate_color) / (4 * s->hw_parms.motor_full_steps_per_inch); if (s->val[OPT_BACKTRACK].b) { steps_to_reverse = 0x3f; setreg (s, 0x50, steps_to_reverse); setreg (s, 0x51, 0x15); /* accel profile */ } else { s->hw_parms.steps_to_reverse = 0; setreg (s, 0x50, s->hw_parms.steps_to_reverse); setreg (s, 0x51, 0); /* accel profile */ s->hw_parms.fsteps_25_speed = 0; s->hw_parms.fsteps_50_speed = 0; } dpd = compute_dpd (s, step_size, line_end); /* 0x0ada; */ #ifdef DEBUG fprintf (stderr, "dpd = %d\n", dpd); #endif setreg (s, 0x52, HIBYTE (dpd)); setreg (s, 0x53, LOBYTE (dpd)); setreg (s, 0x46, HIBYTE (step_size)); setreg (s, 0x47, LOBYTE (step_size)); ff_step_size = compute_fastfeed_step_size (s->hw_parms.crystal_frequency, s->mclk_div, s->hw_parms.motor_max_speed, s->hw_parms. motor_full_steps_per_inch, 0); setreg (s, 0x48, HIBYTE (ff_step_size)); setreg (s, 0x49, LOBYTE (ff_step_size)); setreg (s, 0x4b, 0x15); /* this is to stay the motor running */ setbits (s, 0x45, (1 << 4)); setreg (s, 0x4a, HIBYTE (47 + s->runtime_parms.steps_to_skip)); setreg (s, 0x4b, LOBYTE (47 + s->runtime_parms.steps_to_skip)); setreg (s, 0x58, 0); ciclic_buffer_init (&(s->ciclic_buffer), s->runtime_parms.image_line_size, s->user_parms.vertical_resolution, s->runtime_parms.status_bytes); s->runtime_parms.num_bytes_left_to_scan = s->user_parms.lines_to_scan * s->runtime_parms.image_line_size; #ifdef DEBUG fprintf (stderr, "bytes to scan = %ld\n", s->runtime_parms.num_bytes_left_to_scan); #endif cache_write (s); #ifdef DEBUG lm9830_dump_registers (s->fd); #endif lm9830_reset (s->fd); setreg (s, 0x07, 0x03); usleep (100); return SANE_STATUS_GOOD; } static SANE_Status end_scan (HP4200_Scanner * s) { s->scanning = SANE_FALSE; setreg (s, 0x07, 0x00); lm9830_reset (s->fd); setbits (s, 0x58, PAPER_SENSOR_2_STOP_SCAN); cache_write (s); setreg (s, 0x07, 0x02); /* Free some buffers */ if (s->ciclic_buffer.buffer) { free (s->ciclic_buffer.buffer); s->ciclic_buffer.buffer = NULL; } if (s->ciclic_buffer.buffer_ptrs) { free (s->ciclic_buffer.buffer_ptrs); s->ciclic_buffer.buffer_ptrs = NULL; } if (s->scanner_buffer.buffer) { free (s->scanner_buffer.buffer); s->scanner_buffer.buffer = NULL; } return SANE_STATUS_GOOD; } static int hp4200_init_scanner (HP4200_Scanner * s) { int ff_step_size; int mclk_div; lm9830_ini_scanner (s->fd, NULL); hp4200_init_registers (s); scanner_buffer_init (&(s->scanner_buffer), s->hw_parms.SRAM_size); setreg (s, 0x07, 0x08); usleep (10 * 1000); setreg (s, 0x07, 0x00); usleep (10 * 1000); mclk_div = 2; setreg (s, 0x08, (mclk_div - 1) * 2); ff_step_size = compute_fastfeed_step_size (s->hw_parms.crystal_frequency, mclk_div, s->hw_parms.motor_max_speed, s->hw_parms.motor_full_steps_per_inch, 0); setreg (s, 0x48, HIBYTE (ff_step_size)); setreg (s, 0x49, LOBYTE (ff_step_size)); setbits (s, 0x45, (1 << 4)); cache_write (s); return 0; } static void ciclic_buffer_copy (ciclic_buffer_t * cb, SANE_Byte * buf, SANE_Int num_bytes, int image_line_size, int status_bytes) { int biggest_upper_block_size; int upper_block_size; int lower_block_size; int bytes_to_be_a_entire_line; /* copy the upper block */ biggest_upper_block_size = cb->size - (cb->buffer_position - cb->buffer); upper_block_size = min (biggest_upper_block_size, num_bytes); memcpy (buf, cb->buffer_position, upper_block_size); cb->good_bytes -= upper_block_size; bytes_to_be_a_entire_line = (cb->buffer_position - cb->buffer) % image_line_size; cb->can_consume += upper_block_size + status_bytes * (((bytes_to_be_a_entire_line + upper_block_size) / image_line_size) - 1); if (num_bytes < biggest_upper_block_size) { cb->buffer_position += num_bytes; return; } /* copy the lower block */ lower_block_size = num_bytes - biggest_upper_block_size; if (lower_block_size > 0) { memcpy (buf + biggest_upper_block_size, cb->buffer, lower_block_size); cb->good_bytes -= lower_block_size; cb->can_consume += lower_block_size + status_bytes * (lower_block_size / image_line_size); cb->buffer_position = cb->buffer + lower_block_size; } else { cb->buffer_position = cb->buffer; } assert (cb->good_bytes >= 0); assert (lower_block_size >= 0); } static void ciclic_buffer_consume (ciclic_buffer_t * cb, scanner_buffer_t * scanner_buffer, int image_width, int status_bytes) { int to_consume; int to_consume_now; int i; int processed; to_consume = min (cb->can_consume, scanner_buffer->num_bytes); while (to_consume) { if (cb->pixel_position == image_width) { if (scanner_buffer->num_bytes >= status_bytes) { /* forget status bytes */ scanner_buffer->data_ptr += status_bytes; scanner_buffer->num_bytes -= status_bytes; cb->can_consume -= status_bytes; to_consume -= status_bytes; cb->pixel_position = 0; /* back to the start pixel */ cb->red_idx = (cb->red_idx + 1) % cb->num_lines; cb->green_idx = (cb->green_idx + 1) % cb->num_lines; cb->blue_idx = (cb->blue_idx + 1) % cb->num_lines; cb->current_line++; } else break; } to_consume_now = min ((image_width - cb->pixel_position) * 3, to_consume); if (to_consume_now < 3) break; for (i = cb->pixel_position * 3; to_consume_now >= 3; i += 3, to_consume_now -= 3) { cb->buffer_ptrs[cb->red_idx][i] = scanner_buffer->data_ptr[0]; cb->buffer_ptrs[cb->green_idx][i + 1] = scanner_buffer->data_ptr[1]; cb->buffer_ptrs[cb->blue_idx][i + 2] = scanner_buffer->data_ptr[2]; scanner_buffer->data_ptr += 3; } processed = i - (cb->pixel_position * 3); cb->pixel_position = i / 3; to_consume -= processed; cb->can_consume -= processed; scanner_buffer->num_bytes -= processed; if (cb->current_line > cb->first_good_line) cb->good_bytes += processed; } } SANE_Status sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len) { SANE_Status status; int to_copy_now; int bytes_to_copy_to_frontend; HP4200_Scanner *s = h; static char me[] = "sane_read"; DBG (DBG_proc, "%s\n", me); if (!(s->scanning)) { /* OOPS, not scanning */ return SANE_STATUS_CANCELLED; } if (!buf || !len) return SANE_STATUS_INVAL; *len = 0; if (s->runtime_parms.num_bytes_left_to_scan == 0) { end_scan (s); return SANE_STATUS_EOF; } bytes_to_copy_to_frontend = min (s->runtime_parms.num_bytes_left_to_scan, maxlen); /* first copy available data from the ciclic buffer */ to_copy_now = min (s->ciclic_buffer.good_bytes, bytes_to_copy_to_frontend); if (to_copy_now > 0) { ciclic_buffer_copy (&(s->ciclic_buffer), buf, to_copy_now, s->runtime_parms.image_line_size, s->runtime_parms.status_bytes); buf += to_copy_now; bytes_to_copy_to_frontend -= to_copy_now; *len += to_copy_now; } /* if not enough bytes, get data from the scanner */ while (bytes_to_copy_to_frontend) { if (s->scanner_buffer.num_bytes < 3) { /* cicl buf consumes modulo 3 bytes at least now for rgb color 8 bpp fixme: but this is ugly and not generic */ status = scanner_buffer_read (s); if (status == SANE_STATUS_CANCELLED) { end_scan (s); s->aborted_by_user = SANE_FALSE; return status; } if (status != SANE_STATUS_GOOD) return status; } while ((s->scanner_buffer.num_bytes > 3) && bytes_to_copy_to_frontend) { ciclic_buffer_consume (&(s->ciclic_buffer), &(s->scanner_buffer), s->user_parms.image_width, s->runtime_parms.status_bytes); to_copy_now = min (s->ciclic_buffer.good_bytes, bytes_to_copy_to_frontend); if (to_copy_now > 0) { ciclic_buffer_copy (&(s->ciclic_buffer), buf, to_copy_now, s->runtime_parms.image_line_size, s->runtime_parms.status_bytes); buf += to_copy_now; bytes_to_copy_to_frontend -= to_copy_now; *len += to_copy_now; } } } s->runtime_parms.num_bytes_left_to_scan -= *len; if (s->runtime_parms.num_bytes_left_to_scan < 0) *len += s->runtime_parms.num_bytes_left_to_scan; return SANE_STATUS_GOOD; } static HP4200_Device * find_device (SANE_String_Const name) { static char me[] = "find_device"; HP4200_Device *dev; DBG (DBG_proc, "%s\n", me); for (dev = first_device; dev; dev = dev->next) { if (strcmp (dev->dev.name, name) == 0) { return dev; } } return NULL; } static SANE_Status add_device (SANE_String_Const name, HP4200_Device ** argpd) { int fd; HP4200_Device *pd; static const char me[] = "add_device"; SANE_Status status; DBG (DBG_proc, "%s(%s)\n", me, name); /* Avoid adding the same device more than once */ if ((pd = find_device (name))) { if (argpd) *argpd = pd; return SANE_STATUS_GOOD; } /* open the device file, but read only or read/write to perform ioctl's ? */ if ((status = sanei_usb_open (name, &fd)) != SANE_STATUS_GOOD) { DBG (DBG_error, "%s: open(%s) failed: %s\n", me, name, sane_strstatus (status)); return SANE_STATUS_INVAL; } /* put here some code to probe that the device attached to the device file is a supported scanner. Maybe some ioctl */ sanei_usb_close (fd); pd = (HP4200_Device *) calloc (1, sizeof (HP4200_Device)); if (!pd) { DBG (DBG_error, "%s: out of memory allocating device.\n", me); return SANE_STATUS_NO_MEM; } pd->dev.name = strdup (name); pd->dev.vendor = "Hewlett-Packard"; pd->dev.model = "HP-4200"; pd->dev.type = "flatbed scanner"; if (!pd->dev.name || !pd->dev.vendor || !pd->dev.model || !pd->dev.type) { DBG (DBG_error, "%s: out of memory allocating device descriptor strings.\n", me); free (pd); return SANE_STATUS_NO_MEM; } pd->handle = NULL; pd->next = first_device; first_device = pd; n_devices++; if (argpd) *argpd = pd; return SANE_STATUS_GOOD; } static SANE_Status attach (SANE_String_Const name) { static char me[] = "attach"; DBG (DBG_proc, "%s\n", me); return add_device (name, NULL); } SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { static const char me[] = "sane_hp4200_init"; char dev_name[PATH_MAX]; FILE *fp; (void) authorize; /* keep gcc quiet */ DBG_INIT (); DBG (DBG_proc, "%s\n", me); DBG (DBG_error, "SANE hp4200 backend version %d.%d build %d from %s\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD, PACKAGE_STRING); /* put some version_code checks here */ if (NULL != version_code) { *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); } sanei_usb_init (); sanei_pv8630_init (); fp = sanei_config_open (HP4200_CONFIG_FILE); if (!fp) { DBG (DBG_error, "%s: configuration file not found!\n", me); return SANE_STATUS_INVAL; } else { while (sanei_config_read (dev_name, sizeof (dev_name), fp)) { if (dev_name[0] == '#') /* ignore line comments */ continue; if (strlen (dev_name) == 0) continue; /* ignore empty lines */ DBG (DBG_info, "%s: looking for devices matching %s\n", me, dev_name); sanei_usb_attach_matching_devices (dev_name, attach); } fclose (fp); } return SANE_STATUS_GOOD; } void sane_exit (void) { HP4200_Device *device, *next; DBG (DBG_proc, "sane_hp4200_exit\n"); for (device = first_device; device; device = next) { next = device->next; if (device->handle) { sane_close (device->handle); } if (device->dev.name) { free ((void *) device->dev.name); } free (device); } first_device = NULL; if (devlist) { free (devlist); devlist = NULL; } n_devices = 0; DBG (DBG_proc, "sane_exit: exit\n"); } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { int i; HP4200_Device *pdev; DBG (DBG_proc, "sane_get_devices (%p, %d)\n", (void *) device_list, local_only); /* Waste the last list returned from this function */ if (devlist) free (devlist); devlist = (const SANE_Device **) malloc ((n_devices + 1) * sizeof (SANE_Device *)); if (!devlist) { DBG (DBG_error, "sane_get_devices: out of memory\n"); return SANE_STATUS_NO_MEM; } for (i = 0, pdev = first_device; pdev; i++, pdev = pdev->next) { devlist[i] = &(pdev->dev); } devlist[i] = NULL; *device_list = devlist; DBG (DBG_proc, "sane_get_devices: exit\n"); return SANE_STATUS_GOOD; } static void init_options (HP4200_Scanner * s) { s->opt[OPT_NUM_OPTS].name = ""; s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].unit = SANE_UNIT_NONE; s->opt[OPT_NUM_OPTS].size = sizeof (SANE_Word); s->opt[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; s->opt[OPT_RES].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_RES].title = SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_RES].desc = SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_RES].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_RES].type = SANE_TYPE_INT; s->opt[OPT_RES].size = sizeof (SANE_Word); s->opt[OPT_RES].unit = SANE_UNIT_DPI; s->opt[OPT_RES].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_RES].constraint.word_list = dpi_list; s->val[OPT_RES].w = 150; s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->opt[OPT_TL_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; s->opt[OPT_TL_X].size = sizeof (SANE_Fixed); s->opt[OPT_TL_X].unit = SANE_UNIT_MM; s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_X].constraint.range = &x_range; s->val[OPT_TL_X].w = x_range.min; s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->opt[OPT_TL_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; s->opt[OPT_TL_Y].size = sizeof (SANE_Fixed); s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_Y].constraint.range = &y_range; s->val[OPT_TL_Y].w = y_range.min; s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->opt[OPT_BR_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; s->opt[OPT_BR_X].size = sizeof (SANE_Fixed); s->opt[OPT_BR_X].unit = SANE_UNIT_MM; s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_X].constraint.range = &x_range; s->val[OPT_BR_X].w = x_range.max; s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->opt[OPT_BR_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; s->opt[OPT_BR_Y].size = sizeof (SANE_Fixed); s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_Y].constraint.range = &y_range; s->val[OPT_BR_Y].w = y_range.max; s->opt[OPT_BACKTRACK].name = SANE_NAME_BACKTRACK; s->opt[OPT_BACKTRACK].title = SANE_TITLE_BACKTRACK; s->opt[OPT_BACKTRACK].desc = SANE_DESC_BACKTRACK; s->opt[OPT_BACKTRACK].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_BACKTRACK].type = SANE_TYPE_BOOL; s->opt[OPT_BACKTRACK].size = sizeof (SANE_Bool); s->opt[OPT_BACKTRACK].unit = SANE_UNIT_NONE; s->opt[OPT_BACKTRACK].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_BACKTRACK].b = SANE_TRUE; s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_R].size = 1024 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_R].wa = s->user_parms.gamma[0]; s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_G].size = 1024 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_G].wa = s->user_parms.gamma[1]; s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_B].size = 1024 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_B].wa = s->user_parms.gamma[2]; { int i; double gamma = 2.0; for (i = 0; i < 1024; i++) { s->user_parms.gamma[0][i] = 255 * pow (((double) i + 1) / 1024, 1.0 / gamma); s->user_parms.gamma[1][i] = s->user_parms.gamma[0][i]; s->user_parms.gamma[2][i] = s->user_parms.gamma[0][i]; #ifdef DEBUG printf ("%d %d\n", i, s->user_parms.gamma[0][i]); #endif } } /* preview */ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; s->opt[OPT_PREVIEW].size = sizeof (SANE_Word); s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; s->val[OPT_PREVIEW].w = SANE_FALSE; } SANE_Status sane_open (SANE_String_Const name, SANE_Handle * h) { static const char me[] = "sane_hp4200_open"; SANE_Status status; HP4200_Device *dev; HP4200_Scanner *s; DBG (DBG_proc, "%s (%s, %p)\n", me, name, (void *) h); if (name && name[0]) { dev = find_device (name); if (!dev) { status = add_device (name, &dev); if (status != SANE_STATUS_GOOD) return status; } } else { dev = first_device; } if (!dev) return SANE_STATUS_INVAL; if (!h) return SANE_STATUS_INVAL; s = *h = (HP4200_Scanner *) calloc (1, sizeof (HP4200_Scanner)); if (!s) { DBG (DBG_error, "%s: out of memory creating scanner structure.\n", me); return SANE_STATUS_NO_MEM; } dev->handle = s; s->aborted_by_user = SANE_FALSE; s->ciclic_buffer.buffer = NULL; s->scanner_buffer.buffer = NULL; s->dev = dev; s->user_parms.image_width = 0; s->user_parms.lines_to_scan = 0; s->user_parms.vertical_resolution = 0; s->scanning = SANE_FALSE; s->fd = -1; init_options (s); if ((sanei_usb_open (dev->dev.name, &s->fd) != SANE_STATUS_GOOD)) { DBG (DBG_error, "%s: Can't open %s.\n", me, dev->dev.name); return SANE_STATUS_IO_ERROR; /* fixme: return busy when file is being accessed already */ } return SANE_STATUS_GOOD; } void sane_close (SANE_Handle h) { HP4200_Scanner *s = (HP4200_Scanner *) h; DBG (DBG_proc, "sane_hp4200_close (%p)\n", (void *) h); if (s) { s->dev->handle = NULL; if (s->fd != -1) { sanei_usb_close (s->fd); } free (s); } } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle h, SANE_Int n) { static char me[] = "sane_get_option_descriptor"; HP4200_Scanner *s = (HP4200_Scanner *) h; DBG (DBG_proc, "%s\n", me); if ((n < 0) || (n >= NUM_OPTIONS)) return NULL; return s->opt + n; } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { HP4200_Scanner *s = (HP4200_Scanner *) handle; SANE_Status status; SANE_Int myinfo = 0; SANE_Word cap; DBG (DBG_proc, "sane_control_option\n"); if (info) *info = 0; if (s->scanning) { return SANE_STATUS_DEVICE_BUSY; } if (option < 0 || option >= NUM_OPTIONS) { return SANE_STATUS_INVAL; } cap = s->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) { return SANE_STATUS_INVAL; } if (action == SANE_ACTION_GET_VALUE) { switch (option) { case OPT_NUM_OPTS: case OPT_RES: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_PREVIEW: *(SANE_Word *) val = s->val[option].w; break; case OPT_BACKTRACK: *(SANE_Bool *) val = s->val[option].b; break; case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: memcpy (val, s->val[option].wa, s->opt[option].size); break; default: return SANE_STATUS_UNSUPPORTED; } } else if (action == SANE_ACTION_SET_VALUE) { if (!SANE_OPTION_IS_SETTABLE (cap)) { DBG (DBG_error, "could not set option, not settable\n"); return SANE_STATUS_INVAL; } status = sanei_constrain_value (s->opt + option, val, &myinfo); if (status != SANE_STATUS_GOOD) return status; switch (option) { /* Numeric side-effect free options */ case OPT_PREVIEW: s->val[option].w = *(SANE_Word *) val; return SANE_STATUS_GOOD; /* Numeric side-effect options */ case OPT_RES: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: myinfo |= SANE_INFO_RELOAD_PARAMS; s->val[option].w = *(SANE_Word *) val; break; case OPT_BACKTRACK: s->val[option].b = *(SANE_Bool *) val; break; case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: memcpy (s->val[option].wa, val, s->opt[option].size); break; default: return SANE_STATUS_UNSUPPORTED; } } else { return SANE_STATUS_UNSUPPORTED; } if (info) *info = myinfo; return SANE_STATUS_GOOD; } static void compute_parameters (HP4200_Scanner * s) { int resolution; int opt_tl_x; int opt_br_x; int opt_tl_y; int opt_br_y; if (s->val[OPT_PREVIEW].w == SANE_TRUE) { resolution = 50; opt_tl_x = SANE_UNFIX (x_range.min); opt_tl_y = SANE_UNFIX (y_range.min); opt_br_x = SANE_UNFIX (x_range.max); opt_br_y = SANE_UNFIX (y_range.max); } else { resolution = s->val[OPT_RES].w; opt_tl_x = SANE_UNFIX (s->val[OPT_TL_X].w); opt_tl_y = SANE_UNFIX (s->val[OPT_TL_Y].w); opt_br_x = SANE_UNFIX (s->val[OPT_BR_X].w); opt_br_y = SANE_UNFIX (s->val[OPT_BR_Y].w); } s->user_parms.horizontal_resolution = resolution; s->user_parms.vertical_resolution = resolution; s->runtime_parms.steps_to_skip = floor (300.0 / MM_PER_INCH * opt_tl_y); s->user_parms.lines_to_scan = floor ((opt_br_y - opt_tl_y) / MM_PER_INCH * resolution); s->user_parms.image_width = floor ((opt_br_x - opt_tl_x) / MM_PER_INCH * resolution); s->runtime_parms.first_pixel = floor (opt_tl_x / MM_PER_INCH * resolution); /* fixme: add support for more depth's and bpp's. */ s->runtime_parms.image_line_size = s->user_parms.image_width * 3; } SANE_Status sane_get_parameters (SANE_Handle h, SANE_Parameters * p) { static char me[] = "sane_get_parameters"; HP4200_Scanner *s = (HP4200_Scanner *) h; DBG (DBG_proc, "%s\n", me); if (!p) return SANE_STATUS_INVAL; p->format = SANE_FRAME_RGB; p->last_frame = SANE_TRUE; p->depth = 8; if (!s->scanning) { compute_parameters (s); } p->lines = s->user_parms.lines_to_scan; p->pixels_per_line = s->user_parms.image_width; p->bytes_per_line = s->runtime_parms.image_line_size; return SANE_STATUS_GOOD; } SANE_Status sane_start (SANE_Handle h) { HP4200_Scanner *s = (HP4200_Scanner *) h; struct coarse_t coarse; static char me[] = "sane_start"; DBG (DBG_proc, "%s\n", me); s->scanning = SANE_TRUE; s->aborted_by_user = SANE_FALSE; s->user_parms.color = SANE_TRUE; compute_parameters (s); hp4200_init_scanner (s); hp4200_goto_home (s); hp4200_wait_homed (s); /* restore default register values here... */ write_gamma (s); hp4200_init_registers (s); lm9830_ini_scanner (s->fd, NULL); /* um... do not call cache_write() here, don't know why :( */ do_coarse_calibration (s, &coarse); do_fine_calibration (s, &coarse); prepare_for_a_scan (s); return SANE_STATUS_GOOD; } void sane_cancel (SANE_Handle h) { static char me[] = "sane_cancel"; HP4200_Scanner *s = (HP4200_Scanner *) h; DBG (DBG_proc, "%s\n", me); s->aborted_by_user = SANE_TRUE; end_scan (s); } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { HP4200_Scanner *dev = handle; SANE_Status status; (void) non_blocking; /* silence gcc */ if (dev->scanning == SANE_FALSE) { return SANE_STATUS_INVAL; } if (non_blocking == SANE_FALSE) { status = SANE_STATUS_GOOD; } else { status = SANE_STATUS_UNSUPPORTED; } DBG (DBG_proc, "sane_set_io_mode: exit\n"); return status; } SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int * fd) { static char me[] = "sane_get_select_fd"; (void) h; /* keep gcc quiet */ (void) fd; /* keep gcc quiet */ DBG (DBG_proc, "%s\n", me); return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/hp4200.conf.in000066400000000000000000000001141456256263500170570ustar00rootroot00000000000000# # Configuration file for the hp4200 backend # # HP4200 usb 0x03f0 0x0105 backends-1.3.0/backend/hp4200.h000066400000000000000000000146671456256263500157760ustar00rootroot00000000000000/* Copyright (C) 2000 by Adrian Perez Jorge 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, see . */ #ifndef _HP4200_H #define _HP4200_H #include #define MCLKDIV_SCALING 2 #define NUM_REGISTERS 0x80 #define min(a, b) (((a) < (b)) ? (a) : (b)) #define max(a, b) (((a) > (b)) ? (a) : (b)) /*--------------------------------------------------------------------------*/ #define DBG_error0 0 #define DBG_error 1 #define DBG_sense 2 #define DBG_warning 3 #define DBG_inquiry 4 #define DBG_info 5 #define DBG_info2 6 #define DBG_proc 7 #define DBG_read 8 #define DBG_sane_init 10 #define DBG_sane_proc 11 #define DBG_sane_info 12 #define DBG_sane_option 13 /*--------------------------------------------------------------------------*/ enum HP4200_Option { OPT_NUM_OPTS = 0, OPT_RES, OPT_TL_X, /* top-left x */ OPT_TL_Y, /* top-left y */ OPT_BR_X, /* bottom-right x */ OPT_BR_Y, /* bottom-right y */ OPT_BACKTRACK, OPT_GAMMA_VECTOR_R, OPT_GAMMA_VECTOR_G, OPT_GAMMA_VECTOR_B, OPT_PREVIEW, NUM_OPTIONS /* must come last */ }; /* already declared in the sane includes... typedef union { SANE_Word w; SANE_Bool b; SANE_Fixed f; SANE_Word *wa; } Option_Value; */ enum ScannerModels { HP4200 }; typedef struct HP4200_Device { struct HP4200_Device *next; SANE_Device dev; SANE_Handle handle; } HP4200_Device; struct _scanner_buffer_t { unsigned char *buffer; /* buffer memory space */ int size; /* size of the buffer */ int num_bytes; /* number of bytes left (to read) */ unsigned char *data_ptr; /* cursor in buffer */ }; typedef struct _scanner_buffer_t scanner_buffer_t; struct _ciclic_buffer_t { int good_bytes; /* number of valid bytes of the image */ int num_lines; /* number of lines of the ciclic buffer */ int size; /* size in bytes of the buffer space */ unsigned char *buffer; /* pointer to the buffer space */ unsigned char **buffer_ptrs; /* pointers to the beginning of each line in the buffer space */ int can_consume; /* num of bytes the ciclic buf can consume */ int current_line; /* current scanned line */ int first_good_line; /* number of lines to fill the ``pipeline'' */ unsigned char *buffer_position; /* pointer to the first byte that can be copied */ int pixel_position; /* pixel position in current line */ /* color indexes for the proper line in the ciclic buffer */ int red_idx; int green_idx; int blue_idx; }; typedef struct _ciclic_buffer_t ciclic_buffer_t; struct _hardware_parameters_t { unsigned int SRAM_size; unsigned char SRAM_bandwidth; unsigned long crystal_frequency; unsigned int min_pixel_data_buffer_limit; unsigned int motor_full_steps_per_inch; float motor_max_speed; unsigned int scan_bar_max_speed; unsigned int start_of_scanning_area; unsigned int calibration_strip_height; unsigned int scan_area_width; double scan_area_length; /* in inches */ unsigned int sensor_num_pixels; unsigned int sensor_pixel_start; unsigned int sensor_pixel_end; int sensor_cds_state; /* 0 == off, 1 == on */ int sensor_signal_polarity; /* 0 == ??, 1 == ?? */ int sensor_max_integration_time; int sensor_line_separation; int sensor_type; unsigned int sensor_resolution; int sensor_control_signals_polarity; int sensor_control_signals_state; int sensor_control_pixel_rate_timing; int sensor_control_line_rate_timing; unsigned int sensor_black_clamp_timing; /* ??? */ unsigned int sensor_CIS_timing; int sensor_toshiba_timing; int sensor_color_modes; /* bitmask telling color modes supported */ int illumination_mode; int motor_control_mode; int motor_paper_sense_mode; int motor_pause_reverse_mode; int misc_io_mode; int num_tr_pulses; int guard_band_duration; int pulse_duration; int fsteps_25_speed; int fsteps_50_speed; int steps_to_reverse; struct { int red; int green; int blue; } target_value; unsigned short home_sensor; }; typedef struct _hardware_parameters_t hardware_parameters_t; struct _user_parameters_t { unsigned int image_width; unsigned int lines_to_scan; unsigned int horizontal_resolution; unsigned int vertical_resolution; int hres_reduction_method; /* interpolation/??? */ int vres_reduction_method; SANE_Bool color; /* color/grayscale */ int bpp; int scan_mode; /* preview/full scan */ SANE_Bool no_reverse; SANE_Word gamma[3][1024]; /* gamma table for rgb */ }; typedef struct _user_parameters_t user_parameters_t; struct _measured_parameters_t { unsigned int datalink_bandwidth; struct { int red; int green; int blue; } coarse_calibration_data; struct { int *pRedOffset; int *pGreenOffset; int *pBlueOffset; int *pRedGain; int *pGreenGain; int *pBlueGain; } fine_calibration_data; int max_integration_time; int color_mode; }; typedef struct _measured_parameters_t measured_parameters_t; struct _runtime_parameters_t { long num_bytes_left_to_scan; int status_bytes; /* number of status bytes per line */ int image_line_size; /* line size in bytes without status bytes */ int scanner_line_size; /* line size in bytes including the status bytes */ int first_pixel; /* first pixel in the line to be scanned */ int steps_to_skip; }; typedef struct _runtime_parameters_t runtime_parameters_t; struct _HP4200_Scanner { struct _HP4200_Scanner *next; SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; SANE_Bool scanning; SANE_Bool aborted_by_user; SANE_Parameters params; HP4200_Device *dev; hardware_parameters_t hw_parms; user_parameters_t user_parms; measured_parameters_t msrd_parms; unsigned int regs[NUM_REGISTERS]; int mclk; float mclk_div; int fd; ciclic_buffer_t ciclic_buffer; scanner_buffer_t scanner_buffer; runtime_parameters_t runtime_parms; }; typedef struct _HP4200_Scanner HP4200_Scanner; #endif /* !_HP4200_H */ backends-1.3.0/backend/hp4200_lm9830.c000066400000000000000000000127641456256263500170010ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2000 Adrian Perez Jorge 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, see . 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 backend for the HP4200C flatbed scanner */ #include "hp4200_lm9830.h" static SANE_Status lm9830_read_register (int fd, unsigned char reg, unsigned char *data) { SANE_Status retval; if (!data) return -SANE_STATUS_INVAL; retval = sanei_pv8630_write_byte (fd, PV8630_REPPADDRESS, reg); if (retval != SANE_STATUS_GOOD) return retval; return sanei_pv8630_read_byte (fd, PV8630_RDATA, data); } static SANE_Status lm9830_write_register (int fd, unsigned char reg, unsigned char value) { SANE_Status retval; retval = sanei_pv8630_write_byte (fd, PV8630_REPPADDRESS, reg); if (retval != SANE_STATUS_GOOD) return retval; return sanei_pv8630_write_byte (fd, PV8630_RDATA, value); } #ifdef DEBUG static int lm9830_dump_registers (int fd) { int i; unsigned char value = 0; for (i = 0; i < 0x80; i++) { lm9830_read_register (fd, i, &value); printf ("%.2x:0x%.2x", i, value); if ((i + 1) % 8) printf (", "); else printf ("\n"); } puts (""); return 0; } #endif #if 0 static int pv8630_reset_buttons (int fd) { lm9830_write_register (fd, 0x59, 0x10); lm9830_write_register (fd, 0x59, 0x90); return 0; } #endif #if 0 static int lm9830_lamp_off (int fd) { lm9830_write_register (fd, 0x07, 0x00); lm9830_write_register (fd, 0x2c, 0x00); lm9830_write_register (fd, 0x2d, 0x01); lm9830_write_register (fd, 0x2e, 0x3f); lm9830_write_register (fd, 0x2f, 0xff); return 0; } static int lm9830_lamp_on (int fd) { lm9830_write_register (fd, 0x07, 0x00); lm9830_write_register (fd, 0x2c, 0x3f); lm9830_write_register (fd, 0x2d, 0xff); lm9830_write_register (fd, 0x2e, 0x00); lm9830_write_register (fd, 0x2f, 0x01); return 0; } #endif #if 0 /* * This function prints what button was pressed (the time before this * code was executed). */ static int hp4200c_what_button (int fd) { unsigned char button; pv8630_read_buttons (fd, &button); if (button & 0x08) puts ("Scan"); if (button & 0x10) puts ("Copy"); if (button & 0x20) puts ("E-mail"); if ((button & 0x38) == 0) puts ("None"); pv8630_reset_buttons (fd); return 0; } #endif static int lm9830_ini_scanner (int fd, unsigned char *regs) { #ifdef unused unsigned char inittable[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1A, 0x00, 0x0A, 0x60, 0x2F, 0x13, 0x06, 0x17, 0x01, 0x03, 0x03, 0x05, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x21, 0x00, 0x40, 0x15, 0x18, 0x00, 0x40, 0x02, 0x98, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x3F, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x30, 0x25, 0x25, 0x24, 0x28, 0x24, 0x28, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1D, 0x00, 0x13, 0x05, 0x48, 0x01, 0xC2, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x02, 0x00, 0x15, 0x00, 0x45, 0x00, 0x10, 0x08, 0x17, 0x2B, 0x90, 0x00, 0x00, 0x01, 0x00, 0x80, 0x00 }; #endif unsigned char daisy[] = { 0x99, 0x66, 0xcc, 0x33 }; unsigned char *regdata; unsigned int i; sanei_pv8630_write_byte (fd, PV8630_RMODE, 0x02); for (i = 0; i < sizeof (daisy); i++) { sanei_pv8630_write_byte (fd, PV8630_RDATA, daisy[i]); } sanei_pv8630_write_byte (fd, PV8630_RMODE, 0x16); lm9830_write_register (fd, 0x42, 0x06); if (!regs) return 0; /* regdata = inittable; */ else regdata = regs; for (i = 8; i < 0x60; i++) { lm9830_write_register (fd, i, regdata[i]); } for (i = 0x60; i < 0x70; i++) { lm9830_write_register (fd, i, 0); } lm9830_write_register (fd, 0x70, 0x70); for (i = 0x71; i < 0x80; i++) { lm9830_write_register (fd, i, 0); } return 0; } static int lm9830_reset (int fd) { lm9830_write_register (fd, 0x07, 0x08); usleep (100); lm9830_write_register (fd, 0x07, 0x00); usleep (100); return 0; } backends-1.3.0/backend/hp4200_lm9830.h000066400000000000000000000136171456256263500170040ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2000 Adrian Perez Jorge 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, see . 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 backend for the HP4200C flatbed scanner */ #define INPUT_SIGNAL_POLARITY_NEGATIVE 0 #define INPUT_SIGNAL_POLARITY_POSITIVE 1 #define CDS_OFF 0 #define CDS_ON (1 << 1) #define SENSOR_EVENODD (1 << 2) #define SENSOR_STANDARD 0 #define SENSOR_RESOLUTION_300 0 #define SENSOR_RESOLUTION_600 (1 << 3) #define LINE_SKIPPING_COLOR_PHASE_DELAY(n) (((n) & 0x0f) << 4) #define PHI1_POLARITY_POSITIVE 0 #define PHI1_POLARITY_NEGATIVE 1 #define PHI2_POLARITY_POSITIVE 0 #define PHI2_POLARITY_NEGATIVE (1 << 1) #define RS_POLARITY_POSITIVE 0 #define RS_POLARITY_NEGATIVE (1 << 2) #define CP1_POLARITY_POSITIVE 0 #define CP1_POLARITY_NEGATIVE (1 << 3) #define CP2_POLARITY_POSITIVE 0 #define CP2_POLARITY_NEGATIVE (1 << 4) #define TR1_POLARITY_POSITIVE 0 #define TR1_POLARITY_NEGATIVE (1 << 5) #define TR2_POLARITY_POSITIVE 0 #define TR2_POLARITY_NEGATIVE (1 << 6) #define PHI1_OFF 0 #define PHI1_ACTIVE 1 #define PHI2_OFF 0 #define PHI2_ACTIVE (1 << 1) #define RS_OFF 0 #define RS_ACTIVE (1 << 2) #define CP1_OFF 0 #define CP1_ACTIVE (1 << 3) #define CP2_OFF 0 #define CP2_ACTIVE (1 << 4) #define TR1_OFF 0 #define TR1_ACTIVE (1 << 5) #define TR2_OFF 0 #define TR2_ACTIVE (1 << 6) #define NUMBER_OF_TR_PULSES(n) (((n) - 1) << 7) #define TR_PULSE_DURATION(n) ((n) & 0x0f) #define TR_PHI1_GUARDBAND_DURATION(n) (((n) & 0x0f) << 4) #define CIS_TR1_TIMING_OFF 0 #define CIS_TR1_TIMING_MODE1 1 #define CIS_TR1_TIMING_MODE2 2 #define FAKE_OPTICAL_BLACK_PIXELS_OFF 0 #define FAKE_OPTICAL_BLACK_PIXELS_ON (1 << 2) #define PIXEL_RATE_3_CHANNELS 0 #define LINE_RATE_3_CHANNELS 1 #define MODEA_1_CHANNEL 4 #define MODEB_1_CHANNEL 5 #define GRAY_CHANNEL_RED 0 #define GRAY_CHANNEL_GREEN (1 << 3) #define GRAY_CHANNEL_BLU (2 << 3) #define TR_RED(n) ((n) << 5) #define TR_GREEN(n) ((n) << 6) #define TR_BLUE(n) ((n) << 7) #define TR_RED_DROP(n) (n) #define TR_GREEN_DROP(n) ((n) << 2) #define TR_BLUE_DROP(n) ((n) << 4) #define ILLUMINATION_MODE(n) (n) #define HIBYTE(w) ((SANE_Byte)(((w) >> 8) & 0xff)) #define LOBYTE(w) ((SANE_Byte)((w) & 0xff)) #define EPP_MODE 0 #define NIBBLE_MODE 1 #define PPORT_DRIVE_CURRENT(n) ((n) << 1) #define RAM_SIZE_64 0 #define RAM_SIZE_128 1 #define RAM_SIZE_256 2 #define SRAM_DRIVER_CURRENT(n) ((n) << 2) #define SRAM_BANDWIDTH_4 0 #define SRAM_BANDWIDTH_8 (1 << 4) #define SCANNING_FULL_DUPLEX 0 #define SCANNING_HALF_DUPLEX (1 << 5) #define FULL_STEPPING 0 #define MICRO_STEPPING 1 #define CURRENT_SENSING_PHASES(n) (((n) - 1) << 1) #define PHASE_A_POLARITY_POSITIVE 0 #define PHASE_A_POLARITY_NEGATIVE (1 << 2) #define PHASE_B_POLARITY_POSITIVE 0 #define PHASE_B_POLARITY_NEGATIVE (1 << 3) #define STEPPER_MOTOR_TRISTATE 0 #define STEPPER_MOTOR_OUTPUT (1 << 4) #define ACCELERATION_PROFILE_STOPPED(n) (n) #define ACCELERATION_PROFILE_25P(n) ((n) << 2) #define ACCELERATION_PROFILE_50P(n) ((n) << 4) #define NON_REVERSING_EXTRA_LINES(n) (n) #define FIRST_LINE_TO_PROCESS(n) ((n) << 3) #define KICKSTART_STEPS(n) (n) #define HOLD_CURRENT_TIMEOUT(n) ((n) << 3) #define PAPER_SENSOR_1_POLARITY_LOW 0 #define PAPER_SENSOR_1_POLARITY_HIGH 1 #define PAPER_SENSOR_1_TRIGGER_LEVEL 0 #define PAPER_SENSOR_1_TRIGGER_EDGE (1 << 1) #define PAPER_SENSOR_1_NO_STOP_SCAN 0 #define PAPER_SENSOR_1_STOP_SCAN (1 << 2) #define PAPER_SENSOR_2_POLARITY_LOW 0 #define PAPER_SENSOR_2_POLARITY_HIGH (1 << 3) #define PAPER_SENSOR_2_TRIGGER_LEVEL 0 #define PAPER_SENSOR_2_TRIGGER_EDGE (1 << 4) #define PAPER_SENSOR_2_NO_STOP_SCAN 0 #define PAPER_SENSOR_2_STOP_SCAN (1 << 5) #define MISCIO_1_TYPE_INPUT 0 #define MISCIO_1_TYPE_OUTPUT 1 #define MISCIO_1_POLARITY_LOW 0 #define MISCIO_1_POLARITY_HIGH (1 << 1) #define MISCIO_1_TRIGGER_LEVEL 0 #define MISCIO_1_TRIGGER_EDGE (1 << 2) #define MISCIO_1_OUTPUT_STATE_LOW 0 #define MISCIO_1_OUTPUT_STATE_HIGH (1 << 3) #define MISCIO_2_TYPE_INPUT 0 #define MISCIO_2_TYPE_OUTPUT (1 << 4) #define MISCIO_2_POLARITY_LOW 0 #define MISCIO_2_POLARITY_HIGH (1 << 5) #define MISCIO_2_TRIGGER_LEVEL 0 #define MISCIO_2_TRIGGER_EDGE (1 << 6) #define MISCIO_2_OUTPUT_STATE_LOW 0 #define MISCIO_2_OUTPUT_STATE_HIGH (1 << 7) #define PIXEL_PACKING(n) ((n) << 3) #define DATAMODE(n) ((n) << 5) backends-1.3.0/backend/hp5400.c000066400000000000000000000054311456256263500157610ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2003 Martijn van Oosterhout Copyright (C) 2003 Thomas Soumarmon This file was initially copied from the hp3300 testools and adjusted to suit. Original copyright notice follows: Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl) 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, see . 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. */ /* SANE interface for hp54xx scanners. Prototype. Parts of this source were inspired by other backends. */ #include "../include/sane/config.h" #include "hp5400_debug.h" #include "hp5400.h" #include "../include/sane/config.h" #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_config.h" #include "../include/sane/saneopts.h" #include /* malloc, free */ #include /* memcpy */ #include #ifdef HAVE_SYS_TYPES_H #include #endif #define HP5400_CONFIG_FILE "hp5400.conf" #define BUILD 3 /* (source) includes for data transfer methods */ #include "hp5400_debug.c" #include "hp5400_internal.c" #include "hp5400_sane.c" #include "hp5400_sanei.c" backends-1.3.0/backend/hp5400.conf.in000066400000000000000000000003561456256263500170720ustar00rootroot00000000000000# 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 backends-1.3.0/backend/hp5400.h000066400000000000000000000121021456256263500157570ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 20020 Ralph Little Copyright (C) 2003 Martijn van Oosterhout Copyright (C) 2003 Thomas Soumarmon Originally copied from HP3300 testtools. Original notice follows: Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl) 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, see . 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. */ /* Core HP5400 functions. */ #ifndef _HP5400_H_ #define _HP5400_H_ #include #include "hp5400_xfer.h" /* for EScannerModel */ #define HW_DPI 300 /* horizontal resolution of hardware */ #define HW_LPI 300 /* vertical resolution of hardware */ enum ScanType { SCAN_TYPE_CALIBRATION, SCAN_TYPE_PREVIEW, SCAN_TYPE_NORMAL }; /* In case we ever need to track multiple models */ typedef struct { char *pszVendor; char *pszName; } TScannerModel; typedef struct { /* transfer buffer */ void *buffer; /* Pointer to memory allocated for buffer */ int roff, goff, boff; /* Offset into buffer of rows to be copied *next* */ int bufstart, bufend; /* What is currently the valid buffer */ int bpp; /* Bytes per pixel per colour (1 or 2) */ int linelength, pixels; /* Bytes per line from scanner */ int transfersize; /* Number of bytes to transfer resulting image */ int blksize; /* Size of blocks to pull from scanner */ int buffersize; /* Size of the buffer */ } TDataPipe; typedef struct { int iXferHandle; /* handle used for data transfer to HW */ TDataPipe pipe; /* Pipe for data */ int iTopLeftX; /* in mm */ int iTopLeftY; /* in mm */ /* int iSensorSkew; *//* in units of 1/1200 inch */ /* int iSkipLines; *//* lines of garbage to skip */ /* int fReg07; *//* NIASH00019 */ /* int fGamma16; *//* if TRUE, gamma entries are 16 bit */ /* int iExpTime; */ /* int iReversedHead; *//* Head is reversed */ /* int iBufferSize; *//* Size of internal scan buffer */ /* EScannerModel eModel; */ } THWParams; /* The scanner needs a Base DPI off which all it's calibration and * offset/size parameters are based. For the time being this is the same as * the iDpi but maybe we want it separate. This is because while this field * would have limited values (300,600,1200,2400) the x/y dpi can vary. The * windows interface seems to allow 200dpi (though I've never tried it). We * need to decide how these values are related to the HW coordinates. */ typedef struct { int iDpi; /* horizontal resolution */ int iLpi; /* vertical resolution */ int iTop; /* in HW coordinates (units HW_LPI) */ int iLeft; /* in HW coordinates (units HW_LPI) */ int iWidth; /* in HW coordinates (units HW_LPI) */ int iHeight; /* in HW coordinates (units HW_LPI) */ int iBytesPerLine; /* Resulting bytes per line */ int iLines; /* Resulting lines of image */ int iLinesRead; /* Lines of image already read */ int iColourOffset; /* How far the colours are offset. Currently this is * set by the caller. This doesn't seem to be * necessary anymore since the scanner is doing it * internally. Leave it for the time being as it * may be needed later. */ } TScanParams; /* * Panel settings. We can read and set these. * */ typedef struct { SANE_Word copycount; // 0..99 LCD display value SANE_Word bwcolour; // 1=Colour or 2=Black/White from scan type LEDs } TPanelInfo; #endif /* NO _HP5400_H_ */ backends-1.3.0/backend/hp5400_debug.c000066400000000000000000000044671456256263500171370ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2003 Martijn van Oosterhout Copyright (C) 2003 Thomas Soumarmon Originally copied from HP3300 testtools. Original notice follows: Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl) 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, see . 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 "hp5400_debug.h" #ifdef STANDALONE #include FILE *DBG_ASSERT = NULL; FILE *DBG_ERR = NULL; FILE *DBG_MSG = NULL; void hp5400_dbg_start() { DBG_MSG = stdout; DBG_ERR = stderr; DBG_ASSERT = stderr; } #else /* #define DEBUG_NOT_STATIC */ #undef DEBUG_DECLARE_ONLY #undef _SANEI_DEBUG_H #include "../include/sane/sanei_debug.h" #endif backends-1.3.0/backend/hp5400_debug.h000066400000000000000000000047061456256263500171400ustar00rootroot00000000000000#ifndef __HP5400_DEBUG_H_ #define __HP5400_DEBUG_H_ /* sane - Scanner Access Now Easy. Copyright (C) 2003 Martijn van Oosterhout Copyright (C) 2003 Thomas Soumarmon Originally copied from HP3300 testtools. Original notice follows: Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl) 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, see . 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 STANDALONE #define DEBUG_NOT_STATIC #define DEBUG_DECLARE_ONLY #include "../include/sane/sanei_debug.h" #define DBG_ASSERT 1 #define DBG_ERR 16 #define DBG_MSG 32 #define HP5400_DBG DBG #define HP5400_SANE_STATIC static #else #include #define LOCAL_DBG #define HP5400_DBG fprintf extern FILE *DBG_ASSERT; extern FILE *DBG_ERR; extern FILE *DBG_MSG; void hp5400_dbg_start(); #define HP5400_SANE_STATIC #endif #endif backends-1.3.0/backend/hp5400_internal.c000066400000000000000000001201151456256263500176520ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2020 Ralph Little Copyright (C) 2003 Martijn van Oosterhout Copyright (C) 2003 Thomas Soumarmon Copyright (c) 2003 Henning Meier-Geinitz, Originally copied from HP3300 testtools. Original notice follows: Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl) 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, see . 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. HP5400/5470 Test util. Currently is only able to read back the scanner version string, but this basically demonstrates ability to communicate with the scanner. Massively expanded. Can do calibration scan, upload gamma and calibration tables and stores the results of a scan. - 19/02/2003 Martijn */ #include /* for printf */ #include /* for exit */ #include #include #include #include #include #include /* for htons */ #include #include "hp5400.h" #include "hp5400_xfer.h" #include "hp5400_internal.h" #include "hp5400_debug.h" /* debug functions */ #ifndef min #define min(A,B) (((A)<(B)) ? (A) : (B)) #endif #ifndef max #define max(A,B) (((A)>(B)) ? (A) : (B)) #endif #ifndef STRING_VERSION_MATCH #define NO_STRING_VERSION_MATCH 1 #endif #ifdef __GNUC__ #define PACKED __attribute__ ((packed)) #else #define PACKED #endif /* If this is enabled, a copy of the raw data from the scanner will be saved to imagedebug.dat and the attempted conversion to imagedebug.ppm */ /* #define IMAGE_DEBUG */ /* If this is defined you get extra info on the calibration */ /* #define CALIB_DEBUG */ typedef struct versionString { char strVersion[128]; } versionString; const int numVersions = 3; versionString *MatchVersions; static TScannerModel Model_HP54xx = { "Hewlett-Packard", "HP54xx Flatbed Scanner" }; HP5400_SANE_STATIC int InitHp5400_internal() { MatchVersions = malloc( sizeof(versionString) * numVersions ); strcpy( MatchVersions[0].strVersion, "SilitekIBlizd C3 ScannerV0.84"); strcpy( MatchVersions[1].strVersion, "SilitekIBlizd C3 ScannerV0.86"); strcpy( MatchVersions[2].strVersion, "SilitekIBlizd C3 ScannerV0.87"); return 1; } HP5400_SANE_STATIC int FreeHp5400_internal() { free(MatchVersions); MatchVersions = NULL; return 1; } HP5400_SANE_STATIC int WriteByte (int iHandle, int cmd, char data) { if (hp5400_command_write (iHandle, cmd, 1, &data) < 0) { HP5400_DBG (DBG_MSG, "failed to send byte (cmd=%04X)\n", cmd); return -1; } return 0; } HP5400_SANE_STATIC int SetLamp (THWParams * pHWParams, int fLampOn) { if (fLampOn) { if (WriteByte (pHWParams->iXferHandle, 0x0000, 0x01) == 0) return 0; } return -1; } HP5400_SANE_STATIC int GetSensors(THWParams * pHWParams, uint16_t *sensorMap) { /* * Read until we get 0. * Max 10 iterations for safety. * */ *sensorMap = 0; uint16_t thisSensorMap = 0; size_t iterCount = 10; do { if (hp5400_command_read (pHWParams->iXferHandle, CMD_GETSENSORS, sizeof (uint16_t), &thisSensorMap) < 0) { HP5400_DBG (DBG_MSG, "failed to read sensors\n"); return -1; } *sensorMap |= thisSensorMap; } while (iterCount-- && (thisSensorMap > 0)); return 0; } HP5400_SANE_STATIC int GetPanelInfo (THWParams * pHWParams, TPanelInfo *panelInfo) { struct PanelInfo info; if (hp5400_command_read (pHWParams->iXferHandle, CMD_READPANEL, sizeof(info), &info) < 0) { HP5400_DBG (DBG_MSG, "failed to read panel info\n"); return -1; } panelInfo->copycount = (SANE_Word)info.copycount; panelInfo->bwcolour = (SANE_Word)info.bwcolour; return 0; } HP5400_SANE_STATIC int SetCopyCount(THWParams * pHWParams, SANE_Word copyCount) { /* * I don't know what most of these things are but it is * necessary to send something sane otherwise we get an error from the scanner. * I got these settings from a USB trace. * Hopefully, we will learn what it is all about at some point * and hopefully it doesn't screw with other settings. * */ uint8_t packetImage[] = {0x02, 0x06, 0x32, 0x01, 0xf2, 0x40, 0x16, 0x01, 0x7b, 0x41, 0x16, 0x01, 0xdc, 0x06, 0x32, 0x01, 0xd7, 0x5b, 0x16, 0x01, 0xac, 0x06, 0x32, 0x01, 0xf8, 0xd7, 0x18, 0x01, 0xd8, 0x06, 0x32, 0x01, 0x2c, 0xf3, 0x12, 0x00, 0x70, 0x8d, 0x18, 0x01, 0x7b, 0x00, 0x00, 0x00}; struct PanelInfo workingInfo; (void)memcpy(&workingInfo, packetImage, sizeof(workingInfo)); workingInfo.copycount = (uint8_t)copyCount; if (hp5400_command_write (pHWParams->iXferHandle, CMD_WRITEPANEL, sizeof(workingInfo), &workingInfo) < 0) { HP5400_DBG (DBG_MSG, "failed to write panel info\n"); return -1; } return 0; } HP5400_SANE_STATIC int SetColourBW(THWParams * pHWParams, SANE_Word colourBW) { /* * I don't know what most of these things are but it is * necessary to send something sane otherwise we get an error from the scanner. * I got these settings from a USB trace. * Hopefully, we will learn what it is all about at some point * and hopefully it doesn't screw with other settings. * */ uint8_t packetImage[] = {0x03, 0x06, 0x32, 0x01, 0xf2, 0x40, 0x16, 0x01, 0x7b, 0x41, 0x16, 0x01, 0xdc, 0x06, 0x32, 0x01, 0xd7, 0x5b, 0x16, 0x01, 0xac, 0x06, 0x32, 0x01, 0xf8, 0xd7, 0x18, 0x01, 0xd8, 0x06, 0x32, 0x01, 0x68, 0xf5, 0x12, 0x00, 0x70, 0x8d, 0x18, 0x01, 0x7b, 0x00, 0x00, 0x00}; struct PanelInfo workingInfo; (void)memcpy(&workingInfo, packetImage, sizeof(workingInfo)); workingInfo.bwcolour = (uint8_t)colourBW; if (hp5400_command_write (pHWParams->iXferHandle, CMD_WRITEPANEL, sizeof(workingInfo), &workingInfo) < 0) { HP5400_DBG (DBG_MSG, "failed to write panel info\n"); return -1; } return 0; } HP5400_SANE_STATIC int WarmupLamp (int iHandle) { int i = 30; /* Max 30 seconds, 15 is typical for cold start? */ int couldRead; unsigned char dataVerify[0x02]; /* Keep writing 01 to 0000 until no error... */ unsigned char data0000[] = { 0x01 }; unsigned char data0300[3]; hp5400_command_write_noverify (iHandle, 0x0000, data0000, 1); do { hp5400_command_read_noverify (iHandle, 0x0300, 3, data0300); hp5400_command_write_noverify (iHandle, 0x0000, data0000, 1); couldRead = hp5400_command_read_noverify (iHandle, 0xc500, 0x02, dataVerify); if ((dataVerify[0] != 0) || (dataVerify[1] != 0)) sleep (1); } while ((i-- > 0) && (couldRead >= 0) && ((dataVerify[0] != 0) || (dataVerify[1] != 0))); if (i > 0) return 0; /* while( i > 0 ) { if( WriteByte( iHandle, 0x0000, 0x01 ) == 0 ) return 0; sleep(1); i--; } */ HP5400_DBG (DBG_MSG, "***WARNING*** Warmup lamp failed...\n"); return -1; } #define CALPIXBYBLOCK 42 HP5400_SANE_STATIC int SetCalibration (int iHandle, int numPixels, unsigned int *low_vals[3], unsigned int *high_vals[3], int dpi) { char cmd[8]; /* unsigned char cmd[8]; */ /* should fix the compilation warning but I don't have a scanner right now to check that the fix does not break calibration */ int i, j, k; struct CalPixel { char highr[2], highg[2], highb[2]; char lowr[2], lowg[2], lowb[2]; }; struct CalBlock { struct CalPixel pixels[CALPIXBYBLOCK]; char pad[8]; } PACKED; struct CalBlock *calinfo; /** we did scan test at 300 dpi, so we don't have all the needed pixels. To fill the gap, we loop */ int numLoop = max (1, dpi / 300); int calBlockSize = CALPIXBYBLOCK * (6 * sizeof (short)) + 8 * sizeof (char); /* = sizeof(calBlock) */ int numCalBlock = ((numPixels / CALPIXBYBLOCK) + ((numPixels % CALPIXBYBLOCK != 0) ? 1 : 0)); int calSize = numLoop * calBlockSize * numCalBlock; calinfo = malloc (calSize); memset (calinfo, 0, calSize); for (j = 0; j < numLoop * numCalBlock * CALPIXBYBLOCK; j++) { struct CalPixel *pixel = &calinfo[j / CALPIXBYBLOCK].pixels[j % CALPIXBYBLOCK]; /* i = ( j % (int)( 0.80 * numPixels ) ) + (int)(0.10 * numPixels ); */ /* better solution : stretch the actual scan size to the calibration size */ i = j / numLoop; /* This is obviously not quite right. The values on * the right are approximately what windows sends */ 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); cmd[1] = 0xff & (calSize >> 8); cmd[2] = 0xff & (calSize >> 0); cmd[3] = 0x00; cmd[4] = 0x54; cmd[5] = 0x02; cmd[6] = -128; // 0x80; fixes compiler warning (for // signed char implementations), see // also comment above cmd[7] = 0x00; hp5400_bulk_command_write (iHandle, 0xE603, cmd, 8, calSize, calSize, (void *) calinfo); free (calinfo); return 0; } /* Write a gamma table */ HP5400_SANE_STATIC void WriteGammaCalibTable (int iHandle, const int *pabGammaR, const int *pabGammaG, const int *pabGammaB) { char cmd[3]; char *buffer; int i, j; /* Setup dummy gamma correction table */ buffer = malloc (2 * 65536); cmd[0] = 2; cmd[1] = 0; cmd[2] = 0; for (i = 0; i < 3; i++) { const int *ptr = (i == 0) ? pabGammaR : (i == 1) ? pabGammaG : pabGammaB; 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); } free (buffer); return; } #ifdef STANDALONE HP5400_SANE_STATIC void SetDefaultGamma (int iHandle) { int *buffer = malloc (sizeof (int) * 65536); int i; for (i = 0; i < 65336; i++) buffer[i] = i; WriteGammaCalibTable (iHandle, buffer, buffer, buffer); } #endif #define BUFFER_SIZE (6*65536) #ifdef IMAGE_DEBUG FILE *temp; #endif /* Bytes per line is the number of pixels. The actual bytes is one more */ HP5400_SANE_STATIC void CircBufferInit (int iHandle, TDataPipe * p, int iBytesPerLine, int bpp, int iMisAlignment, int blksize, int iTransferSize) { (void) iHandle; /* to avoid compilation warning */ p->buffersize = max (BUFFER_SIZE, 3 * blksize); if (p->buffer) { free (p->buffer); } /* Allocate a large enough buffer for transfer */ p->buffer = malloc (p->buffersize); p->pixels = (iBytesPerLine / 3) / bpp; /* These three must always be positive */ p->roff = 0; p->goff = p->pixels * bpp + 1; p->boff = 2 * p->pixels * bpp + 2;; p->linelength = iBytesPerLine + 3; /* NUL at end of each row */ p->bpp = bpp; p->bufstart = p->bufend = 0; if (iMisAlignment > 0) { p->roff += 0; p->goff += p->linelength * iMisAlignment; p->boff += p->linelength * iMisAlignment * 2; } if (iMisAlignment < 0) { p->roff -= p->linelength * iMisAlignment * 2; p->goff -= p->linelength * iMisAlignment; p->boff -= 0; } p->blksize = blksize; p->transfersize = iTransferSize; #ifdef IMAGE_DEBUG temp = fopen ("imagedebug.dat", "w+b"); #endif HP5400_DBG (DBG_MSG, "Begin: line=%d (%X), pixels=%d (%X), r=%d (%X), g=%d (%X), b=%d (%X), bpp=%d, step=%d\n", p->linelength, p->linelength, p->pixels, p->pixels, p->roff, p->roff, p->goff, p->goff, p->boff, p->boff, bpp, iMisAlignment); } HP5400_SANE_STATIC int CircBufferGetLine (int iHandle, TDataPipe * p, void *pabLine) { int i; int maxoff = 0; char* buftmp = (char*) (p->buffer); /* HP5400_DBG(DBG_MSG, "CircBufferGetLine:\n"); */ if (p->roff > maxoff) maxoff = p->roff; if (p->goff > maxoff) maxoff = p->goff; if (p->boff > maxoff) maxoff = p->boff; maxoff += p->pixels * p->bpp; if (maxoff < p->linelength) maxoff = p->linelength; /* resize buffer if needed */ if (p->bufstart + maxoff >= p->buffersize + p->blksize) { /* store actual buffer pointer */ void *tmpBuf = p->buffer; /* calculate new size for buffer (oversize a bit) */ int newsize = p->bufstart + maxoff + 2 * p->blksize; p->buffer = malloc (newsize); memcpy (p->buffer, tmpBuf, p->buffersize); p->buffersize = newsize; /* free old buffer */ free (tmpBuf); buftmp = (char*)(p->buffer); } while (p->bufstart + maxoff >= p->bufend) /* Not enough data in buffer */ { int res; 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); HP5400_DBG (DBG_MSG, "Reading block, %d bytes remain\n", p->transfersize); p->transfersize -= p->blksize; res = hp5400_bulk_read_block (iHandle, CMD_INITBULK3, cmd, sizeof (cmd), buftmp + p->bufend, p->blksize); if (res != p->blksize) { HP5400_DBG (DBG_ERR, "*** ERROR: Read returned %d. FATAL.\n", res); return -1; } #ifdef IMAGE_DEBUG fwrite ( buftmp + p->bufend ,p->blksize ,1 ,temp ); #endif p->bufend += p->blksize; } assert (p->bufstart + maxoff < p->bufend); /* Copy a line into the result buffer */ if (p->bpp == 1) { char *itPix = (char *) pabLine; char *itR = (char *) (buftmp + p->bufstart + p->roff); char *itG = (char *) (buftmp + p->bufstart + p->goff); char *itB = (char *) (buftmp + p->bufstart + p->boff); for (i = 0; i < p->pixels; i++) { /* pointer move goes a little bit faster than vector access */ /* Although I wouldn't be surprised if the compiler worked that out anyway. * No matter, this is easier to read :) */ *(itPix++) = *(itR++); *(itPix++) = *(itG++); *(itPix++) = *(itB++); } } else { short *itPix = (short *) pabLine; short *itR = (short *) (buftmp + p->bufstart + p->roff); short *itG = (short *) (buftmp + p->bufstart + p->goff); short *itB = (short *) (buftmp + p->bufstart + p->boff); for (i = 0; i < p->pixels; i++) { #if 0 /* This code, while correct for PBM is not correct for the host and * since we need it correct for calibration, it has to go */ ((short *) pabLine)[3 * i + 0] = *((short *) (p->buffer + p->bufstart + p->roff + 2 * i)); ((short *) pabLine)[3 * i + 1] = *((short *) (p->buffer + p->bufstart + p->goff + 2 * i)); ((short *) pabLine)[3 * i + 2] = *((short *) (p->buffer + p->bufstart + p->boff + 2 * i)); #else /* pointer move goes a little bit faster than vector access */ *(itPix++) = htons (*(itR++)); *(itPix++) = htons (*(itG++)); *(itPix++) = htons (*(itB++)); #endif } } p->bufstart += p->linelength; assert (p->bufstart <= p->bufend); /* If we've used a whole block at the beginning, move it */ if (p->bufstart > p->blksize) { memmove ( buftmp ,buftmp + p->bufstart ,p->bufend - p->bufstart ); p->bufend -= p->bufstart; p->bufstart = 0; } return 0; } HP5400_SANE_STATIC void CircBufferExit (TDataPipe * p) { free (p->buffer); p->buffer = NULL; #ifdef IMAGE_DEBUG fclose (temp); temp = NULL; #endif return; } #ifdef STANDALONE /* bpp is BYTES per pixel */ HP5400_SANE_STATIC void DecodeImage (FILE * file, int planes, int bpp, int xsize, int ysize, const char *filename) { char *in, *buf; char *p[3]; FILE *output; int i, j, k; /* xsize is byte width, not pixel width */ xsize /= planes * bpp; HP5400_DBG (DBG_MSG, "DecodeImage(planes=%d,bpp=%d,xsize=%d,ysize=%d) => %d (file=%s)\n", planes, bpp, xsize, ysize, planes * bpp * xsize * ysize, filename); in = malloc (planes * (xsize * bpp + 1)); for (i = 0; i < planes; i++) p[i] = in + i * (xsize * bpp + 1); buf = malloc (3 * xsize * bpp); output = fopen (filename, "wb"); fprintf (output, "P%d\n%d %d\n", (planes == 3) ? 6 : 5, xsize, ysize); fprintf (output, "%d\n", (bpp == 1) ? 255 : 0xb000); for (i = 0; i < ysize; i++) { fread (in, planes * (xsize * bpp + 1), 1, file); for (j = 0; j < xsize; j++) { for (k = 0; k < planes; k++) { if (bpp == 1) { buf[j * planes + k] = p[k][j]; } else if (bpp == 2) { buf[j * planes * 2 + k * 2 + 0] = p[k][2 * j + 0]; buf[j * planes * 2 + k * 2 + 1] = p[k][2 * j + 1]; } } } fwrite (buf, planes * xsize * bpp, 1, output); } fclose (output); free (in); free (buf); } HP5400_SANE_STATIC int hp5400_test_scan_response (struct ScanResponse *resp, struct ScanRequest *req) { (void) req; /* to avoid compilation warning */ HP5400_DBG (DBG_MSG, "Scan response:\n"); HP5400_DBG (DBG_MSG, " transfersize=%d htonl-> %d\n", resp->transfersize, htonl (resp->transfersize)); HP5400_DBG (DBG_MSG, " xsize=%d htonl-> %d\n", resp->xsize, htonl (resp->xsize)); HP5400_DBG (DBG_MSG, " ysize=%d htons-> %d\n", resp->ysize, htons (resp->ysize)); return 1; } #endif /* This is a very specialised scanning function. It does the scan request as * usual but instead of producing an image it returns three arrays of ints. * These are averages of the R,G,B values for each column. * * Note the array argument should point to an array of three NULL. These * will be overwritten with allocated pointers. */ HP5400_SANE_STATIC int DoAverageScan (int iHandle, struct ScanRequest *req, int code, unsigned int **array) { THWParams HWParams; struct ScanResponse res; unsigned short *buffer; int i, j, k, length; memset (&HWParams, 0, sizeof (HWParams)); HWParams.iXferHandle = iHandle; if (InitScan2 (SCAN_TYPE_CALIBRATION, req, &HWParams, &res, 0, code) != 0) return -1; /* No colour offsetting, we want raw */ length = htonl (res.xsize) / 6; HP5400_DBG (DBG_MSG, "Calibration scan: %d pixels wide\n", length); for (j = 0; j < 3; j++) { array[j] = malloc (sizeof (int) * length); memset (array[j], 0, sizeof (int) * length); /* Clear array */ } buffer = malloc (htonl (res.xsize) + 1); /* First we just sum them all */ for (i = 0; i < htons (res.ysize); i++) { CircBufferGetLine (iHandle, &HWParams.pipe, buffer); for (j = 0; j < length; j++) for (k = 0; k < 3; k++) array[k][j] += buffer[3 * j + k]; } free (buffer); FinishScan (&HWParams); /* Now divide by the height to get the average */ for (j = 0; j < length; j++) for (k = 0; k < 3; k++) array[k][j] /= htons (res.ysize); return 0; } #ifdef STANDALONE HP5400_SANE_STATIC int DoScan (int iHandle, struct ScanRequest *req, const char *filename, int code, struct ScanResponse *res) { FILE *file; THWParams HWParams; struct ScanResponse res_temp; void *buffer; /* int bpp, planes; */ int i; (void) code; /*to avoid compilation warning*/ if (res == NULL) res = &res_temp; memset (&HWParams, 0, sizeof (HWParams)); file = fopen (filename, "w+b"); if (!file) { HP5400_DBG (DBG_MSG, "Couldn't open outputfile (%s)\n", strerror (errno)); return -1; } HWParams.iXferHandle = iHandle; if (InitScan2 (SCAN_TYPE_NORMAL, req, &HWParams, res, 1, 0x40) != 0) return -1; fprintf (file, "P%d\n%d %d\n", 6, htonl (res->xsize) / 3, htons (res->ysize)); fprintf (file, "%d\n", 255); buffer = malloc (htonl (res->xsize) + 1); for (i = 0; i < htons (res->ysize); i++) { CircBufferGetLine (iHandle, &HWParams.pipe, buffer); fwrite (buffer, htonl (res->xsize), 1, file); } free (buffer); FinishScan (&HWParams); fclose (file); return 0; } #endif HP5400_SANE_STATIC int Calibrate (int iHandle, int dpi) { struct ScanRequest req; unsigned int *low_array[3]; unsigned int *high_array[3]; #ifdef CALIB_DEBUG char buffer[512]; int i, j; #endif /* The first calibration scan. Finds maximum of each CCD */ memset(&req, 0, sizeof(req)); req.x1 = 0x08; req.dpix = htons (300); /* = 300 dpi */ req.dpiy = htons (300); /* = 300 dpi */ req.offx = htons (0); /* = 0cm */ req.offy = htons (0); /* = 0cm */ req.lenx = htons (2690); /* = 22.78cm */ req.leny = htons (50); /* = 0.42cm */ req.flags1 = htons (0x0000); req.flags2 = htons (0x0010); req.flags3 = htons (0x3020); /* First calibration scan, 48bpp */ req.gamma[0] = htons (100); req.gamma[1] = htons (100); req.gamma[2] = htons (100); if (DoAverageScan (iHandle, &req, 0x40, high_array) != 0) return -1; #ifdef CALIB_DEBUG for (i = 0; i < 3; i++) { int len; buffer[0] = 0; sprintf (buffer, "Average %d: \n", i); len = strlen (buffer); for (j = 0; j < 24; j++) { sprintf (buffer + len, "%04X ", high_array[i][j]); len += 5; } strcat (buffer, " ... \n"); len += 6; for (j = 1000; j < 1024; j++) { sprintf (buffer + len, "%04X ", high_array[i][j]); len += 5; } strcat (buffer, " ... \n"); len += 6; for (j = 2000; j < 2024; j++) { sprintf (buffer + len, "%04X ", high_array[i][j]); len += 5; } strcat (buffer, " ... \n"); len += 6; HP5400_DBG (DBG_MSG, buffer); } #endif /* The second calibration scan. Finds minimum of each CCD */ memset(&req, 0, sizeof(req)); req.x1 = 0x08; req.dpix = htons (300); /* = 300 dpi */ req.dpiy = htons (300); /* = 300 dpi */ req.offx = htons (0); /* = 0cm */ req.offy = htons (0); /* = 0cm */ req.lenx = htons (2690); /* = 22.78cm */ req.leny = htons (16); /* = 0.14cm */ req.flags1 = htons (0x0000); req.flags2 = htons (0x0010); req.flags3 = htons (0x3024); /* Second calibration scan, 48bpp */ req.gamma[0] = htons (100); req.gamma[1] = htons (100); req.gamma[2] = htons (100); if (DoAverageScan (iHandle, &req, 0x00, low_array) != 0) return -1; #ifdef CALIB_DEBUG for (i = 0; i < 3; i++) { int len; buffer[0] = 0; sprintf (buffer, "Average %d: \n", i); len = strlen (buffer); for (j = 0; j < 24; j++) { sprintf (buffer + len, "%04X ", low_array[i][j]); len += 5; } strcat (buffer, " ... \n"); len += 6; for (j = 1000; j < 1024; j++) { sprintf (buffer + len, "%04X ", low_array[i][j]); len += 5; } strcat (buffer, " ... \n"); len += 6; for (j = 2000; j < 2024; j++) { sprintf (buffer + len, "%04X ", low_array[i][j]); len += 5; } strcat (buffer, " ... \n"); len += 6; HP5400_DBG (DBG_MSG, buffer); } #endif SetCalibration (iHandle, 2690, low_array, high_array, dpi); return 0; } #ifdef STANDALONE HP5400_SANE_STATIC int hp5400_scan (int iHandle, TScanParams * params, THWParams * pHWParams, const char *filename) { struct ScanRequest req; struct ScanResponse res; int result; (void) pHWParams; /*to avoid compilation warning*/ HP5400_DBG (DBG_MSG, "\n"); HP5400_DBG (DBG_MSG, "Scanning :\n"); HP5400_DBG (DBG_MSG, " dpi(x) : %d\n", params->iDpi); HP5400_DBG (DBG_MSG, " dpi(y) : %d\n", params->iLpi); HP5400_DBG (DBG_MSG, " x0 : %d\n", params->iLeft); HP5400_DBG (DBG_MSG, " y0 : %d\n", params->iTop); HP5400_DBG (DBG_MSG, " width : %d\n", params->iWidth); HP5400_DBG (DBG_MSG, " height : %d\n", params->iHeight); HP5400_DBG (DBG_MSG, "\n"); memset(&req, 0, sizeof(req)); req.x1 = 0x08; req.dpix = htons (params->iDpi); req.dpiy = htons (params->iLpi); /* These offsets and lengths should all be in the reference DPI which * is set to HW_LPI */ req.offx = htons (params->iLeft); req.offy = htons (params->iTop); req.lenx = htons (params->iWidth); req.leny = htons (params->iHeight); req.flags1 = htons (0x0080); req.flags2 = htons (0x0000); req.flags3 = htons ((params->iDpi < 2400) ? 0x18E8 : 0x18C0); /* Try preview scan */ req.gamma[0] = htons (100); req.gamma[1] = htons (100); req.gamma[2] = htons (100); if (Calibrate (iHandle, params->iDpi) != 0) return -1; SetDefaultGamma (iHandle); result = DoScan (iHandle, &req, filename, 0x40, &res); /* Pass the results back to the parent */ params->iBytesPerLine = htonl (res.xsize); params->iLines = htons (res.ysize); #if 0 imageFile = fopen ("output.dat", "r+b"); if (imageFile) { int planes = 3; int bpp = 1; fseek (imageFile, 0, SEEK_SET); DecodeImage (imageFile, planes, bpp, planes * bpp * params->iWidth, params->iHeight, filename); fclose (imageFile); } #endif return result; } HP5400_SANE_STATIC int PreviewScan (int iHandle) { TScanParams params; THWParams pHWParams; /* Reference LPI is 300dpi, remember */ params.iDpi = 75; params.iLpi = 75; params.iLeft = 0; params.iTop = 0; params.iWidth = 2552; /* = 21.61cm * 300dpi */ params.iHeight = 3510; /* = 29.72cm * 300dpi */ params.iColourOffset = 1; return hp5400_scan (iHandle, ¶ms, &pHWParams, "output.ppm"); } static char UISetup1[] = { /* Offset 40 */ 0x50, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6E, 0x67, 0x14, 0x00, 0x52, 0x65, 0x61, 0x64, 0x79, 0x00, 0x53, 0x63, 0x61, 0x6E, 0x6E, 0x65, 0x72, 0x20, 0x4C, 0x6F, 0x63, 0x6B, 0x65, 0x64, 0x00, 0x45, 0x72, 0x72, 0x6F, 0x72, 0x00, 0x43, 0x61, 0x6E, 0x63, 0x65, 0x6C, 0x69, 0x6E, 0x67, 0x14, 0x00, 0x50, 0x6F, 0x77, 0x65, 0x72, 0x53, 0x61, 0x76, 0x65, 0x20, 0x4F, 0x6E, 0x00, 0x53, }; static char UISetup2[] = { 0x63, 0x61, 0x6E, 0x6E, 0x69, 0x6E, 0x67, 0x14, 0x00, 0x41, 0x44, 0x46, 0x20, 0x70, 0x61, 0x70, 0x65, 0x72, 0x20, 0x6A, 0x61, 0x6D, 0x00, 0x43, 0x6F, 0x70, 0x69, 0x65, 0x73, 0x00, 0x00, }; HP5400_SANE_STATIC int InitScanner (int iHandle) { WarmupLamp (iHandle); if (WriteByte (iHandle, 0xF200, 0x40) < 0) return -1; if (hp5400_command_write (iHandle, 0xF10B, sizeof (UISetup1), UISetup1) < 0) { HP5400_DBG (DBG_MSG, "failed to send UISetup1 (%lu)\n", (u_long) sizeof (UISetup1)); return -1; } if (WriteByte (iHandle, 0xF200, 0x00) < 0) return -1; if (hp5400_command_write (iHandle, 0xF10C, sizeof (UISetup2), UISetup2) < 0) { HP5400_DBG (DBG_MSG, "failed to send UISetup2\n"); return -1; } return 0; } #endif /* Warning! The caller must have configured the gamma tables at this stage */ HP5400_SANE_STATIC int InitScan (enum ScanType scantype, TScanParams * pParams, THWParams * pHWParams) { struct ScanRequest req; struct ScanResponse res; int ret; memset(&req, 0, sizeof(req)); req.x1 = 0x08; req.dpix = htons (pParams->iDpi); /* = 300 dpi */ req.dpiy = htons (pParams->iLpi); /* = 300 dpi */ req.offx = htons (pParams->iLeft); /* = 0cm */ req.offy = htons (pParams->iTop); /* = 0cm */ req.lenx = htons (pParams->iWidth); /* = 22.78cm */ /* Scan a few extra rows so we can colour offset properly. At 300dpi the * difference is 4 lines */ req.leny = htons (pParams->iHeight); /* = 0.42cm */ req.flags1 = htons ((scantype == SCAN_TYPE_CALIBRATION) ? 0x0000 : 0x0080); req.flags2 = htons ((scantype == SCAN_TYPE_CALIBRATION) ? 0x0010 : (scantype == SCAN_TYPE_PREVIEW) ? 0x0000 : /* SCAN_TYPE_NORMAL */ 0x0040); req.flags3 = htons (0x18E8); req.gamma[0] = htons (100); req.gamma[1] = htons (100); req.gamma[2] = htons (100); if (Calibrate (pHWParams->iXferHandle, pParams->iDpi) != 0) return -1; /* SetDefaultGamma( pHWParams->iXferHandle ); ** Must be done by caller */ HP5400_DBG (DBG_MSG, "Calibration complete\n"); ret = InitScan2 (scantype, &req, pHWParams, &res, pParams->iColourOffset, 0x40); HP5400_DBG (DBG_MSG, "InitScan2 returned %d\n", ret); /* Pass the results back to the parent */ pParams->iBytesPerLine = htonl (res.xsize); /* Hide the extra lines we're scanning */ pParams->iLines = htons (res.ysize); return ret; /* 0 is good, -1 is bad */ } /* For want of a better name ... */ HP5400_SANE_STATIC int InitScan2 (enum ScanType scantype, struct ScanRequest *req, THWParams * pHWParams, struct ScanResponse *result, int iColourOffset, int code) { struct ScanResponse res; int iHandle = pHWParams->iXferHandle; memset(&res, 0, sizeof(res)); /* Protect scanner from damage. This stops stpuid errors. It basically * limits you to the scanner glass. Stuff like calibrations which need * more access do it safely by fiddling other parameters. Note you can * still break things by fiddling the ScanOffset, but that is not yet * under user control */ if (scantype != SCAN_TYPE_CALIBRATION) { HP5400_DBG (DBG_MSG, "Off(%d,%d) : Len(%d,%d)\n", htons (req->offx), htons (req->offy), htons (req->lenx), htons (req->leny)); /* Yes, all the htons() is silly but we want this check as late as possible */ if (htons (req->offx) > 0x09F8) req->offx = htons (0x09F8); if (htons (req->offy) > 0x0DB6) req->offy = htons (0x0DB6); /* These tests are meaningless as htons() returns unsigned if( htons( req->offx ) < 0 ) req->offx = 0; if( htons( req->offy ) < 0 ) req->offy = 0; */ if (htons (req->offx) + htons (req->lenx) > 0x09F8) req->lenx = htons (0x09F8 - htons (req->offx)); if (htons (req->offy) + htons (req->leny) > 0x0DB6) req->leny = htons (0x0DB6 - htons (req->offy)); if (htons (req->lenx) <= 1) return -1; if (htons (req->leny) <= 1) return -1; } WarmupLamp (iHandle); { /* Try to set it to make scan succeed, URB 53 *//* 0x1B01 => 0x40 */ /* I think this tries to cancel any existing scan */ char flag = 0x40; if (hp5400_command_write (iHandle, CMD_STOPSCAN, sizeof (flag), &flag) < 0) { HP5400_DBG (DBG_MSG, "failed to cancel scan flag\n"); return -1; } } { /* Try to set it to make scan succeed, URB 55 */ char data[4] = { 0x02, 0x03, 0x03, 0x3C }; if (hp5400_command_write (iHandle, CMD_UNKNOWN3, sizeof (data), data) < 0) { HP5400_DBG (DBG_MSG, "failed to set unknown1\n"); return -1; } } { /* Try to set it to make scan succeed, URB 59 */ char flag = 0x04; if (hp5400_command_write (iHandle, CMD_UNKNOWN2, sizeof (flag), &flag) < 0) { HP5400_DBG (DBG_MSG, "failed to set unknown2\n"); return -1; } } { /* URB 67 *//* Reference DPI = 300 currently */ short dpi = htons (HW_LPI); if (hp5400_command_write (iHandle, CMD_SETDPI, sizeof (dpi), &dpi) < 0) { HP5400_DBG (DBG_MSG, "failed to set dpi\n"); return -1; } } if (scantype != SCAN_TYPE_CALIBRATION) { /* Setup scan offsets - Should only apply to non-calibration scans */ short offsets[2]; offsets [0] = htons (0x0054); offsets [1] = htons (0x0282); if (hp5400_command_write (iHandle, CMD_SETOFFSET, sizeof (offsets), offsets) < 0) { HP5400_DBG (DBG_MSG, "failed to set offsets\n"); return -1; } } HP5400_DBG (DBG_MSG, "Scan request: \n "); { size_t i; for (i = 0; i < sizeof (*req); i++) { HP5400_DBG (DBG_MSG, "%02X ", ((unsigned char *) req)[i]); } HP5400_DBG (DBG_MSG, "\n"); } if (hp5400_command_write (iHandle, (scantype != SCAN_TYPE_CALIBRATION) ? CMD_SCANREQUEST2 : CMD_SCANREQUEST, sizeof (*req), req) < 0) { HP5400_DBG (DBG_MSG, "failed to send scan request\n"); return -1; } { /* Try to set it to make scan succeed, URB 71 */ char flag = code; /* Start scan with light on or off as requested */ if (hp5400_command_write (iHandle, CMD_STARTSCAN, sizeof (flag), &flag) < 0) { HP5400_DBG (DBG_MSG, "failed to set gamma flag\n"); return -1; } } if (hp5400_command_read (iHandle, CMD_SCANRESPONSE, sizeof (res), &res) < 0) { HP5400_DBG (DBG_MSG, "failed to read scan response\n"); return -1; } HP5400_DBG (DBG_MSG, "Scan response: \n "); { size_t i; for (i = 0; i < sizeof (res); i++) { HP5400_DBG (DBG_MSG, "%02X ", ((unsigned char *) &res)[i]); } HP5400_DBG (DBG_MSG, "\n"); } HP5400_DBG (DBG_MSG, "Bytes to transfer: %d\nBitmap resolution: %d x %d\n", htonl (res.transfersize), htonl (res.xsize), htons (res.ysize)); HP5400_DBG (DBG_MSG, "Proceeding to scan\n"); if (htonl (res.transfersize) == 0) { HP5400_DBG (DBG_MSG, "Hmm, size is zero. Obviously a problem. Aborting...\n"); return -1; } { char x1 = 0x14, x2 = 0x24; float pixels = ((float) htons (req->lenx) * (float) htons (req->leny)) * ((float) htons (req->dpix) * (float) htons (req->dpiy)) / ((float) HW_LPI * (float) HW_LPI); int bpp = (int) ((float) htonl (res.transfersize) / pixels + 0.5); int planes = (bpp == 1) ? 1 : 3; bpp /= planes; HP5400_DBG (DBG_MSG, "bpp = %d / ( (%d * %d) * (%d * %d) / (%d * %d) ) = %d\n", htonl (res.transfersize), htons (req->lenx), htons (req->leny), htons (req->dpix), htons (req->dpiy), HW_LPI, HW_LPI, bpp); hp5400_command_write_noverify (iHandle, CMD_INITBULK1, &x1, 1); hp5400_command_write_noverify (iHandle, CMD_INITBULK2, &x2, 1); if (bpp > 2) /* Bug!! */ bpp = 2; CircBufferInit (pHWParams->iXferHandle, &pHWParams->pipe, htonl (res.xsize), bpp, iColourOffset, 0xF000, htonl (res.transfersize) + 3 * htons (res.ysize)); } if (result) /* copy ScanResult to parent if they asked for it */ memcpy (result, &res, sizeof (*result)); return 0; } HP5400_SANE_STATIC void FinishScan (THWParams * pHWParams) { int iHandle = pHWParams->iXferHandle; CircBufferExit (&pHWParams->pipe); { /* Finish scan request */ char flag = 0x40; if (hp5400_command_write (iHandle, CMD_STOPSCAN, sizeof (flag), &flag) < 0) { HP5400_DBG (DBG_MSG, "failed to set gamma flag\n"); return; } } } HP5400_SANE_STATIC int HP5400Open (THWParams * params, const char *filename) { int iHandle = hp5400_open (filename); char szVersion[32]; int i; #ifndef NO_STRING_VERSION_MATCH int versionMatched; #endif if (iHandle < 0) { HP5400_DBG (DBG_MSG, "hp5400_open failed\n"); return -1; } params->iXferHandle = 0; /* read version info */ if (hp5400_command_read (iHandle, CMD_GETVERSION, sizeof (szVersion), szVersion) < 0) { HP5400_DBG (DBG_MSG, "failed to read version string\n"); goto hp5400_close_exit; } HP5400_DBG (DBG_MSG, "version String :\n"); for (i=0; i < 32; i++) { HP5400_DBG (DBG_MSG, "%c\n", szVersion[i]); } HP5400_DBG (DBG_MSG, "\n"); #ifndef NO_STRING_VERSION_MATCH i = 0; versionMatched = 0; while ( !versionMatched && (i < numVersions) ) { if (!strncmp (szVersion + 1, MatchVersions[i] .strVersion, strlen(MatchVersions[i] .strVersion) - 4)) { versionMatched = 1; } i++; } if ( !versionMatched ) { HP5400_DBG (DBG_MSG, "Sorry, unknown scanner version. Attempted match on :\n"); i = 0; while ( i < numVersions ) { HP5400_DBG (DBG_MSG, "* '%s'\n", MatchVersions[i] .strVersion); i++; } HP5400_DBG (DBG_MSG, "Version is '%s'\n", szVersion); goto hp5400_close_exit; } #else HP5400_DBG (DBG_MSG, "Warning, Version match is disabled. Version is '%s'\n", szVersion); #endif /* NO_STRING_VERSION_MATCH */ params->iXferHandle = iHandle; /* Start the lamp warming up */ /* No point checking the return value, we don't care anyway */ WriteByte (iHandle, 0x0000, 0x01); /* Success */ return 0; hp5400_close_exit: hp5400_close (iHandle); return -1; } HP5400_SANE_STATIC void HP5400Close (THWParams * params) { hp5400_close (params->iXferHandle); } HP5400_SANE_STATIC int HP5400Detect (const char *filename, int (*_ReportDevice) (TScannerModel * pModel, const char *pszDeviceName)) { int iHandle = hp5400_open (filename); char szVersion[32]; int ret = 0; #ifndef NO_STRING_VERSION_MATCH int versionMatched = 0; int i = 0; #endif if (iHandle < 0) { HP5400_DBG (DBG_MSG, "hp5400_open failed\n"); return -1; } /* read version info */ if (hp5400_command_read (iHandle, CMD_GETVERSION, sizeof (szVersion), szVersion) < 0) { HP5400_DBG (DBG_MSG, "failed to read version string\n"); ret = -1; goto hp5400_close_exit; } #ifndef NO_STRING_VERSION_MATCH i = 0; versionMatched = 0; while ( !versionMatched && (i < numVersions) ) { if (!memcmp (szVersion + 1, MatchVersions[i] .strVersion, strlen (MatchVersions[i] .strVersion) - 4)) { versionMatched = 1; } i++; } if ( !versionMatched ) { HP5400_DBG (DBG_MSG, "Sorry, unknown scanner version. Attempted match on :\n"); i = 0; while ( i < numVersions ) { HP5400_DBG (DBG_MSG, "* '%s'\n", MatchVersions[i] .strVersion); i++; } HP5400_DBG (DBG_MSG, "Version is '%s'\n", szVersion); goto hp5400_close_exit; } #else HP5400_DBG (DBG_MSG, "Warning, Version match is disabled. Version is '%s'\n", szVersion); #endif /* NO_STRING_VERSION_MATCH */ if (_ReportDevice) _ReportDevice (&Model_HP54xx, filename); hp5400_close_exit: hp5400_close (iHandle); return ret; } #ifdef STANDALONE int main (int argc, char *argv[]) { THWParams scanner; assert (sizeof (struct ScanRequest) == 32); assert (sizeof (struct ScanResponse) == 16); hp5400_dbg_start(); HP5400_DBG (DBG_MSG, "HP5400/5470C sample scan utility, by Martijn van Oosterhout \n"); HP5400_DBG (DBG_MSG, "Based on the testutils by Bertrik Sikken (bertrik@zonnet.nl)\n"); if ((argc == 6) && (!strcmp (argv[1], "-decode"))) { int width = atoi (argv[3]); int height = atoi (argv[4]); FILE *temp = fopen (argv[2], "r+b"); if (temp) { int planes = 3; int bpp = 1; fseek (temp, 0, SEEK_SET); DecodeImage (temp, planes, bpp, planes * bpp * width, height, argv[5]); fclose (temp); } return 1; } if (HP5400Open (&scanner, NULL) < 0) { return 1; } PreviewScan (scanner.iXferHandle); HP5400Close (&scanner); fprintf (stderr, "Note: output is in output.ppm\n"); return 0; } #endif backends-1.3.0/backend/hp5400_internal.h000066400000000000000000000233501456256263500176620ustar00rootroot00000000000000#ifndef _HP5400_INTERNAL_H_ #define _HP5400_INTERNAL_H_ /* sane - Scanner Access Now Easy. Copyright (C) 2020 Ralph Little (C) 2003 Thomas Soumarmon (c) 2003 Martijn van Oosterhout, kleptog@svana.org (c) 2002 Bertrik Sikken, bertrik@zonnet.nl 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, see . 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. HP5400/5470 Test util. Currently is only able to read back the scanner version string, but this basically demonstrates ability to communicate with the scanner. Massively expanded. Can do calibration scan, upload gamma and calibration tables and stores the results of a scan. - 19/02/2003 Martijn */ #include "../include/_stdint.h" #ifdef __GNUC__ #define PACKED __attribute__ ((packed)) #else #define PACKED #endif /* If this is enabled, a copy of the raw data from the scanner will be saved to imagedebug.dat and the attempted conversion to imagedebug.ppm */ /* #define IMAGE_DEBUG */ /* If this is defined you get extra info on the calibration */ /* #define CALIB_DEBUG */ #define CMD_GETVERSION 0x1200 #define CMD_GETUITEXT 0xf00b #define CMD_GETCMDID 0xc500 #define CMD_SCANREQUEST 0x2505 /* This is for previews */ #define CMD_SCANREQUEST2 0x2500 /* This is for real scans */ #define CMD_SCANRESPONSE 0x3400 #define CMD_GETSENSORS 0x2000 #define CMD_READPANEL 0x2100 // Reads info from the scanner. BW/Col + Copy Count. Others, not sure. #define CMD_WRITEPANEL 0x2200 // Ditto for setting. /* Testing stuff to make it work */ #define CMD_SETDPI 0x1500 /* ??? */ #define CMD_STOPSCAN 0x1B01 /* 0x40 = lamp in on, 0x00 = lamp is off */ #define CMD_STARTSCAN 0x1B05 /* 0x40 = lamp in on, 0x00 = lamp is off */ #define CMD_UNKNOWN 0x2300 /* Send fixed string */ #define CMD_UNKNOWN2 0xD600 /* ??? Set to 0x04 */ #define CMD_UNKNOWN3 0xC000 /* ??? Set to 02 03 03 3C */ #define CMD_SETOFFSET 0xE700 /* two ints in network order. X-offset, Y-offset of full scan */ /* Given values seem to be 0x0054 (=4.57mm) and 0x0282 (=54.36mm) */ #define CMD_INITBULK1 0x0087 /* send 0x14 */ #define CMD_INITBULK2 0x0083 /* send 0x24 */ #define CMD_INITBULK3 0x0082 /* transfer length 0xf000 */ struct ScanRequest { uint8_t x1; /* Set to 0x08 */ uint16_t dpix, dpiy; /* Set to 75, 150 or 300 in network order */ uint16_t offx, offy; /* Offset to scan, in 1/300th of dpi, in network order */ uint16_t lenx, leny; /* Size of scan, in 1/300th of dpi, in network order */ uint16_t flags1, flags2, flags3; /* Undetermined flag info */ /* Known combinations are: 1st calibration scan: 0x0000, 0x0010, 0x1820 = 24bpp 2nd calibration scan: 0x0000, 0x0010, 0x3020 = 48bpp ??? 3rd calibration scan: 0x0000, 0x0010, 0x3024 = 48bpp ??? Preview scan: 0x0080, 0x0000, 0x18E8 = 8bpp 4th & 5th like 2nd and 3rd B&W scan: 0x0080, 0x0040, 0x08E8 = 8bpp 6th & 7th like 2nd and 3rd True colour scan 0x0080, 0x0040, 0x18E8 = 24bpp */ uint8_t zero; /* Seems to always be zero */ uint16_t gamma[3]; /* Set to 100 in network order. Gamma? */ uint16_t pad[3]; /* Zero padding to 32 bytes??? */ } PACKED; /* More known combos (All 24-bit): 300 x 300 light calibration: 0x0000, 0x0010, 0x1820 300 x 300 dark calibration: 0x0000, 0x0010, 0x3024 75 x 75 preview scan: 0x0080, 0x0000, 0x18E8 300 x 300 full scan: 0x0080, 0x0000, 0x18E8 600 x 300 light calibration: 0x0000, 0x0010, 0x3000 600 x 300 dark calibration: 0x0000, 0x0010, 0x3004 600 x 600 full scan: 0x0080, 0x0000, 0x18C8 1200 x 300 light calibration: 0x0000, 0x0010, 0x3000 1200 x 300 dark calibration: 0x0000, 0x0010, 0x3004 1200 x 1200 full scan: 0x0080, 0x0000, 0x18C8 2400 x 300 light calibration: 0x0000, 0x0010, 0x3000 2400 x 300 dark calibration: 0x0000, 0x0010, 0x3004 2400 x 2400 full scan: 0x0080, 0x0000, 0x18C0 */ struct ScanResponse { uint16_t x1; /* Usually 0x0000 or 0x4000 */ uint32_t transfersize; /* Number of bytes to be transferred */ uint32_t xsize; /* Shape of returned bitmap */ uint16_t ysize; /* Why does the X get more bytes? */ uint16_t pad[2]; /* Zero padding to 16 bytes??? */ } PACKED; /* * Note: this is the structure of the response we get from CMD_READPANEL. * We only know about two items for the moment. The rest will be ignored * until we understand it better. * * 44 bytes in total. * * Since we don't know what the other things mean, I will assume that they * mean the same things for Get and SET. For SET, we will have to GET, change * what we wish change and SET it back otherwise goodness knows what evil * we will unleash. * * Note that for setting, different values in the buffer seem to apply between the copy count * and the colour/BW switch setting. I don't know what that means at the moment. * * I'm calling it PanelInfo because I can't think of anything better. * That may change as the other values are revealed. * */ struct PanelInfo { uint32_t unknown1[10]; uint8_t unknown2; uint8_t copycount; // 0..99 from the LCD display. uint8_t bwcolour; // 1 or 2 from the Colour/BW leds. uint8_t unknown3; } PACKED; HP5400_SANE_STATIC int InitScan2 (enum ScanType type, struct ScanRequest *req, THWParams * pHWParams, struct ScanResponse *res, int iColourOffset, int code); HP5400_SANE_STATIC void FinishScan (THWParams * pHWParams); HP5400_SANE_STATIC int WriteByte (int iHandle, int cmd, char data); HP5400_SANE_STATIC int SetLamp (THWParams * pHWParams, int fLampOn); HP5400_SANE_STATIC int GetSensors (THWParams * pHWParams, uint16_t *sensorMap); HP5400_SANE_STATIC int GetPanelInfo (THWParams * pHWParams, TPanelInfo *panelInfo); HP5400_SANE_STATIC int SetCopyCount(THWParams * pHWParams, SANE_Word copyCount); HP5400_SANE_STATIC int SetColourBW(THWParams * pHWParams, SANE_Word colourBW); HP5400_SANE_STATIC int WarmupLamp (int iHandle); HP5400_SANE_STATIC int SetCalibration (int iHandle, int numPixels, unsigned int *low_vals[3], unsigned int *high_vals[3], int dpi); HP5400_SANE_STATIC void WriteGammaCalibTable (int iHandle, const int *pabGammaR, const int *pabGammaG, const int *pabGammaB); #ifdef STANDALONE HP5400_SANE_STATIC void SetDefaultGamma (int iHandle); #endif HP5400_SANE_STATIC void CircBufferInit (int iHandle, TDataPipe * p, int iBytesPerLine, int bpp, int iMisAlignment, int blksize, int iTransferSize); HP5400_SANE_STATIC int CircBufferGetLine (int iHandle, TDataPipe * p, void *pabLine); HP5400_SANE_STATIC void CircBufferExit (TDataPipe * p); #ifdef STANDALONE HP5400_SANE_STATIC void DecodeImage (FILE * file, int planes, int bpp, int xsize, int ysize, const char *filename); HP5400_SANE_STATIC int hp5400_test_scan_response (struct ScanResponse *resp, struct ScanRequest *req); #endif HP5400_SANE_STATIC int DoAverageScan (int iHandle, struct ScanRequest *req, int code, unsigned int **array); #ifdef STANDALONE HP5400_SANE_STATIC int DoScan (int iHandle, struct ScanRequest *req, const char *filename, int code, struct ScanResponse *res); #endif HP5400_SANE_STATIC int Calibrate (int iHandle, int dpi); #ifdef STANDALONE HP5400_SANE_STATIC int hp5400_scan (int iHandle, TScanParams * params, THWParams * pHWParams, const char *filename); HP5400_SANE_STATIC int PreviewScan (int iHandle); HP5400_SANE_STATIC int InitScanner (int iHandle); #endif HP5400_SANE_STATIC int InitScan (enum ScanType scantype, TScanParams * pParams, THWParams * pHWParams); HP5400_SANE_STATIC void FinishScan (THWParams * pHWParams); HP5400_SANE_STATIC int HP5400Open (THWParams * params, const char *filename); HP5400_SANE_STATIC void HP5400Close (THWParams * params); HP5400_SANE_STATIC int HP5400Detect (const char *filename, int (*_ReportDevice) (TScannerModel * pModel, const char *pszDeviceName)); HP5400_SANE_STATIC int InitHp5400_internal( void ); HP5400_SANE_STATIC int FreeHp5400_internal( void ); #ifdef STANDALONE int main (int argc, char *argv[]); #endif #endif backends-1.3.0/backend/hp5400_sane.c000066400000000000000000001127071456256263500167740ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2020 Ralph Little Copyright (C) 2003 Martijn van Oosterhout Copyright (C) 2003 Thomas Soumarmon Originally copied from HP3300 testtools. Original notice follows: Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl) 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, see . 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. */ /* SANE interface for hp54xx scanners. Prototype. Parts of this source were inspired by other backends. */ #include "../include/sane/config.h" /* definitions for debug */ #include "hp5400_debug.h" #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_config.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_usb.h" #include /* malloc, free */ #include /* memcpy */ #include #include #define HP5400_CONFIG_FILE "hp5400.conf" #include "hp5400.h" /* other definitions */ #ifndef min #define min(A,B) (((A)<(B)) ? (A) : (B)) #endif #ifndef max #define max(A,B) (((A)>(B)) ? (A) : (B)) #endif #define TRUE 1 #define FALSE 0 #define MM_TO_PIXEL(_mm_, _dpi_) ((_mm_) * (_dpi_) / 25.4) #define PIXEL_TO_MM(_pixel_, _dpi_) ((_pixel_) * 25.4 / (_dpi_)) #define NUM_GAMMA_ENTRIES 65536 /* options enumerator */ typedef enum { optCount = 0, optDPI, optGroupGeometry, optTLX, optTLY, optBRX, optBRY, optGroupEnhancement, optGammaTableRed, /* Gamma Tables */ optGammaTableGreen, optGammaTableBlue, optGroupSensors, optSensorScanTo, optSensorWeb, optSensorReprint, optSensorEmail, optSensorCopy, optSensorMoreOptions, optSensorCancel, optSensorPowerSave, optSensorCopiesUp, optSensorCopiesDown, optSensorColourBW, optSensorColourBWState, optSensorCopyCount, // Unsupported as yet. //optGroupMisc, //optLamp, //optCalibrate, optLast, /* Disable the offset code */ } EOptionIndex; /* * Array mapping (optSensor* - optGroupSensors - 1) to the bit mask of the * corresponding sensor bit that we get from the scanner. * All sensor bits are reported as a complete 16-bit word with individual bits set * to indicate that the sensor has been activated. * They seem to be latched so that they are picked up on next query and a number * of bits can be set in any one query. * */ #define SENSOR_BIT_SCAN 0x0400 #define SENSOR_BIT_WEB 0x0200 #define SENSOR_BIT_REPRINT 0x0002 #define SENSOR_BIT_EMAIL 0x0080 #define SENSOR_BIT_COPY 0x0040 #define SENSOR_BIT_MOREOPTIONS 0x0004 #define SENSOR_BIT_CANCEL 0x0100 #define SENSOR_BIT_POWERSAVE 0x2000 #define SENSOR_BIT_COPIESUP 0x0008 #define SENSOR_BIT_COPIESDOWN 0x0020 #define SENSOR_BIT_COLOURBW 0x0010 uint16_t sensorMaskMap[] = { SENSOR_BIT_SCAN, SENSOR_BIT_WEB, SENSOR_BIT_REPRINT, SENSOR_BIT_EMAIL, SENSOR_BIT_COPY, SENSOR_BIT_MOREOPTIONS, SENSOR_BIT_CANCEL, // Special buttons. // These affect local machine settings, but we can still detect them being pressed. SENSOR_BIT_POWERSAVE, SENSOR_BIT_COPIESUP, SENSOR_BIT_COPIESDOWN, SENSOR_BIT_COLOURBW, // Extra entries to make the array up to the 16 possible bits. 0x0000, // Unused 0x0000, // Unused 0x0000, // Unused 0x0000, // Unused 0x0000 // Unused }; typedef union { SANE_Word w; SANE_Word *wa; /* word array */ SANE_String s; } TOptionValue; typedef struct { SANE_Option_Descriptor aOptions[optLast]; TOptionValue aValues[optLast]; TScanParams ScanParams; THWParams HWParams; TDataPipe DataPipe; int iLinesLeft; SANE_Int *aGammaTableR; /* a 16-to-16 bit color lookup table */ SANE_Int *aGammaTableG; /* a 16-to-16 bit color lookup table */ SANE_Int *aGammaTableB; /* a 16-to-16 bit color lookup table */ int fScanning; /* TRUE if actively scanning */ int fCanceled; uint16_t sensorMap; /* Contains the current unreported sensor bits. */ } TScanner; /* linked list of SANE_Device structures */ typedef struct TDevListEntry { struct TDevListEntry *pNext; SANE_Device dev; char* devname; } TDevListEntry; /* Device filename for USB access */ char usb_devfile[128]; static TDevListEntry *_pFirstSaneDev = 0; static int iNumSaneDev = 0; static const SANE_Device **_pSaneDevList = 0; /* option constraints */ static const SANE_Range rangeGammaTable = {0, 65535, 1}; static const SANE_Range rangeCopyCountTable = {0, 99, 1}; static SANE_String_Const modeSwitchList[] = { SANE_VALUE_SCAN_MODE_COLOR, SANE_VALUE_SCAN_MODE_GRAY, NULL }; #ifdef SUPPORT_2400_DPI static const SANE_Int setResolutions[] = {6, 75, 150, 300, 600, 1200, 2400}; #else static const SANE_Int setResolutions[] = {5, 75, 150, 300, 600, 1200}; #endif static const SANE_Range rangeXmm = {0, 216, 1}; static const SANE_Range rangeYmm = {0, 297, 1}; static void _InitOptions(TScanner *s) { int i, j; SANE_Option_Descriptor *pDesc; TOptionValue *pVal; /* set a neutral gamma */ if( s->aGammaTableR == NULL ) /* Not yet allocated */ { s->aGammaTableR = malloc( NUM_GAMMA_ENTRIES * sizeof( SANE_Int ) ); s->aGammaTableG = malloc( NUM_GAMMA_ENTRIES * sizeof( SANE_Int ) ); s->aGammaTableB = malloc( NUM_GAMMA_ENTRIES * sizeof( SANE_Int ) ); for (j = 0; j < NUM_GAMMA_ENTRIES; j++) { s->aGammaTableR[j] = j; s->aGammaTableG[j] = j; s->aGammaTableB[j] = j; } } for (i = optCount; i < optLast; i++) { pDesc = &s->aOptions[i]; pVal = &s->aValues[i]; /* defaults */ pDesc->name = ""; pDesc->title = ""; pDesc->desc = ""; pDesc->type = SANE_TYPE_INT; pDesc->unit = SANE_UNIT_NONE; pDesc->size = sizeof(SANE_Word); pDesc->constraint_type = SANE_CONSTRAINT_NONE; 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 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[1]; break; //--------------------------------- case optGroupGeometry: pDesc->name = SANE_NAME_GEOMETRY; pDesc->title = SANE_TITLE_GEOMETRY; pDesc->desc = SANE_DESC_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 = 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 = rangeYmm.max; break; //--------------------------------- case optGroupEnhancement: pDesc->name = SANE_NAME_ENHANCEMENT; pDesc->title = SANE_TITLE_ENHANCEMENT; pDesc->desc = SANE_DESC_ENHANCEMENT; pDesc->type = SANE_TYPE_GROUP; pDesc->size = 0; break; case optGammaTableRed: pDesc->name = SANE_NAME_GAMMA_VECTOR_R; pDesc->title = SANE_TITLE_GAMMA_VECTOR_R; pDesc->desc = SANE_DESC_GAMMA_VECTOR_R; pDesc->size = NUM_GAMMA_ENTRIES * sizeof( SANE_Int ); pDesc->constraint_type = SANE_CONSTRAINT_RANGE; pDesc->constraint.range = &rangeGammaTable; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->wa = s->aGammaTableR; break; case optGammaTableGreen: pDesc->name = SANE_NAME_GAMMA_VECTOR_G; pDesc->title = SANE_TITLE_GAMMA_VECTOR_G; pDesc->desc = SANE_DESC_GAMMA_VECTOR_G; pDesc->size = NUM_GAMMA_ENTRIES * sizeof( SANE_Int ); pDesc->constraint_type = SANE_CONSTRAINT_RANGE; pDesc->constraint.range = &rangeGammaTable; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->wa = s->aGammaTableG; break; case optGammaTableBlue: pDesc->name = SANE_NAME_GAMMA_VECTOR_B; pDesc->title = SANE_TITLE_GAMMA_VECTOR_B; pDesc->desc = SANE_DESC_GAMMA_VECTOR_B; pDesc->size = NUM_GAMMA_ENTRIES * sizeof( SANE_Int ); pDesc->constraint_type = SANE_CONSTRAINT_RANGE; pDesc->constraint.range = &rangeGammaTable; pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; pVal->wa = s->aGammaTableB; break; //--------------------------------- case optGroupSensors: pDesc->name = SANE_NAME_SENSORS; pDesc->title = SANE_TITLE_SENSORS; pDesc->type = SANE_TYPE_GROUP; pDesc->desc = SANE_DESC_SENSORS; pDesc->size = 0; break; case optSensorScanTo: pDesc->name = SANE_NAME_SCAN; pDesc->title = SANE_TITLE_SCAN; pDesc->desc = SANE_DESC_SCAN; pDesc->type = SANE_TYPE_BOOL; pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; break; case optSensorWeb: pDesc->name = SANE_I18N("web"); pDesc->title = SANE_I18N("Share-To-Web button"); pDesc->desc = SANE_I18N("Scan an image and send it on the web"); pDesc->type = SANE_TYPE_BOOL; pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; break; case optSensorReprint: pDesc->name = SANE_I18N("reprint"); pDesc->title = SANE_I18N("Reprint Photos button"); pDesc->desc = SANE_I18N("Button for reprinting photos"); pDesc->type = SANE_TYPE_BOOL; pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; break; case optSensorEmail: pDesc->name = SANE_NAME_EMAIL; pDesc->title = SANE_TITLE_EMAIL; pDesc->desc = SANE_DESC_EMAIL; pDesc->type = SANE_TYPE_BOOL; pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; break; case optSensorCopy: pDesc->name = SANE_NAME_COPY; pDesc->title = SANE_TITLE_COPY; pDesc->desc = SANE_DESC_COPY; pDesc->type = SANE_TYPE_BOOL; pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; break; case optSensorMoreOptions: pDesc->name = SANE_I18N("more-options"); pDesc->title = SANE_I18N("More Options button"); pDesc->desc = SANE_I18N("Button for additional options/configuration"); pDesc->type = SANE_TYPE_BOOL; pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; break; case optSensorCancel: pDesc->name = SANE_NAME_CANCEL; pDesc->title = SANE_TITLE_CANCEL; pDesc->desc = SANE_DESC_CANCEL; pDesc->type = SANE_TYPE_BOOL; pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; break; case optSensorPowerSave: pDesc->name = SANE_I18N("power-save"); pDesc->title = SANE_I18N("Power Save button"); pDesc->desc = SANE_I18N("Puts the scanner in an energy-conservation mode"); pDesc->type = SANE_TYPE_BOOL; pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; break; case optSensorCopiesUp: pDesc->name = SANE_I18N("copies-up"); pDesc->title = SANE_I18N("Increase Copies button"); pDesc->desc = SANE_I18N("Increase the number of copies"); pDesc->type = SANE_TYPE_BOOL; pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; break; case optSensorCopiesDown: pDesc->name = SANE_I18N("copies-down"); pDesc->title = SANE_I18N("Decrease Copies button"); pDesc->desc = SANE_I18N("Decrease the number of copies"); pDesc->type = SANE_TYPE_BOOL; pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; break; case optSensorColourBW: pDesc->name = SANE_I18N("color-bw"); pDesc->title = SANE_I18N("Select color/BW button"); pDesc->desc = SANE_I18N("Alternates between color and black/white scanning"); pDesc->type = SANE_TYPE_BOOL; pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; break; case optSensorColourBWState: pDesc->name = SANE_I18N("color-bw-state"); pDesc->title = SANE_I18N("Read color/BW button state"); pDesc->desc = SANE_I18N("Reads state of BW/colour panel setting"); pDesc->type = SANE_TYPE_STRING; pDesc->constraint_type = SANE_CONSTRAINT_STRING_LIST; pDesc->constraint.string_list = modeSwitchList; pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; break; case optSensorCopyCount: pDesc->name = SANE_I18N("copies-count"); pDesc->title = SANE_I18N("Read copy count value"); pDesc->desc = SANE_I18N("Reads state of copy count panel setting"); pDesc->type = SANE_TYPE_INT; pDesc->constraint_type = SANE_CONSTRAINT_RANGE; pDesc->constraint.range = &rangeCopyCountTable; pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; break; #if 0 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; #endif default: HP5400_DBG(DBG_ERR, "Uninitialised option %d\n", i); break; } } } static int _ReportDevice(TScannerModel *pModel, const char *pszDeviceName) { TDevListEntry *pNew, *pDev; HP5400_DBG(DBG_MSG, "hp5400: _ReportDevice '%s'\n", pszDeviceName); pNew = malloc(sizeof(TDevListEntry)); if (!pNew) { HP5400_DBG(DBG_ERR, "no mem\n"); return -1; } /* add new element to the end of the list */ if (_pFirstSaneDev == NULL) { _pFirstSaneDev = pNew; } else { for (pDev = _pFirstSaneDev; pDev->pNext; pDev = pDev->pNext) { ; } pDev->pNext = pNew; } /* fill in new element */ pNew->pNext = 0; /* we use devname to avoid having to free a const * pointer */ pNew->devname = (char*)strdup(pszDeviceName); pNew->dev.name = pNew->devname; pNew->dev.vendor = pModel->pszVendor; pNew->dev.model = pModel->pszName; pNew->dev.type = "flatbed scanner"; iNumSaneDev++; return 0; } static SANE_Status attach_one_device (SANE_String_Const devname) { const char * filename = (const char*) devname; if (HP5400Detect (filename, _ReportDevice) < 0) { HP5400_DBG (DBG_MSG, "attach_one_device: couldn't attach %s\n", devname); return SANE_STATUS_INVAL; } HP5400_DBG (DBG_MSG, "attach_one_device: attached %s successfully\n", devname); return SANE_STATUS_GOOD; } /*****************************************************************************/ SANE_Status sane_init (SANE_Int * piVersion, SANE_Auth_Callback pfnAuth) { FILE *conf_fp; /* Config file stream */ SANE_Char line[PATH_MAX]; SANE_Char *str = NULL; SANE_String_Const proper_str; int nline = 0; /* prevent compiler from complaining about unused parameters */ (void) pfnAuth; strcpy(usb_devfile, "/dev/usb/scanner0"); _pFirstSaneDev = 0; iNumSaneDev = 0; InitHp5400_internal(); DBG_INIT (); HP5400_DBG (DBG_MSG, "sane_init: SANE hp5400 backend version %d.%d-%d (from %s)\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD, PACKAGE_STRING); sanei_usb_init (); conf_fp = sanei_config_open (HP5400_CONFIG_FILE); iNumSaneDev = 0; if (conf_fp) { HP5400_DBG (DBG_MSG, "Reading config file\n"); while (sanei_config_read (line, sizeof (line), conf_fp)) { ++nline; if (str) { free (str); } proper_str = sanei_config_get_string (line, &str); /* Discards white lines and comments */ if (!str || proper_str == line || str[0] == '#') { HP5400_DBG (DBG_MSG, "Discarding line %d\n", nline); } else { /* If line's not blank or a comment, then it's the device * filename or a usb directive. */ HP5400_DBG (DBG_MSG, "Trying to attach %s\n", line); sanei_usb_attach_matching_devices (line, attach_one_device); } } /* while */ fclose (conf_fp); } else { HP5400_DBG (DBG_ERR, "Unable to read config file \"%s\": %s\n", HP5400_CONFIG_FILE, strerror (errno)); HP5400_DBG (DBG_MSG, "Using default built-in values\n"); attach_one_device (usb_devfile); } if (piVersion != NULL) { *piVersion = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); } return SANE_STATUS_GOOD; } void sane_exit (void) { TDevListEntry *pDev, *pNext; HP5400_DBG (DBG_MSG, "sane_exit\n"); /* free device list memory */ if (_pSaneDevList) { for (pDev = _pFirstSaneDev; pDev; pDev = pNext) { pNext = pDev->pNext; free (pDev->devname); /* pDev->dev.name is the same pointer that pDev->devname */ free (pDev); } _pFirstSaneDev = 0; free (_pSaneDevList); _pSaneDevList = 0; } FreeHp5400_internal(); } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { TDevListEntry *pDev; int i; HP5400_DBG (DBG_MSG, "sane_get_devices\n"); (void) local_only; if (_pSaneDevList) { free (_pSaneDevList); } _pSaneDevList = malloc (sizeof (*_pSaneDevList) * (iNumSaneDev + 1)); if (!_pSaneDevList) { HP5400_DBG (DBG_MSG, "no mem\n"); return SANE_STATUS_NO_MEM; } i = 0; for (pDev = _pFirstSaneDev; pDev; pDev = pDev->pNext) { _pSaneDevList[i++] = &pDev->dev; } _pSaneDevList[i++] = 0; /* last entry is 0 */ *device_list = _pSaneDevList; return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const name, SANE_Handle * h) { TScanner *s; HP5400_DBG (DBG_MSG, "sane_open: %s\n", name); /* check the name */ if (strlen (name) == 0) { /* default to first available device */ name = _pFirstSaneDev->dev.name; } s = malloc (sizeof (TScanner)); if (!s) { HP5400_DBG (DBG_MSG, "malloc failed\n"); return SANE_STATUS_NO_MEM; } memset (s, 0, sizeof (TScanner)); /* Clear everything to zero */ if (HP5400Open (&s->HWParams, name) < 0) { /* is this OK ? */ HP5400_DBG (DBG_ERR, "HP5400Open failed\n"); free ((void *) s); return SANE_STATUS_INVAL; /* is this OK? */ } HP5400_DBG (DBG_MSG, "Handle=%d\n", s->HWParams.iXferHandle); _InitOptions (s); *h = s; /* Turn on lamp by default at startup */ /* SetLamp(&s->HWParams, TRUE); */ return SANE_STATUS_GOOD; } void sane_close (SANE_Handle h) { TScanner *s; HP5400_DBG (DBG_MSG, "sane_close\n"); s = (TScanner *) h; /* turn of scanner lamp */ SetLamp (&s->HWParams, FALSE); /* close scanner */ HP5400Close (&s->HWParams); /* free scanner object memory */ free ((void *) s); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle h, SANE_Int n) { TScanner *s; HP5400_DBG (DBG_MSG, "sane_get_option_descriptor %d\n", n); if ((n < optCount) || (n >= optLast)) { return NULL; } s = (TScanner *) h; return &s->aOptions[n]; } SANE_Status sane_control_option (SANE_Handle h, SANE_Int n, SANE_Action Action, void *pVal, SANE_Int * pInfo) { TScanner *s; SANE_Int info; HP5400_DBG (DBG_MSG, "sane_control_option: option %d, action %d\n", n, Action); s = (TScanner *) h; info = 0; switch (Action) { case SANE_ACTION_GET_VALUE: switch (n) { /* Get options of type SANE_Word */ case optBRX: case optTLX: *(SANE_Word *) pVal = s->aValues[n].w; HP5400_DBG (DBG_MSG, "sane_control_option: SANE_ACTION_GET_VALUE %d = %d\n", n, *(SANE_Word *) pVal); break; case optBRY: case optTLY: *(SANE_Word *) pVal = s->aValues[n].w; HP5400_DBG (DBG_MSG, "sane_control_option: SANE_ACTION_GET_VALUE %d = %d\n", n, *(SANE_Word *) pVal); break; case optCount: case optDPI: HP5400_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 optGammaTableRed: case optGammaTableGreen: case optGammaTableBlue: HP5400_DBG (DBG_MSG, "Reading gamma table\n"); memcpy (pVal, s->aValues[n].wa, s->aOptions[n].size); break; case optSensorScanTo: case optSensorWeb: case optSensorReprint: case optSensorEmail: case optSensorCopy: case optSensorMoreOptions: case optSensorCancel: case optSensorPowerSave: case optSensorCopiesUp: case optSensorCopiesDown: case optSensorColourBW: { HP5400_DBG (DBG_MSG, "Reading sensor state\n"); uint16_t sensorMap; if (GetSensors(&s->HWParams, &sensorMap) != 0) { HP5400_DBG (DBG_ERR, "sane_control_option: SANE_ACTION_SET_VALUE could not retrieve sensors\n"); return SANE_STATUS_IO_ERROR; } HP5400_DBG (DBG_MSG, "Sensor state=%x\n", sensorMap); // Add read flags to what we already have so that we can report them when requested. s->sensorMap |= sensorMap; // Look up the mask based on the option number. uint16_t mask = sensorMaskMap[n - optGroupSensors - 1]; *(SANE_Word *) pVal = (s->sensorMap & mask)? 1:0; s->sensorMap &= ~mask; break; } case optSensorCopyCount: { HP5400_DBG (DBG_MSG, "Reading copy count\n"); TPanelInfo panelInfo; if (GetPanelInfo(&s->HWParams, &panelInfo) != 0) { HP5400_DBG (DBG_ERR, "sane_control_option: SANE_ACTION_SET_VALUE could not retrieve panel info\n"); return SANE_STATUS_IO_ERROR; } HP5400_DBG (DBG_MSG, "Copy count setting=%u\n", panelInfo.copycount); *(SANE_Word *) pVal = panelInfo.copycount; break; } case optSensorColourBWState: { HP5400_DBG (DBG_MSG, "Reading BW/Colour setting\n"); TPanelInfo panelInfo; if (GetPanelInfo(&s->HWParams, &panelInfo) != 0) { HP5400_DBG (DBG_ERR, "sane_control_option: SANE_ACTION_SET_VALUE could not retrieve panel info\n"); return SANE_STATUS_IO_ERROR; } HP5400_DBG (DBG_MSG, "BW/Colour setting=%u\n", panelInfo.bwcolour); // Just for safety: if (panelInfo.bwcolour < 1) { panelInfo.bwcolour = 1; } else if (panelInfo.bwcolour > 2) { panelInfo.bwcolour = 2; } (void)strcpy((SANE_String)pVal, modeSwitchList[panelInfo.bwcolour - 1]); break; } #if 0 /* 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; #endif default: HP5400_DBG (DBG_MSG, "SANE_ACTION_GET_VALUE: Invalid option (%d)\n", n); } break; case SANE_ACTION_SET_VALUE: if (s->fScanning) { HP5400_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; break; case optBRX: case optTLX: { // Check against legal values. SANE_Word value = *(SANE_Word *) pVal; if ((value < s->aOptions[n].constraint.range->min) || (value > s->aOptions[n].constraint.range->max)) { HP5400_DBG (DBG_ERR, "sane_control_option: SANE_ACTION_SET_VALUE out of range X value\n"); return SANE_STATUS_INVAL; } info |= SANE_INFO_RELOAD_PARAMS; s->ScanParams.iLines = 0; /* Forget actual image settings */ s->aValues[n].w = value; break; } case optBRY: case optTLY: { // Check against legal values. SANE_Word value = *(SANE_Word *) pVal; if ((value < s->aOptions[n].constraint.range->min) || (value > s->aOptions[n].constraint.range->max)) { HP5400_DBG (DBG_ERR, "sane_control_option: SANE_ACTION_SET_VALUE out of range Y value\n"); return SANE_STATUS_INVAL; } info |= SANE_INFO_RELOAD_PARAMS; s->ScanParams.iLines = 0; /* Forget actual image settings */ s->aValues[n].w = value; break; } case optDPI: { // Check against legal values. SANE_Word dpiValue = *(SANE_Word *) pVal; // First check too large. SANE_Word maxRes = setResolutions[setResolutions[0]]; if (dpiValue > maxRes) { dpiValue = maxRes; } else // Check smaller values: if not exact match, pick next higher available. { for (SANE_Int resIdx = 1; resIdx <= setResolutions[0]; resIdx++) { if (dpiValue <= setResolutions[resIdx]) { dpiValue = setResolutions[resIdx]; break; } } } info |= SANE_INFO_RELOAD_PARAMS; s->ScanParams.iLines = 0; /* Forget actual image settings */ (s->aValues[n].w) = dpiValue; break; } case optGammaTableRed: case optGammaTableGreen: case optGammaTableBlue: HP5400_DBG (DBG_MSG, "Writing gamma table\n"); memcpy (s->aValues[n].wa, pVal, s->aOptions[n].size); break; case optSensorColourBWState: { SANE_String bwColour = (SANE_String)pVal; SANE_Word bwColourValue; if (strcmp(bwColour, SANE_VALUE_SCAN_MODE_COLOR) == 0) { bwColourValue = 1; } else if (strcmp(bwColour, SANE_VALUE_SCAN_MODE_GRAY) == 0) { bwColourValue = 2; } else { HP5400_DBG (DBG_ERR, "sane_control_option: SANE_ACTION_SET_VALUE invalid colour/bw mode\n"); return SANE_STATUS_INVAL; } HP5400_DBG (DBG_MSG, "Setting BW/Colour state=%d\n", bwColourValue); /* * Now write it with the other panel settings back to the scanner. * */ if (SetColourBW(&s->HWParams, bwColourValue) != 0) { HP5400_DBG (DBG_ERR, "sane_control_option: SANE_ACTION_SET_VALUE could not set colour/BW mode\n"); return SANE_STATUS_IO_ERROR; } break; } case optSensorCopyCount: { SANE_Word copyCount = *(SANE_Word *) pVal; if (copyCount < 0) { copyCount = 0; } else if (copyCount > 99) { copyCount = 99; } HP5400_DBG (DBG_MSG, "Setting Copy Count=%d\n", copyCount); /* * Now write it with the other panel settings back to the scanner. * */ if (SetCopyCount(&s->HWParams, copyCount) != 0) { HP5400_DBG (DBG_ERR, "sane_control_option: SANE_ACTION_SET_VALUE could not set copy count\n"); return SANE_STATUS_IO_ERROR; } break; } /* case optLamp: fVal = *(SANE_Bool *)pVal; HP5400_DBG(DBG_MSG, "lamp %s\n", fVal ? "on" : "off"); SetLamp(&s->HWParams, fVal); break; */ #if 0 case optCalibrate: /* SimpleCalib(&s->HWParams); */ break; #endif default: HP5400_DBG (DBG_ERR, "SANE_ACTION_SET_VALUE: Invalid option (%d)\n", n); } if (pInfo != NULL) { *pInfo = info; } break; case SANE_ACTION_SET_AUTO: return SANE_STATUS_UNSUPPORTED; default: HP5400_DBG (DBG_ERR, "Invalid action (%d)\n", Action); return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } SANE_Status sane_get_parameters (SANE_Handle h, SANE_Parameters * p) { TScanner *s; HP5400_DBG (DBG_MSG, "sane_get_parameters\n"); s = (TScanner *) h; /* first do some checks */ if (s->aValues[optTLX].w >= s->aValues[optBRX].w) { HP5400_DBG (DBG_ERR, "TLX should be smaller than BRX\n"); return SANE_STATUS_INVAL; /* proper error code? */ } if (s->aValues[optTLY].w >= s->aValues[optBRY].w) { HP5400_DBG (DBG_ERR, "TLY should be smaller than BRY\n"); return SANE_STATUS_INVAL; /* proper error code? */ } /* return the data */ p->format = SANE_FRAME_RGB; p->last_frame = SANE_TRUE; p->depth = 8; if (s->ScanParams.iLines) /* Initialised by doing a scan */ { p->pixels_per_line = s->ScanParams.iBytesPerLine / 3; p->lines = s->ScanParams.iLines; p->bytes_per_line = s->ScanParams.iBytesPerLine; } else { p->lines = MM_TO_PIXEL (s->aValues[optBRY].w - s->aValues[optTLY].w, s->aValues[optDPI].w); p->pixels_per_line = MM_TO_PIXEL (s->aValues[optBRX].w - s->aValues[optTLX].w, s->aValues[optDPI].w); p->bytes_per_line = p->pixels_per_line * 3; } return SANE_STATUS_GOOD; } #define BUFFER_READ_HEADER_SIZE 32 SANE_Status sane_start (SANE_Handle h) { TScanner *s; SANE_Parameters par; HP5400_DBG (DBG_MSG, "sane_start\n"); s = (TScanner *) h; if (sane_get_parameters (h, &par) != SANE_STATUS_GOOD) { HP5400_DBG (DBG_MSG, "Invalid scan parameters (sane_get_parameters)\n"); return SANE_STATUS_INVAL; } s->iLinesLeft = par.lines; /* fill in the scanparams using the option values */ s->ScanParams.iDpi = s->aValues[optDPI].w; s->ScanParams.iLpi = s->aValues[optDPI].w; /* Guessing here. 75dpi => 1, 2400dpi => 32 */ /* s->ScanParams.iColourOffset = s->aValues[optDPI].w / 75; */ /* now we don't need correction => corrected by scan request type ? */ s->ScanParams.iColourOffset = 0; s->ScanParams.iTop = MM_TO_PIXEL (s->aValues[optTLY].w + s->HWParams.iTopLeftY, HW_LPI); s->ScanParams.iLeft = MM_TO_PIXEL (s->aValues[optTLX].w + s->HWParams.iTopLeftX, HW_DPI); /* Note: All measurements passed to the scanning routines must be in HW_LPI */ s->ScanParams.iWidth = MM_TO_PIXEL (s->aValues[optBRX].w - s->aValues[optTLX].w, HW_LPI); s->ScanParams.iHeight = MM_TO_PIXEL (s->aValues[optBRY].w - s->aValues[optTLY].w, HW_LPI); /* After the scanning, the iLines and iBytesPerLine will be filled in */ /* copy gamma table */ WriteGammaCalibTable (s->HWParams.iXferHandle, s->aGammaTableR, s->aGammaTableG, s->aGammaTableB); /* prepare the actual scan */ /* We say normal here. In future we should have a preview flag to set preview mode */ if (InitScan (SCAN_TYPE_NORMAL, &s->ScanParams, &s->HWParams) != 0) { HP5400_DBG (DBG_MSG, "Invalid scan parameters (InitScan)\n"); return SANE_STATUS_INVAL; } /* for the moment no lines has been read */ s->ScanParams.iLinesRead = 0; s->fScanning = TRUE; s->fCanceled = FALSE; return SANE_STATUS_GOOD; } SANE_Status sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len) { /* Read actual scan from the circular buffer */ /* Note: this is already color corrected, though some work still needs to be done to deal with the colour offsetting */ TScanner *s; char *buffer = (char*)buf; HP5400_DBG (DBG_MSG, "sane_read: request %d bytes \n", maxlen); s = (TScanner *) h; /* nothing has been read for the moment */ *len = 0; if (!s->fScanning || s->fCanceled) { HP5400_DBG (DBG_MSG, "sane_read: we're not scanning.\n"); return SANE_STATUS_EOF; } /* if we read all the lines return EOF */ if (s->ScanParams.iLinesRead == s->ScanParams.iLines) { /* FinishScan( &s->HWParams ); *** FinishScan called in sane_cancel */ HP5400_DBG (DBG_MSG, "sane_read: EOF\n"); return SANE_STATUS_EOF; } /* read as many lines the buffer may contain and while there are lines to be read */ while ((*len + s->ScanParams.iBytesPerLine <= maxlen) && (s->ScanParams.iLinesRead < s->ScanParams.iLines)) { /* get one more line from the circular buffer */ CircBufferGetLine (s->HWParams.iXferHandle, &s->HWParams.pipe, buffer); /* increment pointer, size and line number */ buffer += s->ScanParams.iBytesPerLine; *len += s->ScanParams.iBytesPerLine; s->ScanParams.iLinesRead++; } HP5400_DBG (DBG_MSG, "sane_read: %d bytes read\n", *len); return SANE_STATUS_GOOD; } void sane_cancel (SANE_Handle h) { TScanner *s; HP5400_DBG (DBG_MSG, "sane_cancel\n"); s = (TScanner *) h; /* to be implemented more thoroughly */ /* Make sure the scanner head returns home */ FinishScan (&s->HWParams); s->fCanceled = TRUE; s->fScanning = FALSE; } SANE_Status sane_set_io_mode (SANE_Handle h, SANE_Bool m) { HP5400_DBG (DBG_MSG, "sane_set_io_mode %s\n", m ? "non-blocking" : "blocking"); /* prevent compiler from complaining about unused parameters */ (void) h; if (m) { return SANE_STATUS_UNSUPPORTED; } return SANE_STATUS_GOOD; } SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int * fd) { HP5400_DBG (DBG_MSG, "sane_select_fd\n"); /* prevent compiler from complaining about unused parameters */ (void) h; (void) fd; return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/hp5400_sanei.c000066400000000000000000000233671456256263500171500ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2003 Martijn van Oosterhout Copyright (C) 2003 Thomas Soumarmon Copyright (c) 2003 Henning Meier-Geinitz, Originally copied from HP3300 testtools. Original notice follows: Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl) 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, see . 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. Transport layer for communication with HP5400/5470 scanner. Implementation using sanei_usb Additions to support bulk data transport. Added debugging info - 19/02/2003 Martijn Changed to use sanei_usb instead of direct /dev/usb/scanner access - 15/04/2003 Henning */ #include "hp5400_xfer.h" #include "hp5400_debug.h" #include #include "../include/sane/sanei_usb.h" #define CMD_INITBULK1 0x0087 /* send 0x14 */ #define CMD_INITBULK2 0x0083 /* send 0x24 */ #define CMD_INITBULK3 0x0082 /* transfer length 0xf000 */ static void _UsbWriteControl (int fd, int iValue, int iIndex, void *pabData, int iSize) { int requesttype = USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT; int request = (iSize > 1) ? 0x04 : 0x0C; HP5400_DBG (DBG_MSG, "Write: reqtype = 0x%02X, req = 0x%02X, value = %04X, len = %d\n", requesttype, request, iValue, iSize); if (iSize > 0) { int i; HP5400_DBG (DBG_MSG, " Data: "); for (i = 0; i < iSize && i < 8; i++) HP5400_DBG (DBG_MSG, "%02X ", ((unsigned char *) pabData)[i]); if (iSize > 8) HP5400_DBG (DBG_MSG, "..."); HP5400_DBG (DBG_MSG, "\n"); } if (fd != -1) { sanei_usb_control_msg (fd, requesttype, request, iValue, iIndex, iSize, pabData); } /* No error checking? */ } void hp5400_command_write_noverify (int fd, int iValue, void *pabData, int iSize) { _UsbWriteControl (fd, iValue, 0, pabData, iSize); } static void _UsbReadControl (int fd, int iValue, int iIndex, void *pabData, int iSize) { int requesttype = USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN; int request = (iSize > 1) ? 0x04 : 0x0C; HP5400_DBG (DBG_MSG, "Read: reqtype = 0x%02X, req = 0x%02X, value = %04X\n", requesttype, request, iValue); if (fd != -1) { sanei_usb_control_msg (fd, requesttype, request, iValue, iIndex, iSize, pabData); } } HP5400_SANE_STATIC int hp5400_open (const char *filename) { int fd, iVendor, iProduct; SANE_Status status; if (!filename) filename = "/dev/usb/scanner0"; status = sanei_usb_open (filename, &fd); if (status != SANE_STATUS_GOOD) { HP5400_DBG (DBG_MSG, "hp5400_open: open returned %s\n", sane_strstatus (status)); return -1; } status = sanei_usb_get_vendor_product (fd, &iVendor, &iProduct); if (status != SANE_STATUS_GOOD) { HP5400_DBG (DBG_MSG, "hp5400_open: can't get vendor/product ids: %s\n", sane_strstatus (status)); sanei_usb_close (fd); return -1; } if ((iVendor != 0x03F0) || ((iProduct != 0x1005) && (iProduct != 0x1105))) { HP5400_DBG (DBG_MSG, "hp5400_open: vendor/product 0x%04X-0x%04X is not " "supported\n", iVendor, iProduct); sanei_usb_close (fd); return -1; } HP5400_DBG (DBG_MSG, "vendor/product 0x%04X-0x%04X opened\n", iVendor, iProduct); return fd; } void hp5400_close (int iHandle) { sanei_usb_close (iHandle); } /* returns value > 0 if verify ok */ int hp5400_command_verify (int iHandle, int iCmd) { unsigned char abData[4]; int fd; if (iHandle < 0) { HP5400_DBG (DBG_ERR, "hp5400_command_verify: invalid handle\n"); return -1; } fd = iHandle; /* command 0xc500: read back previous command */ _UsbReadControl (fd, 0xc500, 0, (char *) abData, 2); if (abData[0] != (iCmd >> 8)) { HP5400_DBG (DBG_ERR, "hp5400_command_verify failed, expected 0x%02X%02X, got 0x%02X%02X\n", (int) (iCmd >> 8), (int) (iCmd & 0xff), (int) abData[0], (int) abData[1]); return -1; } if (abData[1] != 0) /* Error code non-zero */ { _UsbReadControl (fd, 0x0300, 0, (char *) abData, 3); HP5400_DBG (DBG_ERR, " error response is: %02X %02X %02X\n", abData[0], abData[1], abData[2]); return -1; } HP5400_DBG (DBG_MSG, "Command %02X verified\n", abData[0]); return 1; } /* returns > 0 if command OK */ int hp5400_command_read_noverify (int iHandle, int iCmd, int iLen, void *pbData) { int fd; if (iHandle < 0) { HP5400_DBG (DBG_ERR, "hp5400_command_read: invalid handle\n"); return -1; } fd = iHandle; _UsbReadControl (fd, iCmd, 0, pbData, iLen); return 1; } /* returns > 0 if command OK */ int hp5400_command_read (int iHandle, int iCmd, int iLen, void *pbData) { hp5400_command_read_noverify (iHandle, iCmd, iLen, pbData); return hp5400_command_verify (iHandle, iCmd); } /* returns >0 if command OK */ int hp5400_command_write (int iHandle, int iCmd, int iLen, void *pbData) { int fd; if (iHandle < 0) { HP5400_DBG (DBG_ERR, "hp5400_command_write: invalid handle\n"); return -1; } fd = iHandle; _UsbWriteControl (fd, iCmd, 0, (char *) pbData, iLen); return hp5400_command_verify (iHandle, iCmd); } #ifdef STANDALONE /* returns >0 if command OK */ int hp5400_bulk_read (int iHandle, size_t len, int block, FILE * file) { SANE_Int fd; char x1 = 0x14, x2 = 0x24; short buf[4] = { 0, 0, 0, 0 }; SANE_Byte *buffer; size_t res = 0; buf[2] = block; if (iHandle < 0) { HP5400_DBG (DBG_ERR, "hp5400_command_read: invalid handle\n"); return -1; } fd = iHandle; buffer = (unsigned char*) malloc (block); _UsbWriteControl (fd, CMD_INITBULK1, 0, &x1, 1); _UsbWriteControl (fd, CMD_INITBULK2, 0, &x2, 1); while (len > 0) { _UsbWriteControl (fd, CMD_INITBULK3, 0, (unsigned char *) &buf, sizeof (buf)); res = block; sanei_usb_read_bulk (fd, buffer, &res); HP5400_DBG (DBG_MSG, "Read bulk returned %lu, %lu remain\n", (u_long) res, (u_long) len); if (res > 0) { fwrite (buffer, (len < res) ? len : res, 1, file); } len -= block; } /* error handling? */ return 0; } #endif /* returns >0 if command OK */ int hp5400_bulk_read_block (int iHandle, int iCmd, void *cmd, int cmdlen, void *buffer, int len) { SANE_Int fd; size_t res = 0; if (iHandle < 0) { HP5400_DBG (DBG_ERR, "hp5400_command_read_block: invalid handle\n"); return -1; } fd = iHandle; _UsbWriteControl (fd, iCmd, 0, cmd, cmdlen); res = len; sanei_usb_read_bulk (fd, (SANE_Byte *) buffer, &res); HP5400_DBG (DBG_MSG, "Read block returned %lu when reading %d\n", (u_long) res, len); return res; } /* returns >0 if command OK */ int hp5400_bulk_command_write (int iHandle, int iCmd, void *cmd, int cmdlen, int datalen, int block, char *data) { SANE_Int fd; size_t res = 0, offset = 0; if (iHandle < 0) { HP5400_DBG (DBG_ERR, "hp5400_bulk_command_write: invalid handle\n"); return -1; } fd = iHandle; HP5400_DBG (DBG_MSG, "bulk_command_write(%04X,<%d bytes>,<%d bytes>)\n", iCmd, cmdlen, datalen); _UsbWriteControl (fd, iCmd, 0, cmd, cmdlen); while (datalen > 0) { { int i; HP5400_DBG (DBG_MSG, " Data: "); for (i = 0; i < datalen && i < block && i < 8; i++) HP5400_DBG (DBG_MSG, "%02X ", ((unsigned char *) data + offset)[i]); if (i >= 8) HP5400_DBG (DBG_MSG, "..."); HP5400_DBG (DBG_MSG, "\n"); } res = (datalen < block) ? datalen : block; sanei_usb_write_bulk (fd, (SANE_Byte *) (data + offset), &res); HP5400_DBG (DBG_MSG, "Write returned %lu, %d remain\n", (u_long) res, datalen); datalen -= block; offset += block; } return hp5400_command_verify (iHandle, iCmd); } #ifdef STANDALONE /** ScannerIsOn retrieve on/off status from scanner @return 1 if is on 0 if is off -1 if is not reachable */ int hp5400_isOn (int iHandle) { unsigned char text2400[3]; hp5400_command_read (iHandle, 0x2400, 0x03, text2400); /* byte 0 indicates if is on or off if 0x02 */ /* byte 1 indicates time since is on */ /* byte 2 indicates time since is power plugged */ if (text2400[0] & 0x02) { return 1; /* is on */ } return 0; /* is off */ } #endif backends-1.3.0/backend/hp5400_sanei.h000066400000000000000000000100211456256263500171340ustar00rootroot00000000000000#ifndef _HP5400_SANEI_H_ #define _HP5400_SANEI_H_ /* sane - Scanner Access Now Easy. Copyright (C) 2003 Martijn van Oosterhout Copyright (C) 2003 Thomas Soumarmon Copyright (c) 2003 Henning Meier-Geinitz, Originally copied from HP3300 testtools. Original notice follows: Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl) 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, see . 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. Transport layer for communication with HP5400/5470 scanner. Implementation using sanei_usb Additions to support bulk data transport. Added debugging info - 19/02/2003 Martijn Changed to use sanei_usb instead of direct /dev/usb/scanner access - 15/04/2003 Henning */ #define CMD_INITBULK1 0x0087 /* send 0x14 */ #define CMD_INITBULK2 0x0083 /* send 0x24 */ #define CMD_INITBULK3 0x0082 /* transfer length 0xf000 */ HP5400_SANE_STATIC void _UsbWriteControl (int fd, int iValue, int iIndex, void *pabData, int iSize); HP5400_SANE_STATIC void hp5400_command_write_noverify (int fd, int iValue, void *pabData, int iSize); HP5400_SANE_STATIC void _UsbReadControl (int fd, int iValue, int iIndex, void *pabData, int iSize); HP5400_SANE_STATIC int hp5400_open (const char *filename); HP5400_SANE_STATIC void hp5400_close (int iHandle); /* returns value > 0 if verify ok */ HP5400_SANE_STATIC int hp5400_command_verify (int iHandle, int iCmd); /* returns > 0 if command OK */ HP5400_SANE_STATIC int hp5400_command_read_noverify (int iHandle, int iCmd, int iLen, void *pbData); /* returns > 0 if command OK */ HP5400_SANE_STATIC int hp5400_command_read (int iHandle, int iCmd, int iLen, void *pbData); /* returns >0 if command OK */ HP5400_SANE_STATIC int hp5400_command_write (int iHandle, int iCmd, int iLen, void *pbData); #ifdef STANDALONE /* returns >0 if command OK */ HP5400_SANE_STATIC int hp5400_bulk_read (int iHandle, size_t len, int block, FILE * file); #endif /* returns >0 if command OK */ HP5400_SANE_STATIC int hp5400_bulk_read_block (int iHandle, int iCmd, void *cmd, int cmdlen, void *buffer, int len); /* returns >0 if command OK */ HP5400_SANE_STATIC int hp5400_bulk_command_write (int iHandle, int iCmd, void *cmd, int cmdlen, int datalen, int block, char *data); /** ScannerIsOn retrieve on/off status from scanner @return 1 if is on 0 if is off -1 if is not reachable */ HP5400_SANE_STATIC int hp5400_isOn (int iHandle); #endif backends-1.3.0/backend/hp5400_xfer.h000066400000000000000000000062631456256263500170160ustar00rootroot00000000000000#ifndef _HP5400_XFER_H_ #define _HP5400_XFER_H_ /* sane - Scanner Access Now Easy. Copyright (C) 2003 Martijn van Oosterhout Copyright (C) 2003 Thomas Soumarmon Originally copied from HP3300 testtools. Original notice follows: Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl) 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, see . 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. Transport layer for communication with HP5400/5470 scanner. Add support for bulk transport of data - 19/02/2003 Martijn */ #include HP5400_SANE_STATIC int hp5400_open (const char *filename); HP5400_SANE_STATIC void hp5400_close (int iHandle); HP5400_SANE_STATIC int hp5400_command_verify (int iHandle, int iCmd); HP5400_SANE_STATIC int hp5400_command_read (int iHandle, int iCmd, int iLen, void *pbData); HP5400_SANE_STATIC int hp5400_command_read_noverify (int iHandle, int iCmd, int iLen, void *pbData); 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 backends-1.3.0/backend/hp5590.c000066400000000000000000002750771456256263500160120ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2007 Ilia Sotnikov HP ScanJet 4570c support by Markham Thomas ADF page detection and high DPI fixes by Bernard Badeer scanbd integration by Damiano Scaramuzza and Bernard Badeer 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, see . 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 is part of a SANE backend for HP ScanJet 4500C/4570C/5500C/5550C/5590/7650 Scanners */ #include "../include/sane/config.h" #include #include #include #ifdef HAVE_SYS_TYPES_H #include #endif #include "../include/sane/sane.h" #define BACKEND_NAME hp5590 #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/saneopts.h" #include "hp5590_cmds.c" #include "hp5590_low.c" #include "../include/sane/sanei_net.h" /* Debug levels */ #define DBG_err 0 #define DBG_proc 10 #define DBG_verbose 20 #define DBG_details 30 #define hp5590_assert(exp) if(!(exp)) { \ DBG (DBG_err, "Assertion '%s' failed at %s:%u\n", #exp, __FILE__, __LINE__);\ return SANE_STATUS_INVAL; \ } #define hp5590_assert_void_return(exp) if(!(exp)) { \ DBG (DBG_err, "Assertion '%s' failed at %s:%u\n", #exp, __FILE__, __LINE__);\ return; \ } #define MY_MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MY_MAX(a, b) (((a) > (b)) ? (a) : (b)) /* #define HAS_WORKING_COLOR_48 */ #define BUILD 8 #define USB_TIMEOUT 30 * 1000 static SANE_Word res_list[] = { 6, 100, 200, 300, 600, 1200, 2400 }; #define SANE_VALUE_SCAN_SOURCE_FLATBED SANE_I18N("Flatbed") #define SANE_VALUE_SCAN_SOURCE_ADF SANE_I18N("ADF") #define SANE_VALUE_SCAN_SOURCE_ADF_DUPLEX SANE_I18N("ADF Duplex") #define SANE_VALUE_SCAN_SOURCE_TMA_SLIDES SANE_I18N("TMA Slides") #define SANE_VALUE_SCAN_SOURCE_TMA_NEGATIVES SANE_I18N("TMA Negatives") static SANE_String_Const sources_list[] = { SANE_VALUE_SCAN_SOURCE_FLATBED, SANE_VALUE_SCAN_SOURCE_ADF, SANE_VALUE_SCAN_SOURCE_ADF_DUPLEX, SANE_VALUE_SCAN_SOURCE_TMA_SLIDES, SANE_VALUE_SCAN_SOURCE_TMA_NEGATIVES, NULL }; #define SANE_VALUE_SCAN_MODE_COLOR_24 SANE_VALUE_SCAN_MODE_COLOR #define SANE_VALUE_SCAN_MODE_COLOR_48 SANE_I18N("Color (48 bits)") #define HAS_WORKING_COLOR_48 1 #define SANE_NAME_LAMP_TIMEOUT "extend-lamp-timeout" #define SANE_TITLE_LAMP_TIMEOUT SANE_I18N("Extend lamp timeout") #define SANE_DESC_LAMP_TIMEOUT SANE_I18N("Extends lamp timeout (from 15 minutes to 1 hour)") #define SANE_NAME_WAIT_FOR_BUTTON "wait-for-button" #define SANE_TITLE_WAIT_FOR_BUTTON SANE_I18N("Wait for button") #define SANE_DESC_WAIT_FOR_BUTTON SANE_I18N("Waits for button before scanning") #define SANE_NAME_BUTTON_PRESSED "button-pressed" #define SANE_TITLE_BUTTON_PRESSED SANE_I18N("Last button pressed") #define SANE_DESC_BUTTON_PRESSED SANE_I18N("Get ID of last button pressed (read only)") #define SANE_NAME_LCD_COUNTER "counter-value" #define SANE_TITLE_LCD_COUNTER SANE_I18N("LCD counter") #define SANE_DESC_LCD_COUNTER SANE_I18N("Get value of LCD counter (read only)") #define SANE_NAME_COLOR_LED "color-led" #define SANE_TITLE_COLOR_LED SANE_I18N("Color LED indicator") #define SANE_DESC_COLOR_LED SANE_I18N("Get value of LED indicator (read only)") #define SANE_NAME_DOC_IN_ADF "doc-in-adf" #define SANE_TITLE_DOC_IN_ADF SANE_I18N("Document available in ADF") #define SANE_DESC_DOC_IN_ADF SANE_I18N("Get state of document-available indicator in ADF (read only)") #define SANE_NAME_OVERWRITE_EOP_PIXEL "hide-eop-pixel" #define SANE_TITLE_OVERWRITE_EOP_PIXEL SANE_I18N("Hide end-of-page pixel") #define SANE_DESC_OVERWRITE_EOP_PIXEL SANE_I18N("Hide end-of-page indicator pixels and overwrite with neighbor pixels") #define SANE_NAME_TRAILING_LINES_MODE "trailing-lines-mode" #define SANE_TITLE_TRAILING_LINES_MODE SANE_I18N("Filling mode of trailing lines after scan data (ADF)") #define SANE_DESC_TRAILING_LINES_MODE SANE_I18N("raw = raw scan data, last = repeat last scan line, raster = b/w raster, "\ "white = white color, black = black color, color = RGB or gray color value") #define SANE_NAME_TRAILING_LINES_COLOR "trailing-lines-color" #define SANE_TITLE_TRAILING_LINES_COLOR SANE_I18N("RGB or gray color value for filling mode 'color'") #define SANE_DESC_TRAILING_LINES_COLOR SANE_I18N("Color value for trailing lines filling mode 'color'. "\ "RGB color as r*65536+256*g+b or gray value (default=violet or gray)") #define BUTTON_PRESSED_VALUE_COUNT 11 #define BUTTON_PRESSED_VALUE_NONE_KEY "none" #define BUTTON_PRESSED_VALUE_POWER_KEY "power" #define BUTTON_PRESSED_VALUE_SCAN_KEY "scan" #define BUTTON_PRESSED_VALUE_COLLECT_KEY "collect" #define BUTTON_PRESSED_VALUE_FILE_KEY "file" #define BUTTON_PRESSED_VALUE_EMAIL_KEY "email" #define BUTTON_PRESSED_VALUE_COPY_KEY "copy" #define BUTTON_PRESSED_VALUE_UP_KEY "up" #define BUTTON_PRESSED_VALUE_DOWN_KEY "down" #define BUTTON_PRESSED_VALUE_MODE_KEY "mode" #define BUTTON_PRESSED_VALUE_CANCEL_KEY "cancel" #define BUTTON_PRESSED_VALUE_MAX_KEY_LEN 32 static SANE_String_Const buttonstate_list[] = { BUTTON_PRESSED_VALUE_NONE_KEY, BUTTON_PRESSED_VALUE_POWER_KEY, BUTTON_PRESSED_VALUE_SCAN_KEY, BUTTON_PRESSED_VALUE_COLLECT_KEY, BUTTON_PRESSED_VALUE_FILE_KEY, BUTTON_PRESSED_VALUE_EMAIL_KEY, BUTTON_PRESSED_VALUE_COPY_KEY, BUTTON_PRESSED_VALUE_UP_KEY, BUTTON_PRESSED_VALUE_DOWN_KEY, BUTTON_PRESSED_VALUE_MODE_KEY, BUTTON_PRESSED_VALUE_CANCEL_KEY, NULL }; #define COLOR_LED_VALUE_COUNT 2 #define COLOR_LED_VALUE_COLOR_KEY "color" #define COLOR_LED_VALUE_BLACKWHITE_KEY "black_white" #define COLOR_LED_VALUE_MAX_KEY_LEN 32 static SANE_String_Const colorledstate_list[] = { COLOR_LED_VALUE_COLOR_KEY, COLOR_LED_VALUE_BLACKWHITE_KEY, NULL }; #define LCD_COUNTER_VALUE_MIN 1 #define LCD_COUNTER_VALUE_MAX 99 #define LCD_COUNTER_VALUE_QUANT 1 static SANE_Range lcd_counter_range = { LCD_COUNTER_VALUE_MIN, LCD_COUNTER_VALUE_MAX, LCD_COUNTER_VALUE_QUANT }; #define TRAILING_LINES_MODE_RAW 0 #define TRAILING_LINES_MODE_LAST 1 #define TRAILING_LINES_MODE_RASTER 2 #define TRAILING_LINES_MODE_WHITE 3 #define TRAILING_LINES_MODE_BLACK 4 #define TRAILING_LINES_MODE_COLOR 5 #define TRAILING_LINES_MODE_VALUE_COUNT 6 #define TRAILING_LINES_MODE_RAW_KEY "raw" #define TRAILING_LINES_MODE_LAST_KEY "last" #define TRAILING_LINES_MODE_RASTER_KEY "raster" #define TRAILING_LINES_MODE_WHITE_KEY "white" #define TRAILING_LINES_MODE_BLACK_KEY "black" #define TRAILING_LINES_MODE_COLOR_KEY "color" #define TRAILING_LINES_MODE_MAX_KEY_LEN 24 static SANE_String_Const trailingmode_list[] = { TRAILING_LINES_MODE_RAW_KEY, TRAILING_LINES_MODE_LAST_KEY, TRAILING_LINES_MODE_RASTER_KEY, TRAILING_LINES_MODE_WHITE_KEY, TRAILING_LINES_MODE_BLACK_KEY, TRAILING_LINES_MODE_COLOR_KEY, NULL }; #define MAX_SCAN_SOURCE_VALUE_LEN 24 #define MAX_SCAN_MODE_VALUE_LEN 24 static SANE_Range range_x, range_y, range_qual; static SANE_String_Const mode_list[] = { SANE_VALUE_SCAN_MODE_COLOR_24, #ifdef HAS_WORKING_COLOR_48 SANE_VALUE_SCAN_MODE_COLOR_48, #endif /* HAS_WORKING_COLOR_48 */ SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_LINEART, NULL }; enum hp5590_opt_idx { HP5590_OPT_NUM = 0, HP5590_OPT_TL_X, HP5590_OPT_TL_Y, HP5590_OPT_BR_X, HP5590_OPT_BR_Y, HP5590_OPT_MODE, HP5590_OPT_SOURCE, HP5590_OPT_RESOLUTION, HP5590_OPT_LAMP_TIMEOUT, HP5590_OPT_WAIT_FOR_BUTTON, HP5590_OPT_BUTTON_PRESSED, HP5590_OPT_COLOR_LED, HP5590_OPT_LCD_COUNTER, HP5590_OPT_DOC_IN_ADF, HP5590_OPT_PREVIEW, HP5590_OPT_OVERWRITE_EOP_PIXEL, HP5590_OPT_TRAILING_LINES_MODE, HP5590_OPT_TRAILING_LINES_COLOR, HP5590_OPT_LAST }; struct hp5590_scanner { struct scanner_info *info; enum proto_flags proto_flags; SANE_Device sane; SANE_Int dn; float br_x, br_y, tl_x, tl_y; unsigned int dpi; enum color_depths depth; enum scan_sources source; SANE_Bool extend_lamp_timeout; SANE_Bool wait_for_button; SANE_Bool preview; unsigned int quality; SANE_Option_Descriptor *opts; struct hp5590_scanner *next; unsigned long long image_size; unsigned long long transferred_image_size; void *bulk_read_state; SANE_Bool scanning; SANE_Bool overwrite_eop_pixel; SANE_Byte *eop_last_line_data; unsigned int eop_last_line_data_rpos; SANE_Int eop_trailing_lines_mode; SANE_Int eop_trailing_lines_color; SANE_Byte *adf_next_page_lines_data; unsigned int adf_next_page_lines_data_size; unsigned int adf_next_page_lines_data_rpos; unsigned int adf_next_page_lines_data_wpos; SANE_Byte *one_line_read_buffer; unsigned int one_line_read_buffer_rpos; SANE_Byte *color_shift_line_buffer1; unsigned int color_shift_buffered_lines1; SANE_Byte *color_shift_line_buffer2; unsigned int color_shift_buffered_lines2; }; static struct hp5590_scanner *scanners_list; /******************************************************************************/ static SANE_Status calc_image_params (struct hp5590_scanner *scanner, unsigned int *pixel_bits, unsigned int *pixels_per_line, unsigned int *bytes_per_line, unsigned int *lines, unsigned long long *image_size) { unsigned int _pixel_bits; SANE_Status ret; unsigned int _pixels_per_line; unsigned int _bytes_per_line; unsigned int _lines; unsigned int _image_size; float var; DBG (DBG_proc, "%s\n", __func__); if (!scanner) return SANE_STATUS_INVAL; ret = hp5590_calc_pixel_bits (scanner->dpi, scanner->depth, &_pixel_bits); if (ret != SANE_STATUS_GOOD) return ret; var = (float) (1.0 * (scanner->br_x - scanner->tl_x) * scanner->dpi); _pixels_per_line = var; if (var > _pixels_per_line) _pixels_per_line++; var = (float) (1.0 * (scanner->br_y - scanner->tl_y) * scanner->dpi); _lines = var; if (var > _lines) _lines++; var = (float) (1.0 * _pixels_per_line / 8 * _pixel_bits); _bytes_per_line = var; if (var > _bytes_per_line) _bytes_per_line++; _image_size = (unsigned long long) _lines * _bytes_per_line; DBG (DBG_verbose, "%s: pixel_bits: %u, pixels_per_line: %u, " "bytes_per_line: %u, lines: %u, image_size: %u\n", __func__, _pixel_bits, _pixels_per_line, _bytes_per_line, _lines, _image_size); if (pixel_bits) *pixel_bits = _pixel_bits; if (pixels_per_line) *pixels_per_line = _pixels_per_line; if (bytes_per_line) *bytes_per_line = _bytes_per_line; if (lines) *lines = _lines; if (image_size) *image_size = _image_size; return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status attach_usb_device (SANE_String_Const devname, enum hp_scanner_types hp_scanner_type) { struct scanner_info *info; struct hp5590_scanner *scanner, *ptr; unsigned int max_count, count; SANE_Int dn; SANE_Status ret; const struct hp5590_model *hp5590_model; DBG (DBG_proc, "%s: Opening USB device\n", __func__); if (sanei_usb_open (devname, &dn) != SANE_STATUS_GOOD) return SANE_STATUS_IO_ERROR; DBG (DBG_proc, "%s: USB device opened\n", __func__); ret = hp5590_model_def (hp_scanner_type, &hp5590_model); if (ret != SANE_STATUS_GOOD) return ret; if (hp5590_init_scanner (dn, hp5590_model->proto_flags, &info, hp_scanner_type) != 0) return SANE_STATUS_IO_ERROR; DBG (1, "%s: found HP%s scanner at '%s'\n", __func__, info->model, devname); DBG (DBG_verbose, "%s: Reading max scan count\n", __func__); if (hp5590_read_max_scan_count (dn, hp5590_model->proto_flags, &max_count) != 0) return SANE_STATUS_IO_ERROR; DBG (DBG_verbose, "%s: Max Scanning count %u\n", __func__, max_count); DBG (DBG_verbose, "%s: Reading scan count\n", __func__); if (hp5590_read_scan_count (dn, hp5590_model->proto_flags, &count) != 0) return SANE_STATUS_IO_ERROR; DBG (DBG_verbose, "%s: Scanning count %u\n", __func__, count); ret = hp5590_read_part_number (dn, hp5590_model->proto_flags); if (ret != SANE_STATUS_GOOD) return ret; ret = hp5590_stop_scan (dn, hp5590_model->proto_flags); if (ret != SANE_STATUS_GOOD) return ret; scanner = malloc (sizeof(struct hp5590_scanner)); if (!scanner) return SANE_STATUS_NO_MEM; memset (scanner, 0, sizeof(struct hp5590_scanner)); scanner->sane.model = info->model; scanner->sane.vendor = "HP"; scanner->sane.type = info->kind; scanner->sane.name = devname; scanner->dn = dn; scanner->proto_flags = hp5590_model->proto_flags; scanner->info = info; scanner->bulk_read_state = NULL; scanner->opts = NULL; scanner->eop_last_line_data = NULL; scanner->eop_last_line_data_rpos = 0; scanner->adf_next_page_lines_data = NULL; scanner->adf_next_page_lines_data_size = 0; scanner->adf_next_page_lines_data_rpos = 0; scanner->adf_next_page_lines_data_wpos = 0; scanner->one_line_read_buffer = NULL; scanner->one_line_read_buffer_rpos = 0; scanner->color_shift_line_buffer1 = NULL; scanner->color_shift_buffered_lines1 = 0; scanner->color_shift_line_buffer2 = NULL; scanner->color_shift_buffered_lines2 = 0; if (!scanners_list) scanners_list = scanner; else { for (ptr = scanners_list; ptr->next; ptr = ptr->next); ptr->next = scanner; } return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status attach_hp4570 (SANE_String_Const devname) { return attach_usb_device (devname, SCANNER_HP4570); } /******************************************************************************/ static SANE_Status attach_hp5550 (SANE_String_Const devname) { return attach_usb_device (devname, SCANNER_HP5550); } /******************************************************************************/ static SANE_Status attach_hp5590 (SANE_String_Const devname) { return attach_usb_device (devname, SCANNER_HP5590); } /******************************************************************************/ static SANE_Status attach_hp7650 (SANE_String_Const devname) { return attach_usb_device (devname, SCANNER_HP7650); } /******************************************************************************/ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize) { SANE_Status ret; SANE_Word vendor_id, product_id; DBG_INIT(); DBG (1, "SANE backed for HP ScanJet 4500C/4570C/5500C/5550C/5590/7650 %u.%u.%u\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); DBG (1, "(c) Ilia Sotnikov \n"); if (version_code) *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); sanei_usb_init(); sanei_usb_set_timeout (USB_TIMEOUT); scanners_list = NULL; ret = hp5590_vendor_product_id (SCANNER_HP4570, &vendor_id, &product_id); if (ret != SANE_STATUS_GOOD) return ret; ret = sanei_usb_find_devices (vendor_id, product_id, attach_hp4570); if (ret != SANE_STATUS_GOOD) return ret; ret = hp5590_vendor_product_id (SCANNER_HP5550, &vendor_id, &product_id); if (ret != SANE_STATUS_GOOD) return ret; ret = sanei_usb_find_devices (vendor_id, product_id, attach_hp5550); if (ret != SANE_STATUS_GOOD) return ret; ret = hp5590_vendor_product_id (SCANNER_HP5590, &vendor_id, &product_id); if (ret != SANE_STATUS_GOOD) return ret; ret = sanei_usb_find_devices (vendor_id, product_id, attach_hp5590); if (ret != SANE_STATUS_GOOD) return ret; ret = hp5590_vendor_product_id (SCANNER_HP7650, &vendor_id, &product_id); if (ret != SANE_STATUS_GOOD) return ret; ret = sanei_usb_find_devices (vendor_id, product_id, attach_hp7650); if (ret != SANE_STATUS_GOOD) return ret; return SANE_STATUS_GOOD; } /******************************************************************************/ void sane_exit (void) { struct hp5590_scanner *ptr, *pnext; DBG (DBG_proc, "%s\n", __func__); for (ptr = scanners_list; ptr; ptr = pnext) { if (ptr->opts != NULL) free (ptr->opts); if (ptr->eop_last_line_data != NULL) { free (ptr->eop_last_line_data); ptr->eop_last_line_data = NULL; ptr->eop_last_line_data_rpos = 0; } if (ptr->adf_next_page_lines_data != NULL) { free (ptr->adf_next_page_lines_data); ptr->adf_next_page_lines_data = NULL; ptr->adf_next_page_lines_data_size = 0; ptr->adf_next_page_lines_data_wpos = 0; ptr->adf_next_page_lines_data_rpos = 0; } if (ptr->one_line_read_buffer != NULL) { free (ptr->one_line_read_buffer); ptr->one_line_read_buffer = NULL; ptr->one_line_read_buffer_rpos = 0; } if (ptr->color_shift_line_buffer1 != NULL) { free (ptr->color_shift_line_buffer1); ptr->color_shift_line_buffer1 = NULL; ptr->color_shift_buffered_lines1 = 0; } if (ptr->color_shift_line_buffer2 != NULL) { free (ptr->color_shift_line_buffer2); ptr->color_shift_line_buffer2 = NULL; ptr->color_shift_buffered_lines2 = 0; } pnext = ptr->next; free (ptr); } } /******************************************************************************/ SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { struct hp5590_scanner *ptr; unsigned int found, i; DBG (DBG_proc, "%s, local only: %u\n", __func__, local_only); if (!device_list) return SANE_STATUS_INVAL; for (found = 0, ptr = scanners_list; ptr; found++, ptr = ptr->next); DBG (1, "Found %u devices\n", found); found++; *device_list = malloc (found * sizeof (SANE_Device)); if (!*device_list) return SANE_STATUS_NO_MEM; memset (*device_list, 0, found * sizeof(SANE_Device)); for (i = 0, ptr = scanners_list; ptr; i++, ptr = ptr->next) { (*device_list)[i] = &(ptr->sane); } return SANE_STATUS_GOOD; } /******************************************************************************/ SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { struct hp5590_scanner *ptr; SANE_Option_Descriptor *opts; DBG (DBG_proc, "%s: device name: %s\n", __func__, devicename); if (!handle) return SANE_STATUS_INVAL; /* Allow to open the first available device by specifying zero-length name */ if (!devicename || !devicename[0]) { ptr = scanners_list; } else { for (ptr = scanners_list; ptr && strcmp (ptr->sane.name, devicename) != 0; ptr = ptr->next); } if (!ptr) return SANE_STATUS_INVAL; /* DS: Without this after the first scan (and sane_close) * it was impossible to use again the read_buttons usb routine. * Function sane_close puts dn = -1. Now sane_open needs to open * the usb communication again. */ if (ptr->dn < 0) { DBG (DBG_proc, "%s: Reopening USB device\n", __func__); if (sanei_usb_open (ptr->sane.name, &ptr->dn) != SANE_STATUS_GOOD) return SANE_STATUS_IO_ERROR; DBG (DBG_proc, "%s: USB device reopened\n", __func__); } ptr->tl_x = 0; ptr->tl_y = 0; ptr->br_x = ptr->info->max_size_x; ptr->br_y = ptr->info->max_size_y; ptr->dpi = res_list[1]; ptr->depth = DEPTH_BW; ptr->source = SOURCE_FLATBED; ptr->extend_lamp_timeout = SANE_FALSE; ptr->wait_for_button = SANE_FALSE; ptr->preview = SANE_FALSE; ptr->quality = 4; ptr->image_size = 0; ptr->scanning = SANE_FALSE; ptr->overwrite_eop_pixel = SANE_TRUE; ptr->eop_trailing_lines_mode = TRAILING_LINES_MODE_LAST; ptr->eop_trailing_lines_color = 0x7f007f; *handle = ptr; opts = malloc (sizeof (SANE_Option_Descriptor) * HP5590_OPT_LAST); if (!opts) return SANE_STATUS_NO_MEM; opts[HP5590_OPT_NUM].name = SANE_NAME_NUM_OPTIONS; opts[HP5590_OPT_NUM].title = SANE_TITLE_NUM_OPTIONS; opts[HP5590_OPT_NUM].desc = SANE_DESC_NUM_OPTIONS; opts[HP5590_OPT_NUM].type = SANE_TYPE_INT; opts[HP5590_OPT_NUM].unit = SANE_UNIT_NONE; opts[HP5590_OPT_NUM].size = sizeof(SANE_Word); opts[HP5590_OPT_NUM].cap = SANE_CAP_INACTIVE | SANE_CAP_SOFT_DETECT; opts[HP5590_OPT_NUM].constraint_type = SANE_CONSTRAINT_NONE; opts[HP5590_OPT_NUM].constraint.string_list = NULL; range_x.min = SANE_FIX(0); range_x.max = SANE_FIX(ptr->info->max_size_x * 25.4); range_x.quant = SANE_FIX(0.1); range_y.min = SANE_FIX(0); range_y.max = SANE_FIX(ptr->info->max_size_y * 25.4); range_y.quant = SANE_FIX(0.1); range_qual.min = SANE_FIX(4); range_qual.max = SANE_FIX(16); range_qual.quant = SANE_FIX(1); opts[HP5590_OPT_TL_X].name = SANE_NAME_SCAN_TL_X; opts[HP5590_OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; opts[HP5590_OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; opts[HP5590_OPT_TL_X].type = SANE_TYPE_FIXED; opts[HP5590_OPT_TL_X].unit = SANE_UNIT_MM; opts[HP5590_OPT_TL_X].size = sizeof(SANE_Fixed); opts[HP5590_OPT_TL_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; opts[HP5590_OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; opts[HP5590_OPT_TL_X].constraint.range = &range_x; opts[HP5590_OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; opts[HP5590_OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; opts[HP5590_OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; opts[HP5590_OPT_TL_Y].type = SANE_TYPE_FIXED; opts[HP5590_OPT_TL_Y].unit = SANE_UNIT_MM; opts[HP5590_OPT_TL_Y].size = sizeof(SANE_Fixed); opts[HP5590_OPT_TL_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; opts[HP5590_OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; opts[HP5590_OPT_TL_Y].constraint.range = &range_y; opts[HP5590_OPT_BR_X].name = SANE_NAME_SCAN_BR_X; opts[HP5590_OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; opts[HP5590_OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; opts[HP5590_OPT_BR_X].type = SANE_TYPE_FIXED; opts[HP5590_OPT_BR_X].unit = SANE_UNIT_MM; opts[HP5590_OPT_BR_X].size = sizeof(SANE_Fixed); opts[HP5590_OPT_BR_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; opts[HP5590_OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; opts[HP5590_OPT_BR_X].constraint.range = &range_x; opts[HP5590_OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; opts[HP5590_OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; opts[HP5590_OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; opts[HP5590_OPT_BR_Y].type = SANE_TYPE_FIXED; opts[HP5590_OPT_BR_Y].unit = SANE_UNIT_MM; opts[HP5590_OPT_BR_Y].size = sizeof(SANE_Fixed); opts[HP5590_OPT_BR_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; opts[HP5590_OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; opts[HP5590_OPT_BR_Y].constraint.range = &range_y; opts[HP5590_OPT_MODE].name = SANE_NAME_SCAN_MODE; opts[HP5590_OPT_MODE].title = SANE_TITLE_SCAN_MODE; opts[HP5590_OPT_MODE].desc = SANE_DESC_SCAN_MODE; opts[HP5590_OPT_MODE].type = SANE_TYPE_STRING; opts[HP5590_OPT_MODE].unit = SANE_UNIT_NONE; opts[HP5590_OPT_MODE].size = MAX_SCAN_MODE_VALUE_LEN; opts[HP5590_OPT_MODE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; opts[HP5590_OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; opts[HP5590_OPT_MODE].constraint.string_list = mode_list; /* Show all features, check on feature in command line evaluation. */ opts[HP5590_OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE; opts[HP5590_OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE; opts[HP5590_OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE; opts[HP5590_OPT_SOURCE].type = SANE_TYPE_STRING; opts[HP5590_OPT_SOURCE].unit = SANE_UNIT_NONE; opts[HP5590_OPT_SOURCE].size = MAX_SCAN_SOURCE_VALUE_LEN; opts[HP5590_OPT_SOURCE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; opts[HP5590_OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; opts[HP5590_OPT_SOURCE].constraint.string_list = sources_list; opts[HP5590_OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; opts[HP5590_OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; opts[HP5590_OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; opts[HP5590_OPT_RESOLUTION].type = SANE_TYPE_INT; opts[HP5590_OPT_RESOLUTION].unit = SANE_UNIT_DPI; opts[HP5590_OPT_RESOLUTION].size = sizeof(SANE_Int); opts[HP5590_OPT_RESOLUTION].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; opts[HP5590_OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; opts[HP5590_OPT_RESOLUTION].constraint.word_list = res_list; opts[HP5590_OPT_LAMP_TIMEOUT].name = SANE_NAME_LAMP_TIMEOUT; opts[HP5590_OPT_LAMP_TIMEOUT].title = SANE_TITLE_LAMP_TIMEOUT; opts[HP5590_OPT_LAMP_TIMEOUT].desc = SANE_DESC_LAMP_TIMEOUT; opts[HP5590_OPT_LAMP_TIMEOUT].type = SANE_TYPE_BOOL; opts[HP5590_OPT_LAMP_TIMEOUT].unit = SANE_UNIT_NONE; opts[HP5590_OPT_LAMP_TIMEOUT].size = sizeof(SANE_Bool); opts[HP5590_OPT_LAMP_TIMEOUT].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; opts[HP5590_OPT_LAMP_TIMEOUT].constraint_type = SANE_CONSTRAINT_NONE; opts[HP5590_OPT_LAMP_TIMEOUT].constraint.string_list = NULL; opts[HP5590_OPT_WAIT_FOR_BUTTON].name = SANE_NAME_WAIT_FOR_BUTTON; opts[HP5590_OPT_WAIT_FOR_BUTTON].title = SANE_TITLE_WAIT_FOR_BUTTON; opts[HP5590_OPT_WAIT_FOR_BUTTON].desc = SANE_DESC_WAIT_FOR_BUTTON; opts[HP5590_OPT_WAIT_FOR_BUTTON].type = SANE_TYPE_BOOL; opts[HP5590_OPT_WAIT_FOR_BUTTON].unit = SANE_UNIT_NONE; opts[HP5590_OPT_WAIT_FOR_BUTTON].size = sizeof(SANE_Bool); opts[HP5590_OPT_WAIT_FOR_BUTTON].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; opts[HP5590_OPT_WAIT_FOR_BUTTON].constraint_type = SANE_CONSTRAINT_NONE; opts[HP5590_OPT_WAIT_FOR_BUTTON].constraint.string_list = NULL; opts[HP5590_OPT_BUTTON_PRESSED].name = SANE_NAME_BUTTON_PRESSED; opts[HP5590_OPT_BUTTON_PRESSED].title = SANE_TITLE_BUTTON_PRESSED; opts[HP5590_OPT_BUTTON_PRESSED].desc = SANE_DESC_BUTTON_PRESSED; opts[HP5590_OPT_BUTTON_PRESSED].type = SANE_TYPE_STRING; opts[HP5590_OPT_BUTTON_PRESSED].unit = SANE_UNIT_NONE; opts[HP5590_OPT_BUTTON_PRESSED].size = BUTTON_PRESSED_VALUE_MAX_KEY_LEN; opts[HP5590_OPT_BUTTON_PRESSED].cap = SANE_CAP_HARD_SELECT | SANE_CAP_SOFT_DETECT; opts[HP5590_OPT_BUTTON_PRESSED].constraint_type = SANE_CONSTRAINT_STRING_LIST; opts[HP5590_OPT_BUTTON_PRESSED].constraint.string_list = buttonstate_list; opts[HP5590_OPT_COLOR_LED].name = SANE_NAME_COLOR_LED; opts[HP5590_OPT_COLOR_LED].title = SANE_TITLE_COLOR_LED; opts[HP5590_OPT_COLOR_LED].desc = SANE_DESC_COLOR_LED; opts[HP5590_OPT_COLOR_LED].type = SANE_TYPE_STRING; opts[HP5590_OPT_COLOR_LED].unit = SANE_UNIT_NONE; opts[HP5590_OPT_COLOR_LED].size = COLOR_LED_VALUE_MAX_KEY_LEN; opts[HP5590_OPT_COLOR_LED].cap = SANE_CAP_HARD_SELECT | SANE_CAP_SOFT_DETECT; opts[HP5590_OPT_COLOR_LED].constraint_type = SANE_CONSTRAINT_STRING_LIST; opts[HP5590_OPT_COLOR_LED].constraint.string_list = colorledstate_list; opts[HP5590_OPT_LCD_COUNTER].name = SANE_NAME_LCD_COUNTER; opts[HP5590_OPT_LCD_COUNTER].title = SANE_TITLE_LCD_COUNTER; opts[HP5590_OPT_LCD_COUNTER].desc = SANE_DESC_LCD_COUNTER; opts[HP5590_OPT_LCD_COUNTER].type = SANE_TYPE_INT; opts[HP5590_OPT_LCD_COUNTER].unit = SANE_UNIT_NONE; opts[HP5590_OPT_LCD_COUNTER].size = sizeof(SANE_Int); opts[HP5590_OPT_LCD_COUNTER].cap = SANE_CAP_HARD_SELECT | SANE_CAP_SOFT_DETECT; opts[HP5590_OPT_LCD_COUNTER].constraint_type = SANE_CONSTRAINT_RANGE; opts[HP5590_OPT_LCD_COUNTER].constraint.range = &lcd_counter_range; opts[HP5590_OPT_DOC_IN_ADF].name = SANE_NAME_DOC_IN_ADF; opts[HP5590_OPT_DOC_IN_ADF].title = SANE_TITLE_DOC_IN_ADF; opts[HP5590_OPT_DOC_IN_ADF].desc = SANE_DESC_DOC_IN_ADF; opts[HP5590_OPT_DOC_IN_ADF].type = SANE_TYPE_BOOL; opts[HP5590_OPT_DOC_IN_ADF].unit = SANE_UNIT_NONE; opts[HP5590_OPT_DOC_IN_ADF].size = sizeof(SANE_Bool); opts[HP5590_OPT_DOC_IN_ADF].cap = SANE_CAP_HARD_SELECT | SANE_CAP_SOFT_DETECT; opts[HP5590_OPT_DOC_IN_ADF].constraint_type = SANE_CONSTRAINT_NONE; opts[HP5590_OPT_DOC_IN_ADF].constraint.range = NULL; opts[HP5590_OPT_PREVIEW].name = SANE_NAME_PREVIEW; opts[HP5590_OPT_PREVIEW].title = SANE_TITLE_PREVIEW; opts[HP5590_OPT_PREVIEW].desc = SANE_DESC_PREVIEW; opts[HP5590_OPT_PREVIEW].type = SANE_TYPE_BOOL; opts[HP5590_OPT_PREVIEW].unit = SANE_UNIT_NONE; opts[HP5590_OPT_PREVIEW].size = sizeof(SANE_Bool); opts[HP5590_OPT_PREVIEW].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; opts[HP5590_OPT_PREVIEW].constraint_type = SANE_CONSTRAINT_NONE; opts[HP5590_OPT_PREVIEW].constraint.string_list = NULL; opts[HP5590_OPT_OVERWRITE_EOP_PIXEL].name = SANE_NAME_OVERWRITE_EOP_PIXEL; opts[HP5590_OPT_OVERWRITE_EOP_PIXEL].title = SANE_TITLE_OVERWRITE_EOP_PIXEL; opts[HP5590_OPT_OVERWRITE_EOP_PIXEL].desc = SANE_DESC_OVERWRITE_EOP_PIXEL; opts[HP5590_OPT_OVERWRITE_EOP_PIXEL].type = SANE_TYPE_BOOL; opts[HP5590_OPT_OVERWRITE_EOP_PIXEL].unit = SANE_UNIT_NONE; opts[HP5590_OPT_OVERWRITE_EOP_PIXEL].size = sizeof(SANE_Bool); opts[HP5590_OPT_OVERWRITE_EOP_PIXEL].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; opts[HP5590_OPT_OVERWRITE_EOP_PIXEL].constraint_type = SANE_CONSTRAINT_NONE; opts[HP5590_OPT_OVERWRITE_EOP_PIXEL].constraint.string_list = NULL; opts[HP5590_OPT_TRAILING_LINES_MODE].name = SANE_NAME_TRAILING_LINES_MODE; opts[HP5590_OPT_TRAILING_LINES_MODE].title = SANE_TITLE_TRAILING_LINES_MODE; opts[HP5590_OPT_TRAILING_LINES_MODE].desc = SANE_DESC_TRAILING_LINES_MODE; opts[HP5590_OPT_TRAILING_LINES_MODE].type = SANE_TYPE_STRING; opts[HP5590_OPT_TRAILING_LINES_MODE].unit = SANE_UNIT_NONE; opts[HP5590_OPT_TRAILING_LINES_MODE].size = TRAILING_LINES_MODE_MAX_KEY_LEN; opts[HP5590_OPT_TRAILING_LINES_MODE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; opts[HP5590_OPT_TRAILING_LINES_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; opts[HP5590_OPT_TRAILING_LINES_MODE].constraint.string_list = trailingmode_list; opts[HP5590_OPT_TRAILING_LINES_COLOR].name = SANE_NAME_TRAILING_LINES_COLOR; opts[HP5590_OPT_TRAILING_LINES_COLOR].title = SANE_TITLE_TRAILING_LINES_COLOR; opts[HP5590_OPT_TRAILING_LINES_COLOR].desc = SANE_DESC_TRAILING_LINES_COLOR; opts[HP5590_OPT_TRAILING_LINES_COLOR].type = SANE_TYPE_INT; opts[HP5590_OPT_TRAILING_LINES_COLOR].unit = SANE_UNIT_NONE; opts[HP5590_OPT_TRAILING_LINES_COLOR].size = sizeof(SANE_Int); opts[HP5590_OPT_TRAILING_LINES_COLOR].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; opts[HP5590_OPT_TRAILING_LINES_COLOR].constraint_type = SANE_CONSTRAINT_NONE; opts[HP5590_OPT_TRAILING_LINES_COLOR].constraint.string_list = NULL; ptr->opts = opts; return SANE_STATUS_GOOD; } /******************************************************************************/ void sane_close (SANE_Handle handle) { struct hp5590_scanner *scanner = handle; DBG (DBG_proc, "%s\n", __func__); sanei_usb_close (scanner->dn); scanner->dn = -1; } /******************************************************************************/ const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { struct hp5590_scanner *scanner = handle; DBG (DBG_proc, "%s, option: %u\n", __func__, option); if (option >= HP5590_OPT_LAST) return NULL; return &scanner->opts[option]; } /*************************************DS:Support function read buttons status */ SANE_Status read_button_pressed(SANE_Handle handle, enum button_status * button_pressed) { struct hp5590_scanner * scanner = handle; *button_pressed = BUTTON_NONE; enum button_status status = BUTTON_NONE; DBG (DBG_verbose, "%s: Checking button status (device_number = %u) (device_name = %s)\n", __func__, scanner->dn, scanner->sane.name); SANE_Status ret = hp5590_read_buttons (scanner->dn, scanner->proto_flags, &status); if (ret != SANE_STATUS_GOOD) { DBG (DBG_proc, "%s: Error reading button status (%u)\n", __func__, ret); return ret; } DBG (DBG_verbose, "%s: Button pressed = %d\n", __func__, status); *button_pressed = status; return SANE_STATUS_GOOD; } /******************************************************************************/ SANE_Status read_lcd_and_led_values(SANE_Handle handle, SANE_Int * lcd_counter, enum color_led_status * color_led) { struct hp5590_scanner * scanner = handle; *lcd_counter = 1; *color_led = LED_COLOR; DBG (DBG_verbose, "%s: Reading LCD and LED values (device_number = %u) (device_name = %s)\n", __func__, scanner->dn, scanner->sane.name); SANE_Status ret = hp5590_read_lcd_and_led (scanner->dn, scanner->proto_flags, lcd_counter, color_led); if (ret != SANE_STATUS_GOOD) { DBG (DBG_proc, "%s: Error reading LCD and LED values (%u)\n", __func__, ret); return ret; } DBG (DBG_verbose, "%s: LCD = %d, LED = %s\n", __func__, *lcd_counter, *color_led == LED_BLACKWHITE ? COLOR_LED_VALUE_BLACKWHITE_KEY : COLOR_LED_VALUE_COLOR_KEY); return SANE_STATUS_GOOD; } /******************************************************************************/ SANE_Status read_doc_in_adf_value(SANE_Handle handle, SANE_Bool * doc_in_adf) { struct hp5590_scanner * scanner = handle; DBG (DBG_verbose, "%s: Reading state of document-available in ADF (device_number = %u) (device_name = %s)\n", __func__, scanner->dn, scanner->sane.name); SANE_Status ret = hp5590_is_data_available (scanner->dn, scanner->proto_flags); if (ret == SANE_STATUS_GOOD) *doc_in_adf = SANE_TRUE; else if (ret == SANE_STATUS_NO_DOCS) *doc_in_adf = SANE_FALSE; else { DBG (DBG_proc, "%s: Error reading state of document-available in ADF (%u)\n", __func__, ret); return ret; } DBG (DBG_verbose, "%s: doc_in_adf = %s\n", __func__, *doc_in_adf == SANE_FALSE ? "false" : "true"); return SANE_STATUS_GOOD; } /******************************************************************************/ SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int * info) { struct hp5590_scanner *scanner = handle; if (!value) return SANE_STATUS_INVAL; if (!handle) return SANE_STATUS_INVAL; if (option >= HP5590_OPT_LAST) return SANE_STATUS_INVAL; if (action == SANE_ACTION_GET_VALUE) { if (option == HP5590_OPT_NUM) { DBG(3, "%s: get total number of options - %u\n", __func__, HP5590_OPT_LAST); *((SANE_Int *) value) = HP5590_OPT_LAST; return SANE_STATUS_GOOD; } if (!scanner->opts) return SANE_STATUS_INVAL; DBG (DBG_proc, "%s: get option '%s' value\n", __func__, scanner->opts[option].name); if (option == HP5590_OPT_BR_X) { *(SANE_Fixed *) value = SANE_FIX (scanner->br_x * 25.4); } if (option == HP5590_OPT_BR_Y) { *(SANE_Fixed *) value = SANE_FIX (scanner->br_y * 25.4); } if (option == HP5590_OPT_TL_X) { *(SANE_Fixed *) value = SANE_FIX ((scanner->tl_x) * 25.4); } if (option == HP5590_OPT_TL_Y) { *(SANE_Fixed *) value = SANE_FIX (scanner->tl_y * 25.4); } if (option == HP5590_OPT_MODE) { switch (scanner->depth) { case DEPTH_BW: memset (value , 0, scanner->opts[option].size); memcpy (value, SANE_VALUE_SCAN_MODE_LINEART, strlen (SANE_VALUE_SCAN_MODE_LINEART)); break; case DEPTH_GRAY: memset (value , 0, scanner->opts[option].size); memcpy (value, SANE_VALUE_SCAN_MODE_GRAY, strlen (SANE_VALUE_SCAN_MODE_GRAY)); break; case DEPTH_COLOR_24: memset (value , 0, scanner->opts[option].size); memcpy (value, SANE_VALUE_SCAN_MODE_COLOR_24, strlen (SANE_VALUE_SCAN_MODE_COLOR_24)); break; case DEPTH_COLOR_48: memset (value , 0, scanner->opts[option].size); memcpy (value, SANE_VALUE_SCAN_MODE_COLOR_48, strlen (SANE_VALUE_SCAN_MODE_COLOR_48)); break; default: return SANE_STATUS_INVAL; } } if (option == HP5590_OPT_SOURCE) { switch (scanner->source) { case SOURCE_FLATBED: memset (value , 0, scanner->opts[option].size); memcpy (value, SANE_VALUE_SCAN_SOURCE_FLATBED, strlen (SANE_VALUE_SCAN_SOURCE_FLATBED)); break; case SOURCE_ADF: memset (value , 0, scanner->opts[option].size); memcpy (value, SANE_VALUE_SCAN_SOURCE_ADF, strlen (SANE_VALUE_SCAN_SOURCE_ADF)); break; case SOURCE_ADF_DUPLEX: memset (value , 0, scanner->opts[option].size); memcpy (value, SANE_VALUE_SCAN_SOURCE_ADF_DUPLEX, strlen (SANE_VALUE_SCAN_SOURCE_ADF_DUPLEX)); break; case SOURCE_TMA_SLIDES: memset (value , 0, scanner->opts[option].size); memcpy (value, SANE_VALUE_SCAN_SOURCE_TMA_SLIDES, strlen (SANE_VALUE_SCAN_SOURCE_TMA_SLIDES)); break; case SOURCE_TMA_NEGATIVES: memset (value , 0, scanner->opts[option].size); memcpy (value, SANE_VALUE_SCAN_SOURCE_TMA_NEGATIVES, strlen (SANE_VALUE_SCAN_SOURCE_TMA_NEGATIVES)); break; case SOURCE_NONE: default: return SANE_STATUS_INVAL; } } if (option == HP5590_OPT_RESOLUTION) { *(SANE_Int *) value = scanner->dpi; } if (option == HP5590_OPT_LAMP_TIMEOUT) { *(SANE_Bool *) value = scanner->extend_lamp_timeout; } if (option == HP5590_OPT_WAIT_FOR_BUTTON) { *(SANE_Bool *) value = scanner->wait_for_button; } if (option == HP5590_OPT_BUTTON_PRESSED) { enum button_status button_pressed = BUTTON_NONE; SANE_Status ret = read_button_pressed(scanner, &button_pressed); if (ret != SANE_STATUS_GOOD) return ret; switch (button_pressed) { case BUTTON_POWER: strncpy (value, BUTTON_PRESSED_VALUE_POWER_KEY, scanner->opts[option].size); break; case BUTTON_SCAN: strncpy (value, BUTTON_PRESSED_VALUE_SCAN_KEY, scanner->opts[option].size); break; case BUTTON_COLLECT: strncpy (value, BUTTON_PRESSED_VALUE_COLLECT_KEY, scanner->opts[option].size); break; case BUTTON_FILE: strncpy (value, BUTTON_PRESSED_VALUE_FILE_KEY, scanner->opts[option].size); break; case BUTTON_EMAIL: strncpy (value, BUTTON_PRESSED_VALUE_EMAIL_KEY, scanner->opts[option].size); break; case BUTTON_COPY: strncpy (value, BUTTON_PRESSED_VALUE_COPY_KEY, scanner->opts[option].size); break; case BUTTON_UP: strncpy (value, BUTTON_PRESSED_VALUE_UP_KEY, scanner->opts[option].size); break; case BUTTON_DOWN: strncpy (value, BUTTON_PRESSED_VALUE_DOWN_KEY, scanner->opts[option].size); break; case BUTTON_MODE: strncpy (value, BUTTON_PRESSED_VALUE_MODE_KEY, scanner->opts[option].size); break; case BUTTON_CANCEL: strncpy (value, BUTTON_PRESSED_VALUE_CANCEL_KEY, scanner->opts[option].size); break; case BUTTON_NONE: default: strncpy (value, BUTTON_PRESSED_VALUE_NONE_KEY, scanner->opts[option].size); } } if (option == HP5590_OPT_COLOR_LED) { SANE_Int lcd_counter = 0; enum color_led_status color_led = LED_COLOR; SANE_Status ret = read_lcd_and_led_values(scanner, &lcd_counter, &color_led); if (ret != SANE_STATUS_GOOD) return ret; switch (color_led) { case LED_BLACKWHITE: strncpy (value, COLOR_LED_VALUE_BLACKWHITE_KEY, scanner->opts[option].size); break; case LED_COLOR: default: strncpy (value, COLOR_LED_VALUE_COLOR_KEY, scanner->opts[option].size); } } if (option == HP5590_OPT_LCD_COUNTER) { SANE_Int lcd_counter = 0; enum color_led_status color_led = LED_COLOR; SANE_Status ret = read_lcd_and_led_values(scanner, &lcd_counter, &color_led); if (ret != SANE_STATUS_GOOD) return ret; *(SANE_Int *) value = lcd_counter; } if (option == HP5590_OPT_DOC_IN_ADF) { SANE_Bool doc_in_adf = SANE_FALSE; SANE_Status ret = read_doc_in_adf_value(scanner, &doc_in_adf); if (ret != SANE_STATUS_GOOD) return ret; *(SANE_Bool *) value = doc_in_adf; } if (option == HP5590_OPT_PREVIEW) { *(SANE_Bool *) value = scanner->preview; } if (option == HP5590_OPT_OVERWRITE_EOP_PIXEL) { *(SANE_Bool *) value = scanner->overwrite_eop_pixel; } if (option == HP5590_OPT_TRAILING_LINES_MODE) { switch (scanner->eop_trailing_lines_mode) { case TRAILING_LINES_MODE_RAW: memset (value , 0, scanner->opts[option].size); memcpy (value, TRAILING_LINES_MODE_RAW_KEY, strlen (TRAILING_LINES_MODE_RAW_KEY)); break; case TRAILING_LINES_MODE_LAST: memset (value , 0, scanner->opts[option].size); memcpy (value, TRAILING_LINES_MODE_LAST_KEY, strlen (TRAILING_LINES_MODE_LAST_KEY)); break; case TRAILING_LINES_MODE_RASTER: memset (value , 0, scanner->opts[option].size); memcpy (value, TRAILING_LINES_MODE_RASTER_KEY, strlen (TRAILING_LINES_MODE_RASTER_KEY)); break; case TRAILING_LINES_MODE_BLACK: memset (value , 0, scanner->opts[option].size); memcpy (value, TRAILING_LINES_MODE_BLACK_KEY, strlen (TRAILING_LINES_MODE_BLACK_KEY)); break; case TRAILING_LINES_MODE_WHITE: memset (value , 0, scanner->opts[option].size); memcpy (value, TRAILING_LINES_MODE_WHITE_KEY, strlen (TRAILING_LINES_MODE_WHITE_KEY)); break; case TRAILING_LINES_MODE_COLOR: memset (value , 0, scanner->opts[option].size); memcpy (value, TRAILING_LINES_MODE_COLOR_KEY, strlen (TRAILING_LINES_MODE_COLOR_KEY)); break; default: return SANE_STATUS_INVAL; } } if (option == HP5590_OPT_TRAILING_LINES_COLOR) { *(SANE_Int *) value = scanner->eop_trailing_lines_color; } } if (action == SANE_ACTION_SET_VALUE) { if (option == HP5590_OPT_NUM) return SANE_STATUS_INVAL; if (option == HP5590_OPT_BR_X) { float val = SANE_UNFIX(*(SANE_Fixed *) value) / 25.4; if (val <= scanner->tl_x) return SANE_STATUS_GOOD; scanner->br_x = val; if (info) *info = SANE_INFO_RELOAD_PARAMS; } if (option == HP5590_OPT_BR_Y) { float val = SANE_UNFIX(*(SANE_Fixed *) value) / 25.4; if (val <= scanner->tl_y) return SANE_STATUS_GOOD; scanner->br_y = val; if (info) *info = SANE_INFO_RELOAD_PARAMS; } if (option == HP5590_OPT_TL_X) { float val = SANE_UNFIX(*(SANE_Fixed *) value) / 25.4; if (val >= scanner->br_x) return SANE_STATUS_GOOD; scanner->tl_x = val; if (info) *info = SANE_INFO_RELOAD_PARAMS; } if (option == HP5590_OPT_TL_Y) { float val = SANE_UNFIX(*(SANE_Fixed *) value) / 25.4; if (val >= scanner->br_y) return SANE_STATUS_GOOD; scanner->tl_y = val; if (info) *info = SANE_INFO_RELOAD_PARAMS; } if (option == HP5590_OPT_MODE) { if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_MODE_LINEART) == 0) { scanner->depth = DEPTH_BW; } else if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_MODE_GRAY) == 0) { scanner->depth = DEPTH_GRAY; } else if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_MODE_COLOR_24) == 0) { scanner->depth = DEPTH_COLOR_24; } else if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_MODE_COLOR_48) == 0) { scanner->depth = DEPTH_COLOR_48; } else { return SANE_STATUS_INVAL; } if (info) *info = SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; } if (option == HP5590_OPT_SOURCE) { range_y.max = SANE_FIX(scanner->info->max_size_y * 25.4); if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_SOURCE_FLATBED) == 0) { scanner->source = SOURCE_FLATBED; range_x.max = SANE_FIX(scanner->info->max_size_x * 25.4); range_y.max = SANE_FIX(scanner->info->max_size_y * 25.4); scanner->br_x = scanner->info->max_size_x; scanner->br_y = scanner->info->max_size_y; } else if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_SOURCE_ADF) == 0) { /* In ADF modes the device can scan up to ADF_MAX_Y_INCHES, which is usually * bigger than what scanner reports back during initialization */ if (! (scanner->info->features & FEATURE_ADF)) { DBG(DBG_err, "ADF feature not available: %s\n", (char *) value); return SANE_STATUS_UNSUPPORTED; } scanner->source = SOURCE_ADF; range_x.max = SANE_FIX(scanner->info->max_size_x * 25.4); range_y.max = SANE_FIX(ADF_MAX_Y_INCHES * 25.4); scanner->br_x = scanner->info->max_size_x; scanner->br_y = ADF_MAX_Y_INCHES; } else if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_SOURCE_ADF_DUPLEX) == 0) { if (! (scanner->info->features & FEATURE_ADF)) { DBG(DBG_err, "ADF feature not available: %s\n", (char *) value); return SANE_STATUS_UNSUPPORTED; } scanner->source = SOURCE_ADF_DUPLEX; range_x.max = SANE_FIX(scanner->info->max_size_x * 25.4); range_y.max = SANE_FIX(ADF_MAX_Y_INCHES * 25.4); scanner->br_x = scanner->info->max_size_x; scanner->br_y = ADF_MAX_Y_INCHES; } else if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_SOURCE_TMA_SLIDES) == 0) { if (! (scanner->info->features & FEATURE_TMA)) { DBG(DBG_err, "TMA feature not available: %s\n", (char *) value); return SANE_STATUS_UNSUPPORTED; } scanner->source = SOURCE_TMA_SLIDES; range_x.max = SANE_FIX(TMA_MAX_X_INCHES * 25.4); range_y.max = SANE_FIX(TMA_MAX_Y_INCHES * 25.4); scanner->br_x = TMA_MAX_X_INCHES; scanner->br_y = TMA_MAX_Y_INCHES; } else if (strcmp ((char *) value, (char *) SANE_VALUE_SCAN_SOURCE_TMA_NEGATIVES) == 0) { if (! (scanner->info->features & FEATURE_TMA)) { DBG(DBG_err, "TMA feature not available: %s\n", (char *) value); return SANE_STATUS_UNSUPPORTED; } scanner->source = SOURCE_TMA_NEGATIVES; range_x.max = SANE_FIX(TMA_MAX_X_INCHES * 25.4); range_y.max = SANE_FIX(TMA_MAX_Y_INCHES * 25.4); scanner->br_x = TMA_MAX_X_INCHES; scanner->br_y = TMA_MAX_Y_INCHES; } else { return SANE_STATUS_INVAL; } if (info) *info = SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; } if (option == HP5590_OPT_RESOLUTION) { scanner->dpi = *(SANE_Int *) value; if (info) *info = SANE_INFO_RELOAD_PARAMS; } if (option == HP5590_OPT_LAMP_TIMEOUT) { scanner->extend_lamp_timeout = *(SANE_Bool *) value; } if (option == HP5590_OPT_WAIT_FOR_BUTTON) { scanner->wait_for_button = *(SANE_Bool *) value; } if (option == HP5590_OPT_BUTTON_PRESSED) { DBG(DBG_verbose, "State of buttons is read only. Setting of state will be ignored.\n"); } if (option == HP5590_OPT_COLOR_LED) { DBG(DBG_verbose, "State of color LED indicator is read only. Setting of state will be ignored.\n"); } if (option == HP5590_OPT_LCD_COUNTER) { DBG(DBG_verbose, "Value of LCD counter is read only. Setting of value will be ignored.\n"); } if (option == HP5590_OPT_DOC_IN_ADF) { DBG(DBG_verbose, "Value of document-available indicator is read only. Setting of value will be ignored.\n"); } if (option == HP5590_OPT_PREVIEW) { scanner->preview = *(SANE_Bool *) value; } if (option == HP5590_OPT_OVERWRITE_EOP_PIXEL) { scanner->overwrite_eop_pixel = *(SANE_Bool *) value; } if (option == HP5590_OPT_TRAILING_LINES_MODE) { if (strcmp ((char *) value, (char *) TRAILING_LINES_MODE_RAW_KEY) == 0) scanner->eop_trailing_lines_mode = TRAILING_LINES_MODE_RAW; if (strcmp ((char *) value, (char *) TRAILING_LINES_MODE_LAST_KEY) == 0) scanner->eop_trailing_lines_mode = TRAILING_LINES_MODE_LAST; if (strcmp ((char *) value, (char *) TRAILING_LINES_MODE_RASTER_KEY) == 0) scanner->eop_trailing_lines_mode = TRAILING_LINES_MODE_RASTER; if (strcmp ((char *) value, (char *) TRAILING_LINES_MODE_BLACK_KEY) == 0) scanner->eop_trailing_lines_mode = TRAILING_LINES_MODE_BLACK; if (strcmp ((char *) value, (char *) TRAILING_LINES_MODE_WHITE_KEY) == 0) scanner->eop_trailing_lines_mode = TRAILING_LINES_MODE_WHITE; if (strcmp ((char *) value, (char *) TRAILING_LINES_MODE_COLOR_KEY) == 0) scanner->eop_trailing_lines_mode = TRAILING_LINES_MODE_COLOR; } if (option == HP5590_OPT_TRAILING_LINES_COLOR) { scanner->eop_trailing_lines_color = *(SANE_Int *) value; } } return SANE_STATUS_GOOD; } /******************************************************************************/ SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { struct hp5590_scanner *scanner = handle; SANE_Status ret; unsigned int pixel_bits; DBG (DBG_proc, "%s\n", __func__); if (!params) return SANE_STATUS_INVAL; if (!handle) return SANE_STATUS_INVAL; ret = calc_image_params (scanner, (unsigned int *) &pixel_bits, (unsigned int *) ¶ms->pixels_per_line, (unsigned int *) ¶ms->bytes_per_line, (unsigned int *) ¶ms->lines, NULL); if (ret != SANE_STATUS_GOOD) return ret; switch (scanner->depth) { case DEPTH_BW: params->depth = pixel_bits; params->format = SANE_FRAME_GRAY; params->last_frame = SANE_TRUE; break; case DEPTH_GRAY: params->depth = pixel_bits; params->format = SANE_FRAME_GRAY; params->last_frame = SANE_TRUE; break; case DEPTH_COLOR_24: params->depth = pixel_bits / 3; params->last_frame = SANE_TRUE; params->format = SANE_FRAME_RGB; break; case DEPTH_COLOR_48: params->depth = pixel_bits / 3; params->last_frame = SANE_TRUE; params->format = SANE_FRAME_RGB; break; default: DBG(DBG_err, "%s: Unknown depth\n", __func__); return SANE_STATUS_INVAL; } DBG (DBG_proc, "format: %u, last_frame: %u, bytes_per_line: %u, " "pixels_per_line: %u, lines: %u, depth: %u\n", params->format, params->last_frame, params->bytes_per_line, params->pixels_per_line, params->lines, params->depth); return SANE_STATUS_GOOD; } /******************************************************************************/ SANE_Status sane_start (SANE_Handle handle) { struct hp5590_scanner *scanner = handle; SANE_Status ret; unsigned int bytes_per_line; DBG (DBG_proc, "%s\n", __func__); if (!scanner) return SANE_STATUS_INVAL; /* Cleanup for all pages. */ if (scanner->eop_last_line_data) { /* Release last line data */ free (scanner->eop_last_line_data); scanner->eop_last_line_data = NULL; scanner->eop_last_line_data_rpos = 0; } if (scanner->one_line_read_buffer) { /* Release temporary line buffer. */ free (scanner->one_line_read_buffer); scanner->one_line_read_buffer = NULL; scanner->one_line_read_buffer_rpos = 0; } if (scanner->color_shift_line_buffer1) { /* Release line buffer1 for shifting colors. */ free (scanner->color_shift_line_buffer1); scanner->color_shift_line_buffer1 = NULL; scanner->color_shift_buffered_lines1 = 0; } if (scanner->color_shift_line_buffer2) { /* Release line buffer2 for shifting colors. */ free (scanner->color_shift_line_buffer2); scanner->color_shift_line_buffer2 = NULL; scanner->color_shift_buffered_lines2 = 0; } if ( scanner->scanning == SANE_TRUE && ( scanner->source == SOURCE_ADF || scanner->source == SOURCE_ADF_DUPLEX)) { DBG (DBG_verbose, "%s: Scanner is scanning, check if more data is available\n", __func__); ret = hp5590_is_data_available (scanner->dn, scanner->proto_flags); if (ret == SANE_STATUS_GOOD) { DBG (DBG_verbose, "%s: More data is available\n", __func__); scanner->transferred_image_size = scanner->image_size; return SANE_STATUS_GOOD; } if (ret != SANE_STATUS_NO_DOCS) return ret; } sane_cancel (handle); if (scanner->wait_for_button) { enum button_status status; for (;;) { ret = hp5590_read_buttons (scanner->dn, scanner->proto_flags, &status); if (ret != SANE_STATUS_GOOD) return ret; if (status == BUTTON_CANCEL) return SANE_STATUS_CANCELLED; if (status != BUTTON_NONE && status != BUTTON_POWER) break; usleep (100 * 1000); } } DBG (DBG_verbose, "Init scanner\n"); ret = hp5590_init_scanner (scanner->dn, scanner->proto_flags, NULL, SCANNER_NONE); if (ret != SANE_STATUS_GOOD) return ret; ret = hp5590_power_status (scanner->dn, scanner->proto_flags); if (ret != SANE_STATUS_GOOD) return ret; DBG (DBG_verbose, "Wakeup\n"); ret = hp5590_select_source_and_wakeup (scanner->dn, scanner->proto_flags, scanner->source, scanner->extend_lamp_timeout); if (ret != SANE_STATUS_GOOD) return ret; ret = hp5590_set_scan_params (scanner->dn, scanner->proto_flags, scanner->info, scanner->tl_x * scanner->dpi, scanner->tl_y * scanner->dpi, (scanner->br_x - scanner->tl_x) * scanner->dpi, (scanner->br_y - scanner->tl_y) * scanner->dpi, scanner->dpi, scanner->depth, scanner->preview ? MODE_PREVIEW : MODE_NORMAL, scanner->source); if (ret != SANE_STATUS_GOOD) { hp5590_reset_scan_head (scanner->dn, scanner->proto_flags); return ret; } ret = calc_image_params (scanner, NULL, NULL, &bytes_per_line, NULL, &scanner->image_size); if (ret != SANE_STATUS_GOOD) { hp5590_reset_scan_head (scanner->dn, scanner->proto_flags); return ret; } scanner->transferred_image_size = scanner->image_size; if ( scanner->depth == DEPTH_COLOR_24 || scanner->depth == DEPTH_COLOR_48) { DBG (1, "Color 24/48 bits: checking if image size is correctly " "aligned on number of colors\n"); if (bytes_per_line % 3) { DBG (DBG_err, "Color 24/48 bits: image size doesn't lined up on number of colors (3) " "(image size: %llu, bytes per line %u)\n", scanner->image_size, bytes_per_line); hp5590_reset_scan_head (scanner->dn, scanner->proto_flags); return SANE_STATUS_INVAL; } DBG (1, "Color 24/48 bits: image size is correctly aligned on number of colors " "(image size: %llu, bytes per line %u)\n", scanner->image_size, bytes_per_line); DBG (1, "Color 24/48 bits: checking if image size is correctly " "aligned on bytes per line\n"); if (scanner->image_size % bytes_per_line) { DBG (DBG_err, "Color 24/48 bits: image size doesn't lined up on bytes per line " "(image size: %llu, bytes per line %u)\n", scanner->image_size, bytes_per_line); hp5590_reset_scan_head (scanner->dn, scanner->proto_flags); return SANE_STATUS_INVAL; } DBG (1, "Color 24/48 bits: image size correctly aligned on bytes per line " "(images size: %llu, bytes per line: %u)\n", scanner->image_size, bytes_per_line); } DBG (DBG_verbose, "Final image size: %llu\n", scanner->image_size); DBG (DBG_verbose, "Reverse calibration maps\n"); ret = hp5590_send_reverse_calibration_map (scanner->dn, scanner->proto_flags); if (ret != SANE_STATUS_GOOD) { hp5590_reset_scan_head (scanner->dn, scanner->proto_flags); return ret; } DBG (DBG_verbose, "Forward calibration maps\n"); ret = hp5590_send_forward_calibration_maps (scanner->dn, scanner->proto_flags); if (ret != SANE_STATUS_GOOD) { hp5590_reset_scan_head (scanner->dn, scanner->proto_flags); return ret; } if (scanner->adf_next_page_lines_data) { free (scanner->adf_next_page_lines_data); scanner->adf_next_page_lines_data = NULL; scanner->adf_next_page_lines_data_size = 0; scanner->adf_next_page_lines_data_rpos = 0; scanner->adf_next_page_lines_data_wpos = 0; } scanner->scanning = SANE_TRUE; DBG (DBG_verbose, "Starting scan\n"); ret = hp5590_start_scan (scanner->dn, scanner->proto_flags); /* Check for paper jam */ if ( ret == SANE_STATUS_DEVICE_BUSY && ( scanner->source == SOURCE_ADF || scanner->source == SOURCE_ADF_DUPLEX)) return SANE_STATUS_JAMMED; if (ret != SANE_STATUS_GOOD) { hp5590_reset_scan_head (scanner->dn, scanner->proto_flags); return ret; } return SANE_STATUS_GOOD; } /******************************************************************************/ static void invert_negative_colors (unsigned char *buf, unsigned int bytes_per_line, struct hp5590_scanner *scanner) { /* Invert lineart or negatives. */ int is_linear = (scanner->depth == DEPTH_BW); int is_negative = (scanner->source == SOURCE_TMA_NEGATIVES); if (is_linear ^ is_negative) { for (unsigned int k = 0; k < bytes_per_line; k++) buf[k] ^= 0xff; } } /******************************************************************************/ static SANE_Status convert_gray_and_lineart (struct hp5590_scanner *scanner, SANE_Byte *data, SANE_Int size) { unsigned int pixels_per_line; unsigned int pixel_bits; unsigned int bytes_per_line; unsigned int lines; unsigned char *buf; SANE_Status ret; hp5590_assert (scanner != NULL); hp5590_assert (data != NULL); if ( ! (scanner->depth == DEPTH_BW || scanner->depth == DEPTH_GRAY)) return SANE_STATUS_GOOD; DBG (DBG_proc, "%s\n", __func__); ret = calc_image_params (scanner, &pixel_bits, &pixels_per_line, &bytes_per_line, NULL, NULL); if (ret != SANE_STATUS_GOOD) return ret; lines = size / bytes_per_line; buf = data; for (unsigned int i = 0; i < lines; buf += bytes_per_line, ++i) { if (! scanner->eop_last_line_data) { if (pixels_per_line > 0) { /* Test for last-line indicator pixel. If found, store last line * and optionally overwrite indicator pixel with neighbor value. */ unsigned int j = bytes_per_line - 1; int eop_found = 0; if (scanner->depth == DEPTH_GRAY) { eop_found = (buf[j] != 0); if (scanner->overwrite_eop_pixel && (j > 0)) { buf[j] = buf[j-1]; } } else if (scanner->depth == DEPTH_BW) { eop_found = (buf[j] != 0); if (scanner->overwrite_eop_pixel && (j > 0)) { buf[j] = (buf[j-1] & 0x01) ? 0xff : 0; } } invert_negative_colors (buf, bytes_per_line, scanner); if (eop_found && (! scanner->eop_last_line_data)) { DBG (DBG_verbose, "Found end-of-page at line %u in reading block.\n", i); scanner->eop_last_line_data = malloc(bytes_per_line); if (! scanner->eop_last_line_data) return SANE_STATUS_NO_MEM; memcpy (scanner->eop_last_line_data, buf, bytes_per_line); scanner->eop_last_line_data_rpos = 0; /* Fill trailing line buffer with requested color. */ if (scanner->eop_trailing_lines_mode == TRAILING_LINES_MODE_RASTER) { /* Black-white raster. */ if (scanner->depth == DEPTH_BW) { memset (scanner->eop_last_line_data, 0xaa, bytes_per_line); } else { /* Gray. */ for (unsigned int k = 0; k < bytes_per_line; ++k) { scanner->eop_last_line_data[k] = (k & 1 ? 0xff : 0); } } } else if (scanner->eop_trailing_lines_mode == TRAILING_LINES_MODE_WHITE) { /* White. */ if (scanner->depth == DEPTH_BW) { memset (scanner->eop_last_line_data, 0x00, bytes_per_line); } else { memset (scanner->eop_last_line_data, 0xff, bytes_per_line); } } else if (scanner->eop_trailing_lines_mode == TRAILING_LINES_MODE_BLACK) { /* Black. */ if (scanner->depth == DEPTH_BW) { memset (scanner->eop_last_line_data, 0xff, bytes_per_line); } else { memset (scanner->eop_last_line_data, 0x00, bytes_per_line); } } else if (scanner->eop_trailing_lines_mode == TRAILING_LINES_MODE_COLOR) { if (scanner->depth == DEPTH_BW) { /* Black or white. */ memset (scanner->eop_last_line_data, scanner->eop_trailing_lines_color & 0x01 ? 0x00 : 0xff, bytes_per_line); } else { /* Gray value */ memset (scanner->eop_last_line_data, scanner->eop_trailing_lines_color & 0xff, bytes_per_line); } } } } } else { DBG (DBG_verbose, "Trailing lines mode: line=%u, mode=%d, color=%u\n", i, scanner->eop_trailing_lines_mode, scanner->eop_trailing_lines_color); if ((scanner->source == SOURCE_ADF) || (scanner->source == SOURCE_ADF_DUPLEX)) { /* We are in in ADF mode after last-line and store next page data * to buffer. */ if (! scanner->adf_next_page_lines_data) { unsigned int n_rest_lines = lines - i; unsigned int buf_size = n_rest_lines * bytes_per_line; scanner->adf_next_page_lines_data = malloc(buf_size); if (! scanner->adf_next_page_lines_data) return SANE_STATUS_NO_MEM; scanner->adf_next_page_lines_data_size = buf_size; scanner->adf_next_page_lines_data_rpos = 0; scanner->adf_next_page_lines_data_wpos = 0; DBG (DBG_verbose, "ADF between pages: Save n=%u next page lines in buffer.\n", n_rest_lines); } DBG (DBG_verbose, "ADF between pages: Store line %u of %u.\n", i, lines); invert_negative_colors (buf, bytes_per_line, scanner); memcpy (scanner->adf_next_page_lines_data + scanner->adf_next_page_lines_data_wpos, buf, bytes_per_line); scanner->adf_next_page_lines_data_wpos += bytes_per_line; } if (scanner->eop_trailing_lines_mode != TRAILING_LINES_MODE_RAW) { /* Copy last line data or corresponding color over trailing lines * data. */ memcpy (buf, scanner->eop_last_line_data, bytes_per_line); } } } return SANE_STATUS_GOOD; } /******************************************************************************/ static unsigned char get_checked (unsigned char *ptr, unsigned int i, unsigned int length) { if (i < length) { return ptr[i]; } DBG (DBG_details, "get from array out of range: idx=%u, size=%u\n", i, length); return 0; } /******************************************************************************/ static SANE_Status convert_to_rgb (struct hp5590_scanner *scanner, SANE_Byte *data, SANE_Int size) { unsigned int pixels_per_line; unsigned int pixel_bits; unsigned int bytes_per_color; unsigned int bytes_per_line; unsigned int bytes_per_line_limit; unsigned int lines; unsigned int i, j; unsigned char *buf; unsigned char *bufptr; unsigned char *ptr; SANE_Status ret; hp5590_assert (scanner != NULL); hp5590_assert (data != NULL); if ( ! (scanner->depth == DEPTH_COLOR_24 || scanner->depth == DEPTH_COLOR_48)) return SANE_STATUS_GOOD; DBG (DBG_proc, "%s\n", __func__); #ifndef HAS_WORKING_COLOR_48 if (scanner->depth == DEPTH_COLOR_48) return SANE_STATUS_UNSUPPORTED; #endif ret = calc_image_params (scanner, &pixel_bits, &pixels_per_line, &bytes_per_line, NULL, NULL); if (ret != SANE_STATUS_GOOD) return ret; lines = size / bytes_per_line; bytes_per_color = (pixel_bits + 7) / 8; bytes_per_line_limit = bytes_per_line; if ((scanner->depth == DEPTH_COLOR_48) && (bytes_per_line_limit > 3)) { /* Last-line indicator pixel has only 3 bytes instead of 6. */ bytes_per_line_limit -= 3; } DBG (DBG_verbose, "Length : %u\n", size); DBG (DBG_verbose, "Converting row RGB to normal RGB\n"); DBG (DBG_verbose, "Bytes per line %u\n", bytes_per_line); DBG (DBG_verbose, "Bytes per line limited %u\n", bytes_per_line_limit); DBG (DBG_verbose, "Bytes per color %u\n", bytes_per_color); DBG (DBG_verbose, "Pixels per line %u\n", pixels_per_line); DBG (DBG_verbose, "Lines %u\n", lines); /* Use working buffer for color mapping. */ bufptr = malloc (size); if (! bufptr) return SANE_STATUS_NO_MEM; memset (bufptr, 0, size); buf = bufptr; ptr = data; for (j = 0; j < lines; ptr += bytes_per_line_limit, buf += bytes_per_line, j++) { for (i = 0; i < pixels_per_line; i++) { /* Color mapping from raw scanner data to RGB buffer. */ if (scanner->depth == DEPTH_COLOR_24) { /* R */ buf[i*3] = get_checked(ptr, i, bytes_per_line_limit); /* G */ buf[i*3+1] = get_checked(ptr, i+pixels_per_line, bytes_per_line_limit);; /* B */ buf[i*3+2] = get_checked(ptr, i+pixels_per_line*2, bytes_per_line_limit); } else if (scanner->depth == DEPTH_COLOR_48) { /* Note: The last-line indicator pixel uses only 24 bits, not 48. *Blue uses offset of 2 bytes. Green swaps lo and hi. */ /* R lo, hi*/ buf[i*6] = get_checked(ptr, 2*i+(pixels_per_line-1)*0+1, bytes_per_line_limit); buf[i*6+1] = get_checked(ptr, 2*i+(pixels_per_line-1)*0+0, bytes_per_line_limit); /* G lo, hi*/ buf[i*6+2] = get_checked(ptr, 2*i+(pixels_per_line-1)*2+0, bytes_per_line_limit); buf[i*6+3] = get_checked(ptr, 2*i+(pixels_per_line-1)*2+1, bytes_per_line_limit); /* B lo, hi*/ buf[i*6+4] = get_checked(ptr, 2*i+(pixels_per_line-1)*4+1+2, bytes_per_line_limit); buf[i*6+5] = get_checked(ptr, 2*i+(pixels_per_line-1)*4+0+2, bytes_per_line_limit); } } if (! scanner->eop_last_line_data) { if (pixels_per_line > 0) { /* Test for last-line indicator pixel on blue. If found, store * last line and optionally overwrite indicator pixel with * neighbor value. */ i = pixels_per_line - 1; int eop_found = 0; if (scanner->depth == DEPTH_COLOR_24) { /* DBG (DBG_details, "BUF24: %u %u %u\n", buf[i*3], buf[i*3+1], buf[i*3+2]); */ eop_found = (buf[i*3+2] != 0); if (scanner->overwrite_eop_pixel && (i > 0)) { buf[i*3] = buf[(i-1)*3]; buf[i*3+1] = buf[(i-1)*3+1]; buf[i*3+2] = buf[(i-1)*3+2]; } } else if (scanner->depth == DEPTH_COLOR_48) { /* DBG (DBG_details, "BUF48: %u %u %u\n", buf[i*6+1], buf[i*6+3], buf[i*6+5]); */ eop_found = (buf[i*6+5] != 0); if (scanner->overwrite_eop_pixel && (i > 0)) { buf[i*6] = buf[(i-1)*6]; buf[i*6+1] = buf[(i-1)*6+1]; buf[i*6+2] = buf[(i-1)*6+2]; buf[i*6+3] = buf[(i-1)*6+3]; buf[i*6+4] = buf[(i-1)*6+4]; buf[i*6+5] = buf[(i-1)*6+5]; } } invert_negative_colors (buf, bytes_per_line, scanner); if (eop_found && (! scanner->eop_last_line_data)) { DBG (DBG_verbose, "Found end-of-page at line %u in reading block.\n", j); scanner->eop_last_line_data = malloc(bytes_per_line); if (! scanner->eop_last_line_data) return SANE_STATUS_NO_MEM; memcpy (scanner->eop_last_line_data, buf, bytes_per_line); scanner->eop_last_line_data_rpos = 0; /* Fill trailing line buffer with requested color. */ if (scanner->eop_trailing_lines_mode == TRAILING_LINES_MODE_RASTER) { /* Black-white raster. */ if (scanner->depth == DEPTH_COLOR_24) { for (unsigned int k = 0; k < bytes_per_line; ++k) { scanner->eop_last_line_data[k] = (k % 6 < 3 ? 0xff : 0); } } else { /* Color48. */ for (unsigned int k = 0; k < bytes_per_line; ++k) { scanner->eop_last_line_data[k] = (k % 12 < 6 ? 0xff : 0); } } } else if (scanner->eop_trailing_lines_mode == TRAILING_LINES_MODE_WHITE) { memset (scanner->eop_last_line_data, 0xff, bytes_per_line); } else if (scanner->eop_trailing_lines_mode == TRAILING_LINES_MODE_BLACK) { memset (scanner->eop_last_line_data, 0x00, bytes_per_line); } else if (scanner->eop_trailing_lines_mode == TRAILING_LINES_MODE_COLOR) { /* RGB color value. */ int rgb[3]; rgb[0] = (scanner->eop_trailing_lines_color >> 16) & 0xff; rgb[1] = (scanner->eop_trailing_lines_color >> 8) & 0xff; rgb[2] = scanner->eop_trailing_lines_color & 0xff; if (scanner->depth == DEPTH_COLOR_24) { for (unsigned int k = 0; k < bytes_per_line; ++k) { scanner->eop_last_line_data[k] = rgb[k % 3]; } } else { /* Color48. */ for (unsigned int k = 0; k < bytes_per_line; ++k) { scanner->eop_last_line_data[k] = rgb[(k % 6) >> 1]; } } } } } } else { DBG (DBG_verbose, "Trailing lines mode: line=%u, mode=%d, color=%u\n", j, scanner->eop_trailing_lines_mode, scanner->eop_trailing_lines_color); if ((scanner->source == SOURCE_ADF) || (scanner->source == SOURCE_ADF_DUPLEX)) { /* We are in in ADF mode after last-line and store next page data * to buffer. */ if (! scanner->adf_next_page_lines_data) { unsigned int n_rest_lines = lines - j; unsigned int buf_size = n_rest_lines * bytes_per_line; scanner->adf_next_page_lines_data = malloc(buf_size); if (! scanner->adf_next_page_lines_data) return SANE_STATUS_NO_MEM; scanner->adf_next_page_lines_data_size = buf_size; scanner->adf_next_page_lines_data_rpos = 0; scanner->adf_next_page_lines_data_wpos = 0; DBG (DBG_verbose, "ADF between pages: Save n=%u next page lines in buffer.\n", n_rest_lines); } DBG (DBG_verbose, "ADF between pages: Store line %u of %u.\n", j, lines); invert_negative_colors (buf, bytes_per_line, scanner); memcpy (scanner->adf_next_page_lines_data + scanner->adf_next_page_lines_data_wpos, buf, bytes_per_line); scanner->adf_next_page_lines_data_wpos += bytes_per_line; } if (scanner->eop_trailing_lines_mode != TRAILING_LINES_MODE_RAW) { /* Copy last line data or corresponding color over trailing lines * data. */ memcpy (buf, scanner->eop_last_line_data, bytes_per_line); } } } memcpy (data, bufptr, size); free (bufptr); return SANE_STATUS_GOOD; } /******************************************************************************/ static void read_data_from_temporary_buffer(struct hp5590_scanner *scanner, SANE_Byte * data, unsigned int max_length, unsigned int bytes_per_line, SANE_Int *length) { *length = 0; if (scanner && scanner->one_line_read_buffer) { /* Copy scan data from temporary read buffer and return size copied data. */ /* Release buffer, when no data left. */ unsigned int rest_len; rest_len = bytes_per_line - scanner->one_line_read_buffer_rpos; rest_len = (rest_len < max_length) ? rest_len : max_length; if (rest_len > 0) { memcpy (data, scanner->one_line_read_buffer + scanner->one_line_read_buffer_rpos, rest_len); scanner->one_line_read_buffer_rpos += rest_len; scanner->transferred_image_size -= rest_len; *length = rest_len; } DBG (DBG_verbose, "Copy scan data from temporary buffer: length = %u, rest in buffer = %u.\n", *length, bytes_per_line - scanner->one_line_read_buffer_rpos); if (scanner->one_line_read_buffer_rpos >= bytes_per_line) { DBG (DBG_verbose, "Release temporary buffer.\n"); free (scanner->one_line_read_buffer); scanner->one_line_read_buffer = NULL; scanner->one_line_read_buffer_rpos = 0; } } } /******************************************************************************/ static SANE_Status sane_read_internal (struct hp5590_scanner * scanner, SANE_Byte * data, SANE_Int max_length, SANE_Int * length, unsigned int bytes_per_line) { SANE_Status ret; DBG (DBG_proc, "%s, length %u, left %llu\n", __func__, max_length, scanner->transferred_image_size); SANE_Int length_limited = 0; *length = max_length; if ((unsigned long long) *length > scanner->transferred_image_size) *length = (SANE_Int) scanner->transferred_image_size; /* Align reading size to bytes per line. */ *length -= *length % bytes_per_line; if (scanner->depth == DEPTH_COLOR_48) { /* Note: The last-line indicator pixel uses only 24 bits (3 bytes), not * 48 bits (6 bytes). */ if (bytes_per_line > 3) { length_limited = *length - *length % (bytes_per_line - 3); } } DBG (DBG_verbose, "Aligning requested size to bytes per line " "(requested: %d, aligned: %u, limit_for_48bit: %u)\n", max_length, *length, length_limited); if (max_length <= 0) { DBG (DBG_verbose, "Buffer too small for one scan line. Need at least %u bytes per line.\n", bytes_per_line); scanner->scanning = SANE_FALSE; return SANE_STATUS_UNSUPPORTED; } if (scanner->one_line_read_buffer) { /* Copy scan data from temporary read buffer. */ read_data_from_temporary_buffer (scanner, data, max_length, bytes_per_line, length); if (*length > 0) { DBG (DBG_verbose, "Return %d bytes, left %llu bytes.\n", *length, scanner->transferred_image_size); return SANE_STATUS_GOOD; } } /* Buffer to return scanned data. We need at least space for one line to * simplify color processing and last-line detection. If call buffer is too * small, use temporary read buffer for reading one line instead. */ SANE_Byte * scan_data; SANE_Int scan_data_length; scan_data = data; scan_data_length = *length; /* Note, read length is shorter in 48bit mode. */ SANE_Int length_for_read = length_limited ? length_limited : scan_data_length; if (length_for_read == 0) { /* Call buffer is too small for one line. Use temporary read buffer * instead. */ if (! scanner->one_line_read_buffer) { scanner->one_line_read_buffer = malloc (bytes_per_line); if (! scanner->one_line_read_buffer) return SANE_STATUS_NO_MEM; memset (scanner->one_line_read_buffer, 0, bytes_per_line); } DBG (DBG_verbose, "Call buffer too small for one scan line. Use temporary read buffer for one line with %u bytes.\n", bytes_per_line); /* Scan and process next line in temporary buffer. */ scan_data = scanner->one_line_read_buffer; scan_data_length = bytes_per_line; length_for_read = bytes_per_line; if (scanner->depth == DEPTH_COLOR_48) { /* The last-line indicator pixel uses only 24 bits (3 bytes), not 48 * bits (6 bytes). */ if (length_for_read > 3) { length_for_read -= 3; } } } int read_from_scanner = 1; if ((scanner->source == SOURCE_ADF) || (scanner->source == SOURCE_ADF_DUPLEX)) { if (scanner->eop_last_line_data) { /* Scanner is in ADF mode between last-line of previous page and * start of next page. * Fill remaining lines with last-line data. */ unsigned int wpos = 0; while (wpos < (unsigned int) scan_data_length) { unsigned int n1 = scan_data_length - wpos; unsigned int n2 = bytes_per_line - scanner->eop_last_line_data_rpos; n1 = (n1 < n2) ? n1 : n2; memcpy (scan_data + wpos, scanner->eop_last_line_data + scanner->eop_last_line_data_rpos, n1); wpos += n1; scanner->eop_last_line_data_rpos += n1; if (scanner->eop_last_line_data_rpos >= bytes_per_line) scanner->eop_last_line_data_rpos = 0; } read_from_scanner = (wpos == 0); DBG (DBG_verbose, "ADF use last-line data, wlength=%u, length=%u\n", wpos, scan_data_length); } else if (scanner->adf_next_page_lines_data) { /* Scanner is in ADF mode at start of next page and already some next * page data is available from earlier read operation. Return this * data. */ unsigned int wpos = 0; while ((wpos < (unsigned int) scan_data_length) && (scanner->adf_next_page_lines_data_rpos < scanner->adf_next_page_lines_data_size)) { unsigned int n1 = scan_data_length - wpos; unsigned int n2 = scanner->adf_next_page_lines_data_size - scanner->adf_next_page_lines_data_rpos; n1 = (n1 < n2) ? n1 : n2; memcpy (scan_data + wpos, scanner->adf_next_page_lines_data + scanner->adf_next_page_lines_data_rpos, n1); wpos += n1; scanner->adf_next_page_lines_data_rpos += n1; if (scanner->adf_next_page_lines_data_rpos >= scanner->adf_next_page_lines_data_size) { free (scanner->adf_next_page_lines_data); scanner->adf_next_page_lines_data = NULL; scanner->adf_next_page_lines_data_size = 0; scanner->adf_next_page_lines_data_rpos = 0; scanner->adf_next_page_lines_data_wpos = 0; } } scan_data_length = wpos; read_from_scanner = (wpos == 0); DBG (DBG_verbose, "ADF use next-page data, wlength=%u, length=%u\n", wpos, scan_data_length); } } if (read_from_scanner) { /* Read data from scanner. */ ret = hp5590_read (scanner->dn, scanner->proto_flags, scan_data, length_for_read, scanner->bulk_read_state); if (ret != SANE_STATUS_GOOD) { scanner->scanning = SANE_FALSE; return ret; } /* Look for last-line indicator pixels in convert functions. * If found: * - Overwrite indicator pixel with neighboring color (optional). * - Save last line data for later use. */ ret = convert_to_rgb (scanner, scan_data, scan_data_length); if (ret != SANE_STATUS_GOOD) { scanner->scanning = SANE_FALSE; return ret; } ret = convert_gray_and_lineart (scanner, scan_data, scan_data_length); if (ret != SANE_STATUS_GOOD) return ret; } if (data == scan_data) { /* Scanned to call buffer. */ scanner->transferred_image_size -= scan_data_length; *length = scan_data_length; } else { /* Scanned to temporary read buffer. */ if (scanner->one_line_read_buffer) { /* Copy scan data from temporary read buffer. */ read_data_from_temporary_buffer (scanner, data, max_length, scan_data_length, length); } else { *length = 0; } } DBG (DBG_verbose, "Return %d bytes, left %llu bytes\n", *length, scanner->transferred_image_size); return SANE_STATUS_GOOD; } /****************************************************************************** * Copy at maximum the last n lines from the src buffer to the begin of the dst * buffer. * Return number of lines copied. */ static SANE_Int copy_n_last_lines(SANE_Byte * src, SANE_Int src_len, SANE_Byte * dst, SANE_Int n, unsigned int bytes_per_line) { DBG (DBG_proc, "%s\n", __func__); SANE_Int n_copy = MY_MIN(src_len, n); SANE_Byte * src1 = src + (src_len - n_copy) * bytes_per_line; memcpy (dst, src1, n_copy * bytes_per_line); return n_copy; } /****************************************************************************** * Copy the color values from line - delta_lines to line. * buffer2 : Source and target buffer. * buffer1 : Only source buffer. Contains lines scanned before lines in buffer1. * color_idx : Index of color to be copied (0..2). * delta_lines : color shift. * color_48 : True = 2 byte , false = 1 byte per color. */ static void shift_color_lines(SANE_Byte * buffer2, SANE_Int n_lines2, SANE_Byte * buffer1, SANE_Int n_lines1, SANE_Int color_idx, SANE_Int delta_lines, SANE_Bool color_48, unsigned int bytes_per_line) { DBG (DBG_proc, "%s\n", __func__); for (SANE_Int i = n_lines2 - 1; i >= 0; --i) { SANE_Byte * dst = buffer2 + i * bytes_per_line; SANE_Int ii = i - delta_lines; SANE_Byte * src = NULL; SANE_Int source_color_idx = color_idx; if (ii >= 0) { /* Read from source and target buffer. */ src = buffer2 + ii * bytes_per_line; } else { ii += n_lines1; if (ii >= 0) { /* Read from source only buffer. */ src = buffer1 + ii * bytes_per_line; } else { /* Read other color from source position. */ src = dst; source_color_idx = 2; } } /* Copy selected color values. */ SANE_Int step = color_48 ? 2 : 1; SANE_Int stride = 3 * step; for (unsigned int pos = 0; pos < bytes_per_line; pos += stride) { SANE_Int p1 = pos + step * source_color_idx; SANE_Int p2 = pos + step * color_idx; dst[p2] = src[p1]; if (color_48) { dst[p2 + 1] = src[p1 + 1]; } } } } /****************************************************************************** * Append all lines from buffer2 to the end of buffer1 and keep max_lines last * lines. * buffer2 : Source line buffer. * buffer1 : Target line buffer. Length will be adjusted. * max_lines : Max number of lines in buffer1. */ static void append_and_move_lines(SANE_Byte * buffer2, SANE_Int n_lines2, SANE_Byte * buffer1, unsigned int * n_lines1_ptr, SANE_Int max_lines, unsigned int bytes_per_line) { DBG (DBG_proc, "%s\n", __func__); SANE_Int rest1 = max_lines - *n_lines1_ptr; SANE_Int copy2 = MY_MIN(n_lines2, max_lines); if (copy2 > rest1) { SANE_Int shift1 = *n_lines1_ptr + copy2 - max_lines; SANE_Int blen = MY_MIN(max_lines - shift1, (SANE_Int) *n_lines1_ptr); SANE_Byte * pdst = buffer1; SANE_Byte * psrc = pdst + shift1 * bytes_per_line; for (SANE_Int i = 0; i < blen; ++i) { memcpy (pdst, psrc, bytes_per_line); pdst += bytes_per_line; psrc += bytes_per_line; } *n_lines1_ptr -= shift1; } SANE_Int n_copied = copy_n_last_lines(buffer2, n_lines2, buffer1 + *n_lines1_ptr * bytes_per_line, copy2, bytes_per_line); *n_lines1_ptr += n_copied; } /******************************************************************************/ SANE_Status sane_read (SANE_Handle handle, SANE_Byte * data, SANE_Int max_length, SANE_Int * length) { struct hp5590_scanner *scanner = handle; SANE_Status ret; DBG (DBG_proc, "%s, length %u, left %llu\n", __func__, max_length, scanner->transferred_image_size); if (!length) { scanner->scanning = SANE_FALSE; return SANE_STATUS_INVAL; } if (scanner->transferred_image_size == 0) { *length = 0; DBG (DBG_verbose, "Setting scan count\n"); ret = hp5590_inc_scan_count (scanner->dn, scanner->proto_flags); if (ret != SANE_STATUS_GOOD) return ret; /* Don't free bulk read state, some bytes could be left * for the next images from ADF */ return SANE_STATUS_EOF; } if (!scanner->bulk_read_state) { ret = hp5590_low_init_bulk_read_state (&scanner->bulk_read_state); if (ret != SANE_STATUS_GOOD) { scanner->scanning = SANE_FALSE; return ret; } } unsigned int bytes_per_line; ret = calc_image_params (scanner, NULL, NULL, &bytes_per_line, NULL, NULL); if (ret != SANE_STATUS_GOOD) return ret; ret = sane_read_internal(scanner, data, max_length, length, bytes_per_line); if ((ret == SANE_STATUS_GOOD) && (scanner->dpi == 2400) && ((scanner->depth == DEPTH_COLOR_48) || (scanner->depth == DEPTH_COLOR_24))) { /* Correct color shift bug for 2400 dpi. * Note: 2400 dpi only works in color mode. Grey mode and lineart seem to * fail. * Align colors by shifting B channel by 48 lines and G channel by 24 * lines. */ const SANE_Int offset_max = 48; const SANE_Int offset_part = 24; SANE_Bool color_48 = (scanner->depth == DEPTH_COLOR_48); if (! scanner->color_shift_line_buffer1) { scanner->color_shift_buffered_lines1 = 0; scanner->color_shift_line_buffer1 = malloc (bytes_per_line * offset_max); if (! scanner->color_shift_line_buffer1) return SANE_STATUS_NO_MEM; memset (scanner->color_shift_line_buffer1, 0, bytes_per_line * offset_max); } if (! scanner->color_shift_line_buffer2) { scanner->color_shift_buffered_lines2 = 0; scanner->color_shift_line_buffer2 = malloc (bytes_per_line * offset_max); if (! scanner->color_shift_line_buffer2) return SANE_STATUS_NO_MEM; memset (scanner->color_shift_line_buffer2, 0, bytes_per_line * offset_max); } SANE_Int n_lines = *length / bytes_per_line; scanner->color_shift_buffered_lines2 = MY_MIN(n_lines, offset_max); copy_n_last_lines(data, n_lines, scanner->color_shift_line_buffer2, scanner->color_shift_buffered_lines2, bytes_per_line); shift_color_lines(data, n_lines, scanner->color_shift_line_buffer1, scanner->color_shift_buffered_lines1, 1, offset_part, color_48, bytes_per_line); shift_color_lines(data, n_lines, scanner->color_shift_line_buffer1, scanner->color_shift_buffered_lines1, 0, offset_max, color_48, bytes_per_line); append_and_move_lines(scanner->color_shift_line_buffer2, scanner->color_shift_buffered_lines2, scanner->color_shift_line_buffer1, &(scanner->color_shift_buffered_lines1), offset_max, bytes_per_line); } return ret; } /******************************************************************************/ void sane_cancel (SANE_Handle handle) { struct hp5590_scanner *scanner = handle; SANE_Status ret; DBG (DBG_proc, "%s\n", __func__); scanner->scanning = SANE_FALSE; if (scanner->dn < 0) return; hp5590_low_free_bulk_read_state (&scanner->bulk_read_state); ret = hp5590_stop_scan (scanner->dn, scanner->proto_flags); if (ret != SANE_STATUS_GOOD) return; } /******************************************************************************/ SANE_Status sane_set_io_mode (SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking) { DBG (DBG_proc, "%s\n", __func__); return SANE_STATUS_UNSUPPORTED; } /******************************************************************************/ SANE_Status sane_get_select_fd (SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ * fd) { DBG (DBG_proc, "%s\n", __func__); return SANE_STATUS_UNSUPPORTED; } /* vim: sw=2 ts=8 */ backends-1.3.0/backend/hp5590_cmds.c000066400000000000000000002227641456256263500170130ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2007 Ilia Sotnikov HP ScanJet 4570c support by Markham Thomas ADF page detection and high DPI fixes by Bernard Badeer scanbd integration by Damiano Scaramuzza and Bernard Badeer 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, see . 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 is part of a SANE backend for HP ScanJet 4500C/4570C/5500C/5550C/5590/7650 Scanners */ #include "../include/sane/config.h" #ifdef HAVE_UNISTD_H # include #endif /* HAVE_UNISTD_H */ #ifdef HAVE_NETINET_IN_H # include #endif /* HAVE_NETINET_IN_H */ #include #include #include "../include/sane/sanei_debug.h" #include "../include/_stdint.h" #include "hp5590_low.h" #include "hp5590_cmds.h" static const struct hp5590_model hp5590_models[] = { { SCANNER_HP4570, 0x03f0, 0x1305, "SILITEKIElwood", "4570C/5500C", "Workgroup scanner", PF_NONE }, { SCANNER_HP5550, 0x03f0, 0x1205, "SILITEKIPenguin", "4500C/5550C", "Workgroup scanner", PF_NO_USB_IN_USB_ACK /* These devices need no * acknowledgment after USB-in-USB * commands */ }, { SCANNER_HP5590, 0x03f0, 0x1705, "SILITEKIPenguin", "5590", "Workgroup scanner", PF_NONE }, { SCANNER_HP7650, 0x03f0, 0x1805, "SILITEKIArnold", "7650", "Document scanner", PF_NONE } }; /* Debug levels */ #define DBG_err 0 #define DBG_proc 10 #define DBG_verbose 20 #define DBG_cmds 40 #define hp5590_cmds_assert(exp) if(!(exp)) { \ DBG (DBG_err, "Assertion '%s' failed at %s:%u\n", #exp, __FILE__, __LINE__);\ return SANE_STATUS_INVAL; \ } #define WAKEUP_TIMEOUT 90 /* First byte of init (0x12 cmd) response */ #define INIT_FLAG_TMA 1 << 0 #define INIT_FLAG_ADF 1 << 1 #define INIT_FLAG_LCD 1 << 3 /* Power (0x24 cmd) */ #define POWER_FLAG_ON 1 << 1 /* ADF (0x03 cmd) */ #define FLAG_ADF_EMPTY 1 << 1 #define PART_NUMBER_LEN 10 #define REVERSE_MAP_LEN 128 * 1024 / sizeof(uint16_t) #define FORWARD_MAP_LEN 128 * 1024 / sizeof(uint16_t) /* Button flags */ /* From left to right */ /* 1: Power * 1: Scan * 2: Collect * 3: File * 4: Email * 5: Copy * 6,7: Up/down * 8: Mode * 9: Cancel */ #define BUTTON_FLAG_EMAIL 1 << 15 #define BUTTON_FLAG_COPY 1 << 14 #define BUTTON_FLAG_DOWN 1 << 13 #define BUTTON_FLAG_MODE 1 << 12 #define BUTTON_FLAG_UP 1 << 11 #define BUTTON_FLAG_FILE 1 << 9 #define BUTTON_FLAG_POWER 1 << 5 #define BUTTON_FLAG_SCAN 1 << 2 #define BUTTON_FLAG_COLLECT 1 << 1 #define BUTTON_FLAG_CANCEL 1 << 0 #define CMD_INIT 0x0012 #define CMD_EEPROM_ADDR 0x00f2 #define CMD_EEPROM_READ 0x0bf0 #define CMD_EEPROM_WRITE 0x0bf1 #define CMD_DATA_STATUS 0x0001 #define CMD_STOP_SCAN 0x011b #define CMD_CONTROL_LAMP 0x00c0 #define CMD_POWER_STATUS 0x0024 #define CMD_SELECT_SOURCE 0x00d6 #define CMD_MISC_STATUS 0x0003 #define CMD_LOCK_UNLOCK 0x0000 #define CMD_SET_BASE_DPI 0x0015 #define CMD_SET_COLOR_MAP 0x0240 #define CMD_SET_SCAN_PARAMS 0x0025 #define CMD_GET_IMAGE_PARAMS 0x0034 #define CMD_START_SCAN 0x051b #define CMD_BUTTON_STATUS 0x0020 #define CMD_LCD_STATUS 0x0021 struct init_resp { uint8_t flags; /* bit 0 - TMA, bit 1 - ADF, bit 3 - LCD present */ uint8_t id[15]; /* SILITEKPenguin */ uint8_t pad1[9]; /* 00 00 00 00 00 00 00 00 00 */ uint8_t version[5]; /* 0.0.67 */ uint16_t max_dpi_x; /* 09 60 = 2400 */ uint16_t max_dpi_y; /* 09 60 = 2400 */ uint16_t max_pixels_x; /* 4F B0 = 20400 (20400 / 2400 = 8.5") */ uint16_t max_pixels_y; /* 6D E0 = 28128 (28128 / 2400 = 11.72") */ uint8_t pad2[8]; /* 00 00 00 00 00 00 00 00 */ uint16_t motor_param_normal; /* 00 64 = 100 */ uint16_t motor_param_max; /* 03 E8 = 1000 */ } __attribute__ ((packed)); struct power_resp { uint8_t flags; uint16_t unk1; } __attribute__ ((packed)); /* * 215.9 mm x 297.2 mm * 8.5" x 11.72" * * 50 : 425.00 x 586.00 * 75 : 637.50 x 879.50 * 100 : 850.00 x 1172.00 * 150 : 1275.00 x 1758.00 (base DPI) * 200 : 1700.00 x 2344.00 * 300 : 2550.00 x 3516.00 (base DPI) * 400 : 3400.00 x 4688.00 * 600 : 5100.00 x 7032.00 (base DPI) */ #define SCAN_PARAMS_SOURCE_TMA_NEGATIVES 1 << 0 #define SCAN_PARAMS_SOURCE_TMA_SLIDES 1 << 1 #define SCAN_PARAMS_SOURCE_ADF 1 << 2 #define SCAN_PARAMS_SOURCE_FLATBED 1 << 3 #define SCAN_PARAMS_SOURCE_SIMPLEX 1 << 4 #define SCAN_PARAMS_SOURCE_DUPLEX 1 << 6 struct scan_params { uint8_t source; /* * TMA Negatives : 11 = 17 * TMA Slides : 12 = 18 * ADF : 14 = 20 * Flatbed : 18 = 24 * ADF Duplex : 54 = 84 */ uint16_t dpi_x; /* * 50 : 00 64 = 100 * 75 : 00 64 = 100 * 100 : 00 64 = 100 * 150 : 00 c8 = 200 * 200 : 00 c8 = 200 * 300 : 01 2c = 300 * 400 : 02 58 = 600 * 600 : 02 58 = 600 * 1200 : 04 b0 = 1200 */ uint16_t dpi_y; /* * 50 : 00 64 = 100 * 75 : 00 64 = 100 * 100 : 00 64 = 100 * 150 : 00 c8 = 200 * 200 : 00 c8 = 200 * 300 : 01 2c = 300 * 400 : 02 58 = 600 * 600 : 02 58 = 600 * 1200 : 04 b0 = 1200 */ uint16_t top_x; /* * pixels * (Base DPI / current DPI) * 00 00, 01 6e = 366 (x = 425 - 302 = 123) * 04 b0 = 1200 (x = 425 - 24 = 401) */ uint16_t top_y; /* * pixels * (Base DPI / current DPI) * 00 00, 06 99 = 1689 (y = 585 - 21 = 564) */ uint16_t size_x; /* X pixels in Base DPI (CMD 15) * 50 : 04f8 = 1272 ; 150 * 75 : 04f8 = 1272 ; 150 * 100 : 04f8 = 1272 ; 150 * 100 TMA : 00fc = 252 ; 150 * 150 : 09f6 = 2550, 09f0 = 2544, 09f4 = 2548 ; 300 * 200 : 09f0 = 2544, 09f6 = 2550, 09f6 = 2550 ; 300 * 300 : 09f6 = 2550, 09f0 = 2544, 09f4 = 2548 ; 300 * 300 TMA : 01fc = 508 ; 300 * 400 : 13ec = 5100 ; 600 * 600 : 13e8 = 5096, 13ec = 5100 ,13ec = 5100 ; 600 * 1200 : 27a8 = 10152 ; 1200 */ uint16_t size_y; /* Y pixels in Base DPI (CMD 15) * 50 : 06db = 1755 ; 150 * 75 : 06da = 1754 ; 150 * 100 : 06db = 1755 ; 150 * 100 TMA : 0384 = 900 ; 150 * 150 : 0db6 = 3510 ; 300 * 200 : 0db6 = 3510 ; 300 * 300 : 0db6 = 3510 ; 300 * 300 TMA : 0708 = 1800 ; 300 * 400 : 1b6c = 7020 ; 600 * 600 : 1b6c = 7020 ; 600 * 1200 : 36d8 = 14040 ; 1200 */ uint16_t unk1; /* 00 80 */ uint16_t bw_gray_flag; /* * 00 40 - bw (ntsc gray)/gray, * 00 20 - bw (by green band), * 00 10 - bw (by red band), * 00 30 - bw (by blue band), * 00 00 - color */ uint8_t pixel_bits; /* * bw 50/75/150/400 : 08 = 8 * bw 100/200/300/600/1200 : 01 = 1 * gray 50/75/100/150/200/400/600 : 08 = 8 * color 24 bit 50/75/100/150/200/400/600 : 18 = 24 * color 48 bit 100/200 : 30 = 48 */ uint16_t flags; /* * 50/75/100/150/200/300 : e8 40 = 59456 * 400/600/1200 : c8 40 = 51264 */ uint16_t motor_param1; /* * 00 64 = 100 */ uint16_t motor_param2; /* * 00 64 = 100 - ADF, Flatbed, TMA slides * 00 c8 = 200 - TMA Negatives */ uint16_t motor_param3; /* * 00 64 = 100 - ADF, Flatbed, TMA slides * 01 90 = 400 - TMA negatives */ uint32_t pad1; /* 00 00 00 00 */ uint16_t pad2; /* 00 00 */ uint8_t mode; /* 00 - normal scan, 04 - preview scan */ uint16_t pad3; /* 00 00 */ uint16_t line_width; /* Based on current .dpi_x * bw 50 : 03 50 = 848 * gray 50 : 03 50 = 848 * color 50 : 09 f0 = 2544 (3 * gray) * * bw 75 : 03 50 = 848 * gray 75 : 03 50 = 848 * color 75 : 09 f0 = 2544 (3 * gray) * * bw 100 : 00 6a = 106 * gray 100 : 03 50 = 848 (8 * bw) * color 100(24) : 09 f0 = 2544 (3 * gray) * color 100(48) : 13 e0 = 5088 (2 * color 24) * color 100(48) TMA : 03 f0 = 1008 * * bw 150 : 06 a4 = 1700 * gray 150 : 06 a4 = 1700 * color 150 : 13 ec = 5100 (3 * gray) * * bw 200 : 00 d4 = 212 * gray 200 : 06 a4 = 1700 (8 * bw) * color 200(24) : 13 ec = 5100 (3 * gray) * color 200(48) : 27 a8 = 10152 * * bw 300 : 01 3e = 318 * gray 300 : 09 f4 = 2548 (8 * bw) * color 300 : 1d dc = 7644 (3 * gray) * color 300(48) TMA : 0b e8 = 3048 * * bw 400 : 13 ec = 5100 * gray 400 : 13 ec = 5100 * color 400 : 3b c4 = 15300 (3 * gray) * * bw 600 : 02 7d = 637 * gray 600 : 13 ec = 5100 (8 * bw) * color 600 : 3b c4 = 15300 (3 * gray) * * bw 1200 : 04 f5 = 1269 */ } __attribute__ ((packed)); struct image_params { uint8_t signature; /* c0 */ uint8_t pad1; /* 00 */ uint32_t image_size; /* * bw 50 : 00 0f 23 a0 = 992 160 * gray 50 : 00 0f 23 a0 = 992 160 * color 50 : 00 2d 6a e0 = 2 976 480 * * bw 75 : 00 0f 20 50 = 991 312 * gray 75 : 00 0f 20 50 = 991 312 * color 75 : 00 2d 60 f0 = 2 973 936 * color 75(48) : 00 5a 86 40 = 5 932 608 * * bw 100 : 00 01 e4 74 = 124 020 * gray 100 : 00 0f 23 a0 = 992 160 * color 100 : 00 2d 6a e0 = 2 976 480 * color 100(48) : 00 5a 68 10 = 5 924 880 * color 100(48), preview: 00 5a d5 c0 = 5 952 960 * * bw 150 : 00 3c b3 10 = 3 978 000 * gray 150 : 00 3c b3 10 = 3 978 000 * color 150 : 00 b6 19 30 = 11 934 000 * color 150(48) : 01 6a 7b a0 = 23 755 680 * * bw 200 : 00 07 91 d0 = 496 080 * gray 200 : 00 3c b3 10 = 3 978 000 * color 200 : 00 b6 19 30 = 11 934 000 * color 200(48) : 01 6a f3 a0 = 23 786 400 * * bw 300 : 00 11 08 14 = 1 116 180 * gray 300 : 00 88 77 78 = 8 943 480 * color 300 : 01 99 66 68 = 26 830 440 * * bw 400 : 02 22 4b 90 = 35 802 000 * gray 400 : 02 22 4b 90 = 35 802 000 * color 400 : 06 66 e2 b0 = 107 406 000 * * bw 600 : 00 44 3b bc = 4 471 740 * gray 600 : 02 22 4b 90 = 35 802 000 * color 600 : 06 66 e2 b0 = 107 406 000 */ uint16_t pad2; /* 00 00 */ uint16_t line_width; uint16_t real_size_y; uint32_t pad3; /* 00 00 00 00 */ } __attribute__ ((packed)); struct lamp_state { uint8_t unk1; /* 02 */ uint8_t flag; /* 01 on start, 02 - TMA, 03 - all other */ uint16_t turnoff_time; /* 0a 0a, 03 36, 0f 36 */ } __attribute__ ((packed)); struct color_map { uint8_t color1[6]; /* 00 00 00 00 01 00 */ uint8_t color2[6]; /* 00 00 00 00 01 00 */ uint8_t color3[6]; /* 00 00 00 00 01 00 */ } __attribute__ ((packed)); struct reg_03 { uint8_t unk1; /* 0x0b - ADF ready, 0x03 - not */ uint8_t unk2; /* 0x80 */ uint8_t adf_flags; /* 0x01 - ADF ready when selected, 0x02 - not */ } __attribute__ ((packed)); /******************************************************************************/ static SANE_Status hp5590_model_def (enum hp_scanner_types scanner_type, const struct hp5590_model ** model) { unsigned int i; hp5590_cmds_assert (model != NULL); for (i = 0; i < sizeof (hp5590_models) / sizeof (struct hp5590_model); i++) { if (hp5590_models[i].scanner_type == scanner_type) { *model = &hp5590_models[i]; return SANE_STATUS_GOOD; } } return SANE_STATUS_INVAL; } /******************************************************************************/ static SANE_Status hp5590_vendor_product_id (enum hp_scanner_types scanner_type, SANE_Word * vendor_id, SANE_Word * product_id) { const struct hp5590_model *model; SANE_Status ret; hp5590_cmds_assert (vendor_id != NULL); hp5590_cmds_assert (product_id != NULL); ret = hp5590_model_def (scanner_type, &model); if (ret != SANE_STATUS_GOOD) return ret; *vendor_id = model->usb_vendor_id; *product_id = model->usb_product_id; return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status hp5590_init_scanner (SANE_Int dn, enum proto_flags proto_flags, struct scanner_info ** info, enum hp_scanner_types scanner_type) { struct init_resp init_resp; char id_buf[sizeof (init_resp.id) + 1]; char ver_buf[sizeof (init_resp.version) + 1]; SANE_Status ret; const struct hp5590_model *scanner_model; /* * 0A 53 49 4C 49 54 45 4B 49 50 65 6E 67 75 69 6E .SILITEKIPenguin * 00 00 00 00 00 00 00 00 00 30 2E 30 36 37 09 60 .........0.067.. * 09 60 4F B0 6D E0 00 00 00 00 00 00 00 00 00 64 ..O.m..........d * 03 E8 .. */ DBG (DBG_proc, "%s\n", __func__); hp5590_cmds_assert (sizeof (init_resp) == 50); /* Init scanner */ ret = hp5590_cmd (dn, proto_flags, CMD_IN | CMD_VERIFY, CMD_INIT, (unsigned char *) &init_resp, sizeof (init_resp), CORE_NONE); if (ret != SANE_STATUS_GOOD) return ret; memset (id_buf, 0, sizeof (id_buf)); memcpy (id_buf, init_resp.id, sizeof (id_buf) - 1); scanner_model = NULL; if (scanner_type != SCANNER_NONE) { unsigned int i; for (i = 0; i < sizeof (hp5590_models) / sizeof (struct hp5590_model); i++) { if (hp5590_models[i].scanner_type == scanner_type) { if (strcmp (id_buf, hp5590_models[i].vendor_id) != 0) { DBG (DBG_err, "%s: Vendor id mismatch for scanner HP%s - " "required '%s', got '%s'\n", __func__, hp5590_models[i].model, hp5590_models[i].vendor_id, id_buf); return SANE_STATUS_INVAL; } scanner_model = &hp5590_models[i]; break; } } hp5590_cmds_assert (scanner_model != NULL); } if (scanner_model) { DBG (DBG_cmds, "HP%s flags (0x%02x)\n", scanner_model->model, init_resp.flags); DBG (DBG_cmds, "HP%s flags: ADF %s, TMA %s, LCD %s\n", scanner_model->model, init_resp.flags & INIT_FLAG_ADF ? "yes" : "no", init_resp.flags & INIT_FLAG_TMA ? "yes" : "no", init_resp.flags & INIT_FLAG_LCD ? "yes" : "no"); memset (ver_buf, 0, sizeof (ver_buf)); memcpy (ver_buf, init_resp.version, sizeof (ver_buf) - 1); DBG (DBG_cmds, "HP%s firmware version: %s\n", scanner_model->model, ver_buf); DBG (DBG_cmds, "HP%s max resolution X: %u DPI\n", scanner_model->model, ntohs (init_resp.max_dpi_x)); DBG (DBG_cmds, "HP%s max resolution Y: %u DPI\n", scanner_model->model, ntohs (init_resp.max_dpi_y)); DBG (DBG_cmds, "HP%s max pixels X: %u\n", scanner_model->model, ntohs (init_resp.max_pixels_x)); DBG (DBG_cmds, "HP%s max pixels Y: %u\n", scanner_model->model, ntohs (init_resp.max_pixels_y)); DBG (DBG_cmds, "HP%s max size X: %.3f inches\n", scanner_model->model, ntohs (init_resp.max_pixels_x) * 1.0 / ntohs (init_resp.max_dpi_x)); DBG (DBG_cmds, "HP%s max size Y: %.3f inches\n", scanner_model->model, ntohs (init_resp.max_pixels_y) * 1.0 / ntohs (init_resp.max_dpi_y)); DBG (DBG_cmds, "HP%s normal motor param: %u, max motor param: %u\n", scanner_model->model, ntohs (init_resp.motor_param_normal), ntohs (init_resp.motor_param_max)); } if (info) { *info = malloc (sizeof (struct scanner_info)); if (!*info) { DBG (DBG_err, "Memory allocation failed\n"); return SANE_STATUS_NO_MEM; } memset (*info, 0, sizeof (struct scanner_info)); (*info)->max_dpi_x = ntohs (init_resp.max_dpi_x); (*info)->max_dpi_y = ntohs (init_resp.max_dpi_y); (*info)->max_pixels_x = ntohs (init_resp.max_pixels_x) - 1; (*info)->max_pixels_y = ntohs (init_resp.max_pixels_y) + 1; (*info)->max_size_x = (*info)->max_pixels_x * 1.0 / (*info)->max_dpi_x; (*info)->max_size_y = (*info)->max_pixels_y * 1.0 / (*info)->max_dpi_y; (*info)->features = FEATURE_NONE; if (init_resp.flags & INIT_FLAG_LCD) (*info)->features |= FEATURE_LCD; if (init_resp.flags & INIT_FLAG_ADF) (*info)->features |= FEATURE_ADF; if (init_resp.flags & INIT_FLAG_TMA) (*info)->features |= FEATURE_TMA; if (scanner_model) { (*info)->model = scanner_model->model; (*info)->kind = scanner_model->kind; } } ret = hp5590_get_status (dn, proto_flags); if (ret != SANE_STATUS_GOOD) { DBG (DBG_err, "%s: scanner reports non-zero status: %s\n", __func__, sane_strstatus (ret)); return ret; } DBG (DBG_cmds, "%s: scanner status OK\n", __func__); return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status hp5590_read_eeprom (SANE_Int dn, enum proto_flags proto_flags, unsigned int addr, unsigned char *data, unsigned int size) { uint8_t eeprom_addr = addr; SANE_Status ret; hp5590_cmds_assert (data != NULL); hp5590_cmds_assert (sizeof (eeprom_addr) == 1); DBG (DBG_proc, "%s\n", __func__); DBG (DBG_proc, "Reading EEPROM: addr %04x, size %u\n", addr, size); ret = hp5590_cmd (dn, proto_flags, CMD_VERIFY, CMD_EEPROM_ADDR, (unsigned char *) &eeprom_addr, sizeof (eeprom_addr), CORE_NONE); if (ret != SANE_STATUS_GOOD) return ret; ret = hp5590_cmd (dn, proto_flags, CMD_IN | CMD_VERIFY, CMD_EEPROM_READ, data, size, CORE_NONE); if (ret != SANE_STATUS_GOOD) return ret; return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status hp5590_write_eeprom (SANE_Int dn, enum proto_flags proto_flags, unsigned int addr, unsigned char *data, unsigned int size) { uint8_t eeprom_addr = addr; SANE_Status ret; hp5590_cmds_assert (data != NULL); hp5590_cmds_assert (sizeof (eeprom_addr) == 1); DBG (DBG_proc, "%s\n", __func__); DBG (DBG_proc, "Writing EEPROM: addr %04x, size: %u\n", addr, size); ret = hp5590_cmd (dn, proto_flags, CMD_VERIFY, CMD_EEPROM_ADDR, (unsigned char *) &eeprom_addr, sizeof (eeprom_addr), CORE_NONE); if (ret != SANE_STATUS_GOOD) return ret; ret = hp5590_cmd (dn, proto_flags, CMD_VERIFY, CMD_EEPROM_WRITE, data, size, CORE_DATA); if (ret != SANE_STATUS_GOOD) return ret; return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status hp5590_read_scan_count (SANE_Int dn, enum proto_flags proto_flags, unsigned int *count) { uint32_t scan_count; SANE_Status ret; hp5590_cmds_assert (count != NULL); hp5590_cmds_assert (sizeof (scan_count) == 4); DBG (DBG_proc, "%s\n", __func__); DBG (DBG_proc, "Reading scan count\n"); ret = hp5590_read_eeprom (dn, proto_flags, 0x00, (unsigned char *) &scan_count, sizeof (scan_count)); if (ret != SANE_STATUS_GOOD) return ret; /* Host order */ *count = scan_count; DBG (DBG_proc, "Scan count %u\n", *count); return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status hp5590_inc_scan_count (SANE_Int dn, enum proto_flags proto_flags) { uint32_t scan_count; uint32_t count; uint32_t new_count; SANE_Status ret; DBG (DBG_proc, "%s\n", __func__); hp5590_cmds_assert (sizeof (scan_count) == 4); ret = hp5590_read_scan_count (dn, proto_flags, &count); if (ret != SANE_STATUS_GOOD) return ret; scan_count = ++count; DBG (DBG_verbose, "Scan count = %u\n", scan_count); ret = hp5590_write_eeprom (dn, proto_flags, 0x00, (unsigned char *) &scan_count, sizeof (scan_count)); if (ret != SANE_STATUS_GOOD) return ret; /* Verify its setting */ ret = hp5590_read_scan_count (dn, proto_flags, &new_count); if (ret != SANE_STATUS_GOOD) return ret; if (count != new_count) { DBG (DBG_err, "Scan count wasn't set\n"); return SANE_STATUS_IO_ERROR; } return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status hp5590_read_max_scan_count (SANE_Int dn, enum proto_flags proto_flags, unsigned int *max_count) { uint8_t max_scan_count[3]; SANE_Status ret; hp5590_cmds_assert (max_count != NULL); hp5590_cmds_assert (sizeof (max_scan_count) == 3); DBG (DBG_proc, "%s\n", __func__); DBG (DBG_proc, "Reading max scan count\n"); ret = hp5590_read_eeprom (dn, proto_flags, 0x10, (unsigned char *) max_scan_count, sizeof (max_scan_count)); if (ret != SANE_STATUS_GOOD) return ret; /* Host order */ *max_count = 0; memcpy (max_count, max_scan_count, sizeof (max_scan_count)); DBG (DBG_proc, "Max scan count %u\n", *max_count); return SANE_STATUS_GOOD; } /*************************************************************************** * * EEPROM contents: * * 0000: 6A 11 00 00 FF FF FF FF FF FF FF FF 09 0E 0F 00 j............... * 0010: 0C 13 0F 00 00 3A 00 FF FF FF 4E 35 39 45 54 52 ..........N59ETR * 0020: 31 52 4D 00 FF FF 00 16 00 0A 00 0D 00 11 00 10 1RM............. * 0030: FF FF FF FF FF FF FF FF FF 00 FF FF FF FF FF FF ................ * 0040: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................ * 0050: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................ * 0060: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................ * 0070: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................ * 0080: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................ * 0090: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................ * 00A0: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................ * 00B0: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................ * 00C0: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................ * 00D0: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................ * 00E0: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ................ * 00F0: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ............... * * Addr 0x00, len: 0x04 - scan count (little-endian) * Addr 0x1A, len: 0x0A - part number (w/o first 'CN' letters) * Addr 0x10, len: 0x03 - max scan count (little-endian) (0C 13 0F) * */ /******************************************************************************/ static __sane_unused__ SANE_Status hp5590_read_eeprom_all_cmd (SANE_Int dn, enum proto_flags proto_flags) { uint8_t eeprom[255]; SANE_Status ret; DBG (DBG_proc, "%s\n", __func__); ret = hp5590_read_eeprom (dn, proto_flags, 0x00, (unsigned char *) eeprom, sizeof (eeprom)); if (ret != SANE_STATUS_GOOD) return ret; DBG (DBG_verbose, "hp5590_read_eeprom_all_cmd: rc = %d\n", ret); { const int LEN = 4096; char buf[LEN]; char* p = buf; for (size_t i = 0; i < sizeof(eeprom); ++i) { if (i % 16 == 0) { int n = sprintf(p, "\n%04x ", (int)i); if (n < 0) { break; } p += n; } int n = sprintf(p, " %02x", eeprom[i]); if (n < 0 ) { break; } p += n; } *p = '\0'; DBG (DBG_verbose, "dump:%s\n", buf); } return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status hp5590_read_part_number (SANE_Int dn, enum proto_flags proto_flags) { unsigned int part_number_len = PART_NUMBER_LEN; unsigned char part_number[PART_NUMBER_LEN + 1]; SANE_Status ret; DBG (DBG_proc, "%s\n", __func__); memset (part_number, 0, sizeof (part_number)); ret = hp5590_read_eeprom (dn, proto_flags, 0x1a, part_number, part_number_len); if (ret != SANE_STATUS_GOOD) return ret; DBG (DBG_cmds, "Part number: '%s'\n", part_number); return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status hp5590_is_data_available (SANE_Int dn, enum proto_flags proto_flags) { uint8_t data_status; SANE_Status ret; SANE_Bool data_available; DBG (DBG_proc, "%s\n", __func__); hp5590_cmds_assert (sizeof (data_status) == 1); data_available = SANE_FALSE; ret = hp5590_cmd (dn, proto_flags, CMD_IN | CMD_VERIFY, CMD_DATA_STATUS, (unsigned char *) &data_status, sizeof (data_status), CORE_DATA); if (ret != SANE_STATUS_GOOD) return ret; DBG (DBG_cmds, "%s: Data status: %02x\n", __func__, data_status); if (data_status == 0x40) data_available = SANE_TRUE; DBG (DBG_cmds, "%s: Data is %s\n", __func__, data_available == SANE_TRUE ? "available" : "not available"); return data_available == SANE_TRUE ? SANE_STATUS_GOOD : SANE_STATUS_NO_DOCS; } /******************************************************************************/ static SANE_Status hp5590_stop_scan (SANE_Int dn, enum proto_flags proto_flags) { uint8_t reg_011b = 0x40; SANE_Status ret; DBG (DBG_proc, "%s\n", __func__); hp5590_cmds_assert (sizeof (reg_011b) == 1); ret = hp5590_cmd (dn, proto_flags, CMD_VERIFY, CMD_STOP_SCAN, (unsigned char *) ®_011b, sizeof (reg_011b), CORE_NONE); if (ret != SANE_STATUS_GOOD) return ret; /* FIXME */ return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status hp5590_turnon_lamp (SANE_Int dn, enum proto_flags proto_flags, enum hp5590_lamp_state state) { struct lamp_state lamp_state; SANE_Status ret; DBG (DBG_proc, "%s\n", __func__); hp5590_cmds_assert (sizeof (lamp_state) == 4); if (state == LAMP_STATE_TURNON) { /* Turn on lamp */ lamp_state.unk1 = 0x02; lamp_state.flag = 0x01; lamp_state.turnoff_time = htons (0x0a0a); DBG (DBG_cmds, "%s: turning lamp on\n", __func__); } if (state == LAMP_STATE_TURNOFF) { /* Turn off lamp */ lamp_state.unk1 = 0x02; lamp_state.flag = 0x02; lamp_state.turnoff_time = htons (0x0a0a); DBG (DBG_cmds, "%s: turning lamp off\n", __func__); } if (state == LAMP_STATE_SET_TURNOFF_TIME) { /* Turn on lamp */ lamp_state.unk1 = 0x02; lamp_state.flag = 0x03; lamp_state.turnoff_time = htons (0x0336); DBG (DBG_cmds, "%s: setting turnoff time\n", __func__); } if (state == LAMP_STATE_SET_TURNOFF_TIME_LONG) { /* Turn on lamp */ lamp_state.unk1 = 0x02; lamp_state.flag = 0x03; lamp_state.turnoff_time = htons (0x0f36); DBG (DBG_cmds, "%s: setting long turnoff time\n", __func__); } ret = hp5590_cmd (dn, proto_flags, CMD_VERIFY, CMD_CONTROL_LAMP, (unsigned char *) &lamp_state, sizeof (lamp_state), CORE_DATA); if (ret != SANE_STATUS_GOOD) return ret; if (state == LAMP_STATE_TURNON) { ret = hp5590_init_scanner (dn, proto_flags, NULL, SCANNER_NONE); if (ret != SANE_STATUS_GOOD) return ret; } return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status hp5590_power_status (SANE_Int dn, enum proto_flags proto_flags) { struct power_resp power_resp; SANE_Status ret; DBG (DBG_proc, "%s\n", __func__); hp5590_cmds_assert (sizeof (power_resp) == 3); ret = hp5590_cmd (dn, proto_flags, CMD_IN | CMD_VERIFY, CMD_POWER_STATUS, (unsigned char *) &power_resp, sizeof (power_resp), CORE_NONE); if (ret != SANE_STATUS_GOOD) return ret; DBG (DBG_cmds, "Power status: %s (%02x)\n", power_resp.flags & POWER_FLAG_ON ? "on" : "off", power_resp.flags); if (!(power_resp.flags & POWER_FLAG_ON)) { DBG (DBG_cmds, "Turning lamp on\n"); ret = hp5590_turnon_lamp (dn, proto_flags, LAMP_STATE_TURNON); if (ret != SANE_STATUS_GOOD) return ret; } return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status hp5590_read_error_code (SANE_Int dn, enum proto_flags proto_flags, unsigned int *adf_flags) { struct reg_03 reg_03; SANE_Status ret; DBG (DBG_proc, "%s\n", __func__); hp5590_cmds_assert (sizeof (reg_03) == 3); hp5590_cmds_assert (adf_flags != NULL); memset (®_03, 0, sizeof (reg_03)); *adf_flags = 0; ret = hp5590_cmd (dn, proto_flags, CMD_IN, CMD_MISC_STATUS, (unsigned char *) ®_03, sizeof (reg_03), CORE_NONE); if (ret != SANE_STATUS_GOOD) return ret; DBG (DBG_cmds, "%s: adf_flags: %04x\n", __func__, reg_03.adf_flags); DBG (DBG_cmds, "%s: unk1 : %04x\n", __func__, reg_03.unk1); DBG (DBG_cmds, "%s: unk2 : %04x\n", __func__, reg_03.unk2); *adf_flags = reg_03.adf_flags; return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status hp5590_reset_scan_head (SANE_Int dn, enum proto_flags proto_flags) { SANE_Status ret; DBG (DBG_proc, "%s\n", __func__); ret = hp5590_turnon_lamp (dn, proto_flags, LAMP_STATE_TURNOFF); if (ret != SANE_STATUS_GOOD) return ret; usleep (100 * 1000); ret = hp5590_turnon_lamp (dn, proto_flags, LAMP_STATE_TURNON); if (ret != SANE_STATUS_GOOD) return ret; return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status hp5590_select_source_and_wakeup (SANE_Int dn, enum proto_flags proto_flags, enum scan_sources source, SANE_Bool extend_lamp_timeout) { uint8_t reg_d6 = 0x04; SANE_Status ret; unsigned int adf_flags; DBG (DBG_proc, "%s\n", __func__); hp5590_cmds_assert (sizeof (reg_d6) == 1); if (source == SOURCE_TMA_SLIDES || source == SOURCE_TMA_NEGATIVES) { ret = hp5590_turnon_lamp (dn, proto_flags, LAMP_STATE_TURNOFF); if (ret != SANE_STATUS_GOOD) return ret; } else { ret = hp5590_turnon_lamp (dn, proto_flags, extend_lamp_timeout == SANE_TRUE ? LAMP_STATE_SET_TURNOFF_TIME_LONG : LAMP_STATE_SET_TURNOFF_TIME); if (ret != SANE_STATUS_GOOD) return ret; } switch (source) { case SOURCE_ADF: case SOURCE_ADF_DUPLEX: reg_d6 = 0x03; break; case SOURCE_FLATBED: reg_d6 = 0x04; break; case SOURCE_TMA_SLIDES: reg_d6 = 0x02; break; case SOURCE_TMA_NEGATIVES: reg_d6 = 0x01; break; case SOURCE_NONE: DBG (DBG_err, "Scan source not selected\n"); return SANE_STATUS_INVAL; default: DBG (DBG_err, "Unknown scan source: %u\n", source); return SANE_STATUS_INVAL; } DBG (DBG_cmds, "Scan source: %u\n", reg_d6); ret = hp5590_cmd (dn, proto_flags, CMD_VERIFY, CMD_SELECT_SOURCE, (unsigned char *) ®_d6, sizeof (reg_d6), CORE_NONE); if (ret != SANE_STATUS_GOOD && ret != SANE_STATUS_DEVICE_BUSY) return ret; ret = hp5590_read_error_code (dn, proto_flags, &adf_flags); if (ret != SANE_STATUS_GOOD) return ret; if (adf_flags & FLAG_ADF_EMPTY) { DBG (DBG_cmds, "ADF empty\n"); return SANE_STATUS_NO_DOCS; } return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status hp5590_lock_unlock_scanner (SANE_Int dn, enum proto_flags proto_flags) { uint8_t reg_00 = 0x01; SANE_Status ret; unsigned int adf_flags; unsigned int waiting; DBG (DBG_proc, "%s\n", __func__); hp5590_cmds_assert (sizeof (reg_00) == 1); for (waiting = 0; waiting < WAKEUP_TIMEOUT; waiting++, sleep (1)) { ret = hp5590_cmd (dn, proto_flags, CMD_VERIFY, CMD_LOCK_UNLOCK, (unsigned char *) ®_00, sizeof (reg_00), CORE_NONE); if (ret == SANE_STATUS_GOOD) break; if (ret != SANE_STATUS_DEVICE_BUSY) return ret; DBG (DBG_cmds, "Waiting for scanner...\n"); ret = hp5590_read_error_code (dn, proto_flags, &adf_flags); if (ret != SANE_STATUS_GOOD) return ret; if (adf_flags & FLAG_ADF_EMPTY) { DBG (DBG_cmds, "ADF empty\n"); return SANE_STATUS_NO_DOCS; } } if (waiting == WAKEUP_TIMEOUT) return SANE_STATUS_DEVICE_BUSY; return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status hp5590_set_base_dpi (SANE_Int dn, enum proto_flags proto_flags, struct scanner_info *scanner_info, unsigned int base_dpi) { uint16_t _base_dpi; SANE_Status ret; DBG (DBG_proc, "%s\n", __func__); hp5590_cmds_assert (scanner_info != NULL); hp5590_cmds_assert (base_dpi != 0); hp5590_cmds_assert (sizeof (_base_dpi) == 2); if (base_dpi > scanner_info->max_dpi_x || base_dpi > scanner_info->max_dpi_y) { DBG (DBG_err, "Base DPI too large " "(given: %u, max X DPI: %u, max Y DPI: %u)\n", base_dpi, scanner_info->max_dpi_x, scanner_info->max_dpi_y); return SANE_STATUS_INVAL; } _base_dpi = htons (base_dpi); ret = hp5590_cmd (dn, proto_flags, CMD_VERIFY, CMD_SET_BASE_DPI, (unsigned char *) &_base_dpi, sizeof (_base_dpi), CORE_DATA); if (ret != SANE_STATUS_GOOD) return ret; return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status hp5590_set_color_map (SANE_Int dn, enum proto_flags proto_flags, unsigned int base_dpi) { struct color_map color_map; SANE_Status ret; DBG (DBG_proc, "%s\n", __func__); hp5590_cmds_assert (sizeof (color_map) == 18); hp5590_cmds_assert (base_dpi != 0); memset (&color_map, 0, sizeof (color_map)); if (base_dpi < 2400) { color_map.color1[4] = 0x01; color_map.color2[4] = 0x01; color_map.color3[4] = 0x01; } else { if (0) { /* Does not work with hp5590 and 2400 dpi. */ color_map.color1[2] = 0xff; color_map.color1[3] = 0x01; color_map.color1[4] = 0x04; color_map.color1[5] = 0x02; color_map.color2[2] = 0xff; color_map.color2[3] = 0x01; color_map.color2[4] = 0x04; color_map.color2[5] = 0x02; color_map.color3[2] = 0xff; color_map.color3[3] = 0x01; color_map.color3[4] = 0x04; color_map.color3[5] = 0x02; } else { /* Needs documentation. */ color_map.color1[2] = 0x00; color_map.color1[3] = 0x00; color_map.color1[4] = 0x01; color_map.color1[5] = 0x00; color_map.color2[2] = 0x00; color_map.color2[3] = 0x00; color_map.color2[4] = 0x01; color_map.color2[5] = 0x00; color_map.color3[2] = 0x00; color_map.color3[3] = 0x00; color_map.color3[4] = 0x01; color_map.color3[5] = 0x00; } } ret = hp5590_cmd (dn, proto_flags, CMD_VERIFY, CMD_SET_COLOR_MAP, (unsigned char *) &color_map, sizeof (color_map), CORE_DATA); if (ret != SANE_STATUS_GOOD) return ret; return SANE_STATUS_GOOD; } /****************************************************************************** * Calculate base DPI * Base DPI is what image dimensions are calculated on (top X,Y; bottom X,Y) * Calculated according the following rules: * Base DPI is 150 when 0 < DPI < 150; * Base DPI is 300 when 150 <= DPI <= 300; * Base DPI is 600 when 300 < DPI <= 600; * Base DPI is 1200 when 600 < DPI; */ static SANE_Status calc_base_dpi (unsigned int dpi, unsigned int *base_dpi) { DBG (DBG_proc, "%s\n", __func__); hp5590_cmds_assert (base_dpi != NULL); hp5590_cmds_assert (dpi != 0); *base_dpi = 0; if (dpi < 150) { *base_dpi = 150; return SANE_STATUS_GOOD; } if (dpi >= 150 && dpi <= 300) { *base_dpi = 300; return SANE_STATUS_GOOD; } if (dpi > 300 && dpi <= 600) { *base_dpi = 600; return SANE_STATUS_GOOD; } if (dpi > 600 && dpi <= 1200) { *base_dpi = 1200; return SANE_STATUS_GOOD; } if (dpi > 1200 && dpi <= 2400) { *base_dpi = 2400; return SANE_STATUS_GOOD; } DBG (DBG_err, "Error calculating base DPI (given DPI: %u)\n", dpi); return SANE_STATUS_INVAL; } /******************************************************************************/ static SANE_Status calc_scanner_dpi (unsigned int dpi, unsigned int *scanner_dpi) { DBG (DBG_proc, "%s\n", __func__); hp5590_cmds_assert (scanner_dpi != NULL); hp5590_cmds_assert (dpi != 0); if (dpi <= 100) { *scanner_dpi = 100; return SANE_STATUS_GOOD; } if (dpi > 100 && dpi <= 200) { *scanner_dpi = 200; return SANE_STATUS_GOOD; } if (dpi == 300) { *scanner_dpi = 300; return SANE_STATUS_GOOD; } if (dpi > 300 && dpi <= 600) { *scanner_dpi = 600; return SANE_STATUS_GOOD; } if (dpi > 600 && dpi <= 1200) { *scanner_dpi = 1200; return SANE_STATUS_GOOD; } if (dpi > 1200 && dpi <= 2400) { *scanner_dpi = 2400; return SANE_STATUS_GOOD; } DBG (DBG_err, "Error calculating scanner DPI (given DPI: %u)\n", dpi); return SANE_STATUS_INVAL; } /******************************************************************************/ static SANE_Status hp5590_calc_pixel_bits (unsigned int dpi, enum color_depths color_depth, unsigned int *pixel_bits) { unsigned int scanner_dpi; SANE_Status ret; DBG (DBG_proc, "%s\n", __func__); hp5590_cmds_assert (pixel_bits != NULL); hp5590_cmds_assert (dpi != 0); ret = calc_scanner_dpi (dpi, &scanner_dpi); if (ret != SANE_STATUS_GOOD) return ret; if (color_depth == DEPTH_COLOR_48) { *pixel_bits = 48; return SANE_STATUS_GOOD; } if (color_depth == DEPTH_COLOR_24) { *pixel_bits = 24; return SANE_STATUS_GOOD; } if (color_depth == DEPTH_GRAY) { *pixel_bits = 8; return SANE_STATUS_GOOD; } if (color_depth == DEPTH_BW) { if (dpi == scanner_dpi) *pixel_bits = 1; else *pixel_bits = 8; return SANE_STATUS_GOOD; } DBG (DBG_err, "Error calculating pixel bits (given DPI: %u)\n", dpi); return SANE_STATUS_INVAL; } /******************************************************************************/ static SANE_Status hp5590_set_scan_area (SANE_Int dn, enum proto_flags proto_flags, struct scanner_info *scanner_info, unsigned int top_x, unsigned int top_y, unsigned int width, unsigned int height, unsigned int dpi, enum color_depths color_depth, enum scan_modes scan_mode, enum scan_sources scan_source) { struct scan_params scan_params; unsigned int scanner_top_x; unsigned int scanner_top_y; unsigned int scanner_pixels_x; unsigned int scanner_pixels_y; unsigned int base_dpi; unsigned int scanner_dpi; unsigned int pixel_bits; unsigned int scanner_line_width; unsigned int max_pixels_x_current_dpi; unsigned int max_pixels_y_current_dpi; unsigned int pixels_x; unsigned int pixels_y; SANE_Status ret; DBG (DBG_proc, "%s\n", __func__); hp5590_cmds_assert (sizeof (scan_params) == 37); hp5590_cmds_assert (dpi != 0); hp5590_cmds_assert (scanner_info != NULL); memset (&scan_params, 0, sizeof (scan_params)); scan_params.source = SCAN_PARAMS_SOURCE_SIMPLEX; if (scan_source == SOURCE_ADF) scan_params.source |= SCAN_PARAMS_SOURCE_ADF; if (scan_source == SOURCE_ADF_DUPLEX) scan_params.source |= SCAN_PARAMS_SOURCE_ADF | SCAN_PARAMS_SOURCE_DUPLEX; if (scan_source == SOURCE_FLATBED) scan_params.source |= SCAN_PARAMS_SOURCE_FLATBED; if (scan_source == SOURCE_TMA_SLIDES) scan_params.source |= SCAN_PARAMS_SOURCE_TMA_SLIDES; if (scan_source == SOURCE_TMA_NEGATIVES) scan_params.source |= SCAN_PARAMS_SOURCE_TMA_NEGATIVES; DBG (DBG_cmds, "Scan params. source : 0x%04x\n", scan_params.source); DBG (DBG_cmds, "DPI: %u\n", dpi); if (dpi > scanner_info->max_dpi_x || dpi > scanner_info->max_dpi_y) { DBG (DBG_err, "DPI too large " "(given: %u, max X DPI: %u, max Y DPI: %u)\n", dpi, scanner_info->max_dpi_x, scanner_info->max_dpi_y); return SANE_STATUS_INVAL; } ret = calc_base_dpi (dpi, &base_dpi); if (ret != SANE_STATUS_GOOD) return ret; DBG (DBG_cmds, "Base DPI: %u\n", base_dpi); ret = calc_scanner_dpi (dpi, &scanner_dpi); if (ret != SANE_STATUS_GOOD) return ret; DBG (DBG_cmds, "Scanner DPI: %u\n", scanner_dpi); scan_params.dpi_x = htons (scanner_dpi); scan_params.dpi_y = htons (scanner_dpi); DBG (DBG_cmds, "DPI X: 0x%04x\n", scanner_dpi); DBG (DBG_cmds, "DPI Y: 0x%04x\n", scanner_dpi); ret = hp5590_calc_pixel_bits (dpi, color_depth, &pixel_bits); if (ret != SANE_STATUS_GOOD) return ret; DBG (DBG_cmds, "Pixel bits: %u\n", pixel_bits); scan_params.pixel_bits = pixel_bits; scan_params.bw_gray_flag = 0; if (color_depth == DEPTH_BW || color_depth == DEPTH_GRAY) scan_params.bw_gray_flag = htons (0x40); scan_params.flags = htons (0xe840); if (dpi > 300 && dpi <= 1200) scan_params.flags = htons (0xc840); if (dpi > 1200) scan_params.flags = htons (0xc040); scan_params.motor_param1 = htons (100); scan_params.motor_param2 = htons (100); scan_params.motor_param3 = htons (100); if (scan_source == SOURCE_TMA_NEGATIVES) { scan_params.motor_param2 = htons (200); scan_params.motor_param3 = htons (400); } scan_params.unk1 = htons (0x80); scan_params.mode = 0; if (scan_mode == MODE_PREVIEW) scan_params.mode = 0x04; max_pixels_x_current_dpi = (float) scanner_info->max_size_x * dpi; max_pixels_y_current_dpi = (float) scanner_info->max_size_y * dpi; if ( scan_source == SOURCE_TMA_NEGATIVES || scan_source == SOURCE_TMA_SLIDES) { max_pixels_x_current_dpi = (float) (TMA_MAX_X_INCHES * dpi); max_pixels_y_current_dpi = (float) (TMA_MAX_Y_INCHES * dpi); } /* In ADF mode the device can scan up to ADF_MAX_Y_INCHES, which is usually * bigger than what scanner reports back during initialization */ if ( scan_source == SOURCE_ADF ) max_pixels_y_current_dpi = (float) (ADF_MAX_Y_INCHES * dpi); /* Allow two times of max pixels for ADF Duplex mode */ if (scan_source == SOURCE_ADF_DUPLEX) max_pixels_y_current_dpi *= 2; pixels_x = width; pixels_y = height; scanner_top_x = (float) (top_x * (1.0 * base_dpi / dpi)); scanner_top_y = (float) (top_y * (1.0 * base_dpi / dpi)); scanner_pixels_x = (float) (pixels_x * (1.0 * base_dpi / dpi)); scanner_pixels_y = (float) (pixels_y * (1.0 * base_dpi / dpi)); DBG (DBG_cmds, "Top X: %u, top Y: %u, size X: %u, size Y: %u\n", top_x, top_y, pixels_x, pixels_y); DBG (DBG_cmds, "Scanner top X: %u, top Y: %u, size X: %u, size Y: %u\n", scanner_top_x, scanner_top_y, scanner_pixels_x, scanner_pixels_y); if (top_x + pixels_x > max_pixels_x_current_dpi) { DBG (DBG_err, "Top X (%u) + pixels X (%u) exceeds max X %u\n", top_x, pixels_x, max_pixels_x_current_dpi); return SANE_STATUS_INVAL; } if (top_y + pixels_y > max_pixels_y_current_dpi) { DBG (DBG_err, "Top Y (%u) + pixels Y (%u) exceeds max Y %u\n", top_y, pixels_y, max_pixels_y_current_dpi); return SANE_STATUS_INVAL; } scan_params.top_x = htons (scanner_top_x); scan_params.top_y = htons (scanner_top_y); scan_params.size_x = htons (scanner_pixels_x); scan_params.size_y = htons (scanner_pixels_y); scanner_line_width = (float) (pixels_x * (1.0 * scanner_dpi / dpi) / 8 * pixel_bits); /* Scanner hangs at scan command if line width less than 18 */ if (scanner_line_width < 18) { DBG (DBG_err, "Line width too small, extending to minimum\n"); scanner_line_width = 18; } scan_params.line_width = htons (scanner_line_width); DBG (DBG_cmds, "Line width: %u\n", scanner_line_width); ret = hp5590_cmd (dn, proto_flags, CMD_VERIFY, CMD_SET_SCAN_PARAMS, (unsigned char *) &scan_params, sizeof (scan_params), CORE_DATA); if (ret != SANE_STATUS_GOOD) return ret; return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status hp5590_read_image_params (SANE_Int dn, enum proto_flags proto_flags) { struct image_params image_params; SANE_Status ret; DBG (DBG_proc, "%s\n", __func__); hp5590_cmds_assert (sizeof (image_params) == 16); memset (&image_params, 0, sizeof (image_params)); ret = hp5590_cmd (dn, proto_flags, CMD_IN | CMD_VERIFY, CMD_GET_IMAGE_PARAMS, (unsigned char *) &image_params, sizeof (image_params), CORE_NONE); if (ret != SANE_STATUS_GOOD) return ret; if (image_params.signature != 0xc0) { DBG (DBG_err, "Wrong signature for image parameters structure " "received (needed 0xc0, got %02x)\n", image_params.signature); return SANE_STATUS_IO_ERROR; } DBG (DBG_cmds, "Received image params:\n"); DBG (DBG_cmds, "Signature %02x\n", image_params.signature); DBG (DBG_cmds, "Image size %lu (%04lx)\n", (unsigned long) ntohl (image_params.image_size), (unsigned long) ntohl (image_params.image_size)); DBG (DBG_cmds, "Line width: %u (%02x)\n", ntohs (image_params.line_width), ntohs (image_params.line_width)); DBG (DBG_cmds, "Actual size Y: %u (%02x)\n", ntohs (image_params.real_size_y), ntohs (image_params.real_size_y)); return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status hp5590_set_scan_params (SANE_Int dn, enum proto_flags proto_flags, struct scanner_info * scanner_info, unsigned int top_x, unsigned int top_y, unsigned int width, unsigned int height, unsigned int dpi, enum color_depths color_depth, enum scan_modes scan_mode, enum scan_sources scan_source) { unsigned int base_dpi; SANE_Status ret; DBG (DBG_proc, "%s\n", __func__); hp5590_cmds_assert (scanner_info != NULL); hp5590_cmds_assert (dpi != 0); /* Lock scanner */ ret = hp5590_lock_unlock_scanner (dn, proto_flags); if (ret != SANE_STATUS_GOOD) return ret; /* Set base DPI */ ret = calc_base_dpi (dpi, &base_dpi); if (ret != SANE_STATUS_GOOD) { /* Unlock scanner */ hp5590_lock_unlock_scanner (dn, proto_flags); return ret; } DBG (DBG_cmds, "Set base DPI: %u\n", base_dpi); ret = hp5590_set_base_dpi (dn, proto_flags, scanner_info, base_dpi); if (ret != SANE_STATUS_GOOD) { /* Unlock scanner */ hp5590_lock_unlock_scanner (dn, proto_flags); return ret; } /* Set color map */ ret = hp5590_set_color_map (dn, proto_flags, base_dpi); if (ret != SANE_STATUS_GOOD) { /* Unlock scanner */ hp5590_lock_unlock_scanner (dn, proto_flags); return ret; } ret = hp5590_set_scan_area (dn, proto_flags, scanner_info, top_x, top_y, width, height, dpi, color_depth, scan_mode, scan_source); if (ret != SANE_STATUS_GOOD) { /* Unlock scanner */ hp5590_lock_unlock_scanner (dn, proto_flags); return ret; } ret = hp5590_read_image_params (dn, proto_flags); if (ret != SANE_STATUS_GOOD) { /* Unlock scanner */ hp5590_lock_unlock_scanner (dn, proto_flags); return ret; } /* Unlock scanner */ ret = hp5590_lock_unlock_scanner (dn, proto_flags); if (ret != SANE_STATUS_GOOD) return ret; return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status hp5590_send_reverse_calibration_map (SANE_Int dn, enum proto_flags proto_flags) { unsigned int reverse_map_size = REVERSE_MAP_LEN; uint16_t reverse_map[REVERSE_MAP_LEN]; unsigned int i; uint16_t val; unsigned int len; SANE_Status ret; DBG (DBG_proc, "%s\n", __func__); DBG (DBG_proc, "Preparing reverse calibration map\n"); val = 0xffff; len = reverse_map_size / 4; for (i = 0; i < len; i++) { reverse_map[i] = htons (val); val -= 1; } for (i = len; i < len * 2; i++) { reverse_map[i] = htons (val); val -= 1; } for (i = len * 2; i < len * 3; i++) { reverse_map[i] = htons (val); val -= 1; } for (i = len * 3; i < len * 4; i++) { if (1) { reverse_map[i] = htons (0xffff); } else { reverse_map[i] = htons (val); val -= 1; } } DBG (DBG_proc, "Done preparing reverse calibration map. n=%u, bytes_per_entry=%zu\n", reverse_map_size, sizeof(uint16_t)); ret = hp5590_bulk_write (dn, proto_flags, 0x2b, (unsigned char *) reverse_map, reverse_map_size * sizeof (uint16_t)); if (ret != SANE_STATUS_GOOD) return ret; return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status hp5590_send_forward_calibration_maps (SANE_Int dn, enum proto_flags proto_flags) { unsigned int forward_map_size = FORWARD_MAP_LEN; uint16_t forward_map[FORWARD_MAP_LEN]; SANE_Status ret; unsigned int i; uint16_t val; DBG (DBG_proc, "%s\n", __func__); DBG (DBG_proc, "Preparing forward calibration map\n"); val = 0x0000; for (i = 0; i < forward_map_size; i++) { forward_map[i] = htons (val); if (val < 0xffff) val += 1; } DBG (DBG_proc, "Done preparing forward calibration map. n=%u, bytes_per_entry=%zu\n", forward_map_size, sizeof(uint16_t)); ret = hp5590_bulk_write (dn, proto_flags, 0x012a, (unsigned char *) forward_map, forward_map_size * sizeof (uint16_t)); if (ret != SANE_STATUS_GOOD) return ret; ret = hp5590_bulk_write (dn, proto_flags, 0x022a, (unsigned char *) forward_map, forward_map_size * sizeof (uint16_t)); if (ret != SANE_STATUS_GOOD) return ret; ret = hp5590_bulk_write (dn, proto_flags, 0x032a, (unsigned char *) forward_map, forward_map_size * sizeof (uint16_t)); if (ret != SANE_STATUS_GOOD) return ret; return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status hp5590_read (SANE_Int dn, enum proto_flags proto_flags, unsigned char *bytes, unsigned int size, void *state) { SANE_Status ret; DBG (DBG_proc, "%s\n", __func__); hp5590_cmds_assert (bytes != NULL); hp5590_cmds_assert (state != NULL); ret = hp5590_bulk_read (dn, proto_flags, bytes, size, state); if (ret != SANE_STATUS_GOOD) return ret; return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status hp5590_start_scan (SANE_Int dn, enum proto_flags proto_flags) { uint8_t reg_051b = 0x40; SANE_Status ret; DBG (DBG_proc, "%s\n", __func__); hp5590_cmds_assert (sizeof (reg_051b) == 1); ret = hp5590_cmd (dn, proto_flags, CMD_VERIFY, CMD_START_SCAN, (unsigned char *) ®_051b, sizeof (reg_051b), CORE_NONE); if (ret != SANE_STATUS_GOOD) return ret; return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status hp5590_read_buttons (SANE_Int dn, enum proto_flags proto_flags, enum button_status * status) { uint16_t button_status; SANE_Status ret; DBG (DBG_proc, "%s\n", __func__); hp5590_cmds_assert (status != NULL); hp5590_cmds_assert (sizeof (button_status) == 2); ret = hp5590_cmd (dn, proto_flags, CMD_IN | CMD_VERIFY, CMD_BUTTON_STATUS, (unsigned char *) &button_status, sizeof (button_status), CORE_NONE); if (ret != SANE_STATUS_GOOD) return ret; *status = BUTTON_NONE; /* Network order */ button_status = ntohs (button_status); DBG (DBG_cmds, "Button status: %04x\n", button_status); DBG (DBG_cmds, "Power: %s, Scan: %s, Collect: %s, File: %s, Email: %s, Copy: %s, " "Up: %s, Down: %s, Mode: %s, Cancel: %s\n", button_status & BUTTON_FLAG_POWER ? " on" : "off", button_status & BUTTON_FLAG_SCAN ? " on" : "off", button_status & BUTTON_FLAG_COLLECT ? " on" : "off", button_status & BUTTON_FLAG_FILE ? " on" : "off", button_status & BUTTON_FLAG_EMAIL ? " on" : "off", button_status & BUTTON_FLAG_COPY ? " on" : "off", button_status & BUTTON_FLAG_UP ? " on" : "off", button_status & BUTTON_FLAG_DOWN ? " on" : "off", button_status & BUTTON_FLAG_MODE ? " on" : "off", button_status & BUTTON_FLAG_CANCEL ? " on" : "off"); if (button_status & BUTTON_FLAG_POWER) *status = BUTTON_POWER; if (button_status & BUTTON_FLAG_SCAN) *status = BUTTON_SCAN; if (button_status & BUTTON_FLAG_COLLECT) *status = BUTTON_COLLECT; if (button_status & BUTTON_FLAG_FILE) *status = BUTTON_FILE; if (button_status & BUTTON_FLAG_EMAIL) *status = BUTTON_EMAIL; if (button_status & BUTTON_FLAG_COPY) *status = BUTTON_COPY; if (button_status & BUTTON_FLAG_UP) *status = BUTTON_UP; if (button_status & BUTTON_FLAG_DOWN) *status = BUTTON_DOWN; if (button_status & BUTTON_FLAG_MODE) *status = BUTTON_MODE; if (button_status & BUTTON_FLAG_CANCEL) *status = BUTTON_CANCEL; return SANE_STATUS_GOOD; } /******************************************************************************/ static SANE_Status hp5590_read_lcd_and_led (SANE_Int dn, enum proto_flags proto_flags, SANE_Int * lcd_counter, enum color_led_status * color_led) { SANE_Status ret; DBG (DBG_proc, "%s\n", __func__); hp5590_cmds_assert (lcd_counter != NULL); hp5590_cmds_assert (color_led != NULL); /* * Read LCD status bytes and get current value of counter and * state of color/black_white LED. * data[0x29]: LCD counter value, data[0x2a]: 1=color / 2=black_white. */ uint8_t data[0x30]; ret = hp5590_cmd (dn, proto_flags, CMD_IN | CMD_VERIFY, CMD_LCD_STATUS, (unsigned char *) &data, sizeof (data), CORE_NONE); if (ret != SANE_STATUS_GOOD) return ret; *lcd_counter = data[0x29]; if (data[0x2a] == 2) { *color_led = LED_BLACKWHITE; } else { *color_led = LED_COLOR; } DBG (DBG_cmds, "LCD and LED values: lcd=%d, led=%s\n", *lcd_counter, *color_led == LED_BLACKWHITE ? "black_white" : "color"); return SANE_STATUS_GOOD; } /* SET SCAN PARAMETERS ==================== 50 ======================= BW 50 (425 x 585) 18 00 64 00 64 00 00 00 00 04 f8 06 db 00 80 00 40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 03 50 BW 50 reduced top left (261 x 469) 18 00 64 00 64 00 00 00 00 03 0c 05 7f 00 80 00 40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 02 08 BW 50 reduced top right (302 x 498) 18 00 64 00 64 01 6e 00 00 03 8a 05 d6 00 80 00 40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 02 5c BW 50 X 0, small width (16 x 585) 18 00 64 00 64 00 00 00 00 00 30 06 db 00 80 00 40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 00 20 BW 50 X large, small width (24 x 585) 18 00 64 00 64 04 b0 00 00 00 48 06 db 00 80 00 40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 00 30 BW 50 Y 0, small height (425 x 21) 18 00 64 00 64 00 00 00 00 04 f8 00 3f 00 80 00 40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 03 50 BW 50 Y large, small height (425 x 21) 18 00 64 00 64 00 00 06 99 04 f8 00 3f 00 80 00 40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 03 50 GRAY 50 18 00 64 00 64 00 00 00 00 04 f8 06 db 00 80 00 40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 03 50 COLOR 50 18 00 64 00 64 00 00 00 00 04 f8 06 db 00 80 00 00 18 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 09 f0 ==================== 75 ======================= BW 75 18 00 64 00 64 00 00 00 00 04 f8 06 da 00 80 00 40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 03 50 GRAY 75 18 00 64 00 64 00 00 00 00 04 f8 06 da 00 80 00 40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 03 50 COLOR 75 18 00 64 00 64 00 00 00 00 04 f8 06 da 00 80 00 00 18 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 09 f0 COLOR 75 48 bit 18 00 64 00 64 00 00 00 00 04 f8 06 db 00 80 00 00 30 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 04 00 00 13 e0 =================== 100 ======================= BW 100 18 00 64 00 64 00 00 00 00 04 f8 06 db 00 80 00 40 01 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 00 6a GRAY 100 18 00 64 00 64 00 00 00 00 04 f8 06 db 00 80 00 40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 03 50 COLOR 100 18 00 64 00 64 00 00 00 00 04 f8 06 db 00 80 00 00 18 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 09 f0 COLOR 100 48bit, preview 18 00 64 00 64 00 00 00 00 04 f8 06 db 00 80 00 00 30 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 04 00 00 13 e0 COLOR 100 48bit 18 00 64 00 64 00 00 00 00 04 f2 06 db 00 80 00 00 30 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 13 c8 COLOR 100 48bit, TMA negatives 11 00 64 00 64 00 00 00 00 00 fc 03 84 00 80 00 00 30 e8 40 00 64 00 c8 01 90 00 00 00 00 00 00 00 00 00 03 f0 COLOR 100 48bit, TMA slides 12 00 64 00 64 00 00 00 00 00 fc 03 84 00 80 00 00 30 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 03 f0 =================== 150 ======================= BW 150 18 00 c8 00 c8 00 00 00 00 09 f6 0d b6 00 80 00 40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 06 a4 GRAY 150 18 00 c8 00 c8 00 00 00 00 09 f6 0d b6 00 80 00 40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 06 a4 COLOR 150 18 00 c8 00 c8 00 00 00 00 09 f6 0d b6 00 80 00 00 18 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 13 ec COLOR 150 48 bit 18 00 c8 00 c8 00 00 00 00 09 ea 0d b6 00 80 00 00 30 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 27 a8 =================== 200 ======================= BW 200 18 00 c8 00 c8 00 00 00 00 09 f0 0d b6 00 80 00 40 01 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 00 d4 GRAY 200 18 00 c8 00 c8 00 00 00 00 09 f6 0d b6 00 80 00 40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 06 a4 COLOR 200 18 00 c8 00 c8 00 00 00 00 09 f6 0d b6 00 80 00 00 18 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 13 ec COLOR 200 48 bit 18 00 c8 00 c8 00 00 00 00 09 f6 0d aa 00 80 00 00 30 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 27 d8 =================== 300 ======================= BW 300 18 01 2c 01 2c 00 00 00 00 09 f0 0d b6 00 80 00 40 01 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 01 3e GRAY 300 18 01 2c 01 2c 00 00 00 00 09 f4 0d b6 00 80 00 40 08 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 09 f4 COLOR 300 18 01 2c 01 2c 00 00 00 00 09 f4 0d b6 00 80 00 00 18 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 1d dc COLOR 300 48bit, TMA negatives 11 01 2c 01 2c 00 00 00 06 01 fc 07 02 00 80 00 00 30 e8 40 00 64 00 c8 01 90 00 00 00 00 00 00 00 00 00 0b e8 COLOR 300 48bit, TMA slides 12 01 2c 01 2c 00 00 00 00 01 fc 07 08 00 80 00 00 30 e8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 0b e8 ==================== 400 ====================== BW 400 18 02 58 02 58 00 00 00 00 13 ec 1b 6c 00 80 00 40 08 c8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 13 ec GRAY 400 18 02 58 02 58 00 00 00 00 13 ec 1b 6c 00 80 00 40 08 c8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 13 ec COLOR 400 18 02 58 02 58 00 00 00 00 13 ec 1b 6c 00 80 00 00 18 c8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 3b c4 ==================== 600 ====================== BW 600 18 02 58 02 58 00 00 00 00 13 e8 1b 6c 00 80 00 40 01 c8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 02 7d GRAY 600 18 02 58 02 58 00 00 00 00 13 ec 1b 6c 00 80 00 40 08 c8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 13 ec COLOR 600 18 02 58 02 58 00 00 00 00 13 ec 1b 6c 00 80 00 00 18 c8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 3b c4 ==================== 1200 ===================== BW 1200 18 04 b0 04 b0 00 00 00 00 27 a8 36 d8 00 80 00 40 01 c8 40 00 64 00 64 00 64 00 00 00 00 00 00 00 00 00 04 f5 */ /* READ SCAN PARAMETERS ====================== 50 ===================== BW 50 c0 00 00 0f 23 a0 00 00 03 50 04 92 00 00 00 00 GRAY 50 c0 00 00 0f 23 a0 00 00 03 50 04 92 00 00 00 00 COLOR 50 c0 00 00 2d 6a e0 00 00 09 f0 04 92 00 00 00 00 ====================== 75 ===================== BW 75 c0 00 00 0f 20 50 00 00 03 50 04 91 00 00 00 00 GRAY 75 c0 00 00 0f 20 50 00 00 03 50 04 91 00 00 00 00 COLOR 75 c0 00 00 2d 60 f0 00 00 09 f0 04 91 00 00 00 00 COLOR 75 48 bit c0 00 00 5a 86 40 00 00 13 e0 04 8e 00 00 00 00 ===================== 100 ===================== BW 100 c0 00 00 01 e4 74 00 00 00 6a 04 92 00 00 00 00 GRAY 100 c0 00 00 0f 23 a0 00 00 03 50 04 92 00 00 00 00 COLOR 100 c0 00 00 2d 6a e0 00 00 09 f0 04 92 00 00 00 00 COLOR 100, 48 bit preview c0 00 00 5a d5 c0 00 00 13 e0 04 92 00 00 00 00 COLOR 100, 48 bit c0 00 00 5a 68 10 00 00 13 c8 04 92 00 00 00 00 ===================== 150 ===================== BW 150 c0 00 00 3c b3 10 00 00 06 a4 09 24 00 00 00 00 GRAY 150 c0 00 00 3c b3 10 00 00 06 a4 09 24 00 00 00 00 COLOR 150 c0 00 00 b6 19 30 00 00 13 ec 09 24 00 00 00 00 COLOR 150 48bit c0 00 01 6a 7b a0 00 00 27 a8 09 24 00 00 00 00 ===================== 200 ===================== BW 200 c0 00 00 07 91 d0 00 00 00 d4 09 24 00 00 00 00 GRAY 200 c0 00 00 3c b3 10 00 00 06 a4 09 24 00 00 00 00 COLOR 200 c0 00 00 b6 19 30 00 00 13 ec 09 24 00 00 00 00 COLOR 200 48 bit c0 00 01 6a f3 a0 00 00 27 d8 09 1c 00 00 00 00 ===================== 300 ===================== BW 300 c0 00 00 11 08 14 00 00 01 3e 0d b6 00 00 00 00 GRAY 300 c0 00 00 88 77 78 00 00 09 f4 0d b6 00 00 00 00 COLOR 300 c0 00 01 99 66 68 00 00 1d dc 0d b6 00 00 00 00 ===================== 400 ===================== BW 400 c0 00 02 22 4b 90 00 00 13 ec 1b 6c 00 00 00 00 GRAY 400 c0 00 02 22 4b 90 00 00 13 ec 1b 6c 00 00 00 00 COLOR 400 c0 00 06 66 e2 b0 00 00 3b c4 1b 6c 00 00 00 00 ===================== 600 ===================== BW 600 c0 00 00 44 3b bc 00 00 02 7d 1b 6c 00 00 00 00 GRAY 600 c0 00 02 22 4b 90 00 00 13 ec 1b 6c 00 00 00 00 COLOR 600 c0 00 06 66 e2 b0 00 00 3b c4 1b 6c 00 00 00 00 */ /* vim: sw=2 ts=8 */ backends-1.3.0/backend/hp5590_cmds.h000066400000000000000000000161521456256263500170100ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2007 Ilia Sotnikov HP ScanJet 4570c support by Markham Thomas 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, see . 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 is part of a SANE backend for HP ScanJet 4500C/4570C/5500C/5550C/5590/7650 Scanners */ #ifndef HP5590_H #define HP5590_H #include "hp5590_low.h" #define TMA_MAX_X_INCHES 1.69 #define TMA_MAX_Y_INCHES 6 #define ADF_MAX_Y_INCHES 14 enum hp_scanner_types { SCANNER_NONE = 0, SCANNER_HP4570, SCANNER_HP5550, SCANNER_HP5590, SCANNER_HP7650 }; enum scan_sources { SOURCE_NONE = 1, SOURCE_FLATBED, SOURCE_ADF, SOURCE_ADF_DUPLEX, SOURCE_TMA_NEGATIVES, SOURCE_TMA_SLIDES }; enum scan_modes { MODE_NORMAL = 1, MODE_PREVIEW }; enum color_depths { DEPTH_BW = 1, DEPTH_GRAY, DEPTH_COLOR_24, DEPTH_COLOR_48 }; enum button_status { BUTTON_NONE = 1, BUTTON_POWER, BUTTON_SCAN, BUTTON_COLLECT, BUTTON_FILE, BUTTON_EMAIL, BUTTON_COPY, BUTTON_UP, BUTTON_DOWN, BUTTON_MODE, BUTTON_CANCEL }; enum color_led_status { LED_COLOR = 1, LED_BLACKWHITE }; enum hp5590_lamp_state { LAMP_STATE_TURNOFF = 1, LAMP_STATE_TURNON, LAMP_STATE_SET_TURNOFF_TIME, LAMP_STATE_SET_TURNOFF_TIME_LONG }; struct hp5590_model { enum hp_scanner_types scanner_type; unsigned int usb_vendor_id; unsigned int usb_product_id; const char *vendor_id; const char *model; const char *kind; enum proto_flags proto_flags; }; #define FEATURE_NONE 0 #define FEATURE_ADF 1 << 0 #define FEATURE_TMA 1 << 1 #define FEATURE_LCD 1 << 2 struct scanner_info { const char *model; const char *kind; unsigned int features; const char *fw_version; unsigned int max_dpi_x; unsigned int max_dpi_y; unsigned int max_pixels_x; unsigned int max_pixels_y; float max_size_x; float max_size_y; unsigned int max_motor_param; unsigned int normal_motor_param; }; static SANE_Status hp5590_model_def (enum hp_scanner_types scanner_type, const struct hp5590_model ** model); static SANE_Status hp5590_vendor_product_id (enum hp_scanner_types scanner_type, SANE_Word * vendor_id, SANE_Word * product_id); static SANE_Status hp5590_init_scanner (SANE_Int dn, enum proto_flags proto_flags, struct scanner_info **info, enum hp_scanner_types scanner_type); static SANE_Status hp5590_power_status (SANE_Int dn, enum proto_flags proto_flags); static SANE_Status hp5590_read_max_scan_count (SANE_Int dn, enum proto_flags proto_flags, unsigned int *max_count); static SANE_Status hp5590_select_source_and_wakeup (SANE_Int dn, enum proto_flags proto_flags, enum scan_sources source, SANE_Bool extend_lamp_timeout); static SANE_Status hp5590_stop_scan (SANE_Int dn, enum proto_flags proto_flags); static SANE_Status hp5590_read_scan_count (SANE_Int dn, enum proto_flags proto_flags, unsigned int *count); static SANE_Status hp5590_set_scan_params (SANE_Int dn, enum proto_flags proto_flags, struct scanner_info *scanner_info, unsigned int top_x, unsigned int top_y, unsigned int width, unsigned int height, unsigned int dpi, enum color_depths color_depth, enum scan_modes scan_mode, enum scan_sources scan_source); static SANE_Status hp5590_send_forward_calibration_maps (SANE_Int dn, enum proto_flags proto_flags); static SANE_Status hp5590_send_reverse_calibration_map (SANE_Int dn, enum proto_flags proto_flags); static SANE_Status hp5590_inc_scan_count (SANE_Int dn, enum proto_flags proto_flags); static SANE_Status hp5590_start_scan (SANE_Int dn, enum proto_flags proto_flags); static SANE_Status hp5590_read (SANE_Int dn, enum proto_flags proto_flags, unsigned char *bytes, unsigned int size, void *state); static SANE_Status hp5590_read_buttons (SANE_Int dn, enum proto_flags proto_flags, enum button_status *status); static SANE_Status hp5590_read_part_number (SANE_Int dn, enum proto_flags proto_flags); static SANE_Status hp5590_calc_pixel_bits (unsigned int dpi, enum color_depths color_depth, unsigned int *pixel_bits); static SANE_Status hp5590_is_data_available (SANE_Int dn, enum proto_flags proto_flags); static SANE_Status hp5590_reset_scan_head (SANE_Int dn, enum proto_flags proto_flags); #endif /* HP5590_H */ /* vim: sw=2 ts=8 */ backends-1.3.0/backend/hp5590_low.c000066400000000000000000000771201456256263500166600ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2007 Ilia Sotnikov 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, see . 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 is part of a SANE backend for HP ScanJet 4500C/4570C/5500C/5550C/5590/7650 Scanners */ #include "../include/sane/config.h" #include #include #include #ifdef HAVE_NETINET_IN_H # include #endif /* HAVE_NETINET_IN_H */ #include "byteorder.h" #include "../include/sane/sanei_debug.h" #include "../include/sane/sanei_usb.h" #include "../include/_stdint.h" #include "hp5590_low.h" /* Debug levels */ #define DBG_err 0 #define DBG_proc 10 #define DBG_usb 50 /* Custom assert() macro */ #define hp5590_low_assert(exp) if(!(exp)) { \ DBG (DBG_err, "Assertion '%s' failed at %s:%u\n", #exp, __FILE__, __LINE__);\ return SANE_STATUS_INVAL; \ } /* Structure describing bulk transfer size */ struct bulk_size { uint16_t size; uint8_t unused; } __attribute__ ((packed)); /* Structure describing bulk URB */ /* FIXME: Verify according to USB standard */ struct usb_in_usb_bulk_setup { uint8_t bRequestType; uint8_t bRequest; uint8_t bEndpoint; uint16_t unknown; uint16_t wLength; /* MSB first */ uint8_t pad; } __attribute__ ((packed)); /* Structure describing control URB */ struct usb_in_usb_ctrl_setup { uint8_t bRequestType; uint8_t bRequest; uint16_t wValue; /* MSB first */ uint16_t wIndex; /* MSB first */ uint16_t wLength; /* LSB first */ } __attribute__ ((packed)); /* CORE status flag - ready or not */ #define CORE_FLAG_NOT_READY 1 << 1 /* Bulk transfers are done in pages, below their respective sizes */ #define BULK_WRITE_PAGE_SIZE 0x0f000 #define BULK_READ_PAGE_SIZE 0x10000 #define ALLOCATE_BULK_READ_PAGES 16 /* 16 * 65536 = 1Mb */ /* Structure describing bulk read state, because bulk reads will be done in * pages, but function caller uses its own buffer, whose size is certainly * different. Also, each bulk read page is ACK'ed by special command * so total pages received should be tracked as well */ struct bulk_read_state { unsigned char *buffer; unsigned int buffer_size; unsigned int bytes_available; unsigned char *buffer_out_ptr; unsigned char *buffer_in_ptr; unsigned int total_pages; unsigned char *buffer_end_ptr; unsigned int initialized; }; /******************************************************************************* * USB-in-USB: get acknowledge for last USB-in-USB operation * * Parameters * dn - sanei_usb device descriptor * * Returns * SANE_STATUS_GOOD - if correct acknowledge was received * SANE_STATUS_DEVICE_BUSY - otherwise */ static SANE_Status hp5590_get_ack (SANE_Int dn, enum proto_flags proto_flags) { uint8_t status; SANE_Status ret; /* Bypass reading acknowledge if the device doesn't need it */ if (proto_flags & PF_NO_USB_IN_USB_ACK) return SANE_STATUS_GOOD; DBG (DBG_proc, "%s\n", __func__); /* Check if USB-in-USB operation was accepted */ ret = sanei_usb_control_msg (dn, USB_DIR_IN | USB_TYPE_VENDOR, 0x0c, 0x8e, 0x20, sizeof (status), &status); if (ret != SANE_STATUS_GOOD) { DBG (DBG_err, "%s: USB-in-USB: error getting acknowledge\n", __func__); return ret; } DBG (DBG_usb, "%s: USB-in-USB: accepted\n", __func__); /* Check if we received correct acknowledgment */ if (status != 0x01) { DBG (DBG_err, "%s: USB-in-USB: not accepted (status %u)\n", __func__, status); return SANE_STATUS_DEVICE_BUSY; } return SANE_STATUS_GOOD; } /******************************************************************************* * USB-in-USB: get device status * * Parameters * dn - sanei_usb device descriptor * * Returns * SANE_STATUS_GOOD - if correct status was received * SANE_STATUS_DEVICE_BUSY - otherwise */ static SANE_Status hp5590_get_status (SANE_Int dn, __sane_unused__ enum proto_flags proto_flags) { uint8_t status; SANE_Status ret; DBG (DBG_proc, "%s\n", __func__); ret = sanei_usb_control_msg (dn, USB_DIR_IN | USB_TYPE_VENDOR, 0x0c, 0x8e, 0x00, sizeof (status), &status); if (ret != SANE_STATUS_GOOD) { DBG (DBG_err, "%s: USB-in-USB: error getting device status\n", __func__); return ret; } /* Check if we received correct status */ if (status != 0x00) { DBG (DBG_err, "%s: USB-in-USB: got non-zero device status (status %u)\n", __func__, status); return SANE_STATUS_DEVICE_BUSY; } return SANE_STATUS_GOOD; } /******************************************************************************* * USB-in-USB: sends control message for IN or OUT operation * * Parameters * dn - sanei_usb device descriptor * requesttype, request, value, index - their meanings are similar to * sanei_control_msg() * bytes - pointer to data buffer * size - size of data * core_flags - * CORE_NONE - no CORE operation will be performed * CORE_DATA - operation on CORE data will be performed * CORE_BULK_IN - preparation for bulk IN transfer (not used yet) * CORE_BULK_OUT - preparation for bulk OUT transfer * * Returns * SANE_STATUS_GOOD - control message was sent w/o any errors * all other SANE_Status values - otherwise */ static SANE_Status hp5590_control_msg (SANE_Int dn, enum proto_flags proto_flags, int requesttype, int request, int value, int index, unsigned char *bytes, int size, int core_flags) { struct usb_in_usb_ctrl_setup ctrl; SANE_Status ret; unsigned int len; unsigned char *ptr; uint8_t ack; uint8_t response; unsigned int needed_response; DBG (DBG_proc, "%s: USB-in-USB: core data: %s\n", __func__, core_flags & CORE_DATA ? "yes" : "no"); hp5590_low_assert (bytes != NULL); /* IN (read) operation will be performed */ if (requesttype & USB_DIR_IN) { /* Prepare USB-in-USB control message */ memset (&ctrl, 0, sizeof (ctrl)); ctrl.bRequestType = 0xc0; ctrl.bRequest = request; ctrl.wValue = htons (value); ctrl.wIndex = htons (index); ctrl.wLength = htole16 (size); DBG (DBG_usb, "%s: USB-in-USB: sending control msg\n", __func__); /* Send USB-in-USB control message */ ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR, 0x04, 0x8f, 0x00, sizeof (ctrl), (unsigned char *) &ctrl); if (ret != SANE_STATUS_GOOD) { DBG (DBG_err, "%s: USB-in-USB: error sending control message\n", __func__); return ret; } /* USB-in-USB: checking acknowledge for control message */ ret = hp5590_get_ack (dn, proto_flags); if (ret != SANE_STATUS_GOOD) return ret; len = size; ptr = bytes; /* Data is read in 8 byte portions */ while (len) { unsigned int next_packet_size; next_packet_size = 8; if (len < 8) next_packet_size = len; /* Read USB-in-USB data */ ret = sanei_usb_control_msg (dn, USB_DIR_IN | USB_TYPE_VENDOR, core_flags & CORE_DATA ? 0x0c : 0x04, 0x90, 0x00, next_packet_size, ptr); if (ret != SANE_STATUS_GOOD) { DBG (DBG_err, "%s: USB-in-USB: error reading data\n", __func__); return ret; } ptr += next_packet_size; len -= next_packet_size; } /* Confirm data reception */ ack = 0; ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR, 0x0c, 0x8f, 0x00, sizeof (ack), &ack); if (ret != SANE_STATUS_GOOD) { DBG (DBG_err, "%s: USB-in-USB: error confirming data reception\n", __func__); return -1; } /* USB-in-USB: checking if confirmation was acknowledged */ ret = hp5590_get_ack (dn, proto_flags); if (ret != SANE_STATUS_GOOD) return ret; } /* OUT (write) operation will be performed */ if (!(requesttype & USB_DIR_IN)) { /* Prepare USB-in-USB control message */ memset (&ctrl, 0, sizeof (ctrl)); ctrl.bRequestType = 0x40; ctrl.bRequest = request; ctrl.wValue = htons (value); ctrl.wIndex = htons (index); ctrl.wLength = htole16 (size); DBG (DBG_usb, "%s: USB-in-USB: sending control msg\n", __func__); /* Send USB-in-USB control message */ ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR, 0x04, 0x8f, 0x00, sizeof (ctrl), (unsigned char *) &ctrl); if (ret != SANE_STATUS_GOOD) { DBG (DBG_err, "%s: USB-in-USB: error sending control message\n", __func__); return ret; } /* USB-in-USB: checking acknowledge for control message */ ret = hp5590_get_ack (dn, proto_flags); if (ret != SANE_STATUS_GOOD) return ret; len = size; ptr = bytes; /* Data is sent in 8 byte portions */ while (len) { unsigned int next_packet_size; next_packet_size = 8; if (len < 8) next_packet_size = len; /* Send USB-in-USB data */ ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR, core_flags & CORE_DATA ? 0x04 : 0x0c, 0x8f, 0x00, next_packet_size, ptr); if (ret != SANE_STATUS_GOOD) { DBG (DBG_err, "%s: USB-in-USB: error sending data\n", __func__); return ret; } /* CORE data is acknowledged packet by packet */ if (core_flags & CORE_DATA) { /* USB-in-USB: checking if data was accepted */ ret = hp5590_get_ack (dn, proto_flags); if (ret != SANE_STATUS_GOOD) return ret; } ptr += next_packet_size; len -= next_packet_size; } /* Normal (non-CORE) data is acknowledged after its full transmission */ if (!(core_flags & CORE_DATA)) { /* USB-in-USB: checking if data was accepted */ ret = hp5590_get_ack (dn, proto_flags); if (ret != SANE_STATUS_GOOD) return ret; } /* Getting response after data transmission */ DBG (DBG_usb, "%s: USB-in-USB: getting response\n", __func__); ret = sanei_usb_control_msg (dn, USB_DIR_IN | USB_TYPE_VENDOR, 0x0c, 0x90, 0x00, sizeof (response), &response); if (ret != SANE_STATUS_GOOD) { DBG (DBG_err, "%s: USB-in-USB: error getting response\n", __func__); return ret; } /* Necessary response after normal (non-CORE) data is 0x00, * after bulk OUT preparation - 0x24 */ needed_response = core_flags & CORE_BULK_OUT ? 0x24 : 0x00; if (response == needed_response) DBG (DBG_usb, "%s: USB-in-USB: got correct response\n", __func__); if (response != needed_response) { DBG (DBG_err, "%s: USB-in-USB: invalid response received " "(needed %04x, got %04x)\n", __func__, needed_response, response); return SANE_STATUS_IO_ERROR; } /* Send bulk OUT flags is bulk OUT preparation is performed */ if (core_flags & CORE_BULK_OUT) { uint8_t bulk_flags = 0x24; DBG (DBG_usb, "%s: USB-in-USB: sending bulk flags\n", __func__); ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR, 0x0c, 0x83, 0x00, sizeof (bulk_flags), &bulk_flags); if (ret != SANE_STATUS_GOOD) { DBG (DBG_err, "%s: USB-in-USB: error sending bulk flags\n", __func__); return ret; } /* USB-in-USB: checking confirmation for bulk flags */ ret = hp5590_get_ack (dn, proto_flags); if (ret != SANE_STATUS_GOOD) return ret; } } return SANE_STATUS_GOOD; } /******************************************************************************* * USB-in-USB: verifies last command * * Parameters * dn - sanei_usb device descriptor * cmd - command to verify * * Returns * SANE_STATUS_GOOD - command verified successfully and CORE is ready * SANE_STATUS_IO_ERROR - command verification failed * SANE_STATUS_DEVICE_BUSY - command verified successfully but CORE isn't ready * all other SANE_Status values - otherwise */ static SANE_Status hp5590_verify_last_cmd (SANE_Int dn, enum proto_flags proto_flags, unsigned int cmd) { uint16_t verify_cmd; unsigned int last_cmd; unsigned int core_status; SANE_Status ret; DBG (3, "%s: USB-in-USB: command verification requested\n", __func__); /* Read last command along with CORE status */ ret = hp5590_control_msg (dn, proto_flags, USB_DIR_IN, 0x04, 0xc5, 0x00, (unsigned char *) &verify_cmd, sizeof (verify_cmd), CORE_NONE); if (ret != SANE_STATUS_GOOD) return ret; verify_cmd = le16toh (verify_cmd); /* Response is LSB first */ /* Last command - minor byte */ last_cmd = verify_cmd & 0xff; /* CORE status - major byte */ core_status = (verify_cmd & 0xff00) >> 8; /* Verify last command */ DBG (DBG_usb, "%s: USB-in-USB: command verification %04x, " "last command: %04x, core status: %04x\n", __func__, verify_cmd, last_cmd, core_status); if ((cmd & 0x00ff) != last_cmd) { DBG (DBG_err, "%s: USB-in-USB: command verification failed: " "expected 0x%04x, got 0x%04x\n", __func__, cmd, last_cmd); return SANE_STATUS_IO_ERROR; } DBG (DBG_usb, "%s: USB-in-USB: command verified successfully\n", __func__); /* Return value depends on CORE status */ return core_status & CORE_FLAG_NOT_READY ? SANE_STATUS_DEVICE_BUSY : SANE_STATUS_GOOD; } /******************************************************************************* * USB-in-USB: send command (convenience wrapper around hp5590_control_msg()) * * Parameters * dn - sanei_usb device descriptor * requesttype, request, value, index - their meanings are similar to * sanei_control_msg() * bytes - pointer to data buffer * size - size of data * core_flags - * CORE_NONE - no CORE operation will be performed * CORE_DATA - operation on CORE data will be performed * CORE_BULK_IN - preparation for bulk IN transfer (not used yet) * CORE_BULK_OUT - preparation for bulk OUT transfer * * Returns * SANE_STATUS_GOOD - command was sent (and possible verified) w/o any errors * all other SANE_Status values - otherwise */ static SANE_Status hp5590_cmd (SANE_Int dn, enum proto_flags proto_flags, unsigned int flags, unsigned int cmd, unsigned char *data, unsigned int size, unsigned int core_flags) { SANE_Status ret; DBG (3, "%s: USB-in-USB: command : %04x\n", __func__, cmd); ret = hp5590_control_msg (dn, proto_flags, flags & CMD_IN ? USB_DIR_IN : USB_DIR_OUT, 0x04, cmd, 0x00, data, size, core_flags); if (ret != SANE_STATUS_GOOD) return ret; ret = SANE_STATUS_GOOD; /* Verify last command if requested */ if (flags & CMD_VERIFY) { ret = hp5590_verify_last_cmd (dn, proto_flags, cmd); } return ret; } /******************************************************************************* * USB-in-USB: initialized bulk read state * * Parameters * state - pointer to a pointer for initialized state * * Returns * SANE_STATUS_GOOD - if state was initialized successfully * SANE_STATUS_NO_MEM - memory allocation failed */ static SANE_Status hp5590_low_init_bulk_read_state (void **state) { struct bulk_read_state *bulk_read_state; DBG (3, "%s: USB-in-USB: initializing bulk read state\n", __func__); hp5590_low_assert (state != NULL); bulk_read_state = malloc (sizeof (struct bulk_read_state)); if (!bulk_read_state) return SANE_STATUS_NO_MEM; memset (bulk_read_state, 0, sizeof (struct bulk_read_state)); bulk_read_state->buffer = malloc (ALLOCATE_BULK_READ_PAGES * BULK_READ_PAGE_SIZE); if (!bulk_read_state->buffer) { DBG (DBG_err, "%s: Memory allocation failed for %u bytes\n", __func__, ALLOCATE_BULK_READ_PAGES * BULK_READ_PAGE_SIZE); return SANE_STATUS_NO_MEM; } bulk_read_state->buffer_size = ALLOCATE_BULK_READ_PAGES * BULK_READ_PAGE_SIZE; bulk_read_state->bytes_available = 0; bulk_read_state->buffer_out_ptr = bulk_read_state->buffer; bulk_read_state->buffer_in_ptr = bulk_read_state->buffer; bulk_read_state->total_pages = 0; bulk_read_state->buffer_end_ptr = bulk_read_state->buffer + bulk_read_state->buffer_size; bulk_read_state->initialized = 1; *state = bulk_read_state; return SANE_STATUS_GOOD; } /******************************************************************************* * USB-in-USB: free bulk read state * * Parameters * state - pointer to a pointer to bulk read state * * Returns * SANE_STATUS_GOOD - bulk read state freed successfully */ static SANE_Status hp5590_low_free_bulk_read_state (void **state) { struct bulk_read_state *bulk_read_state; DBG (3, "%s\n", __func__); hp5590_low_assert (state != NULL); /* Just return if NULL bulk read state was given */ if (*state == NULL) return SANE_STATUS_GOOD; bulk_read_state = *state; DBG (3, "%s: USB-in-USB: freeing bulk read state\n", __func__); free (bulk_read_state->buffer); bulk_read_state->buffer = NULL; free (bulk_read_state); *state = NULL; return SANE_STATUS_GOOD; } /* FIXME: perhaps needs to be converted to use hp5590_control_msg() */ /******************************************************************************* * USB-in-USB: bulk read * * Parameters * dn - sanei_usb device descriptor * bytes - pointer to data buffer * size - size of data to read * state - pointer to initialized bulk read state structure */ static SANE_Status hp5590_bulk_read (SANE_Int dn, enum proto_flags proto_flags, unsigned char *bytes, unsigned int size, void *state) { struct usb_in_usb_bulk_setup ctrl; SANE_Status ret; unsigned int next_pages; uint8_t bulk_flags; size_t next_portion; struct bulk_read_state *bulk_read_state; unsigned int bytes_until_buffer_end; DBG (3, "%s\n", __func__); hp5590_low_assert (state != NULL); hp5590_low_assert (bytes != NULL); bulk_read_state = state; if (bulk_read_state->initialized == 0) { DBG (DBG_err, "%s: USB-in-USB: bulk read state not initialized\n", __func__); return SANE_STATUS_INVAL; } memset (bytes, 0, size); /* Check if requested data would fit into the buffer */ if (size > bulk_read_state->buffer_size) { DBG (DBG_err, "Data requested won't fit in the bulk read buffer " "(requested: %u, buffer size: %u\n", size, bulk_read_state->buffer_size); return SANE_STATUS_NO_MEM; } /* Read data until requested size of data will be received */ while (bulk_read_state->bytes_available < size) { DBG (DBG_usb, "%s: USB-in-USB: not enough data in buffer available " "(available: %u, requested: %u)\n", __func__, bulk_read_state->bytes_available, size); /* IMPORTANT! 'next_pages' means 'request and receive next_pages pages in * one bulk transfer request '. Windows driver uses 4 pages between each * request. The more pages are received between requests the less the * scanner does scan head re-positioning thus improving scanning speed. * On the other hand, scanner expects that all of the requested pages * will be received immediately after the request. In case when a * frontend will have a delay between reads we will get bulk transfer * timeout sooner or later. * Having next_pages = 1 is the most safe case. */ next_pages = 1; /* Count all received pages to calculate when we will need to send * another bulk request */ bulk_read_state->total_pages++; DBG (DBG_usb, "%s: USB-in-USB: total pages done: %u\n", __func__, bulk_read_state->total_pages); /* Send another bulk request for 'next_pages' before first * page or next necessary one */ if ( bulk_read_state->total_pages == 1 || bulk_read_state->total_pages % next_pages == 0) { /* Send bulk flags */ DBG (DBG_usb, "%s: USB-in-USB: sending USB-in-USB bulk flags\n", __func__); bulk_flags = 0x24; ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR, 0x0c, 0x83, 0x00, sizeof (bulk_flags), &bulk_flags); if (ret != SANE_STATUS_GOOD) { DBG (DBG_err, "%s: USB-in-USB: error sending bulk flags\n", __func__); return ret; } /* USB-in-USB: checking confirmation for bulk flags\n" */ ret = hp5590_get_ack (dn, proto_flags); if (ret != SANE_STATUS_GOOD) return ret; /* Prepare bulk read request */ memset (&ctrl, 0, sizeof (ctrl)); ctrl.bRequestType = 0x00; ctrl.bEndpoint = 0x82; ctrl.wLength = htons (next_pages); /* Send bulk read request */ DBG (DBG_usb, "%s: USB-in-USB: sending control msg for bulk\n", __func__); ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR, 0x04, 0x82, 0x00, sizeof (ctrl), (unsigned char *) &ctrl); if (ret != SANE_STATUS_GOOD) { DBG (DBG_err, "%s: USB-in-USB: error sending control msg\n", __func__); return ret; } /* USB-in-USB: checking if control msg was accepted */ ret = hp5590_get_ack (dn, proto_flags); if (ret != SANE_STATUS_GOOD) return ret; } next_portion = BULK_READ_PAGE_SIZE; /* Check if next page will fit into the buffer */ if (bulk_read_state->buffer_size - bulk_read_state->bytes_available < next_portion) { DBG (DBG_err, "%s: USB-in-USB: buffer too small\n", __func__); return SANE_STATUS_NO_MEM; } /* Bulk read next page */ DBG (DBG_usb, "%s: USB-in-USB: bulk reading %lu bytes\n", __func__, (u_long) next_portion); ret = sanei_usb_read_bulk (dn, bulk_read_state->buffer_in_ptr, &next_portion); if (ret != SANE_STATUS_GOOD) { if (ret == SANE_STATUS_EOF) return ret; DBG (DBG_err, "%s: USB-in-USB: error during bulk read: %s\n", __func__, sane_strstatus (ret)); return ret; } /* Check if we received the same amount of data as requested */ if (next_portion != BULK_READ_PAGE_SIZE) { DBG (DBG_err, "%s: USB-in-USB: incomplete bulk read " "(requested %u bytes, got %lu bytes)\n", __func__, BULK_READ_PAGE_SIZE, (u_long) next_portion); return SANE_STATUS_IO_ERROR; } /* Move pointers to the next position */ bulk_read_state->buffer_in_ptr += next_portion; /* Check for the end of the buffer */ if (bulk_read_state->buffer_in_ptr > bulk_read_state->buffer_end_ptr) { DBG (DBG_err, "%s: USB-in-USB: attempted to access over the end of buffer " "(in_ptr: %p, end_ptr: %p, ptr: %p, buffer size: %u\n", __func__, (void *) bulk_read_state->buffer_in_ptr, (void *) bulk_read_state->buffer_end_ptr, (void *) bulk_read_state->buffer, bulk_read_state->buffer_size); return SANE_STATUS_NO_MEM; } /* Check for buffer pointer wrapping */ if (bulk_read_state->buffer_in_ptr == bulk_read_state->buffer_end_ptr) { DBG (DBG_usb, "%s: USB-in-USB: buffer wrapped while writing\n", __func__); bulk_read_state->buffer_in_ptr = bulk_read_state->buffer; } /* Count the amount of data we read */ bulk_read_state->bytes_available += next_portion; } /* Transfer requested amount of data to the caller */ DBG (DBG_usb, "%s: USB-in-USB: data in bulk buffer is available " "(requested %u bytes, available %u bytes)\n", __func__, size, bulk_read_state->bytes_available); /* Check for buffer pointer wrapping */ bytes_until_buffer_end = bulk_read_state->buffer_end_ptr - bulk_read_state->buffer_out_ptr; if (bytes_until_buffer_end <= size) { /* First buffer part */ DBG (DBG_usb, "%s: USB-in-USB: reached bulk read buffer end\n", __func__); memcpy (bytes, bulk_read_state->buffer_out_ptr, bytes_until_buffer_end); bulk_read_state->buffer_out_ptr = bulk_read_state->buffer; /* And second part (if any) */ if (bytes_until_buffer_end < size) { DBG (DBG_usb, "%s: USB-in-USB: giving 2nd buffer part\n", __func__); memcpy (bytes + bytes_until_buffer_end, bulk_read_state->buffer_out_ptr, size - bytes_until_buffer_end); bulk_read_state->buffer_out_ptr += size - bytes_until_buffer_end; } } else { /* The data is in one buffer part (w/o wrapping) */ memcpy (bytes, bulk_read_state->buffer_out_ptr, size); bulk_read_state->buffer_out_ptr += size; if (bulk_read_state->buffer_out_ptr == bulk_read_state->buffer_end_ptr) { DBG (DBG_usb, "%s: USB-in-USB: buffer wrapped while reading\n", __func__); bulk_read_state->buffer_out_ptr = bulk_read_state->buffer; } } /* Count the amount of data transferred to the caller */ bulk_read_state->bytes_available -= size; return SANE_STATUS_GOOD; } /******************************************************************************* * USB-in-USB: bulk write * * Parameters * dn - sanei_usb device descriptor * cmd - command for bulk write operation * bytes - pointer to data buffer * size - size of data * * Returns * SANE_STATUS_GOOD - all data transferred successfully * all other SANE_Status value - otherwise */ static SANE_Status hp5590_bulk_write (SANE_Int dn, enum proto_flags proto_flags, int cmd, unsigned char *bytes, unsigned int size) { struct usb_in_usb_bulk_setup ctrl; SANE_Status ret; struct bulk_size bulk_size; unsigned int len; unsigned char *ptr; size_t next_portion; DBG (3, "%s: USB-in-USB: command: %04x, size %u\n", __func__, cmd, size); hp5590_low_assert (bytes != NULL); /* Prepare bulk write request */ memset (&bulk_size, 0, sizeof (bulk_size)); /* Counted in page size */ bulk_size.size = size / BULK_WRITE_PAGE_SIZE; /* Send bulk write request */ DBG (3, "%s: USB-in-USB: total %u pages (each of %u bytes)\n", __func__, bulk_size.size, BULK_WRITE_PAGE_SIZE); ret = hp5590_control_msg (dn, proto_flags, USB_DIR_OUT, 0x04, cmd, 0, (unsigned char *) &bulk_size, sizeof (bulk_size), CORE_DATA | CORE_BULK_OUT); if (ret != SANE_STATUS_GOOD) return ret; len = size; ptr = bytes; /* Send all data in pages */ while (len) { next_portion = BULK_WRITE_PAGE_SIZE; if (len < next_portion) next_portion = len; DBG (3, "%s: USB-in-USB: next portion %lu bytes\n", __func__, (u_long) next_portion); /* Prepare bulk write request */ memset (&ctrl, 0, sizeof (ctrl)); ctrl.bRequestType = 0x01; ctrl.bEndpoint = 0x82; ctrl.wLength = htons (next_portion); /* Send bulk write request */ ret = sanei_usb_control_msg (dn, USB_DIR_OUT | USB_TYPE_VENDOR, 0x04, 0x82, 0, sizeof (ctrl), (unsigned char *) &ctrl); if (ret != SANE_STATUS_GOOD) return ret; /* USB-in-USB: checking if command was accepted */ ret = hp5590_get_ack (dn, proto_flags); if (ret != SANE_STATUS_GOOD) return ret; /* Write bulk data */ DBG (3, "%s: USB-in-USB: bulk writing %lu bytes\n", __func__, (u_long) next_portion); ret = sanei_usb_write_bulk (dn, ptr, &next_portion); if (ret != SANE_STATUS_GOOD) { /* Treast EOF as successful result */ if (ret == SANE_STATUS_EOF) break; DBG (DBG_err, "%s: USB-in-USB: error during bulk write: %s\n", __func__, sane_strstatus (ret)); return ret; } /* Move to the next page */ len -= next_portion; ptr += next_portion; } /* Verify bulk command */ return hp5590_verify_last_cmd (dn, proto_flags, cmd); } /* vim: sw=2 ts=8 */ backends-1.3.0/backend/hp5590_low.h000066400000000000000000000076431456256263500166700ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2007 Ilia Sotnikov 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, see . 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 is part of a SANE backend for HP ScanJet 4500C/4570C/5500C/5550C/5590/7650 Scanners */ #ifndef HP5590_LOW_H #define HP5590_LOW_H #include "../include/sane/sane.h" enum proto_flags { PF_NONE = 0, PF_NO_USB_IN_USB_ACK = 1 << 0 /* Getting acknowledge after USB-in-USB command * will be skipped */ }; /* Flags for hp5590_cmd() */ #define CMD_IN 1 << 0 /* Indicates IN direction, otherwise - OUT */ #define CMD_VERIFY 1 << 1 /* Requests last command verification */ /* Core flags for hp5590_cmd() - they indicate so called CORE commands */ #define CORE_NONE 0 /* No CORE operation */ #define CORE_DATA 1 << 0 /* Operate on CORE data */ #define CORE_BULK_IN 1 << 1 /* CORE bulk operation - prepare for bulk IN * transfer (not used yet) */ #define CORE_BULK_OUT 1 << 2 /* CORE bulk operation - prepare for bulk OUT * transfer */ static SANE_Status hp5590_cmd (SANE_Int dn, enum proto_flags proto_flags, unsigned int flags, unsigned int cmd, unsigned char *data, unsigned int size, unsigned int core_flags); static SANE_Status hp5590_bulk_read (SANE_Int dn, enum proto_flags proto_flags, unsigned char *bytes, unsigned int size, void *state); static SANE_Status hp5590_bulk_write (SANE_Int dn, enum proto_flags proto_flags, int cmd, unsigned char *bytes, unsigned int size); static SANE_Status hp5590_get_status (SANE_Int dn, enum proto_flags proto_flags); static SANE_Status hp5590_low_init_bulk_read_state (void **state); static SANE_Status hp5590_low_free_bulk_read_state (void **state); #endif /* HP5590_LOW_H */ /* vim: sw=2 ts=8 */ backends-1.3.0/backend/hpljm1005.c000066400000000000000000000727611456256263500164730ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2007-2008 Philippe Rétornaz 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, see . 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 backend is for HP LaserJet M1005 MFP Highly inspired from the epson backend */ #define BUILD 1 #include "../include/sane/config.h" #include #include #include #include #include #include #include #define BACKEND_NAME hpljm1005 #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/saneopts.h" #define MAGIC_NUMBER 0x41535001 #define PKT_READ_STATUS 0x0 #define PKT_UNKNOW_1 0x1 #define PKT_START_SCAN 0x2 #define PKT_GO_IDLE 0x3 #define PKT_DATA 0x5 #define PKT_READCONF 0x6 #define PKT_SETCONF 0x7 #define PKT_END_DATA 0xe #define PKT_RESET 0x15 #define RED_LAYER 0x3 #define GREEN_LAYER 0x4 #define BLUE_LAYER 0x5 #define GRAY_LAYER 0x6 #define MIN_SCAN_ZONE 101 struct usbdev_s { SANE_Int vendor_id; SANE_Int product_id; SANE_String_Const vendor_s; SANE_String_Const model_s; SANE_String_Const type_s; }; /* Zero-terminated USB VID/PID array */ static struct usbdev_s usbid[] = { {0x03f0, 0x3b17, "Hewlett-Packard", "LaserJet M1005", "multi-function peripheral"}, {0x03f0, 0x5617, "Hewlett-Packard", "LaserJet M1120", "multi-function peripheral"}, {0x03f0, 0x5717, "Hewlett-Packard", "LaserJet M1120n", "multi-function peripheral"}, {0, 0, NULL, NULL, NULL}, {0, 0, NULL, NULL, NULL} }; static int cur_idx; #define BR_CONT_MIN 0x1 #define BR_CONT_MAX 0xb #define RGB 1 #define GRAY 0 #define MAX_X_H 0x351 #define MAX_Y_H 0x490 #define MAX_X_S 216 #define MAX_Y_S 297 #define OPTION_MAX 9 static SANE_Word resolution_list[] = { 7, 75, 100, 150, 200, 300, 600, 1200 }; static SANE_Range range_x = { 0, MAX_X_S, 0 }; static SANE_Range range_y = { 0, MAX_Y_S, 0 }; static SANE_Range range_br_cont = { BR_CONT_MIN, BR_CONT_MAX, 0 }; static const SANE_String_Const mode_list[] = { SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_COLOR, NULL }; #define X1_OFFSET 2 #define X2_OFFSET 4 #define Y1_OFFSET 3 #define Y2_OFFSET 5 #define RES_OFFSET 1 #define COLOR_OFFSET 8 #define BRIGH_OFFSET 6 #define CONTR_OFFSET 7 #define STATUS_IDLE 0 #define STATUS_SCANNING 1 #define STATUS_CANCELING 2 struct buffer_s { char *buffer; size_t w_offset; size_t size; }; struct device_s { struct device_s *next; SANE_String_Const devname; int idx; /* Index in the usbid array */ int dn; /* Usb "Handle" */ SANE_Option_Descriptor optiond[OPTION_MAX]; struct buffer_s buf_r; /* also for gray mode */ struct buffer_s buf_g; struct buffer_s buf_b; int read_offset; int status; int width; int height; int height_h; int data_width; /* width + some padding 0xFF which should be ignored */ int scanned_pixels; SANE_Word optionw[OPTION_MAX]; uint32_t conf_data[512]; uint32_t packet_data[512]; }; static void do_cancel(struct device_s *dev); static struct device_s *devlist_head; static int devlist_count; /* Number of element in the list */ /* * List of pointers to devices - will be dynamically allocated depending * on the number of devices found. */ static SANE_Device **devlist = NULL; /* round() is c99, so we provide our own, though this version won't return -0 */ static double round2(double x) { return (double)(x >= 0.0) ? (int)(x+0.5) : (int)(x-0.5); } /* This function is copy/pasted from the Epson backend */ static size_t max_string_size (const SANE_String_Const strings[]) { size_t size, max_size = 0; int i; for (i = 0; strings[i]; i++) { size = strlen (strings[i]) + 1; if (size > max_size) max_size = size; } return max_size; } static SANE_Status attach (SANE_String_Const devname) { struct device_s *dev; dev = malloc (sizeof (struct device_s)); if (!dev) return SANE_STATUS_NO_MEM; memset (dev, 0, sizeof (struct device_s)); dev->devname = devname; DBG(1,"New device found: %s\n",dev->devname); /* Init the whole structure with default values */ /* Number of options */ dev->optiond[0].name = ""; dev->optiond[0].title = NULL; dev->optiond[0].desc = NULL; dev->optiond[0].type = SANE_TYPE_INT; dev->optiond[0].unit = SANE_UNIT_NONE; dev->optiond[0].size = sizeof (SANE_Word); dev->optionw[0] = OPTION_MAX; /* resolution */ dev->optiond[RES_OFFSET].name = "resolution"; dev->optiond[RES_OFFSET].title = "resolution"; dev->optiond[RES_OFFSET].desc = "resolution"; dev->optiond[RES_OFFSET].type = SANE_TYPE_INT; dev->optiond[RES_OFFSET].unit = SANE_UNIT_DPI; dev->optiond[RES_OFFSET].type = SANE_TYPE_INT; dev->optiond[RES_OFFSET].size = sizeof (SANE_Word); dev->optiond[RES_OFFSET].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; dev->optiond[RES_OFFSET].constraint_type = SANE_CONSTRAINT_WORD_LIST; dev->optiond[RES_OFFSET].constraint.word_list = resolution_list; dev->optionw[RES_OFFSET] = 75; /* scan area */ dev->optiond[X1_OFFSET].name = "tl-x"; dev->optiond[X1_OFFSET].title = "tl-x"; dev->optiond[X1_OFFSET].desc = "tl-x"; dev->optiond[X1_OFFSET].type = SANE_TYPE_INT; dev->optiond[X1_OFFSET].unit = SANE_UNIT_MM; dev->optiond[X1_OFFSET].size = sizeof (SANE_Word); dev->optiond[X1_OFFSET].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; dev->optiond[X1_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE; dev->optiond[X1_OFFSET].constraint.range = &range_x; dev->optionw[X1_OFFSET] = 0; dev->optiond[Y1_OFFSET].name = "tl-y"; dev->optiond[Y1_OFFSET].title = "tl-y"; dev->optiond[Y1_OFFSET].desc = "tl-y"; dev->optiond[Y1_OFFSET].type = SANE_TYPE_INT; dev->optiond[Y1_OFFSET].unit = SANE_UNIT_MM; dev->optiond[Y1_OFFSET].size = sizeof (SANE_Word); dev->optiond[Y1_OFFSET].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; dev->optiond[Y1_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE; dev->optiond[Y1_OFFSET].constraint.range = &range_y; dev->optionw[Y1_OFFSET] = 0; dev->optiond[X2_OFFSET].name = "br-x"; dev->optiond[X2_OFFSET].title = "br-x"; dev->optiond[X2_OFFSET].desc = "br-x"; dev->optiond[X2_OFFSET].type = SANE_TYPE_INT; dev->optiond[X2_OFFSET].unit = SANE_UNIT_MM; dev->optiond[X2_OFFSET].size = sizeof (SANE_Word); dev->optiond[X2_OFFSET].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; dev->optiond[X2_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE; dev->optiond[X2_OFFSET].constraint.range = &range_x; dev->optionw[X2_OFFSET] = MAX_X_S; dev->optiond[Y2_OFFSET].name = "br-y"; dev->optiond[Y2_OFFSET].title = "br-y"; dev->optiond[Y2_OFFSET].desc = "br-y"; dev->optiond[Y2_OFFSET].type = SANE_TYPE_INT; dev->optiond[Y2_OFFSET].unit = SANE_UNIT_MM; dev->optiond[Y2_OFFSET].size = sizeof (SANE_Word); dev->optiond[Y2_OFFSET].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; dev->optiond[Y2_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE; dev->optiond[Y2_OFFSET].constraint.range = &range_y; dev->optionw[Y2_OFFSET] = MAX_Y_S; /* brightness */ dev->optiond[BRIGH_OFFSET].name = "brightness"; dev->optiond[BRIGH_OFFSET].title = "Brightness"; dev->optiond[BRIGH_OFFSET].desc = "Set the brightness"; dev->optiond[BRIGH_OFFSET].type = SANE_TYPE_INT; dev->optiond[BRIGH_OFFSET].unit = SANE_UNIT_NONE; dev->optiond[BRIGH_OFFSET].size = sizeof (SANE_Word); dev->optiond[BRIGH_OFFSET].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; dev->optiond[BRIGH_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE; dev->optiond[BRIGH_OFFSET].constraint.range = &range_br_cont; dev->optionw[BRIGH_OFFSET] = 0x6; /* contrast */ dev->optiond[CONTR_OFFSET].name = "contrast"; dev->optiond[CONTR_OFFSET].title = "Contrast"; dev->optiond[CONTR_OFFSET].desc = "Set the contrast"; dev->optiond[CONTR_OFFSET].type = SANE_TYPE_INT; dev->optiond[CONTR_OFFSET].unit = SANE_UNIT_NONE; dev->optiond[CONTR_OFFSET].size = sizeof (SANE_Word); dev->optiond[CONTR_OFFSET].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; dev->optiond[CONTR_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE; dev->optiond[CONTR_OFFSET].constraint.range = &range_br_cont; dev->optionw[CONTR_OFFSET] = 0x6; /* Color */ dev->optiond[COLOR_OFFSET].name = SANE_NAME_SCAN_MODE; dev->optiond[COLOR_OFFSET].title = SANE_TITLE_SCAN_MODE; dev->optiond[COLOR_OFFSET].desc = SANE_DESC_SCAN_MODE; dev->optiond[COLOR_OFFSET].type = SANE_TYPE_STRING; dev->optiond[COLOR_OFFSET].size = max_string_size (mode_list); dev->optiond[COLOR_OFFSET].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; dev->optiond[COLOR_OFFSET].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->optiond[COLOR_OFFSET].constraint.string_list = mode_list; dev->optionw[COLOR_OFFSET] = RGB; dev->dn = 0; dev->idx = cur_idx; dev->status = STATUS_IDLE; dev->next = devlist_head; devlist_head = dev; devlist_count++; return SANE_STATUS_GOOD; } SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize) { if (version_code != NULL) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); DBG_INIT(); sanei_usb_init (); return SANE_STATUS_GOOD; } void sane_exit (void) { /* free everything */ struct device_s *iter; if (devlist) { int i; for (i = 0; devlist[i]; i++) free (devlist[i]); free (devlist); devlist = NULL; } if (devlist_head) { iter = devlist_head->next; free (devlist_head); devlist_head = NULL; while (iter) { struct device_s *tmp = iter; iter = iter->next; free (tmp); } } devlist_count = 0; } SANE_Status sane_get_devices (const SANE_Device * **device_list, SANE_Bool __sane_unused__ local_only) { struct device_s *iter; int i; devlist_count = 0; if (devlist_head) { iter = devlist_head->next; free (devlist_head); devlist_head = NULL; while (iter) { struct device_s *tmp = iter; iter = iter->next; free (tmp); } } /* Rebuild our internal scanner list */ for (cur_idx = 0; usbid[cur_idx].vendor_id; cur_idx++) sanei_usb_find_devices (usbid[cur_idx].vendor_id, usbid[cur_idx].product_id, attach); if (devlist) { for (i = 0; devlist[i]; i++) free (devlist[i]); free (devlist); } /* rebuild the sane-API scanner list array */ devlist = malloc (sizeof (devlist[0]) * (devlist_count + 1)); if (!devlist) return SANE_STATUS_NO_MEM; memset (devlist, 0, sizeof (devlist[0]) * (devlist_count + 1)); for (i = 0, iter = devlist_head; i < devlist_count; i++, iter = iter->next) { devlist[i] = malloc (sizeof (SANE_Device)); if (!devlist[i]) { int j; for (j = 0; j < i; j++) free (devlist[j]); free (devlist); devlist = NULL; return SANE_STATUS_NO_MEM; } devlist[i]->name = iter->devname; devlist[i]->vendor = usbid[iter->idx].vendor_s; devlist[i]->model = usbid[iter->idx].model_s; devlist[i]->type = usbid[iter->idx].type_s; } if (device_list) *device_list = (const SANE_Device **) devlist; return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const name, SANE_Handle * h) { struct device_s *dev; int ret; if(!devlist_head) sane_get_devices(NULL,(SANE_Bool)0); dev = devlist_head; if (strlen (name)) for (; dev; dev = dev->next) if (!strcmp (name, dev->devname)) break; if (!dev) { DBG(1,"Unable to find device %s\n",name); return SANE_STATUS_INVAL; } DBG(1,"Found device %s\n",name); /* Now open the usb device */ ret = sanei_usb_open (name, &(dev->dn)); if (ret != SANE_STATUS_GOOD) { DBG(1,"Unable to open device %s\n",name); return ret; } /* Claim the first interface */ ret = sanei_usb_claim_interface (dev->dn, 0); if (ret != SANE_STATUS_GOOD) { sanei_usb_close (dev->dn); /* if we cannot claim the interface, this is because someone else is using it */ DBG(1,"Unable to claim scanner interface on device %s\n",name); return SANE_STATUS_DEVICE_BUSY; } #ifdef HAVE_SANEI_USB_SET_TIMEOUT sanei_usb_set_timeout (30000); /* 30s timeout */ #endif *h = dev; return SANE_STATUS_GOOD; } void sane_close (SANE_Handle h) { struct device_s *dev = (struct device_s *) h; /* Just in case if sane_cancel() is called * after starting a scan but not while a sane_read */ if (dev->status == STATUS_CANCELING) { do_cancel(dev); } sanei_usb_release_interface (dev->dn, 0); sanei_usb_close (dev->dn); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle h, SANE_Int option) { struct device_s *dev = (struct device_s *) h; if (option >= OPTION_MAX || option < 0) return NULL; return &(dev->optiond[option]); } static SANE_Status getvalue (SANE_Handle h, SANE_Int option, void *v) { struct device_s *dev = (struct device_s *) h; if (option != COLOR_OFFSET) *((SANE_Word *) v) = dev->optionw[option]; else { strcpy ((char *) v, dev->optiond[option].constraint.string_list[dev-> optionw[option]]); } return SANE_STATUS_GOOD; } static SANE_Status setvalue (SANE_Handle h, SANE_Int option, void *value, SANE_Int * info) { struct device_s *dev = (struct device_s *) h; SANE_Status status = SANE_STATUS_GOOD; int s_unit; int s_unit_2; if (option == 0) return SANE_STATUS_UNSUPPORTED; status = sanei_constrain_value (&(dev->optiond[option]), value, info); if (status != SANE_STATUS_GOOD) return status; if (info) *info |= SANE_INFO_RELOAD_PARAMS; switch (option) { case X1_OFFSET: dev->optionw[option] = *((SANE_Word *) value); s_unit = (int) round2 ((dev->optionw[option] / ((double) MAX_X_S)) * MAX_X_H); s_unit_2 = (int) round2 ((dev->optionw[X2_OFFSET] / ((double) MAX_X_S)) * MAX_X_H); if (abs (s_unit_2 - s_unit) < MIN_SCAN_ZONE) s_unit = s_unit_2 - MIN_SCAN_ZONE; dev->optionw[option] = round2 ((s_unit / ((double) MAX_X_H)) * MAX_X_S); if (info) *info |= SANE_INFO_INEXACT; break; case X2_OFFSET: /* X units */ /* convert into "scanner" unit, then back into mm */ dev->optionw[option] = *((SANE_Word *) value); s_unit = (int) round2 ((dev->optionw[option] / ((double) MAX_X_S)) * MAX_X_H); s_unit_2 = (int) round2 ((dev->optionw[X1_OFFSET] / ((double) MAX_X_S)) * MAX_X_H); if (abs (s_unit_2 - s_unit) < MIN_SCAN_ZONE) s_unit = s_unit_2 + MIN_SCAN_ZONE; dev->optionw[option] = round2 ((s_unit / ((double) MAX_X_H)) * MAX_X_S); if (info) *info |= SANE_INFO_INEXACT; break; case Y1_OFFSET: /* Y units */ dev->optionw[option] = *((SANE_Word *) value); s_unit = (int) round2 ((dev->optionw[option] / ((double) MAX_Y_S)) * MAX_Y_H); s_unit_2 = (int) round2 ((dev->optionw[Y2_OFFSET] / ((double) MAX_Y_S)) * MAX_Y_H); if (abs (s_unit_2 - s_unit) < MIN_SCAN_ZONE) s_unit = s_unit_2 - MIN_SCAN_ZONE; dev->optionw[option] = round2 ((s_unit / ((double) MAX_Y_H)) * MAX_Y_S); if (info) *info |= SANE_INFO_INEXACT; break; case Y2_OFFSET: /* Y units */ dev->optionw[option] = *((SANE_Word *) value); s_unit = (int) round2 ((dev->optionw[option] / ((double) MAX_Y_S)) * MAX_Y_H); s_unit_2 = (int) round2 ((dev->optionw[Y1_OFFSET] / ((double) MAX_Y_S)) * MAX_Y_H); if (abs (s_unit_2 - s_unit) < MIN_SCAN_ZONE) s_unit = s_unit_2 + MIN_SCAN_ZONE; dev->optionw[option] = round2 ((s_unit / ((double) MAX_Y_H)) * MAX_Y_S); if (info) *info |= SANE_INFO_INEXACT; break; case COLOR_OFFSET: if (!strcmp ((char *) value, mode_list[0])) dev->optionw[option] = GRAY; /* Gray */ else if (!strcmp ((char *) value, mode_list[1])) dev->optionw[option] = RGB; /* RGB */ else return SANE_STATUS_INVAL; break; default: dev->optionw[option] = *((SANE_Word *) value); } return SANE_STATUS_GOOD; } SANE_Status sane_control_option (SANE_Handle h, SANE_Int option, SANE_Action a, void *v, SANE_Int * i) { if (option < 0 || option >= OPTION_MAX) return SANE_STATUS_INVAL; if (i) *i = 0; switch (a) { case SANE_ACTION_GET_VALUE: return getvalue (h, option, v); case SANE_ACTION_SET_VALUE: return setvalue (h, option, v, i); default: return SANE_STATUS_INVAL; } } SANE_Status sane_get_parameters (SANE_Handle h, SANE_Parameters * p) { struct device_s *dev = (struct device_s *) h; if (!p) return SANE_STATUS_INVAL; p->format = dev->optionw[COLOR_OFFSET] == RGB ? SANE_FRAME_RGB : SANE_FRAME_GRAY; p->last_frame = SANE_TRUE; p->depth = 8; p->pixels_per_line = dev->width; p->lines = dev->height; p->bytes_per_line = p->pixels_per_line; if (p->format == SANE_FRAME_RGB) p->bytes_per_line *= 3; return SANE_STATUS_GOOD; } static void send_pkt (int command, int data_size, struct device_s *dev) { size_t size = 32; DBG(100,"Sending packet %d, next data size %d, device %s\n", command, data_size, dev->devname); memset (dev->packet_data, 0, size); dev->packet_data[0] = htonl (MAGIC_NUMBER); dev->packet_data[1] = htonl (command); dev->packet_data[5] = htonl (data_size); sanei_usb_write_bulk (dev->dn, (unsigned char *) dev->packet_data, &size); } /* s: printer status */ /* Return the next packet size */ static int wait_ack (struct device_s *dev, int *s) { SANE_Status ret; size_t size; DBG(100, "Waiting scanner answer on device %s\n",dev->devname); do { size = 32; ret = sanei_usb_read_bulk (dev->dn, (unsigned char *) dev->packet_data, &size); } while (SANE_STATUS_EOF == ret || size == 0); if (s) *s = ntohl (dev->packet_data[4]); return ntohl (dev->packet_data[5]); } static void send_conf (struct device_s *dev) { int y1, y2, x1, x2; size_t size = 100; DBG(100,"Sending configuration packet on device %s\n",dev->devname); y1 = (int) round2 ((dev->optionw[Y1_OFFSET] / ((double) MAX_Y_S)) * MAX_Y_H); y2 = (int) round2 ((dev->optionw[Y2_OFFSET] / ((double) MAX_Y_S)) * MAX_Y_H); x1 = (int) round2 ((dev->optionw[X1_OFFSET] / ((double) MAX_X_S)) * MAX_X_H); x2 = (int) round2 ((dev->optionw[X2_OFFSET] / ((double) MAX_X_S)) * MAX_X_H); DBG(100,"\t x1: %d, x2: %d, y1: %d, y2: %d\n",x1, x2, y1, y2); DBG(100,"\t brightness: %d, contrast: %d\n", dev->optionw[BRIGH_OFFSET], dev->optionw[CONTR_OFFSET]); DBG(100,"\t resolution: %d\n",dev->optionw[RES_OFFSET]); dev->conf_data[0] = htonl (0x15); dev->conf_data[1] = htonl (dev->optionw[BRIGH_OFFSET]); dev->conf_data[2] = htonl (dev->optionw[CONTR_OFFSET]); dev->conf_data[3] = htonl (dev->optionw[RES_OFFSET]); dev->conf_data[4] = htonl (0x1); dev->conf_data[5] = htonl (0x1); dev->conf_data[6] = htonl (0x1); dev->conf_data[7] = htonl (0x1); dev->conf_data[8] = 0; dev->conf_data[9] = 0; dev->conf_data[10] = htonl (0x8); dev->conf_data[11] = 0; dev->conf_data[12] = 0; dev->conf_data[13] = 0; dev->conf_data[14] = 0; dev->conf_data[16] = htonl (y1); dev->conf_data[17] = htonl (x1); dev->conf_data[18] = htonl (y2); dev->conf_data[19] = htonl (x2); dev->conf_data[20] = 0; dev->conf_data[21] = 0; dev->conf_data[22] = htonl (0x491); dev->conf_data[23] = htonl (0x352); dev->height_h = y2 - y1; if (dev->optionw[COLOR_OFFSET] == RGB) { dev->conf_data[15] = htonl (0x2); dev->conf_data[24] = htonl (0x1); DBG(100,"\t Scanning in RGB format\n"); } else { dev->conf_data[15] = htonl (0x6); dev->conf_data[24] = htonl (0x0); DBG(100,"\t Scanning in Grayscale format\n"); } sanei_usb_write_bulk (dev->dn, (unsigned char *) dev->conf_data, &size); } static SANE_Status create_buffer(struct buffer_s *buf, int buffer_size) { if (buf->buffer) { free(buf->buffer); } buf->buffer = malloc(buffer_size); if (!buf->buffer) return SANE_STATUS_NO_MEM; buf->size = buffer_size; buf->w_offset = 0; return SANE_STATUS_GOOD; } static SANE_Status create_buffers(struct device_s *dev, int buf_size) { if (create_buffer(&dev->buf_r, buf_size) != SANE_STATUS_GOOD) return SANE_STATUS_NO_MEM; if (dev->optionw[COLOR_OFFSET] == RGB) { if (create_buffer(&dev->buf_g, buf_size) != SANE_STATUS_GOOD) return SANE_STATUS_NO_MEM; if (create_buffer(&dev->buf_b, buf_size) != SANE_STATUS_GOOD) return SANE_STATUS_NO_MEM; } return SANE_STATUS_GOOD; } static SANE_Status remove_buffers(struct device_s *dev) { if (dev->buf_r.buffer) free(dev->buf_r.buffer); if (dev->buf_g.buffer) free(dev->buf_g.buffer); if (dev->buf_b.buffer) free(dev->buf_b.buffer); dev->buf_r.w_offset = dev->buf_g.w_offset = dev->buf_b.w_offset = 0; dev->buf_r.size = dev->buf_g.size = dev->buf_b.size = 0; dev->buf_r.buffer = dev->buf_g.buffer = dev->buf_b.buffer = NULL; dev->read_offset = 0; return SANE_STATUS_GOOD; } static SANE_Status get_data (struct device_s *dev) { int color; size_t size; int packet_size; unsigned char *buffer = (unsigned char *) dev->packet_data; if (dev->status == STATUS_IDLE) { DBG(101, "STATUS == IDLE\n"); return SANE_STATUS_IO_ERROR; } /* first wait a standard data pkt */ do { size = 32; sanei_usb_read_bulk (dev->dn, buffer, &size); if (size) { if (ntohl (dev->packet_data[0]) == MAGIC_NUMBER) { if (ntohl (dev->packet_data[1]) == PKT_DATA) break; if (ntohl (dev->packet_data[1]) == PKT_END_DATA) { dev->status = STATUS_IDLE; DBG(100,"End of scan encountered on device %s\n",dev->devname); send_pkt (PKT_GO_IDLE, 0, dev); wait_ack (dev, NULL); wait_ack (dev, NULL); send_pkt (PKT_UNKNOW_1, 0, dev); wait_ack (dev, NULL); send_pkt (PKT_RESET, 0, dev); sleep (2); /* Time for the scanning head to go back home */ return SANE_STATUS_EOF; } } } } while (1); packet_size = ntohl (dev->packet_data[5]); if (! dev->buf_r.buffer) { /* For some reason scanner sends packets in order: R G B ... R G B R G B RRR GGG BBB To hanle the last triple portion create a triple size buffer */ int buf_size = (packet_size - 24) * 3; /* 24 - size of header */ ; if (create_buffers(dev, buf_size) != SANE_STATUS_GOOD) return SANE_STATUS_NO_MEM; } /* Get the "data header" */ do { size = 24; sanei_usb_read_bulk (dev->dn, buffer, &size); } while (!size); color = ntohl (dev->packet_data[0]); packet_size -= size; dev->width = ntohl (dev->packet_data[4]); dev->height = dev->height_h * dev->optionw[RES_OFFSET] / 100; dev->data_width = ntohl (dev->packet_data[5]); DBG(100,"Got data size %d on device %s. Scan width: %d, data width: %d\n",packet_size, dev->devname, dev->width, dev->data_width); /* Now, read the data */ do { int ret; do { size = packet_size > 512 ? 512 : packet_size; ret = sanei_usb_read_bulk (dev->dn, buffer, &size); } while (!size || ret != SANE_STATUS_GOOD); packet_size -= size; struct buffer_s * current_buf; char color_char; switch (color) { case GRAY_LAYER: color_char = 'g'; current_buf = &dev->buf_r; break; case RED_LAYER: color_char = 'R'; current_buf = &dev->buf_r; break; case GREEN_LAYER: color_char = 'G'; current_buf = &dev->buf_g; break; case BLUE_LAYER: color_char = 'B'; current_buf = &dev->buf_b; break; default: DBG(101, "Unknown color code: %d \n", color); return SANE_STATUS_IO_ERROR; } DBG(101,"Got %c layer data on device %s\n", color_char, dev->devname); if (current_buf->w_offset + size > current_buf->size) { DBG(100, "buffer overflow\n"); return SANE_STATUS_IO_ERROR; } memcpy(current_buf->buffer + current_buf->w_offset, buffer, size); current_buf->w_offset += size; } while (packet_size > 0); return SANE_STATUS_GOOD; } SANE_Status sane_start (SANE_Handle h) { struct device_s *dev = (struct device_s *) h; int status; size_t size; dev->read_offset = 0; dev->scanned_pixels = 0; remove_buffers(dev); send_pkt (PKT_RESET, 0, dev); send_pkt (PKT_READ_STATUS, 0, dev); wait_ack (dev, &status); if (status) return SANE_STATUS_IO_ERROR; send_pkt (PKT_READCONF, 0, dev); if ((size = wait_ack (dev, NULL))) { sanei_usb_read_bulk (dev->dn, (unsigned char *) dev->conf_data, &size); } send_pkt (PKT_SETCONF, 100, dev); send_conf (dev); wait_ack (dev, NULL); send_pkt (PKT_START_SCAN, 0, dev); wait_ack (dev, NULL); if ((size = wait_ack (dev, NULL))) { sanei_usb_read_bulk (dev->dn, (unsigned char *) dev->conf_data, &size); } if ((size = wait_ack (dev, NULL))) { sanei_usb_read_bulk (dev->dn, (unsigned char *) dev->conf_data, &size); } if ((size = wait_ack (dev, NULL))) { sanei_usb_read_bulk (dev->dn, (unsigned char *) dev->conf_data, &size); } dev->status = STATUS_SCANNING; /* Get the first data */ return get_data (dev); } static void do_cancel(struct device_s *dev) { while (get_data (dev) == SANE_STATUS_GOOD); remove_buffers(dev); } static int min3 (int r, int g, int b) { if (r < g && r < b) return r; if (b < r && b < g) return b; return g; } static int min_buf_w_offset (struct device_s * dev) { if (dev->optionw[COLOR_OFFSET] == RGB) return min3 (dev->buf_r.w_offset, dev->buf_g.w_offset, dev->buf_b.w_offset); return dev->buf_r.w_offset; } static int is_buf_synchronized(struct device_s * dev) { if (dev->optionw[COLOR_OFFSET] == RGB) return dev->buf_r.w_offset == dev->buf_g.w_offset && dev->buf_r.w_offset == dev->buf_b.w_offset; return 1; } SANE_Status sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len) { struct device_s *dev = (struct device_s *) h; int available; int ret; if (dev->optionw[COLOR_OFFSET] == RGB) { maxlen /= 3; } *len = 0; if (dev->status == STATUS_IDLE) { DBG(101, "STATUS == IDLE\n"); return SANE_STATUS_IO_ERROR; } while (min_buf_w_offset(dev) <= dev->read_offset) { ret = get_data (dev); if (ret != SANE_STATUS_GOOD) { if (min_buf_w_offset(dev) <= dev->read_offset) { return ret; } } } available = min_buf_w_offset(dev); int pixel_len = available - dev->read_offset; if (pixel_len > maxlen) pixel_len = maxlen; int img_size = dev->width * dev->height; for(int i=0; iread_offset+i; if (pos % dev->data_width >= dev->width) continue; if (dev->scanned_pixels >= img_size) { DBG(101, "Extra pixels received.\n"); break; } dev->scanned_pixels++; buf[(*len)++] = dev->buf_r.buffer[pos]; if (dev->optionw[COLOR_OFFSET] == RGB) { buf[(*len)++] = dev->buf_g.buffer[pos]; buf[(*len)++] = dev->buf_b.buffer[pos]; } } DBG(101, "Moved %d pixels to buffer. Total pixel scanned: %d \n", *len, dev->scanned_pixels); if (dev->scanned_pixels == img_size) DBG(100, "Full image received\n"); dev->read_offset += pixel_len; /* If w_offset is the same in all buffers and has already been completely transferred to the common buffer - flush buffer. It will be recreated in get_data with a reserve for the last triple portions */ if (is_buf_synchronized(dev) && available == dev->read_offset) { remove_buffers(dev); } /* Special case where sane_cancel is called while scanning */ if (dev->status == STATUS_CANCELING) { do_cancel(dev); return SANE_STATUS_CANCELLED; } return SANE_STATUS_GOOD; } void sane_cancel (SANE_Handle h) { struct device_s *dev = (struct device_s *) h; if (dev->status == STATUS_SCANNING) { dev->status = STATUS_CANCELING; return; } remove_buffers(dev); } SANE_Status sane_set_io_mode (SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking) { return SANE_STATUS_UNSUPPORTED; } SANE_Status sane_get_select_fd (SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ * fd) { return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/hpsj5s.c000066400000000000000000001173611456256263500162630ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2002 Max Vorobiev 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, see . 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. */ #define BUILD 3 #define BACKEND_NAME hpsj5s #define HPSJ5S_CONFIG_FILE "hpsj5s.conf" #include "../include/sane/config.h" #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_backend.h" #include "hpsj5s.h" #include #include #include #include #define LINES_TO_FEED 480 /*Default feed length */ static int scanner_d = -1; /*This is handler to the only-supported. Will be fixed. */ static char scanner_path[PATH_MAX] = ""; /*String for device-file */ static SANE_Byte bLastCalibration; /*Here we store calibration result */ static SANE_Byte bCalibration; /*Here we store new calibration value */ static SANE_Byte bHardwareState; /*Here we store copy of hardware flags register */ /*Here we store Parameters:*/ static SANE_Word wWidth = 2570; /*Scan area width */ static SANE_Word wResolution = 300; /*Resolution in DPI */ static SANE_Frame wCurrentFormat = SANE_FRAME_GRAY; /*Type of colors in image */ static SANE_Int wCurrentDepth = 8; /*Bits per pixel in image */ /*Here we count lines of every new image...*/ static SANE_Word wVerticalResolution; /*Limits for resolution control*/ static const SANE_Range ImageWidthRange = { 0, /*minimal */ 2570, /*maximum */ 2 /*quant */ }; static const SANE_Word ImageResolutionsList[] = { 6, /*Number of resolutions */ 75, 100, 150, 200, 250, 300 }; static SANE_Option_Descriptor sod[] = { { /*Number of options */ SANE_NAME_NUM_OPTIONS, SANE_TITLE_NUM_OPTIONS, SANE_DESC_NUM_OPTIONS, SANE_TYPE_INT, SANE_UNIT_NONE, sizeof (SANE_Word), SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_NONE, {NULL} /*No constraints required */ } , { /*Width of scanned area */ "width", "Width", "Width of area to scan", SANE_TYPE_INT, SANE_UNIT_PIXEL, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_RANGE, {NULL} /*Range constraint set in sane_init */ } , { /*Resolution for scan */ "resolution", "Resolution", "Image resolution", SANE_TYPE_INT, SANE_UNIT_DPI, sizeof (SANE_Word), SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, SANE_CONSTRAINT_WORD_LIST, {NULL} /*Word list constraint set in sane_init */ } }; static SANE_Parameters parms; /*Recalculate Length in dependence of resolution*/ static SANE_Word LengthForRes (SANE_Word Resolution, SANE_Word Length) { switch (Resolution) { case 75: return Length / 4; case 100: return Length / 3; case 150: return Length / 2; case 200: return Length * 2 / 3; case 250: return Length * 5 / 6; case 300: default: return Length; } } static struct parport_list pl; /*List of detected parallel ports. */ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { char line[PATH_MAX]; /*Line from config file */ FILE *config_file; /*Handle to config file of this backend */ DBG_INIT (); DBG (1, ">>sane_init"); DBG (2, "sane_init: version_code %s 0, authorize %s 0\n", version_code == 0 ? "=" : "!=", authorize == 0 ? "=" : "!="); DBG (1, "sane_init: SANE hpsj5s backend version %d.%d.%d\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); /*Inform about supported version */ if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); /*Open configuration file for this backend */ config_file = sanei_config_open (HPSJ5S_CONFIG_FILE); if (!config_file) /*Failed to open config file */ { DBG (1, "sane_init: no config file found."); return SANE_STATUS_GOOD; } /*Read line by line */ while (sanei_config_read (line, PATH_MAX, config_file)) { if ((line[0] == '#') || (line[0] == '\0')) /*comment line or empty line */ continue; strcpy (scanner_path, line); /*so, we choose last in file (uncommented) */ } fclose (config_file); /*We don't need config file any more */ /*sanei_config_attach_matching_devices(devname, attach_one); To do latter */ scanner_d = -1; /*scanner device not opened yet. */ DBG (1, "<= NELEMS (dev)) /*No such device */ return SANE_STATUS_INVAL; if (scanner_d != -1) /*scanner opened already! */ return SANE_STATUS_DEVICE_BUSY; DBG (1, "sane_open: scanner device path name is \'%s\'\n", scanner_path); scanner_d = OpenScanner (scanner_path); if (scanner_d == -1) return SANE_STATUS_DEVICE_BUSY; /*This should be done more carefully */ /*Check device. */ DBG (1, "sane_open: check scanner started."); if (DetectScanner () == 0) { /*Device malfunction! */ DBG (1, "sane_open: Device malfunction."); CloseScanner (scanner_d); scanner_d = -1; return SANE_STATUS_IO_ERROR; } DBG (1, "sane_open: Device found.All are green."); *handle = (SANE_Handle) (unsigned long)scanner_d; return SANE_STATUS_GOOD; } void sane_close (SANE_Handle handle) { DBG (2, "sane_close\n"); /*We support only single device - so ignore handle (FIX IT LATER) */ if ((handle != (SANE_Handle) (unsigned long)scanner_d) || (scanner_d == -1)) return; /* wrong device */ StandByScanner (); CloseScanner (scanner_d); scanner_d = -1; } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { DBG (2, "sane_get_option_descriptor: option = %d\n", option); if ((handle != (SANE_Handle) (unsigned long)scanner_d) || (scanner_d == -1)) return NULL; /* wrong device */ if (option < 0 || option >= NELEMS (sod)) /*No real options supported */ return NULL; return &sod[option]; /*Return demanded option */ } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int * info) { if ((handle != (SANE_Handle) (unsigned long)scanner_d) || (scanner_d == -1)) return SANE_STATUS_INVAL; /* wrong device */ if ((option >= NELEMS (sod)) || (option < 0)) /*Supported only this option */ return SANE_STATUS_INVAL; switch (option) { case 0: /*Number of options */ if (action != SANE_ACTION_GET_VALUE) /*It can be only read */ return SANE_STATUS_INVAL; *((SANE_Int *) value) = NELEMS (sod); return SANE_STATUS_GOOD; case 1: /*Scan area width */ switch (action) { case SANE_ACTION_GET_VALUE: *((SANE_Word *) value) = wWidth; return SANE_STATUS_GOOD; case SANE_ACTION_SET_VALUE: /*info should be set */ wWidth = *((SANE_Word *) value); if (info != NULL) *info = SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; default: return SANE_STATUS_INVAL; } case 2: /*Resolution */ switch (action) { case SANE_ACTION_GET_VALUE: *((SANE_Word *) value) = wResolution; return SANE_STATUS_GOOD; case SANE_ACTION_SET_VALUE: /*info should be set */ wResolution = *((SANE_Word *) value); if (info != NULL) *info = 0; return SANE_STATUS_GOOD; default: return SANE_STATUS_INVAL; } default: return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; /*For now we have no options to control */ } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { DBG (2, "sane_get_parameters\n"); if ((handle != (SANE_Handle) (unsigned long)scanner_d) || (scanner_d == -1)) return SANE_STATUS_INVAL; /* wrong device */ /*Ignore handle parameter for now. FIX it latter. */ /*These parameters are OK for gray scale mode. */ parms.depth = /*wCurrentDepth */ 8; parms.format = /*wCurrentFormat */ SANE_FRAME_GRAY; parms.last_frame = SANE_TRUE; /*For grayscale... */ parms.lines = -1; /*Unknown a priory */ parms.pixels_per_line = LengthForRes (wResolution, wWidth); /*For grayscale... */ parms.bytes_per_line = parms.pixels_per_line; /*For grayscale... */ *params = parms; return SANE_STATUS_GOOD; } SANE_Status sane_start (SANE_Handle handle) { int i; DBG (2, "sane_start\n"); if ((handle != (SANE_Handle) (unsigned long)scanner_d) || (scanner_d == -1)) return SANE_STATUS_IO_ERROR; CallFunctionWithParameter (0x93, 2); bLastCalibration = CallFunctionWithRetVal (0xA9); if (bLastCalibration == 0) bLastCalibration = -1; /*Turn on the lamp: */ CallFunctionWithParameter (FUNCTION_SETUP_HARDWARE, FLAGS_HW_LAMP_ON); bHardwareState = FLAGS_HW_LAMP_ON; /*Get average white point */ bCalibration = GetCalibration (); if (bLastCalibration - bCalibration > 16) { /*Lamp is not warm enough */ DBG (1, "sane_start: warming lamp for 30 sec.\n"); for (i = 0; i < 30; i++) sleep (1); } /*Check paper presents */ if (CheckPaperPresent () == 0) { DBG (1, "sane_start: no paper detected."); return SANE_STATUS_NO_DOCS; } CalibrateScanElements (); TransferScanParameters (GrayScale, wResolution, wWidth); /*Turn on indicator and prepare engine. */ SwitchHardwareState (FLAGS_HW_INDICATOR_OFF | FLAGS_HW_MOTOR_READY, 1); /*Feed paper */ if (PaperFeed (LINES_TO_FEED) == 0) /*Feed only for fixel length. Change it */ { DBG (1, "sane_start: paper feed failed."); SwitchHardwareState (FLAGS_HW_INDICATOR_OFF | FLAGS_HW_MOTOR_READY, 0); return SANE_STATUS_JAMMED; } /*Set paper moving speed */ TurnOnPaperPulling (GrayScale, wResolution); wVerticalResolution = 0; /*Reset counter */ return SANE_STATUS_GOOD; } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * data, SANE_Int max_length, SANE_Int * length) { SANE_Byte bFuncResult, bTest; int timeout; if (!length) { DBG (1, "sane_read: length == NULL\n"); return SANE_STATUS_INVAL; } *length = 0; if (!data) { DBG (1, "sane_read: data == NULL\n"); return SANE_STATUS_INVAL; } if ((handle != (SANE_Handle) (unsigned long)scanner_d) || (scanner_d == -1)) { DBG (1, "sane_read: unknown handle\n"); return SANE_STATUS_INVAL; } /*While end of paper sheet was not reached */ /*Wait for scanned line ready */ timeout = 0; while (((bFuncResult = CallFunctionWithRetVal (0xB2)) & 0x20) == 0) { bTest = CallFunctionWithRetVal (0xB5); usleep (1); timeout++; if ((timeout < 1000) && (((bTest & 0x80) && ((bTest & 0x3F) <= 2)) || (((bTest & 0x80) == 0) && ((bTest & 0x3F) >= 5)))) continue; if (timeout >= 1000) continue; /*do it again! */ /*Data ready state! */ if ((bFuncResult & 0x20) != 0) /*End of paper reached! */ { *length = 0; return SANE_STATUS_EOF; } /*Data ready */ *length = LengthForRes (wResolution, wWidth); if (*length >= max_length) *length = max_length; CallFunctionWithParameter (0xCD, 0); CallFunctionWithRetVal (0xC8); WriteScannerRegister (REGISTER_FUNCTION_CODE, 0xC8); WriteAddress (ADDRESS_RESULT); /*Test if we need this line for current resolution (scanner doesn't control vertical resolution in hardware) */ wVerticalResolution -= wResolution; if (wVerticalResolution > 0) { timeout = 0; continue; } else wVerticalResolution = 300; /*Reset counter */ ReadDataBlock (data, *length); /*switch indicator */ bHardwareState ^= FLAGS_HW_INDICATOR_OFF; CallFunctionWithParameter (FUNCTION_SETUP_HARDWARE, bHardwareState); return SANE_STATUS_GOOD; } return SANE_STATUS_EOF; } void sane_cancel (SANE_Handle handle) { DBG (2, "sane_cancel: handle = %p\n", handle); /*Stop motor */ TurnOffPaperPulling (); /*Indicator turn off */ bHardwareState |= FLAGS_HW_INDICATOR_OFF; CallFunctionWithParameter (FUNCTION_SETUP_HARDWARE, bHardwareState); /*Get out of paper */ ReleasePaper (); /*Restore indicator */ bHardwareState &= ~FLAGS_HW_INDICATOR_OFF; CallFunctionWithParameter (FUNCTION_SETUP_HARDWARE, bHardwareState); bLastCalibration = CallFunctionWithRetVal (0xA9); CallFunctionWithParameter (0xA9, bLastCalibration); CallFunctionWithParameter (0x93, 4); } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { DBG (2, "sane_set_io_mode: handle = %p, non_blocking = %d\n", handle, non_blocking); return SANE_STATUS_UNSUPPORTED; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { DBG (2, "sane_get_select_fd: handle = %p, fd %s 0\n", handle, fd ? "!=" : "="); return SANE_STATUS_UNSUPPORTED; } /* Middle-level API: */ /* Detect if scanner present and works correctly. Ret Val: 0 = detection failed, 1 = detection OK. */ static int DetectScanner (void) { int Result1, Result2; int Successful, Total; Result1 = OutputCheck (); Result2 = InputCheck (); if (!(Result1 || Result2)) /*If all are 0 - it's error */ { return 0; } WriteScannerRegister (0x7C, 0x80); WriteScannerRegister (0x7F, 0x1); WriteScannerRegister (0x72, 0x10); WriteScannerRegister (0x72, 0x90); WriteScannerRegister (0x7C, 0x24); WriteScannerRegister (0x75, 0x0C); WriteScannerRegister (0x78, 0x0); WriteScannerRegister (0x79, 0x10); WriteScannerRegister (0x71, 0x10); WriteScannerRegister (0x71, 0x1); WriteScannerRegister (0x72, 0x1); for (Successful = 0, Total = 0; Total < 5; Total++) { if (CallCheck ()) Successful++; if (Successful >= 3) return 1; /*Correct and Stable */ } return 0; } static void StandByScanner () { WriteScannerRegister (0x74, 0x80); WriteScannerRegister (0x75, 0x0C); WriteScannerRegister (0x77, 0x0); WriteScannerRegister (0x78, 0x0); WriteScannerRegister (0x79, 0x0); WriteScannerRegister (0x7A, 0x0); WriteScannerRegister (0x7B, 0x0); WriteScannerRegister (0x7C, 0x4); WriteScannerRegister (0x70, 0x0); WriteScannerRegister (0x72, 0x90); WriteScannerRegister (0x70, 0x0); } static void SwitchHardwareState (SANE_Byte mask, SANE_Byte invert_mask) { if (!invert_mask) { bHardwareState &= ~mask; } else bHardwareState |= mask; CallFunctionWithParameter (FUNCTION_SETUP_HARDWARE, bHardwareState); } /*return value: 0 - no paper, 1 - paper loaded.*/ static int CheckPaperPresent () { if ((CallFunctionWithRetVal (0xB2) & 0x10) == 0) return 1; /*Ok - paper present. */ return 0; /*No paper present */ } static int ReleasePaper () { int i; if ((CallFunctionWithRetVal (0xB2) & 0x20) == 0) { /*End of paper was not reached */ CallFunctionWithParameter (0xA7, 0xF); CallFunctionWithParameter (0xA8, 0xFF); CallFunctionWithParameter (0xC2, 0); for (i = 0; i < 90000; i++) { if (CallFunctionWithRetVal (0xB2) & 0x80) break; usleep (1); } if (i >= 90000) return 0; /*Fail. */ for (i = 0; i < 90000; i++) { if ((CallFunctionWithRetVal (0xB2) & 0x20) == 0) break; else if ((CallFunctionWithRetVal (0xB2) & 0x80) == 0) { i = 90000; break; } usleep (1); } CallFunctionWithParameter (0xC5, 0); if (i >= 90000) return 0; /*Fail. */ while (CallFunctionWithRetVal (0xB2) & 0x80); /*Wait bit dismiss */ CallFunctionWithParameter (0xA7, 1); CallFunctionWithParameter (0xA8, 0x25); CallFunctionWithParameter (0xC2, 0); for (i = 0; i < 90000; i++) { if (CallFunctionWithRetVal (0xB2) & 0x80) break; usleep (1); } if (i >= 90000) return 0; /*Fail. */ for (i = 0; i < 90000; i++) { if ((CallFunctionWithRetVal (0xB2) & 0x80) == 0) break; usleep (1); } if (i >= 90000) return 0; /*Fail. */ } if (CallFunctionWithRetVal (0xB2) & 0x10) { CallFunctionWithParameter (0xA7, 1); CallFunctionWithParameter (0xA8, 0x40); } else { CallFunctionWithParameter (0xA7, 0); CallFunctionWithParameter (0xA8, 0xFA); } CallFunctionWithParameter (0xC2, 0); for (i = 0; i < 9000; i++) { if (CallFunctionWithRetVal (0xB2) & 0x80) break; usleep (1); } if (i >= 9000) return 0; /*Fail. */ while (CallFunctionWithRetVal (0xB2) & 0x80) usleep (1); return 1; } static void TransferScanParameters (enumColorDepth enColor, SANE_Word wResolution, SANE_Word wPixelsLength) { SANE_Word wRightBourder = (2570 + wPixelsLength) / 2 + 65; SANE_Word wLeftBourder = (2570 - wPixelsLength) / 2 + 65; switch (enColor) { case Drawing: CallFunctionWithParameter (0x90, 2); /*Not supported correctle. FIX ME!!! */ break; case Halftone: CallFunctionWithParameter (0x90, 0xE3); /*Not supported correctly. FIX ME!!! */ CallFunctionWithParameter (0x92, 3); break; case GrayScale: case TrueColor: CallFunctionWithParameter (0x90, 0); /*Not supported correctly. FIX ME!!! */ break; }; CallFunctionWithParameter (0xA1, 2); CallFunctionWithParameter (0xA2, 1); CallFunctionWithParameter (0xA3, 0x98); /*Resolution: */ CallFunctionWithParameter (0x9A, (SANE_Byte) (wResolution >> 8)); /*High byte */ CallFunctionWithParameter (0x9B, (SANE_Byte) wResolution); /*Low byte */ LoadingPaletteToScanner (); CallFunctionWithParameter (0xA4, 31); /*Some sort of constant parameter */ /*Left bourder */ CallFunctionWithParameter (0xA5, wLeftBourder / 256); CallFunctionWithParameter (0xA6, wLeftBourder % 256); /*Right bourder */ CallFunctionWithParameter (0xAA, wRightBourder / 256); CallFunctionWithParameter (0xAB, wRightBourder % 256); CallFunctionWithParameter (0xD0, 0); CallFunctionWithParameter (0xD1, 0); CallFunctionWithParameter (0xD2, 0); CallFunctionWithParameter (0xD3, 0); CallFunctionWithParameter (0xD4, 0); CallFunctionWithParameter (0xD5, 0); CallFunctionWithParameter (0x9D, 5); } static void TurnOnPaperPulling (enumColorDepth enColor, SANE_Word wResolution) { switch (enColor) { case Drawing: case Halftone: CallFunctionWithParameter (0x91, 0xF7); return; case GrayScale: switch (wResolution) { case 50: case 75: case 100: CallFunctionWithParameter (0x91, 0xB7); return; case 150: case 200: CallFunctionWithParameter (0x91, 0x77); return; case 250: case 300: CallFunctionWithParameter (0x91, 0x37); return; default: return; } case TrueColor: switch (wResolution) { case 75: case 100: CallFunctionWithParameter (0x91, 0xA3); return; case 150: case 200: CallFunctionWithParameter (0x91, 0x53); return; case 250: case 300: CallFunctionWithParameter (0x91, 0x3); return; default: return; } default: return; } } static void TurnOffPaperPulling () { CallFunctionWithParameter (0x91, 0); } /* Returns average value of scanned row. While paper not loaded this is base "white point". */ static SANE_Byte GetCalibration () { int i; int Result; SANE_Byte Buffer[2600]; SANE_Byte bTest; CallFunctionWithParameter (0xA1, 2); CallFunctionWithParameter (0xA2, 1); CallFunctionWithParameter (0xA3, 0x98); /*Resolution to 300 DPI */ CallFunctionWithParameter (0x9A, 1); CallFunctionWithParameter (0x9B, 0x2C); CallFunctionWithParameter (0x92, 0); CallFunctionWithParameter (0xC6, 0); CallFunctionWithParameter (0x92, 0x80); for (i = 1; i < 256; i++) CallFunctionWithParameter (0xC6, i); for (i = 0; i < 256; i++) CallFunctionWithParameter (0xC6, i); for (i = 0; i < 256; i++) CallFunctionWithParameter (0xC6, i); CallFunctionWithParameter (0xA4, 31); /*Some sort of constant */ /*Left bourder */ CallFunctionWithParameter (0xA5, 0); CallFunctionWithParameter (0xA6, 0x41); /*Right bourder */ CallFunctionWithParameter (0xAA, 0xA); CallFunctionWithParameter (0xAB, 0x39); CallFunctionWithParameter (0xD0, 0); CallFunctionWithParameter (0xD1, 0); CallFunctionWithParameter (0xD2, 0); CallFunctionWithParameter (0xD3, 0); CallFunctionWithParameter (0xD4, 0); CallFunctionWithParameter (0xD5, 0); CallFunctionWithParameter (0x9C, 0x1B); CallFunctionWithParameter (0x9D, 5); CallFunctionWithParameter (0x92, 0x10); CallFunctionWithParameter (0xC6, 0xFF); CallFunctionWithParameter (0x92, 0x90); for (i = 0; i < 2999; i++) CallFunctionWithParameter (0xC6, 0xFF); CallFunctionWithParameter (0x92, 0x50); CallFunctionWithParameter (0xC6, 0); CallFunctionWithParameter (0x92, 0xD0); for (i = 0; i < 2999; i++) CallFunctionWithParameter (0xC6, 0); CallFunctionWithParameter (0x98, 0xFF); /*Up limit */ CallFunctionWithParameter (0x95, 0); /*Low limit */ CallFunctionWithParameter (0x90, 0); /*Gray scale... */ CallFunctionWithParameter (0x91, 0x3B); /*Turn motor on. */ for (i = 0; i < 5; i++) { do { /*WARNING!!! Deadlock possible! */ bTest = CallFunctionWithRetVal (0xB5); } while ((bTest & 0x80) ? (bTest & 0x3F) <= 2 : (bTest & 0x3F) >= 5); CallFunctionWithParameter (0xCD, 0); /*Skip this line for ECP: */ CallFunctionWithRetVal (0xC8); WriteScannerRegister (REGISTER_FUNCTION_CODE, 0xC8); WriteAddress (0x20); ReadDataBlock (Buffer, 2552); }; CallFunctionWithParameter (0x91, 0); /*Turn off motor. */ usleep (10); for (Result = 0, i = 0; i < 2552; i++) Result += Buffer[i]; return Result / 2552; } static int PaperFeed (SANE_Word wLinesToFeed) { int i; CallFunctionWithParameter (0xA7, 0xF); CallFunctionWithParameter (0xA8, 0xFF); CallFunctionWithParameter (0xC2, 0); for (i = 0; i < 9000; i++) { if (CallFunctionWithRetVal (0xB2) & 0x80) break; usleep (1); } if (i >= 9000) return 0; /*Fail. */ for (i = 0; i < 9000; i += 5) { if ((CallFunctionWithRetVal (0xB2) & 0x20) == 0) break; else if ((CallFunctionWithRetVal (0xB2) & 0x80) == 0) { i = 9000; break; } usleep (5); } CallFunctionWithParameter (0xC5, 0); if (i >= 9000) return 0; /*Fail. */ /*Potential deadlock */ while (CallFunctionWithRetVal (0xB2) & 0x80); /*Wait bit dismiss */ CallFunctionWithParameter (0xA7, wLinesToFeed / 256); CallFunctionWithParameter (0xA8, wLinesToFeed % 256); CallFunctionWithParameter (0xC2, 0); for (i = 0; i < 9000; i++) { if (CallFunctionWithRetVal (0xB2) & 0x80) break; usleep (1); } if (i >= 9000) return 0; /*Fail. */ for (i = 0; i < 9000; i++) { if ((CallFunctionWithRetVal (0xB2) & 0x80) == 0) break; usleep (1); } if (i >= 9000) return 0; /*Fail. */ return 1; } /*For now we do no calibrate elements - just set maximum limits. FIX ME?*/ static void CalibrateScanElements () { /*Those arrays will be used in future for correct calibration. */ /*Then we need to transfer UP brightness border, we use these registers */ SANE_Byte arUpTransferBorders[] = { 0x10, 0x20, 0x30 }; /*Then we need to transfer LOW brightness border, we use these registers */ SANE_Byte arLowTransferBorders[] = { 0x50, 0x60, 0x70 }; /*Then we need to save UP brightness border, we use these registers */ SANE_Byte arUpSaveBorders[] = { 0x98, 0x97, 0x99 }; /*Then we need to save LOW brightness border, we use these registers */ SANE_Byte arLowSaveBorders[] = { 0x95, 0x94, 0x96 }; /*Speeds, used for calibration */ SANE_Byte arSpeeds[] = { 0x3B, 0x37, 0x3F }; int j, Average, Temp, Index, /* Line, */ timeout,Calibration; SANE_Byte bTest /*, Min, Max, Result */ ; /*For current color component: (values from arrays). Next two lines - starting and terminating. */ SANE_Byte CurrentUpTransferBorder; SANE_Byte CurrentLowTransferBorder; SANE_Byte CurrentUpSaveBorder; SANE_Byte CurrentLowSaveBorder; SANE_Byte CurrentSpeed1, CurrentSpeed2; SANE_Byte CorrectionValue; SANE_Byte FilteredBuffer[2570]; CallFunctionWithParameter (0xA1, 2); CallFunctionWithParameter (0xA2, 0); CallFunctionWithParameter (0xA3, 0x98); /*DPI = 300 */ CallFunctionWithParameter (0x9A, 1); /*High byte */ CallFunctionWithParameter (0x9B, 0x2C); /*Low byte */ /*Paletter settings. */ CallFunctionWithParameter (0x92, 0); CallFunctionWithParameter (0xC6, 0); CallFunctionWithParameter (0x92, 0x80); /*First color component */ for (j = 1; j < 256; j++) CallFunctionWithParameter (0xC6, j); /*Second color component */ for (j = 0; j < 256; j++) CallFunctionWithParameter (0xC6, j); /*Third color component */ for (j = 0; j < 256; j++) CallFunctionWithParameter (0xC6, j); CallFunctionWithParameter (0xA4, 31); /*Left border */ CallFunctionWithParameter (0xA5, 0); /*High byte */ CallFunctionWithParameter (0xA6, 0x41); /*Low byte */ /*Right border */ CallFunctionWithParameter (0xAA, 0xA); /*High byte */ CallFunctionWithParameter (0xAB, 0x4B); /*Low byte */ /*Zero these registers... */ CallFunctionWithParameter (0xD0, 0); CallFunctionWithParameter (0xD1, 0); CallFunctionWithParameter (0xD2, 0); CallFunctionWithParameter (0xD3, 0); CallFunctionWithParameter (0xD4, 0); CallFunctionWithParameter (0xD5, 0); CallFunctionWithParameter (0x9C, 0x1B); CallFunctionWithParameter (0x9D, 0x5); Average = 0; for (Index = 0; Index < 3; Index++) /*For theree color components */ { /*Up border = 0xFF */ CallFunctionWithParameter (0x92, arUpTransferBorders[Index]); CallFunctionWithParameter (0xC6, 0xFF); CallFunctionWithParameter (0x92, arUpTransferBorders[Index] | 0x80); for (j = 2999; j > 0; j--) CallFunctionWithParameter (0xC6, 0xFF); /*Low border = 0x0 */ CallFunctionWithParameter (0x92, arLowTransferBorders[Index]); CallFunctionWithParameter (0xC6, 0x0); CallFunctionWithParameter (0x92, arLowTransferBorders[Index] | 0x80); for (j = 2999; j > 0; j--) CallFunctionWithParameter (0xC6, 0x0); /*Save borders */ CallFunctionWithParameter (arUpSaveBorders[Index], 0xFF); CallFunctionWithParameter (arLowSaveBorders[Index], 0x0); CallFunctionWithParameter (0x90, 0); /*Gray Scale or True color sign :) */ CallFunctionWithParameter (0x91, arSpeeds[Index]); /*waiting for scanned line... */ timeout = 0; do { bTest = CallFunctionWithRetVal (0xB5); timeout++; usleep (1); } while ((timeout < 1000) && ((bTest & 0x80) ? (bTest & 0x3F) <= 2 : (bTest & 0x3F) >= 5)); /*Let's read it... */ if(timeout < 1000) { CallFunctionWithParameter (0xCD, 0); CallFunctionWithRetVal (0xC8); WriteScannerRegister (0x70, 0xC8); WriteAddress (0x20); ReadDataBlock (FilteredBuffer, 2570); } CallFunctionWithParameter (0x91, 0); /*Stop engine. */ /*Note: if first read failed, junk would be calculated, but if previous read was succeeded, but last one failed, previous data'ld be used. */ for(Temp = 0, j = 0; j < 2570; j++) Temp += FilteredBuffer[j]; Temp /= 2570; if((Average == 0)||(Average > Temp)) Average = Temp; } for(Index = 0; Index < 3; Index++) /*Three color components*/ { CurrentUpTransferBorder = arUpTransferBorders[Index]; CallFunctionWithParameter (0xC6, 0xFF); CallFunctionWithParameter (0x92, CurrentUpTransferBorder|0x80); for(j=2999; j>0; j--) CallFunctionWithParameter (0xC6, 0xFF); CurrentLowTransferBorder = arLowTransferBorders[Index]; CallFunctionWithParameter (0xC6, 0x0); CallFunctionWithParameter (0x92, CurrentLowTransferBorder|0x80); for(j=2999; j>0; j--) CallFunctionWithParameter (0xC6, 0); CurrentUpSaveBorder = arUpSaveBorders[Index]; CallFunctionWithParameter (CurrentUpSaveBorder, 0xFF); CurrentLowSaveBorder = arLowSaveBorders[Index]; CallFunctionWithParameter (CurrentLowSaveBorder, 0x0); CallFunctionWithParameter (0x90,0); Calibration = 0x80; CallFunctionWithParameter (CurrentUpSaveBorder, 0x80); CurrentSpeed1 = CurrentSpeed2 = arSpeeds[Index]; for(CorrectionValue = 0x40; CorrectionValue != 0;CorrectionValue >>= 2) { CallFunctionWithParameter (0x91, CurrentSpeed2); usleep(10); /*waiting for scanned line... */ for(j = 0; j < 5; j++) { timeout = 0; do { bTest = CallFunctionWithRetVal (0xB5); timeout++; usleep (1); } while ((timeout < 1000) && ((bTest & 0x80) ? (bTest & 0x3F) <= 2 : (bTest & 0x3F) >= 5)); /*Let's read it... */ if(timeout < 1000) { CallFunctionWithParameter (0xCD, 0); CallFunctionWithRetVal (0xC8); WriteScannerRegister (0x70, 0xC8); WriteAddress (0x20); ReadDataBlock (FilteredBuffer, 2570); } }/*5 times we read. I don't understand what for, but so does HP's driver. Perhaps, we can optimize it in future.*/ WriteScannerRegister (0x91, 0); usleep(10); for(Temp = 0,j = 0; j < 16;j++) Temp += FilteredBuffer[509+j]; /*At this offset calcalates HP's driver.*/ Temp /= 16; if(Average > Temp) { Calibration += CorrectionValue; Calibration = 0xFF < Calibration ? 0xFF : Calibration; /*min*/ } else Calibration -= CorrectionValue; WriteScannerRegister (CurrentUpSaveBorder, Calibration); }/*By CorrectionValue we tune UpSaveBorder*/ WriteScannerRegister (0x90, 8); WriteScannerRegister (0x91, CurrentSpeed1); usleep(10); }/*By color components*/ return; } /* Internal use functions: */ /*Returns 0 in case of fail and 1 in success.*/ static int OutputCheck () { int i; WriteScannerRegister (0x7F, 0x1); WriteAddress (0x7E); for (i = 0; i < 256; i++) WriteData ((SANE_Byte) i); WriteAddress (0x3F); if (ReadDataByte () & 0x80) return 0; return 1; } static int InputCheck () { int i; SANE_Byte Buffer[256]; WriteAddress (0x3E); for (i = 0; i < 256; i++) { Buffer[i] = ReadDataByte (); } for (i = 0; i < 256; i++) { if (Buffer[i] != i) return 0; } return 1; } static int CallCheck () { int i; SANE_Byte Buffer[256]; CallFunctionWithParameter (0x92, 0x10); CallFunctionWithParameter (0xC6, 0x0); CallFunctionWithParameter (0x92, 0x90); WriteScannerRegister (REGISTER_FUNCTION_CODE, 0xC6); WriteAddress (0x60); for (i = 1; i < 256; i++) WriteData ((SANE_Byte) i); CallFunctionWithParameter (0x92, 0x10); CallFunctionWithRetVal (0xC6); CallFunctionWithParameter (0x92, 0x90); WriteScannerRegister (REGISTER_FUNCTION_CODE, 0xC6); WriteAddress (ADDRESS_RESULT); ReadDataBlock (Buffer, 256); for (i = 0; i < 255; i++) { if (Buffer[i + 1] != (SANE_Byte) i) return 0; } return 1; } static void LoadingPaletteToScanner () { /*For now we have statical gamma. */ SANE_Byte Gamma[256]; int i; for (i = 0; i < 256; i++) Gamma[i] = i; CallFunctionWithParameter (0x92, 0); CallFunctionWithParameter (0xC6, Gamma[0]); CallFunctionWithParameter (0x92, 0x80); for (i = 1; i < 256; i++) CallFunctionWithParameter (0xC6, Gamma[i]); for (i = 0; i < 256; i++) CallFunctionWithParameter (0xC6, Gamma[i]); for (i = 0; i < 256; i++) CallFunctionWithParameter (0xC6, Gamma[i]); } /* Low level warappers: */ static void WriteAddress (SANE_Byte Address) { ieee1284_data_dir (pl.portv[scanner_d], 0); /*Forward mode */ ieee1284_frob_control (pl.portv[scanner_d], C1284_NINIT, C1284_NINIT); ieee1284_epp_write_addr (pl.portv[scanner_d], 0, (char *) &Address, 1); } static void WriteData (SANE_Byte Data) { ieee1284_data_dir (pl.portv[scanner_d], 0); /*Forward mode */ ieee1284_frob_control (pl.portv[scanner_d], C1284_NINIT, C1284_NINIT); ieee1284_epp_write_data (pl.portv[scanner_d], 0, (char *) &Data, 1); } static void WriteScannerRegister (SANE_Byte Address, SANE_Byte Data) { WriteAddress (Address); WriteData (Data); } static void CallFunctionWithParameter (SANE_Byte Function, SANE_Byte Parameter) { WriteScannerRegister (REGISTER_FUNCTION_CODE, Function); WriteScannerRegister (REGISTER_FUNCTION_PARAMETER, Parameter); } static SANE_Byte CallFunctionWithRetVal (SANE_Byte Function) { WriteScannerRegister (REGISTER_FUNCTION_CODE, Function); WriteAddress (ADDRESS_RESULT); return ReadDataByte (); } static SANE_Byte ReadDataByte () { SANE_Byte Result; ieee1284_data_dir (pl.portv[scanner_d], 1); /*Reverse mode */ ieee1284_frob_control (pl.portv[scanner_d], C1284_NINIT, C1284_NINIT); ieee1284_epp_read_data (pl.portv[scanner_d], 0, (char *) &Result, 1); return Result; } static void ReadDataBlock (SANE_Byte * Buffer, int length) { ieee1284_data_dir (pl.portv[scanner_d], 1); /*Reverse mode */ ieee1284_frob_control (pl.portv[scanner_d], C1284_NINIT, C1284_NINIT); ieee1284_epp_read_data (pl.portv[scanner_d], 0, (char *) Buffer, length); } /* Send a daisy-chain-style CPP command packet. */ int cpp_daisy (struct parport *port, int cmd) { unsigned char s; ieee1284_data_dir (port, 0); /*forward direction */ ieee1284_write_control (port, C1284_NINIT); ieee1284_write_data (port, 0xaa); usleep (2); ieee1284_write_data (port, 0x55); usleep (2); ieee1284_write_data (port, 0x00); usleep (2); ieee1284_write_data (port, 0xff); usleep (2); s = ieee1284_read_status (port) ^ S1284_INVERTED; /*Converted for PC-style */ s &= (S1284_BUSY | S1284_PERROR | S1284_SELECT | S1284_NFAULT); if (s != (S1284_BUSY | S1284_PERROR | S1284_SELECT | S1284_NFAULT)) { DBG (1, "%s: cpp_daisy: aa5500ff(%02x)\n", port->name, s); return -1; } ieee1284_write_data (port, 0x87); usleep (2); s = ieee1284_read_status (port) ^ S1284_INVERTED; /*Convert to PC-style */ s &= (S1284_BUSY | S1284_PERROR | S1284_SELECT | S1284_NFAULT); if (s != (S1284_SELECT | S1284_NFAULT)) { DBG (1, "%s: cpp_daisy: aa5500ff87(%02x)\n", port->name, s); return -1; } ieee1284_write_data (port, 0x78); usleep (2); ieee1284_write_control (port, C1284_NINIT); ieee1284_write_data (port, cmd); usleep (2); ieee1284_frob_control (port, C1284_NSTROBE, C1284_NSTROBE); usleep (1); ieee1284_frob_control (port, C1284_NSTROBE, 0); usleep (1); s = ieee1284_read_status (port); ieee1284_write_data (port, 0xff); usleep (2); return s; } /*Daisy chain deselect operation.*/ void daisy_deselect_all (struct parport *port) { cpp_daisy (port, 0x30); } /*Daisy chain select operation*/ int daisy_select (struct parport *port, int daisy, int mode) { switch (mode) { /*For these modes we should switch to EPP mode: */ case M1284_EPP: case M1284_EPPSL: case M1284_EPPSWE: return cpp_daisy (port, 0x20 + daisy) & S1284_NFAULT; /*For these modes we should switch to ECP mode: */ case M1284_ECP: case M1284_ECPRLE: case M1284_ECPSWE: return cpp_daisy (port, 0xd0 + daisy) & S1284_NFAULT; /*Nothing was told for BECP in Daisy chain specification. May be it's wise to use ECP? */ case M1284_BECP: /*Others use compat mode */ case M1284_NIBBLE: case M1284_BYTE: case M1284_COMPAT: default: return cpp_daisy (port, 0xe0 + daisy) & S1284_NFAULT; } } /*Daisy chain assign address operation.*/ int assign_addr (struct parport *port, int daisy) { return cpp_daisy (port, daisy); } static int OpenScanner (const char *scanner_path) { int handle; int caps; /*Scaner name was specified in config file?*/ if (strlen(scanner_path) == 0) return -1; for (handle = 0; handle < pl.portc; handle++) { if (strcmp (scanner_path, pl.portv[handle]->name) == 0) break; } if (handle == pl.portc) /*No match found */ return -1; /*Open port */ if (ieee1284_open (pl.portv[handle], 0, &caps) != E1284_OK) return -1; /*Claim port */ if (ieee1284_claim (pl.portv[handle]) != E1284_OK) return -1; /*Total chain reset. */ daisy_deselect_all (pl.portv[handle]); /*Assign addresses. */ assign_addr (pl.portv[handle], 0); /*Assume we have device first in chain. */ /*Select required device. For now - first in chain. */ daisy_select (pl.portv[handle], 0, M1284_EPP); return handle; } static void CloseScanner (int handle) { if (handle == -1) return; daisy_deselect_all (pl.portv[handle]); ieee1284_release (pl.portv[handle]); ieee1284_close (pl.portv[handle]); } backends-1.3.0/backend/hpsj5s.conf.in000066400000000000000000000000271456256263500173610ustar00rootroot00000000000000#hpsj5s.conf #parport0 backends-1.3.0/backend/hpsj5s.h000066400000000000000000000052641456256263500162660ustar00rootroot00000000000000#ifndef __HPSJ5S_MIDDLE_LEVEL_API_HEADER__ #define __HPSJ5S_MIDDLE_LEVEL_API_HEADER__ #include /*Scanner hardware registers*/ #define REGISTER_FUNCTION_CODE 0x70 /*Here goes function code */ #define REGISTER_FUNCTION_PARAMETER 0x60 /*Here goes function param */ #define ADDRESS_RESULT 0x20 /*Here we get result */ /*Scanner functions (not all - some of them I can't identify)*/ #define FUNCTION_SETUP_HARDWARE 0xA0 /*Scanner hardware control flags:*/ /*Set this flag and non-zero speed to start rotation*/ #define FLAGS_HW_MOTOR_READY 0x1 /*Set this flag to turn on lamp*/ #define FLAGS_HW_LAMP_ON 0x2 /*Set this flag to turn indicator lamp off*/ #define FLAGS_HW_INDICATOR_OFF 0x4 /* Types: */ /*Color modes we support: 1-bit Drawing, 2-bit Halftone, 8-bit Gray Scale, 24-bt True Color*/ typedef enum { Drawing, Halftone, GrayScale, TrueColor } enumColorDepth; /*Middle-level API:*/ static int OpenScanner (const char *scanner_path); static void CloseScanner (int handle); static int DetectScanner (void); static void StandByScanner (void); static void SwitchHardwareState (SANE_Byte mask, SANE_Byte invert_mask); static int CheckPaperPresent (void); static int ReleasePaper (void); static int PaperFeed (SANE_Word wLinesToFeed); static void TransferScanParameters (enumColorDepth enColor, SANE_Word wResolution, SANE_Word wCorrectedLength); static void TurnOnPaperPulling (enumColorDepth enColor, SANE_Word wResolution); static void TurnOffPaperPulling (void); static SANE_Byte GetCalibration (void); static void CalibrateScanElements (void); /*Internal-use functions:*/ static int OutputCheck (void); static int InputCheck (void); static int CallCheck (void); static void LoadingPaletteToScanner (void); /*Low level warappers:*/ static void WriteAddress (SANE_Byte Address); static void WriteData (SANE_Byte Data); static void WriteScannerRegister (SANE_Byte Address, SANE_Byte Data); static void CallFunctionWithParameter (SANE_Byte Function, SANE_Byte Parameter); static SANE_Byte CallFunctionWithRetVal (SANE_Byte Function); static SANE_Byte ReadDataByte (void); static void ReadDataBlock (SANE_Byte * Buffer, int length); /*Daisy chaining API: (should be moved to ieee1284 library in future)*/ /*Deselect all devices in chain on this port.*/ static void daisy_deselect_all (struct parport *port); /*Select device with number 'daisy' in 'mode'.*/ static int daisy_select (struct parport *port, int daisy, int mode); /*Setup address for device in chain on this port*/ static int assign_addr (struct parport *port, int daisy); /* Send a daisy-chain-style CPP command packet. */ static int cpp_daisy (struct parport *port, int cmd); #endif backends-1.3.0/backend/hs2p-saneopts.h000066400000000000000000000321351456256263500175550ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2007 Jeremy Johnson This file is part of a SANE backend for Ricoh IS450 and IS420 family of HS2P Scanners using the SCSI controller. 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, see . 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. */ #define SANE_NAME_INQUIRY "inquiry" #define SANE_TITLE_INQUIRY "Inquiry Data" #define SANE_DESC_INQUIRY "Displays scanner inquiry data" #define SANE_TITLE_SCAN_MODE_GROUP "Scan Mode" #define SANE_TITLE_GEOMETRY_GROUP "Geometry" #define SANE_TITLE_FEEDER_GROUP "Feeder" #define SANE_TITLE_ENHANCEMENT_GROUP "Enhancement" #define SANE_TITLE_ICON_GROUP "Icon" #define SANE_TITLE_BARCODE_GROUP "Barcode" #define SANE_TITLE_MISCELLANEOUS_GROUP "Miscellaneous" #define SANE_NAME_AUTOBORDER "autoborder" #define SANE_TITLE_AUTOBORDER "Autoborder" #define SANE_DESC_AUTOBORDER "Enable Automatic Border Detection" #define SANE_NAME_COMPRESSION "compression" #define SANE_TITLE_COMPRESSION "Data Compression" #define SANE_DESC_COMPRESSION "Sets the compression mode of the scanner" #define SANE_NAME_ROTATION "rotation" #define SANE_TITLE_ROTATION "Page Rotation" #define SANE_DESC_ROTATION "Sets the page rotation mode of the scanner" #define SANE_NAME_DESKEW "deskew" #define SANE_TITLE_DESKEW "Page Deskew" #define SANE_DESC_DESKEW "Enable Deskew Mode" #define SANE_NAME_TIMEOUT_ADF "timeout-adf" #define SANE_TITLE_TIMEOUT_ADF "ADF Timeout" #define SANE_DESC_TIMEOUT_ADF "Sets the timeout in seconds for the ADF" #define SANE_NAME_TIMEOUT_MANUAL "timeout-manual" #define SANE_TITLE_TIMEOUT_MANUAL "Manual Timeout" #define SANE_DESC_TIMEOUT_MANUAL "Sets the timeout in seconds for manual feeder" #define SANE_NAME_BATCH "batch" #define SANE_TITLE_BATCH "Batch" #define SANE_DESC_BATCH "Enable Batch Mode" #define SANE_NAME_CHECK_ADF "check-adf" #define SANE_TITLE_CHECK_ADF "Check ADF" #define SANE_DESC_CHECK_ADF "Check ADF Status prior to starting scan" #define SANE_NAME_PREFEED "prefeed" #define SANE_TITLE_PREFEED "Prefeed" #define SANE_DESC_PREFEED "Prefeed" #define SANE_NAME_DUPLEX "duplex" #define SANE_TITLE_DUPLEX "Duplex" #define SANE_DESC_DUPLEX "Enable Duplex (Dual-Sided) Scanning" #define SANE_NAME_ENDORSER "endorser" #define SANE_TITLE_ENDORSER "Endorser" #define SANE_DESC_ENDORSER "Print up to 19 character string on each sheet" #define SANE_NAME_ENDORSER_STRING "endorser-string" #define SANE_TITLE_ENDORSER_STRING "Endorser String" #define SANE_DESC_ENDORSER_STRING "valid characters: [0-9][ :#`'-./][A-Z][a-z]" #define SANE_NAME_BARCODE_SEARCH_COUNT "barcode-search-count" #define SANE_TITLE_BARCODE_SEARCH_COUNT "Barcode Search Count" #define SANE_DESC_BARCODE_SEARCH_COUNT "Number of barcodes to search for in the scanned image" #define SANE_NAME_BARCODE_HMIN "barcode-hmin" #define SANE_TITLE_BARCODE_HMIN "Barcode Minimum Height" #define SANE_DESC_BARCODE_HMIN "Sets the Barcode Minimum Height (larger values increase recognition speed)" #define SANE_NAME_BARCODE_SEARCH_MODE "barcode-search-mode" #define SANE_TITLE_BARCODE_SEARCH_MODE "Barcode Search Mode" #define SANE_DESC_BARCODE_SEARCH_MODE "Chooses the orientation of barcodes to be searched" #define SANE_NAME_BARCODE_SEARCH_TIMEOUT "barcode-search-timeout" #define SANE_TITLE_BARCODE_SEARCH_TIMEOUT "Barcode Search Timeout" #define SANE_DESC_BARCODE_SEARCH_TIMEOUT "Sets the timeout for barcode searching" #define SANE_NAME_BARCODE_SEARCH_BAR "barcode-search-bar" #define SANE_TITLE_BARCODE_SEARCH_BAR "Barcode Search Bar" #define SANE_DESC_BARCODE_SEARCH_BAR "Specifies the barcode type to search for" #define SANE_NAME_SECTION "section" #define SANE_TITLE_SECTION "Image/Barcode Search Sections" #define SANE_DESC_SECTION "Specifies an image section and/or a barcode search region" #define SANE_NAME_BARCODE_RELMAX "barcode-relmax" #define SANE_TITLE_BARCODE_RELMAX "Barcode RelMax" #define SANE_DESC_BARCODE_RELMAX "Specifies the maximum relation from the widest to the smallest bar" #define SANE_NAME_BARCODE_BARMIN "barcode-barmin" #define SANE_TITLE_BARCODE_BARMIN "Barcode Bar Minimum" #define SANE_DESC_BARCODE_BARMIN "Specifies the minimum number of bars in Bar/Patch code" #define SANE_NAME_BARCODE_BARMAX "barcode-barmax" #define SANE_TITLE_BARCODE_BARMAX "Barcode Bar Maximum" #define SANE_DESC_BARCODE_BARMAX "Specifies the maximum number of bars in a Bar/Patch code" #define SANE_NAME_BARCODE_CONTRAST "barcode-contrast" #define SANE_TITLE_BARCODE_CONTRAST "Barcode Contrast" #define SANE_DESC_BARCODE_CONTRAST "Specifies the image contrast used in decoding. Use higher values when " \ "there are more white pixels in the code" #define SANE_NAME_BARCODE_PATCHMODE "barcode-patchmode" #define SANE_TITLE_BARCODE_PATCHMODE "Barcode Patch Mode" #define SANE_DESC_BARCODE_PATCHMODE "Controls Patch Code detection." #define SANE_NAME_SCAN_WAIT_MODE "scan-wait-mode" #define SANE_TITLE_SCAN_WAIT_MODE "Scan Wait Mode " #define SANE_DESC_SCAN_WAIT_MODE "Enables the scanner's start button" #define SANE_NAME_ACE_FUNCTION "ace-function" #define SANE_TITLE_ACE_FUNCTION "ACE Function" #define SANE_DESC_ACE_FUNCTION "ACE Function" #define SANE_NAME_ACE_SENSITIVITY "ace-sensitivity" #define SANE_TITLE_ACE_SENSITIVITY "ACE Sensitivity" #define SANE_DESC_ACE_SENSITIVITY "ACE Sensitivity" #define SANE_NAME_ICON_WIDTH "icon-width" #define SANE_TITLE_ICON_WIDTH "Icon Width" #define SANE_DESC_ICON_WIDTH "Width of icon (thumbnail) image in pixels" #define SANE_NAME_ICON_LENGTH "icon-length" #define SANE_TITLE_ICON_LENGTH "Icon Length" #define SANE_DESC_ICON_LENGTH "Length of icon (thumbnail) image in pixels" #define SANE_NAME_ORIENTATION "orientation" #define SANE_TITLE_ORIENTATION "Paper Orientation" #define SANE_DESC_ORIENTATION "[Portrait]/Landscape" \ #define SANE_NAME_PAPER_SIZE "paper-size" #define SANE_TITLE_PAPER_SIZE "Paper Size" #define SANE_DESC_PAPER_SIZE "Specify the scan window geometry by specifying the paper size " \ "of the documents to be scanned" #define SANE_NAME_PADDING "padding" #define SANE_TITLE_PADDING "Padding" #define SANE_DESC_PADDING "Pad if media length is less than requested" #define SANE_NAME_AUTO_SIZE "auto-size" #define SANE_TITLE_AUTO_SIZE "Auto Size" #define SANE_DESC_AUTO_SIZE "Automatic Paper Size Determination" #define SANE_NAME_BINARYFILTER "binary-filter" #define SANE_TITLE_BINARYFILTER "Binary Filter" #define SANE_DESC_BINARYFILTER "Binary Filter" #define SANE_NAME_SMOOTHING "smoothing" #define SANE_TITLE_SMOOTHING "Smoothing" #define SANE_DESC_SMOOTHING "Binary Smoothing Filter" #define SANE_NAME_NOISEREMOVAL "noise-removal" #define SANE_TITLE_NOISEREMOVAL "Noise Removal" #define SANE_DESC_NOISEREMOVAL "Binary Noise Removal Filter" #define SANE_NAME_NOISEMATRIX "noise-removal-matrix" #define SANE_TITLE_NOISEMATRIX "Noise Removal Matrix" #define SANE_DESC_NOISEMATRIX "Noise Removal Matrix" #define SANE_NAME_GRAYFILTER "gray-filter" #define SANE_TITLE_GRAYFILTER "Gray Filter" #define SANE_DESC_GRAYFILTER "Gray Filter" #define SANE_NAME_HALFTONE_CODE "halftone-type" #define SANE_TITLE_HALFTONE_CODE "Halftone Type" #define SANE_DESC_HALFTONE_CODE "Dither or Error Diffusion" /* #define SANE_NAME_HALFTONE_PATTERN "pattern" #define SANE_TITLE_HALFTONE_PATTERN "Pattern" #define SANE_DESC_HALFTONE_PATTERN "10 built-in halftone patterns + 2 user patterns" */ #define SANE_NAME_ERRORDIFFUSION "error-diffusion" #define SANE_TITLE_ERRORDIFFUSION "Error Diffusion" #define SANE_DESC_ERRORDIFFUSION "Useful for documents with both text and images" /* #define SANE_NAME_HALFTONE "halftone" #define SANE_TITLE_HALFTONE "Halftone" #define SANE_DESC_HALFTONE "Choose a dither pattern or error diffusion" #define SANE_NAME_NEGATIVE "negative image" #define SANE_TITLE_NEGATIVE "Negative Image" #define SANE_DESC_NEGATIVE "Reverse Image Format" #define SANE_NAME_BRIGHTNESS "brightness" #define SANE_TITLE_BRIGHTNESS "Brightness" #define SANE_DESC_BRIGHTNESS "Brightness" #define SANE_NAME_THRESHOLD "threshold" #define SANE_TITLE_THRESHOLD "Threshold" #define SANE_DESC_THRESHOLD "Threshold" */ #define SANE_NAME_GAMMA "gamma" #define SANE_TITLE_GAMMA "Gamma" #define SANE_DESC_GAMMA "Gamma Correction" #define SANE_NAME_AUTOSEP "auto-separation" #define SANE_TITLE_AUTOSEP "Automatic Separation" #define SANE_DESC_AUTOSEP "Automatic Separation" #define SANE_NAME_AUTOBIN "auto-binarization" #define SANE_TITLE_AUTOBIN "Automatic Binarization" #define SANE_DESC_AUTOBIN "Automatic Binarization" #define SANE_NAME_WHITE_BALANCE "white-balance" #define SANE_TITLE_WHITE_BALANCE "White Balance" #define SANE_DESC_WHITE_BALANCE "White Balance" #define SANE_NAME_PADDING_TYPE "padding-type" #define SANE_TITLE_PADDING_TYPE "Padding Type" #define SANE_DESC_PADDING_TYPE "Padding Type" #define SANE_NAME_BITORDER "bit-order" #define SANE_TITLE_BITORDER "Bit Order" #define SANE_DESC_BITORDER "Bit Order" #define SANE_NAME_SELF_DIAGNOSTICS "self-diagnostics" #define SANE_TITLE_SELF_DIAGNOSTICS "Self Diagnostics" #define SANE_DESC_SELF_DIAGNOSTICS "Self Diagnostics" #define SANE_NAME_OPTICAL_ADJUSTMENT "optical-adjustment" #define SANE_TITLE_OPTICAL_ADJUSTMENT "Optical Adjustment" #define SANE_DESC_OPTICAL_ADJUSTMENT "Optical Adjustment" typedef enum { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_INQUIRY, /* inquiry string */ OPT_PREVIEW, OPT_SCAN_MODE, /* scan mode */ OPT_RESOLUTION, OPT_X_RESOLUTION, OPT_Y_RESOLUTION, OPT_COMPRESSION, /* hardware compression */ OPT_GEOMETRY_GROUP, /*OPT_AUTOBORDER, automatic border detection */ /*OPT_ROTATION, hardware rotation */ /*OPT_DESKEW, hardware deskew */ OPT_PAGE_ORIENTATION, /* portrait, landscape */ OPT_PAPER_SIZE, /* paper size */ OPT_TL_X, /* top-left x */ OPT_TL_Y, /* top-left y */ OPT_BR_X, /* bottom-right x */ OPT_BR_Y, /* bottom-right y */ OPT_PADDING, /* Pad to requested length */ OPT_AUTO_SIZE, /* Automatic Size Recognition */ OPT_FEEDER_GROUP, OPT_SCAN_SOURCE, /* scan source (eg. Flatbed, ADF) */ OPT_DUPLEX, /* scan both sides of the page */ OPT_SCAN_WAIT_MODE, /* Enables the scanner's Start Button */ OPT_PREFEED, OPT_ENDORSER, /* Endorser (off,on) */ OPT_ENDORSER_STRING, /* Endorser String */ /*OPT_BATCH, scan in batch mode */ /*OPT_TIMEOUT_MANUAL, timeout in seconds with manual feed */ /*OPT_TIMEOUT_ADF, timeout in seconds with ADF */ /*OPT_CHECK_ADF, check for page in ADF before scanning */ OPT_ENHANCEMENT_GROUP, /* OPT_ACE_FUNCTION, OPT_ACE_SENSITIVITY, */ OPT_BRIGHTNESS, /* Brightness */ OPT_THRESHOLD, /* Threshold */ OPT_CONTRAST, /* Contrast */ OPT_NEGATIVE, /* Negative (reverse image) */ OPT_GAMMA, /* Gamma Correction */ OPT_CUSTOM_GAMMA, OPT_GAMMA_VECTOR_GRAY, OPT_HALFTONE_CODE, /* Halftone Code */ OPT_HALFTONE_PATTERN, /* Halftone Pattern */ OPT_GRAYFILTER, /* MRIF */ OPT_SMOOTHING, /* Smoothing */ OPT_NOISEREMOVAL, /* Noise Removal */ OPT_AUTOSEP, /* Auto Separation */ OPT_AUTOBIN, /* Auto Binarization */ OPT_WHITE_BALANCE, OPT_MISCELLANEOUS_GROUP, OPT_PADDING_TYPE, /*OPT_BITORDER, */ OPT_SELF_DIAGNOSTICS, OPT_OPTICAL_ADJUSTMENT, /* OPT_PARITION_FUNCTION OPT_SECTION */ OPT_DATA_GROUP, OPT_UPDATE, OPT_NREGX_ADF, OPT_NREGY_ADF, OPT_NREGX_BOOK, OPT_NREGY_BOOK, OPT_NSCANS_ADF, OPT_NSCANS_BOOK, OPT_LAMP_TIME, OPT_EO_ODD, OPT_EO_EVEN, OPT_BLACK_LEVEL_ODD, OPT_BLACK_LEVEL_EVEN, OPT_WHITE_LEVEL_ODD, OPT_WHITE_LEVEL_EVEN, OPT_DENSITY, OPT_FIRST_ADJ_WHITE_ODD, OPT_FIRST_ADJ_WHITE_EVEN, OPT_NREGX_REVERSE, OPT_NREGY_REVERSE, OPT_NSCANS_REVERSE_ADF, OPT_REVERSE_TIME, OPT_NCHARS, NUM_OPTIONS /* must come last: */ } HS2P_Option; backends-1.3.0/backend/hs2p-scsi.c000066400000000000000000002107401456256263500166550ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2007 Jeremy Johnson This file is part of a SANE backend for Ricoh IS450 and IS420 family of HS2P Scanners using the SCSI controller. 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, see . 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 "hs2p.h" static SANE_String_Const print_devtype (SANE_Byte devtype) { int i = devtype; static SANE_String devtypes[] = { "disk", "tape", "printer", "processor", "CD-writer", "CD-drive", "scanner", "optical-drive", "jukebox", "communicator" }; return (i >= 0 && i < NELEMS (devtypes)) ? devtypes[i] : "unknown-device"; } static void print_bytes (const void *buf, size_t bufsize) { const SANE_Byte *bp; unsigned i; for (i = 0, bp = buf; i < bufsize; i++, bp++) DBG (DBG_error, "%3d: 0x%02x %d\n", i, *bp, *bp); } static void ScannerDump (HS2P_Scanner * s) { int i; HS2P_Info *info; SANE_Device *sdev; info = &s->hw->info; sdev = &s->hw->sane; DBG (DBG_info, "\n\n"); DBG (DBG_info, ">> ScannerDump:\n"); DBG (DBG_info, "SANE Device: '%s' Vendor: '%s' Model: '%s' Type: '%s'\n", sdev->name, sdev->vendor, sdev->model, sdev->type); DBG (DBG_info, "Type: '%s' Vendor: '%s' Product: '%s' Revision: '%s'\n", print_devtype (info->devtype), info->vendor, info->product, info->revision); DBG (DBG_info, "Automatic Document Feeder: %s%s%s%s\n", info->hasADF ? "Installed " : "Not Installed ", info->hasSimplex ? "simplex" : "", info->hasDuplex ? "duplex" : "", info->hasARDF ? "reverse double-sided" : ""); DBG (DBG_info, "Endorser :%s\n", info->hasEndorser ? " " : " "); DBG (DBG_info, "Image Processing Unit:%s\n", info->hasIPU ? " " : " "); DBG (DBG_info, "Extended Board :%s\n", info->hasXBD ? " " : " "); DBG (DBG_info, "\n"); DBG (DBG_info, "Image Composition Support\n"); DBG (DBG_info, "Line Art (B/W) Support : %s\n", info->supports_lineart ? "Yes" : "No"); DBG (DBG_info, "Dithering (Halftone) Support: %s\n", info->supports_dithering ? "Yes" : "No"); DBG (DBG_info, "Error Diffusion Support : %s\n", info->supports_errordiffusion ? "Yes" : "No"); DBG (DBG_info, "Color Support : %s\n", info->supports_color ? "Yes" : "No"); DBG (DBG_info, "4 Bit Gray Support : %s\n", info->supports_4bitgray ? "Yes" : "No"); DBG (DBG_info, "5-8 Bit Gray Support : %s\n", info->supports_8bitgray ? "Yes" : "No"); DBG (DBG_info, "Image Data processing:%s%s%s%s%s%s\n", info->supports_whiteframing ? " " : "", info->supports_blackframing ? " " : "", info->supports_edgeextraction ? " " : "", info->supports_noiseremoval ? " " : "", info->supports_smoothing ? " " : "", info->supports_linebolding ? " " : ""); DBG (DBG_info, "Image Compression:%s%s%s%s\n", info->supports_MH ? " " : "", info->supports_MR ? " " : "", info->supports_MMR ? " " : "", info->supports_MHB ? " " : ""); DBG (DBG_info, "Marker Recognition: %s\n", info->supports_markerrecognition ? "" : ""); DBG (DBG_info, "Size Recognition : %s\n", info->supports_sizerecognition ? "" : ""); DBG (DBG_info, "X Maximum Output Pixels = %d\n", info->xmaxoutputpixels); /* DBG (DBG_info, "Optional Features:%s%s%s%s\n", info->canBorderRecog ? " " : "", info->canBarCode ? " " : "", info->canIcon ? " " : "", info->canSection ? "
" : ""); */ DBG (DBG_info, "Max bytes per scan-line: %d (%d pixels)\n", info->xmaxoutputpixels / 8, info->xmaxoutputpixels); DBG (DBG_info, "Basic resolution (X/Y) : %d/%d\n", info->resBasicX, info->resBasicY); DBG (DBG_info, "Maximum resolution (X/Y) : %d/%d\n", info->resMaxX, info->resMaxY); DBG (DBG_info, "Minimum resolution (X/Y) : %d/%d\n", info->resMinX, info->resMinY); DBG (DBG_info, "Standard Resolutions:\n"); for (i = 1; i <= info->resStdList[0]; i++) DBG (DBG_info, " %d\n", info->resStdList[i]); DBG (DBG_info, "Window Width/Height (in basic res) %d/%d (%.2f/%.2f inches)\n", info->winWidth, info->winHeight, (info->resBasicX != 0) ? ((float) info->winWidth) / info->resBasicX : 0.0, (info->resBasicY) ? ((float) info->winHeight) / info->resBasicY : 0.0); /* DBG (DBG_info, "Summary:%s%s%s\n", info->canDuplex ? "Duplex Scanner" : "Simplex Scanner", info->canACE ? " (ACE capable)" : "", info->canCheckADF ? " (ADF Paper Sensor capable)" : ""); */ DBG (DBG_info, "Buffer Full Ratio = %#02x\n", info->cxn.buffer_full_ratio); DBG (DBG_info, "Buffer Empty Ratio = %#02x\n", info->cxn.buffer_empty_ratio); DBG (DBG_info, "Bus Inactive Limit = %#02x\n", info->cxn.bus_inactive_limit[0] << 8 | info->cxn. bus_inactive_limit[1]); DBG (DBG_info, "Disconnect Time Limit = %#04x\n", info->cxn.disconnect_time_limit[0] << 8 | info->cxn. disconnect_time_limit[1]); DBG (DBG_info, "Connect Time Limit = %#02x\n", info->cxn.connect_time_limit[0] << 8 | info->cxn. connect_time_limit[1]); DBG (DBG_info, "Maximum Burst Size = %#04x\n", info->cxn.maximum_burst_size[0] << 8 | info->cxn. maximum_burst_size[1]); DBG (DBG_info, "DTDC = %#02x\n", info->cxn.dtdc & 0x03); DBG (DBG_info, "White Balance is %s\n", info->white_balance == 1 ? "Absolute" : "Relative"); DBG (DBG_info, "Medium Wait Timer is \n"); /* get_medium_wait_timer(fd) */ DBG (DBG_info, "Scan Wait Mode is %s\n", info->scan_wait_mode == 0 ? "OFF" : "ON"); DBG (DBG_info, "Service Mode is in Select %s Mode\n", info->service_mode == 0 ? "Self-Diagnostics" : "Optical Adjustment"); sprintf (info->inquiry_data, "Vendor: %s Product: %s Rev: %s %s%s\n", info->vendor, info->product, info->revision, info->hasADF && info->hasDuplex ? "Duplex Scanner" : "", info->hasADF && info->hasSimplex ? "Simplex Scanner" : ""); DBG (DBG_info, "duplex_default=%d\n", info->default_duplex); /* DBG (DBG_info, "autoborder_default=%d\n", info->autoborder_default); DBG (DBG_info, "batch_default=%d\n", info->batch_default); DBG (DBG_info, "deskew_default=%d\n", info->deskew_default); DBG (DBG_info, "check_adf_default=%d\n", info->check_adf_default); DBG (DBG_info, "timeout_adf_default=%d\n", info->timeout_adf_default); DBG (DBG_info, "timeout_manual_default=%d\n", info->timeout_manual_default); DBG (DBG_info, "control_panel_default=%d\n", info->control_panel_default); */ DBG (DBG_info, "bmu = %d\n", info->bmu); DBG (DBG_info, "mud = %d\n", info->mud); DBG (DBG_info, "white balance = %#0x\n", info->white_balance); DBG (DBG_info, "adf control = %#0x\n", info->adf_control); DBG (DBG_info, "adf mode control = %#0x\n", info->adf_mode_control); DBG (DBG_info, "endorser control = %#0x\n", info->endorser_control); DBG (DBG_info, "endorser string = %s\n", info->endorser_string); DBG (DBG_info, "scan wait mode = %#0x\n", info->scan_wait_mode); DBG (DBG_info, "service mode = %#0x\n", info->service_mode); DBG (DBG_info, "BasicXRes = %d\n", info->resBasicX); DBG (DBG_info, "BasicYRes = %d\n", info->resBasicY); DBG (DBG_info, "XResStep = %d\n", info->resXstep); DBG (DBG_info, "YResStep = %d\n", info->resYstep); DBG (DBG_info, "MaxXres = %d\n", info->resMaxX); DBG (DBG_info, "MaxYres = %d\n", info->resMaxY); DBG (DBG_info, "MinXres = %d\n", info->resMinX); DBG (DBG_info, "MinYres = %d\n", info->resMinY); DBG (DBG_info, "Width = %d\n", info->winWidth); DBG (DBG_info, "Height = %d\n", info->winHeight); DBG (DBG_info, "<< ScannerDump\n"); } static void print_vpd_info (struct inquiry_vpd_data *vbuf) { DBG (DBG_info, "VPD IDENTIFIER C0H\n"); DBG (DBG_info, "[00] Peripheral %#02x\n", vbuf->devtype); DBG (DBG_info, "[01] Page Code %#02x\n", vbuf->pagecode); DBG (DBG_info, "[02] reserved %#02x\n", vbuf->byte2); DBG (DBG_info, "[03] Page Length %#02x\n", vbuf->pagelength); DBG (DBG_info, "[04] ADF ID %#02x\n", vbuf->adf_id); DBG (DBG_info, "[05] Endorser ID %#02x\n", vbuf->end_id); DBG (DBG_info, "[06] Image Processing Unit %#02x\n", vbuf->ipu_id); DBG (DBG_info, "[07] Image Composition %#02x\n", vbuf->imagecomposition); DBG (DBG_info, "[08] Image Data Processing %lu\n", _2btol (&vbuf->imagedataprocessing[0])); DBG (DBG_info, "[10] Compression %#02x\n", vbuf->compression); DBG (DBG_info, "[11] Marker Recognition %#02x\n", vbuf->markerrecognition); DBG (DBG_info, "[12] Size Recognition %#02x\n", vbuf->sizerecognition); DBG (DBG_info, "[13] reserved %#02x\n", vbuf->byte13); DBG (DBG_info, "[14] X Maximum Output Pixel %lu\n", _2btol (&vbuf->xmaxoutputpixels[0])); } static void print_jis_info (struct inquiry_jis_data *jbuf) { DBG (DBG_info, "JIS IDENTIFIER F0H\n"); DBG (DBG_info, "[00] devtype %#02x\n", jbuf->devtype); DBG (DBG_info, "[01] Page Code %#02x\n", jbuf->pagecode); DBG (DBG_info, "[02] JIS Ver %#02x\n", jbuf->jisversion); DBG (DBG_info, "[03] reserved1 %#02x\n", jbuf->reserved1); DBG (DBG_info, "[04] Page Len %#02x\n", jbuf->alloclen); DBG (DBG_info, "[05] BasicXRes %lu\n", _2btol (&jbuf->BasicRes.x[0])); DBG (DBG_info, "[07] BasicYRes %lu\n", _2btol (&jbuf->BasicRes.y[0])); DBG (DBG_info, "[09] Resolution step %#02x\n", jbuf->resolutionstep); DBG (DBG_info, "[10] MaxXRes %lu\n", _2btol (&jbuf->MaxRes.x[0])); DBG (DBG_info, "[12] MaxYRes %lu\n", _2btol (&jbuf->MaxRes.y[0])); DBG (DBG_info, "[14] MinXRes %lu\n", _2btol (&jbuf->MinRes.x[0])); DBG (DBG_info, "[16] MinYRes %lu\n", _2btol (&jbuf->MinRes.y[0])); DBG (DBG_info, "[18] Std Res %#0x\n", (jbuf->standardres[0] << 8) | jbuf->standardres[1]); DBG (DBG_info, "[20] Win Width %lu\n", _4btol (&jbuf->Window.width[0])); /* Manual says 4787/12B3H pixels @400dpi = 12in */ DBG (DBG_info, "[24] Win Len %lu\n", _4btol (&jbuf->Window.length[0])); /* Manual says 6803/1A93H pixels @400dpi = 17in) */ DBG (DBG_info, "[28] function %#02x\n", jbuf->functions); DBG (DBG_info, "[29] reserved %#02x\n", jbuf->reserved2); } /* 1-3-1 TEST UNIT READY Byte0: | 0x00 | Byte1: | 7-5 Logical Unit Number | Reserved | Byte2: | Reserved | Byte3: | Reserved | Byte4: | Reserved | Byte5: | 7-6 Vendor Unique | 5-2 Reserved | 1 Flag | 0 Link | */ static SANE_Status test_unit_ready (int fd) { static SANE_Byte cmd[6]; SANE_Status status; DBG (DBG_proc, ">> test_unit_ready\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = HS2P_SCSI_TEST_UNIT_READY; status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0); DBG (DBG_proc, "<< test_unit_ready\n"); return (status); } /* 1-3-2 REQUEST SENSE Byte0: | 0x00 | Byte1: | 7-5 Logical Unit Number | Reserved | Byte2: | Reserved | Byte3: | Reserved | Byte4: | Allocation Length | Byte5: | 7-6 Vendor Unique | 5-2 Reserved | 1 Flag | 0 Link | */ #if 0 static SANE_Status get_sense_data (int fd, SENSE_DATA * sense_data) { SANE_Status status; DBG (DBG_sane_proc, ">> get_sense_data\n"); static SANE_Byte cmd[6]; size_t len; len = sizeof (*sense_data); memset (sense_data, 0, len); memset (cmd, 0, sizeof (cmd)); cmd[0] = HS2P_SCSI_REQUEST_SENSE; cmd[4] = len; status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), sense_data, &len); DBG (DBG_proc, "<< get_sense_data\n"); return (status); } #endif static SANE_Status print_sense_data (int dbg_level, SENSE_DATA * data) { SANE_Status status = SANE_STATUS_GOOD; SANE_Byte *bp, *end; SANE_Int i; DBG (DBG_sane_proc, ">> print_sense_data\n"); bp = (SANE_Byte *) data; end = bp + (SANE_Byte) sizeof (SENSE_DATA); for (i = 0; bp < end; bp++, i++) { DBG (dbg_level, "Byte #%2d is %3d, 0x%02x\n", i, *bp, *bp); } DBG (dbg_level, "Valid=%1d, ErrorCode=%#x\n", (data->error_code & 0x80) >> 7, data->error_code & 0x7F); DBG (dbg_level, "Segment number = %d\n", data->segment_number); DBG (dbg_level, "F-mark=%1d, EOM=%1d, ILI=%1d, Reserved=%1d, SenseKey=%#x\n", (data->sense_key & 0x80) >> 7, (data->sense_key & 0x40) >> 6, (data->sense_key & 0x20) >> 5, (data->sense_key & 0x10) >> 4, (data->sense_key & 0x0F)); DBG (dbg_level, "Information Byte = %lu\n", _4btol (data->information)); DBG (dbg_level, "Additional Sense Length = %d\n", data->sense_length); DBG (dbg_level, "Command Specific Information = %lu\n", _4btol (data->command_specific_information)); DBG (dbg_level, "Additional Sense Code = %#x\n", data->sense_code); DBG (dbg_level, "Additional Sense Code Qualifier = %#x\n", data->sense_code_qualifier); DBG (DBG_proc, "<< print_sense_data\n"); return (status); } static struct sense_key * lookup_sensekey_errmsg (int code) { int i; struct sense_key *k = &sensekey_errmsg[0]; for (i = 0; i < 16; i++, k++) if (k->key == code) return k; return NULL; } static struct ASCQ * lookup_ascq_errmsg (unsigned int code) { unsigned int i; struct ASCQ *k = &ascq_errmsg[0]; for (i = 0; i < 74; i++, k++) if (k->codequalifier == code) return k; return NULL; } /* a sensible sense handler arg is a pointer to the associated HS2P_Scanner structure SENSE DATA FORMAT: 14 bytes bits[7-0] Byte 0: [7]:valid [6-0]:Error Code Byte 1: Segment Number Byte 2: [7]: F-mark; [6]:EOM; [5]:ILI; [4]:reserved; [3-0]:Sense Key Byte 3: Information Byte Byte 4: Information Byte Byte 5: Information Byte Byte 6: Information Byte Byte 7: Additional Sense Length (n-7) Byte 8: Command Specific Information Byte 9: Command Specific Information Byte 10: Command Specific Information Byte 11: Command Specific Information Byte 12: Additional Sense Code Byte 13: Additional Sense Code Qualifier */ static SANE_Status sense_handler (int __sane_unused__ scsi_fd, u_char * sense_buffer, void *sd) { u_char sense, asc, ascq, EOM, ILI, ErrorCode, ValidData; u_long MissingBytes; char *sense_str = ""; struct sense_key *skey; struct ASCQ *ascq_key; SENSE_DATA *sdp = (SENSE_DATA *) sd; SANE_Int i; SANE_Status status = SANE_STATUS_INVAL; SANE_Char print_sense[(16 * 3) + 1]; DBG (DBG_proc, ">> sense_handler\n"); if (DBG_LEVEL >= DBG_info) print_sense_data (DBG_LEVEL, (SENSE_DATA *) sense_buffer); /* store sense_buffer */ DBG (DBG_info, ">> copying %lu bytes from sense_buffer[] to sense_data\n", (u_long) sizeof (SENSE_DATA)); memcpy (sdp, sense_buffer, sizeof (SENSE_DATA)); if (DBG_LEVEL >= DBG_info) print_sense_data (DBG_LEVEL, sdp); ErrorCode = sense_buffer[0] & 0x7F; ValidData = (sense_buffer[0] & 0x80) != 0; sense = sense_buffer[2] & 0x0f; /* Sense Key */ asc = sense_buffer[12]; /* Additional Sense Code */ ascq = sense_buffer[13]; /* Additional Sense Code Qualifier */ EOM = (sense_buffer[2] & 0x40) != 0; /* End Of Media */ ILI = (sense_buffer[2] & 0x20) != 0; /* Invalid Length Indicator */ MissingBytes = ValidData ? _4btol (&sense_buffer[3]) : 0; DBG (DBG_sense, "sense_handler: sense_buffer=%#x, sense=%#x, asc=%#x, ascq=%#x\n", sense_buffer[0], sense, asc, ascq); DBG (DBG_sense, "sense_handler: ErrorCode %02x ValidData: %d " "EOM: %d ILI: %d MissingBytes: %lu\n", ErrorCode, ValidData, EOM, ILI, MissingBytes); memset (print_sense, '\0', sizeof (print_sense)); for (i = 0; i < 16; i++) sprintf (print_sense + strlen (print_sense), "%02x ", sense_buffer[i]); DBG (DBG_sense, "sense_handler: sense=%s\n", print_sense); if (ErrorCode != 0x70 && ErrorCode != 0x71) { DBG (DBG_error, "sense_handler: error code is invalid.\n"); return SANE_STATUS_IO_ERROR; /* error code is invalid */ } skey = lookup_sensekey_errmsg (sense); /* simple sequential search */ DBG (DBG_sense, "sense_handler: sense_key=%#x '%s - %s'\n", skey->key, skey->meaning, skey->description); DBG (DBG_sense, "Looking up ascq=(%#x,%#x)=%#x\n", asc, ascq, (asc << 8) | ascq); ascq_key = lookup_ascq_errmsg ((asc << 8) | ascq); /* simple sequential search */ DBG (DBG_sense, "sense_handler: ascq=(%#x,%#x): %#x '%s'\n", asc, ascq, ascq_key->codequalifier, ascq_key->description); /* handle each sense key: Translate from HS2P message to SANE_STATUS_ message * SANE_STATUS_GOOD, _ACCESS_DEINIED, _NO_MEM, _INVAL, _IO_ERROR, _DEVICE_BUSY, * _EOF, _UNSUPPORTED, _CANCELLED, _JAMMED, _NO_DOCS, _COVER_OPEN */ switch (sense) { case 0x00: /* no sense */ status = SANE_STATUS_GOOD; break; case 0x01: /* recovered error */ status = SANE_STATUS_INVAL; break; case 0x02: /* not ready */ status = SANE_STATUS_DEVICE_BUSY; break; case 0x03: /* medium error */ status = SANE_STATUS_JAMMED; break; case 0x04: /* hardware error */ status = SANE_STATUS_IO_ERROR; break; case 0x05: /* illegal request */ status = SANE_STATUS_INVAL; break; case 0x06: /* unit attention */ status = SANE_STATUS_GOOD; break; case 0x07: /* data protect */ status = SANE_STATUS_INVAL; break; case 0x08: /* blank check */ status = SANE_STATUS_INVAL; break; case 0x09: /* vendor specific */ status = SANE_STATUS_INVAL; break; case 0x0A: /* copy aborted */ status = SANE_STATUS_CANCELLED; break; case 0x0B: /* aborted command */ status = SANE_STATUS_CANCELLED; break; case 0x0C: /* equal */ status = SANE_STATUS_INVAL; break; case 0x0D: /* volume overflow */ status = SANE_STATUS_INVAL; break; case 0x0E: /* miscompare */ status = SANE_STATUS_INVAL; break; case 0x0F: /* reserved */ status = SANE_STATUS_INVAL; break; } if (ErrorCode == 0x70) /* Additional Sense Codes available */ switch ((asc << 8) | ascq) { case 0x0000: /* No additional Information */ status = SANE_STATUS_GOOD; break; case 0x0002: /* End of Medium */ status = SANE_STATUS_NO_DOCS; break; case 0x0005: /* End of Data */ status = SANE_STATUS_EOF; break; case 0x0400: /* LUN not ready */ status = SANE_STATUS_DEVICE_BUSY; break; case 0x0401: /* LUN becoming ready */ status = SANE_STATUS_DEVICE_BUSY; break; case 0x0403: /* LUN not ready. Manual intervention needed */ status = SANE_STATUS_IO_ERROR; break; case 0x0500: /* LUN doesn't respond to selection */ status = SANE_STATUS_INVAL; break; case 0x0700: /* Multiple peripheral devices selected */ status = SANE_STATUS_INVAL; break; case 0x1100: /* Unrecovered read error */ status = SANE_STATUS_IO_ERROR; break; case 0x1101: /* Read retries exhausted */ status = SANE_STATUS_IO_ERROR; break; case 0x1501: /* Mechanical positioning error */ status = SANE_STATUS_IO_ERROR; break; case 0x1A00: /* Parameter list length error */ status = SANE_STATUS_INVAL; break; case 0x2000: /* Invalid command operation code */ status = SANE_STATUS_INVAL; break; case 0x2400: /* Invalid field in CDB (check field pointer) */ status = SANE_STATUS_INVAL; break; case 0x2500: /* LUN not supported */ status = SANE_STATUS_UNSUPPORTED; break; case 0x2600: /* Invalid field in parameter list (check field pointer) */ status = SANE_STATUS_INVAL; break; case 0x2900: /* Power on, reset, or BUS DEVICE RESET occurred */ status = SANE_STATUS_GOOD; break; case 0x2A01: /* (MODE parameter changed) */ status = SANE_STATUS_INVAL; break; case 0x2C00: /* Command sequence error */ status = SANE_STATUS_INVAL; break; case 0x2C01: /* Too many windows specified */ status = SANE_STATUS_INVAL; break; case 0x2C02: /* Invalid combination of windows specified */ status = SANE_STATUS_INVAL; break; case 0x3700: /* (Rounded parameter) */ status = SANE_STATUS_INVAL; break; case 0x3900: /* (Saving parameters not supported) */ status = SANE_STATUS_INVAL; break; case 0x3A00: /* Medium not present */ status = SANE_STATUS_NO_DOCS; break; case 0x3B09: /* Read past end of medium */ status = SANE_STATUS_EOF; break; case 0x3B0B: /* Position past end of medium */ status = SANE_STATUS_EOF; break; case 0x3D00: /* Invalid bits in IDENTIFY message */ status = SANE_STATUS_INVAL; break; case 0x4300: /* Message error */ status = SANE_STATUS_INVAL; break; case 0x4500: /* Select/Reselect failure */ status = SANE_STATUS_IO_ERROR; break; case 0x4700: /* (SCSI parity error) */ status = SANE_STATUS_IO_ERROR; break; case 0x4800: /* Initiator detected error message received */ status = SANE_STATUS_IO_ERROR; break; case 0x4900: /* Invalid message error */ status = SANE_STATUS_INVAL; break; case 0x4B00: /* Data phase error */ status = SANE_STATUS_IO_ERROR; break; case 0x5300: /* (Media Load/Eject failed) */ status = SANE_STATUS_IO_ERROR; break; case 0x6000: /* Lamp failure */ status = SANE_STATUS_IO_ERROR; break; case 0x6001: /* Shading error */ status = SANE_STATUS_IO_ERROR; break; case 0x6002: /* White adjustment error */ status = SANE_STATUS_IO_ERROR; break; case 0x6010: /* Reverse Side Lamp Failure */ status = SANE_STATUS_IO_ERROR; break; case 0x6200: /* Scan head positioning error */ status = SANE_STATUS_IO_ERROR; break; case 0x6300: /* Document Waiting Cancel */ status = SANE_STATUS_CANCELLED; break; case 0x8000: /* (PSU over heate) */ status = SANE_STATUS_IO_ERROR; break; case 0x8001: /* (PSU 24V fuse down) */ status = SANE_STATUS_IO_ERROR; break; case 0x8002: /* (ADF 24V fuse down) */ status = SANE_STATUS_IO_ERROR; break; case 0x8003: /* (5V fuse down) */ status = SANE_STATUS_IO_ERROR; break; case 0x8004: /* (-12V fuse down) */ status = SANE_STATUS_IO_ERROR; break; case 0x8100: /* (ADF 24V power off) */ status = SANE_STATUS_IO_ERROR; break; case 0x8102: /* (Base 12V power off) */ status = SANE_STATUS_IO_ERROR; break; case 0x8103: /* Lamp cover open (Lamp 24V power off) */ status = SANE_STATUS_IO_ERROR; break; case 0x8104: /* (-12V power off) */ status = SANE_STATUS_IO_ERROR; break; case 0x8105: /* (Endorser 6V power off) */ status = SANE_STATUS_IO_ERROR; break; case 0x8106: /* SCU 3.3V power down error */ status = SANE_STATUS_IO_ERROR; break; case 0x8107: /* RCU 3.3V power down error */ status = SANE_STATUS_IO_ERROR; break; case 0x8108: /* OIPU 3.3V power down error */ status = SANE_STATUS_IO_ERROR; break; case 0x8200: /* Memory Error (Bus error) */ status = SANE_STATUS_IO_ERROR; break; case 0x8210: /* Reverse-side memory error (Bus error) */ status = SANE_STATUS_IO_ERROR; break; case 0x8300: /* (Image data processing LSI error) */ status = SANE_STATUS_IO_ERROR; break; case 0x8301: /* (Interface LSI error) */ status = SANE_STATUS_IO_ERROR; break; case 0x8302: /* (SCSI controller error) */ status = SANE_STATUS_IO_ERROR; break; case 0x8303: /* (Compression unit error) */ status = SANE_STATUS_IO_ERROR; break; case 0x8304: /* (Marker detect unit error) */ status = SANE_STATUS_IO_ERROR; break; case 0x8400: /* Endorser error */ status = SANE_STATUS_IO_ERROR; break; case 0x8500: /* (Origin Positioning error) */ status = SANE_STATUS_IO_ERROR; break; case 0x8600: /* Mechanical Time Out error (Pick Up Roller error) */ status = SANE_STATUS_IO_ERROR; break; case 0x8700: /* (Heater error) */ status = SANE_STATUS_IO_ERROR; break; case 0x8800: /* (Thermistor error) */ status = SANE_STATUS_IO_ERROR; break; case 0x8900: /* ADF cover open */ status = SANE_STATUS_COVER_OPEN; break; case 0x8901: /* (ADF lift up) */ status = SANE_STATUS_COVER_OPEN; break; case 0x8902: /* Document jam error for ADF */ status = SANE_STATUS_JAMMED; break; case 0x8903: /* Document misfeed for ADF */ status = SANE_STATUS_JAMMED; break; case 0x8A00: /* (Interlock open) */ status = SANE_STATUS_COVER_OPEN; break; case 0x8B00: /* (Not enough memory) */ status = SANE_STATUS_NO_MEM; break; case 0x8C00: /* Size Detection failed */ status = SANE_STATUS_IO_ERROR; break; default: /* Should never get here */ status = SANE_STATUS_INVAL; DBG (DBG_sense, "sense_handler: 'Undocumented code': ascq=(%#x,%#x)\n", asc & 0xFF00, ascq & 0x00FF); break; } DBG (DBG_proc, "sense_handler %s: '%s'-'%s' '%s' return:%d\n", sense_str, skey->meaning, skey->description, ascq_key->description, status); return status; } /* VPD IDENTIFIER Page Code 0x00 * A list of all Page Codes supported by scanner is returned as data * Byte0 => bit7-5: Peripheral Qualifier, bits4-0: Peripheral Device Type * Byte1 => Page Code of CDB is set as Page Code 0 * Byte2 => Reserved * Byte3 => Page Length is 2 because scanner supports just two page codes: C0H and F0H * Byte4 => First Support Page Code * Byte5 => Second Support Page Code */ #if 0 static SANE_Status vpd_indentifier_00H (int fd) { static SANE_Byte cmd[6]; SANE_Status status; DBG (DBG_proc, ">> vpd_identifier_00H\n"); cmd[0] = HS2P_SCSI_REQUEST_SENSE; memset (cmd, 0, sizeof (cmd)); status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0); DBG (DBG_proc, "<< vpd_identifier_00H\n"); return (status); } #endif #if 0 static SANE_Status vpd_identifier_C0H (int fd) { static SANE_Byte cmd[6]; SANE_Status status; DBG (DBG_proc, ">> vpd_identifier_C0H\n"); cmd[0] = HS2P_SCSI_REQUEST_SENSE; memset (cmd, 0, sizeof (cmd)); status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0); DBG (DBG_proc, "<< vpd_identifier_C0H\n"); return (status); } #endif /* 1-3-3 INQUIRY : 6 bytes: * Byte0 => 0x12 * Byte1 => bits7-5: Logical Unit number * bits4-1: Reserved * bit0: EVPD * Byte2 => Page Code * Byte3 => Reserved * Byte4 => Allocation Length * Byte5 => bits7-6: Vendor Unique * bits5-2: Reserved * bit1: Flag * bit0: Link */ static SANE_Status inquiry (int fd, void *buf, size_t * buf_size, SANE_Byte evpd, SANE_Byte page_code) { static SANE_Byte cmd[6]; SANE_Status status; DBG (DBG_proc, ">> inquiry\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = HS2P_SCSI_INQUIRY; cmd[1] = evpd; cmd[2] = page_code; /*cmd[3] Reserved */ cmd[4] = *buf_size; /*cmd[5] vendorunique+reserved+flag+link */ status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, buf_size); DBG (DBG_proc, "<< inquiry\n"); return (status); } /* 1-3-6 MODE SELECT -- sets various operation mode parameters for scanner */ static SANE_Status mode_select (int fd, MP * settings) { static struct { SELECT cmd; /* Mode page Select command */ MP mp; /* Hdr + Parameters */ } msc; /* Mode Select Command */ SANE_Status status; size_t npages; DBG (DBG_proc, ">> mode_select\n"); memset (&msc, 0, sizeof (msc)); /* Fill struct with zeros */ msc.cmd.opcode = HS2P_SCSI_MODE_SELECT; /* choose Mode Select Command */ msc.cmd.byte1 &= ~SMS_SP; /* unset bit0 SavePage to 0 */ msc.cmd.byte1 |= SMS_PF; /* set bit4 PageFormat to 1 */ npages = (settings->page.code == 2) ? 16 : 8; msc.cmd.len = sizeof (msc.mp.hdr) + npages; /* either 4+8 or 4+20 */ memcpy (&msc.mp, settings, msc.cmd.len); /* Copy hdr+pages from Settings to msc.mp */ memset (&msc.mp.hdr, 0, sizeof (msc.mp.hdr)); /* make sure the hdr is all zeros */ /* msc.hdr.data_len = 0x00; msc.hdr.medium_type = 0x00; msc.hdr.dev_spec = 0x00; msc.hdr.blk_desc_len = 0x00; */ /* Now execute the whole command */ if ((status = sanei_scsi_cmd (fd, &msc, sizeof (msc.cmd) + msc.cmd.len, 0, 0)) != SANE_STATUS_GOOD) { DBG (DBG_error, "ERROR: mode_select: %s\n", sane_strstatus (status)); DBG (DBG_error, "PRINTING CMD BLOCK:\n"); print_bytes (&msc.cmd, sizeof (msc.cmd)); DBG (DBG_error, "PRINTING MP HEADER:\n"); print_bytes (&msc.mp.hdr, sizeof (msc.mp.hdr)); DBG (DBG_error, "PRINTING MP PAGES:\n"); print_bytes (&msc.mp.page, msc.cmd.len); } DBG (DBG_proc, "<< mode_select\n"); return (status); } /* 1-3-7 MODE SENSE -- gets various operation mode parameters from scanner */ static SANE_Status mode_sense (int fd, MP * buf, SANE_Byte page_code) { SANE_Status status; SENSE cmd; /* 6byte cmd */ MP msp; /* Mode Sense Page * 4byte hdr + {2bytes +14 bytes} * buffer to hold mode sense data gotten from scanner */ size_t nbytes; DBG (DBG_proc, ">>>>> mode_sense: fd=%d, page_code=%#02x\n", fd, page_code); nbytes = sizeof (msp); DBG (DBG_info, ">>>>> mode_sense: Zero'ing ModeSenseCommand msc and msp structures\n"); memset (&cmd, 0, sizeof (cmd)); /* Fill cmd struct with zeros */ memset (&msp, 0, sizeof (msp)); /* Fill msp struct with zeros */ /* set up Mode Sense Command */ DBG (DBG_info, ">>>>> mode_sense: Initializing Mode Sense cmd\n"); cmd.opcode = HS2P_SCSI_MODE_SENSE; cmd.dbd &= ~(1 << 3); /* Disable Block Description (bit3) is set to 0 */ cmd.pc = (page_code & 0x3F); /* bits 5-0 */ cmd.pc &= ~(0x03 << 6); /* unset PC Field (bits7-6) * 00 Current Value is the only effective value * 01 Changeable Value * 10 Default Value * 11 Saved Value */ /* cmd.len = ??? Allocation Length */ /* Now execute the whole command and store results in msc */ DBG (DBG_info, ">>>>> mode_sense: sanei_scsi_cmd\n"); DBG (DBG_info, ">>>>> cmd.opcode=%#0x cmd.dbd=%#02x, cmd.pc=%#02x\n", cmd.opcode, cmd.dbd, cmd.pc); nbytes = (page_code == 2) ? 20 : 12; DBG (DBG_info, ">>>>> sizeof(cmd)=%lu sizeof(msp)=%lu sizeof(hdr)=%lu sizeof(page)=%lu requesting %lu bytes\n", (u_long) sizeof (cmd), (u_long) sizeof (msp), (u_long) sizeof (msp.hdr), (u_long) sizeof (msp.page), (u_long) nbytes); status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), &msp, &nbytes); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "ERROR mode_sense: sanei_scsi_cmd error \"%s\"\n", sane_strstatus (status)); DBG (DBG_error, ">>>>> mode sense: number of bytes received from scanner: %lu\n", (u_long) nbytes); DBG (DBG_error, "PRINTING CMD BLOCK:\n"); print_bytes (&cmd, sizeof (cmd)); DBG (DBG_error, "PRINTING MP HEADER:\n"); print_bytes (&msp.hdr, sizeof (msp.hdr)); DBG (DBG_error, "PRINTING MP PAGES:\n"); print_bytes (&msp.page, sizeof (msp.page)); } else { /* nbytes = (page_code==2)? 14 : 6; */ DBG (DBG_info, ">> >> got %lu bytes from scanner\n", (u_long) nbytes); nbytes -= 4; /* we won't copy 4 byte hdr */ DBG (DBG_info, ">>>>> copying from msp to calling function's buf\n" ">>>>> msp.page_size=%lu bytes=%lu buf_size=%lu\n", (u_long) sizeof (msp.page), (u_long) nbytes, (u_long) sizeof (*buf)); memcpy (buf, &(msp.page), nbytes); } DBG (DBG_proc, "<<<<< mode_sense\n"); return (status); } static SANE_Status set_window (int fd, SWD * swd) { static struct { struct set_window_cmd cmd; struct set_window_data swd; } win; SANE_Status status; static size_t wdl, tl; /*window descriptor length, transfer length */ DBG (DBG_proc, ">> set_window\n"); /* initialize our struct with zeros */ memset (&win, 0, sizeof (win)); /* fill in struct with opcode */ win.cmd.opcode = HS2P_SCSI_SET_WINDOW; /* bytes 1-5 are reserved */ /* Transfer length is header + window data */ tl = sizeof (*swd); _lto3b (tl, &win.cmd.len[0]); /* 8 + (2*320) = 648 */ DBG (DBG_info, "set_window: SET WINDOW COMMAND Transfer Length = %lu (should be 648)\n", (unsigned long) tl); /* Copy data from swd (including 8-byte header) to win.swd */ DBG (DBG_info, "set_window: COPYING %lu bytes from settings to Set Window Command (%lu)\n", (u_long) sizeof (*swd), (u_long) sizeof (win.swd)); if (!memcpy (&(win.swd), swd, sizeof (*swd))) { DBG (DBG_error, "set_window: error with memcpy\n"); } /* Set Window Data Header: 0-5:reserved; 6-7:Window Descriptor Length=640 */ wdl = sizeof (win.swd) - sizeof (win.swd.hdr); _lto2b (wdl, &win.swd.hdr.len[0]); DBG (DBG_info, "set_window: SET WINDOW COMMAND Window Descriptor Length = %lu (should be 640)\n", (unsigned long) wdl); /* Now execute command */ DBG (DBG_info, "set_window: calling sanei_scsi_cmd(%d,&win,%lu, NULL, NULL)\n", fd, (u_long) sizeof (win)); status = sanei_scsi_cmd (fd, &win, sizeof (win), NULL, NULL); /* status = sanei_scsi_cmd2 (fd, &win.cmd, sizeof(win.cmd), &win.swd, sizeof(win.swd), NULL, NULL); */ if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "*********************\n"); DBG (DBG_error, "ERROR: set_window: %s\n", sane_strstatus (status)); DBG (DBG_error, "PRINTING SWD CMD BLK:\n"); print_bytes (&win.cmd, sizeof (win.cmd)); DBG (DBG_error, "PRINTING SWD HEADER:\n"); print_bytes (&win.swd.hdr, sizeof (win.swd.hdr)); DBG (DBG_error, "PRINTING SWD DATA[0]:\n"); print_bytes (&win.swd.data[0], sizeof (win.swd.data[0])); DBG (DBG_error, "PRINTING SWD DATA[1]:\n"); print_bytes (&win.swd.data[1], sizeof (win.swd.data[1])); DBG (DBG_error, "*********************\n"); } DBG (DBG_proc, "<< set_window\n"); return (status); } static SANE_Status get_window (int fd, GWD * gwd) { struct get_window_cmd cmd; SANE_Status status; static size_t gwd_size; DBG (DBG_proc, ">> get_window\n"); gwd_size = sizeof (*gwd); DBG (DBG_info, ">> get_window datalen = %lu\n", (unsigned long) gwd_size); /* fill in get_window_cmd */ memset (&cmd, 0, sizeof (cmd)); /* CLEAR cmd */ cmd.opcode = HS2P_SCSI_GET_WINDOW; cmd.byte1 &= ~0x01; /* unset single bit 0 */ cmd.win_id = 0x00; /* either 0 or 1 */ _lto3b (gwd_size, cmd.len); /* Transfer Length is byte length of DATA to be returned */ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), gwd, &gwd_size); DBG (DBG_proc, "<< get_window, datalen = %lu\n", (unsigned long) gwd_size); return (status); } static void print_window_data (SWD * buf) { int i, j, k; struct hs2p_window_data *data; struct window_section *ws; DBG (DBG_proc, ">> print_window_data\n"); DBG (DBG_info, "HEADER\n"); for (i = 0; i < 6; i++) DBG (DBG_info, "%#02x\n", buf->hdr.reserved[i]); DBG (DBG_info, "Window Descriptor Length=%lu\n\n", _2btol (buf->hdr.len)); for (i = 0; i < 2; i++) { data = &buf->data[i]; DBG (DBG_info, "Window Identifier = %d\n", data->window_id); DBG (DBG_info, "AutoBit = %#x\n", data->auto_bit); DBG (DBG_info, "X-Axis Resolution = %lu\n", _2btol (data->xres)); DBG (DBG_info, "Y-Axis Resolution = %lu\n", _2btol (data->yres)); DBG (DBG_info, "X-Axis Upper Left = %lu\n", _4btol (data->ulx)); DBG (DBG_info, "Y-Axis Upper Left = %lu\n", _4btol (data->uly)); DBG (DBG_info, "Window Width = %lu\n", _4btol (data->width)); DBG (DBG_info, "Window Length = %lu\n", _4btol (data->length)); DBG (DBG_info, "Brightness = %d\n", data->brightness); DBG (DBG_info, "Threshold = %d\n", data->threshold); DBG (DBG_info, "Contrast = %d\n", data->contrast); DBG (DBG_info, "Image Composition = %#0x\n", data->image_composition); DBG (DBG_info, "Bits per Pixel = %d\n", data->bpp); DBG (DBG_info, "Halftone Code = %#0x\n", data->halftone_code); DBG (DBG_info, "Halftone Id = %#0x\n", data->halftone_id); DBG (DBG_info, "Byte29 = %#0x RIF=%d PaddingType=%d\n", data->byte29, data->byte29 & 0x80, data->byte29 & 0x7); DBG (DBG_info, "Bit Ordering = %lu\n", _2btol (data->bit_ordering)); DBG (DBG_info, "Compression Type = %#x\n", data->compression_type); DBG (DBG_info, "Compression Arg = %#x\n", data->compression_arg); for (j = 0; j < 6; j++) DBG (DBG_info, "Reserved=%#x\n", data->reserved2[j]); DBG (DBG_info, "Ignored = %#x\n", data->ignored1); DBG (DBG_info, "Ignored = %#x\n", data->ignored2); DBG (DBG_info, "Byte42 = %#x MRIF=%d Filtering=%d GammaID=%d\n", data->byte42, data->byte42 & 0x80, data->byte42 & 0x70, data->byte42 & 0x0F); DBG (DBG_info, "Ignored = %#x\n", data->ignored3); DBG (DBG_info, "Ignored = %#x\n", data->ignored4); DBG (DBG_info, "Binary Filtering = %#x\n", data->binary_filtering); DBG (DBG_info, "Ignored = %#x\n", data->ignored5); DBG (DBG_info, "Ignored = %#x\n", data->ignored6); DBG (DBG_info, "Automatic Separation = %#x\n", data->automatic_separation); DBG (DBG_info, "Ignored = %#x\n", data->ignored7); DBG (DBG_info, "Automatic Binarization = %#x\n", data->automatic_binarization); for (j = 0; j < 13; j++) DBG (DBG_info, "Ignored = %#x\n", data->ignored8[j]); for (k = 0; k < 8; k++) { ws = &data->sec[k]; DBG (DBG_info, "\n\n"); DBG (DBG_info, "SECTION %d\n", k); DBG (DBG_info, "Section Enable Flat (sef bit) = %#x\n", ws->sef); DBG (DBG_info, "ignored = %d\n", ws->ignored0); DBG (DBG_info, "Upper Left X = %lu\n", _4btol (ws->ulx)); DBG (DBG_info, "Upper Left Y = %lu\n", _4btol (ws->uly)); DBG (DBG_info, "Width = %lu\n", _4btol (ws->width)); DBG (DBG_info, "Length = %lu\n", _4btol (ws->length)); DBG (DBG_info, "Binary Filtering = %#x\n", ws->binary_filtering); DBG (DBG_info, "ignored = %d\n", ws->ignored1); DBG (DBG_info, "Threshold = %#x\n", ws->threshold); DBG (DBG_info, "ignored = %d\n", ws->ignored2); DBG (DBG_info, "Image Composition = %#x\n", ws->image_composition); DBG (DBG_info, "Halftone Id = %#x\n", ws->halftone_id); DBG (DBG_info, "Halftone Code = %#x\n", ws->halftone_code); for (j = 0; j < 7; j++) DBG (DBG_info, "ignored = %d\n", ws->ignored3[j]); } } DBG (DBG_proc, "<< print_window_data\n"); } static SANE_Status read_data (int fd, void *buf, size_t * buf_size, SANE_Byte dtc, u_long dtq) { static struct scsi_rs_scanner_cmd cmd; SANE_Status status; DBG (DBG_proc, ">> read_data buf_size=%lu dtc=0x%2.2x dtq=%lu\n", (unsigned long) *buf_size, (int) dtc, dtq); if (fd < 0) { DBG (DBG_error, "read_data: scanner is closed!\n"); return SANE_STATUS_INVAL; } memset (&cmd, 0, sizeof (cmd)); /* CLEAR */ cmd.opcode = HS2P_SCSI_READ_DATA; cmd.dtc = dtc; _lto2b (dtq, cmd.dtq); _lto3b (*buf_size, cmd.len); DBG (DBG_info, "read_data ready to send scsi cmd\n"); DBG (DBG_info, "opcode=0x%2.2x, dtc=0x%2.2x, dtq=%lu, transfer len =%d\n", cmd.opcode, cmd.dtc, _2btol (cmd.dtq), _3btol (cmd.len)); status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), buf, buf_size); if (status != SANE_STATUS_GOOD) DBG (DBG_error, "read_data: %s\n", sane_strstatus (status)); DBG (DBG_proc, "<< read_data %lu\n", (unsigned long) *buf_size); return (status); } #if 0 static SANE_Status send_data (int fd, void *buf, size_t * buf_size) { static struct scsi_rs_scanner_cmd cmd; SANE_Status status; DBG (DBG_proc, ">> send_data %lu\n", (unsigned long) *buf_size); memset (&cmd, 0, sizeof (cmd)); /* CLEAR */ memcpy (&cmd, buf, sizeof (*buf)); /* Fill in our struct with set values */ cmd.opcode = HS2P_SCSI_SEND_DATA; _lto3b (*buf_size, cmd.len); status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), buf, buf_size); DBG (DBG_proc, "<< send_data %lu\n", (unsigned long) *buf_size); return (status); } #endif static SANE_Bool is_valid_endorser_character (char c) { int i = (int) c; /* 44 characters can be printed by endorser */ if (i >= 0x30 && i <= 0x3A) return SANE_TRUE; /* 0123456789: */ if (i == 0x23) return SANE_TRUE; /* # */ if (i == 0x27) return SANE_TRUE; /* ` */ if (i >= 0x2C && i <= 0x2F) return SANE_TRUE; /* '-./ */ if (i == 0x20) return SANE_TRUE; /* space */ if (i >= 0x41 && i <= 0x5A) return SANE_TRUE; /* ABCDEFGHIJKLMNOPQRSTUVWXYZ */ if (i >= 0x61 && i <= 0x7A) return SANE_TRUE; /* abcdefghijklmnopqrstuvwxyz */ return SANE_FALSE; } static SANE_Status set_endorser_string (int fd, SANE_String s) { struct { struct scsi_rs_scanner_cmd cmd; SANE_Byte endorser[19]; } out; char *t; int i, len; SANE_Status status; DBG (DBG_proc, ">> set_endorser_string %s\n", s); for (i = 0, t = s; *t != '\0' && i < 19; i++) { DBG (DBG_info, "CHAR=%c\n", *t); if (!is_valid_endorser_character (*t++)) return SANE_STATUS_INVAL; } len = strlen (s); memset (&out, 0, sizeof (out)); /* CLEAR */ out.cmd.opcode = HS2P_SCSI_SEND_DATA; /* 2AH */ out.cmd.dtc = 0x80; /* Endorser Data */ _lto3b (len, &out.cmd.len[0]); /* 19 bytes max */ memset (&out.endorser[0], ' ', 19); /* fill with spaces */ memcpy (&out.endorser[0], s, len); status = sanei_scsi_cmd (fd, &out, sizeof (out), NULL, NULL); DBG (DBG_proc, "<< set_endorser_string s=\"%s\" len=%d\n", s, len); return (status); } static SANE_Status hs2p_send_gamma (HS2P_Scanner * s) { SANE_Status status; struct { struct scsi_rs_scanner_cmd cmd; SANE_Byte gamma[2 + GAMMA_LENGTH]; } out; int i; size_t len = sizeof (out.gamma); DBG (DBG_proc, ">> teco_send_gamma\n"); memset (&out, 0, sizeof (out)); /* CLEAR */ out.cmd.opcode = HS2P_SCSI_SEND_DATA; /* 2AH */ out.cmd.dtc = 0x03; /* Gamma Function Data */ _lto3b (len, &out.cmd.len[0]); /* 19 bytes max */ out.gamma[0] = 0x08; /* Gamma ID for Download table */ out.gamma[1] = 0x08; /* The Number of gray scale (M) = 8 */ for (i = 0; i < GAMMA_LENGTH; i++) { out.gamma[i + 2] = s->gamma_table[i]; } status = sanei_scsi_cmd (s->fd, &out, sizeof (out), NULL, NULL); DBG (DBG_proc, "<< teco_send_gamma\n"); return (status); } #if 0 static SANE_Status clear_maintenance_data (int fd, int code, char XorY, int number) { struct { struct scsi_rs_scanner_cmd cmd; char string[20]; } out; SANE_Status status; DBG (DBG_proc, ">> set_maintenance data\n"); memset (&out, 0, sizeof (out)); /* CLEAR */ out.cmd.opcode = HS2P_SCSI_SEND_DATA; /* 2AH */ out.cmd.dtc = 0x85; /* Maintenance Data */ _lto3b (20, out.cmd.len); /* 20 bytes */ switch (code) { case 1: strcpy (out.string, "EEPROM ALL ALL RESET"); break; case 2: strcpy (out.string, "EEPROM ALL RESET"); break; case 3: strcpy (out.string, "ADF RESET"); break; case 4: strcpy (out.string, "FLATBED RESET"); break; case 5: strcpy (out.string, "LAMP RESET"); break; case 6: sprintf (out.string, "EEPROM ADF %c %+4.1d", XorY, number); break; case 7: sprintf (out.string, "EEPROM BOOK %c %4.1d", XorY, number); break; case 8: sprintf (out.string, "WHITE ADJUST DATA %3d", number); break; case 9: strcpy (out.string, "EEPROM FIRST WHITE ODD"); break; case 10: strcpy (out.string, "EEPROM FIRST WHITE EVEN"); break; case 11: strcpy (out.string, "R ADF RESET"); break; case 12: strcpy (out.string, "R LAMP RESET"); break; case 13: sprintf (out.string, "EEPROM R ADF %c %4.1d", XorY, number); break; case 14: strcpy (out.string, "ENDORSER RESET"); break; } status = sanei_scsi_cmd (fd, &out, sizeof (out), NULL, NULL); DBG (DBG_proc, "<< set_maintenance data\n"); return (status); } #endif #if 0 static SANE_Status read_halftone_mask (int fd, SANE_Byte halftone_id, void *buf, size_t * buf_size) { static struct scsi_rs_scanner_cmd cmd; SANE_Status status; SANE_Int len; DBG (DBG_proc, ">> read_halftone_mask\n"); memset (&cmd, 0, sizeof (cmd)); /* CLEAR */ cmd.opcode = HS2P_SCSI_READ_DATA; cmd.dtc = DATA_TYPE_HALFTONE; _lto2b (halftone_id, cmd.dtq); /* Each cell of an NxM dither pattern is 1 byte from the set {2,3,4,6,8,16} */ switch (halftone_id) { case 0x01: len = 32; break; /* 8x4, 45 degree */ case 0x02: len = 36; break; /* 6x6, spiral */ case 0x03: len = 16; break; /* 4x4, spiral */ case 0x04: len = 64; break; /* 8x8, 90 degree */ case 0x05: len = 70; break; /* 70 lines */ case 0x06: len = 95; break; /* 95 lines */ case 0x07: len = 180; break; /* 180 lines */ case 0x08: len = 128; break; /* 16x8, 45 degree */ case 0x09: len = 256; break; /* 16x16, 90 degree */ case 0x0A: len = 64; break; /* 8x8, Bayer */ default: return SANE_STATUS_INVAL; /* Reserved */ } _lto3b (len, cmd.len); status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), buf, buf_size); DBG (DBG_proc, "<< read_halftone_mask\n"); return (status); } #endif #if 0 static SANE_Status set_halftone_mask (int fd, SANE_Byte halftone_id, void *buf, size_t * buf_size) { static struct scsi_rs_scanner_cmd cmd; SANE_Status status; DBG (DBG_proc, ">> set_halftone_mask\n"); memset (&cmd, 0, sizeof (cmd)); /* CLEAR */ cmd.opcode = HS2P_SCSI_READ_DATA; cmd.dtc = DATA_TYPE_HALFTONE; _lto2b (halftone_id, cmd.dtq); /* Each cell of an NxM dither pattern is 1 byte from the set {2,3,4,6,8,16} * 0x80, 0x81 are User definable custom dither patterns */ if (halftone_id != 0x80 && halftone_id != 0x81) return SANE_STATUS_INVAL; _lto3b (*buf_size, cmd.len); status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), buf, buf_size); DBG (DBG_proc, "<< set_halftone_mask\n"); return (status); } #endif #if 0 static SANE_Status read_gamma_function (int fd) { SANE_Status status = SANE_STATUS_GOOD; return (status); } static SANE_Status read_endorser_data (int fd) { SANE_Status status = SANE_STATUS_GOOD; return (status); } static SANE_Status read_size_data (int fd) { SANE_Status status = SANE_STATUS_GOOD; return (status); } #endif #if 0 static SANE_Status read_maintenance_data (int fd) { SANE_Status status = SANE_STATUS_GOOD; return (status); } #endif /* Bit0: is 0 if document on ADF; else 1 * Bit1: is 0 if ADF cover is closed; else 1 * Bit2: reserved * Bits7-3: reserved */ #if 0 static SANE_Status read_adf_status (int fd, SANE_Byte * adf_status_byte) { SANE_Status status = SANE_STATUS_GOOD; struct scsi_rs_scanner_cmd cmd; static size_t len = 1; DBG (DBG_proc, ">> read_adf_status\n"); memset (&cmd, 0, sizeof (cmd)); cmd.opcode = HS2P_SCSI_READ_DATA; cmd.dtc = DATA_TYPE_ADF_STATUS; _lto3b (0x01, cmd.len); /* convert 0x01 into 3-byte Transfer Length */ if ((status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), adf_status_byte, &len)) != SANE_STATUS_GOOD) { DBG (DBG_error, "read_adf_status ERROR: %s\n", sane_strstatus (status)); } DBG (DBG_proc, "<< read_adf_status\n"); return (status); } #endif /* * read_ipu_photoletter_parameters * read_ipu_threshold_parameters * read_sensor_data (WHAT DATA TYPE CODE?) */ /* SEND CMD */ /* * send_halftone_mask * send_gamma_function * send_endorser_data * send_maintenance_data * EPROM All Clear * EPROM Counter Clear * ADF Counter Clear * Flatbed Counter Clear * Lamp Counter Clear * ADF Register Data * Flatbed Register Data * White Adjustment Data * White level first Data (ODD) * White level first Data (EVEN) * Reverse side ADF Counter Clear * Reverse side Lamp Counter Clear * Reverse side ADF Register Data * Endorser Character Counter Clear * send_ipu_parameters */ /* OBJECT POSITION */ /* GET DATA BUFFER STATUS */ /* 1-3-4 MODE SELECT */ /* 1-3-5 Reserve Unit: 0x16 * 1-3-6 Release Unit: 0x17 */ static SANE_Status unit_cmd (int fd, SANE_Byte opcode) { static struct { SANE_Byte opcode; /* 16H: Reserve Unit 17H: Release Unit */ SANE_Byte byte1; /* 7-5: LUN; 4: 3rd Party; 3-1: 3rd Party Device; 0: Reserved */ SANE_Byte reserved[3]; SANE_Byte control; /* 7-6: Vendor Unique; 5-2: Reserved; 1: Flag; 0: Link */ } cmd; SANE_Byte LUN = (0x00 & 0x07) << 5; SANE_Status status; DBG (DBG_proc, ">> unit_cmd\n"); cmd.opcode = opcode; cmd.byte1 = LUN & 0xE1; /* Mask=11100001 3rd Party and 3rd Party Device must be 0 */ memset (&cmd, 0, sizeof (cmd)); status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), 0, 0); DBG (DBG_proc, "<< unit_cmd\n"); return (status); } /* The OBJECT POSITION command is used for carriage control or * document feed and eject with ADF * * Position Function: Byte1 bits2-0 * 000 Unload instructs document eject * 001 Load instructs document feed to scan start position * 010 Absolute Positioning - instructs carriage to move to carriage lock position * The carriage moves in the Y-axis direction as the amount set in Count when * count>0 * (Not supported in IS420) * */ static SANE_Status object_position (int fd, int load) { static struct scsi_object_position_cmd cmd; SANE_Status status; DBG (DBG_proc, ">> object_position\n"); /* byte 0 opcode * byte 1 position function * bytes 2-4 reserved * bytes 5-8 reserved * byte 9 control */ memset (&cmd, 0, sizeof (cmd)); cmd.opcode = HS2P_SCSI_OBJECT_POSITION; if (load) cmd.position_func = OBJECT_POSITION_LOAD; else cmd.position_func = OBJECT_POSITION_UNLOAD; status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), 0, 0); DBG (DBG_proc, "<< object_position\n"); return (status); } static SANE_Status get_data_status (int fd, STATUS_DATA * dbs) { static GET_DBS_CMD cmd; static STATUS_BUFFER buf; /* hdr + data */ size_t bufsize = sizeof (buf); SANE_Status status; DBG (DBG_proc, ">> get_data_status %lu\n", (unsigned long) bufsize); /* Set up GET DATA BUFFER STATUS cmd */ memset (&cmd, 0, sizeof (cmd)); /* CLEAR cmd */ cmd.opcode = HS2P_SCSI_GET_BUFFER_STATUS; cmd.wait &= ~0x01; /* unset Wait bit0 */ _lto2b (bufsize, cmd.len); /* Now execute cmd, and put returned results in buf */ status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), &buf, &bufsize); /* Now copy from buf.data to dbs */ memcpy (dbs, &buf.data, sizeof (*dbs)); if (status == SANE_STATUS_GOOD && ((unsigned int) _3btol (buf.hdr.len) <= sizeof (*dbs) || _3btol (buf.data.filled) == 0)) { DBG (DBG_info, "get_data_status: busy\n"); status = SANE_STATUS_DEVICE_BUSY; } DBG (DBG_proc, "<< get_data_status %lu\n", (unsigned long) bufsize); return (status); } /* 1-3-7 MODE SENSE */ /* 1-3-8 SCAN */ /* 1-3-9 Receive Diagnostic * Byte0: 1CH * Byte1: 7-5 LUN; 4-0: reserved * Byte2: Reserved * Byte3-4: Allocation Length * Byte5: 7-6: Vendor Unique; 5-2: Reserved; 1: Flag; 0: Link * * This command is treated as a dummy command * Return GOOD unless there is an error in command in which case it returns CHECK */ /* * The IS450 performs 7 self-diagnostics tests * 1) Home position error check * 2) Exposure lamp error check * 3) White level error check * 4) Document table error check * 5) SCU error check * 6) RCU error check * 7) Memory error check * * and uses the lights on the scanner to indicate the result * * PowerOn MachineBusy DocumentInPlace Error * (green) (green) (green) (red) * * SCU error check Blinking Blinking * RCU error check Blinking On Blinking * Home position error check Blinking Blinking Blinking On * Exposure lamp error check Blinking Blinking On On * White level error check Blinking Blinking * Memory Error (Simplex) Blinking * Memory Error (Duplex) Blinking * */ #if 0 static SANE_Status receive_diagnostic (int fd) { static SANE_Byte cmd[6]; SANE_Status status; DBG (DBG_proc, ">> receive_diagnostic\n"); cmd[0] = HS2P_SCSI_RECEIVE_DIAGNOSTICS; memset (cmd, 0, sizeof (cmd)); status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0); DBG (DBG_proc, "<< receive_diagnostic\n"); return (status); } #endif /* 1-3-10 Send Diagnostic * Byte0: 1DH * Byte1: 7-5 LUN; 4: PF; 3: Reserved; 2: S-Test; 1: DevOfl; 0: U-Ofl * Byte2: Reserved * Byte3-4: Parameter List Length * Byte5: 7-6: Vendor Unique; 5-2: Reserved; 1: Flag; 0: Link * This command executes self-diagnostic and optical-adjustment * PF, DevOfl, and Parameter List Length must be 0 or CHECK condition is returned. */ #if 0 static SANE_Status send_diagnostic (int fd) { static SANE_Byte cmd[6]; SANE_Status status; DBG (DBG_proc, ">> send_diagnostic\n"); cmd[0] = HS2P_SCSI_SEND_DIAGNOSTICS; cmd[1] = 0x00 & (1 << 2) & 0xED; /* Set Self-Test bit and clear PF, DevOfl bits */ cmd[3] = 0x00; cmd[4] = 0x00; /* Parameter list (bytes3-4) must be 0x00 */ memset (cmd, 0, sizeof (cmd)); status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0); DBG (DBG_proc, "<< send_diagnostic\n"); return (status); } #endif /* 1-3-8 SCAN command is used to instruct scanner to start scanning */ static SANE_Status trigger_scan (HS2P_Scanner * s) { static struct { START_SCAN cmd; SANE_Byte wid[2]; /* scanner supports up to 2 windows */ } scan; SANE_Status status; DBG (DBG_proc, ">> trigger scan\n"); memset (&scan, 0, sizeof (scan)); /* CLEAR scan */ scan.cmd.opcode = HS2P_SCSI_START_SCAN; /* Transfer length is the byte length of Window List transferred * Window List is a list of Window Identifier created by SET WINDOW command * Since only 1 Window is supported by SCAN command, 0 or 1 is used for Window Identifier * and 1 or 2 for length status = sanei_scsi_cmd (s->fd, &trigger, sizeof (trigger), &window_id_list[0], &wl_size); */ scan.cmd.len = (s->val[OPT_DUPLEX].w == SANE_TRUE) ? 2 : 1; DBG (DBG_info, "trigger_scan: sending %d Window Id to scanner\n", scan.cmd.len); status = sanei_scsi_cmd (s->fd, &scan, sizeof (scan.cmd) + scan.cmd.len, NULL, NULL); DBG (DBG_proc, "<< trigger scan\n"); return (status); } #define MAX_WAITING_TIME 15 static SANE_Status hs2p_wait_ready (HS2P_Scanner * s) { STATUS_DATA dbs; /* Status Buffer Status DATA */ time_t now, start; SANE_Status status; start = time (NULL); while (1) { status = get_data_status (s->fd, &dbs); switch (status) { default: /* Ignore errors while waiting for scanner to become ready. Some SCSI drivers return EIO while the scanner is returning to the home position. */ DBG (DBG_error, "scsi_wait_ready: get datat status failed (%s)\n", sane_strstatus (status)); /* fall through */ case SANE_STATUS_DEVICE_BUSY: now = time (NULL); if (now - start >= MAX_WAITING_TIME) { DBG (DBG_error, "hs2p_wait_ready: timed out after %lu seconds\n", (u_long) (now - start)); return SANE_STATUS_INVAL; } break; case SANE_STATUS_GOOD: DBG (DBG_proc, "hs2p_wait_ready: %d bytes ready\n", _3btol (dbs.filled)); return status; break; } usleep (1000000); /* retry after 100ms */ } return SANE_STATUS_INVAL; } /* MODE PAGES GET/SET */ static SANE_Status connection_parameters (int fd, MP_CXN * settings, SANE_Bool flag) { SANE_Status status; MP_CXN buf; size_t nbytes; DBG (DBG_proc, ">> connection_parameters\n"); nbytes = sizeof (buf); if (flag) { /* GET */ DBG (DBG_info, ">> GET connection_parameters >> calling mode_sense\n"); status = mode_sense (fd, (MP *) & buf, (SANE_Byte) PAGE_CODE_CONNECTION); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "get_connection_parameters: MODE_SELECT failed with status=%d\n", status); return (status); } memcpy (settings, &buf, nbytes); } else { /* SET */ DBG (DBG_info, ">> SET connection_parameters >> calling mode_select\n"); /* Fill in struct then hand off to mode_select */ memset (&buf, 0, sizeof (buf)); /* Fill struct with zeros */ memcpy (&buf, settings, nbytes); /* Make sure calling function didn't change these bytes */ memset (&buf.hdr, 0, sizeof (buf.hdr)); /* Make sure 4bytes are 0 */ buf.code = PAGE_CODE_CONNECTION; /* bits5-0: Page Code 02H */ buf.code &= ~(1 << 7); /* Bit7 PS is set to 0 */ buf.len = 0x0E; /* This is the only page with 14 bytes */ status = mode_select (fd, (MP *) & buf); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "set_connection_parameters: MODE_SELECT failed with status=%d\n", status); return (-1); } } DBG (DBG_proc, "<< connection_parameters\n"); return (status); } static SANE_Status get_basic_measurement_unit (int fd, SANE_Int * bmu, SANE_Int * mud) { SANE_Status status; MP_SMU buf; DBG (DBG_proc, ">> get_basic_measurement_unit: fd=\"%d\"\n", fd); status = mode_sense (fd, (MP *) & buf, (SANE_Byte) PAGE_CODE_SCANNING_MEASUREMENTS); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "set_basic_measurement_unit: MODE_SELECT failed with status=%d\n", status); return (SANE_STATUS_INVAL); } *bmu = buf.bmu; *mud = ((buf.mud[0] << 8) | buf.mud[1]); DBG (DBG_proc, "<< get_basic_measurement_unit: bmu=%d mud=%d\n", *bmu, *mud); return (status); } static SANE_Status set_basic_measurement_unit (int fd, SANE_Byte bmu) { MP_SMU buf; /* Mode Page Scanning Measurements Page Code */ SANE_Status status; SANE_Int mud; size_t bufsize = sizeof (buf); DBG (DBG_proc, ">> set_basic_measurement_unit: %d\n", bmu); /* Set up buf */ memset (&buf, 0, bufsize); /* CLEAR buf */ buf.code = PAGE_CODE_SCANNING_MEASUREMENTS; /* bits5-0: Page Code */ buf.code &= ~(1 << 7); /* Bit7 PS is set to 0 */ buf.len = 0x06; buf.bmu = bmu; /* Power on default is POINTS */ mud = (bmu == INCHES) ? DEFAULT_MUD : 1; DBG (DBG_info, "SET_BASIC_MEASUREMENT_UNIT: bmu=%d mud=%d\n", bmu, mud); _lto2b (mud, &buf.mud[0]); /* buf.mud[0] = (mud >> 8) & 0xff; buf.mud[1] = (mud & 0xff); */ status = mode_select (fd, (MP *) & buf); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "set_basic_measurement_unit: MODE_SELECT failed with status=%d\n", status); status = SANE_STATUS_INVAL; } DBG (DBG_proc, "<< set_basic_measurement_unit: opcode=%d len=%d bmu=%d mud=%ld\n", buf.code, buf.len, buf.bmu, _2btol (&buf.mud[0])); return (status); } static SANE_Status adf_control (int fd, SANE_Bool flag, SANE_Byte * adf_control, SANE_Byte * adf_mode, SANE_Byte * mwt) { SANE_Status status; MP_ADF buf; size_t bufsize = sizeof (buf); DBG (DBG_proc, ">> adf_control\n"); memset (&buf, 0, bufsize); /* Fill struct with zeros */ if (flag) { /* GET */ DBG (DBG_info, ">> GET ADF_control>> calling mode_sense\n"); status = mode_sense (fd, (MP *) & buf, (SANE_Byte) PAGE_CODE_ADF_CONTROL); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "get_adf_control: MODE_SELECT failed\n"); return (status); } *adf_control = buf.adf_control; *adf_mode = buf.adf_mode_control; *mwt = buf.medium_wait_timer; } else { /* SET */ /* Fill in struct then hand off to mode_select */ buf.code = PAGE_CODE_ADF_CONTROL; /* bits5-0: Page Code */ buf.code &= ~(1 << 7); /* Bit7 PS is set to 0 */ buf.len = 0x06; /* Byte2: adf_control: 7-2:reserved; 1-0:adf_control: Default 00H Flatbed, 01H Simplex, 02H Duplex */ /* Byte3: adf_mode_control: 7-3:reserved; 2: Prefeed Mode: 0 invalid, 1 valid; 1-0: ignored */ /* Byte4: medium_wait_timer: timeout period. Not supported */ buf.adf_control = (*adf_control & 0x03); buf.adf_mode_control = (*adf_mode & 0x04); buf.medium_wait_timer = *mwt; status = mode_select (fd, (MP *) & buf); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "set_adf_control: MODE_SELECT failed with status=%d\n", status); return (status); } } DBG (DBG_proc, ">> adf_control\n"); return (status); } static SANE_Status white_balance (int fd, int *val, SANE_Bool flag) { SANE_Status status; MP_WhiteBal buf; /* White Balance Page Code */ size_t bufsize = sizeof (buf); memset (&buf, 0, bufsize); if (flag) { /* GET */ DBG (DBG_proc, ">> GET white_balance>> calling mode_sense\n"); status = mode_sense (fd, (MP *) & buf, (SANE_Byte) PAGE_CODE_WHITE_BALANCE); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "get_white_balance: MODE_SELECT failed with status=%d\n", status); return (status); } *val = buf.white_balance; } else { /* SET */ /* Fill in struct then hand off to mode_select */ memset (&buf, 0, sizeof (buf)); /* Fill struct with zeros */ buf.code = PAGE_CODE_WHITE_BALANCE; /* bits5-0: Page Code */ buf.code &= ~(1 << 7); /* Bit7 PS is set to 0 */ buf.len = 0x06; buf.white_balance = *val; /* Power on default is RELATIVE_WHITE */ status = mode_select (fd, (MP *) & buf); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "set_white_balance: MODE_SELECT failed with status=%d\n", status); return (status); } } DBG (DBG_proc, "<< white balance: buf.white_balance=%#02x\n", buf.white_balance); return (status); } #if 0 static SANE_Int lamp_timer (int fd, int val, SANE_Bool flag) { SANE_Status status; MP_LampTimer buf; /* Lamp Timer Page Code */ DBG (DBG_proc, ">> lamp timer\n"); if (flag) { /* GET */ DBG (DBG_info, ">> GET lamp_timer>> calling mode_sense\n"); if ((status = mode_sense (fd, (MP *) & buf, (SANE_Byte) PAGE_CODE_LAMP_TIMER_SET)) != SANE_STATUS_GOOD) { DBG (DBG_error, "get_lamp_timer: MODE_SELECT failed\n"); return (-1); } } else { /* SET */ /* Fill in struct then hand off to mode_select */ memset (&buf, 0, sizeof (buf)); /* Fill struct with zeros */ buf.code = PAGE_CODE_LAMP_TIMER_SET; /* bits5-0: Page Code */ buf.code &= ~(1 << 7); /* Bit7 PS is set to 0 */ buf.len = 0x06; buf.time_on = val; /* time lamp has been on */ if ((status = mode_select (fd, (MP *) & buf)) != SANE_STATUS_GOOD) { DBG (DBG_error, "set_lamp_timer: MODE_SELECT failed with status=%d\n", status); return (-1); } } DBG (DBG_proc, "<< lamp timer\n"); return (buf.time_on); } #endif static SANE_Status endorser_control (int fd, int *val, SANE_Bool flag) { SANE_Status status; MP_EndCtrl buf; /* MPHdr (4bytes) + MPP (8bytes) */ SANE_Byte mask = 0x7; /* 7-3:reserved; 2-0: Endorser Control */ size_t bufsize = sizeof (buf); DBG (DBG_proc, ">> endorser_control: fd=%d val=%d flag=%d\n", fd, *val, flag); memset (&buf, 0, bufsize); /* Fill struct with zeros */ if (flag) { /* GET */ DBG (DBG_info, ">> GET endorser control >> calling mode_sense\n"); status = mode_sense (fd, (MP *) & buf, (SANE_Byte) PAGE_CODE_ENDORSER_CONTROL); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "get_endorser_control: MODE_SELECT failed with status=%d\n", status); return (status); } *val = buf.endorser_control & mask; } else { /* SET */ DBG (DBG_info, ">> SET endorser control >> calling mode_select\n"); /* Fill in struct then hand off to mode_select */ memset (&buf, 0, sizeof (buf)); /* Fill struct with zeros */ buf.code = PAGE_CODE_ENDORSER_CONTROL; /* bits5-0: Page Code */ buf.code &= ~(1 << 7); /* Bit7 PS is set to 0 */ buf.len = 0x06; buf.endorser_control = *val & mask; /* Power on default is OFF */ status = mode_select (fd, (MP *) & buf); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "set_endorser_control: MODE_SELECT failed with status=%d\n", status); return (status); } } DBG (DBG_proc, "<< endorser_control: endorser_control=%#02x\n", buf.endorser_control); return (status); } /* When SCAN, READ, or LOAD (in ADF mode) is issued, scanner waits until operator panel start button is pressed */ static SANE_Status scan_wait_mode (int fd, int val, SANE_Bool flag) { SANE_Status status; MP_SWM buf; /* Scan Wait Mode Page Code */ DBG (DBG_proc, ">> scan_wait_mode\n"); if (flag) { /* GET */ DBG (DBG_info, ">> GET scan_wait_mode >> calling mode_sense\n"); status = mode_sense (fd, (MP *) & buf, (SANE_Byte) PAGE_CODE_SCAN_WAIT_MODE); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "get_scan_wait_mode: MODE_SELECT failed with status=%d\n", status); return (-1); } } else { /* SET */ /* Fill in struct then hand off to mode_select */ memset (&buf, 0, sizeof (buf)); /* Fill struct with zeros */ buf.code = PAGE_CODE_SCAN_WAIT_MODE; /* bits5-0: Page Code */ buf.code &= ~(1 << 7); /* Bit7 PS is set to 0 */ buf.len = 0x06; buf.swm = 0x00; if (val == 1) buf.swm |= 1; /* set bit 1 if scan_wait_mode ON */ else buf.swm &= ~1; /* unset bit 1 if scan_wait_mode OFF */ DBG (DBG_info, ">> SET scan_wait_mode >> calling mode_sense\n"); if ((status = mode_select (fd, (MP *) & buf)) != SANE_STATUS_GOOD) { DBG (DBG_error, "mode_select ERROR %s\n", sane_strstatus (status)); } } DBG (DBG_proc, "<< scan_wait_mode: buf.swm=%#02x\n", buf.swm); return (status); } /* Selectable when Send Diagnostics command is performed */ static SANE_Int service_mode (int fd, int val, SANE_Bool flag) { SANE_Status status; MP_SRV buf; /* Service Mode Page Code */ DBG (DBG_proc, ">> service_mode\n"); if (flag) { /* GET */ DBG (DBG_info, ">> GET service_mode >> calling mode_sense\n"); status = mode_sense (fd, (MP *) & buf, (SANE_Byte) PAGE_CODE_SERVICE_MODE_SELECT); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "get_service_mode: MODE_SELECT failed with status=%d\n", status); return (-1); } } else { /* SET */ /* Fill in struct then hand off to mode_select */ memset (&buf, 0, sizeof (buf)); /* Fill struct with zeros */ buf.code = PAGE_CODE_SERVICE_MODE_SELECT; /* bits5-0: Page Code */ buf.code &= ~(1 << 7); /* Bit7 PS is set to 0 */ buf.len = 0x06; /* 0H: Self-Diagnostics Mode, 1H: Optical Adjustment Mode */ buf.service = val & 0x01; status = mode_select (fd, (MP *) & buf); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "set_service_mode: MODE_SELECT failed with status=%d\n", status); return (-1); } } DBG (DBG_proc, "<< service_mode\n"); return (buf.service & 0x01); } backends-1.3.0/backend/hs2p-scsi.h000066400000000000000000001336351456256263500166710ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2007 Jeremy Johnson This file is part of a SANE backend for Ricoh IS450 and IS420 family of HS2P Scanners using the SCSI controller. 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, see . 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 "../include/sane/sane.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_scsi.h" #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_thread.h" /* 1-2 SCSI STATUS BYTE KEYS */ #define HS2P_SCSI_STATUS_GOOD 0x00 #define HS2P_SCSI_STATUS_CHECK 0x02 #define HS2P_SCSI_STATUS_BUSY 0x08 #define HS2P_SCSI_STATUS_RESERVATION CONFLICT 0x18 /* All other status byte keys are reserved */ /* * SCSI Command List for Command Descriptor Block * All reserved bit and fields in the CDB must be zero * Values in the CDB described as "Reserved" must no be specified * The FLAG and LINK bits in the CONTROL byte must be zero * Any values in the Vendor Unique field are ignored * The Logical Unit Number in the CDB must always be zero * All Reserved bit and fields in the data fields must be zero * Values of parameters in the data fields described as * "Reserved" or "Not supported" must not be specified */ /* 1-3 SCSI COMMANDS */ #define HS2P_SCSI_TEST_UNIT_READY 0x00 #define HS2P_SCSI_REQUEST_SENSE 0x03 #define HS2P_SCSI_INQUIRY 0x12 #define HS2P_SCSI_MODE_SELECT 0x15 #define HS2P_SCSI_RESERVE_UNIT 0x16 #define HS2P_SCSI_RELEASE_UNIT 0x17 #define HS2P_SCSI_MODE_SENSE 0x1a #define HS2P_SCSI_START_SCAN 0x1b #define HS2P_SCSI_RECEIVE_DIAGNOSTICS 0x1c #define HS2P_SCSI_SEND_DIAGNOSTICS 0x1d #define HS2P_SCSI_SET_WINDOW 0x24 #define HS2P_SCSI_GET_WINDOW 0x25 #define HS2P_SCSI_READ_DATA 0x28 #define HS2P_SCSI_SEND_DATA 0x2a #define HS2P_SCSI_OBJECT_POSITION 0x31 #define HS2P_SCSI_GET_BUFFER_STATUS 0x34 /* Sense Key Defines */ #define HS2P_SK_NO_SENSE 0x00 #define HS2P_SK_RECOVERED_ERROR 0x01 #define HS2P_SK_NOT_READY 0x02 #define HS2P_SK_MEDIUM_ERROR 0x03 #define HS2P_SK_HARDWARE_ERROR 0x04 #define HS2P_SK_ILLEGAL_REQUEST 0x05 #define HS2P_SK_UNIT_ATTENTION 0x06 #define HS2P_SK_DATA_PROJECT 0x07 #define HS2P_SK_BLANK_CHECK 0x08 #define HS2P_SK_VENDOR_UNIQUE 0x09 #define HS2P_SK_COPY_ABORTED 0x0a #define HS2P_SK_ABORTED_COMMAND 0x0b #define HS2P_SK_EQUAL 0x0c #define HS2P_SK_VOLUME_OVERFLOW 0x0d #define HS2P_SK_MISCOMPARE 0x0e #define HS2P_SK_RESERVED 0x0f struct sense_key { int key; char *meaning; char *description; }; static struct sense_key sensekey_errmsg[16] = { {0x00, "NO SENSE", "Indicates that there is no Sense Key information"}, {0x01, "RECOVERED ERROR", "Invalid"}, {0x02, "NOT READY", "Indicates that the scanner is not ready, e.g. ADF cover not closed"}, {0x03, "MEDIUM ERROR", "Error regarding document such as paper jam"}, {0x04, "HARDWARE ERROR", "Error relating to hardware, e.g. CCD line clock error"}, {0x05, "ILLEGAL REQUEST", "Used such as when illegal parameter exists in data or command"}, {0x06, "UNIT ATTENTION", "Used when power on, BUS DEVICE RESET message or hardware reset"}, {0x07, "DATA PROJECT", "Invalid"}, {0x08, "BLANK CHECK", "Invalid"}, {0x09, "VENDOR UNIQUE", "Invalid"}, {0x0a, "COPY ABORTED", "Invalid"}, {0x0b, "ABORTED COMMAND", "Used when scanner aborts a command execution"}, {0x0c, "EQUAL", "Invalid"}, {0x0d, "VOLUME OVERFLOW", "Invalid"}, {0x0e, "MISCOMPARE", "Invalid"}, {0x0f, "RESERVED", "Invalid"} }; /* When Error_Code = 0x70 more detailed information is available: * code, qualifier, description */ struct ASCQ { /* ADDITIONAL SENSE CODE QUALIFIER */ unsigned int codequalifier; char *description; }; static struct ASCQ ascq_errmsg[74] = { {0x0000, "No additional sense information"}, {0x0002, "End of Medium detected"}, {0x0005, "End of Data detected"}, {0x0400, "Logical unit not ready. Don't know why."}, {0x0401, "Logical unit is in process of becoming ready."}, {0x0403, "Logical unit not ready. Manual intervention required."}, {0x0500, "Logical unit does not respond to selection."}, {0x0700, "Multiple peripheral devices selected."}, {0x1100, "Unrecovered read error."}, {0x1101, "Read retries exhausted."}, {0x1501, "Mechanical positioning error."}, {0x1a00, "Parameter list length error."}, {0x2000, "Invalid command operation mode."}, {0x2400, "Invalid field in CDB (check field pointer)."}, {0x2500, "Logical unit not supported."}, {0x2600, "Invalid field in parameter list (check field pointer)."}, {0x2900, "Power on, reset, or BUS DEVICE RESET occurred."}, {0x2a01, "(MODE parameter changed.)"}, {0x2c00, "Command sequence error."}, {0x2c01, "(Too many windows specified."}, {0x2c02, "(Invalid combination of windows specified."}, {0x3700, "(Rounded parameter.)"}, {0x3900, "(Saving parameters not supported.)"}, {0x3a00, "Medium not present."}, {0x3b09, "(Read past end of medium.)"}, {0x3b0b, "(Position past end of medium.)"}, {0x3d00, "Invalid bits in IDENTIFY message."}, {0x4300, "Message error."}, {0x4500, "Select/Reselect failure."}, {0x4700, "(SCSI parity error)"}, {0x4800, "Initiator detected error message received."}, {0x4900, "Invalid message error."}, {0x4a00, "Command phase error."}, {0x4b00, "Data phase error."}, {0x5300, "(Media Load/Eject failed)"}, {0x6000, "Lamp failure"}, {0x6001, "(Shading Error)"}, {0x6002, "White adjustment error"}, {0x6010, "Reverse Side Lamp Failure"}, {0x6200, "Scan head positioning error"}, {0x6300, "Document Waiting Cancel"}, {0x8000, "(PSU overheat)"}, {0x8001, "(PSU 24V fuse down)"}, {0x8002, "(ADF 24V fuse down)"}, {0x8003, "(5V fuse down)"}, {0x8004, "(-12V fuse down)"}, {0x8100, "(ADF 24V power off)"}, {0x8101, "(Base 12V power off)"}, {0x8102, "(SCSI 5V power off)"}, {0x8103, "Lamp cover open (Lamp 24V power off)"}, {0x8104, "(-12V power off)"}, {0x8105, "(Endorser 6V power off)"}, {0x8106, "SCU 3.3V power down error"}, {0x8107, "RCU 3.3V power down error"}, {0x8108, "OIPU 3.3V power down error"}, {0x8200, "Memory Error (Bus error)"}, {0x8210, "Reverse-side memory error (Bus error)"}, {0x8300, "(Image data processing LSI error)"}, {0x8301, "(Interfac LSI error)"}, {0x8302, "(SCSI controller error)"}, {0x8303, "(Compression unit error)"}, {0x8304, "(Marker detect unit error)"}, {0x8400, "Endorser error"}, {0x8500, "(Origin Positioning error)"}, {0x8600, "Mechanical Time Out error (Pick Up Roller error)"}, {0x8700, "(Heater error)"}, {0x8800, "(Thermistor error)"}, {0x8900, "ADF cover open"}, {0x8901, "(ADF lift up)"}, {0x8902, "Document jam error for ADF"}, {0x8903, "Document misfeed for ADF"}, {0x8a00, "(Interlock open)"}, {0x8b00, "(Not enough memory)"}, {0x8c00, "Size detection failed"} }; typedef struct sense_data { /* HS2P_REQUEST_SENSE_DATA */ /* bit7:valid is 1 if information byte is valid, bits6:0 error_code */ SANE_Byte error_code; /* not used, set to 0 */ SANE_Byte segment_number; /* bit7 file-mark (unused, set to 0), bit6 EOM is 1 if end of document detected before completing scan bit5 ILI (incorrect length indicator) is 1 when data length mismatch occurs on READ bits3:0 sense_key indicates error conditions. */ SANE_Byte sense_key; SANE_Byte information[4]; /* fixed at 6 */ SANE_Byte sense_length; /* not used and set to 0 */ SANE_Byte command_specific_information[4]; SANE_Byte sense_code; SANE_Byte sense_code_qualifier; } SENSE_DATA; /* page codes used with HS2P_SCSI_INQUIRY */ #define HS2P_INQUIRY_STANDARD_PAGE_CODE 0x00 #define HS2P_INQUIRY_VPD_PAGE_CODE 0xC0 #define HS2P_INQUIRY_JIS_PAGE_CODE 0xF0 /* * The EVPD and Page Code are used in pair. When the EVPD bit is 0, INQUIRY data * in the standard format is returned to the initiator. When the EVPD bit is 1, * the EVPD information specified by each Page Code is returned in each Page Code * data format. * * EVPD=0x00, Page_Code=0x00 => Standard Data Format * * EVPD=0x01, PAGE_CODE=0x00 => Return list of supported Page Codes * EVPD=0x01, PAGE_CODE=0x01~0x7F => Not Supported * EVPD=0x01, PAGE_CODE=0x80~0x82 => Not Supported * EVPD=0x01, PAGE_CODE=0x83~0xBF => Reserved * EVPD=0x01, PAGE_CODE=0xC0 => RICOH Scanner VPD information * EVPD=0x01, PAGE_CODE=0xF0 => JIS Version VPD information */ struct inquiry_standard_data { /* bits7-5 peripheral qualifier * bits4-0 peripheral device * Peripheral Qualifier and Peripheral Devide Type are not supported on logical unit * Therefore LUN=0 and this field indicates scanner and is set to 0x06 * When LUN!=0 this field becomes 0x1F and means undefined data */ SANE_Byte devtype; /* must be 0x06 */ /* bit7: repaceable media bit is set to 0 * bits6-1: reserved * bit0: EVPD */ SANE_Byte rmb_evpd; /* bits7-6: ISO Version is set to 0 * bits5-3: ECMA Version is set to 0 * bits2-0: ANSI Version is set to 2 */ SANE_Byte version; /* bit7: AENC (asynchronous event notification capability) is set to 0 * bit6: TrmIOP (terminate I/O process) is set to 0 * bits5-4: reserved * bits3-0: Response Data Format is set to 2 */ SANE_Byte response_data_format; /* Additional Length indicate number of bytes which follows, set to 31 */ SANE_Byte length; SANE_Byte reserved[2]; /* bit7: RelAdr (relative addressing) is set to 0 * bit6: Wbus32 is set to 0 * bit5: Wbus16 is set to 0 * bit4: Sync is set to 0 * bit3: Linked is set to 0 * bit2: reserved * bit1: CmdQue is set to 0 * bit0: SftRe is set to 0 * Sync is set to 1 with this scanner to support synchronous data transfer * When DIPSW2 is on, Sync is set to 0 for asynchronous data transfer */ SANE_Byte byte7; SANE_Byte vendor[8]; /* vendor_id="RICOH " */ SANE_Byte product[16]; /* product_id="IS450 " */ SANE_Byte revision[4]; /* product_revision_level="xRxx" where x indicate firmware version number */ }; /* VPD Information [EVPD=0x01, PageCode=0xC0] */ struct inquiry_vpd_data { SANE_Byte devtype; /* bits7-5: Peripheral Qualifier * bits4-0: Peripheral Device Type */ SANE_Byte pagecode; /* Page Code => 0xC0 */ SANE_Byte byte2; /* Reserved */ SANE_Byte pagelength; /* Page Length => 12 (0x0C) */ SANE_Byte adf_id; /* ADF Identification * 0: No ADF is mounted * 1: Single sided ADF is mounted * 2: Double sided ADF is mounted * 3: ARDF is mounted. (Reverse double side scanning available) * 4: Reserved * It should be 1 or 2 with this scanner. */ SANE_Byte end_id; /* Endorser Identification * 0: No endorser * 1: Endorser mounted * 2: Reserved * It should be 0 or 1 with this scanner */ SANE_Byte ipu_id; /* Image Processing Unit Identification * bits 7:2 Reserved * bit 1 0:Extended board not mounted * 1:Extended board is mounted * bit 0 0:IPU is not mounted * 1:IPU is mounted * It should always be 0 with this scanner */ SANE_Byte imagecomposition; /* indicates supported image data type. * This is set to 0x37 * bit0 => Line art supported ? 1:0 * bit1 => Dither supported ? 1:0 * bit2 => Error Diffusion supported ? 1:0 * bit3 => Color supported ? 1:0 * bit4 => 4bits gray scale supported ? 1:0 * bit5 => 5-8bit gray scale supported ? 1:0 * bit6 => 5-8bit gray scale supported ? 1:0 * bit7 => Reserved */ SANE_Byte imagedataprocessing[2]; /* Image Data Processing Method * IPU installed ? 0x18 : 0x00 * Byte8 => White Framing ? 1:0 * Byte9 => Black Framing ? 1:0 * Byte10 => Edge Extraction ? 1:0 * Byte11 => Noise Removal ? 1:0 * Byte12 => Smoothing ? 1:0 * Byte13 => Line Bolding ? 0:1 * Byte14 => Reserved * Byte15 => Reserved */ SANE_Byte compression; /* Compression Method is set to 0x00 * bit0 => MH supported ? 1:0 * bit1 => MR supported ? 1:0 * bit2 => MMR supported ? 1:0 * bit3 => MH (byte boundary) supported ? 1:0 * bit4 => Reserved */ SANE_Byte markerrecognition; /* Marker Recognition Method is set to 0x00 * bit0 => Marker Recognition supported ? 1:0 * bits1-7 => Reserved */ SANE_Byte sizerecognition; /* Size Detection * bit0 => Size Detection Supported ? 1:0 * bits1-7 => Reserved */ SANE_Byte byte13; /* Reserved */ SANE_Byte xmaxoutputpixels[2]; /* X Maximum Output Pixel is set to 4960 (0x1360) * indicates maximum number of pixels in the main * scanning direction that can be output by scanner */ }; struct inquiry_jis_data { /* JIS INFORMATION VPD_IDENTIFIER_F0H */ SANE_Byte devtype; /* 7-5: peripheral qualifier, 4-0: peripheral device type */ SANE_Byte pagecode; SANE_Byte jisversion; SANE_Byte reserved1; SANE_Byte alloclen; /* page length: Set to 25 (19H) */ struct { SANE_Byte x[2]; /* Basic X Resolution: Set to 400 (01H,90H) */ SANE_Byte y[2]; /* Basic Y Resolution: Set to 400 (01H,90H) */ } BasicRes; SANE_Byte resolutionstep; /* 7-4: xstep, 3-0 ystep: Both set to 1 (11H) */ struct { SANE_Byte x[2]; /* Maximum X resolution: Set to 800 (03H,20H) */ SANE_Byte y[2]; /* Maximum Y resolution: Set to 800 (03H,20H) */ } MaxRes; struct { SANE_Byte x[2]; /* Minimum X resolution: Set to 100 (00H,64H) */ SANE_Byte y[2]; /* Minimum Y resolution */ } MinRes; SANE_Byte standardres[2]; /* Standard Resolution: bits 7-0: * byte18: 60, 75,100,120,150,160,180, 200 * byte19: 240,300,320,400,480,600,800,1200 */ struct { SANE_Byte width[4]; /* in pixels based on basic resolution. Set to 4787 (12B3H) */ SANE_Byte length[4]; /* maximum number of scan lines based on basic resolution. Set to 6803 (1A93H) */ } Window; SANE_Byte functions; /* This is set to 0EH: 0001110 * bit0: data overflow possible * bit1: line art support * bit2: dither support * bit3: gray scale support * bits7-4: reserved */ SANE_Byte reserved2; }; #define SMS_SP 0x01 /* Mask for Bit0 */ #define SMS_PF 0x10 /* Mask for Bit4 */ typedef struct scsi_mode_select_cmd { SANE_Byte opcode; /* 15H */ SANE_Byte byte1; /* 7-5:LUN; 4:PF; 2:Reserved; 1:SP * Save Page Bit must be 0 since pages cannot be saved * Page Format Bit must be 1 */ SANE_Byte reserved[2]; SANE_Byte len; /* Parameter List Length */ SANE_Byte control; /* 7-6:Vendor Unique; 5-2:Reserved; 1:Flag; 0:Link */ } SELECT; /* MODE SELECT PARAMETERS: * 0-n Mode Parameter Header * 0-n mode Block Descriptor (not used) * 0-n mode Page */ typedef struct scsi_mode_parameter_header { SANE_Byte data_len; /* Mode Data Length NOT USED so must be 0 */ SANE_Byte medium_type; /* Medium Type NOT USED so must be 0 */ SANE_Byte dev_spec; /* Device Specific Parameter NOT USED so must be 0 */ SANE_Byte blk_desc_len; /* Block Descriptor Length is set to 0 */ } MPHdr; typedef struct page { SANE_Byte code; /* 7:PS; 6:Reserved; 5-0:Page Code */ SANE_Byte len; /* set to 14 when MPC=02H and 6 otherwise */ SANE_Byte parameter[14]; /* either 14 or 6, so let's allow room for 14 */ } MPP; /* Mode Page Parameters */ typedef struct mode_pages { MPHdr hdr; /* Mode Page Header */ MPP page; /* Mode Page Parameters */ } MP; /* MODE PAGE CODES (MPC) */ /* 00H Reserved (Vendor Unique) */ /* 01H Reserved */ #define PAGE_CODE_CONNECTION 0x02 /* 02H Disconnect/Reconnect Parameters */ #define PAGE_CODE_SCANNING_MEASUREMENTS 0x03 /* 03H Scanning Measurement Parameters */ /* 04H-08H Reserved */ /* 09H-0AH Reserved (Not supported) */ /* 0BH-1FH Reserved */ #define PAGE_CODE_WHITE_BALANCE 0x20 /* 20H White Balance */ /* 21H Reserved (Vendor Unique) */ #define PAGE_CODE_LAMP_TIMER_SET 0x22 /* 22H Lamp Timer Set */ #define PAGE_CODE_SCANNING_SPEED 0x23 /* 23H Reserved (Scanning speed select) */ /* 24H Reserved (Vendor Unique) */ /* 25H Reserved (Vendor Unique) */ #define PAGE_CODE_ADF_CONTROL 0x26 /* 26H ADF Control */ #define PAGE_CODE_ENDORSER_CONTROL 0x27 /* 27H Endorser Control */ /* 28H Reserved (Marker Area Data Processing) */ /* 29H-2AH Reserved (Vendor Unique) */ #define PAGE_CODE_SCAN_WAIT_MODE 0x2B /* 2BH Scan Wait Mode (Medium Wait Mode) */ /* 2CH-3DH Reserved (Vendor Unique) */ #define PAGE_CODE_SERVICE_MODE_SELECT 0x3E /* 3EH Service Mode Select */ /* 3FH Reserved (Not Supported) */ typedef struct mode_page_connect { MPHdr hdr; /* Mode Page Header: 4 bytes */ SANE_Byte code; /* 7-6:Reserved; 5-0: 02H */ SANE_Byte len; /* Parameter Length 0EH */ SANE_Byte buffer_full_ratio; /* Ignored */ SANE_Byte buffer_empty_ratio; /* Ignored */ SANE_Byte bus_inactive_limit[2]; /* Ignored */ SANE_Byte disconnect_time_limit[2]; /* indicates minimum time to disconnect SCSI bus until reconnection. * It is expressed in 100msec increments; i.e. "1" for 100msec, "2" for 200msec * The maximum time is 2sec */ SANE_Byte connect_time_limit[2]; /* Ignored */ SANE_Byte maximum_burst_size[2]; /* expressed in 512 increments, i.e. "1" for 512 bytes, "2" for 1024 bytes * "0" indicates unlimited amount of data */ SANE_Byte dtdc; /* 7-2:Reserved; 1-0:DTDC indicates limitations of disconnection (bit1,bit0): * 00 (DEFAULT) Controlled by the other field in this page * 01 Once the command data transfer starts, the target never disconnects until * the whole data transfer completes * 10 Reserved * 11 Once the command data transfer starts, the target never disconnects until * the completion of the command */ SANE_Byte reserved[3]; } MP_CXN; /* 1 inch = 6 picas = 72 points = 25.4 mm */ #define DEFAULT_MUD 1200 /* WHY ? */ /* BASIC MEASUREMENT UNIT * 00H INCH * 01H MILLIMETER * 02H POINT * 03H-FFH Reserved */ enum BMU { INCHES = 0, MILLIMETERS, POINTS }; /* Basic Measurement Unit */ typedef struct mode_page_scanning_measurement { MPHdr hdr; /* Mode Page Header: 4 bytes */ SANE_Byte code; /* 7-6:Reserved; 5-0:Page Code (03H) */ SANE_Byte len; /* Parameter Length (06H) */ SANE_Byte bmu; /* Basic Measurement Unit */ SANE_Byte reserved0; SANE_Byte mud[2]; /* Measurement Unit Divisor * produces an error if 0 * mud is fixed to 1 for millimeter or point * point is default when scanner powers on */ SANE_Byte reserved1[2]; } MP_SMU; /* Scanning Measurement Units */ typedef struct mode_page_white_balance { MPHdr hdr; /* Mode Page Header: 4 bytes */ SANE_Byte code; /* 7-6:Reserved; 5-0:Page Code (03H) */ SANE_Byte len; /* Parameter Length (06H) */ SANE_Byte white_balance; /* "0" selects relative white mode (DEFAULT when power on) * "1" selects absolute white mode */ SANE_Byte reserved[5]; } MP_WhiteBal; /* White Balance */ typedef struct mode_page_lamp_timer_set { MPHdr hdr; /* Mode Page Header: 4 bytes */ SANE_Byte code; /* 7-6:Reserved; 5-0:Page Code (22H) */ SANE_Byte len; /* Parameter Length (06H) */ SANE_Byte time_on; /* indicates the time of lamp turned on */ SANE_Byte ignored[5]; } MP_LampTimer; /* Lamp Timer Set (Not supported ) */ typedef struct mode_page_adf_control { MPHdr hdr; /* Mode Page Header: 4 bytes */ SANE_Byte code; /* 7-6:Reserved; 5-0:Page Code (26H) */ SANE_Byte len; /* Parameter Length (06H) */ SANE_Byte adf_control; /* 7-2:Reserved; 1-0:ADF selection: * 00H Book Mode (DEFAULT when power on) * 01H Simplex ADF * 02H Duplex ADF * 03H-FFH Reserved */ SANE_Byte adf_mode_control; /* 7-3:Reserved; 2:Prefeed Mode Validity 1-0:Ignored * Prefeed Mode "0" means invalid, "1" means valid */ SANE_Byte medium_wait_timer; /* indicates time for scanner to wait for media. Scanner * will send CHECK on timeout. NOT SUPPORTED */ SANE_Byte ignored[3]; } MP_ADF; /* ADF Control */ typedef struct mode_page_endorser_control { MPHdr hdr; /* Mode Page Header: 4 bytes */ SANE_Byte code; /* 7-6:Reserved; 5-0:Page Code (27H) */ SANE_Byte len; /* Parameter Length (06H) */ SANE_Byte endorser_control; /* 7-3:Reserved; 2-0:Endorser Control: * 0H Disable Endorser (DEFAULT) * 1H Enable Endorser * 3H-7H Reserved */ SANE_Byte ignored[5]; } MP_EndCtrl; /* Endorser Control */ typedef struct mode_page_scan_wait { MPHdr hdr; /* Mode Page Header: 4 bytes */ SANE_Byte code; /* 7-6:Reserved; 5-0:Page Code (2BH) */ SANE_Byte len; /* Parameter Length (06H) */ SANE_Byte swm; /* 7-1:Reserved; 0:Scan Wait Mode * 0H Disable Medium wait mode * 1H Enable Medium wait mode * In Medium wait mode, when SCAN, READ, or LOAD (in ADF mode) is issued, * the scanner waits until start button is pressed on operation panel * When abort button is pressed, the command is cancelled * In ADF mode, when there are no originals on ADF, CHECK condition is * not given unless start button is pressed. */ SANE_Byte ignored[5]; } MP_SWM; /* Scan Wait */ typedef struct mode_page_service { /* Selectable when Send Diagnostic command is performed */ MPHdr hdr; /* Mode Page Header: 4 bytes */ SANE_Byte code; /* 7-6:Reserved; 5-0:Page Code (3EH) */ SANE_Byte len; /* Parameter Length (06H) */ SANE_Byte service; /* 7-1:Reserved; 0:Service Mode * "0" selects Self Diagnostics mode (DEFAULT when power on ) * "1" selects Optical Adjustment mode */ SANE_Byte ignored[5]; } MP_SRV; /* Service */ typedef struct scsi_mode_sense_cmd { SANE_Byte opcode; /* 1AH */ SANE_Byte dbd; /* 7-5:LUN; 4:Reserved; 3:DBD (Disable Block Description) set to "0"; 2-0:Reserved */ SANE_Byte pc; /* 7-6:PC; 5-0:Page Code * PC field indicates the type of data to be returned (bit7,bit6): * 00 Current Value (THIS IS THE ONLY VALUE WHICH WORKS!) * 01 Changeable Value * 10 Default Value * 11 Saved Value * * Page Code indicates requested page. (See PAGE_CODE defines) */ SANE_Byte reserved; SANE_Byte len; /* Allocation length */ SANE_Byte control; /* 7-6:Vendor Unique; 5-2:Reserved; 1:Flag; 0:Link */ } SENSE; /* MODE SENSE DATA FORMAT -- * The format of Sense Data to be returned is Mode Parameter Header + Page * see struct scsi_mode_parameter_header * struct mode_pages */ /* 1-3-8 SCAN command */ typedef struct scsi_start_scan_cmd { SANE_Byte opcode; /* 1BH */ SANE_Byte byte1; /* 7-5:LUN; 4-0:Reserved */ SANE_Byte page_code; SANE_Byte reserved; SANE_Byte len; /* Transfer Length * Length of Window List in bytes * Since scanner supports up to 2 windows, len is 1 or 2 */ SANE_Byte control; /* 7-6:Vendor Unique; 5-2:Reserved; 1:Flag; 0:Link */ } START_SCAN; /* 1-3-9 RECEIVE DIAGNOSTIC * 1-3-10 SEND DIAGNOSTIC */ /* BinaryFilter Byte * bit7: Noise Removal '1':removal * bit6: Smoothing '1':smoothing * bits5-2: ignored * bits1-0: Noise Removal Matrix * 00:3x3 01:4x4 * 10:5x5 11:Reserved */ struct val_id { SANE_Byte val; SANE_Byte id; }; static SANE_String_Const noisematrix_list[] = { "None", "3x3", "4x4", "5x5", NULL }; struct val_id noisematrix[] = { {0x03, 0}, /* dummy value for "None" */ {0x00, 1}, {0x01, 2}, {0x02, 3} }; static SANE_String_Const grayfilter_list[] = { "none", "averaging", "MTF correction", NULL }; struct val_id grayfilter[] = { {0x00, 0}, {0x01, 1}, {0x03, 2} }; static SANE_String_Const paddingtype_list[] = { "Pad with 0's to byte boundary", "Pad with 1's to byte boundary", "Truncate to byte boundary", NULL }; enum paddingtypes { PAD_WITH_ZEROS = 0x01, PAD_WITH_ONES, TRUNCATE }; struct val_id paddingtype[] = { {PAD_WITH_ZEROS, 0}, {PAD_WITH_ONES, 1}, {TRUNCATE, 2} }; #define NPADDINGTYPES 3 #define PADDINGTYPE_DEFAULT 2 static SANE_String_Const auto_separation_list[] = { "Off", "On", "User", NULL }; struct val_id auto_separation[] = { {0x00, 0}, {0x01, 1}, {0x80, 2} }; static SANE_String_Const auto_binarization_list[] = { "Off", "On", "Enhancement of light characters", "Removal of background color", "User", NULL }; struct val_id auto_binarization[] = { {0x00, 0}, {0x01, 1}, {0x02, 2}, {0x03, 3}, {0x80, 4} }; enum imagecomposition { LINEART = 0x00, HALFTONE, GRAYSCALE }; enum halftonecode { DITHER = 0x02, ERROR_DIFFUSION }; static SANE_String_Const halftone_code[] = { "Dither", "Error Diffusion", NULL }; static SANE_String_Const halftone_pattern_list[] = { "8x4, 45 degree", "6x6, 90 degree", "4x4, spiral", "8x8, 90 degree", "70 lines", "95 lines", "180 lines", "16x8, 45 degree", "16x16, 90 degree", "8x8, Bayer", "User #1", "User #2", NULL }; struct val_id halftone[] = { {0x01, 1}, {0x02, 2}, {0x03, 3}, {0x04, 4}, {0x05, 5}, {0x06, 6}, {0x07, 7}, {0x08, 9}, {0x09, 9}, {0x0A, 10}, {0x80, 11}, {0x81, 12} }; #if 0 static struct { SANE_Byte code; char *type; } compression_types[] = { { 0x00, "No compression"}, { 0x01, "CCITT G3, 1-dimensional (MH)"}, { 0x02, "CCITT G3, 2-dimensional (MR)"}, { 0x03, "CCITT G4, 2-dimensional (MMR)"}, /* 04H-0FH Reserved * 10H Reserved (not supported) * 11H-7FH Reserved */ { 0x80, "CCITT G3, 1-dimensional (MH) Padding with 0's to byte boundary"} /* 80H-FFH Reserved (Vendor Unique) */ }; static struct { SANE_Byte code; char *argument; } compression_argument[] = { /* 00H Reserved */ /* 01H Reserved */ { 0x02, "K factor-0~255"} /* 03H Reserved */ /* 04H-0FH Reserved */ /* 10H Reserved */ /* 11H-7FH Reserved */ /* 80H Reserved */ /* 80H-FFH Reserved */ }; #endif #define GAMMA_NORMAL 0x00 #define GAMMA_SOFT 0x01 #define GAMMA_SHARP 0x02 #define GAMMA_LINEAR 0x03 #define GAMMA_USER 0x08 /* 04H-07H Reserved */ /* 09H-0FH Reserved */ static SANE_String gamma_list[6] = { "Normal", "Soft", "Sharp", "Linear", "User", NULL }; /* 1-3-11 SET WINDOW command */ struct window_section { /* 32 bytes */ SANE_Byte sef; /*byte1 7-2:ignored 1:SEF '0'-invalid section; '1'-valid section */ SANE_Byte ignored0; SANE_Byte ulx[4]; SANE_Byte uly[4]; SANE_Byte width[4]; SANE_Byte length[4]; SANE_Byte binary_filtering; SANE_Byte ignored1; SANE_Byte threshold; SANE_Byte ignored2; SANE_Byte image_composition; SANE_Byte halftone_id; SANE_Byte halftone_code; SANE_Byte ignored3[7]; }; /* 1-3-11 SET WINDOW COMMAND * Byte0: 24H * Byte1: 7-5: LUN; 4-0: Reserved * Byte2-5: Reserved * Byte6-8: Transfer Length * Byte9: 7-6: Vendor Unique; 5-2: Reserved; 1: Flag; 0: Link * * Transfer length indicates the byte length of Window Parameters (Set Window Data Header + * Window Descriptor Bytes transferred from the initiator in the DATA OUT PHASE * The scanner supports 2 windows, so Transfer Length is 648 bytes: * Set Window Header 8 bytes + Window Descriptor Bytes 640 (320*2) bytes). * If data length is longer than 648 bytes only the first 648 bytes are valid, The remainng data is ignored. * If data length is shorter than 648 only the specified byte length is valid data. * * * WINDOW DATA HEADER * Byte0-5: Reserved * Byte6-7: Window Descriptor Length (WDL) * WDL indicates the number of bytes of one Window Descriptor Bytes which follows. * In this scanner, this value is 640 since it supports 2 windows. * * WINDOW DESCRIPTOR BYTES */ #define HS2P_WINDOW_DATA_SIZE 640 struct hs2p_window_data { /* HS2P_WINDOW_DATA_FORMAT */ SANE_Byte window_id; /* 0: Window Identifier */ SANE_Byte auto_bit; /* 1: 1-1:Reserved; 0:Auto */ SANE_Byte xres[2]; /* 2-3: X-Axis Resolution 100-800dpi in 1dpi steps */ SANE_Byte yres[2]; /* 4-5: Y-Axis Resolution 100-800dpi in 1dpi steps */ SANE_Byte ulx[4]; /* 6-9: X-Axis Upper Left */ SANE_Byte uly[4]; /* 10-13: Y-Axis Upper Left */ SANE_Byte width[4]; /* 14-17: Window Width */ SANE_Byte length[4]; /* 18-21: Window Length */ SANE_Byte brightness; /* 22: Brightness [0-255] dark-light 0 means default value of 128 */ SANE_Byte threshold; /* 23: Threshold [0-255] 0 means default value of 128 */ SANE_Byte contrast; /* 24: Contrast [0-255] low-high 0 means default value of 128 */ SANE_Byte image_composition; /* 25: Image Composition * 00H Lineart * 01H Dithered Halftone * 02H Gray scale */ SANE_Byte bpp; /* 26: Bits Per Pixel */ SANE_Byte halftone_code; /* 27: Halftone Code * 00H-01H Reserved * 02H Dither (partial Dot) * 03H Error Diffusion * 04H-07H Reserved */ SANE_Byte halftone_id; /* 28: Halftone ID * 00H Reserved * 01H 8x4, 45 degree * 02H 6x6, 90 degree * 03H 4x4, Spiral * 04H 8x8, 90 degree * 05H 70 lines * 06H 95 lines * 07H 180 lines * 08H 16x8, 45 degree * 09H 16x16, 90 degree * 0AH 8x8, Bayer * 0Bh-7FH Reserved * 80H Download #1 * 81H Download #2 * 82H-FFH Reserved */ SANE_Byte byte29; /* 29: 7: RIF (Reverse Image Format) bit inversion * Image Composition field must be lineart or dithered halftone * RIF=0: White=0 Black=1 * RIF=1: White=1 Black=0 * 6-3: Reserved; * 2-0: Padding Type: * 00H Reserved * 01H Pad with 0's to byte boundary * 02H Pad with 1's to byte boundary * 03H Truncate to byte boundary * 04H-FFH Reserved */ SANE_Byte bit_ordering[2]; /* 30-31: Bit Ordering: Default 0xF8 * 0: 0=>output from bit0 of each byte; 1=>output from bit7 * 1: 0=>output from LSB; 1=>output from MSB * 2: 0=>unpacked 4 bits gray; 1=>Packed 4 bits gray * 3: 1=>Bits arrangement from LSB in grayscale; 0=>from MSB * 4-6: reserved * 7: 1=>Mirroring; 0=>Normal output * 8-15: reserved */ SANE_Byte compression_type; /* 32: Compression Type: Unsupported in IS450 */ SANE_Byte compression_arg; /* 33: Compression Argument: Unsupported in IS450 */ SANE_Byte reserved2[6]; /* 34-39: Reserved */ SANE_Byte ignored1; /* 40: Ignored */ SANE_Byte ignored2; /* 41: Ignored */ SANE_Byte byte42; /* 42: 7: MRIF: Grayscale Reverse Image Format * MRIF=0: White=0 Black=1 * MRIF=1: White=1 Black=0 * 6-4: Filtering: for Grayscale * 000 No filter * 001 Averaging * 010 Reserved * 011 MTF Correction * 100 Reserved * 110 Reserved * 111 Reserved * 3-0: Gamma ID * 00H Normal * 01H Soft * 02H Sharp * 03H Linear * 04H-07H Reserved * 08H Download table * 09H-0FH Reserved */ SANE_Byte ignored3; /* 43: Ignored */ SANE_Byte ignored4; /* 44: Ignored */ SANE_Byte binary_filtering; /* 45: Binary Filtering * 0-1: Noise Removal Matrix: * 00: 3x3 * 01: 4x4 * 10: 5x5 * 11: Reserved * 5-2: Ignored * 6: Smoothing Flag * 7: Noise Removal Flag * * Smoothing and Noise removal can be set when option IPU is installed * Setting is ignored for reverse side because optional IPU is not valid * for reverse side scanning */ /* * The following is only available when IPU is installed: * SECTION, Automatic Separation, Automatic Binarization * 46-319 is ignored for Window 2 */ SANE_Byte ignored5; /* 46: Ignored */ SANE_Byte ignored6; /* 47: Ignored */ SANE_Byte automatic_separation; /* 48: Automatic Separation * 00H OFF * 01H Default * 02H-7FH Reserved * 80H Download table * 91H-FFH Reserved */ SANE_Byte ignored7; /* 49: Ignored */ SANE_Byte automatic_binarization; /* 50: Automatic Binarization * 00H OFF * 01H Default * 02H Enhancement of light characters * 03H Removal of background color * 04H-7FH Reserved * 80H Download table * 81H-FFH Reserved */ SANE_Byte ignored8[13]; /* 51-63: Ignored */ struct window_section sec[8]; /* Each window can have multiple sections, each of 32 bytes long * 53-319: = 256 bytes = 8 sections of 32 bytes * IS450 supports up to 4 sections, * IS420 supports up to 6 sections */ }; struct set_window_cmd { SANE_Byte opcode; /* 24H */ SANE_Byte byte2; /* 7-5:LUN 4-0:Reserve */ SANE_Byte reserved[4]; /* Reserved */ SANE_Byte len[3]; /* Transfer Length */ SANE_Byte control; /* 76543210 * XX Vendor Unique * XXXX Reserved * X Flag * X Link */ }; struct set_window_data_hdr { SANE_Byte reserved[6]; SANE_Byte len[2]; }; typedef struct set_window_data { struct set_window_data_hdr hdr; struct hs2p_window_data data[2]; } SWD; /* 1-3-12 GET WINDOW command */ struct get_window_cmd { SANE_Byte opcode; SANE_Byte byte1; /* 7-5: LUN; * 4-1:Reserved; * 0:Single bit is 0 */ SANE_Byte reserved[3]; SANE_Byte win_id; /* Window ID is either 0 or 1 */ SANE_Byte len[3]; /* Transfer Length */ SANE_Byte control; /* 7-6:Vendor Unique; 5-2:Reserved; 1:Flag; 0:Link */ }; /* The data format to be returned is Get Window Data header + Window Descriptor Bytes * The format of Window Descriptor Bytes is the same as that for SET WINDOW */ struct get_window_data_hdr { SANE_Byte data_len[2]; /* Window Data Length indicates byte len of data which follows less its own 2 bytes */ SANE_Byte reserved[4]; SANE_Byte desc_len[2]; /* Window Descriptor Length indicates byte length of one Window Descriptor which is 640 */ }; typedef struct get_window_data { struct get_window_data_hdr hdr; struct hs2p_window_data data[2]; } GWD; /* READ/SEND DATA TYPE CODES */ /* DATA TYPE CODES (DTC): */ #define DATA_TYPE_IMAGE 0x00 /* 01H Reserved (Vendor Unique) */ #define DATA_TYPE_HALFTONE 0x02 #define DATA_TYPE_GAMMA 0x03 /*04H-7FH Reserved */ #define DATA_TYPE_ENDORSER 0x80 #define DATA_TYPE_SIZE 0x81 /* 82H Reserved */ /* 83H Reserved (Vendor Unique) */ #define DATA_TYPE_PAGE_LEN 0x84 #define DATA_TYPE_MAINTENANCE 0x85 #define DATA_TYPE_ADF_STATUS 0x86 /* 87H Reserved (Skew Data) */ /* 88H-91H Reserved (Vendor Unique) */ /* 92H Reserved (Scanner Extension I/O Access) */ /* 93H Reserved (Vendor Unique) */ /* 94H-FFH Reserved (Vendor Unique) */ #define DATA_TYPE_EOL -1 /* va_end */ /* DATA TYPE QUALIFIER CODES when DTC=93H */ #define DTQ 0x00 /* ignored */ #define DTQ_AUTO_PHOTOLETTER 0x00 /* default */ #define DTQ_DYNAMIC_THRESHOLDING 0x01 /* default */ #define DTQ_LIGHT_CHARS_ENHANCEMENT 0x02 #define DTQ_BACKGROUND_REMOVAL 0x03 /* 04H-7FH Reserved */ #define DTQ_AUTO_PHOTOLETTER_DOWNLOAD_TABLE 0x80 #define DTQ_DYNAMIC_THRESHOLD_DOWNLOAD_TABLE 0x81 /* 82H-FFH Reserved */ /* 1-3-13 READ command */ /* 1-3-14 SEND command */ struct scsi_rs_scanner_cmd { SANE_Byte opcode; /* READ=28H SEND=2AH */ SANE_Byte byte1; /* 7-5:LUN; 4-0:Reserved */ SANE_Byte dtc; /* Data Type Code: See DTC DEFINES above */ SANE_Byte reserved; SANE_Byte dtq[2]; /* Data Type Qualifier valid only for DTC 02H,03H,93H */ SANE_Byte len[3]; /* Transfer Length */ SANE_Byte control; /* 7-6:Vendor Unique; 5-2:Reserved; 1:Flag; 0:Link */ }; /* * Data Format for Image Data * Non-compressed: {first_line, second_line, ... nth_line} * MH/MR Compression: {EOL, 1st_line_compressed, EOL, 2nd_line_compressed,..., EOL, last_line_compressed, EOL,EOL,EOL,EOL,EOL,EOL * where EOL = 000000000001 * MMR Compression: = {1st_line_compressed, 2nd_line_compressed,...,last_line_compressed, EOL,EOL} * * Normal Binary Output: MSB-LSB 1stbytes,2nd,3rd...Last * Mirror Binary Output: MSB-LSB Last,...2nd,1st * * Normal Gray Output MSB-LSB: 1st,2nd,3rd...Last * 4 bit/pixel gray: [32103210] * 8 bit/pixel gray: [76543210] * Mirror Gray Output MSB-LSB Last,...2nd,1st * * * HALFTONE MASK DATA: 1byte(row,col) ={2,3,4,6,8,16} * (r0,c0), (r0,c1), (r0,c2)...(r1,c0),(r1,c2)...(rn,cn) * * GAMMA FUNCTION TABLE Output (D) vs. Input (I)(0,0)=(Black,Black) (255,255)=(White,White) * The number of gray scale M = 8 * 2^8 = 256 total table data * D0 = D(I=0), D1=D(I=1)...D255=D(I=255) * DATA= [1st byte ID],[2nd byte M],[D0],[D1],...[D255] * * ENDORSER DATA: 1st_char, 2nd_char,...last_char * * SIZE DATA: 1byte: 4bits-Start Position; 4bits-Width Info * * PAGE LENGTH: 5bytes: 1st byte is MSB, Last byte is LSB */ typedef struct maintenance_data { SANE_Byte nregx_adf; /* number of registers of main-scanning in ADF mode */ SANE_Byte nregy_adf; /* number of registers of sub-scanning in ADF mode */ SANE_Byte nregx_book; /* number of registers of main-scanning in Book mode */ SANE_Byte nregy_book; /* number of registers of sub-scanning in Book mode */ SANE_Byte nscans_adf[4]; /* Number of scanned pages in ADF mode */ SANE_Byte nscans_book[4]; /* Number of scanned pages in Book mode */ SANE_Byte lamp_time[4]; /* Lamp Time */ SANE_Byte eo_odd; /* Adjustment data of E/O balance in black level (ODD) */ SANE_Byte eo_even; /* Adjustment data of E/O balance in black level (EVEN) */ SANE_Byte black_level_odd; /* The adjustment data in black level (ODD) */ SANE_Byte black_level_even; /* The adjustment data in black level (EVEN) */ SANE_Byte white_level_odd[2]; /* The adjustment data in white level (ODD) */ SANE_Byte white_level_even[2]; /* The adjustment data in white level (EVEN) */ SANE_Byte first_adj_white_odd[2]; /* First adjustment data in white level (ODD) */ SANE_Byte first_adj_white_even[2]; /* First adjustment data in white level (EVEN) */ SANE_Byte density_adj; /* Density adjustment */ SANE_Byte nregx_reverse; /* The number of registers of main-scanning of the reverse-side ADF */ SANE_Byte nregy_reverse; /* The number of registers of sub-scanning of the reverse-side ADF */ SANE_Byte nscans_reverse_adf[4]; /* Number of scanned pages of the reverse side ADF */ SANE_Byte reverse_time[4]; /* The period of lamp turn on of the reverse side */ SANE_Byte nchars[4]; /* The number of endorser characters */ SANE_Byte reserved0; SANE_Byte reserved1; SANE_Byte reserved2; SANE_Byte zero[2]; /* All set as 0 */ } MAINTENANCE_DATA; /* ADF status 1byte: * 7-3:Reserved; * 2:Reserved; * 1: '0'-ADF cover closed; '1'-ADF cover open * 0: '0'-Document on ADF; '1'-No document on ADF * */ struct IPU { SANE_Byte byte0; /* 7-4:Reserved; 3:White mode; 2:Reserved; 1-0: Gamma Table Select */ SANE_Byte byte1; /* 7-2:Reserved; 1-0: MTF Filter Select */ }; struct IPU_Auto_PhotoLetter { /* Halftone Separations for each level * 256 steps of relative value with 0 the sharpest and 255 the softest * The relation of strength is Strength2 > Strength3 > Strength4 ... */ struct { SANE_Byte level[6]; } halftone_separation[2]; /* 7-2:Reversed 1-0:Halftone * 00 Default * 01 Peak Detection Soft * 10 Peak Detection Sharp * 11 Don't Use */ SANE_Byte byte12; SANE_Byte black_correction; /* Black correction strength: 0-255 sharpest-softest */ SANE_Byte edge_sep[4]; /* Edge Separation strengths: 0-255 sharpest-softest 1-4 */ SANE_Byte white_background_sep_strength; /* 0-255 sharpest-softest */ SANE_Byte byte19; /* 7-1:Reversed; 0:White mode '0'-Default; '1'-Sharp */ SANE_Byte byte20; /* 7-1:Reversed; 0:Halftone mode '0'-widen dots; '1'-Default */ SANE_Byte halftone_sep_levela; SANE_Byte halftone_sep_levelb; SANE_Byte byte23; /* 7-4:Reversed; 3-0:Adjustment of separation level: usually fixed to 0 */ /* 7-4:Reversed; 3-0:Judge Conditions Select * 0XXX Black Correction OFF 1XXX Black Correction ON * X0XX Halftone Separation OFF X1XX Halftone Separation ON * XX0X White Separation OFF XX1X White Separation ON * XXX0 Edge Separation OFF XXX1 Edge Separation ON */ SANE_Byte byte24; /* 7-4:Filter A; 3-0:Filter B * FilterA: 16 types are valid from 0000 to 1111 * FilterB: 0000 to 1110 are valid; 1111 is not valid */ SANE_Byte MTF_correction; /* 7-4:Filter A; 3-0:Filter B * 0000(soft) to 0111(sharp) are valid; 1000 to 1111 are invalid */ SANE_Byte MTF_strength; /* 7-4:Filter A; 3-0:Filter B * slightly adjusts the strength of the filters */ SANE_Byte MTF_adjustment; /* 7-4:Reserved; 3-0: smoothing filter select * 14 kinds are valid from 0000 to 1101; 1110 to 1111 are invalid */ SANE_Byte smoothing; /* 7-2:Reversed; 1-0: Filter Select * 10 MTF Correction Select * 11 Smoothing Select * from 00 to 01 are not valid and basically it is set as 10 */ SANE_Byte byte29; /* 7-4:Reserved; 3-0: MTF Correction Filter C * 16 kinds are valid from 0000 to 1111 */ SANE_Byte MTF_correction_c; /* 7-3:Reserved; 2-0: MTF Correction Filter strength C * 000(soft) to 111(sharp) are valid */ SANE_Byte MTF_strength_c; }; /* struct IPU_Dynamic { to be implemented }; sensor data */ /* for object_position command */ #define OBJECT_POSITION_UNLOAD 0 #define OBJECT_POSITION_LOAD 1 /* 1-3-15 OBJECT POSITION */ typedef struct scsi_object_position_cmd { SANE_Byte opcode; /* 31H */ SANE_Byte position_func; /* 7-5:LUN; 4-3:Reserved; 2-0:Position Function (bit2,bit1,bit0): * 000 Unload Object (NO CHECK ERROR even though no document on ADF) * 001 Load Object (NO CHECK ERROR even though document already fed to start position) * 010 Absolute Positioning in Y-axis. Not Supported in this scanner * 3H-7H Reserved */ SANE_Byte count[3]; /* Reserved */ SANE_Byte reserved[4]; SANE_Byte control; /* 7-6:Vendor Unique; 5-2:Reserved; 1:Flag; 0:Link */ } POSITION; /* 1-3-16 GET DATA BUFFER STATUS */ typedef struct scsi_get_data_buffer_status_cmd { SANE_Byte opcode; /* 34H */ SANE_Byte wait; /* 7-5:LUN; 4-1:Reserved; 0: Wait bit is "0" */ SANE_Byte reserved[5]; SANE_Byte len[2]; /* Allocation Length */ SANE_Byte control; /* 7-6:Vendor Unique; 5-2:Reserved; 1:Flag; 0:Link */ } GET_DBS_CMD; typedef struct scsi_status_hdr { SANE_Byte len[3]; /* Data Buffer Status Length */ SANE_Byte block; /* 7-1:Reserved; 0:Block bit is 0 */ } STATUS_HDR; typedef struct scsi_status_data { SANE_Byte wid; /* window identifier is 0 or 1 */ SANE_Byte reserved; SANE_Byte free[3]; /* Available Space Data `Buffer */ SANE_Byte filled[3]; /* Scan Data Available (Filled Data Bufferj) */ } STATUS_DATA; /* BUFFER STATUS DATA FORMAT */ typedef struct scsi_buffer_status { STATUS_HDR hdr; STATUS_DATA data; } STATUS_BUFFER; backends-1.3.0/backend/hs2p.c000066400000000000000000003413331456256263500157210ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2007 Jeremy Johnson This file is part of a SANE backend for Ricoh IS450 and IS420 family of HS2P Scanners using the SCSI controller. 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, see . 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. */ /* SANE-FLOW-DIAGRAM - sane_init() : initialize backend, attach scanners . - sane_get_devices() : query list of scanner-devices . - sane_open() : open a particular scanner-device . . - attach : to the device . . . init_options : initialize SANE_OPTIONS array . . - 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 */ #define BUILD 1 /* Begin includes */ #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_scsi.h" #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_thread.h" #define BACKEND_NAME hs2p #include "../include/sane/sanei_backend.h" #ifndef PATH_MAX # define PATH_MAX 1024 #endif #include "hs2p-scsi.c" /* Begin macros */ #define MIN(x,y) ((x)<(y) ? (x) : (y)) #define MAX(x,y) ((x)>(y) ? (x) : (y)) /* Begin static constants */ static int num_devices = 0; static HS2P_Device *first_dev = NULL; static HS2P_Scanner *first_handle = NULL; static SANE_Char inquiry_data[255] = "HS2P scanner"; /* static SANE_Int disable_optional_frames = 0; static SANE_Int fake_inquiry = 0; */ static HS2P_HWEntry HS2P_Device_List[] = { {"RICOH", "IS450"}, {"RICOH", "IS430"}, /*untested */ {"RICOH", "IS420"}, /*untested */ {"RICOH", "IS01"}, /*untested */ {"RICOH", "IS02"}, /*untested */ {NULL, NULL} /*sentinel */ }; #if 0 static int allblank (const char *s) { while (s && *s) if (!isspace (*s++)) return 0; return 1; } #endif static size_t max_string_size (SANE_String_Const strings[]) { size_t size, max_size = 0; int i; DBG (DBG_proc, ">> max_string_size\n"); for (i = 0; strings[i]; ++i) { size = strlen (strings[i]) + 1; if (size > max_size) max_size = size; } DBG (DBG_proc, "<< max_string_size\n"); return max_size; } static void trim_spaces (char *s, size_t n) { for (s += (n - 1); n > 0; n--, s--) { if (*s && !isspace (*s)) break; *s = '\0'; } } static SANE_Bool is_device_supported (char *device) { HS2P_HWEntry *hw; for (hw = &HS2P_Device_List[0]; hw->mfg != NULL; hw++) if (strncmp (device, hw->model, strlen (hw->model)) == 0) break; /* found a match */ return (hw == NULL) ? SANE_FALSE : SANE_TRUE; } static SANE_Int get_list_index (const char *list[], char *s) /* sequential search */ { SANE_Int i; for (i = 0; list[i]; i++) if (strcmp (s, list[i]) == 0) return i; /* FOUND */ /* unknown paper_list strings are treated as 'custom' */ /* unknown compression_list strings are treated as 'none' */ /* unknown scan_source_list strings are treated as 'ADF' */ return 0; } static SANE_Int get_val_id_strndx (struct val_id *vi, int len, SANE_Int val) { int i; for (i = 0; i < len; i++) if (vi[i].val == val) return vi[i].id; /* FOUND */ return vi[0].id; /* NOT FOUND so let's default to first */ } static SANE_Status init_options (HS2P_Scanner * s) { SANE_Int i; DBG (DBG_proc, ">> init_options\n"); memset (s->opt, 0, sizeof (s->opt)); memset (s->val, 0, sizeof (s->val)); for (i = 0; i < NUM_OPTIONS; ++i) { s->opt[i].size = sizeof (SANE_Word); s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } 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; s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* * "Scan Mode" GROUP: */ s->opt[OPT_MODE_GROUP].name = ""; s->opt[OPT_MODE_GROUP].title = SANE_TITLE_SCAN_MODE_GROUP; s->opt[OPT_MODE_GROUP].desc = ""; s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MODE_GROUP].cap = 0; s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* Preview: */ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; s->opt[OPT_PREVIEW].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_PREVIEW].w = SANE_FALSE; /* Inquiry */ s->opt[OPT_INQUIRY].name = SANE_NAME_INQUIRY; s->opt[OPT_INQUIRY].title = SANE_TITLE_INQUIRY; s->opt[OPT_INQUIRY].desc = SANE_DESC_INQUIRY; s->opt[OPT_INQUIRY].type = SANE_TYPE_STRING; s->opt[OPT_INQUIRY].size = sizeof (inquiry_data); s->opt[OPT_INQUIRY].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_INQUIRY].s = strdup (inquiry_data); s->opt[OPT_INQUIRY].cap = SANE_CAP_SOFT_DETECT; /* Display Only */ /* Scan mode */ s->opt[OPT_SCAN_MODE].name = SANE_NAME_SCAN_MODE; s->opt[OPT_SCAN_MODE].title = SANE_TITLE_SCAN_MODE; s->opt[OPT_SCAN_MODE].desc = SANE_DESC_SCAN_MODE; s->opt[OPT_SCAN_MODE].type = SANE_TYPE_STRING; s->opt[OPT_SCAN_MODE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_SCAN_MODE].size = max_string_size ((SANE_String_Const *) scan_mode_list); s->opt[OPT_SCAN_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_SCAN_MODE].constraint.string_list = (SANE_String_Const *) & scan_mode_list[0]; s->val[OPT_SCAN_MODE].s = strdup (scan_mode_list[0]); s->image_composition = LINEART; /* Standard resolutions */ s->opt[OPT_RESOLUTION].name = "std-" SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].title = "Std-" SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].desc = "Std " SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_RESOLUTION].constraint.word_list = s->hw->info.resStdList; s->val[OPT_RESOLUTION].w = s->hw->info.default_res; /* X Resolution */ s->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_X_RESOLUTION; s->opt[OPT_X_RESOLUTION].desc = "X " SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_X_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_X_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_X_RESOLUTION].constraint.range = &(s->hw->info.xres_range); s->val[OPT_X_RESOLUTION].w = s->hw->info.resBasicX; /* Y Resolution */ s->opt[OPT_Y_RESOLUTION].name = SANE_NAME_SCAN_Y_RESOLUTION; s->opt[OPT_Y_RESOLUTION].title = SANE_TITLE_SCAN_Y_RESOLUTION; s->opt[OPT_Y_RESOLUTION].desc = "Y " SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_Y_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_Y_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_Y_RESOLUTION].constraint.range = &(s->hw->info.yres_range); s->val[OPT_Y_RESOLUTION].w = s->hw->info.resBasicY; /* Compression */ s->opt[OPT_COMPRESSION].name = SANE_NAME_COMPRESSION; s->opt[OPT_COMPRESSION].title = SANE_TITLE_COMPRESSION; s->opt[OPT_COMPRESSION].desc = SANE_DESC_COMPRESSION; s->opt[OPT_COMPRESSION].type = SANE_TYPE_STRING; s->opt[OPT_COMPRESSION].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_COMPRESSION].size = max_string_size ((SANE_String_Const *) compression_list); s->opt[OPT_COMPRESSION].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_COMPRESSION].constraint.string_list = (SANE_String_Const *) & compression_list[0]; s->val[OPT_COMPRESSION].s = strdup (compression_list[0]); if (s->hw->info.supports_MH == SANE_FALSE || /* MH G3 1-D */ s->hw->info.supports_MR == SANE_FALSE || /* MR G3 2-D */ s->hw->info.supports_MMR == SANE_FALSE || /* MMR G4 2-D */ s->hw->info.supports_MHB == SANE_FALSE) /* MH byte boundary */ { s->opt[OPT_COMPRESSION].cap |= SANE_CAP_INACTIVE; } /* * "Geometry" GROUP: */ s->opt[OPT_GEOMETRY_GROUP].name = ""; s->opt[OPT_GEOMETRY_GROUP].title = SANE_TITLE_GEOMETRY_GROUP; s->opt[OPT_GEOMETRY_GROUP].desc = ""; s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_GEOMETRY_GROUP].cap = 0; s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* Auto Size Recognition available if IPU installed */ s->opt[OPT_AUTO_SIZE].name = SANE_NAME_AUTO_SIZE; s->opt[OPT_AUTO_SIZE].title = SANE_TITLE_AUTO_SIZE; s->opt[OPT_AUTO_SIZE].desc = SANE_DESC_AUTO_SIZE; s->opt[OPT_AUTO_SIZE].type = SANE_TYPE_BOOL; s->opt[OPT_AUTO_SIZE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_AUTO_SIZE].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_AUTO_SIZE].w = SANE_FALSE; if (!s->hw->info.supports_sizerecognition) s->opt[OPT_AUTO_SIZE].cap |= SANE_CAP_INACTIVE; /* Pad short documents to requested length with white space */ s->opt[OPT_PADDING].name = SANE_NAME_PADDING; s->opt[OPT_PADDING].title = SANE_TITLE_PADDING; s->opt[OPT_PADDING].desc = SANE_DESC_PADDING; s->opt[OPT_PADDING].type = SANE_TYPE_BOOL; s->opt[OPT_PADDING].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_PADDING].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_PADDING].w = SANE_TRUE; /*if (!s->hw->info.hasADF) s->opt[OPT_PADDING].cap |= SANE_CAP_INACTIVE; FIXME: compare to user setting, not the existence of FB? if (!strcmp (scan_source_list, "FB")) s->opt[OPT_PADDING].cap |= SANE_CAP_INACTIVE; */ /* Permanently disable OPT_PADDING */ s->opt[OPT_PADDING].cap |= SANE_CAP_INACTIVE; /* Paper Orientation */ s->opt[OPT_PAGE_ORIENTATION].name = SANE_NAME_ORIENTATION; s->opt[OPT_PAGE_ORIENTATION].title = SANE_TITLE_ORIENTATION; s->opt[OPT_PAGE_ORIENTATION].desc = SANE_DESC_ORIENTATION; s->opt[OPT_PAGE_ORIENTATION].type = SANE_TYPE_STRING; s->opt[OPT_PAGE_ORIENTATION].size = max_string_size (orientation_list); s->opt[OPT_PAGE_ORIENTATION].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_PAGE_ORIENTATION].constraint.string_list = &orientation_list[0]; s->val[OPT_PAGE_ORIENTATION].s = strdup (orientation_list[0]); /* Paper Size */ s->opt[OPT_PAPER_SIZE].name = SANE_NAME_PAPER_SIZE; s->opt[OPT_PAPER_SIZE].title = SANE_TITLE_PAPER_SIZE; s->opt[OPT_PAPER_SIZE].desc = SANE_DESC_PAPER_SIZE; s->opt[OPT_PAPER_SIZE].type = SANE_TYPE_STRING; s->opt[OPT_PAPER_SIZE].size = max_string_size (paper_list); s->opt[OPT_PAPER_SIZE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_PAPER_SIZE].constraint.string_list = &paper_list[0]; s->val[OPT_PAPER_SIZE].s = strdup (paper_list[0]); /* top-left x */ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; s->opt[OPT_TL_X].unit = SANE_UNIT_MM; s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_X].constraint.range = &(s->hw->info.x_range); s->val[OPT_TL_X].w = SANE_FIX (0.0); /* top-left y */ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_Y].constraint.range = &(s->hw->info.y_range); s->val[OPT_TL_Y].w = SANE_FIX (0.0); /* bottom-right x */ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; s->opt[OPT_BR_X].unit = SANE_UNIT_MM; s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_X].constraint.range = &(s->hw->info.x_range); s->val[OPT_BR_X].w = s->hw->info.x_range.max; /* bottom-right y */ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_Y].constraint.range = &(s->hw->info.y_range); s->val[OPT_BR_Y].w = s->hw->info.y_range.max; DBG (DBG_info, "INIT_OPTIONS: ul(x,y) = (%d,%d) br(x,y) = (%d,%d)\n", (unsigned) SANE_UNFIX (s->val[OPT_TL_X].w), (unsigned) SANE_UNFIX (s->val[OPT_TL_Y].w), (unsigned) SANE_UNFIX (s->val[OPT_BR_X].w), (unsigned) SANE_UNFIX (s->val[OPT_BR_Y].w)); /* Autoborder */ /* Rotation */ /* Deskew */ /* * "Feeder" GROUP: */ s->opt[OPT_FEEDER_GROUP].name = ""; s->opt[OPT_FEEDER_GROUP].title = SANE_TITLE_FEEDER_GROUP; s->opt[OPT_FEEDER_GROUP].desc = ""; s->opt[OPT_FEEDER_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_FEEDER_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_FEEDER_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* Scan Source */ s->opt[OPT_SCAN_SOURCE].name = SANE_NAME_SCAN_SOURCE; s->opt[OPT_SCAN_SOURCE].title = SANE_TITLE_SCAN_SOURCE; s->opt[OPT_SCAN_SOURCE].desc = SANE_DESC_SCAN_SOURCE; s->opt[OPT_SCAN_SOURCE].type = SANE_TYPE_STRING; s->opt[OPT_SCAN_SOURCE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_SCAN_SOURCE].size = max_string_size (scan_source_list); s->opt[OPT_SCAN_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_SCAN_SOURCE].constraint.string_list = (SANE_String_Const *) & scan_source_list[0]; s->val[OPT_SCAN_SOURCE].s = strdup (scan_source_list[0]); if (!s->hw->info.hasADF) s->opt[OPT_SCAN_SOURCE].cap |= SANE_CAP_INACTIVE; /* Duplex: */ s->opt[OPT_DUPLEX].name = SANE_NAME_DUPLEX; s->opt[OPT_DUPLEX].title = SANE_TITLE_DUPLEX; s->opt[OPT_DUPLEX].desc = SANE_DESC_DUPLEX; s->opt[OPT_DUPLEX].type = SANE_TYPE_BOOL; s->opt[OPT_DUPLEX].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_DUPLEX].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_DUPLEX].w = s->hw->info.default_duplex; if (!s->hw->info.hasDuplex) s->opt[OPT_DUPLEX].cap |= SANE_CAP_INACTIVE; /* Prefeed: */ s->opt[OPT_PREFEED].name = SANE_NAME_PREFEED; s->opt[OPT_PREFEED].title = SANE_TITLE_PREFEED; s->opt[OPT_PREFEED].desc = SANE_DESC_PREFEED; s->opt[OPT_PREFEED].type = SANE_TYPE_BOOL; s->opt[OPT_PREFEED].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_PREFEED].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_PREFEED].w = SANE_FALSE; s->opt[OPT_PREFEED].cap |= SANE_CAP_INACTIVE; /* Endorser: */ s->opt[OPT_ENDORSER].name = SANE_NAME_ENDORSER; s->opt[OPT_ENDORSER].title = SANE_TITLE_ENDORSER; s->opt[OPT_ENDORSER].desc = SANE_DESC_ENDORSER; s->opt[OPT_ENDORSER].type = SANE_TYPE_BOOL; s->opt[OPT_ENDORSER].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_ENDORSER].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_ENDORSER].w = s->hw->info.endorser_control; if (!s->hw->info.hasEndorser) s->opt[OPT_ENDORSER].cap |= SANE_CAP_INACTIVE; /* Endorser String: */ s->opt[OPT_ENDORSER_STRING].name = SANE_NAME_ENDORSER_STRING; s->opt[OPT_ENDORSER_STRING].title = SANE_TITLE_ENDORSER_STRING; s->opt[OPT_ENDORSER_STRING].desc = SANE_DESC_ENDORSER_STRING; s->opt[OPT_ENDORSER_STRING].type = SANE_TYPE_STRING; s->opt[OPT_ENDORSER_STRING].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_ENDORSER_STRING].size = sizeof (s->hw->info.endorser_string); s->opt[OPT_ENDORSER_STRING].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_ENDORSER_STRING].s = strdup (s->hw->info.endorser_string); if (!s->hw->info.hasEndorser) s->opt[OPT_ENDORSER_STRING].cap |= SANE_CAP_INACTIVE; /* Batch */ /* Check ADF */ /* timeout ADF */ /* timeout Manual */ /* * "Enhancement" GROUP: */ s->opt[OPT_ENHANCEMENT_GROUP].name = ""; s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_TITLE_ENHANCEMENT_GROUP; s->opt[OPT_ENHANCEMENT_GROUP].desc = ""; s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* Halftone Type */ s->opt[OPT_HALFTONE_CODE].name = SANE_NAME_HALFTONE_CODE; s->opt[OPT_HALFTONE_CODE].title = SANE_TITLE_HALFTONE_CODE; s->opt[OPT_HALFTONE_CODE].desc = SANE_DESC_HALFTONE_CODE; s->opt[OPT_HALFTONE_CODE].type = SANE_TYPE_STRING; s->opt[OPT_HALFTONE_CODE].size = max_string_size (halftone_code); s->opt[OPT_HALFTONE_CODE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_HALFTONE_CODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_HALFTONE_CODE].constraint.string_list = (SANE_String_Const *) & halftone_code[0]; s->val[OPT_HALFTONE_CODE].s = strdup (halftone_code[0]); if (s->image_composition == LINEART) s->opt[OPT_HALFTONE_CODE].cap |= SANE_CAP_INACTIVE; /* Halftone patterns */ s->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN; s->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN; s->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN; s->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING; s->opt[OPT_HALFTONE_PATTERN].size = max_string_size ((SANE_String_Const *) halftone_pattern_list); s->opt[OPT_HALFTONE_PATTERN].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_HALFTONE_PATTERN].constraint.string_list = (SANE_String_Const *) & halftone_pattern_list[0]; s->val[OPT_HALFTONE_PATTERN].s = strdup (halftone_pattern_list[0]); if (s->image_composition == LINEART) s->opt[OPT_HALFTONE_CODE].cap |= SANE_CAP_INACTIVE; /* Gray Filter */ s->opt[OPT_GRAYFILTER].name = SANE_NAME_GRAYFILTER; s->opt[OPT_GRAYFILTER].title = SANE_TITLE_GRAYFILTER; s->opt[OPT_GRAYFILTER].desc = SANE_DESC_GRAYFILTER; s->opt[OPT_GRAYFILTER].type = SANE_TYPE_STRING; s->opt[OPT_GRAYFILTER].size = max_string_size (grayfilter_list); s->opt[OPT_GRAYFILTER].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_GRAYFILTER].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_GRAYFILTER].constraint.string_list = (SANE_String_Const *) & grayfilter_list[0]; s->val[OPT_GRAYFILTER].s = strdup (grayfilter_list[0]); /* Scan Wait Mode */ s->opt[OPT_SCAN_WAIT_MODE].name = SANE_NAME_SCAN_WAIT_MODE; s->opt[OPT_SCAN_WAIT_MODE].title = SANE_TITLE_SCAN_WAIT_MODE; s->opt[OPT_SCAN_WAIT_MODE].desc = SANE_DESC_SCAN_WAIT_MODE; s->opt[OPT_SCAN_WAIT_MODE].type = SANE_TYPE_BOOL; s->opt[OPT_SCAN_WAIT_MODE].unit = SANE_UNIT_NONE; s->val[OPT_SCAN_WAIT_MODE].w = (s->hw->info.scan_wait_mode) ? SANE_TRUE : SANE_FALSE; /* Brightness */ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT; s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE; s->opt[OPT_BRIGHTNESS].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BRIGHTNESS].constraint.range = &s->hw->info.brightness_range; s->val[OPT_BRIGHTNESS].w = 128; /* 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_INT; s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE; s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_THRESHOLD].constraint.range = &s->hw->info.threshold_range; s->val[OPT_THRESHOLD].w = 128; /* Contrast */ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST; s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST; s->opt[OPT_CONTRAST].type = SANE_TYPE_INT; s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE; s->opt[OPT_CONTRAST].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CONTRAST].constraint.range = &s->hw->info.contrast_range; s->val[OPT_CONTRAST].w = 128; /* Gamma */ s->opt[OPT_GAMMA].name = SANE_NAME_GAMMA; s->opt[OPT_GAMMA].title = SANE_TITLE_GAMMA; s->opt[OPT_GAMMA].desc = SANE_DESC_GAMMA; s->opt[OPT_GAMMA].type = SANE_TYPE_STRING; s->opt[OPT_GAMMA].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_GAMMA].size = max_string_size ((SANE_String_Const *) gamma_list); /* s->opt[OPT_GAMMA].type = SANE_TYPE_INT; s->opt[OPT_GAMMA].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA].constraint.range = &u8_range; s->val[OPT_GAMMA].w = 0; */ s->opt[OPT_GAMMA].type = SANE_TYPE_STRING; s->opt[OPT_GAMMA].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_GAMMA].constraint.string_list = (SANE_String_Const *) & gamma_list[0]; s->val[OPT_GAMMA].s = strdup (gamma_list[0]); /* custom-gamma table */ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL; s->opt[OPT_CUSTOM_GAMMA].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE; s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; /* grayscale gamma vector */ s->opt[OPT_GAMMA_VECTOR_GRAY].name = SANE_NAME_GAMMA_VECTOR; s->opt[OPT_GAMMA_VECTOR_GRAY].title = SANE_TITLE_GAMMA_VECTOR; s->opt[OPT_GAMMA_VECTOR_GRAY].desc = SANE_DESC_GAMMA_VECTOR; s->opt[OPT_GAMMA_VECTOR_GRAY].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_GAMMA_VECTOR_GRAY].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_GRAY].size = GAMMA_LENGTH * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR_GRAY].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_GRAY].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_GRAY].wa = s->gamma_table; s->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE; /* Control Panel */ /* ACE Function */ /* ACE Sensitivity */ /* Binary Smoothing Filter */ s->opt[OPT_SMOOTHING].name = SANE_NAME_SMOOTHING; s->opt[OPT_SMOOTHING].title = SANE_TITLE_SMOOTHING; s->opt[OPT_SMOOTHING].desc = SANE_DESC_SMOOTHING; s->opt[OPT_SMOOTHING].type = SANE_TYPE_BOOL; s->opt[OPT_SMOOTHING].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_SMOOTHING].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_SMOOTHING].w = SANE_FALSE; if (!s->hw->info.hasIPU) s->opt[OPT_SMOOTHING].cap |= SANE_CAP_INACTIVE; /* Binary Noise Removal Filter */ s->opt[OPT_NOISEREMOVAL].name = SANE_NAME_NOISEREMOVAL; s->opt[OPT_NOISEREMOVAL].title = SANE_TITLE_NOISEREMOVAL; s->opt[OPT_NOISEREMOVAL].desc = SANE_DESC_NOISEREMOVAL; s->opt[OPT_NOISEREMOVAL].type = SANE_TYPE_STRING; s->opt[OPT_NOISEREMOVAL].size = max_string_size (noisematrix_list); s->opt[OPT_NOISEREMOVAL].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_NOISEREMOVAL].constraint.string_list = (SANE_String_Const *) & noisematrix_list[0]; s->val[OPT_NOISEREMOVAL].s = strdup (noisematrix_list[0]); if (!s->hw->info.hasIPU) s->opt[OPT_NOISEREMOVAL].cap |= SANE_CAP_INACTIVE; /* Automatic Separation */ s->opt[OPT_AUTOSEP].name = SANE_NAME_AUTOSEP; s->opt[OPT_AUTOSEP].title = SANE_TITLE_AUTOSEP; s->opt[OPT_AUTOSEP].desc = SANE_DESC_AUTOSEP; s->opt[OPT_AUTOSEP].type = SANE_TYPE_STRING; s->opt[OPT_AUTOSEP].size = max_string_size (auto_separation_list); s->opt[OPT_AUTOSEP].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_AUTOSEP].constraint.string_list = (SANE_String_Const *) & auto_separation_list[0]; s->val[OPT_AUTOSEP].s = strdup (auto_separation_list[0]); if (!s->hw->info.hasIPU) s->opt[OPT_AUTOSEP].cap |= SANE_CAP_INACTIVE; /* Automatic Binarization */ s->opt[OPT_AUTOBIN].name = SANE_NAME_AUTOBIN; s->opt[OPT_AUTOBIN].title = SANE_TITLE_AUTOBIN; s->opt[OPT_AUTOBIN].desc = SANE_DESC_AUTOBIN; s->opt[OPT_AUTOBIN].type = SANE_TYPE_STRING; s->opt[OPT_AUTOBIN].size = max_string_size (auto_binarization_list); s->opt[OPT_AUTOBIN].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_AUTOBIN].constraint.string_list = (SANE_String_Const *) & auto_binarization_list[0]; s->val[OPT_AUTOBIN].s = strdup (auto_binarization_list[0]); if (!s->hw->info.hasIPU) s->opt[OPT_AUTOBIN].cap |= SANE_CAP_INACTIVE; /* SECTION * The IS450 supports up to 4 Section; The IS420 supports up to 6 Sections * For each struct window_section[i] we need to fill in ulx,uly,width,height,etc * NOT YET IMPLEMENTED */ /* Negative */ s->opt[OPT_NEGATIVE].name = SANE_NAME_NEGATIVE; s->opt[OPT_NEGATIVE].title = SANE_TITLE_NEGATIVE; s->opt[OPT_NEGATIVE].desc = SANE_DESC_NEGATIVE; s->opt[OPT_NEGATIVE].type = SANE_TYPE_BOOL; s->opt[OPT_NEGATIVE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_NEGATIVE].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_NEGATIVE].w = SANE_FALSE; /* White Balance */ s->opt[OPT_WHITE_BALANCE].name = SANE_NAME_WHITE_BALANCE; s->opt[OPT_WHITE_BALANCE].title = SANE_TITLE_WHITE_BALANCE; s->opt[OPT_WHITE_BALANCE].desc = SANE_DESC_WHITE_BALANCE; s->opt[OPT_WHITE_BALANCE].type = SANE_TYPE_BOOL; s->opt[OPT_WHITE_BALANCE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->opt[OPT_WHITE_BALANCE].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_WHITE_BALANCE].w = SANE_FALSE; /* F/T = Relative/Absolute White */ /* * "Miscellaneous" GROUP: */ s->opt[OPT_MISCELLANEOUS_GROUP].name = ""; s->opt[OPT_MISCELLANEOUS_GROUP].title = SANE_TITLE_MISCELLANEOUS_GROUP; s->opt[OPT_MISCELLANEOUS_GROUP].desc = ""; s->opt[OPT_MISCELLANEOUS_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MISCELLANEOUS_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_MISCELLANEOUS_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* Padding Type: */ s->opt[OPT_PADDING_TYPE].name = SANE_NAME_PADDING_TYPE; s->opt[OPT_PADDING_TYPE].title = SANE_TITLE_PADDING_TYPE; s->opt[OPT_PADDING_TYPE].desc = SANE_DESC_PADDING_TYPE; s->opt[OPT_PADDING_TYPE].type = SANE_TYPE_STRING; s->opt[OPT_PADDING_TYPE].cap = SANE_CAP_SOFT_DETECT; /* Display only */ s->opt[OPT_PADDING_TYPE].size = max_string_size (paddingtype_list); /* s->opt[OPT_PADDING_TYPE].size = sizeof((paddingtype_list[ get_paddingtype_strndx(TRUNCATE) ])); s->opt[OPT_PADDING_TYPE].constraint_type = SANE_CONSTRAINT_NONE; */ s->opt[OPT_PADDING_TYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_PADDING_TYPE].constraint.string_list = (SANE_String_Const *) & paddingtype_list[0]; s->val[OPT_PADDING_TYPE].s = strdup (paddingtype_list[get_paddingtype_strndx (TRUNCATE)]); DBG (DBG_info, "PADDINGTYPE =%s size=%d\n", s->val[OPT_PADDING_TYPE].s, s->opt[OPT_PADDING_TYPE].size); /* Bit Order s->opt[OPT_BITORDER].name = SANE_NAME_BITORDER; s->opt[OPT_BITORDER].title = SANE_TITLE_BITORDER; s->opt[OPT_BITORDER].desc = SANE_DESC_BITORDER; s->opt[OPT_BITORDER].type = SANE_TYPE_WORD; s->opt[OPT_BITORDER].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_BITORDER].constraint_type = SANE_CONSTRAINT_NONE s->val[OPT_BITORDER].w = 0x7; */ /* Self Diagnostics */ s->opt[OPT_SELF_DIAGNOSTICS].name = SANE_NAME_SELF_DIAGNOSTICS; s->opt[OPT_SELF_DIAGNOSTICS].title = SANE_TITLE_SELF_DIAGNOSTICS; s->opt[OPT_SELF_DIAGNOSTICS].desc = SANE_DESC_SELF_DIAGNOSTICS; s->opt[OPT_SELF_DIAGNOSTICS].type = SANE_TYPE_BUTTON; /* Optical Diagnostics */ s->opt[OPT_OPTICAL_ADJUSTMENT].name = SANE_NAME_OPTICAL_ADJUSTMENT; s->opt[OPT_OPTICAL_ADJUSTMENT].title = SANE_TITLE_OPTICAL_ADJUSTMENT; s->opt[OPT_OPTICAL_ADJUSTMENT].desc = SANE_DESC_OPTICAL_ADJUSTMENT; s->opt[OPT_OPTICAL_ADJUSTMENT].type = SANE_TYPE_BUTTON; /* MAINTENANCE DATA */ s->opt[OPT_DATA_GROUP].name = ""; s->opt[OPT_DATA_GROUP].title = "Maintenance Data"; s->opt[OPT_DATA_GROUP].desc = ""; s->opt[OPT_DATA_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_DATA_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_DATA_GROUP].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_UPDATE].name = "Update"; s->opt[OPT_UPDATE].title = "Update"; s->opt[OPT_UPDATE].desc = "Update scanner data"; s->opt[OPT_UPDATE].type = SANE_TYPE_BUTTON; s->opt[OPT_NREGX_ADF].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_NREGX_ADF].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_NREGX_ADF].name = "# registers in main-scanning in ADF mode"; s->opt[OPT_NREGX_ADF].title = "# registers in main-scanning in ADF mode"; s->opt[OPT_NREGX_ADF].desc = "# registers in main-scanning in ADF mode"; s->opt[OPT_NREGX_ADF].type = SANE_TYPE_INT; s->opt[OPT_NREGX_ADF].unit = SANE_UNIT_NONE; s->opt[OPT_NREGX_ADF].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_NREGX_ADF].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_NREGY_ADF].name = "# registers in sub-scanning in ADF mode"; s->opt[OPT_NREGY_ADF].title = "# registers in sub-scanning in ADF mode"; s->opt[OPT_NREGY_ADF].desc = "# registers in sub-scanning in ADF mode"; s->opt[OPT_NREGY_ADF].type = SANE_TYPE_INT; s->opt[OPT_NREGY_ADF].unit = SANE_UNIT_NONE; s->opt[OPT_NREGY_ADF].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_NREGY_ADF].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_NREGX_BOOK].name = "# registers in main-scanning in book mode"; s->opt[OPT_NREGX_BOOK].title = "# registers in main-scanning in book mode"; s->opt[OPT_NREGX_BOOK].desc = "# registers in main-scanning in book mode"; s->opt[OPT_NREGX_BOOK].type = SANE_TYPE_INT; s->opt[OPT_NREGX_BOOK].unit = SANE_UNIT_NONE; s->opt[OPT_NREGX_BOOK].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_NREGX_BOOK].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_NREGY_BOOK].name = "# registers in sub-scanning in book mode"; s->opt[OPT_NREGY_BOOK].title = "# registers in sub-scanning in book mode"; s->opt[OPT_NREGY_BOOK].desc = "# registers in sub-scanning in book mode"; s->opt[OPT_NREGY_BOOK].type = SANE_TYPE_INT; s->opt[OPT_NREGY_BOOK].unit = SANE_UNIT_NONE; s->opt[OPT_NREGY_BOOK].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_NREGY_BOOK].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_NSCANS_ADF].name = "# ADF Scans"; s->opt[OPT_NSCANS_ADF].title = "# ADF Scans"; s->opt[OPT_NSCANS_ADF].desc = "# ADF Scans"; s->opt[OPT_NSCANS_ADF].type = SANE_TYPE_INT; s->opt[OPT_NSCANS_ADF].unit = SANE_UNIT_NONE; s->opt[OPT_NSCANS_ADF].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_NSCANS_ADF].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_NSCANS_BOOK].name = "# BOOK Scans"; s->opt[OPT_NSCANS_BOOK].title = "# BOOK Scans"; s->opt[OPT_NSCANS_BOOK].desc = "# BOOK Scans"; s->opt[OPT_NSCANS_BOOK].type = SANE_TYPE_INT; s->opt[OPT_NSCANS_BOOK].unit = SANE_UNIT_NONE; s->opt[OPT_NSCANS_BOOK].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_NSCANS_BOOK].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_LAMP_TIME].name = "LAMP TIME"; s->opt[OPT_LAMP_TIME].title = "LAMP TIME"; s->opt[OPT_LAMP_TIME].desc = "LAMP TIME"; s->opt[OPT_LAMP_TIME].type = SANE_TYPE_INT; s->opt[OPT_LAMP_TIME].unit = SANE_UNIT_NONE; s->opt[OPT_LAMP_TIME].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_LAMP_TIME].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_EO_ODD].name = "E/O Balance ODD"; s->opt[OPT_EO_ODD].title = "E/O Balance ODD"; s->opt[OPT_EO_ODD].desc = "Adj. of E/O Balance in black level ODD"; s->opt[OPT_EO_ODD].type = SANE_TYPE_INT; s->opt[OPT_EO_ODD].unit = SANE_UNIT_NONE; s->opt[OPT_EO_ODD].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_EO_ODD].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_EO_EVEN].name = "E/O Balance EVEN"; s->opt[OPT_EO_EVEN].title = "E/O Balance EVEN"; s->opt[OPT_EO_EVEN].desc = "Adj. of E/O Balance in black level EVEN"; s->opt[OPT_EO_EVEN].type = SANE_TYPE_INT; s->opt[OPT_EO_EVEN].unit = SANE_UNIT_NONE; s->opt[OPT_EO_EVEN].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_EO_EVEN].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_BLACK_LEVEL_ODD].name = "Black Level ODD"; s->opt[OPT_BLACK_LEVEL_ODD].title = "Black Level ODD"; s->opt[OPT_BLACK_LEVEL_ODD].desc = "Adj. data in black level (ODD)"; s->opt[OPT_BLACK_LEVEL_ODD].type = SANE_TYPE_INT; s->opt[OPT_BLACK_LEVEL_ODD].unit = SANE_UNIT_NONE; s->opt[OPT_BLACK_LEVEL_ODD].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_BLACK_LEVEL_ODD].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_BLACK_LEVEL_EVEN].name = "Black Level EVEN"; s->opt[OPT_BLACK_LEVEL_EVEN].title = "Black Level EVEN"; s->opt[OPT_BLACK_LEVEL_EVEN].desc = "Adj. data in black level (EVEN)"; s->opt[OPT_BLACK_LEVEL_EVEN].type = SANE_TYPE_INT; s->opt[OPT_BLACK_LEVEL_EVEN].unit = SANE_UNIT_NONE; s->opt[OPT_BLACK_LEVEL_EVEN].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_BLACK_LEVEL_EVEN].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_WHITE_LEVEL_ODD].name = "White Level ODD"; s->opt[OPT_WHITE_LEVEL_ODD].title = "White Level ODD"; s->opt[OPT_WHITE_LEVEL_ODD].desc = "Adj. data in White level (ODD)"; s->opt[OPT_WHITE_LEVEL_ODD].type = SANE_TYPE_INT; s->opt[OPT_WHITE_LEVEL_ODD].unit = SANE_UNIT_NONE; s->opt[OPT_WHITE_LEVEL_ODD].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_WHITE_LEVEL_ODD].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_WHITE_LEVEL_EVEN].name = "White Level EVEN"; s->opt[OPT_WHITE_LEVEL_EVEN].title = "White Level EVEN"; s->opt[OPT_WHITE_LEVEL_EVEN].desc = "Adj. data in White level (EVEN)"; s->opt[OPT_WHITE_LEVEL_EVEN].type = SANE_TYPE_INT; s->opt[OPT_WHITE_LEVEL_EVEN].unit = SANE_UNIT_NONE; s->opt[OPT_WHITE_LEVEL_EVEN].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_WHITE_LEVEL_EVEN].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_WHITE_LEVEL_EVEN].name = "White Level EVEN"; s->opt[OPT_WHITE_LEVEL_EVEN].title = "White Level EVEN"; s->opt[OPT_WHITE_LEVEL_EVEN].desc = "Adj. data in White level (EVEN)"; s->opt[OPT_WHITE_LEVEL_EVEN].type = SANE_TYPE_INT; s->opt[OPT_WHITE_LEVEL_EVEN].unit = SANE_UNIT_NONE; s->opt[OPT_WHITE_LEVEL_EVEN].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_WHITE_LEVEL_EVEN].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_DENSITY].name = "Density Adjustment"; s->opt[OPT_DENSITY].title = "Density Adjustment"; s->opt[OPT_DENSITY].desc = "Density adjustment of std. white board"; s->opt[OPT_DENSITY].type = SANE_TYPE_INT; s->opt[OPT_DENSITY].unit = SANE_UNIT_NONE; s->opt[OPT_DENSITY].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_DENSITY].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_FIRST_ADJ_WHITE_ODD].name = "1st adj. in white level (ODD)"; s->opt[OPT_FIRST_ADJ_WHITE_ODD].title = "1st adj. in white level (ODD)"; s->opt[OPT_FIRST_ADJ_WHITE_ODD].desc = "1st adj. in white level (ODD)"; s->opt[OPT_FIRST_ADJ_WHITE_ODD].type = SANE_TYPE_INT; s->opt[OPT_FIRST_ADJ_WHITE_ODD].unit = SANE_UNIT_NONE; s->opt[OPT_FIRST_ADJ_WHITE_ODD].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_FIRST_ADJ_WHITE_ODD].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_FIRST_ADJ_WHITE_EVEN].name = "1st adj. in white level (EVEN)"; s->opt[OPT_FIRST_ADJ_WHITE_EVEN].title = "1st adj. in white level (EVEN)"; s->opt[OPT_FIRST_ADJ_WHITE_EVEN].desc = "1st adj. in white level (EVEN)"; s->opt[OPT_FIRST_ADJ_WHITE_EVEN].type = SANE_TYPE_INT; s->opt[OPT_FIRST_ADJ_WHITE_EVEN].unit = SANE_UNIT_NONE; s->opt[OPT_FIRST_ADJ_WHITE_EVEN].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_FIRST_ADJ_WHITE_EVEN].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_NREGX_REVERSE].name = "# registers of main-scanning of backside"; s->opt[OPT_NREGX_REVERSE].title = "# registers of main-scanning of backside"; s->opt[OPT_NREGX_REVERSE].desc = "# registers of main-scanning of ADF backside"; s->opt[OPT_NREGX_REVERSE].type = SANE_TYPE_INT; s->opt[OPT_NREGX_REVERSE].unit = SANE_UNIT_NONE; s->opt[OPT_NREGX_REVERSE].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_NREGX_REVERSE].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_NREGY_REVERSE].name = "# registers of sub-scanning of backside"; s->opt[OPT_NREGY_REVERSE].title = "# registers of sub-scanning of backside"; s->opt[OPT_NREGY_REVERSE].desc = "# registers of sub-scanning of ADF backside"; s->opt[OPT_NREGY_REVERSE].type = SANE_TYPE_INT; s->opt[OPT_NREGY_REVERSE].unit = SANE_UNIT_NONE; s->opt[OPT_NREGY_REVERSE].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_NREGY_REVERSE].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_NSCANS_REVERSE_ADF].name = "# of scans of reverse side in ADF"; s->opt[OPT_NSCANS_REVERSE_ADF].title = "# of scans of reverse side in ADF"; s->opt[OPT_NSCANS_REVERSE_ADF].desc = "# of scans of reverse side in ADF"; s->opt[OPT_NSCANS_REVERSE_ADF].type = SANE_TYPE_INT; s->opt[OPT_NSCANS_REVERSE_ADF].unit = SANE_UNIT_NONE; s->opt[OPT_NSCANS_REVERSE_ADF].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_NSCANS_REVERSE_ADF].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_REVERSE_TIME].name = "LAMP TIME (reverse)"; s->opt[OPT_REVERSE_TIME].title = "LAMP TIME (reverse)"; s->opt[OPT_REVERSE_TIME].desc = "LAMP TIME (reverse)"; s->opt[OPT_REVERSE_TIME].type = SANE_TYPE_INT; s->opt[OPT_REVERSE_TIME].unit = SANE_UNIT_NONE; s->opt[OPT_REVERSE_TIME].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_REVERSE_TIME].constraint_type = SANE_CONSTRAINT_NONE; s->opt[OPT_NCHARS].name = "# of endorser characters"; s->opt[OPT_NCHARS].title = "# of endorser characters"; s->opt[OPT_NCHARS].desc = "# of endorser characters"; s->opt[OPT_NCHARS].type = SANE_TYPE_INT; s->opt[OPT_NCHARS].unit = SANE_UNIT_NONE; s->opt[OPT_NCHARS].cap = SANE_CAP_SOFT_DETECT; s->opt[OPT_NCHARS].constraint_type = SANE_CONSTRAINT_NONE; DBG (DBG_proc, "<< init_options\n"); return SANE_STATUS_GOOD; } static SANE_Status attach (SANE_String_Const devname, int __sane_unused__ connType, HS2P_Device ** devp) { SANE_Status status; HS2P_Device *dev; struct inquiry_standard_data ibuf; struct inquiry_vpd_data vbuf; struct inquiry_jis_data jbuf; size_t buf_size; int fd = -1; double mm; char device_string[60]; unsigned int i; SANE_String *str; DBG (DBG_sane_proc, ">>> attach:\n"); for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->sane.name, devname) == 0) { if (devp) *devp = dev; return SANE_STATUS_GOOD; } } DBG (DBG_sane_proc, ">>> attach: opening \"%s\"\n", devname); /* sanei_scsi_open takes an option bufsize argument */ status = sanei_scsi_open (devname, &fd, &sense_handler, &(dev->sense_data)); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, ">>> attach: open failed: %s\n", sane_strstatus (status)); return (status); } DBG (DBG_sane_proc, ">>> attach: opened %s fd=%d\n", devname, fd); DBG (DBG_sane_proc, ">>> attach: sending INQUIRY (standard data)\n"); memset (&ibuf, 0, sizeof (ibuf)); buf_size = sizeof (ibuf); status = inquiry (fd, &ibuf, &buf_size, 0, HS2P_INQUIRY_STANDARD_PAGE_CODE); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, ">>> attach: inquiry failed: %s\n", sane_strstatus (status)); sanei_scsi_close (fd); return (status); } DBG (DBG_info, ">>> attach: reported devtype='%d', vendor='%.8s', product='%.16s', revision='%.4s'\n", ibuf.devtype, ibuf.vendor, ibuf.product, ibuf.revision); DBG (DBG_info, ">>> attach: reported RMB=%#x Ver=%#x ResponseDataFormat=%#x Length=%#x Byte7=%#x\n", ibuf.rmb_evpd, ibuf.version, ibuf.response_data_format, ibuf.length, ibuf.byte7); if (ibuf.devtype != 6 || strncmp ((char *) ibuf.vendor, "RICOH ", 8) != 0) { DBG (DBG_warning, ">>> attach: device is not a RICOH scanner\n"); sanei_scsi_close (fd); return SANE_STATUS_INVAL; } else if (!is_device_supported ((char *) ibuf.product)) { DBG (DBG_warning, ">>> attach: device %s is not yet a supported RICOH scanner\n", ibuf.product); sanei_scsi_close (fd); return SANE_STATUS_INVAL; } /* We should now have an open file descriptor to a supported hs2p scanner */ DBG (DBG_sane_proc, ">>> attach: sending TEST_UNIT_READY\n"); do { status = test_unit_ready (fd); } while (status == HS2P_SK_UNIT_ATTENTION); if (status != HS2P_SCSI_STATUS_GOOD) { DBG (DBG_error, ">>> attach: test unit ready failed (%s)\n", sane_strstatus (status)); sanei_scsi_close (fd); return (status); } DBG (DBG_sane_proc, ">>> attach: sending INQUIRY (vpd data)\n"); memset (&vbuf, 0, sizeof (vbuf)); buf_size = sizeof (vbuf); status = inquiry (fd, &vbuf, &buf_size, 1, HS2P_INQUIRY_VPD_PAGE_CODE); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, ">>> attach: inquiry (vpd data) failed: %s\n", sane_strstatus (status)); sanei_scsi_close (fd); return status; } print_vpd_info (&vbuf); DBG (DBG_sane_proc, ">>> attach: sending INQUIRY (jis data)\n"); memset (&jbuf, 0, sizeof (jbuf)); buf_size = sizeof (jbuf); status = inquiry (fd, &jbuf, &buf_size, 1, HS2P_INQUIRY_JIS_PAGE_CODE); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, ">>> attach: inquiry (jis data) failed: %s\n", sane_strstatus (status)); sanei_scsi_close (fd); return status; } print_jis_info (&jbuf); /* Fill in HS2P_Device {sane;info} */ dev = malloc (sizeof (*dev)); if (!dev) return SANE_STATUS_NO_MEM; memset (dev, 0, sizeof (*dev)); /* Maximum Number of Sub-Sections of Main Scanning Window */ if (strncmp ((char *) ibuf.product, "IS450", 5) == 0) { dev->info.max_win_sections = 4; } else if (strncmp ((char *) ibuf.product, "IS420", 5) == 0) { dev->info.max_win_sections = 6; } /* Some MODE SELECT scanner options */ DBG (DBG_proc, ">>> attach: get_basic_measurement_unit\n"); status = get_basic_measurement_unit (fd, &(dev->info.bmu), &(dev->info.mud)); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, ">>> attach: get_basic_measurement_unit failed (%s)\n", sane_strstatus (status)); DBG (DBG_error, ">>> attach: setting to defaults\n"); status = set_basic_measurement_unit (fd, MILLIMETERS); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, ">>> attach: set_basic_measurement_unit failed (%s)\n", sane_strstatus (status)); } } if ((status = get_connection_parameters (fd, &(dev->info.cxn))) != SANE_STATUS_GOOD) { DBG (DBG_error, ">>> attach: get_connection_parameters failed\n"); } status = get_endorser_control (fd, &dev->info.endorser_control); if (status != SANE_STATUS_GOOD || dev->info.endorser_control != 0x01) { DBG (DBG_error, ">>> attach: get_endorser_control failed: return value=%#02x\n", dev->info.endorser_control); dev->info.endorser_control = 0x00; } if ((dev->info.service_mode = get_service_mode (fd)) != 0x00 && dev->info.service_mode != 0x01) { DBG (DBG_error, ">>> attach: get_service_mode failed %#02x\n", dev->info.service_mode); dev->info.service_mode = 0x00; } if ((dev->info.scan_wait_mode = get_scan_wait_mode (fd)) != 0x00 && dev->info.scan_wait_mode != 0x01) { DBG (DBG_error, ">>> attach: get_scan_wait_mode failed: return value=%#02x\n", dev->info.scan_wait_mode); dev->info.scan_wait_mode = 0x00; } status = get_white_balance (fd, &dev->info.white_balance); if (status != SANE_STATUS_GOOD && dev->info.white_balance != 0x01) { DBG (DBG_error, ">>> attach: get_white_balance failed: return value=%#02x\n", dev->info.white_balance); dev->info.white_balance = RELATIVE_WHITE; } DBG (DBG_info, ">>> attach: flushing and closing fd=%d\n", fd); sanei_scsi_req_flush_all (); sanei_scsi_close (fd); dev->info.devtype = ibuf.devtype; snprintf (dev->info.vendor, 9, "%-.5s", ibuf.vendor); /* RICOH */ trim_spaces (dev->info.vendor, sizeof (dev->info.vendor)); snprintf (dev->info.product, 16, "%-.16s", ibuf.product); /* IS450 */ trim_spaces (dev->info.product, sizeof (dev->info.product)); snprintf (dev->info.revision, 5, "%-.4s", ibuf.revision); /* 1R04 */ trim_spaces (dev->info.revision, sizeof (dev->info.revision)); /* SANE_Device sane information */ dev->sane.name = strdup (devname); dev->sane.vendor = (strcmp (dev->info.vendor, "RICOH") == 0) ? strdup ("Ricoh") : strdup (dev->info.vendor); dev->sane.model = strdup (dev->info.product); dev->sane.type = strdup ("sheetfed scanner"); /* dev->sane.email_backend_author = strdup(" jeremy@acjlaw.net"); dev->sane.backend_website = strdup("http://www.acjlaw.net:8080/~jeremy/Ricoh"); */ /* read these values from backend configuration file using parse_configuration dev->sane.location = strdup(); dev->sane.comment = strdup(); dev->sane.backend_version_code = strdup(); */ /* NOT YET USED */ /* dev->sane.backend_capablity_flags = 0x00; */ /* set capabilities from vpd */ /* adf_id: 0=none,1=simplex,2=duplex,3=ARDF,4=reserved; should be 1 or 2 for IS450 family */ dev->info.hasADF = vbuf.adf_id == 0 ? SANE_FALSE : SANE_TRUE; dev->info.hasSimplex = vbuf.adf_id == 1 ? SANE_TRUE : SANE_FALSE; dev->info.hasDuplex = vbuf.adf_id == 2 ? SANE_TRUE : SANE_FALSE; dev->info.hasARDF = vbuf.adf_id == 3 ? SANE_TRUE : SANE_FALSE; /* end_id 0=none,1=Yes,2=reserved; should always be 0 or 1 */ dev->info.hasEndorser = vbuf.end_id == 1 ? SANE_TRUE : SANE_FALSE; for (i = 0; i < 20; i++) dev->info.endorser_string[i] = '\0'; /* ipu_id: Bit0: '0'-no IPU, '1'-has IPU * Bit1: '0'-no extended board, '1'-has extended board; * should always be 0 */ dev->info.hasIPU = (vbuf.ipu_id & 0x01) == 0x01 ? SANE_TRUE : SANE_FALSE; dev->info.hasXBD = (vbuf.ipu_id & 0x02) == 0x02 ? SANE_TRUE : SANE_FALSE; /* Image Composition Byte is set to 0x37 (0011 0111) */ dev->info.supports_lineart = (vbuf.imagecomposition & 0x01) == 0x01 ? SANE_TRUE : SANE_FALSE; /* TRUE */ dev->info.supports_dithering = (vbuf.imagecomposition & 0x02) == 0x02 ? SANE_TRUE : SANE_FALSE; /* TRUE */ dev->info.supports_errordiffusion = (vbuf.imagecomposition & 0x04) == 0x04 ? SANE_TRUE : SANE_FALSE; /* TRUE */ dev->info.supports_color = (vbuf.imagecomposition & 0x08) == 0x08 ? SANE_TRUE : SANE_FALSE; /* FALSE */ dev->info.supports_4bitgray = (vbuf.imagecomposition & 0x10) == 0x10 ? SANE_TRUE : SANE_FALSE; /* TRUE */ dev->info.supports_8bitgray = (vbuf.imagecomposition & 0x20) == 0x20 ? SANE_TRUE : SANE_FALSE; /* TRUE */ /* vbuf.imagecomposition & 0x40; FALSE */ /* vbuf.imagecomposition & 0x80 FALSE reserved */ str = &scan_mode_list[0]; /* array of string pointers */ if (dev->info.supports_lineart) *str++ = strdup (SM_LINEART); if (dev->info.supports_dithering || dev->info.supports_errordiffusion) *str++ = strdup (SM_HALFTONE); if (dev->info.supports_color) *str++ = strdup (SM_COLOR); if (dev->info.supports_4bitgray) *str++ = strdup (SM_4BITGRAY); if (dev->info.supports_8bitgray) *str++ = strdup (SM_8BITGRAY); *str = NULL; snprintf (device_string, 60, "Flatbed%s%s%s%s%s%s", dev->info.hasADF ? "/ADF" : "", dev->info.hasDuplex ? "/Duplex" : "", dev->info.hasEndorser ? "/Endorser" : "", dev->info.hasIPU ? "/IPU" : "", dev->info.supports_color ? " Color" : " B&W", " Scanner"); dev->sane.type = strdup (device_string); /* ACE Image Data Processing Binary Filters * For IS450 this is set to 0x18 (0001 1000) if IPU installed, else 0x00 * For IS420 this is set to 0x3C (0011 1100) if IPU installed, else 0x00 */ dev->info.supports_whiteframing = ((vbuf.imagedataprocessing[0] & 0x01) == 0x01) ? SANE_TRUE : SANE_FALSE; dev->info.supports_blackframing = ((vbuf.imagedataprocessing[0] & 0x02) == 0x02) ? SANE_TRUE : SANE_FALSE; dev->info.supports_edgeextraction = ((vbuf.imagedataprocessing[0] & 0x04) == 0x04) ? SANE_TRUE : SANE_FALSE; dev->info.supports_noiseremoval = ((vbuf.imagedataprocessing[0] & 0x08) == 0x08) ? SANE_TRUE : SANE_FALSE; dev->info.supports_smoothing = ((vbuf.imagedataprocessing[0] & 0x10) == 0x10) ? SANE_TRUE : SANE_FALSE; dev->info.supports_linebolding = ((vbuf.imagedataprocessing[0] & 0x20) == 0x20) ? SANE_TRUE : SANE_FALSE; /* Compression Method is not supported for IS450 * is supported for IS420 */ dev->info.supports_MH = ((vbuf.compression & 0x01) == 0x01) ? SANE_TRUE : SANE_FALSE; dev->info.supports_MR = ((vbuf.compression & 0x02) == 0x02) ? SANE_TRUE : SANE_FALSE; dev->info.supports_MMR = ((vbuf.compression & 0x04) == 0x04) ? SANE_TRUE : SANE_FALSE; dev->info.supports_MHB = ((vbuf.compression & 0x08) == 0x08) ? SANE_TRUE : SANE_FALSE; /* MH Byte Boundary */ /* compression_list[] will have variable number of elements, but the order will be fixed as follows: */ str = &compression_list[0]; *str++ = strdup ("none"); if (dev->info.supports_MH) *str++ = strdup ("G3 1-D MH"); if (dev->info.supports_MR) *str++ = strdup ("G3 2-D MR"); if (dev->info.supports_MMR) *str++ = strdup ("G4 2-D MMR"); if (dev->info.supports_MHB) *str++ = strdup ("MH Byte Boundary"); *str = NULL; /* Marker Recognition is set to 0x00 */ dev->info.supports_markerrecognition = ((vbuf.markerrecognition & 0x01) == 0x01) ? SANE_TRUE : SANE_FALSE; /* Size Recognition * For IS450 this is set to 0x01 when IPU installed; else 0x00 * For IS420 this is set to 0x01 */ dev->info.supports_sizerecognition = ((vbuf.sizerecognition & 0x01) == 0x01) ? SANE_TRUE : SANE_FALSE; /* X Maximum Output Pixel in main scanning direction * For IS450 this is set to 0x1360 (4960) * For IS420 this is set to (4880) * [MostSignificantByte LeastSignificantByte] */ dev->info.xmaxoutputpixels = (vbuf.xmaxoutputpixels[0] << 8) | vbuf.xmaxoutputpixels[1]; /* Set capabilities from jis VPD IDENTIFIER Page Code F0H */ dev->info.resBasicX = _2btol (&jbuf.BasicRes.x[0]); /* set to 400 */ dev->info.resBasicY = _2btol (&jbuf.BasicRes.y[0]); /* set to 400 */ dev->info.resXstep = (jbuf.resolutionstep >> 4) & 0x0F; /* set to 1 */ dev->info.resYstep = jbuf.resolutionstep & 0x0F; /* set to 1 */ dev->info.resMaxX = _2btol (&jbuf.MaxRes.x[0]); /* set to 800 */ dev->info.resMaxY = _2btol (&jbuf.MaxRes.y[0]); /* set to 800 */ dev->info.resMinX = _2btol (&jbuf.MinRes.x[0]); /* set to 100 for IS450 and 60 for IS420 */ dev->info.resMinY = _2btol (&jbuf.MinRes.y[0]); /* set to 100 for IS450 and 60 for IS420 */ dev->info.xres_range.min = _2btol (&jbuf.MinRes.x[0]); /* set to 100 for IS450 and 60 for IS420 */ dev->info.xres_range.max = _2btol (&jbuf.MaxRes.x[0]); /* set to 800 */ dev->info.resXstep = (jbuf.resolutionstep >> 4) & 0x0F; /* set to 1 */ dev->info.xres_range.quant = dev->info.resXstep; dev->info.yres_range.min = _2btol (&jbuf.MinRes.y[0]); /* set to 100 for IS450 and 60 for IS420 */ dev->info.yres_range.max = _2btol (&jbuf.MaxRes.y[0]); /* set to 800 */ dev->info.resYstep = jbuf.resolutionstep & 0x0F; /* set to 1 */ dev->info.yres_range.quant = dev->info.resYstep; /* set the length of the list to zero first, then append standard resolutions */ i = 0; if ((jbuf.standardres[0] & 0x80) == 0x80) dev->info.resStdList[++i] = 60; if ((jbuf.standardres[0] & 0x40) == 0x40) dev->info.resStdList[++i] = 75; if ((jbuf.standardres[0] & 0x20) == 0x20) dev->info.resStdList[++i] = 100; if ((jbuf.standardres[0] & 0x10) == 0x10) dev->info.resStdList[++i] = 120; if ((jbuf.standardres[0] & 0x08) == 0x08) dev->info.resStdList[++i] = 150; if ((jbuf.standardres[0] & 0x04) == 0x04) dev->info.resStdList[++i] = 160; if ((jbuf.standardres[0] & 0x02) == 0x02) dev->info.resStdList[++i] = 180; if ((jbuf.standardres[0] & 0x01) == 0x01) dev->info.resStdList[++i] = 200; if ((jbuf.standardres[1] & 0x80) == 0x80) dev->info.resStdList[++i] = 240; if ((jbuf.standardres[1] & 0x40) == 0x40) dev->info.resStdList[++i] = 300; if ((jbuf.standardres[1] & 0x20) == 0x20) dev->info.resStdList[++i] = 320; if ((jbuf.standardres[1] & 0x10) == 0x10) dev->info.resStdList[++i] = 400; if ((jbuf.standardres[1] & 0x08) == 0x08) dev->info.resStdList[++i] = 480; if ((jbuf.standardres[1] & 0x04) == 0x04) dev->info.resStdList[++i] = 600; if ((jbuf.standardres[1] & 0x02) == 0x02) dev->info.resStdList[++i] = 800; if ((jbuf.standardres[1] & 0x01) == 0x01) dev->info.resStdList[++i] = 1200; dev->info.resStdList[0] = i; /* number of resolutions */ if (dev->info.resStdList[0] == 0) { /* make a default standard resolutions for 200 and 300dpi */ DBG (DBG_warning, "attach: no standard resolutions reported\n"); dev->info.resStdList[0] = 2; dev->info.resStdList[1] = 200; dev->info.resStdList[2] = 300; dev->info.resBasicX = dev->info.resBasicY = 300; } DBG (DBG_info, "attach: Window(W/L) = (%lu/%lu)\n", _4btol (&jbuf.Window.width[0]), _4btol (&jbuf.Window.length[0])); dev->info.winWidth = _4btol (&jbuf.Window.width[0]); dev->info.winHeight = _4btol (&jbuf.Window.length[0]); if (dev->info.winWidth <= 0) { dev->info.winWidth = (SANE_Int) (dev->info.resBasicX * 8.5); DBG (DBG_warning, "attach: invalid window width reported, using %d\n", dev->info.winWidth); } if (dev->info.winHeight <= 0) { dev->info.winHeight = dev->info.resBasicY * 14; DBG (DBG_warning, "attach: invalid window height reported, using %d\n", dev->info.winHeight); } /* 4692 / 400 * 25.4 = 297 */ mm = (dev->info.resBasicX > 0) ? ((double) dev->info.winWidth / (double) dev->info.resBasicX * MM_PER_INCH) : 0.0; dev->info.x_range.min = SANE_FIX (0.0); dev->info.x_range.max = SANE_FIX (mm); dev->info.x_range.quant = SANE_FIX (0.0); DBG (DBG_info, "attach: winWidth=%d resBasicX=%d mm/in=%f mm=%f\n", dev->info.winWidth, dev->info.resBasicX, MM_PER_INCH, mm); mm = (dev->info.resBasicY > 0) ? ((double) dev->info.winHeight / (double) dev->info.resBasicY * MM_PER_INCH) : 0.0; dev->info.y_range.min = SANE_FIX (0.0); dev->info.y_range.max = SANE_FIX (mm); dev->info.y_range.quant = SANE_FIX (0.0); DBG (DBG_info, "attach: RANGE x_range.max=%f, y_range.max=%f\n", SANE_UNFIX (dev->info.x_range.max), SANE_UNFIX (dev->info.y_range.max)); /* min, max, quantization light-dark 1-255, 0 means default 128 */ dev->info.brightness_range.min = 1; dev->info.brightness_range.max = 255; dev->info.brightness_range.quant = 1; /* min, max, quantization white-black 1-255, 0 means default 128 */ dev->info.contrast_range.min = 1; dev->info.contrast_range.max = 255; dev->info.contrast_range.quant = 1; /* min, max, quantization low-high 1-255, 0 means default 128 */ dev->info.threshold_range.min = 1; dev->info.threshold_range.max = 255; dev->info.threshold_range.quant = 1; /* jbuf.functions */ dev->info.overflow_support = ((jbuf.functions & 0x01) == 0x01) ? SANE_TRUE : SANE_FALSE; dev->info.lineart_support = ((jbuf.functions & 0x02) == 0x02) ? SANE_TRUE : SANE_FALSE; dev->info.dither_support = ((jbuf.functions & 0x04) == 0x04) ? SANE_TRUE : SANE_FALSE; dev->info.grayscale_support = ((jbuf.functions & 0x08) == 0x08) ? SANE_TRUE : SANE_FALSE; /* set option defaults */ dev->info.default_res = dev->info.resBasicX; dev->info.default_xres = dev->info.resBasicX; dev->info.default_yres = dev->info.resBasicY; dev->info.default_imagecomposition = LINEART; dev->info.default_media = FLATBED; dev->info.default_duplex = SANE_FALSE; /* dev->info.autoborder_default = dev->info.canBorderRecog; */ /* dev->info.batch_default = SANE_FALSE; dev->info.deskew_default = SANE_FALSE; dev->info.check_adf_default = SANE_FALSE; dev->info.timeout_adf_default = 0; dev->info.timeout_manual_default = 0; */ /* dev->info.control_panel_default = dev->info.canACE; Image Data Processing */ ++num_devices; dev->next = first_dev; first_dev = dev; if (devp) *devp = dev; DBG (DBG_sane_proc, "<<< attach:\n"); return SANE_STATUS_GOOD; } /* SANE callback to attach a SCSI device */ static SANE_Status attach_one_scsi (const char *devname) { return attach (devname, CONNECTION_SCSI, NULL); /* return SANE_STATUS_GOOD; */ } static void parse_configuration_file (FILE * fp) { char line[PATH_MAX], *s, *t; int linenumber; DBG (DBG_proc, ">> parse_configuration_file\n"); if (fp == NULL) { DBG (DBG_proc, ">> parse_configuration_file: No config file present!\n"); } else { /*parse configuration file */ for (linenumber = 0; sanei_config_read (line, sizeof (line), fp); linenumber++) { DBG (DBG_proc, ">> parse_configuration_file: parsing config line \"%s\"\n", line); if (line[0] == '#') continue; /* ignore line comments */ for (s = line; isspace (*s); ++s); /* skip white space: */ for (t = s; *t != '\0'; t++); for (--t; t > s && isspace (*t); t--); *(++t) = '\0'; /*trim trailing space */ if (!strlen (s)) continue; /* ignore empty lines */ if ((t = strstr (s, "scsi ")) != NULL) { /* scsi VENDOR MODEL TYPE BUS CHANNEL ID LUN */ DBG (DBG_proc, ">> parse_configuration_file: config file line %d: trying to attach SCSI: %s'\n", linenumber, line); sanei_config_attach_matching_devices (t, attach_one_scsi); } else if ((t = strstr (s, "/dev/")) != NULL) { /* /dev/scanner /dev/sg0 */ DBG (DBG_proc, ">> parse_configuration_file: config file line %d: trying to attach SCSI: %s'\n", linenumber, line); sanei_config_attach_matching_devices (t, attach_one_scsi); } else if ((t = strstr (s, "option")) != NULL) { for (t += 6; isspace (*t); t++); /* skip to flag */ /* if(strstr(t,"FLAG_VALUE")!=NULL) FLAG_VALUE=SANE_TRUE; */ } else { DBG (DBG_proc, ">> parse_configuration_file: config file line %d: OBSOLETE !! use the scsi keyword!\n", linenumber); DBG (DBG_proc, ">> parse_configuration_file: (see man sane-avision for details): trying to attach SCSI: %s'\n", line); } } fclose (fp); } DBG (DBG_proc, "<< parse_configuration_file\n"); return; } static SANE_Status do_cancel (HS2P_Scanner * s) { SANE_Status status; DBG (DBG_sane_proc, ">> do_cancel\n"); DBG (DBG_proc, "cancel: sending OBJECT POSITION\n"); s->scanning = SANE_FALSE; s->cancelled = SANE_TRUE; s->EOM = SANE_FALSE; if (s->fd >= 0) { if ((status = object_position (s->fd, OBJECT_POSITION_UNLOAD)) != SANE_STATUS_GOOD) { DBG (DBG_error, "cancel: OBJECT POSITION failed\n"); } sanei_scsi_req_flush_all (); release_unit (s->fd); sanei_scsi_close (s->fd); s->fd = -1; } /* if (s->reader_pid > 0){ int exit_status; sanei_thread_kill (s->reader_pid); sanei_thread_waitpid (s->reader_pid, &exit_status); s->reader_pid = 0; } */ DBG (DBG_sane_proc, "<< do_cancel\n"); return (SANE_STATUS_CANCELLED); } SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize) { FILE *fp; DBG_INIT (); /* initialize SANE DEBUG */ /*DBG (DBG_sane_init, "> sane_init (authorize = %p)\n", (void *) authorize); */ #if defined PACKAGE && defined VERSION DBG (DBG_sane_init, "> sane_init: hs2p backend version %d.%d-%d (" PACKAGE " " VERSION ")\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); #endif /* sanei_thread_init (); */ if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); if ((fp = sanei_config_open (HS2P_CONFIG_FILE)) != NULL) { parse_configuration_file (fp); } else { DBG (DBG_sane_init, "> sane_init: No config file \"%s\" present!\n", HS2P_CONFIG_FILE); } #if 0 /* avision.c: search for all supported scanners on all scsi buses & channels */ for (hw = &HS2P_Device_List[0]; hw->mfg != NULL; hw++) { sanei_scsi_find_devices (hw->mfg, /*vendor */ hw->model, /*model */ NULL, /*all types */ -1, /*all bus */ -1, /*all channel */ -1, /*all id */ -1, /*all lun */ attach_one_scsi); /*callback */ DBG (2, "sane_init: %s %s\n", hw->mfg, hw->model); } #endif DBG (DBG_sane_init, "< sane_init\n"); return SANE_STATUS_GOOD; } void sane_exit (void) { HS2P_Device *dev, *next; DBG (DBG_proc, ">> sane_exit\n"); for (dev = first_dev; dev; dev = next) { next = dev->next; free ((void *) (SANE_String_Const *) dev->sane.name); free ((SANE_String_Const *) dev->sane.model); free (dev); } DBG (DBG_proc, "<< sane_exit\n"); } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { static const SANE_Device **devlist = 0; HS2P_Device *dev; int i; DBG (DBG_proc, ">> sane_get_devices (local_only = %d)\n", local_only); if (devlist) free (devlist); devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); if (!devlist) return (SANE_STATUS_NO_MEM); i = 0; for (dev = first_dev; dev; dev = dev->next) devlist[i++] = &dev->sane; devlist[i++] = 0; *device_list = devlist; DBG (DBG_proc, "<< sane_get_devices\n"); return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const devnam, SANE_Handle * handle) { SANE_Status status; HS2P_Device *dev; HS2P_Scanner *s; DBG (DBG_proc, "> sane_open\n"); if (devnam[0] == '\0') { for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->sane.name, devnam) == 0) break; } if (!dev) { status = attach (devnam, CONNECTION_SCSI, &dev); if (status != SANE_STATUS_GOOD) return (status); } } else { dev = first_dev; } if (!dev) return (SANE_STATUS_INVAL); s = malloc (sizeof (*s)); if (!s) return SANE_STATUS_NO_MEM; memset (s, 0, sizeof (*s)); /* initialize */ s->fd = -1; s->hw = dev; s->hw->info.bmu = s->bmu = MILLIMETERS; /* 01H */ s->hw->info.mud = s->mud = 1; /* If the scale is MM or POINT, mud is fixed to 1 */ s->bpp = 1; /* supports 1,4,6,8 so we set to LINEART 1bpp */ /* s->scanning = SANE_FALSE; s->cancelled = SANE_FALSE; */ /* */ ScannerDump (s); init_options (s); s->next = first_handle; /* insert newly opened handle into list of open handles: */ first_handle = s; /* initialize our parameters here AND in sane_start? get_parameters(s, 0); */ *handle = s; DBG (DBG_proc, "< sane_open\n"); return SANE_STATUS_GOOD; } void sane_close (SANE_Handle handle) { HS2P_Scanner *s = (HS2P_Scanner *) handle; char **str; DBG (DBG_proc, ">> sane_close\n"); if (s->fd != -1) sanei_scsi_close (s->fd); free (s); for (str = &compression_list[0]; *str; str++); free (*str); for (str = &scan_mode_list[0]; *str; str++); free (*str); DBG (DBG_proc, "<< sane_close\n"); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { HS2P_Scanner *s = handle; DBG (DBG_proc, ">> sane_get_option_descriptor: %d name=%s\n", option, s->opt[option].name); if ((unsigned) option >= NUM_OPTIONS) return (0); DBG (DBG_info, "<< sane_get_option_descriptor: name=%s\n", s->opt[option].name); return (s->opt + option); } #if 0 static SANE_Int get_scan_mode_id (char *s) /* sequential search */ { SANE_Int i; for (i = 0; scan_mode_list[i]; i++) if (strcmp (s, scan_mode_list[i]) == 0) break; /* unknown strings are treated as 'lineart' */ return scan_mode_list[i] ? i : 0; } #endif static SANE_Status update_hs2p_data (HS2P_Scanner * s) { DBG (DBG_proc, ">> update_hs2p_data\n"); /* OPT_NREGX_ADF: */ DBG (DBG_sane_option, "OPT_NREGX_ADF\n"); s->val[OPT_NREGX_ADF].w = (SANE_Word) s->data.maintenance.nregx_adf; /* OPT_NREGY_ADF: */ DBG (DBG_sane_option, "OPT_NREGY_ADF\n"); s->val[OPT_NREGY_ADF].w = (SANE_Word) s->data.maintenance.nregx_book; /* OPT_NREGX_BOOK: */ DBG (DBG_sane_option, "OPT_NREGX_BOOK\n"); s->val[OPT_NREGX_BOOK].w = (SANE_Word) s->data.maintenance.nregx_book; /* OPT_NREGY_BOOK: */ DBG (DBG_sane_option, "OPT_NREGY_BOOK\n"); s->val[OPT_NREGY_BOOK].w = (SANE_Word) s->data.maintenance.nregy_book; /* OPT_NSCANS_ADF: */ DBG (DBG_sane_option, "OPT_NSCANS_ADF\n"); s->val[OPT_NSCANS_ADF].w = (SANE_Word) _4btol (&(s->data.maintenance.nscans_adf[0])); /* OPT_NSCANS_BOOK: */ DBG (DBG_sane_option, "OPT_NSCANS_BOOK\n"); s->val[OPT_NSCANS_BOOK].w = (SANE_Word) _4btol (&(s->data.maintenance.nscans_book[0])); /* OPT_LAMP_TIME: */ DBG (DBG_sane_option, "OPT_LAMP_TIME\n"); s->val[OPT_LAMP_TIME].w = (SANE_Word) _4btol (&(s->data.maintenance.lamp_time[0])); /* OPT_EO_ODD: */ DBG (DBG_sane_option, "OPT_EO_ODD\n"); s->val[OPT_EO_ODD].w = (SANE_Word) s->data.maintenance.eo_odd; /* OPT_EO_EVEN: */ DBG (DBG_sane_option, "OPT_EO_EVEN\n"); s->val[OPT_EO_EVEN].w = (SANE_Word) s->data.maintenance.eo_even; /* OPT_BLACK_LEVEL_ODD: */ DBG (DBG_sane_option, "OPT_BLACK_LEVEL_ODD\n"); s->val[OPT_BLACK_LEVEL_ODD].w = (SANE_Word) s->data.maintenance.black_level_odd; /* OPT_BLACK_LEVEL_EVEN: */ DBG (DBG_sane_option, "OPT_BLACK_LEVEL_EVEN\n"); s->val[OPT_BLACK_LEVEL_EVEN].w = (SANE_Word) s->data.maintenance.black_level_even; /* OPT_WHITE_LEVEL_ODD: */ DBG (DBG_sane_option, "OPT_WHITE_LEVEL_ODD\n"); s->val[OPT_WHITE_LEVEL_ODD].w = (SANE_Word) _2btol (&(s->data.maintenance.white_level_odd[0])); /* OPT_WHITE_LEVEL_EVEN: */ DBG (DBG_sane_option, "OPT_WHITE_LEVEL_EVEN\n"); s->val[OPT_WHITE_LEVEL_EVEN].w = (SANE_Word) _2btol (&(s->data.maintenance.white_level_even[0])); /* OPT_FIRST_ADJ_WHITE_ODD: */ DBG (DBG_sane_option, "OPT_FIRST_ADJ_WHITE_ODD\n"); s->val[OPT_FIRST_ADJ_WHITE_ODD].w = (SANE_Word) _2btol (&(s->data.maintenance.first_adj_white_odd[0])); /* OPT_FIRST_ADJ_WHITE_EVEN: */ DBG (DBG_sane_option, "OPT_FIRST_ADJ_WHITE_EVEN\n"); s->val[OPT_FIRST_ADJ_WHITE_EVEN].w = (SANE_Word) _2btol (&(s->data.maintenance.first_adj_white_even[0])); /* OPT_DENSITY: */ DBG (DBG_sane_option, "OPT_DENSITY\n"); s->val[OPT_DENSITY].w = (SANE_Word) s->data.maintenance.density_adj; /* OPT_NREGX_REVERSE: */ DBG (DBG_sane_option, "OPT_NREGX_REVERSE\n"); s->val[OPT_NREGX_REVERSE].w = (SANE_Word) s->data.maintenance.nregx_reverse; /* OPT_NREGY_REVERSE: */ DBG (DBG_sane_option, "OPT_NREGY_REVERSE\n"); s->val[OPT_NREGY_REVERSE].w = (SANE_Word) s->data.maintenance.nregy_reverse; /* OPT_NSCANS_REVERSE_ADF: */ DBG (DBG_sane_option, "OPT_NSCANS_REVERSE_ADF\n"); s->val[OPT_NSCANS_REVERSE_ADF].w = (SANE_Word) _4btol (&(s->data.maintenance.nscans_reverse_adf[0])); /* OPT_REVERSE_TIME: */ DBG (DBG_sane_option, "OPT_REVERSE_TIME\n"); s->val[OPT_REVERSE_TIME].w = (SANE_Word) _4btol (&(s->data.maintenance.reverse_time[0])); /* OPT_NCHARS: */ DBG (DBG_sane_option, "OPT_NCHARS\n"); s->val[OPT_NCHARS].w = (SANE_Word) _4btol (&(s->data.maintenance.nchars[0])); DBG (DBG_proc, "<< update_hs2p_data\n"); return SANE_STATUS_GOOD; } static SANE_Status hs2p_open (HS2P_Scanner * s) { SANE_Status status; DBG (DBG_proc, ">> hs2p_open\n"); DBG (DBG_info, ">> hs2p_open: trying to open: name=\"%s\" fd=%d\n", s->hw->sane.name, s->fd); if ((status = sanei_scsi_open (s->hw->sane.name, &s->fd, &sense_handler, &(s->hw->sense_data))) != SANE_STATUS_GOOD) { DBG (DBG_error, "sane_start: open of %s failed: %d %s\n", s->hw->sane.name, status, sane_strstatus (status)); return (status); } DBG (DBG_info, ">>hs2p_open: OPENED \"%s\" fd=%d\n", s->hw->sane.name, s->fd); if ((status = test_unit_ready (s->fd)) != SANE_STATUS_GOOD) { DBG (DBG_error, "hs2p_open: test_unit_ready() failed: %s\n", sane_strstatus (status)); sanei_scsi_close (s->fd); s->fd = -1; return status; } DBG (DBG_proc, "<< hs2p_open\n"); return SANE_STATUS_GOOD; } static SANE_Status hs2p_close (HS2P_Scanner * s) { DBG (DBG_proc, ">> hs2p_close\n"); release_unit (s->fd); sanei_scsi_close (s->fd); s->fd = -1; DBG (DBG_proc, "<< hs2p_close\n"); return SANE_STATUS_GOOD; } #include static SANE_Status get_hs2p_data (HS2P_Scanner * s, ...) { SANE_Status status; SANE_Byte *buf; size_t *len = &(s->data.bufsize); int dtc, fd = s->fd; u_long dtq = 0; /* two bytes */ va_list ap; DBG (DBG_proc, ">> get_hs2p_data\n"); if (fd < 0) { status = hs2p_open (s); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "get_hs2p_data: error opening scanner: %s\n", sane_strstatus (status)); return status; } } for (va_start (ap, s), dtc = va_arg (ap, int); dtc != DATA_TYPE_EOL; dtc = va_arg (ap, int)) { DBG (DBG_proc, ">> get_hs2p_data 0x%2.2x\n", (int) dtc); switch (dtc) { case DATA_TYPE_GAMMA: buf = &(s->data.gamma[0]); *len = sizeof (s->data.gamma); break; case DATA_TYPE_ENDORSER: buf = &(s->data.endorser[0]); *len = sizeof (s->data.endorser); break; case DATA_TYPE_SIZE: buf = &(s->data.size); *len = sizeof (s->data.size); break; case DATA_TYPE_PAGE_LEN: buf = s->data.nlines; *len = sizeof (s->data.nlines); break; case DATA_TYPE_MAINTENANCE: buf = (SANE_Byte *) & (s->data.maintenance); *len = sizeof (s->data.maintenance); break; case DATA_TYPE_ADF_STATUS: buf = &(s->data.adf_status); *len = sizeof (s->data.adf_status); break; case DATA_TYPE_IMAGE: case DATA_TYPE_HALFTONE: default: DBG (DBG_info, "Data Type Code %2.2x not handled.\n", dtc); return SANE_STATUS_INVAL; } DBG (DBG_info, "get_hs2p_data calling read_data for dtc=%2.2x and bufsize=%lu\n", (int) dtc, (u_long) * len); status = read_data (s->fd, buf, len, (SANE_Byte) dtc, dtq); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "get_scanner_data: ERROR %s\n", sane_strstatus (status)); } } va_end (ap); if (fd < 0) { /* need to return fd to original state */ status = hs2p_close (s); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "get_hs2p_data: error closing fd: %s\n", sane_strstatus (status)); } } DBG (DBG_proc, "<< get_hs2p_data: %d\n", status); return (status); } static SANE_Status print_maintenance_data (MAINTENANCE_DATA * d) { DBG (DBG_proc, ">> print_maintenance_data: \n"); DBG (DBG_LEVEL, "nregx_adf = %d\n", d->nregx_adf); DBG (DBG_LEVEL, "nregy_adf = %d\n", d->nregy_adf); DBG (DBG_LEVEL, "nregx_book = %d\n", d->nregx_book); DBG (DBG_LEVEL, "nregy_book = %d\n", d->nregy_book); DBG (DBG_LEVEL, "nscans_adf = %lu\n", _4btol (&(d->nscans_adf[0]))); DBG (DBG_LEVEL, "nscans_adf = %lu\n", _4btol (&(d->nscans_adf[0]))); DBG (DBG_LEVEL, "lamp time = %lu\n", _4btol (&(d->lamp_time[0]))); DBG (DBG_LEVEL, "eo_odd = %d\n", d->eo_odd); DBG (DBG_LEVEL, "eo_even = %d\n", d->eo_even); DBG (DBG_LEVEL, "black_level_odd = %d\n", d->black_level_odd); DBG (DBG_LEVEL, "black_level_even = %d\n", d->black_level_even); DBG (DBG_LEVEL, "white_level_odd = %lu\n", _2btol (&(d->white_level_odd[0]))); DBG (DBG_LEVEL, "white_level_even = %lu\n", _2btol (&(d->white_level_even[0]))); DBG (DBG_LEVEL, "first_adj_white_odd = %lu\n", _2btol (&(d->first_adj_white_odd[0]))); DBG (DBG_LEVEL, "first_adj_white_even = %lu\n", _2btol (&(d->first_adj_white_even[0]))); DBG (DBG_LEVEL, "density_adj = %d\n", d->density_adj); DBG (DBG_LEVEL, "nregx_reverse = %d\n", d->nregx_reverse); DBG (DBG_LEVEL, "nregy_reverse = %d\n", d->nregy_reverse); DBG (DBG_LEVEL, "nscans_reverse_adf = %lu\n", _4btol (&(d->nscans_reverse_adf[0]))); DBG (DBG_LEVEL, "reverse_time = %lu\n", _4btol (&(d->reverse_time[0]))); DBG (DBG_LEVEL, "nchars = %lu\n", _4btol (&(d->nchars[0]))); DBG (DBG_proc, "<< print_maintenance_data: \n"); return SANE_STATUS_GOOD; } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { HS2P_Scanner *s = handle; SANE_Status status; SANE_Word cap; SANE_String_Const name; SANE_Int paper_id; name = s->opt[option].name ? s->opt[option].name : "(nil)"; if (info) *info = 0; DBG (DBG_proc, ">> sane_control_option: %s option=%d name=%s\n", action == SANE_ACTION_GET_VALUE ? "SET" : "GET", option, name); if (s->scanning) return (SANE_STATUS_DEVICE_BUSY); if (option >= NUM_OPTIONS) return (SANE_STATUS_INVAL); cap = s->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) return (SANE_STATUS_INVAL); if (action == SANE_ACTION_GET_VALUE) { DBG (DBG_proc, "sane_control_option get_value option=%d\n", option); switch (option) { /* word options: */ case OPT_RESOLUTION: case OPT_X_RESOLUTION: case OPT_Y_RESOLUTION: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_BRIGHTNESS: case OPT_THRESHOLD: case OPT_CONTRAST: case OPT_NUM_OPTS: *(SANE_Word *) val = s->val[option].w; return (SANE_STATUS_GOOD); /* bool options: */ /*case OPT_AUTOBORDER: case OPT_DESKEW: case OPT_CHECK_ADF: case OPT_BATCH: */ case OPT_PREVIEW: case OPT_SCAN_WAIT_MODE: case OPT_DUPLEX: case OPT_AUTO_SIZE: case OPT_NEGATIVE: case OPT_ENDORSER: case OPT_SMOOTHING: case OPT_WHITE_BALANCE: case OPT_PREFEED: case OPT_CUSTOM_GAMMA: case OPT_PADDING: *(SANE_Bool *) val = s->val[option].w; return (SANE_STATUS_GOOD); /* string options: */ /* case OPT_ADF: */ /* case OPT_BITORDER: */ /* case OPT_ROTATION */ /* case OPT_SECTION: */ case OPT_INQUIRY: case OPT_SCAN_SOURCE: case OPT_PAGE_ORIENTATION: case OPT_PAPER_SIZE: case OPT_SCAN_MODE: case OPT_ENDORSER_STRING: case OPT_COMPRESSION: case OPT_NOISEREMOVAL: case OPT_GRAYFILTER: case OPT_HALFTONE_CODE: case OPT_HALFTONE_PATTERN: case OPT_GAMMA: case OPT_AUTOSEP: case OPT_AUTOBIN: case OPT_PADDING_TYPE: DBG (DBG_proc, "STRING=%s\n", s->val[option].s); strcpy (val, s->val[option].s); return (SANE_STATUS_GOOD); DBG (DBG_proc, "sizeof(val)=%lu sizeof(s)=%lu\n", (u_long) sizeof (val), (u_long) sizeof (s->val[option].s)); return (SANE_STATUS_GOOD); /* gamma */ case OPT_GAMMA_VECTOR_GRAY: memcpy (val, s->val[option].wa, s->opt[option].size); return SANE_STATUS_GOOD; /* MAINTENANCE DATA */ case OPT_DATA_GROUP: case OPT_UPDATE: return SANE_STATUS_GOOD; case OPT_NREGX_ADF: DBG (DBG_sane_option, "OPT_NREGX_ADF\n"); *(SANE_Word *) val = (SANE_Word) s->data.maintenance.nregx_adf; return SANE_STATUS_GOOD; case OPT_NREGY_ADF: DBG (DBG_sane_option, "OPT_NREGY_ADF\n"); *(SANE_Word *) val = (SANE_Word) s->data.maintenance.nregx_book; return SANE_STATUS_GOOD; case OPT_NREGX_BOOK: DBG (DBG_sane_option, "OPT_NREGX_BOOK\n"); *(SANE_Word *) val = (SANE_Word) s->data.maintenance.nregx_book; return SANE_STATUS_GOOD; case OPT_NREGY_BOOK: DBG (DBG_sane_option, "OPT_NREGY_BOOK\n"); *(SANE_Word *) val = (SANE_Word) s->data.maintenance.nregy_book; return SANE_STATUS_GOOD; case OPT_NSCANS_ADF: DBG (DBG_sane_option, "OPT_NSCANS_ADF\n"); *(SANE_Word *) val = (SANE_Word) _4btol (&(s->data.maintenance.nscans_adf[0])); return SANE_STATUS_GOOD; case OPT_NSCANS_BOOK: DBG (DBG_sane_option, "OPT_NSCANS_BOOK\n"); *(SANE_Word *) val = (SANE_Word) _4btol (&(s->data.maintenance.nscans_book[0])); return SANE_STATUS_GOOD; case OPT_LAMP_TIME: DBG (DBG_sane_option, "OPT_LAMP_TIME\n"); *(SANE_Word *) val = (SANE_Word) _4btol (&(s->data.maintenance.lamp_time[0])); return SANE_STATUS_GOOD; case OPT_EO_ODD: DBG (DBG_sane_option, "OPT_EO_ODD\n"); *(SANE_Word *) val = (SANE_Word) s->data.maintenance.eo_odd; return SANE_STATUS_GOOD; case OPT_EO_EVEN: DBG (DBG_sane_option, "OPT_EO_EVEN\n"); *(SANE_Word *) val = (SANE_Word) s->data.maintenance.eo_even; return SANE_STATUS_GOOD; case OPT_BLACK_LEVEL_ODD: DBG (DBG_sane_option, "OPT_BLACK_LEVEL_ODD\n"); *(SANE_Word *) val = (SANE_Word) s->data.maintenance.black_level_odd; return SANE_STATUS_GOOD; case OPT_BLACK_LEVEL_EVEN: DBG (DBG_sane_option, "OPT_BLACK_LEVEL_EVEN\n"); *(SANE_Word *) val = (SANE_Word) s->data.maintenance.black_level_even; return SANE_STATUS_GOOD; case OPT_WHITE_LEVEL_ODD: DBG (DBG_sane_option, "OPT_WHITE_LEVEL_ODD\n"); *(SANE_Word *) val = (SANE_Word) _2btol (&(s->data.maintenance.white_level_odd[0])); return SANE_STATUS_GOOD; case OPT_WHITE_LEVEL_EVEN: DBG (DBG_sane_option, "OPT_WHITE_LEVEL_EVEN\n"); *(SANE_Word *) val = (SANE_Word) _2btol (&(s->data.maintenance.white_level_even[0])); return SANE_STATUS_GOOD; case OPT_FIRST_ADJ_WHITE_ODD: DBG (DBG_sane_option, "OPT_FIRST_ADJ_WHITE_ODD\n"); *(SANE_Word *) val = (SANE_Word) _2btol (&(s->data.maintenance.first_adj_white_odd[0])); return SANE_STATUS_GOOD; case OPT_FIRST_ADJ_WHITE_EVEN: DBG (DBG_sane_option, "OPT_FIRST_ADJ_WHITE_EVEN\n"); *(SANE_Word *) val = (SANE_Word) _2btol (&(s->data.maintenance.first_adj_white_even[0])); return SANE_STATUS_GOOD; case OPT_DENSITY: DBG (DBG_sane_option, "OPT_DENSITY\n"); *(SANE_Word *) val = (SANE_Word) s->data.maintenance.density_adj; return SANE_STATUS_GOOD; case OPT_NREGX_REVERSE: DBG (DBG_sane_option, "OPT_NREGX_REVERSE\n"); *(SANE_Word *) val = (SANE_Word) s->data.maintenance.nregx_reverse; return SANE_STATUS_GOOD; case OPT_NREGY_REVERSE: DBG (DBG_sane_option, "OPT_NREGY_REVERSE\n"); *(SANE_Word *) val = (SANE_Word) s->data.maintenance.nregy_reverse; return SANE_STATUS_GOOD; case OPT_NSCANS_REVERSE_ADF: DBG (DBG_sane_option, "OPT_NSCANS_REVERSE_ADF\n"); *(SANE_Word *) val = (SANE_Word) _4btol (&(s->data.maintenance.nscans_reverse_adf[0])); return SANE_STATUS_GOOD; case OPT_REVERSE_TIME: DBG (DBG_sane_option, "OPT_REVERSE_TIME\n"); *(SANE_Word *) val = (SANE_Word) _4btol (&(s->data.maintenance.reverse_time[0])); return SANE_STATUS_GOOD; case OPT_NCHARS: DBG (DBG_sane_option, "OPT_NCHARS\n"); *(SANE_Word *) val = (SANE_Word) _4btol (&(s->data.maintenance.nchars[0])); return (SANE_STATUS_GOOD); default: DBG (DBG_proc, "sane_control_option:invalid option number %d\n", option); return SANE_STATUS_INVAL; } } else if (action == SANE_ACTION_SET_VALUE) { DBG (DBG_proc, "sane_control_option set_value\n"); switch (s->opt[option].type) { case SANE_TYPE_BOOL: case SANE_TYPE_INT: DBG (DBG_proc, "sane_control_option: set_value %s [#%d] to %d\n", name, option, *(SANE_Word *) val); break; case SANE_TYPE_FIXED: DBG (DBG_proc, "sane_control_option: set_value %s [#%d] to %f\n", name, option, SANE_UNFIX (*(SANE_Word *) val)); break; case SANE_TYPE_STRING: DBG (DBG_proc, "sane_control_option: set_value %s [#%d] to %s\n", name, option, (char *) val); break; case SANE_TYPE_BUTTON: DBG (DBG_proc, "sane_control_option: set_value %s [#%d]\n", name, option); update_hs2p_data (s); break; default: DBG (DBG_proc, "sane_control_option: set_value %s [#%d]\n", name, option); } if (!SANE_OPTION_IS_SETTABLE (cap)) return (SANE_STATUS_INVAL); if ((status = sanei_constrain_value (s->opt + option, val, info)) != SANE_STATUS_GOOD) return status; switch (option) { /* (mostly) side-effect-free word options: */ case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: s->opt[OPT_AUTO_SIZE].cap |= SANE_CAP_INACTIVE; /* disable auto size */ /* make sure that paper-size is set to custom */ if (info && s->val[option].w != *(SANE_Word *) val) *info |= SANE_INFO_RELOAD_PARAMS; s->val[option].w = *(SANE_Word *) val; /* resets the paper format to user defined */ if (strcmp (s->val[OPT_PAPER_SIZE].s, paper_list[0]) != 0) /* CUSTOM PAPER SIZE */ { if (info) *info |= SANE_INFO_RELOAD_OPTIONS; if (s->val[OPT_PAPER_SIZE].s) free (s->val[OPT_PAPER_SIZE].s); s->val[OPT_PAPER_SIZE].s = strdup (paper_list[0]); /* CUSTOM PAPER SIZE */ } /* fall through */ case OPT_X_RESOLUTION: case OPT_Y_RESOLUTION: if (info && s->val[option].w != *(SANE_Word *) val) *info |= SANE_INFO_RELOAD_PARAMS; /* fall through */ /*case OPT_ACE_FUNCTION: case OPT_ACE_SENSITIVITY: */ case OPT_BRIGHTNESS: case OPT_THRESHOLD: case OPT_CONTRAST: case OPT_NUM_OPTS: s->val[option].w = *(SANE_Word *) val; return (SANE_STATUS_GOOD); /* string options */ case OPT_NOISEREMOVAL: case OPT_AUTOSEP: case OPT_AUTOBIN: case OPT_COMPRESSION: case OPT_PADDING_TYPE: case OPT_GRAYFILTER: case OPT_HALFTONE_CODE: case OPT_HALFTONE_PATTERN: case OPT_ENDORSER_STRING: if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); return SANE_STATUS_GOOD; /* boolean options: */ case OPT_PREVIEW: case OPT_DUPLEX: case OPT_NEGATIVE: case OPT_SCAN_WAIT_MODE: case OPT_ENDORSER: case OPT_SMOOTHING: case OPT_WHITE_BALANCE: case OPT_PREFEED: case OPT_PADDING: s->val[option].w = *(SANE_Word *) val; return SANE_STATUS_GOOD; case OPT_GAMMA_VECTOR_GRAY: memcpy (s->val[option].wa, val, s->opt[option].size); return SANE_STATUS_GOOD; /* options with side effect */ case OPT_GAMMA: if (strcmp (s->val[option].s, (SANE_String) val)) { if (!strcmp ((SANE_String) val, "User")) { s->val[OPT_CUSTOM_GAMMA].b = SANE_TRUE; s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE; /* Brightness and Contrast do not work when downloading Gamma Table */ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; } else { s->val[OPT_CUSTOM_GAMMA].b = SANE_FALSE; s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE; } if (info) *info |= SANE_INFO_RELOAD_OPTIONS; } if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); return SANE_STATUS_GOOD; case OPT_CUSTOM_GAMMA: s->val[OPT_CUSTOM_GAMMA].w = *(SANE_Word *) val; if (s->val[OPT_CUSTOM_GAMMA].w) { s->opt[OPT_GAMMA_VECTOR_GRAY].cap &= ~SANE_CAP_INACTIVE; } else { s->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE; } if (info) *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_RESOLUTION: if (info && s->val[option].w != *(SANE_Word *) val) *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; s->val[option].w = *(SANE_Word *) val; s->val[OPT_X_RESOLUTION].w = *(SANE_Word *) val; s->val[OPT_Y_RESOLUTION].w = *(SANE_Word *) val; return SANE_STATUS_GOOD; case OPT_SCAN_SOURCE: /* a string option */ /* Since the scanner ejects the sheet in ADF mode * it is impossible to scan multiple sections in one document * In ADF mode, because of mechanical limitations: * the minimum document size is (x,y)=(69mm x 120mm) */ if (info && strcmp ((char *) s->val[option].s, (char *) val)) *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); if (!strcmp ("ADF", (SANE_String) val)) { s->opt[OPT_ENDORSER].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_ENDORSER_STRING].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_PREFEED].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_DUPLEX].cap &= ~SANE_CAP_INACTIVE; /*s->opt[OPT_PADDING].cap &= ~SANE_CAP_INACTIVE; */ } else { /* Flatbed */ s->opt[OPT_ENDORSER].cap |= SANE_CAP_INACTIVE; s->opt[OPT_ENDORSER_STRING].cap |= SANE_CAP_INACTIVE; s->opt[OPT_PREFEED].cap |= SANE_CAP_INACTIVE; s->opt[OPT_DUPLEX].cap |= SANE_CAP_INACTIVE; s->opt[OPT_PADDING].cap |= SANE_CAP_INACTIVE; } return SANE_STATUS_GOOD; case OPT_SCAN_MODE: /* a string option */ /* scan mode != lineart disables compression, setting it to 'none' */ if (strcmp (s->val[option].s, (SANE_String) val)) { if (info) *info |= SANE_INFO_RELOAD_OPTIONS; if (!strcmp (SM_LINEART, (SANE_String) val)) { s->image_composition = LINEART; s->opt[OPT_COMPRESSION].cap &= ~SANE_CAP_INACTIVE; /* enable compression control */ s->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE; /* enable threshold control */ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; /* disable brightness control */ s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; /* disable contrast control */ s->opt[OPT_GAMMA].cap |= SANE_CAP_INACTIVE; /* disable gamma */ s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; /* disable gamma */ s->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE; /* disable gamma */ s->opt[OPT_HALFTONE_CODE].cap |= SANE_CAP_INACTIVE; /* disable halftone code */ s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; /* disable halftone pattern */ } else { if (!strcmp (SM_HALFTONE, (SANE_String) val)) { s->image_composition = HALFTONE; s->opt[OPT_HALFTONE_CODE].cap &= ~SANE_CAP_INACTIVE; /* enable halftone code */ s->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE; /* enable halftone pattern */ } else if (!strcmp (SM_4BITGRAY, (SANE_String) val) || !strcmp (SM_6BITGRAY, (SANE_String) val) || !strcmp (SM_8BITGRAY, (SANE_String) val)) { s->image_composition = GRAYSCALE; s->opt[OPT_GAMMA].cap &= ~SANE_CAP_INACTIVE; /* enable gamma */ s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; /* enable brightness */ s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE; /* enable contrast */ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; /* disable threshold */ s->opt[OPT_COMPRESSION].cap |= SANE_CAP_INACTIVE; /* disable compression */ s->opt[OPT_HALFTONE_CODE].cap |= SANE_CAP_INACTIVE; /* disable halftone code */ s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; /* disable halftone pattern */ if (s->val[OPT_COMPRESSION].s && get_compression_id (s->val[OPT_COMPRESSION].s) != 0) { free (s->val[OPT_COMPRESSION].s); s->val[OPT_COMPRESSION].s = strdup (compression_list[0]); } } } free (s->val[option].s); s->val[option].s = strdup (val); } return SANE_STATUS_GOOD; case OPT_PAGE_ORIENTATION: if (strcmp (s->val[option].s, (SANE_String) val)) { free (s->val[option].s); s->val[option].s = strdup (val); if (info) *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; } /* set val to current selected paper size */ paper_id = get_paper_id ((SANE_String) s->val[OPT_PAPER_SIZE].s); goto paper_id; case OPT_PAPER_SIZE: /* a string option */ /* changes geometry options, therefore _RELOAD_PARAMS and _RELOAD_OPTIONS */ s->opt[OPT_AUTO_SIZE].cap |= SANE_CAP_INACTIVE; /* disable auto size */ if (strcmp (s->val[option].s, (SANE_String) val)) { paper_id = get_paper_id ((SANE_String) val); /* paper_id 0 is a special case (custom) that * disables the paper size control of geometry */ paper_id: if (paper_id != 0) { double x_max, y_max, x, y, temp; x_max = SANE_UNFIX (s->hw->info.x_range.max); y_max = SANE_UNFIX (s->hw->info.y_range.max); /* a dimension of 0.0 (or less) is replaced with the max value */ x = (paper_sizes[paper_id].width <= 0.0) ? x_max : paper_sizes[paper_id].width; y = (paper_sizes[paper_id].length <= 0.0) ? y_max : paper_sizes[paper_id].length; if (info) *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; if (!strcmp (s->val[OPT_PAGE_ORIENTATION].s, LANDSCAPE)) /* swap */ { temp = y_max; y_max = x_max; x_max = temp; temp = y; y = x; x = temp; } s->val[OPT_TL_X].w = SANE_FIX (0.0); s->val[OPT_TL_Y].w = SANE_FIX (0.0); s->val[OPT_BR_X].w = SANE_FIX (MIN (x, x_max)); s->val[OPT_BR_Y].w = SANE_FIX (MIN (y, y_max)); } free (s->val[option].s); s->val[option].s = strdup (val); } return SANE_STATUS_GOOD; case OPT_UPDATE: /* SANE_TYPE_BUTTON */ DBG (DBG_info, "OPT_UPDATE: ready to call get_hs2p_data: fd=%d\n", s->fd); get_hs2p_data (s, /* DATA_TYPE_GAMMA, */ /* DATA_TYPE_ENDORSER, */ /* DATA_TYPE_SIZE, */ /* DATA_TYPE_PAGE_LEN, */ DATA_TYPE_MAINTENANCE, /* DATA_TYPE_ADF_STATUS, */ /* DATA_TYPE_IMAGE, */ /* DATA_TYPE_HALFTONE, */ DATA_TYPE_EOL); /* va_list end */ update_hs2p_data (s); if (DBG_LEVEL >= DBG_info) print_maintenance_data (&(s->data.maintenance)); if (info) *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; } return (SANE_STATUS_GOOD); } DBG (DBG_proc, "<< sane_control_option\n"); return (SANE_STATUS_INVAL); } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { HS2P_Scanner *s = handle; DBG (DBG_proc, ">> sane_get_parameters\n"); if (!s->scanning) { int width, length, xres, yres; const char *mode; memset (&s->params, 0, sizeof (s->params)); /* CLEAR SANE_Parameters */ width = (int) (SANE_UNFIX (s->val[OPT_BR_X].w) - SANE_UNFIX (s->val[OPT_TL_X].w)); length = (int) (SANE_UNFIX (s->val[OPT_BR_Y].w) - SANE_UNFIX (s->val[OPT_TL_Y].w)); xres = s->val[OPT_X_RESOLUTION].w; yres = s->val[OPT_Y_RESOLUTION].w; DBG (DBG_proc, ">>sane_get_parameters: (W/L)=(%d/%d) (xres/yres)=(%d/%d) mud=%d\n", width, length, xres, yres, s->hw->info.mud); /* make best-effort guess at what parameters will look like once scanning starts. */ if (xres > 0 && yres > 0 && width > 0 && length > 0) { /* convert from mm to pixels */ s->params.pixels_per_line = width * xres / s->hw->info.mud / MM_PER_INCH; s->params.lines = length * yres / s->hw->info.mud / MM_PER_INCH; } mode = s->val[OPT_SCAN_MODE].s; if ((strcmp (mode, SM_LINEART) == 0) || (strcmp (mode, SM_HALFTONE)) == 0) { s->params.format = SANE_FRAME_GRAY; s->params.bytes_per_line = s->params.pixels_per_line / 8; /* if the scanner truncates to the byte boundary, so: chop! */ s->params.pixels_per_line = s->params.bytes_per_line * 8; s->params.depth = 1; } else /* if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) */ { s->params.format = SANE_FRAME_GRAY; s->params.bytes_per_line = s->params.pixels_per_line; s->params.depth = 8; } s->params.last_frame = SANE_TRUE; } else DBG (DBG_proc, "sane_get_parameters: scanning, so can't get params\n"); if (params) *params = s->params; DBG (DBG_proc, "%d pixels per line, %d bytes per line, %d lines high, total %lu bytes, " "dpi=%ld\n", s->params.pixels_per_line, s->params.bytes_per_line, s->params.lines, (u_long) s->bytes_to_read, (long) SANE_UNFIX (s->val[OPT_Y_RESOLUTION].w)); DBG (DBG_proc, "<< sane_get_parameters\n"); return (SANE_STATUS_GOOD); } static SANE_Status set_window_data (HS2P_Scanner * s, SWD * wbuf) { struct hs2p_window_data *data; int i, nwin, id, xres, yres, xmax, ymax; long ulx, uly, width, length, number, bytes; double offset; DBG (DBG_proc, ">> set_window_data: sizeof(*wbuf)=%lu; window len=%lu\n", (u_long) sizeof (*wbuf), (u_long) sizeof (wbuf->data)); /* initialize our window buffer with zeros */ DBG (DBG_proc, ">> set_window_data: CLEARING wbuf\n"); memset (wbuf, 0, sizeof (*wbuf)); /* Header */ DBG (DBG_proc, ">> set_window_data: writing Window Descriptor Length =%lu\n", (u_long) sizeof (wbuf->data)); _lto2b (sizeof (wbuf->data), &wbuf->hdr.len[0]); /* X-Axis Resolution 100-800dpi in 1 dpi steps */ xres = s->val[OPT_X_RESOLUTION].w; if (xres < s->hw->info.resMinX || xres > s->hw->info.resMaxX) { DBG (DBG_error, "XRESOLUTION %d IS NOT WITHIN [%d, %d]\n", xres, s->hw->info.resMinX, s->hw->info.resMaxX); return (SANE_STATUS_INVAL); } /* Y-Axis Resolution 100-800dpi in 1 dpi steps */ yres = s->val[OPT_Y_RESOLUTION].w; if (yres < s->hw->info.resMinY || yres > s->hw->info.resMaxY) { DBG (DBG_error, "YRESOLUTION %d IS NOT WITHIN [%d, %d]\n", yres, s->hw->info.resMinY, s->hw->info.resMaxY); return (SANE_STATUS_INVAL); } ulx = (long) SANE_UNFIX (s->val[OPT_TL_X].w); uly = (long) SANE_UNFIX (s->val[OPT_TL_Y].w); DBG (DBG_info, "set_window_data: upperleft=(%ld,%ld)\n", ulx, uly); width = (long) SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w); /* Window Width */ length = (long) SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w); /* Window Length */ DBG (DBG_info, "set_window_data: WxL= %ld x %ld\n", width, length); /* NOTE: the width in inches converted to byte unit must be the following values or less * Binary: 620 bytes * 4-bits gray: 2480 bytes * 8-bits gray: 4960 bytes */ if (!strcmp (s->val[OPT_SCAN_MODE].s, SM_LINEART)) { bytes = (width / MM_PER_INCH) * (s->val[OPT_X_RESOLUTION].w / 8.0); if (bytes > 620) { DBG (DBG_error, "width in pixels too large: width=%ld x-resolution=%d bytes=%ld\n", width, s->val[OPT_X_RESOLUTION].w, bytes); return (SANE_STATUS_INVAL); } } else if (!strcmp (s->val[OPT_SCAN_MODE].s, SM_4BITGRAY)) { bytes = (width / MM_PER_INCH) * (s->val[OPT_X_RESOLUTION].w / 2.0); if (bytes > 2480) { DBG (DBG_error, "width in pixels too large: width=%ld x-resolution=%d bytes=%ld\n", width, s->val[OPT_X_RESOLUTION].w, bytes); return (SANE_STATUS_INVAL); } } else if (!strcmp (s->val[OPT_SCAN_MODE].s, SM_8BITGRAY)) { bytes = (width / MM_PER_INCH) * (s->val[OPT_X_RESOLUTION].w); if (bytes > 4960) { DBG (DBG_error, "width in pixels too large: width=%ld x-resolution=%d bytes=%ld\n", width, s->val[OPT_X_RESOLUTION].w, bytes); return (SANE_STATUS_INVAL); } } if (strcmp (s->val[OPT_SCAN_SOURCE].s, scan_source_list[ADF]) == 0) { offset = (SANE_UNFIX (s->hw->info.x_range.max) - width) / 2.0; DBG (DBG_info, "set_window_data: ADF origin offset=%f\n", offset); ulx += (long) offset; } if (strcmp (s->val[OPT_SCAN_SOURCE].s, scan_source_list[FB]) == 0) { /* FB */ xmax = 298; /*mm */ ymax = 432; } else { /* ADF */ xmax = 298; ymax = 2000; } /* Boundary Conditions when BMU = MM */ number = ulx + width; if (number <= 0 || number > xmax) { DBG (DBG_error, "NOT WITHIN BOUNDS: ulx=%ld width=%ld sum=%ld\n", ulx, width, number); return (SANE_STATUS_INVAL); } number = uly + length; if (number <= 0 || number > ymax) { DBG (DBG_error, "NOT WITHIN BOUNDS: uly=%ld length=%ld sum=%ld\n", uly, length, number); return (SANE_STATUS_INVAL); } /* For each window (up to 2 if we're duplexing) */ nwin = (s->val[OPT_DUPLEX].w == SANE_TRUE) ? 2 : 1; for (i = 0; i < nwin; i++) { data = &(wbuf->data[i]); data->window_id = i; data->auto_bit &= 0xFE; /* Auto bit set to 0 since auto function isn't supported */ _lto2b (xres, &data->xres[0]); /* Set X resolution */ _lto2b (yres, &data->yres[0]); /* Set Y resolution */ _lto4b (ulx, &data->ulx[0]); /* X-Axis Upper Left */ _lto4b (uly, &data->uly[0]); /* Y-Axis Upper Left */ _lto4b (width, &data->width[0]); /* Window Width */ _lto4b (length, &data->length[0]); /* Window Length */ data->brightness = s->val[OPT_BRIGHTNESS].w; /* black-white: 1-255; 0 is default 128 */ data->threshold = s->val[OPT_THRESHOLD].w; /* light-dark: 1-255; 0 is default 128 */ data->contrast = s->val[OPT_CONTRAST].w; /* low-high: 1-255: 0 is default 128 */ if (data->brightness == 128) data->brightness = 0; if (data->threshold == 128) data->threshold = 0; if (data->contrast == 128) data->contrast = 0; data->image_composition = s->image_composition; data->bpp = s->bpp = s->params.depth; /* Byte 27, 347 Halftone Code: if HALFTONE, then either DITHER or ERROR_DIFFUSION */ if (s->image_composition == HALFTONE) { /* Then let's use pattern selected by user */ data->halftone_code = (get_halftone_code_id (s->val[OPT_HALFTONE_CODE].s) == 0) ? DITHER : ERROR_DIFFUSION; data->halftone_id = get_halftone_pattern_val (s->val[OPT_HALFTONE_PATTERN].s); } else { data->halftone_code = DITHER; /* 00H reserved */ data->halftone_id = 0x01; /* 00H reserved */ } /* Byte 29, 349: RIF:reserved:padding type */ if (data->image_composition == LINEART || data->image_composition == HALFTONE) { if (s->val[OPT_NEGATIVE].w) data->byte29 |= (1 << 7); /* set bit 7 */ else data->byte29 &= ~(1 << 7); /* unset bit 7 */ } /* Padding Type */ data->byte29 |= (paddingtype[get_paddingtype_id (s->val[OPT_PADDING_TYPE].s)]. val & 0x07); /* Bit Ordering: * Manual Says DEFAULT: [1111 1111][1111 1000] * Bits15-8 reserved; * Bit7: '0'-Normal '1'-Mirroring * Bit6-4: Reserved * Bit3: '0'-arrangement from MSB in grayscale mode * '1'-arrangement from LSB in grayscale mode * 2: '0'-unpacked 4-bits grayscale [DEFAULT] * '1'-packed 4-bits grayscale * 1: '0'-output from LSB of each word [DEFAULT] * '1'-output from MSB of each word * 0: '0'-output from bit 0 of each byte [DEFAULT] * '1'-output from bit 7 of each byte */ _lto2b (0x007, &data->bit_ordering[0]); /* Set to Packed4bitGray, MSB, MSbit */ /* Compression Type and Argument NOT SUPPORTED in this scanner */ data->compression_type = 0x00; data->compression_arg = 0x02; /* Byte42: MRIF:Filtering:GammaID */ if (data->image_composition == GRAYSCALE) { if (s->val[OPT_NEGATIVE].w) data->byte42 &= ~(1 << 7); /* unset bit 7 */ else data->byte42 |= (1 << 7); /* set bit 7 */ data->byte42 |= (get_grayfilter_val (s->val[OPT_GRAYFILTER].s) & (7 << 4)); /* set bits 6-4 to GRAYFILTER */ } else { data->byte42 &= ~(1 << 7); /* unset bit 7 */ data->byte42 &= ~(7 << 4); /* unset bits 6-4 */ } /* Bytes 45, 365 Binary Filtering for lineart and halftone can be set when option IPU is installed */ if ((id = get_noisematrix_id (s->val[OPT_NOISEREMOVAL].s)) != 0) { data->binary_filtering |= (1 << 7); /* set bit 7 */ data->binary_filtering |= noisematrix[id].val; /* 00H, 01H, 02H; 03H:Reserved */ } if (s->val[OPT_SMOOTHING].w == SANE_TRUE) data->binary_filtering |= (1 << 6); /* set bit 6 */ /* Automatic separation, automatic binarization, and SECTION is available if Image Processing Unit is installed */ if (s->hw->info.hasIPU) { /* Byte 48: Automatic Separation */ data->automatic_separation = get_auto_separation_val (s->val[OPT_AUTOSEP].s); /* Byte 50: Automatic Binarization */ data->automatic_binarization = get_auto_binarization_val (s->val[OPT_AUTOBIN].s); /* fill in values for each section for(j=0; j> sane_start\n"); s->cancelled = SANE_FALSE; if (s->another_side) { /* Number of bytes to read for one side of sheet */ s->bytes_to_read = s->params.bytes_per_line * s->params.lines; DBG (DBG_info, "SIDE#2 %d pixels per line, %d bytes, %d lines high, dpi=%d\n", s->params.pixels_per_line, s->params.bytes_per_line, s->params.lines, (int) s->val[OPT_Y_RESOLUTION].w); s->scanning = SANE_TRUE; s->cancelled = SANE_FALSE; s->another_side = SANE_FALSE; /* This is side 2, so no more sides */ DBG (DBG_proc, "<< sane_start\n"); return (SANE_STATUS_GOOD); } if (s->scanning) { DBG (DBG_info, "sane_start: device busy\n"); return SANE_STATUS_DEVICE_BUSY; } /* Let's start a new scan */ if ((status = sane_get_parameters (s, 0)) != SANE_STATUS_GOOD) { /* get preliminary parameters */ DBG (DBG_error, "sane_start: sane_get_parameters failed: %s\n", sane_strstatus (status)); return (status); } DBG (DBG_info, ">> sane_start: trying to open: name=\"%s\" fd=%d\n", s->hw->sane.name, s->fd); if ((status = sanei_scsi_open (s->hw->sane.name, &s->fd, &sense_handler, &(s->hw->sense_data))) != SANE_STATUS_GOOD) { DBG (DBG_error, "sane_start: open of %s failed: %d %s\n", s->hw->sane.name, status, sane_strstatus (status)); return (status); } DBG (DBG_info, ">>sane_start: OPENED \"%s\" fd=%d\n", s->hw->sane.name, s->fd); if ((status = test_unit_ready (s->fd)) != SANE_STATUS_GOOD) { DBG (DBG_error, "sane_start: test_unit_ready() failed: %s\n", sane_strstatus (status)); sanei_scsi_close (s->fd); s->fd = -1; return status; } if ((status = reserve_unit (s->fd)) != SANE_STATUS_GOOD) { DBG (DBG_error, "sane_start: reserve_unit() failed: %s\n", sane_strstatus (status)); sanei_scsi_close (s->fd); s->fd = -1; return (status); } /* NOW SET UP SCANNER ONCE PER BATCH */ DBG (DBG_info, "sane_start: setting basic measurement unit to mm\n"); if ((status = set_basic_measurement_unit (s->fd, s->hw->info.bmu))) { DBG (DBG_error, "set_basic_measurment_unit failed: %s\n", sane_strstatus (status)); release_unit (s->fd); sanei_scsi_close (s->fd); s->fd = -1; return (status); } if (get_scan_source_id (s->val[OPT_SCAN_SOURCE].s) == 0) { mode = FLATBED; } else { mode = (s->val[OPT_DUPLEX].w) ? DUPLEX : SIMPLEX; } prefeed = s->val[OPT_PREFEED].w ? 0x04 : 0x00; DBG (DBG_info, "sane_start: setting scan source to %d %s\n", mode, (SANE_String) s->val[OPT_SCAN_SOURCE].s); DBG (DBG_info, "sane_start: setting prefeed to %d\n", prefeed); if ((status = set_adf_control (s->fd, &mode, &prefeed, &mwt)) != SANE_STATUS_GOOD) { DBG (DBG_error, "sane_start: error set_adf_control: %s\n", sane_strstatus (status)); release_unit (s->fd); sanei_scsi_close (s->fd); s->fd = -1; return (SANE_STATUS_INVAL); } DBG (DBG_info, "sane_start: setting endorser control to %d\n", s->val[OPT_ENDORSER].w); if ((status = set_endorser_control (s->fd, &s->val[OPT_ENDORSER].w)) != SANE_STATUS_GOOD) { DBG (DBG_error, "set_endorser_control failed: %s\n", sane_strstatus (status)); release_unit (s->fd); sanei_scsi_close (s->fd); s->fd = -1; return (status); } if (s->val[OPT_ENDORSER].w) { DBG (DBG_info, "sane_start: setting endorser string to %s\n", s->val[OPT_ENDORSER_STRING].s); if ((status = set_endorser_string (s->fd, (SANE_String) s->val[OPT_ENDORSER_STRING]. s)) != SANE_STATUS_GOOD) { DBG (DBG_error, "set_endorser_string failed: %s\n", sane_strstatus (status)); release_unit (s->fd); sanei_scsi_close (s->fd); s->fd = -1; return (status); } } DBG (DBG_info, "sane_start: setting scan_wait_mode to %d\n", s->val[OPT_SCAN_WAIT_MODE].w); if ((status = set_scan_wait_mode (s->fd, s->val[OPT_SCAN_WAIT_MODE].w)) != SANE_STATUS_GOOD) { DBG (DBG_error, "set_scan_wait_mode failed: %s\n", sane_strstatus (status)); release_unit (s->fd); sanei_scsi_close (s->fd); s->fd = -1; return (status); } DBG (DBG_info, "sane_start: setting white_balance to %d\n", s->val[OPT_WHITE_BALANCE].w); if ((status = set_white_balance (s->fd, &s->val[OPT_WHITE_BALANCE].w)) != SANE_STATUS_GOOD) { DBG (DBG_error, "set_white_balance failed: %s\n", sane_strstatus (status)); release_unit (s->fd); sanei_scsi_close (s->fd); s->fd = -1; return (status); } if (s->val[OPT_CUSTOM_GAMMA].b) { /* Custom Gamma needs to be sent to scanner */ DBG (DBG_info, "sane_start: setting custom gamma\n"); if ((status = hs2p_send_gamma (s))) { DBG (DBG_error, "hs2p_send_gamma failed: %s\n", sane_strstatus (status)); release_unit (s->fd); sanei_scsi_close (s->fd); s->fd = -1; return (status); } /* We succeeded, so we don't need to upload this vector again (unless user modifies gamma table) */ s->val[OPT_CUSTOM_GAMMA].b = SANE_FALSE; } DBG (DBG_info, "sane_start: filling in window data buffer \n"); if ((status = set_window_data (s, &wbuf)) != SANE_STATUS_GOOD) { DBG (DBG_error, "set_window_data failed: %s\n", sane_strstatus (status)); release_unit (s->fd); sanei_scsi_close (s->fd); s->fd = -1; return (status); } DBG (DBG_info, "sane_start: sending SET WINDOW DATA\n"); if ((status = set_window (s->fd, &wbuf)) != SANE_STATUS_GOOD) { DBG (DBG_error, "SET WINDOW DATA failed: %s\n", sane_strstatus (status)); print_window_data (&wbuf); release_unit (s->fd); sanei_scsi_close (s->fd); s->fd = -1; return (status); } DBG (DBG_info, "sane_start: sending GET WINDOW\n"); memset (&gbuf, 0, sizeof (gbuf)); /* CLEAR wbuf */ if ((status = get_window (s->fd, &gbuf)) != SANE_STATUS_GOOD) { DBG (DBG_error, "GET WINDOW failed: %s\n", sane_strstatus (status)); release_unit (s->fd); sanei_scsi_close (s->fd); s->fd = -1; return (status); } /* DONE WITH SETTING UP SCANNER ONCE PER BATCH */ s->EOM = SANE_FALSE; if (mode != FLATBED) { if ((status = get_hs2p_data (s, DATA_TYPE_ADF_STATUS, DATA_TYPE_EOL)) != SANE_STATUS_GOOD) { DBG (DBG_error, "sane_start: error reading adf_status: %s\n", sane_strstatus (status)); return (status); } if ((s->data.adf_status & 0x01) == 0x01) { DBG (DBG_warning, "sane_start: No document on ADF\n"); return (SANE_STATUS_NO_DOCS); } else if ((s->data.adf_status & 0x02) == 0x02) { DBG (DBG_warning, "sane_start: ADF cover open!\n"); return (SANE_STATUS_COVER_OPEN); } } status = trigger_scan (s); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "start of scan failed: %s\n", sane_strstatus (status)); print_window_data (&wbuf); /* this line introduced not to freeze xscanimage */ /*do_cancel (s); */ return status; } /* Wait for scanner to become ready to transmit data */ status = hs2p_wait_ready (s); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "GET DATA STATUS failed: %s\n", sane_strstatus (status)); return (status); } s->another_side = (mode == DUPLEX) ? SANE_TRUE : SANE_FALSE; /* Number of bytes to read for one side of sheet */ DBG (DBG_info, "ANOTHER SIDE = %s\n", (s->another_side) ? "TRUE" : "FALSE"); s->bytes_to_read = s->params.bytes_per_line * s->params.lines; DBG (DBG_info, "%d pixels per line, %d bytes, %d lines high, dpi=%d\n", s->params.pixels_per_line, s->params.bytes_per_line, s->params.lines, (int) s->val[OPT_Y_RESOLUTION].w); s->scanning = SANE_TRUE; s->cancelled = SANE_FALSE; DBG (DBG_proc, "<< sane_start\n"); return (SANE_STATUS_GOOD); } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { HS2P_Scanner *s = handle; SANE_Status status; size_t nread, bytes_requested, i, start; SANE_Byte color; DBG (DBG_proc, ">> sane_read\n"); *len = 0; DBG (DBG_info, "sane_read: bytes left to read: %ld\n", (u_long) s->bytes_to_read); if (s->bytes_to_read == 0) { /* We've reached the end of one side of sheet */ if (!s->another_side) { do_cancel (s); return (SANE_STATUS_EOF); } else { /* let frontend call sane_start again to reset bytes_to_read */ DBG (DBG_proc, "<< sane_read: getting another side\n"); return (SANE_STATUS_EOF); } } if (s->cancelled) { DBG (DBG_info, "sane_read: cancelled!\n"); return SANE_STATUS_CANCELLED; } if (!s->scanning) { DBG (DBG_info, "sane_read: scanning is false!\n"); return (do_cancel (s)); } nread = max_len; if (nread > s->bytes_to_read) nread = s->bytes_to_read; bytes_requested = nread; start = 0; pad: if (s->EOM) { if (s->val[OPT_PADDING].w) { DBG (DBG_info, "sane_read s->EOM padding from %ld to %ld\n", (u_long) start, (u_long) bytes_requested); color = (s->val[OPT_NEGATIVE].w) ? 0 : 255; /* pad to requested length */ for (i = start; i < bytes_requested; i++) buf[i] = color; nread = bytes_requested; /* we've padded to bytes_requested */ *len = nread; s->bytes_to_read -= nread; } else /* TRUNCATE: should never reach here */ { *len = nread; s->bytes_to_read = 0; /* EOM */ } } else { DBG (DBG_info, "sane_read: trying to read %ld bytes\n", (u_long) nread); status = read_data (s->fd, buf, &nread, DATA_TYPE_IMAGE, DTQ); switch (status) { case SANE_STATUS_NO_DOCS: DBG (DBG_error, "sane_read: End-Of-Medium detected\n"); s->EOM = SANE_TRUE; /* * If status != SANE_STATUS_GOOD, then sense_handler() has already * been called and the sanei_* functions have already gotten the * sense data buffer (which apparently clears the error condition) * so the following doesn't work: get_sense_data (s->fd, &(s->hw->sense_data)); print_sense_data (&(s->hw->sense_data)); */ start = (isset_ILI (s->hw->sense_data)) ? /* Invalid Length Indicator */ bytes_requested - _4btol (s->hw->sense_data.information) : nread; goto pad; break; case SANE_STATUS_GOOD: *len = nread; s->bytes_to_read -= nread; break; default: DBG (DBG_error, "sane_read: read error\n"); do_cancel (s); return (SANE_STATUS_IO_ERROR); } } DBG (DBG_proc, "<< sane_read\n"); return (SANE_STATUS_GOOD); } void sane_cancel (SANE_Handle handle) { HS2P_Scanner *s = handle; DBG (DBG_proc, ">> sane_cancel\n"); if (s->scanning) { /* if batchmode is enabled, then call set_window to abort the batch if (_OPT_VAL_WORD(s, OPT_BATCH) == SANE_TRUE) { DBG(5, "sane_cancel: calling set_window to abort batch\n"); set_window(s, BH_BATCH_ABORT); } */ do_cancel (s); } DBG (DBG_proc, "<< sane_cancel\n"); } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { DBG (DBG_proc, ">> sane_set_io_mode (handle = %p, non_blocking = %d)\n", handle, non_blocking); DBG (DBG_proc, "<< sane_set_io_mode\n"); return SANE_STATUS_UNSUPPORTED; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { #ifdef NONBLOCKSUPPORTED HS2P_Scanner *s = handle; #endif DBG (DBG_proc, ">> sane_get_select_fd (handle = %p, fd = %p)\n", handle, (void *) fd); #ifdef NONBLOCKSUPPORTED if (s->fd < 0) { DBG (DBG_proc, "<< sane_get_select_fd\n"); return SANE_STATUS_INVAL; } *fd = s->fd; return SANE_STATUS_GOOD; #else (void) handle; (void) fd; /* get rid of compiler warning */ DBG (DBG_proc, "<< sane_get_select_fd\n"); return SANE_STATUS_UNSUPPORTED; #endif } backends-1.3.0/backend/hs2p.conf.in000066400000000000000000000000301456256263500170130ustar00rootroot00000000000000scsi RICOH /dev/scanner backends-1.3.0/backend/hs2p.h000066400000000000000000000365371456256263500157350ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2007 Jeremy Johnson This file is part of a SANE backend for Ricoh IS450 and IS420 family of HS2P Scanners using the SCSI controller. 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, see . 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 HS2P_H #define HS2P_H 1 #include #include "../include/sane/config.h" #include "hs2p-scsi.h" #include "hs2p-saneopts.h" #define HS2P_CONFIG_FILE "hs2p.conf" #define DBG_error0 0 #define DBG_error 1 #define DBG_sense 2 #define DBG_warning 3 #define DBG_inquiry 4 #define DBG_info 5 #define DBG_info2 6 #define DBG_proc 7 #define DBG_read 8 #define DBG_sane_init 10 #define DBG_sane_proc 11 #define DBG_sane_info 12 #define DBG_sane_option 13 typedef struct { const char *mfg; const char *model; } HS2P_HWEntry; enum CONNECTION_TYPES { CONNECTION_SCSI = 0, CONNECTION_USB }; enum media { FLATBED = 0x00, SIMPLEX, DUPLEX }; typedef struct data { size_t bufsize; /* 00H IMAGE */ /* 01H RESERVED */ /* 02H Halftone Mask */ SANE_Byte gamma[256]; /* 03H Gamma Function */ /* 04H - 7FH Reserved */ SANE_Byte endorser[19]; /* 80H Endorser */ SANE_Byte size; /* 81H startpos(4bits) + width(4bits) */ /* 82H Reserved */ /* 83H Reserved (Vendor Unique) */ SANE_Byte nlines[5]; /* 84H Page Length */ MAINTENANCE_DATA maintenance; /* 85H */ SANE_Byte adf_status; /* 86H */ /* 87H Reserved (Skew Data) */ /* 88H-91H Reserved (Vendor Unique) */ /* 92H Reserved (Scanner Extension I/O Access) */ /* 93H Reserved (Vendor Unique) */ /* 94H-FFH Reserved (Vendor Unique) */ } HS2P_DATA; typedef struct { SANE_Range xres_range; SANE_Range yres_range; SANE_Range x_range; SANE_Range y_range; SANE_Int window_width; SANE_Int window_height; SANE_Range brightness_range; SANE_Range contrast_range; SANE_Range threshold_range; char inquiry_data[256]; SANE_Byte max_win_sections; /* Number of supported window subsections IS450 supports max of 4 sections IS420 supports max of 6 sections */ /* Defaults */ SANE_Int default_res; SANE_Int default_xres; SANE_Int default_yres; SANE_Int default_imagecomposition; /* [lineart], halftone, grayscale, color */ SANE_Int default_media; /* [flatbed], simplex, duplex */ SANE_Int default_paper_size; /* [letter], legal, ledger, ... */ SANE_Int default_brightness; SANE_Int default_contrast; SANE_Int default_gamma; /* Normal, Soft, Sharp, Linear, User */ SANE_Bool default_adf; SANE_Bool default_duplex; /* SANE_Bool default_border; SANE_Bool default_batch; SANE_Bool default_deskew; SANE_Bool default_check_adf; SANE_Int default_timeout_adf; SANE_Int default_timeout_manual; SANE_Bool default_control_panel; */ /* Mode Page Parameters */ MP_CXN cxn; /* hdr + Connection Parameters */ SANE_Int bmu; SANE_Int mud; SANE_Int white_balance; /* 00H Relative, 01H Absolute; power on default is relative */ /* Lamp Timer not supported */ SANE_Int adf_control; /* 00H None, 01H Book, 01H Simplex, 02H Duplex */ SANE_Int adf_mode_control; /* bit2: prefeed mode invalid: "0" : valid "1" */ /* Medium Wait Timer not supported */ SANE_Int endorser_control; /* Default Off when power on */ SANE_Char endorser_string[20]; SANE_Bool scan_wait_mode; /* wait for operator panel start button to be pressed */ SANE_Bool service_mode; /* power on default self_diagnostics 00H; 01H optical_adjustment */ /* standard information: EVPD bit is 0 */ SANE_Byte devtype; /* devtype[6]="scanner" */ SANE_Char vendor[9]; /* model name 8+1 */ SANE_Char product[17]; /* product name 16+1 */ SANE_Char revision[5]; /* revision 4+1 */ /* VPD information: EVPD bit is 1, Page Code=C0H */ /* adf_id: 0: No ADF * 1: Single-sided ADF * 2: Double-sided ADF * 3: ARDF (Reverse double-sided ADF) * 4: Reserved */ SANE_Bool hasADF; /* If YES; can either be one of Simplex,Duplex,ARDF */ SANE_Bool hasSimplex; SANE_Bool hasDuplex; SANE_Bool hasARDF; SANE_Bool hasEndorser; SANE_Bool hasIPU; SANE_Bool hasXBD; /* VPD Image Composition */ SANE_Bool supports_lineart; SANE_Bool supports_dithering; SANE_Bool supports_errordiffusion; SANE_Bool supports_color; SANE_Bool supports_4bitgray; SANE_Bool supports_8bitgray; /* VPD Image Data Processing ACE (supported for IS420) */ SANE_Bool supports_whiteframing; SANE_Bool supports_blackframing; SANE_Bool supports_edgeextraction; SANE_Bool supports_noiseremoval; /* supported for IS450 if IPU installed */ SANE_Bool supports_smoothing; /* supported for IS450 if IPU installed */ SANE_Bool supports_linebolding; /* VPD Compression (not supported for IS450) */ SANE_Bool supports_MH; SANE_Bool supports_MR; SANE_Bool supports_MMR; SANE_Bool supports_MHB; /* VPD Marker Recognition (not supported for IS450) */ SANE_Bool supports_markerrecognition; /* VPD Size Recognition (supported for IS450 if IPU installed) */ SANE_Bool supports_sizerecognition; /* VPD X Maximum Output Pixel: IS450:4960 IS420:4880 */ SANE_Int xmaxoutputpixels; /* jis information VPD IDENTIFIER Page Code F0H */ SANE_Int resBasicX; /* basic X resolution */ SANE_Int resBasicY; /* basic Y resolution */ SANE_Int resXstep; /* resolution step in main scan direction */ SANE_Int resYstep; /* resolution step in sub scan direction */ SANE_Int resMaxX; /* maximum X resolution */ SANE_Int resMaxY; /* maximum Y resolution */ SANE_Int resMinX; /* minimum X resolution */ SANE_Int resMinY; /* minimum Y resolution */ SANE_Int resStdList[16 + 1]; /* list of available standard resolutions (first slot is the length) */ SANE_Int winWidth; /* length of window (in BasicX res DPI) */ SANE_Int winHeight; /* height of window (in BasicY res DPI) */ /* jis.functions duplicates vpd.imagecomposition lineart/dither/grayscale */ SANE_Bool overflow_support; SANE_Bool lineart_support; SANE_Bool dither_support; SANE_Bool grayscale_support; } HS2P_Info; typedef struct HS2P_Device { struct HS2P_Device *next; /* * struct with pointers to device/vendor/model names, and a type value * used to inform sane frontend about the device */ SANE_Device sane; HS2P_Info info; SENSE_DATA sense_data; } HS2P_Device; #define GAMMA_LENGTH 256 typedef struct HS2P_Scanner { /* all the state needed to define a scan request: */ struct HS2P_Scanner *next; /* linked list for housekeeping */ int fd; /* SCSI filedescriptor */ /* --------------------------------------------------------------------- */ /* immutable values which are set during reading of config file. */ int buffer_size; /* for sanei_open */ int connection; /* hardware interface type */ /* SANE option descriptors and values */ SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; SANE_Parameters params; /* SANE image parameters */ /* additional values that don't fit into Option_Value representation */ SANE_Word gamma_table[GAMMA_LENGTH]; /* Custom Gray Gamma Table */ /* state information - not options */ /* scanner dependent/low-level state: */ HS2P_Device *hw; SANE_Int bmu; /* Basic Measurement Unit */ SANE_Int mud; /* Measurement Unit Divisor */ SANE_Byte image_composition; /* LINEART, HALFTONE, GRAYSCALE */ SANE_Byte bpp; /* 1,4,6,or 8 Bits Per Pixel */ u_long InvalidBytes; size_t bytes_to_read; SANE_Bool cancelled; /*SANE_Bool backpage; */ SANE_Bool scanning; SANE_Bool another_side; SANE_Bool EOM; HS2P_DATA data; } HS2P_Scanner; static const SANE_Range u8_range = { 0, /* minimum */ 255, /* maximum */ 0 /* quantization */ }; static const SANE_Range u16_range = { 0, /* minimum */ 65535, /* maximum */ 0 /* quantization */ }; #define SM_LINEART SANE_VALUE_SCAN_MODE_LINEART #define SM_HALFTONE SANE_VALUE_SCAN_MODE_HALFTONE #define SM_DITHER "Dither" #define SM_ERRORDIFFUSION "Error Diffusion" #define SM_COLOR SANE_VALUE_SCAN_MODE_COLOR #define SM_4BITGRAY "4-Bit Gray" #define SM_6BITGRAY "6-Bit Gray" #define SM_8BITGRAY "8-Bit Gray" static SANE_String scan_mode_list[9]; enum { FB, ADF }; static SANE_String_Const scan_source_list[] = { "FB", /* Flatbed */ "ADF", /* Automatic Document Feeder */ NULL }; static SANE_String compression_list[6]; /* "none", "g31d MH", "g32d MR", "g42d MMR", "MH byte boundary", NULL} */ typedef struct { SANE_String name; double width, length; /* paper dimensions in mm */ } HS2P_Paper; /* list of support paper sizes */ /* 'custom' MUST be item 0; otherwise a width or length of 0 indicates * the maximum value supported by the scanner */ static const HS2P_Paper paper_sizes[] = { /* Name, Width, Height in mm */ {"Custom", 0.0, 0.0}, {"Letter", 215.9, 279.4}, {"Legal", 215.9, 355.6}, {"Ledger", 279.4, 431.8}, {"A3", 297, 420}, {"A4", 210, 297}, {"A4R", 297, 210}, {"A5", 148.5, 210}, {"A5R", 210, 148.5}, {"A6", 105, 148.5}, {"B4", 250, 353}, {"B5", 182, 257}, {"Full", 0.0, 0.0}, }; #define PORTRAIT "Portrait" #define LANDSCAPE "Landscape" static SANE_String_Const orientation_list[] = { PORTRAIT, LANDSCAPE, NULL /* sentinel */ }; /* MUST be kept in sync with paper_sizes */ static SANE_String_Const paper_list[] = { "Custom", "Letter", "Legal", "Ledger", "A3", "A4", "A4R", "A5", "A5R", "A6", "B4", "B5", "Full", NULL /* (not the same as "") sentinel */ }; #if 0 static /* inline */ int _is_host_little_endian (void); static /* inline */ int _is_host_little_endian () { SANE_Int val = 255; unsigned char *firstbyte = (unsigned char *) &val; return (*firstbyte == 255) ? SANE_TRUE : SANE_FALSE; } #endif static /* inline */ void _lto2b (u_long val, SANE_Byte * bytes) { bytes[0] = (val >> 8) & 0xff; bytes[1] = val & 0xff; } static /* inline */ void _lto3b (u_long val, SANE_Byte * bytes) { bytes[0] = (val >> 16) & 0xff; bytes[1] = (val >> 8) & 0xff; bytes[2] = val & 0xff; } static /* inline */ void _lto4b (u_long val, SANE_Byte * bytes) { bytes[0] = (val >> 24) & 0xff; bytes[1] = (val >> 16) & 0xff; bytes[2] = (val >> 8) & 0xff; bytes[3] = val & 0xff; } static /* inline */ u_long _2btol (SANE_Byte * bytes) { u_long rv; rv = (bytes[0] << 8) | bytes[1]; return rv; } static /* inline */ u_long _4btol (SANE_Byte * bytes) { u_long rv; rv = (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]; return rv; } /* static inline SANE_Int _2btol(SANE_Byte *bytes) { SANE_Int rv; rv = (bytes[0] << 8) | bytes[1]; return (rv); } */ static inline SANE_Int _3btol (SANE_Byte * bytes) { SANE_Int rv; rv = (bytes[0] << 16) | (bytes[1] << 8) | bytes[2]; return (rv); } /* static inline SANE_Int _4btol(SANE_Byte *bytes) { SANE_Int rv; rv = (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]; return (rv); } */ enum adf_ret_bytes { ADF_SELECTION = 2, ADF_MODE_CONTROL, MEDIUM_WAIT_TIMER }; #define get_paddingtype_id(s) (get_list_index( paddingtype_list, (char *)(s) )) #define get_paddingtype_val(i) (paddingtype[ get_paddingtype_id( (i) ) ].val) #define get_paddingtype_strndx(v) (get_val_id_strndx(&paddingtype[0], NELEMS(paddingtype), (v))) #define get_halftone_code_id(s) (get_list_index( halftone_code, (char *)(s) )) #define get_halftone_code_val(i) (halftone[get_halftone_code_id( (i) ) ].val) #define get_halftone_pattern_id(s) (get_list_index( halftone_pattern_list, (char *)(s) )) #define get_halftone_pattern_val(i) (halftone[get_halftone_pattern_id( (i) ) ].val) #define get_auto_binarization_id(s) (get_list_index( auto_binarization_list, (char *)(s) )) #define get_auto_binarization_val(i) (auto_binarization[ get_auto_binarization_id( (i) ) ].val) #define get_auto_separation_id(s) (get_list_index( auto_separation_list, (char *)(s) )) #define get_auto_separation_val(i) (auto_separation[ get_auto_separation_id( (i) ) ].val) #define get_noisematrix_id(s) (get_list_index( noisematrix_list, (char *)(s) )) #define get_noisematrix_val(i) (noisematrix[ get_noisematrix_id( (i) ) ].val) #define get_grayfilter_id(s) (get_list_index( grayfilter_list, (char *)(s) )) #define get_grayfilter_val(i) (grayfilter[ get_grayfilter_id( (i) ) ].val) #define get_paper_id(s) (get_list_index( paper_list, (char *)(s) )) #define get_compression_id(s) (get_list_index( (const char **)compression_list, (char *)(s) )) #define get_scan_source_id(s) (get_list_index( (const char **)scan_source_list, (char *)(s) )) #define reserve_unit(fd) (unit_cmd((fd),HS2P_SCSI_RESERVE_UNIT)) #define release_unit(fd) (unit_cmd((fd),HS2P_SCSI_RELEASE_UNIT)) #define GET SANE_TRUE #define SET SANE_FALSE #define get_endorser_control(fd,val) (endorser_control( (fd), (val), GET )) #define set_endorser_control(fd,val) (endorser_control( (fd), (val), SET )) #define get_connection_parameters(fd,parm) (connection_parameters( (fd), (parm), GET )) #define set_connection_parameters(fd,parm) (connection_parameters( (fd), (parm), SET )) #define get_adf_control(fd, a, b, c) (adf_control( (fd), GET, (a), (b), (c) )) #define set_adf_control(fd, a, b, c) (adf_control( (fd), SET, (a), (b), (c) )) #define RELATIVE_WHITE 0x00 #define ABSOLUTE_WHITE 0x01 #define get_white_balance(fd,val) (white_balance( (fd), (val), GET )) #define set_white_balance(fd,val) (white_balance( (fd), (val), SET )) #define get_scan_wait_mode(fd) (scan_wait_mode( (fd), 0, GET )) #define set_scan_wait_mode(fd,val) (scan_wait_mode( (fd), (val), SET )) #define get_service_mode(fd) (service_mode( (fd), 0, GET )) #define set_service_mode(fd,val) (service_mode( (fd), (val), SET )) #define isset_ILI(sd) ( ((sd).sense_key & 0x20) != 0) #define isset_EOM(sd) ( ((sd).sense_key & 0x40) != 0) #endif /* HS2P_H */ backends-1.3.0/backend/ibm-scsi.c000066400000000000000000000273321456256263500165530ustar00rootroot00000000000000/* 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, see . 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 /* SCSI commands that the Ibm scanners understand: */ #define IBM_SCSI_TEST_UNIT_READY 0x00 #define IBM_SCSI_SET_WINDOW 0x24 #define IBM_SCSI_GET_WINDOW 0x25 #define IBM_SCSI_READ_SCANNED_DATA 0x28 #define IBM_SCSI_INQUIRY 0x12 #define IBM_SCSI_MODE_SELECT 0x15 #define IBM_SCSI_START_SCAN 0x1b #define IBM_SCSI_MODE_SENSE 0x1a #define IBM_SCSI_GET_BUFFER_STATUS 0x34 #define IBM_SCSI_OBJECT_POSITION 0x31 /* How long do we wait for scanner to have data for us */ #define MAX_WAITING_TIME 15 /* for object_position command */ #define OBJECT_POSITION_UNLOAD 0 #define OBJECT_POSITION_LOAD 1 struct scsi_window_cmd { SANE_Byte opcode; SANE_Byte byte2; SANE_Byte reserved[4]; SANE_Byte len[3]; SANE_Byte control; }; struct scsi_mode_select_cmd { SANE_Byte opcode; SANE_Byte byte2; #define SMS_SP 0x01 #define SMS_PF 0x10 SANE_Byte page_code; /* for mode_sense, reserved for mode_select */ SANE_Byte unused[1]; SANE_Byte len; SANE_Byte control; }; struct scsi_mode_header { SANE_Byte data_length; /* Sense data length */ SANE_Byte medium_type; SANE_Byte dev_spec; SANE_Byte blk_desc_len; }; /* next struct introduced by mf */ struct scsi_object_position_cmd { SANE_Byte opcode; SANE_Byte position_func; SANE_Byte count[3]; SANE_Byte res[3]; SANE_Byte control; SANE_Byte res2; }; struct scsi_get_buffer_status_cmd { SANE_Byte opcode; SANE_Byte byte2; SANE_Byte res[5]; SANE_Byte len[2]; SANE_Byte control; }; struct scsi_status_desc { SANE_Byte window_id; SANE_Byte byte2; SANE_Byte available[3]; SANE_Byte filled[3]; }; struct scsi_status_data { SANE_Byte len[3]; SANE_Byte byte4; struct scsi_status_desc desc; }; struct scsi_start_scan_cmd { SANE_Byte opcode; SANE_Byte byte2; SANE_Byte unused[2]; SANE_Byte len; SANE_Byte control; }; struct scsi_read_scanner_cmd { SANE_Byte opcode; SANE_Byte byte2; SANE_Byte data_type; SANE_Byte byte3; SANE_Byte data_type_qualifier[2]; SANE_Byte len[3]; SANE_Byte control; }; static SANE_Status test_unit_ready (int fd) { static SANE_Byte cmd[6]; SANE_Status status; DBG (11, ">> test_unit_ready\n"); cmd[0] = IBM_SCSI_TEST_UNIT_READY; memset (cmd, 0, sizeof (cmd)); status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0); DBG (11, "<< test_unit_ready\n"); return (status); } static SANE_Status inquiry (int fd, void *buf, size_t * buf_size) { static SANE_Byte cmd[6]; SANE_Status status; DBG (11, ">> inquiry\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = IBM_SCSI_INQUIRY; cmd[4] = *buf_size; status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, buf_size); DBG (11, "<< inquiry\n"); return (status); } static SANE_Status mode_select (int fd, struct mode_pages *mp) { static struct { struct scsi_mode_select_cmd cmd; struct scsi_mode_header smh; struct mode_pages mp; } select_cmd; SANE_Status status; DBG (11, ">> mode_select\n"); memset (&select_cmd, 0, sizeof (select_cmd)); select_cmd.cmd.opcode = IBM_SCSI_MODE_SELECT; select_cmd.cmd.byte2 |= SMS_PF; select_cmd.cmd.len = sizeof(select_cmd.smh) + sizeof(select_cmd.mp); /* next line by mf */ /* select_cmd.cmd.page_code= 20; */ memcpy (&select_cmd.mp, mp, sizeof(*mp)); status = sanei_scsi_cmd (fd, &select_cmd, sizeof (select_cmd), 0, 0); DBG (11, "<< mode_select\n"); return (status); } #if 0 static SANE_Status mode_sense (int fd, struct mode_pages *mp, SANE_Byte page_code) { static struct scsi_mode_select_cmd cmd; /* no type, we can reuse it for sensing */ static struct { struct scsi_mode_header smh; struct mode_pages mp; } select_data; static size_t select_size = sizeof(select_data); SANE_Status status; DBG (11, ">> mode_sense\n"); memset (&cmd, 0, sizeof (cmd)); cmd.opcode = IBM_SCSI_MODE_SENSE; cmd.page_code = page_code; cmd.len = sizeof(select_data); status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), &select_data, &select_size); memcpy (mp, &select_data.mp, sizeof(*mp)); DBG (11, "<< mode_sense\n"); return (status); } #endif static SANE_Status trigger_scan (int fd) { static struct scsi_start_scan_cmd cmd; static char window_id_list[1] = { '\0' }; /* scan start data out */ static size_t wl_size = 1; SANE_Status status; DBG (11, ">> trigger scan\n"); memset (&cmd, 0, sizeof (cmd)); cmd.opcode = IBM_SCSI_START_SCAN; cmd.len = wl_size; /* next line by mf */ /* cmd.unused[0] = 1; */ if (wl_size) status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), &window_id_list, &wl_size); else status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), 0, 0); DBG (11, "<< trigger scan\n"); return (status); } static SANE_Status set_window (int fd, struct ibm_window_data *iwd) { static struct { struct scsi_window_cmd cmd; struct ibm_window_data iwd; } win; SANE_Status status; DBG (11, ">> set_window\n"); memset (&win, 0, sizeof (win)); win.cmd.opcode = IBM_SCSI_SET_WINDOW; _lto3b(sizeof(*iwd), win.cmd.len); memcpy (&win.iwd, iwd, sizeof(*iwd)); status = sanei_scsi_cmd (fd, &win, sizeof (win), 0, 0); DBG (11, "<< set_window\n"); return (status); } static SANE_Status get_window (int fd, struct ibm_window_data *iwd) { static struct scsi_window_cmd cmd; static size_t iwd_size; SANE_Status status; iwd_size = sizeof(*iwd); DBG (11, ">> get_window datalen = %lu\n", (unsigned long) iwd_size); memset (&cmd, 0, sizeof (cmd)); cmd.opcode = IBM_SCSI_GET_WINDOW; #if 1 /* it was if 0 */ cmd.byte2 |= (SANE_Byte)0x01; /* set Single bit to get one window desc. */ #endif _lto3b(iwd_size, cmd.len); status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), iwd, &iwd_size); DBG (11, "<< get_window, datalen = %lu\n", (unsigned long) iwd_size); return (status); } static SANE_Status read_data (int fd, void *buf, size_t * buf_size) { static struct scsi_read_scanner_cmd cmd; SANE_Status status; DBG (11, ">> read_data %lu\n", (unsigned long) *buf_size); memset (&cmd, 0, sizeof (cmd)); cmd.opcode = IBM_SCSI_READ_SCANNED_DATA; _lto3b(*buf_size, cmd.len); status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), buf, buf_size); DBG (11, "<< read_data %lu\n", (unsigned long) *buf_size); return (status); } static SANE_Status object_position (int fd, int load) { static struct scsi_object_position_cmd cmd; SANE_Status status; DBG (11, ">> object_position\n"); #if 0 /* At least the Ricoh 420 doesn't like that command */ DBG (11, "object_position: ignored\n"); return SANE_STATUS_GOOD; #endif memset (&cmd, 0, sizeof (cmd)); cmd.opcode = IBM_SCSI_OBJECT_POSITION; if (load) cmd.position_func = OBJECT_POSITION_LOAD; else cmd.position_func = OBJECT_POSITION_UNLOAD; _lto3b(1, cmd.count); status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), 0, 0); DBG (11, "<< object_position\n"); return (status); } static SANE_Status get_data_status (int fd, struct scsi_status_desc *dbs) { static struct scsi_get_buffer_status_cmd cmd; static struct scsi_status_data ssd; size_t ssd_size = sizeof(ssd); SANE_Status status; DBG (11, ">> get_data_status %lu\n", (unsigned long) ssd_size); memset (&cmd, 0, sizeof (cmd)); cmd.opcode = IBM_SCSI_GET_BUFFER_STATUS; _lto2b(ssd_size, cmd.len); status = sanei_scsi_cmd (fd, &cmd, sizeof (cmd), &ssd, &ssd_size); memcpy (dbs, &ssd.desc, sizeof(*dbs)); if (status == SANE_STATUS_GOOD && ((unsigned int) _3btol(ssd.len) <= sizeof(*dbs) || _3btol(ssd.desc.filled) == 0)) { DBG (11, "get_data_status: busy\n"); status = SANE_STATUS_DEVICE_BUSY; } DBG (11, "<< get_data_status %lu\n", (unsigned long) ssd_size); return (status); } #if 0 static SANE_Status ibm_wait_ready_tur (int fd) { struct timeval now, start; SANE_Status status; gettimeofday (&start, 0); while (1) { DBG(3, "scsi_wait_ready: sending TEST_UNIT_READY\n"); status = sanei_scsi_cmd (fd, test_unit_ready, sizeof (test_unit_ready), 0, 0); switch (status) { default: /* Ignore errors while waiting for scanner to become ready. Some SCSI drivers return EIO while the scanner is returning to the home position. */ DBG(1, "scsi_wait_ready: test unit ready failed (%s)\n", sane_strstatus (status)); /* fall through */ case SANE_STATUS_DEVICE_BUSY: gettimeofday (&now, 0); if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME) { DBG(1, "ibm_wait_ready: timed out after %lu seconds\n", (u_long) (now.tv_sec - start.tv_sec)); return SANE_STATUS_INVAL; } usleep (100000); /* retry after 100ms */ break; case SANE_STATUS_GOOD: return status; } } return SANE_STATUS_INVAL; } #endif static SANE_Status ibm_wait_ready (Ibm_Scanner * s) { struct scsi_status_desc dbs; time_t now, start; SANE_Status status; start = time(NULL); while (1) { status = get_data_status (s->fd, &dbs); switch (status) { default: /* Ignore errors while waiting for scanner to become ready. Some SCSI drivers return EIO while the scanner is returning to the home position. */ DBG(1, "scsi_wait_ready: get datat status failed (%s)\n", sane_strstatus (status)); /* fall through */ case SANE_STATUS_DEVICE_BUSY: now = time(NULL); if (now - start >= MAX_WAITING_TIME) { DBG(1, "ibm_wait_ready: timed out after %lu seconds\n", (u_long) (now - start)); return SANE_STATUS_INVAL; } break; case SANE_STATUS_GOOD: DBG(11, "ibm_wait_ready: %d bytes ready\n", _3btol(dbs.filled)); return status; break; } usleep (1000000); /* retry after 100ms */ } return SANE_STATUS_INVAL; } backends-1.3.0/backend/ibm.c000066400000000000000000001040161456256263500156070ustar00rootroot00000000000000/* 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, see . 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 the Ibm 2456 flatbed scanner, written by mf . It derives from the backend for Ricoh flatbed scanners written by Feico W. Dillema. Currently maintained by Henning Meier-Geinitz . */ #define BUILD 5 #include "../include/sane/config.h" #include #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_scsi.h" #define BACKEND_NAME ibm #include "../include/sane/sanei_backend.h" #ifndef PATH_MAX # define PATH_MAX 1024 #endif #include "../include/sane/sanei_config.h" #define IBM_CONFIG_FILE "ibm.conf" #include "ibm.h" #include "ibm-scsi.c" #define MAX(a,b) ((a) > (b) ? (a) : (b)) static int num_devices = 0; static Ibm_Device *first_dev = NULL; static Ibm_Scanner *first_handle = NULL; /* static int is50 = 0; */ static size_t max_string_size (const SANE_String_Const strings[]) { size_t size, max_size = 0; int i; DBG (11, ">> max_string_size\n"); for (i = 0; strings[i]; ++i) { size = strlen (strings[i]) + 1; if (size > max_size) max_size = size; } DBG (11, "<< max_string_size\n"); return max_size; } static SANE_Status attach (const char *devnam, Ibm_Device ** devp) { SANE_Status status; Ibm_Device *dev; int fd; struct inquiry_data ibuf; struct measurements_units_page mup; struct ibm_window_data wbuf; size_t buf_size; char *str; DBG (11, ">> attach\n"); for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->sane.name, devnam) == 0) { if (devp) *devp = dev; return (SANE_STATUS_GOOD); } } DBG (3, "attach: opening %s\n", devnam); status = sanei_scsi_open (devnam, &fd, NULL, NULL); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: open failed: %s\n", sane_strstatus (status)); return (status); } DBG (3, "attach: sending INQUIRY\n"); memset (&ibuf, 0, sizeof (ibuf)); buf_size = sizeof(ibuf); /* next line by mf */ ibuf.byte2 = 2; status = inquiry (fd, &ibuf, &buf_size); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: inquiry failed: %s\n", sane_strstatus (status)); sanei_scsi_close (fd); return (status); } if (ibuf.devtype != 6) { DBG (1, "attach: device \"%s\" is not a scanner\n", devnam); sanei_scsi_close (fd); return (SANE_STATUS_INVAL); } if (!( (strncmp ((char *)ibuf.vendor, "IBM", 3) ==0 && strncmp ((char *)ibuf.product, "2456", 4) == 0) || (strncmp ((char *)ibuf.vendor, "RICOH", 5) == 0 && strncmp ((char *)ibuf.product, "IS420", 5) == 0) || (strncmp ((char *)ibuf.vendor, "RICOH", 5) == 0 && strncmp ((char *)ibuf.product, "IS410", 5) == 0) || (strncmp ((char *)ibuf.vendor, "RICOH", 5) == 0 && strncmp ((char *)ibuf.product, "IS430", 5) == 0) )) { DBG (1, "attach: device \"%s\" doesn't look like a scanner I know\n", devnam); sanei_scsi_close (fd); return (SANE_STATUS_INVAL); } DBG (3, "attach: sending TEST_UNIT_READY\n"); status = test_unit_ready (fd); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: test unit ready failed (%s)\n", sane_strstatus (status)); sanei_scsi_close (fd); return (status); } /* * Causes a problem with RICOH IS420 * Ignore this function ... seems to work ok * Suggested to George Murphy george@topfloor.ie by henning */ if (strncmp((char *)ibuf.vendor, "RICOH", 5) != 0 && strncmp((char *)ibuf.product, "IS420", 5) != 0) { DBG (3, "attach: sending OBJECT POSITION\n"); status = object_position (fd, OBJECT_POSITION_UNLOAD); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: OBJECT POSITION failed\n"); sanei_scsi_close (fd); return (SANE_STATUS_INVAL); } } memset (&mup, 0, sizeof (mup)); mup.page_code = MEASUREMENTS_PAGE; mup.parameter_length = 0x06; mup.bmu = INCHES; mup.mud[0] = (DEFAULT_MUD >> 8) & 0xff; mup.mud[1] = (DEFAULT_MUD & 0xff); #if 0 DBG (3, "attach: sending MODE SELECT\n"); status = mode_select (fd, (struct mode_pages *) &mup); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: MODE_SELECT failed\n"); sanei_scsi_close (fd); return (SANE_STATUS_INVAL); } #endif #if 0 DBG (3, "attach: sending MODE SENSE\n"); memset (&mup, 0, sizeof (mup)); status = mode_sense (fd, (struct mode_pages *) &mup, PC_CURRENT | MEASUREMENTS_PAGE); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: MODE_SENSE failed\n"); sanei_scsi_close (fd); return (SANE_STATUS_INVAL); } #endif DBG (3, "attach: sending GET WINDOW\n"); memset (&wbuf, 0, sizeof (wbuf)); status = get_window (fd, &wbuf); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: GET_WINDOW failed %d\n", status); sanei_scsi_close (fd); DBG (11, "<< attach\n"); return (SANE_STATUS_INVAL); } sanei_scsi_close (fd); dev = malloc (sizeof (*dev)); if (!dev) return (SANE_STATUS_NO_MEM); memset (dev, 0, sizeof (*dev)); dev->sane.name = strdup (devnam); dev->sane.vendor = "IBM"; size_t prod_rev_size = sizeof(ibuf.product) + sizeof(ibuf.revision) + 1; str = malloc (prod_rev_size); if (str) { snprintf (str, prod_rev_size, "%.*s%.*s", (int) sizeof(ibuf.product), (const char *) ibuf.product, (int) sizeof(ibuf.revision), (const char *) ibuf.revision); } dev->sane.model = str; dev->sane.type = "flatbed scanner"; DBG (5, "dev->sane.name = %s\n", dev->sane.name); DBG (5, "dev->sane.vendor = %s\n", dev->sane.vendor); DBG (5, "dev->sane.model = %s\n", dev->sane.model); DBG (5, "dev->sane.type = %s\n", dev->sane.type); dev->info.xres_default = _2btol(wbuf.x_res); dev->info.yres_default = _2btol(wbuf.y_res); dev->info.image_mode_default = wbuf.image_comp; /* if you throw the MRIF bit the brightness control reverses too */ /* so I reverse the reversal in software for symmetry's sake */ /* I should make this into an option */ if (wbuf.image_comp == IBM_GRAYSCALE || wbuf.image_comp == IBM_DITHERED_MONOCHROME) { dev->info.brightness_default = 256 - wbuf.brightness; /* if (is50) dev->info.contrast_default = wbuf.contrast; else */ dev->info.contrast_default = 256 - wbuf.contrast; } else /* wbuf.image_comp == IBM_BINARY_MONOCHROME */ { dev->info.brightness_default = wbuf.brightness; dev->info.contrast_default = wbuf.contrast; } /* da rivedere dev->info.adf_default = wbuf.adf_state; */ dev->info.adf_default = ADF_UNUSED; dev->info.adf_default = IBM_PAPER_USER_DEFINED; #if 1 dev->info.bmu = mup.bmu; dev->info.mud = _2btol(mup.mud); if (dev->info.mud == 0) { /* The Ricoh says it uses points as default Basic Measurement Unit */ /* but gives a Measurement Unit Divisor of zero */ /* So, we set it to the default (SCSI-standard) of 1200 */ /* with BMU in inches, i.e. 1200 points equal 1 inch */ dev->info.bmu = INCHES; dev->info.mud = DEFAULT_MUD; } #else dev->info.bmu = INCHES; dev->info.mud = DEFAULT_MUD; #endif DBG (5, "xres_default=%d\n", dev->info.xres_default); DBG (5, "xres_range.max=%d\n", dev->info.xres_range.max); DBG (5, "xres_range.min=%d\n", dev->info.xres_range.min); DBG (5, "yres_default=%d\n", dev->info.yres_default); DBG (5, "yres_range.max=%d\n", dev->info.yres_range.max); DBG (5, "yres_range.min=%d\n", dev->info.yres_range.min); DBG (5, "x_range.max=%d\n", dev->info.x_range.max); DBG (5, "y_range.max=%d\n", dev->info.y_range.max); DBG (5, "image_mode=%d\n", dev->info.image_mode_default); DBG (5, "brightness=%d\n", dev->info.brightness_default); DBG (5, "contrast=%d\n", dev->info.contrast_default); DBG (5, "adf_state=%d\n", dev->info.adf_default); DBG (5, "bmu=%d\n", dev->info.bmu); DBG (5, "mud=%d\n", dev->info.mud); ++num_devices; dev->next = first_dev; first_dev = dev; if (devp) *devp = dev; DBG (11, "<< attach\n"); return (SANE_STATUS_GOOD); } static SANE_Status attach_one(const char *devnam) { attach (devnam, NULL); return SANE_STATUS_GOOD; } static SANE_Status init_options (Ibm_Scanner * s) { int i; DBG (11, ">> init_options\n"); memset (s->opt, 0, sizeof (s->opt)); memset (s->val, 0, sizeof (s->val)); for (i = 0; i < NUM_OPTIONS; ++i) { s->opt[i].size = sizeof (SANE_Word); s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* "Mode" group: */ s->opt[OPT_MODE_GROUP].title = "Scan Mode"; s->opt[OPT_MODE_GROUP].desc = ""; s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MODE_GROUP].cap = 0; s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* scan mode */ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; 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 = max_string_size (mode_list); s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_MODE].constraint.string_list = mode_list; s->val[OPT_MODE].s = strdup (mode_list[s->hw->info.image_mode_default]); /* x resolution */ s->opt[OPT_X_RESOLUTION].name = "X" SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_X_RESOLUTION].title = "X " SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_X_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_X_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_X_RESOLUTION].constraint.range = &ibm2456_res_range; s->val[OPT_X_RESOLUTION].w = s->hw->info.xres_default; /* if (is50) s->opt[OPT_X_RESOLUTION].constraint.range = &is50_res_range; else */ s->opt[OPT_X_RESOLUTION].constraint.range = &ibm2456_res_range; /* y resolution */ s->opt[OPT_Y_RESOLUTION].name = "Y" SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_Y_RESOLUTION].title = "Y " SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_Y_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_Y_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_Y_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; s->val[OPT_Y_RESOLUTION].w = s->hw->info.yres_default; s->opt[OPT_Y_RESOLUTION].constraint.range = &ibm2456_res_range; /* adf */ s->opt[OPT_ADF].name = "adf"; s->opt[OPT_ADF].title = "Use ADF"; s->opt[OPT_ADF].desc = "Uses the automatic document feeder."; s->opt[OPT_ADF].type = SANE_TYPE_BOOL; s->opt[OPT_ADF].unit = SANE_UNIT_NONE; s->opt[OPT_ADF].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_ADF].b = s->hw->info.adf_default; /* "Geometry" group: */ s->opt[OPT_GEOMETRY_GROUP].title = "Geometry"; s->opt[OPT_GEOMETRY_GROUP].desc = ""; s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* paper */ s->opt[OPT_PAPER].name = "paper"; s->opt[OPT_PAPER].title = "Paper format"; s->opt[OPT_PAPER].desc = "Sets the paper format."; s->opt[OPT_PAPER].type = SANE_TYPE_STRING; s->opt[OPT_PAPER].size = max_string_size (paper_list); s->opt[OPT_PAPER].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_PAPER].constraint.string_list = paper_list; s->val[OPT_PAPER].s = strdup (paper_list[s->hw->info.paper_default]); /* top-left x */ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->opt[OPT_TL_X].type = SANE_TYPE_INT; s->opt[OPT_TL_X].unit = SANE_UNIT_PIXEL; s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_X].constraint.range = &default_x_range; s->val[OPT_TL_X].w = 0; /* top-left y */ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->opt[OPT_TL_Y].type = SANE_TYPE_INT; s->opt[OPT_TL_Y].unit = SANE_UNIT_PIXEL; s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_Y].constraint.range = &default_y_range; s->val[OPT_TL_Y].w = 0; /* bottom-right x */ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->opt[OPT_BR_X].type = SANE_TYPE_INT; s->opt[OPT_BR_X].unit = SANE_UNIT_PIXEL; s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_X].constraint.range = &default_x_range; s->val[OPT_BR_X].w = default_x_range.max; /* bottom-right y */ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->opt[OPT_BR_Y].type = SANE_TYPE_INT; s->opt[OPT_BR_Y].unit = SANE_UNIT_PIXEL; s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_Y].constraint.range = &default_y_range; s->val[OPT_BR_Y].w = default_y_range.max; /* "Enhancement" group: */ s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement"; s->opt[OPT_ENHANCEMENT_GROUP].desc = ""; s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_ENHANCEMENT_GROUP].cap = 0; s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* brightness */ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT; s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE; s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BRIGHTNESS].constraint.range = &u8_range; s->val[OPT_BRIGHTNESS].w = s->hw->info.brightness_default; /* contrast */ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST; s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST; s->opt[OPT_CONTRAST].type = SANE_TYPE_INT; s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE; s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CONTRAST].constraint.range = &u8_range; s->val[OPT_CONTRAST].w = s->hw->info.contrast_default; DBG (11, "<< init_options\n"); return SANE_STATUS_GOOD; } static SANE_Status do_cancel (Ibm_Scanner * s) { SANE_Status status; DBG (11, ">> do_cancel\n"); DBG (3, "cancel: sending OBJECT POSITION\n"); status = object_position (s->fd, OBJECT_POSITION_UNLOAD); if (status != SANE_STATUS_GOOD) { DBG (1, "cancel: OBJECT POSITION failed\n"); } s->scanning = SANE_FALSE; if (s->fd >= 0) { sanei_scsi_close (s->fd); s->fd = -1; } DBG (11, "<< do_cancel\n"); return (SANE_STATUS_CANCELLED); } SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { char devnam[PATH_MAX] = "/dev/scanner"; FILE *fp; DBG_INIT (); DBG (11, ">> sane_init (authorize %s null)\n", (authorize) ? "!=" : "=="); #if defined PACKAGE && defined VERSION DBG (2, "sane_init: ibm backend version %d.%d-%d (" PACKAGE " " VERSION ")\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); #endif if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); fp = sanei_config_open(IBM_CONFIG_FILE); if (fp) { char line[PATH_MAX], *lp; size_t len; /* read config file */ while (sanei_config_read (line, sizeof (line), fp)) { if (line[0] == '#') /* ignore line comments */ continue; len = strlen (line); if (!len) continue; /* ignore empty lines */ /* skip white space: */ for (lp = line; isspace(*lp); ++lp) ; strcpy (devnam, lp); } fclose (fp); } sanei_config_attach_matching_devices (devnam, attach_one); DBG (11, "<< sane_init\n"); return SANE_STATUS_GOOD; } void sane_exit (void) { Ibm_Device *dev, *next; DBG (11, ">> sane_exit\n"); for (dev = first_dev; dev; dev = next) { next = dev->next; free ((void *) dev->sane.name); free ((void *) dev->sane.model); free (dev); } DBG (11, "<< sane_exit\n"); } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { static const SANE_Device **devlist = 0; Ibm_Device *dev; int i; DBG (11, ">> sane_get_devices (local_only = %d)\n", local_only); if (devlist) free (devlist); devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); if (!devlist) return (SANE_STATUS_NO_MEM); i = 0; for (dev = first_dev; dev; dev = dev->next) devlist[i++] = &dev->sane; devlist[i++] = 0; *device_list = devlist; DBG (11, "<< sane_get_devices\n"); return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const devnam, SANE_Handle * handle) { SANE_Status status; Ibm_Device *dev; Ibm_Scanner *s; DBG (11, ">> sane_open\n"); if (devnam[0] == '\0') { for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->sane.name, devnam) == 0) break; } if (!dev) { status = attach (devnam, &dev); if (status != SANE_STATUS_GOOD) return (status); } } else { dev = first_dev; } if (!dev) return (SANE_STATUS_INVAL); s = malloc (sizeof (*s)); if (!s) return SANE_STATUS_NO_MEM; memset (s, 0, sizeof (*s)); s->fd = -1; s->hw = dev; init_options (s); s->next = first_handle; first_handle = s; *handle = s; DBG (11, "<< sane_open\n"); return SANE_STATUS_GOOD; } void sane_close (SANE_Handle handle) { Ibm_Scanner *s = (Ibm_Scanner *) handle; DBG (11, ">> sane_close\n"); if (s->fd != -1) sanei_scsi_close (s->fd); free (s); DBG (11, ">> sane_close\n"); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { Ibm_Scanner *s = handle; DBG (11, ">> sane_get_option_descriptor\n"); if ((unsigned) option >= NUM_OPTIONS) return (0); DBG (11, "<< sane_get_option_descriptor\n"); return (s->opt + option); } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { Ibm_Scanner *s = handle; SANE_Status status; SANE_Word cap; DBG (11, ">> sane_control_option\n"); if (info) *info = 0; if (s->scanning) return (SANE_STATUS_DEVICE_BUSY); if (option >= NUM_OPTIONS) return (SANE_STATUS_INVAL); cap = s->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) return (SANE_STATUS_INVAL); if (action == SANE_ACTION_GET_VALUE) { DBG (11, "sane_control_option get_value\n"); switch (option) { /* word options: */ case OPT_X_RESOLUTION: case OPT_Y_RESOLUTION: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_NUM_OPTS: case OPT_BRIGHTNESS: case OPT_CONTRAST: *(SANE_Word *) val = s->val[option].w; return (SANE_STATUS_GOOD); /* bool options: */ case OPT_ADF: *(SANE_Bool *) val = s->val[option].b; return (SANE_STATUS_GOOD); /* string options: */ case OPT_MODE: case OPT_PAPER: strcpy (val, s->val[option].s); return (SANE_STATUS_GOOD); } } else { DBG (11, "sane_control_option set_value\n"); if (action == SANE_ACTION_SET_VALUE) { if (!SANE_OPTION_IS_SETTABLE (cap)) return (SANE_STATUS_INVAL); status = sanei_constrain_value (s->opt + option, val, info); if (status != SANE_STATUS_GOOD) return status; switch (option) { /* (mostly) side-effect-free word options: */ case OPT_X_RESOLUTION: case OPT_Y_RESOLUTION: if (info && s->val[option].w != *(SANE_Word *) val) *info |= SANE_INFO_RELOAD_PARAMS; s->val[option].w = *(SANE_Word *) val; return (SANE_STATUS_GOOD); case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: if (info && s->val[option].w != *(SANE_Word *) val) *info |= SANE_INFO_RELOAD_PARAMS; s->val[option].w = *(SANE_Word *) val; /* resets the paper format to user defined */ if (strcmp(s->val[OPT_PAPER].s, paper_list[IBM_PAPER_USER_DEFINED]) != 0) { if (info) *info |= SANE_INFO_RELOAD_OPTIONS; if (s->val[OPT_PAPER].s) free (s->val[OPT_PAPER].s); s->val[OPT_PAPER].s = strdup (paper_list[IBM_PAPER_USER_DEFINED]); } return (SANE_STATUS_GOOD); case OPT_NUM_OPTS: case OPT_BRIGHTNESS: case OPT_CONTRAST: s->val[option].w = *(SANE_Word *) val; return (SANE_STATUS_GOOD); case OPT_MODE: if (info && strcmp (s->val[option].s, (SANE_String) val)) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); return (SANE_STATUS_GOOD); case OPT_ADF: s->val[option].b = *(SANE_Bool *) val; if (*(SANE_Bool *) val) s->adf_state = ADF_ARMED; else s->adf_state = ADF_UNUSED; return (SANE_STATUS_GOOD); case OPT_PAPER: if (info && strcmp (s->val[option].s, (SANE_String) val)) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); if (strcmp (s->val[OPT_PAPER].s, "User") != 0) { s->val[OPT_TL_X].w = 0; s->val[OPT_TL_Y].w = 0; if (strcmp (s->val[OPT_PAPER].s, "A3") == 0) { s->val[OPT_BR_X].w = PAPER_A3_W; s->val[OPT_BR_Y].w = PAPER_A3_H; } else if (strcmp (s->val[OPT_PAPER].s, "A4") == 0) { s->val[OPT_BR_X].w = PAPER_A4_W; s->val[OPT_BR_Y].w = PAPER_A4_H; } else if (strcmp (s->val[OPT_PAPER].s, "A4R") == 0) { s->val[OPT_BR_X].w = PAPER_A4R_W; s->val[OPT_BR_Y].w = PAPER_A4R_H; } else if (strcmp (s->val[OPT_PAPER].s, "A5") == 0) { s->val[OPT_BR_X].w = PAPER_A5_W; s->val[OPT_BR_Y].w = PAPER_A5_H; } else if (strcmp (s->val[OPT_PAPER].s, "A5R") == 0) { s->val[OPT_BR_X].w = PAPER_A5R_W; s->val[OPT_BR_Y].w = PAPER_A5R_H; } else if (strcmp (s->val[OPT_PAPER].s, "A6") == 0) { s->val[OPT_BR_X].w = PAPER_A6_W; s->val[OPT_BR_Y].w = PAPER_A6_H; } else if (strcmp (s->val[OPT_PAPER].s, "B4") == 0) { s->val[OPT_BR_X].w = PAPER_B4_W; s->val[OPT_BR_Y].w = PAPER_B4_H; } else if (strcmp (s->val[OPT_PAPER].s, "Legal") == 0) { s->val[OPT_BR_X].w = PAPER_LEGAL_W; s->val[OPT_BR_Y].w = PAPER_LEGAL_H; } else if (strcmp (s->val[OPT_PAPER].s, "Letter") == 0) { s->val[OPT_BR_X].w = PAPER_LETTER_W; s->val[OPT_BR_Y].w = PAPER_LETTER_H; } } return (SANE_STATUS_GOOD); } } } DBG (11, "<< sane_control_option\n"); return (SANE_STATUS_INVAL); } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { Ibm_Scanner *s = handle; DBG (11, ">> sane_get_parameters\n"); if (!s->scanning) { int width, length, xres, yres; const char *mode; memset (&s->params, 0, sizeof (s->params)); width = s->val[OPT_BR_X].w - s->val[OPT_TL_X].w; length = s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w; xres = s->val[OPT_X_RESOLUTION].w; yres = s->val[OPT_Y_RESOLUTION].w; /* make best-effort guess at what parameters will look like once scanning starts. */ if (xres > 0 && yres > 0 && width > 0 && length > 0) { s->params.pixels_per_line = width * xres / s->hw->info.mud; s->params.lines = length * yres / s->hw->info.mud; } mode = s->val[OPT_MODE].s; if ((strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0) || (strcmp (mode, SANE_VALUE_SCAN_MODE_HALFTONE)) == 0) { s->params.format = SANE_FRAME_GRAY; s->params.bytes_per_line = s->params.pixels_per_line / 8; /* the Ibm truncates to the byte boundary, so: chop! */ s->params.pixels_per_line = s->params.bytes_per_line * 8; s->params.depth = 1; } else /* if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) */ { s->params.format = SANE_FRAME_GRAY; s->params.bytes_per_line = s->params.pixels_per_line; s->params.depth = 8; } s->params.last_frame = SANE_TRUE; } else DBG (5, "sane_get_parameters: scanning, so can't get params\n"); if (params) *params = s->params; DBG (1, "%d pixels per line, %d bytes, %d lines high, total %lu bytes, " "dpi=%d\n", s->params.pixels_per_line, s->params.bytes_per_line, s->params.lines, (u_long) s->bytes_to_read, s->val[OPT_Y_RESOLUTION].w); DBG (11, "<< sane_get_parameters\n"); return (SANE_STATUS_GOOD); } SANE_Status sane_start (SANE_Handle handle) { char *mode_str; Ibm_Scanner *s = handle; SANE_Status status; struct ibm_window_data wbuf; struct measurements_units_page mup; DBG (11, ">> sane_start\n"); /* First make sure we have a current parameter set. Some of the parameters will be overwritten below, but that's OK. */ status = sane_get_parameters (s, 0); if (status != SANE_STATUS_GOOD) return status; status = sanei_scsi_open (s->hw->sane.name, &s->fd, 0, 0); if (status != SANE_STATUS_GOOD) { DBG (1, "open of %s failed: %s\n", s->hw->sane.name, sane_strstatus (status)); return (status); } mode_str = s->val[OPT_MODE].s; s->xres = s->val[OPT_X_RESOLUTION].w; s->yres = s->val[OPT_Y_RESOLUTION].w; s->ulx = s->val[OPT_TL_X].w; s->uly = s->val[OPT_TL_Y].w; s->width = s->val[OPT_BR_X].w - s->val[OPT_TL_X].w; s->length = s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w; s->brightness = s->val[OPT_BRIGHTNESS].w; s->contrast = s->val[OPT_CONTRAST].w; s->bpp = s->params.depth; if (strcmp (mode_str, SANE_VALUE_SCAN_MODE_LINEART) == 0) { s->image_composition = IBM_BINARY_MONOCHROME; } else if (strcmp (mode_str, SANE_VALUE_SCAN_MODE_HALFTONE) == 0) { s->image_composition = IBM_DITHERED_MONOCHROME; } else if (strcmp (mode_str, SANE_VALUE_SCAN_MODE_GRAY) == 0) { s->image_composition = IBM_GRAYSCALE; } memset (&wbuf, 0, sizeof (wbuf)); /* next line commented out by mf */ /* _lto2b(sizeof(wbuf) - 8, wbuf.len); */ /* next line by mf */ _lto2b(IBM_WINDOW_DATA_SIZE, wbuf.len); /* size=320 */ _lto2b(s->xres, wbuf.x_res); _lto2b(s->yres, wbuf.y_res); _lto4b(s->ulx, wbuf.x_org); _lto4b(s->uly, wbuf.y_org); _lto4b(s->width, wbuf.width); _lto4b(s->length, wbuf.length); wbuf.image_comp = s->image_composition; /* if you throw the MRIF bit the brightness control reverses too */ /* so I reverse the reversal in software for symmetry's sake */ if (wbuf.image_comp == IBM_GRAYSCALE || wbuf.image_comp == IBM_DITHERED_MONOCHROME) { if (wbuf.image_comp == IBM_GRAYSCALE) wbuf.mrif_filtering_gamma_id = (SANE_Byte) 0x80; /* it was 0x90 */ if (wbuf.image_comp == IBM_DITHERED_MONOCHROME) wbuf.mrif_filtering_gamma_id = (SANE_Byte) 0x10; wbuf.brightness = 256 - (SANE_Byte) s->brightness; /* if (is50) wbuf.contrast = (SANE_Byte) s->contrast; else */ wbuf.contrast = 256 - (SANE_Byte) s->contrast; } else /* wbuf.image_comp == IBM_BINARY_MONOCHROME */ { wbuf.mrif_filtering_gamma_id = (SANE_Byte) 0x00; wbuf.brightness = (SANE_Byte) s->brightness; wbuf.contrast = (SANE_Byte) s->contrast; } wbuf.threshold = 0; wbuf.bits_per_pixel = s->bpp; wbuf.halftone_code = 2; /* diithering */ wbuf.halftone_id = 0x0A; /* 8x8 Bayer pattenr */ wbuf.pad_type = 3; wbuf.bit_ordering[0] = 0; wbuf.bit_ordering[1] = 7; /* modified by mf (it was 3) */ DBG (5, "xres=%d\n", _2btol(wbuf.x_res)); DBG (5, "yres=%d\n", _2btol(wbuf.y_res)); DBG (5, "ulx=%d\n", _4btol(wbuf.x_org)); DBG (5, "uly=%d\n", _4btol(wbuf.y_org)); DBG (5, "width=%d\n", _4btol(wbuf.width)); DBG (5, "length=%d\n", _4btol(wbuf.length)); DBG (5, "image_comp=%d\n", wbuf.image_comp); DBG (11, "sane_start: sending SET WINDOW\n"); status = set_window (s->fd, &wbuf); if (status != SANE_STATUS_GOOD) { DBG (1, "SET WINDOW failed: %s\n", sane_strstatus (status)); return (status); } DBG (11, "sane_start: sending GET WINDOW\n"); memset (&wbuf, 0, sizeof (wbuf)); status = get_window (s->fd, &wbuf); if (status != SANE_STATUS_GOOD) { DBG (1, "GET WINDOW failed: %s\n", sane_strstatus (status)); return (status); } DBG (5, "xres=%d\n", _2btol(wbuf.x_res)); DBG (5, "yres=%d\n", _2btol(wbuf.y_res)); DBG (5, "ulx=%d\n", _4btol(wbuf.x_org)); DBG (5, "uly=%d\n", _4btol(wbuf.y_org)); DBG (5, "width=%d\n", _4btol(wbuf.width)); DBG (5, "length=%d\n", _4btol(wbuf.length)); DBG (5, "image_comp=%d\n", wbuf.image_comp); DBG (11, "sane_start: sending MODE SELECT\n"); memset (&mup, 0, sizeof (mup)); mup.page_code = MEASUREMENTS_PAGE; mup.parameter_length = 0x06; mup.bmu = INCHES; mup.mud[0] = (DEFAULT_MUD >> 8) & 0xff; mup.mud[1] = (DEFAULT_MUD & 0xff); /* next lines by mf */ mup.adf_page_code = 0x26; mup.adf_parameter_length = 6; if (s->adf_state == ADF_ARMED) mup.adf_control = 1; else mup.adf_control = 0; /* end lines by mf */ status = mode_select (s->fd, (struct mode_pages *) &mup); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: MODE_SELECT failed\n"); return (SANE_STATUS_INVAL); } status = trigger_scan (s->fd); if (status != SANE_STATUS_GOOD) { DBG (1, "start of scan failed: %s\n", sane_strstatus (status)); /* next line introduced not to freeze xscanimage */ do_cancel(s); return status; } /* Wait for scanner to become ready to transmit data */ status = ibm_wait_ready (s); if (status != SANE_STATUS_GOOD) { DBG (1, "GET DATA STATUS failed: %s\n", sane_strstatus (status)); return (status); } s->bytes_to_read = s->params.bytes_per_line * s->params.lines; DBG (1, "%d pixels per line, %d bytes, %d lines high, total %lu bytes, " "dpi=%d\n", s->params.pixels_per_line, s->params.bytes_per_line, s->params.lines, (u_long) s->bytes_to_read, s->val[OPT_Y_RESOLUTION].w); s->scanning = SANE_TRUE; DBG (11, "<< sane_start\n"); return (SANE_STATUS_GOOD); } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { Ibm_Scanner *s = handle; SANE_Status status; size_t nread; DBG (11, ">> sane_read\n"); *len = 0; DBG (11, "sane_read: bytes left to read: %ld\n", (u_long) s->bytes_to_read); if (s->bytes_to_read == 0) { do_cancel (s); return (SANE_STATUS_EOF); } if (!s->scanning) { DBG (11, "sane_read: scanning is false!\n"); return (do_cancel (s)); } nread = max_len; if (nread > s->bytes_to_read) nread = s->bytes_to_read; DBG (11, "sane_read: read %ld bytes\n", (u_long) nread); status = read_data (s->fd, buf, &nread); if (status != SANE_STATUS_GOOD) { DBG (11, "sane_read: read error\n"); do_cancel (s); return (SANE_STATUS_IO_ERROR); } *len = nread; s->bytes_to_read -= nread; DBG (11, "<< sane_read\n"); return (SANE_STATUS_GOOD); } void sane_cancel (SANE_Handle handle) { Ibm_Scanner *s = handle; DBG (11, ">> sane_cancel\n"); s->scanning = SANE_FALSE; DBG (11, "<< sane_cancel\n"); } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { DBG (5, ">> sane_set_io_mode (handle = %p, non_blocking = %d)\n", handle, non_blocking); DBG (5, "<< sane_set_io_mode\n"); return SANE_STATUS_UNSUPPORTED; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { DBG (5, ">> sane_get_select_fd (handle = %p, fd = %p)\n", handle, (void *) fd); DBG (5, "<< sane_get_select_fd\n"); return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/ibm.conf.in000066400000000000000000000000461456256263500167150ustar00rootroot00000000000000scsi IBM 2456 scsi RICOH /dev/scanner backends-1.3.0/backend/ibm.h000066400000000000000000000225121456256263500156140ustar00rootroot00000000000000/* 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, see . 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 ibm_h #define ibm_h 1 #include #include "../include/sane/config.h" /* defines for scan_image_mode field */ #define IBM_BINARY_MONOCHROME 0 #define IBM_DITHERED_MONOCHROME 1 #define IBM_GRAYSCALE 2 /* defines for paper field */ #define IBM_PAPER_USER_DEFINED 0 #define IBM_PAPER_A3 1 #define IBM_PAPER_A4 2 #define IBM_PAPER_A4L 3 #define IBM_PAPER_A5 4 #define IBM_PAPER_A5L 5 #define IBM_PAPER_A6 6 #define IBM_PAPER_B4 7 #define IBM_PAPER_B5 8 #define IBM_PAPER_LEGAL 9 #define IBM_PAPER_LETTER 10 /* sizes for mode parameter's base_measurement_unit */ #define INCHES 0 #define MILLIMETERS 1 #define POINTS 2 #define DEFAULT_MUD 1200 #define MEASUREMENTS_PAGE (SANE_Byte)(0x03) /* Mode Page Control */ #define PC_CURRENT 0x00 #define PC_CHANGE 0x40 #define PC_DEFAULT 0x80 #define PC_SAVED 0xc0 static const SANE_String_Const mode_list[] = { SANE_VALUE_SCAN_MODE_LINEART, SANE_VALUE_SCAN_MODE_HALFTONE, SANE_VALUE_SCAN_MODE_GRAY, 0 }; static const SANE_String_Const paper_list[] = { "User", "A3", "A4", "A4R", "A5", "A5R", "A6", "B4", "B5", "Legal", "Letter", 0 }; #define PAPER_A3_W 14032 #define PAPER_A3_H 19842 #define PAPER_A4_W 9921 #define PAPER_A4_H 14032 #define PAPER_A4R_W 14032 #define PAPER_A4R_H 9921 #define PAPER_A5_W 7016 #define PAPER_A5_H 9921 #define PAPER_A5R_W 9921 #define PAPER_A5R_H 7016 #define PAPER_A6_W 4960 #define PAPER_A6_H 7016 #define PAPER_B4_W 11811 #define PAPER_B4_H 16677 #define PAPER_B5_W 8598 #define PAPER_B5_H 12142 #define PAPER_LEGAL_W 10200 #define PAPER_LEGAL_H 16800 #define PAPER_LETTER_W 10200 #define PAPER_LETTER_H 13200 static const SANE_Range u8_range = { 0, /* minimum */ 255, /* maximum */ 0 /* quantization */ }; static const SANE_Range ibm2456_res_range = { 100, /* minimum */ 600, /* maximum */ 0 /* quantization */ }; static const SANE_Range default_x_range = { 0, /* minimum */ /* (SANE_Word) ( * DEFAULT_MUD), maximum */ 14032, /* maximum (found empirically for Gray mode) */ /* in Lineart mode it works till 14062 */ 2 /* quantization */ }; static const SANE_Range default_y_range = { 0, /* minimum */ /* (SANE_Word) (14 * DEFAULT_MUD), maximum */ 20410, /* maximum (found empirically) */ 2 /* quantization */ }; static inline void _lto2b(SANE_Int val, SANE_Byte *bytes) { bytes[0] = (val >> 8) & 0xff; bytes[1] = val & 0xff; } static inline void _lto3b(SANE_Int val, SANE_Byte *bytes) { bytes[0] = (val >> 16) & 0xff; bytes[1] = (val >> 8) & 0xff; bytes[2] = val & 0xff; } static inline void _lto4b(SANE_Int val, SANE_Byte *bytes) { bytes[0] = (val >> 24) & 0xff; bytes[1] = (val >> 16) & 0xff; bytes[2] = (val >> 8) & 0xff; bytes[3] = val & 0xff; } static inline SANE_Int _2btol(SANE_Byte *bytes) { SANE_Int rv; rv = (bytes[0] << 8) | bytes[1]; return (rv); } static inline SANE_Int _3btol(SANE_Byte *bytes) { SANE_Int rv; rv = (bytes[0] << 16) | (bytes[1] << 8) | bytes[2]; return (rv); } static inline SANE_Int _4btol(SANE_Byte *bytes) { SANE_Int rv; rv = (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]; return (rv); } typedef enum { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_MODE, OPT_X_RESOLUTION, OPT_Y_RESOLUTION, OPT_ADF, OPT_GEOMETRY_GROUP, OPT_PAPER, /* predefined formats */ OPT_TL_X, /* top-left x */ OPT_TL_Y, /* top-left y */ OPT_BR_X, /* bottom-right x */ OPT_BR_Y, /* bottom-right y */ OPT_ENHANCEMENT_GROUP, OPT_BRIGHTNESS, OPT_CONTRAST, /* must come last: */ NUM_OPTIONS } Ibm_Option; typedef struct Ibm_Info { SANE_Range xres_range; SANE_Range yres_range; SANE_Range x_range; SANE_Range y_range; SANE_Range brightness_range; SANE_Range contrast_range; SANE_Int xres_default; SANE_Int yres_default; SANE_Int image_mode_default; SANE_Int paper_default; SANE_Int brightness_default; SANE_Int contrast_default; SANE_Int adf_default; SANE_Int bmu; SANE_Int mud; } Ibm_Info; typedef struct Ibm_Device { struct Ibm_Device *next; SANE_Device sane; Ibm_Info info; } Ibm_Device; typedef struct Ibm_Scanner { /* all the state needed to define a scan request: */ struct Ibm_Scanner *next; int fd; /* SCSI filedescriptor */ SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; SANE_Parameters params; /* scanner dependent/low-level state: */ Ibm_Device *hw; SANE_Int xres; SANE_Int yres; SANE_Int ulx; SANE_Int uly; SANE_Int width; SANE_Int length; SANE_Int brightness; SANE_Int contrast; SANE_Int image_composition; SANE_Int bpp; SANE_Bool reverse; /* next lines by mf */ SANE_Int adf_state; #define ADF_UNUSED 0 /* scan from flatbed, not ADF */ #define ADF_ARMED 1 /* scan from ADF, everything's set up */ #define ADF_CLEANUP 2 /* eject paper from ADF on close */ /* end lines by mf */ size_t bytes_to_read; int scanning; } Ibm_Scanner; struct inquiry_data { SANE_Byte devtype; SANE_Byte byte2; SANE_Byte byte3; SANE_Byte byte4; SANE_Byte byte5; SANE_Byte res1[2]; SANE_Byte flags; SANE_Byte vendor[8]; SANE_Byte product[8]; SANE_Byte revision[4]; SANE_Byte byte[60]; }; #define IBM_WINDOW_DATA_SIZE 320 struct ibm_window_data { /* header */ SANE_Byte reserved[6]; SANE_Byte len[2]; /* data */ SANE_Byte window_id; /* must be zero */ SANE_Byte reserved0; SANE_Byte x_res[2]; SANE_Byte y_res[2]; SANE_Byte x_org[4]; SANE_Byte y_org[4]; SANE_Byte width[4]; SANE_Byte length[4]; SANE_Byte brightness; SANE_Byte threshold; SANE_Byte contrast; SANE_Byte image_comp; /* image composition (data type) */ SANE_Byte bits_per_pixel; SANE_Byte halftone_code; /* halftone_pattern[0] in ricoh.h */ SANE_Byte halftone_id; /* halftone_pattern[1] in ricoh.h */ SANE_Byte pad_type; SANE_Byte bit_ordering[2]; SANE_Byte compression_type; SANE_Byte compression_arg; SANE_Byte res3[6]; /* Vendor Specific parameter byte(s) */ /* Ricoh specific, follow the scsi2 standard ones */ SANE_Byte byte1; SANE_Byte byte2; SANE_Byte mrif_filtering_gamma_id; SANE_Byte byte3; SANE_Byte byte4; SANE_Byte binary_filter; SANE_Byte reserved2[18]; SANE_Byte reserved3[256]; }; struct measurements_units_page { SANE_Byte page_code; /* 0x03 */ SANE_Byte parameter_length; /* 0x06 */ SANE_Byte bmu; SANE_Byte res1; SANE_Byte mud[2]; SANE_Byte res2[2]; /* anybody know what `COH' may mean ??? */ /* next 4 lines by mf */ SANE_Byte adf_page_code; SANE_Byte adf_parameter_length; SANE_Byte adf_control; SANE_Byte res3[5]; }; struct mode_pages { SANE_Byte page_code; SANE_Byte parameter_length; SANE_Byte rest[14]; /* modified by mf; it was 6; see above */ #if 0 SANE_Byte more_pages[243]; /* maximum size 255 bytes (incl header) */ #endif }; #endif /* ibm_h */ backends-1.3.0/backend/kodak-cmd.h000066400000000000000000000613531456256263500167050ustar00rootroot00000000000000#ifndef KODAK_CMD_H #define KODAK_CMD_H /* * Part of SANE - Scanner Access Now Easy. * * Please see to opening comments in kodak.c */ /* ==================================================================== */ /* helper char array manipulation functions */ static void setbitfield (unsigned char *pageaddr, int mask, int shift, int val) { *pageaddr = (*pageaddr & ~(mask << shift)) | ((val & mask) << shift); } static int getbitfield (unsigned char *pageaddr, int shift, int mask) { return ((*pageaddr >> shift) & mask); } static int getnbyte (unsigned char *pnt, int nbytes) { unsigned int result = 0; int i; for (i = 0; i < nbytes; i++) result = (result << 8) | (pnt[i] & 0xff); return result; } static void putnbyte (unsigned char *pnt, unsigned int value, unsigned int nbytes) { int i; for (i = nbytes - 1; i >= 0; i--) { pnt[i] = value & 0xff; value = value >> 8; } } /* ==================================================================== */ /* SCSI commands */ #define set_SCSI_opcode(out, val) out[0]=val #define set_SCSI_lun(out, val) setbitfield(out + 1, 7, 5, val) /* ==================================================================== */ /* TEST_UNIT_READY */ #define TEST_UNIT_READY_code 0x00 #define TEST_UNIT_READY_len 6 /* ==================================================================== */ /* REQUEST_SENSE */ /* out */ #define REQUEST_SENSE_code 0x03 #define REQUEST_SENSE_len 6 #define RS_return_size 0x12 /* in */ #define get_RS_information_valid(b) getbitfield(b + 0x00, 7, 1) #define get_RS_error_code(b) getbitfield(b + 0x00, 0, 0x7f) #define RS_error_code_current 0x70 #define RS_error_code_deferred 0x71 #define get_RS_segment_num(b) b[1] #define get_RS_filemark(b) getbitfield(b + 0x02, 7, 1) #define get_RS_EOM(b) getbitfield(b + 0x02, 6, 1) #define get_RS_ILI(b) getbitfield(b + 0x02, 5, 1) #define get_RS_sense_key(b) getbitfield(b + 0x02, 0, 0x0f) #define get_RS_information(b) getnbyte(b+0x03, 4) #define get_RS_additional_length(b) b[0x07] #define get_RS_cmd_info(b) getnbyte(b+8, 4) #define get_RS_ASC(b) b[0x0c] #define get_RS_ASCQ(b) b[0x0d] #define get_RS_FRUC(b) b[0x0e] #define get_RS_SKSV(b) getbitfield(b+0x0f,7,1) #define get_RS_SKSB(b) getnbyte(b+0x0f, 3) /* ==================================================================== */ /* INQUIRY */ /* out */ #define INQUIRY_code 0x12 #define INQUIRY_len 6 #define set_I_evpd(out, val) setbitfield(out + 1, 1, 0, val) #define set_I_page_code(out, val) out[0x02]=val #define I_page_code_default 0 #define set_I_data_length(out,n) out[0x04]=n #define I_data_len 128 /* in */ #define get_I_periph_qual(in) getbitfield(in, 5, 7) #define I_periph_qual_valid 0x00 #define I_periph_qual_nolun 0x03 #define get_I_periph_devtype(in) getbitfield(in, 0, 0x1f) #define I_periph_devtype_scanner 0x06 #define I_periph_devtype_unknown 0x1f /* don't use these, until vendor */ #define get_I_rmb(in) getbitfield(in + 1, 7, 1) #define get_I_devtype_qual(in) getbitfield(in + 1, 0, 0x7f) #define get_I_iso_version(in) getbitfield(in + 2, 6, 3) #define get_I_ecma_version(in) getbitfield(in + 2, 3, 7) #define get_I_ansi_version(in) getbitfield(in + 2, 0, 7) #define get_I_aenc(in) getbitfield(in + 3, 7, 1) #define get_I_trmiop(in) getbitfield(in + 3, 6, 1) #define get_I_resonse_format(in) getbitfield(in + 3, 0, 0x0f) #define get_I_length(in) getnbyte(in + 4, 1) #define get_I_reladr(in) getbitfield(in + 7, 7, 1) #define get_I_wbus32(in) getbitfield(in + 7, 6, 1) #define get_I_wbus16(in) getbitfield(in + 7, 5, 1) #define get_I_sync(in) getbitfield(in + 7, 4, 1) #define get_I_linked(in) getbitfield(in + 7, 3, 1) #define get_I_cmdque(in) getbitfield(in + 7, 1, 1) #define get_I_sftre(in) getbitfield(in + 7, 0, 1) #define get_I_vendor(in, buf) snprintf(buf, 0x08 + 1, "%.*s", \ 0x08, (char *)in + 0x08) #define get_I_product(in, buf) snprintf(buf, 0x10 + 1, "%.*s", \ 0x10, (char *)in + 0x10) #define get_I_version(in, buf) snprintf(buf, 0x04 + 1, "%.*s", \ 0x04, (char *)in + 0x20) #define get_I_build(in, buf) snprintf(buf, 0x02 + 1, "%.*s", \ 0x02, (char *)in + 0x24) #define get_I_mf_disable(in) getbitfield(in + 38, 7, 1) #define get_I_checkdigit(in) getbitfield(in + 38, 6, 1) #define get_I_front_prism(in) getbitfield(in + 38, 5, 1) #define get_I_compressed_gray(in) getbitfield(in + 38, 4, 1) #define get_I_front_toggle(in) getbitfield(in + 38, 3, 1) #define get_I_front_dp1(in) getbitfield(in + 38, 2, 1) #define get_I_front_color(in) getbitfield(in + 38, 1, 1) #define get_I_front_atp(in) getbitfield(in + 38, 0, 1) #define get_I_dp1_180(in) getbitfield(in + 39, 7, 1) #define get_I_mf_pause(in) getbitfield(in + 39, 6, 1) #define get_I_rear_prism(in) getbitfield(in + 39, 5, 1) #define get_I_uncompressed_gray(in) getbitfield(in + 39, 4, 1) #define get_I_rear_toggle(in) getbitfield(in + 39, 3, 1) #define get_I_rear_dp1(in) getbitfield(in + 39, 2, 1) #define get_I_rear_color(in) getbitfield(in + 39, 1, 1) #define get_I_rear_atp(in) getbitfield(in + 39, 0, 1) #define get_I_min_bin_res(in) getnbyte(in + 40, 2) #define get_I_max_bin_res(in) getnbyte(in + 42, 2) #define get_I_min_col_res(in) getnbyte(in + 44, 2) #define get_I_max_col_res(in) getnbyte(in + 46, 2) #define get_I_max_image_width(in) getnbyte(in + 48, 4) #define get_I_max_image_length(in) getnbyte(in + 52, 4) /* 56-95 reserved */ #define get_I_finecrop(in) getbitfield(in + 96, 7, 1) #define get_I_ithresh(in) getbitfield(in + 96, 6, 1) #define get_I_ecd(in) getbitfield(in + 96, 3, 1) #define get_I_vblr(in) getbitfield(in + 96, 2, 1) #define get_I_elevator(in) getbitfield(in + 96, 1, 1) #define get_I_relcrop(in) getbitfield(in + 96, 0, 1) #define get_I_cdeskew(in) getbitfield(in + 97, 7, 1) #define get_I_ia(in) getbitfield(in + 97, 6, 1) #define get_I_patch(in) getbitfield(in + 97, 5, 1) #define get_I_nullmode(in) getbitfield(in + 97, 4, 1) #define get_I_sabre(in) getbitfield(in + 97, 3, 1) #define get_I_lddds(in) getbitfield(in + 97, 2, 1) #define get_I_uddds(in) getbitfield(in + 97, 1, 1) #define get_I_fixedgap(in) getbitfield(in + 97, 0, 1) #define get_I_hr_printer(in) getbitfield(in + 98, 6, 1) #define get_I_elev_100_250(in) getbitfield(in + 98, 5, 1) #define get_I_udds_individual(in) getbitfield(in + 98, 4, 1) #define get_I_auto_color(in) getbitfield(in + 98, 3, 1) #define get_I_wb(in) getbitfield(in + 98, 2, 1) #define get_I_es(in) getbitfield(in + 98, 1, 1) #define get_I_fc(in) getbitfield(in + 98, 0, 1) #define get_I_max_rate(in) getnbyte(in + 99, 2) #define get_I_buffer_size(in) getnbyte(in + 101, 4) /* ==================================================================== */ /* RESERVE UNIT */ #define RESERVE_UNIT_code 0x16 #define RESERVE_UNIT_len 6 /* ==================================================================== */ /* RELEASE UNIT */ #define RELEASE_UNIT_code 0x17 #define RELEASE_UNIT_len 6 /* ==================================================================== */ /* SCAN */ #define SCAN_code 0x1b #define SCAN_len 10 #define set_SC_xfer_length(sb, val) sb[0x04] = (unsigned char)val /* ==================================================================== */ /* SEND DIAGNOSTIC */ #define SEND_DIAGNOSTIC_code 0x1d #define SEND_DIAGNOSTIC_len 6 /* ==================================================================== */ /* SET WINDOW */ #define SET_WINDOW_code 0x24 #define SET_WINDOW_len 10 /* transfer length is header+descriptor (8+64) */ #define set_SW_xferlen(sb, len) putnbyte(sb + 0x06, len, 3) /* ==================================================================== */ /* GET WINDOW */ #define GET_WINDOW_code 0x25 #define GET_WINDOW_len 10 #define set_GW_single(sb, val) setbitfield(sb + 1, 1, 0, val) #define set_GW_wid(sb, len) sb[5] = len /* window transfer length is for following header+descriptor (8+64) */ #define set_GW_xferlen(sb, len) putnbyte(sb + 0x06, len, 3) /* ==================================================================== */ /* WINDOW HEADER, used by get and set */ #define WINDOW_HEADER_len 8 /* header transfer length is for following descriptor (64) */ #define set_WH_data_len(sb, len) putnbyte(sb, len, 2) #define set_WH_desc_len(sb, len) putnbyte(sb + 0x06, len, 2) /* ==================================================================== */ /* WINDOW descriptor, used by get and set */ #define WINDOW_DESCRIPTOR_len 64 #define set_WD_wid(sb, val) sb[0]=val #define WD_wid_front_binary 0x01 #define WD_wid_back_binary 0x02 #define WD_wid_front_color 0x03 #define WD_wid_back_color 0x04 /* color/gray = 100,150,200,240,300 */ /* binary = 200,240,300,400 */ #define set_WD_Xres(sb, val) putnbyte(sb + 0x02, val, 2) #define get_WD_Xres(sb) getnbyte(sb + 0x02, 2) /* color/gray = 100,150,200,240,300 */ /* binary = 200,240,300,400 */ #define set_WD_Yres(sb, val) putnbyte(sb + 0x04, val, 2) #define get_WD_Yres(sb) getnbyte(sb + 0x04, 2) #define set_WD_ULX(sb, val) putnbyte(sb + 0x06, val, 4) #define get_WD_ULX(sb) getnbyte(sb + 0x06, 4) #define set_WD_ULY(sb, val) putnbyte(sb + 0x0a, val, 4) #define get_WD_ULY(sb) getnbyte(sb + 0x0a, 4) #define set_WD_width(sb, val) putnbyte(sb + 0x0e, val, 4) #define get_WD_width(sb) getnbyte(sb + 0x0e, 4) #define set_WD_length(sb, val) putnbyte(sb + 0x12, val, 4) #define get_WD_length(sb) getnbyte(sb + 0x12, 4) #define set_WD_brightness(sb, val) sb[0x16] = val #define get_WD_brightness(sb) sb[0x16] #define set_WD_threshold(sb, val) sb[0x17] = val #define get_WD_threshold(sb) sb[0x17] #define set_WD_contrast(sb, val) sb[0x18] = val #define get_WD_contrast(sb) sb[0x18] #define set_WD_composition(sb, val) sb[0x19] = val #define get_WD_composition(sb) sb[0x19] #define WD_compo_LINEART 0 #define WD_compo_HALFTONE 1 #define WD_compo_MULTILEVEL 5 /* 1, 8, or 24 */ #define set_WD_bitsperpixel(sb, val) sb[0x1a] = val #define get_WD_bitsperpixel(sb) sb[0x1a] /* not used? */ #define set_WD_halftone(sb, val) putnbyte(sb + 0x1b, val, 2) #define get_WD_halftone(sb) getnbyte(sb + 0x1b, 2) #define set_WD_rif(sb, val) setbitfield(sb + 0x1d, 1, 7, val) #define get_WD_rif(sb) getbitfield(sb + 0x1d, 7, 1) /* always set to 1? */ #define set_WD_bitorder(sb, val) putnbyte(sb + 0x1e, val, 2) #define get_WD_bitorder(sb) getnbyte(sb + 0x1e, 2) #define set_WD_compress_type(sb, val) sb[0x20] = val #define get_WD_compress_type(sb) sb[0x20] #define WD_compr_NONE 0 #define WD_compr_FAXG4 3 #define WD_compr_JPEG 0x80 /* not used? */ #define set_WD_compress_arg(sb, val) sb[0x21] = val #define get_WD_compress_arg(sb) sb[0x21] /* kodak specific stuff starts here */ #define set_WD_noise_filter(sb, val) setbitfield(sb + 40, 7, 2, val) #define WD_nf_NONE 0 #define WD_nf_LONE 1 #define WD_nf_MAJORITY 2 #define get_WD_noise_filter(sb) getbitfield(sb + 40, 7, 1) #define set_WD_allow_zero(sb, val) setbitfield(sb + 40, 1, 1, val) #define get_WD_allow_zero(sb, val) getbitfield(sb + 40, 1, 1) #define set_WD_vblr(sb, val) setbitfield(sb + 40, 1, 0, val) #define get_WD_vblr(sb, val) getbitfield(sb + 40, 0, 1) #define set_WD_image_overscan(sb, val) putnbyte(sb + 41, val, 2) #define set_WD_IS(sb, val) setbitfield(sb + 43, 1, 7, val) #define set_WD_deskew(sb, val) setbitfield(sb + 43, 1, 6, val) #define WD_deskew_DISABLE 0 #define WD_deskew_ENABLE 1 #define set_WD_BR(sb, val) setbitfield(sb + 43, 1, 5, val) #define set_WD_BF(sb, val) setbitfield(sb + 43, 1, 4, val) #define set_WD_ED(sb, val) setbitfield(sb + 43, 1, 3, val) #define set_WD_HR(sb, val) setbitfield(sb + 43, 1, 2, val) #define set_WD_cropping(sb, val) setbitfield(sb + 43, 3, 0, val) #define WD_crop_AUTO 0 #define WD_crop_FIXED 1 #define WD_crop_RELATIVE 2 #define WD_crop_FINE 3 #define set_WD_ithresh(sb, val) setbitfield(sb + 44, 1, 7, val) #define WD_ithresh_DISABLE 0 #define WD_ithresh_ENABLE 1 #define set_WD_dropout_color(sb, val) setbitfield(sb + 44, 7, 0, val) #define set_WD_dropout_bg(sb, val) sb[45] = val #define set_WD_dropout_thresh(sb, val) sb[46] = val #define set_WD_dropout_source(sb, val) sb[47] = val #define set_WD_disable_gamma(sb, val) sb[48] = val #define set_WD_ortho_rotation(sb, val) sb[49] = val #define set_WD_fine_crop_max(sb, val) putnbyte(sb + 50, val, 2) #define set_WD_color_detect_thresh(sb, val) sb[52] = val #define set_WD_color_detect_area(sb, val) sb[53] = val #define set_WD_border_add(sb, val) sb[54] = val #define max_WDB_size 0xc8 /* ==================================================================== */ /* READ */ #define READ_code 0x28 #define READ_len 10 /* ==================================================================== */ /* SEND */ #define SEND_code 0x2a #define SEND_len 10 /* ==================================================================== */ /* READ and SEND are basically the same, so the following 'SR' macros */ /* output */ #define set_SR_datatype_code(sb, val) sb[0x02] = val #define SR_datatype_imagedata 0x00 #define SR_datatype_random 0x80 #define SR_datatype_imageheader 0x81 #define SR_len_imageheader 1088 #define set_SR_datatype_qual(sb, val) memcpy(sb + 4, val, 2) #define set_SR_xfer_length(sb, val) putnbyte(sb + 6, val, 3) /* if the data type is 'imageheader': */ #define get_SR_ih_header_length(in) getnbyte(in + 0x00, 4) #define get_SR_ih_image_length(in) getnbyte(in + 0x04, 4) #define get_SR_ih_image_id(in) in[8] #define get_SR_ih_resolution(in) getnbyte(in + 9, 2) #define get_SR_ih_ulx(in) getnbyte(in + 11, 4) #define get_SR_ih_uly(in) getnbyte(in + 15, 4) #define get_SR_ih_width(in) getnbyte(in + 19, 4) #define get_SR_ih_length(in) getnbyte(in + 23, 4) #define get_SR_ih_bpp(in) in[27] #define get_SR_ih_comp_type(in) in[28] #define get_SR_ih_ACD(in) getbit(in + 29, 1, 5) #define get_SR_ih_MF(in) getbit(in + 29, 1, 4) #define get_SR_ih_RIF(in) getbit(in + 29, 1, 2) #define get_SR_ih_ID(in) getbit(in + 29, 1, 1) #define get_SR_ih_DE(in) getbit(in + 29, 1, 0) #define get_SR_ih_skew_angle(in) getnbyte(in + 30, 2) #define get_SR_ih_ia_level(in) in[32] #define get_SR_ih_ia(in) getnbyte(in + 33, 60) #define get_SR_ih_print_string(in) getnbyte(in + 93, 60) #define get_SR_ih_seqence_counter(in) getnbyte(in + 173, 4) #define get_SR_ih_ia_f1_definition(in) in[177] #define get_SR_ih_ia_f1_value(in) getnbyte(in + 178, 18) #define get_SR_ih_ia_f2_definition(in) in[196] #define get_SR_ih_ia_f2_value(in) getnbyte(in + 197, 18) #define get_SR_ih_ia_f3_definition(in) in[215] #define get_SR_ih_ia_f3_value(in) getnbyte(in + 216, 18) #define get_SR_ih_ia_f4_definition(in) in[234] #define get_SR_ih_ia_f5_value(in) getnbyte(in + 235, 18) #define get_SR_ih_PD(in) getbit(in + 253, 1, 7) #define get_SR_ih_patch_type(in) getbit(in + 253, 0x7f, 0) #define get_SR_ih_deskew_confidence(in) in[255] #define get_SR_ih_bt_contrast_perc(in) in[256] #define get_SR_ih_bt_contrast(in) getnbyte(in + 257, 2) #define get_SR_ih_bt_threshold(in) in[259] #define get_SR_ih_sum_histogram(in) getnbyte(in + 260, 256) #define get_SR_ih_diff_histogram(in) getnbyte(in + 516, 256) #define get_SR_ih_gamma_table(in) getnbyte(in + 772, 256) #define get_SR_ih_auto_color_area(in) in[1028] #define get_SR_ih_auto_color_thresh(in) in[1029] /* ==================================================================== */ /* if the data type is 'random', we have all kinds of 2 byte */ /* qualifiers and oddly sized commands. some are bidirectional */ /* while others are only send or read */ /* all payloads for these seem to start with 4 byte inclusive length? */ #define set_SR_payload_len(sb, val) putnbyte(sb, val, 4) /* */ #define SR_qual_jpeg_quant "JQ" /*both*/ #define SR_len_jpeg_quant 262 /*front and back tables*/ /* */ #define SR_qual_config "SC" /*both*/ #define SR_len_config 512 /*lots of stuff*/ #define set_SR_sc_length(in,val) putnbyte(in, val, 4) #define get_SR_sc_length(in) getnbyte(in, 4) #define set_SR_sc_io1(in,val) putnbyte(in + 4, val, 1) #define get_SR_sc_io1(in) getnbyte(in + 4, 1) #define set_SR_sc_io2(in,val) putnbyte(in + 5, val, 1) #define get_SR_sc_io2(in) getnbyte(in + 5, 1) #define set_SR_sc_io3(in,val) putnbyte(in + 6, val, 1) #define get_SR_sc_io3(in) getnbyte(in + 6, 1) #define set_SR_sc_io4(in,val) putnbyte(in + 7, val, 1) #define get_SR_sc_io4(in) getnbyte(in + 7, 1) #define SR_sc_io_none 0 #define SR_sc_io_front_binary 1 #define SR_sc_io_rear_binary 2 #define SR_sc_io_front_color 3 #define SR_sc_io_rear_color 4 #define set_SR_sc_trans_to(in,val) putnbyte(in + 13, val, 2) #define get_SR_sc_trans_to(in) getnbyte(in + 13, 2) #define set_SR_sc_trans_to_resp(in,val) putnbyte(in + 15, val, 1) #define get_SR_sc_trans_to_resp(in) getnbyte(in + 15, 1) #define get_SR_sc_DLS(in) getbitfield(in + 16, 7, 1) #define set_SR_sc_DLS(in, val) setbitfield(in + 16, 1, 7) #define get_SR_sc_DMS(in) getbitfield(in + 16, 6, 1) #define set_SR_sc_DMS(in, val) setbitfield(in + 16, 1, 6) #define get_SR_sc_DRS(in) getbitfield(in + 16, 5, 1) #define set_SR_sc_DRS(in, val) setbitfield(in + 16, 1, 5) #define get_SR_sc_UD_mode(in) getbitfield(in + 16, 0, 0x1f) #define set_SR_sc_UD_mode(in, val) setbitfield(in + 16, 0x1f, 0) #define get_SR_sc_LD_length(in) getnbyte(in + 17, 2) #define set_SR_sc_LD_length(in, val) setnbyte(in + 17, 2) #define get_SR_sc_DF_resp(in) getnbyte(in + 19, 1) #define set_SR_sc_DF_resp(in, val) setnbyte(in + 19, 1) #define get_SR_sc_TP_mode(in) getnbyte(in + 20, 1) #define get_SR_sc_CPT(in) getbitfield(in + 21, 0, 1) #define get_SR_sc_POD_mode(in) getnbyte(in + 22, 2) #define get_SR_sc_batch_mode(in) getnbyte(in + 24, 2) #define get_SR_sc_batch_level(in) getnbyte(in + 26, 1) #define get_SR_sc_start_batch(in) getnbyte(in + 27, 1) #define get_SR_sc_end_batch(in) getnbyte(in + 28, 1) #define get_SR_sc_patch_conf_tone(in) getnbyte(in + 29, 1) #define get_SR_sc_T6_patch(in) getbitfield(in + 30, 7, 1) #define get_SR_sc_T5_patch(in) getbitfield(in + 30, 6, 1) #define get_SR_sc_T4_patch(in) getbitfield(in + 30, 5, 1) #define get_SR_sc_T3_patch(in) getbitfield(in + 30, 4, 1) #define get_SR_sc_T2_patch(in) getbitfield(in + 30, 3, 1) #define get_SR_sc_T1_patch(in) getbitfield(in + 30, 2, 1) #define get_SR_sc_trans_patch(in) getbitfield(in + 30, 0, 3) #define get_SR_sc_IA_A_start(in) getnbyte(in + 31, 18) #define get_SR_sc_IA_B_start(in) getnbyte(in + 49, 18) #define get_SR_sc_IA_C_start(in) getnbyte(in + 67, 18) #define get_SR_sc_IA_D_start(in) getnbyte(in + 85, 18) #define get_SR_sc_IA_A_def(in) getnbyte(in + 103, 1) #define get_SR_sc_IA_B_def(in) getnbyte(in + 104, 1) #define get_SR_sc_IA_C_def(in) getnbyte(in + 105, 1) #define get_SR_sc_IA_D_def(in) getnbyte(in + 106, 1) #define get_SR_sc_IA_ltf_3(in) getnbyte(in + 107, 1) #define get_SR_sc_IA_ltf_2(in) getnbyte(in + 108, 1) #define get_SR_sc_IA_ltf_1(in) getnbyte(in + 109, 1) #define get_SR_sc_DP1_pos(in) getnbyte(in + 110, 4) #define get_SR_sc_hires_mode(in) getbitfield(in + 114, 4, 0xf) #define get_SR_sc_DP1_font(in) getbitfield(in + 114, 0, 0xf) #define get_SR_sc_DP1_orient(in) getnbyte(in + 115, 1) #define get_SR_sc_DP1_IA_format(in) getnbyte(in + 116, 1) #define get_SR_sc_DP1_date_format(in) getnbyte(in + 117, 1) #define get_SR_sc_DP1_date_delim(in) getnbyte(in + 118, 1) #define get_SR_sc_DP1_start_seq(in) getnbyte(in + 119, 4) #define get_SR_sc_DP1_seq_date_format(in) getnbyte(in + 123, 1) #define get_SR_sc_DP1_seq_print_width(in) getnbyte(in + 124, 1) #define get_SR_sc_DP1_msg1(in) getnbyte(in + 125, 40) #define get_SR_sc_DP1_msg2(in) getnbyte(in + 165, 40) #define get_SR_sc_DP1_msg3(in) getnbyte(in + 205, 40) #define get_SR_sc_DP1_msg4(in) getnbyte(in + 245, 40) #define get_SR_sc_DP1_msg5(in) getnbyte(in + 285, 40) #define get_SR_sc_DP1_msg6(in) getnbyte(in + 325, 40) #define get_SR_sc_DP1_l1_template(in) getnbyte(in + 365, 20) #define get_SR_sc_DP1_l2_template(in) getnbyte(in + 385, 20) #define get_SR_sc_DP1_l3_template(in) getnbyte(in + 405, 20) #define get_SR_sc_UDFK1(in) getnbyte(in + 425, 1) #define get_SR_sc_UDFK2(in) getnbyte(in + 426, 1) #define get_SR_sc_UDFK3(in) getnbyte(in + 427, 1) #define get_SR_sc_MPASS(in) getbitfield(in + 428, 1, 1) #define get_SR_sc_CE(in) getbitfield(in + 428, 0, 1) #define get_SR_sc_elevator_mode(in) getnbyte(in + 429, 1) #define get_SR_sc_stacking_mode(in) getnbyte(in + 430, 1) #define get_SR_sc_energy_star_to(in) getnbyte(in + 433, 1) #define get_SR_sc_PR1(in) getbitfield(in + 435, 3, 1) #define get_SR_sc_PR2(in) getbitfield(in + 435, 2, 1) #define get_SR_sc_PR3(in) getbitfield(in + 435, 1, 1) #define get_SR_sc_PR4(in) getbitfield(in + 435, 0, 1) /* */ #define SR_qual_color_table "CT" /*read*/ #define SR_len_color_table 52 /* */ #define SR_qual_color_table "CT" /*read*/ #define SR_len_color_table 52 /* */ #define SR_qual_clear "CB" /*send*/ #define SR_len_clear 0 /* */ #define SR_qual_end "GX" /*send*/ #define SR_len_end 0 /* local and gmt time commands, same format */ #define SR_qual_clock "LC" /*both*/ #define SR_qual_gmt "GT" /*send*/ #define SR_len_time 10 #define set_SR_time_hour(sb, val) putnbyte(sb+4, val, 1) #define set_SR_time_min(sb, val) putnbyte(sb+5, val, 1) #define set_SR_time_mon(sb, val) putnbyte(sb+6, val, 1) #define set_SR_time_day(sb, val) putnbyte(sb+7, val, 1) #define set_SR_time_year(sb, val) putnbyte(sb+8, val, 2) /* */ #define SR_qual_lamp "LD" /*????*/ #define SR_len_lamp 0 /*????*/ /* */ #define SR_qual_log_en "L0" /*read*/ #define SR_qual_log_fr "L1" /*read*/ #define SR_qual_log_it "L2" /*read*/ #define SR_qual_log_de "L3" /*read*/ #define SR_qual_log_br "L4" /*read*/ #define SR_qual_log_jp "L5" /*read*/ #define SR_qual_log_nl "L6" /*read*/ #define SR_qual_log_es "L7" /*read*/ #define SR_qual_log_cns "L8" /*read*/ #define SR_qual_log_cnt "L9" /*read*/ #define SR_qual_log_ko "LA" /*read*/ #define SR_qual_log_ru "LB" /*read*/ #define SR_qual_log_cz "LE" /*read*/ #define SR_qual_log_tr "LF" /*read*/ /* */ #define SR_qual_startstop "SS" /*send*/ #define SR_len_startstop 5 #define set_SR_startstop_cmd(sb, val) putnbyte(sb+4, val, 1) /* */ #define SR_qual_media "CM" /*read*/ #define SR_len_media 5 #define get_SR_media_status(sb, val) getnbyte(sb+4, 1) /* */ #define SR_qual_img_cal "IC" /*send*/ #define SR_len_img_cal 0 /* */ #define SR_qual_udds_cal "UC" /*send*/ #define SR_len_udds_cal 0 /* ==================================================================== */ /* WRITE BUFFER */ #define WRITE_BUFFER_code 0x3b #define WRITE_BUFFER_len 10 #define set_WB_mode(sb, val) setbitfield(sb + 0x01, 7, 0, val) #define WB_mode_front 6 #define WB_mode_back 7 #define set_WB_buffer_id(sb, val) sb[0x02] = (unsigned char)val #define WB_buffer_id_block(sb, val) 0 #define WB_buffer_id_last(sb, val) 1 #define set_WB_buffer_offset(sb, val) putnbyte(sb + 3, val, 3) #define set_WB_list_length(sb, val) putnbyte(sb + 6, val, 3) #endif backends-1.3.0/backend/kodak.c000066400000000000000000002363271456256263500161440ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. This file is part of the SANE package, and implements a SANE backend for various large Kodak scanners. Copyright (C) 2008-2010 m. allan noah -------------------------------------------------------------------------- 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, see . 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. -------------------------------------------------------------------------- The source code is divided in sections which you can easily find by searching for the tag "@@". Section 1 - Init & static stuff 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: v0 through v5 2008-01-15, MAN - development versions v6 2009-06-22, MAN - improved set_window() to build descriptor from scratch - initial release v7 2010-02-10, MAN - add SANE_I18N to static strings - don't fail if scsi buffer is too small SANE FLOW DIAGRAM - 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 . . - sane_read() : read image data (from pipe) . . (sane_read called multiple times; after sane_read returns EOF, . . loop may continue with sane_start which may return a 2nd page . . when doing duplex scans, or load the next page from the ADF) . . . . - sane_cancel() : cancel operation . - sane_close() : close opened scanner device - sane_exit() : terminate use of backend */ /* * @@ Section 1 - Init */ #include "sane/config.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_LIBC_H # include /* NeXTStep/OpenStep */ #endif #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_scsi.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_config.h" #include "kodak-cmd.h" #include "kodak.h" #define DEBUG 1 #define BUILD 7 /* values for SANE_DEBUG_KODAK env var: - errors 5 - function trace 10 - function detail 15 - get/setopt cmds 20 - scsi cmd trace 25 - scsi cmd detail 30 - useless noise 35 */ /* ------------------------------------------------------------------------- */ #define STRING_ADFFRONT SANE_I18N("ADF Front") #define STRING_ADFBACK SANE_I18N("ADF Back") #define STRING_ADFDUPLEX SANE_I18N("ADF Duplex") #define STRING_LINEART SANE_VALUE_SCAN_MODE_LINEART #define STRING_HALFTONE SANE_VALUE_SCAN_MODE_HALFTONE #define STRING_GRAYSCALE SANE_VALUE_SCAN_MODE_GRAY #define STRING_COLOR SANE_VALUE_SCAN_MODE_COLOR /* Also set via config file. */ static int global_buffer_size = DEFAULT_BUFFER_SIZE; /* * 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 scanner structs */ static const SANE_Device **sane_devArray = NULL; static struct scanner *scanner_devList = NULL; /* * @@ Section 2 - SANE & scanner init code */ /* * 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) { (void) authorize; /* get rid of compiler warning */ DBG_INIT (); DBG (10, "sane_init: start\n"); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); DBG (5, "sane_init: kodak backend %d.%d.%d, from %s\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD, PACKAGE_STRING); DBG (10, "sane_init: finish\n"); 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. */ /* Read the config file, find scanners with help from sanei_* * store in two global lists of device structs */ SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { struct scanner *dev; char line[PATH_MAX]; const char *lp; FILE *fp; int num_devices=0; int i=0; (void) local_only; /* get rid of compiler warning */ DBG (10, "sane_get_devices: start\n"); /* set this to default before reading the file */ global_buffer_size = DEFAULT_BUFFER_SIZE; fp = sanei_config_open (KODAK_CONFIG_FILE); if (fp) { DBG (15, "sane_get_devices: reading config file %s\n", KODAK_CONFIG_FILE); while (sanei_config_read (line, PATH_MAX, fp)) { lp = line; /* ignore comments */ if (*lp == '#') continue; /* 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, "sane_get_devices: config option \"buffer-size\" \ (%d) is < 4096, ignoring!\n", buf); continue; } if (buf > DEFAULT_BUFFER_SIZE) { DBG (5, "sane_get_devices: config option \"buffer-size\" \ (%d) is > %d, warning!\n", buf, DEFAULT_BUFFER_SIZE); } DBG (15, "sane_get_devices: setting \"buffer-size\" to %d\n", buf); global_buffer_size = buf; } else { DBG (5, "sane_get_devices: config option \"%s\" \ unrecognized\n", lp); } } else if ((strncmp ("scsi", lp, 4) == 0) && isspace (lp[4])) { DBG (15, "sane_get_devices: looking for '%s'\n", lp); sanei_config_attach_matching_devices (lp, attach_one); } else{ DBG (5, "sane_get_devices: config line \"%s\" unrecognized\n", lp); } } fclose (fp); } else { DBG (5, "sane_get_devices: no config file '%s', using defaults\n", KODAK_CONFIG_FILE); DBG (15, "sane_get_devices: looking for 'scsi KODAK'\n"); sanei_config_attach_matching_devices ("scsi KODAK", attach_one); } for (dev = scanner_devList; dev; dev=dev->next) { DBG (15, "sane_get_devices: found scanner %s\n",dev->device_name); num_devices++; } DBG (15, "sane_get_devices: found %d scanner(s)\n",num_devices); sane_devArray = calloc (num_devices + 1, sizeof (SANE_Device*)); if (!sane_devArray) return SANE_STATUS_NO_MEM; for (dev = scanner_devList; dev; dev=dev->next) { sane_devArray[i++] = (SANE_Device *)&dev->sane; } sane_devArray[i] = 0; if(device_list){ *device_list = sane_devArray; } DBG (10, "sane_get_devices: finish\n"); return SANE_STATUS_GOOD; } /* build the scanner struct and link to global list * unless struct is already loaded, then pretend */ static SANE_Status attach_one (const char *device_name) { struct scanner *s; int ret; DBG (10, "attach_one: start\n"); DBG (15, "attach_one: looking for '%s'\n", device_name); for (s = scanner_devList; s; s = s->next) { if (strcmp (s->sane.name, device_name) == 0) { DBG (10, "attach_one: already attached!\n"); return SANE_STATUS_GOOD; } } /* build a struct to hold it */ if ((s = calloc (sizeof (*s), 1)) == NULL) return SANE_STATUS_NO_MEM; /* scsi command/data buffer */ s->buffer_size = global_buffer_size; /* copy the device name */ s->device_name = strdup (device_name); if (!s->device_name){ free (s); return SANE_STATUS_NO_MEM; } /* connect the fd */ s->fd = -1; ret = connect_fd(s); if(ret != SANE_STATUS_GOOD){ free (s->device_name); free (s); return ret; } /* 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; } /* clean up the scanner struct based on model */ /* this is the only piece of model specific code */ 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; } /* sets user 'values' to good defaults */ ret = init_user (s); if (ret != SANE_STATUS_GOOD) { disconnect_fd(s); free (s->device_name); free (s); DBG (5, "attach_one: user failed\n"); return ret; } /* sets SANE option 'values' to good defaults */ 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; } /* 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"; s->next = scanner_devList; scanner_devList = s; DBG (10, "attach_one: finish\n"); return SANE_STATUS_GOOD; } /* * connect the fd in the scanner struct */ static SANE_Status connect_fd (struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; int buffer_size = s->buffer_size; DBG (10, "connect_fd: start\n"); if(s->fd > -1){ DBG (5, "connect_fd: already open\n"); ret = SANE_STATUS_GOOD; } else { ret = sanei_scsi_open_extended (s->device_name, &(s->fd), sense_handler, s, &s->buffer_size); if(!ret && buffer_size != s->buffer_size){ DBG (5, "connect_fd: cannot get requested buffer size (%d/%d)\n", buffer_size, s->buffer_size); } else{ DBG (15, "connect_fd: opened SCSI device\n"); } } DBG (10, "connect_fd: finish %d\n", ret); return ret; } /* * This routine will check if a certain device is a Kodak scanner * It also copies interesting data from INQUIRY into the handle structure */ static SANE_Status init_inquire (struct scanner *s) { int i; SANE_Status ret; unsigned char cmd[INQUIRY_len]; size_t cmdLen = INQUIRY_len; unsigned char in[I_data_len]; size_t inLen = I_data_len; DBG (10, "init_inquire: start\n"); memset(cmd,0,cmdLen); set_SCSI_opcode(cmd, INQUIRY_code); set_I_evpd (cmd, 0); set_I_page_code (cmd, I_page_code_default); set_I_data_length (cmd, inLen); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, in, &inLen ); if (ret != SANE_STATUS_GOOD){ return ret; } if (get_I_periph_qual(in) != I_periph_qual_valid){ DBG (5, "The device at '%s' has invalid periph_qual.\n", s->device_name); return SANE_STATUS_INVAL; } if (get_I_periph_devtype(in) != I_periph_devtype_scanner){ DBG (5, "The device at '%s' is not a scanner.\n", s->device_name); return SANE_STATUS_INVAL; } get_I_vendor (in, s->vendor_name); get_I_product (in, s->product_name); get_I_version (in, s->version_name); get_I_build (in, s->build_name); s->vendor_name[8] = 0; s->product_name[16] = 0; s->version_name[4] = 0; s->build_name[2] = 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; for (i = 2; s->build_name[i] == ' ' && i >= 0; i--) s->build_name[i] = 0; if (strcmp ("KODAK", 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 Kodak products.\n"); return SANE_STATUS_INVAL; } DBG (15, "init_inquire: Found '%s' '%s' '%s' '%s' at '%s'\n", s->vendor_name, s->product_name, s->version_name, s->build_name, s->device_name); /*defined in SCSI spec*/ DBG (15, "standard inquiry options\n"); /*FIXME: do we need to save these?*/ DBG (15, " PQ: %d\n",get_I_periph_qual(in)); DBG (15, " PDT: %d\n",get_I_periph_devtype(in)); DBG (15, " RMB: %d\n",get_I_rmb(in)); DBG (15, " DTQ: %d\n",get_I_devtype_qual(in)); DBG (15, " ISO: %d\n",get_I_iso_version(in)); DBG (15, " ECMA: %d\n",get_I_ecma_version(in)); DBG (15, " ANSI: %d\n",get_I_ansi_version(in)); DBG (15, " AENC: %d\n",get_I_aenc(in)); DBG (15, " TrmIOP: %d\n",get_I_trmiop(in)); DBG (15, " RDF: %d\n",get_I_resonse_format(in)); DBG (15, " Length: %d\n",get_I_length(in)); DBG (15, " RelAdr: %d\n",get_I_reladr(in)); DBG (15, " WBus32: %d\n",get_I_wbus32(in)); DBG (15, " WBus16: %d\n",get_I_wbus16(in)); DBG (15, " Sync: %d\n",get_I_sync(in)); DBG (15, " Linked: %d\n",get_I_linked(in)); DBG (15, " CmdQue: %d\n",get_I_cmdque(in)); DBG (15, " SftRe: %d\n",get_I_sftre(in)); /*kodak specific*/ DBG (15, "vendor inquiry options\n"); DBG (15, " MF Disable: %d\n",get_I_mf_disable(in)); DBG (15, " Checkdigit: %d\n",get_I_checkdigit(in)); DBG (15, " Front Prism: %d\n",get_I_front_prism(in)); DBG (15, " Comp Gray: %d\n",get_I_compressed_gray(in)); DBG (15, " Front Toggle: %d\n",get_I_front_toggle(in)); DBG (15, " Front DP1: %d\n",get_I_front_dp1(in)); DBG (15, " Front Color: %d\n",get_I_front_color(in)); DBG (15, " Front ATP: %d\n",get_I_front_atp(in)); DBG (15, " DP1 180: %d\n",get_I_dp1_180(in)); DBG (15, " MF Pause: %d\n",get_I_mf_pause(in)); DBG (15, " Rear Prism: %d\n",get_I_rear_prism(in)); DBG (15, " Uncomp Gray: %d\n",get_I_uncompressed_gray(in)); DBG (15, " Rear Toggle: %d\n",get_I_rear_toggle(in)); DBG (15, " Rear DP1: %d\n",get_I_rear_dp1(in)); DBG (15, " Rear Color: %d\n",get_I_rear_color(in)); DBG (15, " Rear ATP: %d\n",get_I_rear_atp(in)); /* we actually care about these */ DBG (15, " Min Binary Res: %d\n",get_I_min_bin_res(in)); s->s_res_min[MODE_LINEART] = get_I_min_bin_res(in); DBG (15, " Max Binary Res: %d\n",get_I_max_bin_res(in)); s->s_res_max[MODE_LINEART] = get_I_max_bin_res(in); DBG (15, " Min Color Res: %d\n",get_I_min_col_res(in)); s->s_res_min[MODE_COLOR] = get_I_min_col_res(in); DBG (15, " Max Color Res: %d\n",get_I_max_col_res(in)); s->s_res_max[MODE_COLOR] = get_I_max_col_res(in); DBG (15, " Max Width: %d\n",get_I_max_image_width(in)); s->s_width_max = get_I_max_image_width(in); DBG (15, " Max Length: %d\n",get_I_max_image_length(in)); s->s_length_max = get_I_max_image_length(in); /*FIXME: do we need to save these?*/ DBG (15, " Finecrop: %d\n",get_I_finecrop(in)); DBG (15, " iThresh: %d\n",get_I_ithresh(in)); DBG (15, " ECD: %d\n",get_I_ecd(in)); DBG (15, " VBLR: %d\n",get_I_vblr(in)); DBG (15, " Elevator: %d\n",get_I_elevator(in)); DBG (15, " RelCrop: %d\n",get_I_relcrop(in)); DBG (15, " CDeskew: %d\n",get_I_cdeskew(in)); DBG (15, " IA: %d\n",get_I_ia(in)); DBG (15, " Patch: %d\n",get_I_patch(in)); DBG (15, " Null Mode: %d\n",get_I_nullmode(in)); DBG (15, " SABRE: %d\n",get_I_sabre(in)); DBG (15, " LDDDS: %d\n",get_I_lddds(in)); DBG (15, " UDDDS: %d\n",get_I_uddds(in)); DBG (15, " Fixed Gap: %d\n",get_I_fixedgap(in)); DBG (15, " HR Printer: %d\n",get_I_hr_printer(in)); DBG (15, " Elev 100/250: %d\n",get_I_elev_100_250(in)); DBG (15, " UDDS Individual: %d\n",get_I_udds_individual(in)); DBG (15, " Auto Color: %d\n",get_I_auto_color(in)); DBG (15, " WB: %d\n",get_I_wb(in)); DBG (15, " ES: %d\n",get_I_es(in)); DBG (15, " FC: %d\n",get_I_fc(in)); DBG (15, " Max Rate: %d\n",get_I_max_rate(in)); DBG (15, " Buffer Size: %d\n",get_I_buffer_size(in)); DBG (10, "init_inquire: finish\n"); return SANE_STATUS_GOOD; } /* * get model specific info that is not in vpd, and correct * errors in vpd data. struct is already initialized to 0. */ static SANE_Status init_model (struct scanner *s) { DBG (10, "init_model: start\n"); s->s_mode[MODE_LINEART] = 1; s->s_mode[MODE_HALFTONE] = 1; s->s_mode[MODE_GRAYSCALE] = 1; s->s_mode[MODE_COLOR] = 1; /* scanner did not tell us these */ s->s_res_min[MODE_HALFTONE] = s->s_res_min[MODE_LINEART]; s->s_res_max[MODE_HALFTONE] = s->s_res_max[MODE_LINEART]; s->s_res_min[MODE_GRAYSCALE] = s->s_res_min[MODE_COLOR]; s->s_res_max[MODE_GRAYSCALE] = s->s_res_max[MODE_COLOR]; s->s_width_min = 96; s->s_length_min = 96; s->s_brightness_steps = 0; s->s_contrast_steps = 255; s->s_threshold_steps = 255; s->s_rif = 1; DBG (10, "init_model: finish\n"); return SANE_STATUS_GOOD; } /* * set good default user values. * struct is already initialized to 0. */ static SANE_Status init_user (struct scanner *s) { DBG (10, "init_user: start\n"); /* source */ s->u_source = SOURCE_ADF_FRONT; /* scan mode */ s->u_mode = MODE_LINEART; /*res, minimum for this mode*/ s->u_res = s->s_res_min[s->u_mode]; /* page width US-Letter */ s->u_page_width = 8.5 * 1200; if(s->u_page_width > s->s_width_max){ s->u_page_width = s->s_width_max; } /* page height US-Letter */ s->u_page_height = 11 * 1200; if(s->u_page_height > s->s_length_max){ s->u_page_height = s->s_length_max; } /* bottom-right x */ s->u_br_x = s->u_page_width; /* bottom-right y */ s->u_br_y = s->u_page_height; DBG (10, "init_user: finish\n"); return SANE_STATUS_GOOD; } /* * This function presets the "option" array to blank */ static SANE_Status init_options (struct scanner *s) { int i; DBG (10, "init_options: start\n"); 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; } /* 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].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; DBG (10, "init_options: finish\n"); return SANE_STATUS_GOOD; } /* * 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 scanner *dev = NULL; struct scanner *s = NULL; SANE_Status ret; unsigned char cmd[SEND_len]; size_t cmdLen = SEND_len; unsigned char out[SR_len_time]; /*longest used in this function*/ int try=0; time_t gmt_tt; struct tm * gmt_tm_p; struct tm * local_tm_p; DBG (10, "sane_open: start\n"); if(scanner_devList){ DBG (15, "sane_open: searching currently attached scanners\n"); } else{ DBG (15, "sane_open: no scanners currently attached, attaching\n"); ret = sane_get_devices(NULL,0); if(ret != SANE_STATUS_GOOD){ return ret; } } if(name[0] == 0){ DBG (15, "sane_open: no device requested, using default\n"); s = scanner_devList; } else{ DBG (15, "sane_open: device %s requested\n", name); for (dev = scanner_devList; dev; dev = dev->next) { if (strcmp (dev->sane.name, name) == 0) { s = dev; break; } } } if (!s) { DBG (5, "sane_open: no device found\n"); return SANE_STATUS_INVAL; } DBG (15, "sane_open: device %s found\n", s->sane.name); *handle = s; /* connect the fd so we can talk to scanner */ ret = connect_fd(s); if(ret != SANE_STATUS_GOOD){ return ret; } /*send the end batch (GX) command*/ memset(cmd,0,cmdLen); set_SCSI_opcode(cmd,SEND_code); set_SR_datatype_code(cmd,SR_datatype_random); set_SR_datatype_qual(cmd,SR_qual_end); set_SR_xfer_length(cmd,SR_len_end); /*start the following loop*/ ret = SANE_STATUS_DEVICE_BUSY; s->rs_info = 0; /*loop until scanner is ready*/ while(ret == SANE_STATUS_DEVICE_BUSY){ DBG (15, "sane_open: GX, try %d, sleep %lu\n", try, (unsigned long)s->rs_info); try++; sleep(s->rs_info); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, NULL, NULL ); if(try > 5){ break; } } if(ret){ DBG (5, "sane_open: GX error %d\n",ret); return ret; } /*send the clear buffer (CB) command*/ DBG (15, "sane_open: CB\n"); memset(cmd,0,cmdLen); set_SCSI_opcode(cmd,SEND_code); set_SR_datatype_code(cmd,SR_datatype_random); set_SR_datatype_qual(cmd,SR_qual_clear); set_SR_xfer_length(cmd,SR_len_clear); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, NULL, NULL ); if(ret){ DBG (5, "sane_open: CB error %d\n",ret); return ret; } /*send the GT command*/ DBG (15, "sane_open: GT\n"); gmt_tt = time(NULL); gmt_tm_p = gmtime(&gmt_tt); memset(cmd,0,cmdLen); set_SCSI_opcode(cmd,SEND_code); set_SR_datatype_code(cmd,SR_datatype_random); set_SR_datatype_qual(cmd,SR_qual_gmt); set_SR_xfer_length(cmd,SR_len_time); memset(out,0,SR_len_time); set_SR_payload_len(out,SR_len_time); set_SR_time_hour(out,gmt_tm_p->tm_hour); set_SR_time_min(out,gmt_tm_p->tm_min); set_SR_time_mon(out,gmt_tm_p->tm_mon); set_SR_time_day(out,gmt_tm_p->tm_mday); set_SR_time_year(out,gmt_tm_p->tm_year+1900); ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, SR_len_time, NULL, NULL ); if(ret){ DBG (5, "sane_open: GT error %d\n",ret); return ret; } /*FIXME: read the LC command? */ /*send the LC command*/ DBG (15, "sane_open: LC\n"); gmt_tt = time(NULL); local_tm_p = localtime(&gmt_tt); memset(cmd,0,cmdLen); set_SCSI_opcode(cmd,SEND_code); set_SR_datatype_code(cmd,SR_datatype_random); set_SR_datatype_qual(cmd,SR_qual_clock); set_SR_xfer_length(cmd,SR_len_time); memset(out,0,SR_len_time); set_SR_payload_len(out,SR_len_time); set_SR_time_hour(out,local_tm_p->tm_hour); set_SR_time_min(out,local_tm_p->tm_min); set_SR_time_mon(out,local_tm_p->tm_mon); set_SR_time_day(out,local_tm_p->tm_mday); set_SR_time_year(out,local_tm_p->tm_year+1900); ret = do_cmd ( s, 1, 0, cmd, cmdLen, out, SR_len_time, NULL, NULL ); if(ret){ DBG (5, "sane_open: LC error %d\n",ret); return ret; } DBG (10, "sane_open: finish\n"); return SANE_STATUS_GOOD; } /* * @@ Section 3 - SANE Options functions */ /* * 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 scanner *s = handle; int i; SANE_Option_Descriptor *opt = &s->opt[option]; DBG (20, "sane_get_option_descriptor: %d\n", option); if ((unsigned) option >= NUM_OPTIONS) return NULL; /* "Mode" group -------------------------------------------------------- */ if(option==OPT_MODE_GROUP){ opt->title = "Scan Mode"; opt->desc = ""; opt->type = SANE_TYPE_GROUP; opt->constraint_type = SANE_CONSTRAINT_NONE; } /* source */ if(option==OPT_SOURCE){ i=0; s->o_source_list[i++]=STRING_ADFFRONT; s->o_source_list[i++]=STRING_ADFBACK; s->o_source_list[i++]=STRING_ADFDUPLEX; s->o_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->o_source_list; opt->size = maxStringSize (opt->constraint.string_list); opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } /* scan mode */ if(option==OPT_MODE){ i=0; if(s->s_mode[MODE_LINEART]){ s->o_mode_list[i++]=STRING_LINEART; } if(s->s_mode[MODE_HALFTONE]){ s->o_mode_list[i++]=STRING_HALFTONE; } if(s->s_mode[MODE_GRAYSCALE]){ s->o_mode_list[i++]=STRING_GRAYSCALE; } if(s->s_mode[MODE_COLOR]){ s->o_mode_list[i++]=STRING_COLOR; } s->o_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->o_mode_list; opt->size = maxStringSize (opt->constraint.string_list); opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } /* resolution */ /* build a list of possible choices for current mode */ if(option==OPT_RES){ int reslist[]={100,150,200,240,300,400}; int j; i=0; for(j=0;j<6;j++){ if(reslist[j] >= s->s_res_min[s->u_mode] && reslist[j] <= s->s_res_max[s->u_mode]){ s->o_res_list[s->u_mode][++i] = reslist[j]; } } s->o_res_list[s->u_mode][0] = i; opt->name = SANE_NAME_SCAN_RESOLUTION; opt->title = SANE_TITLE_SCAN_RESOLUTION; opt->desc = SANE_DESC_SCAN_RESOLUTION; opt->type = SANE_TYPE_INT; opt->unit = SANE_UNIT_DPI; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; opt->constraint_type = SANE_CONSTRAINT_WORD_LIST; opt->constraint.word_list = s->o_res_list[s->u_mode]; } /* "Geometry" group ---------------------------------------------------- */ if(option==OPT_GEOMETRY_GROUP){ opt->title = "Geometry"; opt->desc = ""; opt->type = SANE_TYPE_GROUP; opt->constraint_type = SANE_CONSTRAINT_NONE; } /* top-left x */ if(option==OPT_TL_X){ /* values stored in 1200 dpi units */ /* must be converted to MM for sane */ s->o_tl_x_range.min = SCANNER_UNIT_TO_FIXED_MM(s->s_width_min); s->o_tl_x_range.max = SCANNER_UNIT_TO_FIXED_MM(s->s_width_max); s->o_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->o_tl_x_range); opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } /* top-left y */ if(option==OPT_TL_Y){ /* values stored in 1200 dpi units */ /* must be converted to MM for sane */ s->o_tl_y_range.min = SCANNER_UNIT_TO_FIXED_MM(s->s_length_min); s->o_tl_y_range.max = SCANNER_UNIT_TO_FIXED_MM(s->s_length_max); s->o_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->o_tl_y_range); opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } /* bottom-right x */ if(option==OPT_BR_X){ /* values stored in 1200 dpi units */ /* must be converted to MM for sane */ s->o_br_x_range.min = SCANNER_UNIT_TO_FIXED_MM(s->s_width_min); s->o_br_x_range.max = SCANNER_UNIT_TO_FIXED_MM(s->s_width_max); s->o_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->o_br_x_range); opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } /* bottom-right y */ if(option==OPT_BR_Y){ /* values stored in 1200 dpi units */ /* must be converted to MM for sane */ s->o_br_y_range.min = SCANNER_UNIT_TO_FIXED_MM(s->s_length_min); s->o_br_y_range.max = SCANNER_UNIT_TO_FIXED_MM(s->s_length_max); s->o_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->o_br_y_range); opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } /* page width */ if(option==OPT_PAGE_WIDTH){ /* values stored in 1200 dpi units */ /* must be converted to MM for sane */ s->o_page_x_range.min = SCANNER_UNIT_TO_FIXED_MM(s->s_width_min); s->o_page_x_range.max = SCANNER_UNIT_TO_FIXED_MM(s->s_width_max); s->o_page_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->o_page_x_range; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } /* page height */ if(option==OPT_PAGE_HEIGHT){ /* values stored in 1200 dpi units */ /* must be converted to MM for sane */ s->o_page_y_range.min = SCANNER_UNIT_TO_FIXED_MM(s->s_length_min); s->o_page_y_range.max = SCANNER_UNIT_TO_FIXED_MM(s->s_length_max); s->o_page_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->o_page_y_range; opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } /* "Enhancement" group ------------------------------------------------- */ if(option==OPT_ENHANCEMENT_GROUP){ opt->title = "Enhancement"; opt->desc = ""; opt->type = SANE_TYPE_GROUP; opt->constraint_type = SANE_CONSTRAINT_NONE; } /* 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->o_brightness_range; s->o_brightness_range.quant=1; s->o_brightness_range.min=-(s->s_brightness_steps/2); s->o_brightness_range.max=s->s_brightness_steps/2; if(opt->constraint.range->max > opt->constraint.range->min){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } else{ opt->cap = SANE_CAP_INACTIVE; } } /* 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->o_contrast_range; s->o_contrast_range.quant=1; s->o_contrast_range.min=-(s->s_contrast_steps/2); s->o_contrast_range.max=s->s_contrast_steps/2; if(opt->constraint.range->max > opt->constraint.range->min){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } else{ opt->cap = SANE_CAP_INACTIVE; } } /*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->o_threshold_range; s->o_threshold_range.min=0; s->o_threshold_range.max=s->s_threshold_steps; s->o_threshold_range.quant=1; if(opt->constraint.range->max > opt->constraint.range->min){ opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } else{ opt->cap = SANE_CAP_INACTIVE; } } /*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->s_rif) opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; else opt->cap = SANE_CAP_INACTIVE; } return opt; } /** * 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 scanner *s = (struct scanner *) handle; SANE_Int dummy = 0; /* Make sure that all those statements involving *info cannot break (better * than having to do "if (info) ..." everywhere!) */ if (info == 0) info = &dummy; if (option >= NUM_OPTIONS) { DBG (5, "sane_control_option: %d too big\n", option); return SANE_STATUS_INVAL; } if (!SANE_OPTION_IS_ACTIVE (s->opt[option].cap)) { DBG (5, "sane_control_option: %d inactive\n", option); return SANE_STATUS_INVAL; } /* * 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; DBG (20, "sane_control_option: get value for '%s' (%d)\n", s->opt[option].name,option); switch (option) { case OPT_NUM_OPTS: *val_p = NUM_OPTIONS; return SANE_STATUS_GOOD; case OPT_SOURCE: if(s->u_source == SOURCE_ADF_FRONT){ strcpy (val, STRING_ADFFRONT); } else if(s->u_source == SOURCE_ADF_BACK){ strcpy (val, STRING_ADFBACK); } else if(s->u_source == SOURCE_ADF_DUPLEX){ strcpy (val, STRING_ADFDUPLEX); } else{ DBG(5,"missing option val for source\n"); } return SANE_STATUS_GOOD; case OPT_MODE: if(s->u_mode == MODE_LINEART){ strcpy (val, STRING_LINEART); } else if(s->u_mode == MODE_HALFTONE){ strcpy (val, STRING_HALFTONE); } else if(s->u_mode == MODE_GRAYSCALE){ strcpy (val, STRING_GRAYSCALE); } else if(s->u_mode == MODE_COLOR){ strcpy (val, STRING_COLOR); } return SANE_STATUS_GOOD; case OPT_RES: *val_p = s->u_res; return SANE_STATUS_GOOD; case OPT_TL_X: *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u_tl_x); return SANE_STATUS_GOOD; case OPT_TL_Y: *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u_tl_y); return SANE_STATUS_GOOD; case OPT_BR_X: *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u_br_x); return SANE_STATUS_GOOD; case OPT_BR_Y: *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u_br_y); return SANE_STATUS_GOOD; case OPT_PAGE_WIDTH: *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u_page_width); return SANE_STATUS_GOOD; case OPT_PAGE_HEIGHT: *val_p = SCANNER_UNIT_TO_FIXED_MM(s->u_page_height); return SANE_STATUS_GOOD; case OPT_BRIGHTNESS: *val_p = s->u_brightness; return SANE_STATUS_GOOD; case OPT_CONTRAST: *val_p = s->u_contrast; return SANE_STATUS_GOOD; case OPT_THRESHOLD: *val_p = s->u_threshold; return SANE_STATUS_GOOD; case OPT_RIF: *val_p = s->u_rif; 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: can't set, device busy\n"); return SANE_STATUS_DEVICE_BUSY; } 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; } /* may have been changed by constrain, so don't copy until now */ val_c = *(SANE_Word *)val; /* * 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) { /* Mode Group */ case OPT_SOURCE: if (!strcmp (val, STRING_ADFFRONT)) { tmp = SOURCE_ADF_FRONT; } else if (!strcmp (val, STRING_ADFBACK)) { tmp = SOURCE_ADF_BACK; } else{ tmp = SOURCE_ADF_DUPLEX; } if (s->u_source != tmp) { s->u_source = tmp; *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; } return SANE_STATUS_GOOD; case OPT_MODE: if (!strcmp (val, STRING_LINEART)) { tmp = MODE_LINEART; } else if (!strcmp (val, STRING_HALFTONE)) { tmp = MODE_HALFTONE; } else if (!strcmp (val, STRING_GRAYSCALE)) { tmp = MODE_GRAYSCALE; } else{ tmp = MODE_COLOR; } if (tmp != s->u_mode){ s->u_mode = tmp; *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; } return SANE_STATUS_GOOD; case OPT_RES: if (s->u_res != val_c) { s->u_res = val_c; *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; } return SANE_STATUS_GOOD; /* Geometry Group */ case OPT_TL_X: if (s->u_tl_x != FIXED_MM_TO_SCANNER_UNIT(val_c)){ s->u_tl_x = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; } return SANE_STATUS_GOOD; case OPT_TL_Y: if (s->u_tl_y != FIXED_MM_TO_SCANNER_UNIT(val_c)){ s->u_tl_y = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; } return SANE_STATUS_GOOD; case OPT_BR_X: if (s->u_br_x != FIXED_MM_TO_SCANNER_UNIT(val_c)){ s->u_br_x = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; } return SANE_STATUS_GOOD; case OPT_BR_Y: if (s->u_br_y != FIXED_MM_TO_SCANNER_UNIT(val_c)){ s->u_br_y = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; } return SANE_STATUS_GOOD; case OPT_PAGE_WIDTH: if (s->u_page_width != FIXED_MM_TO_SCANNER_UNIT(val_c)){ s->u_page_width = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_OPTIONS; } return SANE_STATUS_GOOD; case OPT_PAGE_HEIGHT: if (s->u_page_height != FIXED_MM_TO_SCANNER_UNIT(val_c)){ s->u_page_height = FIXED_MM_TO_SCANNER_UNIT(val_c); *info |= SANE_INFO_RELOAD_OPTIONS; } return SANE_STATUS_GOOD; /* Enhancement Group */ case OPT_BRIGHTNESS: if (s->u_brightness != val_c){ s->u_brightness = val_c; } return SANE_STATUS_GOOD; case OPT_CONTRAST: if (s->u_contrast != val_c){ s->u_contrast = val_c; } return SANE_STATUS_GOOD; case OPT_THRESHOLD: if (s->u_threshold != val_c){ s->u_threshold = val_c; } return SANE_STATUS_GOOD; case OPT_RIF: if (s->u_rif != val_c){ s->u_rif = val_c; } return SANE_STATUS_GOOD; } /* switch */ } /* else */ return SANE_STATUS_INVAL; } /* * @@ 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_Parameters is defined 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_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { SANE_Status ret = SANE_STATUS_GOOD; struct scanner *s = (struct scanner *) handle; DBG (10, "sane_get_parameters: start\n"); /* started? get param data from image header */ if(s->started){ DBG (15, "sane_get_parameters: image settings:\n"); DBG (15, " tlx=%d, brx=%d, iw=%d, maxx=%d\n", s->i_tlx, (s->i_tlx+s->i_width), s->i_width, s->s_width_max/1200); DBG (15, " tly=%d, bry=%d, il=%d, maxy=%d\n", s->i_tly, (s->i_tly+s->i_length), s->i_length, s->s_length_max/1200); DBG (15, " res=%d, id=%d, bytes=%d\n", s->i_dpi, s->i_id, s->i_bytes); params->last_frame = 1; params->lines = s->i_length; params->pixels_per_line = s->i_width; /* bitonal */ if (s->i_bpp == 1) { params->format = SANE_FRAME_GRAY; params->depth = 1; params->bytes_per_line = params->pixels_per_line / 8; #ifdef SANE_FRAME_G42D /*G4 fax compression*/ if (s->i_compr) { params->format = SANE_FRAME_G42D; } #endif } /* gray */ else if (s->i_bpp == 8) { params->format = SANE_FRAME_GRAY; params->depth = 8; params->bytes_per_line = params->pixels_per_line; #ifdef SANE_FRAME_JPEG /*jpeg compression*/ if (s->i_compr) { params->format = SANE_FRAME_JPEG; } #endif } /* color */ else if (s->i_bpp == 24 || s->i_bpp == 96) { params->format = SANE_FRAME_RGB; params->depth = 8; params->bytes_per_line = params->pixels_per_line * 3; #ifdef SANE_FRAME_JPEG /*jpeg compression*/ if (s->i_compr) { params->format = SANE_FRAME_JPEG; } #endif } else{ DBG(5,"sane_get_parameters: unsupported depth %d\n", s->i_bpp); return SANE_STATUS_INVAL; } } /* not started? get param data from user input */ else{ DBG (15, "sane_get_parameters: user settings:\n"); DBG (15, " tlx=%d, brx=%d, pw=%d, maxx=%d\n", s->u_tl_x, s->u_br_x, s->u_page_width, s->s_width_max); DBG (15, " tly=%d, bry=%d, ph=%d, maxy=%d\n", s->u_tl_y, s->u_br_y, s->u_page_height, s->s_length_max); DBG (15, " res=%d, user_x=%d, user_y=%d\n", s->u_res, (s->u_res * (s->u_br_x - s->u_tl_x) / 1200), (s->u_res * (s->u_br_y - s->u_tl_y) / 1200)); if (s->u_mode == MODE_COLOR) { params->format = SANE_FRAME_RGB; params->depth = 8; } else if (s->u_mode == MODE_GRAYSCALE) { params->format = SANE_FRAME_GRAY; params->depth = 8; } else { params->format = SANE_FRAME_GRAY; params->depth = 1; } params->last_frame = 1; params->lines = s->u_res * (s->u_br_y - s->u_tl_y) / 1200; params->pixels_per_line = s->u_res * (s->u_br_x - s->u_tl_x) / 1200; /* bytes per line differs by mode */ if (s->u_mode == MODE_COLOR) { params->bytes_per_line = params->pixels_per_line * 3; } else if (s->u_mode == MODE_GRAYSCALE) { params->bytes_per_line = params->pixels_per_line; } else { params->bytes_per_line = params->pixels_per_line / 8; } } DBG (15, "sane_get_parameters: returning:\n"); DBG (15, " scan_x=%d, Bpl=%d, depth=%d\n", params->pixels_per_line, params->bytes_per_line, params->depth ); DBG (15, " scan_y=%d, frame=%d, last=%d\n", params->lines, params->format, params->last_frame ); DBG (10, "sane_get_parameters: finish\n"); return ret; } /* * 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 will be called before each image, including duplex backsides, * and at the start of adf batch. * hence, we spend a lot of time playing with s->started, etc. */ SANE_Status sane_start (SANE_Handle handle) { struct scanner *s = handle; SANE_Status ret; DBG (10, "sane_start: start\n"); DBG (15, "started=%d, source=%d\n", s->started, s->u_source); /* batch already running */ if(s->started){ /* not finished with current image, error */ if (s->bytes_tx != s->i_bytes) { DBG(5,"sane_start: previous transfer not finished?"); return do_cancel(s); } } /* first page of batch */ else{ unsigned char cmd[SCAN_len]; unsigned char pay[SR_len_startstop]; /* set window command */ ret = set_window(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot set window\n"); do_cancel(s); return ret; } /* read/send JQ command */ /* read/send SC command */ ret = send_sc(s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot send SC\n"); do_cancel(s); return ret; } /* read/send CT command */ DBG (15, "sane_start: send SCAN\n"); memset(cmd, 0, SCAN_len); set_SCSI_opcode(cmd, SCAN_code); ret = do_cmd ( s, 1, 0, cmd, SCAN_len, NULL, 0, NULL, NULL ); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR sending SCAN\n"); do_cancel(s); return ret; } /* send SS command */ DBG (15, "sane_start: send SS\n"); memset(cmd,0,SEND_len); set_SCSI_opcode(cmd,SEND_code); set_SR_datatype_code(cmd,SR_datatype_random); set_SR_datatype_qual(cmd,SR_qual_startstop); set_SR_xfer_length(cmd,SR_len_startstop); memset(pay,0,SR_len_startstop); set_SR_payload_len(pay,SR_len_startstop); set_SR_startstop_cmd(pay,1); ret = do_cmd ( s, 1, 0, cmd, SEND_len, pay, SR_len_startstop, NULL, NULL ); if(ret){ DBG (5, "sane_open: SS error %d\n",ret); return ret; } DBG (15, "sane_start: sleeping\n"); sleep(2); s->started=1; } ret = read_imageheader(s); if(ret){ DBG (5, "sane_open: error reading imageheader %d\n",ret); return ret; } /* set clean defaults */ s->bytes_rx = 0; s->bytes_tx = 0; /* make large buffer to hold the images */ DBG (15, "sane_start: setup buffer\n"); /* free current buffer if too small */ if (s->buffer && s->bytes_buf < s->i_bytes) { DBG (15, "sane_start: free buffer.\n"); free(s->buffer); s->buffer = NULL; s->bytes_buf = 0; } /* grab new buffer if don't have one */ if (!s->buffer) { DBG (15, "sane_start: calloc buffer.\n"); s->buffer = calloc (1,s->i_bytes); if (!s->buffer) { DBG (5, "sane_start: Error, no buffer\n"); do_cancel(s); return SANE_STATUS_NO_MEM; } } DBG (15, "started=%d, source=%d\n", s->started, s->u_source); DBG (10, "sane_start: finish\n"); return SANE_STATUS_GOOD; } /* * This routine issues a SCSI SET WINDOW command to the scanner, using the * values currently in the scanner data structure. * the scanner has 4 separate windows, and all must be set similarly, * even if you don't intend to acquire images from all of them. */ static SANE_Status set_window (struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[SET_WINDOW_len]; size_t cmdLen = SET_WINDOW_len; /* the data phase has a header, followed by a window desc block * the header specifies the number of bytes in 1 window desc block */ unsigned char pay[WINDOW_HEADER_len + WINDOW_DESCRIPTOR_len]; size_t payLen = WINDOW_HEADER_len + WINDOW_DESCRIPTOR_len; unsigned char * desc = pay + WINDOW_HEADER_len; int width = (s->u_br_x - s->u_tl_x) * s->u_res/1200; int length = (s->u_br_y - s->u_tl_y) * s->u_res/1200; DBG (10, "set_window: start\n"); /* binary window settings */ memset(cmd,0,cmdLen); set_SCSI_opcode(cmd,SET_WINDOW_code); set_SW_xferlen(cmd,payLen); memset(pay,0,payLen); set_WH_desc_len(pay,WINDOW_DESCRIPTOR_len); set_WD_wid(desc,WD_wid_front_binary); /* common settings */ set_WD_Xres (desc, s->u_res); set_WD_Yres (desc, s->u_res); set_WD_ULX (desc, s->u_tl_x); set_WD_ULY (desc, s->u_tl_y); /* width % 32 == 0 && length % 1 == 0 */ width -= width % 32; width = width*1200/s->u_res; length = length*1200/s->u_res; set_WD_width (desc, width); set_WD_length (desc, length); /* brightness not supported? */ set_WD_brightness (desc, 0); set_WD_threshold (desc, s->u_threshold); set_WD_contrast (desc, 0); if(s->s_contrast_steps){ /*convert our common -127 to +127 range into HW's range *FIXME: this code assumes hardware range of 1-255 */ set_WD_contrast (desc, s->u_contrast+128); } if(s->u_mode == MODE_HALFTONE){ set_WD_composition (desc, WD_compo_HALFTONE); set_WD_bitsperpixel (desc, 1); } else{ set_WD_composition (desc, WD_compo_LINEART); set_WD_bitsperpixel (desc, 1); } /* FIXME ht pattern */ set_WD_rif (desc, s->u_rif); set_WD_bitorder (desc, 1); /* compression options */ if(s->u_compr) set_WD_compress_type (desc, WD_compr_FAXG4); /*FIXME: noise filter */ set_WD_allow_zero(desc,1); set_WD_cropping (desc, WD_crop_RELATIVE); /*FIXME: more settings here*/ hexdump(15, "front binary window:", desc, WINDOW_DESCRIPTOR_len); DBG (15, "set_window: set window binary back\n"); ret = do_cmd ( s, 1, 0, cmd, cmdLen, pay, payLen, NULL, NULL ); if(ret){ DBG (5, "set_window: error setting binary front window %d\n",ret); return ret; } /*send the window for backside too*/ set_WD_wid(desc,WD_wid_back_binary); DBG (15, "set_window: set window binary back\n"); ret = do_cmd ( s, 1, 0, cmd, cmdLen, pay, payLen, NULL, NULL ); if(ret){ DBG (5, "set_window: error setting binary back window %d\n",ret); return ret; } #if 0 memset(cmd,0,cmdLen); set_SCSI_opcode(cmd,GET_WINDOW_code); set_GW_single(cmd,1); set_GW_wid(cmd,WD_wid_front_color); set_GW_xferlen(cmd,payLen); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, pay, &payLen ); if(ret){ DBG (5, "set_window: error getting window %d\n",ret); return ret; } hexdump(15,"foo",pay,payLen); #endif /* color window settings */ memset(cmd,0,cmdLen); set_SCSI_opcode(cmd,SET_WINDOW_code); set_SW_xferlen(cmd,payLen); memset(pay,0,payLen); set_WH_desc_len(pay,WINDOW_DESCRIPTOR_len); set_WD_wid(desc,WD_wid_front_color); /* common settings */ set_WD_Xres (desc, s->u_res); set_WD_Yres (desc, s->u_res); set_WD_ULX (desc, s->u_tl_x); set_WD_ULY (desc, s->u_tl_y); set_WD_width (desc, width); set_WD_length (desc, length); /*gray mode*/ if(s->u_mode == MODE_GRAYSCALE){ /* gamma width % 8 == 0 && length % 8 == 0 */ set_WD_composition (desc, WD_compo_MULTILEVEL); set_WD_bitsperpixel (desc, 8); } /*color mode or color window in binary mode*/ else{ /* width % 16 == 0 && length % 8 == 0 */ set_WD_composition (desc, WD_compo_MULTILEVEL); set_WD_bitsperpixel (desc, 24); /* compression options */ if(s->u_compr) set_WD_compress_type (desc, WD_compr_JPEG); } set_WD_bitorder (desc, 1); /*FIXME: noise filter */ set_WD_allow_zero(desc,1); set_WD_cropping (desc, WD_crop_RELATIVE); /*FIXME: more settings here*/ DBG (15, "set_window: set window color front\n"); ret = do_cmd ( s, 1, 0, cmd, cmdLen, pay, payLen, NULL, NULL ); if(ret){ DBG (5, "set_window: error setting color front window %d\n",ret); return ret; } /*send the window for backside too*/ set_WD_wid(desc,WD_wid_back_color); DBG (15, "set_window: set window color back\n"); ret = do_cmd ( s, 1, 0, cmd, cmdLen, pay, payLen, NULL, NULL ); if(ret){ DBG (5, "set_window: error setting color back window %d\n",ret); return ret; } DBG (10, "set_window: finish\n"); return ret; } /* * This routine reads the SC (scanner config) data from the scanner * modifies a few params based on user data, and sends it back */ static SANE_Status send_sc(struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[READ_len]; size_t cmdLen = READ_len; unsigned char pay[SR_len_config]; size_t payLen = SR_len_config; /* send SC command */ DBG (10, "send_sc: start\n"); DBG (15, "send_sc: reading config\n"); memset(cmd,0,READ_len); set_SCSI_opcode(cmd,READ_code); set_SR_datatype_code(cmd,SR_datatype_random); set_SR_datatype_qual(cmd,SR_qual_config); set_SR_xfer_length(cmd,SR_len_config); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, pay, &payLen ); if(ret || !payLen){ DBG (5, "send_sc: error reading: %d\n",ret); return ret; } memset(cmd,0,SEND_len); set_SCSI_opcode(cmd,SEND_code); set_SR_datatype_code(cmd,SR_datatype_random); set_SR_datatype_qual(cmd,SR_qual_config); set_SR_xfer_length(cmd,payLen); if(s->u_source == SOURCE_ADF_FRONT){ if(s->u_mode == MODE_COLOR || s->u_mode == MODE_GRAYSCALE){ set_SR_sc_io1(pay,SR_sc_io_front_color); } else{ set_SR_sc_io1(pay,SR_sc_io_front_binary); } set_SR_sc_io2(pay,SR_sc_io_none); set_SR_sc_io3(pay,SR_sc_io_none); set_SR_sc_io4(pay,SR_sc_io_none); } else if(s->u_source == SOURCE_ADF_BACK){ if(s->u_mode == MODE_COLOR || s->u_mode == MODE_GRAYSCALE){ set_SR_sc_io1(pay,SR_sc_io_rear_color); } else{ set_SR_sc_io1(pay,SR_sc_io_rear_binary); } set_SR_sc_io2(pay,SR_sc_io_none); set_SR_sc_io3(pay,SR_sc_io_none); set_SR_sc_io4(pay,SR_sc_io_none); } else{ if(s->u_mode == MODE_COLOR || s->u_mode == MODE_GRAYSCALE){ set_SR_sc_io1(pay,SR_sc_io_front_color); set_SR_sc_io2(pay,SR_sc_io_rear_color); } else{ set_SR_sc_io1(pay,SR_sc_io_front_binary); set_SR_sc_io2(pay,SR_sc_io_rear_binary); } set_SR_sc_io3(pay,SR_sc_io_none); set_SR_sc_io4(pay,SR_sc_io_none); } /*FIXME: there are hundreds of other settings in this payload*/ ret = do_cmd ( s, 1, 0, cmd, cmdLen, pay, payLen, NULL, NULL ); DBG (10, "send_sc: finish %d\n",ret); return ret; } /* * This routine reads the image header from the scanner, and updates * values currently in the scanner data structure. */ static SANE_Status read_imageheader (struct scanner *s) { SANE_Status ret = SANE_STATUS_GOOD; unsigned char cmd[READ_len]; unsigned char pay[SR_len_imageheader]; size_t payLen = SR_len_imageheader; int pass = 0; /* read img header */ DBG (10, "read_imageheader: start\n"); memset(cmd,0,READ_len); set_SCSI_opcode(cmd,READ_code); set_SR_datatype_code(cmd,SR_datatype_imageheader); set_SR_xfer_length(cmd,SR_len_imageheader); while (pass++ < 1000){ DBG (15, "read_imageheader: pass %d\n", pass); payLen = SR_len_imageheader; ret = do_cmd ( s, 1, 0, cmd, READ_len, NULL, 0, pay, &payLen ); DBG (15, "read_imageheader: pass status %d\n", ret); if(ret != SANE_STATUS_DEVICE_BUSY){ break; } usleep(50000); } if (ret == SANE_STATUS_GOOD){ DBG (15, "image header:\n"); DBG (15, " bytes: %d\n",get_SR_ih_image_length(pay)); s->i_bytes = get_SR_ih_image_length(pay); DBG (15, " id: %d\n",get_SR_ih_image_id(pay)); s->i_id = get_SR_ih_image_id(pay); DBG (15, " dpi: %d\n",get_SR_ih_resolution(pay)); s->i_dpi = get_SR_ih_resolution(pay); DBG (15, " tlx: %d\n",get_SR_ih_ulx(pay)); s->i_tlx = get_SR_ih_ulx(pay); DBG (15, " tly: %d\n",get_SR_ih_uly(pay)); s->i_tly = get_SR_ih_uly(pay); DBG (15, " width: %d\n",get_SR_ih_width(pay)); s->i_width = get_SR_ih_width(pay); DBG (15, " length: %d\n",get_SR_ih_length(pay)); s->i_length = get_SR_ih_length(pay); DBG (15, " bpp: %d\n",get_SR_ih_bpp(pay)); s->i_bpp = get_SR_ih_bpp(pay); DBG (15, " comp: %d\n",get_SR_ih_comp_type(pay)); s->i_compr = get_SR_ih_comp_type(pay); /*FIXME: there are a lot more of these?*/ } DBG (10, "read_imageheader: finish %d\n", ret); return ret; } /* * 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 scanner *s = (struct scanner *) handle; SANE_Status ret=0; DBG (10, "sane_read: start\n"); *len=0; /* maybe cancelled? */ if(!s->started){ DBG (5, "sane_read: not started, call sane_start\n"); return SANE_STATUS_CANCELLED; } /* sane_start required between images */ if(s->bytes_tx == s->i_bytes){ DBG (15, "sane_read: returning eof\n"); return SANE_STATUS_EOF; } if(s->i_bytes > s->bytes_rx ){ ret = read_from_scanner(s); if(ret){ DBG(5,"sane_read: returning %d\n",ret); return ret; } } /* copy a block from buffer to frontend */ ret = read_from_buffer(s,buf,max_len,len); DBG (10, "sane_read: finish\n"); return ret; } static SANE_Status read_from_scanner(struct scanner *s) { SANE_Status ret=SANE_STATUS_GOOD; int bytes = s->buffer_size; int remain = s->i_bytes - s->bytes_rx; unsigned char * buf; size_t inLen = 0; unsigned char cmd[READ_len]; int cmdLen=READ_len; DBG (10, "read_from_scanner: start\n"); memset(cmd, 0, cmdLen); set_SCSI_opcode(cmd, READ_code); /* figure out the max amount to transfer */ if(bytes > remain){ bytes = remain; } DBG(15, "read_from_scanner: to:%d rx:%d re:%d bu:%d pa:%d\n", s->i_bytes, s->bytes_rx, remain, s->buffer_size, bytes); if(ret){ return ret; } inLen = bytes; buf = malloc(bytes); if(!buf){ DBG(5, "read_from_scanner: not enough mem for buffer: %d\n",bytes); return SANE_STATUS_NO_MEM; } set_SR_datatype_code (cmd, SR_datatype_imagedata); set_SR_xfer_length (cmd, bytes); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, buf, &inLen ); if (ret == SANE_STATUS_GOOD) { DBG(15, "read_from_scanner: got GOOD, returning GOOD\n"); } else if (ret == SANE_STATUS_EOF) { DBG(15, "read_from_scanner: got EOF, finishing\n"); } else if (ret == SANE_STATUS_DEVICE_BUSY) { DBG(5, "read_from_scanner: got BUSY, returning GOOD\n"); inLen = 0; ret = SANE_STATUS_GOOD; } else { DBG(5, "read_from_scanner: error reading data block status = %d\n",ret); inLen = 0; } if(inLen){ copy_buffer (s, buf, inLen); } free(buf); if(ret == SANE_STATUS_EOF){ DBG (5, "read_from_scanner: unexpected EOF, shortening image\n"); s->i_bytes = s->bytes_rx; ret = SANE_STATUS_GOOD; } DBG (10, "read_from_scanner: finish\n"); return ret; } static SANE_Status copy_buffer(struct scanner *s, unsigned char * buf, int len) { SANE_Status ret=SANE_STATUS_GOOD; DBG (10, "copy_buffer: start\n"); memcpy(s->buffer+s->bytes_rx,buf,len); s->bytes_rx += len; DBG (10, "copy_buffer: finish\n"); return ret; } static SANE_Status read_from_buffer(struct scanner *s, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { SANE_Status ret=SANE_STATUS_GOOD; int bytes = max_len; int remain = s->bytes_rx - s->bytes_tx; DBG (10, "read_from_buffer: start\n"); /* figure out the max amount to transfer */ if(bytes > remain){ bytes = remain; } *len = bytes; DBG(15, "read_from_buffer: to:%d tx:%d re:%d bu:%d pa:%d\n", s->i_bytes, s->bytes_tx, remain, max_len, bytes); /*FIXME this needs to timeout eventually */ if(!bytes){ DBG(5,"read_from_buffer: nothing to do\n"); return SANE_STATUS_GOOD; } memcpy(buf,s->buffer+s->bytes_tx,bytes); s->bytes_tx += *len; DBG (10, "read_from_buffer: finish\n"); return ret; } /* * @@ 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 operation 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 scanner *) handle); DBG (10, "sane_cancel: finish\n"); } /* * Performs cleanup. * FIXME: do better cleanup if scanning is ongoing... */ static SANE_Status do_cancel (struct scanner *s) { DBG (10, "do_cancel: start\n"); s->started = 0; DBG (10, "do_cancel: finish\n"); return SANE_STATUS_CANCELLED; } /* * 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"); do_cancel((struct scanner *) handle); disconnect_fd((struct scanner *) handle); DBG (10, "sane_close: finish\n"); } static SANE_Status disconnect_fd (struct scanner *s) { DBG (10, "disconnect_fd: start\n"); if(s->fd > -1){ DBG (15, "disconnecting scsi device\n"); sanei_scsi_close (s->fd); s->fd = -1; } DBG (10, "disconnect_fd: finish\n"); return SANE_STATUS_GOOD; } /* * 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 scanner *dev, *next; DBG (10, "sane_exit: start\n"); for (dev = scanner_devList; dev; dev = next) { disconnect_fd(dev); next = dev->next; free (dev->device_name); free (dev); } if (sane_devArray) free (sane_devArray); scanner_devList = NULL; sane_devArray = NULL; DBG (10, "sane_exit: finish\n"); } /* * @@ Section 5 - misc helper functions */ /* * Called by the SANE SCSI core on device errors * parses the request sense return data buffer, * decides the best SANE_Status for the problem * and produces debug msgs */ static SANE_Status sense_handler (int fd, unsigned char * sensed_data, void *arg) { struct scanner *s = arg; unsigned int ili = get_RS_ILI (sensed_data); unsigned int sk = get_RS_sense_key (sensed_data); unsigned int asc = get_RS_ASC (sensed_data); unsigned int ascq = get_RS_ASCQ (sensed_data); DBG (5, "sense_handler: start\n"); /* kill compiler warning */ (void) fd; /* save for later */ s->rs_info = get_RS_information (sensed_data); DBG (5, "SK=%#02x, ASC=%#02x, ASCQ=%#02x, ILI=%d, info=%#08lx\n", sk, asc, ascq, ili, (unsigned long)s->rs_info); switch (sk) { /* no sense */ case 0x0: 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 (ili) { DBG (5, "No sense: ILI set\n"); return SANE_STATUS_EOF; } DBG (5, "No sense: ready\n"); return SANE_STATUS_GOOD; /* not ready */ case 0x2: if (0x80 != 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: end of job\n"); return SANE_STATUS_NO_DOCS; break; /* hardware error */ case 0x4: if (0x3b != asc) { DBG (5, "Hardware error: unknown asc\n"); return SANE_STATUS_IO_ERROR; } if (0x05 == ascq) { DBG (5, "Hardware error: paper jam\n"); return SANE_STATUS_JAMMED; } if (0x80 == ascq) { DBG (5, "Hardware error: multi-feed\n"); return SANE_STATUS_JAMMED; } DBG (5, "Hardware error: unknown ascq\n"); return SANE_STATUS_IO_ERROR; break; /* illegal request */ case 0x5: if (asc != 0x20 && asc != 0x24 && asc != 0x25 && asc != 0x26 && asc != 0x83 && asc != 0x8f) { DBG (5, "Illegal request: unknown asc\n"); return SANE_STATUS_IO_ERROR; } if (0x20 == asc && 0x00 == ascq) { DBG (5, "Illegal request: invalid opcode\n"); return SANE_STATUS_INVAL; } if (0x24 == asc && 0x00 == ascq) { DBG (5, "Illegal request: invalid field in CDB\n"); return SANE_STATUS_INVAL; } if (0x25 == asc && 0x00 == ascq) { DBG (5, "Illegal request: invalid LUN\n"); return SANE_STATUS_INVAL; } if (0x26 == asc && 0x00 == ascq) { DBG (5, "Illegal request: invalid field in params\n"); return SANE_STATUS_INVAL; } if (0x83 == asc && 0x00 == ascq) { DBG (5, "Illegal request: command failed, check log\n"); return SANE_STATUS_INVAL; } if (0x83 == asc && 0x01 == ascq) { DBG (5, "Illegal request: command failed, invalid state\n"); return SANE_STATUS_INVAL; } if (0x83 == asc && 0x02 == ascq) { DBG (5, "Illegal request: command failed, critical error\n"); return SANE_STATUS_INVAL; } if (0x8f == asc && 0x00 == ascq) { DBG (5, "Illegal request: no image\n"); return SANE_STATUS_DEVICE_BUSY; } DBG (5, "Illegal request: unknown asc/ascq\n"); return SANE_STATUS_IO_ERROR; break; /* unit attention */ case 0x6: if (asc != 0x29 && asc != 0x80) { DBG (5, "Unit attention: unknown asc\n"); return SANE_STATUS_IO_ERROR; } if (0x29 == asc && 0x60 == ascq) { DBG (5, "Unit attention: device reset\n"); return SANE_STATUS_GOOD; } if (0x80 == asc && 0x00 == ascq) { DBG (5, "Unit attention: Energy Star warm up\n"); return SANE_STATUS_DEVICE_BUSY; } if (0x80 == asc && 0x01 == ascq) { DBG (5, "Unit attention: lamp warm up for scan\n"); return SANE_STATUS_DEVICE_BUSY; } if (0x80 == asc && 0x02 == ascq) { DBG (5, "Unit attention: lamp warm up for cal\n"); return SANE_STATUS_DEVICE_BUSY; } if (0x80 == asc && 0x04 == ascq) { DBG (5, "Unit attention: calibration failed\n"); return SANE_STATUS_INVAL; } DBG (5, "Unit attention: unknown asc/ascq\n"); return SANE_STATUS_IO_ERROR; break; /* ia overflow */ case 0x9: if (0x80 == asc && 0x00 == ascq) { DBG (5, "IA overflow: IA field overflow\n"); return SANE_STATUS_IO_ERROR; } DBG (5, "IA overflow: unknown asc/ascq\n"); return SANE_STATUS_IO_ERROR; break; /* volume overflow */ case 0xd: if (0x80 == asc && 0x00 == ascq) { DBG (5, "Volume overflow: Image buffer full\n"); return SANE_STATUS_IO_ERROR; } DBG (5, "Volume overflow: unknown asc/ascq\n"); return SANE_STATUS_IO_ERROR; break; default: DBG (5, "Unknown Sense Code\n"); return SANE_STATUS_IO_ERROR; } DBG (5, "sense_handler: should never happen!\n"); return SANE_STATUS_IO_ERROR; } /* SANE_Status do_rs(scanner * s) { SANE_Status ret; unsigned char cmd[REQUEST_SENSE_len]; size_t cmdLen = REQUEST_SENSE_len; DBG (10, "do_rs: start\n"); memset(cmd,0,cmdLen); set_SCSI_opcode(cmd,REQUEST_SENSE_code); set_SR_datatype_code(cmd,SR_datatype_random); set_SR_datatype_qual(cmd,SR_qual_end); ret = do_cmd ( s, 1, 0, cmd, cmdLen, NULL, 0, NULL, NULL ); while(ret == SANE_STATUS_DEVICE_BUSY){ ret = run_rs(s); } DBG (10, "do_rs: finish\n"); return SANE_STATUS_GOOD; } */ SANE_Status do_cmd(struct scanner *s, int runRS, int shortTime, unsigned char * cmdBuff, size_t cmdLen, unsigned char * outBuff, size_t outLen, unsigned char * inBuff, size_t * inLen ) { SANE_Status ret = SANE_STATUS_GOOD; /*shut up compiler*/ (void) runRS; (void) shortTime; DBG(10, "do_cmd: start\n"); DBG(25, "cmd: writing %d bytes\n", (int)cmdLen); hexdump(30, "cmd: >>", cmdBuff, cmdLen); 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); } ret = sanei_scsi_cmd2(s->fd, cmdBuff, cmdLen, outBuff, outLen, inBuff, inLen); if(ret != SANE_STATUS_GOOD && ret != SANE_STATUS_EOF){ DBG(5,"do_cmd: return '%s'\n",sane_strstatus(ret)); return ret; } /* FIXME: should we look at s->rs_info here? */ if (inBuff && inLen){ hexdump(30, "in: <<", inBuff, *inLen); DBG(25, "in: read %d bytes\n", (int)*inLen); } DBG(10, "do_cmd: finish\n"); return ret; } #if 0 /* unused */ static SANE_Status wait_scanner(struct scanner *s) { int ret; unsigned char cmd[TEST_UNIT_READY_len]; size_t cmdLen = TEST_UNIT_READY_len; DBG (10, "wait_scanner: start\n"); memset(cmd,0,cmdLen); set_SCSI_opcode(cmd,TEST_UNIT_READY_code); ret = do_cmd ( s, 0, 1, cmd, cmdLen, NULL, 0, NULL, NULL ); if (ret != SANE_STATUS_GOOD) { DBG(5,"WARNING: Brain-dead scanner. Hitting with stick\n"); ret = do_cmd ( s, 0, 1, cmd, cmdLen, NULL, 0, NULL, NULL ); } if (ret != SANE_STATUS_GOOD) { DBG(5,"WARNING: Brain-dead scanner. Hitting with stick again\n"); ret = do_cmd ( s, 0, 1, cmd, cmdLen, NULL, 0, NULL, NULL ); } if (ret != SANE_STATUS_GOOD) { DBG (5, "wait_scanner: error '%s'\n", sane_strstatus (ret)); } DBG (10, "wait_scanner: finish\n"); return ret; } #endif /* 0 - unused */ /** * 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; } return max_size; } /** * 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; if(DBG_LEVEL < level) return; 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; } *ptr = '\0'; DBG (level, "%s\n", line); } /** * 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 (15, "%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) { DBG (10, "sane_get_select_fd\n"); DBG (15, "%p %d\n", h, *fdp); return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/kodak.conf.in000066400000000000000000000005571456256263500172460ustar00rootroot00000000000000# 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 32768 (the default) option buffer-size 32768 # To search for all KODAK scsi devices scsi KODAK # To use a specific scsi device #scsi /dev/sg1 backends-1.3.0/backend/kodak.h000066400000000000000000000177501456256263500161460ustar00rootroot00000000000000#ifndef KODAK_H #define KODAK_H /* * Part of SANE - Scanner Access Now Easy. * Please see opening comment in kodak.c */ /* ------------------------------------------------------------------------- * 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 * still the possibility to say so, later. */ enum kodak_Option { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_SOURCE, /*front/back/duplex*/ OPT_MODE, /*mono/ht/gray/color*/ OPT_RES, OPT_GEOMETRY_GROUP, OPT_TL_X, OPT_TL_Y, OPT_BR_X, OPT_BR_Y, OPT_PAGE_WIDTH, OPT_PAGE_HEIGHT, OPT_ENHANCEMENT_GROUP, OPT_BRIGHTNESS, OPT_CONTRAST, OPT_THRESHOLD, OPT_RIF, /* must come last: */ NUM_OPTIONS }; struct scanner { /* --------------------------------------------------------------------- */ /* immutable values which are set during init of scanner. */ struct scanner *next; char *device_name; /* The name of the scanner device for sane */ /* --------------------------------------------------------------------- */ /* immutable values which are set during reading of config file. */ int buffer_size; /* --------------------------------------------------------------------- */ /* 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]; /* null-term data returned by SCSI inquiry.*/ char product_name[17]; /* null-term data returned by SCSI inquiry.*/ char version_name[5]; /* null-term data returned by SCSI inquiry.*/ char build_name[3]; /* null-term data returned by SCSI inquiry.*/ /* --------------------------------------------------------------------- */ /* immutable values which are set during INQUIRY probing of the scanner, */ /* or in the init_model cleanup routine... */ /* which modes scanner has */ int s_mode[4]; /* min and max resolution for each mode */ int s_res_min[4]; int s_res_max[4]; /* in 1/1200th inches, NOT sane units */ int s_width_min; int s_width_max; int s_length_min; int s_length_max; int s_brightness_steps; int s_contrast_steps; int s_threshold_steps; int s_rif; /* --------------------------------------------------------------------- */ /* 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 */ /* long array of option structs */ SANE_Option_Descriptor opt[NUM_OPTIONS]; /*mode group*/ SANE_String_Const o_source_list[4]; SANE_String_Const o_mode_list[5]; SANE_Int o_res_list[4][6]; /*geometry group*/ SANE_Range o_tl_x_range; SANE_Range o_tl_y_range; SANE_Range o_br_x_range; SANE_Range o_br_y_range; SANE_Range o_page_x_range; SANE_Range o_page_y_range; /*enhancement group*/ SANE_Range o_brightness_range; SANE_Range o_contrast_range; SANE_Range o_threshold_range; /* --------------------------------------------------------------------- */ /* changeable vars to hold user input. modified by SANE_Options above */ /*mode group*/ int u_mode; /* color,lineart,etc */ int u_source; /* adf front,adf duplex,etc */ int u_res; /* resolution in dpi */ /*geometry group*/ /* The desired size of the scan, all in 1/1200 inch */ int u_tl_x; int u_tl_y; int u_br_x; int u_br_y; int u_page_width; int u_page_height; /*enhancement group*/ int u_brightness; int u_contrast; int u_threshold; int u_rif; int u_compr; /* --------------------------------------------------------------------- */ /* values which are set by the scanner's post-scan image header */ int i_bytes; int i_id; int i_dpi; int i_tlx; int i_tly; int i_width; int i_length; int i_bpp; int i_compr; /* --------------------------------------------------------------------- */ /* values which are set by scanning functions to keep track of pages, etc */ int started; /* total to read/write */ int bytes_tot; /* how far we have read */ int bytes_rx; /* how far we have written */ int bytes_tx; /* size of buffer */ int bytes_buf; /* the buffer */ unsigned char * buffer; /* --------------------------------------------------------------------- */ /* values which used by the command and data sending functions (scsi/usb)*/ int fd; /* The scanner device file descriptor. */ size_t rs_info; }; #define DEFAULT_BUFFER_SIZE 32768 #define SIDE_FRONT 0 #define SIDE_BACK 1 #define SOURCE_ADF_FRONT 0 #define SOURCE_ADF_BACK 1 #define SOURCE_ADF_DUPLEX 2 #define MODE_LINEART 0 #define MODE_HALFTONE 1 #define MODE_GRAYSCALE 2 #define MODE_COLOR 3 /* ------------------------------------------------------------------------- */ #define MM_PER_INCH 25.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 KODAK_CONFIG_FILE "kodak.conf" #ifndef PATH_MAX # define PATH_MAX 1024 #endif /* ------------------------------------------------------------------------- */ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize); SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only); SANE_Status sane_open (SANE_String_Const name, SANE_Handle * handle); SANE_Status sane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking); SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int * fdp); 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, SANE_Action action, void *val, SANE_Int * info); SANE_Status sane_start (SANE_Handle handle); SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params); SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len); void sane_cancel (SANE_Handle h); void sane_close (SANE_Handle h); void sane_exit (void); /* ------------------------------------------------------------------------- */ static SANE_Status attach_one (const char *name); static SANE_Status connect_fd (struct scanner *s); static SANE_Status disconnect_fd (struct scanner *s); static SANE_Status sense_handler (int scsi_fd, u_char * result, void *arg); static SANE_Status init_inquire (struct scanner *s); static SANE_Status init_model (struct scanner *s); static SANE_Status init_user (struct scanner *s); static SANE_Status init_options (struct scanner *s); static SANE_Status do_cmd(struct scanner *s, int runRS, int shortTime, unsigned char * cmdBuff, size_t cmdLen, unsigned char * outBuff, size_t outLen, unsigned char * inBuff, size_t * inLen ); #if 0 /* unused */ static SANE_Status wait_scanner (struct scanner *s); #endif static SANE_Status do_cancel (struct scanner *scanner); static SANE_Status set_window (struct scanner *s); static SANE_Status read_imageheader(struct scanner *s); static SANE_Status send_sc(struct scanner *s); static SANE_Status read_from_scanner(struct scanner *s); static SANE_Status copy_buffer(struct scanner *s, unsigned char * buf, int len); static SANE_Status read_from_buffer(struct scanner *s, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len); static void hexdump (int level, char *comment, unsigned char *p, int l); static size_t maxStringSize (const SANE_String_Const strings[]); #endif /* KODAK_H */ backends-1.3.0/backend/kodakaio.c000066400000000000000000003345761456256263500166420ustar00rootroot00000000000000/* * kodakaio.c - SANE library for Kodak ESP Aio scanners. * * Copyright (C) 2011-2017 Paul Newall * * Based on the Magicolor sane backend: * Based on the epson2 sane backend: * Based on Kazuhiro Sasayama previous * work on epson.[ch] file from the SANE package. * Please see those files for additional copyrights. * Author: Paul Newall * * 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, version 2. * Modified 30/12/14 to fix bug where network connection was broken after 30s of idle time. * The connection is now made in sane_start and ended in sane_cancel. * 01/01/13 Now with adf, the scan can be padded to make up the full page length, * or the page can terminate at the end of the paper. This is a selectable option. * 25/11/12 Using avahi now for net autodiscovery. Use configure option --with-avahi to make sure it's enabled * 1/5/17 patched to use local pointer for avahi callback */ /* Packages to add to a clean ubuntu install libavahi-client-dev libusb-dev libsnmp-dev convenient lines to paste export SANE_DEBUG_KODAKAIO=20 for ubuntu prior to 12.10 ./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var BACKENDS="kodakaio test" for ubuntu 12.10 ./configure --prefix=/usr --libdir=/usr/lib/i386-linux-gnu --sysconfdir=/etc --localstatedir=/var BACKENDS="kodakaio test" for ubuntu 14.10 up to at least 17.04 ./configure --prefix=/usr --libdir=/usr/lib/x86_64-linux-gnu --sysconfdir=/etc --localstatedir=/var BACKENDS="kodakaio test" If you want to use the test backend, for example with sane-troubleshoot, you should enable it in /etc/sane.d/dll.conf */ /* SANE-FLOW-DIAGRAM Kodakaio commands in [] brackets - sane_init() : initialize backend, attach scanners(devicename,0) . - sane_get_devices() : query list of scanner-devices . - sane_open() : open a particular scanner-device and attach_scanner(devicename,&dev) . . - 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 [V,L,F,S,C,D,O,Z] first time or after cancel. [(F),E,G] every time . . - sane_get_parameters() : returns actual scan-parameters . . - sane_read() : read image-data (from pipe) . . - sane_cancel() : cancel operation, kill reader_process [(F), U] . - sane_close() : close opened scanner-device, do_cancel, free buffer and handle - sane_exit() : terminate use of backend, free devicename and device-struture */ /* FUNCTION-TREE sane_init sane_open device_detect k_dev_init open_scanner close_scanner sane_get_devices init_options (open_scanner - moved to sane_start 27/12/14 ) sane_control_option getvalue setvalue search_string_list change_source activateOption deactivateOption sane_start open_scanner k_init_parametersta k_lock_scanner k_hello k_set_scanning_parameters print_params k_start_scan cmd_start_scan print_status k_send kodakaio_txrxack sane_get_parameters print_params sane_read k_read cmd_read_data (reads one block) k_recv cmp_array sane_cancel cmd_cancel_scan close_scanner sane_close (close_scanner - moved to sane_cancel 27/12/14) sane_exit free_devices k_recv kodakaio_net_read dump_hex_buffer_dense k_send sanei_kodakaio_net_write_raw dump_hex_buffer_dense open_scanner sanei_kodakaio_net_open close_scanner k_scan_finish cmd_cancel_scan sanei_kodakaio_net_close or sanei_usb_close detect_usb kodakaio_getNumberOfUSBProductIds attach_one_config - (Passed to sanei_configure_attach) kodakaio_getNumberOfUSBProductIds kodak_network_discovery client_callback browse_callback resolve_callback ProcessAvahiDevice attach_one_net attach_one_net attach device_detect attach_one_usb - (passed to sanei_usb_find_devices) attach device_detect k_lock_scanner kodakaio_txrx k_send k_recv kodakaio_txrxack k_send k_recv cmd_set_color_curve kodakaio_expect_ack k_recv cmd_cancel_scan kodakaio_txrxack cmd_set_scanning_parameters kodakaio_txrxack device_detect k_dev_init */ #define KODAKAIO_VERSION 02 #define KODAKAIO_REVISION 7 #define KODAKAIO_BUILD 3 /* for usb (but also used for net though it's not required). */ #define MAX_BLOCK_SIZE 32768 #define SCANNER_READ_TIMEOUT 15 /* POLL_ITN_MS sets the individual poll timeout for network discovery */ #define POLL_ITN_MS 20 /* debugging levels: In terminal use: export SANE_DEBUG_KODAKAIO=40 to set the level to 40 or whatever level you want. Then you can scan with scanimage or simple-scan from terminal and see debug info use these defines to promote certain functions that you are interested in define low values to make detail of a section appear when DBG level is low define a high value eg 99 to get normal behaviour. */ #define DBG_READ 99 #define DBG_AUTO 99 /* for autodiscovery */ /* normal levels. This system is a plan rather than a reality * * 127 recv buffer * 125 send buffer * 35 fine-grained status and progress * 30 sane_read * 25 setvalue, getvalue, control_option * 20 low-level (I/O) functions * 15 mid-level functions * 10 high-level functions * 7 open/close/attach * 6 print_params * 5 basic functions * 3 status info and progress * 2 sane api * 1 errors & warnings */ #include "sane/config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #if WITH_AVAHI /* used for auto detecting network printers */ #include #include #include #include #include #include #endif #include "../include/sane/saneopts.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_tcp.h" #include "../include/sane/sanei_udp.h" #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_backend.h" #include "kodakaio.h" /* vendor and product ids that are allowed */ #define SANE_KODAKAIO_VENDOR_ID (0x040a) #define min(x,y) (((x)<(y))?(x):(y)) /* I think these timeouts (ms) are defaults, overridden by any timeouts in the kodakaio.conf file */ static int K_SNMP_Timeout = 3000; /* used for any auto detection method */ static int K_Scan_Data_Timeout = 10000; static int K_Request_Timeout = 5000; /* static int bitposn=0; was used to pack bits into bytes in lineart mode */ /* This file is used to store directly the raster returned by the scanner for debugging If RawScanPath has no length it will not be created */ FILE *RawScan = NULL; /* example: char RawScanPath[] = "TestRawScan.pgm"; */ char RawScanPath[] = ""; /* empty path means no raw scan file is made */ /* * Devices supported by this backend */ /* kodak command strings */ static unsigned char KodakEsp_V[] = {0x1b,'S','V',0,0,0,0,0}; /* version?? */ static unsigned char KodakEsp_v[] = {0x1b,'s','v',0,0,0,0,0}; /* reply to version?? */ static unsigned char KodakEsp_Lock[] = {0x1b,'S','L',0,0,0,0,0}; /* Locks scanner */ static unsigned char KodakEsp_UnLock[] = {0x1b,'S','U',0,0,0,0,0}; /* Unlocks scanner */ static unsigned char KodakEsp_Ack[] = {0x1b,'S','S',0,0,0,0,0}; /* Acknowledge for all commands */ /* the bytes after esc S S 0 may indicate status: S S 0 1 = docs in adf */ static unsigned char KodakEsp_F[] = {0x1b,'S','F',0,0,0,0,0}; /* Purpose not known? colour balance?*/ static unsigned char KodakEsp_Comp[] = {0x1b,'S','C',3,8,3,0,0}; /* 3,8,3,1,0 does compression. */ /* The compression method is unknown */ /* static unsigned char KodakEsp_E[] = {0x1b,'S','E',1,0,0,0,0}; NET Purpose not known */ /* the extra 1 below could be despeckle option? maybe only for Hero 9.1 but no errors with ESP5250 */ static unsigned char KodakEsp_E[] = {0x1b,'S','E',1,1,0,0,0}; static unsigned char KodakEsp_Go[] = {0x1b,'S','G',0,0,0,0,0}; /* Starts the scan */ /* Other commands are: D (resolution), O (top left), Z (bottom right), R, G, B (curves) */ /* What is the relationship between these and the ranges in cap? */ static SANE_Int kodakaio_resolution_list[] = {75, 150, 300, 600, 1200}; static SANE_Int kodakaio_depth_list[] = {1,8}; /* The first value is the number of following entries */ /* strings to try and match the model ';' separator static unsigned char SupportedMatchString[] = "KODAK ESP;KODAK HERO;KODAK OFFICE HERO;ADVENT WiFi AIO;"; */ static struct KodakaioCap kodakaio_cap[] = { /* usbid,commandtype, modelname, USBoutEP, USBinEP, opticalres, {dpi range}, pointer to res list, res list size max depth, pointer to depth list, flatbed x range, flatbed y range, adf present, adf duplex, adf x range, adf y range (y range should be set a little shorter than the paper being scanned) The following are not used but may be in future commandtype, max depth, pointer to depth list */ /* list of cap data the first scanner is the default */ /* KODAK AIO DEFAULT, */ { 0x9999, "esp", "KODAK AIO DEFAULT", -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_FALSE, SANE_FALSE, /* ADF, duplex */ {0, SANE_FIX(100), 0}, {0, SANE_FIX(100), 0} /* ADF x/y ranges (TODO!) */ }, /* KODAK ESP 5100, */ { 0x4025, "esp", "KODAK ESP 5100 AiO", -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_FALSE, SANE_FALSE, /* ADF, duplex */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK ESP 5300, */ { 0x4026, "esp", "KODAK ESP 5300 AiO", -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_FALSE, SANE_FALSE, /* ADF, duplex */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK ESP 5500, */ { 0x4027, "esp", "KODAK ESP 5500 AiO", -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_FALSE, SANE_FALSE, /* ADF, duplex */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK ESP 5000, */ { 0x4028, "esp", "KODAK ESP 5000 Series AiO", -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_FALSE, SANE_FALSE, /* ADF, duplex */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK ESP 3300, */ { 0x4031, "esp", "KODAK ESP 3300 Series AiO", -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_FALSE, SANE_FALSE, /* ADF, duplex */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK ESP5, */ { 0x4032, "esp", "KODAK ESP 5 AiO", -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_FALSE, SANE_FALSE, /* ADF, duplex */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK ESP7, */ { 0x403E, "esp", "KODAK ESP 7 AiO", -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_FALSE, SANE_FALSE, /* ADF, duplex */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK ESP9, */ { 0x403F, "esp", "KODAK ESP 9 AiO", -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_FALSE, SANE_FALSE, /* ADF, duplex */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK ESP5210 or 5250, */ { 0x4041, "esp", "KODAK ESP 5200 Series AiO", -1, 0x82, /* USBoutEP, USBinEP (-1 means find one) */ 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_FALSE, SANE_FALSE, /* ADF, duplex */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK ESP3200 , */ { 0x4043, "esp", "KODAK ESP 3200 Series AiO", -1, 0x82, 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_FALSE, SANE_FALSE, /* ADF, duplex */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK ESP4100 , */ { 0x4053, "esp", "KODAK ESP Office 4100 Series AiO", -1, 0x82, 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_FALSE, SANE_FALSE, /* ADF, duplex */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK ESP6100 , */ { 0x4054, "esp", "KODAK ESP Office 6100 Series AiO", -1, 0x82, 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_TRUE, SANE_FALSE, /* ADF, duplex */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK ESP7200 , */ { 0x4056, "esp", "KODAK ESP 7200 Series AiO", -1, 0x82, 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_FALSE, SANE_FALSE, /* ADF, duplex */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK ESP C110 , */ { 0x4057, "esp", "KODAK ESP C110 AiO", -1, 0x82, 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_FALSE, SANE_FALSE, /* ADF, duplex */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK ESP C115 , */ { 0x4058, "esp", "KODAK ESP C115 AiO", -1, 0x82, 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_FALSE, SANE_FALSE, /* ADF, duplex */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK ESP 2150 , */ { 0x4059, "esp", "KODAK ESP Office 2150 Series", -1, 0x82, 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_TRUE, SANE_FALSE, /* ADF, duplex */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK ESP C310 , */ { 0x405D, "esp", "KODAK ESP C310 AiO", -1, 0x82, 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_FALSE, SANE_FALSE, /* ADF, duplex */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK ESP C315 , */ { 0x405E, "esp", "KODAK ESP C315 AiO", -1, 0x82, 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_FALSE, SANE_FALSE, /* ADF, duplex */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* ADVENT AW10, */ { 0x4060, "esp", "ADVENT WiFi AIO AW10", -1, 0x82, 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, {from, to, 0} 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_TRUE, SANE_FALSE, /* ADF, duplex. */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK HERO 6.1, */ { 0x4062, "esp", "KODAK OFFICE HERO 6.1 AiO", -1, 0x82, 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, {from, to, 0} 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_TRUE, SANE_FALSE, /* ADF, duplex. */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK HERO 7.1, */ { 0x4063, "esp", "KODAK HERO 7.1 AiO", -1, 0x82, 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, {from, to, 0} 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_FALSE, SANE_TRUE, /* ADF, duplex. */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK HERO 5.1, */ { 0x4064, "esp", "KODAK HERO 5.1 AiO", -1, 0x82, 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, {from, to, 0} 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_FALSE, SANE_TRUE, /* ADF, duplex.*/ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK ESP9200 , */ { 0x4065, "esp", "KODAK ESP 9200 Series AiO", -1, 0x82, 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_TRUE, SANE_FALSE, /* ADF, duplex */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK ESP2170 , */ { 0x4066, "esp", "KODAK ESP Office 2170 Series", -1, 0x82, 1200, {75, 1200, 0}, kodakaio_resolution_list, 5, /* 1200 dpi optical, {from, to, 0} 5 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_TRUE, SANE_FALSE, /* ADF, duplex */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK HERO 9.1, */ { 0x4067, "esp", "KODAK HERO 9.1 AiO", -1, 0x82, 1200, {75, 1200, 0}, kodakaio_resolution_list, 5, /* 1200 dpi optical, {from, to, 0} 5 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_TRUE, SANE_FALSE, /* ADF, duplex. */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK HERO 4.1, */ { 0x4069, "esp", "KODAK HERO 4.1 AiO", -1, 0x82, 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, {from, to, 0} 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_TRUE, SANE_FALSE, /* ADF, duplex.*/ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* KODAK HERO 3.1, */ { 0x406D, "esp", "KODAK HERO 3.1 AiO", -1, 0x82, 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, {from, to, 0} 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_FALSE, SANE_TRUE, /* ADF, duplex. */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ }, /* spare use for specified usbid */ { 0, "esp", "specified", -1, 0x82, 600, {75, 600, 0}, kodakaio_resolution_list, 4, /* 600 dpi max, {from, to, 0} 4 resolutions */ 8, kodakaio_depth_list, /* color depth max 8, list above */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(11.7 * MM_PER_INCH), 0}, /* FBF x/y ranges */ SANE_FALSE, SANE_TRUE, /* ADF, duplex. */ {0, SANE_FIX(8.5 * MM_PER_INCH), 0}, {0, SANE_FIX(14 * MM_PER_INCH), 0} /* 14 ADF x/y ranges */ } }; /**************************************************************************** * General configuration parameter definitions ****************************************************************************/ /* * Definition of the mode_param struct, that is used to * specify the valid parameters for the different scan modes. * * The depth variable gets updated when the bit depth is modified. */ /* could be affecting what data sane delivers */ static struct mode_param mode_params[] = { {0x03, 3, 24}, /* Color, 3 colors, 24 bit */ {0x02, 1, 8}, /* Grayscale, 1 color, 8 bit */ {0x00, 1, 1} /* Lineart, 1 color, 8 bit (was 8 bit) */ }; static SANE_String_Const mode_list[] = { SANE_VALUE_SCAN_MODE_COLOR, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_LINEART, NULL }; static const SANE_String_Const adf_mode_list[] = { SANE_I18N("Simplex"), SANE_I18N("Duplex"), NULL }; /* Define the different scan sources */ #define FBF_STR SANE_I18N("Flatbed") #define ADF_STR SANE_I18N("Automatic Document Feeder") /* * source list need one dummy entry (save device settings is crashing). * NOTE: no const - this list gets created while exploring the capabilities * of the scanner. Here space is reserved for 3 entries + NULL ? */ static SANE_String_Const source_list[] = { FBF_STR, NULL, NULL, NULL }; static const SANE_Range percent_range_fixed = {SANE_FIX(0.0), SANE_FIX(100.0), SANE_FIX(1.0)}; /*static const SANE_Range percent_range_int = {0, 100, 1};*/ /* prototypes */ static SANE_Status attach_one_usb(SANE_String_Const devname); static SANE_Status attach_one_net(SANE_String_Const devname, unsigned int device); void kodakaio_com_str(unsigned char *buf, char *fmt_buf); int cmparray (unsigned char *array1, unsigned char *array2, size_t len); #if WITH_AVAHI static struct KodakaioCap *get_device_from_identification (const char *ident, const char *vid, const char *pid); void ProcessAvahiDevice(const char *device_id, const char *vid, const char *pid, const char *ip_addr); #endif /* Some utility functions */ static size_t max_string_size(const SANE_String_Const strings[]) { /* returns the length of the longest string in an array of strings */ size_t size, max_size = 0; int i; for (i = 0; strings[i]; i++) { size = strlen(strings[i]) + 1; if (size > max_size) max_size = size; } return max_size; } static void print_params(const SANE_Parameters params, int level) { DBG(level, "formats: binary=?, grey=%d, colour=%d\n",SANE_FRAME_GRAY, SANE_FRAME_RGB ); DBG(level, "params.format = %d\n", params.format); DBG(level, "params.last_frame = %d\n", params.last_frame); DBG(level, "params.bytes_per_line = %d\n", params.bytes_per_line); DBG(level, "params.pixels_per_line = %d\n", params.pixels_per_line); DBG(level, "params.lines = %d\n", params.lines); DBG(level, "params.depth = %d\n", params.depth); } static void print_status(KodakAio_Scanner *s,int level) { DBG(level, "print_status with level %d\n", level); DBG(level, "s->bytes_unread = %d\n", s->bytes_unread); /* DBG(level, "params.last_frame = %d\n", params.last_frame); DBG(level, "params.bytes_per_line = %d\n", params.bytes_per_line); DBG(level, "params.pixels_per_line = %d\n", params.pixels_per_line); DBG(level, "params.lines = %d\n", params.lines); DBG(level, "params.depth = %d\n", params.depth); */ } /**************************************************************************** * Low-level Network communication functions ****************************************************************************/ static int kodakaio_net_read(struct KodakAio_Scanner *s, unsigned char *buf, size_t wanted, SANE_Status * status) /* there seems to be a condition where this returns no error and no data without detecting a timeout That is probably if the scanner disconnected the network connection */ { size_t size, read = 0; struct pollfd fds[1]; int pollreply; *status = SANE_STATUS_GOOD; /* poll for data-to-be-read (using K_Request_Timeout) */ fds[0].fd = s->fd; fds[0].events = POLLIN; fds[0].revents = 0; if ((pollreply = poll (fds, 1, K_Request_Timeout)) <= 0) { if (pollreply == 0) DBG(1, "net poll timeout\n"); else /* pollreply is -ve */ DBG(1, "net poll error\n"); *status = SANE_STATUS_IO_ERROR; } else if((fds[0].revents & POLLIN) && !(fds[0].revents & (POLLERR | POLLHUP | POLLNVAL))) { while (read < wanted) { DBG(50, "reading: read %lu, wanted %lu\n",read, wanted); size = sanei_tcp_read(s->fd, buf + read, wanted - read); if (size == 0) { DBG(1, "No data read. Scanner may have disconnected\n"); break; } read += size; } if (read == 0) *status = SANE_STATUS_IO_ERROR; DBG(32, "net read %lu bytes:%x,%x,%x,%x,%x,%x,%x,%x\n",(unsigned long)read,buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7]); } else DBG(1, "Unknown problem with poll\n"); return read; } static int sanei_kodakaio_net_write_raw(struct KodakAio_Scanner *s, const unsigned char *buf, size_t buf_size, SANE_Status *status) { DBG(32, "net write:%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]); sanei_tcp_write(s->fd, buf, buf_size); /* TODO: Check whether sending failed... */ *status = SANE_STATUS_GOOD; return buf_size; } static SANE_Status sanei_kodakaio_net_open(struct KodakAio_Scanner *s) { struct timeval tv; tv.tv_sec = 5; tv.tv_usec = 0; DBG(5, "%s\n", __func__); setsockopt(s->fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)); return SANE_STATUS_GOOD; } static SANE_Status sanei_kodakaio_net_close(struct KodakAio_Scanner *s) { NOT_USED(s); /* Does nothing - maybe should close the socket ? */ return SANE_STATUS_GOOD; } /**************************************************************************** * Low-level USB communication functions ****************************************************************************/ static int kodakaio_getNumberOfUSBProductIds (void) { return sizeof (kodakaio_cap) / sizeof (struct KodakaioCap); } /**************************************************************************** * low-level communication commands ****************************************************************************/ static void dump_hex_buffer_dense (int level, const unsigned char *buf, size_t buf_size) { size_t k; char msg[1024], fmt_buf[1024]; memset (&msg[0], 0x00, 1024); memset (&fmt_buf[0], 0x00, 1024); for (k = 0; k < min(buf_size, 80); k++) { if (k % 16 == 0) { if (k>0) { DBG (level, "%s\n", msg); memset (&msg[0], 0x00, 1024); } sprintf (fmt_buf, " 0x%04lx ", (unsigned long)k); strcat (msg, fmt_buf); } if (k % 8 == 0) { strcat (msg, " "); } sprintf (fmt_buf, " %02x" , buf[k]); strcat (msg, fmt_buf); } if (msg[0] != 0 ) { DBG (level, "%s\n", msg); } } /* changing params to char seems to cause a stack problem */ void kodakaio_com_str(unsigned char *buf, char *fmt_buf) { /* returns a printable string version of the first 8 bytes assuming they are a kodakaio command*/ if(buf[0] == 0x1b) { sprintf (fmt_buf, "esc %c %c %02x %02x %02x %02x %02x", buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); } else { sprintf (fmt_buf, "%02x %02x %02x %02x %02x %02x %02x %02x", buf[0],buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); } } static int k_send(KodakAio_Scanner * s, void *buf, size_t buf_size, SANE_Status * status) { char fmt_buf[25]; kodakaio_com_str(buf, fmt_buf); DBG(15, "%s: size = %lu :%s\n", __func__, (u_long) buf_size, fmt_buf); if (DBG_LEVEL >= 125) { const unsigned char *s = buf; DBG(125, "complete buffer:\n"); dump_hex_buffer_dense (125, s, buf_size); } if (s->hw->connection == SANE_KODAKAIO_NET) { return sanei_kodakaio_net_write_raw(s, buf, buf_size, status); } else if (s->hw->connection == SANE_KODAKAIO_USB) { size_t n; n = buf_size; *status = sanei_usb_write_bulk(s->fd, buf, &n); DBG(50, "USB: wrote %lu bytes, status: %s\n", (unsigned long)n, sane_strstatus(*status)); return n; } *status = SANE_STATUS_INVAL; return 0; } static ssize_t k_recv(KodakAio_Scanner * s, void *buf, ssize_t buf_size, SANE_Status * status) { /* requests and receives data this function makes the split between USB and NET this function called by a number of others In USB mode, this function will wait until data is available for a maximum of SCANNER_READ_TIMEOUT seconds. In NET mode the timeout is in kodakaio_net_read */ ssize_t n = 0; char fmt_buf[25]; time_t time_start; time_t time_now; struct timespec usb_delay, usb_rem; usb_delay.tv_sec = 0; usb_delay.tv_nsec = 300000000; /* 0.3 sec */ if (s->hw->connection == SANE_KODAKAIO_NET) { time(&time_start); DBG(min(16,DBG_READ), "[%ld] %s: net req size = %ld ", (long) time_start, __func__, (long) buf_size); n = kodakaio_net_read(s, buf, buf_size, status); DBG(min(16,DBG_READ), "returned %lu\n", (unsigned long)n); if (*status != SANE_STATUS_GOOD) { DBG(1, "%s: err returned from kodakaio_net_read, %s\n", __func__, sane_strstatus(*status)); } } else if (s->hw->connection == SANE_KODAKAIO_USB) { /* Start the clock for USB timeout */ time(&time_start); /* Loop until we have data */ while (n == 0) { n = buf_size; /* but what if the data is an exact number of blocks? */ DBG(min(16,DBG_READ), "[%ld] %s: usb req size = %ld ", (long) time_start, __func__, (long) n); *status = sanei_usb_read_bulk(s->fd, (SANE_Byte *) buf, (size_t *) & n); DBG(min(16,DBG_READ), "returned %ld\n", (long) n); if(*status != SANE_STATUS_GOOD) { DBG(min(16,DBG_READ), "sanei_usb_read_bulk gave %s\n", sane_strstatus(*status)); if (*status == SANE_STATUS_EOF) { /* If we have EOF status, wait for more data */ time(&time_now); if (difftime(time_now, time_start) < SCANNER_READ_TIMEOUT) { nanosleep(&usb_delay, &usb_rem); } else { /* Timeout */ return n; } } else { /* If we've encountered another type of error, return */ return n; } } } } if (n == 8) { kodakaio_com_str(buf, fmt_buf); DBG(min(14,DBG_READ), "%s: size = %ld, got %s\n", __func__, (long int)n, fmt_buf); } /* dump buffer if appropriate */ if (DBG_LEVEL >= 127 && n > 0) { const unsigned char* b=buf; dump_hex_buffer_dense (125, b, buf_size); } return n; } static SANE_Status kodakaio_expect_ack(KodakAio_Scanner *s, unsigned char *rxbuf) /* gets 8 byte reply, checks reply is an Ack and returns appropriate status */ { SANE_Status status; k_recv(s, rxbuf, 8, &status); if (status != SANE_STATUS_GOOD) { DBG(1, "%s: rx err, %s\n", __func__, sane_strstatus(status)); return status; } /* strncmp ignores diffent possible responses like escSS00000 and escSS02000 */ if (strncmp((char *)KodakEsp_Ack,(char *)rxbuf,4)!=0) { DBG (1, "No Ack received, Expected 0x%2x %2x %2x %2x... got 0x%2x %2x %2x %2x...\n", KodakEsp_Ack[0], KodakEsp_Ack[1], KodakEsp_Ack[2], KodakEsp_Ack[3],rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3]); return SANE_STATUS_IO_ERROR; } return status; } static SANE_Status kodakaio_txrx(KodakAio_Scanner *s, unsigned char *txbuf, unsigned char *rxbuf) /* Sends 8 byte data to scanner and returns reply and appropriate status. */ { SANE_Status status; ssize_t n = 0; k_send(s, txbuf, 8, &status); if (status != SANE_STATUS_GOOD) { DBG(1, "%s: tx err, %s\n", __func__, sane_strstatus(status)); return status; } n = k_recv(s, rxbuf, 8, &status); if (status != SANE_STATUS_GOOD) { DBG(1, "%s: %s gave rx err, %s\n", __func__, "txvalue", sane_strstatus(status)); return status; } if (n == 0) { DBG(1, "%s: try 1 k_recv returned 0 bytes with status %s\n", __func__, sane_strstatus(status)); n = k_recv(s, rxbuf, 8, &status); if (status != SANE_STATUS_GOOD) { DBG(1, "%s: %s gave rx err, %s\n", __func__, "txvalue", sane_strstatus(status)); return status; } if (n == 0) { DBG(1, "%s: try 2 k_recv returned 0 bytes with status %s\n", __func__, sane_strstatus(status)); return status; } } return status; } static SANE_Status kodakaio_txrxack(KodakAio_Scanner *s, unsigned char *txbuf, unsigned char *rxbuf) /* Sends 8 byte data to scanner, gets 8 byte reply, checks reply is an Ack and returns appropriate status */ { SANE_Status status; k_send(s, txbuf, 8, &status); if (status != SANE_STATUS_GOOD) { DBG(1, "%s: tx err, %s\n", __func__, sane_strstatus(status)); return status; } k_recv(s, rxbuf, 8, &status); if (status != SANE_STATUS_GOOD) { DBG(1, "%s: %s gave rx err, %s\n", __func__, "txvalue", sane_strstatus(status)); return status; } /* strncmp ignores different possible responses like escSS00000 and escSS02000 */ if (strncmp((char *)KodakEsp_Ack,(char *)rxbuf,3) == 0) { /* was 4 byte comp */ if (rxbuf[4] == 0x01 && s->adf_loaded == SANE_FALSE) { s->adf_loaded = SANE_TRUE; DBG(5, "%s: News - docs in ADF\n", __func__); } else if (rxbuf[4] != 0x01 && s->adf_loaded == SANE_TRUE) { s->adf_loaded = SANE_FALSE; DBG(5, "%s: News - ADF is empty\n", __func__); } } else { DBG (1, "No Ack received, Sent 0x%2x %2x %2x %2x... got 0x%2x %2x %2x %2x...\n", txbuf[0], txbuf[1], txbuf[2], txbuf[3],rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3]); return SANE_STATUS_IO_ERROR; } return status; } /* unused function static ssize_t kodakaio_rxflush(KodakAio_Scanner *s) Tries to get 64 byte reply and returns number of bytes read { SANE_Status status; unsigned char rxbuf[64]; ssize_t n = 0; n = k_recv(s, rxbuf, 64, &status); if (status != SANE_STATUS_GOOD) { DBG(1, "%s: %s gave rx err, %s\n", __func__, "status", sane_strstatus(status)); } DBG(5, "%s: flushed, %d bytes\n", __func__, (int)n); return n; } */ /* * high-level communication commands */ static SANE_Status k_hello (KodakAio_Scanner * s) { SANE_Status status; unsigned char reply[8]; char fmt_buf[25]; DBG(5, "%s\n", __func__); /* check that there is nothing already in the input buffer before starting kodakaio_rxflush(s); */ /* preset the reply, so I can see if it gets changed */ reply[0] = 0; reply[1] = 1; reply[2] = 2; reply[3] = 3; reply[4] = 4; reply[5] = 5; reply[6] = 6; reply[7] = 7; if((status = kodakaio_txrx(s, KodakEsp_V, reply))!= SANE_STATUS_GOOD) { DBG(1, "%s: KodakEsp_V failure, %s\n", __func__, sane_strstatus(status)); return SANE_STATUS_IO_ERROR; } if (strncmp((char *) reply, (char *) KodakEsp_v, 3)!=0) { kodakaio_com_str(reply, fmt_buf); DBG(1, "%s: KodakEsp_v err, got %s\n", __func__, fmt_buf); return SANE_STATUS_IO_ERROR; } DBG(5, "%s: OK %s\n", __func__, sane_strstatus(status)); return status; } /* Start scan command */ static SANE_Status cmd_start_scan (SANE_Handle handle, size_t expect_total) /* expect_total is the expected total no of bytes just for info -ve value not a problem? or is it for size_t? */ { KodakAio_Scanner *s = (KodakAio_Scanner *) handle; SANE_Status status = SANE_STATUS_GOOD; unsigned char reply[8]; /*send the start command here */ print_status(s, 5); /*adf added 20/2/12, apparently an extra KodakEsp_F is sent when the adf is used */ if (strcmp(source_list[s->val[OPT_SOURCE].w], ADF_STR) == 0) { /* adf is in use */ if (! s->adf_loaded) return SANE_STATUS_CANCELLED; /* was SANE_STATUS_NO_DOCS; */ if (kodakaio_txrxack(s, KodakEsp_F, reply)!= SANE_STATUS_GOOD) { DBG(1, "%s: Did not get a good reply to KodakEsp_F\n", __func__); return SANE_STATUS_IO_ERROR; } } if (kodakaio_txrxack(s, KodakEsp_E, reply)!= SANE_STATUS_GOOD) { DBG(1, "%s: Did not get a good reply to KodakEsp_E\n", __func__); return SANE_STATUS_IO_ERROR; } DBG(20, "starting the scan, expected total bytes %lu\n",(unsigned long)expect_total); k_send(s, KodakEsp_Go, 8, &status); if (status != SANE_STATUS_GOOD) DBG(1, "%s: KodakEsp_Go command NOT successfully sent\n", __func__); else { DBG(30, "%s: KodakEsp_Go command successfully sent\n", __func__); s->scanning = SANE_TRUE; } return status; } static SANE_Status cmd_cancel_scan (SANE_Handle handle) { KodakAio_Scanner *s = (KodakAio_Scanner *) handle; unsigned char reply[8]; /* adf added 20/2/12 should it be adf? or adf with paper in? */ if (strcmp(source_list[s->val[OPT_SOURCE].w], ADF_STR) == 0) { /* adf */ if (kodakaio_txrxack(s, KodakEsp_F, reply)!= SANE_STATUS_GOOD) { DBG(1, "%s: KodakEsp_F command failed\n", __func__); return SANE_STATUS_IO_ERROR; } if (kodakaio_txrxack(s, KodakEsp_UnLock, reply)!= SANE_STATUS_GOOD) { DBG(1, "%s: KodakEsp_UnLock command failed\n", __func__); return SANE_STATUS_IO_ERROR; } DBG(5, "%s unlocked the scanner with adf F U\n", __func__); } else { /* no adf */ if (kodakaio_txrxack(s, KodakEsp_UnLock, reply)!= SANE_STATUS_GOOD) { DBG(1, "%s: KodakEsp_UnLock command failed\n", __func__); return SANE_STATUS_IO_ERROR; } DBG(5, "%s unlocked the scanner U\n", __func__); } s->scanning = SANE_FALSE; return SANE_STATUS_GOOD; } static SANE_Status cmd_get_scanning_parameters(SANE_Handle handle, SANE_Frame *format, SANE_Int *depth, SANE_Int *data_pixels, SANE_Int *pixels_per_line, SANE_Int *lines) { /* data_pixels is per line. Old mc cmd read this stuff from the scanner. I don't think kodak can do that easily */ KodakAio_Scanner *s = (KodakAio_Scanner *) handle; SANE_Status status = SANE_STATUS_GOOD; NOT_USED (format); NOT_USED (depth); DBG(10, "%s\n", __func__); /* Calculate returned values */ *lines = s->params.lines; *pixels_per_line = s->params.pixels_per_line; *data_pixels = s->params.pixels_per_line; DBG (20, "%s: data_pixels = %u, lines = %u, " "pixels_per_line = %u)\n", __func__, *data_pixels, *lines, *pixels_per_line); return status; } /* Set color curve command, low level, sends commands to the scanner*/ static SANE_Status cmd_set_color_curve(SANE_Handle handle, unsigned char col) { /* sends the color curve data for one color*/ KodakAio_Scanner *s = (KodakAio_Scanner *) handle; SANE_Status status = SANE_STATUS_GOOD; unsigned char tx_col[8]; unsigned char rx[8]; unsigned char tx_curve[256]; int i; /* 7/9/14 was unsigned char and that stopped the loop that made the linear curve from going to 255 */ DBG(32, "%s: start\n", __func__); tx_col[0]=0x1b; tx_col[1]='S'; tx_col[2]='K'; tx_col[3]=col; tx_col[4]=0; tx_col[5]=0; tx_col[6]=0; tx_col[7]=0; /* linear curve now but could send tailor made curves in future */ for(i=0;i<=255;++i) tx_curve[i]=i; /* 7/9/14 was i<255 the missing elements caused speckles */ k_send(s, tx_col, 8, &status); if (status != SANE_STATUS_GOOD) { DBG(1, "%s: tx err, %s\n", __func__, "curve command"); return status; } k_send(s, tx_curve, 256, &status); if (status != SANE_STATUS_GOOD) { DBG(1, "%s: tx err, %s\n", __func__, "curve data"); return status; } if (kodakaio_expect_ack(s, rx) != SANE_STATUS_GOOD) return SANE_STATUS_IO_ERROR; DBG(10, "%s: sent curve OK, \n", __func__); return status; } /* Set scanning parameters command, low level, sends commands to the scanner*/ static SANE_Status cmd_set_scanning_parameters(SANE_Handle handle, int resolution, int tl_x, int tl_y, int width, int height, unsigned char source) /* NB. here int tl_x, int tl_y, int width, int height are in DPI units, not optres units! */ /* sends params to scanner, but should we store them too? */ { KodakAio_Scanner *s = (KodakAio_Scanner *) handle; SANE_Status status = SANE_STATUS_GOOD; unsigned char tx_S[8]; unsigned char tx_dpi[8]; unsigned char tx_topleft[8]; unsigned char tx_widthheight[8]; unsigned char bufread[8]; int i; /*don't know the purpose of F yet. windows USB repeated 4 x why? does it affect the colour balance?*/ DBG(8, "%s\n", __func__); for(i=0;i<4;++i) { kodakaio_txrxack(s, KodakEsp_F, bufread); sleep(1); } /* kodakaio_txrxack(s, KodakEsp_F, bufread); for net, just once */ /* Source? bed /ADF */ tx_S[0]=0x1b; tx_S[1]='S'; tx_S[2]='S'; tx_S[3]=source; tx_S[4]=0; tx_S[5]=0; tx_S[6]=0; tx_S[7]=0; kodakaio_txrxack(s, tx_S, bufread); /* Compression */ kodakaio_txrxack(s, KodakEsp_Comp, bufread); /* DPI resolution */ tx_dpi[0]=0x1b; tx_dpi[1]='S'; tx_dpi[2]='D'; tx_dpi[3]=resolution & 0xff; tx_dpi[4]=(resolution >> 8) & 0xff; tx_dpi[5]=resolution & 0xff; tx_dpi[6]=(resolution >> 8) & 0xff; tx_dpi[7]=0; kodakaio_txrxack(s, tx_dpi, bufread); /* colour curves don't seem to be sent for usb preview but it seems to do no harm to send them */ cmd_set_color_curve(s, 'R'); cmd_set_color_curve(s, 'G'); cmd_set_color_curve(s, 'B'); /* Origin top left s->tl_x and s->tl_y are in optres units this command needs actual DPI units*/ DBG(20, "%s: left (DPI)=%d, top (DPI)=%d\n", __func__, tl_x , tl_y); tx_topleft[0]=0x1b; tx_topleft[1]='S'; tx_topleft[2]='O'; tx_topleft[3]=(tl_x) & 0xff; tx_topleft[4]=((tl_x) >> 8) & 0xff; tx_topleft[5]=(tl_y) & 0xff; tx_topleft[6]=((tl_y) >> 8) & 0xff; tx_topleft[7]=0; kodakaio_txrxack(s, tx_topleft, bufread); /* Z width height note the s->width and s->height are in optres units this command needs actual DPI units*/ tx_widthheight[0]=0x1b; tx_widthheight[1]='S'; tx_widthheight[2]='Z'; tx_widthheight[3]=(width) & 0xff; tx_widthheight[4]=((width) >> 8) & 0xff; tx_widthheight[5]=(height) & 0xff; tx_widthheight[6]=((height) >> 8) & 0xff; tx_widthheight[7]=0; kodakaio_txrxack(s, tx_widthheight, bufread); if (status != SANE_STATUS_GOOD) DBG(1, "%s: Data NOT successfully sent\n", __func__); else DBG(20, "%s: Data successfully sent\n", __func__); return status; } int cmparray (unsigned char *array1, unsigned char *array2, size_t len) { /* compares len bytes of the arrays returns 0 if they match returns the first mismatch position if they don't match */ unsigned int i; for(i=0; iack and do padding if the padding option is selected if no padding option return EOF */ KodakAio_Scanner *s = (KodakAio_Scanner *) handle; SANE_Status status; int oldtimeout = K_Request_Timeout; size_t bytecount; unsigned char *Last8; /* will point to the last 8 chars in buf */ int i, line, lines; if (s->ack && s->val[OPT_PADDING].w) { /* do padding of whole block*/ /* memset(buf, 0x80, *len); need to work out the background colour for this */ lines = *len / s->params.bytes_per_line; for (line=0; line < lines; ++line) { for (i=0; i< s->params.pixels_per_line; ++i) { buf[line * s->params.bytes_per_line + i] = s->background[0]; /*red */ buf[line * s->params.bytes_per_line + s->params.pixels_per_line + i] = s->background[1]; /*green */ buf[line * s->params.bytes_per_line + 2 * s->params.pixels_per_line + i] = s->background[2]; /*blue */ } } s->bytes_unread -= *len; if (s->bytes_unread < 0) s->bytes_unread = 0; return SANE_STATUS_GOOD; } if (s->ack && !s->val[OPT_PADDING].w) { s->bytes_unread = 0; s->eof = SANE_TRUE; return SANE_STATUS_EOF; } /* Temporarily set the poll timeout long instead of short, * because a color scan needs >5 seconds to initialize. Is this needed for kodak? maybe */ K_Request_Timeout = K_Scan_Data_Timeout; sanei_usb_set_timeout (K_Scan_Data_Timeout); bytecount = k_recv(s, buf, *len, &status); K_Request_Timeout = oldtimeout; sanei_usb_set_timeout (oldtimeout); /* We may need to do some special thing with the block request size to cope with usb blocks of any length in order to keep the ack bytes, when using adf, at the end of one block ie not split between blocks. But it seems that the scanner takes care of that, and gives you the ack as a separate 8 byte block */ if (bytecount >= 8) { /* it may be the last block from the scanner so look for Ack response in last 8 bytes */ Last8 = buf + bytecount - 8; /* only compare 4 bytes because we sometimes get escSS02.. or escSS00.. is 4 the right number ? */ if (cmparray(Last8,KodakEsp_Ack,4) == 0) { DBG(min(10,DBG_READ), "%s: found KodakEsp_Ack at %lu bytes of %lu\n", __func__, (unsigned long) bytecount, (unsigned long) *len); s->ack = SANE_TRUE; *len = bytecount - 8; /* discard the Ack response */ s->bytes_unread -= *len; /* return a short block */ } else { /* a not full buffer is returned usb does this */ DBG(min(10,DBG_READ), "%s: buffer not full, got %lu bytes of %lu\n", __func__, (unsigned long) bytecount, (unsigned long) *len); *len = bytecount; s->bytes_unread -= bytecount; } } else { DBG(min(1,DBG_READ), "%s: tiny read, got %lu bytes of %lu\n", __func__, (unsigned long) bytecount, (unsigned long) *len); return SANE_STATUS_IO_ERROR; } lines = *len / s->params.bytes_per_line; if (lines > 1) { /* store average colour as background. That's not the ideal method but it's easy to implement. What's it used for? */ s->background[0] = 0; s->background[1] = 0; s->background[2] = 0; for (line=0; line < lines; ++line) { for (i=0; i< s->params.pixels_per_line; ++i) { s->background[0] += buf[line * s->params.bytes_per_line + i]; /*red */ s->background[1] += buf[line * s->params.bytes_per_line + s->params.pixels_per_line + i]; /*green */ s->background[2] += buf[line * s->params.bytes_per_line + 2 * s->params.pixels_per_line + i]; /*blue */ } } s->background[0] = s->background[0] / (lines * s->params.pixels_per_line); s->background[1] = s->background[1] / (lines * s->params.pixels_per_line); s->background[2] = s->background[2] / (lines * s->params.pixels_per_line); } if (status == SANE_STATUS_GOOD) if (s->bytes_unread <= 0) DBG(min(2,DBG_READ), "%s: Page fully read %d blocks, %ld bytes unread\n", __func__, s->counter, (long) s->bytes_unread); else DBG(min(20,DBG_READ), "%s: Image data successfully read %ld bytes, %ld bytes unread\n", __func__, (long) bytecount, (long) s->bytes_unread); else if (s->ack) /* was (status == SANE_STATUS_EOF) */ DBG(min(2,DBG_READ), "%s: scanner data read ended %d blocks %ld bytes, %ld bytes unread\n", __func__, s->counter, (long) bytecount, (long) s->bytes_unread); else DBG(min(1,DBG_READ), "%s: Image data read stopped with %s after %d blocks %ld bytes, %ld bytes unread\n", __func__, sane_strstatus(status), s->counter, (long) bytecount, (long) s->bytes_unread); return status; } /**************************************************************************** * kodakaio backend high-level operations ****************************************************************************/ static void k_dev_init(Kodak_Device *dev, const char *devname, int conntype) { DBG(5, "%s for %s\n", __func__,devname); dev->name = NULL; dev->model = NULL; dev->connection = conntype; dev->sane.name = devname; dev->sane.model = NULL; dev->sane.type = "flatbed scanner"; dev->sane.vendor = "Kodak"; dev->cap = &kodakaio_cap[CAP_DEFAULT]; } static SANE_Status k_set_model(KodakAio_Scanner * s, const char *model, size_t len) { unsigned char *buf; unsigned char *p; struct Kodak_Device *dev = s->hw; if (len<1) return SANE_STATUS_INVAL; /* to handle missing model */ buf = malloc(len + 1); if (buf == NULL) return SANE_STATUS_NO_MEM; memcpy(buf, model, len); buf[len] = '\0'; p = &buf[len - 1]; while (*p == ' ') { *p = '\0'; p--; } if (dev->model) free(dev->model); dev->model = strndup((const char *) buf, len); dev->sane.model = dev->model; DBG(10, "%s: model is '%s'\n", __func__, dev->model); free(buf); return SANE_STATUS_GOOD; } static void k_set_device (SANE_Handle handle, SANE_Word device) { KodakAio_Scanner *s = (KodakAio_Scanner *) handle; Kodak_Device *dev = s->hw; int n; DBG(10, "%s: 0x%x\n", __func__, device); for (n = 0; n < NELEMS (kodakaio_cap); n++) { if (kodakaio_cap[n].id == device) break; } if (n < NELEMS(kodakaio_cap)) { dev->cap = &kodakaio_cap[n]; } else { dev->cap = &kodakaio_cap[CAP_DEFAULT]; DBG(1, " unknown device 0x%x, using default %s\n", device, dev->cap->model); } k_set_model (s, dev->cap->model, strlen (dev->cap->model)); } static SANE_Status k_discover_capabilities(KodakAio_Scanner *s) { SANE_Status status = SANE_STATUS_GOOD; Kodak_Device *dev = s->hw; SANE_String_Const *source_list_add = source_list; DBG(10, "%s\n", __func__); /* always add flatbed */ *source_list_add++ = FBF_STR; /* TODO: How can I check for existence of an ADF??? */ if (dev->cap->ADF == SANE_TRUE) { *source_list_add++ = ADF_STR; DBG(10, "%s: added adf to list\n", __func__); } /* TODO: Is there any capability that we can extract from the * device by some scanner command? So far, it looks like * the device does not support any reporting. */ dev->x_range = &dev->cap->fbf_x_range; dev->y_range = &dev->cap->fbf_y_range; DBG(10, " x-range: %f %f\n", SANE_UNFIX(dev->x_range->min), SANE_UNFIX(dev->x_range->max)); DBG(10, " y-range: %f %f\n", SANE_UNFIX(dev->y_range->min), SANE_UNFIX(dev->y_range->max)); DBG(5, "End of %s, status:%s\n", __func__, sane_strstatus(status)); *source_list_add = NULL; /* add end marker to source list */ return status; } static SANE_Status k_setup_block_mode (KodakAio_Scanner *s) { /* works for USB and for net changing to make block size = a number of complete lines 28/12/12 */ s->block_len = MAX_BLOCK_SIZE / s->scan_bytes_per_line * s->scan_bytes_per_line; s->bytes_unread = s->data_len; s->counter = 0; s->bytes_read_in_line = 0; if (s->line_buffer) free(s->line_buffer); s->line_buffer = malloc(s->scan_bytes_per_line); if (s->line_buffer == NULL) { DBG(1, "out of memory (line %d)\n", __LINE__); return SANE_STATUS_NO_MEM; } DBG (10, " %s: Setup block mode - scan_bytes_per_line=%d, pixels_per_line=%d, depth=%d, data_len=%d, block_len=%d, blocks=%d, last_len=%d\n", __func__, s->scan_bytes_per_line, s->params.pixels_per_line, s->params.depth, s->data_len, s->block_len, s->blocks, s->last_len); return SANE_STATUS_GOOD; } /* Call the commands to set scanning parameters In the Kodak Aio the parameters are: (x1b,"S","F",0,0,0,0,0) (x1b,"S","S",1,0,0,0,0) #It looks like the 4th param byte of the C command is compression 1=compress, 0=no compression #1st (or 3rd) param could be channels, 2nd could be bits per channel (x1b,"S","C",3,8,3,0,0) #3,8,3,1,0) was what the kodak software used with compression (x1b,"S","D",LowByte(Res),HighByte(Res),LowByte(Res),HighByte(Res),0) #resolution in DPI SendColour(tcpCliSock,"R") SendColour(tcpCliSock,"G") SendColour(tcpCliSock,"B") (x1b,"S","O",LowByte(x0*Res),HighByte(x0*Res),LowByte(y0*Res),HighByte(y0*Res),0) #top left in pixels (x1b,"S","Z",LowByte(x1*Res),HighByte(x1*Res),LowByte(y1*Res),HighByte(y1*Res),0) #bot right in pixels (x1b,"S","E",1,0,0,0,0) */ static SANE_Status k_lock_scanner (KodakAio_Scanner * s) { SANE_Status status; unsigned char reply[8]; status = k_hello(s); if(status != SANE_STATUS_GOOD) { DBG(1, "%s k_hello failed with %s\n", __func__, sane_strstatus(status)); return status; } if (kodakaio_txrxack(s, KodakEsp_Lock, reply)!= SANE_STATUS_GOOD) { DBG(1, "%s Could not lock scanner\n", __func__); return SANE_STATUS_IO_ERROR; } if (s->adf_loaded) DBG(5, "%s scanner locked, with docs in adf\n", __func__); else DBG(5, "%s scanner locked, with no docs in adf\n", __func__); return SANE_STATUS_GOOD; } static SANE_Status k_set_scanning_parameters(KodakAio_Scanner * s) { SANE_Status status; unsigned char rs, source; SANE_Int scan_pixels_per_line = 0; int dpi, optres; dpi = s->val[OPT_RESOLUTION].w; optres = s->hw->cap->optical_res; /* Find the resolution in the res list and assign the index (rs) */ for (rs=0; rs < s->hw->cap->res_list_size; rs++ ) { if ( dpi == s->hw->cap->res_list[rs] ) break; } /* ADF used? */ if (strcmp(source_list[s->val[OPT_SOURCE].w], ADF_STR) == 0) { source = 0x00; } else { source = 0x01; } /* TODO: Any way to set PREVIEW??? */ /* Remaining bytes unused */ status = cmd_set_scanning_parameters(s, dpi, s->left * dpi / optres, s->top * dpi / optres, /* top/left start (dpi units)*/ s->params.pixels_per_line, s->params.lines, /* extent was s->width, s->height*/ source); /* source */ if (status != SANE_STATUS_GOOD) DBG (1, "%s: Command cmd_set_scanning_parameters failed, %s\n", __func__, sane_strstatus(status)); /* Now query the scanner for the current image parameters */ status = cmd_get_scanning_parameters (s, &s->params.format, &s->params.depth, &scan_pixels_per_line, &s->params.pixels_per_line, &s->params.lines); if (status != SANE_STATUS_GOOD) { DBG (1, "%s: Command cmd_get_scanning_parameters failed, %s\n", __func__, sane_strstatus(status)); return status; } /* Calculate how many bytes are really used per line */ s->params.bytes_per_line = ceil (s->params.pixels_per_line * s->params.depth / 8.0); if (s->val[OPT_MODE].w == MODE_COLOR) s->params.bytes_per_line *= 3; /* Calculate how many bytes per line will be returned by the scanner. magicolor needed this because it uses padding so scan bytes per line != image bytes per line. * The values needed for this are returned by get_scanning_parameters */ s->scan_bytes_per_line = 3 * ceil (scan_pixels_per_line); /* we always scan in colour 8 bit */ s->data_len = s->scan_bytes_per_line * floor (s->height * dpi / optres + 0.5); /* NB this is the length for a full scan */ DBG (5, "Check: scan_bytes_per_line = %d s->params.bytes_per_line = %d \n", s->scan_bytes_per_line, s->params.bytes_per_line); /* k_setup_block_mode at the start of each page for adf to work */ status = k_setup_block_mode (s); if (status != SANE_STATUS_GOOD) DBG (1, "%s: Command k_setup_block_mode failed, %s\n", __func__, sane_strstatus(status)); DBG (18, "%s: bytes_read_in_line: %d\n", __func__, s->bytes_read_in_line); return status; } static SANE_Status k_check_adf(KodakAio_Scanner * s) { /* 20/2/12 detect paper in the adf? acknowledge esc S S 00 01.. ?*/ if (! s->adf_loaded) { DBG(5, "%s: NO DOCS\n", __func__); return SANE_STATUS_NO_DOCS; } else { /* TODO: Check for jam in ADF */ DBG(5, "%s: DOCS IN ADF\n", __func__); return SANE_STATUS_GOOD; } } static SANE_Status k_scan_finish(KodakAio_Scanner * s) { SANE_Status status = SANE_STATUS_GOOD; DBG(10, "%s called\n", __func__); /* If we have not yet read all data, cancel the scan */ if (s->buf && !s->eof) status = cmd_cancel_scan (s); if (s->line_buffer) free (s->line_buffer); s->line_buffer = NULL; free(s->buf); s->buf = s->end = s->ptr = NULL; return status; } static void k_copy_image_data(KodakAio_Scanner * s, SANE_Byte * data, SANE_Int max_length, SANE_Int * length) /* copies the read data from s->line_buffer to the position in data pointer to by s->ptr uncompressed data is RRRR...GGGG...BBBB per line */ { SANE_Int bytes_available; SANE_Int threshold; DBG (min(18,DBG_READ), "%s: bytes_read in line: %d\n", __func__, s->bytes_read_in_line); *length = 0; threshold = 255 - (int) (SANE_UNFIX(s->val[OPT_THRESHOLD].w) * 255.0 / 100.0 + 0.5); /* 255 - for the grey scale version */ DBG (20, "%s: threshold: %d\n", __func__, threshold); while ((max_length >= s->params.bytes_per_line) && (s->ptr < s->end)) { SANE_Int bytes_to_copy = s->scan_bytes_per_line - s->bytes_read_in_line; /* First, fill the line buffer for the current line: */ bytes_available = (s->end - s->ptr); /* Don't copy more than we have buffer and available */ if (bytes_to_copy > bytes_available) bytes_to_copy = bytes_available; if (bytes_to_copy > 0) { memcpy (s->line_buffer + s->bytes_read_in_line, s->ptr, bytes_to_copy); s->ptr += bytes_to_copy; s->bytes_read_in_line += bytes_to_copy; } /* We have filled as much as possible of the current line * with data from the scanner. If we have a complete line, * copy it over. line points to the current byte in the input s->line_buffer data points to the output buffer*/ if ((s->bytes_read_in_line >= s->scan_bytes_per_line) && (s->params.bytes_per_line <= max_length)) { SANE_Int i; SANE_Byte *line = s->line_buffer; *length += s->params.bytes_per_line; for (i=0; i< s->params.pixels_per_line; ++i) { /* different behaviour for each mode */ if (s->val[OPT_MODE].w == MODE_COLOR){ /*interlace was subtracting from 255 until 6/9/14 */ *data++ = 255-line[0]; /*red */ *data++ = 255-line[s->params.pixels_per_line]; /*green */ *data++ = 255-line[2 * s->params.pixels_per_line]; /*blue */ } else if (s->val[OPT_MODE].w == MODE_LINEART) { /* gives 1 bit output */ /*output image location*/ int offset = i % 8; unsigned char mask = 0x80 >> offset; /*set if any colour is over the threshold */ if (line[0] < threshold || line[s->params.pixels_per_line] < threshold || line[2 * s->params.pixels_per_line] < threshold) *data &= ~mask; /* white clear the bit in mask */ else *data |= mask; /* black set the bit in mask */ if (offset == 7 || i == s->params.pixels_per_line-1) data++; /* move on a byte if the byte is full or the line is complete */ } else { /* greyscale - Average the 3 colours */ *data++ = (255-line[0] +255-line[s->params.pixels_per_line] +255-line[2 * s->params.pixels_per_line]) / 3; } line++; } /*debug file The same for color or grey because the scan is colour */ if (RawScan != NULL) { for (i=0; i< s->scan_bytes_per_line; ++i) fputc(s->line_buffer[i],RawScan); } max_length -= s->params.bytes_per_line; s->bytes_read_in_line -= s->scan_bytes_per_line; } } } static SANE_Status k_init_parametersta(KodakAio_Scanner * s) { int dpi, optres; /* struct mode_param *mparam; */ DBG(10, "%s\n", __func__); memset(&s->params, 0, sizeof(SANE_Parameters)); dpi = s->val[OPT_RESOLUTION].w; optres = s->hw->cap->optical_res; /* mparam = &mode_params[s->val[OPT_MODE].w];does this get used? */ if (SANE_UNFIX(s->val[OPT_BR_Y].w) == 0 || SANE_UNFIX(s->val[OPT_BR_X].w) == 0) return SANE_STATUS_INVAL; /* TODO: Use OPT_RESOLUTION or fixed 600dpi for left/top/width/height? */ s->left = ((SANE_UNFIX(s->val[OPT_TL_X].w) / MM_PER_INCH) * optres) + 0.5; s->top = ((SANE_UNFIX(s->val[OPT_TL_Y].w) / MM_PER_INCH) * optres) + 0.5; /* width in pixels */ s->width = ((SANE_UNFIX(s->val[OPT_BR_X].w - s->val[OPT_TL_X].w) / MM_PER_INCH) * optres) + 0.5; s->height = ((SANE_UNFIX(s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w) / MM_PER_INCH) * optres) + 0.5; DBG(20, "%s: s->width = %d, s->height = %d optres units\n", __func__, s->width, s->height); s->params.pixels_per_line = s->width * dpi / optres + 0.5; /* ADF used without padding? added 30/12/12 */ if (strcmp(source_list[s->val[OPT_SOURCE].w], ADF_STR) == 0 && !s->val[OPT_PADDING].w) s->params.lines = -1; else s->params.lines = s->height * dpi / optres + 0.5; DBG(20, "%s: resolution = %d, preview = %d\n", __func__, dpi, s->val[OPT_PREVIEW].w); DBG(20, "%s: %p %p tlx %f tly %f brx %f bry %f [mm]\n", __func__, (void *) s, (void *) s->val, SANE_UNFIX(s->val[OPT_TL_X].w), SANE_UNFIX(s->val[OPT_TL_Y].w), SANE_UNFIX(s->val[OPT_BR_X].w), SANE_UNFIX(s->val[OPT_BR_Y].w)); /* * The default color depth is stored in mode_params.depth:‭ */ if (mode_params[s->val[OPT_MODE].w].depth == 1) s->params.depth = 1; else { s->params.depth = s->val[OPT_BIT_DEPTH].w; } DBG(20, "%s: bit depth = s->params.depth = %d\n", __func__,s->params.depth); s->params.last_frame = SANE_TRUE; s->params.bytes_per_line = 3 * ceil (s->params.depth * s->params.pixels_per_line / 8.0); /* kodak only scans in color and conversion to grey or lineart is done in the driver s->params.format = SANE_FRAME_RGB; */ DBG(20, "%s: s->val[OPT_MODE].w = %d (color is %d)\n", __func__,s->val[OPT_MODE].w, MODE_COLOR); if (s->val[OPT_MODE].w == MODE_COLOR) s->params.format = SANE_FRAME_RGB; else if (s->val[OPT_MODE].w == MODE_LINEART) s->params.format = SANE_FRAME_GRAY; else s->params.format = SANE_FRAME_GRAY; DBG(20, "%s: format=%d, bytes_per_line=%d, lines=%d\n", __func__, s->params.format, s->params.bytes_per_line, s->params.lines); return (s->params.lines >= -1) ? SANE_STATUS_GOOD : SANE_STATUS_INVAL; } static SANE_Status k_start_scan(KodakAio_Scanner * s) { SANE_Status status; status = cmd_start_scan (s, s->data_len); if (status != SANE_STATUS_GOOD ) { DBG (1, "%s: starting the scan failed (%s)\n", __func__, sane_strstatus(status)); } return status; } static SANE_Status k_read(struct KodakAio_Scanner *s) { unsigned char rx[8]; /* monitors progress of blocks and calls cmd_read_data to get each block you don't know how many blocks there will be in advance because their size may be determined by the scanner*/ SANE_Status status = SANE_STATUS_GOOD; size_t buf_len = 0; /* have we passed everything we read to sane? */ if (s->ptr == s->end) { if (s->eof) return SANE_STATUS_EOF; s->counter++; if (s->bytes_unread >= s->block_len) buf_len = s->block_len; else buf_len = s->bytes_unread; DBG(min(20,DBG_READ), "%s: block %d, size %lu\n", __func__, s->counter, (unsigned long) buf_len); /* receive image data + error code */ status = cmd_read_data (s, s->buf, &buf_len); if (status != SANE_STATUS_GOOD && status != SANE_STATUS_EOF) { /* was just GOOD 20/2/12 */ DBG (1, "%s: Receiving image data failed (%s)\n", __func__, sane_strstatus(status)); cmd_cancel_scan(s); return status; } DBG(min(14,DBG_READ), "%s: success %lu bytes of block %d, %d remain\n", __func__, (unsigned long) buf_len, s->counter, s->bytes_unread); if (s->bytes_unread > 0) { if (s->canceling) { cmd_cancel_scan(s); return SANE_STATUS_CANCELLED; } if (status == SANE_STATUS_EOF) { /* page ended prematurely. */ } } else { /* s->bytes_unread <=0 This is the end of a page */ s->eof = SANE_TRUE; DBG(min(10,DBG_READ), "%s: set EOF after %d blocks\n=============\n", __func__, s->counter); /* look for the terminating ack if required */ if (!s->ack) { if (kodakaio_expect_ack(s, rx) == SANE_STATUS_GOOD) { s->ack = SANE_TRUE; } else { DBG(min(1,DBG_READ), "%s: Did not get expected ack at end of page\n", __func__); return SANE_STATUS_IO_ERROR; } } } s->end = s->buf + buf_len; s->ptr = s->buf; } else { DBG(min(20,DBG_READ), "%s: data left in buffer\n", __func__); } return status; } /* * SANE API implementation (high-level functions) */ #if WITH_AVAHI static struct KodakaioCap * get_device_from_identification (const char *ident, const char *vid, const char *pid) { int n; SANE_Word pidnum, vidnum; if(sscanf(vid, "%x", (unsigned int *)&vidnum) == EOF) { DBG(5, "could not convert hex vid <%s>\n", vid); return NULL; } if(sscanf(pid, "%x", (unsigned int *)&pidnum) == EOF) { DBG(5, "could not convert hex pid <%s>\n", pid); return NULL; } for (n = 0; n < NELEMS (kodakaio_cap); n++) { if (strcmp (kodakaio_cap[n].model, ident)==0) { DBG(20, "matched <%s> & <%s>\n", kodakaio_cap[n].model, ident); return &kodakaio_cap[n]; } else if (kodakaio_cap[n].id == pidnum && 0x040A == vidnum) { DBG(20, "matched <%s> & <%s:%s>\n", kodakaio_cap[n].model, vid, pid); return &kodakaio_cap[n]; } else { DBG(20, "not found <%s> & <%s>\n", kodakaio_cap[n].model, pid); } } return NULL; } #endif /* WITH_AVAHI */ /* * close_scanner() * * Close the open scanner. Depending on the connection method, a different * close function is called. */ static void close_scanner(KodakAio_Scanner *s) { DBG(7, "%s: fd = %d\n", __func__, s->fd); if (s->fd == -1) return; k_scan_finish(s); if (s->hw->connection == SANE_KODAKAIO_NET) { sanei_kodakaio_net_close(s); sanei_tcp_close(s->fd); } else if (s->hw->connection == SANE_KODAKAIO_USB) { sanei_usb_close(s->fd); } s->fd = -1; } static SANE_Bool split_scanner_name (const char *name, char * IP, unsigned int *model) { const char *device = name; const char *qm; *model = 0; /* cut off leading net: */ if (strncmp(device, "net:", 4) == 0) device = &device[4]; qm = strchr(device, '?'); if (qm != NULL) { size_t len = qm-device; strncpy (IP, device, len); IP[len] = '\0'; qm++; if (strncmp(qm, "model=", 6) == 0) { qm += 6; if (!sscanf(qm, "0x%x", model)) sscanf(qm, "%x", model); } } else { strcpy (IP, device); } return SANE_TRUE; } /* * open_scanner() * * Open the scanner device. Depending on the connection method, * different open functions are called. */ static SANE_Status open_scanner(KodakAio_Scanner *s) { SANE_Status status = 0; DBG(7, "%s: %s\n", __func__, s->hw->sane.name); if (s->fd != -1) { DBG(10, "scanner is already open: fd = %d\n", s->fd); return SANE_STATUS_GOOD; /* no need to open the scanner */ } if (s->hw->connection == SANE_KODAKAIO_NET) { /* device name has the form net:ipaddr?model=... */ char IP[1024]; unsigned int model = 0; if (!split_scanner_name (s->hw->sane.name, IP, &model)) return SANE_STATUS_INVAL; DBG(10, "split_scanner_name OK model=0x%x\n",model); /* normal with IP */ status = sanei_tcp_open(IP, 9101, &s->fd); /* (host,port,file pointer) */ if (status != SANE_STATUS_GOOD ) DBG(1, "Is network scanner switched on?\n"); if (model>0) k_set_device (s, model); if (status == SANE_STATUS_GOOD) { status = sanei_kodakaio_net_open (s); } else DBG(1, "status was not good at net open\n"); } else if (s->hw->connection == SANE_KODAKAIO_USB) { DBG(7, "trying to open usb\n"); status = sanei_usb_open(s->hw->sane.name, &s->fd); if (s->hw->cap->out_ep>0) sanei_usb_set_endpoint (s->fd, USB_DIR_OUT | USB_ENDPOINT_TYPE_BULK, s->hw->cap->out_ep); if (s->hw->cap->in_ep>0) sanei_usb_set_endpoint (s->fd, USB_DIR_IN | USB_ENDPOINT_TYPE_BULK, s->hw->cap->in_ep); } if (status == SANE_STATUS_ACCESS_DENIED) { DBG(1, "please check that you have permissions on the device.\n"); DBG(1, "if this is a multi-function device with a printer,\n"); DBG(1, "disable any conflicting driver (like usblp).\n"); } if (status != SANE_STATUS_GOOD) DBG(1, "%s open failed: %s\n", s->hw->sane.name, sane_strstatus(status)); else DBG(3, "scanner opened\n"); /* add check here of usb properties? */ /*sanei_usb_get_descriptor( SANE_Int dn, struct sanei_usb_dev_descriptor *desc );*/ return status; } static SANE_Status detect_usb(struct KodakAio_Scanner *s) { SANE_Status status; SANE_Word vendor, product; int i, numIds; SANE_Bool is_valid; /* if the sanei_usb_get_vendor_product call is not supported, * then we just ignore this and rely on the user to config * the correct device. */ status = sanei_usb_get_vendor_product(s->fd, &vendor, &product); if (status != SANE_STATUS_GOOD) { DBG(1, "the device cannot be verified - will continue\n"); return SANE_STATUS_GOOD; } /* check the vendor ID to see if we are dealing with a kodak device */ if (vendor != SANE_KODAKAIO_VENDOR_ID) { /* this is not a supported vendor ID */ DBG(1, "not a Kodak Aio device at %s (vendor id=0x%x)\n", s->hw->sane.name, vendor); return SANE_STATUS_INVAL; } numIds = kodakaio_getNumberOfUSBProductIds(); is_valid = SANE_FALSE; i = 0; /* check all known product IDs to verify that we know * about the device */ while (i != numIds && !is_valid) { /* if (product == kodakaio_usb_product_ids[i]) */ if (product == kodakaio_cap[i].id) is_valid = SANE_TRUE; i++; } if (is_valid == SANE_FALSE) { DBG(1, "the device at %s is not a supported (product id=0x%x)\n", s->hw->sane.name, product); return SANE_STATUS_INVAL; } DBG(2, "found valid usb Kodak Aio scanner: 0x%x/0x%x (vendorID/productID)\n", vendor, product); k_set_device(s, product); /* added 21/12/11 to try and get a name for the device */ return SANE_STATUS_GOOD; } /* * used by attach* and sane_get_devices * a ptr to a single-linked list of Kodak_Device structs * a ptr to a null term array of ptrs to SANE_Device structs */ static int num_devices; /* number of scanners attached to backend */ static Kodak_Device *first_dev; /* first scanner in list */ static const SANE_Device **devlist = NULL; static struct KodakAio_Scanner * scanner_create(struct Kodak_Device *dev, SANE_Status *status) { struct KodakAio_Scanner *s; s = malloc(sizeof(struct KodakAio_Scanner)); if (s == NULL) { *status = SANE_STATUS_NO_MEM; return NULL; } memset(s, 0x00, sizeof(struct KodakAio_Scanner)); s->fd = -1; s->hw = dev; return s; } static struct KodakAio_Scanner * device_detect(const char *name, int type, SANE_Status *status) { struct KodakAio_Scanner *s; struct Kodak_Device *dev; /* try to find the device in our list */ for (dev = first_dev; dev; dev = dev->next) { if (strcmp(dev->sane.name, name) == 0) { dev->missing = 0; DBG (10, "%s: Device %s already attached!\n", __func__, name); return scanner_create(dev, status); } } if (type == SANE_KODAKAIO_NODEV) { *status = SANE_STATUS_INVAL; return NULL; } /* alloc and clear our device structure */ dev = malloc(sizeof(*dev)); if (!dev) { *status = SANE_STATUS_NO_MEM; return NULL; } memset(dev, 0x00, sizeof(struct Kodak_Device)); s = scanner_create(dev, status); if (s == NULL) return NULL; k_dev_init(dev, name, type); *status = open_scanner(s); if (*status != SANE_STATUS_GOOD) { free(s); free(dev); return NULL; } /* from now on, close_scanner() must be called */ /* USB requires special care */ if (dev->connection == SANE_KODAKAIO_USB) { *status = detect_usb(s); } if (*status != SANE_STATUS_GOOD) goto close; /* set name and model (if not already set) */ if (dev->model == NULL) k_set_model(s, "generic", 7); dev->name = strdup(name); dev->sane.name = dev->name; /* do we need to discover capabilities here? */ *status = k_discover_capabilities(s); if (*status != SANE_STATUS_GOOD) goto close; if (source_list[0] == NULL || dev->cap->dpi_range.min == 0) { DBG(1, "something is wrong in the discovery process, aborting.\n"); *status = SANE_STATUS_IO_ERROR; goto close; } /* add this scanner to the device list */ num_devices++; dev->missing = 0; dev->next = first_dev; first_dev = dev; return s; close: close_scanner(s); free(dev); free(s); return NULL; } #if WITH_AVAHI /* ProcessAvahiDevice is called to process each discovered device in turn */ void ProcessAvahiDevice(const char *device_id, const char *vid, const char *pid, const char *ip_addr) { struct KodakaioCap *cap; DBG(min(10,DBG_AUTO),"device_id = <%s> vid:pid = <%s:%s>\n", device_id,vid,pid); /* check if it is a model likely to be supported: "KODAK ESP" or "KODAK HERO" DBG(min(10,DBG_AUTO),"look up model <%s>\n", device_model); */ cap = get_device_from_identification("", vid, pid); if (cap == NULL) { return; } DBG(min(10,DBG_AUTO), "%s: Found autodiscovered device: %s (type 0x%x)\n", __func__, cap->model, cap->id); attach_one_net (ip_addr, cap->id); } static void resolve_callback( AvahiServiceResolver *r, AVAHI_GCC_UNUSED AvahiIfIndex interface, AVAHI_GCC_UNUSED AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, AVAHI_GCC_UNUSED void* userdata) { AvahiStringList *vid_pair_list = NULL, *pid_pair_list = NULL; char *pidkey, *pidvalue; char *vidkey, *vidvalue; size_t valuesize; NOT_USED (flags); assert(r); /* Called whenever a service has been resolved successfully or timed out */ switch (event) { case AVAHI_RESOLVER_FAILURE: DBG(min(1,DBG_AUTO), "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r)))); break; case AVAHI_RESOLVER_FOUND: { char a[AVAHI_ADDRESS_STR_MAX]; avahi_address_snprint(a, sizeof(a), address); /* Output short for Kodak ESP */ DBG(min(10,DBG_AUTO), "%s:%u %s\n", a,port,host_name); vid_pair_list = avahi_string_list_find(txt, "vid"); if(vid_pair_list != NULL) { avahi_string_list_get_pair(vid_pair_list, &vidkey, &vidvalue, &valuesize); DBG(min(10,DBG_AUTO), "%s=%s ", vidkey, vidvalue); } else DBG(min(10,DBG_AUTO), "failed to find key vid\n"); pid_pair_list = avahi_string_list_find(txt, "pid"); if(pid_pair_list != NULL) { avahi_string_list_get_pair(pid_pair_list, &pidkey, &pidvalue, &valuesize); DBG(min(10,DBG_AUTO), "%s=%s\n", pidkey, pidvalue); } else DBG(min(10,DBG_AUTO), "failed to find key pid\n"); if(pid_pair_list != NULL && vid_pair_list != NULL) { ProcessAvahiDevice(name, vidvalue, pidvalue, a); } else DBG(min(10,DBG_AUTO), "didn't call ProcessAvahiDevice\n"); if(vid_pair_list != NULL) { avahi_free(vidkey); avahi_free(vidvalue); DBG(min(15,DBG_AUTO), "vidkey and vidvalue freed\n"); } if(pid_pair_list != NULL) { avahi_free(pidkey); avahi_free(pidvalue); DBG(min(15,DBG_AUTO), "pidkey and pidvalue freed\n"); } } } DBG(min(10,DBG_AUTO), "ending resolve_callback\n"); avahi_service_resolver_free(r); } static void browse_callback( AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, void* userdata) { AvahiSimplePoll *simple_poll = userdata; AvahiClient *c = avahi_service_browser_get_client (b); /* Called whenever a new services becomes available on the LAN or is removed from the LAN */ switch (event) { case AVAHI_BROWSER_FAILURE: DBG(min(1,DBG_AUTO), "(Browser) %s\n", avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b)))); avahi_simple_poll_quit(simple_poll); return; case AVAHI_BROWSER_NEW: DBG(min(5,DBG_AUTO), "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain); /* We ignore the returned resolver object. In the callback function we free it. If the server is terminated before the callback function is called the server will free the resolver for us. */ if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, c))) DBG(min(1,DBG_AUTO), "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_client_errno(c))); break; case AVAHI_BROWSER_REMOVE: DBG(min(1,DBG_AUTO), "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain); break; case AVAHI_BROWSER_ALL_FOR_NOW: case AVAHI_BROWSER_CACHE_EXHAUSTED: DBG(min(5,DBG_AUTO), "(Browser) %s\n", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW"); break; } } static void client_callback(AvahiClient *c, AvahiClientState state, void * userdata) { AvahiSimplePoll *simple_poll = userdata; assert(c); /* Called whenever the client or server state changes */ if (state == AVAHI_CLIENT_FAILURE) { DBG(min(1,DBG_AUTO), "Server connection failure: %s\n", avahi_strerror(avahi_client_errno(c))); avahi_simple_poll_quit(simple_poll); } } static int kodak_network_discovery(const char*host) /* If host = NULL do autodiscovery. If host != NULL try to verify the model First version only does autodiscovery */ { AvahiSimplePoll *simple_poll; AvahiClient *client = NULL; AvahiServiceBrowser *sb = NULL; int error; int i, ret = 1; NOT_USED(host); DBG(2, "%s: called\n", __func__); /* Allocate main loop object */ if (!(simple_poll = avahi_simple_poll_new())) { DBG(min(1,DBG_AUTO), "Failed to create simple poll object.\n"); goto fail; } /* Allocate a new client */ client = avahi_client_new(avahi_simple_poll_get(simple_poll), 0, client_callback, simple_poll, &error); /* Check whether creating the client object succeeded */ if (!client) { DBG(min(1,DBG_AUTO), "Failed to create client: %s\n", avahi_strerror(error)); goto fail; } /* Create the service browser */ if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_scanner._tcp", NULL, 0, browse_callback, simple_poll))) { DBG(min(1,DBG_AUTO), "Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(client))); goto fail; } /* Run the main loop */ for(i=1;i 0) { snprintf(name, 1024, "net:%s?model=0x%x", dev, model); } else { snprintf(name, 1024, "net:%s", dev); } return attach(name, SANE_KODAKAIO_NET); } static SANE_Status attach_one_config(SANEI_Config __sane_unused__ *config, const char *line, void *data) { int vendor, product, timeout; SANE_Bool local_only = *(SANE_Bool*) data; int len = strlen(line); DBG(7, "%s: len = %d, line = %s\n", __func__, len, line); if (sscanf(line, "usb %i %i", &vendor, &product) == 2) { /* add the vendor and product IDs to the list of * known devices before we call the attach function */ int numIds = kodakaio_getNumberOfUSBProductIds(); if (vendor != SANE_KODAKAIO_VENDOR_ID) { DBG(7, "Wrong vendor: numIds = %d, vendor = %d\n", numIds, vendor); return SANE_STATUS_INVAL; /* this is not a Kodak device */ } /* kodakaio_usb_product_ids[numIds - 1] = product; */ kodakaio_cap[numIds - 1].id = product; sanei_usb_attach_matching_devices(line, attach_one_usb); } else if (strncmp(line, "usb", 3) == 0 && len == 3) { int i, numIds; /* auto detect ? */ numIds = kodakaio_getNumberOfUSBProductIds(); for (i = 0; i < numIds; i++) { /* sanei_usb_find_devices(SANE_KODAKAIO_VENDOR_ID, kodakaio_usb_product_ids[i], attach_one_usb); */ sanei_usb_find_devices(SANE_KODAKAIO_VENDOR_ID, kodakaio_cap[i].id, attach_one_usb); } } else if (strncmp(line, "net", 3) == 0) { if (!local_only) { /* remove the "net" sub string */ const char *name = sanei_config_skip_whitespace(line + 3); char IP[1024]; unsigned int model = 0; if (strncmp(name, "autodiscovery", 13) == 0) { #if WITH_AVAHI DBG (30, "%s: Initiating network autodiscovery via avahi\n", __func__); kodak_network_discovery(NULL); #else DBG (20, "%s: Network autodiscovery not done because not configured with avahi.\n", __func__); #endif } else if (sscanf(name, "%s %x", IP, &model) == 2) { DBG(30, "%s: Using network device on IP %s, forcing model 0x%x\n", __func__, IP, model); attach_one_net(IP, model); } else { DBG(1, "%s: net entry %s may be a host name?\n", __func__, name); attach_one_net(name, 0); } } } else if (sscanf(line, "snmp-timeout %i\n", &timeout)) { /* Timeout for auto network discovery */ DBG(50, "%s: network auto-discovery timeout set to %d\n", __func__, timeout); K_SNMP_Timeout = timeout; } else if (sscanf(line, "scan-data-timeout %i\n", &timeout)) { /* Timeout for scan data requests */ DBG(50, "%s: Scan data timeout set to %d\n", __func__, timeout); K_Scan_Data_Timeout = timeout; } else if (sscanf(line, "request-timeout %i\n", &timeout)) { /* Timeout for all other read requests */ DBG(50, "%s: Request timeout set to %d\n", __func__, timeout); K_Request_Timeout = timeout; } else { /* TODO: Warning about unparsable line! */ } return SANE_STATUS_GOOD; } static void free_devices(void) { Kodak_Device *dev, *next; DBG(5, "%s\n", __func__); for (dev = first_dev; dev; dev = next) { next = dev->next; free(dev->name); free(dev->model); free(dev); } if (devlist) free(devlist); devlist = NULL; first_dev = NULL; } SANE_Status sane_init(SANE_Int *version_code, SANE_Auth_Callback __sane_unused__ authorize) { DBG_INIT(); DBG(1, "========================================== \n"); DBG(2, "%s: " PACKAGE " " VERSION "\n", __func__); DBG(1, "kodakaio backend, version %i.%i.%i\n", KODAKAIO_VERSION, KODAKAIO_REVISION, KODAKAIO_BUILD); DBG(2, "%s: called\n", __func__); if (version_code != NULL) *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, KODAKAIO_BUILD); sanei_usb_init(); #if WITH_AVAHI DBG(min(3,DBG_AUTO), "avahi detected\n"); #else DBG(min(3,DBG_AUTO), "avahi not detected\n"); #endif return SANE_STATUS_GOOD; } /* Clean up the list of attached scanners. */ void sane_exit(void) { DBG(5, "%s\n", __func__); free_devices(); } SANE_Status sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only) { Kodak_Device *dev, *s, *prev=0; int i; DBG(2, "%s: called\n", __func__); sanei_usb_init(); /* mark all existing scanners as missing, attach_one will remove mark */ for (s = first_dev; s; s = s->next) { s->missing = 1; } /* Read the config, mark each device as found, possibly add new devs */ sanei_configure_attach(KODAKAIO_CONFIG_FILE, NULL, attach_one_config, &local_only); /*delete missing scanners from list*/ for (s = first_dev; s;) { if (s->missing) { DBG (5, "%s: missing scanner %s\n", __func__, s->name); /*splice s out of list by changing pointer in prev to next*/ if (prev) { prev->next = s->next; free (s); s = prev->next; num_devices--; } else { /*remove s from head of list */ first_dev = s->next; free(s); s = first_dev; prev=NULL; num_devices--; } } else { prev = s; s = prev->next; } } DBG (15, "%s: found %d scanner(s)\n", __func__, num_devices); for (s = first_dev; s; s=s->next) { DBG (15, "%s: found scanner %s\n", __func__, s->name); } if (devlist) free (devlist); devlist = malloc((num_devices + 1) * sizeof(devlist[0])); if (!devlist) { DBG(1, "out of memory (line %d)\n", __LINE__); return SANE_STATUS_NO_MEM; } DBG(5, "%s - results:\n", __func__); for (i = 0, dev = first_dev; i < num_devices && dev; dev = dev->next, i++) { DBG(5, " %d (%d): %s\n", i, dev->connection, dev->model); devlist[i] = &dev->sane; } devlist[i] = NULL; if(device_list){ *device_list = devlist; } return SANE_STATUS_GOOD; } static SANE_Status init_options(KodakAio_Scanner *s) { int i; SANE_Word *res_list; DBG(5, "%s: called\n", __func__); for (i = 0; i < NUM_OPTIONS; i++) { s->opt[i].size = sizeof(SANE_Word); s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* "Scan Mode" group: */ s->opt[OPT_MODE_GROUP].name = SANE_NAME_STANDARD; s->opt[OPT_MODE_GROUP].title = SANE_TITLE_STANDARD; s->opt[OPT_MODE_GROUP].desc = SANE_DESC_STANDARD; s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MODE_GROUP].cap = 0; /* scan mode */ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; 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 = max_string_size(mode_list); s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_MODE].constraint.string_list = mode_list; s->val[OPT_MODE].w = MODE_COLOR; /* default */ DBG(20, "%s: mode_list has first entry %s, default mode is %s\n", __func__, mode_list[0],mode_list[s->val[OPT_MODE].w]); /* threshold the sane std says should be SANE_TYPE_FIXED 0..100 but all other backends seem to use INT 0..255 */ 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].size = sizeof(SANE_Word); s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_THRESHOLD].constraint.range = &percent_range_fixed; s->val[OPT_THRESHOLD].w = SANE_FIX(50.0); DBG(20, "%s: threshold initialised to fixed %f\n", __func__, SANE_UNFIX(s->val[OPT_THRESHOLD].w)); /* threshold the sane std says should be SANE_TYPE_FIXED 0..100 but all other backends seem to use INT 0..255 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_INT; s->opt[OPT_THRESHOLD].unit = SANE_UNIT_PERCENT; s->opt[OPT_THRESHOLD].size = sizeof(SANE_Word); s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_THRESHOLD].constraint.range = &percent_range_int; s->val[OPT_THRESHOLD].w = 51; DBG(20, "%s: threshold initialised to int %d\n", __func__, s->val[OPT_THRESHOLD].w); */ /* bit depth */ s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT; s->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_NONE; s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_BIT_DEPTH].constraint.word_list = s->hw->cap->depth_list; s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; s->val[OPT_BIT_DEPTH].w = s->hw->cap->depth_list[1]; /* the first "real" element is the default */ DBG(20, "%s: depth list has depth_list[0] = %d entries\n", __func__, s->hw->cap->depth_list[0]); if (s->hw->cap->depth_list[0] == 1) { /* only one element in the list -> hide the option */ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; DBG(20, "%s: Only one depth in list so inactive option\n", __func__); } /* resolution */ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; res_list = malloc((s->hw->cap->res_list_size + 1) * sizeof(SANE_Word)); if (res_list == NULL) { return SANE_STATUS_NO_MEM; } *(res_list) = s->hw->cap->res_list_size; memcpy(&(res_list[1]), s->hw->cap->res_list, s->hw->cap->res_list_size * sizeof(SANE_Word)); s->opt[OPT_RESOLUTION].constraint.word_list = res_list; s->val[OPT_RESOLUTION].w = s->hw->cap->dpi_range.min; /* trial option for debugging s->opt[OPT_TRIALOPT].name = "trialoption"; s->opt[OPT_TRIALOPT].title = "trialoption"; s->opt[OPT_TRIALOPT].desc = "trialoption"; s->opt[OPT_TRIALOPT].type = SANE_TYPE_INT; s->opt[OPT_TRIALOPT].unit = SANE_UNIT_NONE; s->opt[OPT_TRIALOPT].size = sizeof(SANE_Word); s->opt[OPT_TRIALOPT].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TRIALOPT].constraint.range = &percent_range_int; s->val[OPT_TRIALOPT].w = 1; */ /* preview */ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; s->val[OPT_PREVIEW].w = SANE_FALSE; for(i=0;source_list[i]!=NULL;++i) DBG(18, "source_list: %s\n",source_list[i]); /* source */ s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE; s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE; s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE; s->opt[OPT_SOURCE].type = SANE_TYPE_STRING; s->opt[OPT_SOURCE].size = max_string_size(source_list); s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_SOURCE].constraint.string_list = source_list; s->val[OPT_SOURCE].w = 0; /* always use Flatbed as default */ if ((!s->hw->cap->ADF)) { DBG(9, "device with no adf detected source option inactive\n"); s->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE; } /* Are there any ESP scanners that are duplex? */ s->opt[OPT_ADF_MODE].name = "adf-mode"; s->opt[OPT_ADF_MODE].title = SANE_I18N("ADF Mode"); s->opt[OPT_ADF_MODE].desc = SANE_I18N("Selects the ADF mode (simplex/duplex)"); s->opt[OPT_ADF_MODE].type = SANE_TYPE_STRING; s->opt[OPT_ADF_MODE].size = max_string_size(adf_mode_list); s->opt[OPT_ADF_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_ADF_MODE].constraint.string_list = adf_mode_list; s->val[OPT_ADF_MODE].w = 0; /* simplex */ if ((!s->hw->cap->ADF) || (!s->hw->cap->adf_duplex)) s->opt[OPT_ADF_MODE].cap |= SANE_CAP_INACTIVE; /* "Geometry" group: */ s->opt[OPT_GEOMETRY_GROUP].name = SANE_NAME_GEOMETRY; s->opt[OPT_GEOMETRY_GROUP].title = SANE_TITLE_GEOMETRY; s->opt[OPT_GEOMETRY_GROUP].desc = SANE_DESC_GEOMETRY; s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; /* top-left x */ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; s->opt[OPT_TL_X].unit = SANE_UNIT_MM; s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_X].constraint.range = s->hw->x_range; s->val[OPT_TL_X].w = 0; /* top-left y */ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_Y].constraint.range = s->hw->y_range; s->val[OPT_TL_Y].w = 0; /* bottom-right x */ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; s->opt[OPT_BR_X].unit = SANE_UNIT_MM; s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_X].constraint.range = s->hw->x_range; s->val[OPT_BR_X].w = s->hw->x_range->max; /* bottom-right y */ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_Y].constraint.range = s->hw->y_range; s->val[OPT_BR_Y].w = s->hw->y_range->max; /* padding short pages in adf */ s->opt[OPT_PADDING].name = "adf-padding"; s->opt[OPT_PADDING].title = "pad short adf pages"; s->opt[OPT_PADDING].desc = "Selects whether to make short pages up to full length"; s->opt[OPT_PADDING].type = SANE_TYPE_BOOL; s->val[OPT_PADDING].w = SANE_FALSE; if ((!s->hw->cap->ADF) || (strcmp(source_list[s->val[OPT_SOURCE].w], ADF_STR) != 0)) { DBG(9, "adf not source so padding option off and inactive\n"); s->opt[OPT_PADDING].cap |= SANE_CAP_INACTIVE; } return SANE_STATUS_GOOD; } SANE_Status sane_open(SANE_String_Const name, SANE_Handle *handle) { SANE_Status status; KodakAio_Scanner *s = NULL; int l = strlen(name); DBG(2, "%s: name = %s\n", __func__, name); /* probe if empty device name provided */ if (l == 0) { status = sane_get_devices(NULL,0); if (status != SANE_STATUS_GOOD) { return status; } if (first_dev == NULL) { DBG(1, "no device detected\n"); return SANE_STATUS_INVAL; } s = device_detect(first_dev->sane.name, first_dev->connection, &status); if (s == NULL) { DBG(1, "cannot open a perfectly valid device (%s)," " please report to the authors\n", name); return SANE_STATUS_INVAL; } } else { if (strncmp(name, "net:", 4) == 0) { s = device_detect(name, SANE_KODAKAIO_NET, &status); if (s == NULL) return status; } else if (strncmp(name, "libusb:", 7) == 0) { s = device_detect(name, SANE_KODAKAIO_USB, &status); if (s == NULL) return status; } else { /* as a last resort, check for a match * in the device list. This should handle platforms without libusb. */ if (first_dev == NULL) { status = sane_get_devices(NULL,0); if (status != SANE_STATUS_GOOD) { return status; } } s = device_detect(name, SANE_KODAKAIO_NODEV, &status); if (s == NULL) { DBG(1, "invalid device name: %s\n", name); return SANE_STATUS_INVAL; } } } /* s is always valid here */ DBG(10, "handle obtained\n"); status = k_discover_capabilities(s); /* added 27/12/11 to fix source list problem maybe we should only be rebuilding the source list here? */ if (status != SANE_STATUS_GOOD) return status; init_options(s); *handle = (SANE_Handle) s; /* moving the open scanner section below to sane_start 27/12/14 status = open_scanner(s); if (status != SANE_STATUS_GOOD) { free(s); return status; } */ return status; } void sane_close(SANE_Handle handle) { KodakAio_Scanner *s; /* * XXX Test if there is still data pending from * the scanner. If so, then do a cancel */ s = (KodakAio_Scanner *) handle; DBG(2, "%s: called\n", __func__); /* moving the close scanner section below to sane_cancel 27/12/14 */ if (s->fd != -1) close_scanner(s); /* end of section */ if(RawScan != NULL) fclose(RawScan); RawScan = NULL; free(s); } const SANE_Option_Descriptor * sane_get_option_descriptor(SANE_Handle handle, SANE_Int option) { /* this may be a sane call, but it happens way too often to have DBG level 2 */ KodakAio_Scanner *s = (KodakAio_Scanner *) handle; DBG(30, "%s: called for option %d\n", __func__, option); if (option < 0 || option >= NUM_OPTIONS) return NULL; return s->opt + option; } static const SANE_String_Const * search_string_list(const SANE_String_Const *list, SANE_String value) { while (*list != NULL && strcmp(value, *list) != 0) list++; return ((*list == NULL) ? NULL : list); } /* Activate, deactivate an option. Subroutines so we can add debugging info if we want. The change flag is set to TRUE if we changed an option. If we did not change an option, then the value of the changed flag is not modified. */ static void activateOption(KodakAio_Scanner *s, SANE_Int option, SANE_Bool *change) { if (!SANE_OPTION_IS_ACTIVE(s->opt[option].cap)) { s->opt[option].cap &= ~SANE_CAP_INACTIVE; *change = SANE_TRUE; } } static void deactivateOption(KodakAio_Scanner *s, SANE_Int option, SANE_Bool *change) { if (SANE_OPTION_IS_ACTIVE(s->opt[option].cap)) { s->opt[option].cap |= SANE_CAP_INACTIVE; *change = SANE_TRUE; } } static SANE_Status getvalue(SANE_Handle handle, SANE_Int option, void *value) { KodakAio_Scanner *s = (KodakAio_Scanner *) handle; SANE_Option_Descriptor *sopt = &(s->opt[option]); Option_Value *sval = &(s->val[option]); DBG(17, "%s: option = %d\n", __func__, option); switch (option) { case OPT_NUM_OPTS: case OPT_BIT_DEPTH: /* case OPT_TRIALOPT: */ case OPT_RESOLUTION: case OPT_PREVIEW: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: *((SANE_Word *) value) = sval->w; DBG(20, "%s: got option %d as %d\n", __func__, option, *((SANE_Word *) value)); break; case OPT_THRESHOLD: *((SANE_Word *) value) = sval->w; DBG(20, "%s: got option %d as %f\n", __func__, option, SANE_UNFIX(*((SANE_Word *) value))); /*DBG(20, "%s: got option %d as %d\n", __func__, option, *((SANE_Word *) value));*/ break; case OPT_MODE: case OPT_SOURCE: case OPT_ADF_MODE: strcpy((char *) value, sopt->constraint.string_list[sval->w]); break; case OPT_PADDING: *((SANE_Bool *) value) = sval->w; break; default: DBG(20, "%s: returning inval\n", __func__); return SANE_STATUS_INVAL; } DBG(20, "%s: returning good\n", __func__); return SANE_STATUS_GOOD; } /* * Handles setting the source (flatbed, or auto document feeder (ADF)). * */ static void change_source(KodakAio_Scanner *s, SANE_Int optindex, char *value) { int force_max = SANE_FALSE; SANE_Bool dummy; DBG(5, "%s: optindex = %d, source = '%s'\n", __func__, optindex, value); if (s->val[OPT_SOURCE].w == optindex) return; s->val[OPT_SOURCE].w = optindex; if (s->val[OPT_TL_X].w == s->hw->x_range->min && s->val[OPT_TL_Y].w == s->hw->y_range->min && s->val[OPT_BR_X].w == s->hw->x_range->max && s->val[OPT_BR_Y].w == s->hw->y_range->max) { force_max = SANE_TRUE; } if (strcmp(ADF_STR, value) == 0) { s->hw->x_range = &s->hw->cap->adf_x_range; s->hw->y_range = &s->hw->cap->adf_y_range; if (s->hw->cap->adf_duplex) { activateOption(s, OPT_ADF_MODE, &dummy); } else { deactivateOption(s, OPT_ADF_MODE, &dummy); s->val[OPT_ADF_MODE].w = 0; } activateOption(s, OPT_PADDING, &dummy); DBG(5, "adf activated flag = %d\n",s->hw->cap->adf_duplex); } else { /* ADF not active */ s->hw->x_range = &s->hw->cap->fbf_x_range; s->hw->y_range = &s->hw->cap->fbf_y_range; deactivateOption(s, OPT_ADF_MODE, &dummy); deactivateOption(s, OPT_PADDING, &dummy); } s->opt[OPT_BR_X].constraint.range = s->hw->x_range; s->opt[OPT_BR_Y].constraint.range = s->hw->y_range; if (s->val[OPT_TL_X].w < s->hw->x_range->min || force_max) s->val[OPT_TL_X].w = s->hw->x_range->min; if (s->val[OPT_TL_Y].w < s->hw->y_range->min || force_max) s->val[OPT_TL_Y].w = s->hw->y_range->min; if (s->val[OPT_BR_X].w > s->hw->x_range->max || force_max) s->val[OPT_BR_X].w = s->hw->x_range->max; if (s->val[OPT_BR_Y].w > s->hw->y_range->max || force_max) s->val[OPT_BR_Y].w = s->hw->y_range->max; } static SANE_Status setvalue(SANE_Handle handle, SANE_Int option, void *value, SANE_Int *info) { KodakAio_Scanner *s = (KodakAio_Scanner *) handle; SANE_Option_Descriptor *sopt = &(s->opt[option]); Option_Value *sval = &(s->val[option]); SANE_Status status; const SANE_String_Const *optval = NULL; int optindex = 0; SANE_Bool reload = SANE_FALSE; DBG(17, "%s: option = %d, value = %p, as word: %d\n", __func__, option, value, *(SANE_Word *) value); status = sanei_constrain_value(sopt, value, info); if (status != SANE_STATUS_GOOD) return status; if (info && value && (*info & SANE_INFO_INEXACT) && sopt->type == SANE_TYPE_INT) DBG(17, "%s: constrained val = %d\n", __func__, *(SANE_Word *) value); if (sopt->constraint_type == SANE_CONSTRAINT_STRING_LIST) { optval = search_string_list(sopt->constraint.string_list, (char *) value); if (optval == NULL) return SANE_STATUS_INVAL; optindex = optval - sopt->constraint.string_list; } switch (option) { case OPT_MODE: { sval->w = optindex; /* if binary, then disable the bit depth selection and enable threshold */ if (optindex == MODE_LINEART) { DBG(17, "%s: binary mode setting depth to 1\n", __func__); s->val[OPT_BIT_DEPTH].w = 1; s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; s->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE; } else { if (s->hw->cap->depth_list[0] == 1) { /* only one entry in the list ? */ DBG(17, "%s: non-binary mode but only one depth available\n", __func__); s->val[OPT_BIT_DEPTH].w = s->hw->cap->depth_list[1]; s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; } else { /* there is a list to choose from ? */ DBG(17, "%s: non-binary mode and depth list available\n", __func__); s->opt[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE; s->val[OPT_BIT_DEPTH].w = mode_params[optindex].depth; s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; /* does not work in xsane ? */ } } reload = SANE_TRUE; break; } case OPT_BIT_DEPTH: sval->w = *((SANE_Word *) value); mode_params[s->val[OPT_MODE].w].depth = sval->w; reload = SANE_TRUE; break; case OPT_THRESHOLD: sval->w = *((SANE_Word *) value); DBG(17, "setting threshold to %f\n", SANE_UNFIX(sval->w)); /*DBG(17, "setting threshold to %d\n", sval->w);*/ /*reload = SANE_TRUE; what does this do?*/ break; case OPT_RESOLUTION: sval->w = *((SANE_Word *) value); DBG(17, "setting resolution to %d\n", sval->w); reload = SANE_TRUE; break; case OPT_BR_X: case OPT_BR_Y: if (SANE_UNFIX(*((SANE_Word *) value)) == 0) { DBG(17, "invalid br-x or br-y\n"); return SANE_STATUS_INVAL; } // fall through case OPT_TL_X: case OPT_TL_Y: sval->w = *((SANE_Word *) value); DBG(17, "setting size to %f\n", SANE_UNFIX(sval->w)); if (NULL != info) *info |= SANE_INFO_RELOAD_PARAMS; break; case OPT_SOURCE: change_source(s, optindex, (char *) value); reload = SANE_TRUE; break; case OPT_ADF_MODE: sval->w = optindex; /* Simple lists */ break; case OPT_PADDING: sval->w = *((SANE_Word *) value); break; /* case OPT_TRIALOPT: */ case OPT_PREVIEW: /* needed? */ sval->w = *((SANE_Word *) value); break; default: return SANE_STATUS_INVAL; } if (reload && info != NULL) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; DBG(17, "%s: end\n", __func__); return SANE_STATUS_GOOD; } SANE_Status sane_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int *info) { KodakAio_Scanner *s = (KodakAio_Scanner *) handle; if (option < 0 || option >= NUM_OPTIONS) { DBG(1, "%s: option num = %d out of range (0..%d)\n", __func__, option, NUM_OPTIONS - 1); return SANE_STATUS_INVAL; } DBG(5, "%s: action = %x, option = %d %s\n", __func__, action, option, s->opt[option].name); if (info != NULL) *info = 0; switch (action) { case SANE_ACTION_GET_VALUE: return getvalue(handle, option, value); case SANE_ACTION_SET_VALUE: return setvalue(handle, option, value, info); default: return SANE_STATUS_INVAL; } return SANE_STATUS_INVAL; } SANE_Status sane_get_parameters(SANE_Handle handle, SANE_Parameters *params) { KodakAio_Scanner *s = (KodakAio_Scanner *) handle; DBG(2, "%s: called\n", __func__); if (params == NULL) DBG(1, "%s: params is NULL\n", __func__); /* * If sane_start was already called, then just retrieve the parameters * from the scanner data structure */ if (!s->eof && s->ptr != NULL) { DBG(5, "scan in progress, returning saved params structure\n"); } else { /* otherwise initialize the params structure and gather the data */ k_init_parametersta(s); } if (params != NULL) *params = s->params; print_params(s->params,20); return SANE_STATUS_GOOD; } /* * This function is part of the SANE API and gets called from the front end to * start the scan process. */ SANE_Status sane_start(SANE_Handle handle) { KodakAio_Scanner *s = (KodakAio_Scanner *) handle; SANE_Status status; DBG(2, "%s: called\n", __func__); if(! s->scanning) { /* calc scanning parameters */ status = k_init_parametersta(s); if (status != SANE_STATUS_GOOD) return status; /* set scanning parameters; also query the current image * parameters from the sanner and save * them to s->params Only set scanning params the first time, or after a cancel try change 22/2/12 take lock scanner out of k_set_scanning_parameters */ /* moved open_scanner here 27/12/14 from sane_open */ status = open_scanner(s); if (status != SANE_STATUS_GOOD) { free(s); return status; } /* end of open scanner section */ status = k_lock_scanner(s); if (status != SANE_STATUS_GOOD) { DBG(1, "could not lock scanner\n"); return status; } } status = k_set_scanning_parameters(s); if (status != SANE_STATUS_GOOD) return status; print_params(s->params, 5); /* if we scan from ADF, check if it is loaded */ if (strcmp(source_list[s->val[OPT_SOURCE].w], ADF_STR) == 0) { status = k_check_adf(s); if (status != SANE_STATUS_GOOD) { /* returning SANE_STATUS_NO_DOCS seems not to cause simple-scan to end the adf scan, so we cancel */ status = SANE_STATUS_CANCELLED; DBG(10, "%s: returning %s\n", __func__, sane_strstatus(status)); return status; } } /* prepare buffer here so that a memory allocation failure * will leave the scanner in a sane state. */ s->buf = realloc(s->buf, s->block_len); if (s->buf == NULL) return SANE_STATUS_NO_MEM; s->eof = SANE_FALSE; /* page not finished */ s->ack = SANE_FALSE; /* page from scanner not finished */ s->ptr = s->end = s->buf; s->canceling = SANE_FALSE; if (strlen(RawScanPath) > 0 && s->params.lines > 0) RawScan = fopen(RawScanPath, "wb");/* open the debug file if it has a name */ if(RawScan) fprintf(RawScan, "P5\n%d %d\n%d\n",s->scan_bytes_per_line, s->params.lines, 255); /* start scanning */ DBG(2, "%s: scanning...\n", __func__); status = k_start_scan(s); if (status != SANE_STATUS_GOOD) { DBG(1, "%s: start failed: %s\n", __func__, sane_strstatus(status)); return status; } return status; } /* this moves data from our buffers to SANE */ SANE_Status sane_read(SANE_Handle handle, SANE_Byte *data, SANE_Int max_length, SANE_Int *length) { SANE_Status status; KodakAio_Scanner *s = (KodakAio_Scanner *) handle; if (s->buf == NULL || s->canceling) return SANE_STATUS_CANCELLED; *length = 0; DBG(18, "sane-read, bytes unread %d\n",s->bytes_unread); status = k_read(s); if (status == SANE_STATUS_CANCELLED) { k_scan_finish(s); return status; } k_copy_image_data(s, data, max_length, length); DBG(18, "%d lines read, status: %s\n", *length / s->params.bytes_per_line, sane_strstatus(status)); /* continue reading if appropriate */ if (status == SANE_STATUS_GOOD) return status; /* not sure if we want to finish (unlock scanner) here or in sane_close */ /* k_scan_finish(s); */ return status; } void sane_cancel(SANE_Handle handle) { SANE_Status status; KodakAio_Scanner *s = (KodakAio_Scanner *) handle; DBG(2, "%s: called\n", __func__); status = cmd_cancel_scan(s); if (status != SANE_STATUS_GOOD) DBG(1, "%s: cmd_cancel_scan failed: %s\n", __func__, sane_strstatus(status)); /* moved from close scanner section 27/12/14 */ if (s->fd != -1) close_scanner(s); /* end of section */ } /* * SANE_Status sane_set_io_mode() * * not supported - for asynchronous I/O */ SANE_Status sane_set_io_mode(SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking) { return SANE_STATUS_UNSUPPORTED; } /* * SANE_Status sane_get_select_fd() * * not supported - for asynchronous I/O */ SANE_Status sane_get_select_fd(SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ *fd) { return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/kodakaio.conf.in000066400000000000000000000050451456256263500177340ustar00rootroot00000000000000### kodakaio.conf ### ### here are some examples for how to configure the kodakaio backend ### Timeout settings: SNMP autodetection, Scan data read requests and other ### read requests. All values are given in milliseconds, ### i.e. 1000 is 1 second. # snmp-timeout controls auto-detection timeout in ms (1500=1.5s). snmp-timeout 2000 # scan-data-timeout controls the timeout for scan data # (scans may take several seconds to initialize, so we need to wait longer) scan-data-timeout 10000 # request-timeout controls all other data requests request-timeout 5000 ### Network: Format is "net IP_ADDRESS [USB_ID]" or "net autodiscovery" ### if USB_ID is left out, SNMP is used to detect the device type ### Currently autodiscovery seems to not work ### So always use "net IP_ADDRESS [USB_ID]" as shown below ### You can find the printer's IP address on its control panel ### There is a list of USB_IDs at the end of this file net autodiscovery ### The following is a kodak HERO 9.1 with explicit IP-Address #net 10.0.0.5 0x4067 # kodak ESP5250 is usb 0x040a 0x4041 #net 192.168.1.2 0x4041 # kodak HERO 9.1 is usb 0x040a 0x4067 #net 192.168.1.17 0x4067 ### USB: format is "usb" for automatic (libusb) discovery, based on USB IDs, ### or "usb to force the use of a particular ### device (the backend has some additional checks and will not use ### non-kodak devices, though) usb ### For libusb support for unknown scanners use the following command ### usb ### e.g.: # kodak ESP5250 is usb 0x040a 0x4041 #usb 0x040a 0x4041 # kodak HERO 9.1 is usb 0x040a 0x4067 #usb 0x040a 0x4067 ### List of USB device IDs # 0x4059, /* kodak ESP 2150 */ # 0x4066, /* kodak ESP 2170 */ # 0x4043, /* kodak ESP 3200 */ # 0x4031, /* kodak ESP 3300 */ # 0x4053, /* kodak ESP 4100 */ # 0x4028, /* kodak ESP 5000 */ # 0x4025, /* kodak ESP 5100 */ # 0x4041, /* kodak ESP 5200 */ # 0x4026, /* kodak ESP 5300 */ # 0x4027, /* kodak ESP 5500 */ # 0x4054, /* kodak ESP 6100 */ # 0x4056, /* kodak ESP 7200 */ # 0x4065, /* kodak ESP 9200 */ # 0x4032, /* kodak ESP 5 */ # 0x403E, /* kodak ESP 7 */ # 0x403F, /* kodak ESP 9 */ # 0x4057, /* kodak ESP C110 */ # 0x4058, /* kodak ESP C115 */ # 0x405D, /* kodak ESP C310 */ # 0x405E, /* kodak ESP C315 */ # 0x4060, /* ADVENT WiFi AIO AW10 */ # 0x406D, /* kodak Hero 3.1 */ # 0x4064, /* kodak Hero 5.1 */ # 0x4062, /* kodak Office Hero 6.1 */ # 0x4063, /* kodak Hero 7.1 */ # 0x4067, /* kodak Hero 9.1 */ backends-1.3.0/backend/kodakaio.h000066400000000000000000000114361456256263500166320ustar00rootroot00000000000000/* * kodakaio.c - SANE library for Kodak ESP Aio scanners. * * Copyright (C) 2011-2013 Paul Newall * * Based on the Magicolor sane backend: * Based on the epson2 sane backend: * Based on Kazuhiro Sasayama previous * work on epson.[ch] file from the SANE package. * Please see those files for additional copyrights. * Author: Paul Newall * * * 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, version 2. 29/12/12 added KodakAio_Scanner.ack 2/1/13 added KodakAio_Scanner.background[] */ #ifndef kodakaio_h #define kodakaio_h #undef BACKEND_NAME #define BACKEND_NAME kodakaio #define DEBUG_NOT_STATIC #include #ifdef HAVE_STDDEF_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef NEED_SYS_TYPES_H #include #endif #include #include "../include/sane/sane.h" #include "../include/sane/sanei_debug.h" #include "../include/sane/sanei_backend.h" /* Silence the compiler for unused arguments */ #define NOT_USED(x) ( (void)(x) ) #define KODAKAIO_CONFIG_FILE "kodakaio.conf" #define NUM_OF_HEX_ELEMENTS (16) /* number of hex numbers per line for data dump */ #define DEVICE_NAME_LEN (16) /* length of device name in extended status */ #define CAP_DEFAULT 0 /* Structure holding the device capabilities */ struct KodakaioCap { SANE_Word id; /* USB pid */ const char *cmds; /* may be used for different command sets in future */ const char *model; SANE_Int out_ep, in_ep; /* USB bulk out/in endpoints */ SANE_Int optical_res; /* optical resolution */ SANE_Range dpi_range; /* max/min resolutions */ SANE_Int *res_list; /* list of resolutions */ SANE_Int res_list_size; /* number of entries in this list */ SANE_Int maxDepth; /* max. color depth */ SANE_Word *depth_list; /* list of color depths */ /* SANE_Range brightness; brightness range */ SANE_Range fbf_x_range; /* flattbed x range */ SANE_Range fbf_y_range; /* flattbed y range */ SANE_Bool ADF; /* ADF is installed */ SANE_Bool adf_duplex; /* does the ADF handle duplex scanning */ SANE_Range adf_x_range; /* autom. document feeder x range */ SANE_Range adf_y_range; /* autom. document feeder y range */ }; /* Options:OPT_BRIGHTNESS, used to be after BIT_DEPTH */ enum { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_MODE, OPT_THRESHOLD, OPT_BIT_DEPTH, OPT_RESOLUTION, OPT_TRIALOPT, /* for debuggging */ OPT_PREVIEW, OPT_SOURCE, OPT_ADF_MODE, OPT_PADDING, /* Selects padding of adf pages to the specified length */ OPT_GEOMETRY_GROUP, OPT_TL_X, OPT_TL_Y, OPT_BR_X, OPT_BR_Y, NUM_OPTIONS }; typedef enum { /* hardware connection to the scanner */ SANE_KODAKAIO_NODEV, /* default, no HW specified yet */ SANE_KODAKAIO_USB, /* USB interface */ SANE_KODAKAIO_NET /* network interface */ } Kodakaio_Connection_Type; /* Structure holding the hardware description */ struct Kodak_Device { struct Kodak_Device *next; int missing; char *name; char *model; SANE_Device sane; SANE_Range *x_range; /* x range w/out extension */ SANE_Range *y_range; /* y range w/out extension */ Kodakaio_Connection_Type connection; struct KodakaioCap *cap; }; typedef struct Kodak_Device Kodak_Device; /* Structure holding an instance of a scanner (i.e. scanner has been opened) */ struct KodakAio_Scanner { struct KodakAio_Scanner *next; struct Kodak_Device *hw; int fd; SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; SANE_Parameters params; SANE_Bool ack; /* scanner has finished a page (happens early with adf and padding) */ SANE_Bool eof; /* backend has finished a page (after padding with adf) */ SANE_Byte *buf, *end, *ptr; SANE_Bool canceling; SANE_Bool scanning; /* scan in progress */ SANE_Bool adf_loaded; /* paper in adf */ SANE_Int background[3]; /* stores background RGB components for padding */ SANE_Int left, top; /* in optres units? */ SANE_Int width, height; /* in optres units? */ /* SANE_Int threshold; 0..255 for lineart*/ /* image block data */ SANE_Int data_len; SANE_Int block_len; SANE_Int last_len; /* to be phased out */ SANE_Int blocks; /* to be phased out */ SANE_Int counter; SANE_Int bytes_unread; /* to track when to stop */ /* Used to store how many bytes of the current pixel line we have already * read in previous read attempts. Since each line will be padded * to multiples of 512 bytes, this is needed to know which bytes * to ignore. NOT NEEDED FOR KODAKAIO */ SANE_Int bytes_read_in_line; SANE_Byte *line_buffer; /* How many bytes are scanned per line */ SANE_Int scan_bytes_per_line; }; typedef struct KodakAio_Scanner KodakAio_Scanner; struct mode_param { int flags; int colors; int depth; }; enum { MODE_COLOR, MODE_GRAY, MODE_LINEART }; #endif backends-1.3.0/backend/kvs1025.c000066400000000000000000000244741456256263500161640ustar00rootroot00000000000000/* Copyright (C) 2008, Panasonic Russia Ltd. Copyright (C) 2010-2011, m. allan noah */ /* sane - Scanner Access Now Easy. Panasonic KV-S1020C / KV-S1025C USB scanners. */ #define DEBUG_NOT_STATIC #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_config.h" #include "../include/lassert.h" #include "kvs1025.h" #include "kvs1025_low.h" #include "../include/sane/sanei_debug.h" /* SANE backend operations, see SANE Standard for details https://sane-project.gitlab.io/standard/ */ /* Init the KV-S1025 SANE backend. This function must be called before any other SANE function can be called. */ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize) { SANE_Status status; DBG_INIT (); DBG (DBG_sane_init, "sane_init\n"); DBG (DBG_error, "This is panasonic KV-S1020C / KV-S1025C version %d.%d build %d\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, V_BUILD); if (version_code) { *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, V_BUILD); } /* Initialize USB */ sanei_usb_init (); status = kv_enum_devices (); if (status) return status; DBG (DBG_proc, "sane_init: leave\n"); return SANE_STATUS_GOOD; } /* Terminate the KV-S1025 SANE backend */ void sane_exit (void) { DBG (DBG_proc, "sane_exit: enter\n"); kv_exit (); DBG (DBG_proc, "sane_exit: exit\n"); } /* Get device list */ SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only) { DBG (DBG_proc, "sane_get_devices: enter\n"); kv_get_devices_list (device_list); DBG (DBG_proc, "sane_get_devices: leave\n"); return SANE_STATUS_GOOD; } /* Open device, return the device handle */ SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { return kv_open_by_name (devicename, handle); } /* Close device */ void sane_close (SANE_Handle handle) { DBG (DBG_proc, "sane_close: enter\n"); kv_close ((PKV_DEV) handle); DBG (DBG_proc, "sane_close: leave\n"); } /* Get option descriptor */ const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { return kv_get_option_descriptor ((PKV_DEV) handle, option); } /* Control option */ SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { return kv_control_option ((PKV_DEV) handle, option, action, val, info); } /* Get scan parameters */ SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { PKV_DEV dev = (PKV_DEV) handle; int side = dev->current_side == SIDE_FRONT ? 0 : 1; DBG (DBG_proc, "sane_get_parameters: enter\n"); if (!(dev->scanning)) { /* Setup the parameters for the scan. (guessed value) */ int resolution = dev->val[OPT_RESOLUTION].w; int width, length, depth = kv_get_depth (kv_get_mode (dev));; DBG (DBG_proc, "sane_get_parameters: initial settings\n"); kv_calc_paper_size (dev, &width, &length); DBG (DBG_error, "Resolution = %d\n", resolution); DBG (DBG_error, "Paper width = %d, height = %d\n", width, length); /* Prepare the parameters for the caller. */ dev->params[0].format = kv_get_mode (dev) == SM_COLOR ? SANE_FRAME_RGB : SANE_FRAME_GRAY; dev->params[0].last_frame = SANE_TRUE; dev->params[0].pixels_per_line = ((width * resolution) / 1200) & (~0xf); dev->params[0].depth = depth > 8 ? 8 : depth; dev->params[0].bytes_per_line = (dev->params[0].pixels_per_line / 8) * depth; dev->params[0].lines = (length * resolution) / 1200; memcpy (&dev->params[1], &dev->params[0], sizeof (SANE_Parameters)); } /* Return the current values. */ if (params) *params = (dev->params[side]); DBG (DBG_proc, "sane_get_parameters: exit\n"); return SANE_STATUS_GOOD; } /* Start scanning */ SANE_Status sane_start (SANE_Handle handle) { SANE_Status status; PKV_DEV dev = (PKV_DEV) handle; SANE_Bool dev_ready; KV_CMD_RESPONSE rs; DBG (DBG_proc, "sane_start: enter\n"); if (!dev->scanning) { /* open device */ if (!kv_already_open (dev)) { DBG (DBG_proc, "sane_start: need to open device\n"); status = kv_open (dev); if (status) { return status; } } /* Begin scan */ DBG (DBG_proc, "sane_start: begin scan\n"); /* Get necessary parameters */ sane_get_parameters (dev, NULL); dev->current_page = 0; dev->current_side = SIDE_FRONT; /* The scanner must be ready. */ status = CMD_test_unit_ready (dev, &dev_ready); if (status || !dev_ready) { return SANE_STATUS_DEVICE_BUSY; } if (!strcmp (dev->val[OPT_MANUALFEED].s, "off")) { status = CMD_get_document_existanse (dev); if (status) { DBG (DBG_proc, "sane_start: exit with no more docs\n"); return status; } } /* Set window */ status = CMD_reset_window (dev); if (status) { return status; } status = CMD_set_window (dev, SIDE_FRONT, &rs); if (status) { DBG (DBG_proc, "sane_start: error setting window\n"); return status; } if (rs.status) { DBG (DBG_proc, "sane_start: error setting window\n"); DBG (DBG_proc, "sane_start: sense_key=0x%x, ASC=0x%x, ASCQ=0x%x\n", get_RS_sense_key (rs.sense), get_RS_ASC (rs.sense), get_RS_ASCQ (rs.sense)); return SANE_STATUS_DEVICE_BUSY; } if (IS_DUPLEX (dev)) { status = CMD_set_window (dev, SIDE_BACK, &rs); if (status) { DBG (DBG_proc, "sane_start: error setting window\n"); return status; } if (rs.status) { DBG (DBG_proc, "sane_start: error setting window\n"); DBG (DBG_proc, "sane_start: sense_key=0x%x, " "ASC=0x%x, ASCQ=0x%x\n", get_RS_sense_key (rs.sense), get_RS_ASC (rs.sense), get_RS_ASCQ (rs.sense)); return SANE_STATUS_INVAL; } } /* Scan */ status = CMD_scan (dev); if (status) { return status; } status = AllocateImageBuffer (dev); if (status) { return status; } dev->scanning = 1; } else { /* renew page */ if (IS_DUPLEX (dev)) { if (dev->current_side == SIDE_FRONT) { /* back image data already read, so just return */ dev->current_side = SIDE_BACK; DBG (DBG_proc, "sane_start: duplex back\n"); status = SANE_STATUS_GOOD; goto cleanup; } else { dev->current_side = SIDE_FRONT; dev->current_page++; } } else { dev->current_page++; } } DBG (DBG_proc, "sane_start: NOW SCANNING page\n"); /* Read image data */ status = ReadImageData (dev, dev->current_page); if (status) { dev->scanning = 0; return status; } /* Get picture element size */ { int width, height; status = CMD_read_pic_elements (dev, dev->current_page, SIDE_FRONT, &width, &height); if (status) return status; } if (IS_DUPLEX (dev)) { int width, height; status = CMD_read_pic_elements (dev, dev->current_page, SIDE_BACK, &width, &height); if (status) return status; } /* software based enhancement functions from sanei_magic */ /* these will modify the image, and adjust the params */ /* at this point, we are only looking at the front image */ /* of simplex or duplex data, back side has already exited */ /* so, we do both sides now, if required */ if (dev->val[OPT_SWDESKEW].w){ buffer_deskew(dev,SIDE_FRONT); } if (dev->val[OPT_SWCROP].w){ buffer_crop(dev,SIDE_FRONT); } if (dev->val[OPT_SWDESPECK].w){ buffer_despeck(dev,SIDE_FRONT); } if (dev->val[OPT_SWDEROTATE].w || dev->val[OPT_ROTATE].w){ buffer_rotate(dev,SIDE_FRONT); } if (IS_DUPLEX (dev)){ if (dev->val[OPT_SWDESKEW].w){ buffer_deskew(dev,SIDE_BACK); } if (dev->val[OPT_SWCROP].w){ buffer_crop(dev,SIDE_BACK); } if (dev->val[OPT_SWDESPECK].w){ buffer_despeck(dev,SIDE_BACK); } if (dev->val[OPT_SWDEROTATE].w || dev->val[OPT_ROTATE].w){ buffer_rotate(dev,SIDE_BACK); } } cleanup: /* check if we need to skip this page */ if (dev->val[OPT_SWSKIP].w && buffer_isblank(dev,dev->current_side)){ DBG (DBG_proc, "sane_start: blank page, recurse\n"); return sane_start(handle); } DBG (DBG_proc, "sane_start: exit\n"); return status; } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { PKV_DEV dev = (PKV_DEV) handle; int side = dev->current_side == SIDE_FRONT ? 0 : 1; int size = max_len; if (!dev->scanning) return SANE_STATUS_EOF; if (size > dev->img_size[side]) size = dev->img_size[side]; if (size == 0) { *len = size; return SANE_STATUS_EOF; } if (dev->val[OPT_INVERSE].w && (kv_get_mode (dev) == SM_BINARY || kv_get_mode (dev) == SM_DITHER)) { int i; unsigned char *p = dev->img_pt[side]; for (i = 0; i < size; i++) { buf[i] = ~p[i]; } } else { memcpy (buf, dev->img_pt[side], size); } /*hexdump(DBG_error, "img data", buf, 128); */ dev->img_pt[side] += size; dev->img_size[side] -= size; DBG (DBG_proc, "sane_read: %d bytes to read, " "%d bytes read, EOF=%s %d\n", max_len, size, dev->img_size[side] == 0 ? "True" : "False", side); if (len) { *len = size; } if (dev->img_size[side] == 0) { if (!strcmp (dev->val[OPT_FEEDER_MODE].s, "single")) if ((IS_DUPLEX (dev) && side) || !IS_DUPLEX (dev)) dev->scanning = 0; } return SANE_STATUS_GOOD; } void sane_cancel (SANE_Handle handle) { PKV_DEV dev = (PKV_DEV) handle; DBG (DBG_proc, "sane_cancel: scan canceled.\n"); dev->scanning = 0; kv_close (dev); } SANE_Status sane_set_io_mode (SANE_Handle h, SANE_Bool m) { (void) h; (void) m; return SANE_STATUS_UNSUPPORTED; } SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int * fd) { (void) h; (void) fd; return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/kvs1025.conf.in000066400000000000000000000000471456256263500172620ustar00rootroot00000000000000usb "Panasonic KV-S1025C" /dev/scanner backends-1.3.0/backend/kvs1025.h000066400000000000000000000075621456256263500161700ustar00rootroot00000000000000/* Copyright (C) 2008, Panasonic Russia Ltd. */ /* sane - Scanner Access Now Easy. Panasonic KV-S1020C / KV-S1025C USB scanners. */ #ifndef __KVS1025_H #define __KVS1025_H /* SANE backend name */ #ifdef BACKEND_NAME #undef BACKEND_NAME #endif #define BACKEND_NAME kvs1025 /* Build version */ #define V_BUILD 5 /* Paper range supported -- MAX scanner limits */ #define KV_MAX_X_RANGE 216 #define KV_MAX_Y_RANGE 2540 /* Round ULX, ULY, Width and Height to 16 Pixels */ #define KV_PIXEL_ROUND 19200 /* (XR * W / 1200) % 16 == 0 i.e. (XR * W) % 19200 == 0 */ /* MAX IULs per LINE */ #define KV_PIXEL_MAX 14064 /* Max 14064 pixels per line, 1/1200 inch each */ #define MM_PER_INCH 25.4 #define mmToIlu(mm) (((mm) * 1200) / MM_PER_INCH) #define iluToMm(ilu) (((ilu) * MM_PER_INCH) / 1200) /* Vendor defined options */ #define SANE_NAME_DUPLEX "duplex" #define SANE_NAME_PAPER_SIZE "paper-size" #define SANE_NAME_AUTOSEP "autoseparation" #define SANE_NAME_LANDSCAPE "landscape" #define SANE_NAME_INVERSE "inverse" #define SANE_NAME_MIRROR "mirror" #define SANE_NAME_LONGPAPER "longpaper" #define SANE_NAME_LENGTHCTL "length-control" #define SANE_NAME_MANUALFEED "manual-feed" #define SANE_NAME_FEED_TIMEOUT "feed-timeout" #define SANE_NAME_DBLFEED "double-feed" #define SANE_TITLE_DUPLEX SANE_I18N("Duplex") #define SANE_TITLE_PAPER_SIZE SANE_I18N("Paper size") #define SANE_TITLE_AUTOSEP SANE_I18N("Automatic separation") #define SANE_TITLE_LANDSCAPE SANE_I18N("Landscape") #define SANE_TITLE_INVERSE SANE_I18N("Inverse Image") #define SANE_TITLE_MIRROR SANE_I18N("Mirror image") #define SANE_TITLE_LONGPAPER SANE_I18N("Long paper mode") #define SANE_TITLE_LENGTHCTL SANE_I18N("Length control mode") #define SANE_TITLE_MANUALFEED SANE_I18N("Manual feed mode") #define SANE_TITLE_FEED_TIMEOUT SANE_I18N("Manual feed timeout") #define SANE_TITLE_DBLFEED SANE_I18N("Double feed detection") #define SANE_DESC_DUPLEX \ SANE_I18N("Enable Duplex (Dual-Sided) Scanning") #define SANE_DESC_PAPER_SIZE \ SANE_I18N("Physical size of the paper in the ADF"); #define SANE_DESC_AUTOSEP \ SANE_I18N("Automatic separation") #define SIDE_FRONT 0x00 #define SIDE_BACK 0x80 /* Debug levels. * Should be common to all backends. */ #define DBG_error0 0 #define DBG_error 1 #define DBG_sense 2 #define DBG_warning 3 #define DBG_inquiry 4 #define DBG_info 5 #define DBG_info2 6 #define DBG_proc 7 #define DBG_read 8 #define DBG_sane_init 10 #define DBG_sane_proc 11 #define DBG_sane_info 12 #define DBG_sane_option 13 #define DBG_shortread 101 /* Prototypes of SANE backend functions, see kvs1025.c */ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback /* __sane_unused__ authorize */ ); void sane_exit (void); SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool /*__sane_unused__ local_only*/ ); SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle); void sane_close (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, SANE_Action action, void *val, SANE_Int * info); SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params); SANE_Status sane_start (SANE_Handle handle); SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len); void sane_cancel (SANE_Handle handle); SANE_Status sane_set_io_mode (SANE_Handle h, SANE_Bool m); SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int * fd); SANE_String_Const sane_strstatus (SANE_Status status); #endif /* #ifndef __KVS1025_H */ backends-1.3.0/backend/kvs1025_cmds.h000066400000000000000000000040701456256263500171650ustar00rootroot00000000000000/* Copyright (C) 2008, Panasonic Russia Ltd. */ /* sane - Scanner Access Now Easy. Panasonic KV-S1020C / KV-S1025C USB scanners. */ #ifndef KVS1025_CMDS_H #define KVS1025_CMDS_H /* Commands supported by the KV-S1020C / KV-S1025C scanner. */ #define SCSI_TEST_UNIT_READY 0x00 #define SCSI_INQUIRY 0x12 #define SCSI_SET_WINDOW 0x24 #define SCSI_SCAN 0x1B #define SCSI_SEND_10 0x2A #define SCSI_READ_10 0x28 #define SCSI_REQUEST_SENSE 0x03 #define SCSI_GET_BUFFER_STATUS 0x34 #define SCSI_SET_TIMEOUT 0xE1 typedef enum { KV_CMD_NONE = 0, KV_CMD_IN = 0x81, /* scanner to pc */ KV_CMD_OUT = 0x02 /* pc to scanner */ } KV_CMD_DIRECTION; /* equals to endpoint address */ typedef struct { KV_CMD_DIRECTION direction; unsigned char cdb[12]; int cdb_size; int data_size; void *data; } KV_CMD_HEADER, *PKV_CMD_HEADER; #define KV_CMD_TIMEOUT 10000 static inline int getbitfield (unsigned char *pageaddr, int mask, int shift) { return ((*pageaddr >> shift) & mask); } /* defines for request sense return block */ #define get_RS_information_valid(b) getbitfield(b + 0x00, 1, 7) #define get_RS_error_code(b) getbitfield(b + 0x00, 0x7f, 0) #define get_RS_filemark(b) getbitfield(b + 0x02, 1, 7) #define get_RS_EOM(b) getbitfield(b + 0x02, 1, 6) #define get_RS_ILI(b) getbitfield(b + 0x02, 1, 5) #define get_RS_sense_key(b) getbitfield(b + 0x02, 0x0f, 0) #define get_RS_information(b) getnbyte(b+0x03, 4) #define get_RS_additional_length(b) b[0x07] #define get_RS_ASC(b) b[0x0c] #define get_RS_ASCQ(b) b[0x0d] #define get_RS_SKSV(b) getbitfield(b+0x0f,1,7) typedef enum { KV_SUCCESS = 0, KV_FAILED = 1, KV_CHK_CONDITION = 2 } KV_STATUS; typedef struct { KV_STATUS status; unsigned char reserved[16]; unsigned char sense[18]; } KV_CMD_RESPONSE, *PKV_CMD_RESPONSE; #endif /*#ifndef KVS1025_CMDS_H */ backends-1.3.0/backend/kvs1025_low.c000066400000000000000000000653651456256263500170510ustar00rootroot00000000000000/* Copyright (C) 2008, Panasonic Russia Ltd. */ /* sane - Scanner Access Now Easy. Panasonic KV-S1020C / KV-S1025C USB scanners. */ #define DEBUG_DECLARE_ONLY #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_config.h" #include "../include/lassert.h" #include "../include/sane/sanei_magic.h" #include "kvs1025.h" #include "kvs1025_low.h" #include "kvs1025_usb.h" #include "../include/sane/sanei_debug.h" /* Global storage */ PKV_DEV g_devices = NULL; /* Chain of devices */ const SANE_Device **g_devlist = NULL; /* Static functions */ /* Free one device */ static void kv_free (KV_DEV ** pdev) { KV_DEV *dev; dev = *pdev; if (dev == NULL) return; DBG (DBG_proc, "kv_free : enter\n"); kv_close (dev); DBG (DBG_proc, "kv_free : free image buffer 0 \n"); if (dev->img_buffers[0]) free (dev->img_buffers[0]); DBG (DBG_proc, "kv_free : free image buffer 1 \n"); if (dev->img_buffers[1]) free (dev->img_buffers[1]); DBG (DBG_proc, "kv_free : free scsi device name\n"); if (dev->scsi_device_name) free (dev->scsi_device_name); DBG (DBG_proc, "kv_free : free SCSI buffer\n"); if (dev->buffer0) free (dev->buffer0); DBG (DBG_proc, "kv_free : free dev \n"); free (dev); *pdev = NULL; DBG (DBG_proc, "kv_free : exit\n"); } /* Free all devices */ static void kv_free_devices (void) { PKV_DEV dev; while (g_devices) { dev = g_devices; g_devices = dev->next; kv_free (&dev); } if (g_devlist) { free (g_devlist); g_devlist = NULL; } } /* Get all supported scanners, and store into g_scanners_supported */ SANE_Status kv_enum_devices (void) { SANE_Status status; kv_free_devices (); status = kv_usb_enum_devices (); if (status) { kv_free_devices (); } return status; } /* Return devices list to the front end */ void kv_get_devices_list (const SANE_Device *** devices_list) { *devices_list = g_devlist; } /* Close all open handles and clean up global storage */ void kv_exit (void) { kv_free_devices (); /* Free all devices */ kv_usb_cleanup (); /* Clean USB bus */ } /* Open device by name */ SANE_Status kv_open_by_name (SANE_String_Const devicename, SANE_Handle * handle) { PKV_DEV pd = g_devices; DBG (DBG_proc, "sane_open: enter (dev_name=%s)\n", devicename); while (pd) { if (strcmp (pd->sane.name, devicename) == 0) { if (kv_open (pd) == 0) { *handle = (SANE_Handle) pd; DBG (DBG_proc, "sane_open: leave\n"); return SANE_STATUS_GOOD; } } pd = pd->next; } DBG (DBG_proc, "sane_open: leave -- no device found\n"); return SANE_STATUS_UNSUPPORTED; } /* Open a device */ SANE_Status kv_open (PKV_DEV dev) { SANE_Status status = SANE_STATUS_UNSUPPORTED; int i; #define RETRAY_NUM 3 if (dev->bus_mode == KV_USB_BUS) { status = kv_usb_open (dev); } if (status) return status; for (i = 0; i < RETRAY_NUM; i++) { SANE_Bool dev_ready; status = CMD_test_unit_ready (dev, &dev_ready); if (!status && dev_ready) break; } if (status == 0) { /* Read device support info */ status = CMD_read_support_info (dev); if (status == 0) { /* Init options */ kv_init_options (dev); status = CMD_set_timeout (dev, dev->val[OPT_FEED_TIMEOUT].w); } } dev->scanning = 0; return status; } /* Check if device is already open */ SANE_Bool kv_already_open (PKV_DEV dev) { SANE_Bool status = 0; if (dev->bus_mode == KV_USB_BUS) { status = kv_usb_already_open (dev); } return status; } /* Close a device */ void kv_close (PKV_DEV dev) { if (dev->bus_mode == KV_USB_BUS) { kv_usb_close (dev); } dev->scanning = 0; } /* Send command to a device */ SANE_Status kv_send_command (PKV_DEV dev, PKV_CMD_HEADER header, PKV_CMD_RESPONSE response) { SANE_Status status = SANE_STATUS_UNSUPPORTED; if (dev->bus_mode == KV_USB_BUS) { if (!kv_usb_already_open(dev)) { DBG (DBG_error, "kv_send_command error: device not open.\n"); return SANE_STATUS_IO_ERROR; } status = kv_usb_send_command (dev, header, response); } return status; } /* Commands */ SANE_Status CMD_test_unit_ready (PKV_DEV dev, SANE_Bool * ready) { SANE_Status status; KV_CMD_HEADER hdr; KV_CMD_RESPONSE rs; DBG (DBG_proc, "CMD_test_unit_ready\n"); memset (&hdr, 0, sizeof (hdr)); hdr.direction = KV_CMD_NONE; hdr.cdb[0] = SCSI_TEST_UNIT_READY; hdr.cdb_size = 6; status = kv_send_command (dev, &hdr, &rs); if (status == 0) { *ready = (rs.status == KV_SUCCESS ? 1 : 0); } return status; } SANE_Status CMD_set_timeout (PKV_DEV dev, SANE_Word timeout) { SANE_Status status; KV_CMD_HEADER hdr; KV_CMD_RESPONSE rs; DBG (DBG_proc, "CMD_set_timeout\n"); memset (&hdr, 0, sizeof (hdr)); hdr.direction = KV_CMD_OUT; hdr.cdb[0] = SCSI_SET_TIMEOUT; hdr.cdb[2] = 0x8D; hdr.cdb[8] = 0x2; hdr.cdb_size = 10; hdr.data = dev->buffer; dev->buffer[0] = 0; dev->buffer[1] = (SANE_Byte) timeout; hdr.data_size = 2; status = kv_send_command (dev, &hdr, &rs); return status; } SANE_Status CMD_read_support_info (PKV_DEV dev) { SANE_Status status; KV_CMD_HEADER hdr; KV_CMD_RESPONSE rs; DBG (DBG_proc, "CMD_read_support_info\n"); memset (&hdr, 0, sizeof (hdr)); hdr.direction = KV_CMD_IN; hdr.cdb_size = 10; hdr.cdb[0] = SCSI_READ_10; hdr.cdb[2] = 0x93; Ito24 (32, &hdr.cdb[6]); hdr.data = dev->buffer; hdr.data_size = 32; status = kv_send_command (dev, &hdr, &rs); DBG (DBG_error, "test.\n"); if (status == 0) { if (rs.status == 0) { int min_x_res, min_y_res, max_x_res, max_y_res; int step_x_res, step_y_res; dev->support_info.memory_size = (dev->buffer[2] << 8 | dev->buffer[3]); min_x_res = (dev->buffer[4] << 8) | dev->buffer[5]; min_y_res = (dev->buffer[6] << 8) | dev->buffer[7]; max_x_res = (dev->buffer[8] << 8) | dev->buffer[9]; max_y_res = (dev->buffer[10] << 8) | dev->buffer[11]; step_x_res = (dev->buffer[12] << 8) | dev->buffer[13]; step_y_res = (dev->buffer[14] << 8) | dev->buffer[15]; dev->support_info.min_resolution = min_x_res > min_y_res ? min_x_res : min_y_res; dev->support_info.max_resolution = max_x_res < max_y_res ? max_x_res : max_y_res; dev->support_info.step_resolution = step_x_res > step_y_res ? step_x_res : step_y_res; dev->support_info.support_duplex = ((dev->buffer[0] & 0x08) == 0) ? 1 : 0; dev->support_info.support_lamp = ((dev->buffer[23] & 0x80) != 0) ? 1 : 0; dev->support_info.max_x_range = KV_MAX_X_RANGE; dev->support_info.max_y_range = KV_MAX_Y_RANGE; dev->x_range.min = dev->y_range.min = 0; dev->x_range.max = SANE_FIX (dev->support_info.max_x_range); dev->y_range.max = SANE_FIX (dev->support_info.max_y_range); dev->x_range.quant = dev->y_range.quant = 0; DBG (DBG_error, "support_info.memory_size = %d (MB)\n", dev->support_info.memory_size); DBG (DBG_error, "support_info.min_resolution = %d (DPI)\n", dev->support_info.min_resolution); DBG (DBG_error, "support_info.max_resolution = %d (DPI)\n", dev->support_info.max_resolution); DBG (DBG_error, "support_info.step_resolution = %d (DPI)\n", dev->support_info.step_resolution); DBG (DBG_error, "support_info.support_duplex = %s\n", dev->support_info.support_duplex ? "TRUE" : "FALSE"); DBG (DBG_error, "support_info.support_lamp = %s\n", dev->support_info.support_lamp ? "TRUE" : "FALSE"); } else { DBG (DBG_error, "Error in CMD_get_support_info, " "sense_key=%d, ASC=%d, ASCQ=%d\n", get_RS_sense_key (rs.sense), get_RS_ASC (rs.sense), get_RS_ASCQ (rs.sense)); } } return status; } SANE_Status CMD_scan (PKV_DEV dev) { SANE_Status status; KV_CMD_HEADER hdr; KV_CMD_RESPONSE rs; DBG (DBG_proc, "CMD_scan\n"); memset (&hdr, 0, sizeof (hdr)); hdr.direction = KV_CMD_NONE; hdr.cdb[0] = SCSI_SCAN; hdr.cdb_size = 6; status = kv_send_command (dev, &hdr, &rs); if (status == 0 && rs.status != 0) { DBG (DBG_error, "Error in CMD_scan, sense_key=%d, ASC=%d, ASCQ=%d\n", get_RS_sense_key (rs.sense), get_RS_ASC (rs.sense), get_RS_ASCQ (rs.sense)); } return status; } SANE_Status CMD_set_window (PKV_DEV dev, int side, PKV_CMD_RESPONSE rs) { unsigned char *window; unsigned char *windowdata; int size = 74; KV_SCAN_MODE scan_mode; KV_CMD_HEADER hdr; DBG (DBG_proc, "CMD_set_window\n"); window = (unsigned char *) dev->buffer; windowdata = window + 8; memset (&hdr, 0, sizeof (hdr)); memset (window, 0, size); Ito16 (66, &window[6]); /* Window descriptor block length */ /* Set window data */ scan_mode = kv_get_mode (dev); kv_set_window_data (dev, scan_mode, side, windowdata); hdr.direction = KV_CMD_OUT; hdr.cdb_size = 10; hdr.cdb[0] = SCSI_SET_WINDOW; Ito24 (size, &hdr.cdb[6]); hdr.data = window; hdr.data_size = size; hexdump (DBG_error, "window", window, size); return kv_send_command (dev, &hdr, rs); } SANE_Status CMD_reset_window (PKV_DEV dev) { KV_CMD_HEADER hdr; KV_CMD_RESPONSE rs; SANE_Status status; DBG (DBG_proc, "CMD_reset_window\n"); memset (&hdr, 0, sizeof (hdr)); hdr.direction = KV_CMD_NONE; hdr.cdb_size = 10; hdr.cdb[0] = SCSI_SET_WINDOW; status = kv_send_command (dev, &hdr, &rs); if (rs.status != 0) status = SANE_STATUS_INVAL; return status; } SANE_Status CMD_get_buff_status (PKV_DEV dev, int *front_size, int *back_size) { KV_CMD_HEADER hdr; KV_CMD_RESPONSE rs; SANE_Status status; unsigned char *data = (unsigned char *) dev->buffer; int size = 12; memset (&hdr, 0, sizeof (hdr)); memset (data, 0, size); hdr.direction = KV_CMD_IN; hdr.cdb_size = 10; hdr.cdb[0] = SCSI_GET_BUFFER_STATUS; hdr.cdb[8] = size; hdr.data = data; hdr.data_size = size; status = kv_send_command (dev, &hdr, &rs); if (status == 0) { if (rs.status == KV_CHK_CONDITION) return SANE_STATUS_NO_DOCS; else { unsigned char *p = data + 4; if (p[0] == SIDE_FRONT) { *front_size = (p[5] << 16) | (p[6] << 8) | p[7]; } else { *back_size = (p[5] << 16) | (p[6] << 8) | p[7]; } return SANE_STATUS_GOOD; } } return status; } SANE_Status CMD_wait_buff_status (PKV_DEV dev, int *front_size, int *back_size) { SANE_Status status = SANE_STATUS_GOOD; int cnt = 0; *front_size = 0; *back_size = 0; DBG (DBG_proc, "CMD_wait_buff_status: enter feed %s\n", dev->val[OPT_MANUALFEED].s); do { DBG (DBG_proc, "CMD_wait_buff_status: tray #%d of %d\n", cnt, dev->val[OPT_FEED_TIMEOUT].w); status = CMD_get_buff_status (dev, front_size, back_size); sleep (1); } while (status == SANE_STATUS_GOOD && (*front_size == 0) && (*back_size == 0) && cnt++ < dev->val[OPT_FEED_TIMEOUT].w); if (cnt > dev->val[OPT_FEED_TIMEOUT].w) status = SANE_STATUS_NO_DOCS; if (status == 0) DBG (DBG_proc, "CMD_wait_buff_status: exit " "front_size %d, back_size %d\n", *front_size, *back_size); else DBG (DBG_proc, "CMD_wait_buff_status: exit with no docs\n"); return status; } SANE_Status CMD_read_pic_elements (PKV_DEV dev, int page, int side, int *width, int *height) { SANE_Status status; KV_CMD_HEADER hdr; KV_CMD_RESPONSE rs; DBG (DBG_proc, "CMD_read_pic_elements\n"); memset (&hdr, 0, sizeof (hdr)); hdr.direction = KV_CMD_IN; hdr.cdb_size = 10; hdr.cdb[0] = SCSI_READ_10; hdr.cdb[2] = 0x80; hdr.cdb[4] = page; hdr.cdb[5] = side; Ito24 (16, &hdr.cdb[6]); hdr.data = dev->buffer; hdr.data_size = 16; status = kv_send_command (dev, &hdr, &rs); if (status == 0) { if (rs.status == 0) { int s = side == SIDE_FRONT ? 0 : 1; int depth = kv_get_depth (kv_get_mode (dev)); *width = B32TOI (dev->buffer); *height = B32TOI (&dev->buffer[4]); assert ((*width) % 8 == 0); DBG (DBG_proc, "CMD_read_pic_elements: " "Page %d, Side %s, W=%d, H=%d\n", page, side == SIDE_FRONT ? "F" : "B", *width, *height); dev->params[s].format = kv_get_mode (dev) == SM_COLOR ? SANE_FRAME_RGB : SANE_FRAME_GRAY; dev->params[s].last_frame = SANE_TRUE; dev->params[s].depth = depth > 8 ? 8 : depth; dev->params[s].lines = *height ? *height : dev->val[OPT_LANDSCAPE].w ? (*width * 3) / 4 : (*width * 4) / 3; dev->params[s].pixels_per_line = *width; dev->params[s].bytes_per_line = (dev->params[s].pixels_per_line / 8) * depth; } else { DBG (DBG_proc, "CMD_read_pic_elements: failed\n"); status = SANE_STATUS_INVAL; } } return status; } SANE_Status CMD_read_image (PKV_DEV dev, int page, int side, unsigned char *buffer, int *psize, KV_CMD_RESPONSE * rs) { SANE_Status status; KV_CMD_HEADER hdr; int size = *psize; DBG (DBG_proc, "CMD_read_image\n"); memset (&hdr, 0, sizeof (hdr)); hdr.direction = KV_CMD_IN; hdr.cdb_size = 10; hdr.cdb[0] = SCSI_READ_10; hdr.cdb[4] = page; hdr.cdb[5] = side; Ito24 (size, &hdr.cdb[6]); hdr.data = buffer; hdr.data_size = size; *psize = 0; status = kv_send_command (dev, &hdr, rs); if (status) return status; *psize = size; if (rs->status == KV_CHK_CONDITION && get_RS_ILI (rs->sense)) { int delta = B32TOI (&rs->sense[3]); DBG (DBG_error, "size=%d, delta=0x%x (%d)\n", size, delta, delta); *psize = size - delta; } DBG (DBG_error, "CMD_read_image: bytes requested=%d, read=%d\n", size, *psize); DBG (DBG_error, "CMD_read_image: ILI=%d, EOM=%d\n", get_RS_ILI (rs->sense), get_RS_EOM (rs->sense)); return status; } SANE_Status CMD_get_document_existanse (PKV_DEV dev) { SANE_Status status; KV_CMD_HEADER hdr; KV_CMD_RESPONSE rs; DBG (DBG_proc, "CMD_get_document_existanse\n"); memset (&hdr, 0, sizeof (hdr)); hdr.direction = KV_CMD_IN; hdr.cdb_size = 10; hdr.cdb[0] = SCSI_READ_10; hdr.cdb[2] = 0x81; Ito24 (6, &hdr.cdb[6]); hdr.data = dev->buffer; hdr.data_size = 6; status = kv_send_command (dev, &hdr, &rs); if (status) return status; if (rs.status) return SANE_STATUS_NO_DOCS; if ((dev->buffer[0] & 0x20) != 0) { return SANE_STATUS_GOOD; } return SANE_STATUS_NO_DOCS; } SANE_Status CMD_wait_document_existanse (PKV_DEV dev) { SANE_Status status; KV_CMD_HEADER hdr; KV_CMD_RESPONSE rs; int cnt; DBG (DBG_proc, "CMD_wait_document_existanse\n"); memset (&hdr, 0, sizeof (hdr)); hdr.direction = KV_CMD_IN; hdr.cdb_size = 10; hdr.cdb[0] = SCSI_READ_10; hdr.cdb[2] = 0x81; Ito24 (6, &hdr.cdb[6]); hdr.data = dev->buffer; hdr.data_size = 6; for (cnt = 0; cnt < dev->val[OPT_FEED_TIMEOUT].w; cnt++) { DBG (DBG_proc, "CMD_wait_document_existanse: tray #%d of %d\n", cnt, dev->val[OPT_FEED_TIMEOUT].w); status = kv_send_command (dev, &hdr, &rs); if (status) return status; if (rs.status) return SANE_STATUS_NO_DOCS; if ((dev->buffer[0] & 0x20) != 0) { return SANE_STATUS_GOOD; } else if (strcmp (dev->val[OPT_MANUALFEED].s, "off") == 0) { return SANE_STATUS_NO_DOCS; } sleep (1); } return SANE_STATUS_NO_DOCS; } SANE_Status CMD_request_sense (PKV_DEV dev) { KV_CMD_HEADER hdr; KV_CMD_RESPONSE rs; DBG (DBG_proc, "CMD_request_sense\n"); memset (&hdr, 0, sizeof (hdr)); hdr.direction = KV_CMD_IN; hdr.cdb[0] = SCSI_REQUEST_SENSE; hdr.cdb[4] = 0x12; hdr.cdb_size = 6; hdr.data_size = 0x12; hdr.data = dev->buffer; return kv_send_command (dev, &hdr, &rs); } /* Scan routines */ /* Allocate image buffer for one page (1 or 2 sides) */ SANE_Status AllocateImageBuffer (PKV_DEV dev) { int *size = dev->bytes_to_read; int sides = IS_DUPLEX (dev) ? 2 : 1; int i; size[0] = dev->params[0].bytes_per_line * dev->params[0].lines; size[1] = dev->params[1].bytes_per_line * dev->params[1].lines; DBG (DBG_proc, "AllocateImageBuffer: enter\n"); for (i = 0; i < sides; i++) { SANE_Byte *p; DBG (DBG_proc, "AllocateImageBuffer: size(%c)=%d\n", i ? 'B' : 'F', size[i]); if (dev->img_buffers[i] == NULL) { p = (SANE_Byte *) malloc (size[i]); if (p == NULL) { return SANE_STATUS_NO_MEM; } dev->img_buffers[i] = p; } else { p = (SANE_Byte *) realloc (dev->img_buffers[i], size[i]); if (p == NULL) { return SANE_STATUS_NO_MEM; } else { dev->img_buffers[i] = p; } } } DBG (DBG_proc, "AllocateImageBuffer: exit\n"); return SANE_STATUS_GOOD; } /* Read image data from scanner dev->img_buffers[0], for the simplex page */ SANE_Status ReadImageDataSimplex (PKV_DEV dev, int page) { int bytes_to_read = dev->bytes_to_read[0]; SANE_Byte *buffer = (SANE_Byte *) dev->buffer; int buff_size = SCSI_BUFFER_SIZE; SANE_Byte *pt = dev->img_buffers[0]; KV_CMD_RESPONSE rs; dev->img_size[0] = 0; dev->img_size[1] = 0; /* read loop */ do { int size = buff_size; SANE_Status status; DBG (DBG_error, "Bytes left = %d\n", bytes_to_read); status = CMD_read_image (dev, page, SIDE_FRONT, buffer, &size, &rs); if (status) { return status; } if (rs.status) { if (get_RS_sense_key (rs.sense)) { DBG (DBG_error, "Error reading image data, " "sense_key=%d, ASC=%d, ASCQ=%d", get_RS_sense_key (rs.sense), get_RS_ASC (rs.sense), get_RS_ASCQ (rs.sense)); if (get_RS_sense_key (rs.sense) == 3) { if (!get_RS_ASCQ (rs.sense)) return SANE_STATUS_NO_DOCS; return SANE_STATUS_JAMMED; } return SANE_STATUS_IO_ERROR; } } /* copy data to image buffer */ if (size > bytes_to_read) { size = bytes_to_read; } if (size > 0) { memcpy (pt, buffer, size); bytes_to_read -= size; pt += size; dev->img_size[0] += size; } } while (!get_RS_EOM (rs.sense)); assert (pt == dev->img_buffers[0] + dev->img_size[0]); DBG (DBG_error, "Image size = %d\n", dev->img_size[0]); return SANE_STATUS_GOOD; } /* Read image data from scanner dev->img_buffers[0], for the duplex page */ SANE_Status ReadImageDataDuplex (PKV_DEV dev, int page) { int bytes_to_read[2]; SANE_Byte *buffer = (SANE_Byte *) dev->buffer; int buff_size[2]; SANE_Byte *pt[2]; KV_CMD_RESPONSE rs; int sides[2]; SANE_Bool eoms[2]; int current_side = 1; bytes_to_read[0] = dev->bytes_to_read[0]; bytes_to_read[1] = dev->bytes_to_read[1]; pt[0] = dev->img_buffers[0]; pt[1] = dev->img_buffers[1]; sides[0] = SIDE_FRONT; sides[1] = SIDE_BACK; eoms[0] = eoms[1] = 0; buff_size[0] = SCSI_BUFFER_SIZE; buff_size[1] = SCSI_BUFFER_SIZE; dev->img_size[0] = 0; dev->img_size[1] = 0; /* read loop */ do { int size = buff_size[current_side]; SANE_Status status; DBG (DBG_error, "Bytes left (F) = %d\n", bytes_to_read[0]); DBG (DBG_error, "Bytes left (B) = %d\n", bytes_to_read[1]); status = CMD_read_image (dev, page, sides[current_side], buffer, &size, &rs); if (status) { return status; } if (rs.status) { if (get_RS_sense_key (rs.sense)) { DBG (DBG_error, "Error reading image data, " "sense_key=%d, ASC=%d, ASCQ=%d", get_RS_sense_key (rs.sense), get_RS_ASC (rs.sense), get_RS_ASCQ (rs.sense)); if (get_RS_sense_key (rs.sense) == 3) { if (!get_RS_ASCQ (rs.sense)) return SANE_STATUS_NO_DOCS; return SANE_STATUS_JAMMED; } return SANE_STATUS_IO_ERROR; } } /* copy data to image buffer */ if (size > bytes_to_read[current_side]) { size = bytes_to_read[current_side]; } if (size > 0) { memcpy (pt[current_side], buffer, size); bytes_to_read[current_side] -= size; pt[current_side] += size; dev->img_size[current_side] += size; } if (rs.status) { if (get_RS_EOM (rs.sense)) { eoms[current_side] = 1; } if (get_RS_ILI (rs.sense)) { current_side++; current_side &= 1; } } } while (eoms[0] == 0 || eoms[1] == 0); DBG (DBG_error, "Image size (F) = %d\n", dev->img_size[0]); DBG (DBG_error, "Image size (B) = %d\n", dev->img_size[1]); assert (pt[0] == dev->img_buffers[0] + dev->img_size[0]); assert (pt[1] == dev->img_buffers[1] + dev->img_size[1]); return SANE_STATUS_GOOD; } /* Read image data for one page */ SANE_Status ReadImageData (PKV_DEV dev, int page) { SANE_Status status; DBG (DBG_proc, "Reading image data for page %d\n", page); if (IS_DUPLEX (dev)) { DBG (DBG_proc, "ReadImageData: Duplex %d\n", page); status = ReadImageDataDuplex (dev, page); } else { DBG (DBG_proc, "ReadImageData: Simplex %d\n", page); status = ReadImageDataSimplex (dev, page); } dev->img_pt[0] = dev->img_buffers[0]; dev->img_pt[1] = dev->img_buffers[1]; DBG (DBG_proc, "Reading image data for page %d, finished\n", page); return status; } /* Look in image for likely upper and left paper edges, then rotate * image so that upper left corner of paper is upper left of image. * FIXME: should we do this before we binarize instead of after? */ SANE_Status buffer_deskew(PKV_DEV s, int side) { SANE_Status ret = SANE_STATUS_GOOD; int bg_color = 0xd6; int side_index = (side == SIDE_FRONT)?0:1; int resolution = s->val[OPT_RESOLUTION].w; DBG (10, "buffer_deskew: start\n"); /*only find skew on first image from a page, or if first image had error */ if(side == SIDE_FRONT || s->deskew_stat){ s->deskew_stat = sanei_magic_findSkew( &s->params[side_index],s->img_buffers[side_index], resolution,resolution, &s->deskew_vals[0],&s->deskew_vals[1],&s->deskew_slope); if(s->deskew_stat){ DBG (5, "buffer_despeck: bad findSkew, bailing\n"); goto cleanup; } } /* backside images can use a 'flipped' version of frontside data */ else{ s->deskew_slope *= -1; s->deskew_vals[0] = s->params[side_index].pixels_per_line - s->deskew_vals[0]; } ret = sanei_magic_rotate(&s->params[side_index],s->img_buffers[side_index], s->deskew_vals[0],s->deskew_vals[1],s->deskew_slope,bg_color); if(ret){ DBG(5,"buffer_deskew: rotate error: %d",ret); ret = SANE_STATUS_GOOD; goto cleanup; } cleanup: DBG (10, "buffer_deskew: finish\n"); return ret; } /* Look in image for likely left/right/bottom paper edges, then crop image. * Does not attempt to rotate the image, that should be done first. * FIXME: should we do this before we binarize instead of after? */ SANE_Status buffer_crop(PKV_DEV s, int side) { SANE_Status ret = SANE_STATUS_GOOD; int side_index = (side == SIDE_FRONT)?0:1; int resolution = s->val[OPT_RESOLUTION].w; DBG (10, "buffer_crop: start\n"); /*only find edges on first image from a page, or if first image had error */ if(side == SIDE_FRONT || s->crop_stat){ s->crop_stat = sanei_magic_findEdges( &s->params[side_index],s->img_buffers[side_index], resolution,resolution, &s->crop_vals[0],&s->crop_vals[1],&s->crop_vals[2],&s->crop_vals[3]); if(s->crop_stat){ DBG (5, "buffer_crop: bad edges, bailing\n"); goto cleanup; } DBG (15, "buffer_crop: t:%d b:%d l:%d r:%d\n", s->crop_vals[0],s->crop_vals[1],s->crop_vals[2],s->crop_vals[3]); /* we don't listen to the 'top' value, since the top is not padded */ /*s->crop_vals[0] = 0;*/ } /* backside images can use a 'flipped' version of frontside data */ else{ int left = s->crop_vals[2]; int right = s->crop_vals[3]; s->crop_vals[2] = s->params[side_index].pixels_per_line - right; s->crop_vals[3] = s->params[side_index].pixels_per_line - left; } /* now crop the image */ ret = sanei_magic_crop(&s->params[side_index],s->img_buffers[side_index], s->crop_vals[0],s->crop_vals[1],s->crop_vals[2],s->crop_vals[3]); if(ret){ DBG (5, "buffer_crop: bad crop, bailing\n"); ret = SANE_STATUS_GOOD; goto cleanup; } /* update image size counter to new, smaller size */ s->img_size[side_index] = s->params[side_index].lines * s->params[side_index].bytes_per_line; cleanup: DBG (10, "buffer_crop: finish\n"); return ret; } /* Look in image for disconnected 'spots' of the requested size. * Replace the spots with the average color of the surrounding pixels. * FIXME: should we do this before we binarize instead of after? */ SANE_Status buffer_despeck(PKV_DEV s, int side) { SANE_Status ret = SANE_STATUS_GOOD; int side_index = (side == SIDE_FRONT)?0:1; DBG (10, "buffer_despeck: start\n"); ret = sanei_magic_despeck( &s->params[side_index],s->img_buffers[side_index],s->val[OPT_SWDESPECK].w ); if(ret){ DBG (5, "buffer_despeck: bad despeck, bailing\n"); ret = SANE_STATUS_GOOD; goto cleanup; } cleanup: DBG (10, "buffer_despeck: finish\n"); return ret; } /* Look if image has too few dark pixels. * FIXME: should we do this before we binarize instead of after? */ int buffer_isblank(PKV_DEV s, int side) { SANE_Status ret = SANE_STATUS_GOOD; int side_index = (side == SIDE_FRONT)?0:1; int status = 0; DBG (10, "buffer_isblank: start\n"); ret = sanei_magic_isBlank( &s->params[side_index],s->img_buffers[side_index], SANE_UNFIX(s->val[OPT_SWSKIP].w) ); if(ret == SANE_STATUS_NO_DOCS){ DBG (5, "buffer_isblank: blank!\n"); status = 1; } else if(ret){ DBG (5, "buffer_isblank: error %d\n",ret); } DBG (10, "buffer_isblank: finished\n"); return status; } /* Look if image needs rotation * FIXME: should we do this before we binarize instead of after? */ SANE_Status buffer_rotate(PKV_DEV s, int side) { SANE_Status ret = SANE_STATUS_GOOD; int angle = 0; int side_index = (side == SIDE_FRONT)?0:1; int resolution = s->val[OPT_RESOLUTION].w; DBG (10, "buffer_rotate: start\n"); if(s->val[OPT_SWDEROTATE].w){ ret = sanei_magic_findTurn( &s->params[side_index],s->img_buffers[side_index], resolution,resolution,&angle); if(ret){ DBG (5, "buffer_rotate: error %d\n",ret); ret = SANE_STATUS_GOOD; goto cleanup; } } angle += s->val[OPT_ROTATE].w; /*90 or 270 degree rotations are reversed on back side*/ if(side == SIDE_BACK && s->val[OPT_ROTATE].w % 180){ angle += 180; } ret = sanei_magic_turn( &s->params[side_index],s->img_buffers[side_index], angle); if(ret){ DBG (5, "buffer_rotate: error %d\n",ret); ret = SANE_STATUS_GOOD; goto cleanup; } /* update image size counter to new, smaller size */ s->img_size[side_index] = s->params[side_index].lines * s->params[side_index].bytes_per_line; cleanup: DBG (10, "buffer_rotate: finished\n"); return ret; } backends-1.3.0/backend/kvs1025_low.h000066400000000000000000000203151456256263500170400ustar00rootroot00000000000000/* Copyright (C) 2008, Panasonic Russia Ltd. */ /* sane - Scanner Access Now Easy. Panasonic KV-S1020C / KV-S1025C USB scanners. */ #ifndef __KVS1025_LOW_H #define __KVS1025_LOW_H #include "kvs1025_cmds.h" #define VENDOR_ID 0x04DA typedef enum { KV_S1020C = 0x1007, KV_S1025C = 0x1006, KV_S1045C = 0x1010 } KV_MODEL_TYPE; /* Store an integer in 2, 3 or 4 byte in a big-endian array. */ #define Ito16(val, buf) { \ ((unsigned char *)buf)[0] = ((val) >> 8) & 0xff; \ ((unsigned char *)buf)[1] = ((val) >> 0) & 0xff; \ } #define Ito24(val, buf) { \ ((unsigned char *)buf)[0] = ((val) >> 16) & 0xff; \ ((unsigned char *)buf)[1] = ((val) >> 8) & 0xff; \ ((unsigned char *)buf)[2] = ((val) >> 0) & 0xff; \ } #define Ito32(val, buf) { \ ((unsigned char *)buf)[0] = ((val) >> 24) & 0xff; \ ((unsigned char *)buf)[1] = ((val) >> 16) & 0xff; \ ((unsigned char *)buf)[2] = ((val) >> 8) & 0xff; \ ((unsigned char *)buf)[3] = ((val) >> 0) & 0xff; \ } /* 32 bits from an array to an integer (eg ntohl). */ #define B32TOI(buf) \ ((((unsigned char *)buf)[0] << 24) | \ (((unsigned char *)buf)[1] << 16) | \ (((unsigned char *)buf)[2] << 8) | \ (((unsigned char *)buf)[3] << 0)) /* 24 bits from an array to an integer. */ #define B24TOI(buf) \ (((unsigned char *)buf)[0] << 16) | \ (((unsigned char *)buf)[1] << 8) | \ (((unsigned char *)buf)[2] << 0)) #define SCSI_FD int #define SCSI_BUFFER_SIZE (0x40000-12) typedef enum { KV_SCSI_BUS = 0x01, KV_USB_BUS = 0x02 } KV_BUS_MODE; typedef enum { SM_BINARY = 0x00, SM_DITHER = 0x01, SM_GRAYSCALE = 0x02, SM_COLOR = 0x05 } KV_SCAN_MODE; typedef struct { unsigned char data[16]; int len; } CDB; typedef struct { int width; int height; } KV_PAPER_SIZE; /* remarked -- KV-S1020C / KV-S1025C supports ADF only typedef enum { TRUPER_ADF = 0, TRUPER_FLATBED = 1 } KV_SCAN_SOURCE; */ /* options */ typedef enum { OPT_NUM_OPTS = 0, /* General options */ OPT_MODE_GROUP, OPT_MODE, /* scanner modes */ OPT_RESOLUTION, /* X and Y resolution */ OPT_DUPLEX, /* Duplex mode */ OPT_SCAN_SOURCE, /* Scan source, fixed to ADF */ OPT_FEEDER_MODE, /* Feeder mode, fixed to Continuous */ OPT_LONGPAPER, /* Long paper mode */ OPT_LENGTHCTL, /* Length control mode */ OPT_MANUALFEED, /* Manual feed mode */ OPT_FEED_TIMEOUT, /* Feed timeout */ OPT_DBLFEED, /* Double feed detection mode */ OPT_FIT_TO_PAGE, /* Scanner shrinks image to fit scanned page */ /* Geometry group */ OPT_GEOMETRY_GROUP, OPT_PAPER_SIZE, /* Paper size */ OPT_LANDSCAPE, /* true if landscape; new for Truper 3200/3600 */ OPT_TL_X, /* upper left X */ OPT_TL_Y, /* upper left Y */ OPT_BR_X, /* bottom right X */ OPT_BR_Y, /* bottom right Y */ OPT_ENHANCEMENT_GROUP, OPT_BRIGHTNESS, /* Brightness */ OPT_CONTRAST, /* Contrast */ OPT_AUTOMATIC_THRESHOLD, /* Binary threshold */ OPT_HALFTONE_PATTERN, /* Halftone pattern */ OPT_AUTOMATIC_SEPARATION, /* Automatic separation */ OPT_WHITE_LEVEL, /* White level */ OPT_NOISE_REDUCTION, /* Noise reduction */ OPT_IMAGE_EMPHASIS, /* Image emphasis */ OPT_GAMMA, /* Gamma */ OPT_LAMP, /* Lamp -- color drop out */ OPT_INVERSE, /* Inverse image */ OPT_MIRROR, /* Mirror image */ OPT_JPEG, /* JPEG Compression */ OPT_ROTATE, /* Rotate image */ OPT_SWDESKEW, /* Software deskew */ OPT_SWDESPECK, /* Software despeckle */ OPT_SWDEROTATE, /* Software detect/correct 90 deg. rotation */ OPT_SWCROP, /* Software autocrop */ OPT_SWSKIP, /* Software blank page skip */ /* must come last: */ OPT_NUM_OPTIONS } KV_OPTION; typedef struct { int memory_size; /* in MB */ int min_resolution; /* in DPI */ int max_resolution; /* in DPI */ int step_resolution; /* in DPI */ int support_duplex; /* 1 if true */ int support_lamp; /* 1 if true */ int max_x_range; /* in mm */ int max_y_range; /* in mm */ } KV_SUPPORT_INFO; typedef struct kv_scanner_dev { struct kv_scanner_dev *next; SANE_Device sane; /* Infos from inquiry. */ char scsi_type; char scsi_type_str[32]; char scsi_vendor[12]; char scsi_product[20]; char scsi_version[8]; /* Bus info */ KV_BUS_MODE bus_mode; SANE_Int usb_fd; char device_name[100]; char *scsi_device_name; SCSI_FD scsi_fd; KV_MODEL_TYPE model_type; SANE_Parameters params[2]; /* SCSI handling */ SANE_Byte *buffer0; SANE_Byte *buffer; /* buffer = buffer0 + 12 */ /* for USB bulk transfer, a 12 bytes container is required for each block */ /* Scanning handling. */ int scanning; /* TRUE if a scan is running. */ int current_page; /* the current page number, 0 is page 1 */ int current_side; /* the current side */ int bytes_to_read[2]; /* bytes to read */ /* --------------------------------------------------------------------- */ /* values used by the software enhancement code (deskew, crop, etc) */ SANE_Status deskew_stat; int deskew_vals[2]; double deskew_slope; SANE_Status crop_stat; int crop_vals[4]; /* Support info */ KV_SUPPORT_INFO support_info; SANE_Range x_range, y_range; /* Options */ SANE_Option_Descriptor opt[OPT_NUM_OPTIONS]; Option_Value val[OPT_NUM_OPTIONS]; SANE_Bool option_set; /* Image buffer */ SANE_Byte *img_buffers[2]; SANE_Byte *img_pt[2]; int img_size[2]; } KV_DEV, *PKV_DEV; #define GET_OPT_VAL_W(dev, idx) ((dev)->val[idx].w) #define GET_OPT_VAL_L(dev, idx, token) get_optval_list(dev, idx, \ go_##token##_list, go_##token##_val) #define IS_DUPLEX(dev) GET_OPT_VAL_W(dev, OPT_DUPLEX) /* Prototypes in kvs1025_opt.c */ int get_optval_list (const PKV_DEV dev, int idx, const SANE_String_Const * str_list, const int *val_list); KV_SCAN_MODE kv_get_mode (const PKV_DEV dev); int kv_get_depth (KV_SCAN_MODE mode); void kv_calc_paper_size (const PKV_DEV dev, int *w, int *h); const SANE_Option_Descriptor *kv_get_option_descriptor (PKV_DEV dev, SANE_Int option); void kv_init_options (PKV_DEV dev); SANE_Status kv_control_option (PKV_DEV dev, SANE_Int option, SANE_Action action, void *val, SANE_Int * info); void hexdump (int level, const char *comment, unsigned char *p, int l); void kv_set_window_data (PKV_DEV dev, KV_SCAN_MODE scan_mode, int side, unsigned char *windowdata); /* Prototypes in kvs1025_low.c */ SANE_Status kv_enum_devices (void); void kv_get_devices_list (const SANE_Device *** devices_list); void kv_exit (void); SANE_Status kv_open (PKV_DEV dev); SANE_Bool kv_already_open (PKV_DEV dev); SANE_Status kv_open_by_name (SANE_String_Const devicename, SANE_Handle * handle); void kv_close (PKV_DEV dev); SANE_Status kv_send_command (PKV_DEV dev, PKV_CMD_HEADER header, PKV_CMD_RESPONSE response); /* Commands */ SANE_Status CMD_test_unit_ready (PKV_DEV dev, SANE_Bool * ready); SANE_Status CMD_read_support_info (PKV_DEV dev); SANE_Status CMD_scan (PKV_DEV dev); SANE_Status CMD_set_window (PKV_DEV dev, int side, PKV_CMD_RESPONSE rs); SANE_Status CMD_reset_window (PKV_DEV dev); SANE_Status CMD_get_buff_status (PKV_DEV dev, int *front_size, int *back_size); SANE_Status CMD_wait_buff_status (PKV_DEV dev, int *front_size, int *back_size); SANE_Status CMD_read_pic_elements (PKV_DEV dev, int page, int side, int *width, int *height); SANE_Status CMD_read_image (PKV_DEV dev, int page, int side, unsigned char *buffer, int *psize, KV_CMD_RESPONSE * rs); SANE_Status CMD_wait_document_existanse (PKV_DEV dev); SANE_Status CMD_get_document_existanse (PKV_DEV dev); SANE_Status CMD_set_timeout (PKV_DEV dev, SANE_Word timeout); SANE_Status CMD_request_sense (PKV_DEV dev); /* Scan routines */ SANE_Status AllocateImageBuffer (PKV_DEV dev); SANE_Status ReadImageDataSimplex (PKV_DEV dev, int page); SANE_Status ReadImageDataDuplex (PKV_DEV dev, int page); SANE_Status ReadImageData (PKV_DEV dev, int page); SANE_Status buffer_deskew (PKV_DEV dev, int side); SANE_Status buffer_crop (PKV_DEV dev, int side); SANE_Status buffer_despeck (PKV_DEV dev, int side); int buffer_isblank (PKV_DEV dev, int side); SANE_Status buffer_rotate(PKV_DEV dev, int side); #endif /* #ifndef __KVS1025_LOW_H */ backends-1.3.0/backend/kvs1025_opt.c000066400000000000000000001320641456256263500170410ustar00rootroot00000000000000/* Copyright (C) 2008, Panasonic Russia Ltd. */ /* sane - Scanner Access Now Easy. Panasonic KV-S1020C / KV-S1025C USB scanners. */ #define DEBUG_DECLARE_ONLY #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_config.h" #include "../include/lassert.h" #include "kvs1025.h" #include "kvs1025_low.h" #include "../include/sane/sanei_debug.h" /* Option lists */ static SANE_String_Const go_scan_mode_list[] = { SANE_I18N ("bw"), SANE_I18N ("halftone"), SANE_I18N ("gray"), SANE_I18N ("color"), NULL }; /* static int go_scan_mode_val[] = { 0x00, 0x01, 0x02, 0x05 };*/ static const SANE_Word go_resolutions_list[] = { 11, /* list size */ 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600 }; /* List of scan sources */ static SANE_String_Const go_scan_source_list[] = { SANE_I18N ("adf"), SANE_I18N ("fb"), NULL }; static const int go_scan_source_val[] = { 0, 0x1 }; /* List of feeder modes */ static SANE_String_Const go_feeder_mode_list[] = { SANE_I18N ("single"), SANE_I18N ("continuous"), NULL }; static const int go_feeder_mode_val[] = { 0x00, 0xff }; /* List of manual feed mode */ static SANE_String_Const go_manual_feed_list[] = { SANE_I18N ("off"), SANE_I18N ("wait_doc"), SANE_I18N ("wait_key"), NULL }; static const int go_manual_feed_val[] = { 0x00, 0x01, 0x02 }; /* List of paper sizes */ static SANE_String_Const go_paper_list[] = { SANE_I18N ("user_def"), SANE_I18N ("business_card"), SANE_I18N ("Check"), /*SANE_I18N ("A3"), */ SANE_I18N ("A4"), SANE_I18N ("A5"), SANE_I18N ("A6"), SANE_I18N ("Letter"), /*SANE_I18N ("Double letter 11x17 in"), SANE_I18N ("B4"), */ SANE_I18N ("B5"), SANE_I18N ("B6"), SANE_I18N ("Legal"), NULL }; static const int go_paper_val[] = { 0x00, 0x01, 0x02, /*0x03, *//* A3 : not supported */ 0x04, 0x05, 0x06, 0x07, /*0x09, 0x0C, *//* Dbl letter and B4 : not supported */ 0x0D, 0x0E, 0x0F }; static const KV_PAPER_SIZE go_paper_sizes[] = { {210, 297}, /* User defined, default=A4 */ {54, 90}, /* Business card */ {80, 170}, /* Check (China business) */ /*{297, 420}, *//* A3 */ {210, 297}, /* A4 */ {148, 210}, /* A5 */ {105, 148}, /* A6 */ {216, 280}, /* US Letter 8.5 x 11 in */ /*{280, 432}, *//* Double Letter 11 x 17 in */ /*{250, 353}, *//* B4 */ {176, 250}, /* B5 */ {125, 176}, /* B6 */ {216, 356} /* US Legal */ }; static const int default_paper_size_idx = 3; /* A4 */ /* Lists of supported halftone. They are only valid with * for the Black&White mode. */ static SANE_String_Const go_halftone_pattern_list[] = { SANE_I18N ("bayer_64"), SANE_I18N ("bayer_16"), SANE_I18N ("halftone_32"), SANE_I18N ("halftone_64"), SANE_I18N ("diffusion"), NULL }; static const int go_halftone_pattern_val[] = { 0x00, 0x01, 0x02, 0x03, 0x04 }; /* List of automatic threshold options */ static SANE_String_Const go_automatic_threshold_list[] = { SANE_I18N ("normal"), SANE_I18N ("light"), SANE_I18N ("dark"), NULL }; static const int go_automatic_threshold_val[] = { 0, 0x11, 0x1f }; /* List of white level base. */ static SANE_String_Const go_white_level_list[] = { SANE_I18N ("From scanner"), SANE_I18N ("From paper"), SANE_I18N ("Automatic"), NULL }; static const int go_white_level_val[] = { 0x00, 0x80, 0x81 }; /* List of noise reduction options. */ static SANE_String_Const go_noise_reduction_list[] = { SANE_I18N ("default"), "1x1", "2x2", "3x3", "4x4", "5x5", NULL }; static const int go_noise_reduction_val[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 }; /* List of image emphasis options, 5 steps */ static SANE_String_Const go_image_emphasis_list[] = { SANE_I18N ("smooth"), SANE_I18N ("none"), SANE_I18N ("low"), SANE_I18N ("medium"), /* default */ SANE_I18N ("high"), NULL }; static const int go_image_emphasis_val[] = { 0x14, 0x00, 0x11, 0x12, 0x13 }; /* List of gamma */ static SANE_String_Const go_gamma_list[] = { SANE_I18N ("normal"), SANE_I18N ("crt"), SANE_I18N ("linear"), NULL }; static const int go_gamma_val[] = { 0x00, 0x01, 0x02 }; /* List of lamp color dropout */ static SANE_String_Const go_lamp_list[] = { SANE_I18N ("normal"), SANE_I18N ("red"), SANE_I18N ("green"), SANE_I18N ("blue"), NULL }; static const int go_lamp_val[] = { 0x00, 0x01, 0x02, 0x03 }; static SANE_Range go_value_range = { 0, 255, 0 }; static SANE_Range go_jpeg_compression_range = { 0, 0x64, 0 }; static SANE_Range go_rotate_range = { 0, 270, 90 }; static SANE_Range go_swdespeck_range = { 0, 9, 1 }; static SANE_Range go_swskip_range = { SANE_FIX(0), SANE_FIX(100), 1 }; static const char *go_option_name[] = { "OPT_NUM_OPTS", /* General options */ "OPT_MODE_GROUP", "OPT_MODE", /* scanner modes */ "OPT_RESOLUTION", /* X and Y resolution */ "OPT_DUPLEX", /* Duplex mode */ "OPT_SCAN_SOURCE", /* Scan source, fixed to ADF */ "OPT_FEEDER_MODE", /* Feeder mode, fixed to Continuous */ "OPT_LONGPAPER", /* Long paper mode */ "OPT_LENGTHCTL", /* Length control mode */ "OPT_MANUALFEED", /* Manual feed mode */ "OPT_FEED_TIMEOUT", /* Feed timeout */ "OPT_DBLFEED", /* Double feed detection mode */ "OPT_FIT_TO_PAGE", /* Scanner shrinks image to fit scanned page */ /* Geometry group */ "OPT_GEOMETRY_GROUP", "OPT_PAPER_SIZE", /* Paper size */ "OPT_LANDSCAPE", /* true if landscape */ "OPT_TL_X", /* upper left X */ "OPT_TL_Y", /* upper left Y */ "OPT_BR_X", /* bottom right X */ "OPT_BR_Y", /* bottom right Y */ "OPT_ENHANCEMENT_GROUP", "OPT_BRIGHTNESS", /* Brightness */ "OPT_CONTRAST", /* Contrast */ "OPT_AUTOMATIC_THRESHOLD", /* Binary threshold */ "OPT_HALFTONE_PATTERN", /* Halftone pattern */ "OPT_AUTOMATIC_SEPARATION", /* Automatic separation */ "OPT_WHITE_LEVEL", /* White level */ "OPT_NOISE_REDUCTION", /* Noise reduction */ "OPT_IMAGE_EMPHASIS", /* Image emphasis */ "OPT_GAMMA", /* Gamma */ "OPT_LAMP", /* Lamp -- color drop out */ "OPT_INVERSE", /* Inverse image */ "OPT_MIRROR", /* Mirror image */ "OPT_JPEG", /* JPEG Compression */ "OPT_ROTATE", /* Rotate image */ "OPT_SWDESKEW", /* Software deskew */ "OPT_SWDESPECK", /* Software despeckle */ "OPT_SWDEROTATE", /* Software detect/correct 90 deg. rotation */ "OPT_SWCROP", /* Software autocrop */ "OPT_SWSKIP", /* Software blank page skip */ /* must come last: */ "OPT_NUM_OPTIONS" }; /* Round to boundry, return 1 if value modified */ static int round_to_boundry (SANE_Word * pval, SANE_Word boundry, SANE_Word minv, SANE_Word maxv) { SANE_Word lower, upper, k, v; v = *pval; k = v / boundry; lower = k * boundry; upper = (k + 1) * boundry; if (v - lower <= upper - v) { *pval = lower; } else { *pval = upper; } if ((*pval) < minv) *pval = minv; if ((*pval) > maxv) *pval = maxv; return ((*pval) != v); } /* Returns the length of the longest string, including the terminating * character. */ static size_t max_string_size (SANE_String_Const * strings) { size_t size, max_size = 0; int i; for (i = 0; strings[i]; ++i) { size = strlen (strings[i]) + 1; if (size > max_size) { max_size = size; } } return max_size; } /* Lookup a string list from one array and return its index. */ static int get_string_list_index (const SANE_String_Const * list, SANE_String_Const name) { int index; index = 0; while (list[index] != NULL) { if (strcmp (list[index], name) == 0) { return (index); } index++; } DBG (DBG_error, "System bug: option %s not found in list\n", name); return (-1); /* not found */ } /* Lookup a string list from one array and return the correnpond value. */ int get_optval_list (const PKV_DEV dev, int idx, const SANE_String_Const * str_list, const int *val_list) { int index; index = get_string_list_index (str_list, dev->val[idx].s); if (index < 0) index = 0; return val_list[index]; } /* Get device mode from device options */ KV_SCAN_MODE kv_get_mode (const PKV_DEV dev) { int i; i = get_string_list_index (go_scan_mode_list, dev->val[OPT_MODE].s); switch (i) { case 0: return SM_BINARY; case 1: return SM_DITHER; case 2: return SM_GRAYSCALE; case 3: return SM_COLOR; default: assert (0 == 1); return 0; } } void kv_calc_paper_size (const PKV_DEV dev, int *w, int *h) { int i = get_string_list_index (go_paper_list, dev->val[OPT_PAPER_SIZE].s); if (i == 0) { /* Non-standard document */ int x_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_X].w)); int y_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_Y].w)); int x_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_X].w)); int y_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_Y].w)); *w = x_br - x_tl; *h = y_br - y_tl; } else { if (dev->val[OPT_LANDSCAPE].s) { *h = mmToIlu (go_paper_sizes[i].width); *w = mmToIlu (go_paper_sizes[i].height); } else { *w = mmToIlu (go_paper_sizes[i].width); *h = mmToIlu (go_paper_sizes[i].height); } } } /* Get bit depth from scan mode */ int kv_get_depth (KV_SCAN_MODE mode) { switch (mode) { case SM_BINARY: case SM_DITHER: return 1; case SM_GRAYSCALE: return 8; case SM_COLOR: return 24; default: assert (0 == 1); return 0; } } const SANE_Option_Descriptor * kv_get_option_descriptor (PKV_DEV dev, SANE_Int option) { DBG (DBG_proc, "sane_get_option_descriptor: enter, option %s\n", go_option_name[option]); if ((unsigned) option >= OPT_NUM_OPTIONS) { return NULL; } DBG (DBG_proc, "sane_get_option_descriptor: exit\n"); return dev->opt + option; } /* Reset the options for that scanner. */ void kv_init_options (PKV_DEV dev) { int i; if (dev->option_set) return; DBG (DBG_proc, "kv_init_options: enter\n"); /* Pre-initialize the options. */ memset (dev->opt, 0, sizeof (dev->opt)); memset (dev->val, 0, sizeof (dev->val)); for (i = 0; i < OPT_NUM_OPTIONS; ++i) { dev->opt[i].size = sizeof (SANE_Word); dev->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } /* Number of options. */ dev->opt[OPT_NUM_OPTS].name = ""; dev->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; dev->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; dev->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; dev->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; dev->val[OPT_NUM_OPTS].w = OPT_NUM_OPTIONS; /* Mode group */ dev->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode"); dev->opt[OPT_MODE_GROUP].desc = ""; /* not valid for a group */ dev->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; dev->opt[OPT_MODE_GROUP].cap = 0; dev->opt[OPT_MODE_GROUP].size = 0; dev->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* Scanner supported modes */ dev->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; dev->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE; dev->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE; dev->opt[OPT_MODE].type = SANE_TYPE_STRING; dev->opt[OPT_MODE].size = max_string_size (go_scan_mode_list); dev->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->opt[OPT_MODE].constraint.string_list = go_scan_mode_list; dev->val[OPT_MODE].s = strdup (""); /* will be set later */ /* X and Y resolution */ dev->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; dev->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; dev->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; dev->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; dev->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; dev->opt[OPT_RESOLUTION].constraint.word_list = go_resolutions_list; dev->val[OPT_RESOLUTION].w = go_resolutions_list[3]; /* Duplex */ dev->opt[OPT_DUPLEX].name = SANE_NAME_DUPLEX; dev->opt[OPT_DUPLEX].title = SANE_TITLE_DUPLEX; dev->opt[OPT_DUPLEX].desc = SANE_DESC_DUPLEX; dev->opt[OPT_DUPLEX].type = SANE_TYPE_BOOL; dev->opt[OPT_DUPLEX].unit = SANE_UNIT_NONE; dev->val[OPT_DUPLEX].w = SANE_FALSE; if (!dev->support_info.support_duplex) dev->opt[OPT_DUPLEX].cap |= SANE_CAP_INACTIVE; /* Scan source */ dev->opt[OPT_SCAN_SOURCE].name = SANE_NAME_SCAN_SOURCE; dev->opt[OPT_SCAN_SOURCE].title = SANE_TITLE_SCAN_SOURCE; dev->opt[OPT_SCAN_SOURCE].desc = SANE_I18N ("Sets the scan source"); dev->opt[OPT_SCAN_SOURCE].type = SANE_TYPE_STRING; dev->opt[OPT_SCAN_SOURCE].size = max_string_size (go_scan_source_list); dev->opt[OPT_SCAN_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->opt[OPT_SCAN_SOURCE].constraint.string_list = go_scan_source_list; dev->val[OPT_SCAN_SOURCE].s = strdup (go_scan_source_list[0]); dev->opt[OPT_SCAN_SOURCE].cap &= ~SANE_CAP_SOFT_SELECT; /* for KV-S1020C / KV-S1025C, scan source is fixed to ADF */ /* Feeder mode */ dev->opt[OPT_FEEDER_MODE].name = "feeder-mode"; dev->opt[OPT_FEEDER_MODE].title = SANE_I18N ("Feeder mode"); dev->opt[OPT_FEEDER_MODE].desc = SANE_I18N ("Sets the feeding mode"); dev->opt[OPT_FEEDER_MODE].type = SANE_TYPE_STRING; dev->opt[OPT_FEEDER_MODE].size = max_string_size (go_feeder_mode_list); dev->opt[OPT_FEEDER_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->opt[OPT_FEEDER_MODE].constraint.string_list = go_feeder_mode_list; dev->val[OPT_FEEDER_MODE].s = strdup (go_feeder_mode_list[1]); /* Long paper */ dev->opt[OPT_LONGPAPER].name = SANE_NAME_LONGPAPER; dev->opt[OPT_LONGPAPER].title = SANE_TITLE_LONGPAPER; dev->opt[OPT_LONGPAPER].desc = SANE_I18N ("Enable/Disable long paper mode"); dev->opt[OPT_LONGPAPER].type = SANE_TYPE_BOOL; dev->opt[OPT_LONGPAPER].unit = SANE_UNIT_NONE; dev->val[OPT_LONGPAPER].w = SANE_FALSE; /* Length control */ dev->opt[OPT_LENGTHCTL].name = SANE_NAME_LENGTHCTL; dev->opt[OPT_LENGTHCTL].title = SANE_TITLE_LENGTHCTL; dev->opt[OPT_LENGTHCTL].desc = SANE_I18N ("Enable/Disable length control mode"); dev->opt[OPT_LENGTHCTL].type = SANE_TYPE_BOOL; dev->opt[OPT_LENGTHCTL].unit = SANE_UNIT_NONE; dev->val[OPT_LENGTHCTL].w = SANE_TRUE; /* Manual feed */ dev->opt[OPT_MANUALFEED].name = SANE_NAME_MANUALFEED; dev->opt[OPT_MANUALFEED].title = SANE_TITLE_MANUALFEED; dev->opt[OPT_MANUALFEED].desc = SANE_I18N ("Sets the manual feed mode"); dev->opt[OPT_MANUALFEED].type = SANE_TYPE_STRING; dev->opt[OPT_MANUALFEED].size = max_string_size (go_manual_feed_list); dev->opt[OPT_MANUALFEED].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->opt[OPT_MANUALFEED].constraint.string_list = go_manual_feed_list; dev->val[OPT_MANUALFEED].s = strdup (go_manual_feed_list[0]); /*Manual feed timeout */ dev->opt[OPT_FEED_TIMEOUT].name = SANE_NAME_FEED_TIMEOUT; dev->opt[OPT_FEED_TIMEOUT].title = SANE_TITLE_FEED_TIMEOUT; dev->opt[OPT_FEED_TIMEOUT].desc = SANE_I18N ("Sets the manual feed timeout in seconds"); dev->opt[OPT_FEED_TIMEOUT].type = SANE_TYPE_INT; dev->opt[OPT_FEED_TIMEOUT].unit = SANE_UNIT_NONE; dev->opt[OPT_FEED_TIMEOUT].size = sizeof (SANE_Int); dev->opt[OPT_FEED_TIMEOUT].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_FEED_TIMEOUT].constraint.range = &(go_value_range); dev->opt[OPT_FEED_TIMEOUT].cap |= SANE_CAP_INACTIVE; dev->val[OPT_FEED_TIMEOUT].w = 30; /* Double feed */ dev->opt[OPT_DBLFEED].name = SANE_NAME_DBLFEED; dev->opt[OPT_DBLFEED].title = SANE_TITLE_DBLFEED; dev->opt[OPT_DBLFEED].desc = SANE_I18N ("Enable/Disable double feed detection"); dev->opt[OPT_DBLFEED].type = SANE_TYPE_BOOL; dev->opt[OPT_DBLFEED].unit = SANE_UNIT_NONE; dev->val[OPT_DBLFEED].w = SANE_FALSE; /* Fit to page */ dev->opt[OPT_FIT_TO_PAGE].name = SANE_I18N ("fit-to-page"); dev->opt[OPT_FIT_TO_PAGE].title = SANE_I18N ("Fit to page"); dev->opt[OPT_FIT_TO_PAGE].desc = SANE_I18N ("Scanner shrinks image to fit scanned page"); dev->opt[OPT_FIT_TO_PAGE].type = SANE_TYPE_BOOL; dev->opt[OPT_FIT_TO_PAGE].unit = SANE_UNIT_NONE; dev->val[OPT_FIT_TO_PAGE].w = SANE_FALSE; /* Geometry group */ dev->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry"); dev->opt[OPT_GEOMETRY_GROUP].desc = ""; /* not valid for a group */ dev->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; dev->opt[OPT_GEOMETRY_GROUP].cap = 0; dev->opt[OPT_GEOMETRY_GROUP].size = 0; dev->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* Paper sizes list */ dev->opt[OPT_PAPER_SIZE].name = SANE_NAME_PAPER_SIZE; dev->opt[OPT_PAPER_SIZE].title = SANE_TITLE_PAPER_SIZE; dev->opt[OPT_PAPER_SIZE].desc = SANE_DESC_PAPER_SIZE; dev->opt[OPT_PAPER_SIZE].type = SANE_TYPE_STRING; dev->opt[OPT_PAPER_SIZE].size = max_string_size (go_paper_list); dev->opt[OPT_PAPER_SIZE].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->opt[OPT_PAPER_SIZE].constraint.string_list = go_paper_list; dev->val[OPT_PAPER_SIZE].s = strdup (""); /* will be set later */ /* Landscape */ dev->opt[OPT_LANDSCAPE].name = SANE_NAME_LANDSCAPE; dev->opt[OPT_LANDSCAPE].title = SANE_TITLE_LANDSCAPE; dev->opt[OPT_LANDSCAPE].desc = SANE_I18N ("Set paper position : " "true for landscape, false for portrait"); dev->opt[OPT_LANDSCAPE].type = SANE_TYPE_BOOL; dev->opt[OPT_LANDSCAPE].unit = SANE_UNIT_NONE; dev->val[OPT_LANDSCAPE].w = SANE_FALSE; /* Upper left X */ dev->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; dev->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; dev->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; dev->opt[OPT_TL_X].type = SANE_TYPE_FIXED; dev->opt[OPT_TL_X].unit = SANE_UNIT_MM; dev->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_TL_X].constraint.range = &(dev->x_range); /* Upper left Y */ dev->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; dev->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; dev->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; dev->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; dev->opt[OPT_TL_Y].unit = SANE_UNIT_MM; dev->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_TL_Y].constraint.range = &(dev->y_range); /* Bottom-right x */ dev->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; dev->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; dev->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; dev->opt[OPT_BR_X].type = SANE_TYPE_FIXED; dev->opt[OPT_BR_X].unit = SANE_UNIT_MM; dev->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_BR_X].constraint.range = &(dev->x_range); /* Bottom-right y */ dev->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; dev->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; dev->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; dev->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; dev->opt[OPT_BR_Y].unit = SANE_UNIT_MM; dev->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_BR_Y].constraint.range = &(dev->y_range); /* Enhancement group */ dev->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement"); dev->opt[OPT_ENHANCEMENT_GROUP].desc = ""; /* not valid for a group */ dev->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; dev->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED; dev->opt[OPT_ENHANCEMENT_GROUP].size = 0; dev->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* Brightness */ dev->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; dev->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; dev->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; dev->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT; dev->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE; dev->opt[OPT_BRIGHTNESS].size = sizeof (SANE_Int); dev->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_BRIGHTNESS].constraint.range = &(go_value_range); dev->val[OPT_BRIGHTNESS].w = 128; /* Contrast */ dev->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST; dev->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; dev->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST; dev->opt[OPT_CONTRAST].type = SANE_TYPE_INT; dev->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE; dev->opt[OPT_CONTRAST].size = sizeof (SANE_Int); dev->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_CONTRAST].constraint.range = &(go_value_range); dev->val[OPT_CONTRAST].w = 128; /* Automatic threshold */ dev->opt[OPT_AUTOMATIC_THRESHOLD].name = "automatic-threshold"; dev->opt[OPT_AUTOMATIC_THRESHOLD].title = SANE_I18N ("Automatic threshold"); dev->opt[OPT_AUTOMATIC_THRESHOLD].desc = SANE_I18N ("Automatically sets brightness, contrast, white level, " "gamma, noise reduction and image emphasis"); dev->opt[OPT_AUTOMATIC_THRESHOLD].type = SANE_TYPE_STRING; dev->opt[OPT_AUTOMATIC_THRESHOLD].size = max_string_size (go_automatic_threshold_list); dev->opt[OPT_AUTOMATIC_THRESHOLD].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->opt[OPT_AUTOMATIC_THRESHOLD].constraint.string_list = go_automatic_threshold_list; dev->val[OPT_AUTOMATIC_THRESHOLD].s = strdup (go_automatic_threshold_list[0]); /* Halftone pattern */ dev->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN; dev->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN; dev->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN; dev->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING; dev->opt[OPT_HALFTONE_PATTERN].size = max_string_size (go_halftone_pattern_list); dev->opt[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->opt[OPT_HALFTONE_PATTERN].constraint.string_list = go_halftone_pattern_list; dev->val[OPT_HALFTONE_PATTERN].s = strdup (go_halftone_pattern_list[0]); /* Automatic separation */ dev->opt[OPT_AUTOMATIC_SEPARATION].name = SANE_NAME_AUTOSEP; dev->opt[OPT_AUTOMATIC_SEPARATION].title = SANE_TITLE_AUTOSEP; dev->opt[OPT_AUTOMATIC_SEPARATION].desc = SANE_DESC_AUTOSEP; dev->opt[OPT_AUTOMATIC_SEPARATION].type = SANE_TYPE_BOOL; dev->opt[OPT_AUTOMATIC_SEPARATION].unit = SANE_UNIT_NONE; dev->val[OPT_AUTOMATIC_SEPARATION].w = SANE_FALSE; /* White level base */ dev->opt[OPT_WHITE_LEVEL].name = SANE_NAME_WHITE_LEVEL; dev->opt[OPT_WHITE_LEVEL].title = SANE_TITLE_WHITE_LEVEL; dev->opt[OPT_WHITE_LEVEL].desc = SANE_DESC_WHITE_LEVEL; dev->opt[OPT_WHITE_LEVEL].type = SANE_TYPE_STRING; dev->opt[OPT_WHITE_LEVEL].size = max_string_size (go_white_level_list); dev->opt[OPT_WHITE_LEVEL].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->opt[OPT_WHITE_LEVEL].constraint.string_list = go_white_level_list; dev->val[OPT_WHITE_LEVEL].s = strdup (go_white_level_list[0]); /* Noise reduction */ dev->opt[OPT_NOISE_REDUCTION].name = "noise-reduction"; dev->opt[OPT_NOISE_REDUCTION].title = SANE_I18N ("Noise reduction"); dev->opt[OPT_NOISE_REDUCTION].desc = SANE_I18N ("Reduce the isolated dot noise"); dev->opt[OPT_NOISE_REDUCTION].type = SANE_TYPE_STRING; dev->opt[OPT_NOISE_REDUCTION].size = max_string_size (go_noise_reduction_list); dev->opt[OPT_NOISE_REDUCTION].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->opt[OPT_NOISE_REDUCTION].constraint.string_list = go_noise_reduction_list; dev->val[OPT_NOISE_REDUCTION].s = strdup (go_noise_reduction_list[0]); /* Image emphasis */ dev->opt[OPT_IMAGE_EMPHASIS].name = "image-emphasis"; dev->opt[OPT_IMAGE_EMPHASIS].title = SANE_I18N ("Image emphasis"); dev->opt[OPT_IMAGE_EMPHASIS].desc = SANE_I18N ("Sets the image emphasis"); dev->opt[OPT_IMAGE_EMPHASIS].type = SANE_TYPE_STRING; dev->opt[OPT_IMAGE_EMPHASIS].size = max_string_size (go_image_emphasis_list); dev->opt[OPT_IMAGE_EMPHASIS].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->opt[OPT_IMAGE_EMPHASIS].constraint.string_list = go_image_emphasis_list; dev->val[OPT_IMAGE_EMPHASIS].s = strdup (SANE_I18N ("medium")); /* Gamma */ dev->opt[OPT_GAMMA].name = "gamma"; dev->opt[OPT_GAMMA].title = SANE_I18N ("Gamma"); dev->opt[OPT_GAMMA].desc = SANE_I18N ("Gamma"); dev->opt[OPT_GAMMA].type = SANE_TYPE_STRING; dev->opt[OPT_GAMMA].size = max_string_size (go_gamma_list); dev->opt[OPT_GAMMA].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->opt[OPT_GAMMA].constraint.string_list = go_gamma_list; dev->val[OPT_GAMMA].s = strdup (go_gamma_list[0]); /* Lamp color dropout */ dev->opt[OPT_LAMP].name = "lamp-color"; dev->opt[OPT_LAMP].title = SANE_I18N ("Lamp color"); dev->opt[OPT_LAMP].desc = SANE_I18N ("Sets the lamp color (color dropout)"); dev->opt[OPT_LAMP].type = SANE_TYPE_STRING; dev->opt[OPT_LAMP].size = max_string_size (go_lamp_list); dev->opt[OPT_LAMP].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->opt[OPT_LAMP].constraint.string_list = go_lamp_list; dev->val[OPT_LAMP].s = strdup (go_lamp_list[0]); if (!dev->support_info.support_lamp) dev->opt[OPT_LAMP].cap |= SANE_CAP_INACTIVE; /* Inverse image */ dev->opt[OPT_INVERSE].name = SANE_NAME_INVERSE; dev->opt[OPT_INVERSE].title = SANE_TITLE_INVERSE; dev->opt[OPT_INVERSE].desc = SANE_I18N ("Inverse image in B/W or halftone mode"); dev->opt[OPT_INVERSE].type = SANE_TYPE_BOOL; dev->opt[OPT_INVERSE].unit = SANE_UNIT_NONE; dev->val[OPT_INVERSE].w = SANE_FALSE; /* Mirror image (left/right flip) */ dev->opt[OPT_MIRROR].name = SANE_NAME_MIRROR; dev->opt[OPT_MIRROR].title = SANE_TITLE_MIRROR; dev->opt[OPT_MIRROR].desc = SANE_I18N ("Mirror image (left/right flip)"); dev->opt[OPT_MIRROR].type = SANE_TYPE_BOOL; dev->opt[OPT_MIRROR].unit = SANE_UNIT_NONE; dev->val[OPT_MIRROR].w = SANE_FALSE; /* JPEG Image Compression */ dev->opt[OPT_JPEG].name = "jpeg"; dev->opt[OPT_JPEG].title = SANE_I18N ("jpeg compression"); dev->opt[OPT_JPEG].desc = SANE_I18N ("JPEG Image Compression with Q parameter, '0' - no compression"); dev->opt[OPT_JPEG].type = SANE_TYPE_INT; dev->opt[OPT_JPEG].unit = SANE_UNIT_NONE; dev->opt[OPT_JPEG].size = sizeof (SANE_Int); dev->opt[OPT_JPEG].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_JPEG].constraint.range = &(go_jpeg_compression_range); dev->val[OPT_JPEG].w = 0; /* Image Rotation */ dev->opt[OPT_ROTATE].name = "rotate"; dev->opt[OPT_ROTATE].title = SANE_I18N ("Rotate image clockwise"); dev->opt[OPT_ROTATE].desc = SANE_I18N("Request driver to rotate pages by a fixed amount"); dev->opt[OPT_ROTATE].type = SANE_TYPE_INT; dev->opt[OPT_ROTATE].unit = SANE_UNIT_NONE; dev->opt[OPT_ROTATE].size = sizeof (SANE_Int); dev->opt[OPT_ROTATE].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_ROTATE].constraint.range = &(go_rotate_range); dev->val[OPT_ROTATE].w = 0; /* Software Deskew */ dev->opt[OPT_SWDESKEW].name = "swdeskew"; dev->opt[OPT_SWDESKEW].title = SANE_I18N ("Software deskew"); dev->opt[OPT_SWDESKEW].desc = SANE_I18N("Request driver to rotate skewed pages digitally"); dev->opt[OPT_SWDESKEW].type = SANE_TYPE_BOOL; dev->opt[OPT_SWDESKEW].unit = SANE_UNIT_NONE; dev->val[OPT_SWDESKEW].w = SANE_FALSE; /* Software Despeckle */ dev->opt[OPT_SWDESPECK].name = "swdespeck"; dev->opt[OPT_SWDESPECK].title = SANE_I18N ("Software despeckle diameter"); dev->opt[OPT_SWDESPECK].desc = SANE_I18N("Maximum diameter of lone dots to remove from scan"); dev->opt[OPT_SWDESPECK].type = SANE_TYPE_INT; dev->opt[OPT_SWDESPECK].unit = SANE_UNIT_NONE; dev->opt[OPT_SWDESPECK].size = sizeof (SANE_Int); dev->opt[OPT_SWDESPECK].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_SWDESPECK].constraint.range = &(go_swdespeck_range); dev->val[OPT_SWDESPECK].w = 0; /* Software Derotate */ dev->opt[OPT_SWDEROTATE].name = "swderotate"; dev->opt[OPT_SWDEROTATE].title = SANE_I18N ("Software derotate"); dev->opt[OPT_SWDEROTATE].desc = SANE_I18N("Request driver to detect and correct 90 degree image rotation"); dev->opt[OPT_SWDEROTATE].type = SANE_TYPE_BOOL; dev->opt[OPT_SWDEROTATE].unit = SANE_UNIT_NONE; dev->val[OPT_SWDEROTATE].w = SANE_FALSE; /* Software Autocrop*/ dev->opt[OPT_SWCROP].name = "swcrop"; dev->opt[OPT_SWCROP].title = SANE_I18N ("Software automatic cropping"); dev->opt[OPT_SWCROP].desc = SANE_I18N("Request driver to remove border from pages digitally"); dev->opt[OPT_SWCROP].type = SANE_TYPE_BOOL; dev->opt[OPT_SWCROP].unit = SANE_UNIT_NONE; dev->val[OPT_SWCROP].w = SANE_FALSE; /* Software blank page skip */ dev->opt[OPT_SWSKIP].name = "swskip"; dev->opt[OPT_SWSKIP].title = SANE_I18N ("Software blank skip percentage"); dev->opt[OPT_SWSKIP].desc = SANE_I18N("Request driver to discard pages with low numbers of dark pixels"); dev->opt[OPT_SWSKIP].type = SANE_TYPE_FIXED; dev->opt[OPT_SWSKIP].unit = SANE_UNIT_PERCENT; dev->opt[OPT_SWSKIP].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_SWSKIP].constraint.range = &(go_swskip_range); /* Lastly, set the default scan mode. This might change some * values previously set here. */ sane_control_option (dev, OPT_PAPER_SIZE, SANE_ACTION_SET_VALUE, (void *) go_paper_list[default_paper_size_idx], NULL); sane_control_option (dev, OPT_MODE, SANE_ACTION_SET_VALUE, (void *) go_scan_mode_list[0], NULL); DBG (DBG_proc, "kv_init_options: exit\n"); dev->option_set = 1; } SANE_Status kv_control_option (PKV_DEV dev, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { SANE_Status status; SANE_Word cap; SANE_String_Const name; int i; SANE_Word value; DBG (DBG_proc, "sane_control_option: enter, option %s, action %s\n", go_option_name[option], action == SANE_ACTION_GET_VALUE ? "R" : "W"); if (info) { *info = 0; } if (dev->scanning) { return SANE_STATUS_DEVICE_BUSY; } if (option < 0 || option >= OPT_NUM_OPTIONS) { return SANE_STATUS_UNSUPPORTED; } cap = dev->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) { return SANE_STATUS_UNSUPPORTED; } name = dev->opt[option].name; if (!name) { name = "(no name)"; } if (action == SANE_ACTION_GET_VALUE) { switch (option) { /* word options */ case OPT_NUM_OPTS: case OPT_LONGPAPER: case OPT_LENGTHCTL: case OPT_DBLFEED: case OPT_RESOLUTION: case OPT_TL_Y: case OPT_BR_Y: case OPT_TL_X: case OPT_BR_X: case OPT_BRIGHTNESS: case OPT_CONTRAST: case OPT_DUPLEX: case OPT_LANDSCAPE: case OPT_AUTOMATIC_SEPARATION: case OPT_INVERSE: case OPT_MIRROR: case OPT_FEED_TIMEOUT: case OPT_JPEG: case OPT_ROTATE: case OPT_SWDESKEW: case OPT_SWDESPECK: case OPT_SWDEROTATE: case OPT_SWCROP: case OPT_SWSKIP: case OPT_FIT_TO_PAGE: *(SANE_Word *) val = dev->val[option].w; DBG (DBG_error, "opt value = %d\n", *(SANE_Word *) val); return SANE_STATUS_GOOD; /* string options */ case OPT_MODE: case OPT_FEEDER_MODE: case OPT_SCAN_SOURCE: case OPT_MANUALFEED: case OPT_HALFTONE_PATTERN: case OPT_PAPER_SIZE: case OPT_AUTOMATIC_THRESHOLD: case OPT_WHITE_LEVEL: case OPT_NOISE_REDUCTION: case OPT_IMAGE_EMPHASIS: case OPT_GAMMA: case OPT_LAMP: strcpy (val, dev->val[option].s); DBG (DBG_error, "opt value = %s\n", (char *) val); return SANE_STATUS_GOOD; default: return SANE_STATUS_UNSUPPORTED; } } else if (action == SANE_ACTION_SET_VALUE) { if (!SANE_OPTION_IS_SETTABLE (cap)) { DBG (DBG_error, "could not set option %s, not settable\n", go_option_name[option]); return SANE_STATUS_INVAL; } status = sanei_constrain_value (dev->opt + option, val, info); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "could not set option, invalid value\n"); return status; } switch (option) { /* Side-effect options */ case OPT_TL_Y: case OPT_BR_Y: case OPT_RESOLUTION: if (info) { *info |= SANE_INFO_RELOAD_PARAMS; } dev->val[option].w = *(SANE_Word *) val; if (option == OPT_RESOLUTION) { if (round_to_boundry (&(dev->val[option].w), dev->support_info. step_resolution, 100, 600)) { if (info) { *info |= SANE_INFO_INEXACT; } } } else if (option == OPT_TL_Y) { if (dev->val[option].w > dev->val[OPT_BR_Y].w) { dev->val[option].w = dev->val[OPT_BR_Y].w; if (info) { *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT; } } } else { if (dev->val[option].w < dev->val[OPT_TL_Y].w) { dev->val[option].w = dev->val[OPT_TL_Y].w; if (info) { *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT; } } } DBG (DBG_error, "option %s, input = %d, value = %d\n", go_option_name[option], (*(SANE_Word *) val), dev->val[option].w); return SANE_STATUS_GOOD; /* The length of X must be rounded (up). */ case OPT_TL_X: case OPT_BR_X: { SANE_Word xr = dev->val[OPT_RESOLUTION].w; SANE_Word tl_x = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_X].w)) * xr; SANE_Word br_x = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_X].w)) * xr; value = mmToIlu (SANE_UNFIX (*(SANE_Word *) val)) * xr; /* XR * W */ if (option == OPT_TL_X) { SANE_Word max = KV_PIXEL_MAX * xr - KV_PIXEL_ROUND; if (br_x < max) max = br_x; if (round_to_boundry (&value, KV_PIXEL_ROUND, 0, max)) { if (info) { *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT; } } } else { if (round_to_boundry (&value, KV_PIXEL_ROUND, tl_x, KV_PIXEL_MAX * xr)) { if (info) { *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT; } } } dev->val[option].w = SANE_FIX (iluToMm ((double) value / xr)); if (info) { *info |= SANE_INFO_RELOAD_PARAMS; } DBG (DBG_error, "option %s, input = %d, value = %d\n", go_option_name[option], (*(SANE_Word *) val), dev->val[option].w); return SANE_STATUS_GOOD; } case OPT_LANDSCAPE: dev->val[option].w = *(SANE_Word *) val; if (info) { *info |= SANE_INFO_RELOAD_PARAMS; } return SANE_STATUS_GOOD; /* Side-effect free options */ case OPT_CONTRAST: case OPT_BRIGHTNESS: case OPT_DUPLEX: case OPT_LONGPAPER: case OPT_LENGTHCTL: case OPT_DBLFEED: case OPT_INVERSE: case OPT_MIRROR: case OPT_AUTOMATIC_SEPARATION: case OPT_JPEG: case OPT_ROTATE: case OPT_SWDESKEW: case OPT_SWDESPECK: case OPT_SWDEROTATE: case OPT_SWCROP: case OPT_SWSKIP: case OPT_FIT_TO_PAGE: dev->val[option].w = *(SANE_Word *) val; return SANE_STATUS_GOOD; case OPT_FEED_TIMEOUT: dev->val[option].w = *(SANE_Word *) val; return CMD_set_timeout (dev, *(SANE_Word *) val); /* String mode */ case OPT_SCAN_SOURCE: case OPT_WHITE_LEVEL: case OPT_NOISE_REDUCTION: case OPT_IMAGE_EMPHASIS: case OPT_GAMMA: case OPT_LAMP: case OPT_HALFTONE_PATTERN: case OPT_FEEDER_MODE: if (strcmp (dev->val[option].s, val) == 0) return SANE_STATUS_GOOD; free (dev->val[option].s); dev->val[option].s = (SANE_String) strdup (val); if (option == OPT_FEEDER_MODE && get_string_list_index (go_feeder_mode_list, dev->val[option].s) == 1) /* continuous mode */ { free (dev->val[OPT_SCAN_SOURCE].s); dev->val[OPT_SCAN_SOURCE].s = strdup (go_scan_source_list[0]); dev->opt[OPT_LONGPAPER].cap &= ~SANE_CAP_INACTIVE; if (info) *info |= SANE_INFO_RELOAD_OPTIONS; } else { dev->opt[OPT_LONGPAPER].cap |= SANE_CAP_INACTIVE; if (info) *info |= SANE_INFO_RELOAD_OPTIONS; } if (option == OPT_SCAN_SOURCE && get_string_list_index (go_scan_source_list, dev->val[option].s) == 1) /* flatbed */ { free (dev->val[OPT_FEEDER_MODE].s); dev->val[OPT_FEEDER_MODE].s = strdup (go_feeder_mode_list[0]); } return SANE_STATUS_GOOD; case OPT_MODE: if (strcmp (dev->val[option].s, val) == 0) return SANE_STATUS_GOOD; free (dev->val[OPT_MODE].s); dev->val[OPT_MODE].s = (SANE_String) strdup (val); /* Set default options for the scan modes. */ dev->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_AUTOMATIC_THRESHOLD].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_AUTOMATIC_SEPARATION].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_GAMMA].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_INVERSE].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_JPEG].cap &= ~SANE_CAP_INACTIVE; if (strcmp (dev->val[OPT_MODE].s, go_scan_mode_list[0]) == 0) /* binary */ { dev->opt[OPT_AUTOMATIC_THRESHOLD].cap &= ~SANE_CAP_INACTIVE; dev->opt[OPT_INVERSE].cap &= ~SANE_CAP_INACTIVE; dev->opt[OPT_JPEG].cap |= SANE_CAP_INACTIVE; } else if (strcmp (dev->val[OPT_MODE].s, go_scan_mode_list[1]) == 0) /* halftone */ { dev->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE; dev->opt[OPT_AUTOMATIC_SEPARATION].cap &= ~SANE_CAP_INACTIVE; dev->opt[OPT_GAMMA].cap &= ~SANE_CAP_INACTIVE; dev->opt[OPT_INVERSE].cap &= ~SANE_CAP_INACTIVE; dev->opt[OPT_JPEG].cap |= SANE_CAP_INACTIVE; } else if (strcmp (dev->val[OPT_MODE].s, go_scan_mode_list[2]) == 0) /* grayscale */ { dev->opt[OPT_GAMMA].cap &= ~SANE_CAP_INACTIVE; } if (info) { *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; } return SANE_STATUS_GOOD; case OPT_MANUALFEED: if (strcmp (dev->val[option].s, val) == 0) return SANE_STATUS_GOOD; free (dev->val[option].s); dev->val[option].s = (SANE_String) strdup (val); if (strcmp (dev->val[option].s, go_manual_feed_list[0]) == 0) /* off */ dev->opt[OPT_FEED_TIMEOUT].cap |= SANE_CAP_INACTIVE; else dev->opt[OPT_FEED_TIMEOUT].cap &= ~SANE_CAP_INACTIVE; if (info) *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_PAPER_SIZE: if (strcmp (dev->val[option].s, val) == 0) return SANE_STATUS_GOOD; free (dev->val[OPT_PAPER_SIZE].s); dev->val[OPT_PAPER_SIZE].s = (SANE_Char *) strdup (val); i = get_string_list_index (go_paper_list, dev->val[OPT_PAPER_SIZE].s); if (i == 0) { /*user def */ dev->opt[OPT_TL_X].cap &= dev->opt[OPT_TL_Y].cap &= dev->opt[OPT_BR_X].cap &= dev->opt[OPT_BR_Y].cap &= ~SANE_CAP_INACTIVE; dev->opt[OPT_LANDSCAPE].cap |= SANE_CAP_INACTIVE; dev->val[OPT_LANDSCAPE].w = 0; } else { dev->opt[OPT_TL_X].cap |= dev->opt[OPT_TL_Y].cap |= dev->opt[OPT_BR_X].cap |= dev->opt[OPT_BR_Y].cap |= SANE_CAP_INACTIVE; if (i == 4 || i == 5 || i == 7) { /*A5, A6 or B6 */ dev->opt[OPT_LANDSCAPE].cap &= ~SANE_CAP_INACTIVE; } else { dev->opt[OPT_LANDSCAPE].cap |= SANE_CAP_INACTIVE; dev->val[OPT_LANDSCAPE].w = 0; } } if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case OPT_AUTOMATIC_THRESHOLD: if (strcmp (dev->val[option].s, val) == 0) return SANE_STATUS_GOOD; free (dev->val[option].s); dev->val[option].s = (SANE_Char *) strdup (val); /* If the threshold is not set to none, some option must * disappear. */ dev->opt[OPT_WHITE_LEVEL].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_NOISE_REDUCTION].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_IMAGE_EMPHASIS].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_AUTOMATIC_SEPARATION].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; if (strcmp (val, go_automatic_threshold_list[0]) == 0) { dev->opt[OPT_WHITE_LEVEL].cap &= ~SANE_CAP_INACTIVE; dev->opt[OPT_NOISE_REDUCTION].cap &= ~SANE_CAP_INACTIVE; dev->opt[OPT_IMAGE_EMPHASIS].cap &= ~SANE_CAP_INACTIVE; dev->opt[OPT_AUTOMATIC_SEPARATION].cap &= ~SANE_CAP_INACTIVE; if (strcmp (dev->val[OPT_MODE].s, go_scan_mode_list[1]) == 0) { dev->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE; } } if (info) { *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; } return SANE_STATUS_GOOD; default: return SANE_STATUS_INVAL; } } DBG (DBG_proc, "sane_control_option: exit, bad\n"); return SANE_STATUS_UNSUPPORTED; } /* Display a buffer in the log. */ void hexdump (int level, const 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.3d:", i); ptr += 4; } sprintf (ptr, " %2.2x", *p); ptr += 3; } *ptr = '\0'; DBG (level, "%s\n", line); } /* Set window data */ void kv_set_window_data (PKV_DEV dev, KV_SCAN_MODE scan_mode, int side, unsigned char *windowdata) { int paper = go_paper_val[get_string_list_index (go_paper_list, dev->val[OPT_PAPER_SIZE]. s)]; /* Page side */ windowdata[0] = side; /* X and Y resolution */ Ito16 (dev->val[OPT_RESOLUTION].w, &windowdata[2]); Ito16 (dev->val[OPT_RESOLUTION].w, &windowdata[4]); /* Width and length */ if (paper == 0) { /* Non-standard document */ int x_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_X].w)); int y_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_Y].w)); int x_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_X].w)); int y_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_Y].w)); int width = x_br - x_tl; int length = y_br - y_tl; /* Upper Left (X,Y) */ Ito32 (x_tl, &windowdata[6]); Ito32 (y_tl, &windowdata[10]); Ito32 (width, &windowdata[14]); Ito32 (length, &windowdata[18]); Ito32 (width, &windowdata[48]); /* device specific */ Ito32 (length, &windowdata[52]); /* device specific */ } /* Brightness */ windowdata[22] = 255 - GET_OPT_VAL_W (dev, OPT_BRIGHTNESS); windowdata[23] = windowdata[22]; /* threshold, same as brightness. */ /* Contrast */ windowdata[24] = GET_OPT_VAL_W (dev, OPT_CONTRAST); /* Image Composition */ windowdata[25] = (unsigned char) scan_mode; /* Depth */ windowdata[26] = kv_get_depth (scan_mode); /* Halftone pattern. */ if (scan_mode == SM_DITHER) { windowdata[28] = GET_OPT_VAL_L (dev, OPT_HALFTONE_PATTERN, halftone_pattern); } /* Inverse */ if (scan_mode == SM_BINARY || scan_mode == SM_DITHER) { windowdata[29] = GET_OPT_VAL_W (dev, OPT_INVERSE); } /* Bit ordering */ windowdata[31] = 1; /*Compression Type */ if (!(dev->opt[OPT_JPEG].cap & SANE_CAP_INACTIVE) && GET_OPT_VAL_W (dev, OPT_JPEG)) { windowdata[32] = 0x81; /*jpeg */ /*Compression Argument */ windowdata[33] = GET_OPT_VAL_W (dev, OPT_JPEG); } /* Gamma */ if (scan_mode == SM_DITHER || scan_mode == SM_GRAYSCALE) { windowdata[44] = GET_OPT_VAL_L (dev, OPT_GAMMA, gamma); } /* Feeder mode */ windowdata[57] = GET_OPT_VAL_L (dev, OPT_FEEDER_MODE, feeder_mode); /* Stop skew -- disabled */ windowdata[41] = 0; /* Scan source */ if (GET_OPT_VAL_L (dev, OPT_SCAN_SOURCE, scan_source)) { /* flatbed */ windowdata[41] |= 0x80; } else { windowdata[41] &= 0x7f; } /* Paper size */ windowdata[47] = paper; if (paper) /* Standard Document */ windowdata[47] |= 1 << 7; /* Long paper */ if (GET_OPT_VAL_W (dev, OPT_LONGPAPER)) { windowdata[47] |= 0x20; } /* Length control */ if (GET_OPT_VAL_W (dev, OPT_LENGTHCTL)) { windowdata[47] |= 0x40; } /* Landscape */ if (GET_OPT_VAL_W (dev, OPT_LANDSCAPE)) { windowdata[47] |= 1 << 4; } /* Double feed */ if (GET_OPT_VAL_W (dev, OPT_DBLFEED)) { windowdata[56] = 0x10; } /* Fit to page */ if (GET_OPT_VAL_W (dev, OPT_FIT_TO_PAGE)) { windowdata[56] |= 1 << 2; } /* Manual feed */ windowdata[62] = GET_OPT_VAL_L (dev, OPT_MANUALFEED, manual_feed) << 6; /* Mirror image */ if (GET_OPT_VAL_W (dev, OPT_MIRROR)) { windowdata[42] = 0x80; } /* Image emphasis */ windowdata[43] = GET_OPT_VAL_L (dev, OPT_IMAGE_EMPHASIS, image_emphasis); /* White level */ windowdata[60] = GET_OPT_VAL_L (dev, OPT_WHITE_LEVEL, white_level); if (scan_mode == SM_BINARY || scan_mode == SM_DITHER) { /* Noise reduction */ windowdata[61] = GET_OPT_VAL_L (dev, OPT_NOISE_REDUCTION, noise_reduction); /* Automatic separation */ if (scan_mode == SM_DITHER && GET_OPT_VAL_W (dev, OPT_AUTOMATIC_SEPARATION)) { windowdata[59] = 0x80; } } /* Automatic threshold. Must be last because it may override * some previous options. */ if (scan_mode == SM_BINARY) { windowdata[58] = GET_OPT_VAL_L (dev, OPT_AUTOMATIC_THRESHOLD, automatic_threshold); } if (windowdata[58] != 0) { /* Automatic threshold is enabled. */ windowdata[22] = 0; /* brightness. */ windowdata[23] = 0; /* threshold, same as brightness. */ windowdata[24] = 0; /* contrast */ windowdata[27] = windowdata[28] = 0; /* Halftone pattern. */ windowdata[43] = 0; /* Image emphasis */ windowdata[59] = 0; /* Automatic separation */ windowdata[60] = 0; /* White level */ windowdata[61] = 0; /* Noise reduction */ } /* lamp -- color dropout */ windowdata[45] = GET_OPT_VAL_L (dev, OPT_LAMP, lamp) << 4; /*Stop Mode: After 1 page */ windowdata[63] = 1; } backends-1.3.0/backend/kvs1025_usb.c000066400000000000000000000217111456256263500170240ustar00rootroot00000000000000/* Copyright (C) 2008, Panasonic Russia Ltd. */ /* sane - Scanner Access Now Easy. Panasonic KV-S1020C / KV-S1025C USB scanners. */ #define DEBUG_DECLARE_ONLY #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_config.h" #include "../include/lassert.h" #include "kvs1025.h" #include "kvs1025_low.h" #include "kvs1025_usb.h" #include "kvs1025_cmds.h" #include "../include/sane/sanei_debug.h" extern PKV_DEV g_devices; /* Chain of devices */ extern const SANE_Device **g_devlist; /* static functions */ /* Attach USB scanner */ static SANE_Status attach_scanner_usb (const char *device_name) { PKV_DEV dev; SANE_Word vendor, product; DBG (DBG_error, "attaching USB scanner %s\n", device_name); sanei_usb_get_vendor_product_byname(device_name,&vendor,&product); dev = (PKV_DEV) malloc (sizeof (KV_DEV)); if (dev == NULL) return SANE_STATUS_NO_MEM; memset (dev, 0, sizeof (KV_DEV)); dev->bus_mode = KV_USB_BUS; dev->usb_fd = -1; dev->scsi_fd = -1; strcpy (dev->device_name, device_name); dev->buffer0 = (unsigned char *) malloc (SCSI_BUFFER_SIZE + 12); dev->buffer = dev->buffer0 + 12; if (dev->buffer0 == NULL) { free (dev); return SANE_STATUS_NO_MEM; } dev->scsi_type = 6; strcpy (dev->scsi_type_str, "ADF Scanner"); strcpy (dev->scsi_vendor, "Panasonic"); strcpy (dev->scsi_product, product == (int) KV_S1020C ? "KV-S1020C" : product == (int) KV_S1025C ? "KV-S1025C" : product == (int) KV_S1045C ? "KV-S1045C" : "KV-S10xxC"); strcpy (dev->scsi_version, "1.00"); /* Set SANE_Device */ dev->sane.name = dev->device_name; dev->sane.vendor = dev->scsi_vendor; dev->sane.model = dev->scsi_product; dev->sane.type = dev->scsi_type_str; /* Add into g_devices chain */ dev->next = g_devices; g_devices = dev; return SANE_STATUS_GOOD; } /* Get all supported scanners, and store into g_devlist */ SANE_Status kv_usb_enum_devices (void) { int cnt = 0; int i; PKV_DEV pd; char usb_str[18]; DBG (DBG_proc, "kv_usb_enum_devices: enter\n"); sanei_usb_init(); sprintf(usb_str,"usb %#04x %#04x",VENDOR_ID,KV_S1020C); sanei_usb_attach_matching_devices(usb_str, attach_scanner_usb); sprintf(usb_str,"usb %#04x %#04x",VENDOR_ID,KV_S1025C); sanei_usb_attach_matching_devices(usb_str, attach_scanner_usb); sprintf(usb_str,"usb %#04x %#04x",VENDOR_ID,KV_S1045C); sanei_usb_attach_matching_devices(usb_str, attach_scanner_usb); for (pd = g_devices; pd; pd=pd->next) { cnt++; } g_devlist = (const SANE_Device **) malloc (sizeof (SANE_Device *) * (cnt + 1)); if (g_devlist == NULL) { DBG (DBG_proc, "kv_usb_enum_devices: leave on error " " --out of memory\n"); return SANE_STATUS_NO_MEM; } pd = g_devices; for (i = 0; i < cnt; i++) { g_devlist[i] = (const SANE_Device *) &pd->sane; pd = pd->next; } g_devlist[cnt] = 0; DBG (DBG_proc, "kv_usb_enum_devices: leave with %d devices.\n", cnt); return SANE_STATUS_GOOD; } /* Check if device is already open */ SANE_Bool kv_usb_already_open (PKV_DEV dev) { return (dev->usb_fd > -1); } /* Open an USB device */ SANE_Status kv_usb_open (PKV_DEV dev) { SANE_Status ret; DBG (DBG_proc, "kv_usb_open: enter\n"); if (kv_usb_already_open(dev)) { DBG (DBG_proc, "kv_usb_open: leave -- already open\n"); return SANE_STATUS_GOOD; } ret = sanei_usb_open (dev->device_name, &(dev->usb_fd)); if (ret) { DBG (DBG_error, "kv_usb_open: leave -- cannot open device\n"); return SANE_STATUS_IO_ERROR; } sanei_usb_clear_halt (dev->usb_fd); DBG (DBG_proc, "kv_usb_open: leave\n"); return SANE_STATUS_GOOD; } /* Close an USB device */ void kv_usb_close (PKV_DEV dev) { DBG (DBG_proc, "kv_usb_close: enter\n"); if (kv_usb_already_open(dev)) { sanei_usb_close(dev->usb_fd); dev->usb_fd = -1; } DBG (DBG_proc, "kv_usb_close: leave\n"); } /* Clean up the USB bus and release all resources allocated to devices */ void kv_usb_cleanup (void) { } /* Send command via USB, and get response data */ SANE_Status kv_usb_escape (PKV_DEV dev, PKV_CMD_HEADER header, unsigned char *status_byte) { int got_response = 0; size_t len; unsigned char cmd_buff[24]; memset (cmd_buff, 0, 24); cmd_buff[3] = 0x18; /* container length */ cmd_buff[5] = 1; /* container type: command block */ cmd_buff[6] = 0x90; /* code */ if (!kv_usb_already_open(dev)) { DBG (DBG_error, "kv_usb_escape: error, device not open.\n"); return SANE_STATUS_IO_ERROR; } memcpy (cmd_buff + 12, header->cdb, header->cdb_size); /* change timeout */ sanei_usb_set_timeout(KV_CMD_TIMEOUT); /* Send command */ len = 24; if (sanei_usb_write_bulk (dev->usb_fd, (SANE_Byte *) cmd_buff, &len)) { DBG (DBG_error, "usb_bulk_write: Error writing command.\n"); hexdump (DBG_error, "cmd block", cmd_buff, 24); return SANE_STATUS_IO_ERROR; } /* Send / Read data */ if (header->direction == KV_CMD_IN) { size_t size = header->data_size + 12; size_t size_read = size; unsigned char *data = ((unsigned char *) header->data) - 12; SANE_Status ret; ret = sanei_usb_read_bulk (dev->usb_fd, (SANE_Byte *) data, &size_read); /*empty read is ok?*/ if (ret == SANE_STATUS_EOF){ sanei_usb_clear_halt (dev->usb_fd); ret = SANE_STATUS_GOOD; } if (ret) { sanei_usb_clear_halt (dev->usb_fd); DBG (DBG_error, "usb_bulk_read: Error reading data.\n"); return SANE_STATUS_IO_ERROR; } if (size_read != size) { DBG (DBG_shortread, "usb_bulk_read: Warning - short read\n"); DBG (DBG_shortread, "usb_bulk_read: bytes to read = %lu\n", (unsigned long)size); DBG (DBG_shortread, "usb_bulk_read: bytes actual read = %lu\n", (unsigned long)size_read); /*hexdump (DBG_shortread, "data", data, size_read); */ } } if (header->direction == KV_CMD_OUT) { size_t size = header->data_size + 12; size_t size_written = size; unsigned char *data = ((unsigned char *) header->data) - 12; SANE_Status ret; memset (data, 0, 12); Ito32 (size, data); data[5] = 0x02; /* container type: data block */ data[6] = 0xb0; /* code */ ret = sanei_usb_write_bulk (dev->usb_fd, (SANE_Byte *) data, &size_written); /*empty write is ok?*/ if (ret == SANE_STATUS_EOF){ sanei_usb_clear_halt (dev->usb_fd); ret = SANE_STATUS_GOOD; } if (ret) { sanei_usb_clear_halt (dev->usb_fd); DBG (DBG_error, "usb_bulk_write: Error writing data.\n"); return SANE_STATUS_IO_ERROR; } if (size_written != size) { DBG (DBG_shortread, "usb_bulk_write: Warning - short written\n"); DBG (DBG_shortread, "usb_bulk_write: bytes to write = %lu\n", (unsigned long)size); DBG (DBG_shortread, "usb_bulk_write: bytes actual written = %lu\n", (unsigned long)size_written); hexdump (DBG_shortread, "data", data, size_written); } } /* Get response */ if (!got_response) { SANE_Status ret; size_t len = 16; ret = sanei_usb_read_bulk (dev->usb_fd, (SANE_Byte *) cmd_buff, &len); if (ret || len != 16) { DBG (DBG_error, "usb_bulk_read: Error reading response." " read %lu bytes\n", (unsigned long)len); sanei_usb_clear_halt (dev->usb_fd); return SANE_STATUS_IO_ERROR; } } if (cmd_buff[5] != 3) { DBG (DBG_error, "usb_bulk_read: Invalid response block.\n"); hexdump (DBG_error, "response", cmd_buff, 16); return SANE_STATUS_IO_ERROR; } *status_byte = cmd_buff[15] & 0x3E; return SANE_STATUS_GOOD; } /* Send command via USB, and request sense on CHECK CONDITION status */ SANE_Status kv_usb_send_command (PKV_DEV dev, PKV_CMD_HEADER header, PKV_CMD_RESPONSE response) { unsigned char status = 0; SANE_Status s; memset (response, 0, sizeof (KV_CMD_RESPONSE)); response->status = KV_FAILED; s = kv_usb_escape (dev, header, &status); if (s) { status = 0x02; } if (status == 0x02) { /* check condition */ /* request sense */ KV_CMD_HEADER hdr; memset (&hdr, 0, sizeof (hdr)); hdr.direction = KV_CMD_IN; hdr.cdb[0] = SCSI_REQUEST_SENSE; hdr.cdb[4] = 0x12; hdr.cdb_size = 6; hdr.data_size = 0x12; hdr.data = &response->sense; if (kv_usb_escape (dev, &hdr, &status) != 0) return SANE_STATUS_IO_ERROR; hexdump (DBG_error, "sense data", (unsigned char *) &response->sense, 0x12); response->status = KV_CHK_CONDITION; } else { response->status = KV_SUCCESS; } return SANE_STATUS_GOOD; } backends-1.3.0/backend/kvs1025_usb.h000066400000000000000000000012171456256263500170300ustar00rootroot00000000000000/* Copyright (C) 2008, Panasonic Russia Ltd. */ /* sane - Scanner Access Now Easy. Panasonic KV-S1020C / KV-S1025C USB scanners. */ #ifndef __KVS1025_USB_H #define __KVS1025_USB_H #include "kvs1025_cmds.h" SANE_Status kv_usb_enum_devices (void); SANE_Status kv_usb_open (PKV_DEV dev); SANE_Bool kv_usb_already_open (PKV_DEV dev); void kv_usb_close (PKV_DEV dev); void kv_usb_cleanup (void); SANE_Status kv_usb_escape (PKV_DEV dev, PKV_CMD_HEADER header, unsigned char *status_byte); SANE_Status kv_usb_send_command (PKV_DEV dev, PKV_CMD_HEADER header, PKV_CMD_RESPONSE response); #endif /* #ifndef __KVS1025_USB_H */ backends-1.3.0/backend/kvs20xx.c000066400000000000000000000272121456256263500163670ustar00rootroot00000000000000/* Copyright (C) 2008, Panasonic Russia Ltd. Copyright (C) 2010, m. allan noah */ /* Panasonic KV-S20xx USB-SCSI scanners. */ #define DEBUG_NOT_STATIC #define BUILD 2 #include "../include/sane/config.h" #include #include #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_scsi.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_config.h" #include "../include/lassert.h" #include "kvs20xx.h" #include "kvs20xx_cmd.h" struct known_device { const SANE_Int id; const SANE_Device scanner; }; static const struct known_device known_devices[] = { { KV_S2025C, { "", "MATSHITA", "KV-S2025C", "sheetfed scanner" }, }, { KV_S2045C, { "", "MATSHITA", "KV-S2045C", "sheetfed scanner" }, }, { KV_S2026C, { "", "MATSHITA", "KV-S2026C", "sheetfed scanner" }, }, { KV_S2046C, { "", "MATSHITA", "KV-S2046C", "sheetfed scanner" }, }, { KV_S2028C, { "", "MATSHITA", "KV-S2028C", "sheetfed scanner" }, }, { KV_S2048C, { "", "MATSHITA", "KV-S2048C", "sheetfed scanner" }, }, }; SANE_Status sane_init (SANE_Int __sane_unused__ * version_code, SANE_Auth_Callback __sane_unused__ authorize) { DBG_INIT (); DBG (DBG_INFO, "This is panasonic kvs20xx driver\n"); *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); /* Initialize USB */ sanei_usb_init (); return SANE_STATUS_GOOD; } /* * List of available devices, allocated by sane_get_devices, released * by sane_exit() */ static SANE_Device **devlist = NULL; static unsigned curr_scan_dev = 0; void sane_exit (void) { if (devlist) { int i; for (i = 0; devlist[i]; i++) { free ((void *) devlist[i]->name); free ((void *) devlist[i]); } free ((void *) devlist); devlist = NULL; } } static SANE_Status attach (SANE_String_Const devname) { int i = 0; if (devlist) { for (; devlist[i]; i++); devlist = realloc (devlist, sizeof (SANE_Device *) * (i + 1)); if (!devlist) return SANE_STATUS_NO_MEM; } else { devlist = malloc (sizeof (SANE_Device *) * 2); if (!devlist) return SANE_STATUS_NO_MEM; } devlist[i] = malloc (sizeof (SANE_Device)); if (!devlist[i]) return SANE_STATUS_NO_MEM; memcpy (devlist[i], &known_devices[curr_scan_dev].scanner, sizeof (SANE_Device)); devlist[i]->name = strdup (devname); /* terminate device list with NULL entry: */ devlist[i + 1] = 0; DBG (DBG_INFO, "%s device attached\n", devname); return SANE_STATUS_GOOD; } /* Get device list */ SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only) { if (devlist) { int i; for (i = 0; devlist[i]; i++) { free ((void *) devlist[i]->name); free ((void *) devlist[i]); } free ((void *) devlist); devlist = NULL; } for (curr_scan_dev = 0; curr_scan_dev < sizeof (known_devices) / sizeof (known_devices[0]); curr_scan_dev++) { sanei_usb_find_devices (PANASONIC_ID, known_devices[curr_scan_dev].id, attach); } for (curr_scan_dev = 0; curr_scan_dev < sizeof (known_devices) / sizeof (known_devices[0]); curr_scan_dev++) { sanei_scsi_find_devices (known_devices[curr_scan_dev].scanner.vendor, known_devices[curr_scan_dev].scanner.model, NULL, -1, -1, -1, -1, attach); } if(device_list) *device_list = (const SANE_Device **) devlist; return SANE_STATUS_GOOD; } /* Open device, return the device handle */ SANE_Status sane_open (SANE_String_Const devname, SANE_Handle * handle) { unsigned i, j, id = 0; struct scanner *s; SANE_Int h, bus; SANE_Status st; if (!devlist) { st = sane_get_devices (NULL, 0); if (st) return st; } for (i = 0; devlist[i]; i++) { if (!strcmp (devlist[i]->name, devname)) break; } if (!devlist[i]) return SANE_STATUS_INVAL; for (j = 0; j < sizeof (known_devices) / sizeof (known_devices[0]); j++) { if (!strcmp (devlist[i]->model, known_devices[j].scanner.model)) { id = known_devices[j].id; break; } } st = sanei_usb_open (devname, &h); if (st == SANE_STATUS_ACCESS_DENIED) return st; if (st) { st = sanei_scsi_open (devname, &h, kvs20xx_sense_handler, NULL); if (st) { return st; } bus = SCSI; } else { bus = USB; st = sanei_usb_claim_interface (h, 0); if (st) { sanei_usb_close (h); return st; } } s = malloc (sizeof (struct scanner)); if (!s) return SANE_STATUS_NO_MEM; memset (s, 0, sizeof (struct scanner)); s->buffer = malloc (MAX_READ_DATA_SIZE + BULK_HEADER_SIZE); if (!s->buffer) return SANE_STATUS_NO_MEM; s->file = h; s->bus = bus; s->id = id; kvs20xx_init_options (s); *handle = s; for (i = 0; i < 3; i++) { st = kvs20xx_test_unit_ready (s); if (st) { if (s->bus == SCSI) { sanei_scsi_close (s->file); st = sanei_scsi_open (devname, &h, kvs20xx_sense_handler, NULL); if (st) return st; } else { sanei_usb_release_interface (s->file, 0); sanei_usb_close (s->file); st = sanei_usb_open (devname, &h); if (st) return st; st = sanei_usb_claim_interface (h, 0); if (st) { sanei_usb_close (h); return st; } } s->file = h; } else break; } if (i == 3) return SANE_STATUS_DEVICE_BUSY; st = kvs20xx_set_timeout (s, s->val[FEED_TIMEOUT].w); if (st) { sane_close (s); return st; } return SANE_STATUS_GOOD; } /* Close device */ void sane_close (SANE_Handle handle) { struct scanner *s = (struct scanner *) handle; int i; if (s->bus == USB) { sanei_usb_release_interface (s->file, 0); sanei_usb_close (s->file); } else sanei_scsi_close (s->file); for (i = 1; i < NUM_OPTIONS; i++) { if (s->opt[i].type == SANE_TYPE_STRING && s->val[i].s) free (s->val[i].s); } if (s->data) free (s->data); free (s->buffer); free (s); } /* Get option descriptor */ const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { struct scanner *s = handle; if ((unsigned) option >= NUM_OPTIONS || option < 0) return NULL; return s->opt + option; } static SANE_Status wait_document (struct scanner *s) { SANE_Status st; int i; if (!strcmp ("off", s->val[MANUALFEED].s)) return kvs20xx_document_exist (s); for (i = 0; i < s->val[FEED_TIMEOUT].w; i++) { st = kvs20xx_document_exist (s); if (st != SANE_STATUS_NO_DOCS) return st; sleep (1); } return SANE_STATUS_NO_DOCS; } /* Start scanning */ SANE_Status sane_start (SANE_Handle handle) { struct scanner *s = (struct scanner *) handle; SANE_Status st; int duplex = s->val[DUPLEX].w; if (!s->scanning) { unsigned dummy_length; st = kvs20xx_test_unit_ready (s); if (st) return st; st = wait_document (s); if (st) return st; st = kvs20xx_reset_window (s); if (st) return st; st = kvs20xx_set_window (s, SIDE_FRONT); if (st) return st; if (duplex) { st = kvs20xx_set_window (s, SIDE_BACK); if (st) return st; } st = kvs20xx_scan (s); if (st) return st; st = kvs20xx_read_picture_element (s, SIDE_FRONT, &s->params); if (st) return st; if (duplex) { st = get_adjust_data (s, &dummy_length); if (st) return st; } else { dummy_length = 0; } s->scanning = 1; s->page = 0; s->read = 0; s->side = SIDE_FRONT; sane_get_parameters (s, NULL); s->saved_dummy_size = s->dummy_size = dummy_length ? (dummy_length * s->val[RESOLUTION].w / 1200 - 1) * s->params.bytes_per_line : 0; s->side_size = s->params.lines * s->params.bytes_per_line; s->data = realloc (s->data, duplex ? s->side_size * 2 : s->side_size); if (!s->data) { s->scanning = 0; return SANE_STATUS_NO_MEM; } } if (duplex) { unsigned side = SIDE_FRONT; unsigned read, mx; if (s->side == SIDE_FRONT && s->read == s->side_size - s->dummy_size) { s->side = SIDE_BACK; s->read = s->dummy_size; s->dummy_size = 0; return SANE_STATUS_GOOD; } s->read = 0; s->dummy_size = s->saved_dummy_size; s->side = SIDE_FRONT; st = kvs20xx_document_exist (s); if (st) return st; for (mx = s->side_size * 2; !st; mx -= read, side ^= SIDE_BACK) st = kvs20xx_read_image_data (s, s->page, side, &s->data[s->side_size * 2 - mx], mx, &read); } else { unsigned read, mx; s->read = 0; st = kvs20xx_document_exist (s); if (st) return st; DBG (DBG_INFO, "start: %d\n", s->page); for (mx = s->side_size; !st; mx -= read) st = kvs20xx_read_image_data (s, s->page, SIDE_FRONT, &s->data[s->side_size - mx], mx, &read); } if (st && st != SANE_STATUS_EOF) { s->scanning = 0; return st; } s->page++; return SANE_STATUS_GOOD; } inline static void memcpy24 (u8 * dest, u8 * src, unsigned size, unsigned ls) { unsigned i; for (i = 0; i < size; i++) { dest[i * 3] = src[i]; dest[i * 3 + 1] = src[i + ls]; dest[i * 3 + 2] = src[i + 2 * ls]; } } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { struct scanner *s = (struct scanner *) handle; int duplex = s->val[DUPLEX].w; int color = !strcmp (s->val[MODE].s, SANE_VALUE_SCAN_MODE_COLOR); int rest = s->side_size - s->read - s->dummy_size; *len = 0; if (!s->scanning || !rest) { if (strcmp (s->val[FEEDER_MODE].s, SANE_I18N ("continuous"))) { if (!duplex || s->side == SIDE_BACK) s->scanning = 0; } return SANE_STATUS_EOF; } *len = max_len < rest ? max_len : rest; if (duplex && (s->id == KV_S2025C || s->id == KV_S2026C || s->id == KV_S2028C)) { if (color) { unsigned ls = s->params.bytes_per_line; unsigned i, a = s->side == SIDE_FRONT ? 0 : ls / 3; u8 *data; *len = (*len / ls) * ls; for (i = 0, data = s->data + s->read * 2 + a; i < *len / ls; buf += ls, data += 2 * ls, i++) memcpy24 (buf, data, ls / 3, ls * 2 / 3); } else { unsigned ls = s->params.bytes_per_line; unsigned i = s->side == SIDE_FRONT ? 0 : ls; unsigned head = ls - (s->read % ls); unsigned tail = (*len - head) % ls; unsigned lines = (*len - head) / ls; u8 *data = s->data + (s->read / ls) * ls * 2 + i + s->read % ls; assert (data <= s->data + s->side_size * 2); memcpy (buf, data, head); for (i = 0, buf += head, data += head + (head ? ls : 0); i < lines; buf += ls, data += ls * 2, i++) { assert (data <= s->data + s->side_size * 2); memcpy (buf, data, ls); } assert ((data <= s->data + s->side_size * 2) || !tail); memcpy (buf, data, tail); } s->read += *len; } else { if (color) { unsigned i, ls = s->params.bytes_per_line; u8 *data = s->data + s->read; *len = (*len / ls) * ls; for (i = 0; i < *len / ls; buf += ls, data += ls, i++) memcpy24 (buf, data, ls / 3, ls / 3); } else { memcpy (buf, s->data + s->read, *len); } s->read += *len; } return SANE_STATUS_GOOD; } void sane_cancel (SANE_Handle handle) { struct scanner *s = (struct scanner *) handle; s->scanning = 0; } SANE_Status sane_set_io_mode (SANE_Handle __sane_unused__ h, SANE_Bool __sane_unused__ m) { return SANE_STATUS_UNSUPPORTED; } SANE_Status sane_get_select_fd (SANE_Handle __sane_unused__ h, SANE_Int __sane_unused__ * fd) { return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/kvs20xx.h000066400000000000000000000107151456256263500163740ustar00rootroot00000000000000#ifndef __KVS20XX_H #define __KVS20XX_H /* Copyright (C) 2008, Panasonic Russia Ltd. Copyright (C) 2010, m. allan noah */ /* Panasonic KV-S20xx USB-SCSI scanners. */ #include #undef BACKEND_NAME #define BACKEND_NAME kvs20xx #define DBG_ERR 1 #define DBG_WARN 2 #define DBG_MSG 3 #define DBG_INFO 4 #define DBG_DBG 5 #define PANASONIC_ID 0x04da #define KV_S2025C 0xdeadbeef #define KV_S2045C 0xdeadbeee #define KV_S2026C 0x1000 #define KV_S2046C 0x1001 #define KV_S2048C 0x1009 #define KV_S2028C 0x100a #define USB 1 #define SCSI 2 #define MAX_READ_DATA_SIZE 0x10000 #define BULK_HEADER_SIZE 12 typedef unsigned char u8; typedef unsigned u32; typedef unsigned short u16; #define SIDE_FRONT 0x00 #define SIDE_BACK 0x80 /* options */ typedef enum { NUM_OPTS = 0, /* General options */ MODE_GROUP, MODE, /* scanner modes */ RESOLUTION, /* X and Y resolution */ DUPLEX, /* Duplex mode */ FEEDER_MODE, /* Feeder mode, fixed to Continuous */ LENGTHCTL, /* Length control mode */ MANUALFEED, /* Manual feed mode */ FEED_TIMEOUT, /* Feed timeout */ DBLFEED, /* Double feed detection mode */ FIT_TO_PAGE, /* Scanner shrinks image to fit scanned page */ /* Geometry group */ GEOMETRY_GROUP, PAPER_SIZE, /* Paper size */ LANDSCAPE, /* true if landscape */ TL_X, /* upper left X */ TL_Y, /* upper left Y */ BR_X, /* bottom right X */ BR_Y, /* bottom right Y */ ADVANCED_GROUP, BRIGHTNESS, /* Brightness */ CONTRAST, /* Contrast */ THRESHOLD, /* Binary threshold */ IMAGE_EMPHASIS, /* Image emphasis */ GAMMA_CORRECTION, /* Gamma correction */ LAMP, /* Lamp -- color drop out */ /* must come last: */ NUM_OPTIONS } KV_OPTION; #ifndef SANE_OPTION typedef union { SANE_Bool b; /**< bool */ SANE_Word w; /**< word */ SANE_Word *wa; /**< word array */ SANE_String s; /**< string */ } Option_Value; #define SANE_OPTION 1 #endif struct scanner { unsigned id; int scanning; int page; int side; int bus; SANE_Int file; SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; SANE_Parameters params; u8 *buffer; u8 *data; unsigned side_size; unsigned read; unsigned dummy_size; unsigned saved_dummy_size; }; struct window { u8 reserved[6]; u16 window_descriptor_block_length; u8 window_identifier; u8 reserved2; u16 x_resolution; u16 y_resolution; u32 upper_left_x; u32 upper_left_y; u32 width; u32 length; u8 brightness; u8 threshold; u8 contrast; u8 image_composition; u8 bit_per_pixel; u16 halftone_pattern; u8 reserved3; u16 bit_ordering; u8 compression_type; u8 compression_argument; u8 reserved4[6]; u8 vendor_unique_identifier; u8 nobuf_fstspeed_dfstop; u8 mirror_image; u8 image_emphasis; u8 gamma_correction; u8 mcd_lamp_dfeed_sens; u8 reserved5; u8 document_size; u32 document_width; u32 document_length; u8 ahead_deskew_dfeed_scan_area_fspeed_rshad; u8 continuous_scanning_pages; u8 automatic_threshold_mode; u8 automatic_separation_mode; u8 standard_white_level_mode; u8 b_wnr_noise_reduction; u8 mfeed_toppos_btmpos_dsepa_hsepa_dcont_rstkr; u8 stop_mode; } __attribute__((__packed__)); void kvs20xx_init_options (struct scanner *); void kvs20xx_init_window (struct scanner *s, struct window *wnd, int wnd_id); static inline u16 swap_bytes16 (u16 x) { return x << 8 | x >> 8; } static inline u32 swap_bytes32 (u32 x) { return x << 24 | x >> 24 | (x & (u32) 0x0000ff00UL) << 8 | (x & (u32) 0x00ff0000UL) >> 8; } static inline void copy16 (u8 * p, u16 x) { memcpy (p, (u8 *) &x, sizeof (x)); } #if __BYTE_ORDER == __BIG_ENDIAN static inline void set24 (u8 * p, u32 x) { p[2] = x >> 16; p[1] = x >> 8; p[0] = x >> 0; } #define cpu2be16(x) (x) #define cpu2be32(x) (x) #define cpu2le16(x) swap_bytes16(x) #define cpu2le32(x) swap_bytes32(x) #define le2cpu16(x) swap_bytes16(x) #define le2cpu32(x) swap_bytes32(x) #define be2cpu16(x) (x) #define be2cpu32(x) (x) #define BIT_ORDERING 0 #elif __BYTE_ORDER == __LITTLE_ENDIAN static inline void set24 (u8 * p, u32 x) { p[0] = x >> 16; p[1] = x >> 8; p[2] = x >> 0; } #define cpu2le16(x) (x) #define cpu2le32(x) (x) #define cpu2be16(x) swap_bytes16(x) #define cpu2be32(x) swap_bytes32(x) #define le2cpu16(x) (x) #define le2cpu32(x) (x) #define be2cpu16(x) swap_bytes16(x) #define be2cpu32(x) swap_bytes32(x) #define BIT_ORDERING 1 #else #error __BYTE_ORDER not defined #endif #endif /*__KVS20XX_H*/ backends-1.3.0/backend/kvs20xx_cmd.c000066400000000000000000000162551456256263500172170ustar00rootroot00000000000000/* Copyright (C) 2008, Panasonic Russia Ltd. Copyright (C) 2010, m. allan noah */ /* Panasonic KV-S20xx USB-SCSI scanners. */ #include "../include/sane/config.h" #include /*#include */ #define DEBUG_DECLARE_ONLY #define BACKEND_NAME kvs20xx #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_scsi.h" #include "../include/sane/sanei_usb.h" #include "kvs20xx.h" #include "kvs20xx_cmd.h" static SANE_Status usb_send_command (struct scanner *s, struct cmd *c, struct response *r, void *buf) { SANE_Status st; struct bulk_header *h = (struct bulk_header *) buf; u8 resp[sizeof (*h) + STATUS_SIZE]; size_t sz = sizeof (*h) + MAX_CMD_SIZE; memset (h, 0, sz); h->length = cpu2be32 (sz); h->type = cpu2be16 (COMMAND_BLOCK); h->code = cpu2be16 (COMMAND_CODE); memcpy (h + 1, c->cmd, c->cmd_size); st = sanei_usb_write_bulk (s->file, (const SANE_Byte *) h, &sz); if (st) return st; if (sz != sizeof (*h) + MAX_CMD_SIZE) return SANE_STATUS_IO_ERROR; if (c->dir == CMD_IN) { sz = sizeof (*h) + c->data_size; st = sanei_usb_read_bulk (s->file, (SANE_Byte *) h, &sz); c->data = h + 1; c->data_size = sz - sizeof (*h); if (st || sz < sizeof (*h)) { st = sanei_usb_release_interface (s->file, 0); if (st) return st; st = sanei_usb_claim_interface (s->file, 0); if (st) return st; r->status = CHECK_CONDITION; return SANE_STATUS_GOOD; } } else if (c->dir == CMD_OUT) { sz = sizeof (*h) + c->data_size; memset (h, 0, sizeof (*h)); h->length = cpu2be32 (sizeof (*h) + c->data_size); h->type = cpu2be16 (DATA_BLOCK); h->code = cpu2be16 (DATA_CODE); memcpy (h + 1, c->data, c->data_size); st = sanei_usb_write_bulk (s->file, (const SANE_Byte *) h, &sz); if (st) return st; } sz = sizeof (resp); st = sanei_usb_read_bulk (s->file, resp, &sz); if (st || sz != sizeof (resp)) return SANE_STATUS_IO_ERROR; r->status = be2cpu32 (*((u32 *) (resp + sizeof (*h)))); return st; } SANE_Status kvs20xx_sense_handler (int __sane_unused__ fd, u_char * sense_buffer, void __sane_unused__ * arg) { unsigned i; SANE_Status st = SANE_STATUS_GOOD; for (i = 0; i < sizeof (s_errors) / sizeof (s_errors[0]); i++) if ((sense_buffer[2] & 0xf) == s_errors[i].sense && sense_buffer[12] == s_errors[i].asc && sense_buffer[13] == s_errors[i].ascq) { st = s_errors[i].st; break; } if (st == SANE_STATUS_GOOD && sense_buffer[2] & END_OF_MEDIUM) st = SANE_STATUS_EOF; if (i == sizeof (s_errors) / sizeof (s_errors[0])) st = SANE_STATUS_IO_ERROR; DBG (DBG_ERR, "send_command: CHECK_CONDITION: sense:0x%x ASC:0x%x ASCQ:0x%x\n", sense_buffer[2], sense_buffer[12], sense_buffer[13]); return st; } static SANE_Status send_command (struct scanner * s, struct cmd * c) { SANE_Status st = SANE_STATUS_GOOD; if (s->bus == USB) { struct response r; memset (&r, 0, sizeof (r)); st = usb_send_command (s, c, &r, s->buffer); if (st) return st; if (r.status) { u8 b[sizeof (struct bulk_header) + RESPONSE_SIZE]; struct cmd c2 = { {0}, 6, 0, RESPONSE_SIZE, CMD_IN }; c2.cmd[0] = REQUEST_SENSE; c2.cmd[4] = RESPONSE_SIZE; st = usb_send_command (s, &c2, &r, b); if (st) return st; st = kvs20xx_sense_handler (0, b + sizeof (struct bulk_header), NULL); } } else { if (c->dir == CMD_OUT) { memcpy (s->buffer, c->cmd, c->cmd_size); memcpy (s->buffer + c->cmd_size, c->data, c->data_size); st = sanei_scsi_cmd (s->file, s->buffer, c->cmd_size + c->data_size, NULL, NULL); } else if (c->dir == CMD_IN) { c->data = s->buffer; st = sanei_scsi_cmd (s->file, c->cmd, c->cmd_size, c->data, (size_t *) & c->data_size); } else { st = sanei_scsi_cmd (s->file, c->cmd, c->cmd_size, NULL, NULL); } } return st; } SANE_Status kvs20xx_test_unit_ready (struct scanner * s) { struct cmd c = { {0}, 6, 0, 0, CMD_NONE }; c.cmd[0] = TEST_UNIT_READY; if (send_command (s, &c)) return SANE_STATUS_DEVICE_BUSY; return SANE_STATUS_GOOD; } SANE_Status kvs20xx_set_timeout (struct scanner * s, int timeout) { u16 t = cpu2be16 ((u16) timeout); struct cmd c = { {0}, 10, 0, 0, CMD_OUT }; c.cmd[0] = SET_TIMEOUT; c.cmd[2] = 0x8d; copy16 (c.cmd + 7, cpu2be16 (sizeof (t))); c.data = &t; c.data_size = sizeof (t); if (s->bus == USB) sanei_usb_set_timeout (timeout * 1000); return send_command (s, &c); } SANE_Status kvs20xx_set_window (struct scanner * s, int wnd_id) { struct window wnd; struct cmd c = { {0}, 10, 0, 0, CMD_OUT }; c.cmd[0] = SET_WINDOW; copy16 (c.cmd + 7, cpu2be16 (sizeof (wnd))); c.data = &wnd; c.data_size = sizeof (wnd); kvs20xx_init_window (s, &wnd, wnd_id); return send_command (s, &c); } SANE_Status kvs20xx_reset_window (struct scanner * s) { struct cmd c = { {0}, 10, 0, 0, CMD_NONE }; c.cmd[0] = SET_WINDOW; return send_command (s, &c); } SANE_Status kvs20xx_scan (struct scanner * s) { struct cmd c = { {0}, 6, 0, 0, CMD_NONE }; c.cmd[0] = SCAN; return send_command (s, &c); } SANE_Status kvs20xx_document_exist (struct scanner * s) { SANE_Status status; struct cmd c = { {0}, 10, 0, 6, CMD_IN, }; u8 *d; c.cmd[0] = READ_10; c.cmd[2] = 0x81; set24 (c.cmd + 6, c.data_size); status = send_command (s, &c); if (status) return status; d = c.data; if (d[0] & 0x20) return SANE_STATUS_GOOD; return SANE_STATUS_NO_DOCS; } SANE_Status kvs20xx_read_picture_element (struct scanner * s, unsigned side, SANE_Parameters * p) { SANE_Status status; struct cmd c = { {0}, 10, 0, 16, CMD_IN }; u32 *data; c.cmd[0] = READ_10; c.cmd[2] = 0x80; c.cmd[5] = side; set24 (c.cmd + 6, c.data_size); status = send_command (s, &c); if (status) return status; data = (u32 *) c.data; p->pixels_per_line = be2cpu32 (data[0]); p->lines = be2cpu32 (data[1]); return SANE_STATUS_GOOD; } SANE_Status kvs20xx_read_image_data (struct scanner * s, unsigned page, unsigned side, void *buf, unsigned max_size, unsigned *size) { SANE_Status status; struct cmd c = { {0}, 10, 0, 0, CMD_IN }; c.cmd[0] = READ_10; c.cmd[4] = page; c.cmd[5] = side; c.data_size = max_size < MAX_READ_DATA_SIZE ? max_size : MAX_READ_DATA_SIZE; set24 (c.cmd + 6, c.data_size); status = send_command (s, &c); if (status && status != SANE_STATUS_EOF) return status; *size = c.data_size; DBG (DBG_INFO, "kvs20xx_read_image_data: read %d, status %d\n", *size, status); memcpy (buf, c.data, *size); return status; } SANE_Status get_adjust_data (struct scanner * s, unsigned *dummy_length) { SANE_Status status; struct cmd c = { {0}, 10, 0, 40, CMD_IN }; u16 *data; c.cmd[0] = GET_ADJUST_DATA; c.cmd[2] = 0x9b; c.cmd[8] = 40; status = send_command (s, &c); if (status) return status; data = (u16 *) c.data; *dummy_length = be2cpu16 (data[0]); return SANE_STATUS_GOOD; } backends-1.3.0/backend/kvs20xx_cmd.h000066400000000000000000000057251456256263500172240ustar00rootroot00000000000000#ifndef __KVS20XX_CMD_H #define __KVS20XX_CMD_H /* Copyright (C) 2008, Panasonic Russia Ltd. Copyright (C) 2010, m. allan noah */ /* Panasonic KV-S20xx USB-SCSI scanners. */ #ifdef HAVE_SYS_TYPES_H #include #endif #define COMMAND_BLOCK 1 #define DATA_BLOCK 2 #define RESPONSE_BLOCK 3 #define COMMAND_CODE 0x9000 #define DATA_CODE 0xb000 #define RESPONSE_CODE 0xa000 #define STATUS_SIZE 4 struct bulk_header { u32 length; u16 type; u16 code; u32 transaction_id; }; #define TEST_UNIT_READY 0x00 #define INQUIRY 0x12 #define SET_WINDOW 0x24 #define SCAN 0x1B #define SEND_10 0x2A #define READ_10 0x28 #define REQUEST_SENSE 0x03 #define GET_BUFFER_STATUS 0x34 #define SET_TIMEOUT 0xE1 #define GET_ADJUST_DATA 0xE0 #define GOOD 0 #define CHECK_CONDITION 2 typedef enum { CMD_NONE = 0, CMD_IN = 0x81, /* scanner to pc */ CMD_OUT = 0x02 /* pc to scanner */ } CMD_DIRECTION; /* equals to endpoint address */ #define RESPONSE_SIZE 0x12 #define MAX_CMD_SIZE 12 struct cmd { unsigned char cmd[MAX_CMD_SIZE]; int cmd_size; void *data; int data_size; int dir; }; struct response { int status; unsigned char data[RESPONSE_SIZE]; }; #define END_OF_MEDIUM (1<<6) #define INCORRECT_LENGTH_INDICATOR (1<<5) static const struct { unsigned sense, asc, ascq; SANE_Status st; } s_errors[] = { { 0, 0, 0, SANE_STATUS_GOOD}, { 2, 0, 0, SANE_STATUS_DEVICE_BUSY}, { 2, 4, 1, SANE_STATUS_DEVICE_BUSY}, { 2, 4, 0x80, SANE_STATUS_COVER_OPEN}, { 2, 4, 0x81, SANE_STATUS_COVER_OPEN}, { 2, 4, 0x82, SANE_STATUS_COVER_OPEN}, { 2, 4, 0x83, SANE_STATUS_COVER_OPEN}, { 2, 4, 0x84, SANE_STATUS_COVER_OPEN}, { 2, 0x80, 1, SANE_STATUS_CANCELLED}, { 2, 0x80, 2, SANE_STATUS_CANCELLED}, { 3, 0x3a, 0, SANE_STATUS_NO_DOCS}, { 3, 0x80, 1, SANE_STATUS_JAMMED}, { 3, 0x80, 2, SANE_STATUS_JAMMED}, { 3, 0x80, 3, SANE_STATUS_JAMMED}, { 3, 0x80, 4, SANE_STATUS_JAMMED}, { 3, 0x80, 5, SANE_STATUS_JAMMED}, { 3, 0x80, 6, SANE_STATUS_JAMMED}, { 3, 0x80, 7, SANE_STATUS_JAMMED}, { 3, 0x80, 8, SANE_STATUS_JAMMED}, { 3, 0x80, 9, SANE_STATUS_JAMMED},}; SANE_Status kvs20xx_scan (struct scanner *s); SANE_Status kvs20xx_test_unit_ready (struct scanner *s); SANE_Status kvs20xx_set_timeout (struct scanner *s, int timeout); SANE_Status kvs20xx_set_window (struct scanner *s, int wnd_id); SANE_Status kvs20xx_reset_window (struct scanner *s); SANE_Status kvs20xx_read_picture_element (struct scanner *s, unsigned side, SANE_Parameters * p); SANE_Status kvs20xx_read_image_data (struct scanner *s, unsigned page, unsigned side, void *buf, unsigned max_size, unsigned *size); SANE_Status kvs20xx_document_exist (struct scanner *s); SANE_Status get_adjust_data (struct scanner *s, unsigned *dummy_length); SANE_Status kvs20xx_sense_handler (int fd, u_char * sense_buffer, void *arg); #endif /*__KVS20XX_CMD_H*/ backends-1.3.0/backend/kvs20xx_opt.c000066400000000000000000000531441456256263500172540ustar00rootroot00000000000000/* Copyright (C) 2008, Panasonic Russia Ltd. Copyright (C) 2010, m. allan noah */ /* Panasonic KV-S20xx USB-SCSI scanners. */ #include "../include/sane/config.h" #include #define DEBUG_DECLARE_ONLY #define BACKEND_NAME kvs20xx #include "../include/sane/sane.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei.h" #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_config.h" #include "../include/lassert.h" #include "kvs20xx.h" #include "kvs20xx_cmd.h" #include static size_t max_string_size (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; } static SANE_String_Const mode_list[] = { SANE_VALUE_SCAN_MODE_LINEART, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_COLOR, NULL }; static const unsigned mode_val[] = { 0, 2, 5 }; static const unsigned bps_val[] = { 1, 8, 24 }; static const SANE_Range resolutions_range = {100,600,10}; /* List of feeder modes */ static SANE_String_Const feeder_mode_list[] = { SANE_I18N ("single"), SANE_I18N ("continuous"), NULL }; /* List of manual feed mode */ static SANE_String_Const manual_feed_list[] = { SANE_I18N ("off"), SANE_I18N ("wait_doc"), SANE_I18N ("wait_key"), NULL }; /* List of paper sizes */ static SANE_String_Const paper_list[] = { SANE_I18N ("user_def"), SANE_I18N ("business_card"), /*SANE_I18N("Check"), */ /*SANE_I18N ("A3"), */ SANE_I18N ("A4"), SANE_I18N ("A5"), SANE_I18N ("A6"), SANE_I18N ("Letter"), /*SANE_I18N ("Double letter 11x17 in"), SANE_I18N ("B4"), */ SANE_I18N ("B5"), SANE_I18N ("B6"), SANE_I18N ("Legal"), NULL }; static const unsigned paper_val[] = { 0, 1, 4, 5, 6, 7, 13, 14, 15 }; struct paper_size { int width; int height; }; static const struct paper_size paper_sizes[] = { {210, 297}, /* User defined, default=A4 */ {54, 90}, /* Business card */ /*{80, 170}, *//* Check (China business) */ /*{297, 420}, *//* A3 */ {210, 297}, /* A4 */ {148, 210}, /* A5 */ {105, 148}, /* A6 */ {215, 280}, /* US Letter 8.5 x 11 in */ /*{280, 432}, *//* Double Letter 11 x 17 in */ /*{250, 353}, *//* B4 */ {176, 250}, /* B5 */ {125, 176}, /* B6 */ {215, 355} /* US Legal */ }; #define MIN_WIDTH 51 #define MAX_WIDTH 215 #define MIN_LENGTH 70 #define MAX_LENGTH 355 static SANE_Range tl_x_range = { 0, MAX_WIDTH - MIN_WIDTH, 0 }; static SANE_Range tl_y_range = { 0, MAX_LENGTH - MIN_LENGTH, 0 }; static SANE_Range br_x_range = { MIN_WIDTH, MAX_WIDTH, 0 }; static SANE_Range br_y_range = { MIN_LENGTH, MAX_LENGTH, 0 }; static SANE_Range byte_value_range = { 0, 255, 0 }; /* List of image emphasis options, 5 steps */ static SANE_String_Const image_emphasis_list[] = { SANE_I18N ("none"), SANE_I18N ("low"), SANE_I18N ("medium"), SANE_I18N ("high"), SANE_I18N ("smooth"), NULL }; /* List of gamma */ static SANE_String_Const gamma_list[] = { SANE_I18N ("normal"), SANE_I18N ("crt"), NULL }; static unsigned gamma_val[] = { 0, 1 }; /* List of lamp color dropout */ static SANE_String_Const lamp_list[] = { SANE_I18N ("normal"), SANE_I18N ("red"), SANE_I18N ("green"), SANE_I18N ("blue"), NULL }; /* Reset the options for that scanner. */ void kvs20xx_init_options (struct scanner *s) { int i; SANE_Option_Descriptor *o; /* Pre-initialize the options. */ memset (s->opt, 0, sizeof (s->opt)); memset (s->val, 0, sizeof (s->val)); for (i = 0; i < NUM_OPTIONS; i++) { s->opt[i].size = sizeof (SANE_Word); s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } /* Number of options. */ o = &s->opt[NUM_OPTS]; o->name = ""; o->title = SANE_TITLE_NUM_OPTIONS; o->desc = SANE_DESC_NUM_OPTIONS; o->type = SANE_TYPE_INT; o->cap = SANE_CAP_SOFT_DETECT; s->val[NUM_OPTS].w = NUM_OPTIONS; /* Mode group */ o = &s->opt[MODE_GROUP]; o->title = SANE_I18N ("Scan Mode"); o->desc = ""; /* not valid for a group */ o->type = SANE_TYPE_GROUP; o->cap = 0; o->size = 0; o->constraint_type = SANE_CONSTRAINT_NONE; /* Scanner supported modes */ o = &s->opt[MODE]; o->name = SANE_NAME_SCAN_MODE; o->title = SANE_TITLE_SCAN_MODE; o->desc = SANE_DESC_SCAN_MODE; o->type = SANE_TYPE_STRING; o->size = max_string_size (mode_list); o->constraint_type = SANE_CONSTRAINT_STRING_LIST; o->constraint.string_list = mode_list; s->val[MODE].s = malloc (o->size); strcpy (s->val[MODE].s, mode_list[0]); /* X and Y resolution */ o = &s->opt[RESOLUTION]; o->name = SANE_NAME_SCAN_RESOLUTION; o->title = SANE_TITLE_SCAN_RESOLUTION; o->desc = SANE_DESC_SCAN_RESOLUTION; o->type = SANE_TYPE_INT; o->unit = SANE_UNIT_DPI; o->constraint_type = SANE_CONSTRAINT_RANGE; o->constraint.range = &resolutions_range; s->val[RESOLUTION].w = 100; /* Duplex */ o = &s->opt[DUPLEX]; o->name = "duplex"; o->title = SANE_I18N ("Duplex"); o->desc = SANE_I18N ("Enable Duplex (Dual-Sided) Scanning"); o->type = SANE_TYPE_BOOL; o->unit = SANE_UNIT_NONE; s->val[DUPLEX].w = SANE_FALSE; /*FIXME if (!s->support_info.support_duplex) o->cap |= SANE_CAP_INACTIVE; */ /* Feeder mode */ o = &s->opt[FEEDER_MODE]; o->name = "feeder-mode"; o->title = SANE_I18N ("Feeder mode"); o->desc = SANE_I18N ("Sets the feeding mode"); o->type = SANE_TYPE_STRING; o->size = max_string_size (feeder_mode_list); o->constraint_type = SANE_CONSTRAINT_STRING_LIST; o->constraint.string_list = feeder_mode_list; s->val[FEEDER_MODE].s = malloc (o->size); strcpy (s->val[FEEDER_MODE].s, feeder_mode_list[0]); /* Length control */ o = &s->opt[LENGTHCTL]; o->name = "length-control"; o->title = SANE_I18N ("Length control mode"); o->desc = SANE_I18N ("Length Control Mode causes the scanner to read the shorter of either the length of the actual" " paper or logical document length."); o->type = SANE_TYPE_BOOL; o->unit = SANE_UNIT_NONE; s->val[LENGTHCTL].w = SANE_FALSE; /* Manual feed */ o = &s->opt[MANUALFEED]; o->name = "manual-feed"; o->title = SANE_I18N ("Manual feed mode"); o->desc = SANE_I18N ("Sets the manual feed mode"); o->type = SANE_TYPE_STRING; o->size = max_string_size (manual_feed_list); o->constraint_type = SANE_CONSTRAINT_STRING_LIST; o->constraint.string_list = manual_feed_list; s->val[MANUALFEED].s = malloc (o->size); strcpy (s->val[MANUALFEED].s, manual_feed_list[0]); /*Manual feed timeout */ o = &s->opt[FEED_TIMEOUT]; o->name = "feed-timeout"; o->title = SANE_I18N ("Manual feed timeout"); o->desc = SANE_I18N ("Sets the manual feed timeout in seconds"); o->type = SANE_TYPE_INT; o->unit = SANE_UNIT_NONE; o->size = sizeof (SANE_Int); o->constraint_type = SANE_CONSTRAINT_RANGE; o->constraint.range = &(byte_value_range); o->cap |= SANE_CAP_INACTIVE; s->val[FEED_TIMEOUT].w = 30; /* Double feed */ o = &s->opt[DBLFEED]; o->name = "double-feed"; o->title = SANE_I18N ("Double feed detection"); o->desc = SANE_I18N ("Enable/Disable double feed detection"); o->type = SANE_TYPE_BOOL; o->unit = SANE_UNIT_NONE; s->val[DBLFEED].w = SANE_FALSE; /* Fit to page */ o = &s->opt[FIT_TO_PAGE]; o->name = SANE_I18N ("fit-to-page"); o->title = SANE_I18N ("Fit to page"); o->desc = SANE_I18N ("Scanner shrinks image to fit scanned page"); o->type = SANE_TYPE_BOOL; o->unit = SANE_UNIT_NONE; s->val[FIT_TO_PAGE].w = SANE_FALSE; /* Geometry group */ o = &s->opt[GEOMETRY_GROUP]; o->title = SANE_I18N ("Geometry"); o->desc = ""; /* not valid for a group */ o->type = SANE_TYPE_GROUP; o->cap = 0; o->size = 0; o->constraint_type = SANE_CONSTRAINT_NONE; /* Paper sizes list */ o = &s->opt[PAPER_SIZE]; o->name = "paper-size"; o->title = SANE_I18N ("Paper size"); o->desc = SANE_I18N ("Physical size of the paper in the ADF"); o->type = SANE_TYPE_STRING; o->size = max_string_size (paper_list); o->constraint_type = SANE_CONSTRAINT_STRING_LIST; o->constraint.string_list = paper_list; s->val[PAPER_SIZE].s = malloc (o->size); strcpy (s->val[PAPER_SIZE].s, SANE_I18N ("A4")); /* Landscape */ o = &s->opt[LANDSCAPE]; o->name = "landscape"; o->title = SANE_I18N ("Landscape"); o->desc = SANE_I18N ("Set paper position : " "true for landscape, false for portrait"); o->type = SANE_TYPE_BOOL; o->unit = SANE_UNIT_NONE; s->val[LANDSCAPE].w = SANE_FALSE; o->cap |= SANE_CAP_INACTIVE; /* Upper left X */ o = &s->opt[TL_X]; o->name = SANE_NAME_SCAN_TL_X; o->title = SANE_TITLE_SCAN_TL_X; o->desc = SANE_DESC_SCAN_TL_X; o->type = SANE_TYPE_INT; o->unit = SANE_UNIT_MM; o->constraint_type = SANE_CONSTRAINT_RANGE; o->constraint.range = &tl_x_range; o->cap |= SANE_CAP_INACTIVE; s->val[TL_X].w = 0; /* Upper left Y */ o = &s->opt[TL_Y]; o->name = SANE_NAME_SCAN_TL_Y; o->title = SANE_TITLE_SCAN_TL_Y; o->desc = SANE_DESC_SCAN_TL_Y; o->type = SANE_TYPE_INT; o->unit = SANE_UNIT_MM; o->constraint_type = SANE_CONSTRAINT_RANGE; o->constraint.range = &tl_y_range; o->cap |= SANE_CAP_INACTIVE; s->val[TL_Y].w = 0; /* Bottom-right x */ o = &s->opt[BR_X]; o->name = SANE_NAME_SCAN_BR_X; o->title = SANE_TITLE_SCAN_BR_X; o->desc = SANE_DESC_SCAN_BR_X; o->type = SANE_TYPE_INT; o->unit = SANE_UNIT_MM; o->constraint_type = SANE_CONSTRAINT_RANGE; o->constraint.range = &br_x_range; o->cap |= SANE_CAP_INACTIVE; s->val[BR_X].w = 210; /* Bottom-right y */ o = &s->opt[BR_Y]; o->name = SANE_NAME_SCAN_BR_Y; o->title = SANE_TITLE_SCAN_BR_Y; o->desc = SANE_DESC_SCAN_BR_Y; o->type = SANE_TYPE_INT; o->unit = SANE_UNIT_MM; o->constraint_type = SANE_CONSTRAINT_RANGE; o->constraint.range = &br_y_range; o->cap |= SANE_CAP_INACTIVE; s->val[BR_Y].w = 297; /* Enhancement group */ o = &s->opt[ADVANCED_GROUP]; o->title = SANE_I18N ("Advanced"); o->desc = ""; /* not valid for a group */ o->type = SANE_TYPE_GROUP; o->cap = SANE_CAP_ADVANCED; o->size = 0; o->constraint_type = SANE_CONSTRAINT_NONE; /* Brightness */ o = &s->opt[BRIGHTNESS]; o->name = SANE_NAME_BRIGHTNESS; o->title = SANE_TITLE_BRIGHTNESS; o->desc = SANE_DESC_BRIGHTNESS; o->type = SANE_TYPE_INT; o->unit = SANE_UNIT_NONE; o->size = sizeof (SANE_Int); o->constraint_type = SANE_CONSTRAINT_RANGE; o->constraint.range = &(byte_value_range); s->val[BRIGHTNESS].w = 128; /* Contrast */ o = &s->opt[CONTRAST]; o->name = SANE_NAME_CONTRAST; o->title = SANE_TITLE_CONTRAST; o->desc = SANE_DESC_CONTRAST; o->type = SANE_TYPE_INT; o->unit = SANE_UNIT_NONE; o->size = sizeof (SANE_Int); o->constraint_type = SANE_CONSTRAINT_RANGE; o->constraint.range = &(byte_value_range); s->val[CONTRAST].w = 128; /* threshold */ o = &s->opt[THRESHOLD]; o->name = SANE_NAME_THRESHOLD; o->title = SANE_TITLE_THRESHOLD; o->desc = SANE_DESC_THRESHOLD; o->type = SANE_TYPE_INT; o->size = sizeof (SANE_Int); o->constraint_type = SANE_CONSTRAINT_RANGE; o->constraint.range = &(byte_value_range); s->val[THRESHOLD].w = 128; /* Image emphasis */ o = &s->opt[IMAGE_EMPHASIS]; o->name = "image-emphasis"; o->title = SANE_I18N ("Image emphasis"); o->desc = SANE_I18N ("Sets the image emphasis"); o->type = SANE_TYPE_STRING; o->size = max_string_size (image_emphasis_list); o->constraint_type = SANE_CONSTRAINT_STRING_LIST; o->constraint.string_list = image_emphasis_list; s->val[IMAGE_EMPHASIS].s = malloc (o->size); strcpy (s->val[IMAGE_EMPHASIS].s, image_emphasis_list[0]); /* Gamma */ o = &s->opt[GAMMA_CORRECTION]; o->name = "gamma-cor"; o->title = SANE_I18N ("Gamma correction"); o->desc = SANE_I18N ("Gamma correction"); o->type = SANE_TYPE_STRING; o->size = max_string_size (gamma_list); o->constraint_type = SANE_CONSTRAINT_STRING_LIST; o->constraint.string_list = gamma_list; s->val[GAMMA_CORRECTION].s = malloc (o->size); strcpy (s->val[GAMMA_CORRECTION].s, gamma_list[0]); /* Lamp color dropout */ o = &s->opt[LAMP]; o->name = "lamp-color"; o->title = SANE_I18N ("Lamp color"); o->desc = SANE_I18N ("Sets the lamp color (color dropout)"); o->type = SANE_TYPE_STRING; o->size = max_string_size (lamp_list); o->constraint_type = SANE_CONSTRAINT_STRING_LIST; o->constraint.string_list = lamp_list; s->val[LAMP].s = malloc (o->size); strcpy (s->val[LAMP].s, lamp_list[0]); } /* Lookup a string list from one array and return its index. */ static int str_index (const SANE_String_Const * list, SANE_String_Const name) { int index; index = 0; while (list[index]) { if (!strcmp (list[index], name)) return (index); index++; } return (-1); /* not found */ } /* Control option */ SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { int i; SANE_Status status; SANE_Word cap; struct scanner *s = (struct scanner *) handle; if (info) *info = 0; if (option < 0 || option >= NUM_OPTIONS) return SANE_STATUS_UNSUPPORTED; cap = s->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) return SANE_STATUS_UNSUPPORTED; if (action == SANE_ACTION_GET_VALUE) { if (s->opt[option].type == SANE_TYPE_STRING) { DBG (DBG_INFO, "sane_control_option: reading opt[%d] = %s\n", option, s->val[option].s); strcpy (val, s->val[option].s); } else { *(SANE_Word *) val = s->val[option].w; DBG (DBG_INFO, "sane_control_option: reading opt[%d] = %d\n", option, s->val[option].w); } return SANE_STATUS_GOOD; } else if (action == SANE_ACTION_SET_VALUE) { if (!SANE_OPTION_IS_SETTABLE (cap)) return SANE_STATUS_INVAL; status = sanei_constrain_value (s->opt + option, val, info); if (status != SANE_STATUS_GOOD) return status; if (s->opt[option].type == SANE_TYPE_STRING) { if (!strcmp (val, s->val[option].s)) return SANE_STATUS_GOOD; DBG (DBG_INFO, "sane_control_option: writing opt[%d] = %s\n", option, (SANE_String_Const) val); } else { if (*(SANE_Word *) val == s->val[option].w) return SANE_STATUS_GOOD; DBG (DBG_INFO, "sane_control_option: writing opt[%d] = %d\n", option, *(SANE_Word *) val); } switch (option) { /* Side-effect options */ case RESOLUTION: s->val[option].w = *(SANE_Word *) val; if (info) *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case TL_Y: if ((*(SANE_Word *) val) + MIN_LENGTH <= s->val[BR_Y].w) { s->val[option].w = *(SANE_Word *) val; if (info) *info |= SANE_INFO_RELOAD_PARAMS; } else if (info) *info |= SANE_INFO_INEXACT; return SANE_STATUS_GOOD; case BR_Y: if ((*(SANE_Word *) val) >= s->val[TL_Y].w + MIN_LENGTH) { s->val[option].w = *(SANE_Word *) val; if (info) *info |= SANE_INFO_RELOAD_PARAMS; } else if (info) *info |= SANE_INFO_INEXACT; return SANE_STATUS_GOOD; case TL_X: if ((*(SANE_Word *) val) + MIN_WIDTH <= s->val[BR_X].w) { s->val[option].w = *(SANE_Word *) val; if (info) *info |= SANE_INFO_RELOAD_PARAMS; } else if (info) *info |= SANE_INFO_INEXACT; return SANE_STATUS_GOOD; case BR_X: if (*(SANE_Word *) val >= s->val[TL_X].w + MIN_WIDTH) { s->val[option].w = *(SANE_Word *) val; if (info) *info |= SANE_INFO_RELOAD_PARAMS; } else if (info) *info |= SANE_INFO_INEXACT; return SANE_STATUS_GOOD; case LANDSCAPE: s->val[option].w = *(SANE_Word *) val; if (info) *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; /* Side-effect free options */ case CONTRAST: case BRIGHTNESS: case DUPLEX: case LENGTHCTL: case DBLFEED: case FIT_TO_PAGE: case THRESHOLD: s->val[option].w = *(SANE_Word *) val; return SANE_STATUS_GOOD; case FEED_TIMEOUT: s->val[option].w = *(SANE_Word *) val; return kvs20xx_set_timeout (s, s->val[option].w); /* String mode */ case IMAGE_EMPHASIS: case GAMMA_CORRECTION: case LAMP: case FEEDER_MODE: strcpy (s->val[option].s, val); return SANE_STATUS_GOOD; case MODE: strcpy (s->val[MODE].s, val); if (!strcmp (s->val[MODE].s, SANE_VALUE_SCAN_MODE_LINEART)) { s->opt[THRESHOLD].cap &= ~SANE_CAP_INACTIVE; s->opt[GAMMA_CORRECTION].cap |= SANE_CAP_INACTIVE; s->opt[BRIGHTNESS].cap |= SANE_CAP_INACTIVE; } else { s->opt[THRESHOLD].cap |= SANE_CAP_INACTIVE; s->opt[GAMMA_CORRECTION].cap &= ~SANE_CAP_INACTIVE; s->opt[BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; } if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case MANUALFEED: strcpy (s->val[option].s, val); if (strcmp (s->val[option].s, manual_feed_list[0]) == 0) /* off */ s->opt[FEED_TIMEOUT].cap |= SANE_CAP_INACTIVE; else s->opt[FEED_TIMEOUT].cap &= ~SANE_CAP_INACTIVE; if (info) *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case PAPER_SIZE: strcpy (s->val[PAPER_SIZE].s, val); i = str_index (paper_list, s->val[PAPER_SIZE].s); if (i == 0) { /*user def */ s->opt[TL_X].cap &= s->opt[TL_Y].cap &= s->opt[BR_X].cap &= s->opt[BR_Y].cap &= ~SANE_CAP_INACTIVE; s->opt[LANDSCAPE].cap |= SANE_CAP_INACTIVE; s->val[LANDSCAPE].w = 0; } else { s->opt[TL_X].cap |= s->opt[TL_Y].cap |= s->opt[BR_X].cap |= s->opt[BR_Y].cap |= SANE_CAP_INACTIVE; if (i == 3 || i == 4 || i == 7) { /*A5, A6 or B6 */ s->opt[LANDSCAPE].cap &= ~SANE_CAP_INACTIVE; } else { s->opt[LANDSCAPE].cap |= SANE_CAP_INACTIVE; s->val[LANDSCAPE].w = 0; } } if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; } } return SANE_STATUS_UNSUPPORTED; } static inline unsigned mm2scanner_units (unsigned mm) { return mm * 12000 / 254; } void kvs20xx_init_window (struct scanner *s, struct window *wnd, int wnd_id) { int paper = str_index (paper_list, s->val[PAPER_SIZE].s); memset (wnd, 0, sizeof (struct window)); wnd->window_descriptor_block_length = cpu2be16 (64); wnd->window_identifier = wnd_id; wnd->x_resolution = cpu2be16 (s->val[RESOLUTION].w); wnd->y_resolution = cpu2be16 (s->val[RESOLUTION].w); if (!paper) { wnd->upper_left_x = cpu2be32 (mm2scanner_units (s->val[TL_X].w)); wnd->upper_left_y = cpu2be32 (mm2scanner_units (s->val[TL_Y].w)); wnd->width = cpu2be32 (mm2scanner_units (s->val[BR_X].w - s->val[TL_X].w)); wnd->length = cpu2be32 (mm2scanner_units (s->val[BR_Y].w - s->val[TL_Y].w)); } else { u32 w = cpu2be32 (mm2scanner_units (paper_sizes[paper].width)); u32 h = cpu2be32 (mm2scanner_units (paper_sizes[paper].height)); wnd->upper_left_x = cpu2be32 (mm2scanner_units (0)); wnd->upper_left_y = cpu2be32 (mm2scanner_units (0)); if (!s->val[LANDSCAPE].b) { wnd->document_width = wnd->width = w; wnd->document_length = wnd->length = h; } else { wnd->document_width = wnd->width = h; wnd->document_length = wnd->length = w; } } wnd->brightness = s->val[BRIGHTNESS].w; wnd->threshold = s->val[THRESHOLD].w; wnd->contrast = s->val[CONTRAST].w; wnd->image_composition = mode_val[str_index (mode_list, s->val[MODE].s)]; wnd->bit_per_pixel = bps_val[str_index (mode_list, s->val[MODE].s)]; wnd->halftone_pattern = 0; /*Does not supported */ wnd->bit_ordering = cpu2be16 (BIT_ORDERING); wnd->compression_type = 0; /*Does not supported */ wnd->compression_argument = 0; /*Does not supported */ wnd->vendor_unique_identifier = 0; wnd->nobuf_fstspeed_dfstop = 0; wnd->mirror_image = 0; wnd->image_emphasis = str_index (image_emphasis_list, s->val[IMAGE_EMPHASIS].s); wnd->gamma_correction = gamma_val[str_index (gamma_list, s->val[GAMMA_CORRECTION].s)]; wnd->mcd_lamp_dfeed_sens = str_index (lamp_list, s->val[LAMP].s) << 4 | 2; wnd->document_size = ((paper != 0) << 7) | (s->val[LENGTHCTL].b << 6) | (s->val[LANDSCAPE].b << 4) | paper_val[paper]; wnd->ahead_deskew_dfeed_scan_area_fspeed_rshad = s->val[DBLFEED].b << 4 | s->val[FIT_TO_PAGE].b << 2; wnd->continuous_scanning_pages = str_index (feeder_mode_list, s->val[FEEDER_MODE]. s) ? 0xff : 0; wnd->automatic_threshold_mode = 0; /*Does not supported */ wnd->automatic_separation_mode = 0; /*Does not supported */ wnd->standard_white_level_mode = 0; /*Does not supported */ wnd->b_wnr_noise_reduction = 0; /*Does not supported */ if (str_index (manual_feed_list, s->val[MANUALFEED].s) == 2) wnd->mfeed_toppos_btmpos_dsepa_hsepa_dcont_rstkr = 2 << 6; wnd->stop_mode = 1; } /* Get scan parameters */ SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { struct scanner *s = (struct scanner *) handle; SANE_Parameters *p = &s->params; if (!s->scanning) { unsigned w, h, res = s->val[RESOLUTION].w; unsigned i = str_index (paper_list, s->val[PAPER_SIZE].s); if (i) { if (s->val[LANDSCAPE].b) { w = paper_sizes[i].height; h = paper_sizes[i].width; } else { w = paper_sizes[i].width; h = paper_sizes[i].height; } } else { w = s->val[BR_X].w - s->val[TL_X].w; h = s->val[BR_Y].w - s->val[TL_Y].w; } p->pixels_per_line = w * res / 25.4; p->lines = h * res / 25.4; } p->format = (!strcmp(s->val[MODE].s,SANE_VALUE_SCAN_MODE_COLOR)) ? SANE_FRAME_RGB : SANE_FRAME_GRAY; p->last_frame = SANE_TRUE; p->depth = bps_val[str_index (mode_list, s->val[MODE].s)]; p->bytes_per_line = p->depth * p->pixels_per_line / 8; if (p->depth > 8) p->depth = 8; if (params) memcpy (params, p, sizeof (SANE_Parameters)); return SANE_STATUS_GOOD; } backends-1.3.0/backend/kvs40xx.c000066400000000000000000000357331456256263500164000ustar00rootroot00000000000000/* Copyright (C) 2009, Panasonic Russia Ltd. Copyright (C) 2010,2011, m. allan noah */ /* Panasonic KV-S40xx USB-SCSI scanner driver. */ #include "../include/sane/config.h" #include /*isspace*/ #include /*tan*/ #include #include #include #define DEBUG_NOT_STATIC #include "../include/sane/sanei_backend.h" #include "../include/sane/sane.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei.h" #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_scsi.h" #include "lassert.h" #include "kvs40xx.h" #include "sane/sanei_debug.h" #define DATA_TAIL 0x200 struct known_device { const SANE_Int id; const SANE_Device scanner; }; static const struct known_device known_devices[] = { { KV_S4085C, { "MATSHITA", "KV-S4085C", "High Speed Color ADF Scanner", "scanner" }, }, { KV_S4065C, { "MATSHITA", "KV-S4065C", "High Speed Color ADF Scanner", "scanner" }, }, { KV_S7075C, { "MATSHITA", "KV-S7075C", "High Speed Color ADF Scanner", "scanner" }, }, }; static inline SANE_Status buf_init(struct buf *b, SANE_Int sz) { const int num = sz / BUF_SIZE + 1; b->buf = (u8 **) realloc(b->buf, num * sizeof(u8 *)); if (!b->buf) return SANE_STATUS_NO_MEM; memset(b->buf, 0, num * sizeof(void *)); b->size = b->head = b->tail = 0; b->sem = 0; b->st = SANE_STATUS_GOOD; pthread_cond_init(&b->cond, NULL); pthread_mutex_init(&b->mu, NULL); return SANE_STATUS_GOOD; } static inline void buf_deinit(struct buf *b) { int i; if (!b->buf) return; for (i = b->head; i < b->tail; i++) if (b->buf[i]) free(b->buf[i]); free(b->buf); b->buf = NULL; b->head = b->tail = 0; } static inline SANE_Status new_buf(struct buf *b, u8 ** p) { b->buf[b->tail] = (u8 *) malloc(BUF_SIZE); if (!b->buf[b->tail]) return SANE_STATUS_NO_MEM; *p = b->buf[b->tail]; ++b->tail; return SANE_STATUS_GOOD; } static inline SANE_Status buf_get_err(struct buf *b) { return b->size ? SANE_STATUS_GOOD : b->st; } static inline void buf_set_st(struct buf *b, SANE_Status st) { pthread_mutex_lock(&b->mu); b->st = st; if (buf_get_err(b)) pthread_cond_signal(&b->cond); pthread_mutex_unlock(&b->mu); } static inline void push_buf(struct buf *b, SANE_Int sz) { pthread_mutex_lock(&b->mu); b->sem++; b->size += sz; pthread_cond_signal(&b->cond); pthread_mutex_unlock(&b->mu); } static inline u8 *get_buf(struct buf *b, SANE_Int * sz) { SANE_Status err = buf_get_err(b); if (err) return NULL; pthread_mutex_lock(&b->mu); while (!b->sem && !buf_get_err(b)) pthread_cond_wait(&b->cond, &b->mu); b->sem--; err = buf_get_err(b); if (!err) { *sz = b->size < BUF_SIZE ? b->size : BUF_SIZE; b->size -= *sz; } pthread_mutex_unlock(&b->mu); return err ? NULL : b->buf[b->head]; } static inline void pop_buf(struct buf *b) { free(b->buf[b->head]); b->buf[b->head] = NULL; ++b->head; } SANE_Status sane_init (SANE_Int __sane_unused__ * version_code, SANE_Auth_Callback __sane_unused__ authorize) { DBG_INIT (); DBG (DBG_INFO, "This is panasonic kvs40xx driver\n"); *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 1); /* Initialize USB */ sanei_usb_init (); return SANE_STATUS_GOOD; } /* * List of available devices, allocated by sane_get_devices, released * by sane_exit() */ static SANE_Device **devlist = NULL; static unsigned curr_scan_dev = 0; void sane_exit (void) { if (devlist) { int i; for (i = 0; devlist[i]; i++) { free ((void *) devlist[i]); } free ((void *) devlist); devlist = NULL; } } SANE_Status attach (SANE_String_Const devname); SANE_Status attach (SANE_String_Const devname) { int i = 0; if (devlist) { for (; devlist[i]; i++); devlist = realloc (devlist, sizeof (SANE_Device *) * (i + 1)); if (!devlist) return SANE_STATUS_NO_MEM; } else { devlist = malloc (sizeof (SANE_Device *) * 2); if (!devlist) return SANE_STATUS_NO_MEM; } devlist[i] = malloc (sizeof (SANE_Device)); if (!devlist[i]) return SANE_STATUS_NO_MEM; memcpy (devlist[i], &known_devices[curr_scan_dev].scanner, sizeof (SANE_Device)); devlist[i]->name = strdup (devname); /* terminate device list with NULL entry: */ devlist[i + 1] = 0; DBG (DBG_INFO, "%s device attached\n", devname); return SANE_STATUS_GOOD; } /* Get device list */ SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only) { if (devlist) { int i; for (i = 0; devlist[i]; i++) { free ((void *) devlist[i]); } free ((void *) devlist); devlist = NULL; } for (curr_scan_dev = 0; curr_scan_dev < sizeof (known_devices) / sizeof (known_devices[0]); curr_scan_dev++) { sanei_usb_find_devices (PANASONIC_ID, known_devices[curr_scan_dev].id, attach); } for (curr_scan_dev = 0; curr_scan_dev < sizeof (known_devices) / sizeof (known_devices[0]); curr_scan_dev++) { sanei_scsi_find_devices (known_devices[curr_scan_dev]. scanner.vendor, known_devices[curr_scan_dev]. scanner.model, NULL, -1, -1, -1, -1, attach); } if(device_list) *device_list = (const SANE_Device **) devlist; return SANE_STATUS_GOOD; } /* Open device, return the device handle */ SANE_Status sane_open (SANE_String_Const devname, SANE_Handle * handle) { unsigned i, j, id = 0; struct scanner *s; SANE_Int h, bus; SANE_Status st = SANE_STATUS_GOOD; if (!devlist) { st = sane_get_devices (NULL, 0); if (st) return st; } for (i = 0; devlist[i]; i++) { if (!strcmp (devlist[i]->name, devname)) break; } if (!devlist[i]) return SANE_STATUS_INVAL; for (j = 0; j < sizeof (known_devices) / sizeof (known_devices[0]); j++) { if (!strcmp (devlist[i]->model, known_devices[j].scanner.model)) { id = known_devices[j].id; break; } } st = sanei_usb_open (devname, &h); if (st == SANE_STATUS_ACCESS_DENIED) return st; if (st) { st = sanei_scsi_open (devname, &h, kvs40xx_sense_handler, NULL); if (st) { return st; } bus = SCSI; } else { bus = USB; st = sanei_usb_claim_interface (h, 0); if (st) { sanei_usb_close (h); return st; } } s = malloc (sizeof (struct scanner)); if (!s) return SANE_STATUS_NO_MEM; memset (s, 0, sizeof (struct scanner)); s->buffer = malloc (MAX_READ_DATA_SIZE + BULK_HEADER_SIZE); if (!s->buffer) return SANE_STATUS_NO_MEM; s->file = h; s->bus = bus; s->id = id; strcpy (s->name, devname); *handle = s; for (i = 0; i < 3; i++) { st = kvs40xx_test_unit_ready (s); if (st) { if (s->bus == SCSI) { sanei_scsi_close (s->file); st = sanei_scsi_open (devname, &h, kvs40xx_sense_handler, NULL); if (st) return st; } else { sanei_usb_release_interface (s->file, 0); sanei_usb_close (s->file); st = sanei_usb_open (devname, &h); if (st) return st; st = sanei_usb_claim_interface (h, 0); if (st) { sanei_usb_close (h); return st; } } s->file = h; } else break; } if (i == 3) return SANE_STATUS_DEVICE_BUSY; if (id == KV_S4085C || id == KV_S4065C) { char str[16]; st = inquiry (s, str); if (st) goto err; if (id == KV_S4085C) s->id = !strcmp (str, "KV-S4085CL") ? KV_S4085CL : KV_S4085CW; else s->id = !strcmp (str, "KV-S4065CL") ? KV_S4065CL : KV_S4065CW; } kvs40xx_init_options (s); st = kvs40xx_set_timeout (s, s->val[FEED_TIMEOUT].w); if (st) goto err; return SANE_STATUS_GOOD; err: sane_close (s); return st; } /* Close device */ void sane_close (SANE_Handle handle) { struct scanner *s = (struct scanner *) handle; unsigned i; hopper_down (s); if (s->bus == USB) { sanei_usb_release_interface (s->file, 0); sanei_usb_close (s->file); } else sanei_scsi_close (s->file); for (i = 1; i < NUM_OPTIONS; i++) { if (s->opt[i].type == SANE_TYPE_STRING && s->val[i].s) free (s->val[i].s); } for (i = 0; i < sizeof (s->buf) / sizeof (s->buf[0]); i++) buf_deinit (&s->buf[i]); free (s->buffer); free (s); } /* Get option descriptor */ const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { struct scanner *s = handle; if ((unsigned) option >= NUM_OPTIONS || option < 0) return NULL; return s->opt + option; } static SANE_Status wait_document (struct scanner *s) { SANE_Status st; int i; if (!strcmp ("fb", s->val[SOURCE].s)) return SANE_STATUS_GOOD; if (!strcmp ("off", s->val[MANUALFEED].s)) return kvs40xx_document_exist (s); for (i = 0; i < s->val[FEED_TIMEOUT].w; i++) { st = kvs40xx_document_exist (s); if (st != SANE_STATUS_NO_DOCS) return st; sleep (1); } return SANE_STATUS_NO_DOCS; } static SANE_Status read_image_duplex(SANE_Handle handle) { struct scanner *s = (struct scanner *) handle; SANE_Status st = SANE_STATUS_GOOD; unsigned read, side; int i; struct side { unsigned mx, eof; u8 *p; struct buf *buf; } a[2], *b; for (i = 0; i < 2; i++) { a[i].mx = BUF_SIZE; a[i].eof = 0; a[i].buf = &s->buf[i]; st = new_buf(&s->buf[i], &a[i].p); if (st) goto err; } for (b = &a[0], side = SIDE_FRONT; (!a[0].eof || !a[1].eof);) { pthread_testcancel(); if (b->mx == 0) { push_buf(b->buf, BUF_SIZE); st = new_buf(b->buf, &b->p); if (st) goto err; b->mx = BUF_SIZE; } st = kvs40xx_read_image_data(s, s->page, side, b->p + BUF_SIZE - b->mx, b->mx, &read); b->mx -= read; if (st) { if (st != INCORRECT_LENGTH && st != SANE_STATUS_EOF) goto err; if (st == SANE_STATUS_EOF) { b->eof = 1; push_buf(b->buf, BUF_SIZE - b->mx); } side ^= SIDE_BACK; b = &a[side == SIDE_FRONT ? 0 : 1]; } } err: for (i = 0; i < 2; i++) buf_set_st(&s->buf[i], st); return st; } static SANE_Status read_image_simplex(SANE_Handle handle) { struct scanner *s = (struct scanner *) handle; SANE_Status st = SANE_STATUS_GOOD; for (; (!st || st == INCORRECT_LENGTH);) { unsigned read, mx; unsigned char *p = NULL; st = new_buf(&s->buf[0], &p); for (read = 0, mx = BUF_SIZE; mx && (!st || st == INCORRECT_LENGTH); mx -= read) { pthread_testcancel(); st = kvs40xx_read_image_data(s, s->page, SIDE_FRONT, p + BUF_SIZE - mx, mx, &read); } push_buf(&s->buf[0], BUF_SIZE - mx); } buf_set_st(&s->buf[0], st); return st; } static void * read_data (void *arg) { struct scanner *s = (struct scanner *) arg; SANE_Status st; int duplex = s->val[DUPLEX].w; s->read = 0; s->side = SIDE_FRONT; st = duplex ? read_image_duplex(s) : read_image_simplex(s); if (st && (st != SANE_STATUS_EOF)) goto err; st = kvs40xx_read_picture_element(s, SIDE_FRONT, &s->params); if (st) goto err; if (!s->params.lines) { st = SANE_STATUS_INVAL; goto err; } sane_get_parameters(s, NULL); s->page++; return NULL; err: s->scanning = 0; return NULL; } /* Start scanning */ SANE_Status sane_start (SANE_Handle handle) { struct scanner *s = (struct scanner *) handle; SANE_Status st = SANE_STATUS_GOOD; int duplex = s->val[DUPLEX].w, i; unsigned data_avalible; int start = 0; if (s->thread) { pthread_join (s->thread, NULL); s->thread = 0; } if (!s->scanning) { st = kvs40xx_test_unit_ready (s); if (st) return st; st = wait_document (s); if (st) return st; st = kvs40xx_reset_window (s); if (st) return st; st = kvs40xx_set_window (s, SIDE_FRONT); if (st) return st; if (duplex) { st = kvs40xx_set_window (s, SIDE_BACK); if (st) return st; } st = kvs40xx_scan (s); if (st) return st; if (s->val[CROP].b || s->val[LENGTHCTL].b || s->val[LONG_PAPER].b) { unsigned w, h, res = s->val[RESOLUTION].w; SANE_Parameters *p = &s->params; w = 297; /*A3 */ h = 420; p->pixels_per_line = w * res / 25.4 + .5; p->lines = h * res / 25.4 + .5; } else { st = kvs40xx_read_picture_element (s, SIDE_FRONT, &s->params); if (st) return st; } start = 1; s->scanning = 1; s->page = 0; s->read = 0; s->side = SIDE_FRONT; sane_get_parameters (s, NULL); } if (duplex && s->side == SIDE_FRONT && !start) { s->side = SIDE_BACK; s->read = 0; return SANE_STATUS_GOOD; } do { st = get_buffer_status(s, &data_avalible); if (st) goto err; } while (!data_avalible); for (i = 0; i < (duplex ? 2 : 1); i++) { st = buf_init (&s->buf[i], s->side_size); if (st) goto err; } if (pthread_create (&s->thread, NULL, read_data, s)) { st = SANE_STATUS_IO_ERROR; goto err; } if (s->val[CROP].b || s->val[LENGTHCTL].b || s->val[LONG_PAPER].b) { pthread_join (s->thread, NULL); s->thread = 0; } return SANE_STATUS_GOOD; err: s->scanning = 0; return st; } SANE_Status sane_read(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { struct scanner *s = (struct scanner *) handle; int duplex = s->val[DUPLEX].w; struct buf *b = s->side == SIDE_FRONT ? &s->buf[0] : &s->buf[1]; SANE_Status err = buf_get_err(b); SANE_Int inbuf = 0; *len = 0; if (!s->scanning) return SANE_STATUS_EOF; if (err) goto out; if (s->read) { *len = max_len < (SANE_Int) s->read ? max_len : (SANE_Int) s->read; memcpy(buf, s->data + BUF_SIZE - s->read, *len); s->read -= *len; if (!s->read) pop_buf(b); goto out; } s->data = get_buf(b, &inbuf); if (!s->data) goto out; *len = max_len < inbuf ? max_len : inbuf; if (*len > BUF_SIZE) *len = BUF_SIZE; memcpy(buf, s->data, *len); s->read = inbuf > BUF_SIZE ? BUF_SIZE - *len : inbuf - *len; if (!s->read) pop_buf(b); out: err = *len ? SANE_STATUS_GOOD : buf_get_err(b); if (err == SANE_STATUS_EOF) { if (strcmp(s->val[FEEDER_MODE].s, SANE_I18N("continuous"))) { if (!duplex || s->side == SIDE_BACK) s->scanning = 0; } buf_deinit(b); } else if (err) { unsigned i; for (i = 0; i < sizeof(s->buf) / sizeof(s->buf[0]); i++) buf_deinit(&s->buf[i]); } return err; } void sane_cancel (SANE_Handle handle) { unsigned i; struct scanner *s = (struct scanner *) handle; if (s->scanning && !strcmp (s->val[FEEDER_MODE].s, SANE_I18N ("continuous"))) { stop_adf (s); } if (s->thread) { pthread_cancel (s->thread); pthread_join (s->thread, NULL); s->thread = 0; } for (i = 0; i < sizeof (s->buf) / sizeof (s->buf[0]); i++) buf_deinit (&s->buf[i]); s->scanning = 0; } SANE_Status sane_set_io_mode (SANE_Handle __sane_unused__ h, SANE_Bool __sane_unused__ m) { return SANE_STATUS_UNSUPPORTED; } SANE_Status sane_get_select_fd (SANE_Handle __sane_unused__ h, SANE_Int __sane_unused__ * fd) { return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/kvs40xx.h000066400000000000000000000145471456256263500164050ustar00rootroot00000000000000#ifndef __KVS40XX_H #define __KVS40XX_H /* Copyright (C) 2009, Panasonic Russia Ltd. */ /* Panasonic KV-S40xx USB-SCSI scanner driver. */ #include "../include/sane/config.h" #include #ifdef HAVE_SYS_TYPES_H #include #endif #undef BACKEND_NAME #define BACKEND_NAME kvs40xx #define DBG_ERR 1 #define DBG_WARN 2 #define DBG_MSG 3 #define DBG_INFO 4 #define DBG_DBG 5 #define PANASONIC_ID 0x04da #define KV_S4085C 0x100c #define KV_S4065C 0x100d #define KV_S7075C 0x100e #define KV_S4085CL (KV_S4085C|0x10000) #define KV_S4085CW (KV_S4085C|0x20000) #define KV_S4065CL (KV_S4065C|0x10000) #define KV_S4065CW (KV_S4065C|0x20000) #define USB 1 #define SCSI 2 #define BULK_HEADER_SIZE 12 #define MAX_READ_DATA_SIZE (0x10000-0x100) #define BUF_SIZE MAX_READ_DATA_SIZE #define INCORRECT_LENGTH 0xfafafafa typedef unsigned char u8; typedef unsigned u32; typedef unsigned short u16; #define SIDE_FRONT 0x00 #define SIDE_BACK 0x80 /* options */ typedef enum { NUM_OPTS = 0, /* General options */ MODE_GROUP, MODE, /* scanner modes */ RESOLUTION, /* X and Y resolution */ SOURCE, DUPLEX, /* Duplex mode */ FEEDER_MODE, /* Feeder mode, fixed to Continuous */ LENGTHCTL, /* Length control mode */ LONG_PAPER, MANUALFEED, /* Manual feed mode */ FEED_TIMEOUT, /* Feed timeout */ DBLFEED, /* Double feed detection mode */ DFEED_SENCE, DFSTOP, DFEED_L, DFEED_C, DFEED_R, STAPELED_DOC, /* Detect stapled document */ FIT_TO_PAGE, /* Scanner shrinks image to fit scanned page */ /* Geometry group */ GEOMETRY_GROUP, PAPER_SIZE, /* Paper size */ LANDSCAPE, /* true if landscape */ TL_X, /* upper left X */ TL_Y, /* upper left Y */ BR_X, /* bottom right X */ BR_Y, /* bottom right Y */ ADVANCED_GROUP, BRIGHTNESS, /* Brightness */ CONTRAST, /* Contrast */ THRESHOLD, /* Binary threshold */ AUTOMATIC_THRESHOLD, WHITE_LEVEL, NOISE_REDUCTION, INVERSE, /* Monochrome reversing */ IMAGE_EMPHASIS, /* Image emphasis */ GAMMA_CORRECTION, /* Gamma correction */ LAMP, /* Lamp -- color drop out */ RED_CHROMA, BLUE_CHROMA, HALFTONE_PATTERN, /* Halftone pattern */ COMPRESSION, /* JPEG Compression */ COMPRESSION_PAR, /* Compression parameter */ DESKEW, STOP_SKEW, CROP, MIRROR, BTMPOS, TOPPOS, /* must come last: */ NUM_OPTIONS } KV_OPTION; struct buf { u8 **buf; volatile int head; volatile int tail; volatile unsigned size; volatile int sem; volatile SANE_Status st; pthread_mutex_t mu; pthread_cond_t cond; }; struct scanner { char name[128]; unsigned id; volatile int scanning; int page; int side; int bus; SANE_Int file; SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; SANE_Parameters params; u8 *buffer; struct buf buf[2]; u8 *data; unsigned side_size; unsigned read; pthread_t thread; }; struct window { u8 reserved[6]; u8 window_descriptor_block_length[2]; u8 window_identifier; u8 reserved2; u8 x_resolution[2]; u8 y_resolution[2]; u8 upper_left_x[4]; u8 upper_left_y[4]; u8 width[4]; u8 length[4]; u8 brightness; u8 threshold; u8 contrast; u8 image_composition; u8 bit_per_pixel; u8 halftone_pattern[2]; u8 rif_padding; /*RIF*/ u8 bit_ordering[2]; u8 compression_type; u8 compression_argument; u8 reserved4[6]; u8 vendor_unique_identifier; u8 nobuf_fstspeed_dfstop; u8 mirror_image; u8 image_emphasis; u8 gamma_correction; u8 mcd_lamp_dfeed_sens; u8 reserved5; /*rmoir*/ u8 document_size; u8 document_width[4]; u8 document_length[4]; u8 ahead_deskew_dfeed_scan_area_fspeed_rshad; u8 continuous_scanning_pages; u8 automatic_threshold_mode; u8 automatic_separation_mode; u8 standard_white_level_mode; u8 b_wnr_noise_reduction; u8 mfeed_toppos_btmpos_dsepa_hsepa_dcont_rstkr; u8 stop_mode; u8 red_chroma; u8 blue_chroma; }; struct support_info { /*TODO: */ unsigned char data[32]; }; void kvs40xx_init_options (struct scanner *); SANE_Status kvs40xx_test_unit_ready (struct scanner *s); SANE_Status kvs40xx_set_timeout (struct scanner *s, int timeout); void kvs40xx_init_window (struct scanner *s, struct window *wnd, int wnd_id); SANE_Status kvs40xx_set_window (struct scanner *s, int wnd_id); SANE_Status kvs40xx_reset_window (struct scanner *s); SANE_Status kvs40xx_read_picture_element (struct scanner *s, unsigned side, SANE_Parameters * p); SANE_Status read_support_info (struct scanner *s, struct support_info *inf); SANE_Status kvs40xx_read_image_data (struct scanner *s, unsigned page, unsigned side, void *buf, unsigned max_size, unsigned *size); SANE_Status kvs40xx_document_exist (struct scanner *s); SANE_Status get_buffer_status (struct scanner *s, unsigned *data_avalible); SANE_Status kvs40xx_scan (struct scanner *s); SANE_Status kvs40xx_sense_handler (int fd, u_char * sense_buffer, void *arg); SANE_Status stop_adf (struct scanner *s); SANE_Status hopper_down (struct scanner *s); SANE_Status inquiry (struct scanner *s, char *id); static inline u16 swap_bytes16 (u16 x) { return x << 8 | x >> 8; } static inline u32 swap_bytes32 (u32 x) { return x << 24 | x >> 24 | (x & (u32) 0x0000ff00UL) << 8 | (x & (u32) 0x00ff0000UL) >> 8; } static inline void copy16 (u8 * p, u16 x) { memcpy (p, (u8 *) &x, sizeof (x)); } static inline void copy32 (u8 * p, u32 x) { memcpy (p, (u8 *) &x, sizeof (x)); } #if WORDS_BIGENDIAN static inline void set24 (u8 * p, u32 x) { p[2] = x >> 16; p[1] = x >> 8; p[0] = x >> 0; } #define cpu2be16(x) (x) #define cpu2be32(x) (x) #define cpu2le16(x) swap_bytes16(x) #define cpu2le32(x) swap_bytes32(x) #define le2cpu16(x) swap_bytes16(x) #define le2cpu32(x) swap_bytes32(x) #define be2cpu16(x) (x) #define be2cpu32(x) (x) #define BIT_ORDERING 0 #elif __BYTE_ORDER == __LITTLE_ENDIAN static inline void set24 (u8 * p, u32 x) { p[0] = x >> 16; p[1] = x >> 8; p[2] = x >> 0; } #define cpu2le16(x) (x) #define cpu2le32(x) (x) #define cpu2be16(x) swap_bytes16(x) #define cpu2be32(x) swap_bytes32(x) #define le2cpu16(x) (x) #define le2cpu32(x) (x) #define be2cpu16(x) swap_bytes16(x) #define be2cpu32(x) swap_bytes32(x) #define BIT_ORDERING 1 #else #error __BYTE_ORDER not defined #endif static inline u32 get24 (u8 * p) { u32 x = (((u32) p[0]) << 16) | (((u32) p[1]) << 8) | (((u32) p[0]) << 0); return x; } #endif /*__KVS40XX_H*/ backends-1.3.0/backend/kvs40xx_cmd.c000066400000000000000000000270521456256263500172160ustar00rootroot00000000000000/* Copyright (C) 2009, Panasonic Russia Ltd. Copyright (C) 2010,2011, m. allan noah */ /* Panasonic KV-S40xx USB-SCSI scanner driver. */ #include "../include/sane/config.h" #include #define DEBUG_DECLARE_ONLY #define BACKEND_NAME kvs40xx #include "../include/sane/sanei_backend.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_scsi.h" #include "../include/sane/sanei_config.h" #include "kvs40xx.h" #include "../include/sane/sanei_debug.h" #define COMMAND_BLOCK 1 #define DATA_BLOCK 2 #define RESPONSE_BLOCK 3 #define COMMAND_CODE 0x9000 #define DATA_CODE 0xb000 #define RESPONSE_CODE 0xa000 #define STATUS_SIZE 4 struct bulk_header { u32 length; u16 type; u16 code; u32 transaction_id; }; #define TEST_UNIT_READY 0x00 #define INQUIRY 0x12 #define SET_WINDOW 0x24 #define SCAN 0x1B #define SEND_10 0x2A #define READ_10 0x28 #define REQUEST_SENSE 0x03 #define GET_BUFFER_STATUS 0x34 #define SET_TIMEOUT 0xE1 #define GET_ADJUST_DATA 0xE0 #define HOPPER_DOWN 0xE1 #define STOP_ADF 0xE1 #define SUPPORT_INFO 0x93 #define GOOD 0 #define CHECK_CONDITION 2 typedef enum { CMD_NONE = 0, CMD_IN = 0x81, /* scanner to pc */ CMD_OUT = 0x02 /* pc to scanner */ } CMD_DIRECTION; /* equals to endpoint address */ #define RESPONSE_SIZE 0x12 #define MAX_CMD_SIZE 12 struct cmd { unsigned char cmd[MAX_CMD_SIZE]; int cmd_size; void *data; int data_size; int dir; }; struct response { int status; unsigned char data[RESPONSE_SIZE]; }; static SANE_Status usb_send_command (struct scanner *s, struct cmd *c, struct response *r, void *buf) { SANE_Status st; struct bulk_header *h = (struct bulk_header *) buf; u8 resp[sizeof (*h) + STATUS_SIZE]; size_t sz = sizeof (*h) + MAX_CMD_SIZE; memset (h, 0, sz); h->length = cpu2be32 (sz); h->type = cpu2be16 (COMMAND_BLOCK); h->code = cpu2be16 (COMMAND_CODE); memcpy (h + 1, c->cmd, c->cmd_size); st = sanei_usb_write_bulk (s->file, (const SANE_Byte *) h, &sz); if (st) return st; if (sz != sizeof (*h) + MAX_CMD_SIZE) return SANE_STATUS_IO_ERROR; if (c->dir == CMD_IN) { unsigned l; sz = sizeof (*h) + c->data_size; c->data_size = 0; st = sanei_usb_read_bulk (s->file, (SANE_Byte *) h, &sz); for (l = sz; !st && l != be2cpu32 (h->length); l += sz) { DBG (DBG_WARN, "usb wrong read (%d instead %d)\n", c->data_size, be2cpu32 (h->length)); sz = be2cpu32 (h->length) - l; st = sanei_usb_read_bulk (s->file, ((SANE_Byte *) h) + l, &sz); } c->data = h + 1; if (st) { st = sanei_usb_release_interface (s->file, 0); if (st) return st; st = sanei_usb_claim_interface (s->file, 0); if (st) return st; r->status = CHECK_CONDITION; return SANE_STATUS_GOOD; } c->data_size = sz - sizeof (*h); } else if (c->dir == CMD_OUT) { sz = sizeof (*h) + c->data_size; memset (h, 0, sizeof (*h)); h->length = cpu2be32 (sizeof (*h) + c->data_size); h->type = cpu2be16 (DATA_BLOCK); h->code = cpu2be16 (DATA_CODE); memcpy (h + 1, c->data, c->data_size); st = sanei_usb_write_bulk (s->file, (const SANE_Byte *) h, &sz); if (st) return st; } sz = sizeof (resp); st = sanei_usb_read_bulk (s->file, resp, &sz); if (st || sz != sizeof (resp)) return SANE_STATUS_IO_ERROR; r->status = be2cpu32 (*((u32 *) (resp + sizeof (*h)))); return st; } #define END_OF_MEDIUM (1<<6) #define INCORRECT_LENGTH_INDICATOR (1<<5) static const struct { unsigned sense, asc, ascq; SANE_Status st; } s_errors[] = { { 2, 0, 0, SANE_STATUS_DEVICE_BUSY}, { 2, 4, 1, SANE_STATUS_DEVICE_BUSY}, { 2, 4, 0x80, SANE_STATUS_COVER_OPEN}, { 2, 4, 0x81, SANE_STATUS_COVER_OPEN}, { 2, 4, 0x82, SANE_STATUS_COVER_OPEN}, { 2, 4, 0x83, SANE_STATUS_COVER_OPEN}, { 2, 4, 0x84, SANE_STATUS_COVER_OPEN}, { 2, 0x80, 1, SANE_STATUS_CANCELLED}, { 2, 0x80, 2, SANE_STATUS_CANCELLED}, { 3, 0x3a, 0, SANE_STATUS_NO_DOCS}, { 3, 0x80, 1, SANE_STATUS_JAMMED}, { 3, 0x80, 2, SANE_STATUS_JAMMED}, { 3, 0x80, 3, SANE_STATUS_JAMMED}, { 3, 0x80, 4, SANE_STATUS_JAMMED}, { 3, 0x80, 5, SANE_STATUS_JAMMED}, { 3, 0x80, 6, SANE_STATUS_JAMMED}, { 3, 0x80, 7, SANE_STATUS_JAMMED}, { 3, 0x80, 8, SANE_STATUS_JAMMED}, { 3, 0x80, 9, SANE_STATUS_JAMMED}, { 3, 0x80, 0xa, SANE_STATUS_JAMMED}, { 3, 0x80, 0xb, SANE_STATUS_JAMMED}, { 3, 0x80, 0xc, SANE_STATUS_JAMMED}, { 3, 0x80, 0xd, SANE_STATUS_JAMMED}, { 3, 0x80, 0xe, SANE_STATUS_JAMMED}, { 3, 0x80, 0xf, SANE_STATUS_JAMMED}, { 3, 0x80, 0x10, SANE_STATUS_JAMMED}, { 3, 0x80, 0x11, SANE_STATUS_JAMMED}, { 5, 0x1a, 0x0, SANE_STATUS_INVAL}, { 5, 0x20, 0x0, SANE_STATUS_INVAL}, { 5, 0x24, 0x0, SANE_STATUS_INVAL}, { 5, 0x25, 0x0, SANE_STATUS_INVAL}, { 5, 0x26, 0x0, SANE_STATUS_INVAL}, { 5, 0x2c, 0x01, SANE_STATUS_INVAL}, { 5, 0x2c, 0x02, SANE_STATUS_INVAL}, { 5, 0x2c, 0x80, SANE_STATUS_INVAL}, { 5, 0x2c, 0x81, SANE_STATUS_INVAL}, { 5, 0x2c, 0x82, SANE_STATUS_INVAL}, { 5, 0x2c, 0x83, SANE_STATUS_INVAL},}; SANE_Status kvs40xx_sense_handler (int __sane_unused__ fd, u_char * sense_buffer, void __sane_unused__ * arg) { unsigned i; SANE_Status st = SANE_STATUS_GOOD; if (sense_buffer[2] & 0xf) { /*error */ for (i = 0; i < sizeof (s_errors) / sizeof (s_errors[0]); i++) { if ((sense_buffer[2] & 0xf) == s_errors[i].sense && sense_buffer[12] == s_errors[i].asc && sense_buffer[13] == s_errors[i].ascq) { st = s_errors[i].st; break; } } if (i == sizeof (s_errors) / sizeof (s_errors[0])) st = SANE_STATUS_IO_ERROR; } else { if (sense_buffer[2] & END_OF_MEDIUM) st = SANE_STATUS_EOF; else if (sense_buffer[2] & INCORRECT_LENGTH_INDICATOR) st = INCORRECT_LENGTH; } DBG (DBG_ERR, "send_command: CHECK_CONDITION: sense:0x%x ASC:0x%x ASCQ:0x%x\n", sense_buffer[2], sense_buffer[12], sense_buffer[13]); return st; } static SANE_Status send_command (struct scanner * s, struct cmd * c) { SANE_Status st = SANE_STATUS_GOOD; if (s->bus == USB) { struct response r; memset (&r, 0, sizeof (r)); st = usb_send_command (s, c, &r, s->buffer); if (st) return st; if (r.status) { u8 b[sizeof (struct bulk_header) + RESPONSE_SIZE]; struct cmd c2 = { {0}, 6, NULL, RESPONSE_SIZE, CMD_IN }; c2.cmd[0] = REQUEST_SENSE; c2.cmd[4] = RESPONSE_SIZE; st = usb_send_command (s, &c2, &r, b); if (st) return st; st = kvs40xx_sense_handler (0, b + sizeof (struct bulk_header), NULL); } } else { if (c->dir == CMD_OUT) { memcpy (s->buffer, c->cmd, c->cmd_size); memcpy (s->buffer + c->cmd_size, c->data, c->data_size); st = sanei_scsi_cmd (s->file, s->buffer, c->cmd_size + c->data_size, NULL, NULL); } else if (c->dir == CMD_IN) { c->data = s->buffer; st = sanei_scsi_cmd (s->file, c->cmd, c->cmd_size, c->data, (size_t *) & c->data_size); } else { st = sanei_scsi_cmd (s->file, c->cmd, c->cmd_size, NULL, NULL); } } return st; } SANE_Status kvs40xx_test_unit_ready (struct scanner * s) { struct cmd c = { {0}, 6, NULL, 0, CMD_NONE }; c.cmd[0] = TEST_UNIT_READY; if (send_command (s, &c)) return SANE_STATUS_DEVICE_BUSY; return SANE_STATUS_GOOD; } SANE_Status kvs40xx_set_timeout (struct scanner * s, int timeout) { u16 t = cpu2be16 ((u16) timeout); struct cmd c = { {0}, 10, NULL, 0, CMD_OUT }; c.data = &t; c.data_size = sizeof (t); c.cmd[0] = SET_TIMEOUT; c.cmd[2] = 0x8d; copy16 (c.cmd + 7, cpu2be16 (sizeof (t))); if (s->bus == USB) sanei_usb_set_timeout (timeout * 1000); return send_command (s, &c); } SANE_Status kvs40xx_set_window (struct scanner * s, int wnd_id) { struct window wnd; struct cmd c = { {0}, 10, NULL, 0, CMD_OUT }; c.data = &wnd; c.data_size = sizeof (wnd); c.cmd[0] = SET_WINDOW; copy16 (c.cmd + 7, cpu2be16 (sizeof (wnd))); kvs40xx_init_window (s, &wnd, wnd_id); return send_command (s, &c); } SANE_Status kvs40xx_reset_window (struct scanner * s) { struct cmd c = { {0}, 10, NULL, 0, CMD_NONE }; c.cmd[0] = SET_WINDOW; return send_command (s, &c); } SANE_Status kvs40xx_scan (struct scanner * s) { struct cmd c = { {0}, 6, NULL, 0, CMD_NONE }; c.cmd[0] = SCAN; return send_command (s, &c); } SANE_Status hopper_down (struct scanner * s) { struct cmd c = { {0}, 10, NULL, 0, CMD_NONE }; c.cmd[0] = HOPPER_DOWN; c.cmd[2] = 5; if (s->id == KV_S7075C) return SANE_STATUS_GOOD; return send_command (s, &c); } SANE_Status stop_adf (struct scanner * s) { struct cmd c = { {0}, 10, NULL, 0, CMD_NONE }; c.cmd[0] = STOP_ADF; c.cmd[2] = 0x8b; return send_command (s, &c); } SANE_Status kvs40xx_document_exist (struct scanner * s) { SANE_Status status; struct cmd c = { {0}, 10, NULL, 6, CMD_IN }; u8 *d; c.cmd[0] = READ_10; c.cmd[2] = 0x81; set24 (c.cmd + 6, c.data_size); status = send_command (s, &c); if (status) return status; d = c.data; if (d[0] & 0x20) return SANE_STATUS_GOOD; return SANE_STATUS_NO_DOCS; } SANE_Status kvs40xx_read_picture_element (struct scanner * s, unsigned side, SANE_Parameters * p) { SANE_Status status; struct cmd c = { {0}, 10, NULL, 16, CMD_IN }; u32 *data; c.cmd[0] = READ_10; c.cmd[2] = 0x80; c.cmd[5] = side; set24 (c.cmd + 6, c.data_size); status = send_command (s, &c); if (status) return status; data = (u32 *) c.data; p->pixels_per_line = be2cpu32 (data[0]); p->lines = be2cpu32 (data[1]); return SANE_STATUS_GOOD; } SANE_Status get_buffer_status (struct scanner * s, unsigned *data_avalible) { SANE_Status status; struct cmd c = { {0}, 10, NULL, 12, CMD_IN }; c.cmd[0] = GET_BUFFER_STATUS; c.cmd[7] = 12; status = send_command (s, &c); if (status) return status; *data_avalible = get24 ((unsigned char *)c.data + 9); return SANE_STATUS_GOOD; } SANE_Status kvs40xx_read_image_data (struct scanner * s, unsigned page, unsigned side, void *buf, unsigned max_size, unsigned *size) { SANE_Status status; struct cmd c = { {0}, 10, NULL, 0, CMD_IN }; c.data_size = max_size < MAX_READ_DATA_SIZE ? max_size : MAX_READ_DATA_SIZE; c.cmd[0] = READ_10; c.cmd[4] = page; c.cmd[5] = side; set24 (c.cmd + 6, c.data_size); *size = 0; status = send_command (s, &c); if (status && status != SANE_STATUS_EOF && status != INCORRECT_LENGTH) return status; *size = c.data_size; memcpy (buf, c.data, *size); return status; } SANE_Status read_support_info (struct scanner * s, struct support_info * inf) { SANE_Status st; struct cmd c = { {0}, 10, NULL, sizeof (*inf), CMD_IN }; c.cmd[0] = READ_10; c.cmd[2] = SUPPORT_INFO; set24 (c.cmd + 6, c.data_size); st = send_command (s, &c); if (st) return st; memcpy (inf, c.data, sizeof (*inf)); return SANE_STATUS_GOOD; } SANE_Status inquiry (struct scanner * s, char *id) { int i; SANE_Status st; struct cmd c = { {0}, 5, NULL, 0x60, CMD_IN }; c.cmd[0] = INQUIRY; c.cmd[4] = c.data_size; st = send_command (s, &c); if (st) return st; memcpy (id, (unsigned char *)c.data + 16, 16); for (i = 0; i < 15 && id[i] != ' '; i++); id[i] = 0; return SANE_STATUS_GOOD; } backends-1.3.0/backend/kvs40xx_opt.c000066400000000000000000001212341456256263500172520ustar00rootroot00000000000000/* Copyright (C) 2009, Panasonic Russia Ltd. Copyright (C) 2010,2011, m. allan noah */ /* Panasonic KV-S40xx USB-SCSI scanner driver. */ #include "../include/sane/config.h" #include #define DEBUG_DECLARE_ONLY #define BACKEND_NAME kvs40xx #include "../include/sane/sanei_backend.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei.h" #include "../include/sane/sanei_config.h" #include "lassert.h" #include "kvs40xx.h" #include "../include/sane/sanei_debug.h" #include static inline unsigned mm2scanner_units (unsigned mm) { return (mm * 12000 / 254.0 + .5); } struct restriction { unsigned ux, uy, ux_pix, uy_pix; }; static struct restriction flatbad = { 14064, 20400, 7031, 63999 }; static struct restriction cw = { 14268, 128000, 7133, 63999 }; static struct restriction cl = { 10724, 128000, 5361, 63999 }; static inline int check_area (struct scanner *s, unsigned ux, unsigned uy, unsigned bx, unsigned by) { int fb = !strcmp (s->val[SOURCE].s, SANE_I18N ("fb")); struct restriction *r = fb ? &flatbad : (s->id == KV_S4085CL || s->id == KV_S4065CL) ? &cl : &cw; unsigned res = s->val[RESOLUTION].w; unsigned w = bx - ux; unsigned h = by - uy; unsigned c1 = mm2scanner_units (ux + w); unsigned c2 = mm2scanner_units (uy + h); int c = c1 <= r->ux && c1 >= 16 && c2 >= 1 && c2 <= r->uy ? 0 : -1; if (c) return c; if (mm2scanner_units (ux) > r->ux) return -1; if (res * mm2scanner_units (ux) / 1200 > r->ux_pix) return -1; if (res * mm2scanner_units (uy) / 1200 > r->uy_pix) return -1; return 0; } 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; } static SANE_String_Const mode_list[] = { SANE_VALUE_SCAN_MODE_LINEART, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_COLOR, NULL }; static const unsigned mode_val[] = { 0, 2, 5 }; static const unsigned bps_val[] = { 1, 8, 24 }; static const SANE_Range resolutions_range = { 100,600,1 }; /* List of feeder modes */ static SANE_String_Const feeder_mode_list[] = { SANE_I18N ("single"), SANE_I18N ("continuous"), NULL }; /* List of scan sources */ static SANE_String_Const source_list[] = { SANE_I18N ("adf"), SANE_I18N ("fb"), NULL }; /* List of manual feed mode */ static SANE_String_Const manual_feed_list[] = { SANE_I18N ("off"), SANE_I18N ("wait_doc"), SANE_I18N ("wait_doc_hopper_up"), SANE_I18N ("wait_key"), NULL }; /* List of paper sizes */ static SANE_String_Const paper_list[] = { SANE_I18N ("user_def"), SANE_I18N ("business_card"), SANE_I18N ("Check"), SANE_I18N ("A3"), SANE_I18N ("A4"), SANE_I18N ("A5"), SANE_I18N ("A6"), SANE_I18N ("Letter"), SANE_I18N ("Double letter 11x17 in"), SANE_I18N ("B4"), SANE_I18N ("B5"), SANE_I18N ("B6"), SANE_I18N ("Legal"), NULL }; static SANE_String_Const paper_list_woA3[] = { SANE_I18N ("user_def"), SANE_I18N ("business_card"), SANE_I18N ("Check"), /*SANE_I18N ("A3"), */ SANE_I18N ("A4"), SANE_I18N ("A5"), SANE_I18N ("A6"), SANE_I18N ("Letter"), /*SANE_I18N ("Double letter 11x17 in"), */ /*SANE_I18N ("B4"), */ SANE_I18N ("B5"), SANE_I18N ("B6"), SANE_I18N ("Legal"), NULL }; static const unsigned paper_val[] = { 0, 1, 2, 3, 4, 5, 6, 7, 9, 12, 13, 14, 15 }; struct paper_size { int width; int height; }; static const struct paper_size paper_sizes[] = { {210, 297}, /* User defined, default=A4 */ {54, 90}, /* Business card */ {80, 170}, /* Check (China business) */ {297, 420}, /* A3 */ {210, 297}, /* A4 */ {148, 210}, /* A5 */ {105, 148}, /* A6 */ {215, 280}, /* US Letter 8.5 x 11 in */ {280, 432}, /* Double Letter 11 x 17 in */ {250, 353}, /* B4 */ {176, 250}, /* B5 */ {125, 176}, /* B6 */ {215, 355} /* US Legal */ }; #define MIN_WIDTH 48 #define MIN_LENGTH 70 #define MAX_WIDTH 297 #define MAX_LENGTH 432 #define MAX_WIDTH_A4 227 #define MAX_LENGTH_A4 432 static SANE_Range tl_x_range = { 0, MAX_WIDTH - MIN_WIDTH, 0 }; static SANE_Range tl_y_range = { 0, MAX_LENGTH - MIN_LENGTH, 0 }; static SANE_Range br_x_range = { MIN_WIDTH, MAX_WIDTH, 0 }; static SANE_Range br_y_range = { MIN_LENGTH, MAX_LENGTH, 0 }; static SANE_Range tl_x_range_A4 = { 0, MAX_WIDTH_A4 - MIN_WIDTH, 0 }; static SANE_Range tl_y_range_A4 = { 0, MAX_LENGTH_A4 - MIN_LENGTH, 0 }; static SANE_Range br_x_range_A4 = { MIN_WIDTH, MAX_WIDTH_A4, 0 }; static SANE_Range br_y_range_A4 = { MIN_LENGTH, MAX_LENGTH_A4, 0 }; static SANE_Range byte_value_range = { 0, 255, 0 }; static SANE_Range compression_value_range = { 1, 0x64, 0 }; /* List of image emphasis options, 5 steps */ static SANE_String_Const image_emphasis_list[] = { SANE_I18N ("none"), SANE_I18N ("low"), SANE_I18N ("medium"), SANE_I18N ("high"), SANE_I18N ("smooth"), NULL }; /* List of gamma */ static SANE_String_Const gamma_list[] = { SANE_I18N ("normal"), SANE_I18N ("crt"), NULL }; static unsigned gamma_val[] = { 0, 1 }; /* List of lamp color dropout */ static SANE_String_Const lamp_list[] = { SANE_I18N ("normal"), SANE_I18N ("red"), SANE_I18N ("green"), SANE_I18N ("blue"), NULL }; static SANE_String_Const dfeed_sence_list[] = { SANE_I18N ("Normal"), SANE_I18N ("High sensitivity"), SANE_I18N ("Low sensitivity"), NULL }; /* Lists of supported halftone. They are only valid with * for the Black&White mode. */ static SANE_String_Const halftone_pattern[] = { SANE_I18N ("bayer_64"), SANE_I18N ("bayer_16"), SANE_I18N ("halftone_32"), SANE_I18N ("halftone_64"), SANE_I18N ("err_diffusion"), NULL }; /* Stapled document */ static SANE_String_Const stapeled_list[] = { SANE_I18N ("No detection"), SANE_I18N ("Normal mode"), SANE_I18N ("Enhanced mode"), NULL }; /* List of automatic threshold options */ static SANE_String_Const automatic_threshold_list[] = { SANE_I18N ("normal"), SANE_I18N ("light"), SANE_I18N ("dark"), NULL }; static const int automatic_threshold_val[] = { 0, 0x11, 0x1f }; /* List of white level base. */ static SANE_String_Const white_level_list[] = { SANE_I18N ("From scanner"), SANE_I18N ("From paper"), SANE_I18N ("Automatic"), NULL }; static const int white_level_val[] = { 0x00, 0x80, 0x81 }; /* List of noise reduction options. */ static SANE_String_Const noise_reduction_list[] = { SANE_I18N ("default"), "1x1", "2x2", "3x3", "4x4", "5x5", NULL }; /* Reset the options for that scanner. */ void kvs40xx_init_options (struct scanner *s) { int i; SANE_Option_Descriptor *o; /* Pre-initialize the options. */ memset (s->opt, 0, sizeof (s->opt)); memset (s->val, 0, sizeof (s->val)); for (i = 0; i < NUM_OPTIONS; i++) { s->opt[i].size = sizeof (SANE_Word); s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } /* Number of options. */ o = &s->opt[NUM_OPTS]; o->name = ""; o->title = SANE_TITLE_NUM_OPTIONS; o->desc = SANE_DESC_NUM_OPTIONS; o->type = SANE_TYPE_INT; o->cap = SANE_CAP_SOFT_DETECT; s->val[NUM_OPTS].w = NUM_OPTIONS; /* Mode group */ o = &s->opt[MODE_GROUP]; o->title = SANE_I18N ("Scan Mode"); o->desc = ""; /* not valid for a group */ o->type = SANE_TYPE_GROUP; o->cap = 0; o->size = 0; o->constraint_type = SANE_CONSTRAINT_NONE; /* Scanner supported modes */ o = &s->opt[MODE]; o->name = SANE_NAME_SCAN_MODE; o->title = SANE_TITLE_SCAN_MODE; o->desc = SANE_DESC_SCAN_MODE; o->type = SANE_TYPE_STRING; o->size = max_string_size (mode_list); o->constraint_type = SANE_CONSTRAINT_STRING_LIST; o->constraint.string_list = mode_list; s->val[MODE].s = malloc (o->size); strcpy (s->val[MODE].s, mode_list[2]); /* X and Y resolution */ o = &s->opt[RESOLUTION]; o->name = SANE_NAME_SCAN_RESOLUTION; o->title = SANE_TITLE_SCAN_RESOLUTION; o->desc = SANE_DESC_SCAN_RESOLUTION; o->type = SANE_TYPE_INT; o->unit = SANE_UNIT_DPI; o->constraint_type = SANE_CONSTRAINT_RANGE; o->constraint.range = &resolutions_range; s->val[RESOLUTION].w = 100; /* Duplex */ o = &s->opt[DUPLEX]; o->name = "duplex"; o->title = SANE_I18N ("Duplex"); o->desc = SANE_I18N ("Enable Duplex (Dual-Sided) Scanning"); o->type = SANE_TYPE_BOOL; o->unit = SANE_UNIT_NONE; s->val[DUPLEX].w = SANE_FALSE; /*FIXME if (!s->support_info.support_duplex) o->cap |= SANE_CAP_INACTIVE; */ /* Feeder mode */ o = &s->opt[FEEDER_MODE]; o->name = "feeder-mode"; o->title = SANE_I18N ("Feeder mode"); o->desc = SANE_I18N ("Sets the feeding mode"); o->type = SANE_TYPE_STRING; o->size = max_string_size (feeder_mode_list); o->constraint_type = SANE_CONSTRAINT_STRING_LIST; o->constraint.string_list = feeder_mode_list; s->val[FEEDER_MODE].s = malloc (o->size); strcpy (s->val[FEEDER_MODE].s, feeder_mode_list[0]); /* Scan source */ o = &s->opt[SOURCE]; o->name = SANE_NAME_SCAN_SOURCE; o->title = SANE_TITLE_SCAN_SOURCE; o->desc = SANE_DESC_SCAN_SOURCE; o->type = SANE_TYPE_STRING; o->size = max_string_size (source_list); o->constraint_type = SANE_CONSTRAINT_STRING_LIST; o->constraint.string_list = source_list; s->val[SOURCE].s = malloc (o->size); strcpy (s->val[SOURCE].s, source_list[0]); if (s->id != KV_S7075C) o->cap |= SANE_CAP_INACTIVE; /* Length control */ o = &s->opt[LENGTHCTL]; o->name = "length-control"; o->title = SANE_I18N ("Length control mode"); o->desc = SANE_I18N ("Length Control Mode causes the scanner to read the shorter of either the length of the actual" " paper or logical document length"); o->type = SANE_TYPE_BOOL; o->unit = SANE_UNIT_NONE; s->val[LENGTHCTL].w = SANE_FALSE; o = &s->opt[LONG_PAPER]; o->name = "long-paper"; o->title = SANE_I18N ("Long paper mode"); o->desc = SANE_I18N ("Long Paper Mode is a mode that the scanner " "reads the image after it divides long paper " "by the length which is set in Document Size option."); o->type = SANE_TYPE_BOOL; o->unit = SANE_UNIT_NONE; s->val[LONG_PAPER].w = SANE_FALSE; o->cap |= SANE_CAP_INACTIVE; /* Manual feed */ o = &s->opt[MANUALFEED]; o->name = "manual-feed"; o->title = SANE_I18N ("Manual feed mode"); o->desc = SANE_I18N ("Sets the manual feed mode"); o->type = SANE_TYPE_STRING; o->size = max_string_size (manual_feed_list); o->constraint_type = SANE_CONSTRAINT_STRING_LIST; o->constraint.string_list = manual_feed_list; s->val[MANUALFEED].s = malloc (o->size); strcpy (s->val[MANUALFEED].s, manual_feed_list[0]); /*Manual feed timeout */ o = &s->opt[FEED_TIMEOUT]; o->name = "feed-timeout"; o->title = SANE_I18N ("Manual feed timeout"); o->desc = SANE_I18N ("Sets the manual feed timeout in seconds"); o->type = SANE_TYPE_INT; o->unit = SANE_UNIT_NONE; o->size = sizeof (SANE_Int); o->constraint_type = SANE_CONSTRAINT_RANGE; o->constraint.range = &(byte_value_range); o->cap |= SANE_CAP_INACTIVE; s->val[FEED_TIMEOUT].w = 30; /* Double feed */ o = &s->opt[DBLFEED]; o->name = "dfeed"; o->title = SANE_I18N ("Double feed detection"); o->desc = SANE_I18N ("Enable/Disable double feed detection"); o->type = SANE_TYPE_BOOL; o->unit = SANE_UNIT_NONE; s->val[DBLFEED].w = SANE_FALSE; o = &s->opt[DFEED_SENCE]; o->name = "dfeed-sense"; o->title = SANE_I18N ("Double feed detector sensitivity"); o->desc = SANE_I18N ("Set the double feed detector sensitivity"); o->type = SANE_TYPE_STRING; o->size = max_string_size (dfeed_sence_list); o->constraint_type = SANE_CONSTRAINT_STRING_LIST; o->constraint.string_list = dfeed_sence_list; s->val[DFEED_SENCE].s = malloc (o->size); strcpy (s->val[DFEED_SENCE].s, dfeed_sence_list[0]); o->cap |= SANE_CAP_INACTIVE; o = &s->opt[DFSTOP]; o->name = "dfstop"; o->title = SANE_I18N ("Do not stop after double feed detection"); o->desc = SANE_I18N ("Do not stop after double feed detection"); o->type = SANE_TYPE_BOOL; o->unit = SANE_UNIT_NONE; s->val[DFSTOP].w = SANE_FALSE; o->cap |= SANE_CAP_INACTIVE; o = &s->opt[DFEED_L]; o->name = "dfeed_l"; o->title = SANE_I18N ("Ignore left double feed sensor"); o->desc = SANE_I18N ("Ignore left double feed sensor"); o->type = SANE_TYPE_BOOL; o->unit = SANE_UNIT_NONE; s->val[DFEED_L].w = SANE_FALSE; o->cap |= SANE_CAP_INACTIVE; o = &s->opt[DFEED_C]; o->name = "dfeed_c"; o->title = SANE_I18N ("Ignore center double feed sensor"); o->desc = SANE_I18N ("Ignore center double feed sensor"); o->type = SANE_TYPE_BOOL; o->unit = SANE_UNIT_NONE; s->val[DFEED_C].w = SANE_FALSE; o->cap |= SANE_CAP_INACTIVE; o = &s->opt[DFEED_R]; o->name = "dfeed_r"; o->title = SANE_I18N ("Ignore right double feed sensor"); o->desc = SANE_I18N ("Ignore right double feed sensor"); o->type = SANE_TYPE_BOOL; o->unit = SANE_UNIT_NONE; s->val[DFEED_R].w = SANE_FALSE; o->cap |= SANE_CAP_INACTIVE; /* Fit to page */ o = &s->opt[FIT_TO_PAGE]; o->name = SANE_I18N ("fit-to-page"); o->title = SANE_I18N ("Fit to page"); o->desc = SANE_I18N ("Scanner shrinks image to fit scanned page"); o->type = SANE_TYPE_BOOL; o->unit = SANE_UNIT_NONE; s->val[FIT_TO_PAGE].w = SANE_FALSE; /* Geometry group */ o = &s->opt[GEOMETRY_GROUP]; o->title = SANE_I18N ("Geometry"); o->desc = ""; /* not valid for a group */ o->type = SANE_TYPE_GROUP; o->cap = 0; o->size = 0; o->constraint_type = SANE_CONSTRAINT_NONE; /* Paper sizes list */ o = &s->opt[PAPER_SIZE]; o->name = "paper-size"; o->title = SANE_I18N ("Paper size"); o->desc = SANE_I18N ("Physical size of the paper in the ADF"); o->type = SANE_TYPE_STRING; o->constraint.string_list = s->id == KV_S4085CL || s->id == KV_S4065CL ? paper_list_woA3 : paper_list; o->size = max_string_size (o->constraint.string_list); o->constraint_type = SANE_CONSTRAINT_STRING_LIST; s->val[PAPER_SIZE].s = malloc (o->size); strcpy (s->val[PAPER_SIZE].s, SANE_I18N ("A4")); /* Landscape */ o = &s->opt[LANDSCAPE]; o->name = "landscape"; o->title = SANE_I18N ("Landscape"); o->desc = SANE_I18N ("Set paper position : " "true for landscape, false for portrait"); o->type = SANE_TYPE_BOOL; o->unit = SANE_UNIT_NONE; s->val[LANDSCAPE].w = SANE_FALSE; /* Upper left X */ o = &s->opt[TL_X]; o->name = SANE_NAME_SCAN_TL_X; o->title = SANE_TITLE_SCAN_TL_X; o->desc = SANE_DESC_SCAN_TL_X; o->type = SANE_TYPE_INT; o->unit = SANE_UNIT_MM; o->constraint_type = SANE_CONSTRAINT_RANGE; o->constraint.range = (s->id == KV_S4085CL || s->id == KV_S4065CL) ? &tl_x_range_A4 : &tl_x_range; o->cap |= SANE_CAP_INACTIVE; s->val[TL_X].w = 0; /* Upper left Y */ o = &s->opt[TL_Y]; o->name = SANE_NAME_SCAN_TL_Y; o->title = SANE_TITLE_SCAN_TL_Y; o->desc = SANE_DESC_SCAN_TL_Y; o->type = SANE_TYPE_INT; o->unit = SANE_UNIT_MM; o->constraint_type = SANE_CONSTRAINT_RANGE; o->constraint.range = (s->id == KV_S4085CL || s->id == KV_S4065CL) ? &tl_y_range_A4 : &tl_y_range; o->cap |= SANE_CAP_INACTIVE; s->val[TL_Y].w = 0; /* Bottom-right x */ o = &s->opt[BR_X]; o->name = SANE_NAME_SCAN_BR_X; o->title = SANE_TITLE_SCAN_BR_X; o->desc = SANE_DESC_SCAN_BR_X; o->type = SANE_TYPE_INT; o->unit = SANE_UNIT_MM; o->constraint_type = SANE_CONSTRAINT_RANGE; o->constraint.range = (s->id == KV_S4085CL || s->id == KV_S4065CL) ? &br_x_range_A4 : &br_x_range; o->cap |= SANE_CAP_INACTIVE; s->val[BR_X].w = 210; /* Bottom-right y */ o = &s->opt[BR_Y]; o->name = SANE_NAME_SCAN_BR_Y; o->title = SANE_TITLE_SCAN_BR_Y; o->desc = SANE_DESC_SCAN_BR_Y; o->type = SANE_TYPE_INT; o->unit = SANE_UNIT_MM; o->constraint_type = SANE_CONSTRAINT_RANGE; o->constraint.range = (s->id == KV_S4085CL || s->id == KV_S4065CL) ? &br_y_range_A4 : &br_y_range; o->cap |= SANE_CAP_INACTIVE; s->val[BR_Y].w = 297; /* Enhancement group */ o = &s->opt[ADVANCED_GROUP]; o->title = SANE_I18N ("Advanced"); o->desc = ""; /* not valid for a group */ o->type = SANE_TYPE_GROUP; o->cap = SANE_CAP_ADVANCED; o->size = 0; o->constraint_type = SANE_CONSTRAINT_NONE; /* Brightness */ o = &s->opt[BRIGHTNESS]; o->name = SANE_NAME_BRIGHTNESS; o->title = SANE_TITLE_BRIGHTNESS; o->desc = SANE_DESC_BRIGHTNESS; o->type = SANE_TYPE_INT; o->unit = SANE_UNIT_NONE; o->size = sizeof (SANE_Int); o->constraint_type = SANE_CONSTRAINT_RANGE; o->constraint.range = &(byte_value_range); s->val[BRIGHTNESS].w = 128; /* Contrast */ o = &s->opt[CONTRAST]; o->name = SANE_NAME_CONTRAST; o->title = SANE_TITLE_CONTRAST; o->desc = SANE_DESC_CONTRAST; o->type = SANE_TYPE_INT; o->unit = SANE_UNIT_NONE; o->size = sizeof (SANE_Int); o->constraint_type = SANE_CONSTRAINT_RANGE; o->constraint.range = &(byte_value_range); s->val[CONTRAST].w = 128; /* threshold */ o = &s->opt[THRESHOLD]; o->name = SANE_NAME_THRESHOLD; o->title = SANE_TITLE_THRESHOLD; o->desc = SANE_DESC_THRESHOLD; o->type = SANE_TYPE_INT; o->size = sizeof (SANE_Int); o->constraint_type = SANE_CONSTRAINT_RANGE; o->constraint.range = &(byte_value_range); s->val[THRESHOLD].w = 128; o->cap |= SANE_CAP_INACTIVE; o = &s->opt[AUTOMATIC_THRESHOLD]; o->name = "athreshold"; o->title = SANE_I18N ("Automatic threshold mode"); o->desc = SANE_I18N ("Sets the automatic threshold mode"); o->type = SANE_TYPE_STRING; o->size = max_string_size (automatic_threshold_list); o->constraint_type = SANE_CONSTRAINT_STRING_LIST; o->constraint.string_list = automatic_threshold_list; s->val[AUTOMATIC_THRESHOLD].s = malloc (o->size); strcpy (s->val[AUTOMATIC_THRESHOLD].s, automatic_threshold_list[0]); o->cap |= SANE_CAP_INACTIVE; /* Image emphasis */ o = &s->opt[IMAGE_EMPHASIS]; o->name = "image-emphasis"; o->title = SANE_I18N ("Image emphasis"); o->desc = SANE_I18N ("Sets the image emphasis"); o->type = SANE_TYPE_STRING; o->size = max_string_size (image_emphasis_list); o->constraint_type = SANE_CONSTRAINT_STRING_LIST; o->constraint.string_list = image_emphasis_list; s->val[IMAGE_EMPHASIS].s = malloc (o->size); strcpy (s->val[IMAGE_EMPHASIS].s, image_emphasis_list[0]);; o->cap |= SANE_CAP_INACTIVE; /* Gamma */ o = &s->opt[GAMMA_CORRECTION]; o->name = "gamma-cor"; o->title = SANE_I18N ("Gamma correction"); o->desc = SANE_I18N ("Gamma correction"); o->type = SANE_TYPE_STRING; o->size = max_string_size (gamma_list); o->constraint_type = SANE_CONSTRAINT_STRING_LIST; o->constraint.string_list = gamma_list; s->val[GAMMA_CORRECTION].s = malloc (o->size); strcpy (s->val[GAMMA_CORRECTION].s, gamma_list[0]); o->cap |= SANE_CAP_INACTIVE; /* Lamp color dropout */ o = &s->opt[LAMP]; o->name = "lamp-color"; o->title = SANE_I18N ("Lamp color"); o->desc = SANE_I18N ("Sets the lamp color (color dropout)"); o->type = SANE_TYPE_STRING; o->size = max_string_size (lamp_list); o->constraint_type = SANE_CONSTRAINT_STRING_LIST; o->constraint.string_list = lamp_list; s->val[LAMP].s = malloc (o->size); strcpy (s->val[LAMP].s, lamp_list[0]); /* Inverse image */ o = &s->opt[INVERSE]; o->name = "inverse"; o->title = SANE_I18N ("Inverse Image"); o->desc = SANE_I18N ("Inverse image in B/W mode"); o->type = SANE_TYPE_BOOL; o->unit = SANE_UNIT_NONE; o->cap |= SANE_CAP_INACTIVE; /* Halftone pattern */ o = &s->opt[HALFTONE_PATTERN]; o->name = SANE_NAME_HALFTONE_PATTERN; o->title = SANE_TITLE_HALFTONE_PATTERN; o->desc = SANE_DESC_HALFTONE_PATTERN; o->type = SANE_TYPE_STRING; o->size = max_string_size (halftone_pattern); o->constraint_type = SANE_CONSTRAINT_STRING_LIST; o->constraint.string_list = halftone_pattern; s->val[HALFTONE_PATTERN].s = malloc (o->size); strcpy (s->val[HALFTONE_PATTERN].s, halftone_pattern[0]); o->cap |= SANE_CAP_INACTIVE; /* JPEG Compression */ o = &s->opt[COMPRESSION]; o->name = "jpeg"; o->title = SANE_I18N ("JPEG compression"); o->desc = SANE_I18N ("JPEG compression (your application must be able to uncompress)"); o->type = SANE_TYPE_BOOL; o->unit = SANE_UNIT_NONE; /* Compression parameter */ o = &s->opt[COMPRESSION_PAR]; o->name = "comp_arg"; o->title = "Compression Argument"; o->desc = "Compression Argument (Q parameter for JPEG)"; o->type = SANE_TYPE_INT; o->size = sizeof (SANE_Int); o->constraint_type = SANE_CONSTRAINT_RANGE; o->constraint.range = &(compression_value_range); s->val[COMPRESSION_PAR].w = 0x4b; o->cap |= SANE_CAP_INACTIVE; /* Stapled document */ o = &s->opt[STAPELED_DOC]; o->name = "stapeled_doc"; o->title = SANE_I18N ("Detect stapled document"); o->desc = SANE_I18N ("Detect stapled document"); o->type = SANE_TYPE_STRING; o->size = max_string_size (stapeled_list); o->constraint_type = SANE_CONSTRAINT_STRING_LIST; o->constraint.string_list = stapeled_list; s->val[STAPELED_DOC].s = malloc (o->size); strcpy (s->val[STAPELED_DOC].s, stapeled_list[0]); if (s->id == KV_S7075C) o->cap |= SANE_CAP_INACTIVE; /* White level base */ o = &s->opt[WHITE_LEVEL]; o->name = SANE_NAME_WHITE_LEVEL; o->title = SANE_TITLE_WHITE_LEVEL; o->desc = SANE_DESC_WHITE_LEVEL; o->type = SANE_TYPE_STRING; o->size = max_string_size (white_level_list); o->constraint_type = SANE_CONSTRAINT_STRING_LIST; o->constraint.string_list = white_level_list; s->val[WHITE_LEVEL].s = malloc (o->size); strcpy (s->val[WHITE_LEVEL].s, white_level_list[0]); o->cap |= SANE_CAP_INACTIVE; /* Noise reduction */ o = &s->opt[NOISE_REDUCTION]; o->name = "noise-reduction"; o->title = SANE_I18N ("Noise reduction"); o->desc = SANE_I18N ("Reduce the isolated dot noise"); o->type = SANE_TYPE_STRING; o->size = max_string_size (noise_reduction_list); o->constraint_type = SANE_CONSTRAINT_STRING_LIST; o->constraint.string_list = noise_reduction_list; s->val[NOISE_REDUCTION].s = malloc (o->size); strcpy (s->val[NOISE_REDUCTION].s, noise_reduction_list[0]); o->cap |= SANE_CAP_INACTIVE; o = &s->opt[RED_CHROMA]; o->name = "red-chroma"; o->title = SANE_I18N ("chroma of red"); o->desc = SANE_I18N ("Set chroma of red"); o->type = SANE_TYPE_INT; o->unit = SANE_UNIT_NONE; o->size = sizeof (SANE_Int); o->constraint_type = SANE_CONSTRAINT_RANGE; o->constraint.range = &(byte_value_range); s->val[RED_CHROMA].w = 0; o = &s->opt[BLUE_CHROMA]; o->name = "blue chroma"; o->title = SANE_I18N ("chroma of blue"); o->desc = SANE_I18N ("Set chroma of blue"); o->type = SANE_TYPE_INT; o->unit = SANE_UNIT_NONE; o->size = sizeof (SANE_Int); o->constraint_type = SANE_CONSTRAINT_RANGE; o->constraint.range = &(byte_value_range); s->val[BLUE_CHROMA].w = 0; o = &s->opt[DESKEW]; o->name = "deskew"; o->title = SANE_I18N ("Skew adjustment"); o->desc = SANE_I18N ("Skew adjustment"); o->type = SANE_TYPE_BOOL; o->unit = SANE_UNIT_NONE; s->val[DESKEW].w = SANE_FALSE; if (s->id != KV_S4085CL && s->id != KV_S4085CW) o->cap |= SANE_CAP_INACTIVE; o = &s->opt[STOP_SKEW]; o->name = "stop-skew"; o->title = SANE_I18N ("Stop scanner if a sheet is skewed"); o->desc = SANE_I18N ("Scanner will stop if a sheet is skewed"); o->type = SANE_TYPE_BOOL; o->unit = SANE_UNIT_NONE; s->val[STOP_SKEW].w = SANE_FALSE; o = &s->opt[CROP]; o->name = "crop"; o->title = SANE_I18N ("Crop actual image area"); o->desc = SANE_I18N ("Scanner will automatically detect image area and crop to it"); o->type = SANE_TYPE_BOOL; o->unit = SANE_UNIT_NONE; s->val[CROP].w = SANE_FALSE; if (s->id != KV_S4085CL && s->id != KV_S4085CW) o->cap |= SANE_CAP_INACTIVE; o = &s->opt[MIRROR]; o->name = "mirror"; o->title = SANE_I18N ("Mirror image"); o->desc = SANE_I18N ("Left/right mirror image"); o->type = SANE_TYPE_BOOL; o->unit = SANE_UNIT_NONE; s->val[MIRROR].w = SANE_FALSE; o = &s->opt[TOPPOS]; o->name = "toppos"; o->title = SANE_I18N ("Addition of space in top position"); o->desc = SANE_I18N ("Addition of space in top position"); o->type = SANE_TYPE_BOOL; o->unit = SANE_UNIT_NONE; s->val[TOPPOS].w = SANE_FALSE; o = &s->opt[BTMPOS]; o->name = "btmpos"; o->title = SANE_I18N ("Addition of space in bottom position"); o->desc = SANE_I18N ("Addition of space in bottom position"); o->type = SANE_TYPE_BOOL; o->unit = SANE_UNIT_NONE; s->val[BTMPOS].w = SANE_FALSE; } /* Lookup a string list from one array and return its index. */ static int str_index (const SANE_String_Const * list, SANE_String_Const name) { int index; index = 0; while (list[index]) { if (!strcmp (list[index], name)) return (index); index++; } return (-1); /* not found */ } /* Control option */ SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { int i; SANE_Status status; SANE_Word cap; struct scanner *s = (struct scanner *) handle; if (info) *info = 0; if (option < 0 || option >= NUM_OPTIONS) return SANE_STATUS_UNSUPPORTED; cap = s->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) return SANE_STATUS_UNSUPPORTED; if (action == SANE_ACTION_GET_VALUE) { if (s->opt[option].type == SANE_TYPE_STRING) { DBG (DBG_INFO, "sane_control_option: reading opt[%d] = %s\n", option, s->val[option].s); strcpy (val, s->val[option].s); } else { *(SANE_Word *) val = s->val[option].w; DBG (DBG_INFO, "sane_control_option: reading opt[%d] = %d\n", option, s->val[option].w); } return SANE_STATUS_GOOD; } else if (action == SANE_ACTION_SET_VALUE) { if (!SANE_OPTION_IS_SETTABLE (cap)) return SANE_STATUS_INVAL; status = sanei_constrain_value (s->opt + option, val, info); if (status != SANE_STATUS_GOOD) return status; if (s->opt[option].type == SANE_TYPE_STRING) { if (!strcmp (val, s->val[option].s)) return SANE_STATUS_GOOD; DBG (DBG_INFO, "sane_control_option: writing opt[%d] = %s\n", option, (SANE_String_Const) val); } else { if (*(SANE_Word *) val == s->val[option].w) return SANE_STATUS_GOOD; DBG (DBG_INFO, "sane_control_option: writing opt[%d] = %d\n", option, *(SANE_Word *) val); } switch (option) { /* Side-effect options */ case RESOLUTION: s->val[option].w = *(SANE_Word *) val; if (info) *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case TL_Y: if ((*(SANE_Word *) val) + MIN_LENGTH <= s->val[BR_Y].w && !check_area (s, s->val[TL_X].w, *(SANE_Word *) val, s->val[BR_X].w, s->val[BR_Y].w)) { s->val[option].w = *(SANE_Word *) val; if (info) *info |= SANE_INFO_RELOAD_PARAMS; } else if (info) *info |= SANE_INFO_INEXACT | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case BR_Y: if ((*(SANE_Word *) val) >= s->val[TL_Y].w + MIN_LENGTH && !check_area (s, s->val[TL_X].w, s->val[TL_Y].w, s->val[BR_X].w, *(SANE_Word *) val)) { s->val[option].w = *(SANE_Word *) val; if (info) *info |= SANE_INFO_RELOAD_PARAMS; } else if (info) *info |= SANE_INFO_INEXACT | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case TL_X: if ((*(SANE_Word *) val) + MIN_WIDTH <= s->val[BR_X].w && !check_area (s, *(SANE_Word *) val, s->val[TL_Y].w, s->val[BR_X].w, s->val[BR_Y].w)) { s->val[option].w = *(SANE_Word *) val; if (info) *info |= SANE_INFO_RELOAD_PARAMS; } else if (info) *info |= SANE_INFO_INEXACT | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case BR_X: if (*(SANE_Word *) val >= s->val[TL_X].w + MIN_WIDTH && !check_area (s, s->val[TL_X].w, s->val[TL_Y].w, *(SANE_Word *) val, s->val[BR_Y].w)) { s->val[option].w = *(SANE_Word *) val; if (info) *info |= SANE_INFO_RELOAD_PARAMS; } else if (info) *info |= SANE_INFO_INEXACT | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case LANDSCAPE: s->val[option].w = *(SANE_Word *) val; if (info) *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; /* Side-effect free options */ case CONTRAST: case BRIGHTNESS: case DUPLEX: case LENGTHCTL: case LONG_PAPER: case FIT_TO_PAGE: case THRESHOLD: case INVERSE: case COMPRESSION_PAR: case DFSTOP: case DFEED_L: case DFEED_C: case DFEED_R: case STOP_SKEW: case DESKEW: case MIRROR: case CROP: case TOPPOS: case BTMPOS: case RED_CHROMA: case BLUE_CHROMA: s->val[option].w = *(SANE_Word *) val; return SANE_STATUS_GOOD; case FEED_TIMEOUT: s->val[option].w = *(SANE_Word *) val; return kvs40xx_set_timeout (s, s->val[option].w); /* String mode */ case IMAGE_EMPHASIS: case GAMMA_CORRECTION: case LAMP: case HALFTONE_PATTERN: case DFEED_SENCE: case AUTOMATIC_THRESHOLD: case WHITE_LEVEL: case NOISE_REDUCTION: strcpy (s->val[option].s, val); return SANE_STATUS_GOOD; case SOURCE: strcpy (s->val[option].s, val); if (strcmp (s->val[option].s, SANE_I18N ("adf"))) { strcpy (s->val[FEEDER_MODE].s, feeder_mode_list[0]); strcpy (s->val[MANUALFEED].s, manual_feed_list[0]); s->val[DUPLEX].w = SANE_FALSE; s->val[DBLFEED].w = SANE_FALSE; s->val[BTMPOS].w = SANE_FALSE; s->val[TOPPOS].w = SANE_FALSE; s->val[STOP_SKEW].w = SANE_FALSE; s->val[LENGTHCTL].w = SANE_FALSE; s->val[LONG_PAPER].w = SANE_FALSE; s->opt[FEEDER_MODE].cap |= SANE_CAP_INACTIVE; s->opt[MANUALFEED].cap |= SANE_CAP_INACTIVE; s->opt[DUPLEX].cap |= SANE_CAP_INACTIVE; s->opt[DBLFEED].cap |= SANE_CAP_INACTIVE; s->opt[BTMPOS].cap |= SANE_CAP_INACTIVE; s->opt[TOPPOS].cap |= SANE_CAP_INACTIVE; s->opt[STOP_SKEW].cap |= SANE_CAP_INACTIVE; s->opt[LENGTHCTL].cap |= SANE_CAP_INACTIVE; s->opt[LONG_PAPER].cap |= SANE_CAP_INACTIVE; } else { s->opt[FEEDER_MODE].cap &= ~SANE_CAP_INACTIVE; s->opt[MANUALFEED].cap &= ~SANE_CAP_INACTIVE; s->opt[DUPLEX].cap &= ~SANE_CAP_INACTIVE; s->opt[DBLFEED].cap &= ~SANE_CAP_INACTIVE; s->opt[BTMPOS].cap &= ~SANE_CAP_INACTIVE; s->opt[TOPPOS].cap &= ~SANE_CAP_INACTIVE; s->opt[STOP_SKEW].cap &= ~SANE_CAP_INACTIVE; s->opt[LENGTHCTL].cap &= ~SANE_CAP_INACTIVE; s->opt[LONG_PAPER].cap &= ~SANE_CAP_INACTIVE; } if (info) *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case FEEDER_MODE: strcpy (s->val[option].s, val); if (strcmp (s->val[option].s, SANE_I18N ("continuous"))) { s->opt[LONG_PAPER].cap |= SANE_CAP_INACTIVE; } else { s->opt[LONG_PAPER].cap &= ~SANE_CAP_INACTIVE; } if (info) *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case MODE: strcpy (s->val[option].s, val); if (!strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_LINEART)) { s->opt[GAMMA_CORRECTION].cap |= SANE_CAP_INACTIVE; s->opt[COMPRESSION].cap |= SANE_CAP_INACTIVE; s->opt[COMPRESSION_PAR].cap |= SANE_CAP_INACTIVE; s->opt[THRESHOLD].cap &= ~SANE_CAP_INACTIVE; s->opt[HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE; s->opt[AUTOMATIC_THRESHOLD].cap &= ~SANE_CAP_INACTIVE; s->opt[WHITE_LEVEL].cap &= ~SANE_CAP_INACTIVE; s->opt[NOISE_REDUCTION].cap &= ~SANE_CAP_INACTIVE; s->opt[INVERSE].cap &= ~SANE_CAP_INACTIVE; s->opt[RED_CHROMA].cap |= SANE_CAP_INACTIVE; s->opt[BLUE_CHROMA].cap |= SANE_CAP_INACTIVE; } else { s->opt[COMPRESSION].cap &= ~SANE_CAP_INACTIVE; s->opt[COMPRESSION_PAR].cap &= ~SANE_CAP_INACTIVE; s->opt[THRESHOLD].cap |= SANE_CAP_INACTIVE; s->opt[INVERSE].cap |= SANE_CAP_INACTIVE; s->opt[HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; s->opt[AUTOMATIC_THRESHOLD].cap |= SANE_CAP_INACTIVE; s->opt[WHITE_LEVEL].cap |= SANE_CAP_INACTIVE; s->opt[NOISE_REDUCTION].cap |= SANE_CAP_INACTIVE; s->opt[RED_CHROMA].cap &= ~SANE_CAP_INACTIVE; s->opt[BLUE_CHROMA].cap &= ~SANE_CAP_INACTIVE; } if (!strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_GRAY)) { s->opt[INVERSE].cap &= ~SANE_CAP_INACTIVE; s->opt[GAMMA_CORRECTION].cap &= ~SANE_CAP_INACTIVE; } else { s->opt[GAMMA_CORRECTION].cap |= SANE_CAP_INACTIVE; } if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case MANUALFEED: strcpy (s->val[option].s, val); if (strcmp (s->val[option].s, manual_feed_list[0]) == 0) /* off */ s->opt[FEED_TIMEOUT].cap |= SANE_CAP_INACTIVE; else s->opt[FEED_TIMEOUT].cap &= ~SANE_CAP_INACTIVE; if (info) *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case STAPELED_DOC: strcpy (s->val[option].s, val); if (strcmp (s->val[option].s, stapeled_list[0]) == 0) { s->opt[DBLFEED].cap &= ~SANE_CAP_INACTIVE; s->opt[DFSTOP].cap &= ~SANE_CAP_INACTIVE; s->opt[DFEED_L].cap &= ~SANE_CAP_INACTIVE; s->opt[DFEED_C].cap &= ~SANE_CAP_INACTIVE; s->opt[DFEED_C].cap &= ~SANE_CAP_INACTIVE; s->opt[DFEED_R].cap &= ~SANE_CAP_INACTIVE; s->opt[DFEED_SENCE].cap &= ~SANE_CAP_INACTIVE; } else { s->opt[DBLFEED].cap |= SANE_CAP_INACTIVE; s->opt[DFSTOP].cap |= SANE_CAP_INACTIVE; s->opt[DFEED_L].cap |= SANE_CAP_INACTIVE; s->opt[DFEED_C].cap |= SANE_CAP_INACTIVE; s->opt[DFEED_R].cap |= SANE_CAP_INACTIVE; s->opt[DFEED_SENCE].cap |= SANE_CAP_INACTIVE; } if (info) *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case DBLFEED: s->val[option].w = *(SANE_Word *) val; if (!s->val[option].b) { s->opt[DFSTOP].cap |= SANE_CAP_INACTIVE; s->opt[DFEED_L].cap |= SANE_CAP_INACTIVE; s->opt[DFEED_C].cap |= SANE_CAP_INACTIVE; s->opt[DFEED_R].cap |= SANE_CAP_INACTIVE; s->opt[DFEED_SENCE].cap |= SANE_CAP_INACTIVE; } else { s->opt[DFSTOP].cap &= ~SANE_CAP_INACTIVE; s->opt[DFEED_L].cap &= ~SANE_CAP_INACTIVE; s->opt[DFEED_C].cap &= ~SANE_CAP_INACTIVE; s->opt[DFEED_C].cap &= ~SANE_CAP_INACTIVE; s->opt[DFEED_R].cap &= ~SANE_CAP_INACTIVE; s->opt[DFEED_SENCE].cap &= ~SANE_CAP_INACTIVE; } if (info) *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case PAPER_SIZE: strcpy (s->val[option].s, val); i = str_index (paper_list, s->val[option].s); if (i == 0) { /*user def */ s->opt[TL_X].cap &= s->opt[TL_Y].cap &= s->opt[BR_X].cap &= s->opt[BR_Y].cap &= ~SANE_CAP_INACTIVE; s->opt[LANDSCAPE].cap |= SANE_CAP_INACTIVE; s->val[LANDSCAPE].w = 0; } else { s->opt[TL_X].cap |= s->opt[TL_Y].cap |= s->opt[BR_X].cap |= s->opt[BR_Y].cap |= SANE_CAP_INACTIVE; if ( /*i == 4 || */ i == 5 || i == 6 /*XXX*/ || i == 10 || i == 11) { /*A4, A5, A6, B5, B6 */ if ((s->id == KV_S4085CL || s->id == KV_S4065CL) && i == 4 && i == 10) { /*A4, B5 */ s->opt[LANDSCAPE].cap |= SANE_CAP_INACTIVE; s->val[LANDSCAPE].w = 0; } else s->opt[LANDSCAPE].cap &= ~SANE_CAP_INACTIVE; } else { s->opt[LANDSCAPE].cap |= SANE_CAP_INACTIVE; s->val[LANDSCAPE].w = 0; } } if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case COMPRESSION: s->val[option].w = *(SANE_Word *) val; if (!s->val[option].b) { s->opt[COMPRESSION_PAR].cap |= SANE_CAP_INACTIVE; } else { s->opt[COMPRESSION_PAR].cap &= ~SANE_CAP_INACTIVE; } if (info) *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; } } return SANE_STATUS_UNSUPPORTED; } void kvs40xx_init_window (struct scanner *s, struct window *wnd, int wnd_id) { int paper = str_index (paper_list, s->val[PAPER_SIZE].s), i; memset (wnd, 0, sizeof (struct window)); copy16 (wnd->window_descriptor_block_length, cpu2be16 (66)); wnd->window_identifier = wnd_id; copy16 (wnd->x_resolution, cpu2be16 (s->val[RESOLUTION].w)); copy16 (wnd->y_resolution, cpu2be16 (s->val[RESOLUTION].w)); if (!paper) { copy32 (wnd->upper_left_x, cpu2be32 (mm2scanner_units (s->val[TL_X].w))); copy32 (wnd->upper_left_y, cpu2be32 (mm2scanner_units (s->val[TL_Y].w))); copy32 (wnd->document_width, cpu2be32 (mm2scanner_units (s->val[BR_X].w))); copy32 (wnd->width, cpu2be32 (mm2scanner_units (s->val[BR_X].w - s->val[TL_X].w))); copy32 (wnd->document_length, cpu2be32 (mm2scanner_units (s->val[BR_Y].w))); copy32 (wnd->length, cpu2be32 (mm2scanner_units (s->val[BR_Y].w - s->val[TL_Y].w))); } else { u32 w = cpu2be32 (mm2scanner_units (paper_sizes[paper].width)); u32 h = cpu2be32 (mm2scanner_units (paper_sizes[paper].height)); copy32 (wnd->upper_left_x, cpu2be32 (mm2scanner_units (0))); copy32 (wnd->upper_left_y, cpu2be32 (mm2scanner_units (0))); if (!s->val[LANDSCAPE].b) { copy32 (wnd->width, w); copy32 (wnd->length, h); copy32 (wnd->document_width, w); copy32 (wnd->document_length, h); } else { copy32 (wnd->width, h); copy32 (wnd->length, w); copy32 (wnd->document_width, h); copy32 (wnd->document_length, w); } } wnd->brightness = s->val[BRIGHTNESS].w; wnd->threshold = s->val[THRESHOLD].w; wnd->contrast = s->val[CONTRAST].w; wnd->image_composition = mode_val[str_index (mode_list, s->val[MODE].s)]; wnd->bit_per_pixel = bps_val[str_index (mode_list, s->val[MODE].s)]; copy16 (wnd->halftone_pattern, cpu2be16 (str_index (halftone_pattern, s->val[HALFTONE_PATTERN].s))); wnd->rif_padding = s->val[INVERSE].b << 7; copy16 (wnd->bit_ordering, cpu2be16 (BIT_ORDERING)); wnd->compression_type = s->val[COMPRESSION].b ? 0x81 : 0; wnd->compression_argument = s->val[COMPRESSION_PAR].w; wnd->vendor_unique_identifier = 0; wnd->nobuf_fstspeed_dfstop = str_index (source_list, s->val[SOURCE].s) << 7 | str_index (stapeled_list, s->val[STAPELED_DOC].s) << 5 | s->val[STOP_SKEW].b << 4 | s->val[CROP].b << 3 | s->val[DFSTOP].b << 0; wnd->mirror_image = s->val[MIRROR].b << 7 | s->val[DFEED_L].b << 2 | s->val[DFEED_C].b << 1 | s->val[DFEED_R].b << 0; wnd->image_emphasis = str_index (image_emphasis_list, s->val[IMAGE_EMPHASIS].s); wnd->gamma_correction = gamma_val[str_index (gamma_list, s->val[GAMMA_CORRECTION].s)]; wnd->mcd_lamp_dfeed_sens = str_index (lamp_list, s->val[LAMP].s) << 4 | str_index (dfeed_sence_list, s->val[DFEED_SENCE].s); wnd->document_size = ((paper != 0) << 7) | (s->val[LENGTHCTL].b << 6) | (s->val[LONG_PAPER].b << 5) | (s->val[LANDSCAPE].b << 4) | paper_val[paper]; wnd->ahead_deskew_dfeed_scan_area_fspeed_rshad = (s->val[DESKEW].b || s->val[CROP].b ? 2 : 0) << 5 | /*XXX*/ s->val[DBLFEED].b << 4 | s->val[FIT_TO_PAGE].b << 2; wnd->continuous_scanning_pages = str_index (feeder_mode_list, s->val[FEEDER_MODE].s) ? 0xff : 0; wnd->automatic_threshold_mode = automatic_threshold_val [str_index (automatic_threshold_list, s->val[AUTOMATIC_THRESHOLD].s)]; wnd->automatic_separation_mode = 0; /*Does not supported */ wnd->standard_white_level_mode = white_level_val[str_index (white_level_list, s->val[WHITE_LEVEL].s)]; wnd->b_wnr_noise_reduction = str_index (noise_reduction_list, s->val[NOISE_REDUCTION].s); i = str_index (manual_feed_list, s->val[MANUALFEED].s); wnd->mfeed_toppos_btmpos_dsepa_hsepa_dcont_rstkr = i << 6 | s->val[TOPPOS].b << 5 | s->val[BTMPOS].b << 4; wnd->stop_mode = 1; wnd->red_chroma = s->val[RED_CHROMA].w; wnd->blue_chroma = s->val[BLUE_CHROMA].w; } /* Get scan parameters */ SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { struct scanner *s = (struct scanner *) handle; SANE_Parameters *p = &s->params; if (!s->scanning) { unsigned w, h, res = s->val[RESOLUTION].w; unsigned i = str_index (paper_list, s->val[PAPER_SIZE].s); if (i) { if (s->val[LANDSCAPE].b) { w = paper_sizes[i].height; h = paper_sizes[i].width; } else { w = paper_sizes[i].width; h = paper_sizes[i].height; } } else { w = s->val[BR_X].w - s->val[TL_X].w; h = s->val[BR_Y].w - s->val[TL_Y].w; } p->pixels_per_line = w * res / 25.4 + .5; p->lines = h * res / 25.4 + .5; } p->format = !strcmp (s->val[MODE].s, SANE_VALUE_SCAN_MODE_COLOR) ? SANE_FRAME_RGB : SANE_FRAME_GRAY; p->last_frame = SANE_TRUE; p->depth = bps_val[str_index (mode_list, s->val[MODE].s)]; p->bytes_per_line = p->depth * p->pixels_per_line / 8; if (p->depth > 8) p->depth = 8; if (params) memcpy (params, p, sizeof (SANE_Parameters)); s->side_size = p->bytes_per_line * p->lines; return SANE_STATUS_GOOD; } backends-1.3.0/backend/leo.c000066400000000000000000001423731456256263500156270ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2002-2003 Frank Zago (sane at zago dot net) 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, see . 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. */ /* Across FS-1130 */ /*--------------------------------------------------------------------------*/ #define BUILD 11 /* 2004/06/30 */ #define BACKEND_NAME leo #define LEO_CONFIG_FILE "leo.conf" /*--------------------------------------------------------------------------*/ #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_scsi.h" #include "../include/sane/sanei_debug.h" #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_config.h" #include "../include/lassert.h" #include "leo.h" /*--------------------------------------------------------------------------*/ /* Lists of possible scan modes. */ static SANE_String_Const scan_mode_list[] = { BLACK_WHITE_STR, GRAY_STR, COLOR_STR, NULL }; /*--------------------------------------------------------------------------*/ /* Minimum and maximum width and length supported. */ static SANE_Range x_range = { SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0 }; static SANE_Range y_range = { SANE_FIX (0), SANE_FIX (11.5 * MM_PER_INCH), 0 }; /*--------------------------------------------------------------------------*/ static const SANE_Range gamma_range = { 0, /* minimum */ 255, /* maximum */ 0 /* quantization */ }; /*--------------------------------------------------------------------------*/ static SANE_String_Const halftone_pattern_list[] = { SANE_I18N ("None"), SANE_I18N ("Diamond"), SANE_I18N ("8x8 Coarse Fatting"), SANE_I18N ("8x8 Fine Fatting"), SANE_I18N ("8x8 Bayer"), SANE_I18N ("8x8 Vertical Line"), NULL }; static const halftone_pattern_t *const halftone_pattern_val[] = { NULL, &haltfone_pattern_diamond, &haltfone_pattern_8x8_Coarse_Fatting, &haltfone_pattern_8x8_Fine_Fatting, &haltfone_pattern_8x8_Bayer, &haltfone_pattern_8x8_Vertical_Line }; /*--------------------------------------------------------------------------*/ /* Define the supported scanners and their characteristics. */ static const struct scanners_supported scanners[] = { {6, "ACROSS ", " ", "Across", "FS-1130"}, {6, "LEO ", "LEOScan-S3 ", "Leo", "S3"}, {6, "LEO", "LEOScan-S3", "Leo", "S3"}, {6, "KYE CORP", "ColorPage-CS ", "Genius", "FS1130"} }; /*--------------------------------------------------------------------------*/ /* List of scanner attached. */ static Leo_Scanner *first_dev = NULL; static int num_devices = 0; static const SANE_Device **devlist = NULL; /* Local functions. */ /* Display a buffer in the log. */ static void hexdump (int level, const char *comment, unsigned char *p, int l) { int i; char line[128]; char *ptr; char asc_buf[17]; char *asc_ptr; DBG (level, "%s\n", comment); ptr = line; *ptr = '\0'; asc_ptr = asc_buf; *asc_ptr = '\0'; for (i = 0; i < l; i++, p++) { if ((i % 16) == 0) { if (ptr != line) { DBG (level, "%s %s\n", line, asc_buf); ptr = line; *ptr = '\0'; asc_ptr = asc_buf; *asc_ptr = '\0'; } sprintf (ptr, "%3.3d:", i); ptr += 4; } ptr += sprintf (ptr, " %2.2x", *p); if (*p >= 32 && *p <= 127) { asc_ptr += sprintf (asc_ptr, "%c", *p); } else { asc_ptr += sprintf (asc_ptr, "."); } } *ptr = '\0'; DBG (level, "%s %s\n", line, asc_buf); } /* Returns the length of the longest string, including the terminating * character. */ static size_t max_string_size (SANE_String_Const strings[]) { size_t size, max_size = 0; int i; for (i = 0; strings[i]; ++i) { size = strlen (strings[i]) + 1; if (size > max_size) { max_size = size; } } return max_size; } /* Lookup a string list from one array and return its index. */ static int get_string_list_index (SANE_String_Const list[], SANE_String_Const name) { int index; index = 0; while (list[index] != NULL) { if (strcmp (list[index], name) == 0) { return (index); } index++; } DBG (DBG_error, "name %s not found in list\n", name); assert (0 == 1); /* bug in backend, core dump */ return (-1); } /* Initialize a scanner entry. Return an allocated scanner with some * preset values. */ static Leo_Scanner * leo_init (void) { Leo_Scanner *dev; DBG (DBG_proc, "leo_init: enter\n"); /* Allocate a new scanner entry. */ dev = malloc (sizeof (Leo_Scanner)); if (dev == NULL) { return NULL; } memset (dev, 0, sizeof (Leo_Scanner)); /* Allocate the buffer used to transfer the SCSI data. */ dev->buffer_size = 64 * 1024; dev->buffer = malloc (dev->buffer_size); if (dev->buffer == NULL) { free (dev); return NULL; } /* Allocate a buffer to store the temporary image. */ dev->image_size = 64 * 1024; /* enough for 1 line at max res */ dev->image = malloc (dev->image_size); if (dev->image == NULL) { free (dev->buffer); free (dev); return NULL; } dev->sfd = -1; DBG (DBG_proc, "leo_init: exit\n"); return (dev); } /* Closes an open scanner. */ static void leo_close (Leo_Scanner * dev) { DBG (DBG_proc, "leo_close: enter\n"); if (dev->sfd != -1) { sanei_scsi_close (dev->sfd); dev->sfd = -1; } DBG (DBG_proc, "leo_close: exit\n"); } /* Frees the memory used by a scanner. */ static void leo_free (Leo_Scanner * dev) { int i; DBG (DBG_proc, "leo_free: enter\n"); if (dev == NULL) return; leo_close (dev); if (dev->devicename) { free (dev->devicename); } if (dev->buffer) { free (dev->buffer); } if (dev->image) { free (dev->image); } for (i = 1; i < OPT_NUM_OPTIONS; i++) { if (dev->opt[i].type == SANE_TYPE_STRING && dev->val[i].s) { free (dev->val[i].s); } } free (dev); DBG (DBG_proc, "leo_free: exit\n"); } /* Inquiry a device and returns TRUE if is supported. */ static int leo_identify_scanner (Leo_Scanner * dev) { CDB cdb; SANE_Status status; size_t size; int i; DBG (DBG_proc, "leo_identify_scanner: enter\n"); size = 5; MKSCSI_INQUIRY (cdb, size); status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, dev->buffer, &size); if (status) { DBG (DBG_error, "leo_identify_scanner: inquiry failed with status %s\n", sane_strstatus (status)); return (SANE_FALSE); } size = dev->buffer[4] + 5; /* total length of the inquiry data */ if (size < 36) { DBG (DBG_error, "leo_identify_scanner: not enough data to identify device\n"); return (SANE_FALSE); } MKSCSI_INQUIRY (cdb, size); status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, dev->buffer, &size); if (status) { DBG (DBG_error, "leo_identify_scanner: inquiry failed with status %s\n", sane_strstatus (status)); return (SANE_FALSE); } dev->scsi_type = dev->buffer[0] & 0x1f; memcpy (dev->scsi_vendor, dev->buffer + 0x08, 0x08); dev->scsi_vendor[0x08] = 0; memcpy (dev->scsi_product, dev->buffer + 0x10, 0x010); dev->scsi_product[0x10] = 0; memcpy (dev->scsi_version, dev->buffer + 0x20, 0x04); dev->scsi_version[0x04] = 0; DBG (DBG_info, "device is \"%s\" \"%s\" \"%s\"\n", dev->scsi_vendor, dev->scsi_product, dev->scsi_version); /* Lookup through the supported scanners table to find if this * backend supports that one. */ for (i = 0; i < NELEMS (scanners); i++) { if (dev->scsi_type == scanners[i].scsi_type && strcmp (dev->scsi_vendor, scanners[i].scsi_vendor) == 0 && strcmp (dev->scsi_product, scanners[i].scsi_product) == 0) { DBG (DBG_error, "leo_identify_scanner: scanner supported\n"); /* Get the full inquiry, since that scanner does not fill the length correctly. */ size = 0x30; MKSCSI_INQUIRY (cdb, size); status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, dev->buffer, &size); if (status != SANE_STATUS_GOOD) { return (SANE_FALSE); } hexdump (DBG_info2, "inquiry", dev->buffer, size); dev->def = &(scanners[i]); dev->res_range.min = 1; dev->res_range.max = B16TOI (&dev->buffer[42]); dev->x_resolution_max = B16TOI (&dev->buffer[40]); dev->y_resolution_max = B16TOI (&dev->buffer[42]); return (SANE_TRUE); } } DBG (DBG_proc, "leo_identify_scanner: exit, device not supported\n"); return (SANE_FALSE); } /* SCSI sense handler. Callback for SANE. */ static SANE_Status leo_sense_handler (int scsi_fd, unsigned char *result, void __sane_unused__ *arg) { int asc, ascq, sensekey; int len; DBG (DBG_proc, "leo_sense_handler (scsi_fd = %d)\n", scsi_fd); sensekey = get_RS_sense_key (result); len = 7 + get_RS_additional_length (result); hexdump (DBG_info2, "sense", result, len); if (get_RS_error_code (result) != 0x70) { DBG (DBG_error, "leo_sense_handler: invalid sense key error code (%d)\n", get_RS_error_code (result)); return SANE_STATUS_IO_ERROR; } if (get_RS_ILI (result) != 0) { DBG (DBG_sense, "leo_sense_handler: short read\n"); } if (len < 14) { DBG (DBG_error, "leo_sense_handler: sense too short, no ASC/ASCQ\n"); return SANE_STATUS_IO_ERROR; } asc = get_RS_ASC (result); ascq = get_RS_ASCQ (result); DBG (DBG_sense, "leo_sense_handler: sense=%d, ASC/ASCQ=%02x%02x\n", sensekey, asc, ascq); switch (sensekey) { case 0x00: /* no sense */ case 0x02: /* not ready */ case 0x03: /* medium error */ case 0x05: case 0x06: break; } DBG (DBG_sense, "leo_sense_handler: unknown error condition. Please report it to the backend maintainer\n"); return SANE_STATUS_IO_ERROR; } /* Set a window. */ static SANE_Status leo_set_window (Leo_Scanner * dev) { size_t size; CDB cdb; unsigned char window[48]; SANE_Status status; DBG (DBG_proc, "leo_set_window: enter\n"); size = sizeof (window); MKSCSI_SET_WINDOW (cdb, size); memset (window, 0, size); /* size of the windows descriptor block */ window[7] = sizeof (window) - 8; window[1] = sizeof (window) - 2; /* X and Y resolution */ Ito16 (dev->x_resolution, &window[10]); Ito16 (dev->y_resolution, &window[12]); /* Upper Left (X,Y) */ Ito32 (dev->x_tl, &window[14]); Ito32 (dev->y_tl, &window[18]); /* Width and length */ Ito32 (dev->width, &window[22]); Ito32 (dev->length, &window[26]); /* Image Composition */ switch (dev->scan_mode) { case LEO_BW: window[33] = 0x00; break; case LEO_HALFTONE: window[33] = 0x01; break; case LEO_GRAYSCALE: window[33] = 0x02; break; case LEO_COLOR: window[33] = 0x05; break; } /* Depth */ window[34] = dev->depth; /* Unknown - invariants */ window[31] = 0x80; window[43] = 0x01; hexdump (DBG_info2, "windows", window, sizeof (window)); status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, window, sizeof (window), NULL, NULL); DBG (DBG_proc, "leo_set_window: exit, status=%d\n", status); return status; } /* Read the size of the scan. */ static SANE_Status leo_get_scan_size (Leo_Scanner * dev) { size_t size; CDB cdb; SANE_Status status; DBG (DBG_proc, "leo_get_scan_size: enter\n"); size = 0x10; MKSCSI_GET_DATA_BUFFER_STATUS (cdb, 1, size); hexdump (DBG_info2, "CDB:", cdb.data, cdb.len); status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, dev->buffer, &size); if (size != 0x10) { DBG (DBG_error, "leo_get_scan_size: GET DATA BUFFER STATUS returned an invalid size (%ld)\n", (long) size); return SANE_STATUS_IO_ERROR; } hexdump (DBG_info2, "leo_get_scan_size return", dev->buffer, size); dev->params.pixels_per_line = B16TOI (&dev->buffer[14]); /* The number of lines if the number of lines left plus the number * of lines already waiting in the buffer. */ dev->params.lines = B16TOI (&dev->buffer[12]) + (B24TOI (&dev->buffer[9]) / dev->params.bytes_per_line); switch (dev->scan_mode) { case LEO_BW: case LEO_HALFTONE: dev->params.pixels_per_line &= ~0x7; dev->params.bytes_per_line = dev->params.pixels_per_line / 8; break; case LEO_GRAYSCALE: dev->params.bytes_per_line = dev->params.pixels_per_line; break; case LEO_COLOR: dev->params.bytes_per_line = dev->params.pixels_per_line * 3; break; } DBG (DBG_proc, "leo_get_scan_size: exit, status=%d\n", status); DBG (DBG_proc, "lines=%d, bpl=%d\n", dev->params.lines, dev->params.bytes_per_line); return (status); } /* Return the number of byte that can be read. */ static SANE_Status get_filled_data_length (Leo_Scanner * dev, size_t * to_read) { size_t size; CDB cdb; SANE_Status status; DBG (DBG_proc, "get_filled_data_length: enter\n"); *to_read = 0; size = 0x10; MKSCSI_GET_DATA_BUFFER_STATUS (cdb, 1, size); status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, dev->buffer, &size); if (size != 0x10) { DBG (DBG_error, "get_filled_data_length: GET DATA BUFFER STATUS returned an invalid size (%ld)\n", (long) size); return SANE_STATUS_IO_ERROR; } hexdump (DBG_info2, "get_filled_data_length return", dev->buffer, size); *to_read = B24TOI (&dev->buffer[9]); DBG (DBG_info, "get_filled_data_length: to read = %ld\n", (long) *to_read); DBG (DBG_proc, "get_filled_data_length: exit, status=%d\n", status); return (status); } /* Start a scan. */ static SANE_Status leo_scan (Leo_Scanner * dev) { CDB cdb; SANE_Status status; DBG (DBG_proc, "leo_scan: enter\n"); MKSCSI_SCAN (cdb); status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, NULL, NULL); DBG (DBG_proc, "leo_scan: exit, status=%d\n", status); return status; } /* Attach a scanner to this backend. */ static SANE_Status attach_scanner (const char *devicename, Leo_Scanner ** devp) { Leo_Scanner *dev; int sfd; DBG (DBG_sane_proc, "attach_scanner: %s\n", devicename); if (devp) *devp = NULL; /* Check if we know this device name. */ for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->sane.name, devicename) == 0) { if (devp) { *devp = dev; } DBG (DBG_info, "device is already known\n"); return SANE_STATUS_GOOD; } } /* Allocate a new scanner entry. */ dev = leo_init (); if (dev == NULL) { DBG (DBG_error, "ERROR: not enough memory\n"); return SANE_STATUS_NO_MEM; } DBG (DBG_info, "attach_scanner: opening %s\n", devicename); if (sanei_scsi_open (devicename, &sfd, leo_sense_handler, dev) != 0) { DBG (DBG_error, "ERROR: attach_scanner: open failed\n"); leo_free (dev); return SANE_STATUS_INVAL; } /* Fill some scanner specific values. */ dev->devicename = strdup (devicename); dev->sfd = sfd; /* Now, check that it is a scanner we support. */ if (leo_identify_scanner (dev) == SANE_FALSE) { DBG (DBG_error, "ERROR: attach_scanner: scanner-identification failed\n"); leo_free (dev); return SANE_STATUS_INVAL; } leo_close (dev); /* Set the default options for that scanner. */ dev->sane.name = dev->devicename; dev->sane.vendor = dev->def->real_vendor; dev->sane.model = dev->def->real_product; dev->sane.type = "flatbed scanner"; /* Link the scanner with the others. */ dev->next = first_dev; first_dev = dev; if (devp) { *devp = dev; } num_devices++; DBG (DBG_proc, "attach_scanner: exit\n"); return SANE_STATUS_GOOD; } static SANE_Status attach_one (const char *dev) { attach_scanner (dev, NULL); return SANE_STATUS_GOOD; } /* Reset the options for that scanner. */ static void leo_init_options (Leo_Scanner * dev) { int i; /* Pre-initialize the options. */ memset (dev->opt, 0, sizeof (dev->opt)); memset (dev->val, 0, sizeof (dev->val)); for (i = 0; i < OPT_NUM_OPTIONS; ++i) { dev->opt[i].size = sizeof (SANE_Word); dev->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } /* Number of options. */ dev->opt[OPT_NUM_OPTS].name = ""; dev->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; dev->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; dev->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; dev->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; dev->val[OPT_NUM_OPTS].w = OPT_NUM_OPTIONS; /* Mode group */ dev->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan mode"); dev->opt[OPT_MODE_GROUP].desc = ""; /* not valid for a group */ dev->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; dev->opt[OPT_MODE_GROUP].cap = 0; dev->opt[OPT_MODE_GROUP].size = 0; dev->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* Scanner supported modes */ dev->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; dev->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE; dev->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE; dev->opt[OPT_MODE].type = SANE_TYPE_STRING; dev->opt[OPT_MODE].size = max_string_size (scan_mode_list); dev->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->opt[OPT_MODE].constraint.string_list = scan_mode_list; dev->val[OPT_MODE].s = (SANE_Char *) strdup (""); /* will be set later */ /* X and Y resolution */ dev->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; dev->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; dev->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; dev->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; dev->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_RESOLUTION].constraint.range = &dev->res_range; dev->val[OPT_RESOLUTION].w = dev->res_range.max / 2; /* Halftone pattern */ dev->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN; dev->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN; dev->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN; dev->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING; dev->opt[OPT_HALFTONE_PATTERN].size = max_string_size (halftone_pattern_list); dev->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->opt[OPT_HALFTONE_PATTERN].constraint.string_list = halftone_pattern_list; dev->val[OPT_HALFTONE_PATTERN].s = strdup (halftone_pattern_list[0]); /* Geometry group */ dev->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry"); dev->opt[OPT_GEOMETRY_GROUP].desc = ""; /* not valid for a group */ dev->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; dev->opt[OPT_GEOMETRY_GROUP].cap = 0; dev->opt[OPT_GEOMETRY_GROUP].size = 0; dev->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* Upper left X */ dev->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; dev->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; dev->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; dev->opt[OPT_TL_X].type = SANE_TYPE_FIXED; dev->opt[OPT_TL_X].unit = SANE_UNIT_MM; dev->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_TL_X].constraint.range = &x_range; dev->val[OPT_TL_X].w = x_range.min; /* Upper left Y */ dev->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; dev->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; dev->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; dev->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; dev->opt[OPT_TL_Y].unit = SANE_UNIT_MM; dev->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_TL_Y].constraint.range = &y_range; dev->val[OPT_TL_Y].w = y_range.min; /* Bottom-right x */ dev->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; dev->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; dev->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; dev->opt[OPT_BR_X].type = SANE_TYPE_FIXED; dev->opt[OPT_BR_X].unit = SANE_UNIT_MM; dev->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_BR_X].constraint.range = &x_range; dev->val[OPT_BR_X].w = x_range.max; /* Bottom-right y */ dev->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; dev->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; dev->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; dev->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; dev->opt[OPT_BR_Y].unit = SANE_UNIT_MM; dev->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_BR_Y].constraint.range = &y_range; dev->val[OPT_BR_Y].w = y_range.max; /* Enhancement group */ dev->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement"); dev->opt[OPT_ENHANCEMENT_GROUP].desc = ""; /* not valid for a group */ dev->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; dev->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED; dev->opt[OPT_ENHANCEMENT_GROUP].size = 0; dev->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* custom-gamma table */ dev->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA; dev->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA; dev->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA; dev->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL; dev->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; dev->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE; /* red gamma vector */ dev->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; dev->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R; dev->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R; dev->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT; dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE; dev->opt[OPT_GAMMA_VECTOR_R].size = GAMMA_LENGTH * sizeof (SANE_Word); dev->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range; dev->val[OPT_GAMMA_VECTOR_R].wa = dev->gamma_R; /* green gamma vector */ dev->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; dev->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G; dev->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G; dev->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT; dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE; dev->opt[OPT_GAMMA_VECTOR_G].size = GAMMA_LENGTH * sizeof (SANE_Word); dev->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range; dev->val[OPT_GAMMA_VECTOR_G].wa = dev->gamma_G; /* blue gamma vector */ dev->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; dev->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B; dev->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B; dev->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT; dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE; dev->opt[OPT_GAMMA_VECTOR_B].size = GAMMA_LENGTH * sizeof (SANE_Word); dev->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range; dev->val[OPT_GAMMA_VECTOR_B].wa = dev->gamma_B; /* grayscale gamma vector */ dev->opt[OPT_GAMMA_VECTOR_GRAY].name = SANE_NAME_GAMMA_VECTOR; dev->opt[OPT_GAMMA_VECTOR_GRAY].title = SANE_TITLE_GAMMA_VECTOR; dev->opt[OPT_GAMMA_VECTOR_GRAY].desc = SANE_DESC_GAMMA_VECTOR; dev->opt[OPT_GAMMA_VECTOR_GRAY].type = SANE_TYPE_INT; dev->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_GAMMA_VECTOR_GRAY].unit = SANE_UNIT_NONE; dev->opt[OPT_GAMMA_VECTOR_GRAY].size = GAMMA_LENGTH * sizeof (SANE_Word); dev->opt[OPT_GAMMA_VECTOR_GRAY].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_GAMMA_VECTOR_GRAY].constraint.range = &gamma_range; dev->val[OPT_GAMMA_VECTOR_GRAY].wa = dev->gamma_GRAY; /* preview */ dev->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; dev->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; dev->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; dev->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; dev->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; dev->val[OPT_PREVIEW].w = SANE_FALSE; /* Lastly, set the default scan mode. This might change some * values previously set here. */ sane_control_option (dev, OPT_MODE, SANE_ACTION_SET_VALUE, (SANE_String_Const *) scan_mode_list[0], NULL); } /* * Wait until the scanner is ready. */ static SANE_Status leo_wait_scanner (Leo_Scanner * dev) { SANE_Status status; int timeout; CDB cdb; DBG (DBG_proc, "leo_wait_scanner: enter\n"); MKSCSI_TEST_UNIT_READY (cdb); /* Set the timeout to 60 seconds. */ timeout = 60; while (timeout > 0) { /* test unit ready */ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, NULL, NULL); if (status == SANE_STATUS_GOOD) { return SANE_STATUS_GOOD; } sleep (1); }; DBG (DBG_proc, "leo_wait_scanner: scanner not ready\n"); return (SANE_STATUS_IO_ERROR); } /* Read the image from the scanner and fill the temporary buffer with it. */ static SANE_Status leo_fill_image (Leo_Scanner * dev) { SANE_Status status; size_t size; CDB cdb; unsigned char *image; DBG (DBG_proc, "leo_fill_image: enter\n"); assert (dev->image_begin == dev->image_end); assert (dev->real_bytes_left > 0); dev->image_begin = 0; dev->image_end = 0; while (dev->real_bytes_left) { /* * Try to read the maximum number of bytes. */ size = 0; while (size == 0) { status = get_filled_data_length (dev, &size); if (status) return (status); if (size == 0) usleep (100000); /* sleep 1/10th of second */ } if (size > dev->real_bytes_left) size = dev->real_bytes_left; if (size > dev->image_size - dev->image_end) size = dev->image_size - dev->image_end; /* The scanner seems to hang if more than 32KB are read. */ if (size > 0x7fff) size = 0x7fff; /* Always read a multiple of a line. */ size = size - (size % dev->params.bytes_per_line); if (size == 0) { /* Probably reached the end of the buffer. * Check, just in case. */ assert (dev->image_end != 0); return (SANE_STATUS_GOOD); } DBG (DBG_info, "leo_fill_image: to read = %ld bytes (bpl=%d)\n", (long) size, dev->params.bytes_per_line); MKSCSI_READ_10 (cdb, 0, 0, size); hexdump (DBG_info2, "leo_fill_image: READ_10 CDB", cdb.data, 10); image = dev->image + dev->image_end; status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, image, &size); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "leo_fill_image: cannot read from the scanner\n"); return status; } /* Some format conversion. */ if (dev->scan_mode == LEO_COLOR) { /* Reorder the lines. The scanner gives color by color for * each line. */ unsigned char *src = image; int nb_lines = size / dev->params.bytes_per_line; int i, j; for (i = 0; i < nb_lines; i++) { unsigned char *dest = dev->buffer; for (j = 0; j < dev->params.pixels_per_line; j++) { *dest = src[j + 0 * dev->params.pixels_per_line]; dest++; *dest = src[j + 1 * dev->params.pixels_per_line]; dest++; *dest = src[j + 2 * dev->params.pixels_per_line]; dest++; } /* Copy the line back. */ memcpy (src, dev->buffer, dev->params.bytes_per_line); src += dev->params.bytes_per_line; } } dev->image_end += size; dev->real_bytes_left -= size; DBG (DBG_info, "leo_fill_image: real bytes left = %ld\n", (long) dev->real_bytes_left); } return (SANE_STATUS_GOOD); /* unreachable */ } /* Copy from the raw buffer to the buffer given by the backend. * * len in input is the maximum length available in buf, and, in * output, is the length written into buf. */ static void leo_copy_raw_to_frontend (Leo_Scanner * dev, SANE_Byte * buf, size_t * len) { size_t size; size = dev->image_end - dev->image_begin; if (size > *len) { size = *len; } *len = size; memcpy (buf, dev->image + dev->image_begin, size); dev->image_begin += size; } /* Stop a scan. */ static SANE_Status do_cancel (Leo_Scanner * dev) { DBG (DBG_sane_proc, "do_cancel enter\n"); if (dev->scanning == SANE_TRUE) { /* Reset the scanner */ dev->x_tl = 0; dev->x_tl = 0; dev->width = 0; dev->length = 0; leo_set_window (dev); leo_scan (dev); leo_close (dev); } dev->scanning = SANE_FALSE; DBG (DBG_sane_proc, "do_cancel exit\n"); return SANE_STATUS_CANCELLED; } /* A default gamma table. */ static const SANE_Word gamma_init[GAMMA_LENGTH] = { 0x00, 0x06, 0x0A, 0x0D, 0x10, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F, 0x21, 0x23, 0x25, 0x27, 0x28, 0x2A, 0x2C, 0x2D, 0x2F, 0x30, 0x32, 0x33, 0x35, 0x36, 0x38, 0x39, 0x3A, 0x3C, 0x3D, 0x3F, 0x40, 0x41, 0x43, 0x44, 0x45, 0x46, 0x48, 0x49, 0x4A, 0x4B, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xB9, 0xBA, 0xBB, 0xBC, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC0, 0xC1, 0xC2, 0xC3, 0xC3, 0xC4, 0xC5, 0xC6, 0xC6, 0xC7, 0xC8, 0xC9, 0xC9, 0xCA, 0xCB, 0xCC, 0xCC, 0xCD, 0xCE, 0xCF, 0xCF, 0xD0, 0xD1, 0xD2, 0xD2, 0xD3, 0xD4, 0xD5, 0xD5, 0xD6, 0xD7, 0xD7, 0xD8, 0xD9, 0xDA, 0xDA, 0xDB, 0xDC, 0xDC, 0xDD, 0xDE, 0xDF, 0xDF, 0xE0, 0xE1, 0xE1, 0xE2, 0xE3, 0xE4, 0xE4, 0xE5, 0xE6, 0xE6, 0xE7, 0xE8, 0xE8, 0xE9, 0xEA, 0xEB, 0xEB, 0xEC, 0xED, 0xED, 0xEE, 0xEF, 0xEF, 0xF0, 0xF1, 0xF1, 0xF2, 0xF3, 0xF4, 0xF4, 0xF5, 0xF6, 0xF6, 0xF7, 0xF8, 0xF8, 0xF9, 0xFA, 0xFA, 0xFB, 0xFC, 0xFC, 0xFD, 0xFE, 0xFE, 0xFF }; /* Send the gamma */ static SANE_Status leo_send_gamma (Leo_Scanner * dev) { CDB cdb; SANE_Status status; struct { unsigned char gamma_R[GAMMA_LENGTH]; unsigned char gamma_G[GAMMA_LENGTH]; /* also gray */ unsigned char gamma_B[GAMMA_LENGTH]; } param; size_t i; size_t size; DBG (DBG_proc, "leo_send_gamma: enter\n"); size = sizeof (param); assert (size == 3 * GAMMA_LENGTH); MKSCSI_SEND_10 (cdb, 0x03, 0x01, size); if (dev->val[OPT_CUSTOM_GAMMA].w) { /* Use the custom gamma. */ if (dev->scan_mode == LEO_GRAYSCALE) { /* Gray */ for (i = 0; i < GAMMA_LENGTH; i++) { param.gamma_R[i] = dev->gamma_GRAY[i]; param.gamma_G[i] = 0; param.gamma_B[i] = 0; } } else { /* Color */ for (i = 0; i < GAMMA_LENGTH; i++) { param.gamma_R[i] = dev->gamma_R[i]; param.gamma_G[i] = dev->gamma_G[i]; param.gamma_B[i] = dev->gamma_B[i]; } } } else { for (i = 0; i < GAMMA_LENGTH; i++) { param.gamma_R[i] = gamma_init[i]; param.gamma_G[i] = gamma_init[i]; param.gamma_B[i] = gamma_init[i]; } } hexdump (DBG_info2, "leo_send_gamma:", cdb.data, cdb.len); status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, ¶m, size, NULL, NULL); DBG (DBG_proc, "leo_send_gamma: exit, status=%d\n", status); return (status); } /* Send the halftone pattern */ static SANE_Status leo_send_halftone_pattern (Leo_Scanner * dev) { int i; const halftone_pattern_t *pattern; SANE_Status status; size_t size; CDB cdb; DBG (DBG_proc, "leo_send_halftone_pattern: enter\n"); if (dev->scan_mode == LEO_HALFTONE) { i = get_string_list_index (halftone_pattern_list, dev->val[OPT_HALFTONE_PATTERN].s); pattern = halftone_pattern_val[i]; assert (pattern != NULL); size = sizeof (halftone_pattern_t); assert (size == 256); MKSCSI_SEND_10 (cdb, 0x02, 0x0F, size); hexdump (DBG_info2, "leo_send_gamma:", cdb.data, cdb.len); status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, pattern, size, NULL, NULL); } else { status = SANE_STATUS_GOOD; } DBG (DBG_proc, "leo_send_halftone_pattern: exit, status=%d\n", status); return status; } /*--------------------------------------------------------------------------*/ /* Sane entry points */ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize) { FILE *fp; char dev_name[PATH_MAX]; size_t len; DBG_INIT (); DBG (DBG_sane_init, "sane_init\n"); DBG (DBG_error, "This is sane-leo version %d.%d-%d\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); DBG (DBG_error, "(C) 2002 by Frank Zago\n"); if (version_code) { *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); } fp = sanei_config_open (LEO_CONFIG_FILE); if (!fp) { /* default to /dev/scanner instead of insisting on config file */ attach_scanner ("/dev/scanner", 0); return SANE_STATUS_GOOD; } while (sanei_config_read (dev_name, sizeof (dev_name), fp)) { if (dev_name[0] == '#') /* ignore line comments */ continue; len = strlen (dev_name); if (!len) continue; /* ignore empty lines */ sanei_config_attach_matching_devices (dev_name, attach_one); } fclose (fp); DBG (DBG_proc, "sane_init: leave\n"); return SANE_STATUS_GOOD; } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only) { Leo_Scanner *dev; int i; DBG (DBG_proc, "sane_get_devices: enter\n"); if (devlist) free (devlist); devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); if (!devlist) return SANE_STATUS_NO_MEM; i = 0; for (dev = first_dev; i < num_devices; dev = dev->next) devlist[i++] = &dev->sane; devlist[i++] = 0; *device_list = devlist; DBG (DBG_proc, "sane_get_devices: exit\n"); return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { Leo_Scanner *dev; SANE_Status status; DBG (DBG_proc, "sane_open: enter\n"); /* search for devicename */ if (devicename[0]) { DBG (DBG_info, "sane_open: devicename=%s\n", devicename); for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->sane.name, devicename) == 0) { break; } } if (!dev) { status = attach_scanner (devicename, &dev); if (status != SANE_STATUS_GOOD) { return status; } } } else { DBG (DBG_sane_info, "sane_open: no devicename, opening first device\n"); dev = first_dev; /* empty devicename -> use first device */ } if (!dev) { DBG (DBG_error, "No scanner found\n"); return SANE_STATUS_INVAL; } leo_init_options (dev); /* Initialize the gamma table. */ memcpy (dev->gamma_R, gamma_init, dev->opt[OPT_GAMMA_VECTOR_R].size); memcpy (dev->gamma_G, gamma_init, dev->opt[OPT_GAMMA_VECTOR_G].size); memcpy (dev->gamma_B, gamma_init, dev->opt[OPT_GAMMA_VECTOR_B].size); memcpy (dev->gamma_GRAY, gamma_init, dev->opt[OPT_GAMMA_VECTOR_GRAY].size); *handle = dev; DBG (DBG_proc, "sane_open: exit\n"); return SANE_STATUS_GOOD; } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { Leo_Scanner *dev = handle; DBG (DBG_proc, "sane_get_option_descriptor: enter, option %d\n", option); if ((unsigned) option >= OPT_NUM_OPTIONS) { return NULL; } DBG (DBG_proc, "sane_get_option_descriptor: exit\n"); return dev->opt + option; } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { Leo_Scanner *dev = handle; SANE_Status status; SANE_Word cap; int i; DBG (DBG_proc, "sane_control_option: enter, option %d, action %d\n", option, action); if (info) { *info = 0; } if (dev->scanning) { return SANE_STATUS_DEVICE_BUSY; } if (option < 0 || option >= OPT_NUM_OPTIONS) { return SANE_STATUS_INVAL; } cap = dev->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) { return SANE_STATUS_INVAL; } if (action == SANE_ACTION_GET_VALUE) { switch (option) { /* word options */ case OPT_NUM_OPTS: case OPT_RESOLUTION: case OPT_TL_Y: case OPT_BR_Y: case OPT_TL_X: case OPT_BR_X: case OPT_CUSTOM_GAMMA: case OPT_PREVIEW: *(SANE_Word *) val = dev->val[option].w; return SANE_STATUS_GOOD; /* string options */ case OPT_MODE: case OPT_HALFTONE_PATTERN: strcpy (val, dev->val[option].s); return SANE_STATUS_GOOD; /* Gamma */ case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: case OPT_GAMMA_VECTOR_GRAY: memcpy (val, dev->val[option].wa, dev->opt[option].size); return SANE_STATUS_GOOD; default: return SANE_STATUS_INVAL; } } else if (action == SANE_ACTION_SET_VALUE) { if (!SANE_OPTION_IS_SETTABLE (cap)) { DBG (DBG_error, "could not set option, not settable\n"); return SANE_STATUS_INVAL; } status = sanei_constrain_value (dev->opt + option, val, info); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "could not set option, invalid value\n"); return status; } switch (option) { /* Numeric side-effect options */ case OPT_TL_Y: case OPT_BR_Y: case OPT_TL_X: case OPT_BR_X: case OPT_RESOLUTION: if (info) { *info |= SANE_INFO_RELOAD_PARAMS; } dev->val[option].w = *(SANE_Word *) val; return SANE_STATUS_GOOD; /* Numeric side-effect free options */ case OPT_PREVIEW: dev->val[option].w = *(SANE_Word *) val; return SANE_STATUS_GOOD; /* String side-effect options */ case OPT_MODE: if (strcmp (dev->val[option].s, val) == 0) return SANE_STATUS_GOOD; free (dev->val[OPT_MODE].s); dev->val[OPT_MODE].s = (SANE_Char *) strdup (val); dev->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; if (strcmp (dev->val[OPT_MODE].s, BLACK_WHITE_STR) == 0) { i = get_string_list_index (halftone_pattern_list, dev->val[OPT_HALFTONE_PATTERN].s); if (halftone_pattern_val[i] == NULL) { dev->scan_mode = LEO_BW; } else { dev->scan_mode = LEO_HALFTONE; } dev->depth = 1; dev->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE; } else if (strcmp (dev->val[OPT_MODE].s, GRAY_STR) == 0) { dev->scan_mode = LEO_GRAYSCALE; dev->depth = 8; dev->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE; if (dev->val[OPT_CUSTOM_GAMMA].w) { dev->opt[OPT_GAMMA_VECTOR_GRAY].cap &= ~SANE_CAP_INACTIVE; } } else if (strcmp (dev->val[OPT_MODE].s, COLOR_STR) == 0) { dev->scan_mode = LEO_COLOR; dev->depth = 8; dev->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE; if (dev->val[OPT_CUSTOM_GAMMA].w) { dev->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; dev->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; dev->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; } } if (info) { *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; } return SANE_STATUS_GOOD; case OPT_HALFTONE_PATTERN: free (dev->val[option].s); dev->val[option].s = (SANE_String) strdup (val); i = get_string_list_index (halftone_pattern_list, dev->val[OPT_HALFTONE_PATTERN].s); if (halftone_pattern_val[i] == NULL) { dev->scan_mode = LEO_BW; } else { dev->scan_mode = LEO_HALFTONE; } return SANE_STATUS_GOOD; case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: case OPT_GAMMA_VECTOR_GRAY: memcpy (dev->val[option].wa, val, dev->opt[option].size); return SANE_STATUS_GOOD; case OPT_CUSTOM_GAMMA: dev->val[OPT_CUSTOM_GAMMA].w = *(SANE_Word *) val; if (dev->val[OPT_CUSTOM_GAMMA].w) { /* use custom_gamma_table */ if (dev->scan_mode == LEO_GRAYSCALE) { dev->opt[OPT_GAMMA_VECTOR_GRAY].cap &= ~SANE_CAP_INACTIVE; } else { /* color mode */ dev->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; dev->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; dev->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; } } else { dev->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_GAMMA_VECTOR_GRAY].cap |= SANE_CAP_INACTIVE; } if (info) { *info |= SANE_INFO_RELOAD_OPTIONS; } return SANE_STATUS_GOOD; default: return SANE_STATUS_INVAL; } } DBG (DBG_proc, "sane_control_option: exit, bad\n"); return SANE_STATUS_UNSUPPORTED; } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { Leo_Scanner *dev = handle; DBG (DBG_proc, "sane_get_parameters: enter\n"); if (!(dev->scanning)) { /* Setup the parameters for the scan. These values will be re-used * in the SET WINDOWS command. */ if (dev->val[OPT_PREVIEW].w == SANE_TRUE) { dev->x_resolution = 28; dev->y_resolution = 28; dev->x_tl = 0; dev->y_tl = 0; dev->x_br = mmToIlu (SANE_UNFIX (x_range.max)); dev->y_br = mmToIlu (SANE_UNFIX (y_range.max)); } else { dev->x_resolution = dev->val[OPT_RESOLUTION].w; dev->y_resolution = dev->val[OPT_RESOLUTION].w; dev->x_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_X].w)); dev->y_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_Y].w)); dev->x_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_X].w)); dev->y_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_Y].w)); } /* Check the corners are OK. */ if (dev->x_tl > dev->x_br) { int s; s = dev->x_tl; dev->x_tl = dev->x_br; dev->x_br = s; } if (dev->y_tl > dev->y_br) { int s; s = dev->y_tl; dev->y_tl = dev->y_br; dev->y_br = s; } dev->width = dev->x_br - dev->x_tl; dev->length = dev->y_br - dev->y_tl; /* Prepare the parameters for the caller. */ memset (&dev->params, 0, sizeof (SANE_Parameters)); dev->params.last_frame = SANE_TRUE; switch (dev->scan_mode) { case LEO_BW: case LEO_HALFTONE: dev->params.format = SANE_FRAME_GRAY; dev->params.pixels_per_line = dev->width & ~0x7; dev->params.bytes_per_line = dev->params.pixels_per_line / 8; dev->params.depth = 1; break; case LEO_GRAYSCALE: dev->params.format = SANE_FRAME_GRAY; dev->params.pixels_per_line = dev->width; dev->params.bytes_per_line = dev->params.pixels_per_line; dev->params.depth = 8; break; case LEO_COLOR: dev->params.format = SANE_FRAME_RGB; dev->params.pixels_per_line = dev->width; dev->params.bytes_per_line = dev->params.pixels_per_line * 3; dev->params.depth = 8; break; } dev->params.lines = dev->length; } /* Return the current values. */ if (params) { *params = (dev->params); } DBG (DBG_proc, "sane_get_parameters: exit\n"); return SANE_STATUS_GOOD; } SANE_Status sane_start (SANE_Handle handle) { Leo_Scanner *dev = handle; SANE_Status status; DBG (DBG_proc, "sane_start: enter\n"); if (!(dev->scanning)) { sane_get_parameters (dev, NULL); /* Open again the scanner. */ if (sanei_scsi_open (dev->devicename, &(dev->sfd), leo_sense_handler, dev) != 0) { DBG (DBG_error, "ERROR: sane_start: open failed\n"); return SANE_STATUS_INVAL; } /* The scanner must be ready. */ status = leo_wait_scanner (dev); if (status) { leo_close (dev); return status; } status = leo_set_window (dev); if (status) { leo_close (dev); return status; } status = leo_send_gamma (dev); if (status) { leo_close (dev); return status; } status = leo_send_halftone_pattern (dev); if (status) { leo_close (dev); return status; } status = leo_scan (dev); if (status) { leo_close (dev); return status; } status = leo_wait_scanner (dev); if (status) { leo_close (dev); return status; } status = leo_get_scan_size (dev); if (status) { leo_close (dev); return status; } } dev->image_end = 0; dev->image_begin = 0; dev->bytes_left = dev->params.bytes_per_line * dev->params.lines; dev->real_bytes_left = dev->params.bytes_per_line * dev->params.lines; dev->scanning = SANE_TRUE; DBG (DBG_proc, "sane_start: exit\n"); return SANE_STATUS_GOOD; } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { SANE_Status status; Leo_Scanner *dev = handle; size_t size; int buf_offset; /* offset into buf */ DBG (DBG_proc, "sane_read: enter\n"); *len = 0; if (!(dev->scanning)) { /* OOPS, not scanning */ return do_cancel (dev); } if (dev->bytes_left <= 0) { return (SANE_STATUS_EOF); } buf_offset = 0; do { if (dev->image_begin == dev->image_end) { /* Fill image */ status = leo_fill_image (dev); if (status != SANE_STATUS_GOOD) { return (status); } } /* Something must have been read */ if (dev->image_begin == dev->image_end) { DBG (DBG_info, "sane_read: nothing read\n"); return SANE_STATUS_IO_ERROR; } /* Copy the data to the frontend buffer. */ size = max_len - buf_offset; if (size > dev->bytes_left) { size = dev->bytes_left; } leo_copy_raw_to_frontend (dev, buf + buf_offset, &size); buf_offset += size; dev->bytes_left -= size; *len += size; } while ((buf_offset != max_len) && dev->bytes_left); DBG (DBG_info, "sane_read: leave, bytes_left=%ld\n", (long) dev->bytes_left); return SANE_STATUS_GOOD; } SANE_Status sane_set_io_mode (SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking) { SANE_Status status; Leo_Scanner *dev = handle; DBG (DBG_proc, "sane_set_io_mode: enter\n"); if (!dev->scanning) { return SANE_STATUS_INVAL; } if (non_blocking == SANE_FALSE) { status = SANE_STATUS_GOOD; } else { status = SANE_STATUS_UNSUPPORTED; } DBG (DBG_proc, "sane_set_io_mode: exit\n"); return status; } SANE_Status sane_get_select_fd (SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ * fd) { DBG (DBG_proc, "sane_get_select_fd: enter\n"); DBG (DBG_proc, "sane_get_select_fd: exit\n"); return SANE_STATUS_UNSUPPORTED; } void sane_cancel (SANE_Handle handle) { Leo_Scanner *dev = handle; DBG (DBG_proc, "sane_cancel: enter\n"); do_cancel (dev); DBG (DBG_proc, "sane_cancel: exit\n"); } void sane_close (SANE_Handle handle) { Leo_Scanner *dev = handle; Leo_Scanner *dev_tmp; DBG (DBG_proc, "sane_close: enter\n"); do_cancel (dev); leo_close (dev); /* Unlink dev. */ if (first_dev == dev) { first_dev = dev->next; } else { dev_tmp = first_dev; while (dev_tmp->next && dev_tmp->next != dev) { dev_tmp = dev_tmp->next; } if (dev_tmp->next != NULL) { dev_tmp->next = dev_tmp->next->next; } } leo_free (dev); num_devices--; DBG (DBG_proc, "sane_close: exit\n"); } void sane_exit (void) { DBG (DBG_proc, "sane_exit: enter\n"); while (first_dev) { sane_close (first_dev); } if (devlist) { free (devlist); devlist = NULL; } DBG (DBG_proc, "sane_exit: exit\n"); } backends-1.3.0/backend/leo.conf.in000066400000000000000000000001601456256263500167220ustar00rootroot00000000000000# The FS-1130 respond to all luns scsi ACROSS * Scanner * * * 0 # LEO S3 scsi "LEO" "LEOScan-S3" /dev/scanner backends-1.3.0/backend/leo.h000066400000000000000000000460031456256263500156250ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2002 Frank Zago (sane at zago dot net) 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, see . 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. */ /* Commands supported by the scanner. */ #define SCSI_TEST_UNIT_READY 0x00 #define SCSI_INQUIRY 0x12 #define SCSI_SCAN 0x1b #define SCSI_SET_WINDOW 0x24 #define SCSI_READ_10 0x28 #define SCSI_SEND_10 0x2a #define SCSI_REQUEST_SENSE 0x03 #define SCSI_GET_DATA_BUFFER_STATUS 0x34 typedef struct { unsigned char data[16]; int len; } CDB; /* Set a specific bit depending on a boolean. * MKSCSI_BIT(TRUE, 3) will generate 0x08. */ #define MKSCSI_BIT(bit, pos) ((bit)? 1<<(pos): 0) /* Set a value in a range of bits. * MKSCSI_I2B(5, 3, 5) will generate 0x28 */ #define MKSCSI_I2B(bits, pos_b, pos_e) ((bits) << (pos_b) & ((1<<((pos_e)-(pos_b)+1))-1)) /* Store an integer in 2, 3 or 4 byte in an array. */ #define Ito16(val, buf) { \ ((unsigned char *)buf)[0] = ((val) >> 8) & 0xff; \ ((unsigned char *)buf)[1] = ((val) >> 0) & 0xff; \ } #define Ito24(val, buf) { \ ((unsigned char *)buf)[0] = ((val) >> 16) & 0xff; \ ((unsigned char *)buf)[1] = ((val) >> 8) & 0xff; \ ((unsigned char *)buf)[2] = ((val) >> 0) & 0xff; \ } #define Ito32(val, buf) { \ ((unsigned char *)buf)[0] = ((val) >> 24) & 0xff; \ ((unsigned char *)buf)[1] = ((val) >> 16) & 0xff; \ ((unsigned char *)buf)[2] = ((val) >> 8) & 0xff; \ ((unsigned char *)buf)[3] = ((val) >> 0) & 0xff; \ } #define MKSCSI_GET_DATA_BUFFER_STATUS(cdb, wait, buflen) \ cdb.data[0] = SCSI_GET_DATA_BUFFER_STATUS; \ cdb.data[1] = MKSCSI_BIT(wait, 0); \ cdb.data[2] = 0; \ cdb.data[3] = 0; \ cdb.data[4] = 0; \ cdb.data[5] = 0; \ cdb.data[6] = 0; \ cdb.data[7] = (((buflen) >> 8) & 0xff); \ cdb.data[8] = (((buflen) >> 0) & 0xff); \ cdb.data[9] = 0; \ cdb.len = 10; #define MKSCSI_INQUIRY(cdb, buflen) \ cdb.data[0] = SCSI_INQUIRY; \ cdb.data[1] = 0; \ cdb.data[2] = 0; \ cdb.data[3] = 0; \ cdb.data[4] = buflen; \ cdb.data[5] = 0; \ cdb.len = 6; #define MKSCSI_SCAN(cdb) \ cdb.data[0] = SCSI_SCAN; \ cdb.data[1] = 0; \ cdb.data[2] = 0; \ cdb.data[3] = 0; \ cdb.data[4] = 0; \ cdb.data[5] = 0; \ cdb.len = 6; #define MKSCSI_SEND_10(cdb, dtc, dtq, buflen) \ cdb.data[0] = SCSI_SEND_10; \ cdb.data[1] = 0; \ cdb.data[2] = (dtc); \ cdb.data[3] = 0; \ cdb.data[4] = (((dtq) >> 8) & 0xff); \ cdb.data[5] = (((dtq) >> 0) & 0xff); \ cdb.data[6] = (((buflen) >> 16) & 0xff); \ cdb.data[7] = (((buflen) >> 8) & 0xff); \ cdb.data[8] = (((buflen) >> 0) & 0xff); \ cdb.data[9] = 0; \ cdb.len = 10; #define MKSCSI_SET_WINDOW(cdb, buflen) \ cdb.data[0] = SCSI_SET_WINDOW; \ cdb.data[1] = 0; \ cdb.data[2] = 0; \ cdb.data[3] = 0; \ cdb.data[4] = 0; \ cdb.data[5] = 0; \ cdb.data[6] = (((buflen) >> 16) & 0xff); \ cdb.data[7] = (((buflen) >> 8) & 0xff); \ cdb.data[8] = (((buflen) >> 0) & 0xff); \ cdb.data[9] = 0; \ cdb.len = 10; #define MKSCSI_READ_10(cdb, dtc, dtq, buflen) \ cdb.data[0] = SCSI_READ_10; \ cdb.data[1] = 0; \ cdb.data[2] = (dtc); \ cdb.data[3] = 0; \ cdb.data[4] = (((dtq) >> 8) & 0xff); \ cdb.data[5] = (((dtq) >> 0) & 0xff); \ cdb.data[6] = (((buflen) >> 16) & 0xff); \ cdb.data[7] = (((buflen) >> 8) & 0xff); \ cdb.data[8] = (((buflen) >> 0) & 0xff); \ cdb.data[9] = 0; \ cdb.len = 10; #define MKSCSI_REQUEST_SENSE(cdb, buflen) \ cdb.data[0] = SCSI_REQUEST_SENSE; \ cdb.data[1] = 0; \ cdb.data[2] = 0; \ cdb.data[3] = 0; \ cdb.data[4] = (buflen); \ cdb.data[5] = 0; \ cdb.len = 6; #define MKSCSI_TEST_UNIT_READY(cdb) \ cdb.data[0] = SCSI_TEST_UNIT_READY; \ cdb.data[1] = 0; \ cdb.data[2] = 0; \ cdb.data[3] = 0; \ cdb.data[4] = 0; \ cdb.data[5] = 0; \ cdb.len = 6; /*--------------------------------------------------------------------------*/ static int getbitfield (unsigned char *pageaddr, int mask, int shift) { return ((*pageaddr >> shift) & mask); } /* defines for request sense return block */ #define get_RS_information_valid(b) getbitfield(b + 0x00, 1, 7) #define get_RS_error_code(b) getbitfield(b + 0x00, 0x7f, 0) #define get_RS_filemark(b) getbitfield(b + 0x02, 1, 7) #define get_RS_EOM(b) getbitfield(b + 0x02, 1, 6) #define get_RS_ILI(b) getbitfield(b + 0x02, 1, 5) #define get_RS_sense_key(b) getbitfield(b + 0x02, 0x0f, 0) #define get_RS_information(b) getnbyte(b+0x03, 4) #define get_RS_additional_length(b) b[0x07] #define get_RS_ASC(b) b[0x0c] #define get_RS_ASCQ(b) b[0x0d] #define get_RS_SKSV(b) getbitfield(b+0x0f,1,7) /*--------------------------------------------------------------------------*/ #define mmToIlu(mm) (((mm) * dev->x_resolution) / MM_PER_INCH) #define iluToMm(ilu) (((ilu) * MM_PER_INCH) / dev->x_resolution) /*--------------------------------------------------------------------------*/ #define GAMMA_LENGTH 0x100 /* number of value per color */ /*--------------------------------------------------------------------------*/ enum Leo_Option { /* Must come first */ OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_MODE, /* scanner modes */ OPT_RESOLUTION, /* X and Y resolution */ OPT_GEOMETRY_GROUP, OPT_TL_X, /* upper left X */ OPT_TL_Y, /* upper left Y */ OPT_BR_X, /* bottom right X */ OPT_BR_Y, /* bottom right Y */ OPT_ENHANCEMENT_GROUP, OPT_CUSTOM_GAMMA, /* Use the custom gamma tables */ OPT_GAMMA_VECTOR_R, /* Custom Red gamma table */ OPT_GAMMA_VECTOR_G, /* Custom Green Gamma table */ OPT_GAMMA_VECTOR_B, /* Custom Blue Gamma table */ OPT_GAMMA_VECTOR_GRAY, /* Custom Gray Gamma table */ OPT_HALFTONE_PATTERN, /* Halftone pattern */ OPT_PREVIEW, /* preview mode */ /* must come last: */ OPT_NUM_OPTIONS }; /*--------------------------------------------------------------------------*/ /* * Scanner supported by this backend. */ struct scanners_supported { int scsi_type; char scsi_vendor[9]; char scsi_product[17]; const char *real_vendor; const char *real_product; }; /*--------------------------------------------------------------------------*/ #define BLACK_WHITE_STR SANE_VALUE_SCAN_MODE_LINEART #define GRAY_STR SANE_VALUE_SCAN_MODE_GRAY #define COLOR_STR SANE_VALUE_SCAN_MODE_COLOR /*--------------------------------------------------------------------------*/ /* Define a scanner occurrence. */ typedef struct Leo_Scanner { struct Leo_Scanner *next; SANE_Device sane; char *devicename; int sfd; /* device handle */ /* Infos from inquiry. */ char scsi_type; char scsi_vendor[9]; char scsi_product[17]; char scsi_version[5]; SANE_Range res_range; int x_resolution_max; /* maximum X dpi */ int y_resolution_max; /* maximum Y dpi */ /* SCSI handling */ size_t buffer_size; /* size of the buffer */ SANE_Byte *buffer; /* for SCSI transfer. */ /* Scanner infos. */ const struct scanners_supported *def; /* default options for that scanner */ /* Scanning handling. */ int scanning; /* TRUE if a scan is running. */ int x_resolution; /* X resolution in DPI */ int y_resolution; /* Y resolution in DPI */ int x_tl; /* X top left */ int y_tl; /* Y top left */ int x_br; /* X bottom right */ int y_br; /* Y bottom right */ int width; /* width of the scan area in mm */ int length; /* length of the scan area in mm */ int pass; /* current pass number */ enum { LEO_BW, LEO_HALFTONE, LEO_GRAYSCALE, LEO_COLOR } scan_mode; int depth; /* depth per color */ size_t bytes_left; /* number of bytes left to give to the backend */ size_t real_bytes_left; /* number of bytes left the scanner will return. */ SANE_Byte *image; /* keep the raw image here */ size_t image_size; /* allocated size of image */ size_t image_begin; /* first significant byte in image */ size_t image_end; /* first free byte in image */ SANE_Parameters params; /* Options */ SANE_Option_Descriptor opt[OPT_NUM_OPTIONS]; Option_Value val[OPT_NUM_OPTIONS]; /* Gamma table. 1 array per colour. */ SANE_Word gamma_R[GAMMA_LENGTH]; SANE_Word gamma_G[GAMMA_LENGTH]; SANE_Word gamma_B[GAMMA_LENGTH]; SANE_Word gamma_GRAY[GAMMA_LENGTH]; } Leo_Scanner; /*--------------------------------------------------------------------------*/ /* Debug levels. * Should be common to all backends. */ #define DBG_error0 0 #define DBG_error 1 #define DBG_sense 2 #define DBG_warning 3 #define DBG_inquiry 4 #define DBG_info 5 #define DBG_info2 6 #define DBG_proc 7 #define DBG_read 8 #define DBG_sane_init 10 #define DBG_sane_proc 11 #define DBG_sane_info 12 #define DBG_sane_option 13 /*--------------------------------------------------------------------------*/ /* 32 bits from an array to an integer (eg ntohl). */ #define B32TOI(buf) \ ((((unsigned char *)buf)[0] << 24) | \ (((unsigned char *)buf)[1] << 16) | \ (((unsigned char *)buf)[2] << 8) | \ (((unsigned char *)buf)[3] << 0)) #define B24TOI(buf) \ ((((unsigned char *)buf)[0] << 16) | \ (((unsigned char *)buf)[1] << 8) | \ (((unsigned char *)buf)[2] << 0)) #define B16TOI(buf) \ ((((unsigned char *)buf)[0] << 8) | \ (((unsigned char *)buf)[1] << 0)) /*--------------------------------------------------------------------------*/ /* Downloadable halftone patterns */ typedef unsigned char halftone_pattern_t[256]; static const halftone_pattern_t haltfone_pattern_diamond = { 0xF0, 0xE0, 0x60, 0x20, 0x00, 0x19, 0x61, 0xD8, 0xF0, 0xE0, 0x60, 0x20, 0x00, 0x19, 0x61, 0xD8, 0xC0, 0xA0, 0x88, 0x40, 0x38, 0x58, 0x80, 0xB8, 0xC0, 0xA0, 0x88, 0x40, 0x38, 0x58, 0x80, 0xB8, 0x30, 0x50, 0x98, 0xB0, 0xC8, 0xA8, 0x90, 0x48, 0x30, 0x50, 0x98, 0xB0, 0xC8, 0xA8, 0x90, 0x48, 0x08, 0x10, 0x70, 0xD0, 0xF8, 0xE8, 0x68, 0x28, 0x08, 0x10, 0x70, 0xD0, 0xF8, 0xE8, 0x68, 0x28, 0x00, 0x18, 0x78, 0xD8, 0xF0, 0xE0, 0x60, 0x20, 0x00, 0x18, 0x78, 0xD8, 0xF0, 0xE0, 0x60, 0x20, 0x38, 0x58, 0x80, 0xB8, 0xC0, 0xA0, 0x88, 0x40, 0x38, 0x58, 0x80, 0xB8, 0xC0, 0xA0, 0x88, 0x40, 0xC8, 0xA8, 0x90, 0x48, 0x30, 0x50, 0x9B, 0xB0, 0xC8, 0xA8, 0x90, 0x48, 0x30, 0x50, 0x9B, 0xB0, 0xF8, 0xE8, 0x68, 0x28, 0x08, 0x18, 0x70, 0xD0, 0xF8, 0xE8, 0x68, 0x28, 0x08, 0x18, 0x70, 0xD0, 0xF0, 0xE0, 0x60, 0x20, 0x00, 0x19, 0x61, 0xD8, 0xF0, 0xE0, 0x60, 0x20, 0x00, 0x19, 0x61, 0xD8, 0xC0, 0xA0, 0x88, 0x40, 0x38, 0x58, 0x80, 0xB8, 0xC0, 0xA0, 0x88, 0x40, 0x38, 0x58, 0x80, 0xB8, 0x30, 0x50, 0x98, 0xB0, 0xC8, 0xA8, 0x90, 0x48, 0x30, 0x50, 0x98, 0xB0, 0xC8, 0xA8, 0x90, 0x48, 0x08, 0x10, 0x70, 0xD0, 0xF8, 0xE8, 0x68, 0x28, 0x08, 0x10, 0x70, 0xD0, 0xF8, 0xE8, 0x68, 0x28, 0x00, 0x18, 0x78, 0xD8, 0xF0, 0xE0, 0x60, 0x20, 0x00, 0x18, 0x78, 0xD8, 0xF0, 0xE0, 0x60, 0x20, 0x38, 0x58, 0x80, 0xB8, 0xC0, 0xA0, 0x88, 0x40, 0x38, 0x58, 0x80, 0xB8, 0xC0, 0xA0, 0x88, 0x40, 0xC8, 0xA8, 0x90, 0x48, 0x30, 0x50, 0x9B, 0xB0, 0xC8, 0xA8, 0x90, 0x48, 0x30, 0x50, 0x9B, 0xB0, 0xF8, 0xE8, 0x68, 0x28, 0x08, 0x18, 0x70, 0xD0, 0xF8, 0xE8, 0x68, 0x28, 0x08, 0x18, 0x70, 0xD0 }; static const halftone_pattern_t haltfone_pattern_8x8_Coarse_Fatting = { 0x12, 0x3A, 0xD2, 0xEA, 0xE2, 0xB6, 0x52, 0x1A, 0x12, 0x3A, 0xD2, 0xEA, 0xE2, 0xB6, 0x52, 0x1A, 0x42, 0x6A, 0x9A, 0xCA, 0xC2, 0x92, 0x72, 0x4A, 0x42, 0x6A, 0x9A, 0xCA, 0xC2, 0x92, 0x72, 0x4A, 0xAE, 0x8E, 0x7E, 0x26, 0x2E, 0x66, 0x86, 0xA6, 0xAE, 0x8E, 0x7E, 0x26, 0x2E, 0x66, 0x86, 0xA6, 0xFA, 0xBA, 0x5E, 0x06, 0x0E, 0x36, 0xDE, 0xF6, 0xFA, 0xBA, 0x5E, 0x06, 0x0E, 0x36, 0xDE, 0xF6, 0xE6, 0xBE, 0x56, 0x1E, 0x16, 0x3E, 0xD6, 0xEE, 0xE6, 0xBE, 0x56, 0x1E, 0x16, 0x3E, 0xD6, 0xEE, 0xC6, 0x96, 0x76, 0x4E, 0x46, 0x6E, 0x9E, 0xCE, 0xC6, 0x96, 0x76, 0x4E, 0x46, 0x6E, 0x9E, 0xCE, 0x2A, 0x62, 0x82, 0xA2, 0xAA, 0x8A, 0x7A, 0x22, 0x2A, 0x62, 0x82, 0xA2, 0xAA, 0x8A, 0x7A, 0x22, 0x0A, 0x32, 0xDA, 0xF2, 0xFE, 0xB2, 0x5A, 0x02, 0x0A, 0x32, 0xDA, 0xF2, 0xFE, 0xB2, 0x5A, 0x02, 0x12, 0x3A, 0xD2, 0xEA, 0xE2, 0xB6, 0x52, 0x1A, 0x12, 0x3A, 0xD2, 0xEA, 0xE2, 0xB6, 0x52, 0x1A, 0x42, 0x6A, 0x9A, 0xCA, 0xC2, 0x92, 0x72, 0x4A, 0x42, 0x6A, 0x9A, 0xCA, 0xC2, 0x92, 0x72, 0x4A, 0xAE, 0x8E, 0x7E, 0x26, 0x2E, 0x66, 0x86, 0xA6, 0xAE, 0x8E, 0x7E, 0x26, 0x2E, 0x66, 0x86, 0xA6, 0xFA, 0xBA, 0x5E, 0x06, 0x0E, 0x36, 0xDE, 0xF6, 0xFA, 0xBA, 0x5E, 0x06, 0x0E, 0x36, 0xDE, 0xF6, 0xE6, 0xBE, 0x56, 0x1E, 0x16, 0x3E, 0xD6, 0xEE, 0xE6, 0xBE, 0x56, 0x1E, 0x16, 0x3E, 0xD6, 0xEE, 0xC6, 0x96, 0x76, 0x4E, 0x46, 0x6E, 0x9E, 0xCE, 0xC6, 0x96, 0x76, 0x4E, 0x46, 0x6E, 0x9E, 0xCE, 0x2A, 0x62, 0x82, 0xA2, 0xAA, 0x8A, 0x7A, 0x22, 0x2A, 0x62, 0x82, 0xA2, 0xAA, 0x8A, 0x7A, 0x22, 0x0A, 0x32, 0xDA, 0xF2, 0xFE, 0xB2, 0x5A, 0x02, 0x0A, 0x32, 0xDA, 0xF2, 0xFE, 0xB2, 0x5A, 0x02 }; static const halftone_pattern_t haltfone_pattern_8x8_Fine_Fatting = { 0x02, 0x22, 0x92, 0xB2, 0x0A, 0x2A, 0x9A, 0xBA, 0x02, 0x22, 0x92, 0xB2, 0x0A, 0x2A, 0x9A, 0xBA, 0x42, 0x62, 0xD2, 0xF2, 0x4A, 0x6A, 0xDA, 0xFA, 0x42, 0x62, 0xD2, 0xF2, 0x4A, 0x6A, 0xDA, 0xFA, 0x82, 0xA2, 0x12, 0x32, 0x8A, 0xAA, 0x1A, 0x3A, 0x82, 0xA2, 0x12, 0x32, 0x8A, 0xAA, 0x1A, 0x3A, 0xC2, 0xE2, 0x52, 0x72, 0xCA, 0xEA, 0x5A, 0x7A, 0xC2, 0xE2, 0x52, 0x72, 0xCA, 0xEA, 0x5A, 0x7A, 0x0E, 0x2E, 0x9E, 0xBE, 0x06, 0x26, 0x96, 0xB6, 0x0E, 0x2E, 0x9E, 0xBE, 0x06, 0x26, 0x96, 0xB6, 0x4C, 0x6E, 0xDE, 0xFE, 0x46, 0x66, 0xD6, 0xF6, 0x4C, 0x6E, 0xDE, 0xFE, 0x46, 0x66, 0xD6, 0xF6, 0x8E, 0xAE, 0x1E, 0x3E, 0x86, 0xA6, 0x16, 0x36, 0x8E, 0xAE, 0x1E, 0x3E, 0x86, 0xA6, 0x16, 0x36, 0xCE, 0xEE, 0x60, 0x7E, 0xC6, 0xE6, 0x56, 0x76, 0xCE, 0xEE, 0x60, 0x7E, 0xC6, 0xE6, 0x56, 0x76, 0x02, 0x22, 0x92, 0xB2, 0x0A, 0x2A, 0x9A, 0xBA, 0x02, 0x22, 0x92, 0xB2, 0x0A, 0x2A, 0x9A, 0xBA, 0x42, 0x62, 0xD2, 0xF2, 0x4A, 0x6A, 0xDA, 0xFA, 0x42, 0x62, 0xD2, 0xF2, 0x4A, 0x6A, 0xDA, 0xFA, 0x82, 0xA2, 0x12, 0x32, 0x8A, 0xAA, 0x1A, 0x3A, 0x82, 0xA2, 0x12, 0x32, 0x8A, 0xAA, 0x1A, 0x3A, 0xC2, 0xE2, 0x52, 0x72, 0xCA, 0xEA, 0x5A, 0x7A, 0xC2, 0xE2, 0x52, 0x72, 0xCA, 0xEA, 0x5A, 0x7A, 0x0E, 0x2E, 0x9E, 0xBE, 0x06, 0x26, 0x96, 0xB6, 0x0E, 0x2E, 0x9E, 0xBE, 0x06, 0x26, 0x96, 0xB6, 0x4C, 0x6E, 0xDE, 0xFE, 0x46, 0x66, 0xD6, 0xF6, 0x4C, 0x6E, 0xDE, 0xFE, 0x46, 0x66, 0xD6, 0xF6, 0x8E, 0xAE, 0x1E, 0x3E, 0x86, 0xA6, 0x16, 0x36, 0x8E, 0xAE, 0x1E, 0x3E, 0x86, 0xA6, 0x16, 0x36, 0xCE, 0xEE, 0x60, 0x7E, 0xC6, 0xE6, 0x56, 0x76, 0xCE, 0xEE, 0x60, 0x7E, 0xC6, 0xE6, 0x56, 0x76 }; static const halftone_pattern_t haltfone_pattern_8x8_Bayer = { 0xF2, 0x42, 0x82, 0xC2, 0xFA, 0x4A, 0x8A, 0xCA, 0xF2, 0x42, 0x82, 0xC2, 0xFA, 0x4A, 0x8A, 0xCA, 0xB2, 0x02, 0x12, 0x52, 0xBA, 0x0A, 0x1A, 0x5A, 0xB2, 0x02, 0x12, 0x52, 0xBA, 0x0A, 0x1A, 0x5A, 0x72, 0x32, 0x22, 0x92, 0x7A, 0x3A, 0x2A, 0x9A, 0x72, 0x32, 0x22, 0x92, 0x7A, 0x3A, 0x2A, 0x9A, 0xE2, 0xA2, 0x62, 0xD2, 0xEA, 0xAA, 0x6A, 0xDA, 0xE2, 0xA2, 0x62, 0xD2, 0xEA, 0xAA, 0x6A, 0xDA, 0xFE, 0x4E, 0x8E, 0xCE, 0xF6, 0x46, 0xD6, 0xC6, 0xFE, 0x4E, 0x8E, 0xCE, 0xF6, 0x46, 0xD6, 0xC6, 0xBE, 0x0E, 0x1E, 0x5E, 0xB6, 0x06, 0x16, 0x56, 0xBE, 0x0E, 0x1E, 0x5E, 0xB6, 0x06, 0x16, 0x56, 0x7E, 0x3E, 0x2E, 0x9E, 0x76, 0x36, 0x26, 0x96, 0x7E, 0x3E, 0x2E, 0x9E, 0x76, 0x36, 0x26, 0x96, 0xEE, 0xAE, 0x6E, 0xDE, 0xE6, 0xA6, 0x66, 0xD6, 0xEE, 0xAE, 0x6E, 0xDE, 0xE6, 0xA6, 0x66, 0xD6, 0xF2, 0x42, 0x82, 0xC2, 0xFA, 0x4A, 0x8A, 0xCA, 0xF2, 0x42, 0x82, 0xC2, 0xFA, 0x4A, 0x8A, 0xCA, 0xB2, 0x02, 0x12, 0x52, 0xBA, 0x0A, 0x1A, 0x5A, 0xB2, 0x02, 0x12, 0x52, 0xBA, 0x0A, 0x1A, 0x5A, 0x72, 0x32, 0x22, 0x92, 0x7A, 0x3A, 0x2A, 0x9A, 0x72, 0x32, 0x22, 0x92, 0x7A, 0x3A, 0x2A, 0x9A, 0xE2, 0xA2, 0x62, 0xD2, 0xEA, 0xAA, 0x6A, 0xDA, 0xE2, 0xA2, 0x62, 0xD2, 0xEA, 0xAA, 0x6A, 0xDA, 0xFE, 0x4E, 0x8E, 0xCE, 0xF6, 0x46, 0xD6, 0xC6, 0xFE, 0x4E, 0x8E, 0xCE, 0xF6, 0x46, 0xD6, 0xC6, 0xBE, 0x0E, 0x1E, 0x5E, 0xB6, 0x06, 0x16, 0x56, 0xBE, 0x0E, 0x1E, 0x5E, 0xB6, 0x06, 0x16, 0x56, 0x7E, 0x3E, 0x2E, 0x9E, 0x76, 0x36, 0x26, 0x96, 0x7E, 0x3E, 0x2E, 0x9E, 0x76, 0x36, 0x26, 0x96, 0xEE, 0xAE, 0x6E, 0xDE, 0xE6, 0xA6, 0x66, 0xD6, 0xEE, 0xAE, 0x6E, 0xDE, 0xE6, 0xA6, 0x66, 0xD6 }; static const halftone_pattern_t haltfone_pattern_8x8_Vertical_Line = { 0x02, 0x42, 0x82, 0xC4, 0x0A, 0x4C, 0x8A, 0xCA, 0x02, 0x42, 0x82, 0xC4, 0x0A, 0x4C, 0x8A, 0xCA, 0x12, 0x52, 0x92, 0xD2, 0x1A, 0x5A, 0x9A, 0xDA, 0x12, 0x52, 0x92, 0xD2, 0x1A, 0x5A, 0x9A, 0xDA, 0x22, 0x62, 0xA2, 0xE2, 0x2A, 0x6A, 0xAA, 0xEA, 0x22, 0x62, 0xA2, 0xE2, 0x2A, 0x6A, 0xAA, 0xEA, 0x32, 0x72, 0xB2, 0xF2, 0x3A, 0x7A, 0xBA, 0xFA, 0x32, 0x72, 0xB2, 0xF2, 0x3A, 0x7A, 0xBA, 0xFA, 0x0E, 0x4E, 0x8E, 0xCE, 0x06, 0x46, 0x86, 0xC6, 0x0E, 0x4E, 0x8E, 0xCE, 0x06, 0x46, 0x86, 0xC6, 0x1E, 0x5E, 0x9E, 0xDE, 0x16, 0x56, 0x96, 0xD6, 0x1E, 0x5E, 0x9E, 0xDE, 0x16, 0x56, 0x96, 0xD6, 0x2E, 0x6E, 0xAE, 0xEE, 0x26, 0x66, 0xA6, 0xE6, 0x2E, 0x6E, 0xAE, 0xEE, 0x26, 0x66, 0xA6, 0xE6, 0x3E, 0x7E, 0xBE, 0xFE, 0x36, 0x76, 0xB6, 0xF6, 0x3E, 0x7E, 0xBE, 0xFE, 0x36, 0x76, 0xB6, 0xF6, 0x02, 0x42, 0x82, 0xC4, 0x0A, 0x4C, 0x8A, 0xCA, 0x02, 0x42, 0x82, 0xC4, 0x0A, 0x4C, 0x8A, 0xCA, 0x12, 0x52, 0x92, 0xD2, 0x1A, 0x5A, 0x9A, 0xDA, 0x12, 0x52, 0x92, 0xD2, 0x1A, 0x5A, 0x9A, 0xDA, 0x22, 0x62, 0xA2, 0xE2, 0x2A, 0x6A, 0xAA, 0xEA, 0x22, 0x62, 0xA2, 0xE2, 0x2A, 0x6A, 0xAA, 0xEA, 0x32, 0x72, 0xB2, 0xF2, 0x3A, 0x7A, 0xBA, 0xFA, 0x32, 0x72, 0xB2, 0xF2, 0x3A, 0x7A, 0xBA, 0xFA, 0x0E, 0x4E, 0x8E, 0xCE, 0x06, 0x46, 0x86, 0xC6, 0x0E, 0x4E, 0x8E, 0xCE, 0x06, 0x46, 0x86, 0xC6, 0x1E, 0x5E, 0x9E, 0xDE, 0x16, 0x56, 0x96, 0xD6, 0x1E, 0x5E, 0x9E, 0xDE, 0x16, 0x56, 0x96, 0xD6, 0x2E, 0x6E, 0xAE, 0xEE, 0x26, 0x66, 0xA6, 0xE6, 0x2E, 0x6E, 0xAE, 0xEE, 0x26, 0x66, 0xA6, 0xE6, 0x3E, 0x7E, 0xBE, 0xFE, 0x36, 0x76, 0xB6, 0xF6, 0x3E, 0x7E, 0xBE, 0xFE, 0x36, 0x76, 0xB6, 0xF6 }; backends-1.3.0/backend/lexmark.c000066400000000000000000001115611456256263500165060ustar00rootroot00000000000000/* lexmark.c: SANE backend for Lexmark scanners. (C) 2003-2004 Lexmark International, Inc. (Original Source code) (C) 2005 Fred Odendaal (C) 2006-2013 Stéphane Voltz (C) 2010 "Torsten Houwaart" X74 support 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, see . 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 "lexmark.h" #define LEXMARK_CONFIG_FILE "lexmark.conf" #define BUILD 32 #define MAX_OPTION_STRING_SIZE 255 static Lexmark_Device *first_lexmark_device = 0; static SANE_Int num_lexmark_device = 0; static const SANE_Device **sane_device_list = NULL; /* Program globals F.O - Should this be per device?*/ static SANE_Bool initialized = SANE_FALSE; static SANE_String_Const mode_list[] = { SANE_VALUE_SCAN_MODE_COLOR, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_LINEART, NULL }; /* possible resolutions are: 75x75, 150x150, 300x300, 600x600, 600x1200 */ static SANE_Int x1100_dpi_list[] = { 5, 75, 150, 300, 600, 1200 }; static SANE_Int a920_dpi_list[] = { 4, 75, 150, 300, 600 }; static SANE_Int x1200_dpi_list[] = { 4, 75, 150, 300, 600 }; static SANE_Int x74_dpi_list[] = { 75, 150, 300, 600 }; static SANE_Range threshold_range = { SANE_FIX (0.0), /* minimum */ SANE_FIX (100.0), /* maximum */ SANE_FIX (1.0) /* quantization */ }; static const SANE_Range gain_range = { 0, /* minimum */ 31, /* maximum */ 0 /* quantization */ }; /* for now known models (2 ...) have the same scan window geometry. coordinates are expressed in pixels, with a quantization factor of 8 to have 'even' coordinates at 75 dpi */ static SANE_Range x_range = { 0, /* minimum */ 5104, /* maximum */ 16 /* quantization : 16 is required so we never have an odd width */ }; static SANE_Range y_range = { 0, /* minimum */ 6848, /* maximum */ /* 7032, for X74 */ 8 /* quantization */ }; /* static functions */ static SANE_Status init_options (Lexmark_Device * lexmark_device); static SANE_Status attachLexmark (SANE_String_Const devname); SANE_Status init_options (Lexmark_Device * dev) { SANE_Option_Descriptor *od; DBG (2, "init_options: dev = %p\n", (void *) dev); /* number of options */ od = &(dev->opt[OPT_NUM_OPTS]); od->name = SANE_NAME_NUM_OPTIONS; od->title = SANE_TITLE_NUM_OPTIONS; od->desc = SANE_DESC_NUM_OPTIONS; od->type = SANE_TYPE_INT; od->unit = SANE_UNIT_NONE; od->size = sizeof (SANE_Word); od->cap = SANE_CAP_SOFT_DETECT; od->constraint_type = SANE_CONSTRAINT_NONE; od->constraint.range = 0; dev->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* mode - sets the scan mode: Color, Gray, or Line Art */ od = &(dev->opt[OPT_MODE]); od->name = SANE_NAME_SCAN_MODE; od->title = SANE_TITLE_SCAN_MODE; od->desc = SANE_DESC_SCAN_MODE;; od->type = SANE_TYPE_STRING; od->unit = SANE_UNIT_NONE; od->size = MAX_OPTION_STRING_SIZE; od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; od->constraint_type = SANE_CONSTRAINT_STRING_LIST; od->constraint.string_list = mode_list; dev->val[OPT_MODE].s = malloc (od->size); if (!dev->val[OPT_MODE].s) return SANE_STATUS_NO_MEM; strcpy (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR); /* resolution */ od = &(dev->opt[OPT_RESOLUTION]); od->name = SANE_NAME_SCAN_RESOLUTION; od->title = SANE_TITLE_SCAN_RESOLUTION; od->desc = SANE_DESC_SCAN_RESOLUTION; od->type = SANE_TYPE_INT; od->unit = SANE_UNIT_DPI; od->size = sizeof (SANE_Word); od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; od->constraint_type = SANE_CONSTRAINT_WORD_LIST; switch (dev->model.sensor_type) { case X1100_2C_SENSOR: case A920_SENSOR: od->constraint.word_list = a920_dpi_list; break; case X1100_B2_SENSOR: od->constraint.word_list = x1100_dpi_list; break; case X1200_SENSOR: case X1200_USB2_SENSOR: od->constraint.word_list = x1200_dpi_list; break; case X74_SENSOR: od->constraint.word_list = x74_dpi_list; break; } dev->val[OPT_RESOLUTION].w = 75; /* preview mode */ od = &(dev->opt[OPT_PREVIEW]); od->name = SANE_NAME_PREVIEW; od->title = SANE_TITLE_PREVIEW; od->desc = SANE_DESC_PREVIEW; od->size = sizeof (SANE_Word); od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; od->type = SANE_TYPE_BOOL; od->constraint_type = SANE_CONSTRAINT_NONE; dev->val[OPT_PREVIEW].w = SANE_FALSE; /* "Geometry" group: */ od = &(dev->opt[OPT_GEOMETRY_GROUP]); od->name = ""; od->title = SANE_I18N ("Geometry"); od->desc = ""; od->type = SANE_TYPE_GROUP; od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; od->size = 0; od->constraint_type = SANE_CONSTRAINT_NONE; /* top-left x */ od = &(dev->opt[OPT_TL_X]); od->name = SANE_NAME_SCAN_TL_X; od->title = SANE_TITLE_SCAN_TL_X; od->desc = SANE_DESC_SCAN_TL_X; od->type = SANE_TYPE_INT; od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; od->size = sizeof (SANE_Word); od->unit = SANE_UNIT_PIXEL; od->constraint_type = SANE_CONSTRAINT_RANGE; od->constraint.range = &x_range; dev->val[OPT_TL_X].w = 0; /* top-left y */ od = &(dev->opt[OPT_TL_Y]); od->name = SANE_NAME_SCAN_TL_Y; od->title = SANE_TITLE_SCAN_TL_Y; od->desc = SANE_DESC_SCAN_TL_Y; od->type = SANE_TYPE_INT; od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; od->size = sizeof (SANE_Word); od->unit = SANE_UNIT_PIXEL; od->constraint_type = SANE_CONSTRAINT_RANGE; od->constraint.range = &y_range; dev->val[OPT_TL_Y].w = 0; /* bottom-right x */ od = &(dev->opt[OPT_BR_X]); od->name = SANE_NAME_SCAN_BR_X; od->title = SANE_TITLE_SCAN_BR_X; od->desc = SANE_DESC_SCAN_BR_X; od->type = SANE_TYPE_INT; od->size = sizeof (SANE_Word); od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; od->unit = SANE_UNIT_PIXEL; od->constraint_type = SANE_CONSTRAINT_RANGE; od->constraint.range = &x_range; dev->val[OPT_BR_X].w = x_range.max; /* bottom-right y */ od = &(dev->opt[OPT_BR_Y]); od->name = SANE_NAME_SCAN_BR_Y; od->title = SANE_TITLE_SCAN_BR_Y; od->desc = SANE_DESC_SCAN_BR_Y; od->type = SANE_TYPE_INT; od->size = sizeof (SANE_Word); od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; od->unit = SANE_UNIT_PIXEL; od->constraint_type = SANE_CONSTRAINT_RANGE; od->constraint.range = &y_range; dev->val[OPT_BR_Y].w = y_range.max; /* threshold */ od = &(dev->opt[OPT_THRESHOLD]); od->name = SANE_NAME_THRESHOLD; od->title = SANE_TITLE_THRESHOLD; od->desc = SANE_DESC_THRESHOLD; od->type = SANE_TYPE_FIXED; od->unit = SANE_UNIT_PERCENT; od->size = sizeof (SANE_Fixed); od->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE; od->constraint_type = SANE_CONSTRAINT_RANGE; od->constraint.range = &threshold_range; dev->val[OPT_THRESHOLD].w = SANE_FIX (50.0); /* gain group */ dev->opt[OPT_MANUAL_GAIN].name = "manual-channel-gain"; dev->opt[OPT_MANUAL_GAIN].title = SANE_I18N ("Gain"); dev->opt[OPT_MANUAL_GAIN].desc = SANE_I18N ("Color channels gain settings"); dev->opt[OPT_MANUAL_GAIN].type = SANE_TYPE_BOOL; dev->opt[OPT_MANUAL_GAIN].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; dev->opt[OPT_MANUAL_GAIN].size = sizeof (SANE_Bool); dev->val[OPT_MANUAL_GAIN].w = SANE_FALSE; /* gray gain */ dev->opt[OPT_GRAY_GAIN].name = "gray-gain"; dev->opt[OPT_GRAY_GAIN].title = SANE_I18N ("Gray gain"); dev->opt[OPT_GRAY_GAIN].desc = SANE_I18N ("Sets gray channel gain"); dev->opt[OPT_GRAY_GAIN].type = SANE_TYPE_INT; dev->opt[OPT_GRAY_GAIN].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; dev->opt[OPT_GRAY_GAIN].unit = SANE_UNIT_NONE; dev->opt[OPT_GRAY_GAIN].size = sizeof (SANE_Int); dev->opt[OPT_GRAY_GAIN].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_GRAY_GAIN].constraint.range = &gain_range; dev->val[OPT_GRAY_GAIN].w = 10; /* red gain */ dev->opt[OPT_RED_GAIN].name = "red-gain"; dev->opt[OPT_RED_GAIN].title = SANE_I18N ("Red gain"); dev->opt[OPT_RED_GAIN].desc = SANE_I18N ("Sets red channel gain"); dev->opt[OPT_RED_GAIN].type = SANE_TYPE_INT; dev->opt[OPT_RED_GAIN].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; dev->opt[OPT_RED_GAIN].unit = SANE_UNIT_NONE; dev->opt[OPT_RED_GAIN].size = sizeof (SANE_Int); dev->opt[OPT_RED_GAIN].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_RED_GAIN].constraint.range = &gain_range; dev->val[OPT_RED_GAIN].w = 10; /* green gain */ dev->opt[OPT_GREEN_GAIN].name = "green-gain"; dev->opt[OPT_GREEN_GAIN].title = SANE_I18N ("Green gain"); dev->opt[OPT_GREEN_GAIN].desc = SANE_I18N ("Sets green channel gain"); dev->opt[OPT_GREEN_GAIN].type = SANE_TYPE_INT; dev->opt[OPT_GREEN_GAIN].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; dev->opt[OPT_GREEN_GAIN].unit = SANE_UNIT_NONE; dev->opt[OPT_GREEN_GAIN].size = sizeof (SANE_Int); dev->opt[OPT_GREEN_GAIN].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_GREEN_GAIN].constraint.range = &gain_range; dev->val[OPT_GREEN_GAIN].w = 10; /* blue gain */ dev->opt[OPT_BLUE_GAIN].name = "blue-gain"; dev->opt[OPT_BLUE_GAIN].title = SANE_I18N ("Blue gain"); dev->opt[OPT_BLUE_GAIN].desc = SANE_I18N ("Sets blue channel gain"); dev->opt[OPT_BLUE_GAIN].type = SANE_TYPE_INT; dev->opt[OPT_BLUE_GAIN].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; dev->opt[OPT_BLUE_GAIN].unit = SANE_UNIT_NONE; dev->opt[OPT_BLUE_GAIN].size = sizeof (SANE_Int); dev->opt[OPT_BLUE_GAIN].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_BLUE_GAIN].constraint.range = &gain_range; dev->val[OPT_BLUE_GAIN].w = 10; return SANE_STATUS_GOOD; } /***************************** SANE API ****************************/ SANE_Status attachLexmark (SANE_String_Const devname) { Lexmark_Device *lexmark_device; SANE_Int dn, vendor, product, variant; SANE_Status status; DBG (2, "attachLexmark: devname=%s\n", devname); for (lexmark_device = first_lexmark_device; lexmark_device; lexmark_device = lexmark_device->next) { /* already attached devices */ if (strcmp (lexmark_device->sane.name, devname) == 0) { lexmark_device->missing = SANE_FALSE; return SANE_STATUS_GOOD; } } lexmark_device = (Lexmark_Device *) malloc (sizeof (Lexmark_Device)); if (lexmark_device == NULL) return SANE_STATUS_NO_MEM; #ifdef FAKE_USB status = SANE_STATUS_GOOD; #else status = sanei_usb_open (devname, &dn); #endif if (status != SANE_STATUS_GOOD) { DBG (1, "attachLexmark: couldn't open device `%s': %s\n", devname, sane_strstatus (status)); return status; } else DBG (2, "attachLexmark: device `%s' successfully opened\n", devname); #ifdef FAKE_USB status = SANE_STATUS_GOOD; /* put the id of the model you want to fake here */ vendor = 0x043d; product = 0x007c; /* X11xx */ variant = 0xb2; #else variant = 0; status = sanei_usb_get_vendor_product (dn, &vendor, &product); #endif if (status != SANE_STATUS_GOOD) { DBG (1, "attachLexmark: couldn't get vendor and product ids of device `%s': %s\n", devname, sane_strstatus (status)); #ifndef FAKE_USB sanei_usb_close (dn); #endif return status; } #ifndef FAKE_USB sanei_usb_close (dn); #endif DBG (2, "attachLexmark: testing device `%s': 0x%04x:0x%04x, variant=%d\n", devname, vendor, product, variant); if (sanei_lexmark_low_assign_model (lexmark_device, devname, vendor, product, variant) != SANE_STATUS_GOOD) { DBG (2, "attachLexmark: unsupported device `%s': 0x%04x:0x%04x\n", devname, vendor, product); return SANE_STATUS_UNSUPPORTED; } /* add new device to device list */ /* there are two variant of the scanner with the same USB id, * so we need to read registers from scanner to detect which one * is really connected */ status = sanei_lexmark_low_open_device (lexmark_device); sanei_usb_close (lexmark_device->devnum); /* set up scanner start status */ sanei_lexmark_low_init (lexmark_device); /* Set the default resolution here */ lexmark_device->x_dpi = 75; lexmark_device->y_dpi = 75; /* Make the pointer to the read buffer null here */ lexmark_device->read_buffer = NULL; /* Set the default threshold for lineart mode here */ lexmark_device->threshold = 0x80; lexmark_device->shading_coeff = NULL; /* mark device as present */ lexmark_device->missing = SANE_FALSE; /* insert it a the start of the chained list */ lexmark_device->next = first_lexmark_device; first_lexmark_device = lexmark_device; num_lexmark_device++; return status; } /** probe for supported lexmark devices * This function scan usb and try to attached to scanner * configured in lexmark.conf . */ static SANE_Status probe_lexmark_devices (void) { FILE *fp; SANE_Char line[PATH_MAX]; const char *lp; SANE_Int vendor, product; size_t len; Lexmark_Device *dev; /* mark already detected devices as missing, during device probe * detected devices will clear this flag */ dev = first_lexmark_device; while (dev != NULL) { dev->missing = SANE_TRUE; dev = dev->next; } /* open config file, parse option and try to open * any device configure in it */ fp = sanei_config_open (LEXMARK_CONFIG_FILE); if (!fp) { return SANE_STATUS_ACCESS_DENIED; } while (sanei_config_read (line, PATH_MAX, fp)) { /* 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); /* skip empty lines */ if (*lp == 0) continue; if (sscanf (lp, "usb %i %i", &vendor, &product) == 2) ; else if (strncmp ("libusb", lp, 6) == 0) ; else if ((strncmp ("usb", lp, 3) == 0) && isspace (lp[3])) { lp += 3; lp = sanei_config_skip_whitespace (lp); } else continue; #ifdef FAKE_USB attachLexmark ("FAKE_USB"); #else sanei_usb_attach_matching_devices (lp, attachLexmark); #endif } fclose (fp); return SANE_STATUS_GOOD; } SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize) { SANE_Status status; DBG_INIT (); DBG (1, "SANE Lexmark backend version %d.%d.%d-devel\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); DBG (2, "sane_init: version_code=%p\n", (void *) version_code); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); #ifndef FAKE_USB sanei_usb_init (); #endif status = probe_lexmark_devices (); if (status == SANE_STATUS_GOOD) { initialized = SANE_TRUE; } else { initialized = SANE_FALSE; } return status; } void sane_exit (void) { Lexmark_Device *lexmark_device, *next_lexmark_device; DBG (2, "sane_exit\n"); if (!initialized) return; for (lexmark_device = first_lexmark_device; lexmark_device; lexmark_device = next_lexmark_device) { next_lexmark_device = lexmark_device->next; sanei_lexmark_low_destroy (lexmark_device); free (lexmark_device); } if (sane_device_list) free (sane_device_list); sanei_usb_exit(); initialized = SANE_FALSE; return; } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { Lexmark_Device *lexmark_device; SANE_Int index; DBG (2, "sane_get_devices: device_list=%p, local_only=%d\n", (void *) device_list, local_only); /* hot-plug case : detection of newly connected scanners */ sanei_usb_scan_devices (); probe_lexmark_devices (); if (sane_device_list) free (sane_device_list); sane_device_list = malloc ((num_lexmark_device + 1) * sizeof (sane_device_list[0])); if (!sane_device_list) return SANE_STATUS_NO_MEM; index = 0; lexmark_device = first_lexmark_device; while (lexmark_device != NULL) { if (lexmark_device->missing == SANE_FALSE) { sane_device_list[index] = &(lexmark_device->sane); index++; } lexmark_device = lexmark_device->next; } sane_device_list[index] = 0; *device_list = sane_device_list; return SANE_STATUS_GOOD; } /** * Open the backend, ie return the struct handle of a detected scanner * The struct returned is choosne if it matches the name given, which is * useful when several scanners handled by the backend have been detected. * However, special case empty string "" and "lexmark" pick the first * available handle. */ SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { Lexmark_Device *lexmark_device; SANE_Status status; DBG (2, "sane_open: devicename=\"%s\", handle=%p\n", devicename, (void *) handle); if (!initialized) { DBG (2, "sane_open: not initialized\n"); return SANE_STATUS_INVAL; } if (!handle) { DBG (2, "sane_open: no handle\n"); return SANE_STATUS_INVAL; } /* walk the linked list of scanner device until there is a match * with the device name */ for (lexmark_device = first_lexmark_device; lexmark_device; lexmark_device = lexmark_device->next) { DBG (2, "sane_open: devname from list: %s\n", lexmark_device->sane.name); if (strcmp (devicename, "") == 0 || strcmp (devicename, "lexmark") == 0 || strcmp (devicename, lexmark_device->sane.name) == 0) break; } *handle = lexmark_device; if (!lexmark_device) { DBG (2, "sane_open: Not a lexmark device\n"); return SANE_STATUS_INVAL; } status = init_options (lexmark_device); if (status != SANE_STATUS_GOOD) return status; status = sanei_lexmark_low_open_device (lexmark_device); DBG (2, "sane_open: end.\n"); return status; } void sane_close (SANE_Handle handle) { Lexmark_Device *lexmark_device; DBG (2, "sane_close: handle=%p\n", (void *) handle); if (!initialized) return; for (lexmark_device = first_lexmark_device; lexmark_device; lexmark_device = lexmark_device->next) { if (lexmark_device == handle) break; } if (!lexmark_device) return; sanei_lexmark_low_close_device (lexmark_device); return; } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { Lexmark_Device *lexmark_device; DBG (2, "sane_get_option_descriptor: handle=%p, option = %d\n", (void *) handle, option); if (!initialized) return NULL; /* Check for valid option number */ if ((option < 0) || (option >= NUM_OPTIONS)) return NULL; for (lexmark_device = first_lexmark_device; lexmark_device; lexmark_device = lexmark_device->next) { if (lexmark_device == handle) break; } if (!lexmark_device) return NULL; if (lexmark_device->opt[option].name) { DBG (2, "sane_get_option_descriptor: name=%s\n", lexmark_device->opt[option].name); } return &(lexmark_device->opt[option]); } /* rebuilds parameters if needed, called each time SANE_INFO_RELOAD_OPTIONS is set */ static void calc_parameters (Lexmark_Device * lexmark_device) { if (strcmp (lexmark_device->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) == 0) { lexmark_device->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE; } else { lexmark_device->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; } /* changing color mode implies changing gain setting */ if (lexmark_device->val[OPT_MANUAL_GAIN].w == SANE_TRUE) { if (strcmp (lexmark_device->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) != 0) { lexmark_device->opt[OPT_GRAY_GAIN].cap &= ~SANE_CAP_INACTIVE; lexmark_device->opt[OPT_RED_GAIN].cap |= SANE_CAP_INACTIVE; lexmark_device->opt[OPT_GREEN_GAIN].cap |= SANE_CAP_INACTIVE; lexmark_device->opt[OPT_BLUE_GAIN].cap |= SANE_CAP_INACTIVE; } else { lexmark_device->opt[OPT_GRAY_GAIN].cap |= SANE_CAP_INACTIVE; lexmark_device->opt[OPT_RED_GAIN].cap &= ~SANE_CAP_INACTIVE; lexmark_device->opt[OPT_GREEN_GAIN].cap &= ~SANE_CAP_INACTIVE; lexmark_device->opt[OPT_BLUE_GAIN].cap &= ~SANE_CAP_INACTIVE; } } else { lexmark_device->opt[OPT_GRAY_GAIN].cap |= SANE_CAP_INACTIVE; lexmark_device->opt[OPT_RED_GAIN].cap |= SANE_CAP_INACTIVE; lexmark_device->opt[OPT_GREEN_GAIN].cap |= SANE_CAP_INACTIVE; lexmark_device->opt[OPT_BLUE_GAIN].cap |= SANE_CAP_INACTIVE; } } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int * info) { Lexmark_Device *lexmark_device; SANE_Status status; SANE_Word w; DBG (2, "sane_control_option: handle=%p, opt=%d, act=%d, val=%p, info=%p\n", (void *) handle, option, action, (void *) value, (void *) info); if (!initialized) return SANE_STATUS_INVAL; for (lexmark_device = first_lexmark_device; lexmark_device; lexmark_device = lexmark_device->next) { if (lexmark_device == handle) break; } if (!lexmark_device) return SANE_STATUS_INVAL; if (value == NULL) return SANE_STATUS_INVAL; if (info != NULL) *info = 0; if (option < 0 || option >= NUM_OPTIONS) return SANE_STATUS_INVAL; if (lexmark_device->opt[option].type == SANE_TYPE_GROUP) return SANE_STATUS_INVAL; switch (action) { case SANE_ACTION_SET_AUTO: if (!SANE_OPTION_IS_SETTABLE (lexmark_device->opt[option].cap)) return SANE_STATUS_INVAL; if (!(lexmark_device->opt[option].cap & SANE_CAP_AUTOMATIC)) return SANE_STATUS_INVAL; break; case SANE_ACTION_SET_VALUE: if (!SANE_OPTION_IS_SETTABLE (lexmark_device->opt[option].cap)) return SANE_STATUS_INVAL; /* Make sure boolean values are only TRUE or FALSE */ if (lexmark_device->opt[option].type == SANE_TYPE_BOOL) { if (! ((*(SANE_Bool *) value == SANE_FALSE) || (*(SANE_Bool *) value == SANE_TRUE))) return SANE_STATUS_INVAL; } /* Check range constraints */ if (lexmark_device->opt[option].constraint_type == SANE_CONSTRAINT_RANGE) { status = sanei_constrain_value (&(lexmark_device->opt[option]), value, info); if (status != SANE_STATUS_GOOD) { DBG (2, "SANE_CONTROL_OPTION: Bad value for range\n"); return SANE_STATUS_INVAL; } } switch (option) { case OPT_NUM_OPTS: case OPT_RESOLUTION: lexmark_device->val[option].w = *(SANE_Int *) value; sane_get_parameters (handle, 0); break; case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: DBG (2, "Option value set to %d (%s)\n", *(SANE_Word *) value, lexmark_device->opt[option].name); lexmark_device->val[option].w = *(SANE_Word *) value; if (lexmark_device->val[OPT_TL_X].w > lexmark_device->val[OPT_BR_X].w) { w = lexmark_device->val[OPT_TL_X].w; lexmark_device->val[OPT_TL_X].w = lexmark_device->val[OPT_BR_X].w; lexmark_device->val[OPT_BR_X].w = w; if (info) *info |= SANE_INFO_RELOAD_PARAMS; } if (lexmark_device->val[OPT_TL_Y].w > lexmark_device->val[OPT_BR_Y].w) { w = lexmark_device->val[OPT_TL_Y].w; lexmark_device->val[OPT_TL_Y].w = lexmark_device->val[OPT_BR_Y].w; lexmark_device->val[OPT_BR_Y].w = w; if (info) *info |= SANE_INFO_RELOAD_PARAMS; } break; case OPT_THRESHOLD: lexmark_device->val[option].w = *(SANE_Fixed *) value; lexmark_device->threshold = (0xFF * lexmark_device->val[option].w) / 100; break; case OPT_PREVIEW: lexmark_device->val[option].w = *(SANE_Int *) value; if (*(SANE_Word *) value) { lexmark_device->y_dpi = lexmark_device->val[OPT_RESOLUTION].w; lexmark_device->val[OPT_RESOLUTION].w = 75; } else { lexmark_device->val[OPT_RESOLUTION].w = lexmark_device->y_dpi; } if (info) *info |= SANE_INFO_RELOAD_OPTIONS; sane_get_parameters (handle, 0); if (info) *info |= SANE_INFO_RELOAD_PARAMS; break; case OPT_GRAY_GAIN: case OPT_GREEN_GAIN: case OPT_RED_GAIN: case OPT_BLUE_GAIN: lexmark_device->val[option].w = *(SANE_Word *) value; return SANE_STATUS_GOOD; break; case OPT_MODE: strcpy (lexmark_device->val[option].s, value); calc_parameters (lexmark_device); if (info) *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_MANUAL_GAIN: w = *(SANE_Word *) value; if (w == lexmark_device->val[OPT_MANUAL_GAIN].w) return SANE_STATUS_GOOD; /* no change */ lexmark_device->val[OPT_MANUAL_GAIN].w = w; calc_parameters (lexmark_device); if (info) *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; } if (info != NULL) *info |= SANE_INFO_RELOAD_PARAMS; break; case SANE_ACTION_GET_VALUE: switch (option) { case OPT_NUM_OPTS: case OPT_RESOLUTION: case OPT_PREVIEW: case OPT_MANUAL_GAIN: case OPT_GRAY_GAIN: case OPT_GREEN_GAIN: case OPT_RED_GAIN: case OPT_BLUE_GAIN: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: *(SANE_Word *) value = lexmark_device->val[option].w; DBG (2, "Option value = %d (%s)\n", *(SANE_Word *) value, lexmark_device->opt[option].name); break; case OPT_THRESHOLD: *(SANE_Fixed *) value = lexmark_device->val[option].w; DBG (2, "Option value = %f\n", SANE_UNFIX (*(SANE_Fixed *) value)); break; case OPT_MODE: strcpy (value, lexmark_device->val[option].s); break; default: return SANE_STATUS_INVAL; } break; default: return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { Lexmark_Device *lexmark_device; SANE_Parameters *device_params; SANE_Int xres, yres, width_px, height_px; SANE_Int channels, bitsperchannel; DBG (2, "sane_get_parameters: handle=%p, params=%p\n", (void *) handle, (void *) params); if (!initialized) return SANE_STATUS_INVAL; for (lexmark_device = first_lexmark_device; lexmark_device; lexmark_device = lexmark_device->next) { if (lexmark_device == handle) break; } if (!lexmark_device) return SANE_STATUS_INVAL; yres = lexmark_device->val[OPT_RESOLUTION].w; if (yres == 1200) xres = 600; else xres = yres; /* 24 bit colour = 8 bits/channel for each of the RGB channels */ channels = 3; bitsperchannel = 8; /* If not color there is only 1 channel */ if (strcmp (lexmark_device->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) != 0) { channels = 1; bitsperchannel = 8; } /* geometry in pixels */ width_px = lexmark_device->val[OPT_BR_X].w - lexmark_device->val[OPT_TL_X].w; height_px = lexmark_device->val[OPT_BR_Y].w - lexmark_device->val[OPT_TL_Y].w; DBG (7, "sane_get_parameters: tl=(%d,%d) br=(%d,%d)\n", lexmark_device->val[OPT_TL_X].w, lexmark_device->val[OPT_TL_Y].w, lexmark_device->val[OPT_BR_X].w, lexmark_device->val[OPT_BR_Y].w); /* we must tell the front end the bitsperchannel for lineart is really */ /* only 1, so it can calculate the correct image size */ /* If not color there is only 1 channel */ if (strcmp (lexmark_device->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) == 0) { bitsperchannel = 1; } device_params = &(lexmark_device->params); device_params->format = SANE_FRAME_RGB; if (channels == 1) device_params->format = SANE_FRAME_GRAY; device_params->last_frame = SANE_TRUE; device_params->lines = (height_px * yres) / 600; device_params->depth = bitsperchannel; device_params->pixels_per_line = (width_px * xres) / 600; /* we always read an even number of sensor pixels */ if (device_params->pixels_per_line & 1) device_params->pixels_per_line++; /* data_size is the size transferred from the scanner to the backend */ /* therefore bitsperchannel is the same for gray and lineart */ /* note: bytes_per_line has been divided by 8 in lineart mode */ lexmark_device->data_size = channels * device_params->pixels_per_line * device_params->lines; if (bitsperchannel == 1) { device_params->bytes_per_line = (SANE_Int) ((7 + device_params->pixels_per_line) / 8); } else { device_params->bytes_per_line = (SANE_Int) (channels * device_params->pixels_per_line); } DBG (2, "sane_get_parameters: Data size determined as %ld\n", lexmark_device->data_size); DBG (2, "sane_get_parameters: \n"); if (device_params->format == SANE_FRAME_GRAY) DBG (2, " format: SANE_FRAME_GRAY\n"); else if (device_params->format == SANE_FRAME_RGB) DBG (2, " format: SANE_FRAME_RGB\n"); else DBG (2, " format: UNKNOWN\n"); if (device_params->last_frame == SANE_TRUE) DBG (2, " last_frame: TRUE\n"); else DBG (2, " last_frame: FALSE\n"); DBG (2, " lines %d\n", device_params->lines); DBG (2, " depth %d\n", device_params->depth); DBG (2, " pixels_per_line %d\n", device_params->pixels_per_line); DBG (2, " bytes_per_line %d\n", device_params->bytes_per_line); if (params != 0) { params->format = device_params->format; params->last_frame = device_params->last_frame; params->lines = device_params->lines; params->depth = device_params->depth; params->pixels_per_line = device_params->pixels_per_line; params->bytes_per_line = device_params->bytes_per_line; } return SANE_STATUS_GOOD; } SANE_Status sane_start (SANE_Handle handle) { Lexmark_Device *lexmark_device; SANE_Int offset; SANE_Status status; int resolution; DBG (2, "sane_start: handle=%p\n", (void *) handle); if (!initialized) return SANE_STATUS_INVAL; for (lexmark_device = first_lexmark_device; lexmark_device; lexmark_device = lexmark_device->next) { if (lexmark_device == handle) break; } sane_get_parameters (handle, 0); if ((lexmark_device->params.lines == 0) || (lexmark_device->params.pixels_per_line == 0) || (lexmark_device->params.bytes_per_line == 0)) { DBG (2, "sane_start: \n"); DBG (2, " ERROR: Zero size encountered in:\n"); DBG (2, " number of lines, bytes per line, or pixels per line\n"); return SANE_STATUS_INVAL; } lexmark_device->device_cancelled = SANE_FALSE; lexmark_device->data_ctr = 0; lexmark_device->eof = SANE_FALSE; /* Need this cancel_ctr to determine how many times sane_cancel is called since it is called more than once. */ lexmark_device->cancel_ctr = 0; /* Find Home */ if (sanei_lexmark_low_search_home_fwd (lexmark_device)) { DBG (2, "sane_start: Scan head initially at home position\n"); } else { /* We may have been rewound too far, so move forward the distance from the edge to the home position */ sanei_lexmark_low_move_fwd (0x01a8, lexmark_device, lexmark_device->shadow_regs); /* Scan backwards until we find home */ sanei_lexmark_low_search_home_bwd (lexmark_device); } /* do calibration before offset detection , use sensor max dpi, not motor's one */ resolution = lexmark_device->val[OPT_RESOLUTION].w; if (resolution > 600) { resolution = 600; } sanei_lexmark_low_set_scan_regs (lexmark_device, resolution, 0, SANE_FALSE); status = sanei_lexmark_low_calibration (lexmark_device); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: calibration failed : %s ! \n", sane_strstatus (status)); return status; } /* At this point we're somewhere in the dot. We need to read a number of lines greater than the diameter of the dot and determine how many lines past the dot we've gone. We then use this information to see how far the scan head must move before starting the scan. */ /* offset is in 600 dpi unit */ offset = sanei_lexmark_low_find_start_line (lexmark_device); DBG (7, "start line offset=%d\n", offset); /* Set the shadow registers for scan with the options (resolution, mode, size) set in the front end. Pass the offset so we can get the vert. start. */ sanei_lexmark_low_set_scan_regs (lexmark_device, lexmark_device->val[OPT_RESOLUTION].w, offset, SANE_TRUE); if (sanei_lexmark_low_start_scan (lexmark_device) == SANE_STATUS_GOOD) { DBG (2, "sane_start: scan started\n"); return SANE_STATUS_GOOD; } else { lexmark_device->device_cancelled = SANE_TRUE; return SANE_STATUS_INVAL; } } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * data, SANE_Int max_length, SANE_Int * length) { Lexmark_Device *lexmark_device; long bytes_read; DBG (2, "sane_read: handle=%p, data=%p, max_length = %d, length=%p\n", (void *) handle, (void *) data, max_length, (void *) length); if (!initialized) { DBG (2, "sane_read: Not initialized\n"); return SANE_STATUS_INVAL; } for (lexmark_device = first_lexmark_device; lexmark_device; lexmark_device = lexmark_device->next) { if (lexmark_device == handle) break; } if (lexmark_device->device_cancelled) { DBG (2, "sane_read: Device was cancelled\n"); /* We don't know how far we've gone, so search for home. */ sanei_lexmark_low_search_home_bwd (lexmark_device); return SANE_STATUS_EOF; } if (!length) { DBG (2, "sane_read: NULL length pointer\n"); return SANE_STATUS_INVAL; } *length = 0; if (lexmark_device->eof) { DBG (2, "sane_read: Trying to read past EOF\n"); return SANE_STATUS_EOF; } if (!data) return SANE_STATUS_INVAL; bytes_read = sanei_lexmark_low_read_scan_data (data, max_length, lexmark_device); if (bytes_read < 0) return SANE_STATUS_IO_ERROR; else if (bytes_read == 0) return SANE_STATUS_EOF; else { *length = bytes_read; lexmark_device->data_ctr += bytes_read; } return SANE_STATUS_GOOD; } void sane_cancel (SANE_Handle handle) { Lexmark_Device *lexmark_device; /* ssize_t bytes_read; */ DBG (2, "sane_cancel: handle = %p\n", (void *) handle); if (!initialized) return; for (lexmark_device = first_lexmark_device; lexmark_device; lexmark_device = lexmark_device->next) { if (lexmark_device == handle) break; } /*If sane_cancel called more than once, return */ if (++lexmark_device->cancel_ctr > 1) return; /* Set the device flag so the next call to sane_read() can stop the scan. */ lexmark_device->device_cancelled = SANE_TRUE; return; } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { Lexmark_Device *lexmark_device; DBG (2, "sane_set_io_mode: handle = %p, non_blocking = %d\n", (void *) handle, non_blocking); if (!initialized) return SANE_STATUS_INVAL; for (lexmark_device = first_lexmark_device; lexmark_device; lexmark_device = lexmark_device->next) { if (lexmark_device == handle) break; } if (non_blocking) return SANE_STATUS_UNSUPPORTED; return SANE_STATUS_GOOD; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { Lexmark_Device *lexmark_device; DBG (2, "sane_get_select_fd: handle = %p, fd %s 0\n", (void *) handle, fd ? "!=" : "="); if (!initialized) return SANE_STATUS_INVAL; for (lexmark_device = first_lexmark_device; lexmark_device; lexmark_device = lexmark_device->next) { if (lexmark_device == handle) break; } return SANE_STATUS_UNSUPPORTED; } /***************************** END OF SANE API ****************************/ /* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */ backends-1.3.0/backend/lexmark.conf.in000066400000000000000000000001671456256263500176150ustar00rootroot00000000000000# X11xx series usb 0x043d 0x007c # X12xx series usb 0x043d 0x007d # Dell A920 usb 0x413c 0x5105 # X74 usb 0x43d 0x0060 backends-1.3.0/backend/lexmark.h000066400000000000000000000200271456256263500165070ustar00rootroot00000000000000/************************************************************************ lexmark.h - SANE library for Lexmark scanners. Copyright (C) 2003-2004 Lexmark International, Inc. (original source) Copyright (C) 2005 Fred Odendaal Copyright (C) 2006-2010 Stéphane Voltz Copyright (C) 2010 "Torsten Houwaart" X74 support 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, see . 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 LEXMARK_H #define LEXMARK_H #undef DEEP_DEBUG #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "../include/_stdint.h" #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_backend.h" typedef enum { OPT_NUM_OPTS = 0, OPT_MODE, OPT_RESOLUTION, OPT_PREVIEW, OPT_THRESHOLD, OPT_GEOMETRY_GROUP, OPT_TL_X, /* top-left x */ OPT_TL_Y, /* top-left y */ OPT_BR_X, /* bottom-right x */ OPT_BR_Y, /* bottom-right y */ /* manual gain handling */ OPT_MANUAL_GAIN, /* 6 */ OPT_GRAY_GAIN, OPT_RED_GAIN, OPT_GREEN_GAIN, OPT_BLUE_GAIN, /* must come last: */ NUM_OPTIONS } Lexmark_Options; /* * this struct is used to described the specific parts of each model */ typedef struct Lexmark_Model { SANE_Int vendor_id; SANE_Int product_id; SANE_Byte mainboard_id; /* matched against the content of reg B0 */ SANE_String_Const name; SANE_String_Const vendor; SANE_String_Const model; SANE_Int motor_type; SANE_Int sensor_type; SANE_Int HomeEdgePoint1; SANE_Int HomeEdgePoint2; } Lexmark_Model; /* * this struct is used to store per sensor model constants */ typedef struct Lexmark_Sensor { SANE_Int id; SANE_Int offset_startx; /* starting x for offset calibration */ SANE_Int offset_endx; /* end x for offset calibration */ SANE_Int offset_threshold; /* target threshold for offset calibration */ SANE_Int xoffset; /* number of unusable pixels on the start of the sensor */ SANE_Int default_gain; /* value of the default gain for a scan */ SANE_Int red_gain_target; SANE_Int green_gain_target; SANE_Int blue_gain_target; SANE_Int gray_gain_target; SANE_Int red_shading_target; SANE_Int green_shading_target; SANE_Int blue_shading_target; SANE_Int gray_shading_target; SANE_Int offset_fallback; /* offset to use in case offset calibration fails */ SANE_Int gain_fallback; /* gain to use in case offset calibration fails */ } Lexmark_Sensor; typedef enum { RED = 0, GREEN, BLUE } Scan_Regions; /* struct to hold pre channel settings */ typedef struct Channels { SANE_Word red; SANE_Word green; SANE_Word blue; SANE_Word gray; } Channels; /** @name Option_Value union * convenience union to access option values given to the backend * @{ */ #ifndef SANE_OPTION #define SANE_OPTION 1 typedef union { SANE_Bool b; SANE_Word w; SANE_Word *wa; /* word array */ SANE_String s; } Option_Value; #endif /* @} */ typedef struct Read_Buffer { SANE_Int gray_offset; SANE_Int max_gray_offset; SANE_Int region; SANE_Int red_offset; SANE_Int green_offset; SANE_Int blue_offset; SANE_Int max_red_offset; SANE_Int max_green_offset; SANE_Int max_blue_offset; SANE_Byte *data; SANE_Byte *readptr; SANE_Byte *writeptr; SANE_Byte *max_writeptr; size_t size; size_t linesize; SANE_Bool empty; SANE_Int image_line_no; SANE_Int bit_counter; SANE_Int max_lineart_offset; } Read_Buffer; typedef struct Lexmark_Device { struct Lexmark_Device *next; SANE_Bool missing; /**< devices has been unplugged or swtiched off */ SANE_Device sane; SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; SANE_Parameters params; SANE_Int devnum; long data_size; SANE_Bool initialized; SANE_Bool eof; SANE_Int x_dpi; SANE_Int y_dpi; long data_ctr; SANE_Bool device_cancelled; SANE_Int cancel_ctr; SANE_Byte *transfer_buffer; size_t bytes_read; size_t bytes_remaining; size_t bytes_in_buffer; SANE_Byte *read_pointer; Read_Buffer *read_buffer; SANE_Byte threshold; Lexmark_Model model; /* per model data */ Lexmark_Sensor *sensor; SANE_Byte shadow_regs[255]; /* shadow registers */ struct Channels offset; struct Channels gain; float *shading_coeff; } Lexmark_Device; /* Maximum transfer size */ #define MAX_XFER_SIZE 0xFFC0 /* motors and sensors type defines */ #define X1100_MOTOR 1 #define A920_MOTOR 2 #define X74_MOTOR 3 #define X1100_B2_SENSOR 4 #define A920_SENSOR 5 #define X1100_2C_SENSOR 6 #define X1200_SENSOR 7 /* X1200 on USB 1.0 */ #define X1200_USB2_SENSOR 8 /* X1200 on USB 2.0 */ #define X74_SENSOR 9 /* Non-static Function Proto-types (called by lexmark.c) */ SANE_Status sanei_lexmark_low_init (Lexmark_Device * dev); void sanei_lexmark_low_destroy (Lexmark_Device * dev); SANE_Status sanei_lexmark_low_open_device (Lexmark_Device * dev); void sanei_lexmark_low_close_device (Lexmark_Device * dev); SANE_Bool sanei_lexmark_low_search_home_fwd (Lexmark_Device * dev); void sanei_lexmark_low_move_fwd (SANE_Int distance, Lexmark_Device * dev, SANE_Byte * regs); SANE_Bool sanei_lexmark_low_X74_search_home (Lexmark_Device * dev, SANE_Byte * regs); SANE_Bool sanei_lexmark_low_search_home_bwd (Lexmark_Device * dev); SANE_Int sanei_lexmark_low_find_start_line (Lexmark_Device * dev); SANE_Status sanei_lexmark_low_set_scan_regs (Lexmark_Device * dev, SANE_Int resolution, SANE_Int offset, SANE_Bool calibrated); SANE_Status sanei_lexmark_low_start_scan (Lexmark_Device * dev); long sanei_lexmark_low_read_scan_data (SANE_Byte * data, SANE_Int size, Lexmark_Device * dev); SANE_Status sanei_lexmark_low_assign_model (Lexmark_Device * dev, SANE_String_Const devname, SANE_Int vendor, SANE_Int product, SANE_Byte mainboard); /* * scanner calibration functions */ SANE_Status sanei_lexmark_low_offset_calibration (Lexmark_Device * dev); SANE_Status sanei_lexmark_low_gain_calibration (Lexmark_Device * dev); SANE_Status sanei_lexmark_low_shading_calibration (Lexmark_Device * dev); SANE_Status sanei_lexmark_low_calibration (Lexmark_Device * dev); #endif /* LEXMARK_H */ backends-1.3.0/backend/lexmark_low.c000066400000000000000000005312341456256263500173720ustar00rootroot00000000000000/* lexmark-low.c: scanner-interface file for low Lexmark scanners. (C) 2005 Fred Odendaal (C) 2006-2013 Stéphane Voltz (C) 2010 "Torsten Houwaart" X74 support 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, see . 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. **************************************************************************/ #undef BACKEND_NAME #define BACKEND_NAME lexmark_low #include "lexmark.h" #include "lexmark_sensors.c" #include "lexmark_models.c" /* numbre of ranges for offset */ #define OFFSET_RANGES 5 typedef enum { black = 0, white } region_type; #define HomeTolerance 32 #define LOBYTE(x) ((uint8_t)((x) & 0xFF)) #define HIBYTE(x) ((uint8_t)((x) >> 8)) /* Static low function proto-types */ static SANE_Status low_usb_bulk_write (SANE_Int devnum, SANE_Byte * cmd, size_t * size); static SANE_Status low_usb_bulk_read (SANE_Int devnum, SANE_Byte * buf, size_t * size); static SANE_Status low_write_all_regs (SANE_Int devnum, SANE_Byte * regs); static SANE_Bool low_is_home_line (Lexmark_Device * dev, unsigned char *buffer); static SANE_Status low_get_start_loc (SANE_Int resolution, SANE_Int * vert_start, SANE_Int * hor_start, SANE_Int offset, Lexmark_Device * dev); static void low_rewind (Lexmark_Device * dev, SANE_Byte * regs); static SANE_Status low_start_mvmt (SANE_Int devnum); static SANE_Status low_stop_mvmt (SANE_Int devnum); static SANE_Status low_clr_c6 (SANE_Int devnum); static SANE_Status low_simple_scan (Lexmark_Device * dev, SANE_Byte * regs, int xoffset, int pixels, int yoffset, int lines, SANE_Byte ** data); static void low_set_scan_area (SANE_Int res, SANE_Int tlx, SANE_Int tly, SANE_Int brx, SANE_Int bry, SANE_Int offset, SANE_Bool half_step, SANE_Byte * regs, Lexmark_Device * dev); /* Static Read Buffer Proto-types */ static SANE_Status read_buffer_init (Lexmark_Device * dev, int bytesperline); static SANE_Status read_buffer_free (Read_Buffer * rb); static size_t read_buffer_bytes_available (Read_Buffer * rb); static SANE_Status read_buffer_add_byte (Read_Buffer * rb, SANE_Byte * byte_pointer); static SANE_Status read_buffer_add_byte_gray (Read_Buffer * rb, SANE_Byte * byte_pointer); static SANE_Status read_buffer_add_bit_lineart (Read_Buffer * rb, SANE_Byte * byte_pointer, SANE_Byte threshold); static size_t read_buffer_get_bytes (Read_Buffer * rb, SANE_Byte * buffer, size_t rqst_size); static SANE_Bool read_buffer_is_empty (Read_Buffer * rb); /* * RTS88XX START * * these rts88xx functions will be spin off in a separate lib * so that they can be reused. */ /* * registers helpers to avoid direct access */ static SANE_Bool rts88xx_is_color (SANE_Byte * regs) { if ((regs[0x2f] & 0x11) == 0x11) return SANE_TRUE; return SANE_FALSE; } static void rts88xx_set_gray_scan (SANE_Byte * regs) { regs[0x2f] = (regs[0x2f] & 0x0f) | 0x20; } #if 0 static void rts88xx_set_color_scan (SANE_Byte * regs) { regs[0x2f] = (regs[0x2f] & 0x0f) | 0x10; } #endif static void rts88xx_set_offset (SANE_Byte * regs, SANE_Byte red, SANE_Byte green, SANE_Byte blue) { /* offset for odd pixels */ regs[0x02] = red; regs[0x03] = green; regs[0x04] = blue; /* offset for even pixels */ regs[0x05] = red; regs[0x06] = green; regs[0x07] = blue; } static void rts88xx_set_gain (SANE_Byte * regs, SANE_Byte red, SANE_Byte green, SANE_Byte blue) { regs[0x08] = red; regs[0x09] = green; regs[0x0a] = blue; } /* set # of head moves per CIS read */ static int rts88xx_set_scan_frequency (SANE_Byte * regs, int frequency) { regs[0x64] = (regs[0x64] & 0xf0) | (frequency & 0x0f); return 0; } /* * read one register at given index */ static SANE_Status rts88xx_read_reg (SANE_Int devnum, SANE_Int index, SANE_Byte * reg) { SANE_Status status = SANE_STATUS_GOOD; unsigned char cmd[] = { 0x80, 0x00, 0x00, 0x01 }; size_t size; cmd[1] = index; size = 4; #ifdef FAKE_USB status = SANE_STATUS_GOOD; #else status = sanei_usb_write_bulk (devnum, cmd, &size); #endif if (status != SANE_STATUS_GOOD) { DBG (5, "rts88xx_read_reg: bulk write failed\n"); return status; } size = 1; #ifdef FAKE_USB status = SANE_STATUS_GOOD; #else status = sanei_usb_read_bulk (devnum, reg, &size); #endif if (status != SANE_STATUS_GOOD) { DBG (5, "rts88xx_read_reg: bulk read failed\n"); return status; } DBG (15, "rts88xx_read_reg: reg[0x%02x]=0x%02x\n", index, *reg); return status; } /* * write one register at given index */ static SANE_Status rts88xx_write_reg (SANE_Int devnum, SANE_Int index, SANE_Byte * reg) { SANE_Status status = SANE_STATUS_GOOD; unsigned char cmd[] = { 0x88, 0x00, 0x00, 0x01 }; size_t size; cmd[1] = index; size = 4; #ifdef FAKE_USB status = SANE_STATUS_GOOD; #else status = sanei_usb_write_bulk (devnum, cmd, &size); #endif if (status != SANE_STATUS_GOOD) { DBG (5, "rts88xx_write_reg: bulk write failed\n"); return status; } size = 1; #ifdef FAKE_USB status = SANE_STATUS_GOOD; #else status = sanei_usb_write_bulk (devnum, reg, &size); #endif if (status != SANE_STATUS_GOOD) { DBG (5, "rts88xx_write_reg: bulk write failed\n"); return status; } DBG (15, "rts88xx_write_reg: reg[0x%02x]=0x%02x\n", index, *reg); return status; } /* * write length consecutive registers, starting at index * register 0xb3 is never wrote in bulk register write, so we split * write if it belongs to the register set sent */ static SANE_Status rts88xx_write_regs (SANE_Int devnum, SANE_Int start, SANE_Byte * source, SANE_Int length) { size_t size = 0; /* when writing several registers at a time, we avoid writing 0xb3 register */ if ((start + length > 0xb3) && (length > 1)) { size = 0xb3 - start; if (low_usb_bulk_write (devnum, source, &size) != SANE_STATUS_GOOD) { DBG (5, "rts88xx_write_regs : write registers part 1 failed ...\n"); return SANE_STATUS_IO_ERROR; } /* skip 0xB3 register */ size++; start = 0xb4; source = source + size; } size = length - size; if (low_usb_bulk_write (devnum, source, &size) != SANE_STATUS_GOOD) { DBG (5, "rts88xx_write_regs : write registers part 2 failed ...\n"); return SANE_STATUS_IO_ERROR; } return SANE_STATUS_GOOD; } /* * reads 'needed' bytes of scanned data into 'data'. Actual number of bytes get * is returned in 'size' */ static SANE_Status rts88xx_read_data (SANE_Int devnum, size_t needed, SANE_Byte * data, size_t * size) { SANE_Byte read_cmd[] = { 0x91, 0x00, 0x00, 0x00 }; size_t cmd_size; SANE_Status status = SANE_STATUS_GOOD; /* this block would deserve to be a function */ if (needed > MAX_XFER_SIZE) *size = MAX_XFER_SIZE; else *size = needed; read_cmd[3] = (*size) & 0xff; read_cmd[2] = (*size >> 8) & 0xff; read_cmd[1] = (*size >> 16) & 0xff; /* send header for 'get scanned data' */ cmd_size = 4; status = low_usb_bulk_write (devnum, read_cmd, &cmd_size); if (status != SANE_STATUS_GOOD) { *size = 0; DBG (5, "rts88xx_read_data : header sending failed ...\n"); return status; } /* get actual scanned data */ status = low_usb_bulk_read (devnum, data, size); if (status != SANE_STATUS_GOOD) { *size = 0; DBG (5, "rts88xx_read_data : data reading failed ...\n"); } return status; } /* starts scan by sending color depth, stopping head, the starting it */ static SANE_Status rts88xx_commit (SANE_Int devnum, SANE_Byte depth) { SANE_Status status; SANE_Byte reg; DBG (2, "rts88xx_commit: start\n"); /* send color depth depth ?? * X1100 -> 0x0f * X1100/B2 -> 0x0d * X1200 -> 0x01 */ reg = depth; rts88xx_write_reg (devnum, 0x2c, ®); /* stop before starting */ low_stop_mvmt (devnum); /* effective start */ status = low_start_mvmt (devnum); DBG (2, "rts88xx_commit: end\n"); return status; } /* * RTS88XX END */ /* * sets the scanner idle */ static SANE_Status lexmark_low_set_idle (SANE_Int devnum) { SANE_Byte regs[14] = { 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60 }; if (rts88xx_write_regs (devnum, 16, regs, 14) != SANE_STATUS_GOOD) { DBG (5, "lexmark_low_set_idle : register write failed ...\n"); return SANE_STATUS_IO_ERROR; } return SANE_STATUS_GOOD; } /* wake up scanner */ #if 0 static SANE_Status lexmark_low_wake_up (Lexmark_Device * dev) { SANE_Byte regs[5] = { 0x12, 0x14, 0x16, 0x18, 0x1a }; SANE_Byte values[5] = { 0x0f, 0x00, 0x07, 0x00, 0x00 }; int i; /* send the wake-up sequence, one reg at at time */ for (i = 0; i < 10; i++) { if (rts88xx_write_reg (dev->devnum, regs[i], values + i) != SANE_STATUS_GOOD) { DBG (5, "lexmark_low_wake_up : register write pass %d failed ...\n", i); return SANE_STATUS_IO_ERROR; } } return SANE_STATUS_GOOD; } #endif /** * */ #ifdef DEEP_DEBUG static void write_pnm_file (char *title, int pixels, int lines, int color, unsigned char *data) { FILE *fdbg; int x, y; fdbg = fopen (title, "wb"); if (fdbg == NULL) return; if (color) { fprintf (fdbg, "P6\n%d %d\n255\n", pixels, lines); for (y = 0; y < lines; y++) { for (x = 0; x < pixels; x += 2) { fputc (data[y * pixels * 3 + x + 1], fdbg); fputc (data[y * pixels * 3 + x + 1 + pixels], fdbg); fputc (data[y * pixels * 3 + x + 1 + pixels * 2], fdbg); fputc (data[y * pixels * 3 + x], fdbg); fputc (data[y * pixels * 3 + x + pixels], fdbg); fputc (data[y * pixels * 3 + x + pixels * 2], fdbg); } } } else { fprintf (fdbg, "P5\n%d %d\n255\n", pixels, lines); fwrite (data, pixels, lines, fdbg); } fclose (fdbg); } #endif /* * mid level hardware functions */ /* * model init */ SANE_Status sanei_lexmark_low_init (Lexmark_Device * dev) { int i; SANE_Status status; DBG_INIT (); status = SANE_STATUS_UNSUPPORTED; DBG (2, "low_init: start\n"); /* clear all registers first */ for (i = 0; i < 255; i++) { dev->shadow_regs[i] = 0; } /* set up per model constant values */ dev->shadow_regs[0xf3] = 0xf8; dev->shadow_regs[0xf4] = 0x7f; switch (dev->model.sensor_type) { case X74_SENSOR: dev->shadow_regs[0x00] = 0x04; dev->shadow_regs[0x01] = 0x43; dev->shadow_regs[0x0b] = 0x70; dev->shadow_regs[0x12] = 0x0f; dev->shadow_regs[0x16] = 0x07; dev->shadow_regs[0x1d] = 0x20; dev->shadow_regs[0x28] = 0xe0; dev->shadow_regs[0x29] = 0xe3; dev->shadow_regs[0x2a] = 0xeb; dev->shadow_regs[0x2b] = 0x0d; dev->shadow_regs[0x2e] = 0x40; dev->shadow_regs[0x2e] = 0x86; dev->shadow_regs[0x2f] = 0x01; dev->shadow_regs[0x30] = 0x48; dev->shadow_regs[0x31] = 0x06; dev->shadow_regs[0x33] = 0x01; dev->shadow_regs[0x34] = 0x50; dev->shadow_regs[0x35] = 0x01; dev->shadow_regs[0x36] = 0x50; dev->shadow_regs[0x37] = 0x01; dev->shadow_regs[0x38] = 0x50; dev->shadow_regs[0x3a] = 0x20; dev->shadow_regs[0x3c] = 0x88; dev->shadow_regs[0x3d] = 0x08; dev->shadow_regs[0x65] = 0x80; dev->shadow_regs[0x66] = 0x64; dev->shadow_regs[0x6c] = 0xc8; dev->shadow_regs[0x72] = 0x1a; dev->shadow_regs[0x74] = 0x23; dev->shadow_regs[0x75] = 0x03; dev->shadow_regs[0x79] = 0x40; dev->shadow_regs[0x7A] = 0x01; dev->shadow_regs[0x8d] = 0x01; dev->shadow_regs[0x8e] = 0x60; dev->shadow_regs[0x8f] = 0x80; dev->shadow_regs[0x93] = 0x02; dev->shadow_regs[0x94] = 0x0e; dev->shadow_regs[0xa3] = 0xcc; dev->shadow_regs[0xa4] = 0x27; dev->shadow_regs[0xa5] = 0x24; dev->shadow_regs[0xc2] = 0x80; dev->shadow_regs[0xc3] = 0x01; dev->shadow_regs[0xc4] = 0x20; dev->shadow_regs[0xc5] = 0x0a; dev->shadow_regs[0xc8] = 0x04; dev->shadow_regs[0xc9] = 0x39; dev->shadow_regs[0xca] = 0x0a; dev->shadow_regs[0xe2] = 0x70; dev->shadow_regs[0xe3] = 0x17; dev->shadow_regs[0xf3] = 0xe0; dev->shadow_regs[0xf4] = 0xff; dev->shadow_regs[0xf5] = 0x01; status = SANE_STATUS_GOOD; break; case X1100_B2_SENSOR: dev->shadow_regs[0x01] = 0x43; dev->shadow_regs[0x0b] = 0x70; dev->shadow_regs[0x11] = 0x01; dev->shadow_regs[0x12] = 0x0f; dev->shadow_regs[0x13] = 0x01; dev->shadow_regs[0x15] = 0x01; dev->shadow_regs[0x16] = 0x0f; dev->shadow_regs[0x1d] = 0x20; dev->shadow_regs[0x28] = 0xeb; dev->shadow_regs[0x29] = 0xee; dev->shadow_regs[0x2a] = 0xf7; dev->shadow_regs[0x2b] = 0x01; dev->shadow_regs[0x2e] = 0x86; dev->shadow_regs[0x30] = 0x48; dev->shadow_regs[0x33] = 0x01; dev->shadow_regs[0x3a] = 0x20; dev->shadow_regs[0x3b] = 0x37; dev->shadow_regs[0x3c] = 0x88; dev->shadow_regs[0x3d] = 0x08; dev->shadow_regs[0x40] = 0x80; dev->shadow_regs[0x72] = 0x05; dev->shadow_regs[0x74] = 0x0e; dev->shadow_regs[0x8b] = 0xff; dev->shadow_regs[0x8c] = 0x02; dev->shadow_regs[0x8d] = 0x01; dev->shadow_regs[0x8e] = 0x60; dev->shadow_regs[0x8f] = 0x80; dev->shadow_regs[0x94] = 0x0e; dev->shadow_regs[0xa3] = 0xcc; dev->shadow_regs[0xa4] = 0x27; dev->shadow_regs[0xa5] = 0x24; dev->shadow_regs[0xb0] = 0xb2; dev->shadow_regs[0xb2] = 0x04; dev->shadow_regs[0xc2] = 0x80; dev->shadow_regs[0xc4] = 0x20; dev->shadow_regs[0xc8] = 0x04; dev->shadow_regs[0xc9] = 0x3b; dev->shadow_regs[0xed] = 0xc2; dev->shadow_regs[0xee] = 0x02; status = SANE_STATUS_GOOD; break; case X1100_2C_SENSOR: dev->shadow_regs[0x00] = 0x00; dev->shadow_regs[0x01] = 0x43; dev->shadow_regs[0x0b] = 0x70; dev->shadow_regs[0x0c] = 0x28; dev->shadow_regs[0x0d] = 0xa4; dev->shadow_regs[0x11] = 0x01; dev->shadow_regs[0x12] = 0x0f; dev->shadow_regs[0x13] = 0x01; dev->shadow_regs[0x15] = 0x01; dev->shadow_regs[0x16] = 0x0f; dev->shadow_regs[0x17] = 0x00; dev->shadow_regs[0x1d] = 0x20; dev->shadow_regs[0x28] = 0xf5; dev->shadow_regs[0x29] = 0xf7; dev->shadow_regs[0x2a] = 0xf5; dev->shadow_regs[0x2b] = 0x17; dev->shadow_regs[0x2d] = 0x41; dev->shadow_regs[0x2e] = 0x86; dev->shadow_regs[0x2f] = 0x11; dev->shadow_regs[0x30] = 0x48; dev->shadow_regs[0x31] = 0x01; dev->shadow_regs[0x33] = 0x01; dev->shadow_regs[0x34] = 0x50; dev->shadow_regs[0x35] = 0x01; dev->shadow_regs[0x36] = 0x50; dev->shadow_regs[0x37] = 0x01; dev->shadow_regs[0x38] = 0x50; dev->shadow_regs[0x3a] = 0x20; dev->shadow_regs[0x3b] = 0x37; dev->shadow_regs[0x3c] = 0x88; dev->shadow_regs[0x3d] = 0x08; dev->shadow_regs[0x40] = 0x80; dev->shadow_regs[0x47] = 0x01; dev->shadow_regs[0x48] = 0x1a; dev->shadow_regs[0x49] = 0x5b; dev->shadow_regs[0x4a] = 0x1b; dev->shadow_regs[0x4b] = 0x5b; dev->shadow_regs[0x4c] = 0x05; dev->shadow_regs[0x4d] = 0x3f; dev->shadow_regs[0x60] = 0x2f; dev->shadow_regs[0x61] = 0x36; dev->shadow_regs[0x62] = 0x30; dev->shadow_regs[0x63] = 0x36; dev->shadow_regs[0x65] = 0x80; dev->shadow_regs[0x66] = 0x64; dev->shadow_regs[0x6c] = 0xc8; dev->shadow_regs[0x6d] = 0x00; dev->shadow_regs[0x72] = 0x35; dev->shadow_regs[0x74] = 0x4e; dev->shadow_regs[0x75] = 0x03; dev->shadow_regs[0x79] = 0x40; dev->shadow_regs[0x7a] = 0x01; dev->shadow_regs[0x85] = 0x02; dev->shadow_regs[0x86] = 0x33; dev->shadow_regs[0x87] = 0x0f; dev->shadow_regs[0x88] = 0x24; dev->shadow_regs[0x8b] = 0xff; dev->shadow_regs[0x8c] = 0x02; dev->shadow_regs[0x8d] = 0x01; dev->shadow_regs[0x8e] = 0x60; dev->shadow_regs[0x8f] = 0x80; dev->shadow_regs[0x91] = 0x19; dev->shadow_regs[0x92] = 0x20; dev->shadow_regs[0x93] = 0x02; dev->shadow_regs[0x94] = 0x0e; dev->shadow_regs[0xa3] = 0x0d; dev->shadow_regs[0xa4] = 0x5e; dev->shadow_regs[0xa5] = 0x23; dev->shadow_regs[0xb0] = 0x2c; dev->shadow_regs[0xb1] = 0x07; dev->shadow_regs[0xb2] = 0x04; dev->shadow_regs[0xc2] = 0x80; dev->shadow_regs[0xc3] = 0x01; dev->shadow_regs[0xc4] = 0x20; dev->shadow_regs[0xc5] = 0x0a; dev->shadow_regs[0xc8] = 0x04; dev->shadow_regs[0xc9] = 0x3b; dev->shadow_regs[0xca] = 0x0a; dev->shadow_regs[0xe2] = 0xf8; dev->shadow_regs[0xe3] = 0x2a; status = SANE_STATUS_GOOD; break; case X1200_USB2_SENSOR: dev->shadow_regs[0x01] = 0x43; dev->shadow_regs[0x11] = 0x01; dev->shadow_regs[0x12] = 0x0f; dev->shadow_regs[0x13] = 0x01; dev->shadow_regs[0x15] = 0x01; dev->shadow_regs[0x16] = 0x0f; dev->shadow_regs[0x17] = 0x00; dev->shadow_regs[0x1d] = 0x20; dev->shadow_regs[0x28] = 0xf5; dev->shadow_regs[0x29] = 0xf7; dev->shadow_regs[0x2a] = 0xf5; dev->shadow_regs[0x2b] = 0x17; dev->shadow_regs[0x2d] = 0x41; dev->shadow_regs[0x2e] = 0x86; dev->shadow_regs[0x30] = 0x48; dev->shadow_regs[0x31] = 0x01; dev->shadow_regs[0x33] = 0x01; dev->shadow_regs[0x34] = 0x50; dev->shadow_regs[0x35] = 0x01; dev->shadow_regs[0x36] = 0x50; dev->shadow_regs[0x37] = 0x01; dev->shadow_regs[0x38] = 0x50; dev->shadow_regs[0x3c] = 0x88; dev->shadow_regs[0x3d] = 0x08; dev->shadow_regs[0x66] = 0x64; dev->shadow_regs[0x67] = 0x00; dev->shadow_regs[0x6c] = 0xc8; dev->shadow_regs[0x6d] = 0x00; dev->shadow_regs[0x72] = 0x35; dev->shadow_regs[0x74] = 0x4e; dev->shadow_regs[0x75] = 0x03; dev->shadow_regs[0x7a] = 0x01; dev->shadow_regs[0x93] = 0x0a; dev->shadow_regs[0x94] = 0x0e; dev->shadow_regs[0xc3] = 0x01; dev->shadow_regs[0xc4] = 0x20; dev->shadow_regs[0xc5] = 0x0a; dev->shadow_regs[0xc8] = 0x04; dev->shadow_regs[0xc9] = 0x3b; dev->shadow_regs[0xca] = 0x0a; dev->shadow_regs[0xe2] = 0xf8; dev->shadow_regs[0xe3] = 0x2a; status = SANE_STATUS_GOOD; break; case A920_SENSOR: dev->shadow_regs[0x01] = 0x43; dev->shadow_regs[0x0b] = 0x70; dev->shadow_regs[0x0c] = 0x28; dev->shadow_regs[0x0d] = 0xa4; dev->shadow_regs[0x11] = 0x01; dev->shadow_regs[0x12] = 0x0f; dev->shadow_regs[0x13] = 0x01; dev->shadow_regs[0x15] = 0x01; dev->shadow_regs[0x16] = 0x07; dev->shadow_regs[0x1d] = 0x20; dev->shadow_regs[0x28] = 0xf5; dev->shadow_regs[0x29] = 0xf7; dev->shadow_regs[0x2a] = 0xf5; dev->shadow_regs[0x2b] = 0x17; dev->shadow_regs[0x2e] = 0x86; dev->shadow_regs[0x30] = 0x48; dev->shadow_regs[0x31] = 0x01; dev->shadow_regs[0x33] = 0x01; dev->shadow_regs[0x3a] = 0x20; dev->shadow_regs[0x3b] = 0x37; dev->shadow_regs[0x3c] = 0x88; dev->shadow_regs[0x3d] = 0x08; dev->shadow_regs[0x47] = 0x21; dev->shadow_regs[0x48] = 0x1a; dev->shadow_regs[0x49] = 0x5b; dev->shadow_regs[0x4a] = 0x1b; dev->shadow_regs[0x4b] = 0x5b; dev->shadow_regs[0x4c] = 0x05; dev->shadow_regs[0x4d] = 0x3f; dev->shadow_regs[0x65] = 0x80; dev->shadow_regs[0x86] = 0x14; dev->shadow_regs[0x87] = 0x06; dev->shadow_regs[0x89] = 0xf5; dev->shadow_regs[0x8d] = 0x01; dev->shadow_regs[0x8e] = 0x60; dev->shadow_regs[0x8f] = 0x80; dev->shadow_regs[0x94] = 0x0e; dev->shadow_regs[0xa3] = 0x0d; dev->shadow_regs[0xa4] = 0x5e; dev->shadow_regs[0xa5] = 0x23; dev->shadow_regs[0xb0] = 0x2c; dev->shadow_regs[0xb1] = 0x0f; dev->shadow_regs[0xc2] = 0x80; dev->shadow_regs[0xc4] = 0x20; dev->shadow_regs[0xc8] = 0x04; status = SANE_STATUS_GOOD; break; case X1200_SENSOR: dev->shadow_regs[0x01] = 0x43; dev->shadow_regs[0x0b] = 0x70; dev->shadow_regs[0x0c] = 0x28; dev->shadow_regs[0x0d] = 0xa4; dev->shadow_regs[0x11] = 0x01; dev->shadow_regs[0x12] = 0x0f; dev->shadow_regs[0x13] = 0x01; dev->shadow_regs[0x15] = 0x01; dev->shadow_regs[0x16] = 0x0f; dev->shadow_regs[0x1d] = 0x20; dev->shadow_regs[0x28] = 0xe9; dev->shadow_regs[0x29] = 0xeb; dev->shadow_regs[0x2a] = 0xe9; dev->shadow_regs[0x2b] = 0x0b; dev->shadow_regs[0x2d] = 0x01; dev->shadow_regs[0x2e] = 0x86; dev->shadow_regs[0x2f] = 0x11; dev->shadow_regs[0x30] = 0x48; dev->shadow_regs[0x33] = 0x01; dev->shadow_regs[0x34] = 0x50; dev->shadow_regs[0x35] = 0x01; dev->shadow_regs[0x36] = 0x50; dev->shadow_regs[0x37] = 0x01; dev->shadow_regs[0x38] = 0x50; dev->shadow_regs[0x3a] = 0x20; dev->shadow_regs[0x3b] = 0x37; dev->shadow_regs[0x3c] = 0x88; dev->shadow_regs[0x3d] = 0x08; dev->shadow_regs[0x40] = 0x80; dev->shadow_regs[0x47] = 0x01; dev->shadow_regs[0x48] = 0x1a; dev->shadow_regs[0x49] = 0x5b; dev->shadow_regs[0x4a] = 0x1b; dev->shadow_regs[0x4b] = 0x5b; dev->shadow_regs[0x4c] = 0x05; dev->shadow_regs[0x4d] = 0x3f; dev->shadow_regs[0x60] = 0x12; dev->shadow_regs[0x62] = 0x81; dev->shadow_regs[0x63] = 0x03; dev->shadow_regs[0x65] = 0x80; dev->shadow_regs[0x66] = 0x64; dev->shadow_regs[0x6c] = 0xc8; dev->shadow_regs[0x72] = 0x1e; dev->shadow_regs[0x74] = 0x3c; dev->shadow_regs[0x75] = 0x03; dev->shadow_regs[0x79] = 0x40; dev->shadow_regs[0x7a] = 0x01; dev->shadow_regs[0x85] = 0x20; dev->shadow_regs[0x86] = 0x1e; dev->shadow_regs[0x87] = 0x39; dev->shadow_regs[0x8b] = 0xff; dev->shadow_regs[0x8c] = 0x02; dev->shadow_regs[0x8d] = 0x01; dev->shadow_regs[0x8e] = 0x60; dev->shadow_regs[0x8f] = 0x80; dev->shadow_regs[0x92] = 0x92; dev->shadow_regs[0x93] = 0x02; dev->shadow_regs[0x94] = 0x0e; dev->shadow_regs[0xa3] = 0x0d; dev->shadow_regs[0xa4] = 0x5e; dev->shadow_regs[0xa5] = 0x23; dev->shadow_regs[0xb0] = 0x2c; dev->shadow_regs[0xb1] = 0x07; dev->shadow_regs[0xb2] = 0x04; dev->shadow_regs[0xc2] = 0x80; dev->shadow_regs[0xc3] = 0x01; dev->shadow_regs[0xc4] = 0x20; dev->shadow_regs[0xc5] = 0x0a; dev->shadow_regs[0xc8] = 0x04; dev->shadow_regs[0xc9] = 0x3b; dev->shadow_regs[0xca] = 0x0a; dev->shadow_regs[0xe2] = 0xf8; dev->shadow_regs[0xe3] = 0x2a; dev->shadow_regs[0xf3] = 0xff; dev->shadow_regs[0xf4] = 0x0f; break; } DBG (5, "sanei_lexmark_low_init: init done for model %s/%s\n", dev->model.model, dev->model.name); DBG (2, "low_init: done\n"); return status; } void sanei_lexmark_low_destroy (Lexmark_Device * dev) { /* free the read buffer */ if (dev->read_buffer != NULL) read_buffer_free (dev->read_buffer); } SANE_Status low_usb_bulk_write (SANE_Int devnum, SANE_Byte * cmd, size_t * size) { SANE_Status status; size_t cmd_size; cmd_size = *size; #ifdef FAKE_USB status = SANE_STATUS_GOOD; #else status = sanei_usb_write_bulk (devnum, cmd, size); #endif if (status != SANE_STATUS_GOOD) { DBG (5, "low_usb_bulk_write: returned %s (size = %lu, expected %lu)\n", sane_strstatus (status), (u_long) * size, (u_long) cmd_size); /* F.O. should reset the pipe here... */ } return status; } SANE_Status low_usb_bulk_read (SANE_Int devnum, SANE_Byte * buf, size_t * size) { SANE_Status status; size_t exp_size; exp_size = *size; #ifdef FAKE_USB status = SANE_STATUS_GOOD; #else status = sanei_usb_read_bulk (devnum, buf, size); #endif if (status != SANE_STATUS_GOOD) { DBG (5, "low_usb_bulk_read: returned %s (size = %lu, expected %lu)\n", sane_strstatus (status), (u_long) * size, (u_long) exp_size); /* F.O. should reset the pipe here... */ } DBG (7, "low_usb_bulk_read: returned size = %lu (required %lu)\n", (u_long) * size, (u_long) exp_size); return status; } SANE_Status low_start_mvmt (SANE_Int devnum) { SANE_Status status; SANE_Byte reg; reg = 0x68; rts88xx_write_reg (devnum, 0xb3, ®); status = rts88xx_write_reg (devnum, 0xb3, ®); return status; } SANE_Status low_stop_mvmt (SANE_Int devnum) { SANE_Status status; SANE_Byte reg; /* Stop scanner - clear reg 0xb3: */ reg = 0x02; rts88xx_write_reg (devnum, 0xb3, ®); rts88xx_write_reg (devnum, 0xb3, ®); reg = 0x00; rts88xx_write_reg (devnum, 0xb3, ®); status = rts88xx_write_reg (devnum, 0xb3, ®); return status; } SANE_Status low_clr_c6 (SANE_Int devnum) { SANE_Status status; SANE_Byte reg; /* Clear register 0xC6 */ /* cmd_size = 0x05; return low_usb_bulk_write (devnum, clearC6_command_block, &cmd_size); */ reg = 0x00; status = rts88xx_write_reg (devnum, 0xc6, ®); return status; } /* stops current scan */ static SANE_Status low_cancel (SANE_Int devnum) { SANE_Status status; DBG (2, "low_cancel: start\n"); status = low_stop_mvmt (devnum); if (status != SANE_STATUS_GOOD) return status; status = low_clr_c6 (devnum); if (status != SANE_STATUS_GOOD) return status; DBG (2, "low_cancel: end.\n"); return status; } static SANE_Status low_start_scan (SANE_Int devnum, SANE_Byte * regs) { SANE_Status status; DBG (2, "low_start_scan: start\n"); /* writes registers to scanner */ regs[0x32] = 0x00; status = low_write_all_regs (devnum, regs); if (status != SANE_STATUS_GOOD) return status; regs[0x32] = 0x40; status = low_write_all_regs (devnum, regs); if (status != SANE_STATUS_GOOD) return status; /* Stop scanner - clear reg 0xb3: */ /* status = low_stop_mvmt (devnum); if (status != SANE_STATUS_GOOD) return status; */ /* then start */ status = rts88xx_commit (devnum, regs[0x2c]); DBG (2, "low_start_scan: end.\n"); return status; } /* wait for scan data being available */ static SANE_Status low_poll_data (SANE_Int devnum) { SANE_Status status; int loops = 0; size_t size; static SANE_Byte command4_block[] = { 0x90, 0x00, 0x00, 0x03 }; SANE_Byte result[3]; SANE_Word count; /* Poll the available byte count until not 0 */ while (loops < 1000) { /* 10 ms sleep */ usleep (10000); /* as stated in sanei_lexmark_low_search_home_bwd, we read * available data count twice */ size = 4; status = low_usb_bulk_write (devnum, command4_block, &size); if (status != SANE_STATUS_GOOD) return status; size = 0x3; status = low_usb_bulk_read (devnum, result, &size); if (status != SANE_STATUS_GOOD) return status; size = 4; /* read available data size again */ status = low_usb_bulk_write (devnum, command4_block, &size); if (status != SANE_STATUS_GOOD) return status; size = 0x3; status = low_usb_bulk_read (devnum, result, &size); if (status != SANE_STATUS_GOOD) return status; count = result[0] + (result[1] << 8) + (result[2] << 16); if (count != 0) { DBG (15, "low_poll_data: %d bytes available\n", count); return SANE_STATUS_GOOD; } loops++; } return SANE_STATUS_IO_ERROR; } /** * do a simple scan with the given registers. data buffer is allocated within * the function */ static SANE_Status low_simple_scan (Lexmark_Device * dev, SANE_Byte * regs, int xoffset, int pixels, int yoffset, int lines, SANE_Byte ** data) { SANE_Status status = SANE_STATUS_GOOD; static SANE_Byte reg; size_t size, read, needed; int i, bpl, yend; DBG (2, "low_simple_scan: start\n"); DBG (15, "low_simple_scan: x=%d, pixels=%d (ex=%d), y=%d, lines=%d\n", xoffset, pixels, xoffset + pixels * regs[0x7a], yoffset, lines); /* set up registers */ regs[0x60] = LOBYTE (yoffset); regs[0x61] = HIBYTE (yoffset); yend = yoffset + lines; if ((dev->model.motor_type == A920_MOTOR || dev->model.motor_type == X74_MOTOR) && rts88xx_is_color (regs) && dev->val[OPT_RESOLUTION].w == 600) yend *= 2; regs[0x62] = LOBYTE (yend); regs[0x63] = HIBYTE (yend); regs[0x66] = LOBYTE (xoffset); regs[0x67] = HIBYTE (xoffset); regs[0x6c] = LOBYTE (xoffset + pixels * regs[0x7a]); regs[0x6d] = HIBYTE (xoffset + pixels * regs[0x7a]); /* allocate memory */ if (rts88xx_is_color (regs)) bpl = 3 * pixels; else bpl = pixels; *data = (SANE_Byte *) malloc (bpl * lines); if (*data == NULL) { DBG (2, "low_simple_scan: failed to allocate %d bytes !\n", bpl * lines); return SANE_STATUS_NO_MEM; } /* start scan */ status = low_cancel (dev->devnum); if (status != SANE_STATUS_GOOD) return status; status = low_start_scan (dev->devnum, regs); if (status != SANE_STATUS_GOOD) return status; /* wait for data */ status = low_poll_data (dev->devnum); if (status != SANE_STATUS_GOOD) { DBG (1, "low_simple_scan: time-out while waiting for data.\n"); return status; } /* data reading loop */ needed = bpl * lines; DBG (1, "low_simple_scan: bpl=%d, lines=%d, needed=%lu.\n", bpl, lines, (u_long) needed); read = 0; do { /* this block would deserve to be a function */ status = rts88xx_read_data (dev->devnum, needed - read, (*data) + read, &size); if (status != SANE_STATUS_GOOD) return status; read += size; } while (read < needed); /* if needed, wait for motor to stop */ if (regs[0xc3] & 0x80) { i = 0; do { if (rts88xx_read_reg (dev->devnum, 0xb3, ®) != SANE_STATUS_GOOD) { DBG (5, "low_simple_scan: register read failed ...\n"); return SANE_STATUS_IO_ERROR; } usleep (100000); i++; } while ((reg & 0x08) && (i < 100)); if (reg & 0x08) { DBG (5, "low_simple_scan : timeout waiting for motor to stop ...\n"); return SANE_STATUS_IO_ERROR; } } /* stop scan */ status = low_cancel (dev->devnum); if (status != SANE_STATUS_GOOD) { DBG (1, "low_simple_scan: cancel failed.\n"); return status; } DBG (2, "low_simple_scan: end.\n"); return status; } /* * open USB device ,read initial registers values and probe sensor */ SANE_Status sanei_lexmark_low_open_device (Lexmark_Device * dev) { /* This function calls the Sane Interface to open this usb device. It also needlessly does what the Windows driver does and reads the entire register set - this may be removed. */ SANE_Status result; static SANE_Byte command_block[] = { 0x80, 0, 0x00, 0xFF }; size_t size; SANE_Byte variant = 0; SANE_Byte shadow_regs[255]; int sx, ex; int sy, ey; int i; char msg[2048]; #ifdef FAKE_USB result = SANE_STATUS_GOOD; shadow_regs[0x00] = 0x91; shadow_regs[0xb0] = 0x2c; shadow_regs[0x10] = 0x97; shadow_regs[0x10] = 0x87; shadow_regs[0xf3] = 0xf8; shadow_regs[0xf4] = 0x7f; #else result = sanei_usb_open (dev->sane.name, &(dev->devnum)); #endif DBG (2, "sanei_lexmark_low_open_device: devnum=%d\n", dev->devnum); size = 4; low_usb_bulk_write (dev->devnum, command_block, &size); size = 0xFF; memset (shadow_regs, 0, sizeof (shadow_regs)); low_usb_bulk_read (dev->devnum, shadow_regs, &size); if (DBG_LEVEL > 2) { DBG (2, "sanei_lexmark_low_open_device: initial registers values\n"); for (i = 0; i < 255; i++) { sprintf (msg + i * 5, "0x%02x ", shadow_regs[i]); } DBG (3, "%s\n", msg); } /* it seems that at first read after reset, registers hold information * about the scanner. Register 0x00 is overwritten with 0, so only first read * after USB plug-in gives this value */ if (shadow_regs[0] == 0x91) { sx = shadow_regs[0x67] * 256 + shadow_regs[0x66]; ex = shadow_regs[0x6d] * 256 + shadow_regs[0x6c]; DBG (7, "startx=%d, endx=%d, pixels=%d, coef=%d, r2f=0x%02x\n", sx, ex, ex - sx, dev->shadow_regs[0x7a], shadow_regs[0x2f]); sy = shadow_regs[0x61] * 256 + shadow_regs[0x60]; ey = shadow_regs[0x63] * 256 + shadow_regs[0x62]; DBG (7, "starty=%d, endy=%d, lines=%d\n", sy, ey, ey - sy); } /* we use register 0xb0 to identify details about models */ /* this register isn't overwritten during normal operation */ if (shadow_regs[0xb0] == 0x2c && dev->model.sensor_type == X1100_B2_SENSOR) { variant = shadow_regs[0xb0]; } /* now the same with register 0x10 */ /* which most likely signals USB2.0/USB1.1 */ if ((dev->model.sensor_type == X1200_SENSOR) && (shadow_regs[0x10] == 0x97)) { variant = shadow_regs[0x10]; } /* if find a case where default model given is inappropriate, reassign it * since we have now the information to get the real one. * We could avoid this if attach() did open and read registers, not init */ if (variant != 0) { DBG (3, "sanei_lexmark_low_open_device: reassign model/sensor for variant 0x%02x\n", variant); sanei_lexmark_low_assign_model (dev, dev->sane.name, dev->model.vendor_id, dev->model.product_id, variant); /* since model has changed, run init again */ sanei_lexmark_low_init (dev); } DBG (2, "sanei_lexmark_low_open_device: end\n"); return result; } void sanei_lexmark_low_close_device (Lexmark_Device * dev) { /* put scanner in idle state */ lexmark_low_set_idle (dev->devnum); /* This function calls the Sane USB library to close this usb device */ #ifndef FAKE_USB sanei_usb_close (dev->devnum); #endif return; } /* This function writes the contents of the given registers to the scanner. */ SANE_Status low_write_all_regs (SANE_Int devnum, SANE_Byte * regs) { int i; SANE_Status status; size_t size; static SANE_Byte command_block1[0xb7]; static SANE_Byte command_block2[0x4f]; command_block1[0] = 0x88; command_block1[1] = 0x00; command_block1[2] = 0x00; command_block1[3] = 0xb3; for (i = 0; i < 0xb3; i++) { command_block1[i + 4] = regs[i]; } command_block2[0] = 0x88; command_block2[1] = 0xb4; command_block2[2] = 0x00; command_block2[3] = 0x4b; for (i = 0; i < 0x4b; i++) { command_block2[i + 4] = regs[i + 0xb4]; } size = 0xb7; #ifdef DEEP_DEBUG fprintf (stderr, "write_all(0x00,255)="); for (i = 0; i < 255; i++) { fprintf (stderr, "0x%02x ", regs[i]); } fprintf (stderr, "\n"); #endif status = low_usb_bulk_write (devnum, command_block1, &size); if (status != SANE_STATUS_GOOD) return status; size = 0x4f; status = low_usb_bulk_write (devnum, command_block2, &size); if (status != SANE_STATUS_GOOD) return status; return SANE_STATUS_GOOD; } SANE_Bool low_is_home_line (Lexmark_Device * dev, unsigned char *buffer) { /* This function assumes the buffer has a size of 2500 bytes.It is destructive to the buffer. Here is what it does: Go through the buffer finding low and high values, which are computed by comparing to the average: average = (lowest value + highest value)/2 High bytes are changed to 0xFF (white), lower or equal bytes are changed to 0x00 (black),so that the buffer only contains white (0xFF) or black (0x00) values. Next, we go through the buffer. We use a tolerance of 5 bytes on each end of the buffer and check a region from bytes 5 to 2495. We start assuming we are in a white region and look for the start of a black region. We save this index as the transition from white to black. We also save where we change from black back to white. We continue checking for transitions until the end of the check region. If we don't have exactly two transitions when we reach the end we return SANE_FALSE. The final check compares the transition indices to the nominal values plus or minus the tolerance. For the first transition (white to black index) the value must lie in the range 1235-30 (1205) to 1235+30 (1265). For the second transition (black to white) the value must lie in the range 1258-30 (1228) to 1258+30 (1288). If the indices are out of range we return SANE_FALSE. Otherwise, we return SANE_TRUE. */ unsigned char max_byte = 0; unsigned char min_byte = 0xFF; unsigned char average; int i; int home_point1; int home_point2; region_type region; int transition_counter; int index1 = 0; int index2 = 0; int low_range, high_range; #ifdef DEEP_DEBUG static int numero = 0; char titre[80]; FILE *trace = NULL; sprintf (titre, "lgn%03d.pnm", numero); trace = fopen (titre, "wb"); if (trace) { fprintf (trace, "P5\n2500 1\n255\n"); fwrite (buffer, 2500, 1, trace); fclose (trace); } numero++; #endif DBG (15, "low_is_home_line: start\n"); /* Find the max and the min */ for (i = 0; i < 2500; i++) { if (*(buffer + i) > max_byte) max_byte = *(buffer + i); if (*(buffer + i) < min_byte) min_byte = *(buffer + i); } /* The average */ average = ((max_byte + min_byte) / 2); /* Set bytes as white (0xFF) or black (0x00) */ for (i = 0; i < 2500; i++) { if (*(buffer + i) > average) *(buffer + i) = 0xFF; else *(buffer + i) = 0x00; } region = white; transition_counter = 0; /* Go through the check region - bytes 5 to 2495 */ /* XXX STEF XXX shrink the area to where the dot should be * +-100 around the 1250 expected location */ for (i = 1150; i <= 1350; i++) { /* Check for transition to black */ if ((region == white) && (*(buffer + i) == 0)) { if (transition_counter < 2) { region = black; index1 = i; transition_counter++; } else { DBG (15, "low_is_home_line: no transition to black \n"); return SANE_FALSE; } } /* Check for transition to white */ else if ((region == black) && (*(buffer + i) == 0xFF)) { if (transition_counter < 2) { region = white; index2 = i; transition_counter++; } else { DBG (15, "low_is_home_line: no transition to white \n"); return SANE_FALSE; } } } /* Check that the number of transitions is 2 */ if (transition_counter != 2) { DBG (15, "low_is_home_line: transitions!=2 (%d)\n", transition_counter); return SANE_FALSE; } /* Check that the 1st index is in range */ home_point1 = dev->model.HomeEdgePoint1; low_range = home_point1 - HomeTolerance; high_range = home_point1 + HomeTolerance; if ((index1 < low_range) || (index1 > high_range)) { DBG (15, "low_is_home_line: index1=%d out of range\n", index1); return SANE_FALSE; } /* Check that the 2nd index is in range */ home_point2 = dev->model.HomeEdgePoint2; low_range = home_point2 - HomeTolerance; high_range = home_point2 + HomeTolerance; if ((index2 < low_range) || (index2 > high_range)) { DBG (15, "low_is_home_line: index2=%d out of range.\n", index2); return SANE_FALSE; } /* We made it this far, so its a good home line. Return True */ DBG (15, "low_is_home_line: success\n"); return SANE_TRUE; } void sanei_lexmark_low_move_fwd (SANE_Int distance, Lexmark_Device * dev, SANE_Byte * regs) { /* This function moves the scan head forward with the highest vertical resolution of 1200dpi. The distance moved is given by the distance parameter. As an example, given a distance parameter of 600, the scan head will move 600/1200", or 1/2" forward. */ static SANE_Byte pollstopmoving_command_block[] = { 0x80, 0xb3, 0x00, 0x01 }; size_t cmd_size; SANE_Int devnum; SANE_Bool scan_head_moving; SANE_Byte read_result; DBG (2, "sanei_lexmark_low_move_fwd: \n"); devnum = dev->devnum; /* registers set-up */ regs[0x2c] = 0x00; regs[0x2d] = 0x41; regs[0x65] = 0x80; switch (dev->model.sensor_type) { case X74_SENSOR: rts88xx_set_scan_frequency (regs, 0); regs[0x93] = 0x06; break; case X1100_B2_SENSOR: regs[0x8b] = 0x00; regs[0x8c] = 0x00; regs[0x93] = 0x06; break; case X1100_2C_SENSOR: rts88xx_set_scan_frequency (regs, 0); regs[0x93] = 0x06; break; case A920_SENSOR: rts88xx_set_scan_frequency (regs, 0); regs[0x8b] = 0xff; regs[0x8c] = 0x02; regs[0x93] = 0x0e; break; case X1200_SENSOR: dev->shadow_regs[0x2d] = 0x01; rts88xx_set_scan_frequency (regs, 0); break; case X1200_USB2_SENSOR: dev->shadow_regs[0x2d] = 0x4f; rts88xx_set_scan_frequency (regs, 0); break; } /* set grayscale scan + nodata/nochannel? */ regs[0x2f] = 0xa1; /* set ? */ regs[0x34] = 0x50; regs[0x35] = 0x01; regs[0x36] = 0x50; regs[0x37] = 0x01; regs[0x38] = 0x50; /* set motor resolution divisor */ regs[0x39] = 0x00; /* set vertical start/end positions */ regs[0x60] = LOBYTE (distance - 1); regs[0x61] = HIBYTE (distance - 1); regs[0x62] = LOBYTE (distance); regs[0x63] = HIBYTE (distance); /* set horizontal start position */ regs[0x66] = 0x64; regs[0x67] = 0x00; /* set horizontal end position */ regs[0x6c] = 0xc8; regs[0x6d] = 0x00; /* set horizontal resolution */ regs[0x79] = 0x40; regs[0x7a] = 0x01; /* don't buffer data for this scan */ regs[0xb2] = 0x04; /* Motor enable & Coordinate space denominator */ regs[0xc3] = 0x81; /* Movement direction & step size */ regs[0xc6] = 0x09; /* ? */ regs[0x80] = 0x00; regs[0x81] = 0x00; regs[0x82] = 0x00; regs[0xc5] = 0x0a; switch (dev->model.motor_type) { case X1100_MOTOR: case A920_MOTOR: /* step size range2 */ regs[0xc9] = 0x3b; /* ? */ regs[0xca] = 0x0a; /* motor curve stuff */ regs[0xe0] = 0x00; regs[0xe1] = 0x00; regs[0xe4] = 0x00; regs[0xe5] = 0x00; regs[0xe7] = 0x00; regs[0xe8] = 0x00; regs[0xe2] = 0x09; regs[0xe3] = 0x1a; regs[0xe6] = 0xdc; regs[0xe9] = 0x1b; regs[0xec] = 0x07; regs[0xef] = 0x03; break; case X74_MOTOR: regs[0xc5] = 0x41; /* step size range2 */ regs[0xc9] = 0x39; /* ? */ regs[0xca] = 0x40; /* motor curve stuff */ regs[0xe0] = 0x00; regs[0xe1] = 0x00; regs[0xe2] = 0x09; regs[0xe3] = 0x1a; regs[0xe4] = 0x00; regs[0xe5] = 0x00; regs[0xe6] = 0x64; regs[0xe7] = 0x00; regs[0xe8] = 0x00; regs[0xe9] = 0x32; regs[0xec] = 0x0c; regs[0xef] = 0x08; break; } /* prepare for register write */ low_clr_c6 (devnum); low_stop_mvmt (devnum); /* Move Forward without scanning: */ regs[0x32] = 0x00; low_write_all_regs (devnum, regs); regs[0x32] = 0x40; low_write_all_regs (devnum, regs); /* Stop scanner - clear reg 0xb3: */ /* low_stop_mvmt (devnum); */ rts88xx_commit (devnum, regs[0x2c]); /* Poll for scanner stopped - return value(3:0) = 0: */ scan_head_moving = SANE_TRUE; while (scan_head_moving) { #ifdef FAKE_USB scan_head_moving = SANE_FALSE; #else cmd_size = 0x04; low_usb_bulk_write (devnum, pollstopmoving_command_block, &cmd_size); cmd_size = 0x1; low_usb_bulk_read (devnum, &read_result, &cmd_size); if ((read_result & 0xF) == 0x0) { scan_head_moving = SANE_FALSE; } #endif } /* this is needed to find the start line properly */ if (dev->model.sensor_type == X74_SENSOR) low_stop_mvmt (devnum); DBG (2, "sanei_lexmark_low_move_fwd: end.\n"); } SANE_Bool sanei_lexmark_low_search_home_fwd (Lexmark_Device * dev) { /* This function actually searches backwards one line looking for home */ SANE_Int devnum; int i; SANE_Byte poll_result[3]; SANE_Byte *buffer; SANE_Byte temp_byte; static SANE_Byte command4_block[] = { 0x90, 0x00, 0x00, 0x03 }; static SANE_Byte command5_block[] = { 0x91, 0x00, 0x09, 0xc4 }; size_t cmd_size; SANE_Bool got_line; SANE_Bool ret_val; devnum = dev->devnum; DBG (2, "sanei_lexmark_low_search_home_fwd:\n"); /* set up registers according to the sensor type */ switch (dev->model.sensor_type) { case X74_SENSOR: dev->shadow_regs[0x2c] = 0x03; dev->shadow_regs[0x2d] = 0x45; dev->shadow_regs[0x2f] = 0x21; dev->shadow_regs[0x30] = 0x48; dev->shadow_regs[0x31] = 0x06; dev->shadow_regs[0x34] = 0x05; dev->shadow_regs[0x35] = 0x05; dev->shadow_regs[0x36] = 0x09; dev->shadow_regs[0x37] = 0x09; dev->shadow_regs[0x38] = 0x0d; dev->shadow_regs[0x40] = 0x80; dev->shadow_regs[0x75] = 0x00; dev->shadow_regs[0x8b] = 0xff; dev->shadow_regs[0x93] = 0x06; break; case X1100_B2_SENSOR: dev->shadow_regs[0x2c] = 0x0f; dev->shadow_regs[0x2d] = 0x51; dev->shadow_regs[0x2f] = 0x21; dev->shadow_regs[0x34] = 0x04; dev->shadow_regs[0x35] = 0x04; dev->shadow_regs[0x36] = 0x08; dev->shadow_regs[0x37] = 0x08; dev->shadow_regs[0x38] = 0x0b; dev->shadow_regs[0x93] = 0x06; break; case X1100_2C_SENSOR: dev->shadow_regs[0x2c] = 0x0d; dev->shadow_regs[0x2d] = 0x4f; dev->shadow_regs[0x34] = 0x05; dev->shadow_regs[0x35] = 0x05; dev->shadow_regs[0x36] = 0x09; dev->shadow_regs[0x37] = 0x09; dev->shadow_regs[0x38] = 0x0d; dev->shadow_regs[0x40] = 0x80; dev->shadow_regs[0x72] = 0x35; dev->shadow_regs[0x74] = 0x4e; dev->shadow_regs[0x85] = 0x20; /* 05 */ dev->shadow_regs[0x86] = 0x00; /* 05 */ dev->shadow_regs[0x87] = 0x00; /* 05 */ dev->shadow_regs[0x88] = 0x00; /* 45 */ dev->shadow_regs[0x89] = 0x00; dev->shadow_regs[0x8b] = 0xff; dev->shadow_regs[0x93] = 0x06; /* 0e */ dev->shadow_regs[0x75] = 0x00; /* */ dev->shadow_regs[0x91] = 0x00; /* 60 */ dev->shadow_regs[0x92] = 0x00; /* 8d */ dev->shadow_regs[0xb1] = 0x00; /* */ dev->shadow_regs[0xc5] = 0x00; /* */ dev->shadow_regs[0xca] = 0x00; /* */ dev->shadow_regs[0xc3] = 0x01; /* */ break; case A920_SENSOR: dev->shadow_regs[0x2c] = 0x0d; dev->shadow_regs[0x2d] = 0x4f; dev->shadow_regs[0x34] = 0x05; dev->shadow_regs[0x35] = 0x05; dev->shadow_regs[0x36] = 0x09; dev->shadow_regs[0x37] = 0x09; dev->shadow_regs[0x38] = 0x0d; dev->shadow_regs[0x40] = 0x80; dev->shadow_regs[0x72] = 0x35; dev->shadow_regs[0x74] = 0x4e; dev->shadow_regs[0x85] = 0x05; dev->shadow_regs[0x88] = 0x45; dev->shadow_regs[0x89] = 0x00; dev->shadow_regs[0x8b] = 0xff; dev->shadow_regs[0x91] = 0x60; dev->shadow_regs[0x92] = 0x8d; dev->shadow_regs[0x93] = 0x0e; break; case X1200_SENSOR: dev->shadow_regs[0x2c] = 0x01; dev->shadow_regs[0x2d] = 0x03; dev->shadow_regs[0x34] = 0x04; dev->shadow_regs[0x35] = 0x04; dev->shadow_regs[0x36] = 0x08; dev->shadow_regs[0x37] = 0x08; dev->shadow_regs[0x38] = 0x0b; dev->shadow_regs[0x66] = 0x88; dev->shadow_regs[0x6c] = 0x10; dev->shadow_regs[0x6d] = 0x14; dev->shadow_regs[0x75] = 0x00; dev->shadow_regs[0x93] = 0x06; dev->shadow_regs[0xc5] = 0x00; dev->shadow_regs[0xca] = 0x00; break; case X1200_USB2_SENSOR: dev->shadow_regs[0x0b] = 0x70; dev->shadow_regs[0x0c] = 0x28; dev->shadow_regs[0x0d] = 0xa4; dev->shadow_regs[0x2c] = 0x0d; dev->shadow_regs[0x2d] = 0x4f; dev->shadow_regs[0x2f] = 0x21; dev->shadow_regs[0x32] = 0x40; dev->shadow_regs[0x34] = 0x05; dev->shadow_regs[0x35] = 0x05; dev->shadow_regs[0x36] = 0x09; dev->shadow_regs[0x37] = 0x09; dev->shadow_regs[0x38] = 0x0d; dev->shadow_regs[0x3a] = 0x20; dev->shadow_regs[0x3b] = 0x37; dev->shadow_regs[0x40] = 0x80; dev->shadow_regs[0x47] = 0x01; dev->shadow_regs[0x48] = 0x1a; dev->shadow_regs[0x49] = 0x5b; dev->shadow_regs[0x4a] = 0x1b; dev->shadow_regs[0x4b] = 0x5b; dev->shadow_regs[0x4c] = 0x05; dev->shadow_regs[0x4d] = 0x3f; dev->shadow_regs[0x75] = 0x00; dev->shadow_regs[0x85] = 0x03; dev->shadow_regs[0x86] = 0x33; dev->shadow_regs[0x87] = 0x8f; dev->shadow_regs[0x88] = 0x34; dev->shadow_regs[0x8b] = 0xff; dev->shadow_regs[0x8e] = 0x60; dev->shadow_regs[0x8f] = 0x80; dev->shadow_regs[0x91] = 0x59; dev->shadow_regs[0x92] = 0x10; dev->shadow_regs[0x93] = 0x06; dev->shadow_regs[0xa3] = 0x0d; dev->shadow_regs[0xa4] = 0x5e; dev->shadow_regs[0xa5] = 0x23; dev->shadow_regs[0xb1] = 0x07; dev->shadow_regs[0xc2] = 0x80; dev->shadow_regs[0xc5] = 0x00; dev->shadow_regs[0xca] = 0x00; break; } dev->shadow_regs[0x65] = 0x80; dev->shadow_regs[0x8c] = 0x02; dev->shadow_regs[0x8d] = 0x01; dev->shadow_regs[0xb2] = 0x00; dev->shadow_regs[0xed] = 0x00; dev->shadow_regs[0xee] = 0x00; rts88xx_set_gain (dev->shadow_regs, dev->sensor->default_gain, dev->sensor->default_gain, dev->sensor->default_gain); rts88xx_set_offset (dev->shadow_regs, 0x80, 0x80, 0x80); /* set grayscale scan */ rts88xx_set_gray_scan (dev->shadow_regs); /* set motor resolution divisor */ dev->shadow_regs[0x39] = 0x07; /* set vertical start/end positions */ dev->shadow_regs[0x60] = 0x01; dev->shadow_regs[0x61] = 0x00; dev->shadow_regs[0x62] = 0x02; dev->shadow_regs[0x63] = 0x00; /* set # of head moves per CIS read */ rts88xx_set_scan_frequency (dev->shadow_regs, 1); /* set horizontal start position */ dev->shadow_regs[0x66] = 0x6a; /* 0x88 for X1200 */ dev->shadow_regs[0x67] = 0x00; /* set horizontal end position */ dev->shadow_regs[0x6c] = 0xf2; /* 0x1410 for X1200 */ dev->shadow_regs[0x6d] = 0x13; /* set horizontal resolution */ dev->shadow_regs[0x79] = 0x40; dev->shadow_regs[0x7a] = 0x02; /* Motor disable & Coordinate space denominator */ dev->shadow_regs[0xc3] = 0x01; /* Movement direction & step size */ dev->shadow_regs[0xc6] = 0x01; switch (dev->model.motor_type) { case A920_MOTOR: case X1100_MOTOR: /* step size range2 */ dev->shadow_regs[0xc9] = 0x3b; /* step size range0 */ dev->shadow_regs[0xe2] = 0x01; /* ? */ dev->shadow_regs[0xe3] = 0x03; break; case X74_MOTOR: dev->shadow_regs[0xc4] = 0x20; dev->shadow_regs[0xc5] = 0x00; dev->shadow_regs[0xc8] = 0x04; /* step size range2 */ dev->shadow_regs[0xc9] = 0x39; dev->shadow_regs[0xca] = 0x00; /* motor curve stuff */ dev->shadow_regs[0xe0] = 0x29; dev->shadow_regs[0xe1] = 0x17; dev->shadow_regs[0xe2] = 0x8f; dev->shadow_regs[0xe3] = 0x06; dev->shadow_regs[0xe4] = 0x61; dev->shadow_regs[0xe5] = 0x16; dev->shadow_regs[0xe6] = 0x64; dev->shadow_regs[0xe7] = 0xb5; dev->shadow_regs[0xe8] = 0x08; dev->shadow_regs[0xe9] = 0x32; dev->shadow_regs[0xec] = 0x0c; dev->shadow_regs[0xef] = 0x08; break; } /* Stop the scanner */ low_stop_mvmt (devnum); /* write regs out twice */ dev->shadow_regs[0x32] = 0x00; low_write_all_regs (devnum, dev->shadow_regs); dev->shadow_regs[0x32] = 0x40; low_write_all_regs (devnum, dev->shadow_regs); /* Start Scan */ rts88xx_commit (devnum, dev->shadow_regs[0x2c]); /* Poll the available byte count until not 0 */ got_line = SANE_FALSE; while (!got_line) { cmd_size = 4; low_usb_bulk_write (devnum, command4_block, &cmd_size); cmd_size = 0x3; low_usb_bulk_read (devnum, poll_result, &cmd_size); if (! (poll_result[0] == 0 && poll_result[1] == 0 && poll_result[2] == 0)) { /* if result != 00 00 00 we got data */ got_line = SANE_TRUE; } } /* create buffer for scan data */ buffer = calloc (2500, sizeof (char)); if (buffer == NULL) { return SANE_FALSE; } /* Tell the scanner to send the data */ /* Write: 91 00 09 c4 */ cmd_size = 4; low_usb_bulk_write (devnum, command5_block, &cmd_size); /* Read it */ cmd_size = 0x09c4; low_usb_bulk_read (devnum, buffer, &cmd_size); /* Reverse order of bytes in words of buffer */ for (i = 0; i < 2500; i = i + 2) { temp_byte = *(buffer + i); *(buffer + i) = *(buffer + i + 1); *(buffer + i + 1) = temp_byte; } /* check for home position */ ret_val = low_is_home_line (dev, buffer); if (ret_val) DBG (2, "sanei_lexmark_low_search_home_fwd: !!!HOME POSITION!!!\n"); /* free the buffer */ free (buffer); DBG (2, "sanei_lexmark_low_search_home_fwd: end.\n"); return ret_val; } SANE_Bool sanei_lexmark_low_search_home_bwd (Lexmark_Device * dev) { /* This function must only be called if the scan head is past the home dot. It could damage the scanner if not. This function tells the scanner to do a grayscale scan backwards with a 300dpi resolution. It reads 2500 bytes of data between horizontal coordinates 0x6a and 0x13f2. The scan is set to read between vertical coordinates from 0x0a to 0x0f46, or 3900 lines. This equates to 13" at 300dpi, so we must stop the scan before it bangs against the end. A line limit is set so that a maximum of 0x0F3C (13"*300dpi) lines can be read. To read the scan data we create a buffer space large enough to hold 10 lines of data. For each read we poll twice, ignoring the first poll. This is required for timing. We repeat the double poll until there is data available. The number of lines (or number of buffers in our buffer space) is calculated from the size of the data available from the scanner. The number of buffers is calculated as the space required to hold 1.5 times the size of the data available from the scanner. After data is read from the scanner each line is checked if it is on the home dot. Lines are continued to be read until we are no longer on the home dot. */ SANE_Int devnum; SANE_Status status; int i, j; SANE_Byte poll_result[3]; SANE_Byte *buffer; SANE_Byte *buffer_start; SANE_Byte temp_byte; SANE_Int buffer_count = 0; SANE_Int size_requested; SANE_Int size_returned; SANE_Int no_of_buffers; SANE_Int buffer_limit = 0xF3C; SANE_Int high_byte, mid_byte, low_byte; SANE_Int home_line_count; SANE_Bool in_home_region; static SANE_Byte command4_block[] = { 0x90, 0x00, 0x00, 0x03 }; static SANE_Byte command5_block[] = { 0x91, 0x00, 0xff, 0xc0 }; #ifdef DEEP_DEBUG FILE *img = NULL; #endif size_t cmd_size; SANE_Bool got_line; devnum = dev->devnum; DBG (2, "sanei_lexmark_low_search_home_bwd:\n"); /* set up registers */ switch (dev->model.sensor_type) { case X74_SENSOR: dev->shadow_regs[0x2c] = 0x03; dev->shadow_regs[0x2d] = 0x45; dev->shadow_regs[0x34] = 0x09; dev->shadow_regs[0x35] = 0x09; dev->shadow_regs[0x36] = 0x11; dev->shadow_regs[0x37] = 0x11; dev->shadow_regs[0x38] = 0x19; dev->shadow_regs[0x85] = 0x00; dev->shadow_regs[0x93] = 0x06; dev->shadow_regs[0x40] = 0x80; /* important for detection of b/w transitions */ dev->shadow_regs[0x75] = 0x00; dev->shadow_regs[0x8b] = 0xff; break; case X1100_B2_SENSOR: dev->shadow_regs[0x2c] = 0x0f; dev->shadow_regs[0x2d] = 0x51; dev->shadow_regs[0x34] = 0x07; dev->shadow_regs[0x35] = 0x07; dev->shadow_regs[0x36] = 0x0f; dev->shadow_regs[0x37] = 0x0f; dev->shadow_regs[0x38] = 0x15; dev->shadow_regs[0x85] = 0x20; dev->shadow_regs[0x93] = 0x06; break; case X1100_2C_SENSOR: dev->shadow_regs[0x2c] = 0x0d; dev->shadow_regs[0x2d] = 0x4f; dev->shadow_regs[0x34] = 0x09; dev->shadow_regs[0x35] = 0x09; dev->shadow_regs[0x36] = 0x11; dev->shadow_regs[0x37] = 0x11; dev->shadow_regs[0x38] = 0x19; dev->shadow_regs[0x85] = 0x20; dev->shadow_regs[0x93] = 0x06; break; case A920_SENSOR: dev->shadow_regs[0x2c] = 0x0d; dev->shadow_regs[0x2d] = 0x4f; dev->shadow_regs[0x34] = 0x09; dev->shadow_regs[0x35] = 0x09; dev->shadow_regs[0x36] = 0x11; dev->shadow_regs[0x37] = 0x11; dev->shadow_regs[0x38] = 0x19; dev->shadow_regs[0x85] = 0x05; dev->shadow_regs[0x93] = 0x0e; break; case X1200_SENSOR: dev->shadow_regs[0x2c] = 0x01; dev->shadow_regs[0x2d] = 0x03; dev->shadow_regs[0x34] = 0x07; dev->shadow_regs[0x35] = 0x07; dev->shadow_regs[0x36] = 0x0f; dev->shadow_regs[0x37] = 0x0f; dev->shadow_regs[0x38] = 0x15; break; case X1200_USB2_SENSOR: dev->shadow_regs[0x2c] = 0x0d; dev->shadow_regs[0x2d] = 0x4f; dev->shadow_regs[0x34] = 0x09; dev->shadow_regs[0x35] = 0x09; dev->shadow_regs[0x36] = 0x11; dev->shadow_regs[0x37] = 0x11; dev->shadow_regs[0x38] = 0x19; dev->shadow_regs[0x85] = 0x03; dev->shadow_regs[0x93] = 0x06; break; } rts88xx_set_gain (dev->shadow_regs, dev->sensor->default_gain, dev->sensor->default_gain, dev->sensor->default_gain); dev->shadow_regs[0x65] = 0x80; dev->shadow_regs[0x8b] = 0xff; dev->shadow_regs[0x8c] = 0x02; dev->shadow_regs[0xb2] = 0x00; /* set calibration */ rts88xx_set_offset (dev->shadow_regs, 0x80, 0x80, 0x80); /* set grayscale scan */ dev->shadow_regs[0x2f] = 0x21; /* set motor resolution divisor */ dev->shadow_regs[0x39] = 0x03; /* set vertical start/end positions */ dev->shadow_regs[0x60] = 0x0a; dev->shadow_regs[0x61] = 0x00; dev->shadow_regs[0x62] = 0x46; dev->shadow_regs[0x63] = 0x0f; /* set # of head moves per CIS read */ rts88xx_set_scan_frequency (dev->shadow_regs, 2); /* set horizontal start position */ dev->shadow_regs[0x66] = 0x6a; /* 0x88 for X1200 */ dev->shadow_regs[0x67] = 0x00; /* set horizontal end position */ dev->shadow_regs[0x6c] = 0xf2; /* 0x1410 for X1200, 13f2 for X1200/rev. 97 */ dev->shadow_regs[0x6d] = 0x13; /* set horizontal resolution */ dev->shadow_regs[0x79] = 0x40; dev->shadow_regs[0x7a] = 0x02; /* Movement direction & step size */ dev->shadow_regs[0xc6] = 0x01; /* Motor enable & Coordinate space denominator */ dev->shadow_regs[0xc3] = 0x81; switch (dev->model.motor_type) { case X74_MOTOR: dev->shadow_regs[0xc4] = 0x20; dev->shadow_regs[0xc5] = 0x00; dev->shadow_regs[0xc8] = 0x04; /* step size range2 */ dev->shadow_regs[0xc9] = 0x39; dev->shadow_regs[0xca] = 0x00; /* motor curve stuff */ dev->shadow_regs[0xe0] = 0x29; dev->shadow_regs[0xe1] = 0x17; dev->shadow_regs[0xe2] = 0x8f; dev->shadow_regs[0xe3] = 0x06; dev->shadow_regs[0xe4] = 0x61; dev->shadow_regs[0xe5] = 0x16; dev->shadow_regs[0xe6] = 0x64; dev->shadow_regs[0xe7] = 0xb5; dev->shadow_regs[0xe8] = 0x08; dev->shadow_regs[0xe9] = 0x32; dev->shadow_regs[0xec] = 0x0c; dev->shadow_regs[0xef] = 0x08; break; case A920_MOTOR: case X1100_MOTOR: /* ? */ dev->shadow_regs[0xc5] = 0x19; /* step size range2 */ dev->shadow_regs[0xc9] = 0x3a; /* ? */ dev->shadow_regs[0xca] = 0x08; /* motor curve stuff */ dev->shadow_regs[0xe0] = 0xe3; dev->shadow_regs[0xe1] = 0x18; dev->shadow_regs[0xe2] = 0x03; dev->shadow_regs[0xe3] = 0x06; dev->shadow_regs[0xe4] = 0x2b; dev->shadow_regs[0xe5] = 0x17; dev->shadow_regs[0xe6] = 0xdc; dev->shadow_regs[0xe7] = 0xb3; dev->shadow_regs[0xe8] = 0x07; dev->shadow_regs[0xe9] = 0x1b; dev->shadow_regs[0xec] = 0x07; dev->shadow_regs[0xef] = 0x03; break; } /* Stop the scanner */ low_stop_mvmt (devnum); /* write regs out twice */ dev->shadow_regs[0x32] = 0x00; low_write_all_regs (devnum, dev->shadow_regs); dev->shadow_regs[0x32] = 0x40; low_write_all_regs (devnum, dev->shadow_regs); /* Start Scan */ status = rts88xx_commit (devnum, dev->shadow_regs[0x2c]); /* create buffer to hold up to 10 lines of scan data */ buffer = calloc (10 * 2500, sizeof (char)); if (buffer == NULL) { return SANE_FALSE; } home_line_count = 0; in_home_region = SANE_FALSE; #ifdef DEEP_DEBUG img = fopen ("find_bwd.pnm", "wb"); fprintf (img, "P5\n2500 100\n255\n"); #endif while (buffer_count < buffer_limit) { size_returned = 0; got_line = SANE_FALSE; while (!got_line) { /* always poll twice (needed for timing) - disregard 1st poll */ cmd_size = 4; status = low_usb_bulk_write (devnum, command4_block, &cmd_size); if (status != SANE_STATUS_GOOD) return SANE_FALSE; cmd_size = 0x3; status = low_usb_bulk_read (devnum, poll_result, &cmd_size); if (status != SANE_STATUS_GOOD) return SANE_FALSE; cmd_size = 4; status = low_usb_bulk_write (devnum, command4_block, &cmd_size); if (status != SANE_STATUS_GOOD) return SANE_FALSE; cmd_size = 0x3; status = low_usb_bulk_read (devnum, poll_result, &cmd_size); if (status != SANE_STATUS_GOOD) return SANE_FALSE; if (! (poll_result[0] == 0 && poll_result[1] == 0 && poll_result[2] == 0)) { /* if result != 00 00 00 we got data */ got_line = SANE_TRUE; high_byte = poll_result[2] << 16; mid_byte = poll_result[1] << 8; low_byte = poll_result[0]; size_returned = high_byte + mid_byte + low_byte; } } /*size_requested = size_returned;*/ size_requested = 2500; no_of_buffers = size_returned * 3; no_of_buffers = no_of_buffers / 2500; no_of_buffers = no_of_buffers >> 1; /* force 1 buffer at a time to improve accuracy, which slow downs search */ no_of_buffers = 1; if (no_of_buffers < 1) no_of_buffers = 1; else if (no_of_buffers > 10) no_of_buffers = 10; buffer_count = buffer_count + no_of_buffers; size_requested = no_of_buffers * 2500; /* Tell the scanner to send the data */ /* Write: 91 */ command5_block[1] = (SANE_Byte) (size_requested >> 16); command5_block[2] = (SANE_Byte) (size_requested >> 8); command5_block[3] = (SANE_Byte) (size_requested & 0xFF); cmd_size = 4; status = low_usb_bulk_write (devnum, command5_block, &cmd_size); if (status != SANE_STATUS_GOOD) return SANE_FALSE; /* Read it */ cmd_size = size_requested; status = low_usb_bulk_read (devnum, buffer, &cmd_size); if (status != SANE_STATUS_GOOD) return SANE_FALSE; for (i = 0; i < no_of_buffers; i++) { buffer_start = buffer + (i * 2500); /* Reverse order of bytes in words of buffer */ for (j = 0; j < 2500; j = j + 2) { temp_byte = *(buffer_start + j); *(buffer_start + j) = *(buffer_start + j + 1); *(buffer_start + j + 1) = temp_byte; } #ifdef DEEP_DEBUG fwrite (buffer + (i * 2500), 2500, 1, img); #endif if (low_is_home_line (dev, buffer_start)) { home_line_count++; if (home_line_count > 7) in_home_region = SANE_TRUE; } if (in_home_region) { /* slow down scanning : on purpose backtracking */ if (home_line_count) sleep (1); free (buffer); #ifdef DEEP_DEBUG fflush (img); i = ftell (img) / 2500; rewind (img); DBG (2, "sanei_lexmark_low_search_home_bwd: offset=%d\n", i); fprintf (img, "P5\n2500 %03d\n", i); fclose (img); #endif low_stop_mvmt (devnum); DBG (2, "sanei_lexmark_low_search_home_bwd: in home region, end.\n"); return SANE_TRUE; } } } /* end while (buffer_count > buffer_limit); */ free (buffer); #ifdef DEEP_DEBUG fflush (img); i = ftell (img) / 2500; rewind (img); fprintf (img, "P5\n2500 %03d\n", i); fclose (img); #endif low_stop_mvmt (devnum); DBG (2, "sanei_lexmark_low_search_home_bwd: end.\n"); return SANE_FALSE; } SANE_Status low_get_start_loc (SANE_Int resolution, SANE_Int * vert_start, SANE_Int * hor_start, SANE_Int offset, Lexmark_Device * dev) { SANE_Int start_600; switch (dev->model.sensor_type) { case X1100_2C_SENSOR: case X1200_USB2_SENSOR: case A920_SENSOR: case X1200_SENSOR: start_600 = 195 - offset; *hor_start = 0x68; break; case X1100_B2_SENSOR: start_600 = 195 - offset; switch (resolution) { case 75: *hor_start = 0x68; break; case 150: *hor_start = 0x68; break; case 300: *hor_start = 0x6a; break; case 600: *hor_start = 0x6b; break; case 1200: *hor_start = 0x6b; break; default: /* If we're here we have an invalid resolution */ return SANE_STATUS_INVAL; } break; case X74_SENSOR: start_600 = 268 - offset; switch (resolution) { case 75: *hor_start = 0x48; break; case 150: *hor_start = 0x48; break; case 300: *hor_start = 0x4a; break; case 600: *hor_start = 0x4b; break; default: /* If we're here we have an invalid resolution */ return SANE_STATUS_INVAL; } break; default: /* If we're here we have an unknown dev->model.sensor_type */ return SANE_STATUS_INVAL; } /* Calculate vertical start distance at 600dpi */ switch (resolution) { case 75: *vert_start = start_600 / 8; break; case 150: *vert_start = start_600 / 4; break; case 300: *vert_start = start_600 / 2; break; case 600: *vert_start = start_600; break; case 1200: *vert_start = start_600 * 2; break; default: /* If we're here we have an invalid resolution */ return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } void low_set_scan_area (SANE_Int res, SANE_Int tlx, SANE_Int tly, SANE_Int brx, SANE_Int bry, SANE_Int offset, SANE_Bool half_step, SANE_Byte * regs, Lexmark_Device * dev) { SANE_Int vert_start = 0; SANE_Int hor_start = 0; SANE_Int vert_end; SANE_Int hor_end; low_get_start_loc (res, &vert_start, &hor_start, offset, dev); /* convert pixel height to vertical location coordinates */ vert_end = vert_start + (bry * res) / 600; vert_start += (tly * res) / 600; /* scan area size : for A920, 600 color scans are done at 1200 y dpi */ /* this follow what was found in usb logs */ if (half_step) { vert_end = vert_end * 2; vert_start = vert_start * 2; } /* set vertical start position registers */ regs[0x60] = LOBYTE (vert_start); regs[0x61] = HIBYTE (vert_start); /* set vertical end position registers */ regs[0x62] = LOBYTE (vert_end); regs[0x63] = HIBYTE (vert_end); /* convert pixel width to horizontal location coordinates */ hor_end = hor_start + brx; hor_start += tlx; regs[0x66] = LOBYTE (hor_start); regs[0x67] = HIBYTE (hor_start); /* set horizontal end position registers */ regs[0x6c] = LOBYTE (hor_end); regs[0x6d] = HIBYTE (hor_end); /* Debug */ DBG (2, "low_set_scan_area: vert_start: %d (tly=%d)\n", vert_start, tly); DBG (2, "low_set_scan_area: vert_end: %d\n", vert_end); DBG (2, "low_set_scan_area: hor_start: %d\n", hor_start); DBG (2, "low_set_scan_area: hor_end: %d\n", hor_end); } SANE_Int sanei_lexmark_low_find_start_line (Lexmark_Device * dev) { /* This function scans forward 59 lines, reading 88 bytes per line from the middle of the horizontal line: pixel 0xa84 to pixel 0x9d4. It scans with the following parameters: dir=fwd mode=grayscale h.res=300 dpi v.res=600 dpi hor. pixels = (0xa84 - 0x9d4)/2 = 0x58 = 88 vert. pixels = 0x3e - 0x03 = 0x3b = 59 data = 88x59=5192=0x1448 It assumes we are in the start dot, or just before it. We are reading enough lines at 600dpi to read past the dot. We return the number of entirely white lines read consecutively, so we know how far past the dot we are. To find the number of consecutive white lines we do the following: Byte swap the order of the bytes in the buffer. Go through the buffer finding low and high values, which are computed by comparing to the weighted average: weighted_average = (lowest value + (highest value - lowest value)/4) Low bytes are changed to 0xFF (white), higher of equal bytes are changed to 0x00 (black),so that the buffer only contains white (0xFF) or black (0x00) values. Next, we go through the buffer a line (88 bytes) at a time for 59 lines to read the entire buffer. For each byte in a line we check if the byte is black. If it is we increment the black byte counter. After each line we check the black byte counter. If it is greater than 0 we increment the black line count and set the white line count to 0. If there were no black bytes in the line we set the black line count to 0 and increment the white line count. When all lines have been processed we return the white line count. */ int blackLineCount = 0; int whiteLineCount = 0; int blackByteCounter = 0; unsigned char max_byte = 0; unsigned char min_byte = 0xFF; unsigned char weighted_average; int i, j; #ifdef DEEP_DEBUG FILE *fdbg = NULL; #endif SANE_Byte poll_result[3]; SANE_Byte *buffer; SANE_Byte temp_byte; static SANE_Byte command4_block[] = { 0x90, 0x00, 0x00, 0x03 }; static SANE_Byte command5_block[] = { 0x91, 0x00, 0x14, 0x48 }; size_t cmd_size; SANE_Bool got_line; DBG (2, "sanei_lexmark_low_find_start_line:\n"); /* set up registers */ switch (dev->model.sensor_type) { case X74_SENSOR: dev->shadow_regs[0x2c] = 0x04; dev->shadow_regs[0x2d] = 0x46; dev->shadow_regs[0x34] = 0x05; dev->shadow_regs[0x35] = 0x05; dev->shadow_regs[0x36] = 0x0b; dev->shadow_regs[0x37] = 0x0b; dev->shadow_regs[0x38] = 0x11; dev->shadow_regs[0x40] = 0x40; rts88xx_set_gain (dev->shadow_regs, 6, 6, 6); break; case X1100_B2_SENSOR: dev->shadow_regs[0x2c] = 0x0f; dev->shadow_regs[0x2d] = 0x51; dev->shadow_regs[0x34] = 0x0d; dev->shadow_regs[0x35] = 0x0d; dev->shadow_regs[0x36] = 0x1d; dev->shadow_regs[0x37] = 0x1d; dev->shadow_regs[0x38] = 0x29; dev->shadow_regs[0x65] = 0x80; dev->shadow_regs[0x85] = 0x00; dev->shadow_regs[0x93] = 0x06; rts88xx_set_gain (dev->shadow_regs, 6, 6, 6); break; case X1100_2C_SENSOR: rts88xx_set_gain (dev->shadow_regs, 10, 10, 10); dev->shadow_regs[0x28] = 0xf5; dev->shadow_regs[0x29] = 0xf7; dev->shadow_regs[0x2a] = 0xf5; dev->shadow_regs[0x2b] = 0x17; dev->shadow_regs[0x2c] = 0x0d; dev->shadow_regs[0x2d] = 0x4f; dev->shadow_regs[0x31] = 0x01; dev->shadow_regs[0x34] = 0x11; dev->shadow_regs[0x35] = 0x11; dev->shadow_regs[0x36] = 0x21; dev->shadow_regs[0x37] = 0x21; dev->shadow_regs[0x38] = 0x31; dev->shadow_regs[0x72] = 0x35; dev->shadow_regs[0x74] = 0x4e; dev->shadow_regs[0x85] = 0x02; dev->shadow_regs[0x86] = 0x33; dev->shadow_regs[0x87] = 0x0f; dev->shadow_regs[0x88] = 0x24; dev->shadow_regs[0x91] = 0x19; dev->shadow_regs[0x92] = 0x20; dev->shadow_regs[0xea] = 0x00; dev->shadow_regs[0xeb] = 0x00; break; case A920_SENSOR: dev->shadow_regs[0x2c] = 0x0d; dev->shadow_regs[0x2d] = 0x4f; dev->shadow_regs[0x34] = 0x11; dev->shadow_regs[0x35] = 0x11; dev->shadow_regs[0x36] = 0x21; dev->shadow_regs[0x37] = 0x21; dev->shadow_regs[0x38] = 0x31; dev->shadow_regs[0x85] = 0x05; dev->shadow_regs[0x88] = 0x44; dev->shadow_regs[0x92] = 0x85; dev->shadow_regs[0x93] = 0x0e; rts88xx_set_gain (dev->shadow_regs, 6, 6, 6); break; case X1200_SENSOR: dev->shadow_regs[0x2c] = 0x01; dev->shadow_regs[0x2d] = 0x03; dev->shadow_regs[0x34] = 0x0d; dev->shadow_regs[0x35] = 0x0d; dev->shadow_regs[0x36] = 0x1d; dev->shadow_regs[0x37] = 0x1d; dev->shadow_regs[0x38] = 0x29; dev->shadow_regs[0xea] = 0x00; dev->shadow_regs[0xeb] = 0x00; dev->shadow_regs[0x40] = 0x80; dev->shadow_regs[0x50] = 0x00; dev->shadow_regs[0x81] = 0x00; dev->shadow_regs[0x82] = 0x00; dev->shadow_regs[0x85] = 0x00; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0xff; dev->shadow_regs[0x88] = 0x02; dev->shadow_regs[0x92] = 0x00; rts88xx_set_gain (dev->shadow_regs, 10, 10, 10); break; case X1200_USB2_SENSOR: dev->shadow_regs[0x2c] = 0x01; dev->shadow_regs[0x2d] = 0x03; dev->shadow_regs[0x34] = 0x0d; dev->shadow_regs[0x35] = 0x0d; dev->shadow_regs[0x36] = 0x1d; dev->shadow_regs[0x37] = 0x1d; dev->shadow_regs[0x38] = 0x29; dev->shadow_regs[0xea] = 0x00; dev->shadow_regs[0xeb] = 0x00; rts88xx_set_gain (dev->shadow_regs, 10, 10, 10); break; } /* set offset to a safe value */ rts88xx_set_offset (dev->shadow_regs, 0x80, 0x80, 0x80); /* set grayscale scan */ dev->shadow_regs[0x2f] = 0x21; /* set motor resolution divisor */ dev->shadow_regs[0x39] = 0x01; /* set vertical start/end positions */ dev->shadow_regs[0x60] = 0x03; dev->shadow_regs[0x61] = 0x00; dev->shadow_regs[0x62] = 0x3e; dev->shadow_regs[0x63] = 0x00; /* set # of head moves per CIS read */ rts88xx_set_scan_frequency (dev->shadow_regs, 1); /* set horizontal start position */ dev->shadow_regs[0x66] = 0xd4; /* 0xf2 for X1200 */ dev->shadow_regs[0x67] = 0x09; /* set horizontal end position */ dev->shadow_regs[0x6c] = 0x84; /* 0xa2 for X1200 */ dev->shadow_regs[0x6d] = 0x0a; /* set horizontal resolution */ dev->shadow_regs[0x79] = 0x40; dev->shadow_regs[0x7a] = 0x02; /* set for ? */ /* Motor enable & Coordinate space denominator */ dev->shadow_regs[0xc3] = 0x81; switch (dev->model.motor_type) { case X1100_MOTOR: case A920_MOTOR: /* set for ? */ dev->shadow_regs[0xc5] = 0x22; /* Movement direction & step size */ dev->shadow_regs[0xc6] = 0x09; /* step size range2 */ dev->shadow_regs[0xc9] = 0x3b; /* set for ? */ dev->shadow_regs[0xca] = 0x1f; dev->shadow_regs[0xe0] = 0xf7; dev->shadow_regs[0xe1] = 0x16; /* step size range0 */ dev->shadow_regs[0xe2] = 0x87; /* ? */ dev->shadow_regs[0xe3] = 0x13; dev->shadow_regs[0xe4] = 0x1b; dev->shadow_regs[0xe5] = 0x16; dev->shadow_regs[0xe6] = 0xdc; dev->shadow_regs[0xe7] = 0x00; dev->shadow_regs[0xe8] = 0x00; dev->shadow_regs[0xe9] = 0x1b; dev->shadow_regs[0xec] = 0x07; dev->shadow_regs[0xef] = 0x03; break; case X74_MOTOR: dev->shadow_regs[0xc4] = 0x20; dev->shadow_regs[0xc5] = 0x22; /* Movement direction & step size */ dev->shadow_regs[0xc6] = 0x0b; dev->shadow_regs[0xc8] = 0x04; dev->shadow_regs[0xc9] = 0x39; dev->shadow_regs[0xca] = 0x1f; /* bounds of movement range0 */ dev->shadow_regs[0xe0] = 0x2f; dev->shadow_regs[0xe1] = 0x11; /* step size range0 */ dev->shadow_regs[0xe2] = 0x9f; /* ? */ dev->shadow_regs[0xe3] = 0x0f; /* bounds of movement range1 */ dev->shadow_regs[0xe4] = 0xcb; dev->shadow_regs[0xe5] = 0x10; /* step size range1 */ dev->shadow_regs[0xe6] = 0x64; /* bounds of movement range2 */ dev->shadow_regs[0xe7] = 0x00; dev->shadow_regs[0xe8] = 0x00; /* step size range2 */ dev->shadow_regs[0xe9] = 0x32; /* bounds of movement range3 */ dev->shadow_regs[0xea] = 0x00; dev->shadow_regs[0xeb] = 0x00; /* step size range3 */ dev->shadow_regs[0xec] = 0x0c; /* bounds of movement range4 -only for 75dpi grayscale */ dev->shadow_regs[0xed] = 0x00; dev->shadow_regs[0xee] = 0x00; /* step size range4 */ dev->shadow_regs[0xef] = 0x08; break; } /* Stop the scanner */ low_stop_mvmt (dev->devnum); /* write regs out twice */ dev->shadow_regs[0x32] = 0x00; low_write_all_regs (dev->devnum, dev->shadow_regs); dev->shadow_regs[0x32] = 0x40; low_write_all_regs (dev->devnum, dev->shadow_regs); /* Start Scan */ rts88xx_commit (dev->devnum, dev->shadow_regs[0x2c]); /* Poll the available byte count until not 0 */ got_line = SANE_FALSE; while (!got_line) { cmd_size = 4; low_usb_bulk_write (dev->devnum, command4_block, &cmd_size); cmd_size = 0x3; low_usb_bulk_read (dev->devnum, poll_result, &cmd_size); if (! (poll_result[0] == 0 && poll_result[1] == 0 && poll_result[2] == 0)) { /* if result != 00 00 00 we got data */ got_line = SANE_TRUE; } #ifdef FAKE_USB got_line = SANE_TRUE; #endif } /* create buffer for scan data */ buffer = calloc (5192, sizeof (char)); if (buffer == NULL) { return -1; } /* Tell the scanner to send the data */ /* Write: 91 00 14 48 */ cmd_size = 4; low_usb_bulk_write (dev->devnum, command5_block, &cmd_size); /* Read it */ cmd_size = 0x1448; low_usb_bulk_read (dev->devnum, buffer, &cmd_size); /* Stop the scanner */ low_stop_mvmt (dev->devnum); /* Reverse order of bytes in words of buffer */ for (i = 0; i < 5192; i = i + 2) { temp_byte = *(buffer + i); *(buffer + i) = *(buffer + i + 1); *(buffer + i + 1) = temp_byte; } #ifdef DEEP_DEBUG fdbg = fopen ("find_start.pnm", "wb"); if (fdbg != NULL) { fprintf (fdbg, "P5\n%d %d\n255\n", 88, 59); fwrite (buffer, 5192, 1, fdbg); fclose (fdbg); } #endif /* Find the max and the min */ for (i = 0; i < 5192; i++) { if (*(buffer + i) > max_byte) max_byte = *(buffer + i); if (*(buffer + i) < min_byte) min_byte = *(buffer + i); } weighted_average = min_byte + ((max_byte - min_byte) / 4); /* Set bytes as black (0x00) or white (0xFF) */ for (i = 0; i < 5192; i++) { if (*(buffer + i) > weighted_average) *(buffer + i) = 0xFF; else *(buffer + i) = 0x00; } #ifdef DEEP_DEBUG fdbg = fopen ("find_start_after.pnm", "wb"); if (fdbg != NULL) { fprintf (fdbg, "P5\n%d %d\n255\n", 88, 59); fwrite (buffer, 5192, 1, fdbg); fclose (fdbg); } #endif /* Go through 59 lines */ for (j = 0; j < 59; j++) { blackByteCounter = 0; /* Go through 88 bytes per line */ for (i = 0; i < 88; i++) { /* Is byte black? */ if (*(buffer + (j * 88) + i) == 0) { blackByteCounter++; } } /* end for line */ if (blackByteCounter > 0) { /* This was a black line */ blackLineCount++; whiteLineCount = 0; } else { /* This is a white line */ whiteLineCount++; blackLineCount = 0; } } /* end for buffer */ free (buffer); /* Stop the scanner. This is needed to get the right distance to the scanning area */ if (dev->model.sensor_type == X74_SENSOR) low_stop_mvmt (dev->devnum); DBG (2, "sanei_lexmark_low_find_start_line: end.\n"); return whiteLineCount; } SANE_Status sanei_lexmark_low_set_scan_regs (Lexmark_Device * dev, SANE_Int resolution, SANE_Int offset, SANE_Bool calibrated) { SANE_Bool isColourScan; DBG (2, "sanei_lexmark_low_set_scan_regs:\n"); DBG (7, "sanei_lexmark_low_set_scan_regs: resolution=%d DPI\n", resolution); /* colour mode */ if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0) isColourScan = SANE_TRUE; else isColourScan = SANE_FALSE; /* set up registers */ switch (dev->model.sensor_type) { case X74_SENSOR: dev->shadow_regs[0x2c] = 0x03; dev->shadow_regs[0x2d] = 0x45; break; case X1100_B2_SENSOR: dev->shadow_regs[0x2c] = 0x0f; dev->shadow_regs[0x2d] = 0x51; break; case X1100_2C_SENSOR: dev->shadow_regs[0x2c] = 0x0d; dev->shadow_regs[0x2d] = 0x4f; break; case A920_SENSOR: dev->shadow_regs[0x2c] = 0x0d; dev->shadow_regs[0x2d] = 0x4f; break; case X1200_SENSOR: dev->shadow_regs[0x2c] = 0x01; dev->shadow_regs[0x2d] = 0x03; break; case X1200_USB2_SENSOR: dev->shadow_regs[0x2c] = 0x01; dev->shadow_regs[0x2d] = 0x03; break; } low_set_scan_area (resolution, dev->val[OPT_TL_X].w, dev->val[OPT_TL_Y].w, dev->val[OPT_BR_X].w, dev->val[OPT_BR_Y].w, offset, (dev->model.motor_type == A920_MOTOR || dev->model.motor_type == X74_MOTOR) && isColourScan && (resolution == 600), dev->shadow_regs, dev); /* may be we could use a sensor descriptor that would held the max horiz dpi */ if (dev->val[OPT_RESOLUTION].w < 600) dev->shadow_regs[0x7a] = 600 / dev->val[OPT_RESOLUTION].w; else dev->shadow_regs[0x7a] = 1; /* 75dpi x 75dpi */ if (resolution == 75) { DBG (5, "sanei_lexmark_low_set_scan_regs(): 75 DPI resolution\n"); if (isColourScan) { switch (dev->model.sensor_type) { case X74_SENSOR: dev->shadow_regs[0x34] = 0x04; dev->shadow_regs[0x36] = 0x03; dev->shadow_regs[0x38] = 0x03; dev->shadow_regs[0x79] = 0x08; dev->shadow_regs[0x80] = 0x0d; dev->shadow_regs[0x81] = 0x0e; dev->shadow_regs[0x82] = 0x02; dev->shadow_regs[0x85] = 0x00; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0x00; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x91] = 0x00; dev->shadow_regs[0x92] = 0x00; dev->shadow_regs[0x93] = 0x06;; break; case X1100_B2_SENSOR: dev->shadow_regs[0x34] = 0x05; dev->shadow_regs[0x36] = 0x05; dev->shadow_regs[0x38] = 0x05; dev->shadow_regs[0x80] = 0x0c; dev->shadow_regs[0x81] = 0x0c; dev->shadow_regs[0x82] = 0x09; dev->shadow_regs[0x85] = 0x00; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0x00; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x91] = 0x8c; dev->shadow_regs[0x92] = 0x40; dev->shadow_regs[0x93] = 0x06; break; case X1100_2C_SENSOR: dev->shadow_regs[0x34] = 0x03; dev->shadow_regs[0x36] = 0x04; dev->shadow_regs[0x38] = 0x03; dev->shadow_regs[0x80] = 0x00; dev->shadow_regs[0x81] = 0x02; dev->shadow_regs[0x82] = 0x03; dev->shadow_regs[0x85] = 0x20; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0x00; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x91] = 0x00; dev->shadow_regs[0x92] = 0x00; dev->shadow_regs[0x93] = 0x06; break; case A920_SENSOR: dev->shadow_regs[0x34] = 0x02; dev->shadow_regs[0x36] = 0x04; dev->shadow_regs[0x38] = 0x03; dev->shadow_regs[0x80] = 0x07; dev->shadow_regs[0x81] = 0x0f; dev->shadow_regs[0x82] = 0x03; dev->shadow_regs[0x85] = 0x05; dev->shadow_regs[0x86] = 0x14; dev->shadow_regs[0x87] = 0x06; dev->shadow_regs[0x88] = 0x44; dev->shadow_regs[0x91] = 0x60; dev->shadow_regs[0x92] = 0x85; dev->shadow_regs[0x93] = 0x0e; break; case X1200_SENSOR: dev->shadow_regs[0x34] = 0x02; dev->shadow_regs[0x36] = 0x03; dev->shadow_regs[0x38] = 0x01; dev->shadow_regs[0x79] = 0x20; dev->shadow_regs[0x80] = 0x08; dev->shadow_regs[0x81] = 0x02; dev->shadow_regs[0x82] = 0x0d; dev->shadow_regs[0x85] = 0x20; dev->shadow_regs[0x86] = 0x1e; dev->shadow_regs[0x87] = 0x39; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x91] = 0x00; /* dev->shadow_regs[0x92] = 0x92; */ dev->shadow_regs[0x93] = 0x06; break; case X1200_USB2_SENSOR: dev->shadow_regs[0x34] = 0x04; dev->shadow_regs[0x36] = 0x05; dev->shadow_regs[0x38] = 0x04; dev->shadow_regs[0x80] = 0x01; dev->shadow_regs[0x81] = 0x0a; dev->shadow_regs[0x82] = 0x0b; break; } switch (dev->model.motor_type) { case X74_MOTOR: dev->shadow_regs[0xc2] = 0x80; /* ? */ dev->shadow_regs[0xc4] = 0x20; dev->shadow_regs[0xc5] = 0x0c; dev->shadow_regs[0xc6] = 0x0b; dev->shadow_regs[0xc8] = 0x04; dev->shadow_regs[0xc9] = 0x39; dev->shadow_regs[0xca] = 0x01; /* bounds of movement range0 */ dev->shadow_regs[0xe0] = 0x1b; dev->shadow_regs[0xe1] = 0x0a; /* step size range0 */ dev->shadow_regs[0xe2] = 0x4f; /* ? */ dev->shadow_regs[0xe3] = 0x01; /* bounds of movement range1 */ dev->shadow_regs[0xe4] = 0xb3; dev->shadow_regs[0xe5] = 0x09; /* step size range1 */ dev->shadow_regs[0xe6] = 0x0d; /* bounds of movement range2 */ dev->shadow_regs[0xe7] = 0xe5; dev->shadow_regs[0xe8] = 0x02; /* step size range2 */ dev->shadow_regs[0xe9] = 0x05; /* bounds of movement range3 */ dev->shadow_regs[0xea] = 0xa0; dev->shadow_regs[0xeb] = 0x01; /* step size range3 */ dev->shadow_regs[0xec] = 0x01; /* bounds of movement range4 */ dev->shadow_regs[0xed] = 0x00; dev->shadow_regs[0xee] = 0x00; /* step size range4 */ dev->shadow_regs[0xef] = 0x01; break; case A920_MOTOR: case X1100_MOTOR: /* ? */ dev->shadow_regs[0xc5] = 0x0a; /* bounds of movement range0 */ dev->shadow_regs[0xe0] = 0x2b; dev->shadow_regs[0xe1] = 0x0a; /* step size range0 */ dev->shadow_regs[0xe2] = 0x7f; /* ? */ dev->shadow_regs[0xe3] = 0x01; /* bounds of movement range1 */ dev->shadow_regs[0xe4] = 0xbb; dev->shadow_regs[0xe5] = 0x09; /* step size range1 */ dev->shadow_regs[0xe6] = 0x0e; /* bounds of movement range2 */ dev->shadow_regs[0xe7] = 0x2b; dev->shadow_regs[0xe8] = 0x03; /* step size range2 */ dev->shadow_regs[0xe9] = 0x05; /* bounds of movement range3 */ dev->shadow_regs[0xea] = 0xa0; dev->shadow_regs[0xeb] = 0x01; /* step size range3 */ dev->shadow_regs[0xec] = 0x01; /* step size range4 */ dev->shadow_regs[0xef] = 0x01; break; } /* set colour scan */ dev->shadow_regs[0x2f] = 0x11; dev->shadow_regs[0x35] = 0x01; dev->shadow_regs[0x37] = 0x01; /* Motor enable & Coordinate space denominator */ dev->shadow_regs[0xc3] = 0x83; } else /* 75 dpi gray */ { switch (dev->model.sensor_type) { case X74_SENSOR: dev->shadow_regs[0x34] = 0x01; dev->shadow_regs[0x35] = 0x01; dev->shadow_regs[0x36] = 0x02; dev->shadow_regs[0x37] = 0x02; dev->shadow_regs[0x38] = 0x03; dev->shadow_regs[0x39] = 0x0f; dev->shadow_regs[0x40] = 0x80; dev->shadow_regs[0x79] = 0x08; dev->shadow_regs[0x85] = 0x00; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0x00; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x8c] = 0x02; dev->shadow_regs[0x8d] = 0x01; dev->shadow_regs[0x8e] = 0x60; dev->shadow_regs[0x8f] = 0x80; dev->shadow_regs[0x91] = 0x00; dev->shadow_regs[0x92] = 0x00; dev->shadow_regs[0x93] = 0x06; break; case X1100_B2_SENSOR: dev->shadow_regs[0x34] = 0x02; dev->shadow_regs[0x35] = 0x02; dev->shadow_regs[0x36] = 0x04; dev->shadow_regs[0x37] = 0x04; dev->shadow_regs[0x38] = 0x06; dev->shadow_regs[0x85] = 0x20; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0x00; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x91] = 0x00; dev->shadow_regs[0x92] = 0x00; dev->shadow_regs[0x93] = 0x06; break; case X1100_2C_SENSOR: dev->shadow_regs[0x34] = 0x01; dev->shadow_regs[0x35] = 0x01; dev->shadow_regs[0x36] = 0x02; dev->shadow_regs[0x37] = 0x02; dev->shadow_regs[0x38] = 0x03; /* these are half of B2 sensor */ dev->shadow_regs[0x85] = 0x20; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0x00; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x91] = 0x00; dev->shadow_regs[0x92] = 0x00; dev->shadow_regs[0x92] = 0x00; dev->shadow_regs[0x93] = 0x06; break; case A920_SENSOR: dev->shadow_regs[0x34] = 0x01; dev->shadow_regs[0x35] = 0x01; dev->shadow_regs[0x36] = 0x02; dev->shadow_regs[0x37] = 0x02; dev->shadow_regs[0x38] = 0x03; dev->shadow_regs[0x85] = 0x0d; dev->shadow_regs[0x86] = 0x14; dev->shadow_regs[0x87] = 0x06; dev->shadow_regs[0x88] = 0x45; dev->shadow_regs[0x91] = 0x60; dev->shadow_regs[0x92] = 0x8d; dev->shadow_regs[0x93] = 0x0e; break; case X1200_SENSOR: dev->shadow_regs[0x34] = 0x01; dev->shadow_regs[0x35] = 0x01; dev->shadow_regs[0x36] = 0x02; dev->shadow_regs[0x37] = 0x02; dev->shadow_regs[0x38] = 0x02; dev->shadow_regs[0x79] = 0x20; dev->shadow_regs[0x85] = 0x00; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0xff; dev->shadow_regs[0x88] = 0x02; dev->shadow_regs[0x92] = 0x00; break; case X1200_USB2_SENSOR: dev->shadow_regs[0x34] = 0x01; dev->shadow_regs[0x35] = 0x01; dev->shadow_regs[0x36] = 0x02; dev->shadow_regs[0x37] = 0x02; dev->shadow_regs[0x38] = 0x02; dev->shadow_regs[0x79] = 0x20; dev->shadow_regs[0x85] = 0x00; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0xff; dev->shadow_regs[0x88] = 0x02; dev->shadow_regs[0x92] = 0x00; break; } switch (dev->model.motor_type) { case X74_MOTOR: /* ? */ dev->shadow_regs[0xc4] = 0x20; dev->shadow_regs[0xc5] = 0x0a; dev->shadow_regs[0xc6] = 0x0b; dev->shadow_regs[0xc8] = 0x04; dev->shadow_regs[0xc9] = 0x39; dev->shadow_regs[0xca] = 0x01; /* bounds of movement range0 */ dev->shadow_regs[0xe0] = 0x07; dev->shadow_regs[0xe1] = 0x18; /* step size range0 */ dev->shadow_regs[0xe2] = 0xe7; /* ? */ dev->shadow_regs[0xe3] = 0x03; /* bounds of movement range1 */ dev->shadow_regs[0xe4] = 0xe7; dev->shadow_regs[0xe5] = 0x14; /* step size range1 */ dev->shadow_regs[0xe6] = 0x64; /* bounds of movement range2 */ dev->shadow_regs[0xe7] = 0xcb; dev->shadow_regs[0xe8] = 0x08; /* step size range2 */ dev->shadow_regs[0xe9] = 0x32; /* bounds of movement range3 */ dev->shadow_regs[0xea] = 0xe3; dev->shadow_regs[0xeb] = 0x04; /* step size range3 */ dev->shadow_regs[0xec] = 0x0c; /* bounds of movement range4 */ dev->shadow_regs[0xed] = 0x00; dev->shadow_regs[0xee] = 0x00; /* step size range4 */ dev->shadow_regs[0xef] = 0x08; break; case A920_MOTOR: case X1100_MOTOR: /* ? */ dev->shadow_regs[0xc5] = 0x10; /* Movement direction & step size */ dev->shadow_regs[0xc6] = 0x09; dev->shadow_regs[0xc9] = 0x3b; dev->shadow_regs[0xca] = 0x01; /* bounds of movement range0 */ dev->shadow_regs[0xe0] = 0x4d; dev->shadow_regs[0xe1] = 0x1c; /* step size range0 */ dev->shadow_regs[0xe2] = 0x71; /* ? */ dev->shadow_regs[0xe3] = 0x02; /* bounds of movement range1 */ dev->shadow_regs[0xe4] = 0x6d; dev->shadow_regs[0xe5] = 0x15; /* step size range1 */ dev->shadow_regs[0xe6] = 0xdc; /* bounds of movement range2 */ dev->shadow_regs[0xe7] = 0xad; dev->shadow_regs[0xe8] = 0x07; /* step size range2 */ dev->shadow_regs[0xe9] = 0x1b; /* bounds of movement range3 */ dev->shadow_regs[0xea] = 0xe1; dev->shadow_regs[0xeb] = 0x03; /* step size range3 */ dev->shadow_regs[0xec] = 0x07; /* bounds of movement range4 -only for 75dpi grayscale */ dev->shadow_regs[0xed] = 0xc2; dev->shadow_regs[0xee] = 0x02; /* step size range4 */ dev->shadow_regs[0xef] = 0x03; break; } /* set grayscale scan */ dev->shadow_regs[0x2f] = 0x21; /* set ? only for colour? */ dev->shadow_regs[0x80] = 0x00; dev->shadow_regs[0x81] = 0x00; dev->shadow_regs[0x82] = 0x00; /* Motor enable & Coordinate space denominator */ dev->shadow_regs[0xc3] = 0x81; } /* set motor resolution divisor */ dev->shadow_regs[0x39] = 0x0f; /* set # of head moves per CIS read */ rts88xx_set_scan_frequency (dev->shadow_regs, 1); /* set horizontal resolution */ if (dev->model.sensor_type != X1200_SENSOR) dev->shadow_regs[0x79] = 0x08; } /* 150dpi x 150dpi */ if (resolution == 150) { DBG (5, "sanei_lexmark_low_set_scan_regs(): 150 DPI resolution\n"); if (isColourScan) { switch (dev->model.sensor_type) { case X74_SENSOR: dev->shadow_regs[0x34] = 0x08; dev->shadow_regs[0x36] = 0x06; dev->shadow_regs[0x38] = 0x05; dev->shadow_regs[0x39] = 0x07; /* resolution divisor */ dev->shadow_regs[0x79] = 0x08; dev->shadow_regs[0x80] = 0x0a; dev->shadow_regs[0x81] = 0x0c; dev->shadow_regs[0x82] = 0x04; dev->shadow_regs[0x85] = 0x00; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0x00; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x91] = 0x00; dev->shadow_regs[0x92] = 0x00; dev->shadow_regs[0x93] = 0x06; break; case X1100_B2_SENSOR: dev->shadow_regs[0x34] = 0x0b; dev->shadow_regs[0x36] = 0x0b; dev->shadow_regs[0x38] = 0x0a; dev->shadow_regs[0x80] = 0x05; dev->shadow_regs[0x81] = 0x05; dev->shadow_regs[0x82] = 0x0a; dev->shadow_regs[0x85] = 0x83; dev->shadow_regs[0x86] = 0x7e; dev->shadow_regs[0x87] = 0xad; dev->shadow_regs[0x88] = 0x35; dev->shadow_regs[0x91] = 0xfe; dev->shadow_regs[0x92] = 0xdf; dev->shadow_regs[0x93] = 0x0e; break; case X1100_2C_SENSOR: dev->shadow_regs[0x34] = 0x05; dev->shadow_regs[0x36] = 0x07; dev->shadow_regs[0x38] = 0x05; dev->shadow_regs[0x80] = 0x00; dev->shadow_regs[0x81] = 0x02; dev->shadow_regs[0x82] = 0x06; dev->shadow_regs[0x85] = 0x20; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0x00; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x91] = 0x00; dev->shadow_regs[0x92] = 0x00; dev->shadow_regs[0x93] = 0x06; break; case A920_SENSOR: dev->shadow_regs[0x34] = 0x03; dev->shadow_regs[0x36] = 0x08; dev->shadow_regs[0x38] = 0x05; dev->shadow_regs[0x80] = 0x0e; dev->shadow_regs[0x81] = 0x07; dev->shadow_regs[0x82] = 0x02; dev->shadow_regs[0x85] = 0x05; dev->shadow_regs[0x86] = 0x14; dev->shadow_regs[0x87] = 0x06; dev->shadow_regs[0x88] = 0x04; dev->shadow_regs[0x91] = 0xe0; dev->shadow_regs[0x92] = 0x85; dev->shadow_regs[0x93] = 0x0e; break; case X1200_SENSOR: dev->shadow_regs[0x34] = 0x04; dev->shadow_regs[0x36] = 0x05; dev->shadow_regs[0x38] = 0x02; /* data compression dev->shadow_regs[0x40] = 0x90; dev->shadow_regs[0x50] = 0x20; */ /* no data compression */ dev->shadow_regs[0x40] = 0x80; dev->shadow_regs[0x50] = 0x00; dev->shadow_regs[0x79] = 0x20; dev->shadow_regs[0x80] = 0x00; dev->shadow_regs[0x81] = 0x07; dev->shadow_regs[0x82] = 0x0b; dev->shadow_regs[0x85] = 0x20; dev->shadow_regs[0x86] = 0x1e; dev->shadow_regs[0x87] = 0x39; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x92] = 0x92; break; case X1200_USB2_SENSOR: dev->shadow_regs[0x34] = 0x04; dev->shadow_regs[0x36] = 0x05; dev->shadow_regs[0x38] = 0x02; dev->shadow_regs[0x40] = 0x80; dev->shadow_regs[0x50] = 0x00; dev->shadow_regs[0x79] = 0x20; dev->shadow_regs[0x80] = 0x00; dev->shadow_regs[0x81] = 0x07; dev->shadow_regs[0x82] = 0x0b; dev->shadow_regs[0x85] = 0x20; dev->shadow_regs[0x86] = 0x1e; dev->shadow_regs[0x87] = 0x39; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x92] = 0x92; break; } /* switch */ switch (dev->model.motor_type) { case X74_MOTOR: dev->shadow_regs[0xc2] = 0x80; /* ? */ dev->shadow_regs[0xc4] = 0x20; dev->shadow_regs[0xc5] = 0x0e; /* Movement direction & step size */ dev->shadow_regs[0xc6] = 0x0b; dev->shadow_regs[0xc7] = 0x00; dev->shadow_regs[0xc8] = 0x04; dev->shadow_regs[0xc9] = 0x39; dev->shadow_regs[0xca] = 0x03; /* bounds of movement range0 */ dev->shadow_regs[0xe0] = 0x41; dev->shadow_regs[0xe1] = 0x09; /* step size range0 */ dev->shadow_regs[0xe2] = 0x89; /* ? */ dev->shadow_regs[0xe3] = 0x02; /* bounds of movement range1 */ dev->shadow_regs[0xe4] = 0x0d; dev->shadow_regs[0xe5] = 0x09; /* step size range1 */ dev->shadow_regs[0xe6] = 0x0d; /* bounds of movement range2 */ dev->shadow_regs[0xe7] = 0xe8; dev->shadow_regs[0xe8] = 0x02; /* step size range2 */ dev->shadow_regs[0xe9] = 0x05; /* bounds of movement range3 */ dev->shadow_regs[0xea] = 0x00; dev->shadow_regs[0xeb] = 0x00; /* step size range3 */ dev->shadow_regs[0xec] = 0x01; /* bounds of movement range4 */ dev->shadow_regs[0xed] = 0x00; dev->shadow_regs[0xee] = 0x00; /* step size range4 */ dev->shadow_regs[0xef] = 0x01; break; case X1100_MOTOR: case A920_MOTOR: /* ? */ dev->shadow_regs[0xc5] = 0x0e; /* ? */ dev->shadow_regs[0xc9] = 0x3a; dev->shadow_regs[0xca] = 0x03; /* bounds of movement range0 */ dev->shadow_regs[0xe0] = 0x61; dev->shadow_regs[0xe1] = 0x0a; /* step size range0 */ dev->shadow_regs[0xe2] = 0xed; /* ? */ dev->shadow_regs[0xe3] = 0x02; /* bounds of movement range1 */ dev->shadow_regs[0xe4] = 0x29; dev->shadow_regs[0xe5] = 0x0a; /* step size range1 */ dev->shadow_regs[0xe6] = 0x0e; /* bounds of movement range2 */ dev->shadow_regs[0xe7] = 0x29; dev->shadow_regs[0xe8] = 0x03; /* step size range2 */ dev->shadow_regs[0xe9] = 0x05; /* bounds of movement range3 */ dev->shadow_regs[0xea] = 0x00; dev->shadow_regs[0xeb] = 0x00; /* step size range3 */ dev->shadow_regs[0xec] = 0x01; /* step size range4 */ dev->shadow_regs[0xef] = 0x01; break; } /* set colour scan */ dev->shadow_regs[0x2f] = 0x11; dev->shadow_regs[0x35] = 0x01; dev->shadow_regs[0x37] = 0x01; /* Motor enable & Coordinate space denominator */ dev->shadow_regs[0xc3] = 0x83; } /* if (isColourScan) */ else { switch (dev->model.sensor_type) { case X74_SENSOR: dev->shadow_regs[0x34] = 0x02; dev->shadow_regs[0x35] = 0x02; dev->shadow_regs[0x36] = 0x04; dev->shadow_regs[0x37] = 0x04; dev->shadow_regs[0x38] = 0x06; dev->shadow_regs[0x39] = 0x07; /* Motor enable & Coordinate space denominator */ dev->shadow_regs[0x40] = 0x40; /* resolution divisor */ dev->shadow_regs[0x79] = 0x08; dev->shadow_regs[0x85] = 0x00; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0x00; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x91] = 0x00; dev->shadow_regs[0x92] = 0x00; dev->shadow_regs[0x93] = 0x06; break; case X1100_B2_SENSOR: dev->shadow_regs[0x34] = 0x04; dev->shadow_regs[0x35] = 0x04; dev->shadow_regs[0x36] = 0x07; dev->shadow_regs[0x37] = 0x07; dev->shadow_regs[0x38] = 0x0a; dev->shadow_regs[0x85] = 0x00; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0x00; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x91] = 0x00; dev->shadow_regs[0x92] = 0x00; dev->shadow_regs[0x93] = 0x06; break; case X1100_2C_SENSOR: dev->shadow_regs[0x34] = 0x02; dev->shadow_regs[0x35] = 0x02; dev->shadow_regs[0x36] = 0x04; dev->shadow_regs[0x37] = 0x04; dev->shadow_regs[0x38] = 0x05; dev->shadow_regs[0x85] = 0x20; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0x00; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x91] = 0x00; dev->shadow_regs[0x92] = 0x00; dev->shadow_regs[0x93] = 0x06; break; case A920_SENSOR: dev->shadow_regs[0x34] = 0x02; dev->shadow_regs[0x35] = 0x02; dev->shadow_regs[0x36] = 0x04; dev->shadow_regs[0x37] = 0x04; dev->shadow_regs[0x38] = 0x05; dev->shadow_regs[0x85] = 0x0d; dev->shadow_regs[0x86] = 0x14; dev->shadow_regs[0x87] = 0x06; dev->shadow_regs[0x88] = 0x45; dev->shadow_regs[0x91] = 0x60; dev->shadow_regs[0x92] = 0x8d; dev->shadow_regs[0x93] = 0x0e; break; case X1200_SENSOR: dev->shadow_regs[0x34] = 0x01; dev->shadow_regs[0x35] = 0x01; dev->shadow_regs[0x36] = 0x02; dev->shadow_regs[0x37] = 0x02; dev->shadow_regs[0x38] = 0x03; /* dev->shadow_regs[0x40] = 0x90; dev->shadow_regs[0x50] = 0x20; */ /* no data compression */ dev->shadow_regs[0x40] = 0x80; dev->shadow_regs[0x50] = 0x00; dev->shadow_regs[0x79] = 0x20; dev->shadow_regs[0x85] = 0x00; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0xff; dev->shadow_regs[0x88] = 0x02; dev->shadow_regs[0x92] = 0x92; break; case X1200_USB2_SENSOR: dev->shadow_regs[0x34] = 0x01; dev->shadow_regs[0x35] = 0x01; dev->shadow_regs[0x36] = 0x02; dev->shadow_regs[0x37] = 0x02; dev->shadow_regs[0x38] = 0x03; dev->shadow_regs[0x40] = 0x80; dev->shadow_regs[0x50] = 0x00; dev->shadow_regs[0x79] = 0x20; dev->shadow_regs[0x85] = 0x00; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0xff; dev->shadow_regs[0x88] = 0x02; dev->shadow_regs[0x92] = 0x92; break; } /* switch */ switch (dev->model.motor_type) { case X74_MOTOR: /* ? */ dev->shadow_regs[0xc4] = 0x20; dev->shadow_regs[0xc5] = 0x14; /* Movement direction & step size */ dev->shadow_regs[0xc6] = 0x0b; dev->shadow_regs[0xc8] = 0x04; dev->shadow_regs[0xc9] = 0x39; dev->shadow_regs[0xca] = 0x01; /* bounds of movement range0 */ dev->shadow_regs[0xe0] = 0x09; dev->shadow_regs[0xe1] = 0x18; /* step size range0 */ dev->shadow_regs[0xe2] = 0xe9; /* ? */ dev->shadow_regs[0xe3] = 0x03; /* bounds of movement range1 */ dev->shadow_regs[0xe4] = 0x79; dev->shadow_regs[0xe5] = 0x16; /* step size range1 */ dev->shadow_regs[0xe6] = 0x64; /* bounds of movement range2 */ dev->shadow_regs[0xe7] = 0xcd; dev->shadow_regs[0xe8] = 0x08; /* step size range2 */ dev->shadow_regs[0xe9] = 0x32; /* bounds of movement range3 */ dev->shadow_regs[0xea] = 0xe5; dev->shadow_regs[0xeb] = 0x04; /* step size range3 */ dev->shadow_regs[0xec] = 0x0c; /* bounds of movement range4 */ dev->shadow_regs[0xed] = 0x00; dev->shadow_regs[0xee] = 0x00; /* step size range4 */ dev->shadow_regs[0xef] = 0x08; break; case X1100_MOTOR: case A920_MOTOR: /* ? */ dev->shadow_regs[0xc5] = 0x16; /* Movement direction & step size */ dev->shadow_regs[0xc6] = 0x09; /* ? */ dev->shadow_regs[0xc9] = 0x3b; dev->shadow_regs[0xca] = 0x01; /* bounds of movement range0 */ dev->shadow_regs[0xe0] = 0xdd; dev->shadow_regs[0xe1] = 0x18; /* step size range0 */ dev->shadow_regs[0xe2] = 0x01; /* ? */ dev->shadow_regs[0xe3] = 0x03; /* bounds of movement range1 */ dev->shadow_regs[0xe4] = 0x6d; dev->shadow_regs[0xe5] = 0x15; /* step size range1 */ dev->shadow_regs[0xe6] = 0xdc; /* bounds of movement range2 */ dev->shadow_regs[0xe7] = 0xad; dev->shadow_regs[0xe8] = 0x07; /* step size range2 */ dev->shadow_regs[0xe9] = 0x1b; /* bounds of movement range3 */ dev->shadow_regs[0xea] = 0xe1; dev->shadow_regs[0xeb] = 0x03; /* step size range3 */ dev->shadow_regs[0xec] = 0x07; /* step size range4 */ dev->shadow_regs[0xef] = 0x03; break; } /* set grayscale scan */ dev->shadow_regs[0x2f] = 0x21; /* set motor resolution divisor */ dev->shadow_regs[0x39] = 0x07; /* set ? only for colour? */ dev->shadow_regs[0x80] = 0x00; dev->shadow_regs[0x81] = 0x00; dev->shadow_regs[0x82] = 0x00; /* Motor enable & Coordinate space denominator */ dev->shadow_regs[0xc3] = 0x81; } /* else (greyscale) */ /* set # of head moves per CIS read */ rts88xx_set_scan_frequency (dev->shadow_regs, 1); /* hum, horizontal resolution different for X1200 ? */ /* if (dev->model.sensor_type != X1200_SENSOR) dev->shadow_regs[0x79] = 0x20; */ } /*300dpi x 300dpi */ if (resolution == 300) { DBG (5, "sanei_lexmark_low_set_scan_regs(): 300 DPI resolution\n"); if (isColourScan) { switch (dev->model.sensor_type) { case X74_SENSOR: dev->shadow_regs[0x34] = 0x08; dev->shadow_regs[0x36] = 0x06; dev->shadow_regs[0x38] = 0x05; dev->shadow_regs[0x39] = 0x07; dev->shadow_regs[0x80] = 0x08; dev->shadow_regs[0x81] = 0x0a; dev->shadow_regs[0x82] = 0x03; dev->shadow_regs[0x85] = 0x00; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0x00; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x91] = 0x00; dev->shadow_regs[0x92] = 0x00; dev->shadow_regs[0x93] = 0x06; break; case X1100_B2_SENSOR: dev->shadow_regs[0x34] = 0x15; dev->shadow_regs[0x36] = 0x15; dev->shadow_regs[0x38] = 0x14; /* set motor resolution divisor */ dev->shadow_regs[0x39] = 0x03; dev->shadow_regs[0x80] = 0x0a; dev->shadow_regs[0x81] = 0x0a; dev->shadow_regs[0x82] = 0x06; dev->shadow_regs[0x85] = 0x83; dev->shadow_regs[0x86] = 0x7e; dev->shadow_regs[0x87] = 0xad; dev->shadow_regs[0x88] = 0x35; dev->shadow_regs[0x91] = 0xfe; dev->shadow_regs[0x92] = 0xdf; dev->shadow_regs[0x93] = 0x0e; break; case X1100_2C_SENSOR: dev->shadow_regs[0x34] = 0x08; dev->shadow_regs[0x36] = 0x0d; dev->shadow_regs[0x38] = 0x09; /* set motor resolution divisor */ dev->shadow_regs[0x39] = 0x03; dev->shadow_regs[0x80] = 0x0e; dev->shadow_regs[0x81] = 0x04; dev->shadow_regs[0x82] = 0x0a; dev->shadow_regs[0x85] = 0x20; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0x00; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x91] = 0x00; dev->shadow_regs[0x92] = 0x00; dev->shadow_regs[0x93] = 0x06; break; case A920_SENSOR: dev->shadow_regs[0x34] = 0x06; dev->shadow_regs[0x36] = 0x10; dev->shadow_regs[0x38] = 0x09; /* set motor resolution divisor */ dev->shadow_regs[0x39] = 0x03; dev->shadow_regs[0x80] = 0x0c; dev->shadow_regs[0x81] = 0x02; dev->shadow_regs[0x82] = 0x04; dev->shadow_regs[0x85] = 0x05; dev->shadow_regs[0x86] = 0x14; dev->shadow_regs[0x87] = 0x06; dev->shadow_regs[0x88] = 0x04; dev->shadow_regs[0x91] = 0xe0; dev->shadow_regs[0x92] = 0x85; dev->shadow_regs[0x93] = 0x0e; break; case X1200_SENSOR: dev->shadow_regs[0x34] = 0x07; dev->shadow_regs[0x36] = 0x09; dev->shadow_regs[0x38] = 0x04; /* set motor resolution divisor */ dev->shadow_regs[0x39] = 0x03; /* data compression dev->shadow_regs[0x40] = 0x90; dev->shadow_regs[0x50] = 0x20; */ /* no data compression */ dev->shadow_regs[0x40] = 0x80; dev->shadow_regs[0x50] = 0x00; dev->shadow_regs[0x80] = 0x00; dev->shadow_regs[0x81] = 0x0e; dev->shadow_regs[0x82] = 0x06; break; case X1200_USB2_SENSOR: dev->shadow_regs[0x34] = 0x07; dev->shadow_regs[0x36] = 0x09; dev->shadow_regs[0x38] = 0x04; /* set motor resolution divisor */ dev->shadow_regs[0x39] = 0x03; dev->shadow_regs[0x40] = 0x80; dev->shadow_regs[0x50] = 0x00; dev->shadow_regs[0x80] = 0x00; dev->shadow_regs[0x81] = 0x0e; dev->shadow_regs[0x82] = 0x06; break; } switch (dev->model.motor_type) { case X74_MOTOR: /* ? */ dev->shadow_regs[0xc4] = 0x20; dev->shadow_regs[0xc5] = 0x12; /* Movement direction & step size */ dev->shadow_regs[0xc6] = 0x09; dev->shadow_regs[0xc8] = 0x04; dev->shadow_regs[0xc9] = 0x39; dev->shadow_regs[0xca] = 0x0f; /* bounds of movement range0 */ dev->shadow_regs[0xe0] = 0x5d; dev->shadow_regs[0xe1] = 0x05; /* step size range0 */ dev->shadow_regs[0xe2] = 0xed; /* ? */ dev->shadow_regs[0xe3] = 0x02; /* bounds of movement range1 */ dev->shadow_regs[0xe4] = 0x29; dev->shadow_regs[0xe5] = 0x05; /* step size range1 */ dev->shadow_regs[0xe6] = 0x0d; /* bounds of movement range2 */ dev->shadow_regs[0xe7] = 0x00; dev->shadow_regs[0xe8] = 0x00; /* step size range2 */ dev->shadow_regs[0xe9] = 0x05; /* bounds of movement range3 */ dev->shadow_regs[0xea] = 0x00; dev->shadow_regs[0xeb] = 0x00; /* step size range3 */ dev->shadow_regs[0xec] = 0x01; /* bounds of movement range4 -only for 75dpi grayscale */ dev->shadow_regs[0xed] = 0x00; dev->shadow_regs[0xee] = 0x00; /* step size range4 */ dev->shadow_regs[0xef] = 0x01; break; case A920_MOTOR: case X1100_MOTOR: /* ? */ dev->shadow_regs[0xc5] = 0x17; /* Movement direction & step size */ dev->shadow_regs[0xc6] = 0x09; /* ? */ dev->shadow_regs[0xc9] = 0x3a; dev->shadow_regs[0xca] = 0x0a; /* bounds of movement range0 */ dev->shadow_regs[0xe0] = 0x75; dev->shadow_regs[0xe1] = 0x0a; /* step size range0 */ dev->shadow_regs[0xe2] = 0xdd; /* ? */ dev->shadow_regs[0xe3] = 0x05; /* bounds of movement range1 */ dev->shadow_regs[0xe4] = 0x59; dev->shadow_regs[0xe5] = 0x0a; /* step size range1 */ dev->shadow_regs[0xe6] = 0x0e; /* bounds of movement range2 */ dev->shadow_regs[0xe7] = 0x00; dev->shadow_regs[0xe8] = 0x00; /* step size range2 */ dev->shadow_regs[0xe9] = 0x05; /* bounds of movement range3 */ dev->shadow_regs[0xea] = 0x00; dev->shadow_regs[0xeb] = 0x00; /* step size range3 */ dev->shadow_regs[0xec] = 0x01; /* step size range4 */ dev->shadow_regs[0xef] = 0x01; break; } dev->shadow_regs[0x35] = 0x01; dev->shadow_regs[0x37] = 0x01; /* set colour scan */ dev->shadow_regs[0x2f] = 0x11; /* Motor enable & Coordinate space denominator */ dev->shadow_regs[0xc3] = 0x83; } else /* greyscale */ { switch (dev->model.sensor_type) { case X74_SENSOR: dev->shadow_regs[0x34] = 0x04; dev->shadow_regs[0x35] = 0x04; dev->shadow_regs[0x36] = 0x08; dev->shadow_regs[0x37] = 0x08; dev->shadow_regs[0x38] = 0x0c; dev->shadow_regs[0x85] = 0x00; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0x00; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x91] = 0x00; dev->shadow_regs[0x92] = 0x00; dev->shadow_regs[0x93] = 0x06; break; case X1100_B2_SENSOR: dev->shadow_regs[0x34] = 0x08; dev->shadow_regs[0x35] = 0x08; dev->shadow_regs[0x36] = 0x0f; dev->shadow_regs[0x37] = 0x0f; dev->shadow_regs[0x38] = 0x16; dev->shadow_regs[0x85] = 0x00; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0x00; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x91] = 0x00; dev->shadow_regs[0x92] = 0x00; dev->shadow_regs[0x93] = 0x06; break; case X1100_2C_SENSOR: dev->shadow_regs[0x34] = 0x04; dev->shadow_regs[0x35] = 0x04; dev->shadow_regs[0x36] = 0x07; dev->shadow_regs[0x37] = 0x07; dev->shadow_regs[0x38] = 0x0a; dev->shadow_regs[0x85] = 0x20; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0x00; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x91] = 0x00; dev->shadow_regs[0x92] = 0x00; dev->shadow_regs[0x93] = 0x06; break; case A920_SENSOR: dev->shadow_regs[0x34] = 0x03; dev->shadow_regs[0x35] = 0x03; dev->shadow_regs[0x36] = 0x06; dev->shadow_regs[0x37] = 0x06; dev->shadow_regs[0x38] = 0x09; dev->shadow_regs[0x85] = 0x05; dev->shadow_regs[0x86] = 0x14; dev->shadow_regs[0x87] = 0x06; dev->shadow_regs[0x88] = 0x04; dev->shadow_regs[0x91] = 0xe0; dev->shadow_regs[0x92] = 0x85; dev->shadow_regs[0x93] = 0x0e; break; case X1200_SENSOR: dev->shadow_regs[0x34] = 0x02; dev->shadow_regs[0x35] = 0x02; dev->shadow_regs[0x36] = 0x04; dev->shadow_regs[0x37] = 0x04; dev->shadow_regs[0x38] = 0x06; break; case X1200_USB2_SENSOR: dev->shadow_regs[0x34] = 0x02; dev->shadow_regs[0x35] = 0x02; dev->shadow_regs[0x36] = 0x04; dev->shadow_regs[0x37] = 0x04; dev->shadow_regs[0x38] = 0x06; break; } switch (dev->model.motor_type) { case X74_MOTOR: /* ? */ dev->shadow_regs[0xc4] = 0x20; dev->shadow_regs[0xc5] = 0x1c; /* Movement direction & step size */ dev->shadow_regs[0xc6] = 0x0b; dev->shadow_regs[0xc8] = 0x04; dev->shadow_regs[0xc9] = 0x39; dev->shadow_regs[0xca] = 0x05; /* bounds of movement range0 */ dev->shadow_regs[0xe0] = 0x29; dev->shadow_regs[0xe1] = 0x17; /* step size range0 */ dev->shadow_regs[0xe2] = 0x8f; /* ? */ dev->shadow_regs[0xe3] = 0x06; /* bounds of movement range1 */ dev->shadow_regs[0xe4] = 0x61; dev->shadow_regs[0xe5] = 0x16; /* step size range1 */ dev->shadow_regs[0xe6] = 0x64; /* bounds of movement range2 */ dev->shadow_regs[0xe7] = 0xb5; dev->shadow_regs[0xe8] = 0x08; /* step size range2 */ dev->shadow_regs[0xe9] = 0x32; /* bounds of movement range3 */ dev->shadow_regs[0xea] = 0x00; dev->shadow_regs[0xeb] = 0x00; /* step size range3 */ dev->shadow_regs[0xec] = 0x0c; /* bounds of movement range4 -only for 75dpi grayscale */ dev->shadow_regs[0xed] = 0x00; dev->shadow_regs[0xee] = 0x00; /* step size range4 */ dev->shadow_regs[0xef] = 0x08; break; case A920_MOTOR: case X1100_MOTOR: /* ? */ dev->shadow_regs[0xc5] = 0x19; /* Movement direction & step size */ dev->shadow_regs[0xc6] = 0x09; /* ? */ dev->shadow_regs[0xc9] = 0x3a; dev->shadow_regs[0xca] = 0x08; /* bounds of movement range0 */ dev->shadow_regs[0xe0] = 0xe3; dev->shadow_regs[0xe1] = 0x18; /* step size range0 */ dev->shadow_regs[0xe2] = 0x03; /* ? */ dev->shadow_regs[0xe3] = 0x06; /* bounds of movement range1 */ dev->shadow_regs[0xe4] = 0x2b; dev->shadow_regs[0xe5] = 0x17; /* step size range1 */ dev->shadow_regs[0xe6] = 0xdc; /* bounds of movement range2 */ dev->shadow_regs[0xe7] = 0xb3; dev->shadow_regs[0xe8] = 0x07; /* step size range2 */ dev->shadow_regs[0xe9] = 0x1b; /* bounds of movement range3 */ dev->shadow_regs[0xea] = 0x00; dev->shadow_regs[0xeb] = 0x00; /* step size range3 */ dev->shadow_regs[0xec] = 0x07; /* step size range4 */ dev->shadow_regs[0xef] = 0x03; break; } /* switch motortype */ /* set grayscale scan */ dev->shadow_regs[0x2f] = 0x21; /* set motor resolution divisor */ dev->shadow_regs[0x39] = 0x03; /* set ? only for colour? */ dev->shadow_regs[0x80] = 0x00; dev->shadow_regs[0x81] = 0x00; dev->shadow_regs[0x82] = 0x00; /* Motor enable & Coordinate space denominator */ dev->shadow_regs[0xc3] = 0x81; } /* else (gray) */ /* set # of head moves per CIS read */ rts88xx_set_scan_frequency (dev->shadow_regs, 1); /* set horizontal resolution */ dev->shadow_regs[0x79] = 0x20; } /* 600dpi x 600dpi */ if (resolution == 600) { DBG (5, "sanei_lexmark_low_set_scan_regs(): 600 DPI resolution\n"); if (isColourScan) { /* 600 dpi color doesn't work for X74 yet */ if (dev->model.sensor_type == X74_SENSOR) return SANE_STATUS_INVAL; switch (dev->model.sensor_type) { case X74_SENSOR: dev->shadow_regs[0x34] = 0x10; dev->shadow_regs[0x35] = 0x01; dev->shadow_regs[0x36] = 0x0c; dev->shadow_regs[0x37] = 0x01; dev->shadow_regs[0x38] = 0x09; dev->shadow_regs[0x80] = 0x02; dev->shadow_regs[0x81] = 0x08; dev->shadow_regs[0x82] = 0x08; dev->shadow_regs[0x85] = 0x00; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0x00; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x91] = 0x00; dev->shadow_regs[0x92] = 0x00; dev->shadow_regs[0x93] = 0x06; break; /*dev->shadow_regs[0x34] = 0x08; dev->shadow_regs[0x35] = 0x01; dev->shadow_regs[0x36] = 0x06; dev->shadow_regs[0x37] = 0x01; dev->shadow_regs[0x38] = 0x05; dev->shadow_regs[0x80] = 0x09; dev->shadow_regs[0x81] = 0x0c; dev->shadow_regs[0x82] = 0x04; dev->shadow_regs[0x85] = 0x00; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0x00; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x91] = 0x00; dev->shadow_regs[0x92] = 0x00; dev->shadow_regs[0x93] = 0x06; break; */ case X1100_B2_SENSOR: dev->shadow_regs[0x34] = 0x15; dev->shadow_regs[0x36] = 0x15; dev->shadow_regs[0x38] = 0x14; dev->shadow_regs[0x80] = 0x02; dev->shadow_regs[0x81] = 0x02; dev->shadow_regs[0x82] = 0x08; dev->shadow_regs[0x85] = 0x83; dev->shadow_regs[0x86] = 0x7e; dev->shadow_regs[0x87] = 0xad; dev->shadow_regs[0x88] = 0x35; dev->shadow_regs[0x91] = 0xfe; dev->shadow_regs[0x92] = 0xdf; dev->shadow_regs[0x93] = 0x0e; break; case X1100_2C_SENSOR: dev->shadow_regs[0x34] = 0x08; dev->shadow_regs[0x36] = 0x0d; dev->shadow_regs[0x38] = 0x09; dev->shadow_regs[0x80] = 0x0e; dev->shadow_regs[0x81] = 0x02; dev->shadow_regs[0x82] = 0x0a; dev->shadow_regs[0x85] = 0x20; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0x00; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x91] = 0x00; dev->shadow_regs[0x92] = 0x00; dev->shadow_regs[0x93] = 0x06; break; case A920_SENSOR: dev->shadow_regs[0x34] = 0x06; dev->shadow_regs[0x36] = 0x0f; dev->shadow_regs[0x38] = 0x09; dev->shadow_regs[0x79] = 0x40; dev->shadow_regs[0x80] = 0x0e; dev->shadow_regs[0x81] = 0x0e; dev->shadow_regs[0x82] = 0x00; dev->shadow_regs[0x85] = 0x05; dev->shadow_regs[0x86] = 0x14; dev->shadow_regs[0x87] = 0x06; dev->shadow_regs[0x88] = 0x04; dev->shadow_regs[0x91] = 0x60; dev->shadow_regs[0x92] = 0x85; dev->shadow_regs[0x93] = 0x0e; break; case X1200_SENSOR: dev->shadow_regs[0x34] = 0x07; dev->shadow_regs[0x36] = 0x0a; dev->shadow_regs[0x38] = 0x04; /* data compression dev->shadow_regs[0x40] = 0x90; dev->shadow_regs[0x50] = 0x20; */ /* no data compression */ dev->shadow_regs[0x40] = 0x80; dev->shadow_regs[0x50] = 0x00; dev->shadow_regs[0x80] = 0x02; dev->shadow_regs[0x81] = 0x00; dev->shadow_regs[0x82] = 0x06; break; case X1200_USB2_SENSOR: dev->shadow_regs[0x34] = 0x0d; dev->shadow_regs[0x36] = 0x13; dev->shadow_regs[0x38] = 0x10; dev->shadow_regs[0x80] = 0x04; dev->shadow_regs[0x81] = 0x0e; dev->shadow_regs[0x82] = 0x08; dev->shadow_regs[0x85] = 0x02; dev->shadow_regs[0x86] = 0x3b; dev->shadow_regs[0x87] = 0x0f; dev->shadow_regs[0x88] = 0x24; dev->shadow_regs[0x91] = 0x19; dev->shadow_regs[0x92] = 0x30; dev->shadow_regs[0x93] = 0x0e; dev->shadow_regs[0xc5] = 0x17; dev->shadow_regs[0xc6] = 0x09; dev->shadow_regs[0xca] = 0x0a; break; } switch (dev->model.motor_type) { case X74_MOTOR: /* Motor enable & Coordinate space denominator */ dev->shadow_regs[0xc3] = 0x81; /* ? */ dev->shadow_regs[0xc4] = 0x20; dev->shadow_regs[0xc5] = 0x21; /* Movement direction & step size */ dev->shadow_regs[0xc6] = 0x09; dev->shadow_regs[0xc8] = 0x04; dev->shadow_regs[0xc9] = 0x39; dev->shadow_regs[0xca] = 0x20; /* bounds of movement range0 */ dev->shadow_regs[0xe0] = 0x00; dev->shadow_regs[0xe1] = 0x00; /* step size range0 */ dev->shadow_regs[0xe2] = 0xbf; /* ? */ dev->shadow_regs[0xe3] = 0x05; /* bounds of movement range1 */ dev->shadow_regs[0xe4] = 0x00; dev->shadow_regs[0xe5] = 0x00; /* step size range1 */ dev->shadow_regs[0xe6] = 0x0d; /* bounds of movement range2 */ dev->shadow_regs[0xe7] = 0x00; dev->shadow_regs[0xe8] = 0x00; /* step size range2 */ dev->shadow_regs[0xe9] = 0x05; /* bounds of movement range3 */ dev->shadow_regs[0xea] = 0x00; dev->shadow_regs[0xeb] = 0x00; /* step size range3 */ dev->shadow_regs[0xec] = 0x01; /* bounds of movement range4 -only for 75dpi grayscale */ dev->shadow_regs[0xed] = 0x00; dev->shadow_regs[0xee] = 0x00; /* step size range4 */ dev->shadow_regs[0xef] = 0x01; break; case A920_MOTOR: case X1100_MOTOR: /* Motor enable & Coordinate space denominator */ dev->shadow_regs[0xc3] = 0x86; /* ? */ dev->shadow_regs[0xc5] = 0x27; /* Movement direction & step size */ dev->shadow_regs[0xc6] = 0x0c; /* ? */ dev->shadow_regs[0xc9] = 0x3a; dev->shadow_regs[0xca] = 0x1a; /* bounds of movement range0 */ dev->shadow_regs[0xe0] = 0x57; dev->shadow_regs[0xe1] = 0x0a; /* step size range0 */ dev->shadow_regs[0xe2] = 0xbf; /* ? */ dev->shadow_regs[0xe3] = 0x05; /* bounds of movement range1 */ dev->shadow_regs[0xe4] = 0x3b; dev->shadow_regs[0xe5] = 0x0a; /* step size range1 */ dev->shadow_regs[0xe6] = 0x0e; /* bounds of movement range2 */ dev->shadow_regs[0xe7] = 0x00; dev->shadow_regs[0xe8] = 0x00; /* step size range2 */ dev->shadow_regs[0xe9] = 0x05; /* bounds of movement range3 */ dev->shadow_regs[0xea] = 0x00; dev->shadow_regs[0xeb] = 0x00; /* step size range3 */ dev->shadow_regs[0xec] = 0x01; /* step size range4 */ dev->shadow_regs[0xef] = 0x01; break; } /* set colour scan */ dev->shadow_regs[0x2f] = 0x11; dev->shadow_regs[0x35] = 0x01; dev->shadow_regs[0x37] = 0x01; /* set motor resolution divisor */ dev->shadow_regs[0x39] = 0x03; /* set # of head moves per CIS read */ rts88xx_set_scan_frequency (dev->shadow_regs, 2); } else { switch (dev->model.sensor_type) { case X74_SENSOR: dev->shadow_regs[0x2c] = 0x04; dev->shadow_regs[0x2d] = 0x46; dev->shadow_regs[0x34] = 0x05; dev->shadow_regs[0x35] = 0x05; dev->shadow_regs[0x36] = 0x0b; dev->shadow_regs[0x37] = 0x0b; dev->shadow_regs[0x38] = 0x11; dev->shadow_regs[0x40] = 0x40; dev->shadow_regs[0x85] = 0x00; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0x00; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x91] = 0x00; dev->shadow_regs[0x92] = 0x00; dev->shadow_regs[0x93] = 0x06; break; case X1100_B2_SENSOR: dev->shadow_regs[0x34] = 0x11; dev->shadow_regs[0x35] = 0x11; dev->shadow_regs[0x36] = 0x21; dev->shadow_regs[0x37] = 0x21; dev->shadow_regs[0x38] = 0x31; dev->shadow_regs[0x85] = 0x00; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0x00; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x91] = 0x00; dev->shadow_regs[0x92] = 0x00; dev->shadow_regs[0x93] = 0x06; break; case X1100_2C_SENSOR: dev->shadow_regs[0x34] = 0x07; dev->shadow_regs[0x35] = 0x07; dev->shadow_regs[0x36] = 0x0d; dev->shadow_regs[0x37] = 0x0d; dev->shadow_regs[0x38] = 0x13; dev->shadow_regs[0x85] = 0x20; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0x00; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x91] = 0x00; dev->shadow_regs[0x92] = 0x00; dev->shadow_regs[0x93] = 0x06; break; case A920_SENSOR: dev->shadow_regs[0x34] = 0x05; dev->shadow_regs[0x35] = 0x05; dev->shadow_regs[0x36] = 0x0b; dev->shadow_regs[0x37] = 0x0b; dev->shadow_regs[0x38] = 0x11; dev->shadow_regs[0x85] = 0x05; dev->shadow_regs[0x86] = 0x14; dev->shadow_regs[0x87] = 0x06; dev->shadow_regs[0x88] = 0x04; dev->shadow_regs[0x91] = 0xe0; dev->shadow_regs[0x92] = 0x85; dev->shadow_regs[0x93] = 0x0e; break; case X1200_SENSOR: dev->shadow_regs[0x34] = 0x03; dev->shadow_regs[0x35] = 0x03; dev->shadow_regs[0x36] = 0x07; dev->shadow_regs[0x37] = 0x07; dev->shadow_regs[0x38] = 0x0b; /* data compression dev->shadow_regs[0x40] = 0x90; dev->shadow_regs[0x50] = 0x20; */ /* no data compression */ dev->shadow_regs[0x40] = 0x80; dev->shadow_regs[0x50] = 0x00; dev->shadow_regs[0x85] = 0x00; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0xff; dev->shadow_regs[0x88] = 0x02; dev->shadow_regs[0x92] = 0x00; break; case X1200_USB2_SENSOR: dev->shadow_regs[0x34] = 0x03; dev->shadow_regs[0x35] = 0x03; dev->shadow_regs[0x36] = 0x07; dev->shadow_regs[0x37] = 0x07; dev->shadow_regs[0x38] = 0x0b; dev->shadow_regs[0x40] = 0x80; dev->shadow_regs[0x50] = 0x00; dev->shadow_regs[0x85] = 0x00; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0xff; dev->shadow_regs[0x88] = 0x02; dev->shadow_regs[0x92] = 0x00; break; } switch (dev->model.motor_type) { case X74_MOTOR: /* set # of head moves per CIS read */ rts88xx_set_scan_frequency (dev->shadow_regs, 1); /* ? */ dev->shadow_regs[0xc4] = 0x20; dev->shadow_regs[0xc5] = 0x22; /* Movement direction & step size */ dev->shadow_regs[0xc6] = 0x0b; dev->shadow_regs[0xc8] = 0x04; dev->shadow_regs[0xc9] = 0x39; dev->shadow_regs[0xca] = 0x1f; /* bounds of movement range0 */ dev->shadow_regs[0xe0] = 0x2f; dev->shadow_regs[0xe1] = 0x11; /* step size range0 */ dev->shadow_regs[0xe2] = 0x9f; /* ? */ dev->shadow_regs[0xe3] = 0x0f; /* bounds of movement range1 */ dev->shadow_regs[0xe4] = 0xcb; dev->shadow_regs[0xe5] = 0x10; /* step size range1 */ dev->shadow_regs[0xe6] = 0x64; /* bounds of movement range2 */ dev->shadow_regs[0xe7] = 0x00; dev->shadow_regs[0xe8] = 0x00; /* step size range2 */ dev->shadow_regs[0xe9] = 0x32; /* bounds of movement range3 */ dev->shadow_regs[0xea] = 0x00; dev->shadow_regs[0xeb] = 0x00; /* step size range3 */ dev->shadow_regs[0xec] = 0x0c; /* bounds of movement range4 -only for 75dpi grayscale */ dev->shadow_regs[0xed] = 0x00; dev->shadow_regs[0xee] = 0x00; /* step size range4 */ dev->shadow_regs[0xef] = 0x08; break; case X1100_MOTOR: case A920_MOTOR: /* set ? only for colour? */ dev->shadow_regs[0x80] = 0x00; dev->shadow_regs[0x81] = 0x00; dev->shadow_regs[0x82] = 0x00; /* ? */ dev->shadow_regs[0xc5] = 0x22; /* Movement direction & step size */ dev->shadow_regs[0xc6] = 0x09; /* ? */ dev->shadow_regs[0xc9] = 0x3b; dev->shadow_regs[0xca] = 0x1f; /* bounds of movement range0 */ dev->shadow_regs[0xe0] = 0xf7; dev->shadow_regs[0xe1] = 0x16; /* step size range0 */ dev->shadow_regs[0xe2] = 0x87; /* ? */ dev->shadow_regs[0xe3] = 0x13; /* bounds of movement range1 */ dev->shadow_regs[0xe4] = 0x1b; dev->shadow_regs[0xe5] = 0x16; /* step size range1 */ dev->shadow_regs[0xe6] = 0xdc; /* bounds of movement range2 */ dev->shadow_regs[0xe7] = 0x00; dev->shadow_regs[0xe8] = 0x00; /* step size range2 */ dev->shadow_regs[0xe9] = 0x1b; /* bounds of movement range3 */ dev->shadow_regs[0xea] = 0x00; dev->shadow_regs[0xeb] = 0x00; /* step size range3 */ dev->shadow_regs[0xec] = 0x07; /* step size range4 */ dev->shadow_regs[0xef] = 0x03; break; } /* set grayscale scan */ dev->shadow_regs[0x2f] = 0x21; /* set motor resolution divisor */ dev->shadow_regs[0x39] = 0x01; /* set # of head moves per CIS read */ rts88xx_set_scan_frequency (dev->shadow_regs, 1); /* Motor enable & Coordinate space denominator */ dev->shadow_regs[0xc3] = 0x81; } /* else (grayscale) */ /* set horizontal resolution */ dev->shadow_regs[0x79] = 0x40; } /*600dpi x 1200dpi */ if (resolution == 1200) { DBG (5, "sanei_lexmark_low_set_scan_regs(): 1200 DPI resolution\n"); /* 1200 dpi doesn't work for X74 yet */ if (dev->model.sensor_type == X74_SENSOR) return SANE_STATUS_INVAL; if (isColourScan) { /* set colour scan */ dev->shadow_regs[0x2f] = 0x11; /* set motor resolution divisor */ dev->shadow_regs[0x39] = 0x01; /* set # of head moves per CIS read */ rts88xx_set_scan_frequency (dev->shadow_regs, 2); if (dev->model.sensor_type == X1100_B2_SENSOR) { /* set ? */ dev->shadow_regs[0x34] = 0x29; dev->shadow_regs[0x36] = 0x29; dev->shadow_regs[0x38] = 0x28; /* set ? */ dev->shadow_regs[0x80] = 0x04; dev->shadow_regs[0x81] = 0x04; dev->shadow_regs[0x82] = 0x08; dev->shadow_regs[0x85] = 0x83; dev->shadow_regs[0x86] = 0x7e; dev->shadow_regs[0x87] = 0xad; dev->shadow_regs[0x88] = 0x35; dev->shadow_regs[0x91] = 0xfe; dev->shadow_regs[0x92] = 0xdf; } else { /* A920 case */ dev->shadow_regs[0x34] = 0x0c; dev->shadow_regs[0x36] = 0x1e; dev->shadow_regs[0x38] = 0x10; dev->shadow_regs[0x80] = 0x0c; dev->shadow_regs[0x81] = 0x08; dev->shadow_regs[0x82] = 0x0c; dev->shadow_regs[0x85] = 0x05; dev->shadow_regs[0x86] = 0x14; dev->shadow_regs[0x87] = 0x06; dev->shadow_regs[0x88] = 0x04; dev->shadow_regs[0x91] = 0x60; dev->shadow_regs[0x92] = 0x85; } dev->shadow_regs[0x35] = 0x01; dev->shadow_regs[0x37] = 0x01; /* set motor resolution divisor */ dev->shadow_regs[0x39] = 0x01; dev->shadow_regs[0x93] = 0x0e; /* Motor enable & Coordinate space denominator */ dev->shadow_regs[0xc3] = 0x86; /* ? */ dev->shadow_regs[0xc5] = 0x41; /* Movement direction & step size */ dev->shadow_regs[0xc6] = 0x0c; /* ? */ dev->shadow_regs[0xc9] = 0x3a; dev->shadow_regs[0xca] = 0x40; /* bounds of movement range0 */ dev->shadow_regs[0xe0] = 0x00; dev->shadow_regs[0xe1] = 0x00; /* step size range0 */ dev->shadow_regs[0xe2] = 0x85; /* ? */ dev->shadow_regs[0xe3] = 0x0b; /* bounds of movement range1 */ dev->shadow_regs[0xe4] = 0x00; dev->shadow_regs[0xe5] = 0x00; /* step size range1 */ dev->shadow_regs[0xe6] = 0x0e; /* bounds of movement range2 */ dev->shadow_regs[0xe7] = 0x00; dev->shadow_regs[0xe8] = 0x00; /* step size range2 */ dev->shadow_regs[0xe9] = 0x05; /* bounds of movement range3 */ dev->shadow_regs[0xea] = 0x00; dev->shadow_regs[0xeb] = 0x00; /* step size range3 */ dev->shadow_regs[0xec] = 0x01; /* step size range4 */ dev->shadow_regs[0xef] = 0x01; } else { /* set grayscale scan */ dev->shadow_regs[0x2f] = 0x21; /* set ? */ dev->shadow_regs[0x34] = 0x22; dev->shadow_regs[0x35] = 0x22; dev->shadow_regs[0x36] = 0x42; dev->shadow_regs[0x37] = 0x42; dev->shadow_regs[0x38] = 0x62; /* set motor resolution divisor */ dev->shadow_regs[0x39] = 0x01; /* set # of head moves per CIS read */ rts88xx_set_scan_frequency (dev->shadow_regs, 0); /* set ? only for colour? */ dev->shadow_regs[0x80] = 0x00; dev->shadow_regs[0x81] = 0x00; dev->shadow_regs[0x82] = 0x00; dev->shadow_regs[0x85] = 0x00; dev->shadow_regs[0x86] = 0x00; dev->shadow_regs[0x87] = 0x00; dev->shadow_regs[0x88] = 0x00; dev->shadow_regs[0x91] = 0x00; dev->shadow_regs[0x92] = 0x00; dev->shadow_regs[0x93] = 0x06; /* Motor enable & Coordinate space denominator */ dev->shadow_regs[0xc3] = 0x81; /* ? */ dev->shadow_regs[0xc5] = 0x41; /* Movement direction & step size */ dev->shadow_regs[0xc6] = 0x09; /* ? */ dev->shadow_regs[0xc9] = 0x3a; dev->shadow_regs[0xca] = 0x40; /* bounds of movement range0 */ dev->shadow_regs[0xe0] = 0x00; dev->shadow_regs[0xe1] = 0x00; /* step size range0 */ dev->shadow_regs[0xe2] = 0xc7; /* ? */ dev->shadow_regs[0xe3] = 0x29; /* bounds of movement range1 */ dev->shadow_regs[0xe4] = 0x00; dev->shadow_regs[0xe5] = 0x00; /* step size range1 */ dev->shadow_regs[0xe6] = 0xdc; /* bounds of movement range2 */ dev->shadow_regs[0xe7] = 0x00; dev->shadow_regs[0xe8] = 0x00; /* step size range2 */ dev->shadow_regs[0xe9] = 0x1b; /* bounds of movement range3 */ dev->shadow_regs[0xea] = 0x00; dev->shadow_regs[0xeb] = 0x00; /* step size range3 */ dev->shadow_regs[0xec] = 0x07; /* step size range4 */ dev->shadow_regs[0xef] = 0x03; } /* set horizontal resolution */ dev->shadow_regs[0x79] = 0x40; } /* is calibration has been done, we override fixed settings with detected ones */ if (calibrated) { /* override fixed values with ones from calibration */ if (rts88xx_is_color (dev->shadow_regs)) { rts88xx_set_offset (dev->shadow_regs, dev->offset.red, dev->offset.green, dev->offset.blue); rts88xx_set_gain (dev->shadow_regs, dev->gain.red, dev->gain.green, dev->gain.blue); } else { rts88xx_set_offset (dev->shadow_regs, dev->offset.gray, dev->offset.gray, dev->offset.gray); rts88xx_set_gain (dev->shadow_regs, dev->gain.gray, dev->gain.gray, dev->gain.gray); } } DBG (2, "sanei_lexmark_low_set_scan_regs: end.\n"); return SANE_STATUS_GOOD; } SANE_Status sanei_lexmark_low_start_scan (Lexmark_Device * dev) { SANE_Int devnum; static SANE_Byte command4_block[] = { 0x90, 0x00, 0x00, 0x03 }; static SANE_Byte command5_block[] = { 0x80, 0xb3, 0x00, 0x01 }; SANE_Byte poll_result[3]; SANE_Byte read_result; SANE_Bool scan_head_moving; size_t size; devnum = dev->devnum; dev->transfer_buffer = NULL; /* No data xferred yet */ DBG (2, "sanei_lexmark_low_start_scan:\n"); /* 80 b3 00 01 - poll for scanner not moving */ scan_head_moving = SANE_TRUE; while (scan_head_moving) { size = 4; low_usb_bulk_write (devnum, command5_block, &size); size = 0x1; low_usb_bulk_read (devnum, &read_result, &size); if ((read_result & 0xF) == 0x0) { scan_head_moving = SANE_FALSE; } /* F.O. Should be a timeout here so we don't hang if something breaks */ #ifdef FAKE_USB scan_head_moving = SANE_FALSE; #endif } /* Clear C6 */ low_clr_c6 (devnum); /* Stop the scanner */ low_stop_mvmt (devnum); /*Set regs x2 */ dev->shadow_regs[0x32] = 0x00; low_write_all_regs (devnum, dev->shadow_regs); dev->shadow_regs[0x32] = 0x40; low_write_all_regs (devnum, dev->shadow_regs); /* Start Scan */ rts88xx_commit (devnum, dev->shadow_regs[0x2c]); /* We start with 0 bytes remaining to be read */ dev->bytes_remaining = 0; /* and 0 bytes in the transfer buffer */ dev->bytes_in_buffer = 0; dev->bytes_read = 0; /* Poll the available byte count until not 0 */ while (1) { size = 4; low_usb_bulk_write (devnum, command4_block, &size); size = 0x3; low_usb_bulk_read (devnum, poll_result, &size); if (! (poll_result[0] == 0 && poll_result[1] == 0 && poll_result[2] == 0)) { /* if result != 00 00 00 we got data */ /* data_size should be used to set bytes_remaining */ /* data_size is set from sane_get_parameters () */ dev->bytes_remaining = dev->data_size; /* Initialize the read buffer */ read_buffer_init (dev, dev->params.bytes_per_line); return SANE_STATUS_GOOD; } size = 4; /* I'm not sure why the Windows driver does this - probably a timeout? */ low_usb_bulk_write (devnum, command5_block, &size); size = 0x1; low_usb_bulk_read (devnum, &read_result, &size); if (read_result != 0x68) { dev->bytes_remaining = 0; return SANE_STATUS_IO_ERROR; } } DBG (2, "sanei_lexmark_low_start_scan: end.\n"); return SANE_STATUS_GOOD; } long sanei_lexmark_low_read_scan_data (SANE_Byte * data, SANE_Int size, Lexmark_Device * dev) { SANE_Bool isColourScan, isGrayScan; static SANE_Byte command1_block[] = { 0x91, 0x00, 0xff, 0xc0 }; size_t cmd_size, xfer_request; long bytes_read; SANE_Bool even_byte; SANE_Status status; int i, k, val; DBG (2, "sanei_lexmark_low_read_scan_data:\n"); /* colour mode */ isGrayScan = SANE_FALSE; if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0) isColourScan = SANE_TRUE; else { isColourScan = SANE_FALSE; /* grayscale mode */ if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY) == 0) isGrayScan = SANE_TRUE; } /* Check if we have a transfer buffer. Create one and fill it if we don't */ if (dev->transfer_buffer == NULL) { if (dev->bytes_remaining > 0) { if (dev->bytes_remaining > MAX_XFER_SIZE) xfer_request = MAX_XFER_SIZE; else xfer_request = dev->bytes_remaining; command1_block[2] = (SANE_Byte) (xfer_request >> 8); command1_block[3] = (SANE_Byte) (xfer_request & 0xFF); /* wait for data */ status = low_poll_data (dev->devnum); if (status != SANE_STATUS_GOOD) { DBG (1, "sanei_lexmark_low_read_scan_data: time-out while waiting for data.\n"); return status; } /* Create buffer to hold the amount we will request */ dev->transfer_buffer = (SANE_Byte *) malloc (MAX_XFER_SIZE); if (dev->transfer_buffer == NULL) return SANE_STATUS_NO_MEM; /* Fill it */ /* Write: 91 00 (xfer_size) */ cmd_size = 4; low_usb_bulk_write (dev->devnum, command1_block, &cmd_size); /* Read: xfer_size bytes */ cmd_size = xfer_request; low_usb_bulk_read (dev->devnum, dev->transfer_buffer, &cmd_size); /* apply shading coefficients */ k = dev->bytes_read % dev->read_buffer->linesize; for (i = 0; i < (int) cmd_size; i++) { val = dev->transfer_buffer[i]; val = (int) ((float) val * dev->shading_coeff[k] + 0.5); if (val > 255) val = 255; dev->transfer_buffer[i] = val; k++; if ((size_t) k == dev->read_buffer->linesize) k = 0; } /* advance by the amount actually read from device */ dev->bytes_read += cmd_size; dev->bytes_remaining -= cmd_size; dev->bytes_in_buffer = cmd_size; dev->read_pointer = dev->transfer_buffer; DBG (2, "sanei_lexmark_low_read_scan_data:\n"); DBG (2, " Filled a buffer from the scanner\n"); DBG (2, " bytes_remaining: %lu\n", (u_long) dev->bytes_remaining); DBG (2, " bytes_in_buffer: %lu\n", (u_long) dev->bytes_in_buffer); DBG (2, " read_pointer: %p\n", (void *) dev->read_pointer); } } DBG (5, "READ BUFFER INFO: \n"); DBG (5, " write ptr: %p\n", (void *) dev->read_buffer->writeptr); DBG (5, " read ptr: %p\n", (void *) dev->read_buffer->readptr); DBG (5, " max write ptr: %p\n", (void *) dev->read_buffer->max_writeptr); DBG (5, " buffer size: %lu\n", (u_long) dev->read_buffer->size); DBG (5, " line size: %lu\n", (u_long) dev->read_buffer->linesize); DBG (5, " empty: %d\n", dev->read_buffer->empty); DBG (5, " line no: %d\n", dev->read_buffer->image_line_no); /* If there is space in the read buffer, copy the transfer buffer over */ if (read_buffer_bytes_available (dev->read_buffer) >= dev->bytes_in_buffer) { even_byte = SANE_TRUE; while (dev->bytes_in_buffer) { /* Colour Scan */ if (isColourScan) { if (even_byte) read_buffer_add_byte (dev->read_buffer, dev->read_pointer + 1); else read_buffer_add_byte (dev->read_buffer, dev->read_pointer - 1); even_byte = !even_byte; } /* Gray Scan */ else if (isGrayScan) { if (even_byte) read_buffer_add_byte_gray (dev->read_buffer, dev->read_pointer + 1); else read_buffer_add_byte_gray (dev->read_buffer, dev->read_pointer - 1); even_byte = !even_byte; } /* Lineart Scan */ else { if (even_byte) read_buffer_add_bit_lineart (dev->read_buffer, dev->read_pointer + 1, dev->threshold); else read_buffer_add_bit_lineart (dev->read_buffer, dev->read_pointer - 1, dev->threshold); even_byte = !even_byte; } dev->read_pointer = dev->read_pointer + sizeof (SANE_Byte); dev->bytes_in_buffer--; } /* free the transfer buffer */ free (dev->transfer_buffer); dev->transfer_buffer = NULL; } DBG (5, "READ BUFFER INFO: \n"); DBG (5, " write ptr: %p\n", (void *) dev->read_buffer->writeptr); DBG (5, " read ptr: %p\n", (void *) dev->read_buffer->readptr); DBG (5, " max write ptr: %p\n", (void *) dev->read_buffer->max_writeptr); DBG (5, " buffer size: %lu\n", (u_long) dev->read_buffer->size); DBG (5, " line size: %lu\n", (u_long) dev->read_buffer->linesize); DBG (5, " empty: %d\n", dev->read_buffer->empty); DBG (5, " line no: %d\n", dev->read_buffer->image_line_no); /* Read blocks out of read buffer */ bytes_read = read_buffer_get_bytes (dev->read_buffer, data, size); DBG (2, "sanei_lexmark_low_read_scan_data:\n"); DBG (2, " Copying lines from buffer to data\n"); DBG (2, " bytes_remaining: %lu\n", (u_long) dev->bytes_remaining); DBG (2, " bytes_in_buffer: %lu\n", (u_long) dev->bytes_in_buffer); DBG (2, " read_pointer: %p\n", (void *) dev->read_buffer->readptr); DBG (2, " bytes_read %lu\n", (u_long) bytes_read); /* if no more bytes to xfer and read buffer empty we're at the end */ if ((dev->bytes_remaining == 0) && read_buffer_is_empty (dev->read_buffer)) { if (!dev->eof) { DBG (2, "sanei_lexmark_low_read_scan_data: EOF- parking the scanner\n"); dev->eof = SANE_TRUE; low_rewind (dev, dev->shadow_regs); } else { DBG (2, "ERROR: Why are we trying to set eof more than once?\n"); } } DBG (2, "sanei_lexmark_low_read_scan_data: end.\n"); return bytes_read; } void low_rewind (Lexmark_Device * dev, SANE_Byte * regs) { SANE_Int new_location; SANE_Int location; SANE_Int scale; DBG (2, "low_rewind: \n"); /* We rewind at 1200dpi resolution. We rely on content of shadow registers to compute the number of lines at 1200 dpi to go back */ /* first move to start of scanning area */ scale = 600 / dev->val[OPT_RESOLUTION].w; new_location = ((dev->val[OPT_BR_Y].w / scale) * scale) * 2; /* then add distance to go to the "origin dot" */ if (rts88xx_is_color (regs)) new_location += 400; else new_location += 420; if (dev->model.sensor_type == X74_SENSOR) new_location += 150; location = new_location - 1; DBG (2, "low_rewind: %d=>new_location=%d\n", dev->val[OPT_BR_Y].w, new_location); /* stops any pending scan */ low_clr_c6 (dev->devnum); low_cancel (dev->devnum); /* set regs for rewind */ regs[0x2f] = 0xa1; regs[0x32] = 0x00; regs[0x39] = 0x00; /* all other regs are always the same. these ones change with parameters */ /* the following 4 regs are the location 61,60 and the location+1 63,62 */ regs[0x60] = LOBYTE (location); regs[0x61] = HIBYTE (location); regs[0x62] = LOBYTE (new_location); regs[0x63] = HIBYTE (new_location); switch (dev->model.motor_type) { case X74_MOTOR: regs[0xc3] = 0x81; regs[0xc6] = 0x03; regs[0xc9] = 0x39; regs[0xe0] = 0x81; regs[0xe1] = 0x16; regs[0xe2] = 0xe1; regs[0xe3] = 0x04; regs[0xe4] = 0xe7; regs[0xe5] = 0x14; regs[0xe6] = 0x64; regs[0xe7] = 0xd5; regs[0xe8] = 0x08; regs[0xe9] = 0x32; regs[0xea] = 0xed; regs[0xeb] = 0x04; regs[0xec] = 0x0c; regs[0xef] = 0x08; break; case X1100_MOTOR: case A920_MOTOR: /* set regs for rewind */ regs[0x79] = 0x40; regs[0xb2] = 0x04; regs[0xc3] = 0x81; regs[0xc6] = 0x01; regs[0xc9] = 0x3b; regs[0xe0] = 0x2b; regs[0xe1] = 0x17; regs[0xe2] = 0xe7; regs[0xe3] = 0x03; regs[0xe6] = 0xdc; regs[0xe7] = 0xb3; regs[0xe8] = 0x07; regs[0xe9] = 0x1b; regs[0xea] = 0x00; regs[0xeb] = 0x00; regs[0xec] = 0x07; regs[0xef] = 0x03; break; } /* starts scan */ low_start_scan (dev->devnum, regs); DBG (2, "low_rewind: end.\n"); } SANE_Status read_buffer_init (Lexmark_Device * dev, int bytesperline) { size_t no_lines_in_buffer; DBG (2, "read_buffer_init: Start\n"); dev->read_buffer = (Read_Buffer *) malloc (sizeof (Read_Buffer)); if (dev->read_buffer == NULL) return SANE_STATUS_NO_MEM; dev->read_buffer->linesize = bytesperline; dev->read_buffer->gray_offset = 0; dev->read_buffer->max_gray_offset = bytesperline - 1; dev->read_buffer->region = RED; dev->read_buffer->red_offset = 0; dev->read_buffer->green_offset = 1; dev->read_buffer->blue_offset = 2; dev->read_buffer->max_red_offset = bytesperline - 3; dev->read_buffer->max_green_offset = bytesperline - 2; dev->read_buffer->max_blue_offset = bytesperline - 1; no_lines_in_buffer = 3 * MAX_XFER_SIZE / bytesperline; dev->read_buffer->size = bytesperline * no_lines_in_buffer; dev->read_buffer->data = (SANE_Byte *) malloc (dev->read_buffer->size); if (dev->read_buffer->data == NULL) return SANE_STATUS_NO_MEM; dev->read_buffer->readptr = dev->read_buffer->data; dev->read_buffer->writeptr = dev->read_buffer->data; dev->read_buffer->max_writeptr = dev->read_buffer->data + (no_lines_in_buffer - 1) * bytesperline; dev->read_buffer->empty = SANE_TRUE; dev->read_buffer->image_line_no = 0; dev->read_buffer->bit_counter = 0; dev->read_buffer->max_lineart_offset = dev->params.pixels_per_line - 1; return SANE_STATUS_GOOD; } SANE_Status read_buffer_free (Read_Buffer * read_buffer) { DBG (2, "read_buffer_free:\n"); if (read_buffer) { free (read_buffer->data); free (read_buffer); read_buffer = NULL; } return SANE_STATUS_GOOD; } size_t read_buffer_bytes_available (Read_Buffer * rb) { DBG (2, "read_buffer_bytes_available:\n"); if (rb->empty) return rb->size; else if (rb->writeptr < rb->readptr) return (size_t)(rb->readptr - rb->writeptr) < rb->linesize ? 0 : (size_t)(rb->readptr - rb->writeptr) - rb->linesize; else return (size_t)(rb->writeptr - rb->readptr) < rb->linesize ? 0 : rb->size - (size_t)(rb->writeptr - rb->readptr) - rb->linesize; } SANE_Status read_buffer_add_byte (Read_Buffer * rb, SANE_Byte * byte_pointer) { /* DBG(2, "read_buffer_add_byte:\n"); */ /* F.O. Need to fix the endian byte ordering here */ switch (rb->region) { case RED: *(rb->writeptr + rb->red_offset) = *byte_pointer; if (rb->red_offset == rb->max_red_offset) { rb->red_offset = 0; rb->region = GREEN; } else rb->red_offset = rb->red_offset + (3 * sizeof (SANE_Byte)); return SANE_STATUS_GOOD; case GREEN: *(rb->writeptr + rb->green_offset) = *byte_pointer; if (rb->green_offset == rb->max_green_offset) { rb->green_offset = 1; rb->region = BLUE; } else rb->green_offset = rb->green_offset + (3 * sizeof (SANE_Byte)); return SANE_STATUS_GOOD; case BLUE: *(rb->writeptr + rb->blue_offset) = *byte_pointer; if (rb->blue_offset == rb->max_blue_offset) { rb->image_line_no++; /* finished a line. read_buffer no longer empty */ rb->empty = SANE_FALSE; rb->blue_offset = 2; rb->region = RED; if (rb->writeptr == rb->max_writeptr) rb->writeptr = rb->data; /* back to beginning of buffer */ else rb->writeptr = rb->writeptr + rb->linesize; /* next line */ } else rb->blue_offset = rb->blue_offset + (3 * sizeof (SANE_Byte)); return SANE_STATUS_GOOD; } return SANE_STATUS_GOOD; } SANE_Status read_buffer_add_byte_gray (Read_Buffer * rb, SANE_Byte * byte_pointer) { /* DBG(2, "read_buffer_add_byte_gray:\n"); */ *(rb->writeptr + rb->gray_offset) = *byte_pointer; if (rb->gray_offset == rb->max_gray_offset) { rb->image_line_no++; /* finished a line. read_buffer no longer empty */ rb->empty = SANE_FALSE; rb->gray_offset = 0; if (rb->writeptr == rb->max_writeptr) rb->writeptr = rb->data; /* back to beginning of buffer */ else rb->writeptr = rb->writeptr + rb->linesize; /* next line */ } else rb->gray_offset = rb->gray_offset + (1 * sizeof (SANE_Byte)); return SANE_STATUS_GOOD; } SANE_Status read_buffer_add_bit_lineart (Read_Buffer * rb, SANE_Byte * byte_pointer, SANE_Byte threshold) { SANE_Byte tmpByte; SANE_Byte *currentBytePtr; SANE_Int bitIndex; /* DBG(2, "read_buffer_add_bit_lineart:\n"); */ /* threshold = 0x80; */ tmpByte = 0; /* Create a bit by comparing incoming byte to threshold */ if (*byte_pointer <= threshold) { tmpByte = 128; } /* Calculate the bit index in the current byte */ bitIndex = rb->bit_counter % 8; /* Move the bit to its correct position in the temporary byte */ tmpByte = tmpByte >> bitIndex; /* Get the pointer to the current byte */ currentBytePtr = rb->writeptr + rb->gray_offset; /* If this is the first write to this byte, clear the byte */ if (bitIndex == 0) *currentBytePtr = 0; /* Set the value of the bit in the current byte */ *currentBytePtr = *currentBytePtr | tmpByte; /* last bit in the line? */ if (rb->bit_counter == rb->max_lineart_offset) { /* Check if we're at the last byte of the line - error if not */ if (rb->gray_offset != rb->max_gray_offset) { DBG (5, "read_buffer_add_bit_lineart:\n"); DBG (5, " Last bit of line is not last byte.\n"); DBG (5, " Bit Index: %d, Byte Index: %d. \n", rb->bit_counter, rb->max_gray_offset); return SANE_STATUS_INVAL; } rb->image_line_no++; /* line finished read_buffer no longer empty */ rb->empty = SANE_FALSE; rb->gray_offset = 0; /* are we at the last line in the read buffer ? */ if (rb->writeptr == rb->max_writeptr) rb->writeptr = rb->data; /* back to beginning of buffer */ else rb->writeptr = rb->writeptr + rb->linesize; /* next line */ /* clear the bit counter */ rb->bit_counter = 0; } /* last bit in the byte? */ else if (bitIndex == 7) { /* Not at the end of the line, but byte done. Increment byte offset */ rb->gray_offset = rb->gray_offset + (1 * sizeof (SANE_Byte)); /* increment bit counter */ rb->bit_counter++; } else { /* else increment bit counter */ rb->bit_counter++; } return SANE_STATUS_GOOD; } size_t read_buffer_get_bytes (Read_Buffer * rb, SANE_Byte * buffer, size_t rqst_size) { /* Read_Buffer *rb; */ size_t available_bytes; /* rb = read_buffer; */ if (rb->empty) return 0; else if (rb->writeptr > rb->readptr) { available_bytes = rb->writeptr - rb->readptr; if (available_bytes <= rqst_size) { /* We can read from the read pointer up to the write pointer */ memcpy (buffer, rb->readptr, available_bytes); rb->readptr = rb->writeptr; rb->empty = SANE_TRUE; return available_bytes; } else { /* We can read from the full request size */ memcpy (buffer, rb->readptr, rqst_size); rb->readptr = rb->readptr + rqst_size; return rqst_size; } } else { /* The read pointer is ahead of the write pointer. Its wrapped around. */ /* We can read to the end of the buffer and make a recursive call to */ /* read any available lines at the beginning of the buffer */ available_bytes = rb->data + rb->size - rb->readptr; if (available_bytes <= rqst_size) { /* We can read from the read pointer up to the end of the buffer */ memcpy (buffer, rb->readptr, available_bytes); rb->readptr = rb->data; if (rb->writeptr == rb->readptr) rb->empty = SANE_TRUE; return available_bytes + read_buffer_get_bytes (rb, buffer + available_bytes, rqst_size - available_bytes); } else { /* We can read from the full request size */ memcpy (buffer, rb->readptr, rqst_size); rb->readptr = rb->readptr + rqst_size; return rqst_size; } } } SANE_Bool read_buffer_is_empty (Read_Buffer * read_buffer) { return read_buffer->empty; } /* * average a width*height rgb/monochrome area * return values in given pointers */ static int average_area (SANE_Byte * regs, SANE_Byte * data, int width, int height, int *ra, int *ga, int *ba) { int x, y; int global = 0; int rc, gc, bc; *ra = 0; *ga = 0; *ba = 0; rc = 0; gc = 0; bc = 0; if (rts88xx_is_color (regs)) { for (x = 0; x < width; x++) for (y = 0; y < height; y++) { rc += data[3 * width * y + x]; gc += data[3 * width * y + width + x]; bc += data[3 * width * y + 2 * width + x]; } global = (rc + gc + bc) / (3 * width * height); *ra = rc / (width * height); *ga = gc / (width * height); *ba = bc / (width * height); } else { for (x = 0; x < width; x++) for (y = 0; y < height; y++) { gc += data[width * y + x]; } global = gc / (width * height); *ga = gc / (width * height); } DBG (7, "average_area: global=%d, red=%d, green=%d, blue=%d\n", global, *ra, *ga, *ba); return global; } /** * we scan a dark area with gain minimum to detect offset */ SANE_Status sanei_lexmark_low_offset_calibration (Lexmark_Device * dev) { SANE_Byte regs[255]; /* we have our own copy of shadow registers */ SANE_Status status = SANE_STATUS_GOOD; int i, lines = 8, yoffset = 2; int pixels; int failed = 0; /* offsets */ int ro = 0, go = 0, bo = 0; /* averages */ int ra, ga, ba, average; SANE_Byte *data = NULL; SANE_Byte top[OFFSET_RANGES] = { 0, 0x7f, 0x9f, 0xbf, 0xff }; #ifdef DEEP_DEBUG char title[20]; #endif DBG (2, "sanei_lexmark_low_offset_calibration: start\n"); /* copy registers */ for (i = 0; i < 255; i++) regs[i] = dev->shadow_regs[i]; /* we clear movement bit */ regs[0xc3] = regs[0xc3] & 0x7f; pixels = (dev->sensor->offset_endx - dev->sensor->offset_startx) / regs[0x7a]; /* there are 4 ranges of offset: 00-7F : almost no offset 80-9F : high noise A0-BF : high noise C0-FF : high noise we start with the highest one and decrease until overall offset is ok First loop may have such an high offset that scanned data overflow and gives a low average. So we always skip its results */ /* minimal gains */ DBG (3, "sanei_lexmark_low_offset_calibration: setting gains to (1,1,1).\n"); rts88xx_set_gain (regs, 1, 1, 1); i = OFFSET_RANGES; average = 255; /* loop on ranges until one fits. Then adjust offset, first loop is * always done. TODO detect overflow by 'noise looking' data pattern */ while (((i > 0) && (average > dev->sensor->offset_threshold)) || (i == OFFSET_RANGES)) { /* next range */ i--; /* sets to top of range */ ro = top[i]; go = top[i]; bo = top[i]; rts88xx_set_offset (regs, ro, ro, ro); DBG (3, "sanei_lexmark_low_offset_calibration: setting offsets to (%d,%d,%d).\n", ro, ro, ro); status = low_simple_scan (dev, regs, dev->sensor->offset_startx, pixels, yoffset, lines, &data); if (status != SANE_STATUS_GOOD) { DBG (1, "sanei_lexmark_low_offset_calibration: low_simple_scan failed!\n"); if (data != NULL) free (data); return status; } #ifdef DEEP_DEBUG sprintf (title, "offset%02x.pnm", ro); write_pnm_file (title, pixels, lines, rts88xx_is_color (regs), data); #endif average = average_area (regs, data, pixels, lines, &ra, &ga, &ba); free (data); } if (i == 0) { DBG (2, "sanei_lexmark_low_offset_calibration: failed !\n"); failed = 1; } /* increase gain and scan again */ /* increase gain for fine offset tuning */ rts88xx_set_gain (regs, 6, 6, 6); status = low_simple_scan (dev, regs, dev->sensor->offset_startx, pixels, yoffset, lines, &data); if (status != SANE_STATUS_GOOD) { DBG (1, "sanei_lexmark_low_offset_calibration: low_simple_scan failed!\n"); if (data != NULL) free (data); return status; } average_area (regs, data, pixels, lines, &ra, &ga, &ba); #ifdef DEEP_DEBUG write_pnm_file ("offset-final.pnm", pixels, lines, rts88xx_is_color (regs), data); #endif /* this "law" is a guess, may (should?) be changed ... */ if (!failed) { if (ro > ra) dev->offset.red = ro - ra; if (go > ga) { dev->offset.green = go - ga; dev->offset.gray = go - ga; } if (bo > ba) dev->offset.blue = bo - ba; } else { dev->offset.red = dev->sensor->offset_fallback; dev->offset.green = dev->sensor->offset_fallback; dev->offset.blue = dev->sensor->offset_fallback; } DBG (7, "sanei_lexmark_low_offset_calibration: offset=(0x%02x,0x%02x,0x%02x).\n", dev->offset.red, dev->offset.green, dev->offset.blue); DBG (2, "sanei_lexmark_low_offset_calibration: end.\n"); free (data); return status; } /* * we scan a white area until average is good enough * level is good enough when it maximize the range value of output: * ie max-min is maximum */ SANE_Status sanei_lexmark_low_gain_calibration (Lexmark_Device * dev) { SANE_Byte regs[255]; /* we have our own copy of shadow registers */ SANE_Status status = SANE_STATUS_GOOD; int i, lines = 4, yoffset = 1; int sx, ex; int pixels; /* averages */ int ra, ga, ba; SANE_Byte *data = NULL; int red, green, blue; #ifdef DEEP_DEBUG char title[20]; #endif DBG (2, "sanei_lexmark_low_gain_calibration: start\n"); /* copy registers */ for (i = 0; i < 255; i++) regs[i] = dev->shadow_regs[i]; /* we clear movement bit */ regs[0xc3] = regs[0xc3] & 0x7f; sx = regs[0x67] * 256 + regs[0x66]; ex = regs[0x6d] * 256 + regs[0x6c]; pixels = (ex - sx) / regs[0x7a]; /* set up initial gains */ red = 6; green = 6; blue = 6; rts88xx_set_gain (regs, red, green, blue); /* init loop */ i = 0; ra = 0; ba = 0; ga = 0; status = low_cancel (dev->devnum); if (status != SANE_STATUS_GOOD) return status; /* we do a simple scan all 3 averages give the chosen level */ while (((rts88xx_is_color (regs) && ((ra < dev->sensor->red_gain_target) || (ga < dev->sensor->green_gain_target) || (ba < dev->sensor->blue_gain_target))) || (!rts88xx_is_color (regs) && (ga < dev->sensor->gray_gain_target))) && (i < 25)) { status = low_simple_scan (dev, regs, sx, pixels, yoffset, lines, &data); if (status != SANE_STATUS_GOOD) { DBG (1, "sanei_lexmark_low_gain_calibration: low_simple_scan failed!\n"); if (data != NULL) free (data); return status; } #ifdef DEEP_DEBUG sprintf (title, "gain%02d.pnm", i); write_pnm_file (title, pixels, lines, rts88xx_is_color (regs), data); #endif average_area (regs, data, pixels, lines, &ra, &ga, &ba); free (data); if (ra < dev->sensor->red_gain_target) red++; if (ga < dev->sensor->green_gain_target || (dev->sensor->gray_gain_target && !rts88xx_is_color (regs))) green++; if (ba < dev->sensor->blue_gain_target) blue++; rts88xx_set_gain (regs, red, green, blue); i++; } dev->gain.red = red; dev->gain.green = green; dev->gain.blue = blue; dev->gain.gray = green; DBG (7, "sanei_lexmark_low_gain_calibration: gain=(0x%02x,0x%02x,0x%02x).\n", dev->gain.red, dev->gain.green, dev->gain.blue); DBG (2, "sanei_lexmark_low_gain_calibration: end.\n"); return status; } /** * there is no hardware shading correction. So we have to do it in software. * We do it by scanning a pure white area which is before scanning area. Then * we compute per pixel coefficient to move the scanned value to the target * value. These coefficients are used later to correct scanned data. * The scan is done with all the final scan settings but the height and vertical * start position. */ SANE_Status sanei_lexmark_low_shading_calibration (Lexmark_Device * dev) { SANE_Byte regs[255]; /* we have our own copy of shadow registers */ int i, j, pixels, bpl; int sx, ex; SANE_Byte *data = NULL; SANE_Status status; /* enough 75 dpi lines to "go off" home position dot, and include shading area */ int lines = 4 + 4; int lineoffset = 1; int linetotal = lines + lineoffset; int yoffset; int x, y; float rtarget, btarget, gtarget; DBG (2, "sanei_lexmark_low_shading_calibration: start\n"); /* copy registers */ for (i = 0; i < 255; i++) regs[i] = dev->shadow_regs[i]; /* allocate memory for scan */ sx = regs[0x67] * 256 + regs[0x66]; ex = regs[0x6d] * 256 + regs[0x6c]; DBG (7, "startx=%d, endx=%d, coef=%d, r2f=0x%02x\n", sx, ex, regs[0x7a], regs[0x2f]); pixels = (ex - sx) / regs[0x7a]; if (rts88xx_is_color (regs)) bpl = 3 * pixels; else bpl = pixels; /* adjust the target area to the scanning resolution */ lines = (8 * lines) / regs[0x7a]; lineoffset = (8 * lineoffset) / regs[0x7a]; linetotal = (8 * linetotal) / regs[0x7a]; data = (SANE_Byte *) malloc (bpl * lines); DBG (7, "pixels=%d, lines=%d, size=%d\n", pixels, lines, bpl * lines); if (data == NULL) { DBG (2, "sanei_lexmark_low_shading_calibration: failed to allocate %d bytes !\n", bpl * lines); return SANE_STATUS_NO_MEM; } if (dev->shading_coeff != NULL) free (dev->shading_coeff); dev->shading_coeff = (float *) malloc (bpl * sizeof (float)); if (dev->shading_coeff == NULL) { DBG (2, "sanei_lexmark_low_shading_calibration: failed to allocate %d floats !\n", bpl); free (data); return SANE_STATUS_NO_MEM; } /* we set movement bit this time */ regs[0xc3] = regs[0xc3] | 0x80; /* execute scan */ status = low_simple_scan (dev, regs, sx, pixels, lineoffset, lines, &data); if (status != SANE_STATUS_GOOD) { DBG (1, "sanei_lexmark_low_shading_calibration: low_simple_scan failed!\n"); if (data != NULL) free (data); return status; } yoffset = -1; /* the very first lines of the scan may include the dark dot used * locate park position. We find the first line free of it in the scan. * We can't use is_home_line since it modifies data. */ for (y = 0; (y < lines) && (yoffset == y - 1); y++) { if (rts88xx_is_color (regs)) { for (x = 0; x < 3 * pixels; x++) { if (data[x + y * 3 * pixels] < 30) yoffset = y; } } else { for (x = 0; x < pixels; x++) { if (data[x + y * pixels] < 30) yoffset = y; } } } /* make sure we are really out of the dot */ yoffset++; /* yoffset is index of last dot line, go to first white line */ if (yoffset >= lines - 1) { DBG (7, "sanei_lexmark_low_shading_calibration: failed to detect yoffset.\n"); /* fail safe fallback, picture will be altered at dot position, but scanner is safe */ yoffset = lines - 2; } else yoffset++; DBG (7, "sanei_lexmark_low_shading_calibration: yoffset=%d.\n", yoffset); #ifdef DEEP_DEBUG write_pnm_file ("shading.pnm", pixels, lines, rts88xx_is_color (regs), data); #endif /* computes coefficients */ /* there are 8 lines usable for shading calibration at 150 dpi, between bottom of "home position" dot and the start of the scanner's window assembly, we only use 7 of them */ if (yoffset + (8 * 4) / regs[0x7a] < lines) lines = yoffset + (8 * 4) / regs[0x7a]; rtarget = dev->sensor->red_shading_target; gtarget = dev->sensor->green_shading_target; btarget = dev->sensor->blue_shading_target; for (i = 0; i < pixels; i++) { /* we computes the coefficient needed to move the scanned value to the target value */ if (rts88xx_is_color (dev->shadow_regs)) { /* RED */ dev->shading_coeff[i] = 0; for (j = yoffset; j < lines; j++) dev->shading_coeff[i] += data[i + j * bpl]; dev->shading_coeff[i] = (rtarget / (dev->shading_coeff[i] / (lines - yoffset))); /* GREEN */ dev->shading_coeff[i + pixels] = 0; for (j = yoffset; j < lines; j++) dev->shading_coeff[i + pixels] += data[i + j * bpl + pixels]; dev->shading_coeff[i + pixels] = ((gtarget / dev->shading_coeff[i + pixels]) * (lines - yoffset)); /* BLUE */ dev->shading_coeff[i + 2 * pixels] = 0; for (j = yoffset; j < lines; j++) dev->shading_coeff[i + 2 * pixels] += data[i + j * bpl + 2 * pixels]; dev->shading_coeff[i + 2 * pixels] = ((btarget / dev->shading_coeff[i + 2 * pixels]) * (lines - yoffset)); } else { dev->shading_coeff[i] = 0; for (j = yoffset; j < lines; j++) { dev->shading_coeff[i] += data[i + j * bpl]; } dev->shading_coeff[i] = (rtarget / dev->shading_coeff[i]) * (lines - yoffset); } } free(data); /* do the scan backward to go back to start position */ regs[0xc6] &= 0xF7; lines = (8 * 8) / regs[0x7a]; /* it should use linetotal to account for the lineoffset */ if (dev->model.sensor_type == X74_SENSOR) lines = linetotal; /* execute scan */ status = low_simple_scan (dev, regs, sx, pixels, 1, lines, &data); if (status != SANE_STATUS_GOOD) { DBG (1, "sanei_lexmark_low_shading_calibration: low_simple_scan failed!\n"); if(data!=NULL) free(data); return status; } #ifdef DEEP_DEBUG write_pnm_file ("shading_bwd.pnm", pixels, lines, rts88xx_is_color (regs), data); #endif free (data); DBG (2, "sanei_lexmark_low_shading_calibration: end.\n"); return status; } SANE_Status sanei_lexmark_low_calibration (Lexmark_Device * dev) { SANE_Status status; DBG (2, "sanei_lexmark_low_calibration: start.\n"); status = sanei_lexmark_low_offset_calibration (dev); if (status != SANE_STATUS_GOOD) return status; /* we put the offset just computed in scanning regs */ if (rts88xx_is_color (dev->shadow_regs)) { rts88xx_set_offset (dev->shadow_regs, dev->offset.red, dev->offset.green, dev->offset.blue); } else { rts88xx_set_offset (dev->shadow_regs, dev->offset.gray, dev->offset.gray, dev->offset.gray); } /* if manual gain settings, no gain calibration */ if (dev->val[OPT_MANUAL_GAIN].w == SANE_TRUE) { if (rts88xx_is_color (dev->shadow_regs)) { dev->gain.red = dev->val[OPT_RED_GAIN].w; dev->gain.green = dev->val[OPT_GREEN_GAIN].w; dev->gain.blue = dev->val[OPT_BLUE_GAIN].w; } else dev->gain.gray = dev->val[OPT_GRAY_GAIN].w; } else { status = sanei_lexmark_low_gain_calibration (dev); if (status != SANE_STATUS_GOOD) return status; } /* put the calibrated or manual settings before shading calibration which must be done with final setting values */ if (rts88xx_is_color (dev->shadow_regs)) { rts88xx_set_gain (dev->shadow_regs, dev->gain.red, dev->gain.green, dev->gain.blue); } else { rts88xx_set_gain (dev->shadow_regs, dev->gain.gray, dev->gain.gray, dev->gain.gray); } status = sanei_lexmark_low_shading_calibration (dev); if (status != SANE_STATUS_GOOD) return status; DBG (2, "sanei_lexmark_low_calibration: end.\n"); return SANE_STATUS_GOOD; } /* assign sensor data */ static SANE_Status sanei_lexmark_low_assign_sensor (Lexmark_Device * dev) { int dn; /* init sensor data */ dn = 0; while (sensor_list[dn].id != 0 && sensor_list[dn].id != dev->model.sensor_type) dn++; if (sensor_list[dn].id == 0) { DBG (1, "sanei_lexmark_low_assign_sensor: unknown sensor %d\n", dev->model.sensor_type); return SANE_STATUS_UNSUPPORTED; } dev->sensor = &(sensor_list[dn]); DBG (1, "sanei_lexmark_low_assign_sensor: assigned sensor number %d\n", dev->model.sensor_type); return SANE_STATUS_GOOD; } /* assign model description, based on USB id, and register content when * available */ SANE_Status sanei_lexmark_low_assign_model (Lexmark_Device * dev, SANE_String_Const devname, SANE_Int vendor, SANE_Int product, SANE_Byte mainboard) { int dn; SANE_Bool found = SANE_FALSE; DBG_INIT (); DBG (2, "sanei_lexmark_low_assign_model: start\n"); DBG (3, "sanei_lexmark_low_assign_model: assigning %04x:%04x, variant %d\n", vendor, product, mainboard); dn = 0; /* walk the list of known devices */ while (!found && model_list[dn].vendor_id != 0) { /* no mainboard id given (at attach time) */ if (mainboard == 0 && vendor == model_list[dn].vendor_id && product == model_list[dn].product_id) found = SANE_TRUE; /* mainboard given (init time) */ if (mainboard != 0 && mainboard == model_list[dn].mainboard_id && vendor == model_list[dn].vendor_id && product == model_list[dn].product_id) found = SANE_TRUE; if (!found) dn++; } /* we hit the end of list, so we don't know about the current model */ if (!found) { DBG (1, "sanei_lexmark_low_assign_model: unknown device 0x%04x:0x%04x\n", vendor, product); return SANE_STATUS_UNSUPPORTED; } dev->sane.name = strdup (devname); dev->sane.vendor = model_list[dn].vendor; dev->sane.model = model_list[dn].model; dev->model = model_list[dn]; dev->sane.type = "flatbed scanner"; DBG (3, "sanei_lexmark_low_assign_model: assigned %s\n", dev->model.model); /* init sensor data */ DBG (2, "sanei_lexmark_low_assign_model: end.\n"); return sanei_lexmark_low_assign_sensor (dev); } backends-1.3.0/backend/lexmark_models.c000066400000000000000000000077111456256263500200520ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2006-2010 Stéphane Voltz Copyright (C) 2010 "Torsten Houwaart" X74 support 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, see . 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. */ static Lexmark_Model model_list[] = { { 0x043d, /* vendor id */ 0x007c, /* product id */ 0xb2, /* submodel id */ "Lexmark X1100", /* name */ "Lexmark", /* vendor */ "X1100/rev. B2", /* model */ X1100_MOTOR, /* X1100 series has 2 sensors */ X1100_B2_SENSOR, 1235, /* first x-coordinate of Home Point */ 1258}, /* second x-coordinate of Home Point */ { 0x043d, /* vendor id */ 0x007c, /* product id */ 0x2c, /* submodel id */ "Lexmark X1100", /* name */ "Lexmark", /* vendor */ "X1100/rev. 2C", /* model */ A920_MOTOR, /* X1100 series has 2 sensors, 2C or B2. It is detected at sane_open() */ X1100_2C_SENSOR, 1235, /* first x-coordinate of Home Point */ 1258}, /* second x-coordinate of Home Point */ { 0x413c, /* vendor id */ 0x5105, /* product id */ 0x2c, /* submodel id */ "Dell A920", /* name */ "Dell", /* vendor */ "A920", /* model */ A920_MOTOR, A920_SENSOR, 1235, /* first x-coordinate of Home Point */ 1258}, /* second x-coordinate of Home Point */ { 0x043d, /* vendor id */ 0x007d, /* product id */ 0x87, /* submodel id */ "Lexmark X1200", /* name */ "Lexmark", /* vendor */ "X1200/USB1.1", /* model */ A920_MOTOR, X1200_SENSOR, 1235, /* first x-coordinate of Home Point */ 1258}, /* second x-coordinate of Home Point */ { 0x043d, /* vendor id */ 0x007d, /* product id */ 0x97, /* submodel id */ "Lexmark X1200", /* name */ "Lexmark", /* vendor */ "X1200/USB2.0", /* model */ A920_MOTOR, X1200_USB2_SENSOR, 1235, /* first x-coordinate of Home Point */ 1258}, /* second x-coordinate of Home Point */ { 0x043d, /* vendor id */ 0x0060, /* product id */ 0x00, /* submodel id */ "Lexmark X74", /* name */ "Lexmark", /* vendor */ "X74", /* model */ X74_MOTOR, X74_SENSOR, 1222, /* first x-coordinate of Home Point */ 1322}, /* second x-coordinate of Home Point */ { /* termination model, must be last */ 0, 0, 0, NULL, NULL, NULL, 0, 0, 0, 0} }; /* end models description */ backends-1.3.0/backend/lexmark_sensors.c000066400000000000000000000077661456256263500202750ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2006-2010 Stéphane Voltz Copyright (C) 2010 "Torsten Houwaart" X74 support 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, see . 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. */ static Lexmark_Sensor sensor_list[] = { { X1100_B2_SENSOR, /* start x, end x and target average for offset calibration */ 48, 80, 6, /* usable pixel sensor startx */ 106, /* default gain */ 16, /* gain calibration targets */ 180, 180, 180, 180, /* shading correction targets */ 260, 260, 260, 260, /* offset and gain fallback */ 0x70, 17}, { X1100_2C_SENSOR, /* start x, end x and target average for offset calibration */ 48, 80, 12, /* usable pixel sensor startx */ 106, /* default gain */ 10, /* gain calibration */ 140, 150, 150, 150, /* shading correction */ 260, 260, 260, 260, /* offset and gain fallback */ 0x70, 11}, { /* USB 1.1 settings */ X1200_SENSOR, /* start x, end x and target average for offset calibration */ 32, 64, 15, /* usable pixel sensor startx */ 136, /* default gain */ 16, /* gain calibration */ 180, 180, 180, 180, /* shading correction */ 260, 260, 260, 260, /* offset and gain fallback */ 0x86, 16}, { /* this one is a 1200 on USB2.0 */ X1200_USB2_SENSOR, /* start x, end x and target average for offset calibration */ 32, 64, 12, /* usable pixel sensor startx */ 136, /* default gain */ 16, /* gain calibration */ 180, 180, 180, 180, /* shading correction */ 260, 260, 260, 260, /* offset and gain fallback */ 0x86, 16}, { A920_SENSOR, /* start x, end x and target average for offset calibration */ 48, 80, 6, /* usable pixel sensor startx */ 106, /* default gain */ 12, /* gain calibration target */ 130, 145, 150, 145, /* gain calibration target */ 260, 260, 260, 260, /* offset and gain fallback */ 0x70, 13}, { X74_SENSOR, /* start x TDONE, end x and target average for offset calibration */ /*36,68,12, */ 20, 52, 12, /* usable pixel sensor startx */ /*104, */ 104, /* default gain */ 10, /* gain calibration target */ 130, 145, 150, 145, /* gain calibration target */ 260, 260, 260, 260, /* offset and gain fallback */ 0x70, 13}, /* termination list sensor, must be last */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; backends-1.3.0/backend/lexmark_x2600.c000066400000000000000000001215441456256263500173470ustar00rootroot00000000000000/* lexmark_x2600.c: SANE backend for Lexmark x2600 scanners. (C) 2023 "Benoit Juin" 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, see . 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 "lexmark_x2600.h" #define BUILD 1 #define LEXMARK_X2600_CONFIG_FILE "lexmark_x2600.conf" #define MAX_OPTION_STRING_SIZE 255 static SANE_Int transfer_buffer_size = 32768; static Lexmark_Device *first_device = 0; static SANE_Int num_devices = 0; static const SANE_Device **devlist = 0; static SANE_Bool initialized = SANE_FALSE; // first value is the size of the wordlist! static SANE_Int dpi_list[] = { 4, 100, 200, 300, 600 }; static SANE_Int dpi_list_size = sizeof(dpi_list) / sizeof(dpi_list[0]); static SANE_String_Const mode_list[] = { SANE_VALUE_SCAN_MODE_COLOR, SANE_VALUE_SCAN_MODE_GRAY, NULL }; static SANE_Range x_range = { 0, /* minimum */ 5078, /* maximum */ 1 /* quantization */ }; static SANE_Range y_range = { 0, /* minimum */ 7015, /* maximum */ 1 /* quantization */ }; static SANE_Byte command1_block[] = { 0xA5, 0x00, 0x19, 0x10, 0x01, 0x83, 0xAA, 0xBB, 0xCC, 0xDD, 0x02, 0x00, 0x1B, 0x53, 0x03, 0x00, 0x00, 0x00, 0x80, 0x00, 0xAA, 0xBB, 0xCC, 0xDD, 0xAA, 0xBB, 0xCC, 0xDD}; static SANE_Int command1_block_size = sizeof(command1_block); static SANE_Byte command2_block[] = { 0xA5, 0x00, 0x19, 0x10, 0x01, 0x83, 0xAA, 0xBB, 0xCC, 0xDD, 0x02, 0x00, 0x1B, 0x53, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00, 0xAA, 0xBB, 0xCC, 0xDD, 0xAA, 0xBB, 0xCC, 0xDD}; static SANE_Int command2_block_size = sizeof(command2_block); static SANE_Byte command_with_params_block[] = { 0xA5, 0x00, 0x31, 0x10, 0x01, 0x83, 0xAA, 0xBB, 0xCC, 0xDD, 0x02, 0x00, 0x1B, 0x53, 0x05, 0x00, 0x18, 0x00, 0x80, 0x00, 0xFF, 0x00, 0x00, 0x02, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0xBB, 0xCC, 0xDD, 0xAA, 0xBB, 0xCC, 0xDD}; static SANE_Int command_with_params_block_size = sizeof(command_with_params_block); static SANE_Byte command_cancel1_block[] = { 0xa5, 0x00, 0x19, 0x10, 0x01, 0x83, 0xaa, 0xbb, 0xcc, 0xdd, 0x02, 0x00, 0x1b, 0x53, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xbb, 0xcc, 0xdd, 0xaa, 0xbb, 0xcc, 0xdd}; static SANE_Byte command_cancel2_block[] = { 0xa5, 0x00, 0x19, 0x10, 0x01, 0x83, 0xaa, 0xbb, 0xcc, 0xdd, 0x02, 0x00, 0x1b, 0x53, 0x06, 0x00, 0x00, 0x00, 0x80, 0x00, 0xaa, 0xbb, 0xcc, 0xdd, 0xaa, 0xbb, 0xcc, 0xdd}; static SANE_Int command_cancel_size = sizeof(command_cancel1_block); static SANE_Byte empty_line_data_packet[] = { 0x1b, 0x53, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00}; static SANE_Int empty_line_data_packet_size = sizeof(empty_line_data_packet); static SANE_Byte last_data_packet[] = { 0x1b, 0x53, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01}; static SANE_Int last_data_packet_size = sizeof(last_data_packet); static SANE_Byte cancel_packet[] = { 0x1b, 0x53, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03}; static SANE_Int cancel_packet_size = sizeof(cancel_packet); static SANE_Byte linebegin_data_packet[] = { 0x1b, 0x53, 0x02, 0x00}; static SANE_Int linebegin_data_packet_size = sizeof(linebegin_data_packet); static SANE_Byte unknown_a_data_packet[] = { 0x1b, 0x53, 0x01, 0x00, 0x01, 0x00, 0x80, 0x00}; static SANE_Int unknown_a_data_packet_size = sizeof(unknown_a_data_packet); static SANE_Byte unknown_b_data_packet[] = { 0x1b, 0x53, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00}; static SANE_Int unknown_b_data_packet_size = sizeof(unknown_b_data_packet); static SANE_Byte unknown_c_data_packet[] = { 0x1b, 0x53, 0x04, 0x00, 0x00, 0x00, 0x84, 0x00}; static SANE_Int unknown_c_data_packet_size = sizeof(unknown_c_data_packet); static SANE_Byte unknown_d_data_packet[] = { 0x1b, 0x53, 0x05, 0x00, 0x00, 0x00}; static SANE_Int unknown_d_data_packet_size = sizeof(unknown_d_data_packet); static SANE_Byte unknown_e_data_packet[] = { 0xa5, 0x00, 0x06, 0x10, 0x01, 0xaa, 0xbb, 0xcc, 0xdd}; static SANE_Int unknown_e_data_packet_size = sizeof(unknown_e_data_packet); /* static SANE_Byte not_ready_data_packet[] = { */ /* 0x1b, 0x53, 0x01, 0x00, 0x01, 0x00, 0x84, 0x00}; */ /* static SANE_Int not_ready_data_packet_size = sizeof(not_ready_data_packet); */ static SANE_Int line_header_length = 9; //static SANE_Byte empty_data_packet[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; SANE_Status clean_and_copy_data(const SANE_Byte * source, SANE_Int source_size, SANE_Byte * destination, SANE_Int * destination_length, SANE_Int mode, SANE_Int max_length, SANE_Handle dev) { DBG (10, "clean_and_copy_data\n"); // if source doesnt start with 1b 53 02, then it is a continuation packet // SANE_Int k = 0; // SANE_Int bytes_written = 0; // BW 1b 53 02 00 21 00 00 00 00 | 32 | 21 -> 33 (segmentlng= 32) // BW 1b 53 02 00 41 00 00 00 00 | 64 | 41 -> 65 (segmentlng= 64) // COLOR 1b 53 02 00 c1 00 00 00 00 | 64 | c1 -> 193 (segmentlng= 192) // COLOR 1b 53 02 00 01 06 00 00 00 | 512 | 601 -> 1537 (segmentlng= 1536) // COLOR 1b 53 02 00 99 3a 00 00 00 | 5000 | 3a99 -> 15001 (segmentlng=15000) // COLOR 1b 53 02 00 f7 0f 00 | 1362 | 0ff7 -> 4087 <- limit where sane_read can a read a line at e time, more that 1362 and then the rest // of the line will be available in the next sane_read call // COLOR 1b 53 02 00 fa 0f 00 | | 0ffa -> 4090 <- in that case the line doesnt fit, clean_and_copy_data will be called again with the rest of the data // edge case segment doesn(t feet in the packet size /* if(segment_length > source_size - 9) */ /* segment_length = source_size - 9; */ // the scanner sends series of 8 lines function param source // every lines has prefix see linebegin_data_packet // the source parameter as a limited length :function param source_size // so the serie og 8 lines can be splited // in such case, in the next call of this function, source contain the end of the // broken segment. // Here is the way data is read: // 1 - check that source begin with a linebegin_data_packet signature // if this is the case the source[4] & source[5] contains how much data // can be read before onother header is reach (linebegin_data_packet) Lexmark_Device * ldev = (Lexmark_Device * ) dev; SANE_Int i = 0; SANE_Int bytes_read = 0; SANE_Byte tmp = 0; SANE_Int source_read_cursor = 0; SANE_Int block_pixel_data_length = 0; SANE_Int size_to_realloc = 0; if(!ldev->eof){ // does source start with linebegin_data_packet? if (memcmp(linebegin_data_packet, source, linebegin_data_packet_size) == 0){ // extract the number of bytes we can read befor new header is reached // store it in the device in case of continuation packet ldev->read_buffer->linesize = (source[4] + ((source[5] << 8) & 0xFF00)) - 1; ldev->read_buffer->last_line_bytes_read = ldev->read_buffer->linesize; DBG (10, " this is the begining of a line linesize=%ld\n", ldev->read_buffer->linesize); } else { DBG (10, " this is not a new line packet, continue to fill the read buffer\n"); //return; } if(ldev->read_buffer->linesize == 0){ DBG (10, " linesize=0 something went wrong, lets ignore that USB packet\n"); return SANE_STATUS_CANCELLED; } // loop over source buffer while(i < source_size){ // last line was full if(ldev->read_buffer->last_line_bytes_read == ldev->read_buffer->linesize){ // if next block fit in the source if(i + line_header_length + (SANE_Int) ldev->read_buffer->linesize <= source_size){ ldev->read_buffer->image_line_no += 1; source_read_cursor = i + line_header_length; block_pixel_data_length = ldev->read_buffer->linesize; ldev->read_buffer->last_line_bytes_read = block_pixel_data_length; size_to_realloc = ldev->read_buffer->image_line_no * ldev->read_buffer->linesize * sizeof(SANE_Byte); bytes_read = block_pixel_data_length + line_header_length; } // next block cannot be read fully because source_size is too small // (USB packet fragmentation) else{ ldev->read_buffer->image_line_no += 1; source_read_cursor = i + line_header_length; block_pixel_data_length = source_size - i - line_header_length; ldev->read_buffer->last_line_bytes_read = block_pixel_data_length; size_to_realloc = ((ldev->read_buffer->image_line_no-1) * ldev->read_buffer->linesize + block_pixel_data_length) * sizeof(SANE_Byte); bytes_read = block_pixel_data_length + line_header_length; } } // last line was not full lets extract what is left // this is du to USB packet fragmentation else{ // the last line was not full so no increment ldev->read_buffer->image_line_no += 0; source_read_cursor = i; block_pixel_data_length = ldev->read_buffer->linesize - ldev->read_buffer->last_line_bytes_read; // we completed the last line with missing bytes so new the line is full ldev->read_buffer->last_line_bytes_read = ldev->read_buffer->linesize; size_to_realloc = ldev->read_buffer->image_line_no * ldev->read_buffer->linesize * sizeof(SANE_Byte); bytes_read = block_pixel_data_length; } DBG (20, " size_to_realloc=%d i=%d image_line_no=%d\n", size_to_realloc, i, ldev->read_buffer->image_line_no); // do realoc memory space for our buffer SANE_Byte* alloc_result = realloc(ldev->read_buffer->data, size_to_realloc); if(alloc_result == NULL){ // TODO allocation was not possible DBG (20, " REALLOC failed\n"); return SANE_STATUS_NO_MEM; } // point data to our new memary space ldev->read_buffer->data = alloc_result; // reposition writeptr and readptr to the correct memory adress // to do that use write_byte_counter and read_byte_counter ldev->read_buffer->writeptr = ldev->read_buffer->data + ldev->read_buffer->write_byte_counter; // copy new data memcpy( ldev->read_buffer->writeptr, source + source_read_cursor, block_pixel_data_length ); // store how long is the buffer ldev->read_buffer->write_byte_counter += block_pixel_data_length; i += bytes_read; } } // reposition our readptr ldev->read_buffer->readptr = ldev->read_buffer->data + ldev->read_buffer->read_byte_counter; // read our buffer to fill the destination buffer // mulitple call so read may has been already started // length already read is stored in ldev->read_buffer->read_byte_counter SANE_Int available_bytes_to_read = ldev->read_buffer->write_byte_counter - ldev->read_buffer->read_byte_counter; DBG (20, " source read done now sending to destination \n"); // we will copy image data 3 bytes by 3 bytes if color mod to allow color swap // this avoid error on color channels swapping if (mode == SANE_FRAME_RGB){ // get max chunk SANE_Int data_chunk_size = max_length; if(data_chunk_size > available_bytes_to_read){ data_chunk_size = available_bytes_to_read; } data_chunk_size = data_chunk_size / 3; data_chunk_size = data_chunk_size * 3; // we have to invert color channels SANE_Byte * color_swarp_ptr = ldev->read_buffer->readptr; for(SANE_Int j=0; j < data_chunk_size;j += 3){ // DBG (20, " swapping RGB <- BGR j=%d\n", j); tmp = *(color_swarp_ptr + j); *(color_swarp_ptr + j) = *(color_swarp_ptr + j + 2); *(color_swarp_ptr + j + 2) = tmp; } memcpy (destination, ldev->read_buffer->readptr, data_chunk_size); ldev->read_buffer->read_byte_counter += data_chunk_size; *destination_length = data_chunk_size; } // gray mode copy until max_length else{ SANE_Int data_chunk_size = max_length; if(data_chunk_size > available_bytes_to_read){ data_chunk_size = available_bytes_to_read; } memcpy ( destination, ldev->read_buffer->readptr, data_chunk_size ); ldev->read_buffer->read_byte_counter += data_chunk_size;; *destination_length = data_chunk_size; } DBG (20, " done destination_length=%d available_bytes_to_read=%d\n", *destination_length, available_bytes_to_read); if(available_bytes_to_read > 0){ return SANE_STATUS_GOOD; }else{ ldev->eof = 0; return SANE_STATUS_EOF; } } SANE_Status usb_write_then_read (Lexmark_Device * dev, SANE_Byte * cmd, size_t cmd_size) { size_t buf_size = 256; SANE_Byte buf[buf_size]; SANE_Status status; DBG (10, "usb_write_then_read: %d\n", dev->devnum); sanei_usb_set_endpoint(dev->devnum, USB_DIR_OUT|USB_ENDPOINT_TYPE_BULK, 0x02); DBG (10, " endpoint set: %d\n", dev->devnum); /* status = sanei_usb_read_bulk (dev->devnum, buf, &buf_size); */ /* DBG (10, " readdone: %d\n", dev->devnum); */ /* if (status != SANE_STATUS_GOOD && status != SANE_STATUS_EOF) */ /* { */ /* DBG (1, "USB READ IO Error in usb_write_then_read, fail devnum=%d\n", */ /* dev->devnum); */ /* return status; */ /* } */ DBG (10, " attempting to write...: %d\n", dev->devnum); status = sanei_usb_write_bulk (dev->devnum, cmd, &cmd_size); DBG (10, " writedone: %d\n", dev->devnum); if (status != SANE_STATUS_GOOD) { DBG (1, "USB WRITE IO Error in usb_write_then_read, launch fail: %d\n", status); return status; } debug_packet(cmd, cmd_size, WRITE); DBG (10, " attempting to read...: %d\n", dev->devnum); status = sanei_usb_read_bulk (dev->devnum, buf, &buf_size); DBG (10, " readdone: %d\n", dev->devnum); if (status != SANE_STATUS_GOOD && status != SANE_STATUS_EOF) { DBG (1, "USB READ IO Error in usb_write_then_read, fail devnum=%d\n", dev->devnum); return status; } debug_packet(buf, buf_size, READ); return SANE_STATUS_GOOD; } void build_packet(Lexmark_Device * dev, SANE_Byte packet_id, SANE_Byte * buffer){ memcpy(buffer, command_with_params_block, command_with_params_block_size); // protocole related... "ID?" buffer[14] = packet_id; // mode if (memcmp(dev->val[OPT_MODE].s, "Color", 5) == 0 ) buffer[20] = 0x03; else buffer[20] = 0x02; // pixel width (swap lower byte -> higher byte) buffer[24] = dev->val[OPT_BR_X].w & 0xFF; buffer[25] = (dev->val[OPT_BR_X].w >> 8) & 0xFF; // pixel height (swap lower byte -> higher byte) buffer[28] = dev->val[OPT_BR_Y].w & 0xFF; buffer[29] = (dev->val[OPT_BR_Y].w >> 8) & 0xFF; // dpi x (swap lower byte -> higher byte) buffer[40] = dev->val[OPT_RESOLUTION].w & 0xFF; buffer[41] = (dev->val[OPT_RESOLUTION].w >> 8) & 0xFF; // dpi y (swap lower byte -> higher byte) buffer[42] = dev->val[OPT_RESOLUTION].w & 0xFF; buffer[43] = (dev->val[OPT_RESOLUTION].w >> 8) & 0xFF; } SANE_Status init_options (Lexmark_Device * dev) { SANE_Option_Descriptor *od; DBG (2, "init_options: dev = %p\n", (void *) dev); /* number of options */ od = &(dev->opt[OPT_NUM_OPTS]); od->name = SANE_NAME_NUM_OPTIONS; od->title = SANE_TITLE_NUM_OPTIONS; od->desc = SANE_DESC_NUM_OPTIONS; od->type = SANE_TYPE_INT; od->unit = SANE_UNIT_NONE; od->size = sizeof (SANE_Word); od->cap = SANE_CAP_SOFT_DETECT; od->constraint_type = SANE_CONSTRAINT_NONE; od->constraint.range = 0; dev->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* mode - sets the scan mode: Color / Gray */ od = &(dev->opt[OPT_MODE]); od->name = SANE_NAME_SCAN_MODE; od->title = SANE_TITLE_SCAN_MODE; od->desc = SANE_DESC_SCAN_MODE;; od->type = SANE_TYPE_STRING; od->unit = SANE_UNIT_NONE; od->size = MAX_OPTION_STRING_SIZE; od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; od->constraint_type = SANE_CONSTRAINT_STRING_LIST; od->constraint.string_list = mode_list; dev->val[OPT_MODE].s = malloc (od->size); if (!dev->val[OPT_MODE].s) return SANE_STATUS_NO_MEM; strcpy (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR); /* resolution */ od = &(dev->opt[OPT_RESOLUTION]); od->name = SANE_NAME_SCAN_RESOLUTION; od->title = SANE_TITLE_SCAN_RESOLUTION; od->desc = SANE_DESC_SCAN_RESOLUTION; od->type = SANE_TYPE_INT; od->unit = SANE_UNIT_DPI; od->size = sizeof (SANE_Int); od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; od->constraint_type = SANE_CONSTRAINT_WORD_LIST; od->constraint.word_list = dpi_list; dev->val[OPT_RESOLUTION].w = 200; /* preview mode */ od = &(dev->opt[OPT_PREVIEW]); od->name = SANE_NAME_PREVIEW; od->title = SANE_TITLE_PREVIEW; od->desc = SANE_DESC_PREVIEW; od->size = sizeof (SANE_Word); od->cap = SANE_CAP_INACTIVE; od->type = SANE_TYPE_BOOL; od->constraint_type = SANE_CONSTRAINT_NONE; dev->val[OPT_PREVIEW].w = SANE_FALSE; /* "Geometry" group: */ od = &(dev->opt[OPT_GEOMETRY_GROUP]); od->name = ""; od->title = SANE_I18N ("Geometry"); od->desc = ""; od->type = SANE_TYPE_GROUP; od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; od->size = 0; od->constraint_type = SANE_CONSTRAINT_NONE; // /* top-left x */ od = &(dev->opt[OPT_TL_X]); od->name = SANE_NAME_SCAN_TL_X; od->title = SANE_TITLE_SCAN_TL_X; od->desc = SANE_DESC_SCAN_TL_X; od->type = SANE_TYPE_INT; od->cap = SANE_CAP_INACTIVE; od->size = sizeof (SANE_Word); od->unit = SANE_UNIT_PIXEL; od->constraint_type = SANE_CONSTRAINT_RANGE; od->constraint.range = &x_range; dev->val[OPT_TL_X].w = 0; /* top-left y */ od = &(dev->opt[OPT_TL_Y]); od->name = SANE_NAME_SCAN_TL_Y; od->title = SANE_TITLE_SCAN_TL_Y; od->desc = SANE_DESC_SCAN_TL_Y; od->type = SANE_TYPE_INT; od->cap = SANE_CAP_INACTIVE; od->size = sizeof (SANE_Word); od->unit = SANE_UNIT_PIXEL; od->constraint_type = SANE_CONSTRAINT_RANGE; od->constraint.range = &y_range; dev->val[OPT_TL_Y].w = 0; /* bottom-right x */ od = &(dev->opt[OPT_BR_X]); od->name = SANE_NAME_SCAN_BR_X; od->title = SANE_TITLE_SCAN_BR_X; od->desc = SANE_DESC_SCAN_BR_X; od->type = SANE_TYPE_INT; od->size = sizeof (SANE_Word); od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; od->unit = SANE_UNIT_PIXEL; od->constraint_type = SANE_CONSTRAINT_RANGE; od->constraint.range = &x_range; dev->val[OPT_BR_X].w = 1654; /* bottom-right y */ od = &(dev->opt[OPT_BR_Y]); od->name = SANE_NAME_SCAN_BR_Y; od->title = SANE_TITLE_SCAN_BR_Y; od->desc = SANE_DESC_SCAN_BR_Y; od->type = SANE_TYPE_INT; od->size = sizeof (SANE_Word); od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; od->unit = SANE_UNIT_PIXEL; od->constraint_type = SANE_CONSTRAINT_RANGE; od->constraint.range = &y_range; dev->val[OPT_BR_Y].w = 2339; return SANE_STATUS_GOOD; } /* callback function for sanei_usb_attach_matching_devices */ static SANE_Status attach_one (SANE_String_Const devname) { Lexmark_Device *lexmark_device; DBG (2, "attach_one: attachLexmark: devname=%s first_device=%p\n", devname, (void *)first_device); for (lexmark_device = first_device; lexmark_device; lexmark_device = lexmark_device->next){ /* already attached devices */ if (strcmp (lexmark_device->sane.name, devname) == 0){ lexmark_device->missing = SANE_FALSE; return SANE_STATUS_GOOD; } } lexmark_device = (Lexmark_Device *) malloc (sizeof (Lexmark_Device)); if (lexmark_device == NULL) return SANE_STATUS_NO_MEM; lexmark_device->sane.name = strdup (devname); if (lexmark_device->sane.name == NULL) return SANE_STATUS_NO_MEM; lexmark_device->sane.vendor = "Lexmark"; lexmark_device->sane.model = "X2600 series"; lexmark_device->sane.type = "flat bed"; /* init transfer_buffer */ lexmark_device->transfer_buffer = malloc (transfer_buffer_size); if (lexmark_device->transfer_buffer == NULL) return SANE_STATUS_NO_MEM; /* Make the pointer to the read buffer null here */ lexmark_device->read_buffer = malloc (sizeof (Read_Buffer)); if (lexmark_device->read_buffer == NULL) return SANE_STATUS_NO_MEM; /* mark device as present */ lexmark_device->missing = SANE_FALSE; lexmark_device->device_cancelled = SANE_FALSE; /* insert it a the start of the chained list */ lexmark_device->next = first_device; first_device = lexmark_device; num_devices++; DBG (2, " first_device=%p\n", (void *)first_device); return SANE_STATUS_GOOD; } SANE_Status scan_devices(){ DBG (2, "scan_devices\n"); SANE_Char config_line[PATH_MAX]; FILE *fp; const char *lp; num_devices = 0; // -- free existing device we are doning a full re-scan while (first_device){ Lexmark_Device *this_device = first_device; first_device = first_device->next; DBG (2, " free first_device\n"); free(this_device); } fp = sanei_config_open (LEXMARK_X2600_CONFIG_FILE); if (!fp) { DBG (2, " No config no prob...(%s)\n", LEXMARK_X2600_CONFIG_FILE); return SANE_STATUS_GOOD; } while (sanei_config_read (config_line, sizeof (config_line), fp)) { if (config_line[0] == '#') continue; /* ignore line comments */ lp = sanei_config_skip_whitespace (config_line); /* skip empty lines */ if (*lp == 0) continue; DBG (4, " attach_matching_devices(%s)\n", config_line); sanei_usb_init(); sanei_usb_attach_matching_devices (config_line, attach_one); } fclose (fp); return SANE_STATUS_GOOD; } SANE_Status sane_init (SANE_Int *version_code, SANE_Auth_Callback authorize) { DBG_INIT (); DBG (2, "sane_init: version_code %s 0, authorize %s 0\n", version_code == 0 ? "=" : "!=", authorize == 0 ? "=" : "!="); DBG (1, " SANE lexmark_x2600 backend version %d.%d.%d from %s\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD, PACKAGE_STRING); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); SANE_Status status = scan_devices(); initialized = SANE_TRUE; return status; } SANE_Status sane_get_devices (const SANE_Device ***device_list, SANE_Bool local_only) { SANE_Int index; Lexmark_Device *lexmark_device; DBG (2, "sane_get_devices: device_list=%p, local_only=%d num_devices=%d\n", (void *) device_list, local_only, num_devices); //sanei_usb_scan_devices (); SANE_Status status = scan_devices(); if (devlist) free (devlist); devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); if (!devlist) return (SANE_STATUS_NO_MEM); index = 0; lexmark_device = first_device; while (lexmark_device != NULL) { DBG (2, " lexmark_device->missing:%d\n", lexmark_device->missing); if (lexmark_device->missing == SANE_FALSE) { devlist[index] = &(lexmark_device->sane); index++; } lexmark_device = lexmark_device->next; } devlist[index] = 0; *device_list = devlist; return status; } SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { Lexmark_Device *lexmark_device; SANE_Status status; DBG (2, "sane_open: devicename=\"%s\", handle=%p\n", devicename, (void *) handle); /* walk the linked list of scanner device until there is a match * with the device name */ for (lexmark_device = first_device; lexmark_device; lexmark_device = lexmark_device->next) { DBG (10, " devname from list: %s\n", lexmark_device->sane.name); if (strcmp (devicename, "") == 0 || strcmp (devicename, "lexmark") == 0 || strcmp (devicename, lexmark_device->sane.name) == 0) break; } *handle = lexmark_device; status = init_options (lexmark_device); if (status != SANE_STATUS_GOOD) return status; DBG(2, " device `%s' opening devnum: '%d'\n", lexmark_device->sane.name, lexmark_device->devnum); status = sanei_usb_open (lexmark_device->sane.name, &(lexmark_device->devnum)); if (status != SANE_STATUS_GOOD) { DBG (1, " couldn't open device `%s': %s\n", lexmark_device->sane.name, sane_strstatus (status)); return status; } else { DBG (2, " device `%s' successfully opened devnum: '%d'\n", lexmark_device->sane.name, lexmark_device->devnum); } return status; } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { Lexmark_Device *lexmark_device; //DBG (2, "sane_get_option_descriptor: handle=%p, option = %d\n", // (void *) handle, option); /* Check for valid option number */ if ((option < 0) || (option >= NUM_OPTIONS)) return NULL; for (lexmark_device = first_device; lexmark_device; lexmark_device = lexmark_device->next) { if (lexmark_device == handle) break; } if (!lexmark_device) return NULL; if (lexmark_device->opt[option].name) { //DBG (2, " name=%s\n", // lexmark_device->opt[option].name); } return &(lexmark_device->opt[option]); } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void * value, SANE_Word * info) { Lexmark_Device *lexmark_device; SANE_Status status; SANE_Word w; SANE_Int res_selected; DBG (2, "sane_control_option: handle=%p, opt=%d, act=%d, val=%p, info=%p\n", (void *) handle, option, action, (void *) value, (void *) info); for (lexmark_device = first_device; lexmark_device; lexmark_device = lexmark_device->next){ if (lexmark_device == handle) break; } if (value == NULL) return SANE_STATUS_INVAL; switch (action){ case SANE_ACTION_SET_VALUE: if (!SANE_OPTION_IS_SETTABLE (lexmark_device->opt[option].cap)){ return SANE_STATUS_INVAL; } /* Make sure boolean values are only TRUE or FALSE */ if (lexmark_device->opt[option].type == SANE_TYPE_BOOL){ if (! ((*(SANE_Bool *) value == SANE_FALSE) || (*(SANE_Bool *) value == SANE_TRUE))) return SANE_STATUS_INVAL; } /* Check range constraints */ if (lexmark_device->opt[option].constraint_type == SANE_CONSTRAINT_RANGE){ status = sanei_constrain_value (&(lexmark_device->opt[option]), value, info); if (status != SANE_STATUS_GOOD){ DBG (2, " SANE_CONTROL_OPTION: Bad value for range\n"); return SANE_STATUS_INVAL; } } switch (option){ case OPT_NUM_OPTS: case OPT_RESOLUTION: res_selected = *(SANE_Int *) value; // first value is the size of the wordlist! for(int i=1; ival[option].w = *(SANE_Word *) value; } } break; case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: DBG (2, " Option value set to %d (%s)\n", *(SANE_Word *) value, lexmark_device->opt[option].name); lexmark_device->val[option].w = *(SANE_Word *) value; if (lexmark_device->val[OPT_TL_X].w > lexmark_device->val[OPT_BR_X].w){ w = lexmark_device->val[OPT_TL_X].w; lexmark_device->val[OPT_TL_X].w = lexmark_device->val[OPT_BR_X].w; lexmark_device->val[OPT_BR_X].w = w; if (info) *info |= SANE_INFO_RELOAD_PARAMS; } if (lexmark_device->val[OPT_TL_Y].w > lexmark_device->val[OPT_BR_Y].w){ w = lexmark_device->val[OPT_TL_Y].w; lexmark_device->val[OPT_TL_Y].w = lexmark_device->val[OPT_BR_Y].w; lexmark_device->val[OPT_BR_Y].w = w; if (info) *info |= SANE_INFO_RELOAD_PARAMS; } break; case OPT_MODE: strcpy (lexmark_device->val[option].s, value); if (info) *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; } if (info != NULL) *info |= SANE_INFO_RELOAD_PARAMS; break; case SANE_ACTION_GET_VALUE: switch (option){ case OPT_NUM_OPTS: case OPT_RESOLUTION: case OPT_PREVIEW: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: *(SANE_Word *) value = lexmark_device->val[option].w; //DBG (2, " Option value = %d (%s)\n", *(SANE_Word *) value, // lexmark_device->opt[option].name); break; case OPT_MODE: strcpy (value, lexmark_device->val[option].s); break; } break; default: return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { Lexmark_Device *lexmark_device; SANE_Parameters *device_params; SANE_Int width_px; DBG (2, "sane_get_parameters: handle=%p, params=%p\n", (void *) handle, (void *) params); for (lexmark_device = first_device; lexmark_device; lexmark_device = lexmark_device->next) { if (lexmark_device == handle) break; } if (!lexmark_device) return SANE_STATUS_INVAL; // res = lexmark_device->val[OPT_RESOLUTION].w; device_params = &(lexmark_device->params); width_px = lexmark_device->val[OPT_BR_X].w - lexmark_device->val[OPT_TL_X].w; /* 24 bit colour = 8 bits/channel for each of the RGB channels */ device_params->pixels_per_line = width_px; device_params->format = SANE_FRAME_RGB; // SANE_FRAME_GRAY device_params->depth = 8; device_params->bytes_per_line = (SANE_Int) (3 * device_params->pixels_per_line); if (strcmp (lexmark_device->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) != 0) { device_params->format = SANE_FRAME_GRAY; device_params->bytes_per_line = (SANE_Int) (device_params->pixels_per_line); } /* geometry in pixels */ device_params->last_frame = SANE_TRUE; device_params->lines = -1;//lexmark_device->val[OPT_BR_Y].w; DBG (2, " device_params->pixels_per_line=%d\n", device_params->pixels_per_line); DBG (2, " device_params->bytes_per_line=%d\n", device_params->bytes_per_line); DBG (2, " device_params->depth=%d\n", device_params->depth); DBG (2, " device_params->format=%d\n", device_params->format); DBG (2, " SANE_FRAME_GRAY: %d\n", SANE_FRAME_GRAY); DBG (2, " SANE_FRAME_RGB: %d\n", SANE_FRAME_RGB); if (params != 0) { params->format = device_params->format; params->last_frame = device_params->last_frame; params->lines = device_params->lines; params->depth = device_params->depth; params->pixels_per_line = device_params->pixels_per_line; params->bytes_per_line = device_params->bytes_per_line; } return SANE_STATUS_GOOD; } SANE_Status sane_start (SANE_Handle handle) { Lexmark_Device * lexmark_device; SANE_Status status; SANE_Byte * cmd = (SANE_Byte *) malloc (command_with_params_block_size * sizeof (SANE_Byte)); if (cmd == NULL) return SANE_STATUS_NO_MEM; DBG (2, "sane_start: handle=%p initialized=%d\n", (void *) handle, initialized); if (!initialized) return SANE_STATUS_INVAL; for (lexmark_device = first_device; lexmark_device; lexmark_device = lexmark_device->next) { if (lexmark_device == handle) break; } if(lexmark_device == NULL){ DBG (2, " Cannot find device\n"); free(cmd); return SANE_STATUS_IO_ERROR; } lexmark_device->read_buffer->data = NULL; lexmark_device->read_buffer->size = 0; lexmark_device->read_buffer->last_line_bytes_read = 0; lexmark_device->read_buffer->image_line_no = 0; lexmark_device->read_buffer->write_byte_counter = 0; lexmark_device->read_buffer->read_byte_counter = 0; lexmark_device->eof = SANE_FALSE; lexmark_device->device_cancelled = SANE_FALSE; //launch scan commands status = usb_write_then_read(lexmark_device, command1_block, command1_block_size); if (status != SANE_STATUS_GOOD){ free(cmd); return status; } status = usb_write_then_read(lexmark_device, command2_block, command2_block_size); if (status != SANE_STATUS_GOOD){ free(cmd); return status; } build_packet(lexmark_device, 0x05, cmd); status = usb_write_then_read(lexmark_device, cmd, command_with_params_block_size); if (status != SANE_STATUS_GOOD){ free(cmd); return status; } build_packet(lexmark_device, 0x01, cmd);; status = usb_write_then_read(lexmark_device, cmd, command_with_params_block_size); if (status != SANE_STATUS_GOOD){ free(cmd); return status; } free(cmd); return SANE_STATUS_GOOD; } void debug_packet(const SANE_Byte * source, SANE_Int source_size, Debug_Packet dp){ if(dp == READ){ DBG (10, "source READ <<< size=%d\n", source_size); }else{ DBG (10, "source WRITE >>> size=%d\n", source_size); } DBG (10, " %02hhx %02hhx %02hhx %02hhx | %02hhx %02hhx %02hhx %02hhx \n", source[0], source[1], source[2], source[3], source[4], source[5], source[6], source[7]); DBG (10, " %02hhx %02hhx %02hhx %02hhx | %02hhx %02hhx %02hhx %02hhx \n", source[8], source[9], source[10], source[11], source[12], source[13], source[14], source[15]); int debug_offset = 4092; if(source_size > debug_offset){ DBG (10, " %02hhx %02hhx %02hhx %02hhx | %02hhx %02hhx %02hhx %02hhx \n", source[source_size-16-debug_offset], source[source_size-15-debug_offset], source[source_size-14-debug_offset], source[source_size-13-debug_offset], source[source_size-12-debug_offset], source[source_size-11-debug_offset], source[source_size-10-debug_offset], source[source_size-9-debug_offset]); DBG (10, " %02hhx %02hhx %02hhx %02hhx | %02hhx %02hhx %02hhx %02hhx \n", source[source_size-8-debug_offset], source[source_size-7-debug_offset], source[source_size-6-debug_offset], source[source_size-5-debug_offset], source[source_size-4-debug_offset], source[source_size-3-debug_offset], source[source_size-2-debug_offset], source[source_size-1-debug_offset]); } return; } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * data, SANE_Int max_length, SANE_Int * length) { Lexmark_Device * lexmark_device; SANE_Status status; size_t size = transfer_buffer_size; //SANE_Byte buf[size]; DBG (1, "\n"); DBG (1, "sane_read max_length=%d:\n", max_length); for (lexmark_device = first_device; lexmark_device; lexmark_device = lexmark_device->next) { if (lexmark_device == handle) break; } if (lexmark_device->device_cancelled == SANE_TRUE) { DBG (10, "device_cancelled=True \n"); usb_write_then_read(lexmark_device, command_cancel1_block, command_cancel_size); usb_write_then_read(lexmark_device, command_cancel2_block, command_cancel_size); usb_write_then_read(lexmark_device, command_cancel1_block, command_cancel_size); usb_write_then_read(lexmark_device, command_cancel2_block, command_cancel_size); // to empty buffers status = sanei_usb_read_bulk ( lexmark_device->devnum, lexmark_device->transfer_buffer, &size); if(status == SANE_STATUS_GOOD){ status = sanei_usb_read_bulk ( lexmark_device->devnum, lexmark_device->transfer_buffer, &size); } if(status == SANE_STATUS_GOOD){ status = sanei_usb_read_bulk ( lexmark_device->devnum, lexmark_device->transfer_buffer, &size); } return status; } //status = sanei_usb_read_bulk (lexmark_device->devnum, buf, &size); if(!lexmark_device->eof){ DBG (1, " usb_read\n"); status = sanei_usb_read_bulk ( lexmark_device->devnum, lexmark_device->transfer_buffer, &size); if (status != SANE_STATUS_GOOD && status != SANE_STATUS_EOF) { DBG (1, " USB READ Error in sanei_usb_read_bulk, cannot read devnum=%d status=%d size=%ld\n", lexmark_device->devnum, status, size); return status; } DBG (1, " usb_read done size=%ld\n", size); debug_packet(lexmark_device->transfer_buffer, size, READ); }else{ DBG (1, " no usb_read eof reached\n"); } // is last data packet ? if (!lexmark_device->eof && memcmp(last_data_packet, lexmark_device->transfer_buffer, last_data_packet_size) == 0){ // we may still have data left to send in our buffer device->read_buffer->data //length = 0; //return SANE_STATUS_EOF; lexmark_device->eof = SANE_TRUE; DBG (1, " EOF PACKET no more data from scanner\n"); return SANE_STATUS_GOOD; } // cancel packet received? if (memcmp(cancel_packet, lexmark_device->transfer_buffer, cancel_packet_size) == 0){ length = 0; return SANE_STATUS_CANCELLED; } if (memcmp(empty_line_data_packet, lexmark_device->transfer_buffer, empty_line_data_packet_size) == 0){ return SANE_STATUS_GOOD; } if (memcmp(unknown_a_data_packet, lexmark_device->transfer_buffer, unknown_a_data_packet_size) == 0){ return SANE_STATUS_GOOD; } if (memcmp(unknown_b_data_packet, lexmark_device->transfer_buffer, unknown_b_data_packet_size) == 0){ return SANE_STATUS_GOOD; } if (memcmp(unknown_c_data_packet, lexmark_device->transfer_buffer, unknown_c_data_packet_size) == 0){ return SANE_STATUS_GOOD; } if (memcmp(unknown_d_data_packet, lexmark_device->transfer_buffer, unknown_d_data_packet_size) == 0){ return SANE_STATUS_GOOD; } if (memcmp(unknown_e_data_packet, lexmark_device->transfer_buffer, unknown_e_data_packet_size) == 0){ return SANE_STATUS_GOOD; } status = clean_and_copy_data( lexmark_device->transfer_buffer, size, data, length, lexmark_device->params.format, max_length, handle); return status; } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { DBG (2, "sane_set_io_mode: handle = %p, non_blocking = %d\n", (void *) handle, non_blocking); if (non_blocking) return SANE_STATUS_UNSUPPORTED; return SANE_STATUS_GOOD; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { DBG (2, "sane_get_select_fd: handle = %p, fd %s 0\n", (void *) handle, fd ? "!=" : "="); return SANE_STATUS_UNSUPPORTED; } void sane_cancel (SANE_Handle handle) { Lexmark_Device * lexmark_device; DBG (2, "sane_cancel: handle = %p\n", (void *) handle); for (lexmark_device = first_device; lexmark_device; lexmark_device = lexmark_device->next) { if (lexmark_device == handle) break; } sanei_usb_reset (lexmark_device->devnum); lexmark_device->device_cancelled = SANE_TRUE; } void sane_close (SANE_Handle handle) { Lexmark_Device * lexmark_device; DBG (2, "sane_close: handle=%p\n", (void *) handle); for (lexmark_device = first_device; lexmark_device; lexmark_device = lexmark_device->next) { if (lexmark_device == handle) break; } sanei_usb_close (lexmark_device->devnum); } void sane_exit (void) { Lexmark_Device *lexmark_device, *next_lexmark_device; DBG (2, "sane_exit\n"); if (!initialized) return; for (lexmark_device = first_device; lexmark_device; lexmark_device = next_lexmark_device) { next_lexmark_device = lexmark_device->next; free (lexmark_device->transfer_buffer); free (lexmark_device->read_buffer); free (lexmark_device); } if (devlist) free (devlist); sanei_usb_exit(); initialized = SANE_FALSE; } backends-1.3.0/backend/lexmark_x2600.conf.in000066400000000000000000000000411456256263500204430ustar00rootroot00000000000000# X26xx series usb 0x043d 0x011d backends-1.3.0/backend/lexmark_x2600.h000066400000000000000000000100451456256263500173450ustar00rootroot00000000000000/* lexmark_x2600.c: SANE backend for Lexmark x2600 scanners. (C) 2023 "Benoit Juin" 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, see . 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 LEXMARK_X2600_H #define LEXMARK_X2600_H #define BACKEND_NAME lexmark_x2600 #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "../include/_stdint.h" #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_backend.h" typedef struct Read_Buffer { SANE_Int gray_offset; SANE_Int max_gray_offset; SANE_Int region; SANE_Int red_offset; SANE_Int green_offset; SANE_Int blue_offset; SANE_Int max_red_offset; SANE_Int max_green_offset; SANE_Int max_blue_offset; SANE_Byte *data; SANE_Byte *readptr; SANE_Byte *writeptr; SANE_Byte *max_writeptr; size_t size; size_t linesize; size_t last_line_bytes_read; SANE_Bool empty; SANE_Int image_line_no; SANE_Int write_byte_counter; SANE_Int read_byte_counter; } Read_Buffer; typedef enum { OPT_NUM_OPTS = 0, OPT_MODE, OPT_RESOLUTION, OPT_PREVIEW, OPT_GEOMETRY_GROUP, OPT_TL_X, /* top-left x */ OPT_TL_Y, /* top-left y */ OPT_BR_X, /* bottom-right x */ OPT_BR_Y, /* bottom-right y */ /* must come last: */ NUM_OPTIONS } Lexmark_Options; typedef enum { READ = 0, WRITE = 1, } Debug_Packet; typedef struct Lexmark_Device { struct Lexmark_Device *next; SANE_Bool missing; /**< devices has been unplugged or swtiched off */ SANE_Device sane; SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; SANE_Parameters params; SANE_Int devnum; long data_size; SANE_Bool initialized; SANE_Bool eof; SANE_Int x_dpi; SANE_Int y_dpi; long data_ctr; SANE_Bool device_cancelled; SANE_Int cancel_ctr; SANE_Byte *transfer_buffer; size_t bytes_read; size_t bytes_remaining; size_t bytes_in_buffer; SANE_Byte *read_pointer; Read_Buffer *read_buffer; } Lexmark_Device; void debug_packet(const SANE_Byte * source, SANE_Int source_size, Debug_Packet dp); #endif /* LEXMARK_X2600_H */ backends-1.3.0/backend/lm9830.h000066400000000000000000000025421456256263500160020ustar00rootroot00000000000000/* (c) 2001 Nathan Rutman nathan@gordian.com 11/13/01 National Semi LM9830 scanner-on-a-chip register defs */ #ifndef LM9830_H #define LM9830_H /* LM9830 registers (see lm9830 datasheet for a full description */ #define IMAGE_DATA_AVAIL 0x01 #define STATUS 0x02 #define DATAPORT_TARGET 0x03 #define DATAPORT_ADDR 0x04 #define DATAPORT 0x06 #define COMMAND 0x07 #define CLOCK_DIV 0x08 /* Master clock divider */ #define ACTIVE_PX_START 0x1e /* Active pixel start */ #define LINE_END 0x20 #define DATA_PX_START 0x22 #define DATA_PX_END 0x24 #define COLOR_MODE 0x26 #define LAMP_R_ON 0x2c #define LAMP_R_OFF 0x2e #define LAMP_G_ON 0x30 #define LAMP_G_OFF 0x32 #define LAMP_B_ON 0x34 #define LAMP_B_OFF 0x36 #define PARALLEL_PORT 0x42 /* Parallel port settings */ #define MICROSTEP 0x45 #define STEP_SIZE 0x46 #define FAST_STEP 0x48 #define SKIP_STEPS 0x4a #define BUFFER_LIMIT 0x4e /* Pause scanning when buffer is this full */ #define BUFFER_RESUME 0x4f #define REVERSE_STEPS 0x50 #define STEP_PWM 0x57 #define PAPER_SENSOR 0x58 /* Register 02 flags */ #define STATUS_HOME 0x02 #define STATUS_PAUSE 0x10 #define STATUS_POWER 0x20 /* Register 03 flags */ #define DP_GAMMA 0x00 #define DP_OFFSET 0x01 #define DP_R 0x00 #define DP_G 0x02 #define DP_B 0x04 /* Register 04 flags */ #define DP_WRITE 0x0000 #define DP_READ 0x2000 #endif /* LM9830_H */ backends-1.3.0/backend/ma1509.c000066400000000000000000001517511456256263500157640ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. (C) 2003 Henning Meier-Geinitz . Based on the mustek (SCSI) backend. 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, see . 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 scanners based on the Mustek MA-1509 chipset. Currently the Mustek BearPaw 1200F is known to work. */ /**************************************************************************/ /* ma1509 backend version */ #define BUILD 3 /**************************************************************************/ #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_usb.h" #define BACKEND_NAME ma1509 #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_config.h" #include "ma1509.h" #ifndef SANE_I18N #define SANE_I18N(text) text #endif /* Debug level from sanei_init_debug */ static SANE_Int debug_level; static SANE_Int num_devices; static Ma1509_Device *first_dev; static Ma1509_Scanner *first_handle; static const SANE_Device **devlist = 0; static int warmup_time = MA1509_WARMUP_TIME; /* Array of newly attached devices */ static Ma1509_Device **new_dev; /* Length of new_dev array */ static SANE_Int new_dev_len; /* Number of entries allocated for new_dev */ static SANE_Int new_dev_alloced; static SANE_String_Const mode_list[] = { SANE_VALUE_SCAN_MODE_LINEART, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_COLOR, 0 }; static SANE_String_Const ta_source_list[] = { SANE_I18N ("Flatbed"), SANE_I18N ("Transparency Adapter"), 0 }; static SANE_Word resolution_list[] = { 9, 50, 100, 150, 200, 300, 400, 450, 500, 600 }; static const SANE_Range u8_range = { 0, /* minimum */ 255, /* maximum */ 0 /* quantization */ }; /* "SCSI" command buffers used by the backend */ static const SANE_Byte scsi_inquiry[] = { 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, INQ_LEN, 0x00 }; static const SANE_Byte scsi_test_unit_ready[] = { 0x03, 0x01, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00 }; static const SANE_Byte scsi_set_window[] = { 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00 }; static void print_data_buffer (const SANE_Byte * buffer, size_t len) { SANE_Byte buffer_byte_list[50]; SANE_Byte buffer_byte[5]; const SANE_Byte *pp; buffer_byte_list[0] = '\0'; for (pp = buffer; pp < (buffer + len); pp++) { sprintf ((SANE_String) buffer_byte, " %02x", *pp); strcat ((SANE_String) buffer_byte_list, (SANE_String) buffer_byte); if (((pp - buffer) % 0x10 == 0x0f) || (pp >= (buffer + len - 1))) { DBG (5, "buffer: %s\n", buffer_byte_list); buffer_byte_list[0] = '\0'; } } } static SANE_Status ma1509_cmd (Ma1509_Scanner * s, const SANE_Byte * cmd, SANE_Byte * data, size_t * data_size) { SANE_Status status; size_t size; #define MA1509_WRITE_LIMIT (1024 * 64) #define MA1509_READ_LIMIT (1024 * 256) DBG (5, "ma1509_cmd: fd=%d, cmd=%p, data=%p, data_size=%ld\n", s->fd, (void *) cmd, (void *) data, (long int) (data_size ? *data_size : 0)); DBG (5, "ma1509_cmd: cmd = %02x %02x %02x %02x %02x %02x %02x %02x \n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6], cmd[7]); size = MA1509_COMMAND_LENGTH; status = sanei_usb_write_bulk (s->fd, cmd, &size); if (status != SANE_STATUS_GOOD || size != MA1509_COMMAND_LENGTH) { DBG (5, "ma1509_cmd: sanei_usb_write_bulk returned %s (size = %ld, expected %d)\n", sane_strstatus (status), (long int) size, MA1509_COMMAND_LENGTH); return status; } if (cmd[1] == 1) { /* receive data */ if (data && data_size && *data_size) { size_t bytes_left = *data_size; DBG (5, "ma1509_cmd: trying to receive %ld bytes of data\n", (long int) *data_size); while (status == SANE_STATUS_GOOD && bytes_left > 0) { size = bytes_left; if (size > MA1509_READ_LIMIT) size = MA1509_READ_LIMIT; status = sanei_usb_read_bulk (s->fd, data + *data_size - bytes_left, &size); if (status != SANE_STATUS_GOOD) { DBG (1, "ma1509_cmd: sanei_usb_read_bulk returned %s\n", sane_strstatus (status)); return status; } bytes_left -= size; DBG (5, "ma1509_cmd: read %ld bytes, %ld bytes to go\n", (long int) size, (long int) bytes_left); } if (debug_level >= 5) print_data_buffer (data, *data_size); } } else { /* send data */ if (data && data_size && *data_size) { size_t bytes_left = *data_size; DBG (5, "ma1509_cmd: sending %ld bytes of data\n", (long int) *data_size); if (debug_level >= 5) print_data_buffer (data, *data_size); while (status == SANE_STATUS_GOOD && bytes_left > 0) { size = bytes_left; if (size > MA1509_WRITE_LIMIT) size = MA1509_WRITE_LIMIT; status = sanei_usb_write_bulk (s->fd, data + *data_size - bytes_left, &size); if (status != SANE_STATUS_GOOD) { DBG (1, "ma1509_cmd: sanei_usb_write_bulk returned %s\n", sane_strstatus (status)); return status; } bytes_left -= size; DBG (5, "ma1509_cmd: wrote %ld bytes, %ld bytes to go\n", (long int) size, (long int) bytes_left); } } } DBG (5, "ma1509_cmd: finished: data_size=%ld, status=%s\n", (long int) (data_size ? *data_size : 0), sane_strstatus (status)); return status; } static SANE_Status test_unit_ready (Ma1509_Scanner * s) { SANE_Status status; SANE_Byte buffer[0x04]; size_t size = sizeof (buffer); status = ma1509_cmd (s, scsi_test_unit_ready, buffer, &size); if (status != SANE_STATUS_GOOD) { DBG (1, "test_unit_ready: ma1509_cmd failed: %s\n", sane_strstatus (status)); return status; } if (buffer[1] == 0x14) s->hw->has_adf = SANE_TRUE; else s->hw->has_adf = SANE_FALSE; return status; } static SANE_Status attach (SANE_String_Const devname, Ma1509_Device ** devp) { SANE_Int fw_revision; SANE_Byte result[INQ_LEN]; SANE_Byte inquiry_byte_list[50], inquiry_text_list[17]; SANE_Byte inquiry_byte[5], inquiry_text[5]; SANE_Byte *model_name = result + 44; Ma1509_Scanner s; Ma1509_Device *dev, new_dev; SANE_Status status; size_t size; SANE_Byte *pp; SANE_Word vendor, product; if (devp) *devp = 0; for (dev = first_dev; dev; dev = dev->next) if (strcmp (dev->sane.name, devname) == 0) { if (devp) *devp = dev; return SANE_STATUS_GOOD; } memset (&new_dev, 0, sizeof (new_dev)); memset (&s, 0, sizeof (s)); s.hw = &new_dev; DBG (3, "attach: trying device %s\n", devname); status = sanei_usb_open (devname, &s.fd); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: sanei_usb_open failed: %s\n", sane_strstatus (status)); return status; } status = sanei_usb_get_vendor_product (s.fd, &vendor, &product); if (status != SANE_STATUS_GOOD && status != SANE_STATUS_UNSUPPORTED) { DBG (1, "attach: sanei_usb_get_vendor_product failed: %s\n", sane_strstatus (status)); sanei_usb_close (s.fd); return status; } if (status == SANE_STATUS_UNSUPPORTED) { DBG (3, "attach: can't detect vendor/product, trying anyway\n"); } else if (vendor != 0x055f || product != 0x0010) { DBG (1, "attach: unknown vendor/product (0x%x/0x%x)\n", vendor, product); sanei_usb_close (s.fd); return SANE_STATUS_UNSUPPORTED; } DBG (4, "attach: sending TEST_UNIT_READY\n"); status = test_unit_ready (&s); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: test_unit_ready device %s failed (%s)\n", devname, sane_strstatus (status)); sanei_usb_close (s.fd); return status; } DBG (4, "attach: sending INQUIRY\n"); size = sizeof (result); memset (result, 0, sizeof (result)); status = ma1509_cmd (&s, scsi_inquiry, result, &size); if (status != SANE_STATUS_GOOD || size != INQ_LEN) { DBG (1, "attach: inquiry for device %s failed (%s)\n", devname, sane_strstatus (status)); sanei_usb_close (s.fd); return status; } sanei_usb_close (s.fd); if ((result[0] & 0x1f) != 0x06) { DBG (1, "attach: device %s doesn't look like a scanner at all (%d)\n", devname, result[0] & 0x1f); return SANE_STATUS_INVAL; } if (debug_level >= 5) { /* print out inquiry */ DBG (5, "attach: inquiry output:\n"); inquiry_byte_list[0] = '\0'; inquiry_text_list[0] = '\0'; for (pp = result; pp < (result + INQ_LEN); pp++) { sprintf ((SANE_String) inquiry_text, "%c", (*pp < 127) && (*pp > 31) ? *pp : '.'); strcat ((SANE_String) inquiry_text_list, (SANE_String) inquiry_text); sprintf ((SANE_String) inquiry_byte, " %02x", *pp); strcat ((SANE_String) inquiry_byte_list, (SANE_String) inquiry_byte); if ((pp - result) % 0x10 == 0x0f) { DBG (5, "%s %s\n", inquiry_byte_list, inquiry_text_list); inquiry_byte_list[0] = '\0'; inquiry_text_list[0] = '\0'; } } } /* get firmware revision as BCD number: */ fw_revision = (result[32] - '0') << 8 | (result[34] - '0') << 4 | (result[35] - '0'); DBG (4, "attach: firmware revision %d.%02x\n", fw_revision >> 8, fw_revision & 0xff); dev = malloc (sizeof (*dev)); if (!dev) return SANE_STATUS_NO_MEM; memcpy (dev, &new_dev, sizeof (*dev)); dev->name = strdup (devname); if (!dev->name) return SANE_STATUS_NO_MEM; dev->sane.name = (SANE_String_Const) dev->name; dev->sane.vendor = "Mustek"; dev->sane.type = "flatbed scanner"; dev->x_range.min = 0; dev->y_range.min = 0; dev->x_range.quant = SANE_FIX (0.1); dev->y_range.quant = SANE_FIX (0.1); dev->x_trans_range.min = 0; dev->y_trans_range.min = 0; /* default to something really small to be on the safe side: */ dev->x_trans_range.max = SANE_FIX (8.0 * MM_PER_INCH); dev->y_trans_range.max = SANE_FIX (5.0 * MM_PER_INCH); dev->x_trans_range.quant = SANE_FIX (0.1); dev->y_trans_range.quant = SANE_FIX (0.1); DBG (3, "attach: scanner id: %.11s\n", model_name); /* BearPaw 1200F (SCSI-over-USB) */ if (strncmp ((SANE_String) model_name, " B06", 4) == 0) { dev->x_range.max = SANE_FIX (211.3); dev->y_range.min = SANE_FIX (0); dev->y_range.max = SANE_FIX (296.7); dev->x_trans_range.min = SANE_FIX (0); dev->y_trans_range.min = SANE_FIX (0); dev->x_trans_range.max = SANE_FIX (150.0); dev->y_trans_range.max = SANE_FIX (175.0); dev->sane.model = "BearPaw 1200F"; } else { DBG (0, "attach: this scanner (ID: %s) is not supported yet\n", model_name); DBG (0, "attach: please set the debug level to 5 and send a debug " "report\n"); DBG (0, "attach: to henning@meier-geinitz.de (export " "SANE_DEBUG_MA1509=5\n"); DBG (0, "attach: scanimage -L 2>debug.txt). Thank you.\n"); free (dev); return SANE_STATUS_INVAL; } DBG (2, "attach: found Mustek %s %s %s%s\n", dev->sane.model, dev->sane.type, dev->has_ta ? "(TA)" : "", dev->has_adf ? "(ADF)" : ""); ++num_devices; dev->next = first_dev; first_dev = dev; if (devp) *devp = dev; return SANE_STATUS_GOOD; } 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; } static SANE_Status init_options (Ma1509_Scanner * s) { SANE_Int i; memset (s->opt, 0, sizeof (s->opt)); memset (s->val, 0, sizeof (s->val)); for (i = 0; i < NUM_OPTIONS; ++i) { s->opt[i].size = sizeof (SANE_Word); s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } s->opt[OPT_NUM_OPTS].name = ""; s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* "Mode" group: */ s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode"); s->opt[OPT_MODE_GROUP].desc = ""; s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MODE_GROUP].cap = 0; s->opt[OPT_MODE_GROUP].size = 0; s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* scan mode */ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; 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].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_MODE].size = max_string_size (mode_list); s->opt[OPT_MODE].constraint.string_list = mode_list; s->val[OPT_MODE].s = strdup (mode_list[1]); if (!s->val[OPT_MODE].s) return SANE_STATUS_NO_MEM; /* resolution */ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_RESOLUTION].constraint.word_list = resolution_list; s->val[OPT_RESOLUTION].w = 50; /* source */ s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE; s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE; s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE; s->opt[OPT_SOURCE].type = SANE_TYPE_STRING; s->opt[OPT_SOURCE].size = max_string_size (ta_source_list); s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_SOURCE].constraint.string_list = ta_source_list; s->val[OPT_SOURCE].s = strdup (ta_source_list[0]); if (!s->val[OPT_SOURCE].s) return SANE_STATUS_NO_MEM; s->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE; /* preview */ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; s->val[OPT_PREVIEW].w = 0; /* "Geometry" group: */ s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry"); s->opt[OPT_GEOMETRY_GROUP].desc = ""; s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_GEOMETRY_GROUP].size = 0; s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* top-left x */ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; s->opt[OPT_TL_X].unit = SANE_UNIT_MM; s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_X].constraint.range = &s->hw->x_range; s->val[OPT_TL_X].w = s->hw->x_range.min; /* top-left y */ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range; s->val[OPT_TL_Y].w = s->hw->y_range.min; /* bottom-right x */ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; s->opt[OPT_BR_X].unit = SANE_UNIT_MM; s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_X].constraint.range = &s->hw->x_range; s->val[OPT_BR_X].w = s->hw->x_range.max; /* bottom-right y */ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range; s->val[OPT_BR_Y].w = s->hw->y_range.max; /* "Enhancement" group: */ s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement"); s->opt[OPT_ENHANCEMENT_GROUP].desc = ""; s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_ENHANCEMENT_GROUP].size = 0; s->opt[OPT_ENHANCEMENT_GROUP].cap = 0; s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* 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_INT; s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE; s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_THRESHOLD].constraint.range = &u8_range; s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; s->val[OPT_THRESHOLD].w = 128; /* custom-gamma table */ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL; s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE; /* red gamma vector */ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_R].size = MA1509_GAMMA_SIZE * sizeof (SANE_Word); s->val[OPT_GAMMA_VECTOR_R].wa = &s->red_gamma_table[0]; s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range; s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; for (i = 0; i < MA1509_GAMMA_SIZE; i++) s->red_gamma_table[i] = i * MA1509_GAMMA_SIZE / 256; /* green gamma vector */ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_G].size = MA1509_GAMMA_SIZE * sizeof (SANE_Word); s->val[OPT_GAMMA_VECTOR_G].wa = &s->green_gamma_table[0]; s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range; s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; for (i = 0; i < MA1509_GAMMA_SIZE; i++) s->green_gamma_table[i] = i * MA1509_GAMMA_SIZE / 256; /* blue gamma vector */ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_B].size = MA1509_GAMMA_SIZE * sizeof (SANE_Word); s->val[OPT_GAMMA_VECTOR_B].wa = &s->blue_gamma_table[0]; s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range; s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; for (i = 0; i < MA1509_GAMMA_SIZE; i++) s->blue_gamma_table[i] = i * MA1509_GAMMA_SIZE / 256; return SANE_STATUS_GOOD; } static SANE_Status attach_one_device (SANE_String_Const devname) { Ma1509_Device *dev; attach (devname, &dev); if (dev) { /* Keep track of newly attached devices so we can set options as necessary. */ if (new_dev_len >= new_dev_alloced) { new_dev_alloced += 4; if (new_dev) new_dev = realloc (new_dev, new_dev_alloced * sizeof (new_dev[0])); else new_dev = malloc (new_dev_alloced * sizeof (new_dev[0])); if (!new_dev) { DBG (1, "attach_one_device: out of memory\n"); return SANE_STATUS_NO_MEM; } } new_dev[new_dev_len++] = dev; } return SANE_STATUS_GOOD; } static SANE_Status set_window (Ma1509_Scanner * s) { SANE_Byte buffer[0x30], *cp; double pixels_per_mm; size_t size = sizeof (buffer); SANE_Status status; SANE_Int tlx, tly, width, height; SANE_Int offset = 0; struct timeval now; long remaining_time; /* check if lamp is warmed up */ gettimeofday (&now, 0); remaining_time = warmup_time - (now.tv_sec - s->lamp_time); if (remaining_time > 0) { DBG (0, "Warm-up in progress: please wait %2ld seconds\n", remaining_time); sleep (remaining_time); } memset (buffer, 0, size); cp = buffer; STORE16B (cp, 0); /* window identifier */ STORE16B (cp, s->val[OPT_RESOLUTION].w); STORE16B (cp, 0); /* not used acc. to specs */ pixels_per_mm = s->val[OPT_RESOLUTION].w / MM_PER_INCH; tlx = SANE_UNFIX (s->val[OPT_TL_X].w) * pixels_per_mm + 0.5; tly = SANE_UNFIX (s->val[OPT_TL_Y].w) * pixels_per_mm + 0.5; width = (SANE_UNFIX (s->val[OPT_BR_X].w) - SANE_UNFIX (s->val[OPT_TL_X].w)) * pixels_per_mm + 0.5; height = (SANE_UNFIX (s->val[OPT_BR_Y].w) - SANE_UNFIX (s->val[OPT_TL_Y].w)) * pixels_per_mm + 0.5 + offset; if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) == 0) { width /= 64; width *= 64; if (!width) width = 64; } else { width /= 8; width *= 8; if (!width) width = 8; } DBG (4, "set_window: tlx=%d (%d mm); tly=%d (%d mm); width=%d (%d mm); " "height=%d (%d mm)\n", tlx, (int) (tlx / pixels_per_mm), tly, (int) (tly / pixels_per_mm), width, (int) (width / pixels_per_mm), height, (int) (height / pixels_per_mm)); STORE16B (cp, 0); STORE16B (cp, tlx); STORE16B (cp, 0); STORE16B (cp, tly); *cp++ = 0x14; *cp++ = 0xc0; STORE16B (cp, width); *cp++ = 0x28; *cp++ = 0x20; STORE16B (cp, height); s->hw->ppl = width; s->hw->bpl = s->hw->ppl; s->hw->lines = height; *cp++ = 0x00; /* brightness, not impl. */ /* threshold */ if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) == 0) *cp++ = (SANE_Byte) s->val[OPT_THRESHOLD].w; else *cp++ = 0x80; *cp++ = 0x00; /* contrast, not impl. */ *cp++ = 0x00; /* ??? . */ /* Note that 'image composition' has no meaning for the SE series */ /* Mode selection is accomplished solely by bits/pixel (1, 8, 24) */ if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0) { *cp++ = 24; /* 24 bits/pixel in color mode */ s->hw->bpl *= 3; } else if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY) == 0) *cp++ = 8; /* 8 bits/pixel in gray mode */ else { *cp++ = 1; /* 1 bit/pixel in lineart mode */ s->hw->bpl /= 8; } cp += 13; /* skip reserved bytes */ *cp++ = 0x00; /* lamp mode */ if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) != 0) *cp++ = 0x02; /* ??? */ status = ma1509_cmd (s, scsi_set_window, buffer, &size); if (status != SANE_STATUS_GOOD) { DBG (1, "set_window: ma1509_cmd failed: %s\n", sane_strstatus (status)); return status; } return status; } static SANE_Status calibration (Ma1509_Scanner * s) { SANE_Byte cmd[0x08], *buffer, *calibration_buffer; SANE_Status status; SANE_Int ppl = 5312; SANE_Int lines = 40; size_t total_size = lines * ppl; SANE_Int color, column, line; buffer = malloc (total_size * 3); if (!buffer) { DBG (1, "calibration: couldn't malloc %lu bytes for calibration buffer\n", (u_long) (total_size * 3)); return SANE_STATUS_NO_MEM; } memset (buffer, 0x00, total_size); memset (cmd, 0, 8); cmd[0] = 0x28; /* read data */ cmd[1] = 0x01; /* read */ cmd[2] = 0x01; /* calibration */ cmd[4] = (total_size >> 16) & 0xff; cmd[5] = (total_size >> 8) & 0xff; cmd[6] = total_size & 0xff; total_size *= 3; status = ma1509_cmd (s, cmd, buffer, &total_size); if (status != SANE_STATUS_GOOD) { DBG (1, "calibration: ma1509_cmd read data failed: %s\n", sane_strstatus (status)); free (buffer); return status; } calibration_buffer = malloc (ppl); if (!calibration_buffer) { DBG (1, "calibration: couldn't malloc %d bytes for calibration buffer\n", ppl); return SANE_STATUS_NO_MEM; } memset (calibration_buffer, 0x00, ppl); memset (cmd, 0, 8); cmd[0] = 0x2a; /* send data */ cmd[1] = 0x00; /* write */ cmd[2] = 0x01; /* calibration */ cmd[5] = (ppl >> 8) & 0xff; cmd[6] = ppl & 0xff; for (color = 1; color < 4; color++) { cmd[4] = color; for (column = 0; column < ppl; column++) { SANE_Int average = 0; for (line = 0; line < lines; line++) average += buffer[line * ppl * 3 + column * 3 + (color - 1)]; average /= lines; if (average < 1) average = 1; if (average > 255) average = 255; average = (256 * 256) / average - 256; if (average < 0) average = 0; if (average > 255) average = 255; calibration_buffer[column] = average; } total_size = ppl; status = ma1509_cmd (s, cmd, calibration_buffer, &total_size); if (status != SANE_STATUS_GOOD) { DBG (1, "calibration: ma1509_cmd send data failed: %s\n", sane_strstatus (status)); free (buffer); free (calibration_buffer); return status; } } free (buffer); free (calibration_buffer); DBG (4, "calibration: done\n"); return status; } static SANE_Status send_gamma (Ma1509_Scanner * s) { SANE_Byte cmd[0x08], *buffer; SANE_Status status; size_t total_size = MA1509_GAMMA_SIZE; SANE_Int color; buffer = malloc (total_size); if (!buffer) { DBG (1, "send_gamma: couldn't malloc %lu bytes for gamma buffer\n", (u_long) total_size); return SANE_STATUS_NO_MEM; } memset (cmd, 0, 8); cmd[0] = 0x2a; /* send data */ cmd[1] = 0x00; /* write */ cmd[2] = 0x03; /* gamma */ cmd[5] = (total_size >> 8) & 0xff; cmd[6] = total_size & 0xff; for (color = 1; color < 4; color++) { unsigned int i; if (s->val[OPT_CUSTOM_GAMMA].w) { SANE_Int *int_buffer; if (color == 1) int_buffer = s->red_gamma_table; else if (color == 2) int_buffer = s->green_gamma_table; else int_buffer = s->blue_gamma_table; for (i = 0; i < total_size; i++) buffer[i] = int_buffer[i]; } else { /* linear tables */ for (i = 0; i < total_size; i++) buffer[i] = i * 256 / total_size; } cmd[4] = color; status = ma1509_cmd (s, cmd, buffer, &total_size); if (status != SANE_STATUS_GOOD) { DBG (1, "send_gamma: ma1509_cmd send data failed: %s\n", sane_strstatus (status)); free (buffer); return status; } } if (!s->val[OPT_CUSTOM_GAMMA].w) free (buffer); DBG (4, "send_gamma: done\n"); return status; } static SANE_Status start_scan (Ma1509_Scanner * s) { SANE_Byte cmd[8]; SANE_Status status; DBG (4, "start_scan\n"); memset (cmd, 0, 8); cmd[0] = 0x1b; cmd[1] = 0x01; cmd[2] = 0x01; status = ma1509_cmd (s, cmd, NULL, NULL); if (status != SANE_STATUS_GOOD) { DBG (1, "start_scan: ma1509_cmd failed: %s\n", sane_strstatus (status)); return status; } return status; } static SANE_Status turn_lamp (Ma1509_Scanner * s, SANE_Bool is_on) { SANE_Status status; SANE_Byte buffer[0x30]; size_t size = sizeof (buffer); struct timeval lamp_time; DBG (4, "turn_lamp %s\n", is_on ? "on" : "off"); memset (buffer, 0, size); if (is_on) buffer[0x28] = 0x01; else buffer[0x28] = 0x02; status = ma1509_cmd (s, scsi_set_window, buffer, &size); if (status != SANE_STATUS_GOOD) { DBG (1, "turn_lamp: ma1509_cmd set_window failed: %s\n", sane_strstatus (status)); return status; } gettimeofday (&lamp_time, 0); s->lamp_time = lamp_time.tv_sec; return status; } static SANE_Status stop_scan (Ma1509_Scanner * s) { SANE_Byte cmd[8]; SANE_Status status; DBG (4, "stop_scan\n"); memset (cmd, 0, 8); cmd[0] = 0x1b; cmd[1] = 0x01; cmd[2] = 0x00; status = ma1509_cmd (s, cmd, NULL, NULL); if (status != SANE_STATUS_GOOD) { DBG (1, "stop_scan: ma1509_cmd failed: %s\n", sane_strstatus (status)); return status; } DBG (4, "stop_scan: scan stopped\n"); return status; } static SANE_Status start_read_data (Ma1509_Scanner * s) { SANE_Byte cmd[8]; SANE_Status status; SANE_Int total_size = s->hw->ppl * s->hw->lines; if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) == 0) total_size /= 8; memset (cmd, 0, 8); cmd[0] = 0x28; /* read data */ cmd[1] = 0x01; /* read */ cmd[2] = 0x00; /* scan data */ cmd[3] = (total_size >> 24) & 0xff; cmd[4] = (total_size >> 16) & 0xff; cmd[5] = (total_size >> 8) & 0xff; cmd[6] = total_size & 0xff; status = ma1509_cmd (s, cmd, NULL, NULL); if (status != SANE_STATUS_GOOD) { DBG (1, "stop_scan: ma1509_cmd failed: %s\n", sane_strstatus (status)); return status; } return status; } static SANE_Status read_data (Ma1509_Scanner * s, SANE_Byte * buffer, SANE_Int * size) { size_t local_size = *size; SANE_Status status; status = sanei_usb_read_bulk (s->fd, buffer, &local_size); if (status != SANE_STATUS_GOOD) { DBG (1, "read_data: sanei_usb_read_bulk failed: %s\n", sane_strstatus (status)); return status; } *size = local_size; return status; } /**************************************************************************/ /* SANE API calls */ /**************************************************************************/ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { SANE_Char line[PATH_MAX], *word, *end; SANE_String_Const cp; SANE_Int linenumber; FILE *fp; DBG_INIT (); #ifdef DBG_LEVEL debug_level = DBG_LEVEL; #else debug_level = 0; #endif DBG (2, "SANE ma1509 backend version %d.%d build %d from %s\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD, PACKAGE_STRING); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); DBG (4, "sane_init: authorize %s null\n", authorize ? "!=" : "=="); sanei_usb_init (); num_devices = 0; first_dev = 0; first_handle = 0; devlist = 0; new_dev = 0; new_dev_len = 0; new_dev_alloced = 0; fp = sanei_config_open (MA1509_CONFIG_FILE); if (!fp) { /* default to /dev/usb/scanner0 instead of insisting on config file */ DBG (3, "sane_init: couldn't find config file (%s), trying " "/dev/usb/scanner0 directly\n", MA1509_CONFIG_FILE); attach ("/dev/usb/scanner0", 0); return SANE_STATUS_GOOD; } linenumber = 0; DBG (4, "sane_init: reading config file `%s'\n", MA1509_CONFIG_FILE); while (sanei_config_read (line, sizeof (line), fp)) { word = 0; linenumber++; cp = sanei_config_get_string (line, &word); if (!word || cp == line) { DBG (5, "sane_init: config file line %d: ignoring empty line\n", linenumber); if (word) free (word); continue; } if (word[0] == '#') { DBG (5, "sane_init: config file line %d: ignoring comment line\n", linenumber); free (word); continue; } if (strcmp (word, "option") == 0) { free (word); word = 0; cp = sanei_config_get_string (cp, &word); if (!word) { DBG (1, "sane_init: config file line %d: missing quotation mark?\n", linenumber); continue; } if (strcmp (word, "warmup-time") == 0) { long local_warmup_time; free (word); word = 0; cp = sanei_config_get_string (cp, &word); if (!word) { DBG (1, "sane_init: config file line %d: missing quotation mark?\n", linenumber); continue; } errno = 0; local_warmup_time = strtol (word, &end, 0); if (end == word) { DBG (3, "sane-init: config file line %d: warmup-time must " "have a parameter; using default (%d)\n", linenumber, warmup_time); } else if (errno) { DBG (3, "sane-init: config file line %d: warmup-time `%s' " "is invalid (%s); using default (%d)\n", linenumber, word, strerror (errno), warmup_time); } else { warmup_time = local_warmup_time; DBG (4, "sane_init: config file line %d: warmup-time set " "to %d seconds\n", linenumber, warmup_time); } if (word) free (word); word = 0; } else { DBG (3, "sane_init: config file line %d: ignoring unknown " "option `%s'\n", linenumber, word); if (word) free (word); word = 0; } } else { new_dev_len = 0; DBG (4, "sane_init: config file line %d: trying to attach `%s'\n", linenumber, line); sanei_usb_attach_matching_devices (line, attach_one_device); if (word) free (word); word = 0; } } if (new_dev_alloced > 0) { new_dev_len = new_dev_alloced = 0; free (new_dev); } fclose (fp); return SANE_STATUS_GOOD; } void sane_exit (void) { Ma1509_Device *dev, *next; DBG (4, "sane_exit\n"); for (dev = first_dev; dev; dev = next) { next = dev->next; free (dev->name); free (dev); } if (devlist) free (devlist); devlist = 0; first_dev = 0; } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { Ma1509_Device *dev; SANE_Int i; DBG (4, "sane_get_devices: %d devices %s\n", num_devices, local_only ? "(local only)" : ""); if (devlist) free (devlist); devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); if (!devlist) return SANE_STATUS_NO_MEM; i = 0; for (dev = first_dev; i < num_devices; dev = dev->next) devlist[i++] = &dev->sane; devlist[i++] = 0; *device_list = devlist; DBG (5, "sane_get_devices: end\n"); return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { Ma1509_Device *dev; SANE_Status status; Ma1509_Scanner *s; if (!devicename) { DBG (1, "sane_open: devicename is null!\n"); return SANE_STATUS_INVAL; } if (!handle) { DBG (1, "sane_open: handle is null!\n"); return SANE_STATUS_INVAL; } DBG (4, "sane_open: devicename=%s\n", devicename); if (devicename[0]) { for (dev = first_dev; dev; dev = dev->next) if (strcmp (dev->sane.name, devicename) == 0) break; if (!dev) { status = attach (devicename, &dev); if (status != SANE_STATUS_GOOD) return status; } } else /* empty devicname -> use first device */ dev = first_dev; if (!dev) { DBG (1, "sane_open: %s doesn't seem to exist\n", devicename); return SANE_STATUS_INVAL; } s = malloc (sizeof (*s)); if (!s) return SANE_STATUS_NO_MEM; memset (s, 0, sizeof (*s)); s->fd = -1; s->hw = dev; init_options (s); /* insert newly opened handle into list of open handles: */ s->next = first_handle; first_handle = s; status = sanei_usb_open (s->hw->sane.name, &s->fd); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_open: couldn't open %s: %s\n", s->hw->sane.name, sane_strstatus (status)); return status; } status = turn_lamp (s, SANE_TRUE); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_open: couldn't turn on lamp: %s\n", sane_strstatus (status)); return status; } status = turn_lamp (s, SANE_TRUE); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_open: couldn't turn on lamp: %s\n", sane_strstatus (status)); return status; } *handle = s; DBG (5, "sane_open: finished (handle=%p)\n", (void *) s); return SANE_STATUS_GOOD; } void sane_close (SANE_Handle handle) { Ma1509_Scanner *prev, *s; SANE_Status status; DBG (4, "sane_close: handle=%p\n", handle); /* remove handle from list of open handles: */ prev = 0; for (s = first_handle; s; s = s->next) { if (s == handle) break; prev = s; } if (!s) { DBG (1, "sane_close: invalid handle %p\n", handle); return; /* oops, not a handle we know about */ } if (s->val[OPT_MODE].s) free (s->val[OPT_MODE].s); if (s->val[OPT_SOURCE].s) free (s->val[OPT_SOURCE].s); status = turn_lamp (s, SANE_FALSE); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_close: couldn't turn off lamp: %s\n", sane_strstatus (status)); return; } sanei_usb_close (s->fd); if (prev) prev->next = s->next; else first_handle = s->next; free (handle); handle = 0; } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { Ma1509_Scanner *s = handle; if (((unsigned) option >= NUM_OPTIONS) || (option < 0)) { DBG (3, "sane_get_option_descriptor: option %d >= NUM_OPTIONS or < 0\n", option); return 0; } if (!s) { DBG (1, "sane_get_option_descriptor: handle is null!\n"); return 0; } if (s->opt[option].name && s->opt[option].name[0] != 0) DBG (4, "sane_get_option_descriptor for option %s (%sactive%s)\n", s->opt[option].name, s->opt[option].cap & SANE_CAP_INACTIVE ? "in" : "", s->opt[option].cap & SANE_CAP_ADVANCED ? ", advanced" : ""); else DBG (4, "sane_get_option_descriptor for option \"%s\" (%sactive%s)\n", s->opt[option].title, s->opt[option].cap & SANE_CAP_INACTIVE ? "in" : "", s->opt[option].cap & SANE_CAP_ADVANCED ? ", advanced" : ""); return s->opt + option; } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { Ma1509_Scanner *s = handle; SANE_Status status; SANE_Word cap; SANE_Word w; if (((unsigned) option >= NUM_OPTIONS) || (option < 0)) { DBG (3, "sane_control_option: option %d < 0 or >= NUM_OPTIONS\n", option); return SANE_STATUS_INVAL; } if (!s) { DBG (1, "sane_control_option: handle is null!\n"); return SANE_STATUS_INVAL; } if (!val && s->opt[option].type != SANE_TYPE_BUTTON) { DBG (1, "sane_control_option: val is null!\n"); return SANE_STATUS_INVAL; } if (s->opt[option].name && s->opt[option].name[0] != 0) DBG (4, "sane_control_option (%s option %s)\n", action == SANE_ACTION_GET_VALUE ? "get" : (action == SANE_ACTION_SET_VALUE ? "set" : "unknown action with"), s->opt[option].name); else DBG (4, "sane_control_option (%s option \"%s\")\n", action == SANE_ACTION_GET_VALUE ? "get" : (action == SANE_ACTION_SET_VALUE ? "set" : "unknown action with"), s->opt[option].title); if (info) *info = 0; if (s->scanning) { DBG (3, "sane_control_option: don't use while scanning (option %s)\n", s->opt[option].name); return SANE_STATUS_DEVICE_BUSY; } cap = s->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) { DBG (3, "sane_control_option: option %s is inactive\n", s->opt[option].name); return SANE_STATUS_INVAL; } if (action == SANE_ACTION_GET_VALUE) { switch (option) { /* word options: */ case OPT_PREVIEW: case OPT_RESOLUTION: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_THRESHOLD: case OPT_CUSTOM_GAMMA: case OPT_NUM_OPTS: *(SANE_Word *) val = s->val[option].w; return SANE_STATUS_GOOD; /* word-array options: */ case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: memcpy (val, s->val[option].wa, s->opt[option].size); return SANE_STATUS_GOOD; /* string options: */ case OPT_SOURCE: case OPT_MODE: strcpy (val, s->val[option].s); return SANE_STATUS_GOOD; } } else if (action == SANE_ACTION_SET_VALUE) { if (!SANE_OPTION_IS_SETTABLE (cap)) { DBG (3, "sane_control_option: option %s is not setable\n", s->opt[option].name); return SANE_STATUS_INVAL; } status = sanei_constrain_value (s->opt + option, val, info); if (status != SANE_STATUS_GOOD) { DBG (4, "sane_control_option: constrain_value error (option %s)\n", s->opt[option].name); return status; } switch (option) { /* (mostly) side-effect-free word options: */ case OPT_RESOLUTION: case OPT_TL_X: case OPT_BR_X: case OPT_TL_Y: case OPT_BR_Y: if (info) *info |= SANE_INFO_RELOAD_PARAMS; /* fall through */ case OPT_THRESHOLD: case OPT_PREVIEW: s->val[option].w = *(SANE_Word *) val; return SANE_STATUS_GOOD; /* side-effect-free word-array options: */ case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: memcpy (s->val[option].wa, val, s->opt[option].size); return SANE_STATUS_GOOD; case OPT_MODE: { SANE_Char *old_val = s->val[option].s; if (old_val) { if (strcmp (old_val, val) == 0) return SANE_STATUS_GOOD; /* no change */ free (old_val); } if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; s->val[option].s = strdup (val); if (!s->val[option].s) return SANE_STATUS_NO_MEM; s->opt[OPT_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; s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; if (strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_LINEART) == 0) { s->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE; } else { s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE; if (s->val[OPT_CUSTOM_GAMMA].w) { 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; } } return SANE_STATUS_GOOD; } case OPT_SOURCE: if (info) *info |= SANE_INFO_RELOAD_OPTIONS; if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); if (!s->val[option].s) return SANE_STATUS_NO_MEM; if (strcmp (val, "Transparency Adapter") == 0) { s->opt[OPT_TL_X].constraint.range = &s->hw->x_trans_range; s->opt[OPT_TL_Y].constraint.range = &s->hw->y_trans_range; s->opt[OPT_BR_X].constraint.range = &s->hw->x_trans_range; s->opt[OPT_BR_Y].constraint.range = &s->hw->y_trans_range; } else { s->opt[OPT_TL_X].constraint.range = &s->hw->x_range; s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range; s->opt[OPT_BR_X].constraint.range = &s->hw->x_range; s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range; } return SANE_STATUS_GOOD; /* options with side-effects: */ case OPT_CUSTOM_GAMMA: w = *(SANE_Word *) val; if (w == s->val[OPT_CUSTOM_GAMMA].w) return SANE_STATUS_GOOD; /* no change */ if (info) *info |= SANE_INFO_RELOAD_OPTIONS; s->val[OPT_CUSTOM_GAMMA].w = w; 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; if (w && strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) != 0) { 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; } return SANE_STATUS_GOOD; } } DBG (4, "sane_control_option: unknown action for option %s\n", s->opt[option].name); return SANE_STATUS_INVAL; } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { Ma1509_Scanner *s = handle; SANE_String_Const mode; if (!s) { DBG (1, "sane_get_parameters: handle is null!\n"); return SANE_STATUS_INVAL; } if (!s->scanning) { double width, height, dpi; memset (&s->params, 0, sizeof (s->params)); width = SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w); height = SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w); dpi = s->val[OPT_RESOLUTION].w; /* make best-effort guess at what parameters will look like once scanning starts. */ if (dpi > 0.0 && width > 0.0 && height > 0.0) { double dots_per_mm = dpi / MM_PER_INCH; s->params.pixels_per_line = width * dots_per_mm; s->params.lines = height * dots_per_mm; } mode = s->val[OPT_MODE].s; if (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0) { s->params.format = SANE_FRAME_GRAY; s->params.bytes_per_line = (s->params.pixels_per_line + 7) / 8; s->params.depth = 1; } else if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) { s->params.format = SANE_FRAME_GRAY; s->params.bytes_per_line = s->params.pixels_per_line; s->params.depth = 8; } else { /* it's one of the color modes... */ s->params.bytes_per_line = 3 * s->params.pixels_per_line; s->params.depth = 8; s->params.format = SANE_FRAME_RGB; } } s->params.last_frame = SANE_TRUE; if (params) *params = s->params; DBG (4, "sane_get_parameters: frame = %d; last_frame = %s; depth = %d\n", s->params.format, s->params.last_frame ? "true" : "false", s->params.depth); DBG (4, "sane_get_parameters: lines = %d; ppl = %d; bpl = %d\n", s->params.lines, s->params.pixels_per_line, s->params.bytes_per_line); return SANE_STATUS_GOOD; } SANE_Status sane_start (SANE_Handle handle) { Ma1509_Scanner *s = handle; SANE_Status status; struct timeval start; if (!s) { DBG (1, "sane_start: handle is null!\n"); return SANE_STATUS_INVAL; } DBG (4, "sane_start\n"); status = sane_get_parameters (s, 0); if (status != SANE_STATUS_GOOD) return status; /* Check for inconsistencies */ if (s->val[OPT_TL_X].w > s->val[OPT_BR_X].w) { DBG (0, "sane_start: %s (%.1f mm) is bigger than %s (%.1f mm) " "-- aborting\n", s->opt[OPT_TL_X].title, SANE_UNFIX (s->val[OPT_TL_X].w), s->opt[OPT_BR_X].title, SANE_UNFIX (s->val[OPT_BR_X].w)); return SANE_STATUS_INVAL; } if (s->val[OPT_TL_Y].w > s->val[OPT_BR_Y].w) { DBG (0, "sane_start: %s (%.1f mm) is bigger than %s (%.1f mm) " "-- aborting\n", s->opt[OPT_TL_Y].title, SANE_UNFIX (s->val[OPT_TL_Y].w), s->opt[OPT_BR_Y].title, SANE_UNFIX (s->val[OPT_BR_Y].w)); return SANE_STATUS_INVAL; } s->total_bytes = 0; s->read_bytes = 0; /* save start time */ gettimeofday (&start, 0); s->start_time = start.tv_sec; status = set_window (s); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: set window command failed: %s\n", sane_strstatus (status)); goto stop_scanner_and_return; } status = test_unit_ready (s); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: test_unit_ready failed: %s\n", sane_strstatus (status)); goto stop_scanner_and_return; } if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) != 0) { status = calibration (s); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: calibration failed: %s\n", sane_strstatus (status)); goto stop_scanner_and_return; } status = send_gamma (s); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: send_gamma failed: %s\n", sane_strstatus (status)); goto stop_scanner_and_return; } } s->scanning = SANE_TRUE; s->cancelled = SANE_FALSE; status = start_scan (s); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: start_scan command failed: %s\n", sane_strstatus (status)); goto stop_scanner_and_return; } status = start_read_data (s); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: start_read_data command failed: %s\n", sane_strstatus (status)); goto stop_scanner_and_return; } s->params.bytes_per_line = s->hw->bpl; s->params.pixels_per_line = s->params.bytes_per_line; if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0) s->params.pixels_per_line /= 3; else if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) == 0) s->params.pixels_per_line *= 8; s->params.lines = s->hw->lines; s->buffer = (SANE_Byte *) malloc (MA1509_BUFFER_SIZE); if (!s->buffer) return SANE_STATUS_NO_MEM; s->buffer_bytes = 0; DBG (5, "sane_start: finished\n"); return SANE_STATUS_GOOD; stop_scanner_and_return: sanei_usb_close (s->fd); s->scanning = SANE_FALSE; return status; } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { Ma1509_Scanner *s = handle; SANE_Status status; SANE_Int total_size = s->hw->lines * s->hw->bpl; SANE_Int i; if (!s) { DBG (1, "sane_read: handle is null!\n"); return SANE_STATUS_INVAL; } if (!buf) { DBG (1, "sane_read: buf is null!\n"); return SANE_STATUS_INVAL; } if (!len) { DBG (1, "sane_read: len is null!\n"); return SANE_STATUS_INVAL; } DBG (5, "sane_read\n"); *len = 0; if (s->cancelled) { DBG (4, "sane_read: scan was cancelled\n"); return SANE_STATUS_CANCELLED; } if (!s->scanning) { DBG (1, "sane_read: must call sane_start before sane_read\n"); return SANE_STATUS_INVAL; } if (total_size - s->read_bytes <= 0) { DBG (4, "sane_read: EOF\n"); stop_scan (s); s->scanning = SANE_FALSE; return SANE_STATUS_EOF; } if (s->buffer_bytes == 0) { SANE_Int size = MA1509_BUFFER_SIZE; if (size > (total_size - s->total_bytes)) size = total_size - s->total_bytes; DBG (4, "sane_read: trying to read %d bytes\n", size); status = read_data (s, s->buffer, &size); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_read: read_data failed: %s\n", sane_strstatus (status)); *len = 0; return status; } s->total_bytes += size; s->buffer_start = s->buffer; s->buffer_bytes = size; } *len = max_len; if (*len > s->buffer_bytes) *len = s->buffer_bytes; memcpy (buf, s->buffer_start, *len); s->buffer_start += (*len); s->buffer_bytes -= (*len); s->read_bytes += (*len); /* invert for lineart mode */ if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART) == 0) { for (i = 0; i < *len; i++) buf[i] = ~buf[i]; } DBG (4, "sane_read: read %d/%d bytes (%d bytes to go, %d total)\n", *len, max_len, total_size - s->read_bytes, total_size); return SANE_STATUS_GOOD; } void sane_cancel (SANE_Handle handle) { Ma1509_Scanner *s = handle; if (!s) { DBG (1, "sane_cancel: handle is null!\n"); return; } DBG (4, "sane_cancel\n"); if (s->scanning) { s->cancelled = SANE_TRUE; stop_scan (s); free (s->buffer); } s->scanning = SANE_FALSE; DBG (4, "sane_cancel finished\n"); } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { Ma1509_Scanner *s = handle; if (!s) { DBG (1, "sane_set_io_mode: handle is null!\n"); return SANE_STATUS_INVAL; } DBG (4, "sane_set_io_mode: %s\n", non_blocking ? "non-blocking" : "blocking"); if (!s->scanning) { DBG (1, "sane_set_io_mode: call sane_start before sane_set_io_mode"); return SANE_STATUS_INVAL; } if (non_blocking) return SANE_STATUS_UNSUPPORTED; return SANE_STATUS_GOOD; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { Ma1509_Scanner *s = handle; if (!s) { DBG (1, "sane_get_select_fd: handle is null!\n"); return SANE_STATUS_INVAL; } if (!fd) { DBG (1, "sane_get_select_fd: fd is null!\n"); return SANE_STATUS_INVAL; } DBG (4, "sane_get_select_fd\n"); if (!s->scanning) return SANE_STATUS_INVAL; return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/ma1509.conf.in000066400000000000000000000002731456256263500170640ustar00rootroot00000000000000#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 backends-1.3.0/backend/ma1509.h000066400000000000000000000116501456256263500157620ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. (C) 2003 Henning Meier-Geinitz . Based on the mustek (SCSI) backend. 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, see . 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 scanners based on the Mustek MA-1509 chipset. Currently the Mustek BearPaw 1200F is known to work. */ #ifndef ma1509_h #define ma1509_h #include "../include/sane/config.h" #include /* Some constants */ #define INQ_LEN 0x60 /* Length of SCSI inquiry */ #define MA1509_COMMAND_LENGTH 8 #define MA1509_GAMMA_SIZE 1024 #define MA1509_BUFFER_SIZE (1024 * 128) #define MA1509_WARMUP_TIME 30 #ifndef PATH_MAX # define PATH_MAX 1024 #endif #define MA1509_CONFIG_FILE "ma1509.conf" /* Convenience macros */ #if defined(MIN) #undef MIN #endif #if defined(MAX) #undef MAX #endif #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) /* Copy values to memory ('L' = little endian, 'B' = big endian */ #define STORE16L(cp,v) \ do { \ int value = (v); \ \ *(cp)++ = (value >> 0) & 0xff; \ *(cp)++ = (value >> 8) & 0xff; \ } while (0) #define STORE16B(cp,v) \ do { \ int value = (v); \ \ *(cp)++ = (value >> 8) & 0xff; \ *(cp)++ = (value >> 0) & 0xff; \ } while (0) #define STORE32B(cp,v) \ do { \ long int value = (v); \ \ *(cp)++ = (value >> 24) & 0xff; \ *(cp)++ = (value >> 16) & 0xff; \ *(cp)++ = (value >> 8) & 0xff; \ *(cp)++ = (value >> 0) & 0xff; \ } while (0) /* declarations */ enum Ma1509_Option { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_MODE, OPT_RESOLUTION, OPT_SOURCE, OPT_PREVIEW, OPT_GEOMETRY_GROUP, OPT_TL_X, /* top-left x */ OPT_TL_Y, /* top-left y */ OPT_BR_X, /* bottom-right x */ OPT_BR_Y, /* bottom-right y */ OPT_ENHANCEMENT_GROUP, OPT_THRESHOLD, OPT_CUSTOM_GAMMA, /* use custom gamma tables? */ OPT_GAMMA_VECTOR_R, OPT_GAMMA_VECTOR_G, OPT_GAMMA_VECTOR_B, /* must come last: */ NUM_OPTIONS }; typedef struct Ma1509_Device { struct Ma1509_Device *next; SANE_String name; SANE_Device sane; SANE_Bool has_ta; SANE_Bool has_adf; SANE_Range x_range; SANE_Range y_range; /* scan area when transparency adapter is used: */ SANE_Range x_trans_range; SANE_Range y_trans_range; /* values actually used by scanner, not necessarily the desired! */ SANE_Int bpl, ppl, lines; } Ma1509_Device; typedef struct Ma1509_Scanner { /* all the state needed to define a scan request: */ struct Ma1509_Scanner *next; SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; SANE_Bool scanning; SANE_Bool cancelled; SANE_Parameters params; /* Parsed option values and variables that are valid only during actual scanning: */ int fd; /* filedescriptor */ long start_time; /* at this time the scan started */ long lamp_time; /* at this time the lamp was turned on */ SANE_Word total_bytes; /* bytes read from scanner */ SANE_Word read_bytes; /* bytes transmitted by sane_read */ SANE_Int red_gamma_table[MA1509_GAMMA_SIZE]; SANE_Int green_gamma_table[MA1509_GAMMA_SIZE]; SANE_Int blue_gamma_table[MA1509_GAMMA_SIZE]; SANE_Byte *buffer, *buffer_start; SANE_Int buffer_bytes; /* scanner dependent/low-level state: */ Ma1509_Device *hw; } Ma1509_Scanner; #endif /* ma1509_h */ backends-1.3.0/backend/magicolor.c000066400000000000000000002433551456256263500170260ustar00rootroot00000000000000/* * magicolor.c - SANE library for Magicolor scanners. * * (C) 2010 Reinhold Kainhofer * * Based on the epson2 sane backend: * Based on Kazuhiro Sasayama previous * Work on epson.[ch] file from the SANE package. * Please see those files for additional copyrights. * Copyright (C) 2006-10 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ #define MAGICOLOR_VERSION 0 #define MAGICOLOR_REVISION 0 #define MAGICOLOR_BUILD 1 /* debugging levels: * * 127 mc_recv buffer * 125 mc_send buffer * 35 fine-grained status and progress * 30 sane_read * 25 setvalue, getvalue, control_option * 20 low-level (I/O) mc_* functions * 15 mid-level mc_* functions * 10 high-level cmd_* functions * 7 open/close/attach * 6 print_params * 5 basic functions * 3 status info and progress * 2 scanner info and capabilities * 1 errors & warnings */ #include "sane/config.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_SOCKET_H #include #endif #if HAVE_LIBSNMP #include #include #include #include #endif #include "../include/sane/saneopts.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_tcp.h" #include "../include/sane/sanei_udp.h" #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_backend.h" #include "magicolor.h" #define min(x,y) (((x)<(y))?(x):(y)) /**************************************************************************** * Devices supported by this backend ****************************************************************************/ /* Scanner command type * | Start scan * | | Poll for error * | | | Stop scan? * | | | | Query image parameters * | | | | | set scan parameters * | | | | | | Get status? * | | | | | | | Read scanned data * | | | | | | | | Unknown * | | | | | | | | | Unknown * | | | | | | | | | | Net wrapper command type * | | | | | | | | | | | Net Welcome * | | | | | | | | | | | | Net Lock * | | | | | | | | | | | | | Net Lock ACK * | | | | | | | | | | | | | | Net Unlock * | | | | | | | | | | | | | | | */ static struct MagicolorCmd magicolor_cmd[] = { {"mc1690mf", CMD, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x12, NET, 0x00, 0x01, 0x02, 0x03}, {"mc4690mf", CMD, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x12, NET, 0x00, 0x01, 0x02, 0x03}, }; static SANE_Int magicolor_default_resolutions[] = {150, 300, 600}; static SANE_Int magicolor_default_depths[] = {1,8}; static struct MagicolorCap magicolor_cap[] = { /* KONICA MINOLTA magicolor 1690MF, USB ID 0x123b:2089 */ { 0x2089, "mc1690mf", "KONICA MINOLTA magicolor 1690MF", ".1.3.6.1.4.1.18334.1.1.1.1.1.23.1.1", -1, 0x85, 600, {150, 600, 0}, magicolor_default_resolutions, 3, /* 600 dpi max, 3 resolutions */ 8, magicolor_default_depths, /* color depth 8 default, 1 and 8 possible */ {1, 9, 0}, /* brightness ranges (TODO!) */ {0, SANE_FIX(0x13f8 * MM_PER_INCH / 600), 0}, {0, SANE_FIX(0x1b9c * MM_PER_INCH / 600), 0}, /* FBF x/y ranges (TODO!) */ SANE_TRUE, SANE_FALSE, /* non-duplex ADF, x/y ranges (TODO!) */ {0, SANE_FIX(0x1390 * MM_PER_INCH / 600), 0}, {0, SANE_FIX(0x20dc * MM_PER_INCH / 600), 0}, }, /* KONICA MINOLTA magicolor 4690MF, USB ID 0x132b:2079 */ { 0x2079, "mc4690mf", "KONICA MINOLTA magicolor 4690MF", "FIXME", /* FIXME: fill in the correct OID! */ 0x03, 0x85, 600, {150, 600, 0}, magicolor_default_resolutions, 3, /* 600 dpi max, 3 resolutions */ 8, magicolor_default_depths, /* color depth 8 default, 1 and 8 possible */ {1, 9, 0}, /* brightness ranges (TODO!) */ {0, SANE_FIX(0x13f8 * MM_PER_INCH / 600), 0}, {0, SANE_FIX(0x1b9c * MM_PER_INCH / 600), 0}, /* FBF x/y ranges (TODO!) */ SANE_TRUE, SANE_TRUE, /* duplex ADF, x/y ranges (TODO!) */ {0, SANE_FIX(0x1390 * MM_PER_INCH / 600), 0}, {0, SANE_FIX(0x20dc * MM_PER_INCH / 600), 0}, }, }; static int MC_SNMP_Timeout = 2500; static int MC_Scan_Data_Timeout = 15000; static int MC_Request_Timeout = 5000; /**************************************************************************** * General configuration parameter definitions ****************************************************************************/ /* * Definition of the mode_param struct, that is used to * specify the valid parameters for the different scan modes. * * The depth variable gets updated when the bit depth is modified. */ static struct mode_param mode_params[] = { {0x00, 1, 1}, /* Lineart, 1 color, 1 bit */ {0x02, 1, 24}, /* Grayscale, 1 color, 24 bit */ {0x03, 3, 24} /* Color, 3 colors, 24 bit */ }; static SANE_String_Const mode_list[] = { SANE_VALUE_SCAN_MODE_LINEART, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_COLOR, NULL }; static const SANE_String_Const adf_mode_list[] = { SANE_I18N("Simplex"), SANE_I18N("Duplex"), NULL }; /* Define the different scan sources */ #define FBF_STR SANE_I18N("Flatbed") #define ADF_STR SANE_I18N("Automatic Document Feeder") /* * source list need one dummy entry (save device settings is crashing). * NOTE: no const - this list gets created while exploring the capabilities * of the scanner. */ static SANE_String_Const source_list[] = { FBF_STR, NULL, NULL, NULL }; /* Some utility functions */ static size_t max_string_size(const SANE_String_Const strings[]) { size_t size, max_size = 0; int i; for (i = 0; strings[i]; i++) { size = strlen(strings[i]) + 1; if (size > max_size) max_size = size; } return max_size; } static SANE_Status attach_one_usb(SANE_String_Const devname); static SANE_Status attach_one_net(SANE_String_Const devname, unsigned int device); static void print_params(const SANE_Parameters params) { DBG(6, "params.format = %d\n", params.format); DBG(6, "params.last_frame = %d\n", params.last_frame); DBG(6, "params.bytes_per_line = %d\n", params.bytes_per_line); DBG(6, "params.pixels_per_line = %d\n", params.pixels_per_line); DBG(6, "params.lines = %d\n", params.lines); DBG(6, "params.depth = %d\n", params.depth); } /**************************************************************************** * Low-level Network communication functions ****************************************************************************/ #define MAGICOLOR_SNMP_SYSDESCR_OID ".1.3.6.1.2.1.1.1.0" #define MAGICOLOR_SNMP_SYSOBJECT_OID ".1.3.6.1.2.1.1.2.0" #define MAGICOLOR_SNMP_MAC_OID ".1.3.6.1.2.1.2.2.1.6.1" #define MAGICOLOR_SNMP_DEVICE_TREE ".1.3.6.1.4.1.18334.1.1.1.1.1" /* We don't have a packet wrapper, which holds packet size etc., so we don't have to use a *read_raw and a *_read function... */ static int sanei_magicolor_net_read(struct Magicolor_Scanner *s, unsigned char *buf, size_t wanted, SANE_Status * status) { size_t size, read = 0; struct pollfd fds[1]; *status = SANE_STATUS_GOOD; /* poll for data-to-be-read (using a 5 seconds timeout) */ fds[0].fd = s->fd; fds[0].events = POLLIN; if (poll (fds, 1, MC_Request_Timeout) <= 0) { *status = SANE_STATUS_IO_ERROR; return read; } while (read < wanted) { size = sanei_tcp_read(s->fd, buf + read, wanted - read); if (size == 0) break; read += size; } if (read < wanted) *status = SANE_STATUS_IO_ERROR; return read; } /* We need to optionally pad the buffer with 0x00 to send 64-byte chunks. On the other hand, the 0x04 commands don't need this, so we need two functions, one *_write function that pads the buffer and then calls *_write_raw */ static int sanei_magicolor_net_write_raw(struct Magicolor_Scanner *s, const unsigned char *buf, size_t buf_size, SANE_Status *status) { sanei_tcp_write(s->fd, buf, buf_size); /* TODO: Check whether sending failed... */ *status = SANE_STATUS_GOOD; return buf_size; } static int sanei_magicolor_net_write(struct Magicolor_Scanner *s, const unsigned char *buf, size_t buf_size, SANE_Status *status) { size_t len = 64; unsigned char *new_buf = malloc(len); if (!new_buf) { *status = SANE_STATUS_NO_MEM; return 0; } memset(new_buf, 0x00, len); if (buf_size > len) buf_size = len; if (buf_size) memcpy(new_buf, buf, buf_size); return sanei_magicolor_net_write_raw (s, new_buf, len, status); } static SANE_Status sanei_magicolor_net_open(struct Magicolor_Scanner *s) { SANE_Status status; unsigned char buf[5]; ssize_t read; struct timeval tv; struct MagicolorCmd *cmd = s->hw->cmd; tv.tv_sec = 5; tv.tv_usec = 0; setsockopt(s->fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)); DBG(1, "%s\n", __func__); /* the scanner sends a kind of welcome msg */ read = sanei_magicolor_net_read(s, buf, 3, &status); if (read != 3) return SANE_STATUS_IO_ERROR; if (buf[0] != cmd->net_wrapper_cmd || buf[1] != cmd->net_welcome) { DBG (32, "Invalid welcome message received, Expected 0x%02x %02x 00, but got 0x%02x %02x %02x\n", cmd->net_wrapper_cmd, cmd->net_welcome, buf[0], buf[1], buf[2]); return SANE_STATUS_IO_ERROR; } else if (buf[2] != 0x00) { /* TODO: Handle response "04 00 01", indicating an error! */ DBG (32, "Welcome message received, busy status %02x\n", buf[2]); /* TODO: Return a human-readable error message (Unable to connect to scanner, scanner is not ready) */ return SANE_STATUS_DEVICE_BUSY; } buf[0] = cmd->net_wrapper_cmd; buf[1] = cmd->net_lock; buf[2] = 0x00; /* Copy the device's USB id to bytes 3-4: */ buf[3] = s->hw->cap->id & 0xff; buf[4] = (s->hw->cap->id >> 8) & 0xff; DBG(32, "Proper welcome message received, locking the scanner...\n"); sanei_magicolor_net_write_raw(s, buf, 5, &status); read = sanei_magicolor_net_read(s, buf, 3, &status); if (read != 3) return SANE_STATUS_IO_ERROR; if (buf[0] != cmd->net_wrapper_cmd || buf[1] != cmd->net_lock_ack || buf[2] != 0x00) { DBG (32, "Welcome message received, Expected 0x%x %x 00, but got 0x%x %x %x\n", cmd->net_wrapper_cmd, cmd->net_lock_ack, buf[0], buf[1], buf[2]); return SANE_STATUS_IO_ERROR; } DBG(32, "scanner locked\n"); return status; } static SANE_Status sanei_magicolor_net_close(struct Magicolor_Scanner *s) { SANE_Status status; struct MagicolorCmd *cmd = s->hw->cmd; unsigned char buf[3]; DBG(1, "%s\n", __func__); buf[0] = cmd->net_wrapper_cmd; buf[1] = cmd->net_unlock; buf[2] = 0x00; sanei_magicolor_net_write_raw(s, buf, 3, &status); return status; } /**************************************************************************** * Low-level USB communication functions ****************************************************************************/ #define SANE_MAGICOLOR_VENDOR_ID (0x132b) SANE_Word sanei_magicolor_usb_product_ids[] = { 0x2089, /* magicolor 1690MF */ 0x2079, /* magicolor 4690MF */ 0 /* last entry - this is used for devices that are specified in the config file as "usb " */ }; static int sanei_magicolor_getNumberOfUSBProductIds (void) { return sizeof (sanei_magicolor_usb_product_ids) / sizeof (SANE_Word); } /**************************************************************************** * Magicolor low-level communication commands ****************************************************************************/ static void dump_hex_buffer_dense (int level, const unsigned char *buf, size_t buf_size) { size_t k; char msg[1024], fmt_buf[1024]; memset (&msg[0], 0x00, 1024); memset (&fmt_buf[0], 0x00, 1024); for (k = 0; k < min(buf_size, 80); k++) { if (k % 16 == 0) { if (k>0) { DBG (level, "%s\n", msg); memset (&msg[0], 0x00, 1024); } sprintf (fmt_buf, " 0x%04lx ", (unsigned long)k); strcat (msg, fmt_buf); } if (k % 8 == 0) { strcat (msg, " "); } sprintf (fmt_buf, " %02x" , buf[k]); strcat (msg, fmt_buf); } if (msg[0] != 0 ) { DBG (level, "%s\n", msg); } } /* Create buffers containing the command and arguments. Length of reserved * buffer is returned. It's the caller's job to free the buffer! */ static int mc_create_buffer (Magicolor_Scanner *s, unsigned char cmd_type, unsigned char cmd, unsigned char **buf, unsigned char* arg1, size_t len1, SANE_Status *status) { unsigned char* b = NULL; size_t buf_len = 2+4+len1+4; NOT_USED (s); if (len1 <= 0) buf_len = 6; /* no args, just cmd + final 0x00 00 00 00 */ *buf = b = malloc (buf_len); memset (b, 0x00, buf_len); if (!b) { *status = SANE_STATUS_NO_MEM; return 0; } b[0] = cmd_type; b[1] = cmd; if (len1>0) { b[2] = len1 & 0xff; b[3] = (len1 >> 8) & 0xff; b[4] = (len1 >> 16) & 0xff; b[5] = (len1 >> 24) & 0xff; if (arg1) memcpy(b+6, arg1, len1); } /* Writing the final 0x00 00 00 00 is not necessary, they are 0x00 already */ *status = SANE_STATUS_GOOD; return buf_len; } static int mc_create_buffer2 (Magicolor_Scanner *s, unsigned char cmd_type, unsigned char cmd, unsigned char **buf, unsigned char* arg1, size_t len1, unsigned char* arg2, size_t len2, SANE_Status *status) { unsigned char* b = NULL; size_t buf_len = 2+4+len1+4+len2+4; /* If any of the two args has size 0, use the simpler mc_create_buffer */ if (len1<=0) return mc_create_buffer (s, cmd_type, cmd, buf, arg2, len2, status); else if (len2<=0) return mc_create_buffer (s, cmd_type, cmd, buf, arg1, len1, status); /* Allocate memory and copy over args and their lengths */ *buf = b = malloc (buf_len); if (!b) { *status = SANE_STATUS_NO_MEM; return 0; } memset (b, 0x00, buf_len); b[0] = cmd_type; b[1] = cmd; /* copy over the argument length in lower endian */ b[2] = len1 & 0xff; b[3] = (len1 >> 8) & 0xff; b[4] = (len1 >> 16) & 0xff; b[5] = (len1 >> 24) & 0xff; if (arg1) { /* Copy the arguments */ memcpy(b+6, arg1, len1); } /* copy over the second argument length in little endian */ b[6+len1] = len2 & 0xff; b[7+len1] = (len2 >> 8) & 0xff; b[8+len1] = (len2 >> 16) & 0xff; b[9+len1] = (len2 >> 24) & 0xff; if (arg2) { memcpy(b+10+len1, arg2, len2); } *status = SANE_STATUS_GOOD; return buf_len; } static int mc_send(Magicolor_Scanner * s, void *buf, size_t buf_size, SANE_Status * status) { DBG(15, "%s: size = %lu\n", __func__, (u_long) buf_size); if (DBG_LEVEL >= 125) { const unsigned char *s = buf; DBG(125, "Cmd: 0x%02x %02x, complete buffer:\n", s[0], s[1]); dump_hex_buffer_dense (125, s, buf_size); } if (s->hw->connection == SANE_MAGICOLOR_NET) { return sanei_magicolor_net_write(s, buf, buf_size, status); } else if (s->hw->connection == SANE_MAGICOLOR_USB) { size_t n; n = buf_size; *status = sanei_usb_write_bulk(s->fd, buf, &n); DBG(125, "USB: wrote %lu bytes, status: %s\n", (unsigned long)n, sane_strstatus(*status)); return n; } *status = SANE_STATUS_INVAL; return 0; /* never reached */ } static ssize_t mc_recv(Magicolor_Scanner * s, void *buf, ssize_t buf_size, SANE_Status * status) { ssize_t n = 0; DBG(15, "%s: size = %ld, buf = %p\n", __func__, (long) buf_size, buf); if (s->hw->connection == SANE_MAGICOLOR_NET) { n = sanei_magicolor_net_read(s, buf, buf_size, status); } else if (s->hw->connection == SANE_MAGICOLOR_USB) { /* !!! only report an error if we don't read anything */ n = buf_size; /* buf_size gets overwritten */ *status = sanei_usb_read_bulk(s->fd, (SANE_Byte *) buf, (size_t *) & n); if (n > 0) *status = SANE_STATUS_GOOD; } if (n < buf_size) { DBG(1, "%s: expected = %lu, got = %ld\n", __func__, (u_long) buf_size, (long) n); *status = SANE_STATUS_IO_ERROR; } /* dump buffer if appropriate */ if (DBG_LEVEL >= 127 && n > 0) { const unsigned char* b=buf; dump_hex_buffer_dense (125, b, buf_size); } return n; } /* Simple function to exchange a fixed amount of * data with the scanner */ static SANE_Status mc_txrx(Magicolor_Scanner * s, unsigned char *txbuf, size_t txlen, unsigned char *rxbuf, size_t rxlen) { SANE_Status status; mc_send(s, txbuf, txlen, &status); if (status != SANE_STATUS_GOOD) { DBG(1, "%s: tx err, %s\n", __func__, sane_strstatus(status)); return status; } mc_recv(s, rxbuf, rxlen, &status); if (status != SANE_STATUS_GOOD) { DBG(1, "%s: rx err, %s\n", __func__, sane_strstatus(status)); } return status; } /**************************************************************************** * Magicolor high-level communication commands ****************************************************************************/ /** 0x03 09 01 - Request last error * <- Information block (0x00 for OK, 0x01 for ERROR) */ static SANE_Status cmd_request_error (SANE_Handle handle) { Magicolor_Scanner *s = (Magicolor_Scanner *) handle; SANE_Status status; unsigned char params[1]; unsigned char *buf; size_t buflen; DBG(8, "%s\n", __func__); if (s->hw->cmd->request_status == 0) return SANE_STATUS_UNSUPPORTED; buflen = mc_create_buffer (s, s->hw->cmd->scanner_cmd, s->hw->cmd->request_error, &buf, NULL, 1, &status); if (buflen <= 0 ) { return SANE_STATUS_NO_MEM; } else if (status != SANE_STATUS_GOOD) { return status; } status = mc_txrx (s, buf, buflen, params, 1); free(buf); if (status != SANE_STATUS_GOOD) return status; DBG(1, "status: %02x\n", params[0]); switch (params[0]) { case STATUS_READY: DBG(1, " ready\n"); break; case STATUS_ADF_JAM: DBG(1, " paper jam in ADF\n"); return SANE_STATUS_JAMMED; break; case STATUS_OPEN: DBG(1, " printer door open or waiting for button press\n"); return SANE_STATUS_COVER_OPEN; break; case STATUS_NOT_READY: DBG(1, " scanner not ready (in use on another interface or warming up)\n"); return SANE_STATUS_DEVICE_BUSY; break; default: DBG(1, " unknown status 0x%x\n", params[0]); } return status; } /** 0x03 0d - Request status command */ static SANE_Status cmd_request_status(SANE_Handle handle, unsigned char *b) { Magicolor_Scanner *s = (Magicolor_Scanner *) handle; SANE_Status status; unsigned char *buf; size_t buflen; DBG(8, "%s\n", __func__); if (!b) { DBG(1, "%s called with NULL buffer\n", __func__); return SANE_STATUS_INVAL; } memset (b, 0x00, 0x0b); /* initialize all 0x0b bytes with 0 */ buflen = mc_create_buffer (s, s->hw->cmd->scanner_cmd, s->hw->cmd->request_status, &buf, NULL, 0x0b, &status); if (buflen <= 0 ) { return SANE_STATUS_NO_MEM; } else if (status != SANE_STATUS_GOOD) { return status; } status = mc_txrx (s, buf, buflen, b, 0x0b); free (buf); if (status != SANE_STATUS_GOOD) DBG(8, "%s: Status NOT successfully retrieved\n", __func__); else { DBG(8, "%s: Status successfully retrieved:\n", __func__); /* TODO: debug output of the returned parameters... */ DBG (11, " ADF status: 0x%02x", b[1]); if (b[1] & ADF_LOADED) { DBG (11, " loaded\n"); } else { DBG (11, " not loaded\n"); } } return status; } /** 0x03 08 - Start scan command */ static SANE_Status cmd_start_scan (SANE_Handle handle, size_t value) { Magicolor_Scanner *s = (Magicolor_Scanner *) handle; SANE_Status status; unsigned char params1[4], params2[1]; unsigned char *buf; size_t buflen; DBG(8, "%s\n", __func__); /* Copy params to buffers */ /* arg1 is expected returned bytes per line, 4-byte little endian */ /* arg2 is unknown, seems to be always 0x00 */ params1[0] = value & 0xff; params1[1] = (value >> 8) & 0xff; params1[2] = (value >> 16) & 0xff; params1[3] = (value >> 24) & 0xff; params2[0] = 0x00; buflen = mc_create_buffer2 (s, s->hw->cmd->scanner_cmd, s->hw->cmd->start_scanning, &buf, params1, 4, params2, 1, &status); if (buflen <= 0 ) { return SANE_STATUS_NO_MEM; } else if (status != SANE_STATUS_GOOD) { return status; } mc_send(s, buf, buflen, &status); free (buf); if (status != SANE_STATUS_GOOD) DBG(8, "%s: Data NOT successfully sent\n", __func__); else DBG(8, "%s: Data successfully sent\n", __func__); return status; } /** 0x03 0a - Cancel(?) Scan command */ /* TODO: Does this command really mean CANCEL??? */ static SANE_Status cmd_cancel_scan (SANE_Handle handle) { Magicolor_Scanner *s = (Magicolor_Scanner *) handle; SANE_Status status; unsigned char *buf; size_t buflen; DBG(8, "%s\n", __func__); buflen = mc_create_buffer (s, s->hw->cmd->scanner_cmd, s->hw->cmd->stop_scanning, &buf, NULL, 0, &status); if (buflen <= 0 ) { return SANE_STATUS_NO_MEM; } else if (status != SANE_STATUS_GOOD) { return status; } mc_send(s, buf, buflen, &status); free (buf); if (status != SANE_STATUS_GOOD) DBG(8, "%s: Data NOT successfully sent\n", __func__); else DBG(8, "%s: Data successfully sent\n", __func__); return status; } /** 0x03 12 - Finish(?) scan command */ /* TODO: Does this command really mean FINISH??? */ static SANE_Status cmd_finish_scan (SANE_Handle handle) { Magicolor_Scanner *s = (Magicolor_Scanner *) handle; SANE_Status status; unsigned char *buf, returned[0x0b]; size_t buflen; DBG(8, "%s\n", __func__); buflen = mc_create_buffer (s, s->hw->cmd->scanner_cmd, s->hw->cmd->unknown2, &buf, NULL, 0x0b, &status); if (buflen <= 0 ) { return SANE_STATUS_NO_MEM; } else if (status != SANE_STATUS_GOOD) { return status; } memset (&returned[0], 0x00, 0x0b); status = mc_txrx (s, buf, buflen, returned, 0x0b); free (buf); if (status != SANE_STATUS_GOOD) DBG(8, "%s: Data NOT successfully sent\n", __func__); else DBG(8, "%s: Data successfully sent\n", __func__); return status; } /** 0x03 0b - Get scanning parameters command * input buffer seems to be 0x00 always */ static SANE_Status cmd_get_scanning_parameters(SANE_Handle handle, SANE_Frame *format, SANE_Int *depth, SANE_Int *data_pixels, SANE_Int *pixels_per_line, SANE_Int *lines) { Magicolor_Scanner *s = (Magicolor_Scanner *) handle; SANE_Status status; unsigned char *txbuf, rxbuf[8]; size_t buflen; NOT_USED (format); NOT_USED (depth); DBG(8, "%s\n", __func__); buflen = mc_create_buffer (s, s->hw->cmd->scanner_cmd, s->hw->cmd->request_scan_parameters, &txbuf, NULL, 8, &status); if (buflen <= 0 ) { return SANE_STATUS_NO_MEM; } else if (status != SANE_STATUS_GOOD) { return status; } status = mc_txrx (s, txbuf, buflen, rxbuf, 8); free (txbuf); if (status != SANE_STATUS_GOOD) DBG(8, "%s: Parameters NOT successfully retrieved\n", __func__); else { DBG(8, "%s: Parameters successfully retrieved\n", __func__); /* Assign px_per_line and lines. Bytes 7-8 must match 3-4 */ if (rxbuf[2]!=rxbuf[6] || rxbuf[3]!=rxbuf[7]) { DBG (1, "%s: ERROR: Returned image parameters indicate an " "unsupported device: Bytes 3-4 do not match " "bytes 7-8! Trying to continue with bytes 3-4.\n", __func__); dump_hex_buffer_dense (1, rxbuf, 8); } /* Read returned values, encoded in 2-byte little endian */ *data_pixels = rxbuf[1] * 0x100 + rxbuf[0]; *lines = rxbuf[3] * 0x100 + rxbuf[2]; *pixels_per_line = rxbuf[5] * 0x100 + rxbuf[4]; DBG (8, "%s: data_pixels = 0x%x (%u), lines = 0x%x (%u), " "pixels_per_line = 0x%x (%u)\n", __func__, *data_pixels, *data_pixels, *lines, *lines, *pixels_per_line, *pixels_per_line); /* * SECURITY REMEDIATION * See gitlab issue #279 - Issue10 SIGFPE in mc_setup_block_mode * * pixels_per_line cannot be zero, otherwise a division by zero error can occur later. * Added checking the parameters to ensure that this issue cannot occur. * * Additionally to the reported issue, it makes no sense for any of the values of * data_pixels, lines or pixels_per_line to be zero. I do not have any knowledge * of valid maximums for these parameters. * */ if ((*data_pixels == 0) || (*lines == 0) || (*pixels_per_line == 0)) { DBG (1, "%s: ERROR: Returned image parameters contain invalid " "bytes. Zero values for these parameters are not rational.\n", __func__); dump_hex_buffer_dense (1, rxbuf, 8); return SANE_STATUS_INVAL; } } return status; } /** 0x03 0c - Set scanning parameters command */ static SANE_Status cmd_set_scanning_parameters(SANE_Handle handle, unsigned char resolution, unsigned char color_mode, unsigned char brightness, unsigned char contrast, int tl_x, int tl_y, int width, int height, unsigned char source) { Magicolor_Scanner *s = (Magicolor_Scanner *) handle; SANE_Status status; unsigned char param[0x11]; unsigned char *buf; size_t buflen; DBG(8, "%s\n", __func__); /* Copy over the params to the param byte array */ /* Byte structure: * byte 0: resolution * byte 1: color mode * byte 2: brightness * byte 3: 0xff * byte 4-5: x-start * byte 6-7: y-start * byte 8-9: x-extent * byte 10-11: y-extent * byte 12: source (ADF/FBF) **/ memset (¶m[0], 0x00, 0x11); param[0] = resolution; param[1] = color_mode; param[2] = brightness; param[3] = contrast | 0xff; /* TODO: Always 0xff? What about contrast? */ /* Image coordinates are encoded 2-byte little endian: */ param[4] = tl_x & 0xff; param[5] = (tl_x >> 8) & 0xff; param[6] = tl_y & 0xff; param[7] = (tl_y >> 8) & 0xff; param[8] = width & 0xff; param[9] = (width >> 8) & 0xff; param[10] = height & 0xff; param[11] = (height >> 8) & 0xff; param[12] = source; /* dump buffer if appropriate */ DBG (127, " Scanning parameter buffer:"); dump_hex_buffer_dense (127, param, 0x11); buflen = mc_create_buffer (s, s->hw->cmd->scanner_cmd, s->hw->cmd->set_scan_parameters, &buf, param, 0x11, &status); if (buflen <= 0 ) { return SANE_STATUS_NO_MEM; } else if (status != SANE_STATUS_GOOD) { return status; } mc_send(s, buf, buflen, &status); free (buf); if (status != SANE_STATUS_GOOD) DBG(8, "%s: Data NOT successfully sent\n", __func__); else DBG(8, "%s: Data successfully sent\n", __func__); return status; } /** 0x03 ?? - Request push button status command */ #if 0 static SANE_Status cmd_request_push_button_status(SANE_Handle handle, unsigned char *bstatus) { Magicolor_Scanner *s = (Magicolor_Scanner *) handle; SANE_Status status; unsigned char *buf; size_t buflen; DBG(8, "%s\n", __func__); if (s->hw->cmd->unknown1 == 0) return SANE_STATUS_UNSUPPORTED; DBG(8, "%s: Supported\n", __func__); memset (bstatus, 0x00, 1); buflen = mc_create_buffer (s, s->hw->cmd->scanner_cmd, s->hw->cmd->unknown1, &buf, bstatus, 1, &status); if (buflen <= 0 ) { return SANE_STATUS_NO_MEM; } else if (status != SANE_STATUS_GOOD) { return status; } status = mc_txrx (s, buf, buflen, bstatus, 1); free(buf); if (status != SANE_STATUS_GOOD) return status; DBG(1, "push button status: %02x ", bstatus[0]); switch (bstatus[0]) { /* TODO: What's the response code for button pressed??? */ default: DBG(1, " unknown\n"); status = SANE_STATUS_UNSUPPORTED; } return status; } #endif /** 0x03 0e - Read data command */ static SANE_Status cmd_read_data (SANE_Handle handle, unsigned char *buf, size_t len) { Magicolor_Scanner *s = (Magicolor_Scanner *) handle; SANE_Status status; unsigned char *txbuf; unsigned char param[4]; size_t txbuflen; int oldtimeout = MC_Request_Timeout; DBG(8, "%s\n", __func__); param[0] = len & 0xff; param[1] = (len >> 8) & 0xff; param[2] = (len >> 16) & 0xff; param[3] = (len >> 24) & 0xff; txbuflen = mc_create_buffer (s, s->hw->cmd->scanner_cmd, s->hw->cmd->request_data, &txbuf, param, 4, &status); if (txbuflen <= 0 ) { return SANE_STATUS_NO_MEM; } else if (status != SANE_STATUS_GOOD) { return status; } /* Temporarily set the poll timeout to 10 seconds instead of 2, * because a color scan needs >5 seconds to initialize. */ MC_Request_Timeout = MC_Scan_Data_Timeout; status = mc_txrx (s, txbuf, txbuflen, buf, len); MC_Request_Timeout = oldtimeout; free (txbuf); if (status != SANE_STATUS_GOOD) DBG(8, "%s: Image data NOT successfully retrieved\n", __func__); else { DBG(8, "%s: Image data successfully retrieved\n", __func__); } return status; } /* TODO: 0x03 0f command (unknown), 0x03 10 command (set button wait) */ /**************************************************************************** * Magicolor backend high-level operations ****************************************************************************/ static void mc_dev_init(Magicolor_Device *dev, const char *devname, int conntype) { DBG(5, "%s\n", __func__); dev->name = NULL; dev->model = NULL; dev->connection = conntype; dev->sane.name = devname; dev->sane.model = NULL; dev->sane.type = "flatbed scanner"; dev->sane.vendor = "Magicolor"; dev->cap = &magicolor_cap[MAGICOLOR_CAP_DEFAULT]; dev->cmd = &magicolor_cmd[MAGICOLOR_LEVEL_DEFAULT]; /* Change default level when using a network connection */ if (dev->connection == SANE_MAGICOLOR_NET) dev->cmd = &magicolor_cmd[MAGICOLOR_LEVEL_NET]; } static SANE_Status mc_dev_post_init(struct Magicolor_Device *dev) { DBG(5, "%s\n", __func__); NOT_USED (dev); /* Correct device parameters if needed */ return SANE_STATUS_GOOD; } static SANE_Status mc_set_model(Magicolor_Scanner * s, const char *model, size_t len) { unsigned char *buf; unsigned char *p; struct Magicolor_Device *dev = s->hw; buf = malloc(len + 1); if (buf == NULL) return SANE_STATUS_NO_MEM; memcpy(buf, model, len); buf[len] = '\0'; p = &buf[len - 1]; while (*p == ' ') { *p = '\0'; p--; } if (dev->model) free(dev->model); dev->model = strndup((const char *) buf, len); dev->sane.model = dev->model; DBG(10, "%s: model is '%s'\n", __func__, dev->model); free(buf); return SANE_STATUS_GOOD; } static void mc_set_device (SANE_Handle handle, unsigned int device) { Magicolor_Scanner *s = (Magicolor_Scanner *) handle; Magicolor_Device *dev = s->hw; const char* cmd_level; int n; DBG(1, "%s: 0x%x\n", __func__, device); for (n = 0; n < NELEMS (magicolor_cap); n++) { if (magicolor_cap[n].id == device) break; } if (n < NELEMS(magicolor_cap)) { dev->cap = &magicolor_cap[n]; } else { dev->cap = &magicolor_cap[MAGICOLOR_CAP_DEFAULT]; DBG(1, " unknown device 0x%x, using default %s\n", device, dev->cap->model); } mc_set_model (s, dev->cap->model, strlen (dev->cap->model)); cmd_level = dev->cap->cmds; /* set command type and level */ for (n = 0; n < NELEMS(magicolor_cmd); n++) { if (!strcmp(cmd_level, magicolor_cmd[n].level)) break; } if (n < NELEMS(magicolor_cmd)) { dev->cmd = &magicolor_cmd[n]; } else { dev->cmd = &magicolor_cmd[MAGICOLOR_LEVEL_DEFAULT]; DBG(1, " unknown command level %s, using %s\n", cmd_level, dev->cmd->level); } } static SANE_Status mc_discover_capabilities(Magicolor_Scanner *s) { SANE_Status status; Magicolor_Device *dev = s->hw; SANE_String_Const *source_list_add = source_list; DBG(5, "%s\n", __func__); /* always add flatbed */ *source_list_add++ = FBF_STR; /* TODO: How can I check for existence of an ADF??? */ if (dev->cap->ADF) *source_list_add++ = ADF_STR; /* TODO: Is there any capability that we can extract from the * device by some scanne command? So far, it looks like * the device does not support any reporting. I don't even * see a way to determine which device we are talking to! */ /* request error status */ status = cmd_request_error(s); if (status != SANE_STATUS_GOOD) return status; dev->x_range = &dev->cap->fbf_x_range; dev->y_range = &dev->cap->fbf_y_range; DBG(5, " x-range: %f %f\n", SANE_UNFIX(dev->x_range->min), SANE_UNFIX(dev->x_range->max)); DBG(5, " y-range: %f %f\n", SANE_UNFIX(dev->y_range->min), SANE_UNFIX(dev->y_range->max)); DBG(5, "End of %s, status:%s\n", __func__, sane_strstatus(status)); *source_list_add = NULL; /* add end marker to source list */ return status; } static SANE_Status mc_setup_block_mode (Magicolor_Scanner *s) { /* block_len should always be a multiple of bytes_per_line, so * we retrieve only whole lines at once */ s->block_len = (int)(0xff00/s->scan_bytes_per_line) * s->scan_bytes_per_line; s->blocks = s->data_len / s->block_len; s->last_len = s->data_len - (s->blocks * s->block_len); if (s->last_len>0) s->blocks++; DBG(5, "%s: block_len=0x%x, last_len=0x%0x, blocks=%d\n", __func__, s->block_len, s->last_len, s->blocks); s->counter = 0; s->bytes_read_in_line = 0; if (s->line_buffer) free(s->line_buffer); s->line_buffer = malloc(s->scan_bytes_per_line); if (s->line_buffer == NULL) { DBG(1, "out of memory (line %d)\n", __LINE__); return SANE_STATUS_NO_MEM; } DBG (5, " %s: Setup block mode - scan_bytes_per_line=%d, pixels_per_line=%d, depth=%d, data_len=%x, block_len=%x, blocks=%d, last_len=%x\n", __func__, s->scan_bytes_per_line, s->params.pixels_per_line, s->params.depth, s->data_len, s->block_len, s->blocks, s->last_len); return SANE_STATUS_GOOD; } /* Call the 0x03 0c command to set scanning parameters from the s->opt list */ static SANE_Status mc_set_scanning_parameters(Magicolor_Scanner * s) { SANE_Status status; unsigned char rs, source, brightness; struct mode_param *mparam = &mode_params[s->val[OPT_MODE].w]; SANE_Int scan_pixels_per_line = 0; DBG(1, "%s\n", __func__); /* Find the resolution in the res list and assign the index to buf[1] */ for (rs=0; rs < s->hw->cap->res_list_size; rs++ ) { if ( s->val[OPT_RESOLUTION].w == s->hw->cap->res_list[rs] ) break; } if (SANE_OPTION_IS_ACTIVE(s->opt[OPT_BRIGHTNESS].cap)) { brightness = s->val[OPT_BRIGHTNESS].w; } else { brightness = 5; } /* ADF used? */ if (strcmp(source_list[s->val[OPT_SOURCE].w], ADF_STR) == 0) { /* Use ADF */ if (s->val[OPT_ADF_MODE].w == 0) { source = 0x01; } else { /* Use duplex */ source = 0x02; } } else { source = 0x00; } /* TODO: Any way to set PREVIEW??? */ /* Remaining bytes unused */ status = cmd_set_scanning_parameters(s, rs, mparam->flags, /* res, color mode */ brightness, 0xff, /* brightness, contrast? */ s->left, s->top, /* top/left start */ s->width, s->height, /* extent */ source); /* source */ if (status != SANE_STATUS_GOOD) DBG (2, "%s: Command cmd_set_scanning_parameters failed, %s\n", __func__, sane_strstatus(status)); /* Now query the scanner for the current image parameters */ status = cmd_get_scanning_parameters (s, &s->params.format, &s->params.depth, &scan_pixels_per_line, &s->params.pixels_per_line, &s->params.lines); if (status != SANE_STATUS_GOOD) { DBG (2, "%s: Command cmd_get_scanning_parameters failed, %s\n", __func__, sane_strstatus(status)); return status; } /* Calculate how many bytes are really used per line */ s->params.bytes_per_line = ceil (s->params.pixels_per_line * s->params.depth / 8.0); if (s->val[OPT_MODE].w == MODE_COLOR) s->params.bytes_per_line *= 3; /* Calculate how many bytes per line will be returned by the scanner. * The values needed for this are returned by get_scannign_parameters */ s->scan_bytes_per_line = ceil (scan_pixels_per_line * s->params.depth / 8.0); if (s->val[OPT_MODE].w == MODE_COLOR) { s->scan_bytes_per_line *= 3; } s->data_len = s->params.lines * s->scan_bytes_per_line; status = mc_setup_block_mode (s); if (status != SANE_STATUS_GOOD) DBG (2, "%s: Command mc_setup_block_mode failed, %s\n", __func__, sane_strstatus(status)); DBG (1, "%s: bytes_read in line: %d\n", __func__, s->bytes_read_in_line); return status; } static SANE_Status mc_check_adf(Magicolor_Scanner * s) { SANE_Status status; unsigned char buf[0x0b]; DBG(5, "%s\n", __func__); status = cmd_request_status(s, buf); if (status != SANE_STATUS_GOOD) return status; if (!(buf[1] & ADF_LOADED)) return SANE_STATUS_NO_DOCS; /* TODO: Check for jam in ADF */ return SANE_STATUS_GOOD; } static SANE_Status mc_scan_finish(Magicolor_Scanner * s) { SANE_Status status; DBG(5, "%s\n", __func__); /* If we have not yet read all data, cancel the scan */ if (s->buf && !s->eof) status = cmd_cancel_scan (s); if (s->line_buffer) free (s->line_buffer); s->line_buffer = NULL; free(s->buf); s->buf = s->end = s->ptr = NULL; /* TODO: Any magicolor command for "scan finished"? */ status = cmd_finish_scan (s); status = cmd_request_error(s); if (status != SANE_STATUS_GOOD) { cmd_cancel_scan (s); return status; } /* XXX required? */ /* TODO: cmd_reset(s);*/ return SANE_STATUS_GOOD; } static void mc_copy_image_data(Magicolor_Scanner * s, SANE_Byte * data, SANE_Int max_length, SANE_Int * length) { DBG (1, "%s: bytes_read in line: %d\n", __func__, s->bytes_read_in_line); if (s->params.format == SANE_FRAME_RGB) { SANE_Int bytes_available, scan_pixels_per_line = s->scan_bytes_per_line/3; *length = 0; while ((max_length >= s->params.bytes_per_line) && (s->ptr < s->end)) { SANE_Int bytes_to_copy = s->scan_bytes_per_line - s->bytes_read_in_line; /* First, fill the line buffer for the current line: */ bytes_available = (s->end - s->ptr); /* Don't copy more than we have buffer and available */ if (bytes_to_copy > bytes_available) bytes_to_copy = bytes_available; if (bytes_to_copy > 0) { memcpy (s->line_buffer + s->bytes_read_in_line, s->ptr, bytes_to_copy); s->ptr += bytes_to_copy; s->bytes_read_in_line += bytes_to_copy; } /* We have filled as much as possible of the current line * with data from the scanner. If we have a complete line, * copy it over. */ if ((s->bytes_read_in_line >= s->scan_bytes_per_line) && (s->params.bytes_per_line <= max_length)) { SANE_Int i; SANE_Byte *line = s->line_buffer; *length += s->params.bytes_per_line; for (i=0; i< s->params.pixels_per_line; ++i) { *data++ = line[0]; *data++ = line[scan_pixels_per_line]; *data++ = line[2 * scan_pixels_per_line]; line++; } max_length -= s->params.bytes_per_line; s->bytes_read_in_line -= s->scan_bytes_per_line; } } } else { /* B/W and Grayscale use the same structure, so we use the same code */ SANE_Int bytes_available; *length = 0; while ((max_length != 0) && (s->ptr < s->end)) { SANE_Int bytes_to_skip, bytes_to_copy; bytes_available = (s->end - s->ptr); bytes_to_copy = s->params.bytes_per_line - s->bytes_read_in_line; bytes_to_skip = s->scan_bytes_per_line - s->bytes_read_in_line; /* Don't copy more than we have buffer */ if (bytes_to_copy > max_length) { bytes_to_copy = max_length; bytes_to_skip = max_length; } /* Don't copy/skip more bytes than we have read in */ if (bytes_to_copy > bytes_available) bytes_to_copy = bytes_available; if (bytes_to_skip > bytes_available) bytes_to_skip = bytes_available; if (bytes_to_copy > 0) { /* we have not yet copied all pixels of the line */ memcpy (data, s->ptr, bytes_to_copy); max_length -= bytes_to_copy; *length += bytes_to_copy; data += bytes_to_copy; } if (bytes_to_skip > 0) { s->ptr += bytes_to_skip; s->bytes_read_in_line += bytes_to_skip; } if (s->bytes_read_in_line >= s->scan_bytes_per_line) s->bytes_read_in_line -= s->scan_bytes_per_line; } } } static SANE_Status mc_init_parameters(Magicolor_Scanner * s) { int dpi, optres; DBG(5, "%s\n", __func__); memset(&s->params, 0, sizeof(SANE_Parameters)); dpi = s->val[OPT_RESOLUTION].w; optres = s->hw->cap->optical_res; if (SANE_UNFIX(s->val[OPT_BR_Y].w) == 0 || SANE_UNFIX(s->val[OPT_BR_X].w) == 0) return SANE_STATUS_INVAL; /* TODO: Use OPT_RESOLUTION or fixed 600dpi for left/top/width/height? */ s->left = ((SANE_UNFIX(s->val[OPT_TL_X].w) / MM_PER_INCH) * optres) + 0.5; s->top = ((SANE_UNFIX(s->val[OPT_TL_Y].w) / MM_PER_INCH) * optres) + 0.5; s->width = ((SANE_UNFIX(s->val[OPT_BR_X].w - s->val[OPT_TL_X].w) / MM_PER_INCH) * optres) + 0.5; s->height = ((SANE_UNFIX(s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w) / MM_PER_INCH) * optres) + 0.5; s->params.pixels_per_line = s->width * dpi / optres + 0.5; s->params.lines = s->height * dpi / optres + 0.5; DBG(1, "%s: resolution = %d, preview = %d\n", __func__, dpi, s->val[OPT_PREVIEW].w); DBG(1, "%s: %p %p tlx %f tly %f brx %f bry %f [mm]\n", __func__, (void *) s, (void *) s->val, SANE_UNFIX(s->val[OPT_TL_X].w), SANE_UNFIX(s->val[OPT_TL_Y].w), SANE_UNFIX(s->val[OPT_BR_X].w), SANE_UNFIX(s->val[OPT_BR_Y].w)); /* * The default color depth is stored in mode_params.depth: */ DBG(1, " %s, vor depth\n", __func__); if (mode_params[s->val[OPT_MODE].w].depth == 1) s->params.depth = 1; else s->params.depth = s->val[OPT_BIT_DEPTH].w; s->params.last_frame = SANE_TRUE; s->params.bytes_per_line = ceil (s->params.depth * s->params.pixels_per_line / 8.0); switch (s->val[OPT_MODE].w) { case MODE_BINARY: case MODE_GRAY: s->params.format = SANE_FRAME_GRAY; break; case MODE_COLOR: s->params.format = SANE_FRAME_RGB; s->params.bytes_per_line *= 3; break; } DBG(1, "%s: Parameters are format=%d, bytes_per_line=%d, lines=%d\n", __func__, s->params.format, s->params.bytes_per_line, s->params.lines); return (s->params.lines > 0) ? SANE_STATUS_GOOD : SANE_STATUS_INVAL; } static SANE_Status mc_start_scan(Magicolor_Scanner * s) { SANE_Status status = cmd_start_scan (s, s->data_len); if (status != SANE_STATUS_GOOD ) { DBG (1, "%s: starting the scan failed (%s)\n", __func__, sane_strstatus(status)); } return status; } static SANE_Status mc_read(struct Magicolor_Scanner *s) { SANE_Status status = SANE_STATUS_GOOD; ssize_t buf_len = 0; /* did we passed everything we read to sane? */ if (s->ptr == s->end) { if (s->eof) return SANE_STATUS_EOF; s->counter++; buf_len = s->block_len; if (s->counter == s->blocks && s->last_len) buf_len = s->last_len; DBG(18, "%s: block %d/%d, size %lu\n", __func__, s->counter, s->blocks, (unsigned long) buf_len); /* receive image data + error code */ status = cmd_read_data (s, s->buf, buf_len); if (status != SANE_STATUS_GOOD) { DBG (1, "%s: Receiving image data failed (%s)\n", __func__, sane_strstatus(status)); cmd_cancel_scan(s); return status; } DBG(18, "%s: successfully read %lu bytes\n", __func__, (unsigned long) buf_len); if (s->counter < s->blocks) { if (s->canceling) { cmd_cancel_scan(s); return SANE_STATUS_CANCELLED; } } else s->eof = SANE_TRUE; s->end = s->buf + buf_len; s->ptr = s->buf; } return status; } /**************************************************************************** * SANE API implementation (high-level functions) ****************************************************************************/ #if HAVE_LIBSNMP static struct MagicolorCap * mc_get_device_from_identification (const char*ident) { int n; for (n = 0; n < NELEMS (magicolor_cap); n++) { if ((strcmp (magicolor_cap[n].model, ident) == 0) || (strcmp (magicolor_cap[n].OID, ident) == 0)) { return &magicolor_cap[n]; } } return NULL; } #endif /* * close_scanner() * * Close the open scanner. Depending on the connection method, a different * close function is called. */ static void close_scanner(Magicolor_Scanner *s) { DBG(7, "%s: fd = %d\n", __func__, s->fd); if (s->fd == -1) return; mc_scan_finish(s); if (s->hw->connection == SANE_MAGICOLOR_NET) { sanei_magicolor_net_close(s); sanei_tcp_close(s->fd); } else if (s->hw->connection == SANE_MAGICOLOR_USB) { sanei_usb_close(s->fd); } s->fd = -1; } static SANE_Bool split_scanner_name (const char *name, char * IP, unsigned int *model) { const char *device = name; const char *qm; *model = 0; /* cut off leading net: */ if (strncmp(device, "net:", 4) == 0) device = &device[4]; qm = strchr(device, '?'); if (qm != NULL) { size_t len = qm-device; strncpy (IP, device, len); IP[len] = '\0'; qm++; if (strncmp(qm, "model=", 6) == 0) { qm += 6; if (!sscanf(qm, "0x%x", model)) sscanf(qm, "%x", model); } } else { strcpy (IP, device); } return SANE_TRUE; } /* * open_scanner() * * Open the scanner device. Depending on the connection method, * different open functions are called. */ static SANE_Status open_scanner(Magicolor_Scanner *s) { SANE_Status status = 0; DBG(7, "%s: %s\n", __func__, s->hw->sane.name); if (s->fd != -1) { DBG(7, "scanner is already open: fd = %d\n", s->fd); return SANE_STATUS_GOOD; /* no need to open the scanner */ } if (s->hw->connection == SANE_MAGICOLOR_NET) { /* device name has the form net:ipaddr?model=... */ char IP[1024]; unsigned int model = 0; if (!split_scanner_name (s->hw->sane.name, IP, &model)) return SANE_STATUS_INVAL; status = sanei_tcp_open(IP, 4567, &s->fd); if (model>0) mc_set_device (s, model); if (status == SANE_STATUS_GOOD) { DBG(7, "awaiting welcome message\n"); status = sanei_magicolor_net_open (s); } } else if (s->hw->connection == SANE_MAGICOLOR_USB) { status = sanei_usb_open(s->hw->sane.name, &s->fd); if (s->hw->cap->out_ep>0) sanei_usb_set_endpoint (s->fd, USB_DIR_OUT | USB_ENDPOINT_TYPE_BULK, s->hw->cap->out_ep); if (s->hw->cap->in_ep>0) sanei_usb_set_endpoint (s->fd, USB_DIR_IN | USB_ENDPOINT_TYPE_BULK, s->hw->cap->in_ep); } if (status == SANE_STATUS_ACCESS_DENIED) { DBG(1, "please check that you have permissions on the device.\n"); DBG(1, "if this is a multi-function device with a printer,\n"); DBG(1, "disable any conflicting driver (like usblp).\n"); } if (status != SANE_STATUS_GOOD) DBG(1, "%s open failed: %s\n", s->hw->sane.name, sane_strstatus(status)); else DBG(3, "scanner opened\n"); return status; } static SANE_Status detect_usb(struct Magicolor_Scanner *s) { SANE_Status status; int vendor, product; int i, numIds; SANE_Bool is_valid; /* if the sanei_usb_get_vendor_product call is not supported, * then we just ignore this and rely on the user to config * the correct device. */ status = sanei_usb_get_vendor_product(s->fd, &vendor, &product); if (status != SANE_STATUS_GOOD) { DBG(1, "the device cannot be verified - will continue\n"); return SANE_STATUS_GOOD; } /* check the vendor ID to see if we are dealing with an MAGICOLOR device */ if (vendor != SANE_MAGICOLOR_VENDOR_ID) { /* this is not a supported vendor ID */ DBG(1, "not an Magicolor device at %s (vendor id=0x%x)\n", s->hw->sane.name, vendor); return SANE_STATUS_INVAL; } numIds = sanei_magicolor_getNumberOfUSBProductIds(); is_valid = SANE_FALSE; i = 0; /* check all known product IDs to verify that we know * about the device */ while (i != numIds && !is_valid) { if (product == sanei_magicolor_usb_product_ids[i]) is_valid = SANE_TRUE; i++; } if (is_valid == SANE_FALSE) { DBG(1, "the device at %s is not a supported (product id=0x%x)\n", s->hw->sane.name, product); return SANE_STATUS_INVAL; } DBG(2, "found valid Magicolor scanner: 0x%x/0x%x (vendorID/productID)\n", vendor, product); mc_set_device(s, product); return SANE_STATUS_GOOD; } /* * used by attach* and sane_get_devices * a ptr to a single-linked list of Magicolor_Device structs * a ptr to a null term array of ptrs to SANE_Device structs */ static int num_devices; /* number of scanners attached to backend */ static Magicolor_Device *first_dev; /* first MAGICOLOR scanner in list */ static const SANE_Device **devlist = NULL; static struct Magicolor_Scanner * scanner_create(struct Magicolor_Device *dev, SANE_Status *status) { struct Magicolor_Scanner *s; s = malloc(sizeof(struct Magicolor_Scanner)); if (s == NULL) { *status = SANE_STATUS_NO_MEM; return NULL; } memset(s, 0x00, sizeof(struct Magicolor_Scanner)); s->fd = -1; s->hw = dev; return s; } static struct Magicolor_Scanner * device_detect(const char *name, int type, SANE_Status *status) { struct Magicolor_Scanner *s; struct Magicolor_Device *dev; /* try to find the device in our list */ for (dev = first_dev; dev; dev = dev->next) { if (strcmp(dev->sane.name, name) == 0) { dev->missing = 0; DBG (10, "%s: Device %s already attached!\n", __func__, name); return scanner_create(dev, status); } } if (type == SANE_MAGICOLOR_NODEV) { *status = SANE_STATUS_INVAL; return NULL; } /* alloc and clear our device structure */ dev = malloc(sizeof(*dev)); if (!dev) { *status = SANE_STATUS_NO_MEM; return NULL; } memset(dev, 0x00, sizeof(struct Magicolor_Device)); s = scanner_create(dev, status); if (s == NULL) return NULL; mc_dev_init(dev, name, type); *status = open_scanner(s); if (*status != SANE_STATUS_GOOD) { free(s); return NULL; } /* from now on, close_scanner() must be called */ /* USB requires special care */ if (dev->connection == SANE_MAGICOLOR_USB) { *status = detect_usb(s); } if (*status != SANE_STATUS_GOOD) goto close; /* set name and model (if not already set) */ if (dev->model == NULL) mc_set_model(s, "generic", 7); dev->name = strdup(name); dev->sane.name = dev->name; *status = mc_discover_capabilities(s); if (*status != SANE_STATUS_GOOD) goto close; if (source_list[0] == NULL || dev->cap->dpi_range.min == 0) { DBG(1, "something is wrong in the discovery process, aborting.\n"); *status = SANE_STATUS_IO_ERROR; goto close; } mc_dev_post_init(dev); /* add this scanner to the device list */ num_devices++; dev->missing = 0; dev->next = first_dev; first_dev = dev; return s; close: close_scanner(s); free(s); return NULL; } #if HAVE_LIBSNMP /* Keep a linked list of already observed IP addresses */ /* typedef struct snmp_ip SNMP_IP; */ typedef struct snmp_ip { char ip_addr[1024]; struct snmp_ip*next; } snmp_ip; typedef struct { int nr; snmp_ip*handled; snmp_ip*detected; } snmp_discovery_data; /** Handle one SNMP response (whether received sync or async) and if describes * a magicolor device, attach it. Returns the number of attached devices (0 * or 1) */ static int mc_network_discovery_handle (struct snmp_pdu *pdu, snmp_discovery_data *magic) { netsnmp_variable_list *varlist = pdu->variables, *vp; oid anOID[MAX_OID_LEN]; size_t anOID_len = MAX_OID_LEN; /* Device information variables */ char ip_addr[1024] = ""; char model[1024] = ""; char device[1024] = ""; /* remote IP detection variables */ netsnmp_indexed_addr_pair *responder = (netsnmp_indexed_addr_pair *) pdu->transport_data; struct sockaddr_in *remote = NULL; struct MagicolorCap *cap; snmp_ip *ip = NULL; DBG(5, "%s: Handling SNMP response \n", __func__); if (responder == NULL || pdu->transport_data_length != sizeof(netsnmp_indexed_addr_pair )) { DBG(1, "%s: Unable to extract IP address from SNMP response.\n", __func__); return 0; } remote = (struct sockaddr_in *) &(responder->remote_addr); if (remote == NULL) { DBG(1, "%s: Unable to extract IP address from SNMP response.\n", __func__); return 0; } snprintf(ip_addr, sizeof(ip_addr), "%s", inet_ntoa(remote->sin_addr)); DBG(35, "%s: IP Address of responder is %s\n", __func__, ip_addr); if (magic) ip = magic->handled; while (ip) { if (strcmp (ip->ip_addr, ip_addr) == 0) { DBG (5, "%s: Already handled device %s, skipping\n", __func__, ip_addr); return 0; } ip = ip->next; } if (magic) { snmp_ip *new_handled = malloc(sizeof(snmp_ip)); strcpy (&new_handled->ip_addr[0], ip_addr); new_handled->next = magic->handled; magic->handled = new_handled; } /* System Object ID (Unique OID identifying model) * This determines whether we really have a magicolor device */ anOID_len = MAX_OID_LEN; read_objid(MAGICOLOR_SNMP_SYSOBJECT_OID, anOID, &anOID_len); vp = find_varbind_in_list (varlist, anOID, anOID_len); if (vp) { size_t value_len = vp->val_len/sizeof(oid); if (vp->type != ASN_OBJECT_ID) { DBG (3, "%s: SystemObjectID does not return an OID, device is not a magicolor device\n", __func__); return 0; } // Make sure that snprint_objid gives us just numbers, instead of a // SNMPv2-SMI::enterprises prefix. netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, NETSNMP_OID_OUTPUT_NUMERIC); snprint_objid (device, sizeof(device), vp->val.objid, value_len); DBG (5, "%s: Device object ID is '%s'\n", __func__, device); anOID_len = MAX_OID_LEN; read_objid (MAGICOLOR_SNMP_DEVICE_TREE, anOID, &anOID_len); if (netsnmp_oid_is_subtree (anOID, anOID_len, vp->val.objid, value_len) == 0) { DBG (5, "%s: Device appears to be a magicolor device (OID=%s)\n", __func__, device); } else { DBG (5, "%s: Device is not a Magicolor device\n", __func__); return 0; } } /* Retrieve sysDescr (i.e. model name) */ anOID_len = MAX_OID_LEN; read_objid(MAGICOLOR_SNMP_SYSDESCR_OID, anOID, &anOID_len); vp = find_varbind_in_list (varlist, anOID, anOID_len); if (vp) { size_t model_len = min(vp->val_len, sizeof(model) - 1); memcpy(model, vp->val.string, model_len); model[model_len] = '\0'; DBG (5, "%s: Found model: %s\n", __func__, model); } DBG (1, "%s: Detected device '%s' on IP %s\n", __func__, model, ip_addr); vp = pdu->variables; /* TODO: attach the IP with attach_one_net(ip) */ cap = mc_get_device_from_identification (device); if (cap) { DBG(1, "%s: Found autodiscovered device: %s (type 0x%x)\n", __func__, cap->model, cap->id); attach_one_net (ip_addr, cap->id); if (magic) { snmp_ip *new_detected = malloc(sizeof(snmp_ip)); strcpy (&new_detected->ip_addr[0], ip_addr); new_detected->next = magic->detected; magic->detected = new_detected; } return 1; } return 0; } static int mc_network_discovery_cb (int operation, struct snmp_session *sp, int reqid, struct snmp_pdu *pdu, void *magic) { NOT_USED (reqid); NOT_USED (sp); DBG(5, "%s: Received broadcast response \n", __func__); if (operation == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) { snmp_discovery_data *m = (snmp_discovery_data*)magic; int nr = mc_network_discovery_handle (pdu, m); m->nr += nr; DBG(5, "%s: Added %d discovered host(s) for SNMP response.\n", __func__, nr); } return 0; } #endif /* Use SNMP for automatic network discovery. If host is given, try to detect * that one host (using sync SNMP, otherwise send an SNMP broadcast (async). */ static int mc_network_discovery(const char*host) { #if HAVE_LIBSNMP netsnmp_session session, *ss; netsnmp_pdu *pdu; oid anOID[MAX_OID_LEN]; size_t anOID_len = MAX_OID_LEN; snmp_discovery_data magic; magic.nr = 0; magic.handled = 0; magic.detected = 0; DBG(1, "%s: running network discovery \n", __func__); /* Win32: init winsock */ SOCK_STARTUP; init_snmp("sane-magicolor-backend"); snmp_sess_init (&session); session.version = SNMP_VERSION_2c; session.community = (u_char *) "public"; session.community_len = strlen ((char *)session.community); if (host) { session.peername = (char *) host; } else { /* Do a network discovery via a broadcast */ session.peername = "255.255.255.255"; session.flags |= SNMP_FLAGS_UDP_BROADCAST; session.callback = mc_network_discovery_cb; session.callback_magic = &magic; } ss = snmp_open (&session); /* establish the session */ if (!ss) { snmp_sess_perror ("ack", &session); SOCK_CLEANUP; return 0; } /* Create the PDU for the data for our request and add the three * desired OIDs to the PDU */ pdu = snmp_pdu_create (SNMP_MSG_GET); /* SNMPv2-MIB::sysDescr.0 */ anOID_len = MAX_OID_LEN; if (read_objid(MAGICOLOR_SNMP_SYSDESCR_OID, anOID, &anOID_len)) { snmp_add_null_var (pdu, anOID, anOID_len); } /* SNMPv2-MIB::sysObjectID.0 */ anOID_len = MAX_OID_LEN; if (read_objid(MAGICOLOR_SNMP_SYSOBJECT_OID, anOID, &anOID_len)) { snmp_add_null_var (pdu, anOID, anOID_len); } /* IF-MIB::ifPhysAddress.1 */ anOID_len = MAX_OID_LEN; if (read_objid(MAGICOLOR_SNMP_MAC_OID, anOID, &anOID_len)) { snmp_add_null_var (pdu, anOID, anOID_len); } /* TODO: Add more interesting OIDs, in particular vendor OIDs */ /* Now send out the request and wait for responses for some time. * If we get a response, connect to that device (in the callback), * otherwise we probably don't have a magicolor device in the * LAN (or SNMP is turned off, in which case we have no way to detect * it. */ DBG(100, "%s: Sending SNMP packet\n", __func__); if (host) { /* sync request to given hostname, immediately read the reply */ netsnmp_pdu *response = 0; int status = snmp_synch_response(ss, pdu, &response); if (status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR) { magic.nr = mc_network_discovery_handle (response, &magic); } if (response) snmp_free_pdu(response); } else { /* No hostname, so do a broadcast */ struct timeval nowtime, endtime; /* end time for SNMP scan */ struct timeval timeout; int i=0; if (!snmp_send(ss, pdu)) { snmp_free_pdu(pdu); DBG(100, "%s: Sending SNMP packet NOT successful\n", __func__); return 0; } /* listen for responses for MC_AutoDetectionTimeout milliseconds: */ /* First get the final timeout time */ gettimeofday (&nowtime, NULL); timeout.tv_sec = MC_SNMP_Timeout / 1000; timeout.tv_usec = (MC_SNMP_Timeout % 1000) * 1000; timeradd (&nowtime, &timeout, &endtime); while (timercmp(&nowtime, &endtime, <)) { int fds = 0, block = 0; fd_set fdset; DBG(1, " loop=%d\n", i++); timeout.tv_sec = 0; /* Use a 125ms timeout for select. If we get a response, * the loop will be entered earlier again, anyway */ timeout.tv_usec = 125000; FD_ZERO (&fdset); snmp_select_info (&fds, &fdset, &timeout, &block); fds = select (fds, &fdset, NULL, NULL, /*block?NULL:*/&timeout); if (fds) snmp_read(&fdset); else snmp_timeout(); gettimeofday(&nowtime, NULL); } /* Clean up the data in magic */ while (magic.handled) { snmp_ip *tmp = magic.handled->next; free (magic.handled); magic.handled = tmp; } while (magic.detected) { snmp_ip *tmp = magic.detected->next; free (magic.detected); magic.detected = tmp; } } /* Clean up */ snmp_close(ss); SOCK_CLEANUP; DBG (5, "%s: Discovered %d host(s)\n", __func__, magic.nr); return magic.nr; #else DBG (1, "%s: net-snmp library not enabled, auto-detecting network scanners not supported.\n", __func__); NOT_USED (host); return 0; #endif } static SANE_Status attach(const char *name, int type) { SANE_Status status; Magicolor_Scanner *s; DBG(7, "%s: devname = %s, type = %d\n", __func__, name, type); s = device_detect(name, type, &status); if(s == NULL) return status; close_scanner(s); free(s); return status; } SANE_Status attach_one_usb(const char *dev) { DBG(7, "%s: dev = %s\n", __func__, dev); return attach(dev, SANE_MAGICOLOR_USB); } static SANE_Status attach_one_net(const char *dev, unsigned int model) { char name[1024]; DBG(7, "%s: dev = %s\n", __func__, dev); if (model > 0) { snprintf(name, 1024, "net:%s?model=0x%x", dev, model); } else { snprintf(name, 1024, "net:%s", dev); } return attach(name, SANE_MAGICOLOR_NET); } static SANE_Status attach_one_config(SANEI_Config __sane_unused__ *config, const char *line, void *data) { int vendor, product, timeout; SANE_Bool local_only = *(SANE_Bool*) data; int len = strlen(line); DBG(7, "%s: len = %d, line = %s\n", __func__, len, line); if (sscanf(line, "usb %i %i", &vendor, &product) == 2) { /* add the vendor and product IDs to the list of * known devices before we call the attach function */ int numIds = sanei_magicolor_getNumberOfUSBProductIds(); if (vendor != SANE_MAGICOLOR_VENDOR_ID) return SANE_STATUS_INVAL; /* this is not a KONICA MINOLTA device */ sanei_magicolor_usb_product_ids[numIds - 1] = product; sanei_usb_attach_matching_devices(line, attach_one_usb); } else if (strncmp(line, "usb", 3) == 0 && len == 3) { int i, numIds; numIds = sanei_magicolor_getNumberOfUSBProductIds(); for (i = 0; i < numIds; i++) { sanei_usb_find_devices(SANE_MAGICOLOR_VENDOR_ID, sanei_magicolor_usb_product_ids[i], attach_one_usb); } } else if (strncmp(line, "net", 3) == 0) { if (!local_only) { /* remove the "net" sub string */ const char *name = sanei_config_skip_whitespace(line + 3); char IP[1024]; unsigned int model = 0; if (strncmp(name, "autodiscovery", 13) == 0) { DBG (50, "%s: Initiating network autodiscovervy via SNMP\n", __func__); mc_network_discovery(NULL); } else if (sscanf(name, "%s %x", IP, &model) == 2) { DBG(50, "%s: Using network device on IP %s, forcing model 0x%x\n", __func__, IP, model); attach_one_net(IP, model); } else { /* use SNMP to detect the type. If not successful, * add the host with model type 0 */ DBG(50, "%s: Using network device on IP %s, trying to autodetect model\n", __func__, IP); if (mc_network_discovery(name)==0) { DBG(1, "%s: Autodetecting device model failed, using default model\n", __func__); attach_one_net(name, 0); } } } } else if (sscanf(line, "snmp-timeout %i\n", &timeout)) { /* Timeout for SNMP network discovery */ DBG(50, "%s: SNMP timeout set to %d\n", __func__, timeout); MC_SNMP_Timeout = timeout; } else if (sscanf(line, "scan-data-timeout %i\n", &timeout)) { /* Timeout for scan data requests */ DBG(50, "%s: Scan data timeout set to %d\n", __func__, timeout); MC_Scan_Data_Timeout = timeout; } else if (sscanf(line, "request-timeout %i\n", &timeout)) { /* Timeout for all other read requests */ DBG(50, "%s: Request timeout set to %d\n", __func__, timeout); MC_Request_Timeout = timeout; } else { /* TODO: Warning about unparsable line! */ } return SANE_STATUS_GOOD; } static void free_devices(void) { Magicolor_Device *dev, *next; DBG(5, "%s\n", __func__); for (dev = first_dev; dev; dev = next) { next = dev->next; free(dev->name); free(dev->model); free(dev); } if (devlist) free(devlist); devlist = NULL; first_dev = NULL; } SANE_Status sane_init(SANE_Int *version_code, SANE_Auth_Callback __sane_unused__ authorize) { DBG_INIT(); DBG(2, "%s: " PACKAGE " " VERSION "\n", __func__); DBG(1, "magicolor backend, version %i.%i.%i\n", MAGICOLOR_VERSION, MAGICOLOR_REVISION, MAGICOLOR_BUILD); if (version_code != NULL) *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, MAGICOLOR_BUILD); sanei_usb_init(); return SANE_STATUS_GOOD; } /* Clean up the list of attached scanners. */ void sane_exit(void) { DBG(5, "%s\n", __func__); free_devices(); } SANE_Status sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only) { Magicolor_Device *dev, *s, *prev=0; int i; DBG(5, "%s\n", __func__); sanei_usb_init(); /* mark all existing scanners as missing, attach_one will remove mark */ for (s = first_dev; s; s = s->next) { s->missing = 1; } /* Read the config, mark each device as found, possibly add new devs */ sanei_configure_attach(MAGICOLOR_CONFIG_FILE, NULL, attach_one_config, &local_only); /*delete missing scanners from list*/ for (s = first_dev; s;) { if (s->missing) { DBG (5, "%s: missing scanner %s\n", __func__, s->name); /*splice s out of list by changing pointer in prev to next*/ if (prev) { prev->next = s->next; free (s); s = prev->next; num_devices--; } else { /*remove s from head of list */ first_dev = s->next; free(s); s = first_dev; prev=NULL; num_devices--; } } else { prev = s; s = prev->next; } } DBG (15, "%s: found %d scanner(s)\n", __func__, num_devices); for (s = first_dev; s; s=s->next) { DBG (15, "%s: found scanner %s\n", __func__, s->name); } if (devlist) free (devlist); devlist = malloc((num_devices + 1) * sizeof(devlist[0])); if (!devlist) { DBG(1, "out of memory (line %d)\n", __LINE__); return SANE_STATUS_NO_MEM; } DBG(5, "%s - results:\n", __func__); for (i = 0, dev = first_dev; i < num_devices && dev; dev = dev->next, i++) { DBG(1, " %d (%d): %s\n", i, dev->connection, dev->model); devlist[i] = &dev->sane; } devlist[i] = NULL; if(device_list){ *device_list = devlist; } return SANE_STATUS_GOOD; } static SANE_Status init_options(Magicolor_Scanner *s) { int i; SANE_Word *res_list; for (i = 0; i < NUM_OPTIONS; i++) { s->opt[i].size = sizeof(SANE_Word); s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* "Scan Mode" group: */ s->opt[OPT_MODE_GROUP].name = SANE_NAME_STANDARD; s->opt[OPT_MODE_GROUP].title = SANE_TITLE_STANDARD; s->opt[OPT_MODE_GROUP].desc = SANE_DESC_STANDARD; s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MODE_GROUP].cap = 0; /* scan mode */ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; 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 = max_string_size(mode_list); s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_MODE].constraint.string_list = mode_list; s->val[OPT_MODE].w = 0; /* Binary */ /* bit depth */ s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT; s->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_NONE; s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_BIT_DEPTH].constraint.word_list = s->hw->cap->depth_list; s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; s->val[OPT_BIT_DEPTH].w = s->hw->cap->depth_list[1]; /* the first "real" element is the default */ if (s->hw->cap->depth_list[0] == 1) /* only one element in the list -> hide the option */ s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; /* brightness */ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT; s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE; s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BRIGHTNESS].constraint.range = &s->hw->cap->brightness; s->val[OPT_BRIGHTNESS].w = 5; /* Normal */ /* resolution */ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; res_list = malloc((s->hw->cap->res_list_size + 1) * sizeof(SANE_Word)); if (res_list == NULL) { return SANE_STATUS_NO_MEM; } *(res_list) = s->hw->cap->res_list_size; memcpy(&(res_list[1]), s->hw->cap->res_list, s->hw->cap->res_list_size * sizeof(SANE_Word)); s->opt[OPT_RESOLUTION].constraint.word_list = res_list; s->val[OPT_RESOLUTION].w = s->hw->cap->dpi_range.min; /* preview */ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; s->val[OPT_PREVIEW].w = SANE_FALSE; /* source */ s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE; s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE; s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE; s->opt[OPT_SOURCE].type = SANE_TYPE_STRING; s->opt[OPT_SOURCE].size = max_string_size(source_list); s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_SOURCE].constraint.string_list = source_list; s->val[OPT_SOURCE].w = 0; /* always use Flatbed as default */ s->opt[OPT_ADF_MODE].name = "adf-mode"; s->opt[OPT_ADF_MODE].title = SANE_I18N("ADF Mode"); s->opt[OPT_ADF_MODE].desc = SANE_I18N("Selects the ADF mode (simplex/duplex)"); s->opt[OPT_ADF_MODE].type = SANE_TYPE_STRING; s->opt[OPT_ADF_MODE].size = max_string_size(adf_mode_list); s->opt[OPT_ADF_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_ADF_MODE].constraint.string_list = adf_mode_list; s->val[OPT_ADF_MODE].w = 0; /* simplex */ if ((!s->hw->cap->ADF) || (s->hw->cap->adf_duplex == SANE_FALSE)) s->opt[OPT_ADF_MODE].cap |= SANE_CAP_INACTIVE; /* "Geometry" group: */ s->opt[OPT_GEOMETRY_GROUP].name = SANE_NAME_GEOMETRY; s->opt[OPT_GEOMETRY_GROUP].title = SANE_TITLE_GEOMETRY; s->opt[OPT_GEOMETRY_GROUP].desc = SANE_DESC_GEOMETRY; s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; /* top-left x */ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; s->opt[OPT_TL_X].unit = SANE_UNIT_MM; s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_X].constraint.range = s->hw->x_range; s->val[OPT_TL_X].w = 0; /* top-left y */ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_Y].constraint.range = s->hw->y_range; s->val[OPT_TL_Y].w = 0; /* bottom-right x */ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; s->opt[OPT_BR_X].unit = SANE_UNIT_MM; s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_X].constraint.range = s->hw->x_range; s->val[OPT_BR_X].w = s->hw->x_range->max; /* bottom-right y */ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_Y].constraint.range = s->hw->y_range; s->val[OPT_BR_Y].w = s->hw->y_range->max; return SANE_STATUS_GOOD; } SANE_Status sane_open(SANE_String_Const name, SANE_Handle *handle) { SANE_Status status; Magicolor_Scanner *s = NULL; int l = strlen(name); DBG(7, "%s: name = %s\n", __func__, name); /* probe if empty device name provided */ if (l == 0) { status = sane_get_devices(NULL,0); if (status != SANE_STATUS_GOOD) { return status; } if (first_dev == NULL) { DBG(1, "no device detected\n"); return SANE_STATUS_INVAL; } s = device_detect(first_dev->sane.name, first_dev->connection, &status); if (s == NULL) { DBG(1, "cannot open a perfectly valid device (%s)," " please report to the authors\n", name); return SANE_STATUS_INVAL; } } else { if (strncmp(name, "net:", 4) == 0) { s = device_detect(name, SANE_MAGICOLOR_NET, &status); if (s == NULL) return status; } else if (strncmp(name, "libusb:", 7) == 0) { s = device_detect(name, SANE_MAGICOLOR_USB, &status); if (s == NULL) return status; } else { /* as a last resort, check for a match * in the device list. This should handle platforms without libusb. */ if (first_dev == NULL) { status = sane_get_devices(NULL,0); if (status != SANE_STATUS_GOOD) { return status; } } s = device_detect(name, SANE_MAGICOLOR_NODEV, &status); if (s == NULL) { DBG(1, "invalid device name: %s\n", name); return SANE_STATUS_INVAL; } } } /* s is always valid here */ DBG(1, "handle obtained\n"); init_options(s); *handle = (SANE_Handle) s; status = open_scanner(s); if (status != SANE_STATUS_GOOD) { free(s); return status; } return status; } void sane_close(SANE_Handle handle) { Magicolor_Scanner *s; /* * XXX Test if there is still data pending from * the scanner. If so, then do a cancel */ s = (Magicolor_Scanner *) handle; if (s->fd != -1) close_scanner(s); free(s); } const SANE_Option_Descriptor * sane_get_option_descriptor(SANE_Handle handle, SANE_Int option) { Magicolor_Scanner *s = (Magicolor_Scanner *) handle; if (option < 0 || option >= NUM_OPTIONS) return NULL; return s->opt + option; } static const SANE_String_Const * search_string_list(const SANE_String_Const *list, SANE_String value) { while (*list != NULL && strcmp(value, *list) != 0) list++; return ((*list == NULL) ? NULL : list); } /* Activate, deactivate an option. Subroutines so we can add debugging info if we want. The change flag is set to TRUE if we changed an option. If we did not change an option, then the value of the changed flag is not modified. */ static void activateOption(Magicolor_Scanner *s, SANE_Int option, SANE_Bool *change) { if (!SANE_OPTION_IS_ACTIVE(s->opt[option].cap)) { s->opt[option].cap &= ~SANE_CAP_INACTIVE; *change = SANE_TRUE; } } static void deactivateOption(Magicolor_Scanner *s, SANE_Int option, SANE_Bool *change) { if (SANE_OPTION_IS_ACTIVE(s->opt[option].cap)) { s->opt[option].cap |= SANE_CAP_INACTIVE; *change = SANE_TRUE; } } static SANE_Status getvalue(SANE_Handle handle, SANE_Int option, void *value) { Magicolor_Scanner *s = (Magicolor_Scanner *) handle; SANE_Option_Descriptor *sopt = &(s->opt[option]); Option_Value *sval = &(s->val[option]); DBG(17, "%s: option = %d\n", __func__, option); switch (option) { case OPT_NUM_OPTS: case OPT_BIT_DEPTH: case OPT_BRIGHTNESS: case OPT_RESOLUTION: case OPT_PREVIEW: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: *((SANE_Word *) value) = sval->w; break; case OPT_MODE: case OPT_SOURCE: case OPT_ADF_MODE: strcpy((char *) value, sopt->constraint.string_list[sval->w]); break; default: return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } /* * Handles setting the source (flatbed, or auto document feeder (ADF)). * */ static void change_source(Magicolor_Scanner *s, SANE_Int optindex, char *value) { int force_max = SANE_FALSE; SANE_Bool dummy; DBG(1, "%s: optindex = %d, source = '%s'\n", __func__, optindex, value); if (s->val[OPT_SOURCE].w == optindex) return; s->val[OPT_SOURCE].w = optindex; if (s->val[OPT_TL_X].w == s->hw->x_range->min && s->val[OPT_TL_Y].w == s->hw->y_range->min && s->val[OPT_BR_X].w == s->hw->x_range->max && s->val[OPT_BR_Y].w == s->hw->y_range->max) { force_max = SANE_TRUE; } if (strcmp(ADF_STR, value) == 0) { s->hw->x_range = &s->hw->cap->adf_x_range; s->hw->y_range = &s->hw->cap->adf_y_range; if (s->hw->cap->adf_duplex) { activateOption(s, OPT_ADF_MODE, &dummy); } else { deactivateOption(s, OPT_ADF_MODE, &dummy); s->val[OPT_ADF_MODE].w = 0; } DBG(1, "adf activated (%d)\n",s->hw->cap->adf_duplex); } else { /* ADF not active */ s->hw->x_range = &s->hw->cap->fbf_x_range; s->hw->y_range = &s->hw->cap->fbf_y_range; deactivateOption(s, OPT_ADF_MODE, &dummy); } s->opt[OPT_BR_X].constraint.range = s->hw->x_range; s->opt[OPT_BR_Y].constraint.range = s->hw->y_range; if (s->val[OPT_TL_X].w < s->hw->x_range->min || force_max) s->val[OPT_TL_X].w = s->hw->x_range->min; if (s->val[OPT_TL_Y].w < s->hw->y_range->min || force_max) s->val[OPT_TL_Y].w = s->hw->y_range->min; if (s->val[OPT_BR_X].w > s->hw->x_range->max || force_max) s->val[OPT_BR_X].w = s->hw->x_range->max; if (s->val[OPT_BR_Y].w > s->hw->y_range->max || force_max) s->val[OPT_BR_Y].w = s->hw->y_range->max; } static SANE_Status setvalue(SANE_Handle handle, SANE_Int option, void *value, SANE_Int *info) { Magicolor_Scanner *s = (Magicolor_Scanner *) handle; SANE_Option_Descriptor *sopt = &(s->opt[option]); Option_Value *sval = &(s->val[option]); SANE_Status status; const SANE_String_Const *optval = NULL; int optindex = 0; SANE_Bool reload = SANE_FALSE; DBG(17, "%s: option = %d, value = %p, as word: %d\n", __func__, option, value, *(SANE_Word *) value); status = sanei_constrain_value(sopt, value, info); if (status != SANE_STATUS_GOOD) return status; if (info && value && (*info & SANE_INFO_INEXACT) && sopt->type == SANE_TYPE_INT) DBG(17, "%s: constrained val = %d\n", __func__, *(SANE_Word *) value); if (sopt->constraint_type == SANE_CONSTRAINT_STRING_LIST) { optval = search_string_list(sopt->constraint.string_list, (char *) value); if (optval == NULL) return SANE_STATUS_INVAL; optindex = optval - sopt->constraint.string_list; } switch (option) { case OPT_MODE: { sval->w = optindex; /* if binary, then disable the bit depth selection */ if (optindex == 0) { s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; } else { if (s->hw->cap->depth_list[0] == 1) s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; else { s->opt[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE; s->val[OPT_BIT_DEPTH].w = mode_params[optindex].depth; } } reload = SANE_TRUE; break; } case OPT_BIT_DEPTH: sval->w = *((SANE_Word *) value); mode_params[s->val[OPT_MODE].w].depth = sval->w; reload = SANE_TRUE; break; case OPT_RESOLUTION: sval->w = *((SANE_Word *) value); DBG(17, "setting resolution to %d\n", sval->w); reload = SANE_TRUE; break; case OPT_BR_X: case OPT_BR_Y: if (SANE_UNFIX(*((SANE_Word *) value)) == 0) { DBG(17, "invalid br-x or br-y\n"); return SANE_STATUS_INVAL; } // fall through case OPT_TL_X: case OPT_TL_Y: sval->w = *((SANE_Word *) value); DBG(17, "setting size to %f\n", SANE_UNFIX(sval->w)); if (NULL != info) *info |= SANE_INFO_RELOAD_PARAMS; break; case OPT_SOURCE: change_source(s, optindex, (char *) value); reload = SANE_TRUE; break; case OPT_ADF_MODE: sval->w = optindex; /* Simple lists */ break; case OPT_BRIGHTNESS: case OPT_PREVIEW: /* needed? */ sval->w = *((SANE_Word *) value); break; default: return SANE_STATUS_INVAL; } if (reload && info != NULL) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; DBG(17, "%s: end\n", __func__); return SANE_STATUS_GOOD; } SANE_Status sane_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int *info) { DBG(17, "%s: action = %x, option = %d\n", __func__, action, option); if (option < 0 || option >= NUM_OPTIONS) return SANE_STATUS_INVAL; if (info != NULL) *info = 0; switch (action) { case SANE_ACTION_GET_VALUE: return getvalue(handle, option, value); case SANE_ACTION_SET_VALUE: return setvalue(handle, option, value, info); default: return SANE_STATUS_INVAL; } return SANE_STATUS_INVAL; } SANE_Status sane_get_parameters(SANE_Handle handle, SANE_Parameters *params) { Magicolor_Scanner *s = (Magicolor_Scanner *) handle; DBG(5, "%s\n", __func__); if (params == NULL) DBG(1, "%s: params is NULL\n", __func__); /* * If sane_start was already called, then just retrieve the parameters * from the scanner data structure */ if (!s->eof && s->ptr != NULL) { DBG(5, "scan in progress, returning saved params structure\n"); } else { /* otherwise initialize the params structure and gather the data */ mc_init_parameters(s); } if (params != NULL) *params = s->params; print_params(s->params); return SANE_STATUS_GOOD; } /* * This function is part of the SANE API and gets called from the front end to * start the scan process. */ SANE_Status sane_start(SANE_Handle handle) { Magicolor_Scanner *s = (Magicolor_Scanner *) handle; SANE_Status status; DBG(5, "%s\n", __func__); /* calc scanning parameters */ status = mc_init_parameters(s); if (status != SANE_STATUS_GOOD) return status; print_params(s->params); /* set scanning parameters; also query the current image * parameters from the sanner and save * them to s->params */ status = mc_set_scanning_parameters(s); if (status != SANE_STATUS_GOOD) return status; /* if we scan from ADF, check if it is loaded */ if (strcmp(source_list[s->val[OPT_SOURCE].w], ADF_STR) == 0) { status = mc_check_adf(s); if (status != SANE_STATUS_GOOD) return status; } /* prepare buffer here so that a memory allocation failure * will leave the scanner in a sane state. */ s->buf = realloc(s->buf, s->block_len); if (s->buf == NULL) return SANE_STATUS_NO_MEM; s->eof = SANE_FALSE; s->ptr = s->end = s->buf; s->canceling = SANE_FALSE; /* start scanning */ DBG(1, "%s: scanning...\n", __func__); status = mc_start_scan(s); if (status != SANE_STATUS_GOOD) { DBG(1, "%s: start failed: %s\n", __func__, sane_strstatus(status)); return status; } return status; } /* this moves data from our buffers to SANE */ SANE_Status sane_read(SANE_Handle handle, SANE_Byte *data, SANE_Int max_length, SANE_Int *length) { SANE_Status status; Magicolor_Scanner *s = (Magicolor_Scanner *) handle; if (s->buf == NULL || s->canceling) return SANE_STATUS_CANCELLED; *length = 0; status = mc_read(s); if (status == SANE_STATUS_CANCELLED) { mc_scan_finish(s); return status; } DBG(18, "moving data %p %p, %d (%d lines)\n", (void *) s->ptr, (void *) s->end, max_length, max_length / s->params.bytes_per_line); mc_copy_image_data(s, data, max_length, length); DBG(18, "%d lines read, status: %d\n", *length / s->params.bytes_per_line, status); /* continue reading if appropriate */ if (status == SANE_STATUS_GOOD) return status; mc_scan_finish(s); return status; } /* * void sane_cancel(SANE_Handle handle) * * Set the cancel flag to true. The next time the backend requests data * from the scanner the CAN message will be sent. */ void sane_cancel(SANE_Handle handle) { Magicolor_Scanner *s = (Magicolor_Scanner *) handle; s->canceling = SANE_TRUE; } /* * SANE_Status sane_set_io_mode() * * not supported - for asynchronous I/O */ SANE_Status sane_set_io_mode(SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking) { return SANE_STATUS_UNSUPPORTED; } /* * SANE_Status sane_get_select_fd() * * not supported - for asynchronous I/O */ SANE_Status sane_get_select_fd(SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ *fd) { return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/magicolor.conf.in000066400000000000000000000023451456256263500201260ustar00rootroot00000000000000### magicolor.conf ### ### here are some examples for how to configure the Magicolor backend ### Timeout settings: SNMP autodetection, Scan data read requests and other ### read requests. All values are given in milliseconds, ### i.e. 1000 is 1 second. # SNMP auto-detection waits 1.5 seconds snmp-timeout 1500 # wait 15 seconds for scan data (color scans take ~6 seconds to initialize, # so we need to wait longer than that) scan-data-timeout 15000 # Wait 5 seconds for all other data requests request-timeout 5000 ### Network: Format is "net IP_ADDRESS [USB_ID]" or "net autodiscovery" ### if USB_ID is left out, SNMP is used to detect the device type net autodiscovery #net 10.0.0.5 ### The following is a magicolor 1690mf with explicit IP-Address #net 10.0.0.5 0x2098 # net 192.168.0.1 ### USB: format is "usb" for automatic (libusb) discovery, based on USB IDs, ### or "usb to force the use of a particular ### device (the backend has some additional checks and will not use ### non-KONICA MINOLTA devices, though) usb ### For libusb support for unknown scanners use the following command ### usb ### e.g.: # usb 0x132b 0x2098 backends-1.3.0/backend/magicolor.h000066400000000000000000000122361456256263500170230ustar00rootroot00000000000000/* * magicolor.h - SANE library for Magicolor scanners. * * (C) 2010 Reinhold Kainhofer * * Based on the epson2 sane backend: * Based on Kazuhiro Sasayama previous * Work on epson.[ch] file from the SANE package. * Please see those files for original copyrights. * Copyright (C) 2006 Tower Technologies * Author: Alessandro Zummo * * 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, version 2. */ #ifndef magicolor_h #define magicolor_h #undef BACKEND_NAME #define BACKEND_NAME magicolor #define DEBUG_NOT_STATIC #include #ifdef HAVE_STDDEF_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef NEED_SYS_TYPES_H #include #endif #include #include "../include/sane/sane.h" #include "../include/sane/sanei_debug.h" #include "../include/sane/sanei_backend.h" /* Silence the compiler for unused arguments */ #define NOT_USED(x) ( (void)(x) ) #define MAGICOLOR_CONFIG_FILE "magicolor.conf" #define NUM_OF_HEX_ELEMENTS (16) /* number of hex numbers per line for data dump */ #define DEVICE_NAME_LEN (16) /* length of device name in extended status */ /* misc constants */ #define NET 0x04 #define CMD 0x03 /* status values */ #define STATUS_READY 0x00 /* scanner is ready */ #define STATUS_ADF_JAM 0x01 /* ADF paper jam */ #define STATUS_OPEN 0x02 /* scanner is open */ #define STATUS_NOT_READY 0x03 /* scanner is in use on another interface */ #define ADF_LOADED 0x01 /* ADF is loaded */ #define MAGICOLOR_CAP_DEFAULT 0 #define MAGICOLOR_LEVEL_1690mf 0 #define MAGICOLOR_LEVEL_DEFAULT MAGICOLOR_LEVEL_1690mf #define MAGICOLOR_LEVEL_NET MAGICOLOR_LEVEL_1690mf /* Structure holding the command set for a device */ struct MagicolorCmd { const char *level; unsigned char scanner_cmd; unsigned char start_scanning; unsigned char request_error; unsigned char stop_scanning; unsigned char request_scan_parameters; unsigned char set_scan_parameters; unsigned char request_status; unsigned char request_data; unsigned char unknown1; unsigned char unknown2; unsigned char net_wrapper_cmd; unsigned char net_welcome; unsigned char net_lock; unsigned char net_lock_ack; unsigned char net_unlock; }; /* Structure holding the device capabilities */ struct MagicolorCap { unsigned int id; const char *cmds; const char *model; const char *OID; SANE_Int out_ep, in_ep; /* USB bulk out/in endpoints */ SANE_Int optical_res; /* optical resolution */ SANE_Range dpi_range; /* max/min resolutions */ SANE_Int *res_list; /* list of resolutions */ SANE_Int res_list_size; /* number of entries in this list */ SANE_Int maxDepth; /* max. color depth */ SANE_Word *depth_list; /* list of color depths */ SANE_Range brightness; /* brightness range */ SANE_Range fbf_x_range; /* flattbed x range */ SANE_Range fbf_y_range; /* flattbed y range */ SANE_Bool ADF; /* ADF is installed */ SANE_Bool adf_duplex; /* does the ADF handle duplex scanning */ SANE_Range adf_x_range; /* autom. document feeder x range */ SANE_Range adf_y_range; /* autom. document feeder y range */ }; enum { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_MODE, OPT_BIT_DEPTH, OPT_BRIGHTNESS, OPT_RESOLUTION, OPT_PREVIEW, OPT_SOURCE, OPT_ADF_MODE, OPT_GEOMETRY_GROUP, OPT_TL_X, OPT_TL_Y, OPT_BR_X, OPT_BR_Y, NUM_OPTIONS }; typedef enum { /* hardware connection to the scanner */ SANE_MAGICOLOR_NODEV, /* default, no HW specified yet */ SANE_MAGICOLOR_USB, /* USB interface */ SANE_MAGICOLOR_NET /* network interface */ } Magicolor_Connection_Type; /* Structure holding the hardware description */ struct Magicolor_Device { struct Magicolor_Device *next; int missing; char *name; char *model; SANE_Device sane; SANE_Range *x_range; /* x range w/out extension */ SANE_Range *y_range; /* y range w/out extension */ Magicolor_Connection_Type connection; struct MagicolorCmd *cmd; struct MagicolorCap *cap; }; typedef struct Magicolor_Device Magicolor_Device; /* Structure holding an instance of a scanner (i.e. scanner has been opened) */ struct Magicolor_Scanner { struct Magicolor_Scanner *next; struct Magicolor_Device *hw; int fd; SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; SANE_Parameters params; SANE_Bool eof; SANE_Byte *buf, *end, *ptr; SANE_Bool canceling; SANE_Int left, top; SANE_Int width, height; /* image block data */ SANE_Int data_len; SANE_Int block_len; SANE_Int last_len; SANE_Int blocks; SANE_Int counter; /* store how many bytes of the current pixel line we have already * read in previous read attempts. Since each line will be padded * to multiples of 512 bytes, this is needed to know which bytes * to ignore */ SANE_Int bytes_read_in_line; SANE_Byte *line_buffer; /* How many bytes are scanned per line (multiple of 512 bytes */ SANE_Int scan_bytes_per_line; }; typedef struct Magicolor_Scanner Magicolor_Scanner; struct mode_param { int flags; int colors; int depth; }; enum { MODE_BINARY, MODE_GRAY, MODE_COLOR }; #endif backends-1.3.0/backend/matsushita.c000066400000000000000000002022221456256263500172200ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2002, 2004 Frank Zago (sane at zago dot net) Copyright (C) 2002 Other SANE contributors 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, see . 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. */ /* Matsushita/Panasonic KV-SS25, KV-SS50, KV-SS55, KV-SS50EX, KV-SS55EX, KV-SS850, KV-SS855 SCSI scanners. This backend may support more Panasonic scanners. */ /*--------------------------------------------------------------------------*/ #define BUILD 7 /* 2004-02-11 */ #define BACKEND_NAME matsushita #define MATSUSHITA_CONFIG_FILE "matsushita.conf" /*--------------------------------------------------------------------------*/ #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_scsi.h" #include "../include/sane/sanei_debug.h" #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_config.h" #include "../include/lassert.h" #include "matsushita.h" /*--------------------------------------------------------------------------*/ /* Lists of possible scan modes. */ static SANE_String_Const scan_mode_list_1[] = { BLACK_WHITE_STR, NULL }; static SANE_String_Const scan_mode_list_3[] = { BLACK_WHITE_STR, GRAY4_STR, GRAY8_STR, NULL }; /*--------------------------------------------------------------------------*/ /* Lists of supported resolutions (in DPI). * 200 DPI scanners are using resolutions_list_200 * 300 DPI scanners are using resolutions_list_300 * 400 DPI scanners are using resolutions_list_400 * * The resolutions_rounds_* lists provide the value with which round * up the X value given by the interface. */ #ifdef unused_yet static const SANE_Word resolutions_list_200[4] = { 3, 100, 150, 200 }; static const SANE_Word resolutions_rounds_200[4] = { 3, 0x100, 0x40, 0x20 }; #endif static const SANE_Word resolutions_list_300[5] = { 4, 150, 200, 240, 300 }; static const SANE_Word resolutions_rounds_300[5] = { 4, 0x100, 0x40, 0x20, 0x80 }; static const SANE_Word resolutions_list_400[8] = { 7, 100, 150, 200, 240, 300, 360, 400 }; static const SANE_Word resolutions_rounds_400[8] = { 7, 0x100, 0x100, 0x40, 0x20, 0x80, 0x100, 0x100 /* TO FIX */ }; /*--------------------------------------------------------------------------*/ /* Lists of supported halftone. They are only valid with * for the Black&White mode. */ static SANE_String_Const halftone_pattern_list[] = { SANE_I18N ("None"), SANE_I18N ("Bayer Dither 16"), SANE_I18N ("Bayer Dither 64"), SANE_I18N ("Halftone Dot 32"), SANE_I18N ("Halftone Dot 64"), SANE_I18N ("Error Diffusion"), NULL }; static const int halftone_pattern_val[] = { -1, 0x01, 0x00, 0x02, 0x03, 0x04 }; /*--------------------------------------------------------------------------*/ /* List of automatic threshold options */ static SANE_String_Const automatic_threshold_list[] = { SANE_I18N ("None"), SANE_I18N ("Mode 1"), SANE_I18N ("Mode 2"), SANE_I18N ("Mode 3"), NULL }; static const int automatic_threshold_val[] = { 0, 0x80, 0x81, 0x82 }; /*--------------------------------------------------------------------------*/ /* List of white level base. */ static SANE_String_Const white_level_list[] = { SANE_I18N ("From white stick"), SANE_I18N ("From paper"), SANE_I18N ("Automatic"), NULL }; static const int white_level_val[] = { 0x00, 0x80, 0x81 }; /*--------------------------------------------------------------------------*/ /* List of noise reduction options. */ static SANE_String_Const noise_reduction_list[] = { SANE_I18N ("None"), "1x1", "2x2", "3x3", "4x4", "5x5", NULL }; static const int noise_reduction_val[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 }; /*--------------------------------------------------------------------------*/ /* List of image emphasis options, 5 steps */ static SANE_String_Const image_emphasis_list_5[] = { SANE_I18N ("Smooth"), SANE_I18N ("None"), SANE_I18N ("Low"), SANE_I18N ("Medium"), /* default */ SANE_I18N ("High"), NULL }; static const int image_emphasis_val_5[] = { 0x80, 0x00, 0x01, 0x30, 0x50 }; /* List of image emphasis options, 3 steps */ static SANE_String_Const image_emphasis_list_3[] = { SANE_I18N ("Low"), SANE_I18N ("Medium"), /* default ? */ SANE_I18N ("High"), NULL }; static const int image_emphasis_val_3[] = { 0x01, 0x30, 0x50 }; /*--------------------------------------------------------------------------*/ /* List of gamma */ static SANE_String_Const gamma_list[] = { SANE_I18N ("Normal"), SANE_I18N ("CRT"), NULL }; static const int gamma_val[] = { 0x00, 0x01 }; /*--------------------------------------------------------------------------*/ /* Page feeder options */ static SANE_String_Const feeder_mode_list[] = { SANE_I18N ("One page"), SANE_I18N ("All pages"), NULL }; static const int feeder_mode_val[] = { 0x00, 0xff }; /*--------------------------------------------------------------------------*/ /* Paper size in millimeters. * Values from http://www.twics.com/~eds/paper/. */ static const struct paper_sizes paper_sizes[] = { {"2A0", 1189, 1682}, {"4A0", 1682, 2378}, {"A0", 841, 1189}, {"A1", 594, 841}, {"A2", 420, 594}, {"A3", 297, 420}, {"A4", 210, 297}, {"A5", 148, 210}, {"A6", 105, 148}, {"A7", 74, 105}, {"A8", 52, 74}, {"A9", 37, 52}, {"A10", 26, 37}, {"B0", 1000, 1414}, {"B1", 707, 1000}, {"B2", 500, 707}, {"B3", 353, 500}, {"B4", 250, 353}, {"B5", 176, 250}, {"B6", 125, 176}, {"B7", 88, 125}, {"B8", 62, 88}, {"B9", 44, 62}, {"B10", 31, 44}, {"C0", 917, 1297}, {"C1", 648, 917}, {"C2", 458, 648}, {"C3", 324, 458}, {"C4", 229, 324}, {"C5", 162, 229}, {"C6", 114, 162}, {"C7", 81, 114}, {"C8", 57, 81}, {"C9", 40, 57}, {"C10", 28, 40}, {"Legal", 8.5 * MM_PER_INCH, 14 * MM_PER_INCH}, {"Letter", 8.5 * MM_PER_INCH, 11 * MM_PER_INCH} }; /*--------------------------------------------------------------------------*/ /* Define the supported scanners and their characteristics. */ static const struct scanners_supported scanners[] = { /* Panasonic KV-SS25 */ { 0x06, "K.M.E. ", "KV-SS25A ", {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, /* x range 0 to 215.9 mm */ {SANE_FIX (0), SANE_FIX (17 * MM_PER_INCH), 0}, /* y range 0 to 431.8 mm */ {1, 255, 1}, /* brightness range */ {1, 255, 1}, /* contrast range */ scan_mode_list_3, resolutions_list_300, resolutions_rounds_300, image_emphasis_list_5, image_emphasis_val_5, MAT_CAP_DUPLEX | MAT_CAP_CONTRAST | MAT_CAP_AUTOMATIC_THRESHOLD | MAT_CAP_WHITE_LEVEL | MAT_CAP_GAMMA | MAT_CAP_NOISE_REDUCTION}, /* Panasonic KV-SS25D */ { 0x06, "K.M.E. ", "KV-SS25D ", /* TO FIX */ {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, /* x range 0 to 215.9 mm */ {SANE_FIX (0), SANE_FIX (17 * MM_PER_INCH), 0}, /* y range 0 to 431.8 mm */ {1, 255, 1}, /* brightness range */ {1, 255, 1}, /* contrast range */ scan_mode_list_3, resolutions_list_300, resolutions_rounds_300, image_emphasis_list_5, image_emphasis_val_5, MAT_CAP_DUPLEX | MAT_CAP_CONTRAST | MAT_CAP_AUTOMATIC_THRESHOLD | MAT_CAP_WHITE_LEVEL | MAT_CAP_GAMMA | MAT_CAP_NOISE_REDUCTION}, /* Panasonic KV-SS50 */ { 0x06, "K.M.E. ", "KV-SS50 ", /* TO FIX */ {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, /* x range 0 to 215.9 mm */ {SANE_FIX (0), SANE_FIX (14 * MM_PER_INCH), 0}, /* y range 0 to 355.6 mm */ {1, 5, 1}, /* brightness range, TO FIX */ {0, 0, 0}, /* contrast range */ scan_mode_list_1, resolutions_list_300, resolutions_rounds_300, /* TO FIX */ image_emphasis_list_3, image_emphasis_val_3, MAT_CAP_PAPER_DETECT | MAT_CAP_MIRROR_IMAGE}, /* Panasonic KV-SS55 */ { 0x06, "K.M.E. ", "KV-SS55 ", /* TO FIX */ {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, /* x range 0 to 215.9 mm */ {SANE_FIX (0), SANE_FIX (14 * MM_PER_INCH), 0}, /* y range 0 to 355.6 mm */ {1, 5, 1}, /* brightness range, TO FIX */ {1, 255, 1}, /* contrast range, TO FIX */ scan_mode_list_1, resolutions_list_300, resolutions_rounds_300, /* TO FIX */ image_emphasis_list_3, image_emphasis_val_3, MAT_CAP_DUPLEX | MAT_CAP_CONTRAST | MAT_CAP_PAPER_DETECT | MAT_CAP_MIRROR_IMAGE}, /* Panasonic KV-SS50EX */ { 0x06, "K.M.E. ", "KV-SS50EX ", /* TO FIX */ {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, /* x range 0 to 215.9 mm */ {SANE_FIX (0), SANE_FIX (17 * MM_PER_INCH), 0}, /* y range 0 to 355.6 mm */ {1, 255, 1}, /* brightness range */ {0, 0, 0}, /* contrast range */ scan_mode_list_3, resolutions_list_300, resolutions_rounds_300, /* TO FIX */ image_emphasis_list_5, image_emphasis_val_5, MAT_CAP_AUTOMATIC_THRESHOLD | MAT_CAP_WHITE_LEVEL | MAT_CAP_GAMMA | MAT_CAP_NOISE_REDUCTION | MAT_CAP_PAPER_DETECT | MAT_CAP_MIRROR_IMAGE}, /* Panasonic KV-SS55EX */ { 0x06, "K.M.E. ", "KV-SS55EX ", {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, /* x range 0 to 215.9 mm */ {SANE_FIX (0), SANE_FIX (17 * MM_PER_INCH), 0}, /* y range 0 to 431.8 mm */ {1, 255, 1}, /* brightness range */ {1, 255, 1}, /* contrast range */ scan_mode_list_3, resolutions_list_300, resolutions_rounds_300, image_emphasis_list_5, image_emphasis_val_5, MAT_CAP_DUPLEX | MAT_CAP_CONTRAST | MAT_CAP_AUTOMATIC_THRESHOLD | MAT_CAP_WHITE_LEVEL | MAT_CAP_GAMMA | MAT_CAP_NOISE_REDUCTION}, /* Panasonic KV-SS850 */ { 0x06, "K.M.E. ", "KV-SS850 ", /* TO FIX */ {SANE_FIX (0), SANE_FIX (11.5 * MM_PER_INCH), 0}, /* x range 0 to 215.9 mm */ {SANE_FIX (0), SANE_FIX (17 * MM_PER_INCH), 0}, /* y range 0 to 355.6 mm */ {1, 255, 1}, /* brightness range */ {0, 0, 0}, /* contrast range */ scan_mode_list_3, resolutions_list_300, resolutions_rounds_300, /* TO FIX */ image_emphasis_list_5, image_emphasis_val_5, MAT_CAP_AUTOMATIC_THRESHOLD | MAT_CAP_WHITE_LEVEL | MAT_CAP_GAMMA | MAT_CAP_NOISE_REDUCTION | MAT_CAP_PAPER_DETECT | MAT_CAP_DETECT_DOUBLE_FEED | MAT_CAP_MANUAL_FEED}, /* Panasonic KV-SS855 */ { 0x06, "K.M.E. ", "KV-SS855 ", /* TO FIX */ {SANE_FIX (0), SANE_FIX (11.5 * MM_PER_INCH), 0}, /* x range 0 to 215.9 mm */ {SANE_FIX (0), SANE_FIX (17 * MM_PER_INCH), 0}, /* y range 0 to 355.6 mm */ {1, 255, 1}, /* brightness range */ {1, 255, 1}, /* contrast range, TO FIX */ scan_mode_list_3, resolutions_list_400, resolutions_rounds_400, /* TO FIX */ image_emphasis_list_5, image_emphasis_val_5, MAT_CAP_DUPLEX | MAT_CAP_CONTRAST | MAT_CAP_AUTOMATIC_THRESHOLD | MAT_CAP_WHITE_LEVEL | MAT_CAP_GAMMA | MAT_CAP_NOISE_REDUCTION | MAT_CAP_PAPER_DETECT | MAT_CAP_DETECT_DOUBLE_FEED | MAT_CAP_MANUAL_FEED}, /* Panasonic KV-S2065L */ { 0x06, "K.M.E. ", "KV-S2065L ", {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, /* x range 0 to 215.9 mm */ {SANE_FIX (0), SANE_FIX (17 * MM_PER_INCH), 0}, /* y range 0 to 431.8 mm */ {1, 255, 1}, /* brightness range */ {1, 255, 1}, /* contrast range */ scan_mode_list_3, resolutions_list_300, resolutions_rounds_300, image_emphasis_list_5, image_emphasis_val_5, MAT_CAP_DUPLEX | MAT_CAP_CONTRAST | MAT_CAP_AUTOMATIC_THRESHOLD | MAT_CAP_WHITE_LEVEL | MAT_CAP_GAMMA | MAT_CAP_NOISE_REDUCTION}, /* Panasonic KV-S2025C */ { 0x06, "K.M.E. ", "KV-S2025C ", {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, /* x range 0 to 215.9 mm */ {SANE_FIX (0), SANE_FIX (17 * MM_PER_INCH), 0}, /* y range 0 to 431.8 mm */ {1, 255, 1}, /* brightness range */ {1, 255, 1}, /* contrast range */ scan_mode_list_3, resolutions_list_300, resolutions_rounds_300, image_emphasis_list_5, image_emphasis_val_5, MAT_CAP_DUPLEX | MAT_CAP_CONTRAST | MAT_CAP_AUTOMATIC_THRESHOLD | MAT_CAP_WHITE_LEVEL | MAT_CAP_GAMMA | MAT_CAP_NOISE_REDUCTION}, /* Panasonic KV-S2045C */ { 0x06, "K.M.E. ", "KV-S2045C ", {SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0}, /* x range 0 to 215.9 mm */ {SANE_FIX (0), SANE_FIX (17 * MM_PER_INCH), 0}, /* y range 0 to 431.8 mm */ {1, 255, 1}, /* brightness range */ {1, 255, 1}, /* contrast range */ scan_mode_list_3, resolutions_list_300, resolutions_rounds_300, image_emphasis_list_5, image_emphasis_val_5, MAT_CAP_DUPLEX | MAT_CAP_CONTRAST | MAT_CAP_AUTOMATIC_THRESHOLD | MAT_CAP_WHITE_LEVEL | MAT_CAP_GAMMA | MAT_CAP_NOISE_REDUCTION} }; /*--------------------------------------------------------------------------*/ /* List of scanner attached. */ static Matsushita_Scanner *first_dev = NULL; static int num_devices = 0; static const SANE_Device **devlist = NULL; /* Local functions. */ /* Display a buffer in the log. */ static void hexdump (int level, const 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.3d:", i); ptr += 4; } sprintf (ptr, " %2.2x", *p); ptr += 3; } *ptr = '\0'; DBG (level, "%s\n", line); } /* Returns the length of the longest string, including the terminating * character. */ static size_t max_string_size (SANE_String_Const strings[]) { size_t size, max_size = 0; int i; for (i = 0; strings[i]; ++i) { size = strlen (strings[i]) + 1; if (size > max_size) { max_size = size; } } return max_size; } /* After the windows has been set, issue that command to get the * document size. */ static SANE_Status matsushita_read_document_size (Matsushita_Scanner * dev) { CDB cdb; SANE_Status status; size_t size; DBG (DBG_proc, "matsushita_read_document_size: enter\n"); size = 0x10; MKSCSI_READ_10 (cdb, 0x80, 0, size); status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, dev->buffer, &size); if (status != SANE_STATUS_GOOD || size != 0x10) { DBG (DBG_error, "matsushita_read_document_size: cannot read document size\n"); return (SANE_STATUS_IO_ERROR); } hexdump (DBG_info2, "document size", dev->buffer, 16); /* Check that X and Y are the same values the backend computed. */ assert (dev->params.lines == B32TOI (&dev->buffer[4])); assert (dev->params.pixels_per_line == B32TOI (&dev->buffer[0])); DBG (DBG_proc, "matsushita_read_document_size: exit, %ld bytes read\n", (long)size); return (SANE_STATUS_GOOD); } /* Initialize a scanner entry. Return an allocated scanner with some * preset values. */ static Matsushita_Scanner * matsushita_init (void) { Matsushita_Scanner *dev; DBG (DBG_proc, "matsushita_init: enter\n"); /* Allocate a new scanner entry. */ dev = malloc (sizeof (Matsushita_Scanner)); if (dev == NULL) { return NULL; } memset (dev, 0, sizeof (Matsushita_Scanner)); /* Allocate the buffer used to transfer the SCSI data. */ dev->buffer_size = 64 * 1024; dev->buffer = malloc (dev->buffer_size); if (dev->buffer == NULL) { free (dev); return NULL; } /* Allocate a buffer to store the temporary image. */ dev->image_size = 64 * 1024; /* enough for 1 line at max res */ dev->image = malloc (dev->image_size); if (dev->image == NULL) { free (dev->buffer); free (dev); return NULL; } dev->sfd = -1; DBG (DBG_proc, "matsushita_init: exit\n"); return (dev); } /* Closes an open scanner. */ static void matsushita_close (Matsushita_Scanner * dev) { DBG (DBG_proc, "matsushita_close: enter\n"); if (dev->sfd != -1) { sanei_scsi_close (dev->sfd); dev->sfd = -1; } DBG (DBG_proc, "matsushita_close: exit\n"); } /* Frees the memory used by a scanner. */ static void matsushita_free (Matsushita_Scanner * dev) { int i; DBG (DBG_proc, "matsushita_free: enter\n"); if (dev == NULL) return; matsushita_close (dev); if (dev->devicename) { free (dev->devicename); } if (dev->buffer) { free (dev->buffer); } if (dev->image) { free (dev->image); } for (i = 1; i < OPT_NUM_OPTIONS; i++) { if (dev->opt[i].type == SANE_TYPE_STRING && dev->val[i].s) { free (dev->val[i].s); } } free (dev->paper_sizes_list); free (dev->paper_sizes_val); free (dev); DBG (DBG_proc, "matsushita_free: exit\n"); } /* Inquiry a device and returns TRUE if is supported. */ static int matsushita_identify_scanner (Matsushita_Scanner * dev) { CDB cdb; SANE_Status status; size_t size; int i; DBG (DBG_proc, "matsushita_identify_scanner: enter\n"); size = 5; MKSCSI_INQUIRY (cdb, size); status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, dev->buffer, &size); if (status) { DBG (DBG_error, "matsushita_identify_scanner: inquiry failed with status %s\n", sane_strstatus (status)); return (SANE_FALSE); } size = dev->buffer[4] + 5; /* total length of the inquiry data */ if (size < 36) { DBG (DBG_error, "matsushita_identify_scanner: not enough data to identify device\n"); return (SANE_FALSE); } MKSCSI_INQUIRY (cdb, size); status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, dev->buffer, &size); if (status) { DBG (DBG_error, "matsushita_identify_scanner: inquiry failed with status %s\n", sane_strstatus (status)); return (SANE_FALSE); } hexdump (DBG_info2, "inquiry", dev->buffer, size); dev->scsi_type = dev->buffer[0] & 0x1f; memcpy (dev->scsi_vendor, dev->buffer + 0x08, 0x08); dev->scsi_vendor[0x08] = 0; memcpy (dev->scsi_product, dev->buffer + 0x10, 0x010); dev->scsi_product[0x10] = 0; memcpy (dev->scsi_version, dev->buffer + 0x20, 0x04); dev->scsi_version[0x04] = 0; DBG (DBG_info, "device is \"%s\" \"%s\" \"%s\"\n", dev->scsi_vendor, dev->scsi_product, dev->scsi_version); /* Lookup through the supported scanners table to find if this * backend supports that one. */ for (i = 0; i < NELEMS (scanners); i++) { if (dev->scsi_type == scanners[i].scsi_type && strcmp (dev->scsi_vendor, scanners[i].scsi_vendor) == 0 && strcmp (dev->scsi_product, scanners[i].scsi_product) == 0) { DBG (DBG_error, "matsushita_identify_scanner: scanner supported\n"); dev->scnum = i; return (SANE_TRUE); } } DBG (DBG_proc, "matsushita_identify_scanner: exit, device not supported\n"); return (SANE_FALSE); } /* The interface can show different paper sizes. Show only the sizes * available for that scanner. */ static int matsushita_build_paper_sizes (Matsushita_Scanner * dev) { SANE_String_Const *psl; /* string list */ int *psv; /* value list */ int num; int i; DBG (DBG_proc, "matsushita_build_paper_sizes: enter\n"); psl = malloc ((sizeof (SANE_String_Const) + 1) * NELEMS (paper_sizes)); if (psl == NULL) { DBG (DBG_error, "ERROR: not enough memory\n"); return SANE_STATUS_NO_MEM; } psv = malloc ((sizeof (int) + 1) * NELEMS (paper_sizes)); if (psv == NULL) { DBG (DBG_error, "ERROR: not enough memory\n"); free (psl); return SANE_STATUS_NO_MEM; } for (i = 0, num = 0; i < NELEMS (paper_sizes); i++) { if (SANE_UNFIX (scanners[dev->scnum].x_range.max) >= paper_sizes[i].width && SANE_UNFIX (scanners[dev->scnum].y_range.max) >= paper_sizes[i].length) { /* This paper size fits into the scanner. */ psl[num] = paper_sizes[i].name; psv[num] = i; num++; } } psl[num] = NULL; /* terminate the list */ dev->paper_sizes_list = psl; dev->paper_sizes_val = psv; DBG (DBG_proc, "matsushita_build_paper_sizes: exit (%d)\n", num); return SANE_STATUS_GOOD; } /* Lookup a string list from one array and return its index. */ static int get_string_list_index (SANE_String_Const list[], SANE_String_Const name) { int index; index = 0; while (list[index] != NULL) { if (strcmp (list[index], name) == 0) { return (index); } index++; } DBG (DBG_error, "name %s not found in list\n", name); assert (0 == 1); /* bug in backend, core dump */ return (-1); } /* Lookup an int list from one array and return its index. */ static int get_int_list_index (const SANE_Word list[], const SANE_Word value) { int index; int size; /* number of elements */ index = 1; size = list[0]; while (index <= size) { if (list[index] == value) { return (index); } index++; } DBG (DBG_error, "word %d not found in list\n", value); assert (0 == 1); /* bug in backend, core dump */ return (-1); } /* SCSI sense handler. Callback for SANE. */ static SANE_Status matsushita_sense_handler (int scsi_fd, unsigned char *result, void __sane_unused__ *arg) { int asc, ascq, sensekey; int len; DBG (DBG_proc, "matsushita_sense_handler (scsi_fd = %d)\n", scsi_fd); sensekey = get_RS_sense_key (result); len = 7 + get_RS_additional_length (result); hexdump (DBG_info2, "sense", result, len); if (get_RS_error_code (result) != 0x70) { DBG (DBG_error, "matsushita_sense_handler: invalid sense key error code (%d)\n", get_RS_error_code (result)); return SANE_STATUS_IO_ERROR; } if (get_RS_ILI (result) != 0) { DBG (DBG_sense, "matsushita_sense_handler: short read\n"); } if (len < 14) { DBG (DBG_error, "matsushita_sense_handler: sense too short, no ASC/ASCQ\n"); return SANE_STATUS_IO_ERROR; } asc = get_RS_ASC (result); ascq = get_RS_ASCQ (result); DBG (DBG_sense, "matsushita_sense_handler: sense=%d, ASC/ASCQ=%02x%02x\n", sensekey, asc, ascq); switch (sensekey) { case 0x00: /* no sense */ if (get_RS_EOM (result) && asc == 0x00 && ascq == 0x00) { DBG (DBG_sense, "matsushita_sense_handler: EOF\n"); return SANE_STATUS_EOF; } return SANE_STATUS_GOOD; break; case 0x02: /* not ready */ if (asc == 0x04 && ascq == 0x81) { /* Jam door open. */ return SANE_STATUS_COVER_OPEN; } break; case 0x03: /* medium error */ if (asc == 0x3a) { /* No paper in the feeder. */ return SANE_STATUS_NO_DOCS; } if (asc == 0x80) { /* Probably a paper jam. ascq might give more info. */ return SANE_STATUS_JAMMED; } break; case 0x05: if (asc == 0x20 || asc == 0x24 || asc == 0x26) { /* Invalid command, invalid field in CDB or invalid field in data. * The backend has prepared some wrong combination of options. * Shot the backend maintainer. */ return SANE_STATUS_IO_ERROR; } else if (asc == 0x2c && ascq == 0x80) { /* The scanner does have enough memory to scan the whole * area. For instance the KV-SS25 has only 4MB of memory, * which is not enough to scan a A4 page at 300dpi in gray * 8 bits. */ return SANE_STATUS_NO_MEM; } break; case 0x06: if (asc == 0x29) { /* Reset occurred. May be the backend should retry the * command. */ return SANE_STATUS_GOOD; } break; } DBG (DBG_sense, "matsushita_sense_handler: unknown error condition. Please report it to the backend maintainer\n"); return SANE_STATUS_IO_ERROR; } /* Check that a new page is available by issuing an empty read. The * sense handler might return SANE_STATUS_NO_DOCS which indicates that * the feeder is now empty. */ static SANE_Status matsushita_check_next_page (Matsushita_Scanner * dev) { CDB cdb; SANE_Status status; DBG (DBG_proc, "matsushita_check_next_page: enter\n"); MKSCSI_READ_10 (cdb, 0, 0, 0); cdb.data[4] = dev->page_num; /* May be cdb.data[3] too? */ cdb.data[5] = dev->page_side; status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, NULL, NULL); DBG (DBG_proc, "matsushita_check_next_page: exit with status %d\n", status); return (status); } /* Attach a scanner to this backend. */ static SANE_Status attach_scanner (const char *devicename, Matsushita_Scanner ** devp) { Matsushita_Scanner *dev; int sfd; DBG (DBG_sane_proc, "attach_scanner: %s\n", devicename); if (devp) *devp = NULL; /* Check if we know this device name. */ for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->sane.name, devicename) == 0) { if (devp) { *devp = dev; } DBG (DBG_info, "device is already known\n"); return SANE_STATUS_GOOD; } } /* Allocate a new scanner entry. */ dev = matsushita_init (); if (dev == NULL) { DBG (DBG_error, "ERROR: not enough memory\n"); return SANE_STATUS_NO_MEM; } DBG (DBG_info, "attach_scanner: opening %s\n", devicename); if (sanei_scsi_open (devicename, &sfd, matsushita_sense_handler, dev) != 0) { DBG (DBG_error, "ERROR: attach_scanner: open failed\n"); matsushita_free (dev); return SANE_STATUS_INVAL; } /* Fill some scanner specific values. */ dev->devicename = strdup (devicename); dev->sfd = sfd; /* Now, check that it is a scanner we support. */ if (matsushita_identify_scanner (dev) == SANE_FALSE) { DBG (DBG_error, "ERROR: attach_scanner: scanner-identification failed\n"); matsushita_free (dev); return SANE_STATUS_INVAL; } matsushita_close (dev); /* Set the default options for that scanner. */ dev->sane.name = dev->devicename; dev->sane.vendor = "Panasonic"; dev->sane.model = dev->scsi_product; dev->sane.type = SANE_I18N ("sheetfed scanner"); /* Link the scanner with the others. */ dev->next = first_dev; first_dev = dev; if (devp) { *devp = dev; } num_devices++; DBG (DBG_proc, "attach_scanner: exit\n"); return SANE_STATUS_GOOD; } static SANE_Status attach_one (const char *dev) { attach_scanner (dev, NULL); return SANE_STATUS_GOOD; } /* Reset the options for that scanner. */ static void matsushita_init_options (Matsushita_Scanner * dev) { int i; /* Pre-initialize the options. */ memset (dev->opt, 0, sizeof (dev->opt)); memset (dev->val, 0, sizeof (dev->val)); for (i = 0; i < OPT_NUM_OPTIONS; ++i) { dev->opt[i].size = sizeof (SANE_Word); dev->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } /* Number of options. */ dev->opt[OPT_NUM_OPTS].name = ""; dev->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; dev->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; dev->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; dev->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; dev->val[OPT_NUM_OPTS].w = OPT_NUM_OPTIONS; /* Mode group */ dev->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode"); dev->opt[OPT_MODE_GROUP].desc = ""; /* not valid for a group */ dev->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; dev->opt[OPT_MODE_GROUP].cap = 0; dev->opt[OPT_MODE_GROUP].size = 0; dev->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* Scanner supported modes */ dev->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; dev->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE; dev->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE; dev->opt[OPT_MODE].type = SANE_TYPE_STRING; dev->opt[OPT_MODE].size = max_string_size (scanners[dev->scnum].scan_mode_list); dev->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->opt[OPT_MODE].constraint.string_list = scanners[dev->scnum].scan_mode_list; dev->val[OPT_MODE].s = (SANE_Char *) strdup (""); /* will be set later */ /* X and Y resolution */ dev->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; dev->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; dev->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; dev->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; dev->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; dev->opt[OPT_RESOLUTION].constraint.word_list = scanners[dev->scnum].resolutions_list; dev->val[OPT_RESOLUTION].w = resolutions_list_300[1]; /* Duplex */ dev->opt[OPT_DUPLEX].name = SANE_NAME_DUPLEX; dev->opt[OPT_DUPLEX].title = SANE_TITLE_DUPLEX; dev->opt[OPT_DUPLEX].desc = SANE_DESC_DUPLEX; dev->opt[OPT_DUPLEX].type = SANE_TYPE_BOOL; dev->opt[OPT_DUPLEX].unit = SANE_UNIT_NONE; dev->val[OPT_DUPLEX].w = SANE_FALSE; if ((scanners[dev->scnum].cap & MAT_CAP_DUPLEX) == 0) dev->opt[OPT_DUPLEX].cap |= SANE_CAP_INACTIVE; /* Feeder mode */ dev->opt[OPT_FEEDER_MODE].name = "feeder-mode"; dev->opt[OPT_FEEDER_MODE].title = SANE_I18N ("Feeder mode"); dev->opt[OPT_FEEDER_MODE].desc = SANE_I18N ("Sets the feeding mode"); dev->opt[OPT_FEEDER_MODE].type = SANE_TYPE_STRING; dev->opt[OPT_FEEDER_MODE].size = max_string_size (feeder_mode_list); dev->opt[OPT_FEEDER_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->opt[OPT_FEEDER_MODE].constraint.string_list = feeder_mode_list; dev->val[OPT_FEEDER_MODE].s = strdup (feeder_mode_list[0]); /* Geometry group */ dev->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry"); dev->opt[OPT_GEOMETRY_GROUP].desc = ""; /* not valid for a group */ dev->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; dev->opt[OPT_GEOMETRY_GROUP].cap = 0; dev->opt[OPT_GEOMETRY_GROUP].size = 0; dev->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* Paper sizes list. */ dev->opt[OPT_PAPER_SIZE].name = SANE_NAME_PAPER_SIZE; dev->opt[OPT_PAPER_SIZE].title = SANE_TITLE_PAPER_SIZE; dev->opt[OPT_PAPER_SIZE].desc = SANE_DESC_PAPER_SIZE; dev->opt[OPT_PAPER_SIZE].type = SANE_TYPE_STRING; dev->opt[OPT_PAPER_SIZE].size = max_string_size (dev->paper_sizes_list); dev->opt[OPT_PAPER_SIZE].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->opt[OPT_PAPER_SIZE].constraint.string_list = dev->paper_sizes_list; dev->val[OPT_PAPER_SIZE].s = strdup (""); /* will do it later */ /* Upper left X */ dev->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; dev->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; dev->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; dev->opt[OPT_TL_X].type = SANE_TYPE_FIXED; dev->opt[OPT_TL_X].unit = SANE_UNIT_MM; dev->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_TL_X].constraint.range = &(scanners[dev->scnum].x_range); /* Upper left Y */ dev->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; dev->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; dev->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; dev->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; dev->opt[OPT_TL_Y].unit = SANE_UNIT_MM; dev->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_TL_Y].constraint.range = &(scanners[dev->scnum].y_range); /* Bottom-right x */ dev->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; dev->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; dev->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; dev->opt[OPT_BR_X].type = SANE_TYPE_FIXED; dev->opt[OPT_BR_X].unit = SANE_UNIT_MM; dev->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_BR_X].constraint.range = &(scanners[dev->scnum].x_range); /* Bottom-right y */ dev->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; dev->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; dev->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; dev->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; dev->opt[OPT_BR_Y].unit = SANE_UNIT_MM; dev->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_BR_Y].constraint.range = &(scanners[dev->scnum].y_range); /* Enhancement group */ dev->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement"); dev->opt[OPT_ENHANCEMENT_GROUP].desc = ""; /* not valid for a group */ dev->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; dev->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED; dev->opt[OPT_ENHANCEMENT_GROUP].size = 0; dev->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* Brightness */ dev->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; dev->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; dev->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; dev->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT; dev->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE; dev->opt[OPT_BRIGHTNESS].size = sizeof (SANE_Int); dev->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_BRIGHTNESS].constraint.range = &(scanners[dev->scnum].brightness_range); dev->val[OPT_BRIGHTNESS].w = 128; /* Contrast */ dev->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST; dev->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; dev->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST; dev->opt[OPT_CONTRAST].type = SANE_TYPE_INT; dev->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE; dev->opt[OPT_CONTRAST].size = sizeof (SANE_Int); dev->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; dev->opt[OPT_CONTRAST].constraint.range = &(scanners[dev->scnum].contrast_range); dev->val[OPT_CONTRAST].w = 128; if ((scanners[dev->scnum].cap & MAT_CAP_CONTRAST) == 0) dev->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; /* Automatic threshold */ dev->opt[OPT_AUTOMATIC_THRESHOLD].name = "automatic-threshold"; dev->opt[OPT_AUTOMATIC_THRESHOLD].title = SANE_I18N ("Automatic threshold"); dev->opt[OPT_AUTOMATIC_THRESHOLD].desc = SANE_I18N ("Automatically sets brightness, contrast, white level, gamma, noise reduction and image emphasis"); dev->opt[OPT_AUTOMATIC_THRESHOLD].type = SANE_TYPE_STRING; dev->opt[OPT_AUTOMATIC_THRESHOLD].size = max_string_size (automatic_threshold_list); dev->opt[OPT_AUTOMATIC_THRESHOLD].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->opt[OPT_AUTOMATIC_THRESHOLD].constraint.string_list = automatic_threshold_list; dev->val[OPT_AUTOMATIC_THRESHOLD].s = strdup (automatic_threshold_list[0]); if ((scanners[dev->scnum].cap & MAT_CAP_AUTOMATIC_THRESHOLD) == 0) dev->opt[OPT_AUTOMATIC_THRESHOLD].cap |= SANE_CAP_INACTIVE; /* Halftone pattern */ dev->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN; dev->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN; dev->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN; dev->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING; dev->opt[OPT_HALFTONE_PATTERN].size = max_string_size (halftone_pattern_list); dev->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->opt[OPT_HALFTONE_PATTERN].constraint.string_list = halftone_pattern_list; dev->val[OPT_HALFTONE_PATTERN].s = strdup (halftone_pattern_list[0]); /* Automatic separation */ dev->opt[OPT_AUTOMATIC_SEPARATION].name = SANE_NAME_AUTOSEP; dev->opt[OPT_AUTOMATIC_SEPARATION].title = SANE_TITLE_AUTOSEP; dev->opt[OPT_AUTOMATIC_SEPARATION].desc = SANE_DESC_AUTOSEP; dev->opt[OPT_AUTOMATIC_SEPARATION].type = SANE_TYPE_BOOL; dev->opt[OPT_AUTOMATIC_SEPARATION].unit = SANE_UNIT_NONE; dev->val[OPT_AUTOMATIC_SEPARATION].w = SANE_FALSE; /* White level base */ dev->opt[OPT_WHITE_LEVEL].name = SANE_NAME_WHITE_LEVEL; dev->opt[OPT_WHITE_LEVEL].title = SANE_TITLE_WHITE_LEVEL; dev->opt[OPT_WHITE_LEVEL].desc = SANE_DESC_WHITE_LEVEL; dev->opt[OPT_WHITE_LEVEL].type = SANE_TYPE_STRING; dev->opt[OPT_WHITE_LEVEL].size = max_string_size (white_level_list); dev->opt[OPT_WHITE_LEVEL].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->opt[OPT_WHITE_LEVEL].constraint.string_list = white_level_list; dev->val[OPT_WHITE_LEVEL].s = strdup (white_level_list[0]); if ((scanners[dev->scnum].cap & MAT_CAP_WHITE_LEVEL) == 0) dev->opt[OPT_WHITE_LEVEL].cap |= SANE_CAP_INACTIVE; /* Noise reduction */ dev->opt[OPT_NOISE_REDUCTION].name = "noise-reduction"; dev->opt[OPT_NOISE_REDUCTION].title = SANE_I18N ("Noise reduction"); dev->opt[OPT_NOISE_REDUCTION].desc = SANE_I18N ("Reduce the isolated dot noise"); dev->opt[OPT_NOISE_REDUCTION].type = SANE_TYPE_STRING; dev->opt[OPT_NOISE_REDUCTION].size = max_string_size (noise_reduction_list); dev->opt[OPT_NOISE_REDUCTION].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->opt[OPT_NOISE_REDUCTION].constraint.string_list = noise_reduction_list; dev->val[OPT_NOISE_REDUCTION].s = strdup (noise_reduction_list[0]); if ((scanners[dev->scnum].cap & MAT_CAP_NOISE_REDUCTION) == 0) dev->opt[OPT_NOISE_REDUCTION].cap |= SANE_CAP_INACTIVE; /* Image emphasis */ dev->opt[OPT_IMAGE_EMPHASIS].name = "image-emphasis"; dev->opt[OPT_IMAGE_EMPHASIS].title = SANE_I18N ("Image emphasis"); dev->opt[OPT_IMAGE_EMPHASIS].desc = SANE_I18N ("Sets the image emphasis"); dev->opt[OPT_IMAGE_EMPHASIS].type = SANE_TYPE_STRING; dev->opt[OPT_IMAGE_EMPHASIS].size = max_string_size (scanners[dev->scnum].image_emphasis_list); dev->opt[OPT_IMAGE_EMPHASIS].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->opt[OPT_IMAGE_EMPHASIS].constraint.string_list = scanners[dev->scnum].image_emphasis_list; dev->val[OPT_IMAGE_EMPHASIS].s = strdup (SANE_I18N ("Medium")); /* Gamma */ dev->opt[OPT_GAMMA].name = "gamma"; dev->opt[OPT_GAMMA].title = SANE_I18N ("Gamma"); dev->opt[OPT_GAMMA].desc = SANE_I18N ("Gamma"); dev->opt[OPT_GAMMA].type = SANE_TYPE_STRING; dev->opt[OPT_GAMMA].size = max_string_size (gamma_list); dev->opt[OPT_GAMMA].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->opt[OPT_GAMMA].constraint.string_list = gamma_list; dev->val[OPT_GAMMA].s = strdup (gamma_list[0]); /* Lastly, set the default scan mode. This might change some * values previously set here. */ sane_control_option (dev, OPT_PAPER_SIZE, SANE_ACTION_SET_VALUE, (SANE_String_Const *) dev->paper_sizes_list[0], NULL); sane_control_option (dev, OPT_MODE, SANE_ACTION_SET_VALUE, (SANE_String_Const *) scanners[dev->scnum]. scan_mode_list[0], NULL); } /* Wait until the scanner is ready. * * The only reason I know the scanner is not ready is because it is * moving the CCD. */ static SANE_Status matsushita_wait_scanner (Matsushita_Scanner * dev) { SANE_Status status; int timeout; CDB cdb; DBG (DBG_proc, "matsushita_wait_scanner: enter\n"); MKSCSI_TEST_UNIT_READY (cdb); /* Set the timeout to 60 seconds. */ timeout = 60; while (timeout > 0) { /* test unit ready */ status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, NULL, NULL); if (status == SANE_STATUS_GOOD) { return SANE_STATUS_GOOD; } sleep (1); }; DBG (DBG_proc, "matsushita_wait_scanner: scanner not ready\n"); return (SANE_STATUS_IO_ERROR); } /* Reset a window. This is used to re-initialize the scanner. */ static SANE_Status matsushita_reset_window (Matsushita_Scanner * dev) { CDB cdb; SANE_Status status; DBG (DBG_proc, "matsushita_reset_window: enter\n"); MKSCSI_SET_WINDOW (cdb, 0); status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, NULL, NULL); DBG (DBG_proc, "matsushita_reset_window: exit, status=%d\n", status); return status; } /* Set a window. */ static SANE_Status matsushita_set_window (Matsushita_Scanner * dev, int side) { size_t size; CDB cdb; unsigned char window[72]; SANE_Status status; int i; DBG (DBG_proc, "matsushita_set_window: enter\n"); size = sizeof (window); MKSCSI_SET_WINDOW (cdb, size); memset (window, 0, size); /* size of the windows descriptor block */ window[7] = sizeof (window) - 8; /* Page side */ window[8] = side; /* X and Y resolution */ Ito16 (dev->resolution, &window[10]); Ito16 (dev->resolution, &window[12]); /* Upper Left (X,Y) */ Ito32 (dev->x_tl, &window[14]); Ito32 (dev->y_tl, &window[18]); /* Width and length */ Ito32 (dev->width, &window[22]); Ito32 (dev->length, &window[26]); Ito32 (dev->width, &window[56]); /* again, verso? */ Ito32 (dev->length, &window[60]); /* again, verso? */ /* Brightness */ window[30] = 255 - dev->val[OPT_BRIGHTNESS].w; window[31] = window[30]; /* same as brightness. */ /* Contrast */ window[32] = dev->val[OPT_CONTRAST].w; /* Image Composition */ switch (dev->scan_mode) { case MATSUSHITA_BW: window[33] = 0x00; break; case MATSUSHITA_HALFTONE: window[33] = 0x01; break; case MATSUSHITA_GRAYSCALE: window[33] = 0x02; break; } /* Depth */ window[34] = dev->depth; /* Halftone pattern. */ if (dev->scan_mode == MATSUSHITA_HALFTONE) { i = get_string_list_index (halftone_pattern_list, dev->val[OPT_HALFTONE_PATTERN].s); window[36] = halftone_pattern_val[i]; } /* Gamma */ if (dev->scan_mode == MATSUSHITA_GRAYSCALE) { i = get_string_list_index (gamma_list, dev->val[OPT_GAMMA].s); window[52] = gamma_val[i]; } /* Feeder mode */ i = get_string_list_index (feeder_mode_list, dev->val[OPT_FEEDER_MODE].s); window[65] = feeder_mode_val[i]; /* Image emphasis */ i = get_string_list_index (scanners[dev->scnum].image_emphasis_list, dev->val[OPT_IMAGE_EMPHASIS].s); window[51] = scanners[dev->scnum].image_emphasis_val[i]; /* White level */ i = get_string_list_index (white_level_list, dev->val[OPT_WHITE_LEVEL].s); window[68] = white_level_val[i]; if (dev->scan_mode == MATSUSHITA_BW || dev->scan_mode == MATSUSHITA_HALFTONE) { /* Noise reduction */ i = get_string_list_index (noise_reduction_list, dev->val[OPT_NOISE_REDUCTION].s); window[69] = noise_reduction_val[i]; /* Automatic separation */ if (dev->val[OPT_AUTOMATIC_SEPARATION].w) { window[67] = 0x80; } /* Automatic threshold. Must be last because it may override * some previous options. */ i = get_string_list_index (automatic_threshold_list, dev->val[OPT_AUTOMATIC_THRESHOLD].s); window[66] = automatic_threshold_val[i]; if (automatic_threshold_val[i] != 0) { /* Automatic threshold is enabled. */ window[30] = 0; /* brightness. */ window[31] = 0; /* same as brightness. */ window[32] = 0; /* contrast */ window[33] = 0; /* B&W mode */ window[36] = 0; /* Halftone pattern. */ window[51] = 0; /* Image emphasis */ window[67] = 0; /* Automatic separation */ window[68] = 0; /* White level */ window[69] = 0; /* Noise reduction */ } } hexdump (DBG_info2, "windows", window, 72); status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, window, sizeof (window), NULL, NULL); DBG (DBG_proc, "matsushita_set_window: exit, status=%d\n", status); return status; } /* Read the image from the scanner and fill the temporary buffer with it. */ static SANE_Status matsushita_fill_image (Matsushita_Scanner * dev) { SANE_Status status; size_t size; CDB cdb; DBG (DBG_proc, "matsushita_fill_image: enter\n"); assert (dev->image_begin == dev->image_end); assert (dev->real_bytes_left > 0); dev->image_begin = 0; dev->image_end = 0; while (dev->real_bytes_left) { /* * Try to read the maximum number of bytes. * * The windows driver reads no more than 0x8000 byte. * * This backend operates differently than the windows * driver. The windows TWAIN driver always read 2 more bytes * at the end, so it gets a CHECK CONDITION with a short read * sense. Since the linux scsi layer seem to be buggy * regarding the resid, always read exactly the number of * remaining bytes. */ size = dev->real_bytes_left; if (size > dev->image_size - dev->image_end) size = dev->image_size - dev->image_end; if (size > 0x8000) size = 0x8000; if (size == 0) { /* Probably reached the end of the buffer. * Check, just in case. */ assert (dev->image_end != 0); return (SANE_STATUS_GOOD); } DBG (DBG_info, "sane_read: to read = %ld bytes (bpl=%d)\n", (long) size, dev->params.bytes_per_line); MKSCSI_READ_10 (cdb, 0, 0, size); cdb.data[4] = dev->page_num; /* May be cdb.data[3] too? */ cdb.data[5] = dev->page_side; hexdump (DBG_info2, "sane_read: READ_10 CDB", cdb.data, 10); status = sanei_scsi_cmd2 (dev->sfd, cdb.data, cdb.len, NULL, 0, dev->buffer, &size); if (status == SANE_STATUS_EOF) { DBG (DBG_proc, "sane_read: exit, end of page scan\n"); return (SANE_STATUS_EOF); } if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "sane_read: cannot read from the scanner\n"); return status; } dev->real_bytes_left -= size; switch (dev->depth) { case 1: { /* For Black & White, the bits in every bytes are mirrored. * for instance 11010001 is coded as 10001011 */ unsigned char *src = dev->buffer; unsigned char *dest = dev->image + dev->image_end; unsigned char s; unsigned char d; size_t i; for (i = 0; i < size; i++) { s = *src; d = 0; if (s & 0x01) d |= 0x80; if (s & 0x02) d |= 0x40; if (s & 0x04) d |= 0x20; if (s & 0x08) d |= 0x10; if (s & 0x10) d |= 0x08; if (s & 0x20) d |= 0x04; if (s & 0x40) d |= 0x02; if (s & 0x80) d |= 0x01; *dest = d; src++; dest++; } } break; case 4: { /* Adjust from a depth of 4 bits ([0..15]) to * a depth of 8 bits ([0..255]) */ unsigned char *src = dev->buffer; unsigned char *dest = dev->image + dev->image_end; size_t i; /* n bytes from image --> 2*n bytes in buf. */ for (i = 0; i < size; i++) { *dest = ((*src & 0x0f) >> 0) * 17; dest++; *dest = ((*src & 0xf0) >> 4) * 17; dest++; src++; } size *= 2; } break; default: memcpy (dev->image + dev->image_end, dev->buffer, size); break; } dev->image_end += size; } return (SANE_STATUS_GOOD); /* unreachable */ } /* Copy from the raw buffer to the buffer given by the backend. * * len in input is the maximum length available in buf, and, in * output, is the length written into buf. */ static void matsushita_copy_raw_to_frontend (Matsushita_Scanner * dev, SANE_Byte * buf, size_t * len) { size_t size; size = dev->image_end - dev->image_begin; if (size > *len) { size = *len; } *len = size; memcpy (buf, dev->image + dev->image_begin, size); dev->image_begin += size; } /* Stop a scan. */ static SANE_Status do_cancel (Matsushita_Scanner * dev) { DBG (DBG_sane_proc, "do_cancel enter\n"); if (dev->scanning == SANE_TRUE) { /* Reset the scanner */ matsushita_reset_window (dev); matsushita_close (dev); } dev->scanning = SANE_FALSE; DBG (DBG_sane_proc, "do_cancel exit\n"); return SANE_STATUS_CANCELLED; } /*--------------------------------------------------------------------------*/ /* Entry points */ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize) { FILE *fp; char dev_name[PATH_MAX]; size_t len; DBG_INIT (); DBG (DBG_sane_init, "sane_init\n"); DBG (DBG_error, "This is sane-matsushita version %d.%d-%d\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); DBG (DBG_error, "(C) 2002 by Frank Zago\n"); if (version_code) { *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); } fp = sanei_config_open (MATSUSHITA_CONFIG_FILE); if (!fp) { /* default to /dev/scanner instead of insisting on config file */ attach_scanner ("/dev/scanner", 0); return SANE_STATUS_GOOD; } while (sanei_config_read (dev_name, sizeof (dev_name), fp)) { if (dev_name[0] == '#') /* ignore line comments */ continue; len = strlen (dev_name); if (!len) continue; /* ignore empty lines */ sanei_config_attach_matching_devices (dev_name, attach_one); } fclose (fp); DBG (DBG_proc, "sane_init: leave\n"); return SANE_STATUS_GOOD; } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only) { Matsushita_Scanner *dev; int i; DBG (DBG_proc, "sane_get_devices: enter\n"); if (devlist) free (devlist); devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); if (!devlist) return SANE_STATUS_NO_MEM; i = 0; for (dev = first_dev; i < num_devices; dev = dev->next) devlist[i++] = &dev->sane; devlist[i++] = 0; *device_list = devlist; DBG (DBG_proc, "sane_get_devices: exit\n"); return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { Matsushita_Scanner *dev; SANE_Status status; DBG (DBG_proc, "sane_open: enter\n"); /* search for devicename */ if (devicename[0]) { DBG (DBG_info, "sane_open: devicename=%s\n", devicename); for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->sane.name, devicename) == 0) { break; } } if (!dev) { status = attach_scanner (devicename, &dev); if (status != SANE_STATUS_GOOD) { return status; } } } else { DBG (DBG_sane_info, "sane_open: no devicename, opening first device\n"); dev = first_dev; /* empty devicename -> use first device */ } if (!dev) { DBG (DBG_error, "No scanner found\n"); return SANE_STATUS_INVAL; } /* Build a list a paper size that fit into this scanner. */ matsushita_build_paper_sizes (dev); matsushita_init_options (dev); *handle = dev; DBG (DBG_proc, "sane_open: exit\n"); return SANE_STATUS_GOOD; } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { Matsushita_Scanner *dev = handle; DBG (DBG_proc, "sane_get_option_descriptor: enter, option %d\n", option); if ((unsigned) option >= OPT_NUM_OPTIONS) { return NULL; } DBG (DBG_proc, "sane_get_option_descriptor: exit\n"); return dev->opt + option; } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { Matsushita_Scanner *dev = handle; SANE_Status status; SANE_Word cap; SANE_String_Const name; int i; SANE_Word value; int rc; DBG (DBG_proc, "sane_control_option: enter, option %d, action %d\n", option, action); if (info) { *info = 0; } if (dev->scanning) { return SANE_STATUS_DEVICE_BUSY; } if (option < 0 || option >= OPT_NUM_OPTIONS) { return SANE_STATUS_INVAL; } cap = dev->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) { return SANE_STATUS_INVAL; } name = dev->opt[option].name; if (!name) { name = "(no name)"; } if (action == SANE_ACTION_GET_VALUE) { switch (option) { /* word options */ case OPT_NUM_OPTS: case OPT_RESOLUTION: case OPT_TL_Y: case OPT_BR_Y: case OPT_TL_X: case OPT_BR_X: case OPT_BRIGHTNESS: case OPT_CONTRAST: case OPT_DUPLEX: case OPT_AUTOMATIC_SEPARATION: *(SANE_Word *) val = dev->val[option].w; return SANE_STATUS_GOOD; /* string options */ case OPT_MODE: case OPT_FEEDER_MODE: case OPT_HALFTONE_PATTERN: case OPT_PAPER_SIZE: case OPT_AUTOMATIC_THRESHOLD: case OPT_WHITE_LEVEL: case OPT_NOISE_REDUCTION: case OPT_IMAGE_EMPHASIS: case OPT_GAMMA: strcpy (val, dev->val[option].s); return SANE_STATUS_GOOD; default: return SANE_STATUS_INVAL; } } else if (action == SANE_ACTION_SET_VALUE) { if (!SANE_OPTION_IS_SETTABLE (cap)) { DBG (DBG_error, "could not set option, not settable\n"); return SANE_STATUS_INVAL; } status = sanei_constrain_value (dev->opt + option, val, info); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "could not set option, invalid value\n"); return status; } switch (option) { /* Side-effect options */ case OPT_TL_Y: case OPT_BR_Y: case OPT_RESOLUTION: if (info) { *info |= SANE_INFO_RELOAD_PARAMS; } dev->val[option].w = *(SANE_Word *) val; return SANE_STATUS_GOOD; /* The length of X must be rounded (up). */ case OPT_TL_X: case OPT_BR_X: value = mmToIlu (SANE_UNFIX (*(SANE_Word *) val)); i = get_int_list_index (scanners[dev->scnum].resolutions_list, dev->val[OPT_RESOLUTION].w); if (value & (scanners[dev->scnum].resolutions_round[i] - 1)) { value = (value | (scanners[dev->scnum].resolutions_round[i] - 1)) + 1; if (info) { *info |= SANE_INFO_INEXACT; } } *(SANE_Word *) val = SANE_FIX (iluToMm (value)); dev->val[option].w = *(SANE_Word *) val; if (info) { *info |= SANE_INFO_RELOAD_PARAMS; } return SANE_STATUS_GOOD; /* Side-effect free options */ case OPT_CONTRAST: case OPT_BRIGHTNESS: case OPT_DUPLEX: case OPT_AUTOMATIC_SEPARATION: dev->val[option].w = *(SANE_Word *) val; return SANE_STATUS_GOOD; /* String mode */ case OPT_WHITE_LEVEL: case OPT_NOISE_REDUCTION: case OPT_IMAGE_EMPHASIS: case OPT_GAMMA: case OPT_FEEDER_MODE: free (dev->val[option].s); dev->val[option].s = (SANE_String) strdup (val); return SANE_STATUS_GOOD; case OPT_MODE: if (strcmp (dev->val[option].s, val) == 0) return SANE_STATUS_GOOD; free (dev->val[OPT_MODE].s); dev->val[OPT_MODE].s = (SANE_Char *) strdup (val); /* Set default options for the scan modes. */ dev->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_AUTOMATIC_THRESHOLD].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_AUTOMATIC_SEPARATION].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_NOISE_REDUCTION].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_GAMMA].cap |= SANE_CAP_INACTIVE; if (strcmp (dev->val[OPT_MODE].s, BLACK_WHITE_STR) == 0) { dev->depth = 1; dev->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE; dev->opt[OPT_AUTOMATIC_THRESHOLD].cap &= ~SANE_CAP_INACTIVE; dev->opt[OPT_AUTOMATIC_SEPARATION].cap &= ~SANE_CAP_INACTIVE; dev->opt[OPT_NOISE_REDUCTION].cap &= ~SANE_CAP_INACTIVE; i = get_string_list_index (halftone_pattern_list, dev->val[OPT_HALFTONE_PATTERN].s); if (halftone_pattern_val[i] == -1) { dev->scan_mode = MATSUSHITA_BW; } else { dev->scan_mode = MATSUSHITA_HALFTONE; } } else if (strcmp (dev->val[OPT_MODE].s, GRAY4_STR) == 0) { dev->scan_mode = MATSUSHITA_GRAYSCALE; dev->depth = 4; dev->opt[OPT_GAMMA].cap &= ~SANE_CAP_INACTIVE; } else if (strcmp (dev->val[OPT_MODE].s, GRAY8_STR) == 0) { dev->scan_mode = MATSUSHITA_GRAYSCALE; dev->depth = 8; dev->opt[OPT_GAMMA].cap &= ~SANE_CAP_INACTIVE; } else { assert (0 == 1); } /* Some options might not be supported by the scanner. */ if ((scanners[dev->scnum].cap & MAT_CAP_GAMMA) == 0) dev->opt[OPT_GAMMA].cap |= SANE_CAP_INACTIVE; if (info) { *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; } return SANE_STATUS_GOOD; case OPT_HALFTONE_PATTERN: free (dev->val[option].s); dev->val[option].s = (SANE_String) strdup (val); i = get_string_list_index (halftone_pattern_list, dev->val[OPT_HALFTONE_PATTERN].s); if (halftone_pattern_val[i] == -1) { dev->scan_mode = MATSUSHITA_BW; } else { dev->scan_mode = MATSUSHITA_HALFTONE; } return SANE_STATUS_GOOD; case OPT_PAPER_SIZE: if (strcmp (dev->val[option].s, val) == 0) return SANE_STATUS_GOOD; free (dev->val[OPT_PAPER_SIZE].s); dev->val[OPT_PAPER_SIZE].s = (SANE_Char *) strdup (val); i = get_string_list_index (dev->paper_sizes_list, dev->val[OPT_PAPER_SIZE].s); i = dev->paper_sizes_val[i]; /* Set the 4 corners values. */ value = 0; rc = sane_control_option (handle, OPT_TL_X, SANE_ACTION_SET_VALUE, &value, info); assert (rc == SANE_STATUS_GOOD); value = 0; rc = sane_control_option (handle, OPT_TL_Y, SANE_ACTION_SET_VALUE, &value, info); assert (rc == SANE_STATUS_GOOD); value = SANE_FIX (paper_sizes[i].width); rc = sane_control_option (handle, OPT_BR_X, SANE_ACTION_SET_VALUE, &value, info); assert (rc == SANE_STATUS_GOOD); value = SANE_FIX (paper_sizes[i].length); rc = sane_control_option (handle, OPT_BR_Y, SANE_ACTION_SET_VALUE, &value, info); assert (rc == SANE_STATUS_GOOD); if (info) *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_AUTOMATIC_THRESHOLD: if (strcmp (dev->val[option].s, val) == 0) return SANE_STATUS_GOOD; free (dev->val[option].s); dev->val[option].s = (SANE_Char *) strdup (val); /* If the threshold is not set to none, some option must * disappear. */ dev->opt[OPT_WHITE_LEVEL].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_NOISE_REDUCTION].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_IMAGE_EMPHASIS].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_AUTOMATIC_SEPARATION].cap |= SANE_CAP_INACTIVE; dev->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; if (strcmp (dev->val[option].s, automatic_threshold_list[0]) == 0) { dev->opt[OPT_WHITE_LEVEL].cap &= ~SANE_CAP_INACTIVE; dev->opt[OPT_NOISE_REDUCTION].cap &= ~SANE_CAP_INACTIVE; dev->opt[OPT_IMAGE_EMPHASIS].cap &= ~SANE_CAP_INACTIVE; dev->opt[OPT_AUTOMATIC_SEPARATION].cap &= ~SANE_CAP_INACTIVE; if (dev->scan_mode == MATSUSHITA_BW || dev->scan_mode == MATSUSHITA_HALFTONE) { dev->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE; } } if (info) { *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; } return SANE_STATUS_GOOD; default: return SANE_STATUS_INVAL; } } DBG (DBG_proc, "sane_control_option: exit, bad\n"); return SANE_STATUS_UNSUPPORTED; } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { Matsushita_Scanner *dev = handle; DBG (DBG_proc, "sane_get_parameters: enter\n"); if (!(dev->scanning)) { /* Setup the parameters for the scan. These values will be re-used * in the SET WINDOWS command. */ dev->resolution = dev->val[OPT_RESOLUTION].w; dev->x_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_X].w)); dev->y_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_Y].w)); dev->x_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_X].w)); dev->y_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_Y].w)); /* Check the corners are OK. */ if (dev->x_tl > dev->x_br) { int s; s = dev->x_tl; dev->x_tl = dev->x_br; dev->x_br = s; } if (dev->y_tl > dev->y_br) { int s; s = dev->y_tl; dev->y_tl = dev->y_br; dev->y_br = s; } dev->width = dev->x_br - dev->x_tl; dev->length = dev->y_br - dev->y_tl; /* Prepare the parameters for the caller. */ memset (&dev->params, 0, sizeof (SANE_Parameters)); dev->params.format = SANE_FRAME_GRAY; dev->params.last_frame = SANE_TRUE; dev->params.pixels_per_line = (((dev->width * dev->resolution) / 1200) + 7) & ~0x7; if (dev->depth == 4) { dev->params.depth = 8; } else { dev->params.depth = dev->depth; } dev->params.bytes_per_line = (dev->params.pixels_per_line / 8) * dev->params.depth; dev->params.lines = (dev->length * dev->resolution) / 1200; } /* Return the current values. */ if (params) { *params = (dev->params); } DBG (DBG_proc, "sane_get_parameters: exit\n"); return SANE_STATUS_GOOD; } SANE_Status sane_start (SANE_Handle handle) { Matsushita_Scanner *dev = handle; SANE_Status status; DBG (DBG_proc, "sane_start: enter\n"); if (!(dev->scanning)) { sane_get_parameters (dev, NULL); if (dev->image == NULL) { dev->image_size = 3 * dev->buffer_size; dev->image = malloc (dev->image_size); if (dev->image == NULL) { return SANE_STATUS_NO_MEM; } } /* Open again the scanner. */ if (sanei_scsi_open (dev->devicename, &(dev->sfd), matsushita_sense_handler, dev) != 0) { DBG (DBG_error, "ERROR: sane_start: open failed\n"); return SANE_STATUS_INVAL; } dev->page_side = 0; /* page front */ dev->page_num = 0; /* first page */ /* The scanner must be ready. */ status = matsushita_wait_scanner (dev); if (status) { matsushita_close (dev); return status; } status = matsushita_reset_window (dev); if (status) { matsushita_close (dev); return status; } status = matsushita_set_window (dev, PAGE_FRONT); if (status) { matsushita_close (dev); return status; } if (dev->val[OPT_DUPLEX].w == SANE_TRUE) { status = matsushita_set_window (dev, PAGE_BACK); if (status) { matsushita_close (dev); return status; } } status = matsushita_read_document_size (dev); if (status) { matsushita_close (dev); return status; } } else { if (dev->val[OPT_DUPLEX].w == SANE_TRUE && dev->page_side == PAGE_FRONT) { dev->page_side = PAGE_BACK; } else { /* new sheet. */ dev->page_side = PAGE_FRONT; dev->page_num++; } status = matsushita_check_next_page (dev); if (status) { return status; } } dev->bytes_left = dev->params.bytes_per_line * dev->params.lines; dev->real_bytes_left = dev->params.bytes_per_line * dev->params.lines; if (dev->depth == 4) { /* Every byte read will be expanded into 2 bytes. */ dev->real_bytes_left /= 2; } dev->image_end = 0; dev->image_begin = 0; dev->scanning = SANE_TRUE; DBG (DBG_proc, "sane_start: exit\n"); return SANE_STATUS_GOOD; } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { SANE_Status status; Matsushita_Scanner *dev = handle; size_t size; int buf_offset; /* offset into buf */ DBG (DBG_proc, "sane_read: enter\n"); *len = 0; if (!(dev->scanning)) { /* OOPS, not scanning */ return do_cancel (dev); } if (dev->bytes_left <= 0) { return (SANE_STATUS_EOF); } buf_offset = 0; do { if (dev->image_begin == dev->image_end) { /* Fill image */ status = matsushita_fill_image (dev); if (status != SANE_STATUS_GOOD) { return (status); } } /* Something must have been read */ if (dev->image_begin == dev->image_end) { DBG (DBG_info, "sane_read: nothing read\n"); return SANE_STATUS_IO_ERROR; } /* Copy the data to the frontend buffer. */ size = max_len - buf_offset; if (size > dev->bytes_left) { size = dev->bytes_left; } matsushita_copy_raw_to_frontend (dev, buf + buf_offset, &size); buf_offset += size; dev->bytes_left -= size; *len += size; } while ((buf_offset != max_len) && dev->bytes_left); DBG (DBG_info, "sane_read: leave, bytes_left=%ld\n", (long)dev->bytes_left); return SANE_STATUS_GOOD; } SANE_Status sane_set_io_mode (SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking) { SANE_Status status; Matsushita_Scanner *dev = handle; DBG (DBG_proc, "sane_set_io_mode: enter\n"); if (dev->scanning == SANE_FALSE) { return (SANE_STATUS_INVAL); } if (non_blocking == SANE_FALSE) { status = SANE_STATUS_GOOD; } else { status = SANE_STATUS_UNSUPPORTED; } DBG (DBG_proc, "sane_set_io_mode: exit\n"); return status; } SANE_Status sane_get_select_fd (SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ * fd) { DBG (DBG_proc, "sane_get_select_fd: enter\n"); DBG (DBG_proc, "sane_get_select_fd: exit\n"); return SANE_STATUS_UNSUPPORTED; } void sane_cancel (SANE_Handle handle) { Matsushita_Scanner *dev = handle; DBG (DBG_proc, "sane_cancel: enter\n"); do_cancel (dev); DBG (DBG_proc, "sane_cancel: exit\n"); } void sane_close (SANE_Handle handle) { Matsushita_Scanner *dev = handle; Matsushita_Scanner *dev_tmp; DBG (DBG_proc, "sane_close: enter\n"); do_cancel (dev); matsushita_close (dev); /* Unlink dev. */ if (first_dev == dev) { first_dev = dev->next; } else { dev_tmp = first_dev; while (dev_tmp->next && dev_tmp->next != dev) { dev_tmp = dev_tmp->next; } if (dev_tmp->next != NULL) { dev_tmp->next = dev_tmp->next->next; } } matsushita_free (dev); num_devices--; DBG (DBG_proc, "sane_close: exit\n"); } void sane_exit (void) { DBG (DBG_proc, "sane_exit: enter\n"); while (first_dev) { sane_close (first_dev); } if (devlist) { free (devlist); devlist = NULL; } DBG (DBG_proc, "sane_exit: exit\n"); } backends-1.3.0/backend/matsushita.conf.in000066400000000000000000000012311456256263500203250ustar00rootroot00000000000000# # 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 backends-1.3.0/backend/matsushita.h000066400000000000000000000261661456256263500172400ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2002 Frank Zago (sane at zago dot net) 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, see . 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. */ /* Commands supported by the KV-SS 25 scanner. */ #define SCSI_TEST_UNIT_READY 0x00 #define SCSI_INQUIRY 0x12 #define SCSI_SET_WINDOW 0x24 #define SCSI_READ_10 0x28 #define SCSI_REQUEST_SENSE 0x03 typedef struct { unsigned char data[16]; int len; } CDB; /* Set a specific bit depending on a boolean. * MKSCSI_BIT(TRUE, 3) will generate 0x08. */ #define MKSCSI_BIT(bit, pos) ((bit)? 1<<(pos): 0) /* Set a value in a range of bits. * MKSCSI_I2B(5, 3, 5) will generate 0x28 */ #define MKSCSI_I2B(bits, pos_b, pos_e) ((bits) << (pos_b) & ((1<<((pos_e)-(pos_b)+1))-1)) /* Store an integer in 2, 3 or 4 byte in an array. */ #define Ito16(val, buf) { \ ((unsigned char *)buf)[0] = ((val) >> 8) & 0xff; \ ((unsigned char *)buf)[1] = ((val) >> 0) & 0xff; \ } #define Ito24(val, buf) { \ ((unsigned char *)buf)[0] = ((val) >> 16) & 0xff; \ ((unsigned char *)buf)[1] = ((val) >> 8) & 0xff; \ ((unsigned char *)buf)[2] = ((val) >> 0) & 0xff; \ } #define Ito32(val, buf) { \ ((unsigned char *)buf)[0] = ((val) >> 24) & 0xff; \ ((unsigned char *)buf)[1] = ((val) >> 16) & 0xff; \ ((unsigned char *)buf)[2] = ((val) >> 8) & 0xff; \ ((unsigned char *)buf)[3] = ((val) >> 0) & 0xff; \ } #define MKSCSI_TEST_UNIT_READY(cdb) \ cdb.data[0] = SCSI_TEST_UNIT_READY; \ cdb.data[1] = 0; \ cdb.data[2] = 0; \ cdb.data[3] = 0; \ cdb.data[4] = 0; \ cdb.data[5] = 0; \ cdb.len = 6; #define MKSCSI_INQUIRY(cdb, buflen) \ cdb.data[0] = SCSI_INQUIRY; \ cdb.data[1] = 0; \ cdb.data[2] = 0; \ cdb.data[3] = 0; \ cdb.data[4] = buflen; \ cdb.data[5] = 0; \ cdb.len = 6; #define MKSCSI_SET_WINDOW(cdb, buflen) \ cdb.data[0] = SCSI_SET_WINDOW; \ cdb.data[1] = 0; \ cdb.data[2] = 0; \ cdb.data[3] = 0; \ cdb.data[4] = 0; \ cdb.data[5] = 0; \ cdb.data[6] = (((buflen) >> 16) & 0xff); \ cdb.data[7] = (((buflen) >> 8) & 0xff); \ cdb.data[8] = (((buflen) >> 0) & 0xff); \ cdb.data[9] = 0; \ cdb.len = 10; #define MKSCSI_READ_10(cdb, dtc, dtq, buflen) \ cdb.data[0] = SCSI_READ_10; \ cdb.data[1] = 0; \ cdb.data[2] = (dtc); \ cdb.data[3] = 0; \ cdb.data[4] = (((dtq) >> 8) & 0xff); \ cdb.data[5] = (((dtq) >> 0) & 0xff); \ cdb.data[6] = (((buflen) >> 16) & 0xff); \ cdb.data[7] = (((buflen) >> 8) & 0xff); \ cdb.data[8] = (((buflen) >> 0) & 0xff); \ cdb.data[9] = 0; \ cdb.len = 10; #define MKSCSI_REQUEST_SENSE(cdb, buflen) \ cdb.data[0] = SCSI_REQUEST_SENSE; \ cdb.data[1] = 0; \ cdb.data[2] = 0; \ cdb.data[3] = 0; \ cdb.data[4] = (buflen); \ cdb.data[5] = 0; \ cdb.len = 6; /*--------------------------------------------------------------------------*/ static inline int getbitfield (unsigned char *pageaddr, int mask, int shift) { return ((*pageaddr >> shift) & mask); } /* defines for request sense return block */ #define get_RS_information_valid(b) getbitfield(b + 0x00, 1, 7) #define get_RS_error_code(b) getbitfield(b + 0x00, 0x7f, 0) #define get_RS_filemark(b) getbitfield(b + 0x02, 1, 7) #define get_RS_EOM(b) getbitfield(b + 0x02, 1, 6) #define get_RS_ILI(b) getbitfield(b + 0x02, 1, 5) #define get_RS_sense_key(b) getbitfield(b + 0x02, 0x0f, 0) #define get_RS_information(b) getnbyte(b+0x03, 4) #define get_RS_additional_length(b) b[0x07] #define get_RS_ASC(b) b[0x0c] #define get_RS_ASCQ(b) b[0x0d] #define get_RS_SKSV(b) getbitfield(b+0x0f,1,7) /*--------------------------------------------------------------------------*/ #define mmToIlu(mm) (((mm) * 1200) / MM_PER_INCH) #define iluToMm(ilu) (((ilu) * MM_PER_INCH) / 1200) #define PAGE_FRONT 0x00 #define PAGE_BACK 0x80 /*--------------------------------------------------------------------------*/ enum Matsushita_Option { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_MODE, /* scanner modes */ OPT_RESOLUTION, /* X and Y resolution */ OPT_DUPLEX, /* Duplex mode */ OPT_FEEDER_MODE, /* Feeding mode */ OPT_GEOMETRY_GROUP, OPT_PAPER_SIZE, /* Paper size */ OPT_TL_X, /* upper left X */ OPT_TL_Y, /* upper left Y */ OPT_BR_X, /* bottom right X */ OPT_BR_Y, /* bottom right Y */ OPT_ENHANCEMENT_GROUP, OPT_BRIGHTNESS, /* Brightness */ OPT_CONTRAST, /* Contrast */ OPT_AUTOMATIC_THRESHOLD, /* Automatic threshold */ OPT_HALFTONE_PATTERN, /* Halftone pattern */ OPT_AUTOMATIC_SEPARATION, /* Automatic separation */ OPT_WHITE_LEVEL, /* White level */ OPT_NOISE_REDUCTION, /* Noise reduction */ OPT_IMAGE_EMPHASIS, /* Image emphasis */ OPT_GAMMA, /* Gamma */ /* must come last: */ OPT_NUM_OPTIONS }; /*--------------------------------------------------------------------------*/ #define BLACK_WHITE_STR SANE_VALUE_SCAN_MODE_LINEART #define GRAY4_STR SANE_I18N("Grayscale 4 bits") #define GRAY8_STR SANE_I18N("Grayscale 8 bits") /*--------------------------------------------------------------------------*/ #define SANE_NAME_DUPLEX "duplex" #define SANE_NAME_PAPER_SIZE "paper-size" #define SANE_NAME_AUTOSEP "autoseparation" #define SANE_TITLE_DUPLEX SANE_I18N("Duplex") #define SANE_TITLE_PAPER_SIZE SANE_I18N("Paper size") #define SANE_TITLE_AUTOSEP SANE_I18N("Automatic separation") #define SANE_DESC_DUPLEX \ SANE_I18N("Enable Duplex (Dual-Sided) Scanning") #define SANE_DESC_PAPER_SIZE \ SANE_I18N("Physical size of the paper in the ADF"); #define SANE_DESC_AUTOSEP \ SANE_I18N("Automatic separation") /*--------------------------------------------------------------------------*/ /* Differences between the scanners. * The scsi_* fields are used to lookup the correcte entry. */ struct scanners_supported { int scsi_type; char scsi_vendor[9]; char scsi_product[17]; SANE_Range x_range; SANE_Range y_range; SANE_Range brightness_range; SANE_Range contrast_range; SANE_String_Const *scan_mode_list; /* array of scan modes */ const SANE_Word *resolutions_list; /* array of available resolutions */ const SANE_Word *resolutions_round; /* rounding values for each resolutions */ SANE_String_Const *image_emphasis_list; /* list of image emphasis options */ const int *image_emphasis_val; /* list of image emphasis values */ /* Scanner capabilities. */ int cap; /* bit field */ #define MAT_CAP_DUPLEX 0x00000002 /* can do duplex */ #define MAT_CAP_CONTRAST 0x00000004 /* have contrast */ #define MAT_CAP_AUTOMATIC_THRESHOLD 0x00000008 #define MAT_CAP_WHITE_LEVEL 0x00000010 #define MAT_CAP_GAMMA 0x00000020 #define MAT_CAP_NOISE_REDUCTION 0x00000040 #define MAT_CAP_PAPER_DETECT 0x00000080 #define MAT_CAP_MIRROR_IMAGE 0x00000100 #define MAT_CAP_DETECT_DOUBLE_FEED 0x00000200 #define MAT_CAP_MANUAL_FEED 0x00000400 }; struct paper_sizes { SANE_String_Const name; /* name of the paper */ int width; int length; }; /*--------------------------------------------------------------------------*/ /* Define a scanner occurrence. */ typedef struct Matsushita_Scanner { struct Matsushita_Scanner *next; SANE_Device sane; char *devicename; int sfd; /* device handle */ /* Infos from inquiry. */ char scsi_type; char scsi_vendor[9]; char scsi_product[17]; char scsi_version[5]; /* Scanner infos. */ int scnum; /* index of that scanner in * scanners_supported */ SANE_String_Const *paper_sizes_list; /* names of supported papers */ int *paper_sizes_val; /* indirection into paper_sizes[] */ /* SCSI handling */ size_t buffer_size; /* size of the buffer */ SANE_Byte *buffer; /* for SCSI transfer. */ /* Scanning handling. */ int scanning; /* TRUE if a scan is running. */ int resolution; /* resolution in DPI, for both X and Y */ int x_tl; /* X top left */ int y_tl; /* Y top left */ int x_br; /* X bottom right */ int y_br; /* Y bottom right */ int width; /* width of the scan area in mm */ int length; /* length of the scan area in mm */ enum { MATSUSHITA_BW, MATSUSHITA_HALFTONE, MATSUSHITA_GRAYSCALE } scan_mode; int depth; /* depth per color */ int halftone_pattern; /* haltone number, valid for MATSUSHITA_HALFTONE */ size_t bytes_left; /* number of bytes left to give to the backend */ size_t real_bytes_left; /* number of bytes left the scanner will return. */ SANE_Parameters params; int page_side; /* 0=front, 1=back */ int page_num; /* current number of the page */ /* For Grayscale 4 bits only */ SANE_Byte *image; /* keep the current image there */ size_t image_size; /* allocated size of image */ size_t image_begin; /* first significant byte in image */ size_t image_end; /* first free byte in image */ /* Options */ SANE_Option_Descriptor opt[OPT_NUM_OPTIONS]; Option_Value val[OPT_NUM_OPTIONS]; } Matsushita_Scanner; /*--------------------------------------------------------------------------*/ /* Debug levels. * Should be common to all backends. */ #define DBG_error0 0 #define DBG_error 1 #define DBG_sense 2 #define DBG_warning 3 #define DBG_inquiry 4 #define DBG_info 5 #define DBG_info2 6 #define DBG_proc 7 #define DBG_read 8 #define DBG_sane_init 10 #define DBG_sane_proc 11 #define DBG_sane_info 12 #define DBG_sane_option 13 /*--------------------------------------------------------------------------*/ /* 32 bits from an array to an integer (eg ntohl). */ #define B32TOI(buf) \ ((((unsigned char *)buf)[0] << 24) | \ (((unsigned char *)buf)[1] << 16) | \ (((unsigned char *)buf)[2] << 8) | \ (((unsigned char *)buf)[3] << 0)) backends-1.3.0/backend/microtek.c000066400000000000000000004230131456256263500166560ustar00rootroot00000000000000/*************************************************************************** * SANE - Scanner Access Now Easy. microtek.c This file Copyright 2002 Matthew Marjanovic This file is part of the SANE package. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . As a special exception, the authors of SANE give permission for additional uses of the libraries contained in this release of SANE. The exception is that, if you link a SANE library with other files to produce an executable, this does not by itself cause the resulting executable to be covered by the GNU General Public License. Your use of that executable is in no way restricted on account of linking the SANE library code into it. This exception does not, however, invalidate any other reasons why the executable file might be covered by the GNU General Public License. If you submit changes to SANE to the maintainers to be included in a subsequent release, you agree by submitting the changes that those changes may be distributed with this exception intact. If you write modifications of your own for SANE, it is your choice whether to permit this exception to apply to your modifications. If you do not wish that, delete this exception notice. *************************************************************************** This file implements a SANE backend for Microtek scanners. (feedback to: mtek-bugs@mir.com) (for latest info: http://www.mir.com/mtek/) ***************************************************************************/ #define MICROTEK_MAJOR 0 #define MICROTEK_MINOR 13 #define MICROTEK_PATCH 1 #include "../include/sane/config.h" #include #include #include #include #include #include #include "../include/_stdint.h" #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_scsi.h" #include "../include/sane/saneopts.h" #define BACKEND_NAME microtek #include "../include/sane/sanei_backend.h" #include "microtek.h" #define MICROTEK_CONFIG_FILE "microtek.conf" #ifndef PATH_MAX # define PATH_MAX 1024 #endif #define SCSI_BUFF_SIZE sanei_scsi_max_request_size #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #define MAX(a,b) (((a) > (b)) ? (a) : (b)) static int num_devices = 0; static Microtek_Device *first_dev = NULL; /* list of known devices */ static Microtek_Scanner *first_handle = NULL; /* list of open scanners */ static const SANE_Device **devlist = NULL; /* sane_get_devices() */ static SANE_Bool inhibit_clever_precal = SANE_FALSE; static SANE_Bool inhibit_real_calib = SANE_FALSE; #define M_GSS_WAIT 5 /* seconds */ #define M_LINEART SANE_VALUE_SCAN_MODE_LINEART #define M_HALFTONE SANE_VALUE_SCAN_MODE_HALFTONE #define M_GRAY SANE_VALUE_SCAN_MODE_GRAY #define M_COLOR SANE_VALUE_SCAN_MODE_COLOR #define M_OPAQUE "Opaque/Normal" #define M_TRANS "Transparency" #define M_AUTOFEED "AutoFeeder" #define M_NONE "None" #define M_SCALAR "Scalar" #define M_TABLE "Table" static SANE_String_Const gamma_mode_list[4] = { M_NONE, M_SCALAR, M_TABLE, NULL }; /* These are for the E6. Does this hold for other models? */ static SANE_String_Const halftone_mode_list[13] = { " 1 53-dot screen (53 gray levels)", " 2 Horiz. screen (65 gray levels)", " 3 Vert. screen (65 gray levels)", " 4 Mixed page (33 gray levels)", " 5 71-dot screen (29 gray levels)", " 6 60-dot #1 (26 gray levels)", " 7 60-dot #2 (26 gray levels)", " 8 Fine detail #1 (17 gray levels)", " 9 Fine detail #2 (17 gray levels)", "10 Slant line (17 gray levels)", "11 Posterizing (10 gray levels)", "12 High Contrast (5 gray levels)", NULL }; static SANE_Range speed_range = {1, 7, 1}; static SANE_Range brightness_range = {-100, 100, 1}; /*static SANE_Range brightness_range = {0, 255, 1};*/ /*static SANE_Range exposure_range = {-18, 21, 3};*/ /*static SANE_Range contrast_range = {-42, 49, 7};*/ static SANE_Range u8_range = {0, 255, 1}; static SANE_Range analog_gamma_range = { SANE_FIX(0.1), SANE_FIX(4.0), SANE_FIX(0) }; #define MAX_MDBG_LENGTH 1024 static char _mdebug_string[MAX_MDBG_LENGTH]; static void MDBG_INIT(const char *format, ...) { va_list ap; va_start(ap, format); vsnprintf(_mdebug_string, MAX_MDBG_LENGTH, format, ap); va_end(ap); } static void MDBG_ADD(const char *format, ...) { int len = strlen(_mdebug_string); va_list ap; va_start(ap, format); vsnprintf(_mdebug_string+len, MAX_MDBG_LENGTH-len, format, ap); va_end(ap); } static void MDBG_FINISH(int dbglvl) { DBG(dbglvl, "%s\n", _mdebug_string); } /********************************************************************/ /********************************************************************/ /*** Utility Functions **********************************************/ /********************************************************************/ /********************************************************************/ static size_t max_string_size (const SANE_String_Const strings[]) { size_t size, max_size = 0; int i; for (i = 0; strings[i]; ++i) { size = strlen(strings[i]) + 1; if (size > max_size) max_size = size; } return max_size; } /********************************************************************/ /* Allocate/create a new ring buffer */ /********************************************************************/ static ring_buffer * ring_alloc (size_t initial_size, size_t bpl, size_t ppl) { ring_buffer *rb; uint8_t *buff; if ((rb = (ring_buffer *)malloc(sizeof(*rb))) == NULL) return NULL; if ((buff = (uint8_t *)malloc(initial_size * sizeof(*buff))) == NULL) { free(rb); return NULL; } rb->base = buff; rb->size = initial_size; rb->initial_size = initial_size; rb->bpl = bpl; rb->ppl = ppl; rb->tail_red = 0; rb->tail_green = 1; rb->tail_blue = 2; rb->head_complete = 0; rb->red_extra = 0; rb->green_extra = 0; rb->blue_extra = 0; rb->complete_count = 0; return rb; } /********************************************************************/ /* Enlarge an existing ring buffer */ /********************************************************************/ static SANE_Status ring_expand (ring_buffer *rb, size_t amount) { uint8_t *buff; size_t oldsize; if (rb == NULL) return SANE_STATUS_INVAL; buff = (uint8_t *)realloc(rb->base, (rb->size + amount) * sizeof(*buff)); if (buff == NULL) return SANE_STATUS_NO_MEM; rb->base = buff; oldsize = rb->size; rb->size += amount; DBG(23, "ring_expand: old, new, inc size: %lu, %lu, %lu\n", (u_long)oldsize, (u_long)rb->size, (u_long)amount); DBG(23, "ring_expand: old tr: %lu tg: %lu tb: %lu hc: %lu\n", (u_long)rb->tail_red, (u_long)rb->tail_green, (u_long)rb->tail_blue, (u_long)rb->head_complete); /* if necessary, move data and fix up them pointers */ /* (will break subtly if ring is filled with G or B bytes, and tail_g or tail_b have rolled over...) */ if (((rb->complete_count) || (rb->red_extra) || (rb->green_extra) || (rb->blue_extra)) && ((rb->tail_red <= rb->head_complete) || (rb->tail_green <= rb->head_complete) || (rb->tail_blue <= rb->head_complete))) { memmove(rb->base + rb->head_complete + amount, rb->base + rb->head_complete, oldsize - rb->head_complete); if ((rb->tail_red > rb->head_complete) || ((rb->tail_red == rb->head_complete) && !(rb->complete_count) && !(rb->red_extra))) rb->tail_red += amount; if ((rb->tail_green > rb->head_complete) || ((rb->tail_green == rb->head_complete) && !(rb->complete_count) && !(rb->green_extra))) rb->tail_green += amount; if ((rb->tail_blue > rb->head_complete) || ((rb->tail_blue == rb->head_complete) && !(rb->complete_count) && !(rb->blue_extra))) rb->tail_blue += amount; rb->head_complete += amount; } DBG(23, "ring_expand: new tr: %lu tg: %lu tb: %lu hc: %lu\n", (u_long)rb->tail_red, (u_long)rb->tail_green, (u_long)rb->tail_blue, (u_long)rb->head_complete); return SANE_STATUS_GOOD; } /********************************************************************/ /* Deallocate a ring buffer */ /********************************************************************/ static void ring_free (ring_buffer *rb) { free(rb->base); free(rb); } /********************************************************************/ /********************************************************************/ /*** Basic SCSI Commands ********************************************/ /********************************************************************/ /********************************************************************/ /********************************************************************/ /* parse sense from scsi error */ /* (even though microtek sense codes are non-standard and */ /* typically misinterpreted/munged by the low-level scsi driver) */ /********************************************************************/ static SANE_Status sense_handler (int scsi_fd, u_char *sense, void *arg) { int *sense_flags = (int *)arg; SANE_Status stat; DBG(10, "SENSE! fd = %d\n", scsi_fd); DBG(10, "sense = %02x %02x %02x %02x.\n", sense[0], sense[1], sense[2], sense[3]); switch(sense[0]) { case 0x00: return SANE_STATUS_GOOD; case 0x81: /* COMMAND/DATA ERROR */ stat = SANE_STATUS_GOOD; if (sense[1] & 0x01) { if ((sense_flags != NULL) && (*sense_flags & MS_SENSE_IGNORE)) DBG(10, "sense: ERR_SCSICMD -- ignored\n"); else { DBG(10, "sense: ERR_SCSICMD\n"); stat = SANE_STATUS_IO_ERROR; } } if (sense[1] & 0x02) { DBG(10, "sense: ERR_TOOMANY\n"); stat = SANE_STATUS_IO_ERROR; } return stat; case 0x82 : /* SCANNER HARDWARE ERROR */ if (sense[1] & 0x01) DBG(10, "sense: ERR_CPURAMFAIL\n"); if (sense[1] & 0x02) DBG(10, "sense: ERR_SYSRAMFAIL\n"); if (sense[1] & 0x04) DBG(10, "sense: ERR_IMGRAMFAIL\n"); if (sense[1] & 0x10) DBG(10, "sense: ERR_CALIBRATE\n"); if (sense[1] & 0x20) DBG(10, "sense: ERR_LAMPFAIL\n"); if (sense[1] & 0x40) DBG(10, "sense: ERR_MOTORFAIL\n"); if (sense[1] & 0x80) DBG(10, "sense: ERR_FEEDERFAIL\n"); if (sense[2] & 0x01) DBG(10, "sense: ERR_POWERFAIL\n"); if (sense[2] & 0x02) DBG(10, "sense: ERR_ILAMPFAIL\n"); if (sense[2] & 0x04) DBG(10, "sense: ERR_IMOTORFAIL\n"); if (sense[2] & 0x08) DBG(10, "sense: ERR_PAPERFAIL\n"); if (sense[2] & 0x10) DBG(10, "sense: ERR_FILTERFAIL\n"); return SANE_STATUS_IO_ERROR; case 0x83 : /* OPERATION ERROR */ if (sense[1] & 0x01) DBG(10, "sense: ERR_ILLGRAIN\n"); if (sense[1] & 0x02) DBG(10, "sense: ERR_ILLRES\n"); if (sense[1] & 0x04) DBG(10, "sense: ERR_ILLCOORD\n"); if (sense[1] & 0x10) DBG(10, "sense: ERR_ILLCNTR\n"); if (sense[1] & 0x20) DBG(10, "sense: ERR_ILLLENGTH\n"); if (sense[1] & 0x40) DBG(10, "sense: ERR_ILLADJUST\n"); if (sense[1] & 0x80) DBG(10, "sense: ERR_ILLEXPOSE\n"); if (sense[2] & 0x01) DBG(10, "sense: ERR_ILLFILTER\n"); if (sense[2] & 0x02) DBG(10, "sense: ERR_NOPAPER\n"); if (sense[2] & 0x04) DBG(10, "sense: ERR_ILLTABLE\n"); if (sense[2] & 0x08) DBG(10, "sense: ERR_ILLOFFSET\n"); if (sense[2] & 0x10) DBG(10, "sense: ERR_ILLBPP\n"); return SANE_STATUS_IO_ERROR; default : DBG(10, "sense: unknown error\n"); return SANE_STATUS_IO_ERROR; } return SANE_STATUS_GOOD; } /********************************************************************/ /* wait (via polling) until scanner seems "ready" */ /********************************************************************/ static SANE_Status wait_ready(Microtek_Scanner *ms) { uint8_t comm[6] = { 0, 0, 0, 0, 0, 0 }; SANE_Status status; int retry = 0; DBG(23, ".wait_ready %d...\n", ms->sfd); while ((status = sanei_scsi_cmd(ms->sfd, comm, 6, 0, 0)) != SANE_STATUS_GOOD) { DBG(23, "wait_ready failed (%d)\n", retry); if (retry > 5) return SANE_STATUS_IO_ERROR; /* XXXXXXXX */ retry++; sleep(3); } return SANE_STATUS_GOOD; } /********************************************************************/ /* send scan region coordinates */ /********************************************************************/ static SANE_Status scanning_frame(Microtek_Scanner *ms) { uint8_t *data, comm[15] = { 0x04, 0, 0, 0, 0x09, 0 }; int x1, y1, x2, y2; DBG(23, ".scanning_frame...\n"); x1 = ms->x1; x2 = ms->x2; y1 = ms->y1; y2 = ms->y2; /* E6 weirdness (other models too?) */ if (ms->unit_type == MS_UNIT_18INCH) { x1 /= 2; x2 /= 2; y1 /= 2; y2 /= 2; } DBG(23, ".scanning_frame: in- %d,%d %d,%d\n", ms->x1, ms->y1, ms->x2, ms->y2); DBG(23, ".scanning_frame: out- %d,%d %d,%d\n", x1, y1, x2, y2); data = comm + 6; data[0] = ((ms->unit_type == MS_UNIT_PIXELS) ? 0x08 : 0 ) | ((ms->mode == MS_MODE_HALFTONE) ? 0x01 : 0 ); data[1] = x1 & 0xFF; data[2] = (x1 >> 8) & 0xFF; data[3] = y1 & 0xFF; data[4] = (y1 >> 8) & 0xFF; data[5] = x2 & 0xFF; data[6] = (x2 >> 8) & 0xFF; data[7] = y2 & 0xFF; data[8] = (y2 >> 8) & 0xFF; if (DBG_LEVEL >= 192) { int i; #if 0 fprintf(stderr, "SF: "); for (i=0;i<6+0x09;i++) fprintf(stderr, "%2x ", comm[i]); fprintf(stderr, "\n"); #endif MDBG_INIT("SF: "); for (i=0;i<6+0x09;i++) MDBG_ADD("%2x ", comm[i]); MDBG_FINISH(192); } return sanei_scsi_cmd(ms->sfd, comm, 6 + 0x09, 0, 0); } /********************************************************************/ /* send "mode_select" */ /********************************************************************/ static SANE_Status mode_select(Microtek_Scanner *ms) { uint8_t *data, comm[19] = { 0x15, 0, 0, 0, 0, 0 }; DBG(23, ".mode_select %d...\n", ms->sfd); data = comm + 6; data[0] = 0x81 | ((ms->unit_type == MS_UNIT_18INCH) ? 0 : 0x08) | ((ms->res_type == MS_RES_5PER) ? 0 : 0x02); data[1] = ms->resolution_code; data[2] = ms->exposure; data[3] = ms->contrast; data[4] = ms->pattern; data[5] = ms->velocity; data[6] = ms->shadow; data[7] = ms->highlight; DBG(23, ".mode_select: pap_len: %d\n", ms->paper_length); data[8] = ms->paper_length & 0xFF; data[9] = (ms->paper_length >> 8) & 0xFF; data[10] = ms->midtone; /* set command/data length */ comm[4] = (ms->midtone_support) ? 0x0B : 0x0A; if (DBG_LEVEL >= 192) { int i; #if 0 fprintf(stderr, "MSL: "); for (i=0;i<6+comm[4];i++) fprintf(stderr, "%2x ", comm[i]); fprintf(stderr, "\n"); #endif MDBG_INIT("MSL: "); for (i=0;i<6+comm[4];i++) MDBG_ADD("%2x ", comm[i]); MDBG_FINISH(192); } return sanei_scsi_cmd(ms->sfd, comm, 6 + comm[4], 0, 0); } /********************************************************************/ /* send "mode_select_1" */ /********************************************************************/ static SANE_Status mode_select_1(Microtek_Scanner *ms) { uint8_t *data, comm[16] = { 0x16, 0, 0, 0, 0x0A, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; DBG(23, ".mode_select_1 %d...\n", ms->sfd); data = comm + 6; data[1] = ms->bright_r; data[3] = ((ms->allow_calibrate) ? 0 : 0x02); /* | 0x01; */ if (DBG_LEVEL >= 192) { int i; #if 0 fprintf(stderr, "MSL1: "); for (i=0;i<6+0x0A;i++) fprintf(stderr, "%2x ", comm[i]); fprintf(stderr, "\n"); #endif MDBG_INIT("MSL1: "); for (i=0;i<6+0x0A;i++) MDBG_ADD("%2x ", comm[i]); MDBG_FINISH(192); } return sanei_scsi_cmd(ms->sfd, comm, 6 + 0x0A, 0, 0); } /********************************************************************/ /* record mode_sense results in the mode_sense buffer */ /* (this is to tell if something catastrophic has happened */ /* to the scanner in-between scans) */ /********************************************************************/ static SANE_Status save_mode_sense(Microtek_Scanner *ms) { uint8_t data[20], comm[6] = { 0x1A, 0, 0, 0, 0, 0}; size_t lenp; SANE_Status status; int i; DBG(23, ".save_mode_sense %d...\n", ms->sfd); if (ms->onepass) comm[4] = 0x13; else if (ms->midtone_support) comm[4] = 0x0B; else comm[4] = 0x0A; lenp = comm[4]; status = sanei_scsi_cmd(ms->sfd, comm, 6, data, &lenp); for (i=0; i<10; i++) ms->mode_sense_cache[i] = data[i]; if (DBG_LEVEL >= 192) { unsigned int i; #if 0 fprintf(stderr, "SMS: "); for (i=0;isfd); if (ms->onepass) comm[4] = 0x13; else if (ms->midtone_support) comm[4] = 0x0B; else comm[4] = 0x0A; lenp = comm[4]; status = sanei_scsi_cmd(ms->sfd, comm, 6, data, &lenp); *match = 1; for (i=0; i<10; i++) *match = *match && (ms->mode_sense_cache[i] == data[i]); if (DBG_LEVEL >= 192) { unsigned int i; #if 0 fprintf(stderr, "CMS: "); for (i=0;imode_sense_cache[i]); fprintf(stderr, "\n"); #endif MDBG_INIT("CMS: "); for (i=0;imode_sense_cache[i]); MDBG_FINISH(192); } return status; } /********************************************************************/ /* send mode_sense_1, and upset every scsi driver known to mankind */ /********************************************************************/ #if 0 static SANE_Status mode_sense_1(Microtek_Scanner *ms) { uint8_t *data, comm[36] = { 0x19, 0, 0, 0, 0x1E, 0 }; DBG(23, ".mode_sense_1...\n"); data = comm + 6; memset(data, 0, 30); data[1] = ms->bright_r; data[2] = ms->bright_g; data[3] = ms->bright_b; if (DBG_LEVEL >= 192) { int i; fprintf(stderr, "MS1: "); for (i=0;i<6+0x1E;i++) fprintf(stderr, "%2x ", comm[i]); fprintf(stderr, "\n"); } return sanei_scsi_cmd(ms->sfd, comm, 6 + 0x1E, 0, 0); } #endif /********************************************************************/ /* send "accessory" command */ /********************************************************************/ static SANE_Status accessory(Microtek_Scanner *ms) { uint8_t comm[6] = { 0x10, 0, 0, 0, 0, 0 }; DBG(23, ".accessory...\n"); comm[4] = ((ms->useADF) ? 0x41 : 0x40) | ((ms->prescan) ? 0x18 : 0x10) | ((ms->transparency) ? 0x24 : 0x20) | ((ms->allowbacktrack) ? 0x82 : 0x80); if (DBG_LEVEL >= 192) { int i; #if 0 fprintf(stderr, "AC: "); for (i=0;i<6;i++) fprintf(stderr, "%2x ", comm[i]); fprintf(stderr, "\n"); #endif MDBG_INIT("AC: "); for (i=0;i<6;i++) MDBG_ADD("%2x ", comm[i]); MDBG_FINISH(192); } return sanei_scsi_cmd(ms->sfd, comm, 6, 0, 0); } /********************************************************************/ /* start the scanner a-scannin' */ /********************************************************************/ static SANE_Status start_scan(Microtek_Scanner *ms) { uint8_t comm[6] = { 0x1B, 0, 0, 0, 0, 0 }; DBG(23, ".start_scan...\n"); comm[4] = 0x01 | /* "start" */ ((ms->expandedresolution) ? 0x80 : 0) | ((ms->multibit) ? 0x40 : 0) | ((ms->onepasscolor) ? 0x20 : 0) | ((ms->reversecolors) ? 0x04 : 0) | ((ms->fastprescan) ? 0x02 : 0) | ((ms->filter == MS_FILT_RED) ? 0x08 : 0) | ((ms->filter == MS_FILT_GREEN) ? 0x10 : 0) | ((ms->filter == MS_FILT_BLUE) ? 0x18 : 0) ; if (DBG_LEVEL >= 192) { int i; #if 0 fprintf(stderr, "SS: "); for (i=0;i<6;i++) fprintf(stderr, "%2x ", comm[i]); fprintf(stderr, "\n"); #endif MDBG_INIT("SS: "); for (i=0;i<6;i++) MDBG_ADD("%2x ", comm[i]); MDBG_FINISH(192); } return sanei_scsi_cmd(ms->sfd, comm, 6, 0, 0); } /********************************************************************/ /* stop the scanner a-scannin' */ /********************************************************************/ static SANE_Status stop_scan(Microtek_Scanner *ms) { uint8_t comm[6] = { 0x1B, 0, 0, 0, 0, 0 }; DBG(23, ".stop_scan...\n"); if (DBG_LEVEL >= 192) { int i; #if 0 fprintf(stderr, "SPS:"); for (i=0;i<6;i++) fprintf(stderr, "%2x ", comm[i]); fprintf(stderr, "\n"); #endif MDBG_INIT("SPS:"); for (i=0;i<6;i++) MDBG_ADD("%2x ", comm[i]); MDBG_FINISH(192); } return sanei_scsi_cmd(ms->sfd, comm, 6, 0, 0); } /********************************************************************/ /* get scan status */ /********************************************************************/ static SANE_Status get_scan_status(Microtek_Scanner *ms, SANE_Int *busy, SANE_Int *bytes_per_line, SANE_Int *lines) { uint8_t data[6], comm[6] = { 0x0F, 0, 0, 0, 0x06, 0 }; SANE_Status status; size_t lenp; int retry = 0; DBG(23, ".get_scan_status %d...\n", ms->sfd); do { lenp = 6; /* do some retry stuff in here, too */ status = sanei_scsi_cmd(ms->sfd, comm, 6, data, &lenp); if (status != SANE_STATUS_GOOD) { DBG(20, "get_scan_status: scsi error\n"); return status; } *busy = data[0]; *bytes_per_line = (data[1]) + (data[2] << 8); *lines = (data[3]) + (data[4] << 8) + (data[5] << 16); DBG(20, "get_scan_status(%lu): %d, %d, %d -> #%d\n", (u_long) lenp, *busy, *bytes_per_line, *lines, retry); DBG(20, "> %2x %2x %2x %2x %2x %2x\n", data[0], data[1], data[2], data[3], data[4], data[5]); if (*busy != 0) { retry++; DBG(23, "get_scan_status: busy, retry in %d...\n", M_GSS_WAIT * retry); sleep(M_GSS_WAIT * retry); } } while ((*busy != 0) && (retry < 4)); if (*busy == 0) return status; else return SANE_STATUS_IO_ERROR; } /********************************************************************/ /* get scanlines from scanner */ /********************************************************************/ static SANE_Status read_scan_data(Microtek_Scanner *ms, int lines, uint8_t *buffer, size_t *bufsize) { uint8_t comm[6] = { 0x08, 0, 0, 0, 0, 0 }; DBG(23, ".read_scan_data...\n"); comm[2] = (lines >> 16) & 0xFF; comm[3] = (lines >> 8) & 0xFF; comm[4] = (lines) & 0xFF; return sanei_scsi_cmd(ms->sfd, comm, 6, buffer, bufsize); } /********************************************************************/ /* download color LUT to scanner (if it takes one) */ /********************************************************************/ static SANE_Status download_gamma(Microtek_Scanner *ms) { uint8_t *data, *comm; /* commbytes[10] = { 0x55, 0, 0x27, 0, 0, 0, 0, 0, 0, 0 };*/ int i, pl; int commsize; int bit_depth = 8; /* hard-code for now, should match bpp XXXXXXX */ int max_entry; SANE_Status status; DBG(23, ".download_gamma...\n"); /* skip if scanner doesn't take 'em */ if (!(ms->gamma_entries)) { DBG(23, ".download_gamma: no entries; skipping\n"); return SANE_STATUS_GOOD; } if ((ms->gamma_entry_size != 1) && (ms->gamma_entry_size != 2)) { DBG(23, ".download_gamma: entry size %d?!?!?\n", ms->gamma_entry_size); return SANE_STATUS_INVAL; /* XXXXXXXxx */ } max_entry = (1 << bit_depth) - 1; DBG(23, ".download_gamma: %d entries of %d bytes, max %d\n", ms->gamma_entries, ms->gamma_entry_size, max_entry); commsize = 10 + (ms->gamma_entries * ms->gamma_entry_size); comm = calloc(commsize, sizeof(uint8_t)); if (comm == NULL) { DBG(23, ".download_gamma: couldn't allocate %d bytes for comm buffer!\n", commsize); return SANE_STATUS_NO_MEM; } data = comm + 10; comm[0] = 0x55; comm[1] = 0; comm[2] = 0x27; comm[3] = 0; comm[4] = 0; comm[5] = 0; comm[6] = 0; comm[7] = ((ms->gamma_entries * ms->gamma_entry_size) >> 8) & 0xFF; comm[8] = (ms->gamma_entries * ms->gamma_entry_size) & 0xFF; comm[9] = (ms->gamma_entry_size == 2) ? 1 : 0; if (!(strcmp(ms->val[OPT_CUSTOM_GAMMA].s, M_TABLE))) { /***** Gamma by TABLE *****/ int table_shift = (ms->gamma_bit_depth - bit_depth); DBG(23, ".download_gamma: by table (%d bpe, %d shift)\n", ms->gamma_bit_depth, table_shift); if (ms->val[OPT_GAMMA_BIND].w == SANE_TRUE) { for (i=0; igamma_entries; i++) { int val = ms->gray_lut[i] >> table_shift; switch (ms->gamma_entry_size) { case 1: data[i] = (uint8_t) val; break; case 2: data[i*2] = val & 0xFF; data[(i*2)+1] = (val>>8) & 0xFF; break; } } status = sanei_scsi_cmd(ms->sfd, comm, commsize, 0, 0); } else { pl = 1; do { SANE_Int *pl_lut; switch (pl) { case 1: pl_lut = ms->red_lut; break; case 2: pl_lut = ms->green_lut; break; case 3: pl_lut = ms->blue_lut; break; default: DBG(23, ".download_gamma: uh, exceeded pl bound!\n"); free(comm); return SANE_STATUS_INVAL; /* XXXXXXXxx */ break; } for (i=0; igamma_entries; i++) { int val = pl_lut[i] >> table_shift; switch (ms->gamma_entry_size) { case 1: data[i] = (uint8_t) val; break; case 2: data[i*2] = val & 0xFF; data[(i*2)+1] = (val>>8) & 0xFF; break; } } /* XXXXXXX */ comm[9] = (comm[9] & 0x3F) | (pl << 6); status = sanei_scsi_cmd(ms->sfd, comm, commsize, 0, 0); pl++; } while ((pl < 4) && (status == SANE_STATUS_GOOD)); } } else if (!(strcmp(ms->val[OPT_CUSTOM_GAMMA].s, M_SCALAR))) { /***** Gamma by SCALAR *****/ DBG(23, ".download_gamma: by scalar\n"); if (ms->val[OPT_GAMMA_BIND].w == SANE_TRUE) { double gamma = SANE_UNFIX(ms->val[OPT_ANALOG_GAMMA].w); for (i=0; igamma_entries; i++) { int val = (max_entry * pow((double) i / ((double) ms->gamma_entries - 1.0), 1.0 / gamma)); switch (ms->gamma_entry_size) { case 1: data[i] = (uint8_t) val; break; case 2: data[i*2] = val & 0xFF; data[(i*2)+1] = (val>>8) & 0xFF; break; } } status = sanei_scsi_cmd(ms->sfd, comm, commsize, 0, 0); } else { double gamma; pl = 1; do { switch (pl) { case 1: gamma = SANE_UNFIX(ms->val[OPT_ANALOG_GAMMA_R].w); break; case 2: gamma = SANE_UNFIX(ms->val[OPT_ANALOG_GAMMA_G].w); break; case 3: gamma = SANE_UNFIX(ms->val[OPT_ANALOG_GAMMA_B].w); break; default: gamma = 1.0; break; /* should never happen */ } for (i=0; igamma_entries; i++) { int val = (max_entry * pow((double) i / ((double) ms->gamma_entries - 1.0), 1.0 / gamma)); switch (ms->gamma_entry_size) { case 1: data[i] = (uint8_t) val; break; case 2: data[i*2] = val & 0xFF; data[(i*2)+1] = (val>>8) & 0xFF; break; } } comm[9] = (comm[9] & 0x3F) | (pl << 6); status = sanei_scsi_cmd(ms->sfd, comm, commsize, 0, 0); pl++; } while ((pl < 4) && (status == SANE_STATUS_GOOD)); } } else { /***** No custom Gamma *****/ DBG(23, ".download_gamma: by default\n"); for (i=0; igamma_entries; i++) { /* int val = (((double) max_entry * (double) i / ((double) ms->gamma_entries - 1.0)) + 0.5); ROUNDING????*/ int val = (double) max_entry * (double) i / ((double) ms->gamma_entries - 1.0); switch (ms->gamma_entry_size) { case 1: data[i] = (uint8_t) val; break; case 2: data[i*2] = val & 0xFF; data[(i*2)+1] = (val >> 8) & 0xFF; break; } } status = sanei_scsi_cmd(ms->sfd, comm, commsize, 0, 0); } free(comm); return status; } /********************************************************************/ /* magic command to start calibration */ /********************************************************************/ static SANE_Status start_calibration(Microtek_Scanner *ms) { uint8_t comm[8] = { 0x11, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x0a }; DBG(23, ".start_calibrate...\n"); if (DBG_LEVEL >= 192) { int i; #if 0 fprintf(stderr, "STCal:"); for (i=0;i<8;i++) fprintf(stderr, "%2x ", comm[i]); fprintf(stderr, "\n"); #endif MDBG_INIT("STCal:"); for (i=0;i<8;i++) MDBG_ADD("%2x ", comm[i]); MDBG_FINISH(192); } return sanei_scsi_cmd(ms->sfd, comm, 8, 0, 0); } /********************************************************************/ /* magic command to download calibration values */ /********************************************************************/ static SANE_Status download_calibration(Microtek_Scanner *ms, uint8_t *comm, uint8_t letter, int linewidth) { DBG(23, ".download_calibration... %c %d\n", letter, linewidth); comm[0] = 0x0c; comm[1] = 0x00; comm[2] = 0x00; comm[3] = (linewidth >> 8) & 0xFF; comm[4] = linewidth & 0xFF; comm[5] = 0x00; comm[6] = 0x00; switch (letter) { case 'R': comm[7] = 0x40; break; case 'G': comm[7] = 0x80; break; case 'B': comm[7] = 0xc0; break; default: /* XXXXXXX */ break; } return sanei_scsi_cmd(ms->sfd, comm, 6 + linewidth, 0, 0); } /********************************************************************/ /********************************************************************/ /* */ /* Myriad of internal functions */ /* */ /********************************************************************/ /********************************************************************/ /********************************************************************/ /* Initialize the options registry */ /********************************************************************/ static SANE_Status init_options(Microtek_Scanner *ms) { int i; SANE_Option_Descriptor *sod = ms->sod; Option_Value *val = ms->val; DBG(15, "init_options...\n"); memset(ms->sod, 0, sizeof(ms->sod)); memset(ms->val, 0, sizeof(ms->val)); /* default: software selectable word options... */ for (i=0; idev->info.modes & MI_MODES_COLOR) mode_list[i++] = M_COLOR; if (ms->dev->info.modes & MI_MODES_GRAY) mode_list[i++] = M_GRAY; if (ms->dev->info.modes & MI_MODES_HALFTONE) mode_list[i++] = M_HALFTONE; if (ms->dev->info.modes & MI_MODES_LINEART) mode_list[i++] = M_LINEART; mode_list[i] = NULL; sod[OPT_MODE].constraint.string_list = mode_list; sod[OPT_MODE].size = max_string_size(mode_list); val[OPT_MODE].s = strdup(mode_list[0]); } sod[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; sod[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; sod[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; sod[OPT_RESOLUTION].type = SANE_TYPE_FIXED; sod[OPT_RESOLUTION].unit = SANE_UNIT_DPI; sod[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; { SANE_Int maxres = ms->dev->info.base_resolution; ms->res_range.max = SANE_FIX(maxres); ms->exp_res_range.max = SANE_FIX(2 * maxres); if (ms->dev->info.res_step & MI_RESSTEP_1PER) { DBG(23, "init_options: quant yes\n"); ms->res_range.min = SANE_FIX( maxres / 100 ); ms->res_range.quant = ms->res_range.min; ms->exp_res_range.min = SANE_FIX(2 * maxres / 100); ms->exp_res_range.quant = ms->exp_res_range.min; } else { /* XXXXXXXXXXX */ DBG(23, "init_options: quant no\n"); ms->res_range.quant = SANE_FIX( 0 ); } sod[OPT_RESOLUTION].constraint.range = &(ms->res_range); } val[OPT_RESOLUTION].w = SANE_FIX(100); sod[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN; sod[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN; sod[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN; sod[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING; sod[OPT_HALFTONE_PATTERN].size = max_string_size(halftone_mode_list); sod[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; sod[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_STRING_LIST; sod[OPT_HALFTONE_PATTERN].constraint.string_list = halftone_mode_list; val[OPT_HALFTONE_PATTERN].s = strdup(halftone_mode_list[0]); sod[OPT_NEGATIVE].name = SANE_NAME_NEGATIVE; sod[OPT_NEGATIVE].title = SANE_TITLE_NEGATIVE; sod[OPT_NEGATIVE].desc = SANE_DESC_NEGATIVE; sod[OPT_NEGATIVE].type = SANE_TYPE_BOOL; sod[OPT_NEGATIVE].cap |= (ms->dev->info.modes & MI_MODES_NEGATIVE) ? 0 : SANE_CAP_INACTIVE; val[OPT_NEGATIVE].w = SANE_FALSE; sod[OPT_SPEED].name = SANE_NAME_SCAN_SPEED; sod[OPT_SPEED].title = SANE_TITLE_SCAN_SPEED; /* sod[OPT_SPEED].desc = SANE_DESC_SCAN_SPEED;*/ sod[OPT_SPEED].desc = "Scan speed throttle -- higher values are *slower*."; sod[OPT_SPEED].type = SANE_TYPE_INT; sod[OPT_SPEED].cap |= SANE_CAP_ADVANCED; sod[OPT_SPEED].unit = SANE_UNIT_NONE; sod[OPT_SPEED].size = sizeof(SANE_Word); sod[OPT_SPEED].constraint_type = SANE_CONSTRAINT_RANGE; sod[OPT_SPEED].constraint.range = &speed_range; val[OPT_SPEED].w = 1; sod[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE; sod[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE; sod[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE; sod[OPT_SOURCE].type = SANE_TYPE_STRING; sod[OPT_SOURCE].unit = SANE_UNIT_NONE; sod[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; { SANE_String_Const *source_list; source_list = (SANE_String_Const *) malloc(4 * sizeof(SANE_String_Const)); if (source_list == NULL) return SANE_STATUS_NO_MEM; i = 0; source_list[i++] = M_OPAQUE; if (ms->dev->info.source_options & MI_SRC_HAS_TRANS) source_list[i++] = M_TRANS; if (ms->dev->info.source_options & MI_SRC_HAS_FEED) source_list[i++] = M_AUTOFEED; source_list[i] = NULL; sod[OPT_SOURCE].constraint.string_list = source_list; sod[OPT_SOURCE].size = max_string_size(source_list); if (i < 2) sod[OPT_SOURCE].cap |= SANE_CAP_INACTIVE; val[OPT_SOURCE].s = strdup(source_list[0]); } sod[OPT_PREVIEW].name = SANE_NAME_PREVIEW; sod[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; sod[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; sod[OPT_PREVIEW].type = SANE_TYPE_BOOL; sod[OPT_PREVIEW].unit = SANE_UNIT_NONE; sod[OPT_PREVIEW].size = sizeof(SANE_Word); val[OPT_PREVIEW].w = SANE_FALSE; sod[OPT_GEOMETRY_GROUP].name = ""; sod[OPT_GEOMETRY_GROUP].title = "Geometry"; sod[OPT_GEOMETRY_GROUP].desc = ""; sod[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; sod[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; sod[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; sod[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; sod[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; sod[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; sod[OPT_TL_X].type = SANE_TYPE_FIXED; sod[OPT_TL_X].unit = SANE_UNIT_MM; sod[OPT_TL_X].size = sizeof(SANE_Word); sod[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; sod[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; sod[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; sod[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; sod[OPT_TL_Y].type = SANE_TYPE_FIXED; sod[OPT_TL_Y].unit = SANE_UNIT_MM; sod[OPT_TL_Y].size = sizeof(SANE_Word); sod[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; sod[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; sod[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; sod[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; sod[OPT_BR_X].type = SANE_TYPE_FIXED; sod[OPT_BR_X].unit = SANE_UNIT_MM; sod[OPT_BR_X].size = sizeof(SANE_Word); sod[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; sod[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; sod[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; sod[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; sod[OPT_BR_Y].type = SANE_TYPE_FIXED; sod[OPT_BR_Y].unit = SANE_UNIT_MM; sod[OPT_BR_Y].size = sizeof(SANE_Word); sod[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; sod[OPT_TL_X].constraint.range = sod[OPT_BR_X].constraint.range = &(ms->dev->info.doc_x_range); sod[OPT_TL_Y].constraint.range = sod[OPT_BR_Y].constraint.range = &(ms->dev->info.doc_y_range); /* set default scan region to be maximum size */ val[OPT_TL_X].w = sod[OPT_TL_X].constraint.range->min; val[OPT_TL_Y].w = sod[OPT_TL_Y].constraint.range->min; val[OPT_BR_X].w = sod[OPT_BR_X].constraint.range->max; val[OPT_BR_Y].w = sod[OPT_BR_Y].constraint.range->max; sod[OPT_ENHANCEMENT_GROUP].name = ""; sod[OPT_ENHANCEMENT_GROUP].title = "Enhancement"; sod[OPT_ENHANCEMENT_GROUP].desc = ""; sod[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; sod[OPT_ENHANCEMENT_GROUP].cap = 0; sod[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; sod[OPT_EXPOSURE].name = "exposure"; sod[OPT_EXPOSURE].title = "Exposure"; sod[OPT_EXPOSURE].desc = "Analog exposure control"; sod[OPT_EXPOSURE].type = SANE_TYPE_INT; sod[OPT_EXPOSURE].unit = SANE_UNIT_PERCENT; sod[OPT_EXPOSURE].size = sizeof(SANE_Word); sod[OPT_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE; ms->exposure_range.min = ms->dev->info.min_exposure; ms->exposure_range.max = ms->dev->info.max_exposure; ms->exposure_range.quant = 3; sod[OPT_EXPOSURE].constraint.range = &(ms->exposure_range); val[OPT_EXPOSURE].w = 0; sod[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; sod[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; sod[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; sod[OPT_BRIGHTNESS].type = SANE_TYPE_INT; sod[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT; sod[OPT_BRIGHTNESS].size = sizeof(SANE_Word); sod[OPT_BRIGHTNESS].cap |= ((ms->dev->info.extra_cap & MI_EXCAP_OFF_CTL) ? 0: SANE_CAP_INACTIVE); sod[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; sod[OPT_BRIGHTNESS].constraint.range = &brightness_range; val[OPT_BRIGHTNESS].w = 0; sod[OPT_CONTRAST].name = SANE_NAME_CONTRAST; sod[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; sod[OPT_CONTRAST].desc = SANE_DESC_CONTRAST; sod[OPT_CONTRAST].type = SANE_TYPE_INT; sod[OPT_CONTRAST].unit = SANE_UNIT_PERCENT; sod[OPT_CONTRAST].size = sizeof(SANE_Word); sod[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; ms->contrast_range.min = ms->dev->info.min_contrast; ms->contrast_range.max = ms->dev->info.max_contrast; ms->contrast_range.quant = 7; sod[OPT_CONTRAST].constraint.range = &(ms->contrast_range); val[OPT_CONTRAST].w = 0; sod[OPT_HIGHLIGHT].name = SANE_NAME_WHITE_LEVEL; sod[OPT_HIGHLIGHT].title = SANE_TITLE_WHITE_LEVEL; sod[OPT_HIGHLIGHT].desc = SANE_DESC_WHITE_LEVEL; sod[OPT_HIGHLIGHT].type = SANE_TYPE_INT; sod[OPT_HIGHLIGHT].unit = SANE_UNIT_NONE; sod[OPT_HIGHLIGHT].size = sizeof(SANE_Word); sod[OPT_HIGHLIGHT].constraint_type = SANE_CONSTRAINT_RANGE; sod[OPT_HIGHLIGHT].constraint.range = &u8_range; val[OPT_HIGHLIGHT].w = 255; sod[OPT_SHADOW].name = SANE_NAME_BLACK_LEVEL; sod[OPT_SHADOW].title = SANE_TITLE_BLACK_LEVEL; sod[OPT_SHADOW].desc = SANE_DESC_BLACK_LEVEL; sod[OPT_SHADOW].type = SANE_TYPE_INT; sod[OPT_SHADOW].unit = SANE_UNIT_NONE; sod[OPT_SHADOW].size = sizeof(SANE_Word); sod[OPT_SHADOW].constraint_type = SANE_CONSTRAINT_RANGE; sod[OPT_SHADOW].constraint.range = &u8_range; val[OPT_SHADOW].w = 0; if (ms->dev->info.enhance_cap & MI_ENH_CAP_SHADOW) { sod[OPT_HIGHLIGHT].cap |= SANE_CAP_ADVANCED; sod[OPT_SHADOW].cap |= SANE_CAP_ADVANCED; } else { sod[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE; sod[OPT_SHADOW].cap |= SANE_CAP_INACTIVE; } sod[OPT_MIDTONE].name = "midtone"; sod[OPT_MIDTONE].title = "Midtone Level"; sod[OPT_MIDTONE].desc = "Midtone Level"; sod[OPT_MIDTONE].type = SANE_TYPE_INT; sod[OPT_MIDTONE].unit = SANE_UNIT_NONE; sod[OPT_MIDTONE].size = sizeof(SANE_Word); sod[OPT_MIDTONE].constraint_type = SANE_CONSTRAINT_RANGE; sod[OPT_MIDTONE].constraint.range = &u8_range; val[OPT_MIDTONE].w = 128; if (ms->midtone_support) { sod[OPT_MIDTONE].cap |= SANE_CAP_ADVANCED; } else { sod[OPT_MIDTONE].cap |= SANE_CAP_INACTIVE; } /* XXXXXXXX is this supported by all scanners?? if ((strcmp(M_COLOR, val[OPT_MODE].s)) && (strcmp(M_GRAY, val[OPT_MODE].s))) { sod[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE; sod[OPT_SHADOW].cap |= SANE_CAP_INACTIVE; sod[OPT_MIDTONE].cap |= SANE_CAP_INACTIVE; } */ sod[OPT_GAMMA_GROUP].name = ""; sod[OPT_GAMMA_GROUP].title = "Gamma Control"; sod[OPT_GAMMA_GROUP].desc = ""; sod[OPT_GAMMA_GROUP].type = SANE_TYPE_GROUP; if (!(ms->gamma_entries)) sod[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_GROUP].constraint_type = SANE_CONSTRAINT_NONE; sod[OPT_CUSTOM_GAMMA].name = "gamma-mode"; sod[OPT_CUSTOM_GAMMA].title = "Gamma Control Mode"; sod[OPT_CUSTOM_GAMMA].desc = "How to specify gamma correction, if at all"; sod[OPT_CUSTOM_GAMMA].type = SANE_TYPE_STRING; sod[OPT_CUSTOM_GAMMA].size = max_string_size(gamma_mode_list); sod[OPT_CUSTOM_GAMMA].constraint_type = SANE_CONSTRAINT_STRING_LIST; sod[OPT_CUSTOM_GAMMA].constraint.string_list = gamma_mode_list; if (!(ms->gamma_entries)) sod[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; val[OPT_CUSTOM_GAMMA].s = strdup(gamma_mode_list[0]); sod[OPT_GAMMA_BIND].name = SANE_NAME_ANALOG_GAMMA_BIND; sod[OPT_GAMMA_BIND].title = SANE_TITLE_ANALOG_GAMMA_BIND; sod[OPT_GAMMA_BIND].desc = SANE_DESC_ANALOG_GAMMA_BIND; sod[OPT_GAMMA_BIND].type = SANE_TYPE_BOOL; sod[OPT_GAMMA_BIND].cap |= SANE_CAP_INACTIVE; val[OPT_GAMMA_BIND].w = SANE_TRUE; sod[OPT_ANALOG_GAMMA].name = SANE_NAME_ANALOG_GAMMA; sod[OPT_ANALOG_GAMMA].title = SANE_TITLE_ANALOG_GAMMA; sod[OPT_ANALOG_GAMMA].desc = SANE_DESC_ANALOG_GAMMA; sod[OPT_ANALOG_GAMMA].type = SANE_TYPE_FIXED; sod[OPT_ANALOG_GAMMA].unit = SANE_UNIT_NONE; sod[OPT_ANALOG_GAMMA].size = sizeof(SANE_Word); sod[OPT_ANALOG_GAMMA].cap |= SANE_CAP_INACTIVE; sod[OPT_ANALOG_GAMMA].constraint_type = SANE_CONSTRAINT_RANGE; sod[OPT_ANALOG_GAMMA].constraint.range = &analog_gamma_range; val[OPT_ANALOG_GAMMA].w = SANE_FIX(1.0); sod[OPT_ANALOG_GAMMA_R].name = SANE_NAME_ANALOG_GAMMA_R; sod[OPT_ANALOG_GAMMA_R].title = SANE_TITLE_ANALOG_GAMMA_R; sod[OPT_ANALOG_GAMMA_R].desc = SANE_DESC_ANALOG_GAMMA_R; sod[OPT_ANALOG_GAMMA_R].type = SANE_TYPE_FIXED; sod[OPT_ANALOG_GAMMA_R].unit = SANE_UNIT_NONE; sod[OPT_ANALOG_GAMMA_R].size = sizeof(SANE_Word); sod[OPT_ANALOG_GAMMA_R].cap |= SANE_CAP_INACTIVE; sod[OPT_ANALOG_GAMMA_R].constraint_type = SANE_CONSTRAINT_RANGE; sod[OPT_ANALOG_GAMMA_R].constraint.range = &analog_gamma_range; val[OPT_ANALOG_GAMMA_R].w = SANE_FIX(1.0); sod[OPT_ANALOG_GAMMA_G].name = SANE_NAME_ANALOG_GAMMA_G; sod[OPT_ANALOG_GAMMA_G].title = SANE_TITLE_ANALOG_GAMMA_G; sod[OPT_ANALOG_GAMMA_G].desc = SANE_DESC_ANALOG_GAMMA_G; sod[OPT_ANALOG_GAMMA_G].type = SANE_TYPE_FIXED; sod[OPT_ANALOG_GAMMA_G].unit = SANE_UNIT_NONE; sod[OPT_ANALOG_GAMMA_G].size = sizeof(SANE_Word); sod[OPT_ANALOG_GAMMA_G].cap |= SANE_CAP_INACTIVE; sod[OPT_ANALOG_GAMMA_G].constraint_type = SANE_CONSTRAINT_RANGE; sod[OPT_ANALOG_GAMMA_G].constraint.range = &analog_gamma_range; val[OPT_ANALOG_GAMMA_G].w = SANE_FIX(1.0); sod[OPT_ANALOG_GAMMA_B].name = SANE_NAME_ANALOG_GAMMA_B; sod[OPT_ANALOG_GAMMA_B].title = SANE_TITLE_ANALOG_GAMMA_B; sod[OPT_ANALOG_GAMMA_B].desc = SANE_DESC_ANALOG_GAMMA_B; sod[OPT_ANALOG_GAMMA_B].type = SANE_TYPE_FIXED; sod[OPT_ANALOG_GAMMA_B].unit = SANE_UNIT_NONE; sod[OPT_ANALOG_GAMMA_B].size = sizeof(SANE_Word); sod[OPT_ANALOG_GAMMA_B].cap |= SANE_CAP_INACTIVE; sod[OPT_ANALOG_GAMMA_B].constraint_type = SANE_CONSTRAINT_RANGE; sod[OPT_ANALOG_GAMMA_B].constraint.range = &analog_gamma_range; val[OPT_ANALOG_GAMMA_B].w = SANE_FIX(1.0); sod[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR; sod[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR; sod[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR; sod[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT; sod[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE; sod[OPT_GAMMA_VECTOR].size = ms->gamma_entries * sizeof(SANE_Word); sod[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE; sod[OPT_GAMMA_VECTOR].constraint.range = &(ms->gamma_entry_range); val[OPT_GAMMA_VECTOR].wa = ms->gray_lut; sod[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; sod[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R; sod[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R; sod[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT; sod[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE; sod[OPT_GAMMA_VECTOR_R].size = ms->gamma_entries * sizeof(SANE_Word); sod[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE; sod[OPT_GAMMA_VECTOR_R].constraint.range = &(ms->gamma_entry_range); val[OPT_GAMMA_VECTOR_R].wa = ms->red_lut; sod[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; sod[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G; sod[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G; sod[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT; sod[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE; sod[OPT_GAMMA_VECTOR_G].size = ms->gamma_entries * sizeof(SANE_Word); sod[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE; sod[OPT_GAMMA_VECTOR_G].constraint.range = &(ms->gamma_entry_range); val[OPT_GAMMA_VECTOR_G].wa = ms->green_lut; sod[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; sod[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B; sod[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B; sod[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT; sod[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE; sod[OPT_GAMMA_VECTOR_B].size = ms->gamma_entries * sizeof(SANE_Word); sod[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE; sod[OPT_GAMMA_VECTOR_B].constraint.range = &(ms->gamma_entry_range); val[OPT_GAMMA_VECTOR_B].wa = ms->blue_lut; sod[OPT_EXP_RES].name = "exp_res"; sod[OPT_EXP_RES].title = "Expanded Resolution"; sod[OPT_EXP_RES].desc = "Enable double-resolution scans"; sod[OPT_EXP_RES].type = SANE_TYPE_BOOL; sod[OPT_EXP_RES].cap |= SANE_CAP_ADVANCED; if (!(ms->dev->info.expanded_resolution)) sod[OPT_EXP_RES].cap |= SANE_CAP_INACTIVE; val[OPT_EXP_RES].w = SANE_FALSE; sod[OPT_CALIB_ONCE].name = "calib_once"; sod[OPT_CALIB_ONCE].title = "Calibrate Only Once"; sod[OPT_CALIB_ONCE].desc = "Avoid CCD calibration on every scan" \ "(toggle off/on to cause calibration on next scan)"; sod[OPT_CALIB_ONCE].type = SANE_TYPE_BOOL; sod[OPT_CALIB_ONCE].cap |= SANE_CAP_ADVANCED; if (!(ms->do_real_calib)) { sod[OPT_CALIB_ONCE].cap |= SANE_CAP_INACTIVE; val[OPT_CALIB_ONCE].w = SANE_FALSE; } else val[OPT_CALIB_ONCE].w = SANE_TRUE; /* sod[OPT_].name = SANE_NAME_; sod[OPT_].title = SANE_TITLE_; sod[OPT_].desc = SANE_DESC_; sod[OPT_].type = SANE_TYPE_; sod[OPT_].unit = SANE_UNIT_NONE; sod[OPT_].size = sizeof(SANE_Word); sod[OPT_].cap = 0; sod[OPT_].constraint_type = SANE_CONSTRAINT_NONE; sod[OPT_].constraint = ; val[OPT_].w = ; */ DBG(15, "init_options: done.\n"); return SANE_STATUS_GOOD; } /********************************************************************/ /* Parse an INQUIRY information block, as returned by scanner */ /********************************************************************/ static SANE_Status parse_inquiry(Microtek_Info *mi, unsigned char *result) { #if 0 unsigned char result[0x60] = { 0x06,0x31,0x23,0x01,0x5b,0x00,0x00,0x00,0x41,0x47,0x46,0x41,0x20,0x20,0x20,0x20, 0x53,0x74,0x75,0x64,0x69,0x6f,0x53,0x63,0x61,0x6e,0x20,0x49,0x49,0x20,0x20,0x20, 0x32,0x2e,0x33,0x30,0x53,0x43,0x53,0x49,0x20,0x46,0x2f,0x57,0x56,0x33,0x2e,0x31, 0x20,0x43,0x54,0x4c,0x35,0x33,0x38,0x30,0x03,0x4f,0x8c,0xc5,0x00,0xee,0x5b,0x43, 0x01,0x01,0x02,0x00,0x00,0x03,0x00,0x01,0x00,0x4a,0x01,0x04,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff }; #endif DBG(15, "parse_inquiry...\n"); snprintf(mi->vendor_id, 8 + 1, "%.*s", 8, (char *)&result[8]); snprintf(mi->model_name, 16 + 1, "%.*s", 16, (char *)&result[16]); snprintf(mi->revision_num, 4 + 1, "%.*s", 4, (char *)&result[32]); snprintf(mi->vendor_string, 20 + 1, "%.*s", 20, (char *)&result[36]); mi->device_type = (SANE_Byte)(result[0] & 0x1f); mi->SCSI_firmware_ver_major = (SANE_Byte)((result[1] & 0xf0) >> 4); mi->SCSI_firmware_ver_minor = (SANE_Byte)(result[1] & 0x0f); mi->scanner_firmware_ver_major = (SANE_Byte)((result[2] & 0xf0) >> 4); mi->scanner_firmware_ver_minor = (SANE_Byte)(result[2] & 0x0f); mi->response_data_format = (SANE_Byte)(result[3]); mi->res_step = (SANE_Byte)(result[56] & 0x03); mi->modes = (SANE_Byte)(result[57]); mi->pattern_count = (SANE_Int)(result[58] & 0x7f); mi->pattern_dwnld = (SANE_Byte)(result[58] & 0x80) > 0; mi->feed_type = (SANE_Byte)(result[59] & 0x0F); mi->compress_type = (SANE_Byte)(result[59] & 0x30); mi->unit_type = (SANE_Byte)(result[59] & 0xC0); mi->doc_size_code = (SANE_Byte)result[60]; /* we'll compute the max sizes after we know base resolution... */ /* why are these things set in two places (and probably wrong anyway)? */ mi->cont_settings = (SANE_Int)((result[61] & 0xf0) >> 4); if ((SANE_Int)(result[72])) mi->cont_settings = (SANE_Int)(result[72]); mi->min_contrast = -42; mi->max_contrast = (mi->cont_settings * 7) - 49; mi->exp_settings = (SANE_Int)(result[61] & 0x0f); if ((SANE_Int)(result[73])) mi->exp_settings = (SANE_Int)(result[73]); mi->min_exposure = -18; mi->max_exposure = (mi->exp_settings * 3) - 21; #if 0 mi->contrast_vals = (SANE_Int)(result[72]); mi->min_contrast = -42; mi->max_contrast = 49; if (mi->contrast_vals) mi->max_contrast = (mi->contrast_vals * 7) - 49; mi->exposure_vals = (SANE_Int)(result[73]); mi->min_exposure = -18; mi->max_exposure = 21; if (mi->exposure_vals) mi->max_exposure = (mi->exposure_vals * 3) - 21; #endif mi->model_code = (SANE_Byte)(result[62]); switch (mi->model_code) { case 0x16: /* the other ScanMaker 600ZS */ case 0x50: /* ScanMaker II/IIXE */ case 0x54: /* ScanMaker IISP */ case 0x55: /* ScanMaker IIER */ case 0x58: /* ScanMaker IIG */ case 0x5a: /* Agfa StudioScan (untested!) */ case 0x5f: /* ScanMaker E3 */ case 0x56: /* ScanMaker A3t */ case 0x64: /* ScanMaker E2 (,Vobis RealScan) */ case 0x65: /* Color PageWiz */ case 0xC8: /* ScanMaker 600ZS */ mi->base_resolution = 300; break; case 0x5b: /* Agfa StudioScan II/IIsi (untested!) */ mi->base_resolution = 400; break; case 0x57: /* ScanMaker IIHR */ case 0x59: /* ScanMaker III */ case 0x5c: /* Agfa Arcus II */ case 0x5e: /* Agfa StudioStar */ case 0x63: /* ScanMaker E6 */ case 0x66: /* ScanMaker E6 (new)*/ mi->base_resolution = 600; break; case 0x51: /* ScanMaker 45t */ case 0x5d: /* Agfa DuoScan */ mi->base_resolution = 1000; break; case 0x52: /* ScanMaker 35t */ mi->base_resolution = 1828; break; case 0x62: /* ScanMaker 35t+, Polaroid 35/LE */ mi->base_resolution = 1950; break; default: mi->base_resolution = 300; DBG(15, "parse_inquiry: Unknown base resolution for 0x%x!\n", mi->model_code); break; } /* Our max_x,y is in pixels. `Some scanners think in 1/8", though.' */ /* max pixel is, of course, total - 1 */ switch (mi->doc_size_code) { case 0x00: mi->max_x = 8.5 * mi->base_resolution - 1; mi->max_y = 14.0 * mi->base_resolution - 1; break; case 0x01: mi->max_x = 8.5 * mi->base_resolution - 1; mi->max_y = 11.0 * mi->base_resolution - 1; break; case 0x02: mi->max_x = 8.5 * mi->base_resolution - 1; mi->max_y = 11.69 * mi->base_resolution - 1; break; case 0x03: mi->max_x = 8.5 * mi->base_resolution - 1; mi->max_y = 13.0 * mi->base_resolution - 1; break; case 0x04: mi->max_x = 8.0 * mi->base_resolution - 1; mi->max_y = 10.0 * mi->base_resolution - 1; break; case 0x05: mi->max_x = 8.3 * mi->base_resolution - 1; mi->max_y = 14.0 * mi->base_resolution - 1; break; case 0x06: mi->max_x = 8.3 * mi->base_resolution - 1; mi->max_y = 13.5 * mi->base_resolution - 1; break; case 0x07: mi->max_x = 8.0 * mi->base_resolution - 1; mi->max_y = 14.0 * mi->base_resolution - 1; break; case 0x80: /* Slide format, size is mm */ mi->max_x = (35.0 / MM_PER_INCH) * mi->base_resolution - 1; mi->max_y = (35.0 / MM_PER_INCH) * mi->base_resolution - 1; break; case 0x81: mi->max_x = 5.0 * mi->base_resolution - 1; mi->max_y = 5.0 * mi->base_resolution - 1; break; case 0x82: /* Slide format, size is mm */ mi->max_x = (36.0 / MM_PER_INCH) * mi->base_resolution - 1; mi->max_y = (36.0 / MM_PER_INCH) * mi->base_resolution - 1; break; default: /* Undefined document format code */ mi->max_x = mi->max_y = 0; DBG(15, "parse_inquiry: Unknown doc_size_code! 0x%x\n", mi->doc_size_code); } /* create the proper range constraints, given the doc size */ { /* we need base resolution in dots-per-millimeter... */ float base_res_dpmm = (float) mi->base_resolution / MM_PER_INCH; mi->doc_x_range.min = SANE_FIX(0); mi->doc_x_range.max = SANE_FIX((float)mi->max_x / base_res_dpmm); mi->doc_x_range.quant = SANE_FIX(0); mi->doc_y_range.min = SANE_FIX(0); mi->doc_y_range.max = SANE_FIX((float)mi->max_y / base_res_dpmm); mi->doc_y_range.quant = SANE_FIX(0); } mi->source_options = (SANE_Byte)(result[63]); mi->expanded_resolution = (result[64] & 0x01); /* my E6 reports exp-res capability incorrectly */ if ((mi->model_code == 0x66) || (mi->model_code == 0x63)) { mi->expanded_resolution = 0xFF; DBG(4, "parse_inquiry: E6 falsely denies expanded resolution.\n"); } /* the StudioScan II(si) does the expanded-mode aspect correction within the scanner... (do others too?) */ if (mi->model_code == 0x5b) { DBG(4, "parse_inquiry: does expanded-mode expansion internally.\n"); mi->does_expansion = 1; } else mi->does_expansion = 0; mi->enhance_cap = (result[65] & 0x03); /* switch (result[66] & 0x0F) { case 0x00: mi->max_lookup_size = 0; break; case 0x01: mi->max_lookup_size = 256; break; case 0x03: mi->max_lookup_size = 1024; break; case 0x05: mi->max_lookup_size = 4096; break; case 0x09: mi->max_lookup_size = 65536; break; default: mi->max_lookup_size = 0; DBG(15, "parse_inquiry: Unknown gamma LUT size! 0x%x\n", result[66]); } */ /* This is not how the vague documentation specifies this register. We're going to take it literally here -- i.e. if the bit is set, the scanner supports the value, otherwise it doesn't. (The docs say all lower values are always supported. This is not the case for the StudioScan IIsi, at least, which only specifies 0x02==1024-byte table, and only supports that, too.) All-in-all, it doesn't matter, since we take the largest allowed LUT size anyway. */ if (result[66] & 0x08) mi->max_lookup_size = 65536; else if (result[66] & 0x04) mi->max_lookup_size = 4096; else if (result[66] & 0x02) mi->max_lookup_size = 1024; else if (result[66] & 0x01) mi->max_lookup_size = 256; else mi->max_lookup_size = 0; /* my E6 reports incorrectly */ if ((mi->model_code == 0x66) || (mi->model_code == 0x63)) { mi->max_lookup_size = 1024; DBG(4, "parse_inquiry: E6 falsely denies 1024-byte LUT.\n"); } /* switch (result[66] >> 5) { case 0x00: mi->max_gamma_val = 255; mi->gamma_size = 1; break; case 0x01: mi->max_gamma_val = 1023; mi->gamma_size = 2; break; case 0x02: mi->max_gamma_val = 4095; mi->gamma_size = 2; break; case 0x03: mi->max_gamma_val = 65535; mi->gamma_size = 2; break; default: mi->max_gamma_val = 0; mi->gamma_size = 0; DBG(15, "parse_inquiry: Unknown gamma max val! 0x%x\n", result[66]); } */ switch (result[66] >> 5) { case 0x00: mi->max_gamma_bit_depth = 8; mi->gamma_size = 1; break; case 0x01: mi->max_gamma_bit_depth = 10; mi->gamma_size = 2; break; case 0x02: mi->max_gamma_bit_depth = 12; mi->gamma_size = 2; break; case 0x03: mi->max_gamma_bit_depth = 16; mi->gamma_size = 2; break; default: mi->max_gamma_bit_depth = 0; mi->gamma_size = 0; DBG(15, "parse_inquiry: Unknown gamma max val! 0x%x\n", result[66]); } mi->fast_color_preview = (SANE_Byte)(result[67] & 0x01); mi->xfer_format_select = (SANE_Byte)(result[68] & 0x01); mi->color_sequence = (SANE_Byte)(result[69] & 0x7f); mi->does_3pass = (SANE_Byte)(!(result[69] & 0x80)); mi->does_mode1 = (SANE_Byte)(result[71] & 0x01); mi->bit_formats = (SANE_Byte)(result[74] & 0x0F); mi->extra_cap = (SANE_Byte)(result[75] & 0x07); /* XXXXXX a quick hack to disable any [pre/real]cal stuff for anything but an E6... */ if (!((mi->model_code == 0x66) || (mi->model_code == 0x63))) { mi->extra_cap &= ~MI_EXCAP_DIS_RECAL; DBG(4, "parse_inquiry: Not an E6 -- pretend recal cannot be disabled.\n"); } /* The E2 lies... */ if (mi->model_code == 0x64) { DBG(4, "parse_inquiry: The E2 lies about it's 3-pass heritage.\n"); mi->does_3pass = 1; mi->modes &= ~MI_MODES_ONEPASS; } return SANE_STATUS_GOOD; } /********************************************************************/ /* Dump all we know about scanner to stderr */ /********************************************************************/ static SANE_Status dump_inquiry(Microtek_Info *mi, unsigned char *result) { int i; DBG(15, "dump_inquiry...\n"); DBG(1, " === SANE/Microtek backend v%d.%d.%d ===\n", MICROTEK_MAJOR, MICROTEK_MINOR, MICROTEK_PATCH); DBG(1, "========== Scanner Inquiry Block ========mm\n"); for (i=0; i<96; ) { if (!(i % 16)) MDBG_INIT(""); MDBG_ADD("%02x ", (int)result[i++]); if (!(i % 16)) MDBG_FINISH(1); } DBG(1, "========== Scanner Inquiry Report ==========\n"); DBG(1, "===== Scanner ID...\n"); DBG(1, "Device Type Code: 0x%02x\n", mi->device_type); DBG(1, "Model Code: 0x%02x\n", mi->model_code); DBG(1, "Vendor Name: '%s' Model Name: '%s'\n", mi->vendor_id, mi->model_name); DBG(1, "Vendor Specific String: '%s'\n", mi->vendor_string); DBG(1, "Firmware Rev: '%s'\n", mi->revision_num); DBG(1, "SCSI F/W version: %1d.%1d Scanner F/W version: %1d.%1d\n", mi->SCSI_firmware_ver_major, mi->SCSI_firmware_ver_minor, mi->scanner_firmware_ver_major, mi->scanner_firmware_ver_minor); DBG(1, "Response data format: 0x%02x\n", mi->response_data_format); DBG(1, "===== Imaging Capabilities...\n"); DBG(1, "Modes: %s%s%s%s%s%s%s\n", (mi->modes & MI_MODES_LINEART) ? "Lineart " : "", (mi->modes & MI_MODES_HALFTONE) ? "Halftone " : "", (mi->modes & MI_MODES_GRAY) ? "Gray " : "", (mi->modes & MI_MODES_COLOR) ? "Color " : "", (mi->modes & MI_MODES_TRANSMSV) ? "(X-msv) " : "", (mi->modes & MI_MODES_ONEPASS) ? "(OnePass) " : "", (mi->modes & MI_MODES_NEGATIVE) ? "(Negative) " : ""); DBG(1, "Resolution Step Sizes: %s%s Expanded Resolution Support? %s%s\n", (mi->res_step & MI_RESSTEP_1PER) ? "1% " : "", (mi->res_step & MI_RESSTEP_5PER) ? "5%" : "", (mi->expanded_resolution) ? "yes" : "no", (mi->expanded_resolution == 0xFF) ? "(but says no)" : ""); DBG(1, "Supported Bits Per Sample: %s8 %s%s%s\n", (mi->bit_formats & MI_FMT_CAP_4BPP) ? "4 " : "", (mi->bit_formats & MI_FMT_CAP_10BPP) ? "10 " : "", (mi->bit_formats & MI_FMT_CAP_12BPP) ? "12 " : "", (mi->bit_formats & MI_FMT_CAP_16BPP) ? "16 " : ""); DBG(1, "Max. document size code: 0x%02x\n", mi->doc_size_code); DBG(1, "Max. document size: %d x %d pixels\n", mi->max_x, mi->max_y); DBG(1, "Frame units: %s%s\n", (mi->unit_type & MI_UNIT_PIXELS) ? "pixels " : "", (mi->unit_type & MI_UNIT_8TH_INCH) ? "1/8\"'s " : ""); DBG(1, "# of built-in halftones: %d Downloadable patterns? %s\n", mi->pattern_count, (mi->pattern_dwnld) ? "Yes" : "No"); DBG(1, "Data Compression: %s%s\n", (mi->compress_type & MI_COMPRSS_HUFF) ? "huffman " : "", (mi->compress_type & MI_COMPRSS_RD) ? "read-data " : ""); DBG(1, "Contrast Settings: %d Exposure Settings: %d\n", mi->cont_settings, mi->exp_settings); DBG(1, "Adjustable Shadow/Highlight? %s Adjustable Midtone? %s\n", (mi->enhance_cap & MI_ENH_CAP_SHADOW) ? "yes" : "no ", (mi->enhance_cap & MI_ENH_CAP_MIDTONE) ? "yes" : "no "); DBG(1, "Digital brightness/offset? %s\n", (mi->extra_cap & MI_EXCAP_OFF_CTL) ? "yes" : "no"); /* fprintf(stderr, "Gamma Table Size: %d entries of %d bytes (max. value: %d)\n", mi->max_lookup_size, mi->gamma_size, mi->max_gamma_val); */ DBG(1, "Gamma Table Size: %d entries of %d bytes (max. depth: %d)\n", mi->max_lookup_size, mi->gamma_size, mi->max_gamma_bit_depth); DBG(1, "===== Source Options...\n"); DBG(1, "Feed type: %s%s ADF support? %s\n", (mi->feed_type & MI_FEED_FLATBED) ? "flatbed " : "", (mi->feed_type & MI_FEED_EDGEFEED) ? "edge-feed " : "", (mi->feed_type & MI_FEED_AUTOSUPP) ? "yes" : "no"); DBG(1, "Document Feeder Support? %s Feeder Backtracking? %s\n", (mi->source_options & MI_SRC_FEED_SUPP) ? "yes" : "no ", (mi->source_options & MI_SRC_FEED_BT) ? "yes" : "no "); DBG(1, "Feeder Installed? %s Feeder Ready? %s\n", (mi->source_options & MI_SRC_HAS_FEED) ? "yes" : "no ", (mi->source_options & MI_SRC_FEED_RDY) ? "yes" : "no "); DBG(1, "Transparency Adapter Installed? %s\n", (mi->source_options & MI_SRC_HAS_TRANS) ? "yes" : "no "); /* GET_TRANS GET_FEED XXXXXXXXX */ /* mt_SWslct ???? XXXXXXXXXXX */ /*#define DOC_ON_FLATBED 0x00 #define DOC_IN_FEEDER 0x01 #define TRANSPARENCY 0x10 */ DBG(1, "Fast Color Prescan? %s\n", (mi->fast_color_preview) ? "yes" : "no"); DBG(1, "Selectable Transfer Format? %s\n", (mi->xfer_format_select) ? "yes" : "no"); MDBG_INIT("Color Transfer Sequence: "); switch (mi->color_sequence) { case MI_COLSEQ_PLANE: MDBG_ADD("plane-by-plane (3-pass)"); break; case MI_COLSEQ_PIXEL: MDBG_ADD("pixel-by-pixel RGB"); break; case MI_COLSEQ_RGB: MDBG_ADD("line-by-line, R-G-B sequence"); break; case MI_COLSEQ_NONRGB: MDBG_ADD("line-by-line, non-sequential with headers"); break; case MI_COLSEQ_2PIXEL: MDBG_ADD("2pixel-by-2pixel RRGGBB"); break; default: MDBG_ADD("UNKNOWN CODE (0x%02x)", mi->color_sequence); } MDBG_FINISH(1); /* if (mi->modes & MI_MODES_ONEPASS) XXXXXXXXXXX */ DBG(1, "Three pass scan support? %s\n", (mi->does_3pass ? "yes" : "no")); DBG(1, "ModeSelect-1 and ModeSense-1 Support? %s\n", (mi->does_mode1) ? "yes" : "no"); DBG(1, "Can Disable Linearization Table? %s\n", (mi->extra_cap & MI_EXCAP_DIS_LNTBL) ? "yes" : "no"); DBG(1, "Can Disable Start-of-Scan Recalibration? %s\n", (mi->extra_cap & MI_EXCAP_DIS_RECAL) ? "yes" : "no"); DBG(1, "Internal expanded expansion? %s\n", mi->does_expansion ? "yes" : "no"); /* fprintf(stderr, "cntr_vals = %d, min_cntr = %d, max_cntr = %d\n", cntr_vals, min_cntr, max_cntr); fprintf(stderr, "exp_vals = %d, min_exp = %d, max_exp = %d\n", exp_vals, min_exp, max_exp); */ DBG(1, "====== End of Scanner Inquiry Report =======\n"); return SANE_STATUS_GOOD; } /********************************************************************/ /* Dump all we know about some unknown scanner to stderr */ /********************************************************************/ static SANE_Status dump_suspect_inquiry(unsigned char *result) { int i; char vendor_id[9], model_name[17], revision_num[5]; SANE_Byte device_type, model_code; SANE_Byte SCSI_firmware_ver_major, SCSI_firmware_ver_minor; SANE_Byte scanner_firmware_ver_major, scanner_firmware_ver_minor; SANE_Byte response_data_format; DBG(15, "dump_suspect_inquiry...\n"); DBG(1, " === SANE/Microtek backend v%d.%d.%d ===\n", MICROTEK_MAJOR, MICROTEK_MINOR, MICROTEK_PATCH); DBG(1, "========== Scanner Inquiry Block ========mm\n"); for (i=0; i<96; ) { if (!(i % 16)) MDBG_INIT(""); MDBG_ADD("%02x ", (int)result[i++]); if (!(i % 16)) MDBG_FINISH(1); } #if 0 for (i=0; i<96; i++) { if (!(i % 16) && (i)) fprintf(stderr, "\n"); fprintf(stderr, "%02x ", (int)result[i]); } fprintf(stderr, "\n\n"); #endif snprintf(vendor_id, 8 + 1, "%.*s", 8, (char *)&result[8]); snprintf(model_name, 16 + 1, "%.*s", 16, (char *)&result[16]); snprintf(revision_num, 4 + 1, "%.*s", 4, (char *)&result[32]); device_type = (SANE_Byte)(result[0] & 0x1f); SCSI_firmware_ver_major = (SANE_Byte)((result[1] & 0xf0) >> 4); SCSI_firmware_ver_minor = (SANE_Byte)(result[1] & 0x0f); scanner_firmware_ver_major = (SANE_Byte)((result[2] & 0xf0) >> 4); scanner_firmware_ver_minor = (SANE_Byte)(result[2] & 0x0f); response_data_format = (SANE_Byte)(result[3]); model_code = (SANE_Byte)(result[62]); DBG(1, "========== Scanner Inquiry Report ==========\n"); DBG(1, "===== Scanner ID...\n"); DBG(1, "Device Type Code: 0x%02x\n", device_type); DBG(1, "Model Code: 0x%02x\n", model_code); DBG(1, "Vendor Name: '%s' Model Name: '%s'\n", vendor_id, model_name); DBG(1, "Firmware Rev: '%s'\n", revision_num); DBG(1, "SCSI F/W version: %1d.%1d Scanner F/W version: %1d.%1d\n", SCSI_firmware_ver_major, SCSI_firmware_ver_minor, scanner_firmware_ver_major, scanner_firmware_ver_minor); DBG(1, "Response data format: 0x%02x\n", response_data_format); DBG(1, "====== End of Scanner Inquiry Report =======\n"); return SANE_STATUS_GOOD; } /********************************************************************/ /* Determine if device is a Microtek Scanner (from INQUIRY info) */ /********************************************************************/ static SANE_Status id_microtek(uint8_t *result, char **model_string) { SANE_Byte device_type, response_data_format; int forcewarn = 0; DBG(15, "id_microtek...\n"); /* check device type first... */ device_type = (SANE_Byte)(result[0] & 0x1f); if (device_type != 0x06) { DBG(15, "id_microtek: not even a scanner: dev_type = %d\n", device_type); return SANE_STATUS_INVAL; } if (!(strncmp("MICROTEK", (char *)&(result[8]), 8)) || !(strncmp("MII SC31", (char *)&(result[8]), 8)) || /* the IISP */ !(strncmp("MII SC21", (char *)&(result[8]), 8)) || /* the 600ZS */ !(strncmp("MII SC23", (char *)&(result[8]), 8)) || /* the other 600ZS */ !(strncmp("MII SC25", (char *)&(result[8]), 8)) || /* some other 600GS */ !(strncmp("AGFA ", (char *)&(result[8]), 8)) || /* Arcus II */ !(strncmp("Microtek", (char *)&(result[8]), 8)) || /* some 35t+'s */ !(strncmp("Polaroid", (char *)&(result[8]), 8)) || /* SprintScan 35LE */ !(strncmp(" ", (char *)&(result[8]), 8)) ) { switch (result[62]) { case 0x16 : *model_string = "ScanMaker 600ZS"; break; case 0x50 : *model_string = "ScanMaker II/IIXE"; break; case 0x51 : *model_string = "ScanMaker 45t"; break; case 0x52 : *model_string = "ScanMaker 35t"; break; case 0x54 : *model_string = "ScanMaker IISP"; break; case 0x55 : *model_string = "ScanMaker IIER"; break; case 0x56 : *model_string = "ScanMaker A3t"; break; case 0x57 : *model_string = "ScanMaker IIHR"; break; case 0x58 : *model_string = "ScanMaker IIG"; break; case 0x59 : *model_string = "ScanMaker III"; break; case 0x5A : *model_string = "Agfa StudioScan"; break; case 0x5B : *model_string = "Agfa StudioScan II"; break; case 0x5C : *model_string = "Agfa Arcus II"; break; case 0x5f : *model_string = "ScanMaker E3"; break; case 0x62 : if (!(strncmp("Polaroid", (char *)&(result[8]), 8))) *model_string = "Polaroid SprintScan 35/LE"; else *model_string = "ScanMaker 35t+"; break; case 0x63 : case 0x66 : *model_string = "ScanMaker E6"; break; case 0x64 : /* and "Vobis RealScan" */ *model_string = "ScanMaker E2"; break; case 0x65: *model_string = "Color PageWiz"; break; case 0xC8: *model_string = "ScanMaker 600ZS"; break; /* the follow are listed in the docs, but are otherwise a mystery... */ case 0x5D: *model_string = "Agfa DuoScan"; forcewarn = 1; break; case 0x5E: *model_string = "SS3"; forcewarn = 1; break; case 0x60: *model_string = "HR1"; forcewarn = 1; break; case 0x61: *model_string = "45T+"; forcewarn = 1; break; case 0x67: *model_string = "TR3"; forcewarn = 1; break; default : /* this might be a newer scanner, which uses the SCSI II command set. */ /* that's unfortunate, but we'll warn the user anyway.... */ response_data_format = (SANE_Byte)(result[3]); if (response_data_format == 0x02) { DBG(15, "id_microtek: (uses new SCSI II command set)\n"); if (DBG_LEVEL >= 15) { DBG(1, "\n"); DBG(1, "\n"); DBG(1, "\n"); DBG(1, "========== Congratulations! ==========\n"); DBG(1, "You appear to be the proud owner of a \n"); DBG(1, "brand-new Microtek scanner, which uses\n"); DBG(1, "a new SCSI II command set. \n"); DBG(1, "\n"); DBG(1, "Try the `microtek2' backend instead. \n"); DBG(1, "\n"); DBG(1, "\n"); DBG(1, "\n"); } } return SANE_STATUS_INVAL; } if (forcewarn) { /* force debugging on, to encourage user to send in a report */ #ifndef NDEBUG DBG_LEVEL = 1; #endif DBG(1, "\n"); DBG(1, "\n"); DBG(1, "\n"); DBG(1, "========== Congratulations! ==========\n"); DBG(1, "Your scanner appears to be supported \n"); DBG(1, "by the microtek backend. However, it \n"); DBG(1, "has never been tried before, and some \n"); DBG(1, "parameters are bound to be wrong. \n"); DBG(1, "\n"); DBG(1, "Please send the scanner inquiry log in\n"); DBG(1, "its entirety to mtek-bugs@mir.com and \n"); DBG(1, "include a description of the scanner, \n"); DBG(1, "including the base optical resolution.\n"); DBG(1, "\n"); DBG(1, "You'll find complete instructions for \n"); DBG(1, "submitting an error/debug log in the \n"); DBG(1, "'sane-microtek' man-page. \n"); DBG(1, "\n"); DBG(1, "\n"); DBG(1, "\n"); } return SANE_STATUS_GOOD; } DBG(15, "id_microtek: not microtek: %d, %d, %d\n", strncmp("MICROTEK", (char *)&(result[8]), 8), strncmp(" ", (char *)&(result[8]), 8), result[62]); return SANE_STATUS_INVAL; } /********************************************************************/ /* Try to attach a device as a Microtek scanner */ /********************************************************************/ static SANE_Status attach_scanner(const char *devicename, Microtek_Device **devp) { Microtek_Device *dev; int sfd; size_t size; unsigned char result[0x60]; SANE_Status status; char *model_string; uint8_t inquiry[] = { 0x12, 0, 0, 0, 0x60, 0 }; DBG(15,"attach_scanner: %s\n", devicename); /* check if device is already known... */ for (dev = first_dev; dev; dev = dev->next) { if (strcmp(dev->sane.name, devicename) == 0) { if (devp) *devp = dev; return SANE_STATUS_GOOD; } } /* open scsi device... */ DBG(20, "attach_scanner: opening %s\n", devicename); if (sanei_scsi_open(devicename, &sfd, sense_handler, NULL) != 0) { DBG(20, "attach_scanner: open failed\n"); return SANE_STATUS_INVAL; } /* say hello... */ DBG(20, "attach_scanner: sending INQUIRY\n"); size = sizeof(result); status = sanei_scsi_cmd(sfd, inquiry, sizeof(inquiry), result, &size); sanei_scsi_close (sfd); if (status != SANE_STATUS_GOOD || size != 0x60) { DBG(20, "attach_scanner: inquiry failed (%s)\n", sane_strstatus (status)); return status; } if (id_microtek(result, &model_string) != SANE_STATUS_GOOD) { DBG(15, "attach_scanner: device doesn't look like a Microtek scanner."); if (DBG_LEVEL >= 5) dump_suspect_inquiry(result); return SANE_STATUS_INVAL; } dev=malloc(sizeof(*dev)); if (!dev) return SANE_STATUS_NO_MEM; memset(dev, 0, sizeof(*dev)); parse_inquiry(&(dev->info), result); if (DBG_LEVEL > 0) dump_inquiry(&(dev->info), result); /* initialize dev structure */ dev->sane.name = strdup(devicename); dev->sane.vendor = "Microtek"; dev->sane.model = strdup(model_string); dev->sane.type = "flatbed scanner"; /* link into device list... */ ++num_devices; dev->next = first_dev; first_dev = dev; if (devp) *devp = dev; DBG(15, "attach_scanner: happy.\n"); return SANE_STATUS_GOOD; } /********************************************************************/ /* Attach a scanner (convenience wrapper for find_scanners...) */ /********************************************************************/ static SANE_Status attach_one (const char *dev) { attach_scanner (dev, 0); return SANE_STATUS_GOOD; } /********************************************************************/ /* End a scan, and clean up afterwards */ /********************************************************************/ static SANE_Status end_scan(Microtek_Scanner *s, SANE_Status ostat) { SANE_Status status; DBG(15, "end_scan...\n"); if (s->scanning) { s->scanning = SANE_FALSE; /* stop the scanner */ if (s->scan_started) { status = stop_scan(s); if (status != SANE_STATUS_GOOD) DBG(23, "end_scan: OY! on stop_scan\n"); s->scan_started = SANE_FALSE; } /* close the SCSI device */ if (s->sfd != -1) { sanei_scsi_close(s->sfd); s->sfd = -1; } /* free the buffers we malloc'ed */ if (s->scsi_buffer != NULL) { free(s->scsi_buffer); s->scsi_buffer = NULL; } if (s->rb != NULL) { ring_free(s->rb); s->rb = NULL; } } /* if this -was- pass 3, or cancel, then we must be done */ if ((s->this_pass == 3) || (s->cancel)) s->this_pass = 0; return ostat; } /********************************************************************/ /********************************************************************/ /***** Scan-time operations *****/ /********************************************************************/ /********************************************************************/ /* number of lines of calibration data returned by scanner */ #define STRIPS 12 /* well, that's what it seems to be for the E6 */ /* simple comparison for the qsort below */ static int comparo(const void *a, const void *b) { return (*(const int *)a - *(const int *)b); } /* extract values from scanlines and sort */ static void sort_values(int *result, uint8_t *scanline[], int pix) { int i; for (i=0; i= bot) && (sorted[j] <= top)) { sum += sorted[j]; count++; } } if (count) caldata[i] = (sum + (count / 2)) / count; else { DBG(23, "zero: i=%d b/t=%d/%d ", i, bot, top); if (DBG_LEVEL >= 23) { MDBG_INIT(""); for (j=0; j 0; nleft -= ntoget, spot += buffsize) { ntoget = (nleft > nmax) ? nmax : nleft; buffsize = ntoget * 3 * linewidth; DBG(23, "...nleft %d toget %d size %lu spot %d input+spot %p\n", nleft, ntoget, (u_long) buffsize, spot, (void *) (input+spot)); if ((statusA = read_scan_data(s, ntoget, input+spot, &buffsize)) != SANE_STATUS_GOOD) { DBG(23, "...read scan failed\n"); break; } } status = stop_scan(s); if ((statusA != SANE_STATUS_GOOD) || (status != SANE_STATUS_GOOD)) { free(input); free(combuff); return ((statusA != SANE_STATUS_GOOD) ? statusA : status); } /* calculate calibration data for each element and download */ for (letter = 'R'; letter != 'X'; ) { DBG(23, "do_real_calibrate: working on %c\n", letter); for (spot=0, i=0; spot < linewidth * STRIPS * 3; spot += linewidth) { if (input[spot+1] == letter) { DBG(23, " found %d (at %d)\n", i, spot); if (i >= STRIPS) { DBG(23, "WHOA!!! %i have already been found!\n", i); break; } scanline[i] = &(input[spot+2]); i++; } } calc_calibration(combuff + 8, scanline, linewidth - 2); if ((status = download_calibration(s, combuff, letter, linewidth)) != SANE_STATUS_GOOD) { DBG(23, "...download_calibration failed\n"); free(input); free(combuff); return status; } switch (letter) { case 'R': letter = 'G'; break; case 'G': letter = 'B'; break; case 'B': default: letter = 'X'; break; } } /* clean up */ free(input); free(combuff); return SANE_STATUS_GOOD; } /********************************************************************/ /* Cause scanner to calibrate, but don't really scan anything */ /* (i.e. do everything but read data) */ /********************************************************************/ static SANE_Status do_precalibrate(SANE_Handle handle) { Microtek_Scanner *s = handle; SANE_Status status, statusA; SANE_Int busy, linewidth, lines; DBG(10, "do_precalibrate...\n"); if ((status = wait_ready(s)) != SANE_STATUS_GOOD) return status; { SANE_Int y1 = s->y1; SANE_Int y2 = s->y2; /* some small range, but large enough to cause the scanner to think it'll scan *something*... */ s->y1 = 0; s->y2 = (s->resolution > s->dev->info.base_resolution) ? 4 : 4 * s->dev->info.base_resolution / s->resolution; status = scanning_frame(s); s->y1 = y1; s->y2 = y2; if (status != SANE_STATUS_GOOD) return status; } if (s->dev->info.source_options & (MI_SRC_FEED_BT | MI_SRC_HAS_TRANS | MI_SRC_FEED_SUPP | MI_SRC_HAS_FEED)) { /* ZZZZZZZZZZZ */ if ((status = accessory(s)) != SANE_STATUS_GOOD) return status; } if ((status = mode_select(s)) != SANE_STATUS_GOOD) return status; /* why would we even try if this were not true?... */ /*if (s->dev->info.extra_cap & MI_EXCAP_DIS_RECAL) */ { SANE_Bool allow_calibrate = s->allow_calibrate; s->allow_calibrate = SANE_TRUE; status = mode_select_1(s); s->allow_calibrate = allow_calibrate; if (status != SANE_STATUS_GOOD) return status; } if ((status = wait_ready(s)) != SANE_STATUS_GOOD) return status; if ((status = start_scan(s)) != SANE_STATUS_GOOD) return status; if ((statusA = get_scan_status(s, &busy, &linewidth, &lines)) != SANE_STATUS_GOOD) { DBG(10, "do_precalibrate: get_scan_status fails\n"); } if ((status = stop_scan(s)) != SANE_STATUS_GOOD) return status; if ((status = wait_ready(s)) != SANE_STATUS_GOOD) return status; DBG(10, "do_precalibrate done.\n"); if (statusA != SANE_STATUS_GOOD) return statusA; else return SANE_STATUS_GOOD; } /********************************************************************/ /* Calibrate scanner, if necessary; record results */ /********************************************************************/ static SANE_Status finagle_precal(SANE_Handle handle) { Microtek_Scanner *s = handle; SANE_Status status; int match; /* try to check if scanner has been reset */ /* if so, calibrate it (either for real, or via a fake scan, with calibration */ /* (but only bother if you *could* disable calibration) */ DBG(23, "finagle_precal...\n"); if ((s->do_clever_precal) || (s->do_real_calib)) { if ((status = compare_mode_sense(s, &match)) != SANE_STATUS_GOOD) return status; if (((s->do_real_calib) && (!s->calib_once)) || /* user want recal */ (!match) || /* or, possible reset */ ((s->mode == MS_MODE_COLOR) && /* or, other weirdness */ (s->precal_record < MS_PRECAL_COLOR)) || ((s->mode == MS_MODE_COLOR) && (s->expandedresolution) && (s->precal_record < MS_PRECAL_EXP_COLOR))) { DBG(23, "finagle_precal: must precalibrate!\n"); s->precal_record = MS_PRECAL_NONE; if (s->do_real_calib) { /* do a real calibration if allowed */ if ((status = do_real_calibrate(s)) != SANE_STATUS_GOOD) return status; } else if (s->do_clever_precal) {/* otherwise do the fake-scan version */ if ((status = do_precalibrate(s)) != SANE_STATUS_GOOD) return status; } if (s->mode == MS_MODE_COLOR) { if (s->expandedresolution) s->precal_record = MS_PRECAL_EXP_COLOR; else s->precal_record = MS_PRECAL_COLOR; } else s->precal_record = MS_PRECAL_GRAY; } else DBG(23, "finagle_precal: no precalibrate necessary.\n"); } return SANE_STATUS_GOOD; } /********************************************************************/ /* Set pass-dependent parameters (for 3-pass color scans) */ /********************************************************************/ static void set_pass_parameters (SANE_Handle handle) { Microtek_Scanner *s = handle; if (s->threepasscolor) { s->this_pass += 1; DBG(23, "set_pass_parameters: three-pass, on %d\n", s->this_pass); switch (s->this_pass) { case 1: s->filter = MS_FILT_RED; s->params.format = SANE_FRAME_RED; s->params.last_frame = SANE_FALSE; break; case 2: s->filter = MS_FILT_GREEN; s->params.format = SANE_FRAME_GREEN; s->params.last_frame = SANE_FALSE; break; case 3: s->filter = MS_FILT_BLUE; s->params.format = SANE_FRAME_BLUE; s->params.last_frame = SANE_TRUE; break; default: s->filter = MS_FILT_CLEAR; DBG(23, "set_pass_parameters: What?!? pass %d = filter?\n", s->this_pass); break; } } else s->this_pass = 0; } /********************************************************************/ /********************************************************************/ /***** Packing functions *****/ /***** ...process raw scanner bytes, and shove into *****/ /***** the ring buffer *****/ /********************************************************************/ /********************************************************************/ /********************************************************************/ /* Process flat (byte-by-byte) data */ /********************************************************************/ static SANE_Status pack_flat_data(Microtek_Scanner *s, size_t nlines) { SANE_Status status; ring_buffer *rb = s->rb; size_t nbytes = nlines * rb->bpl; size_t start = (rb->head_complete + rb->complete_count) % rb->size; size_t max_xfer = (start < rb->head_complete) ? (rb->head_complete - start) : (rb->size - start + rb->head_complete); size_t length = MIN(nbytes, max_xfer); if (nbytes > max_xfer) { DBG(23, "pack_flat: must expand ring, %lu + %lu\n", (u_long)rb->size, (u_long)(nbytes - max_xfer)); status = ring_expand(rb, (nbytes - max_xfer)); if (status != SANE_STATUS_GOOD) return status; } if (s->doexpansion) { unsigned int line, bit; SANE_Byte *sb, *db, byte; size_t pos; sb = s->scsi_buffer; db = rb->base; pos = start; if (!(s->multibit)) { for (line=0; lineexp_aspect, n1 = 0.0, n2 = floor(x2); i < rb->bpl; i++) { byte = 0; for (bit=0; bit < 8; bit++, x1 = x2, n1 = n2, x2 += s->exp_aspect, n2 = floor(x2)) { /* #define getbit(byte, index) (((byte)>>(index))&1) */ byte |= (( (x2 == n2) ? (((sb[(int)n1 / 8])>>(7 - ((int)n1) % 8))&1) : (((double)(((sb[(int)n1/8])>>(7-(int)n1%8))&1) * (n2 - x1) + (double)(((sb[(int)n2/8])>>(7-(int)n2%8))&1) * (x2 - n2) ) / s->exp_aspect) ) > 0.5) << (7 - bit); } db[pos] = byte; if (++pos >= rb->size) pos = 0; } sb += s->pixel_bpl; } } else { /* multibit scan (8 is assumed!) */ for (line=0; lineexp_aspect, n1 = 0.0, n2 = floor(x2); i < s->dest_ppl; i++, x1 = x2, n1 = n2, x2 += s->exp_aspect, n2 = floor(x2)) { db[pos] = (x2 == n2) ? sb[(int)n1] : (int)(((double)sb[(int)n1] * (n2 - x1) + (double)sb[(int)n2] * (x2 - n2)) / s->exp_aspect); if (++pos >= rb->size) pos = 0; } sb += s->pixel_bpl; } } } else { /* adjust for rollover!!! */ if ((start + length) < rb->size) { memcpy(rb->base + start, s->scsi_buffer, length); } else { size_t chunk1 = rb->size - start; size_t chunk2 = length - chunk1; memcpy(rb->base + start, s->scsi_buffer, chunk1); memcpy(rb->base, s->scsi_buffer + chunk1, chunk2); } } rb->complete_count += length; return SANE_STATUS_GOOD; } /********************************************************************/ /* Process sequential R-G-B scan lines (who uses this??? ) */ /********************************************************************/ static SANE_Status pack_seqrgb_data (Microtek_Scanner *s, size_t nlines) { ring_buffer *rb = s->rb; unsigned int seg; SANE_Byte *db = rb->base; SANE_Byte *sb = s->scsi_buffer; size_t completed; size_t spot; SANE_Byte id; { size_t ar, ag, ab; /* allowed additions */ size_t dr, dg, db; /* additions which will occur */ SANE_Status status; dr = dg = db = nlines * rb->bpl; ar = rb->size - (rb->complete_count + rb->red_extra * 3); ag = rb->size - (rb->complete_count + rb->green_extra * 3); ab = rb->size - (rb->complete_count + rb->blue_extra * 3); DBG(23, "pack_seq: dr/ar: %lu/%lu dg/ag: %lu/%lu db/ab: %lu/%lu\n", (u_long)dr, (u_long)ar, (u_long)dg, (u_long)ag, (u_long)db, (u_long)ab); if ((dr > ar) || (dg > ag) || (db > ab)) { size_t increase = 0; if (dr > ar) increase = (dr - ar); if (dg > ag) increase = MAX(increase, (dg - ag)); if (db > ab) increase = MAX(increase, (db - ab)); DBG(23, "pack_seq: must expand ring, %lu + %lu\n", (u_long)rb->size, (u_long)increase); status = ring_expand(rb, increase); if (status != SANE_STATUS_GOOD) return status; } } for (seg = 0, id = 0; seg < nlines * 3; seg++, id = (id+1)%3) { switch (id) { case 0: spot = rb->tail_red; break; case 1: spot = rb->tail_green; break; case 2: spot = rb->tail_blue; break; default: DBG(18, "pack_seq: missing scanline RGB header!\n"); return SANE_STATUS_IO_ERROR; } if (s->doexpansion) { int i; double x1, x2, n1, n2; for (i = 0, x1 = 0.0, x2 = s->exp_aspect, n1 = 0.0, n2 = floor(x2); i < s->dest_ppl; i++, x1 = x2, n1 = n2, x2 += s->exp_aspect, n2 = floor(x2)) { db[spot] = (x2 == n2) ? sb[(int)n1] : (int)(((double)sb[(int)n1] * (n2 - x1) + (double)sb[(int)n2] * (x2 - n2)) / s->exp_aspect); if ((spot += 3) >= rb->size) spot -= rb->size; } sb += s->ppl; } else { size_t i; for (i=0; i < rb->ppl; i++) { db[spot] = *sb; sb++; if ((spot += 3) >= rb->size) spot -= rb->size; } } switch (id) { case 0: rb->tail_red = spot; rb->red_extra += rb->ppl; break; case 1: rb->tail_green = spot; rb->green_extra += rb->ppl; break; case 2: rb->tail_blue = spot; rb->blue_extra += rb->ppl; break; } } completed = MIN(rb->red_extra, MIN(rb->green_extra, rb->blue_extra)); rb->complete_count += completed * 3; /* 3 complete bytes per pixel! */ rb->red_extra -= completed; rb->green_extra -= completed; rb->blue_extra -= completed; DBG(18, "pack_seq: extra r: %lu g: %lu b: %lu\n", (u_long)rb->red_extra, (u_long)rb->green_extra, (u_long)rb->blue_extra); DBG(18, "pack_seq: completed: %lu complete: %lu\n", (u_long)completed, (u_long)rb->complete_count); return SANE_STATUS_GOOD; } /********************************************************************/ /* Process non-sequential R,G, and B scan-lines */ /********************************************************************/ static SANE_Status pack_goofyrgb_data(Microtek_Scanner *s, size_t nlines) { ring_buffer *rb = s->rb; unsigned int seg; /* , i;*/ SANE_Byte *db; SANE_Byte *sb = s->scsi_buffer; size_t completed; size_t spot; SANE_Byte id; /* prescan to decide if ring should be expanded */ { size_t ar, ag, ab; /* allowed additions */ size_t dr, dg, db; /* additions which will occur */ SANE_Status status; SANE_Byte *pt; for (dr = dg = db = 0, seg = 0, pt = s->scsi_buffer + 1; seg < nlines * 3; seg++, pt += s->ppl + 2) { switch (*pt) { case 'R': dr += rb->bpl; break; case 'G': dg += rb->bpl; break; case 'B': db += rb->bpl; break; } } ar = rb->size - (rb->complete_count + rb->red_extra * 3); ag = rb->size - (rb->complete_count + rb->green_extra * 3); ab = rb->size - (rb->complete_count + rb->blue_extra * 3); DBG(23, "pack_goofy: dr/ar: %lu/%lu dg/ag: %lu/%lu db/ab: %lu/%lu\n", (u_long)dr, (u_long)ar, (u_long)dg, (u_long)ag, (u_long)db, (u_long)ab); /* >, or >= ???????? */ if ((dr > ar) || (dg > ag) || (db > ab)) { size_t increase = 0; if (dr > ar) increase = (dr - ar); if (dg > ag) increase = MAX(increase, (dg - ag)); if (db > ab) increase = MAX(increase, (db - ab)); DBG(23, "pack_goofy: must expand ring, %lu + %lu\n", (u_long)rb->size, (u_long)increase); status = ring_expand(rb, increase); if (status != SANE_STATUS_GOOD) return status; } } db = rb->base; for (seg = 0; seg < nlines * 3; seg++) { sb++; /* skip first byte in line (two byte header) */ id = *sb; switch (id) { case 'R': spot = rb->tail_red; break; case 'G': spot = rb->tail_green; break; case 'B': spot = rb->tail_blue; break; default: DBG(18, "pack_goofy: missing scanline RGB header!\n"); return SANE_STATUS_IO_ERROR; } sb++; /* skip the other header byte */ if (s->doexpansion) { int i; double x1, x2, n1, n2; for (i = 0, x1 = 0.0, x2 = s->exp_aspect, n1 = 0.0, n2 = floor(x2); i < s->dest_ppl; i++, x1 = x2, n1 = n2, x2 += s->exp_aspect, n2 = floor(x2)) { db[spot] = (x2 == n2) ? sb[(int)n1] : (int)(((double)sb[(int)n1] * (n2 - x1) + (double)sb[(int)n2] * (x2 - n2)) / s->exp_aspect); if ((spot += 3) >= rb->size) spot -= rb->size; } sb += s->ppl; } else { unsigned int i; for (i=0; i < rb->ppl; i++) { db[spot] = *sb; sb++; if ((spot += 3) >= rb->size) spot -= rb->size; } } switch (id) { case 'R': rb->tail_red = spot; rb->red_extra += rb->ppl; break; case 'G': rb->tail_green = spot; rb->green_extra += rb->ppl; break; case 'B': rb->tail_blue = spot; rb->blue_extra += rb->ppl; break; } } completed = MIN(rb->red_extra, MIN(rb->green_extra, rb->blue_extra)); rb->complete_count += completed * 3; /* 3 complete bytes per pixel! */ rb->red_extra -= completed; rb->green_extra -= completed; rb->blue_extra -= completed; DBG(18, "pack_goofy: extra r: %lu g: %lu b: %lu\n", (u_long)rb->red_extra, (u_long)rb->green_extra, (u_long)rb->blue_extra); DBG(18, "pack_goofy: completed: %lu complete: %lu\n", (u_long)completed, (u_long)rb->complete_count); return SANE_STATUS_GOOD; } /********************************************************************/ /* Process R1R2-G1G2-B1B2 double pixels (AGFA StudioStar) */ /********************************************************************/ static SANE_Status pack_seq2r2g2b_data(Microtek_Scanner *s, size_t nlines) { SANE_Status status; ring_buffer *rb = s->rb; size_t nbytes = nlines * rb->bpl; size_t start = (rb->head_complete + rb->complete_count) % rb->size; size_t max_xfer = (start < rb->head_complete) ? (rb->head_complete - start) : (rb->size - start + rb->head_complete); size_t length = MIN(nbytes, max_xfer); if (nbytes > max_xfer) { DBG(23, "pack_2r2g2b: must expand ring, %lu + %lu\n", (u_long)rb->size, (u_long)(nbytes - max_xfer)); status = ring_expand(rb, (nbytes - max_xfer)); if (status != SANE_STATUS_GOOD) return status; } { unsigned int line; int p; size_t pos = start; SANE_Byte *sb = s->scsi_buffer; SANE_Byte *db = rb->base; for (line = 0; line < nlines; line++) { for (p = 0; p < s->dest_ppl; p += 2){ /* first pixel */ db[pos] = sb[0]; if (++pos >= rb->size) pos = 0; /* watch out for ringbuff end? */ db[pos] = sb[2]; if (++pos >= rb->size) pos = 0; db[pos] = sb[4]; if (++pos >= rb->size) pos = 0; /* second pixel */ db[pos] = sb[1]; if (++pos >= rb->size) pos = 0; db[pos] = sb[3]; if (++pos >= rb->size) pos = 0; db[pos] = sb[5]; if (++pos >= rb->size) pos = 0; sb += 6; } } } rb->complete_count += length; return SANE_STATUS_GOOD; } /********************************************************************/ /********************************************************************/ /***** the basic scanning chunks for sane_read() *****/ /********************************************************************/ /********************************************************************/ /********************************************************************/ /* Request bytes from scanner (and put in scsi_buffer) */ /********************************************************************/ static SANE_Status read_from_scanner (Microtek_Scanner *s, int *nlines) { SANE_Status status; SANE_Int busy, linewidth, remaining; size_t buffsize; DBG(23, "read_from_scanner...\n"); if (s->unscanned_lines > 0) { status = get_scan_status(s, &busy, &linewidth, &remaining); if (status != SANE_STATUS_GOOD) { DBG(18, "read_from_scanner: bad get_scan_status!\n"); return status; } DBG(18, "read_from_scanner: gss busy, linewidth, remaining: %d, %d, %d\n", busy, linewidth, remaining); } else { DBG(18, "read_from_scanner: no gss/no unscanned\n"); remaining = 0; } *nlines = MIN(remaining, s->max_scsi_lines); DBG(18, "sane_read: max_scsi: %d, rem: %d, nlines: %d\n", s->max_scsi_lines, remaining, *nlines); /* grab them bytes! (only if the scanner still has bytes to give...) */ if (*nlines > 0) { buffsize = *nlines * (s->pixel_bpl + s->header_bpl);/* == "* linewidth" */ status = read_scan_data(s, *nlines, s->scsi_buffer, &buffsize); if (status != SANE_STATUS_GOOD) { DBG(18, "sane_read: bad read_scan_data!\n"); return status; } s->unscanned_lines -= *nlines; DBG(18, "sane_read: buffsize: %lu, unscanned: %d\n", (u_long) buffsize, s->unscanned_lines); } return SANE_STATUS_GOOD; } /********************************************************************/ /* Process scanner bytes, and shove in ring_buffer */ /********************************************************************/ static SANE_Status pack_into_ring(Microtek_Scanner *s, int nlines) { SANE_Status status; DBG(23, "pack_into_ring...\n"); switch (s->line_format) { case MS_LNFMT_FLAT: status = pack_flat_data(s, nlines); break; case MS_LNFMT_SEQ_RGB: status = pack_seqrgb_data(s, nlines); break; case MS_LNFMT_GOOFY_RGB: status = pack_goofyrgb_data(s, nlines); break; case MS_LNFMT_SEQ_2R2G2B: status = pack_seq2r2g2b_data(s, nlines); break; default: status = SANE_STATUS_JAMMED; } return status; } /********************************************************************/ /* Pack processed image bytes into frontend destination buffer */ /********************************************************************/ static SANE_Int pack_into_dest(SANE_Byte *dest_buffer, size_t dest_length, ring_buffer *rb) { size_t ret_length = MIN(rb->complete_count, dest_length); DBG(23, "pack_into_dest...\n"); DBG(23, "pack_into_dest: rl: %lu sz: %lu hc: %lu\n", (u_long)ret_length, (u_long)rb->size, (u_long)rb->head_complete); /* adjust for rollover!!! */ if ((rb->head_complete + ret_length) < rb->size) { memcpy(dest_buffer, rb->base + rb->head_complete, ret_length); rb->head_complete += ret_length; } else { size_t chunk1 = rb->size - rb->head_complete; size_t chunk2 = ret_length - chunk1; memcpy(dest_buffer, rb->base + rb->head_complete, chunk1); memcpy(dest_buffer + chunk1, rb->base, chunk2); rb->head_complete = chunk2; } rb->complete_count -= ret_length; return ret_length; } /********************************************************************/ /********************************************************************/ /****** "Registered" SANE API Functions *****************************/ /********************************************************************/ /********************************************************************/ /********************************************************************/ /* sane_init() */ /********************************************************************/ SANE_Status sane_init(SANE_Int *version_code, SANE_Auth_Callback authorize) { char dev_name[PATH_MAX]; size_t len; FILE *fp; (void) authorize; DBG_INIT(); DBG(1, "sane_init: MICROTEK says hello! (v%d.%d.%d)\n", MICROTEK_MAJOR, MICROTEK_MINOR, MICROTEK_PATCH); /* return the SANE version we got compiled under */ if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); /* parse config file */ fp = sanei_config_open (MICROTEK_CONFIG_FILE); if (!fp) { /* default to /dev/scanner instead of insisting on config file */ DBG(1, "sane_init: missing config file '%s'\n", MICROTEK_CONFIG_FILE); attach_scanner("/dev/scanner", 0); return SANE_STATUS_GOOD; } while (sanei_config_read(dev_name, sizeof (dev_name), fp)) { DBG(23, "sane_init: config-> %s\n", dev_name); if (dev_name[0] == '#') continue; /* ignore comments */ if (!(strncmp("noprecal", dev_name, 8))) { DBG(23, "sane_init: Clever Precalibration will be forcibly disabled...\n"); inhibit_clever_precal = SANE_TRUE; continue; } if (!(strncmp("norealcal", dev_name, 9))) { DBG(23, "sane_init: Real calibration will be forcibly disabled...\n"); inhibit_real_calib = SANE_TRUE; continue; } len = strlen (dev_name); if (!len) continue; /* ignore empty lines */ sanei_config_attach_matching_devices (dev_name, attach_one); } fclose (fp); return SANE_STATUS_GOOD; } /********************************************************************/ /* sane_get_devices */ /********************************************************************/ SANE_Status sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only) { Microtek_Device *dev; int i; (void) local_only; DBG(10, "sane_get_devices\n"); /* we keep an internal copy */ if (devlist) free(devlist); /* hmm, free it if we want a new one, I guess. YYYYY*/ devlist = malloc((num_devices + 1) * sizeof(devlist[0])); if (!devlist) return SANE_STATUS_NO_MEM; for (i=0, dev=first_dev; i < num_devices; dev = dev->next) devlist[i++] = &dev->sane; devlist[i++] = 0; *device_list = devlist; return SANE_STATUS_GOOD; } /********************************************************************/ /* sane_open */ /********************************************************************/ SANE_Status sane_open(SANE_String_Const devicename, SANE_Handle *handle) { Microtek_Scanner *scanner; Microtek_Device *dev; SANE_Status status; DBG(10, "sane_open\n"); /* find device... */ DBG(23, "sane_open: find device...\n"); if (devicename[0]) { for (dev = first_dev; dev; dev = dev->next) { if (strcmp(dev->sane.name, devicename) == 0) break; } if (!dev) { /* not in list, try manually... */ status = attach_scanner(devicename, &dev); if (status != SANE_STATUS_GOOD) return status; } } else { /* no device specified, so use first */ dev = first_dev; } if (!dev) return SANE_STATUS_INVAL; /* create a scanner... */ DBG(23, "sane_open: create scanner...\n"); scanner = malloc(sizeof(*scanner)); if (!scanner) return SANE_STATUS_NO_MEM; memset(scanner, 0, sizeof(*scanner)); /* initialize scanner dependent stuff */ DBG(23, "sane_open: initialize scanner dependent stuff...\n"); /* ZZZZZZZZZZZZZZ */ scanner->unit_type = (dev->info.unit_type & MI_UNIT_PIXELS) ? MS_UNIT_PIXELS : MS_UNIT_18INCH; scanner->res_type = (dev->info.res_step & MI_RESSTEP_1PER) ? MS_RES_1PER : MS_RES_5PER; scanner->midtone_support = (dev->info.enhance_cap & MI_ENH_CAP_MIDTONE) ? SANE_TRUE : SANE_FALSE; scanner->paper_length = (scanner->unit_type == MS_UNIT_PIXELS) ? dev->info.max_y : (SANE_Int)((double)dev->info.max_y * 8.0 / (double)dev->info.base_resolution); /* (SANE_Int)(SANE_UNFIX(dev->info.max_y) * dev->info.base_resolution) : (SANE_Int)(SANE_UNFIX(dev->info.max_y) * 8); ZZZZZZZ */ scanner->bright_r = 0; scanner->bright_g = 0; scanner->bright_b = 0; /* calibration shenanigans */ if ((dev->info.extra_cap & MI_EXCAP_DIS_RECAL) && (!(inhibit_real_calib))) { DBG(23, "sane_open: Real calibration enabled.\n"); scanner->allow_calibrate = SANE_FALSE; scanner->do_real_calib = SANE_TRUE; scanner->do_clever_precal = SANE_FALSE; } else if ((dev->info.extra_cap & MI_EXCAP_DIS_RECAL) && (!(inhibit_clever_precal))) { DBG(23, "sane_open: Clever precalibration enabled.\n"); scanner->allow_calibrate = SANE_FALSE; scanner->do_real_calib = SANE_FALSE; scanner->do_clever_precal = SANE_TRUE; } else { DBG(23, "sane_open: All calibration routines disabled.\n"); scanner->allow_calibrate = SANE_TRUE; scanner->do_real_calib = SANE_FALSE; scanner->do_clever_precal = SANE_FALSE; } scanner->onepass = (dev->info.modes & MI_MODES_ONEPASS); scanner->allowbacktrack = SANE_TRUE; /* ??? XXXXXXX */ scanner->reversecolors = SANE_FALSE; scanner->fastprescan = SANE_FALSE; scanner->bits_per_color = 8; /* init gamma tables */ if (dev->info.max_lookup_size) { int j, v, max_entry; DBG(23, "sane_open: init gamma tables...\n"); scanner->gamma_entries = dev->info.max_lookup_size; scanner->gamma_entry_size = dev->info.gamma_size; scanner->gamma_bit_depth = dev->info.max_gamma_bit_depth; max_entry = (1 << scanner->gamma_bit_depth) - 1; scanner->gamma_entry_range.min = 0; scanner->gamma_entry_range.max = max_entry; scanner->gamma_entry_range.quant = 1; scanner->gray_lut = calloc(scanner->gamma_entries, sizeof(scanner->gray_lut[0])); scanner->red_lut = calloc(scanner->gamma_entries, sizeof(scanner->red_lut[0])); scanner->green_lut = calloc(scanner->gamma_entries, sizeof(scanner->green_lut[0])); scanner->blue_lut = calloc(scanner->gamma_entries, sizeof(scanner->blue_lut[0])); if ((scanner->gray_lut == NULL) || (scanner->red_lut == NULL) || (scanner->green_lut == NULL) || (scanner->blue_lut == NULL)) { DBG(23, "sane_open: unable to allocate space for %d-entry LUT's;\n", scanner->gamma_entries); DBG(23, " so, gamma tables now DISABLED.\n"); free(scanner->gray_lut); free(scanner->red_lut); free(scanner->green_lut); free(scanner->blue_lut); } for (j=0; jgamma_entries; j += scanner->gamma_entry_size) { v = (SANE_Int) ((double) j * (double) max_entry / ((double) scanner->gamma_entries - 1.0) + 0.5); scanner->gray_lut[j] = v; scanner->red_lut[j] = v; scanner->green_lut[j] = v; scanner->blue_lut[j] = v; } } else { DBG(23, "sane_open: NO gamma tables. (max size = %lu)\n", (u_long)dev->info.max_lookup_size); scanner->gamma_entries = 0; scanner->gray_lut = NULL; scanner->red_lut = NULL; scanner->green_lut = NULL; scanner->blue_lut = NULL; } DBG(23, "sane_open: init pass-time variables...\n"); scanner->scanning = SANE_FALSE; scanner->this_pass = 0; scanner->sfd = -1; scanner->dev = dev; scanner->sense_flags = 0; scanner->scan_started = SANE_FALSE; scanner->woe = SANE_FALSE; scanner->cancel = SANE_FALSE; DBG(23, "sane_open: init clever cache...\n"); /* clear out that clever cache, so it doesn't match anything */ { int j; for (j=0; j<10; j++) scanner->mode_sense_cache[j] = 0; scanner->precal_record = MS_PRECAL_NONE; } DBG(23, "sane_open: initialize options: \n"); if ((status = init_options(scanner)) != SANE_STATUS_GOOD) return status; scanner->next = first_handle; first_handle = scanner; *handle = scanner; return SANE_STATUS_GOOD; } /********************************************************************/ /* sane_close */ /********************************************************************/ void sane_close (SANE_Handle handle) { Microtek_Scanner *ms = handle; DBG(10, "sane_close...\n"); /* free malloc'ed stuff (strdup counts too!) */ free((void *) ms->sod[OPT_MODE].constraint.string_list); free((void *) ms->sod[OPT_SOURCE].constraint.string_list); free(ms->val[OPT_MODE].s); free(ms->val[OPT_HALFTONE_PATTERN].s); free(ms->val[OPT_SOURCE].s); free(ms->val[OPT_CUSTOM_GAMMA].s); free(ms->gray_lut); free(ms->red_lut); free(ms->green_lut); free(ms->blue_lut); /* remove Scanner from linked list */ if (first_handle == ms) first_handle = ms->next; else { Microtek_Scanner *ts = first_handle; while ((ts != NULL) && (ts->next != ms)) ts = ts->next; ts->next = ts->next->next; /* == ms->next */ } /* finally, say goodbye to the Scanner */ free(ms); } /********************************************************************/ /* sane_get_option_descriptor */ /********************************************************************/ const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { Microtek_Scanner *scanner = handle; DBG(96, "sane_get_option_descriptor (%d)...\n", option); if ((unsigned)option >= NUM_OPTIONS) return NULL; return &(scanner->sod[option]); } /********************************************************************/ /* sane_control_option */ /********************************************************************/ SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int *info) { Microtek_Scanner *scanner = handle; SANE_Option_Descriptor *sod; Option_Value *val; SANE_Status status; DBG(96, "sane_control_option (opt=%d,act=%d,val=%p,info=%p)\n", option, action, value, (void*) info); sod = scanner->sod; val = scanner->val; /* no changes while in mid-pass! */ if (scanner->scanning) return SANE_STATUS_DEVICE_BUSY; /* and... no changes while in middle of three-pass series! */ if (scanner->this_pass != 0) return SANE_STATUS_DEVICE_BUSY; if ( ((option >= NUM_OPTIONS) || (option < 0)) || (!SANE_OPTION_IS_ACTIVE(scanner->sod[option].cap)) ) return SANE_STATUS_INVAL; if (info) *info = 0; /* choose by action */ switch (action) { case SANE_ACTION_GET_VALUE: switch (option) { /* word options... */ case OPT_RESOLUTION: case OPT_SPEED: case OPT_BACKTRACK: case OPT_NEGATIVE: case OPT_PREVIEW: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_EXPOSURE: case OPT_BRIGHTNESS: case OPT_CONTRAST: case OPT_HIGHLIGHT: case OPT_SHADOW: case OPT_MIDTONE: case OPT_GAMMA_BIND: case OPT_ANALOG_GAMMA: case OPT_ANALOG_GAMMA_R: case OPT_ANALOG_GAMMA_G: case OPT_ANALOG_GAMMA_B: case OPT_EXP_RES: case OPT_CALIB_ONCE: *(SANE_Word *)value = val[option].w; return SANE_STATUS_GOOD; /* word-array options... */ /* case OPT_HALFTONE_PATTERN:*/ case OPT_GAMMA_VECTOR: case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: memcpy(value, val[option].wa, sod[option].size); return SANE_STATUS_GOOD; /* string options... */ case OPT_MODE: case OPT_HALFTONE_PATTERN: case OPT_CUSTOM_GAMMA: case OPT_SOURCE: strcpy(value, val[option].s); return SANE_STATUS_GOOD; /* others.... */ case OPT_NUM_OPTS: *(SANE_Word *) value = NUM_OPTIONS; return SANE_STATUS_GOOD; default: return SANE_STATUS_INVAL; } break; case SANE_ACTION_SET_VALUE: { status = sanei_constrain_value(sod + option, value, info); if (status != SANE_STATUS_GOOD) return status; switch (option) { /* set word options... */ case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_RESOLUTION: if (info) *info |= SANE_INFO_RELOAD_PARAMS; // fall through case OPT_SPEED: case OPT_PREVIEW: case OPT_BACKTRACK: case OPT_NEGATIVE: case OPT_EXPOSURE: case OPT_BRIGHTNESS: case OPT_CONTRAST: case OPT_ANALOG_GAMMA: case OPT_ANALOG_GAMMA_R: case OPT_ANALOG_GAMMA_G: case OPT_ANALOG_GAMMA_B: val[option].w = *(SANE_Word *)value; return SANE_STATUS_GOOD; case OPT_HIGHLIGHT: case OPT_SHADOW: case OPT_MIDTONE: val[option].w = *(SANE_Word *)value; /* we need to (silently) make sure shadow <= midtone <= highlight */ if (scanner->midtone_support) { if (val[OPT_SHADOW].w > val[OPT_MIDTONE].w) { if (option == OPT_SHADOW) val[OPT_SHADOW].w = val[OPT_MIDTONE].w; else val[OPT_MIDTONE].w = val[OPT_SHADOW].w; } if (val[OPT_HIGHLIGHT].w < val[OPT_MIDTONE].w) { if (option == OPT_HIGHLIGHT) val[OPT_HIGHLIGHT].w = val[OPT_MIDTONE].w; else val[OPT_MIDTONE].w = val[OPT_HIGHLIGHT].w; } } else { if (val[OPT_SHADOW].w > val[OPT_HIGHLIGHT].w) { if (option == OPT_SHADOW) val[OPT_SHADOW].w = val[OPT_HIGHLIGHT].w; else val[OPT_HIGHLIGHT].w = val[OPT_SHADOW].w; } } return SANE_STATUS_GOOD; case OPT_EXP_RES: if (val[option].w != *(SANE_Word *) value) { val[option].w = *(SANE_Word *)value; if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; if (val[OPT_EXP_RES].w) { sod[OPT_RESOLUTION].constraint.range = &(scanner->exp_res_range); val[OPT_RESOLUTION].w *= 2; } else { sod[OPT_RESOLUTION].constraint.range = &(scanner->res_range); val[OPT_RESOLUTION].w /= 2; } } return SANE_STATUS_GOOD; case OPT_CALIB_ONCE: val[option].w = *(SANE_Word *)value; /* toggling off and on should force a recalibration... */ if (!(val[option].w)) scanner->precal_record = MS_PRECAL_NONE; return SANE_STATUS_GOOD; case OPT_GAMMA_BIND: case OPT_CUSTOM_GAMMA: if (option == OPT_GAMMA_BIND) { if (val[option].w != *(SANE_Word *) value) if (info) *info |= SANE_INFO_RELOAD_OPTIONS; val[option].w = *(SANE_Word *) value; } else if (option == OPT_CUSTOM_GAMMA) { if (val[option].s) { if (strcmp(value, val[option].s)) if (info) *info |= SANE_INFO_RELOAD_OPTIONS; free(val[option].s); } val[option].s = strdup(value); } if ( !(strcmp(val[OPT_CUSTOM_GAMMA].s, M_NONE)) || !(strcmp(val[OPT_CUSTOM_GAMMA].s, M_SCALAR)) ) { sod[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; } if ( !(strcmp(val[OPT_CUSTOM_GAMMA].s, M_NONE)) || !(strcmp(val[OPT_CUSTOM_GAMMA].s, M_TABLE)) ) { sod[OPT_ANALOG_GAMMA].cap |= SANE_CAP_INACTIVE; sod[OPT_ANALOG_GAMMA_R].cap |= SANE_CAP_INACTIVE; sod[OPT_ANALOG_GAMMA_G].cap |= SANE_CAP_INACTIVE; sod[OPT_ANALOG_GAMMA_B].cap |= SANE_CAP_INACTIVE; } if (!(strcmp(val[OPT_CUSTOM_GAMMA].s, M_SCALAR))) { if (val[OPT_GAMMA_BIND].w == SANE_TRUE) { sod[OPT_ANALOG_GAMMA].cap &= ~SANE_CAP_INACTIVE; sod[OPT_ANALOG_GAMMA_R].cap |= SANE_CAP_INACTIVE; sod[OPT_ANALOG_GAMMA_G].cap |= SANE_CAP_INACTIVE; sod[OPT_ANALOG_GAMMA_B].cap |= SANE_CAP_INACTIVE; } else { sod[OPT_ANALOG_GAMMA].cap |= SANE_CAP_INACTIVE; sod[OPT_ANALOG_GAMMA_R].cap &= ~SANE_CAP_INACTIVE; sod[OPT_ANALOG_GAMMA_G].cap &= ~SANE_CAP_INACTIVE; sod[OPT_ANALOG_GAMMA_B].cap &= ~SANE_CAP_INACTIVE; } } if (!(strcmp(val[OPT_CUSTOM_GAMMA].s, M_TABLE))) { if (val[OPT_GAMMA_BIND].w == SANE_TRUE) { sod[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; sod[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; } else { sod[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; sod[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; sod[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; } } if (!(strcmp(val[OPT_CUSTOM_GAMMA].s, M_NONE))) sod[OPT_GAMMA_BIND].cap |= SANE_CAP_INACTIVE; else if (!(strcmp(val[OPT_MODE].s, M_COLOR))) sod[OPT_GAMMA_BIND].cap &= ~SANE_CAP_INACTIVE; return SANE_STATUS_GOOD; case OPT_MODE: if (val[option].s) { if (strcmp(val[option].s, value)) if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; free(val[option].s); } val[option].s = strdup(value); if (strcmp(val[option].s, M_HALFTONE)) { sod[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; } else { sod[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE; } if (strcmp(val[option].s, M_COLOR)) { /* not color */ /*val[OPT_GAMMA_BIND].w = SANE_TRUE;*/ DBG(23, "FLIP ma LID! bind is %d\n", val[OPT_GAMMA_BIND].w); { SANE_Bool Trueness = SANE_TRUE; SANE_Status status; status = sane_control_option(handle, OPT_GAMMA_BIND, SANE_ACTION_SET_VALUE, &Trueness, NULL); DBG(23, "stat is: %d\n", status); } DBG(23, "LID be FLIPPED! bind is %d\n", val[OPT_GAMMA_BIND].w); sod[OPT_GAMMA_BIND].cap |= SANE_CAP_INACTIVE; /* sod[OPT_FORCE_3PASS].cap |= SANE_CAP_INACTIVE;*/ } else { sod[OPT_GAMMA_BIND].cap &= ~SANE_CAP_INACTIVE; /* if (scanner->dev->info.modes & MI_MODES_ONEPASS) sod[OPT_FORCE_3PASS].cap &= ~SANE_CAP_INACTIVE;*/ } return SANE_STATUS_GOOD; case OPT_HALFTONE_PATTERN: case OPT_SOURCE: if (val[option].s) free(val[option].s); val[option].s = strdup(value); return SANE_STATUS_GOOD; case OPT_GAMMA_VECTOR: case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: memcpy(val[option].wa, value, sod[option].size); return SANE_STATUS_GOOD; default: return SANE_STATUS_INVAL; } } break; case SANE_ACTION_SET_AUTO: return SANE_STATUS_UNSUPPORTED; /* We are DUMB. */ } return SANE_STATUS_GOOD; } /********************************************************************/ /* sane_get_parameters */ /********************************************************************/ SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters *params) { Microtek_Scanner *s = handle; DBG(23, "sane_get_parameters...\n"); if (!s->scanning) { /* decipher scan mode */ if (!(strcmp(s->val[OPT_MODE].s, M_LINEART))) s->mode = MS_MODE_LINEART; else if (!(strcmp(s->val[OPT_MODE].s, M_HALFTONE))) s->mode = MS_MODE_HALFTONE; else if (!(strcmp(s->val[OPT_MODE].s, M_GRAY))) s->mode = MS_MODE_GRAY; else if (!(strcmp(s->val[OPT_MODE].s, M_COLOR))) s->mode = MS_MODE_COLOR; if (s->mode == MS_MODE_COLOR) { if (s->onepass) { /* regular one-pass */ DBG(23, "sane_get_parameters: regular 1-pass color\n"); s->threepasscolor = SANE_FALSE; s->onepasscolor = SANE_TRUE; s->color_seq = s->dev->info.color_sequence; } else { /* 3-pass scanner */ DBG(23, "sane_get_parameters: regular 3-pass color\n"); s->threepasscolor = SANE_TRUE; s->onepasscolor = SANE_FALSE; s->color_seq = s->dev->info.color_sequence; } } else { /* not color! */ DBG(23, "sane_get_parameters: non-color\n"); s->threepasscolor = SANE_FALSE; s->onepasscolor = SANE_FALSE; s->color_seq = s->dev->info.color_sequence; } s->transparency = !(strcmp(s->val[OPT_SOURCE].s, M_TRANS)); s->useADF = !(strcmp(s->val[OPT_SOURCE].s, M_AUTOFEED)); /* disallow exp. res. during preview scan XXXXXXXXXXX */ /*s->expandedresolution = (s->val[OPT_EXP_RES].w) && !(s->val[OPT_PREVIEW].w);*/ s->expandedresolution = (s->val[OPT_EXP_RES].w); s->doexpansion = (s->expandedresolution && !(s->dev->info.does_expansion)); if (s->res_type == MS_RES_1PER) { s->resolution = (SANE_Int)(SANE_UNFIX(s->val[OPT_RESOLUTION].w)); s->resolution_code = 0xFF & ((s->resolution * 100) / s->dev->info.base_resolution / (s->expandedresolution ? 2 : 1)); DBG(23, "sane_get_parameters: res_code = %d (%2x)\n", s->resolution_code, s->resolution_code); } else { DBG(23, "sane_get_parameters: 5 percent!!!\n"); /* XXXXXXXXXXXXX */ } s->calib_once = s->val[OPT_CALIB_ONCE].w; s->reversecolors = s->val[OPT_NEGATIVE].w; s->prescan = s->val[OPT_PREVIEW].w; s->exposure = (s->val[OPT_EXPOSURE].w / 3) + 7; s->contrast = (s->val[OPT_CONTRAST].w / 7) + 7; s->velocity = s->val[OPT_SPEED].w; s->shadow = s->val[OPT_SHADOW].w; s->highlight = s->val[OPT_HIGHLIGHT].w; s->midtone = s->val[OPT_MIDTONE].w; if (SANE_OPTION_IS_ACTIVE(s->sod[OPT_BRIGHTNESS].cap)) { #if 1 /* this is _not_ what the docs specify! */ if (s->val[OPT_BRIGHTNESS].w >= 0) s->bright_r = (SANE_Byte) (s->val[OPT_BRIGHTNESS].w); else s->bright_r = (SANE_Byte) (0x80 | (- s->val[OPT_BRIGHTNESS].w)); #else s->bright_r = (SANE_Byte) (s->val[OPT_BRIGHTNESS].w); #endif s->bright_g = s->bright_b = s->bright_r; DBG(23, "bright_r of %d set to 0x%0x\n", s->val[OPT_BRIGHTNESS].w, s->bright_r); } else { s->bright_r = s->bright_g = s->bright_b = 0; } /* figure out halftone pattern selection... */ if (s->mode == MS_MODE_HALFTONE) { int i = 0; while ((halftone_mode_list[i] != NULL) && (strcmp(halftone_mode_list[i], s->val[OPT_HALFTONE_PATTERN].s))) i++; s->pattern = ((i < s->dev->info.pattern_count) ? i : 0); } else s->pattern = 0; { /* need to 'round' things properly! XXXXXXXX */ SANE_Int widthpix; double dots_per_mm = s->resolution / MM_PER_INCH; double units_per_mm = (s->unit_type == MS_UNIT_18INCH) ? (8.0 / MM_PER_INCH) : /* 1/8 inches */ (s->dev->info.base_resolution / MM_PER_INCH); /* pixels */ DBG(23, "sane_get_parameters: dots_per_mm: %f\n", dots_per_mm); DBG(23, "sane_get_parameters: units_per_mm: %f\n", units_per_mm); /* calculate frame coordinates... * scanner coords are in 'units' -- pixels or 1/8" * option coords are MM */ s->x1 = (SANE_Int)(SANE_UNFIX(s->val[OPT_TL_X].w) * units_per_mm + 0.5); s->y1 = (SANE_Int)(SANE_UNFIX(s->val[OPT_TL_Y].w) * units_per_mm + 0.5); s->x2 = (SANE_Int)(SANE_UNFIX(s->val[OPT_BR_X].w) * units_per_mm + 0.5); s->y2 = (SANE_Int)(SANE_UNFIX(s->val[OPT_BR_Y].w) * units_per_mm + 0.5); /* bug out if length or width is <= zero... */ if ((s->x1 >= s->x2) || (s->y1 >= s->y2)) return SANE_STATUS_INVAL; /* these are just an estimate... (but *should* be completely accurate) * real values come from scanner after sane_start. */ if (s->unit_type == MS_UNIT_18INCH) { /* who *knows* what happens */ widthpix = (SANE_Int)((double)(s->x2 - s->x1 + 1) / 8.0 * (double)s->resolution); s->params.lines = (SANE_Int)((double)(s->y2 - s->y1 + 1) / 8.0 * (double)s->resolution); } else { /* calculate pixels per scanline returned by scanner... */ /* scanner (E6 at least) always seems to return an -even- number of -bytes- */ if (s->resolution <= s->dev->info.base_resolution) widthpix = (SANE_Int)((double)(s->x2 - s->x1 + 1) * (double)(s->resolution) / (double)(s->dev->info.base_resolution)); else widthpix = (s->x2 - s->x1 + 1); if ((s->mode == MS_MODE_LINEART) || (s->mode == MS_MODE_HALFTONE)) { DBG(23, "WIDTHPIX: before: %d", widthpix); widthpix = ((widthpix / 8) & ~0x1) * 8; DBG(23, "after: %d", widthpix); } else { widthpix = widthpix & ~0x1; } DBG(23, "WIDTHPIX: before exp: %d\n", widthpix); /* ok, now fix up expanded-mode conversions */ if (s->resolution > s->dev->info.base_resolution) widthpix = (SANE_Int) ((double)widthpix * (double)s->resolution / (double)s->dev->info.base_resolution); s->params.pixels_per_line = widthpix; s->params.lines = (SANE_Int)((double)(s->y2 - s->y1 + 1) * (double)(s->resolution) / (double)(s->dev->info.base_resolution)); } } switch (s->mode) { case MS_MODE_LINEART: case MS_MODE_HALFTONE: s->multibit = SANE_FALSE; s->params.format = SANE_FRAME_GRAY; s->params.depth = 1; s->filter = MS_FILT_CLEAR; s->params.bytes_per_line = s->params.pixels_per_line / 8; break; case MS_MODE_GRAY: s->multibit = SANE_TRUE; s->params.format = SANE_FRAME_GRAY; s->params.depth = s->bits_per_color; s->filter = MS_FILT_CLEAR; s->params.bytes_per_line = s->params.pixels_per_line; break; case MS_MODE_COLOR: s->multibit = SANE_TRUE; if (s->onepasscolor) { /* a single-pass color scan */ s->params.format = SANE_FRAME_RGB; s->params.depth = s->bits_per_color; s->filter = MS_FILT_CLEAR; s->params.bytes_per_line = s->params.pixels_per_line * 3; } else { /* a three-pass color scan */ s->params.depth = s->bits_per_color; /* this will be correctly set in sane_start */ s->params.format = SANE_FRAME_RED; s->params.bytes_per_line = s->params.pixels_per_line; } break; } DBG(23, "sane_get_parameters: lines: %d ppl: %d bpl: %d\n", s->params.lines, s->params.pixels_per_line, s->params.bytes_per_line); /* also fixed in sane_start for multi-pass scans */ s->params.last_frame = SANE_TRUE; /* ?? XXXXXXXX */ } if (params) *params = s->params; return SANE_STATUS_GOOD; } /********************************************************************/ /* sane_start */ /********************************************************************/ static SANE_Status sane_start_guts (SANE_Handle handle) { Microtek_Scanner *s = handle; SANE_Status status; SANE_Int busy, linewidth; DBG(10, "sane_start...\n"); if (s->sfd != -1) { DBG(23, "sane_start: sfd already set!\n"); return SANE_STATUS_DEVICE_BUSY; } if ((status = sane_get_parameters(s, 0)) != SANE_STATUS_GOOD) return end_scan(s, status); set_pass_parameters(s); s->scanning = SANE_TRUE; s->cancel = SANE_FALSE; status = sanei_scsi_open(s->dev->sane.name, &(s->sfd), sense_handler, &(s->sense_flags)); if (status != SANE_STATUS_GOOD) { DBG(10, "sane_start: open of %s failed: %s\n", s->dev->sane.name, sane_strstatus (status)); s->sfd = -1; return end_scan(s, status); } if ((status = wait_ready(s)) != SANE_STATUS_GOOD) return end_scan(s, status); if ((status = finagle_precal(s)) != SANE_STATUS_GOOD) return end_scan(s, status); if ((status = scanning_frame(s)) != SANE_STATUS_GOOD) return end_scan(s, status); if (s->dev->info.source_options & (MI_SRC_FEED_BT | MI_SRC_HAS_TRANS | MI_SRC_FEED_SUPP | MI_SRC_HAS_FEED)) { /* ZZZZZZZZZZZ */ if ((status = accessory(s)) != SANE_STATUS_GOOD) return end_scan(s, status); /* if SWslct ???? XXXXXXXXXXXXXXX */ } if ((status = download_gamma(s)) != SANE_STATUS_GOOD) return end_scan(s, status); if ((status = mode_select(s)) != SANE_STATUS_GOOD) return end_scan(s, status); if (s->dev->info.does_mode1) { if ((status = mode_select_1(s)) != SANE_STATUS_GOOD) return end_scan(s, status); } if ((s->do_clever_precal) || (s->do_real_calib)) { if ((status = save_mode_sense(s)) != SANE_STATUS_GOOD) return end_scan(s, status); } if ((status = wait_ready(s)) != SANE_STATUS_GOOD) return end_scan(s, status); s->scan_started = SANE_TRUE; if ((status = start_scan(s)) != SANE_STATUS_GOOD) return end_scan(s, status); if ((status = get_scan_status(s, &busy, &linewidth, &(s->unscanned_lines))) != SANE_STATUS_GOOD) { DBG(10, "sane_start: get_scan_status fails\n"); return end_scan(s, status); } /* check for a bizarre linecount */ if ((s->unscanned_lines < 0) || (s->unscanned_lines > (s->params.lines * 2 * (s->expandedresolution ? 2 : 1)))) { DBG(10, "sane_start: get_scan_status returns weird line count %d\n", s->unscanned_lines); return end_scan(s, SANE_STATUS_DEVICE_BUSY); } /* figure out image format parameters */ switch (s->mode) { case MS_MODE_LINEART: case MS_MODE_HALFTONE: s->pixel_bpl = linewidth; s->header_bpl = 0; s->ppl = linewidth * 8; s->planes = 1; s->line_format = MS_LNFMT_FLAT; break; case MS_MODE_GRAY: if (s->bits_per_color < 8) { s->pixel_bpl = linewidth; s->ppl = linewidth * (8 / s->bits_per_color); } else { s->pixel_bpl = linewidth * ((s->bits_per_color + 7) / 8); s->ppl = linewidth; } s->header_bpl = 0; s->planes = 1; s->line_format = MS_LNFMT_FLAT; break; case MS_MODE_COLOR: switch (s->color_seq) { case MI_COLSEQ_PLANE: s->pixel_bpl = linewidth * ((s->bits_per_color + 7) / 8); s->ppl = linewidth; s->header_bpl = 0; s->planes = 1; s->line_format = MS_LNFMT_FLAT; break; case MI_COLSEQ_NONRGB: s->pixel_bpl = (linewidth - 2) * 3 * ((s->bits_per_color + 7) / 8); s->ppl = linewidth - 2; s->header_bpl = 2 * 3; s->planes = 3; s->line_format = MS_LNFMT_GOOFY_RGB; break; case MI_COLSEQ_PIXEL: s->pixel_bpl = linewidth * 3 * ((s->bits_per_color + 7) / 8); s->ppl = linewidth; s->header_bpl = 0; s->planes = 3; s->line_format = MS_LNFMT_FLAT; break; case MI_COLSEQ_2PIXEL: s->pixel_bpl = linewidth * 3 * ((s->bits_per_color + 7) / 8); s->ppl = linewidth; s->header_bpl = 0; s->planes = 3; s->line_format = MS_LNFMT_SEQ_2R2G2B; break; case MI_COLSEQ_RGB: s->pixel_bpl = linewidth * 3 * ((s->bits_per_color + 7) / 8); s->ppl = linewidth; s->header_bpl = 0; s->planes = 3; s->line_format = MS_LNFMT_SEQ_RGB; break; default: DBG(10, "sane_start: Unknown color_sequence: %d\n", s->dev->info.color_sequence); return end_scan(s, SANE_STATUS_INVAL); } break; default: DBG(10, "sane_start: Unknown scan mode: %d\n", s->mode); return end_scan(s, SANE_STATUS_INVAL); } if ((s->doexpansion) && (s->resolution > s->dev->info.base_resolution)) { s->dest_ppl = (int) ((double)s->ppl * (double)s->resolution / (double)s->dev->info.base_resolution); /*+ 0.5 XXXXXX */ s->exp_aspect = (double)s->ppl / (double)s->dest_ppl; s->dest_pixel_bpl = (int) ceil((double)s->pixel_bpl / s->exp_aspect); /*s->exp_aspect = (double) s->dev->info.base_resolution / (double) s->resolution;*/ /* s->dest_pixel_bpl = s->pixel_bpl / s->exp_aspect; s->dest_ppl = s->ppl / s->exp_aspect;*/ /*s->dest_ppl = s->ppl / s->exp_aspect; s->dest_pixel_bpl = (int) ceil((double)s->dest_ppl * (double)s->pixel_bpl / (double)s->ppl);*/ } else { s->exp_aspect = 1.0; s->dest_pixel_bpl = s->pixel_bpl; s->dest_ppl = s->ppl; } s->params.lines = s->unscanned_lines; s->params.pixels_per_line = s->dest_ppl; s->params.bytes_per_line = s->dest_pixel_bpl; /* calculate maximum line capacity of SCSI buffer */ s->max_scsi_lines = SCSI_BUFF_SIZE / (s->pixel_bpl + s->header_bpl); if (s->max_scsi_lines < 1) { DBG(10, "sane_start: SCSI buffer smaller that one scan line!\n"); return end_scan(s, SANE_STATUS_NO_MEM); } s->scsi_buffer = (uint8_t *) malloc(SCSI_BUFF_SIZE * sizeof(uint8_t)); if (s->scsi_buffer == NULL) return SANE_STATUS_NO_MEM; /* what's a good initial size for this? */ s->rb = ring_alloc(s->max_scsi_lines * s->dest_pixel_bpl, s->dest_pixel_bpl, s->dest_ppl); s->undelivered_bytes = s->unscanned_lines * s->dest_pixel_bpl; DBG(23, "Scan Param:\n"); DBG(23, "pix bpl: %d hdr bpl: %d ppl: %d\n", s->pixel_bpl, s->header_bpl, s->ppl); DBG(23, "undel bytes: %d unscan lines: %d planes: %d\n", s->undelivered_bytes, s->unscanned_lines, s->planes); DBG(23, "dest bpl: %d dest ppl: %d aspect: %f\n", s->dest_pixel_bpl, s->dest_ppl, s->exp_aspect); return SANE_STATUS_GOOD; } SANE_Status sane_start (SANE_Handle handle) { Microtek_Scanner *s = handle; SANE_Status status; s->woe = SANE_TRUE; status = sane_start_guts(handle); s->woe = SANE_FALSE; return status; } /********************************************************************/ /* sane_read */ /********************************************************************/ static SANE_Status sane_read_guts (SANE_Handle handle, SANE_Byte *dest_buffer, SANE_Int dest_length, SANE_Int *ret_length) { Microtek_Scanner *s = handle; SANE_Status status; int nlines; ring_buffer *rb = s->rb; DBG(10, "sane_read...\n"); *ret_length = 0; /* default: no data */ /* we have been cancelled... */ if (s->cancel) return end_scan(s, SANE_STATUS_CANCELLED); /* we're not really scanning!... */ if (!(s->scanning)) return SANE_STATUS_INVAL; /* we are done scanning... */ if (s->undelivered_bytes <= 0) return end_scan(s, SANE_STATUS_EOF); /* get more bytes if our ring is empty... */ while (rb->complete_count == 0) { if ((status = read_from_scanner(s, &nlines)) != SANE_STATUS_GOOD) { DBG(18, "sane_read: read_from_scanner failed.\n"); return end_scan(s, status); } if ((status = pack_into_ring(s, nlines)) != SANE_STATUS_GOOD) { DBG(18, "sane_read: pack_into_ring failed.\n"); return end_scan(s, status); } } /* return some data to caller */ *ret_length = pack_into_dest(dest_buffer, dest_length, rb); s->undelivered_bytes -= *ret_length; if (s->cancel) return end_scan(s, SANE_STATUS_CANCELLED); return SANE_STATUS_GOOD; } SANE_Status sane_read (SANE_Handle handle, SANE_Byte *dest_buffer, SANE_Int dest_length, SANE_Int *ret_length) { Microtek_Scanner *s = handle; SANE_Status status; s->woe = SANE_TRUE; status = sane_read_guts(handle, dest_buffer, dest_length, ret_length); s->woe = SANE_FALSE; return status; } /********************************************************************/ /* sane_exit */ /********************************************************************/ void sane_exit (void) { Microtek_Device *next; DBG(10, "sane_exit...\n"); /* close all leftover Scanners */ /*(beware of how sane_close interacts with linked list) */ while (first_handle != NULL) sane_close(first_handle); /* free up device list */ while (first_dev != NULL) { next = first_dev->next; free((void *) first_dev->sane.name); free((void *) first_dev->sane.model); free(first_dev); first_dev = next; } /* the devlist allocated by sane_get_devices */ free(devlist); DBG(10, "sane_exit: MICROTEK says goodbye.\n"); } /********************************************************************/ /* sane_cancel */ /********************************************************************/ void sane_cancel (SANE_Handle handle) { Microtek_Scanner *ms = handle; DBG(10, "sane_cancel...\n"); ms->cancel = SANE_TRUE; if (!(ms->woe)) end_scan(ms, SANE_STATUS_CANCELLED); } /********************************************************************/ /* sane_set_io_mode */ /********************************************************************/ SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { DBG(10, "sane_set_io_mode...\n"); (void) handle; if (non_blocking) return SANE_STATUS_UNSUPPORTED; else return SANE_STATUS_GOOD; } /********************************************************************/ /* sane_get_select_fd */ /********************************************************************/ SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { DBG(10, "sane_get_select_fd...\n"); (void) handle, (void) fd; return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/microtek.conf.in000066400000000000000000000004141456256263500177620ustar00rootroot00000000000000# 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 backends-1.3.0/backend/microtek.h000066400000000000000000000311711456256263500166630ustar00rootroot00000000000000/*************************************************************************** * SANE - Scanner Access Now Easy. microtek.h This file Copyright 2002 Matthew Marjanovic This file is part of the SANE package. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . As a special exception, the authors of SANE give permission for additional uses of the libraries contained in this release of SANE. The exception is that, if you link a SANE library with other files to produce an executable, this does not by itself cause the resulting executable to be covered by the GNU General Public License. Your use of that executable is in no way restricted on account of linking the SANE library code into it. This exception does not, however, invalidate any other reasons why the executable file might be covered by the GNU General Public License. If you submit changes to SANE to the maintainers to be included in a subsequent release, you agree by submitting the changes that those changes may be distributed with this exception intact. If you write modifications of your own for SANE, it is your choice whether to permit this exception to apply to your modifications. If you do not wish that, delete this exception notice. *************************************************************************** This file implements a SANE backend for Microtek scanners. (feedback to: mtek-bugs@mir.com) (for latest info: http://www.mir.com/mtek/) ***************************************************************************/ #ifndef microtek_h #define microtek_h #include /*******************************************************************/ /***** enumeration of Option Descriptors *****/ /*******************************************************************/ enum Mtek_Option { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_MODE, /* -a,b,c,g */ OPT_HALFTONE_PATTERN, /* -H */ OPT_RESOLUTION, /* -r */ OPT_EXP_RES, OPT_NEGATIVE, /* -n */ OPT_SPEED, /* -v */ OPT_SOURCE, /* -t */ OPT_PREVIEW, OPT_CALIB_ONCE, OPT_GEOMETRY_GROUP, /* -f .... */ OPT_TL_X, /* top-left x */ OPT_TL_Y, /* top-left y */ OPT_BR_X, /* bottom-right x */ OPT_BR_Y, /* bottom-right y */ OPT_ENHANCEMENT_GROUP, OPT_EXPOSURE, OPT_BRIGHTNESS, /* -d */ OPT_CONTRAST, /* -k */ OPT_HIGHLIGHT, /* -l */ OPT_SHADOW, /* -s */ OPT_MIDTONE, /* -m */ OPT_GAMMA_GROUP, OPT_CUSTOM_GAMMA, OPT_ANALOG_GAMMA, OPT_ANALOG_GAMMA_R, OPT_ANALOG_GAMMA_G, OPT_ANALOG_GAMMA_B, /* "The gamma vectors MUST appear in the order gray, red, green, blue." */ OPT_GAMMA_VECTOR, OPT_GAMMA_VECTOR_R, OPT_GAMMA_VECTOR_G, OPT_GAMMA_VECTOR_B, OPT_GAMMA_BIND, NUM_OPTIONS, OPT_BACKTRACK, /* -B */ /* must come last: */ RNUM_OPTIONS }; /*******************************************************************/ /***** scanner hardware information (as discovered by INQUIRY) *****/ /*******************************************************************/ typedef struct Microtek_Info { char vendor_id[9]; char model_name[17]; char revision_num[5]; char vendor_string[21]; SANE_Byte device_type; SANE_Byte SCSI_firmware_ver_major; SANE_Byte SCSI_firmware_ver_minor; SANE_Byte scanner_firmware_ver_major; SANE_Byte scanner_firmware_ver_minor; SANE_Byte response_data_format; #define MI_RESSTEP_1PER 0x01 #define MI_RESSTEP_5PER 0x02 SANE_Byte res_step; #define MI_MODES_LINEART 0x01 #define MI_MODES_HALFTONE 0x02 #define MI_MODES_GRAY 0x04 /* ??????? or "MultiBit"??? XXXXX*/ #define MI_MODES_COLOR 0x08 #define MI_MODES_TRANSMSV 0x20 #define MI_MODES_ONEPASS 0x40 #define MI_MODES_NEGATIVE 0x80 SANE_Byte modes; SANE_Int pattern_count; SANE_Byte pattern_dwnld; #define MI_FEED_FLATBED 0x01 #define MI_FEED_EDGEFEED 0x02 #define MI_FEED_AUTOSUPP 0x04 SANE_Byte feed_type; #define MI_COMPRSS_HUFF 0x10 #define MI_COMPRSS_RD 0x20 SANE_Byte compress_type; #define MI_UNIT_8TH_INCH 0x40 #define MI_UNIT_PIXELS 0x80 SANE_Byte unit_type; SANE_Byte doc_size_code; SANE_Int max_x; /* pixels */ SANE_Int max_y; /* pixels */ SANE_Range doc_x_range; /* mm */ SANE_Range doc_y_range; /* mm */ SANE_Int cont_settings; SANE_Int exp_settings; SANE_Byte model_code; SANE_Int base_resolution; /* dpi, guessed by backend, per model code */ #define MI_SRC_FEED_SUPP 0x01 /* support for feeder */ #define MI_SRC_FEED_BT 0x02 /* support for feed backtracking control */ #define MI_SRC_HAS_FEED 0x04 /* feeder installed */ #define MI_SRC_FEED_RDY 0x08 /* feeder ready */ #define MI_SRC_GET_FEED 0x10 /* if opaque: get from feeder */ #define MI_SRC_GET_TRANS 0x20 /* get transparency (not opaque) */ #define MI_SRC_HAS_TRANS 0x40 /* transparency adapter installed */ SANE_Byte source_options; SANE_Byte expanded_resolution; #define MI_ENH_CAP_SHADOW 0x01 /* can adjust shadow/highlight */ #define MI_ENH_CAP_MIDTONE 0x02 /* can adjust midtone */ SANE_Byte enhance_cap; SANE_Int max_lookup_size; /* max. size of gamma LUT */ SANE_Int max_gamma_bit_depth; /* max. bits of a gamma LUT element */ SANE_Int gamma_size; /* size (bytes) of each LUT element */ SANE_Byte fast_color_preview; /* allows fast color preview? */ SANE_Byte xfer_format_select; /* allows select of transfer format? */ #define MI_COLSEQ_PLANE 0x00 #define MI_COLSEQ_PIXEL 0x01 #define MI_COLSEQ_RGB 0x02 #define MI_COLSEQ_NONRGB 0x03 #define MI_COLSEQ_2PIXEL 0x11 /* Agfa StudioStar */ SANE_Byte color_sequence; /* color sequence spec. code */ SANE_Byte does_3pass; /* allows 3-pass scanning? */ SANE_Byte does_mode1; /* allows MODE1 sense/select comm's? */ #define MI_FMT_CAP_4BPP 0x01 #define MI_FMT_CAP_10BPP 0x02 #define MI_FMT_CAP_12BPP 0x04 #define MI_FMT_CAP_16BPP 0x08 SANE_Byte bit_formats; /* output bit formats capabilities */ #define MI_EXCAP_OFF_CTL 0x01 #define MI_EXCAP_DIS_LNTBL 0x02 #define MI_EXCAP_DIS_RECAL 0x04 SANE_Byte extra_cap; /* SANE_Int contrast_vals; rolled into cont_settings */ SANE_Int min_contrast; SANE_Int max_contrast; /* SANE_Int exposure_vals; rolled into exp_settings */ SANE_Int min_exposure; SANE_Int max_exposure; SANE_Byte does_expansion; /* does expanded-mode expansion internally? */ } Microtek_Info; /*******************************************************************/ /***** device structure (one for each device discovered) *****/ /*******************************************************************/ typedef struct Microtek_Device { struct Microtek_Device *next; /* next, for linked list */ SANE_Device sane; /* SANE generic device block */ Microtek_Info info; /* detailed scanner spec */ } Microtek_Device; /*******************************************************************/ /***** ring buffer structure *****/ /***** ....image workspace during scan *****/ /*******************************************************************/ typedef struct ring_buffer { size_t bpl; /* bytes per line */ size_t ppl; /* pixels per line */ uint8_t *base; /* base address of buffer */ size_t size; /* size (bytes) of ring buffer */ size_t initial_size; /* initial size of ring buffer */ size_t tail_blue; /* byte index, next blue line */ size_t tail_green; /* byte index, next green line */ size_t tail_red; /* byte index, next red line */ size_t blue_extra; /* unmatched blue bytes */ size_t green_extra; /* unmatched green bytes */ size_t red_extra; /* unmatched red bytes */ size_t complete_count; size_t head_complete; } ring_buffer; /*******************************************************************/ /***** scanner structure (one for each device in use) *****/ /***** ....all the state needed to define a scan request *****/ /*******************************************************************/ typedef struct Microtek_Scanner { struct Microtek_Scanner *next; /* for linked list */ Microtek_Device *dev; /* raw device info */ SANE_Option_Descriptor sod[RNUM_OPTIONS]; /* option list for session */ Option_Value val[RNUM_OPTIONS]; /* option values for session */ /* SANE_Int gamma_table[4][256];*/ SANE_Int *gray_lut; SANE_Int *red_lut; SANE_Int *green_lut; SANE_Int *blue_lut; SANE_Range res_range; /* range desc. for resolution */ SANE_Range exp_res_range; /* range desc. for exp. resolution */ /* scan parameters, ready to toss to SCSI commands*/ /* ...set by sane_open (i.e. general/default scanner parameters) */ #define MS_UNIT_PIXELS 0 #define MS_UNIT_18INCH 1 SANE_Byte unit_type; /* pixels or 1/8" */ #define MS_RES_1PER 0 #define MS_RES_5PER 1 SANE_Byte res_type; /* 1% or 5% */ SANE_Bool midtone_support; SANE_Int paper_length; /* whatever unit */ SANE_Bool do_clever_precal; /* calibrate scanner once, via fake scan */ SANE_Bool do_real_calib; /* calibrate via magic commands */ SANE_Bool calib_once; /* ...only calibrate magically once */ SANE_Bool allow_calibrate; SANE_Bool onepass; SANE_Bool prescan, allowbacktrack; SANE_Bool reversecolors; SANE_Bool fastprescan; SANE_Int bits_per_color; SANE_Int gamma_entries; SANE_Int gamma_entry_size; SANE_Int gamma_bit_depth; /* SANE_Int gamma_max_entry;*/ SANE_Range gamma_entry_range; SANE_Range contrast_range; SANE_Range exposure_range; /* ...set by sane_get_parameters (i.e. parameters specified by options) */ SANE_Parameters params; /* format, lastframe, lines, depth, ppl, bpl */ SANE_Int x1; /* in 'units' */ SANE_Int y1; SANE_Int x2; SANE_Int y2; #define MS_MODE_LINEART 0 #define MS_MODE_HALFTONE 1 #define MS_MODE_GRAY 2 #define MS_MODE_COLOR 3 SANE_Int mode; #define MS_FILT_CLEAR 0 #define MS_FILT_RED 1 #define MS_FILT_GREEN 2 #define MS_FILT_BLUE 3 SANE_Byte filter; SANE_Bool onepasscolor, transparency, useADF; SANE_Bool threepasscolor, expandedresolution; SANE_Int resolution; SANE_Byte resolution_code; SANE_Byte exposure, contrast; SANE_Byte pattern; SANE_Byte velocity; SANE_Byte shadow, highlight, midtone; SANE_Byte bright_r, bright_g, bright_b; /* ??? XXXXXXXX signed char */ SANE_Bool multibit; SANE_Byte color_seq; /* ...stuff needed while in mid-scan */ #define MS_LNFMT_FLAT 0 #define MS_LNFMT_SEQ_RGB 1 #define MS_LNFMT_GOOFY_RGB 2 #define MS_LNFMT_SEQ_2R2G2B 3 SANE_Int line_format; /* specify how we need to repackage scanlines */ SANE_Int pixel_bpl; /* bytes per line, pixels */ SANE_Int header_bpl; /* bytes per line, headers */ SANE_Int ppl; /* pixels per line */ SANE_Int planes; /* color planes */ SANE_Bool doexpansion; double exp_aspect; SANE_Int dest_pixel_bpl; SANE_Int dest_ppl; SANE_Int unscanned_lines; /* lines still to be read from scanner */ SANE_Int undelivered_bytes; /* bytes still to be returned to frontend */ SANE_Int max_scsi_lines; /* max number of lines that fit in SCSI buffer */ int sfd; /* SCSI device file descriptor, -1 when not opened */ int scanning; /* true == mid-pass (between sane_start & sane_read=EOF) */ int scan_started; /* true == start_scan has scanner going... */ int woe; /* Woe! */ int this_pass; /* non-zero => in midst of a multipass scan (1,2,3) */ int cancel; /* we cleverly compare mode_sense results between scans to detect if the scanner may have been reset/power-cycled in the meantime */ SANE_Byte mode_sense_cache[10]; #define MS_PRECAL_NONE 0 #define MS_PRECAL_GRAY 1 #define MS_PRECAL_COLOR 2 #define MS_PRECAL_EXP_COLOR 3 SANE_Byte precal_record; /* record what precalibrations have been done */ #define MS_SENSE_IGNORE 1 int sense_flags; /* flags passed to the sense handler */ uint8_t *scsi_buffer; ring_buffer *rb; } Microtek_Scanner; #endif /* microtek_h */ backends-1.3.0/backend/microtek2.c000066400000000000000000010531451456256263500167460ustar00rootroot00000000000000/*************************************************************************** * SANE - Scanner Access Now Easy. microtek2.c This file (C) 1998, 1999 Bernd Schroeder modifications 2000, 2001 Karsten Festag 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, see . As a special exception, the authors of SANE give permission for additional uses of the libraries contained in this release of SANE. The exception is that, if you link a SANE library with other files to produce an executable, this does not by itself cause the resulting executable to be covered by the GNU General Public License. Your use of that executable is in no way restricted on account of linking the SANE library code into it. This exception does not, however, invalidate any other reasons why the executable file might be covered by the GNU General Public License. If you submit changes to SANE to the maintainers to be included in a subsequent release, you agree by submitting the changes that those changes may be distributed with this exception intact. If you write modifications of your own for SANE, it is your choice whether to permit this exception to apply to your modifications. If you do not wish that, delete this exception notice. *************************************************************************** This file implements a SANE backend for Microtek scanners with SCSI-2 command set. (feedback to: bernd@aquila.muc.de) ( karsten.festag@t-online.de) ***************************************************************************/ #ifdef _AIX # include /* MUST come first for AIX! */ #endif #include "../include/sane/config.h" #include "../include/lalloca.h" #include #include #include #include #include #include #include #include #include #include #include "../include/_stdint.h" #ifdef HAVE_AUTHORIZATION #include #endif #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_scsi.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_thread.h" #ifndef TESTBACKEND #define BACKEND_NAME microtek2 #else #define BACKEND_NAME microtek2_test #endif /* for testing*/ /*#define NO_PHANTOMTYPE_SHADING*/ #include "../include/sane/sanei_backend.h" #include "microtek2.h" #ifdef HAVE_AUTHORIZATION static SANE_Auth_Callback auth_callback; #endif static int md_num_devices = 0; /* number of devices from config file */ static Microtek2_Device *md_first_dev = NULL; /* list of known devices */ static Microtek2_Scanner *ms_first_handle = NULL; /* list of open scanners */ /* options that can be configured in the config file */ static Config_Options md_options = { 1.0, "off", "off", "off", "off", "off", "off"}; static Config_Temp *md_config_temp = NULL; static int md_dump = 0; /* from config file: */ /* 1: inquiry + scanner attributes */ /* 2: + all scsi commands and data */ /* 3: + all scan data */ static int md_dump_clear = 1; /*---------- sane_cancel() ---------------------------------------------------*/ void sane_cancel (SANE_Handle handle) { Microtek2_Scanner *ms = handle; DBG(30, "sane_cancel: handle=%p\n", handle); if ( ms->scanning == SANE_TRUE ) cleanup_scanner(ms); ms->cancelled = SANE_TRUE; ms->fd[0] = ms->fd[1] = -1; } /*---------- sane_close() ----------------------------------------------------*/ void sane_close (SANE_Handle handle) { Microtek2_Scanner *ms = handle; DBG(30, "sane_close: ms=%p\n", (void *) ms); if ( ! ms ) return; /* free malloc'ed stuff */ cleanup_scanner(ms); /* remove Scanner from linked list */ if ( ms_first_handle == ms ) ms_first_handle = ms->next; else { Microtek2_Scanner *ts = ms_first_handle; while ( (ts != NULL) && (ts->next != ms) ) ts = ts->next; ts->next = ts->next->next; /* == ms->next */ } DBG(100, "free ms at %p\n", (void *) ms); free((void *) ms); ms = NULL; } /*---------- sane_exit() -----------------------------------------------------*/ void sane_exit (void) { Microtek2_Device *next; int i; DBG(30, "sane_exit:\n"); /* close all leftover Scanners */ while (ms_first_handle != NULL) sane_close(ms_first_handle); /* free up device list */ while (md_first_dev != NULL) { next = md_first_dev->next; for ( i = 0; i < 4; i++ ) { if ( md_first_dev->custom_gamma_table[i] ) { DBG(100, "free md_first_dev->custom_gamma_table[%d] at %p\n", i, (void *) md_first_dev->custom_gamma_table[i]); free((void *) md_first_dev->custom_gamma_table[i]); md_first_dev->custom_gamma_table[i] = NULL; } } if ( md_first_dev->shading_table_w ) { DBG(100, "free md_first_dev->shading_table_w at %p\n", (void *) md_first_dev->shading_table_w); free((void *) md_first_dev->shading_table_w); md_first_dev->shading_table_w = NULL; } if ( md_first_dev->shading_table_d ) { DBG(100, "free md_first_dev->shading_table_d at %p\n", (void *) md_first_dev->shading_table_d); free((void *) md_first_dev->shading_table_d); md_first_dev->shading_table_d = NULL; } DBG(100, "free md_first_dev at %p\n", (void *) md_first_dev); free((void *) md_first_dev); md_first_dev = next; } sane_get_devices(NULL, SANE_FALSE); /* free list of SANE_Devices */ DBG(30, "sane_exit: MICROTEK2 says goodbye.\n"); } /*---------- sane_get_devices()-----------------------------------------------*/ SANE_Status sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only) { /* return a list of available devices; available here means that we get */ /* a positive response to an 'INQUIRY' and possibly to a */ /* 'READ SCANNER ATTRIBUTE' call */ static const SANE_Device **sd_list = NULL; Microtek2_Device *md; SANE_Status status; int index; DBG(30, "sane_get_devices: local_only=%d\n", local_only); /* this is hack to get the list freed with a call from sane_exit() */ if ( device_list == NULL ) { if ( sd_list ) { DBG(100, "free sd_list at %p\n", (void *) sd_list); free(sd_list); sd_list=NULL; } DBG(30, "sane_get_devices: sd_list_freed\n"); return SANE_STATUS_GOOD; } /* first free old list, if there is one; frontend wants a new list */ if ( sd_list ) { DBG(100, "free sd_list at %p\n", (void *) sd_list); free(sd_list); /* free array of pointers */ } sd_list = (const SANE_Device **) malloc( (md_num_devices + 1) * sizeof(SANE_Device **)); DBG(100, "sane_get_devices: sd_list=%p, malloc'd %lu bytes\n", (void *) sd_list, (u_long) ((md_num_devices + 1) * sizeof(SANE_Device **))); if ( ! sd_list ) { DBG(1, "sane_get_devices: malloc() for sd_list failed\n"); return SANE_STATUS_NO_MEM; } *device_list = sd_list; index = 0; md = md_first_dev; while ( md ) { status = attach(md); if ( status != SANE_STATUS_GOOD ) { DBG(10, "sane_get_devices: attach status '%s'\n", sane_strstatus(status)); md = md->next; continue; } /* check whether unit is ready, if so add it to the list */ status = scsi_test_unit_ready(md); if ( status != SANE_STATUS_GOOD ) { DBG(10, "sane_get_devices: test_unit_ready status '%s'\n", sane_strstatus(status)); md = md->next; continue; } sd_list[index] = &md->sane; ++index; md = md->next; } sd_list[index] = NULL; return SANE_STATUS_GOOD; } /*---------- sane_get_parameters() -------------------------------------------*/ SANE_Status sane_get_parameters(SANE_Handle handle, SANE_Parameters *params) { Microtek2_Scanner *ms = handle; Microtek2_Device *md; Option_Value *val; Microtek2_Info *mi; int mode; int depth; int bits_pp_in; /* bits per pixel from scanner */ int bits_pp_out; /* bits_per_pixel transferred to frontend */ int bytes_per_line; double x_pixel_per_mm; double y_pixel_per_mm; double x1_pixel; double y1_pixel; double width_pixel; double height_pixel; DBG(40, "sane_get_parameters: handle=%p, params=%p\n", handle, (void *) params); md = ms->dev; mi = &md->info[md->scan_source]; val= ms->val; if ( ! ms->scanning ) /* get an estimate for the params */ { get_scan_mode_and_depth(ms, &mode, &depth, &bits_pp_in, &bits_pp_out); switch ( mode ) { case MS_MODE_COLOR: if ( mi->onepass ) { ms->params.format = SANE_FRAME_RGB; ms->params.last_frame = SANE_TRUE; } else { ms->params.format = SANE_FRAME_RED; ms->params.last_frame = SANE_FALSE; } break; case MS_MODE_GRAY: case MS_MODE_HALFTONE: case MS_MODE_LINEART: case MS_MODE_LINEARTFAKE: ms->params.format = SANE_FRAME_GRAY; ms->params.last_frame = SANE_TRUE; break; default: DBG(1, "sane_get_parameters: Unknown scan mode %d\n", mode); break; } ms->params.depth = (SANE_Int) bits_pp_out; /* calculate lines, pixels per line and bytes per line */ if ( val[OPT_RESOLUTION_BIND].w == SANE_TRUE ) { x_pixel_per_mm = y_pixel_per_mm = SANE_UNFIX(val[OPT_RESOLUTION].w) / MM_PER_INCH; DBG(30, "sane_get_parameters: x_res=y_res=%f\n", SANE_UNFIX(val[OPT_RESOLUTION].w)); } else { 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_RESOLUTION].w), SANE_UNFIX(val[OPT_Y_RESOLUTION].w)); } DBG(30, "sane_get_parameters: x_ppm=%f, y_ppm=%f\n", x_pixel_per_mm, y_pixel_per_mm); y1_pixel = SANE_UNFIX(ms->val[OPT_TL_Y].w) * y_pixel_per_mm; height_pixel = fabs(SANE_UNFIX(ms->val[OPT_BR_Y].w) * y_pixel_per_mm - y1_pixel) + 0.5; ms->params.lines = (SANE_Int) height_pixel; x1_pixel = SANE_UNFIX(ms->val[OPT_TL_X].w) * x_pixel_per_mm; width_pixel = fabs(SANE_UNFIX(ms->val[OPT_BR_X].w) * x_pixel_per_mm - x1_pixel) + 0.5; ms->params.pixels_per_line = (SANE_Int) width_pixel; if ( bits_pp_out == 1 ) bytes_per_line = (width_pixel + 7 ) / 8; else { bytes_per_line = ( width_pixel * bits_pp_out ) / 8 ; if ( mode == MS_MODE_COLOR && mi->onepass ) bytes_per_line *= 3; } ms->params.bytes_per_line = (SANE_Int) bytes_per_line; } /* if ms->scanning */ if ( params ) *params = ms->params; DBG(30,"sane_get_parameters: format=%d, last_frame=%d, lines=%d\n", ms->params.format,ms->params.last_frame, ms->params.lines); DBG(30,"sane_get_parameters: depth=%d, ppl=%d, bpl=%d\n", ms->params.depth,ms->params.pixels_per_line, ms->params.bytes_per_line); return SANE_STATUS_GOOD; } /*---------- sane_get_select_fd() --------------------------------------------*/ SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int *fd) { Microtek2_Scanner *ms = handle; DBG(30, "sane_get_select_fd: ms=%p\n", (void *) ms); if ( ! ms->scanning ) { DBG(1, "sane_get_select_fd: Scanner not scanning\n"); return SANE_STATUS_INVAL; } *fd = (SANE_Int) ms->fd[0]; return SANE_STATUS_GOOD; } /*---------- sane_init() -----------------------------------------------------*/ SANE_Status #ifdef HAVE_AUTHORIZATION sane_init(SANE_Int *version_code, SANE_Auth_Callback authorize) #else sane_init(SANE_Int *version_code, SANE_Auth_Callback __sane_unused__ authorize) #endif { Microtek2_Device *md; FILE *fp; DBG_INIT(); DBG(1, "sane_init: Microtek2 (v%d.%d build %s) says hello...\n", MICROTEK2_MAJOR, MICROTEK2_MINOR, MICROTEK2_BUILD); if ( version_code ) *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); #ifdef HAVE_AUTHORIZATION auth_callback = authorize; #endif sanei_thread_init(); fp = sanei_config_open(MICROTEK2_CONFIG_FILE); if ( fp == NULL ) DBG(10, "sane_init: file not opened: '%s'\n", MICROTEK2_CONFIG_FILE); else { /* check config file for devices and associated options */ parse_config_file(fp, &md_config_temp); while ( md_config_temp ) { sanei_config_attach_matching_devices(md_config_temp->device, attach_one); if ( md_config_temp->next ) /* go to next device, if existent */ md_config_temp = md_config_temp->next; else break; } fclose(fp); } if ( md_first_dev == NULL ) { /* config file not found or no valid entry; default to /dev/scanner */ /* instead of insisting on config file */ add_device_list("/dev/scanner", &md); if ( md ) attach(md); } return SANE_STATUS_GOOD; } /*---------- sane_open() -----------------------------------------------------*/ SANE_Status sane_open(SANE_String_Const name, SANE_Handle *handle) { SANE_Status status; Microtek2_Scanner *ms; Microtek2_Device *md; #ifdef HAVE_AUTHORIZATION struct stat st; int rc; #endif DBG(30, "sane_open: device='%s'\n", name); *handle = NULL; md = md_first_dev; if ( name ) { /* add_device_list() returns a pointer to the device struct if */ /* the device is known or newly added, else it returns NULL */ status = add_device_list(name, &md); if ( status != SANE_STATUS_GOOD ) return status; } if ( ! md ) { DBG(10, "sane_open: invalid device name '%s'\n", name); return SANE_STATUS_INVAL; } /* attach calls INQUIRY and READ SCANNER ATTRIBUTES */ status = attach(md); if ( status != SANE_STATUS_GOOD ) return status; ms = malloc(sizeof(Microtek2_Scanner)); DBG(100, "sane_open: ms=%p, malloc'd %lu bytes\n", (void *) ms, (u_long) sizeof(Microtek2_Scanner)); if ( ms == NULL ) { DBG(1, "sane_open: malloc() for ms failed\n"); return SANE_STATUS_NO_MEM; } memset(ms, 0, sizeof(Microtek2_Scanner)); ms->dev = md; ms->scanning = SANE_FALSE; ms->cancelled = SANE_FALSE; ms->current_pass = 0; ms->sfd = -1; sanei_thread_initialize(ms->pid); ms->fp = NULL; ms->gamma_table = NULL; ms->buf.src_buf = ms->buf.src_buffer[0] = ms->buf.src_buffer[1] = NULL; ms->control_bytes = NULL; ms->shading_image = NULL; ms->condensed_shading_w = NULL; ms->condensed_shading_d = NULL; ms->current_color = MS_COLOR_ALL; ms->current_read_color = MS_COLOR_RED; init_options(ms, MD_SOURCE_FLATBED); /* insert scanner into linked list */ ms->next = ms_first_handle; ms_first_handle = ms; *handle = ms; #ifdef HAVE_AUTHORIZATION /* check whether the file with the passwords exists. If it doesn't */ /* exist, we don't use any authorization */ rc = stat(PASSWD_FILE, &st); if ( rc == -1 && errno == ENOENT ) return SANE_STATUS_GOOD; else { status = do_authorization(md->name); return status; } #else return SANE_STATUS_GOOD; #endif } /*---------- sane_read() -----------------------------------------------------*/ SANE_Status sane_read(SANE_Handle handle, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *len ) { Microtek2_Scanner *ms = handle; SANE_Status status; ssize_t nread; DBG(30, "sane_read: handle=%p, buf=%p, maxlen=%d\n", handle, (void *) buf, maxlen); *len = 0; if ( ! ms->scanning || ms->cancelled ) { if ( ms->cancelled ) { status = SANE_STATUS_CANCELLED; } else { DBG(15, "sane_read: Scanner %p not scanning\n", (void *) ms); status = SANE_STATUS_IO_ERROR; } DBG(15, "sane_read: scan cancelled or scanner not scanning->cleanup\n"); cleanup_scanner(ms); return status; } nread = read(ms->fd[0], (void *) buf, (int) maxlen); if ( nread == -1 ) { if ( errno == EAGAIN ) { DBG(30, "sane_read: currently no data available\n"); return SANE_STATUS_GOOD; } else { DBG(1, "sane_read: read() failed, errno=%d\n", errno); cleanup_scanner(ms); return SANE_STATUS_IO_ERROR; } } if ( nread == 0 ) { DBG(15, "sane_read: read 0 bytes -> EOF\n"); ms->scanning = SANE_FALSE; cleanup_scanner(ms); return SANE_STATUS_EOF; } *len = (SANE_Int) nread; DBG(30, "sane_read: *len=%d\n", *len); return SANE_STATUS_GOOD; } /*---------- sane_set_io_mode() ---------------------------------------------*/ SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { Microtek2_Scanner *ms = handle; int rc; DBG(30, "sane_set_io_mode: handle=%p, nonblocking=%d\n", handle, non_blocking); if ( ! ms->scanning ) { DBG(1, "sane_set_io_mode: Scanner not scanning\n"); return SANE_STATUS_INVAL; } rc = fcntl(ms->fd[0], F_SETFL, non_blocking ? O_NONBLOCK : 0); if ( rc == -1 ) { DBG(1, "sane_set_io_mode: fcntl() failed\n"); return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } /*---------- add_device_list() -----------------------------------------------*/ static SANE_Status add_device_list(SANE_String_Const dev_name, Microtek2_Device **mdev) { Microtek2_Device *md; SANE_String hdev; size_t len; if ( (hdev = strdup(dev_name)) == NULL) { DBG(5, "add_device_list: malloc() for hdev failed\n"); return SANE_STATUS_NO_MEM; } len = strlen(hdev); if ( hdev[len - 1] == '\n' ) hdev[--len] = '\0'; DBG(30, "add_device_list: device='%s'\n", hdev); /* check, if device is already known */ md = md_first_dev; while ( md ) { if ( strcmp(hdev, md->name) == 0 ) { DBG(30, "add_device_list: device '%s' already in list\n", hdev); *mdev = md; return SANE_STATUS_GOOD; } md = md->next; } md = (Microtek2_Device *) malloc(sizeof(Microtek2_Device)); DBG(100, "add_device_list: md=%p, malloc'd %lu bytes\n", (void *) md, (u_long) sizeof(Microtek2_Device)); if ( md == NULL ) { DBG(1, "add_device_list: malloc() for md failed\n"); return SANE_STATUS_NO_MEM; } /* initialize Device and add it at the beginning of the list */ memset(md, 0, sizeof(Microtek2_Device)); md->next = md_first_dev; md_first_dev = md; md->sane.name = NULL; md->sane.vendor = NULL; md->sane.model = NULL; md->sane.type = NULL; md->scan_source = MD_SOURCE_FLATBED; md->shading_table_w = NULL; md->shading_table_d = NULL; strncpy(md->name, hdev, PATH_MAX - 1); if ( md_config_temp ) md->opts = md_config_temp->opts; else md->opts = md_options; ++md_num_devices; *mdev = md; DBG(100, "free hdev at %p\n", (void *) hdev); free(hdev); return SANE_STATUS_GOOD; } /*---------- attach() --------------------------------------------------------*/ static SANE_Status attach(Microtek2_Device *md) { /* This function is called from sane_init() to do the inquiry and to read */ /* the scanner attributes. If one of these calls fails, or if a new */ /* device is passed in sane_open() this function may also be called */ /* from sane_open() or sane_get_devices(). */ SANE_String model_string; SANE_Status status; SANE_Byte source_info; DBG(30, "attach: device='%s'\n", md->name); status = scsi_inquiry( &md->info[MD_SOURCE_FLATBED], md->name ); if ( status != SANE_STATUS_GOOD ) { DBG(1, "attach: '%s'\n", sane_strstatus(status)); return status; } /* We copy the inquiry info into the info structures for each scansource */ /* like ADF, TMA, STRIPE and SLIDE */ for ( source_info = 1; source_info < 5; ++source_info ) memcpy( &md->info[source_info], &md->info[MD_SOURCE_FLATBED], sizeof( Microtek2_Info ) ); /* Here we should insert a function, that stores all the relevant */ /* information in the info structure in a more conveniant format */ /* in the device structure, e.g. the model name with a trailing '\0'. */ status = check_inquiry(md, &model_string); if ( status != SANE_STATUS_GOOD ) return status; md->sane.name = md->name; md->sane.vendor = "Microtek"; md->sane.model = strdup(model_string); if ( md->sane.model == NULL ) DBG(1, "attach: strdup for model string failed\n"); md->sane.type = "flatbed scanner"; md->revision = strtod(md->info[MD_SOURCE_FLATBED].revision, NULL); status = scsi_read_attributes(&md->info[0], md->name, MD_SOURCE_FLATBED); if ( status != SANE_STATUS_GOOD ) { DBG(1, "attach: '%s'\n", sane_strstatus(status)); return status; } if ( MI_LUTCAP_NONE( md->info[MD_SOURCE_FLATBED].lut_cap) ) /* no gamma tables */ md->model_flags |= MD_NO_GAMMA; /* check whether the device supports transparency media adapters */ if ( md->info[MD_SOURCE_FLATBED].option_device & MI_OPTDEV_TMA ) { status = scsi_read_attributes(&md->info[0], md->name, MD_SOURCE_TMA); if ( status != SANE_STATUS_GOOD ) return status; } /* check whether the device supports an ADF */ if ( md->info[MD_SOURCE_FLATBED].option_device & MI_OPTDEV_ADF ) { status = scsi_read_attributes(&md->info[0], md->name, MD_SOURCE_ADF); if ( status != SANE_STATUS_GOOD ) return status; } /* check whether the device supports STRIPES */ if ( md->info[MD_SOURCE_FLATBED].option_device & MI_OPTDEV_STRIPE ) { status = scsi_read_attributes(&md->info[0], md->name, MD_SOURCE_STRIPE); if ( status != SANE_STATUS_GOOD ) return status; } /* check whether the device supports SLIDES */ if ( md->info[MD_SOURCE_FLATBED].option_device & MI_OPTDEV_SLIDE ) { /* The Phantom 636cx indicates in its attributes that it supports */ /* slides, but it doesn't. Thus this command would fail. */ if ( ! (md->model_flags & MD_NO_SLIDE_MODE) ) { status = scsi_read_attributes(&md->info[0], md->name, MD_SOURCE_SLIDE); if ( status != SANE_STATUS_GOOD ) return status; } } status = scsi_read_system_status(md, -1); if ( status != SANE_STATUS_GOOD ) return status; return SANE_STATUS_GOOD; } /*---------- attach_one() ----------------------------------------------------*/ static SANE_Status attach_one (const char *name) { Microtek2_Device *md; Microtek2_Device *md_tmp; DBG(30, "attach_one: name='%s'\n", name); md_tmp = md_first_dev; /* if add_device_list() adds an entry it does this at the beginning */ /* of the list and thus changes md_first_dev */ add_device_list(name, &md); if ( md_tmp != md_first_dev ) attach(md); return SANE_STATUS_GOOD; } /*---------- cancel_scan() ---------------------------------------------------*/ static SANE_Status cancel_scan(Microtek2_Scanner *ms) { SANE_Status status; DBG(30, "cancel_scan: ms=%p\n", (void *) ms); /* READ IMAGE with a transferlength of 0 aborts a scan */ ms->transfer_length = 0; status = scsi_read_image(ms, (uint8_t *) NULL, 1); if ( status != SANE_STATUS_GOOD ) { DBG(1, "cancel_scan: cancel failed: '%s'\n", sane_strstatus(status)); status = SANE_STATUS_IO_ERROR; } else status = SANE_STATUS_CANCELLED; close(ms->fd[1]); /* if we are aborting a scan because, for example, we run out of material on a feeder, then pid may be already -1 and kill(-1, SIGTERM), i.e. killing all our processes, is not likely what we really want - --mj, 2001/Nov/19 */ if (sanei_thread_is_valid (ms->pid)) { sanei_thread_kill(ms->pid); sanei_thread_waitpid(ms->pid, NULL); } return status; } /*---------- check_option() --------------------------------------------------*/ static void check_option(const char *cp, Config_Options *co) { /* This function analyses options in the config file */ char *endptr; /* When this function is called, it is already made sure that this */ /* is an option line, i.e. a line that starts with option */ cp = sanei_config_skip_whitespace(cp); /* skip blanks */ cp = sanei_config_skip_whitespace(cp + 6); /* skip "option" */ if ( strncmp(cp, "dump", 4) == 0 && isspace(cp[4]) ) { cp = sanei_config_skip_whitespace(cp + 4); if ( *cp ) { md_dump = (int) strtol(cp, &endptr, 10); if ( md_dump > 4 || md_dump < 0 ) { md_dump = 1; DBG(30, "check_option: setting dump to %d\n", md_dump); } cp = sanei_config_skip_whitespace(endptr); if ( *cp ) { /* something behind the option value or value wrong */ md_dump = 1; DBG(30, "check_option: option value wrong\n"); } } else { DBG(30, "check_option: missing option value\n"); /* reasonable fallback */ md_dump = 1; } } else if ( strncmp(cp, "strip-height", 12) == 0 && isspace(cp[12]) ) { cp = sanei_config_skip_whitespace(cp + 12); if ( *cp ) { co->strip_height = strtod(cp, &endptr); DBG(30, "check_option: setting strip_height to %f\n", co->strip_height); if ( co->strip_height <= 0.0 ) co->strip_height = 14.0; cp = sanei_config_skip_whitespace(endptr); if ( *cp ) { /* something behind the option value or value wrong */ co->strip_height = 14.0; DBG(30, "check_option: option value wrong: %f\n", co->strip_height); } } } else if ( strncmp(cp, "no-backtrack-option", 19) == 0 && isspace(cp[19]) ) { cp = sanei_config_skip_whitespace(cp + 19); if ( strncmp(cp, "on", 2) == 0 ) { cp = sanei_config_skip_whitespace(cp + 2); co->no_backtracking = "on"; } else if ( strncmp(cp, "off", 3) == 0 ) { cp = sanei_config_skip_whitespace(cp + 3); co->no_backtracking = "off"; } else co->no_backtracking = "off"; if ( *cp ) { /* something behind the option value or value wrong */ co->no_backtracking = "off"; DBG(30, "check_option: option value wrong: %s\n", cp); } } else if ( strncmp(cp, "lightlid-35", 11) == 0 && isspace(cp[11]) ) { cp = sanei_config_skip_whitespace(cp + 11); if ( strncmp(cp, "on", 2) == 0 ) { cp = sanei_config_skip_whitespace(cp + 2); co->lightlid35 = "on"; } else if ( strncmp(cp, "off", 3) == 0 ) { cp = sanei_config_skip_whitespace(cp + 3); co->lightlid35 = "off"; } else co->lightlid35 = "off"; if ( *cp ) { /* something behind the option value or value wrong */ co->lightlid35 = "off"; DBG(30, "check_option: option value wrong: %s\n", cp); } } else if ( strncmp(cp, "toggle-lamp", 11) == 0 && isspace(cp[11]) ) { cp = sanei_config_skip_whitespace(cp + 11); if ( strncmp(cp, "on", 2) == 0 ) { cp = sanei_config_skip_whitespace(cp + 2); co->toggle_lamp = "on"; } else if ( strncmp(cp, "off", 3) == 0 ) { cp = sanei_config_skip_whitespace(cp + 3); co->toggle_lamp = "off"; } else co->toggle_lamp = "off"; if ( *cp ) { /* something behind the option value or value wrong */ co->toggle_lamp = "off"; DBG(30, "check_option: option value wrong: %s\n", cp); } } else if ( strncmp(cp, "lineart-autoadjust", 18) == 0 && isspace(cp[18]) ) { cp = sanei_config_skip_whitespace(cp + 18); if ( strncmp(cp, "on", 2) == 0 ) { cp = sanei_config_skip_whitespace(cp + 2); co->auto_adjust = "on"; } else if ( strncmp(cp, "off", 3) == 0 ) { cp = sanei_config_skip_whitespace(cp + 3); co->auto_adjust = "off"; } else co->auto_adjust = "off"; if ( *cp ) { /* something behind the option value or value wrong */ co->auto_adjust = "off"; DBG(30, "check_option: option value wrong: %s\n", cp); } } else if ( strncmp(cp, "backend-calibration", 19) == 0 && isspace(cp[19]) ) { cp = sanei_config_skip_whitespace(cp + 19); if ( strncmp(cp, "on", 2) == 0 ) { cp = sanei_config_skip_whitespace(cp + 2); co->backend_calibration = "on"; } else if ( strncmp(cp, "off", 3) == 0 ) { cp = sanei_config_skip_whitespace(cp + 3); co->backend_calibration = "off"; } else co->backend_calibration = "off"; if ( *cp ) { /* something behind the option value or value wrong */ co->backend_calibration = "off"; DBG(30, "check_option: option value wrong: %s\n", cp); } } else if ( strncmp(cp, "colorbalance-adjust", 19) == 0 && isspace(cp[19]) ) { cp = sanei_config_skip_whitespace(cp + 19); if ( strncmp(cp, "on", 2) == 0 ) { cp = sanei_config_skip_whitespace(cp + 2); co->colorbalance_adjust = "on"; } else if ( strncmp(cp, "off", 3) == 0 ) { cp = sanei_config_skip_whitespace(cp + 3); co->colorbalance_adjust = "off"; } else co->colorbalance_adjust = "off"; if ( *cp ) { /* something behind the option value or value wrong */ co->colorbalance_adjust = "off"; DBG(30, "check_option: option value wrong: %s\n", cp); } } else DBG(30, "check_option: invalid option in '%s'\n", cp); } /*---------- check_inquiry() -------------------------------------------------*/ static SANE_Status check_inquiry(Microtek2_Device *md, SANE_String *model_string) { Microtek2_Info *mi; DBG(30, "check_inquiry: md=%p\n", (void *) md); md->n_control_bytes = 0; md->shading_length = 0; md->shading_table_contents = 0; mi = &md->info[MD_SOURCE_FLATBED]; if ( mi->scsi_version != MI_SCSI_II_VERSION ) { DBG(1, "check_inquiry: Device is not a SCSI-II device, but 0x%02x\n", mi->scsi_version); return SANE_STATUS_IO_ERROR; } if ( mi->device_type != MI_DEVTYPE_SCANNER ) { DBG(1, "check_inquiry: Device is not a scanner, but 0x%02x\n", mi->device_type); return SANE_STATUS_IO_ERROR; } if ( strncasecmp("MICROTEK", mi->vendor, INQ_VENDOR_L) != 0 && strncmp(" ", mi->vendor, INQ_VENDOR_L) != 0 && strncmp("AGFA ", mi->vendor, INQ_VENDOR_L) != 0 ) { DBG(1, "check_inquiry: Device is not a Microtek, but '%.*s'\n", INQ_VENDOR_L, mi->vendor); return SANE_STATUS_IO_ERROR; } if ( mi->depth & MI_HASDEPTH_16 ) md->shading_depth = 16; else if ( mi->depth & MI_HASDEPTH_14 ) md->shading_depth = 14; else if ( mi->depth & MI_HASDEPTH_12 ) md->shading_depth = 12; else if ( mi->depth & MI_HASDEPTH_10 ) md->shading_depth = 10; else md->shading_depth = 8; switch (mi->model_code) { case 0x81: case 0xab: *model_string = "ScanMaker 4"; break; case 0x85: *model_string = "ScanMaker V300 / ColorPage-EP"; /* The ScanMaker V300 (FW < 2.70) returns some values for the */ /* "read image info" command in only two bytes */ /* and doesn't understand read_image_status */ md->model_flags |= MD_NO_RIS_COMMAND; if ( md->revision < 2.70 ) md->model_flags |= MD_RII_TWO_BYTES; break; case 0x87: *model_string = "ScanMaker 5"; md->model_flags |= MD_NO_GAMMA; break; case 0x89: *model_string = "ScanMaker 6400XL"; break; case 0x8a: *model_string = "ScanMaker 9600XL"; break; case 0x8c: *model_string = "ScanMaker 630 / ScanMaker V600"; break; case 0x8d: *model_string = "ScanMaker 336 / ScanMaker V310"; break; case 0x90: case 0x92: *model_string = "E3+ / Vobis HighScan"; break; case 0x91: *model_string = "ScanMaker X6 / Phantom 636"; /* The X6 indicates a data format of segregated data in TMA mode */ /* but actually transfers as chunky data */ md->model_flags |= MD_DATA_FORMAT_WRONG; if ( md->revision == 1.00 ) md->model_flags |= MD_OFFSET_2; break; case 0x93: *model_string = "ScanMaker 336 / ScanMaker V310"; break; case 0x70: case 0x71: case 0x94: case 0xa0: *model_string = "Phantom 330cx / Phantom 336cx / SlimScan C3"; /* These models do not accept gamma tables. Apparently they */ /* read the control bits and do not accept shading tables */ /* They also don't support enhancements (contrast, brightness...)*/ md->model_flags |= MD_NO_SLIDE_MODE | MD_NO_GAMMA #ifndef NO_PHANTOMTYPE_SHADING | MD_PHANTOM336CX_TYPE_SHADING #endif | MD_READ_CONTROL_BIT | MD_NO_ENHANCEMENTS; md->opt_backend_calib_default = SANE_TRUE; md->opt_no_backtrack_default = SANE_TRUE; md->n_control_bytes = 320; md->shading_length = 18; md->shading_depth = 10; md->controlbit_offset = 7; break; case 0x95: *model_string = "ArtixScan 1010"; break; case 0x97: *model_string = "ScanMaker 636"; break; case 0x98: *model_string = "ScanMaker X6EL"; if ( md->revision == 1.00 ) md->model_flags |= MD_OFFSET_2; break; case 0x99: *model_string = "ScanMaker X6USB"; if ( md->revision == 1.00 ) md->model_flags |= MD_OFFSET_2; md->model_flags |= MD_X6_SHORT_TRANSFER; break; case 0x9a: *model_string = "Phantom 636cx / C6"; /* The Phantom 636cx says it supports the SLIDE mode, but it */ /* doesn't. Thus inquring the attributes for slide mode would */ /* fail. Also it does not accept gamma tables. Apparently */ /* it reads the control bits and does not accept shading tables */ md->model_flags |= MD_NO_SLIDE_MODE | MD_READ_CONTROL_BIT | MD_NO_GAMMA | MD_PHANTOM_C6; md->opt_backend_calib_default = SANE_TRUE; md->opt_no_backtrack_default = SANE_TRUE; md->n_control_bytes = 647; /* md->shading_length = 18; firmware values seem to work better */ md->shading_depth = 12; md->controlbit_offset = 18; break; case 0x9d: *model_string = "AGFA Duoscan T1200"; break; case 0xa3: *model_string = "ScanMaker V6USL"; /* The V6USL does not accept gamma tables */ md->model_flags |= MD_NO_GAMMA; break; case 0xa5: *model_string = "ArtixScan 4000t"; break; case 0xac: *model_string = "ScanMaker V6UL"; /* The V6USL does not accept gamma tables, perhaps the V6UL also */ md->model_flags |= MD_NO_GAMMA; break; case 0xaf: *model_string = "SlimScan C3"; md->model_flags |= MD_NO_SLIDE_MODE | MD_NO_GAMMA | MD_READ_CONTROL_BIT | MD_NO_ENHANCEMENTS; md->opt_backend_calib_default = SANE_TRUE; md->opt_no_backtrack_default = SANE_TRUE; md->n_control_bytes = 320; md->controlbit_offset = 7; break; case 0xb0: *model_string = "ScanMaker X12USL"; md->opt_backend_calib_default = SANE_TRUE; md->model_flags |= MD_16BIT_TRANSFER | MD_CALIB_DIVISOR_600; break; case 0xb3: *model_string = "ScanMaker 3600"; break; case 0xb4: *model_string = "ScanMaker 4700"; break; case 0xb6: *model_string = "ScanMaker V6UPL"; /* is like V6USL but with USB and Parport interface ?? */ md->model_flags |= MD_NO_GAMMA; break; case 0xb8: *model_string = "ScanMaker 3700"; break; case 0xde: *model_string = "ScanMaker 9800XL"; md->model_flags |= MD_NO_GAMMA | MD_16BIT_TRANSFER; md->opt_backend_calib_default = SANE_TRUE; md->opt_no_backtrack_default = SANE_TRUE; break; default: DBG(1, "check_inquiry: Model 0x%02x not supported\n", mi->model_code); return SANE_STATUS_IO_ERROR; } return SANE_STATUS_GOOD; } /*---------- cleanup_scanner() -----------------------------------------------*/ static void cleanup_scanner(Microtek2_Scanner *ms) { DBG(30, "cleanup_scanner: ms=%p, ms->sfd=%d\n", (void *) ms, ms->sfd); if ( ms->scanning == SANE_TRUE ) cancel_scan(ms); if ( ms->sfd != -1 ) sanei_scsi_close(ms->sfd); ms->sfd = -1; sanei_thread_invalidate(ms->pid); ms->fp = NULL; ms->current_pass = 0; ms->scanning = SANE_FALSE; ms->cancelled = SANE_FALSE; /* free buffers */ if ( ms->buf.src_buffer[0] ) { DBG(100, "free ms->buf.src_buffer[0] at %p\n", (void *) ms->buf.src_buffer[0]); free((void *) ms->buf.src_buffer[0]); ms->buf.src_buffer[0] = NULL; ms->buf.src_buf = NULL; } if ( ms->buf.src_buffer[1] ) { DBG(100, "free ms->buf.src_buffer[1] at %p\n", (void *) ms->buf.src_buffer[1]); free((void *) ms->buf.src_buffer[1]); ms->buf.src_buffer[1] = NULL; ms->buf.src_buf = NULL; } if ( ms->buf.src_buf ) { DBG(100, "free ms->buf.src_buf at %p\n", (void *) ms->buf.src_buf); free((void *) ms->buf.src_buf); ms->buf.src_buf = NULL; } if ( ms->temporary_buffer ) { DBG(100, "free ms->temporary_buffer at %p\n", (void *) ms->temporary_buffer); free((void *) ms->temporary_buffer); ms->temporary_buffer = NULL; } if ( ms->gamma_table ) { DBG(100, "free ms->gamma_table at %p\n", (void *) ms->gamma_table); free((void *) ms->gamma_table); ms->gamma_table = NULL; } if ( ms->control_bytes ) { DBG(100, "free ms->control_bytes at %p\n", (void *) ms->control_bytes); free((void *) ms->control_bytes); ms->control_bytes = NULL; } if ( ms->condensed_shading_w ) { DBG(100, "free ms->condensed_shading_w at %p\n", (void *) ms->condensed_shading_w); free((void *) ms->condensed_shading_w); ms->condensed_shading_w = NULL; } if ( ms->condensed_shading_d ) { DBG(100, "free ms->condensed_shading_d at %p\n", (void *) ms->condensed_shading_d); free((void *) ms->condensed_shading_d); ms->condensed_shading_d = NULL; } return; } #ifdef HAVE_AUTHORIZATION /*---------- do_authorization() ----------------------------------------------*/ static SANE_Status do_authorization(char *resource) { /* This function implements a simple authorization function. It looks */ /* up an entry in the file SANE_PATH_CONFIG_DIR/auth. Such an entry */ /* must be of the form device:user:password where password is a crypt() */ /* encrypted password. If several users are allowed to access a device */ /* an entry must be created for each user. If no entry exists for device */ /* or the file does not exist no authentication is necessary. If the */ /* file exists, but can't be opened the authentication fails */ SANE_Status status; FILE *fp; int device_found; char username[SANE_MAX_USERNAME_LEN]; char password[SANE_MAX_PASSWORD_LEN]; char line[MAX_LINE_LEN]; char *linep; char *device; char *user; char *passwd; char *p; DBG(30, "do_authorization: resource=%s\n", resource); if ( auth_callback == NULL ) /* frontend does not require authorization */ return SANE_STATUS_GOOD; /* first check if an entry exists in for this device. If not, we don't */ /* use authorization */ fp = fopen(PASSWD_FILE, "r"); if ( fp == NULL ) { if ( errno == ENOENT ) { DBG(1, "do_authorization: file not found: %s\n", PASSWD_FILE); return SANE_STATUS_GOOD; } else { DBG(1, "do_authorization: fopen() failed, errno=%d\n", errno); return SANE_STATUS_ACCESS_DENIED; } } linep = &line[0]; device_found = 0; while ( fgets(line, MAX_LINE_LEN, fp) ) { p = index(linep, SEPARATOR); if ( p ) { *p = '\0'; device = linep; if ( strcmp(device, resource) == 0 ) { DBG(2, "equal\n"); device_found = 1; break; } } } if ( ! device_found ) { fclose(fp); return SANE_STATUS_GOOD; } fseek(fp, 0L, SEEK_SET); (*auth_callback) (resource, username, password); status = SANE_STATUS_ACCESS_DENIED; do { fgets(line, MAX_LINE_LEN, fp); if ( ! ferror(fp) && ! feof(fp) ) { /* neither strsep(3) nor strtok(3) seem to work on my system */ p = index(linep, SEPARATOR); if ( p == NULL ) continue; *p = '\0'; device = linep; if ( strcmp( device, resource) != 0 ) /* not a matching entry */ continue; linep = ++p; p = index(linep, SEPARATOR); if ( p == NULL ) continue; *p = '\0'; user = linep; if ( strncmp(user, username, SANE_MAX_USERNAME_LEN) != 0 ) continue; /* username doesn't match */ linep = ++p; /* rest of the line is considered to be the password */ passwd = linep; /* remove newline */ *(passwd + strlen(passwd) - 1) = '\0'; p = crypt(password, SALT); if ( strcmp(p, passwd) == 0 ) { /* authentication ok */ status = SANE_STATUS_GOOD; break; } else continue; } } while ( ! ferror(fp) && ! feof(fp) ); fclose(fp); return status; } #endif /*---------- dump_area() -----------------------------------------------------*/ static SANE_Status dump_area(uint8_t *area, int len, char *info) { /* this function dumps control or information blocks */ #define BPL 16 /* bytes per line to print */ int i; int o; int o_limit; char outputline[100]; char *outbuf; if ( ! info[0] ) info = "No additional info available"; DBG(30, "dump_area: %s\n", info); outbuf = outputline; o_limit = (len + BPL - 1) / BPL; for ( o = 0; o < o_limit; o++) { sprintf(outbuf, " %4d: ", o * BPL); outbuf += 8; for ( i=0; i < BPL && (o * BPL + i ) < len; i++) { if ( i == BPL / 2 ) { sprintf(outbuf, " "); outbuf +=1; } sprintf(outbuf, "%02x", area[o * BPL + i]); outbuf += 2; } sprintf(outbuf, "%*s", 2 * ( 2 + BPL - i), " " ); outbuf += (2 * ( 2 + BPL - i)); sprintf(outbuf, "%s", (i == BPL / 2) ? " " : ""); outbuf += ((i == BPL / 2) ? 1 : 0); for ( i = 0; i < BPL && (o * BPL + i ) < len; i++) { if ( i == BPL / 2 ) { sprintf(outbuf, " "); outbuf += 1; } sprintf(outbuf, "%c", isprint(area[o * BPL + i]) ? area[o * BPL + i] : '.'); outbuf += 1; } outbuf = outputline; DBG(1, "%s\n", outbuf); } return SANE_STATUS_GOOD; } /*---------- dump_area2() ----------------------------------------------------*/ static SANE_Status dump_area2(uint8_t *area, int len, char *info) { #define BPL 16 /* bytes per line to print */ int i; char outputline[100]; char *outbuf; if ( ! info[0] ) info = "No additional info available"; DBG(1, "[%s]\n", info); outbuf = outputline; for ( i = 0; i < len; i++) { sprintf(outbuf, "%02x,", *(area + i)); outbuf += 3; if ( ((i+1)%BPL == 0) || (i == len-1) ) { outbuf = outputline; DBG(1, "%s\n", outbuf); } } return SANE_STATUS_GOOD; } /*---------- dump_to_file() --------------------------------------------------*/ /*--- only for debugging, currently not used -----*/ #if 0 static SANE_Status dump_to_file(uint8_t *area, int len, char *filename, char *mode) { FILE *out; int i; out = fopen(filename, mode); for ( i = 0; i < len; i++) fputc( *(area + i ), out); fclose(out); return SANE_STATUS_GOOD; } #endif /*---------- dump_attributes() -----------------------------------------------*/ static SANE_Status dump_attributes(Microtek2_Info *mi) { /* dump all we know about the scanner */ int i; DBG(30, "dump_attributes: mi=%p\n", (void *) mi); DBG(1, "\n"); DBG(1, "Scanner attributes from device structure\n"); DBG(1, "========================================\n"); DBG(1, "Scanner ID...\n"); DBG(1, "~~~~~~~~~~~~~\n"); DBG(1, " Vendor Name%15s: '%s'\n", " ", mi->vendor); DBG(1, " Model Name%16s: '%s'\n", " ", mi->model); DBG(1, " Revision%18s: '%s'\n", " ", mi->revision); DBG(1, " Model Code%16s: 0x%02x\n"," ", mi->model_code); switch(mi->model_code) { case 0x80: DBG(1, "Redondo 2000XL / ArtixScan 2020\n"); break; case 0x81: DBG(1, "ScanMaker 4 / Aruba\n"); break; case 0x82: DBG(1, "Bali\n"); break; case 0x83: DBG(1, "Washington\n"); break; case 0x84: DBG(1, "Manhattan\n"); break; case 0x85: DBG(1, "ScanMaker V300 / Phantom parallel / TR3\n"); break; case 0x86: DBG(1, "CCP\n"); break; case 0x87: DBG(1, "Scanmaker V\n"); break; case 0x88: DBG(1, "Scanmaker VI\n"); break; case 0x89: DBG(1, "ScanMaker 6400XL / A3-400\n"); break; case 0x8a: DBG(1, "ScanMaker 9600XL / A3-600\n"); break; case 0x8b: DBG(1, "Watt\n"); break; case 0x8c: DBG(1, "ScanMaker V600 / TR6\n"); break; case 0x8d: DBG(1, "ScanMaker V310 / Tr3 10-bit\n"); break; case 0x8e: DBG(1, "CCB\n"); break; case 0x8f: DBG(1, "Sun Rise\n"); break; case 0x90: DBG(1, "ScanMaker E3+ 10-bit\n"); break; case 0x91: DBG(1, "ScanMaker X6 / Phantom 636\n"); break; case 0x92: DBG(1, "ScanMaker E3+ / Vobis Highscan\n"); break; case 0x93: DBG(1, "ScanMaker V310\n"); break; case 0x94: DBG(1, "SlimScan C3 / Phantom 330cx / 336cx\n"); break; case 0x95: DBG(1, "ArtixScan 1010\n"); break; case 0x97: DBG(1, "ScanMaker V636\n"); break; case 0x98: DBG(1, "ScanMaker X6EL\n"); break; case 0x99: DBG(1, "ScanMaker X6 / X6USB\n"); break; case 0x9a: DBG(1, "SlimScan C6 / Phantom 636cx\n"); break; case 0x9d: DBG(1, "AGFA DuoScan T1200\n"); break; case 0xa0: DBG(1, "SlimScan C3 / Phantom 336cx\n"); break; case 0xac: DBG(1, "ScanMaker V6UL\n"); break; case 0xa3: DBG(1, "ScanMaker V6USL\n"); break; case 0xaf: DBG(1, "SlimScan C3 / Phantom 336cx\n"); break; case 0xb0: DBG(1, "ScanMaker X12USL\n"); break; case 0xb3: DBG(1, "ScanMaker 3600\n"); break; case 0xb4: DBG(1, "ScanMaker 4700\n"); break; case 0xb6: DBG(1, "ScanMaker V6UPL\n"); break; case 0xb8: DBG(1, "ScanMaker 3700\n"); break; case 0xde: DBG(1, "ScanMaker 9800XL\n"); break; default: DBG(1, "Unknown\n"); break; } DBG(1, " Device Type Code%10s: 0x%02x (%s),\n", " ", mi->device_type, mi->device_type & MI_DEVTYPE_SCANNER ? "Scanner" : "Unknown type"); switch (mi->scanner_type) { case MI_TYPE_FLATBED: DBG(1, " Scanner type%14s:%s", " ", " Flatbed scanner\n"); break; case MI_TYPE_TRANSPARENCY: DBG(1, " Scanner type%14s:%s", " ", " Transparency scanner\n"); break; case MI_TYPE_SHEEDFEED: DBG(1, " Scanner type%14s:%s", " ", " Sheet feed scanner\n"); break; default: DBG(1, " Scanner type%14s:%s", " ", " Unknown\n"); break; } DBG(1, " Supported options%9s: Automatic document feeder: %s\n", " ", mi->option_device & MI_OPTDEV_ADF ? "Yes" : "No"); DBG(1, "%30sTransparency media adapter: %s\n", " ", mi->option_device & MI_OPTDEV_TMA ? "Yes" : "No"); DBG(1, "%30sAuto paper detecting: %s\n", " ", mi->option_device & MI_OPTDEV_ADP ? "Yes" : "No"); DBG(1, "%30sAdvanced picture system: %s\n", " ", mi->option_device & MI_OPTDEV_APS ? "Yes" : "No"); DBG(1, "%30sStripes: %s\n", " ", mi->option_device & MI_OPTDEV_STRIPE ? "Yes" : "No"); DBG(1, "%30sSlides: %s\n", " ", mi->option_device & MI_OPTDEV_SLIDE ? "Yes" : "No"); DBG(1, " Scan button%15s: %s\n", " ", mi->scnbuttn ? "Yes" : "No"); DBG(1, "\n"); DBG(1, " Imaging Capabilities...\n"); DBG(1, " ~~~~~~~~~~~~~~~~~~~~~~~\n"); DBG(1, " Color scanner%6s: %s\n", " ", (mi->color) ? "Yes" : "No"); DBG(1, " Number passes%6s: %d pass%s\n", " ", (mi->onepass) ? 1 : 3, (mi->onepass) ? "" : "es"); DBG(1, " Resolution%9s: X-max: %5d dpi\n%35sY-max: %5d dpi\n", " ", mi->max_xresolution, " ",mi->max_yresolution); DBG(1, " Geometry%11s: Geometric width: %5d pts (%2.2f'')\n", " ", mi->geo_width, (float) mi->geo_width / (float) mi->opt_resolution); DBG(1, "%23sGeometric height:%5d pts (%2.2f'')\n", " ", mi->geo_height, (float) mi->geo_height / (float) mi->opt_resolution); DBG(1, " Optical resolution%1s: %d\n", " ", mi->opt_resolution); DBG(1, " Modes%14s: Lineart: %s\n%35sHalftone: %s\n", " ", (mi->scanmode & MI_HASMODE_LINEART) ? " Yes" : " No", " ", (mi->scanmode & MI_HASMODE_HALFTONE) ? "Yes" : "No"); DBG(1, "%23sGray: %s\n%35sColor: %s\n", " ", (mi->scanmode & MI_HASMODE_GRAY) ? " Yes" : " No", " ", (mi->scanmode & MI_HASMODE_COLOR) ? " Yes" : " No"); DBG(1, " Depths%14s: Nibble Gray: %s\n", " ", (mi->depth & MI_HASDEPTH_NIBBLE) ? "Yes" : "No"); DBG(1, "%23s10-bit-color: %s\n", " ", (mi->depth & MI_HASDEPTH_10) ? "Yes" : "No"); DBG(1, "%23s12-bit-color: %s\n", " ", (mi->depth & MI_HASDEPTH_12) ? "Yes" : "No"); DBG(1, "%23s14-bit-color: %s\n", " ", (mi->depth & MI_HASDEPTH_14) ? "Yes" : "No"); DBG(1, "%23s16-bit-color: %s\n", " ", (mi->depth & MI_HASDEPTH_16) ? "Yes" : "No"); DBG(1, " d/l of HT pattern%2s: %s\n", " ", (mi->has_dnldptrn) ? "Yes" : "No"); DBG(1, " Builtin HT pattern%1s: %d\n", " ", mi->grain_slct); if ( MI_LUTCAP_NONE(mi->lut_cap) ) DBG(1, " LUT capabilities : None\n"); if ( mi->lut_cap & MI_LUTCAP_256B ) DBG(1, " LUT capabilities : 256 bytes\n"); if ( mi->lut_cap & MI_LUTCAP_1024B ) DBG(1, " LUT capabilities : 1024 bytes\n"); if ( mi->lut_cap & MI_LUTCAP_1024W ) DBG(1, " LUT capabilities : 1024 words\n"); if ( mi->lut_cap & MI_LUTCAP_4096B ) DBG(1, " LUT capabilities : 4096 bytes\n"); if ( mi->lut_cap & MI_LUTCAP_4096W ) DBG(1, " LUT capabilities : 4096 words\n"); if ( mi->lut_cap & MI_LUTCAP_64k_W ) DBG(1, " LUT capabilities : 64k words\n"); if ( mi->lut_cap & MI_LUTCAP_16k_W ) DBG(1, " LUT capabilities : 16k words\n"); DBG(1, "\n"); DBG(1, " Miscellaneous capabilities...\n"); DBG(1, " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"); if ( mi->onepass) { switch(mi->data_format) { case MI_DATAFMT_CHUNKY: DBG(1, " Data format :%s", " Chunky data, R, G & B in one pixel\n"); break; case MI_DATAFMT_LPLCONCAT: DBG(1, " Data format :%s", " Line by line in concatenated sequence,\n"); DBG(1, "%23swithout color indicator\n", " "); break; case MI_DATAFMT_LPLSEGREG: DBG(1, " Data format :%s", " Line by line in segregated sequence,\n"); DBG(1, "%23swith color indicator\n", " "); break; case MI_DATAFMT_WORDCHUNKY: DBG(1, " Data format : Word chunky data\n"); break; default: DBG(1, " Data format : Unknown\n"); break; } } else DBG(1, "No information with 3-pass scanners\n"); DBG(1, " Color Sequence%17s: \n", " "); for ( i = 0; i < RSA_COLORSEQUENCE_L; i++) { switch(mi->color_sequence[i]) { case MI_COLSEQ_RED: DBG(1,"%34s%s\n", " ","R"); break; case MI_COLSEQ_GREEN: DBG(1,"%34s%s\n", " ","G"); break; case MI_COLSEQ_BLUE: DBG(1,"%34s%s\n", " ","B"); break; } } if ( mi->new_image_status == SANE_TRUE ) DBG(1, " Using new ReadImageStatus format\n"); else DBG(1, " Using old ReadImageStatus format\n"); if ( mi->direction & MI_DATSEQ_RTOL ) DBG(1, " Scanning direction : right to left\n"); else DBG(1, " Scanning direction : left to right\n"); DBG(1, " CCD gap%24s: %d lines\n", " ", mi->ccd_gap); DBG(1, " CCD pixels%21s: %d\n", " ", mi->ccd_pixels); DBG(1, " Calib white stripe location%4s: %d\n", " ", mi->calib_white); DBG(1, " Max calib space%16s: %d\n", " ", mi->calib_space); DBG(1, " Number of lens%17s: %d\n", " ", mi->nlens); DBG(1, " Max number of windows%10s: %d\n", " ", mi->nwindows); DBG(1, " Shading transfer function%6s: 0x%02x\n", " ",mi->shtrnsferequ); DBG(1, " Red balance%20s: %d\n", " ", mi->balance[0]); DBG(1, " Green balance%18s: %d\n", " ", mi->balance[1]); DBG(1, " Blue balance%19s: %d\n", " " , mi->balance[2]); DBG(1, " Buffer type%20s: %s\n", " ", mi->buftype ? "Ping-Pong" : "Ring"); DBG(1, " FEPROM%25s: %s\n", " ", mi->feprom ? "Yes" : "No"); md_dump_clear = 0; return SANE_STATUS_GOOD; } /*---------- max_string_size() -----------------------------------------------*/ static size_t max_string_size (const SANE_String_Const strings[]) { size_t size; size_t max_size = 0; int i; for (i = 0; strings[i]; ++i) { size = strlen(strings[i]) + 1; /* +1 because NUL counts as part of string */ if (size > max_size) max_size = size; } return max_size; } /*---------- parse_config_file() ---------------------------------------------*/ static void parse_config_file(FILE *fp, Config_Temp **ct) { /* builds a list of device names with associated options from the */ /* config file for later use, when building the list of devices. */ /* ct->device = NULL indicates global options (valid for all devices */ char s[PATH_MAX]; Config_Options global_opts; Config_Temp *hct1; Config_Temp *hct2; DBG(30, "parse_config_file: fp=%p\n", (void *) fp); *ct = hct1 = NULL; /* first read global options and store them in global_opts */ /* initialize global_opts with default values */ global_opts = md_options; while ( sanei_config_read(s, sizeof(s), fp) ) { DBG(100, "parse_config_file: read line: %s\n", s); if ( *s == '#' || *s == '\0' ) /* ignore empty lines and comments */ continue; if ( strncmp( sanei_config_skip_whitespace(s), "option ", 7) == 0 || strncmp( sanei_config_skip_whitespace(s), "option\t", 7) == 0 ) { DBG(100, "parse_config_file: found global option %s\n", s); check_option(s, &global_opts); } else /* it is considered a new device */ break; } if ( ferror(fp) || feof(fp) ) { if ( ferror(fp) ) DBG(1, "parse_config_file: fread failed: errno=%d\n", errno); return; } while ( ! feof(fp) && ! ferror(fp) ) { if ( *s == '#' || *s == '\0' ) /* ignore empty lines and comments */ { sanei_config_read(s, sizeof(s), fp); continue; } if ( strncmp( sanei_config_skip_whitespace(s), "option ", 7) == 0 || strncmp( sanei_config_skip_whitespace(s), "option\t", 7) == 0 ) { /* when we enter this loop for the first time we allocate */ /* memory, because the line surely contains a device name, */ /* so hct1 is always != NULL at this point */ DBG(100, "parse_config_file: found device option %s\n", s); check_option(s, &hct1->opts); } else /* it is considered a new device */ { DBG(100, "parse_config_file: found device %s\n", s); hct2 = (Config_Temp *) malloc(sizeof(Config_Temp)); if ( hct2 == NULL ) { DBG(1, "parse_config_file: malloc() failed\n"); return; } if ( *ct == NULL ) /* first element */ *ct = hct1 = hct2; hct1->next = hct2; hct1 = hct2; hct1->device = strdup(s); hct1->opts = global_opts; hct1->next = NULL; } sanei_config_read(s, sizeof(s), fp); } /* set filepointer to the beginning of the file */ fseek(fp, 0L, SEEK_SET); return; } /*---------- signal_handler() ------------------------------------------------*/ static void signal_handler (int signal) { if ( signal == SIGTERM ) { sanei_scsi_req_flush_all (); _exit (SANE_STATUS_GOOD); } } /*---------- init_options() --------------------------------------------------*/ static SANE_Status init_options(Microtek2_Scanner *ms, uint8_t current_scan_source) { /* This function is called every time, when the scan source changes. */ /* The option values, that possibly change, are then reinitialized, */ /* whereas the option descriptors and option values that never */ /* change are not */ SANE_Option_Descriptor *sod; SANE_Status status; Option_Value *val; Microtek2_Device *md; Microtek2_Info *mi; int tablesize; int option_size; int max_gamma_value; int color; int i; static int first_call = 1; /* indicates, whether option */ /* descriptors must be initialized */ /* cannot be used as after a sane_close the sod's must be initialized */ DBG(30, "init_options: handle=%p, source=%d\n", (void *) ms, current_scan_source); sod = ms->sod; val = ms->val; md = ms->dev; mi = &md->info[current_scan_source]; /* needed for gamma calculation */ get_lut_size(mi, &md->max_lut_size, &md->lut_entry_size); /* calculate new values, where possibly needed */ /* Scan source */ if ( val[OPT_SOURCE].s ) free((void *) val[OPT_SOURCE].s); i = 0; md->scansource_list[i] = (SANE_String) MD_SOURCESTRING_FLATBED; if ( current_scan_source == MD_SOURCE_FLATBED ) val[OPT_SOURCE].s = (SANE_String) strdup(md->scansource_list[i]); if ( md->status.adfcnt ) { md->scansource_list[++i] = (SANE_String) MD_SOURCESTRING_ADF; if ( current_scan_source == MD_SOURCE_ADF ) val[OPT_SOURCE].s = (SANE_String) strdup(md->scansource_list[i]); } if ( md->status.tmacnt ) { md->scansource_list[++i] = (SANE_String) MD_SOURCESTRING_TMA; if ( current_scan_source == MD_SOURCE_TMA ) val[OPT_SOURCE].s = (SANE_String) strdup(md->scansource_list[i]); } if ( mi->option_device & MI_OPTDEV_STRIPE ) { md->scansource_list[++i] = (SANE_String) MD_SOURCESTRING_STRIPE; if ( current_scan_source == MD_SOURCE_STRIPE ) val[OPT_SOURCE].s = (SANE_String) strdup(md->scansource_list[i]); } /* Comment this out as long as I do not know in which bit */ /* it is indicated, whether a slide adapter is connected */ #if 0 if ( mi->option_device & MI_OPTDEV_SLIDE ) { md->scansource_list[++i] = (SANE_String) MD_SOURCESTRING_SLIDE; if ( current_scan_source == MD_SOURCE_SLIDE ) val[OPT_SOURCE].s = (SANE_String) strdup(md->scansource_list[i]); } #endif md->scansource_list[++i] = NULL; /* Scan mode */ if ( val[OPT_MODE].s ) free((void *) val[OPT_MODE].s); i = 0; if ( (mi->scanmode & MI_HASMODE_COLOR) ) { md->scanmode_list[i] = (SANE_String) MD_MODESTRING_COLOR; val[OPT_MODE].s = strdup(md->scanmode_list[i]); ++i; } if ( mi->scanmode & MI_HASMODE_GRAY ) { md->scanmode_list[i] = (SANE_String) MD_MODESTRING_GRAY; if ( ! (mi->scanmode & MI_HASMODE_COLOR ) ) val[OPT_MODE].s = strdup(md->scanmode_list[i]); ++i; } if ( mi->scanmode & MI_HASMODE_HALFTONE ) { md->scanmode_list[i] = (SANE_String) MD_MODESTRING_HALFTONE; if ( ! (mi->scanmode & MI_HASMODE_COLOR ) && ! (mi->scanmode & MI_HASMODE_GRAY ) ) val[OPT_MODE].s = strdup(md->scanmode_list[i]); ++i; } /* Always enable a lineart mode. Some models (X6, FW 1.40) say */ /* that they have no lineart mode. In this case we will do a grayscale */ /* scan and convert it to onebit data */ md->scanmode_list[i] = (SANE_String) MD_MODESTRING_LINEART; if ( ! (mi->scanmode & MI_HASMODE_COLOR ) && ! (mi->scanmode & MI_HASMODE_GRAY ) && ! (mi->scanmode & MI_HASMODE_HALFTONE ) ) val[OPT_MODE].s = strdup(md->scanmode_list[i]); ++i; md->scanmode_list[i] = NULL; /* bitdepth */ i = 0; #if 0 if ( mi->depth & MI_HASDEPTH_NIBBLE ) md->bitdepth_list[++i] = (SANE_Int) MD_DEPTHVAL_4; #endif md->bitdepth_list[++i] = (SANE_Int) MD_DEPTHVAL_8; if ( mi->depth & MI_HASDEPTH_10 ) md->bitdepth_list[++i] = (SANE_Int) MD_DEPTHVAL_10; if ( mi->depth & MI_HASDEPTH_12 ) md->bitdepth_list[++i] = (SANE_Int) MD_DEPTHVAL_12; if ( mi->depth & MI_HASDEPTH_14 ) md->bitdepth_list[++i] = (SANE_Int) MD_DEPTHVAL_14; if ( mi->depth & MI_HASDEPTH_16 ) md->bitdepth_list[++i] = (SANE_Int) MD_DEPTHVAL_16; md->bitdepth_list[0] = i; if ( md->bitdepth_list[1] == (SANE_Int) MD_DEPTHVAL_8 ) val[OPT_BITDEPTH].w = md->bitdepth_list[1]; else val[OPT_BITDEPTH].w = md->bitdepth_list[2]; /* Halftone */ md->halftone_mode_list[0] = (SANE_String) MD_HALFTONE0; md->halftone_mode_list[1] = (SANE_String) MD_HALFTONE1; md->halftone_mode_list[2] = (SANE_String) MD_HALFTONE2; md->halftone_mode_list[3] = (SANE_String) MD_HALFTONE3; md->halftone_mode_list[4] = (SANE_String) MD_HALFTONE4; md->halftone_mode_list[5] = (SANE_String) MD_HALFTONE5; md->halftone_mode_list[6] = (SANE_String) MD_HALFTONE6; md->halftone_mode_list[7] = (SANE_String) MD_HALFTONE7; md->halftone_mode_list[8] = (SANE_String) MD_HALFTONE8; md->halftone_mode_list[9] = (SANE_String) MD_HALFTONE9; md->halftone_mode_list[10] = (SANE_String) MD_HALFTONE10; md->halftone_mode_list[11] = (SANE_String) MD_HALFTONE11; md->halftone_mode_list[12] = NULL; if ( val[OPT_HALFTONE].s ) free((void *) val[OPT_HALFTONE].s); val[OPT_HALFTONE].s = strdup(md->halftone_mode_list[0]); /* Resolution */ md->x_res_range_dpi.min = SANE_FIX(10.0); 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); md->y_res_range_dpi.min = SANE_FIX(10.0); md->y_res_range_dpi.max = SANE_FIX(mi->max_yresolution); md->y_res_range_dpi.quant = SANE_FIX(1.0); val[OPT_Y_RESOLUTION].w = val[OPT_RESOLUTION].w; /* bind is default */ /* Preview mode */ val[OPT_PREVIEW].w = SANE_FALSE; /* Geometry */ md->x_range_mm.min = SANE_FIX(0.0); md->x_range_mm.max = SANE_FIX((double) mi->geo_width / (double) mi->opt_resolution * MM_PER_INCH); md->x_range_mm.quant = SANE_FIX(0.0); md->y_range_mm.min = SANE_FIX(0.0); md->y_range_mm.max = SANE_FIX((double) mi->geo_height / (double) mi->opt_resolution * MM_PER_INCH); md->y_range_mm.quant = SANE_FIX(0.0); val[OPT_TL_X].w = SANE_FIX(0.0); val[OPT_TL_Y].w = SANE_FIX(0.0); val[OPT_BR_X].w = md->x_range_mm.max; val[OPT_BR_Y].w = md->y_range_mm.max; /* Enhancement group */ val[OPT_BRIGHTNESS].w = MD_BRIGHTNESS_DEFAULT; val[OPT_CONTRAST].w = MD_CONTRAST_DEFAULT; val[OPT_THRESHOLD].w = MD_THRESHOLD_DEFAULT; /* Gamma */ /* linear gamma must come first */ i = 0; md->gammamode_list[i++] = (SANE_String) MD_GAMMAMODE_LINEAR; md->gammamode_list[i++] = (SANE_String) MD_GAMMAMODE_SCALAR; md->gammamode_list[i++] = (SANE_String) MD_GAMMAMODE_CUSTOM; if ( val[OPT_GAMMA_MODE].s ) free((void *) val[OPT_GAMMA_MODE].s); val[OPT_GAMMA_MODE].s = strdup(md->gammamode_list[0]); md->gammamode_list[i] = NULL; /* bind gamma */ val[OPT_GAMMA_BIND].w = SANE_TRUE; val[OPT_GAMMA_SCALAR].w = MD_GAMMA_DEFAULT; val[OPT_GAMMA_SCALAR_R].w = MD_GAMMA_DEFAULT; val[OPT_GAMMA_SCALAR_G].w = MD_GAMMA_DEFAULT; val[OPT_GAMMA_SCALAR_B].w = MD_GAMMA_DEFAULT; /* If the device supports gamma tables, we allocate memory according */ /* to lookup table capabilities, otherwise we allocate 4096 elements */ /* which is sufficient for a color depth of 12. If the device */ /* does not support gamma tables, we fill the table according to */ /* the actual bit depth, i.e. 256 entries with a range of 0..255 */ /* if the actual bit depth is 8, for example. This will hopefully*/ /* make no trouble if the bit depth is 1. */ if ( md->model_flags & MD_NO_GAMMA ) { tablesize = 4096; option_size = (int) pow(2.0, (double) val[OPT_BITDEPTH].w ); max_gamma_value = option_size - 1; } else { tablesize = md->max_lut_size; option_size = tablesize; max_gamma_value = md->max_lut_size - 1; } for ( color = 0; color < 4; color++ ) { /* index 0 is used if bind gamma == true, index 1 to 3 */ /* if bind gamma == false */ if ( md->custom_gamma_table[color] ) free((void *) md->custom_gamma_table[color]); md->custom_gamma_table[color] = (SANE_Int *) malloc(tablesize * sizeof(SANE_Int)); DBG(100, "init_options: md->custom_gamma_table[%d]=%p, malloc'd %lu bytes\n", color, (void *) md->custom_gamma_table[color], (u_long) (tablesize * sizeof(SANE_Int))); if ( md->custom_gamma_table[color] == NULL ) { DBG(1, "init_options: malloc for custom gamma table failed\n"); return SANE_STATUS_NO_MEM; } for ( i = 0; i < max_gamma_value; i++ ) md->custom_gamma_table[color][i] = i; } md->custom_gamma_range.min = 0; md->custom_gamma_range.max = max_gamma_value; md->custom_gamma_range.quant = 1; sod[OPT_GAMMA_CUSTOM].size = option_size * sizeof (SANE_Int); sod[OPT_GAMMA_CUSTOM_R].size = option_size * sizeof (SANE_Int); sod[OPT_GAMMA_CUSTOM_G].size = option_size * sizeof (SANE_Int); sod[OPT_GAMMA_CUSTOM_B].size = option_size * sizeof (SANE_Int); val[OPT_GAMMA_CUSTOM].wa = &md->custom_gamma_table[0][0]; val[OPT_GAMMA_CUSTOM_R].wa = &md->custom_gamma_table[1][0]; val[OPT_GAMMA_CUSTOM_G].wa = &md->custom_gamma_table[2][0]; val[OPT_GAMMA_CUSTOM_B].wa = &md->custom_gamma_table[3][0]; /* Shadow, midtone, highlight, exposure time */ md->channel_list[0] = (SANE_String) MD_CHANNEL_MASTER; md->channel_list[1] = (SANE_String) MD_CHANNEL_RED; md->channel_list[2] = (SANE_String) MD_CHANNEL_GREEN; md->channel_list[3] = (SANE_String) MD_CHANNEL_BLUE; md->channel_list[4] = NULL; if ( val[OPT_CHANNEL].s ) free((void *) val[OPT_CHANNEL].s); val[OPT_CHANNEL].s = strdup(md->channel_list[0]); val[OPT_SHADOW].w = MD_SHADOW_DEFAULT; val[OPT_SHADOW_R].w = MD_SHADOW_DEFAULT; val[OPT_SHADOW_G].w = MD_SHADOW_DEFAULT; val[OPT_SHADOW_B].w = MD_SHADOW_DEFAULT; val[OPT_MIDTONE].w = MD_MIDTONE_DEFAULT; val[OPT_MIDTONE_R].w = MD_MIDTONE_DEFAULT; val[OPT_MIDTONE_G].w = MD_MIDTONE_DEFAULT; val[OPT_MIDTONE_B].w = MD_MIDTONE_DEFAULT; val[OPT_HIGHLIGHT].w = MD_HIGHLIGHT_DEFAULT; val[OPT_HIGHLIGHT_R].w = MD_HIGHLIGHT_DEFAULT; val[OPT_HIGHLIGHT_G].w = MD_HIGHLIGHT_DEFAULT; val[OPT_HIGHLIGHT_B].w = MD_HIGHLIGHT_DEFAULT; val[OPT_EXPOSURE].w = MD_EXPOSURE_DEFAULT; val[OPT_EXPOSURE_R].w = MD_EXPOSURE_DEFAULT; val[OPT_EXPOSURE_G].w = MD_EXPOSURE_DEFAULT; val[OPT_EXPOSURE_B].w = MD_EXPOSURE_DEFAULT; /* special options */ val[OPT_RESOLUTION_BIND].w = SANE_TRUE; /* enable/disable option for backtracking */ val[OPT_DISABLE_BACKTRACK].w = md->opt_no_backtrack_default; /* enable/disable calibration by backend */ val[OPT_CALIB_BACKEND].w = md->opt_backend_calib_default; /* turn off the lamp during a scan */ val[OPT_LIGHTLID35].w = SANE_FALSE; /* auto adjustment of threshold during a lineart scan */ val[OPT_AUTOADJUST].w = SANE_FALSE; /* color balance (100% means no correction) */ val[OPT_BALANCE_R].w = SANE_FIX(100); val[OPT_BALANCE_G].w = SANE_FIX(100); val[OPT_BALANCE_B].w = SANE_FIX(100); if ( first_call ) { /* initialize option descriptors and ranges */ /* Percentage range for brightness, contrast */ md->percentage_range.min = 0 << SANE_FIXED_SCALE_SHIFT; md->percentage_range.max = 200 << SANE_FIXED_SCALE_SHIFT; md->percentage_range.quant = 1 << SANE_FIXED_SCALE_SHIFT; md->threshold_range.min = 1; md->threshold_range.max = 255; md->threshold_range.quant = 1; md->scalar_gamma_range.min = SANE_FIX(0.1); md->scalar_gamma_range.max = SANE_FIX(4.0); md->scalar_gamma_range.quant = SANE_FIX(0.1); md->shadow_range.min = 0; md->shadow_range.max = 253; md->shadow_range.quant = 1; md->midtone_range.min = 1; md->midtone_range.max = 254; md->midtone_range.quant = 1; md->highlight_range.min = 2; md->highlight_range.max = 255; md->highlight_range.quant = 1; md->exposure_range.min = 0; md->exposure_range.max = 510; md->exposure_range.quant = 2; md->balance_range.min = 0; md->balance_range.max = 200 << SANE_FIXED_SCALE_SHIFT; md->balance_range.quant = 1 << SANE_FIXED_SCALE_SHIFT; /* default for most options */ for ( i = 0; i < NUM_OPTIONS; i++ ) { sod[i].type = SANE_TYPE_FIXED; sod[i].unit = SANE_UNIT_NONE; sod[i].size = sizeof(SANE_Fixed); sod[i].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; sod[i].constraint_type = SANE_CONSTRAINT_RANGE; } sod[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS; sod[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; sod[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; sod[OPT_NUM_OPTS].type = SANE_TYPE_INT; sod[OPT_NUM_OPTS].size = sizeof (SANE_Int); sod[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; sod[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE; val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* NUM_OPTIONS is no option */ DBG(255, "sod=%p\n", (void *) sod); DBG(255, "OPT_NUM_OPTS=%d\n", OPT_NUM_OPTS); DBG(255, "SANE_CAP_SOFT_DETECT=%d\n", SANE_CAP_SOFT_DETECT); DBG(255, "OPT_NUM_OPTS.cap=%d\n", sod[0].cap); /* The Scan Mode Group */ sod[OPT_MODE_GROUP].title = M_TITLE_SCANMODEGRP; sod[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; sod[OPT_MODE_GROUP].size = 0; sod[OPT_MODE_GROUP].desc = ""; sod[OPT_MODE_GROUP].cap = 0; sod[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* Scan source */ sod[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE; sod[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE; sod[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE; sod[OPT_SOURCE].type = SANE_TYPE_STRING; sod[OPT_SOURCE].size = max_string_size(md->scansource_list); /* if there is only one scan source, deactivate option */ if ( md->scansource_list[1] == NULL ) sod[OPT_SOURCE].cap |= SANE_CAP_INACTIVE; sod[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; sod[OPT_SOURCE].constraint.string_list = md->scansource_list; /* Scan mode */ sod[OPT_MODE].name = SANE_NAME_SCAN_MODE; sod[OPT_MODE].title = SANE_TITLE_SCAN_MODE; sod[OPT_MODE].desc = SANE_DESC_SCAN_MODE; sod[OPT_MODE].type = SANE_TYPE_STRING; sod[OPT_MODE].size = max_string_size(md->scanmode_list); sod[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; sod[OPT_MODE].constraint.string_list = md->scanmode_list; /* Bit depth */ sod[OPT_BITDEPTH].name = SANE_NAME_BIT_DEPTH; sod[OPT_BITDEPTH].title = SANE_TITLE_BIT_DEPTH; sod[OPT_BITDEPTH].desc = SANE_DESC_BIT_DEPTH; sod[OPT_BITDEPTH].type = SANE_TYPE_INT; sod[OPT_BITDEPTH].unit = SANE_UNIT_BIT; sod[OPT_BITDEPTH].size = sizeof(SANE_Int); /* if we have only 8 bit color deactivate this option */ if ( md->bitdepth_list[0] == 1 ) sod[OPT_BITDEPTH].cap |= SANE_CAP_INACTIVE; sod[OPT_BITDEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST; sod[OPT_BITDEPTH].constraint.word_list = md->bitdepth_list; /* Halftone */ sod[OPT_HALFTONE].name = SANE_NAME_HALFTONE; sod[OPT_HALFTONE].title = SANE_TITLE_HALFTONE; sod[OPT_HALFTONE].desc = SANE_DESC_HALFTONE; sod[OPT_HALFTONE].type = SANE_TYPE_STRING; sod[OPT_HALFTONE].size = max_string_size(md->halftone_mode_list); sod[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE; sod[OPT_HALFTONE].constraint_type = SANE_CONSTRAINT_STRING_LIST; 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_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_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_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; /* Preview */ sod[OPT_PREVIEW].name = SANE_NAME_PREVIEW; sod[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; sod[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; sod[OPT_PREVIEW].type = SANE_TYPE_BOOL; sod[OPT_PREVIEW].size = sizeof(SANE_Bool); sod[OPT_PREVIEW].constraint_type = SANE_CONSTRAINT_NONE; /* Geometry group, for scan area selection */ sod[OPT_GEOMETRY_GROUP].title = M_TITLE_GEOMGRP; sod[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; sod[OPT_GEOMETRY_GROUP].size = 0; sod[OPT_GEOMETRY_GROUP].desc = ""; sod[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; sod[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; sod[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; sod[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; sod[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; sod[OPT_TL_X].unit = SANE_UNIT_MM; sod[OPT_TL_X].constraint.range = &md->x_range_mm; sod[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; sod[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; sod[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; sod[OPT_TL_Y].unit = SANE_UNIT_MM; sod[OPT_TL_Y].constraint.range = &md->y_range_mm; sod[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; sod[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; sod[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; sod[OPT_BR_X].unit = SANE_UNIT_MM; sod[OPT_BR_X].constraint.range = &md->x_range_mm; sod[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; sod[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; sod[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; sod[OPT_BR_Y].unit = SANE_UNIT_MM; sod[OPT_BR_Y].constraint.range = &md->y_range_mm; /* Enhancement group */ sod[OPT_ENHANCEMENT_GROUP].title = M_TITLE_ENHANCEGRP; sod[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; sod[OPT_ENHANCEMENT_GROUP].desc = ""; sod[OPT_ENHANCEMENT_GROUP].size = 0; sod[OPT_ENHANCEMENT_GROUP].cap = 0; sod[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; sod[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; sod[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; sod[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; sod[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT; sod[OPT_BRIGHTNESS].constraint.range = &md->percentage_range; sod[OPT_CONTRAST].name = SANE_NAME_CONTRAST; sod[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; sod[OPT_CONTRAST].desc = SANE_DESC_CONTRAST; sod[OPT_CONTRAST].unit = SANE_UNIT_PERCENT; sod[OPT_CONTRAST].constraint.range = &md->percentage_range; sod[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD; sod[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD; sod[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD; sod[OPT_THRESHOLD].type = SANE_TYPE_INT; sod[OPT_THRESHOLD].size = sizeof(SANE_Int); sod[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; sod[OPT_THRESHOLD].constraint.range = &md->threshold_range; /* automatically adjust threshold for a lineart scan */ sod[OPT_AUTOADJUST].name = M_NAME_AUTOADJUST; sod[OPT_AUTOADJUST].title = M_TITLE_AUTOADJUST; sod[OPT_AUTOADJUST].desc = M_DESC_AUTOADJUST; sod[OPT_AUTOADJUST].type = SANE_TYPE_BOOL; sod[OPT_AUTOADJUST].size = sizeof(SANE_Bool); sod[OPT_AUTOADJUST].constraint_type = SANE_CONSTRAINT_NONE; if ( strncmp(md->opts.auto_adjust, "off", 3) == 0 ) sod[OPT_AUTOADJUST].cap |= SANE_CAP_INACTIVE; /* Gamma */ sod[OPT_GAMMA_GROUP].title = "Gamma"; sod[OPT_GAMMA_GROUP].desc = ""; sod[OPT_GAMMA_GROUP].type = SANE_TYPE_GROUP; sod[OPT_GAMMA_GROUP].size = 0; sod[OPT_GAMMA_GROUP].cap = 0; sod[OPT_GAMMA_GROUP].constraint_type = SANE_CONSTRAINT_NONE; sod[OPT_GAMMA_MODE].name = M_NAME_GAMMA_MODE; sod[OPT_GAMMA_MODE].title = M_TITLE_GAMMA_MODE; sod[OPT_GAMMA_MODE].desc = M_DESC_GAMMA_MODE; sod[OPT_GAMMA_MODE].type = SANE_TYPE_STRING; sod[OPT_GAMMA_MODE].size = max_string_size(md->gammamode_list); sod[OPT_GAMMA_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; sod[OPT_GAMMA_MODE].constraint.string_list = md->gammamode_list; sod[OPT_GAMMA_BIND].name = M_NAME_GAMMA_BIND; sod[OPT_GAMMA_BIND].title = M_TITLE_GAMMA_BIND; sod[OPT_GAMMA_BIND].desc = M_DESC_GAMMA_BIND; sod[OPT_GAMMA_BIND].type = SANE_TYPE_BOOL; sod[OPT_GAMMA_BIND].size = sizeof(SANE_Bool); sod[OPT_GAMMA_BIND].constraint_type = SANE_CONSTRAINT_NONE; /* this is active if gamma_bind == true and gammamode == scalar */ sod[OPT_GAMMA_SCALAR].name = M_NAME_GAMMA_SCALAR; sod[OPT_GAMMA_SCALAR].title = M_TITLE_GAMMA_SCALAR; sod[OPT_GAMMA_SCALAR].desc = M_DESC_GAMMA_SCALAR; sod[OPT_GAMMA_SCALAR].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR].constraint.range = &md->scalar_gamma_range; sod[OPT_GAMMA_SCALAR_R].name = M_NAME_GAMMA_SCALAR_R; sod[OPT_GAMMA_SCALAR_R].title = M_TITLE_GAMMA_SCALAR_R; sod[OPT_GAMMA_SCALAR_R].desc = M_DESC_GAMMA_SCALAR_R; sod[OPT_GAMMA_SCALAR_R].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR_R].constraint.range = &md->scalar_gamma_range; sod[OPT_GAMMA_SCALAR_G].name = M_NAME_GAMMA_SCALAR_G; sod[OPT_GAMMA_SCALAR_G].title = M_TITLE_GAMMA_SCALAR_G; sod[OPT_GAMMA_SCALAR_G].desc = M_DESC_GAMMA_SCALAR_G; sod[OPT_GAMMA_SCALAR_G].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR_G].constraint.range = &md->scalar_gamma_range; sod[OPT_GAMMA_SCALAR_B].name = M_NAME_GAMMA_SCALAR_B; sod[OPT_GAMMA_SCALAR_B].title = M_TITLE_GAMMA_SCALAR_B; sod[OPT_GAMMA_SCALAR_B].desc = M_DESC_GAMMA_SCALAR_B; sod[OPT_GAMMA_SCALAR_B].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR_B].constraint.range = &md->scalar_gamma_range; sod[OPT_GAMMA_CUSTOM].name = SANE_NAME_GAMMA_VECTOR; sod[OPT_GAMMA_CUSTOM].title = SANE_TITLE_GAMMA_VECTOR; sod[OPT_GAMMA_CUSTOM].desc = SANE_DESC_GAMMA_VECTOR; sod[OPT_GAMMA_CUSTOM].type = SANE_TYPE_INT; sod[OPT_GAMMA_CUSTOM].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM].size = option_size * sizeof (SANE_Int); sod[OPT_GAMMA_CUSTOM].constraint.range = &md->custom_gamma_range; sod[OPT_GAMMA_CUSTOM_R].name = SANE_NAME_GAMMA_VECTOR_R; sod[OPT_GAMMA_CUSTOM_R].title = SANE_TITLE_GAMMA_VECTOR_R; sod[OPT_GAMMA_CUSTOM_R].desc = SANE_DESC_GAMMA_VECTOR_R; sod[OPT_GAMMA_CUSTOM_R].type = SANE_TYPE_INT; sod[OPT_GAMMA_CUSTOM_R].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM_R].size = option_size * sizeof (SANE_Int); sod[OPT_GAMMA_CUSTOM_R].constraint.range = &md->custom_gamma_range; sod[OPT_GAMMA_CUSTOM_G].name = SANE_NAME_GAMMA_VECTOR_G; sod[OPT_GAMMA_CUSTOM_G].title = SANE_TITLE_GAMMA_VECTOR_G; sod[OPT_GAMMA_CUSTOM_G].desc = SANE_DESC_GAMMA_VECTOR_G; sod[OPT_GAMMA_CUSTOM_G].type = SANE_TYPE_INT; sod[OPT_GAMMA_CUSTOM_G].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM_G].size = option_size * sizeof (SANE_Int); sod[OPT_GAMMA_CUSTOM_G].constraint.range = &md->custom_gamma_range; sod[OPT_GAMMA_CUSTOM_B].name = SANE_NAME_GAMMA_VECTOR_B; sod[OPT_GAMMA_CUSTOM_B].title = SANE_TITLE_GAMMA_VECTOR_B; sod[OPT_GAMMA_CUSTOM_B].desc = SANE_DESC_GAMMA_VECTOR_B; sod[OPT_GAMMA_CUSTOM_B].type = SANE_TYPE_INT; sod[OPT_GAMMA_CUSTOM_B].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM_B].size = option_size * sizeof (SANE_Int); sod[OPT_GAMMA_CUSTOM_B].constraint.range = &md->custom_gamma_range; /* Shadow, midtone, highlight */ sod[OPT_SMH_GROUP].title = M_TITLE_SMHGRP; sod[OPT_SMH_GROUP].desc = ""; sod[OPT_SMH_GROUP].type = SANE_TYPE_GROUP; sod[OPT_SMH_GROUP].size = 0; sod[OPT_SMH_GROUP].cap = 0; sod[OPT_SMH_GROUP].constraint_type = SANE_CONSTRAINT_NONE; sod[OPT_CHANNEL].name = M_NAME_CHANNEL; sod[OPT_CHANNEL].title = M_TITLE_CHANNEL; sod[OPT_CHANNEL].desc = M_DESC_CHANNEL; sod[OPT_CHANNEL].type = SANE_TYPE_STRING; sod[OPT_CHANNEL].size = max_string_size(md->channel_list); sod[OPT_CHANNEL].constraint_type = SANE_CONSTRAINT_STRING_LIST; sod[OPT_CHANNEL].constraint.string_list = md->channel_list; sod[OPT_SHADOW].name = SANE_NAME_SHADOW; sod[OPT_SHADOW].title = SANE_TITLE_SHADOW; sod[OPT_SHADOW].desc = SANE_DESC_SHADOW; sod[OPT_SHADOW].type = SANE_TYPE_INT; sod[OPT_SHADOW].size = sizeof(SANE_Int); sod[OPT_SHADOW].constraint.range = &md->shadow_range; sod[OPT_SHADOW_R].name = SANE_NAME_SHADOW_R; sod[OPT_SHADOW_R].title = SANE_TITLE_SHADOW_R; sod[OPT_SHADOW_R].desc = SANE_DESC_SHADOW_R; sod[OPT_SHADOW_R].type = SANE_TYPE_INT; sod[OPT_SHADOW_R].size = sizeof(SANE_Int); sod[OPT_SHADOW_R].constraint.range = &md->shadow_range; sod[OPT_SHADOW_G].name = SANE_NAME_SHADOW_G; sod[OPT_SHADOW_G].title = SANE_TITLE_SHADOW_G; sod[OPT_SHADOW_G].desc = SANE_DESC_SHADOW_G; sod[OPT_SHADOW_G].type = SANE_TYPE_INT; sod[OPT_SHADOW_G].size = sizeof(SANE_Int); sod[OPT_SHADOW_G].constraint.range = &md->shadow_range; sod[OPT_SHADOW_B].name = SANE_NAME_SHADOW_B; sod[OPT_SHADOW_B].title = SANE_TITLE_SHADOW_B; sod[OPT_SHADOW_B].desc = SANE_DESC_SHADOW_B; sod[OPT_SHADOW_B].type = SANE_TYPE_INT; sod[OPT_SHADOW_B].size = sizeof(SANE_Int); sod[OPT_SHADOW_B].constraint.range = &md->shadow_range; sod[OPT_MIDTONE].name = M_NAME_MIDTONE; sod[OPT_MIDTONE].title = M_TITLE_MIDTONE; sod[OPT_MIDTONE].desc = M_DESC_MIDTONE; sod[OPT_MIDTONE].type = SANE_TYPE_INT; sod[OPT_MIDTONE].size = sizeof(SANE_Int); sod[OPT_MIDTONE].constraint.range = &md->midtone_range; sod[OPT_MIDTONE_R].name = M_NAME_MIDTONE_R; sod[OPT_MIDTONE_R].title = M_TITLE_MIDTONE_R; sod[OPT_MIDTONE_R].desc = M_DESC_MIDTONE_R; sod[OPT_MIDTONE_R].type = SANE_TYPE_INT; sod[OPT_MIDTONE_R].size = sizeof(SANE_Int); sod[OPT_MIDTONE_R].constraint.range = &md->midtone_range; sod[OPT_MIDTONE_G].name = M_NAME_MIDTONE_G; sod[OPT_MIDTONE_G].title = M_TITLE_MIDTONE_G; sod[OPT_MIDTONE_G].desc = M_DESC_MIDTONE_G; sod[OPT_MIDTONE_G].type = SANE_TYPE_INT; sod[OPT_MIDTONE_G].size = sizeof(SANE_Int); sod[OPT_MIDTONE_G].constraint.range = &md->midtone_range; sod[OPT_MIDTONE_B].name = M_NAME_MIDTONE_B; sod[OPT_MIDTONE_B].title = M_TITLE_MIDTONE_B; sod[OPT_MIDTONE_B].desc = M_DESC_MIDTONE_B; sod[OPT_MIDTONE_B].type = SANE_TYPE_INT; sod[OPT_MIDTONE_B].size = sizeof(SANE_Int); sod[OPT_MIDTONE_B].constraint.range = &md->midtone_range; sod[OPT_HIGHLIGHT].name = SANE_NAME_HIGHLIGHT; sod[OPT_HIGHLIGHT].title = SANE_TITLE_HIGHLIGHT; sod[OPT_HIGHLIGHT].desc = SANE_DESC_HIGHLIGHT; sod[OPT_HIGHLIGHT].type = SANE_TYPE_INT; sod[OPT_HIGHLIGHT].size = sizeof(SANE_Int); sod[OPT_HIGHLIGHT].constraint.range = &md->highlight_range; sod[OPT_HIGHLIGHT_R].name = SANE_NAME_HIGHLIGHT_R; sod[OPT_HIGHLIGHT_R].title = SANE_TITLE_HIGHLIGHT_R; sod[OPT_HIGHLIGHT_R].desc = SANE_DESC_HIGHLIGHT_R; sod[OPT_HIGHLIGHT_R].type = SANE_TYPE_INT; sod[OPT_HIGHLIGHT_R].size = sizeof(SANE_Int); sod[OPT_HIGHLIGHT_R].constraint.range = &md->highlight_range; sod[OPT_HIGHLIGHT_G].name = SANE_NAME_HIGHLIGHT_G; sod[OPT_HIGHLIGHT_G].title = SANE_TITLE_HIGHLIGHT_G; sod[OPT_HIGHLIGHT_G].desc = SANE_DESC_HIGHLIGHT_G; sod[OPT_HIGHLIGHT_G].type = SANE_TYPE_INT; sod[OPT_HIGHLIGHT_G].size = sizeof(SANE_Int); sod[OPT_HIGHLIGHT_G].constraint.range = &md->highlight_range; sod[OPT_HIGHLIGHT_B].name = SANE_NAME_HIGHLIGHT_B; sod[OPT_HIGHLIGHT_B].title = SANE_TITLE_HIGHLIGHT_B; sod[OPT_HIGHLIGHT_B].desc = SANE_DESC_HIGHLIGHT_B; sod[OPT_HIGHLIGHT_B].type = SANE_TYPE_INT; sod[OPT_HIGHLIGHT_B].size = sizeof(SANE_Int); sod[OPT_HIGHLIGHT_B].constraint.range = &md->highlight_range; sod[OPT_EXPOSURE].name = SANE_NAME_SCAN_EXPOS_TIME; sod[OPT_EXPOSURE].title = SANE_TITLE_SCAN_EXPOS_TIME; sod[OPT_EXPOSURE].desc = SANE_DESC_SCAN_EXPOS_TIME; sod[OPT_EXPOSURE].type = SANE_TYPE_INT; sod[OPT_EXPOSURE].unit = SANE_UNIT_PERCENT; sod[OPT_EXPOSURE].size = sizeof(SANE_Int); sod[OPT_EXPOSURE].constraint.range = &md->exposure_range; sod[OPT_EXPOSURE_R].name = SANE_NAME_SCAN_EXPOS_TIME_R; sod[OPT_EXPOSURE_R].title = SANE_TITLE_SCAN_EXPOS_TIME_R; sod[OPT_EXPOSURE_R].desc = SANE_DESC_SCAN_EXPOS_TIME_R; sod[OPT_EXPOSURE_R].type = SANE_TYPE_INT; sod[OPT_EXPOSURE_R].unit = SANE_UNIT_PERCENT; sod[OPT_EXPOSURE_R].size = sizeof(SANE_Int); sod[OPT_EXPOSURE_R].constraint.range = &md->exposure_range; sod[OPT_EXPOSURE_G].name = SANE_NAME_SCAN_EXPOS_TIME_G; sod[OPT_EXPOSURE_G].title = SANE_TITLE_SCAN_EXPOS_TIME_G; sod[OPT_EXPOSURE_G].desc = SANE_DESC_SCAN_EXPOS_TIME_G; sod[OPT_EXPOSURE_G].type = SANE_TYPE_INT; sod[OPT_EXPOSURE_G].unit = SANE_UNIT_PERCENT; sod[OPT_EXPOSURE_G].size = sizeof(SANE_Int); sod[OPT_EXPOSURE_G].constraint.range = &md->exposure_range; sod[OPT_EXPOSURE_B].name = SANE_NAME_SCAN_EXPOS_TIME_B; sod[OPT_EXPOSURE_B].title = SANE_TITLE_SCAN_EXPOS_TIME_B; sod[OPT_EXPOSURE_B].desc = SANE_DESC_SCAN_EXPOS_TIME_B; sod[OPT_EXPOSURE_B].type = SANE_TYPE_INT; sod[OPT_EXPOSURE_B].unit = SANE_UNIT_PERCENT; sod[OPT_EXPOSURE_B].size = sizeof(SANE_Int); sod[OPT_EXPOSURE_B].constraint.range = &md->exposure_range; /* The Special Options Group */ sod[OPT_SPECIAL].title = M_TITLE_SPECIALGRP; sod[OPT_SPECIAL].type = SANE_TYPE_GROUP; sod[OPT_SPECIAL].size = 0; sod[OPT_SPECIAL].desc = ""; sod[OPT_SPECIAL].cap = SANE_CAP_ADVANCED; sod[OPT_SPECIAL].constraint_type = SANE_CONSTRAINT_NONE; sod[OPT_RESOLUTION_BIND].name = SANE_NAME_RESOLUTION_BIND; sod[OPT_RESOLUTION_BIND].title = SANE_TITLE_RESOLUTION_BIND; sod[OPT_RESOLUTION_BIND].desc = SANE_DESC_RESOLUTION_BIND; sod[OPT_RESOLUTION_BIND].type = SANE_TYPE_BOOL; sod[OPT_RESOLUTION_BIND].size = sizeof(SANE_Bool); sod[OPT_RESOLUTION_BIND].cap |= SANE_CAP_ADVANCED; sod[OPT_RESOLUTION_BIND].constraint_type = SANE_CONSTRAINT_NONE; /* enable/disable option for backtracking */ sod[OPT_DISABLE_BACKTRACK].name = M_NAME_NOBACKTRACK; sod[OPT_DISABLE_BACKTRACK].title = M_TITLE_NOBACKTRACK; sod[OPT_DISABLE_BACKTRACK].desc = M_DESC_NOBACKTRACK; sod[OPT_DISABLE_BACKTRACK].type = SANE_TYPE_BOOL; sod[OPT_DISABLE_BACKTRACK].size = sizeof(SANE_Bool); sod[OPT_DISABLE_BACKTRACK].cap |= SANE_CAP_ADVANCED; sod[OPT_DISABLE_BACKTRACK].constraint_type = SANE_CONSTRAINT_NONE; if ( strncmp(md->opts.no_backtracking, "off", 3) == 0 ) sod[OPT_DISABLE_BACKTRACK].cap |= SANE_CAP_INACTIVE; /* calibration by driver */ sod[OPT_CALIB_BACKEND].name = M_NAME_CALIBBACKEND; sod[OPT_CALIB_BACKEND].title = M_TITLE_CALIBBACKEND; sod[OPT_CALIB_BACKEND].desc = M_DESC_CALIBBACKEND; sod[OPT_CALIB_BACKEND].type = SANE_TYPE_BOOL; sod[OPT_CALIB_BACKEND].size = sizeof(SANE_Bool); sod[OPT_CALIB_BACKEND].cap |= SANE_CAP_ADVANCED; sod[OPT_CALIB_BACKEND].constraint_type = SANE_CONSTRAINT_NONE; if ( strncmp(md->opts.backend_calibration, "off", 3) == 0 ) sod[OPT_CALIB_BACKEND].cap |= SANE_CAP_INACTIVE; /* turn off the lamp of the flatbed during a scan */ sod[OPT_LIGHTLID35].name = M_NAME_LIGHTLID35; sod[OPT_LIGHTLID35].title = M_TITLE_LIGHTLID35; sod[OPT_LIGHTLID35].desc = M_DESC_LIGHTLID35; sod[OPT_LIGHTLID35].type = SANE_TYPE_BOOL; sod[OPT_LIGHTLID35].size = sizeof(SANE_Bool); sod[OPT_LIGHTLID35].cap |= SANE_CAP_ADVANCED; sod[OPT_LIGHTLID35].constraint_type = SANE_CONSTRAINT_NONE; if ( strncmp(md->opts.lightlid35, "off", 3) == 0 ) sod[OPT_LIGHTLID35].cap |= SANE_CAP_INACTIVE; /* toggle the lamp of the flatbed */ sod[OPT_TOGGLELAMP].name = M_NAME_TOGGLELAMP; sod[OPT_TOGGLELAMP].title = M_TITLE_TOGGLELAMP; sod[OPT_TOGGLELAMP].desc = M_DESC_TOGGLELAMP; sod[OPT_TOGGLELAMP].type = SANE_TYPE_BUTTON; sod[OPT_TOGGLELAMP].size = 0; sod[OPT_TOGGLELAMP].cap |= SANE_CAP_ADVANCED; sod[OPT_TOGGLELAMP].constraint_type = SANE_CONSTRAINT_NONE; if ( strncmp(md->opts.toggle_lamp, "off", 3) == 0 ) sod[OPT_TOGGLELAMP].cap |= SANE_CAP_INACTIVE; /* color balance */ sod[OPT_COLORBALANCE].title = M_TITLE_COLBALANCEGRP; sod[OPT_COLORBALANCE].type = SANE_TYPE_GROUP; sod[OPT_COLORBALANCE].size = 0; sod[OPT_COLORBALANCE].desc = ""; sod[OPT_COLORBALANCE].cap = SANE_CAP_ADVANCED; sod[OPT_COLORBALANCE].constraint_type = SANE_CONSTRAINT_NONE; sod[OPT_BALANCE_R].name = M_NAME_BALANCE_R; sod[OPT_BALANCE_R].title = M_TITLE_BALANCE_R; sod[OPT_BALANCE_R].desc = M_DESC_BALANCE_R; sod[OPT_BALANCE_R].unit = SANE_UNIT_PERCENT; sod[OPT_BALANCE_R].cap |= SANE_CAP_ADVANCED; sod[OPT_BALANCE_R].constraint.range = &md->balance_range; if ( strncmp(md->opts.colorbalance_adjust, "off", 3) == 0 ) sod[OPT_BALANCE_R].cap |= SANE_CAP_INACTIVE; sod[OPT_BALANCE_G].name = M_NAME_BALANCE_G; sod[OPT_BALANCE_G].title = M_TITLE_BALANCE_G; sod[OPT_BALANCE_G].desc = M_DESC_BALANCE_G; sod[OPT_BALANCE_G].unit = SANE_UNIT_PERCENT; sod[OPT_BALANCE_G].cap |= SANE_CAP_ADVANCED; sod[OPT_BALANCE_G].constraint.range = &md->balance_range; if ( strncmp(md->opts.colorbalance_adjust, "off", 3) == 0 ) sod[OPT_BALANCE_G].cap |= SANE_CAP_INACTIVE; sod[OPT_BALANCE_B].name = M_NAME_BALANCE_B; sod[OPT_BALANCE_B].title = M_TITLE_BALANCE_B; sod[OPT_BALANCE_B].desc = M_DESC_BALANCE_B; sod[OPT_BALANCE_B].unit = SANE_UNIT_PERCENT; sod[OPT_BALANCE_B].cap |= SANE_CAP_ADVANCED; sod[OPT_BALANCE_B].constraint.range = &md->balance_range; if ( strncmp(md->opts.colorbalance_adjust, "off", 3) == 0 ) sod[OPT_BALANCE_B].cap |= SANE_CAP_INACTIVE; sod[OPT_BALANCE_FW].name = M_NAME_BALANCE_FW; sod[OPT_BALANCE_FW].title = M_TITLE_BALANCE_FW; sod[OPT_BALANCE_FW].desc = M_DESC_BALANCE_FW; sod[OPT_BALANCE_FW].type = SANE_TYPE_BUTTON; sod[OPT_BALANCE_FW].size = 0; sod[OPT_BALANCE_FW].cap |= SANE_CAP_ADVANCED; sod[OPT_BALANCE_FW].constraint_type = SANE_CONSTRAINT_NONE; if ( strncmp(md->opts.colorbalance_adjust, "off", 3) == 0 ) sod[OPT_BALANCE_FW].cap |= SANE_CAP_INACTIVE; } status = set_option_dependencies(ms, sod, val); if ( status != SANE_STATUS_GOOD ) return status; return SANE_STATUS_GOOD; } /*---------- set_option_dependencies() ---------------------------------------*/ static SANE_Status set_option_dependencies(Microtek2_Scanner *ms, SANE_Option_Descriptor *sod, Option_Value *val) { Microtek2_Device *md; md = ms->dev; DBG(40, "set_option_dependencies: val=%p, sod=%p, mode=%s\n", (void *) val, (void *) sod, val[OPT_MODE].s); if ( strcmp(val[OPT_MODE].s, MD_MODESTRING_COLOR) == 0 ) { /* activate brightness,..., deactivate halftone pattern */ /* and threshold */ sod[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; sod[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE; sod[OPT_CHANNEL].cap &= ~SANE_CAP_INACTIVE; sod[OPT_SHADOW].cap &= ~SANE_CAP_INACTIVE; sod[OPT_MIDTONE].cap &= ~SANE_CAP_INACTIVE; sod[OPT_HIGHLIGHT].cap &= ~SANE_CAP_INACTIVE; sod[OPT_EXPOSURE].cap &= ~SANE_CAP_INACTIVE; sod[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE; sod[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; if ( md->bitdepth_list[0] != 1 ) sod[OPT_BITDEPTH].cap &= ~SANE_CAP_INACTIVE; else sod[OPT_BITDEPTH].cap |= SANE_CAP_INACTIVE; sod[OPT_AUTOADJUST].cap |= SANE_CAP_INACTIVE; if ( ! ( strncmp(md->opts.colorbalance_adjust, "off", 3) == 0 ) ) { sod[OPT_BALANCE_R].cap &= ~SANE_CAP_INACTIVE; sod[OPT_BALANCE_G].cap &= ~SANE_CAP_INACTIVE; sod[OPT_BALANCE_B].cap &= ~SANE_CAP_INACTIVE; sod[OPT_BALANCE_FW].cap &= ~SANE_CAP_INACTIVE; } /* reset options values that are inactive to their default */ val[OPT_THRESHOLD].w = MD_THRESHOLD_DEFAULT; } else if ( strcmp(val[OPT_MODE].s, MD_MODESTRING_GRAY) == 0 ) { sod[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; sod[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE; sod[OPT_CHANNEL].cap |= SANE_CAP_INACTIVE; sod[OPT_SHADOW].cap &= ~SANE_CAP_INACTIVE; sod[OPT_MIDTONE].cap &= ~SANE_CAP_INACTIVE; sod[OPT_HIGHLIGHT].cap &= ~SANE_CAP_INACTIVE; sod[OPT_EXPOSURE].cap &= ~SANE_CAP_INACTIVE; sod[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE; sod[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; if ( md->bitdepth_list[0] != 1 ) sod[OPT_BITDEPTH].cap &= ~SANE_CAP_INACTIVE; else sod[OPT_BITDEPTH].cap |= SANE_CAP_INACTIVE; sod[OPT_AUTOADJUST].cap |= SANE_CAP_INACTIVE; sod[OPT_BALANCE_R].cap |= SANE_CAP_INACTIVE; sod[OPT_BALANCE_G].cap |= SANE_CAP_INACTIVE; sod[OPT_BALANCE_B].cap |= SANE_CAP_INACTIVE; sod[OPT_BALANCE_FW].cap |= SANE_CAP_INACTIVE; /* reset options values that are inactive to their default */ if ( val[OPT_CHANNEL].s ) free((void *) val[OPT_CHANNEL].s); val[OPT_CHANNEL].s = strdup((SANE_String) MD_CHANNEL_MASTER); } else if ( strcmp(val[OPT_MODE].s, MD_MODESTRING_HALFTONE) == 0 ) { sod[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; sod[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; sod[OPT_CHANNEL].cap |= SANE_CAP_INACTIVE; sod[OPT_SHADOW].cap |= SANE_CAP_INACTIVE; sod[OPT_MIDTONE].cap |= SANE_CAP_INACTIVE; sod[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE; sod[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE; sod[OPT_HALFTONE].cap &= ~SANE_CAP_INACTIVE; sod[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; sod[OPT_BITDEPTH].cap |= SANE_CAP_INACTIVE; sod[OPT_AUTOADJUST].cap |= SANE_CAP_INACTIVE; sod[OPT_BALANCE_R].cap |= SANE_CAP_INACTIVE; sod[OPT_BALANCE_G].cap |= SANE_CAP_INACTIVE; sod[OPT_BALANCE_B].cap |= SANE_CAP_INACTIVE; sod[OPT_BALANCE_FW].cap |= SANE_CAP_INACTIVE; /* reset options values that are inactive to their default */ val[OPT_BRIGHTNESS].w = MD_BRIGHTNESS_DEFAULT; val[OPT_CONTRAST].w = MD_CONTRAST_DEFAULT; if ( val[OPT_CHANNEL].s ) free((void *) val[OPT_CHANNEL].s); val[OPT_CHANNEL].s = strdup((SANE_String) MD_CHANNEL_MASTER); val[OPT_SHADOW].w = MD_SHADOW_DEFAULT; val[OPT_MIDTONE].w = MD_MIDTONE_DEFAULT; val[OPT_HIGHLIGHT].w = MD_HIGHLIGHT_DEFAULT; val[OPT_EXPOSURE].w = MD_EXPOSURE_DEFAULT; val[OPT_THRESHOLD].w = MD_THRESHOLD_DEFAULT; } else if ( strcmp(val[OPT_MODE].s, MD_MODESTRING_LINEART) == 0 ) { sod[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; sod[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; sod[OPT_CHANNEL].cap |= SANE_CAP_INACTIVE; sod[OPT_SHADOW].cap |= SANE_CAP_INACTIVE; sod[OPT_MIDTONE].cap |= SANE_CAP_INACTIVE; sod[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE; sod[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE; sod[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE; if ( val[OPT_AUTOADJUST].w == SANE_FALSE ) sod[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE; else sod[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; sod[OPT_BITDEPTH].cap |= SANE_CAP_INACTIVE; sod[OPT_AUTOADJUST].cap &= ~SANE_CAP_INACTIVE; sod[OPT_BALANCE_R].cap |= SANE_CAP_INACTIVE; sod[OPT_BALANCE_G].cap |= SANE_CAP_INACTIVE; sod[OPT_BALANCE_B].cap |= SANE_CAP_INACTIVE; sod[OPT_BALANCE_FW].cap |= SANE_CAP_INACTIVE; /* reset options values that are inactive to their default */ val[OPT_BRIGHTNESS].w = MD_BRIGHTNESS_DEFAULT; val[OPT_CONTRAST].w = MD_CONTRAST_DEFAULT; if ( val[OPT_CHANNEL].s ) free((void *) val[OPT_CHANNEL].s); val[OPT_CHANNEL].s = strdup((SANE_String) MD_CHANNEL_MASTER); val[OPT_SHADOW].w = MD_SHADOW_DEFAULT; val[OPT_MIDTONE].w = MD_MIDTONE_DEFAULT; val[OPT_HIGHLIGHT].w = MD_HIGHLIGHT_DEFAULT; val[OPT_EXPOSURE].w = MD_EXPOSURE_DEFAULT; } else { DBG(1, "set_option_dependencies: unknown mode '%s'\n", val[OPT_MODE].s ); return SANE_STATUS_INVAL; } /* these ones are always inactive if the mode changes */ sod[OPT_SHADOW_R].cap |= SANE_CAP_INACTIVE; sod[OPT_SHADOW_G].cap |= SANE_CAP_INACTIVE; sod[OPT_SHADOW_B].cap |= SANE_CAP_INACTIVE; sod[OPT_MIDTONE_R].cap |= SANE_CAP_INACTIVE; sod[OPT_MIDTONE_G].cap |= SANE_CAP_INACTIVE; sod[OPT_MIDTONE_B].cap |= SANE_CAP_INACTIVE; sod[OPT_HIGHLIGHT_R].cap |= SANE_CAP_INACTIVE; sod[OPT_HIGHLIGHT_G].cap |= SANE_CAP_INACTIVE; sod[OPT_HIGHLIGHT_B].cap |= SANE_CAP_INACTIVE; sod[OPT_EXPOSURE_R].cap |= SANE_CAP_INACTIVE; sod[OPT_EXPOSURE_G].cap |= SANE_CAP_INACTIVE; sod[OPT_EXPOSURE_B].cap |= SANE_CAP_INACTIVE; /* reset options values that are inactive to their default */ val[OPT_SHADOW_R].w = val[OPT_SHADOW_G].w = val[OPT_SHADOW_B].w = MD_SHADOW_DEFAULT; val[OPT_MIDTONE_R].w = val[OPT_MIDTONE_G].w = val[OPT_MIDTONE_B].w = MD_MIDTONE_DEFAULT; val[OPT_HIGHLIGHT_R].w = val[OPT_HIGHLIGHT_G].w = val[OPT_HIGHLIGHT_B].w = MD_HIGHLIGHT_DEFAULT; val[OPT_EXPOSURE_R].w = val[OPT_EXPOSURE_G].w = val[OPT_EXPOSURE_B].w = MD_EXPOSURE_DEFAULT; if ( SANE_OPTION_IS_SETTABLE(sod[OPT_GAMMA_MODE].cap) ) { restore_gamma_options(sod, val); } return SANE_STATUS_GOOD; } /*---------- sane_control_option() -------------------------------------------*/ SANE_Status sane_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int *info) { Microtek2_Scanner *ms = handle; Microtek2_Device *md; Microtek2_Info *mi; Option_Value *val; SANE_Option_Descriptor *sod; SANE_Status status; md = ms->dev; val = &ms->val[0]; sod = &ms->sod[0]; mi = &md->info[md->scan_source]; if ( ms->scanning ) return SANE_STATUS_DEVICE_BUSY; if ( option < 0 || option >= NUM_OPTIONS ) { DBG(100, "sane_control_option: option %d; action %d \n", option, action); DBG(10, "sane_control_option: option %d invalid\n", option); return SANE_STATUS_INVAL; } if ( ! SANE_OPTION_IS_ACTIVE(ms->sod[option].cap) ) { DBG(100, "sane_control_option: option %d; action %d \n", option, action); DBG(10, "sane_control_option: option %d not active\n", option); return SANE_STATUS_INVAL; } if ( info ) *info = 0; switch ( action ) { case SANE_ACTION_GET_VALUE: /* read out option values */ switch ( option ) { /* word options */ case OPT_BITDEPTH: case OPT_RESOLUTION: case OPT_Y_RESOLUTION: case OPT_THRESHOLD: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_PREVIEW: case OPT_BRIGHTNESS: case OPT_CONTRAST: case OPT_SHADOW: case OPT_SHADOW_R: case OPT_SHADOW_G: case OPT_SHADOW_B: case OPT_MIDTONE: case OPT_MIDTONE_R: case OPT_MIDTONE_G: case OPT_MIDTONE_B: case OPT_HIGHLIGHT: case OPT_HIGHLIGHT_R: case OPT_HIGHLIGHT_G: case OPT_HIGHLIGHT_B: case OPT_EXPOSURE: case OPT_EXPOSURE_R: case OPT_EXPOSURE_G: case OPT_EXPOSURE_B: case OPT_GAMMA_SCALAR: case OPT_GAMMA_SCALAR_R: case OPT_GAMMA_SCALAR_G: case OPT_GAMMA_SCALAR_B: case OPT_BALANCE_R: case OPT_BALANCE_G: case OPT_BALANCE_B: *(SANE_Word *) value = val[option].w; if (sod[option].type == SANE_TYPE_FIXED ) DBG(50, "sane_control_option: opt=%d, act=%d, val=%f\n", option, action, SANE_UNFIX(val[option].w)); else DBG(50, "sane_control_option: opt=%d, act=%d, val=%d\n", option, action, val[option].w); return SANE_STATUS_GOOD; /* boolean options */ case OPT_RESOLUTION_BIND: case OPT_DISABLE_BACKTRACK: case OPT_CALIB_BACKEND: case OPT_LIGHTLID35: case OPT_GAMMA_BIND: case OPT_AUTOADJUST: *(SANE_Bool *) value = val[option].w; DBG(50, "sane_control_option: opt=%d, act=%d, val=%d\n", option, action, val[option].w); return SANE_STATUS_GOOD; /* string options */ case OPT_SOURCE: case OPT_MODE: case OPT_HALFTONE: case OPT_CHANNEL: case OPT_GAMMA_MODE: strcpy(value, val[option].s); DBG(50, "sane_control_option: opt=%d, act=%d, val=%s\n", option, action, val[option].s); return SANE_STATUS_GOOD; /* word array options */ case OPT_GAMMA_CUSTOM: case OPT_GAMMA_CUSTOM_R: case OPT_GAMMA_CUSTOM_G: case OPT_GAMMA_CUSTOM_B: memcpy(value, val[option].wa, sod[option].size); return SANE_STATUS_GOOD; /* button options */ case OPT_TOGGLELAMP: case OPT_BALANCE_FW: return SANE_STATUS_GOOD; /* others */ case OPT_NUM_OPTS: *(SANE_Word *) value = NUM_OPTIONS; return SANE_STATUS_GOOD; default: return SANE_STATUS_UNSUPPORTED; } /* NOTREACHED */ /* break; */ case SANE_ACTION_SET_VALUE: /* set option values */ if ( ! SANE_OPTION_IS_SETTABLE(sod[option].cap) ) { DBG(100, "sane_control_option: option %d; action %d \n", option, action); DBG(10, "sane_control_option: trying to set unsettable option\n"); return SANE_STATUS_INVAL; } /* do not check OPT_BR_Y, xscanimage sometimes tries to set */ /* it to a too large value; bug in xscanimage ? */ /* if ( option != OPT_BR_Y ) { */ status = sanei_constrain_value(ms->sod + option, value, info); if (status != SANE_STATUS_GOOD) { DBG(10, "sane_control_option: invalid option value\n"); return status; } /* } */ switch ( sod[option].type ) { case SANE_TYPE_BOOL: DBG(50, "sane_control_option: option=%d, action=%d, value=%d\n", option, action, *(SANE_Int *) value); if ( ! ( ( *(SANE_Bool *) value == SANE_TRUE ) || ( *(SANE_Bool *) value == SANE_FALSE ) ) ) { DBG(10, "sane_control_option: invalid BOOL option value\n"); return SANE_STATUS_INVAL; } if ( val[option].w == *(SANE_Bool *) value ) /* no change */ return SANE_STATUS_GOOD; val[option].w = *(SANE_Bool *) value; break; case SANE_TYPE_INT: if ( sod[option].size == sizeof(SANE_Int) ) { /* word option */ DBG(50, "sane_control_option: option=%d, action=%d, " "value=%d\n", option, action, *(SANE_Int *) value); if ( val[option].w == *(SANE_Int *) value ) /* no change */ return SANE_STATUS_GOOD; val[option].w = *(SANE_Int *) value; } else { /* word array option */ memcpy(val[option].wa, value, sod[option].size); } break; case SANE_TYPE_FIXED: DBG(50, "sane_control_option: option=%d, action=%d, value=%f\n", option, action, SANE_UNFIX( *(SANE_Fixed *) value)); if ( val[option].w == *(SANE_Fixed *) value ) /* no change */ return SANE_STATUS_GOOD; val[option].w = *(SANE_Fixed *) value; break; case SANE_TYPE_STRING: DBG(50, "sane_control_option: option=%d, action=%d, value=%s\n", option, action, (SANE_String) value); if ( strcmp(val[option].s, (SANE_String) value) == 0 ) return SANE_STATUS_GOOD; /* no change */ if ( val[option].s ) free((void *) val[option].s); val[option].s = strdup(value); if ( val[option].s == NULL ) { DBG(1, "sane_control_option: strdup failed\n"); return SANE_STATUS_NO_MEM; } break; case SANE_TYPE_BUTTON: break; default: DBG(1, "sane_control_option: unknown type %d\n", sod[option].type); break; } switch ( option ) { case OPT_RESOLUTION: case OPT_Y_RESOLUTION: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: if ( info ) *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case OPT_DISABLE_BACKTRACK: case OPT_CALIB_BACKEND: case OPT_LIGHTLID35: case OPT_PREVIEW: case OPT_BRIGHTNESS: case OPT_THRESHOLD: case OPT_CONTRAST: case OPT_EXPOSURE: case OPT_EXPOSURE_R: case OPT_EXPOSURE_G: case OPT_EXPOSURE_B: case OPT_GAMMA_SCALAR: case OPT_GAMMA_SCALAR_R: case OPT_GAMMA_SCALAR_G: case OPT_GAMMA_SCALAR_B: case OPT_GAMMA_CUSTOM: case OPT_GAMMA_CUSTOM_R: case OPT_GAMMA_CUSTOM_G: case OPT_GAMMA_CUSTOM_B: case OPT_HALFTONE: case OPT_BALANCE_R: case OPT_BALANCE_G: case OPT_BALANCE_B: return SANE_STATUS_GOOD; case OPT_BITDEPTH: /* If the bitdepth has changed we must change the size of */ /* the gamma table if the device does not support gamma */ /* tables. This will hopefully cause no trouble if the */ /* mode is one bit */ if ( md->model_flags & MD_NO_GAMMA ) { int max_gamma_value; int size; int color; int i; size = (int) pow(2.0, (double) val[OPT_BITDEPTH].w) - 1; max_gamma_value = size - 1; for ( color = 0; color < 4; color++ ) { for ( i = 0; i < max_gamma_value; i++ ) md->custom_gamma_table[color][i] = (SANE_Int) i; } md->custom_gamma_range.max = (SANE_Int) max_gamma_value; sod[OPT_GAMMA_CUSTOM].size = size * sizeof (SANE_Int); sod[OPT_GAMMA_CUSTOM_R].size = size * sizeof (SANE_Int); sod[OPT_GAMMA_CUSTOM_G].size = size * sizeof (SANE_Int); sod[OPT_GAMMA_CUSTOM_B].size = size * sizeof (SANE_Int); if ( info ) *info |= SANE_INFO_RELOAD_OPTIONS; } if ( info ) *info |= SANE_INFO_RELOAD_PARAMS; return SANE_STATUS_GOOD; case OPT_SOURCE: if ( info ) *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; if ( strcmp(val[option].s, MD_SOURCESTRING_FLATBED) == 0 ) md->scan_source = MD_SOURCE_FLATBED; else if ( strcmp(val[option].s, MD_SOURCESTRING_TMA) == 0 ) md->scan_source = MD_SOURCE_TMA; else if ( strcmp(val[option].s, MD_SOURCESTRING_ADF) == 0 ) md->scan_source = MD_SOURCE_ADF; else if ( strcmp(val[option].s, MD_SOURCESTRING_STRIPE) == 0 ) md->scan_source = MD_SOURCE_STRIPE; else if ( strcmp(val[option].s, MD_SOURCESTRING_SLIDE) == 0 ) md->scan_source = MD_SOURCE_SLIDE; else { DBG(1, "sane_control_option: unsupported option %s\n", val[option].s); return SANE_STATUS_UNSUPPORTED; } init_options(ms, md->scan_source); return SANE_STATUS_GOOD; case OPT_MODE: if ( info ) *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; status = set_option_dependencies(ms, sod, val); /* Options with side effects need special treatment. They are */ /* reset, even if they were set by set_option_dependencies(): */ /* if we have more than one color depth activate this option */ if ( md->bitdepth_list[0] == 1 ) sod[OPT_BITDEPTH].cap |= SANE_CAP_INACTIVE; if ( strncmp(md->opts.auto_adjust, "off", 3) == 0 ) sod[OPT_AUTOADJUST].cap |= SANE_CAP_INACTIVE; if ( status != SANE_STATUS_GOOD ) return status; return SANE_STATUS_GOOD; case OPT_CHANNEL: if ( info ) *info |= SANE_INFO_RELOAD_OPTIONS; if ( strcmp(val[option].s, MD_CHANNEL_MASTER) == 0 ) { sod[OPT_SHADOW].cap &= ~SANE_CAP_INACTIVE; sod[OPT_MIDTONE].cap &= ~SANE_CAP_INACTIVE; sod[OPT_HIGHLIGHT].cap &= ~SANE_CAP_INACTIVE; sod[OPT_EXPOSURE].cap &= ~SANE_CAP_INACTIVE; sod[OPT_SHADOW_R].cap |= SANE_CAP_INACTIVE; sod[OPT_MIDTONE_R].cap |= SANE_CAP_INACTIVE; sod[OPT_HIGHLIGHT_R].cap |= SANE_CAP_INACTIVE; sod[OPT_EXPOSURE_R].cap |= SANE_CAP_INACTIVE; sod[OPT_SHADOW_G].cap |= SANE_CAP_INACTIVE; sod[OPT_MIDTONE_G].cap |= SANE_CAP_INACTIVE; sod[OPT_HIGHLIGHT_G].cap |= SANE_CAP_INACTIVE; sod[OPT_EXPOSURE_G].cap |= SANE_CAP_INACTIVE; sod[OPT_SHADOW_B].cap |= SANE_CAP_INACTIVE; sod[OPT_MIDTONE_B].cap |= SANE_CAP_INACTIVE; sod[OPT_HIGHLIGHT_B].cap |= SANE_CAP_INACTIVE; sod[OPT_EXPOSURE_B].cap |= SANE_CAP_INACTIVE; } else if ( strcmp(val[option].s, MD_CHANNEL_RED) == 0 ) { sod[OPT_SHADOW].cap |= SANE_CAP_INACTIVE; sod[OPT_MIDTONE].cap |= SANE_CAP_INACTIVE; sod[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE; sod[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE; sod[OPT_SHADOW_R].cap &= ~SANE_CAP_INACTIVE; sod[OPT_MIDTONE_R].cap &= ~SANE_CAP_INACTIVE; sod[OPT_HIGHLIGHT_R].cap &= ~SANE_CAP_INACTIVE; sod[OPT_EXPOSURE_R].cap &= ~SANE_CAP_INACTIVE; sod[OPT_SHADOW_G].cap |= SANE_CAP_INACTIVE; sod[OPT_MIDTONE_G].cap |= SANE_CAP_INACTIVE; sod[OPT_HIGHLIGHT_G].cap |= SANE_CAP_INACTIVE; sod[OPT_EXPOSURE_G].cap |= SANE_CAP_INACTIVE; sod[OPT_SHADOW_B].cap |= SANE_CAP_INACTIVE; sod[OPT_MIDTONE_B].cap |= SANE_CAP_INACTIVE; sod[OPT_HIGHLIGHT_B].cap |= SANE_CAP_INACTIVE; sod[OPT_EXPOSURE_B].cap |= SANE_CAP_INACTIVE; } else if ( strcmp(val[option].s, MD_CHANNEL_GREEN) == 0 ) { sod[OPT_SHADOW].cap |= SANE_CAP_INACTIVE; sod[OPT_MIDTONE].cap |= SANE_CAP_INACTIVE; sod[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE; sod[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE; sod[OPT_SHADOW_R].cap |= SANE_CAP_INACTIVE; sod[OPT_MIDTONE_R].cap |= SANE_CAP_INACTIVE; sod[OPT_HIGHLIGHT_R].cap |= SANE_CAP_INACTIVE; sod[OPT_EXPOSURE_R].cap |= SANE_CAP_INACTIVE; sod[OPT_SHADOW_G].cap &= ~SANE_CAP_INACTIVE; sod[OPT_MIDTONE_G].cap &= ~SANE_CAP_INACTIVE; sod[OPT_HIGHLIGHT_G].cap &= ~SANE_CAP_INACTIVE; sod[OPT_EXPOSURE_G].cap &= ~SANE_CAP_INACTIVE; sod[OPT_SHADOW_B].cap |= SANE_CAP_INACTIVE; sod[OPT_MIDTONE_B].cap |= SANE_CAP_INACTIVE; sod[OPT_HIGHLIGHT_B].cap |= SANE_CAP_INACTIVE; sod[OPT_EXPOSURE_B].cap |= SANE_CAP_INACTIVE; } else if ( strcmp(val[option].s, MD_CHANNEL_BLUE) == 0 ) { sod[OPT_SHADOW].cap |= SANE_CAP_INACTIVE; sod[OPT_MIDTONE].cap |= SANE_CAP_INACTIVE; sod[OPT_HIGHLIGHT].cap |= SANE_CAP_INACTIVE; sod[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE; sod[OPT_SHADOW_R].cap |= SANE_CAP_INACTIVE; sod[OPT_MIDTONE_R].cap |= SANE_CAP_INACTIVE; sod[OPT_HIGHLIGHT_R].cap |= SANE_CAP_INACTIVE; sod[OPT_EXPOSURE_R].cap |= SANE_CAP_INACTIVE; sod[OPT_SHADOW_G].cap |= SANE_CAP_INACTIVE; sod[OPT_MIDTONE_G].cap |= SANE_CAP_INACTIVE; sod[OPT_HIGHLIGHT_G].cap |= SANE_CAP_INACTIVE; sod[OPT_EXPOSURE_G].cap |= SANE_CAP_INACTIVE; sod[OPT_SHADOW_B].cap &= ~SANE_CAP_INACTIVE; sod[OPT_MIDTONE_B].cap &= ~SANE_CAP_INACTIVE; sod[OPT_HIGHLIGHT_B].cap &= ~SANE_CAP_INACTIVE; sod[OPT_EXPOSURE_B].cap &= ~SANE_CAP_INACTIVE; } return SANE_STATUS_GOOD; case OPT_GAMMA_MODE: restore_gamma_options(sod, val); if ( info ) *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_GAMMA_BIND: restore_gamma_options(sod, val); if ( info ) *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_SHADOW: case OPT_SHADOW_R: case OPT_SHADOW_G: case OPT_SHADOW_B: if ( val[option].w >= val[option + 1].w ) { val[option + 1].w = val[option].w + 1; if ( info ) *info |= SANE_INFO_RELOAD_OPTIONS; } if ( val[option + 1].w >= val[option + 2].w ) val[option + 2].w = val[option + 1].w + 1; return SANE_STATUS_GOOD; case OPT_MIDTONE: case OPT_MIDTONE_R: case OPT_MIDTONE_G: case OPT_MIDTONE_B: if ( val[option].w <= val[option - 1].w ) { val[option - 1].w = val[option].w - 1; if ( info ) *info |= SANE_INFO_RELOAD_OPTIONS; } if ( val[option].w >= val[option + 1].w ) { val[option + 1].w = val[option].w + 1; if ( info ) *info |= SANE_INFO_RELOAD_OPTIONS; } return SANE_STATUS_GOOD; case OPT_HIGHLIGHT: case OPT_HIGHLIGHT_R: case OPT_HIGHLIGHT_G: case OPT_HIGHLIGHT_B: if ( val[option].w <= val[option - 1].w ) { val[option - 1].w = val[option].w - 1; if ( info ) *info |= SANE_INFO_RELOAD_OPTIONS; } if ( val[option - 1].w <= val[option - 2].w ) val[option - 2].w = val[option - 1].w - 1; return SANE_STATUS_GOOD; case OPT_RESOLUTION_BIND: if ( ms->val[option].w == SANE_FALSE ) { ms->sod[OPT_Y_RESOLUTION].cap &= ~SANE_CAP_INACTIVE; } else { ms->sod[OPT_Y_RESOLUTION].cap |= SANE_CAP_INACTIVE; } if ( info ) *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; case OPT_TOGGLELAMP: status = scsi_read_system_status(md, -1); if ( status != SANE_STATUS_GOOD ) return SANE_STATUS_IO_ERROR; md->status.flamp ^= 1; status = scsi_send_system_status(md, -1); if ( status != SANE_STATUS_GOOD ) return SANE_STATUS_IO_ERROR; return SANE_STATUS_GOOD; case OPT_AUTOADJUST: if ( info ) *info |= SANE_INFO_RELOAD_OPTIONS; if ( ms->val[option].w == SANE_FALSE ) ms->sod[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE; else ms->sod[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; return SANE_STATUS_GOOD; case OPT_BALANCE_FW: val[OPT_BALANCE_R].w = SANE_FIX((uint8_t)( (float)mi->balance[0] / 2.55 ) ); val[OPT_BALANCE_G].w = SANE_FIX((uint8_t)( (float)mi->balance[1] / 2.55 ) ); val[OPT_BALANCE_B].w = SANE_FIX((uint8_t)( (float)mi->balance[2] / 2.55 ) ); if ( info ) *info |= SANE_INFO_RELOAD_OPTIONS; return SANE_STATUS_GOOD; default: return SANE_STATUS_UNSUPPORTED; } #if 0 break; #endif default: DBG(1, "sane_control_option: Unsupported action %d\n", action); return SANE_STATUS_UNSUPPORTED; } } /*---------- sane_get_option_descriptor() ------------------------------------*/ const SANE_Option_Descriptor * sane_get_option_descriptor(SANE_Handle handle, SANE_Int n) { Microtek2_Scanner *ms = handle; DBG(255, "sane_get_option_descriptor: handle=%p, sod=%p, opt=%d\n", (void *) handle, (void *) ms->sod, n); if ( n < 0 || n >= NUM_OPTIONS ) { DBG(30, "sane_get_option_descriptor: invalid option %d\n", n); return NULL; } return &ms->sod[n]; } /*---------- restore_gamma_options() -----------------------------------------*/ static SANE_Status restore_gamma_options(SANE_Option_Descriptor *sod, Option_Value *val) { DBG(40, "restore_gamma_options: val=%p, sod=%p\n", (void *) val, (void *) sod); /* if we don't have a gamma table return immediately */ if ( ! val[OPT_GAMMA_MODE].s ) return SANE_STATUS_GOOD; if ( strcmp(val[OPT_MODE].s, MD_MODESTRING_COLOR) == 0 ) { sod[OPT_GAMMA_MODE].cap &= ~SANE_CAP_INACTIVE; if ( strcmp(val[OPT_GAMMA_MODE].s, MD_GAMMAMODE_LINEAR) == 0 ) { sod[OPT_GAMMA_BIND].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR_R].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR_G].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR_B].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM_R].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM_G].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM_B].cap |= SANE_CAP_INACTIVE; } else if ( strcmp(val[OPT_GAMMA_MODE].s, MD_GAMMAMODE_SCALAR) == 0 ) { sod[OPT_GAMMA_BIND].cap &= ~SANE_CAP_INACTIVE; if ( val[OPT_GAMMA_BIND].w == SANE_TRUE ) { sod[OPT_GAMMA_SCALAR].cap &= ~SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR_R].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR_G].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR_B].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM_R].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM_G].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM_B].cap |= SANE_CAP_INACTIVE; } else { sod[OPT_GAMMA_SCALAR].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR_R].cap &= ~SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR_G].cap &= ~SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR_B].cap &= ~SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM_R].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM_G].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM_B].cap |= SANE_CAP_INACTIVE; } } else if ( strcmp(val[OPT_GAMMA_MODE].s, MD_GAMMAMODE_CUSTOM) == 0 ) { sod[OPT_GAMMA_BIND].cap &= ~SANE_CAP_INACTIVE; if ( val[OPT_GAMMA_BIND].w == SANE_TRUE ) { sod[OPT_GAMMA_CUSTOM].cap &= ~SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM_R].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM_G].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM_B].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR_R].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR_G].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR_B].cap |= SANE_CAP_INACTIVE; } else { sod[OPT_GAMMA_CUSTOM].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM_R].cap &= ~SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM_G].cap &= ~SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM_B].cap &= ~SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR_R].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR_G].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR_B].cap |= SANE_CAP_INACTIVE; } } } else if ( strcmp(val[OPT_MODE].s, MD_MODESTRING_GRAY) == 0 ) { sod[OPT_GAMMA_MODE].cap &= ~SANE_CAP_INACTIVE; sod[OPT_GAMMA_BIND].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR_R].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR_G].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR_B].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM_R].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM_G].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM_B].cap |= SANE_CAP_INACTIVE; if ( strcmp(val[OPT_GAMMA_MODE].s, MD_GAMMAMODE_LINEAR) == 0 ) { sod[OPT_GAMMA_SCALAR].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM].cap |= SANE_CAP_INACTIVE; } else if ( strcmp(val[OPT_GAMMA_MODE].s, MD_GAMMAMODE_SCALAR) == 0 ) { sod[OPT_GAMMA_SCALAR].cap &= ~SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM].cap |= SANE_CAP_INACTIVE; } else if ( strcmp(val[OPT_GAMMA_MODE].s, MD_GAMMAMODE_CUSTOM) == 0 ) { sod[OPT_GAMMA_CUSTOM].cap &= ~SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR].cap |= SANE_CAP_INACTIVE; } } else if ( strcmp(val[OPT_MODE].s, MD_MODESTRING_HALFTONE) == 0 || strcmp(val[OPT_MODE].s, MD_MODESTRING_LINEART) == 0 ) { /* reset gamma to default */ if ( val[OPT_GAMMA_MODE].s ) free((void *) val[OPT_GAMMA_MODE].s); val[OPT_GAMMA_MODE].s = strdup(MD_GAMMAMODE_LINEAR); sod[OPT_GAMMA_MODE].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_BIND].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR_R].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR_G].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_SCALAR_B].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM_R].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM_G].cap |= SANE_CAP_INACTIVE; sod[OPT_GAMMA_CUSTOM_B].cap |= SANE_CAP_INACTIVE; } else DBG(1, "restore_gamma_options: unknown mode %s\n", val[OPT_MODE].s); return SANE_STATUS_GOOD; } /*---------- calculate_sane_params() -----------------------------------------*/ static SANE_Status calculate_sane_params(Microtek2_Scanner *ms) { Microtek2_Device *md; Microtek2_Info *mi; DBG(30, "calculate_sane_params: ms=%p\n", (void *) ms); md = ms->dev; mi = &md->info[md->scan_source]; if ( ! mi->onepass && ms->mode == MS_MODE_COLOR ) { if ( ms->current_pass == 1 ) ms->params.format = SANE_FRAME_RED; else if ( ms->current_pass == 2 ) ms->params.format = SANE_FRAME_GREEN; else if ( ms->current_pass == 3 ) ms->params.format = SANE_FRAME_BLUE; else { DBG(1, "calculate_sane_params: invalid pass number %d\n", ms->current_pass); return SANE_STATUS_IO_ERROR; } } else if ( mi->onepass && ms->mode == MS_MODE_COLOR ) ms->params.format = SANE_FRAME_RGB; else ms->params.format = SANE_FRAME_GRAY; if ( ! mi->onepass && ms->mode == MS_MODE_COLOR && ms->current_pass < 3 ) ms->params.last_frame = SANE_FALSE; else ms->params.last_frame = SANE_TRUE; ms->params.lines = ms->src_remaining_lines; ms->params.pixels_per_line = ms->ppl; ms->params.bytes_per_line = ms->real_bpl; ms->params.depth = ms->bits_per_pixel_out; return SANE_STATUS_GOOD; } /*---------- get_calib_params() ----------------------------------------------*/ static void get_calib_params(Microtek2_Scanner *ms) { Microtek2_Device *md; Microtek2_Info *mi; DBG(30, "get_calib_params: handle=%p\n", (void *) ms); md = ms->dev; mi = &md->info[md->scan_source]; if ( md->model_flags & MD_CALIB_DIVISOR_600 ) { if ( ms->x_resolution_dpi <= 600 ) mi->calib_divisor = 2; else mi->calib_divisor = 1; } DBG(30, "Calib Divisor: %d\n", mi->calib_divisor); ms->x_resolution_dpi = mi->opt_resolution / mi->calib_divisor; ms->y_resolution_dpi = mi->opt_resolution / 5; /* ignore dust particles */ ms->x1_dots = 0; ms->y1_dots = mi->calib_white; ms->width_dots = mi->geo_width; if ( md->shading_length != 0 ) ms->height_dots = md->shading_length; else ms->height_dots = mi->calib_space; ms->mode = MS_MODE_COLOR; if ( mi->depth & MI_HASDEPTH_16 ) ms->depth = 16; else if ( mi->depth & MI_HASDEPTH_14 ) ms->depth = 14; else if ( mi->depth & MI_HASDEPTH_12 ) ms->depth = 12; else if ( mi->depth & MI_HASDEPTH_10 ) ms->depth = 10; else ms->depth = 8; ms->stay = 0; if ( mi->calib_space < 10 ) ms->stay = 1; ms->rawdat = 1; ms->quality = 1; ms->fastscan = 0; /* ms->scan_source = md->scan_source; */ ms->scan_source = 0; ms->brightness_m = ms->brightness_r = ms->brightness_g = ms->brightness_b = 128; ms->exposure_m = ms->exposure_r = ms->exposure_g = ms->exposure_b = 0; ms->contrast_m = ms->contrast_r = ms->contrast_g = ms->contrast_b = 128; ms->shadow_m = ms->shadow_r = ms->shadow_g = ms->shadow_b = 0; ms->midtone_m = ms->midtone_r = ms->midtone_g = ms->midtone_b = 128; ms->highlight_m = ms->highlight_r = ms->highlight_g = ms->highlight_b = 255; return; } /*---------- get_scan_parameters () ------------------------------------------*/ static SANE_Status get_scan_parameters(Microtek2_Scanner *ms) { Microtek2_Device *md; Microtek2_Info *mi; double dpm; /* dots per millimeter */ int x2_dots; int y2_dots; int i; DBG(30, "get_scan_parameters: handle=%p\n", (void *) ms); md = ms->dev; mi = &md->info[md->scan_source]; get_scan_mode_and_depth(ms, &ms->mode, &ms->depth, &ms->bits_per_pixel_in, &ms->bits_per_pixel_out); /* get the scan_source */ if ( strcmp(ms->val[OPT_SOURCE].s, MD_SOURCESTRING_FLATBED) == 0 ) ms->scan_source = MS_SOURCE_FLATBED; else if ( strcmp(ms->val[OPT_SOURCE].s, MD_SOURCESTRING_ADF) == 0 ) ms->scan_source = MS_SOURCE_ADF; else if ( strcmp(ms->val[OPT_SOURCE].s, MD_SOURCESTRING_TMA) == 0 ) ms->scan_source = MS_SOURCE_TMA; else if ( strcmp(ms->val[OPT_SOURCE].s, MD_SOURCESTRING_STRIPE) == 0 ) ms->scan_source = MS_SOURCE_STRIPE; else if ( strcmp(ms->val[OPT_SOURCE].s, MD_SOURCESTRING_SLIDE) == 0 ) ms->scan_source = MS_SOURCE_SLIDE; /* enable/disable backtracking */ if ( ms->val[OPT_DISABLE_BACKTRACK].w == SANE_TRUE ) ms->no_backtracking = 1; else ms->no_backtracking = 0; /* turn off the lamp during a scan */ if ( ms->val[OPT_LIGHTLID35].w == SANE_TRUE ) ms->lightlid35 = 1; else ms->lightlid35 = 0; /* automatic adjustment of threshold */ if ( ms->val[OPT_AUTOADJUST].w == SANE_TRUE) ms->auto_adjust = 1; else ms->auto_adjust = 0; /* color calibration by backend */ if ( ms->val[OPT_CALIB_BACKEND].w == SANE_TRUE ) ms->calib_backend = 1; else ms->calib_backend = 0; /* if halftone mode select halftone pattern */ if ( ms->mode == MS_MODE_HALFTONE ) { i = 0; while ( strcmp(md->halftone_mode_list[i], ms->val[OPT_HALFTONE].s) ) ++i; ms->internal_ht_index = i; } /* if lineart get the value for threshold */ if ( ms->mode == MS_MODE_LINEART || ms->mode == MS_MODE_LINEARTFAKE) ms->threshold = (uint8_t) ms->val[OPT_THRESHOLD].w; else ms->threshold = (uint8_t) M_THRESHOLD_DEFAULT; DBG(30, "get_scan_parameters: mode=%d, depth=%d, bpp_in=%d, bpp_out=%d\n", ms->mode, ms->depth, ms->bits_per_pixel_in, ms->bits_per_pixel_out); /* calculate positions, width and height in dots */ /* check for impossible values */ /* ensure a minimum scan area of 10 x 10 pixels */ dpm = (double) mi->opt_resolution / MM_PER_INCH; ms->x1_dots = (SANE_Int) ( SANE_UNFIX(ms->val[OPT_TL_X].w) * dpm + 0.5 ); if ( ms->x1_dots > ( mi->geo_width - 10 ) ) ms->x1_dots = ( mi->geo_width - 10 ); ms->y1_dots = (SANE_Int) ( SANE_UNFIX(ms->val[OPT_TL_Y].w) * dpm + 0.5 ); if ( ms->y1_dots > ( mi->geo_height - 10 ) ) ms->y1_dots = ( mi->geo_height - 10 ); x2_dots = (int) ( SANE_UNFIX(ms->val[OPT_BR_X].w) * dpm + 0.5 ); if ( x2_dots >= mi->geo_width ) x2_dots = mi->geo_width - 1; y2_dots = (int) ( SANE_UNFIX(ms->val[OPT_BR_Y].w) * dpm + 0.5 ); if ( y2_dots >= mi->geo_height ) y2_dots = mi->geo_height - 1; ms->width_dots = x2_dots - ms->x1_dots; if ( md->model_flags & MD_OFFSET_2 ) /* this firmware has problems with */ if ( ( ms->width_dots % 2 ) == 1 ) /* odd pixel numbers */ ms->width_dots -= 1; if ( ms->width_dots < 10 ) ms->width_dots = 10; ms->height_dots = y2_dots - ms->y1_dots; if ( ms->height_dots < 10 ) ms->height_dots = 10; /*test!!!*/ /* ms->y1_dots -= 50;*/ /* take scanning direction into account */ if ((mi->direction & MI_DATSEQ_RTOL) == 1) ms->x1_dots = mi->geo_width - ms->x1_dots - ms->width_dots; if ( ms->val[OPT_RESOLUTION_BIND].w == SANE_TRUE ) { ms->x_resolution_dpi = (SANE_Int) (SANE_UNFIX(ms->val[OPT_RESOLUTION].w) + 0.5); ms->y_resolution_dpi = (SANE_Int) (SANE_UNFIX(ms->val[OPT_RESOLUTION].w) + 0.5); } else { ms->x_resolution_dpi = (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); } if ( ms->x_resolution_dpi < 10 ) ms->x_resolution_dpi = 10; if ( ms->y_resolution_dpi < 10 ) ms->y_resolution_dpi = 10; DBG(30, "get_scan_parameters: yres=%d, x1=%d, width=%d, y1=%d, height=%d\n", ms->y_resolution_dpi, ms->x1_dots, ms->width_dots, ms->y1_dots, ms->height_dots); /* Preview mode */ if ( ms->val[OPT_PREVIEW].w == SANE_TRUE ) { ms->fastscan = SANE_TRUE; ms->quality = SANE_FALSE; } else { ms->fastscan = SANE_FALSE; ms->quality = SANE_TRUE; } ms->rawdat = 0; /* brightness, contrast, values 1,..,255 */ ms->brightness_m = (uint8_t) (SANE_UNFIX(ms->val[OPT_BRIGHTNESS].w) / SANE_UNFIX(md->percentage_range.max) * 254.0) + 1; ms->brightness_r = ms->brightness_g = ms->brightness_b = ms->brightness_m; ms->contrast_m = (uint8_t) (SANE_UNFIX(ms->val[OPT_CONTRAST].w) / SANE_UNFIX(md->percentage_range.max) * 254.0) + 1; ms->contrast_r = ms->contrast_g = ms->contrast_b = ms->contrast_m; /* shadow, midtone, highlight, exposure */ ms->shadow_m = (uint8_t) ms->val[OPT_SHADOW].w; ms->shadow_r = (uint8_t) ms->val[OPT_SHADOW_R].w; ms->shadow_g = (uint8_t) ms->val[OPT_SHADOW_G].w; ms->shadow_b = (uint8_t) ms->val[OPT_SHADOW_B].w; ms->midtone_m = (uint8_t) ms->val[OPT_MIDTONE].w; ms->midtone_r = (uint8_t) ms->val[OPT_MIDTONE_R].w; ms->midtone_g = (uint8_t) ms->val[OPT_MIDTONE_G].w; ms->midtone_b = (uint8_t) ms->val[OPT_MIDTONE_B].w; ms->highlight_m = (uint8_t) ms->val[OPT_HIGHLIGHT].w; ms->highlight_r = (uint8_t) ms->val[OPT_HIGHLIGHT_R].w; ms->highlight_g = (uint8_t) ms->val[OPT_HIGHLIGHT_G].w; ms->highlight_b = (uint8_t) ms->val[OPT_HIGHLIGHT_B].w; ms->exposure_m = (uint8_t) (ms->val[OPT_EXPOSURE].w / 2); ms->exposure_r = (uint8_t) (ms->val[OPT_EXPOSURE_R].w / 2); ms->exposure_g = (uint8_t) (ms->val[OPT_EXPOSURE_G].w / 2); ms->exposure_b = (uint8_t) (ms->val[OPT_EXPOSURE_B].w / 2); ms->gamma_mode = strdup( (char *) ms->val[OPT_GAMMA_MODE].s); ms->balance[0] = (uint8_t) (SANE_UNFIX(ms->val[OPT_BALANCE_R].w)); ms->balance[1] = (uint8_t) (SANE_UNFIX(ms->val[OPT_BALANCE_G].w)); ms->balance[2] = (uint8_t) (SANE_UNFIX(ms->val[OPT_BALANCE_B].w)); DBG(255, "get_scan_parameters:ms->balance[0]=%d,[1]=%d,[2]=%d\n", ms->balance[0], ms->balance[1], ms->balance[2]); return SANE_STATUS_GOOD; } /*---------- get_scan_mode_and_depth() ---------------------------------------*/ static SANE_Status get_scan_mode_and_depth(Microtek2_Scanner *ms, int *mode, int *depth, int *bits_per_pixel_in, int *bits_per_pixel_out) { /* This function translates the strings for the possible modes and */ /* bitdepth into a more conveniant format as needed for SET WINDOW. */ /* bits_per_pixel is the number of bits per color one pixel needs */ /* when transferred from the scanner, bits_perpixel_out is the */ /* number of bits per color one pixel uses when transferred to the */ /* frontend. These may be different. For example, with a depth of 4 */ /* two pixels per byte are transferred from the scanner, but only one */ /* pixel per byte is transferred to the frontend. */ /* If lineart_fake is set to !=0, we need the parameters for a */ /* grayscale scan, because the scanner has no lineart mode */ Microtek2_Device *md; Microtek2_Info *mi; DBG(30, "get_scan_mode_and_depth: handle=%p\n", (void *) ms); md = ms->dev; mi = &md->info[md->scan_source]; if ( strcmp(ms->val[OPT_MODE].s, MD_MODESTRING_COLOR) == 0 ) *mode = MS_MODE_COLOR; else if ( strcmp(ms->val[OPT_MODE].s, MD_MODESTRING_GRAY) == 0 ) *mode = MS_MODE_GRAY; else if ( strcmp(ms->val[OPT_MODE].s, MD_MODESTRING_HALFTONE) == 0) *mode = MS_MODE_HALFTONE; else if ( strcmp(ms->val[OPT_MODE].s, MD_MODESTRING_LINEART) == 0 ) { if ( MI_LINEART_NONE(mi->scanmode) || ms->val[OPT_AUTOADJUST].w == SANE_TRUE || md->model_flags & MD_READ_CONTROL_BIT) *mode = MS_MODE_LINEARTFAKE; else *mode = MS_MODE_LINEART; } else { DBG(1, "get_scan_mode_and_depth: Unknown mode %s\n", ms->val[OPT_MODE].s); return SANE_STATUS_INVAL; } if ( strcmp(ms->val[OPT_MODE].s, MD_MODESTRING_COLOR) == 0 || strcmp(ms->val[OPT_MODE].s, MD_MODESTRING_GRAY) == 0 ) { if ( ms->val[OPT_BITDEPTH].w == MD_DEPTHVAL_16 ) { *depth = 16; *bits_per_pixel_in = *bits_per_pixel_out = 16; } else if ( ms->val[OPT_BITDEPTH].w == MD_DEPTHVAL_14 ) { *depth = 14; *bits_per_pixel_in = *bits_per_pixel_out = 16; } else if ( ms->val[OPT_BITDEPTH].w == MD_DEPTHVAL_12 ) { *depth = 12; *bits_per_pixel_in = *bits_per_pixel_out = 16; } else if ( ms->val[OPT_BITDEPTH].w == MD_DEPTHVAL_10 ) { *depth = 10; *bits_per_pixel_in = *bits_per_pixel_out = 16; } else if ( ms->val[OPT_BITDEPTH].w == MD_DEPTHVAL_8 ) { *depth = 8; *bits_per_pixel_in = *bits_per_pixel_out = 8; } else if ( ms->val[OPT_MODE].w == MD_DEPTHVAL_4 ) { *depth = 4; *bits_per_pixel_in = 4; *bits_per_pixel_out = 8; } } else if ( strcmp(ms->val[OPT_MODE].s, MD_MODESTRING_HALFTONE) == 0 ) { *depth = 1; *bits_per_pixel_in = *bits_per_pixel_out = 1; } else /* lineart */ { *bits_per_pixel_out = 1; if ( *mode == MS_MODE_LINEARTFAKE ) { *depth = 8; *bits_per_pixel_in = 8; } else { *depth = 1; *bits_per_pixel_in = 1; } } #if 0 if ( ms->val[OPT_PREVIEW].w == SANE_TRUE ) { if ( *depth > 8 ) { *depth = 8; *bits_per_pixel_in = *bits_per_pixel_out = 8; } } #endif DBG(30, "get_scan_mode_and_depth: mode=%d, depth=%d," " bits_pp_in=%d, bits_pp_out=%d, preview=%d\n", *mode, *depth, *bits_per_pixel_in, *bits_per_pixel_out, ms->val[OPT_PREVIEW].w); return SANE_STATUS_GOOD; } /*---------- scsi_wait_for_image() -------------------------------------------*/ static SANE_Status scsi_wait_for_image(Microtek2_Scanner *ms) { int retry = 60; SANE_Status status; DBG(30, "scsi_wait_for_image: ms=%p\n", (void *) ms); while ( retry-- > 0 ) { status = scsi_read_image_status(ms); if (status == SANE_STATUS_DEVICE_BUSY ) { sleep(1); continue; } if ( status == SANE_STATUS_GOOD ) return status; /* status != GOOD && != BUSY */ DBG(1, "scsi_wait_for_image: '%s'\n", sane_strstatus(status)); return status; } /* BUSY after n retries */ DBG(1, "scsi_wait_for_image: '%s'\n", sane_strstatus(status)); return status; } /*---------- scsi_read_gamma() -----------------------------------------------*/ /* currently not used */ /* static SANE_Status scsi_read_gamma(Microtek2_Scanner *ms, int color) { uint8_t readgamma[RG_CMD_L]; uint8_t result[3072]; size_t size; SANE_Bool endiantype; SANE_Status status; RG_CMD(readgamma); ENDIAN_TYPE(endiantype); RG_PCORMAC(readgamma, endiantype); RG_COLOR(readgamma, color); RG_WORD(readgamma, ( ms->dev->lut_entry_size == 1 ) ? 0 : 1); RG_TRANSFERLENGTH(readgamma, (color == 3 ) ? 3072 : 1024); dump_area(readgamma, 10, "ReadGamma"); size = sizeof(result); status = sanei_scsi_cmd(ms->sfd, readgamma, sizeof(readgamma), result, &size); if ( status != SANE_STATUS_GOOD ) { DBG(1, "scsi_read_gamma: (L,R) read_gamma failed: status '%s'\n", sane_strstatus(status)); return status; } dump_area(result, 3072, "Result"); return SANE_STATUS_GOOD; } */ /*---------- scsi_send_gamma() -----------------------------------------------*/ static SANE_Status scsi_send_gamma(Microtek2_Scanner *ms) { SANE_Bool endiantype; SANE_Status status; size_t size; uint8_t *cmd, color; DBG(30, "scsi_send_gamma: pos=%p, size=%d, word=%d, color=%d\n", (void *) ms->gamma_table, ms->lut_size_bytes, ms->word, ms->current_color); if ( ( 3 * ms->lut_size_bytes ) <= 0xffff ) /*send Gamma with one command*/ { cmd = (uint8_t *) alloca(SG_CMD_L + 3 * ms->lut_size_bytes); if ( cmd == NULL ) { DBG(1, "scsi_send_gamma: Couldn't get buffer for gamma table\n"); return SANE_STATUS_IO_ERROR; } SG_SET_CMD(cmd); ENDIAN_TYPE(endiantype) SG_SET_PCORMAC(cmd, endiantype); SG_SET_COLOR(cmd, ms->current_color); SG_SET_WORD(cmd, ms->word); SG_SET_TRANSFERLENGTH(cmd, 3 * ms->lut_size_bytes); memcpy(cmd + SG_CMD_L, ms->gamma_table, 3 * ms->lut_size_bytes); size = 3 * ms->lut_size_bytes; if ( md_dump >= 2 ) dump_area2(cmd, SG_CMD_L, "sendgammacmd"); if ( md_dump >= 3 ) dump_area2(cmd + SG_CMD_L, size, "sendgammadata"); status = sanei_scsi_cmd(ms->sfd, cmd, size + SG_CMD_L, NULL, 0); if ( status != SANE_STATUS_GOOD ) DBG(1, "scsi_send_gamma: '%s'\n", sane_strstatus(status)); } else /* send gamma with 3 commands, one for each color */ { for ( color = 0; color < 3; color++ ) { cmd = (uint8_t *) alloca(SG_CMD_L + ms->lut_size_bytes); if ( cmd == NULL ) { DBG(1, "scsi_send_gamma: Couldn't get buffer for gamma table\n"); return SANE_STATUS_IO_ERROR; } SG_SET_CMD(cmd); ENDIAN_TYPE(endiantype) SG_SET_PCORMAC(cmd, endiantype); SG_SET_COLOR(cmd, color); SG_SET_WORD(cmd, ms->word); SG_SET_TRANSFERLENGTH(cmd, ms->lut_size_bytes); memcpy(cmd + SG_CMD_L, ms->gamma_table + color * ms->lut_size_bytes, ms->lut_size_bytes); size = ms->lut_size_bytes; if ( md_dump >= 2 ) dump_area2(cmd, SG_CMD_L, "sendgammacmd"); if ( md_dump >= 3 ) dump_area2(cmd + SG_CMD_L, size, "sendgammadata"); status = sanei_scsi_cmd(ms->sfd, cmd, size + SG_CMD_L, NULL, 0); if ( status != SANE_STATUS_GOOD ) DBG(1, "scsi_send_gamma: '%s'\n", sane_strstatus(status)); } } return status; } /*---------- scsi_inquiry() --------------------------------------------------*/ static SANE_Status scsi_inquiry(Microtek2_Info *mi, char *device) { SANE_Status status; uint8_t cmd[INQ_CMD_L]; uint8_t *result; uint8_t inqlen; size_t size; int sfd; DBG(30, "scsi_inquiry: mi=%p, device='%s'\n", (void *) mi, device); status = sanei_scsi_open(device, &sfd, scsi_sense_handler, 0); if ( status != SANE_STATUS_GOOD ) { DBG(1, "scsi_inquiry: '%s'\n", sane_strstatus(status)); return status; } INQ_CMD(cmd); INQ_SET_ALLOC(cmd, INQ_ALLOC_L); result = (uint8_t *) alloca(INQ_ALLOC_L); if ( result == NULL ) { DBG(1, "scsi_inquiry: malloc failed\n"); sanei_scsi_close(sfd); return SANE_STATUS_NO_MEM; } size = INQ_ALLOC_L; status = sanei_scsi_cmd(sfd, cmd, sizeof(cmd), result, &size); if ( status != SANE_STATUS_GOOD ) { DBG(1, "scsi_inquiry: '%s'\n", sane_strstatus(status)); sanei_scsi_close(sfd); return status; } INQ_GET_INQLEN(inqlen, result); INQ_SET_ALLOC(cmd, inqlen + INQ_ALLOC_L); result = alloca(inqlen + INQ_ALLOC_L); if ( result == NULL ) { DBG(1, "scsi_inquiry: malloc failed\n"); sanei_scsi_close(sfd); return SANE_STATUS_NO_MEM; } size = inqlen + INQ_ALLOC_L; if (md_dump >= 2 ) dump_area2(cmd, sizeof(cmd), "inquiry"); status = sanei_scsi_cmd(sfd, cmd, sizeof(cmd), result, &size); if ( status != SANE_STATUS_GOOD ) { DBG(1, "scsi_inquiry: cmd '%s'\n", sane_strstatus(status)); sanei_scsi_close(sfd); return status; } sanei_scsi_close(sfd); if (md_dump >= 2 ) { dump_area2((uint8_t *) result, size, "inquiryresult"); dump_area((uint8_t *) result, size, "inquiryresult"); } /* copy results */ INQ_GET_QUAL(mi->device_qualifier, result); INQ_GET_DEVT(mi->device_type, result); INQ_GET_VERSION(mi->scsi_version, result); INQ_GET_VENDOR(mi->vendor, (char *)result); INQ_GET_MODEL(mi->model, (char *)result); INQ_GET_REV(mi->revision, (char *)result); INQ_GET_MODELCODE(mi->model_code, result); return SANE_STATUS_GOOD; } /*---------- scsi_read_attributes() ------------------------------------------*/ static SANE_Status scsi_read_attributes(Microtek2_Info *pmi, char *device, uint8_t scan_source) { SANE_Status status; Microtek2_Info *mi; uint8_t readattributes[RSA_CMD_L]; uint8_t result[RSA_TRANSFERLENGTH]; size_t size; int sfd; mi = &pmi[scan_source]; DBG(30, "scsi_read_attributes: mi=%p, device='%s', source=%d\n", (void *) mi, device, scan_source); RSA_CMD(readattributes); RSA_SETMEDIA(readattributes, scan_source); status = sanei_scsi_open(device, &sfd, scsi_sense_handler, 0); if ( status != SANE_STATUS_GOOD ) { DBG(1, "scsi_read_attributes: open '%s'\n", sane_strstatus(status)); return status; } if (md_dump >= 2 ) dump_area2(readattributes, sizeof(readattributes), "scannerattributes"); size = sizeof(result); status = sanei_scsi_cmd(sfd, readattributes, sizeof(readattributes), result, &size); if ( status != SANE_STATUS_GOOD ) { DBG(1, "scsi_read_attributes: cmd '%s'\n", sane_strstatus(status)); sanei_scsi_close(sfd); return status; } sanei_scsi_close(sfd); /* The X6 appears to lie about the data format for a TMA */ if ( (&pmi[0])->model_code == 0x91 ) result[0] &= 0xfd; /* default value for calib_divisor ... bit49?? */ mi->calib_divisor = 1; /* 9600XL */ if ( (&pmi[0])->model_code == 0xde ) mi->calib_divisor = 2; /* 6400XL has problems in lineart mode*/ if ( (&pmi[0])->model_code == 0x89 ) result[13] &= 0xfe; /* simulate no lineart */ #if 0 result[13] &= 0xfe; /* simulate no lineart */ #endif /* copy all the stuff into the info structure */ RSA_COLOR(mi->color, result); RSA_ONEPASS(mi->onepass, result); RSA_SCANNERTYPE(mi->scanner_type, result); RSA_FEPROM(mi->feprom, result); RSA_DATAFORMAT(mi->data_format, result); RSA_COLORSEQUENCE(mi->color_sequence, result); RSA_NIS(mi->new_image_status, result); RSA_DATSEQ(mi->direction, result); RSA_CCDGAP(mi->ccd_gap, result); RSA_MAX_XRESOLUTION(mi->max_xresolution, result); RSA_MAX_YRESOLUTION(mi->max_yresolution, result); RSA_GEOWIDTH(mi->geo_width, result); RSA_GEOHEIGHT(mi->geo_height, result); RSA_OPTRESOLUTION(mi->opt_resolution, result); RSA_DEPTH(mi->depth, result); /* The X12USL doesn't say that it has 14bit */ if ( (&pmi[0])->model_code == 0xb0 ) mi->depth |= MI_HASDEPTH_14; RSA_SCANMODE(mi->scanmode, result); RSA_CCDPIXELS(mi->ccd_pixels, result); RSA_LUTCAP(mi->lut_cap, result); RSA_DNLDPTRN(mi->has_dnldptrn, result); RSA_GRAINSLCT(mi->grain_slct, result); RSA_SUPPOPT(mi->option_device, result); RSA_CALIBWHITE(mi->calib_white, result); RSA_CALIBSPACE(mi->calib_space, result); RSA_NLENS(mi->nlens, result); RSA_NWINDOWS(mi->nwindows, result); RSA_SHTRNSFEREQU(mi->shtrnsferequ, result); RSA_SCNBTTN(mi->scnbuttn, result); RSA_BUFTYPE(mi->buftype, result); RSA_REDBALANCE(mi->balance[0], result); RSA_GREENBALANCE(mi->balance[1], result); RSA_BLUEBALANCE(mi->balance[2], result); RSA_APSMAXFRAMES(mi->aps_maxframes, result); if (md_dump >= 2 ) dump_area2((uint8_t *) result, sizeof(result), "scannerattributesresults"); if ( md_dump >= 1 && md_dump_clear ) dump_attributes(mi); return SANE_STATUS_GOOD; } /*---------- scsi_read_control_bits() ----------------------------------------*/ static SANE_Status scsi_read_control_bits(Microtek2_Scanner *ms) { SANE_Status status; uint8_t cmd[RCB_CMD_L]; uint32_t byte; int bit; int count_1s; DBG(30, "scsi_read_control_bits: ms=%p, fd=%d\n", (void *) ms, ms->sfd); DBG(30, "ms->control_bytes = %p\n", (void *) ms->control_bytes); RCB_SET_CMD(cmd); RCB_SET_LENGTH(cmd, ms->n_control_bytes); if ( md_dump >= 2) dump_area2(cmd, RCB_CMD_L, "readcontrolbits"); status = sanei_scsi_cmd(ms->sfd, cmd, sizeof(cmd), ms->control_bytes, &ms->n_control_bytes); if ( status != SANE_STATUS_GOOD ) { DBG(1, "scsi_read_control_bits: cmd '%s'\n", sane_strstatus(status)); return status; } if ( md_dump >= 2) dump_area2(ms->control_bytes, ms->n_control_bytes, "readcontrolbitsresult"); count_1s = 0; for ( byte = 0; byte < ms->n_control_bytes; byte++ ) { for ( bit = 0; bit < 8; bit++ ) { if ( (ms->control_bytes[byte] >> bit) & 0x01 ) ++count_1s; } } DBG(20, "read_control_bits: number of 1's in controlbytes: %d\n", count_1s); return SANE_STATUS_GOOD; } /*---------- scsi_set_window() -----------------------------------------------*/ static SANE_Status scsi_set_window(Microtek2_Scanner *ms, int n) { /* n windows, not yet */ /* implemented */ SANE_Status status; uint8_t *setwindow; int size; DBG(30, "scsi_set_window: ms=%p, wnd=%d\n", (void *) ms, n); size = SW_CMD_L + SW_HEADER_L + n * SW_BODY_L; setwindow = (uint8_t *) malloc(size); DBG(100, "scsi_set_window: setwindow= %p, malloc'd %d Bytes\n", (void *) setwindow, size); if ( setwindow == NULL ) { DBG(1, "scsi_set_window: malloc for setwindow failed\n"); return SANE_STATUS_NO_MEM; } memset(setwindow, 0, size); SW_CMD(setwindow); SW_PARAM_LENGTH(setwindow, SW_HEADER_L + n * SW_BODY_L); SW_WNDDESCLEN(setwindow + SW_HEADER_P, SW_WNDDESCVAL); #define POS (setwindow + SW_BODY_P(n-1)) SW_WNDID(POS, n-1); SW_XRESDPI(POS, ms->x_resolution_dpi); SW_YRESDPI(POS, ms->y_resolution_dpi); SW_XPOSTL(POS, ms->x1_dots); SW_YPOSTL(POS, ms->y1_dots); SW_WNDWIDTH(POS, ms->width_dots); SW_WNDHEIGHT(POS, ms->height_dots); SW_THRESHOLD(POS, ms->threshold); SW_IMGCOMP(POS, ms->mode); SW_BITSPERPIXEL(POS, ms->depth); SW_EXTHT(POS, ms->use_external_ht); SW_INTHTINDEX(POS, ms->internal_ht_index); SW_RIF(POS, 1); SW_LENS(POS, 0); /* ???? */ SW_INFINITE(POS, 0); SW_STAY(POS, ms->stay); SW_RAWDAT(POS, ms->rawdat); SW_QUALITY(POS, ms->quality); SW_FASTSCAN(POS, ms->fastscan); SW_MEDIA(POS, ms->scan_source); SW_BRIGHTNESS_M(POS, ms->brightness_m); SW_CONTRAST_M(POS, ms->contrast_m); SW_EXPOSURE_M(POS, ms->exposure_m); SW_SHADOW_M(POS, ms->shadow_m); SW_MIDTONE_M(POS, ms->midtone_m); SW_HIGHLIGHT_M(POS, ms->highlight_m); /* the following properties are only referenced if it's a color scan */ /* but I guess they don't matter at a gray scan */ SW_BRIGHTNESS_R(POS, ms->brightness_r); SW_CONTRAST_R(POS, ms->contrast_r); SW_EXPOSURE_R(POS, ms->exposure_r); SW_SHADOW_R(POS, ms->shadow_r); SW_MIDTONE_R(POS, ms->midtone_r); SW_HIGHLIGHT_R(POS, ms->highlight_r); SW_BRIGHTNESS_G(POS, ms->brightness_g); SW_CONTRAST_G(POS, ms->contrast_g); SW_EXPOSURE_G(POS, ms->exposure_g); SW_SHADOW_G(POS, ms->shadow_g); SW_MIDTONE_G(POS, ms->midtone_g); SW_HIGHLIGHT_G(POS, ms->highlight_g); SW_BRIGHTNESS_B(POS, ms->brightness_b); SW_CONTRAST_B(POS, ms->contrast_b); SW_EXPOSURE_B(POS, ms->exposure_b); SW_SHADOW_B(POS, ms->shadow_b); SW_MIDTONE_B(POS, ms->midtone_b); SW_HIGHLIGHT_B(POS, ms->highlight_b); if ( md_dump >= 2 ) { dump_area2(setwindow, 10, "setwindowcmd"); dump_area2(setwindow + 10 ,8 , "setwindowheader"); dump_area2(setwindow + 18 ,61 , "setwindowbody"); } status = sanei_scsi_cmd(ms->sfd, setwindow, size, NULL, 0); if ( status != SANE_STATUS_GOOD ) DBG(1, "scsi_set_window: '%s'\n", sane_strstatus(status)); DBG(100, "scsi_set_window: free setwindow at %p\n", (void *) setwindow); free((void *) setwindow); return status; } /*---------- scsi_read_image_info() ------------------------------------------*/ static SANE_Status scsi_read_image_info(Microtek2_Scanner *ms) { uint8_t cmd[RII_CMD_L]; uint8_t result[RII_RESULT_L]; size_t size; SANE_Status status; Microtek2_Device *md; md = ms->dev; DBG(30, "scsi_read_image_info: ms=%p\n", (void *) ms); RII_SET_CMD(cmd); if ( md_dump >= 2) dump_area2(cmd, RII_CMD_L, "readimageinfo"); size = sizeof(result); status = sanei_scsi_cmd(ms->sfd, cmd, sizeof(cmd), result, &size); if ( status != SANE_STATUS_GOOD ) { DBG(1, "scsi_read_image_info: '%s'\n", sane_strstatus(status)); return status; } if ( md_dump >= 2) dump_area2(result, size, "readimageinforesult"); /* The V300 returns some values in only two bytes */ if ( !(md->revision==2.70) && (md->model_flags & MD_RII_TWO_BYTES) ) { RII_GET_V300_WIDTHPIXEL(ms->ppl, result); RII_GET_V300_WIDTHBYTES(ms->bpl, result); RII_GET_V300_HEIGHTLINES(ms->src_remaining_lines, result); RII_GET_V300_REMAINBYTES(ms->remaining_bytes, result); } else { RII_GET_WIDTHPIXEL(ms->ppl, result); RII_GET_WIDTHBYTES(ms->bpl, result); RII_GET_HEIGHTLINES(ms->src_remaining_lines, result); RII_GET_REMAINBYTES(ms->remaining_bytes, result); } DBG(30, "scsi_read_image_info: ppl=%d, bpl=%d, lines=%d, remain=%d\n", ms->ppl, ms->bpl, ms->src_remaining_lines, ms->remaining_bytes); return SANE_STATUS_GOOD; } /*---------- scsi_read_image() -----------------------------------------------*/ static SANE_Status scsi_read_image(Microtek2_Scanner *ms, uint8_t *buffer, int bytes_per_pixel) { uint8_t cmd[RI_CMD_L]; SANE_Bool endiantype; SANE_Status status; size_t size; size_t i; uint8_t tmp; DBG(30, "scsi_read_image: ms=%p, buffer=%p\n", (void *) ms, (void *) buffer); ENDIAN_TYPE(endiantype) RI_SET_CMD(cmd); RI_SET_PCORMAC(cmd, endiantype); RI_SET_COLOR(cmd, ms->current_read_color); RI_SET_TRANSFERLENGTH(cmd, ms->transfer_length); DBG(30, "scsi_read_image: transferlength=%d\n", ms->transfer_length); if ( md_dump >= 2 ) dump_area2(cmd, RI_CMD_L, "readimagecmd"); size = ms->transfer_length; status = sanei_scsi_cmd(ms->sfd, cmd, sizeof(cmd), buffer, &size); if ( buffer && ( ms->dev->model_flags & MD_PHANTOM_C6 ) && endiantype ) { switch(bytes_per_pixel) { case 1: break; case 2: for ( i = 1; i < size; i += 2 ) { tmp = buffer[i-1]; buffer[i-1] = buffer[i]; buffer[i] = tmp; } break; default: DBG(1, "scsi_read_image: Unexpected bytes_per_pixel=%d\n", bytes_per_pixel); } } if ( status != SANE_STATUS_GOOD ) DBG(1, "scsi_read_image: '%s'\n", sane_strstatus(status)); if ( md_dump > 3 ) dump_area2(buffer, ms->transfer_length, "readimageresult"); return status; } /*---------- scsi_read_image_status() ----------------------------------------*/ static SANE_Status scsi_read_image_status(Microtek2_Scanner *ms) { Microtek2_Device *md; Microtek2_Info *mi; uint8_t cmd[RIS_CMD_L]; uint8_t dummy; size_t dummy_length; SANE_Status status; SANE_Bool endian_type; md = ms->dev; mi = &md->info[md->scan_source]; DBG(30, "scsi_read_image_status: ms=%p\n", (void *) ms); ENDIAN_TYPE(endian_type) RIS_SET_CMD(cmd); RIS_SET_PCORMAC(cmd, endian_type); RIS_SET_COLOR(cmd, ms->current_read_color); /* mi->new_image_status = SANE_TRUE; */ /* for testing*/ if ( mi->new_image_status == SANE_TRUE ) { DBG(30, "scsi_read_image_status: use new image status \n"); dummy_length = 1; cmd[8] = 1; } else { DBG(30, "scsi_read_image_status: use old image status \n"); dummy_length = 0; cmd[8] = 0; } if ( md_dump >= 2 ) dump_area2(cmd, sizeof(cmd), "readimagestatus"); status = sanei_scsi_cmd(ms->sfd, cmd, sizeof(cmd), &dummy, &dummy_length); if ( mi->new_image_status == SANE_TRUE ) { if ( dummy == 0 ) status = SANE_STATUS_GOOD; else status = SANE_STATUS_DEVICE_BUSY; } /* For some (X6USB) scanner We say we are going to try to read 1 byte of data (as recommended in the Microtek SCSI command documentation under "New Image Status") so that dubious SCSI host adapters (like the one in at least some Microtek X6 USB scanners) don't get wedged trying to do a zero length read. However, we do not actually try to read this byte of data, as that wedges the USB scanner as well. IOW the SCSI command says we are going to read 1 byte, but in fact we don't: */ /*cmd[8] = 1; status = sanei_scsi_cmd(ms->sfd, cmd, sizeof(cmd), &dummy, 0); */ if ( status != SANE_STATUS_GOOD ) DBG(1, "scsi_read_image_status: '%s'\n", sane_strstatus(status)); return status; } /*---------- scsi_read_shading () --------------------------------------------*/ static SANE_Status scsi_read_shading(Microtek2_Scanner *ms, uint8_t *buffer, uint32_t length) { uint8_t cmd[RSI_CMD_L]; SANE_Bool endiantype; SANE_Status status = SANE_STATUS_GOOD; size_t size; DBG(30, "scsi_read_shading: pos=%p, size=%d, word=%d, color=%d, dark=%d\n", (void *) buffer, length, ms->word, ms->current_color, ms->dark); size = length; RSI_SET_CMD(cmd); ENDIAN_TYPE(endiantype) RSI_SET_PCORMAC(cmd, endiantype); RSI_SET_COLOR(cmd, ms->current_color); RSI_SET_DARK(cmd, ms->dark); RSI_SET_WORD(cmd, ms->word); RSI_SET_TRANSFERLENGTH(cmd, size); if ( md_dump >= 2 ) dump_area2(cmd, RSI_CMD_L, "readshading"); DBG(100, "scsi_read_shading: sfd=%d, cmd=%p, sizeofcmd=%lu," "dest=%p, destsize=%lu\n", ms->sfd, (void *) cmd, (u_long) sizeof(cmd), (void *) buffer, (u_long) size); status = sanei_scsi_cmd(ms->sfd, cmd, sizeof(cmd), buffer, &size); if ( status != SANE_STATUS_GOOD ) DBG(1, "scsi_read_shading: '%s'\n", sane_strstatus(status)); if ( md_dump > 3) dump_area2(buffer, size, "readshadingresult"); return status; } /*---------- scsi_send_shading () --------------------------------------------*/ static SANE_Status scsi_send_shading(Microtek2_Scanner *ms, uint8_t *shading_data, uint32_t length, uint8_t dark) { SANE_Bool endiantype; SANE_Status status; size_t size; uint8_t *cmd; DBG(30, "scsi_send_shading: pos=%p, size=%d, word=%d, color=%d, dark=%d\n", (void *) shading_data, length, ms->word, ms->current_color, dark); cmd = (uint8_t *) malloc(SSI_CMD_L + length); DBG(100, "scsi_send_shading: cmd=%p, malloc'd %d bytes\n", (void *) cmd, SSI_CMD_L + length); if ( cmd == NULL ) { DBG(1, "scsi_send_shading: Couldn't get buffer for shading table\n"); return SANE_STATUS_NO_MEM; } SSI_SET_CMD(cmd); ENDIAN_TYPE(endiantype) SSI_SET_PCORMAC(cmd, endiantype); SSI_SET_COLOR(cmd, ms->current_color); SSI_SET_DARK(cmd, dark); SSI_SET_WORD(cmd, ms->word); SSI_SET_TRANSFERLENGTH(cmd, length); memcpy(cmd + SSI_CMD_L, shading_data, length); size = length; if ( md_dump >= 2 ) dump_area2(cmd, SSI_CMD_L, "sendshading"); if ( md_dump >= 3 ) dump_area2(cmd + SSI_CMD_L, size, "sendshadingdata"); status = sanei_scsi_cmd(ms->sfd, cmd, size + SSI_CMD_L, NULL, 0); if ( status != SANE_STATUS_GOOD ) DBG(1, "scsi_send_shading: '%s'\n", sane_strstatus(status)); DBG(100, "free cmd at %p\n", (void *) cmd); free((void *) cmd); return status; } /*---------- scsi_read_system_status() ---------------------------------------*/ static SANE_Status scsi_read_system_status(Microtek2_Device *md, int fd) { uint8_t cmd[RSS_CMD_L]; uint8_t result[RSS_RESULT_L]; int sfd; size_t size; SANE_Status status; DBG(30, "scsi_read_system_status: md=%p, fd=%d\n", (void *) md, fd); if ( fd == -1 ) { status = sanei_scsi_open(md->name, &sfd, scsi_sense_handler, 0); if ( status != SANE_STATUS_GOOD ) { DBG(1, "scsi_read_system_status: open '%s'\n", sane_strstatus(status)); return status; } } else sfd = fd; RSS_CMD(cmd); if ( md_dump >= 2) dump_area2(cmd, RSS_CMD_L, "readsystemstatus"); size = sizeof(result); status = sanei_scsi_cmd(sfd, cmd, sizeof(cmd), result, &size); if ( status != SANE_STATUS_GOOD ) { DBG(1, "scsi_read_system_status: cmd '%s'\n", sane_strstatus(status)); sanei_scsi_close(sfd); return status; } if ( fd == -1 ) sanei_scsi_close(sfd); if ( md_dump >= 2) dump_area2(result, size, "readsystemstatusresult"); md->status.sskip = RSS_SSKIP(result); md->status.ntrack = RSS_NTRACK(result); md->status.ncalib = RSS_NCALIB(result); md->status.tlamp = RSS_TLAMP(result); md->status.flamp = RSS_FLAMP(result); md->status.rdyman= RSS_RDYMAN(result); md->status.trdy = RSS_TRDY(result); md->status.frdy = RSS_FRDY(result); md->status.adp = RSS_RDYMAN(result); md->status.detect = RSS_DETECT(result); md->status.adptime = RSS_ADPTIME(result); md->status.lensstatus = RSS_LENSSTATUS(result); md->status.aloff = RSS_ALOFF(result); md->status.timeremain = RSS_TIMEREMAIN(result); md->status.tmacnt = RSS_TMACNT(result); md->status.paper = RSS_PAPER(result); md->status.adfcnt = RSS_ADFCNT(result); md->status.currentmode = RSS_CURRENTMODE(result); md->status.buttoncount = RSS_BUTTONCOUNT(result); return SANE_STATUS_GOOD; } /*---------- scsi_request_sense() --------------------------------------------*/ /* currently not used */ #if 0 static SANE_Status scsi_request_sense(Microtek2_Scanner *ms) { uint8_t requestsense[RQS_CMD_L]; uint8_t buffer[100]; SANE_Status status; int size; int asl; int as_info_length; DBG(30, "scsi_request_sense: ms=%p\n", (void *) ms); RQS_CMD(requestsense); RQS_ALLOCLENGTH(requestsense, 100); size = sizeof(buffer); status = sanei_scsi_cmd(ms->sfd, requestsense, sizeof(requestsense), buffer, &size); if ( status != SANE_STATUS_GOOD ) { DBG(1, "scsi_request_sense: '%s'\n", sane_strstatus(status)); return status; } if ( md_dump >= 2 ) dump_area2(buffer, size, "requestsenseresult"); dump_area(buffer, RQS_LENGTH(buffer), "RequestSense"); asl = RQS_ASL(buffer); if ( (as_info_length = RQS_ASINFOLENGTH(buffer)) > 0 ) DBG(25, "scsi_request_sense: info '%.*s'\n", as_info_length, RQS_ASINFO(buffer)); return SANE_STATUS_GOOD; } #endif /*---------- scsi_send_system_status() ---------------------------------------*/ static SANE_Status scsi_send_system_status(Microtek2_Device *md, int fd) { uint8_t cmd[SSS_CMD_L + SSS_DATA_L]; uint8_t *pos; int sfd; SANE_Status status; DBG(30, "scsi_send_system_status: md=%p, fd=%d\n", (void *) md, fd); memset(cmd, 0, SSS_CMD_L + SSS_DATA_L); if ( fd == -1 ) { status = sanei_scsi_open(md->name, &sfd, scsi_sense_handler, 0); if ( status != SANE_STATUS_GOOD ) { DBG(1, "scsi_send_system_status: open '%s'\n", sane_strstatus(status)); return status; } } else sfd = fd; SSS_CMD(cmd); pos = cmd + SSS_CMD_L; SSS_STICK(pos, md->status.stick); SSS_NTRACK(pos, md->status.ntrack); SSS_NCALIB(pos, md->status.ncalib); SSS_TLAMP(pos, md->status.tlamp); SSS_FLAMP(pos, md->status.flamp); SSS_RESERVED17(pos, md->status.reserved17); SSS_RDYMAN(pos, md->status.rdyman); SSS_TRDY(pos, md->status.trdy); SSS_FRDY(pos, md->status.frdy); SSS_ADP(pos, md->status.adp); SSS_DETECT(pos, md->status.detect); SSS_ADPTIME(pos, md->status.adptime); SSS_LENSSTATUS(pos, md->status.lensstatus); SSS_ALOFF(pos, md->status.aloff); SSS_TIMEREMAIN(pos, md->status.timeremain); SSS_TMACNT(pos, md->status.tmacnt); SSS_PAPER(pos, md->status.paper); SSS_ADFCNT(pos, md->status.adfcnt); SSS_CURRENTMODE(pos, md->status.currentmode); SSS_BUTTONCOUNT(pos, md->status.buttoncount); if ( md_dump >= 2) { dump_area2(cmd, SSS_CMD_L, "sendsystemstatus"); dump_area2(cmd + SSS_CMD_L, SSS_DATA_L, "sendsystemstatusdata"); } status = sanei_scsi_cmd(sfd, cmd, sizeof(cmd), NULL, 0); if ( status != SANE_STATUS_GOOD ) DBG(1, "scsi_send_system_status: '%s'\n", sane_strstatus(status)); if ( fd == -1 ) sanei_scsi_close(sfd); return status; } /*---------- scsi_sense_handler() --------------------------------------------*/ /* rewritten 19.12.2001 for better SANE_STATUS return codes */ static SANE_Status scsi_sense_handler (int fd, u_char *sense, void *arg) { int as_info_length; uint8_t sense_key; uint8_t asc; uint8_t ascq; DBG(30, "scsi_sense_handler: fd=%d, sense=%p arg=%p\n", fd, (void *) sense, arg); dump_area(sense, RQS_LENGTH(sense), "SenseBuffer"); sense_key = RQS_SENSEKEY(sense); asc = RQS_ASC(sense); ascq = RQS_ASCQ(sense); DBG(5, "scsi_sense_handler: SENSE KEY (0x%02x), " "ASC (0x%02x), ASCQ (0x%02x)\n", sense_key, asc, ascq); if ( (as_info_length = RQS_ASINFOLENGTH(sense)) > 0 ) DBG(5,"scsi_sense_handler: info: '%*s'\n", as_info_length, RQS_ASINFO(sense)); switch ( sense_key ) { case RQS_SENSEKEY_NOSENSE: return SANE_STATUS_GOOD; case RQS_SENSEKEY_HWERR: case RQS_SENSEKEY_ILLEGAL: case RQS_SENSEKEY_VENDOR: if ( asc == 0x4a && ascq == 0x00 ) DBG(5, "scsi_sense_handler: Command phase error\n"); else if ( asc == 0x2c && ascq == 0x00 ) DBG(5, "scsi_sense_handler: Command sequence error\n"); else if ( asc == 0x4b && ascq == 0x00 ) DBG(5, "scsi_sense_handler: Data phase error\n"); else if ( asc == 0x40 ) { DBG(5, "scsi_sense_handler: Hardware diagnostic failure:\n"); switch ( ascq ) { case RQS_ASCQ_CPUERR: DBG(5, "scsi_sense_handler: CPU error\n"); break; case RQS_ASCQ_SRAMERR: DBG(5, "scsi_sense_handler: SRAM error\n"); break; case RQS_ASCQ_DRAMERR: DBG(5, "scsi_sense_handler: DRAM error\n"); break; case RQS_ASCQ_DCOFF: DBG(5, "scsi_sense_handler: DC Offset error\n"); break; case RQS_ASCQ_GAIN: DBG(5, "scsi_sense_handler: Gain error\n"); break; case RQS_ASCQ_POS: DBG(5, "scsi_sense_handler: Positioning error\n"); break; default: DBG(5, "scsi_sense_handler: Unknown combination of ASC" " (0x%02x) and ASCQ (0x%02x)\n", asc, ascq); break; } } else if ( asc == 0x00 && ascq == 0x05) { DBG(5, "scsi_sense_handler: End of data detected\n"); return SANE_STATUS_EOF; } else if ( asc == 0x3d && ascq == 0x00) DBG(5, "scsi_sense_handler: Invalid bit in IDENTIFY\n"); else if ( asc == 0x2c && ascq == 0x02 ) /* Ok */ DBG(5, "scsi_sense_handler: Invalid comb. of windows specified\n"); else if ( asc == 0x20 && ascq == 0x00 ) /* Ok */ DBG(5, "scsi_sense_handler: Invalid command opcode\n"); else if ( asc == 0x24 && ascq == 0x00 ) /* Ok */ DBG(5, "scsi_sense_handler: Invalid field in CDB\n"); else if ( asc == 0x26 && ascq == 0x00 ) DBG(5, "scsi_sense_handler: Invalid field in the param list\n"); else if ( asc == 0x49 && ascq == 0x00 ) DBG(5, "scsi_sense_handler: Invalid message error\n"); else if ( asc == 0x60 && ascq == 0x00 ) DBG(5, "scsi_sense_handler: Lamp failure\n"); else if ( asc == 0x25 && ascq == 0x00 ) DBG(5, "scsi_sense_handler: Unsupported logic. unit\n"); else if ( asc == 0x53 && ascq == 0x00 ) { DBG(5, "scsi_sense_handler: ADF paper jam or no paper\n"); return SANE_STATUS_NO_DOCS; } else if ( asc == 0x54 && ascq == 0x00 ) { DBG(5, "scsi_sense_handler: Media bumping\n"); return SANE_STATUS_JAMMED; /* Don't know if this is right! */ } else if ( asc == 0x55 && ascq == 0x00 ) { DBG(5, "scsi_sense_handler: Scan Job stopped or cancelled\n"); return SANE_STATUS_CANCELLED; } else if ( asc == 0x3a && ascq == 0x00 ) { DBG(5, "scsi_sense_handler: Media (ADF or TMA) not available\n"); return SANE_STATUS_NO_DOCS; } else if ( asc == 0x3a && ascq == 0x01 ) { DBG(5, "scsi_sense_handler: Door is not closed\n"); return SANE_STATUS_COVER_OPEN; } else if ( asc == 0x3a && ascq == 0x02 ) DBG(5, "scsi_sense_handler: Door is not opened\n"); else if ( asc == 0x00 && ascq == 0x00 ) DBG(5, "scsi_sense_handler: No additional sense information\n"); /* Ok */ else if ( asc == 0x1a && ascq == 0x00 ) DBG(5, "scsi_sense_handler: Parameter list length error\n"); else if ( asc == 0x26 && ascq == 0x02 ) DBG(5, "scsi_sense_handler: Parameter value invalid\n"); else if ( asc == 0x03 && ascq == 0x00 ) DBG(5, "scsi_sense_handler: Peripheral device write fault - " "Firmware Download Error\n"); else if ( asc == 0x2c && ascq == 0x01 ) DBG(5, "scsi_sense_handler: Too many windows specified\n"); else if ( asc == 0x80 && ascq == 0x00 ) DBG(5, "scsi_sense_handler: Target abort scan\n"); else if ( asc == 0x96 && ascq == 0x08 ) { DBG(5, "scsi_sense_handler: Firewire Device busy\n"); return SANE_STATUS_DEVICE_BUSY; } else DBG(5, "scsi_sense_handler: Unknown combination of SENSE KEY " "(0x%02x), ASC (0x%02x) and ASCQ (0x%02x)\n", sense_key, asc, ascq); return SANE_STATUS_IO_ERROR; default: DBG(5, "scsi_sense_handler: Unknown sense key (0x%02x)\n", sense_key); return SANE_STATUS_IO_ERROR; } } /*---------- scsi_test_unit_ready() ------------------------------------------*/ static SANE_Status scsi_test_unit_ready(Microtek2_Device *md) { SANE_Status status; uint8_t tur[TUR_CMD_L]; int sfd; DBG(30, "scsi_test_unit_ready: md=%s\n", md->name); TUR_CMD(tur); status = sanei_scsi_open(md->name, &sfd, scsi_sense_handler, 0); if ( status != SANE_STATUS_GOOD ) { DBG(1, "scsi_test_unit_ready: open '%s'\n", sane_strstatus(status)); return status; } if ( md_dump >= 2 ) dump_area2(tur, sizeof(tur), "testunitready"); status = sanei_scsi_cmd(sfd, tur, sizeof(tur), NULL, 0); if ( status != SANE_STATUS_GOOD ) DBG(1, "scsi_test_unit_ready: cmd '%s'\n", sane_strstatus(status)); sanei_scsi_close(sfd); return status; } /*---------- sane_start() ----------------------------------------------------*/ SANE_Status sane_start(SANE_Handle handle) { SANE_Status status = SANE_STATUS_GOOD; Microtek2_Scanner *ms = handle; Microtek2_Device *md; Microtek2_Info *mi; uint8_t *pos; int color, rc, retry; DBG(30, "sane_start: handle=0x%p\n", handle); md = ms->dev; mi = &md->info[md->scan_source]; ms->n_control_bytes = md->n_control_bytes; if ( md->model_flags & MD_READ_CONTROL_BIT ) { if (ms->control_bytes) free((void *)ms->control_bytes); ms->control_bytes = (uint8_t *) malloc(ms->n_control_bytes); DBG(100, "sane_start: ms->control_bytes=%p, malloc'd %lu bytes\n", (void *) ms->control_bytes, (u_long) ms->n_control_bytes); if ( ms->control_bytes == NULL ) { DBG(1, "sane_start: malloc() for control bits failed\n"); status = SANE_STATUS_NO_MEM; goto cleanup; } } if (ms->sfd < 0) /* first or only pass of this scan */ { /* open device */ for ( retry = 0; retry < 10; retry++ ) { status = sanei_scsi_open (md->sane.name, &ms->sfd, scsi_sense_handler, 0); if ( status != SANE_STATUS_DEVICE_BUSY ) break; DBG(30, "sane_start: Scanner busy, trying again\n"); sleep(1); } if ( status != SANE_STATUS_GOOD ) { DBG(1, "sane_start: scsi_open: '%s'\n", sane_strstatus(status)); goto cleanup; } status = scsi_read_system_status(md, ms->sfd); if ( status != SANE_STATUS_GOOD ) goto cleanup; if ( ms->val[OPT_CALIB_BACKEND].w == SANE_TRUE ) DBG(30, "sane_start: backend calibration on\n"); else DBG(30, "sane_start: backend calibration off\n"); if ( ( ms->val[OPT_CALIB_BACKEND].w == SANE_TRUE ) && !( md->model_flags & MD_PHANTOM336CX_TYPE_SHADING ) ) { /* Read shading only once - possible with CIS scanners */ /* assuming only CIS scanners use Controlbits */ if ( ( md->shading_table_w == NULL ) || !( md->model_flags & MD_READ_CONTROL_BIT ) ) { status = get_scan_parameters(ms); if ( status != SANE_STATUS_GOOD ) goto cleanup; status = read_shading_image(ms); if ( status != SANE_STATUS_GOOD ) goto cleanup; } } status = get_scan_parameters(ms); if ( status != SANE_STATUS_GOOD ) goto cleanup; status = scsi_read_system_status(md, ms->sfd); if ( status != SANE_STATUS_GOOD ) goto cleanup; md->status.aloff |= 128; md->status.timeremain = 10; if ( ms->scan_source == MS_SOURCE_FLATBED || ms->scan_source == MS_SOURCE_ADF ) { md->status.flamp |= MD_FLAMP_ON; md->status.tlamp &= ~MD_TLAMP_ON; } else { md->status.flamp &= ~MD_FLAMP_ON; md->status.tlamp |= MD_TLAMP_ON; } if ( ms->lightlid35 ) { md->status.flamp &= ~MD_FLAMP_ON; /* md->status.tlamp |= MD_TLAMP_ON;*/ /* with this line on some scanners (X6, 0x91) the Flamp goes on */ } if ( ms->no_backtracking ) md->status.ntrack |= MD_NTRACK_ON; else md->status.ntrack &= ~MD_NTRACK_ON; status = scsi_send_system_status(md, ms->sfd); if ( status != SANE_STATUS_GOOD ) goto cleanup; /* calculate gamma: we assume, that the gamma values are transferred */ /* with one send gamma command, even if it is a 3 pass scanner */ if ( md->model_flags & MD_NO_GAMMA ) { ms->lut_size = (int) pow(2.0, (double) ms->depth); ms->lut_entry_size = ms->depth > 8 ? 2 : 1; } else { get_lut_size(mi, &ms->lut_size, &ms->lut_entry_size); } ms->lut_size_bytes = ms->lut_size * ms->lut_entry_size; ms->word = (ms->lut_entry_size == 2); ms->gamma_table = (uint8_t *) malloc(3 * ms->lut_size_bytes ); DBG(100, "sane_start: ms->gamma_table=%p, malloc'd %d bytes\n", (void *) ms->gamma_table, 3 * ms->lut_size_bytes); if ( ms->gamma_table == NULL ) { DBG(1, "sane_start: malloc for gammatable failed\n"); status = SANE_STATUS_NO_MEM; goto cleanup; } for ( color = 0; color < 3; color++ ) { pos = ms->gamma_table + color * ms->lut_size_bytes; calculate_gamma(ms, pos, color, ms->gamma_mode); } /* Some models ignore the settings for the exposure time, */ /* so we must do it ourselves. Apparently this seems to be */ /* the case for all models that have the chunky data format */ if ( mi->data_format == MI_DATAFMT_CHUNKY ) set_exposure(ms); if ( ! (md->model_flags & MD_NO_GAMMA) ) { status = scsi_send_gamma(ms); if ( status != SANE_STATUS_GOOD ) goto cleanup; } status = scsi_set_window(ms, 1); if ( status != SANE_STATUS_GOOD ) goto cleanup; ms->scanning = SANE_TRUE; ms->cancelled = SANE_FALSE; } ++ms->current_pass; status = scsi_read_image_info(ms); if ( status != SANE_STATUS_GOOD ) goto cleanup; status = prepare_buffers(ms); if ( status != SANE_STATUS_GOOD ) goto cleanup; status = calculate_sane_params(ms); if ( status != SANE_STATUS_GOOD ) goto cleanup; if ( !( md->model_flags & MD_NO_RIS_COMMAND ) ) { /* !!FIXME!! - hack for C6USB because RIS over USB doesn't wait until */ /* scanner ready */ if (mi->model_code == 0x9a) sleep(2); status = scsi_wait_for_image(ms); if ( status != SANE_STATUS_GOOD ) goto cleanup; } if ( ms->calib_backend && ( md->model_flags & MD_PHANTOM336CX_TYPE_SHADING ) && ( ( md->shading_table_w == NULL ) || ( ms->mode != md->shading_table_contents ) ) ) { status = read_cx_shading(ms); if ( status != SANE_STATUS_GOOD ) goto cleanup; } if ( ms->lightlid35 ) /* hopefully this leads to a switched off flatbed lamp with lightlid */ { status = scsi_read_system_status(md, ms->sfd); if ( status != SANE_STATUS_GOOD ) goto cleanup; md->status.flamp &= ~MD_FLAMP_ON; md->status.tlamp &= ~MD_TLAMP_ON; status = scsi_send_system_status(md, ms->sfd); if ( status != SANE_STATUS_GOOD ) goto cleanup; } if ( md->model_flags & MD_READ_CONTROL_BIT ) { status = scsi_read_control_bits(ms); if ( status != SANE_STATUS_GOOD ) goto cleanup; if ( ms->calib_backend ) { status = condense_shading(ms); if ( status != SANE_STATUS_GOOD ) goto cleanup; } } /* open a pipe and fork a child process, that actually reads the data */ rc = pipe(ms->fd); if ( rc == -1 ) { DBG(1, "sane_start: pipe failed\n"); status = SANE_STATUS_IO_ERROR; goto cleanup; } /* create reader routine as new thread or process */ ms->pid = sanei_thread_begin( reader_process,(void*) ms); if ( !sanei_thread_is_valid (ms->pid) ) { DBG(1, "sane_start: fork failed\n"); status = SANE_STATUS_IO_ERROR; goto cleanup; } if (sanei_thread_is_forked()) close(ms->fd[1]); return SANE_STATUS_GOOD; cleanup: cleanup_scanner(ms); return status; } /*---------- prepare_buffers -------------------------------------------------*/ static SANE_Status prepare_buffers(Microtek2_Scanner *ms) { SANE_Status status; Microtek2_Device *md; Microtek2_Info *mi; uint32_t strip_lines; int i; status = SANE_STATUS_GOOD; DBG(30, "prepare_buffers: ms=0x%p\n", (void *) ms); md = ms->dev; mi = &md->info[md->scan_source]; /* calculate maximum number of lines to read */ strip_lines = (int) ((double) ms->y_resolution_dpi * md->opts.strip_height); if ( strip_lines == 0 ) strip_lines = 1; /* calculate number of lines that fit into the source buffer */ #ifdef TESTBACKEND ms->src_max_lines = MIN( 5000000 / ms->bpl, strip_lines); #else ms->src_max_lines = MIN( sanei_scsi_max_request_size / ms->bpl, strip_lines); #endif if ( ms->src_max_lines == 0 ) { DBG(1, "sane_start: Scan buffer too small\n"); status = SANE_STATUS_IO_ERROR; goto cleanup; } /* allocate buffers */ ms->src_buffer_size = ms->src_max_lines * ms->bpl; if ( ms->mode == MS_MODE_COLOR && mi->data_format == MI_DATAFMT_LPLSEGREG ) { /* In this case the data is not necessarily in the order RGB */ /* and there may be different numbers of read red, green and blue */ /* segments. We allocate a second buffer to read new lines in */ /* and hold undelivered pixels in the other buffer */ int extra_buf_size; extra_buf_size = 2 * ms->bpl * mi->ccd_gap * (int) ceil( (double) mi->max_yresolution / (double) mi->opt_resolution); for ( i = 0; i < 2; i++ ) { if ( ms->buf.src_buffer[i] ) free((void *) ms->buf.src_buffer[i]); ms->buf.src_buffer[i] = (uint8_t *) malloc(ms->src_buffer_size + extra_buf_size); DBG(100, "prepare_buffers: ms->buf.src_buffer[%d]=%p," "malloc'd %d bytes\n", i, (void *) ms->buf.src_buffer[i], ms->src_buffer_size + extra_buf_size); if ( ms->buf.src_buffer[i] == NULL ) { DBG(1, "sane_start: malloc for scan buffer failed\n"); status = SANE_STATUS_NO_MEM; goto cleanup; } } ms->buf.free_lines = ms->src_max_lines + extra_buf_size / ms->bpl; ms->buf.free_max_lines = ms->buf.free_lines; ms->buf.src_buf = ms->buf.src_buffer[0]; ms->buf.current_src = 0; /* index to current buffer */ } else { if ( ms->buf.src_buf ) free((void *) ms->buf.src_buf); ms->buf.src_buf = malloc(ms->src_buffer_size); DBG(100, "sane_start: ms->buf.src_buf=%p, malloc'd %d bytes\n", (void *) ms->buf.src_buf, ms->src_buffer_size); if ( ms->buf.src_buf == NULL ) { DBG(1, "sane_start: malloc for scan buffer failed\n"); status = SANE_STATUS_NO_MEM; goto cleanup; } } for ( i = 0; i < 3; i++ ) { ms->buf.current_pos[i] = ms->buf.src_buffer[0]; ms->buf.planes[0][i] = 0; ms->buf.planes[1][i] = 0; } /* allocate a temporary buffer for the data, if auto_adjust threshold */ /* is selected. */ if ( ms->auto_adjust == 1 ) { ms->temporary_buffer = (uint8_t *) malloc(ms->remaining_bytes); DBG(100, "sane_start: ms->temporary_buffer=%p, malloc'd %d bytes\n", (void *) ms->temporary_buffer, ms->remaining_bytes); if ( ms->temporary_buffer == NULL ) { DBG(1, "sane_start: malloc() for temporary buffer failed\n"); status = SANE_STATUS_NO_MEM; goto cleanup; } } else ms->temporary_buffer = NULL; /* some data formats have additional information in a scan line, which */ /* is not transferred to the frontend; real_bpl is the number of bytes */ /* per line, that is copied into the frontend's buffer */ ms->real_bpl = (uint32_t) ceil( ((double) ms->ppl * (double) ms->bits_per_pixel_out) / 8.0 ); if ( mi->onepass && ms->mode == MS_MODE_COLOR ) ms->real_bpl *= 3; ms->real_remaining_bytes = ms->real_bpl * ms->src_remaining_lines; return SANE_STATUS_GOOD; cleanup: cleanup_scanner(ms); return status; } static void write_shading_buf_pnm(Microtek2_Scanner *ms, uint32_t lines) { FILE *outfile; uint16_t pixel, color, linenr, factor; unsigned char img_val_out; float img_val = 0; Microtek2_Device *md; Microtek2_Info *mi; md = ms->dev; mi = &md->info[md->scan_source]; if ( mi->depth & MI_HASDEPTH_16 ) factor = 256; else if ( mi->depth & MI_HASDEPTH_14 ) factor = 64; else if ( mi->depth & MI_HASDEPTH_12 ) factor = 16; else if ( mi->depth & MI_HASDEPTH_10 ) factor = 4; else factor = 1; if ( md->model_flags & MD_16BIT_TRANSFER ) factor = 256; outfile = fopen("shading_buf_w.pnm", "w"); fprintf(outfile, "P6\n#imagedata\n%d %d\n255\n", mi->geo_width / mi->calib_divisor, lines); for ( linenr=0; linenr < lines; linenr++ ) { if (mi->data_format == MI_DATAFMT_LPLSEGREG) { DBG(1, "Output of shading buffer unsupported for" "Segreg Data format\n"); break; } for ( pixel=0; pixel < (uint16_t) (mi->geo_width / mi->calib_divisor); pixel++) { for ( color=0; color < 3; color++ ) { switch( mi->data_format ) { case MI_DATAFMT_LPLCONCAT: if ( md->shading_depth > 8) img_val = *((uint16_t *) ms->shading_image + linenr * ( ms->bpl / ms->lut_entry_size ) + mi->color_sequence[color] * ( ms->bpl / ms->lut_entry_size / 3 ) + pixel); else img_val = *((uint8_t *) ms->shading_image + linenr * ( ms->bpl / ms->lut_entry_size ) + mi->color_sequence[color] * ( ms->bpl / ms->lut_entry_size / 3 ) + pixel); break; case MI_DATAFMT_CHUNKY: case MI_DATAFMT_9800: img_val = *((uint16_t *)ms->shading_image + linenr * 3 * ( mi->geo_width / mi->calib_divisor ) + 3 * pixel + mi->color_sequence[color]); break; } img_val /= factor; img_val_out = (unsigned char)img_val; fputc(img_val_out, outfile); } } } fclose(outfile); return; } static void write_shading_pnm(Microtek2_Scanner *ms) { FILE *outfile_w = NULL, *outfile_d = NULL; int pixel, color, line, offset, num_shading_pixels, output_height; uint16_t img_val, factor; Microtek2_Device *md; Microtek2_Info *mi; output_height = 180; md = ms->dev; mi = &md->info[md->scan_source]; DBG(30, "write_shading_pnm: ms=%p\n", (void *) ms); if ( mi->depth & MI_HASDEPTH_16 ) factor = 256; else if ( mi->depth & MI_HASDEPTH_14 ) factor = 64; else if ( mi->depth & MI_HASDEPTH_12 ) factor = 16; else if ( mi->depth & MI_HASDEPTH_10 ) factor = 4; else factor = 1; if ( md->model_flags & MD_16BIT_TRANSFER ) factor = 256; if ( md->model_flags & MD_PHANTOM336CX_TYPE_SHADING ) num_shading_pixels = ms->n_control_bytes * 8; else num_shading_pixels = mi->geo_width / mi->calib_divisor; if ( md->shading_table_w != NULL ) { outfile_w = fopen("microtek2_shading_w.pnm", "w"); fprintf(outfile_w, "P6\n#imagedata\n%d %d\n255\n", num_shading_pixels, output_height); } if ( md->shading_table_d != NULL ) { outfile_d = fopen("microtek2_shading_d.pnm", "w"); fprintf(outfile_d, "P6\n#imagedata\n%d %d\n255\n", num_shading_pixels, output_height); } for ( line=0; line < output_height; ++line ) { for ( pixel=0; pixel < num_shading_pixels ; ++pixel) { for ( color=0; color < 3; ++color ) { offset = mi->color_sequence[color] * num_shading_pixels + pixel; if ( md->shading_table_w != NULL ) { if ( ms->lut_entry_size == 2 ) { img_val = *((uint16_t *) md->shading_table_w + offset ); img_val /= factor; } else img_val = *((uint8_t *) md->shading_table_w + offset ); fputc((unsigned char)img_val, outfile_w); } if ( md->shading_table_d != NULL ) { if ( ms->lut_entry_size == 2 ) { img_val = *((uint16_t *) md->shading_table_d + offset ); img_val /= factor; } else img_val = *((uint8_t *) md->shading_table_d + offset ); fputc((unsigned char)img_val, outfile_d); } } } } if ( md->shading_table_w != NULL ) fclose(outfile_w); if ( md->shading_table_d != NULL ) fclose(outfile_d); return; } static void write_cshading_pnm(Microtek2_Scanner *ms) { FILE *outfile; Microtek2_Device *md; Microtek2_Info *mi; int pixel, color, line, offset, img_val, img_height=30, factor; md = ms->dev; mi = &md->info[md->scan_source]; if ( mi->depth & MI_HASDEPTH_16 ) factor = 256; else if ( mi->depth & MI_HASDEPTH_14 ) factor = 64; else if ( mi->depth & MI_HASDEPTH_12 ) factor = 16; else if ( mi->depth & MI_HASDEPTH_10 ) factor = 4; else factor = 1; if ( md->model_flags & MD_16BIT_TRANSFER ) factor = 256; outfile = fopen("microtek2_cshading_w.pnm", "w"); if ( ms->mode == MS_MODE_COLOR ) fprintf(outfile, "P6\n#imagedata\n%d %d\n255\n", ms->ppl, img_height); else fprintf(outfile, "P5\n#imagedata\n%d %d\n255\n", ms->ppl, img_height); for ( line=0; line < img_height; ++line ) { for ( pixel=0; pixel < (int)ms->ppl; ++pixel) { for ( color=0; color < 3; ++color ) { offset = color * (int)ms->ppl + pixel; if ( ms->lut_entry_size == 1 ) img_val = (int) *((uint8_t *)ms->condensed_shading_w + offset); else { img_val = (int) *((uint16_t *)ms->condensed_shading_w + offset); img_val /= factor; } fputc((unsigned char)img_val, outfile); if ( ms->mode == MS_MODE_GRAY ) break; } } } fclose(outfile); return; } /*---------- condense_shading() ----------------------------------------------*/ static SANE_Status condense_shading(Microtek2_Scanner *ms) { /* This function extracts the relevant shading pixels from */ /* the shading image according to the 1's in the result of */ /* 'read control bits', and stores them in a memory block. */ /* We will then have as many shading pixels as there are */ /* pixels per line. The order of the pixels in the condensed */ /* shading data block will always be left to right. The color */ /* sequence remains unchanged. */ Microtek2_Device *md; Microtek2_Info *mi; uint32_t byte; uint32_t cond_length; /* bytes per condensed shading line */ int color, count, lfd_bit; int shad_bplc, shad_pixels; /* bytes per line & color in shading image */ int bit, flag; uint32_t sh_offset, csh_offset; int gray_filter_color = 1; /* which color of the shading is taken for gray*/ md = ms->dev; mi = &md->info[md->scan_source]; DBG(30, "condense_shading: ms=%p, ppl=%d\n", (void *) ms, ms->ppl); if ( md->shading_table_w == NULL ) { DBG(1, "condense shading: no shading table found, skip shading\n"); return SANE_STATUS_GOOD; } get_lut_size( mi, &ms->lut_size, &ms->lut_entry_size ); if ( md->model_flags & MD_PHANTOM336CX_TYPE_SHADING ) { shad_pixels = ms->n_control_bytes * 8; gray_filter_color = 0; /* 336CX reads only one shading in gray mode*/ } else shad_pixels = mi->geo_width; shad_bplc = shad_pixels * ms->lut_entry_size; if ( md_dump >= 3 ) { dump_area2(md->shading_table_w, shad_bplc * 3, "shading_table_w"); if ( md->model_flags & MD_PHANTOM336CX_TYPE_SHADING ) write_shading_pnm(ms); } cond_length = ms->bpl * ms->lut_entry_size; if ( ms->condensed_shading_w ) { free((void*) ms->condensed_shading_w ); ms->condensed_shading_w = NULL; } ms->condensed_shading_w = (uint8_t *)malloc(cond_length); DBG(100, "condense_shading: ms->condensed_shading_w=%p," "malloc'd %d bytes\n", (void *) ms->condensed_shading_w, cond_length); if ( ms->condensed_shading_w == NULL ) { DBG(1, "condense_shading: malloc for white table failed\n"); return SANE_STATUS_NO_MEM; } if ( md->shading_table_d != NULL ) { if ( md_dump >= 3 ) dump_area2(md->shading_table_d, shad_bplc * 3, "shading_table_d"); if ( ms->condensed_shading_d ) { free((void*) ms->condensed_shading_d ); ms->condensed_shading_d = NULL; } ms->condensed_shading_d = (uint8_t *)malloc(cond_length); DBG(100, "condense_shading: ms->condensed_shading_d=%p," " malloc'd %d bytes\n", (void *) ms->condensed_shading_d, cond_length); if ( ms->condensed_shading_d == NULL ) { DBG(1, "condense_shading: malloc for dark table failed\n"); return SANE_STATUS_NO_MEM; } } DBG(128, "controlbit offset=%d\n", md->controlbit_offset); count = 0; for (lfd_bit = 0; ( lfd_bit < mi->geo_width ) && ( count < (int)ms->ppl ); ++lfd_bit) { byte = ( lfd_bit + md->controlbit_offset ) / 8; bit = ( lfd_bit + md->controlbit_offset ) % 8; if ( mi->direction & MI_DATSEQ_RTOL ) flag = ((ms->control_bytes[byte] >> bit) & 0x01); else flag = ((ms->control_bytes[byte] >> (7 - bit)) & 0x01); if ( flag == 1 ) /* flag==1 if byte's bit is set */ { for ( color = 0; color < 3; ++color ) { if ( ( ms->mode == MS_MODE_COLOR ) || ( ( ms->mode == MS_MODE_GRAY ) && ( color == gray_filter_color ) ) || ( ( ms->mode == MS_MODE_LINEARTFAKE ) && ( color == gray_filter_color ) ) ) { sh_offset = color * shad_pixels + lfd_bit; if ( md->model_flags & MD_PHANTOM336CX_TYPE_SHADING ) sh_offset += md->controlbit_offset; if ( ms->mode == MS_MODE_COLOR ) csh_offset = color * ms->ppl + count; else csh_offset = count; if ( csh_offset > cond_length ) { DBG(1, "condense_shading: wrong control bits data, " ); DBG(1, "csh_offset (%d) > cond_length(%d)\n", csh_offset, cond_length ); csh_offset = cond_length; } if ( ms->lut_entry_size == 2 ) { *((uint16_t *)ms->condensed_shading_w + csh_offset) = *((uint16_t *)md->shading_table_w + sh_offset); if ( ms->condensed_shading_d != NULL ) *((uint16_t *)ms->condensed_shading_d + csh_offset) = *((uint16_t *)md->shading_table_d + sh_offset); } else { *((uint8_t *)ms->condensed_shading_w + csh_offset) = *((uint8_t *)md->shading_table_w + sh_offset); if ( ms->condensed_shading_d != NULL ) *((uint8_t *)ms->condensed_shading_d + csh_offset) = *((uint8_t *)md->shading_table_d + sh_offset); } } } ++count; } } if ( md_dump >= 3 ) { dump_area2(ms->condensed_shading_w, cond_length, "condensed_shading_w"); if ( ms->condensed_shading_d != NULL ) dump_area2(ms->condensed_shading_d, cond_length, "condensed_shading_d"); write_cshading_pnm(ms); } return SANE_STATUS_GOOD; } /*---------- read_shading_image() --------------------------------------------*/ static SANE_Status read_shading_image(Microtek2_Scanner *ms) { SANE_Status status; Microtek2_Device *md; Microtek2_Info *mi; uint32_t lines; uint8_t *buf; int max_lines; int lines_to_read; DBG(30, "read_shading_image: ms=%p\n", (void *) ms); md = ms->dev; mi = &md->info[md->scan_source]; if ( ! MI_WHITE_SHADING_ONLY(mi->shtrnsferequ) || ( md->model_flags & MD_PHANTOM_C6 ) ) /* Dark shading correction */ /* ~~~~~~~~~~~~~~~~~~~~~~~ */ { DBG(30, "read_shading_image: reading black data\n"); md->status.ntrack |= MD_NTRACK_ON; md->status.ncalib &= ~MD_NCALIB_ON; md->status.flamp |= MD_FLAMP_ON; if ( md->model_flags & MD_PHANTOM_C6 ) { md->status.stick |= MD_STICK_ON; md->status.reserved17 |= MD_RESERVED17_ON; } get_calib_params(ms); if ( md->model_flags & MD_PHANTOM_C6 ) ms->stay = 1; status = scsi_send_system_status(md, ms->sfd); if ( status != SANE_STATUS_GOOD ) return status; status = scsi_set_window(ms, 1); if ( status != SANE_STATUS_GOOD ) return status; #ifdef TESTBACKEND status = scsi_read_sh_image_info(ms); #else status = scsi_read_image_info(ms); #endif if ( status != SANE_STATUS_GOOD ) return status; status = scsi_wait_for_image(ms); if ( status != SANE_STATUS_GOOD ) return status; status = scsi_read_system_status(md, ms->sfd); if ( status != SANE_STATUS_GOOD ) return status; md->status.flamp &= ~MD_FLAMP_ON; status = scsi_send_system_status(md, ms->sfd); if ( status != SANE_STATUS_GOOD ) return status; ms->shading_image = malloc(ms->bpl * ms->src_remaining_lines); DBG(100, "read shading image: ms->shading_image=%p," " malloc'd %d bytes\n", (void *) ms->shading_image, ms->bpl * ms->src_remaining_lines); if ( ms->shading_image == NULL ) { DBG(1, "read_shading_image: malloc for buffer failed\n"); return SANE_STATUS_NO_MEM; } buf = ms->shading_image; #ifdef TESTBACKEND max_lines = 5000000 / ms->bpl; #else max_lines = sanei_scsi_max_request_size / ms->bpl; #endif if ( max_lines == 0 ) { DBG(1, "read_shading_image: buffer too small\n"); return SANE_STATUS_IO_ERROR; } lines = ms->src_remaining_lines; while ( ms->src_remaining_lines > 0 ) { lines_to_read = MIN(max_lines, ms->src_remaining_lines); ms->src_buffer_size = lines_to_read * ms->bpl; ms->transfer_length = ms->src_buffer_size; #ifdef TESTBACKEND status = scsi_read_sh_d_image(ms, buf); #else status = scsi_read_image(ms, buf, md->shading_depth>8 ? 2 : 1); #endif if ( status != SANE_STATUS_GOOD ) { DBG(1, "read_shading_image: read image failed: '%s'\n", sane_strstatus(status)); return status; } ms->src_remaining_lines -= lines_to_read; buf += ms->src_buffer_size; } status = prepare_shading_data(ms, lines, &md->shading_table_d); if ( status != SANE_STATUS_GOOD ) return status; /* send shading data to the device */ /* Some models use "read_control bit", and the shading must be */ /* applied by the backend later */ if ( ! (md->model_flags & MD_READ_CONTROL_BIT) ) { status = shading_function(ms, md->shading_table_d); if ( status != SANE_STATUS_GOOD ) return status; ms->word = ms->lut_entry_size == 2 ? 1 : 0; ms->current_color = MS_COLOR_ALL; status = scsi_send_shading(ms, md->shading_table_d, 3 * ms->lut_entry_size * mi->geo_width / mi->calib_divisor, 1); if ( status != SANE_STATUS_GOOD ) return status; } DBG(100, "free memory for ms->shading_image at %p\n", (void *) ms->shading_image); free((void *) ms->shading_image); ms->shading_image = NULL; } /* white shading correction */ /* ~~~~~~~~~~~~~~~~~~~~~~~~ */ DBG(30, "read_shading_image: reading white data\n"); /* According to the doc NCalib must be set for white shading data */ /* if we have a black and a white shading correction and must be */ /* cleared if we have only a white shading collection */ if ( ! MI_WHITE_SHADING_ONLY(mi->shtrnsferequ) || ( md->model_flags & MD_PHANTOM_C6 ) ) md->status.ncalib |= MD_NCALIB_ON; else md->status.ncalib &= ~MD_NCALIB_ON; md->status.flamp |= MD_FLAMP_ON; /* md->status.tlamp &= ~MD_TLAMP_ON; */ md->status.ntrack |= MD_NTRACK_ON; if ( md->model_flags & MD_PHANTOM_C6 ) { md->status.stick &= ~MD_STICK_ON; md->status.reserved17 |= MD_RESERVED17_ON; } get_calib_params(ms); #ifdef NO_PHANTOMTYPE_SHADING /* md->status.stick &= ~MD_STICK_ON; */ /* md->status.ncalib &= ~MD_NCALIB_ON; */ /* md->status.reserved17 &= ~MD_RESERVED17_ON; */ ms->rawdat = 0; #endif status = scsi_send_system_status(md, ms->sfd); if ( status != SANE_STATUS_GOOD ) return status; status = scsi_set_window(ms, 1); if ( status != SANE_STATUS_GOOD ) return status; #ifdef TESTBACKEND status = scsi_read_sh_image_info(ms); #else status = scsi_read_image_info(ms); #endif if ( status != SANE_STATUS_GOOD ) return status; status = scsi_wait_for_image(ms); if ( status != SANE_STATUS_GOOD ) return status; #ifdef NO_PHANTOMTYPE_SHADING if ( !( md->model_flags & MD_READ_CONTROL_BIT ) ) { #endif status = scsi_read_system_status(md, ms->sfd); if ( status != SANE_STATUS_GOOD ) return status; #ifdef NO_PHANTOMTYPE_SHADING } #endif #ifdef NO_PHANTOMTYPE_SHADING if ( mi->model_code == 0x94 ) status = scsi_read_control_bits(ms); #endif ms->shading_image = malloc(ms->bpl * ms->src_remaining_lines); DBG(100, "read shading image: ms->shading_image=%p, malloc'd %d bytes\n", (void *) ms->shading_image, ms->bpl * ms->src_remaining_lines); if ( ms->shading_image == NULL ) { DBG(1, "read_shading_image: malloc for buffer failed\n"); return SANE_STATUS_NO_MEM; } buf = ms->shading_image; #ifdef TESTBACKEND max_lines = 5000000 / ms->bpl; #else max_lines = sanei_scsi_max_request_size / ms->bpl; #endif if ( max_lines == 0 ) { DBG(1, "read_shading_image: buffer too small\n"); return SANE_STATUS_IO_ERROR; } lines = ms->src_remaining_lines; while ( ms->src_remaining_lines > 0 ) { lines_to_read = MIN(max_lines, ms->src_remaining_lines); ms->src_buffer_size = lines_to_read * ms->bpl; ms->transfer_length = ms->src_buffer_size; #ifdef TESTBACKEND status = scsi_read_sh_w_image(ms, buf); #else status = scsi_read_image(ms, buf, md->shading_depth>8 ? 2 : 1); #endif if ( status != SANE_STATUS_GOOD ) return status; ms->src_remaining_lines -= lines_to_read; buf += ms->src_buffer_size; } status = prepare_shading_data(ms, lines, &md->shading_table_w); if ( status != SANE_STATUS_GOOD ) return status; if ( md_dump >= 3 ) { write_shading_buf_pnm(ms, lines); write_shading_pnm(ms); } /* send shading data to the device */ /* Some models use "read_control bit", and the shading must be */ /* applied by the backend later */ if ( ! (md->model_flags & MD_READ_CONTROL_BIT) ) { status = shading_function(ms, md->shading_table_w); if ( status != SANE_STATUS_GOOD ) return status; ms->word = ms->lut_entry_size == 2 ? 1 : 0; ms->current_color = MS_COLOR_ALL; status = scsi_send_shading(ms, md->shading_table_w, 3 * ms->lut_entry_size * mi->geo_width / mi->calib_divisor, 0); if ( status != SANE_STATUS_GOOD ) return status; } ms->rawdat = 0; ms->stay = 0; md->status.ncalib |= MD_NCALIB_ON; if ( md->model_flags & MD_PHANTOM_C6 ) { md->status.stick &= ~MD_STICK_ON; md->status.reserved17 &= ~MD_RESERVED17_ON; } #ifdef NO_PHANTOMTYPE_SHADING if (mi->model_code == 0x94) md->status.ncalib &= ~MD_NCALIB_ON; #endif status = scsi_send_system_status(md, ms->sfd); if ( status != SANE_STATUS_GOOD ) return status; DBG(100, "free memory for ms->shading_image at %p\n", (void *) ms->shading_image); free((void *) ms->shading_image); ms->shading_image = NULL; return SANE_STATUS_GOOD; } /*---------- prepare_shading_data() ------------------------------------------*/ static SANE_Status prepare_shading_data(Microtek2_Scanner *ms, uint32_t lines, uint8_t **data) { /* This function calculates one line of black or white shading data */ /* from the shading image. At the end we have one line. The */ /* color sequence is unchanged. */ #define MICROTEK2_CALIB_USE_MEDIAN Microtek2_Device *md; Microtek2_Info *mi; uint32_t length,line; int color, i; SANE_Status status; #ifdef MICROTEK2_CALIB_USE_MEDIAN uint16_t *sortbuf, value; #else uint32_t value; #endif DBG(30, "prepare_shading_data: ms=%p, lines=%d, *data=%p\n", (void *) ms, lines, (void *) *data); md = ms->dev; mi = &md->info[md->scan_source]; status = SANE_STATUS_GOOD; get_lut_size(mi, &ms->lut_size, &ms->lut_entry_size); length = 3 * ms->lut_entry_size * mi->geo_width / mi->calib_divisor; if ( *data == NULL ) { *data = (uint8_t *) malloc(length); DBG(100, "prepare_shading_data: malloc'd %d bytes at %p\n", length, (void *) *data); if ( *data == NULL ) { DBG(1, "prepare_shading_data: malloc for shading table failed\n"); return SANE_STATUS_NO_MEM; } } #ifdef MICROTEK2_CALIB_USE_MEDIAN sortbuf = malloc( lines * ms->lut_entry_size ); DBG(100, "prepare_shading_data: sortbuf= %p, malloc'd %d Bytes\n", (void *) sortbuf, lines * ms->lut_entry_size); if ( sortbuf == NULL ) { DBG(1, "prepare_shading_data: malloc for sort buffer failed\n"); return SANE_STATUS_NO_MEM; } #endif switch( mi->data_format ) { case MI_DATAFMT_LPLCONCAT: if ( ms->lut_entry_size == 1 ) { DBG(1, "prepare_shading_data: wordsize == 1 unsupported\n"); return SANE_STATUS_UNSUPPORTED; } for ( color = 0; color < 3; color++ ) { for ( i = 0; i < ( mi->geo_width / mi->calib_divisor ); i++ ) { value = 0; for ( line = 0; line < lines; line++ ) #ifndef MICROTEK2_CALIB_USE_MEDIAN /* average the shading lines to get the shading data */ value += *((uint16_t *) ms->shading_image + line * ( ms->bpl / ms->lut_entry_size ) + color * ( ms->bpl / ms->lut_entry_size / 3 ) + i); value /= lines; *((uint16_t *) *data + color * ( mi->geo_width / mi->calib_divisor ) + i) = (uint16_t) MIN(0xffff, value); #else /* use a median filter to get the shading data -- should be better */ *(sortbuf + line ) = *((uint16_t *) ms->shading_image + line * ( ms->bpl / ms->lut_entry_size ) + color * ( ms->bpl / ms->lut_entry_size / 3 ) + i); qsort(sortbuf, lines, sizeof(uint16_t), (qsortfunc)compare_func_16); value = *(sortbuf + ( lines - 1 ) / 2 ); *((uint16_t *) *data + color * ( mi->geo_width / mi->calib_divisor ) + i) = value; #endif } } break; case MI_DATAFMT_CHUNKY: case MI_DATAFMT_9800: if ( ms->lut_entry_size == 1 ) { DBG(1, "prepare_shading_data: wordsize == 1 unsupported\n"); return SANE_STATUS_UNSUPPORTED; } for ( color = 0; color < 3; color++ ) { for ( i = 0; i < ( mi->geo_width / mi->calib_divisor ); i++ ) { value = 0; for ( line = 0; line < lines; line++ ) #ifndef MICROTEK2_CALIB_USE_MEDIAN /* average the shading lines to get the shading data */ value += *((uint16_t *) ms->shading_image + line * 3 * mi->geo_width / mi->calib_divisor + 3 * i + color); value /= lines; *((uint16_t *) *data + color * ( mi->geo_width / mi->calib_divisor ) + i) = (uint16_t) MIN(0xffff, value); #else /* use a median filter to get the shading data -- should be better */ *(sortbuf + line ) = *((uint16_t *) ms->shading_image + line * 3 * mi->geo_width / mi->calib_divisor + 3 * i + color); qsort(sortbuf, lines, sizeof(uint16_t), (qsortfunc)compare_func_16); value = *(sortbuf + ( lines - 1 ) / 2 ); *((uint16_t *) *data + color * ( mi->geo_width / mi->calib_divisor ) + i) = value; #endif } } break; case MI_DATAFMT_LPLSEGREG: for ( color = 0; color < 3; color++ ) { for ( i = 0; i < ( mi->geo_width / mi->calib_divisor ); i++ ) { value = 0; if ( ms->lut_entry_size == 1 ) { for ( line = 0; line < lines; line++ ) value += *((uint8_t *) ms->shading_image + line * 3 * mi->geo_width / mi->calib_divisor + 3 * i + color); value /= lines; *((uint8_t *) *data + color * ( mi->geo_width / mi->calib_divisor ) + i) = (uint8_t) MIN(0xff, value); } else { for ( line = 0; line < lines; line++ ) value += *((uint16_t *) ms->shading_image + line * 3 * mi->geo_width / mi->calib_divisor + 3 * i + color); value /= lines; #ifndef MICROTEK2_CALIB_USE_MEDIAN *((uint16_t *) *data + color * ( mi->geo_width / mi->calib_divisor ) + i) = (uint16_t) MIN(0xffff, value); #else *((uint16_t *) *data + color * ( mi->geo_width / mi->calib_divisor ) + i) = value; #endif } } } break; default: DBG(1, "prepare_shading_data: Unsupported data format 0x%02x\n", mi->data_format); status = SANE_STATUS_UNSUPPORTED; } #ifdef MICROTEK2_CALIB_USE_MEDIAN DBG(100, "prepare_shading_data: free sortbuf at %p\n", (void *) sortbuf); free(sortbuf); sortbuf = NULL; #endif return status; } /*---------- read_cx_shading() -----------------------------------------------*/ static SANE_Status read_cx_shading(Microtek2_Scanner *ms) { SANE_Status status; Microtek2_Device *md; md = ms->dev; DBG(30, "read_cx_shading: ms=%p\n",(void *) ms); md->shading_table_contents = ms->mode; if ( ms->mode == MS_MODE_COLOR ) ms->current_color = MS_COLOR_ALL; else ms->current_color = MS_COLOR_GREEN; /* for grayscale */ ms->word = 1; ms->dark = 0; status = read_cx_shading_image(ms); if ( status != SANE_STATUS_GOOD ) goto cleanup; ms->word = 0; /* the Windows driver reads dark shading with word=0 */ ms->dark = 1; status = read_cx_shading_image(ms); if ( status != SANE_STATUS_GOOD ) goto cleanup; return SANE_STATUS_GOOD; cleanup: cleanup_scanner(ms); return status; } /*---------- read_cx_shading_image() -----------------------------------------*/ static SANE_Status read_cx_shading_image(Microtek2_Scanner *ms) { SANE_Status status; Microtek2_Device *md; uint32_t shading_bytes, linesize, buffer_size; uint8_t *buf; int max_lines, lines_to_read, remaining_lines; md = ms->dev; shading_bytes = ms->n_control_bytes * 8 * md->shading_length; if ( ms->current_color == MS_COLOR_ALL ) shading_bytes *= 3; if ( ms->word == 1 ) shading_bytes *= 2; if ( ms->shading_image ) { free((void *) ms->shading_image); ms->shading_image = NULL; } ms->shading_image = malloc(shading_bytes); DBG(100, "read_cx_shading: ms->shading_image=%p, malloc'd %d bytes\n", (void *) ms->shading_image, shading_bytes); if ( ms->shading_image == NULL ) { DBG(1, "read_cx_shading: malloc for cx_shading buffer failed\n"); return SANE_STATUS_NO_MEM; } buf = ms->shading_image; DBG(30, "read_cx_shading_image: ms=%p, shading_bytes=%d\n", (void *) ms, shading_bytes); linesize = shading_bytes / md->shading_length; #ifdef TESTBACKEND max_lines = 5000000 / linesize; #else max_lines = sanei_scsi_max_request_size / linesize; #endif /* the following part is like in "read_shading_image" */ remaining_lines = md->shading_length; while ( remaining_lines > 0 ) { lines_to_read = MIN(max_lines, remaining_lines); buffer_size = lines_to_read * linesize; status = scsi_read_shading(ms, buf, buffer_size); if ( status != SANE_STATUS_GOOD ) { DBG(1, "read_cx_shading: '%s'\n", sane_strstatus(status)); return status; } remaining_lines -= lines_to_read; buf += buffer_size; } status = calc_cx_shading_line(ms); if ( status != SANE_STATUS_GOOD ) { DBG(1, "read_cx_shading: '%s'\n", sane_strstatus(status)); return status; } if ( ms->shading_image ) { DBG(100, "free memory for ms->shading_image at %p\n", (void *) ms->shading_image); free((void *) ms->shading_image); ms->shading_image = NULL; } return status; } /*---------- calc_cx_shading_line() ------------------------------------------*/ /* calculates the mean value of the shading lines and stores one line of */ /* 8-bit shading data. Scanning direction + color sequence remain as they are */ /* ToDo: more than 8-bit data */ static SANE_Status calc_cx_shading_line(Microtek2_Scanner *ms) { Microtek2_Device *md; SANE_Status status; uint8_t *current_byte, *buf, *shading_table_pointer; uint8_t color, factor; uint32_t shading_line_pixels, shading_line_bytes, shading_data_bytes, line, i, accu, color_offset; uint16_t *sortbuf, value; md = ms->dev; status = SANE_STATUS_GOOD; sortbuf = malloc( md->shading_length * sizeof(float) ); DBG(100, "calc_cx_shading: sortbuf= %p, malloc'd %lu Bytes\n", (void *) sortbuf, (u_long) (md->shading_length * sizeof(float))); if ( sortbuf == NULL ) { DBG(1, "calc_cx_shading: malloc for sort buffer failed\n"); return SANE_STATUS_NO_MEM; } buf = ms->shading_image; shading_line_pixels = ms->n_control_bytes * 8; /* = 2560 for 330CX */ shading_line_bytes = shading_line_pixels; /* grayscale */ if ( ms->mode == MS_MODE_COLOR ) /* color */ shading_line_bytes *= 3; shading_data_bytes = shading_line_bytes; /* 8-bit color depth */ if (ms->word == 1) /* > 8-bit color depth */ shading_data_bytes *= 2; factor = 4; /* shading bit depth = 10bit; shading line bit depth = 8bit */ if (ms->dark == 0) /* white shading data */ { if ( md->shading_table_w ) free( (void *)md->shading_table_w ); md->shading_table_w = (uint8_t *) malloc(shading_line_bytes); DBG(100, "calc_cx_shading: md->shading_table_w=%p, malloc'd %d bytes\n", (void *) md->shading_table_w, shading_line_bytes); if ( md->shading_table_w == NULL ) { DBG(100, "calc_cx_shading: malloc for white shadingtable failed\n"); status = SANE_STATUS_NO_MEM; cleanup_scanner(ms); } shading_table_pointer = md->shading_table_w; } else /* dark shading data */ { if ( md->shading_table_d ) free( (void *)md->shading_table_d); md->shading_table_d = (uint8_t *) malloc(shading_line_bytes); DBG(100, "calc_cx_shading: md->shading_table_d=%p, malloc'd %d bytes\n", (void *) md->shading_table_d, shading_line_bytes); if ( md->shading_table_d == NULL ) { DBG(1, "calc_cx_shading: malloc for dark shading table failed\n"); status = SANE_STATUS_NO_MEM; cleanup_scanner(ms); } shading_table_pointer = md->shading_table_d; } DBG(30, "calc_cx_shading_line: ms=%p\n" "md->shading_table_w=%p\n" "md->shading_table_d=%p\n" "shading_line_bytes=%d\n" "shading_line_pixels=%d\n" "shading_table_pointer=%p\n", (void *) ms, (void *) md->shading_table_w, (void *) md->shading_table_d, shading_line_bytes, shading_line_pixels, (void *) shading_table_pointer); /* calculating the median pixel values over the shading lines */ /* and write them to the shading table */ for (color = 0; color < 3; color++) { color_offset = color * shading_line_pixels; if ( ms->word == 1 ) color_offset *=2; for (i = 0; i < shading_line_pixels; i++) { value = 0; for (line = 0; line < md->shading_length; line++) { current_byte = buf + ( line * shading_data_bytes ) + color_offset + i; accu = *current_byte; /* word shading data: the lower bytes per line and color are */ /* transferred first in one block and then the high bytes */ /* in one block */ /* the dark shading data is also 10 bit, but only the */ /* low byte is transferred (ms->word = 0) */ if ( ms->word == 1 ) { current_byte = buf + ( line * shading_data_bytes ) + color_offset + shading_line_pixels + i; accu += ( *current_byte * 256 ); } *( sortbuf + line ) = accu; } /* this is the Median filter: sort the values ascending and take the middlest */ qsort(sortbuf, md->shading_length, sizeof(float), (qsortfunc)compare_func_16); value = *( sortbuf + ( md->shading_length - 1 ) / 2 ); *shading_table_pointer = (uint8_t) (value / factor); shading_table_pointer++; } if ( ms->mode != MS_MODE_COLOR ) break; } return status; } /*---------- get_lut_size() --------------------------------------------------*/ static SANE_Status get_lut_size(Microtek2_Info *mi, int *max_lut_size, int *lut_entry_size) { /* returns the maximum lookup table size. A device might indicate */ /* several lookup table sizes. */ DBG(30, "get_lut_size: mi=%p\n", (void *) mi); *max_lut_size = 0; *lut_entry_size = 0; /* Normally this function is used for both gamma and shading tables */ /* If, however, the device indicates, that it does not support */ /* lookup tables, we set these values as if the device has a maximum */ /* bitdepth of 12, and these values are only used to determine the */ /* size of the shading table */ if ( MI_LUTCAP_NONE(mi->lut_cap) ) { *max_lut_size = 4096; *lut_entry_size = 2; } if ( mi->lut_cap & MI_LUTCAP_256B ) { *max_lut_size = 256; *lut_entry_size = 1; } if ( mi->lut_cap & MI_LUTCAP_1024B ) { *max_lut_size = 1024; *lut_entry_size = 1; } if ( mi->lut_cap & MI_LUTCAP_1024W ) { *max_lut_size = 1024; *lut_entry_size = 2; } if ( mi->lut_cap & MI_LUTCAP_4096B ) { *max_lut_size = 4096; *lut_entry_size = 1; } if ( mi->lut_cap & MI_LUTCAP_4096W ) { *max_lut_size = 4096; *lut_entry_size = 2; } if ( mi->lut_cap & MI_LUTCAP_64k_W ) { *max_lut_size = 65536; *lut_entry_size = 2; } if ( mi->lut_cap & MI_LUTCAP_16k_W ) { *max_lut_size = 16384; *lut_entry_size = 2; } DBG(30, "get_lut_size: mi=%p, lut_size=%d, lut_entry_size=%d\n", (void *) mi, *max_lut_size, *lut_entry_size); return SANE_STATUS_GOOD; } /*---------- calculate_gamma() -----------------------------------------------*/ static SANE_Status calculate_gamma(Microtek2_Scanner *ms, uint8_t *pos, int color, char *mode) { Microtek2_Device *md; Microtek2_Info *mi; double exp; double mult; double steps; unsigned int val; int i; int factor; /* take into account the differences between the */ /* possible values for the color and the number */ /* of bits the scanner works with internally. */ /* If depth == 1 handle this as if the maximum */ /* depth was chosen */ DBG(30, "calculate_gamma: ms=%p, pos=%p, color=%d, mode=%s\n", (void *) ms, (void *) pos, color, mode); md = ms->dev; mi = &md->info[md->scan_source]; /* does this work everywhere ? */ if ( md->model_flags & MD_NO_GAMMA ) { factor = 1; mult = (double) (ms->lut_size - 1); } else { if ( mi->depth & MI_HASDEPTH_16 ) { factor = ms->lut_size / 65536; mult = 65535.0; } else if ( mi->depth & MI_HASDEPTH_14 ) { factor = ms->lut_size / 16384; mult = 16383.0; } else if ( mi->depth & MI_HASDEPTH_12 ) { factor = ms->lut_size / 4096; mult = 4095.0; } else if ( mi->depth & MI_HASDEPTH_10 ) { factor = ms->lut_size / 1024; mult = 1023.0; } else { factor = ms->lut_size / 256; mult = 255.0; } } #if 0 factor = ms->lut_size / (int) pow(2.0, (double) ms->depth); mult = pow(2.0, (double) ms->depth) - 1.0; /* depending on output size */ #endif steps = (double) (ms->lut_size - 1); /* depending on input size */ DBG(30, "calculate_gamma: factor=%d, mult =%f, steps=%f, mode=%s\n", factor, mult, steps, ms->val[OPT_GAMMA_MODE].s); if ( strcmp(mode, MD_GAMMAMODE_SCALAR) == 0 ) { int option; option = OPT_GAMMA_SCALAR; /* OPT_GAMMA_SCALAR_R follows OPT_GAMMA_SCALAR directly */ if ( ms->val[OPT_GAMMA_BIND].w == SANE_TRUE ) exp = 1.0 / SANE_UNFIX(ms->val[option].w); else exp = 1.0 / SANE_UNFIX(ms->val[option + color + 1].w); for ( i = 0; i < ms->lut_size; i++ ) { val = (unsigned int) (mult * pow((double) i / steps, exp) + .5); if ( ms->lut_entry_size == 2 ) *((uint16_t *) pos + i) = (uint16_t) val; else *((uint8_t *) pos + i) = (uint8_t) val; } } else if ( strcmp(mode, MD_GAMMAMODE_CUSTOM) == 0 ) { int option; SANE_Int *src; option = OPT_GAMMA_CUSTOM; if ( ms->val[OPT_GAMMA_BIND].w == SANE_TRUE ) src = ms->val[option].wa; else src = ms->val[option + color + 1].wa; for ( i = 0; i < ms->lut_size; i++ ) { if ( ms->lut_entry_size == 2 ) *((uint16_t *) pos + i) = (uint16_t) (src[i] / factor); else *((uint8_t *) pos + i) = (uint8_t) (src[i] / factor); } } else if ( strcmp(mode, MD_GAMMAMODE_LINEAR) == 0 ) { for ( i = 0; i < ms->lut_size; i++ ) { if ( ms->lut_entry_size == 2 ) *((uint16_t *) pos + i) = (uint16_t) (i / factor); else *((uint8_t *) pos + i) = (uint8_t) (i / factor); } } return SANE_STATUS_GOOD; } /*---------- shading_function() ----------------------------------------------*/ static SANE_Status shading_function(Microtek2_Scanner *ms, uint8_t *data) { Microtek2_Device *md; Microtek2_Info *mi; uint32_t value; int color; int i; DBG(40, "shading_function: ms=%p, data=%p\n", (void *) ms, (void *) data); md = ms->dev; mi = &md->info[md->scan_source]; /* mi = &md->info[MD_SOURCE_FLATBED]; */ if ( ms->lut_entry_size == 1 ) { DBG(1, "shading_function: wordsize = 1 unsupported\n"); return SANE_STATUS_IO_ERROR; } for ( color = 0; color < 3; color++ ) { for ( i = 0; i < ( mi->geo_width / mi->calib_divisor ); i++) { value = *((uint16_t *) data + color * ( mi->geo_width / mi->calib_divisor ) + i); switch ( mi->shtrnsferequ ) { case 0x00: /* output == input */ break; case 0x01: value = (ms->lut_size * ms->lut_size) / value; *((uint16_t *) data + color * ( mi->geo_width / mi->calib_divisor ) + i) = (uint16_t) MIN(0xffff, value); break; case 0x11: value = (ms->lut_size * ms->lut_size) / (uint32_t) ( (double) value * ((double) mi->balance[color] / 255.0)); *((uint16_t *) data + color * ( mi->geo_width / mi->calib_divisor ) + i) = (uint16_t) MIN(0xffff, value); break; case 0x15: value = (uint32_t) ( ( 1073741824 / (double) value ) * ( (double) mi->balance[color] / 256.0) ); value = MIN(value, (uint32_t)65535); *((uint16_t *) data + color * ( mi->geo_width / mi->calib_divisor ) + i) = (uint16_t) MIN(0xffff, value); break; default: DBG(1, "Unsupported shading transfer function 0x%02x\n", mi->shtrnsferequ ); break; } } } return SANE_STATUS_GOOD; } /*---------- set_exposure() --------------------------------------------------*/ static void set_exposure(Microtek2_Scanner *ms) { /* This function manipulates the colors according to the exposure time */ /* settings on models where they are ignored. Currently this seems to */ /* be the case for all models with the data format chunky data. They */ /* all have tables with two byte gamma output, so for now we ignore */ /* gamma tables with one byte output */ Microtek2_Device *md; Microtek2_Info *mi; int color; int size; int depth; int maxval; int byte; uint32_t val32; uint8_t *from; uint8_t exposure; uint8_t exposure_rgb[3]; DBG(30, "set_exposure: ms=%p\n", (void *) ms); md = ms->dev; mi = &md->info[md->scan_source]; if ( ms->lut_entry_size == 1 ) { DBG(1, "set_exposure: 1 byte gamma output tables currently ignored\n"); return; } if ( mi->depth & MI_HASDEPTH_16 ) depth = 16; else if ( mi->depth & MI_HASDEPTH_14 ) depth = 14; else if ( mi->depth & MI_HASDEPTH_12 ) depth = 12; else if ( mi->depth & MI_HASDEPTH_10 ) depth = 10; else depth = 8; maxval = ( 1 << depth ) - 1; from = ms->gamma_table; size = ms->lut_size; /* first master channel, apply transformation to all colors */ exposure = ms->exposure_m; for ( byte = 0; byte < ms->lut_size; byte++ ) { for ( color = 0; color < 3; color++) { val32 = (uint32_t) *((uint16_t *) from + color * size + byte); val32 = MIN(val32 + val32 * (2 * (uint32_t) exposure / 100), (uint32_t) maxval); *((uint16_t *) from + color * size + byte) = (uint16_t) val32; } } /* and now apply transformation to each channel */ exposure_rgb[0] = ms->exposure_r; exposure_rgb[1] = ms->exposure_g; exposure_rgb[2] = ms->exposure_b; for ( color = 0; color < 3; color++ ) { for ( byte = 0; byte < size; byte++ ) { val32 = (uint32_t) *((uint16_t *) from + color * size + byte); val32 = MIN(val32 + val32 * (2 * (uint32_t) exposure_rgb[color] / 100), (uint32_t) maxval); *((uint16_t *) from + color * size + byte) = (uint16_t) val32; } } return; } /*---------- reader_process() ------------------------------------------------*/ static int reader_process(void *data) { Microtek2_Scanner *ms = (Microtek2_Scanner *) data; SANE_Status status; Microtek2_Info *mi; Microtek2_Device *md; struct SIGACTION act; sigset_t sigterm_set; static uint8_t *temp_current = NULL; DBG(30, "reader_process: ms=%p\n", (void *) ms); md = ms->dev; mi = &md->info[md->scan_source]; if (sanei_thread_is_forked()) close(ms->fd[0]); sigemptyset (&sigterm_set); sigaddset (&sigterm_set, SIGTERM); memset (&act, 0, sizeof (act)); act.sa_handler = signal_handler; sigaction (SIGTERM, &act, 0); ms->fp = fdopen(ms->fd[1], "w"); if ( ms->fp == NULL ) { DBG(1, "reader_process: fdopen() failed, errno=%d\n", errno); return SANE_STATUS_IO_ERROR; } if ( ms->auto_adjust == 1 ) { if ( temp_current == NULL ) temp_current = ms->temporary_buffer; } while ( ms->src_remaining_lines > 0 ) { ms->src_lines_to_read = MIN(ms->src_remaining_lines, ms->src_max_lines); ms->transfer_length = ms->src_lines_to_read * ms->bpl; DBG(30, "reader_process: transferlength=%d, lines=%d, linelength=%d, " "real_bpl=%d, srcbuf=%p\n", ms->transfer_length, ms->src_lines_to_read, ms->bpl, ms->real_bpl, (void *) ms->buf.src_buf); sigprocmask (SIG_BLOCK, &sigterm_set, 0); status = scsi_read_image(ms, ms->buf.src_buf, (ms->depth > 8) ? 2 : 1); sigprocmask (SIG_UNBLOCK, &sigterm_set, 0); if ( status != SANE_STATUS_GOOD ) return SANE_STATUS_IO_ERROR; ms->src_remaining_lines -= ms->src_lines_to_read; /* prepare data for frontend */ switch (ms->mode) { case MS_MODE_COLOR: if ( ! mi->onepass ) /* TODO */ { DBG(1, "reader_process: 3 pass not yet supported\n"); return SANE_STATUS_IO_ERROR; } else { switch ( mi->data_format ) { case MI_DATAFMT_CHUNKY: case MI_DATAFMT_9800: status = chunky_proc_data(ms); if ( status != SANE_STATUS_GOOD ) return status; break; case MI_DATAFMT_LPLCONCAT: status = lplconcat_proc_data(ms); if ( status != SANE_STATUS_GOOD ) return status; break; case MI_DATAFMT_LPLSEGREG: status = segreg_proc_data(ms); if ( status != SANE_STATUS_GOOD ) return status; break; case MI_DATAFMT_WORDCHUNKY: status = wordchunky_proc_data(ms); if ( status != SANE_STATUS_GOOD ) return status; break; default: DBG(1, "reader_process: format %d\n", mi->data_format); return SANE_STATUS_IO_ERROR; } } break; case MS_MODE_GRAY: status = gray_proc_data(ms); if ( status != SANE_STATUS_GOOD ) return status; break; case MS_MODE_HALFTONE: case MS_MODE_LINEART: status = proc_onebit_data(ms); if ( status != SANE_STATUS_GOOD ) return status; break; case MS_MODE_LINEARTFAKE: if ( ms->auto_adjust == 1 ) status = auto_adjust_proc_data(ms, &temp_current); else status = lineartfake_proc_data(ms); if ( status != SANE_STATUS_GOOD ) return status; break; default: DBG(1, "reader_process: Unknown scan mode %d\n", ms->mode); return SANE_STATUS_IO_ERROR; } } fclose(ms->fp); return SANE_STATUS_GOOD; } /*---------- chunky_proc_data() ----------------------------------------------*/ static SANE_Status chunky_proc_data(Microtek2_Scanner *ms) { SANE_Status status; Microtek2_Device *md; uint32_t line; uint8_t *from; int pad; int bpp; /* bytes per pixel */ int bits_pp_in; /* bits per pixel input */ int bits_pp_out; /* bits per pixel output */ int bpl_ppl_diff; DBG(30, "chunky_proc_data: ms=%p\n", (void *) ms); md = ms->dev; bits_pp_in = ms->bits_per_pixel_in; bits_pp_out = ms->bits_per_pixel_out; pad = (int) ceil( (double) (ms->ppl * bits_pp_in) / 8.0 ) % 2; bpp = bits_pp_out / 8; /* Some models have 3 * ppl + 6 bytes per line if the number of pixels */ /* per line is even and 3 * ppl + 3 bytes per line if the number of */ /* pixels per line is odd. According to the documentation it should be */ /* bpl = 3*ppl (even number of pixels) or bpl=3*ppl+1 (odd number of */ /* pixels. Even worse: On different models it is different at which */ /* position in a scanline the image data starts. bpl_ppl_diff tries */ /* to fix this. */ if ( (md->model_flags & MD_OFFSET_2) && pad == 1 ) bpl_ppl_diff = 2; else bpl_ppl_diff = 0; #if 0 if ( md->revision == 1.00 && mi->model_code != 0x81 ) bpl_ppl_diff = ms->bpl - ( 3 * ms->ppl * bpp ) - pad; else bpl_ppl_diff = ms->bpl - ( 3 * ms->ppl * bpp ); if ( md->revision > 1.00 ) bpl_ppl_diff = ms->bpl - ( 3 * ms->ppl * bpp ); else bpl_ppl_diff = ms->bpl - ( 3 * ms->ppl * bpp ) - pad; #endif from = ms->buf.src_buf; from += bpl_ppl_diff; DBG(30, "chunky_proc_data: lines=%d, bpl=%d, ppl=%d, bpp=%d, depth=%d" " junk=%d\n", ms->src_lines_to_read, ms->bpl, ms->ppl, bpp, ms->depth, bpl_ppl_diff); for ( line = 0; line < (uint32_t) ms->src_lines_to_read; line++ ) { status = chunky_copy_pixels(ms, from); if ( status != SANE_STATUS_GOOD ) return status; from += ms->bpl; } return SANE_STATUS_GOOD; } /*---------- chunky_copy_pixels() --------------------------------------------*/ static SANE_Status chunky_copy_pixels(Microtek2_Scanner *ms, uint8_t *from) { Microtek2_Device *md; uint32_t pixel; int color; DBG(30, "chunky_copy_pixels: from=%p, pixels=%d, fp=%p, depth=%d\n", (void *) from, ms->ppl, (void *) ms->fp, ms->depth); md = ms->dev; if ( ms->depth > 8 ) { if ( !( md->model_flags & MD_16BIT_TRANSFER ) ) { int scale1; int scale2; uint16_t val16; scale1 = 16 - ms->depth; scale2 = 2 * ms->depth - 16; for ( pixel = 0; pixel < ms->ppl; pixel++ ) { for ( color = 0; color < 3; color++ ) { val16 = *( (uint16_t *) from + 3 * pixel + color ); val16 = ( val16 << scale1 ) | ( val16 >> scale2 ); fwrite((void *) &val16, 2, 1, ms->fp); } } } else { fwrite((void *) from, 2, 3 * ms->ppl, ms->fp); } } else if ( ms->depth == 8 ) { fwrite((void *) from, 1, 3 * ms->ppl, ms->fp); } else { DBG(1, "chunky_copy_pixels: Unknown depth %d\n", ms->depth); return SANE_STATUS_IO_ERROR; } return SANE_STATUS_GOOD; } /*---------- segreg_proc_data() ----------------------------------------------*/ static SANE_Status segreg_proc_data(Microtek2_Scanner *ms) { SANE_Status status; Microtek2_Device *md; Microtek2_Info *mi; char colormap[] = "RGB"; uint8_t *from; uint32_t lines_to_deliver; int bpp; /* bytes per pixel */ int bpf; /* bytes per frame including color indicator */ int pad; int colseq2; int color; int save_current_src; int frame; DBG(30, "segreg_proc_data: ms=%p\n", (void *) ms); md = ms->dev; mi = &md->info[md->scan_source]; /* take a trailing junk byte into account */ pad = (int) ceil( (double) (ms->ppl * ms->bits_per_pixel_in) / 8.0 ) % 2; bpp = ms->bits_per_pixel_out / 8; /* bits_per_pixel_out is either 8 or 16 */ bpf = ms->bpl / 3; DBG(30, "segreg_proc_data: lines=%d, bpl=%d, ppl=%d, bpf=%d, bpp=%d,\n" "depth=%d, pad=%d, freelines=%d, calib_backend=%d\n", ms->src_lines_to_read, ms->bpl, ms->ppl, bpf, bpp, ms->depth, pad, ms->buf.free_lines, ms->calib_backend); /* determine how many planes of each color are in the source buffer */ from = ms->buf.src_buf; for ( frame = 0; frame < 3 * ms->src_lines_to_read; frame++, from += bpf ) { switch ( *from ) { case 'R': ++ms->buf.planes[0][MS_COLOR_RED]; break; case 'G': ++ms->buf.planes[0][MS_COLOR_GREEN]; break; case 'B': ++ms->buf.planes[0][MS_COLOR_BLUE]; break; default: DBG(1, "segreg_proc_data: unknown color indicator (1) " "0x%02x\n", *from); return SANE_STATUS_IO_ERROR; } } ms->buf.free_lines -= ms->src_lines_to_read; save_current_src = ms->buf.current_src; if ( ms->buf.free_lines < ms->src_max_lines ) { ms->buf.current_src = !ms->buf.current_src; ms->buf.src_buf = ms->buf.src_buffer[ms->buf.current_src]; ms->buf.free_lines = ms->buf.free_max_lines; } else ms->buf.src_buf += ms->src_lines_to_read * ms->bpl; colseq2 = mi->color_sequence[2]; lines_to_deliver = ms->buf.planes[0][colseq2] + ms->buf.planes[1][colseq2]; if ( lines_to_deliver == 0 ) return SANE_STATUS_GOOD; DBG(30, "segreg_proc_data: planes[0][0]=%d, planes[0][1]=%d, " "planes[0][2]=%d\n", ms->buf.planes[0][0], ms->buf.planes[0][1], ms->buf.planes[0][2] ); DBG(30, "segreg_proc_data: planes[1][0]=%d, planes[1][1]=%d, " "planes[1][2]=%d\n", ms->buf.planes[1][0], ms->buf.planes[1][1], ms->buf.planes[1][2] ); while ( lines_to_deliver > 0 ) { for ( color = 0; color < 3; color++ ) { /* get the position of the next plane for each color */ do { if ( *ms->buf.current_pos[color] == colormap[color] ) break; ms->buf.current_pos[color] += bpf; } while ( 1 ); ms->buf.current_pos[color] += 2; /* skip color indicator */ } status = segreg_copy_pixels(ms); if ( status != SANE_STATUS_GOOD ) { DBG(1, "segreg_copy_pixels:status %d\n", status); return status; } for ( color = 0; color < 3; color++ ) { /* skip a padding byte at the end, if present */ ms->buf.current_pos[color] += pad; if ( ms->buf.planes[1][color] > 0 ) { --ms->buf.planes[1][color]; if ( ms->buf.planes[1][color] == 0 ) /* we have copied from the prehold buffer and are */ /* done now, we continue with the source buffer */ ms->buf.current_pos[color] = ms->buf.src_buffer[save_current_src]; } else { --ms->buf.planes[0][color]; if ( ms->buf.planes[0][color] == 0 && ms->buf.current_src != save_current_src ) ms->buf.current_pos[color] = ms->buf.src_buffer[ms->buf.current_src]; } } DBG(100, "planes_to_deliver=%d\n", lines_to_deliver); --lines_to_deliver; } if ( ms->buf.current_src != save_current_src ) { for ( color = 0; color < 3; color++ ) { ms->buf.planes[1][color] += ms->buf.planes[0][color]; ms->buf.planes[0][color] = 0; } } DBG(30, "segreg_proc_data: src_buf=%p, free_lines=%d\n", (void *) ms->buf.src_buf, ms->buf.free_lines); return SANE_STATUS_GOOD; } /*---------- segreg_copy_pixels() --------------------------------------------*/ static SANE_Status segreg_copy_pixels(Microtek2_Scanner *ms) { Microtek2_Device *md; Microtek2_Info *mi; uint32_t pixel; int color, i, gamma_by_backend, right_to_left, scale1, scale2, bpp_in; float s_w, s_d; /* shading byte from condensed_shading */ float val, maxval = 0, shading_factor = 0; uint16_t val16 = 0; uint8_t val8 = 0; uint8_t *from_effective; uint8_t *gamma[3]; float f[3]; /* color balance factor */ md = ms->dev; mi = &md->info[md->scan_source]; gamma_by_backend = md->model_flags & MD_NO_GAMMA ? 1 : 0; right_to_left = mi->direction & MI_DATSEQ_RTOL; scale1 = 16 - ms->depth; scale2 = 2 * ms->depth - 16; bpp_in = ( ms->bits_per_pixel_in + 7 ) / 8; /*Bytes per pixel from scanner*/ if ((md->model_flags & MD_READ_CONTROL_BIT) && ms->calib_backend) { maxval = (float) pow(2.0, (float) ms->depth) - 1.0; s_w = maxval; s_d = 0.0; shading_factor = (float) pow(2.0, (double) (md->shading_depth - ms->depth) ); } if ( gamma_by_backend ) { i = (ms->depth > 8) ? 2 : 1; for ( color = 0; color < 3; color++) gamma[color] = ms->gamma_table + i * (int) pow(2.0, (double)ms->depth); } DBG(30, "segreg_copy_pixels: pixels=%d\n", ms->ppl); DBG(100, "segreg_copy_pixels: buffer 0x%p, right_to_left=%d, depth=%d\n", (void *) ms->buf.current_pos, right_to_left, ms->depth); for (color = 0; color < 3; color++ ) f[color] = (float) ms->balance[color] / 100.0; DBG(100, "segreg_copy_pixels: color balance:\n" " ms->balance[R]=%d, ms->balance[G]=%d, ms->balance[B]=%d\n", ms->balance[0], ms->balance[1], ms->balance[2]); for ( pixel = 0; pixel < ms->ppl; pixel++ ) { for ( color = 0; color < 3; color++ ) { if ( right_to_left ) from_effective = ms->buf.current_pos[color] + ( ms->ppl - 1 - pixel ) * bpp_in; else from_effective = ms->buf.current_pos[color] + pixel * bpp_in; if ( ms->depth > 8 ) val = (float) *(uint16_t *)from_effective; else if ( ms->depth == 8 ) val = (float) *from_effective; else { DBG(1, "segreg_copy_pixels: Unknown depth %d\n", ms->depth); return SANE_STATUS_IO_ERROR; } if ((md->model_flags & MD_READ_CONTROL_BIT) && ms->calib_backend && ( ms->condensed_shading_w != NULL )) /* apply shading by backend */ { get_cshading_values(ms, color, pixel, shading_factor, right_to_left, &s_d, &s_w); if ( s_w == s_d ) s_w = s_d + 1; if ( val < s_d ) val = s_d; val = maxval *( val - s_d ) / ( s_w - s_d ); val *= f[color]; /* if scanner doesn't support brightness, contrast */ if ( md->model_flags & MD_NO_ENHANCEMENTS ) { val += ( ( ms->brightness_m - 128 ) * 2 ); val = ( val - 128 ) * ( ms->contrast_m / 128 ) + 128; } val = MAX( 0.0, val); val = MIN( maxval, val ); } val16 = (uint16_t) val; val8 = (uint8_t) val; /* apply gamma correction if needed */ if ( gamma_by_backend ) { if ( ms->depth > 8 ) val16 = *((uint16_t *) gamma[color] + val16); else val8 = gamma[color][val8]; } if ( ms->depth > 8 ) { val16 = ( val16 << scale1 ) | ( val16 >> scale2 ); fwrite((void *) &val16, 2, 1, ms->fp); } else { fputc((unsigned char) val8, ms->fp); } } } for ( color = 0; color < 3; color++ ) { ms->buf.current_pos[color] += ms->ppl; if ( ms->depth > 8 ) ms->buf.current_pos[color] += ms->ppl; } return SANE_STATUS_GOOD; } /*---------- lplconcat_proc_data() -------------------------------------------*/ static SANE_Status lplconcat_proc_data(Microtek2_Scanner *ms) { SANE_Status status; Microtek2_Device *md; Microtek2_Info *mi; uint32_t line; uint8_t *from[3]; uint8_t *save_from[3]; int color; int bpp; int gamma_by_backend; int right_to_left; /* 0=left to right, 1=right to left */ DBG(30, "lplconcat_proc_data: ms=%p\n", (void *) ms); /* This data format seems to honour the color sequence indicator */ md = ms->dev; mi = &md->info[md->scan_source]; bpp = ms->bits_per_pixel_out / 8; /* ms->bits_per_pixel_out is 8 or 16 */ right_to_left = mi->direction & MI_DATSEQ_RTOL; gamma_by_backend = md->model_flags & MD_NO_GAMMA ? 1 : 0; if ( right_to_left == 1 ) { for ( color = 0; color < 3; color++ ) { from[color] = ms->buf.src_buf + ( mi->color_sequence[color] + 1 ) * ( ms->bpl / 3 ) - bpp - (ms->bpl - 3 * ms->ppl * bpp) / 3; } } else for ( color = 0; color < 3; color++ ) from[color] = ms->buf.src_buf + mi->color_sequence[color] * ( ms->bpl / 3 ); for ( line = 0; line < (uint32_t) ms->src_lines_to_read; line++ ) { for ( color = 0 ; color < 3; color++ ) save_from[color] = from[color]; status = lplconcat_copy_pixels(ms, from, right_to_left, gamma_by_backend); if ( status != SANE_STATUS_GOOD ) return status; for ( color = 0; color < 3; color++ ) from[color] = save_from[color] + ms->bpl; } return SANE_STATUS_GOOD; } /*---------- lplconcat_copy_pixels() -----------------------------------------*/ static SANE_Status lplconcat_copy_pixels(Microtek2_Scanner *ms, uint8_t **from, int right_to_left, int gamma_by_backend) { Microtek2_Device *md; Microtek2_Info *mi; uint32_t pixel; uint16_t val16 = 0; uint8_t val8 = 0; uint8_t *gamma[3]; float s_d; /* dark shading pixel */ float s_w; /* white shading pixel */ float shading_factor = 0; float f[3]; /* color balance factor */ float val, maxval = 0; int color; int step, scale1, scale2; int i; DBG(30, "lplconcat_copy_pixels: ms=%p, righttoleft=%d, gamma=%d,\n", (void *) ms, right_to_left, gamma_by_backend); md = ms->dev; mi = &md->info[md->scan_source]; if ((md->model_flags & MD_READ_CONTROL_BIT) && ms->calib_backend) { shading_factor = (float) pow(2.0,(double)(md->shading_depth - ms->depth)); maxval = (float) pow(2.0, (double) ms->depth) - 1.0; s_w = maxval; s_d = 0.0; } step = ( right_to_left == 1 ) ? -1 : 1; if ( ms->depth > 8 ) step *= 2; scale1 = 16 - ms->depth; scale2 = 2 * ms->depth - 16; if ( gamma_by_backend ) { i = ( ms->depth > 8 ) ? 2 : 1; for ( color = 0; color < 3; color++ ) gamma[color] = ms->gamma_table + i * (int) pow(2.0,(double)ms->depth); } for (color = 0; color < 3; color++ ) f[color] = (float)ms->balance[color] / 100.0; DBG(100, "lplconcat_copy_pixels: color balance:\n" " ms->balance[R]=%d, ms->balance[G]=%d, ms->balance[B]=%d\n", ms->balance[0], ms->balance[1], ms->balance[2]); for ( pixel = 0; pixel < ms->ppl; pixel++ ) { for ( color = 0; color < 3; color++ ) { if ( ms->depth > 8 ) val = (float) *(uint16_t *) from[color]; else if ( ms->depth == 8 ) val = (float) *from[color]; else { DBG(1, "lplconcat_copy_pixels: Unknown depth %d\n", ms->depth); return SANE_STATUS_IO_ERROR; } if ((md->model_flags & MD_READ_CONTROL_BIT) && ms->calib_backend && ( ms->condensed_shading_w != NULL )) /* apply shading by backend */ { get_cshading_values(ms, mi->color_sequence[color], pixel, shading_factor, right_to_left, &s_d, &s_w); if ( val < s_d ) val = s_d; if ( s_w == s_d ) s_w = s_d + 1; val = ( maxval * ( val - s_d ) ) / (s_w - s_d); val *= f[color]; /* apply color balance */ /* if scanner doesn't support brightness, contrast ... */ if ( md->model_flags & MD_NO_ENHANCEMENTS ) { val += ( ( ms->brightness_m - 128 ) * 2 ); val = ( val - 128 ) * ( ms->contrast_m / 128 ) + 128; } if ( val > maxval ) val = maxval; if ( val < 0.0 ) val = 0.0; } val16 = (uint16_t) val; val8 = (uint8_t) val; /* apply gamma correction if needed */ if ( gamma_by_backend ) { if ( ms->depth > 8 ) val16 = *((uint16_t *) gamma[color] + val16); else val8 = gamma[color][val8]; } if ( ms->depth > 8 ) { val16 = ( val16 << scale1 ) | ( val16 >> scale2 ); fwrite((void *) &val16, 2, 1, ms->fp); } else { fputc((unsigned char) val8, ms->fp); } from[color] += step; } } return SANE_STATUS_GOOD; } /*---------- wordchunky_proc_data() ------------------------------------------*/ static SANE_Status wordchunky_proc_data(Microtek2_Scanner *ms) { SANE_Status status; uint8_t *from; uint32_t line; DBG(30, "wordchunky_proc_data: ms=%p\n", (void *) ms); from = ms->buf.src_buf; for ( line = 0; line < (uint32_t) ms->src_lines_to_read; line++ ) { status = wordchunky_copy_pixels(from, ms->ppl, ms->depth, ms->fp); if ( status != SANE_STATUS_GOOD ) return status; from += ms->bpl; } return SANE_STATUS_GOOD; } /*---------- wordchunky_copy_pixels() ----------------------------------------*/ static SANE_Status wordchunky_copy_pixels(uint8_t *from, uint32_t pixels, int depth, FILE *fp) { uint32_t pixel; int color; DBG(30, "wordchunky_copy_pixels: from=%p, pixels=%d, depth=%d\n", (void *) from, pixels, depth); if ( depth > 8 ) { int scale1; int scale2; uint16_t val16; scale1 = 16 - depth; scale2 = 2 * depth - 16; for ( pixel = 0; pixel < pixels; pixel++ ) { for ( color = 0; color < 3; color++ ) { val16 = *(uint16_t *) from; val16 = ( val16 << scale1 ) | ( val16 >> scale2 ); fwrite((void *) &val16, 2, 1, fp); from += 2; } } } else if ( depth == 8 ) { pixel = 0; do { fputc((char ) *from, fp); fputc((char) *(from + 2), fp); fputc((char) *(from + 4), fp); ++pixel; if ( pixel < pixels ) { fputc((char) *(from + 1), fp); fputc((char) *(from + 3), fp); fputc((char) *(from + 5), fp); ++pixel; } from += 6; } while ( pixel < pixels ); } else { DBG(1, "wordchunky_copy_pixels: Unknown depth %d\n", depth); return SANE_STATUS_IO_ERROR; } return SANE_STATUS_GOOD; } /*---------- gray_proc_data() ------------------------------------------------*/ static SANE_Status gray_proc_data(Microtek2_Scanner *ms) { SANE_Status status; Microtek2_Device *md; Microtek2_Info *mi; uint8_t *from; int gamma_by_backend, bpp; int right_to_left; /* for scanning direction */ DBG(30, "gray_proc_data: lines=%d, bpl=%d, ppl=%d, depth=%d\n", ms->src_lines_to_read, ms->bpl, ms->ppl, ms->depth); md = ms->dev; mi = &md->info[md->scan_source]; gamma_by_backend = md->model_flags & MD_NO_GAMMA ? 1 : 0; right_to_left = mi->direction & MI_DATSEQ_RTOL; bpp = ( ms->bits_per_pixel_in + 7 ) / 8; if ( right_to_left == 1 ) from = ms->buf.src_buf + ms->ppl * bpp - bpp; else from = ms->buf.src_buf; do { status = gray_copy_pixels(ms, from, right_to_left, gamma_by_backend); if ( status != SANE_STATUS_GOOD ) return status; from += ms->bpl; --ms->src_lines_to_read; } while ( ms->src_lines_to_read > 0 ); return SANE_STATUS_GOOD; } /*---------- gray_copy_pixels() ----------------------------------------------*/ static SANE_Status gray_copy_pixels(Microtek2_Scanner *ms, uint8_t *from, int right_to_left, int gamma_by_backend) { Microtek2_Device *md; uint32_t pixel; uint16_t val16; uint8_t val8; int step, scale1, scale2; float val, maxval = 0; float s_w, s_d, shading_factor = 0; DBG(30, "gray_copy_pixels: pixels=%d, from=%p, fp=%p, depth=%d\n", ms->ppl, (void *) from, (void *) ms->fp, ms->depth); md = ms->dev; step = right_to_left == 1 ? -1 : 1; if ( ms->depth > 8 ) step *= 2; val = 0; scale1 = 16 - ms->depth; scale2 = 2 * ms->depth - 16; if ((md->model_flags & MD_READ_CONTROL_BIT) && ms->calib_backend) { maxval = (float) pow(2.0, (float) ms->depth) - 1.0; s_w = maxval; s_d = 0.0; shading_factor = (float) pow(2.0, (double) (md->shading_depth - ms->depth) ); } if ( ms->depth >= 8 ) { for ( pixel = 0; pixel < ms->ppl; pixel++ ) { if ( ms->depth > 8 ) val = (float) *(uint16_t *) from; if ( ms->depth == 8 ) val = (float) *from; if ((md->model_flags & MD_READ_CONTROL_BIT) && ms->calib_backend && ( ms->condensed_shading_w != NULL )) /* apply shading by backend */ { get_cshading_values(ms, 0, pixel, shading_factor, right_to_left, &s_d, &s_w); if ( val < s_d ) val = s_d; val = ( val - s_d ) * maxval / (s_w - s_d ); val = MAX( 0.0, val ); val = MIN( maxval, val ); } if ( ms->depth > 8 ) { val16 = (uint16_t) val; if ( gamma_by_backend ) val16 = *((uint16_t *) ms->gamma_table + val16); if ( !( md->model_flags & MD_16BIT_TRANSFER ) ) val16 = ( val16 << scale1 ) | ( val16 >> scale2 ); fwrite((void *) &val16, 2, 1, ms->fp); } if ( ms->depth == 8 ) { val8 = (uint8_t) val; if ( gamma_by_backend ) val8 = ms->gamma_table[(int)val8]; fputc((char)val8, ms->fp); } from += step; } } else if ( ms->depth == 4 ) { pixel = 0; while ( pixel < ms->ppl ) { fputc((char) ( ((*from >> 4) & 0x0f) | (*from & 0xf0) ), ms->fp); ++pixel; if ( pixel < ms->ppl ) fputc((char) ((*from & 0x0f) | ((*from << 4) & 0xf0)), ms->fp); from += step; ++pixel; } } else { DBG(1, "gray_copy_pixels: Unknown depth %d\n", ms->depth); return SANE_STATUS_IO_ERROR; } return SANE_STATUS_GOOD; } /*---------- proc_onebit_data() ----------------------------------------------*/ static SANE_Status proc_onebit_data(Microtek2_Scanner *ms) { Microtek2_Device *md; Microtek2_Info *mi; uint32_t bytes_to_copy; /* bytes per line to copy */ uint32_t line; uint32_t byte; uint32_t ppl; uint8_t *from; uint8_t to; int right_to_left; int bit; int toindex; DBG(30, "proc_onebit_data: ms=%p\n", (void *) ms); md = ms->dev; mi = &md->info[md->scan_source]; from = ms->buf.src_buf; bytes_to_copy = ( ms->ppl + 7 ) / 8 ; right_to_left = mi->direction & MI_DATSEQ_RTOL; DBG(30, "proc_onebit_data: bytes_to_copy=%d, lines=%d\n", bytes_to_copy, ms->src_lines_to_read); line = 0; to = 0; do { /* in onebit mode black and white colors are inverted */ if ( right_to_left ) { /* If the direction is right_to_left, we must skip some */ /* trailing bits at the end of the scan line and invert the */ /* bit sequence. We copy 8 bits into a byte, but these bits */ /* are normally not byte aligned. */ /* Determine the position of the first bit to copy */ ppl = ms->ppl; byte = ( ppl + 7 ) / 8 - 1; bit = ppl % 8 - 1; to = 0; toindex = 8; while ( ppl > 0 ) { to |= ( ( from[byte] >> (7 - bit) ) & 0x01); --toindex; if ( toindex == 0 ) { fputc( (char) ~to, ms->fp); toindex = 8; to = 0; } else to <<= 1; --bit; if ( bit < 0 ) { bit = 7; --byte; } --ppl; } /* print the last byte of the line, if it was not */ /* completely filled */ bit = ms->ppl % 8; if ( bit != 0 ) fputc( (char) ~(to << (7 - bit)), ms->fp); } else for ( byte = 0; byte < bytes_to_copy; byte++ ) fputc( (char) ~from[byte], ms->fp); from += ms->bpl; } while ( ++line < (uint32_t) ms->src_lines_to_read ); return SANE_STATUS_GOOD; } /*---------- lineartfake_proc_data() -----------------------------------------*/ static SANE_Status lineartfake_proc_data(Microtek2_Scanner *ms) { Microtek2_Device *md; Microtek2_Info *mi; SANE_Status status; uint8_t *from; int right_to_left; DBG(30, "lineartfake_proc_data: lines=%d, bpl=%d, ppl=%d, depth=%d\n", ms->src_lines_to_read, ms->bpl, ms->ppl, ms->depth); md = ms->dev; mi = &md->info[md->scan_source]; right_to_left = mi->direction & MI_DATSEQ_RTOL; if ( right_to_left == 1 ) from = ms->buf.src_buf + ms->ppl - 1; else from = ms->buf.src_buf; do { status = lineartfake_copy_pixels(ms, from, ms->ppl, ms->threshold, right_to_left, ms->fp); if ( status != SANE_STATUS_GOOD ) return status; from += ms->bpl; --ms->src_lines_to_read; } while ( ms->src_lines_to_read > 0 ); return SANE_STATUS_GOOD; } /*---------- lineartfake_copy_pixels() ---------------------------------------*/ static SANE_Status lineartfake_copy_pixels(Microtek2_Scanner *ms, uint8_t *from, uint32_t pixels, uint8_t threshold, int right_to_left, FILE *fp) { Microtek2_Device *md; uint32_t pixel; uint32_t bit; uint8_t dest; uint8_t val; float s_d, s_w, maxval, shading_factor, grayval; int step; DBG(30, "lineartfake_copy_pixels: from=%p,pixels=%d,threshold=%d,file=%p\n", (void *) from, pixels, threshold, (void *) fp); md = ms->dev; bit = 0; dest = 0; step = right_to_left == 1 ? -1 : 1; maxval = 255.0; s_w = maxval; s_d = 0.0; shading_factor = (float) pow(2.0, (double) (md->shading_depth - 8) ); for ( pixel = 0; pixel < pixels; pixel++ ) { if ((md->model_flags & MD_READ_CONTROL_BIT) && ms->calib_backend && ( ms->condensed_shading_w != NULL )) /* apply shading by backend */ { get_cshading_values(ms, 0, pixel, shading_factor, right_to_left, &s_d, &s_w); } else /* no shading */ { s_w = maxval; s_d = 0.0; } grayval = (float) *from; if ( grayval < s_d ) grayval = s_d; grayval = ( grayval - s_d ) * maxval / (s_w - s_d ); grayval = MAX( 0.0, grayval ); grayval = MIN( maxval, grayval ); if ( (uint8_t)grayval < threshold ) val = 1; else val = 0; dest = ( dest << 1 ) | val; bit = ( bit + 1 ) % 8; if ( bit == 0 ) /* 8 input bytes processed */ { fputc((char) dest, fp); dest = 0; } from += step; } if ( bit != 0 ) { dest <<= 7 - bit; fputc((char) dest, fp); } return SANE_STATUS_GOOD; } /*---------- auto_adjust_proc_data() -----------------------------------------*/ static SANE_Status auto_adjust_proc_data(Microtek2_Scanner *ms, uint8_t **temp_current) { Microtek2_Device *md; Microtek2_Info *mi; SANE_Status status; uint8_t *from; uint32_t line; uint32_t lines; uint32_t pixel; uint32_t threshold; int right_to_left; DBG(30, "auto_adjust_proc_data: ms=%p, temp_current=%p\n", (void *) ms, (void *) *temp_current); md = ms->dev; mi = &md->info[md->scan_source]; right_to_left = mi->direction & MI_DATSEQ_RTOL; memcpy(*temp_current, ms->buf.src_buf, ms->transfer_length); *temp_current += ms->transfer_length; threshold = 0; status = SANE_STATUS_GOOD; if ( ms->src_remaining_lines == 0 ) /* we have read all the image data, */ { /* calculate threshold value */ for ( pixel = 0; pixel < ms->remaining_bytes; pixel++ ) threshold += *(ms->temporary_buffer + pixel); threshold /= ms->remaining_bytes; lines = ms->remaining_bytes / ms->bpl; for ( line = 0; line < lines; line++ ) { from = ms->temporary_buffer + line * ms->bpl; if ( right_to_left == 1 ) from += ms->ppl - 1; status = lineartfake_copy_pixels(ms, from, ms->ppl, (uint8_t) threshold, right_to_left, ms->fp); } *temp_current = NULL; } return status; } /*-------------- get_cshading_values -----------------------------------------*/ static SANE_Status get_cshading_values(Microtek2_Scanner *ms, uint8_t color, uint32_t pixel, float shading_factor, int right_to_left, float *s_d, float *s_w) { Microtek2_Device *md; uint32_t csh_offset; md = ms->dev; if ( right_to_left == 1 ) csh_offset = (color + 1) * ms->ppl - 1 - pixel; else csh_offset = color * ms->ppl + pixel; if ( ( md->shading_depth > 8 ) && ( ms->lut_entry_size == 2) ) /* condensed shading is 2 byte color data */ { if ( ms->condensed_shading_d != NULL ) *s_d = (float) *( (uint16_t *)ms->condensed_shading_d + csh_offset ); else *s_d = 0.0; *s_w = (float) *( (uint16_t *)ms->condensed_shading_w + csh_offset ); *s_w /= shading_factor; *s_d /= shading_factor; } else /* condensed shading is 8 bit data */ { *s_w = (float) *( ms->condensed_shading_w + csh_offset ); if ( ms->condensed_shading_d != NULL ) *s_d = (float) *( ms->condensed_shading_d + csh_offset ); else *s_d = 0.0; } return SANE_STATUS_GOOD; } backends-1.3.0/backend/microtek2.conf.in000066400000000000000000000004271456256263500200500ustar00rootroot00000000000000# 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 backends-1.3.0/backend/microtek2.h000066400000000000000000001553541456256263500167570ustar00rootroot00000000000000/******************************************************************************* * SANE - Scanner Access Now Easy. microtek2.h This file (C) 1998, 1999 Bernd Schroeder 2000, 2001 Karsten Festag 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, see . 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 microtek2_h #define microtek2_h #include /******************************************************************************/ /* Miscellaneous defines */ /******************************************************************************/ #ifndef PATH_MAX # define PATH_MAX 1024 #endif #ifdef HAVE_AUTHORIZATION #ifndef PATH_SEP #if defined(_WIN32) || defined(HAVE_OS2_H) # define PATH_SEP "\\" #else # define PATH_SEP "/" #endif #endif #define MAX_LINE_LEN 512 /* max length of entry in password file */ #define PASSWD_FILE STRINGIFY(PATH_SANE_CONFIG_DIR) PATH_SEP "auth" #define SEPARATOR ':' /* separator in that file */ #define SALT "ab" /* used by crypt() */ #endif /* HAVE_AUTHORIZATION */ #define ENDIAN_TYPE(d) { unsigned i, test = 0; \ for (i=0; i < sizeof(int); i++ ) \ test += i << (8 * i); \ d = ((char *) &test)[0] == 0 ? 0 : 1; } #define MIN(a,b) ((a) < (b)) ? (a) : (b) #define MAX(a,b) ((a) > (b)) ? (a) : (b) #define MICROTEK2_MAJOR 0 #define MICROTEK2_MINOR 96 #define MICROTEK2_BUILD "200410042220" #define MICROTEK2_CONFIG_FILE "microtek2.conf" /******************************************************************************/ /* defines that are common to all devices */ /******************************************************************************/ #define MD_RESOLUTION_DEFAULT 72 << SANE_FIXED_SCALE_SHIFT #define MD_BRIGHTNESS_DEFAULT 100 << SANE_FIXED_SCALE_SHIFT #define MD_CONTRAST_DEFAULT 100 << SANE_FIXED_SCALE_SHIFT #define MD_THRESHOLD_DEFAULT 128 #define MD_GAMMA_DEFAULT SANE_FIX(2.2) #define MD_SHADOW_DEFAULT 0 #define MD_MIDTONE_DEFAULT 128 #define MD_HIGHLIGHT_DEFAULT 255 #define MD_EXPOSURE_DEFAULT 0 #define M_BRIGHTNESS_DEFAULT 128 #define M_CONTRAST_DEFAULT 128 #define M_SHADOW_DEFAULT 0 #define M_MIDTONE_DEFAULT 128 #define M_HIGHLIGHT_DEFAULT 255 #define M_EXPOSURE_DEFAULT 0 #define M_THRESHOLD_DEFAULT 128 /******************************************************************************/ /* SCSI commands used by the scanner */ /******************************************************************************/ /* INQUIRY */ #define INQ_CMD(d) d[0] = 0x12; d[1] = 0x00; d[2] = 0x00; \ d[3] = 0x00; d[4] = 0x00; d[5] = 0x00 #define INQ_CMD_L 6 #define INQ_ALLOC_L 5 /* first get 5 bytes */ #define INQ_ALLOC_P 4 #define INQ_SET_ALLOC(d,s) (d)[4] = (s) #define INQ_GET_INQLEN(d,s) d = (s)[4] #define INQ_GET_QUAL(d,s) d = ((s)[0] >> 5) & 0x07 #define INQ_GET_DEVT(d,s) d = (s)[0] & 0x1f #define INQ_GET_VERSION(d,s) d = (s)[2] & 0x02 #define INQ_VENDOR_L 8 #define INQ_GET_VENDOR(d,s) strncpy(d, &(s)[8], INQ_VENDOR_L); \ d[INQ_VENDOR_L] = '\0' #define INQ_MODEL_L 16 #define INQ_GET_MODEL(d,s) strncpy(d, &(s)[16], INQ_MODEL_L); \ d[INQ_MODEL_L] = '\0' #define INQ_REV_L 4 #define INQ_GET_REV(d,s) strncpy(d, &(s)[32], INQ_REV_L); \ d[INQ_REV_L] = '\0' #define INQ_GET_MODELCODE(d,s) d = (s)[36] /* TEST_UNIT_READY */ #define TUR_CMD(d) d[0]=0x00; d[1]=0x00; d[2]=0x00; \ d[3]=0x00; d[4]=0x00; d[5]=0x00 #define TUR_CMD_L 6 /* READ GAMMA TABLE */ #define RG_CMD(d) (d)[0] = 0x28; (d)[1] = 0x00; (d)[2] = 0x03; \ (d)[3] = 0x00; (d)[4] = 0x00; (d)[5] = 0x00; \ (d)[6] = 0x00; (d)[7] = 0x00; (d)[8] = 0x00; \ (d)[9] = 0x00 #define RG_CMD_L 10 #define RG_PCORMAC(d,p) (d)[5] |= (((p) << 7) & 0x80) #define RG_COLOR(d,p) (d)[5] |= (((p) << 5) & 0x60) #define RG_WORD(d,p) (d)[5] |= ((p) & 0x01) #define RG_TRANSFERLENGTH(d,p) (d)[7] = (((p) >> 8) & 0xff); \ (d)[8] = ((p) & 0xff) /* SEND GAMMA TABLE */ #define SG_SET_CMD(d) (d)[0] = 0x2a; (d)[1] = 0x00; (d)[2] = 0x03; \ (d)[3] = 0x00; (d)[4] = 0x00; (d)[5] = 0x00; \ (d)[6] = 0x00; (d)[7] = 0x00; (d)[8] = 0x00; \ (d)[9] = 0x00 #define SG_CMD_L 10 #define SG_SET_PCORMAC(d,p) (d)[5] |= (((p) << 7) & 0x80) #define SG_SET_COLOR(d,p) (d)[5] |= (((p) << 5) & 0x60) #define SG_SET_WORD(d,p) (d)[5] |= ((p) & 0x01) #define SG_SET_TRANSFERLENGTH(d,p) (d)[7] = (((p) >> 8) & 0xff); \ (d)[8] = ((p) & 0xff) #define SG_DATA_P SG_CMD_L /* READ CONTROL BITS */ #define RCB_SET_CMD(d) (d)[0] = 0x28; (d)[1] = 0x00; (d)[2] = 0x90; \ (d)[3] = 0x00; (d)[4] = 0x00; (d)[5] = 0x00; \ (d)[6] = 0x00; (d)[7] = 0x00; (d)[8] = 0x00; \ (d)[9] = 0x00 #define RCB_CMD_L 10 #define RCB_SET_LENGTH(d,s) (d)[6] = (((s) >> 16) & 0xff); \ (d)[7] = (((s) >> 8) & 0xff); \ (d)[8] = ((s) & 0xff); /* READ_SCANNER_ATTRIBUTES */ #define RSA_CMD(d) d[0]=0x28; d[1]=0x00; d[2]=0x82; d[3]=0x00; \ d[4]=0x00; d[5]=0x00; d[6]=0x00; d[7]=0x00; \ d[8]=0x28; d[9]=0x00 #define RSA_CMD_L 10 #define RSA_SETMEDIA(d,p) d[5] |= ((p) & 0x77) #define RSA_TRANSFERLENGTH 40 #define RSA_COLOR(d,s) d = (((s)[0] >> 7) & 0x01) #define RSA_ONEPASS(d,s) d = (((s)[0] >> 6) & 0x01) #define RSA_SCANNERTYPE(d,s) d = (((s)[0] >> 4) & 0x03) #define RSA_FEPROM(d,s) d = (((s)[0] >> 3) & 0x01) #define RSA_DATAFORMAT(d,s) d = ((s)[0] & 0x07) #define RSA_COLORSEQUENCE_L 3 #define RSA_COLORSEQUENCE(d,s) { \ d[0] = (((s)[1] >> 6) & 0x03); \ d[1] = (((s)[1] >> 4) & 0x03); \ d[2] = (((s)[1] >> 2) & 0x03); \ } #define RSA_NIS(d,s) d = ((s)[1] & 0x02) #define RSA_DATSEQ(d,s) d = ((s)[1] & 0x01) #define RSA_CCDGAP(d,s) d = (s)[2] #define RSA_MAX_XRESOLUTION(d,s) d = ((s)[3] << 8) + (s)[4] #define RSA_MAX_YRESOLUTION(d,s) d = ((s)[5] << 8) + (s)[6] #define RSA_GEOWIDTH(d,s) d = ((s)[7] << 8) + (s)[8] #define RSA_GEOHEIGHT(d,s) d = ((s)[9] << 8) + (s)[10] #define RSA_OPTRESOLUTION(d,s) d = ((s)[11] << 8) + (s)[12] #define RSA_DEPTH(d,s) d = (((s)[13] >> 4) & 0x0f) #define RSA_SCANMODE(d,s) d = (s)[13] & 0x0f #define RSA_CCDPIXELS(d,s) d = ((s)[14] << 8) + (s)[15] #define RSA_LUTCAP(d,s) d = (s)[16] #define RSA_DNLDPTRN(d,s) d = (((s)[17] >> 7) & 0x01) #define RSA_GRAINSLCT(d,s) d = (s)[17] & 0x7f #define RSA_SUPPOPT(d,s) d = (s)[18] & 0xf3 #define RSA_CALIBWHITE(d,s) d = ((s)[19] << 24) + ((s)[20] << 16) \ + ((s)[21] << 8) + (s)[22] #define RSA_CALIBSPACE(d,s) d = ((s)[23] << 24) + ((s)[24] << 16) \ + ((s)[25] << 8) + (s)[26] #define RSA_NLENS(d,s) d = (s)[27] #define RSA_NWINDOWS(d,s) d = (s)[28] #define RSA_SHTRNSFEREQU(d,s) d = (((s)[29] >> 2) & 0x3f) #define RSA_SCNBTTN(d,s) d = (((s)[29] >> 1) & 0x01) #define RSA_BUFTYPE(d,s) d = (s)[29] & 0x01 #define RSA_REDBALANCE(d,s) d = ((s)[30] << 8) | (s)[31] #define RSA_GREENBALANCE(d,s) d = ((s)[32] << 8) | (s)[33] #define RSA_BLUEBALANCE(d,s) d = ((s)[34] << 8) | (s)[35] #define RSA_APSMAXFRAMES(d,s) d = (s)[36] /* READ IMAGE INFORMATION */ #define RII_SET_CMD(d) (d)[0] = 0x28; (d)[1] = 0x00; (d)[2] = 0x80; \ (d)[3] = 0x00; (d)[4] = 0x00; (d)[5] = 0x00; \ (d)[6] = 0x00; (d)[7] = 0x00; (d)[8] = 0x10; \ (d)[9] = 0x00 #define RII_CMD_L 10 #define RII_RESULT_L 16 #define RII_GET_WIDTHPIXEL(d,s) d = ((s)[0] << 24) + ((s)[1] << 16) \ + ((s)[2] << 8) + (s)[3] #define RII_GET_WIDTHBYTES(d,s) d = ((s)[4] << 24) + ((s)[5] << 16) \ + ((s)[6] << 8) + (s)[7] #define RII_GET_HEIGHTLINES(d,s) d = ((s)[8] << 24) + ((s)[9] << 16) \ + ((s)[10] << 8) + (s)[11] #define RII_GET_REMAINBYTES(d,s) d = ((s)[12] << 24) + ((s)[13] << 16) \ + ((s)[14] << 8) + (s)[15] /* The V300 returns some values in only two bytes */ #define RII_GET_V300_WIDTHPIXEL(d,s) d = ((s)[0] << 8) + (s)[1] #define RII_GET_V300_WIDTHBYTES(d,s) d = ((s)[2] << 8) + (s)[3] #define RII_GET_V300_HEIGHTLINES(d,s) d = ((s)[4] << 8) + (s)[5] #define RII_GET_V300_REMAINBYTES(d,s) d = ((s)[6] << 24) + ((s)[7] << 16) \ + ((s)[8] << 8) + (s)[9] /* READ SHADING INFORMATION */ #define RSI_SET_CMD(d) (d)[0] = 0x28; (d)[1] = 0x00; (d)[2] = 0x01; \ (d)[3] = 0x00; (d)[4] = 0x00; (d)[5] = 0x00; \ (d)[6] = 0x00; (d)[7] = 0x00; (d)[8] = 0x10; \ (d)[9] = 0x00 #define RSI_CMD_L 10 #define RSI_SET_PCORMAC(d,s) (d)[5] |= (((s) << 7) & 0x80) #define RSI_SET_COLOR(d,s) (d)[5] |= (((s) << 5) & 0x60) #define RSI_SET_DARK(d,s) (d)[5] |= (((s) << 1) & 0x02) /*(KF)*/ /* RSI_SET_DARK was missing*/ #define RSI_SET_WORD(d,s) (d)[5] |= ((s) & 0x01) #define RSI_SET_TRANSFERLENGTH(d,s) (d)[6] = (((s) >> 16) & 0xff); \ (d)[7] = (((s) >> 8) & 0xff); \ (d)[8] = ((s) & 0xff); /* SEND SHADING INFORMATION */ #define SSI_SET_CMD(d) (d)[0] = 0x2a; (d)[1] = 0x00; (d)[2] = 0x01; \ (d)[3] = 0x00; (d)[4] = 0x00; (d)[5] = 0x00; \ (d)[6] = 0x00; (d)[7] = 0x00; (d)[8] = 0x00; \ (d)[9] = 0x00 #define SSI_CMD_L 10 #define SSI_SET_PCORMAC(d,s) (d)[5] |= (((s) << 7) & 0x80) #define SSI_SET_COLOR(d,s) (d)[5] |= (((s) << 5) & 0x60) #define SSI_SET_DARK(d,s) (d)[5] |= (((s) << 1) & 0x02) #define SSI_SET_WORD(d,s) (d)[5] |= ((s) & 0x01) #define SSI_SET_TRANSFERLENGTH(d,s) (d)[6] = (((s) >> 16) & 0xff); \ (d)[7] = (((s) >> 8) & 0xff); \ (d)[8] = ((s) & 0xff); /* READ IMAGE */ /* READ IMAGE */ #define RI_SET_CMD(d) (d)[0] = 0x28; (d)[1] = 0x00; (d)[2] = 0x00; \ (d)[3] = 0x00; (d)[4] = 0x00; (d)[5] = 0x00; \ (d)[6] = 0x00; (d)[7] = 0x00; (d)[8] = 0x00; \ (d)[9] = 0x00 #define RI_CMD_L 10 #define RI_SET_PCORMAC(d,s) (d)[4] |= (((s) << 7) & 0x80) #define RI_SET_COLOR(d,s) (d)[4] |= (((s) << 5) & 0x60) #define RI_SET_TRANSFERLENGTH(d,s) (d)[6] = (((s) >> 16) & 0xff); \ (d)[7] = (((s) >> 8) & 0xff); \ (d)[8] = ((s) & 0xff); /* READ SYSTEM_STATUS */ #define RSS_CMD(d) (d)[0] = 0x28; (d)[1] = 0x00; (d)[2] = 0x81; \ (d)[3] = 0x00; (d)[4] = 0x00; (d)[5] = 0x00; \ (d)[6] = 0x00; (d)[7] = 0x00; (d)[8] = 0x09; \ (d)[9] = 0x00 #define RSS_CMD_L 10 #define RSS_RESULT_L 9 #define RSS_FIRSTSCAN(s) (s)[0] & 0x80 #define RSS_AFOCUS(s) (s)[0] & 0x40 #define RSS_SSKIP(s) (s)[0] & 0x20 #define RSS_STICK(s) (s)[0] & 0x10 #define RSS_NTRACK(s) (s)[0] & 0x08 #define RSS_NCALIB(s) (s)[0] & 0x04 #define RSS_TLAMP(s) (s)[0] & 0x02 #define RSS_FLAMP(s) (s)[0] & 0x01 #define RSS_FSH(s) (s)[1] & 0x20 #define RSS_TEFLAG(s) (s)[1] & 0x10 #define RSS_WHITESTRIE(s) (s)[1] & 0x08 #define RSS_RDYMAN(s) (s)[1] & 0x04 #define RSS_TRDY(s) (s)[1] & 0x02 #define RSS_FRDY(s) (s)[1] & 0x01 #define RSS_ADP(s) (s)[2] & 0x80 #define RSS_DETECT(s) (s)[2] & 0x40 #define RSS_ADPTIME(s) (s)[2] & 0x3f #define RSS_LENSSTATUS(s) (s)[3] #define RSS_ALOFF(s) (s)[4] & 0x80 #define RSS_TIMEREMAIN(s) (s)[4] & 0x7f #define RSS_MTMACNT(s) (s)[5] & 0x40 #define RSS_MOVE(s) (s)[5] & 0x20 #define RSS_LAMP(s) (s)[5] & 0x10 #define RSS_EJECT(s) (s)[5] & 0x08 #define RSS_TMACNT(s) (s)[5] & 0x04 #define RSS_PAPER(s) (s)[5] & 0x02 #define RSS_ADFCNT(s) (s)[5] & 0x01 #define RSS_CANCELBTN(s) (s)[6] & 0x80 #define RSS_COPYBTN(s) (s)[6] & 0x40 #define RSS_EMAILBTN(s) (s)[6] & 0x20 #define RSS_GOBTN(s) (s)[6] & 0x10 #define RSS_TURBOBTN(s) (s)[6] & 0x08 #define RSS_CURRENTMODE(s) (s)[6] & 0x07 #define RSS_BUTTONCOUNT(s) (s)[7] #define RSS_MFOCUS(s) (s)[9] /* SEND SYSTEM STATUS */ #define SSS_CMD(d) (d)[0] = 0x2a; (d)[1] = 0x00; (d)[2] = 0x81; \ (d)[3] = 0x00; (d)[4] = 0x00; (d)[5] = 0x00; \ (d)[6] = 0x00; (d)[7] = 0x00; (d)[8] = 0x09; \ (d)[9] = 0x00 #define SSS_CMD_L 10 #define SSS_DATA_L 9 #define SSS_AFOCUS(d,p) d[0] |= (p) & 0x40 #define SSS_STICK(d,p) d[0] |= (p) & 0x10 #define SSS_NTRACK(d,p) d[0] |= (p) & 0x08 #define SSS_NCALIB(d,p) d[0] |= (p) & 0x04 #define SSS_TLAMP(d,p) d[0] |= (p) & 0x02 #define SSS_FLAMP(d,p) d[0] |= (p) & 0x01 #define SSS_RESERVED17(d,p) d[1] |= (p) & 0x80 #define SSS_FSH(d,p) d[1] |= (p) & 0x40 #define SSS_RDYMAN(d,p) d[1] |= (p) & 0x04 #define SSS_TRDY(d,p) d[1] |= (p) & 0x02 #define SSS_FRDY(d,p) d[1] |= (p) & 0x01 #define SSS_ADP(d,p) d[2] |= (p) & 0x80 #define SSS_DETECT(d,p) d[2] |= (p) & 0x40 #define SSS_ADPTIME(d,p) d[2] |= (p) & 0x3f #define SSS_LENSSTATUS(d,p) d[3] |= (p) #define SSS_ALOFF(d,p) d[4] |= (p) & 0x80 #define SSS_TIMEREMAIN(d,p) d[4] |= (p) & 0x7f #define SSS_TMACNT(d,p) d[5] |= (p) & 0x04 #define SSS_PAPER(d,p) d[5] |= (p) & 0x02 #define SSS_ADFCNT(d,p) d[5] |= (p) & 0x01 #define SSS_CURRENTMODE(d,p) d[6] |= (p) & 0x07 #define SSS_BUTTONCOUNT(d,p) d[6] |= (p) #define SSS_MFOCUS(s) d[9] |= (p) /* SET WINDOW */ #define SW_CMD(d) d[0]=0x24; d[1]=0x00; d[2]=0x00; d[3]=0x00; \ d[4]=0x00; d[5]=0x00; d[6]=0x00; d[7]=0x00;\ d[8]=0x00; d[9]=0x00 #define SW_CMD_L 10 #define SW_HEADER_L 8 #define SW_BODY_L 61 #define SW_CMD_P 0 /* command at position 0 */ #define SW_HEADER_P SW_CMD_L #define SW_BODY_P(n) SW_CMD_L + SW_HEADER_L + (n) * SW_BODY_L /* d: SW_CMD_P, SW_HEADER_P, SW_BODY_P(n) */ #define SW_PARAM_LENGTH(d,p) (d)[6] = ((p) >> 16) & 0xff; \ (d)[7] = ((p) >> 8) & 0xff;\ (d)[8] = (p) & 0xff #define SW_WNDDESCVAL SW_BODY_L #define SW_WNDDESCLEN(d,p) (d)[6] = ((p) >> 8) & 0xff; \ (d)[7] = (p) & 0xff #define SW_WNDID(d,p) (d)[0] = (p) #define SW_XRESDPI(d,p) (d)[2] = ((p) >> 8) & 0xff; \ (d)[3] = (p) & 0xff #define SW_YRESDPI(d,p) (d)[4] = ((p) >> 8) & 0xff; \ (d)[5] = (p) & 0xff #define SW_XPOSTL(d,p) (d)[6] = ((p) >> 24) & 0xff; \ (d)[7] = ((p) >> 16) & 0xff; \ (d)[8] = ((p) >> 8) & 0xff; \ (d)[9] = ((p)) & 0xff #define SW_YPOSTL(d,p) (d)[10] = ((p) >> 24) & 0xff; \ (d)[11] = ((p) >> 16) & 0xff; \ (d)[12] = ((p) >> 8) & 0xff; \ (d)[13] = (p) & 0xff #define SW_WNDWIDTH(d,p) (d)[14] = ((p) >> 24) & 0xff; \ (d)[15] = ((p) >> 16) & 0xff; \ (d)[16] = ((p) >> 8) & 0xff; \ (d)[17] = (p) & 0xff #define SW_WNDHEIGHT(d,p) (d)[18] = ((p) >> 24) & 0xff; \ (d)[19] = ((p) >> 16) & 0xff; \ (d)[20] = ((p) >> 8) & 0xff; \ (d)[21] = (p) & 0xff #define SW_BRIGHTNESS_M(d,p) (d)[22] = (p) #define SW_THRESHOLD(d,p) (d)[23] = (p) #define SW_CONTRAST_M(d,p) (d)[24] = (p) #define SW_IMGCOMP(d,p) (d)[25] = (p) & 0x0f /* take lineartfake */ /* into account */ #define SW_BITSPERPIXEL(d,p) (d)[26] = (p) #define SW_EXPOSURE_M(d,p) (d)[27] = (p) #define SW_EXTHT(d,p) (d)[28] |= (((p) << 7) & 0x80) #define SW_INTHTINDEX(d,p) (d)[28] |= ((p) & 0x7f) #define SW_RIF(d,p) (d)[29] |= (((p) << 7) & 0x80) #define SW_NOGAMMA(d,p) (d)[29] |= (((p) << 6) & 0x40) #define SW_SLOWSCAN(d,p) (d)[29] |= (((p) << 5) & 0x20) #define SW_LENS(d,p) (d)[30] = (p) #define SW_INFINITE(d,p) (d)[31] |= (((p) << 7) & 0x80) #define SW_STAY(d,p) (d)[31] |= (((p) << 6) & 0x40) #define SW_RAWDAT(d,p) (d)[31] |= (((p) << 5) & 0x20) #define SW_QUALITY(d,p) (d)[31] |= (((p) << 4) & 0x10) #define SW_FASTSCAN(d,p) (d)[31] |= (((p) << 3) & 0x08) #define SW_MEDIA(d,p) (d)[31] |= ((p) & 0x07) #define SW_JPEGENABLE(d,p) (d)[34] |= (((p) << 7) & 0x80) #define SW_JPEGALGOR(d,p) (d)[34] |= (((p) << 5) & 0x60) #define SW_JPEGMODE(d,p) (d)[34] |= (((p) << 3) & 0x18) #define SW_SHADOW_M(d,p) (d)[40] = (p) #define SW_MIDTONE_M(d,p) (d)[41] = (p) #define SW_HIGHLIGHT_M(d,p) (d)[42] = (p) #define SW_BRIGHTNESS_R(d,p) (d)[43] = (p) #define SW_CONTRAST_R(d,p) (d)[44] = (p) #define SW_EXPOSURE_R(d,p) (d)[45] = (p) #define SW_SHADOW_R(d,p) (d)[46] = (p) #define SW_MIDTONE_R(d,p) (d)[47] = (p) #define SW_HIGHLIGHT_R(d,p) (d)[48] = (p) #define SW_BRIGHTNESS_G(d,p) (d)[49] = (p) #define SW_CONTRAST_G(d,p) (d)[50] = (p) #define SW_EXPOSURE_G(d,p) (d)[51] = (p) #define SW_SHADOW_G(d,p) (d)[52] = (p) #define SW_MIDTONE_G(d,p) (d)[53] = (p) #define SW_HIGHLIGHT_G(d,p) (d)[54] = (p) #define SW_BRIGHTNESS_B(d,p) (d)[55] = (p) #define SW_CONTRAST_B(d,p) (d)[56] = (p) #define SW_EXPOSURE_B(d,p) (d)[57] = (p) #define SW_SHADOW_B(d,p) (d)[58] = (p) #define SW_MIDTONE_B(d,p) (d)[59] = (p) #define SW_HIGHLIGHT_B(d,p) (d)[60] = (p) /* READ IMAGE STATUS */ #define RIS_SET_CMD(d) (d)[0] = 0x28; (d)[1] = 0x00; (d)[2] = 0x83; \ (d)[3] = 0x00; (d)[4] = 0x00; (d)[5] = 0x00; \ (d)[6] = 0x00; (d)[7] = 0x00; (d)[8] = 0x00; \ (d)[9] = 0x00 #define RIS_CMD_L 10 #define RIS_SET_PCORMAC(d,p) (d)[4] |= (((p) << 7) & 0x80) #define RIS_SET_COLOR(d,p) (d)[4] |= (((p) << 5) & 0x60) /* REQUEST SENSE */ #define RQS_CMD(d) (d)[0] = 0x03; (d)[1] = 0x00; (d)[2] = 0x00; \ (d)[3] = 0x00; (d)[5] = 0x00 #define RQS_CMD_L 6 #define RQS_ALLOCLENGTH(d,p) (d)[4] = (p) #define RQS_LENGTH(s) (s)[7] + 7 #define RQS_SENSEKEY_NOSENSE 0x00 #define RQS_SENSEKEY_HWERR 0x04 #define RQS_SENSEKEY_ILLEGAL 0x05 #define RQS_SENSEKEY_VENDOR 0x09 #define RQS_SENSEKEY(s) ((s)[2] & 0x0f) #define RQS_INFO(s) ((s)[3] << 24) + ((s)[4] << 16) \ + ((s)[5] << 8) + ((s)[6]) #define RQS_ASL(s) (s)[7] #define RQS_REMAINBYTES(s) ((s)[8] << 24) + ((s)[9] << 16) \ + ((s)[10] << 8) + ((s)[11]) #define RQS_ASC(s) (s)[12] /* ASC == 0x40 */ #define RQS_ASCQ_CPUERR 0x81 #define RQS_ASCQ_SRAMERR 0x82 #define RQS_ASCQ_DRAMERR 0x84 #define RQS_ASCQ_DCOFF 0x88 #define RQS_ASCQ_GAIN 0x90 #define RQS_ASCQ_POS 0xa0 #define RQS_ASCQ(s) (s)[13] #define RQS_ASINFO(s) &((s)[18]) #define RQS_ASINFOLENGTH(s) (s)[7] - 11 /******************************************************************************/ /* enumeration of Option Descriptors */ /******************************************************************************/ enum Microtek2_Option { /*0*/ OPT_NUM_OPTS = 0, /*1*/OPT_MODE_GROUP, OPT_SOURCE, OPT_MODE, OPT_BITDEPTH, OPT_RESOLUTION, OPT_Y_RESOLUTION, OPT_PREVIEW, /*9*/ OPT_GEOMETRY_GROUP, OPT_TL_X, /* top-left x */ OPT_TL_Y, /* top-left y */ OPT_BR_X, /* bottom-right x */ OPT_BR_Y, /* bottom-right y */ /*14*/ OPT_ENHANCEMENT_GROUP, OPT_BRIGHTNESS, OPT_CONTRAST, OPT_THRESHOLD, OPT_HALFTONE, OPT_AUTOADJUST, /*20*/ OPT_GAMMA_GROUP, OPT_GAMMA_MODE, OPT_GAMMA_SCALAR, OPT_GAMMA_SCALAR_R, OPT_GAMMA_SCALAR_G, OPT_GAMMA_SCALAR_B, OPT_GAMMA_CUSTOM, OPT_GAMMA_CUSTOM_R, OPT_GAMMA_CUSTOM_G, OPT_GAMMA_CUSTOM_B, OPT_GAMMA_BIND, /*31*/ OPT_SMH_GROUP, /* these options must appear in exactly this order, */ /* sane_control_option relies on it */ OPT_CHANNEL, OPT_SHADOW, OPT_MIDTONE, OPT_HIGHLIGHT, OPT_SHADOW_R, OPT_MIDTONE_R, OPT_HIGHLIGHT_R, OPT_SHADOW_G, OPT_MIDTONE_G, OPT_HIGHLIGHT_G, OPT_SHADOW_B, OPT_MIDTONE_B, OPT_HIGHLIGHT_B, OPT_EXPOSURE, OPT_EXPOSURE_R, OPT_EXPOSURE_G, OPT_EXPOSURE_B, /*49*/ OPT_SPECIAL, OPT_RESOLUTION_BIND, OPT_DISABLE_BACKTRACK, OPT_CALIB_BACKEND, OPT_LIGHTLID35, OPT_TOGGLELAMP, /*55*/ OPT_COLORBALANCE, OPT_BALANCE_R, OPT_BALANCE_G, OPT_BALANCE_B, OPT_BALANCE_FW, /*60*/ NUM_OPTIONS }; /******************************************************************************/ /* Description of options not included in saneopts.h */ /******************************************************************************/ #define M_TITLE_SCANMODEGRP SANE_I18N("Scan Mode") #define M_TITLE_GEOMGRP SANE_I18N("Geometry") #define M_TITLE_ENHANCEGRP SANE_I18N("Enhancement") #define M_TITLE_SMHGRP SANE_I18N("Shadow, midtone, highlight,"\ " exposure time"); #define M_TITLE_SPECIALGRP SANE_I18N("Special options") #define M_TITLE_COLBALANCEGRP SANE_I18N("Color balance") #define M_NAME_NOBACKTRACK "no-backtracking" #define M_TITLE_NOBACKTRACK SANE_I18N("Disable backtracking") #define M_DESC_NOBACKTRACK SANE_I18N("If checked the scanner does not" \ " perform backtracking") #define M_NAME_TOGGLELAMP "toggle-lamp" #define M_TITLE_TOGGLELAMP SANE_I18N("Toggle lamp of flatbed") #define M_DESC_TOGGLELAMP SANE_I18N("Toggles the lamp of the flatbed") #define M_NAME_CALIBBACKEND "backend-calibration" #define M_TITLE_CALIBBACKEND SANE_I18N("Calibration by backend") #define M_DESC_CALIBBACKEND SANE_I18N("If checked the color calibration" \ " before a scan is done by the backend") #define M_NAME_LIGHTLID35 "lightlid35" #define M_TITLE_LIGHTLID35 SANE_I18N("Use the lightlid-35mm adapter") #define M_DESC_LIGHTLID35 SANE_I18N("This option turns off the lamp of" \ " the flatbed during a scan") #define M_NAME_QUALITY_SCAN "quality-scan" #define M_TITLE_QUALITY_SCAN SANE_I18N("Quality scan") #define M_DESC_QUALITY_SCAN SANE_I18N("Highest quality but lower speed") #define M_NAME_FAST_SCAN "fast-scan" #define M_TITLE_FAST_SCAN SANE_I18N("Fast scan") #define M_DESC_FAST_SCAN SANE_I18N("Highest speed but lower quality") #define M_NAME_AUTOADJUST "lineart-auto-adjust" #define M_TITLE_AUTOADJUST SANE_I18N("Automatic adjustment of threshold") #define M_DESC_AUTOADJUST SANE_I18N("If checked the backend automatically"\ " tries to determine an optimal value for the" \ " threshold.") #define M_NAME_GAMMA_MODE "gamma-correction" #define M_TITLE_GAMMA_MODE SANE_I18N("Gamma correction") #define M_DESC_GAMMA_MODE SANE_I18N("Selects the gamma correction mode.") #define M_NAME_GAMMA_BIND "bind-gamma" #define M_TITLE_GAMMA_BIND SANE_I18N("Bind gamma") #define M_DESC_GAMMA_BIND SANE_I18N("Use same gamma values for all" \ " colour channels.") #define M_NAME_GAMMA_SCALAR "scalar-gamma" #define M_TITLE_GAMMA_SCALAR SANE_I18N("Scalar gamma") #define M_DESC_GAMMA_SCALAR SANE_I18N("Selects a value for scalar" \ " gamma correction.") #define M_NAME_GAMMA_SCALAR_R "scalar-gamma-r" #define M_TITLE_GAMMA_SCALAR_R SANE_I18N("Scalar gamma red") #define M_DESC_GAMMA_SCALAR_R SANE_I18N("Selects a value for scalar gamma" \ " correction (red channel)") #define M_NAME_GAMMA_SCALAR_G "scalar-gamma-g" #define M_TITLE_GAMMA_SCALAR_G SANE_I18N("Scalar gamma green") #define M_DESC_GAMMA_SCALAR_G SANE_I18N("Selects a value for scalar gamma" \ " correction (green channel)") #define M_NAME_GAMMA_SCALAR_B "scalar-gamma-b" #define M_TITLE_GAMMA_SCALAR_B SANE_I18N("Scalar gamma blue") #define M_DESC_GAMMA_SCALAR_B SANE_I18N("Selects a value for scalar gamma" \ " correction (blue channel)") #define M_NAME_CHANNEL "channel" #define M_TITLE_CHANNEL SANE_I18N("Channel") #define M_DESC_CHANNEL SANE_I18N("Selects the colour band, \"Master\"" \ " means that all colours are affected.") #define M_NAME_MIDTONE "midtone" #define M_TITLE_MIDTONE SANE_I18N("Midtone") #define M_DESC_MIDTONE SANE_I18N("Selects which radiance level should" \ " be considered \"50 % gray\".") #define M_NAME_MIDTONE_R "midtone-r" #define M_TITLE_MIDTONE_R SANE_I18N("Midtone for red") #define M_DESC_MIDTONE_R SANE_I18N("Selects which radiance level should" \ " be considered \"50 % red\".") #define M_NAME_MIDTONE_G "midtone-g" #define M_TITLE_MIDTONE_G SANE_I18N("Midtone for green") #define M_DESC_MIDTONE_G SANE_I18N("Selects which radiance level should" \ " be considered \"50 % green\".") #define M_NAME_MIDTONE_B "midtone-b" #define M_TITLE_MIDTONE_B SANE_I18N("Midtone for blue") #define M_DESC_MIDTONE_B SANE_I18N("Selects which radiance level should" \ " be considered \"50 % blue\".") #define M_NAME_BALANCE_R "balance-r" #define M_TITLE_BALANCE_R SANE_I18N("Red balance") #define M_DESC_BALANCE_R SANE_I18N("Balance factor for red. A value of" \ " 100% means no correction.") #define M_NAME_BALANCE_G "balance-g" #define M_TITLE_BALANCE_G SANE_I18N("Green balance") #define M_DESC_BALANCE_G SANE_I18N("Balance factor for green. A value of"\ " 100% means no correction.") #define M_NAME_BALANCE_B "balance-b" #define M_TITLE_BALANCE_B SANE_I18N("Blue balance") #define M_DESC_BALANCE_B SANE_I18N("Balance factor for blue. A value of" \ " 100% means no correction.") #define M_NAME_BALANCE_FW "balance-fw" #define M_TITLE_BALANCE_FW SANE_I18N("Firmware balance") #define M_DESC_BALANCE_FW SANE_I18N("Sets the color balance values to"\ " the firmware provided values.") /******************************************************************************/ /* Structure that contains global options */ /******************************************************************************/ typedef struct Config_Options { double strip_height; /* inch */ char *no_backtracking; /* enable/disable option for */ /* backtracking */ char *lightlid35; /* enable/disable lightlid35 option */ char *toggle_lamp; /* enable/disable lightlid35 option */ char *backend_calibration; /* calibration by backend */ char *auto_adjust; /* automatically choose threshold */ char *colorbalance_adjust; /* color balance can be modified */ } Config_Options; /******************************************************************************/ /* Structure that is temporarily used, when the config file is parsed */ /******************************************************************************/ typedef struct Config_Temp { struct Config_Temp *next; char *device; /* possible device name */ Config_Options opts; /* options belonging to this device name */ } Config_Temp; /******************************************************************************/ /* scanner hardware info (as discovered by INQUIRY and READ ATTRIBUTES) */ /******************************************************************************/ typedef struct Microtek2_Info { /* from inquiry */ SANE_Byte device_qualifier; #define MI_DEVTYPE_SCANNER 0x06 SANE_Byte device_type; #define MI_SCSI_II_VERSION 0x02 SANE_Byte scsi_version; SANE_Char vendor[INQ_VENDOR_L + 1]; SANE_Char model[INQ_MODEL_L + 1]; SANE_Char revision[INQ_REV_L + 1]; SANE_Byte model_code; /* from read scanner attributes */ SANE_Bool color; #define MI_HAS_ONEPASS SANE_TRUE SANE_Bool onepass; /* the following 3 defines must correspond to byte 31 of SET WINDOW cmd */ #define MI_TYPE_FLATBED 0x01 #define MI_TYPE_SHEEDFEED 0x02 #define MI_TYPE_TRANSPARENCY 0x03 SANE_Byte scanner_type; #define MI_HAS_FEPROM SANE_TRUE SANE_Bool feprom; /* MI_DATAFMT_X must correspond to Byte 0 in READ SCANNER ATTRIBUTE */ #define MI_DATAFMT_CHUNKY 1 #define MI_DATAFMT_LPLCONCAT 2 #define MI_DATAFMT_LPLSEGREG 3 #define MI_DATAFMT_9800 4 #define MI_DATAFMT_WORDCHUNKY 5 SANE_Byte data_format; #define MI_COLSEQ_RED 0 #define MI_COLSEQ_GREEN 1 #define MI_COLSEQ_BLUE 2 #define MI_COLSEQ_ILLEGAL 3 uint8_t color_sequence[RSA_COLORSEQUENCE_L]; SANE_Bool new_image_status; #define MI_DATSEQ_RTOL 1 uint8_t direction; SANE_Byte ccd_gap; SANE_Int max_xresolution; SANE_Int max_yresolution; SANE_Int geo_width; SANE_Int geo_height; SANE_Int opt_resolution; #define MI_HASDEPTH_NIBBLE 1 #define MI_HASDEPTH_10 2 #define MI_HASDEPTH_12 4 #define MI_HASDEPTH_16 8 #define MI_HASDEPTH_14 16 /*This is not in Byte 13 of scanner attributes but in byte 48-bit0*/ SANE_Byte depth; #define MI_LINEART_NONE(d) (((d) & 0x01) == 0) #define MI_HASMODE_LINEART 1 #define MI_HASMODE_HALFTONE 2 #define MI_HASMODE_GRAY 4 #define MI_HASMODE_COLOR 8 SANE_Byte scanmode; SANE_Int ccd_pixels; #define MI_LUTCAP_NONE(d) ((d) == 0) #define MI_LUTCAP_256B 1 #define MI_LUTCAP_1024B 2 #define MI_LUTCAP_1024W 4 #define MI_LUTCAP_4096B 8 #define MI_LUTCAP_4096W 16 #define MI_LUTCAP_64k_W 32 #define MI_LUTCAP_16k_W 64 #define MI_LUTCAP_UNKNOWN 128 SANE_Byte lut_cap; #define MI_HAS_DNLDPTRN SANE_TRUE SANE_Bool has_dnldptrn; SANE_Byte grain_slct; #define MI_OPTDEV_ADF 0x01 #define MI_OPTDEV_TMA 0x02 #define MI_OPTDEV_ADP 0x10 #define MI_OPTDEV_APS 0x20 #define MI_OPTDEV_STRIPE 0x40 #define MI_OPTDEV_SLIDE 0x80 SANE_Byte option_device; SANE_Int calib_white; SANE_Int calib_space; SANE_Byte nlens; SANE_Byte nwindows; SANE_Byte shtrnsferequ; #define MI_WHITE_SHADING_ONLY(x) (((x) & 0x20) == 0) #define MI_HAS_SCNBTTN SANE_TRUE SANE_Bool scnbuttn; #define MI_HAS_PIPOBUF SANE_TRUE SANE_Bool buftype; uint16_t balance[3]; /* balance factor for red, green */ /* and blue data */ uint16_t aps_maxframes; SANE_Int calib_divisor; /* e.g. the X12USL reads and sends */ /* shading at 1/2 * opt_resolution */ } Microtek2_Info; /******************************************************************************/ /* device structure (one for each device in config file) */ /******************************************************************************/ typedef struct Microtek2_Device { struct Microtek2_Device *next; /* next, for linked list */ #define MD_SOURCE_FLATBED 0 #define MD_SOURCE_ADF 1 #define MD_SOURCE_TMA 2 #define MD_SOURCE_SLIDE 3 #define MD_SOURCE_STRIPE 4 Microtek2_Info info[5]; /* detailed scanner spec */ SANE_Device sane; /* SANE generic device block */ char name[PATH_MAX]; /* name from config file */ SANE_Int *custom_gamma_table[4]; /* used for the custom gamma */ /* values before a scan starts */ /* the following two are derived from lut_cap */ int max_lut_size; /* in bytes */ int lut_entry_size; /* word or byte transfer in LUT */ uint8_t scan_source; double revision; /* basically the following two variables should go into the */ /* Microtek2_Scanner structure, but their values must be retained */ /* over several scans, and would be lost, if the application issues */ /* a sane_close. */ uint8_t *shading_table_w; /* shading table white */ uint8_t *shading_table_d; /* shading table dark */ uint8_t shading_table_contents; /* values like ms->mode */ /* the following structure describes the device status */ #define MD_TLAMP_ON 2 #define MD_FLAMP_ON 1 #define MD_NCALIB_ON 4 #define MD_NTRACK_ON 8 #define MD_STICK_ON 16 #define MD_RESERVED17_ON 128 #define MD_CURRENT_MODE_FLATBED 0 struct { uint8_t sskip; uint8_t stick; uint8_t ntrack; uint8_t ncalib; uint8_t tlamp; uint8_t flamp; uint8_t reserved17; uint8_t rdyman; uint8_t trdy; uint8_t frdy; uint8_t adp; uint8_t detect; uint8_t adptime; uint8_t lensstatus; uint8_t aloff; uint8_t timeremain; uint8_t tmacnt; uint8_t paper; uint8_t adfcnt; uint8_t currentmode; uint8_t buttoncount; } status; /* The following defines are related to the model. Some models have */ /* more or less subtle deviations from the spec, which are indicated */ /* by these defines */ uint32_t model_flags; #define MD_NO_SLIDE_MODE 1 /* indicates that it has slide */ /* mode, but it does not */ #define MD_DATA_FORMAT_WRONG 2 /* X6 indicates wrong mode for TMA */ #define MD_NO_ENHANCEMENTS 4 /* image enhancements do not work */ /* (Brightness, contrast, .....) */ #define MD_RII_TWO_BYTES 8 /* return some image information */ /* in two byte */ #define MD_NO_GAMMA 16 /* if device does not accept */ /* gamma tables */ #define MD_PHANTOM336CX_TYPE_SHADING 32 /* Phantom 336cx type shading */ #define MD_READ_CONTROL_BIT 64 /* uses "read control bit" */ #define MD_PHANTOM_C6 128 /* It is a Phantom C6 */ #define MD_OFFSET_2 256 /* Image data starts 2 bytes */ /* from the beginning of a */ /* scanline */ #define MD_X6_SHORT_TRANSFER 512 /* X6 USB crashes if you read too much */ #define MD_NO_RIS_COMMAND 1024 /* doesn't like read_image_status */ #define MD_16BIT_TRANSFER 2048 /* transfers 10/12/14bit scans as */ /* 16bit data */ #define MD_CALIB_DIVISOR_600 4096 /* uses mi->calib_divisor=2 below 600dpi */ size_t n_control_bytes; /* for read_control_bits; the */ /* number is model dependent */ /* and can not be inquired */ uint32_t shading_length; /* length of the shading image */ /* Phantom 336cx, C6, ... */ uint8_t shading_depth; /* bit depth of shading image */ uint8_t controlbit_offset; /* first relevant control bit */ #define MD_MODESTRING_NUMS 4 #define MD_MODESTRING_COLOR SANE_VALUE_SCAN_MODE_COLOR #define MD_MODESTRING_GRAY SANE_VALUE_SCAN_MODE_GRAY #define MD_MODESTRING_HALFTONE SANE_VALUE_SCAN_MODE_HALFTONE #define MD_MODESTRING_LINEART SANE_VALUE_SCAN_MODE_LINEART SANE_String_Const scanmode_list[MD_MODESTRING_NUMS + 1]; #define MD_DEPTHVAL_NUMS 6 #define MD_DEPTHVAL_16 16 #define MD_DEPTHVAL_14 14 #define MD_DEPTHVAL_12 12 #define MD_DEPTHVAL_10 10 #define MD_DEPTHVAL_8 8 #define MD_DEPTHVAL_4 4 SANE_Int bitdepth_list[MD_DEPTHVAL_NUMS + 1]; #define MD_SOURCESTRING_NUMS 5 #define MD_SOURCESTRING_FLATBED "Flatbed" #define MD_SOURCESTRING_ADF "ADF" #define MD_SOURCESTRING_TMA "TMA" #define MD_SOURCESTRING_STRIPE "Filmstrip" #define MD_SOURCESTRING_SLIDE "Slide" SANE_String_Const scansource_list[MD_SOURCESTRING_NUMS + 1]; #define MD_HALFTONE_NUMS 12 #define MD_HALFTONE0 "53-dot screen (53 gray levels)" #define MD_HALFTONE1 "Horiz. screen (65 gray levels)" #define MD_HALFTONE2 "Vert. screen (65 gray levels)" #define MD_HALFTONE3 "Mixed page (33 gray levels)" #define MD_HALFTONE4 "71-dot screen (29 gray levels)" #define MD_HALFTONE5 "60-dot #1 (26 gray levels)" #define MD_HALFTONE6 "60-dot #2 (26 gray levels)" #define MD_HALFTONE7 "Fine detail #1 (17 gray levels)" #define MD_HALFTONE8 "Fine detail #2 (17 gray levels)" #define MD_HALFTONE9 "Slant line (17 gray levels)" #define MD_HALFTONE10 "Posterizing (10 gray levels)" #define MD_HALFTONE11 "High Contrast (5 gray levels)" SANE_String_Const halftone_mode_list[MD_HALFTONE_NUMS + 1]; #define MD_CHANNEL_NUMS 4 #define MD_CHANNEL_MASTER "Master" #define MD_CHANNEL_RED "Red" #define MD_CHANNEL_GREEN "Green" #define MD_CHANNEL_BLUE "Blue" SANE_String_Const channel_list[MD_CHANNEL_NUMS + 1]; #define MD_GAMMAMODE_NUMS 3 #define MD_GAMMAMODE_LINEAR "None" #define MD_GAMMAMODE_SCALAR "Scalar" #define MD_GAMMAMODE_CUSTOM "Custom" SANE_String_Const gammamode_list[MD_GAMMAMODE_NUMS + 1]; SANE_Range x_res_range_dpi; /* X resolution in dpi */ SANE_Range y_res_range_dpi; /* Y resolution in dpi */ SANE_Range x_range_mm; /* scan width in mm */ SANE_Range y_range_mm; /* scan height in mm */ SANE_Range percentage_range; /* for brightness, shadow, ... */ SANE_Range custom_gamma_range; /* for custom gamma values */ SANE_Range scalar_gamma_range; /* for scalar gamma values */ SANE_Range shadow_range; /* shadow of blue channel */ SANE_Range midtone_range; /* midtone shadow of blue channel */ SANE_Range exposure_range; /* for lengthening exposure time */ SANE_Range highlight_range; /* highlight of master channel */ SANE_Range threshold_range; /* 1 - 255 */ SANE_Range balance_range; /* for user provided color balance */ Config_Options opts; /* options from the config file */ SANE_Word opt_backend_calib_default; /* corresponds to scanner model */ SANE_Word opt_no_backtrack_default; /* corresponds to scanner model */ } Microtek2_Device; /******************************************************************************/ /* scanner structure (one for each device in use) */ /* ....all the state needed to define a scan request */ /******************************************************************************/ typedef struct Microtek2_Scanner { struct Microtek2_Scanner *next; /* for linked list */ Microtek2_Device *dev; /* raw device info */ Option_Value val[NUM_OPTIONS + 1]; /* option values for session */ SANE_Parameters params; /* format, lastframe, lines, depth, ppl, bpl */ SANE_Option_Descriptor sod[NUM_OPTIONS + 1]; /* option list for session */ uint8_t *gamma_table; uint8_t *shading_image; /* used for shading image */ uint8_t *condensed_shading_w; /* used when a model uses "read */ uint8_t *condensed_shading_d; /* control bit", stores the relevant */ /* shading pixels for each color */ uint8_t *temporary_buffer; /* used when automatic adjustment */ /* is selected */ char *gamma_mode; /* none, linear or custom */ /* the following defines must correspond to byte 25 of SET WINDOW body */ #define MS_MODE_LINEART 0x00 #define MS_MODE_HALFTONE 0x01 #define MS_MODE_GRAY 0x02 #define MS_MODE_LINEARTFAKE 0x12 /* no real mode */ #define MS_MODE_COLOR 0x05 /* the following defines must correspond to byte 31 of SET WINDOW body */ #define MS_SOURCE_FLATBED 0x00 #define MS_SOURCE_ADF 0x01 #define MS_SOURCE_TMA 0x02 #define MS_SOURCE_STRIPE 0x05 #define MS_SOURCE_SLIDE 0x06 SANE_Int mode; SANE_Int depth; SANE_Int x_resolution_dpi; SANE_Int y_resolution_dpi; SANE_Int x1_dots; /* x-position in units of optical resolution */ SANE_Int y1_dots; /* same for y-position */ SANE_Int width_dots; /* scan width in units of optical resolution */ SANE_Int height_dots; /* same for height */ uint8_t brightness_m; uint8_t contrast_m; uint8_t exposure_m; uint8_t shadow_m; uint8_t midtone_m; uint8_t highlight_m; uint8_t brightness_r; uint8_t contrast_r; uint8_t exposure_r; uint8_t shadow_r; uint8_t midtone_r; uint8_t highlight_r; uint8_t brightness_g; uint8_t contrast_g; uint8_t exposure_g; uint8_t shadow_g; uint8_t midtone_g; uint8_t highlight_g; uint8_t brightness_b; uint8_t contrast_b; uint8_t exposure_b; uint8_t shadow_b; uint8_t midtone_b; uint8_t highlight_b; uint8_t threshold; SANE_Bool use_external_ht; SANE_Byte internal_ht_index; uint8_t stay; uint8_t rawdat; SANE_Bool quality; SANE_Bool fastscan; SANE_Byte scan_source; uint8_t no_backtracking; uint8_t lightlid35; uint8_t auto_adjust; uint8_t calib_backend; uint8_t colorbalance_adjust; int current_pass; /* current pass if 3-pass scan */ int lut_size; /* size of gamma lookup table */ int lut_entry_size; /* size of one entry in lookup table */ uint32_t lut_size_bytes; /* size of LUT in bytes */ uint8_t word; /* word transfer, used in some read cmds */ /* MS_COLOR_X must correspond to color field in READ IMAGE STATUS */ #define MS_COLOR_RED 0 #define MS_COLOR_GREEN 1 #define MS_COLOR_BLUE 2 #define MS_COLOR_ALL 3 uint8_t current_color; /* for gamma calc. and 3-pass scanners */ uint8_t current_read_color; /* dto, for RI and RIS */ uint8_t dark; /* is 1 for reading dark shading */ uint32_t ppl; /* pixels per line as returned by RII */ uint32_t bpl; /* bytes per line as returned by RII */ uint32_t remaining_bytes; /* remaining bytes as returned by RII */ uint32_t real_remaining_bytes;/* bytes to transfer to the frontend */ uint32_t real_bpl; /* bpl to transfer to the frontend */ SANE_Int src_remaining_lines; /* remaining lines to read */ SANE_Int src_lines_to_read; /* actual number of lines read */ SANE_Int src_max_lines; /* maximum number of lines that fit */ /* into the scsi buffer */ /* sent to the frontend */ int bits_per_pixel_in; /* bits per pixel transferred from scanner */ int bits_per_pixel_out; /* bits per pixel transf. to frontend */ uint32_t src_buffer_size; /* size of the buffer */ int transfer_length; /* transfer length for RI command */ uint8_t balance[3]; /* user provided balance factor for */ /* red, green and blue data */ struct { uint8_t *src_buffer[2]; /* two buffers because of CCD gap */ uint8_t *src_buf; int current_src; SANE_Int free_lines; SANE_Int free_max_lines; uint8_t *current_pos[3]; /* actual position in the source buffer */ int planes[2][3]; /* # of red, green, blue planes in the */ /* current source buffer and leftover */ /* planes from previous "read image" */ } buf; SANE_Bool onepass; size_t n_control_bytes; /* for READ CONTROL BITS */ uint8_t *control_bytes; /* pointer to the result */ int scanning; /* true == between sane_start & sane_read=EOF */ int cancelled; int sfd; /* SCSI filedescriptor */ int fd[2]; /* file descriptors for pipe */ SANE_Pid pid; /* pid of child process */ FILE *fp; } Microtek2_Scanner; /******************************************************************************/ /* Function prototypes */ /******************************************************************************/ static SANE_Status add_device_list(SANE_String_Const, Microtek2_Device **); static SANE_Status attach(Microtek2_Device *); static SANE_Status attach_one (const char *); static SANE_Status auto_adjust_proc_data (Microtek2_Scanner *, uint8_t **); static SANE_Status calc_cx_shading_line(Microtek2_Scanner *); /* (KF) new */ static SANE_Status calculate_gamma(Microtek2_Scanner *, uint8_t *, int, char *); static SANE_Status calculate_sane_params(Microtek2_Scanner *); static SANE_Status cancel_scan(Microtek2_Scanner *); static SANE_Status check_inquiry(Microtek2_Device *, SANE_String *); static void check_option(const char *, Config_Options *); static SANE_Status chunky_copy_pixels(Microtek2_Scanner *, uint8_t *); static SANE_Status chunky_proc_data(Microtek2_Scanner *); #if 0 static void chunky_set_exposure(uint8_t *, uint32_t, uint8_t, uint8_t, int); #endif static void cleanup_scanner(Microtek2_Scanner *); static SANE_Status condense_shading(Microtek2_Scanner *); #ifdef HAVE_AUTHORIZATION static SANE_Status do_authorization(char *); #endif static SANE_Status read_shading_image(Microtek2_Scanner *); static SANE_Status dump_area(uint8_t *, int, char *); static SANE_Status dump_area2(uint8_t *, int, char *); /* currently not used */ #if 0 static SANE_Status dump_to_file(uint8_t *, int, char *, char *); #endif static SANE_Status dump_attributes(Microtek2_Info *); static void get_calib_params(Microtek2_Scanner *); static SANE_Status get_cshading_values(Microtek2_Scanner *,uint8_t, uint32_t, float, int, float *, float *); /* (KF) new */ static SANE_Status get_scan_mode_and_depth(Microtek2_Scanner *, int *, SANE_Int *, int *, int *); static SANE_Status get_scan_parameters(Microtek2_Scanner *); static SANE_Status get_lut_size(Microtek2_Info *, int *, int *); static SANE_Status gray_copy_pixels(Microtek2_Scanner *ms, uint8_t *, int, int); static SANE_Status gray_proc_data(Microtek2_Scanner *); #if 0 static void gray_set_exposure(uint8_t *, uint32_t, uint8_t, uint8_t); #endif static SANE_Status init_options(Microtek2_Scanner *, uint8_t); static SANE_Status lineartfake_copy_pixels(Microtek2_Scanner *, uint8_t *, uint32_t, uint8_t, int, FILE *); static SANE_Status lineartfake_proc_data(Microtek2_Scanner *); static SANE_Status lplconcat_copy_pixels(Microtek2_Scanner *, uint8_t **, int, int); static SANE_Status lplconcat_proc_data(Microtek2_Scanner *ms); static size_t max_string_size (const SANE_String_Const * /* strings[] */); static void parse_config_file(FILE *, Config_Temp **); static SANE_Status prepare_buffers(Microtek2_Scanner *); static SANE_Status prepare_shading_data(Microtek2_Scanner *, uint32_t, uint8_t **); static SANE_Status proc_onebit_data(Microtek2_Scanner *); static SANE_Status read_cx_shading_image(Microtek2_Scanner *); static SANE_Status read_cx_shading(Microtek2_Scanner *); static int reader_process(void *); static SANE_Status restore_gamma_options(SANE_Option_Descriptor *, Option_Value *); static SANE_Status segreg_copy_pixels(Microtek2_Scanner *); static SANE_Status segreg_proc_data(Microtek2_Scanner *ms); static void set_exposure(Microtek2_Scanner *); static SANE_Status set_option_dependencies(Microtek2_Scanner *, SANE_Option_Descriptor *, Option_Value *); static SANE_Status shading_function(Microtek2_Scanner *, uint8_t *); static void signal_handler (int); static SANE_Status wordchunky_copy_pixels(uint8_t *, uint32_t, int, FILE *); static SANE_Status wordchunky_proc_data(Microtek2_Scanner *); typedef int (*qsortfunc)(const void *, const void *); static int compare_func_16(const uint16_t *p1, uint16_t *p2) { return ( *p1 - *p2 ); } /******************************************************************************/ /* Function prototypes for basic SCSI commands */ /******************************************************************************/ static SANE_Status scsi_inquiry(Microtek2_Info *, char *); static SANE_Status scsi_read_attributes(Microtek2_Info *, char *, uint8_t); static SANE_Status scsi_read_control_bits(Microtek2_Scanner *); /* currently not used */ /* static SANE_Status scsi_read_gamma(Microtek2_Scanner *); */ static SANE_Status scsi_read_image(Microtek2_Scanner *, uint8_t *, int); static SANE_Status scsi_read_image_info(Microtek2_Scanner *); static SANE_Status scsi_read_image_status(Microtek2_Scanner *); static SANE_Status scsi_read_system_status(Microtek2_Device *, int); /* currently not used */ /* static SANE_Status scsi_request_sense(Microtek2_Scanner *); */ static SANE_Status scsi_send_gamma(Microtek2_Scanner *); static SANE_Status scsi_send_shading(Microtek2_Scanner *, uint8_t *, uint32_t, uint8_t); static SANE_Status scsi_read_shading(Microtek2_Scanner *, uint8_t *, uint32_t); static SANE_Status scsi_send_system_status(Microtek2_Device *, int); static SANE_Status scsi_sense_handler (int, u_char *, void *); static SANE_Status scsi_test_unit_ready(Microtek2_Device *); static SANE_Status scsi_set_window(Microtek2_Scanner *, int); static SANE_Status scsi_wait_for_image(Microtek2_Scanner *); #endif backends-1.3.0/backend/mustek.c000066400000000000000000006117531456256263500163630ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1996, 1997 David Mosberger-Tang and Andreas Czechanowski, 1998 Andreas Bolsch for extension to ScanExpress models version 0.6, 2000-2005 Henning Meier-Geinitz, 2003 James Perry (600 EP). 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, see . As a special exception, the authors of SANE give permission for additional uses of the libraries contained in this release of SANE. The exception is that, if you link a SANE library with other files to produce an executable, this does not by itself cause the resulting executable to be covered by the GNU General Public License. Your use of that executable is in no way restricted on account of linking the SANE library code into it. This exception does not, however, invalidate any other reasons why the executable file might be covered by the GNU General Public License. If you submit changes to SANE to the maintainers to be included in a subsequent release, you agree by submitting the changes that those changes may be distributed with this exception intact. If you write modifications of your own for SANE, it is your choice whether to permit this exception to apply to your modifications. If you do not wish that, delete this exception notice. This file implements a SANE backend for Mustek and some Trust flatbed scanners with SCSI, parallel port (600 EP) or proprietary interface. */ /**************************************************************************/ /* Mustek backend version */ #define BUILD 138 /**************************************************************************/ #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include #include "../include/_stdint.h" #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_scsi.h" #include "../include/sane/sanei_ab306.h" #include "../include/sane/sanei_thread.h" #define BACKEND_NAME mustek #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_config.h" #include "mustek.h" #include "mustek_scsi_pp.h" #ifndef SANE_I18N #define SANE_I18N(text) text #endif /* Debug level from sanei_init_debug */ static SANE_Int debug_level; /* Maximum # of inches to scan in one swoop. 0 means "unlimited." This is here to be nice on the SCSI bus---Mustek scanners don't disconnect while scanning is in progress, which confuses some drivers that expect no reasonable SCSI request would take more than 10 seconds. That's not really true for Mustek scanners operating in certain modes, hence this limit. Usually you don't need to set it. */ static double strip_height; /* Should we wait for the scan slider to return after each scan? */ static SANE_Bool force_wait; /* Should we disable double buffering when reading data from the scanner? */ static SANE_Bool disable_double_buffering; static SANE_Int num_devices; static Mustek_Device *first_dev; static Mustek_Scanner *first_handle; static const SANE_Device **devlist = 0; /* Array of newly attached devices */ static Mustek_Device **new_dev; /* Length of new_dev array */ static SANE_Int new_dev_len; /* Number of entries allocated for new_dev */ static SANE_Int new_dev_alloced; static SANE_Int lamp_off_time = 60; /* Used for line-distance correction: */ static const SANE_Int color_seq[] = { 1, 2, 0 /* green, blue, red */ }; /* Which modes are supported? */ static SANE_String_Const mode_list_paragon[] = { SANE_VALUE_SCAN_MODE_LINEART, SANE_VALUE_SCAN_MODE_HALFTONE, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_COLOR, 0 }; static SANE_String_Const mode_list_se[] = { SANE_VALUE_SCAN_MODE_LINEART, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_COLOR, 0 }; static SANE_String_Const bit_depth_list_pro[] = { "8", "12", 0 }; /* Some scanners support setting speed manually */ static SANE_String_Const speed_list[] = { SANE_I18N ("Slowest"), SANE_I18N ("Slower"), SANE_I18N ("Normal"), SANE_I18N ("Faster"), SANE_I18N ("Fastest"), 0 }; /* Which scan-sources are supported? */ static const SANE_String_Const source_list[] = { SANE_I18N ("Flatbed"), 0 }; static SANE_String_Const adf_source_list[] = { SANE_I18N ("Flatbed"), SANE_I18N ("Automatic Document Feeder"), 0 }; static SANE_String_Const ta_source_list[] = { SANE_I18N ("Flatbed"), SANE_I18N ("Transparency Adapter"), 0 }; /* Range used for gamma and halftone pattern */ static const SANE_Range u8_range = { 0, /* minimum */ 255, /* maximum */ 0 /* quantization */ }; /* Which kind of halftone patterns are available? */ static SANE_String_Const halftone_list[] = { SANE_I18N ("8x8 coarse"), SANE_I18N ("8x8 normal"), SANE_I18N ("8x8 fine"), SANE_I18N ("8x8 very fine"), SANE_I18N ("6x6 normal"), SANE_I18N ("5x5 coarse"), SANE_I18N ("5x5 fine"), SANE_I18N ("4x4 coarse"), SANE_I18N ("4x4 normal"), SANE_I18N ("4x4 fine"), SANE_I18N ("3x3 normal"), SANE_I18N ("2x2 normal"), SANE_I18N ("8x8 custom"), SANE_I18N ("6x6 custom"), SANE_I18N ("5x5 custom"), SANE_I18N ("4x4 custom"), SANE_I18N ("3x3 custom"), SANE_I18N ("2x2 custom"), 0 }; /* Range used for brightness and contrast */ static const SANE_Range percentage_range = { SANE_FIX(-100), /* minimum */ SANE_FIX(100), /* maximum */ SANE_FIX(1) /* quantization */ }; /* SCSI command buffers used by the backend */ static const SANE_Byte scsi_inquiry[] = { MUSTEK_SCSI_INQUIRY, 0x00, 0x00, 0x00, INQ_LEN, 0x00 }; static const SANE_Byte scsi_test_unit_ready[] = { MUSTEK_SCSI_TEST_UNIT_READY, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const SANE_Byte scsi_request_sense[] = { MUSTEK_SCSI_REQUEST_SENSE, 0x00, 0x00, 0x00, 0x04, 0x00 }; static const SANE_Byte scsi_start_stop[] = { MUSTEK_SCSI_START_STOP, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const SANE_Byte scsi_ccd_distance[] = { MUSTEK_SCSI_CCD_DISTANCE, 0x00, 0x00, 0x00, 0x05, 0x00 }; static const SANE_Byte scsi_get_image_status[] = { MUSTEK_SCSI_GET_IMAGE_STATUS, 0x00, 0x00, 0x00, 0x06, 0x00 }; static const SANE_Byte scsi_get_window[] = { MUSTEK_SCSI_GET_WINDOW, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00 }; /* Sizes for fixed-length, non-vendor-specific CDB formats (others are 0) */ #define CDB_SIZE(opcode) ((opcode) < 0x20 ? 6 : \ (opcode) < 0x60 ? 10 : \ (opcode) < 0x80 ? 0 : \ (opcode) < 0xa0 ? 16 : \ (opcode) < 0xc0 ? 12 : 0) /* prototypes */ static SANE_Status area_and_windows (Mustek_Scanner * s); static SANE_Status inquiry (Mustek_Scanner * s); /* Test if this machine is little endian (from coolscan.c) */ static SANE_Bool little_endian (void) { SANE_Int testvalue = 255; uint8_t *firstbyte = (uint8_t *) & testvalue; if (*firstbyte == 255) return SANE_TRUE; return SANE_FALSE; } /* Used for Pro series. First value seems to be always 85, second one varies. First bit of second value clear == device ready (?) */ static SANE_Status scsi_sense_wait_ready (Mustek_Scanner * s) { struct timeval now, start; SANE_Status status; size_t len; SANE_Byte sense_buffer[4]; SANE_Byte bytetxt[300], dbgtxt[300], *pp; gettimeofday (&start, 0); while (1) { len = sizeof (sense_buffer); DBG (5, "scsi_sense_wait_ready: command size = %ld, sense size = %ld\n", (long int) sizeof (scsi_request_sense), (long int) len); status = sanei_scsi_cmd (s->fd, scsi_request_sense, sizeof (scsi_request_sense), sense_buffer, &len); if (status != SANE_STATUS_GOOD) { DBG (1, "scsi_sense_wait_ready: failed: %s\n", sane_strstatus (status)); return status; } dbgtxt[0] = '\0'; for (pp = sense_buffer; pp < (sense_buffer + 4); pp++) { sprintf ((SANE_String) bytetxt, " %02x", *pp); strcat ((SANE_String) dbgtxt, (SANE_String) bytetxt); } DBG (5, "scsi_sense_wait_ready: sensebuffer: %s\n", dbgtxt); if (!(sense_buffer[1] & 0x01)) { DBG (4, "scsi_sense_wait_ready: ok\n"); return SANE_STATUS_GOOD; } else { gettimeofday (&now, 0); if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME) { DBG (1, "scsi_sense_wait_ready: timed out after %lu seconds\n", (u_long) (now.tv_sec - start.tv_sec)); return SANE_STATUS_INVAL; } usleep (100000); /* retry after 100ms */ } } return SANE_STATUS_INVAL; } /* Used for 3pass series */ static SANE_Status scsi_area_wait_ready (Mustek_Scanner * s) { struct timeval now, start; SANE_Status status; gettimeofday (&start, 0); DBG (5, "scsi_area_wait_ready\n"); while (1) { status = area_and_windows (s); switch (status) { default: /* Ignore errors while waiting for scanner to become ready. Some SCSI drivers return EIO while the scanner is returning to the home position. */ DBG (3, "scsi_area_wait_ready: failed (%s)\n", sane_strstatus (status)); /* fall through */ case SANE_STATUS_DEVICE_BUSY: gettimeofday (&now, 0); if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME) { DBG (1, "scsi_area_wait_ready: timed out after %lu seconds\n", (u_long) (now.tv_sec - start.tv_sec)); return SANE_STATUS_INVAL; } usleep (100000); /* retry after 100ms */ break; case SANE_STATUS_GOOD: return status; } } return SANE_STATUS_INVAL; } static SANE_Status scsi_unit_wait_ready (Mustek_Scanner * s) { struct timeval now, start; SANE_Status status; gettimeofday (&start, 0); while (1) { DBG (5, "scsi_unit_wait_ready: sending TEST_UNIT_READY\n"); status = sanei_scsi_cmd (s->fd, scsi_test_unit_ready, sizeof (scsi_test_unit_ready), 0, 0); DBG (5, "scsi_unit_wait_ready: TEST_UNIT_READY finished\n"); switch (status) { default: /* Ignore errors while waiting for scanner to become ready. Some SCSI drivers return EIO while the scanner is returning to the home position. */ DBG (3, "scsi_unit_wait_ready: test unit ready failed (%s)\n", sane_strstatus (status)); /* fall through */ case SANE_STATUS_DEVICE_BUSY: gettimeofday (&now, 0); if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME) { DBG (1, "scsi_unit_wait_ready: timed out after %lu seconds\n", (u_long) (now.tv_sec - start.tv_sec)); return SANE_STATUS_INVAL; } usleep (100000); /* retry after 100ms */ break; case SANE_STATUS_GOOD: return status; } } return SANE_STATUS_INVAL; } static SANE_Status scsi_inquiry_wait_ready (Mustek_Scanner * s) { struct timeval now, start; SANE_Status status; gettimeofday (&start, 0); while (1) { DBG (5, "scsi_inquiry_wait_ready: sending INQUIRY\n"); status = inquiry (s); DBG (5, "scsi_inquiry_wait_ready: INQUIRY finished\n"); switch (status) { default: /* Ignore errors while waiting for scanner to become ready. Some SCSI drivers return EIO while the scanner is returning to the home position. */ DBG (3, "scsi_unit_wait_ready: inquiry failed (%s)\n", sane_strstatus (status)); /* fall through */ case SANE_STATUS_DEVICE_BUSY: gettimeofday (&now, 0); if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME) { DBG (1, "scsi_unit_wait_ready: timed out after %lu seconds\n", (u_long) (now.tv_sec - start.tv_sec)); return SANE_STATUS_INVAL; } usleep (500000); /* retry after 500ms */ break; case SANE_STATUS_GOOD: return status; } } return SANE_STATUS_INVAL; } static SANE_Status n_wait_ready (Mustek_Scanner * s) { struct timeval now, start; SANE_Status status; gettimeofday (&start, 0); DBG (5, "n_wait_ready\n"); while (1) { status = sanei_ab306_test_ready (s->fd); if (status == SANE_STATUS_GOOD) return SANE_STATUS_GOOD; gettimeofday (&now, 0); if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME) { DBG (1, "n_wait_ready: timed out after %lu seconds\n", (u_long) (now.tv_sec - start.tv_sec)); return SANE_STATUS_INVAL; } usleep (100000); /* retry after 100ms */ } } static SANE_Status scsi_pp_wait_ready (Mustek_Scanner * s) { struct timeval now, start; SANE_Status status; gettimeofday (&start, 0); DBG (5, "scsi_pp_wait_ready\n"); while (1) { status = mustek_scsi_pp_test_ready (s->fd); if (status == SANE_STATUS_GOOD) return SANE_STATUS_GOOD; gettimeofday (&now, 0); if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME) { DBG (1, "scsi_pp_wait_ready: timed out after %lu seconds\n", (u_long) (now.tv_sec - start.tv_sec)); return SANE_STATUS_INVAL; } usleep (100000); /* retry after 100ms */ } } static SANE_Status dev_wait_ready (Mustek_Scanner * s) { if (s->hw->flags & MUSTEK_FLAG_N) return n_wait_ready (s); else if (s->hw->flags & MUSTEK_FLAG_SCSI_PP) return scsi_pp_wait_ready (s); else if (s->hw->flags & MUSTEK_FLAG_THREE_PASS) { SANE_Status status; /* some 3-pass scanners seem to need the inquiry wait, too */ status = scsi_area_wait_ready (s); if (status != SANE_STATUS_GOOD) return status; return scsi_inquiry_wait_ready (s); } else if ((s->hw->flags & MUSTEK_FLAG_PARAGON_1) || (s->hw->flags & MUSTEK_FLAG_PARAGON_2)) return scsi_inquiry_wait_ready (s); else if (s->hw->flags & MUSTEK_FLAG_PRO) return scsi_sense_wait_ready (s); else return scsi_unit_wait_ready (s); } static SANE_Status dev_open (SANE_String_Const devname, Mustek_Scanner * s, SANEI_SCSI_Sense_Handler handler) { SANE_Status status; DBG (5, "dev_open %s\n", devname); #ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED s->hw->buffer_size = s->hw->max_buffer_size; status = sanei_scsi_open_extended (devname, &s->fd, handler, 0, &s->hw->buffer_size); #else s->hw->buffer_size = MIN (sanei_scsi_max_request_size, s->hw->max_buffer_size); status = sanei_scsi_open (devname, &s->fd, handler, 0); #endif if (status == SANE_STATUS_GOOD) { DBG (3, "dev_open: %s is a SCSI device\n", devname); DBG (4, "dev_open: wanted %d kbytes, got %d kbytes buffer\n", s->hw->max_buffer_size / 1024, s->hw->buffer_size / 1024); if (s->hw->buffer_size < 4096) { DBG (1, "dev_open: sanei_scsi_open buffer too small\n"); sanei_scsi_close (s->fd); return SANE_STATUS_NO_MEM; } } else { DBG (3, "dev_open: %s: can't open %s as a SCSI device\n", sane_strstatus (status), devname); status = sanei_ab306_open (devname, &s->fd); if (status == SANE_STATUS_GOOD) { s->hw->flags |= MUSTEK_FLAG_N; DBG (3, "dev_open: %s is an AB306N device\n", devname); } else { DBG (3, "dev_open: %s: can't open %s as an AB306N device\n", sane_strstatus (status), devname); status = mustek_scsi_pp_open (devname, &s->fd); if (status == SANE_STATUS_GOOD) { s->hw->flags |= MUSTEK_FLAG_SCSI_PP; DBG (3, "dev_open: %s is a SCSI-over-parallel device\n", devname); } else { DBG (3, "dev_open: %s: can't open %s as a SCSI-over-parallel device\n", sane_strstatus (status), devname); DBG (1, "dev_open: can't open %s\n", devname); return SANE_STATUS_INVAL; } } } return SANE_STATUS_GOOD; } static SANE_Status dev_cmd (Mustek_Scanner * s, const void *src, size_t src_size, void *dst, size_t * dst_size) { SANE_Status status; SANE_Byte cmd_byte_list[50]; SANE_Byte cmd_byte[5]; const SANE_Byte *pp; DBG (5, "dev_cmd: fd=%d, src=%p, src_size=%ld, dst=%p, dst_size=%ld\n", s->fd, src, (long int) src_size, dst, (long int) (dst_size ? *dst_size : 0)); if (src && (debug_level >= 5)) /* output data sent to SCSI device */ { cmd_byte_list[0] = '\0'; for (pp = (const SANE_Byte *) src; pp < (((const SANE_Byte *) src) + src_size); pp++) { sprintf ((SANE_String) cmd_byte, " %02x", *pp); strcat ((SANE_String) cmd_byte_list, (SANE_String) cmd_byte); if (((pp - (const SANE_Byte *) src) % 0x10 == 0x0f) || (pp >= (((const SANE_Byte *) src) + src_size - 1))) { DBG (5, "dev_cmd: sending: %s\n", cmd_byte_list); cmd_byte_list[0] = '\0'; } } } if (s->hw->flags & MUSTEK_FLAG_N) status = sanei_ab306_cmd (s->fd, src, src_size, dst, dst_size); else if (s->hw->flags & MUSTEK_FLAG_SCSI_PP) status = mustek_scsi_pp_cmd (s->fd, src, src_size, dst, dst_size); else status = sanei_scsi_cmd (s->fd, src, src_size, dst, dst_size); if (dst && dst_size && (debug_level >= 5)) /* output data received from SCSI device */ { cmd_byte_list[0] = '\0'; for (pp = (const SANE_Byte *) dst; pp < (((const SANE_Byte *) dst) + *dst_size); pp++) { sprintf ((SANE_String) cmd_byte, " %02x", *pp); strcat ((SANE_String) cmd_byte_list, (SANE_String) cmd_byte); if (((pp - (const SANE_Byte *) dst) % 0x10 == 0x0f) || (pp >= (((const SANE_Byte *) dst) + *dst_size - 1))) { DBG (5, "dev_cmd: receiving: %s\n", cmd_byte_list); cmd_byte_list[0] = '\0'; } } } DBG (5, "dev_cmd: finished: dst_size=%ld, status=%s\n", (long int) (dst_size ? *dst_size : 0), sane_strstatus (status)); return status; } static SANE_Status dev_req_wait (void *id) { if (!id) return SANE_STATUS_GOOD; else return sanei_scsi_req_wait (id); } static SANE_Status dev_block_read_start (Mustek_Scanner * s, SANE_Int lines) { DBG (4, "dev_block_read_start: entering block for %d lines\n", lines); if (s->hw->flags & MUSTEK_FLAG_N) { SANE_Byte readlines[6]; memset (readlines, 0, sizeof (readlines)); readlines[0] = MUSTEK_SCSI_READ_SCANNED_DATA; readlines[2] = (lines >> 16) & 0xff; readlines[3] = (lines >> 8) & 0xff; readlines[4] = (lines >> 0) & 0xff; return sanei_ab306_cmd (s->fd, readlines, sizeof (readlines), 0, 0); } else if (s->hw->flags & MUSTEK_FLAG_SCSI_PP) { SANE_Byte readlines[6]; memset (readlines, 0, sizeof (readlines)); readlines[0] = MUSTEK_SCSI_READ_SCANNED_DATA; readlines[2] = (lines >> 16) & 0xff; readlines[3] = (lines >> 8) & 0xff; readlines[4] = (lines >> 0) & 0xff; return mustek_scsi_pp_cmd (s->fd, readlines, sizeof (readlines), 0, 0); } else if (s->hw->flags & MUSTEK_FLAG_PARAGON_2) { SANE_Byte buffer[6]; size_t len; SANE_Int color; SANE_Status status; /* reset line-distance values */ if (s->mode & MUSTEK_MODE_COLOR) { for (color = 0; color < 3; ++color) { s->ld.index[color] = -s->ld.dist[color]; } s->ld.lmod3 = -1; s->ld.ld_line = 0; } /* Get image status (necessary to start new block) */ len = sizeof (buffer); status = dev_cmd (s, scsi_get_image_status, sizeof (scsi_get_image_status), buffer, &len); if (status != SANE_STATUS_GOOD) return status; /* tell scanner how many lines to scan in one block */ memset (buffer, 0, sizeof (buffer)); buffer[0] = MUSTEK_SCSI_READ_SCANNED_DATA; buffer[2] = (lines >> 16) & 0xff; buffer[3] = (lines >> 8) & 0xff; buffer[4] = (lines >> 0) & 0xff; buffer[5] = 0x04; return sanei_scsi_cmd (s->fd, buffer, sizeof (buffer), 0, 0); } else return SANE_STATUS_GOOD; } static SANE_Status dev_read_req_enter (Mustek_Scanner * s, SANE_Byte * buf, SANE_Int lines, SANE_Int bpl, size_t * lenp, void **idp, SANE_Int bank, SANE_Byte * command) { *lenp = lines * bpl; if (s->hw->flags & MUSTEK_FLAG_N) { SANE_Int planes; *idp = 0; planes = (s->mode & MUSTEK_MODE_COLOR) ? 3 : 1; return sanei_ab306_rdata (s->fd, planes, buf, lines, bpl); } else if (s->hw->flags & MUSTEK_FLAG_SCSI_PP) { SANE_Int planes; *idp = 0; planes = (s->mode & MUSTEK_MODE_COLOR) ? 3 : 1; return mustek_scsi_pp_rdata (s->fd, planes, buf, lines, bpl); } else { if (s->hw->flags & MUSTEK_FLAG_SE) { if (s->mode & MUSTEK_MODE_COLOR) lines *= 3; memset (command, 0, 10); command[0] = MUSTEK_SCSI_READ_DATA; command[6] = bank; /* buffer bank not used ??? */ command[7] = (lines >> 8) & 0xff; command[8] = (lines >> 0) & 0xff; return sanei_scsi_req_enter (s->fd, command, 10, buf, lenp, idp); } else if (s->hw->flags & MUSTEK_FLAG_PRO) { memset (command, 0, 6); command[0] = MUSTEK_SCSI_READ_SCANNED_DATA; command[2] = ((lines * bpl) >> 16) & 0xff; command[3] = ((lines * bpl) >> 8) & 0xff; command[4] = ((lines * bpl) >> 0) & 0xff; return sanei_scsi_req_enter (s->fd, command, 6, buf, lenp, idp); } else /* Paragon series */ { memset (command, 0, 6); command[0] = MUSTEK_SCSI_READ_SCANNED_DATA; command[2] = (lines >> 16) & 0xff; command[3] = (lines >> 8) & 0xff; command[4] = (lines >> 0) & 0xff; return sanei_scsi_req_enter (s->fd, command, 6, buf, lenp, idp); } } } static void dev_close (Mustek_Scanner * s) { if (s->hw->flags & MUSTEK_FLAG_N) sanei_ab306_close (s->fd); else if (s->hw->flags & MUSTEK_FLAG_SCSI_PP) mustek_scsi_pp_close (s->fd); else sanei_scsi_close (s->fd); } static SANE_Status sense_handler (SANE_Int scsi_fd, SANE_Byte * result, void *arg) { if (!result) { DBG (5, "sense_handler: no sense buffer\n"); return SANE_STATUS_IO_ERROR; } if (!arg) DBG (5, "sense_handler: got sense code %02x for fd %d (arg = null)\n", result[0], scsi_fd); else DBG (5, "sense_handler: got sense code %02x for fd %d (arg = %uc)\n", result[0], scsi_fd, *(SANE_Byte *) arg); switch (result[0]) { case 0x00: break; case 0x82: if (result[1] & 0x80) { DBG (3, "sense_handler: ADF is jammed\n"); return SANE_STATUS_JAMMED; /* ADF is jammed */ } break; case 0x83: if (result[2] & 0x02) { DBG (3, "sense_handler: ADF is out of documents\n"); return SANE_STATUS_NO_DOCS; /* ADF out of documents */ } break; case 0x84: if (result[1] & 0x10) { DBG (3, "sense_handler: transparency adapter cover open\n"); return SANE_STATUS_COVER_OPEN; /* open transparency adapter cover */ } break; default: DBG (1, "sense_handler: got unknown sense code %02x for fd %d\n", result[0], scsi_fd); return SANE_STATUS_IO_ERROR; } return SANE_STATUS_GOOD; } static SANE_Status inquiry (Mustek_Scanner * s) { SANE_Byte result[INQ_LEN]; size_t size; SANE_Status status; DBG (5, "inquiry: sending INQUIRY\n"); size = sizeof (result); memset (result, 0, size); status = dev_cmd (s, scsi_inquiry, sizeof (scsi_inquiry), result, &size); if (status != SANE_STATUS_GOOD) return status; /* checking ADF status */ if (s->hw->flags & MUSTEK_FLAG_ADF) { if (result[63] & (1 << 3)) { s->hw->flags |= MUSTEK_FLAG_ADF_READY; DBG (4, "inquiry: ADF ready\n"); } else { s->hw->flags &= ~MUSTEK_FLAG_ADF_READY; DBG (4, "inquiry: ADF not ready (out of paper)\n"); } } if (!result[0]) return SANE_STATUS_DEVICE_BUSY; return SANE_STATUS_GOOD; } static SANE_Status paragon_2_get_adf_status (Mustek_Scanner * s) { SANE_Status status; size_t len; SANE_Byte sense_buffer[4]; len = sizeof (sense_buffer); status = sanei_scsi_cmd (s->fd, scsi_request_sense, sizeof (scsi_request_sense), sense_buffer, &len); if (status != SANE_STATUS_GOOD) { DBG (1, "paragon_2_get_adf_status: %s\n", sane_strstatus (status)); return status; } DBG (5, "paragon_2_get_adf_status: sense_buffer: %x %x %x %x\n", sense_buffer[0], sense_buffer[1], sense_buffer[3], sense_buffer[3]); if (sense_buffer[0] == 0x00 && sense_buffer[1] == 0x00) return SANE_STATUS_GOOD; return SANE_STATUS_NO_DOCS; } static SANE_Bool ta_available_pro (Mustek_Scanner * s) { SANE_Status status; size_t len; SANE_Byte sense_buffer[4]; len = sizeof (sense_buffer); status = sanei_scsi_cmd (s->fd, scsi_request_sense, sizeof (scsi_request_sense), sense_buffer, &len); if (status != SANE_STATUS_GOOD) { DBG (1, "ta_available_pro: failed: %s\n", sane_strstatus (status)); return status; } DBG (5, "ta_available_pro: sense_buffer[2] = %x\n", sense_buffer[2]); scsi_unit_wait_ready (s); if (sense_buffer[2] == 0x40) return SANE_TRUE; return SANE_FALSE; } static SANE_Status attach (SANE_String_Const devname, Mustek_Device ** devp, SANE_Bool may_wait) { SANE_Int mustek_scanner, fw_revision; SANE_Byte result[INQ_LEN]; SANE_Byte inquiry_byte_list[50], inquiry_text_list[17]; SANE_Byte inquiry_byte[5], inquiry_text[5]; SANE_Byte *model_name = result + 44; Mustek_Scanner s; Mustek_Device *dev, new_dev; SANE_Status status; size_t size; SANE_String scsi_device_type[] = { "Direct-Access", "Sequential-Access", "Printer", "Processor", "Write-Once", "CD-ROM", "Scanner", "Optical Memory", "Medium Changer", "Communications" }; SANE_Byte scsi_vendor[9]; SANE_Byte scsi_product[17]; SANE_Byte scsi_revision[5]; SANE_Byte *pp; SANE_Bool warning = SANE_FALSE; SANE_Int firmware_format = 0; SANE_Int firmware_revision_system = 0; if (devp) *devp = 0; for (dev = first_dev; dev; dev = dev->next) if (strcmp (dev->sane.name, devname) == 0) { if (devp) *devp = dev; return SANE_STATUS_GOOD; } memset (&new_dev, 0, sizeof (new_dev)); memset (&s, 0, sizeof (s)); s.hw = &new_dev; s.hw->max_buffer_size = 8 * 1024; DBG (3, "attach: trying device %s\n", devname); status = dev_open (devname, &s, sense_handler); if (status != SANE_STATUS_GOOD) return status; if (may_wait || force_wait) dev_wait_ready (&s); DBG (5, "attach: sending INQUIRY\n"); size = sizeof (result); memset (result, 0, sizeof (result)); status = dev_cmd (&s, scsi_inquiry, sizeof (scsi_inquiry), result, &size); if (status != SANE_STATUS_GOOD || size != INQ_LEN) { DBG (1, "attach: inquiry for device %s failed (%s)\n", devname, sane_strstatus (status)); dev_close (&s); return status; } status = dev_wait_ready (&s); dev_close (&s); if (status != SANE_STATUS_GOOD) return status; if ((result[0] & 0x1f) != 0x06) { DBG (1, "attach: device %s doesn't look like a scanner at all (%d)\n", devname, result[0] & 0x1f); return SANE_STATUS_INVAL; } if (debug_level >= 3) { /* clear spaces and special chars */ strncpy ((SANE_String) scsi_vendor, (SANE_String) result + 8, 8); scsi_vendor[8] = '\0'; pp = scsi_vendor + 7; while (pp >= scsi_vendor && (*pp == ' ' || *pp >= 127)) *pp-- = '\0'; strncpy ((SANE_String) scsi_product, (SANE_String) result + 16, 16); scsi_product[16] = '\0'; pp = scsi_product + 15; while (pp >= scsi_product && (*pp == ' ' || *pp >= 127)) *pp-- = '\0'; strncpy ((SANE_String) scsi_revision, (SANE_String) result + 32, 4); scsi_revision[4] = '\0'; pp = scsi_revision + 3; while (pp >= scsi_revision && (*pp == ' ' || *pp >= 127)) *pp-- = '\0'; DBG (3, "attach: SCSI Vendor: `%-8s' Model: `%-16s' Rev.: `%-4s'\n", scsi_vendor, scsi_product, scsi_revision); DBG (3, "attach: SCSI Type: %s; ANSI rev.: %d\n", ((result[0] & 0x1f) < 0x10) ? scsi_device_type[result[0] & 0x1f] : "Unknown", result[2] & 0x03); DBG (3, "attach: SCSI flags: %s%s%s%s%s%s%s\n", (result[7] & 0x80) ? "RelAdr " : "", (result[7] & 0x40) ? "WBus32 " : "", (result[7] & 0x20) ? "WBus16 " : "", (result[7] & 0x10) ? "Sync " : "", (result[7] & 0x08) ? "Linked " : "", (result[7] & 0x02) ? "CmdQue " : "", (result[7] & 0x01) ? "SftRe " : ""); } if (debug_level >= 4) { /* print out inquiry */ DBG (4, "attach: inquiry output:\n"); inquiry_byte_list[0] = '\0'; inquiry_text_list[0] = '\0'; for (pp = result; pp < (result + INQ_LEN); pp++) { sprintf ((SANE_String) inquiry_text, "%c", (*pp < 127) && (*pp > 31) ? *pp : '.'); strcat ((SANE_String) inquiry_text_list, (SANE_String) inquiry_text); sprintf ((SANE_String) inquiry_byte, " %02x", *pp); strcat ((SANE_String) inquiry_byte_list, (SANE_String) inquiry_byte); if ((pp - result) % 0x10 == 0x0f) { DBG (4, "%s %s\n", inquiry_byte_list, inquiry_text_list); inquiry_byte_list[0] = '\0'; inquiry_text_list[0] = '\0'; } } } /* first check for new firmware format: */ mustek_scanner = (strncmp ((SANE_String) result + 36, "MUSTEK", 6) == 0); if (mustek_scanner) { if (result[43] == 'M') { DBG (3, "attach: found Mustek scanner (pro series firmware " "format)\n"); firmware_format = 2; model_name = result + 43; } else { DBG (3, "attach: found Mustek scanner (new firmware format)\n"); firmware_format = 1; } } else { /* check for old format: */ mustek_scanner = (strncmp ((SANE_String) result + 8, "MUSTEK", 6) == 0); if (mustek_scanner) { model_name = result + 16; DBG (3, "attach: found Mustek scanner (old firmware format)\n"); firmware_format = 0; } else { /* Check for some non-Mustek scanners an print warning */ if (strncmp ((SANE_String) result + 8, "Trust", 5) == 0) DBG (1, "attach: this is a real Trust scanner. It is not " " supported by this backend.\n"); if (strncmp ((SANE_String) result + 8, "Aashima", 7) == 0) DBG (1, "attach: this is an Aashima/Teco scanner. It is not " " supported by this backend.\n"); if (strncmp ((SANE_String) result + 16, "Flatbed Scanner", 15) == 0 && strncmp ((SANE_String) result + 42, "TECO", 4) == 0) DBG (1, "attach: this is a Relysis/Teco scanner. It is not " " supported by this backend.\n"); DBG (1, "attach: device %s doesn't look like a Mustek scanner\n", devname); return SANE_STATUS_INVAL; } } /* get firmware revision as BCD number: */ /* General format: x.yz */ /* Newer ScanExpress scanners (ID XC06): Vxyz */ if (result[33] == '.') { fw_revision = (result[32] - '0') << 8 | (result[34] - '0') << 4 | (result[35] - '0'); firmware_revision_system = 0; DBG (4, "attach: old firmware revision system\n"); } else { fw_revision = (result[33] - '0') << 8 | (result[34] - '0') << 4 | (result[35] - '0'); firmware_revision_system = 1; DBG (4, "attach: new firmware revision system\n"); } DBG (3, "attach: firmware revision %d.%02x\n", fw_revision >> 8, fw_revision & 0xff); dev = malloc (sizeof (*dev)); if (!dev) return SANE_STATUS_NO_MEM; memcpy (dev, &new_dev, sizeof (*dev)); dev->name = strdup (devname); if (!dev->name) return SANE_STATUS_NO_MEM; dev->sane.name = (SANE_String_Const) dev->name; dev->sane.vendor = "Mustek"; dev->sane.type = "flatbed scanner"; dev->x_range.min = 0; dev->y_range.min = 0; dev->x_range.quant = 0; dev->y_range.quant = 0; dev->x_trans_range.min = 0; dev->y_trans_range.min = 0; /* default to something really small to be on the safe side: */ dev->x_trans_range.max = SANE_FIX (8.0 * MM_PER_INCH); dev->y_trans_range.max = SANE_FIX (5.0 * MM_PER_INCH); dev->x_trans_range.quant = 0; dev->y_trans_range.quant = 0; dev->dpi_range.min = SANE_FIX (72); /* some scanners don't like low dpi */ dev->dpi_range.quant = SANE_FIX (1); /* default to 128 kB */ dev->max_buffer_size = 128 * 1024; /* SCSI buffer -> use 64 k per buffer */ dev->max_block_buffer_size = 1024 * 1024 * 1024; dev->firmware_format = firmware_format; dev->firmware_revision_system = firmware_revision_system; DBG (3, "attach: scanner id: %.11s\n", model_name); if (strncmp ((SANE_String) model_name + 10, "PRO", 3) == 0) DBG (3, "attach: this is probably a Paragon Pro series scanner\n"); else if (strncmp ((SANE_String) model_name, "MFC", 3) == 0) DBG (3, "attach: this is probably a Paragon series II scanner\n"); else if (strncmp ((SANE_String) model_name, "M", 1) == 0) DBG (3, "attach: this is probably a Paragon series I or 3-pass scanner\n"); else if (strncmp ((SANE_String) model_name, " C", 2) == 0) DBG (3, "attach: this is probably a ScanExpress series A4 scanner\n"); else if (strncmp ((SANE_String) model_name, " L", 2) == 0) DBG (3, "attach: this is probably a ScanExpress series A3 scanner\n"); else if (strncmp ((SANE_String) model_name, "XC", 2) == 0) DBG (3, "attach: this is probably a ScanExpress Plus series A4 scanner\n"); else DBG (3, "attach: I am not sure what type of scanner this is\n"); /* Paragon 3-pass series */ if (strncmp ((SANE_String) model_name, "MFS-12000CX", 11) == 0) { /* These values were measured and compared to those from the Windows driver. Tested with a Paragon MFS-12000CX v4.00 */ dev->x_range.min = SANE_FIX (0.0); dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH); dev->y_range.min = SANE_FIX (0.0); dev->y_range.max = SANE_FIX (14.00 * MM_PER_INCH); dev->x_trans_range.min = SANE_FIX (1.0); dev->y_trans_range.min = SANE_FIX (1.0); dev->x_trans_range.max = SANE_FIX (205.0); dev->y_trans_range.max = SANE_FIX (255.0); dev->dpi_range.max = SANE_FIX (1200); dev->sane.model = "MFS-12000CX"; } /* There are two different versions of the MFS-6000CX, one has the model name "MFS-06000CX", the other one is "MSF-06000CZ" */ else if (strncmp ((SANE_String) model_name, "MFS-06000CX", 11) == 0) { /* These values were measured and tested with a Paragon MFS-6000CX v4.06 */ dev->x_range.min = SANE_FIX (0.0); dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH); dev->y_range.min = SANE_FIX (0.0); dev->y_range.max = SANE_FIX (13.86 * MM_PER_INCH); dev->x_trans_range.min = SANE_FIX (1.0); dev->y_trans_range.min = SANE_FIX (2.0); dev->x_trans_range.max = SANE_FIX (203.0); dev->y_trans_range.max = SANE_FIX (255.0); dev->dpi_range.max = SANE_FIX (600); dev->sane.model = "MFS-6000CX"; } else if (strncmp ((SANE_String) model_name, "MSF-06000CZ", 11) == 0) { /* These values were measured and compared to those from the Windows driver. Tested with a Paragon MFS-6000CX v4.00 */ dev->x_range.min = SANE_FIX (0.0); dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH); dev->y_range.min = SANE_FIX (0.0); dev->y_range.max = SANE_FIX (13.85 * MM_PER_INCH); dev->x_trans_range.min = SANE_FIX (1.0); dev->y_trans_range.min = SANE_FIX (2.0); dev->x_trans_range.max = SANE_FIX (205.0); dev->y_trans_range.max = SANE_FIX (255.0); dev->dpi_range.max = SANE_FIX (600); dev->sane.model = "MFS-6000CX"; } /* Paragon 1-pass 14" series I */ /* I haven't seen a single report for this, but it is mentioned in the old man page. All reported Paragon 1200SP had a model name "MFS-12000SP" */ else if (strncmp ((SANE_String) model_name, "MSF-12000SP", 11) == 0) { /* These values are not tested and mostly guessed. */ dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH); dev->y_range.max = SANE_FIX (13.85 * MM_PER_INCH); dev->x_trans_range.min = SANE_FIX (1.0); dev->y_trans_range.min = SANE_FIX (1.0); dev->x_trans_range.max = SANE_FIX (200.0); dev->y_trans_range.max = SANE_FIX (250.0); dev->dpi_range.max = SANE_FIX (1200); dev->flags |= MUSTEK_FLAG_LD_NONE; dev->flags |= MUSTEK_FLAG_PARAGON_1; dev->flags |= MUSTEK_FLAG_USE_BLOCK; dev->sane.model = "MFS-12000SP"; warning = SANE_TRUE; } /* MFS-8000 SP v 1.x */ else if (strncmp ((SANE_String) model_name, "MSF-08000SP", 11) == 0) { /* These values were measured and compared to those from the Windows driver. Tested with a Paragon MFS-8000SP v1.20 */ dev->x_range.min = SANE_FIX (0.0); dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH); dev->y_range.min = SANE_FIX (0); dev->y_range.max = SANE_FIX (355.6); dev->x_trans_range.min = SANE_FIX (1.0); dev->y_trans_range.min = SANE_FIX (1.0); dev->x_trans_range.max = SANE_FIX (205.0); dev->y_trans_range.max = SANE_FIX (255.0); dev->dpi_range.max = SANE_FIX (800); /* At least scanners with firmware 1.20 need a gamma table upload in color mode, otherwise the image is red */ if (fw_revision == 0x120) dev->flags |= MUSTEK_FLAG_FORCE_GAMMA; dev->flags |= MUSTEK_FLAG_PARAGON_1; dev->sane.model = "MFS-8000SP"; } /* This model name exists */ else if (strncmp ((SANE_String) model_name, "MSF-06000SP", 11) == 0) { /* These values were measured and compared to those from the Windows driver. Tested with a Paragon MFS-6000SP v3.12 */ dev->x_range.min = SANE_FIX (0.0); dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH); dev->y_range.min = SANE_FIX (0.0); dev->y_range.max = SANE_FIX (355.6); dev->x_trans_range.min = SANE_FIX (1.0); dev->y_trans_range.min = SANE_FIX (1.0); dev->x_trans_range.max = SANE_FIX (205.0); dev->y_trans_range.max = SANE_FIX (255.0); dev->dpi_range.max = SANE_FIX (600); /* Looks like at least some versions of this scanner produce black images in gray and color mode if MUSTEK_FORCE_GAMMA is not set. At least versions 2.01, 2.02 and 2.10 are reported as having this bug. 3.12 doesn't need this workaround but it doesn't harm either. */ dev->flags |= MUSTEK_FLAG_FORCE_GAMMA; dev->flags |= MUSTEK_FLAG_PARAGON_1; dev->sane.model = "MFS-6000SP"; } /* This one was reported multiple times */ else if (strncmp ((SANE_String) model_name, "MFS-12000SP", 11) == 0) { /* These values were measured and compared to those from the Windows driver. Tested with a Paragon MFS-12000SP v1.02 and v1.00 */ dev->x_range.min = SANE_FIX (0.0); dev->x_range.max = SANE_FIX (217.0); dev->y_range.min = SANE_FIX (2.0); dev->y_range.max = SANE_FIX (352.0); dev->x_trans_range.min = SANE_FIX (0.0); dev->y_trans_range.min = SANE_FIX (0.0); dev->x_trans_range.max = SANE_FIX (205.0); dev->y_trans_range.max = SANE_FIX (250.0); dev->dpi_range.max = SANE_FIX (1200); /* Earlier versions of this source code used MUSTEK_FLAG_LD_MFS for firmware versions < 1.02 and LD_NONE for the rest. This didn't work for my scanners. 1.00 doesn't need any LD correction, 1.02, 1.07 and 1.11 do need normal LD corrections. Maybe all != 1.00 need normal LD */ dev->flags |= MUSTEK_FLAG_PARAGON_1; dev->flags |= MUSTEK_FLAG_LD_BLOCK; dev->flags |= MUSTEK_FLAG_USE_BLOCK; dev->sane.model = "MFS-12000SP"; } /* MFS-8000 SP v2.x */ else if (strncmp ((SANE_String) model_name, "MFS-08000SP", 11) == 0) { /* These values are tested with a MFS-08000SP v 2.04 */ dev->x_range.min = SANE_FIX (0.0); dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH); dev->y_range.min = SANE_FIX (0); dev->y_range.max = SANE_FIX (355.6); dev->x_trans_range.min = SANE_FIX (1.0); dev->y_trans_range.min = SANE_FIX (1.0); dev->x_trans_range.max = SANE_FIX (205.0); dev->y_trans_range.max = SANE_FIX (255.0); dev->dpi_range.max = SANE_FIX (800); /* At least scanners with firmware 1.20 need a gamma table upload in color mode, otherwise the image is red */ if (fw_revision == 0x120) dev->flags |= MUSTEK_FLAG_FORCE_GAMMA; dev->flags |= MUSTEK_FLAG_PARAGON_1; dev->sane.model = "MFS-8000SP"; } /* I have never seen one of those */ else if (strncmp ((SANE_String) model_name, "MFS-06000SP", 11) == 0) { /* These values are not tested. */ dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH); dev->y_range.max = SANE_FIX (13.84 * MM_PER_INCH); /* copied from MSF-06000SP */ dev->x_trans_range.min = SANE_FIX (1.0); dev->y_trans_range.min = SANE_FIX (1.0); dev->x_trans_range.max = SANE_FIX (205.0); dev->y_trans_range.max = SANE_FIX (255.0); dev->dpi_range.max = SANE_FIX (600); dev->flags |= MUSTEK_FLAG_PARAGON_1; dev->sane.model = "MFS-6000SP"; warning = SANE_TRUE; } /* Paragon 1-pass A4 series II */ else if (strncmp ((SANE_String) model_name, "MFC-08000CZ", 11) == 0) { /* These values were measured and compared to those from the Windows driver. Tested with a Paragon 800 II SP v1.06. */ dev->x_range.min = SANE_FIX (1.5); dev->x_range.max = SANE_FIX (218.0); dev->y_range.min = SANE_FIX (0.0); dev->y_range.max = SANE_FIX (293.0); dev->x_trans_range.min = SANE_FIX (0.0); dev->y_trans_range.min = SANE_FIX (0.0); dev->x_trans_range.max = SANE_FIX (205.0); dev->y_trans_range.max = SANE_FIX (254.0); dev->dpi_range.max = SANE_FIX (800); dev->max_block_buffer_size = 2 * 1024 * 1024; dev->flags |= MUSTEK_FLAG_PARAGON_2; dev->flags |= MUSTEK_FLAG_LD_BLOCK; dev->flags |= MUSTEK_FLAG_USE_BLOCK; dev->sane.model = "800S/800 II SP"; } else if (strncmp ((SANE_String) model_name, "MFC-06000CZ", 11) == 0) { /* These values were measured and compared to those from the Windows driver. Tested with a Paragon 600 II CD, a Paragon MFC-600S and a Paragon 600 II N. */ dev->x_range.min = SANE_FIX (0.0); dev->x_range.max = SANE_FIX (218.0); dev->y_range.min = SANE_FIX (0.0); dev->y_range.max = SANE_FIX (293.0); dev->x_trans_range.min = SANE_FIX (0.0); dev->y_trans_range.min = SANE_FIX (0.0); dev->x_trans_range.max = SANE_FIX (201.0); dev->y_trans_range.max = SANE_FIX (257.0); dev->dpi_range.max = SANE_FIX (600); /* This model comes in a non-scsi version, too. It is supplied with its own parallel-port like adapter, an AB306N. Two firmware revisions are known: 1.01 and 2.00. Each needs its own line-distance correction code. */ if (dev->flags & MUSTEK_FLAG_N) { if (fw_revision < 0x200) dev->flags |= MUSTEK_FLAG_LD_N1; else dev->flags |= MUSTEK_FLAG_LD_N2; dev->x_trans_range.min = SANE_FIX (33.0); dev->y_trans_range.min = SANE_FIX (62.0); dev->x_trans_range.max = SANE_FIX (183.0); dev->y_trans_range.max = SANE_FIX (238.0); dev->max_block_buffer_size = 1024 * 1024 * 1024; dev->sane.model = "600 II N"; } else if (dev->flags & MUSTEK_FLAG_SCSI_PP) { /* FIXME; experiment with different line distance codes later */ dev->dpi_range.min = SANE_FIX (75.0); dev->flags |= MUSTEK_FLAG_LD_NONE; dev->max_block_buffer_size = 2 * 1024 * 1024; dev->sane.model = "600 II EP"; } else { dev->sane.model = "600S/600 II CD"; dev->flags |= MUSTEK_FLAG_PARAGON_2; dev->flags |= MUSTEK_FLAG_LD_BLOCK; dev->flags |= MUSTEK_FLAG_USE_BLOCK; dev->max_block_buffer_size = 2 * 1024 * 1024; } } /* ScanExpress and ScanMagic series */ else if (strncmp ((SANE_String) model_name, " C03", 4) == 0) { /* These values were measured and compared to those from the Windows driver. Tested with a ScannExpress 6000SP 1.00 */ dev->x_range.max = SANE_FIX (215); dev->y_range.min = SANE_FIX (0); dev->y_range.max = SANE_FIX (293); dev->x_trans_range.min = SANE_FIX (0); dev->y_trans_range.min = SANE_FIX (0); dev->x_trans_range.max = SANE_FIX (150.0); dev->y_trans_range.max = SANE_FIX (175.0); dev->dpi_range.max = SANE_FIX (600); dev->dpi_range.min = SANE_FIX (60); dev->flags |= MUSTEK_FLAG_SE; /* At least the SE 6000SP with firmware 1.00 limits its x-resolution to 300 dpi and does *no* interpolation at higher resolutions. So this has to be done in software. */ dev->flags |= MUSTEK_FLAG_ENLARGE_X; dev->sane.model = "ScanExpress 6000SP"; } /* There are two different versions of the ScanExpress 12000SP, one has the model name " C06", the other one is "XC06". The latter seems to be used in the newer "Plus" models. Also there is the Mustek ScanExpress 1200 FS, which looks similar to the ScanExpress 12000 SP but has an "F" instead of the "V" in the firmware version. */ else if (strncmp ((SANE_String) model_name, " C06", 4) == 0) { if (result[32] == 'F') { /* Mustek ScanExpress 1200 FS. Completely untested. */ dev->x_range.min = SANE_FIX (0); dev->y_range.min = SANE_FIX (0); dev->x_range.max = SANE_FIX (215.9); dev->y_range.max = SANE_FIX (291.2); dev->x_trans_range.min = SANE_FIX (0); dev->y_trans_range.min = SANE_FIX (0); dev->x_trans_range.max = SANE_FIX (150.0); dev->y_trans_range.max = SANE_FIX (175.0); dev->dpi_range.max = SANE_FIX (1200); dev->dpi_range.min = SANE_FIX (60); dev->flags |= MUSTEK_FLAG_SE; /* The ScanExpress models limit their x-resolution to 600 dpi and do *no* interpolation at higher resolutions. So this has to be done in software. */ dev->flags |= MUSTEK_FLAG_ENLARGE_X; dev->flags |= MUSTEK_FLAG_COVER_SENSOR; dev->sane.model = "ScanExpress 12000 FS (untested)"; } else { /* These values were measured and compared to those from the Windows driver. Tested with a ScaneExpress 12000SP 2.02 and a ScanMagic 9636S v 1.01 */ dev->x_range.min = SANE_FIX (0); dev->y_range.min = SANE_FIX (0); dev->x_range.max = SANE_FIX (215.9); dev->y_range.max = SANE_FIX (291.2); dev->x_trans_range.min = SANE_FIX (0); dev->y_trans_range.min = SANE_FIX (0); dev->x_trans_range.max = SANE_FIX (150.0); dev->y_trans_range.max = SANE_FIX (175.0); dev->dpi_range.max = SANE_FIX (1200); dev->dpi_range.min = SANE_FIX (60); dev->flags |= MUSTEK_FLAG_SE; /* The ScanExpress models limit their x-resolution to 600 dpi and do *no* interpolation at higher resolutions. So this has to be done in software. */ dev->flags |= MUSTEK_FLAG_ENLARGE_X; dev->flags |= MUSTEK_FLAG_COVER_SENSOR; dev->sane.model = "ScanExpress 12000SP"; } } else if (strncmp ((SANE_String) model_name, "XC06", 4) == 0) { /* These values are tested with a SE 12000 SP Plus v 1.01 */ dev->x_range.max = SANE_FIX (216); dev->y_range.min = SANE_FIX (0); dev->y_range.max = SANE_FIX (294.5); dev->x_trans_range.min = SANE_FIX (0); dev->y_trans_range.min = SANE_FIX (0); dev->x_trans_range.max = SANE_FIX (152.0); dev->y_trans_range.max = SANE_FIX (177.0); dev->dpi_range.max = SANE_FIX (1200); dev->dpi_range.min = SANE_FIX (60); dev->flags |= MUSTEK_FLAG_SE; dev->flags |= MUSTEK_FLAG_SE_PLUS; /* The ScanExpress models limit their x-resolution to 600 dpi and do *no* interpolation at higher resolutions. So this has to be done in software. */ dev->flags |= MUSTEK_FLAG_ENLARGE_X; dev->flags |= MUSTEK_FLAG_COVER_SENSOR; dev->sane.model = "ScanExpress 12000SP Plus"; } /* ScanExpress A3 SP */ else if (strncmp ((SANE_String) model_name, " L03", 4) == 0) { /* These values were measured with a ScannExpress A3 SP 2.00 */ dev->x_range.max = SANE_FIX (297); dev->y_range.min = SANE_FIX (0); dev->y_range.max = SANE_FIX (430); /* TA couldn't be tested due to lack of equipment. So At least the TA IV (A4 size) is supported */ dev->x_trans_range.min = SANE_FIX (0); dev->y_trans_range.min = SANE_FIX (0); dev->x_trans_range.max = SANE_FIX (150.0); dev->y_trans_range.max = SANE_FIX (175.0); dev->dpi_range.max = SANE_FIX (600); dev->dpi_range.min = SANE_FIX (60); dev->flags |= MUSTEK_FLAG_SE; /* The ScanExpress models limit their x-resolution to 300 dpi and do *no* interpolation at higher resolutions. So this has to be done in software. */ dev->flags |= MUSTEK_FLAG_ENLARGE_X; dev->flags |= MUSTEK_FLAG_COVER_SENSOR; dev->sane.model = "ScanExpress A3 SP"; } /* Paragon 1200 SP Pro */ else if (strncmp ((SANE_String) model_name, "MFS-1200SPPRO", 13) == 0) { /* These values were measured with a Paragon 1200 SP Pro v2.01 */ dev->x_range.max = SANE_FIX (8.6 * MM_PER_INCH); dev->y_range.max = SANE_FIX (13.70 * MM_PER_INCH); dev->dpi_range.max = SANE_FIX (1200); dev->sane.model = "1200 SP PRO"; dev->flags |= MUSTEK_FLAG_LD_NONE; dev->flags |= MUSTEK_FLAG_ENLARGE_X; } /* No documentation, but it works: Paragon 1200 A3 PRO */ else if (strncmp ((SANE_String) model_name, "MFS-1200A3PRO", 13) == 0) { /* These values were measured and compared to those from the Windows driver. Tested with a Paragon 1200 A3 Pro v1.10 */ dev->x_range.max = SANE_FIX (11.7 * MM_PER_INCH); dev->y_range.max = SANE_FIX (424); dev->dpi_range.max = SANE_FIX (1200); dev->sane.model = "1200 A3 PRO"; dev->flags |= MUSTEK_FLAG_LD_NONE; dev->flags |= MUSTEK_FLAG_ENLARGE_X; } else { DBG (0, "attach: this Mustek scanner (ID: %s) is not supported yet\n", model_name); DBG (0, "attach: please set the debug level to 5 and send a debug " "report\n"); DBG (0, "attach: to henning@meier-geinitz.de (export " "SANE_DEBUG_MUSTEK=5\n"); DBG (0, "attach: scanimage -L 2>debug.txt). Thank you.\n"); free (dev); return SANE_STATUS_INVAL; } if (dev->flags & MUSTEK_FLAG_SE) { DBG (3, "attach: this is a single-pass scanner\n"); if (result[63] & (1 << 6)) { dev->flags |= MUSTEK_FLAG_TA; DBG (3, "attach: scanner supports transparency adapter (TA)\n"); } } else { if (result[57] & (1 << 6)) { DBG (3, "attach: this is a single-pass scanner\n"); if (dev->flags & MUSTEK_FLAG_LD_NONE) DBG (4, "attach: scanner doesn't need line-distance correction\n"); else if (dev->flags & MUSTEK_FLAG_LD_N1) DBG (4, "attach: scanner has N1 line-distance correction\n"); else if (dev->flags & MUSTEK_FLAG_LD_N2) DBG (4, "attach: scanner has N2 line-distance correction\n"); else if (dev->flags & MUSTEK_FLAG_LD_BLOCK) DBG (4, "attach: scanner has block line-distance correction\n"); else DBG (4, "attach: scanner has normal line-distance correction\n"); } else { dev->flags |= MUSTEK_FLAG_THREE_PASS; /* three-pass scanners quantize to 0.5% of the maximum resolution: */ dev->dpi_range.quant = dev->dpi_range.max / 200; dev->dpi_range.min = dev->dpi_range.quant; DBG (3, "attach: this is a three-pass scanner\n"); } if (result[57] & (1 << 5)) { DBG (3, "attach: this is a professional series scanner\n"); dev->flags |= MUSTEK_FLAG_PRO; status = dev_open (devname, &s, sense_handler); if (status == SANE_STATUS_GOOD) { if (ta_available_pro (&s)) { dev->flags |= MUSTEK_FLAG_TA; DBG (3, "attach: found transparency adapter (TA)\n"); } dev_close (&s); } else { DBG (1, "attach: couldn't open device: %s\n", sane_strstatus (status)); return status; } } if (result[63] & (1 << 2)) { dev->flags |= MUSTEK_FLAG_ADF; DBG (3, "attach: found automatic document feeder (ADF)\n"); if (result[63] & (1 << 3)) { dev->flags |= MUSTEK_FLAG_ADF_READY; DBG (4, "attach: automatic document feeder is ready\n"); } else { DBG (4, "attach: automatic document feeder is out of " "documents\n"); } } if (result[63] & (1 << 6)) { dev->flags |= MUSTEK_FLAG_TA; DBG (3, "attach: found transparency adapter (TA)\n"); } } if (dev->flags & MUSTEK_FLAG_COVER_SENSOR) { if (result[62] & (1 << 0)) DBG (4, "attach: scanner cover is closed\n"); else DBG (4, "attach: scanner cover is open\n"); } if (warning == SANE_TRUE) { DBG (0, "WARNING: Your scanner was detected by the SANE Mustek backend, " "but\n it is not fully tested. It may or may not work. Be " "careful and read\n the PROBLEMS file in the sane directory. " "Please set the debug level of this\n backend to maximum " "(export SANE_DEBUG_MUSTEK=255) and send the output of\n " "scanimage -L to the SANE mailing list sane-devel@alioth-lists.debian.net. " "Please include\n the exact model name of your scanner and to " "which extend it works.\n"); } DBG (2, "attach: found Mustek %s %s, %s%s%s%s\n", dev->sane.model, dev->sane.type, (dev->flags & MUSTEK_FLAG_THREE_PASS) ? "3-pass" : "1-pass", (dev->flags & MUSTEK_FLAG_ADF) ? ", ADF" : "", (dev->flags & MUSTEK_FLAG_TA) ? ", TA" : "", (dev->flags & MUSTEK_FLAG_SE) ? ", SE" : ""); ++num_devices; dev->next = first_dev; first_dev = dev; if (devp) *devp = dev; return SANE_STATUS_GOOD; } 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; } static SANE_Status constrain_value (Mustek_Scanner * s, SANE_Int option, void *value, SANE_Int * info) { SANE_Fixed w, dpi; SANE_Status status; if (value) w = *(SANE_Fixed *) value; else w = 0; if (option == OPT_RESOLUTION) { if (s->hw->flags & MUSTEK_FLAG_THREE_PASS) { /* The three pass scanners use a 0.5% of the maximum resolution increment for resolutions less than or equal to half of the maximum resolution. The MFS-06000CX uses a 5% of the maximum resolution increment for larger resolutions. The models MFS-12000CX and MSF-06000CZ use 1% of the maximum resolution. We can't represent this easily in SANE, so the constraint is simply for 0.5% and then we round to the 5% or 1% increments if necessary. */ SANE_Fixed max_dpi, quant, half_res; /*w = *(SANE_Word *) value; */ max_dpi = s->hw->dpi_range.max; half_res = max_dpi / 2; if (w > half_res) { /* quantizize to 1% step */ quant = max_dpi / 100; dpi = (w + quant / 2) / quant; dpi *= quant; if (dpi != w) { *(SANE_Word *) value = dpi; if (info) *info |= SANE_INFO_INEXACT; } } } } status = sanei_constrain_value (s->opt + option, value, info); if (s->opt[option].type == SANE_TYPE_FIXED) DBG (5, "constrain_value: %s = %.2f (was %.2f)\n", s->opt[option].name, SANE_UNFIX (*(SANE_Word *) value), SANE_UNFIX (w)); return status; } /* Quantize s->val[OPT_RESOLUTION].w and return the resolution code for the quantized resolution. Quantization depends on scanner type (single pass vs. three-pass) and resolution */ static SANE_Int encode_resolution (Mustek_Scanner * s) { SANE_Fixed max_dpi, dpi; SANE_Int code, mode = 0; dpi = s->val[OPT_RESOLUTION].w; if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS)) { code = dpi >> SANE_FIXED_SCALE_SHIFT; } else { SANE_Fixed quant, half_res; max_dpi = s->hw->dpi_range.max; half_res = max_dpi / 2; if (dpi <= half_res) { /* quantizize to 0.5% step */ quant = max_dpi / 200; } else { /* quantizize to 1% step */ quant = max_dpi / 100; mode = 0x100; /* indicate 5% or 1% quantization */ } code = (dpi + quant / 2) / quant; if (code < 1) code = 1; } DBG (5, "encode_resolution: code = 0x%x (%d); mode = %x\n", code, code, mode); return code | mode; } static SANE_Int encode_percentage (Mustek_Scanner * s, double value) { SANE_Int max, code, sign = 0; if (s->hw->flags & MUSTEK_FLAG_THREE_PASS) { code = (int) ((value / 100.0 * 12) + 12.5); max = 0x18; } else { if (value < 0.0) { value = -value; sign = 0x80; } code = (int) (value / 100.0 * 127 + 0.5); code |= sign; max = 0xff; } if (code > max) code = max; if (code < 0) code = 0x00; return code; } /* encode halftone pattern type and size */ static SANE_Status encode_halftone (Mustek_Scanner * s) { SANE_String selection = s->val[OPT_HALFTONE_DIMENSION].s; SANE_Int i = 0; while ((halftone_list[i] != 0) && (strcmp (selection, halftone_list[i]) != 0)) { i++; } if (halftone_list[i] == 0) return SANE_STATUS_INVAL; if (i < 0x0c) /* standard pattern */ { s->custom_halftone_pattern = SANE_FALSE; s->halftone_pattern_type = i; } else /* custom pattern */ { s->custom_halftone_pattern = SANE_TRUE; i -= 0x0c; i = 8 - i; if (i < 8) i--; i = i + (i << 4); s->halftone_pattern_type = i; } DBG (5, "encode_halftone: %s pattern type %x\n", s->custom_halftone_pattern ? "custom" : "standard", s->halftone_pattern_type); return SANE_STATUS_GOOD; } /* Paragon series */ static SANE_Status area_and_windows (Mustek_Scanner * s) { SANE_Byte cmd[117], *cp; SANE_Int i, offset; /* setup SCSI command (except length): */ memset (cmd, 0, sizeof (cmd)); cmd[0] = MUSTEK_SCSI_AREA_AND_WINDOWS; cp = cmd + 6; /* Some scanners need a larger scanarea for line-distance correction */ offset = 0; if (((s->hw->flags & MUSTEK_FLAG_LD_N1) || ((s->hw->flags & MUSTEK_FLAG_LD_BLOCK) && (s->hw->flags & MUSTEK_FLAG_PARAGON_1))) && (s->mode & MUSTEK_MODE_COLOR)) offset = MAX_LINE_DIST; /* fill in frame header: */ if (s->hw->flags & MUSTEK_FLAG_USE_EIGHTS) { double eights_per_mm = 8 / MM_PER_INCH; SANE_Int tlx, tly, brx, bry; /* * The MSF-06000CZ seems to lock-up if the pixel-unit is used. * Using 1/8" works. * This doesn't seem to be true with the current scheme. * This code isn't used at the moment. */ *cp++ = ((s->mode & MUSTEK_MODE_LINEART) ? 0x00 : 0x01); tlx = SANE_UNFIX (s->val[OPT_TL_X].w) * eights_per_mm + 0.5; tly = SANE_UNFIX (s->val[OPT_TL_Y].w) * eights_per_mm + 0.5; brx = SANE_UNFIX (s->val[OPT_BR_X].w) * eights_per_mm + 0.5; bry = SANE_UNFIX (s->val[OPT_BR_Y].w) * eights_per_mm + 0.5; STORE16L (cp, tlx); STORE16L (cp, tly); STORE16L (cp, brx); STORE16L (cp, bry); DBG (5, "area_and_windows: tlx=%d (%d mm); tly=%d (%d mm); " "brx=%d (%d mm); bry=%d (%d mm)\n", tlx, (int) (tlx / eights_per_mm), tly, (int) (tly / eights_per_mm), brx, (int) (brx / eights_per_mm), bry, (int) (bry / eights_per_mm)); } else { double pixels_per_mm = SANE_UNFIX (s->hw->dpi_range.max) / MM_PER_INCH; SANE_Int tlx, tly, brx, bry; if (s->hw->flags & MUSTEK_FLAG_THREE_PASS) /* 3pass scanners use 1/2 of the max resolution as base */ pixels_per_mm /= 2; /* pixel unit and halftoning: */ *cp++ = 0x8 | ((s->mode & MUSTEK_MODE_LINEART) ? 0x00 : 0x01); /* fill in scanning area: */ if (strcmp (s->val[OPT_SOURCE].s, "Automatic Document Feeder") == 0) { /* must mirror the x coordinates */ brx = SANE_UNFIX (s->hw->x_range.max - s->val[OPT_TL_X].w) * pixels_per_mm + 0.5; tlx = SANE_UNFIX (s->hw->x_range.max - s->val[OPT_BR_X].w) * pixels_per_mm + 0.5; } else { tlx = SANE_UNFIX (s->val[OPT_TL_X].w) * pixels_per_mm + 0.5; brx = SANE_UNFIX (s->val[OPT_BR_X].w) * pixels_per_mm + 0.5; } tly = SANE_UNFIX (s->val[OPT_TL_Y].w) * pixels_per_mm + 0.5; bry = SANE_UNFIX (s->val[OPT_BR_Y].w) * pixels_per_mm + 0.5 + offset; STORE16L (cp, tlx); STORE16L (cp, tly); STORE16L (cp, brx); STORE16L (cp, bry); DBG (5, "area_and_windows: tlx=%d (%d mm); tly=%d (%d mm); " "brx=%d (%d mm); bry=%d (%d mm)\n", tlx, (int) (tlx / pixels_per_mm), tly, (int) (tly / pixels_per_mm), brx, (int) (brx / pixels_per_mm), bry, (int) (bry / pixels_per_mm)); } if (s->custom_halftone_pattern) { *cp++ = 0x40; /* mark presence of user pattern */ *cp++ = s->halftone_pattern_type; /* set pattern length */ for (i = 0; i < (s->halftone_pattern_type & 0x0f) * ((s->halftone_pattern_type >> 4) & 0x0f); ++i) *cp++ = s->val[OPT_HALFTONE_PATTERN].wa[i]; } cmd[4] = (cp - cmd) - 6; return dev_cmd (s, cmd, (cp - cmd), 0, 0); } /* ScanExpress */ static SANE_Status set_window_se (Mustek_Scanner * s, SANE_Int lamp) { SANE_Byte cmd[58], *cp; double pixels_per_mm; SANE_Int offset; SANE_Int tlx, tly, width, height; /* setup SCSI command (except length): */ memset (cmd, 0, sizeof (cmd)); cmd[0] = MUSTEK_SCSI_SET_WINDOW; cp = cmd + CDB_SIZE (MUSTEK_SCSI_SET_WINDOW); /* skip command block */ if (s->mode & MUSTEK_MODE_COLOR) { /* We have to increase the specified resolution to the next */ /* "standard" resolution due to a firmware bug(?) in color mode */ /* It's possible to scan in 36, 75, 100, 150, 200, 250, 300, */ /* 400, 500, 600, 900, 1200 dpi but the speed is only different */ /* with 36, 150, 300, 600, 1200 dpi. */ /* Additionally we must increase the window length slightly to */ /* compensate for different line counts for r/g/b */ const SANE_Int resolution_list[] = { 36, 150, 300, 600, 1200, 0 }; SANE_Int entry = 0; while (resolution_list[entry] < s->resolution_code) entry++; s->ld.peak_res = resolution_list[entry]; offset = MAX_LINE_DIST; /* distance r/b lines */ } else { /* In gray and lineart modes all resolutions are possible */ s->ld.peak_res = s->resolution_code; offset = 0; } DBG (5, "set_window_se: hardware resolution is %d dpi; offset is %d\n", s->ld.peak_res, offset); STORE16B (cp, 0); /* window identifier */ STORE16B (cp, s->ld.peak_res); /* x and y resolution */ STORE16B (cp, 0); /* not used acc. to specs */ pixels_per_mm = SANE_UNFIX (s->hw->dpi_range.max) / MM_PER_INCH; /* fill in scanning area, begin and length(!) */ if ((strcmp (s->val[OPT_SOURCE].s, "Transparency Adapter") == 0) && !(s->hw->flags & MUSTEK_FLAG_TA)) { /* need to add the start values of the transparency adapter */ tlx = (SANE_UNFIX (s->val[OPT_TL_X].w) + 33.0) * pixels_per_mm + 0.5; tly = (SANE_UNFIX (s->val[OPT_TL_Y].w) + 60.0) * pixels_per_mm + 0.5; DBG (5, "set_window_se: added offset for transparency adapter\n"); } else { /* no transparency adapter selected or calculation done in firmware */ tlx = SANE_UNFIX (s->val[OPT_TL_X].w) * pixels_per_mm + 0.5; tly = SANE_UNFIX (s->val[OPT_TL_Y].w) * pixels_per_mm + 0.5; } width = (SANE_UNFIX (s->val[OPT_BR_X].w) - SANE_UNFIX (s->val[OPT_TL_X].w)) * pixels_per_mm + 0.5; height = (SANE_UNFIX (s->val[OPT_BR_Y].w) - SANE_UNFIX (s->val[OPT_TL_Y].w)) * pixels_per_mm + 0.5 + offset; DBG (5, "set_window_se: tlx=%d (%d mm); tly=%d (%d mm); width=%d (%d mm); " "height=%d (%d mm)\n", tlx, (int) (tlx / pixels_per_mm), tly, (int) (tly / pixels_per_mm), width, (int) (width / pixels_per_mm), height, (int) (height / pixels_per_mm)); STORE32B (cp, tlx); STORE32B (cp, tly); STORE32B (cp, width); STORE32B (cp, height); *cp++ = 0x00; /* brightness, not impl. */ *cp++ = 0x80; /* threshold, not impl. */ *cp++ = 0x00; /* contrast, not impl. */ /* Note that 'image composition' has no meaning for the SE series */ /* Mode selection is accomplished solely by bits/pixel (1, 8, 24) */ if (s->mode & MUSTEK_MODE_COLOR) { *cp++ = 0x05; /* actually not used! */ *cp++ = 24; /* 24 bits/pixel in color mode */ } else if (s->mode & MUSTEK_MODE_GRAY) { *cp++ = 0x02; /* actually not used! */ *cp++ = 8; /* 8 bits/pixel in gray mode */ } else { *cp++ = 0x00; /* actually not used! */ *cp++ = 1; /* 1 bit/pixel in lineart mode */ } cp += 14; /* skip reserved bytes */ *cp++ = lamp; /* 0 = normal, 1 = on, 2 = off */ if ((s->hw->flags & MUSTEK_FLAG_TA) && (strcmp (s->val[OPT_SOURCE].s, "Transparency Adapter") == 0)) *cp++ = 1; else *cp++ = 0; cp += 5; /* skip reserved bytes */ cmd[8] = cp - cmd - CDB_SIZE (MUSTEK_SCSI_SET_WINDOW); return dev_cmd (s, cmd, (cp - cmd), 0, 0); } /* Pro series */ static SANE_Status set_window_pro (Mustek_Scanner * s) { SANE_Byte cmd[20], *cp; double pixels_per_mm; memset (cmd, 0, sizeof (cmd)); cmd[0] = MUSTEK_SCSI_SET_WINDOW; if (strcmp (s->hw->sane.model, "1200 SP PRO") == 0) cmd[8] = 0x09; else cmd[8] = 0x0a; cp = cmd + CDB_SIZE (MUSTEK_SCSI_SET_WINDOW); /* skip command block */ *cp++ = 0; /* what's this? */ pixels_per_mm = SANE_UNFIX (s->hw->dpi_range.max) / MM_PER_INCH; /* The next for 16 bit values are x0, y0, x1, y1 in pixels at max res */ STORE16L (cp, SANE_UNFIX (s->val[OPT_TL_X].w) * pixels_per_mm + 0.5); STORE16L (cp, SANE_UNFIX (s->val[OPT_TL_Y].w) * pixels_per_mm + 0.5); STORE16L (cp, SANE_UNFIX (s->val[OPT_BR_X].w) * pixels_per_mm + 0.5); STORE16L (cp, SANE_UNFIX (s->val[OPT_BR_Y].w) * pixels_per_mm + 0.5); if (strcmp (s->hw->sane.model, "1200 SP PRO") != 0) *cp++ = lamp_off_time; /* Only needed for A3 Pro, default: 60 minutes until lamp-off */ DBG (5, "set_window_pro\n"); return dev_cmd (s, cmd, (cp - cmd), 0, 0); } /* Pro series calibration */ static SANE_Status get_calibration_size_pro (Mustek_Scanner * s) { SANE_Status status; SANE_Byte cmd[6]; SANE_Byte result[6]; size_t len; memset (cmd, 0, sizeof (cmd)); memset (result, 0, sizeof (result)); cmd[0] = MUSTEK_SCSI_GET_IMAGE_STATUS; cmd[4] = 0x06; /* size of result */ cmd[5] = 0x80; /* get back buffer size and number of buffers */ len = sizeof (result); status = dev_cmd (s, cmd, sizeof (cmd), result, &len); if (status != SANE_STATUS_GOOD) return status; s->hw->cal.bytes = result[1] | (result[2] << 8); s->hw->cal.lines = result[3] | (result[4] << 8); DBG (4, "get_calibration_size_pro: bytes=%d, lines=%d\n", s->hw->cal.bytes, s->hw->cal.lines); return SANE_STATUS_GOOD; } static SANE_Status get_calibration_lines_pro (Mustek_Scanner * s) { SANE_Status status; SANE_Byte cmd[10]; size_t len; SANE_Int line; DBG (2, "get_calibration_lines_pro: please wait for warmup\n"); memset (cmd, 0, sizeof (cmd)); cmd[0] = MUSTEK_SCSI_READ_DATA; len = s->hw->cal.bytes; cmd[6] = (len >> 16) & 0xff; cmd[7] = (len >> 8) & 0xff; cmd[8] = (len >> 0) & 0xff; for (line = 0; line < s->hw->cal.lines; line++) { status = dev_cmd (s, cmd, CDB_SIZE (MUSTEK_SCSI_READ_DATA), s->hw->cal.buffer + line * len, &len); if ((status != SANE_STATUS_GOOD) || (len != (unsigned int) s->hw->cal.bytes)) { DBG (1, "get_calibration_lines_pro: read failed\n"); return status; } } DBG (5, "get_calibration_lines_pro finished. Assuming 12 bits per color\n"); return SANE_STATUS_GOOD; } static SANE_Status send_calibration_lines_pro (Mustek_Scanner * s) { SANE_Status status; SANE_Byte *cmd1, *cmd2; size_t buf_size; SANE_Word column, line, color; DBG (5, "send_calibration_lines_pro\n"); buf_size = s->hw->cal.bytes / 2; cmd1 = (SANE_Byte *) malloc (buf_size + CDB_SIZE (MUSTEK_SCSI_SEND_DATA)); cmd2 = (SANE_Byte *) malloc (buf_size + CDB_SIZE (MUSTEK_SCSI_SEND_DATA)); if (!cmd1 || !cmd2) { DBG (1, "send_calibration_lines_pro: failed to malloc %zu bytes for " "sending lines\n", buf_size + CDB_SIZE (MUSTEK_SCSI_SEND_DATA)); return SANE_STATUS_NO_MEM; } memset (cmd1, 0, CDB_SIZE (MUSTEK_SCSI_SEND_DATA)); memset (cmd2, 0, CDB_SIZE (MUSTEK_SCSI_SEND_DATA)); cmd1[0] = cmd2[0] = MUSTEK_SCSI_SEND_DATA; cmd1[6] = cmd2[6] = (buf_size >> 16) & 0xff; cmd1[7] = cmd2[7] = (buf_size >> 8) & 0xff; cmd1[8] = cmd2[8] = (buf_size >> 0) & 0xff; cmd1[9] = 0; /* Least significant 8 bits */ cmd2[9] = 0x80; /* Most significant 2 bits */ for (color = 0; color < 3; color++) { for (column = 0; column < s->hw->cal.bytes / 6; column++) { SANE_Word calibration_word = 0; for (line = 0; line < s->hw->cal.lines; line++) { calibration_word += *(s->hw->cal.buffer + column * 6 + color_seq[color] * 2 + 0) + (*(s->hw->cal.buffer + column * 6 + color_seq[color] * 2 + 1) << 8); } if (!calibration_word) calibration_word = 1; calibration_word = (1024 * 65536 / calibration_word) - 1024; if (calibration_word > 1023) calibration_word = 1023; *(cmd1 + CDB_SIZE (MUSTEK_SCSI_SEND_DATA) + (buf_size / 3) * color + column) = calibration_word & 0xff; *(cmd2 + CDB_SIZE (MUSTEK_SCSI_SEND_DATA) + (buf_size / 3) * color + column) = (calibration_word >> 8) & 0xff; } } status = dev_cmd (s, cmd1, buf_size + CDB_SIZE (MUSTEK_SCSI_SEND_DATA), 0, 0); if (status != SANE_STATUS_GOOD) { DBG (1, "send_calibration_lines_pro: send failed\n"); return status; } status = dev_cmd (s, cmd2, buf_size + CDB_SIZE (MUSTEK_SCSI_SEND_DATA), 0, 0); if (status != SANE_STATUS_GOOD) { DBG (1, "send_calibration_lines_pro: send failed\n"); return status; } free (cmd1); free (cmd2); return SANE_STATUS_GOOD; } static SANE_Status calibration_pro (Mustek_Scanner * s) { SANE_Status status; if (s->val[OPT_QUALITY_CAL].w) DBG (4, "calibration_pro: doing calibration\n"); else { DBG (4, "calibration_pro: calibration not necessary\n"); return SANE_STATUS_GOOD; } status = get_calibration_size_pro (s); if (status != SANE_STATUS_GOOD) return status; s->hw->cal.buffer = (SANE_Byte *) malloc (s->hw->cal.bytes * s->hw->cal.lines); if (!s->hw->cal.buffer) { DBG (1, "calibration_pro: failed to malloc %d bytes for buffer\n", s->hw->cal.bytes * s->hw->cal.lines); return SANE_STATUS_NO_MEM; } status = get_calibration_lines_pro (s); if (status != SANE_STATUS_GOOD) return status; status = send_calibration_lines_pro (s); if (status != SANE_STATUS_GOOD) return status; free (s->hw->cal.buffer); return SANE_STATUS_GOOD; } /* ScanExpress series calibration */ static SANE_Status get_calibration_lines_se (Mustek_Scanner * s) { SANE_Status status; SANE_Byte cmd[10]; size_t len; SANE_Word lines, bytes_per_color; if (s->mode == MUSTEK_MODE_COLOR) { lines = s->hw->cal.lines * 3; bytes_per_color = s->hw->cal.bytes / 3; } else { lines = s->hw->cal.lines; bytes_per_color = s->hw->cal.bytes; } DBG (4, "get_calibration_lines_se: reading %d lines (%d bytes per color)\n", lines, bytes_per_color); memset (cmd, 0, sizeof (cmd)); cmd[0] = MUSTEK_SCSI_READ_DATA; cmd[2] = 1; cmd[7] = (lines >> 8) & 0xff; cmd[8] = (lines >> 0) & 0xff; len = lines * bytes_per_color; status = dev_cmd (s, cmd, CDB_SIZE (MUSTEK_SCSI_READ_DATA), s->hw->cal.buffer, &len); if ((status != SANE_STATUS_GOOD) || (len != (unsigned int) (lines * bytes_per_color))) { DBG (1, "get_calibration_lines_se: read failed\n"); return status; } return SANE_STATUS_GOOD; } static SANE_Status send_calibration_lines_se (Mustek_Scanner * s, SANE_Word color) { SANE_Status status; SANE_Byte *cmd; size_t buf_size; SANE_Word column; SANE_Word bytes_per_color; if (s->mode == MUSTEK_MODE_COLOR) { bytes_per_color = s->hw->cal.bytes / 3; } else { bytes_per_color = s->hw->cal.bytes; } buf_size = bytes_per_color; DBG (5, "send_calibration_lines_se: %d bytes, color: %d\n", bytes_per_color, color + 1); cmd = (SANE_Byte *) malloc (buf_size + CDB_SIZE (MUSTEK_SCSI_SEND_DATA)); if (!cmd) { DBG (1, "send_calibration_lines_se: failed to malloc %zu bytes for " "sending lines\n", buf_size + CDB_SIZE (MUSTEK_SCSI_SEND_DATA)); return SANE_STATUS_NO_MEM; } memset (cmd, 0, CDB_SIZE (MUSTEK_SCSI_SEND_DATA)); for (column = 0; column < bytes_per_color; column++) { SANE_Word line; SANE_Word cali_word = 0; SANE_Int color_seq[] = { 2, 0, 1 }; for (line = 0; line < s->hw->cal.lines; line++) cali_word += *(s->hw->cal.buffer + line * bytes_per_color + bytes_per_color * color_seq[color] + column); if (!cali_word) cali_word = 1; cali_word = 256 * s->hw->cal.lines * 255 / cali_word - 256; if (cali_word > 255) cali_word = 255; *(cmd + CDB_SIZE (MUSTEK_SCSI_SEND_DATA) + column) = cali_word; } cmd[0] = MUSTEK_SCSI_SEND_DATA; cmd[2] = 1; cmd[6] = color + 1; cmd[7] = (buf_size >> 8) & 0xff; cmd[8] = (buf_size >> 0) & 0xff; status = dev_cmd (s, cmd, buf_size + CDB_SIZE (MUSTEK_SCSI_SEND_DATA), 0, 0); if (status != SANE_STATUS_GOOD) { DBG (1, "send_calibration_lines_se: send failed\n"); return status; } free (cmd); return SANE_STATUS_GOOD; } static SANE_Status calibration_se (Mustek_Scanner * s) { SANE_Status status; if (!s->val[OPT_QUALITY_CAL].w || s->val[OPT_PREVIEW].w || s->mode == MUSTEK_MODE_LINEART) return SANE_STATUS_GOOD; DBG (4, "calibration_se: doing calibration\n"); s->hw->cal.lines = MIN (s->hw->cal.lines, s->hw->buffer_size / s->hw->cal.bytes); s->hw->cal.buffer = (SANE_Byte *) malloc (s->hw->cal.bytes * s->hw->cal.lines); if (!s->hw->cal.buffer) { DBG (1, "calibration_se: failed to malloc %d bytes for buffer\n", s->hw->cal.bytes * s->hw->cal.lines); return SANE_STATUS_NO_MEM; } status = get_calibration_lines_se (s); if (status != SANE_STATUS_GOOD) return status; if (s->mode == MUSTEK_MODE_GRAY) status = send_calibration_lines_se (s, 0); else { status = send_calibration_lines_se (s, 0); status = send_calibration_lines_se (s, 1); status = send_calibration_lines_se (s, 2); } if (status != SANE_STATUS_GOOD) return status; free (s->hw->cal.buffer); return SANE_STATUS_GOOD; } /* ScanExpress series */ static SANE_Status send_gamma_table_se (Mustek_Scanner * s) { SANE_Status status; SANE_Byte gamma[10 + 4096], *cp; SANE_Int color, factor, val_a, val_b; SANE_Int i, j; # define CLIP(x) ((x) < 0 ? 0 : ((x) > 255 ? 255 : (x))) memset (gamma, 0, CDB_SIZE (MUSTEK_SCSI_SEND_DATA)); gamma[0] = MUSTEK_SCSI_SEND_DATA; gamma[2] = 0x03; /* indicates gamma table */ if ((s->mode & MUSTEK_MODE_GRAY) || (s->mode & MUSTEK_MODE_COLOR)) { if ((size_t) s->hw->gamma_length + CDB_SIZE (MUSTEK_SCSI_SEND_DATA) > sizeof (gamma)) return SANE_STATUS_NO_MEM; gamma[7] = (s->hw->gamma_length >> 8) & 0xff; gamma[8] = (s->hw->gamma_length >> 0) & 0xff; factor = s->hw->gamma_length / 256; color = (s->mode & MUSTEK_MODE_COLOR) ? 1 : 0; do { gamma[6] = color; if (color == 0) { val_a = s->gamma_table[0][1]; val_b = s->gamma_table[0][0]; } else { /* compose intensity gamma and color channel gamma: */ val_a = s->gamma_table[0][s->gamma_table[color][1]]; val_b = s->gamma_table[0][s->gamma_table[color][0]]; } /* Now val_a is extrapolated from [0] and [1] */ val_a = MAX (2 * val_b - val_a, 0); /* Interpolate first entries from 256 entry table */ cp = gamma + CDB_SIZE (MUSTEK_SCSI_SEND_DATA); for (j = 0; j < factor; j++) *cp++ = CLIP (((factor - j) * val_a + j * val_b + factor / 2) / factor); for (i = 1; i < 256; i++) { if (color == 0) { val_a = s->gamma_table[0][i - 1]; val_b = s->gamma_table[0][i]; } else { /* compose intensity gamma and color channel gamma: */ val_a = s->gamma_table[0][s->gamma_table[color][i - 1]]; val_b = s->gamma_table[0][s->gamma_table[color][i]]; } /* Interpolate next entries from the 256 entry table */ for (j = 0; j < factor; j++) *cp++ = CLIP (((factor - j) * val_a + j * val_b + factor / 2) / factor); } DBG (5, "send_gamma_table_se: sending table for color %d\n", gamma[6]); status = dev_cmd (s, gamma, CDB_SIZE (MUSTEK_SCSI_SEND_DATA) + s->hw->gamma_length, 0, 0); ++color; } while ((color != 1) & (color < 4) & (status == SANE_STATUS_GOOD)); return status; } else { /* In lineart mode the threshold is encoded in byte 8 as follows */ /* brightest -> 00 01 02 ... 7F 80 81 82 ... FF <- darkest image */ gamma[6] = 0x04; gamma[8] = 128 - 127 * SANE_UNFIX (s->val[OPT_BRIGHTNESS].w) / 100.0; DBG (5, "send_gamma_table_se: sending lineart threshold %2X\n", gamma[8]); return dev_cmd (s, gamma, CDB_SIZE (MUSTEK_SCSI_SEND_DATA), 0, 0); } } /* Paragon series */ static SANE_Status mode_select_paragon (Mustek_Scanner * s, SANE_Int color_code) { SANE_Int speed_code; SANE_Byte mode[19], *cp; /* calculate funky speed code: */ for (speed_code = 0; speed_list[speed_code]; ++speed_code) { if (strcmp (speed_list[speed_code], s->val[OPT_SPEED].s) == 0) break; } if (speed_code > 4) speed_code = 4; else if (speed_code < 0) speed_code = 0; if (s->hw->flags & MUSTEK_FLAG_THREE_PASS) { speed_code = 5 - speed_code; /* 1 is fast, 5 is slow */ } else { speed_code = 4 - speed_code; /* 0 is fast, 4 is slow */ } memset (mode, 0, sizeof (mode)); mode[0] = MUSTEK_SCSI_MODE_SELECT; /* set command length and resolution code: */ if (s->hw->flags & MUSTEK_FLAG_THREE_PASS) { mode[4] = 0x0b; mode[7] = s->resolution_code; } else { mode[4] = 0x0d; cp = mode + 17; STORE16L (cp, s->resolution_code); } /* set mode byte: */ mode[6] = 0x83 | (color_code << 5); if (!(s->hw->flags & MUSTEK_FLAG_USE_EIGHTS)) mode[6] |= 0x08; if (s->custom_halftone_pattern) mode[6] |= 0x10; if (s->hw->flags & MUSTEK_FLAG_PARAGON_1) { if ((s->mode == MUSTEK_MODE_LINEART) || (s->mode == MUSTEK_MODE_HALFTONE)) { mode[8] = encode_percentage (s, SANE_UNFIX (s->val[OPT_BRIGHTNESS].w)); mode[9] = encode_percentage (s, SANE_UNFIX (s->val[OPT_CONTRAST].w)); } else { mode[8] = 0x0c; mode[9] = 0x0c; } mode[10] = 2; /* grain */ if (s->val[OPT_PREVIEW].w && s->val[OPT_FAST_PREVIEW].w) mode[11] = 0x01; else if ((s->mode == MUSTEK_MODE_COLOR) || (s->mode == MUSTEK_MODE_HALFTONE)) mode[11] = 0x00; /* speed */ else mode[11] = 0x02; /* speed */ mode[12] = 0x00; /* shadow param not used by Mustek */ mode[13] = 0xff; /* highlight only used by some scanners */ mode[14] = 0x70; /* paper- */ mode[15] = 0x00; /* length */ mode[16] = 0x53; /* midtone param not used by Mustek */ } else if (s->hw->flags & MUSTEK_FLAG_PARAGON_2) { mode[8] = encode_percentage (s, SANE_UNFIX (s->val[OPT_BRIGHTNESS].w)); mode[9] = encode_percentage (s, SANE_UNFIX (s->val[OPT_CONTRAST].w)); mode[10] = 2; /* grain */ if ((s->mode == MUSTEK_MODE_COLOR) || (s->mode == MUSTEK_MODE_HALFTONE)) mode[11] = 0x00; /* speed */ else mode[11] = 0x02; /* speed */ mode[12] = 0x00; /* shadow param not used by Mustek */ mode[13] = 0x00; /* highlight param not used by Mustek */ mode[14] = 0x5c; /* paper- */ mode[15] = 0x00; /* length */ mode[16] = 0x41; /* midtone param not used by Mustek */ } else if (s->hw->flags & MUSTEK_FLAG_THREE_PASS) { if (s->mode & MUSTEK_MODE_COLOR) { mode[8] = encode_percentage (s, SANE_UNFIX (s->val[OPT_BRIGHTNESS + s->pass + 1].w - 1)); mode[9] = encode_percentage (s, SANE_UNFIX (s->val[OPT_CONTRAST + s->pass + 1].w - 1)); } else { mode[8] = encode_percentage (s, SANE_UNFIX (s->val[OPT_BRIGHTNESS].w - 1)); mode[9] = encode_percentage (s, SANE_UNFIX (s->val[OPT_CONTRAST].w - 1)); } mode[10] = s->halftone_pattern_type; mode[11] = speed_code; /* lamp setting not supported yet */ mode[12] = 0; /* shadow param not used by Mustek */ mode[13] = 0; /* highlight param not used by Mustek */ mode[14] = mode[15] = 0; /* paperlength not used by Mustek */ mode[16] = 0; /* midtone param not used by Mustek */ } else { mode[8] = encode_percentage (s, SANE_UNFIX (s->val[OPT_BRIGHTNESS].w)); mode[9] = encode_percentage (s, SANE_UNFIX (s->val[OPT_CONTRAST].w)); mode[10] = s->halftone_pattern_type; mode[11] = speed_code; /* lamp setting not supported yet */ mode[12] = 0; /* shadow param not used by Mustek */ mode[13] = 0; /* highlight param not used by Mustek */ mode[14] = mode[15] = 0; /* paperlength not used by Mustek */ mode[16] = 0; /* midtone param not used by Mustek */ } DBG (5, "mode_select: resolution_code=%d (0x%x)\n", s->resolution_code, s->resolution_code); return dev_cmd (s, mode, 6 + mode[4], 0, 0); } /* Pro series */ static SANE_Status mode_select_pro (Mustek_Scanner * s) { SANE_Byte mode[19], *cp; memset (mode, 0, sizeof (mode)); mode[0] = MUSTEK_SCSI_MODE_SELECT; mode[4] = 0x0d; if (s->mode & MUSTEK_MODE_COLOR) { if (strcmp (s->val[OPT_BIT_DEPTH].s, "12") == 0) mode[6] = 0xE0; else mode[6] = 0x60; } else if (s->mode & MUSTEK_MODE_GRAY) { if (s->val[OPT_FAST_GRAY_MODE].w) mode[6] = 0x20; else mode[6] = 0x40; } else mode[6] = 0x00; /* lineart */ mode[7] = 0; mode[8] = 0; mode[9] = 0; mode[10] = 0; mode[11] = 0x00; mode[12] = 0x27; mode[13] = 0xb0; mode[14] = 0x04; mode[15] = 0x43; mode[16] = 0x41; cp = mode + 17; STORE16L (cp, s->resolution_code); DBG (5, "mode_select_pro: resolution_code=%d (0x%x), mode=0x%x\n", s->resolution_code, s->resolution_code, mode[6]); return dev_cmd (s, mode, 6 + mode[4], 0, 0); } /* Paragon and Pro series. According to Mustek, the only builtin gamma table is a linear table, so all we support here is user-defined gamma tables. */ static SANE_Status gamma_correction (Mustek_Scanner * s, SANE_Int color_code) { SANE_Int i, j, table = 0, len = 0, bytes_per_channel, num_channels = 1; SANE_Byte gamma[4096 + 10], val, *cp; /* for Paragon models 3 x 256 is the maximum. Pro needs 4096 bytes */ if ((s->hw->flags & MUSTEK_FLAG_N) && ((s->mode & MUSTEK_MODE_LINEART) || (s->mode & MUSTEK_MODE_HALFTONE))) { /* sigh! - the 600 II N needs a (dummy) table download even for lineart and halftone mode, else it produces a completely white image. Thank Mustek for their buggy firmware ! */ memset (gamma, 0, sizeof (gamma)); gamma[0] = MUSTEK_SCSI_LOOKUP_TABLE; gamma[2] = 0x0; /* indicate any preloaded gamma table */ DBG (5, "gamma_correction: sending dummy gamma table\n"); return dev_cmd (s, gamma, 6, 0, 0); } if (((s->mode & MUSTEK_MODE_LINEART) || (s->mode & MUSTEK_MODE_HALFTONE)) && !(s->hw->flags & MUSTEK_FLAG_PRO)) { DBG (5, "gamma_correction: nothing to do in lineart mode -- exiting\n"); return SANE_STATUS_GOOD; } if ((!s->val[OPT_CUSTOM_GAMMA].w) && (!(s->hw->flags & MUSTEK_FLAG_PRO))) { /* Do we need to upload a gamma table even if the user didn't select this option? Some scanners need this work around. */ if (!(s->hw->flags & MUSTEK_FLAG_FORCE_GAMMA) || !((s->mode & MUSTEK_MODE_COLOR) || (s->mode & MUSTEK_MODE_GRAY))) { DBG (5, "gamma_correction: no custom table selected -- exititing\n"); return SANE_STATUS_GOOD; } } if (s->mode & MUSTEK_MODE_COLOR) { table = 1; if (s->hw->flags & MUSTEK_FLAG_THREE_PASS) table += s->pass; else { if ((color_code == MUSTEK_CODE_GRAY) && !(s->hw->flags & MUSTEK_FLAG_PRO)) num_channels = 3; else table = color_code; } } else if (s->hw->flags & MUSTEK_FLAG_N) { /* it seems 600 II N (firmware 1.x at least) wants 768 bytes in * gray mode too */ num_channels = 3; } memset (gamma, 0, sizeof (gamma)); gamma[0] = MUSTEK_SCSI_LOOKUP_TABLE; if (s->hw->flags & MUSTEK_FLAG_PRO) { bytes_per_channel = 4096; len = bytes_per_channel; if (s->mode == MUSTEK_MODE_COLOR) { gamma[9] = (color_code << 6); /* color */ if (strcmp (s->val[OPT_BIT_DEPTH].s, "12") == 0) gamma[2] = 0x7f; /* medium brightness */ } else if (s->mode == MUSTEK_MODE_GRAY) { gamma[9] = 0x80; /* grayscale */ if (s->val[OPT_FAST_GRAY_MODE].w) gamma[2] = 0x7f; /* medium brightness */ } else /* lineart */ { gamma[2] = 128 - 127 * SANE_UNFIX (s->val[OPT_BRIGHTNESS].w) / 100.0; gamma[9] = 0x80; /* grayscale/lineart */ DBG (5, "gamma_correction: sending brightness information\n"); } gamma[7] = (len >> 8) & 0xff; /* big endian! */ gamma[8] = (len >> 0) & 0xff; } else { bytes_per_channel = 256; len = num_channels * bytes_per_channel; gamma[2] = 0x27; /* indicate user-selected gamma table */ if (s->hw->flags & MUSTEK_FLAG_N) { /* 600 II N always uses 6-byte cdb */ gamma[3] = (len >> 8) & 0xff; /* big endian! */ gamma[4] = (len >> 0) & 0xff; /* no way to pass color_code (?) */ } else { gamma[7] = (len >> 8) & 0xff; /* big endian! */ gamma[8] = (len >> 0) & 0xff; gamma[9] = (color_code << 6); } } if (len > 0) { cp = gamma + 10; for (j = 0; j < num_channels; ++j) { for (i = 0; i < bytes_per_channel; ++i) { if (s->val[OPT_CUSTOM_GAMMA].w == SANE_TRUE) val = s->gamma_table[table][i * 256 / bytes_per_channel]; else val = i * 256 / bytes_per_channel; if ((s->mode & MUSTEK_MODE_COLOR) && (s->val[OPT_CUSTOM_GAMMA].w == SANE_TRUE)) /* compose intensity gamma and color channel gamma: */ val = s->gamma_table[0][val]; *cp++ = val; } if (!(s->hw->flags & MUSTEK_FLAG_N) || !(s->mode & MUSTEK_MODE_GRAY)) table++; } } DBG (5, "gamma_correction: sending gamma table of %d bytes\n", len); return dev_cmd (s, gamma, 10 + len, 0, 0); } static SANE_Status send_gamma_table (Mustek_Scanner * s) { SANE_Status status; if (s->one_pass_color_scan) { if (s->hw->flags & MUSTEK_FLAG_N) /* This _should_ work for all one-pass scanners (not just AB306N scanners), but it doesn't work for my Paragon 600 II SP with firmware rev 1.01. Too bad, since it would simplify the gamma correction code quite a bit. */ status = gamma_correction (s, MUSTEK_CODE_GRAY); else { status = gamma_correction (s, MUSTEK_CODE_RED); if (status != SANE_STATUS_GOOD) return status; status = gamma_correction (s, MUSTEK_CODE_GREEN); if (status != SANE_STATUS_GOOD) return status; status = gamma_correction (s, MUSTEK_CODE_BLUE); } } else status = gamma_correction (s, MUSTEK_CODE_GRAY); return status; } /* ScanExpress and Paragon series */ static SANE_Status start_scan (Mustek_Scanner * s) { SANE_Byte start[6]; SANE_Status status; memset (start, 0, sizeof (start)); start[0] = MUSTEK_SCSI_START_STOP; start[4] = 0x01; DBG (4, "start_scan\n"); /* ScanExpress and Pro models don't have any variants */ if (!(s->hw->flags & MUSTEK_FLAG_SE) && !(s->hw->flags & MUSTEK_FLAG_PRO)) { if (s->mode & MUSTEK_MODE_COLOR) { if (s->hw->flags & MUSTEK_FLAG_THREE_PASS) start[4] |= ((s->pass + 1) << 3); else start[4] |= 0x20; } /* or in single/multi bit: */ start[4] |= ((s->mode & MUSTEK_MODE_LINEART) || (s->mode & MUSTEK_MODE_HALFTONE)) ? 0 : (1 << 6); /* or in expanded resolution bit: */ if (s->val[OPT_RESOLUTION].w > (s->hw->dpi_range.max / 2) && ((s->hw->flags & MUSTEK_FLAG_THREE_PASS) || (s->hw->flags & MUSTEK_FLAG_PARAGON_1) || (s->hw->flags & MUSTEK_FLAG_PARAGON_2))) start[4] |= 1 << 7; /* block mode (or whatever) */ if (s->hw->flags & MUSTEK_FLAG_USE_BLOCK) { start[5] = 0x08; DBG (4, "start_scan: using block mode\n"); } } status = dev_cmd (s, start, sizeof (start), 0, 0); if (status != SANE_STATUS_GOOD) DBG (1, "start_scan returned status %s\n", sane_strstatus (status)); return status; } static SANE_Status do_eof (Mustek_Scanner * s) { if (s->pipe >= 0) { close (s->pipe); s->pipe = -1; DBG (5, "do_eof: closing pipe\n"); } return SANE_STATUS_EOF; } static SANE_Status do_stop (Mustek_Scanner * s) { SANE_Status status = SANE_STATUS_GOOD; DBG (5, "do_stop\n"); if (s->cancelled) status = SANE_STATUS_CANCELLED; s->scanning = SANE_FALSE; s->pass = 0; if (sanei_thread_is_valid (s->reader_pid)) { SANE_Int exit_status; struct timeval now; long int scan_time; long int scan_size; SANE_Pid pid; /* print scanning time */ gettimeofday (&now, 0); scan_time = now.tv_sec - s->start_time; if (scan_time < 1) scan_time = 1; scan_size = s->hw->bpl * s->hw->lines / 1024; DBG (2, "Scanning time was %ld seconds, %ld kB/s\n", scan_time, scan_size / scan_time); if (s->total_bytes == s->params.lines * s->params.bytes_per_line) DBG (3, "Scanned %d bytes as expected\n", s->total_bytes); else if (s->total_bytes < s->params.lines * s->params.bytes_per_line) DBG (3, "Scanned %d bytes, expected %d bytes\n", s->total_bytes, s->params.lines * s->params.bytes_per_line); else DBG (1, "Warning: Scanned %d bytes, but expected only %d bytes\n", s->total_bytes, s->params.lines * s->params.bytes_per_line); /* ensure child knows it's time to stop: */ DBG (5, "do_stop: terminating reader process\n"); sanei_thread_kill (s->reader_pid); pid = sanei_thread_waitpid (s->reader_pid, &exit_status); if (!sanei_thread_is_valid (pid)) { DBG (1, "do_stop: sanei_thread_waitpid failed, already terminated? (%s)\n", strerror (errno)); } else { DBG (2, "do_stop: reader process terminated with status %s\n", sane_strstatus (exit_status)); if (status != SANE_STATUS_CANCELLED && exit_status != SANE_STATUS_GOOD) status = exit_status; } sanei_thread_invalidate (s->reader_pid); } if (s->fd >= 0) { if (!sanei_thread_is_forked ()) sanei_scsi_req_flush_all (); /* flush SCSI queue */ if (s->hw->flags & MUSTEK_FLAG_PRO) { if (s->total_bytes < s->params.lines * s->params.bytes_per_line) status = dev_cmd (s, scsi_start_stop, sizeof (scsi_start_stop), 0, 0); dev_wait_ready (s); } else if ((s->hw->flags & MUSTEK_FLAG_PARAGON_1) || (s->hw->flags & MUSTEK_FLAG_PARAGON_2) || (s->hw->flags & MUSTEK_FLAG_THREE_PASS)) { if (s->cancelled && (s->total_bytes < s->params.lines * s->params.bytes_per_line)) status = dev_cmd (s, scsi_start_stop, sizeof (scsi_start_stop), 0, 0); } else status = dev_cmd (s, scsi_start_stop, sizeof (scsi_start_stop), 0, 0); if (force_wait) { DBG (5, "do_stop: waiting for scanner to be ready\n"); dev_wait_ready (s); } do_eof (s); DBG (5, "do_stop: closing scanner\n"); dev_close (s); s->fd = -1; } DBG (5, "do_stop: finished\n"); return status; } /* Paragon I + II: Determine the CCD's distance between the primary color lines. */ static SANE_Status line_distance (Mustek_Scanner * s) { SANE_Int factor, color, res, peak_res; SANE_Status status; SANE_Byte result[5]; size_t len; memset (result, 0, 5); res = SANE_UNFIX (s->val[OPT_RESOLUTION].w) + 0.5; peak_res = SANE_UNFIX (s->hw->dpi_range.max) + 0.5; s->ld.buf[0] = NULL; len = sizeof (result); status = dev_cmd (s, scsi_ccd_distance, sizeof (scsi_ccd_distance), result, &len); if (status != SANE_STATUS_GOOD) return status; DBG (3, "line_distance: got factor=%d, (r/g/b)=(%d/%d/%d)\n", result[0] | (result[1] << 8), result[2], result[3], result[4]); if (s->hw->flags & MUSTEK_FLAG_LD_FIX) { result[0] = 0xff; result[1] = 0xff; if (s->mode & MUSTEK_MODE_COLOR) { if (s->hw->flags & MUSTEK_FLAG_N) { /* According to Andreas Czechanowski, the line-distance values returned for the AB306N scanners are garbage, so we have to fix things up manually. Not good. This seems to be true only for firmware 2.00 which is extremely seldom.. AB306N scanners with firmware 1.01 don't need this fix. */ if (peak_res == 600) { if (res < 51) { result[0] = 8; result[1] = 0; result[2] = 0; result[3] = 2; result[4] = 3; } else if (res < 75 || (res > 90 && res < 150)) { /* 51-74 and 91-149 dpi: */ result[0] = 4; result[1] = 0; result[2] = 0; result[3] = 3; result[4] = 5; } else if (res <= 90 || (res >= 150 && res <= 300)) { /* 75-90 and 150-300 dpi: */ result[0] = 2; result[1] = 0; result[2] = 0; result[3] = 5; result[4] = 9; } else { /* 301-600 dpi: */ result[0] = 1; result[1] = 0; result[2] = 0; result[3] = 9; result[4] = 23; } } else DBG (1, "don't know how to fix up line-distance for %d dpi\n", peak_res); } else if (!(s->hw->flags & MUSTEK_FLAG_LD_NONE)) { if (peak_res == 600) { if (res < 51) { /* 1-50 dpi: */ result[0] = 4; result[1] = 0; result[2] = 0; result[3] = 3; result[4] = 5; } else if (res <= 300) { /* 51-300 dpi: */ result[0] = 2; result[1] = 0; result[2] = 0; result[3] = 5; result[4] = 9; } else { /*301-600 dpi: */ result[0] = 1; result[1] = 0; result[2] = 0; result[3] = 9; result[4] = 17; } } else if (peak_res == 800) { if (res < 72) { /* 1-71 dpi: */ result[0] = 4; result[1] = 0; result[2] = 0; result[3] = 3; result[4] = 5; } else if (res <= 400) { /* 72-400 dpi: */ result[0] = 2; result[1] = 0; result[2] = 0; result[3] = 9; result[4] = 17; } else { /*401-800 dpi: */ result[0] = 1; result[1] = 0; result[2] = 0; result[3] = 16; result[4] = 32; } } } } DBG (4, "line_distance: fixed up to factor=%d, (r/g/b)=(%d/%d/%d)\n", result[0] | (result[1] << 8), result[2], result[3], result[4]); } factor = result[0] | (result[1] << 8); if (factor != 0xffff) { /* need to do line-distance adjustment ourselves... */ s->ld.max_value = peak_res; if (factor == 0) { if (res <= peak_res / 2) res *= 2; } else res *= factor; s->ld.peak_res = res; for (color = 0; color < 3; ++color) { s->ld.dist[color] = result[2 + color]; s->ld.quant[color] = s->ld.max_value; s->ld.index[color] = -s->ld.dist[color]; } s->ld.lmod3 = -1; DBG (4, "line_distance: max_value = %d, peak_res = %d, ld.quant = " "(%d, %d, %d)\n", s->ld.max_value, s->ld.peak_res, s->ld.quant[0], s->ld.quant[1], s->ld.quant[2]); } else s->ld.max_value = 0; return SANE_STATUS_GOOD; } /* Paragon + Pro series */ static SANE_Status get_image_status (Mustek_Scanner * s, SANE_Int * bpl, SANE_Int * lines) { SANE_Byte result[6]; SANE_Status status; size_t len; SANE_Int busy, offset; long res, half_res; memset (result, 0, 6); /* The 600 II N v1.01 and Paragon 12000SP need a larger scan-area for line-distance correction in color mode */ offset = 0; if ((s->hw->flags & MUSTEK_FLAG_LD_N1) && (s->mode & MUSTEK_MODE_COLOR)) offset = s->ld.dist[1]; else if ((s->hw->flags & MUSTEK_FLAG_LD_BLOCK) && (s->hw->flags & MUSTEK_FLAG_PARAGON_1) && (s->mode & MUSTEK_MODE_COLOR)) offset = MAX_LINE_DIST * SANE_UNFIX (s->val[OPT_RESOLUTION].w) / SANE_UNFIX (s->hw->dpi_range.max); do { len = sizeof (result); status = dev_cmd (s, scsi_get_image_status, sizeof (scsi_get_image_status), result, &len); if (status != SANE_STATUS_GOOD) return status; busy = result[0]; if (busy) usleep (100000); if (!s->scanning) /* ? */ if (!(s->hw->flags & MUSTEK_FLAG_PRO)) return do_stop (s); } while (busy); s->hw->bpl = result[1] | (result[2] << 8); s->hw->lines = result[3] | (result[4] << 8) | (result[5] << 16); res = SANE_UNFIX (s->val[OPT_RESOLUTION].w); half_res = SANE_UNFIX (s->hw->dpi_range.max) / 2; /* Need to interpolate resolutions > max x-resolution? */ if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res)) { *bpl = (s->hw->bpl * res) / half_res / 3; *bpl *= 3; DBG (4, "get_image_status: resolution > x-max; enlarge %d bpl to " "%d bpl\n", s->hw->bpl, *bpl); } else *bpl = s->hw->bpl; *lines = s->hw->lines - offset; DBG (3, "get_image_status: bytes_per_line=%d, lines=%d (offset = %d)\n", *bpl, *lines, offset); return SANE_STATUS_GOOD; } /* ScanExpress models */ static SANE_Status get_window (Mustek_Scanner * s, SANE_Int * bpl, SANE_Int * lines, SANE_Int * pixels) { SANE_Byte result[48]; SANE_Status status; size_t len; SANE_Int color; long res, half_res; res = s->resolution_code; half_res = SANE_UNFIX (s->hw->dpi_range.max) / 2; DBG (5, "get_window: resolution: %ld dpi (hardware: %d dpi)\n", res, s->ld.peak_res); len = sizeof (result); status = dev_cmd (s, scsi_get_window, sizeof (scsi_get_window), result, &len); if (status != SANE_STATUS_GOOD) return status; if (!s->scanning) return do_stop (s); s->hw->cal.bytes = (result[6] << 24) | (result[7] << 16) | (result[8] << 8) | (result[9] << 0); s->hw->cal.lines = (result[10] << 24) | (result[11] << 16) | (result[12] << 8) | (result[13] << 0); DBG (4, "get_window: calibration bpl=%d, lines=%d\n", s->hw->cal.bytes, s->hw->cal.lines); s->hw->bpl = (result[14] << 24) | (result[15] << 16) | (result[16] << 8) | result[17]; s->hw->lines = (result[18] << 24) | (result[19] << 16) | (result[20] << 8) | result[21]; DBG (4, "get_window: scan bpl=%d, lines=%d\n", s->hw->bpl, s->hw->lines); if ((s->hw->cal.bytes == 0) || (s->hw->cal.lines == 0) || (s->hw->bpl == 0) || (s->hw->lines == 0)) { DBG (1, "get_window: oops, none of these values should be 0 " "-- exiting\n"); return SANE_STATUS_INVAL; } s->hw->gamma_length = 1 << result[26]; DBG (4, "get_window: gamma length=%d\n", s->hw->gamma_length); if (s->mode & MUSTEK_MODE_COLOR) { s->ld.buf[0] = NULL; for (color = 0; color < 3; ++color) { s->ld.dist[color] = result[42 + color]; } DBG (4, "get_window: LD res=%d, (r/g/b)=(%d/%d/%d)\n", (result[40] << 8) | result[41], s->ld.dist[0], s->ld.dist[1], s->ld.dist[2]); s->ld.max_value = (result[40] << 8) | result[41]; if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res)) { /* We must interpolate resolutions > max x-resolution */ *bpl = *pixels = (((s->hw->bpl / 3) * res) / half_res) * 3; } else { /* Scale down the image according to desired resolution */ *bpl = *pixels = (((s->hw->bpl / 3) * res) / s->ld.peak_res) * 3; } *lines = (s->hw->lines - s->ld.dist[2]) * res / s->ld.peak_res; } else { if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res)) { /* We must interpolate resolutions > max x-resolution */ *bpl = s->hw->bpl * res / half_res; } else { *bpl = s->hw->bpl; } *lines = s->hw->lines; } DBG (4, "get_window: bpl = %d (hardware: %d), lines = %d (hardware: %d)\n", *bpl, s->hw->bpl, *lines, s->hw->lines); return SANE_STATUS_GOOD; } static SANE_Status adf_and_backtrack (Mustek_Scanner * s) { SANE_Byte backtrack[6]; SANE_Int code = 0x80; if (!(s->hw->flags & MUSTEK_FLAG_NO_BACKTRACK)) code |= 0x02; /* enable backtracking */ if (strcmp (s->val[OPT_SOURCE].s, "Automatic Document Feeder") == 0) code |= 0x01; else if (strcmp (s->val[OPT_SOURCE].s, "Transparency Adapter") == 0) code |= 0x04; memset (backtrack, 0, sizeof (backtrack)); backtrack[0] = MUSTEK_SCSI_ADF_AND_BACKTRACK; backtrack[4] = code; DBG (4, "adf_and_backtrack: backtrack: %s; ADF: %s; TA: %s\n", code & 0x02 ? "yes" : "no", code & 0x01 ? "yes" : "no", code & 0x04 ? "yes" : "no"); return dev_cmd (s, backtrack, sizeof (backtrack), 0, 0); } /* 600 II N firmware 2.x */ static SANE_Int fix_line_distance_n_2 (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl, SANE_Byte * raw, SANE_Byte * out) { SANE_Byte *out_end, *out_ptr, *raw_end = raw + num_lines * bpl; SANE_Int c, num_saved_lines, line; if (!s->ld.buf[0]) { /* This buffer must be big enough to hold maximum line distance times max_bpl bytes. The maximum line distance for the Paragon 600 II N scanner is 23, so 40 should be safe. */ DBG (5, "fix_line_distance_n_2: allocating temp buffer of %d*%d bytes\n", MAX_LINE_DIST, bpl); s->ld.buf[0] = malloc (MAX_LINE_DIST * (long) bpl); if (!s->ld.buf[0]) { DBG (1, "fix_line_distance_n_2: failed to malloc temporary buffer\n"); return 0; } } num_saved_lines = s->ld.index[0] - s->ld.index[2]; if (num_saved_lines > 0) /* restore the previously saved lines: */ memcpy (out, s->ld.buf[0], num_saved_lines * bpl); while (1) { if (++s->ld.lmod3 >= 3) s->ld.lmod3 = 0; c = color_seq[s->ld.lmod3]; if (s->ld.index[c] < 0) ++s->ld.index[c]; else if (s->ld.index[c] < s->params.lines) { s->ld.quant[c] += s->ld.peak_res; if (s->ld.quant[c] > s->ld.max_value) { s->ld.quant[c] -= s->ld.max_value; line = s->ld.index[c]++ - s->ld.ld_line; out_ptr = out + line * bpl + c; out_end = out_ptr + bpl; while (out_ptr != out_end) { *out_ptr = *raw++; out_ptr += 3; } if (raw >= raw_end) { DBG (3, "fix_line_distance_n_2: lmod3=%d, " "index=(%d,%d,%d)\n", s->ld.lmod3, s->ld.index[0], s->ld.index[1], s->ld.index[2]); num_lines = s->ld.index[2] - s->ld.ld_line; /* copy away the lines with at least one missing color component, so that we can interleave them with new scan data on the next call */ num_saved_lines = s->ld.index[0] - s->ld.index[2]; memcpy (s->ld.buf[0], out + num_lines * bpl, num_saved_lines * bpl); /* notice the number of lines we processed */ s->ld.ld_line = s->ld.index[2]; /* return number of complete (r+g+b) lines */ return num_lines; } } } } } /* 600 II N firmware 1.x */ static SANE_Int fix_line_distance_n_1 (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl, SANE_Byte * raw, SANE_Byte * out) { SANE_Byte *out_end, *out_ptr, *raw_end = raw + num_lines * bpl; SANE_Int c, num_saved_lines, line; /* For firmware 1.x the scanarea must be soemwhat bigger than needed because of the linedistance correction */ if (!s->ld.buf[0]) { /* This buffer must be big enough to hold maximum line distance times max_bpl bytes. The maximum line distance for the 600 II N is 23, so 40 is safe. */ DBG (5, "fix_line_distance_n_1: allocating temp buffer of %d*%d bytes\n", MAX_LINE_DIST, bpl); s->ld.buf[0] = malloc (MAX_LINE_DIST * (long) bpl); if (!s->ld.buf[0]) { DBG (1, "fix_line_distance_n_1: failed to malloc temporary buffer\n"); return 0; } } num_saved_lines = s->ld.index[0] - s->ld.index[1]; DBG (5, "fix_line_distance_n_1: got %d lines, %d bpl\n", num_lines, bpl); DBG (5, "fix_line_distance_n_1: num_saved_lines = %d; peak_res = %d; " "max_value = %d\n", num_saved_lines, s->ld.peak_res, s->ld.max_value); if (num_saved_lines > 0) /* restore the previously saved lines: */ memcpy (out, s->ld.buf[0], num_saved_lines * bpl); while (1) { if (++s->ld.lmod3 >= 3) s->ld.lmod3 = 0; c = s->ld.lmod3; if (s->ld.index[c] < 0) ++s->ld.index[c]; else { s->ld.quant[c] += s->ld.peak_res; if (s->ld.quant[c] > s->ld.max_value) { s->ld.quant[c] -= s->ld.max_value; line = s->ld.index[c]++ - s->ld.ld_line; out_ptr = out + line * bpl + c; out_end = out_ptr + bpl; while (out_ptr != out_end) { *out_ptr = *raw++; out_ptr += 3; } DBG (5, "fix_line_distance_n_1: copied line %d (color %d)\n", line, c); } } if ((raw >= raw_end) || ((s->ld.index[0] >= s->params.lines) && (s->ld.index[1] >= s->params.lines) && (s->ld.index[2] >= s->params.lines))) { DBG (3, "fix_line_distance_n_1: lmod3=%d, index=(%d,%d,%d)%s\n", s->ld.lmod3, s->ld.index[0], s->ld.index[1], s->ld.index[2], raw >= raw_end ? " raw >= raw_end" : ""); num_lines = s->ld.index[1] - s->ld.ld_line; if (num_lines < 0) num_lines = 0; DBG (4, "fix_line_distance_n_1: lines ready: %d\n", num_lines); /* copy away the lines with at least one missing color component, so that we can interleave them with new scan data on the next call */ num_saved_lines = s->ld.index[0] - s->ld.index[1]; DBG (4, "fix_line_distance_n_1: copied %d lines to " "ld.buf\n", num_saved_lines); memcpy (s->ld.buf[0], out + num_lines * bpl, num_saved_lines * bpl); /* notice the number of lines we processed */ s->ld.ld_line = s->ld.index[1]; if (s->ld.ld_line < 0) s->ld.ld_line = 0; /* return number of complete (r+g+b) lines */ return num_lines; } } } /* For ScanExpress models */ static SANE_Int fix_line_distance_se (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl, SANE_Byte * raw, SANE_Byte * out) { SANE_Byte *raw_end = raw + num_lines * bpl; SANE_Byte *out_ptr[3], *ptr; SANE_Int index[3], lines[3], quant[3], dist[3]; SANE_Int max_value; SANE_Int color, pixel, res, half_res, scale; SANE_Int bpc = bpl / 3; /* bytes per color (per line) */ SANE_Bool preview = SANE_FALSE; res = s->resolution_code; half_res = SANE_UNFIX (s->hw->dpi_range.max) / 2; max_value = s->ld.max_value; if ((s->val[OPT_PREVIEW].w == SANE_TRUE) && (s->val[OPT_FAST_PREVIEW].w == SANE_TRUE)) { preview = SANE_TRUE; /*max_value = 75; */ dist[0] = s->ld.dist[0]; dist[1] = s->ld.dist[1]; dist[2] = s->ld.dist[2]; } else { dist[0] = s->ld.dist[0]; dist[1] = s->ld.dist[1]; dist[2] = s->ld.dist[2]; } if (!s->ld.buf[0]) { /* This buffer must be big enough to hold maximum line distance times 3*bpl bytes. The maximum line distance for 1200 dpi is 32 */ DBG (5, "fix_line_distance_se: allocating temp buffer of %d*%d bytes\n", 3 * MAX_LINE_DIST, bpc); s->ld.buf[0] = malloc (3 * MAX_LINE_DIST * (long) bpc); if (!s->ld.buf[0]) { DBG (1, "fix_line_distance_se: failed to malloc temporary buffer\n"); return 0; } /* Note that either s->ld.buf[1] or s->ld.buf[2] is never used. */ s->ld.buf[1] = s->ld.buf[2] = s->ld.buf[0] + 2 * MAX_LINE_DIST * (long) bpc; /* Since the blocks don't start necessarily with red note color. */ s->ld.color = 0; /* The scan area must be longer than desired because of the line distance. So me must count complete (r+g+b) lines already submitted to the fronted. */ s->ld.ld_line = s->params.lines; for (color = 0; color < 3; ++color) { s->ld.index[color] = -dist[color]; s->ld.quant[color] = 0; s->ld.saved[color] = 0; } } num_lines *= 3; DBG (5, "fix_line_distance_se: start color: %d; %d lines \n", s->ld.color, num_lines); /* First scan the lines read and count red, green and blue ones. Since we will step through the lines a second time we must not alter any global variables here! */ for (color = 0; color < 3; ++color) { index[color] = s->ld.index[color]; lines[color] = s->ld.saved[color]; quant[color] = s->ld.quant[color]; } color = s->ld.color; while (num_lines > 0) { if (index[color] < 0) ++index[color]; else { quant[color] += res; if (quant[color] >= max_value) { /* This line must be processed, not dropped. */ quant[color] -= max_value; ++lines[color]; --num_lines; } else if (!preview) --num_lines; } if (++color > 2) color = 0; } /* Calculate how many triples of color lines we can output now. Because the number of available red lines is always greater than for the other colors we may ignore the red ones here. */ num_lines = MIN (lines[1], lines[2]); DBG (5, "fix_line_distance_se: saved lines: %d/%d/%d\n", s->ld.saved[0], s->ld.saved[1], s->ld.saved[2]); DBG (5, "fix_line_distance_se: available: %d/%d/%d --> triples: %d\n", lines[0], lines[1], lines[2], num_lines); lines[0] = lines[1] = lines[2] = num_lines; /* Output the color lines saved in previous call first. Note that data is converted in r/g/b interleave on the fly. */ for (color = 0; color < 3; ++color) { out_ptr[color] = out + color; ptr = s->ld.buf[color]; while ((s->ld.saved[color] > 0) && (lines[color] > 0)) { scale = 0; if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res)) { /* Need to enlarge x-resolution */ SANE_Byte *ptr_start = ptr; for (pixel = 0; pixel < s->params.pixels_per_line; ++pixel) { *out_ptr[color] = *ptr; out_ptr[color] += 3; scale += half_res; if (scale >= half_res) { scale -= res; ++ptr; } } DBG (5, "fix_line_distance_se: got saved line: %d; line: %d; " "color: %d; raw bytes: %lu; out bytes: %d\n", s->ld.saved[color], lines[color], color, (u_long) (ptr - ptr_start), s->params.pixels_per_line); ptr = ptr_start + bpc; } else { if (preview) { for (pixel = 0; pixel < bpc; ++pixel) { *out_ptr[color] = *ptr++; out_ptr[color] += 3; } } else { for (pixel = 0; pixel < bpc; ++pixel) { scale += res; if (scale >= max_value) { scale -= max_value; *out_ptr[color] = *ptr; out_ptr[color] += 3; } ++ptr; } } DBG (5, "fix_line_distance_se: got saved line: %d; line: %d; " "color: %d\n", s->ld.saved[color], lines[color], color); } --(s->ld.saved[color]); --lines[color]; } if (s->ld.saved[color] > 0) memmove (s->ld.buf[color], ptr, s->ld.saved[color] * bpc); } while (1) { if (s->ld.index[s->ld.color] < 0) ++(s->ld.index[s->ld.color]); else { s->ld.quant[s->ld.color] += res; if (s->ld.quant[s->ld.color] >= max_value) { /* This line must be processed, not dropped. */ s->ld.quant[s->ld.color] -= max_value; if (lines[s->ld.color] > 0) { /* There's still a line to be output for current color. Then shuffle current color line to output buffer. */ scale = 0; /* need to enlarge x-resolution? */ if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res)) { SANE_Byte *raw_start = raw; for (pixel = 0; pixel < s->params.pixels_per_line; ++pixel) { *out_ptr[s->ld.color] = *raw; out_ptr[s->ld.color] += 3; scale += half_res; if (scale >= half_res) { scale -= res; ++raw; } } DBG (5, "fix_line_distance_se: got line: %d; color: %d; " "raw bytes: %lu; out bytes: %d\n", lines[s->ld.color], s->ld.color, (u_long) (raw - raw_start), s->params.pixels_per_line); raw = raw_start + bpc; } else { if (preview) { for (pixel = 0; pixel < bpc; ++pixel) { *out_ptr[s->ld.color] = *raw++; out_ptr[s->ld.color] += 3; } } else { for (pixel = 0; pixel < bpc; ++pixel) { scale += res; if (scale >= max_value) { scale -= max_value; *out_ptr[s->ld.color] = *raw; out_ptr[s->ld.color] += 3; } ++raw; } } DBG (5, "fix_line_distance_se: got line: %d; color: " "%d\n", lines[s->ld.color], s->ld.color); } --lines[s->ld.color]; } else { /* At least one component missing, so save this line. */ memcpy (s->ld.buf[s->ld.color] + s->ld.saved[s->ld.color] * bpc, raw, bpc); DBG (5, "fix_line_distance_se: saved line %d; color %d\n", s->ld.saved[s->ld.color], s->ld.color); ++(s->ld.saved[s->ld.color]); raw += bpc; } } else { if (!preview) raw += bpc; DBG (5, "fix_line_distance_se: ignored line; color: %d\n", s->ld.color); } if (raw >= raw_end) { /* Reduce num_lines if we encounter excess lines. */ if (num_lines > s->ld.ld_line) num_lines = s->ld.ld_line; s->ld.ld_line -= num_lines; if (++s->ld.color > 2) s->ld.color = 0; return num_lines; } } if (++s->ld.color > 2) s->ld.color = 0; } } /* For Pro models. Not really a linedistance correction (they don't need one) only enlarging x-res here */ static void fix_line_distance_pro (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl, SANE_Byte * raw, SANE_Byte * out) { SANE_Byte *out_addr, *in_addr; SANE_Int res, half_res, y, x_out, x_in; res = SANE_UNFIX (s->val[OPT_RESOLUTION].w); half_res = SANE_UNFIX (s->hw->dpi_range.max) / 2; DBG (5, "fix_line_distance_pro: res=%d; halfres=%d; num_lines=%d; bpl=%d\n", res, half_res, num_lines, bpl); if (strcmp (s->val[OPT_BIT_DEPTH].s, "12") == 0) { if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res)) { /*12 bit, need to enlarge x-resolution */ DBG (5, "fix_line_distance_pro: res > half_res --> need to " "enlarge x\n"); if (little_endian ()) for (y = 0; y < num_lines; y++) { for (x_out = 0; x_out < s->params.pixels_per_line; x_out++) { x_in = x_out * bpl / s->params.bytes_per_line / 2; x_in *= 2; out_addr = out + y * s->params.bytes_per_line + x_out * 6; in_addr = raw + y * bpl + x_in * 6; *(out_addr) = *(in_addr) << 4; *(out_addr + 1) = (*(in_addr) >> 4) + (*(in_addr + 1) << 4); *(out_addr + 2) = *(in_addr + 2) << 4; *(out_addr + 3) = (*(in_addr + 2) >> 4) + (*(in_addr + 3) << 4); *(out_addr + 4) = *(in_addr + 4) << 4; *(out_addr + 5) = (*(in_addr + 4) >> 4) + (*(in_addr + 5) << 4); } } else /* big endian */ for (y = 0; y < num_lines; y++) { for (x_out = 0; x_out < s->params.pixels_per_line; x_out++) { x_in = x_out * bpl / s->params.bytes_per_line / 2; out_addr = out + y * s->params.bytes_per_line + x_out * 6; in_addr = raw + y * bpl + x_in * 6; *(out_addr) = (*(in_addr) >> 4) + (*(in_addr + 1) << 4); *(out_addr + 1) = *(in_addr) << 4; *(out_addr + 2) = (*(in_addr + 2) >> 4) + (*(in_addr + 3) << 4); *(out_addr + 3) = *(in_addr + 2) << 4; *(out_addr + 4) = (*(in_addr + 4) >> 4) + (*(in_addr + 5) << 4); *(out_addr + 5) = *(in_addr + 4) << 4; } } } else /* 12 bit, no need to enlarge x */ { SANE_Word pixel; if (little_endian ()) for (pixel = 0; pixel < (num_lines * bpl / 2); pixel++) { *(out + pixel * 2) = *(raw + pixel * 2) << 4; *(out + pixel * 2 + 1) = (*(raw + pixel * 2) >> 4) + (*(raw + pixel * 2 + 1) << 4); } else /* big endian */ for (pixel = 0; pixel < (num_lines * bpl / 2); pixel++) { *(out + pixel * 2) = (*(raw + pixel * 2) >> 4) + (*(raw + pixel * 2 + 1) << 4); *(out + pixel * 2 + 1) = *(raw + pixel * 2) << 4; } } } else /* 8 bit */ { /* need to enlarge x-resolution? */ if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res)) { DBG (5, "fix_line_distance_pro: res > half_res --> need to " "enlarge x\n"); for (y = 0; y < num_lines; y++) { for (x_out = 0; x_out < s->params.pixels_per_line; x_out++) { x_in = x_out * bpl / s->params.bytes_per_line; out_addr = out + y * s->params.bytes_per_line + x_out * 3; in_addr = raw + y * bpl + x_in * 3; *(out_addr) = *(in_addr); *(out_addr + 1) = *(in_addr + 1); *(out_addr + 2) = *(in_addr + 2); } } } else memcpy (out, raw, num_lines * bpl); } return; } /* For MFS-08000SP, MFS-06000SP. MFC-08000CZ, MFC-06000CZ */ static void fix_line_distance_normal (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl, SANE_Byte * raw, SANE_Byte * out) { SANE_Byte *out_end, *out_ptr, *raw_end = raw + num_lines * bpl; SANE_Int index[3]; /* index of the next output line for color C */ SANE_Int i, color; /* Initialize the indices with the line distances that were returned by the CCD linedistance command or set manually (option linedistance-fix). We want to skip data for the first OFFSET rounds, so we initialize the indices to the negative of this offset. */ DBG (5, "fix_line_distance_normal: %d lines, %d bpl\n", num_lines, bpl); for (color = 0; color < 3; ++color) index[color] = -s->ld.dist[color]; while (1) { for (i = 0; i < 3; ++i) { color = color_seq[i]; if (index[color] < 0) ++index[color]; else if (index[color] < num_lines) { s->ld.quant[color] += s->ld.peak_res; if (s->ld.quant[color] > s->ld.max_value) { s->ld.quant[color] -= s->ld.max_value; out_ptr = out + index[color] * bpl + color; out_end = out_ptr + bpl; while (out_ptr != out_end) { *out_ptr = *raw++; out_ptr += 3; } ++index[color]; if (raw >= raw_end) return; } } } } } /* Paragon series I + II. */ static SANE_Int fix_line_distance_block (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl, SANE_Byte * raw, SANE_Byte * out, SANE_Int num_lines_total) { SANE_Byte *out_end, *out_ptr, *raw_end = raw + num_lines * bpl; SANE_Int c, num_saved_lines, line, max_index, min_index; if (!s->ld.buf[0]) { DBG (5, "fix_line_distance_block: allocating temp buffer of %d*%d " "bytes\n", MAX_LINE_DIST, bpl); s->ld.buf[0] = malloc (MAX_LINE_DIST * (long) bpl); if (!s->ld.buf[0]) { DBG (1, "fix_line_distance_block: failed to malloc temporary " "buffer\n"); return 0; } } DBG (5, "fix_line_distance_block: s->ld.index = {%d, %d, %d}, " "s->ld.lmod3 = %d\n", s->ld.index[0], s->ld.index[1], s->ld.index[2], s->ld.lmod3); DBG (5, "fix_line_distance_block: s->ld.quant = {%d, %d, %d}, " "s->ld.max_value = %d\n", s->ld.quant[0], s->ld.quant[1], s->ld.quant[2], s->ld.max_value); DBG (5, "fix_line_distance_block: s->ld.peak_res = %d, s->ld.ld_line = %d\n", s->ld.peak_res, s->ld.ld_line); /* search maximum and minimum index */ max_index = MAX (s->ld.index[0], MAX (s->ld.index[1], s->ld.index[2])); min_index = MIN (s->ld.index[0], MIN (s->ld.index[1], s->ld.index[2])); num_saved_lines = max_index - min_index; if (s->ld.index[0] == 0) num_saved_lines = 0; /* restore the previously saved lines: */ memcpy (out, s->ld.buf[0], num_saved_lines * bpl); DBG (5, "fix_line_distance_block: copied %d lines from " "ld.buf to buffer (max=%d, min=%d)\n", num_saved_lines, max_index, min_index); while (1) { if (++s->ld.lmod3 >= 3) s->ld.lmod3 = 0; c = color_seq[s->ld.lmod3]; if (s->ld.index[c] < 0) ++s->ld.index[c]; else if (s->ld.index[c] < num_lines_total) { s->ld.quant[c] += s->ld.peak_res; if (s->ld.quant[c] > s->ld.max_value) { s->ld.quant[c] -= s->ld.max_value; line = s->ld.index[c]++ - s->ld.ld_line; out_ptr = out + line * bpl + c; out_end = out_ptr + bpl; while (out_ptr != out_end) { *out_ptr = *raw++; out_ptr += 3; } DBG (5, "fix_line_distance_block: copied line %d (color %d)\n", line + s->ld.ld_line, c); max_index = MAX (s->ld.index[0], MAX (s->ld.index[1], s->ld.index[2])); min_index = MIN (s->ld.index[0], MIN (s->ld.index[1], s->ld.index[2])); if ((raw >= raw_end) || ((min_index >= num_lines_total))) { DBG (5, "fix_line_distance_block: got num_lines: %d\n", num_lines); num_lines = min_index - s->ld.ld_line; if (num_lines < 0) num_lines = 0; if ((s->total_lines + num_lines) > s->params.lines) num_lines = s->params.lines - s->total_lines; s->total_lines += num_lines; /* copy away the lines with at least one missing color component, so that we can interleave them with new scan data on the next call */ num_saved_lines = max_index - min_index; DBG (5, "fix_line_distance_block: num_saved_lines = %d; " "num_lines = %d; bpl = %d\n", num_saved_lines, num_lines, bpl); memcpy (s->ld.buf[0], out + num_lines * bpl, num_saved_lines * bpl); DBG (5, "fix_line_distance_block: copied %d lines to " "ld.buf\n", num_saved_lines); /* notice the number of lines we processed */ s->ld.ld_line = min_index; if (s->ld.ld_line < 0) s->ld.ld_line = 0; DBG (4, "fix_line_distance_block: lmod3=%d, " "index=(%d,%d,%d), line = %d, lines = %d\n", s->ld.lmod3, s->ld.index[0], s->ld.index[1], s->ld.index[2], s->ld.ld_line, num_lines); /* return number of complete (r+g+b) lines */ return num_lines; } } } } } /* For MFS-1200SP 1.00 and others */ /* No LD correction necessary, just shuffle around data */ static SANE_Int fix_line_distance_none (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl, SANE_Byte * raw, SANE_Byte * out) { SANE_Byte *red_ptr, *grn_ptr, *blu_ptr, *ptr, *ptr_end; SANE_Word y; ptr = out; red_ptr = raw; DBG (5, "fix_line_distance_none: no ld correction necessary (%d lines)\n", num_lines); s->ld.ld_line += num_lines; if (s->ld.ld_line > s->params.lines) num_lines -= (s->ld.ld_line - s->params.lines); if (num_lines < 0) num_lines = 0; DBG (5, "fix_line_distance_none: using %d lines (ld_line = %d, " "s->params.lines = %d)\n", num_lines, s->ld.ld_line, s->params.lines); for (y = 0; y < num_lines; ++y) { grn_ptr = red_ptr + bpl / 3; blu_ptr = grn_ptr + bpl / 3; ptr_end = red_ptr + bpl; while (blu_ptr != ptr_end) { *ptr++ = *red_ptr++; *ptr++ = *grn_ptr++; *ptr++ = *blu_ptr++; } red_ptr = ptr_end; } return num_lines; } static SANE_Status init_options (Mustek_Scanner * s) { SANE_Int i, j, gammasize; memset (s->opt, 0, sizeof (s->opt)); memset (s->val, 0, sizeof (s->val)); for (i = 0; i < NUM_OPTIONS; ++i) { s->opt[i].size = sizeof (SANE_Word); s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } s->opt[OPT_NUM_OPTS].name = ""; s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* "Mode" group: */ s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode"); s->opt[OPT_MODE_GROUP].desc = ""; s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MODE_GROUP].cap = 0; s->opt[OPT_MODE_GROUP].size = 0; s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* scan mode */ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; 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].constraint_type = SANE_CONSTRAINT_STRING_LIST; if (s->hw->flags & MUSTEK_FLAG_SE) { s->opt[OPT_MODE].size = max_string_size (mode_list_se); s->opt[OPT_MODE].constraint.string_list = mode_list_se; s->val[OPT_MODE].s = strdup (mode_list_se[1]); if (!s->val[OPT_MODE].s) return SANE_STATUS_NO_MEM; } else { s->opt[OPT_MODE].size = max_string_size (mode_list_paragon); s->opt[OPT_MODE].constraint.string_list = mode_list_paragon; s->val[OPT_MODE].s = strdup (mode_list_paragon[2]); if (!s->val[OPT_MODE].s) return SANE_STATUS_NO_MEM; } /* fast gray mode (pro models) */ s->opt[OPT_FAST_GRAY_MODE].name = "fast-gray-mode"; s->opt[OPT_FAST_GRAY_MODE].title = SANE_I18N ("Fast gray mode"); s->opt[OPT_FAST_GRAY_MODE].desc = SANE_I18N ("Scan in fast gray mode " "(lower quality)."); s->opt[OPT_FAST_GRAY_MODE].type = SANE_TYPE_BOOL; s->val[OPT_FAST_GRAY_MODE].w = SANE_FALSE; s->opt[OPT_FAST_GRAY_MODE].cap |= SANE_CAP_INACTIVE; if (s->hw->flags & MUSTEK_FLAG_PRO) { /* Only Pro models support fast gray mode */ s->opt[OPT_FAST_GRAY_MODE].cap &= ~SANE_CAP_INACTIVE; } /* resolution */ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].type = SANE_TYPE_FIXED; s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_RESOLUTION].constraint.range = &s->hw->dpi_range; s->val[OPT_RESOLUTION].w = MAX (SANE_FIX (72), s->hw->dpi_range.min); /* bit depth */ s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH; s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_STRING; s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BIT_DEPTH].size = max_string_size (bit_depth_list_pro); s->opt[OPT_BIT_DEPTH].constraint.string_list = bit_depth_list_pro; s->val[OPT_BIT_DEPTH].s = strdup (bit_depth_list_pro[0]); if (!s->val[OPT_BIT_DEPTH].s) return SANE_STATUS_NO_MEM; /* speed */ s->opt[OPT_SPEED].name = SANE_NAME_SCAN_SPEED; s->opt[OPT_SPEED].title = SANE_TITLE_SCAN_SPEED; s->opt[OPT_SPEED].desc = SANE_DESC_SCAN_SPEED; s->opt[OPT_SPEED].type = SANE_TYPE_STRING; s->opt[OPT_SPEED].size = max_string_size (speed_list); s->opt[OPT_SPEED].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_SPEED].constraint.string_list = speed_list; s->val[OPT_SPEED].s = strdup (speed_list[4]); if (!s->val[OPT_SPEED].s) return SANE_STATUS_NO_MEM; if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS)) { /* Speed only supported by 3-pass scanners */ s->opt[OPT_SPEED].cap |= SANE_CAP_INACTIVE; } /* source */ s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE; s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE; s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE; s->opt[OPT_SOURCE].type = SANE_TYPE_STRING; if ((s->hw->flags & MUSTEK_FLAG_SE) || (s->hw->flags & MUSTEK_FLAG_N) || (s->hw->flags & MUSTEK_FLAG_TA)) { s->opt[OPT_SOURCE].size = max_string_size (ta_source_list); s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_SOURCE].constraint.string_list = ta_source_list; s->val[OPT_SOURCE].s = strdup (ta_source_list[0]); if (!s->val[OPT_SOURCE].s) return SANE_STATUS_NO_MEM; } else if (s->hw->flags & MUSTEK_FLAG_ADF) { s->opt[OPT_SOURCE].size = max_string_size (adf_source_list); s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_SOURCE].constraint.string_list = adf_source_list; s->val[OPT_SOURCE].s = strdup (adf_source_list[0]); if (!s->val[OPT_SOURCE].s) return SANE_STATUS_NO_MEM; } else { s->opt[OPT_SOURCE].size = max_string_size (source_list); s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_SOURCE].constraint.string_list = source_list; s->val[OPT_SOURCE].s = strdup (source_list[0]); s->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE; if (!s->val[OPT_SOURCE].s) return SANE_STATUS_NO_MEM; } /* preview */ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; s->val[OPT_PREVIEW].w = 0; /* fast preview */ s->opt[OPT_FAST_PREVIEW].name = "fast-preview"; s->opt[OPT_FAST_PREVIEW].title = SANE_I18N ("Fast preview"); s->opt[OPT_FAST_PREVIEW].desc = SANE_I18N ("Request that all previews are " "done in the fastest (low-quality) mode. This may be a non-color " "mode or a low resolution mode."); s->opt[OPT_FAST_PREVIEW].type = SANE_TYPE_BOOL; s->val[OPT_FAST_PREVIEW].w = SANE_FALSE; /* lamp off time*/ s->opt[OPT_LAMP_OFF_TIME].name = "lamp-off-time"; s->opt[OPT_LAMP_OFF_TIME].title = SANE_I18N ("Lamp off time (minutes)"); s->opt[OPT_LAMP_OFF_TIME].desc = SANE_I18N ("Set the time (in minutes) after " "which the lamp is shut off."); s->opt[OPT_LAMP_OFF_TIME].type = SANE_TYPE_INT; if (strcmp (s->hw->sane.model, "1200 A3 PRO") != 0) s->opt[OPT_LAMP_OFF_TIME].cap |= SANE_CAP_INACTIVE; s->opt[OPT_LAMP_OFF_TIME].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_LAMP_OFF_TIME].constraint.range = &u8_range; s->val[OPT_LAMP_OFF_TIME].w = 60; /* shut lamp off */ s->opt[OPT_LAMP_OFF_BUTTON].name = "lamp-off"; s->opt[OPT_LAMP_OFF_BUTTON].title = SANE_I18N ("Turn lamp off"); s->opt[OPT_LAMP_OFF_BUTTON].desc = SANE_I18N ("Turns the lamp off immediately."); s->opt[OPT_LAMP_OFF_BUTTON].type = SANE_TYPE_BUTTON; s->opt[OPT_LAMP_OFF_BUTTON].cap = SANE_CAP_SOFT_SELECT; if (strcmp (s->hw->sane.model, "1200 A3 PRO") != 0) s->opt[OPT_LAMP_OFF_BUTTON].cap |= SANE_CAP_INACTIVE; /* "Geometry" group: */ s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry"); s->opt[OPT_GEOMETRY_GROUP].desc = ""; s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_GEOMETRY_GROUP].size = 0; s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* top-left x */ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; s->opt[OPT_TL_X].unit = SANE_UNIT_MM; s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_X].constraint.range = &s->hw->x_range; s->val[OPT_TL_X].w = s->hw->x_range.min; /* top-left y */ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range; s->val[OPT_TL_Y].w = s->hw->y_range.min; /* bottom-right x */ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; s->opt[OPT_BR_X].unit = SANE_UNIT_MM; s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_X].constraint.range = &s->hw->x_range; s->val[OPT_BR_X].w = s->hw->x_range.max; /* bottom-right y */ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range; s->val[OPT_BR_Y].w = s->hw->y_range.max; /* "Enhancement" group: */ s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement"); s->opt[OPT_ENHANCEMENT_GROUP].desc = ""; s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_ENHANCEMENT_GROUP].cap = 0; s->opt[OPT_ENHANCEMENT_GROUP].size = 0; s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* brightness */ s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_FIXED; s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT; s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BRIGHTNESS].constraint.range = &percentage_range; if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS)) /* 1-pass scanners don't support brightness in multibit mode */ s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; s->val[OPT_BRIGHTNESS].w = 0; /* brightness red */ s->opt[OPT_BRIGHTNESS_R].name = "brightness-r"; s->opt[OPT_BRIGHTNESS_R].title = SANE_I18N ("Red brightness"); s->opt[OPT_BRIGHTNESS_R].desc = SANE_I18N ("Controls the brightness of " "the red channel of the " "acquired image."); s->opt[OPT_BRIGHTNESS_R].type = SANE_TYPE_FIXED; s->opt[OPT_BRIGHTNESS_R].unit = SANE_UNIT_PERCENT; s->opt[OPT_BRIGHTNESS_R].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BRIGHTNESS_R].constraint.range = &percentage_range; s->opt[OPT_BRIGHTNESS_R].cap |= SANE_CAP_INACTIVE; s->val[OPT_BRIGHTNESS_R].w = 0; /* brightness green */ s->opt[OPT_BRIGHTNESS_G].name = "brightness-g"; s->opt[OPT_BRIGHTNESS_G].title = SANE_I18N ("Green brightness"); s->opt[OPT_BRIGHTNESS_G].desc = SANE_I18N ("Controls the brightness of " "the green channel of the " "acquired image."); s->opt[OPT_BRIGHTNESS_G].type = SANE_TYPE_FIXED; s->opt[OPT_BRIGHTNESS_G].unit = SANE_UNIT_PERCENT; s->opt[OPT_BRIGHTNESS_G].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BRIGHTNESS_G].constraint.range = &percentage_range; s->opt[OPT_BRIGHTNESS_G].cap |= SANE_CAP_INACTIVE; s->val[OPT_BRIGHTNESS_G].w = 0; /* brightness blue */ s->opt[OPT_BRIGHTNESS_B].name = "brightness-b"; s->opt[OPT_BRIGHTNESS_B].title = SANE_I18N ("Blue brightness"); s->opt[OPT_BRIGHTNESS_B].desc = SANE_I18N ("Controls the brightness of " "the blue channel of the " "acquired image."); s->opt[OPT_BRIGHTNESS_B].type = SANE_TYPE_FIXED; s->opt[OPT_BRIGHTNESS_B].unit = SANE_UNIT_PERCENT; s->opt[OPT_BRIGHTNESS_B].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BRIGHTNESS_B].constraint.range = &percentage_range; s->opt[OPT_BRIGHTNESS_B].cap |= SANE_CAP_INACTIVE; s->val[OPT_BRIGHTNESS_B].w = 0; /* contrast */ s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST; s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST; s->opt[OPT_CONTRAST].type = SANE_TYPE_FIXED; s->opt[OPT_CONTRAST].unit = SANE_UNIT_PERCENT; s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CONTRAST].constraint.range = &percentage_range; if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS)) /* 1-pass scanners don't support contrast in multibit mode */ s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; s->val[OPT_CONTRAST].w = 0; /* contrast red */ s->opt[OPT_CONTRAST_R].name = "contrast-r"; s->opt[OPT_CONTRAST_R].title = SANE_I18N ("Contrast red channel"); s->opt[OPT_CONTRAST_R].desc = SANE_I18N ("Controls the contrast of " "the red channel of the " "acquired image."); s->opt[OPT_CONTRAST_R].type = SANE_TYPE_FIXED; s->opt[OPT_CONTRAST_R].unit = SANE_UNIT_PERCENT; s->opt[OPT_CONTRAST_R].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CONTRAST_R].constraint.range = &percentage_range; s->opt[OPT_CONTRAST_R].cap |= SANE_CAP_INACTIVE; s->val[OPT_CONTRAST_R].w = 0; /* contrast green */ s->opt[OPT_CONTRAST_G].name = "contrast-g"; s->opt[OPT_CONTRAST_G].title = SANE_I18N ("Contrast green channel"); s->opt[OPT_CONTRAST_G].desc = SANE_I18N ("Controls the contrast of " "the green channel of the " "acquired image."); s->opt[OPT_CONTRAST_G].type = SANE_TYPE_FIXED; s->opt[OPT_CONTRAST_G].unit = SANE_UNIT_PERCENT; s->opt[OPT_CONTRAST_G].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CONTRAST_G].constraint.range = &percentage_range; s->opt[OPT_CONTRAST_G].cap |= SANE_CAP_INACTIVE; s->val[OPT_CONTRAST_G].w = 0; /* contrast blue */ s->opt[OPT_CONTRAST_B].name = "contrast-b"; s->opt[OPT_CONTRAST_B].title = SANE_I18N ("Contrast blue channel"); s->opt[OPT_CONTRAST_B].desc = SANE_I18N ("Controls the contrast of " "the blue channel of the " "acquired image."); s->opt[OPT_CONTRAST_B].type = SANE_TYPE_FIXED; s->opt[OPT_CONTRAST_B].unit = SANE_UNIT_PERCENT; s->opt[OPT_CONTRAST_B].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_CONTRAST_B].constraint.range = &percentage_range; s->opt[OPT_CONTRAST_B].cap |= SANE_CAP_INACTIVE; s->val[OPT_CONTRAST_B].w = 0; /* gamma */ gammasize = 256; for (i = 0; i < 4; ++i) for (j = 0; j < gammasize; ++j) s->gamma_table[i][j] = j; /* custom-gamma table */ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL; s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE; /* grayscale gamma vector */ s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR; s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR; s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR; s->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word); s->val[OPT_GAMMA_VECTOR].wa = &s->gamma_table[0][0]; s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range; /* red gamma vector */ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word); s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[1][0]; s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range; /* green gamma vector */ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word); s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[2][0]; s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range; /* blue gamma vector */ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word); s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[3][0]; s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range; /* quality calibration */ s->opt[OPT_QUALITY_CAL].name = SANE_NAME_QUALITY_CAL; s->opt[OPT_QUALITY_CAL].title = SANE_TITLE_QUALITY_CAL; s->opt[OPT_QUALITY_CAL].desc = SANE_DESC_QUALITY_CAL; s->opt[OPT_QUALITY_CAL].type = SANE_TYPE_BOOL; if (s->hw->flags & MUSTEK_FLAG_PRO) s->val[OPT_QUALITY_CAL].w = SANE_TRUE; else s->val[OPT_QUALITY_CAL].w = SANE_FALSE; s->opt[OPT_QUALITY_CAL].cap |= SANE_CAP_INACTIVE; if ((s->hw->flags & MUSTEK_FLAG_PRO) || (s->hw->flags & MUSTEK_FLAG_SE_PLUS)) { /* Only Pro and SE Plus models support calibration */ s->opt[OPT_QUALITY_CAL].cap &= ~SANE_CAP_INACTIVE; } /* halftone dimension */ s->opt[OPT_HALFTONE_DIMENSION].name = SANE_NAME_HALFTONE_DIMENSION; s->opt[OPT_HALFTONE_DIMENSION].title = SANE_TITLE_HALFTONE_DIMENSION; s->opt[OPT_HALFTONE_DIMENSION].desc = SANE_DESC_HALFTONE_DIMENSION; s->opt[OPT_HALFTONE_DIMENSION].type = SANE_TYPE_STRING; s->opt[OPT_HALFTONE_DIMENSION].size = max_string_size (halftone_list); s->opt[OPT_HALFTONE_DIMENSION].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_HALFTONE_DIMENSION].constraint.string_list = halftone_list; s->val[OPT_HALFTONE_DIMENSION].s = strdup (halftone_list[0]); if (!s->val[OPT_HALFTONE_DIMENSION].s) return SANE_STATUS_NO_MEM; s->opt[OPT_HALFTONE_DIMENSION].cap |= SANE_CAP_INACTIVE; /* halftone pattern */ s->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN; s->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN; s->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN; s->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_INT; s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; s->opt[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_HALFTONE_PATTERN].constraint.range = &u8_range; s->val[OPT_HALFTONE_PATTERN].wa = s->halftone_pattern; return SANE_STATUS_GOOD; } /* The following three functions execute as a child process. The reason for using a subprocess is that some (most?) generic SCSI interfaces block a SCSI request until it has completed. With a subprocess, we can let it block waiting for the request to finish while the main process can go about to do more important things (such as recognizing when the user presses a cancel button). WARNING: Since this is executed as a subprocess, it's NOT possible to update any of the variables in the main process (in particular the scanner state cannot be updated). NOTE: At least for Linux, it seems that we could get rid of the subprocess. Linux v2.0 does seem to allow select() on SCSI descriptors. */ static void output_data (Mustek_Scanner * s, FILE * fp, SANE_Byte * data, SANE_Int lines_per_buffer, SANE_Int bpl, SANE_Byte * extra) { SANE_Byte *ptr, *ptr_end; SANE_Int y, num_lines; DBG (5, "output_data: data=%p, lpb=%d, bpl=%d, extra=%p\n", (void *) data, lines_per_buffer, bpl, (void *) extra); /* convert to pixel-interleaved format: */ if ((s->mode & MUSTEK_MODE_COLOR) && !(s->hw->flags & MUSTEK_FLAG_THREE_PASS)) { num_lines = lines_per_buffer; /* need to correct for distance between r/g/b sensors: */ if (s->hw->flags & MUSTEK_FLAG_PRO) fix_line_distance_pro (s, num_lines, bpl, data, extra); else if (s->hw->flags & MUSTEK_FLAG_SE) { num_lines = fix_line_distance_se (s, num_lines, bpl, data, extra); } else if (s->hw->flags & MUSTEK_FLAG_N) { if (s->hw->flags & MUSTEK_FLAG_LD_N2) num_lines = fix_line_distance_n_2 (s, num_lines, bpl, data, extra); else num_lines = fix_line_distance_n_1 (s, num_lines, bpl, data, extra); } else if ((s->hw->flags & MUSTEK_FLAG_LD_BLOCK) && (s->ld.max_value != 0)) { if (s->hw->flags & MUSTEK_FLAG_PARAGON_1) num_lines = fix_line_distance_block (s, num_lines, bpl, data, extra, s->hw->lines); else num_lines = fix_line_distance_block (s, num_lines, bpl, data, extra, s->hw->lines_per_block); } else if (!(s->hw->flags & MUSTEK_FLAG_LD_NONE) && (s->ld.max_value != 0)) fix_line_distance_normal (s, num_lines, bpl, data, extra); else num_lines = fix_line_distance_none (s, num_lines, bpl, data, extra); if (strcmp (s->val[OPT_SOURCE].s, "Automatic Document Feeder") == 0) { /* need to revert line direction */ SANE_Int line_number; SANE_Int byte_number; DBG (5, "output_data: ADF found, mirroring lines\n"); for (line_number = 0; line_number < num_lines; line_number++) { for (byte_number = bpl - 3; byte_number >= 0; byte_number -= 3) { fputc (*(extra + line_number * bpl + byte_number), fp); fputc (*(extra + line_number * bpl + byte_number + 1), fp); fputc (*(extra + line_number * bpl + byte_number + 2), fp); } } } else fwrite (extra, num_lines, s->params.bytes_per_line, fp); } else { DBG (5, "output_data: write %d lpb; %d bpl\n", lines_per_buffer, bpl); /* Scale x-resolution above 1/2 of the maximum resolution for SE and Pro scanners */ if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (s->val[OPT_RESOLUTION].w > (s->hw->dpi_range.max / 2))) { SANE_Int x; SANE_Int half_res = SANE_UNFIX (s->hw->dpi_range.max) / 2; SANE_Int res = SANE_UNFIX (s->val[OPT_RESOLUTION].w); SANE_Int res_counter; SANE_Int enlarged_x; DBG (5, "output_data: enlarge lines from %d bpl to %d bpl\n", s->hw->bpl, s->params.bytes_per_line); for (y = 0; y < lines_per_buffer; y++) { SANE_Byte byte = 0; x = 0; res_counter = 0; enlarged_x = 0; while (enlarged_x < s->params.pixels_per_line) { if (s->mode & MUSTEK_MODE_GRAY) { fputc (*(data + y * bpl + x), fp); res_counter += half_res; if (res_counter >= half_res) { res_counter -= res; x++; } enlarged_x++; } else /* lineart */ { /* need to invert image because of funny SANE 1-bit image polarity */ if (*(data + x / 8 + y * bpl) & (1 << (7 - (x % 8)))) byte |= 1 << (7 - (enlarged_x % 8)); if ((enlarged_x % 8) == 7) { fputc (~byte, fp); /* invert image */ byte = 0; } res_counter += half_res; if (res_counter >= half_res) { res_counter -= res; x++; } enlarged_x++; } } } } else /* lineart, gray or halftone (nothing to scale) */ { if ((s->mode & MUSTEK_MODE_LINEART) || (s->mode & MUSTEK_MODE_HALFTONE)) { /* need to invert image because of funny SANE 1-bit image polarity */ ptr = data; ptr_end = ptr + lines_per_buffer * bpl; if (strcmp (s->val[OPT_SOURCE].s, "Automatic Document Feeder") == 0) { while (ptr != ptr_end) { (*ptr) = ~(*ptr); ptr++; /* need to revert bit direction */ *ptr = ((*ptr & 0x80) >> 7) + ((*ptr & 0x40) >> 5) + ((*ptr & 0x20) >> 3) + ((*ptr & 0x10) >> 1) + ((*ptr & 0x08) << 1) + ((*ptr & 0x04) << 3) + ((*ptr & 0x02) << 5) + ((*ptr & 0x01) << 7); } } else while (ptr != ptr_end) { (*ptr) = ~(*ptr); ptr++; } } if (strcmp (s->val[OPT_SOURCE].s, "Automatic Document Feeder") == 0) { /* need to revert line direction */ SANE_Int line_number; SANE_Int byte_number; DBG (5, "output_data: ADF found, mirroring lines\n"); for (line_number = 0; line_number < lines_per_buffer; line_number++) { for (byte_number = bpl - 1; byte_number >= 0; byte_number--) { fputc (*(data + line_number * bpl + byte_number), fp); } } } else { fwrite (data, lines_per_buffer, bpl, fp); } } } DBG (5, "output_data: end\n"); } static void sigterm_handler (int signal) { DBG (4, "sigterm_handler: started, signal is %d, starting sanei_scsi_req_flush_all()\n", signal); sanei_scsi_req_flush_all (); /* flush SCSI queue */ DBG (4, "sigterm_handler: sanei_scsi_req_flush_all() finisheshed, _exiting()\n"); _exit (SANE_STATUS_GOOD); } static SANE_Int reader_process (void *data) { Mustek_Scanner *s = (Mustek_Scanner *) data; SANE_Int lines_per_buffer, bpl; SANE_Byte *extra = 0, *ptr; sigset_t sigterm_set; struct SIGACTION act; SANE_Status status; FILE *fp; int fd = s->reader_fds; SANE_Int buffernumber = 0; SANE_Int buffer_count, max_buffers; struct { void *id; /* scsi queue id */ SANE_Byte *data; /* data buffer */ SANE_Byte *command; /* command buffer */ SANE_Int lines; /* # lines in buffer */ size_t num_read; /* # of bytes read (return value) */ SANE_Int bank; /* needed by SE models */ SANE_Bool ready; /* ready to send to application? */ SANE_Bool finished; /* block is finished */ } bstat[2]; DBG (3, "reader_process: started\n"); if (sanei_thread_is_forked ()) { DBG (4, "reader_process: using fork ()\n"); close (s->pipe); s->pipe = -1; } else { DBG (4, "reader_process: using threads\n"); } if (sanei_thread_is_forked ()) { /* ignore SIGTERM while writing SCSI commands */ sigemptyset (&sigterm_set); sigaddset (&sigterm_set, SIGTERM); /* call our sigterm handler to clean up ongoing SCSI requests */ memset (&act, 0, sizeof (act)); act.sa_handler = sigterm_handler; sigaction (SIGTERM, &act, 0); } if (disable_double_buffering) DBG (3, "reader_process: disable_double_buffering is set, this may be " "slow\n"); fp = fdopen (fd, "w"); if (!fp) return SANE_STATUS_IO_ERROR; s->total_lines = 0; bpl = s->hw->bpl; /* buffer size is scanner dependent */ lines_per_buffer = s->hw->buffer_size / bpl / 2; if (strip_height > 0.0) { SANE_Int max_lines; double dpi; dpi = SANE_UNFIX (s->val[OPT_RESOLUTION].w); max_lines = (int) (strip_height * dpi + 0.5); if (lines_per_buffer > max_lines) { DBG (2, "reader_process: limiting strip height to %g inches " "(%d lines)\n", strip_height, max_lines); lines_per_buffer = max_lines; } } if (!lines_per_buffer) { DBG (1, "reader_process: bpl (%d) > SCSI buffer size / 2 (%d)\n", bpl, s->hw->buffer_size / 2); return SANE_STATUS_NO_MEM; /* resolution is too high */ } DBG (4, "reader_process: %d lines per buffer, %d bytes per line, " "%d bytes per buffer\n", lines_per_buffer, bpl, lines_per_buffer * bpl); bstat[0].data = malloc (2 * lines_per_buffer * (long) bpl); if (!bstat[0].data) { DBG (1, "reader_process: failed to malloc %ld bytes for data buffer\n", lines_per_buffer * (long) bpl); return SANE_STATUS_NO_MEM; } bstat[1].data = bstat[0].data + lines_per_buffer * (long) bpl; bstat[0].command = malloc (2 * 10); if (!bstat[0].command) { DBG (1, "reader_process: failed to malloc %d bytes for command buffer\n", 2 * 10); return SANE_STATUS_NO_MEM; } bstat[1].command = bstat[0].command + 10; /* Touch all pages of the buffer to fool the memory management. */ ptr = bstat[0].data + 2 * lines_per_buffer * (long) bpl - 1; while (ptr >= bstat[0].data) { *ptr = 0x00; ptr -= 256; } if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS)) { /* get temporary buffer for line-distance correction and/or bit expansion. For some scanners more space is needed because the data must be read in as single big block (cut up into pieces of lines_per_buffer). This requires that the line distance correction continues on every call exactly where it stopped if the image shall be reconstructed without any stripes. */ extra = malloc ((lines_per_buffer + MAX_LINE_DIST) * (long) s->params.bytes_per_line); if (!extra) { DBG (1, "reader_process: failed to malloc extra buffer\n"); return SANE_STATUS_NO_MEM; } } if (s->hw->flags & MUSTEK_FLAG_N) { /* reacquire port access rights (lost because of fork()): */ sanei_ab306_get_io_privilege (s->fd); } if ((s->hw->flags & MUSTEK_FLAG_N) || (s->hw->flags & MUSTEK_FLAG_LD_BLOCK)) { /* reset counter of line number for line-dictance correction */ s->ld.ld_line = 0; } max_buffers = s->hw->max_block_buffer_size / (lines_per_buffer * bpl); if (max_buffers < 1) { DBG (1, "reader_process: buffersize > blocksize!\n"); return SANE_STATUS_NO_MEM; } DBG (4, "reader_process: limiting block read to %d buffers (%d lines)\n", max_buffers, MIN (s->hw->lines, (max_buffers * lines_per_buffer))); while (s->line < s->hw->lines) { s->hw->lines_per_block = MIN (s->hw->lines - s->line, (max_buffers * lines_per_buffer)); status = dev_block_read_start (s, s->hw->lines_per_block); if (status != SANE_STATUS_GOOD) return status; for (buffernumber = 0; buffernumber < 2; buffernumber++) { bstat[buffernumber].ready = SANE_FALSE; bstat[buffernumber].finished = SANE_FALSE; } buffer_count = 0; buffernumber = 0; while (1) { /* omit reading first two buffers (not yet ready) */ if (bstat[buffernumber].ready == SANE_TRUE) { DBG (4, "reader_process: buffer %d: waiting for request to be " "ready\n", buffernumber + 1); status = dev_req_wait (bstat[buffernumber].id); if (status == SANE_STATUS_GOOD) { DBG (4, "reader_process: buffer %d is ready, wanted %d, " "got %ld bytes\n", buffernumber + 1, bstat[buffernumber].lines * bpl, (long int) bstat[buffernumber].num_read); } else { DBG (1, "reader_process: failed to read data, status: %s, " "buffer: %d\n", sane_strstatus (status), buffernumber + 1); if (status == SANE_STATUS_NO_MEM) { DBG (1, "Probably the size of the kernel SCSI buffer is " "too small for the\n selected buffersize " "in mustek.conf. Either decrease " "buffersize in\n mustek.conf to e.g. 32, " "increase SG_BIG_BUF in kernel to 130560, " "or\n use SANE_SG_BUFFERSIZE variable. " "See man sane-scsi and README for\n " "details.\n"); } return status; } DBG (4, "reader_process: buffer %d: sending %ld bytes to " "output_data\n", buffernumber + 1, (long int) bstat[buffernumber].num_read); output_data (s, fp, bstat[buffernumber].data, bstat[buffernumber].lines, bpl, extra); if (bstat[buffernumber].finished) break; /* everything written; exit loop */ } if (disable_double_buffering) { /* Enter only one buffer at once */ if (buffernumber == 1) buffernumber = 0; else buffernumber = 1; } /* enter read requests only if data left */ if ((s->line < s->hw->lines) && (buffer_count < max_buffers)) { if (s->line + lines_per_buffer >= s->hw->lines) { /* do the last few lines: */ bstat[buffernumber].lines = s->hw->lines - s->line; bstat[buffernumber].bank = 0x01; bstat[buffernumber].finished = SANE_TRUE; } else { bstat[buffernumber].lines = lines_per_buffer; bstat[buffernumber].bank = 0x00; } if ((buffer_count + 1) >= max_buffers) bstat[buffernumber].finished = SANE_TRUE; s->line += bstat[buffernumber].lines; bstat[buffernumber].ready = SANE_TRUE; buffer_count++; DBG (4, "reader_process: buffer %d: entering read request for %d " "bytes (buffer %d)\n", buffernumber + 1, bstat[buffernumber].lines * bpl, buffer_count); sigprocmask (SIG_BLOCK, &sigterm_set, 0); status = dev_read_req_enter (s, bstat[buffernumber].data, bstat[buffernumber].lines, bpl, &bstat[buffernumber].num_read, &bstat[buffernumber].id, bstat[buffernumber].bank, bstat[buffernumber].command); sigprocmask (SIG_UNBLOCK, &sigterm_set, 0); if (status == SANE_STATUS_GOOD) { DBG (5, "reader_process: buffer %d: entered (line %d of %d," " buffer %d)\n", buffernumber + 1, s->line, s->hw->lines, buffer_count); } else { DBG (1, "reader_process: buffer %d: failed to enter read " "request, status: %s\n", buffernumber + 1, sane_strstatus (status)); return status; } } if (!disable_double_buffering) { if (buffernumber == 1) buffernumber = 0; else buffernumber = 1; } /* This is said to fix the scanner hangs that reportedly show on some MFS-12000SP scanners. */ if (s->mode == 0 && (s->hw->flags & MUSTEK_FLAG_LINEART_FIX)) usleep (200000); } } fclose (fp); free (bstat[0].data); if (s->ld.buf[0]) free (s->ld.buf[0]); s->ld.buf[0] = NULL; if (extra) free (extra); close (fd); return SANE_STATUS_GOOD; } static SANE_Status attach_one_device (SANE_String_Const devname) { Mustek_Device *dev; attach (devname, &dev, SANE_FALSE); if (dev) { /* Keep track of newly attached devices so we can set options as necessary. */ if (new_dev_len >= new_dev_alloced) { new_dev_alloced += 4; if (new_dev) new_dev = realloc (new_dev, new_dev_alloced * sizeof (new_dev[0])); else new_dev = malloc (new_dev_alloced * sizeof (new_dev[0])); if (!new_dev) { DBG (1, "attach_one_device: out of memory\n"); return SANE_STATUS_NO_MEM; } } new_dev[new_dev_len++] = dev; } return SANE_STATUS_GOOD; } /**************************************************************************/ /* SANE API calls */ /**************************************************************************/ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { SANE_Char line[PATH_MAX], *word, *end; SANE_String_Const cp; SANE_Int linenumber; FILE *fp; DBG_INIT (); sanei_thread_init (); #ifdef DBG_LEVEL debug_level = DBG_LEVEL; #else debug_level = 0; #endif DBG (2, "SANE mustek backend version %d.%d build %d from %s\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD, PACKAGE_STRING); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); DBG (5, "sane_init: authorize %s null\n", authorize ? "!=" : "=="); #ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED DBG (5, "sane_init: using sanei_scsi_open_extended\n"); #else DBG (5, "sane_init: using sanei_scsi_open with buffer size = %d bytes\n", sanei_scsi_max_request_size); #endif num_devices = 0; force_wait = SANE_FALSE; disable_double_buffering = SANE_FALSE; first_dev = 0; first_handle = 0; devlist = 0; new_dev = 0; new_dev_len = 0; new_dev_alloced = 0; fp = sanei_config_open (MUSTEK_CONFIG_FILE); if (!fp) { /* default to /dev/scanner instead of insisting on config file */ DBG (3, "sane_init: couldn't find config file (%s), trying " "/dev/scanner directly\n", MUSTEK_CONFIG_FILE); attach ("/dev/scanner", 0, SANE_FALSE); return SANE_STATUS_GOOD; } linenumber = 0; DBG (4, "sane_init: reading config file `%s'\n", MUSTEK_CONFIG_FILE); while (sanei_config_read (line, sizeof (line), fp)) { word = 0; linenumber++; cp = sanei_config_get_string (line, &word); if (!word || cp == line) { DBG (5, "sane_init: config file line %d: ignoring empty line\n", linenumber); if (word) free (word); continue; } if (word[0] == '#') { DBG (5, "sane_init: config file line %d: ignoring comment line\n", linenumber); free (word); continue; } if (strcmp (word, "option") == 0) { free (word); word = 0; cp = sanei_config_get_string (cp, &word); if (!word) { DBG (1, "sane_init: config file line %d: missing quotation mark?\n", linenumber); continue; } if (strcmp (word, "strip-height") == 0) { free (word); word = 0; cp = sanei_config_get_string (cp, &word); if (!word) { DBG (1, "sane_init: config file line %d: missing quotation mark?\n", linenumber); continue; } errno = 0; strip_height = strtod (word, &end); if (end == word) { DBG (3, "sane-init: config file line %d: strip-height " "must have a parameter; using 1 inch\n", linenumber); strip_height = 1.0; } if (errno) { DBG (3, "sane-init: config file line %d: strip-height `%s' " "is invalid (%s); using 1 inch\n", linenumber, word, strerror (errno)); strip_height = 1.0; } else { if (strip_height < 0.1) strip_height = 0.1; DBG (3, "sane_init: config file line %d: strip-height set " "to %g inches\n", linenumber, strip_height); } if (word) free (word); word = 0; } else if (strcmp (word, "force-wait") == 0) { DBG (3, "sane_init: config file line %d: enabling force-wait\n", linenumber); force_wait = SANE_TRUE; if (word) free (word); word = 0; } else if (strcmp (word, "disable-double-buffering") == 0) { DBG (3, "sane_init: config file line %d: disabling " "double-buffering\n", linenumber); disable_double_buffering = SANE_TRUE; if (word) free (word); word = 0; } else if (strcmp (word, "legal-size") == 0) { if (new_dev_len > 0) { /* Check for 12000 LS, no way to find out automatically */ if (strcmp (new_dev[new_dev_len - 1]->sane.model, "ScanExpress 12000SP") == 0) { new_dev[new_dev_len - 1]->x_range.max = SANE_FIX (220.0); new_dev[new_dev_len - 1]->y_range.max = SANE_FIX (360.0); new_dev[new_dev_len - 1]->sane.model = "Paragon 1200 LS"; DBG (3, "sane_init: config file line %d: enabling " "legal-size for %s\n", linenumber, new_dev[new_dev_len - 1]->sane.name); } else { DBG (3, "sane_init: config file line %d: option " "legal-size ignored, device %s is not a " "Paragon 1200 LS\n", linenumber, new_dev[new_dev_len - 1]->sane.name); } } else { DBG (3, "sane_init: config file line %d: option " "legal-size ignored, was set before any device " "name\n", linenumber); } if (word) free (word); word = 0; } else if (strcmp (word, "linedistance-fix") == 0) { if (new_dev_len > 0) { new_dev[new_dev_len - 1]->flags |= MUSTEK_FLAG_LD_FIX; DBG (3, "sane_init: config file line %d: enabling " "linedistance-fix for %s\n", linenumber, new_dev[new_dev_len - 1]->sane.name); } else { DBG (3, "sane_init: config file line %d: option " "linedistance-fix ignored, was set before any device " "name\n", linenumber); } if (word) free (word); word = 0; } else if (strcmp (word, "disable-backtracking") == 0) { if (new_dev_len > 0) { new_dev[new_dev_len - 1]->flags |= MUSTEK_FLAG_NO_BACKTRACK; DBG (3, "sane_init: config file line %d: disabling " "backtracking for %s\n", linenumber, new_dev[new_dev_len - 1]->sane.name); } else { DBG (3, "sane_init: config file line %d: option " "disable-backtracking ignored, was set before any " "device name\n", linenumber); } if (word) free (word); word = 0; } else if (strcmp (word, "lineart-fix") == 0) { if (new_dev_len > 0) { new_dev[new_dev_len - 1]->flags |= MUSTEK_FLAG_LINEART_FIX; DBG (3, "sane_init: config file line %d: enabling " "lineart-fix for %s\n", linenumber, new_dev[new_dev_len - 1]->sane.name); } else { DBG (3, "sane_init: config file line %d: option " "lineart-fix ignored, was set before any device name\n", linenumber); } if (word) free (word); word = 0; } else if (strcmp (word, "buffersize") == 0) { long buffer_size; free (word); word = 0; cp = sanei_config_get_string (cp, &word); if (!word) { DBG (1, "sane_init: config file line %d: missing quotation mark?\n", linenumber); continue; } errno = 0; buffer_size = strtol (word, &end, 0); if (end == word) { DBG (3, "sane-init: config file line %d: buffersize must " "have a parameter; using default (%d kb)\n", linenumber, new_dev[new_dev_len - 1]->max_buffer_size); } if (errno) { DBG (3, "sane-init: config file line %d: buffersize `%s' " "is invalid (%s); using default (%d kb)\n", linenumber, word, strerror (errno), new_dev[new_dev_len - 1]->max_buffer_size); } else { if (new_dev_len > 0) { if (buffer_size < 32.0) buffer_size = 32.0; new_dev[new_dev_len - 1]->max_buffer_size = buffer_size * 1024; DBG (3, "sane_init: config file line %d: buffersize set " "to %ld kb for %s\n", linenumber, buffer_size, new_dev[new_dev_len - 1]->sane.name); } else { DBG (3, "sane_init: config file line %d: option " "buffersize ignored, was set before any device " "name\n", linenumber); } } if (word) free (word); word = 0; } else if (strcmp (word, "blocksize") == 0) { long block_size; free (word); word = 0; cp = sanei_config_get_string (cp, &word); if (!word) { DBG (1, "sane_init: config file line %d: missing quotation mark?\n", linenumber); continue; } errno = 0; block_size = strtol (word, &end, 0); if (end == word) { DBG (3, "sane-init: config file line %d:: blocksize must " "have a parameter; using default (1 GB)\n", linenumber); } if (errno) { DBG (3, "sane-init: config file line %d: blocksize `%s' " "is invalid (%s); using default (1 GB)\n", linenumber, word, strerror (errno)); } else { if (new_dev_len > 0) { if (block_size < 256.0) block_size = 256.0; new_dev[new_dev_len - 1]->max_block_buffer_size = block_size * 1024; DBG (3, "sane_init: config file line %d: blocksize set " "to %ld kb for %s\n", linenumber, block_size, new_dev[new_dev_len - 1]->sane.name); } else { DBG (3, "sane_init: config file line %d: option " "blocksize ignored, was set before any device " "name\n", linenumber); } } if (word) free (word); word = 0; } else { DBG (3, "sane_init: config file line %d: ignoring unknown " "option `%s'\n", linenumber, word); if (word) free (word); word = 0; } } else { new_dev_len = 0; DBG (4, "sane_init: config file line %d: trying to attach `%s'\n", linenumber, line); sanei_config_attach_matching_devices (line, attach_one_device); if (word) free (word); word = 0; } } if (new_dev_alloced > 0) { new_dev_len = new_dev_alloced = 0; free (new_dev); } fclose (fp); DBG (5, "sane_init: end\n"); return SANE_STATUS_GOOD; } void sane_exit (void) { Mustek_Device *dev, *next; DBG (4, "sane_exit\n"); for (dev = first_dev; dev; dev = next) { next = dev->next; free (dev->name); free (dev); } if (devlist) free (devlist); devlist = 0; first_dev = 0; sanei_ab306_exit (); /* may have to do some cleanup */ mustek_scsi_pp_exit (); DBG (5, "sane_exit: finished\n"); } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { Mustek_Device *dev; SANE_Int i; DBG (4, "sane_get_devices: %d devices %s\n", num_devices, local_only ? "(local only)" : ""); if (devlist) free (devlist); devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); if (!devlist) return SANE_STATUS_NO_MEM; i = 0; for (dev = first_dev; i < num_devices; dev = dev->next) devlist[i++] = &dev->sane; devlist[i++] = 0; *device_list = devlist; DBG (5, "sane_get_devices: end\n"); return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { Mustek_Device *dev; SANE_Status status; Mustek_Scanner *s; if (!devicename) { DBG (1, "sane_open: devicename is null!\n"); return SANE_STATUS_INVAL; } if (!handle) { DBG (1, "sane_open: handle is null!\n"); return SANE_STATUS_INVAL; } DBG (4, "sane_open: devicename=%s\n", devicename); if (devicename[0]) { for (dev = first_dev; dev; dev = dev->next) if (strcmp (dev->sane.name, devicename) == 0) break; if (!dev) { status = attach (devicename, &dev, SANE_TRUE); if (status != SANE_STATUS_GOOD) return status; } } else /* empty devicname -> use first device */ dev = first_dev; if (!dev) return SANE_STATUS_INVAL; s = malloc (sizeof (*s)); if (!s) return SANE_STATUS_NO_MEM; memset (s, 0, sizeof (*s)); s->fd = -1; s->pipe = -1; s->hw = dev; s->ld.ld_line = 0; s->halftone_pattern = malloc (8 * 8 * sizeof (SANE_Int)); if (!s->halftone_pattern) return SANE_STATUS_NO_MEM; init_options (s); /* insert newly opened handle into list of open handles: */ s->next = first_handle; first_handle = s; *handle = s; DBG (4, "sane_open: finished (handle=%p)\n", (void *) s); return SANE_STATUS_GOOD; } void sane_close (SANE_Handle handle) { Mustek_Scanner *prev, *s; DBG (4, "sane_close: handle=%p\n", handle); /* remove handle from list of open handles: */ prev = 0; for (s = first_handle; s; s = s->next) { if (s == handle) break; prev = s; } if (!s) { DBG (1, "sane_close: invalid handle %p\n", handle); return; /* oops, not a handle we know about */ } if (s->scanning) do_stop (handle); if (s->ld.buf[0]) free (s->ld.buf[0]); if (s->val[OPT_MODE].s) free (s->val[OPT_MODE].s); if (s->val[OPT_BIT_DEPTH].s) free (s->val[OPT_BIT_DEPTH].s); if (s->val[OPT_SPEED].s) free (s->val[OPT_SPEED].s); if (s->val[OPT_SOURCE].s) free (s->val[OPT_SOURCE].s); if (s->val[OPT_HALFTONE_DIMENSION].s) free (s->val[OPT_HALFTONE_DIMENSION].s); if (s->halftone_pattern) free (s->halftone_pattern); if (prev) prev->next = s->next; else first_handle = s->next; free (handle); handle = 0; DBG (5, "sane_close: finished\n"); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { Mustek_Scanner *s = handle; if (((unsigned) option >= NUM_OPTIONS) || (option < 0)) { DBG (4, "sane_get_option_descriptor: option %d >= NUM_OPTIONS or < 0\n", option); return 0; } if (!s) { DBG (1, "sane_get_option_descriptor: handle is null!\n"); return 0; } if (s->opt[option].name && s->opt[option].name[0] != 0) DBG (5, "sane_get_option_descriptor for option %s (%sactive%s)\n", s->opt[option].name, s->opt[option].cap & SANE_CAP_INACTIVE ? "in" : "", s->opt[option].cap & SANE_CAP_ADVANCED ? ", advanced" : ""); else DBG (5, "sane_get_option_descriptor for option \"%s\" (%sactive%s)\n", s->opt[option].title, s->opt[option].cap & SANE_CAP_INACTIVE ? "in" : "", s->opt[option].cap & SANE_CAP_ADVANCED ? ", advanced" : ""); return s->opt + option; } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { Mustek_Scanner *s = handle; SANE_Status status; SANE_Word w, cap; if (((unsigned) option >= NUM_OPTIONS) || (option < 0)) { DBG (4, "sane_control_option: option %d < 0 or >= NUM_OPTIONS\n", option); return SANE_STATUS_INVAL; } if (!s) { DBG (1, "sane_control_option: handle is null!\n"); return SANE_STATUS_INVAL; } if (s->opt[option].type != SANE_TYPE_BUTTON && !val) { DBG (1, "sane_control_option: val is null!\n"); return SANE_STATUS_INVAL; } if (s->opt[option].name && s->opt[option].name[0] != 0) DBG (5, "sane_control_option (%s option %s)\n", action == SANE_ACTION_GET_VALUE ? "get" : (action == SANE_ACTION_SET_VALUE ? "set" : "unknown action with"), s->opt[option].name); else DBG (5, "sane_control_option (%s option \"%s\")\n", action == SANE_ACTION_GET_VALUE ? "get" : (action == SANE_ACTION_SET_VALUE ? "set" : "unknown action with"), s->opt[option].title); if (info) *info = 0; if (s->scanning) { DBG (4, "sane_control_option: don't use while scanning (option %s)\n", s->opt[option].name); return SANE_STATUS_DEVICE_BUSY; } cap = s->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) { DBG (4, "sane_control_option: option %s is inactive\n", s->opt[option].name); return SANE_STATUS_INVAL; } if (action == SANE_ACTION_GET_VALUE) { switch (option) { /* word options: */ case OPT_PREVIEW: case OPT_FAST_PREVIEW: case OPT_RESOLUTION: case OPT_FAST_GRAY_MODE: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_NUM_OPTS: case OPT_BRIGHTNESS: case OPT_BRIGHTNESS_R: case OPT_BRIGHTNESS_G: case OPT_BRIGHTNESS_B: case OPT_CONTRAST: case OPT_CONTRAST_R: case OPT_CONTRAST_G: case OPT_CONTRAST_B: case OPT_CUSTOM_GAMMA: case OPT_QUALITY_CAL: case OPT_LAMP_OFF_TIME: *(SANE_Word *) val = s->val[option].w; return SANE_STATUS_GOOD; /* word-array options: */ case OPT_GAMMA_VECTOR: case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: case OPT_HALFTONE_PATTERN: memcpy (val, s->val[option].wa, s->opt[option].size); return SANE_STATUS_GOOD; /* string options: */ case OPT_SPEED: case OPT_SOURCE: case OPT_MODE: case OPT_BIT_DEPTH: case OPT_HALFTONE_DIMENSION: strcpy (val, s->val[option].s); return SANE_STATUS_GOOD; } } else if (action == SANE_ACTION_SET_VALUE) { if (!SANE_OPTION_IS_SETTABLE (cap)) { DBG (4, "sane_control_option: option %s is not setable\n", s->opt[option].name); return SANE_STATUS_INVAL; } status = constrain_value (s, option, val, info); if (status != SANE_STATUS_GOOD) { DBG (4, "sane_control_option: constrain_value error (option %s)\n", s->opt[option].name); return status; } switch (option) { case OPT_LAMP_OFF_BUTTON: { SANE_Int old_time = lamp_off_time; SANE_Status status; status = dev_open (s->hw->sane.name, s, sense_handler); if (status != SANE_STATUS_GOOD) return status; lamp_off_time = 0; set_window_pro (s); lamp_off_time = old_time; dev_close (s); return SANE_STATUS_GOOD; } /* (mostly) side-effect-free word options: */ case OPT_RESOLUTION: case OPT_TL_X: case OPT_BR_X: case OPT_TL_Y: case OPT_BR_Y: if (info) *info |= SANE_INFO_RELOAD_PARAMS; /* fall through */ case OPT_PREVIEW: case OPT_FAST_PREVIEW: case OPT_FAST_GRAY_MODE: case OPT_BRIGHTNESS: case OPT_BRIGHTNESS_R: case OPT_BRIGHTNESS_G: case OPT_BRIGHTNESS_B: case OPT_CONTRAST: case OPT_CONTRAST_R: case OPT_CONTRAST_G: case OPT_CONTRAST_B: case OPT_QUALITY_CAL: case OPT_LAMP_OFF_TIME: s->val[option].w = *(SANE_Word *) val; return SANE_STATUS_GOOD; /* side-effect-free word-array options: */ case OPT_HALFTONE_PATTERN: case OPT_GAMMA_VECTOR: case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: memcpy (s->val[option].wa, val, s->opt[option].size); return SANE_STATUS_GOOD; /* side-effect-free single-string options: */ case OPT_SPEED: if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); if (!s->val[option].s) return SANE_STATUS_NO_MEM; return SANE_STATUS_GOOD; /* side-effect-free string list options: */ case OPT_BIT_DEPTH: { SANE_Char *old_val = s->val[option].s; if (old_val) { if (strcmp (old_val, val) == 0) return SANE_STATUS_GOOD; /* no change */ free (old_val); } s->val[option].s = strdup (val); if (!s->val[option].s) return SANE_STATUS_NO_MEM; return SANE_STATUS_GOOD; } /* options with side-effects: */ case OPT_CUSTOM_GAMMA: w = *(SANE_Word *) val; if (w == s->val[OPT_CUSTOM_GAMMA].w) return SANE_STATUS_GOOD; /* no change */ if (info) *info |= SANE_INFO_RELOAD_OPTIONS; s->val[OPT_CUSTOM_GAMMA].w = w; if (w) { SANE_String_Const mode = s->val[OPT_MODE].s; if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; else if (strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) == 0) { s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; } else if ((strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0) && (s->hw->flags & MUSTEK_FLAG_PRO)) { s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; } } else { s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_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; } return SANE_STATUS_GOOD; case OPT_MODE: { SANE_Char *old_val = s->val[option].s; SANE_Int halftoning, binary; if (old_val) { if (strcmp (old_val, val) == 0) return SANE_STATUS_GOOD; /* no change */ free (old_val); } if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; s->val[option].s = strdup (val); if (!s->val[option].s) return SANE_STATUS_NO_MEM; s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BRIGHTNESS_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BRIGHTNESS_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_BRIGHTNESS_B].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CONTRAST_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CONTRAST_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CONTRAST_B].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; s->opt[OPT_HALFTONE_DIMENSION].cap |= SANE_CAP_INACTIVE; s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; halftoning = strcmp (val, SANE_VALUE_SCAN_MODE_HALFTONE) == 0; binary = (halftoning || strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) == 0); if (binary) { /* enable brightness/contrast for when in a binary mode */ s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; /* The SE and paragon models support only threshold in lineart */ if (!(s->hw->flags & MUSTEK_FLAG_SE) && !(s->hw->flags & MUSTEK_FLAG_PRO)) s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE; if (halftoning) { s->opt[OPT_HALFTONE_DIMENSION].cap &= ~SANE_CAP_INACTIVE; encode_halftone (s); if (s->custom_halftone_pattern) { s->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE; } else { s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; } } } else { s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE; } if (s->hw->flags & MUSTEK_FLAG_THREE_PASS) { if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0) { s->opt[OPT_BRIGHTNESS_R].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_BRIGHTNESS_G].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_BRIGHTNESS_B].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_CONTRAST_R].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_CONTRAST_G].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_CONTRAST_B].cap &= ~SANE_CAP_INACTIVE; } else { s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE; } } else if (s->hw->flags & MUSTEK_FLAG_PRO) { if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY) == 0) s->opt[OPT_FAST_GRAY_MODE].cap &= ~SANE_CAP_INACTIVE; else s->opt[OPT_FAST_GRAY_MODE].cap |= SANE_CAP_INACTIVE; if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0) s->opt[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE; else s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; } else if (s->hw->flags & MUSTEK_FLAG_SE_PLUS) { if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0) s->opt[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE; else s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; } if (s->val[OPT_CUSTOM_GAMMA].w) { if (strcmp (val, SANE_VALUE_SCAN_MODE_GRAY) == 0) s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; else if (strcmp (val, SANE_VALUE_SCAN_MODE_COLOR) == 0) { 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; } } return SANE_STATUS_GOOD; } case OPT_HALFTONE_DIMENSION: /* halftone pattern dimension affects halftone pattern option: */ { if (strcmp (s->val[option].s, (SANE_String) val) == 0) return SANE_STATUS_GOOD; /* no change */ if (info) *info |= SANE_INFO_RELOAD_OPTIONS; s->val[option].s = strdup (val); if (!s->val[option].s) return SANE_STATUS_NO_MEM; encode_halftone (s); s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; if (s->custom_halftone_pattern) { s->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE; /* BUG: The SANE standard does nor allow to change the option size at run time */ s->opt[OPT_HALFTONE_PATTERN].size = (s->halftone_pattern_type & 0x0f) * sizeof (SANE_Word); } return SANE_STATUS_GOOD; } case OPT_SOURCE: if (info) *info |= SANE_INFO_RELOAD_OPTIONS; if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); if (!s->val[option].s) return SANE_STATUS_NO_MEM; if (strcmp (val, "Transparency Adapter") == 0) { s->opt[OPT_TL_X].constraint.range = &s->hw->x_trans_range; s->opt[OPT_TL_Y].constraint.range = &s->hw->y_trans_range; s->opt[OPT_BR_X].constraint.range = &s->hw->x_trans_range; s->opt[OPT_BR_Y].constraint.range = &s->hw->y_trans_range; } else { s->opt[OPT_TL_X].constraint.range = &s->hw->x_range; s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range; s->opt[OPT_BR_X].constraint.range = &s->hw->x_range; s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range; } return SANE_STATUS_GOOD; } } DBG (4, "sane_control_option: unknown action for option %s\n", s->opt[option].name); return SANE_STATUS_INVAL; } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { Mustek_Scanner *s = handle; SANE_String_Const mode; if (!s) { DBG (1, "sane_get_parameters: handle is null!\n"); return SANE_STATUS_INVAL; } if (!s->scanning) { double width, height, dpi; memset (&s->params, 0, sizeof (s->params)); width = SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w); height = SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w); dpi = SANE_UNFIX (s->val[OPT_RESOLUTION].w); /* make best-effort guess at what parameters will look like once scanning starts. */ if (dpi > 0.0 && width > 0.0 && height > 0.0) { double dots_per_mm = dpi / MM_PER_INCH; s->params.pixels_per_line = width * dots_per_mm; s->params.lines = height * dots_per_mm; } encode_halftone (s); mode = s->val[OPT_MODE].s; if (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0 || strcmp (mode, SANE_VALUE_SCAN_MODE_HALFTONE) == 0) { s->params.format = SANE_FRAME_GRAY; s->params.bytes_per_line = (s->params.pixels_per_line + 7) / 8; s->params.depth = 1; } else if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) { s->params.format = SANE_FRAME_GRAY; s->params.bytes_per_line = s->params.pixels_per_line; s->params.depth = 8; } else { /* it's one of the color modes... */ if (s->hw->flags & MUSTEK_FLAG_THREE_PASS) { s->params.format = SANE_FRAME_RED + s->pass; s->params.bytes_per_line = s->params.pixels_per_line; s->params.depth = 8; } else /* 1-pass */ { if (strcmp (s->val[OPT_BIT_DEPTH].s, "12") == 0) { s->params.bytes_per_line = 6 * s->params.pixels_per_line; s->params.depth = 16; } else { s->params.bytes_per_line = 3 * s->params.pixels_per_line; s->params.depth = 8; } s->params.format = SANE_FRAME_RGB; } } } else if ((s->mode & MUSTEK_MODE_COLOR) && (s->hw->flags & MUSTEK_FLAG_THREE_PASS)) s->params.format = SANE_FRAME_RED + s->pass; s->params.last_frame = (s->params.format != SANE_FRAME_RED && s->params.format != SANE_FRAME_GREEN); if (params) *params = s->params; DBG (4, "sane_get_parameters: frame = %d; last_frame = %s; depth = %d\n", s->params.format, s->params.last_frame ? "true" : "false", s->params.depth); DBG (4, "sane_get_parameters: lines = %d; ppl = %d; bpl = %d\n", s->params.lines, s->params.pixels_per_line, s->params.bytes_per_line); return SANE_STATUS_GOOD; } SANE_Status sane_start (SANE_Handle handle) { Mustek_Scanner *s = handle; SANE_Status status; int fds[2]; struct SIGACTION act; if (!s) { DBG (1, "sane_start: handle is null!\n"); return SANE_STATUS_INVAL; } DBG (4, "sane_start\n"); /* First make sure we have a current parameter set. Some of the parameters will be overwritten below, but that's OK. */ status = sane_get_parameters (s, 0); if (status != SANE_STATUS_GOOD) return status; /* Check for inconsistencies */ if (s->val[OPT_TL_X].w > s->val[OPT_BR_X].w) { DBG (0, "sane_start: %s (%.1f mm) is bigger than %s (%.1f mm) " "-- aborting\n", s->opt[OPT_TL_X].title, SANE_UNFIX (s->val[OPT_TL_X].w), s->opt[OPT_BR_X].title, SANE_UNFIX (s->val[OPT_BR_X].w)); return SANE_STATUS_INVAL; } if (s->val[OPT_TL_Y].w > s->val[OPT_BR_Y].w) { DBG (0, "sane_start: %s (%.1f mm) is bigger than %s (%.1f mm) " "-- aborting\n", s->opt[OPT_TL_Y].title, SANE_UNFIX (s->val[OPT_TL_Y].w), s->opt[OPT_BR_Y].title, SANE_UNFIX (s->val[OPT_BR_Y].w)); return SANE_STATUS_INVAL; } s->total_bytes = 0; if (s->fd < 0) { /* this is the first (and maybe only) pass... */ SANE_String_Const mode; struct timeval start; /* save start time */ gettimeofday (&start, 0); s->start_time = start.tv_sec; /* translate options into s->mode for convenient access: */ mode = s->val[OPT_MODE].s; if (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0) s->mode = MUSTEK_MODE_LINEART; else if (strcmp (mode, SANE_VALUE_SCAN_MODE_HALFTONE) == 0) s->mode = MUSTEK_MODE_HALFTONE; else if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) s->mode = MUSTEK_MODE_GRAY; else if (strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) == 0) s->mode = MUSTEK_MODE_COLOR; /* scanner dependent specials */ s->one_pass_color_scan = SANE_FALSE; if ((s->mode & MUSTEK_MODE_COLOR) && !(s->hw->flags & MUSTEK_FLAG_THREE_PASS)) { s->one_pass_color_scan = SANE_TRUE; } s->resolution_code = encode_resolution (s); if (s->val[OPT_PREVIEW].w && s->val[OPT_FAST_PREVIEW].w) { if (s->hw->flags & MUSTEK_FLAG_THREE_PASS) { if (s->mode & MUSTEK_MODE_COLOR) { /* Force gray-scale mode when previewing. */ s->mode = MUSTEK_MODE_GRAY; s->params.format = SANE_FRAME_GRAY; s->params.bytes_per_line = s->params.pixels_per_line; s->params.last_frame = SANE_TRUE; } } else if (s->hw->flags & MUSTEK_FLAG_SE) { /* use 36 dpi color in any case */ s->mode = MUSTEK_MODE_COLOR; s->params.format = SANE_FRAME_RGB; s->params.depth = 8; s->one_pass_color_scan = SANE_TRUE; s->resolution_code = 36; } else if (s->hw->flags & MUSTEK_FLAG_PARAGON_1) { /* use 36 dpi */ s->resolution_code = 36; } else if (s->hw->flags & MUSTEK_FLAG_PRO) { /* use 30 dpi color mode */ s->mode = MUSTEK_MODE_COLOR; s->params.format = SANE_FRAME_RGB; s->params.depth = 8; s->one_pass_color_scan = SANE_TRUE; s->resolution_code = 30; } DBG (4, "sane_start: use fast preview (res=%d dpi)\n", s->resolution_code); } status = dev_open (s->hw->sane.name, s, sense_handler); if (status != SANE_STATUS_GOOD) return status; } status = dev_wait_ready (s); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: wait_ready() failed: %s\n", sane_strstatus (status)); goto stop_scanner_and_return; } if (!(s->hw->flags & MUSTEK_FLAG_SCSI_PP)) { /* SCSI-over-parallel port doesn't seem to like being inquired here */ status = inquiry (s); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: inquiry command failed: %s\n", sane_strstatus (status)); goto stop_scanner_and_return; } } if ((strcmp (s->val[OPT_SOURCE].s, "Automatic Document Feeder") == 0) && !(s->hw->flags & MUSTEK_FLAG_ADF_READY)) { DBG (2, "sane_start: automatic document feeder is out of documents\n"); status = SANE_STATUS_NO_DOCS; goto stop_scanner_and_return; } if (s->hw->flags & MUSTEK_FLAG_SE) { status = set_window_se (s, 0); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: set window command failed: %s\n", sane_strstatus (status)); goto stop_scanner_and_return; } s->scanning = SANE_TRUE; s->cancelled = SANE_FALSE; dev_wait_ready (s); status = get_window (s, &s->params.bytes_per_line, &s->params.lines, &s->params.pixels_per_line); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: get window command failed: %s\n", sane_strstatus (status)); goto stop_scanner_and_return; } status = calibration_se (s); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; status = start_scan (s); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; status = send_gamma_table_se (s); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; } else if (s->hw->flags & MUSTEK_FLAG_PRO) { status = dev_wait_ready (s); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; status = set_window_pro (s); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; s->scanning = SANE_TRUE; s->cancelled = SANE_FALSE; status = adf_and_backtrack (s); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; status = mode_select_pro (s); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; status = scsi_sense_wait_ready (s); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; status = calibration_pro (s); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; status = send_gamma_table (s); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; status = start_scan (s); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; status = get_image_status (s, &s->params.bytes_per_line, &s->params.lines); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; status = scsi_sense_wait_ready (s); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; } else /* Paragon series */ { status = area_and_windows (s); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: set scan area command failed: %s\n", sane_strstatus (status)); goto stop_scanner_and_return; } status = adf_and_backtrack (s); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; if (s->one_pass_color_scan) { status = mode_select_paragon (s, MUSTEK_CODE_RED); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; status = mode_select_paragon (s, MUSTEK_CODE_GREEN); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; status = mode_select_paragon (s, MUSTEK_CODE_BLUE); } else status = mode_select_paragon (s, MUSTEK_CODE_GRAY); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; s->scanning = SANE_TRUE; s->cancelled = SANE_FALSE; status = send_gamma_table (s); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; status = start_scan (s); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; if (!(s->hw->flags & MUSTEK_FLAG_SCSI_PP)) { /* This second gamma table download upsets the SCSI-over-parallel models */ status = send_gamma_table (s); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; } s->ld.max_value = 0; if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS)) { status = line_distance (s); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; } status = get_image_status (s, &s->params.bytes_per_line, &s->params.lines); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; if ((strcmp (s->val[OPT_SOURCE].s, "Automatic Document Feeder") == 0) && (s->hw->flags & MUSTEK_FLAG_PARAGON_2)) { status = paragon_2_get_adf_status (s); if (status != SANE_STATUS_GOOD) goto stop_scanner_and_return; } } s->params.pixels_per_line = s->params.bytes_per_line; if (s->one_pass_color_scan) { if (strcmp (s->val[OPT_BIT_DEPTH].s, "12") == 0) s->params.pixels_per_line /= 6; else s->params.pixels_per_line /= 3; } else if ((s->mode & MUSTEK_MODE_LINEART) || (s->mode & MUSTEK_MODE_HALFTONE)) s->params.pixels_per_line *= 8; s->line = 0; /* don't call any SIGTERM or SIGCHLD handlers this is to stop xsane and other frontends from calling its quit handlers */ memset (&act, 0, sizeof (act)); sigaction (SIGTERM, &act, 0); sigaction (SIGCHLD, &act, 0); if (pipe (fds) < 0) return SANE_STATUS_IO_ERROR; s->reader_fds = fds[1]; /* create reader routine as new process or thread */ s->reader_pid = sanei_thread_begin (reader_process, (void *) s); if (!sanei_thread_is_valid (s->reader_pid)) { DBG (1, "sane_start: sanei_thread_begin failed (%s)\n", strerror (errno)); return SANE_STATUS_NO_MEM; } if (sanei_thread_is_forked ()) { close (s->reader_fds); s->reader_fds = -1; } s->pipe = fds[0]; return SANE_STATUS_GOOD; stop_scanner_and_return: do_stop (s); return status; } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { Mustek_Scanner *s = handle; SANE_Status status; ssize_t nread; if (!s) { DBG (1, "sane_read: handle is null!\n"); return SANE_STATUS_INVAL; } if (!buf) { DBG (1, "sane_read: buf is null!\n"); return SANE_STATUS_INVAL; } if (!len) { DBG (1, "sane_read: len is null!\n"); return SANE_STATUS_INVAL; } DBG (5, "sane_read\n"); *len = 0; if (s->cancelled) { DBG (4, "sane_read: scan was cancelled\n"); return SANE_STATUS_CANCELLED; } if (!s->scanning) { DBG (3, "sane_read: must call sane_start before sane_read\n"); return SANE_STATUS_INVAL; } while (*len < max_len) { nread = read (s->pipe, buf + *len, max_len - *len); if (s->cancelled) { DBG (4, "sane_read: scan was cancelled\n"); *len = 0; return SANE_STATUS_CANCELLED; } if (nread < 0) { if (errno == EAGAIN) { if (*len == 0) DBG (5, "sane_read: no more data at the moment--try again\n"); else DBG (5, "sane_read: read buffer of %d bytes " "(%d bytes total)\n", *len, s->total_bytes); return SANE_STATUS_GOOD; } else { DBG (1, "sane_read: IO error\n"); do_stop (s); *len = 0; return SANE_STATUS_IO_ERROR; } } *len += nread; s->total_bytes += nread; if (nread == 0) { if (*len == 0) { if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS) || !(s->mode & MUSTEK_MODE_COLOR) || ++s->pass >= 3) { DBG (5, "sane_read: pipe was closed ... calling do_stop\n"); status = do_stop (s); if (status != SANE_STATUS_CANCELLED && status != SANE_STATUS_GOOD) return status; /* something went wrong */ } else /* 3pass color first or second pass */ { DBG (5, "sane_read: pipe was closed ... finishing pass %d\n", s->pass); } return do_eof (s); } else { DBG (5, "sane_read: read last buffer of %d bytes " "(%d bytes total)\n", *len, s->total_bytes); return SANE_STATUS_GOOD; } } } DBG (5, "sane_read: read full buffer of %d bytes (%d total bytes)\n", *len, s->total_bytes); return SANE_STATUS_GOOD; } void sane_cancel (SANE_Handle handle) { Mustek_Scanner *s = handle; if (!s) { DBG (1, "sane_cancel: handle is null!\n"); return; } DBG (4, "sane_cancel\n"); if (s->scanning) { s->cancelled = SANE_TRUE; do_stop (handle); } DBG (5, "sane_cancel: finished\n"); } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { Mustek_Scanner *s = handle; if (!s) { DBG (1, "sane_set_io_mode: handle is null!\n"); return SANE_STATUS_INVAL; } DBG (4, "sane_set_io_mode: %s\n", non_blocking ? "non-blocking" : "blocking"); if (!s->scanning) { DBG (1, "sane_set_io_mode: call sane_start before sane_set_io_mode"); return SANE_STATUS_INVAL; } if (fcntl (s->pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0) { DBG (1, "sane_set_io_mode: can't set io mode"); return SANE_STATUS_IO_ERROR; } return SANE_STATUS_GOOD; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { Mustek_Scanner *s = handle; if (!s) { DBG (1, "sane_get_select_fd: handle is null!\n"); return SANE_STATUS_INVAL; } if (!fd) { DBG (1, "sane_get_select_fd: fd is null!\n"); return SANE_STATUS_INVAL; } DBG (4, "sane_get_select_fd\n"); if (!s->scanning) return SANE_STATUS_INVAL; *fd = s->pipe; return SANE_STATUS_GOOD; } #include "mustek_scsi_pp.c" backends-1.3.0/backend/mustek.conf.in000066400000000000000000000041061456256263500174570ustar00rootroot00000000000000# 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 necessary with firmware 2.x #-------------------------- 600 II EP --------------------------------------- #parport0 # parport0, parport1, ..., # or: 0x378(=lpt1), 0x278(=lpt2), 0x3bc(=lpt3) backends-1.3.0/backend/mustek.h000066400000000000000000000236241456256263500163620ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1996, 1997 David Mosberger-Tang, 1998 Andreas Bolsch for extension to ScanExpress models version 0.5, 2000 - 2005 Henning Meier-Geinitz. 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, see . As a special exception, the authors of SANE give permission for additional uses of the libraries contained in this release of SANE. The exception is that, if you link a SANE library with other files to produce an executable, this does not by itself cause the resulting executable to be covered by the GNU General Public License. Your use of that executable is in no way restricted on account of linking the SANE library code into it. This exception does not, however, invalidate any other reasons why the executable file might be covered by the GNU General Public License. If you submit changes to SANE to the maintainers to be included in a subsequent release, you agree by submitting the changes that those changes may be distributed with this exception intact. If you write modifications of your own for SANE, it is your choice whether to permit this exception to apply to your modifications. If you do not wish that, delete this exception notice. This file implements a SANE backend for Mustek and some Trust flatbed scanners with SCSI or proprietary interface. */ #ifndef mustek_h #define mustek_h #include "../include/sane/config.h" #include /* Some constants */ #define INQ_LEN 0x60 /* Length of SCSI inquiry */ #ifndef PATH_MAX # define PATH_MAX 1024 #endif #define MUSTEK_CONFIG_FILE "mustek.conf" #define MAX_WAITING_TIME 60 /* How long to wait for scanner to become ready */ #define MAX_LINE_DIST 40 /* Extra lines needed for LD correction */ /* Flag values */ /* Scanner types */ #define MUSTEK_FLAG_THREE_PASS (1 << 0) /* three pass scanner */ #define MUSTEK_FLAG_PARAGON_1 (1 << 1) /* Paragon series I scanner */ #define MUSTEK_FLAG_PARAGON_2 (1 << 2) /* Paragon series II(A4) scanner */ #define MUSTEK_FLAG_SE (1 << 3) /* ScanExpress scanner */ #define MUSTEK_FLAG_SE_PLUS (1 << 4) /* ScanExpress Plus scanner */ #define MUSTEK_FLAG_PRO (1 << 5) /* Professional series scanner */ #define MUSTEK_FLAG_N (1 << 6) /* N-type scanner (non SCSI) */ #define MUSTEK_FLAG_SCSI_PP (1 << 22) /* SCSI over parallel (e.g. 600 II EP) */ /* Additional equipment */ #define MUSTEK_FLAG_ADF (1 << 7) /* automatic document feeder */ #define MUSTEK_FLAG_ADF_READY (1 << 8) /* paper present */ #define MUSTEK_FLAG_TA (1 << 9) /* transparency adapter */ /* Line-distance correction */ #define MUSTEK_FLAG_LD_NONE (1 << 10) /* no line-distance corr */ #define MUSTEK_FLAG_LD_BLOCK (1 << 11) /* blockwise LD corr */ #define MUSTEK_FLAG_LD_N1 (1 << 12) /* LD corr for N-type v1 */ #define MUSTEK_FLAG_LD_N2 (1 << 13) /* LD corr for N-type v2 */ /* Manual fixes */ #define MUSTEK_FLAG_LD_FIX (1 << 14) /* need line-distance fix? */ #define MUSTEK_FLAG_LINEART_FIX (1 << 15) /* lineart fix/hack */ #define MUSTEK_FLAG_USE_EIGHTS (1 << 16) /* use 1/8" lengths */ #define MUSTEK_FLAG_FORCE_GAMMA (1 << 17) /* force gamma table upload */ #define MUSTEK_FLAG_ENLARGE_X (1 << 18) /* need to enlarge x-res */ #define MUSTEK_FLAG_COVER_SENSOR (1 << 19) /* scanner can detect open cover */ #define MUSTEK_FLAG_USE_BLOCK (1 << 20) /* use blockmode */ #define MUSTEK_FLAG_LEGAL_SIZE (1 << 21) /* scanner has legal size */ #define MUSTEK_FLAG_NO_BACKTRACK (1 << 21) /* scanner has legal size */ /* Source values: */ #define MUSTEK_SOURCE_FLATBED 0 #define MUSTEK_SOURCE_ADF 1 #define MUSTEK_SOURCE_TA 2 /* Mode values: */ #define MUSTEK_MODE_LINEART (1 << 0) /* grayscale 1 bit / pixel */ #define MUSTEK_MODE_GRAY (1 << 1) /* grayscale 8 bits / pixel */ #define MUSTEK_MODE_COLOR (1 << 2) /* color 24 bits / pixel */ #define MUSTEK_MODE_HALFTONE (1 << 3) /* use dithering */ /* Color band codes: */ #define MUSTEK_CODE_GRAY 0 #define MUSTEK_CODE_RED 1 #define MUSTEK_CODE_GREEN 2 #define MUSTEK_CODE_BLUE 3 /* SCSI commands that the Mustek scanners understand (or not): */ #define MUSTEK_SCSI_TEST_UNIT_READY 0x00 #define MUSTEK_SCSI_REQUEST_SENSE 0x03 #define MUSTEK_SCSI_AREA_AND_WINDOWS 0x04 #define MUSTEK_SCSI_READ_SCANNED_DATA 0x08 #define MUSTEK_SCSI_GET_IMAGE_STATUS 0x0f #define MUSTEK_SCSI_ADF_AND_BACKTRACK 0x10 #define MUSTEK_SCSI_CCD_DISTANCE 0x11 #define MUSTEK_SCSI_INQUIRY 0x12 #define MUSTEK_SCSI_MODE_SELECT 0x15 #define MUSTEK_SCSI_START_STOP 0x1b #define MUSTEK_SCSI_SET_WINDOW 0x24 #define MUSTEK_SCSI_GET_WINDOW 0x25 #define MUSTEK_SCSI_READ_DATA 0x28 #define MUSTEK_SCSI_SEND_DATA 0x2a #define MUSTEK_SCSI_LOOKUP_TABLE 0x55 /* Convenience macros */ #if defined(MIN) #undef MIN #endif #if defined(MAX) #undef MAX #endif #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) /* Copy values to memory ('L' = little endian, 'B' = big endian */ #define STORE16L(cp,v) \ do { \ int value = (v); \ \ *(cp)++ = (value >> 0) & 0xff; \ *(cp)++ = (value >> 8) & 0xff; \ } while (0) #define STORE16B(cp,v) \ do { \ int value = (v); \ \ *(cp)++ = (value >> 8) & 0xff; \ *(cp)++ = (value >> 0) & 0xff; \ } while (0) #define STORE32B(cp,v) \ do { \ long int value = (v); \ \ *(cp)++ = (value >> 24) & 0xff; \ *(cp)++ = (value >> 16) & 0xff; \ *(cp)++ = (value >> 8) & 0xff; \ *(cp)++ = (value >> 0) & 0xff; \ } while (0) /* declarations */ enum Mustek_Option { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_MODE, OPT_FAST_GRAY_MODE, OPT_RESOLUTION, OPT_BIT_DEPTH, OPT_SPEED, OPT_SOURCE, OPT_PREVIEW, OPT_FAST_PREVIEW, OPT_LAMP_OFF_TIME, OPT_LAMP_OFF_BUTTON, OPT_GEOMETRY_GROUP, OPT_TL_X, /* top-left x */ OPT_TL_Y, /* top-left y */ OPT_BR_X, /* bottom-right x */ OPT_BR_Y, /* bottom-right y */ OPT_ENHANCEMENT_GROUP, OPT_BRIGHTNESS, OPT_BRIGHTNESS_R, OPT_BRIGHTNESS_G, OPT_BRIGHTNESS_B, OPT_CONTRAST, OPT_CONTRAST_R, OPT_CONTRAST_G, OPT_CONTRAST_B, OPT_CUSTOM_GAMMA, /* use custom gamma tables? */ /* The gamma vectors MUST appear in the order gray, red, green, blue. */ OPT_GAMMA_VECTOR, OPT_GAMMA_VECTOR_R, OPT_GAMMA_VECTOR_G, OPT_GAMMA_VECTOR_B, OPT_QUALITY_CAL, OPT_HALFTONE_DIMENSION, OPT_HALFTONE_PATTERN, /* must come last: */ NUM_OPTIONS }; typedef struct Mustek_Device { struct Mustek_Device *next; SANE_String name; SANE_Device sane; SANE_Range dpi_range; SANE_Range x_range; SANE_Range y_range; /* scan area when transparency adapter is used: */ SANE_Range x_trans_range; SANE_Range y_trans_range; SANE_Word flags; /* length of gamma table, probably always <= 4096 for the SE */ SANE_Int gamma_length; /* values actually used by scanner, not necessarily the desired! */ SANE_Int bpl, lines; /* what is needed for calibration (ScanExpress and Pro series) */ struct { SANE_Int bytes; SANE_Int lines; SANE_Byte *buffer; SANE_Word *line_buffer[3]; } cal; /* current and maximum buffer size used by the backend */ /* the buffer sent to the scanner is actually half of this size */ SANE_Int buffer_size; SANE_Int max_buffer_size; /* maximum size scanned in one block and corresponding lines */ SANE_Int max_block_buffer_size; SANE_Int lines_per_block; SANE_Byte *block_buffer; /* firmware format: 0 = old, MUSTEK at pos 8; 1 = new, MUSTEK at pos 36 */ SANE_Int firmware_format; /* firmware revision system: 0 = old, x.yz; 1 = new, Vxyz */ SANE_Int firmware_revision_system; } Mustek_Device; typedef struct Mustek_Scanner { /* all the state needed to define a scan request: */ struct Mustek_Scanner *next; SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; SANE_Int gamma_table[4][256]; SANE_Int *halftone_pattern; SANE_Bool custom_halftone_pattern; SANE_Int halftone_pattern_type; SANE_Bool scanning; SANE_Bool cancelled; SANE_Int pass; /* pass number */ SANE_Int line; /* current line number */ SANE_Parameters params; /* Parsed option values and variables that are valid only during actual scanning: */ SANE_Word mode; SANE_Bool one_pass_color_scan; SANE_Int resolution_code; int fd; /* SCSI filedescriptor */ SANE_Pid reader_pid; /* process id of reader */ int reader_fds; /* OS/2: pipe write handler for reader */ int pipe; /* pipe to reader process */ long start_time; /* at this time the scan started */ SANE_Word total_bytes; /* bytes transmitted by sane_read */ SANE_Word total_lines; /* lines transmitted to sane_read pipe */ /* scanner dependent/low-level state: */ Mustek_Device *hw; /* line-distance correction related state: */ struct { SANE_Int color; /* first color appearing in read data */ SANE_Int max_value; SANE_Int peak_res; SANE_Int dist[3]; /* line distance */ SANE_Int index[3]; /* index for R/G/B color assignment */ SANE_Int quant[3]; /* for resolution correction */ SANE_Int saved[3]; /* number of saved color lines */ /* these are used for SE, MFS and N line-distance correction: */ SANE_Byte *buf[3]; /* these are used for N line-distance correction only: */ SANE_Int ld_line; /* line # currently processed in ld-correction */ SANE_Int lmod3; /* line # modulo 3 */ } ld; } Mustek_Scanner; #endif /* mustek_h */ backends-1.3.0/backend/mustek_pp.c000066400000000000000000001452231456256263500170540ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2000-2003 Jochen Eisinger This file is part of the SANE package. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . As a special exception, the authors of SANE give permission for additional uses of the libraries contained in this release of SANE. The exception is that, if you link a SANE library with other files to produce an executable, this does not by itself cause the resulting executable to be covered by the GNU General Public License. Your use of that executable is in no way restricted on account of linking the SANE library code into it. This exception does not, however, invalidate any other reasons why the executable file might be covered by the GNU General Public License. If you submit changes to SANE to the maintainers to be included in a subsequent release, you agree by submitting the changes that those changes may be distributed with this exception intact. If you write modifications of your own for SANE, it is your choice whether to permit this exception to apply to your modifications. If you do not wish that, delete this exception notice. This file implements a SANE backend for Mustek PP flatbed scanners. */ #include "../include/sane/config.h" #if defined(HAVE_STDLIB_H) # include #endif #include #include #include #include #include #if defined(HAVE_STRING_H) # include #elif defined(HAVE_STRINGS_H) # include #endif #if defined(HAVE_UNISTD_H) # include #endif #include #include #include #if defined(HAVE_SYS_TIME_H) # include #endif #if defined(HAVE_SYS_TYPES_H) # include #endif #include #define BACKEND_NAME mustek_pp #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_config.h" #define MUSTEK_PP_CONFIG_FILE "mustek_pp.conf" #include "../include/sane/sanei_pa4s2.h" #include "mustek_pp.h" #include "mustek_pp_drivers.h" #define MIN(a,b) ((a) < (b) ? (a) : (b)) /* converts millimeter to pixels at a given resolution */ #define MM_TO_PIXEL(mm, dpi) (((float )mm * 5.0 / 127.0) * (float)dpi) /* and back */ #define PIXEL_TO_MM(pixel, dpi) (((float )pixel / (float )dpi) * 127.0 / 5.0) /* if you change the source, please set MUSTEK_PP_STATE to "devel". Do *not* * change the MUSTEK_PP_BUILD. */ #define MUSTEK_PP_BUILD 13 #define MUSTEK_PP_STATE "beta" /* auth callback... since basic user authentication is done by saned, this * callback mechanism isn't used */ SANE_Auth_Callback sane_auth; /* count of present devices */ static int num_devices = 0; /* list of present devices */ static Mustek_pp_Device *devlist = NULL; /* temporary array of configuration options used during device attachment */ static Mustek_pp_config_option *cfgoptions = NULL; static int numcfgoptions = 0; /* list of pointers to the SANE_Device structures of the Mustek_pp_Devices */ static SANE_Device **devarray = NULL; /* currently active Handles */ static Mustek_pp_Handle *first_hndl = NULL; static SANE_String_Const mustek_pp_modes[4] = {SANE_VALUE_SCAN_MODE_LINEART, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_COLOR, NULL}; static SANE_Word mustek_pp_modes_size = 10; static SANE_String_Const mustek_pp_speeds[6] = {"Slowest", "Slower", "Normal", "Faster", "Fastest", NULL}; static SANE_Word mustek_pp_speeds_size = 8; static SANE_Word mustek_pp_depths[5] = {4, 8, 10, 12, 16}; /* prototypes */ static void free_cfg_options(int *numoptions, Mustek_pp_config_option** options); static SANE_Status do_eof(Mustek_pp_Handle *hndl); static SANE_Status do_stop(Mustek_pp_Handle *hndl); static int reader_process (Mustek_pp_Handle * hndl, int pipe); static SANE_Status sane_attach(SANE_String_Const port, SANE_String_Const name, SANE_Int driver, SANE_Int info); static void init_options(Mustek_pp_Handle *hndl); static void attach_device(SANE_String *driver, SANE_String *name, SANE_String *port, SANE_String *option_ta); /* * Auxiliary function for freeing arrays of configuration options, */ static void free_cfg_options(int *numoptions, Mustek_pp_config_option** options) { int i; if (*numoptions) { for (i=0; i<*numoptions; ++i) { free ((*options)[i].name); free ((*options)[i].value); } free (*options); } *options = NULL; *numoptions = 0; } /* do_eof: * closes the pipeline * * Description: * closes the pipe (read-only end) */ static SANE_Status do_eof (Mustek_pp_Handle *hndl) { if (hndl->pipe >= 0) { close (hndl->pipe); hndl->pipe = -1; } return SANE_STATUS_EOF; } /* do_stop: * ends the reader_process and stops the scanner * * Description: * kills the reader process with a SIGTERM and cancels the scanner */ static SANE_Status do_stop(Mustek_pp_Handle *hndl) { int exit_status; do_eof (hndl); if (hndl->reader > 0) { DBG (3, "do_stop: terminating reader process\n"); kill (hndl->reader, SIGTERM); while (wait (&exit_status) != hndl->reader); DBG ((exit_status == SANE_STATUS_GOOD ? 3 : 1), "do_stop: reader_process terminated with status ``%s''\n", sane_strstatus(exit_status)); hndl->reader = 0; hndl->dev->func->stop (hndl); return exit_status; } hndl->dev->func->stop (hndl); return SANE_STATUS_GOOD; } /* sigterm_handler: * cancel scanner when receiving a SIGTERM * * Description: * just exit... reader_process takes care that nothing bad will happen * * EDG - Jan 14, 2004: * Make sure that the parport is released again by the child process * under all circumstances, because otherwise the parent process may no * longer be able to claim it (they share the same file descriptor, and * the kernel doesn't release the child's claim because the file * descriptor isn't cleaned up). If that would happen, the lamp may stay * on and may not return to its home position, unless the scanner * frontend is restarted. * (This happens only when sanei_pa4s2 uses libieee1284 AND * libieee1284 goes via /dev/parportX). * */ static int fd_to_release = 0; /*ARGSUSED*/ static void sigterm_handler (int signal __UNUSED__) { sanei_pa4s2_enable(fd_to_release, SANE_FALSE); _exit (SANE_STATUS_GOOD); } /* reader_process: * receives data from the scanner and stuff it into the pipeline * * Description: * The signal handle for SIGTERM is initialized. * */ static int reader_process (Mustek_pp_Handle * hndl, int pipe) { sigset_t sigterm_set; struct SIGACTION act; FILE *fp; SANE_Status status; int line; int size, elem; SANE_Byte *buffer; sigemptyset (&sigterm_set); sigaddset (&sigterm_set, SIGTERM); if (!(buffer = malloc (hndl->params.bytes_per_line))) return SANE_STATUS_NO_MEM; if (!(fp = fdopen(pipe, "w"))) return SANE_STATUS_IO_ERROR; fd_to_release = hndl->fd; memset (&act, 0, sizeof(act)); act.sa_handler = sigterm_handler; sigaction (SIGTERM, &act, NULL); if ((status = hndl->dev->func->start (hndl)) != SANE_STATUS_GOOD) return status; size = hndl->params.bytes_per_line; elem = 1; for (line=0; lineparams.lines ; line++) { sigprocmask (SIG_BLOCK, &sigterm_set, NULL); hndl->dev->func->read (hndl, buffer); if (getppid() == 1) { /* The parent process has died. Stop the scan (to make sure that the lamp is off and returns home). This is a safety measure to make sure that we don't break the scanner in case the frontend crashes. */ DBG (1, "reader_process: front-end died; aborting.\n"); hndl->dev->func->stop (hndl); return SANE_STATUS_CANCELLED; } sigprocmask (SIG_UNBLOCK, &sigterm_set, NULL); fwrite (buffer, size, elem, fp); } fclose (fp); free (buffer); return SANE_STATUS_GOOD; } /* sane_attach: * adds a new entry to the Mustek_pp_Device *devlist list * * Description: * After memory for a new device entry is allocated, the * parameters for the device are determined by a call to * capabilities(). * * Afterwards the new device entry is inserted into the * devlist * */ static SANE_Status sane_attach (SANE_String_Const port, SANE_String_Const name, SANE_Int driver, SANE_Int info) { Mustek_pp_Device *dev; DBG (3, "sane_attach: attaching device ``%s'' to port %s (driver %s v%s by %s)\n", name, port, Mustek_pp_Drivers[driver].driver, Mustek_pp_Drivers[driver].version, Mustek_pp_Drivers[driver].author); if ((dev = malloc (sizeof (Mustek_pp_Device))) == NULL) { DBG (1, "sane_attach: not enough free memory\n"); return SANE_STATUS_NO_MEM; } memset (dev, 0, sizeof (Mustek_pp_Device)); memset (&dev->sane, 0, sizeof (SANE_Device)); dev->func = &Mustek_pp_Drivers[driver]; dev->sane.name = dev->name = strdup (name); dev->port = strdup (port); dev->info = info; /* Modified by EDG */ /* Transfer the options parsed from the configuration file */ dev->numcfgoptions = numcfgoptions; dev->cfgoptions = cfgoptions; numcfgoptions = 0; cfgoptions = NULL; dev->func->capabilities (info, &dev->model, &dev->vendor, &dev->type, &dev->maxres, &dev->minres, &dev->maxhsize, &dev->maxvsize, &dev->caps); dev->sane.model = dev->model; dev->sane.vendor = dev->vendor; dev->sane.type = dev->type; dev->next = devlist; devlist = dev; num_devices++; return SANE_STATUS_GOOD; } /* init_options: * Sets up the option descriptors for a device * * Description: */ static void init_options(Mustek_pp_Handle *hndl) { int i; memset (hndl->opt, 0, sizeof (hndl->opt)); memset (hndl->val, 0, sizeof (hndl->val)); for (i = 0; i < NUM_OPTIONS; ++i) { hndl->opt[i].size = sizeof (SANE_Word); hndl->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } hndl->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS; hndl->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; hndl->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; hndl->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; hndl->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; hndl->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* "Mode" group: */ hndl->opt[OPT_MODE_GROUP].title = "Scan Mode"; hndl->opt[OPT_MODE_GROUP].desc = ""; hndl->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; hndl->opt[OPT_MODE_GROUP].cap = 0; hndl->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; hndl->opt[OPT_MODE_GROUP].size = 0; /* scan mode */ hndl->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; hndl->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE; hndl->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE; hndl->opt[OPT_MODE].type = SANE_TYPE_STRING; hndl->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; hndl->opt[OPT_MODE].size = mustek_pp_modes_size; hndl->opt[OPT_MODE].constraint.string_list = mustek_pp_modes; hndl->val[OPT_MODE].s = strdup (mustek_pp_modes[2]); /* resolution */ hndl->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; hndl->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; hndl->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; hndl->opt[OPT_RESOLUTION].type = SANE_TYPE_FIXED; hndl->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; hndl->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; hndl->opt[OPT_RESOLUTION].constraint.range = &hndl->dpi_range; hndl->val[OPT_RESOLUTION].w = SANE_FIX (hndl->dev->minres); hndl->dpi_range.min = SANE_FIX (hndl->dev->minres); hndl->dpi_range.max = SANE_FIX (hndl->dev->maxres); hndl->dpi_range.quant = SANE_FIX (1); /* speed */ hndl->opt[OPT_SPEED].name = SANE_NAME_SCAN_SPEED; hndl->opt[OPT_SPEED].title = SANE_TITLE_SCAN_SPEED; hndl->opt[OPT_SPEED].desc = SANE_DESC_SCAN_SPEED; hndl->opt[OPT_SPEED].type = SANE_TYPE_STRING; hndl->opt[OPT_SPEED].size = mustek_pp_speeds_size; hndl->opt[OPT_SPEED].constraint_type = SANE_CONSTRAINT_STRING_LIST; hndl->opt[OPT_SPEED].constraint.string_list = mustek_pp_speeds; hndl->val[OPT_SPEED].s = strdup (mustek_pp_speeds[2]); if (! (hndl->dev->caps & CAP_SPEED_SELECT)) hndl->opt[OPT_SPEED].cap |= SANE_CAP_INACTIVE; /* preview */ hndl->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; hndl->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; hndl->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; hndl->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; hndl->val[OPT_PREVIEW].w = SANE_FALSE; /* gray preview */ hndl->opt[OPT_GRAY_PREVIEW].name = SANE_NAME_GRAY_PREVIEW; hndl->opt[OPT_GRAY_PREVIEW].title = SANE_TITLE_GRAY_PREVIEW; hndl->opt[OPT_GRAY_PREVIEW].desc = SANE_DESC_GRAY_PREVIEW; hndl->opt[OPT_GRAY_PREVIEW].type = SANE_TYPE_BOOL; hndl->val[OPT_GRAY_PREVIEW].w = SANE_FALSE; /* color dept */ hndl->opt[OPT_DEPTH].name = SANE_NAME_BIT_DEPTH; hndl->opt[OPT_DEPTH].title = SANE_TITLE_BIT_DEPTH; hndl->opt[OPT_DEPTH].desc = "Number of bits per sample for color scans, typical values are 8 for truecolor (24bpp)" "up to 16 for far-to-many-color (48bpp)."; hndl->opt[OPT_DEPTH].type = SANE_TYPE_INT; hndl->opt[OPT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST; hndl->opt[OPT_DEPTH].constraint.word_list = mustek_pp_depths; hndl->opt[OPT_DEPTH].unit = SANE_UNIT_BIT; hndl->opt[OPT_DEPTH].size = sizeof(SANE_Word); hndl->val[OPT_DEPTH].w = 8; if ( !(hndl->dev->caps & CAP_DEPTH)) hndl->opt[OPT_DEPTH].cap |= SANE_CAP_INACTIVE; /* "Geometry" group: */ hndl->opt[OPT_GEOMETRY_GROUP].title = "Geometry"; hndl->opt[OPT_GEOMETRY_GROUP].desc = ""; hndl->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; hndl->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; hndl->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; hndl->opt[OPT_GEOMETRY_GROUP].size = 0; /* top-left x */ hndl->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; hndl->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; hndl->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; hndl->opt[OPT_TL_X].type = SANE_TYPE_FIXED; hndl->opt[OPT_TL_X].unit = SANE_UNIT_MM; hndl->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; hndl->opt[OPT_TL_X].constraint.range = &hndl->x_range; hndl->x_range.min = SANE_FIX (0); hndl->x_range.max = SANE_FIX (PIXEL_TO_MM(hndl->dev->maxhsize,hndl->dev->maxres)); hndl->x_range.quant = 0; hndl->val[OPT_TL_X].w = hndl->x_range.min; /* top-left y */ hndl->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; hndl->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; hndl->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; hndl->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; hndl->opt[OPT_TL_Y].unit = SANE_UNIT_MM; hndl->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; hndl->opt[OPT_TL_Y].constraint.range = &hndl->y_range; hndl->y_range.min = SANE_FIX(0); hndl->y_range.max = SANE_FIX(PIXEL_TO_MM(hndl->dev->maxvsize,hndl->dev->maxres)); hndl->y_range.quant = 0; hndl->val[OPT_TL_Y].w = hndl->y_range.min; /* bottom-right x */ hndl->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; hndl->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; hndl->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; hndl->opt[OPT_BR_X].type = SANE_TYPE_FIXED; hndl->opt[OPT_BR_X].unit = SANE_UNIT_MM; hndl->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; hndl->opt[OPT_BR_X].constraint.range = &hndl->x_range; hndl->val[OPT_BR_X].w = hndl->x_range.max; /* bottom-right y */ hndl->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; hndl->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; hndl->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; hndl->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; hndl->opt[OPT_BR_Y].unit = SANE_UNIT_MM; hndl->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; hndl->opt[OPT_BR_Y].constraint.range = &hndl->y_range; hndl->val[OPT_BR_Y].w = hndl->y_range.max; /* "Enhancement" group: */ hndl->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement"; hndl->opt[OPT_ENHANCEMENT_GROUP].desc = ""; hndl->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; hndl->opt[OPT_ENHANCEMENT_GROUP].cap = 0; hndl->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; hndl->opt[OPT_ENHANCEMENT_GROUP].size = 0; /* custom-gamma table */ hndl->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA; hndl->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA; hndl->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA; hndl->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL; hndl->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE; if ( !(hndl->dev->caps & CAP_GAMMA_CORRECT)) hndl->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; /* grayscale gamma vector */ hndl->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR; hndl->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR; hndl->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR; hndl->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT; hndl->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; hndl->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE; hndl->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word); hndl->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE; hndl->opt[OPT_GAMMA_VECTOR].constraint.range = &hndl->gamma_range; hndl->val[OPT_GAMMA_VECTOR].wa = &hndl->gamma_table[0][0]; /* red gamma vector */ hndl->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; hndl->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R; hndl->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R; hndl->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT; hndl->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; hndl->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE; hndl->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word); hndl->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE; hndl->opt[OPT_GAMMA_VECTOR_R].constraint.range = &hndl->gamma_range; hndl->val[OPT_GAMMA_VECTOR_R].wa = &hndl->gamma_table[1][0]; /* green gamma vector */ hndl->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; hndl->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G; hndl->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G; hndl->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT; hndl->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; hndl->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE; hndl->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word); hndl->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE; hndl->opt[OPT_GAMMA_VECTOR_G].constraint.range = &hndl->gamma_range; hndl->val[OPT_GAMMA_VECTOR_G].wa = &hndl->gamma_table[2][0]; /* blue gamma vector */ hndl->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; hndl->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B; hndl->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B; hndl->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT; hndl->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; hndl->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE; hndl->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word); hndl->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE; hndl->opt[OPT_GAMMA_VECTOR_B].constraint.range = &hndl->gamma_range; hndl->val[OPT_GAMMA_VECTOR_B].wa = &hndl->gamma_table[3][0]; hndl->gamma_range.min = 0; hndl->gamma_range.max = 255; hndl->gamma_range.quant = 1; hndl->opt[OPT_INVERT].name = SANE_NAME_NEGATIVE; hndl->opt[OPT_INVERT].title = SANE_TITLE_NEGATIVE; hndl->opt[OPT_INVERT].desc = SANE_DESC_NEGATIVE; hndl->opt[OPT_INVERT].type = SANE_TYPE_BOOL; hndl->val[OPT_INVERT].w = SANE_FALSE; if (! (hndl->dev->caps & CAP_INVERT)) hndl->opt[OPT_INVERT].cap |= SANE_CAP_INACTIVE; } /* attach_device: * Attempts to attach a device to the list after parsing of a section * of the configuration file. * * Description: * After parsing a scanner section of the config file, this function * is called to look for a driver with a matching name. When found, * this driver is called to initialize the device. */ static void attach_device(SANE_String *driver, SANE_String *name, SANE_String *port, SANE_String *option_ta) { int found = 0, driver_no, port_no; const char **ports; if (!strcmp (*port, "*")) { ports = sanei_pa4s2_devices(); DBG (3, "sanei_init: auto probing port\n"); } else { ports = malloc (sizeof(char *) * 2); ports[0] = *port; ports[1] = NULL; } for (port_no=0; ports[port_no] != NULL; port_no++) { for (driver_no=0 ; driver_no [] * * where is a arbitrary name to identify this entry * is the port where the scanner is attached to * is the name of the driver to use * * if the optional argument "option_ta" is present the driver uses special * parameters fitting for a transparency adapter. */ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { FILE *fp; char config_line[1024]; const char *config_line_ptr; int line=0, driver_no; char *driver = 0, *port = 0, *name = 0, *option_ta = 0; DBG_INIT (); DBG (3, "sane-mustek_pp, version 0.%d-%s. build for SANE %s\n", MUSTEK_PP_BUILD, MUSTEK_PP_STATE, VERSION); DBG (3, "backend by Jochen Eisinger \n"); if (version_code != NULL) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, MUSTEK_PP_BUILD); sane_auth = authorize; fp = sanei_config_open (MUSTEK_PP_CONFIG_FILE); if (fp == NULL) { char driver_name[64]; const char **devices = sanei_pa4s2_devices(); int device_no; DBG (2, "sane_init: could not open configuration file\n"); for (device_no = 0; devices[device_no] != NULL; device_no++) { DBG (3, "sane_init: trying ``%s''\n", devices[device_no]); for (driver_no=0 ; driver_no [] Note that the value is optional. */ char *optname, *optval = 0; Mustek_pp_config_option *tmpoptions; config_line_ptr += 6; config_line_ptr = sanei_config_skip_whitespace (config_line_ptr); if (!*config_line_ptr) { DBG (1, "sane_init: parse error in line %d after ``option''\n", line); continue; } config_line_ptr = sanei_config_get_string (config_line_ptr, &optname); if ((optname == NULL) || (!*optname)) { DBG (1, "sane_init: parse error in line %d after ``option''\n", line); if (optname != NULL) free (optname); continue; } config_line_ptr = sanei_config_skip_whitespace (config_line_ptr); if (*config_line_ptr) { /* The option has a value. No need to check the value; that's up to the backend */ config_line_ptr = sanei_config_get_string (config_line_ptr, &optval); config_line_ptr = sanei_config_skip_whitespace (config_line_ptr); } if (*config_line_ptr) { DBG (1, "sane_init: parse error in line %d after " "``option %s %s''\n", line, optname, (optval == 0 ? "" : optval)); free (optname); if (optval) free (optval); continue; } if (!strcmp (optname, "no_epp")) { u_int pa4s2_options; if (name) DBG (2, "sane_init: global option found in local scope, " "executing anyway\n"); free (optname); if (optval) { DBG (1, "sane_init: unexpected value for option no_epp\n"); free (optval); continue; } DBG (3, "sane_init: disabling mode EPP\n"); sanei_pa4s2_options (&pa4s2_options, SANE_FALSE); pa4s2_options |= SANEI_PA4S2_OPT_NO_EPP; sanei_pa4s2_options (&pa4s2_options, SANE_TRUE); continue; } else if (!name) { DBG (1, "sane_init: parse error in line %d: unexpected " " ``option''\n", line); free (optname); if (optval) free (optval); continue; } /* Extend the (global) array of options */ tmpoptions = realloc(cfgoptions, (numcfgoptions+1)*sizeof(cfgoptions[0])); if (!tmpoptions) { DBG (1, "sane_init: not enough memory for device options\n"); free_cfg_options(&numcfgoptions, &cfgoptions); return SANE_STATUS_NO_MEM; } cfgoptions = tmpoptions; cfgoptions[numcfgoptions].name = optname; cfgoptions[numcfgoptions].value = optval; ++numcfgoptions; } else { DBG (1, "sane_init: parse error at beginning of line %d\n", line); continue; } } /* If we hit the end of the file, we still may have to process the last driver */ if (name) attach_device(&driver, &name, &port, &option_ta); fclose(fp); return SANE_STATUS_GOOD; } /* sane_exit: * Unloads all drivers and frees allocated memory * * Description: * All open devices are closed first. Then all registered devices * are removed. * */ void sane_exit (void) { Mustek_pp_Handle *hndl; Mustek_pp_Device *dev; if (first_hndl) DBG (3, "sane_exit: closing open devices\n"); while (first_hndl) { hndl = first_hndl; sane_close (hndl); } dev = devlist; num_devices = 0; devlist = NULL; while (dev) { free (dev->port); free (dev->name); free (dev->vendor); free (dev->model); free (dev->type); free_cfg_options (&dev->numcfgoptions, &dev->cfgoptions); dev = dev->next; } if (devarray != NULL) free (devarray); devarray = NULL; DBG (3, "sane_exit: all drivers unloaded\n"); } /* sane_get_devices: * Returns a list of registered devices * * Description: * A possible present old device_list is removed first. A new * devarray is allocated and filled with pointers to the * SANE_Device structures of the Mustek_pp_Devices */ /*ARGSUSED*/ SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only __UNUSED__) { int ctr; Mustek_pp_Device *dev; if (devarray != NULL) free (devarray); devarray = malloc ((num_devices + 1) * sizeof (devarray[0])); if (devarray == NULL) { DBG (1, "sane_get_devices: not enough memory for device list\n"); return SANE_STATUS_NO_MEM; } dev = devlist; for (ctr=0 ; ctrsane; dev = dev->next; } devarray[num_devices] = NULL; *device_list = (const SANE_Device **)devarray; return SANE_STATUS_GOOD; } /* sane_open: * opens a device and prepares it for operation * * Description: * The device identified by ``devicename'' is looked * up in the list, or if devicename is zero, the * first device from the list is taken. * * open is called for the selected device. * * The handle is set up with default values, and the * option descriptors are initialized */ SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { Mustek_pp_Handle *hndl; Mustek_pp_Device *dev; SANE_Status status; int fd, i; if (devicename[0]) { dev = devlist; while (dev) { if (strcmp (dev->name, devicename) == 0) break; dev = dev->next; } if (!dev) { DBG (1, "sane_open: unknown devicename ``%s''\n", devicename); return SANE_STATUS_INVAL; } } else dev = devlist; if (!dev) { DBG (1, "sane_open: no devices present...\n"); return SANE_STATUS_INVAL; } DBG (3, "sane_open: Using device ``%s'' (driver %s v%s by %s)\n", dev->name, dev->func->driver, dev->func->version, dev->func->author); if ((hndl = malloc (sizeof (Mustek_pp_Handle))) == NULL) { DBG (1, "sane_open: not enough free memory for the handle\n"); return SANE_STATUS_NO_MEM; } if ((status = dev->func->open (dev->port, dev->caps, &fd)) != SANE_STATUS_GOOD) { DBG (1, "sane_open: could not open device (%s)\n", sane_strstatus (status)); return status; } hndl->next = first_hndl; hndl->dev = dev; hndl->fd = fd; hndl->state = STATE_IDLE; hndl->pipe = -1; init_options (hndl); dev->func->setup (hndl); /* Initialize driver-specific configuration options. This must be done after calling the setup() function because only then the driver is guaranteed to be fully initialized */ for (i = 0; inumcfgoptions; ++i) { status = dev->func->config (hndl, dev->cfgoptions[i].name, dev->cfgoptions[i].value); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_open: could not set option %s for device (%s)\n", dev->cfgoptions[i].name, sane_strstatus (status)); /* Question: should the initialization be aborted when an option cannot be handled ? The driver should have reasonable built-in defaults, so an illegal option value or an unknown option should not be fatal. Therefore, it's probably ok to ignore the error. */ } } first_hndl = hndl; *handle = hndl; return SANE_STATUS_GOOD; } /* sane_close: * closes a given device and frees all resources * * Description: * The handle is searched in the list of active handles. * If it's found, the handle is removed. * * If the associated device is still scanning, the process * is cancelled. * * Then the backend makes sure, the lamp was at least * 2 seconds on. * * Afterwards the selected handle is closed */ void sane_close (SANE_Handle handle) { Mustek_pp_Handle *prev, *hndl; prev = NULL; for (hndl = first_hndl; hndl; hndl = hndl->next) { if (hndl == handle) break; prev = hndl; } if (hndl == NULL) { DBG (2, "sane_close: unknown device handle\n"); return; } if (hndl->state == STATE_SCANNING) { sane_cancel (handle); do_eof (handle); } if (prev != NULL) prev->next = hndl->next; else first_hndl = hndl->next; DBG (3, "sane_close: maybe waiting for lamp...\n"); if (hndl->lamp_on) while (time (NULL) - hndl->lamp_on < 2) sleep (1); hndl->dev->func->close (hndl); DBG (3, "sane_close: device closed\n"); free (handle); } /* sane_get_option_descriptor: * does what it says * * Description: * */ const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { Mustek_pp_Handle *hndl = handle; if ((unsigned) option >= NUM_OPTIONS) { DBG (2, "sane_get_option_descriptor: option %d doesn't exist\n", option); return NULL; } return hndl->opt + option; } /* sane_control_option: * Reads or writes an option * * Description: * If a pointer to info is given, the value is initialized to zero * while scanning options cannot be read or written. next a basic * check whether the request is valid is done. * * Depending on ``action'' the value of the option is either read * (in the first block) or written (in the second block). auto * values aren't supported. * * before a value is written, some checks are performed. Depending * on the option, that is written, other options also change * */ SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { Mustek_pp_Handle *hndl = handle; SANE_Status status; SANE_Word w, cap; if (info) *info = 0; if (hndl->state == STATE_SCANNING) { DBG (2, "sane_control_option: device is scanning\n"); return SANE_STATUS_DEVICE_BUSY; } if ((unsigned int) option >= NUM_OPTIONS) { DBG (2, "sane_control_option: option %d doesn't exist\n", option); return SANE_STATUS_INVAL; } cap = hndl->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) { DBG (2, "sane_control_option: option %d isn't active\n", option); return SANE_STATUS_INVAL; } if (action == SANE_ACTION_GET_VALUE) { switch (option) { /* word options: */ case OPT_PREVIEW: case OPT_GRAY_PREVIEW: case OPT_RESOLUTION: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_NUM_OPTS: case OPT_CUSTOM_GAMMA: case OPT_INVERT: case OPT_DEPTH: *(SANE_Word *) val = hndl->val[option].w; return SANE_STATUS_GOOD; /* word-array options: */ case OPT_GAMMA_VECTOR: case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: memcpy (val, hndl->val[option].wa, hndl->opt[option].size); return SANE_STATUS_GOOD; /* string options: */ case OPT_MODE: case OPT_SPEED: strcpy (val, hndl->val[option].s); return SANE_STATUS_GOOD; } } else if (action == SANE_ACTION_SET_VALUE) { if (!SANE_OPTION_IS_SETTABLE (cap)) { DBG (2, "sane_control_option: option can't be set (%s)\n", hndl->opt[option].name); return SANE_STATUS_INVAL; } status = sanei_constrain_value (hndl->opt + option, val, info); if (status != SANE_STATUS_GOOD) { DBG (2, "sane_control_option: constrain_value failed (%s)\n", sane_strstatus (status)); return status; } switch (option) { /* (mostly) side-effect-free word options: */ case OPT_RESOLUTION: case OPT_TL_X: case OPT_BR_X: case OPT_TL_Y: case OPT_BR_Y: case OPT_PREVIEW: case OPT_GRAY_PREVIEW: case OPT_INVERT: case OPT_DEPTH: if (info) *info |= SANE_INFO_RELOAD_PARAMS; hndl->val[option].w = *(SANE_Word *) val; return SANE_STATUS_GOOD; /* side-effect-free word-array options: */ case OPT_GAMMA_VECTOR: case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: memcpy (hndl->val[option].wa, val, hndl->opt[option].size); return SANE_STATUS_GOOD; /* side-effect-free string options: */ case OPT_SPEED: if (hndl->val[option].s) free (hndl->val[option].s); hndl->val[option].s = strdup (val); return SANE_STATUS_GOOD; /* options with side-effects: */ case OPT_CUSTOM_GAMMA: w = *(SANE_Word *) val; if (w == hndl->val[OPT_CUSTOM_GAMMA].w) return SANE_STATUS_GOOD; /* no change */ if (info) *info |= SANE_INFO_RELOAD_OPTIONS; hndl->val[OPT_CUSTOM_GAMMA].w = w; if (w == SANE_TRUE) { const char *mode = hndl->val[OPT_MODE].s; if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) hndl->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; else if (strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) == 0) { hndl->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; hndl->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; hndl->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; hndl->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; } } else { hndl->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; hndl->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; hndl->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; hndl->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; } return SANE_STATUS_GOOD; case OPT_MODE: { char *old_val = hndl->val[option].s; if (old_val) { if (strcmp (old_val, val) == 0) return SANE_STATUS_GOOD; /* no change */ free (old_val); } if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; hndl->val[option].s = strdup (val); hndl->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; hndl->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; hndl->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; hndl->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; hndl->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; hndl->opt[OPT_DEPTH].cap |= SANE_CAP_INACTIVE; if ((hndl->dev->caps & CAP_DEPTH) && (strcmp(val, SANE_VALUE_SCAN_MODE_COLOR) == 0)) hndl->opt[OPT_DEPTH].cap &= ~SANE_CAP_INACTIVE; if (!(hndl->dev->caps & CAP_GAMMA_CORRECT)) return SANE_STATUS_GOOD; if (strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) != 0) hndl->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE; if (hndl->val[OPT_CUSTOM_GAMMA].w == SANE_TRUE) { if (strcmp (val, SANE_VALUE_SCAN_MODE_GRAY) == 0) hndl->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; else if (strcmp (val, SANE_VALUE_SCAN_MODE_COLOR) == 0) { hndl->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; hndl->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; hndl->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; hndl->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; } } return SANE_STATUS_GOOD; } } } DBG (2, "sane_control_option: unknown action\n"); return SANE_STATUS_INVAL; } /* sane_get_parameters: * returns the set of parameters, that is used for the next scan * * Description: * * First of all it is impossible to change the parameter set * while scanning. * * sane_get_parameters not only returns the parameters for * the next scan, it also sets them, i.e. converts the * options in actually parameters. * * The following parameters are set: * * scanmode: according to the option SCANMODE, but * 24bit color, if PREVIEW is selected and * grayscale if GRAY_PREVIEW is selected * depth: the bit depth for color modes (if * supported) or 24bit by default * (ignored in bw/grayscale or if not * supported) * dpi: resolution * invert: if supported else defaults to false * gamma: if supported and selected * ta: if supported by the device * speed: selected speed (or fastest if not * supported) * scanarea: the scanarea is calculated from the * selections the user has mode. note * that the area may slightly differ from * the scanarea selected due to rounding * note also, that a scanarea of * (0,0)-(100,100) will include all pixels * where 0 <= x < 100 and 0 <= y < 100 * afterwards, all values are copied into the SANE_Parameters * structure. */ SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { Mustek_pp_Handle *hndl = handle; char *mode; int dpi, ctr; if (hndl->state != STATE_SCANNING) { memset (&hndl->params, 0, sizeof (hndl->params)); if ((hndl->dev->caps & CAP_DEPTH) && (hndl->mode == MODE_COLOR)) hndl->depth = hndl->val[OPT_DEPTH].w; else hndl->depth = 8; dpi = (int) (SANE_UNFIX (hndl->val[OPT_RESOLUTION].w) + 0.5); hndl->res = dpi; if (hndl->dev->caps & CAP_INVERT) hndl->invert = hndl->val[OPT_INVERT].w; else hndl->invert = SANE_FALSE; if (hndl->dev->caps & CAP_TA) hndl->use_ta = SANE_TRUE; else hndl->use_ta = SANE_FALSE; if ((hndl->dev->caps & CAP_GAMMA_CORRECT) && (hndl->val[OPT_CUSTOM_GAMMA].w == SANE_TRUE)) hndl->do_gamma = SANE_TRUE; else hndl->do_gamma = SANE_FALSE; if (hndl->dev->caps & CAP_SPEED_SELECT) { for (ctr=SPEED_SLOWEST; ctr<=SPEED_FASTEST; ctr++) if (strcmp(mustek_pp_speeds[ctr], hndl->val[OPT_SPEED].s) == 0) hndl->speed = ctr; } else hndl->speed = SPEED_NORMAL; mode = hndl->val[OPT_MODE].s; if (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0) hndl->mode = MODE_BW; else if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) hndl->mode = MODE_GRAYSCALE; else hndl->mode = MODE_COLOR; if (hndl->val[OPT_PREVIEW].w == SANE_TRUE) { hndl->speed = SPEED_FASTEST; hndl->depth = 8; if (! hndl->use_ta) hndl->invert = SANE_FALSE; hndl->do_gamma = SANE_FALSE; if (hndl->val[OPT_GRAY_PREVIEW].w == SANE_TRUE) hndl->mode = MODE_GRAYSCALE; else { hndl->mode = MODE_COLOR; } } hndl->topX = MIN ((int) (MM_TO_PIXEL (SANE_UNFIX(hndl->val[OPT_TL_X].w), hndl->dev->maxres) + 0.5), hndl->dev->maxhsize); hndl->topY = MIN ((int) (MM_TO_PIXEL (SANE_UNFIX(hndl->val[OPT_TL_Y].w), hndl->dev->maxres) + 0.5), hndl->dev->maxvsize); hndl->bottomX = MIN ((int) (MM_TO_PIXEL (SANE_UNFIX(hndl->val[OPT_BR_X].w), hndl->dev->maxres) + 0.5), hndl->dev->maxhsize); hndl->bottomY = MIN ((int) (MM_TO_PIXEL (SANE_UNFIX(hndl->val[OPT_BR_Y].w), hndl->dev->maxres) + 0.5), hndl->dev->maxvsize); /* If necessary, swap the upper and lower boundaries to avoid negative distances. */ if (hndl->topX > hndl->bottomX) { SANE_Int tmp = hndl->topX; hndl->topX = hndl->bottomX; hndl->bottomX = tmp; } if (hndl->topY > hndl->bottomY) { SANE_Int tmp = hndl->topY; hndl->topY = hndl->bottomY; hndl->bottomY = tmp; } hndl->params.pixels_per_line = (hndl->bottomX - hndl->topX) * hndl->res / hndl->dev->maxres; hndl->params.bytes_per_line = hndl->params.pixels_per_line; switch (hndl->mode) { case MODE_BW: hndl->params.bytes_per_line /= 8; if ((hndl->params.pixels_per_line % 8) != 0) hndl->params.bytes_per_line++; hndl->params.depth = 1; break; case MODE_GRAYSCALE: hndl->params.depth = 8; hndl->params.format = SANE_FRAME_GRAY; break; case MODE_COLOR: hndl->params.depth = hndl->depth; hndl->params.bytes_per_line *= 3; if (hndl->depth > 8) hndl->params.bytes_per_line *= 2; hndl->params.format = SANE_FRAME_RGB; break; } hndl->params.last_frame = SANE_TRUE; hndl->params.lines = (hndl->bottomY - hndl->topY) * hndl->res / hndl->dev->maxres; } else DBG (2, "sane_get_parameters: can't set parameters while scanning\n"); if (params != NULL) *params = hndl->params; return SANE_STATUS_GOOD; } /* sane_start: * starts the scan. data acquisition will start immediately * * Description: * */ SANE_Status sane_start (SANE_Handle handle) { Mustek_pp_Handle *hndl = handle; int pipeline[2]; if (hndl->state == STATE_SCANNING) { DBG (2, "sane_start: device is already scanning\n"); return SANE_STATUS_DEVICE_BUSY; } sane_get_parameters (hndl, NULL); if (pipe(pipeline) < 0) { DBG (1, "sane_start: could not initialize pipe (%s)\n", strerror(errno)); return SANE_STATUS_IO_ERROR; } hndl->reader = fork(); if (hndl->reader == 0) { sigset_t ignore_set; struct SIGACTION act; close (pipeline[0]); sigfillset (&ignore_set); sigdelset (&ignore_set, SIGTERM); sigprocmask (SIG_SETMASK, &ignore_set, NULL); memset (&act, 0, sizeof(act)); sigaction (SIGTERM, &act, NULL); _exit (reader_process (hndl, pipeline[1])); } close (pipeline[1]); hndl->pipe = pipeline[0]; hndl->state = STATE_SCANNING; return SANE_STATUS_GOOD; } /* sane_read: * receives data from pipeline and passes it to the caller * * Description: * ditto */ SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { Mustek_pp_Handle *hndl = handle; SANE_Int nread; if (hndl->state == STATE_CANCELLED) { DBG (2, "sane_read: device already cancelled\n"); do_eof (hndl); hndl->state = STATE_IDLE; return SANE_STATUS_CANCELLED; } if (hndl->state != STATE_SCANNING) { DBG (1, "sane_read: device isn't scanning\n"); return SANE_STATUS_INVAL; } *len = nread = 0; while (*len < max_len) { nread = read(hndl->pipe, buf + *len, max_len - *len); if (hndl->state == STATE_CANCELLED) { *len = 0; DBG(3, "sane_read: scan was cancelled\n"); do_eof (hndl); hndl->state = STATE_IDLE; return SANE_STATUS_CANCELLED; } if (nread < 0) { if (errno == EAGAIN) { if (*len == 0) DBG(3, "sane_read: no data at the moment\n"); else DBG(3, "sane_read: %d bytes read\n", *len); return SANE_STATUS_GOOD; } else { DBG(1, "sane_read: IO error (%s)\n", strerror(errno)); hndl->state = STATE_IDLE; do_stop(hndl); do_eof (hndl); *len = 0; return SANE_STATUS_IO_ERROR; } } *len += nread; if (nread == 0) { if (*len == 0) { DBG (3, "sane_read: read finished\n"); do_stop(hndl); hndl->state = STATE_IDLE; return do_eof(hndl); } DBG(3, "sane_read: read last buffer of %d bytes\n", *len); return SANE_STATUS_GOOD; } } DBG(3, "sane_read: read full buffer of %d bytes\n", *len); return SANE_STATUS_GOOD; } /* sane_cancel: * stops a scan and ends the reader process * * Description: * */ void sane_cancel (SANE_Handle handle) { Mustek_pp_Handle *hndl = handle; if (hndl->state != STATE_SCANNING) return; hndl->state = STATE_CANCELLED; do_stop (hndl); } /* sane_set_io_mode: * toggles between blocking and non-blocking reading * * Description: * */ SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { Mustek_pp_Handle *hndl=handle; if (hndl->state != STATE_SCANNING) return SANE_STATUS_INVAL; if (fcntl (hndl->pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0) { DBG(1, "sane_set_io_mode: can't set io mode\n"); return SANE_STATUS_IO_ERROR; } return SANE_STATUS_GOOD; } /* sane_get_select_fd: * returns the pipeline fd for direct reading * * Description: * to allow the frontend to receive the data directly it * can read from the pipeline itself */ SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { Mustek_pp_Handle *hndl=handle; if (hndl->state != STATE_SCANNING) return SANE_STATUS_INVAL; *fd = hndl->pipe; return SANE_STATUS_GOOD; } /* include drivers */ #include "mustek_pp_decl.h" #include "mustek_pp_null.c" #include "mustek_pp_cis.h" #include "mustek_pp_cis.c" #include "mustek_pp_ccd300.h" #include "mustek_pp_ccd300.c" backends-1.3.0/backend/mustek_pp.conf.in000066400000000000000000000073311456256263500201610ustar00rootroot00000000000000# 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 backends-1.3.0/backend/mustek_pp.h000066400000000000000000000155341456256263500170620ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2000-2003 Jochen Eisinger This file is part of the SANE package. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . As a special exception, the authors of SANE give permission for additional uses of the libraries contained in this release of SANE. The exception is that, if you link a SANE library with other files to produce an executable, this does not by itself cause the resulting executable to be covered by the GNU General Public License. Your use of that executable is in no way restricted on account of linking the SANE library code into it. This exception does not, however, invalidate any other reasons why the executable file might be covered by the GNU General Public License. If you submit changes to SANE to the maintainers to be included in a subsequent release, you agree by submitting the changes that those changes may be distributed with this exception intact. If you write modifications of your own for SANE, it is your choice whether to permit this exception to apply to your modifications. If you do not wish that, delete this exception notice. */ #ifndef mustek_pp_h #define mustek_pp_h #if defined(HAVE_SYS_TYPES_H) # include #endif #if defined(HAVE_SYS_TIME_H) # include #endif #define DEBUG_NOT_STATIC #include "../include/sane/sanei_debug.h" /* Please note: ASSERT won't go away if you define NDEBUG, it just won't * output a message when ASSERT fails. So if "cond" does anything, it will * be executed, even if NDEBUG is defined... */ #define ASSERT(cond, retval) do { \ if (!(cond)) { \ DBG(2, "assertion %s failed\n", \ STRINGIFY(cond)); \ if (retval >= 0) \ return retval; \ else \ return; \ } \ } /* This macro uses a otherwise unused argument */ #if defined(__GNUC__) # define __UNUSED__ __attribute__ ((unused)) #else # define __UNUSED__ #endif /* the function init uses this callback to register a device to the backend */ typedef SANE_Status (*SANE_Attach_Callback) (SANE_String_Const port, SANE_String_Const name, SANE_Int driver, SANE_Int info); typedef struct { const char *driver; const char *author; const char *version; /* this function detects the presence of a scanner at the * given location */ SANE_Status (*init)(SANE_Int options, SANE_String_Const port, SANE_String_Const name, SANE_Attach_Callback attach); /* this function returns the information needed to set up * the device entry. the info parameter is passed from * init to the attach_callback to this function, to * help to identify the device, before it is registered */ void (*capabilities)(SANE_Int info, SANE_String *model, SANE_String *vendor, SANE_String *type, SANE_Int *maxres, SANE_Int *minres, SANE_Int *maxhsize, SANE_Int *maxvsize, SANE_Int *caps); /* tries to open the given device. returns a fd on success */ SANE_Status (*open)(SANE_String port, SANE_Int caps, SANE_Int *fd); /* start scanning session */ void (*setup)(SANE_Handle hndl); /* processes a configuration option */ SANE_Status (*config)(SANE_Handle hndl, SANE_String_Const optname, SANE_String_Const optval); /* stop scanning session */ void (*close)(SANE_Handle hndl); /* start actual scan */ SANE_Status (*start)(SANE_Handle hndl); /* read data (one line) */ void (*read)(SANE_Handle hndl, SANE_Byte *buffer); /* stop scanner and return scanhead home */ void (*stop)(SANE_Handle hndl); } Mustek_pp_Functions; /* Drivers */ #define MUSTEK_PP_NUM_DRIVERS ((int)(sizeof(Mustek_pp_Drivers) / \ sizeof(Mustek_pp_Functions))) #define CAP_NOTHING 0 #define CAP_GAMMA_CORRECT 1 #define CAP_INVERT 2 #define CAP_SPEED_SELECT 4 #define CAP_LAMP_OFF 8 #define CAP_TA 16 #define CAP_DEPTH 32 /* Structure for holding name/value options from the configuration file */ typedef struct Mustek_pp_config_option { SANE_String name; SANE_String value; } Mustek_pp_config_option; typedef struct Mustek_pp_Device { struct Mustek_pp_Device *next; SANE_Device sane; /* non-const copy of SANE_Device */ SANE_String name, vendor, model, type; /* port */ SANE_String port; /* part describing hardware capabilities */ int minres; int maxres; int maxhsize; int maxvsize; int caps; /* functions */ Mustek_pp_Functions *func; /* Modified by EDG: device identification is needed to initialize private device descriptor */ SANE_Int info; /* Array of configuration file options */ int numcfgoptions; Mustek_pp_config_option *cfgoptions; } Mustek_pp_Device; #define STATE_IDLE 0 #define STATE_CANCELLED 1 #define STATE_SCANNING 2 #define MODE_BW 0 #define MODE_GRAYSCALE 1 #define MODE_COLOR 2 #define SPEED_SLOWEST 0 #define SPEED_SLOWER 1 #define SPEED_NORMAL 2 #define SPEED_FASTER 3 #define SPEED_FASTEST 4 enum Mustek_pp_Option { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_MODE, OPT_DEPTH, OPT_RESOLUTION, OPT_PREVIEW, OPT_GRAY_PREVIEW, OPT_SPEED, OPT_GEOMETRY_GROUP, OPT_TL_X, /* top-left x */ OPT_TL_Y, /* top-left y */ OPT_BR_X, /* bottom-right x */ OPT_BR_Y, /* bottom-right y */ OPT_ENHANCEMENT_GROUP, OPT_INVERT, OPT_CUSTOM_GAMMA, /* use custom gamma tables? */ /* The gamma vectors MUST appear in the order gray, red, green, blue. */ OPT_GAMMA_VECTOR, OPT_GAMMA_VECTOR_R, OPT_GAMMA_VECTOR_G, OPT_GAMMA_VECTOR_B, /* must come last: */ NUM_OPTIONS }; typedef struct Mustek_pp_Handle { struct Mustek_pp_Handle *next; Mustek_pp_Device *dev; int fd; int reader; int pipe; int state; int topX, topY; int bottomX, bottomY; int mode; int res; /* gamma table, etc... */ SANE_Int gamma_table[4][256]; int do_gamma; int invert; int use_ta; int depth; int speed; /* current parameters */ SANE_Parameters params; SANE_Range dpi_range; SANE_Range x_range; SANE_Range y_range; SANE_Range gamma_range; /* options */ SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; time_t lamp_on; void *priv; } Mustek_pp_Handle; #endif /* mustek_pp_h */ backends-1.3.0/backend/mustek_pp_ccd300.c000066400000000000000000001261471456256263500201140ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2000-2003 Jochen Eisinger This file is part of the SANE package. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . 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 the hardware driver scanners using a 300dpi CCD */ #include "mustek_pp_ccd300.h" #define MUSTEK_PP_CCD300 4 static void config_ccd_101x (Mustek_pp_Handle * dev); static void config_ccd (Mustek_pp_Handle * dev); static void lamp (Mustek_pp_Handle * dev, int lamp_on); #define CCD300_ASIC1013 0xa8 #define CCD300_ASIC1015 0xa5 #define CCD300_CHANNEL_RED 0 #define CCD300_CHANNEL_GREEN 1 #define CCD300_CHANNEL_BLUE 2 #define CCD300_CHANNEL_GRAY 1 #define CCD300_MAXHSIZE 2600 #define CCD300_MAXVSIZE 3500 /* * Here starts the driver code for the different chipsets * * The 1013 & 1015 chipsets share large portions of the code. This * shared functions end with _101x. */ static const u_char chan_codes_1013[] = { 0x82, 0x42, 0xC2 }; static const u_char chan_codes_1015[] = { 0x80, 0x40, 0xC0 }; static const u_char fullstep[] = { 0x09, 0x0C, 0x06, 0x03 }; static const u_char halfstep[] = { 0x02, 0x03, 0x01, 0x09, 0x08, 0x0C, 0x04, 0x06 }; static const u_char voltages[4][3] = { {0x5C, 0x5A, 0x63}, {0xE6, 0xB4, 0xBE}, {0xB4, 0xB4, 0xB4}, {0x64, 0x50, 0x64} }; /* Forward declarations of 1013/1015 functions */ static void set_ccd_channel_1013 (Mustek_pp_Handle * dev, int channel); static void motor_backward_1013 (Mustek_pp_Handle * dev); static void return_home_1013 (Mustek_pp_Handle * dev); static void motor_forward_1013 (Mustek_pp_Handle * dev); static void config_ccd_1013 (Mustek_pp_Handle * dev); static void set_ccd_channel_1015 (Mustek_pp_Handle * dev, int channel); /* static void motor_backward_1015 (Mustek_pp_Handle * dev); */ static void return_home_1015 (Mustek_pp_Handle * dev, SANE_Bool nowait); static void motor_forward_1015 (Mustek_pp_Handle * dev); static void config_ccd_1015 (Mustek_pp_Handle * dev); /* These functions are common to all 1013/1015 chipsets */ static void set_led (Mustek_pp_Handle * dev) { mustek_pp_ccd300_priv *priv = dev->priv; sanei_pa4s2_writebyte (dev->fd, 6, (priv->motor_step % 5 == 0 ? 0x03 : 0x13)); } static void set_sti (Mustek_pp_Handle * dev) { mustek_pp_ccd300_priv *priv = dev->priv; sanei_pa4s2_writebyte (dev->fd, 3, 0); priv->bank_count++; priv->bank_count &= 7; } static void get_bank_count (Mustek_pp_Handle * dev) { u_char val; mustek_pp_ccd300_priv *priv = dev->priv; sanei_pa4s2_readbegin (dev->fd, 3); sanei_pa4s2_readbyte (dev->fd, &val); sanei_pa4s2_readend (dev->fd); priv->bank_count = (val & 0x07); } static void reset_bank_count (Mustek_pp_Handle * dev) { sanei_pa4s2_writebyte (dev->fd, 6, 7); } static void wait_bank_change (Mustek_pp_Handle * dev, int bankcount, int niceload) { struct timeval start, end; unsigned long diff; mustek_pp_ccd300_priv *priv = dev->priv; int first_time = 1; gettimeofday (&start, NULL); do { if ((niceload == 0) && (first_time == 0)) { usleep (1); /* could be as well sched_yield */ first_time = 0; } get_bank_count (dev); gettimeofday (&end, NULL); diff = (end.tv_sec * 1000 + end.tv_usec / 1000) - (start.tv_sec * 1000 + start.tv_usec / 1000); } while ((priv->bank_count != bankcount) && (diff < priv->wait_bank)); } static void set_dpi_value (Mustek_pp_Handle * dev) { u_char val = 0; mustek_pp_ccd300_priv *priv = dev->priv; sanei_pa4s2_writebyte (dev->fd, 6, 0x80); switch (priv->hwres) { case 100: val = 0x00; break; case 200: val = 0x10; break; case 300: val = 0x20; break; } if (priv->ccd_type == 1) val |= 0x01; sanei_pa4s2_writebyte (dev->fd, 5, val); sanei_pa4s2_writebyte (dev->fd, 6, 0x00); DBG (5, "set_dpi_value: value 0x%02x\n", val); } static void set_line_adjust (Mustek_pp_Handle * dev) { int adjustline; mustek_pp_ccd300_priv *priv = dev->priv; adjustline = (dev->bottomX - dev->topX) * priv->hwres / 300; priv->adjustskip = priv->adjustskip * priv->hwres / 300; DBG (5, "set_line_adjust: ppl %u (%u), adjust %u, skip %u\n", dev->params.pixels_per_line, (dev->bottomX - dev->topX), adjustline, priv->adjustskip); sanei_pa4s2_writebyte (dev->fd, 6, 0x11); sanei_pa4s2_writebyte (dev->fd, 5, (adjustline + priv->adjustskip) >> 8); sanei_pa4s2_writebyte (dev->fd, 6, 0x21); sanei_pa4s2_writebyte (dev->fd, 5, (adjustline + priv->adjustskip) & 0xFF); sanei_pa4s2_writebyte (dev->fd, 6, 0x01); } static void set_lamp (Mustek_pp_Handle * dev, int lamp_on) { int ctr; mustek_pp_ccd300_priv *priv = dev->priv; sanei_pa4s2_writebyte (dev->fd, 6, 0xC3); for (ctr = 0; ctr < 3; ctr++) { sanei_pa4s2_writebyte (dev->fd, 6, (lamp_on ? 0x47 : 0x57)); sanei_pa4s2_writebyte (dev->fd, 6, 0x77); } priv->motor_step = lamp_on; set_led (dev); } static void send_voltages (Mustek_pp_Handle * dev) { int voltage, sel = 8, ctr; mustek_pp_ccd300_priv *priv = dev->priv; switch (priv->ccd_type) { case 0: voltage = 0; break; case 1: voltage = 1; break; default: voltage = 2; break; } for (ctr = 0; ctr < 3; ctr++) { sel <<= 1; sanei_pa4s2_writebyte (dev->fd, 6, sel); sanei_pa4s2_writebyte (dev->fd, 5, voltages[voltage][ctr]); } sanei_pa4s2_writebyte (dev->fd, 6, 0x00); } static int compar (const void *a, const void *b) { return (signed int) (*(const SANE_Byte *) a) - (signed int) (*(const SANE_Byte *) b); } static void set_ccd_channel_101x (Mustek_pp_Handle * dev, int channel) { mustek_pp_ccd300_priv *priv = dev->priv; switch (priv->asic) { case CCD300_ASIC1013: set_ccd_channel_1013 (dev, channel); break; case CCD300_ASIC1015: set_ccd_channel_1015 (dev, channel); break; } } static void motor_forward_101x (Mustek_pp_Handle * dev) { mustek_pp_ccd300_priv *priv = dev->priv; switch (priv->asic) { case CCD300_ASIC1013: motor_forward_1013 (dev); break; case CCD300_ASIC1015: motor_forward_1015 (dev); break; } } static void motor_backward_101x (Mustek_pp_Handle * dev) { mustek_pp_ccd300_priv *priv = dev->priv; switch (priv->asic) { case CCD300_ASIC1013: motor_backward_1013 (dev); break; case CCD300_ASIC1015: /* motor_backward_1015 (dev); */ break; } } static void move_motor_101x (Mustek_pp_Handle * dev, int forward) { mustek_pp_ccd300_priv *priv = dev->priv; if (forward == SANE_TRUE) motor_forward_101x (dev); else motor_backward_101x (dev); wait_bank_change (dev, priv->bank_count, 1); reset_bank_count (dev); } static void config_ccd_101x (Mustek_pp_Handle * dev) { mustek_pp_ccd300_priv *priv = dev->priv; switch (priv->asic) { case CCD300_ASIC1013: config_ccd_1013 (dev); break; case CCD300_ASIC1015: config_ccd_1015 (dev); break; } } static void read_line_101x (Mustek_pp_Handle * dev, SANE_Byte * buf, SANE_Int pixel, SANE_Int RefBlack, SANE_Byte * calib, SANE_Int * gamma) { SANE_Byte *cal = calib; u_char color; mustek_pp_ccd300_priv *priv = dev->priv; int ctr, skips = priv->adjustskip + 1, cval; if (pixel <= 0) return; sanei_pa4s2_readbegin (dev->fd, 1); if (priv->hwres == dev->res) { while (skips--) sanei_pa4s2_readbyte (dev->fd, &color); for (ctr = 0; ctr < pixel; ctr++) { sanei_pa4s2_readbyte (dev->fd, &color); cval = color; if (cval < RefBlack) cval = 0; else cval -= RefBlack; if (cal) { if (cval >= cal[ctr]) cval = 0xFF; else { cval <<= 8; cval /= (int) cal[ctr]; } } if (gamma) cval = gamma[cval]; buf[ctr] = cval; } } else { int pos = 0, bpos = 0; while (skips--) sanei_pa4s2_readbyte (dev->fd, &color); ctr = 0; do { sanei_pa4s2_readbyte (dev->fd, &color); cval = color; if (ctr < (pos >> SANE_FIXED_SCALE_SHIFT)) { ctr++; continue; } ctr++; pos += priv->res_step; if (cval < RefBlack) cval = 0; else cval -= RefBlack; if (cal) { if (cval >= cal[bpos]) cval = 0xFF; else { cval <<= 8; cval /= (int) cal[bpos]; } } if (gamma) cval = gamma[cval]; buf[bpos++] = cval; } while (bpos < pixel); } sanei_pa4s2_readend (dev->fd); } static void read_average_line_101x (Mustek_pp_Handle * dev, SANE_Byte * buf, int pixel, int RefBlack) { SANE_Byte lbuf[4][CCD300_MAXHSIZE * 2]; int ctr, sum; mustek_pp_ccd300_priv *priv = dev->priv; for (ctr = 0; ctr < 4; ctr++) { wait_bank_change (dev, priv->bank_count, 1); read_line_101x (dev, lbuf[ctr], pixel, RefBlack, NULL, NULL); reset_bank_count (dev); if (ctr < 3) set_sti (dev); } for (ctr = 0; ctr < pixel; ctr++) { sum = lbuf[0][ctr] + lbuf[1][ctr] + lbuf[2][ctr] + lbuf[3][ctr]; buf[ctr] = (sum / 4); } } static void find_black_side_edge_101x (Mustek_pp_Handle * dev) { SANE_Byte buf[CCD300_MAXHSIZE * 2]; SANE_Byte blackposition[5]; int pos = 0, ctr, blackpos; mustek_pp_ccd300_priv *priv = dev->priv; for (ctr = 0; ctr < 20; ctr++) { motor_forward_101x (dev); wait_bank_change (dev, priv->bank_count, 1); read_line_101x (dev, buf, CCD300_MAXHSIZE, 0, NULL, NULL); reset_bank_count (dev); priv->ref_black = priv->ref_red = priv->ref_green = priv->ref_blue = buf[0]; blackpos = CCD300_MAXHSIZE / 4; while ((abs (buf[blackpos] - buf[0]) >= 15) && (blackpos > 0)) blackpos--; if (blackpos > 1) blackposition[pos++] = blackpos; if (pos == 5) break; } blackpos = 0; for (ctr = 0; ctr < pos; ctr++) if (blackposition[ctr] > blackpos) blackpos = blackposition[ctr]; if (blackpos < 0x66) blackpos = 0x6A; priv->blackpos = blackpos; priv->saved_skipcount = (blackpos + 12) & 0xFF; } static void min_color_levels_101x (Mustek_pp_Handle * dev) { SANE_Byte buf[CCD300_MAXHSIZE * 2]; int ctr, sum = 0; mustek_pp_ccd300_priv *priv = dev->priv; for (ctr = 0; ctr < 8; ctr++) { set_ccd_channel_101x (dev, CCD300_CHANNEL_RED); set_sti (dev); wait_bank_change (dev, priv->bank_count, 1); read_line_101x (dev, buf, CCD300_MAXHSIZE, 0, NULL, NULL); reset_bank_count (dev); sum += buf[3]; } priv->ref_red = sum / 8; sum = 0; for (ctr = 0; ctr < 8; ctr++) { set_ccd_channel_101x (dev, CCD300_CHANNEL_GREEN); set_sti (dev); wait_bank_change (dev, priv->bank_count, 1); read_line_101x (dev, buf, CCD300_MAXHSIZE, 0, NULL, NULL); reset_bank_count (dev); sum += buf[3]; } priv->ref_green = sum / 8; sum = 0; for (ctr = 0; ctr < 8; ctr++) { set_ccd_channel_101x (dev, CCD300_CHANNEL_BLUE); set_sti (dev); wait_bank_change (dev, priv->bank_count, 1); read_line_101x (dev, buf, CCD300_MAXHSIZE, 0, NULL, NULL); reset_bank_count (dev); sum += buf[3]; } priv->ref_blue = sum / 8; } static void max_color_levels_101x (Mustek_pp_Handle * dev) { int ctr, line, sum; SANE_Byte rbuf[32][CCD300_MAXHSIZE * 2]; SANE_Byte gbuf[32][CCD300_MAXHSIZE * 2]; SANE_Byte bbuf[32][CCD300_MAXHSIZE * 2]; mustek_pp_ccd300_priv *priv = dev->priv; SANE_Byte maxbuf[32]; for (ctr = 0; ctr < 32; ctr++) { if (dev->mode == MODE_COLOR) { set_ccd_channel_101x (dev, CCD300_CHANNEL_RED); motor_forward_101x (dev); read_average_line_101x (dev, rbuf[ctr], dev->params.pixels_per_line, priv->ref_red); set_ccd_channel_101x (dev, CCD300_CHANNEL_GREEN); set_sti (dev); read_average_line_101x (dev, gbuf[ctr], dev->params.pixels_per_line, priv->ref_green); set_ccd_channel_101x (dev, CCD300_CHANNEL_BLUE); set_sti (dev); read_average_line_101x (dev, bbuf[ctr], dev->params.pixels_per_line, priv->ref_blue); } else { priv->channel = CCD300_CHANNEL_GRAY; motor_forward_101x (dev); read_average_line_101x (dev, gbuf[ctr], dev->params.pixels_per_line, priv->ref_black); } } for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++) { for (line = 0; line < 32; line++) maxbuf[line] = gbuf[line][ctr]; qsort (maxbuf, 32, sizeof (maxbuf[0]), compar); sum = maxbuf[4] + maxbuf[5] + maxbuf[6] + maxbuf[7]; priv->calib_g[ctr] = sum / 4; } if (dev->mode == MODE_COLOR) { for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++) { for (line = 0; line < 32; line++) maxbuf[line] = rbuf[line][ctr]; qsort (maxbuf, 32, sizeof (maxbuf[0]), compar); sum = maxbuf[4] + maxbuf[5] + maxbuf[6] + maxbuf[7]; priv->calib_r[ctr] = sum / 4; } for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++) { for (line = 0; line < 32; line++) maxbuf[line] = bbuf[line][ctr]; qsort (maxbuf, 32, sizeof (maxbuf[0]), compar); sum = maxbuf[4] + maxbuf[5] + maxbuf[6] + maxbuf[7]; priv->calib_b[ctr] = sum / 4; } } } static void find_black_top_edge_101x (Mustek_pp_Handle * dev) { int lines = 0, ctr, pos; SANE_Byte buf[CCD300_MAXHSIZE * 2]; mustek_pp_ccd300_priv *priv = dev->priv; priv->channel = CCD300_CHANNEL_GRAY; do { motor_forward_101x (dev); wait_bank_change (dev, priv->bank_count, 1); read_line_101x (dev, buf, CCD300_MAXHSIZE, priv->ref_black, NULL, NULL); reset_bank_count (dev); pos = 0; for (ctr = priv->blackpos; ctr > priv->blackpos - 10; ctr--) if (buf[ctr] <= 15) pos++; } while ((pos >= 8) && (lines++ < 67)); } static void calibrate_device_101x (Mustek_pp_Handle * dev) { int saved_ppl = dev->params.pixels_per_line, ctr; mustek_pp_ccd300_priv *priv = dev->priv; priv->saved_mode = dev->mode; priv->saved_invert = dev->invert; priv->saved_skipcount = priv->skipcount; priv->saved_skipimagebyte = priv->skipimagebytes; priv->saved_adjustskip = priv->adjustskip; priv->saved_res = dev->res; priv->saved_hwres = priv->hwres; priv->saved_res_step = priv->res_step; priv->saved_line_step = priv->line_step; priv->saved_channel = priv->channel; dev->params.pixels_per_line = CCD300_MAXHSIZE; priv->hwres = dev->res = 300; dev->mode = MODE_GRAYSCALE; priv->skipcount = priv->skipimagebytes = 0; dev->invert = SANE_FALSE; priv->channel = CCD300_CHANNEL_GRAY; config_ccd_101x (dev); get_bank_count (dev); find_black_side_edge_101x (dev); for (ctr = 0; ctr < 4; ctr++) move_motor_101x (dev, SANE_TRUE); dev->mode = priv->saved_mode; dev->invert = priv->saved_invert; priv->skipcount = priv->saved_skipcount; priv->skipimagebytes = priv->saved_skipimagebyte; priv->adjustskip = priv->saved_adjustskip; dev->res = priv->saved_res; priv->hwres = priv->saved_hwres; priv->res_step = priv->saved_res_step; priv->line_step = priv->saved_line_step; priv->channel = priv->saved_channel; priv->hwres = dev->res = 300; priv->skipcount = priv->skipimagebytes = 0; dev->invert = SANE_FALSE; config_ccd_101x (dev); get_bank_count (dev); if ((dev->mode == MODE_COLOR) && (priv->ccd_type != 0)) min_color_levels_101x (dev); dev->mode = priv->saved_mode; dev->invert = priv->saved_invert; priv->skipcount = priv->saved_skipcount; priv->skipimagebytes = priv->saved_skipimagebyte; priv->adjustskip = priv->saved_adjustskip; dev->res = priv->saved_res; priv->hwres = priv->saved_hwres; priv->res_step = priv->saved_res_step; priv->line_step = priv->saved_line_step; priv->channel = priv->saved_channel; dev->params.pixels_per_line = saved_ppl; dev->invert = SANE_FALSE; config_ccd_101x (dev); get_bank_count (dev); max_color_levels_101x (dev); dev->params.pixels_per_line = CCD300_MAXHSIZE; dev->mode = MODE_GRAYSCALE; priv->hwres = dev->res = 300; priv->skipcount = priv->skipimagebytes = 0; dev->invert = SANE_FALSE; config_ccd_101x (dev); get_bank_count (dev); find_black_top_edge_101x (dev); dev->mode = priv->saved_mode; dev->invert = priv->saved_invert; priv->skipcount = priv->saved_skipcount; priv->skipimagebytes = priv->saved_skipimagebyte; priv->adjustskip = priv->saved_adjustskip; dev->res = priv->saved_res; priv->hwres = priv->saved_hwres; priv->res_step = priv->saved_res_step; priv->line_step = priv->saved_line_step; priv->channel = priv->saved_channel; dev->params.pixels_per_line = saved_ppl; config_ccd_101x (dev); get_bank_count (dev); } static void get_grayscale_line_101x (Mustek_pp_Handle * dev, SANE_Byte * buf) { int skips; mustek_pp_ccd300_priv *priv = dev->priv; priv->line_diff += SANE_FIX (300.0 / (float) dev->res); skips = (priv->line_diff >> SANE_FIXED_SCALE_SHIFT); while (--skips) { motor_forward_101x (dev); wait_bank_change (dev, priv->bank_count, 1); reset_bank_count (dev); } priv->line_diff &= 0xFFFF; motor_forward_101x (dev); wait_bank_change (dev, priv->bank_count, 1); read_line_101x (dev, buf, dev->params.pixels_per_line, priv->ref_black, priv->calib_g, NULL); reset_bank_count (dev); } static void get_lineart_line_101x (Mustek_pp_Handle * dev, SANE_Byte * buf) { int ctr; SANE_Byte gbuf[CCD300_MAXHSIZE * 2]; mustek_pp_ccd300_priv *priv = dev->priv; get_grayscale_line_101x (dev, gbuf); memset (buf, 0xFF, dev->params.bytes_per_line); for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++) buf[ctr >> 3] ^= ((gbuf[ctr] > priv->bw) ? (1 << (7 - ctr % 8)) : 0); } static void get_color_line_101x (Mustek_pp_Handle * dev, SANE_Byte * buf) { SANE_Byte *red, *blue, *src, *dest; int gotline = 0, ctr; int gored, goblue, gogreen; mustek_pp_ccd300_priv *priv = dev->priv; int step = priv->line_step; do { red = priv->red[priv->redline]; blue = priv->blue[priv->blueline]; priv->ccd_line++; if ((priv->rdiff >> SANE_FIXED_SCALE_SHIFT) == priv->ccd_line) { gored = 1; priv->rdiff += step; } else gored = 0; if ((priv->bdiff >> SANE_FIXED_SCALE_SHIFT) == priv->ccd_line) { goblue = 1; priv->bdiff += step; } else goblue = 0; if ((priv->gdiff >> SANE_FIXED_SCALE_SHIFT) == priv->ccd_line) { gogreen = 1; priv->gdiff += step; } else gogreen = 0; if (!gored && !goblue && !gogreen) { motor_forward_101x (dev); wait_bank_change (dev, priv->bank_count, 1); reset_bank_count (dev); if (priv->ccd_line >= (priv->line_step >> SANE_FIXED_SCALE_SHIFT)) priv->redline = (priv->redline + 1) % priv->green_offs; if (priv->ccd_line >= priv->blue_offs + (priv->line_step >> SANE_FIXED_SCALE_SHIFT)) priv->blueline = (priv->blueline + 1) % priv->blue_offs; continue; } if (gored) priv->channel = CCD300_CHANNEL_RED; else if (goblue) priv->channel = CCD300_CHANNEL_BLUE; else priv->channel = CCD300_CHANNEL_GREEN; motor_forward_101x (dev); wait_bank_change (dev, priv->bank_count, 1); if (priv->ccd_line >= priv->green_offs && gogreen) { src = red; dest = buf; for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++) { *dest = *src++; dest += 3; } } if (gored) { read_line_101x (dev, red, dev->params.pixels_per_line, priv->ref_red, priv->calib_r, NULL); reset_bank_count (dev); } priv->redline = (priv->redline + 1) % priv->green_offs; if (priv->ccd_line >= priv->green_offs && gogreen) { src = blue; dest = buf + 2; for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++) { *dest = *src++; dest += 3; } } if (goblue) { if (gored) { set_ccd_channel_101x (dev, CCD300_CHANNEL_BLUE); set_sti (dev); wait_bank_change (dev, priv->bank_count, 1); } read_line_101x (dev, blue, dev->params.pixels_per_line, priv->ref_blue, priv->calib_b, NULL); reset_bank_count (dev); } if (priv->ccd_line >= priv->blue_offs + (priv->line_step >> SANE_FIXED_SCALE_SHIFT)) priv->blueline = (priv->blueline + 1) % priv->blue_offs; if (gogreen) { if (gored || goblue) { set_ccd_channel_101x (dev, CCD300_CHANNEL_GREEN); set_sti (dev); wait_bank_change (dev, priv->bank_count, 1); } read_line_101x (dev, priv->green, dev->params.pixels_per_line, priv->ref_green, priv->calib_g, NULL); reset_bank_count (dev); src = priv->green; dest = buf + 1; for (ctr = 0; ctr < dev->params.pixels_per_line; ctr++) { *dest = *src++; dest += 3; } gotline = 1; } } while (!gotline); } /* these functions are for the 1013 chipset */ static void set_ccd_channel_1013 (Mustek_pp_Handle * dev, int channel) { mustek_pp_ccd300_priv *priv = dev->priv; priv->channel = channel; sanei_pa4s2_writebyte (dev->fd, 6, chan_codes_1013[channel]); } static void motor_backward_1013 (Mustek_pp_Handle * dev) { mustek_pp_ccd300_priv *priv = dev->priv; priv->motor_step++; set_led (dev); if (priv->motor_phase > 3) priv->motor_phase = 3; sanei_pa4s2_writebyte (dev->fd, 6, 0x62); sanei_pa4s2_writebyte (dev->fd, 5, fullstep[priv->motor_phase]); priv->motor_phase = (priv->motor_phase == 0 ? 3 : priv->motor_phase - 1); set_ccd_channel_1013 (dev, priv->channel); set_sti (dev); } static void return_home_1013 (Mustek_pp_Handle * dev) { u_char ishome; int ctr; mustek_pp_ccd300_priv *priv = dev->priv; /* 1013 can't return home all alone, nowait ignored */ for (ctr = 0; ctr < 4500; ctr++) { /* check_is_home_1013 */ sanei_pa4s2_readbegin (dev->fd, 2); sanei_pa4s2_readbyte (dev->fd, &ishome); sanei_pa4s2_readend (dev->fd); /* yes, it should be is_not_home */ if ((ishome & 1) == 0) break; motor_backward_1013 (dev); wait_bank_change (dev, priv->bank_count, 0); reset_bank_count (dev); } } static void motor_forward_1013 (Mustek_pp_Handle * dev) { int ctr; mustek_pp_ccd300_priv *priv = dev->priv; priv->motor_step++; set_led (dev); for (ctr = 0; ctr < 2; ctr++) { sanei_pa4s2_writebyte (dev->fd, 6, 0x62); sanei_pa4s2_writebyte (dev->fd, 5, halfstep[priv->motor_phase]); priv->motor_phase = (priv->motor_phase == 7 ? 0 : priv->motor_phase + 1); } set_ccd_channel_1013 (dev, priv->channel); set_sti (dev); } static void config_ccd_1013 (Mustek_pp_Handle * dev) { mustek_pp_ccd300_priv *priv = dev->priv; if (dev->res != 0) priv->res_step = SANE_FIX ((float) priv->hwres / (float) dev->res); set_dpi_value (dev); /* set_start_channel_1013 (dev); */ sanei_pa4s2_writebyte (dev->fd, 6, 0x05); switch (dev->mode) { case MODE_BW: case MODE_GRAYSCALE: priv->channel = CCD300_CHANNEL_GRAY; break; case MODE_COLOR: priv->channel = CCD300_CHANNEL_RED; break; } set_ccd_channel_1013 (dev, priv->channel); /* set_invert_1013 (dev); */ sanei_pa4s2_writebyte (dev->fd, 6, (dev->invert == SANE_TRUE ? 0x04 : 0x14)); sanei_pa4s2_writebyte (dev->fd, 6, 0x37); reset_bank_count (dev); sanei_pa4s2_writebyte (dev->fd, 6, 0x27); sanei_pa4s2_writebyte (dev->fd, 6, 0x67); sanei_pa4s2_writebyte (dev->fd, 6, 0x17); sanei_pa4s2_writebyte (dev->fd, 6, 0x77); /* set_initial_skip_1013 (dev); */ sanei_pa4s2_writebyte (dev->fd, 6, 0x41); priv->adjustskip = priv->skipcount + priv->skipimagebytes; DBG (5, "config_ccd_1013: adjustskip %u\n", priv->adjustskip); sanei_pa4s2_writebyte (dev->fd, 5, priv->adjustskip / 16 + 2); priv->adjustskip %= 16; sanei_pa4s2_writebyte (dev->fd, 6, 0x81); sanei_pa4s2_writebyte (dev->fd, 5, 0x70); sanei_pa4s2_writebyte (dev->fd, 6, 0x01); set_line_adjust (dev); get_bank_count (dev); } /* these functions are for the 1015 chipset */ static void motor_control_1015 (Mustek_pp_Handle * dev, u_char control) { u_char val; DBG (5, "motor_controll_1015: control code 0x%02x\n", (unsigned int) control); sanei_pa4s2_writebyte (dev->fd, 6, 0xF6); sanei_pa4s2_writebyte (dev->fd, 6, 0x22); sanei_pa4s2_writebyte (dev->fd, 5, control); sanei_pa4s2_writebyte (dev->fd, 6, 0x02); do { sanei_pa4s2_readbegin (dev->fd, 2); sanei_pa4s2_readbyte (dev->fd, &val); sanei_pa4s2_readend (dev->fd); } while ((val & 0x08) != 0); } static void return_home_1015 (Mustek_pp_Handle * dev, SANE_Bool nowait) { u_char ishome, control = 0xC3; motor_control_1015 (dev, control); do { /* check_is_home_1015 */ sanei_pa4s2_readbegin (dev->fd, 2); sanei_pa4s2_readbyte (dev->fd, &ishome); sanei_pa4s2_readend (dev->fd); if (nowait) break; usleep (1000); /* much nicer load */ } while ((ishome & 2) == 0); } static void motor_forward_1015 (Mustek_pp_Handle * dev) { u_char control = 0x1B; mustek_pp_ccd300_priv *priv = dev->priv; priv->motor_step++; set_led (dev); motor_control_1015 (dev, control); set_ccd_channel_1015 (dev, priv->channel); set_sti (dev); } /* static void motor_backward_1015 (Mustek_pp_Handle * dev) { u_char control = 0x43; mustek_pp_ccd300_priv *priv = dev->priv; priv->motor_step++; set_led (dev); switch (priv->ccd_type) { case 1: control = 0x1B; break; default: control = 0x43; break; } motor_control_1015 (dev, control); set_ccd_channel_1015 (dev, priv->channel); set_sti (dev); } */ static void set_ccd_channel_1015 (Mustek_pp_Handle * dev, int channel) { u_char chancode = chan_codes_1015[channel]; mustek_pp_ccd300_priv *priv = dev->priv; priv->channel = channel; priv->image_control &= 0x34; chancode |= priv->image_control; priv->image_control = chancode; sanei_pa4s2_writebyte (dev->fd, 6, chancode); } static void config_ccd_1015 (Mustek_pp_Handle * dev) { u_char val; mustek_pp_ccd300_priv *priv = dev->priv; if (dev->res != 0) priv->res_step = SANE_FIX ((float) priv->hwres / (float) dev->res); set_dpi_value (dev); priv->image_control = 4; /* set_start_channel_1015 (dev); */ switch (dev->mode) { case MODE_BW: case MODE_GRAYSCALE: priv->channel = CCD300_CHANNEL_GRAY; break; case MODE_COLOR: priv->channel = CCD300_CHANNEL_RED; break; } set_ccd_channel_1015 (dev, priv->channel); /* set_invert_1015 (dev); */ priv->image_control &= 0xE4; if (dev->invert == SANE_FALSE) priv->image_control |= 0x10; sanei_pa4s2_writebyte (dev->fd, 6, priv->image_control); sanei_pa4s2_writebyte (dev->fd, 6, 0x23); sanei_pa4s2_writebyte (dev->fd, 5, 0x00); sanei_pa4s2_writebyte (dev->fd, 6, 0x43); switch (priv->ccd_type) { case 1: val = 0x6B; break; case 4: val = 0x9F; break; default: val = 0x92; break; } sanei_pa4s2_writebyte (dev->fd, 5, val); sanei_pa4s2_writebyte (dev->fd, 6, 0x03); sanei_pa4s2_writebyte (dev->fd, 6, 0x37); reset_bank_count (dev); sanei_pa4s2_writebyte (dev->fd, 6, 0x27); sanei_pa4s2_writebyte (dev->fd, 6, 0x67); sanei_pa4s2_writebyte (dev->fd, 6, 0x17); sanei_pa4s2_writebyte (dev->fd, 6, 0x77); /* set_initial_skip_1015 (dev); */ sanei_pa4s2_writebyte (dev->fd, 6, 0x41); priv->adjustskip = priv->skipcount + priv->skipimagebytes; /* if (dev->CCD.mode == MODE_COLOR) dev->CCD.adjustskip <<= 3; */ sanei_pa4s2_writebyte (dev->fd, 5, priv->adjustskip / 32 + 1); priv->adjustskip %= 32; sanei_pa4s2_writebyte (dev->fd, 6, 0x81); /* expose time */ switch (priv->ccd_type) { case 1: val = 0xA8; break; case 0: val = 0x8A; break; default: val = 0xA8; break; } sanei_pa4s2_writebyte (dev->fd, 5, val); sanei_pa4s2_writebyte (dev->fd, 6, 0x01); set_line_adjust (dev); get_bank_count (dev); } /* these functions are interfaces only */ static void config_ccd (Mustek_pp_Handle * dev) { mustek_pp_ccd300_priv *priv = dev->priv; DBG (5, "config_ccd: %d dpi, mode %d, invert %d, size %d\n", priv->hwres, dev->mode, dev->invert, dev->params.pixels_per_line); switch (priv->asic) { case CCD300_ASIC1013: config_ccd_1013 (dev); break; case CCD300_ASIC1015: config_ccd_1015 (dev); break; } } static void return_home (Mustek_pp_Handle * dev, SANE_Bool nowait) { mustek_pp_ccd300_priv *priv = dev->priv; priv->saved_mode = dev->mode; priv->saved_invert = dev->invert; priv->saved_skipcount = priv->skipcount; priv->saved_skipimagebyte = priv->skipimagebytes; priv->saved_adjustskip = priv->adjustskip; priv->saved_res = dev->res; priv->saved_hwres = priv->hwres; priv->saved_res_step = priv->res_step; priv->saved_line_step = priv->line_step; priv->saved_channel = priv->channel; priv->hwres = dev->res = 100; dev->mode = MODE_GRAYSCALE; priv->skipcount = priv->skipimagebytes = 0; config_ccd (dev); switch (priv->asic) { case CCD300_ASIC1013: return_home_1013 (dev); break; case CCD300_ASIC1015: return_home_1015 (dev, nowait); break; } dev->mode = priv->saved_mode; dev->invert = priv->saved_invert; priv->skipcount = priv->saved_skipcount; priv->skipimagebytes = priv->saved_skipimagebyte; priv->adjustskip = priv->saved_adjustskip; dev->res = priv->saved_res; priv->hwres = priv->saved_hwres; priv->res_step = priv->saved_res_step; priv->line_step = priv->saved_line_step; priv->channel = priv->saved_channel; priv->motor_step = 0; config_ccd (dev); } static void lamp (Mustek_pp_Handle * dev, int lamp_on) { set_lamp (dev, lamp_on); } static void set_voltages (Mustek_pp_Handle * dev) { send_voltages (dev); } static void move_motor (Mustek_pp_Handle * dev, int count, int forward) { int ctr; DBG (5, "move_motor: %u steps (%s)\n", count, (forward == SANE_TRUE ? "forward" : "backward")); for (ctr = 0; ctr < count; ctr++) { move_motor_101x (dev, forward); } } static void calibrate (Mustek_pp_Handle * dev) { mustek_pp_ccd300_priv *priv = dev->priv; DBG (5, "calibrate entered (asic = 0x%02x)\n", priv->asic); calibrate_device_101x (dev); DBG (5, "calibrate: ref_black %d, blackpos %d\n", priv->ref_black, priv->blackpos); } static void get_lineart_line (Mustek_pp_Handle * dev, SANE_Byte * buf) { get_lineart_line_101x (dev, buf); } static void get_grayscale_line (Mustek_pp_Handle * dev, SANE_Byte * buf) { get_grayscale_line_101x (dev, buf); } static void get_color_line (Mustek_pp_Handle * dev, SANE_Byte * buf) { get_color_line_101x (dev, buf); } static SANE_Status ccd300_init (SANE_Int options, SANE_String_Const port, SANE_String_Const name, SANE_Attach_Callback attach) { SANE_Status status; unsigned char asic, ccd; int fd; if (options != CAP_NOTHING) { DBG (1, "ccd300_init: called with unknown options (%#02x)\n", options); return SANE_STATUS_INVAL; } /* try to attach to he supplied port */ status = sanei_pa4s2_open (port, &fd); if (status != SANE_STATUS_GOOD) { DBG (2, "ccd300_init: couldn't attach to port ``%s'' (%s)\n", port, sane_strstatus (status)); return status; } sanei_pa4s2_enable (fd, SANE_TRUE); sanei_pa4s2_readbegin (fd, 0); sanei_pa4s2_readbyte (fd, &asic); sanei_pa4s2_readend (fd); sanei_pa4s2_readbegin (fd, 2); sanei_pa4s2_readbyte (fd, &ccd); sanei_pa4s2_readend (fd); sanei_pa4s2_enable (fd, SANE_FALSE); sanei_pa4s2_close (fd); if (asic != CCD300_ASIC1013 && asic != CCD300_ASIC1015) { DBG (2, "ccd300_init: scanner not recognized (unknown ASIC id %#02x)\n", asic); return SANE_STATUS_INVAL; } ccd &= (asic == CCD300_ASIC1013 ? 0x04 : 0x05); DBG (3, "ccd_init: found scanner on port ``%s'' (ASIC id %#02x, CCD %d)\n", port, asic, ccd); return attach (port, name, MUSTEK_PP_CCD300, options); } static void ccd300_capabilities (SANE_Int info, SANE_String * model, SANE_String * vendor, SANE_String * type, SANE_Int * maxres, SANE_Int * minres, SANE_Int * maxhsize, SANE_Int * maxvsize, SANE_Int * caps) { *model = strdup ("600 III EP Plus"); *vendor = strdup ("Mustek"); *type = strdup ("flatbed (CCD 300 dpi)"); DBG (3, "ccd300_capabilities: 600 III EP Plus flatbed CCD (300 dpi) scanner\n"); *maxres = 300; *minres = 50; *maxhsize = CCD300_MAXHSIZE; *maxvsize = CCD300_MAXVSIZE; *caps = info | CAP_INVERT | CAP_LAMP_OFF; } static SANE_Status ccd300_open (SANE_String port, SANE_Int caps, SANE_Int * fd) { SANE_Status status; if (caps & ~(CAP_NOTHING | CAP_INVERT | CAP_LAMP_OFF)) { DBG (1, "ccd300_open: called with unknown capabilities (%#02x)\n", caps); return SANE_STATUS_INVAL; } DBG (3, "ccd300_open: called for port ``%s''\n", port); status = sanei_pa4s2_open (port, fd); if (status != SANE_STATUS_GOOD) DBG (2, "ccd300_open: open failed (%s)\n", sane_strstatus (status)); return status; } static void ccd300_setup (SANE_Handle handle) { Mustek_pp_Handle *dev = handle; mustek_pp_ccd300_priv *priv; unsigned char asic, ccd; DBG (3, "ccd300_setup: called for port ``%s''\n", dev->dev->port); if ((priv = malloc (sizeof (mustek_pp_ccd300_priv))) == NULL) { DBG (1, "ccd300_setup: not enough memory\n"); return; /* can you here the shit hitting the fan? */ } dev->priv = priv; memset (priv, 0, sizeof (mustek_pp_ccd300_priv)); priv->bw = 128; priv->wait_bank = 700; priv->top = 47; sanei_pa4s2_enable (dev->fd, SANE_TRUE); sanei_pa4s2_readbegin (dev->fd, 0); sanei_pa4s2_readbyte (dev->fd, &asic); sanei_pa4s2_readend (dev->fd); sanei_pa4s2_readbegin (dev->fd, 2); sanei_pa4s2_readbyte (dev->fd, &ccd); sanei_pa4s2_readend (dev->fd); ccd &= (asic == CCD300_ASIC1013 ? 0x04 : 0x05); priv->asic = asic; priv->ccd_type = ccd; return_home (dev, SANE_TRUE); lamp (dev, SANE_TRUE); sanei_pa4s2_enable (dev->fd, SANE_FALSE); dev->lamp_on = time (NULL); dev->res = priv->hwres = 300; dev->mode = MODE_COLOR; } static void ccd300_close (SANE_Handle handle) { Mustek_pp_Handle *dev = handle; mustek_pp_ccd300_priv *priv = dev->priv; DBG (3, "ccd300_close: called for port ``%s''\n", dev->dev->port); sanei_pa4s2_enable (dev->fd, SANE_TRUE); lamp (dev, SANE_FALSE); return_home (dev, SANE_FALSE); sanei_pa4s2_enable (dev->fd, SANE_FALSE); sanei_pa4s2_close (dev->fd); free (priv); DBG (3, "ccd300_close: device shut down and all buffers freed\n"); } static SANE_Status ccd300_config (SANE_Handle handle, SANE_String_Const optname, SANE_String_Const optval) { Mustek_pp_Handle *dev = handle; mustek_pp_ccd300_priv *priv = dev->priv; int value = -1; DBG (3, "ccd300_config: called for port ``%s'' (%s%s%s)\n", dev->dev->port, optname, (optval ? " = " : ""), (optval ? optval : "")); if (!strcmp (optname, "bw")) { if (!optval) { DBG (1, "ccd300_config: missing value for option ``bw''\n"); return SANE_STATUS_INVAL; } /* ok, ok, should be strtol... know what? send me a patch. */ value = atoi (optval); if ((value < 0) || (value > 255)) { DBG (1, "ccd300_config: value ``%s'' for option ``bw'' is out of range (0 <= bw <= 255)\n", optval); return SANE_STATUS_INVAL; } priv->bw = value; } else if (!strcmp (optname, "waitbank")) { if (!optval) { DBG (1, "ccd300_config: missing value for option ``waitbank''\n"); return SANE_STATUS_INVAL; } value = atoi (optval); if (value < 0) { DBG (1, "ccd300_config: value ``%s'' for option ``waitbank'' is out of range (>= 0)\n", optval); return SANE_STATUS_INVAL; } priv->wait_bank = value; } else if (!strcmp (optname, "top")) { if (!optval) { DBG (1, "ccd300_config: missing value for option ``top''\n"); return SANE_STATUS_INVAL; } value = atoi (optval); if (value < 0) { DBG (1, "ccd300_config: value ``%s'' for option ``top'' is out of range (>= 0)\n", optval); return SANE_STATUS_INVAL; } priv->top = value; } else { DBG (1, "ccd300_config: unknown option ``%s''", optname); return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } static void ccd300_stop (SANE_Handle handle) { Mustek_pp_Handle *dev = handle; mustek_pp_ccd300_priv *priv = dev->priv; int cnt; DBG (3, "ccd300_stop: stopping scan operating on port ``%s''\n", dev->dev->port); sanei_pa4s2_enable (dev->fd, SANE_TRUE); return_home (dev, SANE_TRUE); sanei_pa4s2_enable (dev->fd, SANE_FALSE); free (priv->calib_r); free (priv->calib_g); free (priv->calib_b); if (priv->red) { for (cnt = 0; cnt < priv->green_offs; cnt++) free (priv->red[cnt]); free (priv->red); } if (priv->blue) { for (cnt = 0; cnt < priv->blue_offs; cnt++) free (priv->blue[cnt]); free (priv->blue); } free (priv->green); priv->calib_r = priv->calib_g = priv->calib_b = NULL; priv->red = priv->blue = NULL; priv->green = NULL; } static SANE_Status ccd300_start (SANE_Handle handle) { Mustek_pp_Handle *dev = handle; mustek_pp_ccd300_priv *priv = dev->priv; DBG (3, "ccd300_start: called for port ``%s''\n", dev->dev->port); if (dev->res <= 100) priv->hwres = 100; else if (dev->res <= 200) priv->hwres = 200; else if (dev->res <= 300) priv->hwres = 300; DBG (4, "ccd300_start: setting hardware resolution to %d dpi\n", priv->hwres); priv->skipimagebytes = dev->topX; sanei_pa4s2_enable (dev->fd, SANE_TRUE); config_ccd (dev); set_voltages (dev); get_bank_count (dev); if (priv->bank_count != 0) { DBG (2, "ccd300_start: bank count is not zero...\n"); } return_home (dev, SANE_FALSE); priv->motor_step = 0; /* allocate memory for calibration */ if ((priv->calib_g = malloc (dev->params.pixels_per_line)) == NULL) { sanei_pa4s2_enable (dev->fd, SANE_FALSE); DBG (1, "ccd300_start: not enough memory\n"); return SANE_STATUS_NO_MEM; } if (dev->mode == MODE_COLOR) { priv->calib_r = malloc (dev->params.pixels_per_line); priv->calib_b = malloc (dev->params.pixels_per_line); if ((priv->calib_r == NULL) || (priv->calib_b == NULL)) { free (priv->calib_g); free (priv->calib_r); free (priv->calib_b); priv->calib_r = priv->calib_g = priv->calib_b = NULL; sanei_pa4s2_enable (dev->fd, SANE_FALSE); DBG (1, "ccd300_start: not enough memory\n"); return SANE_STATUS_NO_MEM; } } calibrate (dev); if (priv->ccd_type == 1) { priv->blue_offs = 4; priv->green_offs = 8; } else { priv->blue_offs = 8; priv->green_offs = 16; } move_motor (dev, priv->top + dev->topY - (dev->mode == MODE_COLOR ? priv->green_offs : 0), SANE_TRUE); if (priv->ccd_type == 1) sanei_pa4s2_writebyte (dev->fd, 6, 0x15); sanei_pa4s2_enable (dev->fd, SANE_FALSE); if (dev->mode == MODE_COLOR) { int failed = SANE_FALSE, cnt; priv->line_step = SANE_FIX (300.0 / (float) dev->res); priv->rdiff = priv->line_step; priv->bdiff = priv->rdiff + (priv->blue_offs << SANE_FIXED_SCALE_SHIFT); priv->gdiff = priv->rdiff + (priv->green_offs << SANE_FIXED_SCALE_SHIFT); priv->red = malloc (sizeof (SANE_Byte *) * priv->green_offs); priv->blue = malloc (sizeof (SANE_Byte *) * priv->blue_offs); priv->green = malloc (dev->params.pixels_per_line); if ((priv->red == NULL) || (priv->blue == NULL) || (priv->green == NULL)) { free (priv->calib_r); free (priv->calib_g); free (priv->calib_b); priv->calib_r = priv->calib_g = priv->calib_b = NULL; free (priv->red); free (priv->green); free (priv->blue); priv->red = priv->blue = NULL; priv->green = NULL; DBG (1, "ccd300_start: not enough memory for ld buffers\n"); return SANE_STATUS_NO_MEM; } /* note to myself: better allocate one huge chunk of memory and set pointers */ for (cnt = 0; cnt < priv->green_offs; cnt++) if ((priv->red[cnt] = malloc (dev->params.pixels_per_line)) == NULL) failed = SANE_TRUE; for (cnt = 0; cnt < priv->blue_offs; cnt++) if ((priv->blue[cnt] = malloc (dev->params.pixels_per_line)) == NULL) failed = SANE_TRUE; if (failed == SANE_TRUE) { free (priv->calib_r); free (priv->calib_g); free (priv->calib_b); priv->calib_r = priv->calib_g = priv->calib_b = NULL; for (cnt = 0; cnt < priv->green_offs; cnt++) free (priv->red[cnt]); for (cnt = 0; cnt < priv->blue_offs; cnt++) free (priv->blue[cnt]); free (priv->red); free (priv->green); free (priv->blue); priv->red = priv->blue = NULL; priv->green = NULL; DBG (1, "ccd300_start: not enough memory for ld buffers\n"); return SANE_STATUS_NO_MEM; } priv->redline = priv->blueline = priv->ccd_line = 0; } priv->lines = 0; priv->lines_left = dev->params.lines; DBG (3, "ccd300_start: device ready for scanning\n"); return SANE_STATUS_GOOD; } static void ccd300_read (SANE_Handle handle, SANE_Byte * buffer) { Mustek_pp_Handle *dev = handle; mustek_pp_ccd300_priv *priv = dev->priv; DBG (3, "ccd300_read: receiving one line from port ``%s''\n", dev->dev->port); sanei_pa4s2_enable (dev->fd, SANE_TRUE); switch (dev->mode) { case MODE_BW: get_lineart_line (dev, buffer); break; case MODE_GRAYSCALE: get_grayscale_line (dev, buffer); break; case MODE_COLOR: get_color_line (dev, buffer); break; } priv->lines_left--; priv->lines++; DBG (4, "ccd300_read: %d lines read (%d to go)\n", priv->lines, priv->lines_left); if (priv->lines_left == 0) { DBG (3, "ccd300_read: scan finished\n"); return_home (dev, SANE_TRUE); } sanei_pa4s2_enable (dev->fd, SANE_FALSE); } backends-1.3.0/backend/mustek_pp_ccd300.h000066400000000000000000000056141456256263500201140ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2000-2003 Jochen Eisinger This file is part of the SANE package. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . 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 the hardware driver scanners using a 300dpi CCD */ #ifndef __MUSTEK_PP_CCD300_H #define __MUSTEK_PP_CCD300_H /* i might sort and comment this struct one day... */ typedef struct { unsigned char asic; unsigned char ccd_type; int top; int motor_stop; int bank_count; unsigned int wait_bank; int hwres; int adjustskip; int ref_black; int ref_red; int ref_green; int ref_blue; int res_step; int blackpos; int motor_step; int saved_skipcount; int channel; int saved_mode; int saved_invert; int skipcount; int saved_skipimagebyte; int skipimagebytes; int saved_adjustskip; int saved_res; int saved_hwres; int saved_res_step; int saved_line_step; int line_step; int saved_channel; unsigned char *calib_g; unsigned char *calib_r; unsigned char *calib_b; int line_diff; int bw; unsigned char **red; unsigned char **blue; unsigned char *green; int redline; int blueline; int ccd_line; int rdiff; int bdiff; int gdiff; int green_offs; int blue_offs; int motor_phase; int image_control; int lines; int lines_left; } mustek_pp_ccd300_priv; #endif backends-1.3.0/backend/mustek_pp_cis.c000066400000000000000000002554471456256263500177240ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. 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 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, see . As a special exception, the authors of SANE give permission for additional uses of the libraries contained in this release of SANE. The exception is that, if you link a SANE library with other files to produce an executable, this does not by itself cause the resulting executable to be covered by the GNU General Public License. Your use of that executable is in no way restricted on account of linking the SANE library code into it. This exception does not, however, invalidate any other reasons why the executable file might be covered by the GNU General Public License. If you submit changes to SANE to the maintainers to be included in a subsequent release, you agree by submitting the changes that those changes may be distributed with this exception intact. If you write modifications of your own for SANE, it is your choice whether to permit this exception to apply to your modifications. If you do not wish that, delete this exception notice. This file implements a SANE backend for Mustek PP flatbed _CIS_ scanners. */ /* Global picture Mustek_PP_handle -> Mustek_PP_dev -> priv = Mustek_PP_CIS_dev -> CIS */ /* * This flag determines whether the scanner uses fast skipping at high * resolutions. It is possible that this fast skipping introduces * inaccuracies. It if turns out to be a problem, fast skipping can * be disabled by setting this flag to 0. */ #define MUSTEK_PP_CIS_FAST_SKIP 1 #define MUSTEK_PP_CIS_WAIT_BANK 200 /* * These parameters determine where the scanable area starts at the top. * If there is a consistent offset error, you can tune it through these * parameters. Note that an inaccuracy in the order of 1 mm seems to be * normal for the Mustek 600/1200 CP series. */ #define MUSTEK_PP_CIS_600CP_DEFAULT_SKIP 250 #define MUSTEK_PP_CIS_1200CP_DEFAULT_SKIP 330 /* * Number of scan lines on which the average is taken to determine the * maximum number of color levels. */ #define MUSTEK_PP_CIS_AVERAGE_COUNT 32 #define MUSTEK_PP_CIS600 1 #define MUSTEK_PP_CIS1200 2 #define MUSTEK_PP_CIS1200PLUS 3 #define MUSTEK_PP_CIS_CHANNEL_RED 0 #define MUSTEK_PP_CIS_CHANNEL_GREEN 1 #define MUSTEK_PP_CIS_CHANNEL_BLUE 2 #define MUSTEK_PP_CIS_CHANNEL_GRAY 1 #define MUSTEK_PP_CIS_MAX_H_PIXEL 5118 #define MUSTEK_PP_CIS_MAX_V_PIXEL 7000 #define MUSTEK_PP_CIS_MOTOR_REVERSE 0 #include "../include/sane/config.h" #include #include #include #include #include #include #ifdef HAVE_SYS_SELECT_H # include #endif #include "../include/sane/sane.h" #include "../include/sane/sanei_pa4s2.h" #define DEBUG_DECLARE_ONLY #include "mustek_pp.h" #include "mustek_pp_decl.h" #include "mustek_pp_cis.h" /****************************************************************************** ****************************************************************************** *** MA1015 chipset related functionality *** ****************************************************************************** *****************************************************************************/ /* These defines control some debugging functionality #define M1015_TRACE_REGS -> trace the status of the internal registers #define M1015_LOG_HL -> create a high-level log file (register-level) #define M1015_LOG_LL -> create a low-level log file (byte-level) By default, all logging/tracing is turned off. */ /****************************************************************************** * Low level logging: logs read and writes at the byte level, similar to * the sequences produced by tool of Jochen Eisinger * for analysing the TWAIN driver communication. * This simplifies comparison of the sequences. *****************************************************************************/ #ifdef M1015_LOG_LL static FILE* M1015_LOG_1; #define M1015_START_LL\ M1015_LOG_1 = fopen("cis_ll.log", "w"); #define M1015_STOP_LL\ fclose(M1015_LOG_1); #define SANEI_PA4S2_WRITEBYTE(fd, reg, val)\ do\ {\ sanei_pa4s2_writebyte (fd, reg, val);\ fprintf(M1015_LOG_1, "\tsanei_pa4s2_writebyte(fd, %d, 0x%02X);\n", \ reg, val);\ } while (0) static const char* cis_last_rreg_name; static int cis_read_count; #define SANEI_PA4S2_READBEGIN(fd, reg)\ do\ {\ cis_last_rreg_name = Mustek_PP_1015_reg_r_name(reg);\ cis_read_count = 0;\ sanei_pa4s2_readbegin(fd, reg);\ } while (0) #define SANEI_PA4S2_READBYTE(fd, val)\ do\ {\ sanei_pa4s2_readbyte(fd, val);\ ++cis_read_count;\ } while (0) #define SANEI_PA4S2_READEND(fd)\ do\ {\ sanei_pa4s2_readend(fd);\ fprintf(M1015_LOG_1, "\tread_reg(%s, %d);\n", \ cis_last_rreg_name, cis_read_count);\ } while (0) #define M1015_MARK_LL(info)\ fprintf(M1015_LOG_1, "* %s\n", info); #else /* M1015_LOG_LL */ #define M1015_START_LL #define M1015_STOP_LL #define SANEI_PA4S2_WRITEBYTE(fd, reg, val)\ sanei_pa4s2_writebyte (fd, reg, val) #define SANEI_PA4S2_READBEGIN(fd, reg)\ sanei_pa4s2_readbegin(fd, reg) #define SANEI_PA4S2_READBYTE(fd, val)\ sanei_pa4s2_readbyte(fd, val) #define SANEI_PA4S2_READEND(fd)\ sanei_pa4s2_readend(fd) #define M1015_MARK_LL(info) #endif /* M1015_LOG_LL */ /****************************************************************************** * High-level logging: traces the flow of the driver in a hierarchical way * up to the level of register accesses. *****************************************************************************/ #ifdef M1015_LOG_HL static FILE* M1015_LOG_2; static char hl_prev_line[4096], hl_next_line[4096], hl_repeat_count; /* * A few variables for hierarchical log message indentation. */ static const char* cis_indent_start = " "; static const char* cis_indent; static const char* cis_indent_end; #define M1015_START_HL\ M1015_LOG_2 = fopen("cis_hl.log", "w");\ cis_indent = cis_indent_start + strlen(cis_indent_start);\ cis_indent_end = cis_indent;\ hl_prev_line[0] = 0;\ hl_next_line[0] = 0;\ hl_repeat_count = 0; #define M1015_FLUSH_HL\ if (strcmp(hl_prev_line, hl_next_line))\ {\ fprintf(M1015_LOG_2, &hl_prev_line[0]);\ strcpy(&hl_prev_line[0], &hl_next_line[0]);\ if (hl_repeat_count != 0)\ {\ fprintf(M1015_LOG_2, "%s [last message repeated %d times]\n",\ cis_indent, hl_repeat_count+1); \ }\ hl_repeat_count = 0;\ }\ else\ {\ hl_repeat_count += 1;\ } #define M1015_MARK(info)\ sprintf(&hl_next_line[0], "%s+ %s\n", cis_indent, info);\ M1015_FLUSH_HL #define M1015_STOP_HL\ hl_next_line[0] = 0;\ M1015_FLUSH_HL\ fclose(M1015_LOG_2); #else /* M1015_LOG_HL */ #define M1015_START_HL #define M1015_STOP_HL #define M1015_MARK(info) #define M1015_FLUSH_HL #endif /* M1015_LOG_HL */ #ifdef M1015_TRACE_REGS #define M1015_DISPLAY_REGS(dev, msg) Mustek_PP_1015_display_regs(dev, msg) #define M1015_DISPLAY_REG(msg, val) Mustek_PP_1015_display_reg(msg, val) #else #define M1015_DISPLAY_REGS(dev, msg) #define M1015_DISPLAY_REG(msg, val) #endif #if defined (M1015_LOG_HL) || defined (M1015_LOG_LL) static const char* Mustek_PP_1015_reg_r_name(Mustek_PP_1015R_reg id) { static const char* names[4] = { "ASIC", "SCAN_VAL", "MOTOR", "BANK_COUNT" }; return names[id & 0x03]; } static const char* Mustek_PP_1015_bit_name(Mustek_PP_1015R_bit id) { static const char* names[4] = { "????", "MOTOR_HOME", "????", "MOTOR_BUSY" }; return names[id & 0x03]; } static const char* Mustek_PP_1015_reg_w_name(Mustek_PP_1015R_reg id) { static const char* names[4][4] = { { "RED_REF", "GREEN_REF", "BLUE_REF", "DPI_CONTROL" }, { "BYTE_COUNT_HB", "BYTE_COUNT_LB", "SKIP_COUNT", "EXPOSE_TIME" }, { "SRAM_SOURCE_PC", "MOTOR_CONTROL", "UNKNOWN_42", "UNKNOWN_82" }, { "POWER_ON_DELAY", "CCD_TIMING", "CCD_TIMING_ADJ", "RIGHT_BOUND" } }; return names[(id & 0x30) >> 4][id & 0x03]; } #endif /****************************************************************************** * Converts a register value to a hex/dec/bin representation. *****************************************************************************/ static const char* Mustek_PP_1015_show_val(int val) { /* Since we use a static temporary buffer, we must make sure that the buffer isn't altered while it is still in use (typically because more than one value is converted in a printf statement). Therefore the buffer is organized as a ring buffer. If should contain at least 21 elements in order to be able to display all registers with one printf statement. */ #define Mustek_PP_1015_RING_BUFFER_SIZE 50 static char buf[Mustek_PP_1015_RING_BUFFER_SIZE][64]; static int index = 0; int i; char* current = (char*)buf[index++]; if (index >= Mustek_PP_1015_RING_BUFFER_SIZE) index = 0; if (val < 0) { /* The register has not been initialized yet. */ sprintf(current, "---- (---) --------"); } else { sprintf(current, "0x%02X (%3d) ", val & 0xFF, val & 0xFF); for (i=0; i<8; ++i) { sprintf(current+11+i, "%d", (val >> (7-i)) & 1); } } return current; } #ifdef M1015_TRACE_REGS /****************************************************************************** * Displays the contents of all registers of the scanner on stderr. *****************************************************************************/ static void Mustek_PP_1015_display_regs(Mustek_PP_CIS_dev * dev, const char* info) { /* * Register naming convention: * Rx : read-only register no. x * ByWx : write-only register no. x of bank no. y */ fprintf(stderr, "\n" "Register status: %s\n" "\n" " R0: %s : ASIC info\n" " R1: %s : scan value\n" " R2: %s : CCD/motor info\n" " R3: %s : bank count\n" "\n" " B0W0: %s : red reference\n" " B0W1: %s : green reference\n" " B0W2: %s : blue reference\n" " B0W3: %s : DPI control\n" "\n" " B1W0: %s : byte count, high byte\n" " B1W1: %s : byte count, low byte\n" " B1W2: %s : skip x32 pixels\n" " B1W3: %s : expose time (CCDWIDTH)\n" "\n" " B2W0: %s : SRAM source PC\n" " B2W1: %s : motor control\n" " B2W2: %s : -\n" " B2W3: %s : -\n" "\n" " B3W0: %s : power on delay\n" " B3W1: %s : CCD timing - always 0x05\n" " B3W2: %s : CCD timing adjust - always 0x00\n" " B3W3: %s : right bound (not used)\n" "\n" " CHAN: %s : channel [%s]\n" "\n", info, Mustek_PP_1015_show_val (dev->CIS.regs.in_regs[0]), Mustek_PP_1015_show_val (dev->CIS.regs.in_regs[1]), Mustek_PP_1015_show_val (dev->CIS.regs.in_regs[2]), Mustek_PP_1015_show_val (dev->CIS.regs.in_regs[3]), Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[0][0]), Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[0][1]), Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[0][2]), Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[0][3]), Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[1][0]), Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[1][1]), Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[1][2]), Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[1][3]), Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[2][0]), Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[2][1]), Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[2][2]), Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[2][3]), Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[3][0]), Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[3][1]), Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[3][2]), Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[3][3]), Mustek_PP_1015_show_val (dev->CIS.regs.channel), (dev->CIS.regs.channel == 0x80 ? "RED" : (dev->CIS.regs.channel == 0x40 ? "GREEN" : (dev->CIS.regs.channel == 0xC0 ? "BLUE" : "unknown"))) ); } /****************************************************************************** * Displays a single register value *****************************************************************************/ static void Mustek_PP_1015_display_reg(const char* info, int val) { fprintf (stderr, "%s: %s\n", info, Mustek_PP_1015_show_val(val)); } #endif /* M1015_TRACE_REGS */ /****************************************************************************** * * Reads one of the 4 internal registers of the scanner * * 0: ASIC identification * 1: scan values * 2: CCD info / motor info * 3: bank count info * *****************************************************************************/ static SANE_Byte Mustek_PP_1015_read_reg(Mustek_PP_CIS_dev * dev, Mustek_PP_1015R_reg reg) { SANE_Byte tmp; assert(reg <= 3); SANEI_PA4S2_READBEGIN (dev->desc->fd, reg & 0x03); SANEI_PA4S2_READBYTE (dev->desc->fd, &tmp); SANEI_PA4S2_READEND (dev->desc->fd); #ifdef M1015_LOG_HL sprintf(&hl_next_line[0], "%s read_reg(%s); [%s]\n", cis_indent, Mustek_PP_1015_reg_r_name(reg), Mustek_PP_1015_show_val(tmp)); M1015_FLUSH_HL; #endif #ifdef M1015_TRACE_REGS dev->CIS.regs.in_regs[reg & 0x03] = tmp; #endif return tmp; } /****************************************************************************** * * Waits for a bit of register to become 1 or 0. The period of checking can be * controlled through the sleep parameter (microseconds). * *****************************************************************************/ static SANE_Bool Mustek_PP_1015_wait_bit(Mustek_PP_CIS_dev * dev, Mustek_PP_1015R_reg reg, Mustek_PP_1015R_bit bit, SANE_Bool on, unsigned period) { SANE_Byte tmp; SANE_Byte mask, val; int tries = 0; assert(reg <= 3); assert(bit <= 3); mask = 1 << bit; /* We don't want to wait forever */ while (dev->desc->state != STATE_CANCELLED) { #if defined (M1015_LOG_LL) || defined (M1015_LOG_HL) ++tries; #endif sanei_pa4s2_readbegin (dev->desc->fd, reg & 0x03); sanei_pa4s2_readbyte (dev->desc->fd, &tmp); sanei_pa4s2_readend (dev->desc->fd); #ifdef M1015_LOG_HL sprintf(&hl_next_line[0], "%s wait_bit(%s, %s, %d): %s %s;\n", cis_indent, Mustek_PP_1015_reg_r_name(reg), Mustek_PP_1015_bit_name(bit), on?1:0, Mustek_PP_1015_show_val(mask), Mustek_PP_1015_show_val(tmp)); M1015_FLUSH_HL; #endif val = ((on == SANE_TRUE) ? tmp : ~tmp ) & mask; if (val != 0) break; if (period) usleep(period); if (tries > 50000) { #ifdef M1015_LOG_HL sprintf(&hl_next_line[0], "%s wait_bit(%s, %s, %d): failed;\n", cis_indent, Mustek_PP_1015_reg_r_name(reg), Mustek_PP_1015_bit_name(bit), on?1:0); M1015_FLUSH_HL; #endif DBG(2, "Mustek_PP_1015_wait_bit: failed (reg %d, bit %d, on: %d)\n", reg, bit, on?1:0); return SANE_FALSE; } } #ifdef M1015_LOG_HL sprintf(&hl_next_line[0], "%s wait_bit(%s, %s, %d);\n", cis_indent, Mustek_PP_1015_reg_r_name(reg), Mustek_PP_1015_bit_name(bit), on?1:0); M1015_FLUSH_HL; #endif #ifdef M1015_LOG_LL fprintf(M1015_LOG_1, "\tread_reg(%s, %d);\n", Mustek_PP_1015_reg_r_name(reg), tries); #endif #ifdef M1015_TRACE_REGS dev->CIS.regs.in_regs[reg & 0x03] = tmp; #endif return dev->desc->state != STATE_CANCELLED ? SANE_TRUE : SANE_FALSE; } /****************************************************************************** * * Writes one out of 4 registers of one of the 4 register banks (I guess) * * Bank 0 * 0: voltage red --+ * 1: voltage green +-> always set to 0x96 * 2: voltage blue --+ * 3: DPI control * * Bank 1 * 0: line adjust (?) - high byte * 1: line adjust (?) - low byte * 2: unknown (values seen: 0x00, 0x02, 0x03, 0x1D) * 3: expose time (?) (values seen: 0xAA, 0xFD, 0xFE, 0xFF) * * Bank 2 * 0: unknown, used to start linear sequence during calibration * 1: motor control code (forward, return home, ...) * 2: never used * 3: never used * * Bank 3 * 0: reduction factor (16bit internal -> 8bit) -> target for calibration * 1: unknown -> always set to 0x05 * 2: unknown -> always set to 0x00 * 3: never used * *****************************************************************************/ static void Mustek_PP_1015_write_reg(Mustek_PP_CIS_dev * dev, Mustek_PP_1015W_reg reg, SANE_Byte val) { SANE_Byte regBank = (reg & 0xF0) >> 4; SANE_Byte regNo = (reg & 0x0F); assert (regNo <= 3); assert (regBank <= 3); SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, (1 << (4+regNo))+ regBank); SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 5, val); SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, regBank); #ifdef M1015_TRACE_REGS dev->CIS.regs.out_regs[regBank][regNo] = val; #endif #ifdef M1015_LOG_HL sprintf(&hl_next_line[0], "%s write_reg(%s, 0x%02X);\n", cis_indent, Mustek_PP_1015_reg_w_name(reg), val); M1015_FLUSH_HL; #endif } /****************************************************************************** * * Writes 2 values to 2 adjecent registers. * It is probably equivalent to 2 simple write operations (but I'm not sure). * * val1 is written to register[regNo] * val2 is written to register[regNo+1] * *****************************************************************************/ static void Mustek_PP_1015_write_reg2(Mustek_PP_CIS_dev * dev, Mustek_PP_1015W_reg reg, SANE_Byte val1, SANE_Byte val2) { SANE_Byte regBank = (reg & 0xF0) >> 4; SANE_Byte regNo = (reg & 0x0F); assert (regNo <= 2); assert (regBank <= 3); SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, (1 << (4+regNo))+ regBank); SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 5, val1); SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, (1 << (5+regNo))+ regBank); SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 5, val2); SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, regBank); #ifdef M1015_TRACE_REGS dev->CIS.regs.out_regs[regBank][regNo] = val1; dev->CIS.regs.out_regs[regBank][regNo+1] = val2; #endif #ifdef M1015_LOG_HL sprintf(&hl_next_line[0], "%s write_reg2(%s, 0x%02X, 0x%02X);\n", cis_indent, Mustek_PP_1015_reg_w_name(reg), val1, val2); M1015_FLUSH_HL; #endif } /****************************************************************************** * * Writes 3 values to 3 adjecent registers. * It is probably equivalent to 3 simple write operations (but I'm not sure). * * val1 is written to register[regNo] * val2 is written to register[regNo+1] * val3 is written to register[regNo+2] * *****************************************************************************/ static void Mustek_PP_1015_write_reg3(Mustek_PP_CIS_dev * dev, Mustek_PP_1015W_reg reg, SANE_Byte val1, SANE_Byte val2, SANE_Byte val3) { SANE_Byte regBank = (reg & 0xF0) >> 4; SANE_Byte regNo = (reg & 0x0F); assert (regNo <= 1); assert (regBank <= 3); SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, (1 << (4+regNo))+ regBank); SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 5, val1); SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, (1 << (5+regNo))+ regBank); SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 5, val2); SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, (1 << (6+regNo))+ regBank); SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 5, val3); SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, regBank); #ifdef M1015_TRACE_REGS dev->CIS.regs.out_regs[regBank][regNo ] = val1; dev->CIS.regs.out_regs[regBank][regNo+1] = val2; dev->CIS.regs.out_regs[regBank][regNo+2] = val3; #endif #ifdef M1015_LOG_HL sprintf(&hl_next_line[0], "%s write_reg3(%s, 0x%02X, 0x%02X, 0x%02X);\n", cis_indent, Mustek_PP_1015_reg_w_name(reg), val1, val2, val3); M1015_FLUSH_HL; #endif } /****************************************************************************** * Opens a register for a (series of) write operation(s). *****************************************************************************/ static void Mustek_PP_1015_write_reg_start(Mustek_PP_CIS_dev * dev, Mustek_PP_1015W_reg reg) { SANE_Byte regBank = (reg & 0xF0) >> 4; SANE_Byte regNo = (reg & 0x0F); assert (regNo <= 3); assert (regBank <= 3); dev->CIS.regs.current_write_reg = reg; #ifdef M1015_LOG_HL dev->CIS.regs.write_count = 0; #endif SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, (1 << (4+regNo))+ regBank); } /****************************************************************************** * Writes a value to the currently open register. *****************************************************************************/ static void Mustek_PP_1015_write_reg_val(Mustek_PP_CIS_dev * dev, SANE_Byte val) { #ifdef M1015_TRACE_REGS SANE_Byte regBank = (dev->CIS.regs.current_write_reg & 0xF0) >> 4; SANE_Byte regNo = (dev->CIS.regs.current_write_reg & 0x0F); assert (regNo <= 3); assert (regBank <= 3); dev->CIS.regs.out_regs[regBank][regNo] = val; #endif SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 5, val); #ifdef M1015_LOG_HL ++dev->CIS.regs.write_count; #endif } /****************************************************************************** * Closes a register after a (series of) write operation(s). *****************************************************************************/ static void Mustek_PP_1015_write_reg_stop(Mustek_PP_CIS_dev * dev) { SANE_Byte regBank = (dev->CIS.regs.current_write_reg & 0xF0) >> 4; #ifdef M1015_LOG_HL SANE_Byte regNo = (dev->CIS.regs.current_write_reg & 0x0F); assert (regNo <= 3); sprintf(&hl_next_line[0], "%s write_reg_multi(%s, *%d);\n", cis_indent, Mustek_PP_1015_reg_w_name(dev->CIS.regs.current_write_reg), dev->CIS.regs.write_count); M1015_FLUSH_HL; #endif assert (regBank <= 3); SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, regBank); } /****************************************************************************** * * Sends a command to the scanner. The command should not access one of the * internal registers, ie., the 3rd bit should not be zero. * *****************************************************************************/ static void Mustek_PP_1015_send_command(Mustek_PP_CIS_dev * dev, SANE_Byte command) { assert (command & 0x04); SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, command); #ifdef M1015_LOG_HL sprintf(&hl_next_line[0], "%s send_command(0x%02X);\n", cis_indent, command); M1015_FLUSH_HL; #endif } /****************************************************************************** ############################################################################## ## CIS driver ## ############################################################################## *****************************************************************************/ /****************************************************************************** * Resolution conversion functions *****************************************************************************/ static int max2hw_hres(Mustek_PP_CIS_dev *dev, int dist) { return (int)((dist * dev->CIS.hw_hres) / dev->desc->dev->maxres + 0.5); } #ifdef NOT_USED static int max2hw_vres(Mustek_PP_CIS_dev *dev, int dist) { return (int)((dist * dev->CIS.hw_vres) / dev->desc->dev->maxres + 0.5); } #endif static int max2cis_hres(Mustek_PP_CIS_dev *dev, int dist) { return (int)((dist * dev->CIS.cisRes) / dev->desc->dev->maxres + 0.5); } static int cis2max_res(Mustek_PP_CIS_dev *dev, int dist) { return (int)((dist * dev->desc->dev->maxres) / dev->CIS.cisRes + 0.5); } #ifdef NOT_USED static int hw2max_vres(Mustek_PP_CIS_dev *dev, int dist) { return (int)((dist * dev->desc->dev->maxres) / dev->CIS.hw_vres + 0.5); } #endif /****************************************************************************** * Attempts to extract the current bank no. *****************************************************************************/ static void cis_get_bank_count(Mustek_PP_CIS_dev *dev) { dev->bank_count = (Mustek_PP_1015_read_reg(dev, MA1015R_BANK_COUNT) & 0x7); if (dev->CIS.use8KBank) dev->bank_count >>= 1; } /****************************************************************************** * Triggers a bank switch (I assume). *****************************************************************************/ static void cis_set_sti(Mustek_PP_CIS_dev *dev) { SANEI_PA4S2_WRITEBYTE(dev->desc->fd, 3, 0xFF); dev->bank_count++; dev->bank_count &= (dev->CIS.use8KBank == SANE_TRUE) ? 3 : 7; } /****************************************************************************** * Wait till the bank with a given number becomes available. *****************************************************************************/ static SANE_Bool cis_wait_bank_change (Mustek_PP_CIS_dev * dev, int bankcount) { struct timeval start, end; unsigned long diff; int firsttime = 1; gettimeofday (&start, NULL); do { if (1 /*niceload*/) { if (firsttime) firsttime = 0; else usleep (10); /* for a little nicer load */ } cis_get_bank_count (dev); gettimeofday (&end, NULL); diff = (end.tv_sec * 1000 + end.tv_usec / 1000) - (start.tv_sec * 1000 + start.tv_usec / 1000); } while ((dev->bank_count != bankcount) && (diff < MUSTEK_PP_CIS_WAIT_BANK)); if (dev->bank_count != bankcount && dev->desc->state != STATE_CANCELLED) { u_char tmp; tmp = Mustek_PP_1015_read_reg(dev, 3); DBG(2, "cis_wait_bank_change: Missed a bank: got %d [%s], " "wanted %d, waited %d msec\n", dev->bank_count, Mustek_PP_1015_show_val(tmp), bankcount, MUSTEK_PP_CIS_WAIT_BANK); } return dev->bank_count == bankcount ? SANE_TRUE : SANE_FALSE; } /****************************************************************************** * Configure the CIS for a given resolution. * * CIS scanners seem to have 2 modes: * * low resolution (50-300 DPI) and * high resolution (300-600 DPI). * * Depending on the resolution requested by the user, the scanner is used * in high or low resolution mode. In high resolution mode, the motor step * sizes are also reduced by a factor of two. * *****************************************************************************/ static void cis_set_dpi_value (Mustek_PP_CIS_dev * dev) { u_char val = 0; if (dev->model == MUSTEK_PP_CIS1200PLUS) { /* Toshiba CIS: only 600 DPI + decimation */ switch (dev->CIS.hw_hres) { case 75: val = 0x48; /* 1/8 */ break; case 100: val = 0x08; /* 1/6 */ break; case 200: val = 0x00; /* 1/3 */ break; case 300: val = 0x50; /* 2/4 */ break; case 400: val = 0x10; /* 2/3 */ break; case 600: val = 0x20; /* 3/3 */ break; default: assert (0); } } else { /* Canon CIS: sensor can use 300 or 600 DPI */ switch (dev->CIS.hw_hres) { case 50: val = 0x08; /* 1/6 */ break; case 100: val = 0x00; /* 1/3 */ break; case 200: val = 0x10; /* 2/3 */ break; case 300: val = 0x20; /* 3/3 */ break; case 400: val = 0x10; /* 2/3 */ break; case 600: val = 0x20; /* 3/3 */ break; default: assert (0); } } Mustek_PP_1015_write_reg(dev, MA1015W_DPI_CONTROL, val | 0x04); DBG (4, "cis_set_dpi_value: dpi: %d -> value 0x%02x\n", dev->CIS.hw_hres, val); } static void cis_set_ccd_channel (Mustek_PP_CIS_dev * dev) { SANE_Byte codes[] = { 0x84, 0x44, 0xC4 }; SANE_Byte chancode; assert (dev->CIS.channel < 3); chancode = codes[dev->CIS.channel]; /* The TWAIN driver sets an extra bit in lineart mode. When I do this too, I don't see any effect on the image. Moreover, for 1 resolution, namely 400 dpi, the bank counter seems to behave strangely, and the synchronization get completely lost. I guess the software conversion from gray to lineart is good enough, so I'll leave it like that. if (dev->CIS.setParameters) { chancode |= (dev->desc->mode == MODE_BW) ? 0x20: 0; } */ SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, chancode); #ifdef M1015_TRACE_REGS dev->CIS.regs.channel = chancode; #endif } static void cis_config_ccd (Mustek_PP_CIS_dev * dev) { SANE_Int skipCount, byteCount; if (dev->CIS.res != 0) dev->CIS.hres_step = SANE_FIX ((float) dev->CIS.hw_hres / (float) dev->CIS.res); /* CIS: <= 300 dpi -> 0x86 > 300 dpi -> 0x96 */ if (dev->CIS.cisRes == 600) SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, 0x96); else SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, 0x86); cis_set_dpi_value(dev); if (dev->CIS.setParameters) { dev->CIS.channel = dev->desc->mode == MODE_COLOR ? MUSTEK_PP_CIS_CHANNEL_RED : MUSTEK_PP_CIS_CHANNEL_GRAY; } else { dev->CIS.channel = MUSTEK_PP_CIS_CHANNEL_GRAY; } cis_set_ccd_channel (dev); Mustek_PP_1015_write_reg (dev, MA1015W_POWER_ON_DELAY, 0xAA); Mustek_PP_1015_write_reg (dev, MA1015W_CCD_TIMING, 0x05); Mustek_PP_1015_write_reg (dev, MA1015W_CCD_TIMING_ADJ, 0x00); Mustek_PP_1015_send_command (dev, 0x45); /* or 0x05 for no 8kbank */ /* * Unknown sequence. * Seems to be always the same during configuration, independent of the * mode and the resolution. */ CIS_CLEAR_FULLFLAG(dev); CIS_INC_READ(dev); CIS_CLEAR_READ_BANK(dev); CIS_CLEAR_WRITE_ADDR(dev); CIS_CLEAR_WRITE_BANK(dev); CIS_CLEAR_TOGGLE(dev); /* # SkipImage = expressed in max resolution (600 DPI) # # Formulas # # <= 300 DPI: # # Skip = 67 + skipimage/2 # # Skip1 = Skip / 32 # Skip2 = Skip % 32 # # Bytes = Skip2 * hw_hres/300 + (imagebytes * hw_hres/res) + 2 # # > 300 DPI # # Skip = 67 + skipimage # # Skip1 = Skip / 32 # Skip2 = Skip % 32 # # Bytes = Skip2*hw_hres/600 + (imagebytes * hw_hres/res) + 2 # */ skipCount = 67; /* Hardware parameter - fixed */ if (dev->CIS.setParameters == SANE_TRUE) { /* * It seems that the TWAIN driver always adds 2 mm extra. When I do the * inverse calculation from the parameters that driver sends, I always * get a difference of exactly 2mm, at every resolution and for * different positions of the scan area. Moreover, when I don't add this * offset, the resulting scan seems to start 2mm to soon. * I can't find this back in the backend of the TWAIN driver, but I * assume that this 2mm offset is taken care off at the higher levels. */ DBG(4, "cis_config_ccd: Skip count: %d\n", skipCount); skipCount += max2cis_hres(dev, dev->CIS.skipimagebytes); DBG(4, "cis_config_ccd: Skip count: %d (cis res: %d)\n", skipCount, dev->CIS.cisRes); skipCount += (int)(2.0/25.4*dev->CIS.cisRes); DBG(4, "cis_config_ccd: Skip count: %d\n", skipCount); Mustek_PP_1015_write_reg (dev, MA1015W_SKIP_COUNT, skipCount / 32); DBG(4, "cis_config_ccd: Skip count: %d (x32)\n", skipCount / 32); } else { Mustek_PP_1015_write_reg (dev, MA1015W_SKIP_COUNT, 0); DBG(4, "cis_config_ccd: Skip count: 67 (x32)\n"); } skipCount %= 32; skipCount = cis2max_res(dev, skipCount); /* Back to max res */ Mustek_PP_1015_write_reg(dev, MA1015W_EXPOSE_TIME, dev->CIS.exposeTime); DBG(4, "cis_config_ccd: skipcount: %d imagebytes: %d\n", skipCount, dev->CIS.imagebytes); /* set_initial_skip_1015 (dev); */ if (dev->CIS.setParameters == SANE_TRUE) { Mustek_PP_1015_write_reg(dev, MA1015W_EXPOSE_TIME, dev->CIS.exposeTime); Mustek_PP_1015_write_reg(dev, MA1015W_POWER_ON_DELAY, 0xAA); /* The TWAIN drivers always sends the same value: 0x96 */ Mustek_PP_1015_write_reg3(dev, MA1015W_RED_REF, 0x96, 0x96, 0x96); dev->CIS.adjustskip = max2hw_hres(dev, skipCount); byteCount = max2hw_hres(dev, skipCount + dev->CIS.imagebytes) + 2; dev->CIS.setParameters = SANE_FALSE; } else { dev->CIS.adjustskip = 0; byteCount = max2hw_hres(dev, skipCount); } DBG(4, "cis_config_ccd: adjust skip: %d bytecount: %d\n", dev->CIS.adjustskip, byteCount); Mustek_PP_1015_write_reg2(dev, MA1015W_BYTE_COUNT_HB, byteCount >> 8, byteCount & 0xFF); cis_get_bank_count (dev); DBG(5, "cis_config_ccd: done\n"); } static SANE_Bool cis_wait_motor_stable (Mustek_PP_CIS_dev * dev) { static struct timeval timeoutVal; SANE_Bool ret = Mustek_PP_1015_wait_bit (dev, MA1015R_MOTOR, MA1015B_MOTOR_STABLE, SANE_FALSE, 0); #ifdef HAVE_SYS_SELECT_H if (dev->engine_delay > 0) { timeoutVal.tv_sec = 0; timeoutVal.tv_usec = dev->engine_delay*1000; select(0, NULL, NULL, NULL, &timeoutVal); } #endif return ret; } static void cis_motor_forward (Mustek_PP_CIS_dev * dev) { SANE_Byte control; if (dev->model == MUSTEK_PP_CIS600) { switch (dev->CIS.hw_vres) { case 150: control = 0x7B; break; case 300: control = 0x73; break; case 600: control = 0x13; break; default: exit(1); } } else { switch (dev->CIS.hw_vres) { case 300: control = 0x7B; break; case 600: control = 0x73; break; case 1200: control = 0x13; break; default: exit(1); } } #if MUSTEK_PP_CIS_MOTOR_REVERSE == 1 control ^= 0x10; #endif DBG(4, "cis_motor_forward: @%d dpi: 0x%02X.\n", dev->CIS.hw_vres, control); if (!cis_wait_motor_stable (dev)) return; Mustek_PP_1015_write_reg(dev, MA1015W_MOTOR_CONTROL, control); } static void cis_move_motor (Mustek_PP_CIS_dev * dev, SANE_Int steps) /* steps @ maxres */ { /* Note: steps is expressed at maximum resolution */ SANE_Byte fullStep = 0x13, biStep = 0x73, quadStep = 0x7B; SANE_Int fullSteps, biSteps, quadSteps; /* * During a multi-step feed, the expose time is fixed. The value depends * on the type of the motor (600/1200 CP) */ SANE_Byte savedExposeTime = dev->CIS.exposeTime; dev->CIS.exposeTime = 85; DBG(4, "cis_move_motor: Moving motor %d steps.\n", steps); /* Just in case ... */ if (steps < 0) { DBG(1, "cis_move_motor: trying to move negative steps: %d\n", steps); steps = 0; /* We must go through the configuration procedure */ } /* * Using the parameter settings for the 600 CP on a 1200 CP scanner * doesn't work: the engine doesn't move and makes a sharp noise, which * doesn't sound too healthy. It could be harmful to the motor ! * Apparently, the same happens on a real 600 CP (reported by Disma * Goggia), so it's probably better to always use the 1200 CP settings. */ dev->CIS.exposeTime <<= 1; cis_config_ccd(dev); dev->CIS.exposeTime = savedExposeTime; /* * This is a minor speed optimization: when we are using the high * resolution mode, long feeds (eg, to move to a scan area at the bottom * of the page) can be made almost twice as fast by using double motor * steps as much as possible. * It is possible, though, that fast skipping (which is the default) is * not very accurate on some scanners. Therefore, the user can disable * this through the configuration file. */ fullSteps = steps & 1; biSteps = steps >> 1; if (dev->fast_skip) { quadSteps = biSteps >> 1; biSteps &= 1; } else { quadSteps = 0; } M1015_DISPLAY_REGS(dev, "Before move"); #if MUSTEK_PP_CIS_MOTOR_REVERSE == 1 fullStep ^= 0x10; biStep ^= 0x10; quadStep ^= 0x10; #endif DBG(4, "cis_move_motor: 4x%d 2x%d 1x%d\n", quadSteps, biSteps, fullSteps); /* Note: the TWAIN driver opens the motor control register only once before the loop, and closes it after the loop. I've tried this too, but it resulted in inaccurate skip distances; therefore, the motor control register is now opened and closed for each step. */ while (quadSteps-- > 0 && dev->desc->state != STATE_CANCELLED) { cis_wait_motor_stable (dev); Mustek_PP_1015_write_reg(dev, MA1015W_MOTOR_CONTROL, quadStep); } while (biSteps-- > 0 && dev->desc->state != STATE_CANCELLED) { cis_wait_motor_stable (dev); Mustek_PP_1015_write_reg(dev, MA1015W_MOTOR_CONTROL, biStep); } while (fullSteps-- > 0 && dev->desc->state != STATE_CANCELLED) { cis_wait_motor_stable (dev); Mustek_PP_1015_write_reg(dev, MA1015W_MOTOR_CONTROL, fullStep); } } static void cis_set_et_pd_sti (Mustek_PP_CIS_dev * dev) { Mustek_PP_1015_write_reg(dev, MA1015W_EXPOSE_TIME, dev->CIS.exposeTime); Mustek_PP_1015_write_reg(dev, MA1015W_POWER_ON_DELAY, dev->CIS.powerOnDelay[dev->CIS.channel]); cis_set_ccd_channel (dev); cis_set_sti (dev); } /* * Prepare the scanner for catching the next channel and, if necessary, * move the head one step further. */ static SANE_Bool cis_wait_next_channel (Mustek_PP_CIS_dev * dev) { int moveAtChannel = dev->desc->mode == MODE_COLOR ? MUSTEK_PP_CIS_CHANNEL_BLUE : MUSTEK_PP_CIS_CHANNEL_GRAY; if (!cis_wait_bank_change (dev, dev->bank_count)) { DBG(2, "cis_wait_next_channel: Could not get next bank.\n"); return SANE_FALSE; } moveAtChannel = (dev->desc->mode == MODE_COLOR) ? MUSTEK_PP_CIS_CHANNEL_BLUE : MUSTEK_PP_CIS_CHANNEL_GRAY; if (dev->CIS.channel == moveAtChannel && !dev->CIS.dontMove) { cis_motor_forward (dev); } cis_set_et_pd_sti (dev); if (dev->desc->mode == MODE_COLOR) { ++dev->CIS.channel; dev->CIS.channel %= 3; } return SANE_TRUE; } /* * Wait for the device to be ready for scanning. Cycles through the different * channels and sets the parameters (only green channel in gray/lineart). */ static SANE_Bool cis_wait_read_ready (Mustek_PP_CIS_dev * dev) { int channel; dev->CIS.dontIncRead = SANE_TRUE; dev->CIS.channel = dev->desc->mode == MODE_COLOR ? MUSTEK_PP_CIS_CHANNEL_RED : MUSTEK_PP_CIS_CHANNEL_GRAY; for (channel = 0; channel < 3; ++channel) { if (!cis_wait_next_channel(dev)) return SANE_FALSE; } return SANE_TRUE; } static int delay_read (int delay) { /* * A (very) smart compiler may complete optimize the delay loop away. By * adding some difficult data dependencies, we can try to prevent this. */ static int prevent_removal, i; for (i = 0; iCIS.adjustskip, cval; int bpos = 0; SANE_Byte low_val = 0, hi_val = 255; if (pixel <= 0) return; SANEI_PA4S2_READBEGIN (dev->desc->fd, 1); while(skips-- >= 0) { if (dev->CIS.delay) delay_read(dev->CIS.delay); SANEI_PA4S2_READBYTE (dev->desc->fd, &color); } if (dev->CIS.hw_hres == dev->CIS.res) { /* One-to one mapping */ DBG (6, "cis_read_line_low_level: one-to-one\n"); for (ctr = 0; ctr < pixel; ctr++) { if (dev->CIS.delay) delay_read(dev->CIS.delay); SANEI_PA4S2_READBYTE (dev->desc->fd, &color); cval = color; if (calib_low) { low_val = calib_low[ctr] ; } if (calib_hi) { hi_val = calib_hi[ctr] ; } cval -= low_val ; cval <<= 8 ; cval /= hi_val-low_val ; if (cval < 0) cval = 0; else if (cval > 255) cval = 255; if (gamma) cval = gamma[cval]; buf[ctr] = cval; } } else if (dev->CIS.hw_hres > dev->CIS.res) { /* Sub-sampling */ int pos = 0; DBG (6, "cis_read_line_low_level: sub-sampling\n"); ctr = 0; do { if (dev->CIS.delay) delay_read(dev->CIS.delay); SANEI_PA4S2_READBYTE (dev->desc->fd, &color); cval = color; if (ctr < (pos >> SANE_FIXED_SCALE_SHIFT)) { ctr++; continue; } ctr++; pos += dev->CIS.hres_step; if (calib_low) { low_val = calib_low[bpos] ; } if (calib_hi) { hi_val = calib_hi[bpos] ; } cval -= low_val ; cval <<= 8 ; cval /= hi_val-low_val ; if (cval < 0) cval = 0 ; else if (cval > 255) cval = 255 ; if (gamma) cval = gamma[cval]; buf[bpos++] = cval; } while (bpos < pixel); } else { int calctr = 0; SANE_Int pos = 0, nextPos = 1; /* Step: eg: 600 DPI -> 700 DPI -> hres_step = 6/7 -> step = 1/7 */ SANE_Int step = SANE_FIX(1) - dev->CIS.hres_step; /* Super-sampling */ DBG (6, "cis_read_line_low_level: super-sampling\n"); do { if (dev->CIS.delay) delay_read(dev->CIS.delay); SANEI_PA4S2_READBYTE (dev->desc->fd, &color); cval = color; if (calib_low) { low_val = calib_low[calctr] ; } if (calib_hi) { hi_val = calib_hi[calctr] ; } if (++calctr >= dev->calib_pixels) { /* Avoid array boundary violations due to rounding errors (due to the incremental calculation, the current position may be inaccurate to up to two pixels, so we may need to read a few extra bytes -> use the last calibration value) */ calctr = dev->calib_pixels - 1; DBG (3, "cis_read_line_low_level: calibration overshoot\n"); } cval -= low_val ; cval <<= 8 ; cval /= hi_val-low_val ; if (cval < 0) cval = 0 ; else if (cval > 255) cval = 255 ; if (gamma) cval = gamma[cval]; pos += step; if ((pos >> SANE_FIXED_SCALE_SHIFT) >= nextPos) { nextPos++; /* Insert an interpolated value */ buf[bpos] = (buf[bpos-1] + cval)/2; /* Interpolate */ ++bpos; /* Store the plain value, but only if we still need pixels */ if (bpos < pixel) buf[bpos++] = cval; pos += step; /* Take interpolated value into account for pos */ } else { buf[bpos++] = cval; } } while (bpos < pixel); } SANEI_PA4S2_READEND (dev->desc->fd); DBG (6, "cis_read_line_low_level: done\n"); } static SANE_Bool cis_read_line (Mustek_PP_CIS_dev * dev, SANE_Byte* buf, SANE_Int pixel, SANE_Bool raw) { if (!dev->CIS.dontIncRead) CIS_INC_READ(dev); else dev->CIS.dontIncRead = SANE_FALSE; if (raw) { /* No color correction; raw data */ cis_read_line_low_level (dev, buf, pixel, NULL, NULL, NULL); } else { /* Color correction */ cis_read_line_low_level (dev, buf, pixel, dev->calib_low[dev->CIS.channel], dev->calib_hi[dev->CIS.channel], (dev->desc->val[OPT_CUSTOM_GAMMA].w ? dev->desc->gamma_table[dev->CIS.channel] : NULL)); } return cis_wait_next_channel(dev); } static void cis_get_next_line (Mustek_PP_CIS_dev * dev, SANE_Byte * buf) { SANE_Byte *dest, *tmpbuf = dev->tmpbuf; int ctr, channel, first, last, stride, step = dev->CIS.line_step; SANE_Byte gotline; if (dev->desc->mode == MODE_COLOR) { first = MUSTEK_PP_CIS_CHANNEL_RED; last = MUSTEK_PP_CIS_CHANNEL_BLUE; stride = 3; } else { first = MUSTEK_PP_CIS_CHANNEL_GRAY; last = MUSTEK_PP_CIS_CHANNEL_GRAY; stride = 1; } gotline = SANE_FALSE; do { dev->ccd_line++; if ((dev->line_diff >> SANE_FIXED_SCALE_SHIFT) != dev->ccd_line) { cis_motor_forward (dev); continue; } dev->line_diff += step; for (channel = first; channel <= last; ++channel) { if (!cis_read_line(dev, tmpbuf, dev->desc->params.pixels_per_line, SANE_FALSE)) return; dest = buf + channel - first; for (ctr = 0; ctr < dev->desc->params.pixels_per_line; ctr++) { *dest = tmpbuf[ctr]; dest += stride; } } gotline = SANE_TRUE; } while (!gotline && dev->desc->state != STATE_CANCELLED); } static void cis_get_grayscale_line (Mustek_PP_CIS_dev * dev, SANE_Byte * buf) { cis_get_next_line(dev, buf); } static void cis_get_lineart_line (Mustek_PP_CIS_dev * dev, SANE_Byte * buf) { int ctr; SANE_Byte gbuf[MUSTEK_PP_CIS_MAX_H_PIXEL * 2]; cis_get_grayscale_line (dev, gbuf); memset (buf, 0xFF, dev->desc->params.bytes_per_line); for (ctr = 0; ctr < dev->desc->params.pixels_per_line; ctr++) buf[ctr >> 3] ^= ((gbuf[ctr] > dev->bw_limit) ? (1 << (7 - ctr % 8)) : 0); } static void cis_get_color_line (Mustek_PP_CIS_dev * dev, SANE_Byte * buf) { cis_get_next_line(dev, buf); } /****************************************************************************** * Saves the state of the device during reset and calibration. *****************************************************************************/ static void cis_save_state (Mustek_PP_CIS_dev * dev) { dev->Saved_CIS = dev->CIS; } /****************************************************************************** * Restores the state of the device after reset and calibration. *****************************************************************************/ static void cis_restore_state (Mustek_PP_CIS_dev * dev) { dev->CIS = dev->Saved_CIS; } #define CIS_TOO_BRIGHT 1 #define CIS_OK 0 #define CIS_TOO_DARK -1 static int cis_check_result(SANE_Byte* buffer, int pixel) { int i, maxVal = 0; for (i=0;i maxVal) maxVal = buffer[i]; if (maxVal > 250) return CIS_TOO_BRIGHT; if (maxVal < 240) return CIS_TOO_DARK; return CIS_OK; } static SANE_Bool cis_maximize_dynamic_range(Mustek_PP_CIS_dev * dev) { /* The device is in its final configuration already. */ int i, j, pixel, channel, minExposeTime, first, last; SANE_Byte powerOnDelayLower[3], powerOnDelayUpper[3], exposeTime[3]; SANE_Byte buf[3][MUSTEK_PP_CIS_MAX_H_PIXEL]; SANE_Int pixels = dev->calib_pixels; DBG(3, "cis_maximize_dynamic_range: starting\n"); for (channel = 0; channel < 3; ++channel) { exposeTime[channel] = 254; dev->CIS.powerOnDelay[channel] = 170; powerOnDelayLower[channel] = 1; powerOnDelayUpper[channel] = 254; } dev->CIS.setParameters = SANE_TRUE; dev->CIS.exposeTime = exposeTime[MUSTEK_PP_CIS_CHANNEL_GREEN]; cis_config_ccd(dev); M1015_DISPLAY_REGS(dev, "before maximizing dynamic range"); dev->CIS.dontMove = SANE_TRUE; /* Don't move while calibrating */ if (!cis_wait_read_ready(dev) && dev->desc->state != STATE_CANCELLED) { DBG(2, "cis_maximize_dynamic_range: DEVICE NOT READY!\n"); return SANE_FALSE; } if (dev->desc->mode == MODE_COLOR) { first = MUSTEK_PP_CIS_CHANNEL_RED; last = MUSTEK_PP_CIS_CHANNEL_BLUE; } else { first = MUSTEK_PP_CIS_CHANNEL_GRAY; last = MUSTEK_PP_CIS_CHANNEL_GRAY; } dev->CIS.channel = first; /* Perform a kind of binary search. In the worst case, we should find the optimal power delay values after 8 iterations */ for( i=0; i<8; i++) { for (channel = first; channel <= last; ++channel) { dev->CIS.powerOnDelay[channel] = (powerOnDelayLower[channel] + powerOnDelayUpper[channel]) / 2; } Mustek_PP_1015_write_reg(dev, MA1015W_POWER_ON_DELAY, dev->CIS.powerOnDelay[1]); /* Green */ for (pixel = 0; pixel < pixels; ++pixel) { buf[0][pixel] = buf[1][pixel] = buf[2][pixel] = 255; } /* Scan 4 lines, but ignore the first 3 ones. */ for (j = 0; j < 4; ++j) { for (channel = first; channel <= last; ++channel) { if (!cis_read_line(dev, &buf[channel][0], pixels, /* raw = */ SANE_TRUE)) return SANE_FALSE; } } for (channel = first; channel <= last; ++channel) { switch (cis_check_result(buf[channel], pixels)) { case CIS_TOO_BRIGHT: powerOnDelayLower[channel] = dev->CIS.powerOnDelay[channel]; break; case CIS_TOO_DARK: powerOnDelayUpper[channel] = dev->CIS.powerOnDelay[channel]; break; default: break; } } DBG (4, "cis_maximize_dynamic_range: power on delay %3d %3d %3d\n", dev->CIS.powerOnDelay[0], dev->CIS.powerOnDelay[1], dev->CIS.powerOnDelay[2]); } dev->CIS.dontMove = SANE_FALSE; DBG (3, "cis_maximize_dynamic_range: power on delay %3d %3d %3d\n", dev->CIS.powerOnDelay[0], dev->CIS.powerOnDelay[1], dev->CIS.powerOnDelay[2]); minExposeTime = (dev->CIS.hw_hres <= 300) ? 170 : 253; for (channel = first; channel <= last; ++channel) { dev->CIS.powerOnDelay[channel] = (powerOnDelayLower[channel] + powerOnDelayUpper[channel]) / 2; exposeTime[channel] -= dev->CIS.powerOnDelay[channel] - 1; dev->CIS.powerOnDelay[channel] = 1; if (exposeTime[channel] < minExposeTime) { dev->CIS.powerOnDelay[channel] += minExposeTime - exposeTime[channel]; exposeTime[channel] = minExposeTime; } } dev->CIS.exposeTime = exposeTime[MUSTEK_PP_CIS_CHANNEL_GREEN]; DBG (3, "cis_maximize_dynamic_range: expose time: %3d\n", exposeTime[1]); DBG (3, "cis_maximize_dynamic_range: power on delay %3d %3d %3d\n", dev->CIS.powerOnDelay[0], dev->CIS.powerOnDelay[1], dev->CIS.powerOnDelay[2]); /* * Short the calibration. Temporary, to find out what is wrong with * the calibration on a 600 CP. * dev->CIS.exposeTime = 170; dev->CIS.powerOnDelay[0] = 120; dev->CIS.powerOnDelay[1] = 120; dev->CIS.powerOnDelay[2] = 120; */ return SANE_TRUE; } static SANE_Bool cis_measure_extremes(Mustek_PP_CIS_dev * dev, SANE_Byte* calib[3], SANE_Int pixels, SANE_Int first, SANE_Int last) { SANE_Byte buf[3][MUSTEK_PP_CIS_MAX_H_PIXEL]; SANE_Byte min[3][MUSTEK_PP_CIS_MAX_H_PIXEL]; SANE_Byte max[3][MUSTEK_PP_CIS_MAX_H_PIXEL]; SANE_Int sum[3][MUSTEK_PP_CIS_MAX_H_PIXEL]; int channel, cnt, p; memset((void*)&min, 255, 3*MUSTEK_PP_CIS_MAX_H_PIXEL*sizeof(SANE_Byte)); memset((void*)&max, 0, 3*MUSTEK_PP_CIS_MAX_H_PIXEL*sizeof(SANE_Byte)); memset((void*)&sum, 0, 3*MUSTEK_PP_CIS_MAX_H_PIXEL*sizeof(SANE_Int)); dev->CIS.channel = first; /* Purge the banks first (there's always a 3-cycle delay) */ for (channel = first; channel <= last; ++channel) { if (!cis_read_line(dev, &buf[channel%3][0], pixels, /* raw = */ SANE_TRUE)) return SANE_FALSE; } --dev->CIS.skipsToOrigin; for (cnt = 0; cnt < MUSTEK_PP_CIS_AVERAGE_COUNT + 2; ++cnt) { for (channel = first; channel <= last; ++channel) { DBG(4, "cis_measure_extremes: Reading line %d - channel %d\n", cnt, channel); if (!cis_read_line(dev, &buf[channel][0], pixels, /* raw = */ SANE_TRUE)) return SANE_FALSE; for (p = 0; p < pixels; ++p) { SANE_Byte val = buf[channel][p]; if (val < min[channel][p]) min[channel][p] = val; if (val > max[channel][p]) max[channel][p] = val; sum[channel][p] += val; } } --dev->CIS.skipsToOrigin; } DBG(4, "cis_measure_extremes: Averaging\n"); for (channel = first; channel <= last; ++channel) { /* Ignore the extreme values and take the average of the others. */ for (p = 0; p < pixels; ++p) { sum[channel][p] -= min[channel][p] + max[channel][p]; sum[channel][p] /= MUSTEK_PP_CIS_AVERAGE_COUNT; if (calib[channel]) calib[channel][p] = sum[channel][p]; } } DBG(4, "cis_measure_extremes: Done\n"); return SANE_TRUE; } static SANE_Bool cis_normalize_ranges(Mustek_PP_CIS_dev * dev) { SANE_Byte cal_low, cal_hi ; SANE_Byte powerOnDelay[3] ; SANE_Int pixels = dev->calib_pixels; SANE_Int channel, p, first, last; if (dev->desc->mode == MODE_COLOR) { first = MUSTEK_PP_CIS_CHANNEL_RED; last = MUSTEK_PP_CIS_CHANNEL_BLUE; } else { first = MUSTEK_PP_CIS_CHANNEL_GRAY; last = MUSTEK_PP_CIS_CHANNEL_GRAY; } DBG(3, "cis_normalize_ranges: Measuring high extremes\n"); /* Measure extremes with normal lighting */ if (!cis_measure_extremes(dev, dev->calib_hi, pixels, first, last)) { return SANE_FALSE; } /* Measure extremes without lighting */ for (channel=first; channel<=last; ++channel) { powerOnDelay[channel] = dev->CIS.powerOnDelay[channel]; dev->CIS.powerOnDelay[channel] = dev->CIS.exposeTime; } DBG(3, "cis_normalize_ranges: Measuring low extremes\n"); if (!cis_measure_extremes(dev, dev->calib_low, pixels, first, last)) { return SANE_FALSE; } /* Restore settings */ for (channel=first; channel<=last; ++channel) { dev->CIS.powerOnDelay[channel] = powerOnDelay[channel]; } /* Make sure calib_hi is greater than calib_low */ for (channel = first; channel <= last; ++channel) { for (p = 0; pcalib_low[channel]) { cal_low = dev->calib_low[channel][p]; } else { cal_low = 0; } if (dev->calib_hi[channel]) { cal_hi = dev->calib_hi[channel][p]; } else { cal_hi = 255; } if (cal_hi <= cal_low) { if(cal_hi<255) { /* calib_hi exists, else cal_hi would be 255 */ dev->calib_hi[channel][p] = cal_low+1; } else { /* calib_low exists, else cal_low would be 0, < 255 */ dev->calib_low[channel][p] = cal_hi-1; } } } } DBG(3, "cis_normalize_ranges: calibration done\n"); return SANE_TRUE; } /* * This routine measures the time that we have to wait between reading * to pixels from the scanner. Especially at low resolutions, but also * for narrow-width scans at high resolutions, reading too fast cause * color stability problems. * This routine sends a test pattern to the scanner memory banks and tries * to measure how fast it can be retrieved without errors. * The same is done by the TWAIN driver (TESTIO.CPP:TestDelay). */ static SANE_Bool cis_measure_delay(Mustek_PP_CIS_dev * dev) { SANE_Byte buf[2][2048]; unsigned i, j, d; int saved_res; SANE_Bool error = SANE_FALSE; CIS_CLEAR_FULLFLAG(dev); CIS_CLEAR_WRITE_ADDR(dev); CIS_CLEAR_WRITE_BANK(dev); CIS_INC_READ(dev); CIS_CLEAR_READ_BANK(dev); M1015_DISPLAY_REGS(dev, "Before delay measurement"); assert(dev->CIS.adjustskip == 0); /* Sawtooth */ for (i=0; i<2048; ++i) { buf[0][i] = i % 255; /* Why 255 ? Seems to have no real importance */ } Mustek_PP_1015_write_reg_start(dev, MA1015W_SRAM_SOURCE_PC); for (i=0; i<2048; ++i) { Mustek_PP_1015_write_reg_val(dev, buf[0][i]); } Mustek_PP_1015_write_reg_stop(dev); /* Bank offset measurement */ dev->CIS.delay = 0; /* Initialize to zero, measure next */ saved_res = dev->CIS.res; dev->CIS.res = dev->CIS.hw_hres; /* * Note: the TWAIN driver seems to have a fast EPP mode too. That one is * tried first, and then they try the normal mode. I haven't figured out * yet how the fast mode works, so I'll only check the normal mode for now. * Moreover, from the behaviour that I've witnessed from the TWAIN driver, * I must conclude that the fast mode probably doesn't work on my computer, * so I can't test it anyhow. */ /* Gradually increase the delay till we have no more errors */ for (d = 0; d < 75 /* 255 */ && dev->desc->state != STATE_CANCELLED; d += 5) { dev->CIS.delay = d; /* * We read the line 5 times to make sure that all garbage is flushed. */ for (i=0; i<5; ++i) { CIS_INC_READ(dev); CIS_CLEAR_READ_BANK(dev); cis_read_line_low_level (dev, &buf[1][0], 2048, NULL, NULL, NULL); if (dev->desc->state == STATE_CANCELLED) return SANE_FALSE; } error = SANE_FALSE; /* Check 100 times whether we can read without errors. */ for (i=0; i<100 && !error; ++i) { CIS_INC_READ(dev); CIS_CLEAR_READ_BANK(dev); cis_read_line_low_level (dev, &buf[1][0], 2048, NULL, NULL, NULL); if (dev->desc->state == STATE_CANCELLED) return SANE_FALSE; for (j=0; j<2048; ++j) { if (buf[0][j] != buf[1][j]) { error = SANE_TRUE; break; } } } DBG (3, "cis_measure_delay: delay %d\n", dev->CIS.delay); if (!error) break; } dev->CIS.res = saved_res; if (error) { fprintf(stderr, "mustek_pp_cis: failed to measure delay.\n"); fprintf(stderr, "Buffer contents:\n"); for (j = 0; j < 20; ++j) { fprintf(stderr, "%d ", buf[1][j]); } fprintf(stderr, "\n"); dev->CIS.delay = 0; } DBG (3, "cis_measure_delay: delay %d\n", dev->CIS.delay); return SANE_TRUE; } static void cis_motor_control (Mustek_PP_CIS_dev * dev, u_char control) { cis_wait_motor_stable (dev); Mustek_PP_1015_write_reg(dev, MA1015W_MOTOR_CONTROL, control); } static void cis_return_home (Mustek_PP_CIS_dev * dev, SANE_Bool nowait) { SANE_Byte savedExposeTime = dev->CIS.exposeTime; DBG(4, "cis_return_home: returning home; nowait: %d\n", nowait); /* During a return-home, the expose time is fixed. */ dev->CIS.exposeTime = 170; cis_config_ccd(dev); dev->CIS.exposeTime = savedExposeTime; cis_motor_control (dev, 0xEB); if (nowait == SANE_FALSE) Mustek_PP_1015_wait_bit(dev, MA1015R_MOTOR, MA1015B_MOTOR_HOME, SANE_TRUE, 1000); } /****************************************************************************** * Does a full reset of the device, ie. configures the CIS to a default * resolution of 300 DPI (in high or low resolution mode, depending on the * resolution requested by the user). *****************************************************************************/ static void cis_reset_device (Mustek_PP_CIS_dev * dev) { DBG(4, "cis_reset_device: resetting device\n"); dev->CIS.adjustskip = 0; dev->CIS.dontIncRead = SANE_TRUE; dev->CIS.dontMove = SANE_FALSE; cis_save_state(dev); dev->CIS.hw_hres = 300; dev->CIS.channel = MUSTEK_PP_CIS_CHANNEL_GREEN; dev->CIS.setParameters = SANE_FALSE; dev->CIS.exposeTime = 0xAA; cis_config_ccd (dev); cis_restore_state(dev); } static SANE_Bool cis_calibrate (Mustek_PP_CIS_dev * dev) { int i, saved_res = dev->CIS.res, saved_vres = dev->CIS.hw_vres; /* * Flow of operation observed from the twain driver * (it is assumed that the lamp is at the origin, and that the CIS is * configured for 300 DPI, ie. cis_reset_device has been called.) * * - Reset the device and return the lamp to its home position * * - Unknown short sequence * * - Send a sawtooth-like pattern to one of the memory banks. * * - Repetitive read_line of 2048 bytes, interleaved with an unknown * command. The number varies between 102 and 170 times, but there * doesn't seem to be any correlation with the current mode of the * scanner, so I assume that the exact number isn't really relevant. * The values that are read are the one that were sent to the bank, * rotated by 1 byte in my case. * * * It seems that the width of the black border is being measured at * this stage, possibly multiple times till it stabilizes. * I assume that the buffer is read 100 times to allow the lamp to * warm up and that the width of the black border is then being * measured till it stabilizes. That would explain the minimum number * of 102 iterations that I've seen. * * - reset the device * * - move the motor 110 steps forward. The TWAIN driver moves 90 steps, * and I've used 90 steps for a long time too, but occasionally, * 90 steps is a fraction to short to reach the start of the * calibration strip (the motor movements are not very accurate; * an offset of 1 mm is not unusual). Therefore, I've increased it to * 110 steps. This gives us an additional 1.6 mm slack, which should * prevent calibration errors. * (Note that the MUSTEK_PP_CIS_????CP_DEFAULT_SKIP constants have to * be adjusted if the number of steps is altered.) * * - configure the CIS : actual resolution + set parameters * */ /* * We must make sure that we are in the scanning state; otherwise we may * still be in the canceled state from a previous scan (even if terminated * normally), and the whole calibration would go wrong. */ dev->desc->state = STATE_SCANNING; cis_reset_device (dev); cis_return_home (dev, SANE_FALSE); /* Wait till it's home */ /* Use maximum resolution during calibration; otherwise we may calibrate past the calibration strip. */ dev->CIS.hw_vres = dev->desc->dev->maxres; /* This field remembers how many steps we still have to go @ max res */ dev->CIS.skipsToOrigin = dev->top_skip; /*max2hw_vres(dev, dev->top_skip); */ if (!cis_measure_delay(dev)) return SANE_FALSE; cis_reset_device (dev); /* Move motor 110 steps @ 300 DPI */ Mustek_PP_1015_write_reg_start(dev, MA1015W_MOTOR_CONTROL); for (i=0; i<110; ++i) { if (dev->model == MUSTEK_PP_CIS600) { Mustek_PP_1015_write_reg_val (dev, 0x73); } else { Mustek_PP_1015_write_reg_val (dev, 0x7B); } cis_wait_motor_stable (dev); } Mustek_PP_1015_write_reg_stop(dev); /* Next, we maximize the dynamic range of the scanner. During calibration we don't want to extrapolate, so we limit the resolution if necessary */ if (dev->CIS.hw_hres < dev->CIS.res) dev->CIS.res = dev->CIS.hw_hres; if (!cis_maximize_dynamic_range(dev)) return SANE_FALSE; if (!cis_normalize_ranges(dev)) return SANE_FALSE; dev->CIS.res = saved_res; dev->CIS.hw_vres = saved_vres; /* Convert steps back to max res size, which are used during skipping */ /* dev->CIS.skipsToOrigin = hw2max_vres(dev, dev->CIS.skipsToOrigin); */ /* Move to the origin */ DBG(3, "cis_calibrate: remaining skips to origin @maxres: %d\n", dev->CIS.skipsToOrigin); cis_move_motor(dev, dev->CIS.skipsToOrigin); if (dev->calib_mode) { /* In calibration mode, we scan the interior of the scanner before the glass plate in order to find the position of the calibration strip and the start of the glass plate. */ DBG(3, "cis_calibrate: running in calibration mode. Returning home.\n"); cis_return_home (dev, SANE_FALSE); /* Wait till it's home */ } return dev->desc->state != STATE_CANCELLED ? SANE_TRUE : SANE_FALSE; } /****************************************************************************** ****************************************************************************** *** Mustek PP interface *** ****************************************************************************** *****************************************************************************/ /****************************************************************************** * Init * ******************************************************************************/ /* Shared initialization routine */ static SANE_Status cis_attach(SANE_String_Const port, SANE_String_Const name, SANE_Attach_Callback attach, SANE_Int driverNo, SANE_Int info) { int fd; SANE_Status status; u_char asic; status = sanei_pa4s2_open (port, &fd); if (status != SANE_STATUS_GOOD) { SANE_Status altStatus; SANE_String_Const altPort; DBG (2, "cis_attach: couldn't attach to `%s' (%s)\n", port, sane_strstatus (status)); /* Make migration to libieee1284 painless for users that used direct io in the past */ if (strcmp(port, "0x378") == 0) altPort = "parport0"; else if (strcmp(port, "0x278") == 0) altPort = "parport1"; else if (strcmp(port, "0x3BC") == 0) altPort = "parport2"; else return status; DBG (2, "cis_attach: trying alternative port name: %s\n", altPort); altStatus = sanei_pa4s2_open (altPort, &fd); if (altStatus != SANE_STATUS_GOOD) { DBG (2, "cis_attach: couldn't attach to alternative port `%s' " "(%s)\n", altPort, sane_strstatus (altStatus)); return status; /* Return original status, not alternative status */ } } M1015_START_LL; M1015_START_HL; sanei_pa4s2_enable (fd, SANE_TRUE); SANEI_PA4S2_READBEGIN (fd, 0); SANEI_PA4S2_READBYTE (fd, &asic); SANEI_PA4S2_READEND (fd); sanei_pa4s2_enable (fd, SANE_FALSE); sanei_pa4s2_close (fd); if (asic != 0xA5) /* Identifies the MA1015 chipset */ { /* CIS driver only works for MA1015 chipset */ DBG (2, "cis_attach: asic id (0x%02x) not recognized\n", asic); return SANE_STATUS_INVAL; } DBG (3, "cis_attach: device %s attached\n", name); DBG (3, "cis_attach: asic 0x%02x\n", asic); return attach(port, name, driverNo, info); } SANE_Status cis600_drv_init(SANE_Int options, SANE_String_Const port, SANE_String_Const name, SANE_Attach_Callback attach) { if (options != CAP_NOTHING) return SANE_STATUS_INVAL; return cis_attach(port, name, attach, MUSTEK_PP_CIS600, MUSTEK_PP_CIS600); } SANE_Status cis1200_drv_init(SANE_Int options, SANE_String_Const port, SANE_String_Const name, SANE_Attach_Callback attach) { if (options != CAP_NOTHING) return SANE_STATUS_INVAL; return cis_attach(port, name, attach, MUSTEK_PP_CIS1200, MUSTEK_PP_CIS1200); } SANE_Status cis1200p_drv_init(SANE_Int options, SANE_String_Const port, SANE_String_Const name, SANE_Attach_Callback attach) { if (options != CAP_NOTHING) return SANE_STATUS_INVAL; return cis_attach(port, name, attach, MUSTEK_PP_CIS1200PLUS, MUSTEK_PP_CIS1200PLUS); } /****************************************************************************** * Capabilities * ******************************************************************************/ void cis_drv_capabilities(SANE_Int info, SANE_String *model, SANE_String *vendor, SANE_String *type, SANE_Int *maxres, SANE_Int *minres, SANE_Int *maxhsize, SANE_Int *maxvsize, SANE_Int *caps) { *vendor = strdup("Mustek"); *type = strdup("flatbed scanner"); *caps = CAP_NOTHING; switch(info) { case MUSTEK_PP_CIS600: *model = strdup("600CP"); *maxres = 600; *minres = 50; *maxhsize = MUSTEK_PP_CIS_MAX_H_PIXEL; *maxvsize = MUSTEK_PP_CIS_MAX_V_PIXEL; break; case MUSTEK_PP_CIS1200: *model = strdup("1200CP"); *maxres = 1200; *minres = 50; *maxhsize = MUSTEK_PP_CIS_MAX_H_PIXEL*2; *maxvsize = MUSTEK_PP_CIS_MAX_V_PIXEL*2; break; case MUSTEK_PP_CIS1200PLUS: *model = strdup("1200CP+"); *maxres = 1200; *minres = 50; *maxhsize = MUSTEK_PP_CIS_MAX_H_PIXEL*2; *maxvsize = MUSTEK_PP_CIS_MAX_V_PIXEL*2; break; } } /****************************************************************************** * Open * ******************************************************************************/ SANE_Status cis_drv_open (SANE_String port, SANE_Int caps, SANE_Int *fd) { SANE_Status status; if (caps != CAP_NOTHING) { DBG (1, "cis_drv_open: called with unknown capabilities (0x%02X)\n", caps); return SANE_STATUS_INVAL; } DBG (3, "cis_drv_open: called for port %s\n", port); status = sanei_pa4s2_open (port, fd); if (status != SANE_STATUS_GOOD) { SANE_Status altStatus; SANE_String_Const altPort; DBG (2, "cis_attach: couldn't attach to `%s' (%s)\n", port, sane_strstatus (status)); /* Make migration to libieee1284 painless for users that used direct io in the past */ if (strcmp(port, "0x378") == 0) altPort = "parport0"; else if (strcmp(port, "0x278") == 0) altPort = "parport1"; else if (strcmp(port, "0x3BC") == 0) altPort = "parport2"; else return status; DBG (2, "cis_attach: trying alternative port name: %s\n", altPort); altStatus = sanei_pa4s2_open (altPort, fd); if (altStatus != SANE_STATUS_GOOD) { DBG (2, "cis_attach: couldn't attach to alternative port `%s' " "(%s)\n", altPort, sane_strstatus (altStatus)); return status; /* Return original status, not alternative status */ } } return SANE_STATUS_GOOD; } /****************************************************************************** * Setup * ******************************************************************************/ void cis_drv_setup (SANE_Handle hndl) { Mustek_pp_Handle *dev = hndl; Mustek_PP_CIS_dev *cisdev; cisdev = (Mustek_PP_CIS_dev*)malloc(sizeof(Mustek_PP_CIS_dev)); if (cisdev == NULL) { DBG (2, "cis_drv_setup: not enough memory for device descriptor\n"); sanei_pa4s2_close (dev->fd); return; } memset(cisdev, 0, sizeof(Mustek_PP_CIS_dev)); DBG(3, "cis_drv_setup: cis device allocated\n"); dev->lamp_on = 0; dev->priv = cisdev; cisdev->desc = dev; cisdev->model = dev->dev->info; cisdev->CIS.hw_hres = 300; cisdev->CIS.cisRes = 300; cisdev->CIS.hw_vres = 300; /* Default values for configurable parameters; configuration file may override them. */ cisdev->fast_skip = SANE_TRUE; cisdev->bw_limit = 127; cisdev->calib_mode = SANE_FALSE; cisdev->engine_delay = 0; if (cisdev->model == MUSTEK_PP_CIS600) { cisdev->top_skip = MUSTEK_PP_CIS_600CP_DEFAULT_SKIP; } else { cisdev->top_skip = MUSTEK_PP_CIS_1200CP_DEFAULT_SKIP; } } /****************************************************************************** * Config * ******************************************************************************/ SANE_Status cis_drv_config(SANE_Handle hndl, SANE_String_Const optname, SANE_String_Const optval) { Mustek_pp_Handle *dev = hndl; Mustek_PP_CIS_dev *cisdev = dev->priv; int value = 0; double dvalue = 0; DBG (3, "cis_drv_cfg option: %s=%s\n", optname, optval ? optval : ""); if (!strcmp(optname, "top_adjust")) { if (!optval) { DBG (1, "cis_drv_config: missing value for option top_adjust\n"); return SANE_STATUS_INVAL; } dvalue = atof(optval); /* An adjustment of +/- 5 mm should be sufficient and safe */ if (dvalue < -5.0) { DBG (1, "cis_drv_config: value for option top_adjust too small: " "%.2f < -5; limiting to -5 mm\n", dvalue); dvalue = -5.0; } if (dvalue > 5.0) { DBG (1, "cis_drv_config: value for option top_adjust too large: " "%.2f > 5; limiting to 5 mm\n", dvalue); dvalue = 5.0; } /* In practice, there is a lower bound on the value that can be used, but if the top_skip value is smaller than that value, the only result will be that the driver tries to move the head a negative number of steps after calibration. The move routine just ignores negative steps, so no harm can be done. */ cisdev->top_skip += MM_TO_PIXEL(dvalue, dev->dev->maxres); DBG (3, "cis_drv_config: setting top skip value to %d\n", cisdev->top_skip); /* Just to be cautious; we don't want the head to hit the bottom */ if (cisdev->top_skip > 600) cisdev->top_skip = 600; if (cisdev->top_skip < -600) cisdev->top_skip = -600; } else if (!strcmp(optname, "slow_skip")) { if (optval) { DBG (1, "cis_drv_config: unexpected value for option slow_skip\n"); return SANE_STATUS_INVAL; } DBG (3, "cis_drv_config: disabling fast skipping\n"); cisdev->fast_skip = SANE_FALSE; } else if (!strcmp(optname, "bw")) { if (!optval) { DBG (1, "cis_drv_config: missing value for option bw\n"); return SANE_STATUS_INVAL; } value = atoi(optval); if (value < 0 || value > 255) { DBG (1, "cis_drv_config: value for option bw out of range: " "%d < 0 or %d > 255\n", value, value); return SANE_STATUS_INVAL; } cisdev->bw_limit = value; } else if (!strcmp(optname, "calibration_mode")) { if (optval) { DBG (1, "cis_drv_config: unexpected value for option calibration_mode\n"); return SANE_STATUS_INVAL; } DBG (3, "cis_drv_config: using calibration mode\n"); cisdev->calib_mode = SANE_TRUE; } else if (!strcmp(optname, "engine_delay")) { if (!optval) { DBG (1, "cis_drv_config: missing value for option engine_delay\n"); return SANE_STATUS_INVAL; } value = atoi(optval); if (value < 0 || value > 100) /* 100 ms is already pretty slow */ { DBG (1, "cis_drv_config: value for option engine_delay out of range: " "%d < 0 or %d > 100\n", value, value); return SANE_STATUS_INVAL; } cisdev->engine_delay = value; } else { DBG (1, "cis_drv_config: unknown options %s\n", optname); return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } /****************************************************************************** * Close * ******************************************************************************/ void cis_drv_close (SANE_Handle hndl) { Mustek_pp_Handle *dev = hndl; Mustek_PP_CIS_dev *cisdev = dev->priv; DBG (3, "cis_close: resetting device.\n"); sanei_pa4s2_enable (dev->fd, SANE_TRUE); cis_reset_device (cisdev); DBG (3, "cis_close: returning home.\n"); cis_return_home (cisdev, SANE_TRUE); /* Don't wait */ DBG (3, "cis_close: disabling fd.\n"); sanei_pa4s2_enable (dev->fd, SANE_FALSE); DBG (3, "cis_close: closing fd.\n"); sanei_pa4s2_close (dev->fd); DBG (3, "cis_close: done.\n"); DBG (6, "cis_close: lamp_on: %d\n", (int)dev->lamp_on); M1015_STOP_LL; M1015_STOP_HL; } /****************************************************************************** * Start * ******************************************************************************/ SANE_Status cis_drv_start (SANE_Handle hndl) { Mustek_pp_Handle *dev = hndl; Mustek_PP_CIS_dev *cisdev = dev->priv; SANE_Int pixels = dev->params.pixels_per_line; if (!cisdev) { DBG (2, "cis_drv_start: not enough memory for device\n"); return SANE_STATUS_NO_MEM; } cisdev->CIS.exposeTime = 0xAA; cisdev->CIS.setParameters = SANE_FALSE; cisdev->CIS.use8KBank = SANE_TRUE; cisdev->CIS.imagebytes = dev->bottomX - dev->topX; cisdev->CIS.skipimagebytes = dev->topX; cisdev->CIS.res = dev->res; DBG (3, "cis_drv_start: %d dpi\n", dev->res); if (dev->res <= 50 && cisdev->model != MUSTEK_PP_CIS1200PLUS) { cisdev->CIS.hw_hres = 50; } else if (dev->res <= 75 && cisdev->model == MUSTEK_PP_CIS1200PLUS) { cisdev->CIS.hw_hres = 75; } else if (dev->res <= 100) { cisdev->CIS.hw_hres = 100; } else if (dev->res <= 200) { cisdev->CIS.hw_hres = 200; } else if (dev->res <= 300) { cisdev->CIS.hw_hres = 300; } else { if (cisdev->model == MUSTEK_PP_CIS600) { cisdev->CIS.hw_hres = 300; /* Limit for 600 CP */ } else if (dev->res <= 400) { cisdev->CIS.hw_hres = 400; } else { cisdev->CIS.hw_hres = 600; /* Limit for 1200 CP/CP+ */ } } if (cisdev->model == MUSTEK_PP_CIS600) { if (dev->res <= 150) { cisdev->CIS.hw_vres = 150; } else if (dev->res <= 300) { cisdev->CIS.hw_vres = 300; } else { cisdev->CIS.hw_vres = 600; } } else { if (dev->res <= 300) { cisdev->CIS.hw_vres = 300; } else if (dev->res <= 600) { cisdev->CIS.hw_vres = 600; } else { cisdev->CIS.hw_vres = 1200; } } if (cisdev->model == MUSTEK_PP_CIS600 || (cisdev->model == MUSTEK_PP_CIS1200 && dev->res <= 300)) cisdev->CIS.cisRes = 300; else cisdev->CIS.cisRes = 600; /* Calibration only makes sense for hardware pixels, not for interpolated pixels, so we limit the number of calibration pixels to the maximum number of hardware pixels corresponding to the selected area */ if (dev->res > cisdev->CIS.hw_hres) cisdev->calib_pixels = (pixels * cisdev->CIS.hw_hres) / dev->res; else cisdev->calib_pixels = pixels; DBG (3, "cis_drv_start: hres: %d vres: %d cisres: %d\n", cisdev->CIS.hw_hres, cisdev->CIS.hw_vres, cisdev->CIS.cisRes); sanei_pa4s2_enable (dev->fd, SANE_TRUE); cis_reset_device (cisdev); cis_return_home (cisdev, SANE_TRUE); /* Don't wait here */ #ifdef M1015_TRACE_REGS { int i, j; /* * Set all registers to -1 (uninitialized) */ for (i=0; i<4; ++i) { cisdev->CIS.regs.in_regs[i] = -1; for (j=0; j<4; ++j) { cisdev->CIS.regs.out_regs[i][j] = -1; } } cisdev->CIS.regs.channel = -1; /* These values have been read earlier. */ cisdev->CIS.regs.in_regs[0] = 0xA5; } #endif cis_reset_device (cisdev); cis_return_home (cisdev, SANE_TRUE); /* no wait */ /* Allocate memory for temporary color buffer */ cisdev->tmpbuf = malloc (pixels); if (cisdev->tmpbuf == NULL) { sanei_pa4s2_enable (dev->fd, SANE_FALSE); DBG (2, "cis_drv_start: not enough memory for temporary buffer\n"); free(cisdev); dev->priv = NULL; return SANE_STATUS_NO_MEM; } /* Allocate memory for calibration; calibrating interpolated pixels makes no sense */ if (pixels > (dev->dev->maxhsize >> 1)) pixels = (dev->dev->maxhsize >> 1); cisdev->calib_low[1] = malloc (pixels); cisdev->calib_hi[1] = malloc (pixels); if (cisdev->calib_low[1] == NULL || cisdev->calib_hi[1] == NULL) { free (cisdev->calib_low[1]); cisdev->calib_low[1] = NULL; free (cisdev->calib_hi[1]); cisdev->calib_hi[1] = NULL; sanei_pa4s2_enable (dev->fd, SANE_FALSE); DBG (2, "cis_drv_start: not enough memory for calibration buffer\n"); free(cisdev->tmpbuf); cisdev->tmpbuf = NULL; free(cisdev); dev->priv = NULL; return SANE_STATUS_NO_MEM; } cisdev->calib_low[0] = NULL; cisdev->calib_low[2] = NULL; cisdev->calib_hi[0] = NULL; cisdev->calib_hi[2] = NULL; if (dev->mode == MODE_COLOR) { cisdev->calib_low[0] = malloc (pixels); cisdev->calib_low[2] = malloc (pixels); cisdev->calib_hi[0] = malloc (pixels); cisdev->calib_hi[2] = malloc (pixels); if ((cisdev->calib_low[0] == NULL) || (cisdev->calib_low[2] == NULL) || (cisdev->calib_hi[0] == NULL) || (cisdev->calib_hi[2] == NULL)) { free (cisdev->calib_low[0]); cisdev->calib_low[0] = NULL; free (cisdev->calib_low[1]); cisdev->calib_low[1] = NULL; free (cisdev->calib_low[2]); cisdev->calib_low[2] = NULL; free (cisdev->calib_hi[0]); cisdev->calib_hi[0] = NULL; free (cisdev->calib_hi[1]); cisdev->calib_hi[1] = NULL; free (cisdev->calib_hi[2]); cisdev->calib_hi[2] = NULL; free(cisdev->tmpbuf); cisdev->tmpbuf = NULL; free(cisdev); dev->priv = NULL; sanei_pa4s2_enable (dev->fd, SANE_FALSE); DBG (2, "cis_drv_start: not enough memory for color calib buffer\n"); return SANE_STATUS_NO_MEM; } } DBG (3, "cis_drv_start: executing calibration\n"); if (!cis_calibrate (cisdev)) { free (cisdev->calib_low[0]); cisdev->calib_low[0] = NULL; free (cisdev->calib_low[1]); cisdev->calib_low[1] = NULL; free (cisdev->calib_low[2]); cisdev->calib_low[2] = NULL; free (cisdev->calib_hi[0]); cisdev->calib_hi[0] = NULL; free (cisdev->calib_hi[1]); cisdev->calib_hi[1] = NULL; free (cisdev->calib_hi[2]); cisdev->calib_hi[2] = NULL; free(cisdev->tmpbuf); cisdev->tmpbuf = NULL; free(cisdev); dev->priv = NULL; return SANE_STATUS_CANCELLED; /* Most likely cause */ } /* M1015_DISPLAY_REGS(dev, "after calibration"); */ cis_get_bank_count(cisdev); cis_move_motor (cisdev, dev->topY); /* Measured in max resolution */ /* It is vital to reinitialize the scanner right before we start the real scanning. Otherwise the bank synchronization may have gotten lost by the time we reach the top of the scan area */ cisdev->CIS.setParameters = SANE_TRUE; cis_config_ccd(cisdev); cis_wait_read_ready(cisdev); sanei_pa4s2_enable (dev->fd, SANE_FALSE); cisdev->CIS.line_step = SANE_FIX ((float) cisdev->CIS.hw_vres / (float) cisdev->CIS.res); /* * It is very important that line_diff is not initialized at zero ! * If it is set to zero, the motor will keep on moving forever (or better, * till the scanner breaks). */ cisdev->line_diff = cisdev->CIS.line_step; cisdev->ccd_line = 0; cisdev->line = 0; cisdev->lines_left = dev->params.lines; dev->state = STATE_SCANNING; DBG (3, "cis_drv_start: device ready for scanning\n"); return SANE_STATUS_GOOD; } /****************************************************************************** * Read * ******************************************************************************/ void cis_drv_read (SANE_Handle hndl, SANE_Byte *buffer) { Mustek_pp_Handle *dev = hndl; Mustek_PP_CIS_dev *cisdev = dev->priv; DBG(6, "cis_drv_read: Reading line\n"); sanei_pa4s2_enable (dev->fd, SANE_TRUE); switch (dev->mode) { case MODE_BW: cis_get_lineart_line(cisdev, buffer); break; case MODE_GRAYSCALE: cis_get_grayscale_line(cisdev, buffer); break; case MODE_COLOR: cis_get_color_line(cisdev, buffer); break; } sanei_pa4s2_enable (dev->fd, SANE_FALSE); } /****************************************************************************** * Stop * ******************************************************************************/ void cis_drv_stop (SANE_Handle hndl) { Mustek_pp_Handle *dev = hndl; Mustek_PP_CIS_dev *cisdev = dev->priv; /* device is scanning: return lamp and free buffers */ DBG (3, "cis_drv_stop: stopping current scan\n"); dev->state = STATE_CANCELLED; DBG (9, "cis_drv_stop: enabling fd\n"); sanei_pa4s2_enable (dev->fd, SANE_TRUE); Mustek_PP_1015_write_reg(cisdev, MA1015W_MOTOR_CONTROL, 0); /* stop */ DBG (9, "cis_drv_stop: resetting device (1)\n"); cis_reset_device (cisdev); DBG (9, "cis_drv_stop: returning home\n"); cis_return_home (cisdev, SANE_TRUE); /* don't wait */ DBG (9, "cis_drv_stop: resetting device (2)\n"); cis_reset_device (cisdev); DBG (9, "cis_drv_stop: disabling fd\n"); sanei_pa4s2_enable (dev->fd, SANE_FALSE); DBG (9, "cis_drv_stop: freeing buffers\n"); /* This is no good: canceling while the device is scanning and freeing the data buffers can result in illegal memory accesses if the device is still scanning in another thread. */ free (cisdev->calib_low[1]); cisdev->calib_low[1] = NULL; free (cisdev->calib_hi[1]); cisdev->calib_hi[1] = NULL; free (cisdev->tmpbuf); cisdev->tmpbuf = NULL; DBG (3, "cis_drv_stop: freed green and temporary buffers\n"); if (cisdev->CIS.mode == MODE_COLOR) { free (cisdev->calib_low[0]); cisdev->calib_low[0] = NULL; free (cisdev->calib_low[2]); cisdev->calib_low[2] = NULL; free (cisdev->calib_hi[0]); cisdev->calib_hi[0] = NULL; free (cisdev->calib_hi[2]); cisdev->calib_hi[2] = NULL; } DBG (3, "cis_drv_stop: freed buffers\n"); DBG (6, "cis_drv_stop: lamp_on: %d\n", (int)dev->lamp_on); } backends-1.3.0/backend/mustek_pp_cis.h000066400000000000000000000226401456256263500177140ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. 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 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, see . As a special exception, the authors of SANE give permission for additional uses of the libraries contained in this release of SANE. The exception is that, if you link a SANE library with other files to produce an executable, this does not by itself cause the resulting executable to be covered by the GNU General Public License. Your use of that executable is in no way restricted on account of linking the SANE library code into it. This exception does not, however, invalidate any other reasons why the executable file might be covered by the GNU General Public License. If you submit changes to SANE to the maintainers to be included in a subsequent release, you agree by submitting the changes that those changes may be distributed with this exception intact. If you write modifications of your own for SANE, it is your choice whether to permit this exception to apply to your modifications. If you do not wish that, delete this exception notice. This file implements a SANE backend for Mustek PP flatbed _CIS_ scanners. */ #ifndef mustek_pp_cis_h #define mustek_pp_cis_h #include "../include/sane/sane.h" /****************************************************************************** * Read register symbols. *****************************************************************************/ typedef enum { MA1015R_ASIC = 0x00, MA1015R_SCAN_VAL = 0x01, MA1015R_MOTOR = 0x02, MA1015R_BANK_COUNT = 0x03 } Mustek_PP_1015R_reg; /****************************************************************************** * Read register bitmask symbols. *****************************************************************************/ typedef enum { MA1015B_MOTOR_HOME = 0x01, MA1015B_MOTOR_STABLE = 0x03 } Mustek_PP_1015R_bit; /****************************************************************************** * Write register symbols: (bank number << 4) + register number. *****************************************************************************/ typedef enum { MA1015W_RED_REF = 0x00, MA1015W_GREEN_REF = 0x01, MA1015W_BLUE_REF = 0x02, MA1015W_DPI_CONTROL = 0x03, MA1015W_BYTE_COUNT_HB = 0x10, MA1015W_BYTE_COUNT_LB = 0x11, MA1015W_SKIP_COUNT = 0x12, MA1015W_EXPOSE_TIME = 0x13, MA1015W_SRAM_SOURCE_PC = 0x20, MA1015W_MOTOR_CONTROL = 0x21, MA1015W_UNKNOWN_42 = 0x22, MA1015W_UNKNOWN_82 = 0x23, MA1015W_POWER_ON_DELAY = 0x30, MA1015W_CCD_TIMING = 0x31, MA1015W_CCD_TIMING_ADJ = 0x32, MA1015W_RIGHT_BOUND = 0x33 } Mustek_PP_1015W_reg; /****************************************************************************** * Mustek MA1015 register tracing structure. * Can be used to trace the status of the registers of the MA1015 chipset * during debugging. Most fields are not used in production code. *****************************************************************************/ typedef struct Mustek_PP_1015_Registers { SANE_Byte in_regs[4]; SANE_Byte out_regs[4][4]; SANE_Byte channel; Mustek_PP_1015R_reg current_read_reg; SANE_Int read_count; Mustek_PP_1015W_reg current_write_reg; /* always used */ SANE_Int write_count; } Mustek_PP_1015_Registers; /****************************************************************************** * CIS information *****************************************************************************/ typedef struct Mustek_PP_CIS_Info { /* Expose time (= time the lamp is on ?) */ SANE_Byte exposeTime; /* Power-on delay (= time between lamp on and start of capturing ?) */ SANE_Byte powerOnDelay[3]; /* Motor step control */ SANE_Byte phaseType; /* Use 8K bank or 4K bank */ SANE_Bool use8KBank; /* High resolution (600 DPI) or not (300 DPI) */ SANE_Bool highRes; /* delay between pixels; reading too fast causes stability problems */ SANE_Int delay; /* Register representation */ Mustek_PP_1015_Registers regs; /* Current color channel */ SANE_Int channel; /* Blocks motor movements during calibration */ SANE_Bool dontMove; /* Prevents read increment the before the first read */ SANE_Bool dontIncRead; /* Controls whether or not calibration parameters are transmitted during CIS configuration */ SANE_Bool setParameters; /* Number of lines to skip to reach the origin (used during calibration) */ SANE_Int skipsToOrigin; /* Physical resolution of the CIS: either 300 or 600 DPI */ SANE_Int cisRes; /* CCD mode (color/grayscale/lineart) */ SANE_Int mode; /* how many positions to skip until scan area starts @ max res */ SANE_Int skipimagebytes; /* how many image bytes to scan @ max res */ SANE_Int imagebytes; /* total skip, adjusted to resolution */ SANE_Int adjustskip; /* current resolution */ SANE_Int res; /* current horizontal hardware resolution */ SANE_Int hw_hres; /* current vertical hardware resolution */ SANE_Int hw_vres; /* how many positions to scan for one pixel */ SANE_Int hres_step; /* how many lines to scan for one scanline */ SANE_Int line_step; /* inversion */ SANE_Bool invert; } Mustek_PP_CIS_Info; struct Mustek_pp_Handle; typedef struct Mustek_PP_CIS_dev { /* device descriptor */ struct Mustek_pp_Handle *desc; /* model identification (600CP/1200CP/1200CP+) */ SANE_Int model; /* CIS status */ Mustek_PP_CIS_Info CIS; /* used during calibration & return_home */ Mustek_PP_CIS_Info Saved_CIS; /* bank count */ int bank_count; /* those are used to count the hardware line the scanner is at, the line the current bank is at and the lines we've scanned */ int line; int line_diff; int ccd_line; int lines_left; /* Configuration parameters that the user can calibrate */ /* Starting position at the top */ SANE_Int top_skip; /* Use fast skipping method for head movements ? (default: yes) */ SANE_Bool fast_skip; /* Discrimination value to choose between black and white */ SANE_Byte bw_limit; /* Run in calibration mode ? (default: no) */ SANE_Bool calib_mode; /* Extra delay between engine commands (ms). Default: zero. */ SANE_Int engine_delay; /* temporary buffer for 1 line (of one color) */ SANE_Byte *tmpbuf; /* calibration buffers (low cut, high cut) */ SANE_Byte *calib_low[3]; SANE_Byte *calib_hi[3]; /* Number of pixels in calibration buffers (<= number of pixels to scan) */ int calib_pixels; } Mustek_PP_CIS_dev; #define CIS_AVERAGE_NONE(dev) Mustek_PP_1015_send_command(dev, 0x05) #define CIS_AVERAGE_TWOPIXEL(dev) Mustek_PP_1015_send_command(dev, 0x15) #define CIS_AVERAGE_THREEPIXEL(dev) Mustek_PP_1015_send_command(dev, 0x35) #define CIS_WIDTH_4K(dev) Mustek_PP_1015_send_command(dev, 0x05) #define CIS_WIDTH_8K(dev) Mustek_PP_1015_send_command(dev, 0x45) #define CIS_STOP_TOGGLE(dev) Mustek_PP_1015_send_command(dev, 0x85) #define CIS_PIP_AS_INPUT(dev) Mustek_PP_1015_send_command(dev, 0x46) #define CIS_PIP_AS_OUTPUT_0(dev) Mustek_PP_1015_send_command(dev, 0x06) #define CIS_PIP_AS_OUTPUT_1(dev) Mustek_PP_1015_send_command(dev, 0x16) #define CIS_POP_AS_INPUT(dev) Mustek_PP_1015_send_command(dev, 0x86) #define CIS_POP_AS_OUTPUT_0(dev) Mustek_PP_1015_send_command(dev, 0x06) #define CIS_POP_AS_OUTPUT_1(dev) Mustek_PP_1015_send_command(dev, 0x26) #define CIS_INC_READ(dev) Mustek_PP_1015_send_command(dev, 0x07) #define CIS_CLEAR_WRITE_BANK(dev) Mustek_PP_1015_send_command(dev, 0x17) #define CIS_CLEAR_READ_BANK(dev) Mustek_PP_1015_send_command(dev, 0x27) #define CIS_CLEAR_FULLFLAG(dev) Mustek_PP_1015_send_command(dev, 0x37) #define CIS_POWER_ON(dev) Mustek_PP_1015_send_command(dev, 0x47) #define CIS_POWER_OFF(dev) Mustek_PP_1015_send_command(dev, 0x57) #define CIS_CLEAR_WRITE_ADDR(dev) Mustek_PP_1015_send_command(dev, 0x67) #define CIS_CLEAR_TOGGLE(dev) Mustek_PP_1015_send_command(dev, 0x77) #define CIS_NO(dev) Mustek_PP_1015_send_command(dev, 0x08) #define CIS_OST_POS(dev) Mustek_PP_1015_send_command(dev, 0x18) #define CIS_OST_TYP(dev) Mustek_PP_1015_send_command(dev, 0x28) #define CIS_OP_MOD_0(dev) Mustek_PP_1015_send_command(dev, 0x48) #define CIS_OP_MOD_1(dev) Mustek_PP_1015_send_command(dev, 0x88) #endif /* __mustek_pp_cis_h */ backends-1.3.0/backend/mustek_pp_decl.h000066400000000000000000000071651456256263500200520ustar00rootroot00000000000000 #ifndef MUSTEK_PP_DECL_H #define MUSTEK_PP_DECL_H /* debug driver, version 0.11-devel, author Jochen Eisinger */ static SANE_Status debug_drv_init (SANE_Int options, SANE_String_Const port, SANE_String_Const name, SANE_Attach_Callback attach); static void debug_drv_capabilities (SANE_Int info, SANE_String *model, SANE_String *vendor, SANE_String *type, SANE_Int *maxres, SANE_Int *minres, SANE_Int *maxhsize, SANE_Int *maxvsize, SANE_Int *caps); static SANE_Status debug_drv_open (SANE_String port, SANE_Int caps, SANE_Int *fd); static void debug_drv_setup (SANE_Handle hndl); static SANE_Status debug_drv_config (SANE_Handle hndl, SANE_String_Const optname, SANE_String_Const optval); static void debug_drv_close (SANE_Handle hndl); static SANE_Status debug_drv_start (SANE_Handle hndl); static void debug_drv_read (SANE_Handle hndl, SANE_Byte *buffer); static void debug_drv_stop (SANE_Handle hndl); /* CIS drivers for 600CP, 1200CP, and 1200CP+ Version 0.13-beta, author Eddy De Greef */ static SANE_Status cis600_drv_init (SANE_Int options, SANE_String_Const port, SANE_String_Const name, SANE_Attach_Callback attach); static SANE_Status cis1200_drv_init (SANE_Int options, SANE_String_Const port, SANE_String_Const name, SANE_Attach_Callback attach); static SANE_Status cis1200p_drv_init(SANE_Int options, SANE_String_Const port, SANE_String_Const name, SANE_Attach_Callback attach); static void cis_drv_capabilities(SANE_Int info, SANE_String *model, SANE_String *vendor, SANE_String *type, SANE_Int *maxres, SANE_Int *minres, SANE_Int *maxhsize, SANE_Int *maxvsize, SANE_Int *caps); static SANE_Status cis_drv_open (SANE_String port, SANE_Int caps, SANE_Int *fd); static void cis_drv_setup (SANE_Handle hndl); static SANE_Status cis_drv_config (SANE_Handle hndl, SANE_String_Const optname, SANE_String_Const optval); static void cis_drv_close (SANE_Handle hndl); static SANE_Status cis_drv_start (SANE_Handle hndl); static void cis_drv_read (SANE_Handle hndl, SANE_Byte *buffer); static void cis_drv_stop (SANE_Handle hndl); /* CCD drivers for 300 dpi models Version 0.11-devel, author Jochen Eisinger */ static SANE_Status ccd300_init (SANE_Int options, SANE_String_Const port, SANE_String_Const name, SANE_Attach_Callback attach); static void ccd300_capabilities(SANE_Int info, SANE_String *model, SANE_String *vendor, SANE_String *type, SANE_Int *maxres, SANE_Int *minres, SANE_Int *maxhsize, SANE_Int *maxvsize, SANE_Int *caps); static SANE_Status ccd300_open (SANE_String port, SANE_Int caps, SANE_Int *fd); static void ccd300_setup (SANE_Handle hndl); static SANE_Status ccd300_config (SANE_Handle hndl, SANE_String_Const optname, SANE_String_Const optval); static void ccd300_close (SANE_Handle hndl); static SANE_Status ccd300_start (SANE_Handle hndl); static void ccd300_read (SANE_Handle hndl, SANE_Byte *buffer); static void ccd300_stop (SANE_Handle hndl); #endif backends-1.3.0/backend/mustek_pp_drivers.h000066400000000000000000000023121456256263500206060ustar00rootroot00000000000000 #ifndef MUSTEK_PP_DRIVERS_H #define MUSTEK_PP_DRIVERS_H #include "mustek_pp.h" #include "mustek_pp_decl.h" static Mustek_pp_Functions Mustek_pp_Drivers[] = { { "debug", "Jochen Eisinger", "0.11-devel", debug_drv_init, debug_drv_capabilities, debug_drv_open, debug_drv_setup, debug_drv_config, debug_drv_close, debug_drv_start, debug_drv_read, debug_drv_stop }, { "cis600", "Eddy De Greef", "0.13-beta", cis600_drv_init, cis_drv_capabilities, cis_drv_open, cis_drv_setup, cis_drv_config, cis_drv_close, cis_drv_start, cis_drv_read, cis_drv_stop }, { "cis1200", "Eddy De Greef", "0.13-beta", cis1200_drv_init, cis_drv_capabilities, cis_drv_open, cis_drv_setup, cis_drv_config, cis_drv_close, cis_drv_start, cis_drv_read, cis_drv_stop }, { "cis1200+", "Eddy De Greef", "0.13-beta", cis1200p_drv_init, cis_drv_capabilities, cis_drv_open, cis_drv_setup, cis_drv_config, cis_drv_close, cis_drv_start, cis_drv_read, cis_drv_stop }, { "ccd300", "Jochen Eisinger", "0.11-devel", ccd300_init, ccd300_capabilities, ccd300_open, ccd300_setup, ccd300_config, ccd300_close, ccd300_start, ccd300_read, ccd300_stop } }; #endif backends-1.3.0/backend/mustek_pp_null.c000066400000000000000000000076761456256263500201170ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2000-2003 Jochen Eisinger This file is part of the SANE package. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . As a special exception, the authors of SANE give permission for additional uses of the libraries contained in this release of SANE. The exception is that, if you link a SANE library with other files to produce an executable, this does not by itself cause the resulting executable to be covered by the GNU General Public License. Your use of that executable is in no way restricted on account of linking the SANE library code into it. This exception does not, however, invalidate any other reasons why the executable file might be covered by the GNU General Public License. If you submit changes to SANE to the maintainers to be included in a subsequent release, you agree by submitting the changes that those changes may be distributed with this exception intact. If you write modifications of your own for SANE, it is your choice whether to permit this exception to apply to your modifications. If you do not wish that, delete this exception notice. This file implements a SANE backend for Mustek PP flatbed scanners. */ #include "../include/sane/config.h" #if defined(HAVE_STDLIB_H) # include #endif #include #include #if defined(HAVE_STRING_H) # include #elif defined(HAVE_STRINGS_H) # include #endif #define DEBUG_DECLARE_ONLY #include "mustek_pp.h" #include "mustek_pp_decl.h" #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #define MUSTEK_PP_NULL_DRIVER 0 static SANE_Status debug_drv_init(SANE_Int options, SANE_String_Const port, SANE_String_Const name, SANE_Attach_Callback attach) { if (options != CAP_NOTHING) return SANE_STATUS_INVAL; return attach(port, name, MUSTEK_PP_NULL_DRIVER, 0); } /*ARGSUSED*/ static void debug_drv_capabilities(SANE_Int info __UNUSED__, SANE_String *model, SANE_String *vendor, SANE_String *type, SANE_Int *maxres, SANE_Int *minres, SANE_Int *maxhsize, SANE_Int *maxvsize, SANE_Int *caps) { *model = strdup("debugger"); *vendor = strdup("mustek_pp"); *type = strdup("software emulated"); *maxres = 300; *minres = 50; *maxhsize = 1000; *maxvsize = 3000; *caps = CAP_NOTHING; } /*ARGSUSED*/ static SANE_Status debug_drv_open (SANE_String port __UNUSED__, SANE_Int caps __UNUSED__, SANE_Int *fd) { *fd = 1; return SANE_STATUS_GOOD; } static void debug_drv_setup (SANE_Handle hndl) { Mustek_pp_Handle *dev = hndl; dev->lamp_on = 0; dev->priv = NULL; } /*ARGSUSED*/ static SANE_Status debug_drv_config(SANE_Handle hndl __UNUSED__, SANE_String_Const optname, SANE_String_Const optval) { DBG (3, "debug_drv cfg option: %s=%s\n", optname, optval ? optval : ""); return SANE_STATUS_GOOD; } /*ARGSUSED*/ static void debug_drv_close (SANE_Handle hndl __UNUSED__) { } /*ARGSUSED*/ static SANE_Status debug_drv_start (SANE_Handle hndl __UNUSED__) { return SANE_STATUS_GOOD; } static void debug_drv_read (SANE_Handle hndl, SANE_Byte *buffer) { Mustek_pp_Handle *dev = hndl; memset (buffer, 0, dev->params.bytes_per_line); } /*ARGSUSED*/ static void debug_drv_stop (SANE_Handle hndl __UNUSED__) { } backends-1.3.0/backend/mustek_scsi_pp.c000066400000000000000000000662001456256263500200720ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2003 James Perry 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, see . 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 the Mustek SCSI-over-parallel port protocol used by, for example, the Paragon 600 II EP */ /**************************************************************************/ #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_debug.h" #include "../include/sane/sanei_pa4s2.h" /* * Number of times to retry sending a SCSI command before giving up */ #define MUSTEK_SCSI_PP_NUM_RETRIES 4 /* * Internal middle-level API functionality */ static int mustek_scsi_pp_timeout = 5000; /* FIXME: use same method as mustek.c ? */ static int mustek_scsi_pp_get_time () { struct timeval tv; int retval; gettimeofday (&tv, 0); retval = tv.tv_sec * 1000 + tv.tv_usec / 1000; return retval; } static u_char mustek_scsi_pp_register = 0; static SANE_Status mustek_scsi_pp_select_register (int fd, u_char reg) { DBG (5, "mustek_scsi_pp_select_register: selecting register %d on fd %d\n", reg, fd); mustek_scsi_pp_register = reg; return sanei_pa4s2_scsi_pp_reg_select (fd, reg); } static SANE_Status mustek_scsi_pp_wait_for_valid_status (int fd) { int start_time; u_char status; DBG (5, "mustek_scsi_pp_wait_for_valid_status: entering\n"); start_time = mustek_scsi_pp_get_time (); do { if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_wait_for_valid_status: I/O error while getting status\n"); return SANE_STATUS_IO_ERROR; } status &= 0xf0; if ((status != 0xf0) && (!(status & 0x40)) && (status & 0x20)) { DBG (5, "mustek_scsi_pp_wait_for_valid_status: returning success\n"); return SANE_STATUS_GOOD; } } while ((mustek_scsi_pp_get_time () - start_time) < mustek_scsi_pp_timeout); DBG (2, "mustek_scsi_pp_wait_for_valid_status: timed out\n"); return SANE_STATUS_DEVICE_BUSY; } static SANE_Status mustek_scsi_pp_wait_for_status_bit_5_set (int fd) { int t; u_char status; DBG (5, "mustek_scsi_pp_wait_for_status_bit_5_set: entering\n"); t = mustek_scsi_pp_get_time (); do { if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_wait_for_status_bit_5_set: I/O error\n"); return SANE_STATUS_IO_ERROR; } if (status & 0x20) { DBG (5, "mustek_scsi_pp_wait_for_status_bit_5_set: returning success\n"); return SANE_STATUS_GOOD; } } while ((mustek_scsi_pp_get_time () - t) < mustek_scsi_pp_timeout); DBG (2, "mustek_scsi_pp_wait_for_status_bit_5_set: timed out\n"); return SANE_STATUS_DEVICE_BUSY; } static SANE_Status mustek_scsi_pp_wait_for_status_bit_5_clear (int fd) { int t; u_char status; DBG (5, "mustek_scsi_pp_wait_for_status_bit_5_clear: entering\n"); t = mustek_scsi_pp_get_time (); do { if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_wait_for_status_bit_5_clear: I/O error\n"); return SANE_STATUS_IO_ERROR; } if (!(status & 0x20)) { DBG (5, "mustek_scsi_pp_wait_for_status_bit_5_clear: returning success\n"); return SANE_STATUS_GOOD; } } while ((mustek_scsi_pp_get_time () - t) < mustek_scsi_pp_timeout); DBG (2, "mustek_scsi_pp_wait_for_status_bit_5_clear: timed out\n"); return SANE_STATUS_DEVICE_BUSY; } static SANE_Status mustek_scsi_pp_wait_for_status_bit_7_set (int fd) { int t; u_char status; DBG (5, "mustek_scsi_pp_wait_for_status_bit_7_set: entering\n"); t = mustek_scsi_pp_get_time (); do { if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_wait_for_status_bit_7_set: I/O error\n"); return SANE_STATUS_IO_ERROR; } if (status & 0x80) { DBG (5, "mustek_scsi_pp_wait_for_status_bit_7_set: returning success\n"); return SANE_STATUS_GOOD; } } while ((mustek_scsi_pp_get_time () - t) < mustek_scsi_pp_timeout); mustek_scsi_pp_select_register (fd, 0); DBG (2, "mustek_scsi_pp_wait_for_status_bit_7_set: timed out\n"); return SANE_STATUS_DEVICE_BUSY; } static SANE_Status mustek_scsi_pp_wait_for_status_bit_7_clear (int fd) { int t; u_char status; DBG (5, "mustek_scsi_pp_wait_for_status_bit_7_clear: entering\n"); t = mustek_scsi_pp_get_time (); do { if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_wait_for_status_bit_7_clear: I/O error\n"); return SANE_STATUS_IO_ERROR; } if (!(status & 0x80)) { DBG (5, "mustek_scsi_pp_wait_for_status_bit_7_clear: returning success\n"); return SANE_STATUS_GOOD; } } while ((mustek_scsi_pp_get_time () - t) < mustek_scsi_pp_timeout); mustek_scsi_pp_select_register (fd, 0); DBG (2, "mustek_scsi_pp_wait_for_status_bit_7_clear: timed out\n"); return SANE_STATUS_DEVICE_BUSY; } static SANE_Status mustek_scsi_pp_wait_for_status_bit_4_set (int fd) { int t; u_char status; DBG (5, "mustek_scsi_pp_wait_for_status_bit_4_set: entering\n"); if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_wait_for_status_bit_4_set: I/O error\n"); return SANE_STATUS_IO_ERROR; } if (status & 0x10) { DBG (5, "mustek_scsi_pp_wait_for_status_bit_4_set: returning success\n"); return SANE_STATUS_GOOD; } t = mustek_scsi_pp_get_time (); do { if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_wait_for_status_bit_4_set: I/O error\n"); return SANE_STATUS_IO_ERROR; } if (status & 0x40) { DBG (2, "mustek_scsi_pp_wait_for_status_bit_4_set: bit 6 set\n"); return SANE_STATUS_IO_ERROR; } if (status & 0x10) { DBG (5, "mustek_scsi_pp_wait_for_status_bit_4_set: returning success\n"); return SANE_STATUS_GOOD; } } while ((mustek_scsi_pp_get_time () - t) < mustek_scsi_pp_timeout); DBG (2, "mustek_scsi_pp_wait_for_status_bit_4_set: timed out\n"); return SANE_STATUS_DEVICE_BUSY; } static SANE_Status mustek_scsi_pp_wait_for_status_bit_4_clear (int fd) { int t; u_char status; DBG (5, "mustek_scsi_pp_wait_for_status_bit_4_clear: entering\n"); if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_wait_for_status_bit_4_clear: I/O error\n"); return SANE_STATUS_IO_ERROR; } if (!(status & 0x10)) { DBG (5, "mustek_scsi_pp_wait_for_status_bit_4_clear: returning success\n"); return SANE_STATUS_GOOD; } t = mustek_scsi_pp_get_time (); do { if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_wait_for_status_bit_4_clear: I/O error\n"); return SANE_STATUS_IO_ERROR; } if (status & 0x40) { DBG (2, "mustek_scsi_pp_wait_for_status_bit_4_clear: bit 6 set\n"); return SANE_STATUS_IO_ERROR; } if (!(status & 0x10)) { DBG (5, "mustek_scsi_pp_wait_for_status_bit_4_clear: returning success\n"); return SANE_STATUS_GOOD; } } while ((mustek_scsi_pp_get_time () - t) < mustek_scsi_pp_timeout); DBG (2, "mustek_scsi_pp_wait_for_status_bit_4_clear: timed out\n"); return SANE_STATUS_DEVICE_BUSY; } static u_char mustek_scsi_pp_bit_4_state = 0; static SANE_Status mustek_scsi_pp_wait_for_status_bit_4_toggle (int fd) { SANE_Status result; DBG (5, "mustek_scsi_pp_wait_for_status_bit_4_toggle: entering\n"); mustek_scsi_pp_bit_4_state ^= 0xff; if (mustek_scsi_pp_bit_4_state) { DBG (5, "mustek_scsi_pp_wait_for_status_bit_4_toggle: waiting for set\n"); result = mustek_scsi_pp_wait_for_status_bit_4_set (fd); mustek_scsi_pp_timeout = 5000; } else { DBG (5, "mustek_scsi_pp_wait_for_status_bit_4_toggle: waiting for clear\n"); result = mustek_scsi_pp_wait_for_status_bit_4_clear (fd); } return result; } static SANE_Status mustek_scsi_pp_send_command_byte (int fd, u_char cmd) { DBG (5, "mustek_scsi_pp_send_command byte: sending 0x%02X\n", cmd); mustek_scsi_pp_select_register (fd, 0); if (mustek_scsi_pp_wait_for_status_bit_7_clear (fd) != SANE_STATUS_GOOD) { mustek_scsi_pp_select_register (fd, 0); return SANE_STATUS_IO_ERROR; } if (sanei_pa4s2_writebyte (fd, mustek_scsi_pp_register, cmd) != SANE_STATUS_GOOD) { return SANE_STATUS_IO_ERROR; } mustek_scsi_pp_select_register (fd, 1); if (mustek_scsi_pp_wait_for_status_bit_7_set (fd) != SANE_STATUS_GOOD) { mustek_scsi_pp_select_register (fd, 0); return SANE_STATUS_IO_ERROR; } mustek_scsi_pp_select_register (fd, 0); DBG (5, "mustek_scsi_pp_send_command_byte: returning success\n"); return SANE_STATUS_GOOD; } static u_char mustek_scsi_pp_read_response (int fd) { u_char result; DBG (5, "mustek_scsi_pp_read_response: entering\n"); if (mustek_scsi_pp_wait_for_status_bit_7_set (fd) != SANE_STATUS_GOOD) { mustek_scsi_pp_select_register (fd, 0); return 0xff; } if (sanei_pa4s2_readbegin (fd, mustek_scsi_pp_register) != SANE_STATUS_GOOD) { return 0xff; } if (sanei_pa4s2_readbyte (fd, &result) != SANE_STATUS_GOOD) { return 0xff; } if (sanei_pa4s2_readend (fd) != SANE_STATUS_GOOD) { return 0xff; } mustek_scsi_pp_select_register (fd, 1); if (mustek_scsi_pp_wait_for_status_bit_7_clear (fd) != SANE_STATUS_GOOD) { result = 0xff; } mustek_scsi_pp_select_register (fd, 0); DBG (5, "mustek_scsi_pp_read_response: returning 0x%02X\n", result); return result; } static SANE_Status mustek_scsi_pp_check_response (int fd) { if (mustek_scsi_pp_wait_for_status_bit_5_clear (fd) != SANE_STATUS_GOOD) { return SANE_STATUS_IO_ERROR; } if (mustek_scsi_pp_read_response (fd) != 0xA5) { DBG (2, "mustek_scsi_pp_check_response: response!=0xA5\n"); return SANE_STATUS_IO_ERROR; } DBG (5, "mustek_scsi_pp_check_response: returning success\n"); return SANE_STATUS_GOOD; } static SANE_Status mustek_scsi_pp_send_command (int fd, const u_char * cmd) { int i; signed char checksum; DBG (5, "mustek_scsi_pp_send_command: sending SCSI command 0x%02X\n", cmd[0]); /* Set timeout depending on command type */ switch (cmd[0]) { case 0xf: case 0x8: mustek_scsi_pp_timeout = 1000; break; case 0x2: mustek_scsi_pp_timeout = 80; break; case 0x12: case 0x3: case 0x11: mustek_scsi_pp_timeout = 500; break; default: mustek_scsi_pp_timeout = 1000; break; } if (mustek_scsi_pp_wait_for_status_bit_5_set (fd) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_send_command: timed out waiting for bit 5 to set\n"); return SANE_STATUS_DEVICE_BUSY; } checksum = 0; for (i = 0; i < 6; i++) { if (mustek_scsi_pp_send_command_byte (fd, cmd[i]) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_send_command: error sending byte %d (0x%02X)\n", i, cmd[i]); return SANE_STATUS_IO_ERROR; } checksum += cmd[i]; } if (mustek_scsi_pp_send_command_byte (fd, -checksum) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_send_command: error sending checksum (0x%02X)\n", -checksum); return SANE_STATUS_IO_ERROR; } return mustek_scsi_pp_check_response (fd); } static SANE_Status mustek_scsi_pp_send_data_block (int fd, const u_char * data, int len) { int i; signed char checksum; DBG (5, "mustek_scsi_pp_send_data_block: sending block of length %d\n", len); if (mustek_scsi_pp_wait_for_status_bit_5_set (fd) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_send_data_block: timed out waiting for bit 5 to set\n"); return SANE_STATUS_DEVICE_BUSY; } checksum = 0; for (i = 0; i < len; i++) { if (mustek_scsi_pp_send_command_byte (fd, data[i]) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_send_data_block: error sending byte %d (0x%02X)\n", i, data[i]); return SANE_STATUS_IO_ERROR; } checksum += data[i]; } if (mustek_scsi_pp_send_command_byte (fd, -checksum) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_send_data_block: error sending checksum (0x%02X)\n", -checksum); return SANE_STATUS_IO_ERROR; } return mustek_scsi_pp_check_response (fd); } static SANE_Status mustek_scsi_pp_read_data_block (int fd, u_char * buffer, int len) { int i; signed char checksum; DBG (5, "mustek_scsi_pp_read_data_block: reading block of length %d\n", len); if (mustek_scsi_pp_wait_for_status_bit_5_clear (fd) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_read_data_block: timed out waiting for bit 5 to clear\n"); return SANE_STATUS_DEVICE_BUSY; } checksum = 0; for (i = 0; i < len; i++) { buffer[i] = mustek_scsi_pp_read_response (fd); checksum += buffer[i]; } if ((signed char) mustek_scsi_pp_read_response (fd) != (-checksum)) { mustek_scsi_pp_send_command_byte (fd, 0xff); DBG (2, "mustek_scsi_pp_read_data_block: checksums do not match\n"); return SANE_STATUS_IO_ERROR; } if (mustek_scsi_pp_wait_for_status_bit_5_set (fd) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_read_data_block: error waiting for bit 5 to set\n"); return SANE_STATUS_IO_ERROR; } if (mustek_scsi_pp_send_command_byte (fd, 0) != SANE_STATUS_GOOD) { mustek_scsi_pp_send_command_byte (fd, 0xff); DBG (2, "mustek_scsi_pp_read_data_block: error sending final 0 byte\n"); return SANE_STATUS_IO_ERROR; } DBG (5, "mustek_scsi_pp_read_data_block: returning success\n"); return SANE_STATUS_GOOD; } /* * Externally visible functions */ SANE_Status mustek_scsi_pp_open (const char *dev, int *fd) { SANE_Status status; status = sanei_pa4s2_scsi_pp_open (dev, fd); if (status == SANE_STATUS_GOOD) { DBG (5, "mustek_scsi_pp_open: device %s opened as fd %d\n", dev, *fd); } else { DBG (2, "mustek_scsi_pp_open: error opening device %s\n", dev); } return status; } static void mustek_scsi_pp_close (int fd) { DBG (5, "mustek_scsi_pp_close: closing fd %d\n", fd); sanei_pa4s2_close (fd); } static void mustek_scsi_pp_exit (void) { DBG (5, "mustek_scsi_pp_exit: entering\n"); } static SANE_Status mustek_scsi_pp_test_ready (int fd) { u_char status; SANE_Status retval; DBG (5, "mustek_scsi_pp_test_ready: entering with fd=%d\n", fd); if (sanei_pa4s2_enable (fd, SANE_TRUE) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_test_ready: error enabling scanner\n"); return SANE_STATUS_IO_ERROR; } if (sanei_pa4s2_scsi_pp_get_status (fd, &status) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_test_ready: error getting status\n"); sanei_pa4s2_enable (fd, SANE_FALSE); return SANE_STATUS_INVAL; } retval = SANE_STATUS_GOOD; status &= 0xf0; if (status == 0xf0) { retval = SANE_STATUS_DEVICE_BUSY; } if (status & 0x40) { retval = SANE_STATUS_DEVICE_BUSY; } if (!(status & 0x20)) { retval = SANE_STATUS_DEVICE_BUSY; } if (sanei_pa4s2_enable (fd, SANE_FALSE) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_test_ready: error disabling scanner\n"); return SANE_STATUS_IO_ERROR; } if (retval == SANE_STATUS_GOOD) { DBG (5, "mustek_scsi_pp_test_ready: returning SANE_STATUS_GOOD\n"); } else { DBG (5, "mustek_scsi_pp_test_ready: returning SANE_STATUS_DEVICE_BUSY\n"); } return retval; } static SANE_Status mustek_scsi_pp_cmd (int fd, const void *src, size_t src_size, void *dst, size_t * dst_size) { SANE_Status stat; int num_tries = 0; static u_char scan_options = 0; const u_char *cmd; u_char stop_cmd[6] = { 0x1b, 0, 0, 0, 0, 0 }; int max_tries; max_tries = MUSTEK_SCSI_PP_NUM_RETRIES; cmd = (const u_char *) src; DBG (5, "mustek_scsi_pp_cmd: sending command 0x%02X to device %d\n", cmd[0], fd); if (sanei_pa4s2_enable (fd, SANE_TRUE) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_cmd: error enabling scanner\n"); return SANE_STATUS_IO_ERROR; } if (cmd[0] == 0x1b) { if (!(cmd[4] & 0x1)) { unsigned char c; int i; DBG (5, "mustek_scsi_pp_cmd: doing stop-specific stuff\n"); /* * Remembers what flags were sent with a 'start' command, and * replicate them with a stop command. */ stop_cmd[4] = scan_options & 0xfe; cmd = &stop_cmd[0]; /* * In color mode at least, the scanner doesn't seem to like stopping at * the end. It's a bit of a horrible hack, but reading loads of bytes and * allowing 20 tries for the stop command is the only way I've found that * solves the problem. */ max_tries = 20; if (sanei_pa4s2_readbegin (fd, mustek_scsi_pp_register) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_cmd: error in readbegin for stop\n"); } for (i = 0; i < 10000; i++) { if (sanei_pa4s2_readbyte (fd, &c) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_cmd: error reading byte for stop\n"); break; } DBG (5, "mustek_scsi_pp_cmd: successfully read byte %d\n", i); } if (sanei_pa4s2_readend (fd) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_cmd: error in readend for stop\n"); } } } if (cmd[0] == 0x08) { DBG (5, "mustek_scsi_pp_cmd: doing read-specific stuff\n"); mustek_scsi_pp_timeout = 30000; mustek_scsi_pp_bit_4_state = 0xff; } /* * Send the command itself in one block, then any extra input data in a second * block. Not sure if that's necessary. */ if (src_size < 6) { sanei_pa4s2_enable (fd, SANE_FALSE); DBG (2, "mustek_scsi_pp_cmd: source size is only %lu (<6)\n", (u_long) src_size); return SANE_STATUS_INVAL; } /* * Retry the command several times, as occasionally it doesn't * work first time. */ do { stat = mustek_scsi_pp_send_command (fd, cmd); num_tries++; } while ((stat != SANE_STATUS_GOOD) && (num_tries < max_tries)); if (stat != SANE_STATUS_GOOD) { sanei_pa4s2_enable (fd, SANE_FALSE); DBG (2, "mustek_scsi_pp_cmd: sending command failed\n"); return stat; } if (src_size > 6) { DBG (5, "mustek_scsi_pp_cmd: sending data block of length %lu\n", (u_long) (src_size - 6)); stat = mustek_scsi_pp_send_data_block (fd, ((const u_char *) src) + 6, src_size - 6); if (stat != SANE_STATUS_GOOD) { sanei_pa4s2_enable (fd, SANE_FALSE); DBG (2, "mustek_scsi_pp_cmd: sending data block failed\n"); return stat; } } if (dst) { unsigned int length; /* check buffer is big enough to receive data */ length = (cmd[3] << 8) | cmd[4]; DBG (5, "mustek_scsi_pp_cmd: reading %d bytes\n", length); if (length > *dst_size) { sanei_pa4s2_enable (fd, SANE_FALSE); DBG (2, "mustek_scsi_pp_cmd: buffer (size %lu) not big enough for data (size %d)\n", (u_long) *dst_size, length); return SANE_STATUS_INVAL; } stat = mustek_scsi_pp_read_data_block (fd, dst, length); if (stat != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_cmd: error reading data block\n"); } } if (cmd[0] == 0x1b) { if (cmd[4] & 0x1) { DBG (5, "mustek_scsi_pp_cmd: doing start-specific stuff\n"); scan_options = cmd[4]; /* 'Start' command - wait for valid status */ mustek_scsi_pp_timeout = 70000; stat = mustek_scsi_pp_wait_for_valid_status (fd); if (stat != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_cmd: error waiting for valid status after start\n"); } } } if (sanei_pa4s2_enable (fd, SANE_FALSE) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_cmd: error disabling scanner\n"); return SANE_STATUS_IO_ERROR; } if (stat == SANE_STATUS_GOOD) { DBG (5, "mustek_scsi_pp_cmd: returning success\n"); } return stat; } static SANE_Status mustek_scsi_pp_rdata (int fd, int planes, SANE_Byte * buf, int lines, int bpl) { int i, j; DBG (5, "mustek_scsi_pp_rdata: reading %d lines at %d bpl, %d planes from %d\n", lines, bpl, planes, fd); if ((planes != 1) && (planes != 3)) { DBG (2, "mustek_scsi_pp_rdata: invalid number of planes (%d)\n", planes); return SANE_STATUS_INVAL; } if (sanei_pa4s2_enable (fd, SANE_TRUE) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_rdata: error enabling scanner\n"); return SANE_STATUS_IO_ERROR; } for (i = 0; i < lines; i++) { if (planes == 3) { if (mustek_scsi_pp_wait_for_status_bit_4_toggle (fd) != SANE_STATUS_GOOD) { sanei_pa4s2_enable (fd, SANE_FALSE); DBG (2, "mustek_scsi_pp_rdata: error waiting for bit 4 toggle for red, line %d\n", i); return SANE_STATUS_IO_ERROR; } if (sanei_pa4s2_readbegin (fd, mustek_scsi_pp_register) != SANE_STATUS_GOOD) { sanei_pa4s2_enable (fd, SANE_FALSE); DBG (2, "mustek_scsi_pp_rdata: error in readbegin for red, line %d\n", i); return SANE_STATUS_IO_ERROR; } for (j = 0; j < (bpl / 3); j++) { if (sanei_pa4s2_readbyte (fd, &buf[j]) != SANE_STATUS_GOOD) { sanei_pa4s2_readend (fd); sanei_pa4s2_enable (fd, SANE_FALSE); DBG (2, "mustek_scsi_pp_rdata: error reading red byte, line %d, byte %d\n", i, j); return SANE_STATUS_IO_ERROR; } } if (sanei_pa4s2_readend (fd) != SANE_STATUS_GOOD) { sanei_pa4s2_enable (fd, SANE_FALSE); DBG (2, "mustek_scsi_pp_rdata: error in readend for red, line %d\n", i); return SANE_STATUS_IO_ERROR; } if (mustek_scsi_pp_wait_for_status_bit_4_toggle (fd) != SANE_STATUS_GOOD) { sanei_pa4s2_enable (fd, SANE_FALSE); DBG (2, "mustek_scsi_pp_rdata: error waiting for bit 4 toggle for green, line %d\n", i); return SANE_STATUS_IO_ERROR; } if (sanei_pa4s2_readbegin (fd, mustek_scsi_pp_register) != SANE_STATUS_GOOD) { sanei_pa4s2_enable (fd, SANE_FALSE); DBG (2, "mustek_scsi_pp_rdata: error in readbegin for green, line %d\n", i); return SANE_STATUS_IO_ERROR; } for (j = 0; j < (bpl / 3); j++) { if (sanei_pa4s2_readbyte (fd, &buf[j + (bpl / 3)]) != SANE_STATUS_GOOD) { sanei_pa4s2_readend (fd); sanei_pa4s2_enable (fd, SANE_FALSE); DBG (2, "mustek_scsi_pp_rdata: error reading green byte, line %d, byte %d\n", i, j); return SANE_STATUS_IO_ERROR; } } if (sanei_pa4s2_readend (fd) != SANE_STATUS_GOOD) { sanei_pa4s2_enable (fd, SANE_FALSE); DBG (2, "mustek_scsi_pp_rdata: error in readend for green, line %d\n", i); return SANE_STATUS_IO_ERROR; } if (mustek_scsi_pp_wait_for_status_bit_4_toggle (fd) != SANE_STATUS_GOOD) { sanei_pa4s2_enable (fd, SANE_FALSE); DBG (2, "mustek_scsi_pp_rdata: error waiting for bit 4 toggle for blue, line %d\n", i); return SANE_STATUS_IO_ERROR; } if (sanei_pa4s2_readbegin (fd, mustek_scsi_pp_register) != SANE_STATUS_GOOD) { sanei_pa4s2_enable (fd, SANE_FALSE); DBG (2, "mustek_scsi_pp_rdata: error in readbegin for blue, line %d\n", i); return SANE_STATUS_IO_ERROR; } for (j = 0; j < (bpl / 3); j++) { if (sanei_pa4s2_readbyte (fd, &buf[j + (2 * (bpl / 3))]) != SANE_STATUS_GOOD) { sanei_pa4s2_readend (fd); sanei_pa4s2_enable (fd, SANE_FALSE); DBG (2, "mustek_scsi_pp_rdata: error reading blue byte, line %d, byte %d\n", i, j); return SANE_STATUS_IO_ERROR; } } if (sanei_pa4s2_readend (fd) != SANE_STATUS_GOOD) { sanei_pa4s2_enable (fd, SANE_FALSE); DBG (2, "mustek_scsi_pp_rdata: error in readend for blue, line %d\n", i); return SANE_STATUS_IO_ERROR; } } else { if (mustek_scsi_pp_wait_for_status_bit_4_toggle (fd) != SANE_STATUS_GOOD) { sanei_pa4s2_enable (fd, SANE_FALSE); DBG (2, "mustek_scsi_pp_rdata: error waiting for bit 4 toggle, line %d\n", i); return SANE_STATUS_IO_ERROR; } if (sanei_pa4s2_readbegin (fd, mustek_scsi_pp_register) != SANE_STATUS_GOOD) { sanei_pa4s2_enable (fd, SANE_FALSE); DBG (2, "mustek_scsi_pp_rdata: error in readbegin, line %d\n", i); return SANE_STATUS_IO_ERROR; } for (j = 0; j < bpl; j++) { if (sanei_pa4s2_readbyte (fd, &buf[j]) != SANE_STATUS_GOOD) { sanei_pa4s2_readend (fd); sanei_pa4s2_enable (fd, SANE_FALSE); DBG (2, "mustek_scsi_pp_rdata: error reading byte, line %d, byte %d\n", i, j); return SANE_STATUS_IO_ERROR; } } if (sanei_pa4s2_readend (fd) != SANE_STATUS_GOOD) { sanei_pa4s2_enable (fd, SANE_FALSE); DBG (2, "mustek_scsi_pp_rdata: error in readend, line %d\n", i); return SANE_STATUS_IO_ERROR; } } buf += bpl; } if (sanei_pa4s2_enable (fd, SANE_FALSE) != SANE_STATUS_GOOD) { DBG (2, "mustek_scsi_pp_rdata: error enabling scanner\n"); return SANE_STATUS_IO_ERROR; } DBG (5, "mustek_scsi_pp_rdata: returning success\n"); return SANE_STATUS_GOOD; } backends-1.3.0/backend/mustek_scsi_pp.h000066400000000000000000000101651456256263500200760ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2003 James Perry 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, see . 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 the SCSI-over-parallel port protocol used in, for example, the Paragon 600 II EP */ #ifndef mustek_scsi_pp_h #define mustek_scsi_pp_h static int mustek_scsi_pp_get_time (void); /** * Open the connection to a Mustek SCSI-over-pp device. * * @param dev Port address as text. * @param fd Information about port address and I/O method. fd is not a file * descriptor. The name and type are used for compatibility reasons. * * @return * - SANE_STATUS_GOOD - on success * - SANE_STATUS_INVAL - if the port address can't be interpreted * - SANE_STATUS_IO_ERROR - if the device file for a port couldn't be accessed */ static SANE_Status mustek_scsi_pp_open (const char *dev, int *fd); /** * Close the connection to a Mustek SCSI-over-PP device. * * @param fd Information about port address and I/O method. * */ static void mustek_scsi_pp_close (int fd); /** * Exit Mustek SCSI-over-PP. */ static void mustek_scsi_pp_exit (void); /** * Find out if the device is ready to accept new commands. * * @param fd Information about port address and I/O method. * * @return * - SANE_STATUS_GOOD - if the device is ready * - SANE_STATUS_DEVICE_BUSY if the device is still busy (try again later) */ static SANE_Status mustek_scsi_pp_test_ready (int fd); /** * Send a command to the Mustek SCSI-over-pp device. * * @param fd Information about port address and I/O method. * @param src Data to be sent to the device. * @param src_size Size of data to be sent to the device. * @param dst Data to be received from the device. * @param dst_size Size of data to be received from the device * * @return * - SANE_STATUS_GOOD - on success * - SANE_STATUS_IO_ERROR - if an error occurred during the dialog with the * device */ static SANE_Status mustek_scsi_pp_cmd (int fd, const void *src, size_t src_size, void *dst, size_t * dst_size); /** * Read scanned image data. * * @param fd Information about port address and I/O method. * @param planes Bytes per pixel (3 for color, 1 for all other modes) * @param buf Buffer for image data. * @param lines Number of lines * @param bpl Bytes per line * * @return * - SANE_STATUS_GOOD - on success * - SANE_STATUS_IO_ERROR - if an error occurred during the dialog with the * device */ static SANE_Status mustek_scsi_pp_rdata (int fd, int planes, SANE_Byte * buf, int lines, int bpl); #endif /* mustek_scsi_pp_h */ backends-1.3.0/backend/mustek_usb.c000066400000000000000000001310001456256263500172120ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2000 Mustek. Originally maintained by Tom Wang Copyright (C) 2001 - 2004 by Henning Meier-Geinitz. 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, see . As a special exception, the authors of SANE give permission for additional uses of the libraries contained in this release of SANE. The exception is that, if you link a SANE library with other files to produce an executable, this does not by itself cause the resulting executable to be covered by the GNU General Public License. Your use of that executable is in no way restricted on account of linking the SANE library code into it. This exception does not, however, invalidate any other reasons why the executable file might be covered by the GNU General Public License. If you submit changes to SANE to the maintainers to be included in a subsequent release, you agree by submitting the changes that those changes may be distributed with this exception intact. If you write modifications of your own for SANE, it is your choice whether to permit this exception to apply to your modifications. If you do not wish that, delete this exception notice. This file implements a SANE backend for Mustek 1200UB and similar USB flatbed scanners. */ #define BUILD 18 #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #define BACKEND_NAME mustek_usb #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_usb.h" #include "mustek_usb.h" #include "mustek_usb_high.c" #ifndef SANE_I18N #define SANE_I18N(text) text #endif static SANE_Int num_devices; static Mustek_Usb_Device *first_dev; static Mustek_Usb_Scanner *first_handle; static const SANE_Device **devlist = 0; /* Maximum amount of data read in one turn from USB. */ static SANE_Word max_block_size = (8 * 1024); /* Array of newly attached devices */ static Mustek_Usb_Device **new_dev; /* Length of new_dev array */ static SANE_Int new_dev_len; /* Number of entries allocated for new_dev */ static SANE_Int new_dev_alloced; static SANE_String_Const mode_list[6]; static const SANE_Range u8_range = { 0, /* minimum */ 255, /* maximum */ 0 /* quantization */ }; 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; } static SANE_Status calc_parameters (Mustek_Usb_Scanner * s) { SANE_String val; SANE_Status status = SANE_STATUS_GOOD; SANE_Int max_x, max_y; DBG (5, "calc_parameters: start\n"); val = s->val[OPT_MODE].s; s->params.last_frame = SANE_TRUE; if (!strcmp (val, SANE_VALUE_SCAN_MODE_LINEART)) { s->params.format = SANE_FRAME_GRAY; s->params.depth = 1; s->bpp = 1; s->channels = 1; } else if (!strcmp (val, SANE_VALUE_SCAN_MODE_GRAY)) { s->params.format = SANE_FRAME_GRAY; s->params.depth = 8; s->bpp = 8; s->channels = 1; } else if (!strcmp (val, SANE_VALUE_SCAN_MODE_COLOR)) { s->params.format = SANE_FRAME_RGB; s->params.depth = 8; s->bpp = 24; s->channels = 3; } else { DBG (1, "calc_parameters: invalid mode %s\n", (SANE_Char *) val); status = SANE_STATUS_INVAL; } s->tl_x = SANE_UNFIX (s->val[OPT_TL_X].w) / MM_PER_INCH; s->tl_y = SANE_UNFIX (s->val[OPT_TL_Y].w) / MM_PER_INCH; s->width = SANE_UNFIX (s->val[OPT_BR_X].w) / MM_PER_INCH - s->tl_x; s->height = SANE_UNFIX (s->val[OPT_BR_Y].w) / MM_PER_INCH - s->tl_y; if (s->width < 0) { DBG (1, "calc_parameters: warning: tl_x > br_x\n"); } if (s->height < 0) { DBG (1, "calc_parameters: warning: tl_y > br_y\n"); } max_x = s->hw->max_width * SANE_UNFIX (s->val[OPT_RESOLUTION].w) / 300; max_y = s->hw->max_height * SANE_UNFIX (s->val[OPT_RESOLUTION].w) / 300; s->tl_x_dots = s->tl_x * SANE_UNFIX (s->val[OPT_RESOLUTION].w); s->width_dots = s->width * SANE_UNFIX (s->val[OPT_RESOLUTION].w); s->tl_y_dots = s->tl_y * SANE_UNFIX (s->val[OPT_RESOLUTION].w); s->height_dots = s->height * SANE_UNFIX (s->val[OPT_RESOLUTION].w); if (s->width_dots > max_x) s->width_dots = max_x; if (s->height_dots > max_y) s->height_dots = max_y; if (!strcmp (val, SANE_VALUE_SCAN_MODE_LINEART)) { s->width_dots = (s->width_dots / 8) * 8; if (s->width_dots == 0) s->width_dots = 8; } if (s->tl_x_dots < 0) s->tl_x_dots = 0; if (s->tl_y_dots < 0) s->tl_y_dots = 0; if (s->tl_x_dots + s->width_dots > max_x) s->tl_x_dots = max_x - s->width_dots; if (s->tl_y_dots + s->height_dots > max_y) s->tl_y_dots = max_y - s->height_dots; s->val[OPT_TL_X].w = SANE_FIX (s->tl_x * MM_PER_INCH); s->val[OPT_TL_Y].w = SANE_FIX (s->tl_y * MM_PER_INCH); s->val[OPT_BR_X].w = SANE_FIX ((s->tl_x + s->width) * MM_PER_INCH); s->val[OPT_BR_Y].w = SANE_FIX ((s->tl_y + s->height) * MM_PER_INCH); s->params.pixels_per_line = s->width_dots; if (s->params.pixels_per_line < 0) s->params.pixels_per_line = 0; s->params.lines = s->height_dots; if (s->params.lines < 0) s->params.lines = 0; s->params.bytes_per_line = s->params.pixels_per_line * s->params.depth / 8 * s->channels; DBG (4, "calc_parameters: format=%d\n", s->params.format); DBG (4, "calc_parameters: last frame=%d\n", s->params.last_frame); DBG (4, "calc_parameters: lines=%d\n", s->params.lines); DBG (4, "calc_parameters: pixels per line=%d\n", s->params.pixels_per_line); DBG (4, "calc_parameters: bytes per line=%d\n", s->params.bytes_per_line); DBG (4, "calc_parameters: Pixels %dx%dx%d\n", s->params.pixels_per_line, s->params.lines, 1 << s->params.depth); DBG (5, "calc_parameters: exit\n"); return status; } static SANE_Status init_options (Mustek_Usb_Scanner * s) { SANE_Int option; SANE_Status status; DBG (5, "init_options: start\n"); memset (s->opt, 0, sizeof (s->opt)); memset (s->val, 0, sizeof (s->val)); for (option = 0; option < NUM_OPTIONS; ++option) { s->opt[option].size = sizeof (SANE_Word); s->opt[option].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } 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].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* "Mode" group: */ s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode"); s->opt[OPT_MODE_GROUP].desc = ""; s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MODE_GROUP].size = 0; s->opt[OPT_MODE_GROUP].cap = 0; s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* scan mode */ mode_list[0] = SANE_VALUE_SCAN_MODE_COLOR; mode_list[1] = SANE_VALUE_SCAN_MODE_GRAY; mode_list[2] = SANE_VALUE_SCAN_MODE_LINEART; mode_list[3] = NULL; s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; 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].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_MODE].size = max_string_size (mode_list); s->opt[OPT_MODE].constraint.string_list = mode_list; s->val[OPT_MODE].s = strdup (mode_list[1]); /* resolution */ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].type = SANE_TYPE_FIXED; s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_RESOLUTION].constraint.range = &s->hw->dpi_range; s->val[OPT_RESOLUTION].w = s->hw->dpi_range.min; if (s->hw->chip->scanner_type == MT_600CU) s->hw->dpi_range.max = SANE_FIX (600); else s->hw->dpi_range.max = SANE_FIX (1200); /* preview */ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; s->val[OPT_PREVIEW].w = SANE_FALSE; /* "Geometry" group: */ s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry"); s->opt[OPT_GEOMETRY_GROUP].desc = ""; s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_GEOMETRY_GROUP].size = 0; s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* top-left x */ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; s->opt[OPT_TL_X].unit = SANE_UNIT_MM; s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_X].constraint.range = &s->hw->x_range; s->val[OPT_TL_X].w = 0; /* top-left y */ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range; s->val[OPT_TL_Y].w = 0; /* bottom-right x */ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; s->opt[OPT_BR_X].unit = SANE_UNIT_MM; s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_X].constraint.range = &s->hw->x_range; s->val[OPT_BR_X].w = s->hw->x_range.max; /* bottom-right y */ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range; s->val[OPT_BR_Y].w = s->hw->y_range.max; /* "Enhancement" group: */ s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement"); s->opt[OPT_ENHANCEMENT_GROUP].desc = ""; s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_ENHANCEMENT_GROUP].size = 0; s->opt[OPT_ENHANCEMENT_GROUP].cap = 0; s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* 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_INT; s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE; s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_THRESHOLD].constraint.range = &u8_range; s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; s->val[OPT_THRESHOLD].w = 128; /* custom-gamma table */ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL; s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE; /* gray gamma vector */ s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR; s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR; s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR; s->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR].wa = &s->gray_gamma_table[0]; /* red gamma vector */ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_R].wa = &s->red_gamma_table[0]; /* green gamma vector */ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_G].wa = &s->green_gamma_table[0]; /* blue gamma vector */ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT; s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_B].wa = &s->blue_gamma_table[0]; RIE (calc_parameters (s)); DBG (5, "init_options: exit\n"); return SANE_STATUS_GOOD; } static SANE_Status attach (SANE_String_Const devname, Mustek_Usb_Device ** devp, SANE_Bool may_wait) { Mustek_Usb_Device *dev; SANE_Status status; Mustek_Type scanner_type; SANE_Int fd; DBG (5, "attach: start: devp %s NULL, may_wait = %d\n", devp ? "!=" : "==", may_wait); if (!devname) { DBG (1, "attach: devname == NULL\n"); return SANE_STATUS_INVAL; } for (dev = first_dev; dev; dev = dev->next) if (strcmp (dev->sane.name, devname) == 0) { if (devp) *devp = dev; DBG (4, "attach: device `%s' was already in device list\n", devname); return SANE_STATUS_GOOD; } DBG (4, "attach: trying to open device `%s'\n", devname); status = sanei_usb_open (devname, &fd); if (status != SANE_STATUS_GOOD) { DBG (3, "attach: couldn't open device `%s': %s\n", devname, sane_strstatus (status)); return status; } DBG (4, "attach: device `%s' successfully opened\n", devname); /* try to identify model */ DBG (4, "attach: trying to identify device `%s'\n", devname); status = usb_low_identify_scanner (fd, &scanner_type); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: device `%s' doesn't look like a supported scanner\n", devname); sanei_usb_close (fd); return status; } sanei_usb_close (fd); if (scanner_type == MT_UNKNOWN) { DBG (3, "attach: warning: couldn't identify device `%s', must set " "type manually\n", devname); } dev = malloc (sizeof (Mustek_Usb_Device)); if (!dev) { DBG (1, "attach: couldn't malloc Mustek_Usb_Device\n"); return SANE_STATUS_NO_MEM; } memset (dev, 0, sizeof (*dev)); dev->name = strdup (devname); dev->sane.name = (SANE_String_Const) dev->name; dev->sane.vendor = "Mustek"; switch (scanner_type) { case MT_1200CU: dev->sane.model = "1200 CU"; break; case MT_1200CU_PLUS: dev->sane.model = "1200 CU Plus"; break; case MT_1200USB: dev->sane.model = "1200 USB (unsupported)"; break; case MT_1200UB: dev->sane.model = "1200 UB"; break; case MT_600CU: dev->sane.model = "600 CU"; break; case MT_600USB: dev->sane.model = "600 USB (unsupported)"; break; default: dev->sane.model = "(unidentified)"; break; } dev->sane.type = "flatbed scanner"; dev->x_range.min = 0; dev->x_range.max = SANE_FIX (8.4 * MM_PER_INCH); dev->x_range.quant = 0; dev->y_range.min = 0; dev->y_range.max = SANE_FIX (11.7 * MM_PER_INCH); dev->y_range.quant = 0; dev->max_height = 11.7 * 300; dev->max_width = 8.4 * 300; dev->dpi_range.min = SANE_FIX (50); dev->dpi_range.max = SANE_FIX (600); dev->dpi_range.quant = SANE_FIX (1); status = usb_high_scan_init (dev); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: usb_high_scan_init returned status: %s\n", sane_strstatus (status)); free (dev); return status; } dev->chip->scanner_type = scanner_type; dev->chip->max_block_size = max_block_size; DBG (2, "attach: found %s %s %s at %s\n", dev->sane.vendor, dev->sane.type, dev->sane.model, dev->sane.name); ++num_devices; dev->next = first_dev; first_dev = dev; if (devp) *devp = dev; DBG (5, "attach: exit\n"); return SANE_STATUS_GOOD; } static SANE_Status attach_one_device (SANE_String_Const devname) { Mustek_Usb_Device *dev; SANE_Status status; RIE (attach (devname, &dev, SANE_FALSE)); if (dev) { /* Keep track of newly attached devices so we can set options as necessary. */ if (new_dev_len >= new_dev_alloced) { new_dev_alloced += 4; if (new_dev) new_dev = realloc (new_dev, new_dev_alloced * sizeof (new_dev[0])); else new_dev = malloc (new_dev_alloced * sizeof (new_dev[0])); if (!new_dev) { DBG (1, "attach_one_device: out of memory\n"); return SANE_STATUS_NO_MEM; } } new_dev[new_dev_len++] = dev; } return SANE_STATUS_GOOD; } static SANE_Status fit_lines (Mustek_Usb_Scanner * s, SANE_Byte * src, SANE_Byte * dst, SANE_Word src_lines, SANE_Word * dst_lines) { SANE_Int threshold; SANE_Word src_width, dst_width; SANE_Word dst_pixel, src_pixel; SANE_Word dst_line, src_line; SANE_Word pixel_switch; SANE_Word src_address, dst_address; src_width = s->hw->width; dst_width = s->width_dots; threshold = s->val[OPT_THRESHOLD].w; DBG (5, "fit_lines: dst_width=%d, src_width=%d, src_lines=%d, " "offset=%d\n", dst_width, src_width, src_lines, s->hw->line_offset); dst_line = 0; src_line = s->hw->line_offset; while (src_line < src_lines) { DBG (5, "fit_lines: getting line: dst_line=%d, src_line=%d, " "line_switch=%d\n", dst_line, src_line, s->hw->line_switch); src_pixel = 0; pixel_switch = src_width; for (dst_pixel = 0; dst_pixel < dst_width; dst_pixel++) { while (pixel_switch > dst_width) { src_pixel++; pixel_switch -= dst_width; } pixel_switch += src_width; src_address = src_pixel * s->hw->bpp / 8 + src_width * src_line * s->hw->bpp / 8; dst_address = dst_pixel * s->bpp / 8 + dst_width * dst_line * s->bpp / 8; if (s->bpp == 8) { dst[dst_address] = s->gray_table[src[src_address]]; } else if (s->bpp == 24) { dst[dst_address] = s->red_table[s->gray_table[src[src_address]]]; dst[dst_address + 1] = s->green_table[s->gray_table[src[src_address + 1]]]; dst[dst_address + 2] = s->blue_table[s->gray_table[src[src_address + 2]]]; } else /* lineart */ { if ((dst_pixel % 8) == 0) dst[dst_address] = 0; dst[dst_address] |= (((src[src_address] > threshold) ? 0 : 1) << (7 - (dst_pixel % 8))); } } dst_line++; while (s->hw->line_switch >= s->height_dots) { src_line++; s->hw->line_switch -= s->height_dots; } s->hw->line_switch += s->hw->height; } *dst_lines = dst_line; s->hw->line_offset = (src_line - src_lines); DBG (4, "fit_lines: exit, src_line=%d, *dst_lines=%d, offset=%d\n", src_line, *dst_lines, s->hw->line_offset); return SANE_STATUS_GOOD; } static SANE_Status check_gamma_table (SANE_Word * table) { SANE_Word entry, value; SANE_Status status = SANE_STATUS_GOOD; for (entry = 0; entry < 256; entry++) { value = table[entry]; if (value > 255) { DBG (1, "check_gamma_table: warning: entry %d > 255 (%d) - fixed\n", entry, value); table[entry] = 255; status = SANE_STATUS_INVAL; } } return status; } /* -------------------------- SANE API functions ------------------------- */ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { SANE_Char line[PATH_MAX]; SANE_Char *word, *end; SANE_String_Const cp; SANE_Int linenumber; FILE *fp; DBG_INIT (); DBG (2, "SANE Mustek USB backend version %d.%d build %d from %s\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD, PACKAGE_STRING); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); DBG (5, "sane_init: authorize %s null\n", authorize ? "!=" : "=="); num_devices = 0; first_dev = 0; first_handle = 0; devlist = 0; new_dev = 0; new_dev_len = 0; new_dev_alloced = 0; sanei_usb_init (); fp = sanei_config_open (MUSTEK_USB_CONFIG_FILE); if (!fp) { /* default to /dev/usb/scanner instead of insisting on config file */ DBG (3, "sane_init: couldn't open config file `%s': %s. Using " "/dev/usb/scanner directly\n", MUSTEK_USB_CONFIG_FILE, strerror (errno)); attach ("/dev/usb/scanner", 0, SANE_FALSE); return SANE_STATUS_GOOD; } linenumber = 0; DBG (4, "sane_init: reading config file `%s'\n", MUSTEK_USB_CONFIG_FILE); while (sanei_config_read (line, sizeof (line), fp)) { word = 0; linenumber++; cp = sanei_config_get_string (line, &word); if (!word || cp == line) { DBG (5, "sane_init: config file line %d: ignoring empty line\n", linenumber); if (word) free (word); continue; } if (word[0] == '#') { DBG (5, "sane_init: config file line %d: ignoring comment line\n", linenumber); free (word); continue; } if (strcmp (word, "option") == 0) { free (word); word = 0; cp = sanei_config_get_string (cp, &word); if (!word) { DBG (1, "sane_init: config file line %d: missing quotation mark?\n", linenumber); continue; } if (strcmp (word, "max_block_size") == 0) { free (word); word = 0; cp = sanei_config_get_string (cp, &word); if (!word) { DBG (1, "sane_init: config file line %d: missing quotation mark?\n", linenumber); continue; } errno = 0; max_block_size = strtol (word, &end, 0); if (end == word) { DBG (3, "sane-init: config file line %d: max_block_size " "must have a parameter; using 8192 bytes\n", linenumber); max_block_size = 8192; } if (errno) { DBG (3, "sane-init: config file line %d: max_block_size `%s' " "is invalid (%s); using 8192 bytes\n", linenumber, word, strerror (errno)); max_block_size = 8192; } else { DBG (3, "sane_init: config file line %d: max_block_size set " "to %d bytes\n", linenumber, max_block_size); } if (word) free (word); word = 0; } else if (strcmp (word, "1200ub") == 0) { if (new_dev_len > 0) { /* this is a 1200 UB */ new_dev[new_dev_len - 1]->chip->scanner_type = MT_1200UB; new_dev[new_dev_len - 1]->sane.model = "1200 UB"; DBG (3, "sane_init: config file line %d: `%s' is a Mustek " "1200 UB\n", linenumber, new_dev[new_dev_len - 1]->sane.name); } else { DBG (3, "sane_init: config file line %d: option " "1200ub ignored, was set before any device " "name\n", linenumber); } if (word) free (word); word = 0; } else if (strcmp (word, "1200cu") == 0) { if (new_dev_len > 0) { /* this is a 1200 CU */ new_dev[new_dev_len - 1]->chip->scanner_type = MT_1200CU; new_dev[new_dev_len - 1]->sane.model = "1200 CU"; DBG (3, "sane_init: config file line %d: `%s' is a Mustek " "1200 CU\n", linenumber, new_dev[new_dev_len - 1]->sane.name); } else { DBG (3, "sane_init: config file line %d: option " "1200cu ignored, was set before any device " "name\n", linenumber); } if (word) free (word); word = 0; } else if (strcmp (word, "1200cu_plus") == 0) { if (new_dev_len > 0) { /* this is a 1200 CU Plus */ new_dev[new_dev_len - 1]->chip->scanner_type = MT_1200CU_PLUS; new_dev[new_dev_len - 1]->sane.model = "1200 CU Plus"; DBG (3, "sane_init: config file line %d: `%s' is a Mustek " "1200 CU Plus\n", linenumber, new_dev[new_dev_len - 1]->sane.name); } else { DBG (3, "sane_init: config file line %d: option " "1200cu_plus ignored, was set before any device " "name\n", linenumber); } if (word) free (word); word = 0; } else if (strcmp (word, "600cu") == 0) { if (new_dev_len > 0) { /* this is a 600 CU */ new_dev[new_dev_len - 1]->chip->scanner_type = MT_600CU; new_dev[new_dev_len - 1]->sane.model = "600 CU"; DBG (3, "sane_init: config file line %d: `%s' is a Mustek " "600 CU\n", linenumber, new_dev[new_dev_len - 1]->sane.name); } else { DBG (3, "sane_init: config file line %d: option " "600cu ignored, was set before any device " "name\n", linenumber); } if (word) free (word); word = 0; } else { DBG (3, "sane_init: config file line %d: option " "%s is unknown\n", linenumber, word); if (word) free (word); word = 0; } } else { new_dev_len = 0; DBG (4, "sane_init: config file line %d: trying to attach `%s'\n", linenumber, line); sanei_usb_attach_matching_devices (line, attach_one_device); if (word) free (word); word = 0; } } if (new_dev_alloced > 0) { new_dev_len = new_dev_alloced = 0; free (new_dev); } fclose (fp); DBG (5, "sane_init: exit\n"); return SANE_STATUS_GOOD; } void sane_exit (void) { Mustek_Usb_Device *dev, *next; SANE_Status status; DBG (5, "sane_exit: start\n"); for (dev = first_dev; dev; dev = next) { next = dev->next; if (dev->is_prepared) { status = usb_high_scan_clearup (dev); if (status != SANE_STATUS_GOOD) DBG (3, "sane_close: usb_high_scan_clearup returned %s\n", sane_strstatus (status)); } status = usb_high_scan_exit (dev); if (status != SANE_STATUS_GOOD) DBG (3, "sane_close: usb_high_scan_exit returned %s\n", sane_strstatus (status)); if (dev->chip) { status = usb_high_scan_exit (dev); if (status != SANE_STATUS_GOOD) DBG (3, "sane_exit: while closing %s, usb_high_scan_exit returned: " "%s\n", dev->name, sane_strstatus (status)); } free ((void *) dev->name); free (dev); } first_dev = 0; if (devlist) free (devlist); devlist = 0; DBG (5, "sane_exit: exit\n"); } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { Mustek_Usb_Device *dev; SANE_Int dev_num; DBG (5, "sane_get_devices: start: local_only = %s\n", local_only == SANE_TRUE ? "true" : "false"); if (devlist) free (devlist); devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); if (!devlist) return SANE_STATUS_NO_MEM; dev_num = 0; for (dev = first_dev; dev_num < num_devices; dev = dev->next) devlist[dev_num++] = &dev->sane; devlist[dev_num++] = 0; *device_list = devlist; DBG (5, "sane_get_devices: exit\n"); return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { Mustek_Usb_Device *dev; SANE_Status status; Mustek_Usb_Scanner *s; SANE_Int value; DBG (5, "sane_open: start (devicename = `%s')\n", devicename); if (devicename[0]) { for (dev = first_dev; dev; dev = dev->next) if (strcmp (dev->sane.name, devicename) == 0) break; if (!dev) { DBG (5, "sane_open: couldn't find `%s' in devlist, trying attach)\n", devicename); RIE (attach (devicename, &dev, SANE_TRUE)); } else DBG (5, "sane_open: found `%s' in devlist\n", dev->name); } else { /* empty devicname -> use first device */ dev = first_dev; if (dev) DBG (5, "sane_open: empty devicename, trying `%s'\n", dev->name); } if (!dev) return SANE_STATUS_INVAL; if (dev->chip->scanner_type == MT_UNKNOWN) { DBG (0, "sane_open: the type of your scanner is unknown, edit " "mustek_usb.conf before using the scanner\n"); return SANE_STATUS_INVAL; } s = malloc (sizeof (*s)); if (!s) return SANE_STATUS_NO_MEM; memset (s, 0, sizeof (*s)); s->hw = dev; RIE (init_options (s)); /* insert newly opened handle into list of open handles: */ s->next = first_handle; first_handle = s; *handle = s; strcpy (s->hw->device_name, dev->name); RIE (usb_high_scan_turn_power (s->hw, SANE_TRUE)); RIE (usb_high_scan_back_home (s->hw)); s->hw->scan_buffer = (SANE_Byte *) malloc (SCAN_BUFFER_SIZE * 2); if (!s->hw->scan_buffer) { DBG (5, "sane_open: couldn't malloc s->hw->scan_buffer (%d bytes)\n", SCAN_BUFFER_SIZE * 2); return SANE_STATUS_NO_MEM; } s->hw->scan_buffer_len = 0; s->hw->scan_buffer_start = s->hw->scan_buffer; s->hw->temp_buffer = (SANE_Byte *) malloc (SCAN_BUFFER_SIZE); if (!s->hw->temp_buffer) { DBG (5, "sane_open: couldn't malloc s->hw->temp_buffer (%d bytes)\n", SCAN_BUFFER_SIZE); return SANE_STATUS_NO_MEM; } s->hw->temp_buffer_len = 0; s->hw->temp_buffer_start = s->hw->temp_buffer; for (value = 0; value < 256; value++) { s->linear_gamma_table[value] = value; s->red_gamma_table[value] = value; s->green_gamma_table[value] = value; s->blue_gamma_table[value] = value; s->gray_gamma_table[value] = value; } s->red_table = s->linear_gamma_table; s->green_table = s->linear_gamma_table; s->blue_table = s->linear_gamma_table; s->gray_table = s->linear_gamma_table; DBG (5, "sane_open: exit\n"); return SANE_STATUS_GOOD; } void sane_close (SANE_Handle handle) { Mustek_Usb_Scanner *prev, *s; SANE_Status status; DBG (5, "sane_close: start\n"); /* remove handle from list of open handles: */ prev = 0; for (s = first_handle; s; s = s->next) { if (s == handle) break; prev = s; } if (!s) { DBG (5, "close: invalid handle %p\n", handle); return; /* oops, not a handle we know about */ } if (prev) prev->next = s->next; else first_handle = s->next; if (s->hw->is_open) { status = usb_high_scan_turn_power (s->hw, SANE_FALSE); if (status != SANE_STATUS_GOOD) DBG (3, "sane_close: usb_high_scan_turn_power returned %s\n", sane_strstatus (status)); } #if 0 if (s->hw->is_prepared) { status = usb_high_scan_clearup (s->hw); if (status != SANE_STATUS_GOOD) DBG (3, "sane_close: usb_high_scan_clearup returned %s\n", sane_strstatus (status)); } status = usb_high_scan_exit (s->hw); if (status != SANE_STATUS_GOOD) DBG (3, "sane_close: usb_high_scan_exit returned %s\n", sane_strstatus (status)); #endif if (s->hw->scan_buffer) { free (s->hw->scan_buffer); s->hw->scan_buffer = 0; } if (s->hw->temp_buffer) { free (s->hw->temp_buffer); s->hw->temp_buffer = 0; } free (handle); DBG (5, "sane_close: exit\n"); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { Mustek_Usb_Scanner *s = handle; if ((unsigned) option >= NUM_OPTIONS) return 0; DBG (5, "sane_get_option_descriptor: option = %s (%d)\n", s->opt[option].name, option); return s->opt + option; } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { Mustek_Usb_Scanner *s = handle; SANE_Status status; SANE_Word cap; SANE_Int myinfo = 0; DBG (5, "sane_control_option: start: action = %s, option = %s (%d)\n", (action == SANE_ACTION_GET_VALUE) ? "get" : (action == SANE_ACTION_SET_VALUE) ? "set" : (action == SANE_ACTION_SET_AUTO) ? "set_auto" : "unknown", s->opt[option].name, option); if (info) *info = 0; if (s->scanning) { DBG (1, "sane_control_option: don't call this function while " "scanning\n"); return SANE_STATUS_DEVICE_BUSY; } if (option >= NUM_OPTIONS || option < 0) { DBG (1, "sane_control_option: option %d >= NUM_OPTIONS || option < 0\n", option); return SANE_STATUS_INVAL; } cap = s->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) { DBG (2, "sane_control_option: option %d is inactive\n", option); return SANE_STATUS_INVAL; } if (action == SANE_ACTION_GET_VALUE) { switch (option) { /* word options: */ case OPT_NUM_OPTS: case OPT_RESOLUTION: case OPT_PREVIEW: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_THRESHOLD: case OPT_CUSTOM_GAMMA: *(SANE_Word *) val = s->val[option].w; break; /* word-array options: */ case OPT_GAMMA_VECTOR: case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: memcpy (val, s->val[option].wa, s->opt[option].size); break; /* string options: */ case OPT_MODE: strcpy (val, s->val[option].s); break; default: DBG (2, "sane_control_option: can't get unknown option %d\n", option); } } else if (action == SANE_ACTION_SET_VALUE) { if (!SANE_OPTION_IS_SETTABLE (cap)) { DBG (2, "sane_control_option: option %d is not settable\n", option); return SANE_STATUS_INVAL; } status = sanei_constrain_value (s->opt + option, val, &myinfo); if (status != SANE_STATUS_GOOD) { DBG (2, "sane_control_option: sanei_constrain_value returned %s\n", sane_strstatus (status)); return status; } switch (option) { /* (mostly) side-effect-free word options: */ case OPT_RESOLUTION: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: s->val[option].w = *(SANE_Word *) val; RIE (calc_parameters (s)); myinfo |= SANE_INFO_RELOAD_PARAMS; break; case OPT_THRESHOLD: s->val[option].w = *(SANE_Word *) val; break; /* Boolean */ case OPT_PREVIEW: s->val[option].w = *(SANE_Bool *) val; break; /* side-effect-free word-array options: */ case OPT_GAMMA_VECTOR: case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: memcpy (s->val[option].wa, val, s->opt[option].size); check_gamma_table (s->val[option].wa); break; case OPT_CUSTOM_GAMMA: s->val[OPT_CUSTOM_GAMMA].w = *(SANE_Word *) val; myinfo |= SANE_INFO_RELOAD_OPTIONS; if (s->val[OPT_CUSTOM_GAMMA].w == SANE_TRUE) { s->red_table = s->red_gamma_table; s->green_table = s->green_gamma_table; s->blue_table = s->blue_gamma_table; s->gray_table = s->gray_gamma_table; if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY) == 0) s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; else if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0) { s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; } } else { s->red_table = s->linear_gamma_table; s->green_table = s->linear_gamma_table; s->blue_table = s->linear_gamma_table; s->gray_table = s->linear_gamma_table; 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; } break; case OPT_MODE: if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); RIE (calc_parameters (s)); s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; if (strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) == 0) { s->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE; } else { s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE; if (s->val[OPT_CUSTOM_GAMMA].w == SANE_TRUE) { 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; } } myinfo |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; break; default: DBG (2, "sane_control_option: can't set unknown option %d\n", option); } } else { DBG (2, "sane_control_option: unknown action %d for option %d\n", action, option); return SANE_STATUS_INVAL; } if (info) *info = myinfo; DBG (5, "sane_control_option: exit\n"); return SANE_STATUS_GOOD; } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { Mustek_Usb_Scanner *s = handle; SANE_Status status; DBG (5, "sane_get_parameters: start\n"); RIE (calc_parameters (s)); if (params) *params = s->params; DBG (5, "sane_get_parameters: exit\n"); return SANE_STATUS_GOOD; } SANE_Status sane_start (SANE_Handle handle) { Mustek_Usb_Scanner *s = handle; SANE_Status status; SANE_String val; Colormode color_mode; SANE_Word dpi, x, y, width, height; DBG (5, "sane_start: start\n"); /* First make sure we have a current parameter set. Some of the parameters will be overwritten below, but that's OK. */ s->total_bytes = 0; s->total_lines = 0; RIE (calc_parameters (s)); if (s->width_dots <= 0) { DBG (0, "sane_start: top left x > bottom right x --- exiting\n"); return SANE_STATUS_INVAL; } if (s->height_dots <= 0) { DBG (0, "sane_start: top left y > bottom right y --- exiting\n"); return SANE_STATUS_INVAL; } val = s->val[OPT_MODE].s; if (!strcmp (val, SANE_VALUE_SCAN_MODE_LINEART)) color_mode = GRAY8; else if (!strcmp (val, SANE_VALUE_SCAN_MODE_GRAY)) color_mode = GRAY8; else /* Color */ color_mode = RGB24; dpi = SANE_UNFIX (s->val[OPT_RESOLUTION].w); x = s->tl_x_dots; y = s->tl_y_dots; width = s->width_dots; height = s->height_dots; if (!s->hw->is_prepared) { RIE (usb_high_scan_prepare (s->hw)); RIE (usb_high_scan_reset (s->hw)); } RIE (usb_high_scan_set_threshold (s->hw, 128)); RIE (usb_high_scan_embed_gamma (s->hw, NULL)); RIE (usb_high_scan_suggest_parameters (s->hw, dpi, x, y, width, height, color_mode)); RIE (usb_high_scan_setup_scan (s->hw, s->hw->scan_mode, s->hw->x_dpi, s->hw->y_dpi, 0, s->hw->x, s->hw->y, s->hw->width)); DBG (3, "sane_start: wanted: dpi=%d, x=%d, y=%d, width=%d, height=%d, " "scan_mode=%d\n", dpi, x, y, width, height, color_mode); DBG (3, "sane_start: got: x_dpi=%d, y_dpi=%d, x=%d, y=%d, width=%d, " "height=%d, scan_mode=%d\n", s->hw->x_dpi, s->hw->y_dpi, s->hw->x, s->hw->y, s->hw->width, s->hw->height, s->hw->scan_mode); s->scanning = SANE_TRUE; s->read_rows = s->hw->height; s->hw->line_switch = s->hw->height; s->hw->line_offset = 0; s->hw->scan_buffer_len = 0; DBG (5, "sane_start: exit\n"); return SANE_STATUS_GOOD; } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { Mustek_Usb_Scanner *s = handle; SANE_Word lines_to_read, lines_read; SANE_Status status; DBG (5, "sane_read: start\n"); if (!s) { DBG (1, "sane_read: handle is null!\n"); return SANE_STATUS_INVAL; } if (!buf) { DBG (1, "sane_read: buf is null!\n"); return SANE_STATUS_INVAL; } if (!len) { DBG (1, "sane_read: len is null!\n"); return SANE_STATUS_INVAL; } *len = 0; if (!s->scanning) { DBG (3, "sane_read: scan was cancelled, is over or has not been " "initiated yet\n"); return SANE_STATUS_CANCELLED; } if (s->hw->scan_buffer_len == 0) { if (s->read_rows > 0) { lines_to_read = SCAN_BUFFER_SIZE / (s->hw->width * s->hw->bpp / 8); if (lines_to_read > s->read_rows) lines_to_read = s->read_rows; s->hw->temp_buffer_start = s->hw->temp_buffer; s->hw->temp_buffer_len = (s->hw->width * s->hw->bpp / 8) * lines_to_read; DBG (4, "sane_read: reading %d source lines\n", lines_to_read); RIE (usb_high_scan_get_rows (s->hw, s->hw->temp_buffer, lines_to_read, SANE_FALSE)); RIE (fit_lines (s, s->hw->temp_buffer, s->hw->scan_buffer, lines_to_read, &lines_read)); s->read_rows -= lines_to_read; if ((s->total_lines + lines_read) > s->height_dots) lines_read = s->height_dots - s->total_lines; s->total_lines += lines_read; DBG (4, "sane_read: %d destination lines, %d total\n", lines_read, s->total_lines); s->hw->scan_buffer_start = s->hw->scan_buffer; s->hw->scan_buffer_len = (s->width_dots * s->bpp / 8) * lines_read; } else { DBG (4, "sane_read: scan finished -- exit\n"); return SANE_STATUS_EOF; } } if (s->hw->scan_buffer_len == 0) { DBG (4, "sane_read: scan finished -- exit\n"); return SANE_STATUS_EOF; } *len = MIN (max_len, (SANE_Int) s->hw->scan_buffer_len); memcpy (buf, s->hw->scan_buffer_start, *len); DBG (4, "sane_read: exit, read %d bytes from scan_buffer; " "%ld bytes remaining\n", *len, (long int) (s->hw->scan_buffer_len - *len)); s->hw->scan_buffer_len -= (*len); s->hw->scan_buffer_start += (*len); s->total_bytes += (*len); return SANE_STATUS_GOOD; } void sane_cancel (SANE_Handle handle) { Mustek_Usb_Scanner *s = handle; SANE_Status status; DBG (5, "sane_cancel: start\n"); status = usb_high_scan_stop_scan (s->hw); if (status != SANE_STATUS_GOOD) DBG (3, "sane_cancel: usb_high_scan_stop_scan returned `%s' for `%s'\n", sane_strstatus (status), s->hw->name); usb_high_scan_back_home (s->hw); if (status != SANE_STATUS_GOOD) DBG (3, "sane_cancel: usb_high_scan_back_home returned `%s' for `%s'\n", sane_strstatus (status), s->hw->name); if (s->scanning) { s->scanning = SANE_FALSE; if (s->total_bytes != (s->params.bytes_per_line * s->params.lines)) DBG (1, "sane_cancel: warning: scanned %d bytes, expected %d " "bytes\n", s->total_bytes, s->params.bytes_per_line * s->params.lines); else DBG (3, "sane_cancel: scan finished, scanned %d bytes\n", s->total_bytes); } else { DBG (4, "sane_cancel: scan has not been initiated yet, " "or it is already aborted\n"); } DBG (5, "sane_cancel: exit\n"); return; } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { Mustek_Usb_Scanner *s = handle; DBG (5, "sane_set_io_mode: handle = %p, non_blocking = %s\n", handle, non_blocking == SANE_TRUE ? "true" : "false"); if (!s->scanning) { DBG (1, "sane_set_io_mode: not scanning\n"); return SANE_STATUS_INVAL; } if (non_blocking) return SANE_STATUS_UNSUPPORTED; return SANE_STATUS_GOOD; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { Mustek_Usb_Scanner *s = handle; DBG (5, "sane_get_select_fd: handle = %p, fd = %p\n", handle, (void *) fd); if (!s->scanning) { DBG (1, "sane_get_select_fd: not scanning\n"); return SANE_STATUS_INVAL; } return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/mustek_usb.conf.in000066400000000000000000000014501456256263500203270ustar00rootroot00000000000000# 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 backends-1.3.0/backend/mustek_usb.h000066400000000000000000000045271456256263500172340ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2000 Mustek. Originally maintained by Tom Wang Copyright (C) 2001, 2002 by Henning Meier-Geinitz. 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, see . As a special exception, the authors of SANE give permission for additional uses of the libraries contained in this release of SANE. The exception is that, if you link a SANE library with other files to produce an executable, this does not by itself cause the resulting executable to be covered by the GNU General Public License. Your use of that executable is in no way restricted on account of linking the SANE library code into it. This exception does not, however, invalidate any other reasons why the executable file might be covered by the GNU General Public License. If you submit changes to SANE to the maintainers to be included in a subsequent release, you agree by submitting the changes that those changes may be distributed with this exception intact. If you write modifications of your own for SANE, it is your choice whether to permit this exception to apply to your modifications. If you do not wish that, delete this exception notice. This file implements a SANE backend for Mustek 1200UB and similar USB flatbed scanners. */ #ifndef mustek_usb_h #define mustek_usb_h #include #include "mustek_usb_high.h" #define ENABLE(OPTION) s->opt[OPTION].cap &= ~SANE_CAP_INACTIVE #define DISABLE(OPTION) s->opt[OPTION].cap |= SANE_CAP_INACTIVE #define IS_ACTIVE(OPTION) (((s->opt[OPTION].cap) & SANE_CAP_INACTIVE) == 0) #define MUSTEK_USB_CONFIG_FILE "mustek_usb.conf" #define SCAN_BUFFER_SIZE (64 * 1024) #endif /* mustek_usb_h */ backends-1.3.0/backend/mustek_usb2.c000066400000000000000000002054001456256263500173020ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2000-2005 Mustek. Originally maintained by Mustek Copyright (C) 2001-2005 by Henning Meier-Geinitz. 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, see . 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 the Mustek BearPaw 2448 TA Pro and similar USB2 scanners. */ #define BUILD 10 #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/saneopts.h" #define BACKEND_NAME mustek_usb2 #include "../include/sane/sanei_backend.h" #include "mustek_usb2_high.c" #include "mustek_usb2.h" static SANE_Int num_devices; static const SANE_Device **devlist = 0; static const SANE_Range u8_range = { 0, /* minimum */ 255, /* maximum */ 0 /* quantization */ }; static SANE_Range x_range = { SANE_FIX (0.0), /* minimum */ SANE_FIX (8.3 * MM_PER_INCH), /* maximum */ SANE_FIX (0.0) /* quantization */ }; static SANE_Range y_range = { SANE_FIX (0.0), /* minimum */ SANE_FIX (11.6 * MM_PER_INCH), /* maximum */ SANE_FIX (0.0) /* quantization */ }; static SANE_Range gamma_range = { SANE_FIX (0.01), /* minimum */ SANE_FIX (5.0), /* maximum */ SANE_FIX (0.01) /* quantization */ }; static SANE_String_Const mode_list[] = { SANE_I18N ("Color48"), SANE_I18N ("Color24"), SANE_I18N ("Gray16"), SANE_I18N ("Gray8"), SANE_VALUE_SCAN_MODE_LINEART, 0 }; static SANE_String_Const negative_mode_list[] = { SANE_I18N ("Color24"), 0 }; static SANE_String_Const source_list[] = { SANE_I18N ("Reflective"), SANE_I18N ("Positive"), SANE_I18N ("Negative"), 0 }; static Scanner_Model mustek_A2nu2_model = { "mustek-A2nu2", /* Name */ "Mustek", /* Device vendor string */ "BearPaw 2448TA Pro", /* Device model name */ "", /* Name of the firmware file */ {1200, 600, 300, 150, 75, 0}, /* possible resolutions */ SANE_FIX (0.0), /* Start of scan area in mm (x) */ SANE_FIX (0.0), /* Start of scan area in mm (y) */ SANE_FIX (8.3 * MM_PER_INCH), /* Size of scan area in mm (x) */ SANE_FIX (11.6 * MM_PER_INCH), /* Size of scan area in mm (y) */ 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 (1.46 * MM_PER_INCH), /* Size of scan area in TA mode in mm (x) */ SANE_FIX (6.45 * MM_PER_INCH), /* Size of scan area in TA mode in mm (y) */ 0, /* Order of the CCD/CIS colors 0:RO_RGB 1:RO_BGR */ SANE_FIX (2.0), /* Default gamma value */ SANE_FALSE, /* Is this a CIS scanner? */ 0 /* Which flags are needed for this scanner? */ /* Setup and tested */ }; /* Forward declarations */ static SANE_Bool GetDeviceStatus (void); static SANE_Bool PowerControl (SANE_Bool isLampOn, SANE_Bool isTaLampOn); static SANE_Bool CarriageHome (void); static SANE_Bool SetParameters (LPSETPARAMETERS pSetParameters); static SANE_Bool GetParameters (LPGETPARAMETERS pGetParameters); static SANE_Bool StartScan (void); static SANE_Bool ReadScannedData (LPIMAGEROWS pImageRows); static SANE_Bool StopScan (void); static SANE_Bool IsTAConnected (void); static void AutoLevel (SANE_Byte *lpSource, SCANMODE scanMode, unsigned short ScanLines, unsigned int BytesPerLine); static size_t max_string_size (const SANE_String_Const strings[]); static SANE_Status calc_parameters (Mustek_Scanner * s); #ifdef SANE_UNUSED static SANE_Bool GetGammaInfo (LPGAMMAINFO pGamaInfo); static SANE_Bool GetKeyStatus (SANE_Byte * pKey); static void QBetChange (SANE_Byte *lpSource, SCANMODE scanMode, unsigned short ScanLines, unsigned int BytesPerLine); static void QBETDetectAutoLevel (void *pDIB, unsigned int ImageWidth, unsigned int ImageHeight); #endif 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; } static SANE_Status calc_parameters (Mustek_Scanner * s) { SANE_String val, val_source; val = s->val[OPT_MODE].s; val_source = s->val[OPT_SOURCE].s; s->params.last_frame = SANE_TRUE; if (strcmp (val, "Color48") == 0) /* Color48 */ { s->params.format = SANE_FRAME_RGB; s->params.depth = 16; s->setpara.smScanMode = SM_RGB48; if (s->val[OPT_PREVIEW].w) { DBG (DBG_DET, "calc_parameters : preview set ScanMode SM_RGB24\n"); s->params.depth = 8; s->setpara.smScanMode = SM_RGB24; } } else if (strcmp (val, "Color24") == 0) /* Color24 */ { s->params.format = SANE_FRAME_RGB; s->params.depth = 8; s->setpara.smScanMode = SM_RGB24; } else if (strcmp (val, "Gray16") == 0) { s->params.format = SANE_FRAME_GRAY; s->params.depth = 16; s->setpara.smScanMode = SM_GRAY16; if (s->val[OPT_PREVIEW].w) { s->params.depth = 8; DBG (DBG_DET, "calc_parameters : preview set ScanMode SM_GRAY\n"); s->setpara.smScanMode = SM_GRAY; } } else if (strcmp (val, "Gray8") == 0) { s->params.format = SANE_FRAME_GRAY; s->params.depth = 8; s->setpara.smScanMode = SM_GRAY; } else if (strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) == 0) { s->params.format = SANE_FRAME_GRAY; s->params.depth = 1; s->setpara.smScanMode = SM_TEXT; } /*set Scan Source */ DBG (DBG_DET, "calc_parameters :scan Source = %s\n", val_source); if (strcmp (val_source, "Reflective") == 0) { s->setpara.ssScanSource = SS_Reflective; } else if (strcmp (val_source, "Positive") == 0) { s->setpara.ssScanSource = SS_Positive; } else if (strcmp (val_source, "Negative") == 0) { s->setpara.ssScanSource = SS_Negative; } s->setpara.fmArea.x1 = (unsigned short) ((SANE_UNFIX (s->val[OPT_TL_X].w) * 300.0) / MM_PER_INCH + 0.5); s->setpara.fmArea.x2 = (unsigned short) ((SANE_UNFIX (s->val[OPT_BR_X].w) * 300.0) / MM_PER_INCH + 0.5); s->setpara.fmArea.y1 = (unsigned short) ((SANE_UNFIX (s->val[OPT_TL_Y].w) * 300.0) / MM_PER_INCH + 0.5); s->setpara.fmArea.y2 = (unsigned short) ((SANE_UNFIX (s->val[OPT_BR_Y].w) * 300.0) / MM_PER_INCH + 0.5); if (s->val[OPT_PREVIEW].w) { s->setpara.fmArea.y1 = s->setpara.fmArea.y1 + PER_ADD_START_LINES; s->setpara.fmArea.x1 += PRE_ADD_START_X; } /*just for range bug. */ s->setpara.pfPixelFlavor = PF_BlackIs0; s->setpara.wLinearThreshold = s->val[OPT_THRESHOLD].w; s->setpara.wTargetDPI = s->val[OPT_RESOLUTION].w; if (s->val[OPT_PREVIEW].w) { s->setpara.wTargetDPI = 75; } s->setpara.pGammaTable = NULL; s->params.pixels_per_line = (SANE_Int) ((s->setpara.fmArea.x2 - s->setpara.fmArea.x1) * s->setpara.wTargetDPI / 300.0 + 0.5); switch (s->params.format) { case SANE_FRAME_RGB: if (s->params.depth == 8) s->params.bytes_per_line = s->params.pixels_per_line * 3; if (s->params.depth == 16) s->params.bytes_per_line = s->params.pixels_per_line * 6; break; case SANE_FRAME_GRAY: if (s->params.depth == 1) s->params.bytes_per_line = s->params.pixels_per_line / 8; if (s->params.depth == 8) s->params.bytes_per_line = s->params.pixels_per_line; if (s->params.depth == 16) s->params.bytes_per_line = s->params.pixels_per_line * 2; break; default: DBG (DBG_DET, "sane_star:sane params .format = %d\n", s->params.format); } s->params.lines = (SANE_Int) ((s->setpara.fmArea.y2 - s->setpara.fmArea.y1) * s->setpara.wTargetDPI / 300 + 0.5); DBG (DBG_FUNC, "calc_parameters: end\n"); return SANE_STATUS_GOOD; } static SANE_Status init_options (Mustek_Scanner * s) { SANE_Int option, count; SANE_Word *dpi_list; /*Resolution Support */ DBG (DBG_FUNC, "init_options: start\n"); memset (s->opt, 0, sizeof (s->opt)); memset (s->val, 0, sizeof (s->val)); for (option = 0; option < NUM_OPTIONS; ++option) { s->opt[option].size = sizeof (SANE_Word); s->opt[option].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } /* Option num */ 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].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* "Mode" group: */ s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode"); s->opt[OPT_MODE_GROUP].desc = ""; s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MODE_GROUP].size = 0; s->opt[OPT_MODE_GROUP].cap = 0; s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* scan mode */ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; 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].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_MODE].size = max_string_size (mode_list); s->opt[OPT_MODE].constraint.string_list = mode_list; s->val[OPT_MODE].s = strdup ("Color24"); /* Scan Source */ s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE; s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE; s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE; s->opt[OPT_SOURCE].type = SANE_TYPE_STRING; s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[OPT_SOURCE].size = max_string_size (source_list); s->opt[OPT_SOURCE].constraint.string_list = source_list; s->val[OPT_SOURCE].s = strdup ("Reflective"); if (!IsTAConnected ()) { DISABLE (OPT_SOURCE); } /* resolution */ for (count = 0; s->model.dpi_values[count] != 0; count++) { } dpi_list = malloc ((count + 1) * sizeof (SANE_Word)); if (!dpi_list) return SANE_STATUS_NO_MEM; dpi_list[0] = count; for (count = 0; s->model.dpi_values[count] != 0; count++) dpi_list[count + 1] = s->model.dpi_values[count]; s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; s->opt[OPT_RESOLUTION].constraint.word_list = dpi_list; s->val[OPT_RESOLUTION].w = 300; /* preview */ s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; s->val[OPT_PREVIEW].w = SANE_FALSE; /* "Debug" group: */ s->opt[OPT_DEBUG_GROUP].title = SANE_I18N ("Debugging Options"); s->opt[OPT_DEBUG_GROUP].desc = ""; s->opt[OPT_DEBUG_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_DEBUG_GROUP].size = 0; s->opt[OPT_DEBUG_GROUP].cap = 0; s->opt[OPT_DEBUG_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* auto warmup */ s->opt[OPT_AUTO_WARMUP].name = "auto-warmup"; s->opt[OPT_AUTO_WARMUP].title = SANE_I18N ("Automatic warmup"); s->opt[OPT_AUTO_WARMUP].desc = SANE_I18N ("Warm-up until the lamp's brightness is constant " "instead of insisting on 40 seconds warm-up time."); s->opt[OPT_AUTO_WARMUP].type = SANE_TYPE_BOOL; s->opt[OPT_AUTO_WARMUP].unit = SANE_UNIT_NONE; s->opt[OPT_AUTO_WARMUP].constraint_type = SANE_CONSTRAINT_NONE; s->val[OPT_AUTO_WARMUP].w = SANE_FALSE; if (s->model.is_cis) DISABLE (OPT_AUTO_WARMUP); /* "Enhancement" group: */ s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement"); s->opt[OPT_ENHANCEMENT_GROUP].desc = ""; s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_ENHANCEMENT_GROUP].size = 0; s->opt[OPT_ENHANCEMENT_GROUP].cap = 0; s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* 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_INT; s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE; s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_THRESHOLD].constraint.range = &u8_range; s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; s->val[OPT_THRESHOLD].w = DEF_LINEARTTHRESHOLD; /* internal gamma value */ s->opt[OPT_GAMMA_VALUE].name = "gamma-value"; s->opt[OPT_GAMMA_VALUE].title = SANE_I18N ("Gamma value"); s->opt[OPT_GAMMA_VALUE].desc = SANE_I18N ("Sets the gamma value of all channels."); s->opt[OPT_GAMMA_VALUE].type = SANE_TYPE_FIXED; s->opt[OPT_GAMMA_VALUE].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VALUE].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VALUE].constraint.range = &gamma_range; s->opt[OPT_GAMMA_VALUE].cap |= SANE_CAP_EMULATED; s->val[OPT_GAMMA_VALUE].w = s->model.default_gamma_value; DISABLE (OPT_GAMMA_VALUE); /* "Geometry" group: */ s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry"); s->opt[OPT_GEOMETRY_GROUP].desc = ""; s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_GEOMETRY_GROUP].size = 0; s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; x_range.max = s->model.x_size; y_range.max = s->model.y_size; /* top-left x */ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; s->opt[OPT_TL_X].unit = SANE_UNIT_MM; s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_X].constraint.range = &x_range; s->val[OPT_TL_X].w = 0; /* top-left y */ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_Y].constraint.range = &y_range; s->val[OPT_TL_Y].w = 0; /* bottom-right x */ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; s->opt[OPT_BR_X].unit = SANE_UNIT_MM; s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_X].constraint.range = &x_range; s->val[OPT_BR_X].w = x_range.max; /* bottom-right y */ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_Y].constraint.range = &y_range; s->val[OPT_BR_Y].w = y_range.max; calc_parameters (s); DBG (DBG_FUNC, "init_options: exit\n"); return SANE_STATUS_GOOD; } /***************************** Code from spicall.c *****************************/ static SANE_Byte * g_lpNegImageData = NULL; static SANE_Bool g_bIsFirstGetNegData = TRUE; static SANE_Bool g_bIsMallocNegData = FALSE; static unsigned int g_dwAlreadyGetNegLines = 0; /********************************************************************** Author: Jack Date: 2005/05/13 Routine Description: Check the device connect status Parameters: none Return value: if the device is connected return TRUE else return FALSE ***********************************************************************/ static SANE_Bool GetDeviceStatus () { DBG (DBG_FUNC, "GetDeviceStatus: start\n"); return MustScanner_GetScannerState (); } /********************************************************************** Author: Jack Date: 2005/05/13 Routine Description: Turn the lamp on or off Parameters: isLampOn: turn the lamp on or off isTALampOn: turn the TA lamp on or off Return value: if operation success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool PowerControl (SANE_Bool isLampOn, SANE_Bool isTALampOn) { DBG (DBG_FUNC, "PowerControl: start\n"); return MustScanner_PowerControl (isLampOn, isTALampOn); } /********************************************************************** Author: Jack Date: 2005/05/13 Routine Description: Turn the carriage home Parameters: none Return value: if the operation success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool CarriageHome () { DBG (DBG_FUNC, "CarriageHome: start\n"); return MustScanner_BackHome (); } #ifdef SANE_UNUSED /********************************************************************** Author: Jack Date: 2005/05/13 Routine Description: Get gamma input/output bit count Parameters: pGammaInfo: the gamma information Return value: if the operation success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool GetGammaInfo (LPGAMMAINFO pGammaInfo) { DBG (DBG_FUNC, "GetGammaInfo: start\n"); switch (pGammaInfo->smScanMode) { case SM_GRAY: pGammaInfo->wInputGammaBits = 12; pGammaInfo->wOutputGammaBits = 8; break; case SM_RGB24: pGammaInfo->wInputGammaBits = 12; pGammaInfo->wOutputGammaBits = 8; break; case SM_GRAY16: pGammaInfo->wInputGammaBits = 16; pGammaInfo->wOutputGammaBits = 16; break; case SM_RGB48: pGammaInfo->wInputGammaBits = 16; pGammaInfo->wOutputGammaBits = 16; break; default: pGammaInfo->wInputGammaBits = 0; pGammaInfo->wOutputGammaBits = 0; return FALSE; } DBG (DBG_FUNC, "GetGammaInfo: exit\n"); return TRUE; } #endif /********************************************************************** Author: Jack Date: 2005/05/13 Routine Description: set scan parameters Parameters: pSetParameters: the information of scanning Return value: if the operation success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool SetParameters (LPSETPARAMETERS pSetParameters) { unsigned short X1inTargetDpi; unsigned short Y1inTargetDpi; unsigned short X2inTargetDpi; unsigned short Y2inTargetDpi; DBG (DBG_FUNC, "SetParameters: start\n"); /*0. Reset */ if (ST_Reflective == g_ScanType) { Reflective_Reset (); } else { Transparent_Reset (); } /*1. Scan mode */ switch (pSetParameters->smScanMode) { case SM_TEXT: g_tiTarget.cmColorMode = CM_TEXT; break; case SM_GRAY: g_tiTarget.cmColorMode = CM_GRAY8; break; case SM_GRAY16: g_tiTarget.cmColorMode = CM_GRAY16; break; case SM_RGB24: g_tiTarget.cmColorMode = CM_RGB24; break; case SM_RGB48: g_tiTarget.cmColorMode = CM_RGB48; break; default: return FALSE; } /*2. Scan source */ g_ssScanSource = pSetParameters->ssScanSource; g_tiTarget.bScanSource = pSetParameters->ssScanSource; if (SS_Reflective == pSetParameters->ssScanSource) { g_ScanType = ST_Reflective; } else if (SS_Positive == pSetParameters->ssScanSource || SS_Negative == pSetParameters->ssScanSource || SS_ADF == pSetParameters->ssScanSource) { g_ScanType = ST_Transparent; } else { DBG (DBG_ERR, "SetParameters: ScanSource error\n"); return FALSE; } /*3. pixel flavor */ if (PF_BlackIs0 == pSetParameters->pfPixelFlavor || PF_WhiteIs0 == pSetParameters->pfPixelFlavor) { g_PixelFlavor = pSetParameters->pfPixelFlavor; } else { DBG (DBG_ERR, "SetParameters: PixelFlavor error\n"); return FALSE; } /*4. Scan area */ if (pSetParameters->fmArea.x1 >= pSetParameters->fmArea.x2) { DBG (DBG_ERR, "SetParameters: x1 > x2, error\n"); return FALSE; } if (pSetParameters->fmArea.y1 >= pSetParameters->fmArea.y2) { DBG (DBG_ERR, "SetParameters: y1 >= y2, error\n"); return FALSE; } if (pSetParameters->fmArea.x2 > MAX_SCANNING_WIDTH) /* Just for A4 size */ { DBG (DBG_ERR, "SetParameters: x2 > MAX_SCANNING_WIDTH, error\n"); return FALSE; } if (pSetParameters->fmArea.y2 > MAX_SCANNING_HEIGHT) /* Just for A4 size */ { DBG (DBG_ERR, "SetParameters: y2 > MAX_SCANNING_HEIGHT, error\n"); return FALSE; } X1inTargetDpi = (unsigned short) ((unsigned int) (pSetParameters->fmArea.x1) * (unsigned int) (pSetParameters->wTargetDPI) / 300L); Y1inTargetDpi = (unsigned short) ((unsigned int) (pSetParameters->fmArea.y1) * (unsigned int) (pSetParameters->wTargetDPI) / 300L); X2inTargetDpi = (unsigned short) ((unsigned int) (pSetParameters->fmArea.x2) * (unsigned int) (pSetParameters->wTargetDPI) / 300L); Y2inTargetDpi = (unsigned short) ((unsigned int) (pSetParameters->fmArea.y2) * (unsigned int) (pSetParameters->wTargetDPI) / 300L); g_tiTarget.isOptimalSpeed = TRUE; g_tiTarget.wDpi = pSetParameters->wTargetDPI; g_tiTarget.wX = X1inTargetDpi; g_tiTarget.wY = Y1inTargetDpi; g_tiTarget.wWidth = X2inTargetDpi - X1inTargetDpi; g_tiTarget.wHeight = Y2inTargetDpi - Y1inTargetDpi; DBG (DBG_INFO, "SetParameters: g_tiTarget.wDpi=%d\n", g_tiTarget.wDpi); DBG (DBG_INFO, "SetParameters: g_tiTarget.wX=%d\n", g_tiTarget.wX); DBG (DBG_INFO, "SetParameters: g_tiTarget.wY=%d\n", g_tiTarget.wY); DBG (DBG_INFO, "SetParameters: g_tiTarget.wWidth=%d\n", g_tiTarget.wWidth); DBG (DBG_INFO, "SetParameters: g_tiTarget.wHeight=%d\n", g_tiTarget.wHeight); /*5.Prepare */ if (FALSE == MustScanner_Prepare (g_tiTarget.bScanSource)) { DBG (DBG_ERR, "SetParameters: MustScanner_Prepare fail\n"); return FALSE; } /*6. Linear threshold */ if (pSetParameters->wLinearThreshold > 256 && pSetParameters->smScanMode == SM_TEXT) { DBG (DBG_ERR, "SetParameters: LinearThreshold error\n"); return FALSE; } else { g_wLineartThreshold = pSetParameters->wLinearThreshold; } /*7. Gamma table */ if (NULL != pSetParameters->pGammaTable) { DBG (DBG_INFO, "SetParameters: IN gamma table not NULL\n"); g_pGammaTable = pSetParameters->pGammaTable; g_isSelfGamma = FALSE; } else if (pSetParameters->smScanMode == SM_GRAY || pSetParameters->smScanMode == SM_RGB24) { unsigned short i; SANE_Byte byGammaData; double pow_d; double pow_z = (double) 10 / 16.0; g_pGammaTable = (unsigned short *) malloc (sizeof (unsigned short) * 4096 * 3); DBG (DBG_INFO, "SetParameters: gamma table malloc %ld Bytes\n", (long int) sizeof (unsigned short) * 4096 * 3); DBG (DBG_INFO, "SetParameters: address of g_pGammaTable=%p\n", (void *) g_pGammaTable); if (NULL == g_pGammaTable) { DBG (DBG_ERR, "SetParameters: gamma table malloc fail\n"); return FALSE; } g_isSelfGamma = TRUE; for (i = 0; i < 4096; i++) { pow_d = (double) i / (double) 4096; byGammaData = (SANE_Byte) (pow (pow_d, pow_z) * 255); *(g_pGammaTable + i) = byGammaData; *(g_pGammaTable + i + 4096) = byGammaData; *(g_pGammaTable + i + 8192) = byGammaData; } } else if (pSetParameters->smScanMode == SM_GRAY16 || pSetParameters->smScanMode == SM_RGB48) { unsigned int i, wGammaData; g_pGammaTable = (unsigned short *) malloc (sizeof (unsigned short) * 65536 * 3); if (g_pGammaTable == NULL) { DBG (DBG_ERR, "SetParameters: gamma table malloc fail\n"); return FALSE; } g_isSelfGamma = TRUE; for (i = 0; i < 65536; i++) { wGammaData = (unsigned short) (pow ((((float) i) / 65536.0), (((float) 10) / 16.0)) * 65535); *(g_pGammaTable + i) = wGammaData; *(g_pGammaTable + i + 65536) = wGammaData; *(g_pGammaTable + i + 65536 * 2) = wGammaData; } } else { DBG (DBG_INFO, "SetParameters: set g_pGammaTable to NULL\n"); g_pGammaTable = NULL; } DBG (DBG_FUNC, "SetParameters: exit\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/13 Routine Description: get the optical dpi and scan area Parameters: pGetParameters: the information of scan Return value: if the operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool GetParameters (LPGETPARAMETERS pGetParameters) { DBG (DBG_FUNC, "GetParameters: start\n"); if (ST_Reflective == g_ScanType) { if (FALSE == Reflective_ScanSuggest (&g_tiTarget, &g_ssSuggest)) { DBG (DBG_ERR, "GetParameters: Reflective_ScanSuggest error\n"); return FALSE; } } else { if (FALSE == Transparent_ScanSuggest (&g_tiTarget, &g_ssSuggest)) { DBG (DBG_ERR, "GetParameters: Transparent_ScanSuggest error\n"); return FALSE; } } pGetParameters->wSourceXDPI = g_ssSuggest.wXDpi; pGetParameters->wSourceYDPI = g_ssSuggest.wYDpi; pGetParameters->dwLength = (unsigned int) g_ssSuggest.wHeight; pGetParameters->dwLineByteWidth = g_ssSuggest.dwBytesPerRow; DBG (DBG_FUNC, "GetParameters: exit\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/13 Routine Description: start scan image Parameters: none Return value: if operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool StartScan () { DBG (DBG_FUNC, "StartScan: start\n"); if (ST_Reflective == g_ScanType) { DBG (DBG_INFO, "StartScan: g_ScanType==ST_Reflective\n"); return Reflective_SetupScan (g_ssSuggest.cmScanMode, g_ssSuggest.wXDpi, g_ssSuggest.wYDpi, PF_BlackIs0, g_ssSuggest.wX, g_ssSuggest.wY, g_ssSuggest.wWidth, g_ssSuggest.wHeight); } else { DBG (DBG_INFO, "StartScan: g_ScanType==ST_Transparent\n"); return Transparent_SetupScan (g_ssSuggest.cmScanMode, g_ssSuggest.wXDpi, g_ssSuggest.wYDpi, PF_BlackIs0, g_ssSuggest.wX, g_ssSuggest.wY, g_ssSuggest.wWidth, g_ssSuggest.wHeight); } } /********************************************************************** Author: Jack Date: 2005/05/14 Routine Description: Read the scanner data Parameters: pImageRows: the information of the data Return value: if the operation is seccuss return TRUE else return FALSE ***********************************************************************/ static SANE_Bool ReadScannedData (LPIMAGEROWS pImageRows) { SANE_Bool isRGBInvert; unsigned short Rows = 0; SANE_Byte *lpBlock = (SANE_Byte *) pImageRows->pBuffer; SANE_Byte *lpReturnData = (SANE_Byte *) pImageRows->pBuffer; int i = 0; DBG (DBG_FUNC, "ReadScannedData: start\n"); if (pImageRows->roRgbOrder == RO_RGB) isRGBInvert = FALSE; else isRGBInvert = TRUE; Rows = pImageRows->wWantedLineNum; DBG (DBG_INFO, "ReadScannedData: wanted Rows = %d\n", Rows); if (ST_Reflective == g_ScanType) { if (FALSE == Reflective_GetRows (lpBlock, &Rows, isRGBInvert)) return FALSE; } else if (SS_Positive == g_ssScanSource) { if (FALSE == Transparent_GetRows (lpBlock, &Rows, isRGBInvert)) return FALSE; } pImageRows->wXferedLineNum = Rows; if (g_PixelFlavor == PF_WhiteIs0 || g_ScanMode == CM_TEXT) { int TotalSize = Rows * g_ssSuggest.dwBytesPerRow; for (i = 0; i < TotalSize; i++) { *(lpBlock++) ^= 0xff; } } if (SS_Negative == g_ssScanSource) { DBG (DBG_INFO, "ReadScannedData: deal with the Negative\n"); if (g_bIsFirstGetNegData) { unsigned int TotalImgeSize = g_SWHeight * g_ssSuggest.dwBytesPerRow; g_lpNegImageData = (SANE_Byte *) malloc (TotalImgeSize); if (NULL != g_lpNegImageData) { SANE_Byte * lpTempData = g_lpNegImageData; DBG (DBG_INFO, "ReadScannedData: malloc the negative data is success!\n"); g_bIsMallocNegData = TRUE; if (!Transparent_GetRows (g_lpNegImageData, &g_SWHeight, isRGBInvert)) { return FALSE; } DBG (DBG_INFO, "ReadScannedData: get image data is over!\n"); for (i = 0; i < (int) TotalImgeSize; i++) { *(g_lpNegImageData++) ^= 0xff; } g_lpNegImageData = lpTempData; AutoLevel (g_lpNegImageData, g_ScanMode, g_SWHeight, g_ssSuggest.dwBytesPerRow); DBG (DBG_INFO, "ReadScannedData: autolevel is ok\n"); } g_bIsFirstGetNegData = FALSE; } if (g_bIsMallocNegData) { memcpy (pImageRows->pBuffer, g_lpNegImageData + g_ssSuggest.dwBytesPerRow * g_dwAlreadyGetNegLines, g_ssSuggest.dwBytesPerRow * Rows); DBG (DBG_INFO, "ReadScannedData: copy the data over!\n"); g_dwAlreadyGetNegLines += Rows; if (g_dwAlreadyGetNegLines >= g_SWHeight) { DBG (DBG_INFO, "ReadScannedData: free the image data!\n"); free (g_lpNegImageData); g_lpNegImageData = NULL; g_bIsFirstGetNegData = TRUE; g_dwAlreadyGetNegLines = 0; g_bIsMallocNegData = FALSE; } } else { int TotalSize = Rows * g_ssSuggest.dwBytesPerRow; DBG (DBG_INFO, "ReadScannedData: malloc the negative data is fail!\n"); if (!Transparent_GetRows (lpReturnData, &Rows, isRGBInvert)) { return FALSE; } for (i = 0; i < TotalSize; i++) { *(lpReturnData++) ^= 0xff; } pImageRows->wXferedLineNum = Rows; g_dwAlreadyGetNegLines += Rows; if (g_dwAlreadyGetNegLines >= g_SWHeight) { g_bIsFirstGetNegData = TRUE; g_dwAlreadyGetNegLines = 0; g_bIsMallocNegData = FALSE; } } } DBG (DBG_FUNC, "ReadScannedData: leave ReadScannedData\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/14 Routine Description: Stop scan Parameters: none Return value: if operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool StopScan () { SANE_Bool rt; int i; DBG (DBG_FUNC, "StopScan: start\n"); /*stop read data and kill thread */ if (ST_Reflective == g_ScanType) { rt = Reflective_StopScan (); } else { rt = Transparent_StopScan (); } /*free gamma table */ if (g_isSelfGamma && g_pGammaTable != NULL) { for (i = 0; i < 20; i++) { if (!g_isScanning) { free (g_pGammaTable); g_pGammaTable = NULL; break; } else { sleep (1); /*waiting ReadScannedData return. */ } } } /*free image buffer */ if (g_lpReadImageHead != NULL) { free (g_lpReadImageHead); g_lpReadImageHead = NULL; } DBG (DBG_FUNC, "StopScan: exit\n"); return rt; } /********************************************************************** Author: Jack Date: 2005/05/14 Routine Description: Check the status of TA Parameters: none Return value: if operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool IsTAConnected () { SANE_Bool hasTA; DBG (DBG_FUNC, "StopScan: start\n"); if (Asic_Open (&g_chip, g_pDeviceFile) != SANE_STATUS_GOOD) { return FALSE; } if (Asic_IsTAConnected (&g_chip, &hasTA) != SANE_STATUS_GOOD) { Asic_Close (&g_chip); return FALSE; } Asic_Close (&g_chip); DBG (DBG_FUNC, "StopScan: exit\n"); return hasTA; } #ifdef SANE_UNUSED /********************************************************************** Author: Jack Date: 2005/05/14 Routine Description: Get the status of the HK Parameters: pKey: the status of key Return value: if the operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool GetKeyStatus (SANE_Byte * pKey) { SANE_Byte pKeyTemp = 0x00; SANE_Status status = Asic_CheckFunctionKey (&g_chip, &pKeyTemp); DBG (DBG_FUNC, "GetKeyStatus: start\n"); if (SANE_STATUS_GOOD != Asic_Open (&g_chip, g_pDeviceFile)) { DBG (DBG_ERR, "GetKeyStatus: Asic_Open is fail\n"); return FALSE; } if (SANE_STATUS_GOOD != status) { DBG (DBG_ERR, "GetKeyStatus: Asic_CheckFunctionKey is fail\n"); return FALSE; } if (0x01 == pKeyTemp) { *pKey = 0x01; /*Scan key pressed */ } if (0x02 == pKeyTemp) { *pKey = 0x02; /*Copy key pressed */ } if (0x04 == pKeyTemp) { *pKey = 0x03; /*Fax key pressed */ } if (0x08 == pKeyTemp) { *pKey = 0x04; /*Email key pressed */ } if (0x10 == pKeyTemp) { *pKey = 0x05; /*Panel key pressed */ } if (SANE_STATUS_GOOD != Asic_Close (&g_chip)) { DBG (DBG_ERR, "GetKeyStatus: Asic_Close is fail\n"); return FALSE; } DBG (DBG_FUNC, "GetKeyStatus: exit\n"); return TRUE; } #endif /********************************************************************** Author: Jack Date: 2005/05/14 Routine Description: Deal with the image with auto level Parameters: lpSource: the data of image scanMode: the scan mode ScanLines: the rows of image BytesPerLine: the bytes of per line Return value: none ***********************************************************************/ static void AutoLevel (SANE_Byte *lpSource, SCANMODE scanMode, unsigned short ScanLines, unsigned int BytesPerLine) { int ii; unsigned int i, j; unsigned int tLines, CountPixels, TotalImgSize; unsigned short R, G, B, max_R, max_G, max_B, min_R, min_G, min_B; float fmax_R, fmax_G, fmax_B; unsigned int sum_R = 0, sum_G = 0, sum_B = 0; unsigned int hisgram_R[256], hisgram_G[256], hisgram_B[256]; unsigned int iWidth = BytesPerLine / 3; unsigned int iHeight = ScanLines; SANE_Byte *pbmpdata = (SANE_Byte *) lpSource; unsigned short imin_threshold[3]; unsigned short imax_threshold[3]; DBG (DBG_FUNC, "AutoLevel: start\n"); if (scanMode != CM_RGB24ext) { return; } i = j = 0; tLines = CountPixels = TotalImgSize = 0; TotalImgSize = iWidth * iHeight; for (i = 0; i < 256; i++) { hisgram_R[i] = 0; hisgram_G[i] = 0; hisgram_B[i] = 0; } DBG (DBG_INFO, "AutoLevel: init data is over\n"); /* Find min , max, mean */ max_R = max_G = max_B = 0; min_R = min_G = min_B = 255; tLines = 0; DBG (DBG_INFO, "AutoLevel: iHeight = %d, iWidth = %d\n", iHeight, iWidth); for (j = 0; j < iHeight; j++) { tLines = j * iWidth * 3; for (i = 0; i < iWidth; i++) { R = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 2)); G = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 1)); B = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3)); max_R = _MAX (R, max_R); max_G = _MAX (G, max_G); max_B = _MAX (B, max_B); min_R = _MIN (R, min_R); min_G = _MIN (G, min_G); min_B = _MIN (B, min_B); hisgram_R[(SANE_Byte) R]++; hisgram_G[(SANE_Byte) G]++; hisgram_B[(SANE_Byte) B]++; sum_R += R; sum_G += G; sum_B += B; *(pbmpdata + (tLines + i * 3 + 2)) = (SANE_Byte) R; *(pbmpdata + (tLines + i * 3 + 1)) = (SANE_Byte) G; *(pbmpdata + (tLines + i * 3)) = (SANE_Byte) B; CountPixels++; } } DBG (DBG_INFO, "AutoLevel: Find min , max is over!\n"); imin_threshold[0] = 0; imin_threshold[1] = 0; imin_threshold[2] = 0; imax_threshold[0] = 0; imax_threshold[1] = 0; imax_threshold[2] = 0; for (ii = 0; ii < 256; ii++) { if (hisgram_R[ii] > 0) if (hisgram_R[ii] >= imin_threshold[0]) { min_R = ii; break; } } for (ii = 255; ii >= 0; ii--) { if (hisgram_R[ii] > 0) if (hisgram_R[ii] >= imax_threshold[0]) { max_R = ii; break; } } for (ii = 0; ii < 256; ii++) { if (hisgram_G[ii] > 0) if (hisgram_G[ii] >= imin_threshold[1]) { min_G = ii; break; } } for (ii = 255; ii >= 0; ii--) { if (hisgram_G[ii] > 0) if (hisgram_G[ii] >= imax_threshold[1]) { max_G = ii; break; } } for (ii = 0; ii < 256; ii++) { if (hisgram_B[ii] > 0) if (hisgram_B[ii] >= imin_threshold[2]) { min_B = ii; break; } } for (ii = 255; ii >= 0; ii--) { if (hisgram_B[ii] > 0) if (hisgram_B[ii] >= imax_threshold[2]) { max_B = ii; break; } } DBG (DBG_INFO, "AutoLevel: Set min , max is over!\n"); /*Autolevel: */ sum_R = max_R - min_R; sum_G = max_G - min_G; sum_B = max_B - min_B; for (j = 0; j < iHeight; j++) { tLines = j * iWidth * 3; for (i = 0; i < iWidth; i++) { R = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 2)); G = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 1)); B = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3)); /*R*/ if (sum_R == 0) R = max_R; else if (R < min_R) R = 0; else if (R <= 255) { fmax_R = ((float) ((R - min_R) * 255) / (float) sum_R); R = (unsigned short) fmax_R; fmax_R = (fmax_R - R) * 10; if (fmax_R >= 5) R++; } if (R > 255) R = 255; /*G*/ if (sum_G == 0) G = max_G; else if (G < min_G) G = 0; else if (G <= 255) { fmax_G = ((float) ((G - min_G) * 255) / (float) sum_G); G = (unsigned short) fmax_G; fmax_G = (fmax_G - G) * 10; if (fmax_G >= 5) G++; } if (G > 255) G = 255; /*B*/ if (sum_B == 0) B = max_B; else if (B < min_B) B = 0; else if (B <= 255) { fmax_B = ((float) (B - min_B) * 255 / (float) sum_B); B = (unsigned short) fmax_B; fmax_B = (fmax_B - B) * 10; if (fmax_B >= 5) B++; } if (B > 255) B = 255; hisgram_R[(SANE_Byte) R]++; hisgram_G[(SANE_Byte) G]++; hisgram_B[(SANE_Byte) B]++; *(pbmpdata + (tLines + i * 3 + 2)) = (SANE_Byte) R; *(pbmpdata + (tLines + i * 3 + 1)) = (SANE_Byte) G; *(pbmpdata + (tLines + i * 3)) = (SANE_Byte) B; } } DBG (DBG_FUNC, "AutoLevel: exit\n"); return; } #ifdef SANE_UNUSED /********************************************************************** Author: Jack Date: 2005/05/14 Routine Description: Deal with image with auto level Parameters: pDIB: the data of image ImageWidth: the width of image ImageHeight: the height of image Return value: none ***********************************************************************/ static void QBETDetectAutoLevel (void *pDIB, unsigned int ImageWidth, unsigned int ImageHeight) { unsigned short *pbmpdata; float fRPercent = 0.0; float fGPercent = 0.0; float fBPercent = 0.0; float fRSum, fGSum, fBSum; int i, j; unsigned int tLines, CountPixels, TotalImgSize; unsigned short R, G, B, max_R, max_G, max_B, min_R, min_G, min_B; unsigned short wIndexR, wIndexG, wIndexB; float fmax_R, fmax_G, fmax_B; unsigned int sum_R = 0, sum_G = 0, sum_B = 0; unsigned int hisgram_R[1024], hisgram_G[1024], hisgram_B[1024]; if (!pDIB) { return; } pbmpdata = (unsigned short *) pDIB; CountPixels = 0; TotalImgSize = ImageWidth * ImageHeight; for (i = 0; i < 1024; i++) { hisgram_R[i] = 0; hisgram_G[i] = 0; hisgram_B[i] = 0; } /*Find min , max, mean */ max_R = max_G = max_B = 0; min_R = min_G = min_B = 1023; tLines = 0; for (j = 0; j < (int) ImageHeight; j++) { tLines = j * ImageWidth * 3; for (i = 0; i < (int) ImageWidth; i++) { R = *(pbmpdata + (tLines + i * 3 + 2)); G = *(pbmpdata + (tLines + i * 3 + 1)); B = *(pbmpdata + (tLines + i * 3)); max_R = _MAX (R, max_R); max_G = _MAX (G, max_G); max_B = _MAX (B, max_B); min_R = _MIN (R, min_R); min_G = _MIN (G, min_G); min_B = _MIN (B, min_B); hisgram_R[R]++; hisgram_G[G]++; hisgram_B[B]++; sum_R += R; sum_G += G; sum_B += B; *(pbmpdata + (tLines + i * 3 + 2)) = R; *(pbmpdata + (tLines + i * 3 + 1)) = G; *(pbmpdata + (tLines + i * 3)) = B; CountPixels++; } } fRSum = 0.0; fGSum = 0.0; fBSum = 0.0; wIndexR = 511; wIndexG = 511; wIndexB = 511; for (i = 0; i < 1024; i++) { fRSum += (float) hisgram_R[i]; fRPercent = (fRSum / CountPixels) * 100; if (fRPercent > 50) { wIndexR = i; break; } } for (i = 0; i < 1024; i++) { fGSum += (float) hisgram_G[i]; fGPercent = (fGSum / CountPixels) * 100; if (fGPercent > 50) { wIndexG = i; break; } } for (i = 0; i < 1024; i++) { fBSum += (float) hisgram_B[i]; fBPercent = (fBSum / CountPixels) * 100; if (fBPercent > 50) { wIndexB = i; break; } } fRSum = 0.0; for (i = wIndexR; i >= 0; i--) { fRSum += (float) hisgram_R[i]; fRPercent = (fRSum / CountPixels) * 100; if (fRPercent >= 48) { min_R = i; break; } } fRSum = 0.0; for (i = wIndexR; i < 1024; i++) { fRSum += (float) hisgram_R[i]; fRPercent = (fRSum / CountPixels) * 100; if (fRPercent >= 47) { max_R = i; break; } } fGSum = 0.0; for (i = wIndexG; i >= 0; i--) { fGSum += (float) hisgram_G[i]; fGPercent = (fGSum / CountPixels) * 100; if (fGPercent >= 48) { min_G = i; break; } } fGSum = 0.0; for (i = wIndexG; i < 1024; i++) { fGSum += (float) hisgram_G[i]; fGPercent = (fGSum / CountPixels) * 100; if (fGPercent >= 47) { max_G = i; break; } } fBSum = 0.0; for (i = wIndexB; i >= 0; i--) { fBSum += (float) hisgram_B[i]; fBPercent = (fBSum / CountPixels) * 100; if (fBPercent >= 46) { min_B = i; break; } } fBSum = 0.0; for (i = wIndexB; i < 1024; i++) { fBSum += (float) hisgram_B[i]; fBPercent = (fBSum / CountPixels) * 100; if (fBPercent >= 47) { max_B = i; break; } } /*Autolevel: */ sum_R = max_R - min_R; sum_G = max_G - min_G; sum_B = max_B - min_B; for (j = 0; j < (int) ImageHeight; j++) { tLines = j * ImageWidth * 3; for (i = 0; i < (int) ImageWidth; i++) { R = *(pbmpdata + (tLines + i * 3 + 2)); G = *(pbmpdata + (tLines + i * 3 + 1)); B = *(pbmpdata + (tLines + i * 3)); /*R*/ if (sum_R == 0) R = max_R; else if (R < min_R) { R = 0; } else if ((R >= min_R) && (R <= 1023)) { fmax_R = ((float) ((R - min_R) * 923) / (float) sum_R) + 100; R = (unsigned short) fmax_R; fmax_R = (fmax_R - R) * 10; if (fmax_R >= 5) R++; } if (R > 1023) R = 1023; /*G*/ if (sum_G == 0) G = max_G; else if (G < min_G) { G = 0; } else if ((G >= min_G) && (G <= 1023)) { fmax_G = ((float) ((G - min_G) * 923) / (float) sum_G) + 100; G = (unsigned short) fmax_G; fmax_G = (fmax_G - G) * 10; if (fmax_G >= 5) G++; } if (G > 1023) G = 1023; /*B*/ if (sum_B == 0) B = max_B; else if (B < min_R) { B = 0; } else if ((B >= min_B) && (R <= 1023)) { fmax_B = ((float) (B - min_B) * 923 / (float) sum_B) + 100; B = (unsigned short) fmax_B; fmax_B = (fmax_B - B) * 10; if (fmax_B >= 5) B++; } if (B > 1023) B = 1023; *(pbmpdata + (tLines + i * 3 + 2)) = R; *(pbmpdata + (tLines + i * 3 + 1)) = G; *(pbmpdata + (tLines + i * 3)) = B; } } return; } #endif #ifdef SANE_UNUSED /********************************************************************** Author: Jack Date: 2005/05/14 Routine Description: Change the image data and deal with auto level Parameters: lpSource: the data of image scanMode: the scan mode ScanLines: the rows of image BytesPerLine: the bytes of per line Return value: none ***********************************************************************/ static void QBetChange (SANE_Byte *lpSource, SCANMODE scanMode, unsigned short ScanLines, unsigned int BytesPerLine) { unsigned short i, j; unsigned int tLines, TotalImgSize; unsigned short R1, G1, B1, R, G, B, R2, G2, B2, QBET_RGB = 0, PointF, PointB; unsigned short *pwRGB; int k; unsigned int ImageWidth = BytesPerLine / 3; unsigned int ImageHeight = ScanLines; SANE_Byte *pbmpdata = (SANE_Byte *) lpSource; if (scanMode != CM_RGB24ext) { return; } TotalImgSize = ImageWidth * ImageHeight * 3 * 2; if ((pwRGB = (unsigned short *) malloc (TotalImgSize)) == NULL) { return; } for (j = 0; j < ImageHeight; j++) { tLines = j * ImageWidth * 3; for (i = 0; i < ImageWidth; i++) { if (i == 0) { R1 = R = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 2)); G1 = G = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 1)); B1 = B = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3)); R2 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i + 1) * 3 + 2)); G2 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i + 1) * 3 + 1)); B2 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i + 1) * 3)); } else if (i == (ImageWidth - 1)) { R1 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i - 1) * 3 + 2)); G1 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i - 1) * 3 + 1)); B1 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i - 1) * 3)); R2 = R = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 2)); G2 = G = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 1)); B2 = B = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3)); } else { R1 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i - 1) * 3 + 2)); G1 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i - 1) * 3 + 1)); B1 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i - 1) * 3)); R = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 2)); G = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 1)); B = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3)); R2 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i + 1) * 3 + 2)); G2 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i + 1) * 3 + 1)); B2 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i + 1) * 3)); } R1 = R1 & 0x0003; G1 = G1 & 0x0003; B1 = B1 & 0x0003; R2 = R2 & 0x0003; G2 = G2 & 0x0003; B2 = B2 & 0x0003; for (k = 0; k < 3; k++) { if (k == 0) { PointF = R1; PointB = R2; } else if (k == 1) { PointF = G1; PointB = G2; } else if (k == 2) { PointF = B1; PointB = B2; } switch (PointF) { case 0: case 1: if (PointB == 0) QBET_RGB = 0xFFFC; else if (PointB == 1) QBET_RGB = 0xFFFC; else if (PointB == 2) QBET_RGB = 0xFFFD; else if (PointB == 3) QBET_RGB = 0xFFFE; break; case 2: if (PointB == 0) QBET_RGB = 0xFFFD; else if (PointB == 1) QBET_RGB = 0xFFFD; else if (PointB == 2) QBET_RGB = 0xFFFF; else if (PointB == 3) QBET_RGB = 0xFFFF; break; case 3: if (PointB == 0) QBET_RGB = 0xFFFE; else if (PointB == 1) QBET_RGB = 0xFFFE; else if (PointB == 2) QBET_RGB = 0xFFFF; else if (PointB == 3) QBET_RGB = 0xFFFF; break; default: break; } if (k == 0) { R = R << 2; R = R + 0x0003; R = R & QBET_RGB; } else if (k == 1) { G = G << 2; G = G + 0x0003; G = G & QBET_RGB; } else if (k == 2) { B = B << 2; B = B + 0x0003; B = B & QBET_RGB; } } *(pwRGB + (tLines + i * 3 + 2)) = R; *(pwRGB + (tLines + i * 3 + 1)) = G; *(pwRGB + (tLines + i * 3)) = B; } } QBETDetectAutoLevel (pwRGB, ImageWidth, ImageHeight); for (j = 0; j < ImageHeight; j++) { tLines = j * ImageWidth * 3; for (i = 0; i < ImageWidth; i++) { R = *(pwRGB + (tLines + i * 3 + 2)); G = *(pwRGB + (tLines + i * 3 + 1)); B = *(pwRGB + (tLines + i * 3)); R = R >> 2; G = G >> 2; B = B >> 2; if (R > 255) R = 255; if (G > 255) G = 255; if (B > 255) B = 255; *(pbmpdata + (tLines + i * 3 + 2)) = (SANE_Byte) R; *(pbmpdata + (tLines + i * 3 + 1)) = (SANE_Byte) G; *(pbmpdata + (tLines + i * 3)) = (SANE_Byte) B; } } if (pwRGB != NULL) { free (pwRGB); } return; } #endif /****************************** SANE API functions *****************************/ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { DBG_INIT (); DBG (DBG_FUNC, "sane_init: start\n"); DBG (DBG_ERR, "SANE Mustek USB2 backend version %d.%d build %d from %s\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD, PACKAGE_STRING); num_devices = 1; /* HOLD: only one device in this backend */ if (version_code != NULL) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); DBG (DBG_INFO, "sane_init: authorize %s null\n", authorize ? "!=" : "=="); DBG (DBG_FUNC, "sane_init: exit\n"); return SANE_STATUS_GOOD; } void sane_exit (void) { DBG (DBG_FUNC, "sane_exit: start\n"); if (devlist != NULL) { free (devlist); devlist = NULL; } devlist = NULL; DBG (DBG_FUNC, "sane_exit: exit\n"); } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { SANE_Int dev_num; DBG (DBG_FUNC, "sane_get_devices: start: local_only = %s\n", local_only == SANE_TRUE ? "true" : "false"); if (devlist != NULL) { free (devlist); devlist = NULL; } devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); if (devlist == NULL) return SANE_STATUS_NO_MEM; dev_num = 0; /* HOLD: This is ugly (only one scanner!) and should go to sane_init */ if (GetDeviceStatus ()) { SANE_Device *sane_device; sane_device = malloc (sizeof (*sane_device)); if (sane_device == NULL) return SANE_STATUS_NO_MEM; sane_device->name = strdup (device_name); sane_device->vendor = strdup ("Mustek"); sane_device->model = strdup ("BearPaw 2448 TA Pro"); sane_device->type = strdup ("flatbed scanner"); devlist[dev_num++] = sane_device; } devlist[dev_num] = 0; *device_list = devlist; DBG (DBG_FUNC, "sane_get_devices: exit\n"); return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { Mustek_Scanner *s; DBG (DBG_FUNC, "sane_open: start :devicename = %s\n", devicename); if (!MustScanner_Init ()) { return SANE_STATUS_INVAL; } if (!PowerControl (SANE_FALSE, SANE_FALSE)) { return SANE_STATUS_INVAL; } if (!CarriageHome ()) { return SANE_STATUS_INVAL; } s = malloc (sizeof (*s)); if (s == NULL) return SANE_STATUS_NO_MEM; memset (s, 0, sizeof (*s)); s->gamma_table = NULL; memcpy (&s->model, &mustek_A2nu2_model, sizeof (Scanner_Model)); s->next = NULL; s->bIsScanning = SANE_FALSE; s->bIsReading = SANE_FALSE; init_options (s); *handle = s; s->read_rows = 0; s->scan_buffer_len = 0; DBG (DBG_FUNC, "sane_open: exit\n"); return SANE_STATUS_GOOD; } void sane_close (SANE_Handle handle) { Mustek_Scanner *s = handle; DBG (DBG_FUNC, "sane_close: start\n"); PowerControl (SANE_FALSE, SANE_FALSE); CarriageHome (); if (NULL != g_pDeviceFile) { free (g_pDeviceFile); g_pDeviceFile = NULL; } if (s->Scan_data_buf != NULL) free (s->Scan_data_buf); s->Scan_data_buf = NULL; free (handle); DBG (DBG_FUNC, "sane_close: exit\n"); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { Mustek_Scanner *s = handle; if ((unsigned) option >= NUM_OPTIONS) return 0; DBG (DBG_FUNC, "sane_get_option_descriptor: option = %s (%d)\n", s->opt[option].name, option); return s->opt + option; } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { Mustek_Scanner *s = handle; SANE_Status status; SANE_Word cap; SANE_Int myinfo = 0; DBG (DBG_FUNC, "sane_control_option: start: action = %s, option = %s (%d)\n", (action == SANE_ACTION_GET_VALUE) ? "get" : (action == SANE_ACTION_SET_VALUE) ? "set" : (action == SANE_ACTION_SET_AUTO) ? "set_auto" : "unknown", s->opt[option].name, option); if (info) *info = 0; if (s->bIsScanning) { DBG (DBG_ERR, "sane_control_option: don't call this function while " "scanning\n"); return SANE_STATUS_DEVICE_BUSY; } if (option >= NUM_OPTIONS || option < 0) { DBG (DBG_ERR, "sane_control_option: option %d >= NUM_OPTIONS || option < 0\n", option); return SANE_STATUS_INVAL; } cap = s->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) { DBG (DBG_ERR, "sane_control_option: option %d is inactive\n", option); return SANE_STATUS_INVAL; } if (action == SANE_ACTION_GET_VALUE) { switch (option) { /* word options: */ case OPT_NUM_OPTS: case OPT_RESOLUTION: case OPT_PREVIEW: case OPT_AUTO_WARMUP: case OPT_GAMMA_VALUE: case OPT_THRESHOLD: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: *(SANE_Word *) val = s->val[option].w; break; /* string options: */ case OPT_MODE: strcpy (val, s->val[option].s); break; case OPT_SOURCE: strcpy (val, s->val[option].s); break; default: DBG (DBG_ERR, "sane_control_option: can't get unknown option %d\n", option); ; } } else if (action == SANE_ACTION_SET_VALUE) { if (!SANE_OPTION_IS_SETTABLE (cap)) { DBG (DBG_ERR, "sane_control_option: option %d is not settable\n", option); return SANE_STATUS_INVAL; } status = sanei_constrain_value (s->opt + option, val, &myinfo); if (status != SANE_STATUS_GOOD) { DBG (2, "sane_control_option: sanei_constrain_value returned %s\n", sane_strstatus (status)); return status; } switch (option) { /* (mostly) side-effect-free word options: */ case OPT_RESOLUTION: case OPT_PREVIEW: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: s->val[option].w = *(SANE_Word *) val; RIE (calc_parameters (s)); myinfo |= SANE_INFO_RELOAD_PARAMS; break; case OPT_THRESHOLD: case OPT_AUTO_WARMUP: case OPT_GAMMA_VALUE: s->val[option].w = *(SANE_Word *) val; break; /* side-effect-free word-array options: */ case OPT_MODE: if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); if (strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_LINEART) == 0) { ENABLE (OPT_THRESHOLD); } else { DISABLE (OPT_THRESHOLD); } RIE (calc_parameters (s)); myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; break; case OPT_SOURCE: if (strcmp (s->val[option].s, val) != 0) { /* something changed */ if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); if (strcmp (s->val[option].s, "Reflective") == 0) { PowerControl (SANE_TRUE, SANE_FALSE); s->opt[OPT_MODE].size = max_string_size (mode_list); s->opt[OPT_MODE].constraint.string_list = mode_list; s->val[OPT_MODE].s = strdup ("Color24"); x_range.max = s->model.x_size; y_range.max = s->model.y_size; } else if (0 == strcmp (s->val[option].s, "Negative")) { PowerControl (SANE_FALSE, SANE_TRUE); s->opt[OPT_MODE].size = max_string_size (negative_mode_list); s->opt[OPT_MODE].constraint.string_list = negative_mode_list; s->val[OPT_MODE].s = strdup ("Color24"); x_range.max = s->model.x_size_ta; y_range.max = s->model.y_size_ta; } else if (0 == strcmp (s->val[option].s, "Positive")) { PowerControl (SANE_FALSE, SANE_TRUE); s->opt[OPT_MODE].size = max_string_size (mode_list); s->opt[OPT_MODE].constraint.string_list = mode_list; s->val[OPT_MODE].s = strdup ("Color24"); x_range.max = s->model.x_size_ta; y_range.max = s->model.y_size_ta; } } myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; break; default: DBG (DBG_ERR, "sane_control_option: can't set unknown option %d\n", option); } } else { DBG (DBG_ERR, "sane_control_option: unknown action %d for option %d\n", action, option); return SANE_STATUS_INVAL; } if (info) *info = myinfo; DBG (DBG_FUNC, "sane_control_option: exit\n"); return SANE_STATUS_GOOD; } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { Mustek_Scanner *s = handle; DBG (DBG_FUNC, "sane_get_parameters: start\n"); DBG (DBG_INFO, "sane_get_parameters :params.format = %d\n", s->params.format); DBG (DBG_INFO, "sane_get_parameters :params.depth = %d\n", s->params.depth); DBG (DBG_INFO, "sane_get_parameters :params.pixels_per_line = %d\n", s->params.pixels_per_line); DBG (DBG_INFO, "sane_get_parameters :params.bytes_per_line = %d\n", s->params.bytes_per_line); DBG (DBG_INFO, "sane_get_parameters :params.lines = %d\n", s->params.lines); if (params != NULL) *params = s->params; DBG (DBG_FUNC, "sane_get_parameters: exit\n"); return SANE_STATUS_GOOD; } SANE_Status sane_start (SANE_Handle handle) { int i; Mustek_Scanner *s = handle; DBG (DBG_FUNC, "sane_start: start\n"); s->scan_buffer_len = 0; calc_parameters (s); if (s->val[OPT_TL_X].w >= s->val[OPT_BR_X].w) { DBG (DBG_CRIT, "sane_start: top left x >= bottom right x --- exiting\n"); return SANE_STATUS_INVAL; } if (s->val[OPT_TL_Y].w >= s->val[OPT_BR_Y].w) { DBG (DBG_CRIT, "sane_start: top left y >= bottom right y --- exiting\n"); return SANE_STATUS_INVAL; } s->setpara.pGammaTable = NULL; DBG (DBG_INFO, "Sane_start:setpara ,setpara.fmArea.x1=%d\n", s->setpara.fmArea.x1); DBG (DBG_INFO, "Sane_start:setpara ,setpara.fmArea.x2=%d\n", s->setpara.fmArea.x2); DBG (DBG_INFO, "Sane_start:setpara ,setpara.fmArea.y1=%d\n", s->setpara.fmArea.y1); DBG (DBG_INFO, "Sane_start:setpara ,setpara.fmArea.y2=%d\n", s->setpara.fmArea.y2); DBG (DBG_INFO, "Sane_start:setpara ,setpara.pfPixelFlavor=%d\n", s->setpara.pfPixelFlavor); DBG (DBG_INFO, "Sane_start:setpara ,setpara.wLinearThreshold=%d\n", s->setpara.wLinearThreshold); DBG (DBG_INFO, "Sane_start:setpara ,setpara.wTargetDPI=%d\n", s->setpara.wTargetDPI); DBG (DBG_INFO, "Sane_start:setpara ,setpara.smScanMode=%d\n", s->setpara.smScanMode); DBG (DBG_INFO, "Sane_start:setpara ,setpara.ssScanSource =%d\n", s->setpara.ssScanSource); DBG (DBG_INFO, "Sane_start:setpara ,setpara.pGammaTable =%p\n", (void *) s->setpara.pGammaTable); SetParameters (&s->setpara); GetParameters (&s->getpara); switch (s->params.format) { case SANE_FRAME_RGB: if (s->params.depth == 8) s->params.pixels_per_line = s->getpara.dwLineByteWidth / 3; if (s->params.depth == 16) s->params.pixels_per_line = s->getpara.dwLineByteWidth / 6; break; case SANE_FRAME_GRAY: if (s->params.depth == 1) s->params.pixels_per_line = s->getpara.dwLineByteWidth * 8; if (s->params.depth == 8) s->params.pixels_per_line = s->getpara.dwLineByteWidth; if (s->params.depth == 16) s->params.pixels_per_line = s->getpara.dwLineByteWidth / 2; break; default: DBG (DBG_INFO, "sane_start: sane_params.format = %d\n", s->params.format); } s->params.bytes_per_line = s->getpara.dwLineByteWidth; s->params.lines = s->getpara.dwLength; s->params.last_frame = TRUE; s->read_rows = s->getpara.dwLength; DBG (DBG_INFO, "sane_start : read_rows = %d\n", s->read_rows); /*warmming up */ if (s->val[OPT_AUTO_WARMUP].w) { for (i = 30; i > 0; i--) { sleep (1); DBG (DBG_ERR, "warming up: %d\n", i); } } DBG (DBG_INFO, "SCANNING ... \n"); s->bIsScanning = SANE_TRUE; if (s->Scan_data_buf != NULL) free (s->Scan_data_buf); s->Scan_data_buf = NULL; s->Scan_data_buf = malloc (SCAN_BUFFER_SIZE * sizeof (SANE_Byte)); if (s->Scan_data_buf == NULL) return SANE_STATUS_NO_MEM; StartScan (); DBG (DBG_FUNC, "sane_start: exit\n"); return SANE_STATUS_GOOD; } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { Mustek_Scanner *s = handle; static SANE_Byte *tempbuf; SANE_Int lines_to_read, lines_read; IMAGEROWS image_row; int maxbuffersize = max_len; DBG (DBG_FUNC, "sane_read: start: max_len=%d\n", max_len); if (s == NULL) { DBG (DBG_ERR, "sane_read: handle is null!\n"); return SANE_STATUS_INVAL; } if (buf == NULL) { DBG (DBG_ERR, "sane_read: buf is null!\n"); return SANE_STATUS_INVAL; } if (len == NULL) { DBG (DBG_ERR, "sane_read: len is null!\n"); return SANE_STATUS_INVAL; } *len = 0; if (!s->bIsScanning) { DBG (DBG_WARN, "sane_read: scan was cancelled, is over or has not been " "initiated yet\n"); return SANE_STATUS_CANCELLED; } DBG (DBG_DBG, "sane_read: before read data read_row=%d\n", s->read_rows); if (s->scan_buffer_len == 0) { if (s->read_rows > 0) { lines_to_read = SCAN_BUFFER_SIZE / s->getpara.dwLineByteWidth; if (lines_to_read > s->read_rows) lines_to_read = s->read_rows; tempbuf = (SANE_Byte *) malloc (sizeof (SANE_Byte) * lines_to_read * s->getpara.dwLineByteWidth + 3 * 1024 + 1); memset (tempbuf, 0, sizeof (SANE_Byte) * lines_to_read * s->getpara.dwLineByteWidth + 3 * 1024 + 1); DBG (DBG_INFO, "sane_read: buffer size is %ld\n", (long int) sizeof (SANE_Byte) * lines_to_read * s->getpara.dwLineByteWidth + 3 * 1024 + 1); image_row.roRgbOrder = mustek_A2nu2_model.line_mode_color_order; image_row.wWantedLineNum = lines_to_read; image_row.pBuffer = (SANE_Byte *) tempbuf; s->bIsReading = SANE_TRUE; if (!ReadScannedData (&image_row)) { DBG (DBG_ERR, "sane_read: ReadScannedData error\n"); s->bIsReading = SANE_FALSE; return SANE_STATUS_INVAL; } DBG (DBG_DBG, "sane_read: Finish ReadScanedData\n"); s->bIsReading = SANE_FALSE; memset (s->Scan_data_buf, 0, SCAN_BUFFER_SIZE); s->scan_buffer_len = image_row.wXferedLineNum * s->getpara.dwLineByteWidth; DBG (DBG_INFO, "sane_read : s->scan_buffer_len = %ld\n", (long int) s->scan_buffer_len); memcpy (s->Scan_data_buf, tempbuf, s->scan_buffer_len); DBG (DBG_DBG, "sane_read :after memcpy\n"); free (tempbuf); s->Scan_data_buf_start = s->Scan_data_buf; s->read_rows -= image_row.wXferedLineNum; } else { DBG (DBG_FUNC, "sane_read: scan finished -- exit\n"); sane_cancel (handle); return SANE_STATUS_EOF; } } if (s->scan_buffer_len == 0) { DBG (DBG_FUNC, "sane_read: scan finished -- exit\n"); sane_cancel (handle); return SANE_STATUS_EOF; } lines_read = (maxbuffersize < (SANE_Int) s->scan_buffer_len) ? maxbuffersize : (SANE_Int) s->scan_buffer_len; DBG (DBG_DBG, "sane_read: after %d\n", lines_read); *len = (SANE_Int) lines_read; DBG (DBG_INFO, "sane_read : get lines_read = %d\n", lines_read); DBG (DBG_INFO, "sane_read : get *len = %d\n", *len); memcpy (buf, s->Scan_data_buf_start, lines_read); s->scan_buffer_len -= lines_read; s->Scan_data_buf_start += lines_read; DBG (DBG_FUNC, "sane_read: exit\n"); return SANE_STATUS_GOOD; } void sane_cancel (SANE_Handle handle) { Mustek_Scanner *s = handle; int i; DBG (DBG_FUNC, "sane_cancel: start\n"); if (s->bIsScanning) { s->bIsScanning = SANE_FALSE; if (s->read_rows > 0) { DBG (DBG_INFO, "sane_cancel: warning: is scanning\n"); } else { DBG (DBG_INFO, "sane_cancel: Scan finished\n"); } StopScan (); CarriageHome (); for (i = 0; i < 20; i++) { if (s->bIsReading == SANE_FALSE) { if (s->gamma_table != NULL) { free (s->gamma_table); s->gamma_table = NULL; break; } } else sleep (1); } if (s->Scan_data_buf != NULL) { free (s->Scan_data_buf); s->Scan_data_buf = NULL; s->Scan_data_buf_start = NULL; } s->read_rows = 0; s->scan_buffer_len = 0; memset (&s->setpara, 0, sizeof (s->setpara)); memset (&s->getpara, 0, sizeof (s->getpara)); } else { DBG (DBG_INFO, "sane_cancel: do nothing\n"); } DBG (DBG_FUNC, "sane_cancel: exit\n"); } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { Mustek_Scanner *s = handle; DBG (DBG_FUNC, "sane_set_io_mode: handle = %p, non_blocking = %s\n", handle, non_blocking == SANE_TRUE ? "true" : "false"); if (!s->bIsScanning) { DBG (DBG_WARN, "sane_set_io_mode: not scanning\n"); return SANE_STATUS_INVAL; } if (non_blocking) return SANE_STATUS_UNSUPPORTED; return SANE_STATUS_GOOD; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { Mustek_Scanner *s = handle; DBG (DBG_FUNC, "sane_get_select_fd: handle = %p, fd = %p\n", handle, (void *) fd); if (!s->bIsScanning) { DBG (DBG_WARN, "%s", "sane_get_select_fd: not scanning\n"); return SANE_STATUS_INVAL; } return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/mustek_usb2.h000066400000000000000000000114351456256263500173120ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2000-2005 Mustek. Originally maintained by Mustek Copyright (C) 2001-2005 by Henning Meier-Geinitz. 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, see . 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 the Mustek BearPaw 2448 TA Pro and similar USB2 scanners. */ #ifndef MUSTEK_USB2_H #define MUSTEK_USB2_H #ifndef SANE_I18N #define SANE_I18N(text) text #endif #define ENABLE(OPTION) s->opt[OPTION].cap &= ~SANE_CAP_INACTIVE #define DISABLE(OPTION) s->opt[OPTION].cap |= SANE_CAP_INACTIVE #define IS_ACTIVE(OPTION) (((s->opt[OPTION].cap) & SANE_CAP_INACTIVE) == 0) /* RIE: return if error */ #define RIE(function) do {status = function; if (status != SANE_STATUS_GOOD) \ return status;} while (SANE_FALSE) #define SCAN_BUFFER_SIZE (64 * 1024) #define MAX_RESOLUTIONS 12 #define DEF_LINEARTTHRESHOLD 128 #define PER_ADD_START_LINES 0 #define PRE_ADD_START_X 0 enum Mustek_Usb_Option { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_MODE, OPT_SOURCE, OPT_RESOLUTION, OPT_PREVIEW, OPT_DEBUG_GROUP, OPT_AUTO_WARMUP, OPT_ENHANCEMENT_GROUP, OPT_THRESHOLD, OPT_GAMMA_VALUE, OPT_GEOMETRY_GROUP, OPT_TL_X, /* top-left x */ OPT_TL_Y, /* top-left y */ OPT_BR_X, /* bottom-right x */ OPT_BR_Y, /* bottom-right y */ /* must come last: */ NUM_OPTIONS }; typedef struct Scanner_Model { /** @name Identification */ /*@{ */ /** A single lowercase word to be used in the configuration file. */ SANE_String_Const name; /** Device vendor string. */ SANE_String_Const vendor; /** Device model name. */ SANE_String_Const model; /** Name of the firmware file. */ SANE_String_Const firmware_name; /** @name Scanner model parameters */ /*@{ */ SANE_Int dpi_values[MAX_RESOLUTIONS]; /* possible resolutions */ SANE_Fixed x_offset; /* Start of scan area in mm */ SANE_Fixed y_offset; /* Start of scan area in mm */ SANE_Fixed x_size; /* Size of scan area in mm */ SANE_Fixed y_size; /* Size of scan area in mm */ SANE_Fixed x_offset_ta; /* Start of scan area in TA mode in mm */ SANE_Fixed y_offset_ta; /* Start of scan area in TA mode in mm */ SANE_Fixed x_size_ta; /* Size of scan area in TA mode in mm */ SANE_Fixed y_size_ta; /* Size of scan area in TA mode in mm */ RGBORDER line_mode_color_order; /* Order of the CCD/CIS colors */ SANE_Fixed default_gamma_value; /* Default gamma value */ SANE_Bool is_cis; /* Is this a CIS or CCD scanner? */ SANE_Word flags; /* Which hacks are needed for this scanner? */ /*@} */ } Scanner_Model; typedef struct Mustek_Scanner { /* all the state needed to define a scan request: */ struct Mustek_Scanner *next; SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; unsigned short *gamma_table; SANE_Parameters params; /**< SANE Parameters */ Scanner_Model model; SETPARAMETERS setpara; GETPARAMETERS getpara; SANE_Bool bIsScanning; SANE_Bool bIsReading; SANE_Word read_rows; /* transfer image's lines */ SANE_Byte *Scan_data_buf; /*store Scanned data for transfer */ SANE_Byte *Scan_data_buf_start; /*point to data need to transfer */ size_t scan_buffer_len; /* length of data buf */ } Mustek_Scanner; #endif backends-1.3.0/backend/mustek_usb2_asic.c000066400000000000000000004712431456256263500203130ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2005 Mustek. Originally maintained by Mustek Author:Roy 2005.5.24 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, see . 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 the Mustek BearPaw 2448 TA Pro and similar USB2 scanners. */ #include "mustek_usb2_asic.h" /* ---------------------- low level asic functions -------------------------- */ static SANE_Byte RegisterBankStatus = -1; static SANE_Status WriteIOControl (PAsic chip, unsigned short wValue, unsigned short wIndex, unsigned short wLength, SANE_Byte * lpbuf) { SANE_Status status = SANE_STATUS_GOOD; status = sanei_usb_control_msg (chip->fd, 0x40, 0x01, wValue, wIndex, wLength, lpbuf); if (status != SANE_STATUS_GOOD) { DBG (DBG_ERR, "WriteIOControl Error!\n"); return status; } return SANE_STATUS_GOOD; } static SANE_Status ReadIOControl (PAsic chip, unsigned short wValue, unsigned short wIndex, unsigned short wLength, SANE_Byte * lpbuf) { SANE_Status status = SANE_STATUS_GOOD; status = sanei_usb_control_msg (chip->fd, 0xc0, 0x01, wValue, wIndex, wLength, lpbuf); if (status != SANE_STATUS_GOOD) { DBG (DBG_ERR, "WriteIOControl Error!\n"); return status; } return status; } static SANE_Status Mustek_ClearFIFO (PAsic chip) { SANE_Status status = SANE_STATUS_GOOD; SANE_Byte buf[4]; DBG (DBG_ASIC, "Mustek_ClearFIFO:Enter\n"); buf[0] = 0; buf[1] = 0; buf[2] = 0; buf[3] = 0; status = WriteIOControl (chip, 0x05, 0, 4, (SANE_Byte *) (buf)); if (status != SANE_STATUS_GOOD) return status; status = WriteIOControl (chip, 0xc0, 0, 4, (SANE_Byte *) (buf)); if (status != SANE_STATUS_GOOD) return status; DBG (DBG_ASIC, "Mustek_ClearFIFO:Exit\n"); return SANE_STATUS_GOOD; } static SANE_Status Mustek_SendData (PAsic chip, unsigned short reg, SANE_Byte data) { SANE_Byte buf[4]; SANE_Status status = SANE_STATUS_GOOD; DBG (DBG_ASIC, "Mustek_SendData: Enter. reg=%x,data=%x\n", reg, data); if (reg <= 0xFF) { if (RegisterBankStatus != 0) { DBG (DBG_ASIC, "RegisterBankStatus=%d\n", RegisterBankStatus); buf[0] = ES01_5F_REGISTER_BANK_SELECT; buf[1] = SELECT_REGISTER_BANK0; buf[2] = ES01_5F_REGISTER_BANK_SELECT; buf[3] = SELECT_REGISTER_BANK0; WriteIOControl (chip, 0xb0, 0, 4, buf); RegisterBankStatus = 0; DBG (DBG_ASIC, "RegisterBankStatus=%d\n", RegisterBankStatus); } } else if (reg <= 0x1FF) { if (RegisterBankStatus != 1) { DBG (DBG_ASIC, "RegisterBankStatus=%d\n", RegisterBankStatus); buf[0] = ES01_5F_REGISTER_BANK_SELECT; buf[1] = SELECT_REGISTER_BANK1; buf[2] = ES01_5F_REGISTER_BANK_SELECT; buf[3] = SELECT_REGISTER_BANK1; WriteIOControl (chip, 0xb0, 0, 4, buf); RegisterBankStatus = 1; } } else if (reg <= 0x2FF) { if (RegisterBankStatus != 2) { DBG (DBG_ASIC, "RegisterBankStatus=%d\n", RegisterBankStatus); buf[0] = ES01_5F_REGISTER_BANK_SELECT; buf[1] = SELECT_REGISTER_BANK2; buf[2] = ES01_5F_REGISTER_BANK_SELECT; buf[3] = SELECT_REGISTER_BANK2; WriteIOControl (chip, 0xb0, 0, 4, buf); RegisterBankStatus = 2; } } buf[0] = LOBYTE (reg); buf[1] = data; buf[2] = LOBYTE (reg); buf[3] = data; status = WriteIOControl (chip, 0xb0, 0, 4, buf); if (status != SANE_STATUS_GOOD) DBG (DBG_ERR, ("Mustek_SendData: write error\n")); return status; } static SANE_Status Mustek_ReceiveData (PAsic chip, SANE_Byte * reg) { SANE_Status status = SANE_STATUS_GOOD; SANE_Byte buf[4]; DBG (DBG_ASIC, "Mustek_ReceiveData\n"); status = ReadIOControl (chip, 0x07, 0, 4, buf); *reg = buf[0]; return status; } static SANE_Status Mustek_WriteAddressLineForRegister (PAsic chip, SANE_Byte x) { SANE_Status status = SANE_STATUS_GOOD; SANE_Byte buf[4]; DBG (DBG_ASIC, "Mustek_WriteAddressLineForRegister: Enter\n"); buf[0] = x; buf[1] = x; buf[2] = x; buf[3] = x; status = WriteIOControl (chip, 0x04, x, 4, buf); DBG (DBG_ASIC, "Mustek_WriteAddressLineForRegister: Exit\n"); return status; } static SANE_Status SetRWSize (PAsic chip, SANE_Byte ReadWrite, unsigned int size) { SANE_Status status = SANE_STATUS_GOOD; DBG (DBG_ASIC, "SetRWSize: Enter\n"); if (ReadWrite == 0) { /*write */ status = Mustek_SendData (chip, 0x7C, (SANE_Byte) (size)); if (status != SANE_STATUS_GOOD) return status; status = Mustek_SendData (chip, 0x7D, (SANE_Byte) (size >> 8)); if (status != SANE_STATUS_GOOD) return status; status = Mustek_SendData (chip, 0x7E, (SANE_Byte) (size >> 16)); if (status != SANE_STATUS_GOOD) return status; status = Mustek_SendData (chip, 0x7F, (SANE_Byte) (size >> 24)); if (status != SANE_STATUS_GOOD) return status; } else { /* read */ status = Mustek_SendData (chip, 0x7C, (SANE_Byte) (size >> 1)); if (status != SANE_STATUS_GOOD) return status; status = Mustek_SendData (chip, 0x7D, (SANE_Byte) (size >> 9)); if (status != SANE_STATUS_GOOD) return status; status = Mustek_SendData (chip, 0x7E, (SANE_Byte) (size >> 17)); if (status != SANE_STATUS_GOOD) return status; status = Mustek_SendData (chip, 0x7F, (SANE_Byte) (size >> 25)); if (status != SANE_STATUS_GOOD) return status; } DBG (DBG_ASIC, "SetRWSize: Exit\n"); return SANE_STATUS_GOOD; } static SANE_Status Mustek_DMARead (PAsic chip, unsigned int size, SANE_Byte * lpdata) { SANE_Status status = SANE_STATUS_GOOD; unsigned int i, buf[1]; unsigned int read_size; size_t read_size_usb; DBG (DBG_ASIC, "Mustek_DMARead: Enter\n"); status = Mustek_ClearFIFO (chip); if (status != SANE_STATUS_GOOD) return status; buf[0] = read_size = 32 * 1024; for (i = 0; i < size / (read_size); i++) { SetRWSize (chip, 1, buf[0]); status = WriteIOControl (chip, 0x03, 0, 4, (SANE_Byte *) (buf)); read_size_usb = buf[0]; status = sanei_usb_read_bulk (chip->fd, lpdata + i * read_size, &read_size_usb); buf[0] = read_size_usb; if (status != SANE_STATUS_GOOD) { DBG (DBG_ERR, "Mustek_DMARead: read error\n"); return status; } } buf[0] = size - i * read_size; if (buf[0] > 0) { SetRWSize (chip, 1, buf[0]); status = WriteIOControl (chip, 0x03, 0, 4, (SANE_Byte *) (buf)); read_size_usb = buf[0]; status = sanei_usb_read_bulk (chip->fd, lpdata + i * read_size, &read_size_usb); buf[0] = read_size_usb; if (status != SANE_STATUS_GOOD) { DBG (DBG_ERR, "Mustek_DMARead: read error\n"); return status; } usleep (20000); } DBG (DBG_ASIC, "Mustek_DMARead: Exit\n"); return SANE_STATUS_GOOD; } static SANE_Status Mustek_DMAWrite (PAsic chip, unsigned int size, SANE_Byte * lpdata) { SANE_Status status = SANE_STATUS_GOOD; unsigned int buf[1]; unsigned int i; unsigned int write_size; size_t write_size_usb; DBG (DBG_ASIC, "Mustek_DMAWrite: Enter:size=%d\n", size); status = Mustek_ClearFIFO (chip); if (status != SANE_STATUS_GOOD) return status; buf[0] = write_size = 32 * 1024; for (i = 0; i < size / (write_size); i++) { SetRWSize (chip, 0, buf[0]); WriteIOControl (chip, 0x02, 0, 4, (SANE_Byte *) buf); write_size_usb = buf[0]; status = sanei_usb_write_bulk (chip->fd, lpdata + i * write_size, &write_size_usb); buf[0] = write_size_usb; if (status != SANE_STATUS_GOOD) { DBG (DBG_ERR, "Mustek_DMAWrite: write error\n"); return status; } } buf[0] = size - i * write_size; if (buf[0] > 0) { SetRWSize (chip, 0, buf[0]); WriteIOControl (chip, 0x02, 0, 4, (SANE_Byte *) buf); write_size_usb = buf[0]; status = sanei_usb_write_bulk (chip->fd, lpdata + i * write_size, &write_size_usb); buf[0] = write_size_usb; if (status != SANE_STATUS_GOOD) { DBG (DBG_ERR, "Mustek_DMAWrite: write error\n"); return status; } } Mustek_ClearFIFO (chip); DBG (DBG_ASIC, "Mustek_DMAWrite: Exit\n"); return SANE_STATUS_GOOD; } static SANE_Status Mustek_SendData2Byte (PAsic chip, unsigned short reg, SANE_Byte data) { static SANE_Bool isTransfer = FALSE; static SANE_Byte BankBuf[4]; static SANE_Byte DataBuf[4]; if (reg <= 0xFF) { if (RegisterBankStatus != 0) { DBG (DBG_ASIC, "RegisterBankStatus=%d\n", RegisterBankStatus); BankBuf[0] = ES01_5F_REGISTER_BANK_SELECT; BankBuf[1] = SELECT_REGISTER_BANK0; BankBuf[2] = ES01_5F_REGISTER_BANK_SELECT; BankBuf[3] = SELECT_REGISTER_BANK0; WriteIOControl (chip, 0xb0, 0, 4, BankBuf); RegisterBankStatus = 0; } } else if (reg <= 0x1FF) { if (RegisterBankStatus != 1) { DBG (DBG_ASIC, "RegisterBankStatus=%d\n", RegisterBankStatus); BankBuf[0] = ES01_5F_REGISTER_BANK_SELECT; BankBuf[1] = SELECT_REGISTER_BANK1; BankBuf[2] = ES01_5F_REGISTER_BANK_SELECT; BankBuf[3] = SELECT_REGISTER_BANK1; WriteIOControl (chip, 0xb0, 0, 4, BankBuf); RegisterBankStatus = 1; } } else if (reg <= 0x2FF) { if (RegisterBankStatus != 2) { DBG (DBG_ASIC, "RegisterBankStatus=%d\n", RegisterBankStatus); BankBuf[0] = ES01_5F_REGISTER_BANK_SELECT; BankBuf[1] = SELECT_REGISTER_BANK2; BankBuf[2] = ES01_5F_REGISTER_BANK_SELECT; BankBuf[3] = SELECT_REGISTER_BANK2; WriteIOControl (chip, 0xb0, 0, 4, BankBuf); RegisterBankStatus = 2; } } if (isTransfer == FALSE) { DataBuf[0] = LOBYTE (reg); DataBuf[1] = data; isTransfer = TRUE; } else { DataBuf[2] = LOBYTE (reg); DataBuf[3] = data; WriteIOControl (chip, 0xb0, 0, 4, DataBuf); isTransfer = FALSE; } return SANE_STATUS_GOOD; } /* ---------------------- asic motor functions ----------------------------- */ static SANE_Status LLFRamAccess (PAsic chip, LLF_RAMACCESS * RamAccess) { SANE_Status status = SANE_STATUS_GOOD; SANE_Byte a[2]; DBG (DBG_ASIC, "LLFRamAccess:Enter\n"); /*Set start address. Unit is a word. */ Mustek_SendData (chip, ES01_A0_HostStartAddr0_7, LOBYTE (RamAccess->LoStartAddress)); if (RamAccess->IsOnChipGamma == ON_CHIP_FINAL_GAMMA) { /* final gamma */ Mustek_SendData (chip, ES01_A1_HostStartAddr8_15, HIBYTE (RamAccess->LoStartAddress)); Mustek_SendData (chip, ES01_A2_HostStartAddr16_21, LOBYTE (RamAccess->HiStartAddress) | ACCESS_GAMMA_RAM); } else if (RamAccess->IsOnChipGamma == ON_CHIP_PRE_GAMMA) { /* pre gamma */ Mustek_SendData (chip, ES01_A1_HostStartAddr8_15, HIBYTE (RamAccess-> LoStartAddress) | ES01_ACCESS_PRE_GAMMA); Mustek_SendData (chip, ES01_A2_HostStartAddr16_21, LOBYTE (RamAccess->HiStartAddress) | ACCESS_GAMMA_RAM); } else { /* dram */ Mustek_SendData (chip, ES01_A1_HostStartAddr8_15, HIBYTE (RamAccess->LoStartAddress)); Mustek_SendData (chip, ES01_A2_HostStartAddr16_21, LOBYTE (RamAccess->HiStartAddress) | ACCESS_DRAM); } /* set SDRAM delay time */ Mustek_SendData (chip, ES01_79_AFEMCLK_SDRAMCLK_DELAY_CONTROL, SDRAMCLK_DELAY_12_ns); /*Set end address. Unit is a word. */ Mustek_SendData (chip, ES01_A3_HostEndAddr0_7, 0xff); Mustek_SendData (chip, ES01_A4_HostEndAddr8_15, 0xff); Mustek_SendData (chip, ES01_A5_HostEndAddr16_21, 0xff); Mustek_ClearFIFO (chip); if (RamAccess->ReadWrite == WRITE_RAM) { /*Write RAM */ Mustek_DMAWrite (chip, RamAccess->RwSize, RamAccess->BufferPtr); /* read-write size must be even */ /*steal read 2byte */ usleep (20000); RamAccess->RwSize = 2; RamAccess->BufferPtr = (SANE_Byte *) a; RamAccess->ReadWrite = READ_RAM; LLFRamAccess (chip, RamAccess); DBG (DBG_ASIC, "end steal 2 byte!\n"); } else { /* Read RAM */ Mustek_DMARead (chip, RamAccess->RwSize, RamAccess->BufferPtr); /* read-write size must be even */ } DBG (DBG_ASIC, "LLFRamAccess:Exit\n"); return status; } static SANE_Status LLFSetMotorCurrentAndPhase (PAsic chip, LLF_MOTOR_CURRENT_AND_PHASE * MotorCurrentAndPhase) { SANE_Status status = SANE_STATUS_GOOD; SANE_Byte MotorPhase; DBG (DBG_ASIC, "LLFSetMotorCurrentAndPhase:Enter\n"); if (MotorCurrentAndPhase->MotorDriverIs3967 == 1) { MotorPhase = 0xFE; } else { MotorPhase = 0xFF; } DBG (DBG_ASIC, "MotorPhase=0x%x\n", MotorPhase); Mustek_SendData (chip, ES02_50_MOTOR_CURRENT_CONTORL, 0x01); if (MotorCurrentAndPhase->FillPhase == 0) { Mustek_SendData (chip, ES01_AB_PWM_CURRENT_CONTROL, 0x00); Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, MotorCurrentAndPhase->MotorCurrentTableA[0]); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, MotorCurrentAndPhase->MotorCurrentTableB[0]); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x08 & MotorPhase); /*2 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, MotorCurrentAndPhase->MotorCurrentTableA[0]); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, MotorCurrentAndPhase->MotorCurrentTableB[0]); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x09 & MotorPhase); /*3 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, MotorCurrentAndPhase->MotorCurrentTableA[0]); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, MotorCurrentAndPhase->MotorCurrentTableB[0]); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x01 & MotorPhase); /*4 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, MotorCurrentAndPhase->MotorCurrentTableA[0]); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, MotorCurrentAndPhase->MotorCurrentTableB[0]); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x00 & MotorPhase); } else { if (MotorCurrentAndPhase->MoveType == _4_TABLE_SPACE_FOR_FULL_STEP) { /* Full Step */ Mustek_SendData (chip, ES01_AB_PWM_CURRENT_CONTROL, 0x00); /*1 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, MotorCurrentAndPhase->MotorCurrentTableA[0]); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, MotorCurrentAndPhase->MotorCurrentTableB[0]); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x08 & MotorPhase); /*2 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, MotorCurrentAndPhase->MotorCurrentTableA[0]); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, MotorCurrentAndPhase->MotorCurrentTableB[0]); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x09 & MotorPhase); /*3 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, MotorCurrentAndPhase->MotorCurrentTableA[0]); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, MotorCurrentAndPhase->MotorCurrentTableB[0]); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x01 & MotorPhase); /*4 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, MotorCurrentAndPhase->MotorCurrentTableA[0]); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, MotorCurrentAndPhase->MotorCurrentTableB[0]); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x00 & MotorPhase); } else if (MotorCurrentAndPhase->MoveType == _8_TABLE_SPACE_FOR_1_DIV_2_STEP) { /* Half Step */ Mustek_SendData (chip, ES01_AB_PWM_CURRENT_CONTROL, 0x01); /*1 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, MotorCurrentAndPhase->MotorCurrentTableA[0]); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, MotorCurrentAndPhase->MotorCurrentTableB[0]); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x25 & MotorPhase); /*2 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, MotorCurrentAndPhase->MotorCurrentTableA[0]); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, MotorCurrentAndPhase->MotorCurrentTableB[0]); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x07 & MotorPhase); /*3 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, MotorCurrentAndPhase->MotorCurrentTableA[0]); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, MotorCurrentAndPhase->MotorCurrentTableB[0]); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x24 & MotorPhase); /*4 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, MotorCurrentAndPhase->MotorCurrentTableA[0]); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, MotorCurrentAndPhase->MotorCurrentTableB[0]); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x30 & MotorPhase); /*5 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, MotorCurrentAndPhase->MotorCurrentTableA[0]); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, MotorCurrentAndPhase->MotorCurrentTableB[0]); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x2c & MotorPhase); /*6 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, MotorCurrentAndPhase->MotorCurrentTableA[0]); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, MotorCurrentAndPhase->MotorCurrentTableB[0]); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x0e & MotorPhase); /*7 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, MotorCurrentAndPhase->MotorCurrentTableA[0]); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, MotorCurrentAndPhase->MotorCurrentTableB[0]); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x2d & MotorPhase); /*8 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, MotorCurrentAndPhase->MotorCurrentTableA[0]); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, MotorCurrentAndPhase->MotorCurrentTableB[0]); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x39 & MotorPhase); } else if (MotorCurrentAndPhase->MoveType == _16_TABLE_SPACE_FOR_1_DIV_4_STEP) { /* 1/4 step */ Mustek_SendData (chip, ES01_AB_PWM_CURRENT_CONTROL, 0x02); /*1 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (0 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (0 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x08 & MotorPhase); /*2 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (1 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (1 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x08 & MotorPhase); /*3 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (2 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (2 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x08 & MotorPhase); /*4 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (3 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (3 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x08 & MotorPhase); /*5 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * cos (0 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * sin (0 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x09 & MotorPhase); /*6 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * cos (1 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * sin (1 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x09 & MotorPhase); /*7 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * cos (2 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * sin (2 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x09 & MotorPhase); /*8 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * cos (3 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * sin (3 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x09 & MotorPhase); /*9 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (0 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (0 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x01 & MotorPhase); /*10 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (1 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (1 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x01 & MotorPhase); /*11 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (2 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (2 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x01 & MotorPhase); /*12 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (3 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (3 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x01 & MotorPhase); /*13 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * cos (0 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * sin (0 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x00 & MotorPhase); /*14 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * cos (1 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * sin (1 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x00 & MotorPhase); /*15 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * cos (2 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * sin (2 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x00 & MotorPhase); /*16 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * cos (3 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * sin (3 * 3.141592654 * 90 / 4 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x00 & MotorPhase); } else if (MotorCurrentAndPhase->MoveType == _32_TABLE_SPACE_FOR_1_DIV_8_STEP) { Mustek_SendData (chip, ES01_AB_PWM_CURRENT_CONTROL, 0x03); /*1 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (0 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (0 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x00 & MotorPhase); /*2 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (1 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (1 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x00 & MotorPhase); /*3 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (2 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (2 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x00 & MotorPhase); /*4 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (3 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (3 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x00 & MotorPhase); /*5 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (4 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (4 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x00 & MotorPhase); /*6 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (5 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (5 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x00 & MotorPhase); /*7 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (6 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (6 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x00 & MotorPhase); /*8 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (7 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (7 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x00 & MotorPhase); /*9 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (0 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (0 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x08 & MotorPhase); /*10 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (1 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (1 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x08 & MotorPhase); /*11 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (2 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (2 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x08 & MotorPhase); /*12 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (3 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (3 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x08 & MotorPhase); /*13 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (4 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (4 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x08 & MotorPhase); /*14 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (5 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (5 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x08 & MotorPhase); /*15 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (6 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (6 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x08 & MotorPhase); /*16 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (7 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (7 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x08 & MotorPhase); /*17 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (0 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (0 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x09 & MotorPhase); /*18 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (1 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (1 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x09 & MotorPhase); /*19 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (2 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (2 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x09 & MotorPhase); /*20 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (3 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (3 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x09 & MotorPhase); /*21 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (4 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (4 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x09 & MotorPhase); /*22 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (5 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (5 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x09 & MotorPhase); /*23 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (6 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (6 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x09 & MotorPhase); /*24 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (7 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (7 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x09 & MotorPhase); /*25 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (0 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (0 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x01 & MotorPhase); /*26 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (1 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (1 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x01 & MotorPhase); /*27 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (2 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (2 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x01 & MotorPhase); /*28 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (3 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (3 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x01 & MotorPhase); /*29 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (4 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (4 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x01 & MotorPhase); /*30 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (5 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (5 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x01 & MotorPhase); /*31 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (6 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (6 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x01 & MotorPhase); /*32 */ Mustek_SendData2Byte (chip, ES02_52_MOTOR_CURRENT_TABLE_A, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableA[0] * sin (7 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_53_MOTOR_CURRENT_TABLE_B, (SANE_Byte) (MotorCurrentAndPhase-> MotorCurrentTableB[0] * cos (7 * 3.141592654 * 90 / 8 / 180))); Mustek_SendData2Byte (chip, ES02_51_MOTOR_PHASE_TABLE_1, 0x01 & MotorPhase); } } if (MotorCurrentAndPhase->FillPhase != 0) { Mustek_SendData (chip, ES02_50_MOTOR_CURRENT_CONTORL, 0x00 | MotorCurrentAndPhase->MoveType); } else { Mustek_SendData (chip, ES02_50_MOTOR_CURRENT_CONTORL, 0x00); } DBG (DBG_ASIC, "LLFSetMotorCurrentAndPhase:Exit\n"); return status; } #if SANE_UNUSED static SANE_Status LLFStopMotorMove (PAsic chip) { SANE_Status status = SANE_STATUS_GOOD; DBG (DBG_ASIC, "LLFStopMotorMove:Enter\n"); Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_DISABLE); Asic_WaitUnitReady (chip); DBG (DBG_ASIC, "LLFStopMotorMove:Exit\n"); return status; } #endif static SANE_Status LLFSetMotorTable (PAsic chip, LLF_SETMOTORTABLE * LLF_SetMotorTable) { SANE_Status status = SANE_STATUS_GOOD; LLF_RAMACCESS RamAccess; DBG (DBG_ASIC, "LLFSetMotorTable:Enter\n"); if (LLF_SetMotorTable->MotorTablePtr != NULL) { RamAccess.ReadWrite = WRITE_RAM; RamAccess.IsOnChipGamma = EXTERNAL_RAM; RamAccess.DramDelayTime = SDRAMCLK_DELAY_12_ns; RamAccess.LoStartAddress = 0; RamAccess.LoStartAddress |= LLF_SetMotorTable->MotorTableAddress; RamAccess.LoStartAddress <<= TABLE_OFFSET_BASE; RamAccess.LoStartAddress |= 0x3000; RamAccess.HiStartAddress = 0; RamAccess.HiStartAddress |= LLF_SetMotorTable->MotorTableAddress; RamAccess.HiStartAddress >>= (16 - TABLE_OFFSET_BASE); RamAccess.RwSize = 512 * 2 * 8; /* BYTE */ RamAccess.BufferPtr = (SANE_Byte *) LLF_SetMotorTable->MotorTablePtr; LLFRamAccess (chip, &RamAccess); /* tell scan chip the motor table address, unit is 2^14 words */ Mustek_SendData (chip, ES01_9D_MotorTableAddrA14_A21, LLF_SetMotorTable->MotorTableAddress); } DBG (DBG_ASIC, "LLFSetMotorTable:Exit\n"); return status; } static SANE_Status LLFMotorMove (PAsic chip, LLF_MOTORMOVE * LLF_MotorMove) { SANE_Status status = SANE_STATUS_GOOD; unsigned int motor_steps; SANE_Byte temp_motor_action; DBG (DBG_ASIC, "LLFMotorMove:Enter\n"); Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_DISABLE); status = Asic_WaitUnitReady (chip); DBG (DBG_ASIC, "Set start/end pixel\n"); Mustek_SendData (chip, ES01_B8_ChannelRedExpStartPixelLSB, LOBYTE (100)); Mustek_SendData (chip, ES01_B9_ChannelRedExpStartPixelMSB, HIBYTE (100)); Mustek_SendData (chip, ES01_BA_ChannelRedExpEndPixelLSB, LOBYTE (101)); Mustek_SendData (chip, ES01_BB_ChannelRedExpEndPixelMSB, HIBYTE (101)); Mustek_SendData (chip, ES01_BC_ChannelGreenExpStartPixelLSB, LOBYTE (100)); Mustek_SendData (chip, ES01_BD_ChannelGreenExpStartPixelMSB, HIBYTE (100)); Mustek_SendData (chip, ES01_BE_ChannelGreenExpEndPixelLSB, LOBYTE (101)); Mustek_SendData (chip, ES01_BF_ChannelGreenExpEndPixelMSB, HIBYTE (101)); Mustek_SendData (chip, ES01_C0_ChannelBlueExpStartPixelLSB, LOBYTE (100)); Mustek_SendData (chip, ES01_C1_ChannelBlueExpStartPixelMSB, HIBYTE (100)); Mustek_SendData (chip, ES01_C2_ChannelBlueExpEndPixelLSB, LOBYTE (101)); Mustek_SendData (chip, ES01_C3_ChannelBlueExpEndPixelMSB, HIBYTE (101)); /*set motor accelerate steps MAX 511 steps */ Mustek_SendData (chip, ES01_E0_MotorAccStep0_7, LOBYTE (LLF_MotorMove->AccStep)); Mustek_SendData (chip, ES01_E1_MotorAccStep8_8, HIBYTE (LLF_MotorMove->AccStep)); DBG (DBG_ASIC, "AccStep=%d\n", LLF_MotorMove->AccStep); Mustek_SendData (chip, ES01_E2_MotorStepOfMaxSpeed0_7, LOBYTE (LLF_MotorMove->FixMoveSteps)); Mustek_SendData (chip, ES01_E3_MotorStepOfMaxSpeed8_15, HIBYTE (LLF_MotorMove->FixMoveSteps)); Mustek_SendData (chip, ES01_E4_MotorStepOfMaxSpeed16_19, 0); DBG (DBG_ASIC, "FixMoveSteps=%d\n", LLF_MotorMove->FixMoveSteps); /*set motor decelerate steps MAX 255 steps */ Mustek_SendData (chip, ES01_E5_MotorDecStep, LLF_MotorMove->DecStep); DBG (DBG_ASIC, "DecStep=%d\n", LLF_MotorMove->DecStep); /*set motor uniform speed only for uniform speed //only used for UNIFORM_MOTOR_AND_SCAN_SPEED_ENABLE //If you use acc mode, this two reg are not used. */ Mustek_SendData (chip, ES01_FD_MotorFixedspeedLSB, LOBYTE (LLF_MotorMove->FixMoveSpeed)); Mustek_SendData (chip, ES01_FE_MotorFixedspeedMSB, HIBYTE (LLF_MotorMove->FixMoveSpeed)); DBG (DBG_ASIC, "FixMoveSpeed=%d\n", LLF_MotorMove->FixMoveSpeed); /*Set motor type */ Mustek_SendData (chip, ES01_A6_MotorOption, LLF_MotorMove->MotorSelect | LLF_MotorMove->HomeSensorSelect | LLF_MotorMove-> MotorMoveUnit); /*Set motor speed unit, for all motor mode, //include uniform, acc, motor speed of scan */ Mustek_SendData (chip, ES01_F6_MorotControl1, LLF_MotorMove->MotorSpeedUnit | LLF_MotorMove-> MotorSyncUnit); /* below is setting action register */ if (LLF_MotorMove->ActionType == ACTION_TYPE_BACKTOHOME) { DBG (DBG_ASIC, "ACTION_TYPE_BACKTOHOME\n"); temp_motor_action = MOTOR_BACK_HOME_AFTER_SCAN_ENABLE; motor_steps = 30000 * 2; } else { DBG (DBG_ASIC, "Forward or Backward\n"); temp_motor_action = MOTOR_MOVE_TO_FIRST_LINE_ENABLE; motor_steps = LLF_MotorMove->FixMoveSteps; if (LLF_MotorMove->ActionType == ACTION_TYPE_BACKWARD) { DBG (DBG_ASIC, "ACTION_TYPE_BACKWARD\n"); temp_motor_action = temp_motor_action | INVERT_MOTOR_DIRECTION_ENABLE; } } if (LLF_MotorMove->ActionType == ACTION_TYPE_TEST_MODE) { DBG (DBG_ASIC, "ACTION_TYPE_TEST_MODE\n"); temp_motor_action = temp_motor_action | MOTOR_MOVE_TO_FIRST_LINE_ENABLE | MOTOR_BACK_HOME_AFTER_SCAN_ENABLE | MOTOR_TEST_LOOP_ENABLE; } Mustek_SendData (chip, ES01_94_PowerSaveControl, 0x27 | LLF_MotorMove->Lamp0PwmFreq | LLF_MotorMove-> Lamp1PwmFreq); /* fix speed move steps */ Mustek_SendData (chip, ES01_E2_MotorStepOfMaxSpeed0_7, LOBYTE (motor_steps)); Mustek_SendData (chip, ES01_E3_MotorStepOfMaxSpeed8_15, HIBYTE (motor_steps)); Mustek_SendData (chip, ES01_E4_MotorStepOfMaxSpeed16_19, (SANE_Byte) ((motor_steps & 0x00ff0000) >> 16)); DBG (DBG_ASIC, "motor_steps=%d\n", motor_steps); DBG (DBG_ASIC, "LOBYTE(motor_steps)=%d\n", LOBYTE (motor_steps)); DBG (DBG_ASIC, "HIBYTE(motor_steps)=%d\n", HIBYTE (motor_steps)); DBG (DBG_ASIC, "(SANE_Byte)((motor_steps & 0x00ff0000) >> 16)=%d\n", (SANE_Byte) ((motor_steps & 0x00ff0000) >> 16)); if (LLF_MotorMove->ActionMode == ACTION_MODE_UNIFORM_SPEED_MOVE) { temp_motor_action = temp_motor_action | UNIFORM_MOTOR_AND_SCAN_SPEED_ENABLE; } Mustek_SendData (chip, ES01_F3_ActionOption, SCAN_DISABLE | SCAN_BACK_TRACKING_DISABLE | temp_motor_action); Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_ENABLE); if (LLF_MotorMove->WaitOrNoWait == 1) { if (LLF_MotorMove->ActionType == ACTION_TYPE_BACKTOHOME) { DBG (DBG_ASIC, "ACTION_TYPE_BACKTOHOME\n"); Asic_WaitCarriageHome (chip, FALSE); } else { Asic_WaitUnitReady (chip); } } DBG (DBG_ASIC, "LLFMotorMove:Exit\n"); return status; } static SANE_Status SetMotorStepTable (PAsic chip, LLF_MOTORMOVE * MotorStepsTable, unsigned short wStartY, unsigned int dwScanImageSteps, unsigned short wYResolution) { SANE_Status status = SANE_STATUS_GOOD; unsigned short wAccSteps = 511; unsigned short wForwardSteps = 20; SANE_Byte bDecSteps = 255; unsigned short wMotorSycnPixelNumber = 0; unsigned short wScanAccSteps = 511; SANE_Byte bScanDecSteps = 255; unsigned short wFixScanSteps = 20; unsigned short wScanBackTrackingSteps = 40; unsigned short wScanRestartSteps = 40; unsigned short wScanBackHomeExtSteps = 100; unsigned int dwTotalMotorSteps; DBG (DBG_ASIC, "SetMotorStepTable:Enter\n"); dwTotalMotorSteps = dwScanImageSteps; switch (wYResolution) { case 2400: case 1200: wScanAccSteps = 100; bScanDecSteps = 10; wScanBackTrackingSteps = 10; wScanRestartSteps = 10; break; case 600: case 300: wScanAccSteps = 300; bScanDecSteps = 40; break; case 150: wScanAccSteps = 300; bScanDecSteps = 40; break; case 100: case 75: case 50: wScanAccSteps = 300; bScanDecSteps = 40; break; } if (wStartY < (wAccSteps + wForwardSteps + bDecSteps + wScanAccSteps)) /*not including T0,T1 steps */ { wAccSteps = 1; bDecSteps = 1; wFixScanSteps = (wStartY - wScanAccSteps) > 0 ? (wStartY - wScanAccSteps) : 0; wForwardSteps = 0; chip->isMotorGoToFirstLine = MOTOR_MOVE_TO_FIRST_LINE_DISABLE; } else { wForwardSteps = (wStartY - wAccSteps - (unsigned short) bDecSteps - wScanAccSteps - wFixScanSteps) > 0 ? (wStartY - wAccSteps - (unsigned short) bDecSteps - wScanAccSteps - wFixScanSteps) : 0; chip->isMotorGoToFirstLine = MOTOR_MOVE_TO_FIRST_LINE_ENABLE; } dwTotalMotorSteps += wAccSteps; dwTotalMotorSteps += wForwardSteps; dwTotalMotorSteps += bDecSteps; dwTotalMotorSteps += wScanAccSteps; dwTotalMotorSteps += wFixScanSteps; dwTotalMotorSteps += bScanDecSteps; dwTotalMotorSteps += 2; MotorStepsTable->AccStep = wAccSteps; MotorStepsTable->DecStep = bDecSteps; MotorStepsTable->wForwardSteps = wForwardSteps; MotorStepsTable->wScanAccSteps = wScanAccSteps; MotorStepsTable->bScanDecSteps = bScanDecSteps; MotorStepsTable->wFixScanSteps = wFixScanSteps; MotorStepsTable->MotorSyncUnit = (SANE_Byte) wMotorSycnPixelNumber; MotorStepsTable->wScanBackHomeExtSteps = wScanBackHomeExtSteps; MotorStepsTable->wScanRestartSteps = wScanRestartSteps; MotorStepsTable->wScanBackTrackingSteps = wScanBackTrackingSteps; /*state 1 */ Mustek_SendData (chip, ES01_E0_MotorAccStep0_7, LOBYTE (wAccSteps)); Mustek_SendData (chip, ES01_E1_MotorAccStep8_8, HIBYTE (wAccSteps)); /*state 2 */ Mustek_SendData (chip, ES01_E2_MotorStepOfMaxSpeed0_7, LOBYTE (wForwardSteps)); Mustek_SendData (chip, ES01_E3_MotorStepOfMaxSpeed8_15, HIBYTE (wForwardSteps)); Mustek_SendData (chip, ES01_E4_MotorStepOfMaxSpeed16_19, 0); /*state 3 */ Mustek_SendData (chip, ES01_E5_MotorDecStep, bDecSteps); /*state 4 */ Mustek_SendData (chip, ES01_AE_MotorSyncPixelNumberM16LSB, LOBYTE (wMotorSycnPixelNumber)); Mustek_SendData (chip, ES01_AF_MotorSyncPixelNumberM16MSB, HIBYTE (wMotorSycnPixelNumber)); /*state 5 */ Mustek_SendData (chip, ES01_EC_ScanAccStep0_7, LOBYTE (wScanAccSteps)); Mustek_SendData (chip, ES01_ED_ScanAccStep8_8, HIBYTE (wScanAccSteps)); /*state 6 */ Mustek_SendData (chip, ES01_EE_FixScanStepLSB, LOBYTE (wFixScanSteps)); Mustek_SendData (chip, ES01_8A_FixScanStepMSB, HIBYTE (wFixScanSteps)); /*state 8 */ Mustek_SendData (chip, ES01_EF_ScanDecStep, bScanDecSteps); /*state 10 */ Mustek_SendData (chip, ES01_E6_ScanBackTrackingStepLSB, LOBYTE (wScanBackTrackingSteps)); Mustek_SendData (chip, ES01_E7_ScanBackTrackingStepMSB, HIBYTE (wScanBackTrackingSteps)); /*state 15 */ Mustek_SendData (chip, ES01_E8_ScanRestartStepLSB, LOBYTE (wScanRestartSteps)); Mustek_SendData (chip, ES01_E9_ScanRestartStepMSB, HIBYTE (wScanRestartSteps)); /*state 19 */ Mustek_SendData (chip, ES01_EA_ScanBackHomeExtStepLSB, LOBYTE (wScanBackHomeExtSteps)); Mustek_SendData (chip, ES01_EB_ScanBackHomeExtStepMSB, HIBYTE (wScanBackHomeExtSteps)); /*total motor steps */ Mustek_SendData (chip, ES01_F0_ScanImageStep0_7, LOBYTE (dwTotalMotorSteps)); Mustek_SendData (chip, ES01_F1_ScanImageStep8_15, HIBYTE (dwTotalMotorSteps)); Mustek_SendData (chip, ES01_F2_ScanImageStep16_19, (SANE_Byte) ((dwTotalMotorSteps & 0x00ff0000) >> 16)); DBG (DBG_ASIC, "SetMotorStepTable:Exit\n"); return status; } static SANE_Status CalculateMotorTable (LLF_CALCULATEMOTORTABLE * lpCalculateMotorTable, unsigned short wYResolution) { SANE_Status status = SANE_STATUS_GOOD; unsigned short i; unsigned short wEndSpeed, wStartSpeed; unsigned short wScanAccSteps; SANE_Byte bScanDecSteps; double PI = 3.1415926; double x = PI / 2; long double y; unsigned short *lpMotorTable; DBG (DBG_ASIC, "CalculateMotorTable:Enter\n"); wStartSpeed = lpCalculateMotorTable->StartSpeed; wEndSpeed = lpCalculateMotorTable->EndSpeed; wScanAccSteps = lpCalculateMotorTable->AccStepBeforeScan; bScanDecSteps = lpCalculateMotorTable->DecStepAfterScan; lpMotorTable = lpCalculateMotorTable->lpMotorTable; /*Motor T0 & T6 Acc Table */ for (i = 0; i < 512; i++) { y = (6000 - 3500); y *= (pow (0.09, (x * i) / 512) - pow (0.09, (x * 511) / 512)); y += 4500; *((unsigned short *) lpMotorTable + i) = (unsigned short) y; /*T0 */ *((unsigned short *) lpMotorTable + i + 512 * 6) = (unsigned short) y; /*T6 */ } /*Motor T1 & T7 Dec Table */ for (i = 0; i < 256; i++) { y = (6000 - 3500); y *= pow (0.3, (x * i) / 256); y = 6000 - y; *((unsigned short *) lpMotorTable + i + 512) = (unsigned short) y; /*T1 */ *((unsigned short *) lpMotorTable + i + 512 * 7) = (unsigned short) y; /*T7 */ } switch (wYResolution) { case 2400: case 1200: case 600: case 300: case 150: case 100: case 75: case 50: for (i = 0; i < wScanAccSteps; i++) { y = (wStartSpeed - wEndSpeed); y *= (pow (0.09, (x * i) / wScanAccSteps) - pow (0.09, (x * (wScanAccSteps - 1)) / wScanAccSteps)); y += wEndSpeed; *((unsigned short *) lpMotorTable + i + 512 * 2) = (unsigned short) y; /*T2 */ *((unsigned short *) lpMotorTable + i + 512 * 4) = (unsigned short) y; /*T4 */ } for (i = wScanAccSteps; i < 512; i++) { *((unsigned short *) lpMotorTable + i + 512 * 2) = (unsigned short) wEndSpeed; /*T2 */ *((unsigned short *) lpMotorTable + i + 512 * 4) = (unsigned short) wEndSpeed; /*T4 */ } for (i = 0; i < (unsigned short) bScanDecSteps; i++) { y = (wStartSpeed - wEndSpeed); y *= pow (0.3, (x * i) / bScanDecSteps); y = wStartSpeed - y; *((unsigned short *) lpMotorTable + i + 512 * 3) = (unsigned short) (y); /*T3 */ *((unsigned short *) lpMotorTable + i + 512 * 5) = (unsigned short) (y); /*T5 */ } for (i = bScanDecSteps; i < 256; i++) { *((unsigned short *) lpMotorTable + i + 512 * 3) = (unsigned short) wStartSpeed; /*T3 */ *((unsigned short *) lpMotorTable + i + 512 * 5) = (unsigned short) wStartSpeed; /*T5 */ } break; } DBG (DBG_ASIC, "CalculateMotorTable:Exit\n"); return status; } static SANE_Status LLFCalculateMotorTable (LLF_CALCULATEMOTORTABLE * LLF_CalculateMotorTable) { SANE_Status status = SANE_STATUS_GOOD; unsigned short i; double PI = 3.1415926535; double x; DBG (DBG_ASIC, "LLF_CALCULATEMOTORTABLE:Enter\n"); x = PI / 2; for (i = 0; i < 512; i++) { /* before scan acc table */ *(LLF_CalculateMotorTable->lpMotorTable + i) = (unsigned short) ((LLF_CalculateMotorTable->StartSpeed - LLF_CalculateMotorTable->EndSpeed) * pow (0.09, (x * i) / 512) + LLF_CalculateMotorTable->EndSpeed); *(LLF_CalculateMotorTable->lpMotorTable + i + 512 * 2) = (unsigned short) ((LLF_CalculateMotorTable->StartSpeed - LLF_CalculateMotorTable->EndSpeed) * pow (0.09, (x * i) / 512) + LLF_CalculateMotorTable->EndSpeed); *(LLF_CalculateMotorTable->lpMotorTable + i + 512 * 4) = (unsigned short) ((LLF_CalculateMotorTable->StartSpeed - LLF_CalculateMotorTable->EndSpeed) * pow (0.09, (x * i) / 512) + LLF_CalculateMotorTable->EndSpeed); *(LLF_CalculateMotorTable->lpMotorTable + i + 512 * 6) = (unsigned short) ((LLF_CalculateMotorTable->StartSpeed - LLF_CalculateMotorTable->EndSpeed) * pow (0.09, (x * i) / 512) + LLF_CalculateMotorTable->EndSpeed); } for (i = 0; i < 255; i++) { *(LLF_CalculateMotorTable->lpMotorTable + i + 512) = (unsigned short) (LLF_CalculateMotorTable->StartSpeed - (LLF_CalculateMotorTable->StartSpeed - LLF_CalculateMotorTable->EndSpeed) * pow (0.3, (x * i) / 256)); *(LLF_CalculateMotorTable->lpMotorTable + i + 512 * 3) = (unsigned short) (LLF_CalculateMotorTable->StartSpeed - (LLF_CalculateMotorTable->StartSpeed - LLF_CalculateMotorTable->EndSpeed) * pow (0.3, (x * i) / 256)); *(LLF_CalculateMotorTable->lpMotorTable + i + 512 * 5) = (unsigned short) (LLF_CalculateMotorTable->StartSpeed - (LLF_CalculateMotorTable->StartSpeed - LLF_CalculateMotorTable->EndSpeed) * pow (0.3, (x * i) / 256)); *(LLF_CalculateMotorTable->lpMotorTable + i + 512 * 7) = (unsigned short) (LLF_CalculateMotorTable->StartSpeed - (LLF_CalculateMotorTable->StartSpeed - LLF_CalculateMotorTable->EndSpeed) * pow (0.3, (x * i) / 256)); } for (i = 0; i < 512; i++) { /* back acc table */ *(LLF_CalculateMotorTable->lpMotorTable + i) = (unsigned short) ((LLF_CalculateMotorTable->StartSpeed - LLF_CalculateMotorTable->EndSpeed) * pow (0.09, (x * i) / 512) + LLF_CalculateMotorTable->EndSpeed); *(LLF_CalculateMotorTable->lpMotorTable + i + 512 * 6) = (unsigned short) ((LLF_CalculateMotorTable->StartSpeed - LLF_CalculateMotorTable->EndSpeed) * pow (0.09, (x * i) / 512) + LLF_CalculateMotorTable->EndSpeed); } if (LLF_CalculateMotorTable->AccStepBeforeScan == 0) { } else { for (i = 0; i < LLF_CalculateMotorTable->AccStepBeforeScan; i++) { *(LLF_CalculateMotorTable->lpMotorTable + i + 512 * 2) = (unsigned short) ((LLF_CalculateMotorTable->StartSpeed - LLF_CalculateMotorTable->EndSpeed) * (pow (0.09, (x * i) / LLF_CalculateMotorTable-> AccStepBeforeScan) - pow (0.09, (x * (LLF_CalculateMotorTable-> AccStepBeforeScan - 1)) / LLF_CalculateMotorTable-> AccStepBeforeScan)) + LLF_CalculateMotorTable->EndSpeed); } } DBG (DBG_ASIC, "LLF_CALCULATEMOTORTABLE:Exit\n"); return status; } static SANE_Status SetMotorCurrent (PAsic chip, unsigned short dwMotorSpeed, LLF_MOTOR_CURRENT_AND_PHASE * CurrentPhase) { SANE_Status status = SANE_STATUS_GOOD; DBG (DBG_ASIC, "SetMotorCurrent:Enter\n"); (void) chip; if (dwMotorSpeed < 2000) { CurrentPhase->MotorCurrentTableA[0] = 255; CurrentPhase->MotorCurrentTableB[0] = 255; } else if (dwMotorSpeed < 3500) { CurrentPhase->MotorCurrentTableA[0] = 200; CurrentPhase->MotorCurrentTableB[0] = 200; } else if (dwMotorSpeed < 5000) { CurrentPhase->MotorCurrentTableA[0] = 160; CurrentPhase->MotorCurrentTableB[0] = 160; } else if (dwMotorSpeed < 10000) { CurrentPhase->MotorCurrentTableA[0] = 70; CurrentPhase->MotorCurrentTableB[0] = 70; } else if (dwMotorSpeed < 17000) { CurrentPhase->MotorCurrentTableA[0] = 60; CurrentPhase->MotorCurrentTableB[0] = 60; } else if (dwMotorSpeed < 25000) { CurrentPhase->MotorCurrentTableA[0] = 50; CurrentPhase->MotorCurrentTableB[0] = 50; } else { CurrentPhase->MotorCurrentTableA[0] = 50; CurrentPhase->MotorCurrentTableB[0] = 50; } DBG (DBG_ASIC, "SetMotorCurrent:Exit\n"); return status; } static SANE_Status MotorBackHome (PAsic chip, SANE_Byte WaitOrNoWait) { SANE_Status status = SANE_STATUS_GOOD; unsigned short BackHomeMotorTable[512 * 8]; LLF_CALCULATEMOTORTABLE CalMotorTable; LLF_MOTOR_CURRENT_AND_PHASE CurrentPhase; LLF_SETMOTORTABLE LLF_SetMotorTable; LLF_MOTORMOVE MotorMove; DBG (DBG_ASIC, "MotorBackHome:Enter\n"); CalMotorTable.StartSpeed = 5000; CalMotorTable.EndSpeed = 1200; CalMotorTable.AccStepBeforeScan = 511; CalMotorTable.DecStepAfterScan = 255; CalMotorTable.lpMotorTable = BackHomeMotorTable; LLFCalculateMotorTable (&CalMotorTable); CurrentPhase.MotorCurrentTableA[0] = 220; CurrentPhase.MotorCurrentTableB[0] = 220; CurrentPhase.MoveType = _4_TABLE_SPACE_FOR_FULL_STEP; LLFSetMotorCurrentAndPhase (chip, &CurrentPhase); LLF_SetMotorTable.MotorTableAddress = 0; LLF_SetMotorTable.MotorTablePtr = BackHomeMotorTable; LLFSetMotorTable (chip, &LLF_SetMotorTable); MotorMove.MotorSelect = MOTOR_0_ENABLE | MOTOR_1_DISABLE; MotorMove.MotorMoveUnit = ES03_TABLE_DEFINE; MotorMove.MotorSpeedUnit = SPEED_UNIT_1_PIXEL_TIME; MotorMove.MotorSyncUnit = MOTOR_SYNC_UNIT_1_PIXEL_TIME; MotorMove.HomeSensorSelect = HOME_SENSOR_0_ENABLE; MotorMove.ActionMode = ACTION_MODE_ACCDEC_MOVE; MotorMove.ActionType = ACTION_TYPE_BACKTOHOME; MotorMove.AccStep = 511; MotorMove.DecStep = 255; MotorMove.FixMoveSteps = 0; MotorMove.FixMoveSpeed = 3000; MotorMove.WaitOrNoWait = WaitOrNoWait; LLFMotorMove (chip, &MotorMove); DBG (DBG_ASIC, "MotorBackHome:Exit\n"); return status; } static SANE_Status LLFSetRamAddress (PAsic chip, unsigned int dwStartAddr, unsigned int dwEndAddr, SANE_Byte byAccessTarget) { SANE_Status status = SANE_STATUS_GOOD; SANE_Byte * pStartAddr = (SANE_Byte *) & dwStartAddr; SANE_Byte * pEndAddr = (SANE_Byte *) & dwEndAddr; DBG (DBG_ASIC, "LLFSetRamAddress:Enter\n"); /*Set start address. */ Mustek_SendData (chip, ES01_A0_HostStartAddr0_7, *(pStartAddr)); Mustek_SendData (chip, ES01_A1_HostStartAddr8_15, *(pStartAddr + 1)); if (byAccessTarget == ACCESS_DRAM) Mustek_SendData (chip, ES01_A2_HostStartAddr16_21, *(pStartAddr + 2) | ACCESS_DRAM); else Mustek_SendData (chip, ES01_A2_HostStartAddr16_21, *(pStartAddr + 2) | ACCESS_GAMMA_RAM); /*Set end address. */ Mustek_SendData (chip, ES01_A3_HostEndAddr0_7, *(pEndAddr)); Mustek_SendData (chip, ES01_A4_HostEndAddr8_15, *(pEndAddr + 1)); Mustek_SendData (chip, ES01_A5_HostEndAddr16_21, *(pEndAddr + 2)); Mustek_ClearFIFO (chip); DBG (DBG_ASIC, "LLFSetRamAddress:Exit\n"); return status; } /* ---------------------- medium level asic functions ---------------------- */ static SANE_Status InitTiming (PAsic chip) { SANE_Status status = SANE_STATUS_GOOD; DBG (DBG_ASIC, "InitTiming:Enter\n"); chip->Timing.AFE_ADCCLK_Timing = 1010580480; chip->Timing.AFE_ADCVS_Timing = 12582912; chip->Timing.AFE_ADCRS_Timing = 3072; chip->Timing.AFE_ChannelA_LatchPos = 3080; chip->Timing.AFE_ChannelB_LatchPos = 3602; chip->Timing.AFE_ChannelC_LatchPos = 5634; chip->Timing.AFE_ChannelD_LatchPos = 1546; chip->Timing.AFE_Secondary_FF_LatchPos = 12; /* Sensor */ chip->Timing.CCD_DummyCycleTiming = 0; chip->Timing.PHTG_PluseWidth = 12; chip->Timing.PHTG_WaitWidth = 1; chip->Timing.PHTG_TimingAdj = 1; chip->Timing.PHTG_TimingSetup = 0; chip->Timing.ChannelR_StartPixel = 100; chip->Timing.ChannelR_EndPixel = 200; chip->Timing.ChannelG_StartPixel = 100; chip->Timing.ChannelG_EndPixel = 200; chip->Timing.ChannelB_StartPixel = 100; chip->Timing.ChannelB_EndPixel = 200; /*1200dpi Timing */ chip->Timing.CCD_PH2_Timing_1200 = 1048320; chip->Timing.CCD_PHRS_Timing_1200 = 983040; chip->Timing.CCD_PHCP_Timing_1200 = 61440; chip->Timing.CCD_PH1_Timing_1200 = 4293918720u; chip->Timing.DE_CCD_SETUP_REGISTER_1200 = 32; chip->Timing.wCCDPixelNumber_1200 = 11250; /*600dpi Timing */ chip->Timing.CCD_PH2_Timing_600 = 1048320; chip->Timing.CCD_PHRS_Timing_600 = 983040; chip->Timing.CCD_PHCP_Timing_600 = 61440; chip->Timing.CCD_PH1_Timing_600 = 4293918720u; chip->Timing.DE_CCD_SETUP_REGISTER_600 = 0; chip->Timing.wCCDPixelNumber_600 = 7500; DBG (DBG_ASIC, "InitTiming:Exit\n"); return status; } static SANE_Status OpenScanChip (PAsic chip) { SANE_Status status = SANE_STATUS_GOOD; SANE_Byte x[4]; DBG (DBG_ASIC, "OpenScanChip:Enter\n"); x[0] = 0x64; x[1] = 0x64; x[2] = 0x64; x[3] = 0x64; status = WriteIOControl (chip, 0x90, 0, 4, x); if (status != SANE_STATUS_GOOD) return status; x[0] = 0x65; x[1] = 0x65; x[2] = 0x65; x[3] = 0x65; status = WriteIOControl (chip, 0x90, 0, 4, x); if (status != SANE_STATUS_GOOD) return status; x[0] = 0x44; x[1] = 0x44; x[2] = 0x44; x[3] = 0x44; status = WriteIOControl (chip, 0x90, 0, 4, x); if (status != SANE_STATUS_GOOD) return status; x[0] = 0x45; x[1] = 0x45; x[2] = 0x45; x[3] = 0x45; status = WriteIOControl (chip, 0x90, 0, 4, x); DBG (DBG_ASIC, "OpenScanChip: Exit\n"); return status; } static SANE_Status CloseScanChip (PAsic chip) { SANE_Status status = SANE_STATUS_GOOD; SANE_Byte x[4]; DBG (DBG_ASIC, "CloseScanChip:Enter\n"); x[0] = 0x64; x[1] = 0x64; x[2] = 0x64; x[3] = 0x64; status = WriteIOControl (chip, 0x90, 0, 4, x); if (status != SANE_STATUS_GOOD) return status; x[0] = 0x65; x[1] = 0x65; x[2] = 0x65; x[3] = 0x65; status = WriteIOControl (chip, 0x90, 0, 4, x); if (status != SANE_STATUS_GOOD) return status; x[0] = 0x16; x[1] = 0x16; x[2] = 0x16; x[3] = 0x16; status = WriteIOControl (chip, 0x90, 0, 4, x); if (status != SANE_STATUS_GOOD) return status; x[0] = 0x17; x[1] = 0x17; x[2] = 0x17; x[3] = 0x17; status = WriteIOControl (chip, 0x90, 0, 4, x); DBG (DBG_ASIC, "CloseScanChip: Exit\n"); return status; } static SANE_Status SafeInitialChip (PAsic chip) { SANE_Status status = SANE_STATUS_GOOD; DBG (DBG_ASIC, "SafeInitialChip:Enter\n"); Mustek_SendData (chip, ES01_F3_ActionOption, 0); Mustek_SendData (chip, ES01_86_DisableAllClockWhenIdle, CLOSE_ALL_CLOCK_DISABLE); Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_DISABLE); status = Asic_WaitUnitReady (chip); DBG (DBG_ASIC, "isFirstOpenChip=%d\n", chip->isFirstOpenChip); if (chip->isFirstOpenChip) { DBG (DBG_ASIC, "isFirstOpenChip=%d\n", chip->isFirstOpenChip); status = DRAM_Test (chip); if (status != SANE_STATUS_GOOD) { DBG (DBG_ASIC, "DRAM_Test: Error\n"); return status; } chip->isFirstOpenChip = FALSE; } DBG (DBG_ASIC, "SafeInitialChip: exit\n"); return status; } static SANE_Status DRAM_Test (PAsic chip) { SANE_Status status = SANE_STATUS_GOOD; unsigned char *temps; unsigned int i; DBG (DBG_ASIC, "DRAM_Test:Enter\n"); temps = (unsigned char *) malloc (64); for (i = 0; i < 64; i++) { *(temps + i) = i; } /*set start address */ status = Mustek_SendData (chip, ES01_A0_HostStartAddr0_7, 0x00); if (status != SANE_STATUS_GOOD) { free (temps); return status; } status = Mustek_SendData (chip, ES01_A1_HostStartAddr8_15, 0x00); if (status != SANE_STATUS_GOOD) { free (temps); return status; } status = Mustek_SendData (chip, ES01_A2_HostStartAddr16_21, 0x00 | ACCESS_DRAM); if (status != SANE_STATUS_GOOD) { free (temps); return status; } Mustek_SendData (chip, ES01_79_AFEMCLK_SDRAMCLK_DELAY_CONTROL, SDRAMCLK_DELAY_12_ns); status = Mustek_SendData (chip, ES01_A3_HostEndAddr0_7, 0xff); if (status != SANE_STATUS_GOOD) { free (temps); return status; } status = Mustek_SendData (chip, ES01_A4_HostEndAddr8_15, 0xff); if (status != SANE_STATUS_GOOD) { free (temps); return status; } status = Mustek_SendData (chip, ES01_A5_HostEndAddr16_21, 0xff); if (status != SANE_STATUS_GOOD) { free (temps); return status; } status = Mustek_DMAWrite (chip, 64, (SANE_Byte *) (temps)); if (status != SANE_STATUS_GOOD) { DBG (DBG_ASIC, "Mustek_DMAWrite error\n"); free (temps); return status; } status = Mustek_SendData (chip, ES01_A0_HostStartAddr0_7, 0x00); if (status != SANE_STATUS_GOOD) { free (temps); return status; } status = Mustek_SendData (chip, ES01_A1_HostStartAddr8_15, 0x00); if (status != SANE_STATUS_GOOD) { free (temps); return status; } status = Mustek_SendData (chip, ES01_A2_HostStartAddr16_21, 0x00 | ACCESS_DRAM); if (status != SANE_STATUS_GOOD) { free (temps); return status; } /*set end address */ status = Mustek_SendData (chip, ES01_A3_HostEndAddr0_7, 0xff); if (status != SANE_STATUS_GOOD) { free (temps); return status; } status = Mustek_SendData (chip, ES01_A4_HostEndAddr8_15, 0xff); if (status != SANE_STATUS_GOOD) { free (temps); return status; } status = Mustek_SendData (chip, ES01_A5_HostEndAddr16_21, 0xff); if (status != SANE_STATUS_GOOD) { free (temps); return status; } memset (temps, 0, 64); status = Mustek_DMARead (chip, 64, (SANE_Byte *) (temps)); if (status != SANE_STATUS_GOOD) { free (temps); return status; } for (i = 0; i < 60; i += 10) { DBG (DBG_ASIC, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", *(temps + i), *(temps + i + 1), *(temps + i + 2), *(temps + i + 3), *(temps + i + 4), *(temps + i + 5), *(temps + i + 6), *(temps + i + 7), *(temps + i + 8), *(temps + i + 9)); } for (i = 0; i < 64; i++) { if (*(temps + i) != i) { DBG (DBG_ERR, "DRAM Test error...(No.=%d)\n", i + 1); return SANE_STATUS_IO_ERROR; } } free (temps); DBG (DBG_ASIC, "DRAM_Text: Exit\n"); return status; } #if SANE_UNUSED static SANE_Status SetPowerSave (PAsic chip) { SANE_Status status = SANE_STATUS_GOOD; DBG (DBG_ASIC, "SetPowerSave:Enter\n"); if (chip->firmwarestate < FS_OPENED) OpenScanChip (chip); if (chip->firmwarestate > FS_OPENED) Asic_ScanStop (chip); Mustek_SendData (chip, ES01_94_PowerSaveControl, 0x10); chip->firmwarestate = FS_OPENED; DBG (DBG_ASIC, "SetPowerSave:Exit\n"); return status; } #endif static SANE_Status SetLineTimeAndExposure (PAsic chip) { SANE_Status status = SANE_STATUS_GOOD; DBG (DBG_ASIC, "SetLineTimeAndExposure:Enter\n"); if (chip->firmwarestate < FS_OPENED) OpenScanChip (chip); Mustek_SendData (chip, ES01_C4_MultiTGTimesRed, 0); Mustek_SendData (chip, ES01_C5_MultiTGTimesGreen, 0); Mustek_SendData (chip, ES01_C6_MultiTGTimesBlue, 0); Mustek_SendData (chip, ES01_C7_MultiTGDummyPixelNumberLSB, 0); Mustek_SendData (chip, ES01_C8_MultiTGDummyPixelNumberMSB, 0); Mustek_SendData (chip, ES01_C9_CCDDummyPixelNumberLSB, 0); Mustek_SendData (chip, ES01_CA_CCDDummyPixelNumberMSB, 0); Mustek_SendData (chip, ES01_CB_CCDDummyCycleNumber, 0); chip->firmwarestate = FS_OPENED; DBG (DBG_ASIC, "SetLineTimeAndExposure:Exit\n"); return status; } static SANE_Status CCDTiming (PAsic chip) { SANE_Status status = SANE_STATUS_GOOD; unsigned int dwPH1, dwPH2, dwPHRS, dwPHCP; DBG (DBG_ASIC, "CCDTiming:Enter\n"); DBG (DBG_ASIC, "Dpi=%d\n", chip->Scan.Dpi); if (chip->firmwarestate < FS_OPENED) OpenScanChip (chip); Mustek_SendData (chip, ES01_82_AFE_ADCCLK_TIMING_ADJ_BYTE0, (SANE_Byte) (chip->Timing.AFE_ADCCLK_Timing)); Mustek_SendData (chip, ES01_83_AFE_ADCCLK_TIMING_ADJ_BYTE1, (SANE_Byte) (chip->Timing.AFE_ADCCLK_Timing >> 8)); Mustek_SendData (chip, ES01_84_AFE_ADCCLK_TIMING_ADJ_BYTE2, (SANE_Byte) (chip->Timing.AFE_ADCCLK_Timing >> 16)); Mustek_SendData (chip, ES01_85_AFE_ADCCLK_TIMING_ADJ_BYTE3, (SANE_Byte) (chip->Timing.AFE_ADCCLK_Timing >> 24)); Mustek_SendData (chip, ES01_1F0_AFERS_TIMING_ADJ_B0, (SANE_Byte) (chip->Timing.AFE_ADCRS_Timing)); Mustek_SendData (chip, ES01_1F1_AFERS_TIMING_ADJ_B1, (SANE_Byte) (chip->Timing.AFE_ADCRS_Timing >> 8)); Mustek_SendData (chip, ES01_1F2_AFERS_TIMING_ADJ_B2, (SANE_Byte) (chip->Timing.AFE_ADCRS_Timing >> 16)); Mustek_SendData (chip, ES01_1F3_AFERS_TIMING_ADJ_B3, (SANE_Byte) (chip->Timing.AFE_ADCRS_Timing >> 24)); Mustek_SendData (chip, ES01_1EC_AFEVS_TIMING_ADJ_B0, (SANE_Byte) (chip->Timing.AFE_ADCVS_Timing)); Mustek_SendData (chip, ES01_1ED_AFEVS_TIMING_ADJ_B1, (SANE_Byte) (chip->Timing.AFE_ADCVS_Timing >> 8)); Mustek_SendData (chip, ES01_1EE_AFEVS_TIMING_ADJ_B2, (SANE_Byte) (chip->Timing.AFE_ADCVS_Timing >> 16)); Mustek_SendData (chip, ES01_1EF_AFEVS_TIMING_ADJ_B3, (SANE_Byte) (chip->Timing.AFE_ADCVS_Timing >> 24)); Mustek_SendData (chip, ES01_160_CHANNEL_A_LATCH_POSITION_HB, HIBYTE (chip->Timing.AFE_ChannelA_LatchPos)); Mustek_SendData (chip, ES01_161_CHANNEL_A_LATCH_POSITION_LB, LOBYTE (chip->Timing.AFE_ChannelA_LatchPos)); Mustek_SendData (chip, ES01_162_CHANNEL_B_LATCH_POSITION_HB, HIBYTE (chip->Timing.AFE_ChannelB_LatchPos)); Mustek_SendData (chip, ES01_163_CHANNEL_B_LATCH_POSITION_LB, LOBYTE (chip->Timing.AFE_ChannelB_LatchPos)); Mustek_SendData (chip, ES01_164_CHANNEL_C_LATCH_POSITION_HB, HIBYTE (chip->Timing.AFE_ChannelC_LatchPos)); Mustek_SendData (chip, ES01_165_CHANNEL_C_LATCH_POSITION_LB, LOBYTE (chip->Timing.AFE_ChannelC_LatchPos)); Mustek_SendData (chip, ES01_166_CHANNEL_D_LATCH_POSITION_HB, HIBYTE (chip->Timing.AFE_ChannelD_LatchPos)); Mustek_SendData (chip, ES01_167_CHANNEL_D_LATCH_POSITION_LB, LOBYTE (chip->Timing.AFE_ChannelD_LatchPos)); Mustek_SendData (chip, ES01_168_SECONDARY_FF_LATCH_POSITION, chip->Timing.AFE_Secondary_FF_LatchPos); Mustek_SendData (chip, ES01_1D0_DUMMY_CYCLE_TIMING_B0, (SANE_Byte) (chip->Timing.CCD_DummyCycleTiming)); Mustek_SendData (chip, ES01_1D1_DUMMY_CYCLE_TIMING_B1, (SANE_Byte) (chip->Timing.CCD_DummyCycleTiming >> 8)); Mustek_SendData (chip, ES01_1D2_DUMMY_CYCLE_TIMING_B2, (SANE_Byte) (chip->Timing.CCD_DummyCycleTiming >> 16)); Mustek_SendData (chip, ES01_1D3_DUMMY_CYCLE_TIMING_B3, (SANE_Byte) (chip->Timing.CCD_DummyCycleTiming >> 24)); if (chip->Scan.Dpi >= 1200) { dwPH1 = chip->Timing.CCD_PH1_Timing_1200; dwPH2 = chip->Timing.CCD_PH2_Timing_1200; dwPHRS = chip->Timing.CCD_PHRS_Timing_1200; dwPHCP = chip->Timing.CCD_PHCP_Timing_1200; } else { dwPH1 = chip->Timing.CCD_PH1_Timing_600; dwPH2 = chip->Timing.CCD_PH2_Timing_600; dwPHRS = chip->Timing.CCD_PHRS_Timing_600; dwPHCP = chip->Timing.CCD_PHCP_Timing_600; } Mustek_SendData (chip, ES01_1D4_PH1_TIMING_ADJ_B0, (SANE_Byte) (dwPH1)); Mustek_SendData (chip, ES01_1D5_PH1_TIMING_ADJ_B1, (SANE_Byte) (dwPH1 >> 8)); Mustek_SendData (chip, ES01_1D6_PH1_TIMING_ADJ_B2, (SANE_Byte) (dwPH1 >> 16)); Mustek_SendData (chip, ES01_1D7_PH1_TIMING_ADJ_B3, (SANE_Byte) (dwPH1 >> 24)); /* set ccd ph1 ph2 rs cp */ Mustek_SendData (chip, ES01_D0_PH1_0, 0); Mustek_SendData (chip, ES01_D1_PH2_0, 4); Mustek_SendData (chip, ES01_D4_PHRS_0, 0); Mustek_SendData (chip, ES01_D5_PHCP_0, 0); Mustek_SendData (chip, ES01_1D8_PH2_TIMING_ADJ_B0, (SANE_Byte) (dwPH2)); Mustek_SendData (chip, ES01_1D9_PH2_TIMING_ADJ_B1, (SANE_Byte) (dwPH2 >> 8)); Mustek_SendData (chip, ES01_1DA_PH2_TIMING_ADJ_B2, (SANE_Byte) (dwPH2 >> 16)); Mustek_SendData (chip, ES01_1DB_PH2_TIMING_ADJ_B3, (SANE_Byte) (dwPH2 >> 24)); Mustek_SendData (chip, ES01_1E4_PHRS_TIMING_ADJ_B0, (SANE_Byte) (dwPHRS)); Mustek_SendData (chip, ES01_1E5_PHRS_TIMING_ADJ_B1, (SANE_Byte) (dwPHRS >> 8)); Mustek_SendData (chip, ES01_1E6_PHRS_TIMING_ADJ_B2, (SANE_Byte) (dwPHRS >> 16)); Mustek_SendData (chip, ES01_1E7_PHRS_TIMING_ADJ_B3, (SANE_Byte) (dwPHRS >> 24)); Mustek_SendData (chip, ES01_1E8_PHCP_TIMING_ADJ_B0, (SANE_Byte) (dwPHCP)); Mustek_SendData (chip, ES01_1E9_PHCP_TIMING_ADJ_B1, (SANE_Byte) (dwPHCP >> 8)); Mustek_SendData (chip, ES01_1EA_PHCP_TIMING_ADJ_B2, (SANE_Byte) (dwPHCP >> 16)); Mustek_SendData (chip, ES01_1EB_PHCP_TIMING_ADJ_B3, (SANE_Byte) (dwPHCP >> 24)); chip->firmwarestate = FS_OPENED; DBG (DBG_ASIC, "CCDTiming:Exit\n"); return status; } static SANE_Status IsCarriageHome (PAsic chip, SANE_Bool * LampHome, SANE_Bool * TAHome) { SANE_Status status = SANE_STATUS_GOOD; SANE_Byte temp; DBG (DBG_ASIC, "IsCarriageHome:Enter\n"); status = GetChipStatus (chip, 0, &temp); if (status != SANE_STATUS_GOOD) { DBG (DBG_ASIC, "IsCarriageHome:Error!\n"); return status; } if ((temp & SENSOR0_DETECTED) == SENSOR0_DETECTED) *LampHome = TRUE; else { *LampHome = FALSE; } *TAHome = TRUE; DBG (DBG_ASIC, "LampHome=%d\n", *LampHome); DBG (DBG_ASIC, "IsCarriageHome:Exit\n"); return status; } static SANE_Status GetChipStatus (PAsic chip, SANE_Byte Selector, SANE_Byte * ChipStatus) { SANE_Status status = SANE_STATUS_GOOD; DBG (DBG_ASIC, "GetChipStatus:Enter\n"); status = Mustek_SendData (chip, ES01_8B_Status, Selector); if (status != SANE_STATUS_GOOD) return status; status = Mustek_WriteAddressLineForRegister (chip, ES01_8B_Status); if (status != SANE_STATUS_GOOD) return status; *ChipStatus = ES01_8B_Status; status = Mustek_ReceiveData (chip, ChipStatus); if (status != SANE_STATUS_GOOD) return status; DBG (DBG_ASIC, "GetChipStatus:Exit\n"); return status; } static SANE_Status SetAFEGainOffset (PAsic chip) { SANE_Status status = SANE_STATUS_GOOD; int i = 0; DBG (DBG_ASIC, "SetAFEGainOffset:Enter\n"); if (chip->AD.DirectionR) { /* Negative */ Mustek_SendData (chip, ES01_60_AFE_AUTO_GAIN_OFFSET_RED_LB, (chip->AD.GainR << 1) | 0x01); Mustek_SendData (chip, ES01_61_AFE_AUTO_GAIN_OFFSET_RED_HB, chip->AD.OffsetR); } else { /* Positive */ Mustek_SendData (chip, ES01_60_AFE_AUTO_GAIN_OFFSET_RED_LB, (chip->AD.GainR << 1)); Mustek_SendData (chip, ES01_61_AFE_AUTO_GAIN_OFFSET_RED_HB, chip->AD.OffsetR); } if (chip->AD.DirectionG) { Mustek_SendData (chip, ES01_62_AFE_AUTO_GAIN_OFFSET_GREEN_LB, (chip->AD.GainG << 1) | 0x01); Mustek_SendData (chip, ES01_63_AFE_AUTO_GAIN_OFFSET_GREEN_HB, chip->AD.OffsetG); } else { Mustek_SendData (chip, ES01_62_AFE_AUTO_GAIN_OFFSET_GREEN_LB, (chip->AD.GainG << 1)); Mustek_SendData (chip, ES01_63_AFE_AUTO_GAIN_OFFSET_GREEN_HB, chip->AD.OffsetG); } if (chip->AD.DirectionB) { Mustek_SendData (chip, ES01_64_AFE_AUTO_GAIN_OFFSET_BLUE_LB, (chip->AD.GainB << 1) | 0x01); Mustek_SendData (chip, ES01_65_AFE_AUTO_GAIN_OFFSET_BLUE_HB, chip->AD.OffsetB); } else { Mustek_SendData (chip, ES01_64_AFE_AUTO_GAIN_OFFSET_BLUE_LB, (chip->AD.GainB << 1)); Mustek_SendData (chip, ES01_65_AFE_AUTO_GAIN_OFFSET_BLUE_HB, chip->AD.OffsetB); } Mustek_SendData (chip, ES01_2A0_AFE_GAIN_OFFSET_CONTROL, 0x01); for (i = 0; i < 4; i++) { if (chip->AD.DirectionR == 0) { Mustek_SendData (chip, ES01_2A1_AFE_AUTO_CONFIG_GAIN, (SANE_Byte) (chip->AD.GainR << 1)); Mustek_SendData (chip, ES01_2A2_AFE_AUTO_CONFIG_OFFSET, (SANE_Byte) (chip->AD.OffsetR)); } else { Mustek_SendData (chip, ES01_2A1_AFE_AUTO_CONFIG_GAIN, (SANE_Byte) (chip->AD.GainR << 1) | 0x01); Mustek_SendData (chip, ES01_2A2_AFE_AUTO_CONFIG_OFFSET, (SANE_Byte) (chip->AD.OffsetR)); } } for (i = 0; i < 4; i++) { if (chip->AD.DirectionG == 0) { Mustek_SendData (chip, ES01_2A1_AFE_AUTO_CONFIG_GAIN, (SANE_Byte) (chip->AD.GainG << 1)); Mustek_SendData (chip, ES01_2A2_AFE_AUTO_CONFIG_OFFSET, (SANE_Byte) (chip->AD.OffsetG)); } else { Mustek_SendData (chip, ES01_2A1_AFE_AUTO_CONFIG_GAIN, (SANE_Byte) (chip->AD.GainG << 1) | 0x01); Mustek_SendData (chip, ES01_2A2_AFE_AUTO_CONFIG_OFFSET, (SANE_Byte) (chip->AD.OffsetG)); } } for (i = 0; i < 4; i++) { if (chip->AD.DirectionB == 0) { Mustek_SendData (chip, ES01_2A1_AFE_AUTO_CONFIG_GAIN, (SANE_Byte) (chip->AD.GainB << 1)); Mustek_SendData (chip, ES01_2A2_AFE_AUTO_CONFIG_OFFSET, (SANE_Byte) (chip->AD.OffsetB)); } else { Mustek_SendData (chip, ES01_2A1_AFE_AUTO_CONFIG_GAIN, (SANE_Byte) (chip->AD.GainB << 1) | 0x01); Mustek_SendData (chip, ES01_2A2_AFE_AUTO_CONFIG_OFFSET, (SANE_Byte) (chip->AD.OffsetB)); } } for (i = 0; i < 36; i++) { Mustek_SendData (chip, ES01_2A1_AFE_AUTO_CONFIG_GAIN, 0); Mustek_SendData (chip, ES01_2A2_AFE_AUTO_CONFIG_OFFSET, 0); } Mustek_SendData (chip, ES01_2A0_AFE_GAIN_OFFSET_CONTROL, 0x00); /* Set to AFE */ Mustek_SendData (chip, ES01_04_ADAFEPGACH1, chip->AD.GainR); Mustek_SendData (chip, ES01_06_ADAFEPGACH2, chip->AD.GainG); Mustek_SendData (chip, ES01_08_ADAFEPGACH3, chip->AD.GainB); if (chip->AD.DirectionR) Mustek_SendData (chip, ES01_0B_AD9826OffsetRedN, chip->AD.OffsetR); else Mustek_SendData (chip, ES01_0A_AD9826OffsetRedP, chip->AD.OffsetR); if (chip->AD.DirectionG) Mustek_SendData (chip, ES01_0D_AD9826OffsetGreenN, chip->AD.OffsetG); else Mustek_SendData (chip, ES01_0C_AD9826OffsetGreenP, chip->AD.OffsetG); if (chip->AD.DirectionB) Mustek_SendData (chip, ES01_0F_AD9826OffsetBlueN, chip->AD.OffsetB); else Mustek_SendData (chip, ES01_0E_AD9826OffsetBlueP, chip->AD.OffsetB); LLFSetRamAddress (chip, 0x0, PackAreaStartAddress - (512 * 8 - 1), ACCESS_DRAM); Mustek_SendData (chip, ES01_F3_ActionOption, MOTOR_MOVE_TO_FIRST_LINE_DISABLE | MOTOR_BACK_HOME_AFTER_SCAN_DISABLE | SCAN_ENABLE | SCAN_BACK_TRACKING_DISABLE | INVERT_MOTOR_DIRECTION_DISABLE | UNIFORM_MOTOR_AND_SCAN_SPEED_ENABLE | ES01_STATIC_SCAN_DISABLE | MOTOR_TEST_LOOP_DISABLE); Mustek_SendData (chip, ES01_9A_AFEControl, AD9826_AFE | AUTO_CHANGE_AFE_GAIN_OFFSET_DISABLE); Mustek_SendData (chip, ES01_00_ADAFEConfiguration, 0x70); Mustek_SendData (chip, ES01_02_ADAFEMuxConfig, 0x80); DBG (DBG_ASIC, "SetAFEGainOffset:Exit\n"); return status; } static SANE_Status SetLEDTime (PAsic chip) { SANE_Status status = SANE_STATUS_GOOD; DBG (DBG_ASIC, "SetLEDTime:Enter\n"); Mustek_SendData (chip, ES01_B8_ChannelRedExpStartPixelLSB, LOBYTE (chip->Timing.ChannelR_StartPixel)); Mustek_SendData (chip, ES01_B9_ChannelRedExpStartPixelMSB, HIBYTE (chip->Timing.ChannelR_StartPixel)); Mustek_SendData (chip, ES01_BA_ChannelRedExpEndPixelLSB, LOBYTE (chip->Timing.ChannelR_EndPixel)); Mustek_SendData (chip, ES01_BB_ChannelRedExpEndPixelMSB, HIBYTE (chip->Timing.ChannelR_EndPixel)); Mustek_SendData (chip, ES01_BC_ChannelGreenExpStartPixelLSB, LOBYTE (chip->Timing.ChannelG_StartPixel)); Mustek_SendData (chip, ES01_BD_ChannelGreenExpStartPixelMSB, HIBYTE (chip->Timing.ChannelG_StartPixel)); Mustek_SendData (chip, ES01_BE_ChannelGreenExpEndPixelLSB, LOBYTE (chip->Timing.ChannelG_EndPixel)); Mustek_SendData (chip, ES01_BF_ChannelGreenExpEndPixelMSB, HIBYTE (chip->Timing.ChannelG_EndPixel)); Mustek_SendData (chip, ES01_C0_ChannelBlueExpStartPixelLSB, LOBYTE (chip->Timing.ChannelB_StartPixel)); Mustek_SendData (chip, ES01_C1_ChannelBlueExpStartPixelMSB, HIBYTE (chip->Timing.ChannelB_StartPixel)); Mustek_SendData (chip, ES01_C2_ChannelBlueExpEndPixelLSB, LOBYTE (chip->Timing.ChannelB_EndPixel)); Mustek_SendData (chip, ES01_C3_ChannelBlueExpEndPixelMSB, HIBYTE (chip->Timing.ChannelB_EndPixel)); DBG (DBG_ASIC, "SetLEDTime:Exit\n"); return status; } static SANE_Status SetScanMode (PAsic chip, SANE_Byte bScanBits) { SANE_Status status = SANE_STATUS_GOOD; SANE_Byte temp_f5_register = 0; SANE_Byte GrayBWChannel; DBG (DBG_ASIC, "SetScanMode():Enter; set f5 register\n"); if (bScanBits >= 24) { temp_f5_register |= COLOR_ES02; } else { temp_f5_register |= GRAY_ES02; } if ((bScanBits == 8) || (bScanBits == 24)) { temp_f5_register |= _8_BITS_ES02; } else if (bScanBits == 1) { temp_f5_register |= _1_BIT_ES02; } else { temp_f5_register |= _16_BITS_ES02; } if (bScanBits < 24) { GrayBWChannel = 1; } else { GrayBWChannel = 4; } if (GrayBWChannel == 0) { temp_f5_register |= GRAY_RED_ES02; } else if (GrayBWChannel == 1) { temp_f5_register |= GRAY_GREEN_ES02; } else if (GrayBWChannel == 2) { temp_f5_register |= GRAY_BLUE_ES02; } else { temp_f5_register |= GRAY_GREEN_BLUE_ES02; } status = Mustek_SendData (chip, ES01_F5_ScanDataFormat, temp_f5_register); DBG (DBG_ASIC, "F5_ScanDataFormat=0x%x\n", temp_f5_register); DBG (DBG_ASIC, "SetScanMode():Exit\n"); return status; } static SANE_Status SetPackAddress (PAsic chip, unsigned short wXResolution, unsigned short wWidth, unsigned short wX, double XRatioAdderDouble, double XRatioTypeDouble, SANE_Byte byClear_Pulse_Width, unsigned short * PValidPixelNumber) { SANE_Status status = SANE_STATUS_GOOD; SANE_Byte OverLapPixel; SANE_Byte TotalLineShift; unsigned short SegmentTotalPixel; unsigned int dwLineTotalPixel; unsigned short ValidPixelNumber = *PValidPixelNumber; unsigned int FinalLinePixelPerSegment; SANE_Byte InValidPixelNumber; unsigned int CISPackAreaStartAddress; SANE_Byte PackAreaUseLine; unsigned int MaxPixelHW; int i; DBG (DBG_ASIC, "SetPackAddress:Enter\n"); OverLapPixel = 0; TotalLineShift = 1; PackAreaUseLine = TotalLineShift + 1; if (wXResolution > (SENSOR_DPI / 2)) { ValidPixelNumber = ValidPixelNumberFor1200DPI; OverLapPixel = OverLapPixelNumber1200; } else { ValidPixelNumber = ValidPixelNumberFor600DPI; OverLapPixel = OverLapPixelNumber600; } ValidPixelNumber = (unsigned short) ((wWidth + 10 + 15) * XRatioAdderDouble); ValidPixelNumber >>= 4; ValidPixelNumber <<= 4; ValidPixelNumber += (OverLapPixel * 2); for (i = 0; i < 16; i++) { Mustek_SendData (chip, ES01_2B0_SEGMENT0_OVERLAP_SEGMENT1 + i, OverLapPixel); Mustek_SendData (chip, ES01_2C0_VALID_PIXEL_PARAMETER_OF_SEGMENT1 + i, 0); } FinalLinePixelPerSegment = ValidPixelNumber + OverLapPixel * 2; if ((FinalLinePixelPerSegment % 8) > 0) { InValidPixelNumber = (SANE_Byte) (8 - (FinalLinePixelPerSegment % 8)); } else { InValidPixelNumber = 0; } Mustek_SendData (chip, ES01_1B0_SEGMENT_PIXEL_NUMBER_LB, LOBYTE (ValidPixelNumber)); Mustek_SendData (chip, ES01_1B1_SEGMENT_PIXEL_NUMBER_HB, HIBYTE (ValidPixelNumber)); SegmentTotalPixel = ValidPixelNumber + OverLapPixel * 2 + InValidPixelNumber; Mustek_SendData (chip, ES01_169_NUMBER_OF_SEGMENT_PIXEL_LB, LOBYTE (ValidPixelNumber)); Mustek_SendData (chip, ES01_16A_NUMBER_OF_SEGMENT_PIXEL_HB, HIBYTE (ValidPixelNumber)); Mustek_SendData (chip, ES01_16B_BETWEEN_SEGMENT_INVALID_PIXEL, 0); Mustek_SendData (chip, ES01_B6_LineWidthPixelLSB, LOBYTE (ValidPixelNumber)); Mustek_SendData (chip, ES01_B7_LineWidthPixelMSB, HIBYTE (ValidPixelNumber)); Mustek_SendData (chip, ES01_19A_CHANNEL_LINE_GAP_LB, LOBYTE (ValidPixelNumber)); Mustek_SendData (chip, ES01_19B_CHANNEL_LINE_GAP_HB, HIBYTE (ValidPixelNumber)); DBG (DBG_ASIC, "ValidPixelNumber=%d\n", ValidPixelNumber); for (i = 0; i < 36; i++) { Mustek_SendData (chip, 0x270 + i, 0); } Mustek_SendData (chip, 0x270, (SANE_Byte) ((SegmentTotalPixel * (PackAreaUseLine) * 1))); Mustek_SendData (chip, 0x271, (SANE_Byte) ((SegmentTotalPixel * (PackAreaUseLine) * 1) >> 8)); Mustek_SendData (chip, 0x272, (SANE_Byte) ((SegmentTotalPixel * (PackAreaUseLine) * 1) >> 16)); Mustek_SendData (chip, 0x27C, (SANE_Byte) ((SegmentTotalPixel * (PackAreaUseLine) * 2))); Mustek_SendData (chip, 0x27D, (SANE_Byte) ((SegmentTotalPixel * (PackAreaUseLine) * 2) >> 8)); Mustek_SendData (chip, 0x27E, (SANE_Byte) ((SegmentTotalPixel * (PackAreaUseLine) * 2) >> 16)); Mustek_SendData (chip, 0x288, (SANE_Byte) ((SegmentTotalPixel * (PackAreaUseLine) * 3))); Mustek_SendData (chip, 0x289, (SANE_Byte) ((SegmentTotalPixel * (PackAreaUseLine) * 3) >> 8)); Mustek_SendData (chip, 0x28A, (SANE_Byte) ((SegmentTotalPixel * (PackAreaUseLine) * 3) >> 16)); DBG (DBG_ASIC, "channel gap=%d\n", SegmentTotalPixel * (PackAreaUseLine)); Mustek_SendData (chip, ES01_B4_StartPixelLSB, LOBYTE (wX + 0)); Mustek_SendData (chip, ES01_B5_StartPixelMSB, HIBYTE (wX + 0)); dwLineTotalPixel = ValidPixelNumber; Mustek_SendData (chip, ES01_1B9_LINE_PIXEL_NUMBER_LB, LOBYTE (XRatioTypeDouble * (dwLineTotalPixel - 1))); Mustek_SendData (chip, ES01_1BA_LINE_PIXEL_NUMBER_HB, HIBYTE (XRatioTypeDouble * (dwLineTotalPixel - 1))); /* final start read out pixel */ Mustek_SendData (chip, ES01_1F4_START_READ_OUT_PIXEL_LB, LOBYTE (0)); Mustek_SendData (chip, ES01_1F5_START_READ_OUT_PIXEL_HB, HIBYTE (0)); MaxPixelHW = (dwLineTotalPixel + InValidPixelNumber) - 10; if (wWidth > MaxPixelHW) { DBG (DBG_ERR, "read out pixel over max pixel! image will shift!!!\n"); } /* final read pixel width */ Mustek_SendData (chip, ES01_1F6_READ_OUT_PIXEL_LENGTH_LB, LOBYTE (wWidth + 9)); Mustek_SendData (chip, ES01_1F7_READ_OUT_PIXEL_LENGTH_HB, HIBYTE (wWidth + 9)); /* data output sequence */ Mustek_SendData (chip, ES01_1F8_PACK_CHANNEL_SELECT_B0, 0); Mustek_SendData (chip, ES01_1F9_PACK_CHANNEL_SELECT_B1, 0); Mustek_SendData (chip, ES01_1FA_PACK_CHANNEL_SELECT_B2, 0x18); Mustek_SendData (chip, ES01_1FB_PACK_CHANNEL_SIZE_B0, (SANE_Byte) ((SegmentTotalPixel * PackAreaUseLine))); Mustek_SendData (chip, ES01_1FC_PACK_CHANNEL_SIZE_B1, (SANE_Byte) ((SegmentTotalPixel * PackAreaUseLine) >> 8)); Mustek_SendData (chip, ES01_1FD_PACK_CHANNEL_SIZE_B2, (SANE_Byte) ((SegmentTotalPixel * PackAreaUseLine) >> 16)); Mustek_SendData (chip, ES01_16C_LINE_SHIFT_OUT_TIMES_DIRECTION, 0x01); Mustek_SendData (chip, ES01_1CE_LINE_SEGMENT_NUMBER, 0x00); Mustek_SendData (chip, ES01_D8_PHTG_EDGE_TIMING_ADJUST, 0x17); Mustek_SendData (chip, ES01_D9_CLEAR_PULSE_WIDTH, byClear_Pulse_Width); Mustek_SendData (chip, ES01_DA_CLEAR_SIGNAL_INVERTING_OUTPUT, 0x54 | 0x01); Mustek_SendData (chip, ES01_CD_TG_R_CONTROL, 0x3C); Mustek_SendData (chip, ES01_CE_TG_G_CONTROL, 0); Mustek_SendData (chip, ES01_CF_TG_B_CONTROL, 0x3C); /* set pack area address */ CISPackAreaStartAddress = PackAreaStartAddress; DBG (DBG_ASIC, "CISPackAreaStartAddress=%d\n", CISPackAreaStartAddress); /* cycle 1 */ Mustek_SendData (chip, ES01_16D_EXPOSURE_CYCLE1_SEGMENT1_START_ADDR_BYTE0, (SANE_Byte) ((CISPackAreaStartAddress + 0))); Mustek_SendData (chip, ES01_16E_EXPOSURE_CYCLE1_SEGMENT1_START_ADDR_BYTE1, (SANE_Byte) ((CISPackAreaStartAddress + 0) >> 8)); Mustek_SendData (chip, ES01_16F_EXPOSURE_CYCLE1_SEGMENT1_START_ADDR_BYTE2, (SANE_Byte) ((CISPackAreaStartAddress + 0) >> 16)); Mustek_SendData (chip, ES01_170_EXPOSURE_CYCLE1_SEGMENT2_START_ADDR_BYTE0, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000))); Mustek_SendData (chip, ES01_171_EXPOSURE_CYCLE1_SEGMENT2_START_ADDR_BYTE1, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 8)); Mustek_SendData (chip, ES01_172_EXPOSURE_CYCLE1_SEGMENT2_START_ADDR_BYTE2, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 16)); Mustek_SendData (chip, ES01_173_EXPOSURE_CYCLE1_SEGMENT3_START_ADDR_BYTE0, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000))); Mustek_SendData (chip, ES01_174_EXPOSURE_CYCLE1_SEGMENT3_START_ADDR_BYTE1, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 8)); Mustek_SendData (chip, ES01_175_EXPOSURE_CYCLE1_SEGMENT3_START_ADDR_BYTE2, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 16)); Mustek_SendData (chip, ES01_176_EXPOSURE_CYCLE1_SEGMENT4_START_ADDR_BYTE0, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000))); Mustek_SendData (chip, ES01_177_EXPOSURE_CYCLE1_SEGMENT4_START_ADDR_BYTE1, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 8)); Mustek_SendData (chip, ES01_178_EXPOSURE_CYCLE1_SEGMENT4_START_ADDR_BYTE2, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 16)); /* cycle 2 */ Mustek_SendData (chip, ES01_179_EXPOSURE_CYCLE2_SEGMENT1_START_ADDR_BYTE0, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000))); Mustek_SendData (chip, ES01_17A_EXPOSURE_CYCLE2_SEGMENT1_START_ADDR_BYTE1, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 8)); Mustek_SendData (chip, ES01_17B_EXPOSURE_CYCLE2_SEGMENT1_START_ADDR_BYTE2, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 16)); Mustek_SendData (chip, ES01_17C_EXPOSURE_CYCLE2_SEGMENT2_START_ADDR_BYTE0, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000))); Mustek_SendData (chip, ES01_17D_EXPOSURE_CYCLE2_SEGMENT2_START_ADDR_BYTE1, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 8)); Mustek_SendData (chip, ES01_17E_EXPOSURE_CYCLE2_SEGMENT2_START_ADDR_BYTE2, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 16)); Mustek_SendData (chip, ES01_17F_EXPOSURE_CYCLE2_SEGMENT3_START_ADDR_BYTE0, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000))); Mustek_SendData (chip, ES01_180_EXPOSURE_CYCLE2_SEGMENT3_START_ADDR_BYTE1, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 8)); Mustek_SendData (chip, ES01_181_EXPOSURE_CYCLE2_SEGMENT3_START_ADDR_BYTE2, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 16)); Mustek_SendData (chip, ES01_182_EXPOSURE_CYCLE2_SEGMENT4_START_ADDR_BYTE0, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000))); Mustek_SendData (chip, ES01_183_EXPOSURE_CYCLE2_SEGMENT4_START_ADDR_BYTE1, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 8)); Mustek_SendData (chip, ES01_184_EXPOSURE_CYCLE2_SEGMENT4_START_ADDR_BYTE2, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 16)); /* cycle 3 */ Mustek_SendData (chip, ES01_185_EXPOSURE_CYCLE3_SEGMENT1_START_ADDR_BYTE0, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000))); Mustek_SendData (chip, ES01_186_EXPOSURE_CYCLE3_SEGMENT1_START_ADDR_BYTE1, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 8)); Mustek_SendData (chip, ES01_187_EXPOSURE_CYCLE3_SEGMENT1_START_ADDR_BYTE2, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 16)); Mustek_SendData (chip, ES01_188_EXPOSURE_CYCLE3_SEGMENT2_START_ADDR_BYTE0, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000))); Mustek_SendData (chip, ES01_189_EXPOSURE_CYCLE3_SEGMENT2_START_ADDR_BYTE1, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 8)); Mustek_SendData (chip, ES01_18A_EXPOSURE_CYCLE3_SEGMENT2_START_ADDR_BYTE2, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 16)); Mustek_SendData (chip, ES01_18B_EXPOSURE_CYCLE3_SEGMENT3_START_ADDR_BYTE0, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000))); Mustek_SendData (chip, ES01_18C_EXPOSURE_CYCLE3_SEGMENT3_START_ADDR_BYTE1, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 8)); Mustek_SendData (chip, ES01_18D_EXPOSURE_CYCLE3_SEGMENT3_START_ADDR_BYTE2, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 16)); Mustek_SendData (chip, ES01_18E_EXPOSURE_CYCLE3_SEGMENT4_START_ADDR_BYTE0, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000))); Mustek_SendData (chip, ES01_18F_EXPOSURE_CYCLE3_SEGMENT4_START_ADDR_BYTE1, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 8)); Mustek_SendData (chip, ES01_190_EXPOSURE_CYCLE3_SEGMENT4_START_ADDR_BYTE2, (SANE_Byte) ((CISPackAreaStartAddress + 0xC0000) >> 16)); DBG (DBG_ASIC, "set CISPackAreaStartAddress ok\n"); Mustek_SendData (chip, 0x260, InValidPixelNumber); Mustek_SendData (chip, 0x261, InValidPixelNumber << 4); Mustek_SendData (chip, 0x262, InValidPixelNumber); Mustek_SendData (chip, 0x263, 0); DBG (DBG_ASIC, "InValidPixelNumber=%d\n", InValidPixelNumber); Mustek_SendData (chip, 0x264, 0); Mustek_SendData (chip, 0x265, 0); Mustek_SendData (chip, 0x266, 0); Mustek_SendData (chip, 0x267, 0); Mustek_SendData (chip, 0x268, 0); Mustek_SendData (chip, 0x269, 0); Mustek_SendData (chip, 0x26A, 0); Mustek_SendData (chip, 0x26B, 0); Mustek_SendData (chip, 0x26C, 0); Mustek_SendData (chip, 0x26D, 0); Mustek_SendData (chip, 0x26E, 0); Mustek_SendData (chip, 0x26F, 0); DBG (DBG_ASIC, "Set Invalid Pixel ok\n"); /* Pack Start Address */ Mustek_SendData (chip, ES01_19E_PACK_AREA_R_START_ADDR_BYTE0, (SANE_Byte) ((CISPackAreaStartAddress + (SegmentTotalPixel * (PackAreaUseLine * 0))))); Mustek_SendData (chip, ES01_19F_PACK_AREA_R_START_ADDR_BYTE1, (SANE_Byte) ((CISPackAreaStartAddress + (SegmentTotalPixel * (PackAreaUseLine * 0))) >> 8)); Mustek_SendData (chip, ES01_1A0_PACK_AREA_R_START_ADDR_BYTE2, (SANE_Byte) ((CISPackAreaStartAddress + (SegmentTotalPixel * (PackAreaUseLine * 0))) >> 16)); Mustek_SendData (chip, ES01_1A1_PACK_AREA_G_START_ADDR_BYTE0, (SANE_Byte) ((CISPackAreaStartAddress + (SegmentTotalPixel * (PackAreaUseLine * 1))))); Mustek_SendData (chip, ES01_1A2_PACK_AREA_G_START_ADDR_BYTE1, (SANE_Byte) ((CISPackAreaStartAddress + (SegmentTotalPixel * (PackAreaUseLine * 1))) >> 8)); Mustek_SendData (chip, ES01_1A3_PACK_AREA_G_START_ADDR_BYTE2, (SANE_Byte) ((CISPackAreaStartAddress + (SegmentTotalPixel * (PackAreaUseLine * 1))) >> 16)); Mustek_SendData (chip, ES01_1A4_PACK_AREA_B_START_ADDR_BYTE0, (SANE_Byte) ((CISPackAreaStartAddress + (SegmentTotalPixel * (PackAreaUseLine * 2))))); Mustek_SendData (chip, ES01_1A5_PACK_AREA_B_START_ADDR_BYTE1, (SANE_Byte) ((CISPackAreaStartAddress + (SegmentTotalPixel * (PackAreaUseLine * 2))) >> 8)); Mustek_SendData (chip, ES01_1A6_PACK_AREA_B_START_ADDR_BYTE2, (SANE_Byte) ((CISPackAreaStartAddress + (SegmentTotalPixel * (PackAreaUseLine * 2))) >> 16)); /* Pack End Address */ Mustek_SendData (chip, ES01_1A7_PACK_AREA_R_END_ADDR_BYTE0, (SANE_Byte) ((CISPackAreaStartAddress + (SegmentTotalPixel * (PackAreaUseLine * 1) - 1)))); Mustek_SendData (chip, ES01_1A8_PACK_AREA_R_END_ADDR_BYTE1, (SANE_Byte) ((CISPackAreaStartAddress + (SegmentTotalPixel * (PackAreaUseLine * 1) - 1)) >> 8)); Mustek_SendData (chip, ES01_1A9_PACK_AREA_R_END_ADDR_BYTE2, (SANE_Byte) ((CISPackAreaStartAddress + (SegmentTotalPixel * (PackAreaUseLine * 1) - 1)) >> 16)); Mustek_SendData (chip, ES01_1AA_PACK_AREA_G_END_ADDR_BYTE0, (SANE_Byte) ((CISPackAreaStartAddress + (SegmentTotalPixel * (PackAreaUseLine * 2) - 1)))); Mustek_SendData (chip, ES01_1AB_PACK_AREA_G_END_ADDR_BYTE1, (SANE_Byte) ((CISPackAreaStartAddress + (SegmentTotalPixel * (PackAreaUseLine * 2) - 1)) >> 8)); Mustek_SendData (chip, ES01_1AC_PACK_AREA_G_END_ADDR_BYTE2, (SANE_Byte) ((CISPackAreaStartAddress + (SegmentTotalPixel * (PackAreaUseLine * 2) - 1)) >> 16)); Mustek_SendData (chip, ES01_1AD_PACK_AREA_B_END_ADDR_BYTE0, (SANE_Byte) ((CISPackAreaStartAddress + (SegmentTotalPixel * (PackAreaUseLine * 3) - 1)))); Mustek_SendData (chip, ES01_1AE_PACK_AREA_B_END_ADDR_BYTE1, (SANE_Byte) ((CISPackAreaStartAddress + (SegmentTotalPixel * (PackAreaUseLine * 3) - 1)) >> 8)); Mustek_SendData (chip, ES01_1AF_PACK_AREA_B_END_ADDR_BYTE2, (SANE_Byte) ((CISPackAreaStartAddress + (SegmentTotalPixel * (PackAreaUseLine * 3) - 1)) >> 16)); DBG (DBG_ASIC, "CISPackAreaStartAddress + (SegmentTotalPixel*(PackAreaUseLine*1))=%d\n", (CISPackAreaStartAddress + (SegmentTotalPixel * (PackAreaUseLine * 1)))); Mustek_SendData (chip, ES01_19C_MAX_PACK_LINE, PackAreaUseLine); status = Mustek_SendData (chip, ES01_19D_PACK_THRESHOLD_LINE, TotalLineShift); DBG (DBG_ASIC, "PackAreaUseLine=%d,TotalLineShift=%d\n", PackAreaUseLine, TotalLineShift); *PValidPixelNumber = ValidPixelNumber; DBG (DBG_ASIC, "SetPackAddress:Enter\n"); return status; } static SANE_Status SetExtraSetting (PAsic chip, unsigned short wXResolution, unsigned short wCCD_PixelNumber, SANE_Bool isCaribrate) { SANE_Status status = SANE_STATUS_GOOD; SANE_Byte byPHTG_PulseWidth, byPHTG_WaitWidth; SANE_Byte temp_ff_register = 0; SANE_Byte bThreshold = 128; DBG (DBG_ASIC, "SetExtraSetting:Enter\n"); Mustek_SendData (chip, ES01_B8_ChannelRedExpStartPixelLSB, LOBYTE (chip->Timing.ChannelR_StartPixel)); Mustek_SendData (chip, ES01_B9_ChannelRedExpStartPixelMSB, HIBYTE (chip->Timing.ChannelR_StartPixel)); Mustek_SendData (chip, ES01_BA_ChannelRedExpEndPixelLSB, LOBYTE (chip->Timing.ChannelR_EndPixel)); Mustek_SendData (chip, ES01_BB_ChannelRedExpEndPixelMSB, HIBYTE (chip->Timing.ChannelR_EndPixel)); Mustek_SendData (chip, ES01_BC_ChannelGreenExpStartPixelLSB, LOBYTE (chip->Timing.ChannelG_StartPixel)); Mustek_SendData (chip, ES01_BD_ChannelGreenExpStartPixelMSB, HIBYTE (chip->Timing.ChannelG_StartPixel)); Mustek_SendData (chip, ES01_BE_ChannelGreenExpEndPixelLSB, LOBYTE (chip->Timing.ChannelG_EndPixel)); Mustek_SendData (chip, ES01_BF_ChannelGreenExpEndPixelMSB, HIBYTE (chip->Timing.ChannelG_EndPixel)); Mustek_SendData (chip, ES01_C0_ChannelBlueExpStartPixelLSB, LOBYTE (chip->Timing.ChannelB_StartPixel)); Mustek_SendData (chip, ES01_C1_ChannelBlueExpStartPixelMSB, HIBYTE (chip->Timing.ChannelB_StartPixel)); Mustek_SendData (chip, ES01_C2_ChannelBlueExpEndPixelLSB, LOBYTE (chip->Timing.ChannelB_EndPixel)); Mustek_SendData (chip, ES01_C3_ChannelBlueExpEndPixelMSB, HIBYTE (chip->Timing.ChannelB_EndPixel)); byPHTG_PulseWidth = chip->Timing.PHTG_PluseWidth; byPHTG_WaitWidth = chip->Timing.PHTG_WaitWidth; Mustek_SendData (chip, ES01_B2_PHTGPulseWidth, byPHTG_PulseWidth); Mustek_SendData (chip, ES01_B3_PHTGWaitWidth, byPHTG_WaitWidth); Mustek_SendData (chip, ES01_CC_PHTGTimingAdjust, chip->Timing.PHTG_TimingAdj); Mustek_SendData (chip, ES01_D0_PH1_0, chip->Timing.PHTG_TimingSetup); DBG (DBG_ASIC, "ChannelR_StartPixel=%d,ChannelR_EndPixel=%d\n", chip->Timing.ChannelR_StartPixel, chip->Timing.ChannelR_EndPixel); if (wXResolution == 1200) { Mustek_SendData (chip, ES01_DE_CCD_SETUP_REGISTER, chip->Timing.DE_CCD_SETUP_REGISTER_1200); } else { Mustek_SendData (chip, ES01_DE_CCD_SETUP_REGISTER, chip->Timing.DE_CCD_SETUP_REGISTER_600); } if (isCaribrate == TRUE) { temp_ff_register |= BYPASS_DARK_SHADING_ENABLE; temp_ff_register |= BYPASS_WHITE_SHADING_ENABLE; } else /*Setwindow */ { temp_ff_register |= BYPASS_DARK_SHADING_DISABLE; temp_ff_register |= BYPASS_WHITE_SHADING_DISABLE; } temp_ff_register |= BYPASS_PRE_GAMMA_ENABLE; temp_ff_register |= BYPASS_CONVOLUTION_ENABLE; temp_ff_register |= BYPASS_MATRIX_ENABLE; temp_ff_register |= BYPASS_GAMMA_ENABLE; if (isCaribrate == TRUE) { Mustek_SendData (chip, ES01_FF_SCAN_IMAGE_OPTION, 0xfc | (0x00 & 0x03)); DBG (DBG_ASIC, "FF_SCAN_IMAGE_OPTION=0x%x\n", 0xfc | (0x00 & 0x03)); } else /*Setwindow */ { Mustek_SendData (chip, ES01_FF_SCAN_IMAGE_OPTION, temp_ff_register | (0x00 & 0x03)); DBG (DBG_ASIC, "FF_SCAN_IMAGE_OPTION=0x%x\n", temp_ff_register | (0x00 & 0x03)); } /* pixel process time */ Mustek_SendData (chip, ES01_B0_CCDPixelLSB, LOBYTE (wCCD_PixelNumber)); Mustek_SendData (chip, ES01_B1_CCDPixelMSB, HIBYTE (wCCD_PixelNumber)); Mustek_SendData (chip, ES01_DF_ICG_CONTROL, 0x17); DBG (DBG_ASIC, "wCCD_PixelNumber=%d\n", wCCD_PixelNumber); Mustek_SendData (chip, ES01_88_LINE_ART_THRESHOLD_HIGH_VALUE, bThreshold); Mustek_SendData (chip, ES01_89_LINE_ART_THRESHOLD_LOW_VALUE, bThreshold - 1); DBG (DBG_ASIC, "bThreshold=%d\n", bThreshold); usleep (50000); DBG (DBG_ASIC, "SetExtraSetting:Exit\n"); return status; } /* ---------------------- high level asic functions ------------------------ */ /* HOLD: We don't want to have global vid/pids */ static unsigned short ProductID = 0x0409; static unsigned short VendorID = 0x055f; static SANE_String_Const device_name; static SANE_Status attach_one_scanner (SANE_String_Const devname) { DBG (DBG_ASIC, "attach_one_scanner: enter\n"); DBG (DBG_INFO, "attach_one_scanner: devname = %s\n", devname); device_name = devname; return SANE_STATUS_GOOD; } static SANE_Status Asic_Open (PAsic chip, SANE_Byte *pDeviceName) { SANE_Status status; SANE_Status sane_status; DBG (DBG_ASIC, "Asic_Open: Enter\n"); device_name = NULL; if (chip->firmwarestate > FS_OPENED) { DBG (DBG_ASIC, "chip has been opened. fd=%d\n", chip->fd); return SANE_STATUS_INVAL; } /* init usb */ sanei_usb_init (); /* find scanner */ sane_status = sanei_usb_find_devices (VendorID, ProductID, attach_one_scanner); if (sane_status != SANE_STATUS_GOOD) { DBG (DBG_ERR, "Asic_Open: sanei_usb_find_devices failed: %s\n", sane_strstatus (sane_status)); return SANE_STATUS_INVAL; } /* open usb */ if (device_name == NULL) { DBG (DBG_ERR, "Asic_Open: no scanner found\n"); return SANE_STATUS_INVAL; } sane_status = sanei_usb_open (device_name, &chip->fd); if (sane_status != SANE_STATUS_GOOD) { DBG (DBG_ERR, "Asic_Open: sanei_usb_open of %s failed: %s\n", device_name, sane_strstatus (sane_status)); return SANE_STATUS_INVAL; } /* open scanner chip */ status = OpenScanChip (chip); if (status != SANE_STATUS_GOOD) { sanei_usb_close (chip->fd); DBG (DBG_ASIC, "Asic_Open: OpenScanChip error\n"); return status; } Mustek_SendData (chip, ES01_94_PowerSaveControl, 0x27); Mustek_SendData (chip, ES01_86_DisableAllClockWhenIdle, CLOSE_ALL_CLOCK_DISABLE); Mustek_SendData (chip, ES01_79_AFEMCLK_SDRAMCLK_DELAY_CONTROL, SDRAMCLK_DELAY_12_ns); /* SDRAM initial sequence */ Mustek_SendData (chip, ES01_87_SDRAM_Timing, 0xf1); Mustek_SendData (chip, ES01_87_SDRAM_Timing, 0xa5); Mustek_SendData (chip, ES01_87_SDRAM_Timing, 0x91); Mustek_SendData (chip, ES01_87_SDRAM_Timing, 0x81); Mustek_SendData (chip, ES01_87_SDRAM_Timing, 0xf0); chip->firmwarestate = FS_OPENED; Asic_WaitUnitReady (chip); DBG (DBG_ASIC, "Asic_WaitUnitReady\n"); status = SafeInitialChip (chip); if (status != SANE_STATUS_GOOD) { DBG (DBG_ERR, "Asic_Open: SafeInitialChip error\n"); return status; } pDeviceName = (SANE_Byte *) strdup (device_name); if (!pDeviceName) { DBG (DBG_ERR, "Asic_Open: not enough memory\n"); return SANE_STATUS_INVAL; } DBG (DBG_INFO, "Asic_Open: device %s successfully opened\n", pDeviceName); DBG (DBG_ASIC, "Asic_Open: Exit\n"); return status; } static SANE_Status Asic_Close (PAsic chip) { SANE_Status status; DBG (DBG_ASIC, "Asic_Close: Enter\n"); if (chip->firmwarestate < FS_OPENED) { DBG (DBG_ASIC, "Asic_Close: Scanner is not opened\n"); return SANE_STATUS_GOOD; } if (chip->firmwarestate > FS_OPENED) { DBG (DBG_ASIC, "Asic_Close: Scanner is scanning, try to stop scanning\n"); Asic_ScanStop (chip); } Mustek_SendData (chip, ES01_86_DisableAllClockWhenIdle, CLOSE_ALL_CLOCK_ENABLE); status = CloseScanChip (chip); if (status != SANE_STATUS_GOOD) { DBG (DBG_ERR, "Asic_Close: CloseScanChip error\n"); return status; } sanei_usb_close (chip->fd); chip->firmwarestate = FS_ATTACHED; DBG (DBG_ASIC, "Asic_Close: Exit\n"); return status; } static SANE_Status Asic_TurnLamp (PAsic chip, SANE_Bool isLampOn) { SANE_Status status = SANE_STATUS_GOOD; SANE_Byte PWM; DBG (DBG_ASIC, "Asic_TurnLamp: Enter\n"); if (chip->firmwarestate < FS_OPENED) { DBG (DBG_ERR, "Asic_TurnLamp: Scanner is not opened\n"); return SANE_STATUS_INVAL; } if (chip->firmwarestate > FS_OPENED) { Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_DISABLE); } if (isLampOn) { PWM = LAMP0_PWM_DEFAULT; } else { PWM = 0; } Mustek_SendData (chip, ES01_99_LAMP_PWM_FREQ_CONTROL, 1); Mustek_SendData (chip, ES01_90_Lamp0PWM, PWM); DBG (DBG_ASIC, "Lamp0 PWM = %d\n", PWM); chip->firmwarestate = FS_OPENED; DBG (DBG_ASIC, "Asic_TurnLamp: Exit\n"); return status; } static SANE_Status Asic_TurnTA (PAsic chip, SANE_Bool isTAOn) { SANE_Byte PWM; DBG (DBG_ASIC, "Asic_TurnTA: Enter\n"); if (chip->firmwarestate < FS_OPENED) { DBG (DBG_ERR, "Asic_TurnTA: Scanner is not opened\n"); return SANE_STATUS_INVAL; } if (chip->firmwarestate > FS_OPENED) Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_DISABLE); if (isTAOn) { PWM = LAMP1_PWM_DEFAULT; } else { PWM = 0; } Mustek_SendData (chip, ES01_99_LAMP_PWM_FREQ_CONTROL, 1); Mustek_SendData (chip, ES01_91_Lamp1PWM, PWM); DBG (DBG_ASIC, "Lamp1 PWM = %d\n", PWM); chip->firmwarestate = FS_OPENED; DBG (DBG_ASIC, "Asic_TurnTA: Exit\n"); return SANE_STATUS_GOOD; } static SANE_Status Asic_WaitUnitReady (PAsic chip) { SANE_Status status = SANE_STATUS_GOOD; SANE_Byte temp_status; int i = 0; DBG (DBG_ASIC, "Asic_WaitUnitReady:Enter\n"); if (chip->firmwarestate < FS_OPENED) { DBG (DBG_ERR, "Asic_WaitUnitReady: Scanner has not been opened\n"); return SANE_STATUS_INVAL; } do { status = GetChipStatus (chip, 1, &temp_status); if (status != SANE_STATUS_GOOD) { DBG (DBG_ASIC, "WaitChipIdle:Error!\n"); return status; } i++; usleep (100000); } while (((temp_status & 0x1f) != 0) && i < 300); DBG (DBG_ASIC, "Wait %d s\n", (unsigned short) (i * 0.1)); Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_DISABLE); chip->motorstate = MS_STILL; DBG (DBG_ASIC, "Asic_WaitUnitReady: Exit\n"); return status; } #if SANE_UNUSED static SANE_Status Asic_Release (PAsic chip) { SANE_Status status = SANE_STATUS_GOOD; DBG (DBG_ASIC, "Asic_Release()\n"); if (chip->firmwarestate > FS_ATTACHED) status = Asic_Close (chip); chip->firmwarestate = FS_NULL; DBG (DBG_ASIC, "Asic_Release: Exit\n"); return status; } #endif static SANE_Status Asic_Initialize (PAsic chip) { SANE_Status status = SANE_STATUS_GOOD; DBG (DBG_ASIC, "Asic_Initialize:Enter\n"); chip->motorstate = MS_STILL; chip->dwBytesCountPerRow = 0; chip->lpGammaTable = NULL; DBG (DBG_ASIC, "isFirstOpenChip=%d\n", chip->isFirstOpenChip); chip->isFirstOpenChip = TRUE; DBG (DBG_ASIC, "isFirstOpenChip=%d\n", chip->isFirstOpenChip); chip->SWWidth = 0; chip->TA_Status = TA_UNKNOW; chip->lpShadingTable = NULL; chip->isMotorMove = MOTOR_0_ENABLE; Asic_Reset (chip); InitTiming (chip); chip->isUniformSpeedToScan = UNIFORM_MOTOR_AND_SCAN_SPEED_DISABLE; chip->isMotorGoToFirstLine = MOTOR_MOVE_TO_FIRST_LINE_ENABLE; chip->UsbHost = HT_USB10; DBG (DBG_ASIC, "Asic_Initialize: Exit\n"); return status; } static SANE_Status Asic_SetWindow (PAsic chip, SANE_Byte bScanBits, unsigned short wXResolution, unsigned short wYResolution, unsigned short wX, unsigned short wY, unsigned short wWidth, unsigned short wLength) { SANE_Status status = SANE_STATUS_GOOD; unsigned short ValidPixelNumber; unsigned short BytePerPixel = 0; unsigned int dwTotalLineTheBufferNeed = 0; unsigned short dwTotal_CCDResolution = 1200; unsigned short wThinkCCDResolution = 0; unsigned short wCCD_PixelNumber = 0; unsigned int dwLineWidthPixel = 0; unsigned short wNowMotorDPI; unsigned short XRatioTypeWord; double XRatioTypeDouble; double XRatioAdderDouble; LLF_MOTORMOVE *lpMotorStepsTable = (LLF_MOTORMOVE *) malloc (sizeof (LLF_MOTORMOVE)); SANE_Byte byDummyCycleNum = 0; unsigned short Total_MotorDPI; unsigned short wMultiMotorStep = 1; SANE_Byte bMotorMoveType = _MOTOR_MOVE_TYPE; SANE_Byte byClear_Pulse_Width = 0; unsigned int dwLinePixelReport; SANE_Byte byPHTG_PulseWidth, byPHTG_WaitWidth; unsigned short StartSpeed, EndSpeed; LLF_CALCULATEMOTORTABLE CalMotorTable; LLF_MOTOR_CURRENT_AND_PHASE CurrentPhase; LLF_RAMACCESS RamAccess; unsigned int dwStartAddr, dwEndAddr, dwTableBaseAddr, dwShadingTableAddr; SANE_Byte isMotorMoveToFirstLine = chip->isMotorGoToFirstLine; SANE_Byte isUniformSpeedToScan = chip->isUniformSpeedToScan; SANE_Byte isScanBackTracking = SCAN_BACK_TRACKING_ENABLE; unsigned short * lpMotorTable; unsigned int RealTableSize; double dbXRatioAdderDouble; unsigned short wFullBank; DBG (DBG_ASIC, "Asic_SetWindow: Enter\n"); DBG (DBG_ASIC, "bScanBits=%d,wXResolution=%d,wYResolution=%d,wX=%d,wY=%d,wWidth=%d,wLength=%d\n", bScanBits, wXResolution, wYResolution, wX, wY, wWidth, wLength); if (chip->firmwarestate != FS_OPENED) { DBG (DBG_ERR, "Asic_SetWindow: Scanner is not opened\n"); return SANE_STATUS_INVAL; } Mustek_SendData (chip, ES01_F3_ActionOption, 0); Mustek_SendData (chip, ES01_86_DisableAllClockWhenIdle, CLOSE_ALL_CLOCK_DISABLE); Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_DISABLE); status = Asic_WaitUnitReady (chip); /* dummy clock mode */ Mustek_SendData (chip, 0x1CD, 0); /* LED Flash */ Mustek_SendData (chip, ES01_94_PowerSaveControl, 0x27 | 64 | 128); /* calculate byte per line */ if (bScanBits > 24) { BytePerPixel = 6; chip->dwBytesCountPerRow = (unsigned int) (wWidth) * 6; } else if (bScanBits == 24) { BytePerPixel = 3; chip->dwBytesCountPerRow = (unsigned int) (wWidth) * 3; } else if ((bScanBits > 8) && (bScanBits <= 16)) { BytePerPixel = 2; chip->dwBytesCountPerRow = (unsigned int) (wWidth) * 2; } else if (bScanBits == 8) { BytePerPixel = 1; chip->dwBytesCountPerRow = (unsigned int) (wWidth); } else if (bScanBits < 8) { BytePerPixel = 1; chip->dwBytesCountPerRow = (unsigned int) (wWidth); } DBG (DBG_ASIC, "dwBytesCountPerRow = %d\n", chip->dwBytesCountPerRow); byDummyCycleNum = 0; if (chip->lsLightSource == LS_REFLECTIVE) { if (chip->UsbHost == HT_USB10) { switch (wYResolution) { case 2400: case 1200: if (chip->dwBytesCountPerRow > 22000) byDummyCycleNum = 4; else if (chip->dwBytesCountPerRow > 15000) byDummyCycleNum = 3; else if (chip->dwBytesCountPerRow > 10000) byDummyCycleNum = 2; else if (chip->dwBytesCountPerRow > 5000) byDummyCycleNum = 1; break; case 600: case 300: case 150: case 100: if (chip->dwBytesCountPerRow > 21000) byDummyCycleNum = 7; else if (chip->dwBytesCountPerRow > 18000) byDummyCycleNum = 6; else if (chip->dwBytesCountPerRow > 15000) byDummyCycleNum = 5; else if (chip->dwBytesCountPerRow > 12000) byDummyCycleNum = 4; else if (chip->dwBytesCountPerRow > 9000) byDummyCycleNum = 3; else if (chip->dwBytesCountPerRow > 6000) byDummyCycleNum = 2; else if (chip->dwBytesCountPerRow > 3000) byDummyCycleNum = 1; break; case 75: case 50: byDummyCycleNum = 1; break; default: byDummyCycleNum = 0; break; } } else { switch (wYResolution) { case 2400: case 1200: case 75: case 50: byDummyCycleNum = 1; break; default: byDummyCycleNum = 0; break; } } } dwTotalLineTheBufferNeed = wLength; chip->Scan.Dpi = wXResolution; CCDTiming (chip); dwTotal_CCDResolution = SENSOR_DPI; if (chip->lsLightSource == LS_REFLECTIVE) { if (wXResolution > (dwTotal_CCDResolution / 2)) { /* full ccd resolution 1200dpi */ wThinkCCDResolution = dwTotal_CCDResolution; Mustek_SendData (chip, ES01_98_GPIOControl8_15, 0x01); Mustek_SendData (chip, ES01_96_GPIOValue8_15, 0x01); wCCD_PixelNumber = chip->Timing.wCCDPixelNumber_1200; } else { /*600dpi */ wThinkCCDResolution = dwTotal_CCDResolution / 2; Mustek_SendData (chip, ES01_98_GPIOControl8_15, 0x01); Mustek_SendData (chip, ES01_96_GPIOValue8_15, 0x00); wCCD_PixelNumber = chip->Timing.wCCDPixelNumber_600; } } else { if (wXResolution > (dwTotal_CCDResolution / 2)) { wThinkCCDResolution = dwTotal_CCDResolution; Mustek_SendData (chip, ES01_98_GPIOControl8_15, 0x01); Mustek_SendData (chip, ES01_96_GPIOValue8_15, 0x01); wCCD_PixelNumber = TA_IMAGE_PIXELNUMBER; } else { wThinkCCDResolution = dwTotal_CCDResolution / 2; Mustek_SendData (chip, ES01_98_GPIOControl8_15, 0x01); Mustek_SendData (chip, ES01_96_GPIOValue8_15, 0x00); wCCD_PixelNumber = TA_IMAGE_PIXELNUMBER; } } dwLineWidthPixel = wWidth; SetLineTimeAndExposure (chip); Mustek_SendData (chip, ES01_CB_CCDDummyCycleNumber, byDummyCycleNum); SetLEDTime (chip); /* calculate Y ratio */ Total_MotorDPI = 1200; wNowMotorDPI = Total_MotorDPI; /* SetADConverter */ Mustek_SendData (chip, ES01_74_HARDWARE_SETTING, MOTOR1_SERIAL_INTERFACE_G10_8_ENABLE | LED_OUT_G11_DISABLE | SLAVE_SERIAL_INTERFACE_G15_14_DISABLE | SHUTTLE_CCD_DISABLE); /* set AFE */ Mustek_SendData (chip, ES01_9A_AFEControl, AD9826_AFE | AUTO_CHANGE_AFE_GAIN_OFFSET_DISABLE); SetAFEGainOffset (chip); Mustek_SendData (chip, ES01_F7_DigitalControl, DIGITAL_REDUCE_DISABLE); /* calculate X ratio */ XRatioTypeDouble = wXResolution; XRatioTypeDouble /= wThinkCCDResolution; XRatioAdderDouble = 1 / XRatioTypeDouble; XRatioTypeWord = (unsigned short) (XRatioTypeDouble * 32768); /* 32768 = 2^15 */ XRatioAdderDouble = (double) (XRatioTypeWord) / 32768; XRatioAdderDouble = 1 / XRatioAdderDouble; /* 0x8000 = 1.00000 -> get all pixel */ Mustek_SendData (chip, ES01_9E_HorizontalRatio1to15LSB, LOBYTE (XRatioTypeWord)); Mustek_SendData (chip, ES01_9F_HorizontalRatio1to15MSB, HIBYTE (XRatioTypeWord)); /* SetMotorType */ Mustek_SendData (chip, ES01_A6_MotorOption, MOTOR_0_ENABLE | MOTOR_1_DISABLE | HOME_SENSOR_0_ENABLE | ES03_TABLE_DEFINE); Mustek_SendData (chip, ES01_F6_MorotControl1, SPEED_UNIT_1_PIXEL_TIME | MOTOR_SYNC_UNIT_1_PIXEL_TIME); /* SetScanStepTable */ /*set Motor move type */ if (wYResolution >= 1200) bMotorMoveType = _8_TABLE_SPACE_FOR_1_DIV_2_STEP; switch (bMotorMoveType) { case _4_TABLE_SPACE_FOR_FULL_STEP: wMultiMotorStep = 1; break; case _8_TABLE_SPACE_FOR_1_DIV_2_STEP: wMultiMotorStep = 2; break; case _16_TABLE_SPACE_FOR_1_DIV_4_STEP: wMultiMotorStep = 4; break; case _32_TABLE_SPACE_FOR_1_DIV_8_STEP: wMultiMotorStep = 8; break; } wY *= wMultiMotorStep; SetScanMode (chip, bScanBits); /*set white shading int and dec */ if (chip->lsLightSource == LS_REFLECTIVE) Mustek_SendData (chip, ES01_F8_WHITE_SHADING_DATA_FORMAT, ES01_SHADING_3_INT_13_DEC); else Mustek_SendData (chip, ES01_F8_WHITE_SHADING_DATA_FORMAT, ES01_SHADING_4_INT_12_DEC); SetPackAddress (chip, wXResolution, wWidth, wX, XRatioAdderDouble, XRatioTypeDouble, byClear_Pulse_Width, &ValidPixelNumber); SetExtraSetting (chip, wXResolution, wCCD_PixelNumber, FALSE); /* calculate line time */ byPHTG_PulseWidth = chip->Timing.PHTG_PluseWidth; byPHTG_WaitWidth = chip->Timing.PHTG_WaitWidth; dwLinePixelReport = ((byClear_Pulse_Width + 1) * 2 + (byPHTG_PulseWidth + 1) * (1) + (byPHTG_WaitWidth + 1) * (1) + (wCCD_PixelNumber + 1)) * (byDummyCycleNum + 1); DBG (DBG_ASIC, "Motor Time = %d\n", (dwLinePixelReport * wYResolution / wNowMotorDPI) / wMultiMotorStep); if ((dwLinePixelReport * wYResolution / wNowMotorDPI) / wMultiMotorStep > 64000) { DBG (DBG_ASIC, "Motor Time Over Flow !!!\n"); } EndSpeed = (unsigned short) ((dwLinePixelReport * wYResolution / wNowMotorDPI) / wMultiMotorStep); SetMotorStepTable (chip, lpMotorStepsTable, wY, dwTotalLineTheBufferNeed * wNowMotorDPI / wYResolution * wMultiMotorStep, wYResolution); /*modified by Chester 92/04/08 */ if (EndSpeed >= 20000) { Asic_MotorMove (chip, 1, wY / wMultiMotorStep); isUniformSpeedToScan = UNIFORM_MOTOR_AND_SCAN_SPEED_ENABLE; isScanBackTracking = SCAN_BACK_TRACKING_DISABLE; isMotorMoveToFirstLine = MOTOR_MOVE_TO_FIRST_LINE_DISABLE; } Mustek_SendData (chip, ES01_F3_ActionOption, isMotorMoveToFirstLine | MOTOR_BACK_HOME_AFTER_SCAN_DISABLE | SCAN_ENABLE | isScanBackTracking | INVERT_MOTOR_DIRECTION_DISABLE | isUniformSpeedToScan | ES01_STATIC_SCAN_DISABLE | MOTOR_TEST_LOOP_DISABLE); if (EndSpeed > 8000) { StartSpeed = EndSpeed; } else { if (EndSpeed <= 1000) StartSpeed = EndSpeed + 4500; else StartSpeed = EndSpeed + 3500; } Mustek_SendData (chip, ES01_FD_MotorFixedspeedLSB, LOBYTE (EndSpeed)); Mustek_SendData (chip, ES01_FE_MotorFixedspeedMSB, HIBYTE (EndSpeed)); lpMotorTable = (unsigned short *) malloc (512 * 8 * 2); memset (lpMotorTable, 0, 512 * 8 * sizeof (unsigned short)); CalMotorTable.StartSpeed = StartSpeed; CalMotorTable.EndSpeed = EndSpeed; CalMotorTable.AccStepBeforeScan = lpMotorStepsTable->wScanAccSteps; CalMotorTable.DecStepAfterScan = lpMotorStepsTable->bScanDecSteps; CalMotorTable.lpMotorTable = lpMotorTable; CalculateMotorTable (&CalMotorTable, wYResolution); CurrentPhase.MotorDriverIs3967 = 0; CurrentPhase.FillPhase = 1; CurrentPhase.MoveType = bMotorMoveType; SetMotorCurrent (chip, EndSpeed, &CurrentPhase); LLFSetMotorCurrentAndPhase (chip, &CurrentPhase); DBG (DBG_ASIC, "EndSpeed = %d, BytesCountPerRow=%d, MotorCurrentTable=%d, LinePixelReport=%d\n", EndSpeed, chip->dwBytesCountPerRow, CurrentPhase.MotorCurrentTableA[0], dwLinePixelReport); /* write motor table */ RealTableSize = 512 * 8; dwTableBaseAddr = PackAreaStartAddress - RealTableSize; dwStartAddr = dwTableBaseAddr; RamAccess.ReadWrite = WRITE_RAM; RamAccess.IsOnChipGamma = EXTERNAL_RAM; RamAccess.DramDelayTime = SDRAMCLK_DELAY_12_ns; RamAccess.LoStartAddress = (unsigned short) (dwStartAddr); RamAccess.HiStartAddress = (unsigned short) (dwStartAddr >> 16); RamAccess.RwSize = RealTableSize * 2; RamAccess.BufferPtr = (SANE_Byte *) lpMotorTable; LLFRamAccess (chip, &RamAccess); Mustek_SendData (chip, ES01_DB_PH_RESET_EDGE_TIMING_ADJUST, 0x00); Mustek_SendData (chip, ES01_DC_CLEAR_EDGE_TO_PH_TG_EDGE_WIDTH, 0); Mustek_SendData (chip, ES01_9D_MotorTableAddrA14_A21, (SANE_Byte) (dwTableBaseAddr >> TABLE_OFFSET_BASE)); /* set address and shading table */ /*set image buffer range and write shading table */ RealTableSize = (ACC_DEC_STEP_TABLE_SIZE * NUM_OF_ACC_DEC_STEP_TABLE) + ShadingTableSize (ValidPixelNumber); dwShadingTableAddr = PackAreaStartAddress - (((RealTableSize + (TABLE_BASE_SIZE - 1)) >> TABLE_OFFSET_BASE) << TABLE_OFFSET_BASE); dwEndAddr = dwShadingTableAddr - 1; dwStartAddr = dwShadingTableAddr; if (wXResolution > 600) dbXRatioAdderDouble = 1200 / wXResolution; else dbXRatioAdderDouble = 600 / wXResolution; RamAccess.ReadWrite = WRITE_RAM; RamAccess.IsOnChipGamma = EXTERNAL_RAM; RamAccess.DramDelayTime = SDRAMCLK_DELAY_12_ns; RamAccess.LoStartAddress = (unsigned short) (dwStartAddr); RamAccess.HiStartAddress = (unsigned short) (dwStartAddr >> 16); RamAccess.RwSize = ShadingTableSize ((int) ((wWidth + 4) * dbXRatioAdderDouble)) * sizeof (unsigned short); RamAccess.BufferPtr = (SANE_Byte *) chip->lpShadingTable; LLFRamAccess (chip, &RamAccess); /*tell scan chip the shading table address, unit is 2^15 bytes(2^14 word) */ Mustek_SendData (chip, ES01_9B_ShadingTableAddrA14_A21, (SANE_Byte) (dwShadingTableAddr >> TABLE_OFFSET_BASE)); /*empty bank */ Mustek_SendData (chip, ES01_FB_BufferEmptySize16WordLSB, LOBYTE (WaitBufferOneLineSize >> (7 - 3))); Mustek_SendData (chip, ES01_FC_BufferEmptySize16WordMSB, HIBYTE (WaitBufferOneLineSize >> (7 - 3))); /*full bank */ wFullBank = (unsigned short) ((dwEndAddr - (((dwLineWidthPixel * BytePerPixel) / 2) * 3 * 1)) / BANK_SIZE); Mustek_SendData (chip, ES01_F9_BufferFullSize16WordLSB, LOBYTE (wFullBank)); Mustek_SendData (chip, ES01_FA_BufferFullSize16WordMSB, HIBYTE (wFullBank)); /* set buffer address */ LLFSetRamAddress (chip, 0x0, dwEndAddr, ACCESS_DRAM); Mustek_SendData (chip, ES01_00_ADAFEConfiguration, 0x70); Mustek_SendData (chip, ES01_02_ADAFEMuxConfig, 0x80); free (lpMotorTable); free (lpMotorStepsTable); chip->firmwarestate = FS_OPENED; DBG (DBG_ASIC, "Asic_SetWindow: Exit\n"); return status; } static SANE_Status Asic_Reset (PAsic chip) { SANE_Status status = SANE_STATUS_GOOD; DBG (DBG_ASIC, "Asic_Reset: Enter\n"); chip->lsLightSource = LS_REFLECTIVE; chip->dwBytesCountPerRow = 0; chip->AD.DirectionR = 0; chip->AD.DirectionG = 0; chip->AD.DirectionB = 0; chip->AD.GainR = 0; chip->AD.GainG = 0; chip->AD.GainB = 0; chip->AD.OffsetR = 0; chip->AD.OffsetG = 0; chip->AD.OffsetB = 0; chip->Scan.TotalMotorSteps = 60000; chip->Scan.StartLine = 0; chip->Scan.StartPixel = 0; DBG (DBG_ASIC, "Asic_Reset: Exit\n"); return status; } static SANE_Status Asic_SetSource (PAsic chip, LIGHTSOURCE lsLightSource) { SANE_Status status = SANE_STATUS_GOOD; DBG (DBG_ASIC, "Asic_SetSource: Enter\n"); chip->lsLightSource = lsLightSource; switch (chip->lsLightSource) { case 1: DBG (DBG_ASIC, "Asic_SetSource: Source is Reflect\n"); break; case 2: DBG (DBG_ASIC, "Asic_SetSource: Source is Position\n"); break; case 4: DBG (DBG_ASIC, "Asic_SetSource: Source is Negative\n"); break; default: DBG (DBG_ASIC, "Asic_SetSource: Source error\n"); } DBG (DBG_ASIC, "Asic_SetSource: Exit\n"); return status; } static SANE_Status Asic_ScanStart (PAsic chip) { SANE_Status status = SANE_STATUS_GOOD; DBG (DBG_ASIC, "Asic_ScanStart: Enter\n"); if (chip->firmwarestate != FS_OPENED) { DBG (DBG_ERR, "Asic_ScanStart: Scanner is not opened\n"); return SANE_STATUS_INVAL; } Mustek_SendData (chip, ES01_8B_Status, 0x1c | 0x20); Mustek_WriteAddressLineForRegister (chip, 0x8B); Mustek_ClearFIFO (chip); Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_ENABLE); chip->firmwarestate = FS_SCANNING; DBG (DBG_ASIC, "Asic_ScanStart: Exit\n"); return status; } static SANE_Status Asic_ScanStop (PAsic chip) { SANE_Status status = SANE_STATUS_GOOD; SANE_Byte temps[2]; SANE_Byte buf[4]; DBG (DBG_ASIC, "Asic_ScanStop: Enter\n"); if (chip->firmwarestate < FS_SCANNING) { return status; } usleep (100 * 1000); buf[0] = 0x02; /*stop */ buf[1] = 0x02; buf[2] = 0x02; buf[3] = 0x02; status = WriteIOControl (chip, 0xc0, 0, 4, buf); if (status != SANE_STATUS_GOOD) { DBG (DBG_ERR, "Asic_ScanStop: Stop scan error\n"); return status; } buf[0] = 0x00; /*clear */ buf[1] = 0x00; buf[2] = 0x00; buf[3] = 0x00; status = WriteIOControl (chip, 0xc0, 0, 4, buf); if (status != SANE_STATUS_GOOD) { DBG (DBG_ERR, "Asic_ScanStop: Clear scan error\n"); return status; } status = Mustek_DMARead (chip, 2, temps); if (status != SANE_STATUS_GOOD) { DBG (DBG_ERR, "Asic_ScanStop: DMAReadGeneralMode error\n"); return status; } Mustek_SendData (chip, ES01_F3_ActionOption, 0); Mustek_SendData (chip, ES01_86_DisableAllClockWhenIdle, CLOSE_ALL_CLOCK_DISABLE); Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_DISABLE); Mustek_ClearFIFO (chip); chip->firmwarestate = FS_OPENED; DBG (DBG_ASIC, "Asic_ScanStop: Exit\n"); return status; } static SANE_Status Asic_ReadImage (PAsic chip, SANE_Byte * pBuffer, unsigned short LinesCount) { SANE_Status status = SANE_STATUS_GOOD; unsigned int dwXferBytes; DBG (DBG_ASIC, "Asic_ReadImage: Enter : LinesCount = %d\n", LinesCount); if (chip->firmwarestate != FS_SCANNING) { DBG (DBG_ERR, "Asic_ReadImage: Scanner is not scanning\n"); return SANE_STATUS_INVAL; } dwXferBytes = (unsigned int) (LinesCount) * chip->dwBytesCountPerRow; DBG (DBG_ASIC, "Asic_ReadImage: chip->dwBytesCountPerRow = %d\n", chip->dwBytesCountPerRow); /* HOLD: an unsigned long can't be < 0 if (dwXferBytes < 0) { DBG (DBG_ASIC, "Asic_ReadImage: dwXferBytes <0\n"); return SANE_STATUS_INVAL; } */ if (dwXferBytes == 0) { DBG (DBG_ASIC, "Asic_ReadImage: dwXferBytes == 0\n"); return SANE_STATUS_GOOD; } status = Mustek_DMARead (chip, dwXferBytes, pBuffer); DBG (DBG_ASIC, "Asic_ReadImage: Exit\n"); return status; } #if SANE_UNUSED static SANE_Status Asic_CheckFunctionKey (PAsic chip, SANE_Byte * key) { SANE_Status status = SANE_STATUS_GOOD; SANE_Byte bBuffer_1 = 0xff; SANE_Byte bBuffer_2 = 0xff; DBG (DBG_ASIC, "Asic_CheckFunctionKey: Enter\n"); if (chip->firmwarestate != FS_OPENED) { DBG (DBG_ERR, "Asic_CheckFunctionKey: Scanner is not Opened\n"); return SANE_STATUS_INVAL; } Mustek_SendData (chip, ES01_97_GPIOControl0_7, 0x00); Mustek_SendData (chip, ES01_95_GPIOValue0_7, 0x17); Mustek_SendData (chip, ES01_98_GPIOControl8_15, 0x00); Mustek_SendData (chip, ES01_96_GPIOValue8_15, 0x08); GetChipStatus (chip, 0x02, &bBuffer_1); GetChipStatus (chip, 0x03, &bBuffer_2); if (((0xff - bBuffer_1) & 0x10) == 0x10) *key = 0x01; if (((0xff - bBuffer_1) & 0x01) == 0x01) *key = 0x02; if (((0xff - bBuffer_1) & 0x04) == 0x04) *key = 0x04; if (((0xff - bBuffer_2) & 0x08) == 0x08) *key = 0x08; if (((0xff - bBuffer_1) & 0x02) == 0x02) *key = 0x10; DBG (DBG_ASIC, "CheckFunctionKey=%d\n", *key); DBG (DBG_ASIC, "Asic_CheckFunctionKey: Exit\n"); return status; } #endif static SANE_Status Asic_IsTAConnected (PAsic chip, SANE_Bool * hasTA) { SANE_Byte bBuffer_1 = 0xff; DBG (DBG_ASIC, "Asic_IsTAConnected: Enter\n"); Mustek_SendData (chip, ES01_97_GPIOControl0_7, 0x00); Mustek_SendData (chip, ES01_95_GPIOValue0_7, 0x00); Mustek_SendData (chip, ES01_98_GPIOControl8_15, 0x00); Mustek_SendData (chip, ES01_96_GPIOValue8_15, 0x00); GetChipStatus (chip, 0x02, &bBuffer_1); if (((0xff - bBuffer_1) & 0x08) == 0x08) *hasTA = TRUE; else *hasTA = FALSE; DBG (DBG_ASIC, "hasTA=%d\n", *hasTA); DBG (DBG_ASIC, "Asic_IsTAConnected():Exit\n"); return SANE_STATUS_GOOD; } #if SANE_UNUSED static SANE_Status Asic_DownloadGammaTable (PAsic chip, void * lpBuffer) { SANE_Status status = SANE_STATUS_GOOD; DBG (DBG_ASIC, "Asic_DownloadGammaTable()\n"); chip->lpGammaTable = lpBuffer; DBG (DBG_ASIC, "Asic_DownloadGammaTable: Exit\n"); return status; } #endif static SANE_Status Asic_ReadCalibrationData (PAsic chip, void * pBuffer, unsigned int dwXferBytes, SANE_Byte bScanBits) { SANE_Status status = SANE_STATUS_GOOD; SANE_Byte * pCalBuffer; unsigned int dwTotalReadData; unsigned int dwReadImageData; DBG (DBG_ASIC, "Asic_ReadCalibrationData: Enter\n"); if (chip->firmwarestate != FS_SCANNING) { DBG (DBG_ERR, "Asic_ReadCalibrationData: Scanner is not scanning\n"); return SANE_STATUS_INVAL; } if (bScanBits == 24) { unsigned int i; pCalBuffer = (SANE_Byte *) malloc (dwXferBytes); if (pCalBuffer == NULL) { DBG (DBG_ERR, "Asic_ReadCalibrationData: Can't malloc bCalBuffer memory\n"); return SANE_STATUS_NO_MEM; } for (dwTotalReadData = 0; dwTotalReadData < dwXferBytes;) { dwReadImageData = (dwXferBytes - dwTotalReadData) < 65536 ? (dwXferBytes - dwTotalReadData) : 65536; Mustek_DMARead (chip, dwReadImageData, (SANE_Byte *) (pCalBuffer + dwTotalReadData)); dwTotalReadData += dwReadImageData; } dwXferBytes /= 3; for (i = 0; i < dwXferBytes; i++) { *((SANE_Byte *) pBuffer + i) = *(pCalBuffer + i * 3); *((SANE_Byte *) pBuffer + dwXferBytes + i) = *(pCalBuffer + i * 3 + 1); *((SANE_Byte *) pBuffer + dwXferBytes * 2 + i) = *(pCalBuffer + i * 3 + 2); } free (pCalBuffer); } else if (bScanBits == 8) { for (dwTotalReadData = 0; dwTotalReadData < dwXferBytes;) { dwReadImageData = (dwXferBytes - dwTotalReadData) < 65536 ? (dwXferBytes - dwTotalReadData) : 65536; Mustek_DMARead (chip, dwReadImageData, (SANE_Byte *) pBuffer + dwTotalReadData); dwTotalReadData += dwReadImageData; } } DBG (DBG_ASIC, "Asic_ReadCalibrationData: Exit\n"); return status; } static SANE_Status Asic_SetMotorType (PAsic chip, SANE_Bool isMotorMove, SANE_Bool isUniformSpeed) { SANE_Status status = SANE_STATUS_GOOD; (void) isUniformSpeed; DBG (DBG_ASIC, "Asic_SetMotorType:Enter\n"); if (isMotorMove) { chip->isMotorMove = MOTOR_0_ENABLE; } else chip->isMotorMove = MOTOR_0_DISABLE; DBG (DBG_ASIC, "isMotorMove=%d\n", chip->isMotorMove); DBG (DBG_ASIC, "Asic_SetMotorType: Exit\n"); return status; } static SANE_Status Asic_MotorMove (PAsic chip, SANE_Bool isForward, unsigned int dwTotalSteps) { SANE_Status status = SANE_STATUS_GOOD; unsigned short *NormalMoveMotorTable; LLF_CALCULATEMOTORTABLE CalMotorTable; LLF_MOTOR_CURRENT_AND_PHASE CurrentPhase; LLF_SETMOTORTABLE LLF_SetMotorTable; LLF_MOTORMOVE MotorMove; DBG (DBG_ASIC, "Asic_MotorMove:Enter\n"); NormalMoveMotorTable = (unsigned short *) malloc (512 * 8 * 2); CalMotorTable.StartSpeed = 5000; CalMotorTable.EndSpeed = 1800; CalMotorTable.AccStepBeforeScan = 511; CalMotorTable.lpMotorTable = NormalMoveMotorTable; LLFCalculateMotorTable (&CalMotorTable); CurrentPhase.MotorDriverIs3967 = 0; CurrentPhase.MotorCurrentTableA[0] = 200; CurrentPhase.MotorCurrentTableB[0] = 200; CurrentPhase.MoveType = _4_TABLE_SPACE_FOR_FULL_STEP; LLFSetMotorCurrentAndPhase (chip, &CurrentPhase); LLF_SetMotorTable.MotorTableAddress = 0; LLF_SetMotorTable.MotorTablePtr = NormalMoveMotorTable; LLFSetMotorTable (chip, &LLF_SetMotorTable); free (NormalMoveMotorTable); MotorMove.MotorSelect = MOTOR_0_ENABLE | MOTOR_1_DISABLE; MotorMove.MotorMoveUnit = ES03_TABLE_DEFINE; MotorMove.MotorSpeedUnit = SPEED_UNIT_1_PIXEL_TIME; MotorMove.MotorSyncUnit = MOTOR_SYNC_UNIT_1_PIXEL_TIME; MotorMove.HomeSensorSelect = HOME_SENSOR_0_ENABLE; MotorMove.ActionMode = ACTION_MODE_ACCDEC_MOVE; MotorMove.ActionType = isForward; if (dwTotalSteps > 1000) { MotorMove.AccStep = 511; MotorMove.DecStep = 255; MotorMove.FixMoveSteps = dwTotalSteps - (511 + 255); } else { MotorMove.ActionMode = ACTION_MODE_UNIFORM_SPEED_MOVE; MotorMove.AccStep = 1; MotorMove.DecStep = 1; MotorMove.FixMoveSteps = dwTotalSteps - 2; } MotorMove.FixMoveSpeed = 7000; MotorMove.WaitOrNoWait = TRUE; LLFMotorMove (chip, &MotorMove); DBG (DBG_ASIC, "Asic_MotorMove: Exit\n"); return status; } static SANE_Status Asic_CarriageHome (PAsic chip, SANE_Bool isTA) { SANE_Status status = SANE_STATUS_GOOD; SANE_Bool LampHome, TAHome; (void) isTA; DBG (DBG_ASIC, "Asic_CarriageHome:Enter\n"); status = IsCarriageHome (chip, &LampHome, &TAHome); if (!LampHome) { status = MotorBackHome (chip, TRUE); } DBG (DBG_ASIC, "Asic_CarriageHome: Exit\n"); return status; } static SANE_Status Asic_SetShadingTable (PAsic chip, unsigned short * lpWhiteShading, unsigned short * lpDarkShading, unsigned short wXResolution, unsigned short wWidth, unsigned short wX) { SANE_Status status = SANE_STATUS_GOOD; unsigned short i, j, n; unsigned short wValidPixelNumber; double dbXRatioAdderDouble; unsigned int wShadingTableSize; (void) wX; DBG (DBG_ASIC, "Asic_SetShadingTable:Enter\n"); if (chip->firmwarestate < FS_OPENED) OpenScanChip (chip); if (chip->firmwarestate == FS_SCANNING) Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_DISABLE); if (wXResolution > 600) dbXRatioAdderDouble = 1200 / wXResolution; else dbXRatioAdderDouble = 600 / wXResolution; wValidPixelNumber = (unsigned short) ((wWidth + 4) * dbXRatioAdderDouble); DBG (DBG_ASIC, "wValidPixelNumber = %d\n", wValidPixelNumber); /* clear old Shading table, if it has. */ /* first 4 element and latest 5 of Shading table can't been used */ wShadingTableSize = (ShadingTableSize (wValidPixelNumber)) * sizeof (unsigned short); if (chip->lpShadingTable != NULL) { free (chip->lpShadingTable); } DBG (DBG_ASIC, "Alloc a new shading table= %d Byte!\n", wShadingTableSize); chip->lpShadingTable = (SANE_Byte *) malloc (wShadingTableSize); if (chip->lpShadingTable == NULL) { DBG (DBG_ASIC, "lpShadingTable == NULL\n"); return SANE_STATUS_NO_MEM; } n = 0; for (i = 0; i <= (wValidPixelNumber / 40); i++) { if (i < (wValidPixelNumber / 40)) { for (j = 0; j < 40; j++) { *((unsigned short *) chip->lpShadingTable + i * 256 + j * 6) = *((unsigned short *) lpDarkShading + n * 3); *((unsigned short *) chip->lpShadingTable + i * 256 + j * 6 + 2) = *((unsigned short *) lpDarkShading + n * 3 + 1); *((unsigned short *) chip->lpShadingTable + i * 256 + j * 6 + 4) = *((unsigned short *) lpDarkShading + n * 3 + 2); *((unsigned short *) chip->lpShadingTable + i * 256 + j * 6 + 1) = *((unsigned short *) lpWhiteShading + n * 3); *((unsigned short *) chip->lpShadingTable + i * 256 + j * 6 + 3) = *((unsigned short *) lpWhiteShading + n * 3 + 1); *((unsigned short *) chip->lpShadingTable + i * 256 + j * 6 + 5) = *((unsigned short *) lpWhiteShading + n * 3 + 2); if ((j % (unsigned short) dbXRatioAdderDouble) == (dbXRatioAdderDouble - 1)) n++; if (i == 0 && j < 4 * dbXRatioAdderDouble) n = 0; } } else { for (j = 0; j < (wValidPixelNumber % 40); j++) { *((unsigned short *) chip->lpShadingTable + i * 256 + j * 6) = *((unsigned short *) lpDarkShading + (n) * 3); *((unsigned short *) chip->lpShadingTable + i * 256 + j * 6 + 2) = *((unsigned short *) lpDarkShading + (n) * 3 + 1); *((unsigned short *) chip->lpShadingTable + i * 256 + j * 6 + 4) = *((unsigned short *) lpDarkShading + (n) * 3 + 2); *((unsigned short *) chip->lpShadingTable + i * 256 + j * 6 + 1) = *((unsigned short *) lpWhiteShading + (n) * 3); *((unsigned short *) chip->lpShadingTable + i * 256 + j * 6 + 3) = *((unsigned short *) lpWhiteShading + (n) * 3 + 1); *((unsigned short *) chip->lpShadingTable + i * 256 + j * 6 + 5) = *((unsigned short *) lpWhiteShading + (n) * 3 + 2); if ((j % (unsigned short) dbXRatioAdderDouble) == (dbXRatioAdderDouble - 1)) n++; if (i == 0 && j < 4 * dbXRatioAdderDouble) n = 0; } } } DBG (DBG_ASIC, "Asic_SetShadingTable: Exit\n"); return status; } static SANE_Status Asic_WaitCarriageHome (PAsic chip, SANE_Bool isTA) { SANE_Status status = SANE_STATUS_GOOD; SANE_Bool LampHome, TAHome; int i; (void) isTA; DBG (DBG_ASIC, "Asic_WaitCarriageHome:Enter\n"); for (i = 0; i < 100; i++) { status = IsCarriageHome (chip, &LampHome, &TAHome); if (LampHome) break; usleep (300000); } if (i == 100) status = SANE_STATUS_DEVICE_BUSY; DBG (DBG_ASIC, "Wait %d s\n", (unsigned short) (i * 0.3)); Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_DISABLE); chip->firmwarestate = FS_OPENED; chip->motorstate = MS_STILL; DBG (DBG_ASIC, "Asic_WaitCarriageHome: Exit\n"); return status; } static SANE_Status Asic_SetCalibrate (PAsic chip, SANE_Byte bScanBits, unsigned short wXResolution, unsigned short wYResolution, unsigned short wX, unsigned short wY, unsigned short wWidth, unsigned short wLength, SANE_Bool isShading) { SANE_Status status = SANE_STATUS_GOOD; unsigned short ValidPixelNumber; unsigned short wPerLineNeedBufferSize = 0; unsigned short BytePerPixel = 0; unsigned int dwTotalLineTheBufferNeed = 0; unsigned short dwTotal_CCDResolution = 0; unsigned short wThinkCCDResolution = 0; unsigned short wCCD_PixelNumber = 0; unsigned short wScanAccSteps = 1; SANE_Byte byScanDecSteps = 1; unsigned int dwLineWidthPixel = 0; unsigned short wNowMotorDPI; unsigned short XRatioTypeWord; double XRatioTypeDouble; double XRatioAdderDouble; LLF_MOTORMOVE *lpMotorStepsTable = (LLF_MOTORMOVE *) malloc (sizeof (LLF_MOTORMOVE)); SANE_Byte byDummyCycleNum = 1; unsigned short Total_MotorDPI; unsigned short BeforeScanFixSpeedStep = 0; unsigned short BackTrackFixSpeedStep = 20; unsigned short wMultiMotorStep = 1; SANE_Byte bMotorMoveType = _MOTOR_MOVE_TYPE; SANE_Byte isMotorMoveToFirstLine = MOTOR_MOVE_TO_FIRST_LINE_DISABLE; SANE_Byte isUniformSpeedToScan = UNIFORM_MOTOR_AND_SCAN_SPEED_ENABLE; SANE_Byte isScanBackTracking = SCAN_BACK_TRACKING_DISABLE; unsigned int TotalStep = 0; unsigned short StartSpeed, EndSpeed; LLF_CALCULATEMOTORTABLE CalMotorTable; LLF_MOTOR_CURRENT_AND_PHASE CurrentPhase; LLF_RAMACCESS RamAccess; unsigned int dwStartAddr, dwEndAddr, dwTableBaseAddr; SANE_Byte byClear_Pulse_Width = 0; unsigned int dwLinePixelReport = 0; SANE_Byte byPHTG_PulseWidth, byPHTG_WaitWidth; unsigned short * lpMotorTable = (unsigned short *) malloc (512 * 8 * 2); unsigned int RealTableSize; unsigned short wFullBank; DBG (DBG_ASIC, "Asic_SetCalibrate: Enter\n"); DBG (DBG_ASIC, "bScanBits=%d,wXResolution=%d, wYResolution=%d, wX=%d, wY=%d, wWidth=%d, wLength=%d\n", bScanBits, wXResolution, wYResolution, wX, wY, wWidth, wLength); if (chip->firmwarestate != FS_OPENED) { DBG (DBG_ERR, "Asic_SetCalibrate: Scanner is not opened\n"); return SANE_STATUS_INVAL; } if (lpMotorStepsTable == NULL) { DBG (DBG_ERR, "Asic_SetCalibrate: insufficiency memory!\n"); return SANE_STATUS_INVAL; } DBG (DBG_ASIC, "malloc LLF_MOTORMOVE =%ld Byte\n", (long int) (sizeof (LLF_MOTORMOVE))); Mustek_SendData (chip, ES01_F3_ActionOption, 0); Mustek_SendData (chip, ES01_86_DisableAllClockWhenIdle, CLOSE_ALL_CLOCK_DISABLE); Mustek_SendData (chip, ES01_F4_ActiveTriger, ACTION_TRIGER_DISABLE); status = Asic_WaitUnitReady (chip); Mustek_SendData (chip, 0x1CD, 0); Mustek_SendData (chip, ES01_94_PowerSaveControl, 0x27 | 64 | 128); if (bScanBits > 24) { wPerLineNeedBufferSize = wWidth * 6; BytePerPixel = 6; chip->dwBytesCountPerRow = (unsigned int) (wWidth) * 6; } else if (bScanBits == 24) { wPerLineNeedBufferSize = wWidth * 3; chip->dwCalibrationBytesCountPerRow = wWidth * 3; BytePerPixel = 3; chip->dwBytesCountPerRow = (unsigned int) (wWidth) * 3; } else if ((bScanBits > 8) && (bScanBits <= 16)) { wPerLineNeedBufferSize = wWidth * 2; BytePerPixel = 2; chip->dwBytesCountPerRow = (unsigned int) (wWidth) * 2; } else if (bScanBits == 8) { wPerLineNeedBufferSize = wWidth; BytePerPixel = 1; chip->dwBytesCountPerRow = (unsigned int) (wWidth); } else if (bScanBits < 8) { wPerLineNeedBufferSize = wWidth >> 3; BytePerPixel = 1; chip->dwBytesCountPerRow = (unsigned int) (wWidth); } DBG (DBG_ASIC, "wPerLineNeedBufferSize=%d,BytePerPixel=%d,dwBytesCountPerRow=%d\n", wPerLineNeedBufferSize, BytePerPixel, chip->dwBytesCountPerRow); dwTotalLineTheBufferNeed = wLength; DBG (DBG_ASIC, "wPerLineNeedBufferSize=%d,wLength=%d\n", wPerLineNeedBufferSize, wLength); chip->Scan.Dpi = wXResolution; CCDTiming (chip); dwTotal_CCDResolution = SENSOR_DPI; if (chip->lsLightSource == LS_REFLECTIVE) { if (wXResolution > (dwTotal_CCDResolution / 2)) { wThinkCCDResolution = dwTotal_CCDResolution; Mustek_SendData (chip, ES01_98_GPIOControl8_15, 0x01); Mustek_SendData (chip, ES01_96_GPIOValue8_15, 0x01); wCCD_PixelNumber = chip->Timing.wCCDPixelNumber_1200; } else { wThinkCCDResolution = dwTotal_CCDResolution / 2; Mustek_SendData (chip, ES01_98_GPIOControl8_15, 0x01); Mustek_SendData (chip, ES01_96_GPIOValue8_15, 0x00); wCCD_PixelNumber = chip->Timing.wCCDPixelNumber_600; } } else { if (wXResolution > (dwTotal_CCDResolution / 2)) { wThinkCCDResolution = dwTotal_CCDResolution; Mustek_SendData (chip, ES01_98_GPIOControl8_15, 0x01); Mustek_SendData (chip, ES01_96_GPIOValue8_15, 0x01); wCCD_PixelNumber = TA_CAL_PIXELNUMBER; } else { wThinkCCDResolution = dwTotal_CCDResolution / 2; Mustek_SendData (chip, ES01_98_GPIOControl8_15, 0x01); Mustek_SendData (chip, ES01_96_GPIOValue8_15, 0x00); wCCD_PixelNumber = TA_CAL_PIXELNUMBER; } } DBG (DBG_ASIC, "wThinkCCDResolution=%d,wCCD_PixelNumber=%d\n", wThinkCCDResolution, wCCD_PixelNumber); dwLineWidthPixel = wWidth; if (isShading) wYResolution = 600; DBG (DBG_ASIC, "dwLineWidthPixel=%d,wYResolution=%d\n", dwLineWidthPixel, wYResolution); SetLineTimeAndExposure (chip); if (wYResolution == 600) { Mustek_SendData (chip, ES01_CB_CCDDummyCycleNumber, byDummyCycleNum); DBG (DBG_ASIC, "Find Boundary CCDDummyCycleNumber == %d\n", byDummyCycleNum); } SetLEDTime (chip); /* calculate Y ratio */ Total_MotorDPI = 1200; wNowMotorDPI = Total_MotorDPI; DBG (DBG_ASIC, "wNowMotorDPI=%d\n", wNowMotorDPI); /* SetADConverter */ Mustek_SendData (chip, ES01_74_HARDWARE_SETTING, MOTOR1_SERIAL_INTERFACE_G10_8_ENABLE | LED_OUT_G11_DISABLE | SLAVE_SERIAL_INTERFACE_G15_14_DISABLE | SHUTTLE_CCD_DISABLE); /* set AFE */ Mustek_SendData (chip, ES01_9A_AFEControl, AD9826_AFE | AUTO_CHANGE_AFE_GAIN_OFFSET_DISABLE); Mustek_SendData (chip, ES01_F7_DigitalControl, DIGITAL_REDUCE_DISABLE); XRatioTypeDouble = wXResolution; XRatioTypeDouble /= wThinkCCDResolution; XRatioAdderDouble = 1 / XRatioTypeDouble; XRatioTypeWord = (unsigned short) (XRatioTypeDouble * 32768); XRatioAdderDouble = (double) (XRatioTypeWord) / 32768; XRatioAdderDouble = 1 / XRatioAdderDouble; Mustek_SendData (chip, ES01_9E_HorizontalRatio1to15LSB, LOBYTE (XRatioTypeWord)); Mustek_SendData (chip, ES01_9F_HorizontalRatio1to15MSB, HIBYTE (XRatioTypeWord)); DBG (DBG_ASIC, "XRatioTypeDouble=%.2f,XRatioAdderDouble=%.2f,XRatioTypeWord=%d\n", XRatioTypeDouble, XRatioAdderDouble, XRatioTypeWord); if (chip->isMotorMove == MOTOR_0_ENABLE) { Mustek_SendData (chip, ES01_A6_MotorOption, MOTOR_0_ENABLE | MOTOR_1_DISABLE | HOME_SENSOR_0_ENABLE | ES03_TABLE_DEFINE); } else { Mustek_SendData (chip, ES01_A6_MotorOption, MOTOR_0_DISABLE | MOTOR_1_DISABLE | HOME_SENSOR_0_ENABLE | ES03_TABLE_DEFINE); } DBG (DBG_ASIC, "isMotorMove=%d\n", chip->isMotorMove); Mustek_SendData (chip, ES01_F6_MorotControl1, SPEED_UNIT_1_PIXEL_TIME | MOTOR_SYNC_UNIT_1_PIXEL_TIME); wScanAccSteps = 1; byScanDecSteps = 1; DBG (DBG_ASIC, "wScanAccSteps=%d,byScanDecSteps=%d\n", wScanAccSteps, byScanDecSteps); Mustek_SendData (chip, ES01_AE_MotorSyncPixelNumberM16LSB, LOBYTE (0)); Mustek_SendData (chip, ES01_AF_MotorSyncPixelNumberM16MSB, HIBYTE (0)); DBG (DBG_ASIC, "MotorSyncPixelNumber=%d\n", 0); Mustek_SendData (chip, ES01_EC_ScanAccStep0_7, LOBYTE (wScanAccSteps)); Mustek_SendData (chip, ES01_ED_ScanAccStep8_8, HIBYTE (wScanAccSteps)); DBG (DBG_ASIC, "wScanAccSteps=%d\n", wScanAccSteps); DBG (DBG_ASIC, "BeforeScanFixSpeedStep=%d,BackTrackFixSpeedStep=%d\n", BeforeScanFixSpeedStep, BackTrackFixSpeedStep); Mustek_SendData (chip, ES01_EE_FixScanStepLSB, LOBYTE (BeforeScanFixSpeedStep)); Mustek_SendData (chip, ES01_8A_FixScanStepMSB, HIBYTE (BeforeScanFixSpeedStep)); DBG (DBG_ASIC, "BeforeScanFixSpeedStep=%d\n", BeforeScanFixSpeedStep); Mustek_SendData (chip, ES01_EF_ScanDecStep, byScanDecSteps); DBG (DBG_ASIC, "byScanDecSteps=%d\n", byScanDecSteps); Mustek_SendData (chip, ES01_E6_ScanBackTrackingStepLSB, LOBYTE (BackTrackFixSpeedStep)); Mustek_SendData (chip, ES01_E7_ScanBackTrackingStepMSB, HIBYTE (BackTrackFixSpeedStep)); DBG (DBG_ASIC, "BackTrackFixSpeedStep=%d\n", BackTrackFixSpeedStep); Mustek_SendData (chip, ES01_E8_ScanRestartStepLSB, LOBYTE (BackTrackFixSpeedStep)); Mustek_SendData (chip, ES01_E9_ScanRestartStepMSB, HIBYTE (BackTrackFixSpeedStep)); DBG (DBG_ASIC, "BackTrackFixSpeedStep=%d\n", BackTrackFixSpeedStep); switch (bMotorMoveType) { case _4_TABLE_SPACE_FOR_FULL_STEP: wMultiMotorStep = 1; break; case _8_TABLE_SPACE_FOR_1_DIV_2_STEP: wMultiMotorStep = 2; break; case _16_TABLE_SPACE_FOR_1_DIV_4_STEP: wMultiMotorStep = 4; break; case _32_TABLE_SPACE_FOR_1_DIV_8_STEP: wMultiMotorStep = 8; break; } DBG (DBG_ASIC, "wMultiMotorStep=%d\n", wMultiMotorStep); TotalStep = wScanAccSteps + BeforeScanFixSpeedStep + (dwTotalLineTheBufferNeed * wNowMotorDPI / wYResolution) + byScanDecSteps; DBG (DBG_ASIC, "TotalStep=%d\n", TotalStep); Mustek_SendData (chip, ES01_F0_ScanImageStep0_7, (SANE_Byte) (TotalStep)); Mustek_SendData (chip, ES01_F1_ScanImageStep8_15, (SANE_Byte) (TotalStep >> 8)); Mustek_SendData (chip, ES01_F2_ScanImageStep16_19, (SANE_Byte) (TotalStep >> 16)); SetScanMode (chip, bScanBits); DBG (DBG_ASIC, "isMotorMoveToFirstLine=%d,isUniformSpeedToScan=%d,isScanBackTracking=%d\n", isMotorMoveToFirstLine, isUniformSpeedToScan, isScanBackTracking); Mustek_SendData (chip, ES01_F3_ActionOption, isMotorMoveToFirstLine | MOTOR_BACK_HOME_AFTER_SCAN_DISABLE | SCAN_ENABLE | isScanBackTracking | INVERT_MOTOR_DIRECTION_DISABLE | isUniformSpeedToScan | ES01_STATIC_SCAN_DISABLE | MOTOR_TEST_LOOP_DISABLE); if (chip->lsLightSource == LS_REFLECTIVE) Mustek_SendData (chip, ES01_F8_WHITE_SHADING_DATA_FORMAT, ES01_SHADING_3_INT_13_DEC); else Mustek_SendData (chip, ES01_F8_WHITE_SHADING_DATA_FORMAT, ES01_SHADING_4_INT_12_DEC); SetPackAddress (chip, wXResolution, wWidth, wX, XRatioAdderDouble, XRatioTypeDouble, byClear_Pulse_Width, &ValidPixelNumber); SetExtraSetting (chip, wXResolution, wCCD_PixelNumber, TRUE); byPHTG_PulseWidth = chip->Timing.PHTG_PluseWidth; byPHTG_WaitWidth = chip->Timing.PHTG_WaitWidth; dwLinePixelReport = ((byClear_Pulse_Width + 1) * 2 + (byPHTG_PulseWidth + 1) * (1) + (byPHTG_WaitWidth + 1) * (1) + (wCCD_PixelNumber + 1)) * (byDummyCycleNum + 1); DBG (DBG_ASIC, "Motor Time = %d\n", (dwLinePixelReport * wYResolution / wNowMotorDPI)); if ((dwLinePixelReport * wYResolution / wNowMotorDPI) > 64000) { DBG (DBG_ASIC, "Motor Time Over Flow !!!\n"); } EndSpeed = (unsigned short) (dwLinePixelReport / (wNowMotorDPI / wYResolution)); if (wXResolution > 600) { StartSpeed = EndSpeed; } else { StartSpeed = EndSpeed + 3500; } DBG (DBG_ASIC, "StartSpeed =%d, EndSpeed = %d\n", StartSpeed, EndSpeed); Mustek_SendData (chip, ES01_FD_MotorFixedspeedLSB, LOBYTE (EndSpeed)); Mustek_SendData (chip, ES01_FE_MotorFixedspeedMSB, HIBYTE (EndSpeed)); memset (lpMotorTable, 0, 512 * 8 * sizeof (unsigned short)); CalMotorTable.StartSpeed = StartSpeed; CalMotorTable.EndSpeed = EndSpeed; CalMotorTable.AccStepBeforeScan = wScanAccSteps; CalMotorTable.lpMotorTable = lpMotorTable; LLFCalculateMotorTable (&CalMotorTable); CurrentPhase.MotorDriverIs3967 = 0; CurrentPhase.FillPhase = 1; CurrentPhase.MoveType = bMotorMoveType; CurrentPhase.MotorCurrentTableA[0] = 200; CurrentPhase.MotorCurrentTableB[0] = 200; LLFSetMotorCurrentAndPhase (chip, &CurrentPhase); RealTableSize = 512 * 8; dwTableBaseAddr = PackAreaStartAddress - RealTableSize; dwStartAddr = dwTableBaseAddr; RamAccess.ReadWrite = WRITE_RAM; RamAccess.IsOnChipGamma = EXTERNAL_RAM; RamAccess.DramDelayTime = SDRAMCLK_DELAY_12_ns; RamAccess.LoStartAddress = (unsigned short) (dwStartAddr); RamAccess.HiStartAddress = (unsigned short) (dwStartAddr >> 16); RamAccess.RwSize = RealTableSize * 2; RamAccess.BufferPtr = (SANE_Byte *) lpMotorTable; LLFRamAccess (chip, &RamAccess); Mustek_SendData (chip, ES01_9D_MotorTableAddrA14_A21, (SANE_Byte) (dwTableBaseAddr >> TABLE_OFFSET_BASE)); dwEndAddr = PackAreaStartAddress - (512 * 8) - 1; Mustek_SendData (chip, ES01_FB_BufferEmptySize16WordLSB, LOBYTE (WaitBufferOneLineSize >> (7 - 3))); Mustek_SendData (chip, ES01_FC_BufferEmptySize16WordMSB, HIBYTE (WaitBufferOneLineSize >> (7 - 3))); wFullBank = (unsigned short) ((dwEndAddr - (((dwLineWidthPixel * BytePerPixel) / 2) * 3 * 1)) / BANK_SIZE); Mustek_SendData (chip, ES01_F9_BufferFullSize16WordLSB, LOBYTE (wFullBank)); Mustek_SendData (chip, ES01_FA_BufferFullSize16WordMSB, HIBYTE (wFullBank)); Mustek_SendData (chip, ES01_DB_PH_RESET_EDGE_TIMING_ADJUST, 0x00); LLFSetRamAddress (chip, 0x0, dwEndAddr, ACCESS_DRAM); Mustek_SendData (chip, ES01_DC_CLEAR_EDGE_TO_PH_TG_EDGE_WIDTH, 0); Mustek_SendData (chip, ES01_00_ADAFEConfiguration, 0x70); Mustek_SendData (chip, ES01_02_ADAFEMuxConfig, 0x80); free (lpMotorTable); free (lpMotorStepsTable); DBG (DBG_ASIC, "Asic_SetCalibrate: Exit\n"); return status; } static SANE_Status Asic_SetAFEGainOffset (PAsic chip) { SANE_Status status = SANE_STATUS_GOOD; DBG (DBG_ASIC, "Asic_SetAFEGainOffset:Enter\n"); status = SetAFEGainOffset (chip); DBG (DBG_ASIC, "Asic_SetAFEGainOffset: Exit\n"); return status; } backends-1.3.0/backend/mustek_usb2_asic.h000066400000000000000000001344461456256263500203210ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2005 Mustek. Originally maintained by Mustek Author:Roy 2005.5.24 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, see . 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 the Mustek BearPaw 2448 TA Pro and similar USB2 scanners. */ #ifndef MUSTEK_USB2_ASIC_H #define MUSTEK_USB2_ASIC_H #include "../include/sane/sanei_usb.h" /* ---------------------- low level asic defines -------------------------- */ #define TRUE 1 #define FALSE 0 #define _MAX(a,b) ((a)>(b)?(a):(b)) #define _MIN(a,b) ((a)<(b)?(a):(b)) #ifndef LOBYTE #define LOBYTE(w) (SANE_Byte)((unsigned short)(w) & 0x00ff) #endif #ifndef HIBYTE #define HIBYTE(w) (SANE_Byte)((unsigned short)(w)>>8 & 0x00ff) #endif typedef enum tagFIRMWARESTATE { FS_NULL = 0, FS_ATTACHED = 1, FS_OPENED = 2, FS_SCANNING = 3 } FIRMWARESTATE, *LPFIRMWARESTATE; typedef enum tagMOTORSTATE { MS_STILL = 0, MS_MOVED = 1 } MOTORSTATE, *LPMOTORSTATE; typedef enum tagUSBHOST { HT_USB10 = 0, HT_USB20 = 1 } USBHOST; typedef enum tagLIGHTSOURCE { LS_REFLECTIVE = 1, LS_POSITIVE = 2, LS_NEGATIVE = 4 } LIGHTSOURCE; typedef struct { unsigned int LongX; unsigned int PicWidth; unsigned int PicHeight; unsigned int Top; unsigned int Bottom; unsigned int Left; unsigned int Right; unsigned int ScanMode; unsigned int Dpi; unsigned int TotalMotorSteps; unsigned int CCD_Pixel_Length; SANE_Byte LineGap; SANE_Byte TG_Pulse_Width_Pixel; SANE_Byte TG_Wait_Width_Pixel; unsigned short Multi_TG_Dummy_Pixel; unsigned short CCD_Dummy_Pixel; SANE_Byte Dummy_Cycle; SANE_Byte TG_Times; double LineTime; unsigned short StartPixel; unsigned short StartLine; } ScanParam; typedef struct { unsigned int Shading_Table_Size; unsigned int Image_Buffer_Size; unsigned int Full_Bank; unsigned int Line_Pixel; double Line_Time; SANE_Byte LineGap; } Temps; typedef struct { /* AFE */ unsigned int AFE_ADCCLK_Timing; unsigned int AFE_ADCVS_Timing; unsigned int AFE_ADCRS_Timing; unsigned short AFE_ChannelA_LatchPos; unsigned short AFE_ChannelB_LatchPos; unsigned short AFE_ChannelC_LatchPos; unsigned short AFE_ChannelD_LatchPos; SANE_Byte AFE_Secondary_FF_LatchPos; /* Sensor */ unsigned int CCD_DummyCycleTiming; SANE_Byte PHTG_PluseWidth; SANE_Byte PHTG_WaitWidth; unsigned short ChannelR_StartPixel; unsigned short ChannelR_EndPixel; unsigned short ChannelG_StartPixel; unsigned short ChannelG_EndPixel; unsigned short ChannelB_StartPixel; unsigned short ChannelB_EndPixel; SANE_Byte PHTG_TimingAdj; SANE_Byte PHTG_TimingSetup; /*1200dpi */ unsigned int CCD_PHRS_Timing_1200; unsigned int CCD_PHCP_Timing_1200; unsigned int CCD_PH1_Timing_1200; unsigned int CCD_PH2_Timing_1200; SANE_Byte DE_CCD_SETUP_REGISTER_1200; unsigned short wCCDPixelNumber_1200; /*600dpi */ unsigned int CCD_PHRS_Timing_600; unsigned int CCD_PHCP_Timing_600; unsigned int CCD_PH1_Timing_600; unsigned int CCD_PH2_Timing_600; SANE_Byte DE_CCD_SETUP_REGISTER_600; unsigned short wCCDPixelNumber_600; } Timings; typedef struct tagADConverter { SANE_Byte GainR; SANE_Byte GainG; SANE_Byte GainB; SANE_Byte OffsetR; SANE_Byte OffsetG; SANE_Byte OffsetB; SANE_Bool DirectionR; SANE_Bool DirectionG; SANE_Bool DirectionB; } ADConverter, LPADConverter; typedef struct { unsigned int Shading; SANE_Byte Shading_0; SANE_Byte Shading_1; SANE_Byte Shading_2; unsigned int Motor; SANE_Byte Motor_0; SANE_Byte Motor_1; SANE_Byte Motor_2; SANE_Byte ImageEndAddr_0; SANE_Byte ImageEndAddr_1; SANE_Byte ImageEndAddr_2; SANE_Byte ImageFullBank_0; SANE_Byte ImageFullBank_1; } RamPosition; typedef enum tagTASSTATUS { TA_NOT_PLUGIN = 0, TA_PLUGIN = 1, TA_UNKNOW = 2 } TASTATUS; typedef struct { int fd; /* File Description of Scanner */ FIRMWARESTATE firmwarestate; /* record firmware state */ MOTORSTATE motorstate; /* record motor status */ SANE_Bool isFirstOpenChip; /* If first open chip, is TRUE */ USBHOST UsbHost; /* The type of USB port */ LIGHTSOURCE lsLightSource; /* light source of scanner */ ScanParam Scan; /* The parameters of Scan */ unsigned int dwBytesCountPerRow; unsigned int dwCalibrationBytesCountPerRow; Temps Temp; Timings Timing; ADConverter AD; SANE_Bool isHardwareShading; RamPosition RamPositions; unsigned short * lpGammaTable; SANE_Byte isMotorMove; unsigned int ibase1; unsigned int ibase2; unsigned short SWWidth; TASTATUS TA_Status; SANE_Byte isMotorGoToFirstLine; /*Roy add */ SANE_Byte * lpShadingTable; /*Roy add */ SANE_Byte isUniformSpeedToScan; } Asic, *PAsic; /* For ScanObj */ typedef struct Point { unsigned int x; unsigned int y; } Point; typedef struct Rect { unsigned int left; unsigned int right; unsigned int top; unsigned int bottom; } Rect; typedef struct RGBColor { unsigned short Red; unsigned short Green; unsigned short Blue; } RGBColor; /* debug levels */ #define DBG_CRIT 0 /* Critical errors thatshould be printed even if user hasn't enabled debugging -- use with care and only after sane_open has been called */ #define DBG_ERR 1 /* Other errors */ #define DBG_WARN 2 /* unusual conditions that may not be fatal */ #define DBG_INFO 3 /* information useful for the deucated user */ #define DBG_DET 4 /* more detailed information */ #define DBG_FUNC 5 /* start and exits of high level functions */ #define DBG_ASIC 6 /* starts and exits of low level functions */ #define DBG_DBG 10 /* useful only for tracing bugs */ #define DPI_2400 0x8000 #define DPI_1200 0x8000 #define DPI_600 0x8000 #define DPI_300 0x4000 #define DPI_200 0x2aaa #define DPI_150 0x2000 #define DPI_100 0x1555 #define DPI_75 0x1000 #define DPI_50 0xaaa #define PIXEL_TIME 333 /*unit : ms */ #define DRAM_1Mx16_SIZE (1024*1024) /*unit : word */ #define PackAreaStartAddress ((DRAM_1Mx16_SIZE/4)*3) #define TEMP_MEMORY_SIZE_64K 64*1024 #define CALIBRATION_PIXEL_WIDTH 10240 /*need 512x */ #define CALIBRATE_WHITE_LINECOUNT 40 #define CALIBRATE_DARK_LINECOUNT 2 #define ACTION_MODE_ACCDEC_MOVE 0 #define ACTION_MODE_UNIFORM_SPEED_MOVE 1 #define ACTION_TYPE_BACKWARD 0 #define ACTION_TYPE_FORWARD 1 #define ACTION_TYPE_BACKTOHOME 2 #define ACTION_TYPE_TEST_MODE 3 #define SENSOR0_DETECTED 0x10 #define SENSOR1_DETECTED 0x20 #define SENSOR0AND1_DETECTED 0x30 #define READ_RAM 0 #define WRITE_RAM 1 #define EXTERNAL_RAM 0 #define ON_CHIP_PRE_GAMMA 1 #define ON_CHIP_FINAL_GAMMA 2 #define MOTOR_TABLE_SIZE 512*8 #define ValidPixelNumberFor600DPI 5100 + 50 + 250 #define ValidPixelNumberFor1200DPI 10200 + 100 + 500 #define OverLapPixelNumber600 0 #define OverLapPixelNumber1200 0 #define SegmentGap 0 #define BANK_SIZE (64) #define WaitBufferOneLineSize 11000*6 #define CCD_PIXEL_NUMBER 21600 #define CCD_Line_Spacing 24 #define CCD_EvneOdd_Spacing 2 #define ShadingTableSize(x) ( ((x + 10)*6) + ( ((x + 10)*6)/240)*16 ) #define ACC_DEC_STEP_TABLE_SIZE (512) /*unit : word */ #define TableBase(x) ((((x)+((1<>TABLE_OFFSET_BASE)<. 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 the Mustek BearPaw 2448 TA Pro and similar USB2 scanners. */ #include /* HOLD */ #include /* local header files */ #include "mustek_usb2_asic.c" #include "mustek_usb2_high.h" /* ******************++ spuicall_g.h ****************************/ /*global variable HOLD: these should go to scanner structure */ /*base type*/ static SANE_Bool g_bOpened; static SANE_Bool g_bPrepared; static SANE_Bool g_isCanceled; static SANE_Bool g_bSharpen; static SANE_Bool g_bFirstReadImage; static SANE_Bool g_isScanning; static SANE_Bool g_isSelfGamma; static SANE_Byte g_bScanBits; static SANE_Byte *g_lpReadImageHead; static unsigned short s_wOpticalYDpi[] = { 1200, 600, 300, 150, 75, 0 }; static unsigned short s_wOpticalXDpi[] = { 1200, 600, 300, 150, 75, 0 }; static unsigned short g_X; static unsigned short g_Y; static unsigned short g_Width; static unsigned short g_Height; static unsigned short g_XDpi; static unsigned short g_YDpi; static unsigned short g_SWWidth; static unsigned short g_SWHeight; static unsigned short g_wPixelDistance; /*even & odd sensor problem */ static unsigned short g_wLineDistance; static unsigned short g_wScanLinesPerBlock; static unsigned short g_wReadedLines; static unsigned short g_wReadImageLines; static unsigned short g_wReadyShadingLine; static unsigned short g_wStartShadingLinePos; static unsigned short g_wLineartThreshold; static unsigned int g_wtheReadyLines; static unsigned int g_wMaxScanLines; static unsigned int g_dwScannedTotalLines; static unsigned int g_dwImageBufferSize; static unsigned int g_BytesPerRow; static unsigned int g_SWBytesPerRow; static unsigned int g_dwCalibrationSize; static unsigned int g_dwBufferSize; static unsigned int g_dwTotalTotalXferLines; static unsigned short *g_pGammaTable; static unsigned char *g_pDeviceFile; static pthread_t g_threadid_readimage; /*user define type*/ static COLORMODE g_ScanMode; static TARGETIMAGE g_tiTarget; static SCANTYPE g_ScanType = ST_Reflective; static SCANSOURCE g_ssScanSource; static PIXELFLAVOR g_PixelFlavor; static SUGGESTSETTING g_ssSuggest; static Asic g_chip; static int g_nSecLength, g_nDarkSecLength; static int g_nSecNum, g_nDarkSecNum; static unsigned short g_wCalWidth; static unsigned short g_wDarkCalWidth; static int g_nPowerNum; static unsigned short g_wStartPosition; static pthread_mutex_t g_scannedLinesMutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t g_readyLinesMutex = PTHREAD_MUTEX_INITIALIZER; /*for modify the last point*/ static SANE_Byte * g_lpBefLineImageData = NULL; static SANE_Bool g_bIsFirstReadBefData = TRUE; static unsigned int g_dwAlreadyGetLines = 0; /* forward declarations */ static SANE_Bool MustScanner_Init (void); static SANE_Bool MustScanner_GetScannerState (void); static SANE_Bool MustScanner_PowerControl (SANE_Bool isLampOn, SANE_Bool isTALampOn); static SANE_Bool MustScanner_BackHome (void); static SANE_Bool MustScanner_Prepare (SANE_Byte bScanSource); #ifdef SANE_UNUSED static SANE_Bool MustScanner_AdjustOffset (int nTimes, SANE_Bool * bDirection, SANE_Byte * bOffset, SANE_Byte * bLastMin, SANE_Byte * bLastOffset, unsigned short * wMinValue, SANE_Byte * bOffsetUpperBound, SANE_Byte * bOffsetLowerBound, unsigned short wStdMinLevel, unsigned short wStdMaxLevel); static SANE_Bool MustScanner_SecondAdjustOffset (int nTimes, SANE_Bool * bDirection, SANE_Byte * bOffset, SANE_Byte * bLastMin, SANE_Byte * bLastOffset, unsigned short * wMinValue, SANE_Byte * bOffsetUpperBound, SANE_Byte * bOffsetLowerBound, unsigned short wStdMinLevel, unsigned short wStdMaxLevel); #endif static unsigned short MustScanner_FiltLower (unsigned short * pSort, unsigned short TotalCount, unsigned short LowCount, unsigned short HighCount); static SANE_Bool MustScanner_GetRgb48BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert, unsigned short * wLinesCount); static SANE_Bool MustScanner_GetRgb48BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert, unsigned short * wLinesCount); static SANE_Bool MustScanner_GetRgb24BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert, unsigned short * wLinesCount); static SANE_Bool MustScanner_GetRgb24BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert, unsigned short * wLinesCount); static SANE_Bool MustScanner_GetMono16BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert, unsigned short * wLinesCount); static SANE_Bool MustScanner_GetMono16BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert, unsigned short * wLinesCount); static SANE_Bool MustScanner_GetMono8BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert, unsigned short * wLinesCount); static SANE_Bool MustScanner_GetMono8BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert, unsigned short * wLinesCount); static SANE_Bool MustScanner_GetMono1BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert, unsigned short * wLinesCount); static SANE_Bool MustScanner_GetMono1BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert, unsigned short * wLinesCount); static void *MustScanner_ReadDataFromScanner (void * dummy); static void MustScanner_PrepareCalculateMaxMin (unsigned short wResolution); static void MustScanner_CalculateMaxMin (SANE_Byte * pBuffer, unsigned short * lpMaxValue, unsigned short * lpMinValue, unsigned short wResolution); static SANE_Byte QBET4 (SANE_Byte A, SANE_Byte B); static unsigned int GetScannedLines (void); static unsigned int GetReadyLines (void); static void AddScannedLines (unsigned short wAddLines); static void AddReadyLines (void); static void ModifyLinePoint (SANE_Byte * lpImageData, SANE_Byte * lpImageDataBefore, unsigned int dwBytesPerLine, unsigned int dwLinesCount, unsigned short wPixDistance, unsigned short wModPtCount); #include "mustek_usb2_reflective.c" #include "mustek_usb2_transparent.c" /********************************************************************** Author: Jack Date: 2005/05/13 Routine Description: Parameters: none Return value: if initialize the scanner success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool MustScanner_Init () { DBG (DBG_FUNC, "MustScanner_Init: Call in\n"); g_chip.firmwarestate = FS_NULL; if (SANE_STATUS_GOOD != Asic_Open (&g_chip, g_pDeviceFile)) { DBG (DBG_FUNC, "MustScanner_Init: Asic_Open return error\n"); return FALSE; } if (SANE_STATUS_GOOD != Asic_Initialize (&g_chip)) { DBG (DBG_FUNC, "MustScanner_Init: Asic_Initialize return error\n"); return FALSE; } g_dwImageBufferSize = 24L * 1024L * 1024L; g_dwBufferSize = 64L * 1024L; g_dwCalibrationSize = 64L * 1024L; g_lpReadImageHead = NULL; g_isCanceled = FALSE; g_bFirstReadImage = TRUE; g_bOpened = FALSE; g_bPrepared = FALSE; g_bSharpen = FALSE; g_isScanning = FALSE; g_isSelfGamma = FALSE; g_pGammaTable = NULL; if (NULL != g_pDeviceFile) { free (g_pDeviceFile); g_pDeviceFile = NULL; } g_ssScanSource = SS_Reflective; g_PixelFlavor = PF_BlackIs0; Asic_Close (&g_chip); DBG (DBG_FUNC, "MustScanner_Init: leave MustScanner_Init\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/13 Routine Description: check the scanner connect status Parameters: none Return value: if scanner's status is OK return TRUE else return FASLE ***********************************************************************/ static SANE_Bool MustScanner_GetScannerState () { if (SANE_STATUS_GOOD != Asic_Open (&g_chip, g_pDeviceFile)) { DBG (DBG_FUNC, "MustScanner_GetScannerState: Asic_Open return error\n"); return FALSE; } else { Asic_Close (&g_chip); return TRUE; } } /********************************************************************** Author: Jack Date: 2005/05/13 Routine Description: Turn the lamp on or off Parameters: isLampOn: turn the lamp on or off isTALampOn: turn the TA lamp on or off Return value: if operation success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool MustScanner_PowerControl (SANE_Bool isLampOn, SANE_Bool isTALampOn) { SANE_Bool hasTA; DBG (DBG_FUNC, "MustScanner_PowerControl: Call in\n"); if (SANE_STATUS_GOOD != Asic_Open (&g_chip, g_pDeviceFile)) { DBG (DBG_FUNC, "MustScanner_PowerControl: Asic_Open return error\n"); return FALSE; } if (SANE_STATUS_GOOD != Asic_TurnLamp (&g_chip, isLampOn)) { DBG (DBG_FUNC, "MustScanner_PowerControl: Asic_TurnLamp return error\n"); return FALSE; } if (SANE_STATUS_GOOD != Asic_IsTAConnected (&g_chip, &hasTA)) { DBG (DBG_FUNC, "MustScanner_PowerControl: Asic_IsTAConnected return error\n"); return FALSE; } if (hasTA) { if (SANE_STATUS_GOOD != Asic_TurnTA (&g_chip, isTALampOn)) { DBG (DBG_FUNC, "MustScanner_PowerControl: Asic_TurnTA return error\n"); return FALSE; } } Asic_Close (&g_chip); DBG (DBG_FUNC, "MustScanner_PowerControl: leave MustScanner_PowerControl\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/13 Routine Description: Turn the carriage home Parameters: none Return value: if the operation success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool MustScanner_BackHome () { DBG (DBG_FUNC, "MustScanner_BackHome: call in \n"); if (SANE_STATUS_GOOD != Asic_Open (&g_chip, g_pDeviceFile)) { DBG (DBG_FUNC, "MustScanner_BackHome: Asic_Open return error\n"); return FALSE; } if (SANE_STATUS_GOOD != Asic_CarriageHome (&g_chip, FALSE)) { DBG (DBG_FUNC, "MustScanner_BackHome: Asic_CarriageHome return error\n"); return FALSE; } if (SANE_STATUS_GOOD != Asic_WaitUnitReady (&g_chip)) { DBG (DBG_FUNC, "MustScanner_BackHome: Asic_WaitUnitReady return error\n"); return FALSE; } Asic_Close (&g_chip); DBG (DBG_FUNC, "MustScanner_BackHome: leave MustScanner_BackHome\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/13 Routine Description: prepare the scan image Parameters: bScanSource: the scan source Return value: if operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool MustScanner_Prepare (SANE_Byte bScanSource) { DBG (DBG_FUNC, "MustScanner_Prepare: call in\n"); if (SANE_STATUS_GOOD != Asic_Open (&g_chip, g_pDeviceFile)) { DBG (DBG_FUNC, "MustScanner_Prepare: Asic_Open return error\n"); return FALSE; } if (SANE_STATUS_GOOD != Asic_WaitUnitReady (&g_chip)) { DBG (DBG_FUNC, "MustScanner_Prepare: Asic_WaitUnitReady return error\n"); return FALSE; } if (SS_Reflective == bScanSource) { DBG (DBG_FUNC, "MustScanner_Prepare:ScanSource is SS_Reflective\n"); if (SANE_STATUS_GOOD != Asic_TurnLamp (&g_chip, TRUE)) { DBG (DBG_FUNC, "MustScanner_Prepare: Asic_TurnLamp return error\n"); return FALSE; } if (SANE_STATUS_GOOD != Asic_SetSource (&g_chip, LS_REFLECTIVE)) { DBG (DBG_FUNC, "MustScanner_Prepare: Asic_SetSource return error\n"); return FALSE; } } else if (SS_Positive == bScanSource) { DBG (DBG_FUNC, "MustScanner_Prepare:ScanSource is SS_Positive\n"); if (SANE_STATUS_GOOD != Asic_TurnTA (&g_chip, TRUE)) { DBG (DBG_FUNC, "MustScanner_Prepare: Asic_TurnTA return error\n"); return FALSE; } if (SANE_STATUS_GOOD != Asic_SetSource (&g_chip, LS_POSITIVE)) { DBG (DBG_FUNC, "MustScanner_Prepare: Asic_SetSource return error\n"); return FALSE; } } else if (SS_Negative == bScanSource) { DBG (DBG_FUNC, "MustScanner_Prepare:ScanSource is SS_Negative\n"); if (SANE_STATUS_GOOD != Asic_TurnTA (&g_chip, TRUE)) { DBG (DBG_FUNC, "MustScanner_Prepare: Asic_TurnTA return error\n"); return FALSE; } if (SANE_STATUS_GOOD != Asic_SetSource (&g_chip, LS_NEGATIVE)) { DBG (DBG_FUNC, "MustScanner_Prepare: Asic_SetSource return error\n"); return FALSE; } DBG (DBG_FUNC, "MustScanner_Prepare: Asic_SetSource return good\n"); } Asic_Close (&g_chip); g_bPrepared = TRUE; DBG (DBG_FUNC, "MustScanner_Prepare: leave MustScanner_Prepare\n"); return TRUE; } #ifdef SANE_UNUSED /********************************************************************** Author: Jack Date: 2005/05/15 Routine Description: Adjuest the offset Parameters: nTimes: Adjuest offset the times bDirection: whether direction bOffset: the data of offset bLastMin: the last min data bLastOffset: the last offset data wMinValue: the min value of offset bOffsetUpperBound: the upper bound of offset bOffsetLowerBound: the lower bound of offset wStdMinLevel: the min level of offset wStdMaxLevel: the max level of offset Return value: if the operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool MustScanner_AdjustOffset (int nTimes, SANE_Bool * bDirection, SANE_Byte * bOffset, SANE_Byte * bLastMin, SANE_Byte * bLastOffset, unsigned short * wMinValue, SANE_Byte * bOffsetUpperBound, SANE_Byte * bOffsetLowerBound, unsigned short wStdMinLevel, unsigned short wStdMaxLevel) { if ((*wMinValue <= wStdMaxLevel) && (*wMinValue >= wStdMinLevel)) { return TRUE; } if (nTimes == 0) { *bLastMin = LOSANE_Byte (*wMinValue); *bLastOffset = *bOffset; if (*wMinValue == 255) { *bOffset = 0; } else { *bOffset = 255; } } if (nTimes == 1) { if (*wMinValue > *bLastMin) { if (*wMinValue > wStdMaxLevel && *bLastMin > wStdMaxLevel) { *bDirection = !(*bDirection); return TRUE; } if (*wMinValue < wStdMinLevel && *bLastMin < wStdMinLevel) return TRUE; } if (*wMinValue < *bLastMin) { if (*wMinValue < wStdMinLevel && *bLastMin < wStdMinLevel) *bDirection = !(*bDirection); if (*wMinValue > wStdMaxLevel && *bLastMin > wStdMaxLevel) return TRUE; } } if (nTimes > 1) { if (*wMinValue > *bLastMin) { SANE_Byte bTemp; bTemp = *bOffset; *bOffset = (*bOffsetUpperBound + *bOffsetLowerBound) / 2; if (nTimes > 2) { if (*wMinValue > wStdMaxLevel) if (bDirection) *bOffsetLowerBound = bTemp; else *bOffsetUpperBound = bTemp; else if (bDirection) *bOffsetUpperBound = bTemp; else *bOffsetLowerBound = bTemp; } *bLastOffset = bTemp; *bLastMin = (SANE_Byte) * wMinValue; } else { SANE_Byte bTemp; bTemp = *bOffset; *bOffset = (*bOffsetUpperBound + *bOffsetLowerBound) / 2; if (nTimes > 2) { if (*wMinValue == *bLastMin) { if (*wMinValue > wStdMaxLevel) { if (!bDirection) *bOffsetUpperBound = bTemp; else *bOffsetLowerBound = bTemp; } else { if (!bDirection) *bOffsetLowerBound = bTemp; else *bOffsetUpperBound = bTemp; } } else { if (*wMinValue > wStdMaxLevel) { if (bDirection) *bOffsetUpperBound = bTemp; else *bOffsetLowerBound = bTemp; } else { if (bDirection) *bOffsetLowerBound = bTemp; else *bOffsetUpperBound = bTemp; } } } *bLastOffset = bTemp; *bLastMin = (SANE_Byte) * wMinValue; } } /* end of if(nTimes > 1) */ return TRUE; } #endif #ifdef SANE_UNUSED /********************************************************************** Author: Jack Date: 2005/05/15 Routine Description: Adjuest the offset second times Parameters: nTimes: Adjuest offset the times bDirection: whether direction bOffset: the data of offset bLastMin: the last min data bLastOffset: the last offset data wMinValue: the min value of offset bOffsetUpperBound: the upper bound of offset bOffsetLowerBound: the lower bound of offset wStdMinLevel: the min level of offset wStdMaxLevel: the max level of offset Return value: if the operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool MustScanner_SecondAdjustOffset (int nTimes, SANE_Bool * bDirection, SANE_Byte * bOffset, SANE_Byte * bLastMin, SANE_Byte * bLastOffset, unsigned short * wMinValue, SANE_Byte * bOffsetUpperBound, SANE_Byte * bOffsetLowerBound, unsigned short wStdMinLevel, unsigned short wStdMaxLevel) { if ((*wMinValue <= wStdMaxLevel) && (*wMinValue >= wStdMinLevel)) { return TRUE; } if (nTimes == 0) { *bLastMin = LOSANE_Byte (*wMinValue); *bLastOffset = *bOffset; if (*bDirection == 0) { *bOffsetUpperBound = *bLastOffset; *bOffsetLowerBound = 0; *bOffset = 0; } else { *bOffsetUpperBound = 255; *bOffsetLowerBound = *bLastOffset; *bOffset = 255; } } if (nTimes >= 1) { if (*wMinValue > wStdMaxLevel) { if (*wMinValue > *bLastMin) { if (*bDirection == 0) { *bOffsetUpperBound = *bOffset; } else { *bOffsetLowerBound = *bOffset; } *bOffset = (*bOffsetUpperBound + *bOffsetLowerBound) / 2; } else { if (*bDirection == 1) { *bOffsetUpperBound = *bOffset; *bOffset = (*bOffsetUpperBound + *bOffsetLowerBound) / 2; } else { *bOffsetLowerBound = *bOffset; *bOffset = (*bOffsetUpperBound + *bOffsetLowerBound) / 2; } } } /*end of if(*wMinValue > MAX_OFFSET) */ if (*wMinValue < wStdMinLevel) { if (*wMinValue > *bLastMin) { if (*bDirection == 0) { *bOffsetLowerBound = *bOffset; } else { *bOffsetUpperBound = *bOffset; } *bOffset = (*bOffsetUpperBound + *bOffsetLowerBound) / 2; } else { if (*bDirection == 1) { *bOffsetUpperBound = *bOffset; *bOffset = (*bOffsetUpperBound + *bOffsetLowerBound) / 2; } else { *bOffsetLowerBound = *bOffset; *bOffset = (*bOffsetUpperBound + *bOffsetLowerBound) / 2; } } } /*end of if(*wMinValue > MIN_OFFSET) */ *bLastMin = (SANE_Byte) * wMinValue; } /*end of if(nTimes >= 1) */ /* HOLD: missing return value! Correct? */ return FALSE; } #endif /********************************************************************** Author: Jack Date: 2005/05/15 Routine Description: Filter the data Parameters: pSort: the sort data TotalCount: the total count LowCount: the low count HighCount: the upper count Return value: the data of Filter ***********************************************************************/ static unsigned short MustScanner_FiltLower (unsigned short * pSort, unsigned short TotalCount, unsigned short LowCount, unsigned short HighCount) { unsigned short Bound = TotalCount - 1; unsigned short LeftCount = HighCount - LowCount; int Temp = 0; unsigned int Sum = 0; unsigned short i, j; for (i = 0; i < Bound; i++) { for (j = 0; j < Bound - i; j++) { if (pSort[j + 1] > pSort[j]) { Temp = pSort[j]; pSort[j] = pSort[j + 1]; pSort[j + 1] = Temp; } } } for (i = 0; i < LeftCount; i++) Sum += pSort[i + LowCount]; return (unsigned short) (Sum / LeftCount); } /********************************************************************** Author: Jack Date: 2005/05/15 Routine Description: Repair line when single CCD and color is 48bit Parameters: lpLine: point to image be repaired isOrderInvert: RGB or BGR wLinesCount: how many line be repaired Return value: if the operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool MustScanner_GetRgb48BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert, unsigned short * wLinesCount) { unsigned short wWantedTotalLines; unsigned short TotalXferLines; unsigned short wRLinePos = 0; unsigned short wGLinePos = 0; unsigned short wBLinePos = 0; unsigned short wRTempData; unsigned short wGTempData; unsigned short wBTempData; unsigned short i; DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine: call in \n"); g_isCanceled = FALSE; g_isScanning = TRUE; wWantedTotalLines = *wLinesCount; TotalXferLines = 0; if (g_bFirstReadImage) { pthread_create (&g_threadid_readimage, NULL, MustScanner_ReadDataFromScanner, NULL); DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine: thread create\n"); g_bFirstReadImage = FALSE; } if (!isOrderInvert) { for (; TotalXferLines < wWantedTotalLines;) { if (g_dwTotalTotalXferLines >= g_SWHeight) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine: thread exit\n"); *wLinesCount = TotalXferLines; g_isScanning = FALSE; return TRUE; } if (GetScannedLines () > g_wtheReadyLines) { wRLinePos = g_wtheReadyLines % g_wMaxScanLines; wGLinePos = (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines; wBLinePos = (g_wtheReadyLines - g_wLineDistance * 2) % g_wMaxScanLines; for (i = 0; i < g_SWWidth; i++) { wRTempData = *(g_lpReadImageHead + wRLinePos * g_BytesPerRow + i * 6 + 0); wRTempData += *(g_lpReadImageHead + wRLinePos * g_BytesPerRow + i * 6 + 1) << 8; wGTempData = *(g_lpReadImageHead + wGLinePos * g_BytesPerRow + i * 6 + 2); wGTempData += *(g_lpReadImageHead + wGLinePos * g_BytesPerRow + i * 6 + 3) << 8; wBTempData = *(g_lpReadImageHead + wBLinePos * g_BytesPerRow + i * 6 + 4); wBTempData += *(g_lpReadImageHead + wBLinePos * g_BytesPerRow + i * 6 + 5) << 8; *(lpLine + i * 6 + 0) = LOBYTE (g_pGammaTable[wRTempData]); *(lpLine + i * 6 + 1) = HIBYTE (g_pGammaTable[wRTempData]); *(lpLine + i * 6 + 2) = LOBYTE (g_pGammaTable[wGTempData + 65536]); *(lpLine + i * 6 + 3) = HIBYTE (g_pGammaTable[wGTempData + 65536]); *(lpLine + i * 6 + 4) = LOBYTE (g_pGammaTable[wBTempData + 131072]); *(lpLine + i * 6 + 5) = HIBYTE (g_pGammaTable[wBTempData + 131072]); } TotalXferLines++; g_dwTotalTotalXferLines++; lpLine += g_SWBytesPerRow; AddReadyLines (); } if (g_isCanceled) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine: thread exit\n"); break; } } } else { for (; TotalXferLines < wWantedTotalLines;) { if (g_dwTotalTotalXferLines >= g_SWHeight) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine: thread exit\n"); *wLinesCount = TotalXferLines; g_isScanning = FALSE; return TRUE; } if (GetScannedLines () > g_wtheReadyLines) { wRLinePos = g_wtheReadyLines % g_wMaxScanLines; wGLinePos = (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines; wBLinePos = (g_wtheReadyLines - g_wLineDistance * 2) % g_wMaxScanLines; for (i = 0; i < g_SWWidth; i++) { wRTempData = *(g_lpReadImageHead + wRLinePos * g_BytesPerRow + i * 6 + 0); wRTempData += *(g_lpReadImageHead + wRLinePos * g_BytesPerRow + i * 6 + 1) << 8; wGTempData = *(g_lpReadImageHead + wGLinePos * g_BytesPerRow + i * 6 + 2); wGTempData += *(g_lpReadImageHead + wGLinePos * g_BytesPerRow + i * 6 + 3) << 8; wBTempData = *(g_lpReadImageHead + wBLinePos * g_BytesPerRow + i * 6 + 4); wBTempData += *(g_lpReadImageHead + wBLinePos * g_BytesPerRow + i * 6 + 5) << 8; *(lpLine + i * 6 + 4) = LOBYTE (g_pGammaTable[wRTempData]); *(lpLine + i * 6 + 5) = HIBYTE (g_pGammaTable[wRTempData]); *(lpLine + i * 6 + 2) = LOBYTE (g_pGammaTable[wGTempData + 65536]); *(lpLine + i * 6 + 3) = HIBYTE (g_pGammaTable[wGTempData + 65536]); *(lpLine + i * 6 + 0) = LOBYTE (g_pGammaTable[wBTempData + 131072]); *(lpLine + i * 6 + 1) = HIBYTE (g_pGammaTable[wBTempData + 131072]); } TotalXferLines++; g_dwTotalTotalXferLines++; lpLine += g_SWBytesPerRow; AddReadyLines (); } if (g_isCanceled) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine: thread exit\n"); break; } } /*end for */ } *wLinesCount = TotalXferLines; g_isScanning = FALSE; DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine: leave MustScanner_GetRgb48BitLine\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/15 Routine Description: Repair line when double CCD and color is 48bit Parameters: lpLine: point to image be repaired isOrderInvert: RGB or BGR wLinesCount: how many line be repaired Return value: if the operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool MustScanner_GetRgb48BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert, unsigned short * wLinesCount) { unsigned short wWantedTotalLines; unsigned short TotalXferLines; unsigned short wRLinePosOdd = 0; unsigned short wGLinePosOdd = 0; unsigned short wBLinePosOdd = 0; unsigned short wRLinePosEven = 0; unsigned short wGLinePosEven = 0; unsigned short wBLinePosEven = 0; unsigned int wRTempData; unsigned int wGTempData; unsigned int wBTempData; unsigned int wNextTempData; unsigned short i; DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine1200DPI: call in \n"); TotalXferLines = 0; wWantedTotalLines = *wLinesCount; g_isCanceled = FALSE; g_isScanning = TRUE; if (g_bFirstReadImage) { pthread_create (&g_threadid_readimage, NULL, MustScanner_ReadDataFromScanner, NULL); DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine1200DPI: thread create\n"); g_bFirstReadImage = FALSE; } if (!isOrderInvert) { for (; TotalXferLines < wWantedTotalLines;) { if (g_dwTotalTotalXferLines >= g_SWHeight) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine1200DPI: thread exit\n"); *wLinesCount = TotalXferLines; g_isScanning = FALSE; return TRUE; } if (GetScannedLines () > g_wtheReadyLines) { if (ST_Reflective == g_ScanType) { wRLinePosOdd = (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines; wGLinePosOdd = (g_wtheReadyLines - g_wLineDistance - g_wPixelDistance) % g_wMaxScanLines; wBLinePosOdd = (g_wtheReadyLines - g_wLineDistance * 2 - g_wPixelDistance) % g_wMaxScanLines; wRLinePosEven = (g_wtheReadyLines) % g_wMaxScanLines; wGLinePosEven = (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines; wBLinePosEven = (g_wtheReadyLines - g_wLineDistance * 2) % g_wMaxScanLines; } else { wRLinePosEven = (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines; wGLinePosEven = (g_wtheReadyLines - g_wLineDistance - g_wPixelDistance) % g_wMaxScanLines; wBLinePosEven = (g_wtheReadyLines - g_wLineDistance * 2 - g_wPixelDistance) % g_wMaxScanLines; wRLinePosOdd = (g_wtheReadyLines) % g_wMaxScanLines; wGLinePosOdd = (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines; wBLinePosOdd = (g_wtheReadyLines - g_wLineDistance * 2) % g_wMaxScanLines; } for (i = 0; i < g_SWWidth;) { if (i + 1 != g_SWWidth) { wRTempData = *(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow + i * 6 + 0); wRTempData += *(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow + i * 6 + 1) << 8; wNextTempData = *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow + (i + 1) * 6 + 0); wNextTempData += *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow + (i + 1) * 6 + 1) << 8; wRTempData = (wRTempData + wNextTempData) >> 1; wGTempData = *(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow + i * 6 + 2); wGTempData += *(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow + i * 6 + 3) << 8; wNextTempData = *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow + (i + 1) * 6 + 2); wNextTempData += *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow + (i + 1) * 6 + 3) << 8; wGTempData = (wGTempData + wNextTempData) >> 1; wBTempData = *(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow + i * 6 + 4); wBTempData += *(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow + i * 6 + 5) << 8; wNextTempData = *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow + (i + 1) * 6 + 4); wNextTempData += *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow + (i + 1) * 6 + 5) << 8; wBTempData = (wBTempData + wNextTempData) >> 1; *(lpLine + i * 6 + 0) = LOBYTE (g_pGammaTable[wRTempData]); *(lpLine + i * 6 + 1) = HIBYTE (g_pGammaTable[wRTempData]); *(lpLine + i * 6 + 2) = LOBYTE (g_pGammaTable[wGTempData + 65536]); *(lpLine + i * 6 + 3) = HIBYTE (g_pGammaTable[wGTempData + 65536]); *(lpLine + i * 6 + 4) = LOBYTE (g_pGammaTable[wBTempData + 131072]); *(lpLine + i * 6 + 5) = HIBYTE (g_pGammaTable[wBTempData + 131072]); i++; if (i >= g_SWWidth) { break; } wRTempData = *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow + i * 6 + 0); wRTempData += *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow + i * 6 + 1) << 8; wNextTempData = *(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow + (i + 1) * 6 + 0); wNextTempData += *(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow + (i + 1) * 6 + 1) << 8; wRTempData = (wRTempData + wNextTempData) >> 1; wGTempData = *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow + i * 6 + 2); wGTempData += *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow + i * 6 + 3) << 8; wNextTempData = *(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow + (i + 1) * 6 + 2); wNextTempData += *(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow + (i + 1) * 6 + 3) << 8; wGTempData = (wGTempData + wNextTempData) >> 1; wBTempData = *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow + i * 6 + 4); wBTempData += *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow + i * 6 + 5) << 8; wNextTempData = *(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow + (i + 1) * 6 + 4); wNextTempData += *(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow + (i + 1) * 6 + 5) << 8; wBTempData = (wBTempData + wNextTempData) >> 1; *(lpLine + i * 6 + 0) = LOBYTE (g_pGammaTable[wRTempData]); *(lpLine + i * 6 + 1) = HIBYTE (g_pGammaTable[wRTempData]); *(lpLine + i * 6 + 2) = LOBYTE (g_pGammaTable[wGTempData + 65536]); *(lpLine + i * 6 + 3) = HIBYTE (g_pGammaTable[wGTempData + 65536]); *(lpLine + i * 6 + 4) = LOBYTE (g_pGammaTable[wBTempData + 131072]); *(lpLine + i * 6 + 5) = HIBYTE (g_pGammaTable[wBTempData + 131072]); i++; } } TotalXferLines++; g_dwTotalTotalXferLines++; lpLine += g_SWBytesPerRow; AddReadyLines (); } if (g_isCanceled) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine1200DPI: thread exit\n"); break; } } } else { for (; TotalXferLines < wWantedTotalLines;) { if (g_dwTotalTotalXferLines >= g_SWHeight) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine1200DPI: thread exit\n"); *wLinesCount = TotalXferLines; g_isScanning = FALSE; return TRUE; } if (GetScannedLines () > g_wtheReadyLines) { if (ST_Reflective == g_ScanType) { wRLinePosOdd = (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines; wGLinePosOdd = (g_wtheReadyLines - g_wLineDistance - g_wPixelDistance) % g_wMaxScanLines; wBLinePosOdd = (g_wtheReadyLines - g_wLineDistance * 2 - g_wPixelDistance) % g_wMaxScanLines; wRLinePosEven = (g_wtheReadyLines) % g_wMaxScanLines; wGLinePosEven = (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines; wBLinePosEven = (g_wtheReadyLines - g_wLineDistance * 2) % g_wMaxScanLines; } else { wRLinePosEven = (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines; wGLinePosEven = (g_wtheReadyLines - g_wLineDistance - g_wPixelDistance) % g_wMaxScanLines; wBLinePosEven = (g_wtheReadyLines - g_wLineDistance * 2 - g_wPixelDistance) % g_wMaxScanLines; wRLinePosOdd = (g_wtheReadyLines) % g_wMaxScanLines; wGLinePosOdd = (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines; wBLinePosOdd = (g_wtheReadyLines - g_wLineDistance * 2) % g_wMaxScanLines; } for (i = 0; i < g_SWWidth;) { if ((i + 1) != g_SWWidth) { wRTempData = *(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow + i * 6 + 0); wRTempData += *(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow + i * 6 + 1) << 8; wNextTempData = *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow + (i + 1) * 6 + 0); wNextTempData += *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow + (i + 1) * 6 + 1) << 8; wRTempData = (wRTempData + wNextTempData) >> 1; wGTempData = *(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow + i * 6 + 2); wGTempData += *(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow + i * 6 + 3) << 8; wNextTempData = *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow + (i + 1) * 6 + 2); wNextTempData += *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow + (i + 1) * 6 + 3) << 8; wGTempData = (wGTempData + wNextTempData) >> 1; wBTempData = *(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow + i * 6 + 4); wBTempData += *(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow + i * 6 + 5) << 8; wNextTempData = *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow + (i + 1) * 6 + 4); wNextTempData += *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow + (i + 1) * 6 + 5) << 8; wBTempData = (wBTempData + wNextTempData) >> 1; *(lpLine + i * 6 + 4) = LOBYTE (g_pGammaTable[wRTempData]); *(lpLine + i * 6 + 5) = HIBYTE (g_pGammaTable[wRTempData]); *(lpLine + i * 6 + 2) = LOBYTE (g_pGammaTable[wGTempData + 65536]); *(lpLine + i * 6 + 3) = HIBYTE (g_pGammaTable[wGTempData + 65536]); *(lpLine + i * 6 + 0) = LOBYTE (g_pGammaTable[wBTempData + 131072]); *(lpLine + i * 6 + 1) = HIBYTE (g_pGammaTable[wBTempData + 131072]); i++; if (i >= g_SWWidth) { break; } wRTempData = *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow + i * 6 + 0); wRTempData += *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow + i * 6 + 1) << 8; wNextTempData = *(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow + (i + 1) * 6 + 0); wNextTempData += *(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow + (i + 1) * 6 + 1) << 8; wRTempData = (wRTempData + wNextTempData) >> 1; wGTempData = *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow + i * 6 + 2); wGTempData += *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow + i * 6 + 3) << 8; wNextTempData = *(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow + (i + 1) * 6 + 2); wNextTempData += *(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow + (i + 1) * 6 + 3) << 8; wGTempData = (wGTempData + wNextTempData) >> 1; wBTempData = *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow + i * 6 + 4); wBTempData += *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow + i * 6 + 5) << 8; wNextTempData = *(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow + (i + 1) * 6 + 4); wNextTempData += *(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow + (i + 1) * 6 + 5) << 8; wBTempData = (wBTempData + wNextTempData) >> 1; *(lpLine + i * 6 + 4) = LOBYTE (g_pGammaTable[wRTempData]); *(lpLine + i * 6 + 5) = HIBYTE (g_pGammaTable[wRTempData]); *(lpLine + i * 6 + 2) = LOBYTE (g_pGammaTable[wGTempData + 65536]); *(lpLine + i * 6 + 3) = HIBYTE (g_pGammaTable[wGTempData + 65536]); *(lpLine + i * 6 + 0) = LOBYTE (g_pGammaTable[wBTempData + 131072]); *(lpLine + i * 6 + 1) = HIBYTE (g_pGammaTable[wBTempData + 131072]); i++; } } TotalXferLines++; g_dwTotalTotalXferLines++; lpLine += g_SWBytesPerRow; AddReadyLines (); } if (g_isCanceled) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine1200DPI: thread exit\n"); break; } } } *wLinesCount = TotalXferLines; g_isScanning = FALSE; DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine1200DPI: leave MustScanner_GetRgb48BitLine1200DPI\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/15 Routine Description: Repair line when single CCD and color is 24bit Parameters: lpLine: point to image be repaired isOrderInvert: RGB or BGR wLinesCount: how many line be repaired Return value: if the operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool MustScanner_GetRgb24BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert, unsigned short * wLinesCount) { unsigned short wWantedTotalLines; unsigned short TotalXferLines; unsigned short wRLinePos = 0; unsigned short wGLinePos = 0; unsigned short wBLinePos = 0; SANE_Byte byRed; SANE_Byte byGreen; SANE_Byte byBlue; SANE_Byte bNextPixel = 0; unsigned short i; unsigned short tempR, tempG, tempB; DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: call in\n"); g_isCanceled = FALSE; g_isScanning = TRUE; wWantedTotalLines = *wLinesCount; DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: get wWantedTotalLines= %d\n", wWantedTotalLines); TotalXferLines = 0; if (g_bFirstReadImage) { pthread_create (&g_threadid_readimage, NULL, MustScanner_ReadDataFromScanner, NULL); DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: thread create\n"); g_bFirstReadImage = FALSE; } if (!isOrderInvert) { DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: !isOrderInvert\n"); for (; TotalXferLines < wWantedTotalLines;) { if (g_dwTotalTotalXferLines >= g_SWHeight) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: thread exit\n"); *wLinesCount = TotalXferLines; g_isScanning = FALSE; return TRUE; } if (GetScannedLines () > g_wtheReadyLines) { wRLinePos = g_wtheReadyLines % g_wMaxScanLines; wGLinePos = (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines; wBLinePos = (g_wtheReadyLines - g_wLineDistance * 2) % g_wMaxScanLines; for (i = 0; i < g_SWWidth; i++) { byRed = *(g_lpReadImageHead + wRLinePos * g_BytesPerRow + i * 3 + 0); bNextPixel = *(g_lpReadImageHead + wRLinePos * g_BytesPerRow + (i + 1) * 3 + 0); byRed = (byRed + bNextPixel) >> 1; byGreen = *(g_lpReadImageHead + wGLinePos * g_BytesPerRow + i * 3 + 1); bNextPixel = *(g_lpReadImageHead + wGLinePos * g_BytesPerRow + (i + 1) * 3 + 1); byGreen = (byGreen + bNextPixel) >> 1; byBlue = *(g_lpReadImageHead + wBLinePos * g_BytesPerRow + i * 3 + 2); bNextPixel = *(g_lpReadImageHead + wBLinePos * g_BytesPerRow + (i + 1) * 3 + 2); byBlue = (byBlue + bNextPixel) >> 1; #ifdef ENABLE_GAMMA tempR = (unsigned short) ((byRed << 4) | QBET4 (byBlue, byGreen)); tempG = (unsigned short) ((byGreen << 4) | QBET4 (byRed, byBlue)); tempB = (unsigned short) ((byBlue << 4) | QBET4 (byGreen, byRed)); *(lpLine + i * 3 + 0) = (unsigned char) (*(g_pGammaTable + tempR)); *(lpLine + i * 3 + 1) = (unsigned char) (*(g_pGammaTable + 4096 + tempG)); *(lpLine + i * 3 + 2) = (unsigned char) (*(g_pGammaTable + 8192 + tempB)); #else *(lpLine + i * 3 + 0) = (unsigned char) byRed; *(lpLine + i * 3 + 1) = (unsigned char) byGreen; *(lpLine + i * 3 + 2) = (unsigned char) byBlue; #endif } TotalXferLines++; g_dwTotalTotalXferLines++; lpLine += g_SWBytesPerRow; AddReadyLines (); DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: g_dwTotalTotalXferLines=%d,g_SWHeight=%d\n", g_dwTotalTotalXferLines, g_SWHeight); DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: g_SWBytesPerRow=%d\n", g_SWBytesPerRow); } if (g_isCanceled) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: thread exit\n"); break; } } } else { DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: isOrderInvert is TRUE\n"); for (; TotalXferLines < wWantedTotalLines;) { if (g_dwTotalTotalXferLines >= g_SWHeight) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: thread exit\n"); *wLinesCount = TotalXferLines; g_isScanning = FALSE; return TRUE; } if (GetScannedLines () > g_wtheReadyLines) { wRLinePos = g_wtheReadyLines % g_wMaxScanLines; wGLinePos = (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines; wBLinePos = (g_wtheReadyLines - g_wLineDistance * 2) % g_wMaxScanLines; for (i = 0; i < g_SWWidth; i++) { DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: before byRed\n"); byRed = *(g_lpReadImageHead + wRLinePos * g_BytesPerRow + i * 3 + 0); bNextPixel = *(g_lpReadImageHead + wRLinePos * g_BytesPerRow + (i + 1) * 3 + 0); /*R-channel */ byRed = (byRed + bNextPixel) >> 1; DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: before byGreen\n"); byGreen = *(g_lpReadImageHead + wGLinePos * g_BytesPerRow + i * 3 + 1); bNextPixel = *(g_lpReadImageHead + wGLinePos * g_BytesPerRow + (i + 1) * 3 + 1); /*G-channel */ byGreen = (byGreen + bNextPixel) >> 1; DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: before byBlue\n"); byBlue = *(g_lpReadImageHead + wBLinePos * g_BytesPerRow + i * 3 + 2); bNextPixel = *(g_lpReadImageHead + wBLinePos * g_BytesPerRow + (i + 1) * 3 + 2); /*B-channel */ byBlue = (byBlue + bNextPixel) >> 1; DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: before set lpLine\n"); DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: i=%d\n", i); #ifdef ENABLE_GAMMA *(lpLine + i * 3 + 2) = (unsigned char) (*(g_pGammaTable + (unsigned short) ((byRed << 4) | QBET4 (byBlue, byGreen)))); *(lpLine + i * 3 + 1) = (unsigned char) (*(g_pGammaTable + 4096 + (unsigned short) ((byGreen << 4) | QBET4 (byRed, byBlue)))); *(lpLine + i * 3 + 0) = (unsigned char) (*(g_pGammaTable + 8192 + (unsigned short) ((byBlue << 4) | QBET4 (byGreen, byRed)))); #else *(lpLine + i * 3 + 2) = (unsigned char) byRed; *(lpLine + i * 3 + 1) = (unsigned char) byGreen; *(lpLine + i * 3 + 0) = (unsigned char) byBlue; #endif } TotalXferLines++; g_dwTotalTotalXferLines++; lpLine += g_SWBytesPerRow; AddReadyLines (); DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: g_dwTotalTotalXferLines=%d,g_SWHeight=%d\n", g_dwTotalTotalXferLines, g_SWHeight); DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: g_SWBytesPerRow=%d\n", g_SWBytesPerRow); } if (g_isCanceled) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: thread exit\n"); break; } } /*end for */ } *wLinesCount = TotalXferLines; g_isScanning = FALSE; DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: leave MustScanner_GetRgb24BitLine\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/15 Routine Description: Repair line when double CCD and color is 24bit Parameters: lpLine: point to image be repaired isOrderInvert: RGB or BGR wLinesCount: how many line be repaired Return value: if the operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool MustScanner_GetRgb24BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert, unsigned short * wLinesCount) { unsigned short wWantedTotalLines; unsigned short TotalXferLines; unsigned short wRLinePosOdd = 0; unsigned short wGLinePosOdd = 0; unsigned short wBLinePosOdd = 0; unsigned short wRLinePosEven = 0; unsigned short wGLinePosEven = 0; unsigned short wBLinePosEven = 0; SANE_Byte byRed; SANE_Byte byGreen; SANE_Byte byBlue; SANE_Byte bNextPixel = 0; unsigned short i; DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine1200DPI: call in\n"); g_isCanceled = FALSE; g_isScanning = TRUE; TotalXferLines = 0; wWantedTotalLines = *wLinesCount; if (g_bFirstReadImage) { pthread_create (&g_threadid_readimage, NULL, MustScanner_ReadDataFromScanner, NULL); DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine1200DPI: thread create\n"); g_bFirstReadImage = FALSE; } if (!isOrderInvert) { for (; TotalXferLines < wWantedTotalLines;) { if (g_dwTotalTotalXferLines >= g_SWHeight) { DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine1200DPI: g_dwTotalTotalXferLines=%d\n", g_dwTotalTotalXferLines); DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine1200DPI: g_Height=%d\n", g_Height); pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine1200DPI: thread exit\n"); *wLinesCount = TotalXferLines; g_isScanning = FALSE; return TRUE; } if (GetScannedLines () > g_wtheReadyLines) { if (ST_Reflective == g_ScanType) { wRLinePosOdd = (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines; wGLinePosOdd = (g_wtheReadyLines - g_wLineDistance - g_wPixelDistance) % g_wMaxScanLines; wBLinePosOdd = (g_wtheReadyLines - g_wLineDistance * 2 - g_wPixelDistance) % g_wMaxScanLines; wRLinePosEven = (g_wtheReadyLines) % g_wMaxScanLines; wGLinePosEven = (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines; wBLinePosEven = (g_wtheReadyLines - g_wLineDistance * 2) % g_wMaxScanLines; } else { wRLinePosEven = (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines; wGLinePosEven = (g_wtheReadyLines - g_wLineDistance - g_wPixelDistance) % g_wMaxScanLines; wBLinePosEven = (g_wtheReadyLines - g_wLineDistance * 2 - g_wPixelDistance) % g_wMaxScanLines; wRLinePosOdd = (g_wtheReadyLines) % g_wMaxScanLines; wGLinePosOdd = (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines; wBLinePosOdd = (g_wtheReadyLines - g_wLineDistance * 2) % g_wMaxScanLines; } for (i = 0; i < g_SWWidth;) { if ((i + 1) != g_SWWidth) { byRed = *(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow + i * 3 + 0); bNextPixel = *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow + (i + 1) * 3 + 0); /*R-channel */ byRed = (byRed + bNextPixel) >> 1; byGreen = *(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow + i * 3 + 1); bNextPixel = *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow + (i + 1) * 3 + 1); /*G-channel */ byGreen = (byGreen + bNextPixel) >> 1; byBlue = *(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow + i * 3 + 2); bNextPixel = *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow + (i + 1) * 3 + 2); /*B-channel */ byBlue = (byBlue + bNextPixel) >> 1; #ifdef ENABLE_GAMMA *(lpLine + i * 3 + 0) = (unsigned char) (*(g_pGammaTable + (unsigned short) ((byRed << 4) | QBET4 (byBlue, byGreen)))); *(lpLine + i * 3 + 1) = (unsigned char) (*(g_pGammaTable + 4096 + (unsigned short) ((byGreen << 4) | QBET4 (byRed, byBlue)))); *(lpLine + i * 3 + 2) = (unsigned char) (*(g_pGammaTable + 8192 + (unsigned short) ((byBlue << 4) | QBET4 (byGreen, byRed)))); #else *(lpLine + i * 3 + 0) = (unsigned char) byRed; *(lpLine + i * 3 + 1) = (unsigned char) byGreen; *(lpLine + i * 3 + 2) = (unsigned char) byBlue; #endif i++; if (i >= g_SWWidth) { break; } byRed = *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow + i * 3 + 0); bNextPixel = *(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow + (i + 1) * 3 + 0); byRed = (byRed + bNextPixel) >> 1; byGreen = *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow + i * 3 + 1); bNextPixel = *(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow + (i + 1) * 3 + 1); byGreen = (byGreen + bNextPixel) >> 1; byBlue = *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow + i * 3 + 2); bNextPixel = *(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow + (i + 1) * 3 + 2); byBlue = (byBlue + bNextPixel) >> 1; #ifdef ENABLE_GAMMA *(lpLine + i * 3 + 0) = (unsigned char) (*(g_pGammaTable + (unsigned short) ((byRed << 4) | QBET4 (byBlue, byGreen)))); *(lpLine + i * 3 + 1) = (unsigned char) (*(g_pGammaTable + 4096 + (unsigned short) ((byGreen << 4) | QBET4 (byRed, byBlue)))); *(lpLine + i * 3 + 2) = (unsigned char) (*(g_pGammaTable + 8192 + (unsigned short) ((byBlue << 4) | QBET4 (byGreen, byRed)))); #else *(lpLine + i * 3 + 0) = (unsigned char) byRed; *(lpLine + i * 3 + 1) = (unsigned char) byGreen; *(lpLine + i * 3 + 2) = (unsigned char) byBlue; #endif i++; } } TotalXferLines++; g_dwTotalTotalXferLines++; lpLine += g_SWBytesPerRow; AddReadyLines (); DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine1200DPI: g_dwTotalTotalXferLines=%d\n", g_dwTotalTotalXferLines); DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine1200DPI: g_Height=%d\n", g_Height); } if (g_isCanceled) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine1200DPI: thread exit\n"); break; } } } else { for (; TotalXferLines < wWantedTotalLines;) { if (g_dwTotalTotalXferLines >= g_SWHeight) { DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine1200DPI: g_dwTotalTotalXferLines=%d\n", g_dwTotalTotalXferLines); DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine1200DPI: g_Height=%d\n", g_Height); pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine1200DPI: thread exit\n"); *wLinesCount = TotalXferLines; g_isScanning = FALSE; return TRUE; } if (GetScannedLines () > g_wtheReadyLines) { if (ST_Reflective == g_ScanType) { wRLinePosOdd = (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines; wGLinePosOdd = (g_wtheReadyLines - g_wLineDistance - g_wPixelDistance) % g_wMaxScanLines; wBLinePosOdd = (g_wtheReadyLines - g_wLineDistance * 2 - g_wPixelDistance) % g_wMaxScanLines; wRLinePosEven = (g_wtheReadyLines) % g_wMaxScanLines; wGLinePosEven = (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines; wBLinePosEven = (g_wtheReadyLines - g_wLineDistance * 2) % g_wMaxScanLines; } else { wRLinePosEven = (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines; wGLinePosEven = (g_wtheReadyLines - g_wLineDistance - g_wPixelDistance) % g_wMaxScanLines; wBLinePosEven = (g_wtheReadyLines - g_wLineDistance * 2 - g_wPixelDistance) % g_wMaxScanLines; wRLinePosOdd = (g_wtheReadyLines) % g_wMaxScanLines; wGLinePosOdd = (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines; wBLinePosOdd = (g_wtheReadyLines - g_wLineDistance * 2) % g_wMaxScanLines; } for (i = 0; i < g_SWWidth;) { if ((i + 1) != g_SWWidth) { byRed = *(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow + i * 3 + 0); bNextPixel = *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow + (i + 1) * 3 + 0); byRed = (byRed + bNextPixel) >> 1; byGreen = *(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow + i * 3 + 1); bNextPixel = *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow + (i + 1) * 3 + 1); byGreen = (byGreen + bNextPixel) >> 1; byBlue = *(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow + i * 3 + 2); bNextPixel = *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow + (i + 1) * 3 + 2); byBlue = (byBlue + bNextPixel) >> 1; #ifdef ENABLE_GAMMA *(lpLine + i * 3 + 2) = (unsigned char) (*(g_pGammaTable + (unsigned short) ((byRed << 4) | QBET4 (byBlue, byGreen)))); *(lpLine + i * 3 + 1) = (unsigned char) (*(g_pGammaTable + 4096 + (unsigned short) ((byGreen << 4) | QBET4 (byRed, byBlue)))); *(lpLine + i * 3 + 0) = (unsigned char) (*(g_pGammaTable + 8192 + (unsigned short) ((byBlue << 4) | QBET4 (byGreen, byRed)))); #else *(lpLine + i * 3 + 2) = (unsigned char) byRed; *(lpLine + i * 3 + 1) = (unsigned char) byGreen; *(lpLine + i * 3 + 0) = (unsigned char) byBlue; #endif i++; if (i >= g_SWWidth) { break; } byRed = *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow + i * 3 + 0); bNextPixel = *(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow + (i + 1) * 3 + 0); byRed = (byRed + bNextPixel) >> 1; byGreen = *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow + i * 3 + 1); bNextPixel = *(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow + (i + 1) * 3 + 1); byGreen = (byGreen + bNextPixel) >> 1; byBlue = *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow + i * 3 + 2); bNextPixel = *(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow + (i + 1) * 3 + 2); byBlue = (byBlue + bNextPixel) >> 1; #ifdef ENABLE_GAMMA *(lpLine + i * 3 + 2) = (unsigned char) (*(g_pGammaTable + (unsigned short) ((byRed << 4) | QBET4 (byBlue, byGreen)))); *(lpLine + i * 3 + 1) = (unsigned char) (*(g_pGammaTable + 4096 + (unsigned short) ((byGreen << 4) | QBET4 (byRed, byBlue)))); *(lpLine + i * 3 + 0) = (unsigned char) (*(g_pGammaTable + 8192 + (unsigned short) ((byBlue << 4) | QBET4 (byGreen, byRed)))); #else *(lpLine + i * 3 + 2) = (unsigned char) byRed; *(lpLine + i * 3 + 1) = (unsigned char) byGreen; *(lpLine + i * 3 + 0) = (unsigned char) byBlue; #endif i++; } } TotalXferLines++; g_dwTotalTotalXferLines++; lpLine += g_SWBytesPerRow; AddReadyLines (); DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine1200DPI: g_dwTotalTotalXferLines=%d\n", g_dwTotalTotalXferLines); DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine1200DPI: g_Height=%d\n", g_Height); } if (g_isCanceled) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine1200DPI: thread exit\n"); break; } } } *wLinesCount = TotalXferLines; g_isScanning = FALSE; DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine1200DPI: leave MustScanner_GetRgb24BitLine1200DPI\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/15 Routine Description: Repair line when single CCD and color is 16bit Parameters: lpLine: point to image be repaired isOrderInvert: RGB or BGR wLinesCount: how many line be repaired Return value: if the operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool MustScanner_GetMono16BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert, unsigned short * wLinesCount) { unsigned short wWantedTotalLines; unsigned short TotalXferLines; unsigned int wTempData; unsigned short wLinePos = 0; unsigned short i; (void) isOrderInvert; DBG (DBG_FUNC, "MustScanner_GetMono16BitLine: call in\n"); TotalXferLines = 0; g_isCanceled = FALSE; g_isScanning = TRUE; wWantedTotalLines = *wLinesCount; if (g_bFirstReadImage) { pthread_create (&g_threadid_readimage, NULL, MustScanner_ReadDataFromScanner, NULL); DBG (DBG_FUNC, "MustScanner_GetMono16BitLine: thread create\n"); g_bFirstReadImage = FALSE; } for (; TotalXferLines < wWantedTotalLines;) { if (g_dwTotalTotalXferLines >= g_SWHeight) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetMono16BitLine: thread exit\n"); *wLinesCount = TotalXferLines; g_isScanning = FALSE; return TRUE; } if (GetScannedLines () > g_wtheReadyLines) { wLinePos = g_wtheReadyLines % g_wMaxScanLines; for (i = 0; i < g_SWWidth; i++) { wTempData = *(g_lpReadImageHead + wLinePos * g_BytesPerRow + i * 2 + 0); wTempData += *(g_lpReadImageHead + wLinePos * g_BytesPerRow + i * 2 + 1) << 8; *(lpLine + i * 2 + 0) = LOBYTE (g_pGammaTable[wTempData]); *(lpLine + i * 2 + 1) = HIBYTE (g_pGammaTable[wTempData]); } TotalXferLines++; g_dwTotalTotalXferLines++; lpLine += g_SWBytesPerRow; AddReadyLines (); } if (g_isCanceled) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetMono16BitLine: thread exit\n"); break; } } *wLinesCount = TotalXferLines; g_isScanning = FALSE; DBG (DBG_FUNC, "MustScanner_GetMono16BitLine: leave MustScanner_GetMono16BitLine\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/15 Routine Description: Repair line when double CCD and color is 16bit Parameters: lpLine: point to image be repaired isOrderInvert: RGB or BGR wLinesCount: how many line be repaired Return value: if the operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool MustScanner_GetMono16BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert, unsigned short * wLinesCount) { unsigned short wWantedTotalLines; unsigned short TotalXferLines; unsigned int dwTempData; unsigned short wLinePosOdd = 0; unsigned short wLinePosEven = 0; unsigned short i; SANE_Byte * lpTemp = lpLine; (void) isOrderInvert; DBG (DBG_FUNC, "MustScanner_GetMono16BitLine1200DPI: call in\n"); TotalXferLines = 0; g_isCanceled = FALSE; g_isScanning = TRUE; wWantedTotalLines = *wLinesCount; if (g_bFirstReadImage) { pthread_create (&g_threadid_readimage, NULL, MustScanner_ReadDataFromScanner, NULL); DBG (DBG_FUNC, "MustScanner_GetMono16BitLine1200DPI: thread create\n"); g_bFirstReadImage = FALSE; } for (; TotalXferLines < wWantedTotalLines;) { if (g_dwTotalTotalXferLines >= g_SWHeight) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetMono16BitLine1200DPI: thread exit\n"); *wLinesCount = TotalXferLines; g_isScanning = FALSE; return TRUE; } if (GetScannedLines () > g_wtheReadyLines) { if (ST_Reflective == g_ScanType) { wLinePosOdd = (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines; wLinePosEven = (g_wtheReadyLines) % g_wMaxScanLines; } else { wLinePosEven = (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines; wLinePosOdd = (g_wtheReadyLines) % g_wMaxScanLines; } for (i = 0; i < g_SWWidth;) { if ((i + 1) != g_SWWidth) { dwTempData = (unsigned int) (* (g_lpReadImageHead + wLinePosOdd * g_BytesPerRow + i * 2 + 0)); dwTempData += (unsigned int) (* (g_lpReadImageHead + wLinePosOdd * g_BytesPerRow + i * 2 + 1) << 8); dwTempData += (unsigned int) (* (g_lpReadImageHead + wLinePosEven * g_BytesPerRow + (i + 1) * 2 + 0)); dwTempData += (unsigned int) (* (g_lpReadImageHead + wLinePosEven * g_BytesPerRow + (i + 1) * 2 + 1) << 8); dwTempData = g_pGammaTable[dwTempData >> 1]; *(lpLine + i * 2 + 0) = LOBYTE ((unsigned short) dwTempData); *(lpLine + i * 2 + 1) = HIBYTE ((unsigned short) dwTempData); i++; if (i >= g_SWWidth) { break; } dwTempData = (unsigned int) (* (g_lpReadImageHead + wLinePosEven * g_BytesPerRow + i * 2 + 0)); dwTempData += (unsigned int) (* (g_lpReadImageHead + wLinePosEven * g_BytesPerRow + i * 2 + 1) << 8); dwTempData += (unsigned int) (* (g_lpReadImageHead + wLinePosOdd * g_BytesPerRow + (i + 1) * 2 + 0)); dwTempData += (unsigned int) (* (g_lpReadImageHead + wLinePosOdd * g_BytesPerRow + (i + 1) * 2 + 1) << 8); dwTempData = g_pGammaTable[dwTempData >> 1]; *(lpLine + i * 2 + 0) = LOBYTE ((unsigned short) dwTempData); *(lpLine + i * 2 + 1) = HIBYTE ((unsigned short) dwTempData); i++; } } TotalXferLines++; g_dwTotalTotalXferLines++; lpLine += g_SWBytesPerRow; AddReadyLines (); } if (g_isCanceled) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetMono16BitLine1200DPI: thread exit\n"); break; } } *wLinesCount = TotalXferLines; g_isScanning = FALSE; /*for modify the last point */ if (g_bIsFirstReadBefData) { g_lpBefLineImageData = (SANE_Byte *) malloc (g_SWBytesPerRow); if (NULL == g_lpBefLineImageData) { return FALSE; } memset (g_lpBefLineImageData, 0, g_SWBytesPerRow); memcpy (g_lpBefLineImageData, lpTemp, g_SWBytesPerRow); g_bIsFirstReadBefData = FALSE; } ModifyLinePoint (lpTemp, g_lpBefLineImageData, g_SWBytesPerRow, wWantedTotalLines, 2, 4); memcpy (g_lpBefLineImageData, lpTemp + (wWantedTotalLines - 1) * g_SWBytesPerRow, g_SWBytesPerRow); g_dwAlreadyGetLines += wWantedTotalLines; if (g_dwAlreadyGetLines >= g_SWHeight) { DBG (DBG_FUNC, "MustScanner_GetMono16BitLine1200DPI: free before line data!\n"); free (g_lpBefLineImageData); g_lpBefLineImageData = NULL; g_dwAlreadyGetLines = 0; g_bIsFirstReadBefData = TRUE; } DBG (DBG_FUNC, "MustScanner_GetMono16BitLine1200DPI: leave MustScanner_GetMono16BitLine1200DPI\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/15 Routine Description: Repair line when single CCD and color is 8bit Parameters: lpLine: point to image be repaired isOrderInvert: RGB or BGR wLinesCount: how many line be repaired Return value: if the operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool MustScanner_GetMono8BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert, unsigned short * wLinesCount) { unsigned short wWantedTotalLines; unsigned short TotalXferLines; unsigned short i; unsigned short wLinePos = 0; (void) isOrderInvert; DBG (DBG_FUNC, "MustScanner_GetMono8BitLine: call in\n"); TotalXferLines = 0; g_isCanceled = FALSE; g_isScanning = TRUE; wWantedTotalLines = *wLinesCount; if (g_bFirstReadImage) { pthread_create (&g_threadid_readimage, NULL, MustScanner_ReadDataFromScanner, NULL); DBG (DBG_FUNC, "MustScanner_GetMono8BitLine: thread create\n"); g_bFirstReadImage = FALSE; } for (; TotalXferLines < wWantedTotalLines;) { if (g_dwTotalTotalXferLines >= g_SWHeight) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetMono8BitLine: thread exit\n"); *wLinesCount = TotalXferLines; g_isScanning = FALSE; return TRUE; } if (GetScannedLines () > g_wtheReadyLines) { wLinePos = g_wtheReadyLines % g_wMaxScanLines; for (i = 0; i < g_SWWidth; i++) { *(lpLine + i) = (SANE_Byte) * (g_pGammaTable + (unsigned short) ((* (g_lpReadImageHead + wLinePos * g_BytesPerRow + i) << 4) | (rand () & 0x0f))); } TotalXferLines++; g_dwTotalTotalXferLines++; lpLine += g_SWBytesPerRow; AddReadyLines (); } if (g_isCanceled) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetMono8BitLine: thread exit\n"); break; } } *wLinesCount = TotalXferLines; g_isScanning = FALSE; DBG (DBG_FUNC, "MustScanner_GetMono8BitLine: leave MustScanner_GetMono8BitLine\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/15 Routine Description: Repair line when double CCD and color is 8bit Parameters: lpLine: point to image be repaired isOrderInvert: RGB or BGR wLinesCount: how many line be repaired Return value: if the operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool MustScanner_GetMono8BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert, unsigned short * wLinesCount) { SANE_Byte *lpTemp; unsigned short wWantedTotalLines; unsigned short TotalXferLines; unsigned short wLinePosOdd = 0; unsigned short wLinePosEven = 0; SANE_Byte byGray; unsigned short i; SANE_Byte bNextPixel = 0; (void) isOrderInvert; DBG (DBG_FUNC, "MustScanner_GetMono8BitLine1200DPI: call in\n"); TotalXferLines = 0; g_isCanceled = FALSE; g_isScanning = TRUE; wWantedTotalLines = *wLinesCount; lpTemp = lpLine; if (g_bFirstReadImage) { pthread_create (&g_threadid_readimage, NULL, MustScanner_ReadDataFromScanner, NULL); DBG (DBG_FUNC, "MustScanner_GetMono8BitLine1200DPI: thread create\n"); g_bFirstReadImage = FALSE; } for (; TotalXferLines < wWantedTotalLines;) { if (g_dwTotalTotalXferLines >= g_SWHeight) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetMono8BitLine1200DPI: thread exit\n"); *wLinesCount = TotalXferLines; g_isScanning = FALSE; return TRUE; } if (GetScannedLines () > g_wtheReadyLines) { if (ST_Reflective == g_ScanType) { wLinePosOdd = (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines; wLinePosEven = (g_wtheReadyLines) % g_wMaxScanLines; } else { wLinePosEven = (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines; wLinePosOdd = (g_wtheReadyLines) % g_wMaxScanLines; } for (i = 0; i < g_SWWidth;) { if ((i + 1) != g_SWWidth) { byGray = *(g_lpReadImageHead + wLinePosOdd * g_BytesPerRow + i); bNextPixel = *(g_lpReadImageHead + wLinePosEven * g_BytesPerRow + (i + 1)); byGray = (byGray + bNextPixel) >> 1; *(lpLine + i) = (SANE_Byte) * (g_pGammaTable + (byGray << 4 | (rand () & 0x0f))); i++; if (i >= g_SWWidth) { break; } byGray = *(g_lpReadImageHead + wLinePosEven * g_BytesPerRow + i); bNextPixel = *(g_lpReadImageHead + wLinePosOdd * g_BytesPerRow + (i + 1)); byGray = (byGray + bNextPixel) >> 1; *(lpLine + i) = (SANE_Byte) * (g_pGammaTable + (byGray << 4 | (rand () & 0x0f))); i++; } } TotalXferLines++; g_dwTotalTotalXferLines++; lpLine += g_SWBytesPerRow; AddReadyLines (); } if (g_isCanceled) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetMono8BitLine1200DPI: thread exit\n"); break; } } *wLinesCount = TotalXferLines; g_isScanning = FALSE; /*for modify the last point */ if (g_bIsFirstReadBefData) { g_lpBefLineImageData = (SANE_Byte *) malloc (g_SWBytesPerRow); if (NULL == g_lpBefLineImageData) { return FALSE; } memset (g_lpBefLineImageData, 0, g_SWBytesPerRow); memcpy (g_lpBefLineImageData, lpTemp, g_SWBytesPerRow); g_bIsFirstReadBefData = FALSE; } ModifyLinePoint (lpTemp, g_lpBefLineImageData, g_SWBytesPerRow, wWantedTotalLines, 1, 4); memcpy (g_lpBefLineImageData, lpTemp + (wWantedTotalLines - 1) * g_SWBytesPerRow, g_SWBytesPerRow); g_dwAlreadyGetLines += wWantedTotalLines; if (g_dwAlreadyGetLines >= g_SWHeight) { DBG (DBG_FUNC, "MustScanner_GetMono8BitLine1200DPI: free the before line data!\n"); free (g_lpBefLineImageData); g_lpBefLineImageData = NULL; g_dwAlreadyGetLines = 0; g_bIsFirstReadBefData = TRUE; } DBG (DBG_FUNC, "MustScanner_GetMono8BitLine1200DPI: leave MustScanner_GetMono8BitLine1200DPI\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/15 Routine Description: Repair line when single CCD and color is 1bit Parameters: lpLine: point to image be repaired isOrderInvert: RGB or BGR wLinesCount: how many line be repaired Return value: if the operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool MustScanner_GetMono1BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert, unsigned short * wLinesCount) { unsigned short wWantedTotalLines; unsigned short TotalXferLines; unsigned short wLinePos; unsigned short i; (void) isOrderInvert; DBG (DBG_FUNC, "MustScanner_GetMono1BitLine: call in\n"); g_isCanceled = FALSE; g_isScanning = TRUE; wWantedTotalLines = *wLinesCount; if (g_bFirstReadImage) { pthread_create (&g_threadid_readimage, NULL, MustScanner_ReadDataFromScanner, NULL); DBG (DBG_FUNC, "MustScanner_GetMono1BitLine: thread create\n"); g_bFirstReadImage = FALSE; } memset (lpLine, 0, wWantedTotalLines * g_SWWidth / 8); for (TotalXferLines = 0; TotalXferLines < wWantedTotalLines;) { if (g_dwTotalTotalXferLines >= g_SWHeight) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetMono1BitLine: thread exit\n"); *wLinesCount = TotalXferLines; g_isScanning = FALSE; return TRUE; } if (GetScannedLines () > g_wtheReadyLines) { wLinePos = g_wtheReadyLines % g_wMaxScanLines; for (i = 0; i < g_SWWidth; i++) { if (*(g_lpReadImageHead + wLinePos * g_BytesPerRow + i) > g_wLineartThreshold) { *(lpLine + i / 8) += (0x80 >> (i % 8)); } } TotalXferLines++; g_dwTotalTotalXferLines++; lpLine += (g_SWBytesPerRow / 8); AddReadyLines (); } if (g_isCanceled) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetMono1BitLine: thread exit\n"); break; } } *wLinesCount = TotalXferLines; g_isScanning = FALSE; DBG (DBG_FUNC, "MustScanner_GetMono1BitLine: leave MustScanner_GetMono1BitLine\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/15 Routine Description: Repair line when double CCD and color is 1bit Parameters: lpLine: point to image be repaired isOrderInvert: RGB or BGR wLinesCount: how many line be repaired Return value: if the operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool MustScanner_GetMono1BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert, unsigned short * wLinesCount) { unsigned short wWantedTotalLines; unsigned short TotalXferLines; unsigned short i; unsigned short wLinePosOdd; unsigned short wLinePosEven; (void) isOrderInvert; DBG (DBG_FUNC, "MustScanner_GetMono1BitLine1200DPI: call in\n"); g_isCanceled = FALSE; g_isScanning = TRUE; wWantedTotalLines = *wLinesCount; if (g_bFirstReadImage) { pthread_create (&g_threadid_readimage, NULL, MustScanner_ReadDataFromScanner, NULL); DBG (DBG_FUNC, "MustScanner_GetMono1BitLine1200DPI: thread create\n"); g_bFirstReadImage = FALSE; } memset (lpLine, 0, wWantedTotalLines * g_SWWidth / 8); for (TotalXferLines = 0; TotalXferLines < wWantedTotalLines;) { if (g_dwTotalTotalXferLines >= g_SWHeight) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetMono1BitLine1200DPI: thread exit\n"); *wLinesCount = TotalXferLines; g_isScanning = FALSE; return TRUE; } if (GetScannedLines () > g_wtheReadyLines) { if (ST_Reflective == g_ScanType) { wLinePosEven = (g_wtheReadyLines) % g_wMaxScanLines; wLinePosOdd = (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines; } else { wLinePosOdd = (g_wtheReadyLines) % g_wMaxScanLines; wLinePosEven = (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines; } for (i = 0; i < g_SWWidth;) { if ((i + 1) != g_SWWidth) { if (*(g_lpReadImageHead + wLinePosOdd * g_BytesPerRow + i) > g_wLineartThreshold) *(lpLine + i / 8) += (0x80 >> (i % 8)); i++; if (i >= g_SWWidth) { break; } if (*(g_lpReadImageHead + wLinePosEven * g_BytesPerRow + i) > g_wLineartThreshold) *(lpLine + i / 8) += (0x80 >> (i % 8)); i++; } } TotalXferLines++; g_dwTotalTotalXferLines++; lpLine += g_SWBytesPerRow / 8; AddReadyLines (); } if (g_isCanceled) { pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "MustScanner_GetMono1BitLine1200DPI: thread exit\n"); break; } } /*end for */ *wLinesCount = TotalXferLines; g_isScanning = FALSE; DBG (DBG_FUNC, "MustScanner_GetMono1BitLine1200DPI: leave MustScanner_GetMono1BitLine1200DPI\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/21 Routine Description: prepare calculate Max and Min value Parameters: wResolution: the scan resolution Return value: none ***********************************************************************/ static void MustScanner_PrepareCalculateMaxMin (unsigned short wResolution) { g_wDarkCalWidth = 52; if (wResolution <= 600) { g_wCalWidth = ((5120 * wResolution / 600 + 511) >> 9) << 9; g_wDarkCalWidth = g_wDarkCalWidth / (1200 / wResolution); if (wResolution < 200) { g_nPowerNum = 3; g_nSecLength = 8; /* 2^nPowerNum */ g_nDarkSecLength = g_wDarkCalWidth / 2; /* Dark has at least 2 sections */ } else { g_nPowerNum = 6; g_nSecLength = 64; /* 2^nPowerNum */ g_nDarkSecLength = g_wDarkCalWidth / 3; } } else { g_nPowerNum = 6; g_nSecLength = 64; /*2^nPowerNum */ g_wCalWidth = 10240; g_nDarkSecLength = g_wDarkCalWidth / 5; } if (g_nDarkSecLength <= 0) { g_nDarkSecLength = 1; } g_wStartPosition = 13 * wResolution / 1200; g_wCalWidth -= g_wStartPosition; /* start of find Max value */ g_nSecNum = (int) (g_wCalWidth / g_nSecLength); /* start of fin min value */ g_nDarkSecNum = (int) (g_wDarkCalWidth / g_nDarkSecLength); } /********************************************************************** Author: Jack Date: 2005/05/21 Routine Description: calculate the Max and Min value Parameters: pBuffer: the image data lpMaxValue: the max value lpMinValue: the min value wResolution: the scan resolution Return value: none ***********************************************************************/ static void MustScanner_CalculateMaxMin (SANE_Byte * pBuffer, unsigned short * lpMaxValue, unsigned short * lpMinValue, unsigned short wResolution) { unsigned short *wSecData = NULL, *wDarkSecData = NULL; int i, j; (void) wResolution; wSecData = (unsigned short *) malloc (sizeof (unsigned short) * g_nSecNum); if (wSecData == NULL) { return; } else { memset (wSecData, 0, g_nSecNum * sizeof (unsigned short)); } for (i = 0; i < g_nSecNum; i++) { for (j = 0; j < g_nSecLength; j++) wSecData[i] += *(pBuffer + g_wStartPosition + i * g_nSecLength + j); wSecData[i] >>= g_nPowerNum; } *lpMaxValue = wSecData[0]; for (i = 0; i < g_nSecNum; i++) { if (*lpMaxValue < wSecData[i]) *lpMaxValue = wSecData[i]; } free (wSecData); wDarkSecData = (unsigned short *) malloc (sizeof (unsigned short) * g_nDarkSecNum); if (wDarkSecData == NULL) { return; } else { memset (wDarkSecData, 0, g_nDarkSecNum * sizeof (unsigned short)); } for (i = 0; i < g_nDarkSecNum; i++) { for (j = 0; j < g_nDarkSecLength; j++) wDarkSecData[i] += *(pBuffer + g_wStartPosition + i * g_nDarkSecLength + j); wDarkSecData[i] /= g_nDarkSecLength; } *lpMinValue = wDarkSecData[0]; for (i = 0; i < g_nDarkSecNum; i++) { if (*lpMinValue > wDarkSecData[i]) *lpMinValue = wDarkSecData[i]; } free (wDarkSecData); } /********************************************************************** Author: Jack Date: 2005/05/15 Routine Description: Read the data from scanner Parameters: none Return value: if operation is success return TRUE else return FALSE ***********************************************************************/ static void * MustScanner_ReadDataFromScanner (void * dummy) { unsigned short wTotalReadImageLines = 0; unsigned short wWantedLines = g_Height; SANE_Byte * lpReadImage = g_lpReadImageHead; SANE_Bool isWaitImageLineDiff = FALSE; unsigned int wMaxScanLines = g_wMaxScanLines; unsigned short wReadImageLines = 0; unsigned short wScanLinesThisBlock; unsigned short wBufferLines = g_wLineDistance * 2 + g_wPixelDistance; (void) dummy; DBG (DBG_FUNC, "MustScanner_ReadDataFromScanner: call in, and in new thread\n"); while (wTotalReadImageLines < wWantedLines && g_lpReadImageHead) { if (!isWaitImageLineDiff) { wScanLinesThisBlock = (wWantedLines - wTotalReadImageLines) < g_wScanLinesPerBlock ? (wWantedLines - wTotalReadImageLines) : g_wScanLinesPerBlock; DBG (DBG_FUNC, "MustScanner_ReadDataFromScanner: wWantedLines=%d\n", wWantedLines); DBG (DBG_FUNC, "MustScanner_ReadDataFromScanner: wScanLinesThisBlock=%d\n", wScanLinesThisBlock); if (SANE_STATUS_GOOD != Asic_ReadImage (&g_chip, lpReadImage, wScanLinesThisBlock)) { DBG (DBG_FUNC, "MustScanner_ReadDataFromScanner:Asic_ReadImage return error\n"); DBG (DBG_FUNC, "MustScanner_ReadDataFromScanner:thread exit\n"); return NULL; } /*has read in memory Buffer */ wReadImageLines += wScanLinesThisBlock; AddScannedLines (wScanLinesThisBlock); wTotalReadImageLines += wScanLinesThisBlock; lpReadImage += wScanLinesThisBlock * g_BytesPerRow; /*Buffer is full */ if (wReadImageLines >= wMaxScanLines) { lpReadImage = g_lpReadImageHead; wReadImageLines = 0; } if ((g_dwScannedTotalLines - GetReadyLines ()) >= (wMaxScanLines - (wBufferLines + g_wScanLinesPerBlock)) && g_dwScannedTotalLines > GetReadyLines ()) { isWaitImageLineDiff = TRUE; } } else if (g_dwScannedTotalLines <= GetReadyLines () + wBufferLines + g_wScanLinesPerBlock) { isWaitImageLineDiff = FALSE; } pthread_testcancel (); } DBG (DBG_FUNC, "MustScanner_ReadDataFromScanner: Read image ok\n"); DBG (DBG_FUNC, "MustScanner_ReadDataFromScanner: thread exit\n"); DBG (DBG_FUNC, "MustScanner_ReadDataFromScanner: leave MustScanner_ReadDataFromScanner\n"); return NULL; } /********************************************************************** Author: Jack Date: 2005/05/26 Routine Description: get the lines of scanned Parameters: none Return value: the lines of scanned ***********************************************************************/ static unsigned int GetScannedLines () { unsigned int dwScannedLines = 0; pthread_mutex_lock (&g_scannedLinesMutex); dwScannedLines = g_dwScannedTotalLines; pthread_mutex_unlock (&g_scannedLinesMutex); return dwScannedLines; } /********************************************************************** Author: Jack Date: 2005/05/26 Routine Description: get lines which pass to superstratum Parameters: none Return value: the lines which pass to superstratum ***********************************************************************/ static unsigned int GetReadyLines () { unsigned int dwReadyLines = 0; pthread_mutex_lock (&g_readyLinesMutex); dwReadyLines = g_wtheReadyLines; pthread_mutex_unlock (&g_readyLinesMutex); return dwReadyLines; } /********************************************************************** Author: Jack Date: 2005/05/26 Routine Description: add the scanned total lines Parameters: wAddLines: add the lines Return value: none ***********************************************************************/ static void AddScannedLines (unsigned short wAddLines) { pthread_mutex_lock (&g_scannedLinesMutex); g_dwScannedTotalLines += wAddLines; pthread_mutex_unlock (&g_scannedLinesMutex); } /********************************************************************** Author: Jack Date: 2005/05/26 Routine Description: add the ready lines Parameters: none Return value: none ***********************************************************************/ static void AddReadyLines () { pthread_mutex_lock (&g_readyLinesMutex); g_wtheReadyLines++; pthread_mutex_unlock (&g_readyLinesMutex); } /********************************************************************** Author: Jack Date: 2005/05/26 Routine Description: modify the point Parameters: lpImageData: the data of image lpImageDataBefore: the data of before line image dwBytesPerLine: the bytes of per line dwLinesCount: the line count wPixDistance: the pixel distance wModPtCount: the modify point count Return value: none ***********************************************************************/ static void ModifyLinePoint (SANE_Byte * lpImageData, SANE_Byte * lpImageDataBefore, unsigned int dwBytesPerLine, unsigned int dwLinesCount, unsigned short wPixDistance, unsigned short wModPtCount) { unsigned short i = 0; unsigned short j = 0; unsigned short wLines = 0; unsigned int dwWidth = dwBytesPerLine / wPixDistance; for (i = wModPtCount; i > 0; i--) { for (j = 0; j < wPixDistance; j++) { /*modify the first line */ *(lpImageData + (dwWidth - i) * wPixDistance + j) = (*(lpImageData + (dwWidth - i - 1) * wPixDistance + j) + *(lpImageDataBefore + (dwWidth - i) * wPixDistance + j)) / 2; /*modify other lines */ for (wLines = 1; wLines < dwLinesCount; wLines++) { unsigned int dwBytesBefor = (wLines - 1) * dwBytesPerLine; unsigned int dwBytes = wLines * dwBytesPerLine; *(lpImageData + dwBytes + (dwWidth - i) * wPixDistance + j) = (* (lpImageData + dwBytes + (dwWidth - i - 1) * wPixDistance + j) + *(lpImageData + dwBytesBefor + (dwWidth - i) * wPixDistance + j)) / 2; } } } } /********************************************************************** Author: Jack Date: 2005/05/15 Routine Description: Modify the image data Parameters: A: the input the image data B: the input the image data Return value: the modified data ***********************************************************************/ static SANE_Byte QBET4 (SANE_Byte A, SANE_Byte B) { SANE_Byte bQBET[16][16] = { {0, 0, 0, 0, 1, 1, 2, 2, 4, 4, 5, 5, 8, 8, 9, 9}, {0, 0, 0, 0, 1, 1, 2, 2, 4, 4, 5, 5, 8, 8, 9, 9}, {0, 0, 0, 0, 1, 1, 2, 2, 4, 4, 5, 5, 8, 8, 9, 9}, {0, 0, 0, 0, 1, 1, 2, 2, 4, 4, 5, 5, 8, 8, 9, 9}, {1, 1, 1, 1, 3, 3, 3, 3, 6, 6, 6, 6, 10, 10, 11, 11}, {1, 1, 1, 1, 3, 3, 3, 3, 6, 6, 6, 6, 10, 10, 11, 11}, {2, 2, 2, 2, 3, 3, 3, 3, 7, 7, 7, 7, 10, 10, 11, 11}, {2, 2, 2, 2, 3, 3, 3, 3, 7, 7, 7, 7, 10, 10, 11, 11}, {4, 4, 4, 4, 6, 6, 7, 7, 12, 12, 12, 12, 13, 13, 14, 14}, {4, 4, 4, 4, 6, 6, 7, 7, 12, 12, 12, 12, 13, 13, 14, 14}, {5, 5, 5, 5, 6, 6, 7, 7, 12, 12, 12, 12, 13, 13, 14, 14}, {5, 5, 5, 5, 6, 6, 7, 7, 12, 12, 12, 12, 13, 13, 14, 14}, {8, 8, 8, 8, 10, 10, 10, 10, 13, 13, 13, 13, 15, 15, 15, 15}, {8, 8, 8, 8, 10, 10, 10, 10, 13, 13, 13, 13, 15, 15, 15, 15}, {9, 9, 9, 9, 11, 11, 11, 11, 14, 14, 14, 14, 15, 15, 15, 15}, {9, 9, 9, 9, 11, 11, 11, 11, 14, 14, 14, 14, 15, 15, 15, 15} }; A = A & 0x0f; B = B & 0x0f; return bQBET[A][B]; } /* end of the file MustScanner.c */ backends-1.3.0/backend/mustek_usb2_high.h000066400000000000000000000156471456256263500203220ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2005 Mustek. Originally maintained by Mustek Author:Jack Roy 2005.5.24 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, see . 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 the Mustek BearPaw 2448 TA Pro and similar USB2 scanners. */ #ifndef MUSTEK_USB2_HIGH_H #define MUSTEK_USB2_HIGH_H /* const use in structures*/ /*scan mode*/ typedef unsigned short SCANMODE, *LPSCANMODE; #define SM_TEXT 0x00 #define SM_GRAY 0x01 #define SM_RGB24 0x02 #define SM_GRAY10 0x03 #define SM_RGB30 0x04 #define SM_GRAY12 0x05 #define SM_RGB36 0x06 #define SM_GRAY14 0x07 #define SM_RGB42 0x08 #define SM_GRAY16 0x09 #define SM_RGB48 0x0a /*pixel flavor*/ typedef SANE_Byte PIXELFLAVOR, *LPPIXELFLAVOR; #define PF_BlackIs0 0x00 #define PF_WhiteIs0 0x01 /*scan source*/ typedef SANE_Byte SCANSOURCE, *LPSCANSOURCE; #define SS_Reflective 0x00 #define SS_Positive 0x01 #define SS_Negative 0x02 #define SS_ADF 0x03 /*RGB order*/ typedef unsigned short RGBORDER, *LPRGBORDER; #define RO_RGB 0x00 #define RO_BGR 0x01 /* structures use in parameters of export function*/ typedef struct tagGAMMAINFO { SCANMODE smScanMode; unsigned short wInputGammaBits; unsigned short wOutputGammaBits; } GAMMAINFO, *LPGAMMAINFO; typedef struct tagGETPARAMETERS { unsigned short wSourceXDPI; unsigned short wSourceYDPI; unsigned int dwLineByteWidth; unsigned int dwLength; } GETPARAMETERS, *LPGETPARAMETERS; typedef struct tagFRAME { unsigned short x1; unsigned short y1; unsigned short x2; unsigned short y2; } FRAME, *LPFRAME; typedef struct tagSETPARAMETERS { FRAME fmArea; unsigned short wTargetDPI; SCANMODE smScanMode; unsigned short wLinearThreshold; /*threshold for Line art mode */ PIXELFLAVOR pfPixelFlavor; SCANSOURCE ssScanSource; unsigned short * pGammaTable; } SETPARAMETERS, *LPSETPARAMETERS; typedef struct tagIMAGEROWS { RGBORDER roRgbOrder; unsigned short wWantedLineNum; unsigned short wXferedLineNum; SANE_Byte * pBuffer; } IMAGEROWS, *LPIMAGEROWS; /*Macro define*/ #define R_GAIN 0 #define G_GAIN 0 #define B_GAIN 0 #define R_OFFSET 0 #define G_OFFSET 0 #define B_OFFSET 0 #define R_DIRECTION 0 #define G_DIRECTION 0 #define B_DIRECTION 0 /* use for adjust AD's offset*/ /* for Reflective*/ #define REFL_DARK_MAX_LEVEL 20 #define REFL_DARK_MIN_LEVEL 10 #define REFL_WHITE_MAX_LEVEL 220 #define REFL_WHITE_MIN_LEVEL 210 #define REFL_MAX_LEVEL_RANGE 210 #define REFL_MIN_LEVEL_RANGE 190 /*for Transparent*/ #define TRAN_DARK_MAX_LEVEL 20 #define TRAN_DARK_MIN_LEVEL 10 #define TRAN_WHITE_MAX_LEVEL 220 #define TRAN_WHITE_MIN_LEVEL 210 #define TRAN_MAX_LEVEL_RANGE 210 #define TRAN_MIN_LEVEL_RANGE 190 /* in 600 dpi*/ #define FIND_LEFT_TOP_WIDTH_IN_DIP 512 #define FIND_LEFT_TOP_HEIGHT_IN_DIP 180 #define FIND_LEFT_TOP_CALIBRATE_RESOLUTION 600 #define TA_FIND_LEFT_TOP_WIDTH_IN_DIP 2668 #define TA_FIND_LEFT_TOP_HEIGHT_IN_DIP 300 #define TA_MOTOR_BACK_STEP_AFTER_FIND_BOUNDARY 150 #define TA_MOTOR_FORWARD_STEP_AFTER_READ_WHITE_DATA 1100 /*must be 8x*/ #define LINE_CALIBRATION__16BITS_HEIGHT 40 /* the length from block bar to start Calibration position*/ #define BEFORE_SCANNING_MOTOR_FORWARD_PIXEL 40 #define PRE_MOVE_MOTOR_LENGTH_IN_DPI 1450 /* if the motor is 1/8 step, setup MOTOR_STEP_MULTI as 8 if the motor is 1/4 step, setup MOTOR_STEP_MULTI as 4 if the motor is full step, setup MOTOR_STEP_MULTI as 1 #define MOTOR_EIGHTH_STEP*/ #ifdef MOTOR_EIGHTH_STEP #define MOTOR_STEP_MULTI 8 #define GPIO_95_Config 0x68 #else #define MOTOR_STEP_MULTI 4 #define GPIO_95_Config 0x60 #endif #define TRAN_START_POS 4550 /* in 300dpi*/ #define MAX_SCANNING_WIDTH 2550 /*just for A4 */ #define MAX_SCANNING_HEIGHT 3540 /*just for A4 */ #define INIFILENAME "./msam.ini" /*enable gamma*/ #define ENABLE_GAMMA /*save debug image*/ /*#define DEBUG_SAVE_IMAGE*/ /*type define*/ typedef unsigned char SCANTYPE; #define ST_Reflective 0x00 #define ST_Transparent 0x01 typedef enum tagCOLORMODE { CM_RGB48 = 0, CM_RGB42 = 1, CM_RGB36 = 2, CM_RGB30 = 3, CM_RGB24 = 4, CM_GRAY16 = 5, CM_GRAY14 = 6, CM_GRAY12 = 7, CM_GRAY10 = 8, CM_GRAY8 = 9, CM_TEXT = 10, CM_RGB48ext = 11, CM_RGB42ext = 12, CM_RGB36ext = 13, CM_RGB30ext = 14, CM_RGB24ext = 15, CM_GRAY16ext = 16, CM_GRAY14ext = 17, CM_GRAY12ext = 18, CM_GRAY10ext = 19, CM_GRAY8ext = 20, CM_TEXText = 21 } COLORMODE, *PCOLORMODE; typedef struct tagTARGETIMAGE { SANE_Bool isOptimalSpeed; COLORMODE cmColorMode; unsigned short wDpi; unsigned short wX; unsigned short wY; unsigned short wWidth; unsigned short wHeight; SANE_Byte bScanSource; } TARGETIMAGE, *PTARGETIMAGE; typedef struct tagSUGGESTSETTING { COLORMODE cmScanMode; unsigned short wXDpi; unsigned short wYDpi; unsigned short wX; unsigned short wY; unsigned short wWidth; unsigned short wHeight; unsigned int dwBytesPerRow; } SUGGESTSETTING, *PSUGGESTSETTING; #endif backends-1.3.0/backend/mustek_usb2_reflective.c000066400000000000000000001446731456256263500215300ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2005 Mustek. Originally maintained by Mustek Author:Jack Roy 2005.5.24 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, see . 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 the Mustek BearPaw 2448 TA Pro and similar USB2 scanners. */ /* forward declarations */ static SANE_Bool Reflective_Reset (void); static SANE_Bool Reflective_ScanSuggest (PTARGETIMAGE pTarget, PSUGGESTSETTING pSuggest); static SANE_Bool Reflective_SetupScan (COLORMODE ColorMode, unsigned short XDpi, unsigned short YDpi, SANE_Bool isInvert, unsigned short X, unsigned short Y, unsigned short Width, unsigned short Height); static SANE_Bool Reflective_StopScan (void); static SANE_Bool Reflective_GetRows (SANE_Byte * lpBlock, unsigned short * Rows, SANE_Bool isOrderInvert); static SANE_Bool Reflective_AdjustAD (void); static SANE_Bool Reflective_FindTopLeft (unsigned short * lpwStartX, unsigned short * lpwStartY); static SANE_Bool Reflective_LineCalibration16Bits (void); static SANE_Bool Reflective_PrepareScan (void); /*function description*/ /********************************************************************** Author: Jack Date: 2005/05/13 Routine Description: reset the scanner status Parameters: none Return value: if operation is success return TRUE els return FALSE ***********************************************************************/ static SANE_Bool Reflective_Reset () { DBG (DBG_FUNC, "Reflective_Reset: call in\n"); if (g_bOpened) { DBG (DBG_FUNC, "Reflective_Reset: scanner has been opened\n"); return FALSE; } if (SANE_STATUS_GOOD != Asic_Open (&g_chip, g_pDeviceFile)) { DBG (DBG_FUNC, "Reflective_Reset: Asic_Open return error\n"); return FALSE; } if (SANE_STATUS_GOOD != Asic_Reset (&g_chip)) { DBG (DBG_FUNC, "Reflective_Reset: Asic_Reset return error\n"); return FALSE; } if (SANE_STATUS_GOOD != Asic_SetSource (&g_chip, LS_REFLECTIVE)) { DBG (DBG_FUNC, "Reflective_Reset: Asic_SetSource return error\n"); return FALSE; } if (SANE_STATUS_GOOD != Asic_TurnLamp (&g_chip, TRUE)) { DBG (DBG_FUNC, "Reflective_Reset: Asic_TurnLamp return error\n"); return FALSE; } if (SANE_STATUS_GOOD != Asic_Close (&g_chip)) { DBG (DBG_FUNC, "Reflective_Reset: Asic_Close return error\n"); return FALSE; } g_Y = 0; g_X = 0; g_Width = 0; g_SWWidth = 0; g_Height = 0; g_SWHeight = 0; g_wLineartThreshold = 128; g_dwTotalTotalXferLines = 0; g_bFirstReadImage = TRUE; g_pGammaTable = NULL; if (NULL != g_pDeviceFile) { free (g_pDeviceFile); g_pDeviceFile = NULL; } DBG (DBG_FUNC, "Reflective_Reset: exit\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/13 Routine Description: get the suggest parameter of scanning Parameters: pTarget: the information of scanning pSuggest: suggest parameter of scanning Return value: if the operation is success return TRUE els return FALSE ***********************************************************************/ static SANE_Bool Reflective_ScanSuggest (PTARGETIMAGE pTarget, PSUGGESTSETTING pSuggest) { int i; unsigned short wMaxWidth, wMaxHeight; DBG (DBG_FUNC, "Reflective_ScanSuggest: call in\n"); if (NULL == pTarget || NULL == pSuggest) { DBG (DBG_FUNC, "Reflective_ScanSuggest: parameters error\n"); return FALSE; } /*1. Looking up Optical Y Resolution */ for (i = 0; s_wOpticalYDpi[i] != 0; i++) { if (s_wOpticalYDpi[i] <= pTarget->wDpi) { pSuggest->wYDpi = s_wOpticalYDpi[i]; break; } } if (s_wOpticalYDpi[i] == 0) { i--; pSuggest->wYDpi = s_wOpticalYDpi[i]; } /*2. Looking up Optical X Resolution */ for (i = 0; s_wOpticalXDpi[i] != 0; i++) { if (s_wOpticalXDpi[i] <= pTarget->wDpi) { pSuggest->wXDpi = s_wOpticalXDpi[i]; break; } } if (s_wOpticalXDpi[i] == 0) { i--; pSuggest->wXDpi = s_wOpticalXDpi[i]; } DBG (DBG_FUNC, "Reflective_ScanSuggest: pTarget->wDpi = %d\n", pTarget->wDpi); DBG (DBG_FUNC, "Reflective_ScanSuggest: pSuggest->wXDpi = %d\n", pSuggest->wXDpi); DBG (DBG_FUNC, "Reflective_ScanSuggest: pSuggest->wYDpi = %d\n", pSuggest->wYDpi); /*3. suggest scan area */ pSuggest->wX = (unsigned short) (((unsigned int) (pTarget->wX) * (unsigned int) (pSuggest->wXDpi)) / (unsigned int) (pTarget->wDpi)); pSuggest->wY = (unsigned short) (((unsigned int) (pTarget->wY) * (unsigned int) (pSuggest->wYDpi)) / (unsigned int) (pTarget->wDpi)); pSuggest->wWidth = (unsigned short) (((unsigned int) (pTarget->wWidth) * (unsigned int) (pSuggest->wXDpi)) / (unsigned int) (pTarget->wDpi)); pSuggest->wHeight = (unsigned short) (((unsigned int) (pTarget->wHeight) * (unsigned int) (pSuggest->wYDpi)) / (unsigned int) (pTarget->wDpi)); pSuggest->wWidth = (pSuggest->wWidth / 2) * 2; DBG (DBG_FUNC, "Reflective_ScanSuggest: pTarget->wX = %d\n", pTarget->wX); DBG (DBG_FUNC, "Reflective_ScanSuggest: pTarget->wY = %d\n", pTarget->wY); DBG (DBG_FUNC, "Reflective_ScanSuggest: pTarget->wWidth = %d\n", pTarget->wWidth); DBG (DBG_FUNC, "Reflective_ScanSuggest: pTarget->wHeight = %d\n", pTarget->wHeight); DBG (DBG_FUNC, "Reflective_ScanSuggest: pSuggest->wX = %d\n", pSuggest->wX); DBG (DBG_FUNC, "Reflective_ScanSuggest: pSuggest->wY = %d\n", pSuggest->wY); DBG (DBG_FUNC, "Reflective_ScanSuggest: pSuggest->wWidth = %d\n", pSuggest->wWidth); DBG (DBG_FUNC, "Reflective_ScanSuggest: pSuggest->wHeight = %d\n", pSuggest->wHeight); if (pTarget->cmColorMode == CM_TEXT) { pSuggest->wWidth = ((pSuggest->wWidth + 7) >> 3) << 3; if (pSuggest->wWidth < 8) pSuggest->wWidth = 8; } /*4. check width and height */ wMaxWidth = (MAX_SCANNING_WIDTH * pSuggest->wXDpi) / 300; wMaxHeight = (3480 * pSuggest->wYDpi) / 300; /* 3480 for bumping */ DBG (DBG_FUNC, "Reflective_ScanSuggest: wMaxWidth = %d\n", wMaxWidth); DBG (DBG_FUNC, "Reflective_ScanSuggest: wMaxHeight = %d\n", wMaxHeight); if (CM_TEXT == pTarget->cmColorMode) { wMaxWidth = (wMaxWidth >> 3) << 3; } if (pSuggest->wWidth > wMaxWidth) { pSuggest->wWidth = wMaxWidth; } if (pSuggest->wHeight > wMaxHeight) { pSuggest->wHeight = wMaxHeight; } DBG (DBG_FUNC, "Reflective_ScanSuggest: g_Width=%d\n", g_Width); g_Width = ((pSuggest->wWidth + 15) >> 4) << 4; /*Real Scan Width */ DBG (DBG_FUNC, "Reflective_ScanSuggest: again, g_Width=%d\n", g_Width); g_Height = pSuggest->wHeight; if (pTarget->isOptimalSpeed) { switch (pTarget->cmColorMode) { case CM_RGB48: pSuggest->cmScanMode = CM_RGB48; pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth) * 6); break; case CM_RGB24: pSuggest->cmScanMode = CM_RGB24ext; pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth) * 3); break; case CM_GRAY16: pSuggest->cmScanMode = CM_GRAY16ext; pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth) * 2); break; case CM_GRAY8: pSuggest->cmScanMode = CM_GRAY8ext; pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth)); break; case CM_TEXT: pSuggest->cmScanMode = CM_TEXT; pSuggest->dwBytesPerRow = (unsigned int) (pSuggest->wWidth) / 8; break; default: break; } } else { switch (pTarget->cmColorMode) { case CM_RGB48: pSuggest->cmScanMode = CM_RGB48; pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth) * 6); break; case CM_RGB24: pSuggest->cmScanMode = CM_RGB24ext; pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth) * 3); break; case CM_GRAY16: pSuggest->cmScanMode = CM_GRAY16ext; pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth) * 2); break; case CM_GRAY8: pSuggest->cmScanMode = CM_GRAY8ext; pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth)); break; case CM_TEXT: pSuggest->cmScanMode = CM_TEXT; pSuggest->dwBytesPerRow = (unsigned int) (pSuggest->wWidth) / 8; break; default: break; } } DBG (DBG_FUNC, "Reflective_ScanSuggest: pSuggest->dwBytesPerRow = %d\n", pSuggest->dwBytesPerRow); DBG (DBG_FUNC, "Reflective_ScanSuggest: leave Reflective_ScanSuggest\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/13 Routine Description: setup scanning process Parameters: ColorMode: ScanMode of Scanning, CM_RGB48, CM_GRAY and so on XDpi: X Resolution YDpi: Y Resolution isInvert: the RGB order X: X start coordinate Y: Y start coordinate Width: Width of Scan Image Height: Height of Scan Image Return value: if the operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool Reflective_SetupScan (COLORMODE ColorMode, unsigned short XDpi, unsigned short YDpi, SANE_Bool isInvert, unsigned short X, unsigned short Y, unsigned short Width, unsigned short Height) { (void) isInvert; DBG (DBG_FUNC, "Reflective_SetupScan: Call in\n"); if (g_bOpened) { DBG (DBG_FUNC, "Reflective_SetupScan: scanner has been opened\n"); return FALSE; } if (!g_bPrepared) { DBG (DBG_FUNC, "Reflective_SetupScan: scanner not prepared\n"); return FALSE; } g_ScanMode = ColorMode; g_XDpi = XDpi; g_YDpi = YDpi; g_SWWidth = Width; g_SWHeight = Height; switch (g_YDpi) { case 1200: g_wPixelDistance = 4; /*even & odd sensor problem */ g_wLineDistance = 24; g_Height += g_wPixelDistance; break; case 600: g_wPixelDistance = 0; /*no even & odd problem */ g_wLineDistance = 12; break; case 300: g_wPixelDistance = 0; g_wLineDistance = 6; break; case 150: g_wPixelDistance = 0; g_wLineDistance = 3; break; case 75: case 50: g_wPixelDistance = 0; g_wLineDistance = 1; break; default: g_wLineDistance = 0; } switch (g_ScanMode) { case CM_RGB48: g_BytesPerRow = 6 * g_Width; g_SWBytesPerRow = 6 * g_SWWidth; g_bScanBits = 48; g_Height += g_wLineDistance * 2; /*add height to do line distance */ break; case CM_RGB24ext: g_BytesPerRow = 3 * g_Width; g_SWBytesPerRow = 3 * g_SWWidth; g_bScanBits = 24; g_Height += g_wLineDistance * 2; /*add height to do line distance */ break; case CM_GRAY16ext: g_BytesPerRow = 2 * g_Width; g_SWBytesPerRow = 2 * g_SWWidth; g_bScanBits = 16; break; case CM_GRAY8ext: case CM_TEXT: g_BytesPerRow = g_Width; g_SWBytesPerRow = g_SWWidth; g_bScanBits = 8; break; default: break; } if (Asic_Open (&g_chip, g_pDeviceFile) != SANE_STATUS_GOOD) { DBG (DBG_FUNC, "Reflective_SetupScan: Asic_Open return error\n"); return FALSE; } DBG (DBG_FUNC, "Reflective_SetupScan: Asic_Open successfully\n"); g_bOpened = TRUE; Asic_TurnLamp (&g_chip, FALSE); Asic_TurnTA (&g_chip, FALSE); Asic_TurnLamp (&g_chip, TRUE); if (1200 == g_XDpi) { g_XDpi = 600; if (Reflective_AdjustAD () == FALSE) { DBG (DBG_FUNC, "Reflective_SetupScan: Reflective_AdjustAD return error\n"); return FALSE; } DBG (DBG_FUNC, "Reflective_SetupScan: Reflective_AdjustAD successfully\n"); if (Reflective_FindTopLeft (&g_X, &g_Y) == FALSE) { g_X = 187; g_Y = 43; } g_XDpi = 1200; if (Reflective_AdjustAD () == FALSE) { DBG (DBG_FUNC, "Reflective_SetupScan: Reflective_AdjustAD return error\n"); return FALSE; } DBG (DBG_FUNC, "Reflective_SetupScan: Reflective_AdjustAD successfully\n"); } else { if (Reflective_AdjustAD () == FALSE) { DBG (DBG_FUNC, "Reflective_SetupScan: Reflective_AdjustAD return error\n"); return FALSE; } DBG (DBG_FUNC, "Reflective_SetupScan: Reflective_AdjustAD successfully\n"); if (Reflective_FindTopLeft (&g_X, &g_Y) == FALSE) { g_X = 187; g_Y = 43; } } DBG (DBG_FUNC, "after find top left,g_X=%d,g_Y=%d\n", g_X, g_Y); if (1200 == g_XDpi) { g_X = g_X * 1200 / FIND_LEFT_TOP_CALIBRATE_RESOLUTION + X * 1200 / g_XDpi + 47; } else { if (75 == g_XDpi) { g_X = g_X + X * 600 / g_XDpi; } else { g_X = g_X + X * 600 / g_XDpi + 23; } } g_Y = g_Y * 1200 / FIND_LEFT_TOP_CALIBRATE_RESOLUTION + Y * 1200 / g_YDpi + 47; DBG (DBG_FUNC, "before line calibration,g_X=%d,g_Y=%d\n", g_X, g_Y); if (Reflective_LineCalibration16Bits () == FALSE) { DBG (DBG_FUNC, "Reflective_SetupScan: Reflective_LineCalibration16Bits return error\n"); return FALSE; } DBG (DBG_FUNC, "Reflective_SetupScan: after Reflective_LineCalibration16Bits,g_X=%d,g_Y=%d\n", g_X, g_Y); DBG (DBG_FUNC, "Reflective_SetupScan: before Asic_SetWindow\n"); DBG (DBG_FUNC, "Reflective_SetupScan: g_bScanBits=%d, g_XDpi=%d, g_YDpi=%d, g_X=%d, g_Y=%d, g_Width=%d, g_Height=%d\n", g_bScanBits, g_XDpi, g_YDpi, g_X, g_Y, g_Width, g_Height); if (g_Y > 300) { Asic_MotorMove (&g_chip, TRUE, g_Y - 300); } else { Asic_MotorMove (&g_chip, FALSE, 300 - g_Y); } g_Y = 300; Asic_SetWindow (&g_chip, g_bScanBits, g_XDpi, g_YDpi, g_X, g_Y, g_Width, g_Height); DBG (DBG_FUNC, "Reflective_SetupScan: leave Reflective_SetupScan\n"); return Reflective_PrepareScan (); } /********************************************************************** Author: Jack Date: 2005/05/13 Routine Description: To adjust the value of offset gain of R/G/B Parameters: none Return value: if operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool Reflective_AdjustAD () { SANE_Byte * lpCalData; unsigned short wCalWidth; int nTimesOfCal; unsigned short wMaxValueR, wMinValueR, wMaxValueG, wMinValueG, wMaxValueB, wMinValueB; #if 0 SANE_Byte bDarkMaxLevel; SANE_Byte bDarkMinLevel; SANE_Byte bLastMinR, bLastROffset, bROffsetUpperBound = 255, bROffsetLowerBound = 0; SANE_Byte bLastMinG, bLastGOffset, bGOffsetUpperBound = 255, bGOffsetLowerBound = 0; SANE_Byte bLastMinB, bLastBOffset, bBOffsetUpperBound = 255, bBOffsetLowerBound = 0; float fRFactor = 1.0; float fGFactor = 1.0; float fBFactor = 1.0; #endif unsigned short wAdjustADResolution; DBG (DBG_FUNC, "Reflective_AdjustAD: call in\n"); if (!g_bOpened) { DBG (DBG_FUNC, "Reflective_AdjustAD: scanner has been opened\n"); return FALSE; } if (!g_bPrepared) { DBG (DBG_FUNC, "Reflective_AdjustAD: scanner not prepared\n"); return FALSE; } g_chip.AD.DirectionR = R_DIRECTION; g_chip.AD.DirectionG = G_DIRECTION; g_chip.AD.DirectionB = B_DIRECTION; g_chip.AD.GainR = R_GAIN; g_chip.AD.GainG = G_GAIN; g_chip.AD.GainB = B_GAIN; g_chip.AD.OffsetR = 152; g_chip.AD.OffsetG = 56; g_chip.AD.OffsetB = 8; if (g_XDpi <= 600) { wAdjustADResolution = 600; } else { wAdjustADResolution = 1200; } wCalWidth = 10240; lpCalData = (SANE_Byte *) malloc (sizeof (SANE_Byte) * wCalWidth * 3); if (lpCalData == NULL) { DBG (DBG_FUNC, "Reflective_AdjustAD: lpCalData malloc error\n"); return FALSE; } Asic_SetMotorType (&g_chip, FALSE, TRUE); Asic_SetCalibrate (&g_chip, 24, wAdjustADResolution, wAdjustADResolution, 0, 0, wCalWidth, 1, FALSE); MustScanner_PrepareCalculateMaxMin (wAdjustADResolution); nTimesOfCal = 0; #ifdef DEBUG_SAVE_IMAGE Asic_SetAFEGainOffset (&g_chip); Asic_ScanStart (&g_chip); Asic_ReadCalibrationData (&g_chip, lpCalData, wCalWidth * 3, 24); Asic_ScanStop (&g_chip); FILE *stream = NULL; SANE_Byte * lpBuf = (SANE_Byte *) malloc (50); if (NULL == lpBuf) { return FALSE; } memset (lpBuf, 0, 50); stream = fopen ("/root/AD(Ref).pnm\n", "wb+\n"); sprintf (lpBuf, "P6\n%d %d\n255\n\n", wCalWidth, 1); fwrite (lpBuf, sizeof (SANE_Byte), strlen (lpBuf), stream); fwrite (lpCalData, sizeof (SANE_Byte), wCalWidth * 3, stream); fclose (stream); free (lpBuf); #endif do { DBG (DBG_FUNC, "Reflective_AdjustAD: run in first adjust offset do-while\n"); Asic_SetAFEGainOffset (&g_chip); Asic_ScanStart (&g_chip); Asic_ReadCalibrationData (&g_chip, lpCalData, wCalWidth * 3, 24); Asic_ScanStop (&g_chip); MustScanner_CalculateMaxMin (lpCalData, &wMaxValueR, &wMinValueR, wAdjustADResolution); MustScanner_CalculateMaxMin (lpCalData + wCalWidth, &wMaxValueG, &wMinValueG, wAdjustADResolution); MustScanner_CalculateMaxMin (lpCalData + wCalWidth * 2, &wMaxValueB, &wMinValueB, wAdjustADResolution); if (g_chip.AD.DirectionR == 0) { if (wMinValueR > 15) { if (g_chip.AD.OffsetR < 8) g_chip.AD.DirectionR = 1; else g_chip.AD.OffsetR -= 8; } else if (wMinValueR < 5) g_chip.AD.OffsetR += 8; } else { if (wMinValueR > 15) g_chip.AD.OffsetR += 8; else if (wMinValueR < 5) g_chip.AD.OffsetR -= 8; } if (g_chip.AD.DirectionG == 0) { if (wMinValueG > 15) { if (g_chip.AD.OffsetG < 8) g_chip.AD.DirectionG = 1; else g_chip.AD.OffsetG -= 8; } else if (wMinValueG < 5) g_chip.AD.OffsetG += 8; } else { if (wMinValueG > 15) g_chip.AD.OffsetG += 8; else if (wMinValueG < 5) g_chip.AD.OffsetG -= 8; } if (g_chip.AD.DirectionB == 0) { if (wMinValueB > 15) { if (g_chip.AD.OffsetB < 8) g_chip.AD.DirectionB = 1; else g_chip.AD.OffsetB -= 8; } else if (wMinValueB < 5) g_chip.AD.OffsetB += 8; } else { if (wMinValueB > 15) g_chip.AD.OffsetB += 8; else if (wMinValueB < 5) g_chip.AD.OffsetB -= 8; } nTimesOfCal++; if (nTimesOfCal > 10) break; } while (wMinValueR > 15 || wMinValueR < 5 || wMinValueG > 15 || wMinValueG < 5 || wMinValueB > 15 || wMinValueB < 5); DBG (DBG_FUNC, "Reflective_AdjustAD: run out first adjust offset do-while\n"); DBG (DBG_FUNC, "Reflective_AdjustAD: \ g_chip.AD.OffsetR=%d,\ g_chip.AD.OffsetG=%d,\ g_chip.AD.OffsetB=%d\n", g_chip.AD.OffsetR, g_chip.AD.OffsetG, g_chip.AD.OffsetB); g_chip.AD.GainR = 1 - (double) (wMaxValueR - wMinValueR) / 210 > 0 ? (SANE_Byte) (((1 - (double) (wMaxValueR - wMinValueR) / 210)) * 63 * 6 / 5) : 0; g_chip.AD.GainG = 1 - (double) (wMaxValueG - wMinValueG) / 210 > 0 ? (SANE_Byte) (((1 - (double) (wMaxValueG - wMinValueG) / 210)) * 63 * 6 / 5) : 0; g_chip.AD.GainB = 1 - (double) (wMaxValueB - wMinValueB) / 210 > 0 ? (SANE_Byte) (((1 - (double) (wMaxValueB - wMinValueB) / 210)) * 63 * 6 / 5) : 0; if (g_chip.AD.GainR > 63) g_chip.AD.GainR = 63; if (g_chip.AD.GainG > 63) g_chip.AD.GainG = 63; if (g_chip.AD.GainB > 63) g_chip.AD.GainB = 63; DBG (DBG_FUNC, "Reflective_AdjustAD: " "g_chip.AD.GainR = %d," "g_chip.AD.GainG = %d," "g_chip.AD.GainB = %d\n", g_chip.AD.GainR, g_chip.AD.GainG, g_chip.AD.GainB); nTimesOfCal = 0; do { Asic_SetAFEGainOffset (&g_chip); Asic_ScanStart (&g_chip); Asic_ReadCalibrationData (&g_chip, lpCalData, wCalWidth * 3, 24); Asic_ScanStop (&g_chip); MustScanner_CalculateMaxMin (lpCalData, &wMaxValueR, &wMinValueR, wAdjustADResolution); MustScanner_CalculateMaxMin (lpCalData + wCalWidth, &wMaxValueG, &wMinValueG, wAdjustADResolution); MustScanner_CalculateMaxMin (lpCalData + wCalWidth * 2, &wMaxValueB, &wMinValueB, wAdjustADResolution); DBG (DBG_FUNC, "Reflective_AdjustAD: " "RGain=%d, ROffset=%d, RDir=%d GGain=%d, GOffset=%d, GDir=%d BGain=%d, BOffset=%d, BDir=%d\n", g_chip.AD.GainR, g_chip.AD.OffsetR, g_chip.AD.DirectionR, g_chip.AD.GainG, g_chip.AD.OffsetG, g_chip.AD.DirectionG, g_chip.AD.GainB, g_chip.AD.OffsetB, g_chip.AD.DirectionB); DBG (DBG_FUNC, "Reflective_AdjustAD: " "MaxR=%d, MinR=%d MaxG=%d, MinG=%d MaxB=%d, MinB=%d\n", wMaxValueR, wMinValueR, wMaxValueG, wMinValueG, wMaxValueB, wMinValueB); /*R Channel */ if ((wMaxValueR - wMinValueR) > REFL_MAX_LEVEL_RANGE) { if (g_chip.AD.GainR > 0) g_chip.AD.GainR--; } else { if ((wMaxValueR - wMinValueR) < REFL_MIN_LEVEL_RANGE) { if (wMaxValueR < REFL_WHITE_MIN_LEVEL) { g_chip.AD.GainR++; if (g_chip.AD.GainR > 63) g_chip.AD.GainR = 63; } else { if (wMaxValueR > REFL_WHITE_MAX_LEVEL) { if (g_chip.AD.GainR < 1) g_chip.AD.GainR = 0; else g_chip.AD.GainR--; } else { if (g_chip.AD.GainR > 63) g_chip.AD.GainR = 63; else g_chip.AD.GainR++; } } } else { if (wMaxValueR > REFL_WHITE_MAX_LEVEL) { if (g_chip.AD.GainR < 1) g_chip.AD.GainR = 0; else g_chip.AD.GainR--; } if (wMaxValueR < REFL_WHITE_MIN_LEVEL) { if (g_chip.AD.GainR > 63) g_chip.AD.GainR = 63; else g_chip.AD.GainR++; } } } /*G Channel */ if ((wMaxValueG - wMinValueG) > REFL_MAX_LEVEL_RANGE) { if (g_chip.AD.GainG > 0) g_chip.AD.GainG--; } else { if ((wMaxValueG - wMinValueG) < REFL_MIN_LEVEL_RANGE) { if (wMaxValueG < REFL_WHITE_MIN_LEVEL) { g_chip.AD.GainG++; if (g_chip.AD.GainG > 63) g_chip.AD.GainG = 63; } else { if (wMaxValueG > REFL_WHITE_MAX_LEVEL) { if (g_chip.AD.GainG < 1) g_chip.AD.GainG = 0; else g_chip.AD.GainG--; } else { if (g_chip.AD.GainG > 63) g_chip.AD.GainG = 63; else g_chip.AD.GainG++; } } } else { if (wMaxValueG > REFL_WHITE_MAX_LEVEL) { if (g_chip.AD.GainG < 1) g_chip.AD.GainG = 0; else g_chip.AD.GainG--; } if (wMaxValueG < REFL_WHITE_MIN_LEVEL) { if (g_chip.AD.GainG > 63) g_chip.AD.GainG = 63; else g_chip.AD.GainG++; } } } /* B Channel */ if ((wMaxValueB - wMinValueB) > REFL_MAX_LEVEL_RANGE) { if (g_chip.AD.GainB > 0) g_chip.AD.GainB--; } else { if ((wMaxValueB - wMinValueB) < REFL_MIN_LEVEL_RANGE) { if (wMaxValueB < REFL_WHITE_MIN_LEVEL) { g_chip.AD.GainB++; if (g_chip.AD.GainB > 63) g_chip.AD.GainB = 63; } else { if (wMaxValueB > REFL_WHITE_MAX_LEVEL) { if (g_chip.AD.GainB < 1) g_chip.AD.GainB = 0; else g_chip.AD.GainB--; } else { if (g_chip.AD.GainB > 63) g_chip.AD.GainB = 63; else g_chip.AD.GainB++; } } } else { if (wMaxValueB > REFL_WHITE_MAX_LEVEL) { if (g_chip.AD.GainB < 1) g_chip.AD.GainB = 0; else g_chip.AD.GainB--; } if (wMaxValueB < REFL_WHITE_MIN_LEVEL) { if (g_chip.AD.GainB > 63) g_chip.AD.GainB = 63; else g_chip.AD.GainB++; } } } nTimesOfCal++; if (nTimesOfCal > 10) break; } while ((wMaxValueR - wMinValueR) > REFL_MAX_LEVEL_RANGE || (wMaxValueR - wMinValueR) < REFL_MIN_LEVEL_RANGE || (wMaxValueG - wMinValueG) > REFL_MAX_LEVEL_RANGE || (wMaxValueG - wMinValueG) < REFL_MIN_LEVEL_RANGE || (wMaxValueB - wMinValueB) > REFL_MAX_LEVEL_RANGE || (wMaxValueB - wMinValueB) < REFL_MIN_LEVEL_RANGE); /* Adjust Offset 2nd */ nTimesOfCal = 0; do { DBG (DBG_FUNC, "Reflective_AdjustAD: run in second adjust offset do-while\n"); Asic_SetAFEGainOffset (&g_chip); Asic_ScanStart (&g_chip); Asic_ReadCalibrationData (&g_chip, lpCalData, wCalWidth * 3, 24); Asic_ScanStop (&g_chip); MustScanner_CalculateMaxMin (lpCalData, &wMaxValueR, &wMinValueR, wAdjustADResolution); MustScanner_CalculateMaxMin (lpCalData + wCalWidth, &wMaxValueG, &wMinValueG, wAdjustADResolution); MustScanner_CalculateMaxMin (lpCalData + wCalWidth * 2, &wMaxValueB, &wMinValueB, wAdjustADResolution); DBG (DBG_FUNC, "Reflective_AdjustAD: " "RGain=%d, ROffset=%d, RDir=%d GGain=%d, GOffset=%d, GDir=%d BGain=%d, BOffset=%d, BDir=%d\n", g_chip.AD.GainR, g_chip.AD.OffsetR, g_chip.AD.DirectionR, g_chip.AD.GainG, g_chip.AD.OffsetG, g_chip.AD.DirectionG, g_chip.AD.GainB, g_chip.AD.OffsetB, g_chip.AD.DirectionB); DBG (DBG_FUNC, "Reflective_AdjustAD: " "MaxR=%d, MinR=%d MaxG=%d, MinG=%d MaxB=%d, MinB=%d\n", wMaxValueR, wMinValueR, wMaxValueG, wMinValueG, wMaxValueB, wMinValueB); if (g_chip.AD.DirectionR == 0) { if (wMinValueR > 20) { if (g_chip.AD.OffsetR < 8) g_chip.AD.DirectionR = 1; else g_chip.AD.OffsetR -= 8; } else if (wMinValueR < 10) g_chip.AD.OffsetR += 8; } else { if (wMinValueR > 20) g_chip.AD.OffsetR += 8; else if (wMinValueR < 10) g_chip.AD.OffsetR -= 8; } if (g_chip.AD.DirectionG == 0) { if (wMinValueG > 20) { if (g_chip.AD.OffsetG < 8) g_chip.AD.DirectionG = 1; else g_chip.AD.OffsetG -= 8; } else if (wMinValueG < 10) g_chip.AD.OffsetG += 8; } else { if (wMinValueG > 20) g_chip.AD.OffsetG += 8; else if (wMinValueG < 10) g_chip.AD.OffsetG -= 8; } if (g_chip.AD.DirectionB == 0) { if (wMinValueB > 20) { if (g_chip.AD.OffsetB < 8) g_chip.AD.DirectionB = 1; else g_chip.AD.OffsetB -= 8; } else if (wMinValueB < 10) g_chip.AD.OffsetB += 8; } else { if (wMinValueB > 20) g_chip.AD.OffsetB += 8; else if (wMinValueB < 10) g_chip.AD.OffsetB -= 8; } nTimesOfCal++; if (nTimesOfCal > 8) break; } while (wMinValueR > 20 || wMinValueR < 10 || wMinValueG > 20 || wMinValueG < 10 || wMinValueB > 20 || wMinValueB < 10); DBG (DBG_FUNC, "Reflective_AdjustAD: run in second adjust offset do-while\n"); DBG (DBG_FUNC, "Reflective_AdjustAD:after ad gain\n"); DBG (DBG_FUNC, "Reflective_AdjustAD: " "g_chip.AD.GainR = %d," "g_chip.AD.GainG = %d," "g_chip.AD.GainB = %d\n", g_chip.AD.GainR, g_chip.AD.GainG, g_chip.AD.GainB); free (lpCalData); DBG (DBG_FUNC, "Reflective_AdjustAD: leave Reflective_AdjustAD\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/14 Routine Description: Find top and left side Parameters: lpwStartX: the left side lpwStartY: the top side Return value: if operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool Reflective_FindTopLeft (unsigned short * lpwStartX, unsigned short * lpwStartY) { unsigned short wCalWidth = FIND_LEFT_TOP_WIDTH_IN_DIP; unsigned short wCalHeight = FIND_LEFT_TOP_HEIGHT_IN_DIP; int i, j; unsigned short wLeftSide; unsigned short wTopSide; int nScanBlock; SANE_Byte * lpCalData; unsigned int dwTotalSize; unsigned short wXResolution, wYResolution; DBG (DBG_FUNC, "Reflective_FindTopLeft: call in\n"); if (!g_bOpened) { DBG (DBG_FUNC, "Reflective_FindTopLeft: scanner has been opened\n"); return FALSE; } if (!g_bPrepared) { DBG (DBG_FUNC, "Reflective_FindTopLeft: scanner not prepared\n"); return FALSE; } wXResolution = wYResolution = FIND_LEFT_TOP_CALIBRATE_RESOLUTION; lpCalData = (SANE_Byte *) malloc (sizeof (SANE_Byte) * wCalWidth * wCalHeight); if (lpCalData == NULL) { DBG (DBG_FUNC, "Reflective_FindTopLeft: lpCalData malloc error\n"); return FALSE; } dwTotalSize = wCalWidth * wCalHeight; nScanBlock = (int) (dwTotalSize / g_dwCalibrationSize); Asic_SetMotorType (&g_chip, TRUE, TRUE); Asic_SetCalibrate (&g_chip, 8, wXResolution, wYResolution, 0, 0, wCalWidth, wCalHeight, FALSE); Asic_SetAFEGainOffset (&g_chip); if (Asic_ScanStart (&g_chip) != SANE_STATUS_GOOD) { DBG (DBG_FUNC, "Reflective_FindTopLeft: Asic_ScanStart return error\n"); free (lpCalData); return FALSE; } for (i = 0; i < nScanBlock; i++) { if (SANE_STATUS_GOOD != Asic_ReadCalibrationData (&g_chip, lpCalData + i * g_dwCalibrationSize, g_dwCalibrationSize, 8)) { DBG (DBG_FUNC, "Reflective_FindTopLeft: Asic_ReadCalibrationData return error\n"); free (lpCalData); return FALSE; } } if (SANE_STATUS_GOOD != Asic_ReadCalibrationData (&g_chip, lpCalData + (nScanBlock) * g_dwCalibrationSize, (dwTotalSize - g_dwCalibrationSize * nScanBlock), 8)) { DBG (DBG_FUNC, "Reflective_FindTopLeft: Asic_ReadCalibrationData return error\n"); free (lpCalData); return FALSE; } Asic_ScanStop (&g_chip); #ifdef DEBUG_SAVE_IMAGE FILE *stream = NULL; stream = fopen ("/root/bound(Ref).pnm", "wb+\n"); SANE_Byte * lpBuf = (SANE_Byte *) malloc (50); if (NULL == lpBuf) { return FALSE; } memset (lpBuf, 0, 50); sprintf (lpBuf, "P5\n%d %d\n255\n", wCalWidth, wCalHeight); fwrite (lpBuf, sizeof (SANE_Byte), strlen (lpBuf), stream); fwrite (lpCalData, sizeof (SANE_Byte), wCalWidth * wCalHeight, stream); fclose (stream); free (lpBuf); #endif wLeftSide = 0; wTopSide = 0; /* Find Left Side */ for (i = wCalWidth - 1; i > 0; i--) { wLeftSide = *(lpCalData + i); wLeftSide += *(lpCalData + wCalWidth * 2 + i); wLeftSide += *(lpCalData + wCalWidth * 4 + i); wLeftSide += *(lpCalData + wCalWidth * 6 + i); wLeftSide += *(lpCalData + wCalWidth * 8 + i); wLeftSide /= 5; if (wLeftSide < 60) { if (i == wCalWidth - 1) { break; } *lpwStartX = i; { break; } } } /*Find Top Side i=left side */ for (j = 0; j < wCalHeight; j++) { wTopSide = *(lpCalData + wCalWidth * j + i - 2); wTopSide += *(lpCalData + wCalWidth * j + i - 4); wTopSide += *(lpCalData + wCalWidth * j + i - 6); wTopSide += *(lpCalData + wCalWidth * j + i - 8); wTopSide += *(lpCalData + wCalWidth * j + i - 10); wTopSide /= 5; if (wTopSide > 60) { if (j == 0) { break; } *lpwStartY = j; { break; } } } if ((*lpwStartX < 100) || (*lpwStartX > 250)) { *lpwStartX = 187; } if ((*lpwStartY < 10) || (*lpwStartY > 100)) { *lpwStartY = 43; } DBG (DBG_FUNC, "Reflective_FindTopLeft: *lpwStartY = %d, *lpwStartX = %d\n", *lpwStartY, *lpwStartX); Asic_MotorMove (&g_chip, FALSE, (wCalHeight - *lpwStartY + BEFORE_SCANNING_MOTOR_FORWARD_PIXEL) * 1200 / wYResolution); free (lpCalData); DBG (DBG_FUNC, "Reflective_FindTopLeft: leave Reflective_FindTopLeft\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/14 Routine Description: Stop scan Parameters: none Return value: if operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool Reflective_StopScan () { DBG (DBG_FUNC, "Reflective_StopScan: call in\n"); if (!g_bOpened) { DBG (DBG_FUNC, "Reflective_StopScan: scanner not opened\n"); return FALSE; } if (!g_bPrepared) { DBG (DBG_FUNC, "Reflective_StopScan: scanner not prepared\n"); return FALSE; } g_isCanceled = TRUE; /*tell parent process stop read image */ pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "Reflective_StopScan: thread exit\n"); Asic_ScanStop (&g_chip); Asic_Close (&g_chip); g_bOpened = FALSE; DBG (DBG_FUNC, "Reflective_StopScan: leave Reflective_StopScan\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/15 Routine Description: Get the calibration data Parameters: none Return value: if the operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool Reflective_LineCalibration16Bits () { SANE_Status status; SANE_Byte * lpWhiteData; SANE_Byte * lpDarkData; unsigned int dwWhiteTotalSize; unsigned int dwDarkTotalSize; unsigned short wCalHeight = LINE_CALIBRATION__16BITS_HEIGHT; unsigned short wCalWidth; unsigned short *lpWhiteShading; unsigned short *lpDarkShading; double wRWhiteLevel = 0; double wGWhiteLevel = 0; double wBWhiteLevel = 0; unsigned int dwRDarkLevel = 0; unsigned int dwGDarkLevel = 0; unsigned int dwBDarkLevel = 0; unsigned int dwREvenDarkLevel = 0; unsigned int dwGEvenDarkLevel = 0; unsigned int dwBEvenDarkLevel = 0; unsigned short * lpRWhiteSort; unsigned short * lpGWhiteSort; unsigned short * lpBWhiteSort; unsigned short * lpRDarkSort; unsigned short * lpGDarkSort; unsigned short * lpBDarkSort; int i, j; DBG (DBG_FUNC, "Reflective_LineCalibration16Bits: call in\n"); if (!g_bOpened) { DBG (DBG_FUNC, "Reflective_LineCalibration16Bits: scanner not opened\n"); return FALSE; } if (!g_bPrepared) { DBG (DBG_FUNC, "Reflective_LineCalibration16Bits: scanner not prepared\n"); return FALSE; } wCalWidth = g_Width; dwWhiteTotalSize = wCalWidth * wCalHeight * 3 * 2; dwDarkTotalSize = wCalWidth * wCalHeight * 3 * 2; lpWhiteData = (SANE_Byte *) malloc (sizeof (SANE_Byte) * dwWhiteTotalSize); lpDarkData = (SANE_Byte *) malloc (sizeof (SANE_Byte) * dwDarkTotalSize); if (lpWhiteData == NULL || lpDarkData == NULL) { DBG (DBG_FUNC, "Reflective_LineCalibration16Bits: lpWhiteData or lpDarkData malloc error \n"); return FALSE; } Asic_SetMotorType (&g_chip, TRUE, TRUE); Asic_SetAFEGainOffset (&g_chip); status = Asic_SetCalibrate (&g_chip, 48, g_XDpi, g_YDpi, g_X, 0, wCalWidth, wCalHeight, TRUE); if (status != SANE_STATUS_GOOD) { DBG (DBG_FUNC, "Reflective_LineCalibration16Bits: Asic_SetCalibrate return error \n"); free (lpWhiteData); free (lpDarkData); return FALSE; } status = Asic_ScanStart (&g_chip); if (status != SANE_STATUS_GOOD) { DBG (DBG_FUNC, "Reflective_LineCalibration16Bits: Asic_ScanStart return error \n"); free (lpWhiteData); free (lpDarkData); return FALSE; } status = Asic_ReadCalibrationData (&g_chip, lpWhiteData, dwWhiteTotalSize, 8); if (status != SANE_STATUS_GOOD) { free (lpWhiteData); free (lpDarkData); return FALSE; } Asic_ScanStop (&g_chip); /*Read dark level data */ status = Asic_SetMotorType (&g_chip, FALSE, TRUE); if (status != SANE_STATUS_GOOD) { DBG (DBG_FUNC, "Reflective_LineCalibration16Bits: Asic_SetMotorType return error \n"); free (lpWhiteData); free (lpDarkData); return FALSE; } status = Asic_SetCalibrate (&g_chip, 48, g_XDpi, g_YDpi, g_X, 0, wCalWidth, wCalHeight, TRUE); if (status != SANE_STATUS_GOOD) { DBG (DBG_FUNC, "Reflective_LineCalibration16Bits: Asic_SetCalibrate return error \n"); free (lpWhiteData); free (lpDarkData); return FALSE; } status = Asic_TurnLamp (&g_chip, FALSE); if (status != SANE_STATUS_GOOD) { DBG (DBG_FUNC, "Reflective_LineCalibration16Bits: Asic_TurnLamp return error \n"); free (lpWhiteData); free (lpDarkData); return FALSE; } usleep (500000); status = Asic_ScanStart (&g_chip); if (status != SANE_STATUS_GOOD) { DBG (DBG_FUNC, "Reflective_LineCalibration16Bits: Asic_ScanStart return error \n"); free (lpWhiteData); free (lpDarkData); return FALSE; } status = Asic_ReadCalibrationData (&g_chip, lpDarkData, dwDarkTotalSize, 8); if (status != SANE_STATUS_GOOD) { DBG (DBG_FUNC, "Reflective_LineCalibration16Bits: Asic_ReadCalibrationData return error \n"); free (lpWhiteData); free (lpDarkData); return FALSE; } Asic_ScanStop (&g_chip); /* Turn on lamp */ status = Asic_TurnLamp (&g_chip, TRUE); if (status != SANE_STATUS_GOOD) { DBG (DBG_FUNC, "Reflective_LineCalibration16Bits: Asic_TurnLamp return error \n"); free (lpWhiteData); free (lpDarkData); return FALSE; } #ifdef DEBUG_SAVE_IMAGE FILE *stream = NULL; SANE_Byte * lpBuf = (SANE_Byte *) malloc (50); if (NULL == lpBuf) { return FALSE; } memset (lpBuf, 0, 50); stream = fopen ("/root/whiteshading(Ref).pnm", "wb+\n"); sprintf (lpBuf, "P6\n%d %d\n65535\n", wCalWidth, wCalHeight); fwrite (lpBuf, sizeof (SANE_Byte), strlen (lpBuf), stream); fwrite (lpWhiteData, sizeof (SANE_Byte), wCalWidth * wCalHeight * 3 * 2, stream); fclose (stream); memset (lpBuf, 0, 50); stream = fopen ("/root/darkshading(Ref).pnm", "wb+\n"); sprintf (lpBuf, "P6\n%d %d\n65535\n", wCalWidth, wCalHeight); fwrite (lpBuf, sizeof (SANE_Byte), strlen (lpBuf), stream); fwrite (lpDarkData, sizeof (SANE_Byte), wCalWidth * wCalHeight * 3 * 2, stream); fclose (stream); free (lpBuf); #endif sleep (1); lpWhiteShading = (unsigned short *) malloc (sizeof (unsigned short) * wCalWidth * 3); lpDarkShading = (unsigned short *) malloc (sizeof (unsigned short) * wCalWidth * 3); lpRWhiteSort = (unsigned short *) malloc (sizeof (unsigned short) * wCalHeight); lpGWhiteSort = (unsigned short *) malloc (sizeof (unsigned short) * wCalHeight); lpBWhiteSort = (unsigned short *) malloc (sizeof (unsigned short) * wCalHeight); lpRDarkSort = (unsigned short *) malloc (sizeof (unsigned short) * wCalHeight); lpGDarkSort = (unsigned short *) malloc (sizeof (unsigned short) * wCalHeight); lpBDarkSort = (unsigned short *) malloc (sizeof (unsigned short) * wCalHeight); if (lpWhiteShading == NULL || lpDarkShading == NULL || lpRWhiteSort == NULL || lpGWhiteSort == NULL || lpBWhiteSort == NULL || lpRDarkSort == NULL || lpGDarkSort == NULL || lpBDarkSort == NULL) { DBG (DBG_FUNC, "Reflective_LineCalibration16Bits: malloc error \n"); free (lpWhiteData); free (lpDarkData); return FALSE; } /* create dark level shading */ dwRDarkLevel = 0; dwGDarkLevel = 0; dwBDarkLevel = 0; dwREvenDarkLevel = 0; dwGEvenDarkLevel = 0; dwBEvenDarkLevel = 0; DBG (DBG_FUNC, "Reflective_LineCalibration16Bits: wCalWidth = %d, wCalHeight = %d\n", wCalWidth, wCalHeight); for (i = 0; i < wCalWidth; i++) { for (j = 0; j < wCalHeight; j++) { lpRDarkSort[j] = (unsigned short) (*(lpDarkData + j * wCalWidth * 6 + i * 6 + 0)); lpRDarkSort[j] += (unsigned short) (*(lpDarkData + j * wCalWidth * 6 + i * 6 + 1) << 8); lpGDarkSort[j] = (unsigned short) (*(lpDarkData + j * wCalWidth * 6 + i * 6 + 2)); lpGDarkSort[j] += (unsigned short) (*(lpDarkData + j * wCalWidth * 6 + i * 6 + 3) << 8); lpBDarkSort[j] = (unsigned short) (*(lpDarkData + j * wCalWidth * 6 + i * 6 + 4)); lpBDarkSort[j] += (unsigned short) (*(lpDarkData + j * wCalWidth * 6 + i * 6 + 5) << 8); } if (g_XDpi == 1200) { /*do dark shading table with mean */ if (i % 2) { dwRDarkLevel += (unsigned int) MustScanner_FiltLower (lpRDarkSort, wCalHeight, 20, 30); dwGDarkLevel += (unsigned int) MustScanner_FiltLower (lpGDarkSort, wCalHeight, 20, 30); dwBDarkLevel += (unsigned int) MustScanner_FiltLower (lpBDarkSort, wCalHeight, 20, 30); } else { dwREvenDarkLevel += (unsigned int) MustScanner_FiltLower (lpRDarkSort, wCalHeight, 20, 30); dwGEvenDarkLevel += (unsigned int) MustScanner_FiltLower (lpGDarkSort, wCalHeight, 20, 30); dwBEvenDarkLevel += (unsigned int) MustScanner_FiltLower (lpBDarkSort, wCalHeight, 20, 30); } } else { dwRDarkLevel += (unsigned int) MustScanner_FiltLower (lpRDarkSort, wCalHeight, 20, 30); dwGDarkLevel += (unsigned int) MustScanner_FiltLower (lpGDarkSort, wCalHeight, 20, 30); dwBDarkLevel += (unsigned int) MustScanner_FiltLower (lpBDarkSort, wCalHeight, 20, 30); } } if (g_XDpi == 1200) { dwRDarkLevel = (unsigned int) (dwRDarkLevel / (wCalWidth / 2)); dwGDarkLevel = (unsigned int) (dwGDarkLevel / (wCalWidth / 2)); dwBDarkLevel = (unsigned int) (dwBDarkLevel / (wCalWidth / 2)); dwREvenDarkLevel = (unsigned int) (dwREvenDarkLevel / (wCalWidth / 2)); dwGEvenDarkLevel = (unsigned int) (dwGEvenDarkLevel / (wCalWidth / 2)); dwBEvenDarkLevel = (unsigned int) (dwBEvenDarkLevel / (wCalWidth / 2)); } else { dwRDarkLevel = (unsigned int) (dwRDarkLevel / wCalWidth); dwGDarkLevel = (unsigned int) (dwGDarkLevel / wCalWidth); dwBDarkLevel = (unsigned int) (dwBDarkLevel / wCalWidth); } /*Create white shading */ for (i = 0; i < wCalWidth; i++) { wRWhiteLevel = 0; wGWhiteLevel = 0; wBWhiteLevel = 0; for (j = 0; j < wCalHeight; j++) { lpRWhiteSort[j] = (unsigned short) (*(lpWhiteData + j * wCalWidth * 2 * 3 + i * 6 + 0)); lpRWhiteSort[j] += (unsigned short) (*(lpWhiteData + j * wCalWidth * 2 * 3 + i * 6 + 1) << 8); lpGWhiteSort[j] = (unsigned short) (*(lpWhiteData + j * wCalWidth * 2 * 3 + i * 6 + 2)); lpGWhiteSort[j] += (unsigned short) (*(lpWhiteData + j * wCalWidth * 2 * 3 + i * 6 + 3) << 8); lpBWhiteSort[j] = (unsigned short) (*(lpWhiteData + j * wCalWidth * 2 * 3 + i * 6 + 4)); lpBWhiteSort[j] += (unsigned short) (*(lpWhiteData + j * wCalWidth * 2 * 3 + i * 6 + 5) << 8); } if (g_XDpi == 1200) { if (i % 2) { *(lpDarkShading + i * 3 + 0) = (unsigned short) dwRDarkLevel; *(lpDarkShading + i * 3 + 1) = (unsigned short) dwGDarkLevel; *(lpDarkShading + i * 3 + 2) = (unsigned short) dwBDarkLevel; } else { *(lpDarkShading + i * 3 + 0) = (unsigned short) dwREvenDarkLevel; *(lpDarkShading + i * 3 + 1) = (unsigned short) dwGEvenDarkLevel; *(lpDarkShading + i * 3 + 2) = (unsigned short) dwBEvenDarkLevel; } } else { *(lpDarkShading + i * 3 + 0) = (unsigned short) dwRDarkLevel; *(lpDarkShading + i * 3 + 1) = (unsigned short) dwGDarkLevel; *(lpDarkShading + i * 3 + 2) = (unsigned short) dwBDarkLevel; } /*Create white shading */ wRWhiteLevel = (double) (MustScanner_FiltLower (lpRWhiteSort, wCalHeight, 20, 30) - *(lpDarkShading + i * 3 + 0)); wGWhiteLevel = (double) (MustScanner_FiltLower (lpGWhiteSort, wCalHeight, 20, 30) - *(lpDarkShading + i * 3 + 1)); wBWhiteLevel = (double) (MustScanner_FiltLower (lpBWhiteSort, wCalHeight, 20, 30) - *(lpDarkShading + i * 3 + 2)); if (wRWhiteLevel > 0) *(lpWhiteShading + i * 3 + 0) = (unsigned short) (((float) 65535 / wRWhiteLevel * 0x2000)); else *(lpWhiteShading + i * 3 + 0) = 0x2000; if (wGWhiteLevel > 0) *(lpWhiteShading + i * 3 + 1) = (unsigned short) (((float) 65535 / wGWhiteLevel * 0x2000)); else *(lpWhiteShading + i * 3 + 1) = 0x2000; if (wBWhiteLevel > 0) *(lpWhiteShading + i * 3 + 2) = (unsigned short) (((float) 65535 / wBWhiteLevel * 0x2000)); else *(lpWhiteShading + i * 3 + 2) = 0x2000; } free (lpWhiteData); free (lpDarkData); free (lpRWhiteSort); free (lpGWhiteSort); free (lpBWhiteSort); free (lpRDarkSort); free (lpGDarkSort); free (lpBDarkSort); Asic_SetShadingTable (&g_chip, lpWhiteShading, lpDarkShading, g_XDpi, wCalWidth, 0); free (lpWhiteShading); free (lpDarkShading); DBG (DBG_FUNC, "Reflective_LineCalibration16Bits: leave Reflective_LineCalibration16Bits\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/14 Routine Description: Prepare scan image Parameters: none Return value: if operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool Reflective_PrepareScan () { g_wScanLinesPerBlock = g_dwBufferSize / g_BytesPerRow; g_wMaxScanLines = g_dwImageBufferSize / g_BytesPerRow; g_wMaxScanLines = (g_wMaxScanLines / g_wScanLinesPerBlock) * g_wScanLinesPerBlock; g_isCanceled = FALSE; g_dwScannedTotalLines = 0; g_wReadedLines = 0; g_wtheReadyLines = 0; g_wReadImageLines = 0; g_wReadyShadingLine = 0; g_wStartShadingLinePos = 0; switch (g_ScanMode) { case CM_RGB48: g_wtheReadyLines = g_wLineDistance * 2 + g_wPixelDistance; DBG (DBG_FUNC, "Reflective_PrepareScan:g_wtheReadyLines=%d\n", g_wtheReadyLines); DBG (DBG_FUNC, "Reflective_PrepareScan:g_lpReadImageHead malloc %d Bytes\n", g_dwImageBufferSize); g_lpReadImageHead = (SANE_Byte *) malloc (g_dwImageBufferSize); if (g_lpReadImageHead == NULL) { DBG (DBG_FUNC, "Reflective_PrepareScan: g_lpReadImageHead malloc error \n"); return FALSE; } break; case CM_RGB24ext: g_wtheReadyLines = g_wLineDistance * 2 + g_wPixelDistance; DBG (DBG_FUNC, "Reflective_PrepareScan:g_wtheReadyLines=%d\n", g_wtheReadyLines); DBG (DBG_FUNC, "Reflective_PrepareScan:g_lpReadImageHead malloc %d Bytes\n", g_dwImageBufferSize); g_lpReadImageHead = (SANE_Byte *) malloc (g_dwImageBufferSize); if (g_lpReadImageHead == NULL) { DBG (DBG_FUNC, "Reflective_PrepareScan: g_lpReadImageHead malloc error \n"); return FALSE; } break; case CM_GRAY16ext: g_wtheReadyLines = g_wPixelDistance; DBG (DBG_FUNC, "Reflective_PrepareScan:g_wtheReadyLines=%d\n", g_wtheReadyLines); DBG (DBG_FUNC, "Reflective_PrepareScan:g_lpReadImageHead malloc %d Bytes\n", g_dwImageBufferSize); g_lpReadImageHead = (SANE_Byte *) malloc (g_dwImageBufferSize); if (g_lpReadImageHead == NULL) { DBG (DBG_FUNC, "Reflective_PrepareScan: g_lpReadImageHead malloc error \n"); return FALSE; } break; case CM_GRAY8ext: g_wtheReadyLines = g_wPixelDistance; DBG (DBG_FUNC, "Reflective_PrepareScan:g_wtheReadyLines=%d\n", g_wtheReadyLines); DBG (DBG_FUNC, "Reflective_PrepareScan:g_lpReadImageHead malloc %d Bytes\n", g_dwImageBufferSize); g_lpReadImageHead = (SANE_Byte *) malloc (g_dwImageBufferSize); if (g_lpReadImageHead == NULL) { DBG (DBG_FUNC, "Reflective_PrepareScan: g_lpReadImageHead malloc error \n"); return FALSE; } break; case CM_TEXT: g_wtheReadyLines = g_wPixelDistance; DBG (DBG_FUNC, "Reflective_PrepareScan:g_wtheReadyLines=%d\n", g_wtheReadyLines); DBG (DBG_FUNC, "Reflective_PrepareScan:g_lpReadImageHead malloc %d Bytes\n", g_dwImageBufferSize); g_lpReadImageHead = (SANE_Byte *) malloc (g_dwImageBufferSize); if (g_lpReadImageHead == NULL) { DBG (DBG_FUNC, "Reflective_PrepareScan: g_lpReadImageHead malloc error \n"); return FALSE; } break; default: break; } Asic_ScanStart (&g_chip); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/15 Routine Description: Get the data of image Parameters: lpBlock: the data of image Rows: the rows of image isOrderInvert: the RGB order Return value: if the operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool Reflective_GetRows (SANE_Byte * lpBlock, unsigned short * Rows, SANE_Bool isOrderInvert) { DBG (DBG_FUNC, "Reflective_GetRows: call in \n"); if (!g_bOpened) { DBG (DBG_FUNC, "Reflective_GetRows: scanner not opened \n"); return FALSE; } if (!g_bPrepared) { DBG (DBG_FUNC, "Reflective_GetRows: scanner not prepared \n"); return FALSE; } switch (g_ScanMode) { case CM_RGB48: if (g_XDpi == 1200) return MustScanner_GetRgb48BitLine1200DPI (lpBlock, isOrderInvert, Rows); else return MustScanner_GetRgb48BitLine (lpBlock, isOrderInvert, Rows); case CM_RGB24ext: if (g_XDpi == 1200) return MustScanner_GetRgb24BitLine1200DPI (lpBlock, isOrderInvert, Rows); else return MustScanner_GetRgb24BitLine (lpBlock, isOrderInvert, Rows); case CM_GRAY16ext: if (g_XDpi == 1200) return MustScanner_GetMono16BitLine1200DPI (lpBlock, isOrderInvert, Rows); else return MustScanner_GetMono16BitLine (lpBlock, isOrderInvert, Rows); case CM_GRAY8ext: if (g_XDpi == 1200) return MustScanner_GetMono8BitLine1200DPI (lpBlock, isOrderInvert, Rows); else return MustScanner_GetMono8BitLine (lpBlock, isOrderInvert, Rows); case CM_TEXT: if (g_XDpi == 1200) return MustScanner_GetMono1BitLine1200DPI (lpBlock, isOrderInvert, Rows); else return MustScanner_GetMono1BitLine (lpBlock, isOrderInvert, Rows); default: return FALSE; } DBG (DBG_FUNC, "Reflective_GetRows: leave Reflective_GetRows \n"); return FALSE; } /* end of the file ScannerReflective.c */ backends-1.3.0/backend/mustek_usb2_transparent.c000066400000000000000000001335641456256263500217360ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2005 Mustek. Originally maintained by Mustek Author:Jack Roy 2005.5.24 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, see . 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 the Mustek BearPaw 2448 TA Pro and similar USB2 scanners. */ /* forward declarations */ static SANE_Bool Transparent_Reset (void); static SANE_Bool Transparent_ScanSuggest (PTARGETIMAGE pTarget, PSUGGESTSETTING pSuggest); static SANE_Bool Transparent_SetupScan (COLORMODE ColorMode, unsigned short XDpi, unsigned short YDpi, SANE_Bool isInvert, unsigned short X, unsigned short Y, unsigned short Width, unsigned short Height); static SANE_Bool Transparent_StopScan (void); static SANE_Bool Transparent_GetRows (SANE_Byte * lpBlock, unsigned short * Rows, SANE_Bool isOrderInvert); static SANE_Bool Transparent_AdjustAD (void); static SANE_Bool Transparent_FindTopLeft (unsigned short * lpwStartX, unsigned short * lpwStartY); static SANE_Bool Transparent_LineCalibration16Bits (unsigned short wTAShadingMinus); static SANE_Bool Transparent_PrepareScan (void); /*function description*/ /********************************************************************** Author: Jack Date: 2005/05/13 Routine Description: reset the scanner Parameters: none Return value: if operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool Transparent_Reset () { DBG (DBG_FUNC, "Transparent_Reset: call in\n"); if (g_bOpened) { DBG (DBG_FUNC, "Transparent_Reset: scanner has been opened\n"); return FALSE; } if (SANE_STATUS_GOOD != Asic_Open (&g_chip, g_pDeviceFile)) { DBG (DBG_FUNC, "Transparent_Reset: can not open scanner\n"); return FALSE; } if (SANE_STATUS_GOOD != Asic_Reset (&g_chip)) { DBG (DBG_FUNC, "Reflective_Reset: Asic_Reset return error\n"); return FALSE; } if (SANE_STATUS_GOOD != Asic_SetSource (&g_chip, LS_POSITIVE)) { DBG (DBG_FUNC, "Reflective_Reset: Asic_SetSource return error\n"); return FALSE; } if (SANE_STATUS_GOOD != Asic_TurnLamp (&g_chip, FALSE)) { DBG (DBG_FUNC, "Reflective_Reset: Asic_TurnLamp return error\n"); return FALSE; } if (SANE_STATUS_GOOD != Asic_TurnTA (&g_chip, TRUE)) { DBG (DBG_FUNC, "Reflective_Reset: Asic_TurnTA return error\n"); return FALSE; } if (SANE_STATUS_GOOD != Asic_Close (&g_chip)) { DBG (DBG_FUNC, "Reflective_Reset: Asic_Close return error\n"); return FALSE; } g_Y = 0; g_wLineartThreshold = 128; g_dwTotalTotalXferLines = 0; g_bFirstReadImage = TRUE; g_pGammaTable = NULL; DBG (DBG_FUNC, "Transparent_Reset: leave Transparent_Reset\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/13 Routine Description: get suggest parameter of scanning Parameters: pTarget: the information of scanning pSuggest: the suggest parameter of scanning Return value: if operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool Transparent_ScanSuggest (PTARGETIMAGE pTarget, PSUGGESTSETTING pSuggest) { unsigned short wMaxWidth, wMaxHeight; int i; DBG (DBG_FUNC, "Transparent_ScanSuggest: call in\n"); for (i = 0; s_wOpticalYDpi[i] != 0; i++) { if (s_wOpticalYDpi[i] <= pTarget->wDpi) { pSuggest->wYDpi = s_wOpticalYDpi[i]; break; } } if (s_wOpticalYDpi[i] == 0) { i--; pSuggest->wYDpi = s_wOpticalYDpi[i]; } for (i = 0; s_wOpticalXDpi[i] != 0; i++) { if (s_wOpticalXDpi[i] <= pTarget->wDpi) { pSuggest->wXDpi = s_wOpticalXDpi[i]; break; } } if (s_wOpticalXDpi[i] == 0) { i--; pSuggest->wXDpi = s_wOpticalXDpi[i]; } pSuggest->wX = (unsigned short) (((unsigned int) (pTarget->wX) * (unsigned int) (pSuggest->wXDpi)) / (unsigned int) (pTarget->wDpi)); pSuggest->wY = (unsigned short) (((unsigned int) (pTarget->wY) * (unsigned int) (pSuggest->wYDpi)) / (unsigned int) (pTarget->wDpi)); pSuggest->wWidth = (unsigned short) (((unsigned int) (pTarget->wWidth) * (unsigned int) (pSuggest->wXDpi)) / (unsigned int) (pTarget->wDpi)); pSuggest->wHeight = (unsigned short) (((unsigned int) (pTarget->wHeight) * (unsigned int) (pSuggest->wYDpi)) / (unsigned int) (pTarget->wDpi)); pSuggest->wWidth = (pSuggest->wWidth / 2) * 2; if (pTarget->cmColorMode == CM_TEXT) { pSuggest->wWidth = ((pSuggest->wWidth + 7) >> 3) << 3; if (pSuggest->wWidth < 8) pSuggest->wWidth = 8; } g_Width = ((pSuggest->wWidth + 15) >> 4) << 4; /* Real Scan Width */ g_Height = pSuggest->wHeight; wMaxWidth = (MAX_SCANNING_WIDTH * pSuggest->wXDpi) / 300; wMaxHeight = (MAX_SCANNING_HEIGHT * pSuggest->wYDpi) / 300; if (pTarget->cmColorMode == CM_TEXT) wMaxWidth = (wMaxWidth >> 3) << 3; if (pSuggest->wWidth > wMaxWidth) pSuggest->wWidth = wMaxWidth; if (pSuggest->wHeight > wMaxHeight) pSuggest->wHeight = wMaxHeight; if (pTarget->isOptimalSpeed) { DBG (DBG_FUNC, "Transparent_ScanSuggest: isOptimalSpeed is true\n"); switch (pTarget->cmColorMode) { case CM_RGB48: pSuggest->cmScanMode = CM_RGB48; pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth) * 6); break; case CM_RGB24: pSuggest->cmScanMode = CM_RGB24ext; pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth) * 3); break; case CM_GRAY16: pSuggest->cmScanMode = CM_GRAY16ext; pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth) * 2); break; case CM_GRAY8: pSuggest->cmScanMode = CM_GRAY8ext; pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth)); break; case CM_TEXT: pSuggest->cmScanMode = CM_TEXT; pSuggest->dwBytesPerRow = (unsigned int) (pSuggest->wWidth) / 8; break; default: break; } } else { DBG (DBG_FUNC, "Transparent_ScanSuggest: isOptimalSpeed not true\n"); switch (pTarget->cmColorMode) { case CM_RGB48: pSuggest->cmScanMode = CM_RGB48; pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth) * 6); break; case CM_RGB24: pSuggest->cmScanMode = CM_RGB24ext; pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth) * 3); break; case CM_GRAY16: pSuggest->cmScanMode = CM_GRAY16ext; pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth) * 2); break; case CM_GRAY8: pSuggest->cmScanMode = CM_GRAY8ext; pSuggest->dwBytesPerRow = (unsigned int) ((pSuggest->wWidth)); break; case CM_TEXT: pSuggest->cmScanMode = CM_TEXT; pSuggest->dwBytesPerRow = (unsigned int) (pSuggest->wWidth) / 8; break; default: break; } } DBG (DBG_FUNC, "Transparent_ScanSuggest: leave Transparent_ScanSuggest\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/13 Routine Description: setup scanning process Parameters: ColorMode: ScanMode of Scanning, CM_RGB48, CM_GRAY and so on XDpi: X Resolution YDpi: Y Resolution isInvert: the RGB order X: X start coordinate Y: Y start coordinate Width: Width of Scan Image Height: Height of Scan Image Return value: if the operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool Transparent_SetupScan (COLORMODE ColorMode, unsigned short XDpi, unsigned short YDpi, SANE_Bool isInvert, unsigned short X, unsigned short Y, unsigned short Width, unsigned short Height) { SANE_Bool hasTA; unsigned short wTAShadingMinus = 0; (void) isInvert; DBG (DBG_FUNC, "Transparent_SetupScan: call in\n"); if (g_bOpened) { DBG (DBG_FUNC, "Transparent_SetupScan: scanner has been opened\n"); return FALSE; } if (!g_bPrepared) { DBG (DBG_FUNC, "Transparent_SetupScan: scanner not prepared\n"); return FALSE; } g_ScanMode = ColorMode; g_XDpi = XDpi; g_YDpi = YDpi; g_SWWidth = Width; g_SWHeight = Height; switch (g_YDpi) { case 1200: g_wPixelDistance = 4; g_wLineDistance = 24; g_Height += g_wPixelDistance; break; case 600: g_wPixelDistance = 0; g_wLineDistance = 12; g_Height += g_wPixelDistance; break; case 300: g_wPixelDistance = 0; g_wLineDistance = 6; break; case 150: g_wPixelDistance = 0; g_wLineDistance = 3; break; case 75: case 50: g_wPixelDistance = 0; g_wLineDistance = 1; break; default: g_wLineDistance = 0; } DBG (DBG_FUNC, "Transparent_SetupScan: g_YDpi=%d\n", g_YDpi); DBG (DBG_FUNC, "Transparent_SetupScan: g_wLineDistance=%d\n", g_wLineDistance); DBG (DBG_FUNC, "Transparent_SetupScan: g_wPixelDistance=%d\n", g_wPixelDistance); switch (g_ScanMode) { case CM_RGB48: g_BytesPerRow = 6 * g_Width; /* ASIC limit : width must be 8x */ g_SWBytesPerRow = 6 * g_SWWidth; /* ASIC limit : width must be 8x */ g_bScanBits = 48; g_Height += g_wLineDistance * 2; break; case CM_RGB24ext: g_BytesPerRow = 3 * g_Width; /*ASIC limit : width must be 8x */ g_SWBytesPerRow = 3 * g_SWWidth; g_bScanBits = 24; g_Height += g_wLineDistance * 2; break; case CM_GRAY16ext: g_BytesPerRow = 2 * g_Width; /* ASIC limit : width must be 8x */ g_SWBytesPerRow = 2 * g_SWWidth; g_bScanBits = 16; break; case CM_GRAY8ext: case CM_TEXT: g_BytesPerRow = g_Width; /*ASIC limit : width must be 8x */ g_SWBytesPerRow = g_SWWidth; g_bScanBits = 8; break; default: break; } if (Asic_Open (&g_chip, g_pDeviceFile) != SANE_STATUS_GOOD) { DBG (DBG_FUNC, "Transparent_SetupScan: Asic_Open return error\n"); return FALSE; } g_bOpened = TRUE; if (SANE_STATUS_GOOD != Asic_TurnLamp (&g_chip, FALSE)) { DBG (DBG_FUNC, "Transparent_SetupScan: Asic_TurnLamp return error\n"); return FALSE; } if (Asic_IsTAConnected (&g_chip, &hasTA) != SANE_STATUS_GOOD) { DBG (DBG_FUNC, "Transparent_SetupScan: Asic_IsTAConnected return error\n"); return FALSE; } if (!hasTA) { DBG (DBG_FUNC, "Transparent_SetupScan: no TA device\n"); return FALSE; } if (Asic_TurnTA (&g_chip, TRUE) != SANE_STATUS_GOOD) { DBG (DBG_FUNC, "Transparent_SetupScan: Asic_TurnTA return error\n"); return FALSE; } /* Begin Find Left&Top Side */ Asic_MotorMove (&g_chip, TRUE, TRAN_START_POS); if (1200 == g_XDpi) { wTAShadingMinus = 1680; g_XDpi = 600; Transparent_AdjustAD (); Transparent_FindTopLeft (&g_X, &g_Y); g_XDpi = 1200; Transparent_AdjustAD (); } else { wTAShadingMinus = 840; Transparent_AdjustAD (); Transparent_FindTopLeft (&g_X, &g_Y); } DBG (DBG_FUNC, "Transparent_SetupScan: after find top and left g_X=%d, g_Y=%d\n", g_X, g_Y); if (1200 == g_XDpi) { g_X = g_X * 1200 / FIND_LEFT_TOP_CALIBRATE_RESOLUTION + X * 1200 / g_XDpi; } else { if (75 == g_XDpi) { g_X = g_X + X * 600 / g_XDpi - 23; } else { g_X = g_X + X * 600 / g_XDpi; } } DBG (DBG_FUNC, "Transparent_SetupScan: before line calibration,g_X=%d,g_Y=%d\n", g_X, g_Y); Transparent_LineCalibration16Bits (wTAShadingMinus); DBG (DBG_FUNC, "Transparent_SetupScan: after Reflective_LineCalibration16Bits,g_X=%d,g_Y=%d\n", g_X, g_Y); DBG (DBG_FUNC, "Transparent_SetupScan: g_bScanBits=%d, g_XDpi=%d, g_YDpi=%d, g_X=%d, g_Y=%d, g_Width=%d, g_Height=%d\n", g_bScanBits, g_XDpi, g_YDpi, g_X, g_Y, g_Width, g_Height); g_Y = Y * 1200 / g_YDpi + (300 - 40) + 189; Asic_MotorMove (&g_chip, TRUE, g_Y - 360); g_Y = 360; Asic_SetWindow (&g_chip, g_bScanBits, g_XDpi, g_YDpi, g_X, g_Y, g_Width, g_Height); DBG (DBG_FUNC, "Transparent_SetupScan: leave Transparent_SetupScan\n"); return Transparent_PrepareScan (); } /********************************************************************** Author: Jack Date: 2005/05/14 Routine Description: Stop scan Parameters: none Return value: if operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool Transparent_StopScan () { DBG (DBG_FUNC, "Transparent_StopScan: call in\n"); if (!g_bOpened) { return FALSE; } if (!g_bPrepared) { return FALSE; } g_isCanceled = TRUE; pthread_cancel (g_threadid_readimage); pthread_join (g_threadid_readimage, NULL); DBG (DBG_FUNC, "Transparent_StopScan: thread exit\n"); Asic_ScanStop (&g_chip); Asic_Close (&g_chip); g_bOpened = FALSE; DBG (DBG_FUNC, "Transparent_StopScan: leave Transparent_StopScan\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/15 Routine Description: Get the data of image Parameters: lpBlock: the data of image Rows: the rows of image isOrderInvert: the RGB order Return value: if the operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool Transparent_GetRows (SANE_Byte * lpBlock, unsigned short * Rows, SANE_Bool isOrderInvert) { DBG (DBG_FUNC, "Transparent_GetRows: call in\n"); if (!g_bOpened) { return FALSE; } if (!g_bPrepared) { return FALSE; } switch (g_ScanMode) { case CM_RGB48: if (g_XDpi == 1200) return MustScanner_GetRgb48BitLine1200DPI (lpBlock, isOrderInvert, Rows); else return MustScanner_GetRgb48BitLine (lpBlock, isOrderInvert, Rows); case CM_RGB24ext: if (g_XDpi == 1200) return MustScanner_GetRgb24BitLine1200DPI (lpBlock, isOrderInvert, Rows); else return MustScanner_GetRgb24BitLine (lpBlock, isOrderInvert, Rows); case CM_GRAY16ext: if (g_XDpi == 1200) return MustScanner_GetMono16BitLine1200DPI (lpBlock, isOrderInvert, Rows); else return MustScanner_GetMono16BitLine (lpBlock, isOrderInvert, Rows); case CM_GRAY8ext: if (g_XDpi == 1200) return MustScanner_GetMono8BitLine1200DPI (lpBlock, isOrderInvert, Rows); else return MustScanner_GetMono8BitLine (lpBlock, isOrderInvert, Rows); case CM_TEXT: if (g_XDpi == 1200) return MustScanner_GetMono1BitLine1200DPI (lpBlock, isOrderInvert, Rows); else return MustScanner_GetMono1BitLine (lpBlock, isOrderInvert, Rows); default: return FALSE; } return FALSE; } /********************************************************************** Author: Jack Date: 2005/05/13 Routine Description: To adjust the value of offset gain of R/G/B Parameters: none Return value: if operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool Transparent_AdjustAD () { SANE_Byte * lpCalData; unsigned short wCalWidth; int nTimesOfCal; unsigned short wMaxValueR, wMinValueR, wMaxValueG, wMinValueG, wMaxValueB, wMinValueB; #if 0 float fRFactor = 1.0; float fGFactor = 1.0; float fBFactor = 1.0; SANE_Byte bDarkMaxLevel; SANE_Byte bDarkMinLevel; SANE_Byte bLastMinR, bLastROffset, bROffsetUpperBound = 255, bROffsetLowerBound = 0; SANE_Byte bLastMinG, bLastGOffset, bGOffsetUpperBound = 255, bGOffsetLowerBound = 0; SANE_Byte bLastMinB, bLastBOffset, bBOffsetUpperBound = 255, bBOffsetLowerBound = 0; #endif unsigned short wAdjustADResolution; DBG (DBG_FUNC, "Transparent_AdjustAD: call in\n"); if (!g_bOpened) { return FALSE; } if (!g_bPrepared) { return FALSE; } g_chip.AD.DirectionR = R_DIRECTION; g_chip.AD.DirectionG = G_DIRECTION; g_chip.AD.DirectionB = B_DIRECTION; g_chip.AD.GainR = R_GAIN; g_chip.AD.GainG = G_GAIN; g_chip.AD.GainB = B_GAIN; g_chip.AD.OffsetR = 159; g_chip.AD.OffsetG = 50; g_chip.AD.OffsetB = 45; if (g_XDpi <= 600) { wAdjustADResolution = 600; } else { wAdjustADResolution = 1200; } wCalWidth = 10240; lpCalData = (SANE_Byte *) malloc (sizeof (SANE_Byte) * wCalWidth * 3); if (lpCalData == NULL) { return FALSE; } Asic_SetMotorType (&g_chip, FALSE, TRUE); Asic_SetCalibrate (&g_chip, 24, wAdjustADResolution, wAdjustADResolution, 0, 0, wCalWidth, 1, FALSE); MustScanner_PrepareCalculateMaxMin (wAdjustADResolution); nTimesOfCal = 0; #ifdef DEBUG_SAVE_IMAGE Asic_SetAFEGainOffset (&g_chip); Asic_ScanStart (&g_chip); Asic_ReadCalibrationData (&g_chip, lpCalData, wCalWidth * 3, 24); Asic_ScanStop (&g_chip); FILE *stream = NULL; SANE_Byte * lpBuf = (SANE_Byte *) malloc (50); if (NULL == lpBuf) { DBG (DBG_FUNC, "Transparent_AdjustAD: Leave Transparent_AdjustAD for malloc fail!\n"); return FALSE; } memset (lpBuf, 0, 50); stream = fopen ("/root/AD(Tra).pnm", "wb+\n"); sprintf (lpBuf, "P6\n%d %d\n255\n", wCalWidth, 3); fwrite (lpBuf, sizeof (SANE_Byte), strlen (lpBuf), stream); fwrite (lpCalData, sizeof (SANE_Byte), wCalWidth * 3, stream); fclose (stream); free (lpBuf); #endif do { DBG (DBG_FUNC, "Transparent_AdjustAD: run in first adjust offset do-while\n"); Asic_SetAFEGainOffset (&g_chip); Asic_ScanStart (&g_chip); Asic_ReadCalibrationData (&g_chip, lpCalData, wCalWidth * 3, 24); Asic_ScanStop (&g_chip); MustScanner_CalculateMaxMin (lpCalData, &wMaxValueR, &wMinValueR, wAdjustADResolution); MustScanner_CalculateMaxMin (lpCalData + wCalWidth, &wMaxValueG, &wMinValueG, wAdjustADResolution); MustScanner_CalculateMaxMin (lpCalData + wCalWidth * 2, &wMaxValueB, &wMinValueB, wAdjustADResolution); if (g_chip.AD.DirectionR == 0) { if (wMinValueR > 15) { if (g_chip.AD.OffsetR < 8) g_chip.AD.DirectionR = 1; else g_chip.AD.OffsetR -= 8; } else if (wMinValueR < 5) g_chip.AD.OffsetR += 8; } else { if (wMinValueR > 15) g_chip.AD.OffsetR += 8; else if (wMinValueR < 5) g_chip.AD.OffsetR -= 8; } if (g_chip.AD.DirectionG == 0) { if (wMinValueG > 15) { if (g_chip.AD.OffsetG < 8) g_chip.AD.DirectionG = 1; else g_chip.AD.OffsetG -= 8; } else if (wMinValueG < 5) g_chip.AD.OffsetG += 8; } else { if (wMinValueG > 15) g_chip.AD.OffsetG += 8; else if (wMinValueG < 5) g_chip.AD.OffsetG -= 8; } if (g_chip.AD.DirectionB == 0) { if (wMinValueB > 15) { if (g_chip.AD.OffsetB < 8) g_chip.AD.DirectionB = 1; else g_chip.AD.OffsetB -= 8; } else if (wMinValueB < 5) g_chip.AD.OffsetB += 8; } else { if (wMinValueB > 15) g_chip.AD.OffsetB += 8; else if (wMinValueB < 5) g_chip.AD.OffsetB -= 8; } nTimesOfCal++; if (nTimesOfCal > 10) break; } while (wMinValueR > 15 || wMinValueR < 5 || wMinValueG > 15 || wMinValueG < 5 || wMinValueB > 15 || wMinValueB < 5); g_chip.AD.GainR = 1 - (double) (wMaxValueR - wMinValueR) / 210 > 0 ? (SANE_Byte) (((1 - (double) (wMaxValueR - wMinValueR) / 210)) * 63 * 6 / 5) : 0; g_chip.AD.GainG = 1 - (double) (wMaxValueG - wMinValueG) / 210 > 0 ? (SANE_Byte) (((1 - (double) (wMaxValueG - wMinValueG) / 210)) * 63 * 6 / 5) : 0; g_chip.AD.GainB = 1 - (double) (wMaxValueB - wMinValueB) / 210 > 0 ? (SANE_Byte) (((1 - (double) (wMaxValueB - wMinValueB) / 210)) * 63 * 6 / 5) : 0; if (g_chip.AD.GainR > 63) g_chip.AD.GainR = 63; if (g_chip.AD.GainG > 63) g_chip.AD.GainG = 63; if (g_chip.AD.GainB > 63) g_chip.AD.GainB = 63; nTimesOfCal = 0; do { Asic_SetAFEGainOffset (&g_chip); Asic_ScanStart (&g_chip); Asic_ReadCalibrationData (&g_chip, lpCalData, wCalWidth * 3, 24); Asic_ScanStop (&g_chip); MustScanner_CalculateMaxMin (lpCalData, &wMaxValueR, &wMinValueR, wAdjustADResolution); MustScanner_CalculateMaxMin (lpCalData + wCalWidth, &wMaxValueG, &wMinValueG, wAdjustADResolution); MustScanner_CalculateMaxMin (lpCalData + wCalWidth * 2, &wMaxValueB, &wMinValueB, wAdjustADResolution); DBG (DBG_FUNC, "Transparent_AdjustAD: " "RGain=%d, ROffset=%d, RDir=%d GGain=%d, GOffset=%d, GDir=%d BGain=%d, BOffset=%d, BDir=%d\n", g_chip.AD.GainR, g_chip.AD.OffsetR, g_chip.AD.DirectionR, g_chip.AD.GainG, g_chip.AD.OffsetG, g_chip.AD.DirectionG, g_chip.AD.GainB, g_chip.AD.OffsetB, g_chip.AD.DirectionB); DBG (DBG_FUNC, "Transparent_AdjustAD: " "MaxR=%d, MinR=%d MaxG=%d, MinG=%d MaxB=%d, MinB=%d\n", wMaxValueR, wMinValueR, wMaxValueG, wMinValueG, wMaxValueB, wMinValueB); /*R Channel */ if ((wMaxValueR - wMinValueR) > TRAN_MAX_LEVEL_RANGE) { if (g_chip.AD.GainR > 0) g_chip.AD.GainR--; } else { if ((wMaxValueR - wMinValueR) < TRAN_MIN_LEVEL_RANGE) { if (wMaxValueR < TRAN_WHITE_MIN_LEVEL) { g_chip.AD.GainR++; if (g_chip.AD.GainR > 63) g_chip.AD.GainR = 63; } else { if (wMaxValueR > TRAN_WHITE_MAX_LEVEL) { if (g_chip.AD.GainR < 1) g_chip.AD.GainR = 0; else g_chip.AD.GainR--; } else { if (g_chip.AD.GainR > 63) g_chip.AD.GainR = 63; else g_chip.AD.GainR++; } } } else { if (wMaxValueR > TRAN_WHITE_MAX_LEVEL) { if (g_chip.AD.GainR < 1) g_chip.AD.GainR = 0; else g_chip.AD.GainR--; } if (wMaxValueR < TRAN_WHITE_MIN_LEVEL) { if (g_chip.AD.GainR > 63) g_chip.AD.GainR = 63; else g_chip.AD.GainR++; } } } /*G Channel */ if ((wMaxValueG - wMinValueG) > TRAN_MAX_LEVEL_RANGE) { if (g_chip.AD.GainG > 0) g_chip.AD.GainG--; } else { if ((wMaxValueG - wMinValueG) < TRAN_MIN_LEVEL_RANGE) { if (wMaxValueG < TRAN_WHITE_MIN_LEVEL) { g_chip.AD.GainG++; if (g_chip.AD.GainG > 63) g_chip.AD.GainG = 63; } else { if (wMaxValueG > TRAN_WHITE_MAX_LEVEL) { if (g_chip.AD.GainG < 1) g_chip.AD.GainG = 0; else g_chip.AD.GainG--; } else { if (g_chip.AD.GainG > 63) g_chip.AD.GainG = 63; else g_chip.AD.GainG++; } } } else { if (wMaxValueG > TRAN_WHITE_MAX_LEVEL) { if (g_chip.AD.GainG < 1) g_chip.AD.GainG = 0; else g_chip.AD.GainG--; } if (wMaxValueG < TRAN_WHITE_MIN_LEVEL) { if (g_chip.AD.GainG > 63) g_chip.AD.GainG = 63; else g_chip.AD.GainG++; } } } /* B Channel */ if ((wMaxValueB - wMinValueB) > TRAN_MAX_LEVEL_RANGE) { if (g_chip.AD.GainB > 0) g_chip.AD.GainB--; } else { if ((wMaxValueB - wMinValueB) < TRAN_MIN_LEVEL_RANGE) { if (wMaxValueB < TRAN_WHITE_MIN_LEVEL) { g_chip.AD.GainB++; if (g_chip.AD.GainB > 63) g_chip.AD.GainB = 63; } else { if (wMaxValueB > TRAN_WHITE_MAX_LEVEL) { if (g_chip.AD.GainB < 1) g_chip.AD.GainB = 0; else g_chip.AD.GainB--; } else { if (g_chip.AD.GainB > 63) g_chip.AD.GainB = 63; else g_chip.AD.GainB++; } } } else { if (wMaxValueB > TRAN_WHITE_MAX_LEVEL) { if (g_chip.AD.GainB < 1) g_chip.AD.GainB = 0; else g_chip.AD.GainB--; } if (wMaxValueB < TRAN_WHITE_MIN_LEVEL) { if (g_chip.AD.GainB > 63) g_chip.AD.GainB = 63; else g_chip.AD.GainB++; } } } nTimesOfCal++; if (nTimesOfCal > 10) break; } while ((wMaxValueR - wMinValueR) > TRAN_MAX_LEVEL_RANGE || (wMaxValueR - wMinValueR) < TRAN_MIN_LEVEL_RANGE || (wMaxValueG - wMinValueG) > TRAN_MAX_LEVEL_RANGE || (wMaxValueG - wMinValueG) < TRAN_MIN_LEVEL_RANGE || (wMaxValueB - wMinValueB) > TRAN_MAX_LEVEL_RANGE || (wMaxValueB - wMinValueB) < TRAN_MIN_LEVEL_RANGE); /* Adjust Offset 2nd */ nTimesOfCal = 0; do { Asic_SetAFEGainOffset (&g_chip); Asic_ScanStart (&g_chip); Asic_ReadCalibrationData (&g_chip, lpCalData, wCalWidth * 3, 24); Asic_ScanStop (&g_chip); MustScanner_CalculateMaxMin (lpCalData, &wMaxValueR, &wMinValueR, wAdjustADResolution); MustScanner_CalculateMaxMin (lpCalData + wCalWidth, &wMaxValueG, &wMinValueG, wAdjustADResolution); MustScanner_CalculateMaxMin (lpCalData + wCalWidth * 2, &wMaxValueB, &wMinValueB, wAdjustADResolution); DBG (DBG_FUNC, "Transparent_AdjustAD: " "RGain=%d, ROffset=%d, RDir=%d GGain=%d, GOffset=%d, GDir=%d BGain=%d, BOffset=%d, BDir=%d\n", g_chip.AD.GainR, g_chip.AD.OffsetR, g_chip.AD.DirectionR, g_chip.AD.GainG, g_chip.AD.OffsetG, g_chip.AD.DirectionG, g_chip.AD.GainB, g_chip.AD.OffsetB, g_chip.AD.DirectionB); DBG (DBG_FUNC, "Transparent_AdjustAD: " "MaxR=%d, MinR=%d MaxG=%d, MinG=%d MaxB=%d, MinB=%d\n", wMaxValueR, wMinValueR, wMaxValueG, wMinValueG, wMaxValueB, wMinValueB); if (g_chip.AD.DirectionR == 0) { if (wMinValueR > 20) { if (g_chip.AD.OffsetR < 8) g_chip.AD.DirectionR = 1; else g_chip.AD.OffsetR -= 8; } else if (wMinValueR < 10) g_chip.AD.OffsetR += 8; } else { if (wMinValueR > 20) g_chip.AD.OffsetR += 8; else if (wMinValueR < 10) g_chip.AD.OffsetR -= 8; } if (g_chip.AD.DirectionG == 0) { if (wMinValueG > 20) { if (g_chip.AD.OffsetG < 8) g_chip.AD.DirectionG = 1; else g_chip.AD.OffsetG -= 8; } else if (wMinValueG < 10) g_chip.AD.OffsetG += 8; } else { if (wMinValueG > 20) g_chip.AD.OffsetG += 8; else if (wMinValueG < 10) g_chip.AD.OffsetG -= 8; } if (g_chip.AD.DirectionB == 0) { if (wMinValueB > 20) { if (g_chip.AD.OffsetB < 8) g_chip.AD.DirectionB = 1; else g_chip.AD.OffsetB -= 8; } else if (wMinValueB < 10) g_chip.AD.OffsetB += 8; } else { if (wMinValueB > 20) g_chip.AD.OffsetB += 8; else if (wMinValueB < 10) g_chip.AD.OffsetB -= 8; } nTimesOfCal++; if (nTimesOfCal > 8) break; } while (wMinValueR > 20 || wMinValueR < 10 || wMinValueG > 20 || wMinValueG < 10 || wMinValueB > 20 || wMinValueB < 10); DBG (DBG_FUNC, "Transparent_AdjustAD: leave Transparent_AdjustAD\n"); free (lpCalData); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/14 Routine Description: Find top and left side Parameters: lpwStartX: the left side lpwStartY: the top side Return value: if operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool Transparent_FindTopLeft (unsigned short * lpwStartX, unsigned short * lpwStartY) { unsigned short wCalWidth = TA_FIND_LEFT_TOP_WIDTH_IN_DIP; unsigned short wCalHeight = TA_FIND_LEFT_TOP_HEIGHT_IN_DIP; int i, j; unsigned short wLeftSide; unsigned short wTopSide; int nScanBlock; SANE_Byte * lpCalData; unsigned int dwTotalSize; unsigned short wXResolution, wYResolution; DBG (DBG_FUNC, "Transparent_FindTopLeft: call in\n"); if (!g_bOpened) { DBG (DBG_FUNC, "Transparent_FindTopLeft: scanner not opened\n"); return FALSE; } if (!g_bPrepared) { DBG (DBG_FUNC, "Transparent_FindTopLeft: scanner not prepared\n"); return FALSE; } wXResolution = wYResolution = FIND_LEFT_TOP_CALIBRATE_RESOLUTION; lpCalData = (SANE_Byte *) malloc (sizeof (SANE_Byte) * wCalWidth * wCalHeight); if (lpCalData == NULL) { DBG (DBG_FUNC, "Transparent_FindTopLeft: lpCalData malloc fail\n"); return FALSE; } dwTotalSize = wCalWidth * wCalHeight; nScanBlock = (int) (dwTotalSize / g_dwCalibrationSize); Asic_SetMotorType (&g_chip, TRUE, TRUE); Asic_SetCalibrate (&g_chip, 8, wXResolution, wYResolution, 0, 0, wCalWidth, wCalHeight, FALSE); Asic_SetAFEGainOffset (&g_chip); Asic_ScanStart (&g_chip); for (i = 0; i < nScanBlock; i++) Asic_ReadCalibrationData (&g_chip, lpCalData + i * g_dwCalibrationSize, g_dwCalibrationSize, 8); Asic_ReadCalibrationData (&g_chip, lpCalData + (nScanBlock) * g_dwCalibrationSize, (dwTotalSize - g_dwCalibrationSize * nScanBlock), 8); Asic_ScanStop (&g_chip); #ifdef DEBUG_SAVE_IMAGE FILE *stream = NULL; SANE_Byte * lpBuf = (SANE_Byte *) malloc (50); if (NULL == lpBuf) { return FALSE; } memset (lpBuf, 0, 50); stream = fopen ("/root/bound(Tra).pnm", "wb+\n"); sprintf (lpBuf, "P5\n%d %d\n255\n", wCalWidth, wCalHeight); fwrite (lpBuf, sizeof (SANE_Byte), strlen (lpBuf), stream); fwrite (lpCalData, sizeof (SANE_Byte), wCalWidth * wCalHeight, stream); fclose (stream); free (lpBuf); #endif wLeftSide = 0; wTopSide = 0; /* Find Left Side */ for (i = (wCalWidth - 1); i > 0; i--) { wLeftSide = *(lpCalData + i); wLeftSide += *(lpCalData + wCalWidth * 2 + i); wLeftSide += *(lpCalData + wCalWidth * 4 + i); wLeftSide += *(lpCalData + wCalWidth * 6 + i); wLeftSide += *(lpCalData + wCalWidth * 8 + i); wLeftSide /= 5; if (wLeftSide < 60) { if (i == (wCalWidth - 1)) { break; } *lpwStartX = i; break; } } /* Find Top Side i=left side */ for (j = 0; j < wCalHeight; j++) { wTopSide = *(lpCalData + wCalWidth * j + i + 2); wTopSide += *(lpCalData + wCalWidth * j + i + 4); wTopSide += *(lpCalData + wCalWidth * j + i + 6); wTopSide += *(lpCalData + wCalWidth * j + i + 8); wTopSide += *(lpCalData + wCalWidth * j + i + 10); wTopSide /= 5; if (wTopSide < 60) { if (j == 0) { break; } *lpwStartY = j; break; } } if ((*lpwStartX < 2200) || (*lpwStartX > 2300)) { *lpwStartX = 2260; } if ((*lpwStartY < 100) || (*lpwStartY > 200)) { *lpwStartY = 124; } Asic_MotorMove (&g_chip, FALSE, (wCalHeight - *lpwStartY) * 1200 / wYResolution + 300); free (lpCalData); DBG (DBG_FUNC, "Transparent_FindTopLeft: *lpwStartY = %d, *lpwStartX = %d\n", *lpwStartY, *lpwStartX); DBG (DBG_FUNC, "Transparent_FindTopLeft: leave Transparent_FindTopLeft\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/15 Routine Description: Get the calibration data Parameters: none Return value: if the operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool Transparent_LineCalibration16Bits (unsigned short wTAShadingMinus) { unsigned short *lpWhiteShading; unsigned short *lpDarkShading; double wRWhiteLevel = 0; double wGWhiteLevel = 0; double wBWhiteLevel = 0; unsigned int dwRDarkLevel = 0; unsigned int dwGDarkLevel = 0; unsigned int dwBDarkLevel = 0; unsigned int dwREvenDarkLevel = 0; unsigned int dwGEvenDarkLevel = 0; unsigned int dwBEvenDarkLevel = 0; unsigned short * lpRWhiteSort; unsigned short * lpGWhiteSort; unsigned short * lpBWhiteSort; unsigned short * lpRDarkSort; unsigned short * lpGDarkSort; unsigned short * lpBDarkSort; int i, j; SANE_Byte * lpWhiteData; SANE_Byte * lpDarkData; unsigned int dwWhiteTotalSize; unsigned int dwDarkTotalSize; unsigned short wCalHeight = LINE_CALIBRATION__16BITS_HEIGHT; unsigned short wCalWidth = g_Width; DBG (DBG_FUNC, "Transparent_LineCalibration16Bits: call in\n"); if (!g_bOpened) { DBG (DBG_FUNC, "Transparent_LineCalibration16Bits: scanner not opened\n"); return FALSE; } if (!g_bPrepared) { DBG (DBG_FUNC, "Transparent_LineCalibration16Bits: scanner not prepared\n"); return FALSE; } if (g_XDpi < 600) { wTAShadingMinus = wTAShadingMinus * g_XDpi / 600; } dwWhiteTotalSize = wCalWidth * wCalHeight * 3 * 2; dwDarkTotalSize = wCalWidth * wCalHeight * 3 * 2; lpWhiteData = (SANE_Byte *) malloc (sizeof (SANE_Byte) * dwWhiteTotalSize); lpDarkData = (SANE_Byte *) malloc (sizeof (SANE_Byte) * dwDarkTotalSize); if (lpWhiteData == NULL || lpDarkData == NULL) { DBG (DBG_FUNC, "Transparent_LineCalibration16Bits: lpWhiteData or lpDarkData malloc fail\n"); return FALSE; } /*Read white level data */ Asic_SetMotorType (&g_chip, TRUE, TRUE); Asic_SetAFEGainOffset (&g_chip); Asic_SetCalibrate (&g_chip, 48, g_XDpi, g_YDpi, g_X, 0, wCalWidth, wCalHeight, TRUE); Asic_ScanStart (&g_chip); /* Read Data */ Asic_ReadCalibrationData (&g_chip, lpWhiteData, dwWhiteTotalSize, 8); Asic_ScanStop (&g_chip); /* Read dark level data */ Asic_SetMotorType (&g_chip, FALSE, TRUE); Asic_SetAFEGainOffset (&g_chip); Asic_SetCalibrate (&g_chip, 48, g_XDpi, g_YDpi, g_X, 0, wCalWidth, wCalHeight, TRUE); Asic_TurnLamp (&g_chip, FALSE); Asic_TurnTA (&g_chip, FALSE); usleep (500000); Asic_ScanStart (&g_chip); Asic_ReadCalibrationData (&g_chip, lpDarkData, dwDarkTotalSize, 8); Asic_ScanStop (&g_chip); Asic_TurnTA (&g_chip, TRUE); #ifdef DEBUG_SAVE_IMAGE FILE *stream = NULL; SANE_Byte * lpBuf = (SANE_Byte *) malloc (50); if (NULL == lpBuf) { return FALSE; } memset (lpBuf, 0, 50); stream = fopen ("/root/whiteshading(Tra).pnm", "wb+\n"); sprintf (lpBuf, "P6\n%d %d\n65535\n", wCalWidth, wCalHeight); fwrite (lpBuf, sizeof (SANE_Byte), strlen (lpBuf), stream); fwrite (lpWhiteData, sizeof (SANE_Byte), wCalWidth * wCalHeight * 3 * 2, stream); fclose (stream); memset (lpBuf, 0, 50); stream = fopen ("/root/darkshading(Tra).pnm", "wb+\n"); sprintf (lpBuf, "P6\n%d %d\n65535\n", wCalWidth, wCalHeight); fwrite (lpBuf, sizeof (SANE_Byte), strlen (lpBuf), stream); fwrite (lpDarkData, sizeof (SANE_Byte), wCalWidth * wCalHeight * 3 * 2, stream); fclose (stream); free (lpBuf); #endif lpWhiteShading = (unsigned short *) malloc (sizeof (unsigned short) * wCalWidth * 3); lpDarkShading = (unsigned short *) malloc (sizeof (unsigned short) * wCalWidth * 3); lpRWhiteSort = (unsigned short *) malloc (sizeof (unsigned short) * wCalHeight); lpGWhiteSort = (unsigned short *) malloc (sizeof (unsigned short) * wCalHeight); lpBWhiteSort = (unsigned short *) malloc (sizeof (unsigned short) * wCalHeight); lpRDarkSort = (unsigned short *) malloc (sizeof (unsigned short) * wCalHeight); lpGDarkSort = (unsigned short *) malloc (sizeof (unsigned short) * wCalHeight); lpBDarkSort = (unsigned short *) malloc (sizeof (unsigned short) * wCalHeight); if (lpWhiteShading == NULL || lpDarkShading == NULL || lpRWhiteSort == NULL || lpGWhiteSort == NULL || lpBWhiteSort == NULL || lpRDarkSort == NULL || lpGDarkSort == NULL || lpBDarkSort == NULL) { DBG (DBG_FUNC, "Transparent_LineCalibration16Bits: malloc fail\n"); free (lpWhiteData); free (lpDarkData); return FALSE; } DBG (DBG_FUNC, "Transparent_LineCalibration16Bits: wCalWidth = %d, wCalHeight = %d\n", wCalWidth, wCalHeight); /* create dark level shading */ dwRDarkLevel = 0; dwGDarkLevel = 0; dwBDarkLevel = 0; dwREvenDarkLevel = 0; dwGEvenDarkLevel = 0; dwBEvenDarkLevel = 0; for (i = 0; i < wCalWidth; i++) { for (j = 0; j < wCalHeight; j++) { lpRDarkSort[j] = (unsigned short) (*(lpDarkData + j * wCalWidth * 6 + i * 6 + 0)); lpRDarkSort[j] += (unsigned short) (*(lpDarkData + j * wCalWidth * 6 + i * 6 + 1) << 8); lpGDarkSort[j] = (unsigned short) (*(lpDarkData + j * wCalWidth * 6 + i * 6 + 2)); lpGDarkSort[j] += (unsigned short) (*(lpDarkData + j * wCalWidth * 6 + i * 6 + 3) << 8); lpBDarkSort[j] = (unsigned short) (*(lpDarkData + j * wCalWidth * 6 + i * 6 + 4)); lpBDarkSort[j] += (unsigned short) (*(lpDarkData + j * wCalWidth * 6 + i * 6 + 5) << 8); } /* sum of dark level for all pixels */ if (g_XDpi == 1200) { /* do dark shading table with mean */ if (i % 2) { dwRDarkLevel += (unsigned int) MustScanner_FiltLower (lpRDarkSort, wCalHeight, 20, 30); dwGDarkLevel += (unsigned int) MustScanner_FiltLower (lpGDarkSort, wCalHeight, 20, 30); dwBDarkLevel += (unsigned int) MustScanner_FiltLower (lpBDarkSort, wCalHeight, 20, 30); } else { dwREvenDarkLevel += (unsigned int) MustScanner_FiltLower (lpRDarkSort, wCalHeight, 20, 30); dwGEvenDarkLevel += (unsigned int) MustScanner_FiltLower (lpGDarkSort, wCalHeight, 20, 30); dwBEvenDarkLevel += (unsigned int) MustScanner_FiltLower (lpBDarkSort, wCalHeight, 20, 30); } } else { dwRDarkLevel += (unsigned int) MustScanner_FiltLower (lpRDarkSort, wCalHeight, 20, 30); dwGDarkLevel += (unsigned int) MustScanner_FiltLower (lpGDarkSort, wCalHeight, 20, 30); dwBDarkLevel += (unsigned int) MustScanner_FiltLower (lpBDarkSort, wCalHeight, 20, 30); } } if (g_XDpi == 1200) { dwRDarkLevel = (unsigned int) (dwRDarkLevel / (wCalWidth / 2)) - 512; dwGDarkLevel = (unsigned int) (dwGDarkLevel / (wCalWidth / 2)) - 512; dwBDarkLevel = (unsigned int) (dwBDarkLevel / (wCalWidth / 2)) - 512; dwREvenDarkLevel = (unsigned int) (dwREvenDarkLevel / (wCalWidth / 2)) - 512; dwGEvenDarkLevel = (unsigned int) (dwGEvenDarkLevel / (wCalWidth / 2)) - 512; dwBEvenDarkLevel = (unsigned int) (dwBEvenDarkLevel / (wCalWidth / 2)) - 512; } else { dwRDarkLevel = (unsigned int) (dwRDarkLevel / wCalWidth) - 512; dwGDarkLevel = (unsigned int) (dwGDarkLevel / wCalWidth) - 512; dwBDarkLevel = (unsigned int) (dwBDarkLevel / wCalWidth) - 512; } /* Create white shading */ for (i = 0; i < wCalWidth; i++) { wRWhiteLevel = 0; wGWhiteLevel = 0; wBWhiteLevel = 0; for (j = 0; j < wCalHeight; j++) { lpRWhiteSort[j] = (unsigned short) (*(lpWhiteData + j * wCalWidth * 2 * 3 + i * 6 + 0)); lpRWhiteSort[j] += (unsigned short) (*(lpWhiteData + j * wCalWidth * 2 * 3 + i * 6 + 1) << 8); lpGWhiteSort[j] = (unsigned short) (*(lpWhiteData + j * wCalWidth * 2 * 3 + i * 6 + 2)); lpGWhiteSort[j] += (unsigned short) (*(lpWhiteData + j * wCalWidth * 2 * 3 + i * 6 + 3) << 8); lpBWhiteSort[j] = (unsigned short) (*(lpWhiteData + j * wCalWidth * 2 * 3 + i * 6 + 4)); lpBWhiteSort[j] += (unsigned short) (*(lpWhiteData + j * wCalWidth * 2 * 3 + i * 6 + 5) << 8); } if (1200 == g_XDpi) { if (i % 2) { if (SS_Negative == g_ssScanSource) { *(lpDarkShading + i * 3 + 0) = (unsigned short) dwRDarkLevel; *(lpDarkShading + i * 3 + 1) = (unsigned short) dwGDarkLevel; *(lpDarkShading + i * 3 + 2) = (unsigned short) dwBDarkLevel; } else { *(lpDarkShading + i * 3 + 0) = (unsigned short) dwRDarkLevel; *(lpDarkShading + i * 3 + 1) = (unsigned short) (dwGDarkLevel * 0.78); *(lpDarkShading + i * 3 + 2) = (unsigned short) dwBDarkLevel; } } else { if (SS_Negative == g_ssScanSource) { *(lpDarkShading + i * 3 + 0) = (unsigned short) dwREvenDarkLevel; *(lpDarkShading + i * 3 + 1) = (unsigned short) dwGEvenDarkLevel; *(lpDarkShading + i * 3 + 2) = (unsigned short) dwBEvenDarkLevel; } else { *(lpDarkShading + i * 3 + 0) = (unsigned short) dwREvenDarkLevel; *(lpDarkShading + i * 3 + 1) = (unsigned short) (dwGEvenDarkLevel * 0.78); *(lpDarkShading + i * 3 + 2) = (unsigned short) dwBEvenDarkLevel; } } } else { if (SS_Negative == g_ssScanSource) { *(lpDarkShading + i * 3 + 0) = (unsigned short) dwRDarkLevel; *(lpDarkShading + i * 3 + 1) = (unsigned short) dwRDarkLevel; *(lpDarkShading + i * 3 + 2) = (unsigned short) dwRDarkLevel; } else { *(lpDarkShading + i * 3 + 0) = (unsigned short) dwRDarkLevel; *(lpDarkShading + i * 3 + 1) = (unsigned short) (dwRDarkLevel * 0.78); *(lpDarkShading + i * 3 + 2) = (unsigned short) dwRDarkLevel; } } /* Create white shading */ wRWhiteLevel = (double) (MustScanner_FiltLower (lpRWhiteSort, wCalHeight, 20, 30) - *(lpDarkShading + i * 3 + 0)); wGWhiteLevel = (double) (MustScanner_FiltLower (lpGWhiteSort, wCalHeight, 20, 30) - *(lpDarkShading + i * 3 + 1)); wBWhiteLevel = (double) (MustScanner_FiltLower (lpBWhiteSort, wCalHeight, 20, 30) - *(lpDarkShading + i * 3 + 2)); if (g_ssScanSource == SS_Negative) { if (wRWhiteLevel > 0) *(lpWhiteShading + i * 3 + 0) = (unsigned short) ((float) 65536 / wRWhiteLevel * 0x1000); else *(lpWhiteShading + i * 3 + 0) = 0x1000; if (wGWhiteLevel > 0) *(lpWhiteShading + i * 3 + 1) = (unsigned short) ((float) (65536 * 1.5) / wGWhiteLevel * 0x1000); else *(lpWhiteShading + i * 3 + 1) = 0x1000; if (wBWhiteLevel > 0) *(lpWhiteShading + i * 3 + 2) = (unsigned short) ((float) (65536 * 2.0) / wBWhiteLevel * 0x1000); else *(lpWhiteShading + i * 3 + 2) = 0x1000; } else { if (wRWhiteLevel > 0) *(lpWhiteShading + i * 3 + 0) = (unsigned short) ((float) 65536 / wRWhiteLevel * 0x1000); else *(lpWhiteShading + i * 3 + 0) = 0x1000; if (wGWhiteLevel > 0) *(lpWhiteShading + i * 3 + 1) = (unsigned short) ((float) (65536 * 1.04) / wGWhiteLevel * 0x1000); else *(lpWhiteShading + i * 3 + 1) = 0x1000; if (wBWhiteLevel > 0) *(lpWhiteShading + i * 3 + 2) = (unsigned short) ((float) 65536 / wBWhiteLevel * 0x1000); else *(lpWhiteShading + i * 3 + 2) = 0x1000; } } free (lpWhiteData); free (lpDarkData); free (lpRWhiteSort); free (lpGWhiteSort); free (lpBWhiteSort); free (lpRDarkSort); free (lpGDarkSort); free (lpBDarkSort); Asic_SetShadingTable (&g_chip, lpWhiteShading, lpDarkShading, g_XDpi, wCalWidth, 0); free (lpWhiteShading); free (lpDarkShading); DBG (DBG_FUNC, "Transparent_LineCalibration16Bits: leave Transparent_LineCalibration16Bits\n"); return TRUE; } /********************************************************************** Author: Jack Date: 2005/05/14 Routine Description: Prepare scan image Parameters: none Return value: if operation is success return TRUE else return FALSE ***********************************************************************/ static SANE_Bool Transparent_PrepareScan () { DBG (DBG_FUNC, "Transparent_PrepareScan: call in\n"); g_wScanLinesPerBlock = g_dwBufferSize / g_BytesPerRow; g_wMaxScanLines = g_dwImageBufferSize / g_BytesPerRow; g_wMaxScanLines = (g_wMaxScanLines / g_wScanLinesPerBlock) * g_wScanLinesPerBlock; g_isCanceled = FALSE; g_dwScannedTotalLines = 0; g_wReadedLines = 0; g_wtheReadyLines = 0; g_wReadImageLines = 0; g_wReadyShadingLine = 0; g_wStartShadingLinePos = 0; switch (g_ScanMode) { case CM_RGB48: g_wtheReadyLines = g_wLineDistance * 2 + g_wPixelDistance; g_lpReadImageHead = (SANE_Byte *) malloc (g_dwImageBufferSize); if (g_lpReadImageHead == NULL) { DBG (DBG_FUNC, "Transparent_PrepareScan:malloc fail\n"); return FALSE; } break; case CM_RGB24ext: g_wtheReadyLines = g_wLineDistance * 2 + g_wPixelDistance; g_lpReadImageHead = (SANE_Byte *) malloc (g_dwImageBufferSize); if (g_lpReadImageHead == NULL) { DBG (DBG_FUNC, "Transparent_PrepareScan:malloc fail\n"); return FALSE; } break; case CM_GRAY16ext: g_wtheReadyLines = g_wPixelDistance; g_lpReadImageHead = (SANE_Byte *) malloc (g_dwImageBufferSize); if (g_lpReadImageHead == NULL) { DBG (DBG_FUNC, "Transparent_PrepareScan:malloc fail\n"); return FALSE; } break; case CM_GRAY8ext: g_wtheReadyLines = g_wPixelDistance; g_lpReadImageHead = (SANE_Byte *) malloc (g_dwImageBufferSize); if (g_lpReadImageHead == NULL) { DBG (DBG_FUNC, "Transparent_PrepareScan:malloc fail\n"); return FALSE; } break; case CM_TEXT: g_wtheReadyLines = g_wPixelDistance; g_lpReadImageHead = (SANE_Byte *) malloc (g_dwImageBufferSize); if (g_lpReadImageHead == NULL) { DBG (DBG_FUNC, "Transparent_PrepareScan:malloc fail\n"); return FALSE; } break; default: return FALSE; } Asic_ScanStart (&g_chip); DBG (DBG_FUNC, "Transparent_PrepareScan: leave Transparent_PrepareScan\n"); return TRUE; } /* end of the file ScannerTransparent.c */ backends-1.3.0/backend/mustek_usb_high.c000066400000000000000000002473641456256263500202360ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2000 Mustek. Originally maintained by Tom Wang Copyright (C) 2001, 2002 by Henning Meier-Geinitz. 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, see . As a special exception, the authors of SANE give permission for additional uses of the libraries contained in this release of SANE. The exception is that, if you link a SANE library with other files to produce an executable, this does not by itself cause the resulting executable to be covered by the GNU General Public License. Your use of that executable is in no way restricted on account of linking the SANE library code into it. This exception does not, however, invalidate any other reasons why the executable file might be covered by the GNU General Public License. If you submit changes to SANE to the maintainers to be included in a subsequent release, you agree by submitting the changes that those changes may be distributed with this exception intact. If you write modifications of your own for SANE, it is your choice whether to permit this exception to apply to your modifications. If you do not wish that, delete this exception notice. This file implements a SANE backend for Mustek 1200UB and similar USB flatbed scanners. */ #include "mustek_usb_high.h" #include "mustek_usb_mid.c" /* ------------------------ calibration functions ------------------------- */ static SANE_Byte gray_map[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; static inline double filter_lower_end (SANE_Int * buffer, SANE_Word total_count, SANE_Word filter_count) { SANE_Word bound = total_count - 1; SANE_Word left_count = total_count - filter_count; SANE_Int temp = 0; SANE_Word i, j; SANE_Int sum = 0; for (i = 0; i < bound; i++) { for (j = 0; j < bound - i; j++) { if (buffer[j + 1] > buffer[j]) { temp = buffer[j]; buffer[j] = buffer[j + 1]; buffer[j + 1] = temp; } } } for (i = 0; i < left_count; i++) sum += buffer[i]; return (double) sum; } SANE_Status usb_high_cal_init (Calibrator * cal, SANE_Byte type, SANE_Word target_white, SANE_Word target_dark) { DBG (5, "usb_high_cal_init: start, cal=%p, type=%d, target_white=%d " "target_dark=%d\n", (void *) cal, type, target_white, target_dark); cal->is_prepared = SANE_FALSE; cal->k_white = NULL; cal->k_dark = NULL; /* Working Buffer */ cal->white_line = NULL; cal->dark_line = NULL; cal->white_buffer = NULL; /* Necessary Parameters */ cal->k_white_level = 240 << 8; cal->k_dark_level = 0; cal->threshold = 2048; cal->major_average = 0; cal->minor_average = 0; cal->filter = 0; cal->white_needed = 0; cal->dark_needed = 0; cal->max_width = 0; cal->width = 100; cal->gamma_table = 0; cal->calibrator_type = type; cal->k_white_level = target_white / 16; cal->k_dark_level = 0; DBG (5, "usb_high_cal_init: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_cal_exit (Calibrator * cal) { DBG (5, "usb_high_cal_exit: start\n"); if (!cal) { DBG (3, "usb_high_cal_exit: cal == NULL\n"); return SANE_STATUS_INVAL; } if (!cal->is_prepared) { DBG (3, "usb_high_cal_exit: !is_prepared\n"); return SANE_STATUS_INVAL; } DBG (5, "usb_high_cal_exit: 1\n"); if (cal->k_dark) { free (cal->k_dark); } cal->k_dark = NULL; DBG (5, "usb_high_cal_exit: 2\n"); if (cal->k_white) { free (cal->k_white); } cal->k_white = NULL; DBG (5, "usb_high_cal_exit: 3\n"); cal->is_prepared = SANE_FALSE; DBG (5, "usb_high_cal_exit: 4\n"); DBG (5, "usb_high_cal_exit: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_cal_embed_gamma (Calibrator * cal, SANE_Word * gamma_table) { DBG (5, "usb_high_cal_embed_gamma: start\n"); cal->gamma_table = gamma_table; DBG (5, "usb_high_cal_embed_gamma: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_cal_prepare (Calibrator * cal, SANE_Word max_width) { DBG (5, "usb_high_cal_Parepare: start\n"); if (cal->is_prepared) { DBG (3, "usb_high_cal_Parepare: is_prepared\n"); return SANE_STATUS_INVAL; } if (cal->k_white) { free (cal->k_white); } cal->k_white = (SANE_Word *) malloc (max_width * sizeof (SANE_Word)); if (!cal->k_white) return SANE_STATUS_NO_MEM; if (cal->k_dark) { free (cal->k_dark); } cal->k_dark = (SANE_Word *) malloc (max_width * sizeof (SANE_Word)); if (!cal->k_dark) return SANE_STATUS_NO_MEM; cal->max_width = max_width; cal->is_prepared = SANE_TRUE; DBG (5, "usb_high_cal_Parepare: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_cal_setup (Calibrator * cal, SANE_Word major_average, SANE_Word minor_average, SANE_Word filter, SANE_Word width, SANE_Word * white_needed, SANE_Word * dark_needed) { SANE_Int i; DBG (5, "usb_high_cal_setup: start\n"); if (!cal->is_prepared) { DBG (3, "usb_high_cal_setup: !is_prepared\n"); return SANE_STATUS_INVAL; } if (major_average == 0) { DBG (3, "usb_high_cal_setup: major_average==0\n"); return SANE_STATUS_INVAL; } if (minor_average == 0) { DBG (3, "usb_high_cal_setup: minor_average==0\n"); return SANE_STATUS_INVAL; } if (width > cal->max_width) { DBG (3, "usb_high_cal_setup: width>max_width\n"); return SANE_STATUS_INVAL; } cal->major_average = major_average; cal->minor_average = minor_average; cal->filter = filter; cal->width = width; cal->white_needed = major_average * 16 + filter; cal->dark_needed = major_average * 16; *white_needed = cal->white_needed; *dark_needed = cal->dark_needed; if (cal->white_line) { free (cal->white_line); } cal->white_line = (double *) malloc (cal->width * sizeof (double)); if (!cal->white_line) return SANE_STATUS_NO_MEM; if (cal->dark_line) { free (cal->dark_line); } cal->dark_line = (double *) malloc (cal->width * sizeof (double)); if (!cal->dark_line) return SANE_STATUS_NO_MEM; for (i = 0; i < cal->width; i++) { cal->white_line[i] = 0.0; cal->dark_line[i] = 0.0; } if (cal->white_buffer) { free (cal->white_buffer); } cal->white_buffer = (SANE_Int *) malloc (cal->white_needed * cal->width * sizeof (SANE_Int)); if (!cal->white_buffer) return SANE_STATUS_NO_MEM; for (i = 0; i < cal->white_needed * cal->width; i++) { *(cal->white_buffer + i) = 0; } return SANE_STATUS_GOOD; DBG (5, "usb_high_cal_setup: start\n"); } SANE_Status usb_high_cal_evaluate_white (Calibrator * cal, double factor) { /* Calculate white_line */ double loop_division; double average; SANE_Int *buffer; SANE_Word i, j; DBG (5, "usb_high_cal_evaluate_white: start\n"); loop_division = (double) (cal->major_average * cal->minor_average); buffer = (SANE_Int *) malloc (cal->white_needed * sizeof (SANE_Int)); if (!buffer) return SANE_STATUS_NO_MEM; if (cal->white_buffer == NULL) { DBG (3, "usb_high_cal_evaluate_white: white_buffer==NULL\n"); return SANE_STATUS_NO_MEM; } for (i = 0; i < cal->width; i++) { for (j = 0; j < cal->white_needed; j++) { *(buffer + j) = *(cal->white_buffer + j * cal->width + i); } average = filter_lower_end (buffer, cal->white_needed, cal->filter) * factor / loop_division; if (average >= 4096.0) cal->white_line[i] = 4095.9999; else if (average < 0.0) cal->white_line[i] = 0.0; else cal->white_line[i] = average; } free (buffer); buffer = NULL; free (cal->white_buffer); cal->white_buffer = NULL; DBG (5, "usb_high_cal_evaluate_white: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_cal_evaluate_dark (Calibrator * cal, double factor) { SANE_Word i; double loop_division; DBG (5, "usb_high_cal_evaluate_dark: start\n"); /* Calculate dark_line */ factor *= 16.0; loop_division = (double) (cal->major_average * cal->minor_average); for (i = 0; i < cal->width; i++) { cal->dark_line[i] /= loop_division; cal->dark_line[i] -= factor; if (cal->dark_line[i] < 0.0) cal->dark_line[i] = 0.0; } DBG (5, "usb_high_cal_evaluate_dark: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_cal_evaluate_calibrator (Calibrator * cal) { SANE_Int average = 0; SANE_Word i; DBG (5, "usb_high_cal_evaluate_calibrator: start\n"); if (cal->white_line == NULL) { DBG (3, "usb_high_cal_evaluate_calibrator: white_line==NULL\n"); return SANE_FALSE; } if (cal->dark_line == NULL) { DBG (3, "usb_high_cal_evaluate_calibrator: dark_line==NULL\n"); return SANE_FALSE; } for (i = 0; i < cal->width; i++) { average = (SANE_Int) (cal->white_line[i]) - (SANE_Int) (cal->dark_line[i]); if (average <= 0) average = 1; else if (average >= 4096) average = 4095; cal->k_white[i] = (SANE_Word) (average); cal->k_dark[i] = (SANE_Word) (cal->dark_line[i]); } free (cal->dark_line); cal->dark_line = NULL; free (cal->white_line); cal->white_line = NULL; DBG (5, "usb_high_cal_evaluate_calibrator: start\n"); return SANE_STATUS_GOOD; } /* virtual function switcher */ SANE_Status usb_high_cal_fill_in_white (Calibrator * cal, SANE_Word major, SANE_Word minor, void *white_pattern) { DBG (5, "usb_high_cal_fill_in_white: start\n"); switch (cal->calibrator_type) { case I8O8RGB: case I8O8MONO: return usb_high_cal_i8o8_fill_in_white (cal, major, minor, white_pattern); break; case I4O1MONO: return usb_high_cal_i4o1_fill_in_white (cal, major, minor, white_pattern); break; } DBG (5, "usb_high_cal_fill_in_white: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_cal_fill_in_dark (Calibrator * cal, SANE_Word major, SANE_Word minor, void *dark_pattern) { DBG (5, "usb_high_cal_fill_in_dark: start\n"); switch (cal->calibrator_type) { case I8O8RGB: case I8O8MONO: return usb_high_cal_i8o8_fill_in_dark (cal, major, minor, dark_pattern); break; case I4O1MONO: return usb_high_cal_i4o1_fill_in_dark (cal, major, minor, dark_pattern); break; } DBG (5, "usb_high_cal_fill_in_dark: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_cal_calibrate (Calibrator * cal, void *src, void *target) { DBG (5, "usb_high_cal_calibrate: start\n"); switch (cal->calibrator_type) { case I8O8RGB: return usb_high_cal_i8o8_rgb_calibrate (cal, src, target); break; case I8O8MONO: return usb_high_cal_i8o8_mono_calibrate (cal, src, target); break; case I4O1MONO: return usb_high_cal_i4o1_calibrate (cal, src, target); break; } DBG (5, "usb_high_cal_calibrate: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_cal_i8o8_fill_in_white (Calibrator * cal, SANE_Word major, SANE_Word minor, void *white_pattern) { SANE_Byte *pattern; SANE_Word j; pattern = (SANE_Byte *) white_pattern; DBG (5, "usb_high_cal_i8o8_fill_in_white: start, minor=%d\n", minor); if (!cal->is_prepared) { DBG (3, "usb_high_cal_i8o8_fill_in_white: !is_prepared\n"); return SANE_STATUS_INVAL; } if (cal->white_needed == 0) { DBG (3, "usb_high_cal_i8o8_fill_in_white: white_needed==0\n"); return SANE_STATUS_INVAL; } for (j = 0; j < cal->width; j++) { *(cal->white_buffer + major * cal->width + j) += (SANE_Int) (pattern[j]); } DBG (5, "usb_high_cal_i8o8_fill_in_white: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_cal_i8o8_fill_in_dark (Calibrator * cal, SANE_Word major, SANE_Word minor, void *dark_pattern) { SANE_Byte *pattern = (SANE_Byte *) dark_pattern; SANE_Word j; DBG (5, "usb_high_cal_i8o8_fill_in_dark: start, major=%d, minor=%d\n", major, minor); if (!cal->is_prepared) { DBG (3, "usb_high_cal_i8o8_fill_in_dark: !is_prepared\n"); return SANE_FALSE; } if (cal->dark_needed == 0) { DBG (3, "usb_high_cal_i8o8_fill_in_dark: dark_needed==0\n"); return SANE_FALSE; } for (j = 0; j < cal->width; j++) { cal->dark_line[j] += (double) (pattern[j]); } DBG (5, "usb_high_cal_i8o8_fill_in_dark: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_cal_i4o1_fill_in_white (Calibrator * cal, SANE_Word major, SANE_Word minor, void *white_pattern) { SANE_Byte *pattern; SANE_Word j = 0; pattern = (SANE_Byte *) white_pattern; DBG (5, "usb_high_cal_i4o1_fill_in_white: minor=%d\n", minor); if (!cal->is_prepared) { DBG (3, "usb_high_cal_i4o1_fill_in_white: !is_prepared\n"); return SANE_STATUS_INVAL; } if (cal->white_needed == 0) { DBG (3, "usb_high_cal_i4o1_fill_in_white: white_needed==0\n"); return SANE_STATUS_INVAL; } while (j < cal->width) { *(cal->white_buffer + major * cal->width + j) += (SANE_Int) (*(pattern) & 0xf0); j++; if (j >= cal->width) break; *(cal->white_buffer + major * cal->width + j) += (SANE_Int) ((SANE_Byte) (*(pattern++) << 4)); j++; } DBG (5, "usb_high_cal_i8o8_fill_in_white: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_cal_i4o1_fill_in_dark (Calibrator * cal, SANE_Word major, SANE_Word minor, void *dark_pattern) { SANE_Byte *pattern; SANE_Word j = 0; pattern = (SANE_Byte *) dark_pattern; DBG (5, "usb_high_cal_i4o1_fill_in_dark: start, major=%d, minor=%d\n", major, minor); if (!cal->is_prepared) { DBG (3, "usb_high_cal_i4o1_fill_in_dark: !is_prepared\n"); return SANE_STATUS_INVAL; } if (cal->dark_needed == 0) { DBG (5, "usb_high_cal_i4o1_fill_in_dark: dark_needed==0\n"); return SANE_STATUS_INVAL; } while (j < cal->width) { cal->dark_line[j++] += (double) (*(pattern) & 0xf0); if (j >= cal->width) break; cal->dark_line[j++] += (double) ((SANE_Byte) (*(pattern++) << 4)); } DBG (5, "usb_high_cal_i4o1_fill_in_dark: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_cal_i8o8_mono_calibrate (Calibrator * cal, void *src, void *target) { SANE_Byte *gray_src; SANE_Byte *gray_target; SANE_Int base = 0; SANE_Word value = 0; SANE_Word i; DBG (5, "usb_high_cal_i8o8_mono_calibrate: start\n"); gray_src = (SANE_Byte *) src; gray_target = (SANE_Byte *) target; if (cal->gamma_table == NULL) { SANE_Word k_white_level = cal->k_white_level >> 4; for (i = 0; i < cal->width; i++) { base = (SANE_Int) ((SANE_Word) (gray_src[i]) << 4) - (SANE_Int) (cal->k_dark[i]); if (base < 0) base = 0; value = ((SANE_Word) (base) * k_white_level) / cal->k_white[i]; if (value > 0x00ff) value = 0x00ff; gray_target[i] = (SANE_Byte) (value); } } else { for (i = 0; i < cal->width; i++) { base = (SANE_Int) ((SANE_Word) (gray_src[i]) << 4) - (SANE_Int) (cal->k_dark[i]); if (base < 0) base = 0; value = ((SANE_Word) (base) * cal->k_white_level) / cal->k_white[i]; if (value > 0x0fff) value = 0x0fff; gray_target[i] = (SANE_Byte) (cal->gamma_table[value]); } } DBG (5, "usb_high_cal_i8o8_mono_calibrate: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_cal_i8o8_rgb_calibrate (Calibrator * cal, void *src, void *target) { SANE_Byte *gray_src; SANE_Byte *rgb_target; SANE_Int base = 0; SANE_Word value = 0; SANE_Word i; DBG (5, "usb_high_cal_i8o8_rgb_calibrate: start\n"); gray_src = (SANE_Byte *) src; rgb_target = (SANE_Byte *) target; if (cal->gamma_table == NULL) { SANE_Word k_white_level = cal->k_white_level >> 4; for (i = 0; i < cal->width; i++) { base = (SANE_Int) ((SANE_Word) (gray_src[i]) << 4) - (SANE_Int) (cal->k_dark[i]); if (base < 0) base = 0; value = ((SANE_Word) (base) * k_white_level) / cal->k_white[i]; if (value > 0x00ff) value = 0x00ff; *rgb_target = (SANE_Byte) (value); rgb_target += 3; } } else { for (i = 0; i < cal->width; i++) { base = (SANE_Int) ((SANE_Word) (gray_src[i]) << 4) - (SANE_Int) (cal->k_dark[i]); if (base < 0) base = 0; value = ((SANE_Word) (base) * cal->k_white_level) / cal->k_white[i]; if (value > 0x0fff) value = 0x0fff; *(rgb_target) = (SANE_Byte) (cal->gamma_table[value]); rgb_target += 3; } } DBG (5, "usb_high_cal_i8o8_rgb_calibrate: start\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_cal_i4o1_calibrate (Calibrator * cal, void *src, void *target) { SANE_Byte *local_src; SANE_Byte *local_target; SANE_Int base = 0; SANE_Word value = 0; SANE_Word j = 0; SANE_Int count = 0; DBG (5, "usb_high_cal_i4o1_calibrate: start\n"); local_src = (SANE_Byte *) src; local_target = (SANE_Byte *) target; *local_target = 0; while (j < cal->width) { base = (SANE_Int) ((SANE_Word) (*local_src & 0xf0) << 4) - (SANE_Int) (cal->k_dark[j]); if (base < 0) base = 0; value = ((SANE_Word) (base) * cal->k_white_level) / cal->k_white[j]; if (value > 0x0fff) value = 0x0fff; if (value >= cal->threshold) *(local_target) |= gray_map[count]; count++; j++; if (j >= cal->width) break; base = (SANE_Int) ((SANE_Word) (*(local_src++) & 0x0f) << 8) - (SANE_Int) (cal->k_dark[j]); if (base < 0) base = 0; value = ((SANE_Word) (base) * cal->k_white_level) / cal->k_white[j]; if (value > 0x0fff) value = 0x0fff; if (value >= cal->threshold) *(local_target) |= gray_map[count]; count++; if (count >= 8) { local_target++; *local_target = 0; count = 0; } j++; } DBG (5, "usb_high_cal_i4o1_calibrate: exit\n"); return SANE_STATUS_GOOD; } /* --------------------------- scan functions ----------------------------- */ SANE_Status usb_high_scan_init (Mustek_Usb_Device * dev) { SANE_Status status; DBG (5, "usb_high_scan_init: start\n"); dev->init_bytes_per_strip = 8 * 1024; dev->adjust_length_300 = 2560; dev->adjust_length_600 = 5120; dev->init_min_expose_time = 4992; dev->init_skips_per_row_300 = 56; /* this value must be times of 6 */ dev->init_skips_per_row_600 = 72; /* this value must be times of 6 */ dev->init_j_lines = 154; dev->init_k_lines = 16; dev->init_k_filter = 8; dev->init_k_loops = 2; dev->init_pixel_rate_lines = 50; dev->init_pixel_rate_filts = 37; dev->init_powerdelay_lines = 2; dev->init_home_lines = 160; dev->init_dark_lines = 50; dev->init_k_level = 245; dev->init_max_power_delay = 240; dev->init_min_power_delay = 136; dev->init_adjust_way = 1; dev->init_green_black_factor = 0.0; dev->init_blue_black_factor = 0.0; dev->init_red_black_factor = 0.0; dev->init_gray_black_factor = 0.0; dev->init_green_factor = 0.82004; dev->init_blue_factor = 0.84954; dev->init_red_factor = 0.826375; dev->init_gray_factor = 0.833375; dev->init_red_rgb_600_pga = 8; dev->init_green_rgb_600_pga = 8; dev->init_blue_rgb_600_pga = 8; dev->init_mono_600_pga = 8; dev->init_red_rgb_300_pga = 8; dev->init_green_rgb_300_pga = 8; dev->init_blue_rgb_300_pga = 8; dev->init_mono_300_pga = 8; dev->init_expose_time = 9024; dev->init_red_rgb_600_power_delay = 80; dev->init_green_rgb_600_power_delay = 80; dev->init_blue_rgb_600_power_delay = 80; dev->init_red_mono_600_power_delay = 80; dev->init_green_mono_600_power_delay = 80; dev->init_blue_mono_600_power_delay = 80; dev->init_red_rgb_300_power_delay = 80; dev->init_green_rgb_300_power_delay = 80; dev->init_blue_rgb_300_power_delay = 80; dev->init_red_mono_300_power_delay = 80; dev->init_green_mono_300_power_delay = 80; dev->init_blue_mono_300_power_delay = 80; dev->init_threshold = 128; dev->init_top_ref = 128; dev->init_front_end = 16; dev->init_red_offset = 0; dev->init_green_offset = 0; dev->init_blue_offset = 0; dev->init_rgb_24_back_track = 80; dev->init_mono_8_back_track = 80; dev->is_open = SANE_FALSE; dev->is_prepared = SANE_FALSE; dev->expose_time = 4000; dev->width = 2550; dev->x_dpi = 300; dev->y_dpi = 300; dev->scan_mode = RGB24EXT; dev->bytes_per_row = 2550 * 3; dev->dummy = 0; dev->bytes_per_strip = 2550; dev->image_buffer = NULL; dev->red = NULL; dev->green = NULL; dev->blue = NULL; dev->get_line = NULL; dev->backtrack = NULL; dev->is_adjusted_rgb_600_power_delay = SANE_FALSE; dev->is_adjusted_mono_600_power_delay = SANE_FALSE; dev->is_adjusted_rgb_300_power_delay = SANE_FALSE; dev->is_adjusted_mono_300_power_delay = SANE_FALSE; dev->is_evaluate_pixel_rate = SANE_FALSE; dev->red_rgb_600_pga = 0; dev->green_rgb_600_pga = 0; dev->blue_rgb_600_pga = 0; dev->mono_600_pga = 0; dev->red_rgb_600_power_delay = 0; dev->green_rgb_600_power_delay = 0; dev->blue_rgb_600_power_delay = 0; dev->red_mono_600_power_delay = 0; dev->green_mono_600_power_delay = 0; dev->blue_mono_600_power_delay = 0; dev->red_rgb_300_pga = 0; dev->green_rgb_300_pga = 0; dev->blue_rgb_300_pga = 0; dev->mono_300_pga = 0; dev->red_rgb_300_power_delay = 0; dev->green_rgb_300_power_delay = 0; dev->blue_rgb_300_power_delay = 0; dev->red_mono_300_power_delay = 0; dev->green_mono_300_power_delay = 0; dev->blue_mono_300_power_delay = 0; dev->pixel_rate = 2000; dev->threshold = 128; dev->gamma_table = 0; dev->skips_per_row = 0; dev->red_calibrator = NULL; dev->green_calibrator = NULL; dev->blue_calibrator = NULL; dev->mono_calibrator = NULL; dev->is_cis_detected = SANE_FALSE; dev->is_sensor_detected = SANE_FALSE; RIE (usb_low_init (&dev->chip)); DBG (5, "usb_high_scan_init: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_exit (Mustek_Usb_Device * dev) { SANE_Status status; DBG (5, "usb_high_scan_exit: start\n"); if (!dev->chip) { DBG (5, "usb_high_scan_exit: already exited (`%s')\n", dev->name); return SANE_STATUS_INVAL; } RIE (usb_low_exit (dev->chip)); dev->chip = 0; DBG (5, "usb_high_scan_exit: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_prepare (Mustek_Usb_Device * dev) { DBG (5, "usb_high_scan_prepare: start dev=%p\n", (void *) dev); if (dev->is_prepared) { DBG (5, "usb_high_scan_prepare: is already prepared\n"); return SANE_STATUS_GOOD; } if (dev->image_buffer) { free (dev->image_buffer); } dev->image_buffer = (SANE_Byte *) malloc (dev->init_bytes_per_strip * 3); if (!dev->image_buffer) return SANE_STATUS_NO_MEM; dev->red = dev->image_buffer; dev->green = dev->image_buffer + dev->init_bytes_per_strip; dev->blue = dev->image_buffer + dev->init_bytes_per_strip * 2; dev->is_prepared = SANE_TRUE; DBG (5, "usb_high_scan_prepare: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_clearup (Mustek_Usb_Device * dev) { DBG (5, "usb_high_scan_clearup: start, dev=%p\n", (void *) dev); if (!dev->is_prepared) { DBG (3, "usb_high_scan_clearup: is not prepared\n"); return SANE_STATUS_INVAL; } if (dev->image_buffer) { free (dev->image_buffer); } dev->image_buffer = NULL; dev->red = NULL; dev->green = NULL; dev->blue = NULL; dev->is_prepared = SANE_FALSE; DBG (5, "usb_high_scan_clearup: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_turn_power (Mustek_Usb_Device * dev, SANE_Bool is_on) { SANE_Status status; DBG (5, "usb_high_scan_turn_power: start, turn %s power\n", is_on ? "on" : "off"); if (is_on) { if (dev->is_open) { DBG (3, "usb_high_scan_turn_power: wanted to turn on power, " "but scanner already open\n"); return SANE_STATUS_INVAL; } RIE (usb_low_open (dev->chip, dev->device_name)); dev->is_open = SANE_TRUE; RIE (usb_low_turn_peripheral_power (dev->chip, SANE_TRUE)); RIE (usb_low_turn_lamp_power (dev->chip, SANE_TRUE)); } else { if (!dev->is_open) { DBG (3, "usb_high_scan_turn_power: wanted to turn off power, " "but scanner already closed\n"); return SANE_STATUS_INVAL; } RIE (usb_low_turn_lamp_power (dev->chip, SANE_FALSE)); RIE (usb_low_close (dev->chip)); dev->is_open = SANE_FALSE; } DBG (5, "usb_high_scan_turn_power: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_back_home (Mustek_Usb_Device * dev) { SANE_Status status; DBG (5, "usb_high_scan_back_home: start\n"); if (!dev->is_open) { DBG (3, "usb_high_scan_back_home: not open\n"); return SANE_STATUS_INVAL; } RIE (usb_low_set_ccd_width (dev->chip, dev->init_min_expose_time)); RIE (usb_mid_motor_prepare_home (dev->chip)); DBG (5, "usb_high_scan_back_home: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_set_threshold (Mustek_Usb_Device * dev, SANE_Byte threshold) { DBG (5, "usb_high_scan_set_threshold: start, dev=%p, threshold=%d\n", (void *) dev, threshold); dev->threshold = threshold; DBG (5, "usb_high_scan_set_threshold: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_embed_gamma (Mustek_Usb_Device * dev, SANE_Word * gamma_table) { DBG (5, "usb_high_scan_embed_gamma: start, dev=%p, gamma_table=%p\n", (void *) dev, (void *) gamma_table); if (!dev->is_prepared) { DBG (5, "usb_high_scan_embed_gamma !is_prepared\n"); return SANE_STATUS_INVAL; } dev->gamma_table = gamma_table; DBG (5, "usb_high_scan_embed_gamma: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_reset (Mustek_Usb_Device * dev) { SANE_Status status; DBG (5, "usb_high_scan_reset: start\n"); if (!dev->is_open) { DBG (3, "usb_high_scan_reset: not open\n"); return SANE_STATUS_INVAL; } if (!dev->is_prepared) { DBG (3, "usb_high_scan_reset: !is_prepared\n"); return SANE_STATUS_INVAL; } RIE (usb_high_scan_init_asic (dev, dev->chip->sensor)); RIE (usb_low_set_ccd_width (dev->chip, dev->init_min_expose_time)); RIE (usb_mid_motor_prepare_home (dev->chip)); RIE (usb_high_scan_set_threshold (dev, dev->init_threshold)); RIE (usb_high_scan_embed_gamma (dev, NULL)); dev->is_adjusted_rgb_600_power_delay = SANE_FALSE; dev->is_adjusted_mono_600_power_delay = SANE_FALSE; dev->is_adjusted_rgb_300_power_delay = SANE_FALSE; dev->is_adjusted_mono_300_power_delay = SANE_FALSE; dev->is_evaluate_pixel_rate = SANE_FALSE; DBG (5, "usb_high_scan_reset: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_wait_carriage_home (Mustek_Usb_Device * dev) { SANE_Status status; DBG (5, "usb_high_scan_wait_carriage_home: start\n"); status = usb_low_get_home_sensor (dev->chip); if (status != SANE_STATUS_GOOD) { RIE (usb_low_set_ccd_width (dev->chip, dev->init_min_expose_time)); RIE (usb_mid_motor_prepare_home (dev->chip)); do { status = usb_low_get_home_sensor (dev->chip); if (status != SANE_STATUS_GOOD) usleep (18 * 1000); } while (status != SANE_STATUS_GOOD); } /* No Motor & Forward */ RIE (usb_low_move_motor_home (dev->chip, SANE_FALSE, SANE_FALSE)); DBG (5, "usb_high_scan_wait_carriage_home: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_hardware_calibration (Mustek_Usb_Device * dev) { SANE_Status status; DBG (5, "usb_high_scan_hardware_calibration: start\n"); if (dev->is_cis_detected) RIE (usb_high_scan_safe_forward (dev, dev->init_home_lines)); switch (dev->init_adjust_way) { case 1: /* CIS */ switch (dev->scan_mode) { case RGB24EXT: if (usb_mid_sensor_is600_mode (dev->chip, dev->x_dpi)) { dev->expose_time = dev->init_expose_time; dev->red_rgb_600_pga = dev->init_red_rgb_600_pga; dev->green_rgb_600_pga = dev->init_green_rgb_600_pga; dev->blue_rgb_600_pga = dev->init_blue_rgb_600_pga; RIE (usb_high_scan_adjust_rgb_600_power_delay (dev)); } else { dev->expose_time = dev->init_expose_time; dev->red_rgb_300_pga = dev->init_red_rgb_300_pga; dev->green_rgb_300_pga = dev->init_green_rgb_300_pga; dev->blue_rgb_300_pga = dev->init_blue_rgb_300_pga; RIE (usb_high_scan_adjust_rgb_300_power_delay (dev)); } break; case GRAY8EXT: if (usb_mid_sensor_is600_mode (dev->chip, dev->x_dpi)) { dev->expose_time = dev->init_expose_time; dev->mono_600_pga = dev->init_mono_600_pga; RIE (usb_high_scan_evaluate_pixel_rate (dev)); RIE (usb_high_scan_adjust_mono_600_power_delay (dev)); } else { dev->expose_time = dev->init_expose_time; dev->mono_300_pga = dev->init_mono_300_pga; RIE (usb_high_scan_evaluate_pixel_rate (dev)); RIE (usb_high_scan_adjust_mono_300_power_delay (dev)); } break; default: break; } break; case 3: /* CCD */ switch (dev->scan_mode) { case RGB24EXT: dev->red_rgb_600_pga = dev->init_red_rgb_600_pga; dev->green_rgb_600_pga = dev->init_green_rgb_600_pga; dev->blue_rgb_600_pga = dev->init_blue_rgb_600_pga; dev->skips_per_row = dev->init_skips_per_row_600; /* RIE(usb_high_scan_adjust_rgb_600_exposure (dev); fixme */ /* RIE(usb_high_scan_adjust_rgb_600_offset (dev); fixme */ /* RIE(usb_high_scan_adjust_rgb_600_pga (dev); fixme */ /* m_isAdjustedRgb600Offset=FALSE; */ /* RIE(usb_high_scan_adjust_rgb_600_offset (dev); fixme */ /* RIE(usb_high_scan_adjust_rgb_600_skips_per_row (dev); fixme */ break; case GRAY8EXT: dev->mono_600_pga = dev->init_mono_600_pga; dev->skips_per_row = dev->init_skips_per_row_600; RIE (usb_high_scan_adjust_mono_600_exposure (dev)); /* RIE(usb_high_scan_adjust_mono_600_offset (dev); fixme */ /* RIE(usb_high_scan_adjust_mono_600_pga (dev); fixme */ dev->is_adjusted_mono_600_offset = SANE_FALSE; /* RIE(usb_high_scan_adjust_mono_600_offset (dev); fixme */ /* RIE(usb_high_scan_adjust_mono_600_skips_per_row (dev); fixme */ break; default: break; } break; default: dev->expose_time = dev->init_expose_time; dev->red_rgb_600_power_delay = dev->init_red_rgb_600_power_delay; dev->green_rgb_600_power_delay = dev->init_green_rgb_600_power_delay; dev->blue_rgb_600_power_delay = dev->init_blue_rgb_600_power_delay; dev->red_mono_600_power_delay = dev->init_red_mono_600_power_delay; dev->green_mono_600_power_delay = dev->init_green_mono_600_power_delay; dev->blue_mono_600_power_delay = dev->init_blue_mono_600_power_delay; dev->red_rgb_600_pga = dev->init_red_rgb_600_pga; dev->green_rgb_600_pga = dev->init_green_rgb_600_pga; dev->blue_rgb_600_pga = dev->init_blue_rgb_600_pga; dev->mono_600_pga = dev->init_mono_600_pga; dev->red_rgb_300_power_delay = dev->init_red_rgb_300_power_delay; dev->green_rgb_300_power_delay = dev->init_green_rgb_300_power_delay; dev->blue_rgb_300_power_delay = dev->init_blue_rgb_300_power_delay; dev->red_mono_300_power_delay = dev->init_red_mono_300_power_delay; dev->green_mono_300_power_delay = dev->init_green_mono_300_power_delay; dev->blue_mono_300_power_delay = dev->init_blue_mono_300_power_delay; dev->red_rgb_300_pga = dev->init_red_rgb_300_pga; dev->green_rgb_300_pga = dev->init_green_rgb_300_pga; dev->blue_rgb_300_pga = dev->init_blue_rgb_300_pga; dev->mono_300_pga = dev->init_mono_300_pga; break; } DBG (5, "usb_high_scan_hardware_calibration: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_line_calibration (Mustek_Usb_Device * dev) { SANE_Status status; DBG (5, "usb_high_scan_line_calibration: start\n"); switch (dev->scan_mode) { case RGB24EXT: RIE (usb_high_scan_prepare_rgb_24 (dev)); if (usb_mid_sensor_is600_mode (dev->chip, dev->x_dpi)) RIE (usb_high_scan_prepare_rgb_signal_600_dpi (dev)); else RIE (usb_high_scan_prepare_rgb_signal_300_dpi (dev)); RIE (usb_mid_sensor_prepare_rgb (dev->chip, dev->x_dpi)); RIE (usb_high_scan_calibration_rgb_24 (dev)); break; case GRAY8EXT: RIE (usb_high_scan_prepare_mono_8 (dev)); if (usb_mid_sensor_is600_mode (dev->chip, dev->x_dpi)) RIE (usb_high_scan_prepare_mono_signal_600_dpi (dev)); else RIE (usb_high_scan_prepare_mono_signal_300_dpi (dev)); RIE (usb_mid_sensor_prepare_mono (dev->chip, dev->x_dpi)); RIE (usb_high_scan_calibration_mono_8 (dev)); break; default: DBG (3, "usb_high_scan_line_calibration: mode not matched\n"); return SANE_STATUS_INVAL; break; } DBG (5, "usb_high_scan_line_calibration: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_prepare_scan (Mustek_Usb_Device * dev) { SANE_Status status; DBG (5, "usb_high_scan_prepare_scan: start\n"); switch (dev->scan_mode) { case RGB24EXT: RIE (usb_high_scan_prepare_rgb_24 (dev)); dev->get_line = &usb_high_scan_get_rgb_24_bit_line; dev->backtrack = &usb_high_scan_backtrack_rgb_24; if (usb_mid_sensor_is600_mode (dev->chip, dev->x_dpi)) RIE (usb_high_scan_prepare_rgb_signal_600_dpi (dev)); else RIE (usb_high_scan_prepare_rgb_signal_300_dpi (dev)); RIE (usb_mid_sensor_prepare_rgb (dev->chip, dev->x_dpi)); RIE (usb_mid_motor_prepare_rgb (dev->chip, dev->y_dpi)); break; case GRAY8EXT: RIE (usb_high_scan_prepare_mono_8 (dev)); dev->get_line = &usb_high_scan_get_mono_8_bit_line; dev->backtrack = &usb_high_scan_backtrack_mono_8; if (usb_mid_sensor_is600_mode (dev->chip, dev->x_dpi)) RIE (usb_high_scan_prepare_mono_signal_600_dpi (dev)); else RIE (usb_high_scan_prepare_mono_signal_300_dpi (dev)); RIE (usb_mid_sensor_prepare_mono (dev->chip, dev->x_dpi)); RIE (usb_mid_motor_prepare_mono (dev->chip, dev->y_dpi)); break; default: DBG (5, "usb_high_scan_prepare_scan: unmatched mode\n"); return SANE_STATUS_INVAL; break; } DBG (5, "usb_high_scan_prepare_scan: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_suggest_parameters (Mustek_Usb_Device * dev, SANE_Word dpi, SANE_Word x, SANE_Word y, SANE_Word width, SANE_Word height, Colormode color_mode) { SANE_Status status; DBG (5, "usb_high_scan_suggest_parameters: start\n"); RIE (usb_high_scan_detect_sensor (dev)); /* Looking up Optical Y Resolution */ RIE (usb_mid_motor_get_dpi (dev->chip, dpi, &dev->y_dpi)); /* Looking up Optical X Resolution */ RIE (usb_mid_sensor_get_dpi (dev->chip, dpi, &dev->x_dpi)); dev->x = x * dev->x_dpi / dpi; dev->y = y * dev->y_dpi / dpi; dev->width = width * dev->x_dpi / dpi; dev->height = height * dev->y_dpi / dpi; switch (color_mode) { case RGB24: dev->scan_mode = RGB24EXT; dev->bytes_per_row = dev->width * 3; dev->bpp = 24; break; case GRAY8: dev->scan_mode = GRAY8EXT; dev->bpp = 8; dev->bytes_per_row = dev->width; break; default: DBG (3, "usb_high_scan_suggest_parameters: unmatched mode\n"); return SANE_STATUS_INVAL; break; } DBG (5, "usb_high_scan_suggest_parameters: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_detect_sensor (Mustek_Usb_Device * dev) { if (dev->is_sensor_detected) { DBG (5, "usb_high_scan_detect_sensor: sensor already detected\n"); return SANE_STATUS_GOOD; } dev->is_sensor_detected = SANE_TRUE; switch (dev->chip->scanner_type) { case MT_600CU: dev->chip->sensor = ST_CANON300; dev->chip->motor = MT_600; dev->is_cis_detected = SANE_TRUE; DBG (4, "usb_high_scan_detect_sensor: sensor=Canon 300 dpi, motor=" "600 dpi\n"); break; case MT_1200USB: dev->chip->sensor = ST_NEC600; dev->chip->motor = MT_1200; dev->init_min_expose_time = 2250; dev->init_skips_per_row_600 = 0; dev->init_home_lines = 32; dev->init_dark_lines = 10; dev->init_max_power_delay = 220; dev->init_min_power_delay = 220; dev->init_adjust_way = 3; dev->init_red_rgb_600_pga = 30; dev->init_green_rgb_600_pga = 30; dev->init_blue_rgb_600_pga = 30; dev->init_mono_600_pga = 30; dev->init_expose_time = 16000; dev->init_top_ref = 6; dev->init_front_end = 12; dev->init_red_offset = 128; dev->init_green_offset = 128; dev->init_blue_offset = 128; dev->init_rgb_24_back_track = 0; dev->init_mono_8_back_track = 40; dev->is_cis_detected = SANE_FALSE; DBG (4, "usb_high_scan_detect_sensor: sensor=Canon 600 dpi, motor=" "1200 dpi\n"); break; case MT_1200UB: case MT_1200CU_PLUS: case MT_1200CU: /* need to check if it's a 300600 or 600 dpi sensor */ { SANE_Byte *buffer; static SANE_Word l_temp = 0, r_temp = 0; SANE_Int i; SANE_Status status; SANE_Word lines_left; dev->chip->motor = MT_1200; dev->is_cis_detected = SANE_TRUE; buffer = NULL; l_temp = 0; r_temp = 0; buffer = (SANE_Byte *) malloc (dev->init_bytes_per_strip); if (!buffer) return SANE_STATUS_NO_MEM; for (i = 0; i < 5400; i++) buffer[i] = 0xaa; dev->scan_mode = GRAY8EXT; dev->x_dpi = 600; dev->y_dpi = 1200; dev->width = 5400; RIE (usb_high_scan_init_asic (dev, ST_CANON600)); RIE (usb_low_turn_peripheral_power (dev->chip, SANE_TRUE)); RIE (usb_low_enable_motor (dev->chip, SANE_TRUE)); /* Enable Motor */ RIE (usb_low_turn_lamp_power (dev->chip, SANE_TRUE)); RIE (usb_low_invert_image (dev->chip, SANE_FALSE)); RIE (usb_low_set_image_dpi (dev->chip, SANE_TRUE, SW_P6P6)); dev->bytes_per_strip = dev->adjust_length_600; dev->bytes_per_row = 5400; dev->dummy = 0; RIE (usb_high_scan_wait_carriage_home (dev)); RIE (usb_high_scan_hardware_calibration (dev)); RIE (usb_high_scan_prepare_scan (dev)); /* Get Data */ RIE (usb_low_start_rowing (dev->chip)); RIE (usb_low_get_row (dev->chip, buffer, &lines_left)); RIE (usb_low_stop_rowing (dev->chip)); /* Calculate */ for (i = 0; i < 256; i++) l_temp = l_temp + buffer[512 + i]; for (i = 0; i < 256; i++) r_temp = r_temp + buffer[3500 + i]; l_temp = l_temp / 256; r_temp = r_temp / 256; /* 300/600 switch CIS or 600 CIS */ DBG (5, "usb_high_scan_detect_sensor: l_temp=%d, r_temp=%d\n", l_temp, r_temp); if (r_temp > 50) { dev->chip->sensor = ST_CANON600; DBG (4, "usb_high_scan_detect_sensor: sensor=Canon 600 dpi, motor=" "1200 dpi\n"); } else { DBG (4, "usb_high_scan_detect_sensor: sensor=Canon 300/600 dpi, " "motor=1200 dpi\n"); dev->chip->sensor = ST_CANON300600; } /* Release Resource */ free (buffer); buffer = NULL; break; } default: DBG (5, "usb_high_scan_detect_sensor: I don't know this scanner type " "(%d)\n", dev->chip->scanner_type); return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_setup_scan (Mustek_Usb_Device * dev, Colormode color_mode, SANE_Word x_dpi, SANE_Word y_dpi, SANE_Bool is_invert, SANE_Word x, SANE_Word y, SANE_Word width) { SANE_Status status; SANE_Word upper_bound; SANE_Word left_bound; DBG (5, "usb_high_scan_setup_scan: start, is_invert=%d\n", is_invert); if (!dev->is_open) { DBG (5, "usb_high_scan_setup_scan: not open\n"); return SANE_STATUS_INVAL; } if (!dev->is_prepared) { DBG (5, "usb_high_scan_setup_scan: !is_prepared\n"); return SANE_STATUS_INVAL; } RIE (usb_high_scan_init_asic (dev, dev->chip->sensor)); RIE (usb_low_turn_peripheral_power (dev->chip, SANE_TRUE)); RIE (usb_low_enable_motor (dev->chip, SANE_TRUE)); /* Enable Motor */ RIE (usb_low_turn_lamp_power (dev->chip, SANE_TRUE)); RIE (usb_low_invert_image (dev->chip, SANE_FALSE)); if (!dev->is_cis_detected) { usb_mid_front_set_front_end_mode (dev->chip, 16); usb_mid_front_enable (dev->chip, SANE_TRUE); usb_mid_front_set_top_reference (dev->chip, 244); usb_mid_front_set_rgb_signal (dev->chip); } /* Compute necessary variables */ dev->scan_mode = color_mode; dev->x_dpi = x_dpi; dev->y_dpi = y_dpi; dev->width = width; switch (dev->scan_mode) { case RGB24EXT: dev->bytes_per_row = 3 * dev->width; upper_bound = ((y * 600) / dev->y_dpi) + dev->init_j_lines; break; case GRAY8EXT: dev->bytes_per_row = dev->width; upper_bound = ((y * 600) / dev->y_dpi) + dev->init_j_lines + 4; /* fixme */ break; default: upper_bound = ((y * 600) / dev->y_dpi) + dev->init_j_lines + 4; break; } if (usb_mid_sensor_is600_mode (dev->chip, dev->x_dpi)) { /* in 600dpi */ left_bound = (x * 600 / dev->x_dpi) + dev->init_skips_per_row_600; dev->skips_per_row = (((left_bound % 32) * dev->x_dpi + 300) / 600); } else { /* in 300dpi */ left_bound = (x * 300 / dev->x_dpi) + dev->init_skips_per_row_300; dev->skips_per_row = (((left_bound % 32) * dev->x_dpi + 150) / 300); } dev->dummy = (left_bound / 32) * 32; switch (dev->scan_mode) { case RGB24EXT: dev->bytes_per_strip = dev->skips_per_row + dev->width; break; case GRAY8EXT: dev->bytes_per_strip = dev->skips_per_row + dev->width; break; default: break; } dev->bytes_per_strip = ((dev->bytes_per_strip + 1) / 2) * 2; /* make bytes_per_strip is as 2n to advoid 64n+1 */ RIE (usb_high_scan_wait_carriage_home (dev)); RIE (usb_high_scan_hardware_calibration (dev)); RIE (usb_high_scan_line_calibration (dev)); RIE (usb_high_scan_step_forward (dev, upper_bound)); RIE (usb_high_scan_prepare_scan (dev)); RIE (usb_low_start_rowing (dev->chip)); /* pat_chromator fixme (init for calibration?) */ DBG (5, "usb_high_scan_setup_scan: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_get_rows (Mustek_Usb_Device * dev, SANE_Byte * block, SANE_Word rows, SANE_Bool is_order_invert) { SANE_Status status; DBG (5, "usb_high_scan_get_rows: start, %d rows\n", rows); if (!dev->is_open) { DBG (3, "usb_high_scan_get_rows: not open\n"); return SANE_STATUS_INVAL; } if (!dev->is_prepared) { DBG (3, "usb_high_scan_get_rows: !is_prepared\n"); return SANE_STATUS_INVAL; } while (rows > 0) { RIE ((*dev->get_line) (dev, block, is_order_invert)); block += dev->bytes_per_row; rows--; } DBG (5, "usb_high_scan_get_rows: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_stop_scan (Mustek_Usb_Device * dev) { SANE_Status status; DBG (5, "usb_high_scan_stop_scan: start\n"); if (!dev->is_open) { DBG (3, "usb_high_scan_stop_scan: not open\n"); return SANE_STATUS_INVAL; } if (!dev->is_prepared) { DBG (3, "usb_high_scan_stop_scan: !is_prepared\n"); return SANE_STATUS_INVAL; } switch (dev->scan_mode) { case RGB24EXT: RIE (usb_high_cal_exit (dev->blue_calibrator)); if (dev->blue_calibrator) free (dev->blue_calibrator); dev->blue_calibrator = NULL; RIE (usb_high_cal_exit (dev->green_calibrator)); if (dev->green_calibrator) free (dev->green_calibrator); dev->green_calibrator = NULL; RIE (usb_high_cal_exit (dev->red_calibrator)); if (dev->red_calibrator) free (dev->red_calibrator); dev->red_calibrator = NULL; break; case GRAY8EXT: RIE (usb_high_cal_exit (dev->mono_calibrator)); if (dev->mono_calibrator) free (dev->mono_calibrator); dev->mono_calibrator = NULL; break; default: break; } RIE (usb_low_stop_rowing (dev->chip)); if (!dev->is_cis_detected) RIE (usb_low_turn_lamp_power (dev->chip, SANE_FALSE)); DBG (5, "usb_high_scan_stop_scan: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_init_asic (Mustek_Usb_Device * dev, Sensor_Type sensor) { SANE_Byte ccd_dpi = 0; SANE_Byte select = 0; SANE_Byte adjust = 0; SANE_Byte pin = 0; SANE_Byte motor = 0; SANE_Bool fix_pattern = SANE_FALSE; SANE_Byte ad_timing = 0; Banksize bank_size; SANE_Status status; DBG (5, "usb_high_scan_init_asic: start\n"); switch (sensor) { case ST_TOSHIBA600: ccd_dpi = 32; select = 240; adjust = 0; pin = 18; motor = 0; fix_pattern = SANE_FALSE; ad_timing = 0; bank_size = BS_16K; DBG (5, "usb_high_scan_init_asic: sensor is set to TOSHIBA600\n"); break; case ST_CANON300: ccd_dpi = 232; select = 232; adjust = 0; pin = 18; motor = 0; fix_pattern = SANE_FALSE; ad_timing = 1; bank_size = BS_4K; DBG (5, "usb_high_scan_init_asic: sensor is set to CANON300\n"); break; case ST_CANON300600: ccd_dpi = 232; select = 232; adjust = 64; pin = 18; motor = 0; fix_pattern = SANE_FALSE; ad_timing = 1; bank_size = BS_16K; DBG (5, "usb_high_scan_init_asic: sensor is set to CANON300600\n"); break; case ST_CANON600: ccd_dpi = 232; select = 232; adjust = 64; pin = 18; motor = 0; fix_pattern = SANE_FALSE; ad_timing = 1; bank_size = BS_16K; DBG (5, "usb_high_scan_init_asic: sensor is set to CANON600\n"); break; case ST_NEC600: /* fixme */ ccd_dpi = 32; select = 224; adjust = 112; pin = 18; motor = 0; fix_pattern = SANE_FALSE; ad_timing = 0; bank_size = BS_16K; DBG (5, "usb_high_scan_init_asic: sensor is set to NEC600\n"); break; default: DBG (5, "usb_high_scan_init_asic: unknown sensor\n"); return SANE_STATUS_INVAL; break; } RIE (usb_low_adjust_timing (dev->chip, adjust)); RIE (usb_low_select_timing (dev->chip, select)); RIE (usb_low_set_timing (dev->chip, ccd_dpi)); RIE (usb_low_set_sram_bank (dev->chip, bank_size)); RIE (usb_low_set_asic_io_pins (dev->chip, pin)); RIE (usb_low_set_rgb_sel_pins (dev->chip, pin)); RIE (usb_low_set_motor_signal (dev->chip, motor)); RIE (usb_low_set_test_sram_mode (dev->chip, SANE_FALSE)); RIE (usb_low_set_fix_pattern (dev->chip, fix_pattern)); RIE (usb_low_set_ad_timing (dev->chip, ad_timing)); DBG (5, "usb_high_scan_init_asic: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_evaluate_max_level (Mustek_Usb_Device * dev, SANE_Word sample_lines, SANE_Int sample_length, SANE_Byte * ret_max_level) { SANE_Byte max_level = 0; SANE_Word i; SANE_Int j; SANE_Status status; SANE_Word lines_left; DBG (5, "usb_high_scan_evaluate_max_level: start\n"); sample_length -= 20; RIE (usb_low_start_rowing (dev->chip)); for (i = 0; i < sample_lines; i++) { RIE (usb_low_get_row (dev->chip, dev->green, &lines_left)); for (j = 20; j < sample_length; j++) { if (max_level < dev->green[j]) max_level = dev->green[j]; } } RIE (usb_low_stop_rowing (dev->chip)); if (ret_max_level) *ret_max_level = max_level; DBG (5, "usb_high_scan_evaluate_max_level: exit, max_level = %d\n", max_level); return SANE_STATUS_GOOD; } /* Binary Search for Single Channel Power Delay */ SANE_Status usb_high_scan_bssc_power_delay (Mustek_Usb_Device * dev, Powerdelay_Function set_power_delay, Signal_State * signal_state, SANE_Byte * target, SANE_Byte max, SANE_Byte min, SANE_Byte threshold, SANE_Int length) { SANE_Byte max_level; SANE_Byte max_max = max; SANE_Byte min_min = min; SANE_Status status; DBG (5, "usb_high_scan_bssc_power_delay: start\n"); *target = (max + min) / 2; RIE ((*set_power_delay) (dev->chip, *target)); while (*target != min) { RIE (usb_high_scan_evaluate_max_level (dev, dev->init_powerdelay_lines, length, &max_level)); if (max_level > threshold) { min = *target; *target = (max + min) / 2; *signal_state = SS_BRIGHTER; } else if (max_level < threshold) { max = *target; *target = (max + min) / 2; *signal_state = SS_DARKER; } else if (max_level == threshold) { /* Found. */ *signal_state = SS_EQUAL; return SANE_STATUS_GOOD; } RIE ((*set_power_delay) (dev->chip, *target)); } /* Fail... */ if (max == max_max || min == min_min) { /* Boundary check */ if (max == max_max) /*target on max side */ *target = max_max; else *target = min_min; RIE ((*set_power_delay) (dev->chip, *target)); RIE (usb_high_scan_evaluate_max_level (dev, dev->init_powerdelay_lines, length, &max_level)); if (max_level > threshold) { *signal_state = SS_BRIGHTER; } else if (max_level < threshold) { *signal_state = SS_DARKER; } else if (max_level == threshold) { *signal_state = SS_EQUAL; } } else { /* Fail... will always on mimnum side, make it darker */ target++; *signal_state = SS_DARKER; } DBG (5, "usb_high_scan_bssc_power_delay: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_adjust_rgb_600_power_delay (Mustek_Usb_Device * dev) { SANE_Status status; SANE_Byte max_power_delay; Signal_State signal_state = SS_UNKNOWN; DBG (5, "usb_high_scan_adjust_rgb_600_power_delay: start\n"); max_power_delay = (SANE_Byte) (dev->expose_time / 64); if (dev->is_adjusted_rgb_600_power_delay) return SANE_STATUS_GOOD; /* Setup Initial State */ dev->red_rgb_600_power_delay = max_power_delay; dev->green_rgb_600_power_delay = max_power_delay; dev->blue_rgb_600_power_delay = max_power_delay; RIE (usb_low_set_ccd_width (dev->chip, dev->expose_time)); RIE (usb_mid_front_set_front_end_mode (dev->chip, dev->init_front_end)); RIE (usb_mid_front_set_top_reference (dev->chip, dev->init_top_ref)); RIE (usb_mid_front_set_red_offset (dev->chip, dev->init_red_offset)); RIE (usb_mid_front_set_green_offset (dev->chip, dev->init_green_offset)); RIE (usb_mid_front_set_blue_offset (dev->chip, dev->init_blue_offset)); RIE (usb_mid_front_set_rgb_signal (dev->chip)); RIE (usb_low_set_dummy (dev->chip, dev->init_skips_per_row_600)); RIE (usb_low_set_image_byte_width (dev->chip, dev->adjust_length_600)); RIE (usb_low_set_pixel_depth (dev->chip, PD_8BIT)); /* adjust GreenPD */ RIE (usb_mid_motor_prepare_adjust (dev->chip, CH_GREEN)); RIE (usb_mid_sensor_prepare_rgb (dev->chip, 600)); signal_state = SS_UNKNOWN; RIE (usb_mid_front_set_green_pga (dev->chip, dev->green_rgb_600_pga)); RIE (usb_high_scan_bssc_power_delay (dev, &usb_low_set_green_pd, &signal_state, &dev->green_rgb_600_power_delay, max_power_delay, 0, dev->init_max_power_delay, dev->adjust_length_600)); /* adjust BluePD */ RIE (usb_mid_motor_prepare_adjust (dev->chip, CH_BLUE)); RIE (usb_mid_sensor_prepare_rgb (dev->chip, 600)); signal_state = SS_UNKNOWN; RIE (usb_mid_front_set_blue_pga (dev->chip, dev->blue_rgb_600_pga)); RIE (usb_high_scan_bssc_power_delay (dev, &usb_low_set_blue_pd, &signal_state, &dev->blue_rgb_600_power_delay, max_power_delay, 0, dev->init_max_power_delay, dev->adjust_length_600)); /* adjust RedPD */ RIE (usb_mid_motor_prepare_adjust (dev->chip, CH_RED)); RIE (usb_mid_sensor_prepare_rgb (dev->chip, 600)); signal_state = SS_UNKNOWN; RIE (usb_mid_front_set_red_pga (dev->chip, dev->red_rgb_600_pga)); RIE (usb_high_scan_bssc_power_delay (dev, &usb_low_set_red_pd, &signal_state, &dev->red_rgb_600_power_delay, max_power_delay, 0, dev->init_max_power_delay, dev->adjust_length_600)); dev->is_adjusted_rgb_600_power_delay = SANE_TRUE; DBG (5, "usb_high_scan_adjust_rgb_600_power_delay: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_adjust_mono_600_power_delay (Mustek_Usb_Device * dev) { SANE_Byte max_power_delay; Signal_State signal_state = SS_UNKNOWN; SANE_Status status; DBG (5, "usb_high_scan_adjust_mono_600_power_delay: start\n"); max_power_delay = (SANE_Byte) (dev->expose_time / 64); if (dev->is_adjusted_mono_600_power_delay) return SANE_STATUS_GOOD; /* Setup Initial State */ dev->red_mono_600_power_delay = max_power_delay; dev->green_mono_600_power_delay = max_power_delay; dev->blue_mono_600_power_delay = max_power_delay; /* Compute Gray PD */ RIE (usb_low_set_ccd_width (dev->chip, dev->expose_time)); RIE (usb_mid_front_set_front_end_mode (dev->chip, dev->init_front_end)); RIE (usb_mid_front_set_top_reference (dev->chip, dev->init_top_ref)); RIE (usb_mid_front_set_red_offset (dev->chip, dev->init_red_offset)); RIE (usb_mid_front_set_green_offset (dev->chip, dev->init_green_offset)); RIE (usb_mid_front_set_blue_offset (dev->chip, dev->init_blue_offset)); RIE (usb_mid_front_set_rgb_signal (dev->chip)); RIE (usb_low_set_dummy (dev->chip, dev->init_skips_per_row_600)); RIE (usb_low_set_image_byte_width (dev->chip, dev->adjust_length_600)); RIE (usb_low_set_pixel_depth (dev->chip, PD_8BIT)); /* adjust GreenGrayPD */ RIE (usb_mid_motor_prepare_adjust (dev->chip, CH_GREEN)); RIE (usb_mid_sensor_prepare_rgb (dev->chip, 600)); signal_state = SS_UNKNOWN; RIE (usb_mid_front_set_red_pga (dev->chip, dev->mono_600_pga)); RIE (usb_mid_front_set_green_pga (dev->chip, dev->mono_600_pga)); RIE (usb_mid_front_set_blue_pga (dev->chip, dev->mono_600_pga)); RIE (usb_high_scan_bssc_power_delay (dev, &usb_low_set_green_pd, &signal_state, &dev->green_mono_600_power_delay, max_power_delay, 0, dev->init_max_power_delay, dev->adjust_length_600)); dev->is_adjusted_mono_600_power_delay = SANE_TRUE; DBG (5, "usb_high_scan_adjust_mono_600_power_delay: exit\n"); return SANE_STATUS_GOOD; } /* CCD */ SANE_Status usb_high_scan_adjust_mono_600_exposure (Mustek_Usb_Device * dev) { SANE_Word transfer_time; SANE_Status status; DBG (5, "usb_high_scan_adjust_mono_600_exposure: start\n"); if (dev->is_adjusted_mono_600_exposure) return SANE_STATUS_GOOD; RIE (usb_high_scan_evaluate_pixel_rate (dev)); transfer_time = dev->pixel_rate * dev->x_dpi / 600; if (transfer_time > 16000) transfer_time = 16000; dev->mono_600_exposure = MAX (5504, MAX (transfer_time, usb_mid_motor_mono_capability (dev->chip, dev->y_dpi))); dev->mono_600_exposure = ((dev->mono_600_exposure + 63) / 64) * 64; dev->is_adjusted_mono_600_exposure = SANE_TRUE; DBG (5, "usb_high_scan_adjust_mono_600_exposure: exit\n"); return SANE_STATUS_GOOD; } #if 0 /* CCD */ SANE_Status usb_high_scan_adjust_mono_600_offset (Mustek_Usb_Device * dev) { DBG (5, "usb_high_scan_adjust_mono_600_offset: start\n"); if (dev->is_adjusted_mono_600_offset) return SANE_STATUS_GOOD; DBG (5, "usb_high_scan_adjust_mono_600_offset: exit\n"); return SANE_STATUS_GOOD; } /* CCD */ SANE_Status usb_high_scan_adjust_mono_600_pga (Mustek_Usb_Device * dev) { DBG (5, "usb_high_scan_adjust_mono_600_pga: start (dev = %p)\n", dev); DBG (5, "usb_high_scan_adjust_mono_600_pga: exit\n"); return SANE_STATUS_GOOD; } /* CCD */ SANE_Status usb_high_scan_adjust_mono_600_skips_per_row (Mustek_Usb_Device * dev) { DBG (5, "usb_high_scan_adjust_mono_600_skips_per_row: start (dev = %p)\n", dev); DBG (5, "usb_high_scan_adjust_mono_600_skips_per_row: exit\n"); return SANE_STATUS_GOOD; } #endif SANE_Status usb_high_scan_adjust_rgb_300_power_delay (Mustek_Usb_Device * dev) { /* Setup Initial State */ SANE_Byte max_power_delay; Signal_State signal_state = SS_UNKNOWN; SANE_Status status; DBG (5, "usb_high_scan_adjust_rgb_300_power_delay: start\n"); max_power_delay = (SANE_Byte) (dev->expose_time / 64); if (dev->is_adjusted_rgb_300_power_delay) return SANE_STATUS_GOOD; dev->red_rgb_300_power_delay = max_power_delay; dev->green_rgb_300_power_delay = max_power_delay; dev->blue_rgb_300_power_delay = max_power_delay; RIE (usb_low_set_ccd_width (dev->chip, dev->expose_time)); RIE (usb_mid_front_set_front_end_mode (dev->chip, dev->init_front_end)); RIE (usb_mid_front_set_top_reference (dev->chip, dev->init_top_ref)); RIE (usb_mid_front_set_red_offset (dev->chip, dev->init_red_offset)); RIE (usb_mid_front_set_green_offset (dev->chip, dev->init_green_offset)); RIE (usb_mid_front_set_blue_offset (dev->chip, dev->init_blue_offset)); RIE (usb_mid_front_set_rgb_signal (dev->chip)); RIE (usb_low_set_dummy (dev->chip, dev->init_skips_per_row_300)); RIE (usb_low_set_image_byte_width (dev->chip, dev->adjust_length_300)); RIE (usb_low_set_pixel_depth (dev->chip, PD_8BIT)); /* adjust GreenPD */ RIE (usb_mid_motor_prepare_adjust (dev->chip, CH_GREEN)); RIE (usb_mid_sensor_prepare_rgb (dev->chip, 300)); signal_state = SS_UNKNOWN; RIE (usb_mid_front_set_green_pga (dev->chip, dev->green_rgb_300_pga)); RIE (usb_high_scan_bssc_power_delay (dev, &usb_low_set_green_pd, &signal_state, &dev->green_rgb_300_power_delay, max_power_delay, 0, dev->init_max_power_delay, dev->adjust_length_300)); /* adjust BluePD */ RIE (usb_mid_motor_prepare_adjust (dev->chip, CH_BLUE)); RIE (usb_mid_sensor_prepare_rgb (dev->chip, 300)); signal_state = SS_UNKNOWN; RIE (usb_mid_front_set_blue_pga (dev->chip, dev->blue_rgb_300_pga)); RIE (usb_high_scan_bssc_power_delay (dev, &usb_low_set_blue_pd, &signal_state, &dev->blue_rgb_300_power_delay, max_power_delay, 0, dev->init_max_power_delay, dev->adjust_length_300)); /* adjust RedPD */ RIE (usb_mid_motor_prepare_adjust (dev->chip, CH_RED)); RIE (usb_mid_sensor_prepare_rgb (dev->chip, 300)); signal_state = SS_UNKNOWN; RIE (usb_mid_front_set_red_pga (dev->chip, dev->red_rgb_300_pga)); RIE (usb_high_scan_bssc_power_delay (dev, &usb_low_set_red_pd, &signal_state, &dev->red_rgb_300_power_delay, max_power_delay, 0, dev->init_max_power_delay, dev->adjust_length_300)); dev->is_adjusted_rgb_300_power_delay = SANE_TRUE; DBG (5, "usb_high_scan_adjust_rgb_300_power_delay: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_adjust_mono_300_power_delay (Mustek_Usb_Device * dev) { SANE_Byte max_power_delay; Signal_State signal_state = SS_UNKNOWN; SANE_Status status; DBG (5, "usb_high_scan_adjust_mono_300_power_delay: start\n"); max_power_delay = (SANE_Byte) (dev->expose_time / 64); if (dev->is_adjusted_mono_300_power_delay) return SANE_STATUS_GOOD; /* Setup Initial State */ dev->red_mono_300_power_delay = max_power_delay; dev->green_mono_300_power_delay = max_power_delay; dev->blue_mono_300_power_delay = max_power_delay; /* Compute Gray PD */ RIE (usb_low_set_ccd_width (dev->chip, dev->expose_time)); RIE (usb_mid_front_set_front_end_mode (dev->chip, dev->init_front_end)); RIE (usb_mid_front_set_top_reference (dev->chip, dev->init_top_ref)); RIE (usb_mid_front_set_red_offset (dev->chip, dev->init_red_offset)); RIE (usb_mid_front_set_green_offset (dev->chip, dev->init_green_offset)); RIE (usb_mid_front_set_blue_offset (dev->chip, dev->init_blue_offset)); RIE (usb_mid_front_set_rgb_signal (dev->chip)); RIE (usb_low_set_dummy (dev->chip, dev->init_skips_per_row_300)); RIE (usb_low_set_image_byte_width (dev->chip, dev->adjust_length_300)); RIE (usb_low_set_pixel_depth (dev->chip, PD_8BIT)); /* adjust GreenGrayPD */ RIE (usb_mid_motor_prepare_adjust (dev->chip, CH_GREEN)); RIE (usb_mid_sensor_prepare_rgb (dev->chip, 300)); signal_state = SS_UNKNOWN; RIE (usb_mid_front_set_red_pga (dev->chip, dev->mono_300_pga)); RIE (usb_mid_front_set_green_pga (dev->chip, dev->mono_300_pga)); RIE (usb_mid_front_set_blue_pga (dev->chip, dev->mono_300_pga)); RIE (usb_high_scan_bssc_power_delay (dev, &usb_low_set_green_pd, &signal_state, &dev->green_mono_300_power_delay, max_power_delay, 0, dev->init_max_power_delay, dev->adjust_length_300)); dev->is_adjusted_mono_300_power_delay = SANE_TRUE; DBG (5, "usb_high_scan_adjust_mono_300_power_delay: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_evaluate_pixel_rate (Mustek_Usb_Device * dev) { DBG (5, "usb_high_scan_evaluate_pixel_rate: start, dev=%p\n", (void *) dev); /* fixme: new for CCD */ dev->pixel_rate = 2000; dev->is_evaluate_pixel_rate = SANE_TRUE; DBG (5, "usb_high_scan_evaluate_pixel_rate: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_calibration_rgb_24 (Mustek_Usb_Device * dev) { SANE_Word white_need; SANE_Word dark_need; SANE_Word i; SANE_Status status; SANE_Word lines_left; SANE_Word minor_average; DBG (5, "usb_high_scan_calibration_rgb_24: start, dev=%p\n", (void *) dev); if (dev->is_cis_detected) { RIE (usb_mid_motor_prepare_calibrate_rgb (dev->chip, dev->y_dpi)); RIE (usb_low_turn_lamp_power (dev->chip, SANE_TRUE)); minor_average = 2; } else { minor_average = 1; } dev->red_calibrator = (Calibrator *) malloc (sizeof (Calibrator)); if (!dev->red_calibrator) return SANE_STATUS_NO_MEM; RIE (usb_high_cal_init (dev->red_calibrator, I8O8RGB, dev->init_k_level << 8, 0)); RIE (usb_high_cal_prepare (dev->red_calibrator, dev->width)); RIE (usb_high_cal_embed_gamma (dev->red_calibrator, dev->gamma_table)); RIE (usb_high_cal_setup (dev->red_calibrator, 1, minor_average, 8, dev->width, &white_need, &dark_need)); dev->green_calibrator = (Calibrator *) malloc (sizeof (Calibrator)); if (!dev->green_calibrator) return SANE_STATUS_NO_MEM; RIE (usb_high_cal_init (dev->green_calibrator, I8O8RGB, dev->init_k_level << 8, 0)); RIE (usb_high_cal_prepare (dev->green_calibrator, dev->width)); RIE (usb_high_cal_embed_gamma (dev->green_calibrator, dev->gamma_table)); RIE (usb_high_cal_setup (dev->green_calibrator, 1, minor_average, 8, dev->width, &white_need, &dark_need)); dev->blue_calibrator = (Calibrator *) malloc (sizeof (Calibrator)); if (!dev->blue_calibrator) return SANE_STATUS_NO_MEM; RIE (usb_high_cal_init (dev->blue_calibrator, I8O8RGB, dev->init_k_level << 8, 0)); RIE (usb_high_cal_prepare (dev->blue_calibrator, dev->width)); RIE (usb_high_cal_embed_gamma (dev->blue_calibrator, dev->gamma_table)); RIE (usb_high_cal_setup (dev->blue_calibrator, 1, minor_average, 8, dev->width, &white_need, &dark_need)); /* K White */ RIE (usb_low_start_rowing (dev->chip)); for (i = 0; i < white_need; i++) { /* Read Green Channel */ RIE (usb_low_get_row (dev->chip, dev->green, &lines_left)); RIE (usb_high_cal_fill_in_white (dev->green_calibrator, i, 0, (void *) (dev->green + dev->skips_per_row))); RIE (usb_low_get_row (dev->chip, dev->green, &lines_left)); RIE (usb_high_cal_fill_in_white (dev->green_calibrator, i, 1, (void *) (dev->green + dev->skips_per_row))); /* Read Blue Channel */ RIE (usb_low_get_row (dev->chip, dev->blue, &lines_left)); RIE (usb_high_cal_fill_in_white (dev->blue_calibrator, i, 0, (void *) (dev->blue + dev->skips_per_row))); RIE (usb_low_get_row (dev->chip, dev->blue, &lines_left)); RIE (usb_high_cal_fill_in_white (dev->blue_calibrator, i, 1, (void *) (dev->blue + dev->skips_per_row))); /* Read Red Channel */ RIE (usb_low_get_row (dev->chip, dev->red, &lines_left)); RIE (usb_high_cal_fill_in_white (dev->red_calibrator, i, 0, (void *) (dev->red + dev->skips_per_row))); RIE (usb_low_get_row (dev->chip, dev->red, &lines_left)); RIE (usb_high_cal_fill_in_white (dev->red_calibrator, i, 1, (void *) (dev->red + dev->skips_per_row))); } RIE (usb_low_stop_rowing (dev->chip)); /* Calculate average */ RIE (usb_high_cal_evaluate_white (dev->green_calibrator, dev->init_green_factor)); RIE (usb_high_cal_evaluate_white (dev->blue_calibrator, dev->init_blue_factor)); RIE (usb_high_cal_evaluate_white (dev->red_calibrator, dev->init_red_factor)); RIE (usb_mid_motor_prepare_calibrate_rgb (dev->chip, dev->y_dpi)); RIE (usb_low_enable_motor (dev->chip, SANE_FALSE)); RIE (usb_low_turn_lamp_power (dev->chip, SANE_FALSE)); /* K Black */ RIE (usb_low_start_rowing (dev->chip)); for (i = 0; i < dark_need; i++) { /* Read Green Channel */ RIE (usb_low_get_row (dev->chip, dev->green, &lines_left)); RIE (usb_high_cal_fill_in_dark (dev->green_calibrator, i, 0, (void *) (dev->green + dev->skips_per_row))); RIE (usb_low_get_row (dev->chip, dev->green, &lines_left)); RIE (usb_high_cal_fill_in_dark (dev->green_calibrator, i, 1, (void *) (dev->green + dev->skips_per_row))); /* Read Blue Channel */ RIE (usb_low_get_row (dev->chip, dev->blue, &lines_left)); RIE (usb_high_cal_fill_in_dark (dev->blue_calibrator, i, 0, (void *) (dev->blue + dev->skips_per_row))); RIE (usb_low_get_row (dev->chip, dev->blue, &lines_left)); RIE (usb_high_cal_fill_in_dark (dev->blue_calibrator, i, 1, (void *) (dev->blue + dev->skips_per_row))); /* Read Red Channel */ RIE (usb_low_get_row (dev->chip, dev->red, &lines_left)); RIE (usb_high_cal_fill_in_dark (dev->red_calibrator, i, 0, (void *) (dev->red + dev->skips_per_row))); RIE (usb_low_get_row (dev->chip, dev->red, &lines_left)); RIE (usb_high_cal_fill_in_dark (dev->red_calibrator, i, 1, (void *) (dev->red + dev->skips_per_row))); } RIE (usb_low_stop_rowing (dev->chip)); RIE (usb_low_turn_lamp_power (dev->chip, SANE_TRUE)); /* Calculate average */ RIE (usb_high_cal_evaluate_dark (dev->green_calibrator, dev->init_green_black_factor)); RIE (usb_high_cal_evaluate_dark (dev->blue_calibrator, dev->init_blue_black_factor)); RIE (usb_high_cal_evaluate_dark (dev->red_calibrator, dev->init_red_black_factor)); /* Calculate Mapping */ RIE (usb_high_cal_evaluate_calibrator (dev->green_calibrator)); RIE (usb_high_cal_evaluate_calibrator (dev->blue_calibrator)); RIE (usb_high_cal_evaluate_calibrator (dev->red_calibrator)); DBG (5, "usb_high_scan_calibration_rgb_24: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_calibration_mono_8 (Mustek_Usb_Device * dev) { SANE_Word white_need; SANE_Word dark_need; SANE_Word i; SANE_Status status; SANE_Word lines_left; DBG (5, "usb_high_scan_calibration_mono_8: start\n"); RIE (usb_mid_motor_prepare_calibrate_mono (dev->chip, dev->y_dpi)); RIE (usb_low_turn_lamp_power (dev->chip, SANE_TRUE)); dev->mono_calibrator = (Calibrator *) malloc (sizeof (Calibrator)); if (!dev->mono_calibrator) return SANE_STATUS_NO_MEM; RIE (usb_high_cal_init (dev->mono_calibrator, I8O8MONO, dev->init_k_level << 8, 0)); RIE (usb_high_cal_prepare (dev->mono_calibrator, dev->width)); RIE (usb_high_cal_embed_gamma (dev->mono_calibrator, dev->gamma_table)); RIE (usb_high_cal_setup (dev->mono_calibrator, 1, 1, 8, dev->width, &white_need, &dark_need)); /* K White */ RIE (usb_low_start_rowing (dev->chip)); for (i = 0; i < white_need; i++) { /* Read Green Channel */ RIE (usb_low_get_row (dev->chip, dev->green, &lines_left)); RIE (usb_high_cal_fill_in_white (dev->mono_calibrator, i, 0, (void *) (dev->green + dev->skips_per_row))); } RIE (usb_low_stop_rowing (dev->chip)); /* Calculate average */ RIE (usb_high_cal_evaluate_white (dev->mono_calibrator, dev->init_gray_factor)); RIE (usb_mid_motor_prepare_calibrate_mono (dev->chip, dev->y_dpi)); RIE (usb_low_enable_motor (dev->chip, SANE_FALSE)); RIE (usb_low_turn_lamp_power (dev->chip, SANE_FALSE)); /* K Black */ RIE (usb_low_start_rowing (dev->chip)); for (i = 0; i < dark_need; i++) { /* Read Green Channel */ RIE (usb_low_get_row (dev->chip, dev->green, &lines_left)); RIE (usb_high_cal_fill_in_dark (dev->mono_calibrator, i, 0, (void *) (dev->green + dev->skips_per_row))); } RIE (usb_low_stop_rowing (dev->chip)); RIE (usb_low_turn_lamp_power (dev->chip, SANE_TRUE)); /* Calculate Green Black */ RIE (usb_high_cal_evaluate_dark (dev->mono_calibrator, dev->init_gray_black_factor)); /* Calculate Mapping */ RIE (usb_high_cal_evaluate_calibrator (dev->mono_calibrator)); DBG (5, "usb_high_scan_calibration_mono_8: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_step_forward (Mustek_Usb_Device * dev, SANE_Int step_count) { SANE_Status status; DBG (5, "usb_high_scan_step_forward: start\n"); if (step_count <= 0) return SANE_STATUS_INVAL; /* Initialize */ RIE (usb_low_set_ccd_width (dev->chip, dev->init_min_expose_time)); RIE (usb_low_set_motor_direction (dev->chip, SANE_FALSE)); RIE (usb_mid_motor_prepare_step (dev->chip, (SANE_Word) step_count)); /* Startup */ RIE (usb_low_start_rowing (dev->chip)); /* Wait for stop */ /* Linux USB seems buggy on timeout... sleep before really try */ /* to read the flag from scanner */ usleep (step_count * 2 * 1000); RIE (usb_low_wait_rowing_stop (dev->chip)); if (!dev->is_cis_detected) RIE (usb_low_set_ccd_width (dev->chip, dev->expose_time)); DBG (5, "usb_high_scan_step_forward: start\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_safe_forward (Mustek_Usb_Device * dev, SANE_Int step_count) { SANE_Status status; DBG (5, "usb_high_scan_safe_forward: start\n"); if (step_count <= 0) return SANE_STATUS_INVAL; /* Initialize */ RIE (usb_low_set_ccd_width (dev->chip, 5400)); RIE (usb_low_set_motor_direction (dev->chip, SANE_FALSE)); RIE (usb_mid_motor_prepare_step (dev->chip, (SANE_Word) step_count)); /* Startup */ RIE (usb_low_start_rowing (dev->chip)); /* Wait to Stop */ RIE (usb_low_wait_rowing_stop (dev->chip)); RIE (usb_low_set_ccd_width (dev->chip, dev->expose_time)); DBG (5, "usb_high_scan_safe_forward: exit\n"); return SANE_STATUS_GOOD; } SANE_Word usb_high_scan_calculate_max_rgb_600_expose (Mustek_Usb_Device * dev, SANE_Byte * ideal_red_pd, SANE_Byte * ideal_green_pd, SANE_Byte * ideal_blue_pd) { SANE_Word red_light_up; SANE_Word green_light_up; SANE_Word blue_light_up; SANE_Word max_light_up; SANE_Word ideal_expose_time; DBG (5, "usb_high_scan_calculate_max_rgb_600_expose: dev=%p\n", (void *) dev); red_light_up = dev->expose_time - dev->red_rgb_600_power_delay * 64; green_light_up = dev->expose_time - dev->green_rgb_600_power_delay * 64; blue_light_up = dev->expose_time - dev->blue_rgb_600_power_delay * 64; max_light_up = MAX (red_light_up, MAX (green_light_up, blue_light_up)); if (dev->chip->sensor == ST_NEC600) { ideal_expose_time = MAX (MAX (5504, max_light_up), usb_mid_motor_rgb_capability (dev->chip, dev->y_dpi)); } else { ideal_expose_time = MAX (MAX (5376, max_light_up), usb_mid_motor_rgb_capability (dev->chip, dev->y_dpi)); } ideal_expose_time = (ideal_expose_time + 63) / 64 * 64; *ideal_red_pd = (SANE_Byte) ((ideal_expose_time - red_light_up) / 64); *ideal_green_pd = (SANE_Byte) ((ideal_expose_time - green_light_up) / 64); *ideal_blue_pd = (SANE_Byte) ((ideal_expose_time - blue_light_up) / 64); DBG (5, "usb_high_scan_calculate_max_rgb_600_expose: exit\n"); return ideal_expose_time; } SANE_Word usb_high_scan_calculate_max_mono_600_expose (Mustek_Usb_Device * dev, SANE_Byte * ideal_red_pd, SANE_Byte * ideal_green_pd, SANE_Byte * ideal_blue_pd) { SANE_Word max_light_up; SANE_Word ideal_expose_time; SANE_Word transfer_time; DBG (5, "usb_high_scan_calculate_max_mono_600_expose: dev=%p\n", (void *) dev); max_light_up = dev->expose_time - dev->green_mono_600_power_delay * 64; transfer_time = (SANE_Word) ((SANE_Word) (dev->pixel_rate) * (SANE_Word) (dev->x_dpi) / 600); /* base on 600, but double it. */ if (transfer_time > 16000) transfer_time = 16000; if (dev->chip->sensor == ST_NEC600) { ideal_expose_time = MAX (MAX (5504, max_light_up), MAX (transfer_time, usb_mid_motor_mono_capability (dev->chip, dev->y_dpi))); } else { ideal_expose_time = MAX (MAX (5376, max_light_up), MAX (transfer_time, usb_mid_motor_mono_capability (dev->chip, dev->y_dpi))); } ideal_expose_time = (ideal_expose_time + 63) / 64 * 64; *ideal_red_pd = (SANE_Byte) ((ideal_expose_time) / 64); *ideal_green_pd = (SANE_Byte) ((ideal_expose_time - max_light_up) / 64); *ideal_blue_pd = (SANE_Byte) ((ideal_expose_time) / 64); DBG (5, "usb_high_scan_calculate_max_mono_600_expose: exit\n"); return ideal_expose_time; } SANE_Word usb_high_scan_calculate_max_rgb_300_expose (Mustek_Usb_Device * dev, SANE_Byte * ideal_red_pd, SANE_Byte * ideal_green_pd, SANE_Byte * ideal_blue_pd) { SANE_Word red_light_up; SANE_Word green_light_up; SANE_Word blue_light_up; SANE_Word max_light_up; SANE_Word ideal_expose_time; DBG (5, "usb_high_scan_calculate_max_rgb_300_expose: start\n"); red_light_up = dev->expose_time - dev->red_rgb_300_power_delay * 64; green_light_up = dev->expose_time - dev->green_rgb_300_power_delay * 64; blue_light_up = dev->expose_time - dev->blue_rgb_300_power_delay * 64; max_light_up = MAX (red_light_up, MAX (green_light_up, blue_light_up)); if (dev->chip->sensor == ST_CANON300600) { ideal_expose_time = MAX (MAX (2624, max_light_up), usb_mid_motor_rgb_capability (dev->chip, dev->y_dpi)); } else if (dev->chip->sensor == ST_CANON300) { ideal_expose_time = MAX (MAX (2624, max_light_up), /* fixme? */ usb_mid_motor_rgb_capability (dev->chip, dev->y_dpi)); } else { ideal_expose_time = MAX (MAX (5376, max_light_up), usb_mid_motor_rgb_capability (dev->chip, dev->y_dpi)); } ideal_expose_time = (ideal_expose_time + 63) / 64 * 64; *ideal_red_pd = (SANE_Byte) ((ideal_expose_time - red_light_up) / 64); *ideal_green_pd = (SANE_Byte) ((ideal_expose_time - green_light_up) / 64); *ideal_blue_pd = (SANE_Byte) ((ideal_expose_time - blue_light_up) / 64); DBG (5, "usb_high_scan_calculate_max_rgb_300_expose: exit\n"); return ideal_expose_time; } SANE_Word usb_high_scan_calculate_max_mono_300_expose (Mustek_Usb_Device * dev, SANE_Byte * ideal_red_pd, SANE_Byte * ideal_green_pd, SANE_Byte * ideal_blue_pd) { SANE_Word max_light_up; SANE_Word transfer_time; SANE_Word ideal_expose_time; DBG (5, "usb_high_scan_calculate_max_mono_300_expose: start\n"); max_light_up = dev->expose_time - dev->green_mono_300_power_delay * 64; transfer_time = (SANE_Word) ((SANE_Word) (dev->pixel_rate) * (SANE_Word) (dev->x_dpi) / 600); /* base on 600, but double it. */ if (transfer_time > 16000) transfer_time = 16000; if (dev->chip->sensor == ST_CANON300600) { ideal_expose_time = MAX (MAX (2688, max_light_up), MAX (transfer_time, usb_mid_motor_mono_capability (dev->chip, dev->y_dpi))); } else if (dev->chip->sensor == ST_CANON300) { ideal_expose_time = MAX (MAX (2688, max_light_up), /* fixme? */ MAX (transfer_time, usb_mid_motor_mono_capability (dev->chip, dev-> y_dpi))); } else { ideal_expose_time = MAX (MAX (5376, max_light_up), MAX (transfer_time, usb_mid_motor_mono_capability (dev->chip, dev->y_dpi))); } ideal_expose_time = (ideal_expose_time + 63) / 64 * 64; *ideal_red_pd = (SANE_Byte) ((ideal_expose_time) / 64); *ideal_green_pd = (SANE_Byte) ((ideal_expose_time - max_light_up) / 64); *ideal_blue_pd = (SANE_Byte) ((ideal_expose_time) / 64); DBG (5, "usb_high_scan_calculate_max_mono_300_expose: exit\n"); return ideal_expose_time; } SANE_Status usb_high_scan_prepare_rgb_signal_600_dpi (Mustek_Usb_Device * dev) { SANE_Byte ideal_red_pd, ideal_green_pd, ideal_blue_pd; SANE_Word ideal_expose_time; SANE_Status status; DBG (5, "usb_high_scan_prepare_rgb_signal_600_dpi: start\n"); ideal_expose_time = usb_high_scan_calculate_max_rgb_600_expose (dev, &ideal_red_pd, &ideal_green_pd, &ideal_blue_pd); RIE (usb_low_set_ccd_width (dev->chip, ideal_expose_time)); RIE (usb_mid_front_set_front_end_mode (dev->chip, dev->init_front_end)); RIE (usb_mid_front_set_top_reference (dev->chip, dev->init_top_ref)); RIE (usb_mid_front_set_red_offset (dev->chip, dev->init_red_offset)); RIE (usb_mid_front_set_green_offset (dev->chip, dev->init_green_offset)); RIE (usb_mid_front_set_blue_offset (dev->chip, dev->init_blue_offset)); RIE (usb_mid_front_set_red_pga (dev->chip, dev->red_rgb_600_pga)); RIE (usb_mid_front_set_green_pga (dev->chip, dev->green_rgb_600_pga)); RIE (usb_mid_front_set_blue_pga (dev->chip, dev->blue_rgb_600_pga)); RIE (usb_mid_front_set_rgb_signal (dev->chip)); RIE (usb_low_set_red_pd (dev->chip, ideal_red_pd)); RIE (usb_low_set_green_pd (dev->chip, ideal_green_pd)); RIE (usb_low_set_blue_pd (dev->chip, ideal_blue_pd)); DBG (5, "usb_high_scan_prepare_rgb_signal_600_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_prepare_mono_signal_600_dpi (Mustek_Usb_Device * dev) { SANE_Byte ideal_red_pd, ideal_green_pd, ideal_blue_pd; SANE_Word ideal_expose_time; SANE_Status status; DBG (5, "usb_high_scan_prepare_mono_signal_600_dpi: start\n"); ideal_expose_time = usb_high_scan_calculate_max_mono_600_expose (dev, &ideal_red_pd, &ideal_green_pd, &ideal_blue_pd); RIE (usb_low_set_ccd_width (dev->chip, ideal_expose_time)); RIE (usb_mid_front_set_front_end_mode (dev->chip, dev->init_front_end)); RIE (usb_mid_front_set_top_reference (dev->chip, dev->init_top_ref)); RIE (usb_mid_front_set_red_offset (dev->chip, dev->init_red_offset)); RIE (usb_mid_front_set_green_offset (dev->chip, dev->init_green_offset)); RIE (usb_mid_front_set_blue_offset (dev->chip, dev->init_blue_offset)); RIE (usb_mid_front_set_red_pga (dev->chip, dev->mono_600_pga)); RIE (usb_mid_front_set_green_pga (dev->chip, dev->mono_600_pga)); RIE (usb_mid_front_set_blue_pga (dev->chip, dev->mono_600_pga)); RIE (usb_mid_front_set_rgb_signal (dev->chip)); RIE (usb_low_set_red_pd (dev->chip, ideal_red_pd)); RIE (usb_low_set_green_pd (dev->chip, ideal_green_pd)); RIE (usb_low_set_blue_pd (dev->chip, ideal_blue_pd)); DBG (5, "usb_high_scan_prepare_mono_signal_600_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_prepare_rgb_signal_300_dpi (Mustek_Usb_Device * dev) { SANE_Byte ideal_red_pd, ideal_green_pd, ideal_blue_pd; SANE_Word ideal_expose_time; SANE_Status status; DBG (5, "usb_high_scan_prepare_rgb_signal_300_dpi: start\n"); ideal_expose_time = usb_high_scan_calculate_max_rgb_300_expose (dev, &ideal_red_pd, &ideal_green_pd, &ideal_blue_pd); RIE (usb_low_set_ccd_width (dev->chip, ideal_expose_time)); RIE (usb_mid_front_set_front_end_mode (dev->chip, dev->init_front_end)); RIE (usb_mid_front_set_top_reference (dev->chip, dev->init_top_ref)); RIE (usb_mid_front_set_red_offset (dev->chip, dev->init_red_offset)); RIE (usb_mid_front_set_green_offset (dev->chip, dev->init_green_offset)); RIE (usb_mid_front_set_blue_offset (dev->chip, dev->init_blue_offset)); RIE (usb_mid_front_set_red_pga (dev->chip, dev->red_rgb_300_pga)); RIE (usb_mid_front_set_green_pga (dev->chip, dev->green_rgb_300_pga)); RIE (usb_mid_front_set_blue_pga (dev->chip, dev->blue_rgb_300_pga)); RIE (usb_mid_front_set_rgb_signal (dev->chip)); RIE (usb_low_set_red_pd (dev->chip, ideal_red_pd)); RIE (usb_low_set_green_pd (dev->chip, ideal_green_pd)); RIE (usb_low_set_blue_pd (dev->chip, ideal_blue_pd)); DBG (5, "usb_high_scan_prepare_rgb_signal_300_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_prepare_mono_signal_300_dpi (Mustek_Usb_Device * dev) { /* Setup Scan Here */ SANE_Byte ideal_red_pd, ideal_green_pd, ideal_blue_pd; SANE_Word ideal_expose_time; SANE_Status status; DBG (5, "usb_high_scan_prepare_mono_signal_300_dpi: start\n"); ideal_expose_time = usb_high_scan_calculate_max_mono_300_expose (dev, &ideal_red_pd, &ideal_green_pd, &ideal_blue_pd); RIE (usb_low_set_ccd_width (dev->chip, ideal_expose_time)); RIE (usb_mid_front_set_front_end_mode (dev->chip, dev->init_front_end)); RIE (usb_mid_front_set_top_reference (dev->chip, dev->init_top_ref)); RIE (usb_mid_front_set_red_offset (dev->chip, dev->init_red_offset)); RIE (usb_mid_front_set_green_offset (dev->chip, dev->init_green_offset)); RIE (usb_mid_front_set_blue_offset (dev->chip, dev->init_blue_offset)); RIE (usb_mid_front_set_red_pga (dev->chip, dev->mono_300_pga)); RIE (usb_mid_front_set_green_pga (dev->chip, dev->mono_300_pga)); RIE (usb_mid_front_set_blue_pga (dev->chip, dev->mono_300_pga)); RIE (usb_mid_front_set_rgb_signal (dev->chip)); RIE (usb_low_set_red_pd (dev->chip, ideal_red_pd)); RIE (usb_low_set_green_pd (dev->chip, ideal_green_pd)); RIE (usb_low_set_blue_pd (dev->chip, ideal_blue_pd)); DBG (5, "usb_high_scan_prepare_mono_signal_300_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_prepare_rgb_24 (Mustek_Usb_Device * dev) { SANE_Status status; DBG (5, "usb_high_scan_prepare_rgb_24: start\n"); RIE (usb_low_set_image_byte_width (dev->chip, dev->bytes_per_strip)); RIE (usb_low_set_dummy (dev->chip, dev->dummy)); RIE (usb_low_set_pixel_depth (dev->chip, PD_8BIT)); DBG (5, "usb_high_scan_prepare_rgb_24: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_prepare_mono_8 (Mustek_Usb_Device * dev) { SANE_Status status; DBG (5, "usb_high_scan_prepare_mono_8: start\n"); RIE (usb_low_set_image_byte_width (dev->chip, dev->bytes_per_strip)); RIE (usb_low_set_dummy (dev->chip, dev->dummy)); RIE (usb_low_set_pixel_depth (dev->chip, PD_8BIT)); DBG (5, "usb_high_scan_prepare_mono_8: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_get_rgb_24_bit_line (Mustek_Usb_Device * dev, SANE_Byte * line, SANE_Bool is_order_invert) { SANE_Status status; SANE_Word lines_left; DBG (5, "usb_high_scan_get_rgb_24_bit_line: start, dev=%p, line=%p, " "is_order_invert=%d\n", (void *) dev, (void *) line, is_order_invert); RIE (usb_low_get_row (dev->chip, dev->green, &lines_left)); RIE (usb_low_get_row (dev->chip, dev->blue, &lines_left)); RIE (usb_low_get_row (dev->chip, dev->red, &lines_left)); RIE (usb_high_cal_calibrate (dev->green_calibrator, dev->green + dev->skips_per_row, line + 1)); RIE (usb_high_cal_calibrate (dev->blue_calibrator, dev->blue + dev->skips_per_row, line + ((is_order_invert) ? 0 : 2))); RIE (usb_high_cal_calibrate (dev->red_calibrator, dev->red + dev->skips_per_row, line + ((is_order_invert) ? 2 : 0))); DBG (5, "usb_high_scan_get_rgb_24_bit_line: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_get_mono_8_bit_line (Mustek_Usb_Device * dev, SANE_Byte * line, SANE_Bool is_order_invert) { SANE_Status status; SANE_Word lines_left; DBG (5, "usb_high_scan_get_mono_8_bit_line: start, dev=%p, line=%p, " "is_order_invert=%d\n", (void *) dev, (void *) line, is_order_invert); RIE (usb_low_get_row (dev->chip, dev->green, &lines_left)); RIE (usb_high_cal_calibrate (dev->mono_calibrator, dev->green + dev->skips_per_row, line)); DBG (5, "usb_high_scan_get_mono_8_bit_line: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_backtrack_rgb_24 (Mustek_Usb_Device * dev) { DBG (5, "usb_high_scan_backtrack_rgb_24: noop, dev=%p\n", (void *) dev); return SANE_STATUS_GOOD; } SANE_Status usb_high_scan_backtrack_mono_8 (Mustek_Usb_Device * dev) { SANE_Int i; SANE_Status status; SANE_Word lines_left; DBG (5, "usb_high_scan_backtrack_mono_8: start, dev=%p\n", (void *) dev); if (dev->y_dpi >= 300) { RIE (usb_low_stop_rowing (dev->chip)); RIE (usb_low_set_motor_direction (dev->chip, SANE_TRUE)); RIE (usb_low_start_rowing (dev->chip)); for (i = 0; i < dev->init_mono_8_back_track; i++) { RIE (usb_low_get_row (dev->chip, dev->green, &lines_left)); } usleep (100 * 1000); RIE (usb_low_stop_rowing (dev->chip)); RIE (usb_low_set_motor_direction (dev->chip, SANE_FALSE)); RIE (usb_low_start_rowing (dev->chip)); for (i = 0; i < dev->init_mono_8_back_track; i++) { RIE (usb_low_get_row (dev->chip, dev->green, &lines_left)); } } DBG (5, "usb_high_scan_backtrack_mono_8: exit\n"); return SANE_STATUS_GOOD; } backends-1.3.0/backend/mustek_usb_high.h000066400000000000000000000405251456256263500202310ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2000 Mustek. Originally maintained by Tom Wang Copyright (C) 2001, 2002 by Henning Meier-Geinitz. 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, see . As a special exception, the authors of SANE give permission for additional uses of the libraries contained in this release of SANE. The exception is that, if you link a SANE library with other files to produce an executable, this does not by itself cause the resulting executable to be covered by the GNU General Public License. Your use of that executable is in no way restricted on account of linking the SANE library code into it. This exception does not, however, invalidate any other reasons why the executable file might be covered by the GNU General Public License. If you submit changes to SANE to the maintainers to be included in a subsequent release, you agree by submitting the changes that those changes may be distributed with this exception intact. If you write modifications of your own for SANE, it is your choice whether to permit this exception to apply to your modifications. If you do not wish that, delete this exception notice. This file implements a SANE backend for Mustek 1200UB and similar USB flatbed scanners. */ #ifndef mustek_usb_high_h #define mustek_usb_high_h #include "mustek_usb_mid.h" /* ---------------------------------- macros ------------------------------ */ #define I8O8RGB 0 #define I8O8MONO 1 #define I4O1MONO 2 /* ---------------------------------- types ------------------------------- */ struct Mustek_Usb_Device; typedef SANE_Status (*Powerdelay_Function) (ma1017 *, SANE_Byte); typedef SANE_Status (*Getline_Function) (struct Mustek_Usb_Device * dev, SANE_Byte *, SANE_Bool is_order_invert); typedef SANE_Status (*Backtrack_Function) (struct Mustek_Usb_Device * dev); typedef enum Colormode { RGB48 = 0, RGB42 = 1, RGB36 = 2, RGB30 = 3, RGB24 = 4, GRAY16 = 5, GRAY14 = 6, GRAY12 = 7, GRAY10 = 8, GRAY8 = 9, TEXT = 10, RGB48EXT = 11, RGB42EXT = 12, RGB36EXT = 13, RGB30EXT = 14, RGB24EXT = 15, GRAY16EXT = 16, GRAY14EXT = 17, GRAY12EXT = 18, GRAY10EXT = 19, GRAY8EXT = 20, TEXTEXT = 21 } Colormode; typedef enum Signal_State { SS_UNKNOWN = 0, SS_BRIGHTER = 1, SS_DARKER = 2, SS_EQUAL = 3 } Signal_State; typedef struct Calibrator { /* Calibration Data */ SANE_Bool is_prepared; SANE_Word *k_white; SANE_Word *k_dark; /* Working Buffer */ double *white_line; double *dark_line; SANE_Int *white_buffer; /* Necessary Parameters */ SANE_Word k_white_level; SANE_Word k_dark_level; SANE_Word major_average; SANE_Word minor_average; SANE_Word filter; SANE_Word white_needed; SANE_Word dark_needed; SANE_Word max_width; SANE_Word width; SANE_Word threshold; SANE_Word *gamma_table; SANE_Byte calibrator_type; } Calibrator; enum Mustek_Usb_Modes { MUSTEK_USB_MODE_LINEART = 0, MUSTEK_USB_MODE_GRAY, MUSTEK_USB_MODE_COLOR }; enum Mustek_Usb_Option { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_MODE, OPT_RESOLUTION, OPT_PREVIEW, OPT_GEOMETRY_GROUP, /* 5 */ OPT_TL_X, /* top-left x */ OPT_TL_Y, /* top-left y */ OPT_BR_X, /* bottom-right x */ OPT_BR_Y, /* bottom-right y */ OPT_ENHANCEMENT_GROUP, /* 10 */ OPT_THRESHOLD, OPT_CUSTOM_GAMMA, OPT_GAMMA_VECTOR, OPT_GAMMA_VECTOR_R, OPT_GAMMA_VECTOR_G, OPT_GAMMA_VECTOR_B, /* must come last: */ NUM_OPTIONS }; typedef struct Mustek_Usb_Device { struct Mustek_Usb_Device *next; SANE_String name; SANE_Device sane; SANE_Range dpi_range; SANE_Range x_range; SANE_Range y_range; /* max width & max height in 300 dpi */ SANE_Int max_width; SANE_Int max_height; ma1017 *chip; /* registers of the scanner controller chip */ Colormode scan_mode; SANE_Word x_dpi; SANE_Word y_dpi; SANE_Word x; SANE_Word y; SANE_Word width; SANE_Word height; SANE_Word bytes_per_row; SANE_Word bpp; SANE_Byte *scan_buffer; SANE_Byte *scan_buffer_start; size_t scan_buffer_len; SANE_Byte *temp_buffer; SANE_Byte *temp_buffer_start; size_t temp_buffer_len; SANE_Word line_switch; SANE_Word line_offset; SANE_Bool is_cis_detected; SANE_Word init_bytes_per_strip; SANE_Word adjust_length_300; SANE_Word adjust_length_600; SANE_Word init_min_expose_time; SANE_Word init_skips_per_row_300; SANE_Word init_skips_per_row_600; SANE_Word init_j_lines; SANE_Word init_k_lines; SANE_Word init_k_filter; SANE_Int init_k_loops; SANE_Word init_pixel_rate_lines; SANE_Word init_pixel_rate_filts; SANE_Word init_powerdelay_lines; SANE_Word init_home_lines; SANE_Word init_dark_lines; SANE_Word init_k_level; SANE_Byte init_max_power_delay; SANE_Byte init_min_power_delay; SANE_Byte init_adjust_way; double init_green_black_factor; double init_blue_black_factor; double init_red_black_factor; double init_gray_black_factor; double init_green_factor; double init_blue_factor; double init_red_factor; double init_gray_factor; SANE_Int init_red_rgb_600_pga; SANE_Int init_green_rgb_600_pga; SANE_Int init_blue_rgb_600_pga; SANE_Int init_mono_600_pga; SANE_Int init_red_rgb_300_pga; SANE_Int init_green_rgb_300_pga; SANE_Int init_blue_rgb_300_pga; SANE_Int init_mono_300_pga; SANE_Word init_expose_time; SANE_Byte init_red_rgb_600_power_delay; SANE_Byte init_green_rgb_600_power_delay; SANE_Byte init_blue_rgb_600_power_delay; SANE_Byte init_red_mono_600_power_delay; SANE_Byte init_green_mono_600_power_delay; SANE_Byte init_blue_mono_600_power_delay; SANE_Byte init_red_rgb_300_power_delay; SANE_Byte init_green_rgb_300_power_delay; SANE_Byte init_blue_rgb_300_power_delay; SANE_Byte init_red_mono_300_power_delay; SANE_Byte init_green_mono_300_power_delay; SANE_Byte init_blue_mono_300_power_delay; SANE_Byte init_threshold; SANE_Byte init_top_ref; SANE_Byte init_front_end; SANE_Byte init_red_offset; SANE_Byte init_green_offset; SANE_Byte init_blue_offset; SANE_Int init_rgb_24_back_track; SANE_Int init_mono_8_back_track; SANE_Bool is_open; SANE_Bool is_prepared; SANE_Word expose_time; SANE_Word dummy; SANE_Word bytes_per_strip; SANE_Byte *image_buffer; SANE_Byte *red; SANE_Byte *green; SANE_Byte *blue; Getline_Function get_line; Backtrack_Function backtrack; SANE_Bool is_adjusted_rgb_600_power_delay; SANE_Bool is_adjusted_mono_600_power_delay; SANE_Bool is_adjusted_rgb_300_power_delay; SANE_Bool is_adjusted_mono_300_power_delay; SANE_Bool is_evaluate_pixel_rate; SANE_Int red_rgb_600_pga; SANE_Int green_rgb_600_pga; SANE_Int blue_rgb_600_pga; SANE_Int mono_600_pga; SANE_Byte red_rgb_600_power_delay; SANE_Byte green_rgb_600_power_delay; SANE_Byte blue_rgb_600_power_delay; SANE_Byte red_mono_600_power_delay; SANE_Byte green_mono_600_power_delay; SANE_Byte blue_mono_600_power_delay; SANE_Int red_rgb_300_pga; SANE_Int green_rgb_300_pga; SANE_Int blue_rgb_300_pga; SANE_Int mono_300_pga; SANE_Byte red_rgb_300_power_delay; SANE_Byte green_rgb_300_power_delay; SANE_Byte blue_rgb_300_power_delay; SANE_Byte red_mono_300_power_delay; SANE_Byte green_mono_300_power_delay; SANE_Byte blue_mono_300_power_delay; SANE_Word pixel_rate; SANE_Byte threshold; SANE_Word *gamma_table; SANE_Word skips_per_row; /* CCD */ SANE_Bool is_adjusted_mono_600_offset; SANE_Bool is_adjusted_mono_600_exposure; SANE_Word mono_600_exposure; Calibrator *red_calibrator; Calibrator *green_calibrator; Calibrator *blue_calibrator; Calibrator *mono_calibrator; SANE_Char device_name[256]; SANE_Bool is_sensor_detected; } Mustek_Usb_Device; typedef struct Mustek_Usb_Scanner { /* all the state needed to define a scan request: */ struct Mustek_Usb_Scanner *next; SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; SANE_Int channels; /* scan window in inches: top left x+y and width+height */ double tl_x; double tl_y; double width; double height; /* scan window in dots (at current resolution): top left x+y and width+height */ SANE_Int tl_x_dots; SANE_Int tl_y_dots; SANE_Int width_dots; SANE_Int height_dots; SANE_Word bpp; SANE_Bool scanning; SANE_Parameters params; SANE_Word read_rows; SANE_Word red_gamma_table[256]; SANE_Word green_gamma_table[256]; SANE_Word blue_gamma_table[256]; SANE_Word gray_gamma_table[256]; SANE_Word linear_gamma_table[256]; SANE_Word *red_table; SANE_Word *green_table; SANE_Word *blue_table; SANE_Word *gray_table; SANE_Word total_bytes; SANE_Word total_lines; /* scanner dependent/low-level state: */ Mustek_Usb_Device *hw; } Mustek_Usb_Scanner; /* ------------------- calibration function declarations ------------------ */ static SANE_Status usb_high_cal_init (Calibrator * cal, SANE_Byte type, SANE_Word target_white, SANE_Word target_dark); static SANE_Status usb_high_cal_exit (Calibrator * cal); static SANE_Status usb_high_cal_embed_gamma (Calibrator * cal, SANE_Word * gamma_table); static SANE_Status usb_high_cal_prepare (Calibrator * cal, SANE_Word max_width); static SANE_Status usb_high_cal_setup (Calibrator * cal, SANE_Word major_average, SANE_Word minor_average, SANE_Word filter, SANE_Word width, SANE_Word * white_needed, SANE_Word * dark_needed); static SANE_Status usb_high_cal_evaluate_white (Calibrator * cal, double factor); static SANE_Status usb_high_cal_evaluate_dark (Calibrator * cal, double factor); static SANE_Status usb_high_cal_evaluate_calibrator (Calibrator * cal); static SANE_Status usb_high_cal_fill_in_white (Calibrator * cal, SANE_Word major, SANE_Word minor, void *white_pattern); static SANE_Status usb_high_cal_fill_in_dark (Calibrator * cal, SANE_Word major, SANE_Word minor, void *dark_pattern); static SANE_Status usb_high_cal_calibrate (Calibrator * cal, void *src, void *target); static SANE_Status usb_high_cal_i8o8_fill_in_white (Calibrator * cal, SANE_Word major, SANE_Word minor, void *white_pattern); static SANE_Status usb_high_cal_i8o8_fill_in_dark (Calibrator * cal, SANE_Word major, SANE_Word minor, void *dark_pattern); static SANE_Status usb_high_cal_i8o8_mono_calibrate (Calibrator * cal, void *src, void *target); static SANE_Status usb_high_cal_i8o8_rgb_calibrate (Calibrator * cal, void *src, void *target); static SANE_Status usb_high_cal_i4o1_fill_in_white (Calibrator * cal, SANE_Word major, SANE_Word minor, void *white_pattern); static SANE_Status usb_high_cal_i4o1_fill_in_dark (Calibrator * cal, SANE_Word major, SANE_Word minor, void *dark_pattern); static SANE_Status usb_high_cal_i4o1_calibrate (Calibrator * cal, void *src, void *target); /* -------------------- scanning function declarations -------------------- */ static SANE_Status usb_high_scan_init (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_exit (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_prepare (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_clearup (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_turn_power (Mustek_Usb_Device * dev, SANE_Bool is_on); static SANE_Status usb_high_scan_back_home (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_set_threshold (Mustek_Usb_Device * dev, SANE_Byte threshold); static SANE_Status usb_high_scan_embed_gamma (Mustek_Usb_Device * dev, SANE_Word * gamma_table); static SANE_Status usb_high_scan_reset (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_suggest_parameters (Mustek_Usb_Device * dev, SANE_Word dpi, SANE_Word x, SANE_Word y, SANE_Word width, SANE_Word height, Colormode color_mode); static SANE_Status usb_high_scan_detect_sensor (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_setup_scan (Mustek_Usb_Device * dev, Colormode color_mode, SANE_Word x_dpi, SANE_Word y_dpi, SANE_Bool is_invert, SANE_Word x, SANE_Word y, SANE_Word width); static SANE_Status usb_high_scan_get_rows (Mustek_Usb_Device * dev, SANE_Byte * block, SANE_Word rows, SANE_Bool is_order_invert); static SANE_Status usb_high_scan_stop_scan (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_step_forward (Mustek_Usb_Device * dev, SANE_Int step_count); static SANE_Status usb_high_scan_safe_forward (Mustek_Usb_Device * dev, SANE_Int step_count); static SANE_Status usb_high_scan_init_asic (Mustek_Usb_Device * dev, Sensor_Type sensor); static SANE_Status usb_high_scan_wait_carriage_home (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_hardware_calibration (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_line_calibration (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_prepare_scan (Mustek_Usb_Device * dev); static SANE_Word usb_high_scan_calculate_max_rgb_600_expose (Mustek_Usb_Device * dev, SANE_Byte * ideal_red_pd, SANE_Byte * ideal_green_pd, SANE_Byte * ideal_blue_pd); static SANE_Word usb_high_scan_calculate_max_mono_600_expose (Mustek_Usb_Device * dev, SANE_Byte * ideal_red_pd, SANE_Byte * ideal_green_pd, SANE_Byte * ideal_blue_pd); static SANE_Status usb_high_scan_prepare_rgb_signal_600_dpi (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_prepare_mono_signal_600_dpi (Mustek_Usb_Device * dev); static SANE_Word usb_high_scan_calculate_max_rgb_300_expose (Mustek_Usb_Device * dev, SANE_Byte * ideal_red_pd, SANE_Byte * ideal_green_pd, SANE_Byte * ideal_blue_pd); static SANE_Word usb_high_scan_calculate_max_mono_300_expose (Mustek_Usb_Device * dev, SANE_Byte * ideal_red_pd, SANE_Byte * ideal_green_pd, SANE_Byte * ideal_blue_pd); static SANE_Status usb_high_scan_prepare_rgb_signal_300_dpi (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_prepare_mono_signal_300_dpi (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_evaluate_max_level (Mustek_Usb_Device * dev, SANE_Word sample_lines, SANE_Int sample_length, SANE_Byte * ret_max_level); static SANE_Status usb_high_scan_bssc_power_delay (Mustek_Usb_Device * dev, Powerdelay_Function set_power_delay, Signal_State * signal_state, SANE_Byte * target, SANE_Byte max, SANE_Byte min, SANE_Byte threshold, SANE_Int length); static SANE_Status usb_high_scan_adjust_rgb_600_power_delay (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_adjust_mono_600_power_delay (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_adjust_mono_600_exposure (Mustek_Usb_Device * dev); #if 0 /* CCD */ static SANE_Status usb_high_scan_adjust_mono_600_offset (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_adjust_mono_600_pga (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_adjust_mono_600_skips_per_row (Mustek_Usb_Device * dev); #endif static SANE_Status usb_high_scan_adjust_rgb_300_power_delay (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_adjust_mono_300_power_delay (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_evaluate_pixel_rate (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_calibration_rgb_24 (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_calibration_mono_8 (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_prepare_rgb_24 (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_prepare_mono_8 (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_get_rgb_24_bit_line (Mustek_Usb_Device * dev, SANE_Byte * line, SANE_Bool is_order_invert); static SANE_Status usb_high_scan_get_mono_8_bit_line (Mustek_Usb_Device * dev, SANE_Byte * line, SANE_Bool is_order_invert); static SANE_Status usb_high_scan_backtrack_rgb_24 (Mustek_Usb_Device * dev); static SANE_Status usb_high_scan_backtrack_mono_8 (Mustek_Usb_Device * dev); #endif /* mustek_usb_high_h */ backends-1.3.0/backend/mustek_usb_low.c000066400000000000000000002045211456256263500201040ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2000 Mustek. Originally maintained by Tom Wang Copyright (C) 2001 - 2004 by Henning Meier-Geinitz. 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, see . As a special exception, the authors of SANE give permission for additional uses of the libraries contained in this release of SANE. The exception is that, if you link a SANE library with other files to produce an executable, this does not by itself cause the resulting executable to be covered by the GNU General Public License. Your use of that executable is in no way restricted on account of linking the SANE library code into it. This exception does not, however, invalidate any other reasons why the executable file might be covered by the GNU General Public License. If you submit changes to SANE to the maintainers to be included in a subsequent release, you agree by submitting the changes that those changes may be distributed with this exception intact. If you write modifications of your own for SANE, it is your choice whether to permit this exception to apply to your modifications. If you do not wish that, delete this exception notice. This file implements a SANE backend for Mustek 1200UB and similar USB flatbed scanners. */ #include #include "../include/sane/sane.h" #include "../include/sane/sanei_usb.h" #include "mustek_usb_low.h" SANE_Status usb_low_init (ma1017 ** chip_address) { SANE_Int i; ma1017 *chip; DBG (7, "usb_low_init: start\n"); if (!chip_address) return SANE_STATUS_INVAL; chip = (ma1017 *) malloc (sizeof (ma1017)); if (!chip) { DBG (3, "usb_low_init: couldn't malloc %ld bytes for chip\n", (long int) sizeof (ma1017)); *chip_address = 0; return SANE_STATUS_NO_MEM; } *chip_address = chip; /* io */ chip->is_rowing = SANE_FALSE; chip->is_opened = SANE_FALSE; chip->fd = -1; /* Construction/Destruction */ chip->is_opened = SANE_FALSE; chip->is_rowing = SANE_FALSE; /* A2 */ chip->append = 0x00; chip->test_sram = 0x00; chip->fix_pattern = 0x00; /* A4 */ chip->select = 0x00; chip->frontend = 0x00; /* A6 */ chip->rgb_sel_pin = 0x02; chip->asic_io_pins = 0x9c; /* A7 */ chip->timing = 0xe8; chip->sram_bank = 0x02; /* A8 */ chip->dummy_msb = 0x00; chip->ccd_width_msb = 0x00; chip->cmt_table_length = 0x00; /* A9 */ chip->cmt_second_pos = 0x00; /* A10 + A8ID5 */ chip->ccd_width = 0x0c80; /* A11 + A8ID6 */ chip->dummy = 0x0020; /* A12 + A13 */ chip->byte_width = 0x09f6; /* A14 + A30W */ chip->loop_count = 0x0db5; /* A15 */ chip->motor_enable = 0x00; chip->motor_movement = 0x60; chip->motor_direction = 0x10; chip->motor_signal = 0x00; chip->motor_home = 0x00; /* A16 */ chip->pixel_depth = 0x00; chip->image_invert = 0x00; chip->optical_600 = 0x00; chip->sample_way = 0x06; /* A17 + A18 + A19 */ chip->red_ref = 0xff; chip->green_ref = 0xff; chip->blue_ref = 0xff; /* A20 + A21 + A22 */ chip->red_pd = 0x00; chip->green_pd = 0x00; chip->blue_pd = 0x00; /* A23 */ chip->a23 = 0x80; /* A24 */ chip->fy1_delay = 0x00; chip->special_ad = 0x00; /* A27 */ chip->sclk = 0x00; chip->sen = 0x00; chip->serial_length = 0x10; /* Use for Rowing */ chip->get_row = NULL; chip->cmt_table_length_word = 0x00000000; chip->cmt_second_pos_word = 0x00000000; chip->row_size = 0x00; chip->soft_resample = 0x01; chip->total_lines = 0x00; chip->lines_left = 0x00; for (i = 0; i < 32; i++) chip->is_transfer_table[i] = SANE_FALSE; chip->sensor = ST_CANON600; chip->motor = MT_1200; chip->total_read_urbs = 0; chip->total_write_urbs = 0; DBG (7, "usb_low_init: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_exit (ma1017 * chip) { DBG (7, "usb_low_exit: chip = %p\n", (void *) chip); if (chip) { if (chip->fd >= 0 && chip->is_opened) usb_low_close (chip); DBG (7, "usb_low_exit: freeing chip\n"); free (chip); } DBG (5, "usb_low_exit: read %d URBs, wrote %d URBs\n", chip->total_read_urbs, chip->total_write_urbs); DBG (7, "usb_low_exit: exit\n"); return SANE_STATUS_GOOD; } /* A0 ~ A1 */ SANE_Status usb_low_set_cmt_table (ma1017 * chip, SANE_Int index, Channel channel, SANE_Bool is_move_motor, SANE_Bool is_transfer) { SANE_Byte pattern = ((SANE_Byte) index) << 4; SANE_Byte reg_no = 0; SANE_Status status; DBG (7, "usb_low_set_cmt_table: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_cmt_table: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_cmt_table: stop rowing first\n"); return SANE_STATUS_INVAL; } if ((unsigned int) index > 31) { DBG (7, "usb_low_set_cmt_table: CMT index (%d) exceed 31", index); return SANE_STATUS_INVAL; } switch (channel) { case CH_RED: pattern |= 0x04; break; case CH_GREEN: pattern |= 0x08; break; case CH_BLUE: pattern |= 0x0c; break; default: break; } if (is_move_motor) pattern |= 0x02; if (is_transfer) pattern |= 0x01; if (index > 15) reg_no++; RIE (usb_low_write_reg (chip, reg_no, pattern)); chip->is_transfer_table[index] = is_transfer; DBG (7, "usb_low_set_cmt_table: exit\n"); return SANE_STATUS_GOOD; } /* A2 */ SANE_Status usb_low_get_a2 (ma1017 * chip, SANE_Byte * value) { SANE_Byte pattern; SANE_Status status; DBG (7, "usb_low_get_a2: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_get_a2: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_get_a2: stop rowing first\n"); return SANE_STATUS_INVAL; } RIE (usb_low_read_reg (chip, 2, &pattern)); chip->append = pattern & 0x10; chip->test_sram = pattern & 0x20; chip->fix_pattern = pattern & 0x80; if (value) *value = pattern; DBG (7, "usb_low_get_a2: exit, value =%d\n", pattern); return SANE_STATUS_GOOD; } SANE_Status usb_low_start_cmt_table (ma1017 * chip) { SANE_Byte data_field[2]; SANE_Status status; size_t n; DBG (7, "usb_low_start_cmt_table: start\n"); data_field[0] = 0x02 | chip->append | chip->test_sram | chip->fix_pattern; data_field[1] = 2; if (!chip->is_opened) { DBG (3, "usb_low_start_cmt_table: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (7, "usb_low_start_cmt_table: Already Rowing\n"); return SANE_STATUS_INVAL; } data_field[1] |= 0x60; n = 2; status = sanei_usb_write_bulk (chip->fd, data_field, &n); if (status != SANE_STATUS_GOOD || n != 2) { DBG (3, "usb_low_start_cmt_table: can't write, wanted 2 bytes, " "wrote %lu bytes\n", (unsigned long int) n); return SANE_STATUS_IO_ERROR; } chip->total_write_urbs++; chip->is_rowing = SANE_TRUE; DBG (7, "usb_low_start_cmt_table: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_stop_cmt_table (ma1017 * chip) { SANE_Byte data_field[2]; SANE_Byte read_byte; size_t n; SANE_Status status; DBG (7, "usb_low_stop_cmt_table: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_stop_cmt_table: not opened yet\n"); return SANE_STATUS_INVAL; } if (!chip->is_rowing) { DBG (7, "usb_low_stop_cmt_table: Not Rowing yet\n"); return SANE_STATUS_INVAL; } data_field[0] = 0x01 | chip->append | chip->test_sram | chip->fix_pattern; data_field[1] = 2; data_field[1] |= 0x80; n = 2; status = sanei_usb_write_bulk (chip->fd, data_field, &n); if (status != SANE_STATUS_GOOD || n != 2) { DBG (3, "usb_low_stop_cmt_table: couldn't write, wanted 2 bytes, wrote " "%lu bytes\n", (unsigned long int) n); return SANE_STATUS_IO_ERROR; } chip->total_write_urbs++; n = 1; status = sanei_usb_read_bulk (chip->fd, &read_byte, &n); if (status != SANE_STATUS_GOOD || n != 1) { DBG (3, "usb_low_stop_cmt_table: couldn't read, wanted 1 byte, got %lu " "bytes\n", (unsigned long int) n); return SANE_STATUS_IO_ERROR; } chip->total_read_urbs++; chip->is_rowing = SANE_FALSE; DBG (7, "usb_low_stop_cmt_table: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_test_sram_mode (ma1017 * chip, SANE_Bool is_test) { SANE_Byte data, reg_no; SANE_Status status; DBG (7, "usb_low_set_test_sram_mode: start\n"); data = chip->append | chip->test_sram | chip->fix_pattern; reg_no = 2; if (!chip->is_opened) { DBG (3, "usb_low_set_test_sram_mode: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_test_sram_mode: stop rowing first\n"); return SANE_STATUS_INVAL; } if (is_test) chip->test_sram = 0x20; else chip->test_sram = 0x00; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_test_sram_mode: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_fix_pattern (ma1017 * chip, SANE_Bool is_fix) { SANE_Byte data, reg_no; SANE_Status status; DBG (7, "usb_low_set_fix_pattern: start\n"); data = chip->append | chip->test_sram | chip->fix_pattern; reg_no = 2; if (!chip->is_opened) { DBG (3, "usb_low_set_fix_pattern: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_fix_pattern: stop rowing first\n"); return SANE_STATUS_INVAL; } if (is_fix) chip->fix_pattern = 0x80; else chip->fix_pattern = 0x00; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_fix_pattern: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_adjust_timing (ma1017 * chip, SANE_Byte data) { SANE_Status status; SANE_Byte reg_no; DBG (7, "usb_low_adjust_timing: start\n"); reg_no = 3; if (!chip->is_opened) { DBG (3, "usb_low_adjust_timing: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_adjust_timing: stop rowing first\n"); return SANE_STATUS_INVAL; } RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_adjust_timing: exit\n"); return SANE_STATUS_GOOD; } /* A4 */ SANE_Status usb_low_get_a4 (ma1017 * chip, SANE_Byte * value) { SANE_Status status; SANE_Byte pattern; DBG (7, "usb_low_get_a4: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_get_a4: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_get_a4: stop rowing first\n"); return SANE_STATUS_INVAL; } RIE (usb_low_read_reg (chip, 4, &pattern)); chip->select = pattern & 0xfe; chip->frontend = pattern & 0x01; if (value) *value = pattern; DBG (7, "usb_low_get_a4: exit, value=%d\n", pattern); return SANE_STATUS_GOOD; } SANE_Status usb_low_select_timing (ma1017 * chip, SANE_Byte data) { SANE_Status status; SANE_Byte reg_no; DBG (7, "usb_low_select_timing: start\n"); reg_no = 4; if (!chip->is_opened) { DBG (3, "usb_low_select_timing: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_select_timing: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->select = data & 0xfe; chip->frontend = data & 0x01; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_select_timing: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_turn_frontend_mode (ma1017 * chip, SANE_Bool is_on) { SANE_Status status; SANE_Byte data, reg_no; DBG (7, "usb_low_turn_frontend_mode: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_turn_frontend_mode: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_turn_frontend_mode: stop rowing first\n"); return SANE_STATUS_INVAL; } if (is_on) chip->frontend = 0x01; else chip->frontend = 0x00; data = chip->select | chip->frontend; reg_no = 4; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_turn_frontend_mode: exit\n"); return SANE_STATUS_GOOD; } /* A6 */ SANE_Status usb_low_get_a6 (ma1017 * chip, SANE_Byte * value) { SANE_Byte pattern; SANE_Status status; DBG (7, "usb_low_get_a6: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_get_a6: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_get_a6: stop rowing first\n"); return SANE_STATUS_INVAL; } RIE (usb_low_read_reg (chip, 6, &pattern)); chip->asic_io_pins = pattern & 0xdc; chip->rgb_sel_pin = pattern & 0x03; if (value) *value = pattern; DBG (7, "usb_low_get_a6: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_asic_io_pins (ma1017 * chip, SANE_Byte data) { SANE_Status status; SANE_Byte reg_no; DBG (7, "usb_low_set_asic_io_pins: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_asic_io_pins: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_asic_io_pins: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->asic_io_pins = data & 0xdc; data = chip->asic_io_pins | chip->rgb_sel_pin; reg_no = 6; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_asic_io_pins: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_rgb_sel_pins (ma1017 * chip, SANE_Byte data) { SANE_Status status; SANE_Byte reg_no; DBG (7, "usb_low_set_rgb_sel_pins: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_rgb_sel_pins: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_rgb_sel_pins: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->rgb_sel_pin = data & 0x03; data = chip->asic_io_pins | chip->rgb_sel_pin; reg_no = 6; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_rgb_sel_pins: exit\n"); return SANE_STATUS_GOOD; /* was false? */ } /* A7 */ SANE_Status usb_low_get_a7 (ma1017 * chip, SANE_Byte * value) { SANE_Byte pattern; SANE_Status status; DBG (7, "usb_low_get_a7: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_get_a7: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_get_a7: stop rowing first\n"); return SANE_STATUS_INVAL; } RIE (usb_low_read_reg (chip, 7, &pattern)); if (value) *value = pattern; chip->timing = pattern & 0xfc; chip->sram_bank = pattern & 0x03; DBG (7, "usb_low_get_a7: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_timing (ma1017 * chip, SANE_Byte data) { SANE_Status status; SANE_Byte reg_no; DBG (7, "usb_low_set_timing: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_timing: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_timing: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->timing = data & 0xfc; data = chip->timing | chip->sram_bank; reg_no = 7; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_timing: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_sram_bank (ma1017 * chip, Banksize banksize) { SANE_Status status; SANE_Byte data, reg_no; DBG (7, "usb_low_set_sram_bank: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_sram_bank: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_sram_bank: stop rowing first\n"); return SANE_STATUS_INVAL; } switch (banksize) { case BS_4K: chip->sram_bank = 0x00; break; case BS_8K: chip->sram_bank = 0x01; break; case BS_16K: chip->sram_bank = 0x02; break; default: DBG (3, "usb_low_set_sram_bank: bsBankSize error\n"); return SANE_STATUS_INVAL; break; } data = chip->timing | chip->sram_bank; reg_no = 7; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_sram_bank: exit\n"); return SANE_STATUS_GOOD; } /* A8 */ SANE_Status usb_low_get_a8 (ma1017 * chip, SANE_Byte * value) { SANE_Byte pattern; SANE_Status status; DBG (7, "usb_low_get_a8: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_get_a8: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_get_a8: stop rowing first\n"); return SANE_STATUS_INVAL; } RIE (usb_low_read_reg (chip, 8, &pattern)); chip->dummy_msb = pattern & 0x40; chip->ccd_width_msb = pattern & 0x20; chip->cmt_table_length = pattern & 0x1f; chip->ccd_width = ((chip->ccd_width / 32) & 0x00ff) * 32 + ((chip->ccd_width_msb == 0) ? 0 : 0x0100 * 32); chip->dummy = ((chip->dummy / 32) & 0x00ff) * 32 + ((chip->dummy_msb == 0) ? 0 : 0x0100 * 32); if (value) *value = pattern; DBG (7, "usb_low_get_a8: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_cmt_table_length (ma1017 * chip, SANE_Byte table_length) { SANE_Status status; SANE_Byte data, reg_no; DBG (7, "usb_low_set_cmt_table_length: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_cmt_table_length: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_cmt_table_length: stop rowing first\n"); return SANE_STATUS_INVAL; } if (table_length > 32) { DBG (3, "usb_low_set_cmt_table_length: length %d exceeds 32\n", (int) table_length); return SANE_STATUS_INVAL; } if (table_length == 0) { DBG (3, "usb_low_set_cmt_table_length: length is 0\n"); return SANE_STATUS_INVAL; } chip->cmt_table_length = table_length - 1; chip->cmt_table_length_word = (SANE_Word) table_length; data = chip->cmt_table_length | chip->ccd_width_msb | chip->dummy_msb; reg_no = 8; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_cmt_table_length: exit\n"); return SANE_STATUS_GOOD; } /* A9 */ SANE_Status usb_low_get_a9 (ma1017 * chip, SANE_Byte * value) { SANE_Byte pattern; SANE_Status status; DBG (7, "usb_low_get_a9: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_get_a9: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_get_a9: stop rowing first\n"); return SANE_STATUS_INVAL; } RIE (usb_low_read_reg (chip, 9, &pattern)); chip->cmt_second_pos = pattern & 0x1f; if (value) *value = pattern; DBG (7, "usb_low_get_a9: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_cmt_second_position (ma1017 * chip, SANE_Byte position) { SANE_Byte data, reg_no; SANE_Status status; DBG (7, "usb_low_set_cmt_second_position: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_cmt_second_position: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_cmt_second_position: stop rowing first\n"); return SANE_STATUS_INVAL; } if (position > 31) { DBG (3, "usb_low_set_cmt_second_position: length: %d exceeds 31\n", (int) position); return SANE_STATUS_INVAL; } chip->cmt_second_pos = position; chip->cmt_second_pos_word = (SANE_Word) (position); data = chip->cmt_second_pos; reg_no = 9; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_cmt_second_position: exit\n"); return SANE_STATUS_GOOD; } /* A10 + A8ID5 */ SANE_Status usb_low_get_a10 (ma1017 * chip, SANE_Byte * value) { SANE_Byte pattern; SANE_Status status; DBG (7, "usb_low_get_a10: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_get_a10: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_get_a10: stop rowing first\n"); return SANE_STATUS_INVAL; } RIE (usb_low_read_reg (chip, 10, &pattern)); chip->ccd_width = ((SANE_Word) (pattern)) * 32 + ((chip->ccd_width_msb == 0) ? 0 : 0x0100 * 32); if (value) *value = pattern; DBG (7, "usb_low_get_a10: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_ccd_width (ma1017 * chip, SANE_Word ccd_width) { SANE_Status status; SANE_Byte data, reg_no; DBG (7, "usb_low_set_ccd_width: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_ccd_width: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_ccd_width: stop rowing first\n"); return SANE_STATUS_INVAL; } if (ccd_width / 32 > 0x01ff) { DBG (3, "usb_low_set_ccd_width: width %d too high\n", (int) ccd_width); return SANE_STATUS_INVAL; } chip->ccd_width = ccd_width; ccd_width /= 32; if (HIBYTE (ccd_width) == 0x01) chip->ccd_width_msb = 0x20; else chip->ccd_width_msb = 0x00; data = chip->cmt_table_length | chip->ccd_width_msb | chip->dummy_msb; reg_no = 8; RIE (usb_low_write_reg (chip, reg_no, data)); data = LOBYTE (ccd_width); reg_no = 10; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_ccd_width: exit\n"); return SANE_STATUS_GOOD; } /* A11 + A8ID6 */ SANE_Status usb_low_get_a11 (ma1017 * chip, SANE_Byte * value) { SANE_Byte pattern; SANE_Status status; DBG (7, "usb_low_get_a11: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_get_a11: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_get_a11: stop rowing first\n"); return SANE_STATUS_INVAL; } RIE (usb_low_read_reg (chip, 11, &pattern)); chip->dummy = ((SANE_Word) (pattern)) * 32 + ((chip->dummy_msb == 0) ? 0 : 0x0100 * 32); if (value) *value = pattern; DBG (7, "usb_low_get_a11: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_dummy (ma1017 * chip, SANE_Word dummy) { SANE_Status status; SANE_Byte data, reg_no; DBG (7, "usb_low_set_dummy: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_dummy: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_dummy: stop rowing first\n"); return SANE_STATUS_INVAL; } if (dummy / 32 > 0x01ff) { DBG (7, "usb_low_set_dummy: width %d exceeded\n", (int) dummy); return SANE_STATUS_INVAL; } chip->dummy = dummy; dummy /= 32; dummy++; if (HIBYTE (dummy) == 0x01) chip->dummy_msb = 0x40; else chip->dummy_msb = 0x00; data = chip->cmt_table_length | chip->ccd_width_msb | chip->dummy_msb; reg_no = 8; RIE (usb_low_write_reg (chip, reg_no, data)); data = LOBYTE (dummy); reg_no = 11; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_dummy: exit\n"); return SANE_STATUS_GOOD; } /* A12 + A13 */ SANE_Status usb_low_get_a12 (ma1017 * chip, SANE_Byte * value) { SANE_Byte pattern; SANE_Status status; DBG (7, "usb_low_get_a12: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_get_a12: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_get_a12: stop rowing first\n"); return SANE_STATUS_INVAL; } RIE (usb_low_read_reg (chip, 12, &pattern)); chip->byte_width = (chip->byte_width & 0x3f00) + ((SANE_Word) pattern); chip->soft_resample = (chip->soft_resample == 0) ? 1 : chip->soft_resample; chip->get_row = (chip->soft_resample == 1) ? &usb_low_get_row_direct : &usb_low_get_row_resample; chip->row_size = chip->byte_width / chip->soft_resample; if (value) *value = pattern; DBG (7, "usb_low_get_a12: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_get_a13 (ma1017 * chip, SANE_Byte * value) { SANE_Byte pattern; SANE_Status status; DBG (7, "usb_low_get_a13: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_get_a13: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_get_a13: stop rowing first\n"); return SANE_STATUS_INVAL; } RIE (usb_low_read_reg (chip, 13, &pattern)); chip->byte_width = (chip->byte_width & 0x00ff) + (((SANE_Word) (pattern & 0x3f)) << 8); chip->soft_resample = (chip->soft_resample == 0) ? 1 : chip->soft_resample; chip->get_row = (chip->soft_resample == 1) ? &usb_low_get_row_direct : &usb_low_get_row_resample; chip->row_size = chip->byte_width / chip->soft_resample; if (value) *value = pattern; DBG (7, "usb_low_get_a13: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_image_byte_width (ma1017 * chip, SANE_Word row_size) { SANE_Byte data, reg_no; SANE_Status status; DBG (7, "usb_low_set_image_byte_width: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_image_byte_width: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_image_byte_width: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->row_size = row_size; chip->soft_resample = (chip->soft_resample == 0) ? 1 : chip->soft_resample; chip->get_row = (chip->soft_resample == 1) ? &usb_low_get_row_direct : &usb_low_get_row_resample; chip->byte_width = chip->row_size * chip->soft_resample; if (chip->byte_width > 0x3fff) { DBG (3, "usb_low_set_image_byte_width: width %d exceeded\n", (int) chip->byte_width); return SANE_STATUS_INVAL; } data = LOBYTE (chip->byte_width); reg_no = 12; RIE (usb_low_write_reg (chip, reg_no, data)); data = HIBYTE (chip->byte_width); reg_no = 13; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_image_byte_width: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_soft_resample (ma1017 * chip, SANE_Word soft_resample) { SANE_Byte data, reg_no; SANE_Status status; DBG (7, "usb_low_set_soft_resample: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_soft_resample: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_soft_resample: stop rowing first\n"); return SANE_STATUS_INVAL; } if (soft_resample == 0x00) { DBG (3, "usb_low_set_soft_resample: soft_resample==0\n"); return SANE_STATUS_INVAL; } chip->soft_resample = soft_resample; chip->get_row = (chip->soft_resample == 1) ? &usb_low_get_row_direct : &usb_low_get_row_resample; chip->byte_width = chip->row_size * chip->soft_resample; if (chip->byte_width > 0x3fff) { DBG (3, "usb_low_set_soft_resample: width %d exceeded", (int) chip->byte_width); return SANE_STATUS_INVAL; } data = LOBYTE (chip->byte_width); reg_no = 12; RIE (usb_low_write_reg (chip, reg_no, data)); data = HIBYTE (chip->byte_width); reg_no = 13; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_soft_resample: exit\n"); return SANE_STATUS_GOOD; } /* A14 + A30W */ SANE_Status usb_low_set_cmt_loop_count (ma1017 * chip, SANE_Word loop_count) { SANE_Byte data, reg_no; SANE_Status status; DBG (7, "usb_low_set_cmt_loop_count: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_cmt_loop_count: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_cmt_loop_count: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->loop_count = loop_count; data = LOBYTE (loop_count); reg_no = 14; RIE (usb_low_write_reg (chip, reg_no, data)); data = HIBYTE (loop_count); reg_no = 30; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_cmt_loop_count: exit\n"); return SANE_STATUS_GOOD; } /* A15 */ SANE_Status usb_low_get_a15 (ma1017 * chip, SANE_Byte * value) { SANE_Byte pattern; SANE_Status status; DBG (7, "usb_low_get_a15: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_get_a15: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_get_a15: stop rowing first\n"); return SANE_STATUS_INVAL; } RIE (usb_low_read_reg (chip, 15, &pattern)); chip->motor_enable = pattern & 0x80; chip->motor_movement = pattern & 0x68; chip->motor_direction = pattern & 10; chip->motor_signal = pattern & 0x06; chip->motor_home = pattern & 0x01; if (value) *value = pattern; DBG (7, "usb_low_get_a15: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_enable_motor (ma1017 * chip, SANE_Bool is_enable) { SANE_Byte data, reg_no; SANE_Status status; DBG (7, "usb_low_enable_motor: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_enable_motor: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_enable_motor: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->motor_enable = 0x00; if (is_enable) chip->motor_enable |= 0x80; data = chip->motor_enable | chip->motor_movement | chip->motor_direction | chip->motor_signal | chip->motor_home; reg_no = 15; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_enable_motor: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_motor_movement (ma1017 * chip, SANE_Bool is_full_step, SANE_Bool is_double_phase, SANE_Bool is_two_step) { SANE_Byte data, reg_no; SANE_Status status; DBG (7, "usb_low_set_motor_movement: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_motor_movement: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_motor_movement: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->motor_movement = 0x00; if (is_full_step) chip->motor_movement |= 0x40; if (is_double_phase) chip->motor_movement |= 0x20; if (is_two_step) chip->motor_movement |= 0x08; data = chip->motor_enable | chip->motor_movement | chip->motor_direction | chip->motor_signal | chip->motor_home; reg_no = 15; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_motor_movement: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_motor_direction (ma1017 * chip, SANE_Bool is_backward) { SANE_Byte data, reg_no; SANE_Status status; DBG (7, "usb_low_set_motor_direction: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_motor_direction: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_motor_direction: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->motor_direction = 0x00; if (is_backward) chip->motor_direction |= 0x10; data = chip->motor_enable | chip->motor_movement | chip->motor_direction | chip->motor_signal | chip->motor_home; reg_no = 15; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_motor_direction: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_motor_signal (ma1017 * chip, SANE_Byte signal) { SANE_Byte data, reg_no; SANE_Status status; DBG (7, "usb_low_set_motor_signal: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_motor_signal: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_motor_signal: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->motor_signal = signal & 0x06; data = chip->motor_enable | chip->motor_movement | chip->motor_direction | chip->motor_signal | chip->motor_home; reg_no = 15; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_motor_signal: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_move_motor_home (ma1017 * chip, SANE_Bool is_home, SANE_Bool is_backward) { SANE_Byte data, reg_no; SANE_Status status; DBG (7, "usb_low_move_motor_home: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_move_motor_home: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_move_motor_home: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->motor_enable = 0x00; chip->motor_direction = 0x00; chip->motor_home = 0x00; if (is_backward) chip->motor_direction |= 0x10; if (is_home) { chip->motor_enable |= 0x80; chip->motor_home |= 0x01; } data = chip->motor_enable | chip->motor_movement | chip->motor_direction | chip->motor_signal | chip->motor_home; reg_no = 15; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_move_motor_home: exit\n"); return SANE_STATUS_GOOD; } /* A16 */ SANE_Status usb_low_get_a16 (ma1017 * chip, SANE_Byte * value) { SANE_Byte pattern; SANE_Status status; DBG (7, "usb_low_get_a16: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_get_a16: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_get_a16: stop rowing first\n"); return SANE_STATUS_INVAL; } RIE (usb_low_read_reg (chip, 16, &pattern)); chip->pixel_depth = pattern & 0xe0; chip->image_invert = pattern & 0x10; chip->optical_600 = pattern & 0x08; chip->sample_way = pattern & 0x07; if (value) *value = pattern; DBG (7, "usb_low_get_a16: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_image_dpi (ma1017 * chip, SANE_Bool is_optical600, Sampleway sampleway) { SANE_Byte data, reg_no; SANE_Status status; DBG (7, "usb_low_set_image_dpi: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_image_dpi: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_image_dpi: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->optical_600 = 0x00; chip->sample_way = 0x00; if (is_optical600) chip->optical_600 |= 0x08; switch (sampleway) { case SW_P1P6: chip->sample_way = 0x01; break; case SW_P2P6: chip->sample_way = 0x02; break; case SW_P3P6: chip->sample_way = 0x03; break; case SW_P4P6: chip->sample_way = 0x04; break; case SW_P5P6: chip->sample_way = 0x05; break; case SW_P6P6: chip->sample_way = 0x06; break; default: DBG (3, "usb_low_set_image_dpi: swsample_way error\n"); return SANE_STATUS_INVAL; break; } data = chip->pixel_depth | chip->image_invert | chip->optical_600 | chip->sample_way; reg_no = 16; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_image_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_pixel_depth (ma1017 * chip, Pixeldepth pixeldepth) { SANE_Byte data, reg_no; SANE_Status status; DBG (7, "usb_low_set_pixel_depth: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_pixel_depth: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_pixel_depth: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->pixel_depth = 0x00; switch (pixeldepth) { case PD_1BIT: chip->pixel_depth = 0x80; break; case PD_4BIT: chip->pixel_depth = 0xc0; break; case PD_8BIT: chip->pixel_depth = 0x00; break; case PD_12BIT: chip->pixel_depth = 0x20; break; default: DBG (3, "usb_low_set_pixel_depth: pdPixelDepth error\n"); return SANE_STATUS_INVAL; } data = chip->pixel_depth | chip->image_invert | chip->optical_600 | chip->sample_way; reg_no = 16; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_SetPixelDeepth: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_invert_image (ma1017 * chip, SANE_Bool is_invert) { SANE_Byte data, reg_no; SANE_Status status; DBG (7, "usb_low_invert_image: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_invert_image: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_invert_image: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->image_invert = 0x00; if (is_invert) chip->image_invert |= 0x10; data = chip->pixel_depth | chip->image_invert | chip->optical_600 | chip->sample_way; reg_no = 16; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_invert_image: exit\n"); return SANE_STATUS_GOOD; } /* A17 + A18 + A19 */ SANE_Status usb_low_get_a17 (ma1017 * chip, SANE_Byte * value) { SANE_Byte pattern; SANE_Status status; DBG (7, "usb_low_get_a17: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_get_a17: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_get_a17: stop rowing first\n"); return SANE_STATUS_INVAL; } RIE (usb_low_read_reg (chip, 17, &pattern)); chip->red_ref = pattern; if (value) *value = pattern; DBG (7, "usb_low_get_a17: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_get_a18 (ma1017 * chip, SANE_Byte * value) { SANE_Byte pattern; SANE_Status status; DBG (7, "usb_low_get_a18: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_get_a18: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_get_a18: stop rowing first\n"); return SANE_STATUS_INVAL; } RIE (usb_low_read_reg (chip, 18, &pattern)); chip->green_ref = pattern; if (value) *value = pattern; DBG (7, "usb_low_get_a18: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_get_a19 (ma1017 * chip, SANE_Byte * value) { SANE_Byte pattern; SANE_Status status; DBG (7, "usb_low_get_a19: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_get_a19: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_get_a19:stop rowing first\n"); return SANE_STATUS_INVAL; } RIE (usb_low_read_reg (chip, 19, &pattern)); chip->blue_ref = pattern; if (value) *value = pattern; DBG (7, "usb_low_get_a19: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_red_ref (ma1017 * chip, SANE_Byte red_ref) { SANE_Byte data, reg_no; SANE_Status status; DBG (7, "usb_low_set_red_ref: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_red_ref: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_red_ref: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->red_ref = red_ref; data = red_ref; reg_no = 17; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_red_ref: stop\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_green_ref (ma1017 * chip, SANE_Byte green_ref) { SANE_Byte data, reg_no; SANE_Status status; DBG (7, "usb_low_set_green_ref: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_green_ref: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_green_ref: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->green_ref = green_ref; data = green_ref; reg_no = 18; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_green_ref: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_blue_ref (ma1017 * chip, SANE_Byte blue_ref) { SANE_Byte data, reg_no; SANE_Status status; DBG (7, "usb_low_set_blue_ref: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_blue_ref: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_blue_ref: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->blue_ref = blue_ref; data = blue_ref; reg_no = 19; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_blue_ref: stop\n"); return SANE_STATUS_GOOD; } /* A20 + A21 + A22 */ SANE_Status usb_low_get_a20 (ma1017 * chip, SANE_Byte * value) { SANE_Byte pattern; SANE_Status status; DBG (7, "usb_low_get_a20: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_get_a20: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_get_a20: stop rowing first\n"); return SANE_STATUS_INVAL; } RIE (usb_low_read_reg (chip, 20, &pattern)); chip->red_pd = pattern; if (value) *value = pattern; DBG (7, "usb_low_get_a20: stop\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_get_a21 (ma1017 * chip, SANE_Byte * value) { SANE_Byte pattern; SANE_Status status; DBG (7, "usb_low_get_a21: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_get_a21: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_get_a21: stop rowing first\n"); return SANE_STATUS_INVAL; } RIE (usb_low_read_reg (chip, 21, &pattern)); chip->green_pd = pattern; if (value) *value = pattern; DBG (7, "usb_low_get_a21: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_get_a22 (ma1017 * chip, SANE_Byte * value) { SANE_Byte pattern; SANE_Status status; DBG (7, "usb_low_get_a22: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_get_a22: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_get_a22: stop rowing first\n"); return SANE_STATUS_INVAL; } RIE (usb_low_read_reg (chip, 22, &pattern)); chip->blue_pd = pattern; if (value) *value = pattern; DBG (7, "usb_low_get_a22: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_red_pd (ma1017 * chip, SANE_Byte red_pd) { SANE_Byte data, reg_no; SANE_Status status; DBG (7, "usb_low_set_red_pd: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_red_pd: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_red_pd: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->red_pd = red_pd; data = chip->red_pd; reg_no = 20; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_red_pd: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_green_pd (ma1017 * chip, SANE_Byte green_pd) { SANE_Byte data, reg_no; SANE_Status status; DBG (7, "usb_low_set_green_pd: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_green_pd: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_green_pd: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->green_pd = green_pd; data = chip->green_pd; reg_no = 21; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_green_pd: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_blue_pd (ma1017 * chip, SANE_Byte blue_pd) { SANE_Byte data, reg_no; SANE_Status status; DBG (7, "usb_low_set_blue_pd: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_blue_pd: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_blue_pd: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->blue_pd = blue_pd; data = chip->blue_pd; reg_no = 22; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_blue_pd: exit\n"); return SANE_STATUS_GOOD; } /* A23 */ SANE_Status usb_low_get_a23 (ma1017 * chip, SANE_Byte * value) { SANE_Byte pattern; SANE_Status status; DBG (7, "usb_low_get_a23: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_get_a23: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_get_a23: stop rowing first\n"); return SANE_STATUS_INVAL; } RIE (usb_low_read_reg (chip, 23, &pattern)); chip->a23 = pattern; if (value) *value = pattern; DBG (7, "usb_low_get_a23: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_turn_peripheral_power (ma1017 * chip, SANE_Bool is_on) { SANE_Byte data, reg_no; SANE_Status status; DBG (7, "usb_low_turn_peripheral_power: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_turn_peripheral_power: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_turn_peripheral_power: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->a23 &= 0x7f; if (is_on) chip->a23 |= 0x80; data = chip->a23; reg_no = 23; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_turn_peripheral_power: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_turn_lamp_power (ma1017 * chip, SANE_Bool is_on) { SANE_Byte data, reg_no; SANE_Status status; DBG (7, "usb_low_turn_lamp_power: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_turn_lamp_power: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_turn_lamp_power: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->a23 &= 0xbf; if (is_on) chip->a23 |= 0x40; data = chip->a23; reg_no = 23; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_turn_lamp_power: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_io_3 (ma1017 * chip, SANE_Bool is_high) { SANE_Byte data, reg_no; SANE_Status status; DBG (7, "usb_low_set_io_3: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_io_3: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_io_3: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->a23 &= 0xf7; if (is_high) chip->a23 |= 0x08; data = chip->a23; reg_no = 23; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_io_3: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_led_light_all (ma1017 * chip, SANE_Bool is_light_all) { SANE_Byte data, reg_no; SANE_Status status; DBG (7, "usb_low_set_led_light_all: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_led_light_all: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_led_light_all: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->a23 &= 0xfe; if (is_light_all) chip->a23 |= 0x01; data = chip->a23; reg_no = 23; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_led_light_all: exit\n"); return SANE_STATUS_GOOD; } /* A24 */ SANE_Status usb_low_get_a24 (ma1017 * chip, SANE_Byte * value) { SANE_Byte pattern; SANE_Status status; DBG (7, "usb_low_get_a24: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_get_a24: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_get_a24: stop rowing first\n"); return SANE_STATUS_INVAL; } RIE (usb_low_read_reg (chip, 24, &pattern)); chip->fy1_delay = pattern & 0x01; chip->special_ad = pattern & 0x02; if (value) *value = pattern; DBG (7, "usb_low_get_a24: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_ad_timing (ma1017 * chip, SANE_Byte data) { SANE_Byte reg_no; SANE_Status status; DBG (7, "usb_low_set_ad_timing: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_ad_timing: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_ad_timing: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->fy1_delay = data & 0x01; chip->special_ad = data & 0x02; data = chip->special_ad | chip->fy1_delay; reg_no = 24; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_ad_timing: exit\n"); return SANE_STATUS_GOOD; } /* A25 + A26 */ SANE_Status usb_low_set_serial_byte1 (ma1017 * chip, SANE_Byte data) { SANE_Byte reg_no; SANE_Status status; DBG (7, "usb_low_set_serial_byte1: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_serial_byte1: not opened\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_serial_byte1: stop rowing first\n"); return SANE_STATUS_INVAL; } reg_no = 25; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_serial_byte1: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_serial_byte2 (ma1017 * chip, SANE_Byte data) { SANE_Byte reg_no; SANE_Status status; DBG (7, "usb_low_set_serial_byte2: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_serial_byte2: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_serial_byte2: stop rowing first\n"); return SANE_STATUS_INVAL; } reg_no = 26; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_serial_byte2: exit\n"); return SANE_STATUS_GOOD; } /* A27 */ SANE_Status usb_low_get_a27 (ma1017 * chip, SANE_Byte * value) { SANE_Byte pattern; SANE_Status status; DBG (7, "usb_low_get_a27: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_get_a27: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_get_a27: stop rowing first\n"); return SANE_STATUS_INVAL; } RIE (usb_low_read_reg (chip, 27, &pattern)); chip->sclk = pattern & 0x80; chip->sen = pattern & 0x40; chip->serial_length = pattern & 0x1f; if (value) *value = pattern; DBG (7, "usb_low_get_a27: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_set_serial_format (ma1017 * chip, SANE_Byte data) { SANE_Byte reg_no; SANE_Status status; DBG (7, "usb_low_set_serial_format: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_set_serial_format: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_set_serial_format: stop rowing first\n"); return SANE_STATUS_INVAL; } chip->sclk = data & 0x80; chip->sen = data & 0x40; chip->serial_length = data & 0x1f; reg_no = 27; RIE (usb_low_write_reg (chip, reg_no, data)); DBG (7, "usb_low_set_serial_format: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_get_home_sensor (ma1017 * chip) { SANE_Byte data; SANE_Status status; DBG (7, "usb_low_get_home_sensor: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_get_home_sensor: not opened yet\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_get_home_sensor: stop rowing first\n"); return SANE_STATUS_INVAL; } RIE (usb_low_read_reg (chip, 31, &data)); DBG (7, "usb_low_get_home_sensor: exit\n"); if ((data & 0x80) != 0) return SANE_STATUS_GOOD; else return SANE_STATUS_IO_ERROR; } /* Special Mode */ SANE_Status usb_low_start_rowing (ma1017 * chip) { SANE_Word line_of_first = 0; SANE_Word line_of_second = 0; SANE_Int i; SANE_Status status; DBG (7, "usb_low_start_rowing: start\n"); if (chip->loop_count == 0) { DBG (3, "usb_low_start_rowing loop_count hasn't been set yet\n"); return SANE_STATUS_INVAL; } if (chip->cmt_table_length_word == 0) { DBG (3, "usb_low_start_rowing: cmt_table_length_word hasn't been set " "yet\n"); return SANE_STATUS_INVAL; } if (chip->cmt_table_length_word <= chip->cmt_second_pos_word) { DBG (3, "usb_low_start_rowing: cmt_second_pos_word cannot be larger " "than cmt_table_length_word\n"); return SANE_STATUS_INVAL; } for (i = 0; i < (int) chip->cmt_second_pos_word; i++) { if (chip->is_transfer_table[i]) line_of_first++; } for (; i < (int) chip->cmt_table_length_word; i++) { if (chip->is_transfer_table[i]) { line_of_first++; line_of_second++; } } chip->total_lines = ((SANE_Word) (chip->loop_count - 1)) * line_of_second + line_of_first; chip->lines_left = chip->total_lines; RIE (usb_low_start_cmt_table (chip)); DBG (7, "usb_low_start_rowing: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_stop_rowing (ma1017 * chip) { SANE_Status status; DBG (7, "usb_low_stop_rowing: start\n"); RIE (usb_low_stop_cmt_table (chip)); DBG (7, "usb_low_stop_rowing: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_wait_rowing_stop (ma1017 * chip) { SANE_Status status; DBG (7, "usb_low_wait_rowing_stop: start\n"); if (chip->total_lines != 0) { DBG (3, "usb_low_wait_rowing_stop: total_lines must be 0\n"); return SANE_STATUS_INVAL; } RIE (usb_low_wait_rowing (chip)); chip->is_rowing = SANE_FALSE; DBG (7, "usb_low_wait_rowing_stop: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_read_all_registers (ma1017 * chip) { SANE_Status status; DBG (7, "usb_low_read_all_registers: start\n"); RIE (usb_low_get_a2 (chip, 0)); RIE (usb_low_get_a4 (chip, 0)); RIE (usb_low_get_a6 (chip, 0)); RIE (usb_low_get_a7 (chip, 0)); RIE (usb_low_get_a8 (chip, 0)); RIE (usb_low_get_a9 (chip, 0)); RIE (usb_low_get_a10 (chip, 0)); RIE (usb_low_get_a11 (chip, 0)); RIE (usb_low_get_a12 (chip, 0)); RIE (usb_low_get_a13 (chip, 0)); RIE (usb_low_get_a15 (chip, 0)); RIE (usb_low_get_a16 (chip, 0)); RIE (usb_low_get_a17 (chip, 0)); RIE (usb_low_get_a18 (chip, 0)); RIE (usb_low_get_a19 (chip, 0)); RIE (usb_low_get_a20 (chip, 0)); RIE (usb_low_get_a21 (chip, 0)); RIE (usb_low_get_a22 (chip, 0)); RIE (usb_low_get_a23 (chip, 0)); RIE (usb_low_get_a24 (chip, 0)); RIE (usb_low_get_a27 (chip, 0)); return SANE_STATUS_GOOD; DBG (7, "usb_low_read_all_registers: exit\n"); } SANE_Status usb_low_get_row (ma1017 * chip, SANE_Byte * data, SANE_Word * lines_left) { SANE_Status status; DBG (7, "usb_low_get_row: start\n"); RIE ((*chip->get_row) (chip, data, lines_left)); DBG (7, "usb_low_get_row: exit\n"); return SANE_STATUS_GOOD;; } SANE_Status usb_low_get_row_direct (ma1017 * chip, SANE_Byte * data, SANE_Word * lines_left) { SANE_Status status; DBG (7, "usb_low_get_row_direct: start\n"); if (chip->lines_left == 0) { DBG (3, "usb_low_get_row_direct: lines_left == 0\n"); return SANE_STATUS_INVAL; } if (chip->lines_left <= 1) { RIE (usb_low_read_rows (chip, data, chip->byte_width)); RIE (usb_low_wait_rowing (chip)); chip->lines_left = 0x00; chip->is_rowing = SANE_FALSE; *lines_left = 0; } else { RIE (usb_low_read_rows (chip, data, chip->byte_width)); chip->lines_left--; *lines_left = chip->lines_left; } DBG (7, "usb_low_get_row_direct: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_get_row_resample (ma1017 * chip, SANE_Byte * data, SANE_Word * lines_left) { static SANE_Byte resample_buffer[8 * 1024]; SANE_Word *pixel_temp; SANE_Word i; SANE_Word j; SANE_Word k; SANE_Status status; DBG (7, "usb_low_get_row_resample: start\n"); if (chip->lines_left == 0) { DBG (3, "usb_low_get_row_resample: lines_left == 0\n"); return SANE_STATUS_INVAL; } if (chip->lines_left <= 1) { RIE (usb_low_read_rows (chip, resample_buffer, chip->byte_width)); if ((chip->sensor == ST_CANON600) && (chip->pixel_depth == 0x20)) { pixel_temp = (SANE_Word *) malloc (6 * 1024 * sizeof (SANE_Word)); if (!pixel_temp) return SANE_STATUS_NO_MEM; j = 0; for (i = 0; i < chip->byte_width; i += 3) { pixel_temp[j] = (SANE_Word) resample_buffer[i]; pixel_temp[j] |= ((SANE_Word) (resample_buffer[i + 1] & 0xf0)) << 4; j++; pixel_temp[j] = ((SANE_Word) (resample_buffer[i + 1] & 0x0f)) << 8; pixel_temp[j] |= (SANE_Word) resample_buffer[i + 2]; j++; } k = 0; for (i = 0; i < j; i += chip->soft_resample * 2) { data[k] = (SANE_Byte) (pixel_temp[i] & 0x00ff); k++; data[k] = (SANE_Byte) ((pixel_temp[i] & 0x0f00) >> 4); data[k] |= (SANE_Byte) ((pixel_temp[i + 2] & 0x0f00) >> 8); k++; data[k] = (SANE_Byte) (pixel_temp[i + 2] & 0x00ff); k++; } free (pixel_temp); } else /* fixme ? */ { for (i = 0; i < chip->byte_width; i += chip->soft_resample) *(data++) = resample_buffer[i]; } RIE (usb_low_wait_rowing (chip)); chip->lines_left = 0x00; chip->is_rowing = SANE_FALSE; *lines_left = 0; } else { RIE (usb_low_read_rows (chip, resample_buffer, chip->byte_width)); if ((chip->sensor == ST_CANON600) && (chip->pixel_depth == 0x20)) { pixel_temp = (SANE_Word *) malloc (6 * 1024 * sizeof (SANE_Word)); if (!pixel_temp) return SANE_STATUS_NO_MEM; j = 0; for (i = 0; i < chip->byte_width; i += 3) { pixel_temp[j] = (SANE_Word) resample_buffer[i]; pixel_temp[j] |= ((SANE_Word) (resample_buffer[i + 1] & 0xf0)) << 4; j++; pixel_temp[j] = ((SANE_Word) (resample_buffer[i + 1] & 0x0f)) << 8; pixel_temp[j] |= (SANE_Word) resample_buffer[i + 2]; j++; } k = 0; for (i = 0; i < j; i += chip->soft_resample * 2) { data[k] = (SANE_Byte) (pixel_temp[i] & 0x00ff); k++; data[k] = (SANE_Byte) ((pixel_temp[i] & 0x0f00) >> 4); data[k] |= (SANE_Byte) ((pixel_temp[i + 2] & 0x0f00) >> 8); k++; data[k] = (SANE_Byte) (pixel_temp[i + 2] & 0x00ff); k++; } free (pixel_temp); } else /* fixme? */ { for (i = 0; i < chip->byte_width; i += chip->soft_resample) *(data++) = resample_buffer[i]; } chip->lines_left--; *lines_left = chip->lines_left; } DBG (7, "usb_low_get_row_resample: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_wait_rowing (ma1017 * chip) { SANE_Byte read_byte; size_t n; SANE_Status status; DBG (7, "usb_low_wait_rowing: start\n"); if (!chip->is_opened) { DBG (3, "usb_low_wait_rowing: open first\n"); return SANE_STATUS_INVAL; } if (!chip->is_rowing) { DBG (3, "usb_low_wait_rowing: not rowing\n"); return SANE_STATUS_INVAL; } n = 1; status = sanei_usb_read_bulk (chip->fd, (SANE_Byte *) & read_byte, &n); if (status != SANE_STATUS_GOOD || n != 1) { DBG (3, "usb_low_wait_rowing: couldn't read: %s\n", sane_strstatus (status)); return SANE_STATUS_IO_ERROR; } chip->total_read_urbs++; chip->is_rowing = SANE_FALSE; DBG (7, "usb_low_wait_rowing: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_read_rows (ma1017 * chip, SANE_Byte * data, SANE_Word byte_count) { size_t n, bytes_total; SANE_Status status; DBG (7, "usb_low_read_rows: start\n"); if (!(chip->is_opened)) { DBG (3, "usb_low_read_rows: is_opened==SANE_FALSE\n"); return SANE_STATUS_INVAL; } if (!(chip->is_rowing)) { DBG (3, "usb_low_read_rows: is_rowing==SANE_FALSE\n"); return SANE_STATUS_INVAL; } n = MIN (byte_count, chip->max_block_size); bytes_total = 0; while ((SANE_Word) bytes_total < byte_count) { status = sanei_usb_read_bulk (chip->fd, (SANE_Byte *) (data + bytes_total), &n); if (status != SANE_STATUS_GOOD) { DBG (7, "usb_low_read_rows: problems during read: %s -- exiting\n", sane_strstatus (status)); return status; } /* Count the number of URBs. This is a bit tricky, as we are reading bigger chunks here but the scanner can only handle 64 bytes at once. */ chip->total_read_urbs += ((n + 63) / 64); bytes_total += n; if ((SANE_Word) bytes_total != byte_count) { DBG (7, "usb_low_read_rows: wanted %d, got %d " "bytes (%d in total) -- retrying\n", byte_count, (SANE_Word) n, (SANE_Word) bytes_total); } n = MIN ((byte_count - (SANE_Word) bytes_total), chip->max_block_size); } DBG (7, "usb_low_read_rows: exit, read %d bytes\n", (SANE_Word) bytes_total); return SANE_STATUS_GOOD; } SANE_Status usb_low_write_reg (ma1017 * chip, SANE_Byte reg_no, SANE_Byte data) { size_t n; SANE_Status status; SANE_Byte data_field[2]; data_field[0] = data; data_field[1] = reg_no; if (!chip->is_opened) { DBG (3, "usb_low_write_reg: open first\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_write_reg: rowing, stop first\n"); return SANE_STATUS_INVAL; } if (reg_no > 0x20) { DBG (3, "usb_low_write_reg: reg_no out of range\n"); return SANE_STATUS_INVAL; } n = 2; status = sanei_usb_write_bulk (chip->fd, data_field, &n); if (status != SANE_STATUS_GOOD || n != 2) { DBG (3, "usb_low_write_reg: couldn't write, tried to write %d, " "wrote %lu: %s\n", 2, (unsigned long int) n, sane_strstatus (status)); return SANE_STATUS_IO_ERROR; } chip->total_write_urbs++; DBG (7, "usb_low_write_reg: reg: 0x%02x, value: 0x%02x\n", reg_no, data); return SANE_STATUS_GOOD; } SANE_Status usb_low_read_reg (ma1017 * chip, SANE_Byte reg_no, SANE_Byte * data) { SANE_Byte data_field[2]; SANE_Byte read_byte; size_t n; SANE_Status status; data_field[0] = 0x00; data_field[1] = reg_no | 0x20; if (!chip->is_opened) { DBG (3, "usb_low_read_reg: open first\n"); return SANE_STATUS_INVAL; } if (chip->is_rowing) { DBG (3, "usb_low_read_reg: rowing, stop first\n"); return SANE_STATUS_INVAL; } if (reg_no > 0x20) { DBG (3, "usb_low_read_reg: reg_no out of range\n"); return SANE_STATUS_INVAL; } n = 2; DBG (5, "usb_low_read_reg: trying to write %lu bytes\n", (unsigned long int) n); status = sanei_usb_write_bulk (chip->fd, data_field, &n); if (status != SANE_STATUS_GOOD || n != 2) { DBG (3, "usb_low_read_reg: couldn't write, tried to write %d, " "wrote %lu: %s\n", 2, (unsigned long int) n, sane_strstatus (status)); return SANE_STATUS_IO_ERROR; } chip->total_write_urbs++; n = 1; DBG (5, "usb_low_read_reg: trying to read %lu bytes\n", (unsigned long int) n); status = sanei_usb_read_bulk (chip->fd, (SANE_Byte *) & read_byte, &n); if (status != SANE_STATUS_GOOD || n != 1) { DBG (3, "usb_low_read_reg: couldn't read, tried to read %lu, " "read %lu: %s\n", (unsigned long int) 1, (unsigned long int) n, sane_strstatus (status)); return SANE_STATUS_IO_ERROR; } chip->total_read_urbs++; if (data) *data = read_byte; DBG (7, "usb_low_read_reg: Reg: 0x%02x, Value: 0x%02x\n", reg_no, read_byte); return SANE_STATUS_GOOD; } SANE_Status usb_low_identify_scanner (SANE_Int fd, Mustek_Type * scanner_type) { SANE_Status status; SANE_Word devvendor, devproduct; Mustek_Type devtype; DBG (7, "usb_low_identify_scanner: start\n"); status = sanei_usb_get_vendor_product (fd, &devvendor, &devproduct); devtype = MT_UNKNOWN; if (status == SANE_STATUS_GOOD) { if (devvendor == 0x055f) { switch (devproduct) { case 0x0001: devtype = MT_1200CU; break; case 0x0002: devtype = MT_600CU; break; case 0x0003: devtype = MT_1200USB; break; case 0x0006: devtype = MT_1200UB; break; case 0x0008: devtype = MT_1200CU_PLUS; break; case 0x0873: devtype = MT_600USB; break; default: if (scanner_type) *scanner_type = devtype; DBG (3, "usb_low_identify_scanner: unknown product id: " "0x%04x\n", devproduct); return SANE_STATUS_INVAL; break; } } else { if (scanner_type) *scanner_type = devtype; DBG (3, "usb_low_identify_scanner: unknown vendor id: 0x%04d\n", devvendor); return SANE_STATUS_INVAL; } } if (scanner_type) *scanner_type = devtype; DBG (7, "usb_low_identify_scanner: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_low_open (ma1017 * chip, SANE_String_Const devname) { SANE_Status status; Mustek_Type scanner_type; DBG (7, "usb_low_open: start: chip = %p\n", (void *) chip); if (chip->is_rowing) { DBG (3, "usb_low_open: already rowing\n"); return SANE_STATUS_INVAL; } if (chip->is_opened) { DBG (3, "usb_low_open: already opened\n"); return SANE_STATUS_INVAL; } status = sanei_usb_open ((SANE_String_Const) devname, &chip->fd); if (status == SANE_STATUS_GOOD) { DBG (7, "usb_low_open: device %s successfully opened\n", devname); chip->is_opened = SANE_TRUE; /* Try to get vendor and device ids */ DBG (7, "usb_low_open: trying to identify device `%s'\n", devname); status = usb_low_identify_scanner (chip->fd, &scanner_type); if (status != SANE_STATUS_GOOD) { DBG (3, "usb_low_open: device `%s' doesn't look like a supported " "scanner\n", devname); sanei_usb_close (chip->fd); return status; } else { if (scanner_type == MT_UNKNOWN) { DBG (3, "usb_low_open: device `%s' can't be identified\n", devname); } else if (scanner_type != chip->scanner_type) { DBG (3, "usb_low_open: device `%s' is supported but" "it's not the same as at the start\n", devname); return SANE_STATUS_INVAL; } } } else { DBG (1, "usb_low_open: device %s couldn't be opened: %s\n", devname, sane_strstatus (status)); return status; } chip->is_opened = SANE_TRUE; RIE (usb_low_read_all_registers (chip)); DBG (7, "usb_low_open: exit, type is %d\n", scanner_type); return SANE_STATUS_GOOD; } SANE_Status usb_low_close (ma1017 * chip) { DBG (7, "usb_low_close: start, chip=%p\n", (void *) chip); if (!chip->is_opened) { DBG (3, "usb_low_close: already close or never opened\n"); return SANE_STATUS_INVAL; } if (chip->fd >= 0) { SANE_Byte dummy; if (chip->is_rowing) usb_low_stop_rowing (chip); /* Now make sure that both the number of written and read URBs is even. Use some dummy writes/reads. That's to avoid a nasty bug in the MA 1017 chipset that causes timeouts when the number of URBs is odd (toggle bug). */ if ((chip->total_read_urbs % 2) == 1) usb_low_get_a4 (chip, &dummy); if ((chip->total_write_urbs % 2) == 1) usb_low_set_fix_pattern (chip, SANE_FALSE); sanei_usb_close (chip->fd); chip->fd = -1; } chip->is_opened = SANE_FALSE; chip->is_rowing = SANE_FALSE; DBG (7, "usb_low_close: exit\n"); return SANE_STATUS_GOOD; } backends-1.3.0/backend/mustek_usb_low.h000066400000000000000000000273541456256263500201200ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2000 Mustek. Originally maintained by Tom Wang Copyright (C) 2001, 2002 by Henning Meier-Geinitz. 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, see . As a special exception, the authors of SANE give permission for additional uses of the libraries contained in this release of SANE. The exception is that, if you link a SANE library with other files to produce an executable, this does not by itself cause the resulting executable to be covered by the GNU General Public License. Your use of that executable is in no way restricted on account of linking the SANE library code into it. This exception does not, however, invalidate any other reasons why the executable file might be covered by the GNU General Public License. If you submit changes to SANE to the maintainers to be included in a subsequent release, you agree by submitting the changes that those changes may be distributed with this exception intact. If you write modifications of your own for SANE, it is your choice whether to permit this exception to apply to your modifications. If you do not wish that, delete this exception notice. This file implements a SANE backend for Mustek 1200UB and similar USB flatbed scanners. */ #ifndef mustek_usb_low_h #define mustek_usb_low_h #include "../include/sane/sane.h" /* ---------------------------------- macros ------------------------------ */ /* calculate the minimum/maximum values */ #if defined(MIN) #undef MIN #endif #if defined(MAX) #undef MAX #endif #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MAX(a, b) (((a) > (b)) ? (a) : (b)) /* return the lower/upper 8 bits of a 16 bit word */ #define HIBYTE(w) ((SANE_Byte)(((SANE_Word)(w) >> 8) & 0xFF)) #define LOBYTE(w) ((SANE_Byte)(w)) /* RIE: return if error */ #define RIE(function) do {status = function; if (status != SANE_STATUS_GOOD) \ return status;} while (SANE_FALSE) /* ---------------------------------- types ------------------------------- */ typedef enum Mustek_Type { MT_UNKNOWN = 0, MT_1200USB, MT_1200UB, MT_1200CU, MT_1200CU_PLUS, MT_600CU, MT_600USB } Mustek_Type; typedef enum Sensor_Type { ST_NONE = 0, ST_INI = 1, ST_INI_DARK = 2, ST_CANON300 = 3, ST_CANON600 = 4, ST_TOSHIBA600 = 5, ST_CANON300600 = 6, ST_NEC600 = 7 } Sensor_Type; typedef enum Motor_Type { MT_NONE = 0, MT_600 = 1, MT_1200 = 2 } Motor_Type; struct ma1017; typedef struct ma1017 { int fd; SANE_Bool is_opened; SANE_Bool is_rowing; /* A2 */ SANE_Byte append; SANE_Byte test_sram; SANE_Byte fix_pattern; /* A4 */ SANE_Byte select; SANE_Byte frontend; /* A6 */ SANE_Byte rgb_sel_pin; SANE_Byte asic_io_pins; /* A7 */ SANE_Byte timing; SANE_Byte sram_bank; /* A8 */ SANE_Byte dummy_msb; SANE_Byte ccd_width_msb; SANE_Byte cmt_table_length; /* A9 */ SANE_Byte cmt_second_pos; /* A10 + A8ID5 */ SANE_Word ccd_width; /* A11 + A8ID6 */ SANE_Word dummy; /* A12 + A13 */ SANE_Word byte_width; /* A14 + A30W */ SANE_Word loop_count; /* A15 */ SANE_Byte motor_enable; SANE_Byte motor_movement; SANE_Byte motor_direction; SANE_Byte motor_signal; SANE_Byte motor_home; /* A16 */ SANE_Byte pixel_depth; SANE_Byte image_invert; SANE_Byte optical_600; SANE_Byte sample_way; /* A17 + A18 + A19 */ SANE_Byte red_ref; SANE_Byte green_ref; SANE_Byte blue_ref; /* A20 + A21 + A22 */ SANE_Byte red_pd; SANE_Byte green_pd; SANE_Byte blue_pd; /* A23 */ SANE_Byte a23; /* A24 */ SANE_Byte fy1_delay; SANE_Byte special_ad; /* A27 */ SANE_Byte sclk; SANE_Byte sen; SANE_Byte serial_length; /* Use for Rowing */ SANE_Status (*get_row) (struct ma1017 * chip, SANE_Byte * row, SANE_Word * lines_left); SANE_Word cmt_table_length_word; SANE_Word cmt_second_pos_word; SANE_Word row_size; SANE_Word soft_resample; SANE_Word total_lines; SANE_Word lines_left; SANE_Bool is_transfer_table[32]; Sensor_Type sensor; Motor_Type motor; Mustek_Type scanner_type; SANE_Word max_block_size; SANE_Word total_read_urbs; SANE_Word total_write_urbs; } ma1017; typedef enum Channel { CH_NONE = 0, CH_RED = 1, CH_GREEN = 2, CH_BLUE = 3 } Channel; typedef enum Banksize { BS_NONE = 0, BS_4K = 1, BS_8K = 2, BS_16K = 3 } Banksize; typedef enum Pixeldepth { PD_NONE = 0, PD_1BIT = 1, PD_4BIT = 2, PD_8BIT = 3, PD_12BIT = 4 } Pixeldepth; typedef enum Sampleway { SW_NONE = 0, SW_P1P6 = 1, SW_P2P6 = 2, SW_P3P6 = 3, SW_P4P6 = 4, SW_P5P6 = 5, SW_P6P6 = 6 } Sampleway; /* ------------------------- function declarations ------------------------ */ static SANE_Status usb_low_init (ma1017 ** chip); static SANE_Status usb_low_exit (ma1017 * chip); /* Register read and write functions */ /* A0 ~ A1 */ static SANE_Status usb_low_set_cmt_table (ma1017 * chip, SANE_Int index, Channel channel, SANE_Bool is_move_motor, SANE_Bool is_transfer); /* A2 */ static SANE_Status usb_low_get_a2 (ma1017 * chip, SANE_Byte * value); static SANE_Status usb_low_start_cmt_table (ma1017 * chip); static SANE_Status usb_low_stop_cmt_table (ma1017 * chip); static SANE_Status usb_low_set_test_sram_mode (ma1017 * chip, SANE_Bool is_test); static SANE_Status usb_low_set_fix_pattern (ma1017 * chip, SANE_Bool is_fix); /* A3 */ static SANE_Status usb_low_adjust_timing (ma1017 * chip, SANE_Byte data); /* A4 */ static SANE_Status usb_low_get_a4 (ma1017 * chip, SANE_Byte * value); static SANE_Status usb_low_select_timing (ma1017 * chip, SANE_Byte data); static SANE_Status usb_low_turn_frontend_mode (ma1017 * chip, SANE_Bool is_on); /* A6 */ static SANE_Status usb_low_get_a6 (ma1017 * chip, SANE_Byte * value); static SANE_Status usb_low_set_asic_io_pins (ma1017 * chip, SANE_Byte data); static SANE_Status usb_low_set_rgb_sel_pins (ma1017 * chip, SANE_Byte data); /* A7 */ static SANE_Status usb_low_get_a7 (ma1017 * chip, SANE_Byte * value); static SANE_Status usb_low_set_timing (ma1017 * chip, SANE_Byte data); static SANE_Status usb_low_set_sram_bank (ma1017 * chip, Banksize banksize); /* A8 */ static SANE_Status usb_low_get_a8 (ma1017 * chip, SANE_Byte * value); static SANE_Status usb_low_set_cmt_table_length (ma1017 * chip, SANE_Byte table_length); /* A9 */ static SANE_Status usb_low_get_a9 (ma1017 * chip, SANE_Byte * value); static SANE_Status usb_low_set_cmt_second_position (ma1017 * chip, SANE_Byte position); /* A10 + A8ID5 */ static SANE_Status usb_low_get_a10 (ma1017 * chip, SANE_Byte * value); static SANE_Status usb_low_set_ccd_width (ma1017 * chip, SANE_Word ccd_width); /* A11 + A8ID6 */ static SANE_Status usb_low_get_a11 (ma1017 * chip, SANE_Byte * value); static SANE_Status usb_low_set_dummy (ma1017 * chip, SANE_Word dummy); /* A12 + A13 */ static SANE_Status usb_low_get_a12 (ma1017 * chip, SANE_Byte * value); static SANE_Status usb_low_get_a13 (ma1017 * chip, SANE_Byte * value); static SANE_Status usb_low_set_image_byte_width (ma1017 * chip, SANE_Word row_size); static SANE_Status usb_low_set_soft_resample (ma1017 * chip, SANE_Word soft_resample); /* A14 + A30W */ static SANE_Status usb_low_set_cmt_loop_count (ma1017 * chip, SANE_Word loop_count); /* A15 */ static SANE_Status usb_low_get_a15 (ma1017 * chip, SANE_Byte * value); static SANE_Status usb_low_enable_motor (ma1017 * chip, SANE_Bool is_enable); static SANE_Status usb_low_set_motor_movement (ma1017 * chip, SANE_Bool is_full_step, SANE_Bool is_double_phase, SANE_Bool is_two_step); static SANE_Status usb_low_set_motor_signal (ma1017 * chip, SANE_Byte signal); static SANE_Status usb_low_set_motor_direction (ma1017 * chip, SANE_Bool is_backward); static SANE_Status usb_low_move_motor_home (ma1017 * chip, SANE_Bool is_home, SANE_Bool is_backward); /* A16 */ static SANE_Status usb_low_get_a16 (ma1017 * chip, SANE_Byte * value); static SANE_Status usb_low_set_image_dpi (ma1017 * chip, SANE_Bool is_optical600, Sampleway sampleway); static SANE_Status usb_low_set_pixel_depth (ma1017 * chip, Pixeldepth pixeldepth); static SANE_Status usb_low_invert_image (ma1017 * chip, SANE_Bool is_invert); /* A17 + A18 + A19 */ static SANE_Status usb_low_get_a17 (ma1017 * chip, SANE_Byte * value); static SANE_Status usb_low_get_a18 (ma1017 * chip, SANE_Byte * value); static SANE_Status usb_low_get_a19 (ma1017 * chip, SANE_Byte * value); static SANE_Status usb_low_set_red_ref (ma1017 * chip, SANE_Byte red_ref); static SANE_Status usb_low_set_green_ref (ma1017 * chip, SANE_Byte green_ref); static SANE_Status usb_low_set_blue_ref (ma1017 * chip, SANE_Byte blue_ref); /* A20 + A21 + A22 */ static SANE_Status usb_low_get_a20 (ma1017 * chip, SANE_Byte * value); static SANE_Status usb_low_get_a21 (ma1017 * chip, SANE_Byte * value); static SANE_Status usb_low_get_a22 (ma1017 * chip, SANE_Byte * value); static SANE_Status usb_low_set_red_pd (ma1017 * chip, SANE_Byte red_pd); static SANE_Status usb_low_set_green_pd (ma1017 * chip, SANE_Byte green_pd); static SANE_Status usb_low_set_blue_pd (ma1017 * chip, SANE_Byte blue_pd); /* A23 */ static SANE_Status usb_low_get_a23 (ma1017 * chip, SANE_Byte * value); static SANE_Status usb_low_turn_peripheral_power (ma1017 * chip, SANE_Bool is_on); static SANE_Status usb_low_turn_lamp_power (ma1017 * chip, SANE_Bool is_on); static SANE_Status usb_low_set_io_3 (ma1017 * chip, SANE_Bool is_high); static SANE_Status usb_low_set_led_light_all (ma1017 * chip, SANE_Bool is_light_all); /* A24 */ static SANE_Status usb_low_get_a24 (ma1017 * chip, SANE_Byte * value); static SANE_Status usb_low_set_ad_timing (ma1017 * chip, SANE_Byte pattern); /* A25 + A26 */ static SANE_Status usb_low_set_serial_byte1 (ma1017 * chip, SANE_Byte data); static SANE_Status usb_low_set_serial_byte2 (ma1017 * chip, SANE_Byte data); /* A27 */ static SANE_Status usb_low_get_a27 (ma1017 * chip, SANE_Byte * value); static SANE_Status usb_low_set_serial_format (ma1017 * chip, SANE_Byte data); /* A31 */ static SANE_Status usb_low_get_home_sensor (ma1017 * chip); /* Special Mode */ static SANE_Status usb_low_start_rowing (ma1017 * chip); static SANE_Status usb_low_stop_rowing (ma1017 * chip); static SANE_Status usb_low_wait_rowing_stop (ma1017 * chip); /* Global functions */ static SANE_Status usb_low_read_all_registers (ma1017 * chip); static SANE_Status usb_low_get_row (ma1017 * chip, SANE_Byte * data, SANE_Word * lines_left); static SANE_Status usb_low_get_row_direct (ma1017 * chip, SANE_Byte * data, SANE_Word * lines_left); static SANE_Status usb_low_get_row_resample (ma1017 * chip, SANE_Byte * data, SANE_Word * lines_left); /* Direct access */ static SANE_Status usb_low_wait_rowing (ma1017 * chip); static SANE_Status usb_low_read_rows (ma1017 * chip, SANE_Byte * data, SANE_Word byte_count); static SANE_Status usb_low_write_reg (ma1017 * chip, SANE_Byte reg_no, SANE_Byte data); static SANE_Status usb_low_read_reg (ma1017 * chip, SANE_Byte reg_no, SANE_Byte * data); static SANE_Status usb_low_identify_scanner (SANE_Int fd, Mustek_Type * scanner_type); static SANE_Status usb_low_open (ma1017 * chip, const char *devname); static SANE_Status usb_low_close (ma1017 * chip); #endif /* defined mustek_usb_low_h */ backends-1.3.0/backend/mustek_usb_mid.c000066400000000000000000002572031456256263500200610ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2000 Mustek. Originally maintained by Tom Wang Copyright (C) 2001, 2002 by Henning Meier-Geinitz. 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, see . As a special exception, the authors of SANE give permission for additional uses of the libraries contained in this release of SANE. The exception is that, if you link a SANE library with other files to produce an executable, this does not by itself cause the resulting executable to be covered by the GNU General Public License. Your use of that executable is in no way restricted on account of linking the SANE library code into it. This exception does not, however, invalidate any other reasons why the executable file might be covered by the GNU General Public License. If you submit changes to SANE to the maintainers to be included in a subsequent release, you agree by submitting the changes that those changes may be distributed with this exception intact. If you write modifications of your own for SANE, it is your choice whether to permit this exception to apply to your modifications. If you do not wish that, delete this exception notice. This file implements a SANE backend for Mustek 1200UB and similar USB flatbed scanners. */ #include "mustek_usb_mid.h" #include "mustek_usb_low.c" /* ------------------ sensor NEC 3797 600 CIS functions ------------------- */ static SANE_Word usb_mid_n600_optical_x_dpi[] = { 600, 400, 300, 200, 100, 50, 0 }; SANE_Status usb_mid_n600_prepare_rgb_600_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_n600_prepare_rgb_600_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P6P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_n600_prepare_rgb_600_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_n600_prepare_rgb_400_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_n600_prepare_rgb_400_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P4P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_n600_prepare_rgb_400_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_n600_prepare_rgb_300_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_n600_prepare_rgb_300_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P3P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_n600_prepare_rgb_300_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_n600_prepare_rgb_200_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_n600_prepare_rgb_200_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P2P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_n600_prepare_rgb_200_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_n600_prepare_rgb_100_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_n600_prepare_rgb_100_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P1P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_n600_prepare_rgb_100_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_n600_prepare_rgb_50_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_n600_prepare_rgb_50_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P1P6)); RIE (usb_low_set_soft_resample (chip, 2)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_n600_prepare_rgb_50_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_n600_prepare_mono_600_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_n600_prepare_mono_600_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P6P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_TRUE)); DBG (6, "usb_mid_n600_prepare_mono_600_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_n600_prepare_mono_400_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_n600_prepare_mono_400_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P4P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_TRUE)); DBG (6, "usb_mid_n600_prepare_mono_400_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_n600_prepare_mono_300_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_n600_prepare_mono_300_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P3P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_TRUE)); DBG (6, "usb_mid_n600_prepare_mono_300_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_n600_prepare_mono_200_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_n600_prepare_mono_200_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P2P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_TRUE)); DBG (6, "usb_mid_n600_prepare_mono_200_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_n600_prepare_mono_100_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_n600_prepare_mono_100_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P1P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_TRUE)); DBG (6, "usb_mid_n600_prepare_mono_100_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_n600_prepare_mono_50_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_n600_prepare_mono_50_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P1P6)); RIE (usb_low_set_soft_resample (chip, 2)); RIE (usb_low_set_led_light_all (chip, SANE_TRUE)); DBG (6, "usb_mid_n600_prepare_mono_50_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_n600_prepare_rgb (ma1017 * chip, SANE_Word dpi) { DBG (6, "usb_mid_n600_prepare_rgb: start\n"); switch (dpi) { case 50: return usb_mid_n600_prepare_rgb_50_dpi (chip); break; case 100: return usb_mid_n600_prepare_rgb_100_dpi (chip); break; case 200: return usb_mid_n600_prepare_rgb_200_dpi (chip); break; case 300: return usb_mid_n600_prepare_rgb_300_dpi (chip); break; case 400: return usb_mid_n600_prepare_rgb_400_dpi (chip); break; case 600: return usb_mid_n600_prepare_rgb_600_dpi (chip); break; default: DBG (3, "usb_mid_n600_prepare_rgb: unmatched dpi: %d\n", dpi); return SANE_STATUS_INVAL; break; } DBG (6, "usb_mid_n600_prepare_rgb: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_n600_prepare_mono (ma1017 * chip, SANE_Word dpi) { DBG (6, "usb_mid_n600_prepare_mono: start\n"); switch (dpi) { case 50: return usb_mid_n600_prepare_mono_50_dpi (chip); break; case 100: return usb_mid_n600_prepare_mono_100_dpi (chip); break; case 200: return usb_mid_n600_prepare_mono_200_dpi (chip); break; case 300: return usb_mid_n600_prepare_mono_300_dpi (chip); break; case 400: return usb_mid_n600_prepare_mono_400_dpi (chip); break; case 600: return usb_mid_n600_prepare_mono_600_dpi (chip); break; default: DBG (6, "usb_mid_n600_prepare_mono: unmatched dpi: %d\n", dpi); return SANE_STATUS_INVAL; break; } return SANE_STATUS_GOOD; } /* ---------------------- sensor 600 CIS functions ----------------------- */ static SANE_Word usb_mid_c600_optical_x_dpi[] = { 600, 400, 300, 200, 150, 100, 50, 0 }; SANE_Status usb_mid_c600_prepare_rgb_600_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c600_prepare_rgb_600_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P6P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c600_prepare_rgb_600_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c600_prepare_rgb_400_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c600_prepare_rgb_400_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P4P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c600_prepare_rgb_400_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c600_prepare_rgb_300_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c600_prepare_rgb_300_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P3P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c600_prepare_rgb_300_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c600_prepare_rgb_200_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c600_prepare_rgb_200_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P2P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c600_prepare_rgb_200_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c600_prepare_rgb_150_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c600_prepare_rgb_150_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P3P6)); RIE (usb_low_set_soft_resample (chip, 2)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c600_prepare_rgb_150_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c600_prepare_rgb_100_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c600_prepare_rgb_100_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P1P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c600_prepare_rgb_100_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c600_prepare_rgb_50_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c600_prepare_rgb_50_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P1P6)); RIE (usb_low_set_soft_resample (chip, 2)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c600_prepare_rgb_50_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c600_prepare_mono_600_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c600_prepare_mono_600_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P6P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c600_prepare_mono_600_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c600_prepare_mono_400_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c600_prepare_mono_400_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P4P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c600_prepare_mono_400_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c600_prepare_mono_300_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c600_prepare_mono_300_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P3P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c600_prepare_mono_300_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c600_prepare_mono_200_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c600_prepare_mono_200_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P2P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c600_prepare_mono_200_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c600_prepare_mono_150_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c600_prepare_mono_150_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P3P6)); RIE (usb_low_set_soft_resample (chip, 2)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c600_prepare_mono_150_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c600_prepare_mono_100_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c600_prepare_mono_100_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P1P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c600_prepare_mono_100_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c600_prepare_mono_50_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c600_prepare_mono_50_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P1P6)); RIE (usb_low_set_soft_resample (chip, 2)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c600_prepare_mono_50_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c600_prepare_rgb (ma1017 * chip, SANE_Word dpi) { DBG (6, "usb_mid_c600_prepare_rgb: start\n"); switch (dpi) { case 50: return usb_mid_c600_prepare_rgb_50_dpi (chip); break; case 100: return usb_mid_c600_prepare_rgb_100_dpi (chip); break; case 150: return usb_mid_c600_prepare_rgb_150_dpi (chip); break; case 200: return usb_mid_c600_prepare_rgb_200_dpi (chip); break; case 300: return usb_mid_c600_prepare_rgb_300_dpi (chip); break; case 400: return usb_mid_c600_prepare_rgb_400_dpi (chip); break; case 600: return usb_mid_c600_prepare_rgb_600_dpi (chip); break; default: DBG (3, "usb_mid_c600_prepare_rgb: unmatched dpi: %d\n", dpi); return SANE_STATUS_INVAL; break; } DBG (6, "usb_mid_c600_prepare_rgb: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c600_prepare_mono (ma1017 * chip, SANE_Word dpi) { DBG (6, "usb_mid_c600_prepare_mono: start\n"); switch (dpi) { case 50: return usb_mid_c600_prepare_mono_50_dpi (chip); break; case 100: return usb_mid_c600_prepare_mono_100_dpi (chip); break; case 150: return usb_mid_c600_prepare_mono_150_dpi (chip); break; case 200: return usb_mid_c600_prepare_mono_200_dpi (chip); break; case 300: return usb_mid_c600_prepare_mono_300_dpi (chip); break; case 400: return usb_mid_c600_prepare_mono_400_dpi (chip); break; case 600: return usb_mid_c600_prepare_mono_600_dpi (chip); break; default: DBG (6, "usb_mid_c600_prepare_mono: unmatched dpi: %d\n", dpi); return SANE_STATUS_INVAL; break; } return SANE_STATUS_GOOD; } /* ------------------- sensor 300/600 CIS functions ----------------------- */ static SANE_Word usb_mid_c300600_optical_x_dpi[] = { 600, 400, 300, 200, 150, 100, 50, 0 }; SANE_Status usb_mid_c300600_prepare_rgb_600_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c300600_prepare_rgb_600_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P6P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c300600_prepare_rgb_600_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300600_prepare_rgb_400_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c300600_prepare_rgb_400_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P4P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c300600_prepare_rgb_400_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300600_prepare_rgb_300_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c300600_prepare_rgb_300_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P6P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c300600_prepare_rgb_300_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300600_prepare_rgb_200_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c300600_prepare_rgb_200_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P2P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c300600_prepare_rgb_200_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300600_prepare_rgb_150_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c300600_prepare_rgb_150_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P3P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c300600_prepare_rgb_150_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300600_prepare_rgb_100_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c300600_prepare_rgb_100_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P2P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c300600_prepare_rgb_100_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300600_prepare_rgb_50_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c300600_prepare_rgb_50_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P1P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c300600_prepare_rgb_50_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300600_prepare_mono_600_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c300600_prepare_mono_600_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P6P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c300600_prepare_mono_600_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300600_prepare_mono_400_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c300600_prepare_mono_400_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P4P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c300600_prepare_mono_400_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300600_prepare_mono_300_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c300600_prepare_mono_300_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P6P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c300600_prepare_mono_300_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300600_prepare_mono_200_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c300600_prepare_mono_200_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P2P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c300600_prepare_mono_200_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300600_prepare_mono_150_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c300600_prepare_mono_150_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P3P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c300600_prepare_mono_150_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300600_prepare_mono_100_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c300600_prepare_mono_100_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P2P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c300600_prepare_mono_100_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300600_prepare_mono_50_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c300600_prepare_mono_50_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_FALSE, SW_P1P6)); RIE (usb_low_set_soft_resample (chip, 1)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c300600_prepare_mono_50_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300600_prepare_rgb (ma1017 * chip, SANE_Word dpi) { DBG (6, "usb_mid_c300600_prepare_rgb: start\n"); switch (dpi) { case 50: return usb_mid_c300600_prepare_rgb_50_dpi (chip); break; case 100: return usb_mid_c300600_prepare_rgb_100_dpi (chip); break; case 150: return usb_mid_c300600_prepare_rgb_150_dpi (chip); break; case 200: return usb_mid_c300600_prepare_rgb_200_dpi (chip); break; case 300: return usb_mid_c300600_prepare_rgb_300_dpi (chip); break; case 400: return usb_mid_c300600_prepare_rgb_400_dpi (chip); break; case 600: return usb_mid_c300600_prepare_rgb_600_dpi (chip); break; default: DBG (3, "usb_mid_c300600_prepare_rgb: unmatched dpi: %d\n", dpi); return SANE_STATUS_INVAL; break; } return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300600_prepare_mono (ma1017 * chip, SANE_Word dpi) { switch (dpi) { case 50: return usb_mid_c300600_prepare_mono_50_dpi (chip); break; case 100: return usb_mid_c300600_prepare_mono_100_dpi (chip); break; case 150: return usb_mid_c300600_prepare_mono_150_dpi (chip); break; case 200: return usb_mid_c300600_prepare_mono_200_dpi (chip); break; case 300: return usb_mid_c300600_prepare_mono_300_dpi (chip); break; case 400: return usb_mid_c300600_prepare_mono_400_dpi (chip); break; case 600: return usb_mid_c300600_prepare_mono_600_dpi (chip); break; default: DBG (3, "usb_mid_c300600_prepare_mono: unmatched dpi: %d\n", dpi); return SANE_STATUS_INVAL; break; } return SANE_STATUS_GOOD; } /* ---------------------- sensor 300 CIS functions ----------------------- */ static SANE_Word usb_mid_c300_optical_x_dpi[] = { 300, 200, 150, 100, 50, 0 }; SANE_Status usb_mid_c300_prepare_rgb_300_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c300_prepare_rgb_300_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P6P6)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c300_prepare_rgb_300_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300_prepare_rgb_200_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c300_prepare_rgb_200_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P4P6)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c300_prepare_rgb_200_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300_prepare_rgb_150_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c300_prepare_rgb_150_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P3P6)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c300_prepare_rgb_150_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300_prepare_rgb_100_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c300_prepare_rgb_100_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P2P6)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c300_prepare_rgb_100_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300_prepare_rgb_50_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c300_prepare_rgb_50_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P1P6)); RIE (usb_low_set_led_light_all (chip, SANE_FALSE)); DBG (6, "usb_mid_c300_prepare_rgb_50_dpi: start\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300_prepare_mono_300_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c300_prepare_mono_300_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P6P6)); RIE (usb_low_set_led_light_all (chip, SANE_TRUE)); DBG (6, "usb_mid_c300_prepare_mono_300_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300_prepare_mono_200_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c300_prepare_mono_200_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P4P6)); RIE (usb_low_set_led_light_all (chip, SANE_TRUE)); DBG (6, "usb_mid_c300_prepare_mono_200_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300_prepare_mono_150_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c300_prepare_mono_150_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P3P6)); RIE (usb_low_set_led_light_all (chip, SANE_TRUE)); DBG (6, "usb_mid_c300_prepare_mono_150_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300_prepare_mono_100_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c300_prepare_mono_100_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P2P6)); RIE (usb_low_set_led_light_all (chip, SANE_TRUE)); DBG (6, "usb_mid_c300_prepare_mono_100_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300_prepare_mono_50_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_c300_prepare_mono_50_dpi: start\n"); RIE (usb_low_set_image_dpi (chip, SANE_TRUE, SW_P1P6)); RIE (usb_low_set_led_light_all (chip, SANE_TRUE)); DBG (6, "usb_mid_c300_prepare_mono_50_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300_prepare_rgb (ma1017 * chip, SANE_Word dpi) { DBG (6, "usb_mid_c300_prepare_rgb: start\n"); switch (dpi) { case 50: return usb_mid_c300_prepare_rgb_50_dpi (chip); break; case 100: return usb_mid_c300_prepare_rgb_100_dpi (chip); break; case 150: return usb_mid_c300_prepare_rgb_150_dpi (chip); break; case 200: return usb_mid_c300_prepare_rgb_200_dpi (chip); break; case 300: return usb_mid_c300_prepare_rgb_300_dpi (chip); break; default: DBG (3, "usb_mid_c300_prepare_rgb: unmatched dpi: %d\n", dpi); return SANE_STATUS_INVAL; break; } return SANE_STATUS_GOOD; } SANE_Status usb_mid_c300_prepare_mono (ma1017 * chip, SANE_Word dpi) { DBG (6, "usb_mid_c300_prepare_mono: start\n"); switch (dpi) { case 50: return usb_mid_c300_prepare_mono_50_dpi (chip); break; case 100: return usb_mid_c300_prepare_mono_100_dpi (chip); break; case 150: return usb_mid_c300_prepare_mono_150_dpi (chip); break; case 200: return usb_mid_c300_prepare_mono_200_dpi (chip); break; case 300: return usb_mid_c300_prepare_mono_300_dpi (chip); break; default: DBG (3, "usb_mid_c300_prepare_mono: unmatched dpi: %d\n", dpi); return SANE_STATUS_INVAL; break; } return SANE_STATUS_GOOD; } /* -------------------------- sensor functions ---------------------------- */ SANE_Bool usb_mid_sensor_is600_mode (ma1017 * chip, SANE_Word dpi) { if (chip->sensor == ST_CANON300) { DBG (6, "usb_mid_sensor_is600_mode: chip=%p, dpi=%d, FALSE\n", (void *) chip, dpi); return SANE_FALSE; } else if ((chip->sensor == ST_CANON600) || (chip->sensor == ST_NEC600)) { DBG (6, "usb_mid_sensor_is600_mode: chip=%p, dpi=%d, TRUE\n", (void *) chip, dpi); return SANE_TRUE; } else { switch (dpi) { case 300: case 150: case 100: case 50: DBG (6, "usb_mid_sensor_is600_mode: chip=%p, dpi=%d, FALSE\n", (void *) chip, dpi); return SANE_FALSE; case 600: case 400: case 200: DBG (6, "usb_mid_sensor_is600_mode: chip=%p, dpi=%d, TRUE\n", (void *) chip, dpi); return SANE_TRUE; default: DBG (3, "usb_mid_sensor_is600_mode: unmatched dpi: %d\n", dpi); return SANE_FALSE; break; } } return SANE_FALSE; } static SANE_Status usb_mid_sensor_prepare_rgb (ma1017 * chip, SANE_Word dpi) { if (chip->sensor == ST_CANON300) return usb_mid_c300_prepare_rgb (chip, dpi); else if (chip->sensor == ST_CANON600) return usb_mid_c600_prepare_rgb (chip, dpi); else if (chip->sensor == ST_NEC600) return usb_mid_n600_prepare_rgb (chip, dpi); else return usb_mid_c300600_prepare_rgb (chip, dpi); return SANE_STATUS_INVAL; } static SANE_Status usb_mid_sensor_prepare_mono (ma1017 * chip, SANE_Word dpi) { if (chip->sensor == ST_CANON300) return usb_mid_c300_prepare_mono (chip, dpi); else if (chip->sensor == ST_CANON600) return usb_mid_c600_prepare_mono (chip, dpi); else if (chip->sensor == ST_NEC600) return usb_mid_n600_prepare_mono (chip, dpi); else return usb_mid_c300600_prepare_mono (chip, dpi); return SANE_STATUS_INVAL; } static SANE_Status usb_mid_sensor_get_dpi (ma1017 * chip, SANE_Word wanted_dpi, SANE_Word * dpi) { SANE_Word *dpi_list; SANE_Word i; if (!dpi) return SANE_STATUS_INVAL; DBG (5, "usb_mid_sensor_get_dpi: chip->sensor=%d\n", chip->sensor); if (chip->sensor == ST_CANON300) dpi_list = usb_mid_c300_optical_x_dpi; else if (chip->sensor == ST_CANON300600) dpi_list = usb_mid_c300600_optical_x_dpi; else if (chip->sensor == ST_CANON600) dpi_list = usb_mid_c600_optical_x_dpi; else if (chip->sensor == ST_NEC600) dpi_list = usb_mid_n600_optical_x_dpi; else return SANE_STATUS_INVAL; for (i = 0; dpi_list[i] != 0; i++) { if (wanted_dpi > dpi_list[i]) break; } if (i) i--; *dpi = dpi_list[i]; DBG (5, "usb_mid_sensor_get_dpi: wanted %d dpi, got %d dpi\n", wanted_dpi, *dpi); return SANE_STATUS_GOOD; } /* ---------------1200 dpi motor function declarations --------------------- */ static SANE_Word usb_mid_motor1200_optical_dpi[] = { 1200, 600, 400, 300, 200, 150, 100, 50, 0 }; SANE_Status usb_mid_motor1200_prepare_rgb_1200_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_rgb_1200_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 4, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 4)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_FALSE)); DBG (6, "usb_mid_motor1200_prepare_rgb_1200_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_rgb_600_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_rgb_600_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 4, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 4)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_FALSE)); DBG (6, "usb_mid_motor1200_prepare_rgb_600_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_rgb_400_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_rgb_400_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 3)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor1200_prepare_rgb_400_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_rgb_300_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_rgb_300_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 4, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 4)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor1200_prepare_rgb_300_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_rgb_200_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_rgb_200_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 3)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor1200_prepare_rgb_200_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_rgb_150_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_rgb_150_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 4, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 4)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor1200_prepare_rgb_150_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_rgb_100_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_rgb_100_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 3)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor1200_prepare_rgb_100_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_rgb_50_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_rgb_50_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 2, CH_BLUE, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 3, CH_BLUE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 4, CH_RED, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 5, CH_RED, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 6, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 6)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor1200_prepare_rgb_50_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_mono_1200_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_mono_1200_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 2)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor1200_prepare_mono_1200_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_mono_600_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_mono_600_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 4, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 5, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 6, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 7, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 8, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 9, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 10, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 11, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 12, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 13, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 14, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 15, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 16, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 17, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 18, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 19, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 20, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 21, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 22, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 23, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 24, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 25, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 26, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 26)); RIE (usb_low_set_cmt_second_position (chip, 24)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor1200_prepare_mono_600_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_mono_400_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_mono_400_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 3)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor1200_prepare_mono_400_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_mono_300_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_mono_300_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 4, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 5, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 6, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 7, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 8, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 9, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 10, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 11, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 12, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 13, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 14, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 15, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 16, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 17, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 18, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 19, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 20, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 21, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 22, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 23, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 24, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 25, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 26, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 26)); RIE (usb_low_set_cmt_second_position (chip, 24)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor1200_prepare_mono_300_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_mono_200_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_mono_200_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 3)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor1200_prepare_mono_200_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_mono_150_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_mono_150_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 2)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor1200_prepare_mono_150_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_mono_100_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_mono_100_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 3)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor1200_prepare_mono_100_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_mono_50_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_mono_50_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 4, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 5, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 6, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 6)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor1200_prepare_mono_50_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_rgb_half_300_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_rgb_half_300_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, CH_BLUE, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 3, CH_BLUE, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 4, CH_RED, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 5, CH_RED, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 6, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 6)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor1200_prepare_rgb_half_300_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_rgb_bi_full_300_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_rgb_bi_full_300_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, CH_BLUE, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 3, CH_BLUE, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 4, CH_RED, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 5, CH_RED, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 6, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 6)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_FALSE)); DBG (6, "usb_mid_motor1200_prepare_rgb_bi_full_300_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_rgb_bi_full_x2300_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_rgb_bi_full_x2300_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, CH_BLUE, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 3, CH_BLUE, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 4, CH_RED, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 5, CH_RED, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 6, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 6)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor1200_prepare_rgb_bi_full_x2300_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_mono_half_300_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_mono_half_300_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 4, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 4)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor1200_prepare_mono_half_300_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_mono_bi_full_300_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_mono_bi_full_300_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 2)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor1200_prepare_mono_bi_full_300_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_mono_bi_full_x2300_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_mono_bi_full_x2300_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 2)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor1200_prepare_mono_bi_full_x2300_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_rgb (ma1017 * chip, SANE_Word dpi) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_rgb: start\n"); RIE (usb_low_move_motor_home (chip, SANE_FALSE, SANE_FALSE)); /* No Motor & Forward */ RIE (usb_low_set_motor_direction (chip, SANE_FALSE)); RIE (usb_low_enable_motor (chip, SANE_TRUE)); switch (dpi) { case 1200: return usb_mid_motor1200_prepare_rgb_1200_dpi (chip); break; case 600: return usb_mid_motor1200_prepare_rgb_600_dpi (chip); break; case 400: return usb_mid_motor1200_prepare_rgb_400_dpi (chip); break; case 300: return usb_mid_motor1200_prepare_rgb_300_dpi (chip); break; case 200: return usb_mid_motor1200_prepare_rgb_200_dpi (chip); break; case 150: return usb_mid_motor1200_prepare_rgb_150_dpi (chip); break; case 100: return usb_mid_motor1200_prepare_rgb_100_dpi (chip); break; case 50: return usb_mid_motor1200_prepare_rgb_50_dpi (chip); break; default: DBG (3, "usb_mid_motor1200_prepare_rgb: unmatched dpi: %d\n", dpi); return SANE_STATUS_INVAL; break; } return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_mono (ma1017 * chip, SANE_Word dpi) { SANE_Status status; DBG (3, "usb_mid_motor1200_prepare_mono: start\n"); RIE (usb_low_move_motor_home (chip, SANE_FALSE, SANE_FALSE)); /* No Motor & Forward */ RIE (usb_low_set_motor_direction (chip, SANE_FALSE)); RIE (usb_low_enable_motor (chip, SANE_TRUE)); switch (dpi) { case 1200: return usb_mid_motor1200_prepare_mono_1200_dpi (chip); break; case 600: return usb_mid_motor1200_prepare_mono_600_dpi (chip); break; case 400: return usb_mid_motor1200_prepare_mono_400_dpi (chip); break; case 300: return usb_mid_motor1200_prepare_mono_300_dpi (chip); break; case 200: return usb_mid_motor1200_prepare_mono_200_dpi (chip); break; case 150: return usb_mid_motor1200_prepare_mono_150_dpi (chip); break; case 100: return usb_mid_motor1200_prepare_mono_100_dpi (chip); break; case 50: return usb_mid_motor1200_prepare_mono_50_dpi (chip); break; default: DBG (3, "usb_mid_motor1200_prepare_mono_: unmatched dpi: %d\n", dpi); return SANE_STATUS_INVAL; break; } return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_calibrate_rgb (ma1017 * chip, SANE_Word dpi) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_calibrate_rgb: start\n"); RIE (usb_low_move_motor_home (chip, SANE_FALSE, SANE_FALSE)); /* No Motor & Forward */ RIE (usb_low_set_motor_direction (chip, SANE_FALSE)); RIE (usb_low_enable_motor (chip, SANE_TRUE)); switch (dpi) { case 1200: case 400: case 300: return usb_mid_motor1200_prepare_rgb_half_300_dpi (chip); break; case 600: case 200: case 150: return usb_mid_motor1200_prepare_rgb_bi_full_300_dpi (chip); break; case 100: case 50: return usb_mid_motor1200_prepare_rgb_bi_full_x2300_dpi (chip); break; default: DBG (3, "usb_mid_motor1200_prepare_calibrate_rgb: unmatched dpi: " "%d\n", dpi); return SANE_STATUS_INVAL; break; } return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_calibrate_mono (ma1017 * chip, SANE_Word dpi) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_calibrate_mono: start\n"); RIE (usb_low_move_motor_home (chip, SANE_FALSE, SANE_FALSE)); /* No Motor & Forward */ RIE (usb_low_set_motor_direction (chip, SANE_FALSE)); RIE (usb_low_enable_motor (chip, SANE_TRUE)); switch (dpi) { case 1200: case 600: case 400: return usb_mid_motor1200_prepare_mono_half_300_dpi (chip); break; case 300: case 200: return usb_mid_motor1200_prepare_mono_bi_full_300_dpi (chip); break; case 150: case 100: case 50: return usb_mid_motor1200_prepare_mono_bi_full_x2300_dpi (chip); break; default: DBG (3, "usb_mid_motor1200_prepare_calibrate_mono: unmatched dpi: %d\n", dpi); return SANE_STATUS_INVAL; break; } return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_step (ma1017 * chip, SANE_Word step_count) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_step: start\n"); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE)); /* Make it in 600dpi */ RIE (usb_low_set_io_3 (chip, SANE_TRUE)); /* (IO3) ? High power : Low power */ RIE (usb_low_move_motor_home (chip, SANE_FALSE, SANE_FALSE)); /* No Motor & Forward */ if (step_count == 1) { RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 1)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, step_count)); } else if (step_count % 2 == 1) { RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 3)); RIE (usb_low_set_cmt_second_position (chip, 1)); RIE (usb_low_set_cmt_loop_count (chip, (step_count - 1) / 2)); } else { RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 2)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, step_count / 2)); } RIE (usb_low_enable_motor (chip, SANE_TRUE)); DBG (6, "usb_mid_motor1200_prepare_step: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_home (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_home: start\n"); if (chip->sensor == ST_NEC600) RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE)); else RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE)); /* Make it in 600dpi */ RIE (usb_low_set_io_3 (chip, SANE_TRUE)); /* (IO3) ? High power : Low power */ RIE (usb_low_move_motor_home (chip, SANE_TRUE, SANE_TRUE)); DBG (6, "usb_mid_motor1200_prepare_home: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor1200_prepare_adjust (ma1017 * chip, Channel channel) { SANE_Status status; DBG (6, "usb_mid_motor1200_prepare_adjust: start\n"); RIE (usb_low_set_cmt_table (chip, 0, channel, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, channel, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, channel, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 2)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); DBG (6, "usb_mid_motor1200_prepare_adjust: exit\n"); return SANE_STATUS_GOOD; } SANE_Word usb_mid_motor1200_rgb_capability (SANE_Word dpi) { DBG (6, "usb_mid_motor1200_rgb_capability: start\n"); switch (dpi) { case 1200: case 400: return 3008; /* 2816 */ ; case 600: return 3008; case 200: return 5056; case 300: return 3008; case 150: return 5056; case 100: case 50: return 10048; default: DBG (3, "usb_mid_motor1200_rgb_capability: unmatched dpi: %d\n", dpi); return 0; } } SANE_Word usb_mid_motor1200_mono_capability (SANE_Word dpi) { DBG (5, "usb_mid_motor1200_mono_capability: start\n"); switch (dpi) { case 1200: case 400: return 3008; case 600: return 3008; case 200: return 5056; case 300: return 5056; case 150: case 100: case 50: return 10048; default: DBG (3, "usb_mid_motor1200_mono_capability: unmatched dpi: %d\n", dpi); return 0; } } /* ---------------600 dpi motor function declarations --------------------- */ static SANE_Word usb_mid_motor600_optical_dpi[] = { 600, 300, 200, 150, 100, 50, 0 }; SANE_Status usb_mid_motor600_prepare_rgb_600_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor600_prepare_rgb_600_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 4, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 4)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_FALSE)); DBG (6, "usb_mid_motor600_prepare_rgb_600_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor600_prepare_rgb_300_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor600_prepare_rgb_300_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 4, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 4)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_FALSE)); DBG (6, "usb_mid_motor600_prepare_rgb_300_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor600_prepare_rgb_200_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor600_prepare_rgb_200_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 4, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 5, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 5)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor600_prepare_rgb_200_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor600_prepare_rgb_150_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor600_prepare_rgb_150_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 3)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor600_prepare_rgb_150_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor600_prepare_rgb_100_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor600_prepare_rgb_100_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 4, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 5, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 5)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor600_prepare_rgb_100_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor600_prepare_rgb_50_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor600_prepare_rgb_50_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_BLUE, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, CH_RED, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 3)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor600_prepare_rgb_50_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor600_prepare_mono_600_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor600_prepare_mono_600_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 2)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor600_prepare_mono_600_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor600_prepare_mono_300_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor600_prepare_mono_300_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 2)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor600_prepare_mono_300_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor600_prepare_mono_200_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor600_prepare_mono_200_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 3)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor600_prepare_mono_200_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor600_prepare_mono_150_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor600_prepare_mono_150_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 2)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor600_prepare_mono_150_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor600_prepare_mono_100_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor600_prepare_mono_100_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 3)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor600_prepare_mono_100_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor600_prepare_mono_50_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor600_prepare_mono_50_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 3)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor600_prepare_mono_50_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor600_prepare_rgb_half_300_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor600_prepare_rgb_half_300_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, CH_BLUE, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 3, CH_BLUE, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 4, CH_RED, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 5, CH_RED, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 6, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 6)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor600_prepare_rgb_half_300_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor600_prepare_rgb_bi_full_300_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor600_prepare_rgb_bi_full_300_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, CH_BLUE, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 3, CH_BLUE, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 4, CH_RED, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 5, CH_RED, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 6, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 6)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_FALSE)); DBG (6, "usb_mid_motor600_prepare_rgb_bi_full_300_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor600_prepare_mono_half_300_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor600_prepare_mono_half_300_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 2)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor600_prepare_mono_half_300_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor600_prepare_mono_bi_full_300_dpi (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor600_prepare_mono_bi_full_300_dpi: start\n"); RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 2)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); RIE (usb_low_set_motor_movement (chip, SANE_TRUE, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_io_3 (chip, SANE_TRUE)); DBG (6, "usb_mid_motor600_prepare_mono_bi_full_300_dpi: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor600_prepare_rgb (ma1017 * chip, SANE_Word dpi) { DBG (6, "usb_mid_motor600_prepare_rgb: start\n"); switch (dpi) { case 600: return usb_mid_motor600_prepare_rgb_600_dpi (chip); break; case 300: return usb_mid_motor600_prepare_rgb_300_dpi (chip); break; case 200: return usb_mid_motor600_prepare_rgb_200_dpi (chip); break; case 150: return usb_mid_motor600_prepare_rgb_150_dpi (chip); break; case 100: return usb_mid_motor600_prepare_rgb_100_dpi (chip); break; case 50: return usb_mid_motor600_prepare_rgb_50_dpi (chip); break; default: DBG (3, "usb_mid_motor600_prepare_rgb: unmatched dpi: %d\n", dpi); return SANE_STATUS_INVAL; break; } return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor600_prepare_mono (ma1017 * chip, SANE_Word dpi) { DBG (6, "usb_mid_motor600_prepare_mono: start\n"); switch (dpi) { case 600: return usb_mid_motor600_prepare_mono_600_dpi (chip); break; case 300: return usb_mid_motor600_prepare_mono_300_dpi (chip); break; case 200: return usb_mid_motor600_prepare_mono_200_dpi (chip); break; case 150: return usb_mid_motor600_prepare_mono_150_dpi (chip); break; case 100: return usb_mid_motor600_prepare_mono_100_dpi (chip); break; case 50: return usb_mid_motor600_prepare_mono_50_dpi (chip); break; default: DBG (3, "usb_mid_motor600_prepare_mono_: unmatched dpi: %d\n", dpi); return SANE_STATUS_INVAL; break; } return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor600_prepare_calibrate_rgb (ma1017 * chip, SANE_Word dpi) { SANE_Status status; DBG (6, "usb_mid_motor600_prepare_calibrate_rgb: start\n"); RIE (usb_low_move_motor_home (chip, SANE_FALSE, SANE_FALSE)); /* No Motor & Forward */ RIE (usb_low_set_motor_direction (chip, SANE_FALSE)); RIE (usb_low_enable_motor (chip, SANE_TRUE)); switch (dpi) { case 600: case 200: return usb_mid_motor600_prepare_rgb_half_300_dpi (chip); break; case 300: case 150: case 100: case 50: return usb_mid_motor600_prepare_rgb_bi_full_300_dpi (chip); break; default: DBG (3, "usb_mid_motor600_prepare_calibrate_rgb: unmatched dpi: %d\n", dpi); return SANE_STATUS_INVAL; break; } return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor600_prepare_calibrate_mono (ma1017 * chip, SANE_Word dpi) { SANE_Status status; DBG (6, "usb_mid_motor600_prepare_calibrate_mono: start\n"); RIE (usb_low_move_motor_home (chip, SANE_FALSE, SANE_FALSE)); /* No Motor & Forward */ RIE (usb_low_set_motor_direction (chip, SANE_FALSE)); RIE (usb_low_enable_motor (chip, SANE_TRUE)); switch (dpi) { case 600: case 200: return usb_mid_motor600_prepare_mono_half_300_dpi (chip); break; case 300: case 150: case 100: case 50: return usb_mid_motor600_prepare_mono_bi_full_300_dpi (chip); break; default: DBG (3, "usb_mid_motor600_prepare_calibrate_mono: unmatched dpi: %d\n", dpi); return SANE_STATUS_INVAL; break; } return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor600_prepare_step (ma1017 * chip, SANE_Word step_count) { SANE_Status status; DBG (6, "usb_mid_motor600_prepare_step: start\n"); RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_FALSE)); /* Make it in 300dpi */ RIE (usb_low_set_io_3 (chip, SANE_TRUE)); /* (IO3) ? High power : Low power */ RIE (usb_low_move_motor_home (chip, SANE_FALSE, SANE_FALSE)); /* No Motor & Forward */ if (step_count == 1) { RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 1)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, step_count)); } else if (step_count % 2 == 1) { RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 3, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 3)); RIE (usb_low_set_cmt_second_position (chip, 1)); RIE (usb_low_set_cmt_loop_count (chip, (step_count - 1) / 2)); } else { RIE (usb_low_set_cmt_table (chip, 0, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 1, CH_GREEN, SANE_TRUE, SANE_FALSE)); RIE (usb_low_set_cmt_table (chip, 2, CH_GREEN, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 2)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, step_count / 2)); } RIE (usb_low_enable_motor (chip, SANE_TRUE)); DBG (6, "usb_mid_motor600_prepare_step: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor600_prepare_home (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_motor600_prepare_home: start\n"); RIE (usb_low_set_motor_movement (chip, SANE_FALSE, SANE_TRUE, SANE_TRUE)); /* Make it in 600dpi */ RIE (usb_low_set_io_3 (chip, SANE_TRUE)); /* (IO3) ? High power : Low power */ RIE (usb_low_move_motor_home (chip, SANE_TRUE, SANE_TRUE)); DBG (6, "usb_mid_motor600_prepare_home: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_motor600_prepare_adjust (ma1017 * chip, Channel channel) { SANE_Status status; DBG (6, "usb_mid_motor600_prepare_adjust: start\n"); RIE (usb_low_set_cmt_table (chip, 0, channel, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 1, channel, SANE_FALSE, SANE_TRUE)); RIE (usb_low_set_cmt_table (chip, 2, channel, SANE_FALSE, SANE_FALSE)); RIE (usb_low_set_cmt_table_length (chip, 2)); RIE (usb_low_set_cmt_second_position (chip, 0)); RIE (usb_low_set_cmt_loop_count (chip, 0xefff)); DBG (6, "usb_mid_motor600_prepare_adjust: exit\n"); return SANE_STATUS_GOOD; } SANE_Word usb_mid_motor600_rgb_capability (SANE_Word dpi) { DBG (6, "usb_mid_motor600_rgb_capability: start\n"); switch (dpi) { case 600: case 300: case 200: return 2600; case 100: return 4500; case 150: case 50: return 9000; default: DBG (3, "usb_mid_motor600_rgb_capability: unmatched dpi: %d\n", dpi); return 0; } } SANE_Word usb_mid_motor600_mono_capability (SANE_Word dpi) { DBG (5, "usb_mid_motor600_mono_capability: start\n"); switch (dpi) { case 600: case 200: return 2600; case 300: case 100: return 4500; case 150: case 50: return 9000; default: DBG (3, "usb_mid_motor600_mono_capability: unmatched dpi: %d\n", dpi); return 0; } } /* ------------------ motor function declarations ------------------------ */ static SANE_Status usb_mid_motor_prepare_home (ma1017 * chip) { if (chip->motor == MT_600) return usb_mid_motor600_prepare_home (chip); else return usb_mid_motor1200_prepare_home (chip); } static SANE_Status usb_mid_motor_prepare_rgb (ma1017 * chip, SANE_Word dpi) { if (chip->motor == MT_600) return usb_mid_motor600_prepare_rgb (chip, dpi); else return usb_mid_motor1200_prepare_rgb (chip, dpi); } static SANE_Status usb_mid_motor_prepare_mono (ma1017 * chip, SANE_Word dpi) { if (chip->motor == MT_600) return usb_mid_motor600_prepare_mono (chip, dpi); else return usb_mid_motor1200_prepare_mono (chip, dpi); } static SANE_Status usb_mid_motor_prepare_adjust (ma1017 * chip, Channel channel) { if (chip->motor == MT_600) return usb_mid_motor600_prepare_adjust (chip, channel); else return usb_mid_motor1200_prepare_adjust (chip, channel); } static SANE_Status usb_mid_motor_prepare_calibrate_rgb (ma1017 * chip, SANE_Word dpi) { if (chip->motor == MT_600) return usb_mid_motor600_prepare_calibrate_rgb (chip, dpi); else return usb_mid_motor1200_prepare_calibrate_rgb (chip, dpi); } static SANE_Status usb_mid_motor_prepare_calibrate_mono (ma1017 * chip, SANE_Word dpi) { if (chip->motor == MT_600) return usb_mid_motor600_prepare_calibrate_mono (chip, dpi); else return usb_mid_motor1200_prepare_calibrate_mono (chip, dpi); } static SANE_Status usb_mid_motor_prepare_step (ma1017 * chip, SANE_Word step_count) { if (chip->motor == MT_600) return usb_mid_motor600_prepare_step (chip, step_count); else return usb_mid_motor1200_prepare_step (chip, step_count); } static SANE_Word usb_mid_motor_rgb_capability (ma1017 * chip, SANE_Word dpi) { if (chip->motor == MT_600) return usb_mid_motor600_rgb_capability (dpi); else return usb_mid_motor1200_rgb_capability (dpi); } static SANE_Word usb_mid_motor_mono_capability (ma1017 * chip, SANE_Word dpi) { if (chip->motor == MT_600) return usb_mid_motor600_mono_capability (dpi); else return usb_mid_motor1200_mono_capability (dpi); } static SANE_Status usb_mid_motor_get_dpi (ma1017 * chip, SANE_Word wanted_dpi, SANE_Word * dpi) { SANE_Word *dpi_list; SANE_Word i; if (!dpi) return SANE_STATUS_INVAL; if (chip->motor == MT_600) dpi_list = usb_mid_motor600_optical_dpi; else if (chip->motor == MT_1200) dpi_list = usb_mid_motor1200_optical_dpi; else return SANE_STATUS_INVAL; for (i = 0; dpi_list[i] != 0; i++) { if (wanted_dpi > dpi_list[i]) break; } if (i) i--; *dpi = dpi_list[i]; DBG (5, "usb_mid_motor_get_dpi: wanted %d dpi, got %d dpi\n", wanted_dpi, *dpi); return SANE_STATUS_GOOD; } /* ----------------------------- frontend ------------------------------- */ SANE_Status usb_mid_front_set_front_end_mode (ma1017 * chip, SANE_Byte mode) { SANE_Status status; DBG (6, "usb_mid_front_set_front_end_mode: start\n"); RIE (usb_low_set_serial_format (chip, mode)); DBG (6, "usb_mid_front_set_front_end_mode: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_front_enable (ma1017 * chip, SANE_Bool is_enable) { SANE_Status status; DBG (6, "usb_mid_front_enable: start\n"); RIE (usb_low_turn_frontend_mode (chip, is_enable)); DBG (6, "usb_mid_front_enable: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_front_set_top_reference (ma1017 * chip, SANE_Byte top) { SANE_Status status; DBG (6, "usb_mid_front_set_top_reference: start\n"); RIE (usb_mid_front_enable (chip, SANE_TRUE)); RIE (usb_low_set_serial_byte1 (chip, 0x00)); RIE (usb_low_set_serial_byte2 (chip, top)); RIE (usb_mid_front_enable (chip, SANE_FALSE)); DBG (6, "usb_mid_front_set_top_reference: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_front_set_red_offset (ma1017 * chip, SANE_Byte offset) { SANE_Status status; DBG (6, "usb_mid_front_set_red_offset: start\n"); RIE (usb_mid_front_enable (chip, SANE_TRUE)); RIE (usb_low_set_serial_byte1 (chip, 0x10)); RIE (usb_low_set_serial_byte2 (chip, offset)); RIE (usb_mid_front_enable (chip, SANE_FALSE)); DBG (6, "usb_mid_front_set_red_offset: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_front_set_green_offset (ma1017 * chip, SANE_Byte offset) { SANE_Status status; DBG (6, "usb_mid_front_set_green_offset: start\n"); RIE (usb_mid_front_enable (chip, SANE_TRUE)); RIE (usb_low_set_serial_byte1 (chip, 0x50)); RIE (usb_low_set_serial_byte2 (chip, offset)); RIE (usb_mid_front_enable (chip, SANE_FALSE)); DBG (6, "usb_mid_front_set_green_offset: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_front_set_blue_offset (ma1017 * chip, SANE_Byte offset) { SANE_Status status; DBG (6, "usb_mid_front_set_blue_offset: start\n"); RIE (usb_mid_front_enable (chip, SANE_TRUE)); RIE (usb_low_set_serial_byte1 (chip, 0x30)); RIE (usb_low_set_serial_byte2 (chip, offset)); RIE (usb_mid_front_enable (chip, SANE_FALSE)); DBG (6, "usb_mid_front_set_blue_offset: exit\n"); return SANE_STATUS_GOOD; } #if 0 /* CCD */ SANE_Word usb_mid_frontend_max_offset_index (ma1017 * chip) { DBG (6, "usb_mid_front_max_offset_index: start (chip = %p)\n", chip); DBG (6, "usb_mid_front_max_offset_index: exit\n"); return (OFFSET_TABLE_SIZE - 1); } #endif SANE_Status usb_mid_front_set_red_pga (ma1017 * chip, SANE_Byte pga) { SANE_Status status; DBG (6, "usb_mid_front_set_red_pga: start\n"); RIE (usb_mid_front_enable (chip, SANE_TRUE)); RIE (usb_low_set_serial_byte1 (chip, 0x40)); RIE (usb_low_set_serial_byte2 (chip, pga)); RIE (usb_mid_front_enable (chip, SANE_FALSE)); DBG (6, "usb_mid_front_set_red_pga: start\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_front_set_green_pga (ma1017 * chip, SANE_Byte pga) { SANE_Status status; DBG (6, "usb_mid_front_set_green_pga: start\n"); RIE (usb_mid_front_enable (chip, SANE_TRUE)); RIE (usb_low_set_serial_byte1 (chip, 0x20)); RIE (usb_low_set_serial_byte2 (chip, pga)); RIE (usb_mid_front_enable (chip, SANE_FALSE)); DBG (6, "usb_mid_front_set_green_pga: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_front_set_blue_pga (ma1017 * chip, SANE_Byte pga) { SANE_Status status; DBG (6, "usb_mid_front_set_blue_pga: start\n"); RIE (usb_mid_front_enable (chip, SANE_TRUE)); RIE (usb_low_set_serial_byte1 (chip, 0x60)); RIE (usb_low_set_serial_byte2 (chip, pga)); RIE (usb_mid_front_enable (chip, SANE_FALSE)); DBG (6, "usb_mid_front_set_blue_pga: exit\n"); return SANE_STATUS_GOOD; } SANE_Status usb_mid_front_set_rgb_signal (ma1017 * chip) { SANE_Status status; DBG (6, "usb_mid_front_set_rgb_signal: start\n"); RIE (usb_low_set_red_ref (chip, 0xEF)); RIE (usb_low_set_green_ref (chip, 0xF7)); RIE (usb_low_set_blue_ref (chip, 0xFF)); DBG (6, "usb_mid_front_set_rgb_signal: exit\n"); return SANE_STATUS_GOOD; } backends-1.3.0/backend/mustek_usb_mid.h000066400000000000000000000324001456256263500200540ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2000 Mustek. Originally maintained by Tom Wang Copyright (C) 2001, 2002 by Henning Meier-Geinitz. 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, see . As a special exception, the authors of SANE give permission for additional uses of the libraries contained in this release of SANE. The exception is that, if you link a SANE library with other files to produce an executable, this does not by itself cause the resulting executable to be covered by the GNU General Public License. Your use of that executable is in no way restricted on account of linking the SANE library code into it. This exception does not, however, invalidate any other reasons why the executable file might be covered by the GNU General Public License. If you submit changes to SANE to the maintainers to be included in a subsequent release, you agree by submitting the changes that those changes may be distributed with this exception intact. If you write modifications of your own for SANE, it is your choice whether to permit this exception to apply to your modifications. If you do not wish that, delete this exception notice. This file implements a SANE backend for Mustek 1200UB and similar USB flatbed scanners. */ #ifndef mustek_usb_mid_h #define mustek_usb_mid_h #include "mustek_usb_low.h" #include "../include/sane/sane.h" /* ---------------------------------- macros ------------------------------ */ /* ---------------- sensor NEC 600 CCD function declarations -------------- */ static SANE_Status usb_mid_n600_prepare_rgb (ma1017 * chip, SANE_Word dpi); static SANE_Status usb_mid_n600_prepare_mono (ma1017 * chip, SANE_Word dpi); static SANE_Status usb_mid_n600_prepare_rgb_600_dpi (ma1017 * chip); static SANE_Status usb_mid_n600_prepare_rgb_400_dpi (ma1017 * chip); static SANE_Status usb_mid_n600_prepare_rgb_300_dpi (ma1017 * chip); static SANE_Status usb_mid_n600_prepare_rgb_200_dpi (ma1017 * chip); static SANE_Status usb_mid_n600_prepare_rgb_100_dpi (ma1017 * chip); static SANE_Status usb_mid_n600_prepare_rgb_50_dpi (ma1017 * chip); static SANE_Status usb_mid_n600_prepare_mono_600_dpi (ma1017 * chip); static SANE_Status usb_mid_n600_prepare_mono_400_dpi (ma1017 * chip); static SANE_Status usb_mid_n600_prepare_mono_300_dpi (ma1017 * chip); static SANE_Status usb_mid_n600_prepare_mono_200_dpi (ma1017 * chip); static SANE_Status usb_mid_n600_prepare_mono_100_dpi (ma1017 * chip); static SANE_Status usb_mid_n600_prepare_mono_50_dpi (ma1017 * chip); /* ----------------- sensor 600 CIS function declarations ----------------- */ static SANE_Status usb_mid_c600_prepare_rgb (ma1017 * chip, SANE_Word dpi); static SANE_Status usb_mid_c600_prepare_mono (ma1017 * chip, SANE_Word dpi); static SANE_Status usb_mid_c600_prepare_rgb_600_dpi (ma1017 * chip); static SANE_Status usb_mid_c600_prepare_rgb_400_dpi (ma1017 * chip); static SANE_Status usb_mid_c600_prepare_rgb_300_dpi (ma1017 * chip); static SANE_Status usb_mid_c600_prepare_rgb_200_dpi (ma1017 * chip); static SANE_Status usb_mid_c600_prepare_rgb_150_dpi (ma1017 * chip); static SANE_Status usb_mid_c600_prepare_rgb_100_dpi (ma1017 * chip); static SANE_Status usb_mid_c600_prepare_rgb_50_dpi (ma1017 * chip); static SANE_Status usb_mid_c600_prepare_mono_600_dpi (ma1017 * chip); static SANE_Status usb_mid_c600_prepare_mono_400_dpi (ma1017 * chip); static SANE_Status usb_mid_c600_prepare_mono_300_dpi (ma1017 * chip); static SANE_Status usb_mid_c600_prepare_mono_200_dpi (ma1017 * chip); static SANE_Status usb_mid_c600_prepare_mono_150_dpi (ma1017 * chip); static SANE_Status usb_mid_c600_prepare_mono_100_dpi (ma1017 * chip); static SANE_Status usb_mid_c600_prepare_mono_50_dpi (ma1017 * chip); /* -------------- sensor 300/600 CIS function declarations ---------------- */ static SANE_Status usb_mid_c300600_prepare_rgb (ma1017 * chip, SANE_Word dpi); static SANE_Status usb_mid_c300600_prepare_mono (ma1017 * chip, SANE_Word dpi); static SANE_Status usb_mid_c300600_prepare_rgb_600_dpi (ma1017 * chip); static SANE_Status usb_mid_c300600_prepare_rgb_400_dpi (ma1017 * chip); static SANE_Status usb_mid_c300600_prepare_rgb_300_dpi (ma1017 * chip); static SANE_Status usb_mid_c300600_prepare_rgb_200_dpi (ma1017 * chip); static SANE_Status usb_mid_c300600_prepare_rgb_150_dpi (ma1017 * chip); static SANE_Status usb_mid_c300600_prepare_rgb_100_dpi (ma1017 * chip); static SANE_Status usb_mid_c300600_prepare_rgb_50_dpi (ma1017 * chip); static SANE_Status usb_mid_c300600_prepare_mono_600_dpi (ma1017 * chip); static SANE_Status usb_mid_c300600_prepare_mono_400_dpi (ma1017 * chip); static SANE_Status usb_mid_c300600_prepare_mono_300_dpi (ma1017 * chip); static SANE_Status usb_mid_c300600_prepare_mono_200_dpi (ma1017 * chip); static SANE_Status usb_mid_c300600_prepare_mono_150_dpi (ma1017 * chip); static SANE_Status usb_mid_c300600_prepare_mono_100_dpi (ma1017 * chip); static SANE_Status usb_mid_c300600_prepare_mono_50_dpi (ma1017 * chip); /* ----------------- sensor 300 CIS function declarations ----------------- */ static SANE_Status usb_mid_c300_prepare_rgb (ma1017 * chip, SANE_Word dpi); static SANE_Status usb_mid_c300_prepare_mono (ma1017 * chip, SANE_Word dpi); static SANE_Status usb_mid_c300_prepare_rgb_300_dpi (ma1017 * chip); static SANE_Status usb_mid_c300_prepare_rgb_200_dpi (ma1017 * chip); static SANE_Status usb_mid_c300_prepare_rgb_150_dpi (ma1017 * chip); static SANE_Status usb_mid_c300_prepare_rgb_100_dpi (ma1017 * chip); static SANE_Status usb_mid_c300_prepare_rgb_50_dpi (ma1017 * chip); static SANE_Status usb_mid_c300_prepare_mono_300_dpi (ma1017 * chip); static SANE_Status usb_mid_c300_prepare_mono_200_dpi (ma1017 * chip); static SANE_Status usb_mid_c300_prepare_mono_150_dpi (ma1017 * chip); static SANE_Status usb_mid_c300_prepare_mono_100_dpi (ma1017 * chip); static SANE_Status usb_mid_c300_prepare_mono_50_dpi (ma1017 * chip); /* --------------------- sensor function declarations -------------------- */ static SANE_Bool usb_mid_sensor_is600_mode (ma1017 * chip, SANE_Word dpi); static SANE_Status usb_mid_sensor_prepare_rgb (ma1017 * chip, SANE_Word dpi); static SANE_Status usb_mid_sensor_prepare_mono (ma1017 * chip, SANE_Word dpi); static SANE_Status usb_mid_sensor_get_dpi (ma1017 * chip, SANE_Word wanted_dpi, SANE_Word * dpi); /* ------------------- motor 1200 function declarations ------------------ */ static SANE_Status usb_mid_motor1200_prepare_rgb_1200_dpi (ma1017 * chip); static SANE_Status usb_mid_motor1200_prepare_rgb_400_dpi (ma1017 * chip); static SANE_Status usb_mid_motor1200_prepare_rgb_600_dpi (ma1017 * chip); static SANE_Status usb_mid_motor1200_prepare_rgb_200_dpi (ma1017 * chip); static SANE_Status usb_mid_motor1200_prepare_rgb_300_dpi (ma1017 * chip); static SANE_Status usb_mid_motor1200_prepare_rgb_150_dpi (ma1017 * chip); static SANE_Status usb_mid_motor1200_prepare_rgb_100_dpi (ma1017 * chip); static SANE_Status usb_mid_motor1200_prepare_rgb_50_dpi (ma1017 * chip); static SANE_Status usb_mid_motor1200_prepare_mono_1200_dpi (ma1017 * chip); static SANE_Status usb_mid_motor1200_prepare_mono_400_dpi (ma1017 * chip); static SANE_Status usb_mid_motor1200_prepare_mono_600_dpi (ma1017 * chip); static SANE_Status usb_mid_motor1200_prepare_mono_200_dpi (ma1017 * chip); static SANE_Status usb_mid_motor1200_prepare_mono_300_dpi (ma1017 * chip); static SANE_Status usb_mid_motor1200_prepare_mono_150_dpi (ma1017 * chip); static SANE_Status usb_mid_motor1200_prepare_mono_100_dpi (ma1017 * chip); static SANE_Status usb_mid_motor1200_prepare_mono_50_dpi (ma1017 * chip); static SANE_Status usb_mid_motor1200_prepare_rgb_half_300_dpi (ma1017 * chip); static SANE_Status usb_mid_motor1200_prepare_rgb_bi_full_300_dpi (ma1017 * chip); static SANE_Status usb_mid_motor1200_prepare_rgb_bi_full_x2300_dpi (ma1017 * chip); static SANE_Status usb_mid_motor1200_prepare_mono_half_300_dpi (ma1017 * chip); static SANE_Status usb_mid_motor1200_prepare_mono_bi_full_300_dpi (ma1017 * chip); static SANE_Status usb_mid_motor1200_prepare_mono_bi_full_x2300_dpi (ma1017 * chip); static SANE_Status usb_mid_motor1200_prepare_rgb (ma1017 * chip, SANE_Word dpi); static SANE_Status usb_mid_motor1200_prepare_mono (ma1017 * chip, SANE_Word dpi); static SANE_Status usb_mid_motor1200_prepare_calibrate_rgb (ma1017 * chip, SANE_Word dpi); static SANE_Status usb_mid_motor1200_prepare_calibrate_mono (ma1017 * chip, SANE_Word dpi); static SANE_Status usb_mid_motor1200_prepare_step (ma1017 * chip, SANE_Word step_count); static SANE_Status usb_mid_motor1200_prepare_home (ma1017 * chip); static SANE_Status usb_mid_motor1200_prepare_adjust (ma1017 * chip, Channel channel); static SANE_Word usb_mid_motor1200_rgb_capability (SANE_Word dpi); static SANE_Word usb_mid_motor1200_mono_capability (SANE_Word dpi); /* ---------------600 dpi motor function declarations --------------------- */ static SANE_Status usb_mid_motor600_prepare_rgb_600_dpi (ma1017 * chip); static SANE_Status usb_mid_motor600_prepare_rgb_200_dpi (ma1017 * chip); static SANE_Status usb_mid_motor600_prepare_rgb_300_dpi (ma1017 * chip); static SANE_Status usb_mid_motor600_prepare_rgb_150_dpi (ma1017 * chip); static SANE_Status usb_mid_motor600_prepare_rgb_100_dpi (ma1017 * chip); static SANE_Status usb_mid_motor600_prepare_rgb_50_dpi (ma1017 * chip); static SANE_Status usb_mid_motor600_prepare_mono_600_dpi (ma1017 * chip); static SANE_Status usb_mid_motor600_prepare_mono_200_dpi (ma1017 * chip); static SANE_Status usb_mid_motor600_prepare_mono_300_dpi (ma1017 * chip); static SANE_Status usb_mid_motor600_prepare_mono_150_dpi (ma1017 * chip); static SANE_Status usb_mid_motor600_prepare_mono_100_dpi (ma1017 * chip); static SANE_Status usb_mid_motor600_prepare_mono_50_dpi (ma1017 * chip); static SANE_Status usb_mid_motor600_prepare_rgb_half_300_dpi (ma1017 * chip); static SANE_Status usb_mid_motor600_prepare_rgb_bi_full_300_dpi (ma1017 * chip); static SANE_Status usb_mid_motor600_prepare_mono_half_300_dpi (ma1017 * chip); static SANE_Status usb_mid_motor600_prepare_mono_bi_full_300_dpi (ma1017 * chip); static SANE_Status usb_mid_motor600_prepare_rgb (ma1017 * chip, SANE_Word dpi); static SANE_Status usb_mid_motor600_prepare_mono (ma1017 * chip, SANE_Word dpi); static SANE_Status usb_mid_motor600_prepare_calibrate_rgb (ma1017 * chip, SANE_Word dpi); static SANE_Status usb_mid_motor600_prepare_calibrate_mono (ma1017 * chip, SANE_Word dpi); static SANE_Status usb_mid_motor600_prepare_step (ma1017 * chip, SANE_Word step_count); static SANE_Status usb_mid_motor600_prepare_home (ma1017 * chip); static SANE_Status usb_mid_motor600_prepare_adjust (ma1017 * chip, Channel channel); static SANE_Word usb_mid_motor600_rgb_capability (SANE_Word dpi); static SANE_Word usb_mid_motor600_mono_capability (SANE_Word dpi); /* ------------------ motor function declarations ------------------------ */ static SANE_Status usb_mid_motor_prepare_home (ma1017 * chip); static SANE_Status usb_mid_motor_prepare_rgb (ma1017 * chip, SANE_Word dpi); static SANE_Status usb_mid_motor_prepare_mono (ma1017 * chip, SANE_Word dpi); static SANE_Status usb_mid_motor_prepare_adjust (ma1017 * chip, Channel channel); static SANE_Status usb_mid_motor_prepare_calibrate_rgb (ma1017 * chip, SANE_Word dpi); static SANE_Status usb_mid_motor_prepare_calibrate_mono (ma1017 * chip, SANE_Word dpi); static SANE_Status usb_mid_motor_prepare_step (ma1017 * chip, SANE_Word step_count); static SANE_Word usb_mid_motor_rgb_capability (ma1017 * chip, SANE_Word dpi); static SANE_Word usb_mid_motor_mono_capability (ma1017 * chip, SANE_Word dpi); static SANE_Status usb_mid_motor_get_dpi (ma1017 * chip, SANE_Word wanted_dpi, SANE_Word * dpi); /* --------------------- frontend function declarations ------------------- */ static SANE_Status usb_mid_front_set_front_end_mode (ma1017 * chip, SANE_Byte mode); static SANE_Status usb_mid_front_enable (ma1017 * chip, SANE_Bool is_enable); static SANE_Status usb_mid_front_set_top_reference (ma1017 * chip, SANE_Byte top); static SANE_Status usb_mid_front_set_red_offset (ma1017 * chip, SANE_Byte offset); static SANE_Status usb_mid_front_set_green_offset (ma1017 * chip, SANE_Byte offset); static SANE_Status usb_mid_front_set_blue_offset (ma1017 * chip, SANE_Byte offset); static SANE_Status usb_mid_front_set_red_pga (ma1017 * chip, SANE_Byte pga); static SANE_Status usb_mid_front_set_green_pga (ma1017 * chip, SANE_Byte pga); static SANE_Status usb_mid_front_set_blue_pga (ma1017 * chip, SANE_Byte pga); static SANE_Status usb_mid_front_set_rgb_signal (ma1017 * chip); #if 0 /* CCD */ static SANE_Word usb_mid_frontend_max_offset_index (ma1017 * chip); #define OFFSET_TABLE_SIZE 256 #endif #endif /* mustek_usb_mid_h */ backends-1.3.0/backend/nec.c000066400000000000000000003220541456256263500156110ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2000-2001 Kazuya Fukuda, based on sharp.c, which is based on canon.c. 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, see . 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 NEC flatbed scanners. */ /* Version 0.12 - Remove references to sharp backend (grep for "JX"). - Check for HAVE_SYS_SHM_H before including sys/shm.h and disable shared memory support if necessary. - free devlist allocated in sane_get_devices() in sane_exit() - resolution setting bug fixed(PC-IN500/4C 10dpi step) - remove resolution list Version 0.11 - get_data_buffer_status is not called in sane_get_parameter and sane_read_direct, sane_read_shuffled. - change some #include <> to "" Version 0.10 - First release! - supported scanner PC-IN500/4C available MultiReder 300U/300S series not available MultiReder 600U/600S series not available MultiReader PetiScan series not available */ #include "../include/sane/config.h" #include #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_scsi.h" /* QUEUEDEBUG should be undefined unless you want to play with the sanei_scsi.c under Linux and/or with the Linux's SG driver, or your suspect problems with command queueing */ #define QUEUEDEBUG /*#define DEBUG*/ #ifdef DEBUG #include #include #endif /* USE_FORK: fork a special reader process disable shared memory support. */ #if 0 #ifdef HAVE_SYS_SHM_H #define USE_FORK #endif #endif #ifdef USE_FORK #include #include #include #include #include #include #endif /* USE_FORK */ #ifndef USE_CUSTOM_GAMMA #define USE_CUSTOM_GAMMA #endif #ifndef USE_COLOR_THRESHOLD #define USE_COLOR_THRESHOLD #endif /* enable a short list of some standard resolutions. XSane provides its own resolution list; therefore its is generally not reasonable to enable this list, if you mainly using XSane. But it might be handy if you are working with xscanimage */ /* #define USE_RESOLUTION_LIST */ #define BACKEND_NAME nec #include "../include/sane/sanei_backend.h" #ifndef PATH_MAX #define PATH_MAX 1024 #endif #define DEFAULT_MUD_1200 1200 #define PIX_TO_MM(x, mud) ((x) * 25.4 / mud) #define MM_TO_PIX(x, mud) ((x) * mud / 25.4) #include "../include/sane/sanei_config.h" #define NEC_CONFIG_FILE "nec.conf" #include "nec.h" static int num_devices = 0; static NEC_Device *first_dev = NULL; static NEC_Scanner *first_handle = NULL; static const SANE_Device **devlist = 0; typedef enum { MODES_LINEART = 0, MODES_GRAY, MODES_COLOR, MODES_LINEART_COLOR } Modes; #define M_LINEART SANE_VALUE_SCAN_MODE_LINEART #define M_GRAY SANE_VALUE_SCAN_MODE_GRAY #define M_LINEART_COLOR "Lineart Color" #define M_COLOR SANE_VALUE_SCAN_MODE_COLOR static const SANE_String_Const mode_list[] = { #if 0 M_LINEART, M_GRAY, M_LINEART_COLOR, M_COLOR, #endif M_LINEART, M_GRAY, M_COLOR, 0 }; #define M_BILEVEL "none" #define M_BAYER "Dither Bayer" #define M_SPIRAL "Dither Spiral" #define M_DISPERSED "Dither Dispersed" #define M_ERRDIFFUSION "Error Diffusion" #define M_DITHER1 "Dither 1" #define M_DITHER2 "Dither 2" #define M_DITHER3 "Dither 3" #define M_DITHERUSER "User defined" static const SANE_String_Const halftone_list[] = { M_BILEVEL, M_DITHER1, M_DITHER2, M_DITHER3, 0 }; #define LIGHT_GREEN "green" #define LIGHT_RED "red" #define LIGHT_BLUE "blue" #define LIGHT_NONE "none" #define LIGHT_WHITE "white" static const SANE_String_Const light_color_list[] = { LIGHT_GREEN, LIGHT_RED, LIGHT_BLUE, LIGHT_NONE, 0 }; /* possible values for ADF/FSU selection */ static SANE_String use_adf = "Automatic Document Feeder"; static SANE_String use_fsu = "Transparency Adapter"; static SANE_String use_simple = "Flatbed"; #define HAVE_FSU 1 #define HAVE_ADF 2 /* The follow #defines are used in NEC_Scanner.adf_fsu_mode and as indexes for the arrays x_ranges, y_ranges in NEC_Device */ #define SCAN_SIMPLE 0 #define SCAN_WITH_FSU 1 #define SCAN_WITH_ADF 2 #define LOAD_PAPER 1 #define UNLOAD_PAPER 0 #define PAPER_MAX 10 #define W_LETTER "11\"x17\"" #define INVOICE "8.5\"x5.5\"" static const SANE_String_Const paper_list_pcinxxx[] = { "A3", "A4", "A5", "A6", "B4", "B5", W_LETTER, "Legal", "Letter", INVOICE, 0 }; static const SANE_String_Const paper_list_pcin500[] = { "A4", "A5", "A6", "B5", 0 }; #define CRT1 "CRT1" #define CRT2 "CRT2" #define PRINTER1 "PRINTER1" #define PRINTER2 "PRINTER2" #define NONE "NONE" /* #define CUSTOM "CUSTOM" */ static const SANE_String_Const gamma_list[] = { CRT1, CRT2, PRINTER1, PRINTER2, NONE, 0 }; #if 0 #define SPEED_NORMAL "Normal" #define SPEED_FAST "Fast" static const SANE_String_Const speed_list[] = { SPEED_NORMAL, SPEED_FAST, 0 }; #endif #ifdef USE_RESOLUTION_LIST #define RESOLUTION_MAX_PCINXXX 8 static const SANE_String_Const resolution_list_pcinxxx[] = { "50", "75", "100", "150", "200", "300", "400", "600", "Select", 0 }; #define RESOLUTION_MAX_PCIN500 8 static const SANE_String_Const resolution_list_pcin500[] = { "50", "75", "100", "150", "200", "300", "400", "480", "Select", 0 }; #endif #define EDGE_NONE "None" #define EDGE_MIDDLE "Middle" #define EDGE_STRONG "Strong" #define EDGE_BLUR "Blur" static const SANE_String_Const edge_emphasis_list[] = { EDGE_NONE, EDGE_MIDDLE, EDGE_STRONG, EDGE_BLUR, 0 }; #ifdef USE_CUSTOM_GAMMA static const SANE_Range u8_range = { 0, /* minimum */ 255, /* maximum */ 0 /* quantization */ }; #endif static SANE_Status sense_handler(int fd, u_char *sense_buffer, void *ss) { int sense_key; NEC_Sense_Data *sdat = (NEC_Sense_Data *) ss; (void) fd; /* silence compilation warnings */ #define add_sense_code sense_buffer[12] #define add_sense_qual sense_buffer[13] memcpy(sdat->sb, sense_buffer, 16); DBG(10, "sense code: %02x %02x %02x %02x %02x %02x %02x %02x " "%02x %02x %02x %02x %02x %02x %02x %02x\n", sense_buffer[0], sense_buffer[1], sense_buffer[2], sense_buffer[3], sense_buffer[4], sense_buffer[5], sense_buffer[6], sense_buffer[7], sense_buffer[8], sense_buffer[9], sense_buffer[10], sense_buffer[11], sense_buffer[12], sense_buffer[13], sense_buffer[14], sense_buffer[15]); sense_key = sense_buffer[1] & 0x0F; /* do we have additional information ? */ if (sense_buffer[7] >= 5) { if (sdat->model == PCIN500) { switch (sense_key) { case 0x02: /* not ready */ switch (add_sense_code) { case 0x80: switch (add_sense_qual & 0xf0) { case 0x10: DBG(1, "Scanner not ready: memory error\n"); return SANE_STATUS_IO_ERROR; case 0x20: DBG(1, "Scanner not ready: hardware error\n"); return SANE_STATUS_IO_ERROR; case 0x30: DBG(1, "Scanner not ready: optical error\n"); return SANE_STATUS_IO_ERROR; case 0x40: DBG(1, "Scanner not ready: optical error\n"); return SANE_STATUS_IO_ERROR; case 0x50: DBG(1, "Scanner not ready: marker error\n"); return SANE_STATUS_IO_ERROR; case 0x60: DBG(1, "Scanner not ready: mechanical error\n"); return SANE_STATUS_IO_ERROR; case 0x70: DBG(1, "Scanner not ready: hardware error\n"); return SANE_STATUS_IO_ERROR; case 0x80: DBG(1, "Scanner not ready: hardware error\n"); return SANE_STATUS_IO_ERROR; case 0x90: default: DBG(5, "Scanner not ready: undocumented reason\n"); return SANE_STATUS_IO_ERROR; } default: DBG(5, "Scanner not ready: unknown sense code\n"); return SANE_STATUS_IO_ERROR; } case 0x03: /* medium error */ DBG(5, "medium error: undocumented reason\n"); return SANE_STATUS_IO_ERROR; case 0x04: /* hardware error */ DBG(1, "general hardware error\n"); return SANE_STATUS_IO_ERROR; case 0x05: /* illegal request */ DBG(10, "error: illegal request\n"); return SANE_STATUS_IO_ERROR; case 0x06: /* unit attention */ DBG(5, "unit attention: exact reason not documented\n"); return SANE_STATUS_IO_ERROR; case 0x0B: /* data remains */ DBG(5, "error: aborted command\n"); return SANE_STATUS_IO_ERROR; default: DBG(5, "error: sense code not documented\n"); return SANE_STATUS_IO_ERROR; } } } return SANE_STATUS_IO_ERROR; } static SANE_Status test_unit_ready (int fd) { static u_char cmd[] = {TEST_UNIT_READY, 0, 0, 0, 0, 0}; SANE_Status status; DBG (11, "<< test_unit_ready "); status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0); DBG (11, ">>\n"); return (status); } #if 0 static SANE_Status request_sense (int fd, void *sense_buf, size_t *sense_size) { static u_char cmd[] = {REQUEST_SENSE, 0, 0, 0, SENSE_LEN, 0}; SANE_Status status; DBG (11, "<< request_sense "); status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), sense_buf, sense_size); DBG (11, ">>\n"); return (status); } #endif static SANE_Status inquiry (int fd, void *inq_buf, size_t *inq_size) { static u_char cmd[] = {INQUIRY, 0, 0, 0, INQUIRY_LEN, 0}; SANE_Status status; DBG (11, "<< inquiry "); status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), inq_buf, inq_size); DBG (11, ">>\n"); return (status); } static SANE_Status mode_select_mud (int fd, int mud) { static u_char cmd[6 + MODEPARAM_LEN] = {MODE_SELECT6, 0x10, 0, 0, MODEPARAM_LEN, 0}; mode_select_param *mp; SANE_Status status; DBG (11, "<< mode_select_mud "); mp = (mode_select_param *)(cmd + 6); memset (mp, 0, MODEPARAM_LEN); mp->mode_param_header1 = 11; mp->page_code = 3; mp->page_length = 6; mp->mud[0] = mud >> 8; mp->mud[1] = mud & 0xFF; status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0); DBG (11, ">>\n"); return (status); } #if 0 static SANE_Status mode_select_adf_fsu (int fd, int mode) { static u_char cmd[6 + MODE_SUBDEV_LEN] = {MODE_SELECT6, 0x10, 0, 0, MODE_SUBDEV_LEN, 0}; mode_select_subdevice *mp; SANE_Status status; DBG (11, "<< mode_select_adf_fsu "); mp = (mode_select_subdevice *)(cmd + 6); memset (mp, 0, MODE_SUBDEV_LEN); mp->page_code = 0x20; mp->page_length = 26; switch (mode) { case SCAN_SIMPLE: mp->a_mode = 0x40; mp->f_mode = 0x40; break; case SCAN_WITH_FSU: mp->a_mode = 0; mp->f_mode = 0x40; break; case SCAN_WITH_ADF: mp->a_mode = 0x40; mp->f_mode = 0; break; } status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0); DBG (11, ">>\n"); return (status); } #endif static SANE_Status wait_ready(int fd); static SANE_Status mode_sense (int fd, void *modeparam_buf, size_t * modeparam_size, int page) { static u_char cmd[6] = {MODE_SENSE6, 0, 0, 0, 0, 0}; SANE_Status status; DBG (11, "<< mode_sense "); cmd[0] = 0x1a; cmd[2] = page; cmd[4] = *modeparam_size; status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), modeparam_buf, modeparam_size); DBG (11, ">>\n"); return (status); } static SANE_Status scan (int fd) { static u_char cmd[] = {SCAN, 0, 0, 0, 0, 0}; SANE_Status status; DBG (11, "<< scan "); status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0); DBG (11, ">>\n"); return (status); } #if 0 static SANE_Status send_diagnostics (int fd) { static u_char cmd[] = {SEND_DIAGNOSTIC, 0x04, 0, 0, 0, 0}; SANE_Status status; DBG (11, "<< send_diagnostics "); status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0); DBG (11, ">>\n"); return (status); } #endif static SANE_Status set_window (int fd, window_param *wp, int len) { static u_char cmd[10 + WINDOW_LEN] = {SET_WINDOW, 0, 0, 0, 0, 0, 0, 0, 0, 0}; window_param *winp; SANE_Status status; DBG (11, "<< set_window "); cmd[8] = len; winp = (window_param *)(cmd + 10); memset (winp, 0, WINDOW_LEN); memcpy (winp, wp, len); status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), 0, 0); DBG (11, ">>\n"); return (status); } static SANE_Status get_window (int fd, void *buf, size_t * buf_size) { static u_char cmd[10] = {GET_WINDOW, 0, 0, 0, 0, 0, 0, 0, WINDOW_LEN, 0}; SANE_Status status; DBG (11, "<< get_window "); cmd[8] = *buf_size; status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, buf_size); DBG (11, ">>\n"); return (status); } #if 0 static SANE_Status get_data_buffer_status (int fd, void *buf, size_t *buf_size) { static u_char cmd[10] = {GET_DATA_BUFFER_STATUS, 0, 0, 0, 0, 0, 0, 0, 0, 0}; SANE_Status status; DBG (11, "<< get_data_buffer_status "); cmd[8] = *buf_size; status = sanei_scsi_cmd (fd, cmd, sizeof (cmd), buf, buf_size); DBG (11, ">>\n"); return (status); } #endif #ifdef USE_FORK /* the following four functions serve simply the purpose to avoid "over-optimised" code when reader_process and read_data wait for the buffer to become ready. The simple while-loops in these functions which check the buffer status may be optimised so that the machine code only operates with registers instead of using the variable values stored in memory. (This is only a workaround - it would be better to set a compiler pragma, which ensures that the program looks into the RAM in these while loops -- but unfortunately I could not find appropriate information about this at least for gcc, not to speak about other compilers... Abel) */ static int cancel_requested(NEC_Scanner *s) { return s->rdr_ctl->cancel; } static SANE_Status rdr_status(NEC_Scanner *s) { return s->rdr_ctl->status; } static int buf_status(NEC_shmem_ctl *s) { return s->shm_status; } static int reader_running(NEC_Scanner *s) { return s->rdr_ctl->running; } static int reader_process(NEC_Scanner *s) { SANE_Status status; sigset_t sigterm_set; static u_char cmd[] = {READ, 0, 0, 0, 0, 0, 0, 0, 0, 0}; int full_count = 0, counted; size_t waitindex, cmdindex; size_t bytes_to_queue; size_t nread; size_t max_bytes_per_read; int max_queue; int i; NEC_shmem_ctl *bc; s->rdr_ctl->running = 1; DBG(11, "<< reader_process\n"); sigemptyset (&sigterm_set); bytes_to_queue = s->bytes_to_read; max_bytes_per_read = s->dev->info.bufsize / s->params.bytes_per_line; if (max_bytes_per_read) max_bytes_per_read *= s->params.bytes_per_line; else /* this is a really tiny buffer..*/ max_bytes_per_read = s->dev->info.bufsize; /* wait_ready(s->fd); */ if (s->dev->info.queued_reads <= s->dev->info.buffers) max_queue = s->dev->info.queued_reads; else max_queue = s->dev->info.buffers; for (i = 0; i < max_queue; i++) { bc = &s->rdr_ctl->buf_ctl[i]; if (bytes_to_queue) { nread = bytes_to_queue; if (nread > max_bytes_per_read) nread = max_bytes_per_read; bc->used = nread; cmd[6] = nread >> 16; cmd[7] = nread >> 8; cmd[8] = nread; #ifdef QUEUEDEBUG DBG(2, "reader: req_enter...\n"); #endif status = sanei_scsi_req_enter (s->fd, cmd, sizeof (cmd), bc->buffer, &bc->used, &bc->qid); #ifdef QUEUEDEBUG DBG(2, "reader: req_enter ok\n"); #endif if (status != SANE_STATUS_GOOD) { DBG(1, "reader_process: read command failed: %s", sane_strstatus(status)); #ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED sanei_scsi_req_flush_all_extended(s->fd); #else sanei_scsi_req_flush_all(); #endif s->rdr_ctl->status = status; s->rdr_ctl->running = 0; return 2; } bc->shm_status = SHM_BUSY; bc->nreq = bc->used; bytes_to_queue -= bc->nreq; } else { bc->used = 0; bc->shm_status = SHM_EMPTY; } } waitindex = 0; cmdindex = i % s->dev->info.buffers; while(s->bytes_to_read > 0) { if (cancel_requested(s)) { #ifdef QUEUEDEBUG DBG(2, "reader: flushing requests...\n"); #endif #ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED sanei_scsi_req_flush_all_extended(s->fd); #else sanei_scsi_req_flush_all(); #endif #ifdef QUEUEDEBUG DBG(2, "reader: flushing requests ok\n"); #endif s->rdr_ctl->cancel = 0; s->rdr_ctl->status = SANE_STATUS_CANCELLED; s->rdr_ctl->running = 0; DBG(11, " reader_process (cancelled) >>\n"); return 1; } bc = &s->rdr_ctl->buf_ctl[waitindex]; if (bc->shm_status == SHM_BUSY) { #ifdef DEBUG { struct timeval t; gettimeofday(&t, 0); DBG(2, "rd: waiting for data %li.%06li\n", t.tv_sec, t.tv_usec); } #endif #ifdef QUEUEDEBUG DBG(2, "reader: req_wait...\n"); #endif status = sanei_scsi_req_wait(bc->qid); #ifdef QUEUEDEBUG DBG(2, "reader: req_wait ok\n"); #endif #ifdef DEBUG { struct timeval t; gettimeofday(&t, 0); DBG(2, "rd: data received %li.%06li\n", t.tv_sec, t.tv_usec); } #endif if (status != SANE_STATUS_GOOD) { DBG(1, "reader_process: read command failed: %s", sane_strstatus(status)); #ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED sanei_scsi_req_flush_all_extended(s->fd); #else sanei_scsi_req_flush_all(); #endif s->rdr_ctl->status = status; s->rdr_ctl->running = 0; return 2; } s->bytes_to_read -= bc->used; bytes_to_queue += bc->nreq - bc->used; bc->start = 0; bc->shm_status = SHM_FULL; waitindex++; if (waitindex == s->dev->info.buffers) waitindex = 0; } if (bytes_to_queue) { /* wait until the next buffer is completely read via read_data */ bc = &s->rdr_ctl->buf_ctl[cmdindex]; counted = 0; while (buf_status(bc) != SHM_EMPTY) { if (!counted) { counted = 1; full_count++; } if (cancel_requested(s)) { #ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED sanei_scsi_req_flush_all_extended(s->fd); #else sanei_scsi_req_flush_all(); #endif s->rdr_ctl->cancel = 0; s->rdr_ctl->status = SANE_STATUS_CANCELLED; s->rdr_ctl->running = 0; DBG(11, " reader_process (cancelled) >>\n"); return 1; } } nread = bytes_to_queue; if (nread > max_bytes_per_read) nread = max_bytes_per_read; bc->used = nread; cmd[6] = nread >> 16; cmd[7] = nread >> 8; cmd[8] = nread; status = sanei_scsi_req_enter (s->fd, cmd, sizeof (cmd), bc->buffer, &bc->used, &bc->qid); if (status != SANE_STATUS_GOOD) { DBG(1, "reader_process: read command failed: %s", sane_strstatus(status)); #ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED sanei_scsi_req_flush_all_extended(s->fd); #else sanei_scsi_req_flush_all(); #endif s->rdr_ctl->status = status; s->rdr_ctl->running = 0; return 2; } bc->shm_status = SHM_BUSY; bc->nreq = nread; bytes_to_queue -= nread; cmdindex++; if (cmdindex == s->dev->info.buffers) cmdindex = 0; } if (cancel_requested(s)) { #ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED sanei_scsi_req_flush_all_extended(s->fd); #else sanei_scsi_req_flush_all(); #endif s->rdr_ctl->cancel = 0; s->rdr_ctl->status = SANE_STATUS_CANCELLED; s->rdr_ctl->running = 0; DBG(11, " reader_process (cancelled) >>\n"); return 1; } } DBG(1, "buffer full conditions: %i\n", full_count); DBG(11, " reader_process>>\n"); s->rdr_ctl->running = 0; return 0; } static SANE_Status read_data (NEC_Scanner *s, SANE_Byte *buf, size_t * buf_size) { size_t copysize, copied = 0; NEC_shmem_ctl *bc; DBG(11, "<< read_data "); bc = &s->rdr_ctl->buf_ctl[s->read_buff]; while (copied < *buf_size) { /* wait until the reader process delivers data or a scanner error occurs: */ while ( buf_status(bc) != SHM_FULL && rdr_status(s) == SANE_STATUS_GOOD) { usleep(10); /* could perhaps be longer. make this user configurable?? */ } if (rdr_status(s) != SANE_STATUS_GOOD) { return rdr_status(s); DBG(11, ">>\n"); } copysize = bc->used - bc->start; if (copysize > *buf_size - copied ) copysize = *buf_size - copied; memcpy(buf, &(bc->buffer[bc->start]), copysize); copied += copysize; buf = &buf[copysize]; bc->start += copysize; if (bc->start >= bc->used) { bc->start = 0; bc->shm_status = SHM_EMPTY; s->read_buff++; if (s->read_buff == s->dev->info.buffers) s->read_buff = 0; bc = &s->rdr_ctl->buf_ctl[s->read_buff]; } } DBG(11, ">>\n"); return SANE_STATUS_GOOD; } #else /* don't USE_FORK: */ static SANE_Status read_data (NEC_Scanner *s, SANE_Byte *buf, size_t * buf_size) { static u_char cmd[] = {READ, 0, 0, 0, 0, 0, 0, 0, 0, 0}; SANE_Status status = SANE_STATUS_GOOD; size_t remain = *buf_size; size_t nread; DBG (11, "<< read_data "); /* sane_read_shuffled requires that read_data returns exactly *buf_size bytes, so it must be guaranteed here. Further make sure that not more bytes are read in than sanei_scsi_max_request_size allows, to avoid a failure of the read command */ while (remain > 0) { nread = remain; if (nread > s->dev->info.bufsize) nread = s->dev->info.bufsize; cmd[6] = nread >> 16; cmd[7] = nread >> 8; cmd[8] = nread; status = sanei_scsi_cmd (s->fd, cmd, sizeof (cmd), &buf[*buf_size - remain], &nread); if (status != SANE_STATUS_GOOD) { DBG(11, ">>\n"); return(status); } remain -= nread; } DBG (11, ">>\n"); return (status); } #endif static size_t max_string_size (const SANE_String_Const strings[]) { size_t size, max_size = 0; int i; DBG (10, "<< max_string_size "); for (i = 0; strings[i]; ++i) { size = strlen (strings[i]) + 1; if (size > max_size) max_size = size; } DBG (10, ">>\n"); return max_size; } static SANE_Status wait_ready(int fd) { SANE_Status status; int retry = 0; while ((status = test_unit_ready (fd)) != SANE_STATUS_GOOD) { DBG (5, "wait_ready failed (%d)\n", retry); DBG (5, "wait_ready status = (%d)\n", status); if (retry++ > 15){ return SANE_STATUS_IO_ERROR; } sleep(3); } return (status); } static SANE_Status attach (const char *devnam, NEC_Device ** devp) { SANE_Status status; NEC_Device *dev; NEC_Sense_Data sensedat; int fd; unsigned char inquiry_data[INQUIRY_LEN]; const unsigned char *model_name; mode_sense_param msp; size_t buf_size; DBG (10, "<< attach "); for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->sane.name, devnam) == 0) { if (devp) *devp = dev; return (SANE_STATUS_GOOD); } } sensedat.model = unknown; sensedat.complain_on_adf_error = 0; DBG (3, "attach: opening %s\n", devnam); #ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED { int bufsize = 4096; status = sanei_scsi_open_extended (devnam, &fd, &sense_handler, &sensedat, &bufsize); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: open failed: %s\n", sane_strstatus (status)); return (status); } if (bufsize < 4096) { DBG(1, "attach: open failed. no memory\n"); sanei_scsi_close(fd); return SANE_STATUS_NO_MEM; } } #else status = sanei_scsi_open (devnam, &fd, &sense_handler, &sensedat); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: open failed: %s\n", sane_strstatus (status)); return (status); } #endif DBG (3, "attach: sending INQUIRY\n"); memset (inquiry_data, 0, sizeof (inquiry_data)); buf_size = sizeof (inquiry_data); status = inquiry (fd, inquiry_data, &buf_size); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: inquiry failed: %s\n", sane_strstatus (status)); sanei_scsi_close (fd); return (status); } if (inquiry_data[0] == 6 && strncmp ((char *)inquiry_data + 8, "NEC", 3) == 0) { if (strncmp ((char *)inquiry_data + 16, "PC-IN500/4C", 11) == 0) sensedat.model = PCIN500; else sensedat.model = unknown; } if (sensedat.model == unknown) { DBG (1, "attach: device doesn't look like a NEC scanner\n"); DBG (1, " : Only PC-IN500/4C is supported.\n"); sanei_scsi_close (fd); return (SANE_STATUS_INVAL); } DBG (3, "attach: sending TEST_UNIT_READY\n"); status = test_unit_ready (fd); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: test unit ready failed (%s)\n", sane_strstatus (status)); sanei_scsi_close (fd); return (status); } DBG (3, "attach: sending MODE SELECT\n"); if (sensedat.model == PCIN500) status = mode_select_mud (fd, DEFAULT_MUD_1200); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: MODE_SELECT_MUD failed\n"); sanei_scsi_close (fd); return (SANE_STATUS_INVAL); } DBG (3, "attach: sending MODE SENSE/MUP page\n"); memset (&msp, 0, sizeof (msp)); buf_size = sizeof (msp); status = mode_sense (fd, &msp, &buf_size, 3); if (status != SANE_STATUS_GOOD) { DBG (1, "attach: MODE_SENSE/MUP page failed\n"); sanei_scsi_close (fd); return (SANE_STATUS_INVAL); } #ifdef DEBUG_NEC DBG (3,"attach: MODE SENSE parameter\n"); DBG(11, "%02x %02x %02x %02x " "%02x %02x %02x %02x %02x %02x %02x %02x\n", msp.mode_data_length, msp.mode_param_header2, msp.mode_param_header3, msp.mode_desciptor_length, msp.page_code, msp.page_length, msp.bmu, msp.res2, msp.mud[0], msp.mud[1], msp.res3, msp.res4); #endif dev = malloc (sizeof (*dev)); if (!dev) return (SANE_STATUS_NO_MEM); memset (dev, 0, sizeof (*dev)); dev->sane.name = (SANE_String) strdup (devnam); dev->sane.vendor = "NEC"; model_name = inquiry_data + 16; dev->sane.model = strndup ((const char *)model_name, 10); dev->sane.type = "flatbed scanner"; dev->sensedat.model = sensedat.model; DBG (5, "dev->sane.name = %s\n", dev->sane.name); DBG (5, "dev->sane.vendor = %s\n", dev->sane.vendor); DBG (5, "dev->sane.model = %s\n", dev->sane.model); DBG (5, "dev->sane.type = %s\n", dev->sane.type); if (sensedat.model == PCIN500) dev->info.res_range.quant = 10; else dev->info.res_range.quant = 0; dev->info.tl_x_ranges[SCAN_SIMPLE].min = SANE_FIX(0); dev->info.br_x_ranges[SCAN_SIMPLE].min = SANE_FIX(1); dev->info.tl_y_ranges[SCAN_SIMPLE].min = SANE_FIX(0); dev->info.br_y_ranges[SCAN_SIMPLE].min = SANE_FIX(1); dev->info.tl_x_ranges[SCAN_SIMPLE].quant = SANE_FIX(0); dev->info.br_x_ranges[SCAN_SIMPLE].quant = SANE_FIX(0); dev->info.tl_y_ranges[SCAN_SIMPLE].quant = SANE_FIX(0); dev->info.br_y_ranges[SCAN_SIMPLE].quant = SANE_FIX(0); if (sensedat.model == PCIN500) dev->info.res_default = 15; else dev->info.res_default = 150; dev->info.tl_x_ranges[SCAN_SIMPLE].max = SANE_FIX(209); dev->info.br_x_ranges[SCAN_SIMPLE].max = SANE_FIX(210); dev->info.tl_y_ranges[SCAN_SIMPLE].max = SANE_FIX(296); dev->info.br_y_ranges[SCAN_SIMPLE].max = SANE_FIX(297); dev->info.bmu = msp.bmu; dev->info.mud = (msp.mud[0] << 8) + msp.mud[1]; dev->info.adf_fsu_installed = 0; if (dev->sensedat.model == PCIN500) { dev->info.res_range.max = 48; dev->info.res_range.min = 5; dev->info.x_default = SANE_FIX(210); dev->info.tl_x_ranges[SCAN_SIMPLE].max = SANE_FIX(210); /* 304.8mm is the real max */ dev->info.br_x_ranges[SCAN_SIMPLE].max = SANE_FIX(210); /* 304.8mm is the real max */ dev->info.y_default = SANE_FIX(297); dev->info.tl_y_ranges[SCAN_SIMPLE].max = SANE_FIX(297); /* 431.8 is the real max */ dev->info.br_y_ranges[SCAN_SIMPLE].max = SANE_FIX(297); /* 431.8 is the real max */ } else { dev->info.res_range.max = 400; dev->info.res_range.min = 50; dev->info.x_default = SANE_FIX(210); dev->info.tl_x_ranges[SCAN_SIMPLE].max = SANE_FIX(210); /* 304.8mm is the real max */ dev->info.br_x_ranges[SCAN_SIMPLE].max = SANE_FIX(210); /* 304.8mm is the real max */ dev->info.y_default = SANE_FIX(297); dev->info.tl_y_ranges[SCAN_SIMPLE].max = SANE_FIX(297); /* 431.8 is the real max */ dev->info.br_y_ranges[SCAN_SIMPLE].max = SANE_FIX(297); /* 431.8 is the real max */ } sanei_scsi_close (fd); dev->info.threshold_range.min = 1; dev->info.threshold_range.max = 255; dev->info.threshold_range.quant = 0; dev->info.tint_range.min = 1; dev->info.tint_range.max = 255; dev->info.tint_range.quant = 0; dev->info.color_range.min = 1; dev->info.color_range.max = 255; dev->info.color_range.quant = 0; DBG (5, "res_default=%d\n", dev->info.res_default); DBG (5, "res_range.max=%d\n", dev->info.res_range.max); DBG (5, "res_range.min=%d\n", dev->info.res_range.min); DBG (5, "res_range.quant=%d\n", dev->info.res_range.quant); DBG (5, "x_default=%f\n", SANE_UNFIX(dev->info.x_default)); DBG (5, "tl_x_range[0].max=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_SIMPLE].max)); DBG (5, "tl_x_range[0].min=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_SIMPLE].min)); DBG (5, "tl_x_range[0].quant=%d\n", dev->info.tl_x_ranges[SCAN_SIMPLE].quant); DBG (5, "br_x_range[0].max=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_SIMPLE].max)); DBG (5, "br_x_range[0].min=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_SIMPLE].min)); DBG (5, "br_x_range[0].quant=%d\n", dev->info.br_x_ranges[SCAN_SIMPLE].quant); DBG (5, "y_default=%f\n", SANE_UNFIX(dev->info.y_default)); DBG (5, "tl_y_range[0].max=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_SIMPLE].max)); DBG (5, "tl_y_range[0].min=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_SIMPLE].min)); DBG (5, "tl_y_range[0].quant=%d\n", dev->info.tl_y_ranges[SCAN_SIMPLE].quant); DBG (5, "br_y_range[0].max=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_SIMPLE].max)); DBG (5, "br_y_range[0].min=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_SIMPLE].min)); DBG (5, "br_y_range[0].quant=%d\n", dev->info.br_y_ranges[SCAN_SIMPLE].quant); if (dev->info.adf_fsu_installed & HAVE_FSU) { DBG (5, "tl_x_range[1].max=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_WITH_FSU].max)); DBG (5, "tl_x_range[1].min=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_WITH_FSU].min)); DBG (5, "tl_x_range[1].quant=%d\n", dev->info.tl_x_ranges[SCAN_WITH_FSU].quant); DBG (5, "br_x_range[1].max=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_WITH_FSU].max)); DBG (5, "br_x_range[1].min=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_WITH_FSU].min)); DBG (5, "br_x_range[1].quant=%d\n", dev->info.br_x_ranges[SCAN_WITH_FSU].quant); DBG (5, "tl_y_range[1].max=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_WITH_FSU].max)); DBG (5, "tl_y_range[1].min=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_WITH_FSU].min)); DBG (5, "tl_y_range[1].quant=%d\n", dev->info.tl_y_ranges[SCAN_WITH_FSU].quant); DBG (5, "br_y_range[1].max=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_WITH_FSU].max)); DBG (5, "br_y_range[1].min=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_WITH_FSU].min)); DBG (5, "br_y_range[1].quant=%d\n", dev->info.br_y_ranges[SCAN_WITH_FSU].quant); } if (dev->info.adf_fsu_installed & HAVE_ADF) { DBG (5, "tl_x_range[2].max=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_WITH_ADF].max)); DBG (5, "tl_x_range[2].min=%f\n", SANE_UNFIX(dev->info.tl_x_ranges[SCAN_WITH_ADF].min)); DBG (5, "tl_x_range[2].quant=%d\n", dev->info.tl_x_ranges[SCAN_WITH_ADF].quant); DBG (5, "br_x_range[2].max=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_WITH_ADF].max)); DBG (5, "br_x_range[2].min=%f\n", SANE_UNFIX(dev->info.br_x_ranges[SCAN_WITH_ADF].min)); DBG (5, "br_x_range[2].quant=%d\n", dev->info.br_x_ranges[SCAN_WITH_ADF].quant); DBG (5, "tl_y_range[2].max=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_WITH_ADF].max)); DBG (5, "tl_y_range[2].min=%f\n", SANE_UNFIX(dev->info.tl_y_ranges[SCAN_WITH_ADF].min)); DBG (5, "tl_y_range[2].quant=%d\n", dev->info.tl_y_ranges[SCAN_WITH_ADF].quant); DBG (5, "br_y_range[2].max=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_WITH_ADF].max)); DBG (5, "br_y_range[2].min=%f\n", SANE_UNFIX(dev->info.br_y_ranges[SCAN_WITH_ADF].min)); DBG (5, "br_y_range[2].quant=%d\n", dev->info.br_y_ranges[SCAN_WITH_ADF].quant); } DBG (5, "bmu=%d\n", dev->info.bmu); DBG (5, "mud=%d\n", dev->info.mud); ++num_devices; dev->next = first_dev; first_dev = dev; if (devp) *devp = dev; DBG (10, ">>\n"); return (SANE_STATUS_GOOD); } /* Enabling / disabling of gamma options. Depends on many user settable options, so lets put it into one function to be called by init_options and by sane_control_option */ #ifdef USE_CUSTOM_GAMMA static void set_gamma_caps(NEC_Scanner *s) { /* neither fixed nor custom gamma for line art modes */ if ( strcmp(s->val[OPT_MODE].s, M_LINEART) == 0 || strcmp(s->val[OPT_MODE].s, M_LINEART_COLOR) == 0) { s->opt[OPT_GAMMA].cap |= SANE_CAP_INACTIVE; s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; } else if (strcmp(s->val[OPT_MODE].s, M_GRAY) == 0) { s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE; if (s->val[OPT_CUSTOM_GAMMA].w == SANE_FALSE) { s->opt[OPT_GAMMA].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; } else { s->opt[OPT_GAMMA].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE; } s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; } else { /* color mode */ s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE; if (s->val[OPT_CUSTOM_GAMMA].w == SANE_FALSE) { s->opt[OPT_GAMMA].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; } else { s->opt[OPT_GAMMA].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; } s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; } } #endif /* USE_CUSTOM_GAMMA */ /* The next function is a slightly modified version of sanei_constrain_value Instead of returning status information like STATUS_INVAL, it adjusts an invalid value to the nearest allowed one. */ static void clip_value (const SANE_Option_Descriptor * opt, void * value) { const SANE_String_Const * string_list; const SANE_Word * word_list; int i, num_matches, match; const SANE_Range * range; SANE_Word w, v; size_t len; switch (opt->constraint_type) { case SANE_CONSTRAINT_RANGE: w = *(SANE_Word *) value; range = opt->constraint.range; if (w < range->min) w = range->min; else if (w > range->max) w = range->max; if (range->quant) { v = (w - range->min + range->quant/2) / range->quant; w = v * range->quant + range->min; *(SANE_Word*) value = w; } break; case SANE_CONSTRAINT_WORD_LIST: w = *(SANE_Word *) value; word_list = opt->constraint.word_list; for (i = 1; w != word_list[i]; ++i) if (i >= word_list[0]) /* somewhat arbitrary... Would be better to have a default value explicitly defined. */ *(SANE_Word*) value = word_list[1]; break; case SANE_CONSTRAINT_STRING_LIST: /* Matching algorithm: take the longest unique match ignoring case. If there is an exact match, it is admissible even if the same string is a prefix of a longer option name. */ string_list = opt->constraint.string_list; len = strlen (value); /* count how many matches of length LEN characters we have: */ num_matches = 0; match = -1; for (i = 0; string_list[i]; ++i) if (strncasecmp (value, string_list[i], len) == 0 && len <= strlen (string_list[i])) { match = i; if (len == strlen (string_list[i])) { /* exact match... */ if (strcmp (value, string_list[i]) != 0) /* ...but case differs */ strcpy (value, string_list[match]); } ++num_matches; } if (num_matches > 1) /* xxx quite arbitrary... We could also choose the first match */ strcpy(value, string_list[match]); else if (num_matches == 1) strcpy (value, string_list[match]); else strcpy (value, string_list[0]); default: break; } } /* make sure that enough memory is allocated for each string, so that the strcpy in sane_control_option / set value cannot write behind the end of the allocated memory. */ static SANE_Status init_string_option(NEC_Scanner *s, SANE_String_Const name, SANE_String_Const title, SANE_String_Const desc, const SANE_String_Const *string_list, int option, int default_index) { int i; s->opt[option].name = name; s->opt[option].title = title; s->opt[option].desc = desc; s->opt[option].type = SANE_TYPE_STRING; s->opt[option].size = max_string_size (string_list); s->opt[option].constraint_type = SANE_CONSTRAINT_STRING_LIST; s->opt[option].constraint.string_list = string_list; s->val[option].s = malloc(s->opt[option].size); if (s->val[option].s == 0) { for (i = 1; i < NUM_OPTIONS; i++) { if (s->val[i].s && s->opt[i].type == SANE_TYPE_STRING) free(s->val[i].s); } return SANE_STATUS_NO_MEM; } strcpy(s->val[option].s, string_list[default_index]); return SANE_STATUS_GOOD; } static SANE_Status init_options (NEC_Scanner * s) { int i, default_source; SANE_Word scalar; DBG (10, "<< init_options "); memset (s->opt, 0, sizeof (s->opt)); memset (s->val, 0, sizeof (s->val)); for (i = 0; i < NUM_OPTIONS; ++i) { s->opt[i].size = sizeof (SANE_Word); s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; s->val[i].s = 0; } s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; /* Mode group: */ s->opt[OPT_MODE_GROUP].title = "Scan Mode"; s->opt[OPT_MODE_GROUP].desc = ""; s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_MODE_GROUP].cap = 0; s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* scan mode */ init_string_option(s, SANE_NAME_SCAN_MODE, SANE_TITLE_SCAN_MODE, SANE_DESC_SCAN_MODE, mode_list, OPT_MODE, MODES_COLOR); /* half tone */ init_string_option(s, SANE_NAME_HALFTONE_PATTERN, SANE_TITLE_HALFTONE_PATTERN, SANE_DESC_HALFTONE " (not support)", halftone_list, OPT_HALFTONE, 0); if (s->dev->sensedat.model == PCIN500) s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE; i = 0; default_source = -1; if (s->dev->info.adf_fsu_installed & HAVE_ADF) { s->dev->info.scansources[i++] = use_adf; default_source = SCAN_WITH_ADF; } if (s->dev->info.adf_fsu_installed & HAVE_FSU) { s->dev->info.scansources[i++] = use_fsu; if (default_source < 0) default_source = SCAN_WITH_FSU; } s->dev->info.scansources[i++] = use_simple; if (default_source < 0) default_source = SCAN_SIMPLE; s->dev->info.scansources[i] = 0; init_string_option(s, SANE_NAME_SCAN_SOURCE, SANE_TITLE_SCAN_SOURCE, SANE_DESC_SCAN_SOURCE, (SANE_String_Const*)s->dev->info.scansources, OPT_SCANSOURCE, 0); if (i < 2) s->opt[OPT_SCANSOURCE].cap |= SANE_CAP_INACTIVE; if (s->dev->sensedat.model == PCIN500) init_string_option(s, "Paper size", "Paper size", "Paper size", paper_list_pcin500, OPT_PAPER, 0); else init_string_option(s, "Paper size", "Paper size", "Paper size", paper_list_pcinxxx, OPT_PAPER, 1); /* gamma */ init_string_option(s, "Gamma", "Gamma", "Gamma", gamma_list, OPT_GAMMA, 0); /* Resolution Group */ s->opt[OPT_RESOLUTION_GROUP].title = "Resolution"; s->opt[OPT_RESOLUTION_GROUP].desc = ""; s->opt[OPT_RESOLUTION_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_RESOLUTION_GROUP].cap = 0; s->opt[OPT_RESOLUTION_GROUP].constraint_type = SANE_CONSTRAINT_NONE; #ifdef USE_RESOLUTION_LIST /* select resolution */ if (s->dev->sensedat.model == PCIN500) init_string_option(s, "Resolution", "Resolution", "Resolution", resolution_list_pcin500, OPT_RESOLUTION_LIST, RESOLUTION_MAX_PCIN500); else init_string_option(s, "Resolution", "Resolution", "Resolution", resolution_list_pcinxxx, OPT_RESOLUTION_LIST, RESOLUTION_MAX_PCINXXX); #endif /* x & y resolution */ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; if (s->dev->sensedat.model == PCIN500) s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION"(x 10)"; else s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_RESOLUTION].constraint.range = &s->dev->info.res_range; s->val[OPT_RESOLUTION].w = s->dev->info.res_default; /* "Geometry" group: */ s->opt[OPT_GEOMETRY_GROUP].title = "Geometry"; s->opt[OPT_GEOMETRY_GROUP].desc = ""; s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* top-left x */ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; s->opt[OPT_TL_X].unit = SANE_UNIT_MM; s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_X].constraint.range = &s->dev->info.tl_x_ranges[default_source]; s->val[OPT_TL_X].w = s->dev->info.tl_x_ranges[default_source].min; /* top-left y */ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TL_Y].constraint.range = &s->dev->info.tl_y_ranges[default_source]; s->val[OPT_TL_Y].w = s->dev->info.tl_y_ranges[default_source].min; /* bottom-right x */ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; s->opt[OPT_BR_X].unit = SANE_UNIT_MM; s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_X].constraint.range = &s->dev->info.br_x_ranges[default_source]; scalar = s->dev->info.x_default; clip_value (&s->opt[OPT_BR_X], &scalar); s->val[OPT_BR_X].w = scalar; /* bottom-right y */ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_BR_Y].constraint.range = &s->dev->info.br_y_ranges[default_source]; scalar = s->dev->info.y_default; clip_value (&s->opt[OPT_BR_X], &scalar); s->val[OPT_BR_Y].w = scalar; /* "Enhancement" group: */ s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement"; s->opt[OPT_ENHANCEMENT_GROUP].desc = ""; s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; s->opt[OPT_ENHANCEMENT_GROUP].cap = 0; s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; /* edge emphasis */ init_string_option(s, "Edge emphasis", "Edge emphasis", "Edge emphasis", edge_emphasis_list, OPT_EDGE_EMPHASIS, 0); if (s->dev->sensedat.model == PCIN500) s->opt[OPT_EDGE_EMPHASIS].cap |= SANE_CAP_INACTIVE; /* OR */ s->opt[OPT_OR].name = "OR"; s->opt[OPT_OR].title = "OR"; s->opt[OPT_OR].desc = "Select OR emphancement"; s->opt[OPT_OR].type = SANE_TYPE_BOOL; s->val[OPT_OR].w = SANE_FALSE; /* EDGE */ s->opt[OPT_EDGE].name = "edge"; s->opt[OPT_EDGE].title = "Edge"; s->opt[OPT_EDGE].desc = "Select Edge emphancement"; s->opt[OPT_EDGE].type = SANE_TYPE_BOOL; s->val[OPT_EDGE].w = SANE_FALSE; /* NR */ s->opt[OPT_NR].name = "NR"; s->opt[OPT_NR].title = "NR"; s->opt[OPT_NR].desc = "Select noise reduction"; s->opt[OPT_NR].type = SANE_TYPE_BOOL; s->val[OPT_NR].w = SANE_FALSE; if (s->dev->sensedat.model != PCIN500) { s->opt[OPT_EDGE].cap |= SANE_CAP_INACTIVE; s->opt[OPT_NR].cap |= SANE_CAP_INACTIVE; s->opt[OPT_OR].cap |= SANE_CAP_INACTIVE; } /* tint */ s->opt[OPT_TINT].name = "tint"; s->opt[OPT_TINT].title = "Tint"; s->opt[OPT_TINT].desc = "Select tint"; s->opt[OPT_TINT].type = SANE_TYPE_INT; s->opt[OPT_TINT].unit = SANE_UNIT_NONE; s->opt[OPT_TINT].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_TINT].constraint.range = &s->dev->info.tint_range; s->val[OPT_TINT].w = 128; if (s->dev->sensedat.model != PCIN500) s->opt[OPT_TINT].cap |= SANE_CAP_INACTIVE; /* color */ s->opt[OPT_COLOR].name = "color"; s->opt[OPT_COLOR].title = "Color"; s->opt[OPT_COLOR].desc = "Select color"; s->opt[OPT_COLOR].type = SANE_TYPE_INT; s->opt[OPT_COLOR].unit = SANE_UNIT_NONE; s->opt[OPT_COLOR].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_COLOR].constraint.range = &s->dev->info.color_range; s->val[OPT_COLOR].w = 128; if (s->dev->sensedat.model != PCIN500) s->opt[OPT_COLOR].cap |= SANE_CAP_INACTIVE; /* 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_INT; s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE; s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_THRESHOLD].constraint.range = &s->dev->info.threshold_range; s->val[OPT_THRESHOLD].w = 128; s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; #ifdef USE_COLOR_THRESHOLD s->opt[OPT_THRESHOLD_R].name = SANE_NAME_THRESHOLD "-red"; /* xxx the titles and descriptions are confusing: "set white point (red)" Any idea? maybe "threshold to get the red component on" */ s->opt[OPT_THRESHOLD_R].title = SANE_TITLE_THRESHOLD " (red)"; s->opt[OPT_THRESHOLD_R].desc = SANE_DESC_THRESHOLD " (red)"; s->opt[OPT_THRESHOLD_R].type = SANE_TYPE_INT; s->opt[OPT_THRESHOLD_R].unit = SANE_UNIT_NONE; s->opt[OPT_THRESHOLD_R].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_THRESHOLD_R].constraint.range = &s->dev->info.threshold_range; s->val[OPT_THRESHOLD_R].w = 128; s->opt[OPT_THRESHOLD_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_THRESHOLD_G].name = SANE_NAME_THRESHOLD "-green"; s->opt[OPT_THRESHOLD_G].title = SANE_TITLE_THRESHOLD " (green)"; s->opt[OPT_THRESHOLD_G].desc = SANE_DESC_THRESHOLD " (green)"; s->opt[OPT_THRESHOLD_G].type = SANE_TYPE_INT; s->opt[OPT_THRESHOLD_G].unit = SANE_UNIT_NONE; s->opt[OPT_THRESHOLD_G].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_THRESHOLD_G].constraint.range = &s->dev->info.threshold_range; s->val[OPT_THRESHOLD_G].w = 128; s->opt[OPT_THRESHOLD_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_THRESHOLD_B].name = SANE_NAME_THRESHOLD "-blue"; s->opt[OPT_THRESHOLD_B].title = SANE_TITLE_THRESHOLD " (blue)"; s->opt[OPT_THRESHOLD_B].desc = SANE_DESC_THRESHOLD " (blue)"; s->opt[OPT_THRESHOLD_B].type = SANE_TYPE_INT; s->opt[OPT_THRESHOLD_B].unit = SANE_UNIT_NONE; s->opt[OPT_THRESHOLD_B].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_THRESHOLD_B].constraint.range = &s->dev->info.threshold_range; s->val[OPT_THRESHOLD_B].w = 128; s->opt[OPT_THRESHOLD_B].cap |= SANE_CAP_INACTIVE; #endif /* light color (for gray scale and line art scans) */ init_string_option(s, "LightColor", "LightColor", "LightColor", light_color_list, OPT_LIGHTCOLOR, 3); s->opt[OPT_LIGHTCOLOR].cap |= SANE_CAP_INACTIVE; s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; s->val[OPT_PREVIEW].w = SANE_FALSE; #ifdef USE_CUSTOM_GAMMA /* custom-gamma table */ s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA; s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL; s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE; /* grayscale gamma vector */ s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR; s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR; s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR; s->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT; #if 0 s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE; #endif s->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR].wa = &s->gamma_table[0][0]; /* red gamma vector */ s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R; s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT; #if 0 s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; #endif s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[1][0]; /* green gamma vector */ s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G; s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT; #if 0 s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; #endif s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[2][0]; /* blue gamma vector */ s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B; s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT; #if 0 s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; #endif s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE; s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word); s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE; s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range; s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[3][0]; set_gamma_caps(s); #endif DBG (10, ">>\n"); return SANE_STATUS_GOOD; } static SANE_Status do_cancel (NEC_Scanner * s) { DBG (10, "<< do_cancel "); #ifdef USE_FORK if (s->reader_pid > 0) { int exit_status; int count = 0; /* ensure child knows it's time to stop: */ DBG(11, "stopping reader process\n"); s->rdr_ctl->cancel = 1; while(reader_running(s) && count < 100) { usleep(100000); count++; }; if (reader_running(s)) { kill(s->reader_pid, SIGKILL); } wait(&exit_status); DBG(11, "reader process stopped\n"); s->reader_pid = 0; } #endif s->scanning = SANE_FALSE; if (s->fd >= 0) { sanei_scsi_close (s->fd); s->fd = -1; } #ifdef USE_FORK { struct shmid_ds ds; if (s->shmid != -1) shmctl(s->shmid, IPC_RMID, &ds); s->shmid = -1; } #endif if (s->buffer) free(s->buffer); s->buffer = 0; DBG (10, ">>\n"); return (SANE_STATUS_CANCELLED); } static NEC_New_Device *new_devs = 0; static NEC_New_Device *new_dev_pool = 0; static SANE_Status attach_and_list(const char *devnam) { SANE_Status res; NEC_Device *devp; NEC_New_Device *np; res = attach(devnam, &devp); if (res == SANE_STATUS_GOOD) { if (new_dev_pool) { np = new_dev_pool; new_dev_pool = np->next; } else { np = malloc(sizeof(NEC_New_Device)); if (np == 0) return SANE_STATUS_NO_MEM; } np->next =new_devs; np->dev = devp; new_devs = np; } return res; } SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { char devnam[PATH_MAX] = "/dev/scanner"; char line[PATH_MAX]; const char *lp; char *word; char *end; FILE *fp; int opt_index = 0; int buffers[2] = {DEFAULT_BUFFERS, DEFAULT_BUFFERS}; int bufsize[2] = {DEFAULT_BUFSIZE, DEFAULT_BUFSIZE}; int queued_reads[2] = {DEFAULT_QUEUED_READS, DEFAULT_QUEUED_READS}; int linecount = 0; #if 1 NEC_Device nd; NEC_Device *dp = &nd; #else NEC_Device *dp; #endif NEC_New_Device *np; int i; (void) authorize; /* silence compilation warnings */ DBG_INIT (); DBG (10, "<< sane_init "); DBG (1, "sane_init: NEC (Ver %d.%d)\n", NEC_MAJOR, NEC_MINOR); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0); fp = sanei_config_open (NEC_CONFIG_FILE); if (!fp) { /* use "/dev/scanner" as the default device name if no config file is available */ attach (devnam, &dp); /* make sure that there are at least two buffers */ if (DEFAULT_BUFFERS < 2) dp->info.buffers = DEFAULT_BUFFERS; else dp->info.buffers = 2; dp->info.wanted_bufsize = DEFAULT_BUFSIZE; dp->info.queued_reads = DEFAULT_QUEUED_READS; return SANE_STATUS_GOOD; } while (fgets(line, PATH_MAX, fp)) { linecount++; word = 0; lp = sanei_config_get_string(line, &word); if (word) { if (word[0] != '#') { if (strcmp(word, "option") == 0) { free(word); word = 0; lp = sanei_config_get_string(lp, &word); if (strcmp(word, "buffers") == 0) { free(word); word = 0; sanei_config_get_string(lp, &word); i = strtol(word, &end, 0); if (end == word) { DBG(1, "error in config file, line %i: number expected:\n", linecount); DBG(1, "%s\n", line); } else if (i > 2) buffers[opt_index] = i; else buffers[opt_index] = 2; } else if (strcmp(word, "buffersize") == 0) { free(word); word = 0; sanei_config_get_string(lp, &word); i = strtol(word, &end, 0); if (word == end) { DBG(1, "error in config file, line %i: number expected:\n", linecount); DBG(1, "%s\n", line); } else bufsize[opt_index] = i; } else if (strcmp(word, "readqueue") == 0) { free(word); word = 0; sanei_config_get_string(lp, &word); i = strtol(word, &end, 0); if (word == end) { DBG(1, "error in config file, line %i: number expected:\n", linecount); DBG(1, "%s\n", line); } else queued_reads[opt_index] = i; } else { DBG(1, "error in config file, line %i: unknown option\n", linecount); DBG(1, "%s\n", line); } } else { while (new_devs) { if (buffers[1] >= 2) new_devs->dev->info.buffers = buffers[1]; else new_devs->dev->info.buffers = 2; if (bufsize[1] > 0) new_devs->dev->info.wanted_bufsize = bufsize[1]; else new_devs->dev->info.wanted_bufsize = DEFAULT_BUFSIZE; if (queued_reads[1] >= 0) new_devs->dev->info.queued_reads = queued_reads[1]; else new_devs->dev->info.queued_reads = 0; np = new_devs->next; new_devs->next = new_dev_pool; new_dev_pool = new_devs; new_devs = np; } if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0; sanei_config_attach_matching_devices(line, &attach_and_list); buffers[1] = buffers[0]; bufsize[1] = bufsize[0]; queued_reads[1] = queued_reads[0]; opt_index = 1; } } if (word) free(word); } } while (new_devs) { if (buffers[1] >= 2) new_devs->dev->info.buffers = buffers[1]; else new_devs->dev->info.buffers = 2; if (bufsize[1] > 0) new_devs->dev->info.wanted_bufsize = bufsize[1]; else new_devs->dev->info.wanted_bufsize = DEFAULT_BUFSIZE; if (queued_reads[1] >= 0) new_devs->dev->info.queued_reads = queued_reads[1]; else new_devs->dev->info.queued_reads = 0; if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0; np = new_devs->next; free(new_devs); new_devs = np; } while (new_dev_pool) { np = new_dev_pool->next; free(new_dev_pool); new_dev_pool = np; } fclose(fp); DBG (10, ">>\n"); return (SANE_STATUS_GOOD); } void sane_exit (void) { NEC_Device *dev, *next; DBG (10, "<< sane_exit "); for (dev = first_dev; dev; dev = next) { next = dev->next; free ((void *) dev->sane.name); free ((void *) dev->sane.model); free (dev); } first_dev = 0; if (devlist) free(devlist); DBG (10, ">>\n"); } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { NEC_Device *dev; int i; DBG (10, "<< sane_get_devices "); (void) local_only; /* silence compilation warnings */ if (devlist) free (devlist); devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); if (!devlist) return (SANE_STATUS_NO_MEM); i = 0; for (dev = first_dev; dev; dev = dev->next) devlist[i++] = &dev->sane; devlist[i++] = 0; *device_list = devlist; DBG (10, ">>\n"); return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const devnam, SANE_Handle * handle) { SANE_Status status; NEC_Device *dev; NEC_Scanner *s; #ifdef USE_CUSTOM_GAMMA int i, j; #endif DBG (10, "<< sane_open "); if (devnam[0]) { for (dev = first_dev; dev; dev = dev->next) { if (strcmp (dev->sane.name, devnam) == 0) break; } if (!dev) { status = attach (devnam, &dev); if (status != SANE_STATUS_GOOD) return (status); } } else { dev = first_dev; } if (!dev) return (SANE_STATUS_INVAL); s = malloc (sizeof (*s)); if (!s) return SANE_STATUS_NO_MEM; memset (s, 0, sizeof (*s)); s->fd = -1; s->dev = dev; s->buffer = 0; #ifdef USE_CUSTOM_GAMMA for (i = 0; i < 4; ++i) for (j = 0; j < 256; ++j) s->gamma_table[i][j] = j; #endif status = init_options (s); if (status != SANE_STATUS_GOOD) { /* xxx clean up mallocs */ return status; } s->next = first_handle; first_handle = s; *handle = s; DBG (10, ">>\n"); return SANE_STATUS_GOOD; } void sane_close (SANE_Handle handle) { NEC_Scanner *s = (NEC_Scanner *) handle; DBG (10, "<< sane_close "); if (s->fd != -1) sanei_scsi_close (s->fd); #ifdef USE_FORK { struct shmid_ds ds; if (s->shmid != -1) shmctl(s->shmid, IPC_RMID, &ds); } #endif if (s->buffer) free(s->buffer); free (s); DBG (10, ">>\n"); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { NEC_Scanner *s = handle; DBG (10, "<< sane_get_option_descriptor "); if ((unsigned) option >= NUM_OPTIONS) return (0); DBG (10, ">>\n"); return (s->opt + option); } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { NEC_Scanner *s = handle; SANE_Status status; #ifdef USE_CUSTOM_GAMMA SANE_Word w, cap; #else SANE_Word cap; #endif int range_index; DBG (10, "<< sane_control_option %i", option); if (info) *info = 0; if (s->scanning) return (SANE_STATUS_DEVICE_BUSY); if (option >= NUM_OPTIONS) return (SANE_STATUS_INVAL); cap = s->opt[option].cap; if (!SANE_OPTION_IS_ACTIVE (cap)) return (SANE_STATUS_INVAL); if (action == SANE_ACTION_GET_VALUE) { switch (option) { /* word options: */ case OPT_RESOLUTION: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: case OPT_NUM_OPTS: case OPT_THRESHOLD: case OPT_TINT: case OPT_COLOR: #ifdef USE_COLOR_THRESHOLD case OPT_THRESHOLD_R: case OPT_THRESHOLD_G: case OPT_THRESHOLD_B: #endif case OPT_OR: case OPT_NR: case OPT_EDGE: case OPT_PREVIEW: #ifdef USE_CUSTOM_GAMMA case OPT_CUSTOM_GAMMA: #endif *(SANE_Word *) val = s->val[option].w; #if 0 /* here, values are read; reload should not be necessary */ if (info) *info |= SANE_INFO_RELOAD_PARAMS; #endif return (SANE_STATUS_GOOD); #ifdef USE_CUSTOM_GAMMA /* word-array options: */ case OPT_GAMMA_VECTOR: case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: memcpy (val, s->val[option].wa, s->opt[option].size); return SANE_STATUS_GOOD; #endif /* string options: */ case OPT_MODE: case OPT_HALFTONE: case OPT_PAPER: case OPT_GAMMA: #ifdef USE_RESOLUTION_LIST case OPT_RESOLUTION_LIST: #endif case OPT_EDGE_EMPHASIS: case OPT_LIGHTCOLOR: case OPT_SCANSOURCE: strcpy (val, s->val[option].s); #if 0 if (info) *info |= SANE_INFO_RELOAD_PARAMS; #endif return (SANE_STATUS_GOOD); } } else if (action == SANE_ACTION_SET_VALUE) { if (!SANE_OPTION_IS_SETTABLE (cap)) return (SANE_STATUS_INVAL); status = sanei_constrain_value (s->opt + option, val, info); if (status != SANE_STATUS_GOOD) return status; switch (option) { /* (mostly) side-effect-free word options: */ case OPT_RESOLUTION: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: if (info && s->val[option].w != *(SANE_Word *) val) *info |= SANE_INFO_RELOAD_PARAMS; // fall through case OPT_NUM_OPTS: case OPT_THRESHOLD: /* xxx theoretically, we could use OPT_THRESHOLD in bi-level color mode to adjust all three other threshold together. But this would require to set the bit SANE_INFO_RELOAD_OPTIONS in *info, and that would unfortunately cause a crash in both xscanimage and xsane... Therefore, OPT_THRESHOLD is disabled for bi-level color scan right now. */ case OPT_TINT: case OPT_COLOR: #ifdef USE_COLOR_THRESHOLD case OPT_THRESHOLD_R: case OPT_THRESHOLD_G: case OPT_THRESHOLD_B: #endif case OPT_OR: case OPT_NR: case OPT_EDGE: case OPT_PREVIEW: s->val[option].w = *(SANE_Word *) val; return (SANE_STATUS_GOOD); case OPT_MODE: if (strcmp (val, M_LINEART) == 0) { s->opt[OPT_LIGHTCOLOR].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_THRESHOLD].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_TINT].cap |= SANE_CAP_INACTIVE; s->opt[OPT_COLOR].cap |= SANE_CAP_INACTIVE; #ifdef USE_COLOR_THRESHOLD s->opt[OPT_THRESHOLD_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_THRESHOLD_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_THRESHOLD_B].cap |= SANE_CAP_INACTIVE; #endif if (s->dev->sensedat.model == PCIN500) s->opt[OPT_HALFTONE].cap &= ~SANE_CAP_INACTIVE; } else if (strcmp (val, M_LINEART_COLOR) == 0) { s->opt[OPT_LIGHTCOLOR].cap |= SANE_CAP_INACTIVE; s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; s->opt[OPT_TINT].cap |= SANE_CAP_INACTIVE; s->opt[OPT_COLOR].cap |= SANE_CAP_INACTIVE; #ifdef USE_COLOR_THRESHOLD s->opt[OPT_THRESHOLD_R].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_THRESHOLD_G].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_THRESHOLD_B].cap &= ~SANE_CAP_INACTIVE; #endif if (s->dev->sensedat.model == PCIN500) s->opt[OPT_HALFTONE].cap &= ~SANE_CAP_INACTIVE; } else if (strcmp (val, M_GRAY) == 0) { s->opt[OPT_LIGHTCOLOR].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; s->opt[OPT_TINT].cap |= SANE_CAP_INACTIVE; s->opt[OPT_COLOR].cap |= SANE_CAP_INACTIVE; #ifdef USE_COLOR_THRESHOLD s->opt[OPT_THRESHOLD_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_THRESHOLD_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_THRESHOLD_B].cap |= SANE_CAP_INACTIVE; #endif s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE; } else { s->opt[OPT_LIGHTCOLOR].cap |= SANE_CAP_INACTIVE; s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; s->opt[OPT_TINT].cap &= ~SANE_CAP_INACTIVE; s->opt[OPT_COLOR].cap &= ~SANE_CAP_INACTIVE; #ifdef USE_COLOR_THRESHOLD s->opt[OPT_THRESHOLD_R].cap |= SANE_CAP_INACTIVE; s->opt[OPT_THRESHOLD_G].cap |= SANE_CAP_INACTIVE; s->opt[OPT_THRESHOLD_B].cap |= SANE_CAP_INACTIVE; #endif s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE; } #if 0 if ( strcmp (val, M_LINEART) == 0 || strcmp (val, M_GRAY) == 0) { s->opt[OPT_LIGHTCOLOR].cap &= ~SANE_CAP_INACTIVE; } else { s->opt[OPT_LIGHTCOLOR].cap |= SANE_CAP_INACTIVE; } #endif strcpy(s->val[option].s, val); #ifdef USE_CUSTOM_GAMMA set_gamma_caps(s); #endif if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; return (SANE_STATUS_GOOD); case OPT_GAMMA: case OPT_HALFTONE: case OPT_EDGE_EMPHASIS: case OPT_LIGHTCOLOR: #if 0 if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); #endif strcpy(s->val[option].s, val); return (SANE_STATUS_GOOD); case OPT_SCANSOURCE: if (info && strcmp (s->val[option].s, (SANE_String) val)) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; #if 0 if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); #endif strcpy(s->val[option].s, val); if (strcmp(val, use_fsu) == 0) range_index = SCAN_WITH_FSU; else if (strcmp(val, use_adf) == 0) range_index = SCAN_WITH_ADF; else range_index = SCAN_SIMPLE; s->opt[OPT_TL_X].constraint.range = &s->dev->info.tl_x_ranges[range_index]; clip_value (&s->opt[OPT_TL_X], &s->val[OPT_TL_X].w); s->opt[OPT_TL_Y].constraint.range = &s->dev->info.tl_y_ranges[range_index]; clip_value (&s->opt[OPT_TL_Y], &s->val[OPT_TL_Y].w); s->opt[OPT_BR_X].constraint.range = &s->dev->info.br_x_ranges[range_index]; clip_value (&s->opt[OPT_BR_X], &s->val[OPT_BR_X].w); s->opt[OPT_BR_Y].constraint.range = &s->dev->info.br_y_ranges[range_index]; clip_value (&s->opt[OPT_BR_Y], &s->val[OPT_BR_Y].w); return (SANE_STATUS_GOOD); case OPT_PAPER: if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; #if 0 if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); #endif strcpy(s->val[option].s, val); s->val[OPT_TL_X].w = SANE_FIX(0); s->val[OPT_TL_Y].w = SANE_FIX(0); if (strcmp (s->val[option].s, "A3") == 0){ s->val[OPT_BR_X].w = SANE_FIX(297); s->val[OPT_BR_Y].w = SANE_FIX(420); }else if (strcmp (s->val[option].s, "A4") == 0){ s->val[OPT_BR_X].w = SANE_FIX(210); s->val[OPT_BR_Y].w = SANE_FIX(297); }else if (strcmp (s->val[option].s, "A5") == 0){ s->val[OPT_BR_X].w = SANE_FIX(148.5); s->val[OPT_BR_Y].w = SANE_FIX(210); }else if (strcmp (s->val[option].s, "A6") == 0){ s->val[OPT_BR_X].w = SANE_FIX(105); s->val[OPT_BR_Y].w = SANE_FIX(148.5); }else if (strcmp (s->val[option].s, "B4") == 0){ s->val[OPT_BR_X].w = SANE_FIX(250); s->val[OPT_BR_Y].w = SANE_FIX(353); }else if (strcmp (s->val[option].s, "B5") == 0){ s->val[OPT_BR_X].w = SANE_FIX(182); s->val[OPT_BR_Y].w = SANE_FIX(257); }else if (strcmp (s->val[option].s, W_LETTER) == 0){ s->val[OPT_BR_X].w = SANE_FIX(279.4); s->val[OPT_BR_Y].w = SANE_FIX(431.8); }else if (strcmp (s->val[option].s, "Legal") == 0){ s->val[OPT_BR_X].w = SANE_FIX(215.9); s->val[OPT_BR_Y].w = SANE_FIX(355.6); }else if (strcmp (s->val[option].s, "Letter") == 0){ s->val[OPT_BR_X].w = SANE_FIX(215.9); s->val[OPT_BR_Y].w = SANE_FIX(279.4); }else if (strcmp (s->val[option].s, INVOICE) == 0){ s->val[OPT_BR_X].w = SANE_FIX(215.9); s->val[OPT_BR_Y].w = SANE_FIX(139.7); }else{ } return (SANE_STATUS_GOOD); #ifdef USE_RESOLUTION_LIST case OPT_RESOLUTION_LIST: if (info) *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS; #if 0 if (s->val[option].s) free (s->val[option].s); s->val[option].s = strdup (val); #endif for (i = 0; s->opt[OPT_RESOLUTION_LIST].constraint.string_list[i]; i++) { if (strcmp (val, s->opt[OPT_RESOLUTION_LIST].constraint.string_list[i]) == 0){ s->val[OPT_RESOLUTION].w = atoi(s->opt[OPT_RESOLUTION_LIST].constraint.string_list[i]); if (info) *info |= SANE_INFO_RELOAD_PARAMS; break; } } return (SANE_STATUS_GOOD); #endif #ifdef USE_CUSTOM_GAMMA /* side-effect-free word-array options: */ case OPT_GAMMA_VECTOR: case OPT_GAMMA_VECTOR_R: case OPT_GAMMA_VECTOR_G: case OPT_GAMMA_VECTOR_B: memcpy (s->val[option].wa, val, s->opt[option].size); return SANE_STATUS_GOOD; case OPT_CUSTOM_GAMMA: w = *(SANE_Word *) val; if (w == s->val[OPT_CUSTOM_GAMMA].w) return SANE_STATUS_GOOD; /* no change */ if (info) *info |= SANE_INFO_RELOAD_OPTIONS; s->val[OPT_CUSTOM_GAMMA].w = w; set_gamma_caps(s); return SANE_STATUS_GOOD; #endif } } DBG (10, ">>\n"); return (SANE_STATUS_INVAL); } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { int width, length, res; const char *mode; NEC_Scanner *s = handle; DBG (10, "<< sane_get_parameters "); res = s->val[OPT_RESOLUTION].w * s->dev->info.res_range.quant; if (!s->scanning) { /* make best-effort guess at what parameters will look like once scanning starts. */ memset (&s->params, 0, sizeof (s->params)); width = MM_TO_PIX( SANE_UNFIX(s->val[OPT_BR_X].w) - SANE_UNFIX(s->val[OPT_TL_X].w), s->dev->info.mud); length = MM_TO_PIX( SANE_UNFIX(s->val[OPT_BR_Y].w) - SANE_UNFIX(s->val[OPT_TL_Y].w), s->dev->info.mud); s->width = width; s->length = length; s->params.pixels_per_line = width * res / s->dev->info.mud; s->params.lines = length * res / s->dev->info.mud; if (s->dev->sensedat.model == PCIN500) { s->params.pixels_per_line += 1; s->params.lines += 1; } s->unscanned_lines = s->params.lines; } #if 0 else { buffer_status bs; SANE_Status status; size_t len = sizeof (buffer_status); status = get_data_buffer_status (s->fd, &bs, &len); DBG (11, "<< get_data_buffer_status "); if (status != SANE_STATUS_GOOD) { do_cancel(s); return (status); } DBG (11, ">>\n "); { #ifdef DEBUG_NEC int i; u_char *buf = &bs; DBG(11, "get data buffer status(debug):\n"); for (i = 0; i < len; i += 16) { DBG(1, "%2x %2x %2x %2x %2x %2x %2x %2x - %2x %2x %2x %2x \n", buf[i], buf[i+1], buf[i+2], buf[i+3], buf[i+4], buf[i+5], buf[i+6], buf[i+7], buf[i+8], buf[i+9], buf[i+10], buf[i+11]); } #endif } } #endif res = s->val[OPT_RESOLUTION].w * s->dev->info.res_range.quant; mode = s->val[OPT_MODE].s; if (strcmp (mode, M_LINEART) == 0) { s->params.format = SANE_FRAME_GRAY; s->params.bytes_per_line = (s->params.pixels_per_line + 7) / 8; s->params.depth = 1; s->modes = MODES_LINEART; } else if (strcmp (mode, M_GRAY) == 0) { s->params.format = SANE_FRAME_GRAY; s->params.bytes_per_line = s->params.pixels_per_line; s->params.depth = 8; s->modes = MODES_GRAY; } else if (strcmp (mode, M_LINEART_COLOR) == 0) { s->params.format = SANE_FRAME_RGB; s->params.bytes_per_line = 3 * (s->params.pixels_per_line + 7) / 8; s->params.depth = 8; s->modes = MODES_LINEART_COLOR; } else { s->params.format = SANE_FRAME_RGB; s->params.bytes_per_line = 3 * s->params.pixels_per_line; s->params.depth = 8; s->modes = MODES_COLOR; } s->params.last_frame = SANE_TRUE; if (params) *params = s->params; DBG (10, ">>\n"); return (SANE_STATUS_GOOD); } #ifdef USE_CUSTOM_GAMMA static int sprint_gamma(Option_Value val, SANE_Byte *dst) { int i; SANE_Byte *p = dst; p += sprintf((char *) p, "%i", val.wa[0]); for (i = 1; i < 256; i++) p += sprintf((char *) p, ",%i", val.wa[i] > 255 ? 255 : val.wa[i]); return p - dst; } static SANE_Status send_ascii_gamma_tables (NEC_Scanner *s) { SANE_Status status; int i; DBG(11, "<< send_ascii_gamma_tables "); /* we need: 4 bytes for each gamma value (3 digits + delimiter) + 10 bytes for the command header i.e. 4 * 4 * 256 + 10 = 4106 bytes */ if (s->dev->info.bufsize < 4106) return SANE_STATUS_NO_MEM; memset(s->buffer, 0, 4106); i = sprint_gamma(s->val[OPT_GAMMA_VECTOR_R], &s->buffer[10]); s->buffer[10+i++] = '/'; i += sprint_gamma(s->val[OPT_GAMMA_VECTOR_G], &s->buffer[10+i]); s->buffer[10+i++] = '/'; i += sprint_gamma(s->val[OPT_GAMMA_VECTOR_B], &s->buffer[10+i]); s->buffer[10+i++] = '/'; i += sprint_gamma(s->val[OPT_GAMMA_VECTOR], &s->buffer[10+i]); DBG(12, "%s\n", &s->buffer[10]); s->buffer[0] = SEND; s->buffer[2] = 0x03; s->buffer[7] = i >> 8; s->buffer[8] = i & 0xff; wait_ready(s->fd); status = sanei_scsi_cmd (s->fd, s->buffer, i+10, 0, 0); DBG(11, ">>\n"); return status; } #endif static SANE_Status send_binary_g_table(NEC_Scanner *s, SANE_Word *a, int dtq) { SANE_Status status; unsigned int i, j; (void) dtq; /* silence compilation warnings */ DBG(11, "<< send_binary_g_table\n"); i = 256; if (s->dev->info.bufsize < i) return SANE_STATUS_NO_MEM; memset(s->buffer, 0, i+10); s->buffer[0] = SEND; s->buffer[2] = 0x03; s->buffer[7] = i >> 8; s->buffer[8] = i & 0xff; for (i = 0; i < 256; i++) { s->buffer[i+11] = a[i&0xff] & 0xff; } for (j = 0; j < 256; j += 16) { DBG(11, "%02x %02x %02x %02x %02x %02x %02x %02x " "%02x %02x %02x %02x %02x %02x %02x %02x\n", a[j ], a[j+1], a[j+2], a[j+3], a[j+4], a[j+5], a[j+6], a[j+7], a[j+8], a[j+9], a[j+10], a[j+11], a[j+12], a[j+13], a[j+14], a[j+15]); } DBG(12, "transfer length = %d\n", i); DBG(12, "buffer[7] = %d\n", s->buffer[7]); DBG(12, "buffer[8] = %d\n", s->buffer[8]); /* wait_ready(s->fd); */ status = sanei_scsi_cmd (s->fd, s->buffer, i+10, 0, 0); DBG(11, ">>\n"); return status; } #ifdef USE_CUSTOM_GAMMA static SANE_Status send_binary_gamma_tables (NEC_Scanner *s) { SANE_Status status; status = send_binary_g_table(s, s->val[OPT_GAMMA_VECTOR].wa, 0x10); if (status != SANE_STATUS_GOOD) return status; DBG(11, "send_binary_gamma_tables\n"); #if 0 status = send_binary_g_table(s, s->val[OPT_GAMMA_VECTOR_R].wa, 0x11); if (status != SANE_STATUS_GOOD) return status; status = send_binary_g_table(s, s->val[OPT_GAMMA_VECTOR_G].wa, 0x12); if (status != SANE_STATUS_GOOD) return status; status = send_binary_g_table(s, s->val[OPT_GAMMA_VECTOR_B].wa, 0x13); #endif return status; } static SANE_Status send_gamma_tables (NEC_Scanner *s) { if (s->dev->sensedat.model == PCIN500) { return send_binary_gamma_tables(s); } else { return send_ascii_gamma_tables(s); } } #endif #ifdef USE_COLOR_THRESHOLD /* not used? */ #if 0 static SANE_Status send_threshold_data(NEC_Scanner *s) { SANE_Status status; SANE_Byte cmd[26] = {SEND, 0, 0x82, 0, 0, 0, 0, 0, 0, 0}; int len; memset(cmd, 0, sizeof(cmd)); /* maximum string length: 3 bytes for each number (they are restricted to the range 0..255), 3 '/' and the null-byte, total: 16 bytes. */ len = sprintf((char *) &cmd[10], "%i/%i/%i/%i", s->val[OPT_THRESHOLD_R].w, s->val[OPT_THRESHOLD_G].w, s->val[OPT_THRESHOLD_B].w, s->val[OPT_THRESHOLD].w); cmd[8] = len; wait_ready(s->fd); status = sanei_scsi_cmd(s->fd, cmd, len + 10, 0, 0); return status; } #endif #endif SANE_Status sane_start (SANE_Handle handle) { char *mode, *halftone, *gamma, *edge, *lightcolor, *adf_fsu; NEC_Scanner *s = handle; SANE_Status status; size_t buf_size; window_param wp; DBG (10, "<< sane_start "); /* First make sure we have a current parameter set. Some of the parameters will be overwritten below, but that's OK. */ status = sane_get_parameters (s, 0); if (status != SANE_STATUS_GOOD) return status; s->dev->sensedat.complain_on_adf_error = 1; #ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED s->dev->info.bufsize = s->dev->info.wanted_bufsize; if (s->dev->info.bufsize < 32 * 1024) s->dev->info.bufsize = 32 * 1024; { int bsize = s->dev->info.bufsize; status = sanei_scsi_open_extended (s->dev->sane.name, &s->fd, &sense_handler, &s->dev->sensedat, &bsize); s->dev->info.bufsize = bsize; } if (status != SANE_STATUS_GOOD) { DBG (1, "open of %s failed: %s\n", s->dev->sane.name, sane_strstatus (status)); return (status); } /* make sure that we got at least 32 kB. Even then, the scan will be awfully slow. */ if (s->dev->info.bufsize < 32 * 1024) { sanei_scsi_close(s->fd); s->fd = -1; return SANE_STATUS_NO_MEM; } #else status = sanei_scsi_open(s->dev->sane.name, &s->fd, &sense_handler, &s->dev->sensedat); if (s->dev->info.wanted_bufsize < sanei_scsi_max_request_size) s->dev->info.bufsize = s->dev->info.wanted_bufsize; else s->dev->info.bufsize = sanei_scsi_max_request_size; if (status != SANE_STATUS_GOOD) { DBG (1, "open of %s failed: %s\n", s->dev->sane.name, sane_strstatus (status)); return (status); } #endif s->buffer = malloc(s->dev->info.bufsize); if (!s->buffer) { sanei_scsi_close(s->fd); s->fd = -1; free(s); return SANE_STATUS_NO_MEM; } #ifdef USE_FORK { struct shmid_ds ds; size_t n; s->shmid = shmget(IPC_PRIVATE, sizeof(NEC_rdr_ctl) + s->dev->info.buffers * (sizeof(NEC_shmem_ctl) + s->dev->info.bufsize), IPC_CREAT | 0600); if (s->shmid == -1) { free(s->buffer); s->buffer = 0; sanei_scsi_close(s->fd); s->fd = -1; return SANE_STATUS_NO_MEM; } s->rdr_ctl = (NEC_rdr_ctl*) shmat(s->shmid, 0, 0); if ((int)s->rdr_ctl == -1) { shmctl(s->shmid, IPC_RMID, &ds); free(s->buffer); s->buffer = 0; sanei_scsi_close(s->fd); s->fd = -1; return SANE_STATUS_NO_MEM; } s->rdr_ctl->buf_ctl = (NEC_shmem_ctl*) &s->rdr_ctl[1]; for (n = 0; n < s->dev->info.buffers; n++) { s->rdr_ctl->buf_ctl[n].buffer = (SANE_Byte*) &s->rdr_ctl->buf_ctl[s->dev->info.buffers] + n * s->dev->info.bufsize; } } #endif /* USE_FORK */ DBG (5, "start: TEST_UNIT_READY\n"); status = test_unit_ready (s->fd); if (status != SANE_STATUS_GOOD) { DBG (1, "TEST UNIT READY failed: %s\n", sane_strstatus (status)); sanei_scsi_close (s->fd); s->fd = -1; return (status); } DBG (3, "start: sending MODE SELECT\n"); status = mode_select_mud (s->fd, s->dev->info.mud); if (status != SANE_STATUS_GOOD) { DBG (1, "start: MODE_SELECT6 failed\n"); sanei_scsi_close (s->fd); s->fd = -1; return (status); } mode = s->val[OPT_MODE].s; halftone = s->val[OPT_HALFTONE].s; gamma = s->val[OPT_GAMMA].s; edge = s->val[OPT_EDGE_EMPHASIS].s; lightcolor = s->val[OPT_LIGHTCOLOR].s; adf_fsu = s->val[OPT_SCANSOURCE].s; if (s->val[OPT_PREVIEW].w == SANE_FALSE) { s->res = s->val[OPT_RESOLUTION].w * s->dev->info.res_range.quant; } else { s->res = 75; } s->ulx = MM_TO_PIX(SANE_UNFIX(s->val[OPT_TL_X].w), s->dev->info.mud); s->uly = MM_TO_PIX(SANE_UNFIX(s->val[OPT_TL_Y].w), s->dev->info.mud); s->threshold = s->val[OPT_THRESHOLD].w; if (strcmp (mode, M_LINEART_COLOR) == 0) s->bpp = 1; else s->bpp = s->params.depth; s->adf_fsu_mode = SCAN_SIMPLE; /* default: scan without ADF and FSU */ if (strcmp(adf_fsu, use_fsu) == 0) s->adf_fsu_mode = SCAN_WITH_FSU; else if (strcmp(adf_fsu, use_adf) == 0) s->adf_fsu_mode = SCAN_WITH_ADF; else if (strcmp(adf_fsu, use_adf) == 0) s->adf_fsu_mode = SCAN_SIMPLE; /* halftone must not set to be zero PC-IN500 */ s->halftone = 0x01; if (strcmp (mode, M_LINEART) == 0) { s->reverse = 0; s->image_composition = 1; if (strcmp(halftone, M_BILEVEL) == 0) { s->image_composition = 0; s->halftone = 0x01; } else if (strcmp(halftone, M_DITHER1) == 0) s->halftone = 0x01; else if (strcmp(halftone, M_DITHER2) == 0) s->halftone = 0x10; else if (strcmp(halftone, M_DITHER3) == 0) s->halftone = 0x20; } else if (strcmp (mode, M_GRAY) == 0) { s->image_composition = 2; s->reverse = 1; } else if (strcmp (mode, M_LINEART_COLOR) == 0) { s->reverse = 1; s->image_composition = 4; if (strcmp(halftone, M_BILEVEL) == 0) { s->image_composition = 3; s->halftone = 0x01; } else if (strcmp(halftone, M_DITHER1) == 0) s->halftone = 0x01; else if (strcmp(halftone, M_DITHER2) == 0) s->halftone = 0x10; else if (strcmp(halftone, M_DITHER3) == 0) s->halftone = 0x20; } else if (strcmp (mode, M_COLOR) == 0) { s->image_composition = 5; s->reverse = 1; } if (s->dev->sensedat.model == PCIN500) { s->or = s->val[OPT_OR].w; s->nr = s->val[OPT_NR].w; s->edge = s->val[OPT_EDGE].w; } else { if (strcmp (edge, EDGE_NONE) == 0) s->edge = 0; else if (strcmp (edge, EDGE_MIDDLE) == 0) s->edge = 1; else if (strcmp (edge, EDGE_STRONG) == 0) s->edge = 2; else if (strcmp (edge, EDGE_BLUR) == 0) s->edge = 3; } s->lightcolor = 3; if (strcmp(lightcolor, LIGHT_GREEN) == 0) s->lightcolor = 0; else if (strcmp(lightcolor, LIGHT_RED) == 0) s->lightcolor = 1; else if (strcmp(lightcolor, LIGHT_BLUE) == 0) s->lightcolor = 2; else if (strcmp(lightcolor, LIGHT_NONE) == 0) s->lightcolor = 3; s->adf_scan = 0; #ifdef USE_CUSTOM_GAMMA if (s->val[OPT_CUSTOM_GAMMA].w == SANE_FALSE) { #endif if (s->dev->sensedat.model == PCIN500) { if (strcmp (gamma, CRT1)) s->gamma = 1; else if (strcmp (gamma, CRT2)) s->gamma = 2; else if (strcmp (gamma, PRINTER1)) s->gamma = 3; else if (strcmp (gamma, PRINTER2)) s->gamma = 4; else if (strcmp (gamma, NONE)) s->gamma = 5; } #if 0 if (s->dev->sensedat.model != PCIN500) { ss.dtc = 0x03; if (strcmp (gamma, GAMMA10) == 0) ss.dtq = 0x01; else ss.dtq = 0x02; ss.length = 0; DBG (5, "start: SEND\n"); status = send (s->fd, &ss); if (status != SANE_STATUS_GOOD) { DBG (1, "send failed: %s\n", sane_strstatus (status)); sanei_scsi_close (s->fd); s->fd = -1; return (status); } } else #else { int i; SANE_Word gtbl[256]; #if 0 if (strcmp (gamma, GAMMA10) == 0) for (i = 0; i < 256; i++) gtbl[i] = i; else #endif { gtbl[0] = 0; for (i = 1; i < 256; i++) gtbl[i] = 255 * exp(0.45 * log(i/255.0)); } send_binary_g_table(s, gtbl, 0x10); send_binary_g_table(s, gtbl, 0x11); send_binary_g_table(s, gtbl, 0x12); send_binary_g_table(s, gtbl, 0x13); } #endif /* DEBUG_NEC */ #ifdef USE_CUSTOM_GAMMA } else { s->gamma = 9; status = send_gamma_tables(s); } if (status != SANE_STATUS_GOOD) { sanei_scsi_close (s->fd); s->fd = -1; return (status); } #endif s->tint = s->val[OPT_TINT].w; s->color = s->val[OPT_COLOR].w; memset (&wp, 0, sizeof (wp)); /* every NEC scanner seems to have a different window descriptor block... */ if (s->dev->sensedat.model == PCIN500) buf_size = sizeof(WDB) + sizeof(WDBX500); else buf_size = sizeof(WDB); wp.wpdh.wdl[0] = buf_size >> 8; wp.wpdh.wdl[1] = buf_size; wp.wdb.x_res[0] = s->res >> 8; wp.wdb.x_res[1] = s->res; wp.wdb.y_res[0] = s->res >> 8; wp.wdb.y_res[1] = s->res; wp.wdb.x_ul[0] = s->ulx >> 24; wp.wdb.x_ul[1] = s->ulx >> 16; wp.wdb.x_ul[2] = s->ulx >> 8; wp.wdb.x_ul[3] = s->ulx; wp.wdb.y_ul[0] = s->uly >> 24; wp.wdb.y_ul[1] = s->uly >> 16; wp.wdb.y_ul[2] = s->uly >> 8; wp.wdb.y_ul[3] = s->uly; wp.wdb.width[0] = s->width >> 24; wp.wdb.width[1] = s->width >> 16; wp.wdb.width[2] = s->width >> 8; wp.wdb.width[3] = s->width; wp.wdb.length[0] = s->length >> 24; wp.wdb.length[1] = s->length >> 16; wp.wdb.length[2] = s->length >> 8; wp.wdb.length[3] = s->length; wp.wdb.brightness = 0; wp.wdb.threshold = s->threshold; wp.wdb.brightness = 128; wp.wdb.contrast = 128; wp.wdb.image_composition = s->image_composition; if (s->image_composition <= 2 || s->image_composition >= 5) wp.wdb.bpp = s->bpp; else wp.wdb.bpp = 1; wp.wdb.ht_pattern[0] = 0; wp.wdb.ht_pattern[1] = 0; if (s->dev->sensedat.model == PCIN500) wp.wdb.ht_pattern[1] = s->halftone; wp.wdb.rif_padding = (s->reverse * 128) + 0; if (s->dev->sensedat.model == PCIN500) { wp.wdbx500.data_length = 0x07; wp.wdbx500.control = 0 | (s->or << 7) | (s->edge << 6) | (s->nr << 5) | (s->lightcolor << 2); wp.wdbx500.format = 0; wp.wdbx500.gamma = s->gamma; wp.wdbx500.tint = s->tint; wp.wdbx500.color = s->color; wp.wdbx500.reserved1 = 0; wp.wdbx500.reserved2 = 0; } DBG (5, "wdl=%d\n", (wp.wpdh.wdl[0] << 8) + wp.wpdh.wdl[1]); DBG (5, "xres=%d\n", (wp.wdb.x_res[0] << 8) + wp.wdb.x_res[1]); DBG (5, "yres=%d\n", (wp.wdb.y_res[0] << 8) + wp.wdb.y_res[1]); DBG (5, "ulx=%d\n", (wp.wdb.x_ul[0] << 24) + (wp.wdb.x_ul[1] << 16) + (wp.wdb.x_ul[2] << 8) + wp.wdb.x_ul[3]); DBG (5, "uly=%d\n", (wp.wdb.y_ul[0] << 24) + (wp.wdb.y_ul[1] << 16) + (wp.wdb.y_ul[2] << 8) + wp.wdb.y_ul[3]); DBG (5, "width=%d\n", (wp.wdb.width[0] << 8) + (wp.wdb.width[1] << 16) + (wp.wdb.width[2] << 8) + wp.wdb.width[3]); DBG (5, "length=%d\n", (wp.wdb.length[0] << 16) + (wp.wdb.length[1] << 16) + (wp.wdb.length[2] << 8) + wp.wdb.length[3]); DBG (5, "threshold=%d\n", wp.wdb.threshold); DBG (5, "image_composition=%d\n", wp.wdb.image_composition); DBG (5, "bpp=%d\n", wp.wdb.bpp); DBG (5, "rif_padding=%d\n", wp.wdb.rif_padding); #ifdef DEBUG_NEC { window_param foo; size_t len = buf_size; len += sizeof(WPDH); DBG (5, "start: GET WINDOW\n"); status = get_window (s->fd, &foo, &len); if (status != SANE_STATUS_GOOD) { DBG (1, "GET WINDOW failed: %s\n", sane_strstatus (status)); sanei_scsi_close (s->fd); s->fd = -1; return (status); } #if 1 { unsigned char *p = (unsigned char*) &foo; int i; DBG(11, "get window(debug):\n"); for (i = 0; i < len; i += 16) { DBG(1, "%2x %2x %2x %2x %2x %2x %2x %2x - %2x %2x %2x %2x %2x %2x %2x %2x\n", p[i], p[i+1], p[i+2], p[i+3], p[i+4], p[i+5], p[i+6], p[i+7], p[i+8], p[i+9], p[i+10], p[i+11], p[i+12], p[i+13], p[i+14], p[i+15]); } } #endif foo.wpdh.wpdh[1] = 0; status = set_window (s->fd, &foo, len); if (status != SANE_STATUS_GOOD) { DBG (1, "SET WINDOW failed: %s\n", sane_strstatus (status)); sanei_scsi_close (s->fd); s->fd = -1; return (status); } #if 1 { unsigned char *p = (unsigned char*) &foo; int i; DBG(11, "set window(debug):\n"); for (i = 0; i < len; i += 16) { DBG(1, "%2x %2x %2x %2x %2x %2x %2x %2x - %2x %2x %2x %2x %2x %2x %2x %2x\n", p[i], p[i+1], p[i+2], p[i+3], p[i+4], p[i+5], p[i+6], p[i+7], p[i+8], p[i+9], p[i+10], p[i+11], p[i+12], p[i+13], p[i+14], p[i+15]); } } #endif } #endif /* debug_nec */ #ifdef DEBUG_NEC { unsigned char *p = (unsigned char*) ℘ int i; DBG(11, "set window(debug):\n"); for (i = 0; i < sizeof(wp.wdb) + sizeof(wp.wdbx500); i += 16) { DBG(1, "%2x %2x %2x %2x %2x %2x %2x %2x - %2x %2x %2x %2x %2x %2x %2x %2x\n", p[i], p[i+1], p[i+2], p[i+3], p[i+4], p[i+5], p[i+6], p[i+7], p[i+8], p[i+9], p[i+10], p[i+11], p[i+12], p[i+13], p[i+14], p[i+15]); } } #endif /* debug_nec */ buf_size += sizeof(WPDH); DBG (5, "start: SET WINDOW\n"); status = set_window (s->fd, &wp, buf_size); if (status != SANE_STATUS_GOOD) { DBG (1, "SET WINDOW failed: %s\n", sane_strstatus (status)); sanei_scsi_close (s->fd); s->fd = -1; return (status); } memset (&wp, 0, buf_size); DBG (5, "start: GET WINDOW\n"); status = get_window (s->fd, &wp, &buf_size); if (status != SANE_STATUS_GOOD) { DBG (1, "GET WINDOW failed: %s\n", sane_strstatus (status)); sanei_scsi_close (s->fd); s->fd = -1; return (status); } #ifdef DEBUG_NEC { unsigned char *p = (unsigned char*) ℘ int i; DBG(11, "get window(debug):\n"); for (i = 0; i < buf_size; i += 16) { DBG(1, "%2x %2x %2x %2x %2x %2x %2x %2x - %2x %2x %2x %2x %2x %2x %2x %2x\n", p[i], p[i+1], p[i+2], p[i+3], p[i+4], p[i+5], p[i+6], p[i+7], p[i+8], p[i+9], p[i+10], p[i+11], p[i+12], p[i+13], p[i+14], p[i+15]); } } #endif DBG (5, "xres=%d\n", (wp.wdb.x_res[0] << 8) + wp.wdb.x_res[1]); DBG (5, "yres=%d\n", (wp.wdb.y_res[0] << 8) + wp.wdb.y_res[1]); DBG (5, "ulx=%d\n", (wp.wdb.x_ul[0] << 24) + (wp.wdb.x_ul[1] << 16) + (wp.wdb.x_ul[2] << 8) + wp.wdb.x_ul[3]); DBG (5, "uly=%d\n", (wp.wdb.y_ul[0] << 24) + (wp.wdb.y_ul[1] << 16) + (wp.wdb.y_ul[2] << 8) + wp.wdb.y_ul[3]); DBG (5, "width=%d\n", (wp.wdb.width[0] << 24) + (wp.wdb.width[1] << 16) + (wp.wdb.width[2] << 8) + wp.wdb.width[3]); DBG (5, "length=%d\n", (wp.wdb.length[0] << 24) + (wp.wdb.length[1] << 16) + (wp.wdb.length[2] << 8) + wp.wdb.length[3]); DBG (5, "start: SCAN\n"); s->scanning = SANE_TRUE; s->busy = SANE_TRUE; s->cancel = SANE_FALSE; s->get_params_called = 0; status = scan (s->fd); #ifdef DEBUG { struct timeval t; gettimeofday(&t, 0); DBG(2, "rd: scan started %li.%06li\n", t.tv_sec, t.tv_usec); } #endif if (status != SANE_STATUS_GOOD) { DBG (1, "start of scan failed: %s\n", sane_strstatus (status)); sanei_scsi_close (s->fd); s->fd = -1; s->busy = SANE_FALSE; s->cancel = SANE_FALSE; return (status); } /* ask the scanner for the scan size */ /* wait_ready(s->fd); */ #ifdef DEBUG { struct timeval t; gettimeofday(&t, 0); DBG(2, "rd: wait_ready ok %li.%06li\n", t.tv_sec, t.tv_usec); } #endif sane_get_parameters(s, 0); #ifdef DEBUG { struct timeval t; gettimeofday(&t, 0); DBG(2, "rd: get_params ok %li.%06li\n", t.tv_sec, t.tv_usec); } #endif if (strcmp (mode, M_LINEART_COLOR) != 0) s->bytes_to_read = s->params.bytes_per_line * s->params.lines; else { s->bytes_to_read = s->params.bytes_per_line * s->params.lines; } #ifdef USE_FORK { size_t i; for (i = 0; i < s->dev->info.buffers; i++) s->rdr_ctl->buf_ctl[i].shm_status = SHM_EMPTY; s->read_buff = 0; s->rdr_ctl->cancel = 0; s->rdr_ctl->running = 0; s->rdr_ctl->status = SANE_STATUS_GOOD; } s->reader_pid = fork(); #ifdef DEBUG { struct timeval t; gettimeofday(&t, 0); DBG(2, "rd: forked %li.%06li %i\n", t.tv_sec, t.tv_usec, s->reader_pid); } #endif if (s->reader_pid == 0) { sigset_t ignore_set; struct SIGACTION act; sigfillset (&ignore_set); sigdelset (&ignore_set, SIGTERM); sigprocmask (SIG_SETMASK, &ignore_set, 0); memset (&act, 0, sizeof (act)); sigaction (SIGTERM, &act, 0); /* don't use exit() since that would run the atexit() handlers... */ _exit (reader_process (s)); } else if (s->reader_pid == -1) { s->busy = SANE_FALSE; do_cancel(s); return SANE_STATUS_NO_MEM; } #endif /* USE_FORK */ DBG (1, "%d pixels per line, %d bytes, %d lines high, total %lu bytes, " "dpi=%d\n", s->params.pixels_per_line, s->params.bytes_per_line, s->params.lines, (u_long) s->bytes_to_read, s->val[OPT_RESOLUTION].w); s->busy = SANE_FALSE; s->buf_used = 0; s->buf_pos = 0; if (s->cancel == SANE_TRUE) { do_cancel(s); DBG (10, ">>\n"); return(SANE_STATUS_CANCELLED); } DBG (10, ">>\n"); return (SANE_STATUS_GOOD); } static SANE_Status sane_read_direct (SANE_Handle handle, SANE_Byte *dst_buf, SANE_Int max_len, SANE_Int * len) { NEC_Scanner *s = handle; SANE_Status status; size_t nread; DBG (10, "<< sane_read_direct "); #if 0 { buffer_status bs; size_t len = sizeof (buffer_status); get_data_buffer_status (s->fd, &bs, &len); DBG (20, "buffer_status: %i ", bs.fdb[0]*256*256 + bs.fdb[1]*256 + bs.fdb[2]); } #endif DBG (20, "remaining: %lu ", (u_long) s->bytes_to_read); *len = 0; if (s->bytes_to_read == 0) { do_cancel (s); return (SANE_STATUS_EOF); } if (!s->scanning) return (do_cancel (s)); nread = max_len; if (nread > s->bytes_to_read) nread = s->bytes_to_read; if (nread > s->dev->info.bufsize) nread = s->dev->info.bufsize; #ifdef USE_FORK status = read_data(s, dst_buf, &nread); #else #ifdef NOTUSE_PCIN500 wait_ready(s->fd); #endif status = read_data (s, dst_buf, &nread); #endif if (status != SANE_STATUS_GOOD) { do_cancel (s); return (SANE_STATUS_IO_ERROR); } *len = nread; s->bytes_to_read -= nread; DBG (20, "remaining: %lu ", (u_long) s->bytes_to_read); DBG (10, ">>\n"); return (SANE_STATUS_GOOD); } static SANE_Status sane_read_shuffled (SANE_Handle handle, SANE_Byte *dst_buf, SANE_Int max_len, SANE_Int * len, int eight_bit_data) { NEC_Scanner *s = handle; SANE_Status status; SANE_Byte *dest, *red, *green, *blue, mask; SANE_Int transfer; size_t nread, ntest, pixel, max_pixel, line, max_line; size_t start_input, bytes_per_line_in; DBG (10, "<< sane_read_shuffled "); #if 0 { buffer_status bs; size_t len = sizeof (buffer_status); get_data_buffer_status (s->fd, &bs, &len); DBG (20, "buffer_status: %i ", bs.fdb[0]*256*256 + bs.fdb[1]*256 + bs.fdb[2]); } #endif *len = 0; if (s->bytes_to_read == 0 && s->buf_pos == s->buf_used) { do_cancel (s); DBG (10, ">>\n"); return (SANE_STATUS_EOF); } if (!s->scanning) { DBG (10, ">>\n"); return(do_cancel(s)); } if (s->buf_pos < s->buf_used) { transfer = s->buf_used - s->buf_pos; if (transfer > max_len) transfer = max_len; memcpy(dst_buf, &(s->buffer[s->buf_pos]), transfer); s->buf_pos += transfer; max_len -= transfer; *len = transfer; } while (max_len > 0 && s->bytes_to_read > 0) { if (eight_bit_data) { nread = s->dev->info.bufsize / s->params.bytes_per_line - 1; nread *= s->params.bytes_per_line; if (nread > s->bytes_to_read) nread = s->bytes_to_read; max_line = nread / s->params.bytes_per_line; start_input = s->params.bytes_per_line; bytes_per_line_in = s->params.bytes_per_line; } else { bytes_per_line_in = (s->params.pixels_per_line + 7) / 8; bytes_per_line_in *= 3; max_line = s->params.bytes_per_line + bytes_per_line_in; max_line = s->dev->info.bufsize / max_line; nread = max_line * bytes_per_line_in; if (nread > s->bytes_to_read) { nread = s->bytes_to_read; max_line = nread / bytes_per_line_in; } start_input = s->dev->info.bufsize - nread; } ntest = nread; #ifdef USE_FORK status = read_data (s, &(s->buffer[start_input]), &nread); #else status = read_data (s, &(s->buffer[start_input]), &nread); #endif if (status != SANE_STATUS_GOOD) { do_cancel (s); DBG (10, ">>\n"); return (SANE_STATUS_IO_ERROR); } if (nread != ntest) { /* if this happens, something is wrong in the input buffer management... */ DBG(1, "Warning: could not read an integral number of scan lines\n"); DBG(1, " image will be scrambled\n"); } s->buf_used = max_line * s->params.bytes_per_line; s->buf_pos = 0; s->bytes_to_read -= nread; dest = s->buffer; max_pixel = s->params.pixels_per_line; if (eight_bit_data) for (line = 1; line <= max_line; line++) { red = &(s->buffer[line * s->params.bytes_per_line]); green = &(red[max_pixel]); blue = &(green[max_pixel]); for (pixel = 0; pixel < max_pixel; pixel++) { *dest++ = *red++; *dest++ = *green++; *dest++ = *blue++; } } else for (line = 0; line < max_line; line++) { red = &(s->buffer[start_input + line * bytes_per_line_in]); green = &(red[(max_pixel+7)/8]); blue = &(green[(max_pixel+7)/8]); mask = 0x80; for (pixel = 0; pixel < max_pixel; pixel++) { *dest++ = (*red & mask) ? 0xff : 0; *dest++ = (*green & mask) ? 0xff : 0; *dest++ = (*blue & mask) ? 0xff : 0; mask = mask >> 1; if (mask == 0) { mask = 0x80; red++; green++; blue++; } } } transfer = max_len; if (transfer > s->buf_used) transfer = s->buf_used; memcpy(&(dst_buf[*len]), s->buffer, transfer); max_len -= transfer; s->buf_pos += transfer; *len += transfer; } if (s->bytes_to_read == 0 && s->buf_pos == s->buf_used) do_cancel (s); DBG (10, ">>\n"); return (SANE_STATUS_GOOD); } SANE_Status sane_read (SANE_Handle handle, SANE_Byte *dst_buf, SANE_Int max_len, SANE_Int * len) { NEC_Scanner *s = handle; SANE_Status status; DBG (10, "<< sane_read "); s->busy = SANE_TRUE; if (s->cancel == SANE_TRUE) { do_cancel(s); *len = 0; return (SANE_STATUS_CANCELLED); } if (s->image_composition <= 2) status = sane_read_direct(handle, dst_buf, max_len, len); else if (s->image_composition <= 4) status = sane_read_shuffled(handle, dst_buf, max_len, len, 0); else if (s->dev->sensedat.model == PCIN500) status = sane_read_direct(handle, dst_buf, max_len, len); else status = sane_read_shuffled(handle, dst_buf, max_len, len, 1); s->busy = SANE_FALSE; if (s->cancel == SANE_TRUE) { do_cancel(s); return (SANE_STATUS_CANCELLED); } DBG (10, ">> \n"); return (status); } void sane_cancel (SANE_Handle handle) { NEC_Scanner *s = handle; DBG (10, "<< sane_cancel "); s->cancel = SANE_TRUE; if (s->busy == SANE_FALSE) do_cancel(s); DBG (10, ">>\n"); } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { (void) handle; (void) non_blocking; /* silence compilation warnings */ DBG (10, "<< sane_set_io_mode"); DBG (10, ">>\n"); return SANE_STATUS_UNSUPPORTED; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { (void) handle; (void) fd; /* silence compilation warnings */ DBG (10, "<< sane_get_select_fd"); DBG (10, ">>\n"); return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/nec.conf.in000066400000000000000000000000151456256263500167070ustar00rootroot00000000000000/dev/scanner backends-1.3.0/backend/nec.h000066400000000000000000000261771456256263500156250ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2000 Kazuya Fukuda 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, see . 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 nec_h #define nec_h 1 #include /* default values for configurable options. Though these options are only meaningful if USE_FORK is defined, they are DEFAULT_BUFFERS: number of buffers allocated as shared memory for the data transfer from reader_process to read_data. The minimum value is 2 DEFAULT_BUFSIZE: default size of one buffer. Must be greater than zero. DEFAULT_QUEUED_READS: number of read requests queued by sanei_scsi_req_enter. Since queued read requests are currently only supported for Linux and DomainOS, this value should automatically be set dependent on the target OS... For Linux, 2 is the optimum; for DomainOS, I don't have any recommendation; other OS should use the value zero. The value for DEFAULT_BUFSIZE is probably too Linux-oriented... */ #define DEFAULT_BUFFERS 12 #define DEFAULT_BUFSIZE 128 * 1024 #define DEFAULT_QUEUED_READS 2 #define NEC_MAJOR 0 #define NEC_MINOR 12 typedef enum { OPT_NUM_OPTS = 0, OPT_MODE_GROUP, OPT_MODE, OPT_HALFTONE, OPT_PAPER, OPT_SCANSOURCE, OPT_GAMMA, #ifdef USE_CUSTOM_GAMMA OPT_CUSTOM_GAMMA, #endif OPT_RESOLUTION_GROUP, #ifdef USE_RESOLUTION_LIST OPT_RESOLUTION_LIST, #endif OPT_RESOLUTION, OPT_GEOMETRY_GROUP, OPT_TL_X, /* top-left x */ OPT_TL_Y, /* top-left y */ OPT_BR_X, /* bottom-right x */ OPT_BR_Y, /* bottom-right y */ OPT_ENHANCEMENT_GROUP, OPT_EDGE_EMPHASIS, OPT_OR, OPT_NR, OPT_EDGE, OPT_THRESHOLD, #ifdef USE_COLOR_THRESHOLD OPT_THRESHOLD_R, OPT_THRESHOLD_G, OPT_THRESHOLD_B, #endif OPT_LIGHTCOLOR, OPT_TINT, OPT_COLOR, OPT_PREVIEW, #ifdef USE_CUSTOM_GAMMA OPT_GAMMA_VECTOR, OPT_GAMMA_VECTOR_R, OPT_GAMMA_VECTOR_G, OPT_GAMMA_VECTOR_B, #endif /* must come last: */ NUM_OPTIONS } NEC_Option; #ifdef USE_FORK /* status defines for a buffer: buffer not used / read request queued / buffer contains data */ #define SHM_EMPTY 0 #define SHM_BUSY 1 #define SHM_FULL 2 typedef struct NEC_shmem_ctl { int shm_status; /* can be SHM_EMPTY, SHM_BUSY, SHM_FULL */ size_t used; /* number of bytes successfully read from scanner */ size_t nreq; /* number of bytes requested from scanner */ size_t start; /* index of the begin of used area of the buffer */ void *qid; SANE_Byte *buffer; } NEC_shmem_ctl; typedef struct NEC_rdr_ctl { int cancel; /* 1 = flag for the reader process to cancel */ int running; /* 1 indicates that the reader process is alive */ SANE_Status status; /* return status of the reader process */ NEC_shmem_ctl *buf_ctl; } NEC_rdr_ctl; #endif /* USE_FORK */ typedef enum { /* PCIN500, PCINXXX are used as array indices, so the corresponding numbers should start at 0 */ unknown = -1, PCIN500, PCINXXX } NEC_Model; typedef struct NEC_Info { SANE_Range res_range; SANE_Range tl_x_ranges[3]; /* normal / FSU / ADF */ SANE_Range br_x_ranges[3]; /* normal / FSU / ADF */ SANE_Range tl_y_ranges[3]; /* normal / FSU / ADF */ SANE_Range br_y_ranges[3]; /* normal / FSU / ADF */ SANE_Range threshold_range; SANE_Range tint_range; SANE_Range color_range; SANE_Int res_default; SANE_Int x_default; SANE_Int y_default; SANE_Int bmu; SANE_Int mud; SANE_Int adf_fsu_installed; SANE_String_Const scansources[5]; size_t buffers; size_t bufsize; int wanted_bufsize; size_t queued_reads; } NEC_Info; typedef struct NEC_Sense_Data { NEC_Model model; /* flag, if conditions like "paper jam" or "cover open" are considered as an error. Should be 0 for attach, else a frontend might refuse to start, if the scanner returns these errors. */ int complain_on_adf_error; /* Linux returns only 16 bytes of sense data... */ u_char sb[16]; } NEC_Sense_Data; typedef struct NEC_Device { struct NEC_Device *next; SANE_Device sane; NEC_Info info; /* xxx now part of sense data NEC_Model model; */ NEC_Sense_Data sensedat; } NEC_Device; typedef struct NEC_New_Device { struct NEC_Device *dev; struct NEC_New_Device *next; } NEC_New_Device; typedef struct NEC_Scanner { struct NEC_Scanner *next; int fd; NEC_Device *dev; SANE_Option_Descriptor opt[NUM_OPTIONS]; Option_Value val[NUM_OPTIONS]; SANE_Parameters params; int get_params_called; SANE_Byte *buffer; /* for color data re-ordering */ SANE_Int buf_used; SANE_Int buf_pos; SANE_Int modes; SANE_Int res; SANE_Int ulx; SANE_Int uly; SANE_Int width; SANE_Int length; SANE_Int threshold; SANE_Int image_composition; SANE_Int bpp; SANE_Int halftone; SANE_Bool reverse; SANE_Bool or; SANE_Bool nr; SANE_Int gamma; SANE_Int edge; SANE_Int lightcolor; SANE_Int adf_fsu_mode; /* mode selected by user */ SANE_Int adf_scan; /* flag, if the actual scan is an ADF scan */ SANE_Int tint; SANE_Int color; size_t bytes_to_read; size_t max_lines_to_read; size_t unscanned_lines; SANE_Bool scanning; SANE_Bool busy; SANE_Bool cancel; #ifdef USE_CUSTOM_GAMMA SANE_Int gamma_table[4][256]; #endif #ifdef USE_FORK pid_t reader_pid; NEC_rdr_ctl *rdr_ctl; int shmid; size_t read_buff; /* index of the buffer actually used by read_data */ #endif /* USE_FORK */ } NEC_Scanner; typedef struct NEC_Send { SANE_Int dtc; SANE_Int dtq; SANE_Int length; SANE_Byte *data; } NEC_Send; typedef struct WPDH { u_char wpdh[6]; u_char wdl[2]; } WPDH; typedef struct WDB { SANE_Byte wid; SANE_Byte autobit; SANE_Byte x_res[2]; SANE_Byte y_res[2]; SANE_Byte x_ul[4]; SANE_Byte y_ul[4]; SANE_Byte width[4]; SANE_Byte length[4]; SANE_Byte brightness; SANE_Byte threshold; SANE_Byte contrast; SANE_Byte image_composition; SANE_Byte bpp; SANE_Byte ht_pattern[2]; SANE_Byte rif_padding; SANE_Byte bit_ordering[2]; SANE_Byte compression_type; SANE_Byte compression_argument; SANE_Byte reserved[6]; } WDB; /* "extension" of the window descriptor block for the PC-IN500 */ typedef struct XWDBX500 { SANE_Byte data_length; SANE_Byte control; SANE_Byte format; SANE_Byte gamma; SANE_Byte tint; SANE_Byte color; SANE_Byte reserved1; SANE_Byte reserved2; } WDBX500; typedef struct window_param { WPDH wpdh; WDB wdb; WDBX500 wdbx500; } window_param; typedef struct mode_sense_param { SANE_Byte mode_data_length; SANE_Byte mode_param_header2; SANE_Byte mode_param_header3; SANE_Byte mode_desciptor_length; SANE_Byte page_code; SANE_Byte page_length; /* 6 */ SANE_Byte bmu; SANE_Byte res2; SANE_Byte mud[2]; SANE_Byte res3; SANE_Byte res4; } mode_sense_param; typedef struct mode_sense_subdevice { SANE_Byte mode_data_length; SANE_Byte mode_param_header2; SANE_Byte mode_param_header3; SANE_Byte mode_desciptor_length; SANE_Byte res1[5]; SANE_Byte blocklength[3]; SANE_Byte page_code; SANE_Byte page_length; /* 0x1a */ SANE_Byte a_mode_type; SANE_Byte f_mode_type; SANE_Byte res2; SANE_Byte max_x[4]; SANE_Byte max_y[4]; SANE_Byte res3[2]; SANE_Byte x_basic_resolution[2]; SANE_Byte y_basic_resolution[2]; SANE_Byte x_max_resolution[2]; SANE_Byte y_max_resolution[2]; SANE_Byte x_min_resolution[2]; SANE_Byte y_min_resolution[2]; SANE_Byte res4; } mode_sense_subdevice; typedef struct mode_select_param { SANE_Byte mode_param_header1; SANE_Byte mode_param_header2; SANE_Byte mode_param_header3; SANE_Byte mode_param_header4; SANE_Byte page_code; SANE_Byte page_length; /* 6 */ SANE_Byte res1; SANE_Byte res2; SANE_Byte mud[2]; SANE_Byte res3; SANE_Byte res4; } mode_select_param; typedef struct mode_select_subdevice { SANE_Byte mode_param_header1; SANE_Byte mode_param_header2; SANE_Byte mode_param_header3; SANE_Byte mode_param_header4; SANE_Byte page_code; SANE_Byte page_length; /* 0x1A */ SANE_Byte a_mode; SANE_Byte f_mode; SANE_Byte res[24]; } mode_select_subdevice; typedef struct buffer_status { SANE_Byte data_length[3]; SANE_Byte block; SANE_Byte window_id; SANE_Byte reserved; SANE_Byte bsa[3]; /* buffer space available */ SANE_Byte fdb[3]; /* filled data buffer */ } buffer_status; /* SCSI commands */ #define TEST_UNIT_READY 0x00 #define REQUEST_SENSE 0x03 #define INQUIRY 0x12 #define MODE_SELECT6 0x15 #define RESERVE_UNIT 0x16 #define RELEASE_UNIT 0x17 #define MODE_SENSE6 0x1a #define SCAN 0x1b #define SEND_DIAGNOSTIC 0x1d #define SET_WINDOW 0x24 #define GET_WINDOW 0x25 #define READ 0x28 #define SEND 0x2a #define GET_DATA_BUFFER_STATUS 0x34 #define SENSE_LEN 18 #define INQUIRY_LEN 36 #define MODEPARAM_LEN 12 #define MODE_SUBDEV_LEN 32 #define WINDOW_LEN 76 #define BUFFERSTATUS_LEN 12 #endif /* not nec_h */ backends-1.3.0/backend/net.c000066400000000000000000001765001456256263500156350ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1997 David Mosberger-Tang Copyright (C) 2003, 2008 Julien BLACHE AF-independent code + IPv6, Avahi support 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, see . 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 network-based meta backend. */ #ifdef _AIX # include "../include/lalloca.h" /* MUST come first for AIX! */ #endif #include "../include/sane/config.h" #include "../include/lalloca.h" #include "../include/_stdint.h" #include #include #include #include #include #include #include #ifdef HAVE_LIBC_H # include /* NeXTStep/OpenStep */ #endif #include #include #include #include /* OS/2 needs this _after_ , grrr... */ #if WITH_AVAHI # include # include # include # include # include # define SANED_SERVICE_DNS "_sane-port._tcp" static AvahiClient *avahi_client = NULL; static AvahiThreadedPoll *avahi_thread = NULL; static AvahiServiceBrowser *avahi_browser = NULL; #endif /* WITH_AVAHI */ #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/sanei_net.h" #include "../include/sane/sanei_codec_bin.h" #include "net.h" #define BACKEND_NAME net #include "../include/sane/sanei_backend.h" #ifndef PATH_MAX # define PATH_MAX 1024 #endif #include "../include/sane/sanei_config.h" #define NET_CONFIG_FILE "net.conf" /* Please increase version number with every change (don't forget to update net.desc) */ /* define the version string depending on which network code is used */ #if defined (HAVE_GETADDRINFO) && defined (HAVE_GETNAMEINFO) # define NET_USES_AF_INDEP # ifdef ENABLE_IPV6 # define NET_VERSION "1.0.14 (AF-indep+IPv6)" # else # define NET_VERSION "1.0.14 (AF-indep)" # endif /* ENABLE_IPV6 */ #else # undef ENABLE_IPV6 # define NET_VERSION "1.0.14" #endif /* HAVE_GETADDRINFO && HAVE_GETNAMEINFO */ static SANE_Auth_Callback auth_callback; static Net_Device *first_device; static Net_Scanner *first_handle; static const SANE_Device **devlist; static int client_big_endian; /* 1 == big endian; 0 == little endian */ static int server_big_endian; /* 1 == big endian; 0 == little endian */ static int depth; /* bits per pixel */ static int connect_timeout = -1; /* timeout for connection to saned */ #ifndef NET_USES_AF_INDEP static int saned_port; #endif /* !NET_USES_AF_INDEP */ /* This variable is only needed, if the depth is 16bit/channel and client/server have different endianness. A value of -1 means, that there's no hang over; otherwise the value has to be casted to SANE_Byte. hang_over means, that there is a remaining byte from a previous call to sane_read, which could not be byte-swapped, e.g. because the frontend requested an odd number of bytes. */ static int hang_over; /* This variable is only needed, if the depth is 16bit/channel and client/server have different endianness. A value of -1 means, that there's no left over; otherwise the value has to be casted to SANE_Byte. left_over means, that there is a remaining byte from a previous call to sane_read, which already is in the correct byte order, but could not be returned, e.g. because the frontend requested only one byte per call. */ static int left_over; #ifdef NET_USES_AF_INDEP static SANE_Status add_device (const char *name, Net_Device ** ndp) { struct addrinfo hints; struct addrinfo *res; struct addrinfo *resp; struct sockaddr_in *sin; #ifdef ENABLE_IPV6 struct sockaddr_in6 *sin6; #endif /* ENABLE_IPV6 */ Net_Device *nd = NULL; int error; short sane_port = htons (6566); DBG (1, "add_device: adding backend %s\n", name); for (nd = first_device; nd; nd = nd->next) if (strcmp (nd->name, name) == 0) { DBG (1, "add_device: already in list\n"); if (ndp) *ndp = nd; return SANE_STATUS_GOOD; } memset (&hints, 0, sizeof(hints)); # ifdef ENABLE_IPV6 hints.ai_family = PF_UNSPEC; # else hints.ai_family = PF_INET; # endif /* ENABLE_IPV6 */ error = getaddrinfo (name, "sane-port", &hints, &res); if (error) { error = getaddrinfo (name, NULL, &hints, &res); if (error) { DBG (1, "add_device: error while getting address of host %s: %s\n", name, gai_strerror (error)); return SANE_STATUS_IO_ERROR; } else { for (resp = res; resp != NULL; resp = resp->ai_next) { switch (resp->ai_family) { case AF_INET: sin = (struct sockaddr_in *) resp->ai_addr; sin->sin_port = sane_port; break; #ifdef ENABLE_IPV6 case AF_INET6: sin6 = (struct sockaddr_in6 *) resp->ai_addr; sin6->sin6_port = sane_port; break; #endif /* ENABLE_IPV6 */ } } } } nd = malloc (sizeof (Net_Device)); if (!nd) { DBG (1, "add_device: not enough memory for Net_Device struct\n"); freeaddrinfo (res); return SANE_STATUS_NO_MEM; } memset (nd, 0, sizeof (Net_Device)); nd->name = strdup (name); if (!nd->name) { DBG (1, "add_device: not enough memory to duplicate name\n"); free(nd); return SANE_STATUS_NO_MEM; } nd->addr = res; nd->ctl = -1; nd->next = first_device; first_device = nd; if (ndp) *ndp = nd; DBG (2, "add_device: backend %s added\n", name); return SANE_STATUS_GOOD; } #else /* !NET_USES_AF_INDEP */ static SANE_Status add_device (const char *name, Net_Device ** ndp) { struct hostent *he; Net_Device *nd; struct sockaddr_in *sin; DBG (1, "add_device: adding backend %s\n", name); for (nd = first_device; nd; nd = nd->next) if (strcmp (nd->name, name) == 0) { DBG (1, "add_device: already in list\n"); if (ndp) *ndp = nd; return SANE_STATUS_GOOD; } he = gethostbyname (name); if (!he) { DBG (1, "add_device: can't get address of host %s\n", name); return SANE_STATUS_IO_ERROR; } if (he->h_addrtype != AF_INET) { DBG (1, "add_device: don't know how to deal with addr family %d\n", he->h_addrtype); return SANE_STATUS_INVAL; } nd = malloc (sizeof (*nd)); if (!nd) { DBG (1, "add_device: not enough memory for Net_Device struct\n"); return SANE_STATUS_NO_MEM; } memset (nd, 0, sizeof (*nd)); nd->name = strdup (name); if (!nd->name) { DBG (1, "add_device: not enough memory to duplicate name\n"); free (nd); return SANE_STATUS_NO_MEM; } nd->addr.sa_family = he->h_addrtype; sin = (struct sockaddr_in *) &nd->addr; memcpy (&sin->sin_addr, he->h_addr_list[0], he->h_length); nd->ctl = -1; nd->next = first_device; first_device = nd; if (ndp) *ndp = nd; DBG (2, "add_device: backend %s added\n", name); return SANE_STATUS_GOOD; } #endif /* NET_USES_AF_INDEP */ /* Calls getpwuid_r(). The return value must be freed by the caller. */ char* get_current_username() { long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); if (bufsize == -1) { return NULL; } char* buf = (char*) malloc(bufsize); if (buf == NULL) { return NULL; } struct passwd pwd; struct passwd *result; if (getpwuid_r(getuid(), &pwd, buf, bufsize, &result) != 0 || result == NULL) { return NULL; } /* pw_name is allocated somewhere within buf, so we use memmove() */ memmove(buf, pwd.pw_name, strlen(pwd.pw_name)); return buf; } #ifdef NET_USES_AF_INDEP static SANE_Status connect_dev (Net_Device * dev) { struct addrinfo *addrp; SANE_Word version_code; SANE_Init_Reply reply; SANE_Status status = SANE_STATUS_IO_ERROR; SANE_Init_Req req; SANE_Bool connected = SANE_FALSE; #ifdef TCP_NODELAY int on = 1; int level = -1; #endif struct timeval tv; int i; DBG (2, "connect_dev: trying to connect to %s\n", dev->name); for (addrp = dev->addr, i = 0; (addrp != NULL) && (connected == SANE_FALSE); addrp = addrp->ai_next, i++) { # ifdef ENABLE_IPV6 if ((addrp->ai_family != AF_INET) && (addrp->ai_family != AF_INET6)) # else /* !ENABLE_IPV6 */ if (addrp->ai_family != AF_INET) # endif /* ENABLE_IPV6 */ { DBG (1, "connect_dev: [%d] don't know how to deal with addr family %d\n", i, addrp->ai_family); continue; } dev->ctl = socket (addrp->ai_family, SOCK_STREAM, 0); if (dev->ctl < 0) { DBG (1, "connect_dev: [%d] failed to obtain socket (%s)\n", i, strerror (errno)); dev->ctl = -1; continue; } /* Set SO_SNDTIMEO for the connection to saned */ if (connect_timeout > 0) { tv.tv_sec = connect_timeout; tv.tv_usec = 0; if (setsockopt (dev->ctl, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) { DBG (1, "connect_dev: [%d] failed to set SO_SNDTIMEO (%s)\n", i, strerror (errno)); } } if (connect (dev->ctl, addrp->ai_addr, addrp->ai_addrlen) < 0) { DBG (1, "connect_dev: [%d] failed to connect (%s)\n", i, strerror (errno)); dev->ctl = -1; continue; } DBG (3, "connect_dev: [%d] connection succeeded (%s)\n", i, (addrp->ai_family == AF_INET6) ? "IPv6" : "IPv4"); dev->addr_used = addrp; connected = SANE_TRUE; } if (connected != SANE_TRUE) { DBG (1, "connect_dev: couldn't connect to host (see messages above)\n"); return SANE_STATUS_IO_ERROR; } #else /* !NET_USES_AF_INDEP */ static SANE_Status connect_dev (Net_Device * dev) { struct sockaddr_in *sin; SANE_Word version_code; SANE_Init_Reply reply; SANE_Status status = SANE_STATUS_IO_ERROR; SANE_Init_Req req; #ifdef TCP_NODELAY int on = 1; int level = -1; #endif struct timeval tv; DBG (2, "connect_dev: trying to connect to %s\n", dev->name); if (dev->addr.sa_family != AF_INET) { DBG (1, "connect_dev: don't know how to deal with addr family %d\n", dev->addr.sa_family); return SANE_STATUS_IO_ERROR; } dev->ctl = socket (dev->addr.sa_family, SOCK_STREAM, 0); if (dev->ctl < 0) { DBG (1, "connect_dev: failed to obtain socket (%s)\n", strerror (errno)); dev->ctl = -1; return SANE_STATUS_IO_ERROR; } sin = (struct sockaddr_in *) &dev->addr; sin->sin_port = saned_port; /* Set SO_SNDTIMEO for the connection to saned */ if (connect_timeout > 0) { tv.tv_sec = connect_timeout; tv.tv_usec = 0; if (setsockopt (dev->ctl, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) { DBG (1, "connect_dev: failed to set SO_SNDTIMEO (%s)\n", strerror (errno)); } } if (connect (dev->ctl, &dev->addr, sizeof (dev->addr)) < 0) { DBG (1, "connect_dev: failed to connect (%s)\n", strerror (errno)); dev->ctl = -1; return SANE_STATUS_IO_ERROR; } DBG (3, "connect_dev: connection succeeded\n"); #endif /* NET_USES_AF_INDEP */ /* We're connected now, so reset SO_SNDTIMEO to the default value of 0 */ if (connect_timeout > 0) { tv.tv_sec = 0; tv.tv_usec = 0; if (setsockopt (dev->ctl, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) { DBG (1, "connect_dev: failed to reset SO_SNDTIMEO (%s)\n", strerror (errno)); } } #ifdef TCP_NODELAY # ifdef SOL_TCP level = SOL_TCP; # else /* !SOL_TCP */ /* Look up the protocol level in the protocols database. */ { struct protoent *p; p = getprotobyname ("tcp"); if (p == 0) DBG (1, "connect_dev: cannot look up `tcp' protocol number"); else level = p->p_proto; } # endif /* SOL_TCP */ if (level == -1 || setsockopt (dev->ctl, level, TCP_NODELAY, &on, sizeof (on))) DBG (1, "connect_dev: failed to put send socket in TCP_NODELAY mode (%s)", strerror (errno)); #endif /* !TCP_NODELAY */ DBG (2, "connect_dev: sanei_w_init\n"); sanei_w_init (&dev->wire, sanei_codec_bin_init); dev->wire.io.fd = dev->ctl; dev->wire.io.read = read; dev->wire.io.write = write; /* exchange version codes with the server: */ req.version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, SANEI_NET_PROTOCOL_VERSION); req.username = get_current_username(); DBG (2, "connect_dev: net_init (user=%s, local version=%d.%d.%d)\n", req.username, V_MAJOR, V_MINOR, SANEI_NET_PROTOCOL_VERSION); sanei_w_call (&dev->wire, SANE_NET_INIT, (WireCodecFunc) sanei_w_init_req, &req, (WireCodecFunc) sanei_w_init_reply, &reply); free(req.username); req.username = NULL; if (dev->wire.status != 0) { DBG (1, "connect_dev: argument marshalling error (%s)\n", strerror (dev->wire.status)); status = SANE_STATUS_IO_ERROR; goto fail; } status = reply.status; version_code = reply.version_code; DBG (2, "connect_dev: freeing init reply (status=%s, remote " "version=%d.%d.%d)\n", sane_strstatus (status), SANE_VERSION_MAJOR (version_code), SANE_VERSION_MINOR (version_code), SANE_VERSION_BUILD (version_code)); sanei_w_free (&dev->wire, (WireCodecFunc) sanei_w_init_reply, &reply); if (status != 0) { DBG (1, "connect_dev: access to %s denied\n", dev->name); goto fail; } if (SANE_VERSION_MAJOR (version_code) != V_MAJOR) { DBG (1, "connect_dev: major version mismatch: got %d, expected %d\n", SANE_VERSION_MAJOR (version_code), V_MAJOR); status = SANE_STATUS_IO_ERROR; goto fail; } if (SANE_VERSION_BUILD (version_code) != SANEI_NET_PROTOCOL_VERSION && SANE_VERSION_BUILD (version_code) != 2) { DBG (1, "connect_dev: network protocol version mismatch: " "got %d, expected %d\n", SANE_VERSION_BUILD (version_code), SANEI_NET_PROTOCOL_VERSION); status = SANE_STATUS_IO_ERROR; goto fail; } dev->wire.version = SANE_VERSION_BUILD (version_code); DBG (4, "connect_dev: done\n"); return SANE_STATUS_GOOD; fail: DBG (2, "connect_dev: closing connection to %s\n", dev->name); close (dev->ctl); dev->ctl = -1; return status; } static SANE_Status fetch_options (Net_Scanner * s) { int option_number; DBG (3, "fetch_options: %p\n", (void *) s); if (s->opt.num_options) { DBG (2, "fetch_options: %d option descriptors cached... freeing\n", s->opt.num_options); sanei_w_set_dir (&s->hw->wire, WIRE_FREE); s->hw->wire.status = 0; sanei_w_option_descriptor_array (&s->hw->wire, &s->opt); if (s->hw->wire.status) { DBG (1, "fetch_options: failed to free old list (%s)\n", strerror (s->hw->wire.status)); return SANE_STATUS_IO_ERROR; } } DBG (3, "fetch_options: get_option_descriptors\n"); sanei_w_call (&s->hw->wire, SANE_NET_GET_OPTION_DESCRIPTORS, (WireCodecFunc) sanei_w_word, &s->handle, (WireCodecFunc) sanei_w_option_descriptor_array, &s->opt); if (s->hw->wire.status) { DBG (1, "fetch_options: failed to get option descriptors (%s)\n", strerror (s->hw->wire.status)); return SANE_STATUS_IO_ERROR; } if (s->local_opt.num_options == 0) { DBG (3, "fetch_options: creating %d local option descriptors\n", s->opt.num_options); s->local_opt.desc = malloc (s->opt.num_options * sizeof (s->local_opt.desc)); if (!s->local_opt.desc) { DBG (1, "fetch_options: couldn't malloc s->local_opt.desc\n"); return SANE_STATUS_NO_MEM; } for (option_number = 0; option_number < s->opt.num_options; option_number++) { s->local_opt.desc[option_number] = malloc (sizeof (SANE_Option_Descriptor)); if (!s->local_opt.desc[option_number]) { DBG (1, "fetch_options: couldn't malloc " "s->local_opt.desc[%d]\n", option_number); return SANE_STATUS_NO_MEM; } } s->local_opt.num_options = s->opt.num_options; } else if (s->local_opt.num_options != s->opt.num_options) { DBG (1, "fetch_options: option number count changed during runtime?\n"); return SANE_STATUS_INVAL; } DBG (3, "fetch_options: copying %d option descriptors\n", s->opt.num_options); for (option_number = 0; option_number < s->opt.num_options; option_number++) { memcpy (s->local_opt.desc[option_number], s->opt.desc[option_number], sizeof (SANE_Option_Descriptor)); } s->options_valid = 1; DBG (3, "fetch_options: %d options fetched\n", s->opt.num_options); return SANE_STATUS_GOOD; } static SANE_Status do_cancel (Net_Scanner * s) { DBG (2, "do_cancel: %p\n", (void *) s); s->hw->auth_active = 0; if (s->data >= 0) { DBG (3, "do_cancel: closing data pipe\n"); close (s->data); s->data = -1; } return SANE_STATUS_CANCELLED; } static void do_authorization (Net_Device * dev, SANE_String resource) { SANE_Authorization_Req req; SANE_Char username[SANE_MAX_USERNAME_LEN]; SANE_Char password[SANE_MAX_PASSWORD_LEN]; char *net_resource; DBG (2, "do_authorization: dev=%p resource=%s\n", (void *) dev, resource); dev->auth_active = 1; memset (&req, 0, sizeof (req)); memset (username, 0, sizeof (SANE_Char) * SANE_MAX_USERNAME_LEN); memset (password, 0, sizeof (SANE_Char) * SANE_MAX_PASSWORD_LEN); net_resource = malloc (strlen (resource) + 6 + strlen (dev->name)); if (net_resource != NULL) { sprintf (net_resource, "net:%s:%s", dev->name, resource); if (auth_callback) { DBG (2, "do_authorization: invoking auth_callback, resource = %s\n", net_resource); (*auth_callback) (net_resource, username, password); } else DBG (1, "do_authorization: no auth_callback present\n"); free (net_resource); } else /* Is this necessary? If we don't have these few bytes we will get in trouble later anyway */ { DBG (1, "do_authorization: not enough memory for net_resource\n"); if (auth_callback) { DBG (2, "do_authorization: invoking auth_callback, resource = %s\n", resource); (*auth_callback) (resource, username, password); } else DBG (1, "do_authorization: no auth_callback present\n"); } if (dev->auth_active) { SANE_Word ack; req.resource = resource; req.username = username; req.password = password; DBG (2, "do_authorization: relaying authentication data\n"); sanei_w_call (&dev->wire, SANE_NET_AUTHORIZE, (WireCodecFunc) sanei_w_authorization_req, &req, (WireCodecFunc) sanei_w_word, &ack); } else DBG (1, "do_authorization: auth_active is false... strange\n"); } #if WITH_AVAHI static void net_avahi_resolve_callback (AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void *userdata) { char a[AVAHI_ADDRESS_STR_MAX]; char *t; /* unused */ (void) interface; (void) protocol; (void) userdata; if (!r) return; switch (event) { case AVAHI_RESOLVER_FAILURE: DBG (1, "net_avahi_resolve_callback: failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, avahi_strerror (avahi_client_errno (avahi_service_resolver_get_client (r)))); break; case AVAHI_RESOLVER_FOUND: DBG (3, "net_avahi_resolve_callback: service '%s' of type '%s' in domain '%s':\n", name, type, domain); avahi_address_snprint(a, sizeof (a), address); t = avahi_string_list_to_string (txt); DBG (3, "\t%s:%u (%s)\n\tTXT=%s\n\tcookie is %u\n\tis_local: %i\n\tour_own: %i\n" "\twide_area: %i\n\tmulticast: %i\n\tcached: %i\n", host_name, port, a, t, avahi_string_list_get_service_cookie (txt), !!(flags & AVAHI_LOOKUP_RESULT_LOCAL), !!(flags & AVAHI_LOOKUP_RESULT_OUR_OWN), !!(flags & AVAHI_LOOKUP_RESULT_WIDE_AREA), !!(flags & AVAHI_LOOKUP_RESULT_MULTICAST), !!(flags & AVAHI_LOOKUP_RESULT_CACHED)); /* TODO: evaluate TXT record */ /* Try first with the name */ if (add_device (host_name, NULL) != SANE_STATUS_GOOD) { DBG (1, "net_avahi_resolve_callback: couldn't add backend with name %s\n", host_name); /* Then try the raw IP address */ if (add_device (t, NULL) != SANE_STATUS_GOOD) DBG (1, "net_avahi_resolve_callback: couldn't add backend with IP address %s either\n", t); } avahi_free (t); break; } avahi_service_resolver_free(r); } static void net_avahi_browse_callback (AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags flags, void *userdata) { AvahiProtocol proto; /* unused */ (void) flags; (void) userdata; if (!b) return; switch (event) { case AVAHI_BROWSER_FAILURE: DBG (1, "net_avahi_browse_callback: %s\n", avahi_strerror (avahi_client_errno (avahi_service_browser_get_client (b)))); avahi_threaded_poll_quit (avahi_thread); return; case AVAHI_BROWSER_NEW: DBG (3, "net_avahi_browse_callback: NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain); /* The server will actually be added to our list in the resolver callback */ /* The resolver object will be freed in the resolver callback, or by * the server if it terminates before the callback is called. */ #ifdef ENABLE_IPV6 proto = AVAHI_PROTO_UNSPEC; #else proto = AVAHI_PROTO_INET; #endif /* ENABLE_IPV6 */ if (!(avahi_service_resolver_new (avahi_client, interface, protocol, name, type, domain, proto, 0, net_avahi_resolve_callback, NULL))) DBG (2, "net_avahi_browse_callback: failed to resolve service '%s': %s\n", name, avahi_strerror (avahi_client_errno (avahi_client))); break; case AVAHI_BROWSER_REMOVE: DBG (3, "net_avahi_browse_callback: REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain); /* With the current architecture, we cannot safely remove a server from the list */ break; case AVAHI_BROWSER_ALL_FOR_NOW: case AVAHI_BROWSER_CACHE_EXHAUSTED: DBG (3, "net_avahi_browse_callback: %s\n", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW"); break; } } static void net_avahi_callback (AvahiClient *c, AvahiClientState state, void * userdata) { AvahiProtocol proto; int error; /* unused */ (void) userdata; if (!c) return; switch (state) { case AVAHI_CLIENT_CONNECTING: break; case AVAHI_CLIENT_S_COLLISION: case AVAHI_CLIENT_S_REGISTERING: case AVAHI_CLIENT_S_RUNNING: if (avahi_browser) return; #ifdef ENABLE_IPV6 proto = AVAHI_PROTO_UNSPEC; #else proto = AVAHI_PROTO_INET; #endif /* ENABLE_IPV6 */ avahi_browser = avahi_service_browser_new (c, AVAHI_IF_UNSPEC, proto, SANED_SERVICE_DNS, NULL, 0, net_avahi_browse_callback, NULL); if (avahi_browser == NULL) { DBG (1, "net_avahi_callback: could not create service browser: %s\n", avahi_strerror (avahi_client_errno (c))); avahi_threaded_poll_quit (avahi_thread); } break; case AVAHI_CLIENT_FAILURE: error = avahi_client_errno (c); if (error == AVAHI_ERR_DISCONNECTED) { /* Server disappeared - try to reconnect */ if (avahi_browser) { avahi_service_browser_free (avahi_browser); avahi_browser = NULL; } avahi_client_free (avahi_client); avahi_client = NULL; avahi_client = avahi_client_new (avahi_threaded_poll_get (avahi_thread), AVAHI_CLIENT_NO_FAIL, net_avahi_callback, NULL, &error); if (avahi_client == NULL) { DBG (1, "net_avahi_init: could not create Avahi client: %s\n", avahi_strerror (error)); avahi_threaded_poll_quit (avahi_thread); } } else { /* Another error happened - game over */ DBG (1, "net_avahi_callback: server connection failure: %s\n", avahi_strerror (error)); avahi_threaded_poll_quit (avahi_thread); } break; } } static void net_avahi_init (void) { int error; avahi_thread = avahi_threaded_poll_new (); if (avahi_thread == NULL) { DBG (1, "net_avahi_init: could not create threaded poll object\n"); goto fail; } avahi_client = avahi_client_new (avahi_threaded_poll_get (avahi_thread), AVAHI_CLIENT_NO_FAIL, net_avahi_callback, NULL, &error); if (avahi_client == NULL) { DBG (1, "net_avahi_init: could not create Avahi client: %s\n", avahi_strerror (error)); goto fail; } if (avahi_threaded_poll_start (avahi_thread) < 0) { DBG (1, "net_avahi_init: Avahi thread failed to start\n"); goto fail; } /* All done */ return; fail: DBG (1, "net_avahi_init: Avahi init failed, support disabled\n"); if (avahi_client) { avahi_client_free (avahi_client); avahi_client = NULL; } if (avahi_thread) { avahi_threaded_poll_free (avahi_thread); avahi_thread = NULL; } } static void net_avahi_cleanup (void) { if (!avahi_thread) return; DBG (1, "net_avahi_cleanup: stopping thread\n"); avahi_threaded_poll_stop (avahi_thread); if (avahi_browser) avahi_service_browser_free (avahi_browser); if (avahi_client) avahi_client_free (avahi_client); avahi_threaded_poll_free (avahi_thread); DBG (1, "net_avahi_cleanup: done\n"); } #endif /* WITH_AVAHI */ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) { char device_name[PATH_MAX]; const char *optval; const char *env; size_t len; FILE *fp; short ns = 0x1234; unsigned char *p = (unsigned char *)(&ns); #ifndef NET_USES_AF_INDEP struct servent *serv; #endif /* !NET_USES_AF_INDEP */ DBG_INIT (); DBG (2, "sane_init: authorize %s null, version_code %s null\n", (authorize) ? "!=" : "==", (version_code) ? "!=" : "=="); devlist = NULL; first_device = NULL; first_handle = NULL; #if WITH_AVAHI net_avahi_init (); #endif /* WITH_AVAHI */ auth_callback = authorize; /* Return the version number of the sane-backends package to allow the frontend to print them. This is done only for net and dll, because these backends are usually called by the frontend. */ if (version_code) *version_code = SANE_VERSION_CODE (SANE_DLL_V_MAJOR, SANE_DLL_V_MINOR, SANE_DLL_V_BUILD); DBG (1, "sane_init: SANE net backend version %s from %s\n", NET_VERSION, PACKAGE_STRING); /* determine (client) machine byte order */ if (*p == 0x12) { client_big_endian = 1; DBG (3, "sane_init: Client has big endian byte order\n"); } else { client_big_endian = 0; DBG (3, "sane_init: Client has little endian byte order\n"); } #ifndef NET_USES_AF_INDEP DBG (2, "sane_init: determining sane service port\n"); serv = getservbyname ("sane-port", "tcp"); if (serv) { DBG (2, "sane_init: found port %d\n", ntohs (serv->s_port)); saned_port = serv->s_port; } else { saned_port = htons (6566); DBG (1, "sane_init: could not find `sane-port' service (%s); using default " "port %d\n", strerror (errno), ntohs (saned_port)); } #endif /* !NET_USES_AF_INDEP */ DBG (2, "sane_init: searching for config file\n"); fp = sanei_config_open (NET_CONFIG_FILE); if (fp) { while (sanei_config_read (device_name, sizeof (device_name), fp)) { if (device_name[0] == '#') /* ignore line comments */ continue; len = strlen (device_name); if (!len) continue; /* ignore empty lines */ /* * Check for net backend options. * Anything that isn't an option is a saned host. */ if (strstr(device_name, "connect_timeout") != NULL) { /* Look for the = sign; if it's not there, error out */ optval = strchr(device_name, '='); if (!optval) continue; optval = sanei_config_skip_whitespace (++optval); if ((optval != NULL) && (*optval != '\0')) { connect_timeout = atoi(optval); DBG (2, "sane_init: connect timeout set to %d seconds\n", connect_timeout); } continue; } #if WITH_AVAHI avahi_threaded_poll_lock (avahi_thread); #endif /* WITH_AVAHI */ DBG (2, "sane_init: trying to add %s\n", device_name); add_device (device_name, 0); #if WITH_AVAHI avahi_threaded_poll_unlock (avahi_thread); #endif /* WITH_AVAHI */ } fclose (fp); DBG (2, "sane_init: done reading config\n"); } else DBG (1, "sane_init: could not open config file (%s): %s\n", NET_CONFIG_FILE, strerror (errno)); DBG (2, "sane_init: evaluating environment variable SANE_NET_HOSTS\n"); env = getenv ("SANE_NET_HOSTS"); if (env) { char *copy, *next, *host; if ((copy = strdup (env)) != NULL) { next = copy; while ((host = strsep (&next, ":"))) { #ifdef ENABLE_IPV6 if (host[0] == '[') { /* skip '[' (host[0]) */ host++; /* get the rest of the IPv6 addr (we're screwed if ] is missing) * Is it worth checking for the matching ] ? Not for now. */ strsep (&next, "]"); /* add back the ":" that got removed by the strsep() */ host[strlen (host)] = ':'; /* host now holds the IPv6 address */ /* skip the ':' that could be after ] (avoids a call to strsep() */ if (next[0] == ':') next++; } /* * if the IPv6 is last in the list, the strsep() call in the while() * will return a string with the first char being '\0'. Skip it. */ if (host[0] == '\0') continue; #endif /* ENABLE_IPV6 */ #if WITH_AVAHI avahi_threaded_poll_lock (avahi_thread); #endif /* WITH_AVAHI */ DBG (2, "sane_init: trying to add %s\n", host); add_device (host, 0); #if WITH_AVAHI avahi_threaded_poll_unlock (avahi_thread); #endif /* WITH_AVAHI */ } free (copy); } else DBG (1, "sane_init: not enough memory to duplicate " "environment variable\n"); } DBG (2, "sane_init: evaluating environment variable SANE_NET_TIMEOUT\n"); env = getenv ("SANE_NET_TIMEOUT"); if (env) { connect_timeout = atoi(env); DBG (2, "sane_init: connect timeout set to %d seconds from env\n", connect_timeout); } DBG (2, "sane_init: done\n"); return SANE_STATUS_GOOD; } void sane_exit (void) { Net_Scanner *handle, *next_handle; Net_Device *dev, *next_device; int i; DBG (1, "sane_exit: exiting\n"); #if WITH_AVAHI net_avahi_cleanup (); #endif /* WITH_AVAHI */ /* first, close all handles: */ for (handle = first_handle; handle; handle = next_handle) { next_handle = handle->next; sane_close (handle); } first_handle = 0; /* now close all devices: */ for (dev = first_device; dev; dev = next_device) { next_device = dev->next; DBG (2, "sane_exit: closing dev %p, ctl=%d\n", (void *) dev, dev->ctl); if (dev->ctl >= 0) { sanei_w_call (&dev->wire, SANE_NET_EXIT, (WireCodecFunc) sanei_w_void, 0, (WireCodecFunc) sanei_w_void, 0); sanei_w_exit (&dev->wire); close (dev->ctl); } if (dev->name) free ((void *) dev->name); #ifdef NET_USES_AF_INDEP if (dev->addr) freeaddrinfo(dev->addr); #endif /* NET_USES_AF_INDEP */ free (dev); } if (devlist) { for (i = 0; devlist[i]; ++i) { if (devlist[i]->vendor) free ((void *) devlist[i]->vendor); if (devlist[i]->model) free ((void *) devlist[i]->model); if (devlist[i]->type) free ((void *) devlist[i]->type); free ((void *) devlist[i]); } free (devlist); } DBG (3, "sane_exit: finished.\n"); } /* Note that a call to get_devices() implies that we'll have to connect to all remote hosts. To avoid this, you can call sane_open() directly (assuming you know the name of the backend/device). This is appropriate for the command-line interface of SANE, for example. */ SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { static int devlist_size = 0, devlist_len = 0; static const SANE_Device *empty_devlist[1] = { 0 }; SANE_Get_Devices_Reply reply; SANE_Status status; Net_Device *dev; char *full_name; int i, num_devs; size_t len; #define ASSERT_SPACE(n) do \ { \ if (devlist_len + (n) > devlist_size) \ { \ devlist_size += (n) + 15; \ if (devlist) \ devlist = realloc (devlist, devlist_size * sizeof (devlist[0])); \ else \ devlist = malloc (devlist_size * sizeof (devlist[0])); \ if (!devlist) \ { \ DBG (1, "sane_get_devices: not enough memory\n"); \ return SANE_STATUS_NO_MEM; \ } \ } \ } while (0) DBG (3, "sane_get_devices: local_only = %d\n", local_only); if (local_only) { *device_list = empty_devlist; return SANE_STATUS_GOOD; } if (devlist) { DBG (2, "sane_get_devices: freeing devlist\n"); for (i = 0; devlist[i]; ++i) { if (devlist[i]->vendor) free ((void *) devlist[i]->vendor); if (devlist[i]->model) free ((void *) devlist[i]->model); if (devlist[i]->type) free ((void *) devlist[i]->type); free ((void *) devlist[i]); } free (devlist); devlist = 0; } devlist_len = 0; devlist_size = 0; for (dev = first_device; dev; dev = dev->next) { if (dev->ctl < 0) { status = connect_dev (dev); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_get_devices: ignoring failure to connect to %s\n", dev->name); continue; } } sanei_w_call (&dev->wire, SANE_NET_GET_DEVICES, (WireCodecFunc) sanei_w_void, 0, (WireCodecFunc) sanei_w_get_devices_reply, &reply); if (reply.status != SANE_STATUS_GOOD) { DBG (1, "sane_get_devices: ignoring rpc-returned status %s\n", sane_strstatus (reply.status)); sanei_w_free (&dev->wire, (WireCodecFunc) sanei_w_get_devices_reply, &reply); continue; } /* count the number of devices for this backend: */ for (num_devs = 0; reply.device_list[num_devs]; ++num_devs); ASSERT_SPACE (num_devs); for (i = 0; i < num_devs; ++i) { SANE_Device *rdev; char *mem; #ifdef ENABLE_IPV6 SANE_Bool IPv6 = SANE_FALSE; #endif /* ENABLE_IPV6 */ /* create a new device entry with a device name that is the sum of the backend name a colon and the backend's device name: */ len = strlen (dev->name) + 1 + strlen (reply.device_list[i]->name); #ifdef ENABLE_IPV6 if (strchr (dev->name, ':') != NULL) { len += 2; IPv6 = SANE_TRUE; } #endif /* ENABLE_IPV6 */ mem = malloc (sizeof (*dev) + len + 1); if (!mem) { DBG (1, "sane_get_devices: not enough free memory\n"); sanei_w_free (&dev->wire, (WireCodecFunc) sanei_w_get_devices_reply, &reply); return SANE_STATUS_NO_MEM; } memset (mem, 0, sizeof (*dev) + len); full_name = mem + sizeof (*dev); #ifdef ENABLE_IPV6 if (IPv6 == SANE_TRUE) strcat (full_name, "["); #endif /* ENABLE_IPV6 */ strcat (full_name, dev->name); #ifdef ENABLE_IPV6 if (IPv6 == SANE_TRUE) strcat (full_name, "]"); #endif /* ENABLE_IPV6 */ strcat (full_name, ":"); strcat (full_name, reply.device_list[i]->name); DBG (3, "sane_get_devices: got %s\n", full_name); rdev = (SANE_Device *) mem; rdev->name = full_name; rdev->vendor = strdup (reply.device_list[i]->vendor); rdev->model = strdup (reply.device_list[i]->model); rdev->type = strdup (reply.device_list[i]->type); if ((!rdev->vendor) || (!rdev->model) || (!rdev->type)) { DBG (1, "sane_get_devices: not enough free memory\n"); if (rdev->vendor) free ((void *) rdev->vendor); if (rdev->model) free ((void *) rdev->model); if (rdev->type) free ((void *) rdev->type); free (rdev); sanei_w_free (&dev->wire, (WireCodecFunc) sanei_w_get_devices_reply, &reply); return SANE_STATUS_NO_MEM; } devlist[devlist_len++] = rdev; } /* now free up the rpc return value: */ sanei_w_free (&dev->wire, (WireCodecFunc) sanei_w_get_devices_reply, &reply); } /* terminate device list with NULL entry: */ ASSERT_SPACE (1); devlist[devlist_len++] = 0; *device_list = devlist; DBG (2, "sane_get_devices: finished (%d devices)\n", devlist_len - 1); return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const full_name, SANE_Handle * meta_handle) { SANE_Open_Reply reply; const char *dev_name; #ifdef ENABLE_IPV6 const char *tmp_name; SANE_Bool v6addr = SANE_FALSE; #endif /* ENABLE_IPV6 */ SANE_String nd_name; SANE_Status status; SANE_Word handle; SANE_Word ack; Net_Device *dev; Net_Scanner *s; int need_auth; DBG (3, "sane_open(\"%s\")\n", full_name); #ifdef ENABLE_IPV6 /* * Check whether a numerical IPv6 host was specified * [2001:42:42::12] <== check for '[' as full_name[0] * ex: [2001:42:42::12]:test:0 (syntax taken from Apache 2) */ if (full_name[0] == '[') { v6addr = SANE_TRUE; tmp_name = strchr (full_name, ']'); if (!tmp_name) { DBG (1, "sane_open: incorrect host address: missing matching ']'\n"); return SANE_STATUS_INVAL; } } else tmp_name = full_name; dev_name = strchr (tmp_name, ':'); #else /* !ENABLE_IPV6 */ dev_name = strchr (full_name, ':'); #endif /* ENABLE_IPV6 */ if (dev_name) { #ifdef strndupa # ifdef ENABLE_IPV6 if (v6addr == SANE_TRUE) nd_name = strndupa (full_name + 1, dev_name - full_name - 2); else nd_name = strndupa (full_name, dev_name - full_name); # else /* !ENABLE_IPV6 */ nd_name = strndupa (full_name, dev_name - full_name); # endif /* ENABLE_IPV6 */ if (!nd_name) { DBG (1, "sane_open: not enough free memory\n"); return SANE_STATUS_NO_MEM; } #else char *tmp; # ifdef ENABLE_IPV6 if (v6addr == SANE_TRUE) tmp = alloca (dev_name - full_name - 2 + 1); else tmp = alloca (dev_name - full_name + 1); # else /* !ENABLE_IPV6 */ tmp = alloca (dev_name - full_name + 1); # endif /* ENABLE_IPV6 */ if (!tmp) { DBG (1, "sane_open: not enough free memory\n"); return SANE_STATUS_NO_MEM; } # ifdef ENABLE_IPV6 if (v6addr == SANE_TRUE) { memcpy (tmp, full_name + 1, dev_name - full_name - 2); tmp[dev_name - full_name - 2] = '\0'; } else { memcpy (tmp, full_name, dev_name - full_name); tmp[dev_name - full_name] = '\0'; } # else /* !ENABLE_IPV6 */ memcpy (tmp, full_name, dev_name - full_name); tmp[dev_name - full_name] = '\0'; # endif /* ENABLE_IPV6 */ nd_name = tmp; #endif ++dev_name; /* skip colon */ } else { /* if no colon interpret full_name as the host name; an empty device name will cause us to open the first device of that host. */ #ifdef ENABLE_IPV6 if (v6addr == SANE_TRUE) { nd_name = alloca (strlen (full_name) - 2 + 1); if (!nd_name) { DBG (1, "sane_open: not enough free memory\n"); return SANE_STATUS_NO_MEM; } memcpy (nd_name, full_name + 1, strlen (full_name) - 2); nd_name[strlen (full_name) - 2] = '\0'; } else nd_name = (char *) full_name; #else /* !ENABLE_IPV6 */ nd_name = (char *) full_name; #endif /* ENABLE_IPV6 */ dev_name = ""; } DBG (2, "sane_open: host = %s, device = %s\n", nd_name, dev_name); if (!nd_name[0]) { /* Unlike other backends, we never allow an empty backend-name. Otherwise, it's possible that sane_open("") will result in endless looping (consider the case where NET is the first backend...) */ DBG (1, "sane_open: empty backend name is not allowed\n"); return SANE_STATUS_INVAL; } else for (dev = first_device; dev; dev = dev->next) if (strcmp (dev->name, nd_name) == 0) break; if (!dev) { DBG (1, "sane_open: device %s not found, trying to register it anyway\n", nd_name); #if WITH_AVAHI avahi_threaded_poll_lock (avahi_thread); #endif /* WITH_AVAHI */ status = add_device (nd_name, &dev); #if WITH_AVAHI avahi_threaded_poll_unlock (avahi_thread); #endif /* WITH_AVAHI */ if (status != SANE_STATUS_GOOD) { DBG (1, "sane_open: could not open device\n"); return status; } } else DBG (2, "sane_open: device found in list\n"); if (dev->ctl < 0) { DBG (2, "sane_open: device not connected yet...\n"); status = connect_dev (dev); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_open: could not connect to device\n"); return status; } } DBG (3, "sane_open: net_open\n"); sanei_w_call (&dev->wire, SANE_NET_OPEN, (WireCodecFunc) sanei_w_string, &dev_name, (WireCodecFunc) sanei_w_open_reply, &reply); do { if (dev->wire.status != 0) { DBG (1, "sane_open: open rpc call failed (%s)\n", strerror (dev->wire.status)); return SANE_STATUS_IO_ERROR; } status = reply.status; handle = reply.handle; need_auth = (reply.resource_to_authorize != 0); if (need_auth) { DBG (3, "sane_open: authorization required\n"); do_authorization (dev, reply.resource_to_authorize); sanei_w_free (&dev->wire, (WireCodecFunc) sanei_w_open_reply, &reply); if (dev->wire.direction != WIRE_DECODE) sanei_w_set_dir (&dev->wire, WIRE_DECODE); sanei_w_open_reply (&dev->wire, &reply); continue; } else sanei_w_free (&dev->wire, (WireCodecFunc) sanei_w_open_reply, &reply); if (need_auth && !dev->auth_active) { DBG (2, "sane_open: open cancelled\n"); return SANE_STATUS_CANCELLED; } if (status != SANE_STATUS_GOOD) { DBG (1, "sane_open: remote open failed\n"); return reply.status; } } while (need_auth); s = malloc (sizeof (*s)); if (!s) { DBG (1, "sane_open: not enough free memory\n"); return SANE_STATUS_NO_MEM; } memset (s, 0, sizeof (*s)); s->hw = dev; s->handle = handle; s->data = -1; s->next = first_handle; s->local_opt.desc = 0; s->local_opt.num_options = 0; DBG (3, "sane_open: getting option descriptors\n"); status = fetch_options (s); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_open: fetch_options failed (%s), closing device again\n", sane_strstatus (status)); sanei_w_call (&s->hw->wire, SANE_NET_CLOSE, (WireCodecFunc) sanei_w_word, &s->handle, (WireCodecFunc) sanei_w_word, &ack); free (s); return status; } first_handle = s; *meta_handle = s; DBG (3, "sane_open: success\n"); return SANE_STATUS_GOOD; } void sane_close (SANE_Handle handle) { Net_Scanner *prev, *s; SANE_Word ack; int option_number; DBG (3, "sane_close: handle %p\n", handle); prev = 0; for (s = first_handle; s; s = s->next) { if (s == handle) break; prev = s; } if (!s) { DBG (1, "sane_close: invalid handle %p\n", handle); return; /* oops, not a handle we know about */ } if (prev) prev->next = s->next; else first_handle = s->next; if (s->opt.num_options) { DBG (2, "sane_close: removing cached option descriptors\n"); sanei_w_set_dir (&s->hw->wire, WIRE_FREE); s->hw->wire.status = 0; sanei_w_option_descriptor_array (&s->hw->wire, &s->opt); if (s->hw->wire.status) DBG (1, "sane_close: couldn't free sanei_w_option_descriptor_array " "(%s)\n", sane_strstatus (s->hw->wire.status)); } DBG (2, "sane_close: removing local option descriptors\n"); for (option_number = 0; option_number < s->local_opt.num_options; option_number++) free (s->local_opt.desc[option_number]); if (s->local_opt.desc) free (s->local_opt.desc); DBG (2, "sane_close: net_close\n"); sanei_w_call (&s->hw->wire, SANE_NET_CLOSE, (WireCodecFunc) sanei_w_word, &s->handle, (WireCodecFunc) sanei_w_word, &ack); if (s->data >= 0) { DBG (2, "sane_close: closing data pipe\n"); close (s->data); } free (s); DBG (2, "sane_close: done\n"); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { Net_Scanner *s = handle; SANE_Status status; DBG (3, "sane_get_option_descriptor: option %d\n", option); if (!s->options_valid) { DBG (3, "sane_get_option_descriptor: getting option descriptors\n"); status = fetch_options (s); if (status != SANE_STATUS_GOOD) { DBG (1, "sane_get_option_descriptor: fetch_options failed (%s)\n", sane_strstatus (status)); return 0; } } if (((SANE_Word) option >= s->opt.num_options) || (option < 0)) { DBG (2, "sane_get_option_descriptor: invalid option number\n"); return 0; } return s->local_opt.desc[option]; } SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Word * info) { Net_Scanner *s = handle; SANE_Control_Option_Req req; SANE_Control_Option_Reply reply; SANE_Status status; size_t value_size; int need_auth; SANE_Word local_info; DBG (3, "sane_control_option: option %d, action %d\n", option, action); if (!s->options_valid) { DBG (1, "sane_control_option: FRONTEND BUG: option descriptors reload needed\n"); return SANE_STATUS_INVAL; } if (((SANE_Word) option >= s->opt.num_options) || (option < 0)) { DBG (1, "sane_control_option: invalid option number\n"); return SANE_STATUS_INVAL; } switch (s->opt.desc[option]->type) { case SANE_TYPE_BUTTON: case SANE_TYPE_GROUP: /* shouldn't happen... */ /* the SANE standard defines that the option size of a BUTTON or GROUP is IGNORED. */ value_size = 0; break; case SANE_TYPE_STRING: /* strings can be smaller than size */ value_size = s->opt.desc[option]->size; if ((action == SANE_ACTION_SET_VALUE) && (((SANE_Int) strlen ((SANE_String) value) + 1) < s->opt.desc[option]->size)) value_size = strlen ((SANE_String) value) + 1; break; default: value_size = s->opt.desc[option]->size; break; } /* Avoid leaking memory bits */ if (value && (action != SANE_ACTION_SET_VALUE)) memset (value, 0, value_size); /* for SET_AUTO the parameter ``value'' is ignored */ if (action == SANE_ACTION_SET_AUTO) value_size = 0; req.handle = s->handle; req.option = option; req.action = action; req.value_type = s->opt.desc[option]->type; req.value_size = value_size; req.value = value; local_info = 0; DBG (3, "sane_control_option: remote control option\n"); sanei_w_call (&s->hw->wire, SANE_NET_CONTROL_OPTION, (WireCodecFunc) sanei_w_control_option_req, &req, (WireCodecFunc) sanei_w_control_option_reply, &reply); do { status = reply.status; need_auth = (reply.resource_to_authorize != 0); if (need_auth) { DBG (3, "sane_control_option: auth required\n"); do_authorization (s->hw, reply.resource_to_authorize); sanei_w_free (&s->hw->wire, (WireCodecFunc) sanei_w_control_option_reply, &reply); sanei_w_set_dir (&s->hw->wire, WIRE_DECODE); sanei_w_control_option_reply (&s->hw->wire, &reply); continue; } else if (status == SANE_STATUS_GOOD) { local_info = reply.info; if (info) *info = reply.info; if (value_size > 0) { if ((SANE_Word) value_size == reply.value_size) memcpy (value, reply.value, reply.value_size); else DBG (1, "sane_control_option: size changed from %d to %d\n", s->opt.desc[option]->size, reply.value_size); } if (reply.info & SANE_INFO_RELOAD_OPTIONS) s->options_valid = 0; } sanei_w_free (&s->hw->wire, (WireCodecFunc) sanei_w_control_option_reply, &reply); if (need_auth && !s->hw->auth_active) return SANE_STATUS_CANCELLED; } while (need_auth); DBG (2, "sane_control_option: remote done (%s, info %x)\n", sane_strstatus (status), local_info); if ((status == SANE_STATUS_GOOD) && (info == NULL) && (local_info & SANE_INFO_RELOAD_OPTIONS)) { DBG (2, "sane_control_option: reloading options as frontend does not care\n"); status = fetch_options (s); DBG (2, "sane_control_option: reload done (%s)\n", sane_strstatus (status)); } DBG (2, "sane_control_option: done (%s, info %x)\n", sane_strstatus (status), local_info); return status; } SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { Net_Scanner *s = handle; SANE_Get_Parameters_Reply reply; SANE_Status status; DBG (3, "sane_get_parameters\n"); if (!params) { DBG (1, "sane_get_parameters: parameter params not supplied\n"); return SANE_STATUS_INVAL; } DBG (3, "sane_get_parameters: remote get parameters\n"); sanei_w_call (&s->hw->wire, SANE_NET_GET_PARAMETERS, (WireCodecFunc) sanei_w_word, &s->handle, (WireCodecFunc) sanei_w_get_parameters_reply, &reply); status = reply.status; *params = reply.params; depth = reply.params.depth; sanei_w_free (&s->hw->wire, (WireCodecFunc) sanei_w_get_parameters_reply, &reply); DBG (3, "sane_get_parameters: returned status %s\n", sane_strstatus (status)); return status; } #ifdef NET_USES_AF_INDEP SANE_Status sane_start (SANE_Handle handle) { Net_Scanner *s = handle; SANE_Start_Reply reply; struct sockaddr_in sin; struct sockaddr *sa; #ifdef ENABLE_IPV6 struct sockaddr_in6 sin6; #endif /* ENABLE_IPV6 */ SANE_Status status; int fd, need_auth; socklen_t len; uint16_t port; /* Internet-specific */ DBG (3, "sane_start\n"); hang_over = -1; left_over = -1; if (s->data >= 0) { DBG (2, "sane_start: data pipe already exists\n"); return SANE_STATUS_INVAL; } /* Do this ahead of time so in case anything fails, we can recover gracefully (without hanging our server). */ switch (s->hw->addr_used->ai_family) { case AF_INET: len = sizeof (sin); sa = (struct sockaddr *) &sin; break; #ifdef ENABLE_IPV6 case AF_INET6: len = sizeof (sin6); sa = (struct sockaddr *) &sin6; break; #endif /* ENABLE_IPV6 */ default: DBG (1, "sane_start: unknown address family : %d\n", s->hw->addr_used->ai_family); return SANE_STATUS_INVAL; } if (getpeername (s->hw->ctl, sa, &len) < 0) { DBG (1, "sane_start: getpeername() failed (%s)\n", strerror (errno)); return SANE_STATUS_IO_ERROR; } fd = socket (s->hw->addr_used->ai_family, SOCK_STREAM, 0); if (fd < 0) { DBG (1, "sane_start: socket() failed (%s)\n", strerror (errno)); return SANE_STATUS_IO_ERROR; } DBG (3, "sane_start: remote start\n"); sanei_w_call (&s->hw->wire, SANE_NET_START, (WireCodecFunc) sanei_w_word, &s->handle, (WireCodecFunc) sanei_w_start_reply, &reply); do { status = reply.status; port = reply.port; if (reply.byte_order == 0x1234) { server_big_endian = 0; DBG (1, "sane_start: server has little endian byte order\n"); } else { server_big_endian = 1; DBG (1, "sane_start: server has big endian byte order\n"); } need_auth = (reply.resource_to_authorize != 0); if (need_auth) { DBG (3, "sane_start: auth required\n"); do_authorization (s->hw, reply.resource_to_authorize); sanei_w_free (&s->hw->wire, (WireCodecFunc) sanei_w_start_reply, &reply); sanei_w_set_dir (&s->hw->wire, WIRE_DECODE); sanei_w_start_reply (&s->hw->wire, &reply); continue; } sanei_w_free (&s->hw->wire, (WireCodecFunc) sanei_w_start_reply, &reply); if (need_auth && !s->hw->auth_active) return SANE_STATUS_CANCELLED; if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: remote start failed (%s)\n", sane_strstatus (status)); close (fd); return status; } } while (need_auth); DBG (3, "sane_start: remote start finished, data at port %hu\n", port); switch (s->hw->addr_used->ai_family) { case AF_INET: sin.sin_port = htons (port); break; #ifdef ENABLE_IPV6 case AF_INET6: sin6.sin6_port = htons (port); break; #endif /* ENABLE_IPV6 */ } if (connect (fd, sa, len) < 0) { DBG (1, "sane_start: connect() failed (%s)\n", strerror (errno)); close (fd); return SANE_STATUS_IO_ERROR; } shutdown (fd, 1); s->data = fd; s->reclen_buf_offset = 0; s->bytes_remaining = 0; DBG (3, "sane_start: done (%s)\n", sane_strstatus (status)); return status; } #else /* !NET_USES_AF_INDEP */ SANE_Status sane_start (SANE_Handle handle) { Net_Scanner *s = handle; SANE_Start_Reply reply; struct sockaddr_in sin; SANE_Status status; int fd, need_auth; socklen_t len; uint16_t port; /* Internet-specific */ DBG (3, "sane_start\n"); hang_over = -1; left_over = -1; if (s->data >= 0) { DBG (2, "sane_start: data pipe already exists\n"); return SANE_STATUS_INVAL; } /* Do this ahead of time so in case anything fails, we can recover gracefully (without hanging our server). */ len = sizeof (sin); if (getpeername (s->hw->ctl, (struct sockaddr *) &sin, &len) < 0) { DBG (1, "sane_start: getpeername() failed (%s)\n", strerror (errno)); return SANE_STATUS_IO_ERROR; } fd = socket (s->hw->addr.sa_family, SOCK_STREAM, 0); if (fd < 0) { DBG (1, "sane_start: socket() failed (%s)\n", strerror (errno)); return SANE_STATUS_IO_ERROR; } DBG (3, "sane_start: remote start\n"); sanei_w_call (&s->hw->wire, SANE_NET_START, (WireCodecFunc) sanei_w_word, &s->handle, (WireCodecFunc) sanei_w_start_reply, &reply); do { status = reply.status; port = reply.port; if (reply.byte_order == 0x1234) { server_big_endian = 0; DBG (1, "sane_start: server has little endian byte order\n"); } else { server_big_endian = 1; DBG (1, "sane_start: server has big endian byte order\n"); } need_auth = (reply.resource_to_authorize != 0); if (need_auth) { DBG (3, "sane_start: auth required\n"); do_authorization (s->hw, reply.resource_to_authorize); sanei_w_free (&s->hw->wire, (WireCodecFunc) sanei_w_start_reply, &reply); sanei_w_set_dir (&s->hw->wire, WIRE_DECODE); sanei_w_start_reply (&s->hw->wire, &reply); continue; } sanei_w_free (&s->hw->wire, (WireCodecFunc) sanei_w_start_reply, &reply); if (need_auth && !s->hw->auth_active) return SANE_STATUS_CANCELLED; if (status != SANE_STATUS_GOOD) { DBG (1, "sane_start: remote start failed (%s)\n", sane_strstatus (status)); close (fd); return status; } } while (need_auth); DBG (3, "sane_start: remote start finished, data at port %hu\n", port); sin.sin_port = htons (port); if (connect (fd, (struct sockaddr *) &sin, len) < 0) { DBG (1, "sane_start: connect() failed (%s)\n", strerror (errno)); close (fd); return SANE_STATUS_IO_ERROR; } shutdown (fd, 1); s->data = fd; s->reclen_buf_offset = 0; s->bytes_remaining = 0; DBG (3, "sane_start: done (%s)\n", sane_strstatus (status)); return status; } #endif /* NET_USES_AF_INDEP */ SANE_Status sane_read (SANE_Handle handle, SANE_Byte * data, SANE_Int max_length, SANE_Int * length) { Net_Scanner *s = handle; ssize_t nread; SANE_Int cnt; SANE_Int start_cnt; SANE_Int end_cnt; SANE_Byte swap_buf; SANE_Byte temp_hang_over; int is_even; DBG (3, "sane_read: handle=%p, data=%p, max_length=%d, length=%p\n", handle, (void *) data, max_length, (void *) length); if (!length) { DBG (1, "sane_read: length == NULL\n"); return SANE_STATUS_INVAL; } is_even = 1; *length = 0; /* If there's a left over, i.e. a byte already in the correct byte order, return it immediately; otherwise read may fail with a SANE_STATUS_EOF and the caller never can read the last byte */ if ((depth == 16) && (server_big_endian != client_big_endian)) { if (left_over > -1) { DBG (3, "sane_read: left_over from previous call, return " "immediately\n"); /* return the byte, we've currently scanned; hang_over becomes left_over */ *data = (SANE_Byte) left_over; left_over = -1; *length = 1; return SANE_STATUS_GOOD; } } if (s->data < 0) { DBG (1, "sane_read: data pipe doesn't exist, scan cancelled?\n"); return SANE_STATUS_CANCELLED; } if (s->bytes_remaining == 0) { /* boy, is this painful or what? */ DBG (4, "sane_read: reading packet length\n"); nread = read (s->data, s->reclen_buf + s->reclen_buf_offset, 4 - s->reclen_buf_offset); if (nread < 0) { DBG (3, "sane_read: read failed (%s)\n", strerror (errno)); if (errno == EAGAIN) { DBG (3, "sane_read: try again later\n"); return SANE_STATUS_GOOD; } else { DBG (1, "sane_read: cancelling read\n"); do_cancel (s); return SANE_STATUS_IO_ERROR; } } DBG (4, "sane_read: read %lu bytes, %d from 4 total\n", (u_long) nread, s->reclen_buf_offset); s->reclen_buf_offset += nread; if (s->reclen_buf_offset < 4) { DBG (4, "sane_read: enough for now\n"); return SANE_STATUS_GOOD; } s->reclen_buf_offset = 0; s->bytes_remaining = (((u_long) s->reclen_buf[0] << 24) | ((u_long) s->reclen_buf[1] << 16) | ((u_long) s->reclen_buf[2] << 8) | ((u_long) s->reclen_buf[3] << 0)); DBG (3, "sane_read: next record length=%ld bytes\n", (long) s->bytes_remaining); if (s->bytes_remaining == 0xffffffff) { char ch; DBG (2, "sane_read: received error signal\n"); /* turn off non-blocking I/O (s->data will be closed anyhow): */ fcntl (s->data, F_SETFL, 0); /* read the status byte: */ if (read (s->data, &ch, sizeof (ch)) != 1) { DBG (1, "sane_read: failed to read error code\n"); ch = SANE_STATUS_IO_ERROR; } DBG (1, "sane_read: error code %s\n", sane_strstatus ((SANE_Status) ch)); do_cancel (s); return (SANE_Status) ch; } } if (max_length > (SANE_Int) s->bytes_remaining) max_length = s->bytes_remaining; nread = read (s->data, data, max_length); if (nread < 0) { DBG (2, "sane_read: error code %s\n", strerror (errno)); if (errno == EAGAIN) return SANE_STATUS_GOOD; else { DBG (1, "sane_read: cancelling scan\n"); do_cancel (s); return SANE_STATUS_IO_ERROR; } } s->bytes_remaining -= nread; *length = nread; /* Check whether we are scanning with a depth of 16 bits/pixel and whether server and client have different byte order. If this is true, then it's necessary to check whether read returned an odd number. If an odd number has been returned, we must save the last byte. */ if ((depth == 16) && (server_big_endian != client_big_endian)) { DBG (1,"sane_read: client/server have different byte order; " "must swap\n"); /* special case: 1 byte scanned and hang_over */ if ((nread == 1) && (hang_over > -1)) { /* return the byte, we've currently scanned; hang_over becomes left_over */ left_over = hang_over; hang_over = -1; return SANE_STATUS_GOOD; } /* check whether an even or an odd number of bytes has been scanned */ if ((nread % 2) == 0) is_even = 1; else is_even = 0; /* check, whether there's a hang over from a previous call; in this case we memcopy the data up one byte */ if ((nread > 1) && (hang_over > -1)) { /* store last byte */ temp_hang_over = *(data + nread - 1); memmove (data + 1, data, nread - 1); *data = (SANE_Byte) hang_over; /* what happens with the last byte depends on whether the number of bytes is even or odd */ if (is_even == 1) { /* number of bytes is even; no new hang_over, exchange last byte with hang over; last byte becomes left_over */ left_over = *(data + nread - 1); *(data + nread - 1) = temp_hang_over; hang_over = -1; start_cnt = 0; /* last byte already swapped */ end_cnt = nread - 2; } else { /* number of bytes is odd; last byte becomes new hang_over */ hang_over = temp_hang_over; left_over = -1; start_cnt = 0; end_cnt = nread - 1; } } else if (nread == 1) { /* if only one byte has been read, save it as hang_over and return length=0 */ hang_over = (int) *data; *length = 0; return SANE_STATUS_GOOD; } else { /* no hang_over; test for even or odd byte number */ if(is_even == 1) { start_cnt = 0; end_cnt = *length; } else { start_cnt = 0; hang_over = *(data + *length - 1); *length -= 1; end_cnt = *length; } } /* swap the bytes */ for (cnt = start_cnt; cnt < end_cnt - 1; cnt += 2) { swap_buf = *(data + cnt); *(data + cnt) = *(data + cnt + 1); *(data + cnt + 1) = swap_buf; } } DBG (3, "sane_read: %lu bytes read, %lu remaining\n", (u_long) nread, (u_long) s->bytes_remaining); return SANE_STATUS_GOOD; } void sane_cancel (SANE_Handle handle) { Net_Scanner *s = handle; SANE_Word ack; DBG (3, "sane_cancel: sending net_cancel\n"); sanei_w_call (&s->hw->wire, SANE_NET_CANCEL, (WireCodecFunc) sanei_w_word, &s->handle, (WireCodecFunc) sanei_w_word, &ack); do_cancel (s); DBG (4, "sane_cancel: done\n"); } SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { Net_Scanner *s = handle; DBG (3, "sane_set_io_mode: non_blocking = %d\n", non_blocking); if (s->data < 0) { DBG (1, "sane_set_io_mode: pipe doesn't exist\n"); return SANE_STATUS_INVAL; } if (fcntl (s->data, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0) { DBG (1, "sane_set_io_mode: fcntl failed (%s)\n", strerror (errno)); return SANE_STATUS_IO_ERROR; } return SANE_STATUS_GOOD; } SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) { Net_Scanner *s = handle; DBG (3, "sane_get_select_fd\n"); if (s->data < 0) { DBG (1, "sane_get_select_fd: pipe doesn't exist\n"); return SANE_STATUS_INVAL; } *fd = s->data; DBG (3, "sane_get_select_fd: done; *fd = %d\n", *fd); return SANE_STATUS_GOOD; } backends-1.3.0/backend/net.conf.in000066400000000000000000000010751456256263500167370ustar00rootroot00000000000000# This is the net backend config file. ## net backend options # Timeout for the initial connection to saned. This will prevent the backend # from blocking for several minutes trying to connect to an unresponsive # saned host (network outage, host down, ...). Value in seconds. # connect_timeout = 60 ## saned hosts # 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 backends-1.3.0/backend/net.h000066400000000000000000000056041456256263500156360ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 1997 David Mosberger-Tang Copyright (C) 2003 Julien BLACHE AF-independent code + IPv6 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, see . 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 net_h #define net_h #include #include #include "../include/sane/sanei_wire.h" #include "../include/sane/config.h" typedef struct Net_Device { struct Net_Device *next; const char *name; #if defined (HAVE_GETADDRINFO) && defined (HAVE_GETNAMEINFO) struct addrinfo *addr; struct addrinfo *addr_used; #else struct sockaddr addr; #endif /* HAVE_GETADDRINFO && HAVE_GETNAMEINFO */ int ctl; /* socket descriptor (or -1) */ Wire wire; int auth_active; } Net_Device; typedef struct Net_Scanner { /* all the state needed to define a scan request: */ struct Net_Scanner *next; int options_valid; /* are the options current? */ SANE_Option_Descriptor_Array opt, local_opt; SANE_Word handle; /* remote handle (it's a word, not a ptr!) */ int data; /* data socket descriptor */ int reclen_buf_offset; u_char reclen_buf[4]; size_t bytes_remaining; /* how many bytes left in this record? */ /* device (host) info: */ Net_Device *hw; } Net_Scanner; #endif /* net_h */ backends-1.3.0/backend/niash.c000066400000000000000000001154641456256263500161530ustar00rootroot00000000000000/* Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl) 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, see . */ /* Concept for a backend for scanners based on the NIASH chipset, such as HP3300C, HP3400C, HP4300C, Agfa Touch. Parts of this source were inspired by other backends. */ #include "../include/sane/config.h" #include "../include/sane/sane.h" #include "../include/sane/sanei.h" #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_config.h" #include "../include/sane/saneopts.h" #include /* malloc, free */ #include /* memcpy */ #include #include #include /* definitions for debug */ #define BACKEND_NAME niash #define BUILD 1 #define DBG_ASSERT 1 #define DBG_ERR 16 #define DBG_MSG 32 /* Just to avoid conflicts between niash backend and testtool */ #define WITH_NIASH 1 /* (source) includes for data transfer methods */ #define STATIC static #include "niash_core.c" #include "niash_xfer.c" #define ASSERT(cond) (!(cond) ? DBG(DBG_ASSERT, "!!! ASSERT(%S) FAILED!!!\n",STRINGIFY(cond));) #define MM_TO_PIXEL(_mm_, _dpi_) ((_mm_) * (_dpi_) / 25.4 ) #define PIXEL_TO_MM(_pixel_, _dpi_) ((_pixel_) * 25.4 / (_dpi_) ) /* options enumerator */ typedef enum { optCount = 0, optGroupGeometry, optTLX, optTLY, optBRX, optBRY, optDPI, optGroupImage, optGammaTable, /* gamma table */ optGroupMode, optMode, optGroupEnhancement, optThreshold, #ifdef EXPERIMENTAL optGroupMisc, optLamp, optCalibrate, optGamma, /* analog gamma = single number */ #endif optLast } EOptionIndex; typedef union { SANE_Word w; SANE_Word *wa; /* word array */ SANE_String s; } TOptionValue; #define HW_GAMMA_SIZE 4096 #define SANE_GAMMA_SIZE 4096 typedef struct { SANE_Option_Descriptor aOptions[optLast]; TOptionValue aValues[optLast]; TScanParams ScanParams; THWParams HWParams; TDataPipe DataPipe; 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 */ /* fCancelled needed to let sane issue the cancel message instead of an error message */ SANE_Bool fCancelled; /* SANE_TRUE if scanning cancelled */ 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 */ struct timeval WarmUpStarted; /* system type to trace the time elapsed */ } TScanner; /* linked list of SANE_Device structures */ typedef struct TDevListEntry { struct TDevListEntry *pNext; SANE_Device dev; } TDevListEntry; static TDevListEntry *_pFirstSaneDev = 0; static int iNumSaneDev = 0; static const SANE_Device **_pSaneDevList = 0; /* option constraints */ static const SANE_Range rangeGammaTable = { 0, 255, 1 }; /* available scanner resolutions */ static const SANE_Int setResolutions[] = { 4, 75, 150, 300, 600 }; #ifdef EXPERIMENTAL /* range of an analog gamma */ static const SANE_Range rangeGamma = { SANE_FIX (0.25), SANE_FIX (4.0), SANE_FIX (0.0) }; #endif /* 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) { int i; int current = 0; for (i = 0; i < SANE_GAMMA_SIZE; ++i) { int j; int next; /* highest range of copy indices */ next = ((i + 1) * HW_GAMMA_SIZE) / SANE_GAMMA_SIZE; /* always copy the first */ hwGamma[current] = saneGamma[i]; /* 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); } current = next; } } /* create a unity gamma table */ static void _UnityGammaTable (unsigned char *hwGamma) { int i; for (i = 0; i < HW_GAMMA_SIZE; ++i) { hwGamma[i] = (i * 256) / HW_GAMMA_SIZE; } } static const SANE_Range rangeXmm = { 0, 220, 1 }; static const SANE_Range rangeYmm = { 0, 297, 1 }; static const SANE_Int startUpGamma = SANE_FIX (1.6); static const char colorStr[] = { SANE_VALUE_SCAN_MODE_COLOR }; static const char grayStr[] = { SANE_VALUE_SCAN_MODE_GRAY }; static const char lineartStr[] = { SANE_VALUE_SCAN_MODE_LINEART }; #define DEPTH_LINEART 1 #define DEPTH_GRAY 8 #define DEPTH_COLOR 8 #define BYTES_PER_PIXEL_GRAY 1 #define BYTES_PER_PIXEL_COLOR 3 #define BITS_PER_PIXEL_LINEART 1 #define BITS_PER_PIXEL_GRAY DEPTH_GRAY #define BITS_PER_PIXEL_COLOR (DEPTH_COLOR*3) #define BITS_PER_BYTE 8 #define BITS_PADDING (BITS_PER_BYTE-1) #define MODE_COLOR 0 #define MODE_GRAY 1 #define MODE_LINEART 2 /* lineart threshold range */ static const SANE_Range rangeThreshold = { 0, 100, 1 }; /* scanning modes */ static SANE_String_Const modeList[] = { colorStr, grayStr, lineartStr, NULL }; static int _bytesPerLineLineart (int pixelsPerLine) { return (pixelsPerLine * BITS_PER_PIXEL_LINEART + BITS_PADDING) / BITS_PER_BYTE; } static int _bytesPerLineGray (int pixelsPerLine) { return (pixelsPerLine * BITS_PER_PIXEL_GRAY + BITS_PADDING) / BITS_PER_BYTE; } static int _bytesPerLineColor (int pixelsPerLine) { return (pixelsPerLine * BITS_PER_PIXEL_COLOR + BITS_PADDING) / BITS_PER_BYTE; } /* dummy*/ static void _rgb2rgb (unsigned char __sane_unused__ *buffer, int __sane_unused__ pixels, int __sane_unused__ threshold) { /* make the compiler content */ } /* convert 24bit RGB to 8bit GRAY */ static void _rgb2gray (unsigned char *buffer, int pixels, int __sane_unused__ threshold) { #define WEIGHT_R 27 #define WEIGHT_G 54 #define WEIGHT_B 19 #define WEIGHT_W (WEIGHT_R + WEIGHT_G + WEIGHT_B) static int aWeight[BYTES_PER_PIXEL_COLOR] = { WEIGHT_R, WEIGHT_G, WEIGHT_B }; int nbyte = pixels * BYTES_PER_PIXEL_COLOR; int acc = 0; int x; for (x = 0; x < nbyte; ++x) { 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; } } #undef WEIGHT_R #undef WEIGHT_G #undef WEIGHT_B #undef WEIGHT_W } /* convert 24bit RGB to 1bit B/W */ static void _rgb2lineart (unsigned char *buffer, int pixels, int threshold) { static const int aMask[BITS_PER_BYTE] = { 128, 64, 32, 16, 8, 4, 2, 1 }; int acc = 0; int nx; int x; int thresh; _rgb2gray (buffer, pixels, 0); nx = ((pixels + BITS_PADDING) / BITS_PER_BYTE) * BITS_PER_BYTE; thresh = 255 * threshold / rangeThreshold.max; for (x = 0; x < nx; ++x) { if (x < pixels && buffer[x] < thresh) { acc |= aMask[x % BITS_PER_BYTE]; } if ((x + 1) % BITS_PER_BYTE == 0) { buffer[x / BITS_PER_BYTE] = (unsigned char) (acc); acc = 0; } } } typedef struct tgModeParam { SANE_Int depth; SANE_Frame format; int (*bytesPerLine) (int pixelsPerLine); void (*adaptFormat) (unsigned char *rgbBuffer, int pixels, int threshold); } TModeParam; static const TModeParam modeParam[] = { {DEPTH_COLOR, SANE_FRAME_RGB, _bytesPerLineColor, _rgb2rgb}, {DEPTH_GRAY, SANE_FRAME_GRAY, _bytesPerLineGray, _rgb2gray}, {DEPTH_LINEART, SANE_FRAME_GRAY, _bytesPerLineLineart, _rgb2lineart} }; #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 CAL_DEV_MAX 15 /* maximum deviation of cal values in percent between 2 tests */ /* different warm up after start and after automatic off */ static const int aiWarmUpTime[] = { WARMUP_TESTINTERVAL, WARMUP_TIME }; /* returns 1, when the warm up time "iTime" has elasped */ static int _TimeElapsed (struct timeval *start, struct timeval *now, int iTime) { /* this is a bit strange, but can deal with overflows */ if (start->tv_sec > now->tv_sec) return (start->tv_sec / 2 - now->tv_sec / 2 > iTime / 2); else return (now->tv_sec - start->tv_sec >= iTime); } static void _WarmUpLamp (TScanner * s, int iMode) { SANE_Bool fLampOn; /* on startup don't care what was before assume lamp was off, and the previous cal values can never be reached */ if (iMode == WARMUP_AFTERSTART) { fLampOn = SANE_FALSE; s->CalWhite[0] = s->CalWhite[1] = s->CalWhite[2] = (unsigned char) (-1); } else GetLamp (&s->HWParams, &fLampOn); if (!fLampOn) { /* get the current system time */ gettimeofday (&s->WarmUpStarted, 0); /* determine the time to wait at least */ s->WarmUpTime = aiWarmUpTime[iMode]; /* switch on the lamp */ SetLamp (&s->HWParams, SANE_TRUE); } } 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 */ SANE_Bool fHasCal; unsigned char CalWhite[2][3]; /* toggling buffer */ int iDelay = 0; /* delay loop counter */ _WarmUpLamp (s, SANE_FALSE); /* get the time stamp for the wait loops */ if (s->WarmUpTime) gettimeofday (&now[iCurrent], 0); SimpleCalibExt (&s->HWParams, pabCalibTable, CalWhite[iCurrent]); fHasCal = SANE_TRUE; DBG (DBG_MSG, "_WaitForLamp: first calibration\n"); /* wait until time has elapsed or for values to stabilze */ while (s->WarmUpTime) { /* 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"); } } /* break the loop, when the longest wait time has expired 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); } } /* 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); } /* 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; } } /* remember the values of this calibration for the next time */ for (i = 0; i < 3; ++i) { s->CalWhite[i] = CalWhite[iCurrent][i]; } } /* used, when setting gamma as 1 value */ static void _SetScalarGamma (SANE_Int * aiGamma, SANE_Int sfGamma) { int j; double fGamma; fGamma = SANE_UNFIX (sfGamma); for (j = 0; j < SANE_GAMMA_SIZE; j++) { int iData; iData = floor (256.0 * pow (((double) j / (double) SANE_GAMMA_SIZE), 1.0 / fGamma)); if (iData > 255) iData = 255; aiGamma[j] = iData; } } /* return size of longest string in a string 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; } return max_size; } /* change a sane cap and return true, when a change took place */ static int _ChangeCap (SANE_Word * pCap, SANE_Word cap, int isSet) { SANE_Word prevCap = *pCap; if (isSet) { *pCap |= cap; } else { *pCap &= ~cap; } return *pCap != prevCap; } static void _InitOptions (TScanner * s) { int i; SANE_Option_Descriptor *pDesc; TOptionValue *pVal; _SetScalarGamma (s->aGammaTable, startUpGamma); for (i = optCount; i < optLast; i++) { pDesc = &s->aOptions[i]; pVal = &s->aValues[i]; /* defaults */ pDesc->name = ""; pDesc->title = ""; pDesc->desc = ""; pDesc->type = SANE_TYPE_INT; pDesc->unit = SANE_UNIT_NONE; pDesc->size = sizeof (SANE_Word); pDesc->constraint_type = SANE_CONSTRAINT_NONE; 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 instead 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; #ifdef EXPERIMENTAL 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; #endif 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; #ifdef EXPERIMENTAL 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; #endif 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; break; default: DBG (DBG_ERR, "Uninitialised option %d\n", i); break; } } } static int _ReportDevice (TScannerModel * pModel, const char *pszDeviceName) { TDevListEntry *pNew, *pDev; DBG (DBG_MSG, "niash: _ReportDevice '%s'\n", pszDeviceName); pNew = malloc (sizeof (TDevListEntry)); if (!pNew) { DBG (DBG_ERR, "no mem\n"); return -1; } /* add new element to the end of the list */ if (_pFirstSaneDev == 0) { _pFirstSaneDev = pNew; } else { for (pDev = _pFirstSaneDev; pDev->pNext; pDev = pDev->pNext) { ; } pDev->pNext = pNew; } /* fill in new element */ pNew->pNext = 0; pNew->dev.name = strdup (pszDeviceName); pNew->dev.vendor = pModel->pszVendor; pNew->dev.model = pModel->pszName; pNew->dev.type = "flatbed scanner"; iNumSaneDev++; return 0; } /*****************************************************************************/ SANE_Status sane_init (SANE_Int * piVersion, SANE_Auth_Callback __sane_unused__ pfnAuth) { DBG_INIT (); DBG (DBG_MSG, "sane_init\n"); if (piVersion != NULL) { *piVersion = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); } /* initialise transfer methods */ iNumSaneDev = 0; NiashXferInit (_ReportDevice); return SANE_STATUS_GOOD; } void sane_exit (void) { TDevListEntry *pDev, *pNext; DBG (DBG_MSG, "sane_exit\n"); /* free device list memory */ if (_pSaneDevList) { for (pDev = _pFirstSaneDev; pDev; pDev = pNext) { pNext = pDev->pNext; free ((void *) pDev->dev.name); free (pDev); } _pFirstSaneDev = 0; free (_pSaneDevList); _pSaneDevList = 0; } } SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only) { TDevListEntry *pDev; int i; DBG (DBG_MSG, "sane_get_devices\n"); if (_pSaneDevList) { free (_pSaneDevList); } _pSaneDevList = malloc (sizeof (*_pSaneDevList) * (iNumSaneDev + 1)); if (!_pSaneDevList) { DBG (DBG_MSG, "no mem\n"); return SANE_STATUS_NO_MEM; } i = 0; for (pDev = _pFirstSaneDev; pDev; pDev = pDev->pNext) { _pSaneDevList[i++] = &pDev->dev; } _pSaneDevList[i++] = 0; /* last entry is 0 */ *device_list = _pSaneDevList; return SANE_STATUS_GOOD; } SANE_Status sane_open (SANE_String_Const name, SANE_Handle * h) { TScanner *s; DBG (DBG_MSG, "sane_open: %s\n", name); /* check the name */ if (strlen (name) == 0) { /* default to first available device */ name = _pFirstSaneDev->dev.name; } s = malloc (sizeof (TScanner)); if (!s) { DBG (DBG_MSG, "malloc failed\n"); return SANE_STATUS_NO_MEM; } if (NiashOpen (&s->HWParams, name) < 0) { /* is this OK ? */ DBG (DBG_ERR, "NiashOpen failed\n"); free ((void *) s); return SANE_STATUS_DEVICE_BUSY; } _InitOptions (s); s->fScanning = SANE_FALSE; s->fCancelled = SANE_FALSE; *h = s; /* Turn on lamp by default at startup */ _WarmUpLamp (s, WARMUP_AFTERSTART); return SANE_STATUS_GOOD; } void sane_close (SANE_Handle h) { TScanner *s; DBG (DBG_MSG, "sane_close\n"); s = (TScanner *) h; /* turn off scanner lamp */ SetLamp (&s->HWParams, SANE_FALSE); /* close scanner */ NiashClose (&s->HWParams); /* free scanner object memory */ free ((void *) s); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle h, SANE_Int n) { TScanner *s; DBG (DBG_MSG, "sane_get_option_descriptor %d\n", n); if ((n < optCount) || (n >= optLast)) { return NULL; } s = (TScanner *) h; return &s->aOptions[n]; } SANE_Status sane_control_option (SANE_Handle h, SANE_Int n, SANE_Action Action, void *pVal, SANE_Int * pInfo) { TScanner *s; static char szTable[100]; int *pi; int i; SANE_Int info; SANE_Status status; #ifdef EXPERIMENTAL SANE_Bool fLampIsOn; SANE_Bool fVal; SANE_Bool fSame; #endif DBG (DBG_MSG, "sane_control_option: option %d, action %d\n", n, Action); if ((n < optCount) || (n >= optLast)) { return SANE_STATUS_UNSUPPORTED; } if (Action == SANE_ACTION_GET_VALUE || Action == SANE_ACTION_SET_VALUE) { if (pVal == NULL) { return SANE_STATUS_INVAL; } } s = (TScanner *) h; info = 0; switch (Action) { case SANE_ACTION_GET_VALUE: switch (n) { /* Get options of type SANE_Word */ case optCount: case optDPI: #ifdef EXPERIMENTAL case optGamma: #endif 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; #ifdef EXPERIMENTAL /* 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; #endif 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; } switch (n) { case optCount: return SANE_STATUS_INVAL; #ifdef EXPERIMENTAL case optGamma: #endif 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; } #ifdef EXPERIMENTAL /* check values if they are equal */ fSame = s->aValues[n].w == *(SANE_Word *) pVal; #endif /* 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); #ifdef EXPERIMENTAL if (n == optGamma) { if (!fSame && optLast > optGammaTable) { info |= SANE_INFO_RELOAD_OPTIONS; } _SetScalarGamma (s->aGammaTable, s->aValues[n].w); } #endif 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) { DBG (DBG_MSG, "%s\n", szTable); szTable[0] = '\0'; } /* test for number print */ if ((SANE_GAMMA_SIZE / 64) && (i % (SANE_GAMMA_SIZE / 64)) == 0) { sprintf (szTable + strlen(szTable), " %04X", pi[i]); } } if (strlen (szTable)) { DBG (DBG_MSG, "%s\n", 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; #ifdef EXPERIMENTAL 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: /* SimpleCalib(&s->HWParams); */ break; #endif default: DBG (DBG_ERR, "SANE_ACTION_SET_VALUE: Invalid option (%d)\n", n); } if (pInfo != NULL) { *pInfo |= info; } break; case SANE_ACTION_SET_AUTO: return SANE_STATUS_UNSUPPORTED; default: DBG (DBG_ERR, "Invalid action (%d)\n", Action); return SANE_STATUS_INVAL; } return SANE_STATUS_GOOD; } SANE_Status sane_get_parameters (SANE_Handle h, SANE_Parameters * p) { TScanner *s; TModeParam const *pMode; DBG (DBG_MSG, "sane_get_parameters\n"); s = (TScanner *) h; /* first do some checks */ 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? */ } 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? */ } pMode = &modeParam[s->aValues[optMode].w]; /* return the data */ p->format = pMode->format; p->last_frame = SANE_TRUE; p->lines = MM_TO_PIXEL (s->aValues[optBRY].w - s->aValues[optTLY].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); p->bytes_per_line = pMode->bytesPerLine (p->pixels_per_line); return SANE_STATUS_GOOD; } /* get the scale down factor for a resolution that is not supported by hardware */ static int _SaneEmulateScaling (int iDpi) { if (iDpi == 75) return 2; else return 1; } SANE_Status sane_start (SANE_Handle h) { TScanner *s; SANE_Parameters par; int iLineCorr; int iScaleDown; static unsigned char abGamma[HW_GAMMA_SIZE]; static unsigned char abCalibTable[HW_PIXELS * 6]; DBG (DBG_MSG, "sane_start\n"); s = (TScanner *) h; if (sane_get_parameters (h, &par) != SANE_STATUS_GOOD) { DBG (DBG_MSG, "Invalid scan parameters\n"); return SANE_STATUS_INVAL; } iScaleDown = _SaneEmulateScaling (s->aValues[optDPI].w); s->iLinesLeft = par.lines; /* fill in the scanparams using the option values */ s->ScanParams.iDpi = s->aValues[optDPI].w * iScaleDown; s->ScanParams.iLpi = s->aValues[optDPI].w * iScaleDown; /* calculate correction for filling of circular buffer */ 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; s->ScanParams.iLeft = MM_TO_PIXEL (s->aValues[optTLX].w + s->HWParams.iTopLeftX, HW_DPI); s->ScanParams.iWidth = par.pixels_per_line * iScaleDown; s->ScanParams.iHeight = par.lines * iScaleDown; s->ScanParams.iBottom = HP3300C_BOTTOM; s->ScanParams.fCalib = SANE_FALSE; /* perform a simple calibration just before scanning */ _WaitForLamp (s, abCalibTable); if (s->aValues[optMode].w == MODE_LINEART) { /* use a unity gamma table for lineart to be independent from Gamma settings */ _UnityGammaTable (abGamma); } else { /* copy gamma table */ _ConvertGammaTable (s->aGammaTable, abGamma); } WriteGammaCalibTable (abGamma, abGamma, abGamma, abCalibTable, 0, 0, &s->HWParams); /* prepare the actual scan */ if (!InitScan (&s->ScanParams, &s->HWParams)) { DBG (DBG_MSG, "Invalid scan parameters\n"); return SANE_STATUS_INVAL; } /* init data pipe */ s->DataPipe.iSkipLines = s->HWParams.iSkipLines; /* on the hp3400 and hp4300 we cannot set the top of the scan area (yet), so instead we just scan and throw away the data until the top */ if (s->HWParams.fReg07) { s->DataPipe.iSkipLines += MM_TO_PIXEL (s->aValues[optTLY].w + s->HWParams.iTopLeftY, s->aValues[optDPI].w * iScaleDown); } s->iBytesLeft = 0; s->iPixelsPerLine = par.pixels_per_line; /* 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); s->fScanning = SANE_TRUE; s->fCancelled = SANE_FALSE; return SANE_STATUS_GOOD; } SANE_Status sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len) { TScanner *s; TDataPipe *p; TModeParam const *pMode; DBG (DBG_MSG, "sane_read: buf=%p, maxlen=%d, ", (void *) buf, maxlen); s = (TScanner *) h; pMode = &modeParam[s->aValues[optMode].w]; /* sane_read only allowed after sane_start */ 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; } else { DBG (DBG_ERR, "sane_read: sane_read only allowed after sane_start\n"); return SANE_STATUS_INVAL; } } p = &s->DataPipe; /* anything left to read? */ if ((s->iLinesLeft == 0) && (s->iBytesLeft == 0)) { CircBufferExit (p); free (p->pabLineBuf); p->pabLineBuf = NULL; FinishScan (&s->HWParams); *len = 0; DBG (DBG_MSG, "\n"); DBG (DBG_MSG, "sane_read: end of scan\n"); s->fCancelled = SANE_FALSE; s->fScanning = SANE_FALSE; return SANE_STATUS_EOF; } /* time to read the next line? */ if (s->iBytesLeft == 0) { /* 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--; } /* 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; } } /* copy (part of) a line */ *len = MIN (maxlen, s->iBytesLeft); memcpy (buf, &p->pabLineBuf[pMode->bytesPerLine (s->iPixelsPerLine) - s->iBytesLeft], *len); s->iBytesLeft -= *len; DBG (DBG_MSG, " read=%d \n", *len); return SANE_STATUS_GOOD; } void sane_cancel (SANE_Handle h) { TScanner *s; DBG (DBG_MSG, "sane_cancel\n"); s = (TScanner *) h; /* Make sure the scanner head returns home */ FinishScan (&s->HWParams); /* delete allocated data */ if (s->fScanning) { CircBufferExit (&s->DataPipe); free (s->DataPipe.pabLineBuf); s->DataPipe.pabLineBuf = NULL; DBG (DBG_MSG, "sane_cancel: freeing buffers\n"); } s->fCancelled = SANE_TRUE; s->fScanning = SANE_FALSE; } SANE_Status sane_set_io_mode (SANE_Handle __sane_unused__ h, SANE_Bool m) { DBG (DBG_MSG, "sane_set_io_mode %s\n", m ? "non-blocking" : "blocking"); if (m) { return SANE_STATUS_UNSUPPORTED; } return SANE_STATUS_GOOD; } SANE_Status sane_get_select_fd (SANE_Handle __sane_unused__ h, SANE_Int __sane_unused__ * fd) { DBG (DBG_MSG, "sane_select_fd\n"); return SANE_STATUS_UNSUPPORTED; } backends-1.3.0/backend/niash_core.c000066400000000000000000001040211456256263500171460ustar00rootroot00000000000000/* Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl) 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, see . */ /* Core NIASH chip functions. */ #include /* fopen, fread, fwrite, fclose etc */ #include /* va_list for vfprintf */ #include /* memcpy, memset */ #include /* unlink */ #include /* malloc, free */ #include /* exp, pow */ #include "niash_xfer.h" #include "niash_core.h" #ifndef MIN #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #endif #ifndef MAX #define MAX(a,b) (((a) > (b)) ? (a) : (b)) #endif #define XFER_BUF_SIZE 0xF000 /* HP3400 firmware data */ static unsigned char abData0000[] = { 0xfe, 0x9f, 0x58, 0x1b, 0x00, 0x03, 0xa4, 0x02, 0x63, 0x02, 0x33, 0x02, 0x0d, 0x02, 0xf0, 0x01, 0xd8, 0x01, 0xc5, 0x01, 0xb5, 0x01, 0xa8, 0x01, 0x9d, 0x01, 0x93, 0x01, 0x8b, 0x01, 0x84, 0x01, 0x7e, 0x01, 0x79, 0x01, 0x74, 0x01, 0x70, 0x01, 0x6d, 0x01, 0x69, 0x01, 0x67, 0x01, 0x64, 0x01, 0x62, 0x01, 0x60, 0x01, 0x5f, 0x01, 0x5d, 0x01, 0x5c, 0x01, 0x5b, 0x01, 0x5a, 0x01, 0x59, 0x01, 0x58, 0x01, 0x57, 0x01, 0x57, 0x01, 0x56, 0x01, 0x56, 0x01, 0x55, 0x01, 0x55, 0x01, 0x54, 0x01, 0x54, 0x01, 0x54, 0x01, 0x54, 0x01, 0x53, 0x01, 0x53, 0x01, 0x53, 0x01, 0x53, 0x01, 0x52, 0x81 }; /* 1st word : 0x9ffe = 40958, strip 15th bit: 0x1ffe = 8190 2nd word : 0x1b58 = 7000 -> coincidence ? other words: formula: y = 676 / (2 - exp(0.113 * (1-x)) ), where x = 0 for first entry */ /* more HP3400 firmware data */ static unsigned char abData0400[] = { 0xa4, 0x82, 0x00, 0x80, 0xa4, 0x82, 0xaa, 0x02, 0xc0, 0x02, 0xe8, 0x02, 0x3e, 0x03, 0xc8, 0x03, 0x58, 0x1b, 0xfe, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static void _ConvertMotorTable (unsigned char *pabOld, unsigned char *pabNew, int iSize, int iLpi) { int iData, i, iBit15; for (i = 0; i < (iSize / 2); i++) { iData = pabOld[2 * i + 0] + (pabOld[2 * i + 1] << 8); iBit15 = (iData & 0x8000); iData = (iData & 0x7FFF); if (iData <= 0x400) { iData = iData * iLpi / 300; } if (iBit15 != 0) { iData |= 0x8000; } pabNew[2 * i + 0] = iData & 255; pabNew[2 * i + 1] = (iData >> 8) & 255; } } /************************************************************************* _ProbeRegisters =============== Tries to determine certain hardware properties. This is done by checking the writeability of some scanner registers. We cannot rely simply on the scanner model to contain a specific chip. The HP3300c for example uses one of at least three slightly different scanner ASICs (NIASH00012, NIASH00013 and NIASH00014). OUT pHWParams Hardware parameters, updated fields: fGamma16 TRUE if 16 bit gamma tables can be used fReg07 TRUE if reg07 is writeable iBufferSize Size of scanner's internal buffer Returns TRUE if a NIASH chipset was found. *************************************************************************/ static SANE_Bool _ProbeRegisters (THWParams * pHWParams) { unsigned char bData1, bData2; int iHandle; iHandle = pHWParams->iXferHandle; DBG (DBG_MSG, "Probing scanner...\n"); /* check register 0x04 */ NiashWriteReg (iHandle, 0x04, 0x55); NiashReadReg (iHandle, 0x04, &bData1); NiashWriteReg (iHandle, 0x04, 0xAA); NiashReadReg (iHandle, 0x04, &bData2); NiashWriteReg (iHandle, 0x04, 0x07); if ((bData1 != 0x55) || (bData2 != 0xAA)) { DBG (DBG_ERR, " No NIASH chipset found!\n"); return SANE_FALSE; } /* check writeability of register 3 bit 1 */ NiashReadReg (iHandle, 0x03, &bData1); NiashWriteReg (iHandle, 0x03, bData1 | 0x02); NiashReadReg (iHandle, 0x03, &bData2); NiashWriteReg (iHandle, 0x03, bData1); pHWParams->fGamma16 = ((bData2 & 0x02) != 0); DBG (DBG_MSG, " Gamma table entries are %d bit\n", pHWParams->fGamma16 ? 16 : 8); /* check register 0x07 */ NiashReadReg (iHandle, 0x07, &bData1); NiashWriteReg (iHandle, 0x07, 0x1C); NiashReadReg (iHandle, 0x07, &bData2); NiashWriteReg (iHandle, 0x07, bData1); pHWParams->fReg07 = (bData2 == 0x1C); if (!pHWParams->fGamma16) { /* internal scan buffer size is an educated guess, but seems to correlate well with the size calculated from several windows driver log files size = 128kB - 44088 unsigned chars (space required for gamma/calibration table) */ pHWParams->iBufferSize = 86984L; DBG (DBG_MSG, " NIASH version < 00014\n"); } else { pHWParams->iBufferSize = 0x60000L; if (!pHWParams->fReg07) { DBG (DBG_MSG, " NIASH version = 00014\n"); } else { DBG (DBG_MSG, " NIASH version > 00014\n"); } } return SANE_TRUE; } /* returns 0 on success, < 0 otherwise */ STATIC int NiashOpen (THWParams * pHWParams, const char *pszName) { int iXferHandle; iXferHandle = NiashXferOpen (pszName, &pHWParams->eModel); if (iXferHandle < 0) { DBG (DBG_ERR, "NiashXferOpen failed for '%s'\n", pszName); return -1; } pHWParams->iXferHandle = iXferHandle; NiashWakeup (pHWParams->iXferHandle); /* default HW params */ pHWParams->iSensorSkew = 8; pHWParams->iTopLeftX = 0; pHWParams->iTopLeftY = 3; pHWParams->fReg07 = SANE_FALSE; pHWParams->iSkipLines = 0; pHWParams->iExpTime = 5408; pHWParams->iReversedHead = SANE_TRUE; switch (pHWParams->eModel) { case eHp3300c: DBG (DBG_MSG, "Setting params for Hp3300\n"); pHWParams->iTopLeftX = 4; pHWParams->iTopLeftY = 11; pHWParams->iSkipLines = 14; break; case eHp3400c: case eHp4300c: DBG (DBG_MSG, "Setting params for Hp3400c/Hp4300c\n"); pHWParams->iTopLeftX = 3; pHWParams->iTopLeftY = 14; pHWParams->fReg07 = SANE_TRUE; break; case eAgfaTouch: DBG (DBG_MSG, "Setting params for AgfaTouch\n"); pHWParams->iReversedHead = SANE_FALSE; /* head not reversed on Agfa Touch */ pHWParams->iTopLeftX = 3; pHWParams->iTopLeftY = 10; pHWParams->iSkipLines = 7; break; case eUnknownModel: DBG (DBG_MSG, "Setting params for UnknownModel\n"); break; default: DBG (DBG_ERR, "ERROR: internal error! (%d)\n", (int) pHWParams->eModel); return -1; } /* autodetect some hardware properties */ if (!_ProbeRegisters (pHWParams)) { DBG (DBG_ERR, "_ProbeRegisters failed!\n"); return -1; } return 0; } STATIC void NiashClose (THWParams * pHWPar) { NiashXferClose (pHWPar->iXferHandle); pHWPar->iXferHandle = 0; } static void WriteRegWord (int iHandle, unsigned char bReg, SANE_Word wData) { NiashWriteReg (iHandle, bReg, wData & 0xFF); NiashWriteReg (iHandle, bReg + 1, (wData >> 8) & 0xFF); } /* calculate a 4096 unsigned char gamma table */ STATIC void CalcGamma (unsigned char *pabTable, double Gamma) { int i, iData; /* fill gamma table */ for (i = 0; i < 4096; i++) { iData = floor (256.0 * pow (((double) i / 4096.0), 1.0 / Gamma)); pabTable[i] = iData; } } /* Hp3400WriteFw ============= Writes data to scanners with a NIASH00019 chipset, e.g. gamma, calibration and motor control data. IN pabData pointer to firmware data iLen Size of firmware date (unsigned chars) iAddr Scanner address to write to */ static void Hp3400cWriteFW (int iXferHandle, unsigned char *pabData, int iLen, int iAddr) { iAddr--; NiashWriteReg (iXferHandle, 0x21, iAddr & 0xFF); NiashWriteReg (iXferHandle, 0x22, (iAddr >> 8) & 0xFF); NiashWriteReg (iXferHandle, 0x23, (iAddr >> 16) & 0xFF); NiashWriteBulk (iXferHandle, pabData, iLen); } /* Writes the gamma and offset/gain tables to the scanner. In case a calibration file exist, it will be used for offset/gain */ STATIC void WriteGammaCalibTable (unsigned char *pabGammaR, unsigned char *pabGammaG, unsigned char *pabGammaB, unsigned char *pabCalibTable, int iGain, int iOffset, THWParams * pHWPar) { int i, j, k; static unsigned char abGamma[60000]; int iData; int iHandle; iHandle = pHWPar->iXferHandle; j = 0; /* fill gamma table for red component */ /* pad entries with 0 for 16-bit gamma table */ for (i = 0; i < 4096; i++) { if (pHWPar->fGamma16) { abGamma[j++] = 0; } abGamma[j++] = pabGammaR[i]; } /* fill gamma table for green component */ for (i = 0; i < 4096; i++) { if (pHWPar->fGamma16) { abGamma[j++] = 0; } abGamma[j++] = pabGammaG[i]; } /* fill gamma table for blue component */ for (i = 0; i < 4096; i++) { if (pHWPar->fGamma16) { abGamma[j++] = 0; } abGamma[j++] = pabGammaB[i]; } if (pabCalibTable == NULL) { iData = (iGain << 6) + iOffset; for (i = 0; i < HW_PIXELS; i++) { for (k = 0; k < 3; k++) { abGamma[j++] = (iData) & 255; abGamma[j++] = (iData >> 8) & 255; } } } else { memcpy (&abGamma[j], pabCalibTable, HW_PIXELS * 6); j += HW_PIXELS * 6; } NiashWriteReg (iHandle, 0x02, 0x80); NiashWriteReg (iHandle, 0x03, 0x01); NiashWriteReg (iHandle, 0x03, 0x11); NiashWriteReg (iHandle, 0x02, 0x84); if (pHWPar->fReg07) { Hp3400cWriteFW (iHandle, abGamma, j, 0x2000); } else { NiashWriteBulk (iHandle, abGamma, j); } NiashWriteReg (iHandle, 0x02, 0x80); } static void WriteAFEReg (int iHandle, int iReg, int iData) { NiashWriteReg (iHandle, 0x25, iReg); NiashWriteReg (iHandle, 0x26, iData); } /* setup the analog front-end -> coarse calibration */ static void WriteAFE (int iHandle) { /* see WM8143 datasheet */ WriteAFEReg (iHandle, 0x04, 0x00); WriteAFEReg (iHandle, 0x03, 0x12); WriteAFEReg (iHandle, 0x02, 0x04); WriteAFEReg (iHandle, 0x05, 0x10); WriteAFEReg (iHandle, 0x01, 0x03); WriteAFEReg (iHandle, 0x20, 0xc0); /*c8 *//* red offset */ WriteAFEReg (iHandle, 0x21, 0xc0); /*c8 *//* green offset */ WriteAFEReg (iHandle, 0x22, 0xc0); /*d0 *//* blue offset */ WriteAFEReg (iHandle, 0x28, 0x05); /*5 *//* red gain */ WriteAFEReg (iHandle, 0x29, 0x03); /*3 *//* green gain */ WriteAFEReg (iHandle, 0x2A, 0x04); /*4 *//* blue gain */ } /* wait for the carriage to return */ static void WaitReadyBit (int iHandle) { unsigned char bData; do { NiashReadReg (iHandle, 0x03, &bData); } while ((bData & 8) == 0); } /* Initialisation specific for NIASH00014 and lower chips */ static void InitNiash00014 (TScanParams * pParams, THWParams * pHWParams) { int iHandle, iLpiCode; iHandle = pHWParams->iXferHandle; /* exposure time (in units 24/Fcrystal)? */ WriteRegWord (iHandle, 0x08, pHWParams->iExpTime - 1); /* width in pixels */ WriteRegWord (iHandle, 0x12, pParams->iWidth - 1); /* top */ WriteRegWord (iHandle, 0x17, pParams->iTop); WriteRegWord (iHandle, 0x19, pParams->iTop); /* time between stepper motor steps (in units of 24/Fcrystal)? */ iLpiCode = pParams->iLpi * pHWParams->iExpTime / 1200L; if (!pHWParams->fGamma16) { /* NIASH 00012 / 00013 init */ /* LPI specific settings */ if (pParams->iLpi < 600) { /* set halfres bit */ NiashWriteReg (iHandle, 0x06, 0x01); /* double lpi code because of halfres bit */ iLpiCode *= 2; } else { /* clear halfres bit */ NiashWriteReg (iHandle, 0x06, 0x00); /* add exptime to make it scan slower */ iLpiCode += pHWParams->iExpTime; } /* unknown setting */ WriteRegWord (iHandle, 0x27, 0x7FD2); WriteRegWord (iHandle, 0x29, 0x6421); } else { /* NIASH 00014 init */ /* halfres bit always cleared */ NiashWriteReg (iHandle, 0x06, 0x00); /* LPI specific settings */ if (pParams->iLpi >= 600) { /* add exptime to make it scan slower */ iLpiCode += pHWParams->iExpTime; } /* unknown setting */ WriteRegWord (iHandle, 0x27, 0xc862); /*c862 */ WriteRegWord (iHandle, 0x29, 0xb853); /*b853 */ } /* LPI code */ WriteRegWord (iHandle, 0x0A, iLpiCode - 1); /* backtrack reversing speed */ NiashWriteReg (iHandle, 0x1E, (iLpiCode - 1) / 32); } /* Initialisation specific for NIASH00019 chips */ static void InitNiash00019 (TScanParams * pParams, THWParams * pHWParams) { int iHandle, iLpiCode; static unsigned char abMotor[512]; iHandle = pHWParams->iXferHandle; /* exposure time (in units 24/Fcrystal)? */ WriteRegWord (iHandle, 0x08, pHWParams->iExpTime); /* width in pixels */ WriteRegWord (iHandle, 0x12, pParams->iWidth); /* ? */ WriteRegWord (iHandle, 0x27, 0xc862); /*c862 */ WriteRegWord (iHandle, 0x29, 0xb853); /*b853 */ /* specific handling of 150 dpi resolution */ if (pParams->iLpi == 150) { /* use 300 LPI but skip every other line */ pParams->iLpi = 300; NiashWriteReg (iHandle, 0x06, 0x01); } else { NiashWriteReg (iHandle, 0x06, 0x00); } /* DPI and position table */ NiashWriteReg (iHandle, 0x07, 0x02); _ConvertMotorTable (abData0000, abMotor, sizeof (abData0000), pParams->iLpi); Hp3400cWriteFW (iHandle, abMotor, sizeof (abData0000), 0x000); _ConvertMotorTable (abData0400, abMotor, sizeof (abData0400), pParams->iLpi); Hp3400cWriteFW (iHandle, abMotor, sizeof (abData0400), 0x400); /* backtrack reversing speed */ iLpiCode = pParams->iLpi * pHWParams->iExpTime / 1200L; NiashWriteReg (iHandle, 0x1E, (iLpiCode - 1) / 32); } /* Scanner initialisation common to all NIASH chips */ static void InitNiashCommon (TScanParams * pParams, THWParams * pHWParams) { int iWidthHW, iHandle, iMaxLevel; iHandle = pHWParams->iXferHandle; NiashWriteReg (iHandle, 0x02, 0x80); NiashWriteReg (iHandle, 0x03, 0x11); NiashWriteReg (iHandle, 0x01, 0x8B); NiashWriteReg (iHandle, 0x05, 0x01); /* dpi */ WriteRegWord (iHandle, 0x0C, pParams->iDpi); /* calculate width in units of HW resolution */ iWidthHW = pParams->iWidth * (HW_DPI / pParams->iDpi); /* set left and right limits */ if (pHWParams->iReversedHead) { /* head is reversed */ /* right */ WriteRegWord (iHandle, 0x0E, 3 * (HW_PIXELS - (pParams->iLeft + iWidthHW))); /* left */ WriteRegWord (iHandle, 0x10, 3 * (HW_PIXELS - pParams->iLeft) - 1); } else { /* head is not reversed */ /*left */ WriteRegWord (iHandle, 0x0E, 3 * pParams->iLeft); /* right */ WriteRegWord (iHandle, 0x10, 3 * (pParams->iLeft + iWidthHW) - 1); } /* bottom */ WriteRegWord (iHandle, 0x1B, pParams->iBottom); /* 0x393C); */ /* forward jogging speed */ NiashWriteReg (iHandle, 0x1D, 0x60); /* backtrack reversing speed? */ NiashWriteReg (iHandle, 0x2B, 0x15); /* backtrack distance */ if (pParams->iLpi < 600) { NiashWriteReg (iHandle, 0x1F, 0x30); } else { NiashWriteReg (iHandle, 0x1F, 0x18); } /* max buffer level before backtrace */ iMaxLevel = MIN (pHWParams->iBufferSize / pParams->iWidth, 250); NiashWriteReg (iHandle, 0x14, iMaxLevel - 1); /* lamp PWM, max = 0x1ff? */ WriteRegWord (iHandle, 0x2C, 0x01FF); /* not needed? */ NiashWriteReg (iHandle, 0x15, 0x90); /* 90 */ NiashWriteReg (iHandle, 0x16, 0x70); /* 70 */ WriteAFE (iHandle); WaitReadyBit (iHandle); NiashWriteReg (iHandle, 0x03, 0x05); NiashWriteReg (iHandle, 0x02, pParams->fCalib ? 0x88 : 0xA8); } /* write registers */ STATIC SANE_Bool InitScan (TScanParams * pParams, THWParams * pHWParams) { int iHeight; int iExpTime; TScanParams Params; /* check validity of scanparameters */ switch (pParams->iDpi) { case 150: case 300: case 600: break; default: DBG (DBG_ERR, "Invalid dpi (%d)\n", pParams->iDpi); return SANE_FALSE; } iHeight = (pParams->iBottom - pParams->iTop + 1); if (iHeight <= 0) { DBG (DBG_ERR, "Invalid height (%d)\n", iHeight); return SANE_FALSE; } if (pParams->iWidth <= 0) { DBG (DBG_ERR, "Invalid width (%d)\n", pParams->iWidth); return SANE_FALSE; } switch (pParams->iLpi) { case 150: case 300: case 600: break; default: DBG (DBG_ERR, "Invalid lpi (%d)\n", pParams->iLpi); return SANE_FALSE; } /* exposure time (in units of 24/Fcrystal?), must be divisible by 8 !!! */ iExpTime = 5408; if ((iExpTime % 8) != 0) { DBG (DBG_ERR, "Invalid exposure time (%d)\n", iExpTime); return SANE_FALSE; } /* *** Done checking scan parameters validity *** */ /* copy the parameters locally and make pParams point to the local copy */ memcpy (&Params, pParams, sizeof (Params)); pParams = &Params; if (!pHWParams->fReg07) { /* init NIASH00014 and lower */ InitNiash00014 (pParams, pHWParams); } else { /* init NIASH00019 */ InitNiash00019 (pParams, pHWParams); } /* common NIASH init */ InitNiashCommon (pParams, pHWParams); return SANE_TRUE; } /************************************************************************/ static SANE_Bool XferBufferGetLine (int iHandle, TDataPipe * p, unsigned char *pabLine, SANE_Bool fReturn) { unsigned char bData, bData2; SANE_Bool fJustDone = SANE_FALSE; /* all calculated transfers done ? */ if (p->iLinesLeft == 0) return SANE_FALSE; /* time for a fresh read? */ if (p->iCurLine == 0) { int iLines; iLines = p->iLinesPerXferBuf; /* read only as many lines as needed */ if (p->iLinesLeft > 0 && p->iLinesLeft <= iLines) { iLines = p->iLinesLeft; DBG (DBG_MSG, "\n"); DBG (DBG_MSG, "last bulk read\n"); if (iLines < p->iLinesPerXferBuf) { DBG (DBG_MSG, "reading reduced number of lines: %d instead of %d\n", iLines, p->iLinesPerXferBuf); } fJustDone = SANE_TRUE; } /* reading old buffer level */ NiashReadReg (iHandle, 0x20, &bData); NiashReadBulk (iHandle, p->pabXferBuf, iLines * p->iBytesPerLine); /* reding new buffer level */ NiashReadReg (iHandle, 0x20, &bData2); if (fJustDone && fReturn) { NiashWriteReg (iHandle, 0x02, 0x80); DBG (DBG_MSG, "returning scanner head\n"); } DBG (DBG_MSG, "buffer level = %3d, , buffer level = %3d\r", (int) bData, iLines * p->iBytesPerLine, (int) bData2); fflush (stdout); } /* copy one line */ if (pabLine != NULL) { memcpy (pabLine, &p->pabXferBuf[p->iCurLine * p->iBytesPerLine], p->iBytesPerLine); } /* advance pointer */ p->iCurLine = (p->iCurLine + 1) % p->iLinesPerXferBuf; /* one transfer line less to the XFerBuffer */ if (p->iLinesLeft > 0) --(p->iLinesLeft); return SANE_TRUE; } static void XferBufferInit (int iHandle, TDataPipe * p) { int i; p->pabXferBuf = (unsigned char *) malloc (XFER_BUF_SIZE); p->iCurLine = 0; /* skip garbage lines */ for (i = 0; i < p->iSkipLines; i++) { XferBufferGetLine (iHandle, p, NULL, SANE_FALSE); } } /* static procedure that fills the circular buffer in advance to any circular buffer data retrieval */ static void CircBufferFill (int iHandle, TDataPipe * p, SANE_Bool iReversedHead) { int i; for (i = 0; i < p->iLinesPerCircBuf; i++) { if (iReversedHead) { XferBufferGetLine (iHandle, p, &p->pabCircBuf[p->iRedLine * p->iBytesPerLine], SANE_FALSE); } else { XferBufferGetLine (iHandle, p, &p->pabCircBuf[p->iBluLine * p->iBytesPerLine], SANE_FALSE); } /* advance pointers */ p->iRedLine = (p->iRedLine + 1) % p->iLinesPerCircBuf; p->iGrnLine = (p->iGrnLine + 1) % p->iLinesPerCircBuf; p->iBluLine = (p->iBluLine + 1) % p->iLinesPerCircBuf; } } static void XferBufferExit (TDataPipe * p) { if (p->pabXferBuf != NULL) { free (p->pabXferBuf); p->pabXferBuf = NULL; } else { DBG (DBG_ERR, "XferBufExit: Xfer buffer not initialised!\n"); } } /* unscrambles a line: - combining the proper R, G and B lines and converting them to interpixel RGB - mirroring left to right */ static void _UnscrambleLine (unsigned char *pabLine, unsigned char *pabRed, unsigned char *pabGrn, unsigned char *pabBlu, int iWidth, SANE_Bool iReversedHead, int iScaleDownDpi, int iBufWeight) { /* never change an approved algorithm ... so take Bertriks original source for this special case */ if (iScaleDownDpi == 1 && iBufWeight == 0) { int i, j; if (iReversedHead) { /* reversed */ for (i = 0; i < iWidth; i++) { j = (iWidth - i) * 3; pabLine[j - 3] = pabRed[i]; pabLine[j - 2] = pabGrn[i + iWidth]; pabLine[j - 1] = pabBlu[i + iWidth * 2]; } } else { /* not reversed */ for (i = 0; i < iWidth; i++) { pabLine[3 * i] = pabRed[i]; pabLine[3 * i + 1] = pabGrn[i + iWidth]; pabLine[3 * i + 2] = pabBlu[i + iWidth * 2]; } } } else { int i, j; /* loop variables */ int c; /* color buffer accumulator for horizontal average */ /* initialize for incremental color buffer access */ int iInc = 1; int iStart = 0; /* set for "from the end to the front" of the circular color buffers */ if (iReversedHead) { iStart = iWidth - iScaleDownDpi; iInc = -1; } /* each pixel is the mean of iScaleDownDpi so set the skip width accordingly */ iInc *= iScaleDownDpi; for (i = iStart; i >= 0 && i < iWidth; i += iInc) { /* collect the red pixels */ for (c = j = 0; j < iScaleDownDpi; ++j) c += pabRed[i + j]; *pabLine = (*pabLine * iBufWeight + c / iScaleDownDpi) / (iBufWeight + 1); pabLine++; /* collect the green pixels */ for (c = j = 0; j < iScaleDownDpi; ++j) c += pabGrn[i + iWidth + j]; *pabLine = (*pabLine * iBufWeight + c / iScaleDownDpi) / (iBufWeight + 1); pabLine++; /* collect the blue pixels */ for (c = j = 0; j < iScaleDownDpi; ++j) c += pabBlu[i + 2 * iWidth + j]; *pabLine = (*pabLine * iBufWeight + c / iScaleDownDpi) / (iBufWeight + 1); pabLine++; } } } /* gets an unscrambled line from the circular buffer. the first couple of lines contain garbage, if fReturn==SANE_TRUE, the head will return automatically on an end of scan */ STATIC SANE_Bool CircBufferGetLineEx (int iHandle, TDataPipe * p, unsigned char *pabLine, SANE_Bool iReversedHead, SANE_Bool fReturn) { int iLineCount; for (iLineCount = 0; iLineCount < p->iScaleDownLpi; ++iLineCount) { if (iReversedHead) { if (!XferBufferGetLine (iHandle, p, &p->pabCircBuf[p->iRedLine * p->iBytesPerLine], fReturn)) return SANE_FALSE; } else { if (!XferBufferGetLine (iHandle, p, &p->pabCircBuf[p->iBluLine * p->iBytesPerLine], fReturn)) return SANE_FALSE; } if (pabLine != NULL) { _UnscrambleLine (pabLine, &p->pabCircBuf[p->iRedLine * p->iBytesPerLine], &p->pabCircBuf[p->iGrnLine * p->iBytesPerLine], &p->pabCircBuf[p->iBluLine * p->iBytesPerLine], p->iWidth * p->iScaleDownDpi, iReversedHead, p->iScaleDownDpi, iLineCount); } /* advance pointers */ p->iRedLine = (p->iRedLine + 1) % p->iLinesPerCircBuf; p->iGrnLine = (p->iGrnLine + 1) % p->iLinesPerCircBuf; p->iBluLine = (p->iBluLine + 1) % p->iLinesPerCircBuf; } return SANE_TRUE; } /* gets an unscrambled line from the circular buffer. the first couple of lines contain garbage */ STATIC SANE_Bool CircBufferGetLine (int iHandle, TDataPipe * p, unsigned char *pabLine, SANE_Bool iReversedHead) { return CircBufferGetLineEx (iHandle, p, pabLine, iReversedHead, SANE_FALSE); } /* try to keep the number of transfers the same, but make them all as good as possible the same size to avoid cranking in critical situations */ static int _OptimizeXferSize (int iLines, int iLinesPerXfer) { int iXfers; iXfers = (iLines + iLinesPerXfer - 1) / iLinesPerXfer; while (--iLinesPerXfer > 0 && (iLines + iLinesPerXfer - 1) / iLinesPerXfer == iXfers); return iLinesPerXfer + 1; } STATIC void CircBufferInit (int iHandle, TDataPipe * p, int iWidth, int iHeight, int iMisAlignment, SANE_Bool iReversedHead, int iScaleDownDpi, int iScaleDownLpi) { /* relevant for internal read and write functions */ p->iScaleDownLpi = iScaleDownLpi; p->iScaleDownDpi = iScaleDownDpi; p->iWidth = iWidth; p->iBytesPerLine = iWidth * iScaleDownDpi * BYTES_PER_PIXEL; p->iSaneBytesPerLine = iWidth * BYTES_PER_PIXEL; if (iMisAlignment == 0) { p->iLinesPerCircBuf = 1; } else { p->iLinesPerCircBuf = 3 * iMisAlignment; } DBG (DBG_MSG, "_iScaleDown (Dpi,Lpi) = (%d,%d)\n", p->iScaleDownDpi, p->iScaleDownLpi); DBG (DBG_MSG, "_iBytesPerLine = %d\n", p->iBytesPerLine); DBG (DBG_MSG, "_iLinesPerCircBuf = %d\n", p->iLinesPerCircBuf); p->pabCircBuf = (unsigned char *) malloc (p->iBytesPerLine * p->iLinesPerCircBuf); if (p->pabCircBuf == NULL) { DBG (DBG_ERR, "Unable to allocate %d unsigned chars for circular buffer\n", (int) (p->iBytesPerLine * p->iLinesPerCircBuf)); return; } DBG (DBG_MSG, "Allocated %d unsigned chars for circular buffer\n", p->iBytesPerLine * p->iLinesPerCircBuf); if (iReversedHead) { p->iBluLine = 0; p->iGrnLine = iMisAlignment; p->iRedLine = iMisAlignment * 2; } else { p->iRedLine = 0; p->iGrnLine = iMisAlignment; p->iBluLine = iMisAlignment * 2; } /* negative height is an indication for "no Check" */ if (iHeight < 0) { p->iLinesLeft = -1; p->iLinesPerXferBuf = XFER_BUF_SIZE / p->iBytesPerLine; DBG (DBG_MSG, "using unchecked XFER_BUF_SIZE\n"); DBG (DBG_MSG, "_iXFerSize = %d\n", p->iBytesPerLine * p->iLinesPerXferBuf); } else { #define SAFETY_LINES 0 #define MAX_LINES_PER_XFERBUF 800 /* estimate of number of unsigned chars to transfer at all via the USB */ /* add some lines for security */ p->iLinesLeft = iHeight + p->iSkipLines + p->iLinesPerCircBuf + SAFETY_LINES; p->iLinesPerXferBuf = XFER_BUF_SIZE / p->iBytesPerLine; /* with more than 800 lines the timing is spoiled */ if (p->iLinesPerXferBuf > MAX_LINES_PER_XFERBUF) { p->iLinesPerXferBuf = MAX_LINES_PER_XFERBUF; } /* final optimization to keep critical scans smooth */ p->iLinesPerXferBuf = _OptimizeXferSize (p->iLinesLeft, p->iLinesPerXferBuf); DBG (DBG_MSG, "_iXFerSize = %d for %d transfer(s)\n", (int) p->iLinesPerXferBuf * p->iBytesPerLine, (p->iLinesLeft + p->iLinesPerXferBuf - 1) / p->iLinesPerXferBuf); } DBG (DBG_MSG, "_iLinesPerXferBuf = %d\n", p->iLinesPerXferBuf); /* init transfer buffer */ XferBufferInit (iHandle, p); /* fill circular buffer */ CircBufferFill (iHandle, p, iReversedHead); } STATIC void CircBufferExit (TDataPipe * p) { XferBufferExit (p); if (p->pabCircBuf != NULL) { DBG (DBG_MSG, "\n"); free (p->pabCircBuf); p->pabCircBuf = NULL; } else { DBG (DBG_ERR, "CircBufferExit: Circular buffer not initialised!\n"); } } /************************************************************************/ static int _CalcAvg (unsigned char *pabBuf, int n, int iStep) { int i, j, x; for (i = j = x = 0; i < n; i++) { x += pabBuf[j]; j += iStep; } return (x / n); } /* converts white line data and black point data into a calibration table */ static void CreateCalibTable (unsigned char *abWhite, unsigned char bBlackR, unsigned char bBlackG, unsigned char bBlackB, int iReversedHead, unsigned char *pabCalibTable) { int i, j, iGain, iOffset, iData; unsigned char *pabPixel; j = 0; for (i = 0; i < HW_PIXELS; i++) { if (iReversedHead) { pabPixel = &abWhite[(HW_PIXELS - i - 1) * 3]; } else { pabPixel = &abWhite[i * 3]; } /* red */ if (bBlackR > 16) bBlackR = 16; iGain = 65536 / MAX (1, pabPixel[0] - bBlackR); iOffset = bBlackR * 4; if (iOffset > 63) iOffset = 63; iData = (iGain << 6) + iOffset; pabCalibTable[j++] = (iData) & 255; pabCalibTable[j++] = (iData >> 8) & 255; /* green */ if (bBlackG > 16) bBlackG = 16; iGain = 65536 / MAX (1, pabPixel[1] - bBlackG); iOffset = bBlackG * 4; if (iOffset > 63) iOffset = 63; iData = (iGain << 6) + iOffset; pabCalibTable[j++] = (iData) & 255; pabCalibTable[j++] = (iData >> 8) & 255; /* blue */ if (bBlackB > 16) bBlackB = 16; iGain = 65536 / MAX (1, pabPixel[2] - bBlackB); iOffset = bBlackB * 4; if (iOffset > 63) iOffset = 63; iData = (iGain << 6) + iOffset; pabCalibTable[j++] = (iData) & 255; pabCalibTable[j++] = (iData >> 8) & 255; } } /************************************************************************* Lamp control functions *************************************************************************/ STATIC SANE_Bool GetLamp (THWParams * pHWParams, SANE_Bool * pfLampIsOn) { unsigned char bData; NiashReadReg (pHWParams->iXferHandle, 0x03, &bData); *pfLampIsOn = ((bData & 0x01) != 0); return SANE_TRUE; } STATIC SANE_Bool SetLamp (THWParams * pHWParams, SANE_Bool fLampOn) { unsigned char bData; int iHandle; iHandle = pHWParams->iXferHandle; NiashReadReg (iHandle, 0x03, &bData); if (fLampOn) { NiashWriteReg (iHandle, 0x03, bData | 0x01); } else { NiashWriteReg (iHandle, 0x03, bData & ~0x01); } return SANE_TRUE; } /************************************************************************* Experimental simple calibration, but also returning the white levels *************************************************************************/ STATIC SANE_Bool SimpleCalibExt (THWParams * pHWPar, unsigned char *pabCalibTable, unsigned char *pabCalWhite) { unsigned char bMinR, bMinG, bMinB; TDataPipe DataPipe; TScanParams Params; unsigned char abGamma[4096]; int i, j; static unsigned char abBuf[HW_PIXELS * 3 * 71]; /* Careful : see startWhite and endWhite below */ static unsigned char abLine[HW_PIXELS * 3]; static unsigned char abWhite[HW_PIXELS * 3]; unsigned char *pabWhite; int iWhiteR, iWhiteG, iWhiteB; int iHandle; SANE_Bool iReversedHead; int startWhiteY, endWhiteY; int startBlackY, endBlackY; int endBlackX; iHandle = pHWPar->iXferHandle; iReversedHead = pHWPar->iReversedHead; DataPipe.iSkipLines = pHWPar->iSkipLines; Params.iDpi = HW_DPI; Params.iLpi = HW_DPI; if (iReversedHead) /* hp scanners */ Params.iTop = 60; else /* agfa scanners */ Params.iTop = 30; Params.iBottom = HP3300C_BOTTOM; Params.iLeft = 0; Params.iWidth = HW_PIXELS; Params.iHeight = 54; Params.fCalib = SANE_TRUE; /* write gamma table with neutral gain / offset */ CalcGamma (abGamma, 1.0); WriteGammaCalibTable (abGamma, abGamma, abGamma, NULL, 256, 0, pHWPar); if (!InitScan (&Params, pHWPar)) { if (pabCalWhite) pabCalWhite[0] = pabCalWhite[1] = pabCalWhite[2] = 0; return SANE_FALSE; } /* Definition of white and black areas */ if (iReversedHead) { /* hp scanners */ startWhiteY = 0; endWhiteY = 15; startBlackY = 16; endBlackY = 135; endBlackX = HW_PIXELS; } else { /* agfa scanners */ startWhiteY = 0; endWhiteY = 70; startBlackY = 86; endBlackY = 135; endBlackX = 3374; } CircBufferInit (iHandle, &DataPipe, HW_PIXELS, -1, Params.iLpi / 150, iReversedHead, 1, 1); /* white level */ /* skip some lines */ for (i = 0; i < startWhiteY; i++) { CircBufferGetLine (iHandle, &DataPipe, abLine, iReversedHead); } /* Get white lines */ for (i = 0; i < endWhiteY - startWhiteY + 1; i++) { CircBufferGetLine (iHandle, &DataPipe, &abBuf[i * HW_PIXELS * 3], iReversedHead); } /* black level */ bMinR = 255; bMinG = 255; bMinB = 255; /* Skip some lines */ for (i = 0; i < startBlackY; i++) { CircBufferGetLine (iHandle, &DataPipe, abLine, iReversedHead); } for (i = 0; i < endBlackY - startBlackY + 1; i++) { CircBufferGetLine (iHandle, &DataPipe, abLine, iReversedHead); for (j = 0; j < endBlackX; j++) { bMinR = MIN (abLine[j * 3 + 0], bMinR); bMinG = MIN (abLine[j * 3 + 1], bMinG); bMinB = MIN (abLine[j * 3 + 2], bMinB); } } CircBufferExit (&DataPipe); FinishScan (pHWPar); /* calc average white level */ pabWhite = abBuf; for (i = 0; i < HW_PIXELS; i++) { abWhite[i * 3 + 0] = _CalcAvg (&pabWhite[i * 3 + 0], endWhiteY - startWhiteY + 1, HW_PIXELS * 3); abWhite[i * 3 + 1] = _CalcAvg (&pabWhite[i * 3 + 1], endWhiteY - startWhiteY + 1, HW_PIXELS * 3); abWhite[i * 3 + 2] = _CalcAvg (&pabWhite[i * 3 + 2], endWhiteY - startWhiteY + 1, HW_PIXELS * 3); } iWhiteR = _CalcAvg (&abWhite[0], HW_PIXELS, 3); iWhiteG = _CalcAvg (&abWhite[1], HW_PIXELS, 3); iWhiteB = _CalcAvg (&abWhite[2], HW_PIXELS, 3); DBG (DBG_MSG, "Black level (%d,%d,%d), White level (%d,%d,%d)\n", (int) bMinR, (int) bMinG, (int) bMinB, iWhiteR, iWhiteG, iWhiteB); /* convert the white line and black point into a calibration table */ CreateCalibTable (abWhite, bMinR, bMinG, bMinB, iReversedHead, pabCalibTable); /* assign the White Levels */ if (pabCalWhite) { pabCalWhite[0] = iWhiteR; pabCalWhite[1] = iWhiteG; pabCalWhite[2] = iWhiteB; } return SANE_TRUE; } /************************************************************************* FinishScan ========== Finishes the scan. Makes the scanner head move back to the home position. *************************************************************************/ STATIC void FinishScan (THWParams * pHWParams) { NiashWriteReg (pHWParams->iXferHandle, 0x02, 0x80); } backends-1.3.0/backend/niash_core.h000066400000000000000000000111471456256263500171610ustar00rootroot00000000000000/* Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl) 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, see . */ /* Core NIASH chip functions. */ #ifndef _NIASH_CORE_H_ #define _NIASH_CORE_H_ #include #include "niash_xfer.h" /* for EScannerModel */ #define HP3300C_RIGHT 330 #define HP3300C_TOP 452 #define HP3300C_BOTTOM (HP3300C_TOP + 14200UL) #define HW_PIXELS 5300 /* number of pixels supported by hardware */ #define HW_DPI 600 /* horizontal resolution of hardware */ #define HW_LPI 1200 /* vertical resolution of hardware */ #define BYTES_PER_PIXEL 3 typedef struct { int iXferHandle; /* handle used for data transfer to HW */ int iTopLeftX; /* in mm */ int iTopLeftY; /* in mm */ int iSensorSkew; /* in units of 1/1200 inch */ int iSkipLines; /* lines of garbage to skip */ SANE_Bool fReg07; /* NIASH00019 */ SANE_Bool fGamma16; /* if TRUE, gamma entries are 16 bit */ int iExpTime; SANE_Bool iReversedHead; /* Head is reversed */ int iBufferSize; /* Size of internal scan buffer */ EScannerModel eModel; } THWParams; typedef struct { int iDpi; /* horizontal resolution */ int iLpi; /* vertical resolution */ int iTop; /* in HW coordinates */ int iLeft; /* in HW coordinates */ int iWidth; /* pixels */ int iHeight; /* lines */ int iBottom; int fCalib; /* if TRUE, disable backtracking? */ } TScanParams; typedef struct { unsigned char *pabXferBuf; /* transfer buffer */ int iCurLine; /* current line in the transfer buffer */ int iBytesPerLine; /* unsigned chars in one scan line */ int iLinesPerXferBuf; /* number of lines held in the transfer buffer */ int iLinesLeft; /* transfer (down) counter for pabXFerBuf */ int iSaneBytesPerLine; /* how many unsigned chars to be read by SANE per line */ int iScaleDownDpi; /* factors used to emulate lower resolutions */ int iScaleDownLpi; /* than those offered by hardware */ int iSkipLines; /* line to skip at the start of scan */ int iWidth; /* number of pixels expected by SANE */ unsigned char *pabCircBuf; /* circular buffer */ int iLinesPerCircBuf; /* lines held in the circular buffer */ int iRedLine, iGrnLine, /* start indices for the color information */ iBluLine; /* in the circular buffer */ unsigned char *pabLineBuf; /* buffer used to pass data to SANE */ } TDataPipe; STATIC int NiashOpen (THWParams * pHWParams, const char *pszName); STATIC void NiashClose (THWParams * pHWParams); /* more sof. method that also returns the values of the white (RGB) value */ STATIC SANE_Bool SimpleCalibExt (THWParams * pHWPar, unsigned char *pabCalibTable, unsigned char *pabCalWhite); STATIC SANE_Bool GetLamp (THWParams * pHWParams, SANE_Bool * pfLampIsOn); STATIC SANE_Bool SetLamp (THWParams * pHWParams, SANE_Bool fLampOn); STATIC SANE_Bool InitScan (TScanParams * pParams, THWParams * pHWParams); STATIC void FinishScan (THWParams * pHWParams); STATIC void CalcGamma (unsigned char *pabTable, double Gamma); STATIC void WriteGammaCalibTable (unsigned char *pabGammaR, unsigned char *pabGammaG, unsigned char *pabGammaB, unsigned char *pabCalibTable, int iGain, int iOffset, THWParams * pHWPar); /* set -1 for iHeight to disable all checks on buffer transfers */ /* iWidth is in pixels of SANE */ /* iHeight is lines in scanner resolution */ STATIC void CircBufferInit (int iHandle, TDataPipe * p, int iWidth, int iHeight, int iMisAlignment, SANE_Bool iReversedHead, int iScaleDownDpi, int iScaleDownLpi); /* returns false, when trying to read after end of buffer */ STATIC SANE_Bool CircBufferGetLine (int iHandle, TDataPipe * p, unsigned char *pabLine, SANE_Bool iReversedHead); /* returns false, when trying to read after end of buffer if fReturn==SANE_TRUE, the head will return automatically on an end of scan */ STATIC SANE_Bool CircBufferGetLineEx (int iHandle, TDataPipe * p, unsigned char *pabLine, SANE_Bool iReversedHead, SANE_Bool fReturn); STATIC void CircBufferExit (TDataPipe * p); #endif /* _NIASH_CORE_H_ */ backends-1.3.0/backend/niash_xfer.c000066400000000000000000000212041456256263500171630ustar00rootroot00000000000000/* Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl) 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, see . */ /* 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 "niash_xfer.h" #include "../include/sane/sanei_usb.h" /* list of supported models */ STATIC TScannerModel ScannerModels[] = { {"Hewlett-Packard", "ScanJet 3300C", 0x3F0, 0x205, eHp3300c} , {"Hewlett-Packard", "ScanJet 3400C", 0x3F0, 0x405, eHp3400c} , {"Hewlett-Packard", "ScanJet 4300C", 0x3F0, 0x305, eHp4300c} , {"Silitek Corp.", "HP ScanJet 4300c", 0x47b, 0x1002, eHp3400c} , {"Agfa", "Snapscan Touch", 0x6BD, 0x100, eAgfaTouch} , {"Trust", "Office Scanner USB 19200", 0x47b, 0x1000, eAgfaTouch} , /* last entry all zeros */ {0, 0, 0, 0, 0} }; static TFnReportDevice *_pfnReportDevice; static TScannerModel *_pModel; /* MatchUsbDevice ============== Matches a given USB vendor and product id against a list of supported scanners. IN iVendor USB vendor ID iProduct USB product ID OUT *ppModel Pointer to TScannerModel structure Returns TRUE if a matching USB scanner was found */ STATIC SANE_Bool MatchUsbDevice (int iVendor, int iProduct, TScannerModel ** ppModel) { TScannerModel *pModels = ScannerModels; DBG (DBG_MSG, "Matching USB device 0x%04X-0x%04X ... ", iVendor, iProduct); 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; } /* next model to match */ pModels++; } DBG (DBG_MSG, "nothing found\n"); return SANE_FALSE; } /************************************************************************ Public functions for the SANE compilation ************************************************************************/ /* callback for sanei_usb_attach_matching_devices */ static SANE_Status _AttachUsb (SANE_String_Const devname) { DBG (DBG_MSG, "_AttachUsb: found %s\n", devname); _pfnReportDevice (_pModel, (const char *) devname); return SANE_STATUS_GOOD; } /* NiashXferInit =============== 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 */ static void NiashXferInit (TFnReportDevice * pfnReport) { TScannerModel *pModels = ScannerModels; sanei_usb_init (); _pfnReportDevice = pfnReport; /* loop over all scanner models */ while (pModels->pszName != NULL) { 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; } pModels++; } } static int NiashXferOpen (const char *pszName, EScannerModel * peModel) { SANE_Status status; SANE_Word vendor, product; int fd; TScannerModel *pModel = 0; DBG (DBG_MSG, "Trying to open %s...\n", pszName); status = sanei_usb_open (pszName, &fd); if (status != SANE_STATUS_GOOD) { return -1; } status = sanei_usb_get_vendor_product (fd, &vendor, &product); if (status == SANE_STATUS_GOOD) { MatchUsbDevice (vendor, product, &pModel); *peModel = pModel->eModel; } DBG (DBG_MSG, "handle = %d\n", (int) fd); return fd; } static void NiashXferClose (int iHandle) { /* close usb device */ if (iHandle != -1) { sanei_usb_close (iHandle); } } static void 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); } static void 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); } static void NiashWriteReg (int iHandle, unsigned char bReg, unsigned char bData) { if (iHandle < 0) { DBG (DBG_MSG, "Invalid handle %d\n", iHandle); return; } parusb_write_reg (iHandle, SPP_CONTROL, 0x14); parusb_write_reg (iHandle, EPP_ADDR, bReg); parusb_write_reg (iHandle, SPP_CONTROL, 0x14); parusb_write_reg (iHandle, EPP_DATA_WRITE, bData); parusb_write_reg (iHandle, SPP_CONTROL, 0x14); } static void NiashReadReg (int iHandle, unsigned char bReg, unsigned char *pbData) { if (iHandle < 0) { return; } parusb_write_reg (iHandle, SPP_CONTROL, 0x14); parusb_write_reg (iHandle, EPP_ADDR, bReg); parusb_write_reg (iHandle, SPP_CONTROL, 0x34); parusb_read_reg (iHandle, EPP_DATA_READ, pbData); parusb_write_reg (iHandle, SPP_CONTROL, 0x14); } static void NiashWriteBulk (int iHandle, unsigned char *pabBuf, int iSize) { /* byte abSetup[8] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; HP3400 probably needs 0x01, 0x01 */ SANE_Byte abSetup[8] = { 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; size_t size; if (iHandle < 0) { return; } /* select scanner register 0x24 */ parusb_write_reg (iHandle, SPP_CONTROL, 0x14); parusb_write_reg (iHandle, EPP_ADDR, 0x24); parusb_write_reg (iHandle, SPP_CONTROL, 0x14); /* tell scanner that a bulk transfer follows */ 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); /* do the bulk write */ size = iSize; if (sanei_usb_write_bulk (iHandle, pabBuf, &size) != SANE_STATUS_GOOD) { DBG (DBG_ERR, "ERROR: Bulk write failed\n"); } } static void NiashReadBulk (int iHandle, unsigned char *pabBuf, int iSize) { SANE_Byte abSetup[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; size_t size; if (iHandle < 0) { return; } /* select scanner register 0x24 */ parusb_write_reg (iHandle, SPP_CONTROL, 0x14); parusb_write_reg (iHandle, EPP_ADDR, 0x24); parusb_write_reg (iHandle, SPP_CONTROL, 0x14); /* tell scanner that a bulk transfer follows */ 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); /* do the bulk read */ size = iSize; if (sanei_usb_read_bulk (iHandle, pabBuf, &size) != SANE_STATUS_GOOD) { DBG (DBG_ERR, "ERROR: Bulk read failed\n"); } } static void NiashWakeup (int iHandle) { unsigned char abMagic[] = { 0xA0, 0xA8, 0x50, 0x58, 0x90, 0x98, 0xC0, 0xC8, 0x90, 0x98, 0xE0, 0xE8 }; int i; if (iHandle < 0) { return; } /* write magic startup sequence */ parusb_write_reg (iHandle, SPP_CONTROL, 0x14); for (i = 0; i < (int) sizeof (abMagic); i++) { parusb_write_reg (iHandle, SPP_DATA, abMagic[i]); } /* write 0x04 to scanner register 0x00 the hard way */ parusb_write_reg (iHandle, SPP_DATA, 0x00); parusb_write_reg (iHandle, SPP_CONTROL, 0x14); parusb_write_reg (iHandle, SPP_CONTROL, 0x15); parusb_write_reg (iHandle, SPP_CONTROL, 0x1D); parusb_write_reg (iHandle, SPP_CONTROL, 0x15); parusb_write_reg (iHandle, SPP_CONTROL, 0x14); parusb_write_reg (iHandle, SPP_DATA, 0x04); parusb_write_reg (iHandle, SPP_CONTROL, 0x14); parusb_write_reg (iHandle, SPP_CONTROL, 0x15); parusb_write_reg (iHandle, SPP_CONTROL, 0x17); parusb_write_reg (iHandle, SPP_CONTROL, 0x15); parusb_write_reg (iHandle, SPP_CONTROL, 0x14); } backends-1.3.0/backend/niash_xfer.h000066400000000000000000000050411456256263500171710ustar00rootroot00000000000000/* Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl) 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, see . */ /* Provides a simple interface to read and write data from the scanner, without any knowledge whether it's a parallel or USB scanner */ #ifndef _NIASH_XFER_H_ #define _NIASH_XFER_H_ #include /* for FILE * */ /* register codes for the USB - IEEE1284 bridge */ #define USB_SETUP 0x82 #define EPP_ADDR 0x83 #define EPP_DATA_READ 0x84 #define EPP_DATA_WRITE 0x85 #define SPP_STATUS 0x86 #define SPP_CONTROL 0x87 #define SPP_DATA 0x88 typedef enum { eUnknownModel = 0, eHp3300c, eHp3400c, eHp4300c, eAgfaTouch } EScannerModel; typedef struct { char *pszVendor; char *pszName; int iVendor; int iProduct; EScannerModel eModel; } TScannerModel; typedef int (TFnReportDevice) (TScannerModel * pModel, const char *pszDeviceName); /* Creates our own DBG definitions, externs are define in main.c*/ #ifndef WITH_NIASH #define DBG fprintf extern FILE *DBG_MSG; extern FILE *DBG_ERR; extern FILE *BG_ASSERT; #endif /* NO WITH_NIASH */ /* we do not make data prototypes */ #ifndef WITH_NIASH /* list of supported models, the actual list is in niash_xfer.c */ extern TScannerModel ScannerModels[]; #endif /* NO WITH_NIASH */ STATIC void NiashXferInit (TFnReportDevice * pfnReport); STATIC int NiashXferOpen (const char *pszName, EScannerModel * peModel); STATIC void NiashXferClose (int iXferHandle); STATIC void NiashWriteReg (int iXferHandle, unsigned char bReg, unsigned char bData); STATIC void NiashReadReg (int iXferHandle, unsigned char bReg, unsigned char *pbData); STATIC void NiashWriteBulk (int iXferHandle, unsigned char *pabBuf, int iSize); STATIC void NiashReadBulk (int iXferHandle, unsigned char *pabBuf, int iSize); STATIC void NiashWakeup (int iXferHandle); STATIC SANE_Bool MatchUsbDevice (int iVendor, int iProduct, TScannerModel ** ppeModel); #endif /* _NIASH_XFER_H_ */ backends-1.3.0/backend/p5.c000066400000000000000000001762621456256263500154000ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2009-12 Stéphane Voltz 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, see . */ /* -------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */ /*! \mainpage Primax PagePartner Parallel Port scanner Index Page * * \section intro_sec Introduction * * This backend provides support for the Prima PagePartner sheet fed parallel * port scanner. * * \section sane_api SANE API * * \subsection sane_flow sane flow SANE FLOW - sane_init() : initialize backend, attach scanners. - sane_get_devices() : query list of scanner devices, backend must probe for new devices. - sane_open() : open a particular scanner device, adding a handle to the opened 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 for the ongoing scan - sane_read() : read image data - sane_cancel() : cancel operation, end scan - sane_close() : close opened scanner device, freeing scanner handle - sane_exit() : terminate use of backend, freeing all resources for attached devices when last frontend quits */ /** * the build number allow to know which version of the backend is running. */ #define BUILD 2301 #include "p5.h" /** * Import directly the low level part needed to * operate scanner. The alternative is to prefix all public functions * with sanei_p5_ ,and have all the functions prototyped in * p5_device.h . */ #include "p5_device.c" /** * number of time the backend has been loaded by sane_init. */ static int init_count = 0; /** * NULL terminated list of opened frontend sessions. Sessions are * inserted here on sane_open() and removed on sane_close(). */ static P5_Session *sessions = NULL; /** * NULL terminated list of detected physical devices. * The same device may be opened several time by different sessions. * Entry are inserted here by the attach() function. * */ static P5_Device *devices = NULL; /** * NULL terminated list of devices needed by sane_get_devices(), since * the result returned must stay consistent until next call. */ static const SANE_Device **devlist = 0; /** * list of possible color modes */ static SANE_String_Const mode_list[] = { SANE_I18N (COLOR_MODE), SANE_I18N (GRAY_MODE), /* SANE_I18N (LINEART_MODE), not supported yet */ 0 }; static SANE_Range x_range = { SANE_FIX (0.0), /* minimum */ SANE_FIX (216.0), /* maximum */ SANE_FIX (0.0) /* quantization */ }; static SANE_Range y_range = { SANE_FIX (0.0), /* minimum */ SANE_FIX (299.0), /* maximum */ SANE_FIX (0.0) /* no quantization */ }; /** * finds the maximum string length in a string 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; } /**> placeholders for decoded configuration values */ static P5_Config p5cfg; /* ------------------------------------------------------------------------- */ /* * 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) { SANE_Status status; (void) authorize; /* get rid of compiler warning */ init_count++; /* init backend debug */ DBG_INIT (); DBG (DBG_info, "SANE P5 backend version %d.%d-%d\n", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); DBG (DBG_proc, "sane_init: start\n"); DBG (DBG_trace, "sane_init: init_count=%d\n", init_count); if (version_code) *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD); /* cold-plugging case : probe for already plugged devices */ status = probe_p5_devices (); DBG (DBG_proc, "sane_init: exit\n"); return status; } /** * 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. * @param device_list pointer where to store the device list * @param local_only SANE_TRUE if only local devices are required. * @return SANE_STATUS_GOOD when successful */ SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) { int dev_num, devnr; struct P5_Device *device; SANE_Device *sane_device; int i; DBG (DBG_proc, "sane_get_devices: start: local_only = %s\n", local_only == SANE_TRUE ? "true" : "false"); /* free existing devlist first */ if (devlist) { for (i = 0; devlist[i] != NULL; i++) free ((void *)devlist[i]); free (devlist); devlist = NULL; } /** * Since sane_get_devices() may be called repeatedly to detect new devices, * the device detection must be run at each call. We are handling * hot-plugging : we probe for devices plugged since sane_init() was called. */ probe_p5_devices (); /* if no devices detected, just return an empty list */ if (devices == NULL) { devlist = malloc (sizeof (devlist[0])); if (!devlist) return SANE_STATUS_NO_MEM; devlist[0] = NULL; *device_list = devlist; DBG (DBG_proc, "sane_get_devices: exit with no device\n"); return SANE_STATUS_GOOD; } /* count physical devices */ devnr = 1; device = devices; while (device->next) { devnr++; device = device->next; } /* allocate room for the list, plus 1 for the NULL terminator */ devlist = malloc ((devnr + 1) * sizeof (devlist[0])); if (!devlist) return SANE_STATUS_NO_MEM; *device_list = devlist; dev_num = 0; device = devices; /* we build a list of SANE_Device from the list of attached devices */ for (i = 0; i < devnr; i++) { /* add device according to local only flag */ if ((local_only == SANE_TRUE && device->local == SANE_TRUE) || local_only == SANE_FALSE) { /* allocate memory to add the device */ sane_device = malloc (sizeof (*sane_device)); if (!sane_device) { return SANE_STATUS_NO_MEM; } /* copy data */ sane_device->name = device->name; sane_device->vendor = device->model->vendor; sane_device->model = device->model->product; sane_device->type = device->model->type; devlist[dev_num] = sane_device; /* increment device counter */ dev_num++; } /* go to next detected device */ device = device->next; } devlist[dev_num] = 0; *device_list = devlist; DBG (DBG_proc, "sane_get_devices: exit\n"); return SANE_STATUS_GOOD; } /** * Called to establish connection with the session. This function will * also establish meaningful defaults 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). Another special case is to only give * the name of the backend as the device name, in this case the first * available device will also be used. * @param name name of the device to open * @param handle opaque pointer where to store the pointer of * the opened P5_Session * @return SANE_STATUS_GOOD on success */ SANE_Status sane_open (SANE_String_Const name, SANE_Handle * handle) { struct P5_Session *session = NULL; struct P5_Device *device = NULL; DBG (DBG_proc, "sane_open: start (devicename=%s)\n", name); /* check there is at least a device */ if (devices == NULL) { DBG (DBG_proc, "sane_open: exit, no device to open!\n"); return SANE_STATUS_INVAL; } if (name[0] == 0 || strncmp (name, "p5", strlen ("p5")) == 0) { DBG (DBG_info, "sane_open: no specific device requested, using default\n"); if (devices) { device = devices; DBG (DBG_info, "sane_open: device %s used as default device\n", device->name); } } else { DBG (DBG_info, "sane_open: device %s requested\n", name); /* walk the device list until we find a matching name */ device = devices; while (device && strcmp (device->name, name) != 0) { DBG (DBG_trace, "sane_open: device %s doesn't match\n", device->name); device = device->next; } } /* check whether we have found a match or reach the end of the device list */ if (!device) { DBG (DBG_info, "sane_open: no device found\n"); return SANE_STATUS_INVAL; } /* now we have a device, duplicate it and return it in handle */ DBG (DBG_info, "sane_open: device %s found\n", name); /* device initialization */ if (device->initialized == SANE_FALSE) { /** * call to hardware initialization function here. */ device->fd = open_pp (device->name); if (device->fd < 0) { DBG (DBG_error, "sane_open: failed to open '%s' device!\n", device->name); return SANE_STATUS_INVAL; } /* now try to connect to scanner */ if (connect (device->fd) != SANE_TRUE) { DBG (DBG_error, "sane_open: failed to connect!\n"); close_pp (device->fd); return SANE_STATUS_INVAL; } /* load calibration data */ restore_calibration (device); /* device link is OK now */ device->initialized = SANE_TRUE; } device->buffer = NULL; device->gain = NULL; device->offset = NULL; /* prepare handle to return */ session = (P5_Session *) malloc (sizeof (P5_Session)); if (session == NULL) { DBG (DBG_proc, "sane_open: exit OOM\n"); return SANE_STATUS_NO_MEM; } /* initialize session */ session->dev = device; session->scanning = SANE_FALSE; session->non_blocking = SANE_FALSE; /* initialize SANE options for this session */ init_options (session); /* add the handle to the linked list of sessions */ session->next = sessions; sessions = session; /* store result */ *handle = session; /* exit success */ DBG (DBG_proc, "sane_open: exit\n"); return SANE_STATUS_GOOD; } /** * Set non blocking mode. In this mode, read return immediately when * no data is available within sane_read(), instead of polling the scanner. */ SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) { P5_Session *session = (P5_Session *) handle; DBG (DBG_proc, "sane_set_io_mode: start\n"); if (session->scanning != SANE_TRUE) { DBG (DBG_error, "sane_set_io_mode: called out of a scan\n"); return SANE_STATUS_INVAL; } session->non_blocking = non_blocking; DBG (DBG_info, "sane_set_io_mode: I/O mode set to %sblocking.\n", non_blocking ? "non " : " "); DBG (DBG_proc, "sane_set_io_mode: exit\n"); return SANE_STATUS_GOOD; } /** * An advanced method we don't support but have to define. At SANE API * level this function is meant to provide a file descriptor on which the * frontend can do select()/poll() to wait for data. */ SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fdp) { /* make compiler happy ... */ (void) handle; (void) fdp; DBG (DBG_proc, "sane_get_select_fd: start\n"); DBG (DBG_warn, "sane_get_select_fd: unsupported ...\n"); DBG (DBG_proc, "sane_get_select_fd: exit\n"); return SANE_STATUS_UNSUPPORTED; } /** * 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 P5_Session *session = handle; DBG (DBG_proc, "sane_get_option_descriptor: start\n"); if ((unsigned) option >= NUM_OPTIONS) return NULL; DBG (DBG_info, "sane_get_option_descriptor: \"%s\"\n", session->options[option].descriptor.name); DBG (DBG_proc, "sane_get_option_descriptor: exit\n"); return &(session->options[option].descriptor); } /** * sets automatic value for an option , called by sane_control_option after * all checks have been done */ static SANE_Status set_automatic_value (P5_Session * s, int option, SANE_Int * myinfo) { SANE_Status status = SANE_STATUS_GOOD; SANE_Int i, min; SANE_Word *dpi_list; switch (option) { case OPT_TL_X: s->options[OPT_TL_X].value.w = x_range.min; *myinfo |= SANE_INFO_RELOAD_PARAMS; break; case OPT_TL_Y: s->options[OPT_TL_Y].value.w = y_range.min; *myinfo |= SANE_INFO_RELOAD_PARAMS; break; case OPT_BR_X: s->options[OPT_BR_X].value.w = x_range.max; *myinfo |= SANE_INFO_RELOAD_PARAMS; break; case OPT_BR_Y: s->options[OPT_BR_Y].value.w = y_range.max; *myinfo |= SANE_INFO_RELOAD_PARAMS; break; case OPT_RESOLUTION: /* we set up to the lowest available dpi value */ dpi_list = (SANE_Word *) s->options[OPT_RESOLUTION].descriptor.constraint. word_list; min = 65536; for (i = 1; i < dpi_list[0]; i++) { if (dpi_list[i] < min) min = dpi_list[i]; } s->options[OPT_RESOLUTION].value.w = min; *myinfo |= SANE_INFO_RELOAD_PARAMS; break; case OPT_PREVIEW: s->options[OPT_PREVIEW].value.w = SANE_FALSE; *myinfo |= SANE_INFO_RELOAD_PARAMS; break; case OPT_MODE: if (s->options[OPT_MODE].value.s) free (s->options[OPT_MODE].value.s); s->options[OPT_MODE].value.s = strdup (mode_list[0]); *myinfo |= SANE_INFO_RELOAD_OPTIONS; *myinfo |= SANE_INFO_RELOAD_PARAMS; break; default: DBG (DBG_warn, "set_automatic_value: can't set unknown option %d\n", option); } return status; } /** * sets an option , called by sane_control_option after all * checks have been done */ static SANE_Status set_option_value (P5_Session * s, int option, void *val, SANE_Int * myinfo) { SANE_Status status = SANE_STATUS_GOOD; SANE_Word tmpw; switch (option) { case OPT_TL_X: case OPT_BR_X: case OPT_TL_Y: case OPT_BR_Y: s->options[option].value.w = *(SANE_Word *) val; /* we ensure geometry is coherent */ /* this happens when user drags TL corner right or below the BR point */ if (s->options[OPT_BR_Y].value.w < s->options[OPT_TL_Y].value.w) { tmpw = s->options[OPT_BR_Y].value.w; s->options[OPT_BR_Y].value.w = s->options[OPT_TL_Y].value.w; s->options[OPT_TL_Y].value.w = tmpw; } if (s->options[OPT_BR_X].value.w < s->options[OPT_TL_X].value.w) { tmpw = s->options[OPT_BR_X].value.w; s->options[OPT_BR_X].value.w = s->options[OPT_TL_X].value.w; s->options[OPT_TL_X].value.w = tmpw; } *myinfo |= SANE_INFO_RELOAD_PARAMS; break; case OPT_RESOLUTION: case OPT_PREVIEW: s->options[option].value.w = *(SANE_Word *) val; *myinfo |= SANE_INFO_RELOAD_PARAMS; break; case OPT_MODE: if (s->options[option].value.s) free (s->options[option].value.s); s->options[option].value.s = strdup (val); *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; break; case OPT_CALIBRATE: status = sheetfed_calibration (s->dev); *myinfo |= SANE_INFO_RELOAD_OPTIONS; break; case OPT_CLEAR_CALIBRATION: cleanup_calibration (s->dev); *myinfo |= SANE_INFO_RELOAD_OPTIONS; break; default: DBG (DBG_warn, "set_option_value: can't set unknown option %d\n", option); } return status; } /** * gets an option , called by sane_control_option after all checks * have been done */ static SANE_Status get_option_value (P5_Session * s, int option, void *val) { SANE_Status status; switch (option) { /* word or word equivalent options: */ case OPT_NUM_OPTS: case OPT_RESOLUTION: case OPT_PREVIEW: case OPT_TL_X: case OPT_TL_Y: case OPT_BR_X: case OPT_BR_Y: *(SANE_Word *) val = s->options[option].value.w; break; /* string options: */ case OPT_MODE: strcpy (val, s->options[option].value.s); break; /* sensor options */ case OPT_PAGE_LOADED_SW: status = test_document (s->dev->fd); if (status == SANE_STATUS_GOOD) s->options[option].value.b = SANE_TRUE; else s->options[option].value.b = SANE_FALSE; *(SANE_Bool *) val = s->options[option].value.b; break; case OPT_NEED_CALIBRATION_SW: *(SANE_Bool *) val = !s->dev->calibrated; break; /* unhandled options */ default: DBG (DBG_warn, "get_option_value: can't get unknown option %d\n", option); } return SANE_STATUS_GOOD; } /** * 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. * action is SANE_ACTION_GET_VALUE, SANE_ACTION_SET_VALUE or SANE_ACTION_SET_AUTO */ SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { P5_Session *s = handle; SANE_Status status; SANE_Word cap; SANE_Int myinfo = 0; DBG (DBG_io2, "sane_control_option: start: action = %s, option = %s (%d)\n", (action == SANE_ACTION_GET_VALUE) ? "get" : (action == SANE_ACTION_SET_VALUE) ? "set" : (action == SANE_ACTION_SET_AUTO) ? "set_auto" : "unknown", s->options[option].descriptor.name, option); if (info) *info = 0; /* do checks before trying to apply action */ if (s->scanning) { DBG (DBG_warn, "sane_control_option: don't call this function while " "scanning (option = %s (%d))\n", s->options[option].descriptor.name, option); return SANE_STATUS_DEVICE_BUSY; } /* option must be within existing range */ if (option >= NUM_OPTIONS || option < 0) { DBG (DBG_warn, "sane_control_option: option %d >= NUM_OPTIONS || option < 0\n", option); return SANE_STATUS_INVAL; } /* don't access an inactive option */ cap = s->options[option].descriptor.cap; if (!SANE_OPTION_IS_ACTIVE (cap)) { DBG (DBG_warn, "sane_control_option: option %d is inactive\n", option); return SANE_STATUS_INVAL; } /* now checks have been done, apply action */ switch (action) { case SANE_ACTION_GET_VALUE: status = get_option_value (s, option, val); break; case SANE_ACTION_SET_VALUE: if (!SANE_OPTION_IS_SETTABLE (cap)) { DBG (DBG_warn, "sane_control_option: option %d is not settable\n", option); return SANE_STATUS_INVAL; } status = sanei_constrain_value (&s->options[option].descriptor, val, &myinfo); if (status != SANE_STATUS_GOOD) { DBG (DBG_warn, "sane_control_option: sanei_constrain_value returned %s\n", sane_strstatus (status)); return status; } /* return immediately if no change */ if (s->options[option].descriptor.type == SANE_TYPE_INT && *(SANE_Word *) val == s->options[option].value.w) { status = SANE_STATUS_GOOD; } else { /* apply change */ status = set_option_value (s, option, val, &myinfo); } break; case SANE_ACTION_SET_AUTO: /* sets automatic values */ if (!(cap & SANE_CAP_AUTOMATIC)) { DBG (DBG_warn, "sane_control_option: option %d is not autosettable\n", option); return SANE_STATUS_INVAL; } status = set_automatic_value (s, option, &myinfo); break; default: DBG (DBG_error, "sane_control_option: invalid action %d\n", action); status = SANE_STATUS_INVAL; break; } if (info) *info = myinfo; DBG (DBG_io2, "sane_control_option: exit\n"); return status; } /** * Called by SANE when a page acquisition operation is to be started. * @param handle opaque handle to a frontend session * @return SANE_STATUS_GOOD on success, SANE_STATUS_BUSY if the device is * in use by another session or SANE_STATUS_WARMING_UP if the device is * warming up. In this case the fronted as to call sane_start again until * warming up is done. Any other values returned are error status. */ SANE_Status sane_start (SANE_Handle handle) { struct P5_Session *session = handle; int status = SANE_STATUS_GOOD; P5_Device *dev = session->dev; DBG (DBG_proc, "sane_start: start\n"); /* if already scanning, tell we're busy */ if (session->scanning == SANE_TRUE) { DBG (DBG_info, "sane_start: device is already scanning\n"); return SANE_STATUS_DEVICE_BUSY; } /* check that the device has been initialized */ if (dev->initialized == SANE_FALSE) { DBG (DBG_error, "sane_start: device is not initialized\n"); return SANE_STATUS_INVAL; } /* check if there is a document */ status = test_document (dev->fd); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "sane_start: device is already scanning\n"); return status; } /* we compute all the scan parameters so that */ /* we will be able to set up the registers correctly */ compute_parameters (session); /* move to scan area if needed */ if (dev->ystart > 0) { status = move (dev); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "sane_start: failed to move to scan area\n"); return SANE_STATUS_INVAL; } } /* send scan command */ status = start_scan (dev, dev->mode, dev->ydpi, dev->xstart, dev->pixels); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "sane_start: failed to start scan\n"); return SANE_STATUS_INVAL; } /* allocates work buffer */ if (dev->buffer != NULL) { free (dev->buffer); } dev->position = 0; dev->top = 0; /* compute amount of lines needed for lds correction */ dev->bottom = dev->bytes_per_line * 2 * dev->lds; /* computes buffer size, 66 color lines plus eventual amount needed for lds */ dev->size = dev->pixels * 3 * 66 + dev->bottom; dev->buffer = (uint8_t *) malloc (dev->size); if (dev->buffer == NULL) { DBG (DBG_error, "sane_start: failed to allocate %lu bytes\n", (unsigned long)dev->size); sane_cancel (handle); return SANE_STATUS_NO_MEM; } /* return now the scan has been initiated */ session->scanning = SANE_TRUE; session->sent = 0; DBG (DBG_io, "sane_start: to_send=%d\n", session->to_send); DBG (DBG_io, "sane_start: size=%lu\n", (unsigned long)dev->size); DBG (DBG_io, "sane_start: top=%lu\n", (unsigned long)dev->top); DBG (DBG_io, "sane_start: bottom=%lu\n", (unsigned long)dev->bottom); DBG (DBG_io, "sane_start: position=%lu\n", (unsigned long)dev->position); DBG (DBG_proc, "sane_start: exit\n"); return status; } /** @brief compute scan parameters * This function computes two set of parameters. The one for the SANE's standard * and the other for the hardware. Among these parameters are the bit depth, total * number of lines, total number of columns, extra line to read for data reordering... * @param session fronted session to compute final scan parameters * @return SANE_STATUS_GOOD on success */ static SANE_Status compute_parameters (P5_Session * session) { P5_Device *dev = session->dev; SANE_Int dpi; /* dpi for scan */ SANE_String mode; SANE_Status status = SANE_STATUS_GOOD; int tl_x, tl_y, br_x, br_y; mode = session->options[OPT_MODE].value.s; dpi = session->options[OPT_RESOLUTION].value.w; /* scan coordinates */ tl_x = SANE_UNFIX (session->options[OPT_TL_X].value.w); tl_y = SANE_UNFIX (session->options[OPT_TL_Y].value.w); br_x = SANE_UNFIX (session->options[OPT_BR_X].value.w); br_y = SANE_UNFIX (session->options[OPT_BR_Y].value.w); /* only single pass scanning supported */ session->params.last_frame = SANE_TRUE; /* gray modes */ if (strcmp (mode, GRAY_MODE) == 0) { session->params.format = SANE_FRAME_GRAY; dev->mode = MODE_GRAY; dev->lds = 0; } else if (strcmp (mode, LINEART_MODE) == 0) { session->params.format = SANE_FRAME_GRAY; dev->mode = MODE_LINEART; dev->lds = 0; } else { /* Color */ session->params.format = SANE_FRAME_RGB; dev->mode = MODE_COLOR; dev->lds = (dev->model->lds * dpi) / dev->model->max_ydpi; } /* SANE level values */ session->params.lines = ((br_y - tl_y) * dpi) / MM_PER_INCH; if (session->params.lines == 0) session->params.lines = 1; session->params.pixels_per_line = ((br_x - tl_x) * dpi) / MM_PER_INCH; if (session->params.pixels_per_line == 0) session->params.pixels_per_line = 1; DBG (DBG_data, "compute_parameters: pixels_per_line =%d\n", session->params.pixels_per_line); if (strcmp (mode, LINEART_MODE) == 0) { session->params.depth = 1; /* in lineart, having pixels multiple of 8 avoids a costly test */ /* at each bit to see we must go to the next byte */ /* TODO : implement this requirement in sane_control_option */ session->params.pixels_per_line = ((session->params.pixels_per_line + 7) / 8) * 8; } else session->params.depth = 8; /* width needs to be even */ if (session->params.pixels_per_line & 1) session->params.pixels_per_line++; /* Hardware settings : they can differ from the ones at SANE level */ /* for instance the effective DPI used by a sensor may be higher */ /* than the one needed for the SANE scan parameters */ dev->lines = session->params.lines; dev->pixels = session->params.pixels_per_line; /* motor and sensor DPI */ dev->xdpi = dpi; dev->ydpi = dpi; /* handle bounds of motor's dpi range */ if (dev->ydpi > dev->model->max_ydpi) { dev->ydpi = dev->model->max_ydpi; dev->lines = (dev->lines * dev->model->max_ydpi) / dpi; if (dev->lines == 0) dev->lines = 1; /* round number of lines */ session->params.lines = (session->params.lines / dev->lines) * dev->lines; if (session->params.lines == 0) session->params.lines = 1; } if (dev->ydpi < dev->model->min_ydpi) { dev->ydpi = dev->model->min_ydpi; dev->lines = (dev->lines * dev->model->min_ydpi) / dpi; } /* hardware values */ dev->xstart = ((SANE_UNFIX (dev->model->x_offset) + tl_x) * dpi) / MM_PER_INCH; dev->ystart = ((SANE_UNFIX (dev->model->y_offset) + tl_y) * dev->ydpi) / MM_PER_INCH; /* take lds correction into account when moving to scan area */ if (dev->ystart > 2 * dev->lds) dev->ystart -= 2 * dev->lds; /* computes bytes per line */ session->params.bytes_per_line = session->params.pixels_per_line; dev->bytes_per_line = dev->pixels; if (session->params.format == SANE_FRAME_RGB) { dev->bytes_per_line *= 3; } /* in lineart mode we adjust bytes_per_line needed by frontend */ /* we do that here because we needed sent/to_send to be as if */ /* there was no lineart */ if (session->params.depth == 1) { session->params.bytes_per_line = (session->params.bytes_per_line + 7) / 8; } session->params.bytes_per_line = dev->bytes_per_line; session->to_send = session->params.bytes_per_line * session->params.lines; session->params.bytes_per_line = dev->bytes_per_line; DBG (DBG_data, "compute_parameters: bytes_per_line =%d\n", session->params.bytes_per_line); DBG (DBG_data, "compute_parameters: depth =%d\n", session->params.depth); DBG (DBG_data, "compute_parameters: lines =%d\n", session->params.lines); DBG (DBG_data, "compute_parameters: image size =%d\n", session->to_send); DBG (DBG_data, "compute_parameters: xstart =%d\n", dev->xstart); DBG (DBG_data, "compute_parameters: ystart =%d\n", dev->ystart); DBG (DBG_data, "compute_parameters: dev lines =%d\n", dev->lines); DBG (DBG_data, "compute_parameters: dev bytes per line=%d\n", dev->bytes_per_line); DBG (DBG_data, "compute_parameters: dev pixels =%d\n", dev->pixels); DBG (DBG_data, "compute_parameters: lds =%d\n", dev->lds); return status; } /** * 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 of the * device for which the parameters should be obtained and a pointer * to a parameter structure. */ SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { SANE_Status status; struct P5_Session *session = (struct P5_Session *) handle; DBG (DBG_proc, "sane_get_parameters: start\n"); /* call parameters computing function */ status = compute_parameters (session); if (status == SANE_STATUS_GOOD && params) *params = session->params; DBG (DBG_proc, "sane_get_parameters: exit\n"); return status; } /** * 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. * * Returned data is read from working buffer. */ SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { struct P5_Session *session = (struct P5_Session *) handle; struct P5_Device *dev = session->dev; SANE_Status status = SANE_STATUS_GOOD; int count; int size, lines; SANE_Bool x2; SANE_Int i; DBG (DBG_proc, "sane_read: start\n"); DBG (DBG_io, "sane_read: up to %d bytes required by frontend\n", max_len); /* some sanity checks first to protect from would be buggy frontends */ if (!session) { DBG (DBG_error, "sane_read: handle is null!\n"); return SANE_STATUS_INVAL; } if (!buf) { DBG (DBG_error, "sane_read: buf is null!\n"); return SANE_STATUS_INVAL; } if (!len) { DBG (DBG_error, "sane_read: len is null!\n"); return SANE_STATUS_INVAL; } /* no data read yet */ *len = 0; /* check if session is scanning */ if (!session->scanning) { DBG (DBG_warn, "sane_read: scan was cancelled, is over or has not been initiated yet\n"); return SANE_STATUS_CANCELLED; } /* check for EOF, must be done before any physical read */ if (session->sent >= session->to_send) { DBG (DBG_io, "sane_read: end of scan reached\n"); return SANE_STATUS_EOF; } /* if working buffer is empty, we do a physical data read */ if (dev->top <= dev->bottom) { DBG (DBG_io, "sane_read: physical data read\n"); /* check is there is data available. In case of non-blocking mode we return * as soon it is detected there is no data yet. Reads must by done line by * line, so we read only when count is bigger than bytes per line * */ count = available_bytes (dev->fd); DBG (DBG_io, "sane_read: count=%d bytes\n", count); if (count < dev->bytes_per_line && session->non_blocking == SANE_TRUE) { DBG (DBG_io, "sane_read: scanner hasn't enough data available\n"); DBG (DBG_proc, "sane_read: exit\n"); return SANE_STATUS_GOOD; } /* now we can wait for data here */ while (count < dev->bytes_per_line) { /* test if document left the feeder, so we have to terminate the scan */ status = test_document (dev->fd); if (status == SANE_STATUS_NO_DOCS) { session->to_send = session->sent; return SANE_STATUS_EOF; } /* don't call scanner too often */ usleep (10000); count = available_bytes (dev->fd); } /** compute size of physical data to read * on first read, position will be 0, while it will be 'bottom' * for the subsequent reads. * We try to read a complete buffer */ size = dev->size - dev->position; if (session->to_send - session->sent < size) { /* not enough data left, so read remainder of scan */ size = session->to_send - session->sent; } /* 600 dpi is 300x600 physical, and 400 is 200x400 */ if (dev->ydpi > dev->model->max_xdpi) { x2 = SANE_TRUE; } else { x2 = SANE_FALSE; } lines = read_line (dev, dev->buffer + dev->position, dev->bytes_per_line, size / dev->bytes_per_line, SANE_TRUE, x2, dev->mode, dev->calibrated); /* handle document end detection TODO try to recover the partial * buffer already read before EOD */ if (lines == -1) { DBG (DBG_io, "sane_read: error reading line\n"); return SANE_STATUS_IO_ERROR; } /* gather lines until we have more than needed for lds */ dev->position += lines * dev->bytes_per_line; dev->top = dev->position; if (dev->position > dev->bottom) { dev->position = dev->bottom; } DBG (DBG_io, "sane_read: size =%lu\n", (unsigned long)dev->size); DBG (DBG_io, "sane_read: bottom =%lu\n", (unsigned long)dev->bottom); DBG (DBG_io, "sane_read: position=%lu\n", (unsigned long)dev->position); DBG (DBG_io, "sane_read: top =%lu\n", (unsigned long)dev->top); } /* end of physical data reading */ /* logical data reading */ /* check if there data available in working buffer */ if (dev->position < dev->top && dev->position >= dev->bottom) { DBG (DBG_io, "sane_read: logical data read\n"); /* we have more data in internal buffer than asked , * then send only max data */ size = dev->top - dev->position; if (max_len < size) { *len = max_len; } else /* if we don't have enough, send all what we have */ { *len = dev->top - dev->position; } /* data copy */ if (dev->lds == 0) { memcpy (buf, dev->buffer + dev->position, *len); } else { /* compute count of bytes for lds */ count = dev->lds * dev->bytes_per_line; /* adjust for lds as we copy data to frontend */ for (i = 0; i < *len; i++) { switch ((dev->position + i) % 3) { /* red */ case 0: buf[i] = dev->buffer[dev->position + i - 2 * count]; break; /* green */ case 1: buf[i] = dev->buffer[dev->position + i - count]; break; /* blue */ default: buf[i] = dev->buffer[dev->position + i]; break; } } } dev->position += *len; /* update byte accounting */ session->sent += *len; DBG (DBG_io, "sane_read: sent %d bytes from buffer to frontend\n", *len); return SANE_STATUS_GOOD; } /* check if we exhausted working buffer */ if (dev->position >= dev->top && dev->position >= dev->bottom) { /* copy extra lines needed for lds in next buffer */ if (dev->position > dev->bottom && dev->lds > 0) { memcpy (dev->buffer, dev->buffer + dev->position - dev->bottom, dev->bottom); } /* restart buffer */ dev->position = dev->bottom; dev->top = 0; } DBG (DBG_io, "sane_read: size =%lu\n", (unsigned long)dev->size); DBG (DBG_io, "sane_read: bottom =%lu\n", (unsigned long)dev->bottom); DBG (DBG_io, "sane_read: position=%lu\n", (unsigned long)dev->position); DBG (DBG_io, "sane_read: top =%lu\n", (unsigned long)dev->top); DBG (DBG_proc, "sane_read: exit\n"); return status; } /** * Cancels a scan. * * 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 operation 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) { P5_Session *session = handle; DBG (DBG_proc, "sane_cancel: start\n"); /* if scanning, abort and park head */ if (session->scanning == SANE_TRUE) { /* detects if we are called after the scan is finished, * or if the scan is aborted */ if (session->sent < session->to_send) { DBG (DBG_info, "sane_cancel: aborting scan.\n"); /* device hasn't finished scan, we are aborting it * and we may have to do something specific for it here */ } else { DBG (DBG_info, "sane_cancel: cleaning up after scan.\n"); } session->scanning = SANE_FALSE; } eject (session->dev->fd); DBG (DBG_proc, "sane_cancel: exit\n"); } /** * Ends use of the session. * * 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. * * Handle resources are free'd before disposing the handle. But devices * resources must not be mdofied, since it could be used or reused until * sane_exit() is called. */ void sane_close (SANE_Handle handle) { P5_Session *prev, *session; DBG (DBG_proc, "sane_close: start\n"); /* remove handle from list of open handles: */ prev = NULL; for (session = sessions; session; session = session->next) { if (session == handle) break; prev = session; } if (!session) { DBG (DBG_error0, "close: invalid handle %p\n", handle); return; /* oops, not a handle we know about */ } /* cancel any active scan */ if (session->scanning == SANE_TRUE) { sane_cancel (handle); } if (prev) prev->next = session->next; else sessions = session->next; /* close low level device */ if (session->dev->initialized == SANE_TRUE) { if (session->dev->calibrated == SANE_TRUE) { save_calibration (session->dev); } disconnect (session->dev->fd); close_pp (session->dev->fd); session->dev->fd = -1; session->dev->initialized = SANE_FALSE; /* free device data */ if (session->dev->buffer != NULL) { free (session->dev->buffer); } if (session->dev->buffer != NULL) { free (session->dev->gain); free (session->dev->offset); } if (session->dev->calibrated == SANE_TRUE) { cleanup_calibration (session->dev); } } /* free per session data */ free (session->options[OPT_MODE].value.s); free ((void *)session->options[OPT_RESOLUTION].descriptor.constraint.word_list); free (session); DBG (DBG_proc, "sane_close: exit\n"); } /** * 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 P5_Session *session, *next; struct P5_Device *dev, *nextdev; int i; DBG (DBG_proc, "sane_exit: start\n"); init_count--; if (init_count > 0) { DBG (DBG_info, "sane_exit: still %d fronteds to leave before effective exit.\n", init_count); return; } /* free session structs */ for (session = sessions; session; session = next) { next = session->next; sane_close ((SANE_Handle *) session); free (session); } sessions = NULL; /* free devices structs */ for (dev = devices; dev; dev = nextdev) { nextdev = dev->next; free (dev->name); free (dev); } devices = NULL; /* now list of devices */ if (devlist) { i = 0; while ((SANE_Device *) devlist[i]) { free ((SANE_Device *) devlist[i]); i++; } free (devlist); devlist = NULL; } DBG (DBG_proc, "sane_exit: exit\n"); } /** @brief probe for all supported devices * This functions tries to probe if any of the supported devices of * the backend is present. Each detected device will be added to the * 'devices' list */ static SANE_Status probe_p5_devices (void) { /**> configuration structure used during attach */ SANEI_Config config; /**> list of configuration options */ SANE_Option_Descriptor *cfg_options[NUM_CFG_OPTIONS]; /**> placeholders pointers for option values */ void *values[NUM_CFG_OPTIONS]; int i; SANE_Status status; DBG (DBG_proc, "probe_p5_devices: start\n"); /* initialize configuration options */ cfg_options[CFG_MODEL_NAME] = (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor)); cfg_options[CFG_MODEL_NAME]->name = "modelname"; cfg_options[CFG_MODEL_NAME]->desc = "user provided scanner's model name"; cfg_options[CFG_MODEL_NAME]->type = SANE_TYPE_INT; cfg_options[CFG_MODEL_NAME]->unit = SANE_UNIT_NONE; cfg_options[CFG_MODEL_NAME]->size = sizeof (SANE_Word); cfg_options[CFG_MODEL_NAME]->cap = SANE_CAP_SOFT_SELECT; cfg_options[CFG_MODEL_NAME]->constraint_type = SANE_CONSTRAINT_NONE; values[CFG_MODEL_NAME] = &p5cfg.modelname; /* set configuration options structure */ config.descriptors = cfg_options; config.values = values; config.count = NUM_CFG_OPTIONS; /* generic configure and attach function */ status = sanei_configure_attach (P5_CONFIG_FILE, &config, config_attach, NULL); /* free allocated options */ for (i = 0; i < NUM_CFG_OPTIONS; i++) { free (cfg_options[i]); } DBG (DBG_proc, "probe_p5_devices: end\n"); return status; } /** This function is called by sanei_configure_attach to try * to attach the backend to a device specified by the configuration file. * * @param config configuration structure filled with values read * from configuration file * @param devname name of the device to try to attach to, it is * the unprocessed line of the configuration file * * @return status SANE_STATUS_GOOD if no errors (even if no matching * devices found) * SANE_STATUS_INVAL in case of error */ static SANE_Status config_attach (SANEI_Config __sane_unused__ * config, const char *devname, void __sane_unused__ *data) { /* currently, the config is a global variable so config is useless here */ /* the correct thing would be to have a generic sanei_attach_matching_devices * using an attach function with a config parameter */ (void) config; /* the devname has been processed and is ready to be used * directly. The config struct contains all the configuration data for * the corresponding device. Since there is no resources common to each * backends regarding parallel port, we can directly call the attach * function. */ attach_p5 (devname, config); return SANE_STATUS_GOOD; } /** @brief try to attach to a device by its name * The attach tries to open the given device and match it * with devices handled by the backend. The configuration parameter * contains the values of the already parsed configuration options * from the conf file. * @param config configuration structure filled with values read * from configuration file * @param devicename name of the device to try to attach to, it is * the unprocessed line of the configuration file * * @return status SANE_STATUS_GOOD if no errors (even if no matching * devices found) * SANE_STATUS_NOM_MEM if there isn't enough memory to allocate the * device structure * SANE_STATUS_UNSUPPORTED if the device if unknown by the backend * SANE_STATUS_INVAL in case of other error */ static SANE_Status attach_p5 (const char *devicename, SANEI_Config * config) { struct P5_Device *device; struct P5_Model *model; DBG (DBG_proc, "attach(%s): start\n", devicename); if(config==NULL) { DBG (DBG_warn, "attach: config is NULL\n"); } /* search if we already have it attached */ for (device = devices; device; device = device->next) { if (strcmp (device->name, devicename) == 0) { DBG (DBG_info, "attach: device already attached\n"); DBG (DBG_proc, "attach: exit\n"); return SANE_STATUS_GOOD; } } /** * do physical probe of the device here. In case the device is recognized, * we allocate a device struct and give it options and model. * Else we return SANE_STATUS_UNSUPPORTED. */ model = probe (devicename); if (model == NULL) { DBG (DBG_info, "attach: device %s is not managed by the backend\n", devicename); DBG (DBG_proc, "attach: exit\n"); return SANE_STATUS_UNSUPPORTED; } /* allocate device struct */ device = malloc (sizeof (*device)); if (device == NULL) { return SANE_STATUS_NO_MEM; DBG (DBG_proc, "attach: exit\n"); } memset (device, 0, sizeof (*device)); device->model = model; /* name of the device */ device->name = strdup (devicename); DBG (DBG_info, "attach: found %s %s %s at %s\n", device->model->vendor, device->model->product, device->model->type, device->name); /* we insert new device at start of the chained list */ /* head of the list becomes the next, and start is replaced */ /* with the new session struct */ device->next = devices; devices = device; /* initialization is done at sane_open */ device->initialized = SANE_FALSE; device->calibrated = SANE_FALSE; DBG (DBG_proc, "attach: exit\n"); return SANE_STATUS_GOOD; } /** @brief set initial value for the scanning options * for each sessions, control options are initialized based on the capability * of the model of the physical device. * @param session scanner session to initialize options * @return SANE_STATUS_GOOD on success */ static SANE_Status init_options (struct P5_Session *session) { SANE_Int option, i, min, idx; SANE_Word *dpi_list; P5_Model *model = session->dev->model; DBG (DBG_proc, "init_options: start\n"); /* we first initialize each options with a default value */ memset (session->options, 0, sizeof (session->options[OPT_NUM_OPTS])); for (option = 0; option < NUM_OPTIONS; option++) { session->options[option].descriptor.size = sizeof (SANE_Word); session->options[option].descriptor.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; } /* we set up all the options listed in the P5_Option enum */ /* last option / end of list marker */ session->options[OPT_NUM_OPTS].descriptor.name = SANE_NAME_NUM_OPTIONS; session->options[OPT_NUM_OPTS].descriptor.title = SANE_TITLE_NUM_OPTIONS; session->options[OPT_NUM_OPTS].descriptor.desc = SANE_DESC_NUM_OPTIONS; session->options[OPT_NUM_OPTS].descriptor.type = SANE_TYPE_INT; session->options[OPT_NUM_OPTS].descriptor.cap = SANE_CAP_SOFT_DETECT; session->options[OPT_NUM_OPTS].value.w = NUM_OPTIONS; /* "Standard" group: */ session->options[OPT_STANDARD_GROUP].descriptor.title = SANE_TITLE_STANDARD; session->options[OPT_STANDARD_GROUP].descriptor.name = SANE_NAME_STANDARD; session->options[OPT_STANDARD_GROUP].descriptor.desc = SANE_DESC_STANDARD; session->options[OPT_STANDARD_GROUP].descriptor.type = SANE_TYPE_GROUP; session->options[OPT_STANDARD_GROUP].descriptor.size = 0; session->options[OPT_STANDARD_GROUP].descriptor.cap = 0; session->options[OPT_STANDARD_GROUP].descriptor.constraint_type = SANE_CONSTRAINT_NONE; /* scan mode */ session->options[OPT_MODE].descriptor.name = SANE_NAME_SCAN_MODE; session->options[OPT_MODE].descriptor.title = SANE_TITLE_SCAN_MODE; session->options[OPT_MODE].descriptor.desc = SANE_DESC_SCAN_MODE; session->options[OPT_MODE].descriptor.type = SANE_TYPE_STRING; session->options[OPT_MODE].descriptor.cap |= SANE_CAP_AUTOMATIC; session->options[OPT_MODE].descriptor.constraint_type = SANE_CONSTRAINT_STRING_LIST; session->options[OPT_MODE].descriptor.size = max_string_size (mode_list); session->options[OPT_MODE].descriptor.constraint.string_list = mode_list; session->options[OPT_MODE].value.s = strdup (mode_list[0]); /* preview */ session->options[OPT_PREVIEW].descriptor.name = SANE_NAME_PREVIEW; session->options[OPT_PREVIEW].descriptor.title = SANE_TITLE_PREVIEW; session->options[OPT_PREVIEW].descriptor.desc = SANE_DESC_PREVIEW; session->options[OPT_PREVIEW].descriptor.type = SANE_TYPE_BOOL; session->options[OPT_PREVIEW].descriptor.cap |= SANE_CAP_AUTOMATIC; session->options[OPT_PREVIEW].descriptor.unit = SANE_UNIT_NONE; session->options[OPT_PREVIEW].descriptor.constraint_type = SANE_CONSTRAINT_NONE; session->options[OPT_PREVIEW].value.w = SANE_FALSE; /** @brief build resolution list * We merge xdpi and ydpi list to provide only one resolution option control. * This is the most common case for backends and fronteds and give 'square' * pixels. The SANE API allow to control x and y dpi independently, but this is * rarely done and may confuse both frontends and users. In case a dpi value exists * for one but not for the other, the backend will have to crop data so that the * frontend is unaffected. A common case is that motor resolution (ydpi) is higher * than sensor resolution (xdpi), so scan lines must be scaled up to keep square * pixel when doing sane_read(). * TODO this deserves a dedicated function and some unit testing */ /* find minimum first */ min = 65535; for (i = 0; i < MAX_RESOLUTIONS && model->xdpi_values[i] > 0; i++) { if (model->xdpi_values[i] < min) min = model->xdpi_values[i]; } for (i = 0; i < MAX_RESOLUTIONS && model->ydpi_values[i] > 0; i++) { if (model->ydpi_values[i] < min) min = model->ydpi_values[i]; } dpi_list = malloc ((MAX_RESOLUTIONS * 2 + 1) * sizeof (SANE_Word)); if (!dpi_list) return SANE_STATUS_NO_MEM; dpi_list[1] = min; idx = 2; /* find any value greater than the last used min and * less than the max value */ do { min = 65535; for (i = 0; i < MAX_RESOLUTIONS && model->xdpi_values[i] > 0; i++) { if (model->xdpi_values[i] < min && model->xdpi_values[i] > dpi_list[idx - 1]) min = model->xdpi_values[i]; } for (i = 0; i < MAX_RESOLUTIONS && model->ydpi_values[i] > 0; i++) { if (model->ydpi_values[i] < min && model->ydpi_values[i] > dpi_list[idx - 1]) min = model->ydpi_values[i]; } if (min < 65535) { dpi_list[idx] = min; idx++; } } while (min != 65535); dpi_list[idx] = 0; /* the count of different resolution is put at the beginning */ dpi_list[0] = idx - 1; session->options[OPT_RESOLUTION].descriptor.name = SANE_NAME_SCAN_RESOLUTION; session->options[OPT_RESOLUTION].descriptor.title = SANE_TITLE_SCAN_RESOLUTION; session->options[OPT_RESOLUTION].descriptor.desc = SANE_DESC_SCAN_RESOLUTION; session->options[OPT_RESOLUTION].descriptor.type = SANE_TYPE_INT; session->options[OPT_RESOLUTION].descriptor.cap |= SANE_CAP_AUTOMATIC; session->options[OPT_RESOLUTION].descriptor.unit = SANE_UNIT_DPI; session->options[OPT_RESOLUTION].descriptor.constraint_type = SANE_CONSTRAINT_WORD_LIST; session->options[OPT_RESOLUTION].descriptor.constraint.word_list = dpi_list; /* initial value is lowest available dpi */ session->options[OPT_RESOLUTION].value.w = min; /* "Geometry" group: */ session->options[OPT_GEOMETRY_GROUP].descriptor.title = SANE_TITLE_GEOMETRY; session->options[OPT_GEOMETRY_GROUP].descriptor.name = SANE_NAME_GEOMETRY; session->options[OPT_GEOMETRY_GROUP].descriptor.desc = SANE_DESC_GEOMETRY; session->options[OPT_GEOMETRY_GROUP].descriptor.type = SANE_TYPE_GROUP; session->options[OPT_GEOMETRY_GROUP].descriptor.cap = SANE_CAP_ADVANCED; session->options[OPT_GEOMETRY_GROUP].descriptor.size = 0; session->options[OPT_GEOMETRY_GROUP].descriptor.constraint_type = SANE_CONSTRAINT_NONE; /* adapt the constraint range to the detected model */ x_range.max = model->x_size; y_range.max = model->y_size; /* top-left x */ session->options[OPT_TL_X].descriptor.name = SANE_NAME_SCAN_TL_X; session->options[OPT_TL_X].descriptor.title = SANE_TITLE_SCAN_TL_X; session->options[OPT_TL_X].descriptor.desc = SANE_DESC_SCAN_TL_X; session->options[OPT_TL_X].descriptor.type = SANE_TYPE_FIXED; session->options[OPT_TL_X].descriptor.cap |= SANE_CAP_AUTOMATIC; session->options[OPT_TL_X].descriptor.unit = SANE_UNIT_MM; session->options[OPT_TL_X].descriptor.constraint_type = SANE_CONSTRAINT_RANGE; session->options[OPT_TL_X].descriptor.constraint.range = &x_range; session->options[OPT_TL_X].value.w = 0; /* top-left y */ session->options[OPT_TL_Y].descriptor.name = SANE_NAME_SCAN_TL_Y; session->options[OPT_TL_Y].descriptor.title = SANE_TITLE_SCAN_TL_Y; session->options[OPT_TL_Y].descriptor.desc = SANE_DESC_SCAN_TL_Y; session->options[OPT_TL_Y].descriptor.type = SANE_TYPE_FIXED; session->options[OPT_TL_Y].descriptor.cap |= SANE_CAP_AUTOMATIC; session->options[OPT_TL_Y].descriptor.unit = SANE_UNIT_MM; session->options[OPT_TL_Y].descriptor.constraint_type = SANE_CONSTRAINT_RANGE; session->options[OPT_TL_Y].descriptor.constraint.range = &y_range; session->options[OPT_TL_Y].value.w = 0; /* bottom-right x */ session->options[OPT_BR_X].descriptor.name = SANE_NAME_SCAN_BR_X; session->options[OPT_BR_X].descriptor.title = SANE_TITLE_SCAN_BR_X; session->options[OPT_BR_X].descriptor.desc = SANE_DESC_SCAN_BR_X; session->options[OPT_BR_X].descriptor.type = SANE_TYPE_FIXED; session->options[OPT_BR_X].descriptor.cap |= SANE_CAP_AUTOMATIC; session->options[OPT_BR_X].descriptor.unit = SANE_UNIT_MM; session->options[OPT_BR_X].descriptor.constraint_type = SANE_CONSTRAINT_RANGE; session->options[OPT_BR_X].descriptor.constraint.range = &x_range; session->options[OPT_BR_X].value.w = x_range.max; /* bottom-right y */ session->options[OPT_BR_Y].descriptor.name = SANE_NAME_SCAN_BR_Y; session->options[OPT_BR_Y].descriptor.title = SANE_TITLE_SCAN_BR_Y; session->options[OPT_BR_Y].descriptor.desc = SANE_DESC_SCAN_BR_Y; session->options[OPT_BR_Y].descriptor.type = SANE_TYPE_FIXED; session->options[OPT_BR_Y].descriptor.cap |= SANE_CAP_AUTOMATIC; session->options[OPT_BR_Y].descriptor.unit = SANE_UNIT_MM; session->options[OPT_BR_Y].descriptor.constraint_type = SANE_CONSTRAINT_RANGE; session->options[OPT_BR_Y].descriptor.constraint.range = &y_range; session->options[OPT_BR_Y].value.w = y_range.max; /* sensor group */ session->options[OPT_SENSOR_GROUP].descriptor.name = SANE_NAME_SENSORS; session->options[OPT_SENSOR_GROUP].descriptor.title = SANE_TITLE_SENSORS; session->options[OPT_SENSOR_GROUP].descriptor.desc = SANE_DESC_SENSORS; session->options[OPT_SENSOR_GROUP].descriptor.type = SANE_TYPE_GROUP; session->options[OPT_SENSOR_GROUP].descriptor.constraint_type = SANE_CONSTRAINT_NONE; /* page loaded sensor */ session->options[OPT_PAGE_LOADED_SW].descriptor.name = SANE_NAME_PAGE_LOADED; session->options[OPT_PAGE_LOADED_SW].descriptor.title = SANE_TITLE_PAGE_LOADED; session->options[OPT_PAGE_LOADED_SW].descriptor.desc = SANE_DESC_PAGE_LOADED; session->options[OPT_PAGE_LOADED_SW].descriptor.type = SANE_TYPE_BOOL; session->options[OPT_PAGE_LOADED_SW].descriptor.unit = SANE_UNIT_NONE; session->options[OPT_PAGE_LOADED_SW].descriptor.cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; session->options[OPT_PAGE_LOADED_SW].value.b = 0; /* calibration needed */ session->options[OPT_NEED_CALIBRATION_SW].descriptor.name = "need-calibration"; session->options[OPT_NEED_CALIBRATION_SW].descriptor.title = SANE_I18N ("Need calibration"); session->options[OPT_NEED_CALIBRATION_SW].descriptor.desc = SANE_I18N ("The scanner needs calibration for the current settings"); session->options[OPT_NEED_CALIBRATION_SW].descriptor.type = SANE_TYPE_BOOL; session->options[OPT_NEED_CALIBRATION_SW].descriptor.unit = SANE_UNIT_NONE; session->options[OPT_NEED_CALIBRATION_SW].descriptor.cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; session->options[OPT_NEED_CALIBRATION_SW].value.b = 0; /* button group */ session->options[OPT_BUTTON_GROUP].descriptor.name = "Buttons"; session->options[OPT_BUTTON_GROUP].descriptor.title = SANE_I18N ("Buttons"); session->options[OPT_BUTTON_GROUP].descriptor.desc = SANE_I18N ("Buttons"); session->options[OPT_BUTTON_GROUP].descriptor.type = SANE_TYPE_GROUP; session->options[OPT_BUTTON_GROUP].descriptor.constraint_type = SANE_CONSTRAINT_NONE; /* calibrate button */ session->options[OPT_CALIBRATE].descriptor.name = "calibrate"; session->options[OPT_CALIBRATE].descriptor.title = SANE_I18N ("Calibrate"); session->options[OPT_CALIBRATE].descriptor.desc = SANE_I18N ("Start calibration using special sheet"); session->options[OPT_CALIBRATE].descriptor.type = SANE_TYPE_BUTTON; session->options[OPT_CALIBRATE].descriptor.unit = SANE_UNIT_NONE; session->options[OPT_CALIBRATE].descriptor.cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED | SANE_CAP_AUTOMATIC; session->options[OPT_CALIBRATE].value.b = 0; /* clear calibration cache button */ session->options[OPT_CLEAR_CALIBRATION].descriptor.name = "clear"; session->options[OPT_CLEAR_CALIBRATION].descriptor.title = SANE_I18N ("Clear calibration"); session->options[OPT_CLEAR_CALIBRATION].descriptor.desc = SANE_I18N ("Clear calibration cache"); session->options[OPT_CLEAR_CALIBRATION].descriptor.type = SANE_TYPE_BUTTON; session->options[OPT_CLEAR_CALIBRATION].descriptor.unit = SANE_UNIT_NONE; session->options[OPT_CLEAR_CALIBRATION].descriptor.cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED | SANE_CAP_AUTOMATIC; session->options[OPT_CLEAR_CALIBRATION].value.b = 0; /* until work on calibration isfinished */ DISABLE (OPT_CALIBRATE); DISABLE (OPT_CLEAR_CALIBRATION); DBG (DBG_proc, "init_options: exit\n"); return SANE_STATUS_GOOD; } /** @brief physical probe of a device * This function probes for a scanning device using the given name. If the * device is managed, a model structure describing the device will be returned. * @param devicename low level device to access to probe hardware * @return NULL is the device is unsupported, or a model struct describing the * device. */ P5_Model * probe (const char *devicename) { int fd; /* open parallel port device */ fd = open_pp (devicename); if (fd < 0) { DBG (DBG_error, "probe: failed to open '%s' device!\n", devicename); return NULL; } /* now try to connect to scanner */ if (connect (fd) != SANE_TRUE) { DBG (DBG_error, "probe: failed to connect!\n"); close_pp (fd); return NULL; } /* set up for memory test */ write_reg (fd, REG1, 0x00); write_reg (fd, REG7, 0x00); write_reg (fd, REG0, 0x00); write_reg (fd, REG1, 0x00); write_reg (fd, REGF, 0x80); if (memtest (fd, 0x0100) != SANE_TRUE) { disconnect (fd); close_pp (fd); DBG (DBG_error, "probe: memory test failed!\n"); return NULL; } else { DBG (DBG_info, "memtest() OK...\n"); } write_reg (fd, REG7, 0x00); /* check for document presence 0xC6: present, 0xC3 no document */ test_document (fd); /* release device and parport for next uses */ disconnect (fd); close_pp (fd); /* for there is only one supported model, so we use hardcoded values */ DBG (DBG_proc, "probe: exit\n"); return &pagepartner_model; } /* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */ backends-1.3.0/backend/p5.conf.in000066400000000000000000000005551456256263500164770ustar00rootroot00000000000000# configuration file for the p5 backend. # configuration option to override detected model name #option modelname "Prima PagePartner" # when the parser find this line, it detects it is not an option, # then it calls the attach function with this value. # Currently only user mode parallel port support is possible: # auto, /dev/paraport* (ppdev device name) auto backends-1.3.0/backend/p5.h000066400000000000000000000125271456256263500153760ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2009-2012 stef.dev@free.fr 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, see . */ /** @file p5.h * @brief Declaration of high level structures used by the p5 backend. * * The structures and functions declared here are used to do the deal with * the SANE API. */ #ifndef P5_H #define P5_H #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_config.h" #include "../include/sane/sanei_backend.h" /**< macro to enable an option */ #define ENABLE(OPTION) session->options[OPTION].descriptor.cap &= ~SANE_CAP_INACTIVE /**< macro to disable an option */ #define DISABLE(OPTION) session->options[OPTION].descriptor.cap |= SANE_CAP_INACTIVE /** macro to test is an option is active */ #define IS_ACTIVE(OPTION) (((s->opt[OPTION].cap) & SANE_CAP_INACTIVE) == 0) /**< name of the configuration file */ #define P5_CONFIG_FILE "p5.conf" /**< macro to define texts that should translated */ #ifndef SANE_I18N #define SANE_I18N(text) text #endif /** color mode names */ /* @{ */ #define COLOR_MODE "Color" #define GRAY_MODE "Gray" #define LINEART_MODE "Lineart" /* @} */ #include "p5_device.h" /** * List of all SANE options available for the frontend. Given a specific * device, some options may be set to inactive when the scanner model is * detected. The default values and the ranges they belong maybe also model * dependent. */ enum P5_Options { OPT_NUM_OPTS = 0, /** first enum which must be zero */ /** @name standard options group */ /* @{ */ OPT_STANDARD_GROUP, OPT_MODE, /** set the mode: color, grey levels or lineart */ OPT_PREVIEW, /** set up for preview */ OPT_RESOLUTION, /** set scan's resolution */ /* @} */ /** @name geometry group * geometry related options */ /* @{ */ OPT_GEOMETRY_GROUP, /** group of options defining the position and size of the scanned area */ OPT_TL_X, /** top-left x of the scanned area*/ OPT_TL_Y, /** top-left y of the scanned area*/ OPT_BR_X, /** bottom-right x of the scanned area*/ OPT_BR_Y, /** bottom-right y of the scanned area*/ /* @} */ /** @name sensor group * detectors group */ /* @{ */ OPT_SENSOR_GROUP, OPT_PAGE_LOADED_SW, OPT_NEED_CALIBRATION_SW, /* @} */ /** @name button group * buttons group */ /* @{ */ OPT_BUTTON_GROUP, OPT_CALIBRATE, OPT_CLEAR_CALIBRATION, /* @} */ /** @name option list terminator * must come last so it can be used for array and list size */ NUM_OPTIONS }; /** * Contains one SANE option description and its value. */ typedef struct P5_Option { SANE_Option_Descriptor descriptor; /** option description */ Option_Value value; /** option value */ } P5_Option; /** * Frontend session. This struct holds information useful for * the functions defined in SANE's standard. Information closer * to the hardware are in the P5_Device structure. There is * as many session structure than frontends using the backend. */ typedef struct P5_Session { /** * Point to the next session in a linked list */ struct P5_Session *next; /** * low-level device object used by the session */ P5_Device *dev; /** * array of possible options and their values for the backend */ P5_Option options[NUM_OPTIONS]; /** * SANE_True if a scan is in progress, ie sane_start has been called. * Stay SANE_True until sane_cancel() is called. */ SANE_Bool scanning; /** @brief non blocking flag * SANE_TRUE if sane_read are non-blocking, ie returns immediately if there * is no data available from the scanning device. Modified by sane_set_io_mode() */ SANE_Bool non_blocking; /** * SANE Parameters describes what the next or current scan will be * according to the current values of the options */ SANE_Parameters params; /** * bytes to send to frontend for the scan */ SANE_Int to_send; /** * bytes currently sent to frontend during the scan */ SANE_Int sent; } P5_Session; static SANE_Status probe_p5_devices (void); static P5_Model *probe (const char *devicename); static SANE_Status config_attach (SANEI_Config * config, const char *devname, void *data); static SANE_Status attach_p5 (const char *name, SANEI_Config * config); static SANE_Status init_options (struct P5_Session *session); static SANE_Status compute_parameters (struct P5_Session *session); /* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */ #endif /* not P5_H */ backends-1.3.0/backend/p5_device.c000066400000000000000000001135221456256263500167050ustar00rootroot00000000000000/** * Description of the Primax PagePartner model */ static P5_Model pagepartner_model = { "Primax PagePartner", "Primax", "PagePartner", SANE_I18N ("sheetfed scanner"), {300, 200, 150, 100, 0}, /* 500 seems also possible */ {600, 400, 300, 200, 150, 100, 0}, 300, 600, 100, 100, 16, SANE_FIX (0.0), SANE_FIX (0.0), SANE_FIX (215.9), SANE_FIX (300.0), }; #ifdef HAVE_LINUX_PPDEV_H static char * addr_name (uint16_t addr) { switch (addr) { case DATA: return "DATA"; break; case STATUS: return "STATUS"; break; case CONTROL: return "CONTROL"; break; case EPPADR: return "EPPADR"; break; case EPPDATA: return "EPPDATA"; break; default: return "*ERROR*"; } } #endif /** @brief low level hardware access functions * @{ */ static uint8_t p5_inb (int fd, uint16_t addr) { #ifdef HAVE_LINUX_PPDEV_H uint8_t val = 0xff; int rc, mode = 0xff; switch (addr) { case DATA: rc = ioctl (fd, PPRDATA, &val); break; case STATUS: rc = ioctl (fd, PPRSTATUS, &val); break; case CONTROL: rc = ioctl (fd, PPRCONTROL, &val); break; case EPPDATA: mode = 1; /* data_reverse */ rc = ioctl (fd, PPDATADIR, &mode); mode = IEEE1284_MODE_EPP | IEEE1284_DATA; rc = ioctl (fd, PPSETMODE, &mode); #ifdef PPSETFLAGS mode = PP_FASTREAD; rc = ioctl (fd, PPSETFLAGS, &mode); #endif rc = read (fd, &val, 1); break; default: DBG (DBG_error, "p5_inb(%s) escaped ppdev\n", addr_name (addr)); return 0xFF; } if (rc < 0) { DBG (DBG_error, "ppdev ioctl returned <%s>\n", strerror (errno)); } return val; #else if(fd && addr) return 0; return 0; #endif } static void p5_outb (int fd, uint16_t addr, uint8_t value) { #ifdef HAVE_LINUX_PPDEV_H int rc = 0, mode = 0xff; switch (addr) { case DATA: rc = ioctl (fd, PPWDATA, &value); break; case CONTROL: mode = value & 0x20; rc = ioctl (fd, PPDATADIR, &mode); if (!rc) { value = value & 0xDF; rc = ioctl (fd, PPWCONTROL, &value); } break; case EPPDATA: mode = 0; /* data forward */ rc = ioctl (fd, PPDATADIR, &mode); mode = IEEE1284_MODE_EPP | IEEE1284_DATA; rc = ioctl (fd, PPSETMODE, &mode); rc = write (fd, &value, 1); break; case EPPADR: mode = 0; /* data forward */ rc = ioctl (fd, PPDATADIR, &mode); mode = IEEE1284_MODE_EPP | IEEE1284_ADDR; rc = ioctl (fd, PPSETMODE, &mode); rc = write (fd, &value, 1); break; default: DBG (DBG_error, "p5_outb(%s,0x%02x) escaped ppdev\n", addr_name (addr), value); break; } if (rc < 0) { DBG (DBG_error, "ppdev ioctl returned <%s>\n", strerror (errno)); } #else if(fd && addr && value) return; #endif /* HAVE_LINUX_PPDEV_H */ } static void write_reg (int fd, uint8_t index, uint8_t value) { uint8_t idx; /* both nibbles hold the same value */ idx = index & 0x0F; DBG (DBG_io2, "write_reg(REG%X,0x%x)\n", idx, value); idx = idx << 4 | idx; p5_outb (fd, EPPADR, idx); p5_outb (fd, EPPDATA, value); } static uint8_t read_reg (int fd, uint8_t index) { uint8_t idx; /* both nibbles hold the same value */ idx = index & 0x0F; idx = idx << 4 | idx; p5_outb (fd, EPPADR, idx); return p5_inb (fd, EPPDATA); } #ifdef HAVE_LINUX_PPDEV_H static int read_data (int fd, uint8_t * data, int length) { int mode, rc, nb; unsigned char bval; bval = REG8; mode = IEEE1284_MODE_EPP | IEEE1284_ADDR; rc = ioctl (fd, PPSETMODE, &mode); rc = write (fd, &bval, 1); mode = 1; /* data_reverse */ rc = ioctl (fd, PPDATADIR, &mode); #ifdef PPSETFLAGS mode = PP_FASTREAD; rc = ioctl (fd, PPSETFLAGS, &mode); #endif mode = IEEE1284_MODE_EPP | IEEE1284_DATA; rc = ioctl (fd, PPSETMODE, &mode); nb = 0; while (nb < length) { rc = read (fd, data + nb, length - nb); if (rc < 0) { DBG (DBG_error, "memtest: error reading data back!\n"); return 0; } else { nb += rc; } } return 1; } static void index_write_data (int fd, uint8_t index, uint8_t * data, int length) { int mode; unsigned char bval; bval = index; mode = IEEE1284_MODE_EPP | IEEE1284_ADDR; ioctl (fd, PPSETMODE, &mode); write (fd, &bval, 1); mode = IEEE1284_MODE_EPP | IEEE1284_DATA; ioctl (fd, PPSETMODE, &mode); mode = 0; /* data forward */ ioctl (fd, PPDATADIR, &mode); write (fd, data, length); return; } static void write_data (int fd, uint8_t * data, int length) { index_write_data (fd, REG8, data, length); } static void write_reg2 (int fd, uint8_t index, uint16_t value) { uint8_t data2[2]; data2[0] = value & 0xff; data2[1] = value >> 8; index_write_data (fd, index, data2, 2); } #else static int read_data (int fd, uint8_t * data, int length) { if(fd && data && length) return -1; return -1; } static void write_data (int fd, uint8_t * data, int length) { if(fd && data && length) return; } static void write_reg2 (int fd, uint8_t index, uint16_t value) { if(fd && index && value) return; } #endif /** * @} */ /** @brief This function checks a memory buffer. * This function writes at the given memory address then read it back * to check the scanner is correctly working. * @param fd file descriptor used to access hardware * @param addr address where to write and read * @return SANE_TRUE on success, SANE_FALSE otherwise */ static int memtest (int fd, uint16_t addr) { uint8_t sent[256]; uint8_t back[256]; int i; write_reg2 (fd, REG1, addr); for (i = 0; i < 256; i++) { sent[i] = (uint8_t) i; back[i] = 0; } write_data (fd, sent, 256); read_data (fd, back, 256); /* check if data read back is the same that the one sent */ for (i = 0; i < 256; i++) { if (back[i] != sent[i]) { return SANE_FALSE; } } return SANE_TRUE; } #define P5_INB(k,y,z) val=p5_inb(k,y); if(val!=z) { DBG(DBG_error,"expected 0x%02x, got 0x%02x\n",z, val); return SANE_FALSE; } /** @brief connect to scanner * This function sends the connect sequence for the scanner. * @param fd filedescriptor of the parallel port communication channel * @return SANE_TRUE in case of success, SANE_FALSE otherwise */ static int connect (int fd) { uint8_t val; p5_inb (fd, CONTROL); p5_outb (fd, CONTROL, 0x04); p5_outb (fd, DATA, 0x02); P5_INB (fd, DATA, 0x02); p5_outb (fd, DATA, 0x03); P5_INB (fd, DATA, 0x03); p5_outb (fd, DATA, 0x03); p5_outb (fd, DATA, 0x83); p5_outb (fd, DATA, 0x03); p5_outb (fd, DATA, 0x83); P5_INB (fd, DATA, 0x83); p5_outb (fd, DATA, 0x82); P5_INB (fd, DATA, 0x82); p5_outb (fd, DATA, 0x02); p5_outb (fd, DATA, 0x82); p5_outb (fd, DATA, 0x02); p5_outb (fd, DATA, 0x82); P5_INB (fd, DATA, 0x82); p5_outb (fd, DATA, 0x82); P5_INB (fd, DATA, 0x82); p5_outb (fd, DATA, 0x02); p5_outb (fd, DATA, 0x82); p5_outb (fd, DATA, 0x02); p5_outb (fd, DATA, 0x82); P5_INB (fd, DATA, 0x82); p5_outb (fd, DATA, 0x83); P5_INB (fd, DATA, 0x83); p5_outb (fd, DATA, 0x03); p5_outb (fd, DATA, 0x83); p5_outb (fd, DATA, 0x03); p5_outb (fd, DATA, 0x83); P5_INB (fd, DATA, 0x83); p5_outb (fd, DATA, 0x82); P5_INB (fd, DATA, 0x82); p5_outb (fd, DATA, 0x02); p5_outb (fd, DATA, 0x82); p5_outb (fd, DATA, 0x02); p5_outb (fd, DATA, 0x82); P5_INB (fd, DATA, 0x82); p5_outb (fd, DATA, 0x83); P5_INB (fd, DATA, 0x83); p5_outb (fd, DATA, 0x03); p5_outb (fd, DATA, 0x83); p5_outb (fd, DATA, 0x03); p5_outb (fd, DATA, 0x83); P5_INB (fd, DATA, 0x83); p5_outb (fd, DATA, 0x83); P5_INB (fd, DATA, 0x83); p5_outb (fd, DATA, 0x03); p5_outb (fd, DATA, 0x83); p5_outb (fd, DATA, 0x03); p5_outb (fd, DATA, 0x83); P5_INB (fd, DATA, 0x83); p5_outb (fd, DATA, 0x82); P5_INB (fd, DATA, 0x82); p5_outb (fd, DATA, 0x02); p5_outb (fd, DATA, 0x82); p5_outb (fd, DATA, 0x02); p5_outb (fd, DATA, 0x82); p5_outb (fd, DATA, 0xFF); DBG (DBG_info, "connect() OK...\n"); return SANE_TRUE; } static int disconnect (int fd) { uint8_t val; p5_outb (fd, CONTROL, 0x04); p5_outb (fd, DATA, 0x00); P5_INB (fd, DATA, 0x00); p5_outb (fd, DATA, 0x01); P5_INB (fd, DATA, 0x01); p5_outb (fd, DATA, 0x01); p5_outb (fd, DATA, 0x81); p5_outb (fd, DATA, 0x01); p5_outb (fd, DATA, 0x81); P5_INB (fd, DATA, 0x81); p5_outb (fd, DATA, 0x80); P5_INB (fd, DATA, 0x80); p5_outb (fd, DATA, 0x00); p5_outb (fd, DATA, 0x80); p5_outb (fd, DATA, 0x00); p5_outb (fd, DATA, 0x80); P5_INB (fd, DATA, 0x80); p5_outb (fd, DATA, 0x80); P5_INB (fd, DATA, 0x80); p5_outb (fd, DATA, 0x00); p5_outb (fd, DATA, 0x80); p5_outb (fd, DATA, 0x00); p5_outb (fd, DATA, 0x80); P5_INB (fd, DATA, 0x80); p5_outb (fd, DATA, 0x81); P5_INB (fd, DATA, 0x81); p5_outb (fd, DATA, 0x01); p5_outb (fd, DATA, 0x81); p5_outb (fd, DATA, 0x01); p5_outb (fd, DATA, 0x81); P5_INB (fd, DATA, 0x81); p5_outb (fd, DATA, 0x80); P5_INB (fd, DATA, 0x80); p5_outb (fd, DATA, 0x00); p5_outb (fd, DATA, 0x80); p5_outb (fd, DATA, 0x00); p5_outb (fd, DATA, 0x80); P5_INB (fd, DATA, 0x80); p5_outb (fd, DATA, 0x00); p5_outb (fd, DATA, 0x80); p5_outb (fd, DATA, 0x00); p5_outb (fd, DATA, 0x80); P5_INB (fd, DATA, 0x80); p5_outb (fd, DATA, 0x00); p5_outb (fd, DATA, 0x80); p5_outb (fd, DATA, 0x00); p5_outb (fd, DATA, 0x80); P5_INB (fd, DATA, 0x80); p5_outb (fd, DATA, 0x00); p5_outb (fd, DATA, 0x80); p5_outb (fd, DATA, 0x00); p5_outb (fd, DATA, 0x80); p5_inb (fd, CONTROL); p5_outb (fd, CONTROL, 0x0C); return SANE_STATUS_GOOD; } static void setadresses (int fd, uint16_t start, uint16_t end) { write_reg (fd, REG3, start & 0xff); write_reg (fd, REG4, start >> 8); write_reg (fd, REG5, end & 0xff); write_reg (fd, REG6, end >> 8); DBG (DBG_io, "setadresses(0x%x,0x%x); OK...\n", start, end); } #ifdef HAVE_LINUX_PPDEV_H /** @brief open parallel port device * opens parallel port's low level device in EPP mode * @param devicename nam of the real device or the special value 'auto' * @return file descriptor in cas of successn -1 otherwise */ static int open_pp (const char *devicename) { int fd, mode = 0; char *name; DBG (DBG_proc, "open_pp: start, devicename=%s\n", devicename); /* TODO improve auto device finding */ if (strncmp (devicename, "auto", 4) == 0) { name = strdup("/dev/parport0"); } else { name = strdup(devicename); } /* open device */ fd = open (name, O_RDWR); if (fd < 0) { switch (errno) { case ENOENT: #ifdef ENIO case ENXIO: #endif #ifdef ENODEV case ENODEV: #endif DBG (DBG_error, "open_pp: no %s device ...\n", name); break; case EACCES: DBG (DBG_error, "open_pp: current user cannot use existing %s device ...\n", name); break; default: DBG (DBG_error, "open_pp: %s while opening %s\n", strerror (errno), name); } return -1; } free(name); /* claim device and set it to EPP */ ioctl (fd, PPCLAIM); ioctl (fd, PPGETMODES, &mode); if (mode & PARPORT_MODE_PCSPP) DBG (DBG_io, "PARPORT_MODE_PCSPP\n"); if (mode & PARPORT_MODE_TRISTATE) DBG (DBG_io, "PARPORT_MODE_TRISTATE\n"); if (mode & PARPORT_MODE_EPP) DBG (DBG_io, "PARPORT_MODE_EPP\n"); if (mode & PARPORT_MODE_ECP) DBG (DBG_io, "PARPORT_MODE_ECP\n"); if (mode & PARPORT_MODE_COMPAT) DBG (DBG_io, "PARPORT_MODE_COMPAT\n"); if (mode & PARPORT_MODE_DMA) DBG (DBG_io, "PARPORT_MODE_DMA\n"); if (mode & PARPORT_MODE_EPP) { mode = IEEE1284_MODE_EPP; } else { /* if (mode & PARPORT_MODE_ECP) { mode = IEEE1284_MODE_ECP; } else */ { mode = -1; } } if (mode == -1) { DBG (DBG_error, "open_pp: no EPP mode, giving up ...\n"); ioctl (fd, PPRELEASE); close (fd); return -1; } ioctl (fd, PPNEGOT, &mode); ioctl (fd, PPSETMODE, &mode); DBG (DBG_proc, "open_pp: exit\n"); return fd; } /** close low level device * release and close low level hardware device */ static void close_pp (int fd) { int mode = IEEE1284_MODE_COMPAT; if (fd > 2) { ioctl (fd, PPNEGOT, &mode); ioctl (fd, PPRELEASE); close (fd); } } #else /* HAVE_LINUX_PPDEV_H */ static int open_pp (const char *devicename) { if(devicename) return -1; return -1; } static void close_pp (int fd) { if(fd) return; } #endif /* HAVE_LINUX_PPDEV_H */ /** @brief test if a document is inserted * Test if a document is inserted by reading register E * @param fd file descriptor to access scanner * @return SANE_STATUS_NO_DOCS if no document or SANE_STATUS_GOOD * if something is present. */ static SANE_Status test_document (int fd) { int detector; /* check for document presence 0xC6: present, 0xC3 no document */ detector = read_reg (fd, REGE); DBG (DBG_io, "test_document: detector=0x%02X\n", detector); /* document inserted */ if (detector & 0x04) return SANE_STATUS_GOOD; return SANE_STATUS_NO_DOCS; } /** * return the amount of scanned data available * @param fd file descriptor to access scanner * @return available byte number */ static int available_bytes (int fd) { int counter; /* read the number of 256 bytes block of scanned data */ counter = read_reg (fd, REG9); DBG (DBG_io, "available_bytes: available_bytes=0x%02X\n", counter); return 256 * counter; } static SANE_Status build_correction (P5_Device * dev, unsigned int dpi, unsigned int mode, unsigned int start, unsigned int width) { unsigned int i, j, shift, step; DBG (DBG_proc, "build_correction: start=%d, width=%d\n", start, width); DBG (DBG_trace, "build_correction: dpi=%d, mode=%d\n", dpi, mode); /* loop on calibration data to find the matching one */ j = 0; while (dev->calibration_data[j]->dpi != dpi) { j++; if (j > MAX_RESOLUTIONS) { DBG (DBG_error, "build_correction: couldn't find calibration!\n"); return SANE_STATUS_INVAL; } } if (dev->gain != NULL) { free (dev->gain); dev->gain = NULL; } if (dev->offset != NULL) { free (dev->offset); dev->offset = NULL; } dev->gain = (float *) malloc (width * sizeof (float)); if (dev->gain == NULL) { DBG (DBG_error, "build_correction: failed to allocate memory for gain!\n"); return SANE_STATUS_NO_MEM; } dev->offset = (uint8_t *) malloc (width); if (dev->offset == NULL) { DBG (DBG_error, "build_correction: failed to allocate memory for offset!\n"); return SANE_STATUS_NO_MEM; } /* compute starting point of calibration data to use */ shift = start; step = 1; if (mode == MODE_GRAY) { /* we use green data */ shift += 1; step = 3; } for (i = 0; i < width; i += step) { if (dev->calibration_data[j]->white_data[shift + i] - dev->calibration_data[0]->black_data[shift + i] > BLACK_LEVEL) { dev->gain[i] = WHITE_TARGET / ((float) (dev->calibration_data[j]->white_data[shift + i] - dev->calibration_data[j]->black_data[shift + i])); dev->offset[i] = dev->calibration_data[j]->black_data[shift + i]; } else { dev->gain[i] = 1.0; dev->offset[i] = 0; } } return SANE_STATUS_GOOD; DBG (DBG_proc, "build_correction: end\n"); } /** @brief start up a real scan * This function starts the scan with the given parameters. * @param dev device describing hardware * @param mode color, gray level or lineart. * @param dpi desired scan resolution. * @param startx coordinate of the first pixel to scan in * scan's resolution coordinate * @param width width of the scanned area * scanner's physical scan aread. * @return SANE_STATUS_GOOD if scan is successfully started */ static SANE_Status start_scan (P5_Device * dev, int mode, unsigned int dpi, unsigned int startx, unsigned int width) { uint8_t reg0=0; uint8_t reg2=0; uint8_t regF=0; uint16_t addr=0; uint16_t start, end; unsigned int xdpi; DBG (DBG_proc, "start_scan: start \n"); DBG (DBG_io, "start_scan: startx=%d, width=%d, dpi=%d\n", startx, width, dpi); /** @brief register values * - reg2 : reg2 seems related to x dpi and provides only 100, * 150, 200 and 300 resolutions. * - regF : lower nibble gives y dpi resolution ranging from 150 * to 1200 dpi. */ xdpi = dpi; switch (dpi) { case 100: reg2 = 0x90; regF = 0xA2; break; case 150: reg2 = 0x10; regF = 0xA4; break; case 200: reg2 = 0x80; regF = 0xA6; break; case 300: reg2 = 0x00; regF = 0xA8; break; case 400: reg2 = 0x80; /* xdpi=200 */ regF = 0xAA; xdpi = 200; break; case 500: reg2 = 0x00; regF = 0xAC; xdpi = 300; break; case 600: reg2 = 0x00; regF = 0xAE; xdpi = 300; break; } switch (mode) { case MODE_COLOR: reg0 = 0x00; addr = 0x0100; break; case MODE_GRAY: /* green channel only */ reg0 = 0x20; addr = 0x0100; break; case MODE_LINEART: reg0 = 0x40; addr = 0x0908; break; } write_reg (dev->fd, REG1, 0x01); write_reg (dev->fd, REG7, 0x00); write_reg (dev->fd, REG0, reg0); write_reg (dev->fd, REG1, 0x00); write_reg (dev->fd, REGF, regF); /* the memory addr used to test need not to be related * to resolution, 0x0100 could be always used */ /* TODO get rid of it */ memtest (dev->fd, addr); /* handle case where dpi>xdpi */ start = startx; if (dpi > xdpi) { width = (width * xdpi) / dpi; start = (startx * xdpi) / dpi; } /* compute and set start addr */ if (mode == MODE_COLOR) { start = start * 3; width = width * 3; } end = start + width + 1; /* build calibration data for the scan */ if (dev->calibrated) { build_correction (dev, xdpi, mode, start, width); } setadresses (dev->fd, start, end); write_reg (dev->fd, REG1, addr >> 8); write_reg (dev->fd, REG2, reg2); regF = (regF & 0x0F) | 0x80; write_reg (dev->fd, REGF, regF); write_reg (dev->fd, REG0, reg0); if (mode == MODE_LINEART) { write_reg (dev->fd, 0x07, 0x04); } else { write_reg (dev->fd, 0x07, 0x00); } write_reg (dev->fd, REG1, addr >> 8); write_reg2 (dev->fd, REG1, addr); write_reg (dev->fd, REGF, regF | 0x01); write_reg (dev->fd, REG0, reg0 | 0x0C); if (mode == MODE_LINEART) { write_reg (dev->fd, REG1, 0x19); } else { write_reg (dev->fd, REG1, 0x11); } DBG (DBG_proc, "start_scan: exit\n"); return SANE_STATUS_GOOD; } /** read a line of scan data * @param dev device to read * @param data pointer where to store data * @param length total bytes to read on one line * @param ltr total number of lines to read * @param retry signals that the function must read as much lines it can * @param x2 tells that lines must be enlarged by a 2 factor * @param mode COLOR_MODE if color mode * @returns number of data lines read, -1 in case of error */ static int read_line (P5_Device * dev, uint8_t * data, size_t length, int ltr, SANE_Bool retry, SANE_Bool x2, int mode, SANE_Bool correction) { uint8_t counter, read, cnt; uint8_t inbuffer[MAX_SENSOR_PIXELS * 2 * 3 + 2]; unsigned int i, factor; float val; DBG (DBG_proc, "read_line: trying to read %d lines of %lu bytes\n", ltr, (unsigned long)length); counter = read_reg (dev->fd, REG9); DBG (DBG_io, "read_line: %d bytes available\n", counter * 256); read = 0; if (x2 == SANE_FALSE) { factor = 1; } else { factor = 2; } /* in retry mode we read until not enough data, but in no retry * read only one line , counter give us 256 bytes block available * and we want an number multiple of color channels */ cnt = (255 + length / factor) / 256; while ((counter > cnt && retry == 1) || (counter > cnt && read == 0)) { /* read data from scanner, first and last byte aren't picture data */ read_data (dev->fd, inbuffer, length / factor + 2); /* image correction */ if (correction == SANE_TRUE) { for (i = 0; i < length / factor; i++) { val = inbuffer[i + 1] - dev->offset[i]; if (val > 0) { val = val * dev->gain[i]; if (val < 255) inbuffer[i + 1] = val; else inbuffer[i + 1] = 255; } else { inbuffer[i + 1] = 0; } } } /* handle horizontal data doubling */ if (x2 == SANE_FALSE) { memcpy (data + read * length, inbuffer + 1, length); } else { if (mode == MODE_COLOR) { for (i = 0; i < length / factor; i += 3) { data[read * length + i * factor] = inbuffer[i + 1]; data[read * length + i * factor + 1] = inbuffer[i + 2]; data[read * length + i * factor + 2] = inbuffer[i + 3]; data[read * length + i * factor + 3] = inbuffer[i + 1]; data[read * length + i * factor + 4] = inbuffer[i + 2]; data[read * length + i * factor + 5] = inbuffer[i + 3]; } } else { for (i = 0; i < length / factor; i++) { data[read * length + i * factor] = inbuffer[i + 1]; data[read * length + i * factor + 1] = inbuffer[i + 1]; } } } read++; if (retry == SANE_TRUE) { read_reg (dev->fd, REGF); read_reg (dev->fd, REGA); read_reg (dev->fd, REG9); counter = read_reg (dev->fd, REG9); read_reg (dev->fd, REGA); if (read >= ltr) { DBG (DBG_io, "read_line returning %d lines\n", read); return read; } counter = read_reg (dev->fd, REG9); } } read_reg (dev->fd, REGF); read_reg (dev->fd, REGA); read_reg (dev->fd, REG9); counter = read_reg (dev->fd, REG9); read_reg (dev->fd, REGA); DBG (DBG_io, "read_line returning %d lines\n", read); return read; } static SANE_Status eject (int fd) { int detector; DBG (DBG_proc, "eject: start ...\n"); do { write_reg2 (fd, REG1, 0x1110); detector = read_reg (fd, REGE); detector = read_reg (fd, REGE); } while ((detector & 0x04) != 0); write_reg (fd, REG0, 0x00); write_reg (fd, REG1, 0x00); write_reg (fd, REGF, 0x82); write_reg (fd, REG7, 0x00); DBG (DBG_proc, "eject: end.\n"); return SANE_STATUS_GOOD; } /** @brief wait for document to be present in feeder * Polls document sensor until something is present. Give up after 20 seconds * @param fd file descriptor of the physical device */ /* static int wait_document (int fd, uint8_t detector) { int count = 0; uint8_t val; write_reg (fd, REG1, 0x00); write_reg (fd, REG7, 0x00); detector = read_reg (fd, REGE); while (detector == 0xc3 && count < 20) { sleep (1); count++; detector = read_reg (fd, REGE); } setadresses (fd, 0x002d, 0x09c7); write_reg (fd, REG1, 0x00); write_reg (fd, REG2, 0x90); write_reg (fd, REGF, 0x82); write_reg (fd, REG0, 0x00); val = p5_inb (fd, STATUS) & 0xf8; if (val != 0xf8) { DBG (DBG_error, "wait_document: unexpected STATUS value 0x%02x instead of 0xf8", val); } if (count >= 20) { DBG (DBG_error, "wait_document: failed to detect document!\n"); return 0; } return 1; } */ /** @brief move at 150 dpi * move the paper at 150 dpi motor speed by the amount specified * @params dev pointer to the device structure */ static SANE_Status move (P5_Device * dev) { int skip, done, read, count; SANE_Status status = SANE_STATUS_GOOD; unsigned char buffer[256]; DBG (DBG_proc, "move: start\n"); /* compute number of lines to skip */ skip = dev->ystart; /* works, but remains to be explained ... */ if (dev->ydpi > 300) skip = skip / 2; DBG (DBG_io, "move: skipping %d lines at %d dpi\n", skip, dev->ydpi); /* we do a real scan of small width, discarding data */ done = 0; status = start_scan (dev, MODE_GRAY, dev->ydpi, 0, 256); if (status != SANE_STATUS_GOOD) { DBG (DBG_error, "move: failed to start scan\n"); return SANE_STATUS_INVAL; } do { /* test if document left the feeder */ status = test_document (dev->fd); if (status == SANE_STATUS_NO_DOCS) { DBG (DBG_info, "move: document was shorter than the required move\n"); return SANE_STATUS_INVAL; } /* test if data is available */ count = available_bytes (dev->fd); if (count) { read = read_line (dev, buffer, 256, 1, SANE_FALSE, SANE_FALSE, MODE_GRAY, SANE_FALSE); if (read == -1) { DBG (DBG_error, "move: failed to read data\n"); return SANE_STATUS_INVAL; } done += read; } } while (done < skip); /* reset scanner */ write_reg2 (dev->fd, REG1, 0x1110); count = read_reg (dev->fd, REGE); count = read_reg (dev->fd, REGE); write_reg (dev->fd, REG0, 0x00); write_reg (dev->fd, REG1, 0x00); write_reg (dev->fd, REGF, 0x82); write_reg (dev->fd, REG7, 0x00); DBG (DBG_proc, "move: exit\n"); return status; } /** clean up calibration data * @param dev device to clean up */ static void cleanup_calibration (P5_Device * dev) { int i; for (i = 0; i < MAX_RESOLUTIONS * 2; i++) { if (dev->calibration_data[i] != NULL) { free (dev->calibration_data[i]); dev->calibration_data[i] = NULL; } } dev->calibrated = SANE_FALSE; } /** detect a black scan line * parses the given buffer and return SANE_TRUE if the line is an * acceptable black line for calibration * @param buffer data line to parse * @param pixels number of pixels * @param mode MODE_COLOR or MODE_GRAY * @returns SANE_TRUE if it is considered as a white line */ static SANE_Bool is_black_line (uint8_t * buffer, unsigned int pixels, int mode) { unsigned int i, start, end, count, width; /* compute width in bytes */ if (mode == MODE_COLOR) { width = pixels * 3; } else { width = pixels; } /* we allow the calibration target to be narrower than full width, ie * black margin at both ends of the line */ start = (5 * width) / 100; end = (95 * width) / 100; count = 0; /* count number of black bytes */ for (i = start; i < end; i++) { if (buffer[i] > BLACK_LEVEL) { count++; } } /* we allow 3% black pixels maximum */ if (count > (3 * width) / 100) { DBG (DBG_io, "is_black_line=SANE_FALSE\n"); return SANE_FALSE; } DBG (DBG_io, "is_black_line=SANE_TRUE\n"); return SANE_TRUE; } /** detect a white scan line * parses the given buffer and return SANE_TRUE if the line is an * acceptable white line for calibration * @param buffer data line to parse * @param pixels number of pixels * @param mode MODE_COLOR or MODE_GRAY * @returns SANE_TRUE if it is considered as a white line */ static SANE_Bool is_white_line (uint8_t * buffer, unsigned int pixels, int mode) { unsigned int i, start, end, count, width; /* compute width in bytes */ if (mode == MODE_COLOR) { width = pixels * 3; } else { width = pixels; } /* we allow the calibration target to be narrower than full width, ie * black margin at both ends of the line */ start = (5 * width) / 100; end = (95 * width) / 100; count = 0; /* count number of black bytes */ for (i = start; i < end; i++) { if (buffer[i] < BLACK_LEVEL) { count++; } } /* we allow 3% black pixels maximum */ if (count > (3 * width) / 100) { DBG (DBG_io, "is_white_line=SANE_FALSE\n"); return SANE_FALSE; } DBG (DBG_io, "is_white_line=SANE_TRUE\n"); return SANE_TRUE; } /* ------------------------------------------------------------------------- */ /* writes gray data to a pnm file */ /* static void write_gray_data (unsigned char *image, char *name, SANE_Int width, SANE_Int height) { FILE *fdbg = NULL; fdbg = fopen (name, "wb"); if (fdbg == NULL) return; fprintf (fdbg, "P5\n%d %d\n255\n", width, height); fwrite (image, width, height, fdbg); fclose (fdbg); } */ /* ------------------------------------------------------------------------- */ /* writes rgb data to a pnm file */ static void write_rgb_data (char *name, unsigned char *image, SANE_Int width, SANE_Int height) { FILE *fdbg = NULL; fdbg = fopen (name, "wb"); if (fdbg == NULL) return; fprintf (fdbg, "P6\n%d %d\n255\n", width, height); fwrite (image, width * 3, height, fdbg); fclose (fdbg); } /** give calibration file name * computes the calibration file name to use based on the * backend name and device */ static char * calibration_file (const char *devicename) { char *ptr = NULL; char tmp_str[PATH_MAX]; ptr = getenv ("HOME"); if (ptr != NULL) { sprintf (tmp_str, "%s/.sane/p5-%s.cal", ptr, devicename); } else { ptr = getenv ("TMPDIR"); if (ptr != NULL) { sprintf (tmp_str, "%s/p5-%s.cal", ptr, devicename); } else { sprintf (tmp_str, "/tmp/p5-%s.cal", devicename); } } DBG (DBG_trace, "calibration_file: using >%s< for calibration file name\n", tmp_str); return strdup (tmp_str); } /** restore calibration data * restore calibration data by loading previously saved calibration data * @param dev device to restore * @return SANE_STATUS_GOOD on success, otherwise error code */ static SANE_Status restore_calibration (P5_Device * dev) { SANE_Status status = SANE_STATUS_GOOD; char *fname = NULL; FILE *fcalib = NULL; size_t size; int i; DBG (DBG_proc, "restore_calibration: start\n"); cleanup_calibration (dev); fname = calibration_file (dev->model->name); fcalib = fopen (fname, "rb"); if (fcalib == NULL) { DBG (DBG_error, "restore_calibration: failed to open %s!\n", fname); free (fname); return SANE_STATUS_IO_ERROR; } /* loop filling calibration data until EOF reached */ i = 0; while (!feof (fcalib) && (i < 2 * MAX_RESOLUTIONS)) { dev->calibration_data[i] = malloc (sizeof (P5_Calibration_Data)); if (dev->calibration_data[i] == NULL) { cleanup_calibration (dev); free (fname); fclose (fcalib); DBG (DBG_error, "restore_calibration: failed to allocate memory for calibration\n"); return SANE_STATUS_NO_MEM; } size = fread (dev->calibration_data[i], 1, sizeof (P5_Calibration_Data), fcalib); if (feof (fcalib)) { free (dev->calibration_data[i]); dev->calibration_data[i] = NULL; } else if (size != sizeof (P5_Calibration_Data)) { cleanup_calibration (dev); free (fname); fclose (fcalib); DBG (DBG_error, "restore_calibration: failed to read from file\n"); return SANE_STATUS_IO_ERROR; } DBG (DBG_trace, "restore_calibration: read 1 calibration structure from file\n"); i++; } dev->calibrated = SANE_TRUE; fclose (fcalib); free (fname); DBG (DBG_proc, "restore_calibration: end\n"); return status; } /** save calibration data * save calibration data from memory to file * @param dev device calibration to save * @return SANE_STATUS_GOOD on success, otherwise error code */ static SANE_Status save_calibration (P5_Device * dev) { SANE_Status status = SANE_STATUS_GOOD; char *fname = NULL; FILE *fcalib = NULL; int i; size_t size; DBG (DBG_proc, "save_calibration: start\n"); fname = calibration_file (dev->model->name); fcalib = fopen (fname, "wb"); if (fcalib == NULL) { DBG (DBG_error, "save_calibration: failed to open %s!\n", fname); free (fname); return SANE_STATUS_IO_ERROR; } /* loop filling calibration data until EOF reached */ i = 0; while (dev->calibration_data[i] != NULL && (i < 2 * MAX_RESOLUTIONS)) { size = fwrite (dev->calibration_data[i], sizeof (P5_Calibration_Data), 1, fcalib); if (size != sizeof (P5_Calibration_Data)) { free (fname); fclose (fcalib); DBG (DBG_error, "save_calibration: failed to write to file\n"); return SANE_STATUS_IO_ERROR; } DBG (DBG_trace, "save_calibration: wrote 1 calibration structure to file\n"); i++; } fclose (fcalib); free (fname); DBG (DBG_proc, "save_calibration: end\n"); return status; } /** calibrate scanner * calibrates scanner by scanning a white sheet to get * reference data. The black reference data is extracted from the lines * that precede the physical document. * Calibration is done at 300 color, then data is built for other modes * and resolutions. * @param dev device to calibrate */ static SANE_Status sheetfed_calibration (P5_Device * dev) { uint8_t buffer[MAX_SENSOR_PIXELS * 3]; uint16_t white_data[MAX_SENSOR_PIXELS * 3]; uint16_t black_data[MAX_SENSOR_PIXELS * 3]; unsigned int i, j, k, dpi, pixels, read, black, white; float coeff; unsigned int red, green, blue; int line; SANE_Status status; char title[40]; FILE *dbg = fopen ("debug.pnm", "wb"); fprintf (dbg, "P6\n%d %d\n255\n", MAX_SENSOR_PIXELS, CALIBRATION_SKIP_LINES * 4); DBG (DBG_proc, "sheetfed_calibration: start\n"); /* check calibration target has been loaded in ADF */ status = test_document (dev->fd); if (status == SANE_STATUS_NO_DOCS) { DBG (DBG_error, "sheetfed_calibration: no calibration target present!\n"); return SANE_STATUS_NO_DOCS; } /* clean up calibration data */ cleanup_calibration (dev); /* a RGB scan to get reference data */ /* initialize calibration slot for the resolution */ i = 0; dpi = dev->model->max_xdpi; pixels = MAX_SENSOR_PIXELS; dev->calibration_data[i] = (P5_Calibration_Data *) malloc (sizeof (P5_Calibration_Data)); if (dev->calibration_data[i] == NULL) { cleanup_calibration (dev); DBG (DBG_error, "sheetfed_calibration: failed to allocate memory for calibration\n"); return SANE_STATUS_NO_MEM; } dev->calibration_data[i]->dpi = dpi; /* start scan */ status = start_scan (dev, MODE_COLOR, dpi, 0, pixels); if (status != SANE_STATUS_GOOD) { cleanup_calibration (dev); DBG (DBG_error, "sheetfed_calibration: failed to start scan at %d dpi\n", dpi); return SANE_STATUS_INVAL; } white = 0; black = 0; read = 0; for (j = 0; j < pixels * 3; j++) { black_data[j] = 0; white_data[j] = 0; } /* read lines and gather black and white ones until enough for sensor's * native resolution */ do { status = test_document (dev->fd); if (status == SANE_STATUS_NO_DOCS && (white < 10 || black < 10)) { cleanup_calibration (dev); DBG (DBG_error, "sheetfed_calibration: calibration sheet too short!\n"); return SANE_STATUS_INVAL; } memset (buffer, 0x00, MAX_SENSOR_PIXELS * 3); line = read_line (dev, buffer, pixels * 3, 1, SANE_FALSE, SANE_FALSE, MODE_COLOR, SANE_FALSE); if (line == -1) { DBG (DBG_error, "sheetfed_calibration: failed to read data\n"); return SANE_STATUS_INVAL; } /* if a data line has been read, add it to reference data */ if (line) { read++; fwrite (buffer, pixels * 3, 1, dbg); if (is_white_line (buffer, pixels, MODE_COLOR) && white < 256) { white++; /* first calibration lines are skipped */ for (j = 0; j < pixels * 3 && read > CALIBRATION_SKIP_LINES; j++) { white_data[j] += buffer[j]; } } if (is_black_line (buffer, pixels, MODE_COLOR) && black < 256) { black++; for (j = 0; j < pixels * 3; j++) { black_data[j] += buffer[j]; } } } } while (test_document (dev->fd) != SANE_STATUS_NO_DOCS); DBG (DBG_trace, "sheetfed_calibration: white lines=%d, black lines=%d\n", white, black); /* average pixels and store in per dpi calibration data */ for (j = 0; j < pixels * 3; j++) { dev->calibration_data[i]->white_data[j] = white_data[j] / white; dev->calibration_data[i]->black_data[j] = black_data[j] / black; } /* we average red, green and blue offset on the full sensor */ red = 0; green = 0; blue = 0; for (j = 0; j < pixels * 3; j += 3) { red += dev->calibration_data[i]->black_data[j]; green += dev->calibration_data[i]->black_data[j + 1]; blue += dev->calibration_data[i]->black_data[j + 2]; } for (j = 0; j < pixels * 3; j += 3) { dev->calibration_data[i]->black_data[j] = red / pixels; dev->calibration_data[i]->black_data[j + 1] = green / pixels; dev->calibration_data[i]->black_data[j + 2] = blue / pixels; } /* trace calibration data for debug */ if (DBG_LEVEL > DBG_data) { sprintf (title, "calibration-white-%d.pnm", dev->calibration_data[i]->dpi); write_rgb_data (title, dev->calibration_data[i]->white_data, pixels, 1); sprintf (title, "calibration-black-%d.pnm", dev->calibration_data[i]->dpi); write_rgb_data (title, dev->calibration_data[i]->black_data, pixels, 1); } /* loop on all remaining resolution and compute calibration data from it */ for (i = 1; i < MAX_RESOLUTIONS && dev->model->xdpi_values[i] > 0; i++) { dev->calibration_data[i] = (P5_Calibration_Data *) malloc (sizeof (P5_Calibration_Data)); if (dev->calibration_data[i] == NULL) { cleanup_calibration (dev); DBG (DBG_error, "sheetfed_calibration: failed to allocate memory for calibration\n"); return SANE_STATUS_INVAL; } dev->calibration_data[i]->dpi = dev->model->xdpi_values[i]; coeff = ((float) dev->model->xdpi_values[i]) / (float) dpi; /* generate data by decimation */ for (j = 0; j < pixels / coeff; j++) { k = j * coeff; dev->calibration_data[i]->white_data[j] = dev->calibration_data[0]->white_data[k]; dev->calibration_data[i]->white_data[j + 1] = dev->calibration_data[0]->white_data[k + 1]; dev->calibration_data[i]->white_data[j + 2] = dev->calibration_data[0]->white_data[k + 2]; dev->calibration_data[i]->black_data[j] = dev->calibration_data[0]->black_data[k]; dev->calibration_data[i]->black_data[j + 1] = dev->calibration_data[0]->black_data[k + 1]; dev->calibration_data[i]->black_data[j + 2] = dev->calibration_data[0]->black_data[k + 2]; } } fclose (dbg); dev->calibrated = SANE_TRUE; /* eject calibration target */ eject (dev->fd); DBG (DBG_proc, "sheetfed_calibration: end\n"); return SANE_STATUS_GOOD; } /* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */ backends-1.3.0/backend/p5_device.h000066400000000000000000000175241456256263500167170ustar00rootroot00000000000000/* sane - Scanner Access Now Easy. Copyright (C) 2009-2012 stef.dev@free.fr 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, see . */ /** @file p5_device.h * @brief Declaration of low level structures used by the p5 backend. * * The structures and function declared here are used to do the low level * communication with the physical device. */ #ifndef P5_DEVICE_H #define P5_DEVICE_H #include #include #include #include "../include/_stdint.h" #ifdef HAVE_LINUX_PPDEV_H #include #include #include #include #include #include #endif /** @name debugging levels */ /* @{ */ #define DBG_error0 0 /* errors/warnings printed even with devuglevel 0 */ #define DBG_error 1 /* fatal errors */ #define DBG_warn 2 /* warnings and non-fatal errors */ #define DBG_info 4 /* informational messages */ #define DBG_proc 8 /* starting/finishing functions */ #define DBG_trace 16 /* tracing messages */ #define DBG_io 32 /* io functions */ #define DBG_io2 64 /* io functions that are called very often */ #define DBG_data 128 /* log image data */ /* @} */ /** * maximal number of resolutions */ #define MAX_RESOLUTIONS 8 /**> sensor's number of pixels 8.5' @ 300 dpi */ #define MAX_SENSOR_PIXELS 2550 /**> number of lines to skip when doing calibration */ #define CALIBRATION_SKIP_LINES 80 /**> last value considered as black for calibration */ #define BLACK_LEVEL 40 /**> white target value for calibration */ #define WHITE_TARGET 220.0 /** per dpi calibration rgb data * Calibration data structure */ typedef struct P5_Calibration_Data { unsigned int dpi; uint8_t black_data[MAX_SENSOR_PIXELS * 3]; uint8_t white_data[MAX_SENSOR_PIXELS * 3]; } P5_Calibration_Data; /** * This structure describes a particular model which is handled by the backend. * Contained data is immutable and is used to initialize the P5_Device * structure. */ typedef struct P5_Model { /** @name device identifier * These values are set up once the physical device has been detected. They * are used to build the return value of sane_get_devices(). */ /* @{ */ SANE_String_Const name; SANE_String_Const vendor; SANE_String_Const product; SANE_String_Const type; /* @} */ /** @name resolution * list of avalailable physical resolution. * The resolutions must sorted from lower to higher value. The list is terminated * by a value of 0. */ /* @{ */ int xdpi_values[MAX_RESOLUTIONS]; /** possible x resolutions */ int ydpi_values[MAX_RESOLUTIONS]; /** possible y resolutions */ /* @} */ /** @name scan area description * Minimal and maximal values. It's easier to have dedicated members instead * of searching these values in the dpi lists. They are initialized from dpi * lists. */ /* @{ */ int max_xdpi; /** physical maximum x dpi */ int max_ydpi; /** physical maximum y dpi */ int min_xdpi; /** physical minimum x dpi */ int min_ydpi; /** physical minimum y dpi */ /* @} */ /** @name line distance shift * Distance between CCD arrays for each color. Expressed in line * number at maximum motor resolution. */ int lds; /** @name scan area description * The geometry values are expressed from the head parking position, * or the start. For a given model, the scan area selected by a frontend * will have to fit within these values. */ /* @{ */ SANE_Fixed x_offset; /** Start of scan area in mm */ SANE_Fixed y_offset; /** Start of scan area in mm */ SANE_Fixed x_size; /** Size of scan area in mm */ SANE_Fixed y_size; /** Size of scan area in mm */ /* @} */ } P5_Model; /** * Enumeration of configuration options for a device. It must starts at 0. */ enum P5_Configure_Option { CFG_MODEL_NAME = 0, /**